| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008100910101011101210131014101510161017101810191020102110221023102410251026102710281029103010311032103310341035103610371038103910401041104210431044104510461047104810491050105110521053105410551056105710581059106010611062106310641065106610671068106910701071107210731074107510761077107810791080108110821083108410851086108710881089109010911092109310941095109610971098109911001101110211031104110511061107110811091110111111121113111411151116111711181119112011211122112311241125112611271128112911301131113211331134113511361137113811391140114111421143114411451146114711481149115011511152115311541155115611571158115911601161116211631164116511661167116811691170117111721173117411751176117711781179118011811182118311841185118611871188118911901191119211931194119511961197119811991200120112021203120412051206120712081209121012111212121312141215121612171218121912201221122212231224122512261227122812291230123112321233123412351236123712381239124012411242124312441245124612471248124912501251125212531254125512561257125812591260126112621263126412651266126712681269127012711272127312741275127612771278127912801281128212831284128512861287128812891290129112921293129412951296129712981299130013011302130313041305130613071308130913101311131213131314131513161317131813191320132113221323132413251326132713281329133013311332133313341335133613371338133913401341134213431344134513461347134813491350135113521353135413551356135713581359136013611362136313641365136613671368136913701371137213731374137513761377137813791380138113821383138413851386138713881389139013911392139313941395139613971398139914001401140214031404140514061407140814091410141114121413141414151416141714181419142014211422142314241425142614271428142914301431143214331434143514361437143814391440144114421443144414451446144714481449145014511452145314541455145614571458145914601461146214631464146514661467146814691470147114721473147414751476147714781479148014811482148314841485148614871488148914901491149214931494149514961497149814991500150115021503150415051506150715081509151015111512151315141515151615171518151915201521152215231524152515261527152815291530153115321533153415351536153715381539154015411542154315441545154615471548154915501551155215531554155515561557155815591560156115621563156415651566156715681569157015711572157315741575157615771578157915801581158215831584158515861587158815891590159115921593159415951596159715981599160016011602160316041605160616071608160916101611161216131614161516161617161816191620162116221623162416251626162716281629163016311632163316341635163616371638163916401641164216431644164516461647164816491650165116521653165416551656165716581659166016611662166316641665166616671668166916701671167216731674167516761677167816791680168116821683168416851686168716881689169016911692169316941695169616971698169917001701170217031704170517061707170817091710171117121713171417151716171717181719172017211722172317241725172617271728172917301731173217331734173517361737173817391740174117421743174417451746174717481749175017511752175317541755175617571758175917601761176217631764176517661767176817691770177117721773177417751776177717781779178017811782178317841785178617871788178917901791179217931794179517961797179817991800180118021803180418051806180718081809181018111812181318141815181618171818181918201821182218231824182518261827182818291830183118321833183418351836183718381839184018411842184318441845184618471848184918501851185218531854185518561857185818591860186118621863186418651866186718681869187018711872187318741875187618771878187918801881188218831884188518861887188818891890189118921893189418951896189718981899190019011902190319041905190619071908190919101911191219131914191519161917191819191920192119221923192419251926192719281929193019311932193319341935193619371938193919401941194219431944194519461947194819491950195119521953195419551956195719581959196019611962196319641965196619671968196919701971197219731974197519761977197819791980198119821983198419851986198719881989199019911992199319941995199619971998199920002001200220032004200520062007200820092010201120122013201420152016201720182019202020212022202320242025202620272028202920302031203220332034203520362037203820392040204120422043204420452046204720482049205020512052205320542055205620572058205920602061206220632064206520662067206820692070207120722073207420752076207720782079208020812082208320842085208620872088208920902091209220932094209520962097209820992100210121022103210421052106210721082109211021112112211321142115211621172118211921202121212221232124212521262127212821292130213121322133213421352136213721382139214021412142214321442145214621472148214921502151215221532154215521562157215821592160216121622163216421652166216721682169217021712172217321742175217621772178217921802181218221832184218521862187218821892190219121922193219421952196219721982199220022012202220322042205220622072208220922102211221222132214221522162217221822192220222122222223222422252226222722282229223022312232223322342235223622372238223922402241224222432244224522462247224822492250225122522253225422552256225722582259226022612262226322642265226622672268226922702271227222732274227522762277227822792280228122822283228422852286228722882289229022912292229322942295229622972298229923002301230223032304230523062307230823092310231123122313231423152316231723182319232023212322232323242325232623272328232923302331233223332334233523362337233823392340234123422343234423452346234723482349235023512352235323542355235623572358235923602361236223632364236523662367236823692370237123722373237423752376237723782379238023812382238323842385238623872388238923902391239223932394239523962397239823992400240124022403240424052406240724082409241024112412241324142415241624172418241924202421242224232424242524262427242824292430243124322433243424352436243724382439244024412442244324442445244624472448244924502451245224532454245524562457245824592460246124622463246424652466246724682469247024712472247324742475247624772478247924802481248224832484248524862487248824892490249124922493249424952496249724982499250025012502250325042505250625072508250925102511251225132514251525162517251825192520252125222523252425252526252725282529253025312532253325342535253625372538253925402541254225432544254525462547254825492550255125522553255425552556255725582559256025612562256325642565256625672568256925702571257225732574257525762577257825792580258125822583258425852586258725882589259025912592259325942595259625972598259926002601260226032604260526062607260826092610261126122613261426152616261726182619262026212622262326242625262626272628262926302631263226332634263526362637263826392640264126422643264426452646264726482649265026512652265326542655265626572658265926602661266226632664266526662667266826692670267126722673267426752676267726782679268026812682268326842685268626872688268926902691269226932694269526962697269826992700270127022703270427052706270727082709271027112712271327142715271627172718271927202721272227232724272527262727272827292730273127322733273427352736273727382739274027412742274327442745274627472748274927502751275227532754275527562757275827592760276127622763276427652766276727682769277027712772277327742775277627772778277927802781278227832784278527862787278827892790279127922793279427952796279727982799280028012802280328042805280628072808280928102811281228132814281528162817281828192820282128222823282428252826282728282829283028312832283328342835283628372838283928402841284228432844284528462847284828492850285128522853285428552856285728582859286028612862286328642865286628672868286928702871287228732874287528762877287828792880288128822883288428852886288728882889289028912892289328942895289628972898289929002901290229032904290529062907290829092910291129122913291429152916291729182919292029212922292329242925292629272928292929302931293229332934293529362937293829392940294129422943294429452946294729482949295029512952295329542955295629572958295929602961296229632964296529662967296829692970297129722973297429752976297729782979298029812982298329842985298629872988298929902991299229932994299529962997299829993000300130023003300430053006300730083009301030113012301330143015301630173018301930203021302230233024302530263027302830293030303130323033303430353036303730383039304030413042304330443045304630473048304930503051305230533054305530563057305830593060306130623063306430653066306730683069307030713072307330743075307630773078307930803081308230833084308530863087308830893090309130923093309430953096309730983099310031013102310331043105310631073108310931103111311231133114311531163117311831193120312131223123312431253126312731283129313031313132313331343135313631373138313931403141314231433144314531463147314831493150315131523153315431553156315731583159316031613162316331643165316631673168316931703171317231733174317531763177317831793180318131823183318431853186318731883189319031913192319331943195319631973198319932003201320232033204320532063207320832093210321132123213321432153216321732183219322032213222322332243225322632273228322932303231323232333234323532363237323832393240324132423243324432453246324732483249325032513252325332543255325632573258325932603261326232633264326532663267326832693270327132723273327432753276327732783279328032813282328332843285328632873288328932903291329232933294329532963297329832993300330133023303330433053306330733083309331033113312331333143315331633173318331933203321332233233324332533263327332833293330333133323333333433353336333733383339334033413342334333443345334633473348334933503351335233533354335533563357335833593360336133623363336433653366336733683369337033713372337333743375337633773378337933803381338233833384338533863387338833893390339133923393339433953396339733983399340034013402340334043405340634073408340934103411341234133414341534163417341834193420342134223423342434253426342734283429343034313432343334343435343634373438343934403441344234433444344534463447344834493450345134523453345434553456345734583459346034613462346334643465346634673468346934703471347234733474347534763477347834793480348134823483348434853486348734883489349034913492349334943495349634973498349935003501350235033504350535063507350835093510351135123513351435153516351735183519352035213522352335243525352635273528352935303531353235333534353535363537353835393540354135423543354435453546354735483549355035513552355335543555355635573558355935603561356235633564356535663567356835693570357135723573357435753576357735783579358035813582358335843585358635873588358935903591359235933594359535963597359835993600360136023603360436053606360736083609361036113612361336143615361636173618361936203621362236233624362536263627362836293630363136323633363436353636363736383639364036413642364336443645364636473648364936503651365236533654365536563657365836593660366136623663366436653666366736683669367036713672367336743675367636773678367936803681368236833684368536863687368836893690369136923693369436953696369736983699370037013702370337043705370637073708370937103711371237133714371537163717371837193720372137223723372437253726372737283729373037313732373337343735373637373738373937403741374237433744374537463747374837493750375137523753375437553756375737583759376037613762376337643765376637673768376937703771377237733774377537763777377837793780378137823783378437853786378737883789379037913792379337943795379637973798379938003801380238033804380538063807380838093810381138123813381438153816381738183819382038213822382338243825382638273828382938303831383238333834383538363837383838393840384138423843384438453846384738483849385038513852385338543855385638573858385938603861386238633864386538663867386838693870387138723873387438753876387738783879388038813882388338843885388638873888388938903891389238933894389538963897389838993900390139023903390439053906390739083909391039113912391339143915391639173918391939203921392239233924392539263927392839293930393139323933393439353936393739383939394039413942394339443945394639473948394939503951395239533954395539563957395839593960396139623963396439653966396739683969397039713972397339743975397639773978397939803981398239833984398539863987398839893990399139923993399439953996399739983999400040014002400340044005400640074008400940104011401240134014401540164017401840194020402140224023402440254026402740284029403040314032403340344035403640374038403940404041404240434044404540464047404840494050405140524053405440554056405740584059406040614062406340644065406640674068406940704071407240734074407540764077407840794080408140824083408440854086408740884089409040914092409340944095409640974098409941004101410241034104410541064107410841094110411141124113411441154116411741184119412041214122412341244125412641274128412941304131413241334134413541364137413841394140414141424143414441454146414741484149415041514152415341544155415641574158415941604161416241634164416541664167416841694170417141724173417441754176417741784179418041814182418341844185418641874188418941904191419241934194419541964197419841994200420142024203420442054206420742084209421042114212421342144215421642174218421942204221422242234224422542264227422842294230423142324233423442354236423742384239424042414242424342444245424642474248424942504251425242534254425542564257425842594260426142624263426442654266426742684269427042714272427342744275427642774278427942804281428242834284428542864287428842894290429142924293429442954296429742984299430043014302430343044305430643074308430943104311431243134314431543164317431843194320432143224323432443254326432743284329433043314332433343344335433643374338433943404341434243434344434543464347434843494350435143524353435443554356435743584359436043614362436343644365436643674368436943704371437243734374437543764377437843794380438143824383438443854386438743884389439043914392439343944395439643974398439944004401440244034404440544064407440844094410441144124413441444154416441744184419442044214422442344244425442644274428442944304431443244334434443544364437443844394440444144424443444444454446444744484449445044514452445344544455445644574458445944604461446244634464446544664467446844694470447144724473447444754476447744784479448044814482448344844485448644874488448944904491449244934494449544964497449844994500450145024503450445054506450745084509451045114512451345144515451645174518451945204521452245234524452545264527452845294530453145324533453445354536453745384539454045414542454345444545454645474548454945504551455245534554455545564557455845594560456145624563456445654566456745684569457045714572457345744575457645774578457945804581458245834584458545864587458845894590459145924593459445954596459745984599460046014602460346044605460646074608460946104611461246134614461546164617461846194620462146224623462446254626462746284629463046314632463346344635463646374638463946404641464246434644464546464647464846494650465146524653465446554656465746584659466046614662466346644665466646674668466946704671467246734674467546764677467846794680468146824683468446854686468746884689469046914692469346944695469646974698469947004701470247034704470547064707470847094710471147124713471447154716471747184719472047214722472347244725472647274728472947304731473247334734473547364737473847394740474147424743474447454746474747484749475047514752475347544755475647574758475947604761476247634764476547664767476847694770477147724773477447754776477747784779478047814782478347844785478647874788478947904791479247934794479547964797479847994800480148024803480448054806480748084809481048114812481348144815481648174818481948204821482248234824482548264827482848294830483148324833483448354836483748384839484048414842484348444845484648474848484948504851485248534854485548564857485848594860486148624863486448654866486748684869487048714872487348744875487648774878487948804881488248834884488548864887488848894890489148924893489448954896489748984899490049014902490349044905490649074908490949104911491249134914491549164917491849194920492149224923492449254926492749284929493049314932493349344935493649374938493949404941494249434944494549464947494849494950495149524953495449554956495749584959496049614962496349644965496649674968496949704971497249734974497549764977497849794980498149824983498449854986498749884989499049914992499349944995499649974998499950005001500250035004500550065007500850095010501150125013501450155016501750185019502050215022502350245025502650275028502950305031503250335034503550365037503850395040504150425043504450455046504750485049505050515052505350545055505650575058505950605061506250635064506550665067506850695070507150725073507450755076507750785079508050815082508350845085508650875088508950905091509250935094509550965097509850995100510151025103510451055106510751085109511051115112511351145115511651175118511951205121512251235124512551265127512851295130513151325133513451355136513751385139514051415142514351445145514651475148514951505151515251535154515551565157515851595160516151625163516451655166516751685169517051715172517351745175517651775178517951805181518251835184518551865187518851895190519151925193519451955196519751985199520052015202520352045205520652075208520952105211521252135214521552165217521852195220522152225223522452255226522752285229523052315232523352345235523652375238523952405241524252435244524552465247524852495250525152525253525452555256525752585259526052615262526352645265526652675268526952705271527252735274527552765277527852795280528152825283528452855286528752885289529052915292529352945295529652975298529953005301530253035304530553065307530853095310531153125313531453155316531753185319532053215322532353245325532653275328532953305331533253335334533553365337533853395340534153425343534453455346534753485349535053515352535353545355535653575358535953605361536253635364536553665367536853695370537153725373537453755376537753785379538053815382538353845385538653875388538953905391539253935394539553965397539853995400540154025403540454055406540754085409541054115412541354145415541654175418541954205421542254235424542554265427542854295430543154325433543454355436543754385439544054415442544354445445544654475448544954505451545254535454545554565457545854595460546154625463546454655466546754685469547054715472547354745475547654775478547954805481548254835484548554865487548854895490549154925493549454955496549754985499550055015502550355045505550655075508550955105511551255135514551555165517551855195520552155225523552455255526552755285529553055315532553355345535553655375538553955405541554255435544554555465547554855495550555155525553555455555556555755585559556055615562556355645565556655675568556955705571557255735574557555765577557855795580558155825583558455855586558755885589559055915592559355945595559655975598559956005601560256035604560556065607560856095610561156125613561456155616561756185619562056215622562356245625562656275628562956305631563256335634563556365637563856395640564156425643564456455646564756485649565056515652565356545655565656575658565956605661566256635664566556665667566856695670567156725673567456755676567756785679568056815682568356845685568656875688568956905691569256935694569556965697569856995700570157025703570457055706570757085709571057115712571357145715571657175718571957205721572257235724572557265727572857295730573157325733573457355736573757385739574057415742574357445745574657475748574957505751575257535754575557565757575857595760576157625763576457655766576757685769577057715772577357745775577657775778577957805781578257835784578557865787578857895790579157925793579457955796579757985799580058015802580358045805580658075808580958105811581258135814581558165817581858195820582158225823582458255826582758285829583058315832583358345835583658375838583958405841584258435844584558465847584858495850585158525853585458555856585758585859586058615862586358645865586658675868586958705871587258735874587558765877587858795880588158825883588458855886588758885889589058915892589358945895589658975898589959005901590259035904590559065907590859095910591159125913591459155916591759185919592059215922592359245925592659275928592959305931593259335934593559365937593859395940594159425943594459455946594759485949595059515952595359545955595659575958595959605961596259635964596559665967596859695970597159725973597459755976597759785979598059815982598359845985598659875988598959905991599259935994599559965997599859996000600160026003600460056006600760086009601060116012601360146015601660176018601960206021602260236024602560266027602860296030603160326033603460356036603760386039604060416042604360446045604660476048604960506051605260536054605560566057605860596060606160626063606460656066606760686069607060716072607360746075607660776078607960806081608260836084608560866087608860896090609160926093609460956096609760986099610061016102610361046105610661076108610961106111611261136114611561166117611861196120612161226123612461256126612761286129613061316132613361346135613661376138613961406141614261436144614561466147614861496150615161526153615461556156615761586159616061616162616361646165616661676168616961706171617261736174617561766177617861796180618161826183618461856186618761886189619061916192619361946195619661976198619962006201620262036204620562066207620862096210621162126213621462156216621762186219622062216222622362246225622662276228622962306231623262336234623562366237623862396240624162426243624462456246624762486249625062516252625362546255625662576258625962606261626262636264626562666267626862696270627162726273627462756276627762786279628062816282628362846285628662876288628962906291629262936294629562966297629862996300630163026303630463056306630763086309631063116312631363146315631663176318631963206321632263236324632563266327632863296330633163326333633463356336633763386339634063416342634363446345634663476348634963506351635263536354635563566357635863596360636163626363636463656366636763686369637063716372637363746375637663776378637963806381638263836384638563866387638863896390639163926393639463956396639763986399640064016402640364046405640664076408640964106411641264136414641564166417641864196420642164226423642464256426642764286429643064316432643364346435643664376438643964406441644264436444644564466447644864496450645164526453645464556456645764586459646064616462646364646465646664676468646964706471647264736474647564766477647864796480648164826483648464856486648764886489649064916492649364946495649664976498649965006501650265036504650565066507650865096510651165126513651465156516651765186519652065216522652365246525652665276528652965306531653265336534653565366537653865396540654165426543654465456546654765486549655065516552655365546555655665576558655965606561656265636564656565666567656865696570657165726573657465756576657765786579658065816582658365846585658665876588658965906591659265936594659565966597659865996600660166026603660466056606660766086609661066116612661366146615661666176618661966206621662266236624662566266627662866296630663166326633663466356636663766386639664066416642664366446645664666476648664966506651665266536654665566566657665866596660666166626663666466656666666766686669667066716672667366746675667666776678667966806681668266836684668566866687668866896690669166926693669466956696669766986699670067016702670367046705670667076708670967106711671267136714671567166717671867196720672167226723672467256726672767286729673067316732673367346735673667376738673967406741674267436744674567466747674867496750675167526753675467556756675767586759676067616762676367646765676667676768676967706771677267736774677567766777677867796780678167826783678467856786678767886789679067916792679367946795679667976798679968006801680268036804680568066807680868096810681168126813681468156816681768186819682068216822682368246825682668276828682968306831683268336834683568366837683868396840684168426843684468456846684768486849685068516852685368546855685668576858685968606861686268636864686568666867686868696870687168726873687468756876687768786879688068816882688368846885688668876888688968906891689268936894689568966897689868996900690169026903690469056906690769086909691069116912691369146915691669176918691969206921692269236924692569266927692869296930693169326933693469356936693769386939694069416942694369446945694669476948694969506951695269536954695569566957695869596960696169626963696469656966696769686969697069716972697369746975697669776978697969806981698269836984698569866987698869896990699169926993699469956996699769986999700070017002700370047005700670077008700970107011701270137014701570167017701870197020702170227023702470257026702770287029703070317032703370347035703670377038703970407041704270437044704570467047704870497050705170527053705470557056705770587059706070617062706370647065706670677068706970707071707270737074707570767077707870797080708170827083708470857086708770887089709070917092709370947095709670977098709971007101710271037104710571067107710871097110711171127113711471157116711771187119712071217122712371247125712671277128712971307131713271337134713571367137713871397140714171427143714471457146714771487149715071517152715371547155715671577158715971607161716271637164716571667167716871697170717171727173717471757176717771787179718071817182718371847185718671877188718971907191719271937194719571967197719871997200720172027203720472057206720772087209721072117212721372147215721672177218721972207221722272237224722572267227722872297230723172327233723472357236723772387239724072417242724372447245724672477248724972507251725272537254725572567257725872597260726172627263726472657266726772687269727072717272727372747275727672777278727972807281728272837284728572867287728872897290729172927293729472957296729772987299730073017302730373047305730673077308730973107311731273137314731573167317731873197320732173227323732473257326732773287329733073317332733373347335733673377338733973407341734273437344734573467347734873497350735173527353735473557356735773587359736073617362736373647365736673677368736973707371737273737374737573767377737873797380738173827383738473857386738773887389739073917392739373947395739673977398739974007401740274037404740574067407740874097410741174127413741474157416741774187419742074217422742374247425742674277428742974307431743274337434743574367437743874397440744174427443744474457446744774487449745074517452745374547455745674577458745974607461746274637464746574667467746874697470747174727473747474757476747774787479748074817482748374847485748674877488748974907491749274937494749574967497749874997500750175027503750475057506750775087509751075117512751375147515751675177518751975207521752275237524752575267527752875297530753175327533753475357536753775387539754075417542754375447545754675477548754975507551755275537554755575567557755875597560756175627563756475657566756775687569757075717572757375747575757675777578757975807581758275837584758575867587758875897590759175927593759475957596759775987599760076017602760376047605760676077608760976107611761276137614761576167617761876197620762176227623762476257626762776287629763076317632763376347635763676377638763976407641764276437644764576467647764876497650765176527653765476557656765776587659766076617662766376647665766676677668766976707671767276737674767576767677767876797680768176827683768476857686768776887689769076917692769376947695769676977698769977007701770277037704770577067707770877097710771177127713771477157716771777187719772077217722772377247725772677277728772977307731773277337734773577367737773877397740774177427743774477457746774777487749775077517752775377547755775677577758775977607761776277637764776577667767776877697770777177727773777477757776777777787779778077817782778377847785778677877788778977907791779277937794779577967797779877997800780178027803780478057806780778087809781078117812781378147815781678177818781978207821782278237824782578267827782878297830783178327833783478357836783778387839784078417842784378447845784678477848784978507851785278537854785578567857785878597860786178627863786478657866786778687869787078717872787378747875787678777878787978807881788278837884788578867887788878897890789178927893789478957896789778987899790079017902790379047905790679077908790979107911791279137914791579167917791879197920792179227923792479257926792779287929793079317932793379347935793679377938793979407941794279437944794579467947794879497950795179527953795479557956795779587959796079617962796379647965796679677968796979707971797279737974797579767977797879797980798179827983798479857986798779887989799079917992799379947995799679977998799980008001800280038004800580068007800880098010801180128013801480158016801780188019802080218022802380248025802680278028802980308031803280338034803580368037803880398040804180428043804480458046804780488049805080518052805380548055805680578058805980608061806280638064806580668067806880698070807180728073807480758076807780788079808080818082808380848085808680878088808980908091809280938094809580968097809880998100810181028103810481058106810781088109811081118112811381148115811681178118811981208121812281238124812581268127812881298130813181328133813481358136813781388139814081418142814381448145814681478148814981508151815281538154815581568157815881598160816181628163816481658166816781688169817081718172817381748175817681778178817981808181818281838184818581868187818881898190819181928193819481958196819781988199820082018202820382048205820682078208820982108211821282138214821582168217821882198220822182228223822482258226822782288229823082318232823382348235823682378238823982408241824282438244824582468247824882498250825182528253825482558256825782588259826082618262826382648265826682678268826982708271827282738274827582768277827882798280828182828283828482858286828782888289829082918292829382948295829682978298829983008301830283038304830583068307830883098310831183128313831483158316831783188319832083218322832383248325832683278328832983308331833283338334833583368337833883398340834183428343834483458346834783488349835083518352835383548355835683578358835983608361836283638364836583668367836883698370837183728373837483758376837783788379838083818382838383848385838683878388838983908391839283938394839583968397839883998400840184028403840484058406840784088409841084118412841384148415841684178418841984208421842284238424842584268427842884298430843184328433843484358436843784388439844084418442844384448445844684478448844984508451845284538454845584568457845884598460846184628463846484658466846784688469847084718472847384748475847684778478847984808481848284838484848584868487848884898490849184928493849484958496849784988499850085018502850385048505850685078508850985108511851285138514851585168517851885198520852185228523852485258526852785288529853085318532853385348535853685378538853985408541854285438544854585468547854885498550855185528553855485558556855785588559856085618562856385648565856685678568856985708571857285738574857585768577857885798580858185828583858485858586858785888589859085918592859385948595859685978598859986008601860286038604860586068607860886098610861186128613861486158616861786188619862086218622862386248625862686278628862986308631863286338634863586368637863886398640864186428643864486458646864786488649865086518652865386548655865686578658865986608661866286638664866586668667866886698670867186728673867486758676867786788679868086818682868386848685868686878688868986908691869286938694869586968697869886998700870187028703870487058706870787088709871087118712871387148715871687178718871987208721872287238724872587268727872887298730873187328733873487358736873787388739874087418742874387448745874687478748874987508751875287538754875587568757875887598760876187628763876487658766876787688769877087718772877387748775877687778778877987808781878287838784878587868787878887898790879187928793879487958796879787988799880088018802880388048805880688078808880988108811881288138814881588168817881888198820882188228823882488258826882788288829883088318832883388348835883688378838883988408841884288438844884588468847884888498850885188528853885488558856885788588859886088618862886388648865886688678868886988708871887288738874887588768877887888798880888188828883888488858886888788888889889088918892889388948895889688978898889989008901890289038904890589068907890889098910891189128913891489158916891789188919892089218922892389248925892689278928892989308931893289338934893589368937893889398940894189428943894489458946894789488949895089518952895389548955895689578958895989608961896289638964896589668967896889698970897189728973897489758976897789788979898089818982898389848985898689878988898989908991899289938994899589968997899889999000900190029003900490059006900790089009901090119012901390149015901690179018901990209021902290239024902590269027902890299030903190329033903490359036903790389039904090419042904390449045904690479048904990509051905290539054905590569057905890599060906190629063906490659066906790689069907090719072907390749075907690779078907990809081908290839084908590869087908890899090909190929093909490959096909790989099910091019102910391049105910691079108910991109111911291139114911591169117911891199120912191229123912491259126912791289129913091319132913391349135913691379138913991409141914291439144914591469147914891499150915191529153915491559156915791589159916091619162916391649165916691679168916991709171917291739174917591769177917891799180918191829183918491859186918791889189919091919192919391949195919691979198919992009201920292039204920592069207920892099210921192129213921492159216921792189219922092219222922392249225922692279228922992309231923292339234923592369237923892399240924192429243924492459246924792489249925092519252925392549255925692579258925992609261926292639264926592669267926892699270927192729273927492759276927792789279928092819282928392849285928692879288928992909291929292939294929592969297929892999300930193029303930493059306930793089309931093119312931393149315931693179318931993209321932293239324932593269327932893299330933193329333933493359336933793389339934093419342934393449345934693479348934993509351935293539354935593569357935893599360936193629363936493659366936793689369937093719372937393749375937693779378937993809381938293839384938593869387938893899390939193929393939493959396939793989399940094019402940394049405940694079408940994109411941294139414941594169417941894199420942194229423942494259426942794289429943094319432943394349435943694379438943994409441944294439444944594469447944894499450945194529453945494559456945794589459946094619462946394649465946694679468946994709471947294739474947594769477947894799480948194829483948494859486948794889489949094919492949394949495949694979498949995009501950295039504950595069507950895099510951195129513951495159516951795189519952095219522952395249525952695279528952995309531953295339534953595369537953895399540954195429543954495459546954795489549955095519552955395549555955695579558955995609561956295639564956595669567956895699570957195729573957495759576957795789579958095819582958395849585958695879588958995909591959295939594959595969597959895999600960196029603960496059606960796089609961096119612961396149615961696179618961996209621962296239624962596269627962896299630963196329633963496359636963796389639964096419642964396449645964696479648964996509651965296539654965596569657965896599660966196629663966496659666966796689669967096719672967396749675967696779678967996809681968296839684968596869687968896899690969196929693969496959696969796989699970097019702970397049705970697079708970997109711971297139714971597169717971897199720972197229723972497259726972797289729973097319732973397349735973697379738973997409741974297439744974597469747974897499750975197529753975497559756975797589759976097619762976397649765976697679768976997709771977297739774977597769777977897799780978197829783978497859786978797889789979097919792979397949795979697979798979998009801980298039804980598069807980898099810981198129813981498159816981798189819982098219822982398249825982698279828982998309831983298339834983598369837983898399840984198429843984498459846984798489849985098519852985398549855985698579858985998609861986298639864986598669867986898699870987198729873987498759876987798789879988098819882988398849885988698879888988998909891989298939894989598969897989898999900990199029903990499059906990799089909991099119912991399149915991699179918991999209921992299239924992599269927992899299930993199329933993499359936993799389939994099419942994399449945994699479948994999509951995299539954995599569957995899599960996199629963996499659966996799689969997099719972997399749975997699779978997999809981998299839984998599869987998899899990999199929993999499959996999799989999100001000110002100031000410005100061000710008100091001010011100121001310014100151001610017100181001910020100211002210023100241002510026100271002810029100301003110032100331003410035100361003710038100391004010041100421004310044100451004610047100481004910050100511005210053100541005510056100571005810059100601006110062100631006410065100661006710068100691007010071100721007310074100751007610077100781007910080100811008210083100841008510086100871008810089100901009110092100931009410095100961009710098100991010010101101021010310104101051010610107101081010910110101111011210113101141011510116101171011810119101201012110122101231012410125101261012710128101291013010131101321013310134101351013610137101381013910140101411014210143101441014510146101471014810149101501015110152101531015410155101561015710158101591016010161101621016310164101651016610167101681016910170101711017210173101741017510176101771017810179101801018110182101831018410185101861018710188101891019010191101921019310194101951019610197101981019910200102011020210203102041020510206102071020810209102101021110212102131021410215102161021710218102191022010221102221022310224102251022610227102281022910230102311023210233102341023510236102371023810239102401024110242102431024410245102461024710248102491025010251102521025310254102551025610257102581025910260102611026210263102641026510266102671026810269102701027110272102731027410275102761027710278102791028010281102821028310284102851028610287102881028910290102911029210293102941029510296102971029810299103001030110302103031030410305103061030710308103091031010311103121031310314103151031610317103181031910320103211032210323103241032510326103271032810329103301033110332103331033410335103361033710338103391034010341103421034310344103451034610347103481034910350103511035210353103541035510356103571035810359103601036110362103631036410365103661036710368103691037010371103721037310374103751037610377103781037910380103811038210383103841038510386103871038810389103901039110392103931039410395103961039710398103991040010401104021040310404104051040610407104081040910410104111041210413104141041510416104171041810419104201042110422104231042410425104261042710428104291043010431104321043310434104351043610437104381043910440104411044210443104441044510446104471044810449104501045110452104531045410455104561045710458104591046010461104621046310464104651046610467104681046910470104711047210473104741047510476104771047810479104801048110482104831048410485104861048710488104891049010491104921049310494104951049610497104981049910500105011050210503105041050510506105071050810509105101051110512105131051410515105161051710518105191052010521105221052310524105251052610527105281052910530105311053210533105341053510536105371053810539105401054110542105431054410545105461054710548105491055010551105521055310554105551055610557105581055910560105611056210563105641056510566105671056810569105701057110572105731057410575105761057710578105791058010581105821058310584105851058610587105881058910590105911059210593105941059510596105971059810599106001060110602106031060410605106061060710608106091061010611106121061310614106151061610617106181061910620106211062210623106241062510626106271062810629106301063110632106331063410635106361063710638106391064010641106421064310644106451064610647106481064910650106511065210653106541065510656106571065810659106601066110662106631066410665106661066710668106691067010671106721067310674106751067610677106781067910680106811068210683106841068510686106871068810689106901069110692106931069410695106961069710698106991070010701107021070310704107051070610707107081070910710107111071210713107141071510716107171071810719107201072110722107231072410725107261072710728107291073010731107321073310734107351073610737107381073910740107411074210743107441074510746107471074810749107501075110752107531075410755107561075710758107591076010761107621076310764107651076610767107681076910770107711077210773107741077510776107771077810779107801078110782107831078410785107861078710788107891079010791107921079310794107951079610797107981079910800108011080210803108041080510806108071080810809108101081110812108131081410815108161081710818108191082010821108221082310824108251082610827108281082910830108311083210833108341083510836108371083810839108401084110842108431084410845108461084710848108491085010851108521085310854108551085610857108581085910860108611086210863108641086510866108671086810869108701087110872108731087410875108761087710878108791088010881108821088310884108851088610887108881088910890108911089210893108941089510896108971089810899109001090110902109031090410905109061090710908109091091010911109121091310914109151091610917109181091910920109211092210923109241092510926109271092810929109301093110932109331093410935109361093710938109391094010941109421094310944109451094610947109481094910950109511095210953109541095510956109571095810959109601096110962109631096410965109661096710968109691097010971109721097310974109751097610977109781097910980109811098210983109841098510986109871098810989109901099110992109931099410995109961099710998109991100011001110021100311004110051100611007110081100911010110111101211013110141101511016110171101811019110201102111022110231102411025110261102711028110291103011031110321103311034110351103611037110381103911040110411104211043110441104511046110471104811049110501105111052110531105411055110561105711058110591106011061110621106311064110651106611067110681106911070110711107211073110741107511076110771107811079110801108111082110831108411085110861108711088110891109011091110921109311094110951109611097110981109911100111011110211103111041110511106111071110811109111101111111112111131111411115111161111711118111191112011121111221112311124111251112611127111281112911130111311113211133111341113511136111371113811139111401114111142111431114411145111461114711148111491115011151111521115311154111551115611157111581115911160111611116211163111641116511166111671116811169111701117111172111731117411175111761117711178111791118011181111821118311184111851118611187111881118911190111911119211193111941119511196111971119811199112001120111202112031120411205112061120711208112091121011211112121121311214112151121611217112181121911220112211122211223112241122511226112271122811229112301123111232112331123411235112361123711238112391124011241112421124311244112451124611247112481124911250112511125211253112541125511256112571125811259112601126111262112631126411265112661126711268112691127011271112721127311274112751127611277112781127911280112811128211283112841128511286112871128811289112901129111292112931129411295112961129711298112991130011301113021130311304113051130611307113081130911310113111131211313113141131511316113171131811319113201132111322113231132411325113261132711328113291133011331113321133311334113351133611337113381133911340113411134211343113441134511346113471134811349113501135111352113531135411355113561135711358113591136011361113621136311364113651136611367113681136911370113711137211373113741137511376113771137811379113801138111382113831138411385113861138711388113891139011391113921139311394113951139611397113981139911400114011140211403114041140511406114071140811409114101141111412114131141411415114161141711418114191142011421114221142311424114251142611427114281142911430114311143211433114341143511436114371143811439114401144111442114431144411445114461144711448114491145011451114521145311454114551145611457114581145911460114611146211463114641146511466114671146811469114701147111472114731147411475114761147711478114791148011481114821148311484114851148611487114881148911490114911149211493114941149511496114971149811499115001150111502115031150411505115061150711508115091151011511115121151311514115151151611517115181151911520115211152211523115241152511526115271152811529115301153111532115331153411535115361153711538115391154011541115421154311544115451154611547115481154911550115511155211553115541155511556115571155811559115601156111562115631156411565115661156711568115691157011571115721157311574115751157611577115781157911580115811158211583115841158511586115871158811589115901159111592115931159411595115961159711598115991160011601116021160311604116051160611607116081160911610116111161211613116141161511616116171161811619116201162111622116231162411625116261162711628116291163011631116321163311634116351163611637116381163911640116411164211643116441164511646116471164811649116501165111652116531165411655116561165711658116591166011661116621166311664116651166611667116681166911670116711167211673116741167511676116771167811679116801168111682116831168411685116861168711688116891169011691116921169311694116951169611697116981169911700117011170211703117041170511706117071170811709117101171111712117131171411715117161171711718117191172011721117221172311724117251172611727117281172911730117311173211733117341173511736117371173811739117401174111742117431174411745117461174711748117491175011751117521175311754117551175611757117581175911760117611176211763117641176511766117671176811769117701177111772117731177411775117761177711778117791178011781117821178311784117851178611787117881178911790117911179211793117941179511796117971179811799118001180111802118031180411805118061180711808118091181011811118121181311814118151181611817118181181911820118211182211823118241182511826118271182811829118301183111832118331183411835118361183711838118391184011841118421184311844118451184611847118481184911850118511185211853118541185511856118571185811859118601186111862118631186411865118661186711868118691187011871118721187311874118751187611877118781187911880118811188211883118841188511886118871188811889118901189111892118931189411895118961189711898118991190011901119021190311904119051190611907119081190911910119111191211913119141191511916119171191811919119201192111922119231192411925119261192711928119291193011931119321193311934119351193611937119381193911940119411194211943119441194511946119471194811949119501195111952119531195411955119561195711958119591196011961119621196311964119651196611967119681196911970119711197211973119741197511976119771197811979119801198111982119831198411985119861198711988119891199011991119921199311994119951199611997119981199912000120011200212003120041200512006120071200812009120101201112012120131201412015120161201712018120191202012021120221202312024120251202612027120281202912030120311203212033120341203512036120371203812039120401204112042120431204412045120461204712048120491205012051120521205312054120551205612057120581205912060120611206212063120641206512066120671206812069120701207112072120731207412075120761207712078120791208012081120821208312084120851208612087120881208912090120911209212093120941209512096120971209812099121001210112102121031210412105121061210712108121091211012111121121211312114121151211612117121181211912120121211212212123121241212512126121271212812129121301213112132121331213412135121361213712138121391214012141121421214312144121451214612147121481214912150121511215212153121541215512156121571215812159121601216112162121631216412165121661216712168121691217012171121721217312174121751217612177121781217912180121811218212183121841218512186121871218812189121901219112192121931219412195121961219712198121991220012201122021220312204122051220612207122081220912210122111221212213122141221512216122171221812219122201222112222122231222412225122261222712228122291223012231122321223312234122351223612237122381223912240122411224212243122441224512246122471224812249122501225112252122531225412255122561225712258122591226012261122621226312264122651226612267122681226912270122711227212273122741227512276122771227812279122801228112282122831228412285122861228712288122891229012291122921229312294122951229612297122981229912300123011230212303123041230512306123071230812309123101231112312123131231412315123161231712318123191232012321123221232312324123251232612327123281232912330123311233212333123341233512336123371233812339123401234112342123431234412345123461234712348123491235012351123521235312354123551235612357123581235912360123611236212363123641236512366123671236812369123701237112372123731237412375123761237712378123791238012381123821238312384123851238612387123881238912390123911239212393123941239512396123971239812399124001240112402124031240412405124061240712408124091241012411124121241312414124151241612417124181241912420124211242212423124241242512426124271242812429124301243112432124331243412435124361243712438124391244012441124421244312444124451244612447124481244912450124511245212453124541245512456124571245812459124601246112462124631246412465124661246712468124691247012471124721247312474124751247612477124781247912480124811248212483124841248512486124871248812489124901249112492124931249412495124961249712498124991250012501125021250312504125051250612507125081250912510125111251212513125141251512516125171251812519125201252112522125231252412525125261252712528125291253012531125321253312534125351253612537125381253912540125411254212543125441254512546125471254812549125501255112552125531255412555125561255712558125591256012561125621256312564125651256612567125681256912570125711257212573125741257512576125771257812579125801258112582125831258412585125861258712588125891259012591125921259312594125951259612597125981259912600126011260212603126041260512606126071260812609126101261112612126131261412615126161261712618126191262012621126221262312624126251262612627126281262912630126311263212633126341263512636126371263812639126401264112642126431264412645126461264712648126491265012651126521265312654126551265612657126581265912660126611266212663126641266512666126671266812669126701267112672126731267412675126761267712678126791268012681126821268312684126851268612687126881268912690126911269212693126941269512696126971269812699127001270112702127031270412705127061270712708127091271012711127121271312714127151271612717127181271912720127211272212723127241272512726127271272812729127301273112732127331273412735127361273712738127391274012741127421274312744127451274612747127481274912750127511275212753127541275512756127571275812759127601276112762127631276412765127661276712768127691277012771127721277312774127751277612777127781277912780127811278212783127841278512786127871278812789127901279112792127931279412795127961279712798127991280012801128021280312804128051280612807128081280912810128111281212813128141281512816128171281812819128201282112822128231282412825128261282712828128291283012831128321283312834128351283612837128381283912840128411284212843128441284512846128471284812849128501285112852128531285412855128561285712858128591286012861128621286312864128651286612867128681286912870128711287212873128741287512876128771287812879128801288112882128831288412885128861288712888128891289012891128921289312894128951289612897128981289912900129011290212903129041290512906129071290812909129101291112912129131291412915129161291712918129191292012921129221292312924129251292612927129281292912930129311293212933129341293512936129371293812939129401294112942129431294412945129461294712948129491295012951129521295312954129551295612957129581295912960129611296212963129641296512966129671296812969129701297112972129731297412975129761297712978129791298012981129821298312984129851298612987129881298912990129911299212993129941299512996129971299812999130001300113002130031300413005130061300713008130091301013011130121301313014130151301613017130181301913020130211302213023130241302513026130271302813029130301303113032130331303413035130361303713038130391304013041130421304313044130451304613047130481304913050130511305213053130541305513056130571305813059130601306113062130631306413065130661306713068130691307013071130721307313074130751307613077130781307913080130811308213083130841308513086130871308813089130901309113092130931309413095130961309713098130991310013101131021310313104131051310613107131081310913110131111311213113131141311513116131171311813119131201312113122131231312413125131261312713128131291313013131131321313313134131351313613137131381313913140131411314213143131441314513146131471314813149131501315113152131531315413155131561315713158131591316013161131621316313164131651316613167131681316913170131711317213173131741317513176131771317813179131801318113182131831318413185131861318713188131891319013191131921319313194131951319613197131981319913200132011320213203132041320513206132071320813209132101321113212132131321413215132161321713218132191322013221132221322313224132251322613227132281322913230132311323213233132341323513236132371323813239132401324113242132431324413245132461324713248132491325013251132521325313254132551325613257132581325913260132611326213263132641326513266132671326813269132701327113272132731327413275132761327713278132791328013281132821328313284132851328613287132881328913290132911329213293132941329513296132971329813299133001330113302133031330413305133061330713308133091331013311133121331313314133151331613317133181331913320133211332213323133241332513326133271332813329133301333113332133331333413335133361333713338133391334013341133421334313344133451334613347133481334913350133511335213353133541335513356133571335813359133601336113362133631336413365133661336713368133691337013371133721337313374133751337613377133781337913380133811338213383133841338513386133871338813389133901339113392133931339413395133961339713398133991340013401134021340313404134051340613407134081340913410134111341213413134141341513416134171341813419134201342113422134231342413425134261342713428134291343013431134321343313434134351343613437134381343913440134411344213443134441344513446134471344813449134501345113452134531345413455134561345713458134591346013461134621346313464134651346613467134681346913470134711347213473134741347513476134771347813479134801348113482134831348413485134861348713488134891349013491134921349313494134951349613497134981349913500135011350213503135041350513506135071350813509135101351113512135131351413515135161351713518135191352013521135221352313524135251352613527135281352913530135311353213533135341353513536135371353813539135401354113542135431354413545135461354713548135491355013551135521355313554135551355613557135581355913560135611356213563135641356513566135671356813569135701357113572135731357413575135761357713578135791358013581135821358313584135851358613587135881358913590135911359213593135941359513596135971359813599136001360113602136031360413605136061360713608136091361013611136121361313614136151361613617136181361913620136211362213623136241362513626136271362813629136301363113632136331363413635136361363713638136391364013641136421364313644136451364613647136481364913650136511365213653136541365513656136571365813659136601366113662136631366413665136661366713668136691367013671136721367313674136751367613677136781367913680136811368213683136841368513686136871368813689136901369113692136931369413695136961369713698136991370013701137021370313704137051370613707137081370913710137111371213713137141371513716137171371813719137201372113722137231372413725137261372713728137291373013731137321373313734137351373613737137381373913740137411374213743137441374513746137471374813749137501375113752137531375413755137561375713758137591376013761137621376313764137651376613767137681376913770137711377213773137741377513776137771377813779137801378113782137831378413785137861378713788137891379013791137921379313794137951379613797137981379913800138011380213803138041380513806138071380813809138101381113812138131381413815138161381713818138191382013821138221382313824138251382613827138281382913830138311383213833138341383513836138371383813839138401384113842138431384413845138461384713848138491385013851138521385313854138551385613857138581385913860138611386213863138641386513866138671386813869138701387113872138731387413875138761387713878138791388013881138821388313884138851388613887138881388913890138911389213893138941389513896138971389813899139001390113902139031390413905139061390713908139091391013911139121391313914139151391613917139181391913920139211392213923139241392513926139271392813929139301393113932139331393413935139361393713938139391394013941139421394313944139451394613947139481394913950139511395213953139541395513956139571395813959139601396113962139631396413965139661396713968139691397013971139721397313974139751397613977139781397913980139811398213983139841398513986139871398813989139901399113992139931399413995139961399713998139991400014001140021400314004140051400614007140081400914010140111401214013140141401514016140171401814019140201402114022140231402414025140261402714028140291403014031140321403314034140351403614037140381403914040140411404214043140441404514046140471404814049140501405114052140531405414055140561405714058140591406014061140621406314064140651406614067140681406914070140711407214073140741407514076140771407814079140801408114082140831408414085140861408714088140891409014091140921409314094140951409614097140981409914100141011410214103141041410514106141071410814109141101411114112141131411414115141161411714118141191412014121141221412314124141251412614127141281412914130141311413214133141341413514136141371413814139141401414114142141431414414145141461414714148141491415014151141521415314154141551415614157141581415914160141611416214163141641416514166141671416814169141701417114172141731417414175141761417714178141791418014181141821418314184141851418614187141881418914190141911419214193141941419514196141971419814199142001420114202142031420414205142061420714208142091421014211142121421314214142151421614217142181421914220142211422214223142241422514226142271422814229142301423114232142331423414235142361423714238142391424014241142421424314244142451424614247142481424914250142511425214253142541425514256142571425814259142601426114262142631426414265142661426714268142691427014271142721427314274142751427614277142781427914280142811428214283142841428514286142871428814289142901429114292142931429414295142961429714298142991430014301143021430314304143051430614307143081430914310143111431214313143141431514316143171431814319143201432114322143231432414325143261432714328143291433014331143321433314334143351433614337143381433914340143411434214343143441434514346143471434814349143501435114352143531435414355143561435714358143591436014361143621436314364143651436614367143681436914370143711437214373143741437514376143771437814379143801438114382143831438414385143861438714388143891439014391143921439314394143951439614397143981439914400144011440214403144041440514406144071440814409144101441114412144131441414415144161441714418144191442014421144221442314424144251442614427144281442914430144311443214433144341443514436144371443814439144401444114442144431444414445144461444714448144491445014451144521445314454144551445614457144581445914460144611446214463144641446514466144671446814469144701447114472144731447414475144761447714478144791448014481144821448314484144851448614487144881448914490144911449214493144941449514496144971449814499145001450114502145031450414505145061450714508145091451014511145121451314514145151451614517145181451914520145211452214523145241452514526145271452814529145301453114532145331453414535145361453714538145391454014541145421454314544145451454614547145481454914550145511455214553145541455514556145571455814559145601456114562145631456414565145661456714568145691457014571145721457314574145751457614577145781457914580145811458214583145841458514586145871458814589145901459114592145931459414595145961459714598145991460014601146021460314604146051460614607146081460914610146111461214613146141461514616146171461814619146201462114622146231462414625146261462714628146291463014631146321463314634146351463614637146381463914640146411464214643146441464514646146471464814649146501465114652146531465414655146561465714658146591466014661146621466314664146651466614667146681466914670146711467214673146741467514676146771467814679146801468114682146831468414685146861468714688146891469014691146921469314694146951469614697146981469914700147011470214703147041470514706147071470814709147101471114712147131471414715147161471714718147191472014721147221472314724147251472614727147281472914730147311473214733147341473514736147371473814739147401474114742147431474414745147461474714748147491475014751147521475314754147551475614757147581475914760147611476214763147641476514766147671476814769147701477114772147731477414775147761477714778147791478014781147821478314784147851478614787147881478914790147911479214793147941479514796147971479814799148001480114802148031480414805148061480714808148091481014811148121481314814148151481614817148181481914820148211482214823148241482514826148271482814829148301483114832148331483414835148361483714838148391484014841148421484314844148451484614847148481484914850148511485214853148541485514856148571485814859148601486114862148631486414865148661486714868148691487014871148721487314874148751487614877148781487914880148811488214883148841488514886148871488814889148901489114892148931489414895148961489714898148991490014901149021490314904149051490614907149081490914910149111491214913149141491514916149171491814919149201492114922149231492414925149261492714928149291493014931149321493314934149351493614937149381493914940149411494214943149441494514946149471494814949149501495114952149531495414955149561495714958149591496014961149621496314964149651496614967149681496914970149711497214973149741497514976149771497814979149801498114982149831498414985149861498714988149891499014991149921499314994149951499614997149981499915000150011500215003150041500515006150071500815009150101501115012150131501415015150161501715018150191502015021150221502315024150251502615027150281502915030150311503215033150341503515036150371503815039150401504115042150431504415045150461504715048150491505015051150521505315054150551505615057150581505915060150611506215063150641506515066150671506815069150701507115072150731507415075150761507715078150791508015081150821508315084150851508615087150881508915090150911509215093150941509515096150971509815099151001510115102151031510415105151061510715108151091511015111151121511315114151151511615117151181511915120151211512215123151241512515126151271512815129151301513115132151331513415135151361513715138151391514015141151421514315144151451514615147151481514915150151511515215153151541515515156151571515815159151601516115162151631516415165151661516715168151691517015171151721517315174151751517615177151781517915180151811518215183151841518515186151871518815189151901519115192151931519415195151961519715198151991520015201152021520315204152051520615207152081520915210152111521215213152141521515216152171521815219152201522115222152231522415225152261522715228152291523015231152321523315234152351523615237152381523915240152411524215243152441524515246152471524815249152501525115252152531525415255152561525715258152591526015261152621526315264152651526615267152681526915270152711527215273152741527515276152771527815279152801528115282152831528415285152861528715288152891529015291152921529315294152951529615297152981529915300153011530215303153041530515306153071530815309153101531115312153131531415315153161531715318153191532015321153221532315324153251532615327153281532915330153311533215333153341533515336153371533815339153401534115342153431534415345153461534715348153491535015351153521535315354153551535615357153581535915360153611536215363153641536515366153671536815369153701537115372153731537415375153761537715378153791538015381153821538315384153851538615387153881538915390153911539215393153941539515396153971539815399154001540115402154031540415405154061540715408154091541015411154121541315414154151541615417154181541915420154211542215423154241542515426154271542815429154301543115432154331543415435154361543715438154391544015441154421544315444154451544615447154481544915450154511545215453154541545515456154571545815459154601546115462154631546415465154661546715468154691547015471154721547315474154751547615477154781547915480154811548215483154841548515486154871548815489154901549115492154931549415495154961549715498154991550015501155021550315504155051550615507155081550915510155111551215513155141551515516155171551815519155201552115522155231552415525155261552715528155291553015531155321553315534155351553615537155381553915540155411554215543155441554515546155471554815549155501555115552155531555415555155561555715558155591556015561155621556315564155651556615567155681556915570155711557215573155741557515576155771557815579155801558115582155831558415585155861558715588155891559015591155921559315594155951559615597155981559915600156011560215603156041560515606156071560815609156101561115612156131561415615156161561715618156191562015621156221562315624156251562615627156281562915630156311563215633156341563515636156371563815639156401564115642156431564415645156461564715648156491565015651156521565315654156551565615657156581565915660156611566215663156641566515666156671566815669156701567115672156731567415675156761567715678156791568015681156821568315684156851568615687156881568915690156911569215693156941569515696156971569815699157001570115702157031570415705157061570715708157091571015711157121571315714157151571615717157181571915720157211572215723157241572515726157271572815729157301573115732157331573415735157361573715738157391574015741157421574315744157451574615747157481574915750157511575215753157541575515756157571575815759157601576115762157631576415765157661576715768157691577015771157721577315774157751577615777157781577915780157811578215783157841578515786157871578815789157901579115792157931579415795157961579715798157991580015801158021580315804158051580615807158081580915810158111581215813158141581515816158171581815819158201582115822158231582415825158261582715828158291583015831158321583315834158351583615837158381583915840158411584215843158441584515846158471584815849158501585115852158531585415855158561585715858158591586015861158621586315864158651586615867158681586915870158711587215873158741587515876158771587815879158801588115882158831588415885158861588715888158891589015891158921589315894158951589615897158981589915900159011590215903159041590515906159071590815909159101591115912159131591415915159161591715918159191592015921159221592315924159251592615927159281592915930159311593215933159341593515936159371593815939159401594115942159431594415945159461594715948159491595015951159521595315954159551595615957159581595915960159611596215963159641596515966159671596815969159701597115972159731597415975159761597715978159791598015981159821598315984159851598615987159881598915990159911599215993159941599515996159971599815999160001600116002160031600416005160061600716008160091601016011160121601316014160151601616017160181601916020160211602216023160241602516026160271602816029160301603116032160331603416035160361603716038160391604016041160421604316044160451604616047160481604916050160511605216053160541605516056160571605816059160601606116062160631606416065160661606716068160691607016071160721607316074160751607616077160781607916080160811608216083160841608516086160871608816089160901609116092160931609416095160961609716098160991610016101161021610316104161051610616107161081610916110161111611216113161141611516116161171611816119161201612116122161231612416125161261612716128161291613016131161321613316134161351613616137161381613916140161411614216143161441614516146161471614816149161501615116152161531615416155161561615716158161591616016161161621616316164161651616616167161681616916170161711617216173161741617516176161771617816179161801618116182161831618416185161861618716188161891619016191161921619316194161951619616197161981619916200162011620216203162041620516206162071620816209162101621116212162131621416215162161621716218162191622016221162221622316224162251622616227162281622916230162311623216233162341623516236162371623816239162401624116242162431624416245162461624716248162491625016251162521625316254162551625616257162581625916260162611626216263162641626516266162671626816269162701627116272162731627416275162761627716278162791628016281162821628316284162851628616287162881628916290162911629216293162941629516296162971629816299163001630116302163031630416305163061630716308163091631016311163121631316314163151631616317163181631916320163211632216323163241632516326163271632816329163301633116332163331633416335163361633716338163391634016341163421634316344163451634616347163481634916350163511635216353163541635516356163571635816359163601636116362163631636416365163661636716368163691637016371163721637316374163751637616377163781637916380163811638216383163841638516386163871638816389163901639116392163931639416395163961639716398163991640016401164021640316404164051640616407164081640916410164111641216413164141641516416164171641816419164201642116422164231642416425164261642716428164291643016431164321643316434164351643616437164381643916440164411644216443164441644516446164471644816449164501645116452164531645416455164561645716458164591646016461164621646316464164651646616467164681646916470164711647216473164741647516476164771647816479164801648116482164831648416485164861648716488164891649016491164921649316494164951649616497164981649916500165011650216503165041650516506165071650816509165101651116512165131651416515165161651716518165191652016521165221652316524165251652616527165281652916530165311653216533165341653516536165371653816539165401654116542165431654416545165461654716548165491655016551165521655316554165551655616557165581655916560165611656216563165641656516566165671656816569165701657116572165731657416575165761657716578165791658016581165821658316584165851658616587165881658916590165911659216593165941659516596165971659816599166001660116602166031660416605166061660716608166091661016611166121661316614166151661616617166181661916620166211662216623166241662516626166271662816629166301663116632166331663416635166361663716638166391664016641166421664316644166451664616647166481664916650166511665216653166541665516656166571665816659166601666116662166631666416665166661666716668166691667016671166721667316674166751667616677166781667916680166811668216683166841668516686166871668816689166901669116692166931669416695166961669716698166991670016701167021670316704167051670616707167081670916710167111671216713167141671516716167171671816719167201672116722167231672416725167261672716728167291673016731167321673316734167351673616737167381673916740167411674216743167441674516746167471674816749167501675116752167531675416755167561675716758167591676016761167621676316764167651676616767167681676916770167711677216773167741677516776167771677816779167801678116782167831678416785167861678716788167891679016791167921679316794167951679616797167981679916800168011680216803168041680516806168071680816809168101681116812168131681416815168161681716818168191682016821168221682316824168251682616827168281682916830168311683216833168341683516836168371683816839168401684116842168431684416845168461684716848168491685016851168521685316854168551685616857168581685916860168611686216863168641686516866168671686816869168701687116872168731687416875168761687716878168791688016881168821688316884168851688616887168881688916890168911689216893168941689516896168971689816899169001690116902169031690416905169061690716908169091691016911169121691316914169151691616917169181691916920169211692216923169241692516926169271692816929169301693116932169331693416935169361693716938169391694016941169421694316944169451694616947169481694916950169511695216953169541695516956169571695816959169601696116962169631696416965169661696716968169691697016971169721697316974169751697616977169781697916980169811698216983169841698516986169871698816989169901699116992169931699416995169961699716998169991700017001170021700317004170051700617007170081700917010170111701217013170141701517016170171701817019170201702117022170231702417025170261702717028170291703017031170321703317034170351703617037170381703917040170411704217043170441704517046170471704817049170501705117052170531705417055170561705717058170591706017061170621706317064170651706617067170681706917070170711707217073170741707517076170771707817079170801708117082170831708417085170861708717088170891709017091170921709317094170951709617097170981709917100171011710217103171041710517106171071710817109171101711117112171131711417115171161711717118171191712017121171221712317124171251712617127171281712917130171311713217133171341713517136171371713817139171401714117142171431714417145171461714717148171491715017151171521715317154171551715617157171581715917160171611716217163171641716517166171671716817169171701717117172171731717417175171761717717178171791718017181171821718317184171851718617187171881718917190171911719217193171941719517196171971719817199172001720117202172031720417205172061720717208172091721017211172121721317214172151721617217172181721917220172211722217223172241722517226172271722817229172301723117232172331723417235172361723717238172391724017241172421724317244172451724617247172481724917250172511725217253172541725517256172571725817259172601726117262172631726417265172661726717268172691727017271172721727317274172751727617277172781727917280172811728217283172841728517286172871728817289172901729117292172931729417295172961729717298172991730017301173021730317304173051730617307173081730917310173111731217313173141731517316173171731817319173201732117322173231732417325173261732717328173291733017331173321733317334173351733617337173381733917340173411734217343173441734517346173471734817349173501735117352173531735417355173561735717358173591736017361173621736317364173651736617367173681736917370173711737217373173741737517376173771737817379173801738117382173831738417385173861738717388173891739017391173921739317394173951739617397173981739917400174011740217403174041740517406174071740817409174101741117412174131741417415174161741717418174191742017421174221742317424174251742617427174281742917430174311743217433174341743517436174371743817439174401744117442174431744417445174461744717448174491745017451174521745317454174551745617457174581745917460174611746217463174641746517466174671746817469174701747117472174731747417475174761747717478174791748017481174821748317484174851748617487174881748917490174911749217493174941749517496174971749817499175001750117502175031750417505175061750717508175091751017511175121751317514175151751617517175181751917520175211752217523175241752517526175271752817529175301753117532175331753417535175361753717538175391754017541175421754317544175451754617547175481754917550175511755217553175541755517556175571755817559175601756117562175631756417565175661756717568175691757017571175721757317574175751757617577175781757917580175811758217583175841758517586175871758817589175901759117592175931759417595175961759717598175991760017601176021760317604176051760617607176081760917610176111761217613176141761517616176171761817619176201762117622176231762417625176261762717628176291763017631176321763317634176351763617637176381763917640176411764217643176441764517646176471764817649176501765117652176531765417655176561765717658176591766017661176621766317664176651766617667176681766917670176711767217673176741767517676176771767817679176801768117682176831768417685176861768717688176891769017691176921769317694176951769617697176981769917700177011770217703177041770517706177071770817709177101771117712177131771417715177161771717718177191772017721177221772317724177251772617727177281772917730177311773217733177341773517736177371773817739177401774117742177431774417745177461774717748177491775017751177521775317754177551775617757177581775917760177611776217763177641776517766177671776817769177701777117772177731777417775177761777717778177791778017781177821778317784177851778617787177881778917790177911779217793177941779517796177971779817799178001780117802178031780417805178061780717808178091781017811178121781317814178151781617817178181781917820178211782217823178241782517826178271782817829178301783117832178331783417835178361783717838178391784017841178421784317844178451784617847178481784917850178511785217853178541785517856178571785817859178601786117862178631786417865178661786717868178691787017871178721787317874178751787617877178781787917880178811788217883178841788517886178871788817889178901789117892178931789417895178961789717898178991790017901179021790317904179051790617907179081790917910179111791217913179141791517916179171791817919179201792117922179231792417925179261792717928179291793017931179321793317934179351793617937179381793917940179411794217943179441794517946179471794817949179501795117952179531795417955179561795717958179591796017961179621796317964179651796617967179681796917970179711797217973179741797517976179771797817979179801798117982179831798417985179861798717988179891799017991179921799317994179951799617997179981799918000180011800218003180041800518006180071800818009180101801118012180131801418015180161801718018180191802018021180221802318024180251802618027180281802918030180311803218033180341803518036180371803818039180401804118042180431804418045180461804718048180491805018051180521805318054180551805618057180581805918060180611806218063180641806518066180671806818069180701807118072180731807418075180761807718078180791808018081180821808318084180851808618087180881808918090180911809218093180941809518096180971809818099181001810118102181031810418105181061810718108181091811018111181121811318114181151811618117181181811918120181211812218123181241812518126181271812818129181301813118132181331813418135181361813718138181391814018141181421814318144181451814618147181481814918150181511815218153181541815518156181571815818159181601816118162181631816418165181661816718168181691817018171181721817318174181751817618177181781817918180181811818218183181841818518186181871818818189181901819118192181931819418195181961819718198181991820018201182021820318204182051820618207182081820918210182111821218213182141821518216182171821818219182201822118222182231822418225182261822718228182291823018231182321823318234182351823618237182381823918240182411824218243182441824518246182471824818249182501825118252182531825418255182561825718258182591826018261182621826318264182651826618267182681826918270182711827218273182741827518276182771827818279182801828118282182831828418285182861828718288182891829018291182921829318294182951829618297182981829918300183011830218303183041830518306183071830818309183101831118312183131831418315183161831718318183191832018321183221832318324183251832618327183281832918330183311833218333183341833518336183371833818339183401834118342183431834418345183461834718348183491835018351183521835318354183551835618357183581835918360183611836218363183641836518366183671836818369183701837118372183731837418375183761837718378183791838018381183821838318384183851838618387183881838918390183911839218393183941839518396183971839818399184001840118402184031840418405184061840718408184091841018411184121841318414184151841618417184181841918420184211842218423184241842518426184271842818429184301843118432184331843418435184361843718438184391844018441184421844318444184451844618447184481844918450184511845218453184541845518456184571845818459184601846118462184631846418465184661846718468184691847018471184721847318474184751847618477184781847918480184811848218483184841848518486184871848818489184901849118492184931849418495184961849718498184991850018501185021850318504185051850618507185081850918510185111851218513185141851518516185171851818519185201852118522185231852418525185261852718528185291853018531185321853318534185351853618537185381853918540185411854218543185441854518546185471854818549185501855118552185531855418555185561855718558185591856018561185621856318564185651856618567185681856918570185711857218573185741857518576185771857818579185801858118582185831858418585185861858718588185891859018591185921859318594185951859618597185981859918600186011860218603186041860518606186071860818609186101861118612186131861418615186161861718618186191862018621186221862318624186251862618627186281862918630186311863218633186341863518636186371863818639186401864118642186431864418645186461864718648186491865018651186521865318654186551865618657186581865918660186611866218663186641866518666186671866818669186701867118672186731867418675186761867718678186791868018681186821868318684186851868618687186881868918690186911869218693186941869518696186971869818699187001870118702187031870418705187061870718708187091871018711187121871318714187151871618717187181871918720187211872218723187241872518726187271872818729187301873118732187331873418735187361873718738187391874018741187421874318744187451874618747187481874918750187511875218753187541875518756187571875818759187601876118762187631876418765187661876718768187691877018771187721877318774187751877618777187781877918780187811878218783187841878518786187871878818789187901879118792187931879418795187961879718798187991880018801188021880318804188051880618807188081880918810188111881218813188141881518816188171881818819188201882118822188231882418825188261882718828188291883018831188321883318834188351883618837188381883918840188411884218843188441884518846188471884818849188501885118852188531885418855188561885718858188591886018861188621886318864188651886618867188681886918870188711887218873188741887518876188771887818879188801888118882188831888418885188861888718888188891889018891188921889318894188951889618897188981889918900189011890218903189041890518906189071890818909189101891118912189131891418915189161891718918189191892018921189221892318924189251892618927189281892918930189311893218933189341893518936189371893818939189401894118942189431894418945189461894718948189491895018951189521895318954189551895618957189581895918960189611896218963189641896518966189671896818969189701897118972189731897418975189761897718978189791898018981189821898318984189851898618987189881898918990189911899218993189941899518996189971899818999190001900119002190031900419005190061900719008190091901019011190121901319014190151901619017190181901919020190211902219023190241902519026190271902819029190301903119032190331903419035190361903719038190391904019041190421904319044190451904619047190481904919050190511905219053190541905519056190571905819059190601906119062190631906419065190661906719068190691907019071190721907319074190751907619077190781907919080190811908219083190841908519086190871908819089190901909119092190931909419095190961909719098190991910019101191021910319104191051910619107191081910919110191111911219113191141911519116191171911819119191201912119122191231912419125191261912719128191291913019131191321913319134191351913619137191381913919140191411914219143191441914519146191471914819149191501915119152191531915419155191561915719158191591916019161191621916319164191651916619167191681916919170191711917219173191741917519176191771917819179191801918119182191831918419185191861918719188191891919019191191921919319194191951919619197191981919919200192011920219203192041920519206192071920819209192101921119212192131921419215192161921719218192191922019221192221922319224192251922619227192281922919230192311923219233192341923519236192371923819239192401924119242192431924419245192461924719248192491925019251192521925319254192551925619257192581925919260192611926219263192641926519266192671926819269192701927119272192731927419275192761927719278192791928019281192821928319284192851928619287192881928919290192911929219293192941929519296192971929819299193001930119302193031930419305193061930719308193091931019311193121931319314193151931619317193181931919320193211932219323193241932519326193271932819329193301933119332193331933419335193361933719338193391934019341193421934319344193451934619347193481934919350193511935219353193541935519356193571935819359193601936119362193631936419365193661936719368193691937019371193721937319374193751937619377193781937919380193811938219383193841938519386193871938819389193901939119392193931939419395193961939719398193991940019401194021940319404194051940619407194081940919410194111941219413194141941519416194171941819419194201942119422194231942419425194261942719428194291943019431194321943319434194351943619437194381943919440194411944219443194441944519446194471944819449194501945119452194531945419455194561945719458194591946019461194621946319464194651946619467194681946919470194711947219473194741947519476194771947819479194801948119482194831948419485194861948719488194891949019491194921949319494194951949619497194981949919500195011950219503195041950519506195071950819509195101951119512195131951419515195161951719518195191952019521195221952319524195251952619527195281952919530195311953219533195341953519536195371953819539195401954119542195431954419545195461954719548195491955019551195521955319554195551955619557195581955919560195611956219563195641956519566195671956819569195701957119572195731957419575195761957719578195791958019581195821958319584195851958619587195881958919590195911959219593195941959519596195971959819599196001960119602196031960419605196061960719608196091961019611196121961319614196151961619617196181961919620196211962219623196241962519626196271962819629196301963119632196331963419635196361963719638196391964019641196421964319644196451964619647196481964919650196511965219653196541965519656196571965819659196601966119662196631966419665196661966719668196691967019671196721967319674196751967619677196781967919680196811968219683196841968519686196871968819689196901969119692196931969419695196961969719698196991970019701197021970319704197051970619707197081970919710197111971219713197141971519716197171971819719197201972119722197231972419725197261972719728197291973019731197321973319734197351973619737197381973919740197411974219743197441974519746197471974819749197501975119752197531975419755197561975719758197591976019761197621976319764197651976619767197681976919770197711977219773197741977519776197771977819779197801978119782197831978419785197861978719788197891979019791197921979319794197951979619797197981979919800198011980219803198041980519806198071980819809198101981119812198131981419815198161981719818198191982019821198221982319824198251982619827198281982919830198311983219833198341983519836198371983819839198401984119842198431984419845198461984719848198491985019851198521985319854198551985619857198581985919860198611986219863198641986519866198671986819869198701987119872198731987419875198761987719878198791988019881198821988319884198851988619887198881988919890198911989219893198941989519896198971989819899199001990119902199031990419905199061990719908199091991019911199121991319914199151991619917199181991919920199211992219923199241992519926199271992819929199301993119932199331993419935199361993719938199391994019941199421994319944199451994619947199481994919950199511995219953199541995519956199571995819959199601996119962199631996419965199661996719968199691997019971199721997319974199751997619977199781997919980199811998219983199841998519986199871998819989199901999119992199931999419995199961999719998199992000020001200022000320004200052000620007200082000920010200112001220013200142001520016200172001820019200202002120022200232002420025200262002720028200292003020031200322003320034200352003620037200382003920040200412004220043200442004520046200472004820049200502005120052200532005420055200562005720058200592006020061200622006320064200652006620067200682006920070200712007220073200742007520076200772007820079200802008120082200832008420085200862008720088200892009020091200922009320094200952009620097200982009920100201012010220103201042010520106201072010820109201102011120112201132011420115201162011720118201192012020121201222012320124201252012620127201282012920130201312013220133201342013520136201372013820139201402014120142201432014420145201462014720148201492015020151201522015320154201552015620157201582015920160201612016220163201642016520166201672016820169201702017120172201732017420175201762017720178201792018020181201822018320184201852018620187201882018920190201912019220193201942019520196201972019820199202002020120202202032020420205202062020720208202092021020211202122021320214202152021620217202182021920220202212022220223202242022520226202272022820229202302023120232202332023420235202362023720238202392024020241202422024320244202452024620247202482024920250202512025220253202542025520256202572025820259202602026120262202632026420265202662026720268202692027020271202722027320274202752027620277202782027920280202812028220283202842028520286202872028820289202902029120292202932029420295202962029720298202992030020301203022030320304203052030620307203082030920310203112031220313203142031520316203172031820319203202032120322203232032420325203262032720328203292033020331203322033320334203352033620337203382033920340203412034220343203442034520346203472034820349203502035120352203532035420355203562035720358203592036020361203622036320364203652036620367203682036920370203712037220373203742037520376203772037820379203802038120382203832038420385203862038720388203892039020391203922039320394203952039620397203982039920400204012040220403204042040520406204072040820409204102041120412204132041420415204162041720418204192042020421204222042320424204252042620427204282042920430204312043220433204342043520436204372043820439204402044120442204432044420445204462044720448204492045020451204522045320454204552045620457204582045920460204612046220463204642046520466204672046820469204702047120472204732047420475204762047720478204792048020481204822048320484204852048620487204882048920490204912049220493204942049520496204972049820499205002050120502205032050420505205062050720508205092051020511205122051320514205152051620517205182051920520205212052220523205242052520526205272052820529205302053120532205332053420535205362053720538205392054020541205422054320544205452054620547205482054920550205512055220553205542055520556205572055820559205602056120562205632056420565205662056720568205692057020571205722057320574205752057620577205782057920580205812058220583205842058520586205872058820589205902059120592205932059420595205962059720598205992060020601206022060320604206052060620607206082060920610206112061220613206142061520616206172061820619206202062120622206232062420625206262062720628206292063020631206322063320634206352063620637206382063920640206412064220643206442064520646206472064820649206502065120652206532065420655206562065720658206592066020661206622066320664206652066620667206682066920670206712067220673206742067520676206772067820679206802068120682206832068420685206862068720688206892069020691206922069320694206952069620697206982069920700207012070220703207042070520706207072070820709207102071120712207132071420715207162071720718207192072020721207222072320724207252072620727207282072920730207312073220733207342073520736207372073820739207402074120742207432074420745207462074720748207492075020751207522075320754207552075620757207582075920760207612076220763207642076520766207672076820769207702077120772207732077420775207762077720778207792078020781207822078320784207852078620787207882078920790207912079220793207942079520796207972079820799208002080120802208032080420805208062080720808208092081020811208122081320814208152081620817208182081920820208212082220823208242082520826208272082820829208302083120832208332083420835208362083720838208392084020841208422084320844208452084620847208482084920850208512085220853208542085520856208572085820859208602086120862208632086420865208662086720868208692087020871208722087320874208752087620877208782087920880208812088220883208842088520886208872088820889208902089120892208932089420895208962089720898208992090020901209022090320904209052090620907209082090920910209112091220913209142091520916209172091820919209202092120922209232092420925209262092720928209292093020931209322093320934209352093620937209382093920940209412094220943209442094520946209472094820949209502095120952209532095420955209562095720958209592096020961209622096320964209652096620967209682096920970209712097220973209742097520976209772097820979209802098120982209832098420985209862098720988209892099020991209922099320994209952099620997209982099921000210012100221003210042100521006210072100821009210102101121012210132101421015210162101721018210192102021021210222102321024210252102621027210282102921030210312103221033210342103521036210372103821039210402104121042210432104421045210462104721048210492105021051210522105321054210552105621057210582105921060210612106221063210642106521066210672106821069210702107121072210732107421075210762107721078210792108021081210822108321084210852108621087210882108921090210912109221093210942109521096210972109821099211002110121102211032110421105211062110721108211092111021111211122111321114211152111621117211182111921120211212112221123211242112521126211272112821129211302113121132211332113421135211362113721138211392114021141211422114321144211452114621147211482114921150211512115221153211542115521156211572115821159211602116121162211632116421165211662116721168211692117021171211722117321174211752117621177211782117921180211812118221183211842118521186211872118821189211902119121192211932119421195211962119721198211992120021201212022120321204212052120621207212082120921210212112121221213212142121521216212172121821219212202122121222212232122421225212262122721228212292123021231212322123321234212352123621237212382123921240212412124221243212442124521246212472124821249212502125121252212532125421255212562125721258212592126021261212622126321264212652126621267212682126921270212712127221273212742127521276212772127821279212802128121282212832128421285212862128721288212892129021291212922129321294212952129621297212982129921300213012130221303213042130521306213072130821309213102131121312213132131421315213162131721318213192132021321213222132321324213252132621327213282132921330213312133221333213342133521336213372133821339213402134121342213432134421345213462134721348213492135021351213522135321354213552135621357213582135921360213612136221363213642136521366213672136821369213702137121372213732137421375213762137721378213792138021381213822138321384213852138621387213882138921390213912139221393213942139521396213972139821399214002140121402214032140421405214062140721408214092141021411214122141321414214152141621417214182141921420214212142221423214242142521426214272142821429214302143121432214332143421435214362143721438214392144021441214422144321444214452144621447214482144921450214512145221453214542145521456214572145821459214602146121462214632146421465214662146721468214692147021471214722147321474214752147621477214782147921480214812148221483214842148521486214872148821489214902149121492214932149421495214962149721498214992150021501215022150321504215052150621507215082150921510215112151221513215142151521516215172151821519215202152121522215232152421525215262152721528215292153021531215322153321534215352153621537215382153921540215412154221543215442154521546215472154821549215502155121552215532155421555215562155721558215592156021561215622156321564215652156621567215682156921570215712157221573215742157521576215772157821579215802158121582215832158421585215862158721588215892159021591215922159321594215952159621597215982159921600216012160221603216042160521606216072160821609216102161121612216132161421615216162161721618216192162021621216222162321624216252162621627216282162921630216312163221633216342163521636216372163821639216402164121642216432164421645216462164721648216492165021651216522165321654216552165621657216582165921660216612166221663216642166521666216672166821669216702167121672216732167421675216762167721678216792168021681216822168321684216852168621687216882168921690216912169221693216942169521696216972169821699217002170121702217032170421705217062170721708217092171021711217122171321714217152171621717217182171921720217212172221723217242172521726217272172821729217302173121732217332173421735217362173721738217392174021741217422174321744217452174621747217482174921750217512175221753217542175521756217572175821759217602176121762217632176421765217662176721768217692177021771217722177321774217752177621777217782177921780217812178221783217842178521786217872178821789217902179121792217932179421795217962179721798217992180021801218022180321804218052180621807218082180921810218112181221813218142181521816218172181821819218202182121822218232182421825218262182721828218292183021831218322183321834218352183621837218382183921840218412184221843218442184521846218472184821849218502185121852218532185421855218562185721858218592186021861218622186321864218652186621867218682186921870218712187221873218742187521876218772187821879218802188121882218832188421885218862188721888218892189021891218922189321894218952189621897218982189921900219012190221903219042190521906219072190821909219102191121912219132191421915219162191721918219192192021921219222192321924219252192621927219282192921930219312193221933219342193521936219372193821939219402194121942219432194421945219462194721948219492195021951219522195321954219552195621957219582195921960219612196221963219642196521966219672196821969219702197121972219732197421975219762197721978219792198021981219822198321984219852198621987219882198921990219912199221993219942199521996219972199821999220002200122002220032200422005220062200722008220092201022011220122201322014220152201622017220182201922020220212202222023220242202522026220272202822029220302203122032220332203422035220362203722038220392204022041220422204322044220452204622047220482204922050220512205222053220542205522056220572205822059220602206122062220632206422065220662206722068220692207022071220722207322074220752207622077220782207922080220812208222083220842208522086220872208822089220902209122092220932209422095220962209722098220992210022101221022210322104221052210622107221082210922110221112211222113221142211522116221172211822119221202212122122221232212422125221262212722128221292213022131221322213322134221352213622137221382213922140221412214222143221442214522146221472214822149221502215122152221532215422155221562215722158221592216022161221622216322164221652216622167221682216922170221712217222173221742217522176221772217822179221802218122182221832218422185221862218722188221892219022191221922219322194221952219622197221982219922200222012220222203222042220522206222072220822209222102221122212222132221422215222162221722218222192222022221222222222322224222252222622227222282222922230222312223222233222342223522236222372223822239222402224122242222432224422245222462224722248222492225022251222522225322254222552225622257222582225922260222612226222263222642226522266222672226822269222702227122272222732227422275222762227722278222792228022281222822228322284222852228622287222882228922290222912229222293222942229522296222972229822299223002230122302223032230422305223062230722308223092231022311223122231322314223152231622317223182231922320223212232222323223242232522326223272232822329223302233122332223332233422335223362233722338223392234022341223422234322344223452234622347223482234922350223512235222353223542235522356223572235822359223602236122362223632236422365223662236722368223692237022371223722237322374223752237622377223782237922380223812238222383223842238522386223872238822389223902239122392223932239422395223962239722398223992240022401224022240322404224052240622407224082240922410224112241222413224142241522416224172241822419224202242122422224232242422425224262242722428224292243022431224322243322434224352243622437224382243922440224412244222443224442244522446224472244822449224502245122452224532245422455224562245722458224592246022461224622246322464224652246622467224682246922470224712247222473224742247522476224772247822479224802248122482224832248422485224862248722488224892249022491224922249322494224952249622497224982249922500225012250222503225042250522506225072250822509225102251122512225132251422515225162251722518225192252022521225222252322524225252252622527225282252922530225312253222533225342253522536225372253822539225402254122542225432254422545225462254722548225492255022551225522255322554225552255622557225582255922560225612256222563225642256522566225672256822569225702257122572225732257422575225762257722578225792258022581225822258322584225852258622587225882258922590225912259222593225942259522596225972259822599226002260122602226032260422605226062260722608226092261022611226122261322614226152261622617226182261922620226212262222623226242262522626226272262822629226302263122632226332263422635226362263722638226392264022641226422264322644226452264622647226482264922650226512265222653226542265522656226572265822659226602266122662226632266422665226662266722668226692267022671226722267322674226752267622677226782267922680226812268222683226842268522686226872268822689226902269122692226932269422695226962269722698226992270022701227022270322704227052270622707227082270922710227112271222713227142271522716227172271822719227202272122722227232272422725227262272722728227292273022731227322273322734227352273622737227382273922740227412274222743227442274522746227472274822749227502275122752227532275422755227562275722758227592276022761227622276322764227652276622767227682276922770227712277222773227742277522776227772277822779227802278122782227832278422785227862278722788227892279022791227922279322794227952279622797227982279922800228012280222803228042280522806228072280822809228102281122812228132281422815228162281722818228192282022821228222282322824228252282622827228282282922830228312283222833228342283522836228372283822839228402284122842228432284422845228462284722848228492285022851228522285322854228552285622857228582285922860228612286222863228642286522866228672286822869228702287122872228732287422875228762287722878228792288022881228822288322884228852288622887228882288922890228912289222893228942289522896228972289822899229002290122902229032290422905229062290722908229092291022911229122291322914229152291622917229182291922920229212292222923229242292522926229272292822929229302293122932229332293422935229362293722938229392294022941229422294322944229452294622947229482294922950229512295222953229542295522956229572295822959229602296122962229632296422965229662296722968229692297022971229722297322974229752297622977229782297922980229812298222983229842298522986229872298822989229902299122992229932299422995229962299722998229992300023001230022300323004230052300623007230082300923010230112301223013230142301523016230172301823019230202302123022230232302423025230262302723028230292303023031230322303323034230352303623037230382303923040230412304223043230442304523046230472304823049230502305123052230532305423055230562305723058230592306023061230622306323064230652306623067230682306923070230712307223073230742307523076230772307823079230802308123082230832308423085230862308723088230892309023091230922309323094230952309623097230982309923100231012310223103231042310523106231072310823109231102311123112231132311423115231162311723118231192312023121231222312323124231252312623127231282312923130231312313223133231342313523136231372313823139231402314123142231432314423145231462314723148231492315023151231522315323154231552315623157231582315923160231612316223163231642316523166231672316823169231702317123172231732317423175231762317723178231792318023181231822318323184231852318623187231882318923190231912319223193231942319523196231972319823199232002320123202232032320423205232062320723208232092321023211232122321323214232152321623217232182321923220232212322223223232242322523226232272322823229232302323123232232332323423235232362323723238232392324023241232422324323244232452324623247232482324923250232512325223253232542325523256232572325823259232602326123262232632326423265232662326723268232692327023271232722327323274232752327623277232782327923280232812328223283232842328523286232872328823289232902329123292232932329423295232962329723298232992330023301233022330323304233052330623307233082330923310233112331223313233142331523316233172331823319233202332123322233232332423325233262332723328233292333023331233322333323334233352333623337233382333923340233412334223343233442334523346233472334823349233502335123352233532335423355233562335723358233592336023361233622336323364233652336623367233682336923370233712337223373233742337523376233772337823379233802338123382233832338423385233862338723388233892339023391233922339323394233952339623397233982339923400234012340223403234042340523406234072340823409234102341123412234132341423415234162341723418234192342023421234222342323424234252342623427234282342923430234312343223433234342343523436234372343823439234402344123442234432344423445234462344723448234492345023451234522345323454234552345623457234582345923460234612346223463234642346523466234672346823469234702347123472234732347423475234762347723478234792348023481234822348323484234852348623487234882348923490234912349223493234942349523496234972349823499235002350123502235032350423505235062350723508235092351023511235122351323514235152351623517235182351923520235212352223523235242352523526235272352823529235302353123532235332353423535235362353723538235392354023541235422354323544235452354623547235482354923550235512355223553235542355523556235572355823559235602356123562235632356423565235662356723568235692357023571235722357323574235752357623577235782357923580235812358223583235842358523586235872358823589235902359123592235932359423595235962359723598235992360023601236022360323604236052360623607236082360923610236112361223613236142361523616236172361823619236202362123622236232362423625236262362723628236292363023631236322363323634236352363623637236382363923640236412364223643236442364523646236472364823649236502365123652236532365423655236562365723658236592366023661236622366323664236652366623667236682366923670236712367223673236742367523676236772367823679236802368123682236832368423685236862368723688236892369023691236922369323694236952369623697236982369923700237012370223703237042370523706237072370823709237102371123712237132371423715237162371723718237192372023721237222372323724237252372623727237282372923730237312373223733237342373523736237372373823739237402374123742237432374423745237462374723748237492375023751237522375323754237552375623757237582375923760237612376223763237642376523766237672376823769237702377123772237732377423775237762377723778237792378023781237822378323784237852378623787237882378923790237912379223793237942379523796237972379823799238002380123802238032380423805238062380723808238092381023811238122381323814238152381623817238182381923820238212382223823238242382523826238272382823829238302383123832238332383423835238362383723838238392384023841238422384323844238452384623847238482384923850238512385223853238542385523856238572385823859238602386123862238632386423865238662386723868238692387023871238722387323874238752387623877238782387923880238812388223883238842388523886238872388823889238902389123892238932389423895238962389723898238992390023901239022390323904239052390623907239082390923910239112391223913239142391523916239172391823919239202392123922239232392423925239262392723928239292393023931239322393323934239352393623937239382393923940239412394223943239442394523946239472394823949239502395123952239532395423955239562395723958239592396023961239622396323964239652396623967239682396923970239712397223973239742397523976239772397823979239802398123982239832398423985239862398723988239892399023991239922399323994239952399623997239982399924000240012400224003240042400524006240072400824009240102401124012240132401424015240162401724018240192402024021240222402324024240252402624027240282402924030240312403224033240342403524036240372403824039240402404124042240432404424045240462404724048240492405024051240522405324054240552405624057240582405924060240612406224063240642406524066240672406824069240702407124072240732407424075240762407724078240792408024081240822408324084240852408624087240882408924090240912409224093240942409524096240972409824099241002410124102241032410424105241062410724108241092411024111241122411324114241152411624117241182411924120241212412224123241242412524126241272412824129241302413124132241332413424135241362413724138241392414024141241422414324144241452414624147241482414924150241512415224153241542415524156241572415824159241602416124162241632416424165241662416724168241692417024171241722417324174241752417624177241782417924180241812418224183241842418524186241872418824189241902419124192241932419424195241962419724198241992420024201242022420324204242052420624207242082420924210242112421224213242142421524216242172421824219242202422124222242232422424225242262422724228242292423024231242322423324234242352423624237242382423924240242412424224243242442424524246242472424824249242502425124252242532425424255242562425724258242592426024261242622426324264242652426624267242682426924270242712427224273242742427524276242772427824279242802428124282242832428424285242862428724288242892429024291242922429324294242952429624297242982429924300243012430224303243042430524306243072430824309243102431124312243132431424315243162431724318243192432024321243222432324324243252432624327243282432924330243312433224333243342433524336243372433824339243402434124342243432434424345243462434724348243492435024351243522435324354243552435624357243582435924360243612436224363243642436524366243672436824369243702437124372243732437424375243762437724378243792438024381243822438324384243852438624387243882438924390243912439224393243942439524396243972439824399244002440124402244032440424405244062440724408244092441024411244122441324414244152441624417244182441924420244212442224423244242442524426244272442824429244302443124432244332443424435244362443724438244392444024441244422444324444244452444624447244482444924450244512445224453244542445524456244572445824459244602446124462244632446424465244662446724468244692447024471244722447324474244752447624477244782447924480244812448224483244842448524486244872448824489244902449124492244932449424495244962449724498244992450024501245022450324504245052450624507245082450924510245112451224513245142451524516245172451824519245202452124522245232452424525245262452724528245292453024531245322453324534245352453624537245382453924540245412454224543245442454524546245472454824549245502455124552245532455424555245562455724558245592456024561245622456324564245652456624567245682456924570245712457224573245742457524576245772457824579245802458124582245832458424585245862458724588245892459024591245922459324594245952459624597245982459924600246012460224603246042460524606246072460824609246102461124612246132461424615246162461724618246192462024621246222462324624246252462624627246282462924630246312463224633246342463524636246372463824639246402464124642246432464424645246462464724648246492465024651246522465324654246552465624657246582465924660246612466224663246642466524666246672466824669246702467124672246732467424675246762467724678246792468024681246822468324684246852468624687246882468924690246912469224693246942469524696246972469824699247002470124702247032470424705247062470724708247092471024711247122471324714247152471624717247182471924720247212472224723247242472524726247272472824729247302473124732247332473424735247362473724738247392474024741247422474324744247452474624747247482474924750247512475224753247542475524756247572475824759247602476124762247632476424765247662476724768247692477024771247722477324774247752477624777247782477924780247812478224783247842478524786247872478824789247902479124792247932479424795247962479724798247992480024801248022480324804248052480624807248082480924810248112481224813248142481524816248172481824819248202482124822248232482424825248262482724828248292483024831248322483324834248352483624837248382483924840248412484224843248442484524846248472484824849248502485124852248532485424855248562485724858248592486024861248622486324864248652486624867248682486924870248712487224873248742487524876248772487824879248802488124882248832488424885248862488724888248892489024891248922489324894248952489624897248982489924900249012490224903249042490524906249072490824909249102491124912249132491424915249162491724918249192492024921249222492324924249252492624927249282492924930249312493224933249342493524936249372493824939249402494124942249432494424945249462494724948249492495024951249522495324954249552495624957249582495924960249612496224963249642496524966249672496824969249702497124972249732497424975249762497724978249792498024981249822498324984249852498624987249882498924990249912499224993249942499524996249972499824999250002500125002250032500425005250062500725008250092501025011250122501325014250152501625017250182501925020250212502225023250242502525026250272502825029250302503125032250332503425035250362503725038250392504025041250422504325044250452504625047250482504925050250512505225053250542505525056250572505825059250602506125062250632506425065250662506725068250692507025071250722507325074250752507625077250782507925080250812508225083250842508525086250872508825089250902509125092250932509425095250962509725098250992510025101251022510325104251052510625107251082510925110251112511225113251142511525116251172511825119251202512125122251232512425125251262512725128251292513025131251322513325134251352513625137251382513925140251412514225143251442514525146251472514825149251502515125152251532515425155251562515725158251592516025161251622516325164251652516625167251682516925170251712517225173251742517525176251772517825179251802518125182251832518425185251862518725188251892519025191251922519325194251952519625197251982519925200252012520225203252042520525206252072520825209252102521125212252132521425215252162521725218252192522025221252222522325224252252522625227252282522925230252312523225233252342523525236252372523825239252402524125242252432524425245252462524725248252492525025251252522525325254252552525625257252582525925260252612526225263252642526525266252672526825269252702527125272252732527425275252762527725278252792528025281252822528325284252852528625287252882528925290252912529225293252942529525296252972529825299253002530125302253032530425305253062530725308253092531025311253122531325314253152531625317253182531925320253212532225323253242532525326253272532825329253302533125332253332533425335253362533725338253392534025341253422534325344253452534625347253482534925350253512535225353253542535525356253572535825359253602536125362253632536425365253662536725368253692537025371253722537325374253752537625377253782537925380253812538225383253842538525386253872538825389253902539125392253932539425395253962539725398253992540025401254022540325404254052540625407254082540925410254112541225413254142541525416254172541825419254202542125422254232542425425254262542725428254292543025431254322543325434254352543625437254382543925440254412544225443254442544525446254472544825449254502545125452254532545425455254562545725458254592546025461254622546325464254652546625467254682546925470254712547225473254742547525476254772547825479254802548125482254832548425485254862548725488254892549025491254922549325494254952549625497254982549925500255012550225503255042550525506255072550825509255102551125512255132551425515255162551725518255192552025521255222552325524255252552625527255282552925530255312553225533255342553525536255372553825539255402554125542255432554425545255462554725548255492555025551255522555325554255552555625557255582555925560255612556225563255642556525566255672556825569255702557125572255732557425575255762557725578255792558025581255822558325584255852558625587255882558925590255912559225593255942559525596255972559825599256002560125602256032560425605256062560725608256092561025611256122561325614256152561625617256182561925620256212562225623256242562525626256272562825629256302563125632256332563425635256362563725638256392564025641256422564325644256452564625647256482564925650256512565225653256542565525656256572565825659256602566125662256632566425665256662566725668256692567025671256722567325674256752567625677256782567925680256812568225683256842568525686256872568825689256902569125692256932569425695256962569725698256992570025701257022570325704257052570625707257082570925710257112571225713257142571525716257172571825719257202572125722257232572425725257262572725728257292573025731257322573325734257352573625737257382573925740257412574225743257442574525746257472574825749257502575125752257532575425755257562575725758257592576025761257622576325764257652576625767257682576925770257712577225773257742577525776257772577825779257802578125782257832578425785257862578725788257892579025791257922579325794257952579625797257982579925800258012580225803258042580525806258072580825809258102581125812258132581425815258162581725818258192582025821258222582325824258252582625827258282582925830258312583225833258342583525836258372583825839258402584125842258432584425845258462584725848258492585025851258522585325854258552585625857258582585925860258612586225863258642586525866258672586825869258702587125872258732587425875258762587725878258792588025881258822588325884258852588625887258882588925890258912589225893258942589525896258972589825899259002590125902259032590425905259062590725908259092591025911259122591325914259152591625917259182591925920259212592225923259242592525926259272592825929259302593125932259332593425935259362593725938259392594025941259422594325944259452594625947259482594925950259512595225953259542595525956259572595825959259602596125962259632596425965259662596725968259692597025971259722597325974259752597625977259782597925980259812598225983259842598525986259872598825989259902599125992259932599425995259962599725998259992600026001260022600326004260052600626007260082600926010260112601226013260142601526016260172601826019260202602126022260232602426025260262602726028260292603026031260322603326034260352603626037260382603926040260412604226043260442604526046260472604826049260502605126052260532605426055260562605726058260592606026061260622606326064260652606626067260682606926070260712607226073260742607526076260772607826079260802608126082260832608426085260862608726088260892609026091260922609326094260952609626097260982609926100261012610226103261042610526106261072610826109261102611126112261132611426115261162611726118261192612026121261222612326124261252612626127261282612926130261312613226133261342613526136261372613826139261402614126142261432614426145261462614726148261492615026151261522615326154261552615626157261582615926160261612616226163261642616526166261672616826169261702617126172261732617426175261762617726178261792618026181261822618326184261852618626187261882618926190261912619226193261942619526196261972619826199262002620126202262032620426205262062620726208262092621026211262122621326214262152621626217262182621926220262212622226223262242622526226262272622826229262302623126232262332623426235262362623726238262392624026241262422624326244262452624626247262482624926250262512625226253262542625526256262572625826259262602626126262262632626426265262662626726268262692627026271262722627326274262752627626277262782627926280262812628226283262842628526286262872628826289262902629126292262932629426295262962629726298262992630026301263022630326304263052630626307263082630926310263112631226313263142631526316263172631826319263202632126322263232632426325263262632726328263292633026331263322633326334263352633626337263382633926340263412634226343263442634526346263472634826349263502635126352263532635426355263562635726358263592636026361263622636326364263652636626367263682636926370263712637226373263742637526376263772637826379263802638126382263832638426385263862638726388263892639026391263922639326394263952639626397263982639926400264012640226403264042640526406264072640826409264102641126412264132641426415264162641726418264192642026421264222642326424264252642626427264282642926430264312643226433264342643526436264372643826439264402644126442264432644426445264462644726448264492645026451264522645326454264552645626457264582645926460264612646226463264642646526466264672646826469264702647126472264732647426475264762647726478264792648026481264822648326484264852648626487264882648926490264912649226493264942649526496264972649826499265002650126502265032650426505265062650726508265092651026511265122651326514265152651626517265182651926520265212652226523265242652526526265272652826529265302653126532265332653426535265362653726538265392654026541265422654326544265452654626547265482654926550265512655226553265542655526556265572655826559265602656126562265632656426565265662656726568265692657026571265722657326574265752657626577265782657926580265812658226583265842658526586265872658826589265902659126592265932659426595265962659726598265992660026601266022660326604266052660626607266082660926610266112661226613266142661526616266172661826619266202662126622266232662426625266262662726628266292663026631266322663326634266352663626637266382663926640266412664226643266442664526646266472664826649266502665126652266532665426655266562665726658266592666026661266622666326664266652666626667266682666926670266712667226673266742667526676266772667826679266802668126682266832668426685266862668726688266892669026691266922669326694266952669626697266982669926700267012670226703267042670526706267072670826709267102671126712267132671426715267162671726718267192672026721267222672326724267252672626727267282672926730267312673226733267342673526736267372673826739267402674126742267432674426745267462674726748267492675026751267522675326754267552675626757267582675926760267612676226763267642676526766267672676826769267702677126772267732677426775267762677726778267792678026781267822678326784267852678626787267882678926790267912679226793267942679526796267972679826799268002680126802268032680426805268062680726808268092681026811268122681326814268152681626817268182681926820268212682226823268242682526826268272682826829268302683126832268332683426835268362683726838268392684026841268422684326844268452684626847268482684926850268512685226853268542685526856268572685826859268602686126862268632686426865268662686726868268692687026871268722687326874268752687626877268782687926880268812688226883268842688526886268872688826889268902689126892268932689426895268962689726898268992690026901269022690326904269052690626907269082690926910269112691226913269142691526916269172691826919269202692126922269232692426925269262692726928269292693026931269322693326934269352693626937269382693926940269412694226943269442694526946269472694826949269502695126952269532695426955269562695726958269592696026961269622696326964269652696626967269682696926970269712697226973269742697526976269772697826979269802698126982269832698426985269862698726988269892699026991269922699326994269952699626997269982699927000270012700227003270042700527006270072700827009270102701127012270132701427015270162701727018270192702027021270222702327024270252702627027270282702927030270312703227033270342703527036270372703827039270402704127042270432704427045270462704727048270492705027051270522705327054270552705627057270582705927060270612706227063270642706527066270672706827069270702707127072270732707427075270762707727078270792708027081270822708327084270852708627087270882708927090270912709227093270942709527096270972709827099271002710127102271032710427105271062710727108271092711027111271122711327114271152711627117271182711927120271212712227123271242712527126271272712827129271302713127132271332713427135271362713727138271392714027141271422714327144271452714627147271482714927150271512715227153271542715527156271572715827159271602716127162271632716427165271662716727168271692717027171271722717327174271752717627177271782717927180271812718227183271842718527186271872718827189271902719127192271932719427195271962719727198271992720027201272022720327204272052720627207272082720927210272112721227213272142721527216272172721827219272202722127222272232722427225272262722727228272292723027231272322723327234272352723627237272382723927240272412724227243272442724527246272472724827249272502725127252272532725427255272562725727258272592726027261272622726327264272652726627267272682726927270272712727227273272742727527276272772727827279272802728127282272832728427285272862728727288272892729027291272922729327294272952729627297272982729927300273012730227303273042730527306273072730827309273102731127312273132731427315273162731727318273192732027321273222732327324273252732627327273282732927330273312733227333273342733527336273372733827339273402734127342273432734427345273462734727348273492735027351273522735327354273552735627357273582735927360273612736227363273642736527366273672736827369273702737127372273732737427375273762737727378273792738027381273822738327384273852738627387273882738927390273912739227393273942739527396273972739827399274002740127402274032740427405274062740727408274092741027411274122741327414274152741627417274182741927420274212742227423274242742527426274272742827429274302743127432274332743427435274362743727438274392744027441274422744327444274452744627447274482744927450274512745227453274542745527456274572745827459274602746127462274632746427465274662746727468274692747027471274722747327474274752747627477274782747927480274812748227483274842748527486274872748827489274902749127492274932749427495274962749727498274992750027501275022750327504275052750627507275082750927510275112751227513275142751527516275172751827519275202752127522275232752427525275262752727528275292753027531275322753327534275352753627537275382753927540275412754227543275442754527546275472754827549275502755127552275532755427555275562755727558275592756027561275622756327564275652756627567275682756927570275712757227573275742757527576275772757827579275802758127582275832758427585275862758727588275892759027591275922759327594275952759627597275982759927600276012760227603276042760527606276072760827609276102761127612276132761427615276162761727618276192762027621276222762327624276252762627627276282762927630276312763227633276342763527636276372763827639276402764127642276432764427645276462764727648276492765027651276522765327654276552765627657276582765927660276612766227663276642766527666276672766827669276702767127672276732767427675276762767727678276792768027681276822768327684276852768627687276882768927690276912769227693276942769527696276972769827699277002770127702277032770427705277062770727708277092771027711277122771327714277152771627717277182771927720277212772227723277242772527726277272772827729277302773127732277332773427735277362773727738277392774027741277422774327744277452774627747277482774927750277512775227753277542775527756277572775827759277602776127762277632776427765277662776727768277692777027771277722777327774277752777627777277782777927780277812778227783277842778527786277872778827789277902779127792277932779427795277962779727798277992780027801278022780327804278052780627807278082780927810278112781227813278142781527816278172781827819278202782127822278232782427825278262782727828278292783027831278322783327834278352783627837278382783927840278412784227843278442784527846278472784827849278502785127852278532785427855278562785727858278592786027861278622786327864278652786627867278682786927870278712787227873278742787527876278772787827879278802788127882278832788427885278862788727888278892789027891278922789327894278952789627897278982789927900279012790227903279042790527906279072790827909279102791127912279132791427915279162791727918279192792027921279222792327924279252792627927279282792927930279312793227933279342793527936279372793827939279402794127942279432794427945279462794727948279492795027951279522795327954279552795627957279582795927960279612796227963279642796527966279672796827969279702797127972279732797427975279762797727978279792798027981279822798327984279852798627987279882798927990279912799227993279942799527996279972799827999280002800128002280032800428005280062800728008280092801028011280122801328014280152801628017280182801928020280212802228023280242802528026280272802828029280302803128032280332803428035280362803728038280392804028041280422804328044280452804628047280482804928050280512805228053280542805528056280572805828059280602806128062280632806428065280662806728068280692807028071280722807328074280752807628077280782807928080280812808228083280842808528086280872808828089280902809128092280932809428095280962809728098280992810028101281022810328104281052810628107281082810928110281112811228113281142811528116281172811828119281202812128122281232812428125281262812728128281292813028131281322813328134281352813628137281382813928140281412814228143281442814528146281472814828149281502815128152281532815428155281562815728158281592816028161281622816328164281652816628167281682816928170281712817228173281742817528176281772817828179281802818128182281832818428185281862818728188281892819028191281922819328194281952819628197281982819928200282012820228203282042820528206282072820828209282102821128212282132821428215282162821728218282192822028221282222822328224282252822628227282282822928230282312823228233282342823528236282372823828239282402824128242282432824428245282462824728248282492825028251282522825328254282552825628257282582825928260282612826228263282642826528266282672826828269282702827128272282732827428275282762827728278282792828028281282822828328284282852828628287282882828928290282912829228293282942829528296282972829828299283002830128302283032830428305283062830728308283092831028311283122831328314283152831628317283182831928320283212832228323283242832528326283272832828329283302833128332283332833428335283362833728338283392834028341283422834328344283452834628347283482834928350283512835228353283542835528356283572835828359283602836128362283632836428365283662836728368283692837028371283722837328374283752837628377283782837928380283812838228383283842838528386283872838828389283902839128392283932839428395283962839728398283992840028401284022840328404284052840628407284082840928410284112841228413284142841528416284172841828419284202842128422284232842428425284262842728428284292843028431284322843328434284352843628437284382843928440284412844228443284442844528446284472844828449284502845128452284532845428455284562845728458284592846028461284622846328464284652846628467284682846928470284712847228473284742847528476284772847828479284802848128482284832848428485284862848728488284892849028491284922849328494284952849628497284982849928500285012850228503285042850528506285072850828509285102851128512285132851428515285162851728518285192852028521285222852328524285252852628527285282852928530285312853228533285342853528536285372853828539285402854128542285432854428545285462854728548285492855028551285522855328554285552855628557285582855928560285612856228563285642856528566285672856828569285702857128572285732857428575285762857728578285792858028581285822858328584285852858628587285882858928590285912859228593285942859528596285972859828599286002860128602286032860428605286062860728608286092861028611286122861328614286152861628617286182861928620286212862228623286242862528626286272862828629286302863128632286332863428635286362863728638286392864028641286422864328644286452864628647286482864928650286512865228653286542865528656286572865828659286602866128662286632866428665286662866728668286692867028671286722867328674286752867628677286782867928680286812868228683286842868528686286872868828689286902869128692286932869428695286962869728698286992870028701287022870328704287052870628707287082870928710287112871228713287142871528716287172871828719287202872128722287232872428725287262872728728287292873028731287322873328734287352873628737287382873928740287412874228743287442874528746287472874828749287502875128752287532875428755287562875728758287592876028761287622876328764287652876628767287682876928770287712877228773287742877528776287772877828779287802878128782287832878428785287862878728788287892879028791287922879328794287952879628797287982879928800288012880228803288042880528806288072880828809288102881128812288132881428815288162881728818288192882028821288222882328824288252882628827288282882928830288312883228833288342883528836288372883828839288402884128842288432884428845288462884728848288492885028851288522885328854288552885628857288582885928860288612886228863288642886528866288672886828869288702887128872288732887428875288762887728878288792888028881288822888328884288852888628887288882888928890288912889228893288942889528896288972889828899289002890128902289032890428905289062890728908289092891028911289122891328914289152891628917289182891928920289212892228923289242892528926289272892828929289302893128932289332893428935289362893728938289392894028941289422894328944289452894628947289482894928950289512895228953289542895528956289572895828959289602896128962289632896428965289662896728968289692897028971289722897328974289752897628977289782897928980289812898228983289842898528986289872898828989289902899128992289932899428995289962899728998289992900029001290022900329004290052900629007290082900929010290112901229013290142901529016290172901829019290202902129022290232902429025290262902729028290292903029031290322903329034290352903629037290382903929040290412904229043290442904529046290472904829049290502905129052290532905429055290562905729058290592906029061290622906329064290652906629067290682906929070290712907229073290742907529076290772907829079290802908129082290832908429085290862908729088290892909029091290922909329094290952909629097290982909929100291012910229103291042910529106291072910829109291102911129112291132911429115291162911729118291192912029121291222912329124291252912629127291282912929130291312913229133291342913529136291372913829139291402914129142291432914429145291462914729148291492915029151291522915329154291552915629157291582915929160291612916229163291642916529166291672916829169291702917129172291732917429175291762917729178291792918029181291822918329184291852918629187291882918929190291912919229193291942919529196291972919829199292002920129202292032920429205292062920729208292092921029211292122921329214292152921629217292182921929220292212922229223292242922529226292272922829229292302923129232292332923429235292362923729238292392924029241292422924329244292452924629247292482924929250292512925229253292542925529256292572925829259292602926129262292632926429265292662926729268292692927029271292722927329274292752927629277292782927929280292812928229283292842928529286292872928829289292902929129292292932929429295292962929729298292992930029301293022930329304293052930629307293082930929310293112931229313293142931529316293172931829319293202932129322293232932429325293262932729328293292933029331293322933329334293352933629337293382933929340293412934229343293442934529346293472934829349293502935129352293532935429355293562935729358293592936029361293622936329364293652936629367293682936929370293712937229373293742937529376293772937829379293802938129382293832938429385293862938729388293892939029391293922939329394293952939629397293982939929400294012940229403294042940529406294072940829409294102941129412294132941429415294162941729418294192942029421294222942329424294252942629427294282942929430294312943229433294342943529436294372943829439294402944129442294432944429445294462944729448294492945029451294522945329454294552945629457294582945929460294612946229463294642946529466294672946829469294702947129472294732947429475294762947729478294792948029481294822948329484294852948629487294882948929490294912949229493294942949529496294972949829499295002950129502295032950429505295062950729508295092951029511295122951329514295152951629517295182951929520295212952229523295242952529526295272952829529295302953129532295332953429535295362953729538295392954029541295422954329544295452954629547295482954929550295512955229553295542955529556295572955829559295602956129562295632956429565295662956729568295692957029571295722957329574295752957629577295782957929580295812958229583295842958529586295872958829589295902959129592295932959429595295962959729598295992960029601296022960329604296052960629607296082960929610296112961229613296142961529616296172961829619296202962129622296232962429625296262962729628296292963029631296322963329634296352963629637296382963929640296412964229643296442964529646296472964829649296502965129652296532965429655296562965729658296592966029661296622966329664296652966629667296682966929670296712967229673296742967529676296772967829679296802968129682296832968429685296862968729688296892969029691296922969329694296952969629697296982969929700297012970229703297042970529706297072970829709297102971129712297132971429715297162971729718297192972029721297222972329724297252972629727297282972929730297312973229733297342973529736297372973829739297402974129742297432974429745297462974729748297492975029751297522975329754297552975629757297582975929760297612976229763297642976529766297672976829769297702977129772297732977429775297762977729778297792978029781297822978329784297852978629787297882978929790297912979229793297942979529796297972979829799298002980129802298032980429805298062980729808298092981029811298122981329814298152981629817298182981929820298212982229823298242982529826298272982829829298302983129832298332983429835298362983729838298392984029841298422984329844298452984629847298482984929850298512985229853298542985529856298572985829859298602986129862298632986429865298662986729868298692987029871298722987329874298752987629877298782987929880298812988229883298842988529886298872988829889298902989129892298932989429895298962989729898298992990029901299022990329904299052990629907299082990929910299112991229913299142991529916299172991829919299202992129922299232992429925299262992729928299292993029931299322993329934299352993629937299382993929940299412994229943299442994529946299472994829949299502995129952299532995429955299562995729958299592996029961299622996329964299652996629967299682996929970299712997229973299742997529976299772997829979299802998129982299832998429985299862998729988299892999029991299922999329994299952999629997299982999930000300013000230003300043000530006300073000830009300103001130012300133001430015300163001730018300193002030021300223002330024300253002630027300283002930030300313003230033300343003530036300373003830039300403004130042300433004430045300463004730048300493005030051300523005330054300553005630057300583005930060300613006230063300643006530066300673006830069300703007130072300733007430075300763007730078300793008030081300823008330084300853008630087300883008930090300913009230093300943009530096300973009830099301003010130102301033010430105301063010730108301093011030111301123011330114301153011630117301183011930120301213012230123301243012530126301273012830129301303013130132301333013430135301363013730138301393014030141301423014330144301453014630147301483014930150301513015230153301543015530156301573015830159301603016130162301633016430165301663016730168301693017030171301723017330174301753017630177301783017930180301813018230183301843018530186301873018830189301903019130192301933019430195301963019730198301993020030201302023020330204302053020630207302083020930210302113021230213302143021530216302173021830219302203022130222302233022430225302263022730228302293023030231302323023330234302353023630237302383023930240302413024230243302443024530246302473024830249302503025130252302533025430255302563025730258302593026030261302623026330264302653026630267302683026930270302713027230273302743027530276302773027830279302803028130282302833028430285302863028730288302893029030291302923029330294302953029630297302983029930300303013030230303303043030530306303073030830309303103031130312303133031430315303163031730318303193032030321303223032330324303253032630327303283032930330303313033230333303343033530336303373033830339303403034130342303433034430345303463034730348303493035030351303523035330354303553035630357303583035930360303613036230363303643036530366303673036830369303703037130372303733037430375303763037730378303793038030381303823038330384303853038630387303883038930390303913039230393303943039530396303973039830399304003040130402304033040430405304063040730408304093041030411304123041330414304153041630417304183041930420304213042230423304243042530426304273042830429304303043130432304333043430435304363043730438304393044030441304423044330444304453044630447304483044930450304513045230453304543045530456304573045830459304603046130462304633046430465304663046730468304693047030471304723047330474304753047630477304783047930480304813048230483304843048530486304873048830489304903049130492304933049430495304963049730498304993050030501305023050330504305053050630507305083050930510305113051230513305143051530516305173051830519305203052130522305233052430525305263052730528305293053030531305323053330534305353053630537305383053930540305413054230543305443054530546305473054830549305503055130552305533055430555305563055730558305593056030561305623056330564305653056630567305683056930570305713057230573305743057530576305773057830579305803058130582305833058430585305863058730588305893059030591305923059330594305953059630597305983059930600306013060230603306043060530606306073060830609306103061130612306133061430615306163061730618306193062030621306223062330624306253062630627306283062930630306313063230633306343063530636306373063830639306403064130642306433064430645306463064730648306493065030651306523065330654306553065630657306583065930660306613066230663306643066530666306673066830669306703067130672306733067430675306763067730678306793068030681306823068330684306853068630687306883068930690306913069230693306943069530696306973069830699307003070130702307033070430705307063070730708307093071030711307123071330714307153071630717307183071930720307213072230723307243072530726307273072830729307303073130732307333073430735307363073730738307393074030741307423074330744307453074630747307483074930750307513075230753307543075530756307573075830759307603076130762307633076430765307663076730768307693077030771307723077330774307753077630777307783077930780307813078230783307843078530786307873078830789307903079130792307933079430795307963079730798307993080030801308023080330804308053080630807308083080930810308113081230813308143081530816308173081830819308203082130822308233082430825308263082730828308293083030831308323083330834308353083630837308383083930840308413084230843308443084530846308473084830849308503085130852308533085430855308563085730858308593086030861308623086330864308653086630867308683086930870308713087230873308743087530876308773087830879308803088130882308833088430885308863088730888308893089030891308923089330894308953089630897308983089930900309013090230903309043090530906309073090830909309103091130912309133091430915309163091730918309193092030921309223092330924309253092630927309283092930930309313093230933309343093530936309373093830939309403094130942309433094430945309463094730948309493095030951309523095330954309553095630957309583095930960309613096230963309643096530966309673096830969309703097130972309733097430975309763097730978309793098030981309823098330984309853098630987309883098930990309913099230993309943099530996309973099830999310003100131002310033100431005310063100731008310093101031011310123101331014310153101631017310183101931020310213102231023310243102531026310273102831029310303103131032310333103431035310363103731038310393104031041310423104331044310453104631047310483104931050310513105231053310543105531056310573105831059310603106131062310633106431065310663106731068310693107031071310723107331074310753107631077310783107931080310813108231083310843108531086310873108831089310903109131092310933109431095310963109731098310993110031101311023110331104311053110631107311083110931110311113111231113311143111531116311173111831119311203112131122311233112431125311263112731128311293113031131311323113331134311353113631137311383113931140311413114231143311443114531146311473114831149311503115131152311533115431155311563115731158311593116031161311623116331164311653116631167311683116931170311713117231173311743117531176311773117831179311803118131182311833118431185311863118731188311893119031191311923119331194311953119631197311983119931200312013120231203312043120531206312073120831209312103121131212312133121431215312163121731218312193122031221312223122331224312253122631227312283122931230312313123231233312343123531236312373123831239312403124131242312433124431245312463124731248312493125031251312523125331254312553125631257312583125931260312613126231263312643126531266312673126831269312703127131272312733127431275312763127731278312793128031281312823128331284312853128631287312883128931290312913129231293312943129531296312973129831299313003130131302313033130431305313063130731308313093131031311313123131331314313153131631317313183131931320313213132231323313243132531326313273132831329313303133131332313333133431335313363133731338313393134031341313423134331344313453134631347313483134931350313513135231353313543135531356313573135831359313603136131362313633136431365313663136731368313693137031371313723137331374313753137631377313783137931380313813138231383313843138531386313873138831389313903139131392313933139431395313963139731398313993140031401314023140331404314053140631407314083140931410314113141231413314143141531416314173141831419314203142131422314233142431425314263142731428314293143031431314323143331434314353143631437314383143931440314413144231443314443144531446314473144831449314503145131452314533145431455314563145731458314593146031461314623146331464314653146631467314683146931470314713147231473314743147531476314773147831479314803148131482314833148431485314863148731488314893149031491314923149331494314953149631497314983149931500315013150231503315043150531506315073150831509315103151131512315133151431515315163151731518315193152031521315223152331524315253152631527315283152931530315313153231533315343153531536315373153831539315403154131542315433154431545315463154731548315493155031551315523155331554315553155631557315583155931560315613156231563315643156531566315673156831569315703157131572315733157431575315763157731578315793158031581315823158331584315853158631587315883158931590315913159231593315943159531596315973159831599316003160131602316033160431605316063160731608316093161031611316123161331614316153161631617316183161931620316213162231623316243162531626316273162831629316303163131632316333163431635316363163731638316393164031641316423164331644316453164631647316483164931650316513165231653316543165531656316573165831659316603166131662316633166431665316663166731668316693167031671316723167331674316753167631677316783167931680316813168231683316843168531686316873168831689316903169131692316933169431695316963169731698316993170031701317023170331704317053170631707317083170931710317113171231713317143171531716317173171831719317203172131722317233172431725317263172731728317293173031731317323173331734317353173631737317383173931740317413174231743317443174531746317473174831749317503175131752317533175431755317563175731758317593176031761317623176331764317653176631767317683176931770317713177231773317743177531776317773177831779317803178131782317833178431785317863178731788317893179031791317923179331794317953179631797317983179931800318013180231803318043180531806318073180831809318103181131812318133181431815318163181731818318193182031821318223182331824318253182631827318283182931830318313183231833318343183531836318373183831839318403184131842318433184431845318463184731848318493185031851318523185331854318553185631857318583185931860318613186231863318643186531866318673186831869318703187131872318733187431875318763187731878318793188031881318823188331884318853188631887318883188931890318913189231893318943189531896318973189831899319003190131902319033190431905319063190731908319093191031911319123191331914319153191631917319183191931920319213192231923319243192531926319273192831929319303193131932319333193431935319363193731938319393194031941319423194331944319453194631947319483194931950319513195231953319543195531956319573195831959319603196131962319633196431965319663196731968319693197031971319723197331974319753197631977319783197931980319813198231983319843198531986319873198831989319903199131992319933199431995319963199731998319993200032001320023200332004320053200632007320083200932010320113201232013320143201532016320173201832019320203202132022320233202432025320263202732028320293203032031320323203332034320353203632037320383203932040320413204232043320443204532046320473204832049320503205132052320533205432055320563205732058320593206032061320623206332064320653206632067320683206932070320713207232073320743207532076320773207832079320803208132082320833208432085320863208732088320893209032091320923209332094320953209632097320983209932100321013210232103321043210532106321073210832109321103211132112321133211432115321163211732118321193212032121321223212332124321253212632127321283212932130321313213232133321343213532136321373213832139321403214132142321433214432145321463214732148321493215032151321523215332154321553215632157321583215932160321613216232163321643216532166321673216832169321703217132172321733217432175321763217732178321793218032181321823218332184321853218632187321883218932190321913219232193321943219532196321973219832199322003220132202322033220432205322063220732208322093221032211322123221332214322153221632217322183221932220322213222232223322243222532226322273222832229322303223132232322333223432235322363223732238322393224032241322423224332244322453224632247322483224932250322513225232253322543225532256322573225832259322603226132262322633226432265322663226732268322693227032271322723227332274322753227632277322783227932280322813228232283322843228532286322873228832289322903229132292322933229432295322963229732298322993230032301323023230332304323053230632307323083230932310323113231232313323143231532316323173231832319323203232132322323233232432325323263232732328323293233032331323323233332334323353233632337323383233932340323413234232343323443234532346323473234832349323503235132352323533235432355323563235732358323593236032361323623236332364323653236632367323683236932370323713237232373323743237532376323773237832379323803238132382323833238432385323863238732388323893239032391323923239332394323953239632397323983239932400324013240232403324043240532406324073240832409324103241132412324133241432415324163241732418324193242032421324223242332424324253242632427324283242932430324313243232433324343243532436324373243832439324403244132442324433244432445324463244732448324493245032451324523245332454324553245632457324583245932460324613246232463324643246532466324673246832469324703247132472324733247432475324763247732478324793248032481324823248332484324853248632487324883248932490324913249232493324943249532496324973249832499325003250132502325033250432505325063250732508325093251032511325123251332514325153251632517325183251932520325213252232523325243252532526325273252832529325303253132532325333253432535325363253732538325393254032541325423254332544325453254632547325483254932550325513255232553325543255532556325573255832559325603256132562325633256432565325663256732568325693257032571325723257332574325753257632577325783257932580325813258232583325843258532586325873258832589325903259132592325933259432595325963259732598325993260032601326023260332604326053260632607326083260932610326113261232613326143261532616326173261832619326203262132622326233262432625326263262732628326293263032631326323263332634326353263632637326383263932640326413264232643326443264532646326473264832649326503265132652326533265432655326563265732658326593266032661326623266332664326653266632667326683266932670326713267232673326743267532676326773267832679326803268132682326833268432685326863268732688326893269032691326923269332694326953269632697326983269932700327013270232703327043270532706327073270832709327103271132712327133271432715327163271732718327193272032721327223272332724327253272632727327283272932730327313273232733327343273532736327373273832739327403274132742327433274432745327463274732748327493275032751327523275332754327553275632757327583275932760327613276232763327643276532766327673276832769327703277132772327733277432775327763277732778327793278032781327823278332784327853278632787327883278932790327913279232793327943279532796327973279832799328003280132802328033280432805328063280732808328093281032811328123281332814328153281632817328183281932820328213282232823328243282532826328273282832829328303283132832328333283432835328363283732838328393284032841328423284332844328453284632847328483284932850328513285232853328543285532856328573285832859328603286132862328633286432865328663286732868328693287032871328723287332874328753287632877328783287932880328813288232883328843288532886328873288832889328903289132892328933289432895328963289732898328993290032901329023290332904329053290632907329083290932910329113291232913329143291532916329173291832919329203292132922329233292432925329263292732928329293293032931329323293332934329353293632937329383293932940329413294232943329443294532946329473294832949329503295132952329533295432955329563295732958329593296032961329623296332964329653296632967329683296932970329713297232973329743297532976329773297832979329803298132982329833298432985329863298732988329893299032991329923299332994329953299632997329983299933000330013300233003330043300533006330073300833009330103301133012330133301433015330163301733018330193302033021330223302333024330253302633027330283302933030330313303233033330343303533036330373303833039330403304133042330433304433045330463304733048330493305033051330523305333054330553305633057330583305933060330613306233063330643306533066330673306833069330703307133072330733307433075330763307733078330793308033081330823308333084330853308633087330883308933090330913309233093330943309533096330973309833099331003310133102331033310433105331063310733108331093311033111331123311333114331153311633117331183311933120331213312233123331243312533126331273312833129331303313133132331333313433135331363313733138331393314033141331423314333144331453314633147331483314933150331513315233153331543315533156331573315833159331603316133162331633316433165331663316733168331693317033171331723317333174331753317633177331783317933180331813318233183331843318533186331873318833189331903319133192331933319433195331963319733198331993320033201332023320333204332053320633207332083320933210332113321233213332143321533216332173321833219332203322133222332233322433225332263322733228332293323033231332323323333234332353323633237332383323933240332413324233243332443324533246332473324833249332503325133252332533325433255332563325733258332593326033261332623326333264332653326633267332683326933270332713327233273332743327533276332773327833279332803328133282332833328433285332863328733288332893329033291332923329333294332953329633297332983329933300333013330233303333043330533306333073330833309333103331133312333133331433315333163331733318333193332033321333223332333324333253332633327333283332933330333313333233333333343333533336333373333833339333403334133342333433334433345333463334733348333493335033351333523335333354333553335633357333583335933360333613336233363333643336533366333673336833369333703337133372333733337433375333763337733378333793338033381333823338333384333853338633387333883338933390333913339233393333943339533396333973339833399334003340133402334033340433405334063340733408334093341033411334123341333414334153341633417334183341933420334213342233423334243342533426334273342833429334303343133432334333343433435334363343733438334393344033441334423344333444334453344633447334483344933450334513345233453334543345533456334573345833459334603346133462334633346433465334663346733468334693347033471334723347333474334753347633477334783347933480334813348233483334843348533486334873348833489334903349133492334933349433495334963349733498334993350033501335023350333504335053350633507335083350933510335113351233513335143351533516335173351833519335203352133522335233352433525335263352733528335293353033531335323353333534335353353633537335383353933540335413354233543335443354533546335473354833549335503355133552335533355433555335563355733558335593356033561335623356333564335653356633567335683356933570335713357233573335743357533576335773357833579335803358133582335833358433585335863358733588335893359033591335923359333594335953359633597335983359933600336013360233603336043360533606336073360833609336103361133612336133361433615336163361733618336193362033621336223362333624336253362633627336283362933630336313363233633336343363533636336373363833639336403364133642336433364433645336463364733648336493365033651336523365333654336553365633657336583365933660336613366233663336643366533666336673366833669336703367133672336733367433675336763367733678336793368033681336823368333684336853368633687336883368933690336913369233693336943369533696336973369833699337003370133702337033370433705337063370733708337093371033711337123371333714337153371633717337183371933720337213372233723337243372533726337273372833729337303373133732337333373433735337363373733738337393374033741337423374333744337453374633747337483374933750337513375233753337543375533756337573375833759337603376133762337633376433765337663376733768337693377033771337723377333774337753377633777337783377933780337813378233783337843378533786337873378833789337903379133792337933379433795337963379733798337993380033801338023380333804338053380633807338083380933810338113381233813338143381533816338173381833819338203382133822338233382433825338263382733828338293383033831338323383333834338353383633837338383383933840338413384233843338443384533846338473384833849338503385133852338533385433855338563385733858338593386033861338623386333864338653386633867338683386933870338713387233873338743387533876338773387833879338803388133882338833388433885338863388733888338893389033891338923389333894338953389633897338983389933900339013390233903339043390533906339073390833909339103391133912339133391433915339163391733918339193392033921339223392333924339253392633927339283392933930339313393233933339343393533936339373393833939339403394133942339433394433945339463394733948339493395033951339523395333954339553395633957339583395933960339613396233963339643396533966339673396833969339703397133972339733397433975339763397733978339793398033981339823398333984339853398633987339883398933990339913399233993339943399533996339973399833999340003400134002340033400434005340063400734008340093401034011340123401334014340153401634017340183401934020340213402234023340243402534026340273402834029340303403134032340333403434035340363403734038340393404034041340423404334044340453404634047340483404934050340513405234053340543405534056340573405834059340603406134062340633406434065340663406734068340693407034071340723407334074340753407634077340783407934080340813408234083340843408534086340873408834089340903409134092340933409434095340963409734098340993410034101341023410334104341053410634107341083410934110341113411234113341143411534116341173411834119341203412134122341233412434125341263412734128341293413034131341323413334134341353413634137341383413934140341413414234143341443414534146341473414834149341503415134152341533415434155341563415734158341593416034161341623416334164341653416634167341683416934170341713417234173341743417534176341773417834179341803418134182341833418434185341863418734188341893419034191341923419334194341953419634197341983419934200342013420234203342043420534206342073420834209342103421134212342133421434215342163421734218342193422034221342223422334224342253422634227342283422934230342313423234233342343423534236342373423834239342403424134242342433424434245342463424734248342493425034251342523425334254342553425634257342583425934260342613426234263342643426534266342673426834269342703427134272342733427434275342763427734278342793428034281342823428334284342853428634287342883428934290342913429234293342943429534296342973429834299343003430134302343033430434305343063430734308343093431034311343123431334314343153431634317343183431934320343213432234323343243432534326343273432834329343303433134332343333433434335343363433734338343393434034341343423434334344343453434634347343483434934350343513435234353343543435534356343573435834359343603436134362343633436434365343663436734368343693437034371343723437334374343753437634377343783437934380343813438234383343843438534386343873438834389343903439134392343933439434395343963439734398343993440034401344023440334404344053440634407344083440934410344113441234413344143441534416344173441834419344203442134422344233442434425344263442734428344293443034431344323443334434344353443634437344383443934440344413444234443344443444534446344473444834449344503445134452344533445434455344563445734458344593446034461344623446334464344653446634467344683446934470344713447234473344743447534476344773447834479344803448134482344833448434485344863448734488344893449034491344923449334494344953449634497344983449934500345013450234503345043450534506345073450834509345103451134512345133451434515345163451734518345193452034521345223452334524345253452634527345283452934530345313453234533345343453534536345373453834539345403454134542345433454434545345463454734548345493455034551345523455334554345553455634557345583455934560345613456234563345643456534566345673456834569345703457134572345733457434575345763457734578345793458034581345823458334584345853458634587345883458934590345913459234593345943459534596345973459834599346003460134602346033460434605346063460734608346093461034611346123461334614346153461634617346183461934620346213462234623346243462534626346273462834629346303463134632346333463434635346363463734638346393464034641346423464334644346453464634647346483464934650346513465234653346543465534656346573465834659346603466134662346633466434665346663466734668346693467034671346723467334674346753467634677346783467934680346813468234683346843468534686346873468834689346903469134692346933469434695346963469734698346993470034701347023470334704347053470634707347083470934710347113471234713347143471534716347173471834719347203472134722347233472434725347263472734728347293473034731347323473334734347353473634737347383473934740347413474234743347443474534746347473474834749347503475134752347533475434755347563475734758347593476034761347623476334764347653476634767347683476934770347713477234773347743477534776347773477834779347803478134782347833478434785347863478734788347893479034791347923479334794347953479634797347983479934800348013480234803348043480534806348073480834809348103481134812348133481434815348163481734818348193482034821348223482334824348253482634827348283482934830348313483234833348343483534836348373483834839348403484134842348433484434845348463484734848348493485034851348523485334854348553485634857348583485934860348613486234863348643486534866348673486834869348703487134872348733487434875348763487734878348793488034881348823488334884348853488634887348883488934890348913489234893348943489534896348973489834899349003490134902349033490434905349063490734908349093491034911349123491334914349153491634917349183491934920349213492234923349243492534926349273492834929349303493134932349333493434935349363493734938349393494034941349423494334944349453494634947349483494934950349513495234953349543495534956349573495834959349603496134962349633496434965349663496734968349693497034971349723497334974349753497634977349783497934980349813498234983349843498534986349873498834989349903499134992349933499434995349963499734998349993500035001350023500335004350053500635007350083500935010350113501235013350143501535016350173501835019350203502135022350233502435025350263502735028350293503035031350323503335034350353503635037350383503935040350413504235043350443504535046350473504835049350503505135052350533505435055350563505735058350593506035061350623506335064350653506635067350683506935070350713507235073350743507535076350773507835079350803508135082350833508435085350863508735088350893509035091350923509335094350953509635097350983509935100351013510235103351043510535106351073510835109351103511135112351133511435115351163511735118351193512035121351223512335124351253512635127351283512935130351313513235133351343513535136351373513835139351403514135142351433514435145351463514735148351493515035151351523515335154351553515635157351583515935160351613516235163351643516535166351673516835169351703517135172351733517435175351763517735178351793518035181351823518335184351853518635187351883518935190351913519235193351943519535196351973519835199352003520135202352033520435205352063520735208352093521035211352123521335214352153521635217352183521935220352213522235223352243522535226352273522835229352303523135232352333523435235352363523735238352393524035241352423524335244352453524635247352483524935250352513525235253352543525535256352573525835259352603526135262352633526435265352663526735268352693527035271352723527335274352753527635277352783527935280352813528235283352843528535286352873528835289352903529135292352933529435295352963529735298352993530035301353023530335304353053530635307353083530935310353113531235313353143531535316353173531835319353203532135322353233532435325353263532735328353293533035331353323533335334353353533635337353383533935340353413534235343353443534535346353473534835349353503535135352353533535435355353563535735358353593536035361353623536335364353653536635367353683536935370353713537235373353743537535376353773537835379353803538135382353833538435385353863538735388353893539035391353923539335394353953539635397353983539935400354013540235403354043540535406354073540835409354103541135412354133541435415354163541735418354193542035421354223542335424354253542635427354283542935430354313543235433354343543535436354373543835439354403544135442354433544435445354463544735448354493545035451354523545335454354553545635457354583545935460354613546235463354643546535466354673546835469354703547135472354733547435475354763547735478354793548035481354823548335484354853548635487354883548935490354913549235493354943549535496354973549835499355003550135502355033550435505355063550735508355093551035511355123551335514355153551635517355183551935520355213552235523355243552535526355273552835529355303553135532355333553435535355363553735538355393554035541355423554335544355453554635547355483554935550355513555235553355543555535556355573555835559355603556135562355633556435565355663556735568355693557035571355723557335574355753557635577355783557935580355813558235583355843558535586355873558835589355903559135592355933559435595355963559735598355993560035601356023560335604356053560635607356083560935610356113561235613356143561535616356173561835619356203562135622356233562435625356263562735628356293563035631356323563335634356353563635637356383563935640356413564235643356443564535646356473564835649356503565135652356533565435655356563565735658356593566035661356623566335664356653566635667356683566935670356713567235673356743567535676356773567835679356803568135682356833568435685356863568735688356893569035691356923569335694356953569635697356983569935700357013570235703357043570535706357073570835709357103571135712357133571435715357163571735718357193572035721357223572335724357253572635727357283572935730357313573235733357343573535736357373573835739357403574135742357433574435745357463574735748357493575035751357523575335754357553575635757357583575935760357613576235763357643576535766357673576835769357703577135772357733577435775357763577735778357793578035781357823578335784357853578635787357883578935790357913579235793357943579535796357973579835799358003580135802358033580435805358063580735808358093581035811358123581335814358153581635817358183581935820358213582235823358243582535826358273582835829358303583135832358333583435835358363583735838358393584035841358423584335844358453584635847358483584935850358513585235853358543585535856358573585835859358603586135862358633586435865358663586735868358693587035871358723587335874358753587635877358783587935880358813588235883358843588535886358873588835889358903589135892358933589435895358963589735898358993590035901359023590335904359053590635907359083590935910359113591235913359143591535916359173591835919359203592135922359233592435925359263592735928359293593035931359323593335934359353593635937359383593935940359413594235943359443594535946359473594835949359503595135952359533595435955359563595735958359593596035961359623596335964359653596635967359683596935970359713597235973359743597535976359773597835979359803598135982359833598435985359863598735988359893599035991359923599335994359953599635997359983599936000360013600236003360043600536006360073600836009360103601136012360133601436015360163601736018360193602036021360223602336024360253602636027360283602936030360313603236033360343603536036360373603836039360403604136042360433604436045360463604736048360493605036051360523605336054360553605636057360583605936060360613606236063360643606536066360673606836069360703607136072360733607436075360763607736078360793608036081360823608336084360853608636087360883608936090360913609236093360943609536096360973609836099361003610136102361033610436105361063610736108361093611036111361123611336114361153611636117361183611936120361213612236123361243612536126361273612836129361303613136132361333613436135361363613736138361393614036141361423614336144361453614636147361483614936150361513615236153361543615536156361573615836159361603616136162361633616436165361663616736168361693617036171361723617336174361753617636177361783617936180361813618236183361843618536186361873618836189361903619136192361933619436195361963619736198361993620036201362023620336204362053620636207362083620936210362113621236213362143621536216362173621836219362203622136222362233622436225362263622736228362293623036231362323623336234362353623636237362383623936240362413624236243362443624536246362473624836249362503625136252362533625436255362563625736258362593626036261362623626336264362653626636267362683626936270362713627236273362743627536276362773627836279362803628136282362833628436285362863628736288362893629036291362923629336294362953629636297362983629936300363013630236303363043630536306363073630836309363103631136312363133631436315363163631736318363193632036321363223632336324363253632636327363283632936330363313633236333363343633536336363373633836339363403634136342363433634436345363463634736348363493635036351363523635336354363553635636357363583635936360363613636236363363643636536366363673636836369363703637136372363733637436375363763637736378363793638036381363823638336384363853638636387363883638936390363913639236393363943639536396363973639836399364003640136402364033640436405364063640736408364093641036411364123641336414364153641636417364183641936420364213642236423364243642536426364273642836429364303643136432364333643436435364363643736438364393644036441364423644336444364453644636447364483644936450364513645236453364543645536456364573645836459364603646136462364633646436465364663646736468364693647036471364723647336474364753647636477364783647936480364813648236483364843648536486364873648836489364903649136492364933649436495364963649736498364993650036501365023650336504365053650636507365083650936510365113651236513365143651536516365173651836519365203652136522365233652436525365263652736528365293653036531365323653336534365353653636537365383653936540365413654236543365443654536546365473654836549365503655136552365533655436555365563655736558365593656036561365623656336564365653656636567365683656936570365713657236573365743657536576365773657836579365803658136582365833658436585365863658736588365893659036591365923659336594365953659636597365983659936600366013660236603366043660536606366073660836609366103661136612366133661436615366163661736618366193662036621366223662336624366253662636627366283662936630366313663236633366343663536636366373663836639366403664136642366433664436645366463664736648366493665036651366523665336654366553665636657366583665936660366613666236663366643666536666366673666836669366703667136672366733667436675366763667736678366793668036681366823668336684366853668636687366883668936690366913669236693366943669536696366973669836699367003670136702367033670436705367063670736708367093671036711367123671336714367153671636717367183671936720367213672236723367243672536726367273672836729367303673136732367333673436735367363673736738367393674036741367423674336744367453674636747367483674936750367513675236753367543675536756367573675836759367603676136762367633676436765367663676736768367693677036771367723677336774367753677636777367783677936780367813678236783367843678536786367873678836789367903679136792367933679436795367963679736798367993680036801368023680336804368053680636807368083680936810368113681236813368143681536816368173681836819368203682136822368233682436825368263682736828368293683036831368323683336834368353683636837368383683936840368413684236843368443684536846368473684836849368503685136852368533685436855368563685736858368593686036861368623686336864368653686636867368683686936870368713687236873368743687536876368773687836879368803688136882368833688436885368863688736888368893689036891368923689336894368953689636897368983689936900369013690236903369043690536906369073690836909369103691136912369133691436915369163691736918369193692036921369223692336924369253692636927369283692936930369313693236933369343693536936369373693836939369403694136942369433694436945369463694736948369493695036951369523695336954369553695636957369583695936960369613696236963369643696536966369673696836969369703697136972369733697436975369763697736978369793698036981369823698336984369853698636987369883698936990369913699236993369943699536996369973699836999370003700137002370033700437005370063700737008370093701037011370123701337014370153701637017370183701937020370213702237023370243702537026370273702837029370303703137032370333703437035370363703737038370393704037041370423704337044370453704637047370483704937050370513705237053370543705537056370573705837059370603706137062370633706437065370663706737068370693707037071370723707337074370753707637077370783707937080370813708237083370843708537086370873708837089370903709137092370933709437095370963709737098370993710037101371023710337104371053710637107371083710937110371113711237113371143711537116371173711837119371203712137122371233712437125371263712737128371293713037131371323713337134371353713637137371383713937140371413714237143371443714537146371473714837149371503715137152371533715437155371563715737158371593716037161371623716337164371653716637167371683716937170371713717237173371743717537176371773717837179371803718137182371833718437185371863718737188371893719037191371923719337194371953719637197371983719937200372013720237203372043720537206372073720837209372103721137212372133721437215372163721737218372193722037221372223722337224372253722637227372283722937230372313723237233372343723537236372373723837239372403724137242372433724437245372463724737248372493725037251372523725337254372553725637257372583725937260372613726237263372643726537266372673726837269372703727137272372733727437275372763727737278372793728037281372823728337284372853728637287372883728937290372913729237293372943729537296372973729837299373003730137302373033730437305373063730737308373093731037311373123731337314373153731637317373183731937320373213732237323373243732537326373273732837329373303733137332373333733437335373363733737338373393734037341373423734337344373453734637347373483734937350373513735237353373543735537356373573735837359373603736137362373633736437365373663736737368373693737037371373723737337374373753737637377373783737937380373813738237383373843738537386373873738837389373903739137392373933739437395373963739737398373993740037401374023740337404374053740637407374083740937410374113741237413374143741537416374173741837419374203742137422374233742437425374263742737428374293743037431374323743337434374353743637437374383743937440374413744237443374443744537446374473744837449374503745137452374533745437455374563745737458374593746037461374623746337464374653746637467374683746937470374713747237473374743747537476374773747837479374803748137482374833748437485374863748737488374893749037491374923749337494374953749637497374983749937500375013750237503375043750537506375073750837509375103751137512375133751437515375163751737518375193752037521375223752337524375253752637527375283752937530375313753237533375343753537536375373753837539375403754137542375433754437545375463754737548375493755037551375523755337554375553755637557375583755937560375613756237563375643756537566375673756837569375703757137572375733757437575375763757737578375793758037581375823758337584375853758637587375883758937590375913759237593375943759537596375973759837599376003760137602376033760437605376063760737608376093761037611376123761337614376153761637617376183761937620376213762237623376243762537626376273762837629376303763137632376333763437635376363763737638376393764037641376423764337644376453764637647376483764937650376513765237653376543765537656376573765837659376603766137662376633766437665376663766737668376693767037671376723767337674376753767637677376783767937680376813768237683376843768537686376873768837689376903769137692376933769437695376963769737698376993770037701377023770337704377053770637707377083770937710377113771237713377143771537716377173771837719377203772137722377233772437725377263772737728377293773037731377323773337734377353773637737377383773937740377413774237743377443774537746377473774837749377503775137752377533775437755377563775737758377593776037761377623776337764377653776637767377683776937770377713777237773377743777537776377773777837779377803778137782377833778437785377863778737788377893779037791377923779337794377953779637797377983779937800378013780237803378043780537806378073780837809378103781137812378133781437815378163781737818378193782037821378223782337824378253782637827378283782937830378313783237833378343783537836378373783837839378403784137842378433784437845378463784737848378493785037851378523785337854378553785637857378583785937860378613786237863378643786537866378673786837869378703787137872378733787437875378763787737878378793788037881378823788337884378853788637887378883788937890378913789237893378943789537896378973789837899379003790137902379033790437905379063790737908379093791037911379123791337914379153791637917379183791937920379213792237923379243792537926379273792837929379303793137932379333793437935379363793737938379393794037941379423794337944379453794637947379483794937950379513795237953379543795537956379573795837959379603796137962379633796437965379663796737968379693797037971379723797337974379753797637977379783797937980379813798237983379843798537986379873798837989379903799137992379933799437995379963799737998379993800038001380023800338004380053800638007380083800938010380113801238013380143801538016380173801838019380203802138022380233802438025380263802738028380293803038031380323803338034380353803638037380383803938040380413804238043380443804538046380473804838049380503805138052380533805438055380563805738058380593806038061380623806338064380653806638067380683806938070380713807238073380743807538076380773807838079380803808138082380833808438085380863808738088380893809038091380923809338094380953809638097380983809938100381013810238103381043810538106381073810838109381103811138112381133811438115381163811738118381193812038121381223812338124381253812638127381283812938130381313813238133381343813538136381373813838139381403814138142381433814438145381463814738148381493815038151381523815338154381553815638157381583815938160381613816238163381643816538166381673816838169381703817138172381733817438175381763817738178381793818038181381823818338184381853818638187381883818938190381913819238193381943819538196381973819838199382003820138202382033820438205382063820738208382093821038211382123821338214382153821638217382183821938220382213822238223382243822538226382273822838229382303823138232382333823438235382363823738238382393824038241382423824338244382453824638247382483824938250382513825238253382543825538256382573825838259382603826138262382633826438265382663826738268382693827038271382723827338274382753827638277382783827938280382813828238283382843828538286382873828838289382903829138292382933829438295382963829738298382993830038301383023830338304383053830638307383083830938310383113831238313383143831538316383173831838319383203832138322383233832438325383263832738328383293833038331383323833338334383353833638337383383833938340383413834238343383443834538346383473834838349383503835138352383533835438355383563835738358383593836038361383623836338364383653836638367383683836938370383713837238373383743837538376383773837838379383803838138382383833838438385383863838738388383893839038391383923839338394383953839638397383983839938400384013840238403384043840538406384073840838409384103841138412384133841438415384163841738418384193842038421384223842338424384253842638427384283842938430384313843238433384343843538436384373843838439384403844138442384433844438445384463844738448384493845038451384523845338454384553845638457384583845938460384613846238463384643846538466384673846838469384703847138472384733847438475384763847738478384793848038481384823848338484384853848638487384883848938490384913849238493384943849538496384973849838499385003850138502385033850438505385063850738508385093851038511385123851338514385153851638517385183851938520385213852238523385243852538526385273852838529385303853138532385333853438535385363853738538385393854038541385423854338544385453854638547385483854938550385513855238553385543855538556385573855838559385603856138562385633856438565385663856738568385693857038571385723857338574385753857638577385783857938580385813858238583385843858538586385873858838589385903859138592385933859438595385963859738598385993860038601386023860338604386053860638607386083860938610386113861238613386143861538616386173861838619386203862138622386233862438625386263862738628386293863038631386323863338634386353863638637386383863938640386413864238643386443864538646386473864838649386503865138652386533865438655386563865738658386593866038661386623866338664386653866638667386683866938670386713867238673386743867538676386773867838679386803868138682386833868438685386863868738688386893869038691386923869338694386953869638697386983869938700387013870238703387043870538706387073870838709387103871138712387133871438715387163871738718387193872038721387223872338724387253872638727387283872938730387313873238733387343873538736387373873838739387403874138742387433874438745387463874738748387493875038751387523875338754387553875638757387583875938760387613876238763387643876538766387673876838769387703877138772387733877438775387763877738778387793878038781387823878338784387853878638787387883878938790387913879238793387943879538796387973879838799388003880138802388033880438805388063880738808388093881038811388123881338814388153881638817388183881938820388213882238823388243882538826388273882838829388303883138832388333883438835388363883738838388393884038841388423884338844388453884638847388483884938850388513885238853388543885538856388573885838859388603886138862388633886438865388663886738868388693887038871388723887338874388753887638877388783887938880388813888238883388843888538886388873888838889388903889138892388933889438895388963889738898388993890038901389023890338904389053890638907389083890938910389113891238913389143891538916389173891838919389203892138922389233892438925389263892738928389293893038931389323893338934389353893638937389383893938940389413894238943389443894538946389473894838949389503895138952389533895438955389563895738958389593896038961389623896338964389653896638967389683896938970389713897238973389743897538976389773897838979389803898138982389833898438985389863898738988389893899038991389923899338994389953899638997389983899939000390013900239003390043900539006390073900839009390103901139012390133901439015390163901739018390193902039021390223902339024390253902639027390283902939030390313903239033390343903539036390373903839039390403904139042390433904439045390463904739048390493905039051390523905339054390553905639057390583905939060390613906239063390643906539066390673906839069390703907139072390733907439075390763907739078390793908039081390823908339084390853908639087390883908939090390913909239093390943909539096390973909839099391003910139102391033910439105391063910739108391093911039111391123911339114391153911639117391183911939120391213912239123391243912539126391273912839129391303913139132391333913439135391363913739138391393914039141391423914339144391453914639147391483914939150391513915239153391543915539156391573915839159391603916139162391633916439165391663916739168391693917039171391723917339174391753917639177391783917939180391813918239183391843918539186391873918839189391903919139192391933919439195391963919739198391993920039201392023920339204392053920639207392083920939210392113921239213392143921539216392173921839219392203922139222392233922439225392263922739228392293923039231392323923339234392353923639237392383923939240392413924239243392443924539246392473924839249392503925139252392533925439255392563925739258392593926039261392623926339264392653926639267392683926939270392713927239273392743927539276392773927839279392803928139282392833928439285392863928739288392893929039291392923929339294392953929639297392983929939300393013930239303393043930539306393073930839309393103931139312393133931439315393163931739318393193932039321393223932339324393253932639327393283932939330393313933239333393343933539336393373933839339393403934139342393433934439345393463934739348393493935039351393523935339354393553935639357393583935939360393613936239363393643936539366393673936839369393703937139372393733937439375393763937739378393793938039381393823938339384393853938639387393883938939390393913939239393393943939539396393973939839399394003940139402394033940439405394063940739408394093941039411394123941339414394153941639417394183941939420394213942239423394243942539426394273942839429394303943139432394333943439435394363943739438394393944039441394423944339444394453944639447394483944939450394513945239453394543945539456394573945839459394603946139462394633946439465394663946739468394693947039471394723947339474394753947639477394783947939480394813948239483394843948539486394873948839489394903949139492394933949439495394963949739498394993950039501395023950339504395053950639507395083950939510395113951239513395143951539516395173951839519395203952139522395233952439525395263952739528395293953039531395323953339534395353953639537395383953939540395413954239543395443954539546395473954839549395503955139552395533955439555395563955739558395593956039561395623956339564395653956639567395683956939570395713957239573395743957539576395773957839579395803958139582395833958439585395863958739588395893959039591395923959339594395953959639597395983959939600396013960239603396043960539606396073960839609396103961139612396133961439615396163961739618396193962039621396223962339624396253962639627396283962939630396313963239633396343963539636396373963839639396403964139642396433964439645396463964739648396493965039651396523965339654396553965639657396583965939660396613966239663396643966539666396673966839669396703967139672396733967439675396763967739678396793968039681396823968339684396853968639687396883968939690396913969239693396943969539696396973969839699397003970139702397033970439705397063970739708397093971039711397123971339714397153971639717397183971939720397213972239723397243972539726397273972839729397303973139732397333973439735397363973739738397393974039741397423974339744397453974639747397483974939750397513975239753397543975539756397573975839759397603976139762397633976439765397663976739768397693977039771397723977339774397753977639777397783977939780397813978239783397843978539786397873978839789397903979139792397933979439795397963979739798397993980039801398023980339804398053980639807398083980939810398113981239813398143981539816398173981839819398203982139822398233982439825398263982739828398293983039831398323983339834398353983639837398383983939840398413984239843398443984539846398473984839849398503985139852398533985439855398563985739858398593986039861398623986339864398653986639867398683986939870398713987239873398743987539876398773987839879398803988139882398833988439885398863988739888398893989039891398923989339894398953989639897398983989939900399013990239903399043990539906399073990839909399103991139912399133991439915399163991739918399193992039921399223992339924399253992639927399283992939930399313993239933399343993539936399373993839939399403994139942399433994439945399463994739948399493995039951399523995339954399553995639957399583995939960399613996239963399643996539966399673996839969399703997139972399733997439975399763997739978399793998039981399823998339984399853998639987399883998939990399913999239993399943999539996399973999839999400004000140002400034000440005400064000740008400094001040011400124001340014400154001640017400184001940020400214002240023400244002540026400274002840029400304003140032400334003440035400364003740038400394004040041400424004340044400454004640047400484004940050400514005240053400544005540056400574005840059400604006140062400634006440065400664006740068400694007040071400724007340074400754007640077400784007940080400814008240083400844008540086400874008840089400904009140092400934009440095400964009740098400994010040101401024010340104401054010640107401084010940110401114011240113401144011540116401174011840119401204012140122401234012440125401264012740128401294013040131401324013340134401354013640137401384013940140401414014240143401444014540146401474014840149401504015140152401534015440155401564015740158401594016040161401624016340164401654016640167401684016940170401714017240173401744017540176401774017840179401804018140182401834018440185401864018740188401894019040191401924019340194401954019640197401984019940200402014020240203402044020540206402074020840209402104021140212402134021440215402164021740218402194022040221402224022340224402254022640227402284022940230402314023240233402344023540236402374023840239402404024140242402434024440245402464024740248402494025040251402524025340254402554025640257402584025940260402614026240263402644026540266402674026840269402704027140272402734027440275402764027740278402794028040281402824028340284402854028640287402884028940290402914029240293402944029540296402974029840299403004030140302403034030440305403064030740308403094031040311403124031340314403154031640317403184031940320403214032240323403244032540326403274032840329403304033140332403334033440335403364033740338403394034040341403424034340344403454034640347403484034940350403514035240353403544035540356403574035840359403604036140362403634036440365403664036740368403694037040371403724037340374403754037640377403784037940380403814038240383403844038540386403874038840389403904039140392403934039440395403964039740398403994040040401404024040340404404054040640407404084040940410404114041240413404144041540416404174041840419404204042140422404234042440425404264042740428404294043040431404324043340434404354043640437404384043940440404414044240443404444044540446404474044840449404504045140452404534045440455404564045740458404594046040461404624046340464404654046640467404684046940470404714047240473404744047540476404774047840479404804048140482404834048440485404864048740488404894049040491404924049340494404954049640497404984049940500405014050240503405044050540506405074050840509405104051140512405134051440515405164051740518405194052040521405224052340524405254052640527405284052940530405314053240533405344053540536405374053840539405404054140542405434054440545405464054740548405494055040551405524055340554405554055640557405584055940560405614056240563405644056540566405674056840569405704057140572405734057440575405764057740578405794058040581405824058340584405854058640587405884058940590405914059240593405944059540596405974059840599406004060140602406034060440605406064060740608406094061040611406124061340614406154061640617406184061940620406214062240623406244062540626406274062840629406304063140632406334063440635406364063740638406394064040641406424064340644406454064640647406484064940650406514065240653406544065540656406574065840659406604066140662406634066440665406664066740668406694067040671406724067340674406754067640677406784067940680406814068240683406844068540686406874068840689406904069140692406934069440695406964069740698406994070040701407024070340704407054070640707407084070940710407114071240713407144071540716407174071840719407204072140722407234072440725407264072740728407294073040731407324073340734407354073640737407384073940740407414074240743407444074540746407474074840749407504075140752407534075440755407564075740758407594076040761407624076340764407654076640767407684076940770407714077240773407744077540776407774077840779407804078140782407834078440785407864078740788407894079040791407924079340794407954079640797407984079940800408014080240803408044080540806408074080840809408104081140812408134081440815408164081740818408194082040821408224082340824408254082640827408284082940830408314083240833408344083540836408374083840839408404084140842408434084440845408464084740848408494085040851408524085340854408554085640857408584085940860408614086240863408644086540866408674086840869408704087140872408734087440875408764087740878408794088040881408824088340884408854088640887408884088940890408914089240893408944089540896408974089840899409004090140902409034090440905409064090740908409094091040911409124091340914409154091640917409184091940920409214092240923409244092540926409274092840929409304093140932409334093440935409364093740938409394094040941409424094340944409454094640947409484094940950409514095240953409544095540956409574095840959409604096140962409634096440965409664096740968409694097040971409724097340974409754097640977409784097940980409814098240983409844098540986409874098840989409904099140992409934099440995409964099740998409994100041001410024100341004410054100641007410084100941010410114101241013410144101541016410174101841019410204102141022410234102441025410264102741028410294103041031410324103341034410354103641037410384103941040410414104241043410444104541046410474104841049410504105141052410534105441055410564105741058410594106041061410624106341064410654106641067410684106941070410714107241073410744107541076410774107841079410804108141082410834108441085410864108741088410894109041091410924109341094410954109641097410984109941100411014110241103411044110541106411074110841109411104111141112411134111441115411164111741118411194112041121411224112341124411254112641127411284112941130411314113241133411344113541136411374113841139411404114141142411434114441145411464114741148411494115041151411524115341154411554115641157411584115941160411614116241163411644116541166411674116841169411704117141172411734117441175411764117741178411794118041181411824118341184411854118641187411884118941190411914119241193411944119541196411974119841199412004120141202412034120441205412064120741208412094121041211412124121341214412154121641217412184121941220412214122241223412244122541226412274122841229412304123141232412334123441235412364123741238412394124041241412424124341244412454124641247412484124941250412514125241253412544125541256412574125841259412604126141262412634126441265412664126741268412694127041271412724127341274412754127641277412784127941280412814128241283412844128541286412874128841289412904129141292412934129441295412964129741298412994130041301413024130341304413054130641307413084130941310413114131241313413144131541316413174131841319413204132141322413234132441325413264132741328413294133041331413324133341334413354133641337413384133941340413414134241343413444134541346413474134841349413504135141352413534135441355413564135741358413594136041361413624136341364413654136641367413684136941370413714137241373413744137541376413774137841379413804138141382413834138441385413864138741388413894139041391413924139341394413954139641397413984139941400414014140241403414044140541406414074140841409414104141141412414134141441415414164141741418414194142041421414224142341424414254142641427414284142941430414314143241433414344143541436414374143841439414404144141442414434144441445414464144741448414494145041451414524145341454414554145641457414584145941460414614146241463414644146541466414674146841469414704147141472414734147441475414764147741478414794148041481414824148341484414854148641487414884148941490414914149241493414944149541496414974149841499415004150141502415034150441505415064150741508415094151041511415124151341514415154151641517415184151941520415214152241523415244152541526415274152841529415304153141532415334153441535415364153741538415394154041541415424154341544415454154641547415484154941550415514155241553415544155541556415574155841559415604156141562415634156441565415664156741568415694157041571415724157341574415754157641577415784157941580415814158241583415844158541586415874158841589415904159141592415934159441595415964159741598415994160041601416024160341604416054160641607416084160941610416114161241613416144161541616416174161841619416204162141622416234162441625416264162741628416294163041631416324163341634416354163641637416384163941640416414164241643416444164541646416474164841649416504165141652416534165441655416564165741658416594166041661416624166341664416654166641667416684166941670416714167241673416744167541676416774167841679416804168141682416834168441685416864168741688416894169041691416924169341694416954169641697416984169941700417014170241703417044170541706417074170841709417104171141712417134171441715417164171741718417194172041721417224172341724417254172641727417284172941730417314173241733417344173541736417374173841739417404174141742417434174441745417464174741748417494175041751417524175341754417554175641757417584175941760417614176241763417644176541766417674176841769417704177141772417734177441775417764177741778417794178041781417824178341784417854178641787417884178941790417914179241793417944179541796417974179841799418004180141802418034180441805418064180741808418094181041811418124181341814418154181641817418184181941820418214182241823418244182541826418274182841829418304183141832418334183441835418364183741838418394184041841418424184341844418454184641847418484184941850418514185241853418544185541856418574185841859418604186141862418634186441865418664186741868418694187041871418724187341874418754187641877418784187941880418814188241883418844188541886418874188841889418904189141892418934189441895418964189741898418994190041901419024190341904419054190641907419084190941910419114191241913419144191541916419174191841919419204192141922419234192441925419264192741928419294193041931419324193341934419354193641937419384193941940419414194241943419444194541946419474194841949419504195141952419534195441955419564195741958419594196041961419624196341964419654196641967419684196941970419714197241973419744197541976419774197841979419804198141982419834198441985419864198741988419894199041991419924199341994419954199641997419984199942000420014200242003420044200542006420074200842009420104201142012420134201442015420164201742018420194202042021420224202342024420254202642027420284202942030420314203242033420344203542036420374203842039420404204142042420434204442045420464204742048420494205042051420524205342054420554205642057420584205942060420614206242063420644206542066420674206842069420704207142072420734207442075420764207742078420794208042081420824208342084420854208642087420884208942090420914209242093420944209542096420974209842099421004210142102421034210442105421064210742108421094211042111421124211342114421154211642117421184211942120421214212242123421244212542126421274212842129421304213142132421334213442135421364213742138421394214042141421424214342144421454214642147421484214942150421514215242153421544215542156421574215842159421604216142162421634216442165421664216742168421694217042171421724217342174421754217642177421784217942180421814218242183421844218542186421874218842189421904219142192421934219442195421964219742198421994220042201422024220342204422054220642207422084220942210422114221242213422144221542216422174221842219422204222142222422234222442225422264222742228422294223042231422324223342234422354223642237422384223942240422414224242243422444224542246422474224842249422504225142252422534225442255422564225742258422594226042261422624226342264422654226642267422684226942270422714227242273422744227542276422774227842279422804228142282422834228442285422864228742288422894229042291422924229342294422954229642297422984229942300423014230242303423044230542306423074230842309423104231142312423134231442315423164231742318423194232042321423224232342324423254232642327423284232942330423314233242333423344233542336423374233842339423404234142342423434234442345423464234742348423494235042351423524235342354423554235642357423584235942360423614236242363423644236542366423674236842369423704237142372423734237442375423764237742378423794238042381423824238342384423854238642387423884238942390423914239242393423944239542396423974239842399424004240142402424034240442405424064240742408424094241042411424124241342414424154241642417424184241942420424214242242423424244242542426424274242842429424304243142432424334243442435424364243742438424394244042441424424244342444424454244642447424484244942450424514245242453424544245542456424574245842459424604246142462424634246442465424664246742468424694247042471424724247342474424754247642477424784247942480424814248242483424844248542486424874248842489424904249142492424934249442495424964249742498424994250042501425024250342504425054250642507425084250942510425114251242513425144251542516425174251842519425204252142522425234252442525425264252742528425294253042531425324253342534425354253642537425384253942540425414254242543425444254542546425474254842549425504255142552425534255442555425564255742558425594256042561425624256342564425654256642567425684256942570425714257242573425744257542576425774257842579425804258142582425834258442585425864258742588425894259042591425924259342594425954259642597425984259942600426014260242603426044260542606426074260842609426104261142612426134261442615426164261742618426194262042621426224262342624426254262642627426284262942630426314263242633426344263542636426374263842639426404264142642426434264442645426464264742648426494265042651426524265342654426554265642657426584265942660426614266242663426644266542666426674266842669426704267142672426734267442675426764267742678426794268042681426824268342684426854268642687426884268942690426914269242693426944269542696426974269842699427004270142702427034270442705427064270742708427094271042711427124271342714427154271642717427184271942720427214272242723427244272542726427274272842729427304273142732427334273442735427364273742738427394274042741427424274342744427454274642747427484274942750427514275242753427544275542756427574275842759427604276142762427634276442765427664276742768427694277042771427724277342774427754277642777427784277942780427814278242783427844278542786427874278842789427904279142792427934279442795427964279742798427994280042801428024280342804428054280642807428084280942810428114281242813428144281542816428174281842819428204282142822428234282442825428264282742828428294283042831428324283342834428354283642837428384283942840428414284242843428444284542846428474284842849428504285142852428534285442855428564285742858428594286042861428624286342864428654286642867428684286942870428714287242873428744287542876428774287842879428804288142882428834288442885428864288742888428894289042891428924289342894428954289642897428984289942900429014290242903429044290542906429074290842909429104291142912429134291442915429164291742918429194292042921429224292342924429254292642927429284292942930429314293242933429344293542936429374293842939429404294142942429434294442945429464294742948429494295042951429524295342954429554295642957429584295942960429614296242963429644296542966429674296842969429704297142972429734297442975429764297742978429794298042981429824298342984429854298642987429884298942990429914299242993429944299542996429974299842999430004300143002430034300443005430064300743008430094301043011430124301343014430154301643017430184301943020430214302243023430244302543026430274302843029430304303143032430334303443035430364303743038430394304043041430424304343044430454304643047430484304943050430514305243053430544305543056430574305843059430604306143062430634306443065430664306743068430694307043071430724307343074430754307643077430784307943080430814308243083430844308543086430874308843089430904309143092430934309443095430964309743098430994310043101431024310343104431054310643107431084310943110431114311243113431144311543116431174311843119431204312143122431234312443125431264312743128431294313043131431324313343134431354313643137431384313943140431414314243143431444314543146431474314843149431504315143152431534315443155431564315743158431594316043161431624316343164431654316643167431684316943170431714317243173431744317543176431774317843179431804318143182431834318443185431864318743188431894319043191431924319343194431954319643197431984319943200432014320243203432044320543206432074320843209432104321143212432134321443215432164321743218432194322043221432224322343224432254322643227432284322943230432314323243233432344323543236432374323843239432404324143242432434324443245432464324743248432494325043251432524325343254432554325643257432584325943260432614326243263432644326543266432674326843269432704327143272432734327443275432764327743278432794328043281432824328343284432854328643287432884328943290432914329243293432944329543296432974329843299433004330143302433034330443305433064330743308433094331043311433124331343314433154331643317433184331943320433214332243323433244332543326433274332843329433304333143332433334333443335433364333743338433394334043341433424334343344433454334643347433484334943350433514335243353433544335543356433574335843359433604336143362433634336443365433664336743368433694337043371433724337343374433754337643377433784337943380433814338243383433844338543386433874338843389433904339143392433934339443395433964339743398433994340043401434024340343404434054340643407434084340943410434114341243413434144341543416434174341843419434204342143422434234342443425434264342743428434294343043431434324343343434434354343643437434384343943440434414344243443434444344543446434474344843449434504345143452434534345443455434564345743458434594346043461434624346343464434654346643467434684346943470434714347243473434744347543476434774347843479434804348143482434834348443485434864348743488434894349043491434924349343494434954349643497434984349943500435014350243503435044350543506435074350843509435104351143512435134351443515435164351743518435194352043521435224352343524435254352643527435284352943530435314353243533435344353543536435374353843539435404354143542435434354443545435464354743548435494355043551435524355343554435554355643557435584355943560435614356243563435644356543566435674356843569435704357143572435734357443575435764357743578435794358043581435824358343584435854358643587435884358943590435914359243593435944359543596435974359843599436004360143602436034360443605436064360743608436094361043611436124361343614436154361643617436184361943620436214362243623436244362543626436274362843629436304363143632436334363443635436364363743638436394364043641436424364343644436454364643647436484364943650436514365243653436544365543656436574365843659436604366143662436634366443665436664366743668436694367043671436724367343674436754367643677436784367943680436814368243683436844368543686436874368843689436904369143692436934369443695436964369743698436994370043701437024370343704437054370643707437084370943710437114371243713437144371543716437174371843719437204372143722437234372443725437264372743728437294373043731437324373343734437354373643737437384373943740437414374243743437444374543746437474374843749437504375143752437534375443755437564375743758437594376043761437624376343764437654376643767437684376943770437714377243773437744377543776437774377843779437804378143782437834378443785437864378743788437894379043791437924379343794437954379643797437984379943800438014380243803438044380543806438074380843809438104381143812438134381443815438164381743818438194382043821438224382343824438254382643827438284382943830438314383243833438344383543836438374383843839438404384143842438434384443845438464384743848438494385043851438524385343854438554385643857438584385943860438614386243863438644386543866438674386843869438704387143872438734387443875438764387743878438794388043881438824388343884438854388643887438884388943890438914389243893438944389543896438974389843899439004390143902439034390443905439064390743908439094391043911439124391343914439154391643917439184391943920439214392243923439244392543926439274392843929439304393143932439334393443935439364393743938439394394043941439424394343944439454394643947439484394943950439514395243953439544395543956439574395843959439604396143962439634396443965439664396743968439694397043971439724397343974439754397643977439784397943980439814398243983439844398543986439874398843989439904399143992439934399443995439964399743998439994400044001440024400344004440054400644007440084400944010440114401244013440144401544016440174401844019440204402144022440234402444025440264402744028440294403044031440324403344034440354403644037440384403944040440414404244043440444404544046440474404844049440504405144052440534405444055440564405744058440594406044061440624406344064440654406644067440684406944070440714407244073440744407544076440774407844079440804408144082440834408444085440864408744088440894409044091440924409344094440954409644097440984409944100441014410244103441044410544106441074410844109441104411144112441134411444115441164411744118441194412044121441224412344124441254412644127441284412944130441314413244133441344413544136441374413844139441404414144142441434414444145441464414744148441494415044151441524415344154441554415644157441584415944160441614416244163441644416544166441674416844169441704417144172441734417444175441764417744178441794418044181441824418344184441854418644187441884418944190441914419244193441944419544196441974419844199442004420144202442034420444205442064420744208442094421044211442124421344214442154421644217442184421944220442214422244223442244422544226442274422844229442304423144232442334423444235442364423744238442394424044241442424424344244442454424644247442484424944250442514425244253442544425544256442574425844259442604426144262442634426444265442664426744268442694427044271442724427344274442754427644277442784427944280442814428244283442844428544286442874428844289442904429144292442934429444295442964429744298442994430044301443024430344304443054430644307443084430944310443114431244313443144431544316443174431844319443204432144322443234432444325443264432744328443294433044331443324433344334443354433644337443384433944340443414434244343443444434544346443474434844349443504435144352443534435444355443564435744358443594436044361443624436344364443654436644367443684436944370443714437244373443744437544376443774437844379443804438144382443834438444385443864438744388443894439044391443924439344394443954439644397443984439944400444014440244403444044440544406444074440844409444104441144412444134441444415444164441744418444194442044421444224442344424444254442644427444284442944430444314443244433444344443544436444374443844439444404444144442444434444444445444464444744448444494445044451444524445344454444554445644457444584445944460444614446244463444644446544466444674446844469444704447144472444734447444475444764447744478444794448044481444824448344484444854448644487444884448944490444914449244493444944449544496444974449844499445004450144502445034450444505445064450744508445094451044511445124451344514445154451644517445184451944520445214452244523445244452544526445274452844529445304453144532445334453444535445364453744538445394454044541445424454344544445454454644547445484454944550445514455244553445544455544556445574455844559445604456144562445634456444565445664456744568445694457044571445724457344574445754457644577445784457944580445814458244583445844458544586445874458844589445904459144592445934459444595445964459744598445994460044601446024460344604446054460644607446084460944610446114461244613446144461544616446174461844619446204462144622446234462444625446264462744628446294463044631446324463344634446354463644637446384463944640446414464244643446444464544646446474464844649446504465144652446534465444655446564465744658446594466044661446624466344664446654466644667446684466944670446714467244673446744467544676446774467844679446804468144682446834468444685446864468744688446894469044691446924469344694446954469644697446984469944700447014470244703447044470544706447074470844709447104471144712447134471444715447164471744718447194472044721447224472344724447254472644727447284472944730447314473244733447344473544736447374473844739447404474144742447434474444745447464474744748447494475044751447524475344754447554475644757447584475944760447614476244763447644476544766447674476844769447704477144772447734477444775447764477744778447794478044781447824478344784447854478644787447884478944790447914479244793447944479544796447974479844799448004480144802448034480444805448064480744808448094481044811448124481344814448154481644817448184481944820448214482244823448244482544826448274482844829448304483144832448334483444835448364483744838448394484044841448424484344844448454484644847448484484944850448514485244853448544485544856448574485844859448604486144862448634486444865448664486744868448694487044871448724487344874448754487644877448784487944880448814488244883448844488544886448874488844889448904489144892448934489444895448964489744898448994490044901449024490344904449054490644907449084490944910449114491244913449144491544916449174491844919449204492144922449234492444925449264492744928449294493044931449324493344934449354493644937449384493944940449414494244943449444494544946449474494844949449504495144952449534495444955449564495744958449594496044961449624496344964449654496644967449684496944970449714497244973449744497544976449774497844979449804498144982449834498444985449864498744988449894499044991449924499344994449954499644997449984499945000450014500245003450044500545006450074500845009450104501145012450134501445015450164501745018450194502045021450224502345024450254502645027450284502945030450314503245033450344503545036450374503845039450404504145042450434504445045450464504745048450494505045051450524505345054450554505645057450584505945060450614506245063450644506545066450674506845069450704507145072450734507445075450764507745078450794508045081450824508345084450854508645087450884508945090450914509245093450944509545096450974509845099451004510145102451034510445105451064510745108451094511045111451124511345114451154511645117451184511945120451214512245123451244512545126451274512845129451304513145132451334513445135451364513745138451394514045141451424514345144451454514645147451484514945150451514515245153451544515545156451574515845159451604516145162451634516445165451664516745168451694517045171451724517345174451754517645177451784517945180451814518245183451844518545186451874518845189451904519145192451934519445195451964519745198451994520045201452024520345204452054520645207452084520945210452114521245213452144521545216452174521845219452204522145222452234522445225452264522745228452294523045231452324523345234452354523645237452384523945240452414524245243452444524545246452474524845249452504525145252452534525445255452564525745258452594526045261452624526345264452654526645267452684526945270452714527245273452744527545276452774527845279452804528145282452834528445285452864528745288452894529045291452924529345294452954529645297452984529945300453014530245303453044530545306453074530845309453104531145312453134531445315453164531745318453194532045321453224532345324453254532645327453284532945330453314533245333453344533545336453374533845339453404534145342453434534445345453464534745348453494535045351453524535345354453554535645357453584535945360453614536245363453644536545366453674536845369453704537145372453734537445375453764537745378453794538045381453824538345384453854538645387453884538945390453914539245393453944539545396453974539845399454004540145402454034540445405454064540745408454094541045411454124541345414454154541645417454184541945420454214542245423454244542545426454274542845429454304543145432454334543445435454364543745438454394544045441454424544345444454454544645447454484544945450454514545245453454544545545456454574545845459454604546145462454634546445465454664546745468454694547045471454724547345474454754547645477454784547945480454814548245483454844548545486454874548845489454904549145492454934549445495454964549745498454994550045501455024550345504455054550645507455084550945510455114551245513455144551545516455174551845519455204552145522455234552445525455264552745528455294553045531455324553345534455354553645537455384553945540455414554245543455444554545546455474554845549455504555145552455534555445555455564555745558455594556045561455624556345564455654556645567455684556945570455714557245573455744557545576455774557845579455804558145582455834558445585455864558745588455894559045591455924559345594455954559645597455984559945600456014560245603456044560545606456074560845609456104561145612456134561445615456164561745618456194562045621456224562345624456254562645627456284562945630456314563245633456344563545636456374563845639456404564145642456434564445645456464564745648456494565045651456524565345654456554565645657456584565945660456614566245663456644566545666456674566845669456704567145672456734567445675456764567745678456794568045681456824568345684456854568645687456884568945690456914569245693456944569545696456974569845699457004570145702457034570445705457064570745708457094571045711457124571345714457154571645717457184571945720457214572245723457244572545726457274572845729457304573145732457334573445735457364573745738457394574045741457424574345744457454574645747457484574945750457514575245753457544575545756457574575845759457604576145762457634576445765457664576745768457694577045771457724577345774457754577645777457784577945780457814578245783457844578545786457874578845789457904579145792457934579445795457964579745798457994580045801458024580345804458054580645807458084580945810458114581245813458144581545816458174581845819458204582145822458234582445825458264582745828458294583045831458324583345834458354583645837458384583945840458414584245843458444584545846458474584845849458504585145852458534585445855458564585745858458594586045861458624586345864458654586645867458684586945870458714587245873458744587545876458774587845879458804588145882458834588445885458864588745888458894589045891458924589345894458954589645897458984589945900459014590245903459044590545906459074590845909459104591145912459134591445915459164591745918459194592045921459224592345924459254592645927459284592945930459314593245933459344593545936459374593845939459404594145942459434594445945459464594745948459494595045951459524595345954459554595645957459584595945960459614596245963459644596545966459674596845969459704597145972459734597445975459764597745978459794598045981459824598345984459854598645987459884598945990459914599245993459944599545996459974599845999460004600146002460034600446005460064600746008460094601046011460124601346014460154601646017460184601946020460214602246023460244602546026460274602846029460304603146032460334603446035460364603746038460394604046041460424604346044460454604646047460484604946050460514605246053460544605546056460574605846059460604606146062460634606446065460664606746068460694607046071460724607346074460754607646077460784607946080460814608246083460844608546086460874608846089460904609146092460934609446095460964609746098460994610046101461024610346104461054610646107461084610946110461114611246113461144611546116461174611846119461204612146122461234612446125461264612746128461294613046131461324613346134461354613646137461384613946140461414614246143461444614546146461474614846149461504615146152461534615446155461564615746158461594616046161461624616346164461654616646167461684616946170461714617246173461744617546176461774617846179461804618146182461834618446185461864618746188461894619046191461924619346194461954619646197461984619946200462014620246203462044620546206462074620846209462104621146212462134621446215462164621746218462194622046221462224622346224462254622646227462284622946230462314623246233462344623546236462374623846239462404624146242462434624446245462464624746248462494625046251462524625346254462554625646257462584625946260462614626246263462644626546266462674626846269462704627146272462734627446275462764627746278462794628046281462824628346284462854628646287462884628946290462914629246293462944629546296462974629846299463004630146302463034630446305463064630746308463094631046311463124631346314463154631646317463184631946320463214632246323463244632546326463274632846329463304633146332463334633446335463364633746338463394634046341463424634346344463454634646347463484634946350463514635246353463544635546356463574635846359463604636146362463634636446365463664636746368463694637046371463724637346374463754637646377463784637946380463814638246383463844638546386463874638846389463904639146392463934639446395463964639746398463994640046401464024640346404464054640646407464084640946410464114641246413464144641546416464174641846419464204642146422464234642446425464264642746428464294643046431464324643346434464354643646437464384643946440464414644246443464444644546446464474644846449464504645146452464534645446455464564645746458464594646046461464624646346464464654646646467464684646946470464714647246473464744647546476464774647846479464804648146482464834648446485464864648746488464894649046491464924649346494464954649646497464984649946500465014650246503465044650546506465074650846509465104651146512465134651446515465164651746518465194652046521465224652346524465254652646527465284652946530465314653246533465344653546536465374653846539465404654146542465434654446545465464654746548465494655046551465524655346554465554655646557465584655946560465614656246563465644656546566465674656846569465704657146572465734657446575465764657746578465794658046581465824658346584465854658646587465884658946590465914659246593465944659546596465974659846599466004660146602466034660446605466064660746608466094661046611466124661346614466154661646617466184661946620466214662246623466244662546626466274662846629466304663146632466334663446635466364663746638466394664046641466424664346644466454664646647466484664946650466514665246653466544665546656466574665846659466604666146662466634666446665466664666746668466694667046671466724667346674466754667646677466784667946680466814668246683466844668546686466874668846689466904669146692466934669446695466964669746698466994670046701467024670346704467054670646707467084670946710467114671246713467144671546716467174671846719467204672146722467234672446725467264672746728467294673046731467324673346734467354673646737467384673946740467414674246743467444674546746467474674846749467504675146752467534675446755467564675746758467594676046761467624676346764467654676646767467684676946770467714677246773467744677546776467774677846779467804678146782467834678446785467864678746788467894679046791467924679346794467954679646797467984679946800468014680246803468044680546806468074680846809468104681146812468134681446815468164681746818468194682046821468224682346824468254682646827468284682946830468314683246833468344683546836468374683846839468404684146842468434684446845468464684746848468494685046851468524685346854468554685646857468584685946860468614686246863468644686546866468674686846869468704687146872468734687446875468764687746878468794688046881468824688346884468854688646887468884688946890468914689246893468944689546896468974689846899469004690146902469034690446905469064690746908469094691046911469124691346914469154691646917469184691946920469214692246923469244692546926469274692846929469304693146932469334693446935469364693746938469394694046941469424694346944469454694646947469484694946950469514695246953469544695546956469574695846959469604696146962469634696446965469664696746968469694697046971469724697346974469754697646977469784697946980469814698246983469844698546986469874698846989469904699146992469934699446995469964699746998469994700047001470024700347004470054700647007470084700947010470114701247013470144701547016470174701847019470204702147022470234702447025470264702747028470294703047031470324703347034470354703647037470384703947040470414704247043470444704547046470474704847049470504705147052470534705447055470564705747058470594706047061470624706347064470654706647067470684706947070470714707247073470744707547076470774707847079470804708147082470834708447085470864708747088470894709047091470924709347094470954709647097470984709947100471014710247103471044710547106471074710847109471104711147112471134711447115471164711747118471194712047121471224712347124471254712647127471284712947130471314713247133471344713547136471374713847139471404714147142471434714447145471464714747148471494715047151471524715347154471554715647157471584715947160471614716247163471644716547166471674716847169471704717147172471734717447175471764717747178471794718047181471824718347184471854718647187471884718947190471914719247193471944719547196471974719847199472004720147202472034720447205472064720747208472094721047211472124721347214472154721647217472184721947220472214722247223472244722547226472274722847229472304723147232472334723447235472364723747238472394724047241472424724347244472454724647247472484724947250472514725247253472544725547256472574725847259472604726147262472634726447265472664726747268472694727047271472724727347274472754727647277472784727947280472814728247283472844728547286472874728847289472904729147292472934729447295472964729747298472994730047301473024730347304473054730647307473084730947310473114731247313473144731547316473174731847319473204732147322473234732447325473264732747328473294733047331473324733347334473354733647337473384733947340473414734247343473444734547346473474734847349473504735147352473534735447355473564735747358473594736047361473624736347364473654736647367473684736947370473714737247373473744737547376473774737847379473804738147382473834738447385473864738747388473894739047391473924739347394473954739647397473984739947400474014740247403474044740547406474074740847409474104741147412474134741447415474164741747418474194742047421474224742347424474254742647427474284742947430474314743247433474344743547436474374743847439474404744147442474434744447445474464744747448474494745047451474524745347454474554745647457474584745947460474614746247463474644746547466474674746847469474704747147472474734747447475474764747747478474794748047481474824748347484474854748647487474884748947490474914749247493474944749547496474974749847499475004750147502475034750447505475064750747508475094751047511475124751347514475154751647517475184751947520475214752247523475244752547526475274752847529475304753147532475334753447535475364753747538475394754047541475424754347544475454754647547475484754947550475514755247553475544755547556475574755847559475604756147562475634756447565475664756747568475694757047571475724757347574475754757647577475784757947580475814758247583475844758547586475874758847589475904759147592475934759447595475964759747598475994760047601476024760347604476054760647607476084760947610476114761247613476144761547616476174761847619476204762147622476234762447625476264762747628476294763047631476324763347634476354763647637476384763947640476414764247643476444764547646476474764847649476504765147652476534765447655476564765747658476594766047661476624766347664476654766647667476684766947670476714767247673476744767547676476774767847679476804768147682476834768447685476864768747688476894769047691476924769347694476954769647697476984769947700477014770247703477044770547706477074770847709477104771147712477134771447715477164771747718477194772047721477224772347724477254772647727477284772947730477314773247733477344773547736477374773847739477404774147742477434774447745477464774747748477494775047751477524775347754477554775647757477584775947760477614776247763477644776547766477674776847769477704777147772477734777447775477764777747778477794778047781477824778347784477854778647787477884778947790477914779247793477944779547796477974779847799478004780147802478034780447805478064780747808478094781047811478124781347814478154781647817478184781947820478214782247823478244782547826478274782847829478304783147832478334783447835478364783747838478394784047841478424784347844478454784647847478484784947850478514785247853478544785547856478574785847859478604786147862478634786447865478664786747868478694787047871478724787347874478754787647877478784787947880478814788247883478844788547886478874788847889478904789147892478934789447895478964789747898478994790047901479024790347904479054790647907479084790947910479114791247913479144791547916479174791847919479204792147922479234792447925479264792747928479294793047931479324793347934479354793647937479384793947940479414794247943479444794547946479474794847949479504795147952479534795447955479564795747958479594796047961479624796347964479654796647967479684796947970479714797247973479744797547976479774797847979479804798147982479834798447985479864798747988479894799047991479924799347994479954799647997479984799948000480014800248003480044800548006480074800848009480104801148012480134801448015480164801748018480194802048021480224802348024480254802648027480284802948030480314803248033480344803548036480374803848039480404804148042480434804448045480464804748048480494805048051480524805348054480554805648057480584805948060480614806248063480644806548066480674806848069480704807148072480734807448075480764807748078480794808048081480824808348084480854808648087480884808948090480914809248093480944809548096480974809848099481004810148102481034810448105481064810748108481094811048111481124811348114481154811648117481184811948120481214812248123481244812548126481274812848129481304813148132481334813448135481364813748138481394814048141481424814348144481454814648147481484814948150481514815248153481544815548156481574815848159481604816148162481634816448165481664816748168481694817048171481724817348174481754817648177481784817948180481814818248183481844818548186481874818848189481904819148192481934819448195481964819748198481994820048201482024820348204482054820648207482084820948210482114821248213482144821548216482174821848219482204822148222482234822448225482264822748228482294823048231482324823348234482354823648237482384823948240482414824248243482444824548246482474824848249482504825148252482534825448255482564825748258482594826048261482624826348264482654826648267482684826948270482714827248273482744827548276482774827848279482804828148282482834828448285482864828748288482894829048291482924829348294482954829648297482984829948300483014830248303483044830548306483074830848309483104831148312483134831448315483164831748318483194832048321483224832348324483254832648327483284832948330483314833248333483344833548336483374833848339483404834148342483434834448345483464834748348483494835048351483524835348354483554835648357483584835948360483614836248363483644836548366483674836848369483704837148372483734837448375483764837748378483794838048381483824838348384483854838648387483884838948390483914839248393483944839548396483974839848399484004840148402484034840448405484064840748408484094841048411484124841348414484154841648417484184841948420484214842248423484244842548426484274842848429484304843148432484334843448435484364843748438484394844048441484424844348444484454844648447484484844948450484514845248453484544845548456484574845848459484604846148462484634846448465484664846748468484694847048471484724847348474484754847648477484784847948480484814848248483484844848548486484874848848489484904849148492484934849448495484964849748498484994850048501485024850348504485054850648507485084850948510485114851248513485144851548516485174851848519485204852148522485234852448525485264852748528485294853048531485324853348534485354853648537485384853948540485414854248543485444854548546485474854848549485504855148552485534855448555485564855748558485594856048561485624856348564485654856648567485684856948570485714857248573485744857548576485774857848579485804858148582485834858448585485864858748588485894859048591485924859348594485954859648597485984859948600486014860248603486044860548606486074860848609486104861148612486134861448615486164861748618486194862048621486224862348624486254862648627486284862948630486314863248633486344863548636486374863848639486404864148642486434864448645486464864748648486494865048651486524865348654486554865648657486584865948660486614866248663486644866548666486674866848669486704867148672486734867448675486764867748678486794868048681486824868348684486854868648687486884868948690486914869248693486944869548696486974869848699487004870148702487034870448705487064870748708487094871048711487124871348714487154871648717487184871948720487214872248723487244872548726487274872848729487304873148732487334873448735487364873748738487394874048741487424874348744487454874648747487484874948750487514875248753487544875548756487574875848759487604876148762487634876448765487664876748768487694877048771487724877348774487754877648777487784877948780487814878248783487844878548786487874878848789487904879148792487934879448795487964879748798487994880048801488024880348804488054880648807488084880948810488114881248813488144881548816488174881848819488204882148822488234882448825488264882748828488294883048831488324883348834488354883648837488384883948840488414884248843488444884548846488474884848849488504885148852488534885448855488564885748858488594886048861488624886348864488654886648867488684886948870488714887248873488744887548876488774887848879488804888148882488834888448885488864888748888488894889048891488924889348894488954889648897488984889948900489014890248903489044890548906489074890848909489104891148912489134891448915489164891748918489194892048921489224892348924489254892648927489284892948930489314893248933489344893548936489374893848939489404894148942489434894448945489464894748948489494895048951489524895348954489554895648957489584895948960489614896248963489644896548966489674896848969489704897148972489734897448975489764897748978489794898048981489824898348984489854898648987489884898948990489914899248993489944899548996489974899848999490004900149002490034900449005490064900749008490094901049011490124901349014490154901649017490184901949020490214902249023490244902549026490274902849029490304903149032490334903449035490364903749038490394904049041490424904349044490454904649047490484904949050490514905249053490544905549056490574905849059490604906149062490634906449065490664906749068490694907049071490724907349074490754907649077490784907949080490814908249083490844908549086490874908849089490904909149092490934909449095490964909749098490994910049101491024910349104491054910649107491084910949110491114911249113491144911549116491174911849119491204912149122491234912449125491264912749128491294913049131491324913349134491354913649137491384913949140491414914249143491444914549146491474914849149491504915149152491534915449155491564915749158491594916049161491624916349164491654916649167491684916949170491714917249173491744917549176491774917849179491804918149182491834918449185491864918749188491894919049191491924919349194491954919649197491984919949200492014920249203492044920549206492074920849209492104921149212492134921449215492164921749218492194922049221492224922349224492254922649227492284922949230492314923249233492344923549236492374923849239492404924149242492434924449245492464924749248492494925049251492524925349254492554925649257492584925949260492614926249263492644926549266492674926849269492704927149272492734927449275492764927749278492794928049281492824928349284492854928649287492884928949290492914929249293492944929549296492974929849299493004930149302493034930449305493064930749308493094931049311493124931349314493154931649317493184931949320493214932249323493244932549326493274932849329493304933149332493334933449335493364933749338493394934049341493424934349344493454934649347493484934949350493514935249353493544935549356493574935849359493604936149362493634936449365493664936749368493694937049371493724937349374493754937649377493784937949380493814938249383493844938549386493874938849389493904939149392493934939449395493964939749398493994940049401494024940349404494054940649407494084940949410494114941249413494144941549416494174941849419494204942149422494234942449425494264942749428494294943049431494324943349434494354943649437494384943949440494414944249443494444944549446494474944849449494504945149452494534945449455494564945749458494594946049461494624946349464494654946649467494684946949470494714947249473494744947549476494774947849479494804948149482494834948449485494864948749488494894949049491494924949349494494954949649497494984949949500495014950249503495044950549506495074950849509495104951149512495134951449515495164951749518495194952049521495224952349524495254952649527495284952949530495314953249533495344953549536495374953849539495404954149542495434954449545495464954749548495494955049551495524955349554495554955649557495584955949560495614956249563495644956549566495674956849569495704957149572495734957449575495764957749578495794958049581495824958349584495854958649587495884958949590495914959249593495944959549596495974959849599496004960149602496034960449605496064960749608496094961049611496124961349614496154961649617496184961949620496214962249623496244962549626496274962849629496304963149632496334963449635496364963749638496394964049641496424964349644496454964649647496484964949650496514965249653496544965549656496574965849659496604966149662496634966449665496664966749668496694967049671496724967349674496754967649677496784967949680496814968249683496844968549686496874968849689496904969149692496934969449695496964969749698496994970049701497024970349704497054970649707497084970949710497114971249713497144971549716497174971849719497204972149722497234972449725497264972749728497294973049731497324973349734497354973649737497384973949740497414974249743497444974549746497474974849749497504975149752497534975449755497564975749758497594976049761497624976349764497654976649767497684976949770497714977249773497744977549776497774977849779497804978149782497834978449785497864978749788497894979049791497924979349794497954979649797497984979949800498014980249803498044980549806498074980849809498104981149812498134981449815498164981749818498194982049821498224982349824498254982649827498284982949830498314983249833498344983549836498374983849839498404984149842498434984449845498464984749848498494985049851498524985349854498554985649857498584985949860498614986249863498644986549866498674986849869498704987149872498734987449875498764987749878498794988049881498824988349884498854988649887498884988949890498914989249893498944989549896498974989849899499004990149902499034990449905499064990749908499094991049911499124991349914499154991649917499184991949920499214992249923499244992549926499274992849929499304993149932499334993449935499364993749938499394994049941499424994349944499454994649947499484994949950499514995249953499544995549956499574995849959499604996149962499634996449965499664996749968499694997049971499724997349974499754997649977499784997949980499814998249983499844998549986499874998849989499904999149992499934999449995499964999749998499995000050001500025000350004500055000650007500085000950010500115001250013500145001550016500175001850019500205002150022500235002450025500265002750028500295003050031500325003350034500355003650037500385003950040500415004250043500445004550046500475004850049500505005150052500535005450055500565005750058500595006050061500625006350064500655006650067500685006950070500715007250073500745007550076500775007850079500805008150082500835008450085500865008750088500895009050091500925009350094500955009650097500985009950100501015010250103501045010550106501075010850109501105011150112501135011450115501165011750118501195012050121501225012350124501255012650127501285012950130501315013250133501345013550136501375013850139501405014150142501435014450145501465014750148501495015050151501525015350154501555015650157501585015950160501615016250163501645016550166501675016850169501705017150172501735017450175501765017750178501795018050181501825018350184501855018650187501885018950190501915019250193501945019550196501975019850199502005020150202502035020450205502065020750208502095021050211502125021350214502155021650217502185021950220502215022250223502245022550226502275022850229502305023150232502335023450235502365023750238502395024050241502425024350244502455024650247502485024950250502515025250253502545025550256502575025850259502605026150262502635026450265502665026750268502695027050271502725027350274502755027650277502785027950280502815028250283502845028550286502875028850289502905029150292502935029450295502965029750298502995030050301503025030350304503055030650307503085030950310503115031250313503145031550316503175031850319503205032150322503235032450325503265032750328503295033050331503325033350334503355033650337503385033950340503415034250343503445034550346503475034850349503505035150352503535035450355503565035750358503595036050361503625036350364503655036650367503685036950370503715037250373503745037550376503775037850379503805038150382503835038450385503865038750388503895039050391503925039350394503955039650397503985039950400504015040250403504045040550406504075040850409504105041150412504135041450415504165041750418504195042050421504225042350424504255042650427504285042950430504315043250433504345043550436504375043850439504405044150442504435044450445504465044750448504495045050451504525045350454504555045650457504585045950460504615046250463504645046550466504675046850469504705047150472504735047450475504765047750478504795048050481504825048350484504855048650487504885048950490504915049250493504945049550496504975049850499505005050150502505035050450505505065050750508505095051050511505125051350514505155051650517505185051950520505215052250523505245052550526505275052850529505305053150532505335053450535505365053750538505395054050541505425054350544505455054650547505485054950550505515055250553505545055550556505575055850559505605056150562505635056450565505665056750568505695057050571505725057350574505755057650577505785057950580505815058250583505845058550586505875058850589505905059150592505935059450595505965059750598505995060050601506025060350604506055060650607506085060950610506115061250613506145061550616506175061850619506205062150622506235062450625506265062750628506295063050631506325063350634506355063650637506385063950640506415064250643506445064550646506475064850649506505065150652506535065450655506565065750658506595066050661506625066350664506655066650667506685066950670506715067250673506745067550676506775067850679506805068150682506835068450685506865068750688506895069050691506925069350694506955069650697506985069950700507015070250703507045070550706507075070850709507105071150712507135071450715507165071750718507195072050721507225072350724507255072650727507285072950730507315073250733507345073550736507375073850739507405074150742507435074450745507465074750748507495075050751507525075350754507555075650757507585075950760507615076250763507645076550766507675076850769507705077150772507735077450775507765077750778507795078050781507825078350784507855078650787507885078950790507915079250793507945079550796507975079850799508005080150802508035080450805508065080750808508095081050811508125081350814508155081650817508185081950820508215082250823508245082550826508275082850829508305083150832508335083450835508365083750838508395084050841508425084350844508455084650847508485084950850508515085250853508545085550856508575085850859508605086150862508635086450865508665086750868508695087050871508725087350874508755087650877508785087950880508815088250883508845088550886508875088850889508905089150892508935089450895508965089750898508995090050901509025090350904509055090650907509085090950910509115091250913509145091550916509175091850919509205092150922509235092450925509265092750928509295093050931509325093350934509355093650937509385093950940509415094250943509445094550946509475094850949509505095150952509535095450955509565095750958509595096050961509625096350964509655096650967509685096950970509715097250973509745097550976509775097850979509805098150982509835098450985509865098750988509895099050991509925099350994509955099650997509985099951000510015100251003510045100551006510075100851009510105101151012510135101451015510165101751018510195102051021510225102351024510255102651027510285102951030510315103251033510345103551036510375103851039510405104151042510435104451045510465104751048510495105051051510525105351054510555105651057510585105951060510615106251063510645106551066510675106851069510705107151072510735107451075510765107751078510795108051081510825108351084510855108651087510885108951090510915109251093510945109551096510975109851099511005110151102511035110451105511065110751108511095111051111511125111351114511155111651117511185111951120511215112251123511245112551126511275112851129511305113151132511335113451135511365113751138511395114051141511425114351144511455114651147511485114951150511515115251153511545115551156511575115851159511605116151162511635116451165511665116751168511695117051171511725117351174511755117651177511785117951180511815118251183511845118551186511875118851189511905119151192511935119451195511965119751198511995120051201512025120351204512055120651207512085120951210512115121251213512145121551216512175121851219512205122151222512235122451225512265122751228512295123051231512325123351234512355123651237512385123951240512415124251243512445124551246512475124851249512505125151252512535125451255512565125751258512595126051261512625126351264512655126651267512685126951270512715127251273512745127551276512775127851279512805128151282512835128451285512865128751288512895129051291512925129351294512955129651297512985129951300513015130251303513045130551306513075130851309513105131151312513135131451315513165131751318513195132051321513225132351324513255132651327513285132951330513315133251333513345133551336513375133851339513405134151342513435134451345513465134751348513495135051351513525135351354513555135651357513585135951360513615136251363513645136551366513675136851369513705137151372513735137451375513765137751378513795138051381513825138351384513855138651387513885138951390513915139251393513945139551396513975139851399514005140151402514035140451405514065140751408514095141051411514125141351414514155141651417514185141951420514215142251423514245142551426514275142851429514305143151432514335143451435514365143751438514395144051441514425144351444514455144651447514485144951450514515145251453514545145551456514575145851459514605146151462514635146451465514665146751468514695147051471514725147351474514755147651477514785147951480514815148251483514845148551486514875148851489514905149151492514935149451495514965149751498514995150051501515025150351504515055150651507515085150951510515115151251513515145151551516515175151851519515205152151522515235152451525515265152751528515295153051531515325153351534515355153651537515385153951540515415154251543515445154551546515475154851549515505155151552515535155451555515565155751558515595156051561515625156351564515655156651567515685156951570515715157251573515745157551576515775157851579515805158151582515835158451585515865158751588515895159051591515925159351594515955159651597515985159951600516015160251603516045160551606516075160851609516105161151612516135161451615516165161751618516195162051621516225162351624516255162651627516285162951630516315163251633516345163551636516375163851639516405164151642516435164451645516465164751648516495165051651516525165351654516555165651657516585165951660516615166251663516645166551666516675166851669516705167151672516735167451675516765167751678516795168051681516825168351684516855168651687516885168951690516915169251693516945169551696516975169851699517005170151702517035170451705517065170751708517095171051711517125171351714517155171651717517185171951720517215172251723517245172551726517275172851729517305173151732517335173451735517365173751738517395174051741517425174351744517455174651747517485174951750517515175251753517545175551756517575175851759517605176151762517635176451765517665176751768517695177051771517725177351774517755177651777517785177951780517815178251783517845178551786517875178851789517905179151792517935179451795517965179751798517995180051801518025180351804518055180651807518085180951810518115181251813518145181551816518175181851819518205182151822518235182451825518265182751828518295183051831518325183351834518355183651837518385183951840518415184251843518445184551846518475184851849518505185151852518535185451855518565185751858518595186051861518625186351864518655186651867518685186951870518715187251873518745187551876518775187851879518805188151882518835188451885518865188751888518895189051891518925189351894518955189651897518985189951900519015190251903519045190551906519075190851909519105191151912519135191451915519165191751918519195192051921519225192351924519255192651927519285192951930519315193251933519345193551936519375193851939519405194151942519435194451945519465194751948519495195051951519525195351954519555195651957519585195951960519615196251963519645196551966519675196851969519705197151972519735197451975519765197751978519795198051981519825198351984519855198651987519885198951990519915199251993519945199551996519975199851999520005200152002520035200452005520065200752008520095201052011520125201352014520155201652017520185201952020520215202252023520245202552026520275202852029520305203152032520335203452035520365203752038520395204052041520425204352044520455204652047520485204952050520515205252053520545205552056520575205852059520605206152062520635206452065520665206752068520695207052071520725207352074520755207652077520785207952080520815208252083520845208552086520875208852089520905209152092520935209452095520965209752098520995210052101521025210352104521055210652107521085210952110521115211252113521145211552116521175211852119521205212152122521235212452125521265212752128521295213052131521325213352134521355213652137521385213952140521415214252143521445214552146521475214852149521505215152152521535215452155521565215752158521595216052161521625216352164521655216652167521685216952170521715217252173521745217552176521775217852179521805218152182521835218452185521865218752188521895219052191521925219352194521955219652197521985219952200522015220252203522045220552206522075220852209522105221152212522135221452215522165221752218522195222052221522225222352224522255222652227522285222952230522315223252233522345223552236522375223852239522405224152242522435224452245522465224752248522495225052251522525225352254522555225652257522585225952260522615226252263522645226552266522675226852269522705227152272522735227452275522765227752278522795228052281522825228352284522855228652287522885228952290522915229252293522945229552296522975229852299523005230152302523035230452305523065230752308523095231052311523125231352314523155231652317523185231952320523215232252323523245232552326523275232852329523305233152332523335233452335523365233752338523395234052341523425234352344523455234652347523485234952350523515235252353523545235552356523575235852359523605236152362523635236452365523665236752368523695237052371523725237352374523755237652377523785237952380523815238252383523845238552386523875238852389523905239152392523935239452395523965239752398523995240052401524025240352404524055240652407524085240952410524115241252413524145241552416524175241852419524205242152422524235242452425524265242752428524295243052431524325243352434524355243652437524385243952440524415244252443524445244552446524475244852449524505245152452524535245452455524565245752458524595246052461524625246352464524655246652467524685246952470524715247252473524745247552476524775247852479524805248152482524835248452485524865248752488524895249052491524925249352494524955249652497524985249952500525015250252503525045250552506525075250852509525105251152512525135251452515525165251752518525195252052521525225252352524525255252652527525285252952530525315253252533525345253552536525375253852539525405254152542525435254452545525465254752548525495255052551525525255352554525555255652557525585255952560525615256252563525645256552566525675256852569525705257152572525735257452575525765257752578525795258052581525825258352584525855258652587525885258952590525915259252593525945259552596525975259852599526005260152602526035260452605526065260752608526095261052611526125261352614526155261652617526185261952620526215262252623526245262552626526275262852629526305263152632526335263452635526365263752638526395264052641526425264352644526455264652647526485264952650526515265252653526545265552656526575265852659526605266152662526635266452665526665266752668526695267052671526725267352674526755267652677526785267952680526815268252683526845268552686526875268852689526905269152692526935269452695526965269752698526995270052701527025270352704527055270652707527085270952710527115271252713527145271552716527175271852719527205272152722527235272452725527265272752728527295273052731527325273352734527355273652737527385273952740527415274252743527445274552746527475274852749527505275152752527535275452755527565275752758527595276052761527625276352764527655276652767527685276952770527715277252773527745277552776527775277852779527805278152782527835278452785527865278752788527895279052791527925279352794527955279652797527985279952800528015280252803528045280552806528075280852809528105281152812528135281452815528165281752818528195282052821528225282352824528255282652827528285282952830528315283252833528345283552836528375283852839528405284152842528435284452845528465284752848528495285052851528525285352854528555285652857528585285952860528615286252863528645286552866528675286852869528705287152872528735287452875528765287752878528795288052881528825288352884528855288652887528885288952890528915289252893528945289552896528975289852899529005290152902529035290452905529065290752908529095291052911529125291352914529155291652917529185291952920529215292252923529245292552926529275292852929529305293152932529335293452935529365293752938529395294052941529425294352944529455294652947529485294952950529515295252953529545295552956529575295852959529605296152962529635296452965529665296752968529695297052971529725297352974529755297652977529785297952980529815298252983529845298552986529875298852989529905299152992529935299452995529965299752998529995300053001530025300353004530055300653007530085300953010530115301253013530145301553016530175301853019530205302153022530235302453025530265302753028530295303053031530325303353034530355303653037530385303953040530415304253043530445304553046530475304853049530505305153052530535305453055530565305753058530595306053061530625306353064530655306653067530685306953070530715307253073530745307553076530775307853079530805308153082530835308453085530865308753088530895309053091530925309353094530955309653097530985309953100531015310253103531045310553106531075310853109531105311153112531135311453115531165311753118531195312053121531225312353124531255312653127531285312953130531315313253133531345313553136531375313853139531405314153142531435314453145531465314753148531495315053151531525315353154531555315653157531585315953160531615316253163531645316553166531675316853169531705317153172531735317453175531765317753178531795318053181531825318353184531855318653187531885318953190531915319253193531945319553196531975319853199532005320153202532035320453205532065320753208532095321053211532125321353214532155321653217532185321953220532215322253223532245322553226532275322853229532305323153232532335323453235532365323753238532395324053241532425324353244532455324653247532485324953250532515325253253532545325553256532575325853259532605326153262532635326453265532665326753268532695327053271532725327353274532755327653277532785327953280532815328253283532845328553286532875328853289532905329153292532935329453295532965329753298532995330053301533025330353304533055330653307533085330953310533115331253313533145331553316533175331853319533205332153322533235332453325533265332753328533295333053331533325333353334533355333653337533385333953340533415334253343533445334553346533475334853349533505335153352533535335453355533565335753358533595336053361533625336353364533655336653367533685336953370533715337253373533745337553376533775337853379533805338153382533835338453385533865338753388533895339053391533925339353394533955339653397533985339953400534015340253403534045340553406534075340853409534105341153412534135341453415534165341753418534195342053421534225342353424534255342653427534285342953430534315343253433534345343553436534375343853439534405344153442534435344453445534465344753448534495345053451534525345353454534555345653457534585345953460534615346253463534645346553466534675346853469534705347153472534735347453475534765347753478534795348053481534825348353484534855348653487534885348953490534915349253493534945349553496534975349853499535005350153502535035350453505535065350753508535095351053511535125351353514535155351653517535185351953520535215352253523535245352553526535275352853529535305353153532535335353453535535365353753538535395354053541535425354353544535455354653547535485354953550535515355253553535545355553556535575355853559535605356153562535635356453565535665356753568535695357053571535725357353574535755357653577535785357953580535815358253583535845358553586535875358853589535905359153592535935359453595535965359753598535995360053601536025360353604536055360653607536085360953610536115361253613536145361553616536175361853619536205362153622536235362453625536265362753628536295363053631536325363353634536355363653637536385363953640536415364253643536445364553646536475364853649536505365153652536535365453655536565365753658536595366053661536625366353664536655366653667536685366953670536715367253673536745367553676536775367853679536805368153682536835368453685536865368753688536895369053691536925369353694536955369653697536985369953700537015370253703537045370553706537075370853709537105371153712537135371453715537165371753718537195372053721537225372353724537255372653727537285372953730537315373253733537345373553736537375373853739537405374153742537435374453745537465374753748537495375053751537525375353754537555375653757537585375953760537615376253763537645376553766537675376853769537705377153772537735377453775537765377753778537795378053781537825378353784537855378653787537885378953790537915379253793537945379553796537975379853799538005380153802538035380453805538065380753808538095381053811538125381353814538155381653817538185381953820538215382253823538245382553826538275382853829538305383153832538335383453835538365383753838538395384053841538425384353844538455384653847538485384953850538515385253853538545385553856538575385853859538605386153862538635386453865538665386753868538695387053871538725387353874538755387653877538785387953880538815388253883538845388553886538875388853889538905389153892538935389453895538965389753898538995390053901539025390353904539055390653907539085390953910539115391253913539145391553916539175391853919539205392153922539235392453925539265392753928539295393053931539325393353934539355393653937539385393953940539415394253943539445394553946539475394853949539505395153952539535395453955539565395753958539595396053961539625396353964539655396653967539685396953970539715397253973539745397553976539775397853979539805398153982539835398453985539865398753988539895399053991539925399353994539955399653997539985399954000540015400254003540045400554006540075400854009540105401154012540135401454015540165401754018540195402054021540225402354024540255402654027540285402954030540315403254033540345403554036540375403854039540405404154042540435404454045540465404754048540495405054051540525405354054540555405654057540585405954060540615406254063540645406554066540675406854069540705407154072540735407454075540765407754078540795408054081540825408354084540855408654087540885408954090540915409254093540945409554096540975409854099541005410154102541035410454105541065410754108541095411054111541125411354114541155411654117541185411954120541215412254123541245412554126541275412854129541305413154132541335413454135541365413754138541395414054141541425414354144541455414654147541485414954150541515415254153541545415554156541575415854159541605416154162541635416454165541665416754168541695417054171541725417354174541755417654177541785417954180541815418254183541845418554186541875418854189541905419154192541935419454195541965419754198541995420054201542025420354204542055420654207542085420954210542115421254213542145421554216542175421854219542205422154222542235422454225542265422754228542295423054231542325423354234542355423654237542385423954240542415424254243542445424554246542475424854249542505425154252542535425454255542565425754258542595426054261542625426354264542655426654267542685426954270542715427254273542745427554276542775427854279542805428154282542835428454285542865428754288542895429054291542925429354294542955429654297542985429954300543015430254303543045430554306543075430854309543105431154312543135431454315543165431754318543195432054321543225432354324543255432654327543285432954330543315433254333543345433554336543375433854339543405434154342543435434454345543465434754348543495435054351543525435354354543555435654357543585435954360543615436254363543645436554366543675436854369543705437154372543735437454375543765437754378543795438054381543825438354384543855438654387543885438954390543915439254393543945439554396543975439854399544005440154402544035440454405544065440754408544095441054411544125441354414544155441654417544185441954420544215442254423544245442554426544275442854429544305443154432544335443454435544365443754438544395444054441544425444354444544455444654447544485444954450544515445254453544545445554456544575445854459544605446154462544635446454465544665446754468544695447054471544725447354474544755447654477544785447954480544815448254483544845448554486544875448854489544905449154492544935449454495544965449754498544995450054501545025450354504545055450654507545085450954510545115451254513545145451554516545175451854519545205452154522545235452454525545265452754528545295453054531545325453354534545355453654537545385453954540545415454254543545445454554546545475454854549545505455154552545535455454555545565455754558545595456054561545625456354564545655456654567545685456954570545715457254573545745457554576545775457854579545805458154582545835458454585545865458754588545895459054591545925459354594545955459654597545985459954600546015460254603546045460554606546075460854609546105461154612546135461454615546165461754618546195462054621546225462354624546255462654627546285462954630546315463254633546345463554636546375463854639546405464154642546435464454645546465464754648546495465054651546525465354654546555465654657546585465954660546615466254663546645466554666546675466854669546705467154672546735467454675546765467754678546795468054681546825468354684546855468654687546885468954690546915469254693546945469554696546975469854699547005470154702547035470454705547065470754708547095471054711547125471354714547155471654717547185471954720547215472254723547245472554726547275472854729547305473154732547335473454735547365473754738547395474054741547425474354744547455474654747547485474954750547515475254753547545475554756547575475854759547605476154762547635476454765547665476754768547695477054771547725477354774547755477654777547785477954780547815478254783547845478554786547875478854789547905479154792547935479454795547965479754798547995480054801548025480354804548055480654807548085480954810548115481254813548145481554816548175481854819548205482154822548235482454825548265482754828548295483054831548325483354834548355483654837548385483954840548415484254843548445484554846548475484854849548505485154852548535485454855548565485754858548595486054861548625486354864548655486654867548685486954870548715487254873548745487554876548775487854879548805488154882548835488454885548865488754888548895489054891548925489354894548955489654897548985489954900549015490254903549045490554906549075490854909549105491154912549135491454915549165491754918549195492054921549225492354924549255492654927549285492954930549315493254933549345493554936549375493854939549405494154942549435494454945549465494754948549495495054951549525495354954549555495654957549585495954960549615496254963549645496554966549675496854969549705497154972549735497454975549765497754978549795498054981549825498354984549855498654987549885498954990549915499254993549945499554996549975499854999550005500155002550035500455005550065500755008550095501055011550125501355014550155501655017550185501955020550215502255023550245502555026550275502855029550305503155032550335503455035550365503755038550395504055041550425504355044550455504655047550485504955050550515505255053550545505555056550575505855059550605506155062550635506455065550665506755068550695507055071550725507355074550755507655077550785507955080550815508255083550845508555086550875508855089550905509155092550935509455095550965509755098550995510055101551025510355104551055510655107551085510955110551115511255113551145511555116551175511855119551205512155122551235512455125551265512755128551295513055131551325513355134551355513655137551385513955140551415514255143551445514555146551475514855149551505515155152551535515455155551565515755158551595516055161551625516355164551655516655167551685516955170551715517255173551745517555176551775517855179551805518155182551835518455185551865518755188551895519055191551925519355194551955519655197551985519955200552015520255203552045520555206552075520855209552105521155212552135521455215552165521755218552195522055221552225522355224552255522655227552285522955230552315523255233552345523555236552375523855239552405524155242552435524455245552465524755248552495525055251552525525355254552555525655257552585525955260552615526255263552645526555266552675526855269552705527155272552735527455275552765527755278552795528055281552825528355284552855528655287552885528955290552915529255293552945529555296552975529855299553005530155302553035530455305553065530755308553095531055311553125531355314553155531655317553185531955320553215532255323553245532555326553275532855329553305533155332553335533455335553365533755338553395534055341553425534355344553455534655347553485534955350553515535255353553545535555356553575535855359553605536155362553635536455365553665536755368553695537055371553725537355374553755537655377553785537955380553815538255383553845538555386553875538855389553905539155392553935539455395553965539755398553995540055401554025540355404554055540655407554085540955410554115541255413554145541555416554175541855419554205542155422554235542455425554265542755428554295543055431554325543355434554355543655437554385543955440554415544255443554445544555446554475544855449554505545155452554535545455455554565545755458554595546055461554625546355464554655546655467554685546955470554715547255473554745547555476554775547855479554805548155482554835548455485554865548755488554895549055491554925549355494554955549655497554985549955500555015550255503555045550555506555075550855509555105551155512555135551455515555165551755518555195552055521555225552355524555255552655527555285552955530555315553255533555345553555536555375553855539555405554155542555435554455545555465554755548555495555055551555525555355554555555555655557555585555955560555615556255563555645556555566555675556855569555705557155572555735557455575555765557755578555795558055581555825558355584555855558655587555885558955590555915559255593555945559555596555975559855599556005560155602556035560455605556065560755608556095561055611556125561355614556155561655617556185561955620556215562255623556245562555626556275562855629556305563155632556335563455635556365563755638556395564055641556425564355644556455564655647556485564955650556515565255653556545565555656556575565855659556605566155662556635566455665556665566755668556695567055671556725567355674556755567655677556785567955680556815568255683556845568555686556875568855689556905569155692556935569455695556965569755698556995570055701557025570355704557055570655707557085570955710557115571255713557145571555716557175571855719557205572155722557235572455725557265572755728557295573055731557325573355734557355573655737557385573955740557415574255743557445574555746557475574855749557505575155752557535575455755557565575755758557595576055761557625576355764557655576655767557685576955770557715577255773557745577555776557775577855779557805578155782557835578455785557865578755788557895579055791557925579355794557955579655797557985579955800558015580255803558045580555806558075580855809558105581155812558135581455815558165581755818558195582055821558225582355824558255582655827558285582955830558315583255833558345583555836558375583855839558405584155842558435584455845558465584755848558495585055851558525585355854558555585655857558585585955860558615586255863558645586555866558675586855869558705587155872558735587455875558765587755878558795588055881558825588355884558855588655887558885588955890558915589255893558945589555896558975589855899559005590155902559035590455905559065590755908559095591055911559125591355914559155591655917559185591955920559215592255923559245592555926559275592855929559305593155932559335593455935559365593755938559395594055941559425594355944559455594655947559485594955950559515595255953559545595555956559575595855959559605596155962559635596455965559665596755968559695597055971559725597355974559755597655977559785597955980559815598255983559845598555986559875598855989559905599155992559935599455995559965599755998559995600056001560025600356004560055600656007560085600956010560115601256013560145601556016560175601856019560205602156022560235602456025560265602756028560295603056031560325603356034560355603656037560385603956040560415604256043560445604556046560475604856049560505605156052560535605456055560565605756058560595606056061560625606356064560655606656067560685606956070560715607256073560745607556076560775607856079560805608156082560835608456085560865608756088560895609056091560925609356094560955609656097560985609956100561015610256103561045610556106561075610856109561105611156112561135611456115561165611756118561195612056121561225612356124561255612656127561285612956130561315613256133561345613556136561375613856139561405614156142561435614456145561465614756148561495615056151561525615356154561555615656157561585615956160561615616256163561645616556166561675616856169561705617156172561735617456175561765617756178561795618056181561825618356184561855618656187561885618956190561915619256193561945619556196561975619856199562005620156202562035620456205562065620756208562095621056211562125621356214562155621656217562185621956220562215622256223562245622556226562275622856229562305623156232562335623456235562365623756238562395624056241562425624356244562455624656247562485624956250562515625256253562545625556256562575625856259562605626156262562635626456265562665626756268562695627056271562725627356274562755627656277562785627956280562815628256283562845628556286562875628856289562905629156292562935629456295562965629756298562995630056301563025630356304563055630656307563085630956310563115631256313563145631556316563175631856319563205632156322563235632456325563265632756328563295633056331563325633356334563355633656337563385633956340563415634256343563445634556346563475634856349563505635156352563535635456355563565635756358563595636056361563625636356364563655636656367563685636956370563715637256373563745637556376563775637856379563805638156382563835638456385563865638756388563895639056391563925639356394563955639656397563985639956400564015640256403564045640556406564075640856409564105641156412564135641456415564165641756418564195642056421564225642356424564255642656427564285642956430564315643256433564345643556436564375643856439564405644156442564435644456445564465644756448564495645056451564525645356454564555645656457564585645956460564615646256463564645646556466564675646856469564705647156472564735647456475564765647756478564795648056481564825648356484564855648656487564885648956490564915649256493564945649556496564975649856499565005650156502565035650456505565065650756508565095651056511565125651356514565155651656517565185651956520565215652256523565245652556526565275652856529565305653156532565335653456535565365653756538565395654056541565425654356544565455654656547565485654956550565515655256553565545655556556565575655856559565605656156562565635656456565565665656756568565695657056571565725657356574565755657656577565785657956580565815658256583565845658556586565875658856589565905659156592565935659456595565965659756598565995660056601566025660356604566055660656607566085660956610566115661256613566145661556616566175661856619566205662156622566235662456625566265662756628566295663056631566325663356634566355663656637566385663956640566415664256643566445664556646566475664856649566505665156652566535665456655566565665756658566595666056661566625666356664566655666656667566685666956670566715667256673566745667556676566775667856679566805668156682566835668456685566865668756688566895669056691566925669356694566955669656697566985669956700567015670256703567045670556706567075670856709567105671156712567135671456715567165671756718567195672056721567225672356724567255672656727567285672956730567315673256733567345673556736567375673856739567405674156742567435674456745567465674756748567495675056751567525675356754567555675656757567585675956760567615676256763567645676556766567675676856769567705677156772567735677456775567765677756778567795678056781567825678356784567855678656787567885678956790567915679256793567945679556796567975679856799568005680156802568035680456805568065680756808568095681056811568125681356814568155681656817568185681956820568215682256823568245682556826568275682856829568305683156832568335683456835568365683756838568395684056841568425684356844568455684656847568485684956850568515685256853568545685556856568575685856859568605686156862568635686456865568665686756868568695687056871568725687356874568755687656877568785687956880568815688256883568845688556886568875688856889568905689156892568935689456895568965689756898568995690056901569025690356904569055690656907569085690956910569115691256913569145691556916569175691856919569205692156922569235692456925569265692756928569295693056931569325693356934569355693656937569385693956940569415694256943569445694556946569475694856949569505695156952569535695456955569565695756958569595696056961569625696356964569655696656967569685696956970569715697256973569745697556976569775697856979569805698156982569835698456985569865698756988569895699056991569925699356994569955699656997569985699957000570015700257003570045700557006570075700857009570105701157012570135701457015570165701757018570195702057021570225702357024570255702657027570285702957030570315703257033570345703557036570375703857039570405704157042570435704457045570465704757048570495705057051570525705357054570555705657057570585705957060570615706257063570645706557066570675706857069570705707157072570735707457075570765707757078570795708057081570825708357084570855708657087570885708957090570915709257093570945709557096570975709857099571005710157102571035710457105571065710757108571095711057111571125711357114571155711657117571185711957120571215712257123571245712557126571275712857129571305713157132571335713457135571365713757138571395714057141571425714357144571455714657147571485714957150571515715257153571545715557156571575715857159571605716157162571635716457165571665716757168571695717057171571725717357174571755717657177571785717957180571815718257183571845718557186571875718857189571905719157192571935719457195571965719757198571995720057201572025720357204572055720657207572085720957210572115721257213572145721557216572175721857219572205722157222572235722457225572265722757228572295723057231572325723357234572355723657237572385723957240572415724257243572445724557246572475724857249572505725157252572535725457255572565725757258572595726057261572625726357264572655726657267572685726957270572715727257273572745727557276572775727857279572805728157282572835728457285572865728757288572895729057291572925729357294572955729657297572985729957300573015730257303573045730557306573075730857309573105731157312573135731457315573165731757318573195732057321573225732357324573255732657327573285732957330573315733257333573345733557336573375733857339573405734157342573435734457345573465734757348573495735057351573525735357354573555735657357573585735957360573615736257363573645736557366573675736857369573705737157372573735737457375573765737757378573795738057381573825738357384573855738657387573885738957390573915739257393573945739557396573975739857399574005740157402574035740457405574065740757408574095741057411574125741357414574155741657417574185741957420574215742257423574245742557426574275742857429574305743157432574335743457435574365743757438574395744057441574425744357444574455744657447574485744957450574515745257453574545745557456574575745857459574605746157462574635746457465574665746757468574695747057471574725747357474574755747657477574785747957480574815748257483574845748557486574875748857489574905749157492574935749457495574965749757498574995750057501575025750357504575055750657507575085750957510575115751257513575145751557516575175751857519575205752157522575235752457525575265752757528575295753057531575325753357534575355753657537575385753957540575415754257543575445754557546575475754857549575505755157552575535755457555575565755757558575595756057561575625756357564575655756657567575685756957570575715757257573575745757557576575775757857579575805758157582575835758457585575865758757588575895759057591575925759357594575955759657597575985759957600576015760257603576045760557606576075760857609576105761157612576135761457615576165761757618576195762057621576225762357624576255762657627576285762957630576315763257633576345763557636576375763857639576405764157642576435764457645576465764757648576495765057651576525765357654576555765657657576585765957660576615766257663576645766557666576675766857669576705767157672576735767457675576765767757678576795768057681576825768357684576855768657687576885768957690576915769257693576945769557696576975769857699577005770157702577035770457705577065770757708577095771057711577125771357714577155771657717577185771957720577215772257723577245772557726577275772857729577305773157732577335773457735577365773757738577395774057741577425774357744577455774657747577485774957750577515775257753577545775557756577575775857759577605776157762577635776457765577665776757768577695777057771577725777357774577755777657777577785777957780577815778257783577845778557786577875778857789577905779157792577935779457795577965779757798577995780057801578025780357804578055780657807578085780957810578115781257813578145781557816578175781857819578205782157822578235782457825578265782757828578295783057831578325783357834578355783657837578385783957840578415784257843578445784557846578475784857849578505785157852578535785457855578565785757858578595786057861578625786357864578655786657867578685786957870578715787257873578745787557876578775787857879578805788157882578835788457885578865788757888578895789057891578925789357894578955789657897578985789957900579015790257903579045790557906579075790857909579105791157912579135791457915579165791757918579195792057921579225792357924579255792657927579285792957930579315793257933579345793557936579375793857939579405794157942579435794457945579465794757948579495795057951579525795357954579555795657957579585795957960579615796257963579645796557966579675796857969579705797157972579735797457975579765797757978579795798057981579825798357984579855798657987579885798957990579915799257993579945799557996579975799857999580005800158002580035800458005580065800758008580095801058011580125801358014580155801658017580185801958020580215802258023580245802558026580275802858029580305803158032580335803458035580365803758038580395804058041580425804358044580455804658047580485804958050580515805258053580545805558056580575805858059580605806158062580635806458065580665806758068580695807058071580725807358074580755807658077580785807958080580815808258083580845808558086580875808858089580905809158092580935809458095580965809758098580995810058101581025810358104581055810658107581085810958110581115811258113581145811558116581175811858119581205812158122581235812458125581265812758128581295813058131581325813358134581355813658137581385813958140581415814258143581445814558146581475814858149581505815158152581535815458155581565815758158581595816058161581625816358164581655816658167581685816958170581715817258173581745817558176581775817858179581805818158182581835818458185581865818758188581895819058191581925819358194581955819658197581985819958200582015820258203582045820558206582075820858209582105821158212582135821458215582165821758218582195822058221582225822358224582255822658227582285822958230582315823258233582345823558236582375823858239582405824158242582435824458245582465824758248582495825058251582525825358254582555825658257582585825958260582615826258263582645826558266582675826858269582705827158272582735827458275582765827758278582795828058281582825828358284582855828658287582885828958290582915829258293582945829558296582975829858299583005830158302583035830458305583065830758308583095831058311583125831358314583155831658317583185831958320583215832258323583245832558326583275832858329583305833158332583335833458335583365833758338583395834058341583425834358344583455834658347583485834958350583515835258353583545835558356583575835858359583605836158362583635836458365583665836758368583695837058371583725837358374583755837658377583785837958380583815838258383583845838558386583875838858389583905839158392583935839458395583965839758398583995840058401584025840358404584055840658407584085840958410584115841258413584145841558416584175841858419584205842158422584235842458425584265842758428584295843058431584325843358434584355843658437584385843958440584415844258443584445844558446584475844858449584505845158452584535845458455584565845758458584595846058461584625846358464584655846658467584685846958470584715847258473584745847558476584775847858479584805848158482584835848458485584865848758488584895849058491584925849358494584955849658497584985849958500585015850258503585045850558506585075850858509585105851158512585135851458515585165851758518585195852058521585225852358524585255852658527585285852958530585315853258533585345853558536585375853858539585405854158542585435854458545585465854758548585495855058551585525855358554585555855658557585585855958560585615856258563585645856558566585675856858569585705857158572585735857458575585765857758578585795858058581585825858358584585855858658587585885858958590585915859258593585945859558596585975859858599586005860158602586035860458605586065860758608586095861058611586125861358614586155861658617586185861958620586215862258623586245862558626586275862858629586305863158632586335863458635586365863758638586395864058641586425864358644586455864658647586485864958650586515865258653586545865558656586575865858659586605866158662586635866458665586665866758668586695867058671586725867358674586755867658677586785867958680586815868258683586845868558686586875868858689586905869158692586935869458695586965869758698586995870058701587025870358704587055870658707587085870958710587115871258713587145871558716587175871858719587205872158722587235872458725587265872758728587295873058731587325873358734587355873658737587385873958740587415874258743587445874558746587475874858749587505875158752587535875458755587565875758758587595876058761587625876358764587655876658767587685876958770587715877258773587745877558776587775877858779587805878158782587835878458785587865878758788587895879058791587925879358794587955879658797587985879958800588015880258803588045880558806588075880858809588105881158812588135881458815588165881758818588195882058821588225882358824588255882658827588285882958830588315883258833588345883558836588375883858839588405884158842588435884458845588465884758848588495885058851588525885358854588555885658857588585885958860588615886258863588645886558866588675886858869588705887158872588735887458875588765887758878588795888058881588825888358884588855888658887588885888958890588915889258893588945889558896588975889858899589005890158902589035890458905589065890758908589095891058911589125891358914589155891658917589185891958920589215892258923589245892558926589275892858929589305893158932589335893458935589365893758938589395894058941589425894358944589455894658947589485894958950589515895258953589545895558956589575895858959589605896158962589635896458965589665896758968589695897058971589725897358974589755897658977589785897958980589815898258983589845898558986589875898858989589905899158992589935899458995589965899758998589995900059001590025900359004590055900659007590085900959010590115901259013590145901559016590175901859019590205902159022590235902459025590265902759028590295903059031590325903359034590355903659037590385903959040590415904259043590445904559046590475904859049590505905159052590535905459055590565905759058590595906059061590625906359064590655906659067590685906959070590715907259073590745907559076590775907859079590805908159082590835908459085590865908759088590895909059091590925909359094590955909659097590985909959100591015910259103591045910559106591075910859109591105911159112591135911459115591165911759118591195912059121591225912359124591255912659127591285912959130591315913259133591345913559136591375913859139591405914159142591435914459145591465914759148591495915059151591525915359154591555915659157591585915959160591615916259163591645916559166591675916859169591705917159172591735917459175591765917759178591795918059181591825918359184591855918659187591885918959190591915919259193591945919559196591975919859199592005920159202592035920459205592065920759208592095921059211592125921359214592155921659217592185921959220592215922259223592245922559226592275922859229592305923159232592335923459235592365923759238592395924059241592425924359244592455924659247592485924959250592515925259253592545925559256592575925859259592605926159262592635926459265592665926759268592695927059271592725927359274592755927659277592785927959280592815928259283592845928559286592875928859289592905929159292592935929459295592965929759298592995930059301593025930359304593055930659307593085930959310593115931259313593145931559316593175931859319593205932159322593235932459325593265932759328593295933059331593325933359334593355933659337593385933959340593415934259343593445934559346593475934859349593505935159352593535935459355593565935759358593595936059361593625936359364593655936659367593685936959370593715937259373593745937559376593775937859379593805938159382593835938459385593865938759388593895939059391593925939359394593955939659397593985939959400594015940259403594045940559406594075940859409594105941159412594135941459415594165941759418594195942059421594225942359424594255942659427594285942959430594315943259433594345943559436594375943859439594405944159442594435944459445594465944759448594495945059451594525945359454594555945659457594585945959460594615946259463594645946559466594675946859469594705947159472594735947459475594765947759478594795948059481594825948359484594855948659487594885948959490594915949259493594945949559496594975949859499595005950159502595035950459505595065950759508595095951059511595125951359514595155951659517595185951959520595215952259523595245952559526595275952859529595305953159532595335953459535595365953759538595395954059541595425954359544595455954659547595485954959550595515955259553595545955559556595575955859559595605956159562595635956459565595665956759568595695957059571595725957359574595755957659577595785957959580595815958259583595845958559586595875958859589595905959159592595935959459595595965959759598595995960059601596025960359604596055960659607596085960959610596115961259613596145961559616596175961859619596205962159622596235962459625596265962759628596295963059631596325963359634596355963659637596385963959640596415964259643596445964559646596475964859649596505965159652596535965459655596565965759658596595966059661596625966359664596655966659667596685966959670596715967259673596745967559676596775967859679596805968159682596835968459685596865968759688596895969059691596925969359694596955969659697596985969959700597015970259703597045970559706597075970859709597105971159712597135971459715597165971759718597195972059721597225972359724597255972659727597285972959730597315973259733597345973559736597375973859739597405974159742597435974459745597465974759748597495975059751597525975359754597555975659757597585975959760597615976259763597645976559766597675976859769597705977159772597735977459775597765977759778597795978059781597825978359784597855978659787597885978959790597915979259793597945979559796597975979859799598005980159802598035980459805598065980759808598095981059811598125981359814598155981659817598185981959820598215982259823598245982559826598275982859829598305983159832598335983459835598365983759838598395984059841598425984359844598455984659847598485984959850598515985259853598545985559856598575985859859598605986159862598635986459865598665986759868598695987059871598725987359874598755987659877598785987959880598815988259883598845988559886598875988859889598905989159892598935989459895598965989759898598995990059901599025990359904599055990659907599085990959910599115991259913599145991559916599175991859919599205992159922599235992459925599265992759928599295993059931599325993359934599355993659937599385993959940599415994259943599445994559946599475994859949599505995159952599535995459955599565995759958599595996059961599625996359964599655996659967599685996959970599715997259973599745997559976599775997859979599805998159982599835998459985599865998759988599895999059991599925999359994599955999659997599985999960000600016000260003600046000560006600076000860009600106001160012600136001460015600166001760018600196002060021600226002360024600256002660027600286002960030600316003260033600346003560036600376003860039600406004160042600436004460045600466004760048600496005060051600526005360054600556005660057600586005960060600616006260063600646006560066600676006860069600706007160072600736007460075600766007760078600796008060081600826008360084600856008660087600886008960090600916009260093600946009560096600976009860099601006010160102601036010460105601066010760108601096011060111601126011360114601156011660117601186011960120601216012260123601246012560126601276012860129601306013160132601336013460135601366013760138601396014060141601426014360144601456014660147601486014960150601516015260153601546015560156601576015860159601606016160162601636016460165601666016760168601696017060171601726017360174601756017660177601786017960180601816018260183601846018560186601876018860189601906019160192601936019460195601966019760198601996020060201602026020360204602056020660207602086020960210602116021260213602146021560216602176021860219602206022160222602236022460225602266022760228602296023060231602326023360234602356023660237602386023960240602416024260243602446024560246602476024860249602506025160252602536025460255602566025760258602596026060261602626026360264602656026660267602686026960270602716027260273602746027560276602776027860279602806028160282602836028460285602866028760288602896029060291602926029360294602956029660297602986029960300603016030260303603046030560306603076030860309603106031160312603136031460315603166031760318603196032060321603226032360324603256032660327603286032960330603316033260333603346033560336603376033860339603406034160342603436034460345603466034760348603496035060351603526035360354603556035660357603586035960360603616036260363603646036560366603676036860369603706037160372603736037460375603766037760378603796038060381603826038360384603856038660387603886038960390603916039260393603946039560396603976039860399604006040160402604036040460405604066040760408604096041060411604126041360414604156041660417604186041960420604216042260423604246042560426604276042860429604306043160432604336043460435604366043760438604396044060441604426044360444604456044660447604486044960450604516045260453604546045560456604576045860459604606046160462604636046460465604666046760468604696047060471604726047360474604756047660477604786047960480604816048260483604846048560486604876048860489604906049160492604936049460495604966049760498604996050060501605026050360504605056050660507605086050960510605116051260513605146051560516605176051860519605206052160522605236052460525605266052760528605296053060531605326053360534605356053660537605386053960540605416054260543605446054560546605476054860549605506055160552605536055460555605566055760558605596056060561605626056360564605656056660567605686056960570605716057260573605746057560576605776057860579605806058160582605836058460585605866058760588605896059060591605926059360594605956059660597605986059960600606016060260603606046060560606606076060860609606106061160612606136061460615606166061760618606196062060621606226062360624606256062660627606286062960630606316063260633606346063560636606376063860639606406064160642606436064460645606466064760648606496065060651606526065360654606556065660657606586065960660606616066260663606646066560666606676066860669606706067160672606736067460675606766067760678606796068060681606826068360684606856068660687606886068960690606916069260693606946069560696606976069860699607006070160702607036070460705607066070760708607096071060711607126071360714607156071660717607186071960720607216072260723607246072560726607276072860729607306073160732607336073460735607366073760738607396074060741607426074360744607456074660747607486074960750607516075260753607546075560756607576075860759607606076160762607636076460765607666076760768607696077060771607726077360774607756077660777607786077960780607816078260783607846078560786607876078860789607906079160792607936079460795607966079760798607996080060801608026080360804608056080660807608086080960810608116081260813608146081560816608176081860819608206082160822608236082460825608266082760828608296083060831608326083360834608356083660837608386083960840608416084260843608446084560846608476084860849608506085160852608536085460855608566085760858608596086060861608626086360864608656086660867608686086960870608716087260873608746087560876608776087860879608806088160882608836088460885608866088760888608896089060891608926089360894608956089660897608986089960900609016090260903609046090560906609076090860909609106091160912609136091460915609166091760918609196092060921609226092360924609256092660927609286092960930609316093260933609346093560936609376093860939609406094160942609436094460945609466094760948609496095060951609526095360954609556095660957609586095960960609616096260963609646096560966609676096860969609706097160972609736097460975609766097760978609796098060981609826098360984609856098660987609886098960990609916099260993609946099560996609976099860999610006100161002610036100461005610066100761008610096101061011610126101361014610156101661017610186101961020610216102261023610246102561026610276102861029610306103161032610336103461035610366103761038610396104061041610426104361044610456104661047610486104961050610516105261053610546105561056610576105861059610606106161062610636106461065610666106761068610696107061071610726107361074610756107661077610786107961080610816108261083610846108561086610876108861089610906109161092610936109461095610966109761098610996110061101611026110361104611056110661107611086110961110611116111261113611146111561116611176111861119611206112161122611236112461125611266112761128611296113061131611326113361134611356113661137611386113961140611416114261143611446114561146611476114861149611506115161152611536115461155611566115761158611596116061161611626116361164611656116661167611686116961170611716117261173611746117561176611776117861179611806118161182611836118461185611866118761188611896119061191611926119361194611956119661197611986119961200612016120261203612046120561206612076120861209612106121161212612136121461215612166121761218612196122061221612226122361224612256122661227612286122961230612316123261233612346123561236612376123861239612406124161242612436124461245612466124761248612496125061251612526125361254612556125661257612586125961260612616126261263612646126561266612676126861269612706127161272612736127461275612766127761278612796128061281612826128361284612856128661287612886128961290612916129261293612946129561296612976129861299613006130161302613036130461305613066130761308613096131061311613126131361314613156131661317613186131961320613216132261323613246132561326613276132861329613306133161332613336133461335613366133761338613396134061341613426134361344613456134661347613486134961350613516135261353613546135561356613576135861359613606136161362613636136461365613666136761368613696137061371613726137361374613756137661377613786137961380613816138261383613846138561386613876138861389613906139161392613936139461395613966139761398613996140061401614026140361404614056140661407614086140961410614116141261413614146141561416614176141861419614206142161422614236142461425614266142761428614296143061431614326143361434614356143661437614386143961440614416144261443614446144561446614476144861449614506145161452614536145461455614566145761458614596146061461614626146361464614656146661467614686146961470614716147261473614746147561476614776147861479614806148161482614836148461485614866148761488614896149061491614926149361494614956149661497614986149961500615016150261503615046150561506615076150861509615106151161512615136151461515615166151761518615196152061521615226152361524615256152661527615286152961530615316153261533615346153561536615376153861539615406154161542615436154461545615466154761548615496155061551615526155361554615556155661557615586155961560615616156261563615646156561566615676156861569615706157161572615736157461575615766157761578615796158061581615826158361584615856158661587615886158961590615916159261593615946159561596615976159861599616006160161602616036160461605616066160761608616096161061611616126161361614616156161661617616186161961620616216162261623616246162561626616276162861629616306163161632616336163461635616366163761638616396164061641616426164361644616456164661647616486164961650616516165261653616546165561656616576165861659616606166161662616636166461665616666166761668616696167061671616726167361674616756167661677616786167961680616816168261683616846168561686616876168861689616906169161692616936169461695616966169761698616996170061701617026170361704617056170661707617086170961710617116171261713617146171561716617176171861719617206172161722617236172461725617266172761728617296173061731617326173361734617356173661737617386173961740617416174261743617446174561746617476174861749617506175161752617536175461755617566175761758617596176061761617626176361764617656176661767617686176961770617716177261773617746177561776617776177861779617806178161782617836178461785617866178761788617896179061791617926179361794617956179661797617986179961800618016180261803618046180561806618076180861809618106181161812618136181461815618166181761818618196182061821618226182361824618256182661827618286182961830618316183261833618346183561836618376183861839618406184161842618436184461845618466184761848618496185061851618526185361854618556185661857618586185961860618616186261863618646186561866618676186861869618706187161872618736187461875618766187761878618796188061881618826188361884618856188661887618886188961890618916189261893618946189561896618976189861899619006190161902619036190461905619066190761908619096191061911619126191361914619156191661917619186191961920619216192261923619246192561926619276192861929619306193161932619336193461935619366193761938619396194061941619426194361944619456194661947619486194961950619516195261953619546195561956619576195861959619606196161962619636196461965619666196761968619696197061971619726197361974619756197661977619786197961980619816198261983619846198561986619876198861989619906199161992619936199461995619966199761998619996200062001620026200362004620056200662007620086200962010620116201262013620146201562016620176201862019620206202162022620236202462025620266202762028620296203062031620326203362034620356203662037620386203962040620416204262043620446204562046620476204862049620506205162052620536205462055620566205762058620596206062061620626206362064620656206662067620686206962070620716207262073620746207562076620776207862079620806208162082620836208462085620866208762088620896209062091620926209362094620956209662097620986209962100621016210262103621046210562106621076210862109621106211162112621136211462115621166211762118621196212062121621226212362124621256212662127621286212962130621316213262133621346213562136621376213862139621406214162142621436214462145621466214762148621496215062151621526215362154621556215662157621586215962160621616216262163621646216562166621676216862169621706217162172621736217462175621766217762178621796218062181621826218362184621856218662187621886218962190621916219262193621946219562196621976219862199622006220162202622036220462205622066220762208622096221062211622126221362214622156221662217622186221962220622216222262223622246222562226622276222862229622306223162232622336223462235622366223762238622396224062241622426224362244622456224662247622486224962250622516225262253622546225562256622576225862259622606226162262622636226462265622666226762268622696227062271622726227362274622756227662277622786227962280622816228262283622846228562286622876228862289622906229162292622936229462295622966229762298622996230062301623026230362304623056230662307623086230962310623116231262313623146231562316623176231862319623206232162322623236232462325623266232762328623296233062331623326233362334623356233662337623386233962340623416234262343623446234562346623476234862349623506235162352623536235462355623566235762358623596236062361623626236362364623656236662367623686236962370623716237262373623746237562376623776237862379623806238162382623836238462385623866238762388623896239062391623926239362394623956239662397623986239962400624016240262403624046240562406624076240862409624106241162412624136241462415624166241762418624196242062421624226242362424624256242662427624286242962430624316243262433624346243562436624376243862439624406244162442624436244462445624466244762448624496245062451624526245362454624556245662457624586245962460624616246262463624646246562466624676246862469624706247162472624736247462475624766247762478624796248062481624826248362484624856248662487624886248962490624916249262493624946249562496624976249862499625006250162502625036250462505625066250762508625096251062511625126251362514625156251662517625186251962520625216252262523625246252562526625276252862529625306253162532625336253462535625366253762538625396254062541625426254362544625456254662547625486254962550625516255262553625546255562556625576255862559625606256162562625636256462565625666256762568625696257062571625726257362574625756257662577625786257962580625816258262583625846258562586625876258862589625906259162592625936259462595625966259762598625996260062601626026260362604626056260662607626086260962610626116261262613626146261562616626176261862619626206262162622626236262462625626266262762628626296263062631626326263362634626356263662637626386263962640626416264262643626446264562646626476264862649626506265162652626536265462655626566265762658626596266062661626626266362664626656266662667626686266962670626716267262673626746267562676626776267862679626806268162682626836268462685626866268762688626896269062691626926269362694626956269662697626986269962700627016270262703627046270562706627076270862709627106271162712627136271462715627166271762718627196272062721627226272362724627256272662727627286272962730627316273262733627346273562736627376273862739627406274162742627436274462745627466274762748627496275062751627526275362754627556275662757627586275962760627616276262763627646276562766627676276862769627706277162772627736277462775627766277762778627796278062781627826278362784627856278662787627886278962790627916279262793627946279562796627976279862799628006280162802628036280462805628066280762808628096281062811628126281362814628156281662817628186281962820628216282262823628246282562826628276282862829628306283162832628336283462835628366283762838628396284062841628426284362844628456284662847628486284962850628516285262853628546285562856628576285862859628606286162862628636286462865628666286762868628696287062871628726287362874628756287662877628786287962880628816288262883628846288562886628876288862889628906289162892628936289462895628966289762898628996290062901629026290362904629056290662907629086290962910629116291262913629146291562916629176291862919629206292162922629236292462925629266292762928629296293062931629326293362934629356293662937629386293962940629416294262943629446294562946629476294862949629506295162952629536295462955629566295762958629596296062961629626296362964629656296662967629686296962970629716297262973629746297562976629776297862979629806298162982629836298462985629866298762988629896299062991629926299362994629956299662997629986299963000630016300263003630046300563006630076300863009630106301163012630136301463015630166301763018630196302063021630226302363024630256302663027630286302963030630316303263033630346303563036630376303863039630406304163042630436304463045630466304763048630496305063051630526305363054630556305663057630586305963060630616306263063630646306563066630676306863069630706307163072630736307463075630766307763078630796308063081630826308363084630856308663087630886308963090630916309263093630946309563096630976309863099631006310163102631036310463105631066310763108631096311063111631126311363114631156311663117631186311963120631216312263123631246312563126631276312863129631306313163132631336313463135631366313763138631396314063141631426314363144631456314663147631486314963150631516315263153631546315563156631576315863159631606316163162631636316463165631666316763168631696317063171631726317363174631756317663177631786317963180631816318263183631846318563186631876318863189631906319163192631936319463195631966319763198631996320063201632026320363204632056320663207632086320963210632116321263213632146321563216632176321863219632206322163222632236322463225632266322763228632296323063231632326323363234632356323663237632386323963240632416324263243632446324563246632476324863249632506325163252632536325463255632566325763258632596326063261632626326363264632656326663267632686326963270632716327263273632746327563276632776327863279632806328163282632836328463285632866328763288632896329063291632926329363294632956329663297632986329963300633016330263303633046330563306633076330863309633106331163312633136331463315633166331763318633196332063321633226332363324633256332663327633286332963330633316333263333633346333563336633376333863339633406334163342633436334463345633466334763348633496335063351633526335363354633556335663357633586335963360633616336263363633646336563366633676336863369633706337163372633736337463375633766337763378633796338063381633826338363384633856338663387633886338963390633916339263393633946339563396633976339863399634006340163402634036340463405634066340763408634096341063411634126341363414634156341663417634186341963420634216342263423634246342563426634276342863429634306343163432634336343463435634366343763438634396344063441634426344363444634456344663447634486344963450634516345263453634546345563456634576345863459634606346163462634636346463465634666346763468634696347063471634726347363474634756347663477634786347963480634816348263483634846348563486634876348863489634906349163492634936349463495634966349763498634996350063501635026350363504635056350663507635086350963510635116351263513635146351563516635176351863519635206352163522635236352463525635266352763528635296353063531635326353363534635356353663537635386353963540635416354263543635446354563546635476354863549635506355163552635536355463555635566355763558635596356063561635626356363564635656356663567635686356963570635716357263573635746357563576635776357863579635806358163582635836358463585635866358763588635896359063591635926359363594635956359663597635986359963600636016360263603636046360563606636076360863609636106361163612636136361463615636166361763618636196362063621636226362363624636256362663627636286362963630636316363263633636346363563636636376363863639636406364163642636436364463645636466364763648636496365063651636526365363654636556365663657636586365963660636616366263663636646366563666636676366863669636706367163672636736367463675636766367763678636796368063681636826368363684636856368663687636886368963690636916369263693636946369563696636976369863699637006370163702637036370463705637066370763708637096371063711637126371363714637156371663717637186371963720637216372263723637246372563726637276372863729637306373163732637336373463735637366373763738637396374063741637426374363744637456374663747637486374963750637516375263753637546375563756637576375863759637606376163762637636376463765637666376763768637696377063771637726377363774637756377663777637786377963780637816378263783637846378563786637876378863789637906379163792637936379463795637966379763798637996380063801638026380363804638056380663807638086380963810638116381263813638146381563816638176381863819638206382163822638236382463825638266382763828638296383063831638326383363834638356383663837638386383963840638416384263843638446384563846638476384863849638506385163852638536385463855638566385763858638596386063861638626386363864638656386663867638686386963870638716387263873638746387563876638776387863879638806388163882638836388463885638866388763888638896389063891638926389363894638956389663897638986389963900639016390263903639046390563906639076390863909639106391163912639136391463915639166391763918639196392063921639226392363924639256392663927639286392963930639316393263933639346393563936639376393863939639406394163942639436394463945639466394763948639496395063951639526395363954639556395663957639586395963960639616396263963639646396563966639676396863969639706397163972639736397463975639766397763978639796398063981639826398363984639856398663987639886398963990639916399263993639946399563996639976399863999640006400164002640036400464005640066400764008640096401064011640126401364014640156401664017640186401964020640216402264023640246402564026640276402864029640306403164032640336403464035640366403764038640396404064041640426404364044640456404664047640486404964050640516405264053640546405564056640576405864059640606406164062640636406464065640666406764068640696407064071640726407364074640756407664077640786407964080640816408264083640846408564086640876408864089640906409164092640936409464095640966409764098640996410064101641026410364104641056410664107641086410964110641116411264113641146411564116641176411864119641206412164122641236412464125641266412764128641296413064131641326413364134641356413664137641386413964140641416414264143641446414564146641476414864149641506415164152641536415464155641566415764158641596416064161641626416364164641656416664167641686416964170641716417264173641746417564176641776417864179641806418164182641836418464185641866418764188641896419064191641926419364194641956419664197641986419964200642016420264203642046420564206642076420864209642106421164212642136421464215642166421764218642196422064221642226422364224642256422664227642286422964230642316423264233642346423564236642376423864239642406424164242642436424464245642466424764248642496425064251642526425364254642556425664257642586425964260642616426264263642646426564266642676426864269642706427164272642736427464275642766427764278642796428064281642826428364284642856428664287642886428964290642916429264293642946429564296642976429864299643006430164302643036430464305643066430764308643096431064311643126431364314643156431664317643186431964320643216432264323643246432564326643276432864329643306433164332643336433464335643366433764338643396434064341643426434364344643456434664347643486434964350643516435264353643546435564356643576435864359643606436164362643636436464365643666436764368643696437064371643726437364374643756437664377643786437964380643816438264383643846438564386643876438864389643906439164392643936439464395643966439764398643996440064401644026440364404644056440664407644086440964410644116441264413644146441564416644176441864419644206442164422644236442464425644266442764428644296443064431644326443364434644356443664437644386443964440644416444264443644446444564446644476444864449644506445164452644536445464455644566445764458644596446064461644626446364464644656446664467644686446964470644716447264473644746447564476644776447864479644806448164482644836448464485644866448764488644896449064491644926449364494644956449664497644986449964500645016450264503645046450564506645076450864509645106451164512645136451464515645166451764518645196452064521645226452364524645256452664527645286452964530645316453264533645346453564536645376453864539645406454164542645436454464545645466454764548645496455064551645526455364554645556455664557645586455964560645616456264563645646456564566645676456864569645706457164572645736457464575645766457764578645796458064581645826458364584645856458664587645886458964590645916459264593645946459564596645976459864599646006460164602646036460464605646066460764608646096461064611646126461364614646156461664617646186461964620646216462264623646246462564626646276462864629646306463164632646336463464635646366463764638646396464064641646426464364644646456464664647646486464964650646516465264653646546465564656646576465864659646606466164662646636466464665646666466764668646696467064671646726467364674646756467664677646786467964680646816468264683646846468564686646876468864689646906469164692646936469464695646966469764698646996470064701647026470364704647056470664707647086470964710647116471264713647146471564716647176471864719647206472164722647236472464725647266472764728647296473064731647326473364734647356473664737647386473964740647416474264743647446474564746647476474864749647506475164752647536475464755647566475764758647596476064761647626476364764647656476664767647686476964770647716477264773647746477564776647776477864779647806478164782647836478464785647866478764788647896479064791647926479364794647956479664797647986479964800648016480264803648046480564806648076480864809648106481164812648136481464815648166481764818648196482064821648226482364824648256482664827648286482964830648316483264833648346483564836648376483864839648406484164842648436484464845648466484764848648496485064851648526485364854648556485664857648586485964860648616486264863648646486564866648676486864869648706487164872648736487464875648766487764878648796488064881648826488364884648856488664887648886488964890648916489264893648946489564896648976489864899649006490164902649036490464905649066490764908649096491064911649126491364914649156491664917649186491964920649216492264923649246492564926649276492864929649306493164932649336493464935649366493764938649396494064941649426494364944649456494664947649486494964950649516495264953649546495564956649576495864959649606496164962649636496464965649666496764968649696497064971649726497364974649756497664977649786497964980649816498264983649846498564986649876498864989649906499164992649936499464995649966499764998649996500065001650026500365004650056500665007650086500965010650116501265013650146501565016650176501865019650206502165022650236502465025650266502765028650296503065031650326503365034650356503665037650386503965040650416504265043650446504565046650476504865049650506505165052650536505465055650566505765058650596506065061650626506365064650656506665067650686506965070650716507265073650746507565076650776507865079650806508165082650836508465085650866508765088650896509065091650926509365094650956509665097650986509965100651016510265103651046510565106651076510865109651106511165112651136511465115651166511765118651196512065121651226512365124651256512665127651286512965130651316513265133651346513565136651376513865139651406514165142651436514465145651466514765148651496515065151651526515365154651556515665157651586515965160651616516265163651646516565166651676516865169651706517165172651736517465175651766517765178651796518065181651826518365184651856518665187651886518965190651916519265193651946519565196651976519865199652006520165202652036520465205652066520765208652096521065211652126521365214652156521665217652186521965220652216522265223652246522565226652276522865229652306523165232652336523465235652366523765238652396524065241652426524365244652456524665247652486524965250652516525265253652546525565256652576525865259652606526165262652636526465265652666526765268652696527065271652726527365274652756527665277652786527965280652816528265283652846528565286652876528865289652906529165292652936529465295652966529765298652996530065301653026530365304653056530665307653086530965310653116531265313653146531565316653176531865319653206532165322653236532465325653266532765328653296533065331653326533365334653356533665337653386533965340653416534265343653446534565346653476534865349653506535165352653536535465355653566535765358653596536065361653626536365364653656536665367653686536965370653716537265373653746537565376653776537865379653806538165382653836538465385653866538765388653896539065391653926539365394653956539665397653986539965400654016540265403654046540565406654076540865409654106541165412654136541465415654166541765418654196542065421654226542365424654256542665427654286542965430654316543265433654346543565436654376543865439654406544165442654436544465445654466544765448654496545065451654526545365454654556545665457654586545965460654616546265463654646546565466654676546865469654706547165472654736547465475654766547765478654796548065481654826548365484654856548665487654886548965490654916549265493654946549565496654976549865499655006550165502655036550465505655066550765508655096551065511655126551365514655156551665517655186551965520655216552265523655246552565526655276552865529655306553165532655336553465535655366553765538655396554065541655426554365544655456554665547655486554965550655516555265553655546555565556655576555865559655606556165562655636556465565655666556765568655696557065571655726557365574655756557665577655786557965580655816558265583655846558565586655876558865589655906559165592655936559465595655966559765598655996560065601656026560365604656056560665607656086560965610656116561265613656146561565616656176561865619656206562165622656236562465625656266562765628656296563065631656326563365634656356563665637656386563965640656416564265643656446564565646656476564865649656506565165652656536565465655656566565765658656596566065661656626566365664656656566665667656686566965670656716567265673656746567565676656776567865679656806568165682656836568465685656866568765688656896569065691656926569365694656956569665697656986569965700657016570265703657046570565706657076570865709657106571165712657136571465715657166571765718657196572065721657226572365724657256572665727657286572965730657316573265733657346573565736657376573865739657406574165742657436574465745657466574765748657496575065751657526575365754657556575665757657586575965760657616576265763657646576565766657676576865769657706577165772657736577465775657766577765778657796578065781657826578365784657856578665787657886578965790657916579265793657946579565796657976579865799658006580165802658036580465805658066580765808658096581065811658126581365814658156581665817658186581965820658216582265823658246582565826658276582865829658306583165832658336583465835658366583765838658396584065841658426584365844658456584665847658486584965850658516585265853658546585565856658576585865859658606586165862658636586465865658666586765868658696587065871658726587365874658756587665877658786587965880658816588265883658846588565886658876588865889658906589165892658936589465895658966589765898658996590065901659026590365904659056590665907659086590965910659116591265913659146591565916659176591865919659206592165922659236592465925659266592765928659296593065931659326593365934659356593665937659386593965940659416594265943659446594565946659476594865949659506595165952659536595465955659566595765958659596596065961659626596365964659656596665967659686596965970659716597265973659746597565976659776597865979659806598165982659836598465985659866598765988659896599065991659926599365994659956599665997659986599966000660016600266003660046600566006660076600866009660106601166012660136601466015660166601766018660196602066021660226602366024660256602666027660286602966030660316603266033660346603566036660376603866039660406604166042660436604466045660466604766048660496605066051660526605366054660556605666057660586605966060660616606266063660646606566066660676606866069660706607166072660736607466075660766607766078660796608066081660826608366084660856608666087660886608966090660916609266093660946609566096660976609866099661006610166102661036610466105661066610766108661096611066111661126611366114661156611666117661186611966120661216612266123661246612566126661276612866129661306613166132661336613466135661366613766138661396614066141661426614366144661456614666147661486614966150661516615266153661546615566156661576615866159661606616166162661636616466165661666616766168661696617066171661726617366174661756617666177661786617966180661816618266183661846618566186661876618866189661906619166192661936619466195661966619766198661996620066201662026620366204662056620666207662086620966210662116621266213662146621566216662176621866219662206622166222662236622466225662266622766228662296623066231662326623366234662356623666237662386623966240662416624266243662446624566246662476624866249662506625166252662536625466255662566625766258662596626066261662626626366264662656626666267662686626966270662716627266273662746627566276662776627866279662806628166282662836628466285662866628766288662896629066291662926629366294662956629666297662986629966300663016630266303663046630566306663076630866309663106631166312663136631466315663166631766318663196632066321663226632366324663256632666327663286632966330663316633266333663346633566336663376633866339663406634166342663436634466345663466634766348663496635066351663526635366354663556635666357663586635966360663616636266363663646636566366663676636866369663706637166372663736637466375663766637766378663796638066381663826638366384663856638666387663886638966390663916639266393663946639566396663976639866399664006640166402664036640466405664066640766408664096641066411664126641366414664156641666417664186641966420664216642266423664246642566426664276642866429664306643166432664336643466435664366643766438664396644066441664426644366444664456644666447664486644966450664516645266453664546645566456664576645866459664606646166462664636646466465664666646766468664696647066471664726647366474664756647666477664786647966480664816648266483664846648566486664876648866489664906649166492664936649466495664966649766498664996650066501665026650366504665056650666507665086650966510665116651266513665146651566516665176651866519665206652166522665236652466525665266652766528665296653066531665326653366534665356653666537665386653966540665416654266543665446654566546665476654866549665506655166552665536655466555665566655766558665596656066561665626656366564665656656666567665686656966570665716657266573665746657566576665776657866579665806658166582665836658466585665866658766588665896659066591665926659366594665956659666597665986659966600666016660266603666046660566606666076660866609666106661166612666136661466615666166661766618666196662066621666226662366624666256662666627666286662966630666316663266633666346663566636666376663866639666406664166642666436664466645666466664766648666496665066651666526665366654666556665666657666586665966660666616666266663666646666566666666676666866669666706667166672666736667466675666766667766678666796668066681666826668366684666856668666687666886668966690666916669266693666946669566696666976669866699667006670166702667036670466705667066670766708667096671066711667126671366714667156671666717667186671966720667216672266723667246672566726667276672866729667306673166732667336673466735667366673766738667396674066741667426674366744667456674666747667486674966750667516675266753667546675566756667576675866759667606676166762667636676466765667666676766768667696677066771667726677366774667756677666777667786677966780667816678266783667846678566786667876678866789667906679166792667936679466795667966679766798667996680066801668026680366804668056680666807668086680966810668116681266813668146681566816668176681866819668206682166822668236682466825668266682766828668296683066831668326683366834668356683666837668386683966840668416684266843668446684566846668476684866849668506685166852668536685466855668566685766858668596686066861668626686366864668656686666867668686686966870668716687266873668746687566876668776687866879668806688166882668836688466885668866688766888668896689066891668926689366894668956689666897668986689966900669016690266903669046690566906669076690866909669106691166912669136691466915669166691766918669196692066921669226692366924669256692666927669286692966930669316693266933669346693566936669376693866939669406694166942669436694466945669466694766948669496695066951669526695366954669556695666957669586695966960669616696266963669646696566966669676696866969669706697166972669736697466975669766697766978669796698066981669826698366984669856698666987669886698966990669916699266993669946699566996669976699866999670006700167002670036700467005670066700767008670096701067011670126701367014670156701667017670186701967020670216702267023670246702567026670276702867029670306703167032670336703467035670366703767038670396704067041670426704367044670456704667047670486704967050670516705267053670546705567056670576705867059670606706167062670636706467065670666706767068670696707067071670726707367074670756707667077670786707967080670816708267083670846708567086670876708867089670906709167092670936709467095670966709767098670996710067101671026710367104671056710667107671086710967110671116711267113671146711567116671176711867119671206712167122671236712467125671266712767128671296713067131671326713367134671356713667137671386713967140671416714267143671446714567146671476714867149671506715167152671536715467155671566715767158671596716067161671626716367164671656716667167671686716967170671716717267173671746717567176671776717867179671806718167182671836718467185671866718767188671896719067191671926719367194671956719667197671986719967200672016720267203672046720567206672076720867209672106721167212672136721467215672166721767218672196722067221672226722367224672256722667227672286722967230672316723267233672346723567236672376723867239672406724167242672436724467245672466724767248672496725067251672526725367254672556725667257672586725967260672616726267263672646726567266672676726867269672706727167272672736727467275672766727767278672796728067281672826728367284672856728667287672886728967290672916729267293672946729567296672976729867299673006730167302673036730467305673066730767308673096731067311673126731367314673156731667317673186731967320673216732267323673246732567326673276732867329673306733167332673336733467335673366733767338673396734067341673426734367344673456734667347673486734967350673516735267353673546735567356673576735867359673606736167362673636736467365673666736767368673696737067371673726737367374673756737667377673786737967380673816738267383673846738567386673876738867389673906739167392673936739467395673966739767398673996740067401674026740367404674056740667407674086740967410674116741267413674146741567416674176741867419674206742167422674236742467425674266742767428674296743067431674326743367434674356743667437674386743967440674416744267443674446744567446674476744867449674506745167452674536745467455674566745767458674596746067461674626746367464674656746667467674686746967470674716747267473674746747567476674776747867479674806748167482674836748467485674866748767488674896749067491674926749367494674956749667497674986749967500675016750267503675046750567506675076750867509675106751167512675136751467515675166751767518675196752067521675226752367524675256752667527675286752967530675316753267533675346753567536675376753867539675406754167542675436754467545675466754767548675496755067551675526755367554675556755667557675586755967560675616756267563675646756567566675676756867569675706757167572675736757467575675766757767578675796758067581675826758367584675856758667587675886758967590675916759267593675946759567596675976759867599676006760167602676036760467605676066760767608676096761067611676126761367614676156761667617676186761967620676216762267623676246762567626676276762867629676306763167632676336763467635676366763767638676396764067641676426764367644676456764667647676486764967650676516765267653676546765567656676576765867659676606766167662676636766467665676666766767668676696767067671676726767367674676756767667677676786767967680676816768267683676846768567686676876768867689676906769167692676936769467695676966769767698676996770067701677026770367704677056770667707677086770967710677116771267713677146771567716677176771867719677206772167722677236772467725677266772767728677296773067731677326773367734677356773667737677386773967740677416774267743677446774567746677476774867749677506775167752677536775467755677566775767758677596776067761677626776367764677656776667767677686776967770677716777267773677746777567776677776777867779677806778167782677836778467785677866778767788677896779067791677926779367794677956779667797677986779967800678016780267803678046780567806678076780867809678106781167812678136781467815678166781767818678196782067821678226782367824678256782667827678286782967830678316783267833678346783567836678376783867839678406784167842678436784467845678466784767848678496785067851678526785367854678556785667857678586785967860678616786267863678646786567866678676786867869678706787167872678736787467875678766787767878678796788067881678826788367884678856788667887678886788967890678916789267893678946789567896678976789867899679006790167902679036790467905679066790767908679096791067911679126791367914679156791667917679186791967920679216792267923679246792567926679276792867929679306793167932679336793467935679366793767938679396794067941679426794367944679456794667947679486794967950679516795267953679546795567956679576795867959679606796167962679636796467965679666796767968679696797067971679726797367974679756797667977679786797967980679816798267983679846798567986679876798867989679906799167992679936799467995679966799767998679996800068001680026800368004680056800668007680086800968010680116801268013680146801568016680176801868019680206802168022680236802468025680266802768028680296803068031680326803368034680356803668037680386803968040680416804268043680446804568046680476804868049680506805168052680536805468055680566805768058680596806068061680626806368064680656806668067680686806968070680716807268073680746807568076680776807868079680806808168082680836808468085680866808768088680896809068091680926809368094680956809668097680986809968100681016810268103681046810568106681076810868109681106811168112681136811468115681166811768118681196812068121681226812368124681256812668127681286812968130681316813268133681346813568136681376813868139681406814168142681436814468145681466814768148681496815068151681526815368154681556815668157681586815968160681616816268163681646816568166681676816868169681706817168172681736817468175681766817768178681796818068181681826818368184681856818668187681886818968190681916819268193681946819568196681976819868199682006820168202682036820468205682066820768208682096821068211682126821368214682156821668217682186821968220682216822268223682246822568226682276822868229682306823168232682336823468235682366823768238682396824068241682426824368244682456824668247682486824968250682516825268253682546825568256682576825868259682606826168262682636826468265682666826768268682696827068271682726827368274682756827668277682786827968280682816828268283682846828568286682876828868289682906829168292682936829468295682966829768298682996830068301683026830368304683056830668307683086830968310683116831268313683146831568316683176831868319683206832168322683236832468325683266832768328683296833068331683326833368334683356833668337683386833968340683416834268343683446834568346683476834868349683506835168352683536835468355683566835768358683596836068361683626836368364683656836668367683686836968370683716837268373683746837568376683776837868379683806838168382683836838468385683866838768388683896839068391683926839368394683956839668397683986839968400684016840268403684046840568406684076840868409684106841168412684136841468415684166841768418684196842068421684226842368424684256842668427684286842968430684316843268433684346843568436684376843868439684406844168442684436844468445684466844768448684496845068451684526845368454684556845668457684586845968460684616846268463684646846568466684676846868469684706847168472684736847468475684766847768478684796848068481684826848368484684856848668487684886848968490684916849268493684946849568496684976849868499685006850168502685036850468505685066850768508685096851068511685126851368514685156851668517685186851968520685216852268523685246852568526685276852868529685306853168532685336853468535685366853768538685396854068541685426854368544685456854668547685486854968550685516855268553685546855568556685576855868559685606856168562685636856468565685666856768568685696857068571685726857368574685756857668577685786857968580685816858268583685846858568586685876858868589685906859168592685936859468595685966859768598685996860068601686026860368604686056860668607686086860968610686116861268613686146861568616686176861868619686206862168622686236862468625686266862768628686296863068631686326863368634686356863668637686386863968640686416864268643686446864568646686476864868649686506865168652686536865468655686566865768658686596866068661686626866368664686656866668667686686866968670686716867268673686746867568676686776867868679686806868168682686836868468685686866868768688686896869068691686926869368694686956869668697686986869968700687016870268703687046870568706687076870868709687106871168712687136871468715687166871768718687196872068721687226872368724687256872668727687286872968730687316873268733687346873568736687376873868739687406874168742687436874468745687466874768748687496875068751687526875368754687556875668757687586875968760687616876268763687646876568766687676876868769687706877168772687736877468775687766877768778687796878068781687826878368784687856878668787687886878968790687916879268793687946879568796687976879868799688006880168802688036880468805688066880768808688096881068811688126881368814688156881668817688186881968820688216882268823688246882568826688276882868829688306883168832688336883468835688366883768838688396884068841688426884368844688456884668847688486884968850688516885268853688546885568856688576885868859688606886168862688636886468865688666886768868688696887068871688726887368874688756887668877688786887968880688816888268883688846888568886688876888868889688906889168892688936889468895688966889768898688996890068901689026890368904689056890668907689086890968910689116891268913689146891568916689176891868919689206892168922689236892468925689266892768928689296893068931689326893368934689356893668937689386893968940689416894268943689446894568946689476894868949689506895168952689536895468955689566895768958689596896068961689626896368964689656896668967689686896968970689716897268973689746897568976689776897868979689806898168982689836898468985689866898768988689896899068991689926899368994689956899668997689986899969000690016900269003690046900569006690076900869009690106901169012690136901469015690166901769018690196902069021690226902369024690256902669027690286902969030690316903269033690346903569036690376903869039690406904169042690436904469045690466904769048690496905069051690526905369054690556905669057690586905969060690616906269063690646906569066690676906869069690706907169072690736907469075690766907769078690796908069081690826908369084690856908669087690886908969090690916909269093690946909569096690976909869099691006910169102691036910469105691066910769108691096911069111691126911369114691156911669117691186911969120691216912269123691246912569126691276912869129691306913169132691336913469135691366913769138691396914069141691426914369144691456914669147691486914969150691516915269153691546915569156691576915869159691606916169162691636916469165691666916769168691696917069171691726917369174691756917669177691786917969180691816918269183691846918569186691876918869189691906919169192691936919469195691966919769198691996920069201692026920369204692056920669207692086920969210692116921269213692146921569216692176921869219692206922169222692236922469225692266922769228692296923069231692326923369234692356923669237692386923969240692416924269243692446924569246692476924869249692506925169252692536925469255692566925769258692596926069261692626926369264692656926669267692686926969270692716927269273692746927569276692776927869279692806928169282692836928469285692866928769288692896929069291692926929369294692956929669297692986929969300693016930269303693046930569306693076930869309693106931169312693136931469315693166931769318693196932069321693226932369324693256932669327693286932969330693316933269333693346933569336693376933869339693406934169342693436934469345693466934769348693496935069351693526935369354693556935669357693586935969360693616936269363693646936569366693676936869369693706937169372693736937469375693766937769378693796938069381693826938369384693856938669387693886938969390693916939269393693946939569396693976939869399694006940169402694036940469405694066940769408694096941069411694126941369414694156941669417694186941969420694216942269423694246942569426694276942869429694306943169432694336943469435694366943769438694396944069441694426944369444694456944669447694486944969450694516945269453694546945569456694576945869459694606946169462694636946469465694666946769468694696947069471694726947369474694756947669477694786947969480694816948269483694846948569486694876948869489694906949169492694936949469495694966949769498694996950069501695026950369504695056950669507695086950969510695116951269513695146951569516695176951869519695206952169522695236952469525695266952769528695296953069531695326953369534695356953669537695386953969540695416954269543695446954569546695476954869549695506955169552695536955469555695566955769558695596956069561695626956369564695656956669567695686956969570695716957269573695746957569576695776957869579695806958169582695836958469585695866958769588695896959069591695926959369594695956959669597695986959969600696016960269603696046960569606696076960869609696106961169612696136961469615696166961769618696196962069621696226962369624696256962669627696286962969630696316963269633696346963569636696376963869639696406964169642696436964469645696466964769648696496965069651696526965369654696556965669657696586965969660696616966269663696646966569666696676966869669696706967169672696736967469675696766967769678696796968069681696826968369684696856968669687696886968969690696916969269693696946969569696696976969869699697006970169702697036970469705697066970769708697096971069711697126971369714697156971669717697186971969720697216972269723697246972569726697276972869729697306973169732697336973469735697366973769738697396974069741697426974369744697456974669747697486974969750697516975269753697546975569756697576975869759697606976169762697636976469765697666976769768697696977069771697726977369774697756977669777697786977969780697816978269783697846978569786697876978869789697906979169792697936979469795697966979769798697996980069801698026980369804698056980669807698086980969810698116981269813698146981569816698176981869819698206982169822698236982469825698266982769828698296983069831698326983369834698356983669837698386983969840698416984269843698446984569846698476984869849698506985169852698536985469855698566985769858698596986069861698626986369864698656986669867698686986969870698716987269873698746987569876698776987869879698806988169882698836988469885698866988769888698896989069891698926989369894698956989669897698986989969900699016990269903699046990569906699076990869909699106991169912699136991469915699166991769918699196992069921699226992369924699256992669927699286992969930699316993269933699346993569936699376993869939699406994169942699436994469945699466994769948699496995069951699526995369954699556995669957699586995969960699616996269963699646996569966699676996869969699706997169972699736997469975699766997769978699796998069981699826998369984699856998669987699886998969990699916999269993699946999569996699976999869999700007000170002700037000470005700067000770008700097001070011700127001370014700157001670017700187001970020700217002270023700247002570026700277002870029700307003170032700337003470035700367003770038700397004070041700427004370044700457004670047700487004970050700517005270053700547005570056700577005870059700607006170062700637006470065700667006770068700697007070071700727007370074700757007670077700787007970080700817008270083700847008570086700877008870089700907009170092700937009470095700967009770098700997010070101701027010370104701057010670107701087010970110701117011270113701147011570116701177011870119701207012170122701237012470125701267012770128701297013070131701327013370134701357013670137701387013970140701417014270143701447014570146701477014870149701507015170152701537015470155701567015770158701597016070161701627016370164701657016670167701687016970170701717017270173701747017570176701777017870179701807018170182701837018470185701867018770188701897019070191701927019370194701957019670197701987019970200702017020270203702047020570206702077020870209702107021170212702137021470215702167021770218702197022070221702227022370224702257022670227702287022970230702317023270233702347023570236702377023870239702407024170242702437024470245702467024770248702497025070251702527025370254702557025670257702587025970260702617026270263702647026570266702677026870269702707027170272702737027470275702767027770278702797028070281702827028370284702857028670287702887028970290702917029270293702947029570296702977029870299703007030170302703037030470305703067030770308703097031070311703127031370314703157031670317703187031970320703217032270323703247032570326703277032870329703307033170332703337033470335703367033770338703397034070341703427034370344703457034670347703487034970350703517035270353703547035570356703577035870359703607036170362703637036470365703667036770368703697037070371703727037370374703757037670377703787037970380703817038270383703847038570386703877038870389703907039170392703937039470395703967039770398703997040070401704027040370404704057040670407704087040970410704117041270413704147041570416704177041870419704207042170422704237042470425704267042770428704297043070431704327043370434704357043670437704387043970440704417044270443704447044570446704477044870449704507045170452704537045470455704567045770458704597046070461704627046370464704657046670467704687046970470704717047270473704747047570476704777047870479704807048170482704837048470485704867048770488704897049070491704927049370494704957049670497704987049970500705017050270503705047050570506705077050870509705107051170512705137051470515705167051770518705197052070521705227052370524705257052670527705287052970530705317053270533705347053570536705377053870539705407054170542705437054470545705467054770548705497055070551705527055370554705557055670557705587055970560705617056270563705647056570566705677056870569705707057170572705737057470575705767057770578705797058070581705827058370584705857058670587705887058970590705917059270593705947059570596705977059870599706007060170602706037060470605706067060770608706097061070611706127061370614706157061670617706187061970620706217062270623706247062570626706277062870629706307063170632706337063470635706367063770638706397064070641706427064370644706457064670647706487064970650706517065270653706547065570656706577065870659706607066170662706637066470665706667066770668706697067070671706727067370674706757067670677706787067970680706817068270683706847068570686706877068870689706907069170692706937069470695706967069770698706997070070701707027070370704707057070670707707087070970710707117071270713707147071570716707177071870719707207072170722707237072470725707267072770728707297073070731707327073370734707357073670737707387073970740707417074270743707447074570746707477074870749707507075170752707537075470755707567075770758707597076070761707627076370764707657076670767707687076970770707717077270773707747077570776707777077870779707807078170782707837078470785707867078770788707897079070791707927079370794707957079670797707987079970800708017080270803708047080570806708077080870809708107081170812708137081470815708167081770818708197082070821708227082370824708257082670827708287082970830708317083270833708347083570836708377083870839708407084170842708437084470845708467084770848708497085070851708527085370854708557085670857708587085970860708617086270863708647086570866708677086870869708707087170872708737087470875708767087770878708797088070881708827088370884708857088670887708887088970890708917089270893708947089570896708977089870899709007090170902709037090470905709067090770908709097091070911709127091370914709157091670917709187091970920709217092270923709247092570926709277092870929709307093170932709337093470935709367093770938709397094070941709427094370944709457094670947709487094970950709517095270953709547095570956709577095870959709607096170962709637096470965709667096770968709697097070971709727097370974709757097670977709787097970980709817098270983709847098570986709877098870989709907099170992709937099470995709967099770998709997100071001710027100371004710057100671007710087100971010710117101271013710147101571016710177101871019710207102171022710237102471025710267102771028710297103071031710327103371034710357103671037710387103971040710417104271043710447104571046710477104871049710507105171052710537105471055710567105771058710597106071061710627106371064710657106671067710687106971070710717107271073710747107571076710777107871079710807108171082710837108471085710867108771088710897109071091710927109371094710957109671097710987109971100711017110271103711047110571106711077110871109711107111171112711137111471115711167111771118711197112071121711227112371124711257112671127711287112971130711317113271133711347113571136711377113871139711407114171142711437114471145711467114771148711497115071151711527115371154711557115671157711587115971160711617116271163711647116571166711677116871169711707117171172711737117471175711767117771178711797118071181711827118371184711857118671187711887118971190711917119271193711947119571196711977119871199712007120171202712037120471205712067120771208712097121071211712127121371214712157121671217712187121971220712217122271223712247122571226712277122871229712307123171232712337123471235712367123771238712397124071241712427124371244712457124671247712487124971250712517125271253712547125571256712577125871259712607126171262712637126471265712667126771268712697127071271712727127371274712757127671277712787127971280712817128271283712847128571286712877128871289712907129171292712937129471295712967129771298712997130071301713027130371304713057130671307713087130971310713117131271313713147131571316713177131871319713207132171322713237132471325713267132771328713297133071331713327133371334713357133671337713387133971340713417134271343713447134571346713477134871349713507135171352713537135471355713567135771358713597136071361713627136371364713657136671367713687136971370713717137271373713747137571376713777137871379713807138171382713837138471385713867138771388713897139071391713927139371394713957139671397713987139971400714017140271403714047140571406714077140871409714107141171412714137141471415714167141771418714197142071421714227142371424714257142671427714287142971430714317143271433714347143571436714377143871439714407144171442714437144471445714467144771448714497145071451714527145371454714557145671457714587145971460714617146271463714647146571466714677146871469714707147171472714737147471475714767147771478714797148071481714827148371484714857148671487714887148971490714917149271493714947149571496714977149871499715007150171502715037150471505715067150771508715097151071511715127151371514715157151671517715187151971520715217152271523715247152571526715277152871529715307153171532715337153471535715367153771538715397154071541715427154371544715457154671547715487154971550715517155271553715547155571556715577155871559715607156171562715637156471565715667156771568715697157071571715727157371574715757157671577715787157971580715817158271583715847158571586715877158871589715907159171592715937159471595715967159771598715997160071601716027160371604716057160671607716087160971610716117161271613716147161571616716177161871619716207162171622716237162471625716267162771628716297163071631716327163371634716357163671637716387163971640716417164271643716447164571646716477164871649716507165171652716537165471655716567165771658716597166071661716627166371664716657166671667716687166971670716717167271673716747167571676716777167871679716807168171682716837168471685716867168771688716897169071691716927169371694716957169671697716987169971700717017170271703717047170571706717077170871709717107171171712717137171471715717167171771718717197172071721717227172371724717257172671727717287172971730717317173271733717347173571736717377173871739717407174171742717437174471745717467174771748717497175071751717527175371754717557175671757717587175971760717617176271763717647176571766717677176871769717707177171772717737177471775717767177771778717797178071781717827178371784717857178671787717887178971790717917179271793717947179571796717977179871799718007180171802718037180471805718067180771808718097181071811718127181371814718157181671817718187181971820718217182271823718247182571826718277182871829718307183171832718337183471835718367183771838718397184071841718427184371844718457184671847718487184971850718517185271853718547185571856718577185871859718607186171862718637186471865718667186771868718697187071871718727187371874718757187671877718787187971880718817188271883718847188571886718877188871889718907189171892718937189471895718967189771898718997190071901719027190371904719057190671907719087190971910719117191271913719147191571916719177191871919719207192171922719237192471925719267192771928719297193071931719327193371934719357193671937719387193971940719417194271943719447194571946719477194871949719507195171952719537195471955719567195771958719597196071961719627196371964719657196671967719687196971970719717197271973719747197571976719777197871979719807198171982719837198471985719867198771988719897199071991719927199371994719957199671997719987199972000720017200272003720047200572006720077200872009720107201172012720137201472015720167201772018720197202072021720227202372024720257202672027720287202972030720317203272033720347203572036720377203872039720407204172042720437204472045720467204772048720497205072051720527205372054720557205672057720587205972060720617206272063720647206572066720677206872069720707207172072720737207472075720767207772078720797208072081720827208372084720857208672087720887208972090720917209272093720947209572096720977209872099721007210172102721037210472105721067210772108721097211072111721127211372114721157211672117721187211972120721217212272123721247212572126721277212872129721307213172132721337213472135721367213772138721397214072141721427214372144721457214672147721487214972150721517215272153721547215572156721577215872159721607216172162721637216472165721667216772168721697217072171721727217372174721757217672177721787217972180721817218272183721847218572186721877218872189721907219172192721937219472195721967219772198721997220072201722027220372204722057220672207722087220972210722117221272213722147221572216722177221872219722207222172222722237222472225722267222772228722297223072231722327223372234722357223672237722387223972240722417224272243722447224572246722477224872249722507225172252722537225472255722567225772258722597226072261722627226372264722657226672267722687226972270722717227272273722747227572276722777227872279722807228172282722837228472285722867228772288722897229072291722927229372294722957229672297722987229972300723017230272303723047230572306723077230872309723107231172312723137231472315723167231772318723197232072321723227232372324723257232672327723287232972330723317233272333723347233572336723377233872339723407234172342723437234472345723467234772348723497235072351723527235372354723557235672357723587235972360723617236272363723647236572366723677236872369723707237172372723737237472375723767237772378723797238072381723827238372384723857238672387723887238972390723917239272393723947239572396723977239872399724007240172402724037240472405724067240772408724097241072411724127241372414724157241672417724187241972420724217242272423724247242572426724277242872429724307243172432724337243472435724367243772438724397244072441724427244372444724457244672447724487244972450724517245272453724547245572456724577245872459724607246172462724637246472465724667246772468724697247072471724727247372474724757247672477724787247972480724817248272483724847248572486724877248872489724907249172492724937249472495724967249772498724997250072501725027250372504725057250672507725087250972510725117251272513725147251572516725177251872519725207252172522725237252472525725267252772528725297253072531725327253372534725357253672537725387253972540725417254272543725447254572546725477254872549725507255172552725537255472555725567255772558725597256072561725627256372564725657256672567725687256972570725717257272573725747257572576725777257872579725807258172582725837258472585725867258772588725897259072591725927259372594725957259672597725987259972600726017260272603726047260572606726077260872609726107261172612726137261472615726167261772618726197262072621726227262372624726257262672627726287262972630726317263272633726347263572636726377263872639726407264172642726437264472645726467264772648726497265072651726527265372654726557265672657726587265972660726617266272663726647266572666726677266872669726707267172672726737267472675726767267772678726797268072681726827268372684726857268672687726887268972690726917269272693726947269572696726977269872699727007270172702727037270472705727067270772708727097271072711727127271372714727157271672717727187271972720727217272272723727247272572726727277272872729727307273172732727337273472735727367273772738727397274072741727427274372744727457274672747727487274972750727517275272753727547275572756727577275872759727607276172762727637276472765727667276772768727697277072771727727277372774727757277672777727787277972780727817278272783727847278572786727877278872789727907279172792727937279472795727967279772798727997280072801728027280372804728057280672807728087280972810728117281272813728147281572816728177281872819728207282172822728237282472825728267282772828728297283072831728327283372834728357283672837728387283972840728417284272843728447284572846728477284872849728507285172852728537285472855728567285772858728597286072861728627286372864728657286672867728687286972870728717287272873728747287572876728777287872879728807288172882728837288472885728867288772888728897289072891728927289372894728957289672897728987289972900729017290272903729047290572906729077290872909729107291172912729137291472915729167291772918729197292072921729227292372924729257292672927729287292972930729317293272933729347293572936729377293872939729407294172942729437294472945729467294772948729497295072951729527295372954729557295672957729587295972960729617296272963729647296572966729677296872969729707297172972729737297472975729767297772978729797298072981729827298372984729857298672987729887298972990729917299272993729947299572996729977299872999730007300173002730037300473005730067300773008730097301073011730127301373014730157301673017730187301973020730217302273023730247302573026730277302873029730307303173032730337303473035730367303773038730397304073041730427304373044730457304673047730487304973050730517305273053730547305573056730577305873059730607306173062730637306473065730667306773068730697307073071730727307373074730757307673077730787307973080730817308273083730847308573086730877308873089730907309173092730937309473095730967309773098730997310073101731027310373104731057310673107731087310973110731117311273113731147311573116731177311873119731207312173122731237312473125731267312773128731297313073131731327313373134731357313673137731387313973140731417314273143731447314573146731477314873149731507315173152731537315473155731567315773158731597316073161731627316373164731657316673167731687316973170731717317273173731747317573176731777317873179731807318173182731837318473185731867318773188731897319073191731927319373194731957319673197731987319973200732017320273203732047320573206732077320873209732107321173212732137321473215732167321773218732197322073221732227322373224732257322673227732287322973230732317323273233732347323573236732377323873239732407324173242732437324473245732467324773248732497325073251732527325373254732557325673257732587325973260732617326273263732647326573266732677326873269732707327173272732737327473275732767327773278732797328073281732827328373284732857328673287732887328973290732917329273293732947329573296732977329873299733007330173302733037330473305733067330773308733097331073311733127331373314733157331673317733187331973320733217332273323733247332573326733277332873329733307333173332733337333473335733367333773338733397334073341733427334373344733457334673347733487334973350733517335273353733547335573356733577335873359733607336173362733637336473365733667336773368733697337073371733727337373374733757337673377733787337973380733817338273383733847338573386733877338873389733907339173392733937339473395733967339773398733997340073401734027340373404734057340673407734087340973410734117341273413734147341573416734177341873419734207342173422734237342473425734267342773428734297343073431734327343373434734357343673437734387343973440734417344273443734447344573446734477344873449734507345173452734537345473455734567345773458734597346073461734627346373464734657346673467734687346973470734717347273473734747347573476734777347873479734807348173482734837348473485734867348773488734897349073491734927349373494734957349673497734987349973500735017350273503735047350573506735077350873509735107351173512735137351473515735167351773518735197352073521735227352373524735257352673527735287352973530735317353273533735347353573536735377353873539735407354173542735437354473545735467354773548735497355073551735527355373554735557355673557735587355973560735617356273563735647356573566735677356873569735707357173572735737357473575735767357773578735797358073581735827358373584735857358673587735887358973590735917359273593735947359573596735977359873599736007360173602736037360473605736067360773608736097361073611736127361373614736157361673617736187361973620736217362273623736247362573626736277362873629736307363173632736337363473635736367363773638736397364073641736427364373644736457364673647736487364973650736517365273653736547365573656736577365873659736607366173662736637366473665736667366773668736697367073671736727367373674736757367673677736787367973680736817368273683736847368573686736877368873689736907369173692736937369473695736967369773698736997370073701737027370373704737057370673707737087370973710737117371273713737147371573716737177371873719737207372173722737237372473725737267372773728737297373073731737327373373734737357373673737737387373973740737417374273743737447374573746737477374873749737507375173752737537375473755737567375773758737597376073761737627376373764737657376673767737687376973770737717377273773737747377573776737777377873779737807378173782737837378473785737867378773788737897379073791737927379373794737957379673797737987379973800738017380273803738047380573806738077380873809738107381173812738137381473815738167381773818738197382073821738227382373824738257382673827738287382973830738317383273833738347383573836738377383873839738407384173842738437384473845738467384773848738497385073851738527385373854738557385673857738587385973860738617386273863738647386573866738677386873869738707387173872738737387473875738767387773878738797388073881738827388373884738857388673887738887388973890738917389273893738947389573896738977389873899739007390173902739037390473905739067390773908739097391073911739127391373914739157391673917739187391973920739217392273923739247392573926739277392873929739307393173932739337393473935739367393773938739397394073941739427394373944739457394673947739487394973950739517395273953739547395573956739577395873959739607396173962739637396473965739667396773968739697397073971739727397373974739757397673977739787397973980739817398273983739847398573986739877398873989739907399173992739937399473995739967399773998739997400074001740027400374004740057400674007740087400974010740117401274013740147401574016740177401874019740207402174022740237402474025740267402774028740297403074031740327403374034740357403674037740387403974040740417404274043740447404574046740477404874049740507405174052740537405474055740567405774058740597406074061740627406374064740657406674067740687406974070740717407274073740747407574076740777407874079740807408174082740837408474085740867408774088740897409074091740927409374094740957409674097740987409974100741017410274103741047410574106741077410874109741107411174112741137411474115741167411774118741197412074121741227412374124741257412674127741287412974130741317413274133741347413574136741377413874139741407414174142741437414474145741467414774148741497415074151741527415374154741557415674157741587415974160741617416274163741647416574166741677416874169741707417174172741737417474175741767417774178741797418074181741827418374184741857418674187741887418974190741917419274193741947419574196741977419874199742007420174202742037420474205742067420774208742097421074211742127421374214742157421674217742187421974220742217422274223742247422574226742277422874229742307423174232742337423474235742367423774238742397424074241742427424374244742457424674247742487424974250742517425274253742547425574256742577425874259742607426174262742637426474265742667426774268742697427074271742727427374274742757427674277742787427974280742817428274283742847428574286742877428874289742907429174292742937429474295742967429774298742997430074301743027430374304743057430674307743087430974310743117431274313743147431574316743177431874319743207432174322743237432474325743267432774328743297433074331743327433374334743357433674337743387433974340743417434274343743447434574346743477434874349743507435174352743537435474355743567435774358743597436074361743627436374364743657436674367743687436974370743717437274373743747437574376743777437874379743807438174382743837438474385743867438774388743897439074391743927439374394743957439674397743987439974400744017440274403744047440574406744077440874409744107441174412744137441474415744167441774418744197442074421744227442374424744257442674427744287442974430744317443274433744347443574436744377443874439744407444174442744437444474445744467444774448744497445074451744527445374454744557445674457744587445974460744617446274463744647446574466744677446874469744707447174472744737447474475744767447774478744797448074481744827448374484744857448674487744887448974490744917449274493744947449574496744977449874499745007450174502745037450474505745067450774508745097451074511745127451374514745157451674517745187451974520745217452274523745247452574526745277452874529745307453174532745337453474535745367453774538745397454074541745427454374544745457454674547745487454974550745517455274553745547455574556745577455874559745607456174562745637456474565745667456774568745697457074571745727457374574745757457674577745787457974580745817458274583745847458574586745877458874589745907459174592745937459474595745967459774598745997460074601746027460374604746057460674607746087460974610746117461274613746147461574616746177461874619746207462174622746237462474625746267462774628746297463074631746327463374634746357463674637746387463974640746417464274643746447464574646746477464874649746507465174652746537465474655746567465774658746597466074661746627466374664746657466674667746687466974670746717467274673746747467574676746777467874679746807468174682746837468474685746867468774688746897469074691746927469374694746957469674697746987469974700747017470274703747047470574706747077470874709747107471174712747137471474715747167471774718747197472074721747227472374724747257472674727747287472974730747317473274733747347473574736747377473874739747407474174742747437474474745747467474774748747497475074751747527475374754747557475674757747587475974760747617476274763747647476574766747677476874769747707477174772747737477474775747767477774778747797478074781747827478374784747857478674787747887478974790747917479274793747947479574796747977479874799748007480174802748037480474805748067480774808748097481074811748127481374814748157481674817748187481974820748217482274823748247482574826748277482874829748307483174832748337483474835748367483774838748397484074841748427484374844748457484674847748487484974850748517485274853748547485574856748577485874859748607486174862748637486474865748667486774868748697487074871748727487374874748757487674877748787487974880748817488274883748847488574886748877488874889748907489174892748937489474895748967489774898748997490074901749027490374904749057490674907749087490974910749117491274913749147491574916749177491874919749207492174922749237492474925749267492774928749297493074931749327493374934749357493674937749387493974940749417494274943749447494574946749477494874949749507495174952749537495474955749567495774958749597496074961749627496374964749657496674967749687496974970749717497274973749747497574976749777497874979749807498174982749837498474985749867498774988749897499074991749927499374994749957499674997749987499975000750017500275003750047500575006750077500875009750107501175012750137501475015750167501775018750197502075021750227502375024750257502675027750287502975030750317503275033750347503575036750377503875039750407504175042750437504475045750467504775048750497505075051750527505375054750557505675057750587505975060750617506275063750647506575066750677506875069750707507175072750737507475075750767507775078750797508075081750827508375084750857508675087750887508975090750917509275093750947509575096750977509875099751007510175102751037510475105751067510775108751097511075111751127511375114751157511675117751187511975120751217512275123751247512575126751277512875129751307513175132751337513475135751367513775138751397514075141751427514375144751457514675147751487514975150751517515275153751547515575156751577515875159751607516175162751637516475165751667516775168751697517075171751727517375174751757517675177751787517975180751817518275183751847518575186751877518875189751907519175192751937519475195751967519775198751997520075201752027520375204752057520675207752087520975210752117521275213752147521575216752177521875219752207522175222752237522475225752267522775228752297523075231752327523375234752357523675237752387523975240752417524275243752447524575246752477524875249752507525175252752537525475255752567525775258752597526075261752627526375264752657526675267752687526975270752717527275273752747527575276752777527875279752807528175282752837528475285752867528775288752897529075291752927529375294752957529675297752987529975300753017530275303753047530575306753077530875309753107531175312753137531475315753167531775318753197532075321753227532375324753257532675327753287532975330753317533275333753347533575336753377533875339753407534175342753437534475345753467534775348753497535075351753527535375354753557535675357753587535975360753617536275363753647536575366753677536875369753707537175372753737537475375753767537775378753797538075381753827538375384753857538675387753887538975390753917539275393753947539575396753977539875399754007540175402754037540475405754067540775408754097541075411754127541375414754157541675417754187541975420754217542275423754247542575426754277542875429754307543175432754337543475435754367543775438754397544075441754427544375444754457544675447754487544975450754517545275453754547545575456754577545875459754607546175462754637546475465754667546775468754697547075471754727547375474754757547675477754787547975480754817548275483754847548575486754877548875489754907549175492754937549475495754967549775498754997550075501755027550375504755057550675507755087550975510755117551275513755147551575516755177551875519755207552175522755237552475525755267552775528755297553075531755327553375534755357553675537755387553975540755417554275543755447554575546755477554875549755507555175552755537555475555755567555775558755597556075561755627556375564755657556675567755687556975570755717557275573755747557575576755777557875579755807558175582755837558475585755867558775588755897559075591755927559375594755957559675597755987559975600756017560275603756047560575606756077560875609756107561175612756137561475615756167561775618756197562075621756227562375624756257562675627756287562975630756317563275633756347563575636756377563875639756407564175642756437564475645756467564775648756497565075651756527565375654756557565675657756587565975660756617566275663756647566575666756677566875669756707567175672756737567475675756767567775678756797568075681756827568375684756857568675687756887568975690756917569275693756947569575696756977569875699757007570175702757037570475705757067570775708757097571075711757127571375714757157571675717757187571975720757217572275723757247572575726757277572875729757307573175732757337573475735757367573775738757397574075741757427574375744757457574675747757487574975750757517575275753757547575575756757577575875759757607576175762757637576475765757667576775768757697577075771757727577375774757757577675777757787577975780757817578275783757847578575786757877578875789757907579175792757937579475795757967579775798757997580075801758027580375804758057580675807758087580975810758117581275813758147581575816758177581875819758207582175822758237582475825758267582775828758297583075831758327583375834758357583675837758387583975840758417584275843758447584575846758477584875849758507585175852758537585475855758567585775858758597586075861758627586375864758657586675867758687586975870758717587275873758747587575876758777587875879758807588175882758837588475885758867588775888758897589075891758927589375894758957589675897758987589975900759017590275903759047590575906759077590875909759107591175912759137591475915759167591775918759197592075921759227592375924759257592675927759287592975930759317593275933759347593575936759377593875939759407594175942759437594475945759467594775948759497595075951759527595375954759557595675957759587595975960759617596275963759647596575966759677596875969759707597175972759737597475975759767597775978759797598075981759827598375984759857598675987759887598975990759917599275993759947599575996759977599875999760007600176002760037600476005760067600776008760097601076011760127601376014760157601676017760187601976020760217602276023760247602576026760277602876029760307603176032760337603476035760367603776038760397604076041760427604376044760457604676047760487604976050760517605276053760547605576056760577605876059760607606176062760637606476065760667606776068760697607076071760727607376074760757607676077760787607976080760817608276083760847608576086760877608876089760907609176092760937609476095760967609776098760997610076101761027610376104761057610676107761087610976110761117611276113761147611576116761177611876119761207612176122761237612476125761267612776128761297613076131761327613376134761357613676137761387613976140761417614276143761447614576146761477614876149761507615176152761537615476155761567615776158761597616076161761627616376164761657616676167761687616976170761717617276173761747617576176761777617876179761807618176182761837618476185761867618776188761897619076191761927619376194761957619676197761987619976200762017620276203762047620576206762077620876209762107621176212762137621476215762167621776218762197622076221762227622376224762257622676227762287622976230762317623276233762347623576236762377623876239762407624176242762437624476245762467624776248762497625076251762527625376254762557625676257762587625976260762617626276263762647626576266762677626876269762707627176272762737627476275762767627776278762797628076281762827628376284762857628676287762887628976290762917629276293762947629576296762977629876299763007630176302763037630476305763067630776308763097631076311763127631376314763157631676317763187631976320763217632276323763247632576326763277632876329763307633176332763337633476335763367633776338763397634076341763427634376344763457634676347763487634976350763517635276353763547635576356763577635876359763607636176362763637636476365763667636776368763697637076371763727637376374763757637676377763787637976380763817638276383763847638576386763877638876389763907639176392763937639476395763967639776398763997640076401764027640376404764057640676407764087640976410764117641276413764147641576416764177641876419764207642176422764237642476425764267642776428764297643076431764327643376434764357643676437764387643976440764417644276443764447644576446764477644876449764507645176452764537645476455764567645776458764597646076461764627646376464764657646676467764687646976470764717647276473764747647576476764777647876479764807648176482764837648476485764867648776488764897649076491764927649376494764957649676497764987649976500765017650276503765047650576506765077650876509765107651176512765137651476515765167651776518765197652076521765227652376524765257652676527765287652976530765317653276533765347653576536765377653876539765407654176542765437654476545765467654776548765497655076551765527655376554765557655676557765587655976560765617656276563765647656576566765677656876569765707657176572765737657476575765767657776578765797658076581765827658376584765857658676587765887658976590765917659276593765947659576596765977659876599766007660176602766037660476605766067660776608766097661076611766127661376614766157661676617766187661976620766217662276623766247662576626766277662876629766307663176632766337663476635766367663776638766397664076641766427664376644766457664676647766487664976650766517665276653766547665576656766577665876659766607666176662766637666476665766667666776668766697667076671766727667376674766757667676677766787667976680766817668276683766847668576686766877668876689766907669176692766937669476695766967669776698766997670076701767027670376704767057670676707767087670976710767117671276713767147671576716767177671876719767207672176722767237672476725767267672776728767297673076731767327673376734767357673676737767387673976740767417674276743767447674576746767477674876749767507675176752767537675476755767567675776758767597676076761767627676376764767657676676767767687676976770767717677276773767747677576776767777677876779767807678176782767837678476785767867678776788767897679076791767927679376794767957679676797767987679976800768017680276803768047680576806768077680876809768107681176812768137681476815768167681776818768197682076821768227682376824768257682676827768287682976830768317683276833768347683576836768377683876839768407684176842768437684476845768467684776848768497685076851768527685376854768557685676857768587685976860768617686276863768647686576866768677686876869768707687176872768737687476875768767687776878768797688076881768827688376884768857688676887768887688976890768917689276893768947689576896768977689876899769007690176902769037690476905769067690776908769097691076911769127691376914769157691676917769187691976920769217692276923769247692576926769277692876929769307693176932769337693476935769367693776938769397694076941769427694376944769457694676947769487694976950769517695276953769547695576956769577695876959769607696176962769637696476965769667696776968769697697076971769727697376974769757697676977769787697976980769817698276983769847698576986769877698876989769907699176992769937699476995769967699776998769997700077001770027700377004770057700677007770087700977010770117701277013770147701577016770177701877019770207702177022770237702477025770267702777028770297703077031770327703377034770357703677037770387703977040770417704277043770447704577046770477704877049770507705177052770537705477055770567705777058770597706077061770627706377064770657706677067770687706977070770717707277073770747707577076770777707877079770807708177082770837708477085770867708777088770897709077091770927709377094770957709677097770987709977100771017710277103771047710577106771077710877109771107711177112771137711477115771167711777118771197712077121771227712377124771257712677127771287712977130771317713277133771347713577136771377713877139771407714177142771437714477145771467714777148771497715077151771527715377154771557715677157771587715977160771617716277163771647716577166771677716877169771707717177172771737717477175771767717777178771797718077181771827718377184771857718677187771887718977190771917719277193771947719577196771977719877199772007720177202772037720477205772067720777208772097721077211772127721377214772157721677217772187721977220772217722277223772247722577226772277722877229772307723177232772337723477235772367723777238772397724077241772427724377244772457724677247772487724977250772517725277253772547725577256772577725877259772607726177262772637726477265772667726777268772697727077271772727727377274772757727677277772787727977280772817728277283772847728577286772877728877289772907729177292772937729477295772967729777298772997730077301773027730377304773057730677307773087730977310773117731277313773147731577316773177731877319773207732177322773237732477325773267732777328773297733077331773327733377334773357733677337773387733977340773417734277343773447734577346773477734877349773507735177352773537735477355773567735777358773597736077361773627736377364773657736677367773687736977370773717737277373773747737577376773777737877379773807738177382773837738477385773867738777388773897739077391773927739377394773957739677397773987739977400774017740277403774047740577406774077740877409774107741177412774137741477415774167741777418774197742077421774227742377424774257742677427774287742977430774317743277433774347743577436774377743877439774407744177442774437744477445774467744777448774497745077451774527745377454774557745677457774587745977460774617746277463774647746577466774677746877469774707747177472774737747477475774767747777478774797748077481774827748377484774857748677487774887748977490774917749277493774947749577496774977749877499775007750177502775037750477505775067750777508775097751077511775127751377514775157751677517775187751977520775217752277523775247752577526775277752877529775307753177532775337753477535775367753777538775397754077541775427754377544775457754677547775487754977550775517755277553775547755577556775577755877559775607756177562775637756477565775667756777568775697757077571775727757377574775757757677577775787757977580775817758277583775847758577586775877758877589775907759177592775937759477595775967759777598775997760077601776027760377604776057760677607776087760977610776117761277613776147761577616776177761877619776207762177622776237762477625776267762777628776297763077631776327763377634776357763677637776387763977640776417764277643776447764577646776477764877649776507765177652776537765477655776567765777658776597766077661776627766377664776657766677667776687766977670776717767277673776747767577676776777767877679776807768177682776837768477685776867768777688776897769077691776927769377694776957769677697776987769977700777017770277703777047770577706777077770877709777107771177712777137771477715777167771777718777197772077721777227772377724777257772677727777287772977730777317773277733777347773577736777377773877739777407774177742777437774477745777467774777748777497775077751777527775377754777557775677757777587775977760777617776277763777647776577766777677776877769777707777177772777737777477775777767777777778777797778077781777827778377784777857778677787777887778977790777917779277793777947779577796777977779877799778007780177802778037780477805778067780777808778097781077811778127781377814778157781677817778187781977820778217782277823778247782577826778277782877829778307783177832778337783477835778367783777838778397784077841778427784377844778457784677847778487784977850778517785277853778547785577856778577785877859778607786177862778637786477865778667786777868778697787077871778727787377874778757787677877778787787977880778817788277883778847788577886778877788877889778907789177892778937789477895778967789777898778997790077901779027790377904779057790677907779087790977910779117791277913779147791577916779177791877919779207792177922779237792477925779267792777928779297793077931779327793377934779357793677937779387793977940779417794277943779447794577946779477794877949779507795177952779537795477955779567795777958779597796077961779627796377964779657796677967779687796977970779717797277973779747797577976779777797877979779807798177982779837798477985779867798777988779897799077991779927799377994779957799677997779987799978000780017800278003780047800578006780077800878009780107801178012780137801478015780167801778018780197802078021780227802378024780257802678027780287802978030780317803278033780347803578036780377803878039780407804178042780437804478045780467804778048780497805078051780527805378054780557805678057780587805978060780617806278063780647806578066780677806878069780707807178072780737807478075780767807778078780797808078081780827808378084780857808678087780887808978090780917809278093780947809578096780977809878099781007810178102781037810478105781067810778108781097811078111781127811378114781157811678117781187811978120781217812278123781247812578126781277812878129781307813178132781337813478135781367813778138781397814078141781427814378144781457814678147781487814978150781517815278153781547815578156781577815878159781607816178162781637816478165781667816778168781697817078171781727817378174781757817678177781787817978180781817818278183781847818578186781877818878189781907819178192781937819478195781967819778198781997820078201782027820378204782057820678207782087820978210782117821278213782147821578216782177821878219782207822178222782237822478225782267822778228782297823078231782327823378234782357823678237782387823978240782417824278243782447824578246782477824878249782507825178252782537825478255782567825778258782597826078261782627826378264782657826678267782687826978270782717827278273782747827578276782777827878279782807828178282782837828478285782867828778288782897829078291782927829378294782957829678297782987829978300783017830278303783047830578306783077830878309783107831178312783137831478315783167831778318783197832078321783227832378324783257832678327783287832978330783317833278333783347833578336783377833878339783407834178342783437834478345783467834778348783497835078351783527835378354783557835678357783587835978360783617836278363783647836578366783677836878369783707837178372783737837478375783767837778378783797838078381783827838378384783857838678387783887838978390783917839278393783947839578396783977839878399784007840178402784037840478405784067840778408784097841078411784127841378414784157841678417784187841978420784217842278423784247842578426784277842878429784307843178432784337843478435784367843778438784397844078441784427844378444784457844678447784487844978450784517845278453784547845578456784577845878459784607846178462784637846478465784667846778468784697847078471784727847378474784757847678477784787847978480784817848278483784847848578486784877848878489784907849178492784937849478495784967849778498784997850078501785027850378504785057850678507785087850978510785117851278513785147851578516785177851878519785207852178522785237852478525785267852778528785297853078531785327853378534785357853678537785387853978540785417854278543785447854578546785477854878549785507855178552785537855478555785567855778558785597856078561785627856378564785657856678567785687856978570785717857278573785747857578576785777857878579785807858178582785837858478585785867858778588785897859078591785927859378594785957859678597785987859978600786017860278603786047860578606786077860878609786107861178612786137861478615786167861778618786197862078621786227862378624786257862678627786287862978630786317863278633786347863578636786377863878639786407864178642786437864478645786467864778648786497865078651786527865378654786557865678657786587865978660786617866278663786647866578666786677866878669786707867178672786737867478675786767867778678786797868078681786827868378684786857868678687786887868978690786917869278693786947869578696786977869878699787007870178702787037870478705787067870778708787097871078711787127871378714787157871678717787187871978720787217872278723787247872578726787277872878729787307873178732787337873478735787367873778738787397874078741787427874378744787457874678747787487874978750787517875278753787547875578756787577875878759787607876178762787637876478765787667876778768787697877078771787727877378774787757877678777787787877978780787817878278783787847878578786787877878878789787907879178792787937879478795787967879778798787997880078801788027880378804788057880678807788087880978810788117881278813788147881578816788177881878819788207882178822788237882478825788267882778828788297883078831788327883378834788357883678837788387883978840788417884278843788447884578846788477884878849788507885178852788537885478855788567885778858788597886078861788627886378864788657886678867788687886978870788717887278873788747887578876788777887878879788807888178882788837888478885788867888778888788897889078891788927889378894788957889678897788987889978900789017890278903789047890578906789077890878909789107891178912789137891478915789167891778918789197892078921789227892378924789257892678927789287892978930789317893278933789347893578936789377893878939789407894178942789437894478945789467894778948789497895078951789527895378954789557895678957789587895978960789617896278963789647896578966789677896878969789707897178972789737897478975789767897778978789797898078981789827898378984789857898678987789887898978990789917899278993789947899578996789977899878999790007900179002790037900479005790067900779008790097901079011790127901379014790157901679017790187901979020790217902279023790247902579026790277902879029790307903179032790337903479035790367903779038790397904079041790427904379044790457904679047790487904979050790517905279053790547905579056790577905879059790607906179062790637906479065790667906779068790697907079071790727907379074790757907679077790787907979080790817908279083790847908579086790877908879089790907909179092790937909479095790967909779098790997910079101791027910379104791057910679107791087910979110791117911279113791147911579116791177911879119791207912179122791237912479125791267912779128791297913079131791327913379134791357913679137791387913979140791417914279143791447914579146791477914879149791507915179152791537915479155791567915779158791597916079161791627916379164791657916679167791687916979170791717917279173791747917579176791777917879179791807918179182791837918479185791867918779188791897919079191791927919379194791957919679197791987919979200792017920279203792047920579206792077920879209792107921179212792137921479215792167921779218792197922079221792227922379224792257922679227792287922979230792317923279233792347923579236792377923879239792407924179242792437924479245792467924779248792497925079251792527925379254792557925679257792587925979260792617926279263792647926579266792677926879269792707927179272792737927479275792767927779278792797928079281792827928379284792857928679287792887928979290792917929279293792947929579296792977929879299793007930179302793037930479305793067930779308793097931079311793127931379314793157931679317793187931979320793217932279323793247932579326793277932879329793307933179332793337933479335793367933779338793397934079341793427934379344793457934679347793487934979350793517935279353793547935579356793577935879359793607936179362793637936479365793667936779368793697937079371793727937379374793757937679377793787937979380793817938279383793847938579386793877938879389793907939179392793937939479395793967939779398793997940079401794027940379404794057940679407794087940979410794117941279413794147941579416794177941879419794207942179422794237942479425794267942779428794297943079431794327943379434794357943679437794387943979440794417944279443794447944579446794477944879449794507945179452794537945479455794567945779458794597946079461794627946379464794657946679467794687946979470794717947279473794747947579476794777947879479794807948179482794837948479485794867948779488794897949079491794927949379494794957949679497794987949979500795017950279503795047950579506795077950879509795107951179512795137951479515795167951779518795197952079521795227952379524795257952679527795287952979530795317953279533795347953579536795377953879539795407954179542795437954479545795467954779548795497955079551795527955379554795557955679557795587955979560795617956279563795647956579566795677956879569795707957179572795737957479575795767957779578795797958079581795827958379584795857958679587795887958979590795917959279593795947959579596795977959879599796007960179602796037960479605796067960779608796097961079611796127961379614796157961679617796187961979620796217962279623796247962579626796277962879629796307963179632796337963479635796367963779638796397964079641796427964379644796457964679647796487964979650796517965279653796547965579656796577965879659796607966179662796637966479665796667966779668796697967079671796727967379674796757967679677796787967979680796817968279683796847968579686796877968879689796907969179692796937969479695796967969779698796997970079701797027970379704797057970679707797087970979710797117971279713797147971579716797177971879719797207972179722797237972479725797267972779728797297973079731797327973379734797357973679737797387973979740797417974279743797447974579746797477974879749797507975179752797537975479755797567975779758797597976079761797627976379764797657976679767797687976979770797717977279773797747977579776797777977879779797807978179782797837978479785797867978779788797897979079791797927979379794797957979679797797987979979800798017980279803798047980579806798077980879809798107981179812798137981479815798167981779818798197982079821798227982379824798257982679827798287982979830798317983279833798347983579836798377983879839798407984179842798437984479845798467984779848798497985079851798527985379854798557985679857798587985979860798617986279863798647986579866798677986879869798707987179872798737987479875798767987779878798797988079881798827988379884798857988679887798887988979890798917989279893798947989579896798977989879899799007990179902799037990479905799067990779908799097991079911799127991379914799157991679917799187991979920799217992279923799247992579926799277992879929799307993179932799337993479935799367993779938799397994079941799427994379944799457994679947799487994979950799517995279953799547995579956799577995879959799607996179962799637996479965799667996779968799697997079971799727997379974799757997679977799787997979980799817998279983799847998579986799877998879989799907999179992799937999479995799967999779998799998000080001800028000380004800058000680007800088000980010800118001280013800148001580016800178001880019800208002180022800238002480025800268002780028800298003080031800328003380034800358003680037800388003980040800418004280043800448004580046800478004880049800508005180052800538005480055800568005780058800598006080061800628006380064800658006680067800688006980070800718007280073800748007580076800778007880079800808008180082800838008480085800868008780088800898009080091800928009380094800958009680097800988009980100801018010280103801048010580106801078010880109801108011180112801138011480115801168011780118801198012080121801228012380124801258012680127801288012980130801318013280133801348013580136801378013880139801408014180142801438014480145801468014780148801498015080151801528015380154801558015680157801588015980160801618016280163801648016580166801678016880169801708017180172801738017480175801768017780178801798018080181801828018380184801858018680187801888018980190801918019280193801948019580196801978019880199802008020180202802038020480205802068020780208802098021080211802128021380214802158021680217802188021980220802218022280223802248022580226802278022880229802308023180232802338023480235802368023780238802398024080241802428024380244802458024680247802488024980250802518025280253802548025580256802578025880259802608026180262802638026480265802668026780268802698027080271802728027380274802758027680277802788027980280802818028280283802848028580286802878028880289802908029180292802938029480295802968029780298802998030080301803028030380304803058030680307803088030980310803118031280313803148031580316803178031880319803208032180322803238032480325803268032780328803298033080331803328033380334803358033680337803388033980340803418034280343803448034580346803478034880349803508035180352803538035480355803568035780358803598036080361803628036380364803658036680367803688036980370803718037280373803748037580376803778037880379803808038180382803838038480385803868038780388803898039080391803928039380394803958039680397803988039980400804018040280403804048040580406804078040880409804108041180412804138041480415804168041780418804198042080421804228042380424804258042680427804288042980430804318043280433804348043580436804378043880439804408044180442804438044480445804468044780448804498045080451804528045380454804558045680457804588045980460804618046280463804648046580466804678046880469804708047180472804738047480475804768047780478804798048080481804828048380484804858048680487804888048980490804918049280493804948049580496804978049880499805008050180502805038050480505805068050780508805098051080511805128051380514805158051680517805188051980520805218052280523805248052580526805278052880529805308053180532805338053480535805368053780538805398054080541805428054380544805458054680547805488054980550805518055280553805548055580556805578055880559805608056180562805638056480565805668056780568805698057080571805728057380574805758057680577805788057980580805818058280583805848058580586805878058880589805908059180592805938059480595805968059780598805998060080601806028060380604806058060680607806088060980610806118061280613806148061580616806178061880619806208062180622806238062480625806268062780628806298063080631806328063380634806358063680637806388063980640806418064280643806448064580646806478064880649806508065180652806538065480655806568065780658806598066080661806628066380664806658066680667806688066980670806718067280673806748067580676806778067880679806808068180682806838068480685806868068780688806898069080691806928069380694806958069680697806988069980700807018070280703807048070580706807078070880709807108071180712807138071480715807168071780718807198072080721807228072380724807258072680727807288072980730807318073280733807348073580736807378073880739807408074180742807438074480745807468074780748807498075080751807528075380754807558075680757807588075980760807618076280763807648076580766807678076880769807708077180772807738077480775807768077780778807798078080781807828078380784807858078680787807888078980790807918079280793807948079580796807978079880799808008080180802808038080480805808068080780808808098081080811808128081380814808158081680817808188081980820808218082280823808248082580826808278082880829808308083180832808338083480835808368083780838808398084080841808428084380844808458084680847808488084980850808518085280853808548085580856808578085880859808608086180862808638086480865808668086780868808698087080871808728087380874808758087680877808788087980880808818088280883808848088580886808878088880889808908089180892808938089480895808968089780898808998090080901809028090380904809058090680907809088090980910809118091280913809148091580916809178091880919809208092180922809238092480925809268092780928809298093080931809328093380934809358093680937809388093980940809418094280943809448094580946809478094880949809508095180952809538095480955809568095780958809598096080961809628096380964809658096680967809688096980970809718097280973809748097580976809778097880979809808098180982809838098480985809868098780988809898099080991809928099380994809958099680997809988099981000810018100281003810048100581006810078100881009810108101181012810138101481015810168101781018810198102081021810228102381024810258102681027810288102981030810318103281033810348103581036810378103881039810408104181042810438104481045810468104781048810498105081051810528105381054810558105681057810588105981060810618106281063810648106581066810678106881069810708107181072810738107481075810768107781078810798108081081810828108381084810858108681087810888108981090810918109281093810948109581096810978109881099811008110181102811038110481105811068110781108811098111081111811128111381114811158111681117811188111981120811218112281123811248112581126811278112881129811308113181132811338113481135811368113781138811398114081141811428114381144811458114681147811488114981150811518115281153811548115581156811578115881159811608116181162811638116481165811668116781168811698117081171811728117381174811758117681177811788117981180811818118281183811848118581186811878118881189811908119181192811938119481195811968119781198811998120081201812028120381204812058120681207812088120981210812118121281213812148121581216812178121881219812208122181222812238122481225812268122781228812298123081231812328123381234812358123681237812388123981240812418124281243812448124581246812478124881249812508125181252812538125481255812568125781258812598126081261812628126381264812658126681267812688126981270812718127281273812748127581276812778127881279812808128181282812838128481285812868128781288812898129081291812928129381294812958129681297812988129981300813018130281303813048130581306813078130881309813108131181312813138131481315813168131781318813198132081321813228132381324813258132681327813288132981330813318133281333813348133581336813378133881339813408134181342813438134481345813468134781348813498135081351813528135381354813558135681357813588135981360813618136281363813648136581366813678136881369813708137181372813738137481375813768137781378813798138081381813828138381384813858138681387813888138981390813918139281393813948139581396813978139881399814008140181402814038140481405814068140781408814098141081411814128141381414814158141681417814188141981420814218142281423814248142581426814278142881429814308143181432814338143481435814368143781438814398144081441814428144381444814458144681447814488144981450814518145281453814548145581456814578145881459814608146181462814638146481465814668146781468814698147081471814728147381474814758147681477814788147981480814818148281483814848148581486814878148881489814908149181492814938149481495814968149781498814998150081501815028150381504815058150681507815088150981510815118151281513815148151581516815178151881519815208152181522815238152481525815268152781528815298153081531815328153381534815358153681537815388153981540815418154281543815448154581546815478154881549815508155181552815538155481555815568155781558815598156081561815628156381564815658156681567815688156981570815718157281573815748157581576815778157881579815808158181582815838158481585815868158781588815898159081591815928159381594815958159681597815988159981600816018160281603816048160581606816078160881609816108161181612816138161481615816168161781618816198162081621816228162381624816258162681627816288162981630816318163281633816348163581636816378163881639816408164181642816438164481645816468164781648816498165081651816528165381654816558165681657816588165981660816618166281663816648166581666816678166881669816708167181672816738167481675816768167781678816798168081681816828168381684816858168681687816888168981690816918169281693816948169581696816978169881699817008170181702817038170481705817068170781708817098171081711817128171381714817158171681717817188171981720817218172281723817248172581726817278172881729817308173181732817338173481735817368173781738817398174081741817428174381744817458174681747817488174981750817518175281753817548175581756817578175881759817608176181762817638176481765817668176781768817698177081771817728177381774817758177681777817788177981780817818178281783817848178581786817878178881789817908179181792817938179481795817968179781798817998180081801818028180381804818058180681807818088180981810818118181281813818148181581816818178181881819818208182181822818238182481825818268182781828818298183081831818328183381834818358183681837818388183981840818418184281843818448184581846818478184881849818508185181852818538185481855818568185781858818598186081861818628186381864818658186681867818688186981870818718187281873818748187581876818778187881879818808188181882818838188481885818868188781888818898189081891818928189381894818958189681897818988189981900819018190281903819048190581906819078190881909819108191181912819138191481915819168191781918819198192081921819228192381924819258192681927819288192981930819318193281933819348193581936819378193881939819408194181942819438194481945819468194781948819498195081951819528195381954819558195681957819588195981960819618196281963819648196581966819678196881969819708197181972819738197481975819768197781978819798198081981819828198381984819858198681987819888198981990819918199281993819948199581996819978199881999820008200182002820038200482005820068200782008820098201082011820128201382014820158201682017820188201982020820218202282023820248202582026820278202882029820308203182032820338203482035820368203782038820398204082041820428204382044820458204682047820488204982050820518205282053820548205582056820578205882059820608206182062820638206482065820668206782068820698207082071820728207382074820758207682077820788207982080820818208282083820848208582086820878208882089820908209182092820938209482095820968209782098820998210082101821028210382104821058210682107821088210982110821118211282113821148211582116821178211882119821208212182122821238212482125821268212782128821298213082131821328213382134821358213682137821388213982140821418214282143821448214582146821478214882149821508215182152821538215482155821568215782158821598216082161821628216382164821658216682167821688216982170821718217282173821748217582176821778217882179821808218182182821838218482185821868218782188821898219082191821928219382194821958219682197821988219982200822018220282203822048220582206822078220882209822108221182212822138221482215822168221782218822198222082221822228222382224822258222682227822288222982230822318223282233822348223582236822378223882239822408224182242822438224482245822468224782248822498225082251822528225382254822558225682257822588225982260822618226282263822648226582266822678226882269822708227182272822738227482275822768227782278822798228082281822828228382284822858228682287822888228982290822918229282293822948229582296822978229882299823008230182302823038230482305823068230782308823098231082311823128231382314823158231682317823188231982320823218232282323823248232582326823278232882329823308233182332823338233482335823368233782338823398234082341823428234382344823458234682347823488234982350823518235282353823548235582356823578235882359823608236182362823638236482365823668236782368823698237082371823728237382374823758237682377823788237982380823818238282383823848238582386823878238882389823908239182392823938239482395823968239782398823998240082401824028240382404824058240682407824088240982410824118241282413824148241582416824178241882419824208242182422824238242482425824268242782428824298243082431824328243382434824358243682437824388243982440824418244282443824448244582446824478244882449824508245182452824538245482455824568245782458824598246082461824628246382464824658246682467824688246982470824718247282473824748247582476824778247882479824808248182482824838248482485824868248782488824898249082491824928249382494824958249682497824988249982500825018250282503825048250582506825078250882509825108251182512825138251482515825168251782518825198252082521825228252382524825258252682527825288252982530825318253282533825348253582536825378253882539825408254182542825438254482545825468254782548825498255082551825528255382554825558255682557825588255982560825618256282563825648256582566825678256882569825708257182572825738257482575825768257782578825798258082581825828258382584825858258682587825888258982590825918259282593825948259582596825978259882599826008260182602826038260482605826068260782608826098261082611826128261382614826158261682617826188261982620826218262282623826248262582626826278262882629826308263182632826338263482635826368263782638826398264082641826428264382644826458264682647826488264982650826518265282653826548265582656826578265882659826608266182662826638266482665826668266782668826698267082671826728267382674826758267682677826788267982680826818268282683826848268582686826878268882689826908269182692826938269482695826968269782698826998270082701827028270382704827058270682707827088270982710827118271282713827148271582716827178271882719827208272182722827238272482725827268272782728827298273082731827328273382734827358273682737827388273982740827418274282743827448274582746827478274882749827508275182752827538275482755827568275782758827598276082761827628276382764827658276682767827688276982770827718277282773827748277582776827778277882779827808278182782827838278482785827868278782788827898279082791827928279382794827958279682797827988279982800828018280282803828048280582806828078280882809828108281182812828138281482815828168281782818828198282082821828228282382824828258282682827828288282982830828318283282833828348283582836828378283882839828408284182842828438284482845828468284782848828498285082851828528285382854828558285682857828588285982860828618286282863828648286582866828678286882869828708287182872828738287482875828768287782878828798288082881828828288382884828858288682887828888288982890828918289282893828948289582896828978289882899829008290182902829038290482905829068290782908829098291082911829128291382914829158291682917829188291982920829218292282923829248292582926829278292882929829308293182932829338293482935829368293782938829398294082941829428294382944829458294682947829488294982950829518295282953829548295582956829578295882959829608296182962829638296482965829668296782968829698297082971829728297382974829758297682977829788297982980829818298282983829848298582986829878298882989829908299182992829938299482995829968299782998829998300083001830028300383004830058300683007830088300983010830118301283013830148301583016830178301883019830208302183022830238302483025830268302783028830298303083031830328303383034830358303683037830388303983040830418304283043830448304583046830478304883049830508305183052830538305483055830568305783058830598306083061830628306383064830658306683067830688306983070830718307283073830748307583076830778307883079830808308183082830838308483085830868308783088830898309083091830928309383094830958309683097830988309983100831018310283103831048310583106831078310883109831108311183112831138311483115831168311783118831198312083121831228312383124831258312683127831288312983130831318313283133831348313583136831378313883139831408314183142831438314483145831468314783148831498315083151831528315383154831558315683157831588315983160831618316283163831648316583166831678316883169831708317183172831738317483175831768317783178831798318083181831828318383184831858318683187831888318983190831918319283193831948319583196831978319883199832008320183202832038320483205832068320783208832098321083211832128321383214832158321683217832188321983220832218322283223832248322583226832278322883229832308323183232832338323483235832368323783238832398324083241832428324383244832458324683247832488324983250832518325283253832548325583256832578325883259832608326183262832638326483265832668326783268832698327083271832728327383274832758327683277832788327983280832818328283283832848328583286832878328883289832908329183292832938329483295832968329783298832998330083301833028330383304833058330683307833088330983310833118331283313833148331583316833178331883319833208332183322833238332483325833268332783328833298333083331833328333383334833358333683337833388333983340833418334283343833448334583346833478334883349833508335183352833538335483355833568335783358833598336083361833628336383364833658336683367833688336983370833718337283373833748337583376833778337883379833808338183382833838338483385833868338783388833898339083391833928339383394833958339683397833988339983400834018340283403834048340583406834078340883409834108341183412834138341483415834168341783418834198342083421834228342383424834258342683427834288342983430834318343283433834348343583436834378343883439834408344183442834438344483445834468344783448834498345083451834528345383454834558345683457834588345983460834618346283463834648346583466834678346883469834708347183472834738347483475834768347783478834798348083481834828348383484834858348683487834888348983490834918349283493834948349583496834978349883499835008350183502835038350483505835068350783508835098351083511835128351383514835158351683517835188351983520835218352283523835248352583526835278352883529835308353183532835338353483535835368353783538835398354083541835428354383544835458354683547835488354983550835518355283553835548355583556835578355883559835608356183562835638356483565835668356783568835698357083571835728357383574835758357683577835788357983580835818358283583835848358583586835878358883589835908359183592835938359483595835968359783598835998360083601836028360383604836058360683607836088360983610836118361283613836148361583616836178361883619836208362183622836238362483625836268362783628836298363083631836328363383634836358363683637836388363983640836418364283643836448364583646836478364883649836508365183652836538365483655836568365783658836598366083661836628366383664836658366683667836688366983670836718367283673836748367583676836778367883679836808368183682836838368483685836868368783688836898369083691836928369383694836958369683697836988369983700837018370283703837048370583706837078370883709837108371183712837138371483715837168371783718837198372083721837228372383724837258372683727837288372983730837318373283733837348373583736837378373883739837408374183742837438374483745837468374783748837498375083751837528375383754837558375683757837588375983760837618376283763837648376583766837678376883769837708377183772837738377483775837768377783778837798378083781837828378383784837858378683787837888378983790837918379283793837948379583796837978379883799838008380183802838038380483805838068380783808838098381083811838128381383814838158381683817838188381983820838218382283823838248382583826838278382883829838308383183832838338383483835838368383783838838398384083841838428384383844838458384683847838488384983850838518385283853838548385583856838578385883859838608386183862838638386483865838668386783868838698387083871838728387383874838758387683877838788387983880838818388283883838848388583886838878388883889838908389183892838938389483895838968389783898838998390083901839028390383904839058390683907839088390983910839118391283913839148391583916839178391883919839208392183922839238392483925839268392783928839298393083931839328393383934839358393683937839388393983940839418394283943839448394583946839478394883949839508395183952839538395483955839568395783958839598396083961839628396383964839658396683967839688396983970839718397283973839748397583976839778397883979839808398183982839838398483985839868398783988839898399083991839928399383994839958399683997839988399984000840018400284003840048400584006840078400884009840108401184012840138401484015840168401784018840198402084021840228402384024840258402684027840288402984030840318403284033840348403584036840378403884039840408404184042840438404484045840468404784048840498405084051840528405384054840558405684057840588405984060840618406284063840648406584066840678406884069840708407184072840738407484075840768407784078840798408084081840828408384084840858408684087840888408984090840918409284093840948409584096840978409884099841008410184102841038410484105841068410784108841098411084111841128411384114841158411684117841188411984120841218412284123841248412584126841278412884129841308413184132841338413484135841368413784138841398414084141841428414384144841458414684147841488414984150841518415284153841548415584156841578415884159841608416184162841638416484165841668416784168841698417084171841728417384174841758417684177841788417984180841818418284183841848418584186841878418884189841908419184192841938419484195841968419784198841998420084201842028420384204842058420684207842088420984210842118421284213842148421584216842178421884219842208422184222842238422484225842268422784228842298423084231842328423384234842358423684237842388423984240842418424284243842448424584246842478424884249842508425184252842538425484255842568425784258842598426084261842628426384264842658426684267842688426984270842718427284273842748427584276842778427884279842808428184282842838428484285842868428784288842898429084291842928429384294842958429684297842988429984300843018430284303843048430584306843078430884309843108431184312843138431484315843168431784318843198432084321843228432384324843258432684327843288432984330843318433284333843348433584336843378433884339843408434184342843438434484345843468434784348843498435084351843528435384354843558435684357843588435984360843618436284363843648436584366843678436884369843708437184372843738437484375843768437784378843798438084381843828438384384843858438684387843888438984390843918439284393843948439584396843978439884399844008440184402844038440484405844068440784408844098441084411844128441384414844158441684417844188441984420844218442284423844248442584426844278442884429844308443184432844338443484435844368443784438844398444084441844428444384444844458444684447844488444984450844518445284453844548445584456844578445884459844608446184462844638446484465844668446784468844698447084471844728447384474844758447684477844788447984480844818448284483844848448584486844878448884489844908449184492844938449484495844968449784498844998450084501845028450384504845058450684507845088450984510845118451284513845148451584516845178451884519845208452184522845238452484525845268452784528845298453084531845328453384534845358453684537845388453984540845418454284543845448454584546845478454884549845508455184552845538455484555845568455784558845598456084561845628456384564845658456684567845688456984570845718457284573845748457584576845778457884579845808458184582845838458484585845868458784588845898459084591845928459384594845958459684597845988459984600846018460284603846048460584606846078460884609846108461184612846138461484615846168461784618846198462084621846228462384624846258462684627846288462984630846318463284633846348463584636846378463884639846408464184642846438464484645846468464784648846498465084651846528465384654846558465684657846588465984660846618466284663846648466584666846678466884669846708467184672846738467484675846768467784678846798468084681846828468384684846858468684687846888468984690846918469284693846948469584696846978469884699847008470184702847038470484705847068470784708847098471084711847128471384714847158471684717847188471984720847218472284723847248472584726847278472884729847308473184732847338473484735847368473784738847398474084741847428474384744847458474684747847488474984750847518475284753847548475584756847578475884759847608476184762847638476484765847668476784768847698477084771847728477384774847758477684777847788477984780847818478284783847848478584786847878478884789847908479184792847938479484795847968479784798847998480084801848028480384804848058480684807848088480984810848118481284813848148481584816848178481884819848208482184822848238482484825848268482784828848298483084831848328483384834848358483684837848388483984840848418484284843848448484584846848478484884849848508485184852848538485484855848568485784858848598486084861848628486384864848658486684867848688486984870848718487284873848748487584876848778487884879848808488184882848838488484885848868488784888848898489084891848928489384894848958489684897848988489984900849018490284903849048490584906849078490884909849108491184912849138491484915849168491784918849198492084921849228492384924849258492684927849288492984930849318493284933849348493584936849378493884939849408494184942849438494484945849468494784948849498495084951849528495384954849558495684957849588495984960849618496284963849648496584966849678496884969849708497184972849738497484975849768497784978849798498084981849828498384984849858498684987849888498984990849918499284993849948499584996849978499884999850008500185002850038500485005850068500785008850098501085011850128501385014850158501685017850188501985020850218502285023850248502585026850278502885029850308503185032850338503485035850368503785038850398504085041850428504385044850458504685047850488504985050850518505285053850548505585056850578505885059850608506185062850638506485065850668506785068850698507085071850728507385074850758507685077850788507985080850818508285083850848508585086850878508885089850908509185092850938509485095850968509785098850998510085101851028510385104851058510685107851088510985110851118511285113851148511585116851178511885119851208512185122851238512485125851268512785128851298513085131851328513385134851358513685137851388513985140851418514285143851448514585146851478514885149851508515185152851538515485155851568515785158851598516085161851628516385164851658516685167851688516985170851718517285173851748517585176851778517885179851808518185182851838518485185851868518785188851898519085191851928519385194851958519685197851988519985200852018520285203852048520585206852078520885209852108521185212852138521485215852168521785218852198522085221852228522385224852258522685227852288522985230852318523285233852348523585236852378523885239852408524185242852438524485245852468524785248852498525085251852528525385254852558525685257852588525985260852618526285263852648526585266852678526885269852708527185272852738527485275852768527785278852798528085281852828528385284852858528685287852888528985290852918529285293852948529585296852978529885299853008530185302853038530485305853068530785308853098531085311853128531385314853158531685317853188531985320853218532285323853248532585326853278532885329853308533185332853338533485335853368533785338853398534085341853428534385344853458534685347853488534985350853518535285353853548535585356853578535885359853608536185362853638536485365853668536785368853698537085371853728537385374853758537685377853788537985380853818538285383853848538585386853878538885389853908539185392853938539485395853968539785398853998540085401854028540385404854058540685407854088540985410854118541285413854148541585416854178541885419854208542185422854238542485425854268542785428854298543085431854328543385434854358543685437854388543985440854418544285443854448544585446854478544885449854508545185452854538545485455854568545785458854598546085461854628546385464854658546685467854688546985470854718547285473854748547585476854778547885479854808548185482854838548485485854868548785488854898549085491854928549385494854958549685497854988549985500855018550285503855048550585506855078550885509855108551185512855138551485515855168551785518855198552085521855228552385524855258552685527855288552985530855318553285533855348553585536855378553885539855408554185542855438554485545855468554785548855498555085551855528555385554855558555685557855588555985560855618556285563855648556585566855678556885569855708557185572855738557485575855768557785578855798558085581855828558385584855858558685587855888558985590855918559285593855948559585596855978559885599856008560185602856038560485605856068560785608856098561085611856128561385614856158561685617856188561985620856218562285623856248562585626856278562885629856308563185632856338563485635856368563785638856398564085641856428564385644856458564685647856488564985650856518565285653856548565585656856578565885659856608566185662856638566485665856668566785668856698567085671856728567385674856758567685677856788567985680856818568285683856848568585686856878568885689856908569185692856938569485695856968569785698856998570085701857028570385704857058570685707857088570985710857118571285713857148571585716857178571885719857208572185722857238572485725857268572785728857298573085731857328573385734857358573685737857388573985740857418574285743857448574585746857478574885749857508575185752857538575485755857568575785758857598576085761857628576385764857658576685767857688576985770857718577285773857748577585776857778577885779857808578185782857838578485785857868578785788857898579085791857928579385794857958579685797857988579985800858018580285803858048580585806858078580885809858108581185812858138581485815858168581785818858198582085821858228582385824858258582685827858288582985830858318583285833858348583585836858378583885839858408584185842858438584485845858468584785848858498585085851858528585385854858558585685857858588585985860858618586285863858648586585866858678586885869858708587185872858738587485875858768587785878858798588085881858828588385884858858588685887858888588985890858918589285893858948589585896858978589885899859008590185902859038590485905859068590785908859098591085911859128591385914859158591685917859188591985920859218592285923859248592585926859278592885929859308593185932859338593485935859368593785938859398594085941859428594385944859458594685947859488594985950859518595285953859548595585956859578595885959859608596185962859638596485965859668596785968859698597085971859728597385974859758597685977859788597985980859818598285983859848598585986859878598885989859908599185992859938599485995859968599785998859998600086001860028600386004860058600686007860088600986010860118601286013860148601586016860178601886019860208602186022860238602486025860268602786028860298603086031860328603386034860358603686037860388603986040860418604286043860448604586046860478604886049860508605186052860538605486055860568605786058860598606086061860628606386064860658606686067860688606986070860718607286073860748607586076860778607886079860808608186082860838608486085860868608786088860898609086091860928609386094860958609686097860988609986100861018610286103861048610586106861078610886109861108611186112861138611486115861168611786118861198612086121861228612386124861258612686127861288612986130861318613286133861348613586136861378613886139861408614186142861438614486145861468614786148861498615086151861528615386154861558615686157861588615986160861618616286163861648616586166861678616886169861708617186172861738617486175861768617786178861798618086181861828618386184861858618686187861888618986190861918619286193861948619586196861978619886199862008620186202862038620486205862068620786208862098621086211862128621386214862158621686217862188621986220862218622286223862248622586226862278622886229862308623186232862338623486235862368623786238862398624086241862428624386244862458624686247862488624986250862518625286253862548625586256862578625886259862608626186262862638626486265862668626786268862698627086271862728627386274862758627686277862788627986280862818628286283862848628586286862878628886289862908629186292862938629486295862968629786298862998630086301863028630386304863058630686307863088630986310863118631286313863148631586316863178631886319863208632186322863238632486325863268632786328863298633086331863328633386334863358633686337863388633986340863418634286343863448634586346863478634886349863508635186352863538635486355863568635786358863598636086361863628636386364863658636686367863688636986370863718637286373863748637586376863778637886379863808638186382863838638486385863868638786388863898639086391863928639386394863958639686397863988639986400864018640286403864048640586406864078640886409864108641186412864138641486415864168641786418864198642086421864228642386424864258642686427864288642986430864318643286433864348643586436864378643886439864408644186442864438644486445864468644786448864498645086451864528645386454864558645686457864588645986460864618646286463864648646586466864678646886469864708647186472864738647486475864768647786478864798648086481864828648386484864858648686487864888648986490864918649286493864948649586496864978649886499865008650186502865038650486505865068650786508865098651086511865128651386514865158651686517865188651986520865218652286523865248652586526865278652886529865308653186532865338653486535865368653786538865398654086541865428654386544865458654686547865488654986550865518655286553865548655586556865578655886559865608656186562865638656486565865668656786568865698657086571865728657386574865758657686577865788657986580865818658286583865848658586586865878658886589865908659186592865938659486595865968659786598865998660086601866028660386604866058660686607866088660986610866118661286613866148661586616866178661886619866208662186622866238662486625866268662786628866298663086631866328663386634866358663686637866388663986640866418664286643866448664586646866478664886649866508665186652866538665486655866568665786658866598666086661866628666386664866658666686667866688666986670866718667286673866748667586676866778667886679866808668186682866838668486685866868668786688866898669086691866928669386694866958669686697866988669986700867018670286703867048670586706867078670886709867108671186712867138671486715867168671786718867198672086721867228672386724867258672686727867288672986730867318673286733867348673586736867378673886739867408674186742867438674486745867468674786748867498675086751867528675386754867558675686757867588675986760867618676286763867648676586766867678676886769867708677186772867738677486775867768677786778867798678086781867828678386784867858678686787867888678986790867918679286793867948679586796867978679886799868008680186802868038680486805868068680786808868098681086811868128681386814868158681686817868188681986820868218682286823868248682586826868278682886829868308683186832868338683486835868368683786838868398684086841868428684386844868458684686847868488684986850868518685286853868548685586856868578685886859868608686186862868638686486865868668686786868868698687086871868728687386874868758687686877868788687986880868818688286883868848688586886868878688886889868908689186892868938689486895868968689786898868998690086901869028690386904869058690686907869088690986910869118691286913869148691586916869178691886919869208692186922869238692486925869268692786928869298693086931869328693386934869358693686937869388693986940869418694286943869448694586946869478694886949869508695186952869538695486955869568695786958869598696086961869628696386964869658696686967869688696986970869718697286973869748697586976869778697886979869808698186982869838698486985869868698786988869898699086991869928699386994869958699686997869988699987000870018700287003870048700587006870078700887009870108701187012870138701487015870168701787018870198702087021870228702387024870258702687027870288702987030870318703287033870348703587036870378703887039870408704187042870438704487045870468704787048870498705087051870528705387054870558705687057870588705987060870618706287063870648706587066870678706887069870708707187072870738707487075870768707787078870798708087081870828708387084870858708687087870888708987090870918709287093870948709587096870978709887099871008710187102871038710487105871068710787108871098711087111871128711387114871158711687117871188711987120871218712287123871248712587126871278712887129871308713187132871338713487135871368713787138871398714087141871428714387144871458714687147871488714987150871518715287153871548715587156871578715887159871608716187162871638716487165871668716787168871698717087171871728717387174871758717687177871788717987180871818718287183871848718587186871878718887189871908719187192871938719487195871968719787198871998720087201872028720387204872058720687207872088720987210872118721287213872148721587216872178721887219872208722187222872238722487225872268722787228872298723087231872328723387234872358723687237872388723987240872418724287243872448724587246872478724887249872508725187252872538725487255872568725787258872598726087261872628726387264872658726687267872688726987270872718727287273872748727587276872778727887279872808728187282872838728487285872868728787288872898729087291872928729387294872958729687297872988729987300873018730287303873048730587306873078730887309873108731187312873138731487315873168731787318873198732087321873228732387324873258732687327873288732987330873318733287333873348733587336873378733887339873408734187342873438734487345873468734787348873498735087351873528735387354873558735687357873588735987360873618736287363873648736587366873678736887369873708737187372873738737487375873768737787378873798738087381873828738387384873858738687387873888738987390873918739287393873948739587396873978739887399874008740187402874038740487405874068740787408874098741087411874128741387414874158741687417874188741987420874218742287423874248742587426874278742887429874308743187432874338743487435874368743787438874398744087441874428744387444874458744687447874488744987450874518745287453874548745587456874578745887459874608746187462874638746487465874668746787468874698747087471874728747387474874758747687477874788747987480874818748287483874848748587486874878748887489874908749187492874938749487495874968749787498874998750087501875028750387504875058750687507875088750987510875118751287513875148751587516875178751887519875208752187522875238752487525875268752787528875298753087531875328753387534875358753687537875388753987540875418754287543875448754587546875478754887549875508755187552875538755487555875568755787558875598756087561875628756387564875658756687567875688756987570875718757287573875748757587576875778757887579875808758187582875838758487585875868758787588875898759087591875928759387594875958759687597875988759987600876018760287603876048760587606876078760887609876108761187612876138761487615876168761787618876198762087621876228762387624876258762687627876288762987630876318763287633876348763587636876378763887639876408764187642876438764487645876468764787648876498765087651876528765387654876558765687657876588765987660876618766287663876648766587666876678766887669876708767187672876738767487675876768767787678876798768087681876828768387684876858768687687876888768987690876918769287693876948769587696876978769887699877008770187702877038770487705877068770787708877098771087711877128771387714877158771687717877188771987720877218772287723877248772587726877278772887729877308773187732877338773487735877368773787738877398774087741877428774387744877458774687747877488774987750877518775287753877548775587756877578775887759877608776187762877638776487765877668776787768877698777087771877728777387774877758777687777877788777987780877818778287783877848778587786877878778887789877908779187792877938779487795877968779787798877998780087801878028780387804878058780687807878088780987810878118781287813878148781587816878178781887819878208782187822878238782487825878268782787828878298783087831878328783387834878358783687837878388783987840878418784287843878448784587846878478784887849878508785187852878538785487855878568785787858878598786087861878628786387864878658786687867878688786987870878718787287873878748787587876878778787887879878808788187882878838788487885878868788787888878898789087891878928789387894878958789687897878988789987900879018790287903879048790587906879078790887909879108791187912879138791487915879168791787918879198792087921879228792387924879258792687927879288792987930879318793287933879348793587936879378793887939879408794187942879438794487945879468794787948879498795087951879528795387954879558795687957879588795987960879618796287963879648796587966879678796887969879708797187972879738797487975879768797787978879798798087981879828798387984879858798687987879888798987990879918799287993879948799587996879978799887999880008800188002880038800488005880068800788008880098801088011880128801388014880158801688017880188801988020880218802288023880248802588026880278802888029880308803188032880338803488035880368803788038880398804088041880428804388044880458804688047880488804988050880518805288053880548805588056880578805888059880608806188062880638806488065880668806788068880698807088071880728807388074880758807688077880788807988080880818808288083880848808588086880878808888089880908809188092880938809488095880968809788098880998810088101881028810388104881058810688107881088810988110881118811288113881148811588116881178811888119881208812188122881238812488125881268812788128881298813088131881328813388134881358813688137881388813988140881418814288143881448814588146881478814888149881508815188152881538815488155881568815788158881598816088161881628816388164881658816688167881688816988170881718817288173881748817588176881778817888179881808818188182881838818488185881868818788188881898819088191881928819388194881958819688197881988819988200882018820288203882048820588206882078820888209882108821188212882138821488215882168821788218882198822088221882228822388224882258822688227882288822988230882318823288233882348823588236882378823888239882408824188242882438824488245882468824788248882498825088251882528825388254882558825688257882588825988260882618826288263882648826588266882678826888269882708827188272882738827488275882768827788278882798828088281882828828388284882858828688287882888828988290882918829288293882948829588296882978829888299883008830188302883038830488305883068830788308883098831088311883128831388314883158831688317883188831988320883218832288323883248832588326883278832888329883308833188332883338833488335883368833788338883398834088341883428834388344883458834688347883488834988350883518835288353883548835588356883578835888359883608836188362883638836488365883668836788368883698837088371883728837388374883758837688377883788837988380883818838288383883848838588386883878838888389883908839188392883938839488395883968839788398883998840088401884028840388404884058840688407884088840988410884118841288413884148841588416884178841888419884208842188422884238842488425884268842788428884298843088431884328843388434884358843688437884388843988440884418844288443884448844588446884478844888449884508845188452884538845488455884568845788458884598846088461884628846388464884658846688467884688846988470884718847288473884748847588476884778847888479884808848188482884838848488485884868848788488884898849088491884928849388494884958849688497884988849988500885018850288503885048850588506885078850888509885108851188512885138851488515885168851788518885198852088521885228852388524885258852688527885288852988530885318853288533885348853588536885378853888539885408854188542885438854488545885468854788548885498855088551885528855388554885558855688557885588855988560885618856288563885648856588566885678856888569885708857188572885738857488575885768857788578885798858088581885828858388584885858858688587885888858988590885918859288593885948859588596885978859888599886008860188602886038860488605886068860788608886098861088611886128861388614886158861688617886188861988620886218862288623886248862588626886278862888629886308863188632886338863488635886368863788638886398864088641886428864388644886458864688647886488864988650886518865288653886548865588656886578865888659886608866188662886638866488665886668866788668886698867088671886728867388674886758867688677886788867988680886818868288683886848868588686886878868888689886908869188692886938869488695886968869788698886998870088701887028870388704887058870688707887088870988710887118871288713887148871588716887178871888719887208872188722887238872488725887268872788728887298873088731887328873388734887358873688737887388873988740887418874288743887448874588746887478874888749887508875188752887538875488755887568875788758887598876088761887628876388764887658876688767887688876988770887718877288773887748877588776887778877888779887808878188782887838878488785887868878788788887898879088791887928879388794887958879688797887988879988800888018880288803888048880588806888078880888809888108881188812888138881488815888168881788818888198882088821888228882388824888258882688827888288882988830888318883288833888348883588836888378883888839888408884188842888438884488845888468884788848888498885088851888528885388854888558885688857888588885988860888618886288863888648886588866888678886888869888708887188872888738887488875888768887788878888798888088881888828888388884888858888688887888888888988890888918889288893888948889588896888978889888899889008890188902889038890488905889068890788908889098891088911889128891388914889158891688917889188891988920889218892288923889248892588926889278892888929889308893188932889338893488935889368893788938889398894088941889428894388944889458894688947889488894988950889518895288953889548895588956889578895888959889608896188962889638896488965889668896788968889698897088971889728897388974889758897688977889788897988980889818898288983889848898588986889878898888989889908899188992889938899488995889968899788998889998900089001890028900389004890058900689007890088900989010890118901289013890148901589016890178901889019890208902189022890238902489025890268902789028890298903089031890328903389034890358903689037890388903989040890418904289043890448904589046890478904889049890508905189052890538905489055890568905789058890598906089061890628906389064890658906689067890688906989070890718907289073890748907589076890778907889079890808908189082890838908489085890868908789088890898909089091890928909389094890958909689097890988909989100891018910289103891048910589106891078910889109891108911189112891138911489115891168911789118891198912089121891228912389124891258912689127891288912989130891318913289133891348913589136891378913889139891408914189142891438914489145891468914789148891498915089151891528915389154891558915689157891588915989160891618916289163891648916589166891678916889169891708917189172891738917489175891768917789178891798918089181891828918389184891858918689187891888918989190891918919289193891948919589196891978919889199892008920189202892038920489205892068920789208892098921089211892128921389214892158921689217892188921989220892218922289223892248922589226892278922889229892308923189232892338923489235892368923789238892398924089241892428924389244892458924689247892488924989250892518925289253892548925589256892578925889259892608926189262892638926489265892668926789268892698927089271892728927389274892758927689277892788927989280892818928289283892848928589286892878928889289892908929189292892938929489295892968929789298892998930089301893028930389304893058930689307893088930989310893118931289313893148931589316893178931889319893208932189322893238932489325893268932789328893298933089331893328933389334893358933689337893388933989340893418934289343893448934589346893478934889349893508935189352893538935489355893568935789358893598936089361893628936389364893658936689367893688936989370893718937289373893748937589376893778937889379893808938189382893838938489385893868938789388893898939089391893928939389394893958939689397893988939989400894018940289403894048940589406894078940889409894108941189412894138941489415894168941789418894198942089421894228942389424894258942689427894288942989430894318943289433894348943589436894378943889439894408944189442894438944489445894468944789448894498945089451894528945389454894558945689457894588945989460894618946289463894648946589466894678946889469894708947189472894738947489475894768947789478894798948089481894828948389484894858948689487894888948989490894918949289493894948949589496894978949889499895008950189502895038950489505895068950789508895098951089511895128951389514895158951689517895188951989520895218952289523895248952589526895278952889529895308953189532895338953489535895368953789538895398954089541895428954389544895458954689547895488954989550895518955289553895548955589556895578955889559895608956189562895638956489565895668956789568895698957089571895728957389574895758957689577895788957989580895818958289583895848958589586895878958889589895908959189592895938959489595895968959789598895998960089601896028960389604896058960689607896088960989610896118961289613896148961589616896178961889619896208962189622896238962489625896268962789628896298963089631896328963389634896358963689637896388963989640896418964289643896448964589646896478964889649896508965189652896538965489655896568965789658896598966089661896628966389664896658966689667896688966989670896718967289673896748967589676896778967889679896808968189682896838968489685896868968789688896898969089691896928969389694896958969689697896988969989700897018970289703897048970589706897078970889709897108971189712897138971489715897168971789718897198972089721897228972389724897258972689727897288972989730897318973289733897348973589736897378973889739897408974189742897438974489745897468974789748897498975089751897528975389754897558975689757897588975989760897618976289763897648976589766897678976889769897708977189772897738977489775897768977789778897798978089781897828978389784897858978689787897888978989790897918979289793897948979589796897978979889799898008980189802898038980489805898068980789808898098981089811898128981389814898158981689817898188981989820898218982289823898248982589826898278982889829898308983189832898338983489835898368983789838898398984089841898428984389844898458984689847898488984989850898518985289853898548985589856898578985889859898608986189862898638986489865898668986789868898698987089871898728987389874898758987689877898788987989880898818988289883898848988589886898878988889889898908989189892898938989489895898968989789898898998990089901899028990389904899058990689907899088990989910899118991289913899148991589916899178991889919899208992189922899238992489925899268992789928899298993089931899328993389934899358993689937899388993989940899418994289943899448994589946899478994889949899508995189952899538995489955899568995789958899598996089961899628996389964899658996689967899688996989970899718997289973899748997589976899778997889979899808998189982899838998489985899868998789988899898999089991899928999389994899958999689997899988999990000900019000290003900049000590006900079000890009900109001190012900139001490015900169001790018900199002090021900229002390024900259002690027900289002990030900319003290033900349003590036900379003890039900409004190042900439004490045900469004790048900499005090051900529005390054900559005690057900589005990060900619006290063900649006590066900679006890069900709007190072900739007490075900769007790078900799008090081900829008390084900859008690087900889008990090900919009290093900949009590096900979009890099901009010190102901039010490105901069010790108901099011090111901129011390114901159011690117901189011990120901219012290123901249012590126901279012890129901309013190132901339013490135901369013790138901399014090141901429014390144901459014690147901489014990150901519015290153901549015590156901579015890159901609016190162901639016490165901669016790168901699017090171901729017390174901759017690177901789017990180901819018290183901849018590186901879018890189901909019190192901939019490195901969019790198901999020090201902029020390204902059020690207902089020990210902119021290213902149021590216902179021890219902209022190222902239022490225902269022790228902299023090231902329023390234902359023690237902389023990240902419024290243902449024590246902479024890249902509025190252902539025490255902569025790258902599026090261902629026390264902659026690267902689026990270902719027290273902749027590276902779027890279902809028190282902839028490285902869028790288902899029090291902929029390294902959029690297902989029990300903019030290303903049030590306903079030890309903109031190312903139031490315903169031790318903199032090321903229032390324903259032690327903289032990330903319033290333903349033590336903379033890339903409034190342903439034490345903469034790348903499035090351903529035390354903559035690357903589035990360903619036290363903649036590366903679036890369903709037190372903739037490375903769037790378903799038090381903829038390384903859038690387903889038990390903919039290393903949039590396903979039890399904009040190402904039040490405904069040790408904099041090411904129041390414904159041690417904189041990420904219042290423904249042590426904279042890429904309043190432904339043490435904369043790438904399044090441904429044390444904459044690447904489044990450904519045290453904549045590456904579045890459904609046190462904639046490465904669046790468904699047090471904729047390474904759047690477904789047990480904819048290483904849048590486904879048890489904909049190492904939049490495904969049790498904999050090501905029050390504905059050690507905089050990510905119051290513905149051590516905179051890519905209052190522905239052490525905269052790528905299053090531905329053390534905359053690537905389053990540905419054290543905449054590546905479054890549905509055190552905539055490555905569055790558905599056090561905629056390564905659056690567905689056990570905719057290573905749057590576905779057890579905809058190582905839058490585905869058790588905899059090591905929059390594905959059690597905989059990600906019060290603906049060590606906079060890609906109061190612906139061490615906169061790618906199062090621906229062390624906259062690627906289062990630906319063290633906349063590636906379063890639906409064190642906439064490645906469064790648906499065090651906529065390654906559065690657906589065990660906619066290663906649066590666906679066890669906709067190672906739067490675906769067790678906799068090681906829068390684906859068690687906889068990690906919069290693906949069590696906979069890699907009070190702907039070490705907069070790708907099071090711907129071390714907159071690717907189071990720907219072290723907249072590726907279072890729907309073190732907339073490735907369073790738907399074090741907429074390744907459074690747907489074990750907519075290753907549075590756907579075890759907609076190762907639076490765907669076790768907699077090771907729077390774907759077690777907789077990780907819078290783907849078590786907879078890789907909079190792907939079490795907969079790798907999080090801908029080390804908059080690807908089080990810908119081290813908149081590816908179081890819908209082190822908239082490825908269082790828908299083090831908329083390834908359083690837908389083990840908419084290843908449084590846908479084890849908509085190852908539085490855908569085790858908599086090861908629086390864908659086690867908689086990870908719087290873908749087590876908779087890879908809088190882908839088490885908869088790888908899089090891908929089390894908959089690897908989089990900909019090290903909049090590906909079090890909909109091190912909139091490915909169091790918909199092090921909229092390924909259092690927909289092990930909319093290933909349093590936909379093890939909409094190942909439094490945909469094790948909499095090951909529095390954909559095690957909589095990960909619096290963909649096590966909679096890969909709097190972909739097490975909769097790978909799098090981909829098390984909859098690987909889098990990909919099290993909949099590996909979099890999910009100191002910039100491005910069100791008910099101091011910129101391014910159101691017910189101991020910219102291023910249102591026910279102891029910309103191032910339103491035910369103791038910399104091041910429104391044910459104691047910489104991050910519105291053910549105591056910579105891059910609106191062910639106491065910669106791068910699107091071910729107391074910759107691077910789107991080910819108291083910849108591086910879108891089910909109191092910939109491095910969109791098910999110091101911029110391104911059110691107911089110991110911119111291113911149111591116911179111891119911209112191122911239112491125911269112791128911299113091131911329113391134911359113691137911389113991140911419114291143911449114591146911479114891149911509115191152911539115491155911569115791158911599116091161911629116391164911659116691167911689116991170911719117291173911749117591176911779117891179911809118191182911839118491185911869118791188911899119091191911929119391194911959119691197911989119991200912019120291203912049120591206912079120891209912109121191212912139121491215912169121791218912199122091221912229122391224912259122691227912289122991230912319123291233912349123591236912379123891239912409124191242912439124491245912469124791248912499125091251912529125391254912559125691257912589125991260912619126291263912649126591266912679126891269912709127191272912739127491275912769127791278912799128091281912829128391284912859128691287912889128991290912919129291293912949129591296912979129891299913009130191302913039130491305913069130791308913099131091311913129131391314913159131691317913189131991320913219132291323913249132591326913279132891329913309133191332913339133491335913369133791338913399134091341913429134391344913459134691347913489134991350913519135291353913549135591356913579135891359913609136191362913639136491365913669136791368913699137091371913729137391374913759137691377913789137991380913819138291383913849138591386913879138891389913909139191392913939139491395913969139791398913999140091401914029140391404914059140691407914089140991410914119141291413914149141591416914179141891419914209142191422914239142491425914269142791428914299143091431914329143391434914359143691437914389143991440914419144291443914449144591446914479144891449914509145191452914539145491455914569145791458914599146091461914629146391464914659146691467914689146991470914719147291473914749147591476914779147891479914809148191482914839148491485914869148791488914899149091491914929149391494914959149691497914989149991500915019150291503915049150591506915079150891509915109151191512915139151491515915169151791518915199152091521915229152391524915259152691527915289152991530915319153291533915349153591536915379153891539915409154191542915439154491545915469154791548915499155091551915529155391554915559155691557915589155991560915619156291563915649156591566915679156891569915709157191572915739157491575915769157791578915799158091581915829158391584915859158691587915889158991590915919159291593915949159591596915979159891599916009160191602916039160491605916069160791608916099161091611916129161391614916159161691617916189161991620916219162291623916249162591626916279162891629916309163191632916339163491635916369163791638916399164091641916429164391644916459164691647916489164991650916519165291653916549165591656916579165891659916609166191662916639166491665916669166791668916699167091671916729167391674916759167691677916789167991680916819168291683916849168591686916879168891689916909169191692916939169491695916969169791698916999170091701917029170391704917059170691707917089170991710917119171291713917149171591716917179171891719917209172191722917239172491725917269172791728917299173091731917329173391734917359173691737917389173991740917419174291743917449174591746917479174891749917509175191752917539175491755917569175791758917599176091761917629176391764917659176691767917689176991770917719177291773917749177591776917779177891779917809178191782917839178491785917869178791788917899179091791917929179391794917959179691797917989179991800918019180291803918049180591806918079180891809918109181191812918139181491815918169181791818918199182091821918229182391824918259182691827918289182991830918319183291833918349183591836918379183891839918409184191842918439184491845918469184791848918499185091851918529185391854918559185691857918589185991860918619186291863918649186591866918679186891869918709187191872918739187491875918769187791878918799188091881918829188391884918859188691887918889188991890918919189291893918949189591896918979189891899919009190191902919039190491905919069190791908919099191091911919129191391914919159191691917919189191991920919219192291923919249192591926919279192891929919309193191932919339193491935919369193791938919399194091941919429194391944919459194691947919489194991950919519195291953919549195591956919579195891959919609196191962919639196491965919669196791968919699197091971919729197391974919759197691977919789197991980919819198291983919849198591986919879198891989919909199191992919939199491995919969199791998919999200092001920029200392004920059200692007920089200992010920119201292013920149201592016920179201892019920209202192022920239202492025920269202792028920299203092031920329203392034920359203692037920389203992040920419204292043920449204592046920479204892049920509205192052920539205492055920569205792058920599206092061920629206392064920659206692067920689206992070920719207292073920749207592076920779207892079920809208192082920839208492085920869208792088920899209092091920929209392094920959209692097920989209992100921019210292103921049210592106921079210892109921109211192112921139211492115921169211792118921199212092121921229212392124921259212692127921289212992130921319213292133921349213592136921379213892139921409214192142921439214492145921469214792148921499215092151921529215392154921559215692157921589215992160921619216292163921649216592166921679216892169921709217192172921739217492175921769217792178921799218092181921829218392184921859218692187921889218992190921919219292193921949219592196921979219892199922009220192202922039220492205922069220792208922099221092211922129221392214922159221692217922189221992220922219222292223922249222592226922279222892229922309223192232922339223492235922369223792238922399224092241922429224392244922459224692247922489224992250922519225292253922549225592256922579225892259922609226192262922639226492265922669226792268922699227092271922729227392274922759227692277922789227992280922819228292283922849228592286922879228892289922909229192292922939229492295922969229792298922999230092301923029230392304923059230692307923089230992310923119231292313923149231592316923179231892319923209232192322923239232492325923269232792328923299233092331923329233392334923359233692337923389233992340923419234292343923449234592346923479234892349923509235192352923539235492355923569235792358923599236092361923629236392364923659236692367923689236992370923719237292373923749237592376923779237892379923809238192382923839238492385923869238792388923899239092391923929239392394923959239692397923989239992400924019240292403924049240592406924079240892409924109241192412924139241492415924169241792418924199242092421924229242392424924259242692427924289242992430924319243292433924349243592436924379243892439924409244192442924439244492445924469244792448924499245092451924529245392454924559245692457924589245992460924619246292463924649246592466924679246892469924709247192472924739247492475924769247792478924799248092481924829248392484924859248692487924889248992490924919249292493924949249592496924979249892499925009250192502925039250492505925069250792508925099251092511925129251392514925159251692517925189251992520925219252292523925249252592526925279252892529925309253192532925339253492535925369253792538925399254092541925429254392544925459254692547925489254992550925519255292553925549255592556925579255892559925609256192562925639256492565925669256792568925699257092571925729257392574925759257692577925789257992580925819258292583925849258592586925879258892589925909259192592925939259492595925969259792598925999260092601926029260392604926059260692607926089260992610926119261292613926149261592616926179261892619926209262192622926239262492625926269262792628926299263092631926329263392634926359263692637926389263992640926419264292643926449264592646926479264892649926509265192652926539265492655926569265792658926599266092661926629266392664926659266692667926689266992670926719267292673926749267592676926779267892679926809268192682926839268492685926869268792688926899269092691926929269392694926959269692697926989269992700927019270292703927049270592706927079270892709927109271192712927139271492715927169271792718927199272092721927229272392724927259272692727927289272992730927319273292733927349273592736927379273892739927409274192742927439274492745927469274792748927499275092751927529275392754927559275692757927589275992760927619276292763927649276592766927679276892769927709277192772927739277492775927769277792778927799278092781927829278392784927859278692787927889278992790927919279292793927949279592796927979279892799928009280192802928039280492805928069280792808928099281092811928129281392814928159281692817928189281992820928219282292823928249282592826928279282892829928309283192832928339283492835928369283792838928399284092841928429284392844928459284692847928489284992850928519285292853928549285592856928579285892859928609286192862928639286492865928669286792868928699287092871928729287392874928759287692877928789287992880928819288292883928849288592886928879288892889928909289192892928939289492895928969289792898928999290092901929029290392904929059290692907929089290992910929119291292913929149291592916929179291892919929209292192922929239292492925929269292792928929299293092931929329293392934929359293692937929389293992940929419294292943929449294592946929479294892949929509295192952929539295492955929569295792958929599296092961929629296392964929659296692967929689296992970929719297292973929749297592976929779297892979929809298192982929839298492985929869298792988929899299092991929929299392994929959299692997929989299993000930019300293003930049300593006930079300893009930109301193012930139301493015930169301793018930199302093021930229302393024930259302693027930289302993030930319303293033930349303593036930379303893039930409304193042930439304493045930469304793048930499305093051930529305393054930559305693057930589305993060930619306293063930649306593066930679306893069930709307193072930739307493075930769307793078930799308093081930829308393084930859308693087930889308993090930919309293093930949309593096930979309893099931009310193102931039310493105931069310793108931099311093111931129311393114931159311693117931189311993120931219312293123931249312593126931279312893129931309313193132931339313493135931369313793138931399314093141931429314393144931459314693147931489314993150931519315293153931549315593156931579315893159931609316193162931639316493165931669316793168931699317093171931729317393174931759317693177931789317993180931819318293183931849318593186931879318893189931909319193192931939319493195931969319793198931999320093201932029320393204932059320693207932089320993210932119321293213932149321593216932179321893219932209322193222932239322493225932269322793228932299323093231932329323393234932359323693237932389323993240932419324293243932449324593246932479324893249932509325193252932539325493255932569325793258932599326093261932629326393264932659326693267932689326993270932719327293273932749327593276932779327893279932809328193282932839328493285932869328793288932899329093291932929329393294932959329693297932989329993300933019330293303933049330593306933079330893309933109331193312933139331493315933169331793318933199332093321933229332393324933259332693327933289332993330933319333293333933349333593336933379333893339933409334193342933439334493345933469334793348933499335093351933529335393354933559335693357933589335993360933619336293363933649336593366933679336893369933709337193372933739337493375933769337793378933799338093381933829338393384933859338693387933889338993390933919339293393933949339593396933979339893399934009340193402934039340493405934069340793408934099341093411934129341393414934159341693417934189341993420934219342293423934249342593426934279342893429934309343193432934339343493435934369343793438934399344093441934429344393444934459344693447934489344993450934519345293453934549345593456934579345893459934609346193462934639346493465934669346793468934699347093471934729347393474934759347693477934789347993480934819348293483934849348593486934879348893489934909349193492934939349493495934969349793498934999350093501935029350393504935059350693507935089350993510935119351293513935149351593516935179351893519935209352193522935239352493525935269352793528935299353093531935329353393534935359353693537935389353993540935419354293543935449354593546935479354893549935509355193552935539355493555935569355793558935599356093561935629356393564935659356693567935689356993570935719357293573935749357593576935779357893579935809358193582935839358493585935869358793588935899359093591935929359393594935959359693597935989359993600936019360293603936049360593606936079360893609936109361193612936139361493615936169361793618936199362093621936229362393624936259362693627936289362993630936319363293633936349363593636936379363893639936409364193642936439364493645936469364793648936499365093651936529365393654936559365693657936589365993660936619366293663936649366593666936679366893669936709367193672936739367493675936769367793678936799368093681936829368393684936859368693687936889368993690936919369293693936949369593696936979369893699937009370193702937039370493705937069370793708937099371093711937129371393714937159371693717937189371993720937219372293723937249372593726937279372893729937309373193732937339373493735937369373793738937399374093741937429374393744937459374693747937489374993750937519375293753937549375593756937579375893759937609376193762937639376493765937669376793768937699377093771937729377393774937759377693777937789377993780937819378293783937849378593786937879378893789937909379193792937939379493795937969379793798937999380093801938029380393804938059380693807938089380993810938119381293813938149381593816938179381893819938209382193822938239382493825938269382793828938299383093831938329383393834938359383693837938389383993840938419384293843938449384593846938479384893849938509385193852938539385493855938569385793858938599386093861938629386393864938659386693867938689386993870938719387293873938749387593876938779387893879938809388193882938839388493885938869388793888938899389093891938929389393894938959389693897938989389993900939019390293903939049390593906939079390893909939109391193912939139391493915939169391793918939199392093921939229392393924939259392693927939289392993930939319393293933939349393593936939379393893939939409394193942939439394493945939469394793948939499395093951939529395393954939559395693957939589395993960939619396293963939649396593966939679396893969939709397193972939739397493975939769397793978939799398093981939829398393984939859398693987939889398993990939919399293993939949399593996939979399893999940009400194002940039400494005940069400794008940099401094011940129401394014940159401694017940189401994020940219402294023940249402594026940279402894029940309403194032940339403494035940369403794038940399404094041940429404394044940459404694047940489404994050940519405294053940549405594056940579405894059940609406194062940639406494065940669406794068940699407094071940729407394074940759407694077940789407994080940819408294083940849408594086940879408894089940909409194092940939409494095940969409794098940999410094101941029410394104941059410694107941089410994110941119411294113941149411594116941179411894119941209412194122941239412494125941269412794128941299413094131941329413394134941359413694137941389413994140941419414294143941449414594146941479414894149941509415194152941539415494155941569415794158941599416094161941629416394164941659416694167941689416994170941719417294173941749417594176941779417894179941809418194182941839418494185941869418794188941899419094191941929419394194941959419694197941989419994200942019420294203942049420594206942079420894209942109421194212942139421494215942169421794218942199422094221942229422394224942259422694227942289422994230942319423294233942349423594236942379423894239942409424194242942439424494245942469424794248942499425094251942529425394254942559425694257942589425994260942619426294263942649426594266942679426894269942709427194272942739427494275942769427794278942799428094281942829428394284942859428694287942889428994290942919429294293942949429594296942979429894299943009430194302943039430494305943069430794308943099431094311943129431394314943159431694317943189431994320943219432294323943249432594326943279432894329943309433194332943339433494335943369433794338943399434094341943429434394344943459434694347943489434994350943519435294353943549435594356943579435894359943609436194362943639436494365943669436794368943699437094371943729437394374943759437694377943789437994380943819438294383943849438594386943879438894389943909439194392943939439494395943969439794398943999440094401944029440394404944059440694407944089440994410944119441294413944149441594416944179441894419944209442194422944239442494425944269442794428944299443094431944329443394434944359443694437944389443994440944419444294443944449444594446944479444894449944509445194452944539445494455944569445794458944599446094461944629446394464944659446694467944689446994470944719447294473944749447594476944779447894479944809448194482944839448494485944869448794488944899449094491944929449394494944959449694497944989449994500945019450294503945049450594506945079450894509945109451194512945139451494515945169451794518945199452094521945229452394524945259452694527945289452994530945319453294533945349453594536945379453894539945409454194542945439454494545945469454794548945499455094551945529455394554945559455694557945589455994560945619456294563945649456594566945679456894569945709457194572945739457494575945769457794578945799458094581945829458394584945859458694587945889458994590945919459294593945949459594596945979459894599946009460194602946039460494605946069460794608946099461094611946129461394614946159461694617946189461994620946219462294623946249462594626946279462894629946309463194632946339463494635946369463794638946399464094641946429464394644946459464694647946489464994650946519465294653946549465594656946579465894659946609466194662946639466494665946669466794668946699467094671946729467394674946759467694677946789467994680946819468294683946849468594686946879468894689946909469194692946939469494695946969469794698946999470094701947029470394704947059470694707947089470994710947119471294713947149471594716947179471894719947209472194722947239472494725947269472794728947299473094731947329473394734947359473694737947389473994740947419474294743947449474594746947479474894749947509475194752947539475494755947569475794758947599476094761947629476394764947659476694767947689476994770947719477294773947749477594776947779477894779947809478194782947839478494785947869478794788947899479094791947929479394794947959479694797947989479994800948019480294803948049480594806948079480894809948109481194812948139481494815948169481794818948199482094821948229482394824948259482694827948289482994830948319483294833948349483594836948379483894839948409484194842948439484494845948469484794848948499485094851948529485394854948559485694857948589485994860948619486294863948649486594866948679486894869948709487194872948739487494875948769487794878948799488094881948829488394884948859488694887948889488994890948919489294893948949489594896948979489894899949009490194902949039490494905949069490794908949099491094911949129491394914949159491694917949189491994920949219492294923949249492594926949279492894929949309493194932949339493494935949369493794938949399494094941949429494394944949459494694947949489494994950949519495294953949549495594956949579495894959949609496194962949639496494965949669496794968949699497094971949729497394974949759497694977949789497994980949819498294983949849498594986949879498894989949909499194992949939499494995949969499794998949999500095001950029500395004950059500695007950089500995010950119501295013950149501595016950179501895019950209502195022950239502495025950269502795028950299503095031950329503395034950359503695037950389503995040950419504295043950449504595046950479504895049950509505195052950539505495055950569505795058950599506095061950629506395064950659506695067950689506995070950719507295073950749507595076950779507895079950809508195082950839508495085950869508795088950899509095091950929509395094950959509695097950989509995100951019510295103951049510595106951079510895109951109511195112951139511495115951169511795118951199512095121951229512395124951259512695127951289512995130951319513295133951349513595136951379513895139951409514195142951439514495145951469514795148951499515095151951529515395154951559515695157951589515995160951619516295163951649516595166951679516895169951709517195172951739517495175951769517795178951799518095181951829518395184951859518695187951889518995190951919519295193951949519595196951979519895199952009520195202952039520495205952069520795208952099521095211952129521395214952159521695217952189521995220952219522295223952249522595226952279522895229952309523195232952339523495235952369523795238952399524095241952429524395244952459524695247952489524995250952519525295253952549525595256952579525895259952609526195262952639526495265952669526795268952699527095271952729527395274952759527695277952789527995280952819528295283952849528595286952879528895289952909529195292952939529495295952969529795298952999530095301953029530395304953059530695307953089530995310953119531295313953149531595316953179531895319953209532195322953239532495325953269532795328953299533095331953329533395334953359533695337953389533995340953419534295343953449534595346953479534895349953509535195352953539535495355953569535795358953599536095361953629536395364953659536695367953689536995370953719537295373953749537595376953779537895379953809538195382953839538495385953869538795388953899539095391953929539395394953959539695397953989539995400954019540295403954049540595406954079540895409954109541195412954139541495415954169541795418954199542095421954229542395424954259542695427954289542995430954319543295433954349543595436954379543895439954409544195442954439544495445954469544795448954499545095451954529545395454954559545695457954589545995460954619546295463954649546595466954679546895469954709547195472954739547495475954769547795478954799548095481954829548395484954859548695487954889548995490954919549295493954949549595496954979549895499955009550195502955039550495505955069550795508955099551095511955129551395514955159551695517955189551995520955219552295523955249552595526955279552895529955309553195532955339553495535955369553795538955399554095541955429554395544955459554695547955489554995550955519555295553955549555595556955579555895559955609556195562955639556495565955669556795568955699557095571955729557395574955759557695577955789557995580955819558295583955849558595586955879558895589955909559195592955939559495595955969559795598955999560095601956029560395604956059560695607956089560995610956119561295613956149561595616956179561895619956209562195622956239562495625956269562795628956299563095631956329563395634956359563695637956389563995640956419564295643956449564595646956479564895649956509565195652956539565495655956569565795658956599566095661956629566395664956659566695667956689566995670956719567295673956749567595676956779567895679956809568195682956839568495685956869568795688956899569095691956929569395694956959569695697956989569995700957019570295703957049570595706957079570895709957109571195712957139571495715957169571795718957199572095721957229572395724957259572695727957289572995730957319573295733957349573595736957379573895739957409574195742957439574495745957469574795748957499575095751957529575395754957559575695757957589575995760957619576295763957649576595766957679576895769957709577195772957739577495775957769577795778957799578095781957829578395784957859578695787957889578995790957919579295793957949579595796957979579895799958009580195802958039580495805958069580795808958099581095811958129581395814958159581695817958189581995820958219582295823958249582595826958279582895829958309583195832958339583495835958369583795838958399584095841958429584395844958459584695847958489584995850958519585295853958549585595856958579585895859958609586195862958639586495865958669586795868958699587095871958729587395874958759587695877958789587995880958819588295883958849588595886958879588895889958909589195892958939589495895958969589795898958999590095901959029590395904959059590695907959089590995910959119591295913959149591595916959179591895919959209592195922959239592495925959269592795928959299593095931959329593395934959359593695937959389593995940959419594295943959449594595946959479594895949959509595195952959539595495955959569595795958959599596095961959629596395964959659596695967959689596995970959719597295973959749597595976959779597895979959809598195982959839598495985959869598795988959899599095991959929599395994959959599695997959989599996000960019600296003960049600596006960079600896009960109601196012960139601496015960169601796018960199602096021960229602396024960259602696027960289602996030960319603296033960349603596036960379603896039960409604196042960439604496045960469604796048960499605096051960529605396054960559605696057960589605996060960619606296063960649606596066960679606896069960709607196072960739607496075960769607796078960799608096081960829608396084960859608696087960889608996090960919609296093960949609596096960979609896099961009610196102961039610496105961069610796108961099611096111961129611396114961159611696117961189611996120961219612296123961249612596126961279612896129961309613196132961339613496135961369613796138961399614096141961429614396144961459614696147961489614996150961519615296153961549615596156961579615896159961609616196162961639616496165961669616796168961699617096171961729617396174961759617696177961789617996180961819618296183961849618596186961879618896189961909619196192961939619496195961969619796198961999620096201962029620396204962059620696207962089620996210962119621296213962149621596216962179621896219962209622196222962239622496225962269622796228962299623096231962329623396234962359623696237962389623996240962419624296243962449624596246962479624896249962509625196252962539625496255962569625796258962599626096261962629626396264962659626696267962689626996270962719627296273962749627596276962779627896279962809628196282962839628496285962869628796288962899629096291962929629396294962959629696297962989629996300963019630296303963049630596306963079630896309963109631196312963139631496315963169631796318963199632096321963229632396324963259632696327963289632996330963319633296333963349633596336963379633896339963409634196342963439634496345963469634796348963499635096351963529635396354963559635696357963589635996360963619636296363963649636596366963679636896369963709637196372963739637496375963769637796378963799638096381963829638396384963859638696387963889638996390963919639296393963949639596396963979639896399964009640196402964039640496405964069640796408964099641096411964129641396414964159641696417964189641996420964219642296423964249642596426964279642896429964309643196432964339643496435964369643796438964399644096441964429644396444964459644696447964489644996450964519645296453964549645596456964579645896459964609646196462964639646496465964669646796468964699647096471964729647396474964759647696477964789647996480964819648296483964849648596486964879648896489964909649196492964939649496495964969649796498964999650096501965029650396504965059650696507965089650996510965119651296513965149651596516965179651896519965209652196522965239652496525965269652796528965299653096531965329653396534965359653696537965389653996540965419654296543965449654596546965479654896549965509655196552965539655496555965569655796558965599656096561965629656396564965659656696567965689656996570965719657296573965749657596576965779657896579965809658196582965839658496585965869658796588965899659096591965929659396594965959659696597965989659996600966019660296603966049660596606966079660896609966109661196612966139661496615966169661796618966199662096621966229662396624966259662696627966289662996630966319663296633966349663596636966379663896639966409664196642966439664496645966469664796648966499665096651966529665396654966559665696657966589665996660966619666296663966649666596666966679666896669966709667196672966739667496675966769667796678966799668096681966829668396684966859668696687966889668996690966919669296693966949669596696966979669896699967009670196702967039670496705967069670796708967099671096711967129671396714967159671696717967189671996720967219672296723967249672596726967279672896729967309673196732967339673496735967369673796738967399674096741967429674396744967459674696747967489674996750967519675296753967549675596756967579675896759967609676196762967639676496765967669676796768967699677096771967729677396774967759677696777967789677996780967819678296783967849678596786967879678896789967909679196792967939679496795967969679796798967999680096801968029680396804968059680696807968089680996810968119681296813968149681596816968179681896819968209682196822968239682496825968269682796828968299683096831968329683396834968359683696837968389683996840968419684296843968449684596846968479684896849968509685196852968539685496855968569685796858968599686096861968629686396864968659686696867968689686996870968719687296873968749687596876968779687896879968809688196882968839688496885968869688796888968899689096891968929689396894968959689696897968989689996900969019690296903969049690596906969079690896909969109691196912969139691496915969169691796918969199692096921969229692396924969259692696927969289692996930969319693296933969349693596936969379693896939969409694196942969439694496945969469694796948969499695096951969529695396954969559695696957969589695996960969619696296963969649696596966969679696896969969709697196972969739697496975969769697796978969799698096981969829698396984969859698696987969889698996990969919699296993969949699596996969979699896999970009700197002970039700497005970069700797008970099701097011970129701397014970159701697017970189701997020970219702297023970249702597026970279702897029970309703197032970339703497035970369703797038970399704097041970429704397044970459704697047970489704997050970519705297053970549705597056970579705897059970609706197062970639706497065970669706797068970699707097071970729707397074970759707697077970789707997080970819708297083970849708597086970879708897089970909709197092970939709497095970969709797098970999710097101971029710397104971059710697107971089710997110971119711297113971149711597116971179711897119971209712197122971239712497125971269712797128971299713097131971329713397134971359713697137971389713997140971419714297143971449714597146971479714897149971509715197152971539715497155971569715797158971599716097161971629716397164971659716697167971689716997170971719717297173971749717597176971779717897179971809718197182971839718497185971869718797188971899719097191971929719397194971959719697197971989719997200972019720297203972049720597206972079720897209972109721197212972139721497215972169721797218972199722097221972229722397224972259722697227972289722997230972319723297233972349723597236972379723897239972409724197242972439724497245972469724797248972499725097251972529725397254972559725697257972589725997260972619726297263972649726597266972679726897269972709727197272972739727497275972769727797278972799728097281972829728397284972859728697287972889728997290972919729297293972949729597296972979729897299973009730197302973039730497305973069730797308973099731097311973129731397314973159731697317973189731997320973219732297323973249732597326973279732897329973309733197332973339733497335973369733797338973399734097341973429734397344973459734697347973489734997350973519735297353973549735597356973579735897359973609736197362973639736497365973669736797368973699737097371973729737397374973759737697377973789737997380973819738297383973849738597386973879738897389973909739197392973939739497395973969739797398973999740097401974029740397404974059740697407974089740997410974119741297413974149741597416974179741897419974209742197422974239742497425974269742797428974299743097431974329743397434974359743697437974389743997440974419744297443974449744597446974479744897449974509745197452974539745497455974569745797458974599746097461974629746397464974659746697467974689746997470974719747297473974749747597476974779747897479974809748197482974839748497485974869748797488974899749097491974929749397494974959749697497974989749997500975019750297503975049750597506975079750897509975109751197512975139751497515975169751797518975199752097521975229752397524975259752697527975289752997530975319753297533975349753597536975379753897539975409754197542975439754497545975469754797548975499755097551975529755397554975559755697557975589755997560975619756297563975649756597566975679756897569975709757197572975739757497575975769757797578975799758097581975829758397584975859758697587975889758997590975919759297593975949759597596975979759897599976009760197602976039760497605976069760797608976099761097611976129761397614976159761697617976189761997620976219762297623976249762597626976279762897629976309763197632976339763497635976369763797638976399764097641976429764397644976459764697647976489764997650976519765297653976549765597656976579765897659976609766197662976639766497665976669766797668976699767097671976729767397674976759767697677976789767997680976819768297683976849768597686976879768897689976909769197692976939769497695976969769797698976999770097701977029770397704977059770697707977089770997710977119771297713977149771597716977179771897719977209772197722977239772497725977269772797728977299773097731977329773397734977359773697737977389773997740977419774297743977449774597746977479774897749977509775197752977539775497755977569775797758977599776097761977629776397764977659776697767977689776997770977719777297773977749777597776977779777897779977809778197782977839778497785977869778797788977899779097791977929779397794977959779697797977989779997800978019780297803978049780597806978079780897809978109781197812978139781497815978169781797818978199782097821978229782397824978259782697827978289782997830978319783297833978349783597836978379783897839978409784197842978439784497845978469784797848978499785097851978529785397854978559785697857978589785997860978619786297863978649786597866978679786897869978709787197872978739787497875978769787797878978799788097881978829788397884978859788697887978889788997890978919789297893978949789597896978979789897899979009790197902979039790497905979069790797908979099791097911979129791397914979159791697917979189791997920979219792297923979249792597926979279792897929979309793197932979339793497935979369793797938979399794097941979429794397944979459794697947979489794997950979519795297953979549795597956979579795897959979609796197962979639796497965979669796797968979699797097971979729797397974979759797697977979789797997980979819798297983979849798597986979879798897989979909799197992979939799497995979969799797998979999800098001980029800398004980059800698007980089800998010980119801298013980149801598016980179801898019980209802198022980239802498025980269802798028980299803098031980329803398034980359803698037980389803998040980419804298043980449804598046980479804898049980509805198052980539805498055980569805798058980599806098061980629806398064980659806698067980689806998070980719807298073980749807598076980779807898079980809808198082980839808498085980869808798088980899809098091980929809398094980959809698097980989809998100981019810298103981049810598106981079810898109981109811198112981139811498115981169811798118981199812098121981229812398124981259812698127981289812998130981319813298133981349813598136981379813898139981409814198142981439814498145981469814798148981499815098151981529815398154981559815698157981589815998160981619816298163981649816598166981679816898169981709817198172981739817498175981769817798178981799818098181981829818398184981859818698187981889818998190981919819298193981949819598196981979819898199982009820198202982039820498205982069820798208982099821098211982129821398214982159821698217982189821998220982219822298223982249822598226982279822898229982309823198232982339823498235982369823798238982399824098241982429824398244982459824698247982489824998250982519825298253982549825598256982579825898259982609826198262982639826498265982669826798268982699827098271982729827398274982759827698277982789827998280982819828298283982849828598286982879828898289982909829198292982939829498295982969829798298982999830098301983029830398304983059830698307983089830998310983119831298313983149831598316983179831898319983209832198322983239832498325983269832798328983299833098331983329833398334983359833698337983389833998340983419834298343983449834598346983479834898349983509835198352983539835498355983569835798358983599836098361983629836398364983659836698367983689836998370983719837298373983749837598376983779837898379983809838198382983839838498385983869838798388983899839098391983929839398394983959839698397983989839998400984019840298403984049840598406984079840898409984109841198412984139841498415984169841798418984199842098421984229842398424984259842698427984289842998430984319843298433984349843598436984379843898439984409844198442984439844498445984469844798448984499845098451984529845398454984559845698457984589845998460984619846298463984649846598466984679846898469984709847198472984739847498475984769847798478984799848098481984829848398484984859848698487984889848998490984919849298493984949849598496984979849898499985009850198502985039850498505985069850798508985099851098511985129851398514985159851698517985189851998520985219852298523985249852598526985279852898529985309853198532985339853498535985369853798538985399854098541985429854398544985459854698547985489854998550985519855298553985549855598556985579855898559985609856198562985639856498565985669856798568985699857098571985729857398574985759857698577985789857998580985819858298583985849858598586985879858898589985909859198592985939859498595985969859798598985999860098601986029860398604986059860698607986089860998610986119861298613986149861598616986179861898619986209862198622986239862498625986269862798628986299863098631986329863398634986359863698637986389863998640986419864298643986449864598646986479864898649986509865198652986539865498655986569865798658986599866098661986629866398664986659866698667986689866998670986719867298673986749867598676986779867898679986809868198682986839868498685986869868798688986899869098691986929869398694986959869698697986989869998700987019870298703987049870598706987079870898709987109871198712987139871498715987169871798718987199872098721987229872398724987259872698727987289872998730987319873298733987349873598736987379873898739987409874198742987439874498745987469874798748987499875098751987529875398754987559875698757987589875998760987619876298763987649876598766987679876898769987709877198772987739877498775987769877798778987799878098781987829878398784987859878698787987889878998790987919879298793987949879598796987979879898799988009880198802988039880498805988069880798808988099881098811988129881398814988159881698817988189881998820988219882298823988249882598826988279882898829988309883198832988339883498835988369883798838988399884098841988429884398844988459884698847988489884998850988519885298853988549885598856988579885898859988609886198862988639886498865988669886798868988699887098871988729887398874988759887698877988789887998880988819888298883988849888598886988879888898889988909889198892988939889498895988969889798898988999890098901989029890398904989059890698907989089890998910989119891298913989149891598916989179891898919989209892198922989239892498925989269892798928989299893098931989329893398934989359893698937989389893998940989419894298943989449894598946989479894898949989509895198952989539895498955989569895798958989599896098961989629896398964989659896698967989689896998970989719897298973989749897598976989779897898979989809898198982989839898498985989869898798988989899899098991989929899398994989959899698997989989899999000990019900299003990049900599006990079900899009990109901199012990139901499015990169901799018990199902099021990229902399024990259902699027990289902999030990319903299033990349903599036990379903899039990409904199042990439904499045990469904799048990499905099051990529905399054990559905699057990589905999060990619906299063990649906599066990679906899069990709907199072990739907499075990769907799078990799908099081990829908399084990859908699087990889908999090990919909299093990949909599096990979909899099991009910199102991039910499105991069910799108991099911099111991129911399114991159911699117991189911999120991219912299123991249912599126991279912899129991309913199132991339913499135991369913799138991399914099141991429914399144991459914699147991489914999150991519915299153991549915599156991579915899159991609916199162991639916499165991669916799168991699917099171991729917399174991759917699177991789917999180991819918299183991849918599186991879918899189991909919199192991939919499195991969919799198991999920099201992029920399204992059920699207992089920999210992119921299213992149921599216992179921899219992209922199222992239922499225992269922799228992299923099231992329923399234992359923699237992389923999240992419924299243992449924599246992479924899249992509925199252992539925499255992569925799258992599926099261992629926399264992659926699267992689926999270992719927299273992749927599276992779927899279992809928199282992839928499285992869928799288992899929099291992929929399294992959929699297992989929999300993019930299303993049930599306993079930899309993109931199312993139931499315993169931799318993199932099321993229932399324993259932699327993289932999330993319933299333993349933599336993379933899339993409934199342993439934499345993469934799348993499935099351993529935399354993559935699357993589935999360993619936299363993649936599366993679936899369993709937199372993739937499375993769937799378993799938099381993829938399384993859938699387993889938999390993919939299393993949939599396993979939899399994009940199402994039940499405994069940799408994099941099411994129941399414994159941699417994189941999420994219942299423994249942599426994279942899429994309943199432994339943499435994369943799438994399944099441994429944399444994459944699447994489944999450994519945299453994549945599456994579945899459994609946199462994639946499465994669946799468994699947099471994729947399474994759947699477994789947999480994819948299483994849948599486994879948899489994909949199492994939949499495994969949799498994999950099501995029950399504995059950699507995089950999510995119951299513995149951599516995179951899519995209952199522995239952499525995269952799528995299953099531995329953399534995359953699537995389953999540995419954299543995449954599546995479954899549995509955199552995539955499555995569955799558995599956099561995629956399564995659956699567995689956999570995719957299573995749957599576995779957899579995809958199582995839958499585995869958799588995899959099591995929959399594995959959699597995989959999600996019960299603996049960599606996079960899609996109961199612996139961499615996169961799618996199962099621996229962399624996259962699627996289962999630996319963299633996349963599636996379963899639996409964199642996439964499645996469964799648996499965099651996529965399654996559965699657996589965999660996619966299663996649966599666996679966899669996709967199672996739967499675996769967799678996799968099681996829968399684996859968699687996889968999690996919969299693996949969599696996979969899699997009970199702997039970499705997069970799708997099971099711997129971399714997159971699717997189971999720997219972299723997249972599726997279972899729997309973199732997339973499735997369973799738997399974099741997429974399744997459974699747997489974999750997519975299753997549975599756997579975899759997609976199762997639976499765997669976799768997699977099771997729977399774997759977699777997789977999780997819978299783997849978599786997879978899789997909979199792997939979499795997969979799798997999980099801998029980399804998059980699807998089980999810998119981299813998149981599816998179981899819998209982199822998239982499825998269982799828998299983099831998329983399834998359983699837998389983999840998419984299843998449984599846998479984899849998509985199852998539985499855998569985799858998599986099861998629986399864998659986699867998689986999870998719987299873998749987599876998779987899879998809988199882998839988499885998869988799888998899989099891998929989399894998959989699897998989989999900999019990299903999049990599906999079990899909999109991199912999139991499915999169991799918999199992099921999229992399924999259992699927999289992999930999319993299933999349993599936999379993899939999409994199942999439994499945999469994799948999499995099951999529995399954999559995699957999589995999960999619996299963999649996599966999679996899969999709997199972999739997499975999769997799978999799998099981999829998399984999859998699987999889998999990999919999299993999949999599996999979999899999100000100001100002100003100004100005100006100007100008100009100010100011100012100013100014100015100016100017100018100019100020100021100022100023100024100025100026100027100028100029100030100031100032100033100034100035100036100037100038100039100040100041100042100043100044100045100046100047100048100049100050100051100052100053100054100055100056100057100058100059100060100061100062100063100064100065100066100067100068100069100070100071100072100073100074100075100076100077100078100079100080100081100082100083100084100085100086100087100088100089100090100091100092100093100094100095100096100097100098100099100100100101100102100103100104100105100106100107100108100109100110100111100112100113100114100115100116100117100118100119100120100121100122100123100124100125100126100127100128100129100130100131100132100133100134100135100136100137100138100139100140100141100142100143100144100145100146100147100148100149100150100151100152100153100154100155100156100157100158100159100160100161100162100163100164100165100166100167100168100169100170100171100172100173100174100175100176100177100178100179100180100181100182100183100184100185100186100187100188100189100190100191100192100193100194100195100196100197100198100199100200100201100202100203100204100205100206100207100208100209100210100211100212100213100214100215100216100217100218100219100220100221100222100223100224100225100226100227100228100229100230100231100232100233100234100235100236100237100238100239100240100241100242100243100244100245100246100247100248100249100250100251100252100253100254100255100256100257100258100259100260100261100262100263100264100265100266100267100268100269100270100271100272100273100274100275100276100277100278100279100280100281100282100283100284100285100286100287100288100289100290100291100292100293100294100295100296100297100298100299100300100301100302100303100304100305100306100307100308100309100310100311100312100313100314100315100316100317100318100319100320100321100322100323100324100325100326100327100328100329100330100331100332100333100334100335100336100337100338100339100340100341100342100343100344100345100346100347100348100349100350100351100352100353100354100355100356100357100358100359100360100361100362100363100364100365100366100367100368100369100370100371100372100373100374100375100376100377100378100379100380100381100382100383100384100385100386100387100388100389100390100391100392100393100394100395100396100397100398100399100400100401100402100403100404100405100406100407100408100409100410100411100412100413100414100415100416100417100418100419100420100421100422100423100424100425100426100427100428100429100430100431100432100433100434100435100436100437100438100439100440100441100442100443100444100445100446100447100448100449100450100451100452100453100454100455100456100457100458100459100460100461100462100463100464100465100466100467100468100469100470100471100472100473100474100475100476100477100478100479100480100481100482100483100484100485100486100487100488100489100490100491100492100493100494100495100496100497100498100499100500100501100502100503100504100505100506100507100508100509100510100511100512100513100514100515100516100517100518100519100520100521100522100523100524100525100526100527100528100529100530100531100532100533100534100535100536100537100538100539100540100541100542100543100544100545100546100547100548100549100550100551100552100553100554100555100556100557100558100559100560100561100562100563100564100565100566100567100568100569100570100571100572100573100574100575100576100577100578100579100580100581100582100583100584100585100586100587100588100589100590100591100592100593100594100595100596100597100598100599100600100601100602100603100604100605100606100607100608100609100610100611100612100613100614100615100616100617100618100619100620100621100622100623100624100625100626100627100628100629100630100631100632100633100634100635100636100637100638100639100640100641100642100643100644100645100646100647100648100649100650100651100652100653100654100655100656100657100658100659100660100661100662100663100664100665100666100667100668100669100670100671100672100673100674100675100676100677100678100679100680100681100682100683100684100685100686100687100688100689100690100691100692100693100694100695100696100697100698100699100700100701100702100703100704100705100706100707100708100709100710100711100712100713100714100715100716100717100718100719100720100721100722100723100724100725100726100727100728100729100730100731100732100733100734100735100736100737100738100739100740100741100742100743100744100745100746100747100748100749100750100751100752100753100754100755100756100757100758100759100760100761100762100763100764100765100766100767100768100769100770100771100772100773100774100775100776100777100778100779100780100781100782100783100784100785100786100787100788100789100790100791100792100793100794100795100796100797100798100799100800100801100802100803100804100805100806100807100808100809100810100811100812100813100814100815100816100817100818100819100820100821100822100823100824100825100826100827100828100829100830100831100832100833100834100835100836100837100838100839100840100841100842100843100844100845100846100847100848100849100850100851100852100853100854100855100856100857100858100859100860100861100862100863100864100865100866100867100868100869100870100871100872100873100874100875100876100877100878100879100880100881100882100883100884100885100886100887100888100889100890100891100892100893100894100895100896100897100898100899100900100901100902100903100904100905100906100907100908100909100910100911100912100913100914100915100916100917100918100919100920100921100922100923100924100925100926100927100928100929100930100931100932100933100934100935100936100937100938100939100940100941100942100943100944100945100946100947100948100949100950100951100952100953100954100955100956100957100958100959100960100961100962100963100964100965100966100967100968100969100970100971100972100973100974100975100976100977100978100979100980100981100982100983100984100985100986100987100988100989100990100991100992100993100994100995100996100997100998100999101000101001101002101003101004101005101006101007101008101009101010101011101012101013101014101015101016101017101018101019101020101021101022101023101024101025101026101027101028101029101030101031101032101033101034101035101036101037101038101039101040101041101042101043101044101045101046101047101048101049101050101051101052101053101054101055101056101057101058101059101060101061101062101063101064101065101066101067101068101069101070101071101072101073101074101075101076101077101078101079101080101081101082101083101084101085101086101087101088101089101090101091101092101093101094101095101096101097101098101099101100101101101102101103101104101105101106101107101108101109101110101111101112101113101114101115101116101117101118101119101120101121101122101123101124101125101126101127101128101129101130101131101132101133101134101135101136101137101138101139101140101141101142101143101144101145101146101147101148101149101150101151101152101153101154101155101156101157101158101159101160101161101162101163101164101165101166101167101168101169101170101171101172101173101174101175101176101177101178101179101180101181101182101183101184101185101186101187101188101189101190101191101192101193101194101195101196101197101198101199101200101201101202101203101204101205101206101207101208101209101210101211101212101213101214101215101216101217101218101219101220101221101222101223101224101225101226101227101228101229101230101231101232101233101234101235101236101237101238101239101240101241101242101243101244101245101246101247101248101249101250101251101252101253101254101255101256101257101258101259101260101261101262101263101264101265101266101267101268101269101270101271101272101273101274101275101276101277101278101279101280101281101282101283101284101285101286101287101288101289101290101291101292101293101294101295101296101297101298101299101300101301101302101303101304101305101306101307101308101309101310101311101312101313101314101315101316101317101318101319101320101321101322101323101324101325101326101327101328101329101330101331101332101333101334101335101336101337101338101339101340101341101342101343101344101345101346101347101348101349101350101351101352101353101354101355101356101357101358101359101360101361101362101363101364101365101366101367101368101369101370101371101372101373101374101375101376101377101378101379101380101381101382101383101384101385101386101387101388101389101390101391101392101393101394101395101396101397101398101399101400101401101402101403101404101405101406101407101408101409101410101411101412101413101414101415101416101417101418101419101420101421101422101423101424101425101426101427101428101429101430101431101432101433101434101435101436101437101438101439101440101441101442101443101444101445101446101447101448101449101450101451101452101453101454101455101456101457101458101459101460101461101462101463101464101465101466101467101468101469101470101471101472101473101474101475101476101477101478101479101480101481101482101483101484101485101486101487101488101489101490101491101492101493101494101495101496101497101498101499101500101501101502101503101504101505101506101507101508101509101510101511101512101513101514101515101516101517101518101519101520101521101522101523101524101525101526101527101528101529101530101531101532101533101534101535101536101537101538101539101540101541101542101543101544101545101546101547101548101549101550101551101552101553101554101555101556101557101558101559101560101561101562101563101564101565101566101567101568101569101570101571101572101573101574101575101576101577101578101579101580101581101582101583101584101585101586101587101588101589101590101591101592101593101594101595101596101597101598101599101600101601101602101603101604101605101606101607101608101609101610101611101612101613101614101615101616101617101618101619101620101621101622101623101624101625101626101627101628101629101630101631101632101633101634101635101636101637101638101639101640101641101642101643101644101645101646101647101648101649101650101651101652101653101654101655101656101657101658101659101660101661101662101663101664101665101666101667101668101669101670101671101672101673101674101675101676101677101678101679101680101681101682101683101684101685101686101687101688101689101690101691101692101693101694101695101696101697101698101699101700101701101702101703101704101705101706101707101708101709101710101711101712101713101714101715101716101717101718101719101720101721101722101723101724101725101726101727101728101729101730101731101732101733101734101735101736101737101738101739101740101741101742101743101744101745101746101747101748101749101750101751101752101753101754101755101756101757101758101759101760101761101762101763101764101765101766101767101768101769101770101771101772101773101774101775101776101777101778101779101780101781101782101783101784101785101786101787101788101789101790101791101792101793101794101795101796101797101798101799101800101801101802101803101804101805101806101807101808101809101810101811101812101813101814101815101816101817101818101819101820101821101822101823101824101825101826101827101828101829101830101831101832101833101834101835101836101837101838101839101840101841101842101843101844101845101846101847101848101849101850101851101852101853101854101855101856101857101858101859101860101861101862101863101864101865101866101867101868101869101870101871101872101873101874101875101876101877101878101879101880101881101882101883101884101885101886101887101888101889101890101891101892101893101894101895101896101897101898101899101900101901101902101903101904101905101906101907101908101909101910101911101912101913101914101915101916101917101918101919101920101921101922101923101924101925101926101927101928101929101930101931101932101933101934101935101936101937101938101939101940101941101942101943101944101945101946101947101948101949101950101951101952101953101954101955101956101957101958101959101960101961101962101963101964101965101966101967101968101969101970101971101972101973101974101975101976101977101978101979101980101981101982101983101984101985101986101987101988101989101990101991101992101993101994101995101996101997101998101999102000102001102002102003102004102005102006102007102008102009102010102011102012102013102014102015102016102017102018102019102020102021102022102023102024102025102026102027102028102029102030102031102032102033102034102035102036102037102038102039102040102041102042102043102044102045102046102047102048102049102050102051102052102053102054102055102056102057102058102059102060102061102062102063102064102065102066102067102068102069102070102071102072102073102074102075102076102077102078102079102080102081102082102083102084102085102086102087102088102089102090102091102092102093102094102095102096102097102098102099102100102101102102102103102104102105102106102107102108102109102110102111102112102113102114102115102116102117102118102119102120102121102122102123102124102125102126102127102128102129102130102131102132102133102134102135102136102137102138102139102140102141102142102143102144102145102146102147102148102149102150102151102152102153102154102155102156102157102158102159102160102161102162102163102164102165102166102167102168102169102170102171102172102173102174102175102176102177102178102179102180102181102182102183102184102185102186102187102188102189102190102191102192102193102194102195102196102197102198102199102200102201102202102203102204102205102206102207102208102209102210102211102212102213102214102215102216102217102218102219102220102221102222102223102224102225102226102227102228102229102230102231102232102233102234102235102236102237102238102239102240102241102242102243102244102245102246102247102248102249102250102251102252102253102254102255102256102257102258102259102260102261102262102263102264102265102266102267102268102269102270102271102272102273102274102275102276102277102278102279102280102281102282102283102284102285102286102287102288102289102290102291102292102293102294102295102296102297102298102299102300102301102302102303102304102305102306102307102308102309102310102311102312102313102314102315102316102317102318102319102320102321102322102323102324102325102326102327102328102329102330102331102332102333102334102335102336102337102338102339102340102341102342102343102344102345102346102347102348102349102350102351102352102353102354102355102356102357102358102359102360102361102362102363102364102365102366102367102368102369102370102371102372102373102374102375102376102377102378102379102380102381102382102383102384102385102386102387102388102389102390102391102392102393102394102395102396102397102398102399102400102401102402102403102404102405102406102407102408102409102410102411102412102413102414102415102416102417102418102419102420102421102422102423102424102425102426102427102428102429102430102431102432102433102434102435102436102437102438102439102440102441102442102443102444102445102446102447102448102449102450102451102452102453102454102455102456102457102458102459102460102461102462102463102464102465102466102467102468102469102470102471102472102473102474102475102476102477102478102479102480102481102482102483102484102485102486102487102488102489102490102491102492102493102494102495102496102497102498102499102500102501102502102503102504102505102506102507102508102509102510102511102512102513102514102515102516102517102518102519102520102521102522102523102524102525102526102527102528102529102530102531102532102533102534102535102536102537102538102539102540102541102542102543102544102545102546102547102548102549102550102551102552102553102554102555102556102557102558102559102560102561102562102563102564102565102566102567102568102569102570102571102572102573102574102575102576102577102578102579102580102581102582102583102584102585102586102587102588102589102590102591102592102593102594102595102596102597102598102599102600102601102602102603102604102605102606102607102608102609102610102611102612102613102614102615102616102617102618102619102620102621102622102623102624102625102626102627102628102629102630102631102632102633102634102635102636102637102638102639102640102641102642102643102644102645102646102647102648102649102650102651102652102653102654102655102656102657102658102659102660102661102662102663102664102665102666102667102668102669102670102671102672102673102674102675102676102677102678102679102680102681102682102683102684102685102686102687102688102689102690102691102692102693102694102695102696102697102698102699102700102701102702102703102704102705102706102707102708102709102710102711102712102713102714102715102716102717102718102719102720102721102722102723102724102725102726102727102728102729102730102731102732102733102734102735102736102737102738102739102740102741102742102743102744102745102746102747102748102749102750102751102752102753102754102755102756102757102758102759102760102761102762102763102764102765102766102767102768102769102770102771102772102773102774102775102776102777102778102779102780102781102782102783102784102785102786102787102788102789102790102791102792102793102794102795102796102797102798102799102800102801102802102803102804102805102806102807102808102809102810102811102812102813102814102815102816102817102818102819102820102821102822102823102824102825102826102827102828102829102830102831102832102833102834102835102836102837102838102839102840102841102842102843102844102845102846102847102848102849102850102851102852102853102854102855102856102857102858102859102860102861102862102863102864102865102866102867102868102869102870102871102872102873102874102875102876102877102878102879102880102881102882102883102884102885102886102887102888102889102890102891102892102893102894102895102896102897102898102899102900102901102902102903102904102905102906102907102908102909102910102911102912102913102914102915102916102917102918102919102920102921102922102923102924102925102926102927102928102929102930102931102932102933102934102935102936102937102938102939102940102941102942102943102944102945102946102947102948102949102950102951102952102953102954102955102956102957102958102959102960102961102962102963102964102965102966102967102968102969102970102971102972102973102974102975102976102977102978102979102980102981102982102983102984102985102986102987102988102989102990102991102992102993102994102995102996102997102998102999103000103001103002103003103004103005103006103007103008103009103010103011103012103013103014103015103016103017103018103019103020103021103022103023103024103025103026103027103028103029103030103031103032103033103034103035103036103037103038103039103040103041103042103043103044103045103046103047103048103049103050103051103052103053103054103055103056103057103058103059103060103061103062103063103064103065103066103067103068103069103070103071103072103073103074103075103076103077103078103079103080103081103082103083103084103085103086103087103088103089103090103091103092103093103094103095103096103097103098103099103100103101103102103103103104103105103106103107103108103109103110103111103112103113103114103115103116103117103118103119103120103121103122103123103124103125103126103127103128103129103130103131103132103133103134103135103136103137103138103139103140103141103142103143103144103145103146103147103148103149103150103151103152103153103154103155103156103157103158103159103160103161103162103163103164103165103166103167103168103169103170103171103172103173103174103175103176103177103178103179103180103181103182103183103184103185103186103187103188103189103190103191103192103193103194103195103196103197103198103199103200103201103202103203103204103205103206103207103208103209103210103211103212103213103214103215103216103217103218103219103220103221103222103223103224103225103226103227103228103229103230103231103232103233103234103235103236103237103238103239103240103241103242103243103244103245103246103247103248103249103250103251103252103253103254103255103256103257103258103259103260103261103262103263103264103265103266103267103268103269103270103271103272103273103274103275103276103277103278103279103280103281103282103283103284103285103286103287103288103289103290103291103292103293103294103295103296103297103298103299103300103301103302103303103304103305103306103307103308103309103310103311103312103313103314103315103316103317103318103319103320103321103322103323103324103325103326103327103328103329103330103331103332103333103334103335103336103337103338103339103340103341103342103343103344103345103346103347103348103349103350103351103352103353103354103355103356103357103358103359103360103361103362103363103364103365103366103367103368103369103370103371103372103373103374103375103376103377103378103379103380103381103382103383103384103385103386103387103388103389103390103391103392103393103394103395103396103397103398103399103400103401103402103403103404103405103406103407103408103409103410103411103412103413103414103415103416103417103418103419103420103421103422103423103424103425103426103427103428103429103430103431103432103433103434103435103436103437103438103439103440103441103442103443103444103445103446103447103448103449103450103451103452103453103454103455103456103457103458103459103460103461103462103463103464103465103466103467103468103469103470103471103472103473103474103475103476103477103478103479103480103481103482103483103484103485103486103487103488103489103490103491103492103493103494103495103496103497103498103499103500103501103502103503103504103505103506103507103508103509103510103511103512103513103514103515103516103517103518103519103520103521103522103523103524103525103526103527103528103529103530103531103532103533103534103535103536103537103538103539103540103541103542103543103544103545103546103547103548103549103550103551103552103553103554103555103556103557103558103559103560103561103562103563103564103565103566103567103568103569103570103571103572103573103574103575103576103577103578103579103580103581103582103583103584103585103586103587103588103589103590103591103592103593103594103595103596103597103598103599103600103601103602103603103604103605103606103607103608103609103610103611103612103613103614103615103616103617103618103619103620103621103622103623103624103625103626103627103628103629103630103631103632103633103634103635103636103637103638103639103640103641103642103643103644103645103646103647103648103649103650103651103652103653103654103655103656103657103658103659103660103661103662103663103664103665103666103667103668103669103670103671103672103673103674103675103676103677103678103679103680103681103682103683103684103685103686103687103688103689103690103691103692103693103694103695103696103697103698103699103700103701103702103703103704103705103706103707103708103709103710103711103712103713103714103715103716103717103718103719103720103721103722103723103724103725103726103727103728103729103730103731103732103733103734103735103736103737103738103739103740103741103742103743103744103745103746103747103748103749103750103751103752103753103754103755103756103757103758103759103760103761103762103763103764103765103766103767103768103769103770103771103772103773103774103775103776103777103778103779103780103781103782103783103784103785103786103787103788103789103790103791103792103793103794103795103796103797103798103799103800103801103802103803103804103805103806103807103808103809103810103811103812103813103814103815103816103817103818103819103820103821103822103823103824103825103826103827103828103829103830103831103832103833103834103835103836103837103838103839103840103841103842103843103844103845103846103847103848103849103850103851103852103853103854103855103856103857103858103859103860103861103862103863103864103865103866103867103868103869103870103871103872103873103874103875103876103877103878103879103880103881103882103883103884103885103886103887103888103889103890103891103892103893103894103895103896103897103898103899103900103901103902103903103904103905103906103907103908103909103910103911103912103913103914103915103916103917103918103919103920103921103922103923103924103925103926103927103928103929103930103931103932103933103934103935103936103937103938103939103940103941103942103943103944103945103946103947103948103949103950103951103952103953103954103955103956103957103958103959103960103961103962103963103964103965103966103967103968103969103970103971103972103973103974103975103976103977103978103979103980103981103982103983103984103985103986103987103988103989103990103991103992103993103994103995103996103997103998103999104000104001104002104003104004104005104006104007104008104009104010104011104012104013104014104015104016104017104018104019104020104021104022104023104024104025104026104027104028104029104030104031104032104033104034104035104036104037104038104039104040104041104042104043104044104045104046104047104048104049104050104051104052104053104054104055104056104057104058104059104060104061104062104063104064104065104066104067104068104069104070104071104072104073104074104075104076104077104078104079104080104081104082104083104084104085104086104087104088104089104090104091104092104093104094104095104096104097104098104099104100104101104102104103104104104105104106104107104108104109104110104111104112104113104114104115104116104117104118104119104120104121104122104123104124104125104126104127104128104129104130104131104132104133104134104135104136104137104138104139104140104141104142104143104144104145104146104147104148104149104150104151104152104153104154104155104156104157104158104159104160104161104162104163104164104165104166104167104168104169104170104171104172104173104174104175104176104177104178104179104180104181104182104183104184104185104186104187104188104189104190104191104192104193104194104195104196104197104198104199104200104201104202104203104204104205104206104207104208104209104210104211104212104213104214104215104216104217104218104219104220104221104222104223104224104225104226104227104228104229104230104231104232104233104234104235104236104237104238104239104240104241104242104243104244104245104246104247104248104249104250104251104252104253104254104255104256104257104258104259104260104261104262104263104264104265104266104267104268104269104270104271104272104273104274104275104276104277104278104279104280104281104282104283104284104285104286104287104288104289104290104291104292104293104294104295104296104297104298104299104300104301104302104303104304104305104306104307104308104309104310104311104312104313104314104315104316104317104318104319104320104321104322104323104324104325104326104327104328104329104330104331104332104333104334104335104336104337104338104339104340104341104342104343104344104345104346104347104348104349104350104351104352104353104354104355104356104357104358104359104360104361104362104363104364104365104366104367104368104369104370104371104372104373104374104375104376104377104378104379104380104381104382104383104384104385104386104387104388104389104390104391104392104393104394104395104396104397104398104399104400104401104402104403104404104405104406104407104408104409104410104411104412104413104414104415104416104417104418104419104420104421104422104423104424104425104426104427104428104429104430104431104432104433104434104435104436104437104438104439104440104441104442104443104444104445104446104447104448104449104450104451104452104453104454104455104456104457104458104459104460104461104462104463104464104465104466104467104468104469104470104471104472104473104474104475104476104477104478104479104480104481104482104483104484104485104486104487104488104489104490104491104492104493104494104495104496104497104498104499104500104501104502104503104504104505104506104507104508104509104510104511104512104513104514104515104516104517104518104519104520104521104522104523104524104525104526104527104528104529104530104531104532104533104534104535104536104537104538104539104540104541104542104543104544104545104546104547104548104549104550104551104552104553104554104555104556104557104558104559104560104561104562104563104564104565104566104567104568104569104570104571104572104573104574104575104576104577104578104579104580104581104582104583104584104585104586104587104588104589104590104591104592104593104594104595104596104597104598104599104600104601104602104603104604104605104606104607104608104609104610104611104612104613104614104615104616104617104618104619104620104621104622104623104624104625104626104627104628104629104630104631104632104633104634104635104636104637104638104639104640104641104642104643104644104645104646104647104648104649104650104651104652104653104654104655104656104657104658104659104660104661104662104663104664104665104666104667104668104669104670104671104672104673104674104675104676104677104678104679104680104681104682104683104684104685104686104687104688104689104690104691104692104693104694104695104696104697104698104699104700104701104702104703104704104705104706104707104708104709104710104711104712104713104714104715104716104717104718104719104720104721104722104723104724104725104726104727104728104729104730104731104732104733104734104735104736104737104738104739104740104741104742104743104744104745104746104747104748104749104750104751104752104753104754104755104756104757104758104759104760104761104762104763104764104765104766104767104768104769104770104771104772104773104774104775104776104777104778104779104780104781104782104783104784104785104786104787104788104789104790104791104792104793104794104795104796104797104798104799104800104801104802104803104804104805104806104807104808104809104810104811104812104813104814104815104816104817104818104819104820104821104822104823104824104825104826104827104828104829104830104831104832104833104834104835104836104837104838104839104840104841104842104843104844104845104846104847104848104849104850104851104852104853104854104855104856104857104858104859104860104861104862104863104864104865104866104867104868104869104870104871104872104873104874104875104876104877104878104879104880104881104882104883104884104885104886104887104888104889104890104891104892104893104894104895104896104897104898104899104900104901104902104903104904104905104906104907104908104909104910104911104912104913104914104915104916104917104918104919104920104921104922104923104924104925104926104927104928104929104930104931104932104933104934104935104936104937104938104939104940104941104942104943104944104945104946104947104948104949104950104951104952104953104954104955104956104957104958104959104960104961104962104963104964104965104966104967104968104969104970104971104972104973104974104975104976104977104978104979104980104981104982104983104984104985104986104987104988104989104990104991104992104993104994104995104996104997104998104999105000105001105002105003105004105005105006105007105008105009105010105011105012105013105014105015105016105017105018105019105020105021105022105023105024105025105026105027105028105029105030105031105032105033105034105035105036105037105038105039105040105041105042105043105044105045105046105047105048105049105050105051105052105053105054105055105056105057105058105059105060105061105062105063105064105065105066105067105068105069105070105071105072105073105074105075105076105077105078105079105080105081105082105083105084105085105086105087105088105089105090105091105092105093105094105095105096105097105098105099105100105101105102105103105104105105105106105107105108105109105110105111105112105113105114105115105116105117105118105119105120105121105122105123105124105125105126105127105128105129105130105131105132105133105134105135105136105137105138105139105140105141105142105143105144105145105146105147105148105149105150105151105152105153105154105155105156105157105158105159105160105161105162105163105164105165105166105167105168105169105170105171105172105173105174105175105176105177105178105179105180105181105182105183105184105185105186105187105188105189105190105191105192105193105194105195105196105197105198105199105200105201105202105203105204105205105206105207105208105209105210105211105212105213105214105215105216105217105218105219105220105221105222105223105224105225105226105227105228105229105230105231105232105233105234105235105236105237105238105239105240105241105242105243105244105245105246105247105248105249105250105251105252105253105254105255105256105257105258105259105260105261105262105263105264105265105266105267105268105269105270105271105272105273105274105275105276105277105278105279105280105281105282105283105284105285105286105287105288105289105290105291105292105293105294105295105296105297105298105299105300105301105302105303105304105305105306105307105308105309105310105311105312105313105314105315105316105317105318105319105320105321105322105323105324105325105326105327105328105329105330105331105332105333105334105335105336105337105338105339105340105341105342105343105344105345105346105347105348105349105350105351105352105353105354105355105356105357105358105359105360105361105362105363105364105365105366105367105368105369105370105371105372105373105374105375105376105377105378105379105380105381105382105383105384105385105386105387105388105389105390105391105392105393105394105395105396105397105398105399105400105401105402105403105404105405105406105407105408105409105410105411105412105413105414105415105416105417105418105419105420105421105422105423105424105425105426105427105428105429105430105431105432105433105434105435105436105437105438105439105440105441105442105443105444105445105446105447105448105449105450105451105452105453105454105455105456105457105458105459105460105461105462105463105464105465105466105467105468105469105470105471105472105473105474105475105476105477105478105479105480105481105482105483105484105485105486105487105488105489105490105491105492105493105494105495105496105497105498105499105500105501105502105503105504105505105506105507105508105509105510105511105512105513105514105515105516105517105518105519105520105521105522105523105524105525105526105527105528105529105530105531105532105533105534105535105536105537105538105539105540105541105542105543105544105545105546105547105548105549105550105551105552105553105554105555105556105557105558105559105560105561105562105563105564105565105566105567105568105569105570105571105572105573105574105575105576105577105578105579105580105581105582105583105584105585105586105587105588105589105590105591105592105593105594105595105596105597105598105599105600105601105602105603105604105605105606105607105608105609105610105611105612105613105614105615105616105617105618105619105620105621105622105623105624105625105626105627105628105629105630105631105632105633105634105635105636105637105638105639105640105641105642105643105644105645105646105647105648105649105650105651105652105653105654105655105656105657105658105659105660105661105662105663105664105665105666105667105668105669105670105671105672105673105674105675105676105677105678105679105680105681105682105683105684105685105686105687105688105689105690105691105692105693105694105695105696105697105698105699105700105701105702105703105704105705105706105707105708105709105710105711105712105713105714105715105716105717105718105719105720105721105722105723105724105725105726105727105728105729105730105731105732105733105734105735105736105737105738105739105740105741105742105743105744105745105746105747105748105749105750105751105752105753105754105755105756105757105758105759105760105761105762105763105764105765105766105767105768105769105770105771105772105773105774105775105776105777105778105779105780105781105782105783105784105785105786105787105788105789105790105791105792105793105794105795105796105797105798105799105800105801105802105803105804105805105806105807105808105809105810105811105812105813105814105815105816105817105818105819105820105821105822105823105824105825105826105827105828105829105830105831105832105833105834105835105836105837105838105839105840105841105842105843105844105845105846105847105848105849105850105851105852105853105854105855105856105857105858105859105860105861105862105863105864105865105866105867105868105869105870105871105872105873105874105875105876105877105878105879105880105881105882105883105884105885105886105887105888105889105890105891105892105893105894105895105896105897105898105899105900105901105902105903105904105905105906105907105908105909105910105911105912105913105914105915105916105917105918105919105920105921105922105923105924105925105926105927105928105929105930105931105932105933105934105935105936105937105938105939105940105941105942105943105944105945105946105947105948105949105950105951105952105953105954105955105956105957105958105959105960105961105962105963105964105965105966105967105968105969105970105971105972105973105974105975105976105977105978105979105980105981105982105983105984105985105986105987105988105989105990105991105992105993105994105995105996105997105998105999106000106001106002106003106004106005106006106007106008106009106010106011106012106013106014106015106016106017106018106019106020106021106022106023106024106025106026106027106028106029106030106031106032106033106034106035106036106037106038106039106040106041106042106043106044106045106046106047106048106049106050106051106052106053106054106055106056106057106058106059106060106061106062106063106064106065106066106067106068106069106070106071106072106073106074106075106076106077106078106079106080106081106082106083106084106085106086106087106088106089106090106091106092106093106094106095106096106097106098106099106100106101106102106103106104106105106106106107106108106109106110106111106112106113106114106115106116106117106118106119106120106121106122106123106124106125106126106127106128106129106130106131106132106133106134106135106136106137106138106139106140106141106142106143106144106145106146106147106148106149106150106151106152106153106154106155106156106157106158106159106160106161106162106163106164106165106166106167106168106169106170106171106172106173106174106175106176106177106178106179106180106181106182106183106184106185106186106187106188106189106190106191106192106193106194106195106196106197106198106199106200106201106202106203106204106205106206106207106208106209106210106211106212106213106214106215106216106217106218106219106220106221106222106223106224106225106226106227106228106229106230106231106232106233106234106235106236106237106238106239106240106241106242106243106244106245106246106247106248106249106250106251106252106253106254106255106256106257106258106259106260106261106262106263106264106265106266106267106268106269106270106271106272106273106274106275106276106277106278106279106280106281106282106283106284106285106286106287106288106289106290106291106292106293106294106295106296106297106298106299106300106301106302106303106304106305106306106307106308106309106310106311106312106313106314106315106316106317106318106319106320106321106322106323106324106325106326106327106328106329106330106331106332106333106334106335106336106337106338106339106340106341106342106343106344106345106346106347106348106349106350106351106352106353106354106355106356106357106358106359106360106361106362106363106364106365106366106367106368106369106370106371106372106373106374106375106376106377106378106379106380106381106382106383106384106385106386106387106388106389106390106391106392106393106394106395106396106397106398106399106400106401106402106403106404106405106406106407106408106409106410106411106412106413106414106415106416106417106418106419106420106421106422106423106424106425106426106427106428106429106430106431106432106433106434106435106436106437106438106439106440106441106442106443106444106445106446106447106448106449106450106451106452106453106454106455106456106457106458106459106460106461106462106463106464106465106466106467106468106469106470106471106472106473106474106475106476106477106478106479106480106481106482106483106484106485106486106487106488106489106490106491106492106493106494106495106496106497106498106499106500106501106502106503106504106505106506106507106508106509106510106511106512106513106514106515106516106517106518106519106520106521106522106523106524106525106526106527106528106529106530106531106532106533106534106535106536106537106538106539106540106541106542106543106544106545106546106547106548106549106550106551106552106553106554106555106556106557106558106559106560106561106562106563106564106565106566106567106568106569106570106571106572106573106574106575106576106577106578106579106580106581106582106583106584106585106586106587106588106589106590106591106592106593106594106595106596106597106598106599106600106601106602106603106604106605106606106607106608106609106610106611106612106613106614106615106616106617106618106619106620106621106622106623106624106625106626106627106628106629106630106631106632106633106634106635106636106637106638106639106640106641106642106643106644106645106646106647106648106649106650106651106652106653106654106655106656106657106658106659106660106661106662106663106664106665106666106667106668106669106670106671106672106673106674106675106676106677106678106679106680106681106682106683106684106685106686106687106688106689106690106691106692106693106694106695106696106697106698106699106700106701106702106703106704106705106706106707106708106709106710106711106712106713106714106715106716106717106718106719106720106721106722106723106724106725106726106727106728106729106730106731106732106733106734106735106736106737106738106739106740106741106742106743106744106745106746106747106748106749106750106751106752106753106754106755106756106757106758106759106760106761106762106763106764106765106766106767106768106769106770106771106772106773106774106775106776106777106778106779106780106781106782106783106784106785106786106787106788106789106790106791106792106793106794106795106796106797106798106799106800106801106802106803106804106805106806106807106808106809106810106811106812106813106814106815106816106817106818106819106820106821106822106823106824106825106826106827106828106829106830106831106832106833106834106835106836106837106838106839106840106841106842106843106844106845106846106847106848106849106850106851106852106853106854106855106856106857106858106859106860106861106862106863106864106865106866106867106868106869106870106871106872106873106874106875106876106877106878106879106880106881106882106883106884106885106886106887106888106889106890106891106892106893106894106895106896106897106898106899106900106901106902106903106904106905106906106907106908106909106910106911106912106913106914106915106916106917106918106919106920106921106922106923106924106925106926106927106928106929106930106931106932106933106934106935106936106937106938106939106940106941106942106943106944106945106946106947106948106949106950106951106952106953106954106955106956106957106958106959106960106961106962106963106964106965106966106967106968106969106970106971106972106973106974106975106976106977106978106979106980106981106982106983106984106985106986106987106988106989106990106991106992106993106994106995106996106997106998106999107000107001107002107003107004107005107006107007107008107009107010107011107012107013107014107015107016107017107018107019107020107021107022107023107024107025107026107027107028107029107030107031107032107033107034107035107036107037107038107039107040107041107042107043107044107045107046107047107048107049107050107051107052107053107054107055107056107057107058107059107060107061107062107063107064107065107066107067107068107069107070107071107072107073107074107075107076107077107078107079107080107081107082107083107084107085107086107087107088107089107090107091107092107093107094107095107096107097107098107099107100107101107102107103107104107105107106107107107108107109107110107111107112107113107114107115107116107117107118107119107120107121107122107123107124107125107126107127107128107129107130107131107132107133107134107135107136107137107138107139107140107141107142107143107144107145107146107147107148107149107150107151107152107153107154107155107156107157107158107159107160107161107162107163107164107165107166107167107168107169107170107171107172107173107174107175107176107177107178107179107180107181107182107183107184107185107186107187107188107189107190107191107192107193107194107195107196107197107198107199107200107201107202107203107204107205107206107207107208107209107210107211107212107213107214107215107216107217107218107219107220107221107222107223107224107225107226107227107228107229107230107231107232107233107234107235107236107237107238107239107240107241107242107243107244107245107246107247107248107249107250107251107252107253107254107255107256107257107258107259107260107261107262107263107264107265107266107267107268107269107270107271107272107273107274107275107276107277107278107279107280107281107282107283107284107285107286107287107288107289107290107291107292107293107294107295107296107297107298107299107300107301107302107303107304107305107306107307107308107309107310107311107312107313107314107315107316107317107318107319107320107321107322107323107324107325107326107327107328107329107330107331107332107333107334107335107336107337107338107339107340107341107342107343107344107345107346107347107348107349107350107351107352107353107354107355107356107357107358107359107360107361107362107363107364107365107366107367107368107369107370107371107372107373107374107375107376107377107378107379107380107381107382107383107384107385107386107387107388107389107390107391107392107393107394107395107396107397107398107399107400107401107402107403107404107405107406107407107408107409107410107411107412107413107414107415107416107417107418107419107420107421107422107423107424107425107426107427107428107429107430107431107432107433107434107435107436107437107438107439107440107441107442107443107444107445107446107447107448107449107450107451107452107453107454107455107456107457107458107459107460107461107462107463107464107465107466107467107468107469107470107471107472107473107474107475107476107477107478107479107480107481107482107483107484107485107486107487107488107489107490107491107492107493107494107495107496107497107498107499107500107501107502107503107504107505107506107507107508107509107510107511107512107513107514107515107516107517107518107519107520107521107522107523107524107525107526107527107528107529107530107531107532107533107534107535107536107537107538107539107540107541107542107543107544107545107546107547107548107549107550107551107552107553107554107555107556107557107558107559107560107561107562107563107564107565107566107567107568107569107570107571107572107573107574107575107576107577107578107579107580107581107582107583107584107585107586107587107588107589107590107591107592107593107594107595107596107597107598107599107600107601107602107603107604107605107606107607107608107609107610107611107612107613107614107615107616107617107618107619107620107621107622107623107624107625107626107627107628107629107630107631107632107633107634107635107636107637107638107639107640107641107642107643107644107645107646107647107648107649107650107651107652107653107654107655107656107657107658107659107660107661107662107663107664107665107666107667107668107669107670107671107672107673107674107675107676107677107678107679107680107681107682107683107684107685107686107687107688107689107690107691107692107693107694107695107696107697107698107699107700107701107702107703107704107705107706107707107708107709107710107711107712107713107714107715107716107717107718107719107720107721107722107723107724107725107726107727107728107729107730107731107732107733107734107735107736107737107738107739107740107741107742107743107744107745107746107747107748107749107750107751107752107753107754107755107756107757107758107759107760107761107762107763107764107765107766107767107768107769107770107771107772107773107774107775107776107777107778107779107780107781107782107783107784107785107786107787107788107789107790107791107792107793107794107795107796107797107798107799107800107801107802107803107804107805107806107807107808107809107810107811107812107813107814107815107816107817107818107819107820107821107822107823107824107825107826107827107828107829107830107831107832107833107834107835107836107837107838107839107840107841107842107843107844107845107846107847107848107849107850107851107852107853107854107855107856107857107858107859107860107861107862107863107864107865107866107867107868107869107870107871107872107873107874107875107876107877107878107879107880107881107882107883107884107885107886107887107888107889107890107891107892107893107894107895107896107897107898107899107900107901107902107903107904107905107906107907107908107909107910107911107912107913107914107915107916107917107918107919107920107921107922107923107924107925107926107927107928107929107930107931107932107933107934107935107936107937107938107939107940107941107942107943107944107945107946107947107948107949107950107951107952107953107954107955107956107957107958107959107960107961107962107963107964107965107966107967107968107969107970107971107972107973107974107975107976107977107978107979107980107981107982107983107984107985107986107987107988107989107990107991107992107993107994107995107996107997107998107999108000108001108002108003108004108005108006108007108008108009108010108011108012108013108014108015108016108017108018108019108020108021108022108023108024108025108026108027108028108029108030108031108032108033108034108035108036108037108038108039108040108041108042108043108044108045108046108047108048108049108050108051108052108053108054108055108056108057108058108059108060108061108062108063108064108065108066108067108068108069108070108071108072108073108074108075108076108077108078108079108080108081108082108083108084108085108086108087108088108089108090108091108092108093108094108095108096108097108098108099108100108101108102108103108104108105108106108107108108108109108110108111108112108113108114108115108116108117108118108119108120108121108122108123108124108125108126108127108128108129108130108131108132108133108134108135108136108137108138108139108140108141108142108143108144108145108146108147108148108149108150108151108152108153108154108155108156108157108158108159108160108161108162108163108164108165108166108167108168108169108170108171108172108173108174108175108176108177108178108179108180108181108182108183108184108185108186108187108188108189108190108191108192108193108194108195108196108197108198108199108200108201108202108203108204108205108206108207108208108209108210108211108212108213108214108215108216108217108218108219108220108221108222108223108224108225108226108227108228108229108230108231108232108233108234108235108236108237108238108239108240108241108242108243108244108245108246108247108248108249108250108251108252108253108254108255108256108257108258108259108260108261108262108263108264108265108266108267108268108269108270108271108272108273108274108275108276108277108278108279108280108281108282108283108284108285108286108287108288108289108290108291108292108293108294108295108296108297108298108299108300108301108302108303108304108305108306108307108308108309108310108311108312108313108314108315108316108317108318108319108320108321108322108323108324108325108326108327108328108329108330108331108332108333108334108335108336108337108338108339108340108341108342108343108344108345108346108347108348108349108350108351108352108353108354108355108356108357108358108359108360108361108362108363108364108365108366108367108368108369108370108371108372108373108374108375108376108377108378108379108380108381108382108383108384108385108386108387108388108389108390108391108392108393108394108395108396108397108398108399108400108401108402108403108404108405108406108407108408108409108410108411108412108413108414108415108416108417108418108419108420108421108422108423108424108425108426108427108428108429108430108431108432108433108434108435108436108437108438108439108440108441108442108443108444108445108446108447108448108449108450108451108452108453108454108455108456108457108458108459108460108461108462108463108464108465108466108467108468108469108470108471108472108473108474108475108476108477108478108479108480108481108482108483108484108485108486108487108488108489108490108491108492108493108494108495108496108497108498108499108500108501108502108503108504108505108506108507108508108509108510108511108512108513108514108515108516108517108518108519108520108521108522108523108524108525108526108527108528108529108530108531108532108533108534108535108536108537108538108539108540108541108542108543108544108545108546108547108548108549108550108551108552108553108554108555108556108557108558108559108560108561108562108563108564108565108566108567108568108569108570108571108572108573108574108575108576108577108578108579108580108581108582108583108584108585108586108587108588108589108590108591108592108593108594108595108596108597108598108599108600108601108602108603108604108605108606108607108608108609108610108611108612108613108614108615108616108617108618108619108620108621108622108623108624108625108626108627108628108629108630108631108632108633108634108635108636108637108638108639108640108641108642108643108644108645108646108647108648108649108650108651108652108653108654108655108656108657108658108659108660108661108662108663108664108665108666108667108668108669108670108671108672108673108674108675108676108677108678108679108680108681108682108683108684108685108686108687108688108689108690108691108692108693108694108695108696108697108698108699108700108701108702108703108704108705108706108707108708108709108710108711108712108713108714108715108716108717108718108719108720108721108722108723108724108725108726108727108728108729108730108731108732108733108734108735108736108737108738108739108740108741108742108743108744108745108746108747108748108749108750108751108752108753108754108755108756108757108758108759108760108761108762108763108764108765108766108767108768108769108770108771108772108773108774108775108776108777108778108779108780108781108782108783108784108785108786108787108788108789108790108791108792108793108794108795108796108797108798108799108800108801108802108803108804108805108806108807108808108809108810108811108812108813108814108815108816108817108818108819108820108821108822108823108824108825108826108827108828108829108830108831108832108833108834108835108836108837108838108839108840108841108842108843108844108845108846108847108848108849108850108851108852108853108854108855108856108857108858108859108860108861108862108863108864108865108866108867108868108869108870108871108872108873108874108875108876108877108878108879108880108881108882108883108884108885108886108887108888108889108890108891108892108893108894108895108896108897108898108899108900108901108902108903108904108905108906108907108908108909108910108911108912108913108914108915108916108917108918108919108920108921108922108923108924108925108926108927108928108929108930108931108932108933108934108935108936108937108938108939108940108941108942108943108944108945108946108947108948108949108950108951108952108953108954108955108956108957108958108959108960108961108962108963108964108965108966108967108968108969108970108971108972108973108974108975108976108977108978108979108980108981108982108983108984108985108986108987108988108989108990108991108992108993108994108995108996108997108998108999109000109001109002109003109004109005109006109007109008109009109010109011109012109013109014109015109016109017109018109019109020109021109022109023109024109025109026109027109028109029109030109031109032109033109034109035109036109037109038109039109040109041109042109043109044109045109046109047109048109049109050109051109052109053109054109055109056109057109058109059109060109061109062109063109064109065109066109067109068109069109070109071109072109073109074109075109076109077109078109079109080109081109082109083109084109085109086109087109088109089109090109091109092109093109094109095109096109097109098109099109100109101109102109103109104109105109106109107109108109109109110109111109112109113109114109115109116109117109118109119109120109121109122109123109124109125109126109127109128109129109130109131109132109133109134109135109136109137109138109139109140109141109142109143109144109145109146109147109148109149109150109151109152109153109154109155109156109157109158109159109160109161109162109163109164109165109166109167109168109169109170109171109172109173109174109175109176109177109178109179109180109181109182109183109184109185109186109187109188109189109190109191109192109193109194109195109196109197109198109199109200109201109202109203109204109205109206109207109208109209109210109211109212109213109214109215109216109217109218109219109220109221109222109223109224109225109226109227109228109229109230109231109232109233109234109235109236109237109238109239109240109241109242109243109244109245109246109247109248109249109250109251109252109253109254109255109256109257109258109259109260109261109262109263109264109265109266109267109268109269109270109271109272109273109274109275109276109277109278109279109280109281109282109283109284109285109286109287109288109289109290109291109292109293109294109295109296109297109298109299109300109301109302109303109304109305109306109307109308109309109310109311109312109313109314109315109316109317109318109319109320109321109322109323109324109325109326109327109328109329109330109331109332109333109334109335109336109337109338109339109340109341109342109343109344109345109346109347109348109349109350109351109352109353109354109355109356109357109358109359109360109361109362109363109364109365109366109367109368109369109370109371109372109373109374109375109376109377109378109379109380109381109382109383109384109385109386109387109388109389109390109391109392109393109394109395109396109397109398109399109400109401109402109403109404109405109406109407109408109409109410109411109412109413109414109415109416109417109418109419109420109421109422109423109424109425109426109427109428109429109430109431109432109433109434109435109436109437109438109439109440109441109442109443109444109445109446109447109448109449109450109451109452109453109454109455109456109457109458109459109460109461109462109463109464109465109466109467109468109469109470109471109472109473109474109475109476109477109478109479109480109481109482109483109484109485109486109487109488109489109490109491109492109493109494109495109496109497109498109499109500109501109502109503109504109505109506109507109508109509109510109511109512109513109514109515109516109517109518109519109520109521109522109523109524109525109526109527109528109529109530109531109532109533109534109535109536109537109538109539109540109541109542109543109544109545109546109547109548109549109550109551109552109553109554109555109556109557109558109559109560109561109562109563109564109565109566109567109568109569109570109571109572109573109574109575109576109577109578109579109580109581109582109583109584109585109586109587109588109589109590109591109592109593109594109595109596109597109598109599109600109601109602109603109604109605109606109607109608109609109610109611109612109613109614109615109616109617109618109619109620109621109622109623109624109625109626109627109628109629109630109631109632109633109634109635109636109637109638109639109640109641109642109643109644109645109646109647109648109649109650109651109652109653109654109655109656109657109658109659109660109661109662109663109664109665109666109667109668109669109670109671109672109673109674109675109676109677109678109679109680109681109682109683109684109685109686109687109688109689109690109691109692109693109694109695109696109697109698109699109700109701109702109703109704109705109706109707109708109709109710109711109712109713109714109715109716109717109718109719109720109721109722109723109724109725109726109727109728109729109730109731109732109733109734109735109736109737109738109739109740109741109742109743109744109745109746109747109748109749109750109751109752109753109754109755109756109757109758109759109760109761109762109763109764109765109766109767109768109769109770109771109772109773109774109775109776109777109778109779109780109781109782109783109784109785109786109787109788109789109790109791109792109793109794109795109796109797109798109799109800109801109802109803109804109805109806109807109808109809109810109811109812109813109814109815109816109817109818109819109820109821109822109823109824109825109826109827109828109829109830109831109832109833109834109835109836109837109838109839109840109841109842109843109844109845109846109847109848109849109850109851109852109853109854109855109856109857109858109859109860109861109862109863109864109865109866109867109868109869109870109871109872109873109874109875109876109877109878109879109880109881109882109883109884109885109886109887109888109889109890109891109892109893109894109895109896109897109898109899109900109901109902109903109904109905109906109907109908109909109910109911109912109913109914109915109916109917109918109919109920109921109922109923109924109925109926109927109928109929109930109931109932109933109934109935109936109937109938109939109940109941109942109943109944109945109946109947109948109949109950109951109952109953109954109955109956109957109958109959109960109961109962109963109964109965109966109967109968109969109970109971109972109973109974109975109976109977109978109979109980109981109982109983109984109985109986109987109988109989109990109991109992109993109994109995109996109997109998109999110000110001110002110003110004110005110006110007110008110009110010110011110012110013110014110015110016110017110018110019110020110021110022110023110024110025110026110027110028110029110030110031110032110033110034110035110036110037110038110039110040110041110042110043110044110045110046110047110048110049110050110051110052110053110054110055110056110057110058110059110060110061110062110063110064110065110066110067110068110069110070110071110072110073110074110075110076110077110078110079110080110081110082110083110084110085110086110087110088110089110090110091110092110093110094110095110096110097110098110099110100110101110102110103110104110105110106110107110108110109110110110111110112110113110114110115110116110117110118110119110120110121110122110123110124110125110126110127110128110129110130110131110132110133110134110135110136110137110138110139110140110141110142110143110144110145110146110147110148110149110150110151110152110153110154110155110156110157110158110159110160110161110162110163110164110165110166110167110168110169110170110171110172110173110174110175110176110177110178110179110180110181110182110183110184110185110186110187110188110189110190110191110192110193110194110195110196110197110198110199110200110201110202110203110204110205110206110207110208110209110210110211110212110213110214110215110216110217110218110219110220110221110222110223110224110225110226110227110228110229110230110231110232110233110234110235110236110237110238110239110240110241110242110243110244110245110246110247110248110249110250110251110252110253110254110255110256110257110258110259110260110261110262110263110264110265110266110267110268110269110270110271110272110273110274110275110276110277110278110279110280110281110282110283110284110285110286110287110288110289110290110291110292110293110294110295110296110297110298110299110300110301110302110303110304110305110306110307110308110309110310110311110312110313110314110315110316110317110318110319110320110321110322110323110324110325110326110327110328110329110330110331110332110333110334110335110336110337110338110339110340110341110342110343110344110345110346110347110348110349110350110351110352110353110354110355110356110357110358110359110360110361110362110363110364110365110366110367110368110369110370110371110372110373110374110375110376110377110378110379110380110381110382110383110384110385110386110387110388110389110390110391110392110393110394110395110396110397110398110399110400110401110402110403110404110405110406110407110408110409110410110411110412110413110414110415110416110417110418110419110420110421110422110423110424110425110426110427110428110429110430110431110432110433110434110435110436110437110438110439110440110441110442110443110444110445110446110447110448110449110450110451110452110453110454110455110456110457110458110459110460110461110462110463110464110465110466110467110468110469110470110471110472110473110474110475110476110477110478110479110480110481110482110483110484110485110486110487110488110489110490110491110492110493110494110495110496110497110498110499110500110501110502110503110504110505110506110507110508110509110510110511110512110513110514110515110516110517110518110519110520110521110522110523110524110525110526110527110528110529110530110531110532110533110534110535110536110537110538110539110540110541110542110543110544110545110546110547110548110549110550110551110552110553110554110555110556110557110558110559110560110561110562110563110564110565110566110567110568110569110570110571110572110573110574110575110576110577110578110579110580110581110582110583110584110585110586110587110588110589110590110591110592110593110594110595110596110597110598110599110600110601110602110603110604110605110606110607110608110609110610110611110612110613110614110615110616110617110618110619110620110621110622110623110624110625110626110627110628110629110630110631110632110633110634110635110636110637110638110639110640110641110642110643110644110645110646110647110648110649110650110651110652110653110654110655110656110657110658110659110660110661110662110663110664110665110666110667110668110669110670110671110672110673110674110675110676110677110678110679110680110681110682110683110684110685110686110687110688110689110690110691110692110693110694110695110696110697110698110699110700110701110702110703110704110705110706110707110708110709110710110711110712110713110714110715110716110717110718110719110720110721110722110723110724110725110726110727110728110729110730110731110732110733110734110735110736110737110738110739110740110741110742110743110744110745110746110747110748110749110750110751110752110753110754110755110756110757110758110759110760110761110762110763110764110765110766110767110768110769110770110771110772110773110774110775110776110777110778110779110780110781110782110783110784110785110786110787110788110789110790110791110792110793110794110795110796110797110798110799110800110801110802110803110804110805110806110807110808110809110810110811110812110813110814110815110816110817110818110819110820110821110822110823110824110825110826110827110828110829110830110831110832110833110834110835110836110837110838110839110840110841110842110843110844110845110846110847110848110849110850110851110852110853110854110855110856110857110858110859110860110861110862110863110864110865110866110867110868110869110870110871110872110873110874110875110876110877110878110879110880110881110882110883110884110885110886110887110888110889110890110891110892110893110894110895110896110897110898110899110900110901110902110903110904110905110906110907110908110909110910110911110912110913110914110915110916110917110918110919110920110921110922110923110924110925110926110927110928110929110930110931110932110933110934110935110936110937110938110939110940110941110942110943110944110945110946110947110948110949110950110951110952110953110954110955110956110957110958110959110960110961110962110963110964110965110966110967110968110969110970110971110972110973110974110975110976110977110978110979110980110981110982110983110984110985110986110987110988110989110990110991110992110993110994110995110996110997110998110999111000111001111002111003111004111005111006111007111008111009111010111011111012111013111014111015111016111017111018111019111020111021111022111023111024111025111026111027111028111029111030111031111032111033111034111035111036111037111038111039111040111041111042111043111044111045111046111047111048111049111050111051111052111053111054111055111056111057111058111059111060111061111062111063111064111065111066111067111068111069111070111071111072111073111074111075111076111077111078111079111080111081111082111083111084111085111086111087111088111089111090111091111092111093111094111095111096111097111098111099111100111101111102111103111104111105111106111107111108111109111110111111111112111113111114111115111116111117111118111119111120111121111122111123111124111125111126111127111128111129111130111131111132111133111134111135111136111137111138111139111140111141111142111143111144111145111146111147111148111149111150111151111152111153111154111155111156111157111158111159111160111161111162111163111164111165111166111167111168111169111170111171111172111173111174111175111176111177111178111179111180111181111182111183111184111185111186111187111188111189111190111191111192111193111194111195111196111197111198111199111200111201111202111203111204111205111206111207111208111209111210111211111212111213111214111215111216111217111218111219111220111221111222111223111224111225111226111227111228111229111230111231111232111233111234111235111236111237111238111239111240111241111242111243111244111245111246111247111248111249111250111251111252111253111254111255111256111257111258111259111260111261111262111263111264111265111266111267111268111269111270111271111272111273111274111275111276111277111278111279111280111281111282111283111284111285111286111287111288111289111290111291111292111293111294111295111296111297111298111299111300111301111302111303111304111305111306111307111308111309111310111311111312111313111314111315111316111317111318111319111320111321111322111323111324111325111326111327111328111329111330111331111332111333111334111335111336111337111338111339111340111341111342111343111344111345111346111347111348111349111350111351111352111353111354111355111356111357111358111359111360111361111362111363111364111365111366111367111368111369111370111371111372111373111374111375111376111377111378111379111380111381111382111383111384111385111386111387111388111389111390111391111392111393111394111395111396111397111398111399111400111401111402111403111404111405111406111407111408111409111410111411111412111413111414111415111416111417111418111419111420111421111422111423111424111425111426111427111428111429111430111431111432111433111434111435111436111437111438111439111440111441111442111443111444111445111446111447111448111449111450111451111452111453111454111455111456111457111458111459111460111461111462111463111464111465111466111467111468111469111470111471111472111473111474111475111476111477111478111479111480111481111482111483111484111485111486111487111488111489111490111491111492111493111494111495111496111497111498111499111500111501111502111503111504111505111506111507111508111509111510111511111512111513111514111515111516111517111518111519111520111521111522111523111524111525111526111527111528111529111530111531111532111533111534111535111536111537111538111539111540111541111542111543111544111545111546111547111548111549111550111551111552111553111554111555111556111557111558111559111560111561111562111563111564111565111566111567111568111569111570111571111572111573111574111575111576111577111578111579111580111581111582111583111584111585111586111587111588111589111590111591111592111593111594111595111596111597111598111599111600111601111602111603111604111605111606111607111608111609111610111611111612111613111614111615111616111617111618111619111620111621111622111623111624111625111626111627111628111629111630111631111632111633111634111635111636111637111638111639111640111641111642111643111644111645111646111647111648111649111650111651111652111653111654111655111656111657111658111659111660111661111662111663111664111665111666111667111668111669111670111671111672111673111674111675111676111677111678111679111680111681111682111683111684111685111686111687111688111689111690111691111692111693111694111695111696111697111698111699111700111701111702111703111704111705111706111707111708111709111710111711111712111713111714111715111716111717111718111719111720111721111722111723111724111725111726111727111728111729111730111731111732111733111734111735111736111737111738111739111740111741111742111743111744111745111746111747111748111749111750111751111752111753111754111755111756111757111758111759111760111761111762111763111764111765111766111767111768111769111770111771111772111773111774111775111776111777111778111779111780111781111782111783111784111785111786111787111788111789111790111791111792111793111794111795111796111797111798111799111800111801111802111803111804111805111806111807111808111809111810111811111812111813111814111815111816111817111818111819111820111821111822111823111824111825111826111827111828111829111830111831111832111833111834111835111836111837111838111839111840111841111842111843111844111845111846111847111848111849111850111851111852111853111854111855111856111857111858111859111860111861111862111863111864111865111866111867111868111869111870111871111872111873111874111875111876111877111878111879111880111881111882111883111884111885111886111887111888111889111890111891111892111893111894111895111896111897111898111899111900111901111902111903111904111905111906111907111908111909111910111911111912111913111914111915111916111917111918111919111920111921111922111923111924111925111926111927111928111929111930111931111932111933111934111935111936111937111938111939111940111941111942111943111944111945111946111947111948111949111950111951111952111953111954111955111956111957111958111959111960111961111962111963111964111965111966111967111968111969111970111971111972111973111974111975111976111977111978111979111980111981111982111983111984111985111986111987111988111989111990111991111992111993111994111995111996111997111998111999112000112001112002112003112004112005112006112007112008112009112010112011112012112013112014112015112016112017112018112019112020112021112022112023112024112025112026112027112028112029112030112031112032112033112034112035112036112037112038112039112040112041112042112043112044112045112046112047112048112049112050112051112052112053112054112055112056112057112058112059112060112061112062112063112064112065112066112067112068112069112070112071112072112073112074112075112076112077112078112079112080112081112082112083112084112085112086112087112088112089112090112091112092112093112094112095112096112097112098112099112100112101112102112103112104112105112106112107112108112109112110112111112112112113112114112115112116112117112118112119112120112121112122112123112124112125112126112127112128112129112130112131112132112133112134112135112136112137112138112139112140112141112142112143112144112145112146112147112148112149112150112151112152112153112154112155112156112157112158112159112160112161112162112163112164112165112166112167112168112169112170112171112172112173112174112175112176112177112178112179112180112181112182112183112184112185112186112187112188112189112190112191112192112193112194112195112196112197112198112199112200112201112202112203112204112205112206112207112208112209112210112211112212112213112214112215112216112217112218112219112220112221112222112223112224112225112226112227112228112229112230112231112232112233112234112235112236112237112238112239112240112241112242112243112244112245112246112247112248112249112250112251112252112253112254112255112256112257112258112259112260112261112262112263112264112265112266112267112268112269112270112271112272112273112274112275112276112277112278112279112280112281112282112283112284112285112286112287112288112289112290112291112292112293112294112295112296112297112298112299112300112301112302112303112304112305112306112307112308112309112310112311112312112313112314112315112316112317112318112319112320112321112322112323112324112325112326112327112328112329112330112331112332112333112334112335112336112337112338112339112340112341112342112343112344112345112346112347112348112349112350112351112352112353112354112355112356112357112358112359112360112361112362112363112364112365112366112367112368112369112370112371112372112373112374112375112376112377112378112379112380112381112382112383112384112385112386112387112388112389112390112391112392112393112394112395112396112397112398112399112400112401112402112403112404112405112406112407112408112409112410112411112412112413112414112415112416112417112418112419112420112421112422112423112424112425112426112427112428112429112430112431112432112433112434112435112436112437112438112439112440112441112442112443112444112445112446112447112448112449112450112451112452112453112454112455112456112457112458112459112460112461112462112463112464112465112466112467112468112469112470112471112472112473112474112475112476112477112478112479112480112481112482112483112484112485112486112487112488112489112490112491112492112493112494112495112496112497112498112499112500112501112502112503112504112505112506112507112508112509112510112511112512112513112514112515112516112517112518112519112520112521112522112523112524112525112526112527112528112529112530112531112532112533112534112535112536112537112538112539112540112541112542112543112544112545112546112547112548112549112550112551112552112553112554112555112556112557112558112559112560112561112562112563112564112565112566112567112568112569112570112571112572112573112574112575112576112577112578112579112580112581112582112583112584112585112586112587112588112589112590112591112592112593112594112595112596112597112598112599112600112601112602112603112604112605112606112607112608112609112610112611112612112613112614112615112616112617112618112619112620112621112622112623112624112625112626112627112628112629112630112631112632112633112634112635112636112637112638112639112640112641112642112643112644112645112646112647112648112649112650112651112652112653112654112655112656112657112658112659112660112661112662112663112664112665112666112667112668112669112670112671112672112673112674112675112676112677112678112679112680112681112682112683112684112685112686112687112688112689112690112691112692112693112694112695112696112697112698112699112700112701112702112703112704112705112706112707112708112709112710112711112712112713112714112715112716112717112718112719112720112721112722112723112724112725112726112727112728112729112730112731112732112733112734112735112736112737112738112739112740112741112742112743112744112745112746112747112748112749112750112751112752112753112754112755112756112757112758112759112760112761112762112763112764112765112766112767112768112769112770112771112772112773112774112775112776112777112778112779112780112781112782112783112784112785112786112787112788112789112790112791112792112793112794112795112796112797112798112799112800112801112802112803112804112805112806112807112808112809112810112811112812112813112814112815112816112817112818112819112820112821112822112823112824112825112826112827112828112829112830112831112832112833112834112835112836112837112838112839112840112841112842112843112844112845112846112847112848112849112850112851112852112853112854112855112856112857112858112859112860112861112862112863112864112865112866112867112868112869112870112871112872112873112874112875112876112877112878112879112880112881112882112883112884112885112886112887112888112889112890112891112892112893112894112895112896112897112898112899112900112901112902112903112904112905112906112907112908112909112910112911112912112913112914112915112916112917112918112919112920112921112922112923112924112925112926112927112928112929112930112931112932112933112934112935112936112937112938112939112940112941112942112943112944112945112946112947112948112949112950112951112952112953112954112955112956112957112958112959112960112961112962112963112964112965112966112967112968112969112970112971112972112973112974112975112976112977112978112979112980112981112982112983112984112985112986112987112988112989112990112991112992112993112994112995112996112997112998112999113000113001113002113003113004113005113006113007113008113009113010113011113012113013113014113015113016113017113018113019113020113021113022113023113024113025113026113027113028113029113030113031113032113033113034113035113036113037113038113039113040113041113042113043113044113045113046113047113048113049113050113051113052113053113054113055113056113057113058113059113060113061113062113063113064113065113066113067113068113069113070113071113072113073113074113075113076113077113078113079113080113081113082113083113084113085113086113087113088113089113090113091113092113093113094113095113096113097113098113099113100113101113102113103113104113105113106113107113108113109113110113111113112113113113114113115113116113117113118113119113120113121113122113123113124113125113126113127113128113129113130113131113132113133113134113135113136113137113138113139113140113141113142113143113144113145113146113147113148113149113150113151113152113153113154113155113156113157113158113159113160113161113162113163113164113165113166113167113168113169113170113171113172113173113174113175113176113177113178113179113180113181113182113183113184113185113186113187113188113189113190113191113192113193113194113195113196113197113198113199113200113201113202113203113204113205113206113207113208113209113210113211113212113213113214113215113216113217113218113219113220113221113222113223113224113225113226113227113228113229113230113231113232113233113234113235113236113237113238113239113240113241113242113243113244113245113246113247113248113249113250113251113252113253113254113255113256113257113258113259113260113261113262113263113264113265113266113267113268113269113270113271113272113273113274113275113276113277113278113279113280113281113282113283113284113285113286113287113288113289113290113291113292113293113294113295113296113297113298113299113300113301113302113303113304113305113306113307113308113309113310113311113312113313113314113315113316113317113318113319113320113321113322113323113324113325113326113327113328113329113330113331113332113333113334113335113336113337113338113339113340113341113342113343113344113345113346113347113348113349113350113351113352113353113354113355113356113357113358113359113360113361113362113363113364113365113366113367113368113369113370113371113372113373113374113375113376113377113378113379113380113381113382113383113384113385113386113387113388113389113390113391113392113393113394113395113396113397113398113399113400113401113402113403113404113405113406113407113408113409113410113411113412113413113414113415113416113417113418113419113420113421113422113423113424113425113426113427113428113429113430113431113432113433113434113435113436113437113438113439113440113441113442113443113444113445113446113447113448113449113450113451113452113453113454113455113456113457113458113459113460113461113462113463113464113465113466113467113468113469113470113471113472113473113474113475113476113477113478113479113480113481113482113483113484113485113486113487113488113489113490113491113492113493113494113495113496113497113498113499113500113501113502113503113504113505113506113507113508113509113510113511113512113513113514113515113516113517113518113519113520113521113522113523113524113525113526113527113528113529113530113531113532113533113534113535113536113537113538113539113540113541113542113543113544113545113546113547113548113549113550113551113552113553113554113555113556113557113558113559113560113561113562113563113564113565113566113567113568113569113570113571113572113573113574113575113576113577113578113579113580113581113582113583113584113585113586113587113588113589113590113591113592113593113594113595113596113597113598113599113600113601113602113603113604113605113606113607113608113609113610113611113612113613113614113615113616113617113618113619113620113621113622113623113624113625113626113627113628113629113630113631113632113633113634113635113636113637113638113639113640113641113642113643113644113645113646113647113648113649113650113651113652113653113654113655113656113657113658113659113660113661113662113663113664113665113666113667113668113669113670113671113672113673113674113675113676113677113678113679113680113681113682113683113684113685113686113687113688113689113690113691113692113693113694113695113696113697113698113699113700113701113702113703113704113705113706113707113708113709113710113711113712113713113714113715113716113717113718113719113720113721113722113723113724113725113726113727113728113729113730113731113732113733113734113735113736113737113738113739113740113741113742113743113744113745113746113747113748113749113750113751113752113753113754113755113756113757113758113759113760113761113762113763113764113765113766113767113768113769113770113771113772113773113774113775113776113777113778113779113780113781113782113783113784113785113786113787113788113789113790113791113792113793113794113795113796113797113798113799113800113801113802113803113804113805113806113807113808113809113810113811113812113813113814113815113816113817113818113819113820113821113822113823113824113825113826113827113828113829113830113831113832113833113834113835113836113837113838113839113840113841113842113843113844113845113846113847113848113849113850113851113852113853113854113855113856113857113858113859113860113861113862113863113864113865113866113867113868113869113870113871113872113873113874113875113876113877113878113879113880113881113882113883113884113885113886113887113888113889113890113891113892113893113894113895113896113897113898113899113900113901113902113903113904113905113906113907113908113909113910113911113912113913113914113915113916113917113918113919113920113921113922113923113924113925113926113927113928113929113930113931113932113933113934113935113936113937113938113939113940113941113942113943113944113945113946113947113948113949113950113951113952113953113954113955113956113957113958113959113960113961113962113963113964113965113966113967113968113969113970113971113972113973113974113975113976113977113978113979113980113981113982113983113984113985113986113987113988113989113990113991113992113993113994113995113996113997113998113999114000114001114002114003114004114005114006114007114008114009114010114011114012114013114014114015114016114017114018114019114020114021114022114023114024114025114026114027114028114029114030114031114032114033114034114035114036114037114038114039114040114041114042114043114044114045114046114047114048114049114050114051114052114053114054114055114056114057114058114059114060114061114062114063114064114065114066114067114068114069114070114071114072114073114074114075114076114077114078114079114080114081114082114083114084114085114086114087114088114089114090114091114092114093114094114095114096114097114098114099114100114101114102114103114104114105114106114107114108114109114110114111114112114113114114114115114116114117114118114119114120114121114122114123114124114125114126114127114128114129114130114131114132114133114134114135114136114137114138114139114140114141114142114143114144114145114146114147114148114149114150114151114152114153114154114155114156114157114158114159114160114161114162114163114164114165114166114167114168114169114170114171114172114173114174114175114176114177114178114179114180114181114182114183114184114185114186114187114188114189114190114191114192114193114194114195114196114197114198114199114200114201114202114203114204114205114206114207114208114209114210114211114212114213114214114215114216114217114218114219114220114221114222114223114224114225114226114227114228114229114230114231114232114233114234114235114236114237114238114239114240114241114242114243114244114245114246114247114248114249114250114251114252114253114254114255114256114257114258114259114260114261114262114263114264114265114266114267114268114269114270114271114272114273114274114275114276114277114278114279114280114281114282114283114284114285114286114287114288114289114290114291114292114293114294114295114296114297114298114299114300114301114302114303114304114305114306114307114308114309114310114311114312114313114314114315114316114317114318114319114320114321114322114323114324114325114326114327114328114329114330114331114332114333114334114335114336114337114338114339114340114341114342114343114344114345114346114347114348114349114350114351114352114353114354114355114356114357114358114359114360114361114362114363114364114365114366114367114368114369114370114371114372114373114374114375114376114377114378114379114380114381114382114383114384114385114386114387114388114389114390114391114392114393114394114395114396114397114398114399114400114401114402114403114404114405114406114407114408114409114410114411114412114413114414114415114416114417114418114419114420114421114422114423114424114425114426114427114428114429114430114431114432114433114434114435114436114437114438114439114440114441114442114443114444114445114446114447114448114449114450114451114452114453114454114455114456114457114458114459114460114461114462114463114464114465114466114467114468114469114470114471114472114473114474114475114476114477114478114479114480114481114482114483114484114485114486114487114488114489114490114491114492114493114494114495114496114497114498114499114500114501114502114503114504114505114506114507114508114509114510114511114512114513114514114515114516114517114518114519114520114521114522114523114524114525114526114527114528114529114530114531114532114533114534114535114536114537114538114539114540114541114542114543114544114545114546114547114548114549114550114551114552114553114554114555114556114557114558114559114560114561114562114563114564114565114566114567114568114569114570114571114572114573114574114575114576114577114578114579114580114581114582114583114584114585114586114587114588114589114590114591114592114593114594114595114596114597114598114599114600114601114602114603114604114605114606114607114608114609114610114611114612114613114614114615114616114617114618114619114620114621114622114623114624114625114626114627114628114629114630114631114632114633114634114635114636114637114638114639114640114641114642114643114644114645114646114647114648114649114650114651114652114653114654114655114656114657114658114659114660114661114662114663114664114665114666114667114668114669114670114671114672114673114674114675114676114677114678114679114680114681114682114683114684114685114686114687114688114689114690114691114692114693114694114695114696114697114698114699114700114701114702114703114704114705114706114707114708114709114710114711114712114713114714114715114716114717114718114719114720114721114722114723114724114725114726114727114728114729114730114731114732114733114734114735114736114737114738114739114740114741114742114743114744114745114746114747114748114749114750114751114752114753114754114755114756114757114758114759114760114761114762114763114764114765114766114767114768114769114770114771114772114773114774114775114776114777114778114779114780114781114782114783114784114785114786114787114788114789114790114791114792114793114794114795114796114797114798114799114800114801114802114803114804114805114806114807114808114809114810114811114812114813114814114815114816114817114818114819114820114821114822114823114824114825114826114827114828114829114830114831114832114833114834114835114836114837114838114839114840114841114842114843114844114845114846114847114848114849114850114851114852114853114854114855114856114857114858114859114860114861114862114863114864114865114866114867114868114869114870114871114872114873114874114875114876114877114878114879114880114881114882114883114884114885114886114887114888114889114890114891114892114893114894114895114896114897114898114899114900114901114902114903114904114905114906114907114908114909114910114911114912114913114914114915114916114917114918114919114920114921114922114923114924114925114926114927114928114929114930114931114932114933114934114935114936114937114938114939114940114941114942114943114944114945114946114947114948114949114950114951114952114953114954114955114956114957114958114959114960114961114962114963114964114965114966114967114968114969114970114971114972114973114974114975114976114977114978114979114980114981114982114983114984114985114986114987114988114989114990114991114992114993114994114995114996114997114998114999115000115001115002115003115004115005115006115007115008115009115010115011115012115013115014115015115016115017115018115019115020115021115022115023115024115025115026115027115028115029115030115031115032115033115034115035115036115037115038115039115040115041115042115043115044115045115046115047115048115049115050115051115052115053115054115055115056115057115058115059115060115061115062115063115064115065115066115067115068115069115070115071115072115073115074115075115076115077115078115079115080115081115082115083115084115085115086115087115088115089115090115091115092115093115094115095115096115097115098115099115100115101115102115103115104115105115106115107115108115109115110115111115112115113115114115115115116115117115118115119115120115121115122115123115124115125115126115127115128115129115130115131115132115133115134115135115136115137115138115139115140115141115142115143115144115145115146115147115148115149115150115151115152115153115154115155115156115157115158115159115160115161115162115163115164115165115166115167115168115169115170115171115172115173115174115175115176115177115178115179115180115181115182115183115184115185115186115187115188115189115190115191115192115193115194115195115196115197115198115199115200115201115202115203115204115205115206115207115208115209115210115211115212115213115214115215115216115217115218115219115220115221115222115223115224115225115226115227115228115229115230115231115232115233115234115235115236115237115238115239115240115241115242115243115244115245115246115247115248115249115250115251115252115253115254115255115256115257115258115259115260115261115262115263115264115265115266115267115268115269115270115271115272115273115274115275115276115277115278115279115280115281115282115283115284115285115286115287115288115289115290115291115292115293115294115295115296115297115298115299115300115301115302115303115304115305115306115307115308115309115310115311115312115313115314115315115316115317115318115319115320115321115322115323115324115325115326115327115328115329115330115331115332115333115334115335115336115337115338115339115340115341115342115343115344115345115346115347115348115349115350115351115352115353115354115355115356115357115358115359115360115361115362115363115364115365115366115367115368115369115370115371115372115373115374115375115376115377115378115379115380115381115382115383115384115385115386115387115388115389115390115391115392115393115394115395115396115397115398115399115400115401115402115403115404115405115406115407115408115409115410115411115412115413115414115415115416115417115418115419115420115421115422115423115424115425115426115427115428115429115430115431115432115433115434115435115436115437115438115439115440115441115442115443115444115445115446115447115448115449115450115451115452115453115454115455115456115457115458115459115460115461115462115463115464115465115466115467115468115469115470115471115472115473115474115475115476115477115478115479115480115481115482115483115484115485115486115487115488115489115490115491115492115493115494115495115496115497115498115499115500115501115502115503115504115505115506115507115508115509115510115511115512115513115514115515115516115517115518115519115520115521115522115523115524115525115526115527115528115529115530115531115532115533115534115535115536115537115538115539115540115541115542115543115544115545115546115547115548115549115550115551115552115553115554115555115556115557115558115559115560115561115562115563115564115565115566115567115568115569115570115571115572115573115574115575115576115577115578115579115580115581115582115583115584115585115586115587115588115589115590115591115592115593115594115595115596115597115598115599115600115601115602115603115604115605115606115607115608115609115610115611115612115613115614115615115616115617115618115619115620115621115622115623115624115625115626115627115628115629115630115631115632115633115634115635115636115637115638115639115640115641115642115643115644115645115646115647115648115649115650115651115652115653115654115655115656115657115658115659115660115661115662115663115664115665115666115667115668115669115670115671115672115673115674115675115676115677115678115679115680115681115682115683115684115685115686115687115688115689115690115691115692115693115694115695115696115697115698115699115700115701115702115703115704115705115706115707115708115709115710115711115712115713115714115715115716115717115718115719115720115721115722115723115724115725115726115727115728115729115730115731115732115733115734115735115736115737115738115739115740115741115742115743115744115745115746115747115748115749115750115751115752115753115754115755115756115757115758115759115760115761115762115763115764115765115766115767115768115769115770115771115772115773115774115775115776115777115778115779115780115781115782115783115784115785115786115787115788115789115790115791115792115793115794115795115796115797115798115799115800115801115802115803115804115805115806115807115808115809115810115811115812115813115814115815115816115817115818115819115820115821115822115823115824115825115826115827115828115829115830115831115832115833115834115835115836115837115838115839115840115841115842115843115844115845115846115847115848115849115850115851115852115853115854115855115856115857115858115859115860115861115862115863115864115865115866115867115868115869115870115871115872115873115874115875115876115877115878115879115880115881115882115883115884115885115886115887115888115889115890115891115892115893115894115895115896115897115898115899115900115901115902115903115904115905115906115907115908115909115910115911115912115913115914115915115916115917115918115919115920115921115922115923115924115925115926115927115928115929115930115931115932115933115934115935115936115937115938115939115940115941115942115943115944115945115946115947115948115949115950115951115952115953115954115955115956115957115958115959115960115961115962115963115964115965115966115967115968115969115970115971115972115973115974115975115976115977115978115979115980115981115982115983115984115985115986115987115988115989115990115991115992115993115994115995115996115997115998115999116000116001116002116003116004116005116006116007116008116009116010116011116012116013116014116015116016116017116018116019116020116021116022116023116024116025116026116027116028116029116030116031116032116033116034116035116036116037116038116039116040116041116042116043116044116045116046116047116048116049116050116051116052116053116054116055116056116057116058116059116060116061116062116063116064116065116066116067116068116069116070116071116072116073116074116075116076116077116078116079116080116081116082116083116084116085116086116087116088116089116090116091116092116093116094116095116096116097116098116099116100116101116102116103116104116105116106116107116108116109116110116111116112116113116114116115116116116117116118116119116120116121116122116123116124116125116126116127116128116129116130116131116132116133116134116135116136116137116138116139116140116141116142116143116144116145116146116147116148116149116150116151116152116153116154116155116156116157116158116159116160116161116162116163116164116165116166116167116168116169116170116171116172116173116174116175116176116177116178116179116180116181116182116183116184116185116186116187116188116189116190116191116192116193116194116195116196116197116198116199116200116201116202116203116204116205116206116207116208116209116210116211116212116213116214116215116216116217116218116219116220116221116222116223116224116225116226116227116228116229116230116231116232116233116234116235116236116237116238116239116240116241116242116243116244116245116246116247116248116249116250116251116252116253116254116255116256116257116258116259116260116261116262116263116264116265116266116267116268116269116270116271116272116273116274116275116276116277116278116279116280116281116282116283116284116285116286116287116288116289116290116291116292116293116294116295116296116297116298116299116300116301116302116303116304116305116306116307116308116309116310116311116312116313116314116315116316116317116318116319116320116321116322116323116324116325116326116327116328116329116330116331116332116333116334116335116336116337116338116339116340116341116342116343116344116345116346116347116348116349116350116351116352116353116354116355116356116357116358116359116360116361116362116363116364116365116366116367116368116369116370116371116372116373116374116375116376116377116378116379116380116381116382116383116384116385116386116387116388116389116390116391116392116393116394116395116396116397116398116399116400116401116402116403116404116405116406116407116408116409116410116411116412116413116414116415116416116417116418116419116420116421116422116423116424116425116426116427116428116429116430116431116432116433116434116435116436116437116438116439116440116441116442116443116444116445116446116447116448116449116450116451116452116453116454116455116456116457116458116459116460116461116462116463116464116465116466116467116468116469116470116471116472116473116474116475116476116477116478116479116480116481116482116483116484116485116486116487116488116489116490116491116492116493116494116495116496116497116498116499116500116501116502116503116504116505116506116507116508116509116510116511116512116513116514116515116516116517116518116519116520116521116522116523116524116525116526116527116528116529116530116531116532116533116534116535116536116537116538116539116540116541116542116543116544116545116546116547116548116549116550116551116552116553116554116555116556116557116558116559116560116561116562116563116564116565116566116567116568116569116570116571116572116573116574116575116576116577116578116579116580116581116582116583116584116585116586116587116588116589116590116591116592116593116594116595116596116597116598116599116600116601116602116603116604116605116606116607116608116609116610116611116612116613116614116615116616116617116618116619116620116621116622116623116624116625116626116627116628116629116630116631116632116633116634116635116636116637116638116639116640116641116642116643116644116645116646116647116648116649116650116651116652116653116654116655116656116657116658116659116660116661116662116663116664116665116666116667116668116669116670116671116672116673116674116675116676116677116678116679116680116681116682116683116684116685116686116687116688116689116690116691116692116693116694116695116696116697116698116699116700116701116702116703116704116705116706116707116708116709116710116711116712116713116714116715116716116717116718116719116720116721116722116723116724116725116726116727116728116729116730116731116732116733116734116735116736116737116738116739116740116741116742116743116744116745116746116747116748116749116750116751116752116753116754116755116756116757116758116759116760116761116762116763116764116765116766116767116768116769116770116771116772116773116774116775116776116777116778116779116780116781116782116783116784116785116786116787116788116789116790116791116792116793116794116795116796116797116798116799116800116801116802116803116804116805116806116807116808116809116810116811116812116813116814116815116816116817116818116819116820116821116822116823116824116825116826116827116828116829116830116831116832116833116834116835116836116837116838116839116840116841116842116843116844116845116846116847116848116849116850116851116852116853116854116855116856116857116858116859116860116861116862116863116864116865116866116867116868116869116870116871116872116873116874116875116876116877116878116879116880116881116882116883116884116885116886116887116888116889116890116891116892116893116894116895116896116897116898116899116900116901116902116903116904116905116906116907116908116909116910116911116912116913116914116915116916116917116918116919116920116921116922116923116924116925116926116927116928116929116930116931116932116933116934116935116936116937116938116939116940116941116942116943116944116945116946116947116948116949116950116951116952116953116954116955116956116957116958116959116960116961116962116963116964116965116966116967116968116969116970116971116972116973116974116975116976116977116978116979116980116981116982116983116984116985116986116987116988116989116990116991116992116993116994116995116996116997116998116999117000117001117002117003117004117005117006117007117008117009117010117011117012117013117014117015117016117017117018117019117020117021117022117023117024117025117026117027117028117029117030117031117032117033117034117035117036117037117038117039117040117041117042117043117044117045117046117047117048117049117050117051117052117053117054117055117056117057117058117059117060117061117062117063117064117065117066117067117068117069117070117071117072117073117074117075117076117077117078117079117080117081117082117083117084117085117086117087117088117089117090117091117092117093117094117095117096117097117098117099117100117101117102117103117104117105117106117107117108117109117110117111117112117113117114117115117116117117117118117119117120117121117122117123117124117125117126117127117128117129117130117131117132117133117134117135117136117137117138117139117140117141117142117143117144117145117146117147117148117149117150117151117152117153117154117155117156117157117158117159117160117161117162117163117164117165117166117167117168117169117170117171117172117173117174117175117176117177117178117179117180117181117182117183117184117185117186117187117188117189117190117191117192117193117194117195117196117197117198117199117200117201117202117203117204117205117206117207117208117209117210117211117212117213117214117215117216117217117218117219117220117221117222117223117224117225117226117227117228117229117230117231117232117233117234117235117236117237117238117239117240117241117242117243117244117245117246117247117248117249117250117251117252117253117254117255117256117257117258117259117260117261117262117263117264117265117266117267117268117269117270117271117272117273117274117275117276117277117278117279117280117281117282117283117284117285117286117287117288117289117290117291117292117293117294117295117296117297117298117299117300117301117302117303117304117305117306117307117308117309117310117311117312117313117314117315117316117317117318117319117320117321117322117323117324117325117326117327117328117329117330117331117332117333117334117335117336117337117338117339117340117341117342117343117344117345117346117347117348117349117350117351117352117353117354117355117356117357117358117359117360117361117362117363117364117365117366117367117368117369117370117371117372117373117374117375117376117377117378117379117380117381117382117383117384117385117386117387117388117389117390117391117392117393117394117395117396117397117398117399117400117401117402117403117404117405117406117407117408117409117410117411117412117413117414117415117416117417117418117419117420117421117422117423117424117425117426117427117428117429117430117431117432117433117434117435117436117437117438117439117440117441117442117443117444117445117446117447117448117449117450117451117452117453117454117455117456117457117458117459117460117461117462117463117464117465117466117467117468117469117470117471117472117473117474117475117476117477117478117479117480117481117482117483117484117485117486117487117488117489117490117491117492117493117494117495117496117497117498117499117500117501117502117503117504117505117506117507117508117509117510117511117512117513117514117515117516117517117518117519117520117521117522117523117524117525117526117527117528117529117530117531117532117533117534117535117536117537117538117539117540117541117542117543117544117545117546117547117548117549117550117551117552117553117554117555117556117557117558117559117560117561117562117563117564117565117566117567117568117569117570117571117572117573117574117575117576117577117578117579117580117581117582117583117584117585117586117587117588117589117590117591117592117593117594117595117596117597117598117599117600117601117602117603117604117605117606117607117608117609117610117611117612117613117614117615117616117617117618117619117620117621117622117623117624117625117626117627117628117629117630117631117632117633117634117635117636117637117638117639117640117641117642117643117644117645117646117647117648117649117650117651117652117653117654117655117656117657117658117659117660117661117662117663117664117665117666117667117668117669117670117671117672117673117674117675117676117677117678117679117680117681117682117683117684117685117686117687117688117689117690117691117692117693117694117695117696117697117698117699117700117701117702117703117704117705117706117707117708117709117710117711117712117713117714117715117716117717117718117719117720117721117722117723117724117725117726117727117728117729117730117731117732117733117734117735117736117737117738117739117740117741117742117743117744117745117746117747117748117749117750117751117752117753117754117755117756117757117758117759117760117761117762117763117764117765117766117767117768117769117770117771117772117773117774117775117776117777117778117779117780117781117782117783117784117785117786117787117788117789117790117791117792117793117794117795117796117797117798117799117800117801117802117803117804117805117806117807117808117809117810117811117812117813117814117815117816117817117818117819117820117821117822117823117824117825117826117827117828117829117830117831117832117833117834117835117836117837117838117839117840117841117842117843117844117845117846117847117848117849117850117851117852117853117854117855117856117857117858117859117860117861117862117863117864117865117866117867117868117869117870117871117872117873117874117875117876117877117878117879117880117881117882117883117884117885117886117887117888117889117890117891117892117893117894117895117896117897117898117899117900117901117902117903117904117905117906117907117908117909117910117911117912117913117914117915117916117917117918117919117920117921117922117923117924117925117926117927117928117929117930117931117932117933117934117935117936117937117938117939117940117941117942117943117944117945117946117947117948117949117950117951117952117953117954117955117956117957117958117959117960117961117962117963117964117965117966117967117968117969117970117971117972117973117974117975117976117977117978117979117980117981117982117983117984117985117986117987117988117989117990117991117992117993117994117995117996117997117998117999118000118001118002118003118004118005118006118007118008118009118010118011118012118013118014118015118016118017118018118019118020118021118022118023118024118025118026118027118028118029118030118031118032118033118034118035118036118037118038118039118040118041118042118043118044118045118046118047118048118049118050118051118052118053118054118055118056118057118058118059118060118061118062118063118064118065118066118067118068118069118070118071118072118073118074118075118076118077118078118079118080118081118082118083118084118085118086118087118088118089118090118091118092118093118094118095118096118097118098118099118100118101118102118103118104118105118106118107118108118109118110118111118112118113118114118115118116118117118118118119118120118121118122118123118124118125118126118127118128118129118130118131118132118133118134118135118136118137118138118139118140118141118142118143118144118145118146118147118148118149118150118151118152118153118154118155118156118157118158118159118160118161118162118163118164118165118166118167118168118169118170118171118172118173118174118175118176118177118178118179118180118181118182118183118184118185118186118187118188118189118190118191118192118193118194118195118196118197118198118199118200118201118202118203118204118205118206118207118208118209118210118211118212118213118214118215118216118217118218118219118220118221118222118223118224118225118226118227118228118229118230118231118232118233118234118235118236118237118238118239118240118241118242118243118244118245118246118247118248118249118250118251118252118253118254118255118256118257118258118259118260118261118262118263118264118265118266118267118268118269118270118271118272118273118274118275118276118277118278118279118280118281118282118283118284118285118286118287118288118289118290118291118292118293118294118295118296118297118298118299118300118301118302118303118304118305118306118307118308118309118310118311118312118313118314118315118316118317118318118319118320118321118322118323118324118325118326118327118328118329118330118331118332118333118334118335118336118337118338118339118340118341118342118343118344118345118346118347118348118349118350118351118352118353118354118355118356118357118358118359118360118361118362118363118364118365118366118367118368118369118370118371118372118373118374118375118376118377118378118379118380118381118382118383118384118385118386118387118388118389118390118391118392118393118394118395118396118397118398118399118400118401118402118403118404118405118406118407118408118409118410118411118412118413118414118415118416118417118418118419118420118421118422118423118424118425118426118427118428118429118430118431118432118433118434118435118436118437118438118439118440118441118442118443118444118445118446118447118448118449118450118451118452118453118454118455118456118457118458118459118460118461118462118463118464118465118466118467118468118469118470118471118472118473118474118475118476118477118478118479118480118481118482118483118484118485118486118487118488118489118490118491118492118493118494118495118496118497118498118499118500118501118502118503118504118505118506118507118508118509118510118511118512118513118514118515118516118517118518118519118520118521118522118523118524118525118526118527118528118529118530118531118532118533118534118535118536118537118538118539118540118541118542118543118544118545118546118547118548118549118550118551118552118553118554118555118556118557118558118559118560118561118562118563118564118565118566118567118568118569118570118571118572118573118574118575118576118577118578118579118580118581118582118583118584118585118586118587118588118589118590118591118592118593118594118595118596118597118598118599118600118601118602118603118604118605118606118607118608118609118610118611118612118613118614118615118616118617118618118619118620118621118622118623118624118625118626118627118628118629118630118631118632118633118634118635118636118637118638118639118640118641118642118643118644118645118646118647118648118649118650118651118652118653118654118655118656118657118658118659118660118661118662118663118664118665118666118667118668118669118670118671118672118673118674118675118676118677118678118679118680118681118682118683118684118685118686118687118688118689118690118691118692118693118694118695118696118697118698118699118700118701118702118703118704118705118706118707118708118709118710118711118712118713118714118715118716118717118718118719118720118721118722118723118724118725118726118727118728118729118730118731118732118733118734118735118736118737118738118739118740118741118742118743118744118745118746118747118748118749118750118751118752118753118754118755118756118757118758118759118760118761118762118763118764118765118766118767118768118769118770118771118772118773118774118775118776118777118778118779118780118781118782118783118784118785118786118787118788118789118790118791118792118793118794118795118796118797118798118799118800118801118802118803118804118805118806118807118808118809118810118811118812118813118814118815118816118817118818118819118820118821118822118823118824118825118826118827118828118829118830118831118832118833118834118835118836118837118838118839118840118841118842118843118844118845118846118847118848118849118850118851118852118853118854118855118856118857118858118859118860118861118862118863118864118865118866118867118868118869118870118871118872118873118874118875118876118877118878118879118880118881118882118883118884118885118886118887118888118889118890118891118892118893118894118895118896118897118898118899118900118901118902118903118904118905118906118907118908118909118910118911118912118913118914118915118916118917118918118919118920118921118922118923118924118925118926118927118928118929118930118931118932118933118934118935118936118937118938118939118940118941118942118943118944118945118946118947118948118949118950118951118952118953118954118955118956118957118958118959118960118961118962118963118964118965118966118967118968118969118970118971118972118973118974118975118976118977118978118979118980118981118982118983118984118985118986118987118988118989118990118991118992118993118994118995118996118997118998118999119000119001119002119003119004119005119006119007119008119009119010119011119012119013119014119015119016119017119018119019119020119021119022119023119024119025119026119027119028119029119030119031119032119033119034119035119036119037119038119039119040119041119042119043119044119045119046119047119048119049119050119051119052119053119054119055119056119057119058119059119060119061119062119063119064119065119066119067119068119069119070119071119072119073119074119075119076119077119078119079119080119081119082119083119084119085119086119087119088119089119090119091119092119093119094119095119096119097119098119099119100119101119102119103119104119105119106119107119108119109119110119111119112119113119114119115119116119117119118119119119120119121119122119123119124119125119126119127119128119129119130119131119132119133119134119135119136119137119138119139119140119141119142119143119144119145119146119147119148119149119150119151119152119153119154119155119156119157119158119159119160119161119162119163119164119165119166119167119168119169119170119171119172119173119174119175119176119177119178119179119180119181119182119183119184119185119186119187119188119189119190119191119192119193119194119195119196119197119198119199119200119201119202119203119204119205119206119207119208119209119210119211119212119213119214119215119216119217119218119219119220119221119222119223119224119225119226119227119228119229119230119231119232119233119234119235119236119237119238119239119240119241119242119243119244119245119246119247119248119249119250119251119252119253119254119255119256119257119258119259119260119261119262119263119264119265119266119267119268119269119270119271119272119273119274119275119276119277119278119279119280119281119282119283119284119285119286119287119288119289119290119291119292119293119294119295119296119297119298119299119300119301119302119303119304119305119306119307119308119309119310119311119312119313119314119315119316119317119318119319119320119321119322119323119324119325119326119327119328119329119330119331119332119333119334119335119336119337119338119339119340119341119342119343119344119345119346119347119348119349119350119351119352119353119354119355119356119357119358119359119360119361119362119363119364119365119366119367119368119369119370119371119372119373119374119375119376119377119378119379119380119381119382119383119384119385119386119387119388119389119390119391119392119393119394119395119396119397119398119399119400119401119402119403119404119405119406119407119408119409119410119411119412119413119414119415119416119417119418119419119420119421119422119423119424119425119426119427119428119429119430119431119432119433119434119435119436119437119438119439119440119441119442119443119444119445119446119447119448119449119450119451119452119453119454119455119456119457119458119459119460119461119462119463119464119465119466119467119468119469119470119471119472119473119474119475119476119477119478119479119480119481119482119483119484119485119486119487119488119489119490119491119492119493119494119495119496119497119498119499119500119501119502119503119504119505119506119507119508119509119510119511119512119513119514119515119516119517119518119519119520119521119522119523119524119525119526119527119528119529119530119531119532119533119534119535119536119537119538119539119540119541119542119543119544119545119546119547119548119549119550119551119552119553119554119555119556119557119558119559119560119561119562119563119564119565119566119567119568119569119570119571119572119573119574119575119576119577119578119579119580119581119582119583119584119585119586119587119588119589119590119591119592119593119594119595119596119597119598119599119600119601119602119603119604119605119606119607119608119609119610119611119612119613119614119615119616119617119618119619119620119621119622119623119624119625119626119627119628119629119630119631119632119633119634119635119636119637119638119639119640119641119642119643119644119645119646119647119648119649119650119651119652119653119654119655119656119657119658119659119660119661119662119663119664119665119666119667119668119669119670119671119672119673119674119675119676119677119678119679119680119681119682119683119684119685119686119687119688119689119690119691119692119693119694119695119696119697119698119699119700119701119702119703119704119705119706119707119708119709119710119711119712119713119714119715119716119717119718119719119720119721119722119723119724119725119726119727119728119729119730119731119732119733119734119735119736119737119738119739119740119741119742119743119744119745119746119747119748119749119750119751119752119753119754119755119756119757119758119759119760119761119762119763119764119765119766119767119768119769119770119771119772119773119774119775119776119777119778119779119780119781119782119783119784119785119786119787119788119789119790119791119792119793119794119795119796119797119798119799119800119801119802119803119804119805119806119807119808119809119810119811119812119813119814119815119816119817119818119819119820119821119822119823119824119825119826119827119828119829119830119831119832119833119834119835119836119837119838119839119840119841119842119843119844119845119846119847119848119849119850119851119852119853119854119855119856119857119858119859119860119861119862119863119864119865119866119867119868119869119870119871119872119873119874119875119876119877119878119879119880119881119882119883119884119885119886119887119888119889119890119891119892119893119894119895119896119897119898119899119900119901119902119903119904119905119906119907119908119909119910119911119912119913119914119915119916119917119918119919119920119921119922119923119924119925119926119927119928119929119930119931119932119933119934119935119936119937119938119939119940119941119942119943119944119945119946119947119948119949119950119951119952119953119954119955119956119957119958119959119960119961119962119963119964119965119966119967119968119969119970119971119972119973119974119975119976119977119978119979119980119981119982119983119984119985119986119987119988119989119990119991119992119993119994119995119996119997119998119999120000120001120002120003120004120005120006120007120008120009120010120011120012120013120014120015120016120017120018120019120020120021120022120023120024120025120026120027120028120029120030120031120032120033120034120035120036120037120038120039120040120041120042120043120044120045120046120047120048120049120050120051120052120053120054120055120056120057120058120059120060120061120062120063120064120065120066120067120068120069120070120071120072120073120074120075120076120077120078120079120080120081120082120083120084120085120086120087120088120089120090120091120092120093120094120095120096120097120098120099120100120101120102120103120104120105120106120107120108120109120110120111120112120113120114120115120116120117120118120119120120120121120122120123120124120125120126120127120128120129120130120131120132120133120134120135120136120137120138120139120140120141120142120143120144120145120146120147120148120149120150120151120152120153120154120155120156120157120158120159120160120161120162120163120164120165120166120167120168120169120170120171120172120173120174120175120176120177120178120179120180120181120182120183120184120185120186120187120188120189120190120191120192120193120194120195120196120197120198120199120200120201120202120203120204120205120206120207120208120209120210120211120212120213120214120215120216120217120218120219120220120221120222120223120224120225120226120227120228120229120230120231120232120233120234120235120236120237120238120239120240120241120242120243120244120245120246120247120248120249120250120251120252120253120254120255120256120257120258120259120260120261120262120263120264120265120266120267120268120269120270120271120272120273120274120275120276120277120278120279120280120281120282120283120284120285120286120287120288120289120290120291120292120293120294120295120296120297120298120299120300120301120302120303120304120305120306120307120308120309120310120311120312120313120314120315120316120317120318120319120320120321120322120323120324120325120326120327120328120329120330120331120332120333120334120335120336120337120338120339120340120341120342120343120344120345120346120347120348120349120350120351120352120353120354120355120356120357120358120359120360120361120362120363120364120365120366120367120368120369120370120371120372120373120374120375120376120377120378120379120380120381120382120383120384120385120386120387120388120389120390120391120392120393120394120395120396120397120398120399120400120401120402120403120404120405120406120407120408120409120410120411120412120413120414120415120416120417120418120419120420120421120422120423120424120425120426120427120428120429120430120431120432120433120434120435120436120437120438120439120440120441120442120443120444120445120446120447120448120449120450120451120452120453120454120455120456120457120458120459120460120461120462120463120464120465120466120467120468120469120470120471120472120473120474120475120476120477120478120479120480120481120482120483120484120485120486120487120488120489120490120491120492120493120494120495120496120497120498120499120500120501120502120503120504120505120506120507120508120509120510120511120512120513120514120515120516120517120518120519120520120521120522120523120524120525120526120527120528120529120530120531120532120533120534120535120536120537120538120539120540120541120542120543120544120545120546120547120548120549120550120551120552120553120554120555120556120557120558120559120560120561120562120563120564120565120566120567120568120569120570120571120572120573120574120575120576120577120578120579120580120581120582120583120584120585120586120587120588120589120590120591120592120593120594120595120596120597120598120599120600120601120602120603120604120605120606120607120608120609120610120611120612120613120614120615120616120617120618120619120620120621120622120623120624120625120626120627120628120629120630120631120632120633120634120635120636120637120638120639120640120641120642120643120644120645120646120647120648120649120650120651120652120653120654120655120656120657120658120659120660120661120662120663120664120665120666120667120668120669120670120671120672120673120674120675120676120677120678120679120680120681120682120683120684120685120686120687120688120689120690120691120692120693120694120695120696120697120698120699120700120701120702120703120704120705120706120707120708120709120710120711120712120713120714120715120716120717120718120719120720120721120722120723120724120725120726120727120728120729120730120731120732120733120734120735120736120737120738120739120740120741120742120743120744120745120746120747120748120749120750120751120752120753120754120755120756120757120758120759120760120761120762120763120764120765120766120767120768120769120770120771120772120773120774120775120776120777120778120779120780120781120782120783120784120785120786120787120788120789120790120791120792120793120794120795120796120797120798120799120800120801120802120803120804120805120806120807120808120809120810120811120812120813120814120815120816120817120818120819120820120821120822120823120824120825120826120827120828120829120830120831120832120833120834120835120836120837120838120839120840120841120842120843120844120845120846120847120848120849120850120851120852120853120854120855120856120857120858120859120860120861120862120863120864120865120866120867120868120869120870120871120872120873120874120875120876120877120878120879120880120881120882120883120884120885120886120887120888120889120890120891120892120893120894120895120896120897120898120899120900120901120902120903120904120905120906120907120908120909120910120911120912120913120914120915120916120917120918120919120920120921120922120923120924120925120926120927120928120929120930120931120932120933120934120935120936120937120938120939120940120941120942120943120944120945120946120947120948120949120950120951120952120953120954120955120956120957120958120959120960120961120962120963120964120965120966120967120968120969120970120971120972120973120974120975120976120977120978120979120980120981120982120983120984120985120986120987120988120989120990120991120992120993120994120995120996120997120998120999121000121001121002121003121004121005121006121007121008121009121010121011121012121013121014121015121016121017121018121019121020121021121022121023121024121025121026121027121028121029121030121031121032121033121034121035121036121037121038121039121040121041121042121043121044121045121046121047121048121049121050121051121052121053121054121055121056121057121058121059121060121061121062121063121064121065121066121067121068121069121070121071121072121073121074121075121076121077121078121079121080121081121082121083121084121085121086121087121088121089121090121091121092121093121094121095121096121097121098121099121100121101121102121103121104121105121106121107121108121109121110121111121112121113121114121115121116121117121118121119121120121121121122121123121124121125121126121127121128121129121130121131121132121133121134121135121136121137121138121139121140121141121142121143121144121145121146121147121148121149121150121151121152121153121154121155121156121157121158121159121160121161121162121163121164121165121166121167121168121169121170121171121172121173121174121175121176121177121178121179121180121181121182121183121184121185121186121187121188121189121190121191121192121193121194121195121196121197121198121199121200121201121202121203121204121205121206121207121208121209121210121211121212121213121214121215121216121217121218121219121220121221121222121223121224121225121226121227121228121229121230121231121232121233121234121235121236121237121238121239121240121241121242121243121244121245121246121247121248121249121250121251121252121253121254121255121256121257121258121259121260121261121262121263121264121265121266121267121268121269121270121271121272121273121274121275121276121277121278121279121280121281121282121283121284121285121286121287121288121289121290121291121292121293121294121295121296121297121298121299121300121301121302121303121304121305121306121307121308121309121310121311121312121313121314121315121316121317121318121319121320121321121322121323121324121325121326121327121328121329121330121331121332121333121334121335121336121337121338121339121340121341121342121343121344121345121346121347121348121349121350121351121352121353121354121355121356121357121358121359121360121361121362121363121364121365121366121367121368121369121370121371121372121373121374121375121376121377121378121379121380121381121382121383121384121385121386121387121388121389121390121391121392121393121394121395121396121397121398121399121400121401121402121403121404121405121406121407121408121409121410121411121412121413121414121415121416121417121418121419121420121421121422121423121424121425121426121427121428121429121430121431121432121433121434121435121436121437121438121439121440121441121442121443121444121445121446121447121448121449121450121451121452121453121454121455121456121457121458121459121460121461121462121463121464121465121466121467121468121469121470121471121472121473121474121475121476121477121478121479121480121481121482121483121484121485121486121487121488121489121490121491121492121493121494121495121496121497121498121499121500121501121502121503121504121505121506121507121508121509121510121511121512121513121514121515121516121517121518121519121520121521121522121523121524121525121526121527121528121529121530121531121532121533121534121535121536121537121538121539121540121541121542121543121544121545121546121547121548121549121550121551121552121553121554121555121556121557121558121559121560121561121562121563121564121565121566121567121568121569121570121571121572121573121574121575121576121577121578121579121580121581121582121583121584121585121586121587121588121589121590121591121592121593121594121595121596121597121598121599121600121601121602121603121604121605121606121607121608121609121610121611121612121613121614121615121616121617121618121619121620121621121622121623121624121625121626121627121628121629121630121631121632121633121634121635121636121637121638121639121640121641121642121643121644121645121646121647121648121649121650121651121652121653121654121655121656121657121658121659121660121661121662121663121664121665121666121667121668121669121670121671121672121673121674121675121676121677121678121679121680121681121682121683121684121685121686121687121688121689121690121691121692121693121694121695121696121697121698121699121700121701121702121703121704121705121706121707121708121709121710121711121712121713121714121715121716121717121718121719121720121721121722121723121724121725121726121727121728121729121730121731121732121733121734121735121736121737121738121739121740121741121742121743121744121745121746121747121748121749121750121751121752121753121754121755121756121757121758121759121760121761121762121763121764121765121766121767121768121769121770121771121772121773121774121775121776121777121778121779121780121781121782121783121784121785121786121787121788121789121790121791121792121793121794121795121796121797121798121799121800121801121802121803121804121805121806121807121808121809121810121811121812121813121814121815121816121817121818121819121820121821121822121823121824121825121826121827121828121829121830121831121832121833121834121835121836121837121838121839121840121841121842121843121844121845121846121847121848121849121850121851121852121853121854121855121856121857121858121859121860121861121862121863121864121865121866121867121868121869121870121871121872121873121874121875121876121877121878121879121880121881121882121883121884121885121886121887121888121889121890121891121892121893121894121895121896121897121898121899121900121901121902121903121904121905121906121907121908121909121910121911121912121913121914121915121916121917121918121919121920121921121922121923121924121925121926121927121928121929121930121931121932121933121934121935121936121937121938121939121940121941121942121943121944121945121946121947121948121949121950121951121952121953121954121955121956121957121958121959121960121961121962121963121964121965121966121967121968121969121970121971121972121973121974121975121976121977121978121979121980121981121982121983121984121985121986121987121988121989121990121991121992121993121994121995121996121997121998121999122000122001122002122003122004122005122006122007122008122009122010122011122012122013122014122015122016122017122018122019122020122021122022122023122024122025122026122027122028122029122030122031122032122033122034122035122036122037122038122039122040122041122042122043122044122045122046122047122048122049122050122051122052122053122054122055122056122057122058122059122060122061122062122063122064122065122066122067122068122069122070122071122072122073122074122075122076122077122078122079122080122081122082122083122084122085122086122087122088122089122090122091122092122093122094122095122096122097122098122099122100122101122102122103122104122105122106122107122108122109122110122111122112122113122114122115122116122117122118122119122120122121122122122123122124122125122126122127122128122129122130122131122132122133122134122135122136122137122138122139122140122141122142122143122144122145122146122147122148122149122150122151122152122153122154122155122156122157122158122159122160122161122162122163122164122165122166122167122168122169122170122171122172122173122174122175122176122177122178122179122180122181122182122183122184122185122186122187122188122189122190122191122192122193122194122195122196122197122198122199122200122201122202122203122204122205122206122207122208122209122210122211122212122213122214122215122216122217122218122219122220122221122222122223122224122225122226122227122228122229122230122231122232122233122234122235122236122237122238122239122240122241122242122243122244122245122246122247122248122249122250122251122252122253122254122255122256122257122258122259122260122261122262122263122264122265122266122267122268122269122270122271122272122273122274122275122276122277122278122279122280122281122282122283122284122285122286122287122288122289122290122291122292122293122294122295122296122297122298122299122300122301122302122303122304122305122306122307122308122309122310122311122312122313122314122315122316122317122318122319122320122321122322122323122324122325122326122327122328122329122330122331122332122333122334122335122336122337122338122339122340122341122342122343122344122345122346122347122348122349122350122351122352122353122354122355122356122357122358122359122360122361122362122363122364122365122366122367122368122369122370122371122372122373122374122375122376122377122378122379122380122381122382122383122384122385122386122387122388122389122390122391122392122393122394122395122396122397122398122399122400122401122402122403122404122405122406122407122408122409122410122411122412122413122414122415122416122417122418122419122420122421122422122423122424122425122426122427122428122429122430122431122432122433122434122435122436122437122438122439122440122441122442122443122444122445122446122447122448122449122450122451122452122453122454122455122456122457122458122459122460122461122462122463122464122465122466122467122468122469122470122471122472122473122474122475122476122477122478122479122480122481122482122483122484122485122486122487122488122489122490122491122492122493122494122495122496122497122498122499122500122501122502122503122504122505122506122507122508122509122510122511122512122513122514122515122516122517122518122519122520122521122522122523122524122525122526122527122528122529122530122531122532122533122534122535122536122537122538122539122540122541122542122543122544122545122546122547122548122549122550122551122552122553122554122555122556122557122558122559122560122561122562122563122564122565122566122567122568122569122570122571122572122573122574122575122576122577122578122579122580122581122582122583122584122585122586122587122588122589122590122591122592122593122594122595122596122597122598122599122600122601122602122603122604122605122606122607122608122609122610122611122612122613122614122615122616122617122618122619122620122621122622122623122624122625122626122627122628122629122630122631122632122633122634122635122636122637122638122639122640122641122642122643122644122645122646122647122648122649122650122651122652122653122654122655122656122657122658122659122660122661122662122663122664122665122666122667122668122669122670122671122672122673122674122675122676122677122678122679122680122681122682122683122684122685122686122687122688122689122690122691122692122693122694122695122696122697122698122699122700122701122702122703122704122705122706122707122708122709122710122711122712122713122714122715122716122717122718122719122720122721122722122723122724122725122726122727122728122729122730122731122732122733122734122735122736122737122738122739122740122741122742122743122744122745122746122747122748122749122750122751122752122753122754122755122756122757122758122759122760122761122762122763122764122765122766122767122768122769122770122771122772122773122774122775122776122777122778122779122780122781122782122783122784122785122786122787122788122789122790122791122792122793122794122795122796122797122798122799122800122801122802122803122804122805122806122807122808122809122810122811122812122813122814122815122816122817122818122819122820122821122822122823122824122825122826122827122828122829122830122831122832122833122834122835122836122837122838122839122840122841122842122843122844122845122846122847122848122849122850122851122852122853122854122855122856122857122858122859122860122861122862122863122864122865122866122867122868122869122870122871122872122873122874122875122876122877122878122879122880122881122882122883122884122885122886122887122888122889122890122891122892122893122894122895122896122897122898122899122900122901122902122903122904122905122906122907122908122909122910122911122912122913122914122915122916122917122918122919122920122921122922122923122924122925122926122927122928122929122930122931122932122933122934122935122936122937122938122939122940122941122942122943122944122945122946122947122948122949122950122951122952122953122954122955122956122957122958122959122960122961122962122963122964122965122966122967122968122969122970122971122972122973122974122975122976122977122978122979122980122981122982122983122984122985122986122987122988122989122990122991122992122993122994122995122996122997122998122999123000123001123002123003123004123005123006123007123008123009123010123011123012123013123014123015123016123017123018123019123020123021123022123023123024123025123026123027123028123029123030123031123032123033123034123035123036123037123038123039123040123041123042123043123044123045123046123047123048123049123050123051123052123053123054123055123056123057123058123059123060123061123062123063123064123065123066123067123068123069123070123071123072123073123074123075123076123077123078123079123080123081123082123083123084123085123086123087123088123089123090123091123092123093123094123095123096123097123098123099123100123101123102123103123104123105123106123107123108123109123110123111123112123113123114123115123116123117123118123119123120123121123122123123123124123125123126123127123128123129123130123131123132123133123134123135123136123137123138123139123140123141123142123143123144123145123146123147123148123149123150123151123152123153123154123155123156123157123158123159123160123161123162123163123164123165123166123167123168123169123170123171123172123173123174123175123176123177123178123179123180123181123182123183123184123185123186123187123188123189123190123191123192123193123194123195123196123197123198123199123200123201123202123203123204123205123206123207123208123209123210123211123212123213123214123215123216123217123218123219123220123221123222123223123224123225123226123227123228123229123230123231123232123233123234123235123236123237123238123239123240123241123242123243123244123245123246123247123248123249123250123251123252123253123254123255123256123257123258123259123260123261123262123263123264123265123266123267123268123269123270123271123272123273123274123275123276123277123278123279123280123281123282123283123284123285123286123287123288123289123290123291123292123293123294123295123296123297123298123299123300123301123302123303123304123305123306123307123308123309123310123311123312123313123314123315123316123317123318123319123320123321123322123323123324123325123326123327123328123329123330123331123332123333123334123335123336123337123338123339123340123341123342123343123344123345123346123347123348123349123350123351123352123353123354123355123356123357123358123359123360123361123362123363123364123365123366123367123368123369123370123371123372123373123374123375123376123377123378123379123380123381123382123383123384123385123386123387123388123389123390123391123392123393123394123395123396123397123398123399123400123401123402123403123404123405123406123407123408123409123410123411123412123413123414123415123416123417123418123419123420123421123422123423123424123425123426123427123428123429123430123431123432123433123434123435123436123437123438123439123440123441123442123443123444123445123446123447123448123449123450123451123452123453123454123455123456123457123458123459123460123461123462123463123464123465123466123467123468123469123470123471123472123473123474123475123476123477123478123479123480123481123482123483123484123485123486123487123488123489123490123491123492123493123494123495123496123497123498123499123500123501123502123503123504123505123506123507123508123509123510123511123512123513123514123515123516123517123518123519123520123521123522123523123524123525123526123527123528123529123530123531123532123533123534123535123536123537123538123539123540123541123542123543123544123545123546123547123548123549123550123551123552123553123554123555123556123557123558123559123560123561123562123563123564123565123566123567123568123569123570123571123572123573123574123575123576123577123578123579123580123581123582123583123584123585123586123587123588123589123590123591123592123593123594123595123596123597123598123599123600123601123602123603123604123605123606123607123608123609123610123611123612123613123614123615123616123617123618123619123620123621123622123623123624123625123626123627123628123629123630123631123632123633123634123635123636123637123638123639123640123641123642123643123644123645123646123647123648123649123650123651123652123653123654123655123656123657123658123659123660123661123662123663123664123665123666123667123668123669123670123671123672123673123674123675123676123677123678123679123680123681123682123683123684123685123686123687123688123689123690123691123692123693123694123695123696123697123698123699123700123701123702123703123704123705123706123707123708123709123710123711123712123713123714123715123716123717123718123719123720123721123722123723123724123725123726123727123728123729123730123731123732123733123734123735123736123737123738123739123740123741123742123743123744123745123746123747123748123749123750123751123752123753123754123755123756123757123758123759123760123761123762123763123764123765123766123767123768123769123770123771123772123773123774123775123776123777123778123779123780123781123782123783123784123785123786123787123788123789123790123791123792123793123794123795123796123797123798123799123800123801123802123803123804123805123806123807123808123809123810123811123812123813123814123815123816123817123818123819123820123821123822123823123824123825123826123827123828123829123830123831123832123833123834123835123836123837123838123839123840123841123842123843123844123845123846123847123848123849123850123851123852123853123854123855123856123857123858123859123860123861123862123863123864123865123866123867123868123869123870123871123872123873123874123875123876123877123878123879123880123881123882123883123884123885123886123887123888123889123890123891123892123893123894123895123896123897123898123899123900123901123902123903123904123905123906123907123908123909123910123911123912123913123914123915123916123917123918123919123920123921123922123923123924123925123926123927123928123929123930123931123932123933123934123935123936123937123938123939123940123941123942123943123944123945123946123947123948123949123950123951123952123953123954123955123956123957123958123959123960123961123962123963123964123965123966123967123968123969123970123971123972123973123974123975123976123977123978123979123980123981123982123983123984123985123986123987123988123989123990123991123992123993123994123995123996123997123998123999124000124001124002124003124004124005124006124007124008124009124010124011124012124013124014124015124016124017124018124019124020124021124022124023124024124025124026124027124028124029124030124031124032124033124034124035124036124037124038124039124040124041124042124043124044124045124046124047124048124049124050124051124052124053124054124055124056124057124058124059124060124061124062124063124064124065124066124067124068124069124070124071124072124073124074124075124076124077124078124079124080124081124082124083124084124085124086124087124088124089124090124091124092124093124094124095124096124097124098124099124100124101124102124103124104124105124106124107124108124109124110124111124112124113124114124115124116124117124118124119124120124121124122124123124124124125124126124127124128124129124130124131124132124133124134124135124136124137124138124139124140124141124142124143124144124145124146124147124148124149124150124151124152124153124154124155124156124157124158124159124160124161124162124163124164124165124166124167124168124169124170124171124172124173124174124175124176124177124178124179124180124181124182124183124184124185124186124187124188124189124190124191124192124193124194124195124196124197124198124199124200124201124202124203124204124205124206124207124208124209124210124211124212124213124214124215124216124217124218124219124220124221124222124223124224124225124226124227124228124229124230124231124232124233124234124235124236124237124238124239124240124241124242124243124244124245124246124247124248124249124250124251124252124253124254124255124256124257124258124259124260124261124262124263124264124265124266124267124268124269124270124271124272124273124274124275124276124277124278124279124280124281124282124283124284124285124286124287124288124289124290124291124292124293124294124295124296124297124298124299124300124301124302124303124304124305124306124307124308124309124310124311124312124313124314124315124316124317124318124319124320124321124322124323124324124325124326124327124328124329124330124331124332124333124334124335124336124337124338124339124340124341124342124343124344124345124346124347124348124349124350124351124352124353124354124355124356124357124358124359124360124361124362124363124364124365124366124367124368124369124370124371124372124373124374124375124376124377124378124379124380124381124382124383124384124385124386124387124388124389124390124391124392124393124394124395124396124397124398124399124400124401124402124403124404124405124406124407124408124409124410124411124412124413124414124415124416124417124418124419124420124421124422124423124424124425124426124427124428124429124430124431124432124433124434124435124436124437124438124439124440124441124442124443124444124445124446124447124448124449124450124451124452124453124454124455124456124457124458124459124460124461124462124463124464124465124466124467124468124469124470124471124472124473124474124475124476124477124478124479124480124481124482124483124484124485124486124487124488124489124490124491124492124493124494124495124496124497124498124499124500124501124502124503124504124505124506124507124508124509124510124511124512124513124514124515124516124517124518124519124520124521124522124523124524124525124526124527124528124529124530124531124532124533124534124535124536124537124538124539124540124541124542124543124544124545124546124547124548124549124550124551124552124553124554124555124556124557124558124559124560124561124562124563124564124565124566124567124568124569124570124571124572124573124574124575124576124577124578124579124580124581124582124583124584124585124586124587124588124589124590124591124592124593124594124595124596124597124598124599124600124601124602124603124604124605124606124607124608124609124610124611124612124613124614124615124616124617124618124619124620124621124622124623124624124625124626124627124628124629124630124631124632124633124634124635124636124637124638124639124640124641124642124643124644124645124646124647124648124649124650124651124652124653124654124655124656124657124658124659124660124661124662124663124664124665124666124667124668124669124670124671124672124673124674124675124676124677124678124679124680124681124682124683124684124685124686124687124688124689124690124691124692124693124694124695124696124697124698124699124700124701124702124703124704124705124706124707124708124709124710124711124712124713124714124715124716124717124718124719124720124721124722124723124724124725124726124727124728124729124730124731124732124733124734124735124736124737124738124739124740124741124742124743124744124745124746124747124748124749124750124751124752124753124754124755124756124757124758124759124760124761124762124763124764124765124766124767124768124769124770124771124772124773124774124775124776124777124778124779124780124781124782124783124784124785124786124787124788124789124790124791124792124793124794124795124796124797124798124799124800124801124802124803124804124805124806124807124808124809124810124811124812124813124814124815124816124817124818124819124820124821124822124823124824124825124826124827124828124829124830124831124832124833124834124835124836124837124838124839124840124841124842124843124844124845124846124847124848124849124850124851124852124853124854124855124856124857124858124859124860124861124862124863124864124865124866124867124868124869124870124871124872124873124874124875124876124877124878124879124880124881124882124883124884124885124886124887124888124889124890124891124892124893124894124895124896124897124898124899124900124901124902124903124904124905124906124907124908124909124910124911124912124913124914124915124916124917124918124919124920124921124922124923124924124925124926124927124928124929124930124931124932124933124934124935124936124937124938124939124940124941124942124943124944124945124946124947124948124949124950124951124952124953124954124955124956124957124958124959124960124961124962124963124964124965124966124967124968124969124970124971124972124973124974124975124976124977124978124979124980124981124982124983124984124985124986124987124988124989124990124991124992124993124994124995124996124997124998124999125000125001125002125003125004125005125006125007125008125009125010125011125012125013125014125015125016125017125018125019125020125021125022125023125024125025125026125027125028125029125030125031125032125033125034125035125036125037125038125039125040125041125042125043125044125045125046125047125048125049125050125051125052125053125054125055125056125057125058125059125060125061125062125063125064125065125066125067125068125069125070125071125072125073125074125075125076125077125078125079125080125081125082125083125084125085125086125087125088125089125090125091125092125093125094125095125096125097125098125099125100125101125102125103125104125105125106125107125108125109125110125111125112125113125114125115125116125117125118125119125120125121125122125123125124125125125126125127125128125129125130125131125132125133125134125135125136125137125138125139125140125141125142125143125144125145125146125147125148125149125150125151125152125153125154125155125156125157125158125159125160125161125162125163125164125165125166125167125168125169125170125171125172125173125174125175125176125177125178125179125180125181125182125183125184125185125186125187125188125189125190125191125192125193125194125195125196125197125198125199125200125201125202125203125204125205125206125207125208125209125210125211125212125213125214125215125216125217125218125219125220125221125222125223125224125225125226125227125228125229125230125231125232125233125234125235125236125237125238125239125240125241125242125243125244125245125246125247125248125249125250125251125252125253125254125255125256125257125258125259125260125261125262125263125264125265125266125267125268125269125270125271125272125273125274125275125276125277125278125279125280125281125282125283125284125285125286125287125288125289125290125291125292125293125294125295125296125297125298125299125300125301125302125303125304125305125306125307125308125309125310125311125312125313125314125315125316125317125318125319125320125321125322125323125324125325125326125327125328125329125330125331125332125333125334125335125336125337125338125339125340125341125342125343125344125345125346125347125348125349125350125351125352125353125354125355125356125357125358125359125360125361125362125363125364125365125366125367125368125369125370125371125372125373125374125375125376125377125378125379125380125381125382125383125384125385125386125387125388125389125390125391125392125393125394125395125396125397125398125399125400125401125402125403125404125405125406125407125408125409125410125411125412125413125414125415125416125417125418125419125420125421125422125423125424125425125426125427125428125429125430125431125432125433125434125435125436125437125438125439125440125441125442125443125444125445125446125447125448125449125450125451125452125453125454125455125456125457125458125459125460125461125462125463125464125465125466125467125468125469125470125471125472125473125474125475125476125477125478125479125480125481125482125483125484125485125486125487125488125489125490125491125492125493125494125495125496125497125498125499125500125501125502125503125504125505125506125507125508125509125510125511125512125513125514125515125516125517125518125519125520125521125522125523125524125525125526125527125528125529125530125531125532125533125534125535125536125537125538125539125540125541125542125543125544125545125546125547125548125549125550125551125552125553125554125555125556125557125558125559125560125561125562125563125564125565125566125567125568125569125570125571125572125573125574125575125576125577125578125579125580125581125582125583125584125585125586125587125588125589125590125591125592125593125594125595125596125597125598125599125600125601125602125603125604125605125606125607125608125609125610125611125612125613125614125615125616125617125618125619125620125621125622125623125624125625125626125627125628125629125630125631125632125633125634125635125636125637125638125639125640125641125642125643125644125645125646125647125648125649125650125651125652125653125654125655125656125657125658125659125660125661125662125663125664125665125666125667125668125669125670125671125672125673125674125675125676125677125678125679125680125681125682125683125684125685125686125687125688125689125690125691125692125693125694125695125696125697125698125699125700125701125702125703125704125705125706125707125708125709125710125711125712125713125714125715125716125717125718125719125720125721125722125723125724125725125726125727125728125729125730125731125732125733125734125735125736125737125738125739125740125741125742125743125744125745125746125747125748125749125750125751125752125753125754125755125756125757125758125759125760125761125762125763125764125765125766125767125768125769125770125771125772125773125774125775125776125777125778125779125780125781125782125783125784125785125786125787125788125789125790125791125792125793125794125795125796125797125798125799125800125801125802125803125804125805125806125807125808125809125810125811125812125813125814125815125816125817125818125819125820125821125822125823125824125825125826125827125828125829125830125831125832125833125834125835125836125837125838125839125840125841125842125843125844125845125846125847125848125849125850125851125852125853125854125855125856125857125858125859125860125861125862125863125864125865125866125867125868125869125870125871125872125873125874125875125876125877125878125879125880125881125882125883125884125885125886125887125888125889125890125891125892125893125894125895125896125897125898125899125900125901125902125903125904125905125906125907125908125909125910125911125912125913125914125915125916125917125918125919125920125921125922125923125924125925125926125927125928125929125930125931125932125933125934125935125936125937125938125939125940125941125942125943125944125945125946125947125948125949125950125951125952125953125954125955125956125957125958125959125960125961125962125963125964125965125966125967125968125969125970125971125972125973125974125975125976125977125978125979125980125981125982125983125984125985125986125987125988125989125990125991125992125993125994125995125996125997125998125999126000126001126002126003126004126005126006126007126008126009126010126011126012126013126014126015126016126017126018126019126020126021126022126023126024126025126026126027126028126029126030126031126032126033126034126035126036126037126038126039126040126041126042126043126044126045126046126047126048126049126050126051126052126053126054126055126056126057126058126059126060126061126062126063126064126065126066126067126068126069126070126071126072126073126074126075126076126077126078126079126080126081126082126083126084126085126086126087126088126089126090126091126092126093126094126095126096126097126098126099126100126101126102126103126104126105126106126107126108126109126110126111126112126113126114126115126116126117126118126119126120126121126122126123126124126125126126126127126128126129126130126131126132126133126134126135126136126137126138126139126140126141126142126143126144126145126146126147126148126149126150126151126152126153126154126155126156126157126158126159126160126161126162126163126164126165126166126167126168126169126170126171126172126173126174126175126176126177126178126179126180126181126182126183126184126185126186126187126188126189126190126191126192126193126194126195126196126197126198126199126200126201126202126203126204126205126206126207126208126209126210126211126212126213126214126215126216126217126218126219126220126221126222126223126224126225126226126227126228126229126230126231126232126233126234126235126236126237126238126239126240126241126242126243126244126245126246126247126248126249126250126251126252126253126254126255126256126257126258126259126260126261126262126263126264126265126266126267126268126269126270126271126272126273126274126275126276126277126278126279126280126281126282126283126284126285126286126287126288126289126290126291126292126293126294126295126296126297126298126299126300126301126302126303126304126305126306126307126308126309126310126311126312126313126314126315126316126317126318126319126320126321126322126323126324126325126326126327126328126329126330126331126332126333126334126335126336126337126338126339126340126341126342126343126344126345126346126347126348126349126350126351126352126353126354126355126356126357126358126359126360126361126362126363126364126365126366126367126368126369126370126371126372126373126374126375126376126377126378126379126380126381126382126383126384126385126386126387126388126389126390126391126392126393126394126395126396126397126398126399126400126401126402126403126404126405126406126407126408126409126410126411126412126413126414126415126416126417126418126419126420126421126422126423126424126425126426126427126428126429126430126431126432126433126434126435126436126437126438126439126440126441126442126443126444126445126446126447126448126449126450126451126452126453126454126455126456126457126458126459126460126461126462126463126464126465126466126467126468126469126470126471126472126473126474126475126476126477126478126479126480126481126482126483126484126485126486126487126488126489126490126491126492126493126494126495126496126497126498126499126500126501126502126503126504126505126506126507126508126509126510126511126512126513126514126515126516126517126518126519126520126521126522126523126524126525126526126527126528126529126530126531126532126533126534126535126536126537126538126539126540126541126542126543126544126545126546126547126548126549126550126551126552126553126554126555126556126557126558126559126560126561126562126563126564126565126566126567126568126569126570126571126572126573126574126575126576126577126578126579126580126581126582126583126584126585126586126587126588126589126590126591126592126593126594126595126596126597126598126599126600126601126602126603126604126605126606126607126608126609126610126611126612126613126614126615126616126617126618126619126620126621126622126623126624126625126626126627126628126629126630126631126632126633126634126635126636126637126638126639126640126641126642126643126644126645126646126647126648126649126650126651126652126653126654126655126656126657126658126659126660126661126662126663126664126665126666126667126668126669126670126671126672126673126674126675126676126677126678126679126680126681126682126683126684126685126686126687126688126689126690126691126692126693126694126695126696126697126698126699126700126701126702126703126704126705126706126707126708126709126710126711126712126713126714126715126716126717126718126719126720126721126722126723126724126725126726126727126728126729126730126731126732126733126734126735126736126737126738126739126740126741126742126743126744126745126746126747126748126749126750126751126752126753126754126755126756126757126758126759126760126761126762126763126764126765126766126767126768126769126770126771126772126773126774126775126776126777126778126779126780126781126782126783126784126785126786126787126788126789126790126791126792126793126794126795126796126797126798126799126800126801126802126803126804126805126806126807126808126809126810126811126812126813126814126815126816126817126818126819126820126821126822126823126824126825126826126827126828126829126830126831126832126833126834126835126836126837126838126839126840126841126842126843126844126845126846126847126848126849126850126851126852126853126854126855126856126857126858126859126860126861126862126863126864126865126866126867126868126869126870126871126872126873126874126875126876126877126878126879126880126881126882126883126884126885126886126887126888126889126890126891126892126893126894126895126896126897126898126899126900126901126902126903126904126905126906126907126908126909126910126911126912126913126914126915126916126917126918126919126920126921126922126923126924126925126926126927126928126929126930126931126932126933126934126935126936126937126938126939126940126941126942126943126944126945126946126947126948126949126950126951126952126953126954126955126956126957126958126959126960126961126962126963126964126965126966126967126968126969126970126971126972126973126974126975126976126977126978126979126980126981126982126983126984126985126986126987126988126989126990126991126992126993126994126995126996126997126998126999127000127001127002127003127004127005127006127007127008127009127010127011127012127013127014127015127016127017127018127019127020127021127022127023127024127025127026127027127028127029127030127031127032127033127034127035127036127037127038127039127040127041127042127043127044127045127046127047127048127049127050127051127052127053127054127055127056127057127058127059127060127061127062127063127064127065127066127067127068127069127070127071127072127073127074127075127076127077127078127079127080127081127082127083127084127085127086127087127088127089127090127091127092127093127094127095127096127097127098127099127100127101127102127103127104127105127106127107127108127109127110127111127112127113127114127115127116127117127118127119127120127121127122127123127124127125127126127127127128127129127130127131127132127133127134127135127136127137127138127139127140127141127142127143127144127145127146127147127148127149127150127151127152127153127154127155127156127157127158127159127160127161127162127163127164127165127166127167127168127169127170127171127172127173127174127175127176127177127178127179127180127181127182127183127184127185127186127187127188127189127190127191127192127193127194127195127196127197127198127199127200127201127202127203127204127205127206127207127208127209127210127211127212127213127214127215127216127217127218127219127220127221127222127223127224127225127226127227127228127229127230127231127232127233127234127235127236127237127238127239127240127241127242127243127244127245127246127247127248127249127250127251127252127253127254127255127256127257127258127259127260127261127262127263127264127265127266127267127268127269127270127271127272127273127274127275127276127277127278127279127280127281127282127283127284127285127286127287127288127289127290127291127292127293127294127295127296127297127298127299127300127301127302127303127304127305127306127307127308127309127310127311127312127313127314127315127316127317127318127319127320127321127322127323127324127325127326127327127328127329127330127331127332127333127334127335127336127337127338127339127340127341127342127343127344127345127346127347127348127349127350127351127352127353127354127355127356127357127358127359127360127361127362127363127364127365127366127367127368127369127370127371127372127373127374127375127376127377127378127379127380127381127382127383127384127385127386127387127388127389127390127391127392127393127394127395127396127397127398127399127400127401127402127403127404127405127406127407127408127409127410127411127412127413127414127415127416127417127418127419127420127421127422127423127424127425127426127427127428127429127430127431127432127433127434127435127436127437127438127439127440127441127442127443127444127445127446127447127448127449127450127451127452127453127454127455127456127457127458127459127460127461127462127463127464127465127466127467127468127469127470127471127472127473127474127475127476127477127478127479127480127481127482127483127484127485127486127487127488127489127490127491127492127493127494127495127496127497127498127499127500127501127502127503127504127505127506127507127508127509127510127511127512127513127514127515127516127517127518127519127520127521127522127523127524127525127526127527127528127529127530127531127532127533127534127535127536127537127538127539127540127541127542127543127544127545127546127547127548127549127550127551127552127553127554127555127556127557127558127559127560127561127562127563127564127565127566127567127568127569127570127571127572127573127574127575127576127577127578127579127580127581127582127583127584127585127586127587127588127589127590127591127592127593127594127595127596127597127598127599127600127601127602127603127604127605127606127607127608127609127610127611127612127613127614127615127616127617127618127619127620127621127622127623127624127625127626127627127628127629127630127631127632127633127634127635127636127637127638127639127640127641127642127643127644127645127646127647127648127649127650127651127652127653127654127655127656127657127658127659127660127661127662127663127664127665127666127667127668127669127670127671127672127673127674127675127676127677127678127679127680127681127682127683127684127685127686127687127688127689127690127691127692127693127694127695127696127697127698127699127700127701127702127703127704127705127706127707127708127709127710127711127712127713127714127715127716127717127718127719127720127721127722127723127724127725127726127727127728127729127730127731127732127733127734127735127736127737127738127739127740127741127742127743127744127745127746127747127748127749127750127751127752127753127754127755127756127757127758127759127760127761127762127763127764127765127766127767127768127769127770127771127772127773127774127775127776127777127778127779127780127781127782127783127784127785127786127787127788127789127790127791127792127793127794127795127796127797127798127799127800127801127802127803127804127805127806127807127808127809127810127811127812127813127814127815127816127817127818127819127820127821127822127823127824127825127826127827127828127829127830127831127832127833127834127835127836127837127838127839127840127841127842127843127844127845127846127847127848127849127850127851127852127853127854127855127856127857127858127859127860127861127862127863127864127865127866127867127868127869127870127871127872127873127874127875127876127877127878127879127880127881127882127883127884127885127886127887127888127889127890127891127892127893127894127895127896127897127898127899127900127901127902127903127904127905127906127907127908127909127910127911127912127913127914127915127916127917127918127919127920127921127922127923127924127925127926127927127928127929127930127931127932127933127934127935127936127937127938127939127940127941127942127943127944127945127946127947127948127949127950127951127952127953127954127955127956127957127958127959127960127961127962127963127964127965127966127967127968127969127970127971127972127973127974127975127976127977127978127979127980127981127982127983127984127985127986127987127988127989127990127991127992127993127994127995127996127997127998127999128000128001128002128003128004128005128006128007128008128009128010128011128012128013128014128015128016128017128018128019128020128021128022128023128024128025128026128027128028128029128030128031128032128033128034128035128036128037128038128039128040128041128042128043128044128045128046128047128048128049128050128051128052128053128054128055128056128057128058128059128060128061128062128063128064128065128066128067128068128069128070128071128072128073128074128075128076128077128078128079128080128081128082128083128084128085128086128087128088128089128090128091128092128093128094128095128096128097128098128099128100128101128102128103128104128105128106128107128108128109128110128111128112128113128114128115128116128117128118128119128120128121128122128123128124128125128126128127128128128129128130128131128132128133128134128135128136128137128138128139128140128141128142128143128144128145128146128147128148128149128150128151128152128153128154128155128156128157128158128159128160128161128162128163128164128165128166128167128168128169128170128171128172128173128174128175128176128177128178128179128180128181128182128183128184128185128186128187128188128189128190128191128192128193128194128195128196128197128198128199128200128201128202128203128204128205128206128207128208128209128210128211128212128213128214128215128216128217128218128219128220128221128222128223128224128225128226128227128228128229128230128231128232128233128234128235128236128237128238128239128240128241128242128243128244128245128246128247128248128249128250128251128252128253128254128255128256128257128258128259128260128261128262128263128264128265128266128267128268128269128270128271128272128273128274128275128276128277128278128279128280128281128282128283128284128285128286128287128288128289128290128291128292128293128294128295128296128297128298128299128300128301128302128303128304128305128306128307128308128309128310128311128312128313128314128315128316128317128318128319128320128321128322128323128324128325128326128327128328128329128330128331128332128333128334128335128336128337128338128339128340128341128342128343128344128345128346128347128348128349128350128351128352128353128354128355128356128357128358128359128360128361128362128363128364128365128366128367128368128369128370128371128372 |
- (function (global, factory) {
- typeof exports === 'object' && typeof module !== 'undefined' ? factory(exports) :
- typeof define === 'function' && define.amd ? define(['exports'], factory) :
- (global = global || self, factory(global.Potree = {}));
- }(this, (function (exports) { 'use strict';
- function mobileVersion(e, t) {
- //ios的版本
- var i = window.navigator.userAgent,
- n = i.match(e);
- return (
- (n = n ? n[1].split(t) : []),
- {
- major: parseInt(n[0]) || 0,
- minor: parseInt(n[1]) || 0,
- patch: parseInt(n[2]) || 0,
- }
- )
- }
- var browser = {
- isFullscreen: function() {
- return (
- document.fullscreenElement ||
- document.mozFullscreenElement ||
- document.mozFullScreenElement ||
- document.webkitFullscreenElement ||
- document.msFullscreenElement
- )
- },
- supportsFullscreen: function() {
- return (
- document.fullscreenEnabled ||
- document.mozFullscreenEnabled ||
- document.mozFullScreenEnabled ||
- document.webkitFullscreenEnabled ||
- document.msFullscreenEnabled
- )
- },
- isPointerLocked: function() {
- return (
- document.pointerLockElement ||
- document.mozPointerLockElement ||
- document.webkitPointerLockElement
- )
- },
- requestFullscreen: function(dom, t) {
- dom.requestFullscreen ?
- dom.requestFullscreen() :
- dom.mozRequestFullScreen ?
- dom.mozRequestFullScreen() :
- dom.webkitRequestFullscreen ?
- dom.webkitRequestFullscreen(Element.ALLOW_KEYBOARD_INPUT) :
- dom.msRequestFullscreen && dom.msRequestFullscreen(),
- t &&
- $(document).on(
- "fullscreenchange webkitfullscreenchange mozfullscreenchange MSFullscreenChange",
- browser.requestPointerLock
- );
- },
- requestPointerLock: function() {
- var e;
- if (document.fullscreenElement) e = document.fullscreenElement();
- else if (document.mozFullscreenElement) e = document.mozFullscreenElement();
- else if (document.mozFullScreenElement) e = document.mozFullScreenElement();
- else {
- if (!document.webkitFullscreenElement) return
- e = document.webkitFullscreenElement();
- };
- (e.requestPointerLock =
- e.requestPointerLock || e.mozRequestPointerLock || e.webkitRequestPointerLock),
- e.requestPointerLock(),
- $(document).off(
- "fullscreenchange webkitfullscreenchange mozfullscreenchange MSFullscreenChange",
- this
- );
- },
- exitPointerLock: function() {
- ;
- (document.exitPointerLock =
- document.exitPointerLock ||
- document.mozExitPointerLock ||
- document.webkitExitPointerLock),
- document.exitPointerLock();
- },
- exitFullscreen: function() {
- document.exitFullscreen ?
- document.exitFullscreen() :
- document.msExitFullscreen ?
- document.msExitFullscreen() :
- document.mozCancelFullScreen ?
- document.mozCancelFullScreen() :
- document.webkitExitFullscreen && document.webkitExitFullscreen();
- },
- details: function() {
- var e = navigator.userAgent.match("(Firefox|Chrome|Safari)/([\\d]+)");
- return e ?
- {
- name: e[1],
- version: parseInt(e[2]),
- platform: navigator.platform,
- } : {}
- },
- is: function(e) {
- return this.details() && this.details().name === e
- },
- inIframe: function() {
- return window.parent !== window
- },
- aspectRatio: function($elem) {
- $elem = $elem || $("#player");
- var e = $elem.width() / $elem.height();
- return isFinite(e) ? e : 0
- },
- userAgent: function() {
- return window.navigator.userAgent
- },
- isMobile: function() {
- var e = navigator.userAgent || navigator.vendor || window.opera;
- return (
- /(android|bb\d+|meego).+mobile|android|avantgo|bada\/|blackberry|blazer|compal|elaine|fennec|hiptop|iemobile|ip(hone|od|ad)|iris|kindle|lge |maemo|midp|mmp|mobile.+firefox|netfront|opera m(ob|in)i|palm( os)?|phone|p(ixi|re)\/|plucker|pocket|psp|series(4|6)0|symbian|treo|up\.(browser|link)|vodafone|wap|windows ce|xda|xiino/i.test(
- e
- ) ||
- /1207|6310|6590|3gso|4thp|50[1-6]i|770s|802s|a wa|abac|ac(er|oo|s\-)|ai(ko|rn)|al(av|ca|co)|amoi|an(ex|ny|yw)|aptu|ar(ch|go)|as(te|us)|attw|au(di|\-m|r |s )|avan|be(ck|ll|nq)|bi(lb|rd)|bl(ac|az)|br(e|v)w|bumb|bw\-(n|u)|c55\/|capi|ccwa|cdm\-|cell|chtm|cldc|cmd\-|co(mp|nd)|craw|da(it|ll|ng)|dbte|dc\-s|devi|dica|dmob|do(c|p)o|ds(12|\-d)|el(49|ai)|em(l2|ul)|er(ic|k0)|esl8|ez([4-7]0|os|wa|ze)|fetc|fly(\-|_)|g1 u|g560|gene|gf\-5|g\-mo|go(\.w|od)|gr(ad|un)|haie|hcit|hd\-(m|p|t)|hei\-|hi(pt|ta)|hp( i|ip)|hs\-c|ht(c(\-| |_|a|g|p|s|t)|tp)|hu(aw|tc)|i\-(20|go|ma)|i230|iac( |\-|\/)|ibro|idea|ig01|ikom|im1k|inno|ipaq|iris|ja(t|v)a|jbro|jemu|jigs|kddi|keji|kgt( |\/)|klon|kpt |kwc\-|kyo(c|k)|le(no|xi)|lg( g|\/(k|l|u)|50|54|\-[a-w])|libw|lynx|m1\-w|m3ga|m50\/|ma(te|ui|xo)|mc(01|21|ca)|m\-cr|me(rc|ri)|mi(o8|oa|ts)|mmef|mo(01|02|bi|de|do|t(\-| |o|v)|zz)|mt(50|p1|v )|mwbp|mywa|n10[0-2]|n20[2-3]|n30(0|2)|n50(0|2|5)|n7(0(0|1)|10)|ne((c|m)\-|on|tf|wf|wg|wt)|nok(6|i)|nzph|o2im|op(ti|wv)|oran|owg1|p800|pan(a|d|t)|pdxg|pg(13|\-([1-8]|c))|phil|pire|pl(ay|uc)|pn\-2|po(ck|rt|se)|prox|psio|pt\-g|qa\-a|qc(07|12|21|32|60|\-[2-7]|i\-)|qtek|r380|r600|raks|rim9|ro(ve|zo)|s55\/|sa(ge|ma|mm|ms|ny|va)|sc(01|h\-|oo|p\-)|sdk\/|se(c(\-|0|1)|47|mc|nd|ri)|sgh\-|shar|sie(\-|m)|sk\-0|sl(45|id)|sm(al|ar|b3|it|t5)|so(ft|ny)|sp(01|h\-|v\-|v )|sy(01|mb)|t2(18|50)|t6(00|10|18)|ta(gt|lk)|tcl\-|tdg\-|tel(i|m)|tim\-|t\-mo|to(pl|sh)|ts(70|m\-|m3|m5)|tx\-9|up(\.b|g1|si)|utst|v400|v750|veri|vi(rg|te)|vk(40|5[0-3]|\-v)|vm40|voda|vulc|vx(52|53|60|61|70|80|81|83|85|98)|w3c(\-| )|webc|whit|wi(g |nc|nw)|wmlb|wonu|x700|yas\-|your|zeto|zte\-/i.test(
- e.substr(0, 4)
- )
- )
- },
- isLandscape: function() {
- return this.isMobile && this.aspectRatio() > 1
- },
- isSmallScreen: function() {
- var e = screen.width / window.devicePixelRatio;
- return e < 240
- },
- detectIE: function() {
- var e = window.navigator.userAgent,
- t = e.indexOf("MSIE ");
- return t !== -1 || !!navigator.userAgent.match(/Trident.*rv\:11\./)
- },
- detectSafari: function() {
- var e = window.navigator.userAgent,
- t = e.indexOf("Safari");
- return t !== -1 && !this.detectOpera() && !this.detectChrome() //xzw add detectOpera
- },
- detectFirefox: function() {
- var e = window.navigator.userAgent;
- return e.indexOf("Firefox") !== -1
- },
- detectChrome: function() {
- var e = window.navigator.userAgent;
- return e.indexOf("Chrome") !== -1 && !this.detectOpera()
- },
- detectOpera: function() {
- var e = window.navigator.userAgent;
- return e.indexOf("OPR") !== -1
- },
- detectIOS: function() {
- return this.detectIPhone() || this.detectIPad() || this.detectIPod()
- },
- detectIPad: function() {
- var e = window.navigator.userAgent,
- t = /iPad/;
- return t.test(e)
- },
- detectIPod: function() {
- var e = window.navigator.userAgent,
- t = /iPod/;
- return t.test(e)
- },
- detectIPhone: function() {
- var e = window.navigator.userAgent,
- t = /iPhone/;
- return t.test(e)
- },
- detectAndroid: function() {
- var e = window.navigator.userAgent;
- return e.indexOf("Android") !== -1
- },
- detectAndroidMobile: function() {
- var e = window.navigator.userAgent;
- return this.detectAndroid() && e.indexOf("Mobile") !== -1
- },
- detectSamsungNative: function() {
- var e = window.navigator.userAgent;
- return (
- e.indexOf("SM-G900H") !== -1 ||
- e.indexOf("GT-I9500") !== -1 ||
- e.indexOf("SM-N900") !== -1
- )
- },
- detectSamsungS6: function() {
- var e = window.navigator.userAgent;
- return e.indexOf("SM-G92") !== -1
- },
- /************************************************************徐世廷*************************************************************/
- detectHUAWEI5X: function() {
- return -1 !== window.navigator.userAgent.indexOf("KIW-TL00H")
- },
- /*******************************************************************************************************************************/
- detectWebVR: function() {
- return !(!window.navigator.getVRDisplays || !window.VRDisplay)
- },
- getVRDisplay: function() {
- var e = $.Deferred();
- return this.detectWebVR() ?
- (navigator.getVRDisplays().then(function(t) {
- t.length >= 1 && e.resolve(t[0]), e.reject(null);
- }),
- e) :
- e.reject(null)
- },
- iosVersion: function() {
- if (!this.detectIOS()) throw new DeviceMismatchException("Did not detect an iDevice")
- var e = /((?:\d+\_?){1,3}) like Mac OS/,
- t = "_";
- return mobileVersion(e, t)
- },
- androidVersion: function() {
- if (!this.detectAndroid())
- throw new DeviceMismatchException("Did not detect an Android based device")
- var e = /Android ((?:\d+\.?){1,3})/,
- t = ".";
- return mobileVersion(e, t)
- },
- valueFromCookie: function(e, t) {
- var i = new RegExp(e + "=([0-9a-f]+)(; ?|$)").exec(document.cookie);
- if (!i) return t
- var n = i[1];
- return "boolean" == typeof t ?
- "true" === n || "1" === n :
- "number" == typeof t ?
- parseFloat(n) :
- n
- },
- valueFromHash: function(e, t) {
- var i = new RegExp("[#&?]" + e + "=([^#&?]*)"),
- n = i.exec(window.location.href);
- if (!n) return t
- var r = n[1];
- return "boolean" == typeof t ?
- "true" === r || "1" === r :
- "number" == typeof t ?
- parseFloat(r) :
- window.decodeURIComponent(r)
- },
- //-------许钟文:-------------------------------------------------
- getProjectNum: function() {
- //获取场景projectNum
- if (window.__ProjectNum && window.__ProjectNum != "__ProjectNum__") {
- return window.__ProjectNum
- }
- var number = window.location.href.substring(window.location.href.indexOf("=") + 1);
- if (number.indexOf("&") != -1) {
- number = number.substring(0, number.indexOf("&"));
- }
- if (number.indexOf("#") != -1) {
- number = number.substring(0, number.indexOf("#"));
- }
- return number
- },
- urlHasValue: function(key, isGetValue) {
- // debugger
- // if (getValue) { //得到类似n=1 的 1
- // var b = window.location.href.substring(window.location.href.indexOf("?") + 1);
- // var a = b.indexOf('&' + t + "=");
- // if (a > -1) {
- // var s = b.substring(a + ('&' + t + "=").length);
- // s.indexOf("&") > -1 && (s = s.substring(0, s.indexOf("&")));
- // s.indexOf("#") > -1 && (s = s.substring(0, s.indexOf("#")));
- // return s;
- // } else return false;
- // } else return window.location.search.match("&" + t + "|\\?" + t) != null; //window.location.href.substring(window.location.href.indexOf("?") + 1).indexOf('&' + t) > -1;
- //const value = window.location.search.match("&" + t + "|\\?" + t)
- if (key === "m" && window.__ProjectNum && window.__ProjectNum != "__ProjectNum__") {
- return window.__ProjectNum
- }
- let querys = window.location.search.substr(1).split("&");
- if (isGetValue) {
- for (let i = 0; i < querys.length; i++) {
- let keypair = querys[i].split("=");
- if (keypair.length === 2 && keypair[0] === key) {
- return keypair[1]
- }
- }
- return ""
- } else {
- //return window.location.search.match("&" + key + "|\\?" + key) != null 有bug
- for (let i = 0; i < querys.length; i++) {
- let keypair = querys[i].split("=");
- if (keypair[0] == key) {
- return true
- }
- }
- return false
- }
- },
- /**
- * 获取查询参数的值
- * @param {String} key
- * @returns String
- */
- urlQueryValue(key) {
- return this.urlHasValue(key, true) || ""
- },
- /**
- * 获取hash参数的值
- * @param {String} key
- * @returns String
- */
- urlHashValue(key) {
- let querys = window.location.hash.substr(1).replace('/?', '').split("&");
- for (let i = 0; i < querys.length; i++) {
- let keypair = querys[i].split("=");
- if (keypair.length === 2 && keypair[0] === key) {
- return keypair[1]
- }
- }
- return ""
- },
- /**
- * 判断是否存在hash
- * @param {String} key
- * @returns Boolean
- */
- urlIsHasHash(key) {
- let querys = window.location.hash.substr(1).replace('/?', '').split("&");
- return querys.includes(key)
- },
- islongPhone: function() {
- //是否是刘海全面屏幕 仅仅根据比例判断 - -
- //screen.height == 812 && screen.width == 375)
- var r = screen.height / screen.width; //可能横屏
- return this.isMobile() && (r > 1.99 || r < 0.502512) //18/9=2.165 //???
- },
- detectWeixin: function() {
- //微信 包括PC的微信
- return window.navigator.userAgent.toLowerCase().match(/MicroMessenger/i) == "micromessenger"
- },
- detectWeixinMiniProgram: function() {
- return window.navigator.userAgent.match("miniProgram")
- },
- detectEdge: function() {
- return window.navigator.userAgent.indexOf("Edge") > -1
- },
- detectApp: function() {
- return this.urlHasValue("app")
- },
- /**
- * 判断标签页是否切换状态
- */
- isTabHidden: function() {
- var prefixes = ["webkit", "moz", "ms", "o"];
- if ("hidden" in document) return document.hidden
- for (var i = 0; i < prefixes.length; i++) {
- if (prefixes[i] + "Hidden" in document) return document[prefixes[i] + "Hidden"]
- }
- return false
- },
- };
- //xzw add
- const config$1 = {//配置参数 不可修改
- displayMode:{
- showPointCloud:{
- atPano:{
- showPoint : true,
- showSkybox: false,
- pointUsePanoTex : false
-
- },
- transition:{
- showPoint: true,
- showSkybox: false,
- pointUsePanoTex: false
- },
- canLeavePano: true //是否能离开pano位置
- },
- showPanos:{
- atPano:{
- showPoint : false,
- showSkybox: true,
- pointUsePanoTex : false
- },
- transition:{
- //showPoint: true,
- showSkybox: true,
- //pointUsePanoTex: true //是否使用全景贴图
- },
- canLeavePano: false
- },
-
- showBoth:{
- atPano:{
- showPoint : true,
- showSkybox: true,
- pointUsePanoTex : false //?
- },
- transition:{
- showPoint: true,
- showSkybox: true,
- pointUsePanoTex: true
- },
- canLeavePano: true //是否能离开pano位置 离开后自动变为showPointCloud
- },
- //test:
- pointUsePanoTex:{ //---静止时调点云
- atPano:{
- showPoint : true,
- showSkybox: false,
- pointUsePanoTex : true
- },
- transition:{
- showPoint: true,
- showSkybox: true,
- pointUsePanoTex: true //是否使用全景贴图
- },
- canLeavePano: false
- },
- },
-
-
-
- urls:{
- //localTextures:'../resources/textures/',
- prefix: 'https://laser-oss.4dkankan.com',//oss
- prefix2: 'https://testlaser.4dkankan.com',
- prefix3: 'https://4dkk.4dage.com',
- prefix4: 'https://uat-laser.4dkankan.com/',//test.4dkankan
-
- },
-
- /* transitionsTime:{
- flyTime : 1000 // 毫秒/米
- panoToPano: 1000,
- flyIn:1000,
- flyOut:1000,
- } */
- transitionsTime:{
- flyMinTime : 500 /* * 3 */, // 毫秒/米
- flytimeDistanceMultiplier: 150/* * 3 */,
- panoToPanoMax: 2000/* * 3 */,
- flyIn:1000,
- flyOut:1000
- }
-
- ,
- moveSpeedAdujust : 0.5 //越小越慢
- ,
- view:{
- fov:70, //navvis:50
- near:0.1,
- far: 10000,
- },
-
- map:{//mapViewer
- mapHeight : -1000,//要比点云低。最低
- cameraHeight : 1000, //最高 ,注意(如sitemodel)其他的物体不能超过这个高度
- },
-
-
- pointDensity:{
- magnifier:{
- maxLevelPercent: 1,
- pointBudget : 8*1000*1000,
- },
- panorama:{//显示全景时的漫游。因为点只能显示1个像素的大小,所以必须很密集,但又要限制点的数量
- maxLevelPercent: 0.6,
- pointBudget : /* 4*1000*1000// */browser.isMobile() ? 0.1*1000*1000 : 0.4*1000*1000, //点云总最大数
- },
-
- fourViewports:{//分四屏时防止卡顿
- maxLevelPercent: 0.4,
- pointBudget :1*1000*1000, // 只要限制这个就足够 (为什么分屏focus区域不同会闪烁,navvis不会)(navvis:maxLevel:5,pointBudget:1*1000*1000)
- },
- fourViewportsMain:{//分四屏时防止卡顿
- maxLevelPercent: 0.8,
- pointBudget :1*1000*1000, // 只要限制这个就足够 (为什么分屏focus区域不同会闪烁,navvis不会)(navvis:maxLevel:5,pointBudget:1*1000*1000)
- }
- ,
- panoEdit:{
- maxLevelPercent: 1, //在远处时由于pointBudget限制而展示稀疏,凑近时就变为最高质量了
- pointBudget :1*1000*1000, //避免卡顿
- },
-
- low:{//highPerformance
- maxLevelPercent: 0.4, //最小为0
- percentByUser:true, //如果用户定义了percent,使用用户的
- pointBudget : 1*1000*1000,
- },
- middle:{//balanced //不同场景相同级别所产生的numVisibleNodes和numVisiblePoints不同,如果分层比较细,可能要到level8才能看清,那么level5看到的点就很大且很少,如隧道t-e2Kb2iU
- maxLevelPercent: 0.7,
- percentByUser:true,
- pointBudget:browser.isMobile() ? 4*1000*1000 : 2*1000*1000,
- },
- high:{//highQuality
- maxLevelPercent: 1,
- percentByUser:true,
- pointBudget:browser.isMobile() ? 8*1000*1000 : 4*1000*1000,
- }
- //browser.isMobile() 时要不要限制下pointBudget,还是让用户自己调低质量?
- //minNodeSize?
- //数值由testLevelSteps得来,其中nodeMaxLevel为2时,low和middle的都是1,如果真有这么低的点云就单独处理下。
- //多个viewport尽量保证pointBudget一样,或者pointBudget不能太低于所需,否则会反复加载了又清除
- },
-
-
-
- clip:{
- color: '#FFC266', //map
-
- },
- panoFieldRadius : 10, //当前位置多远范围内可以切全景模式
-
- measure:{
- color:'#00C8AF',
- default:{
- color:"#64C8BB",//"#00c7b2",
- opacity:0.7
- },
- highlight:{
- color:'#00C8AF',//"#00c7b2",
- opacity:1
- },
- guide:{
- color:'#FFFFFF',
- opacity:1
- }
- ,
- backColor:'#333333',
-
- lineWidth: 4,
-
- textColor: "#FFFFFF"
-
- },
- material:{//初始化
- pointSize: 0.1,
- realPointSize : 0.1,//实际上的ui滑动条默认大小(兼容旧的版本)
- minSize: 0.1,
- maxSize: 10000,
- pointSizeType: 'ATTENUATED', //'ADAPTIVE'//'ADAPTIVE' \ FIXED //ADAPTIVE的在小房间里大小会不太匹配,但在远景似乎更好
- /*
- ATTENUATED : 衰减 真实大小,靠近时感觉是点云一点点变多,缝隙变小
- ADAPTIVE: 自适应 大小根据level变化,越高越小。靠近时感觉点云由大慢慢细分成小份。这个感觉更佳但是navvis为何不用这个
- */
-
- absolutePanoramaSize: 1.3 ,//全景漫游时的size 是fixed的模式
-
- //sizeAtPanoRtEDL : 2000,
- pointColor:'#ffffff',
-
-
- //sizeAddAtPanoRtEDL : 0.5, //全景模式静止时的size
- //ADAPTIVE : 字会失真扭曲
- //'ATTENUATED' 往旁边看有缝隙、点在浮动
- //navvis的shader在哪里 为什么不会抖动
- }
- ,
-
-
- renderLayers:{//渲染层,方便分批渲染管理,替代scene的创建。数字不代表顺序。(数字不能太大)
- bg: 20,
- bg2: 21,
-
- skybox: 1,
- pointcloud: 11,
- sceneObjects:0,//default
-
-
- measure:4,
- magnifier:5,
- magnifierContent:16,
- volume:6,
- transformationTool:7,
-
- map:8,
- mapObjects:9,//default
-
-
- bothMapAndScene: 3,
-
- siteModeOnlyMapVisi:12,//只能mapViewer可见
- siteModelMapUnvisi:13,//只有mapViewer不可见
- siteModeSideVisi:14,//只有侧面可见
- },
-
-
- siteModel:{
- names:{
- 'building': '建筑',
- 'floor':'楼层',
- 'room':'房间'
- },
- floorHeightDefault: 5,//一层楼的高度
-
-
- },
-
- panosEdit:{
-
- },
-
- tiling: {
- panoPreRenderRepeatDelay: 2500,
- panoPreRenderDelay: 500,
- preRenderTourPanos: browser.valueFromHash("tileprerender", 0),
- tilingFlagNames: ["usetiles", "tiles"],
- maxNavPanoQuality: browser.valueFromHash("maxtileq", null),
- maxZoomPanoQuality: browser.valueFromHash("maxztileq", null),
- overlayStyle: browser.valueFromHash("tileoverlay", 0),
- uploadIntervalDelay: browser.valueFromHash("tileupdelay", 10),
- initialIntervalDelay: browser.valueFromHash("itiledelay", 0),
- maxNonBaseUploadsPerFrame: browser.valueFromHash("maxnbtpf", 2),
- maxBaseUploadsPerFrame: browser.valueFromHash("maxbtpf", 6),
- customCompression: browser.valueFromHash("tilecustcomp", 0),
- mobileHighQualityOverride: !1,
- allowUltraHighResolution: !0
- },
- navigation: {
- panoScores: !1,
- mouseDirection: !0,
- filterStrictness: .75,
- angleFactor: -30,
- directionFactor: 10,
- distanceFactor: -1,
- optionalityFactor: 3
- }
- ,
- axis : { 'x':{color:'#d0021b'/* 'red' */}, 'y':{ color:'#86c542' /* 'green' */}, 'z': {color:'#3399c8' /* 'blue' */}},
-
-
- highQualityMaxZoom: 2,
- ultraHighQualityMaxZoom: 3,
-
- clickMaxDragDis:5,
- clickMaxPressTime:500, //ms
-
-
-
- background: '#232323',
- mapBG:'#F5F5F5', //地图的clearColor
- colors: { //from navvis
- red: [213,0,0],
- pink: [197,17,98],
- purple: [170,0,255],
- "deep purple": [98,0,234],
- blue: [ 41,98,255],
- "light blue": [ 0,145,234],
- cyan: [ 0,184,212],
- teal: [ 0,191,165],
- green: [0,200,83],
- "light green": [ 100,221,23],
- lime: [ 174,234,0],
- yellow: [ 255,214,0],
- amber: [ 255,171,0],
- orange: [ 255,109,0],
- "deep orange": [ 255,61,0],
-
- },
- };
- config$1.OrthoCameraLimit = {
- standard:{
- zoom:{min:0.001, max:500}, //如果camera缩太小,地图会因为数字边界问题而扭曲
- posBound:{
- min: {x:-1e5, y:-1e5,z: config$1.map.cameraHeight},
- max: {x:1e5, y:1e5, z:1 / 0 }
- }
- },
- expand:{ //范围再大些,用于编辑空间模型等(但是万一中心点靠近地图边缘的话,就很容易扭曲了)
- zoom:{min:0.0004, max:100}, //如果camera缩太小,地图会因为数字边界问题而扭曲
- posBound:{
- min: {x:-5000000, y:-1000000,z:config$1.map.cameraHeight},
- max: {x:5000000, y:1000000, z:1 / 0 }
- }//40075017
- }
-
- };
- /* 显示模式:
- 1只显示点云: 滚轮为前进后退,方向键可以行走。进入漫游点时自动变为混合(这样全景可以弥补缝隙),过渡时只显示点云。
- 2只显示全景: 不能任意行走。 过渡时显示贴图材质非edl的点云(否则有折痕不贴合)。
- 3混合:都显示。 不能任意行走。过渡时显示贴图材质非edl的点云(因为只显示点云的话不太美,点云很碎,不细腻)
- */
- window.testLevelSteps = function(steps){//[0.4,0.7,1]
- if(!steps){
- let s = Potree.config.pointDensity;
- steps = [s.low.maxLevelPercent, s.middle.maxLevelPercent, s.high.maxLevelPercent, ];
- }
- let max = 1;
- while(++max<=12){
- let r1 = steps.map(e=>e * max);
- let r2 = steps.map(e=>Math.round(e * max));
- console.log(`当nodeMaxLevel为${max}时,每一级的level分别为${r2}, (小数:${r1})`);
- }
- console.log('请检查每一层的三个level是否有重复');
-
- };
- function getPrefix(){
- let u = window.location.href.split('//');
- let v = u[1].split('/');
- return v[0]
- }
-
- let settings = {//设置 可修改
- editType : '',
- number: '', //场景序号
- originDatasetId:'',//场景原本的数据集id,应该就是数据集第一个吧
- isOfficial:false,
- webSite:'testdata',//'data', //不同环境对应的静态文件的地址不同
-
- isLocal:false, //是否本地 局域网版本
-
- displayMode:'',
- isTest :browser.urlHasValue('test'),
- prefix: getPrefix(),
- pointDensity: '', UserPointDensity:'',//pointDensity会随着进入不同的模块而自动改变,UserPointDensity记录了用户的设置
- UserDensityPercent:null,//点云密度百分比
- ifShowMarker:true,//显示漫游点
- floorplanType:{},//平面图类型 'default' | 'diy' 不同数据集不同{datasetId:...}
- floorplanEnable:false,
- floorplanEnables:{},
- floorplanRequests:{},//开始加载了的
-
- mapEnable:true,//地图区域是否加载地图
- cameraFar : config$1.view.far, //相机最远范围 1-300
- //limitFar: true, //是否使用setting的cameraFar来限制(如在点云裁剪时为false)
- showPanoMesh:false, //显示小球,
- dblToFocusPoint:false,//调试时如果需要双击飞向某个点云的点,就打开。此时不在漫游点的话单击将无法漫游。//因和单击漫游冲突
-
- unableNavigate : false,//进入如裁剪界面时 禁止漫游
- sizeFitToLevel: false,//当type为衰减模式时自动根据level调节大小。每长一级,大小就除以2
- zoom:{
- enabled : true,
- min:1,
- max: config$1.highQualityMaxZoom,
- activationThreshold: 1.1,
- },
- navConstantly:true,
- navTileClass: browser.isMobile() ? '1k' : '2k', //默认加载到
- tileClass:'4k', //最高可达
- /* loadTilesWhenUnfocus:false, //页面unfocus时也仍在加载tiles
- loadPointsWhenUnfocus:true, //页面unfocus时也仍在加载点云 */
-
- //initialShowPano:true
- drawEntityData: false,
-
- zoomFromPointert:{//定点缩放(包括点云模式、全景模式、地图)
- whenPanos:true,
- whenPointCloud:true,
- map:true,
- },
- rotAroundPoint:true,//点云模式是否能绕intersectPoint旋转
- tourTestCameraMove:false, //测试镜头时,不移动真实的镜头, 只移动frustum
- cameraAniSmoothRatio : 20, //镜头动画平滑系数,越高越平滑
- urls : $.extend({}, config$1.urls),
-
-
- useDepthTex: true,//使用深度贴图,但不代表一定有(得到的intersect更快速准确和稳定) SS-t-7DUfWAUZ3V
-
- //panoEdit:
- datasetsPanos:{},
-
- //mergeModel:
- boundAddObjs:false,
- intersectOnObjs:false,
- };
-
- //JSON.parse(localStorage.getItem('setting'))
- settings.isLocalhost = settings.prefix.includes('localhost');
- // threejs.org/license
- const REVISION = '124';
- const MOUSE = { LEFT: 0, MIDDLE: 1, RIGHT: 2, ROTATE: 0, DOLLY: 1, PAN: 2 };
- const TOUCH = { ROTATE: 0, PAN: 1, DOLLY_PAN: 2, DOLLY_ROTATE: 3 };
- const CullFaceNone = 0;
- const CullFaceBack = 1;
- const CullFaceFront = 2;
- const CullFaceFrontBack = 3;
- const BasicShadowMap = 0;
- const PCFShadowMap = 1;
- const PCFSoftShadowMap = 2;
- const VSMShadowMap = 3;
- const FrontSide = 0;
- const BackSide = 1;
- const DoubleSide = 2;
- const FlatShading = 1;
- const SmoothShading = 2;
- const NoBlending = 0;
- const NormalBlending = 1;
- const AdditiveBlending = 2;
- const SubtractiveBlending = 3;
- const MultiplyBlending = 4;
- const CustomBlending = 5;
- const AddEquation = 100;
- const SubtractEquation = 101;
- const ReverseSubtractEquation = 102;
- const MinEquation = 103;
- const MaxEquation = 104;
- const ZeroFactor = 200;
- const OneFactor = 201;
- const SrcColorFactor = 202;
- const OneMinusSrcColorFactor = 203;
- const SrcAlphaFactor = 204;
- const OneMinusSrcAlphaFactor = 205;
- const DstAlphaFactor = 206;
- const OneMinusDstAlphaFactor = 207;
- const DstColorFactor = 208;
- const OneMinusDstColorFactor = 209;
- const SrcAlphaSaturateFactor = 210;
- const NeverDepth = 0;
- const AlwaysDepth = 1;
- const LessDepth = 2;
- const LessEqualDepth = 3;
- const EqualDepth = 4;
- const GreaterEqualDepth = 5;
- const GreaterDepth = 6;
- const NotEqualDepth = 7;
- const MultiplyOperation = 0;
- const MixOperation = 1;
- const AddOperation = 2;
- const NoToneMapping = 0;
- const LinearToneMapping = 1;
- const ReinhardToneMapping = 2;
- const CineonToneMapping = 3;
- const ACESFilmicToneMapping = 4;
- const CustomToneMapping = 5;
- const UVMapping = 300;
- const CubeReflectionMapping = 301;
- const CubeRefractionMapping = 302;
- const EquirectangularReflectionMapping = 303;
- const EquirectangularRefractionMapping = 304;
- const CubeUVReflectionMapping = 306;
- const CubeUVRefractionMapping = 307;
- const RepeatWrapping = 1000;
- const ClampToEdgeWrapping = 1001;
- const MirroredRepeatWrapping = 1002;
- const NearestFilter = 1003;
- const NearestMipmapNearestFilter = 1004;
- const NearestMipMapNearestFilter = 1004;
- const NearestMipmapLinearFilter = 1005;
- const NearestMipMapLinearFilter = 1005;
- const LinearFilter = 1006;
- const LinearMipmapNearestFilter = 1007;
- const LinearMipMapNearestFilter = 1007;
- const LinearMipmapLinearFilter = 1008;
- const LinearMipMapLinearFilter = 1008;
- const UnsignedByteType = 1009;
- const ByteType = 1010;
- const ShortType = 1011;
- const UnsignedShortType = 1012;
- const IntType = 1013;
- const UnsignedIntType = 1014;
- const FloatType = 1015;
- const HalfFloatType = 1016;
- const UnsignedShort4444Type = 1017;
- const UnsignedShort5551Type = 1018;
- const UnsignedShort565Type = 1019;
- const UnsignedInt248Type$1 = 1020;
- const AlphaFormat = 1021;
- const RGBFormat = 1022;
- const RGBAFormat = 1023;
- const LuminanceFormat = 1024;
- const LuminanceAlphaFormat = 1025;
- const RGBEFormat = RGBAFormat;
- const DepthFormat = 1026;
- const DepthStencilFormat = 1027;
- const RedFormat = 1028;
- const RedIntegerFormat = 1029;
- const RGFormat = 1030;
- const RGIntegerFormat = 1031;
- const RGBIntegerFormat = 1032;
- const RGBAIntegerFormat = 1033;
- const RGB_S3TC_DXT1_Format = 33776;
- const RGBA_S3TC_DXT1_Format$1 = 33777;
- const RGBA_S3TC_DXT3_Format = 33778;
- const RGBA_S3TC_DXT5_Format$1 = 33779;
- const RGB_PVRTC_4BPPV1_Format = 35840;
- const RGB_PVRTC_2BPPV1_Format = 35841;
- const RGBA_PVRTC_4BPPV1_Format = 35842;
- const RGBA_PVRTC_2BPPV1_Format = 35843;
- const RGB_ETC1_Format = 36196;
- const RGB_ETC2_Format = 37492;
- const RGBA_ETC2_EAC_Format = 37496;
- const RGBA_ASTC_4x4_Format = 37808;
- const RGBA_ASTC_5x4_Format = 37809;
- const RGBA_ASTC_5x5_Format = 37810;
- const RGBA_ASTC_6x5_Format = 37811;
- const RGBA_ASTC_6x6_Format = 37812;
- const RGBA_ASTC_8x5_Format = 37813;
- const RGBA_ASTC_8x6_Format = 37814;
- const RGBA_ASTC_8x8_Format = 37815;
- const RGBA_ASTC_10x5_Format = 37816;
- const RGBA_ASTC_10x6_Format = 37817;
- const RGBA_ASTC_10x8_Format = 37818;
- const RGBA_ASTC_10x10_Format = 37819;
- const RGBA_ASTC_12x10_Format = 37820;
- const RGBA_ASTC_12x12_Format = 37821;
- const RGBA_BPTC_Format = 36492;
- const SRGB8_ALPHA8_ASTC_4x4_Format = 37840;
- const SRGB8_ALPHA8_ASTC_5x4_Format = 37841;
- const SRGB8_ALPHA8_ASTC_5x5_Format = 37842;
- const SRGB8_ALPHA8_ASTC_6x5_Format = 37843;
- const SRGB8_ALPHA8_ASTC_6x6_Format = 37844;
- const SRGB8_ALPHA8_ASTC_8x5_Format = 37845;
- const SRGB8_ALPHA8_ASTC_8x6_Format = 37846;
- const SRGB8_ALPHA8_ASTC_8x8_Format = 37847;
- const SRGB8_ALPHA8_ASTC_10x5_Format = 37848;
- const SRGB8_ALPHA8_ASTC_10x6_Format = 37849;
- const SRGB8_ALPHA8_ASTC_10x8_Format = 37850;
- const SRGB8_ALPHA8_ASTC_10x10_Format = 37851;
- const SRGB8_ALPHA8_ASTC_12x10_Format = 37852;
- const SRGB8_ALPHA8_ASTC_12x12_Format = 37853;
- const LoopOnce = 2200;
- const LoopRepeat = 2201;
- const LoopPingPong = 2202;
- const InterpolateDiscrete = 2300;
- const InterpolateLinear = 2301;
- const InterpolateSmooth = 2302;
- const ZeroCurvatureEnding = 2400;
- const ZeroSlopeEnding = 2401;
- const WrapAroundEnding = 2402;
- const NormalAnimationBlendMode = 2500;
- const AdditiveAnimationBlendMode = 2501;
- const TrianglesDrawMode = 0;
- const TriangleStripDrawMode = 1;
- const TriangleFanDrawMode = 2;
- const LinearEncoding = 3000;
- const sRGBEncoding = 3001;
- const GammaEncoding = 3007;
- const RGBEEncoding = 3002;
- const LogLuvEncoding = 3003;
- const RGBM7Encoding = 3004;
- const RGBM16Encoding = 3005;
- const RGBDEncoding = 3006;
- const BasicDepthPacking = 3200;
- const RGBADepthPacking = 3201;
- const TangentSpaceNormalMap = 0;
- const ObjectSpaceNormalMap = 1;
- const ZeroStencilOp = 0;
- const KeepStencilOp = 7680;
- const ReplaceStencilOp = 7681;
- const IncrementStencilOp = 7682;
- const DecrementStencilOp = 7683;
- const IncrementWrapStencilOp = 34055;
- const DecrementWrapStencilOp = 34056;
- const InvertStencilOp = 5386;
- const NeverStencilFunc = 512;
- const LessStencilFunc = 513;
- const EqualStencilFunc = 514;
- const LessEqualStencilFunc = 515;
- const GreaterStencilFunc = 516;
- const NotEqualStencilFunc = 517;
- const GreaterEqualStencilFunc = 518;
- const AlwaysStencilFunc = 519;
- const StaticDrawUsage = 35044;
- const DynamicDrawUsage = 35048;
- const StreamDrawUsage = 35040;
- const StaticReadUsage = 35045;
- const DynamicReadUsage = 35049;
- const StreamReadUsage = 35041;
- const StaticCopyUsage = 35046;
- const DynamicCopyUsage = 35050;
- const StreamCopyUsage = 35042;
- const GLSL1 = '100';
- const GLSL3 = '300 es';
- /**
- * https://github.com/mrdoob/eventdispatcher.js/
- */
- function EventDispatcher() {}
- Object.assign( EventDispatcher.prototype, {
- addEventListener: function ( type, listener, importance=0 ) {//add importance
- if ( this._listeners === undefined ) this._listeners = {};
- const listeners = this._listeners;
- if ( listeners[ type ] === undefined ) {
- listeners[ type ] = [];
- }
- if ( !listeners[ type ].some(e=>e.listener == listener ) ) {
- //listeners[ type ].push( listener );
- listeners[type].push({ listener, importance});
- listeners[type] = listeners[type].sort((e,a)=> a.importance - e.importance);//add
- }
- },
- hasEventListener: function ( type, listener ) {
- if ( this._listeners === undefined ) return false;
- const listeners = this._listeners;
- return listeners[ type ] !== undefined && listeners[ type ].some(e=>e.listener == listener )
- },
- removeEventListener: function ( type, listener ) {
- if ( this._listeners === undefined ) return;
- const listeners = this._listeners;
- const listenerArray = listeners[ type ];
- if ( listenerArray !== undefined ) {
- /* const index = listenerArray.indexOf( listener );
- if ( index !== - 1 ) {
- listenerArray.splice( index, 1 );
- } */
- let item = listenerArray.find(e=>e.listener == listener);
- item && listenerArray.splice(listenerArray.indexOf(item), 1);
- }
- },
- removeEventListeners(type){//add
- if(this._listeners && this._listeners[type] !== undefined){
- delete this._listeners[type];
- }
- } ,
- removeAllListeners(){ //add
- this._listeners = {};
-
- },
-
-
-
- dispatchEvent: function ( event ) {
- if(typeof event == 'string'){//add
- event = {type:event};
- }
- if ( this._listeners === undefined ) return;
- const listeners = this._listeners;
- const listenerArray = listeners[ event.type ];
- if ( listenerArray !== undefined ) {
- event.target = this;
- // Make a copy, in case listeners are removed while iterating.
-
- for(let {listener} of listenerArray.slice(0)){
- let result = listener.call(this, event); //add stopContinue
- if(result && result.stopContinue){
- break
- }
- }
- }
- }
-
- } );
- const _lut = [];
- for ( let i = 0; i < 256; i ++ ) {
- _lut[ i ] = ( i < 16 ? '0' : '' ) + ( i ).toString( 16 );
- }
- let _seed = 1234567;
- const MathUtils = {
- DEG2RAD: Math.PI / 180,
- RAD2DEG: 180 / Math.PI,
- generateUUID: function () {
- // http://stackoverflow.com/questions/105034/how-to-create-a-guid-uuid-in-javascript/21963136#21963136
- const d0 = Math.random() * 0xffffffff | 0;
- const d1 = Math.random() * 0xffffffff | 0;
- const d2 = Math.random() * 0xffffffff | 0;
- const d3 = Math.random() * 0xffffffff | 0;
- const uuid = _lut[ d0 & 0xff ] + _lut[ d0 >> 8 & 0xff ] + _lut[ d0 >> 16 & 0xff ] + _lut[ d0 >> 24 & 0xff ] + '-' +
- _lut[ d1 & 0xff ] + _lut[ d1 >> 8 & 0xff ] + '-' + _lut[ d1 >> 16 & 0x0f | 0x40 ] + _lut[ d1 >> 24 & 0xff ] + '-' +
- _lut[ d2 & 0x3f | 0x80 ] + _lut[ d2 >> 8 & 0xff ] + '-' + _lut[ d2 >> 16 & 0xff ] + _lut[ d2 >> 24 & 0xff ] +
- _lut[ d3 & 0xff ] + _lut[ d3 >> 8 & 0xff ] + _lut[ d3 >> 16 & 0xff ] + _lut[ d3 >> 24 & 0xff ];
- // .toUpperCase() here flattens concatenated strings to save heap memory space.
- return uuid.toUpperCase();
- },
- clamp: function ( value, min, max ) {
- return Math.max( min, Math.min( max, value ) );
- },
- // compute euclidian modulo of m % n
- // https://en.wikipedia.org/wiki/Modulo_operation
- euclideanModulo: function ( n, m ) {
- return ( ( n % m ) + m ) % m;
- },
- // Linear mapping from range <a1, a2> to range <b1, b2>
- mapLinear: function ( x, a1, a2, b1, b2 ) {
- return b1 + ( x - a1 ) * ( b2 - b1 ) / ( a2 - a1 );
- },
- // https://en.wikipedia.org/wiki/Linear_interpolation
- lerp: function ( x, y, t ) {
- return ( 1 - t ) * x + t * y;
- },
- // http://en.wikipedia.org/wiki/Smoothstep
- smoothstep: function ( x, min, max ) {
- if ( x <= min ) return 0;
- if ( x >= max ) return 1;
- x = ( x - min ) / ( max - min );
- return x * x * ( 3 - 2 * x );
- },
- smootherstep: function ( x, min, max ) {
- if ( x <= min ) return 0;
- if ( x >= max ) return 1;
- x = ( x - min ) / ( max - min );
- return x * x * x * ( x * ( x * 6 - 15 ) + 10 );
- },
- // Random integer from <low, high> interval
- randInt: function ( low, high ) {
- return low + Math.floor( Math.random() * ( high - low + 1 ) );
- },
- // Random float from <low, high> interval
- randFloat: function ( low, high ) {
- return low + Math.random() * ( high - low );
- },
- // Random float from <-range/2, range/2> interval
- randFloatSpread: function ( range ) {
- return range * ( 0.5 - Math.random() );
- },
- // Deterministic pseudo-random float in the interval [ 0, 1 ]
- seededRandom: function ( s ) {
- if ( s !== undefined ) _seed = s % 2147483647;
- // Park-Miller algorithm
- _seed = _seed * 16807 % 2147483647;
- return ( _seed - 1 ) / 2147483646;
- },
- degToRad: function ( degrees ) {
- return degrees * MathUtils.DEG2RAD;
- },
- radToDeg: function ( radians ) {
- return radians * MathUtils.RAD2DEG;
- },
- isPowerOfTwo: function ( value ) {
- return ( value & ( value - 1 ) ) === 0 && value !== 0;
- },
- ceilPowerOfTwo: function ( value ) {
- return Math.pow( 2, Math.ceil( Math.log( value ) / Math.LN2 ) );
- },
- floorPowerOfTwo: function ( value ) {
- return Math.pow( 2, Math.floor( Math.log( value ) / Math.LN2 ) );
- },
- setQuaternionFromProperEuler: function ( q, a, b, c, order ) {
- // Intrinsic Proper Euler Angles - see https://en.wikipedia.org/wiki/Euler_angles
- // rotations are applied to the axes in the order specified by 'order'
- // rotation by angle 'a' is applied first, then by angle 'b', then by angle 'c'
- // angles are in radians
- const cos = Math.cos;
- const sin = Math.sin;
- const c2 = cos( b / 2 );
- const s2 = sin( b / 2 );
- const c13 = cos( ( a + c ) / 2 );
- const s13 = sin( ( a + c ) / 2 );
- const c1_3 = cos( ( a - c ) / 2 );
- const s1_3 = sin( ( a - c ) / 2 );
- const c3_1 = cos( ( c - a ) / 2 );
- const s3_1 = sin( ( c - a ) / 2 );
- switch ( order ) {
- case 'XYX':
- q.set( c2 * s13, s2 * c1_3, s2 * s1_3, c2 * c13 );
- break;
- case 'YZY':
- q.set( s2 * s1_3, c2 * s13, s2 * c1_3, c2 * c13 );
- break;
- case 'ZXZ':
- q.set( s2 * c1_3, s2 * s1_3, c2 * s13, c2 * c13 );
- break;
- case 'XZX':
- q.set( c2 * s13, s2 * s3_1, s2 * c3_1, c2 * c13 );
- break;
- case 'YXY':
- q.set( s2 * c3_1, c2 * s13, s2 * s3_1, c2 * c13 );
- break;
- case 'ZYZ':
- q.set( s2 * s3_1, s2 * c3_1, c2 * s13, c2 * c13 );
- break;
- default:
- console.warn( 'THREE.MathUtils: .setQuaternionFromProperEuler() encountered an unknown order: ' + order );
- }
- }
- };
- class Vector2$1 {
- constructor( x = 0, y = 0 ) {
- Object.defineProperty( this, 'isVector2', { value: true } );
- this.x = x;
- this.y = y;
- }
- get width() {
- return this.x;
- }
- set width( value ) {
- this.x = value;
- }
- get height() {
- return this.y;
- }
- set height( value ) {
- this.y = value;
- }
- set( x, y ) {
- this.x = x;
- this.y = y;
- return this;
- }
- setScalar( scalar ) {
- this.x = scalar;
- this.y = scalar;
- return this;
- }
- setX( x ) {
- this.x = x;
- return this;
- }
- setY( y ) {
- this.y = y;
- return this;
- }
- setComponent( index, value ) {
- switch ( index ) {
- case 0: this.x = value; break;
- case 1: this.y = value; break;
- default: throw new Error( 'index is out of range: ' + index );
- }
- return this;
- }
- getComponent( index ) {
- switch ( index ) {
- case 0: return this.x;
- case 1: return this.y;
- default: throw new Error( 'index is out of range: ' + index );
- }
- }
- clone() {
- return new this.constructor( this.x, this.y );
- }
- copy( v ) {
- this.x = v.x;
- this.y = v.y;
- return this;
- }
- add( v, w ) {
- if ( w !== undefined ) {
- console.warn( 'THREE.Vector2: .add() now only accepts one argument. Use .addVectors( a, b ) instead.' );
- return this.addVectors( v, w );
- }
- this.x += v.x;
- this.y += v.y;
- return this;
- }
- addScalar( s ) {
- this.x += s;
- this.y += s;
- return this;
- }
- addVectors( a, b ) {
- this.x = a.x + b.x;
- this.y = a.y + b.y;
- return this;
- }
- addScaledVector( v, s ) {
- this.x += v.x * s;
- this.y += v.y * s;
- return this;
- }
- sub( v, w ) {
- if ( w !== undefined ) {
- console.warn( 'THREE.Vector2: .sub() now only accepts one argument. Use .subVectors( a, b ) instead.' );
- return this.subVectors( v, w );
- }
- this.x -= v.x;
- this.y -= v.y;
- return this;
- }
- subScalar( s ) {
- this.x -= s;
- this.y -= s;
- return this;
- }
- subVectors( a, b ) {
- this.x = a.x - b.x;
- this.y = a.y - b.y;
- return this;
- }
- multiply( v ) {
- this.x *= v.x;
- this.y *= v.y;
- return this;
- }
- multiplyScalar( scalar ) {
- this.x *= scalar;
- this.y *= scalar;
- return this;
- }
- divide( v ) {
- this.x /= v.x;
- this.y /= v.y;
- return this;
- }
- divideScalar( scalar ) {
- return this.multiplyScalar( 1 / scalar );
- }
- applyMatrix3( m ) {
- const x = this.x, y = this.y;
- const e = m.elements;
- this.x = e[ 0 ] * x + e[ 3 ] * y + e[ 6 ];
- this.y = e[ 1 ] * x + e[ 4 ] * y + e[ 7 ];
- return this;
- }
- min( v ) {
- this.x = Math.min( this.x, v.x );
- this.y = Math.min( this.y, v.y );
- return this;
- }
- max( v ) {
- this.x = Math.max( this.x, v.x );
- this.y = Math.max( this.y, v.y );
- return this;
- }
- clamp( min, max ) {
- // assumes min < max, componentwise
- this.x = Math.max( min.x, Math.min( max.x, this.x ) );
- this.y = Math.max( min.y, Math.min( max.y, this.y ) );
- return this;
- }
- clampScalar( minVal, maxVal ) {
- this.x = Math.max( minVal, Math.min( maxVal, this.x ) );
- this.y = Math.max( minVal, Math.min( maxVal, this.y ) );
- return this;
- }
- clampLength( min, max ) {
- const length = this.length();
- return this.divideScalar( length || 1 ).multiplyScalar( Math.max( min, Math.min( max, length ) ) );
- }
- floor() {
- this.x = Math.floor( this.x );
- this.y = Math.floor( this.y );
- return this;
- }
- ceil() {
- this.x = Math.ceil( this.x );
- this.y = Math.ceil( this.y );
- return this;
- }
- round() {
- this.x = Math.round( this.x );
- this.y = Math.round( this.y );
- return this;
- }
- roundToZero() {
- this.x = ( this.x < 0 ) ? Math.ceil( this.x ) : Math.floor( this.x );
- this.y = ( this.y < 0 ) ? Math.ceil( this.y ) : Math.floor( this.y );
- return this;
- }
- negate() {
- this.x = - this.x;
- this.y = - this.y;
- return this;
- }
- dot( v ) {
- return this.x * v.x + this.y * v.y;
- }
- cross( v ) {
- return this.x * v.y - this.y * v.x;
- }
- lengthSq() {
- return this.x * this.x + this.y * this.y;
- }
- length() {
- return Math.sqrt( this.x * this.x + this.y * this.y );
- }
- manhattanLength() {
- return Math.abs( this.x ) + Math.abs( this.y );
- }
- normalize() {
- return this.divideScalar( this.length() || 1 );
- }
- angle() {
- // computes the angle in radians with respect to the positive x-axis
- const angle = Math.atan2( - this.y, - this.x ) + Math.PI;
- return angle;
- }
- distanceTo( v ) {
- return Math.sqrt( this.distanceToSquared( v ) );
- }
- distanceToSquared( v ) {
- const dx = this.x - v.x, dy = this.y - v.y;
- return dx * dx + dy * dy;
- }
- manhattanDistanceTo( v ) {
- return Math.abs( this.x - v.x ) + Math.abs( this.y - v.y );
- }
- setLength( length ) {
- return this.normalize().multiplyScalar( length );
- }
- lerp( v, alpha ) {
- this.x += ( v.x - this.x ) * alpha;
- this.y += ( v.y - this.y ) * alpha;
- return this;
- }
- lerpVectors( v1, v2, alpha ) {
- this.x = v1.x + ( v2.x - v1.x ) * alpha;
- this.y = v1.y + ( v2.y - v1.y ) * alpha;
- return this;
- }
- equals( v ) {
- return ( ( v.x === this.x ) && ( v.y === this.y ) );
- }
- fromArray( array, offset = 0 ) {
- this.x = array[ offset ];
- this.y = array[ offset + 1 ];
- return this;
- }
- toArray( array = [], offset = 0 ) {
- array[ offset ] = this.x;
- array[ offset + 1 ] = this.y;
- return array;
- }
- fromBufferAttribute( attribute, index, offset ) {
- if ( offset !== undefined ) {
- console.warn( 'THREE.Vector2: offset has been removed from .fromBufferAttribute().' );
- }
- this.x = attribute.getX( index );
- this.y = attribute.getY( index );
- return this;
- }
- rotateAround( center, angle ) {
- const c = Math.cos( angle ), s = Math.sin( angle );
- const x = this.x - center.x;
- const y = this.y - center.y;
- this.x = x * c - y * s + center.x;
- this.y = x * s + y * c + center.y;
- return this;
- }
- random() {
- this.x = Math.random();
- this.y = Math.random();
- return this;
- }
- }
- class Matrix3 {
- constructor() {
- Object.defineProperty( this, 'isMatrix3', { value: true } );
- this.elements = [
- 1, 0, 0,
- 0, 1, 0,
- 0, 0, 1
- ];
- if ( arguments.length > 0 ) {
- console.error( 'THREE.Matrix3: the constructor no longer reads arguments. use .set() instead.' );
- }
- }
- set( n11, n12, n13, n21, n22, n23, n31, n32, n33 ) {
- const te = this.elements;
- te[ 0 ] = n11; te[ 1 ] = n21; te[ 2 ] = n31;
- te[ 3 ] = n12; te[ 4 ] = n22; te[ 5 ] = n32;
- te[ 6 ] = n13; te[ 7 ] = n23; te[ 8 ] = n33;
- return this;
- }
- identity() {
- this.set(
- 1, 0, 0,
- 0, 1, 0,
- 0, 0, 1
- );
- return this;
- }
- clone() {
- return new this.constructor().fromArray( this.elements );
- }
- copy( m ) {
- const te = this.elements;
- const me = m.elements;
- te[ 0 ] = me[ 0 ]; te[ 1 ] = me[ 1 ]; te[ 2 ] = me[ 2 ];
- te[ 3 ] = me[ 3 ]; te[ 4 ] = me[ 4 ]; te[ 5 ] = me[ 5 ];
- te[ 6 ] = me[ 6 ]; te[ 7 ] = me[ 7 ]; te[ 8 ] = me[ 8 ];
- return this;
- }
- extractBasis( xAxis, yAxis, zAxis ) {
- xAxis.setFromMatrix3Column( this, 0 );
- yAxis.setFromMatrix3Column( this, 1 );
- zAxis.setFromMatrix3Column( this, 2 );
- return this;
- }
- setFromMatrix4( m ) {
- const me = m.elements;
- this.set(
- me[ 0 ], me[ 4 ], me[ 8 ],
- me[ 1 ], me[ 5 ], me[ 9 ],
- me[ 2 ], me[ 6 ], me[ 10 ]
- );
- return this;
- }
- multiply( m ) {
- return this.multiplyMatrices( this, m );
- }
- premultiply( m ) {
- return this.multiplyMatrices( m, this );
- }
- multiplyMatrices( a, b ) {
- const ae = a.elements;
- const be = b.elements;
- const te = this.elements;
- const a11 = ae[ 0 ], a12 = ae[ 3 ], a13 = ae[ 6 ];
- const a21 = ae[ 1 ], a22 = ae[ 4 ], a23 = ae[ 7 ];
- const a31 = ae[ 2 ], a32 = ae[ 5 ], a33 = ae[ 8 ];
- const b11 = be[ 0 ], b12 = be[ 3 ], b13 = be[ 6 ];
- const b21 = be[ 1 ], b22 = be[ 4 ], b23 = be[ 7 ];
- const b31 = be[ 2 ], b32 = be[ 5 ], b33 = be[ 8 ];
- te[ 0 ] = a11 * b11 + a12 * b21 + a13 * b31;
- te[ 3 ] = a11 * b12 + a12 * b22 + a13 * b32;
- te[ 6 ] = a11 * b13 + a12 * b23 + a13 * b33;
- te[ 1 ] = a21 * b11 + a22 * b21 + a23 * b31;
- te[ 4 ] = a21 * b12 + a22 * b22 + a23 * b32;
- te[ 7 ] = a21 * b13 + a22 * b23 + a23 * b33;
- te[ 2 ] = a31 * b11 + a32 * b21 + a33 * b31;
- te[ 5 ] = a31 * b12 + a32 * b22 + a33 * b32;
- te[ 8 ] = a31 * b13 + a32 * b23 + a33 * b33;
- return this;
- }
- multiplyScalar( s ) {
- const te = this.elements;
- te[ 0 ] *= s; te[ 3 ] *= s; te[ 6 ] *= s;
- te[ 1 ] *= s; te[ 4 ] *= s; te[ 7 ] *= s;
- te[ 2 ] *= s; te[ 5 ] *= s; te[ 8 ] *= s;
- return this;
- }
- determinant() {
- const te = this.elements;
- const a = te[ 0 ], b = te[ 1 ], c = te[ 2 ],
- d = te[ 3 ], e = te[ 4 ], f = te[ 5 ],
- g = te[ 6 ], h = te[ 7 ], i = te[ 8 ];
- return a * e * i - a * f * h - b * d * i + b * f * g + c * d * h - c * e * g;
- }
- invert() {
- const te = this.elements,
- n11 = te[ 0 ], n21 = te[ 1 ], n31 = te[ 2 ],
- n12 = te[ 3 ], n22 = te[ 4 ], n32 = te[ 5 ],
- n13 = te[ 6 ], n23 = te[ 7 ], n33 = te[ 8 ],
- t11 = n33 * n22 - n32 * n23,
- t12 = n32 * n13 - n33 * n12,
- t13 = n23 * n12 - n22 * n13,
- det = n11 * t11 + n21 * t12 + n31 * t13;
- if ( det === 0 ) return this.set( 0, 0, 0, 0, 0, 0, 0, 0, 0 );
- const detInv = 1 / det;
- te[ 0 ] = t11 * detInv;
- te[ 1 ] = ( n31 * n23 - n33 * n21 ) * detInv;
- te[ 2 ] = ( n32 * n21 - n31 * n22 ) * detInv;
- te[ 3 ] = t12 * detInv;
- te[ 4 ] = ( n33 * n11 - n31 * n13 ) * detInv;
- te[ 5 ] = ( n31 * n12 - n32 * n11 ) * detInv;
- te[ 6 ] = t13 * detInv;
- te[ 7 ] = ( n21 * n13 - n23 * n11 ) * detInv;
- te[ 8 ] = ( n22 * n11 - n21 * n12 ) * detInv;
- return this;
- }
- transpose() {
- let tmp;
- const m = this.elements;
- tmp = m[ 1 ]; m[ 1 ] = m[ 3 ]; m[ 3 ] = tmp;
- tmp = m[ 2 ]; m[ 2 ] = m[ 6 ]; m[ 6 ] = tmp;
- tmp = m[ 5 ]; m[ 5 ] = m[ 7 ]; m[ 7 ] = tmp;
- return this;
- }
- getNormalMatrix( matrix4 ) {
- return this.setFromMatrix4( matrix4 ).copy( this ).invert().transpose();
- }
- transposeIntoArray( r ) {
- const m = this.elements;
- r[ 0 ] = m[ 0 ];
- r[ 1 ] = m[ 3 ];
- r[ 2 ] = m[ 6 ];
- r[ 3 ] = m[ 1 ];
- r[ 4 ] = m[ 4 ];
- r[ 5 ] = m[ 7 ];
- r[ 6 ] = m[ 2 ];
- r[ 7 ] = m[ 5 ];
- r[ 8 ] = m[ 8 ];
- return this;
- }
- setUvTransform( tx, ty, sx, sy, rotation, cx, cy ) {
- const c = Math.cos( rotation );
- const s = Math.sin( rotation );
- this.set(
- sx * c, sx * s, - sx * ( c * cx + s * cy ) + cx + tx,
- - sy * s, sy * c, - sy * ( - s * cx + c * cy ) + cy + ty,
- 0, 0, 1
- );
- return this;
- }
- scale( sx, sy ) {
- const te = this.elements;
- te[ 0 ] *= sx; te[ 3 ] *= sx; te[ 6 ] *= sx;
- te[ 1 ] *= sy; te[ 4 ] *= sy; te[ 7 ] *= sy;
- return this;
- }
- rotate( theta ) {
- const c = Math.cos( theta );
- const s = Math.sin( theta );
- const te = this.elements;
- const a11 = te[ 0 ], a12 = te[ 3 ], a13 = te[ 6 ];
- const a21 = te[ 1 ], a22 = te[ 4 ], a23 = te[ 7 ];
- te[ 0 ] = c * a11 + s * a21;
- te[ 3 ] = c * a12 + s * a22;
- te[ 6 ] = c * a13 + s * a23;
- te[ 1 ] = - s * a11 + c * a21;
- te[ 4 ] = - s * a12 + c * a22;
- te[ 7 ] = - s * a13 + c * a23;
- return this;
- }
- translate( tx, ty ) {
- const te = this.elements;
- te[ 0 ] += tx * te[ 2 ]; te[ 3 ] += tx * te[ 5 ]; te[ 6 ] += tx * te[ 8 ];
- te[ 1 ] += ty * te[ 2 ]; te[ 4 ] += ty * te[ 5 ]; te[ 7 ] += ty * te[ 8 ];
- return this;
- }
- equals( matrix ) {
- const te = this.elements;
- const me = matrix.elements;
- for ( let i = 0; i < 9; i ++ ) {
- if ( te[ i ] !== me[ i ] ) return false;
- }
- return true;
- }
- fromArray( array, offset = 0 ) {
- for ( let i = 0; i < 9; i ++ ) {
- this.elements[ i ] = array[ i + offset ];
- }
- return this;
- }
- toArray( array = [], offset = 0 ) {
- const te = this.elements;
- array[ offset ] = te[ 0 ];
- array[ offset + 1 ] = te[ 1 ];
- array[ offset + 2 ] = te[ 2 ];
- array[ offset + 3 ] = te[ 3 ];
- array[ offset + 4 ] = te[ 4 ];
- array[ offset + 5 ] = te[ 5 ];
- array[ offset + 6 ] = te[ 6 ];
- array[ offset + 7 ] = te[ 7 ];
- array[ offset + 8 ] = te[ 8 ];
- return array;
- }
- }
- let _canvas;
- const ImageUtils = {
- getDataURL: function ( image ) {
- if ( /^data:/i.test( image.src ) ) {
- return image.src;
- }
- if ( typeof HTMLCanvasElement == 'undefined' ) {
- return image.src;
- }
- let canvas;
- if ( image instanceof HTMLCanvasElement ) {
- canvas = image;
- } else {
- if ( _canvas === undefined ) _canvas = document.createElementNS( 'http://www.w3.org/1999/xhtml', 'canvas' );
- _canvas.width = image.width;
- _canvas.height = image.height;
- const context = _canvas.getContext( '2d' );
- if ( image instanceof ImageData ) {
- context.putImageData( image, 0, 0 );
- } else {
- context.drawImage( image, 0, 0, image.width, image.height );
- }
- canvas = _canvas;
- }
- if ( canvas.width > 2048 || canvas.height > 2048 ) {
- return canvas.toDataURL( 'image/jpeg', 0.6 );
- } else {
- return canvas.toDataURL( 'image/png' );
- }
- }
- };
- let textureId = 0;
- function Texture( image = Texture.DEFAULT_IMAGE, mapping = Texture.DEFAULT_MAPPING, wrapS = ClampToEdgeWrapping, wrapT = ClampToEdgeWrapping, magFilter = LinearFilter, minFilter = LinearMipmapLinearFilter, format = RGBAFormat, type = UnsignedByteType, anisotropy = 1, encoding = LinearEncoding ) {
- Object.defineProperty( this, 'id', { value: textureId ++ } );
- this.uuid = MathUtils.generateUUID();
- this.name = '';
- this.image = image;
- this.mipmaps = [];
- this.mapping = mapping;
- this.wrapS = wrapS;
- this.wrapT = wrapT;
- this.magFilter = magFilter;
- this.minFilter = minFilter;
- this.anisotropy = anisotropy;
- this.format = format;
- this.internalFormat = null;
- this.type = type;
- this.offset = new Vector2$1( 0, 0 );
- this.repeat = new Vector2$1( 1, 1 );
- this.center = new Vector2$1( 0, 0 );
- this.rotation = 0;
- this.matrixAutoUpdate = true;
- this.matrix = new Matrix3();
- this.generateMipmaps = true;
- this.premultiplyAlpha = false;
- this.flipY = true;
- this.unpackAlignment = 4; // valid values: 1, 2, 4, 8 (see http://www.khronos.org/opengles/sdk/docs/man/xhtml/glPixelStorei.xml)
- // Values of encoding !== THREE.LinearEncoding only supported on map, envMap and emissiveMap.
- //
- // Also changing the encoding after already used by a Material will not automatically make the Material
- // update. You need to explicitly call Material.needsUpdate to trigger it to recompile.
- this.encoding = encoding;
- this.version = 0;
- this.onUpdate = null;
- }
- Texture.DEFAULT_IMAGE = undefined;
- Texture.DEFAULT_MAPPING = UVMapping;
- Texture.prototype = Object.assign( Object.create( EventDispatcher.prototype ), {
- constructor: Texture,
- isTexture: true,
- updateMatrix: function () {
- this.matrix.setUvTransform( this.offset.x, this.offset.y, this.repeat.x, this.repeat.y, this.rotation, this.center.x, this.center.y );
- },
- clone: function () {
- return new this.constructor().copy( this );
- },
- copy: function ( source ) {
- this.name = source.name;
- this.image = source.image;
- this.mipmaps = source.mipmaps.slice( 0 );
- this.mapping = source.mapping;
- this.wrapS = source.wrapS;
- this.wrapT = source.wrapT;
- this.magFilter = source.magFilter;
- this.minFilter = source.minFilter;
- this.anisotropy = source.anisotropy;
- this.format = source.format;
- this.internalFormat = source.internalFormat;
- this.type = source.type;
- this.offset.copy( source.offset );
- this.repeat.copy( source.repeat );
- this.center.copy( source.center );
- this.rotation = source.rotation;
- this.matrixAutoUpdate = source.matrixAutoUpdate;
- this.matrix.copy( source.matrix );
- this.generateMipmaps = source.generateMipmaps;
- this.premultiplyAlpha = source.premultiplyAlpha;
- this.flipY = source.flipY;
- this.unpackAlignment = source.unpackAlignment;
- this.encoding = source.encoding;
- return this;
- },
- toJSON: function ( meta ) {
- const isRootObject = ( meta === undefined || typeof meta === 'string' );
- if ( ! isRootObject && meta.textures[ this.uuid ] !== undefined ) {
- return meta.textures[ this.uuid ];
- }
- const output = {
- metadata: {
- version: 4.5,
- type: 'Texture',
- generator: 'Texture.toJSON'
- },
- uuid: this.uuid,
- name: this.name,
- mapping: this.mapping,
- repeat: [ this.repeat.x, this.repeat.y ],
- offset: [ this.offset.x, this.offset.y ],
- center: [ this.center.x, this.center.y ],
- rotation: this.rotation,
- wrap: [ this.wrapS, this.wrapT ],
- format: this.format,
- type: this.type,
- encoding: this.encoding,
- minFilter: this.minFilter,
- magFilter: this.magFilter,
- anisotropy: this.anisotropy,
- flipY: this.flipY,
- premultiplyAlpha: this.premultiplyAlpha,
- unpackAlignment: this.unpackAlignment
- };
- if ( this.image !== undefined ) {
- // TODO: Move to THREE.Image
- const image = this.image;
- if ( image.uuid === undefined ) {
- image.uuid = MathUtils.generateUUID(); // UGH
- }
- if ( ! isRootObject && meta.images[ image.uuid ] === undefined ) {
- let url;
- if ( Array.isArray( image ) ) {
- // process array of images e.g. CubeTexture
- url = [];
- for ( let i = 0, l = image.length; i < l; i ++ ) {
- // check cube texture with data textures
- if ( image[ i ].isDataTexture ) {
- url.push( serializeImage( image[ i ].image ) );
- } else {
- url.push( serializeImage( image[ i ] ) );
- }
- }
- } else {
- // process single image
- url = serializeImage( image );
- }
- meta.images[ image.uuid ] = {
- uuid: image.uuid,
- url: url
- };
- }
- output.image = image.uuid;
- }
- if ( ! isRootObject ) {
- meta.textures[ this.uuid ] = output;
- }
- return output;
- },
- dispose: function () {
- this.dispatchEvent( { type: 'dispose' } );
- },
- transformUv: function ( uv ) {
- if ( this.mapping !== UVMapping ) return uv;
- uv.applyMatrix3( this.matrix );
- if ( uv.x < 0 || uv.x > 1 ) {
- switch ( this.wrapS ) {
- case RepeatWrapping:
- uv.x = uv.x - Math.floor( uv.x );
- break;
- case ClampToEdgeWrapping:
- uv.x = uv.x < 0 ? 0 : 1;
- break;
- case MirroredRepeatWrapping:
- if ( Math.abs( Math.floor( uv.x ) % 2 ) === 1 ) {
- uv.x = Math.ceil( uv.x ) - uv.x;
- } else {
- uv.x = uv.x - Math.floor( uv.x );
- }
- break;
- }
- }
- if ( uv.y < 0 || uv.y > 1 ) {
- switch ( this.wrapT ) {
- case RepeatWrapping:
- uv.y = uv.y - Math.floor( uv.y );
- break;
- case ClampToEdgeWrapping:
- uv.y = uv.y < 0 ? 0 : 1;
- break;
- case MirroredRepeatWrapping:
- if ( Math.abs( Math.floor( uv.y ) % 2 ) === 1 ) {
- uv.y = Math.ceil( uv.y ) - uv.y;
- } else {
- uv.y = uv.y - Math.floor( uv.y );
- }
- break;
- }
- }
- if ( this.flipY ) {
- uv.y = 1 - uv.y;
- }
- return uv;
- }
- } );
- Object.defineProperty( Texture.prototype, 'needsUpdate', {
- set: function ( value ) {
- if ( value === true ) this.version ++;
- }
- } );
- function serializeImage( image ) {
- if ( ( typeof HTMLImageElement !== 'undefined' && image instanceof HTMLImageElement ) ||
- ( typeof HTMLCanvasElement !== 'undefined' && image instanceof HTMLCanvasElement ) ||
- ( typeof ImageBitmap !== 'undefined' && image instanceof ImageBitmap ) ) {
- // default images
- return ImageUtils.getDataURL( image );
- } else {
- if ( image.data ) {
- // images of DataTexture
- return {
- data: Array.prototype.slice.call( image.data ),
- width: image.width,
- height: image.height,
- type: image.data.constructor.name
- };
- } else {
- console.warn( 'THREE.Texture: Unable to serialize Texture.' );
- return {};
- }
- }
- }
- class Vector4 {
- constructor( x = 0, y = 0, z = 0, w = 1 ) {
- Object.defineProperty( this, 'isVector4', { value: true } );
- this.x = x;
- this.y = y;
- this.z = z;
- this.w = w;
- }
- get width() {
- return this.z;
- }
- set width( value ) {
- this.z = value;
- }
- get height() {
- return this.w;
- }
- set height( value ) {
- this.w = value;
- }
- set( x, y, z, w ) {
- this.x = x;
- this.y = y;
- this.z = z;
- this.w = w;
- return this;
- }
- setScalar( scalar ) {
- this.x = scalar;
- this.y = scalar;
- this.z = scalar;
- this.w = scalar;
- return this;
- }
- setX( x ) {
- this.x = x;
- return this;
- }
- setY( y ) {
- this.y = y;
- return this;
- }
- setZ( z ) {
- this.z = z;
- return this;
- }
- setW( w ) {
- this.w = w;
- return this;
- }
- setComponent( index, value ) {
- switch ( index ) {
- case 0: this.x = value; break;
- case 1: this.y = value; break;
- case 2: this.z = value; break;
- case 3: this.w = value; break;
- default: throw new Error( 'index is out of range: ' + index );
- }
- return this;
- }
- getComponent( index ) {
- switch ( index ) {
- case 0: return this.x;
- case 1: return this.y;
- case 2: return this.z;
- case 3: return this.w;
- default: throw new Error( 'index is out of range: ' + index );
- }
- }
- clone() {
- return new this.constructor( this.x, this.y, this.z, this.w );
- }
- copy( v ) {
- this.x = v.x;
- this.y = v.y;
- this.z = v.z;
- this.w = ( v.w !== undefined ) ? v.w : 1;
- return this;
- }
- add( v, w ) {
- if ( w !== undefined ) {
- console.warn( 'THREE.Vector4: .add() now only accepts one argument. Use .addVectors( a, b ) instead.' );
- return this.addVectors( v, w );
- }
- this.x += v.x;
- this.y += v.y;
- this.z += v.z;
- this.w += v.w;
- return this;
- }
- addScalar( s ) {
- this.x += s;
- this.y += s;
- this.z += s;
- this.w += s;
- return this;
- }
- addVectors( a, b ) {
- this.x = a.x + b.x;
- this.y = a.y + b.y;
- this.z = a.z + b.z;
- this.w = a.w + b.w;
- return this;
- }
- addScaledVector( v, s ) {
- this.x += v.x * s;
- this.y += v.y * s;
- this.z += v.z * s;
- this.w += v.w * s;
- return this;
- }
- sub( v, w ) {
- if ( w !== undefined ) {
- console.warn( 'THREE.Vector4: .sub() now only accepts one argument. Use .subVectors( a, b ) instead.' );
- return this.subVectors( v, w );
- }
- this.x -= v.x;
- this.y -= v.y;
- this.z -= v.z;
- this.w -= v.w;
- return this;
- }
- subScalar( s ) {
- this.x -= s;
- this.y -= s;
- this.z -= s;
- this.w -= s;
- return this;
- }
- subVectors( a, b ) {
- this.x = a.x - b.x;
- this.y = a.y - b.y;
- this.z = a.z - b.z;
- this.w = a.w - b.w;
- return this;
- }
- multiplyScalar( scalar ) {
- this.x *= scalar;
- this.y *= scalar;
- this.z *= scalar;
- this.w *= scalar;
- return this;
- }
- applyMatrix4( m ) {
- const x = this.x, y = this.y, z = this.z, w = this.w;
- const e = m.elements;
- this.x = e[ 0 ] * x + e[ 4 ] * y + e[ 8 ] * z + e[ 12 ] * w;
- this.y = e[ 1 ] * x + e[ 5 ] * y + e[ 9 ] * z + e[ 13 ] * w;
- this.z = e[ 2 ] * x + e[ 6 ] * y + e[ 10 ] * z + e[ 14 ] * w;
- this.w = e[ 3 ] * x + e[ 7 ] * y + e[ 11 ] * z + e[ 15 ] * w;
- return this;
- }
- divideScalar( scalar ) {
- return this.multiplyScalar( 1 / scalar );
- }
- setAxisAngleFromQuaternion( q ) {
- // http://www.euclideanspace.com/maths/geometry/rotations/conversions/quaternionToAngle/index.htm
- // q is assumed to be normalized
- this.w = 2 * Math.acos( q.w );
- const s = Math.sqrt( 1 - q.w * q.w );
- if ( s < 0.0001 ) {
- this.x = 1;
- this.y = 0;
- this.z = 0;
- } else {
- this.x = q.x / s;
- this.y = q.y / s;
- this.z = q.z / s;
- }
- return this;
- }
- setAxisAngleFromRotationMatrix( m ) {
- // http://www.euclideanspace.com/maths/geometry/rotations/conversions/matrixToAngle/index.htm
- // assumes the upper 3x3 of m is a pure rotation matrix (i.e, unscaled)
- let angle, x, y, z; // variables for result
- const epsilon = 0.01, // margin to allow for rounding errors
- epsilon2 = 0.1, // margin to distinguish between 0 and 180 degrees
- te = m.elements,
- m11 = te[ 0 ], m12 = te[ 4 ], m13 = te[ 8 ],
- m21 = te[ 1 ], m22 = te[ 5 ], m23 = te[ 9 ],
- m31 = te[ 2 ], m32 = te[ 6 ], m33 = te[ 10 ];
- if ( ( Math.abs( m12 - m21 ) < epsilon ) &&
- ( Math.abs( m13 - m31 ) < epsilon ) &&
- ( Math.abs( m23 - m32 ) < epsilon ) ) {
- // singularity found
- // first check for identity matrix which must have +1 for all terms
- // in leading diagonal and zero in other terms
- if ( ( Math.abs( m12 + m21 ) < epsilon2 ) &&
- ( Math.abs( m13 + m31 ) < epsilon2 ) &&
- ( Math.abs( m23 + m32 ) < epsilon2 ) &&
- ( Math.abs( m11 + m22 + m33 - 3 ) < epsilon2 ) ) {
- // this singularity is identity matrix so angle = 0
- this.set( 1, 0, 0, 0 );
- return this; // zero angle, arbitrary axis
- }
- // otherwise this singularity is angle = 180
- angle = Math.PI;
- const xx = ( m11 + 1 ) / 2;
- const yy = ( m22 + 1 ) / 2;
- const zz = ( m33 + 1 ) / 2;
- const xy = ( m12 + m21 ) / 4;
- const xz = ( m13 + m31 ) / 4;
- const yz = ( m23 + m32 ) / 4;
- if ( ( xx > yy ) && ( xx > zz ) ) {
- // m11 is the largest diagonal term
- if ( xx < epsilon ) {
- x = 0;
- y = 0.707106781;
- z = 0.707106781;
- } else {
- x = Math.sqrt( xx );
- y = xy / x;
- z = xz / x;
- }
- } else if ( yy > zz ) {
- // m22 is the largest diagonal term
- if ( yy < epsilon ) {
- x = 0.707106781;
- y = 0;
- z = 0.707106781;
- } else {
- y = Math.sqrt( yy );
- x = xy / y;
- z = yz / y;
- }
- } else {
- // m33 is the largest diagonal term so base result on this
- if ( zz < epsilon ) {
- x = 0.707106781;
- y = 0.707106781;
- z = 0;
- } else {
- z = Math.sqrt( zz );
- x = xz / z;
- y = yz / z;
- }
- }
- this.set( x, y, z, angle );
- return this; // return 180 deg rotation
- }
- // as we have reached here there are no singularities so we can handle normally
- let s = Math.sqrt( ( m32 - m23 ) * ( m32 - m23 ) +
- ( m13 - m31 ) * ( m13 - m31 ) +
- ( m21 - m12 ) * ( m21 - m12 ) ); // used to normalize
- if ( Math.abs( s ) < 0.001 ) s = 1;
- // prevent divide by zero, should not happen if matrix is orthogonal and should be
- // caught by singularity test above, but I've left it in just in case
- this.x = ( m32 - m23 ) / s;
- this.y = ( m13 - m31 ) / s;
- this.z = ( m21 - m12 ) / s;
- this.w = Math.acos( ( m11 + m22 + m33 - 1 ) / 2 );
- return this;
- }
- min( v ) {
- this.x = Math.min( this.x, v.x );
- this.y = Math.min( this.y, v.y );
- this.z = Math.min( this.z, v.z );
- this.w = Math.min( this.w, v.w );
- return this;
- }
- max( v ) {
- this.x = Math.max( this.x, v.x );
- this.y = Math.max( this.y, v.y );
- this.z = Math.max( this.z, v.z );
- this.w = Math.max( this.w, v.w );
- return this;
- }
- clamp( min, max ) {
- // assumes min < max, componentwise
- this.x = Math.max( min.x, Math.min( max.x, this.x ) );
- this.y = Math.max( min.y, Math.min( max.y, this.y ) );
- this.z = Math.max( min.z, Math.min( max.z, this.z ) );
- this.w = Math.max( min.w, Math.min( max.w, this.w ) );
- return this;
- }
- clampScalar( minVal, maxVal ) {
- this.x = Math.max( minVal, Math.min( maxVal, this.x ) );
- this.y = Math.max( minVal, Math.min( maxVal, this.y ) );
- this.z = Math.max( minVal, Math.min( maxVal, this.z ) );
- this.w = Math.max( minVal, Math.min( maxVal, this.w ) );
- return this;
- }
- clampLength( min, max ) {
- const length = this.length();
- return this.divideScalar( length || 1 ).multiplyScalar( Math.max( min, Math.min( max, length ) ) );
- }
- floor() {
- this.x = Math.floor( this.x );
- this.y = Math.floor( this.y );
- this.z = Math.floor( this.z );
- this.w = Math.floor( this.w );
- return this;
- }
- ceil() {
- this.x = Math.ceil( this.x );
- this.y = Math.ceil( this.y );
- this.z = Math.ceil( this.z );
- this.w = Math.ceil( this.w );
- return this;
- }
- round() {
- this.x = Math.round( this.x );
- this.y = Math.round( this.y );
- this.z = Math.round( this.z );
- this.w = Math.round( this.w );
- return this;
- }
- roundToZero() {
- this.x = ( this.x < 0 ) ? Math.ceil( this.x ) : Math.floor( this.x );
- this.y = ( this.y < 0 ) ? Math.ceil( this.y ) : Math.floor( this.y );
- this.z = ( this.z < 0 ) ? Math.ceil( this.z ) : Math.floor( this.z );
- this.w = ( this.w < 0 ) ? Math.ceil( this.w ) : Math.floor( this.w );
- return this;
- }
- negate() {
- this.x = - this.x;
- this.y = - this.y;
- this.z = - this.z;
- this.w = - this.w;
- return this;
- }
- dot( v ) {
- return this.x * v.x + this.y * v.y + this.z * v.z + this.w * v.w;
- }
- lengthSq() {
- return this.x * this.x + this.y * this.y + this.z * this.z + this.w * this.w;
- }
- length() {
- return Math.sqrt( this.x * this.x + this.y * this.y + this.z * this.z + this.w * this.w );
- }
- manhattanLength() {
- return Math.abs( this.x ) + Math.abs( this.y ) + Math.abs( this.z ) + Math.abs( this.w );
- }
- normalize() {
- return this.divideScalar( this.length() || 1 );
- }
- setLength( length ) {
- return this.normalize().multiplyScalar( length );
- }
- lerp( v, alpha ) {
- this.x += ( v.x - this.x ) * alpha;
- this.y += ( v.y - this.y ) * alpha;
- this.z += ( v.z - this.z ) * alpha;
- this.w += ( v.w - this.w ) * alpha;
- return this;
- }
- lerpVectors( v1, v2, alpha ) {
- this.x = v1.x + ( v2.x - v1.x ) * alpha;
- this.y = v1.y + ( v2.y - v1.y ) * alpha;
- this.z = v1.z + ( v2.z - v1.z ) * alpha;
- this.w = v1.w + ( v2.w - v1.w ) * alpha;
- return this;
- }
- equals( v ) {
- return ( ( v.x === this.x ) && ( v.y === this.y ) && ( v.z === this.z ) && ( v.w === this.w ) );
- }
- fromArray( array, offset = 0 ) {
- this.x = array[ offset ];
- this.y = array[ offset + 1 ];
- this.z = array[ offset + 2 ];
- this.w = array[ offset + 3 ];
- return this;
- }
- toArray( array = [], offset = 0 ) {
- array[ offset ] = this.x;
- array[ offset + 1 ] = this.y;
- array[ offset + 2 ] = this.z;
- array[ offset + 3 ] = this.w;
- return array;
- }
- fromBufferAttribute( attribute, index, offset ) {
- if ( offset !== undefined ) {
- console.warn( 'THREE.Vector4: offset has been removed from .fromBufferAttribute().' );
- }
- this.x = attribute.getX( index );
- this.y = attribute.getY( index );
- this.z = attribute.getZ( index );
- this.w = attribute.getW( index );
- return this;
- }
- random() {
- this.x = Math.random();
- this.y = Math.random();
- this.z = Math.random();
- this.w = Math.random();
- return this;
- }
- }
- /*
- In options, we can specify:
- * Texture parameters for an auto-generated target texture
- * depthBuffer/stencilBuffer: Booleans to indicate if we should generate these buffers
- */
- function WebGLRenderTarget( width, height, options ) {
- this.width = width;
- this.height = height;
- this.scissor = new Vector4( 0, 0, width, height );
- this.scissorTest = false;
- this.viewport = new Vector4( 0, 0, width, height );
- options = options || {};
- this.texture = new Texture( undefined, options.mapping, options.wrapS, options.wrapT, options.magFilter, options.minFilter, options.format, options.type, options.anisotropy, options.encoding );
- this.texture.image = {};
- this.texture.image.width = width;
- this.texture.image.height = height;
- this.texture.generateMipmaps = options.generateMipmaps !== undefined ? options.generateMipmaps : false;
- this.texture.minFilter = options.minFilter !== undefined ? options.minFilter : LinearFilter;
- this.depthBuffer = options.depthBuffer !== undefined ? options.depthBuffer : true;
- this.stencilBuffer = options.stencilBuffer !== undefined ? options.stencilBuffer : false;
- this.depthTexture = options.depthTexture !== undefined ? options.depthTexture : null;
- }
- WebGLRenderTarget.prototype = Object.assign( Object.create( EventDispatcher.prototype ), {
- constructor: WebGLRenderTarget,
- isWebGLRenderTarget: true,
- setSize: function ( width, height ) {
- if ( this.width !== width || this.height !== height ) {
- this.width = width;
- this.height = height;
- this.texture.image.width = width;
- this.texture.image.height = height;
- this.dispose();
- }
- this.viewport.set( 0, 0, width, height );
- this.scissor.set( 0, 0, width, height );
- },
- clone: function () {
- return new this.constructor().copy( this );
- },
- copy: function ( source ) {
- this.width = source.width;
- this.height = source.height;
- this.viewport.copy( source.viewport );
- this.texture = source.texture.clone();
- this.depthBuffer = source.depthBuffer;
- this.stencilBuffer = source.stencilBuffer;
- this.depthTexture = source.depthTexture;
- return this;
- },
- dispose: function () {
- this.dispatchEvent( { type: 'dispose' } );
- }
- } );
- function WebGLMultisampleRenderTarget( width, height, options ) {
- WebGLRenderTarget.call( this, width, height, options );
- this.samples = 4;
- }
- WebGLMultisampleRenderTarget.prototype = Object.assign( Object.create( WebGLRenderTarget.prototype ), {
- constructor: WebGLMultisampleRenderTarget,
- isWebGLMultisampleRenderTarget: true,
- copy: function ( source ) {
- WebGLRenderTarget.prototype.copy.call( this, source );
- this.samples = source.samples;
- return this;
- }
- } );
- class Quaternion {
- constructor( x = 0, y = 0, z = 0, w = 1 ) {
- Object.defineProperty( this, 'isQuaternion', { value: true } );
- this._x = x;
- this._y = y;
- this._z = z;
- this._w = w;
- }
- static slerp( qa, qb, qm, t ) {
- return qm.copy( qa ).slerp( qb, t );
- }
- static slerpFlat( dst, dstOffset, src0, srcOffset0, src1, srcOffset1, t ) {
- // fuzz-free, array-based Quaternion SLERP operation
- let x0 = src0[ srcOffset0 + 0 ],
- y0 = src0[ srcOffset0 + 1 ],
- z0 = src0[ srcOffset0 + 2 ],
- w0 = src0[ srcOffset0 + 3 ];
- const x1 = src1[ srcOffset1 + 0 ],
- y1 = src1[ srcOffset1 + 1 ],
- z1 = src1[ srcOffset1 + 2 ],
- w1 = src1[ srcOffset1 + 3 ];
- if ( w0 !== w1 || x0 !== x1 || y0 !== y1 || z0 !== z1 ) {
- let s = 1 - t;
- const cos = x0 * x1 + y0 * y1 + z0 * z1 + w0 * w1,
- dir = ( cos >= 0 ? 1 : - 1 ),
- sqrSin = 1 - cos * cos;
- // Skip the Slerp for tiny steps to avoid numeric problems:
- if ( sqrSin > Number.EPSILON ) {
- const sin = Math.sqrt( sqrSin ),
- len = Math.atan2( sin, cos * dir );
- s = Math.sin( s * len ) / sin;
- t = Math.sin( t * len ) / sin;
- }
- const tDir = t * dir;
- x0 = x0 * s + x1 * tDir;
- y0 = y0 * s + y1 * tDir;
- z0 = z0 * s + z1 * tDir;
- w0 = w0 * s + w1 * tDir;
- // Normalize in case we just did a lerp:
- if ( s === 1 - t ) {
- const f = 1 / Math.sqrt( x0 * x0 + y0 * y0 + z0 * z0 + w0 * w0 );
- x0 *= f;
- y0 *= f;
- z0 *= f;
- w0 *= f;
- }
- }
- dst[ dstOffset ] = x0;
- dst[ dstOffset + 1 ] = y0;
- dst[ dstOffset + 2 ] = z0;
- dst[ dstOffset + 3 ] = w0;
- }
- static multiplyQuaternionsFlat( dst, dstOffset, src0, srcOffset0, src1, srcOffset1 ) {
- const x0 = src0[ srcOffset0 ];
- const y0 = src0[ srcOffset0 + 1 ];
- const z0 = src0[ srcOffset0 + 2 ];
- const w0 = src0[ srcOffset0 + 3 ];
- const x1 = src1[ srcOffset1 ];
- const y1 = src1[ srcOffset1 + 1 ];
- const z1 = src1[ srcOffset1 + 2 ];
- const w1 = src1[ srcOffset1 + 3 ];
- dst[ dstOffset ] = x0 * w1 + w0 * x1 + y0 * z1 - z0 * y1;
- dst[ dstOffset + 1 ] = y0 * w1 + w0 * y1 + z0 * x1 - x0 * z1;
- dst[ dstOffset + 2 ] = z0 * w1 + w0 * z1 + x0 * y1 - y0 * x1;
- dst[ dstOffset + 3 ] = w0 * w1 - x0 * x1 - y0 * y1 - z0 * z1;
- return dst;
- }
- get x() {
- return this._x;
- }
- set x( value ) {
- this._x = value;
- this._onChangeCallback();
- }
- get y() {
- return this._y;
- }
- set y( value ) {
- this._y = value;
- this._onChangeCallback();
- }
- get z() {
- return this._z;
- }
- set z( value ) {
- this._z = value;
- this._onChangeCallback();
- }
- get w() {
- return this._w;
- }
- set w( value ) {
- this._w = value;
- this._onChangeCallback();
- }
- set( x, y, z, w ) {
- this._x = x;
- this._y = y;
- this._z = z;
- this._w = w;
- this._onChangeCallback();
- return this;
- }
- clone() {
- return new this.constructor( this._x, this._y, this._z, this._w );
- }
- copy( quaternion ) {
- this._x = quaternion.x;
- this._y = quaternion.y;
- this._z = quaternion.z;
- this._w = quaternion.w;
- this._onChangeCallback();
- return this;
- }
- setFromEuler( euler, update ) {
- if ( ! ( euler && euler.isEuler ) ) {
- throw new Error( 'THREE.Quaternion: .setFromEuler() now expects an Euler rotation rather than a Vector3 and order.' );
- }
- const x = euler._x, y = euler._y, z = euler._z, order = euler._order;
- // http://www.mathworks.com/matlabcentral/fileexchange/
- // 20696-function-to-convert-between-dcm-euler-angles-quaternions-and-euler-vectors/
- // content/SpinCalc.m
- const cos = Math.cos;
- const sin = Math.sin;
- const c1 = cos( x / 2 );
- const c2 = cos( y / 2 );
- const c3 = cos( z / 2 );
- const s1 = sin( x / 2 );
- const s2 = sin( y / 2 );
- const s3 = sin( z / 2 );
- switch ( order ) {
- case 'XYZ':
- this._x = s1 * c2 * c3 + c1 * s2 * s3;
- this._y = c1 * s2 * c3 - s1 * c2 * s3;
- this._z = c1 * c2 * s3 + s1 * s2 * c3;
- this._w = c1 * c2 * c3 - s1 * s2 * s3;
- break;
- case 'YXZ':
- this._x = s1 * c2 * c3 + c1 * s2 * s3;
- this._y = c1 * s2 * c3 - s1 * c2 * s3;
- this._z = c1 * c2 * s3 - s1 * s2 * c3;
- this._w = c1 * c2 * c3 + s1 * s2 * s3;
- break;
- case 'ZXY':
- this._x = s1 * c2 * c3 - c1 * s2 * s3;
- this._y = c1 * s2 * c3 + s1 * c2 * s3;
- this._z = c1 * c2 * s3 + s1 * s2 * c3;
- this._w = c1 * c2 * c3 - s1 * s2 * s3;
- break;
- case 'ZYX':
- this._x = s1 * c2 * c3 - c1 * s2 * s3;
- this._y = c1 * s2 * c3 + s1 * c2 * s3;
- this._z = c1 * c2 * s3 - s1 * s2 * c3;
- this._w = c1 * c2 * c3 + s1 * s2 * s3;
- break;
- case 'YZX':
- this._x = s1 * c2 * c3 + c1 * s2 * s3;
- this._y = c1 * s2 * c3 + s1 * c2 * s3;
- this._z = c1 * c2 * s3 - s1 * s2 * c3;
- this._w = c1 * c2 * c3 - s1 * s2 * s3;
- break;
- case 'XZY':
- this._x = s1 * c2 * c3 - c1 * s2 * s3;
- this._y = c1 * s2 * c3 - s1 * c2 * s3;
- this._z = c1 * c2 * s3 + s1 * s2 * c3;
- this._w = c1 * c2 * c3 + s1 * s2 * s3;
- break;
- default:
- console.warn( 'THREE.Quaternion: .setFromEuler() encountered an unknown order: ' + order );
- }
- if ( update !== false ) this._onChangeCallback();
- return this;
- }
- setFromAxisAngle( axis, angle ) {
- // http://www.euclideanspace.com/maths/geometry/rotations/conversions/angleToQuaternion/index.htm
- // assumes axis is normalized
- const halfAngle = angle / 2, s = Math.sin( halfAngle );
- this._x = axis.x * s;
- this._y = axis.y * s;
- this._z = axis.z * s;
- this._w = Math.cos( halfAngle );
- this._onChangeCallback();
- return this;
- }
- setFromRotationMatrix( m ) {
- // http://www.euclideanspace.com/maths/geometry/rotations/conversions/matrixToQuaternion/index.htm
- // assumes the upper 3x3 of m is a pure rotation matrix (i.e, unscaled)
- const te = m.elements,
- m11 = te[ 0 ], m12 = te[ 4 ], m13 = te[ 8 ],
- m21 = te[ 1 ], m22 = te[ 5 ], m23 = te[ 9 ],
- m31 = te[ 2 ], m32 = te[ 6 ], m33 = te[ 10 ],
- trace = m11 + m22 + m33;
- if ( trace > 0 ) {
- const s = 0.5 / Math.sqrt( trace + 1.0 );
- this._w = 0.25 / s;
- this._x = ( m32 - m23 ) * s;
- this._y = ( m13 - m31 ) * s;
- this._z = ( m21 - m12 ) * s;
- } else if ( m11 > m22 && m11 > m33 ) {
- const s = 2.0 * Math.sqrt( 1.0 + m11 - m22 - m33 );
- this._w = ( m32 - m23 ) / s;
- this._x = 0.25 * s;
- this._y = ( m12 + m21 ) / s;
- this._z = ( m13 + m31 ) / s;
- } else if ( m22 > m33 ) {
- const s = 2.0 * Math.sqrt( 1.0 + m22 - m11 - m33 );
- this._w = ( m13 - m31 ) / s;
- this._x = ( m12 + m21 ) / s;
- this._y = 0.25 * s;
- this._z = ( m23 + m32 ) / s;
- } else {
- const s = 2.0 * Math.sqrt( 1.0 + m33 - m11 - m22 );
- this._w = ( m21 - m12 ) / s;
- this._x = ( m13 + m31 ) / s;
- this._y = ( m23 + m32 ) / s;
- this._z = 0.25 * s;
- }
- this._onChangeCallback();
- return this;
- }
- setFromUnitVectors( vFrom, vTo ) {
- // assumes direction vectors vFrom and vTo are normalized
- const EPS = 0.000001;
- let r = vFrom.dot( vTo ) + 1;
- if ( r < EPS ) {
- r = 0;
- if ( Math.abs( vFrom.x ) > Math.abs( vFrom.z ) ) {
- this._x = - vFrom.y;
- this._y = vFrom.x;
- this._z = 0;
- this._w = r;
- } else {
- this._x = 0;
- this._y = - vFrom.z;
- this._z = vFrom.y;
- this._w = r;
- }
- } else {
- // crossVectors( vFrom, vTo ); // inlined to avoid cyclic dependency on Vector3
- this._x = vFrom.y * vTo.z - vFrom.z * vTo.y;
- this._y = vFrom.z * vTo.x - vFrom.x * vTo.z;
- this._z = vFrom.x * vTo.y - vFrom.y * vTo.x;
- this._w = r;
- }
- return this.normalize();
- }
- angleTo( q ) {
- return 2 * Math.acos( Math.abs( MathUtils.clamp( this.dot( q ), - 1, 1 ) ) );
- }
- rotateTowards( q, step ) {
- const angle = this.angleTo( q );
- if ( angle === 0 ) return this;
- const t = Math.min( 1, step / angle );
- this.slerp( q, t );
- return this;
- }
- identity() {
- return this.set( 0, 0, 0, 1 );
- }
- invert() {
- // quaternion is assumed to have unit length
- return this.conjugate();
- }
- conjugate() {
- this._x *= - 1;
- this._y *= - 1;
- this._z *= - 1;
- this._onChangeCallback();
- return this;
- }
- dot( v ) {
- return this._x * v._x + this._y * v._y + this._z * v._z + this._w * v._w;
- }
- lengthSq() {
- return this._x * this._x + this._y * this._y + this._z * this._z + this._w * this._w;
- }
- length() {
- return Math.sqrt( this._x * this._x + this._y * this._y + this._z * this._z + this._w * this._w );
- }
- normalize() {
- let l = this.length();
- if ( l === 0 ) {
- this._x = 0;
- this._y = 0;
- this._z = 0;
- this._w = 1;
- } else {
- l = 1 / l;
- this._x = this._x * l;
- this._y = this._y * l;
- this._z = this._z * l;
- this._w = this._w * l;
- }
- this._onChangeCallback();
- return this;
- }
- multiply( q, p ) {
- if ( p !== undefined ) {
- console.warn( 'THREE.Quaternion: .multiply() now only accepts one argument. Use .multiplyQuaternions( a, b ) instead.' );
- return this.multiplyQuaternions( q, p );
- }
- return this.multiplyQuaternions( this, q );
- }
- premultiply( q ) {
- return this.multiplyQuaternions( q, this );
- }
- multiplyQuaternions( a, b ) {
- // from http://www.euclideanspace.com/maths/algebra/realNormedAlgebra/quaternions/code/index.htm
- const qax = a._x, qay = a._y, qaz = a._z, qaw = a._w;
- const qbx = b._x, qby = b._y, qbz = b._z, qbw = b._w;
- this._x = qax * qbw + qaw * qbx + qay * qbz - qaz * qby;
- this._y = qay * qbw + qaw * qby + qaz * qbx - qax * qbz;
- this._z = qaz * qbw + qaw * qbz + qax * qby - qay * qbx;
- this._w = qaw * qbw - qax * qbx - qay * qby - qaz * qbz;
- this._onChangeCallback();
- return this;
- }
- slerp( qb, t ) {
- if ( t === 0 ) return this;
- if ( t === 1 ) return this.copy( qb );
- const x = this._x, y = this._y, z = this._z, w = this._w;
- // http://www.euclideanspace.com/maths/algebra/realNormedAlgebra/quaternions/slerp/
- let cosHalfTheta = w * qb._w + x * qb._x + y * qb._y + z * qb._z;
- if ( cosHalfTheta < 0 ) {
- this._w = - qb._w;
- this._x = - qb._x;
- this._y = - qb._y;
- this._z = - qb._z;
- cosHalfTheta = - cosHalfTheta;
- } else {
- this.copy( qb );
- }
- if ( cosHalfTheta >= 1.0 ) {
- this._w = w;
- this._x = x;
- this._y = y;
- this._z = z;
- return this;
- }
- const sqrSinHalfTheta = 1.0 - cosHalfTheta * cosHalfTheta;
- if ( sqrSinHalfTheta <= Number.EPSILON ) {
- const s = 1 - t;
- this._w = s * w + t * this._w;
- this._x = s * x + t * this._x;
- this._y = s * y + t * this._y;
- this._z = s * z + t * this._z;
- this.normalize();
- this._onChangeCallback();
- return this;
- }
- const sinHalfTheta = Math.sqrt( sqrSinHalfTheta );
- const halfTheta = Math.atan2( sinHalfTheta, cosHalfTheta );
- const ratioA = Math.sin( ( 1 - t ) * halfTheta ) / sinHalfTheta,
- ratioB = Math.sin( t * halfTheta ) / sinHalfTheta;
- this._w = ( w * ratioA + this._w * ratioB );
- this._x = ( x * ratioA + this._x * ratioB );
- this._y = ( y * ratioA + this._y * ratioB );
- this._z = ( z * ratioA + this._z * ratioB );
- this._onChangeCallback();
- return this;
- }
- equals( quaternion ) {
- return ( quaternion._x === this._x ) && ( quaternion._y === this._y ) && ( quaternion._z === this._z ) && ( quaternion._w === this._w );
- }
- fromArray( array, offset = 0 ) {
- this._x = array[ offset ];
- this._y = array[ offset + 1 ];
- this._z = array[ offset + 2 ];
- this._w = array[ offset + 3 ];
- this._onChangeCallback();
- return this;
- }
- toArray( array = [], offset = 0 ) {
- array[ offset ] = this._x;
- array[ offset + 1 ] = this._y;
- array[ offset + 2 ] = this._z;
- array[ offset + 3 ] = this._w;
- return array;
- }
- fromBufferAttribute( attribute, index ) {
- this._x = attribute.getX( index );
- this._y = attribute.getY( index );
- this._z = attribute.getZ( index );
- this._w = attribute.getW( index );
- return this;
- }
- _onChange( callback ) {
- this._onChangeCallback = callback;
- return this;
- }
- _onChangeCallback() {}
- }
- class Vector3 {
- constructor( x = 0, y = 0, z = 0 ) {
- Object.defineProperty( this, 'isVector3', { value: true } );
- this.x = x;
- this.y = y;
- this.z = z;
- }
- set( x, y, z ) {
- if ( z === undefined ) z = this.z; // sprite.scale.set(x,y)
- this.x = x;
- this.y = y;
- this.z = z;
- return this;
- }
- setScalar( scalar ) {
- this.x = scalar;
- this.y = scalar;
- this.z = scalar;
- return this;
- }
- setX( x ) {
- this.x = x;
- return this;
- }
- setY( y ) {
- this.y = y;
- return this;
- }
- setZ( z ) {
- this.z = z;
- return this;
- }
- setComponent( index, value ) {
- switch ( index ) {
- case 0: this.x = value; break;
- case 1: this.y = value; break;
- case 2: this.z = value; break;
- default: throw new Error( 'index is out of range: ' + index );
- }
- return this;
- }
- getComponent( index ) {
- switch ( index ) {
- case 0: return this.x;
- case 1: return this.y;
- case 2: return this.z;
- default: throw new Error( 'index is out of range: ' + index );
- }
- }
- clone() {
- return new this.constructor( this.x, this.y, this.z );
- }
- copy( v ) {
- this.x = v.x;
- this.y = v.y;
- this.z = v.z;
- return this;
- }
- add( v, w ) {
- if ( w !== undefined ) {
- console.warn( 'THREE.Vector3: .add() now only accepts one argument. Use .addVectors( a, b ) instead.' );
- return this.addVectors( v, w );
- }
- this.x += v.x;
- this.y += v.y;
- this.z += v.z;
- return this;
- }
- addScalar( s ) {
- this.x += s;
- this.y += s;
- this.z += s;
- return this;
- }
- addVectors( a, b ) {
- this.x = a.x + b.x;
- this.y = a.y + b.y;
- this.z = a.z + b.z;
- return this;
- }
- addScaledVector( v, s ) {
- this.x += v.x * s;
- this.y += v.y * s;
- this.z += v.z * s;
- return this;
- }
- sub( v, w ) {
- if ( w !== undefined ) {
- console.warn( 'THREE.Vector3: .sub() now only accepts one argument. Use .subVectors( a, b ) instead.' );
- return this.subVectors( v, w );
- }
- this.x -= v.x;
- this.y -= v.y;
- this.z -= v.z;
- return this;
- }
- subScalar( s ) {
- this.x -= s;
- this.y -= s;
- this.z -= s;
- return this;
- }
- subVectors( a, b ) {
- this.x = a.x - b.x;
- this.y = a.y - b.y;
- this.z = a.z - b.z;
- return this;
- }
- multiply( v, w ) {
- if ( w !== undefined ) {
- console.warn( 'THREE.Vector3: .multiply() now only accepts one argument. Use .multiplyVectors( a, b ) instead.' );
- return this.multiplyVectors( v, w );
- }
- this.x *= v.x;
- this.y *= v.y;
- this.z *= v.z;
- return this;
- }
- multiplyScalar( scalar ) {
- this.x *= scalar;
- this.y *= scalar;
- this.z *= scalar;
- return this;
- }
- multiplyVectors( a, b ) {
- this.x = a.x * b.x;
- this.y = a.y * b.y;
- this.z = a.z * b.z;
- return this;
- }
- applyEuler( euler ) {
- if ( ! ( euler && euler.isEuler ) ) {
- console.error( 'THREE.Vector3: .applyEuler() now expects an Euler rotation rather than a Vector3 and order.' );
- }
- return this.applyQuaternion( _quaternion.setFromEuler( euler ) );
- }
- applyAxisAngle( axis, angle ) {
- return this.applyQuaternion( _quaternion.setFromAxisAngle( axis, angle ) );
- }
- applyMatrix3( m ) {
- const x = this.x, y = this.y, z = this.z;
- const e = m.elements;
- this.x = e[ 0 ] * x + e[ 3 ] * y + e[ 6 ] * z;
- this.y = e[ 1 ] * x + e[ 4 ] * y + e[ 7 ] * z;
- this.z = e[ 2 ] * x + e[ 5 ] * y + e[ 8 ] * z;
- return this;
- }
- applyNormalMatrix( m ) {
- return this.applyMatrix3( m ).normalize();
- }
- applyMatrix4( m ) {
- const x = this.x, y = this.y, z = this.z;
- const e = m.elements;
- const w = 1 / ( e[ 3 ] * x + e[ 7 ] * y + e[ 11 ] * z + e[ 15 ] );
- this.x = ( e[ 0 ] * x + e[ 4 ] * y + e[ 8 ] * z + e[ 12 ] ) * w;
- this.y = ( e[ 1 ] * x + e[ 5 ] * y + e[ 9 ] * z + e[ 13 ] ) * w;
- this.z = ( e[ 2 ] * x + e[ 6 ] * y + e[ 10 ] * z + e[ 14 ] ) * w;
- return this;
- }
- applyQuaternion( q ) {
- const x = this.x, y = this.y, z = this.z;
- const qx = q.x, qy = q.y, qz = q.z, qw = q.w;
- // calculate quat * vector
- const ix = qw * x + qy * z - qz * y;
- const iy = qw * y + qz * x - qx * z;
- const iz = qw * z + qx * y - qy * x;
- const iw = - qx * x - qy * y - qz * z;
- // calculate result * inverse quat
- this.x = ix * qw + iw * - qx + iy * - qz - iz * - qy;
- this.y = iy * qw + iw * - qy + iz * - qx - ix * - qz;
- this.z = iz * qw + iw * - qz + ix * - qy - iy * - qx;
- return this;
- }
- project( camera ) {
- return this.applyMatrix4( camera.matrixWorldInverse ).applyMatrix4( camera.projectionMatrix );
- }
- unproject( camera ) {
- return this.applyMatrix4( camera.projectionMatrixInverse ).applyMatrix4( camera.matrixWorld );
- }
- transformDirection( m ) {
- // input: THREE.Matrix4 affine matrix
- // vector interpreted as a direction
- const x = this.x, y = this.y, z = this.z;
- const e = m.elements;
- this.x = e[ 0 ] * x + e[ 4 ] * y + e[ 8 ] * z;
- this.y = e[ 1 ] * x + e[ 5 ] * y + e[ 9 ] * z;
- this.z = e[ 2 ] * x + e[ 6 ] * y + e[ 10 ] * z;
- return this.normalize();
- }
- divide( v ) {
- this.x /= v.x;
- this.y /= v.y;
- this.z /= v.z;
- return this;
- }
- divideScalar( scalar ) {
- return this.multiplyScalar( 1 / scalar );
- }
- min( v ) {
- this.x = Math.min( this.x, v.x );
- this.y = Math.min( this.y, v.y );
- this.z = Math.min( this.z, v.z );
- return this;
- }
- max( v ) {
- this.x = Math.max( this.x, v.x );
- this.y = Math.max( this.y, v.y );
- this.z = Math.max( this.z, v.z );
- return this;
- }
- clamp( min, max ) {
- // assumes min < max, componentwise
- this.x = Math.max( min.x, Math.min( max.x, this.x ) );
- this.y = Math.max( min.y, Math.min( max.y, this.y ) );
- this.z = Math.max( min.z, Math.min( max.z, this.z ) );
- return this;
- }
- clampScalar( minVal, maxVal ) {
- this.x = Math.max( minVal, Math.min( maxVal, this.x ) );
- this.y = Math.max( minVal, Math.min( maxVal, this.y ) );
- this.z = Math.max( minVal, Math.min( maxVal, this.z ) );
- return this;
- }
- clampLength( min, max ) {
- const length = this.length();
- return this.divideScalar( length || 1 ).multiplyScalar( Math.max( min, Math.min( max, length ) ) );
- }
- floor() {
- this.x = Math.floor( this.x );
- this.y = Math.floor( this.y );
- this.z = Math.floor( this.z );
- return this;
- }
- ceil() {
- this.x = Math.ceil( this.x );
- this.y = Math.ceil( this.y );
- this.z = Math.ceil( this.z );
- return this;
- }
- round() {
- this.x = Math.round( this.x );
- this.y = Math.round( this.y );
- this.z = Math.round( this.z );
- return this;
- }
- roundToZero() {
- this.x = ( this.x < 0 ) ? Math.ceil( this.x ) : Math.floor( this.x );
- this.y = ( this.y < 0 ) ? Math.ceil( this.y ) : Math.floor( this.y );
- this.z = ( this.z < 0 ) ? Math.ceil( this.z ) : Math.floor( this.z );
- return this;
- }
- negate() {
- this.x = - this.x;
- this.y = - this.y;
- this.z = - this.z;
- return this;
- }
- dot( v ) {
- return this.x * v.x + this.y * v.y + this.z * v.z;
- }
- // TODO lengthSquared?
- lengthSq() {
- return this.x * this.x + this.y * this.y + this.z * this.z;
- }
- length() {
- return Math.sqrt( this.x * this.x + this.y * this.y + this.z * this.z );
- }
- manhattanLength() {
- return Math.abs( this.x ) + Math.abs( this.y ) + Math.abs( this.z );
- }
- normalize() {
- return this.divideScalar( this.length() || 1 );
- }
- setLength( length ) {
- return this.normalize().multiplyScalar( length );
- }
- lerp( v, alpha ) {
- this.x += ( v.x - this.x ) * alpha;
- this.y += ( v.y - this.y ) * alpha;
- this.z += ( v.z - this.z ) * alpha;
- return this;
- }
- lerpVectors( v1, v2, alpha ) {
- this.x = v1.x + ( v2.x - v1.x ) * alpha;
- this.y = v1.y + ( v2.y - v1.y ) * alpha;
- this.z = v1.z + ( v2.z - v1.z ) * alpha;
- return this;
- }
- cross( v, w ) {
- if ( w !== undefined ) {
- console.warn( 'THREE.Vector3: .cross() now only accepts one argument. Use .crossVectors( a, b ) instead.' );
- return this.crossVectors( v, w );
- }
- return this.crossVectors( this, v );
- }
- crossVectors( a, b ) {
- const ax = a.x, ay = a.y, az = a.z;
- const bx = b.x, by = b.y, bz = b.z;
- this.x = ay * bz - az * by;
- this.y = az * bx - ax * bz;
- this.z = ax * by - ay * bx;
- return this;
- }
- projectOnVector( v ) {
- const denominator = v.lengthSq();
- if ( denominator === 0 ) return this.set( 0, 0, 0 );
- const scalar = v.dot( this ) / denominator;
- return this.copy( v ).multiplyScalar( scalar );
- }
- projectOnPlane( planeNormal ) {
- _vector.copy( this ).projectOnVector( planeNormal );
- return this.sub( _vector );
- }
- reflect( normal ) {
- // reflect incident vector off plane orthogonal to normal
- // normal is assumed to have unit length
- return this.sub( _vector.copy( normal ).multiplyScalar( 2 * this.dot( normal ) ) );
- }
- angleTo( v ) {
- const denominator = Math.sqrt( this.lengthSq() * v.lengthSq() );
- if ( denominator === 0 ) return Math.PI / 2;
- const theta = this.dot( v ) / denominator;
- // clamp, to handle numerical problems
- return Math.acos( MathUtils.clamp( theta, - 1, 1 ) );
- }
- distanceTo( v ) {
- return Math.sqrt( this.distanceToSquared( v ) );
- }
- distanceToSquared( v ) {
- const dx = this.x - v.x, dy = this.y - v.y, dz = this.z - v.z;
- return dx * dx + dy * dy + dz * dz;
- }
- manhattanDistanceTo( v ) {
- return Math.abs( this.x - v.x ) + Math.abs( this.y - v.y ) + Math.abs( this.z - v.z );
- }
- setFromSpherical( s ) {
- return this.setFromSphericalCoords( s.radius, s.phi, s.theta );
- }
- setFromSphericalCoords( radius, phi, theta ) {
- const sinPhiRadius = Math.sin( phi ) * radius;
- this.x = sinPhiRadius * Math.sin( theta );
- this.y = Math.cos( phi ) * radius;
- this.z = sinPhiRadius * Math.cos( theta );
- return this;
- }
- setFromCylindrical( c ) {
- return this.setFromCylindricalCoords( c.radius, c.theta, c.y );
- }
- setFromCylindricalCoords( radius, theta, y ) {
- this.x = radius * Math.sin( theta );
- this.y = y;
- this.z = radius * Math.cos( theta );
- return this;
- }
- setFromMatrixPosition( m ) {
- const e = m.elements;
- this.x = e[ 12 ];
- this.y = e[ 13 ];
- this.z = e[ 14 ];
- return this;
- }
- setFromMatrixScale( m ) {
- const sx = this.setFromMatrixColumn( m, 0 ).length();
- const sy = this.setFromMatrixColumn( m, 1 ).length();
- const sz = this.setFromMatrixColumn( m, 2 ).length();
- this.x = sx;
- this.y = sy;
- this.z = sz;
- return this;
- }
- setFromMatrixColumn( m, index ) {
- return this.fromArray( m.elements, index * 4 );
- }
- setFromMatrix3Column( m, index ) {
- return this.fromArray( m.elements, index * 3 );
- }
- equals( v ) {
- return ( ( v.x === this.x ) && ( v.y === this.y ) && ( v.z === this.z ) );
- }
- fromArray( array, offset = 0 ) {
- this.x = array[ offset ];
- this.y = array[ offset + 1 ];
- this.z = array[ offset + 2 ];
- return this;
- }
- toArray( array = [], offset = 0 ) {
- array[ offset ] = this.x;
- array[ offset + 1 ] = this.y;
- array[ offset + 2 ] = this.z;
- return array;
- }
- fromBufferAttribute( attribute, index, offset ) {
- if ( offset !== undefined ) {
- console.warn( 'THREE.Vector3: offset has been removed from .fromBufferAttribute().' );
- }
- this.x = attribute.getX( index );
- this.y = attribute.getY( index );
- this.z = attribute.getZ( index );
- return this;
- }
- random() {
- this.x = Math.random();
- this.y = Math.random();
- this.z = Math.random();
- return this;
- }
- }
- const _vector = /*@__PURE__*/ new Vector3();
- const _quaternion = /*@__PURE__*/ new Quaternion();
- class Box3 {
- constructor( min, max ) {
- Object.defineProperty( this, 'isBox3', { value: true } );
- this.min = ( min !== undefined ) ? min : new Vector3( + Infinity, + Infinity, + Infinity );
- this.max = ( max !== undefined ) ? max : new Vector3( - Infinity, - Infinity, - Infinity );
- }
- set( min, max ) {
- this.min.copy( min );
- this.max.copy( max );
- return this;
- }
- setFromArray( array ) {
- let minX = + Infinity;
- let minY = + Infinity;
- let minZ = + Infinity;
- let maxX = - Infinity;
- let maxY = - Infinity;
- let maxZ = - Infinity;
- for ( let i = 0, l = array.length; i < l; i += 3 ) {
- const x = array[ i ];
- const y = array[ i + 1 ];
- const z = array[ i + 2 ];
- if ( x < minX ) minX = x;
- if ( y < minY ) minY = y;
- if ( z < minZ ) minZ = z;
- if ( x > maxX ) maxX = x;
- if ( y > maxY ) maxY = y;
- if ( z > maxZ ) maxZ = z;
- }
- this.min.set( minX, minY, minZ );
- this.max.set( maxX, maxY, maxZ );
- return this;
- }
- setFromBufferAttribute( attribute ) {
- let minX = + Infinity;
- let minY = + Infinity;
- let minZ = + Infinity;
- let maxX = - Infinity;
- let maxY = - Infinity;
- let maxZ = - Infinity;
- for ( let i = 0, l = attribute.count; i < l; i ++ ) {
- const x = attribute.getX( i );
- const y = attribute.getY( i );
- const z = attribute.getZ( i );
- if ( x < minX ) minX = x;
- if ( y < minY ) minY = y;
- if ( z < minZ ) minZ = z;
- if ( x > maxX ) maxX = x;
- if ( y > maxY ) maxY = y;
- if ( z > maxZ ) maxZ = z;
- }
- this.min.set( minX, minY, minZ );
- this.max.set( maxX, maxY, maxZ );
- return this;
- }
- setFromPoints( points ) {
- this.makeEmpty();
- for ( let i = 0, il = points.length; i < il; i ++ ) {
- this.expandByPoint( points[ i ] );
- }
- return this;
- }
- setFromCenterAndSize( center, size ) {
- const halfSize = _vector$1.copy( size ).multiplyScalar( 0.5 );
- this.min.copy( center ).sub( halfSize );
- this.max.copy( center ).add( halfSize );
- return this;
- }
- setFromObject( object ) {
- this.makeEmpty();
- return this.expandByObject( object );
- }
- clone() {
- return new this.constructor().copy( this );
- }
- copy( box ) {
- this.min.copy( box.min );
- this.max.copy( box.max );
- return this;
- }
- makeEmpty() {
- this.min.x = this.min.y = this.min.z = + Infinity;
- this.max.x = this.max.y = this.max.z = - Infinity;
- return this;
- }
- isEmpty() {
- // this is a more robust check for empty than ( volume <= 0 ) because volume can get positive with two negative axes
- return ( this.max.x < this.min.x ) || ( this.max.y < this.min.y ) || ( this.max.z < this.min.z );
- }
- getCenter( target ) {
- if ( target === undefined ) {
- console.warn( 'THREE.Box3: .getCenter() target is now required' );
- target = new Vector3();
- }
- return this.isEmpty() ? target.set( 0, 0, 0 ) : target.addVectors( this.min, this.max ).multiplyScalar( 0.5 );
- }
- getSize( target ) {
- if ( target === undefined ) {
- console.warn( 'THREE.Box3: .getSize() target is now required' );
- target = new Vector3();
- }
- return this.isEmpty() ? target.set( 0, 0, 0 ) : target.subVectors( this.max, this.min );
- }
- expandByPoint( point ) {
- this.min.min( point );
- this.max.max( point );
- return this;
- }
- expandByVector( vector ) {
- this.min.sub( vector );
- this.max.add( vector );
- return this;
- }
- expandByScalar( scalar ) {
- this.min.addScalar( - scalar );
- this.max.addScalar( scalar );
- return this;
- }
- expandByObject( object ) {
- // Computes the world-axis-aligned bounding box of an object (including its children),
- // accounting for both the object's, and children's, world transforms
- object.updateWorldMatrix( false, false );
- const geometry = object.geometry;
- if ( geometry !== undefined ) {
- if ( geometry.boundingBox === null ) {
- geometry.computeBoundingBox();
- }
- _box.copy( geometry.boundingBox );
- _box.applyMatrix4( object.matrixWorld );
- this.union( _box );
- }
- const children = object.children;
- for ( let i = 0, l = children.length; i < l; i ++ ) {
- this.expandByObject( children[ i ] );
- }
- return this;
- }
- containsPoint( point ) {
- return point.x < this.min.x || point.x > this.max.x ||
- point.y < this.min.y || point.y > this.max.y ||
- point.z < this.min.z || point.z > this.max.z ? false : true;
- }
- containsBox( box ) {
- return this.min.x <= box.min.x && box.max.x <= this.max.x &&
- this.min.y <= box.min.y && box.max.y <= this.max.y &&
- this.min.z <= box.min.z && box.max.z <= this.max.z;
- }
- getParameter( point, target ) {
- // This can potentially have a divide by zero if the box
- // has a size dimension of 0.
- if ( target === undefined ) {
- console.warn( 'THREE.Box3: .getParameter() target is now required' );
- target = new Vector3();
- }
- return target.set(
- ( point.x - this.min.x ) / ( this.max.x - this.min.x ),
- ( point.y - this.min.y ) / ( this.max.y - this.min.y ),
- ( point.z - this.min.z ) / ( this.max.z - this.min.z )
- );
- }
- intersectsBox( box ) {
- // using 6 splitting planes to rule out intersections.
- return box.max.x < this.min.x || box.min.x > this.max.x ||
- box.max.y < this.min.y || box.min.y > this.max.y ||
- box.max.z < this.min.z || box.min.z > this.max.z ? false : true;
- }
- intersectsSphere( sphere ) {
- // Find the point on the AABB closest to the sphere center.
- this.clampPoint( sphere.center, _vector$1 );
- // If that point is inside the sphere, the AABB and sphere intersect.
- return _vector$1.distanceToSquared( sphere.center ) <= ( sphere.radius * sphere.radius );
- }
- intersectsPlane( plane ) {
- // We compute the minimum and maximum dot product values. If those values
- // are on the same side (back or front) of the plane, then there is no intersection.
- let min, max;
- if ( plane.normal.x > 0 ) {
- min = plane.normal.x * this.min.x;
- max = plane.normal.x * this.max.x;
- } else {
- min = plane.normal.x * this.max.x;
- max = plane.normal.x * this.min.x;
- }
- if ( plane.normal.y > 0 ) {
- min += plane.normal.y * this.min.y;
- max += plane.normal.y * this.max.y;
- } else {
- min += plane.normal.y * this.max.y;
- max += plane.normal.y * this.min.y;
- }
- if ( plane.normal.z > 0 ) {
- min += plane.normal.z * this.min.z;
- max += plane.normal.z * this.max.z;
- } else {
- min += plane.normal.z * this.max.z;
- max += plane.normal.z * this.min.z;
- }
- return ( min <= - plane.constant && max >= - plane.constant );
- }
- intersectsTriangle( triangle ) {
- if ( this.isEmpty() ) {
- return false;
- }
- // compute box center and extents
- this.getCenter( _center );
- _extents.subVectors( this.max, _center );
- // translate triangle to aabb origin
- _v0.subVectors( triangle.a, _center );
- _v1.subVectors( triangle.b, _center );
- _v2.subVectors( triangle.c, _center );
- // compute edge vectors for triangle
- _f0.subVectors( _v1, _v0 );
- _f1.subVectors( _v2, _v1 );
- _f2.subVectors( _v0, _v2 );
- // test against axes that are given by cross product combinations of the edges of the triangle and the edges of the aabb
- // make an axis testing of each of the 3 sides of the aabb against each of the 3 sides of the triangle = 9 axis of separation
- // axis_ij = u_i x f_j (u0, u1, u2 = face normals of aabb = x,y,z axes vectors since aabb is axis aligned)
- let axes = [
- 0, - _f0.z, _f0.y, 0, - _f1.z, _f1.y, 0, - _f2.z, _f2.y,
- _f0.z, 0, - _f0.x, _f1.z, 0, - _f1.x, _f2.z, 0, - _f2.x,
- - _f0.y, _f0.x, 0, - _f1.y, _f1.x, 0, - _f2.y, _f2.x, 0
- ];
- if ( ! satForAxes( axes, _v0, _v1, _v2, _extents ) ) {
- return false;
- }
- // test 3 face normals from the aabb
- axes = [ 1, 0, 0, 0, 1, 0, 0, 0, 1 ];
- if ( ! satForAxes( axes, _v0, _v1, _v2, _extents ) ) {
- return false;
- }
- // finally testing the face normal of the triangle
- // use already existing triangle edge vectors here
- _triangleNormal.crossVectors( _f0, _f1 );
- axes = [ _triangleNormal.x, _triangleNormal.y, _triangleNormal.z ];
- return satForAxes( axes, _v0, _v1, _v2, _extents );
- }
- clampPoint( point, target ) {
- if ( target === undefined ) {
- console.warn( 'THREE.Box3: .clampPoint() target is now required' );
- target = new Vector3();
- }
- return target.copy( point ).clamp( this.min, this.max );
- }
- distanceToPoint( point ) {
- const clampedPoint = _vector$1.copy( point ).clamp( this.min, this.max );
- return clampedPoint.sub( point ).length();
- }
- getBoundingSphere( target ) {
- if ( target === undefined ) {
- console.error( 'THREE.Box3: .getBoundingSphere() target is now required' );
- //target = new Sphere(); // removed to avoid cyclic dependency
- }
- this.getCenter( target.center );
- target.radius = this.getSize( _vector$1 ).length() * 0.5;
- return target;
- }
- intersect( box ) {
- this.min.max( box.min );
- this.max.min( box.max );
- // ensure that if there is no overlap, the result is fully empty, not slightly empty with non-inf/+inf values that will cause subsequence intersects to erroneously return valid values.
- if ( this.isEmpty() ) this.makeEmpty();
- return this;
- }
- union( box ) {
- this.min.min( box.min );
- this.max.max( box.max );
- return this;
- }
- applyMatrix4( matrix ) {
- // transform of empty box is an empty box.
- if ( this.isEmpty() ) return this;
- // NOTE: I am using a binary pattern to specify all 2^3 combinations below
- _points[ 0 ].set( this.min.x, this.min.y, this.min.z ).applyMatrix4( matrix ); // 000
- _points[ 1 ].set( this.min.x, this.min.y, this.max.z ).applyMatrix4( matrix ); // 001
- _points[ 2 ].set( this.min.x, this.max.y, this.min.z ).applyMatrix4( matrix ); // 010
- _points[ 3 ].set( this.min.x, this.max.y, this.max.z ).applyMatrix4( matrix ); // 011
- _points[ 4 ].set( this.max.x, this.min.y, this.min.z ).applyMatrix4( matrix ); // 100
- _points[ 5 ].set( this.max.x, this.min.y, this.max.z ).applyMatrix4( matrix ); // 101
- _points[ 6 ].set( this.max.x, this.max.y, this.min.z ).applyMatrix4( matrix ); // 110
- _points[ 7 ].set( this.max.x, this.max.y, this.max.z ).applyMatrix4( matrix ); // 111
- this.setFromPoints( _points );
- return this;
- }
- translate( offset ) {
- this.min.add( offset );
- this.max.add( offset );
- return this;
- }
- equals( box ) {
- return box.min.equals( this.min ) && box.max.equals( this.max );
- }
- }
- function satForAxes( axes, v0, v1, v2, extents ) {
- for ( let i = 0, j = axes.length - 3; i <= j; i += 3 ) {
- _testAxis.fromArray( axes, i );
- // project the aabb onto the seperating axis
- const r = extents.x * Math.abs( _testAxis.x ) + extents.y * Math.abs( _testAxis.y ) + extents.z * Math.abs( _testAxis.z );
- // project all 3 vertices of the triangle onto the seperating axis
- const p0 = v0.dot( _testAxis );
- const p1 = v1.dot( _testAxis );
- const p2 = v2.dot( _testAxis );
- // actual test, basically see if either of the most extreme of the triangle points intersects r
- if ( Math.max( - Math.max( p0, p1, p2 ), Math.min( p0, p1, p2 ) ) > r ) {
- // points of the projected triangle are outside the projected half-length of the aabb
- // the axis is seperating and we can exit
- return false;
- }
- }
- return true;
- }
- const _points = [
- /*@__PURE__*/ new Vector3(),
- /*@__PURE__*/ new Vector3(),
- /*@__PURE__*/ new Vector3(),
- /*@__PURE__*/ new Vector3(),
- /*@__PURE__*/ new Vector3(),
- /*@__PURE__*/ new Vector3(),
- /*@__PURE__*/ new Vector3(),
- /*@__PURE__*/ new Vector3()
- ];
- const _vector$1 = /*@__PURE__*/ new Vector3();
- const _box = /*@__PURE__*/ new Box3();
- // triangle centered vertices
- const _v0 = /*@__PURE__*/ new Vector3();
- const _v1 = /*@__PURE__*/ new Vector3();
- const _v2 = /*@__PURE__*/ new Vector3();
- // triangle edge vectors
- const _f0 = /*@__PURE__*/ new Vector3();
- const _f1 = /*@__PURE__*/ new Vector3();
- const _f2 = /*@__PURE__*/ new Vector3();
- const _center = /*@__PURE__*/ new Vector3();
- const _extents = /*@__PURE__*/ new Vector3();
- const _triangleNormal = /*@__PURE__*/ new Vector3();
- const _testAxis = /*@__PURE__*/ new Vector3();
- const _box$1 = /*@__PURE__*/ new Box3();
- class Sphere {
- constructor( center, radius ) {
- this.center = ( center !== undefined ) ? center : new Vector3();
- this.radius = ( radius !== undefined ) ? radius : - 1;
- }
- set( center, radius ) {
- this.center.copy( center );
- this.radius = radius;
- return this;
- }
- setFromPoints( points, optionalCenter ) {
- const center = this.center;
- if ( optionalCenter !== undefined ) {
- center.copy( optionalCenter );
- } else {
- _box$1.setFromPoints( points ).getCenter( center );
- }
- let maxRadiusSq = 0;
- for ( let i = 0, il = points.length; i < il; i ++ ) {
- maxRadiusSq = Math.max( maxRadiusSq, center.distanceToSquared( points[ i ] ) );
- }
- this.radius = Math.sqrt( maxRadiusSq );
- return this;
- }
- clone() {
- return new this.constructor().copy( this );
- }
- copy( sphere ) {
- this.center.copy( sphere.center );
- this.radius = sphere.radius;
- return this;
- }
- isEmpty() {
- return ( this.radius < 0 );
- }
- makeEmpty() {
- this.center.set( 0, 0, 0 );
- this.radius = - 1;
- return this;
- }
- containsPoint( point ) {
- return ( point.distanceToSquared( this.center ) <= ( this.radius * this.radius ) );
- }
- distanceToPoint( point ) {
- return ( point.distanceTo( this.center ) - this.radius );
- }
- intersectsSphere( sphere ) {
- const radiusSum = this.radius + sphere.radius;
- return sphere.center.distanceToSquared( this.center ) <= ( radiusSum * radiusSum );
- }
- intersectsBox( box ) {
- return box.intersectsSphere( this );
- }
- intersectsPlane( plane ) {
- return Math.abs( plane.distanceToPoint( this.center ) ) <= this.radius;
- }
- clampPoint( point, target ) {
- const deltaLengthSq = this.center.distanceToSquared( point );
- if ( target === undefined ) {
- console.warn( 'THREE.Sphere: .clampPoint() target is now required' );
- target = new Vector3();
- }
- target.copy( point );
- if ( deltaLengthSq > ( this.radius * this.radius ) ) {
- target.sub( this.center ).normalize();
- target.multiplyScalar( this.radius ).add( this.center );
- }
- return target;
- }
- getBoundingBox( target ) {
- if ( target === undefined ) {
- console.warn( 'THREE.Sphere: .getBoundingBox() target is now required' );
- target = new Box3();
- }
- if ( this.isEmpty() ) {
- // Empty sphere produces empty bounding box
- target.makeEmpty();
- return target;
- }
- target.set( this.center, this.center );
- target.expandByScalar( this.radius );
- return target;
- }
- applyMatrix4( matrix ) {
- this.center.applyMatrix4( matrix );
- this.radius = this.radius * matrix.getMaxScaleOnAxis();
- return this;
- }
- translate( offset ) {
- this.center.add( offset );
- return this;
- }
- equals( sphere ) {
- return sphere.center.equals( this.center ) && ( sphere.radius === this.radius );
- }
- }
- const _vector$2 = /*@__PURE__*/ new Vector3();
- const _segCenter = /*@__PURE__*/ new Vector3();
- const _segDir = /*@__PURE__*/ new Vector3();
- const _diff = /*@__PURE__*/ new Vector3();
- const _edge1 = /*@__PURE__*/ new Vector3();
- const _edge2 = /*@__PURE__*/ new Vector3();
- const _normal = /*@__PURE__*/ new Vector3();
- class Ray {
- constructor( origin, direction ) {
- this.origin = ( origin !== undefined ) ? origin : new Vector3();
- this.direction = ( direction !== undefined ) ? direction : new Vector3( 0, 0, - 1 );
- }
- set( origin, direction ) {
- this.origin.copy( origin );
- this.direction.copy( direction );
- return this;
- }
- clone() {
- return new this.constructor().copy( this );
- }
- copy( ray ) {
- this.origin.copy( ray.origin );
- this.direction.copy( ray.direction );
- return this;
- }
- at( t, target ) {
- if ( target === undefined ) {
- console.warn( 'THREE.Ray: .at() target is now required' );
- target = new Vector3();
- }
- return target.copy( this.direction ).multiplyScalar( t ).add( this.origin );
- }
- lookAt( v ) {
- this.direction.copy( v ).sub( this.origin ).normalize();
- return this;
- }
- recast( t ) {
- this.origin.copy( this.at( t, _vector$2 ) );
- return this;
- }
- closestPointToPoint( point, target ) {
- if ( target === undefined ) {
- console.warn( 'THREE.Ray: .closestPointToPoint() target is now required' );
- target = new Vector3();
- }
- target.subVectors( point, this.origin );
- const directionDistance = target.dot( this.direction );
- if ( directionDistance < 0 ) {
- return target.copy( this.origin );
- }
- return target.copy( this.direction ).multiplyScalar( directionDistance ).add( this.origin );
- }
- distanceToPoint( point ) {
- return Math.sqrt( this.distanceSqToPoint( point ) );
- }
- distanceSqToPoint( point ) {
- const directionDistance = _vector$2.subVectors( point, this.origin ).dot( this.direction );
- // point behind the ray
- if ( directionDistance < 0 ) {
- return this.origin.distanceToSquared( point );
- }
- _vector$2.copy( this.direction ).multiplyScalar( directionDistance ).add( this.origin );
- return _vector$2.distanceToSquared( point );
- }
- distanceSqToSegment( v0, v1, optionalPointOnRay, optionalPointOnSegment ) {
- // from http://www.geometrictools.com/GTEngine/Include/Mathematics/GteDistRaySegment.h
- // It returns the min distance between the ray and the segment
- // defined by v0 and v1
- // It can also set two optional targets :
- // - The closest point on the ray
- // - The closest point on the segment
- _segCenter.copy( v0 ).add( v1 ).multiplyScalar( 0.5 );
- _segDir.copy( v1 ).sub( v0 ).normalize();
- _diff.copy( this.origin ).sub( _segCenter );
- const segExtent = v0.distanceTo( v1 ) * 0.5;
- const a01 = - this.direction.dot( _segDir );
- const b0 = _diff.dot( this.direction );
- const b1 = - _diff.dot( _segDir );
- const c = _diff.lengthSq();
- const det = Math.abs( 1 - a01 * a01 );
- let s0, s1, sqrDist, extDet;
- if ( det > 0 ) {
- // The ray and segment are not parallel.
- s0 = a01 * b1 - b0;
- s1 = a01 * b0 - b1;
- extDet = segExtent * det;
- if ( s0 >= 0 ) {
- if ( s1 >= - extDet ) {
- if ( s1 <= extDet ) {
- // region 0
- // Minimum at interior points of ray and segment.
- const invDet = 1 / det;
- s0 *= invDet;
- s1 *= invDet;
- sqrDist = s0 * ( s0 + a01 * s1 + 2 * b0 ) + s1 * ( a01 * s0 + s1 + 2 * b1 ) + c;
- } else {
- // region 1
- s1 = segExtent;
- s0 = Math.max( 0, - ( a01 * s1 + b0 ) );
- sqrDist = - s0 * s0 + s1 * ( s1 + 2 * b1 ) + c;
- }
- } else {
- // region 5
- s1 = - segExtent;
- s0 = Math.max( 0, - ( a01 * s1 + b0 ) );
- sqrDist = - s0 * s0 + s1 * ( s1 + 2 * b1 ) + c;
- }
- } else {
- if ( s1 <= - extDet ) {
- // region 4
- s0 = Math.max( 0, - ( - a01 * segExtent + b0 ) );
- s1 = ( s0 > 0 ) ? - segExtent : Math.min( Math.max( - segExtent, - b1 ), segExtent );
- sqrDist = - s0 * s0 + s1 * ( s1 + 2 * b1 ) + c;
- } else if ( s1 <= extDet ) {
- // region 3
- s0 = 0;
- s1 = Math.min( Math.max( - segExtent, - b1 ), segExtent );
- sqrDist = s1 * ( s1 + 2 * b1 ) + c;
- } else {
- // region 2
- s0 = Math.max( 0, - ( a01 * segExtent + b0 ) );
- s1 = ( s0 > 0 ) ? segExtent : Math.min( Math.max( - segExtent, - b1 ), segExtent );
- sqrDist = - s0 * s0 + s1 * ( s1 + 2 * b1 ) + c;
- }
- }
- } else {
- // Ray and segment are parallel.
- s1 = ( a01 > 0 ) ? - segExtent : segExtent;
- s0 = Math.max( 0, - ( a01 * s1 + b0 ) );
- sqrDist = - s0 * s0 + s1 * ( s1 + 2 * b1 ) + c;
- }
- if ( optionalPointOnRay ) {
- optionalPointOnRay.copy( this.direction ).multiplyScalar( s0 ).add( this.origin );
- }
- if ( optionalPointOnSegment ) {
- optionalPointOnSegment.copy( _segDir ).multiplyScalar( s1 ).add( _segCenter );
- }
- return sqrDist;
- }
- intersectSphere( sphere, target ) {
- _vector$2.subVectors( sphere.center, this.origin );
- const tca = _vector$2.dot( this.direction );
- const d2 = _vector$2.dot( _vector$2 ) - tca * tca;
- const radius2 = sphere.radius * sphere.radius;
- if ( d2 > radius2 ) return null;
- const thc = Math.sqrt( radius2 - d2 );
- // t0 = first intersect point - entrance on front of sphere
- const t0 = tca - thc;
- // t1 = second intersect point - exit point on back of sphere
- const t1 = tca + thc;
- // test to see if both t0 and t1 are behind the ray - if so, return null
- if ( t0 < 0 && t1 < 0 ) return null;
- // test to see if t0 is behind the ray:
- // if it is, the ray is inside the sphere, so return the second exit point scaled by t1,
- // in order to always return an intersect point that is in front of the ray.
- if ( t0 < 0 ) return this.at( t1, target );
- // else t0 is in front of the ray, so return the first collision point scaled by t0
- return this.at( t0, target );
- }
- intersectsSphere( sphere ) {
- return this.distanceSqToPoint( sphere.center ) <= ( sphere.radius * sphere.radius );
- }
- distanceToPlane( plane ) {
- const denominator = plane.normal.dot( this.direction );
- if ( denominator === 0 ) {
- // line is coplanar, return origin
- if ( plane.distanceToPoint( this.origin ) === 0 ) {
- return 0;
- }
- // Null is preferable to undefined since undefined means.... it is undefined
- return null;
- }
- const t = - ( this.origin.dot( plane.normal ) + plane.constant ) / denominator;
- // Return if the ray never intersects the plane
- return t >= 0 ? t : null;
- }
- intersectPlane( plane, target ) {
- const t = this.distanceToPlane( plane );
- if ( t === null ) {
- return null;
- }
- return this.at( t, target );
- }
- intersectsPlane( plane ) {
- // check if the ray lies on the plane first
- const distToPoint = plane.distanceToPoint( this.origin );
- if ( distToPoint === 0 ) {
- return true;
- }
- const denominator = plane.normal.dot( this.direction );
- if ( denominator * distToPoint < 0 ) {
- return true;
- }
- // ray origin is behind the plane (and is pointing behind it)
- return false;
- }
- intersectBox( box, target ) {
- let tmin, tmax, tymin, tymax, tzmin, tzmax;
- const invdirx = 1 / this.direction.x,
- invdiry = 1 / this.direction.y,
- invdirz = 1 / this.direction.z;
- const origin = this.origin;
- if ( invdirx >= 0 ) {
- tmin = ( box.min.x - origin.x ) * invdirx;
- tmax = ( box.max.x - origin.x ) * invdirx;
- } else {
- tmin = ( box.max.x - origin.x ) * invdirx;
- tmax = ( box.min.x - origin.x ) * invdirx;
- }
- if ( invdiry >= 0 ) {
- tymin = ( box.min.y - origin.y ) * invdiry;
- tymax = ( box.max.y - origin.y ) * invdiry;
- } else {
- tymin = ( box.max.y - origin.y ) * invdiry;
- tymax = ( box.min.y - origin.y ) * invdiry;
- }
- if ( ( tmin > tymax ) || ( tymin > tmax ) ) return null;
- // These lines also handle the case where tmin or tmax is NaN
- // (result of 0 * Infinity). x !== x returns true if x is NaN
- if ( tymin > tmin || tmin !== tmin ) tmin = tymin;
- if ( tymax < tmax || tmax !== tmax ) tmax = tymax;
- if ( invdirz >= 0 ) {
- tzmin = ( box.min.z - origin.z ) * invdirz;
- tzmax = ( box.max.z - origin.z ) * invdirz;
- } else {
- tzmin = ( box.max.z - origin.z ) * invdirz;
- tzmax = ( box.min.z - origin.z ) * invdirz;
- }
- if ( ( tmin > tzmax ) || ( tzmin > tmax ) ) return null;
- if ( tzmin > tmin || tmin !== tmin ) tmin = tzmin;
- if ( tzmax < tmax || tmax !== tmax ) tmax = tzmax;
- //return point closest to the ray (positive side)
- if ( tmax < 0 ) return null;
- return this.at( tmin >= 0 ? tmin : tmax, target );
- }
- intersectsBox( box ) {
- return this.intersectBox( box, _vector$2 ) !== null;
- }
- intersectTriangle( a, b, c, backfaceCulling, target ) {
- // Compute the offset origin, edges, and normal.
- // from http://www.geometrictools.com/GTEngine/Include/Mathematics/GteIntrRay3Triangle3.h
- _edge1.subVectors( b, a );
- _edge2.subVectors( c, a );
- _normal.crossVectors( _edge1, _edge2 );
- // Solve Q + t*D = b1*E1 + b2*E2 (Q = kDiff, D = ray direction,
- // E1 = kEdge1, E2 = kEdge2, N = Cross(E1,E2)) by
- // |Dot(D,N)|*b1 = sign(Dot(D,N))*Dot(D,Cross(Q,E2))
- // |Dot(D,N)|*b2 = sign(Dot(D,N))*Dot(D,Cross(E1,Q))
- // |Dot(D,N)|*t = -sign(Dot(D,N))*Dot(Q,N)
- let DdN = this.direction.dot( _normal );
- let sign;
- if ( DdN > 0 ) {
- if ( backfaceCulling ) return null;
- sign = 1;
- } else if ( DdN < 0 ) {
- sign = - 1;
- DdN = - DdN;
- } else {
- return null;
- }
- _diff.subVectors( this.origin, a );
- const DdQxE2 = sign * this.direction.dot( _edge2.crossVectors( _diff, _edge2 ) );
- // b1 < 0, no intersection
- if ( DdQxE2 < 0 ) {
- return null;
- }
- const DdE1xQ = sign * this.direction.dot( _edge1.cross( _diff ) );
- // b2 < 0, no intersection
- if ( DdE1xQ < 0 ) {
- return null;
- }
- // b1+b2 > 1, no intersection
- if ( DdQxE2 + DdE1xQ > DdN ) {
- return null;
- }
- // Line intersects triangle, check if ray does.
- const QdN = - sign * _diff.dot( _normal );
- // t < 0, no intersection
- if ( QdN < 0 ) {
- return null;
- }
- // Ray intersects triangle.
- return this.at( QdN / DdN, target );
- }
- applyMatrix4( matrix4 ) {
- this.origin.applyMatrix4( matrix4 );
- this.direction.transformDirection( matrix4 );
- return this;
- }
- equals( ray ) {
- return ray.origin.equals( this.origin ) && ray.direction.equals( this.direction );
- }
- }
- class Matrix4 {
- constructor() {
- Object.defineProperty( this, 'isMatrix4', { value: true } );
- this.elements = [
- 1, 0, 0, 0,
- 0, 1, 0, 0,
- 0, 0, 1, 0,
- 0, 0, 0, 1
- ];
- if ( arguments.length > 0 ) {
- console.error( 'THREE.Matrix4: the constructor no longer reads arguments. use .set() instead.' );
- }
- }
- set( n11, n12, n13, n14, n21, n22, n23, n24, n31, n32, n33, n34, n41, n42, n43, n44 ) {
- const te = this.elements;
- te[ 0 ] = n11; te[ 4 ] = n12; te[ 8 ] = n13; te[ 12 ] = n14;
- te[ 1 ] = n21; te[ 5 ] = n22; te[ 9 ] = n23; te[ 13 ] = n24;
- te[ 2 ] = n31; te[ 6 ] = n32; te[ 10 ] = n33; te[ 14 ] = n34;
- te[ 3 ] = n41; te[ 7 ] = n42; te[ 11 ] = n43; te[ 15 ] = n44;
- return this;
- }
- identity() {
- this.set(
- 1, 0, 0, 0,
- 0, 1, 0, 0,
- 0, 0, 1, 0,
- 0, 0, 0, 1
- );
- return this;
- }
- clone() {
- return new Matrix4().fromArray( this.elements );
- }
- copy( m ) {
- const te = this.elements;
- const me = m.elements;
- te[ 0 ] = me[ 0 ]; te[ 1 ] = me[ 1 ]; te[ 2 ] = me[ 2 ]; te[ 3 ] = me[ 3 ];
- te[ 4 ] = me[ 4 ]; te[ 5 ] = me[ 5 ]; te[ 6 ] = me[ 6 ]; te[ 7 ] = me[ 7 ];
- te[ 8 ] = me[ 8 ]; te[ 9 ] = me[ 9 ]; te[ 10 ] = me[ 10 ]; te[ 11 ] = me[ 11 ];
- te[ 12 ] = me[ 12 ]; te[ 13 ] = me[ 13 ]; te[ 14 ] = me[ 14 ]; te[ 15 ] = me[ 15 ];
- return this;
- }
- copyPosition( m ) {
- const te = this.elements, me = m.elements;
- te[ 12 ] = me[ 12 ];
- te[ 13 ] = me[ 13 ];
- te[ 14 ] = me[ 14 ];
- return this;
- }
- extractBasis( xAxis, yAxis, zAxis ) {
- xAxis.setFromMatrixColumn( this, 0 );
- yAxis.setFromMatrixColumn( this, 1 );
- zAxis.setFromMatrixColumn( this, 2 );
- return this;
- }
- makeBasis( xAxis, yAxis, zAxis ) {
- this.set(
- xAxis.x, yAxis.x, zAxis.x, 0,
- xAxis.y, yAxis.y, zAxis.y, 0,
- xAxis.z, yAxis.z, zAxis.z, 0,
- 0, 0, 0, 1
- );
- return this;
- }
- extractRotation( m ) {
- // this method does not support reflection matrices
- const te = this.elements;
- const me = m.elements;
- const scaleX = 1 / _v1$1.setFromMatrixColumn( m, 0 ).length();
- const scaleY = 1 / _v1$1.setFromMatrixColumn( m, 1 ).length();
- const scaleZ = 1 / _v1$1.setFromMatrixColumn( m, 2 ).length();
- te[ 0 ] = me[ 0 ] * scaleX;
- te[ 1 ] = me[ 1 ] * scaleX;
- te[ 2 ] = me[ 2 ] * scaleX;
- te[ 3 ] = 0;
- te[ 4 ] = me[ 4 ] * scaleY;
- te[ 5 ] = me[ 5 ] * scaleY;
- te[ 6 ] = me[ 6 ] * scaleY;
- te[ 7 ] = 0;
- te[ 8 ] = me[ 8 ] * scaleZ;
- te[ 9 ] = me[ 9 ] * scaleZ;
- te[ 10 ] = me[ 10 ] * scaleZ;
- te[ 11 ] = 0;
- te[ 12 ] = 0;
- te[ 13 ] = 0;
- te[ 14 ] = 0;
- te[ 15 ] = 1;
- return this;
- }
- makeRotationFromEuler( euler ) {
- if ( ! ( euler && euler.isEuler ) ) {
- console.error( 'THREE.Matrix4: .makeRotationFromEuler() now expects a Euler rotation rather than a Vector3 and order.' );
- }
- const te = this.elements;
- const x = euler.x, y = euler.y, z = euler.z;
- const a = Math.cos( x ), b = Math.sin( x );
- const c = Math.cos( y ), d = Math.sin( y );
- const e = Math.cos( z ), f = Math.sin( z );
- if ( euler.order === 'XYZ' ) {
- const ae = a * e, af = a * f, be = b * e, bf = b * f;
- te[ 0 ] = c * e;
- te[ 4 ] = - c * f;
- te[ 8 ] = d;
- te[ 1 ] = af + be * d;
- te[ 5 ] = ae - bf * d;
- te[ 9 ] = - b * c;
- te[ 2 ] = bf - ae * d;
- te[ 6 ] = be + af * d;
- te[ 10 ] = a * c;
- } else if ( euler.order === 'YXZ' ) {
- const ce = c * e, cf = c * f, de = d * e, df = d * f;
- te[ 0 ] = ce + df * b;
- te[ 4 ] = de * b - cf;
- te[ 8 ] = a * d;
- te[ 1 ] = a * f;
- te[ 5 ] = a * e;
- te[ 9 ] = - b;
- te[ 2 ] = cf * b - de;
- te[ 6 ] = df + ce * b;
- te[ 10 ] = a * c;
- } else if ( euler.order === 'ZXY' ) {
- const ce = c * e, cf = c * f, de = d * e, df = d * f;
- te[ 0 ] = ce - df * b;
- te[ 4 ] = - a * f;
- te[ 8 ] = de + cf * b;
- te[ 1 ] = cf + de * b;
- te[ 5 ] = a * e;
- te[ 9 ] = df - ce * b;
- te[ 2 ] = - a * d;
- te[ 6 ] = b;
- te[ 10 ] = a * c;
- } else if ( euler.order === 'ZYX' ) {
- const ae = a * e, af = a * f, be = b * e, bf = b * f;
- te[ 0 ] = c * e;
- te[ 4 ] = be * d - af;
- te[ 8 ] = ae * d + bf;
- te[ 1 ] = c * f;
- te[ 5 ] = bf * d + ae;
- te[ 9 ] = af * d - be;
- te[ 2 ] = - d;
- te[ 6 ] = b * c;
- te[ 10 ] = a * c;
- } else if ( euler.order === 'YZX' ) {
- const ac = a * c, ad = a * d, bc = b * c, bd = b * d;
- te[ 0 ] = c * e;
- te[ 4 ] = bd - ac * f;
- te[ 8 ] = bc * f + ad;
- te[ 1 ] = f;
- te[ 5 ] = a * e;
- te[ 9 ] = - b * e;
- te[ 2 ] = - d * e;
- te[ 6 ] = ad * f + bc;
- te[ 10 ] = ac - bd * f;
- } else if ( euler.order === 'XZY' ) {
- const ac = a * c, ad = a * d, bc = b * c, bd = b * d;
- te[ 0 ] = c * e;
- te[ 4 ] = - f;
- te[ 8 ] = d * e;
- te[ 1 ] = ac * f + bd;
- te[ 5 ] = a * e;
- te[ 9 ] = ad * f - bc;
- te[ 2 ] = bc * f - ad;
- te[ 6 ] = b * e;
- te[ 10 ] = bd * f + ac;
- }
- // bottom row
- te[ 3 ] = 0;
- te[ 7 ] = 0;
- te[ 11 ] = 0;
- // last column
- te[ 12 ] = 0;
- te[ 13 ] = 0;
- te[ 14 ] = 0;
- te[ 15 ] = 1;
- return this;
- }
- makeRotationFromQuaternion( q ) {
- return this.compose( _zero, q, _one );
- }
- lookAt( eye, target, up ) {
- const te = this.elements;
- _z.subVectors( eye, target );
- if ( _z.lengthSq() === 0 ) {
- // eye and target are in the same position
- _z.z = 1;
- }
- _z.normalize();
- _x.crossVectors( up, _z );
- if ( _x.lengthSq() === 0 ) {
- // up and z are parallel
- if ( Math.abs( up.z ) === 1 ) {
- _z.x += 0.0001;
- } else {
- _z.z += 0.0001;
- }
- _z.normalize();
- _x.crossVectors( up, _z );
- }
- _x.normalize();
- _y.crossVectors( _z, _x );
- te[ 0 ] = _x.x; te[ 4 ] = _y.x; te[ 8 ] = _z.x;
- te[ 1 ] = _x.y; te[ 5 ] = _y.y; te[ 9 ] = _z.y;
- te[ 2 ] = _x.z; te[ 6 ] = _y.z; te[ 10 ] = _z.z;
- return this;
- }
- multiply( m, n ) {
- if ( n !== undefined ) {
- console.warn( 'THREE.Matrix4: .multiply() now only accepts one argument. Use .multiplyMatrices( a, b ) instead.' );
- return this.multiplyMatrices( m, n );
- }
- return this.multiplyMatrices( this, m );
- }
- premultiply( m ) {
- return this.multiplyMatrices( m, this );
- }
- multiplyMatrices( a, b ) {
- const ae = a.elements;
- const be = b.elements;
- const te = this.elements;
- const a11 = ae[ 0 ], a12 = ae[ 4 ], a13 = ae[ 8 ], a14 = ae[ 12 ];
- const a21 = ae[ 1 ], a22 = ae[ 5 ], a23 = ae[ 9 ], a24 = ae[ 13 ];
- const a31 = ae[ 2 ], a32 = ae[ 6 ], a33 = ae[ 10 ], a34 = ae[ 14 ];
- const a41 = ae[ 3 ], a42 = ae[ 7 ], a43 = ae[ 11 ], a44 = ae[ 15 ];
- const b11 = be[ 0 ], b12 = be[ 4 ], b13 = be[ 8 ], b14 = be[ 12 ];
- const b21 = be[ 1 ], b22 = be[ 5 ], b23 = be[ 9 ], b24 = be[ 13 ];
- const b31 = be[ 2 ], b32 = be[ 6 ], b33 = be[ 10 ], b34 = be[ 14 ];
- const b41 = be[ 3 ], b42 = be[ 7 ], b43 = be[ 11 ], b44 = be[ 15 ];
- te[ 0 ] = a11 * b11 + a12 * b21 + a13 * b31 + a14 * b41;
- te[ 4 ] = a11 * b12 + a12 * b22 + a13 * b32 + a14 * b42;
- te[ 8 ] = a11 * b13 + a12 * b23 + a13 * b33 + a14 * b43;
- te[ 12 ] = a11 * b14 + a12 * b24 + a13 * b34 + a14 * b44;
- te[ 1 ] = a21 * b11 + a22 * b21 + a23 * b31 + a24 * b41;
- te[ 5 ] = a21 * b12 + a22 * b22 + a23 * b32 + a24 * b42;
- te[ 9 ] = a21 * b13 + a22 * b23 + a23 * b33 + a24 * b43;
- te[ 13 ] = a21 * b14 + a22 * b24 + a23 * b34 + a24 * b44;
- te[ 2 ] = a31 * b11 + a32 * b21 + a33 * b31 + a34 * b41;
- te[ 6 ] = a31 * b12 + a32 * b22 + a33 * b32 + a34 * b42;
- te[ 10 ] = a31 * b13 + a32 * b23 + a33 * b33 + a34 * b43;
- te[ 14 ] = a31 * b14 + a32 * b24 + a33 * b34 + a34 * b44;
- te[ 3 ] = a41 * b11 + a42 * b21 + a43 * b31 + a44 * b41;
- te[ 7 ] = a41 * b12 + a42 * b22 + a43 * b32 + a44 * b42;
- te[ 11 ] = a41 * b13 + a42 * b23 + a43 * b33 + a44 * b43;
- te[ 15 ] = a41 * b14 + a42 * b24 + a43 * b34 + a44 * b44;
- return this;
- }
- multiplyScalar( s ) {
- const te = this.elements;
- te[ 0 ] *= s; te[ 4 ] *= s; te[ 8 ] *= s; te[ 12 ] *= s;
- te[ 1 ] *= s; te[ 5 ] *= s; te[ 9 ] *= s; te[ 13 ] *= s;
- te[ 2 ] *= s; te[ 6 ] *= s; te[ 10 ] *= s; te[ 14 ] *= s;
- te[ 3 ] *= s; te[ 7 ] *= s; te[ 11 ] *= s; te[ 15 ] *= s;
- return this;
- }
- determinant() {
- const te = this.elements;
- const n11 = te[ 0 ], n12 = te[ 4 ], n13 = te[ 8 ], n14 = te[ 12 ];
- const n21 = te[ 1 ], n22 = te[ 5 ], n23 = te[ 9 ], n24 = te[ 13 ];
- const n31 = te[ 2 ], n32 = te[ 6 ], n33 = te[ 10 ], n34 = te[ 14 ];
- const n41 = te[ 3 ], n42 = te[ 7 ], n43 = te[ 11 ], n44 = te[ 15 ];
- //TODO: make this more efficient
- //( based on http://www.euclideanspace.com/maths/algebra/matrix/functions/inverse/fourD/index.htm )
- return (
- n41 * (
- + n14 * n23 * n32
- - n13 * n24 * n32
- - n14 * n22 * n33
- + n12 * n24 * n33
- + n13 * n22 * n34
- - n12 * n23 * n34
- ) +
- n42 * (
- + n11 * n23 * n34
- - n11 * n24 * n33
- + n14 * n21 * n33
- - n13 * n21 * n34
- + n13 * n24 * n31
- - n14 * n23 * n31
- ) +
- n43 * (
- + n11 * n24 * n32
- - n11 * n22 * n34
- - n14 * n21 * n32
- + n12 * n21 * n34
- + n14 * n22 * n31
- - n12 * n24 * n31
- ) +
- n44 * (
- - n13 * n22 * n31
- - n11 * n23 * n32
- + n11 * n22 * n33
- + n13 * n21 * n32
- - n12 * n21 * n33
- + n12 * n23 * n31
- )
- );
- }
- transpose() {
- const te = this.elements;
- let tmp;
- tmp = te[ 1 ]; te[ 1 ] = te[ 4 ]; te[ 4 ] = tmp;
- tmp = te[ 2 ]; te[ 2 ] = te[ 8 ]; te[ 8 ] = tmp;
- tmp = te[ 6 ]; te[ 6 ] = te[ 9 ]; te[ 9 ] = tmp;
- tmp = te[ 3 ]; te[ 3 ] = te[ 12 ]; te[ 12 ] = tmp;
- tmp = te[ 7 ]; te[ 7 ] = te[ 13 ]; te[ 13 ] = tmp;
- tmp = te[ 11 ]; te[ 11 ] = te[ 14 ]; te[ 14 ] = tmp;
- return this;
- }
- setPosition( x, y, z ) {
- const te = this.elements;
- if ( x.isVector3 ) {
- te[ 12 ] = x.x;
- te[ 13 ] = x.y;
- te[ 14 ] = x.z;
- } else {
- te[ 12 ] = x;
- te[ 13 ] = y;
- te[ 14 ] = z;
- }
- return this;
- }
- invert() {
- // based on http://www.euclideanspace.com/maths/algebra/matrix/functions/inverse/fourD/index.htm
- const te = this.elements,
- n11 = te[ 0 ], n21 = te[ 1 ], n31 = te[ 2 ], n41 = te[ 3 ],
- n12 = te[ 4 ], n22 = te[ 5 ], n32 = te[ 6 ], n42 = te[ 7 ],
- n13 = te[ 8 ], n23 = te[ 9 ], n33 = te[ 10 ], n43 = te[ 11 ],
- n14 = te[ 12 ], n24 = te[ 13 ], n34 = te[ 14 ], n44 = te[ 15 ],
- t11 = n23 * n34 * n42 - n24 * n33 * n42 + n24 * n32 * n43 - n22 * n34 * n43 - n23 * n32 * n44 + n22 * n33 * n44,
- t12 = n14 * n33 * n42 - n13 * n34 * n42 - n14 * n32 * n43 + n12 * n34 * n43 + n13 * n32 * n44 - n12 * n33 * n44,
- t13 = n13 * n24 * n42 - n14 * n23 * n42 + n14 * n22 * n43 - n12 * n24 * n43 - n13 * n22 * n44 + n12 * n23 * n44,
- t14 = n14 * n23 * n32 - n13 * n24 * n32 - n14 * n22 * n33 + n12 * n24 * n33 + n13 * n22 * n34 - n12 * n23 * n34;
- const det = n11 * t11 + n21 * t12 + n31 * t13 + n41 * t14;
- if ( det === 0 ) return this.set( 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 );
- const detInv = 1 / det;
- te[ 0 ] = t11 * detInv;
- te[ 1 ] = ( n24 * n33 * n41 - n23 * n34 * n41 - n24 * n31 * n43 + n21 * n34 * n43 + n23 * n31 * n44 - n21 * n33 * n44 ) * detInv;
- te[ 2 ] = ( n22 * n34 * n41 - n24 * n32 * n41 + n24 * n31 * n42 - n21 * n34 * n42 - n22 * n31 * n44 + n21 * n32 * n44 ) * detInv;
- te[ 3 ] = ( n23 * n32 * n41 - n22 * n33 * n41 - n23 * n31 * n42 + n21 * n33 * n42 + n22 * n31 * n43 - n21 * n32 * n43 ) * detInv;
- te[ 4 ] = t12 * detInv;
- te[ 5 ] = ( n13 * n34 * n41 - n14 * n33 * n41 + n14 * n31 * n43 - n11 * n34 * n43 - n13 * n31 * n44 + n11 * n33 * n44 ) * detInv;
- te[ 6 ] = ( n14 * n32 * n41 - n12 * n34 * n41 - n14 * n31 * n42 + n11 * n34 * n42 + n12 * n31 * n44 - n11 * n32 * n44 ) * detInv;
- te[ 7 ] = ( n12 * n33 * n41 - n13 * n32 * n41 + n13 * n31 * n42 - n11 * n33 * n42 - n12 * n31 * n43 + n11 * n32 * n43 ) * detInv;
- te[ 8 ] = t13 * detInv;
- te[ 9 ] = ( n14 * n23 * n41 - n13 * n24 * n41 - n14 * n21 * n43 + n11 * n24 * n43 + n13 * n21 * n44 - n11 * n23 * n44 ) * detInv;
- te[ 10 ] = ( n12 * n24 * n41 - n14 * n22 * n41 + n14 * n21 * n42 - n11 * n24 * n42 - n12 * n21 * n44 + n11 * n22 * n44 ) * detInv;
- te[ 11 ] = ( n13 * n22 * n41 - n12 * n23 * n41 - n13 * n21 * n42 + n11 * n23 * n42 + n12 * n21 * n43 - n11 * n22 * n43 ) * detInv;
- te[ 12 ] = t14 * detInv;
- te[ 13 ] = ( n13 * n24 * n31 - n14 * n23 * n31 + n14 * n21 * n33 - n11 * n24 * n33 - n13 * n21 * n34 + n11 * n23 * n34 ) * detInv;
- te[ 14 ] = ( n14 * n22 * n31 - n12 * n24 * n31 - n14 * n21 * n32 + n11 * n24 * n32 + n12 * n21 * n34 - n11 * n22 * n34 ) * detInv;
- te[ 15 ] = ( n12 * n23 * n31 - n13 * n22 * n31 + n13 * n21 * n32 - n11 * n23 * n32 - n12 * n21 * n33 + n11 * n22 * n33 ) * detInv;
- return this;
- }
- scale( v ) {
- const te = this.elements;
- const x = v.x, y = v.y, z = v.z;
- te[ 0 ] *= x; te[ 4 ] *= y; te[ 8 ] *= z;
- te[ 1 ] *= x; te[ 5 ] *= y; te[ 9 ] *= z;
- te[ 2 ] *= x; te[ 6 ] *= y; te[ 10 ] *= z;
- te[ 3 ] *= x; te[ 7 ] *= y; te[ 11 ] *= z;
- return this;
- }
- getMaxScaleOnAxis() {
- const te = this.elements;
- const scaleXSq = te[ 0 ] * te[ 0 ] + te[ 1 ] * te[ 1 ] + te[ 2 ] * te[ 2 ];
- const scaleYSq = te[ 4 ] * te[ 4 ] + te[ 5 ] * te[ 5 ] + te[ 6 ] * te[ 6 ];
- const scaleZSq = te[ 8 ] * te[ 8 ] + te[ 9 ] * te[ 9 ] + te[ 10 ] * te[ 10 ];
- return Math.sqrt( Math.max( scaleXSq, scaleYSq, scaleZSq ) );
- }
- makeTranslation( x, y, z ) {
- this.set(
- 1, 0, 0, x,
- 0, 1, 0, y,
- 0, 0, 1, z,
- 0, 0, 0, 1
- );
- return this;
- }
- makeRotationX( theta ) {
- const c = Math.cos( theta ), s = Math.sin( theta );
- this.set(
- 1, 0, 0, 0,
- 0, c, - s, 0,
- 0, s, c, 0,
- 0, 0, 0, 1
- );
- return this;
- }
- makeRotationY( theta ) {
- const c = Math.cos( theta ), s = Math.sin( theta );
- this.set(
- c, 0, s, 0,
- 0, 1, 0, 0,
- - s, 0, c, 0,
- 0, 0, 0, 1
- );
- return this;
- }
- makeRotationZ( theta ) {
- const c = Math.cos( theta ), s = Math.sin( theta );
- this.set(
- c, - s, 0, 0,
- s, c, 0, 0,
- 0, 0, 1, 0,
- 0, 0, 0, 1
- );
- return this;
- }
- makeRotationAxis( axis, angle ) {
- // Based on http://www.gamedev.net/reference/articles/article1199.asp
- const c = Math.cos( angle );
- const s = Math.sin( angle );
- const t = 1 - c;
- const x = axis.x, y = axis.y, z = axis.z;
- const tx = t * x, ty = t * y;
- this.set(
- tx * x + c, tx * y - s * z, tx * z + s * y, 0,
- tx * y + s * z, ty * y + c, ty * z - s * x, 0,
- tx * z - s * y, ty * z + s * x, t * z * z + c, 0,
- 0, 0, 0, 1
- );
- return this;
- }
- makeScale( x, y, z ) {
- this.set(
- x, 0, 0, 0,
- 0, y, 0, 0,
- 0, 0, z, 0,
- 0, 0, 0, 1
- );
- return this;
- }
- makeShear( x, y, z ) {
- this.set(
- 1, y, z, 0,
- x, 1, z, 0,
- x, y, 1, 0,
- 0, 0, 0, 1
- );
- return this;
- }
- compose( position, quaternion, scale ) {
- const te = this.elements;
- const x = quaternion._x, y = quaternion._y, z = quaternion._z, w = quaternion._w;
- const x2 = x + x, y2 = y + y, z2 = z + z;
- const xx = x * x2, xy = x * y2, xz = x * z2;
- const yy = y * y2, yz = y * z2, zz = z * z2;
- const wx = w * x2, wy = w * y2, wz = w * z2;
- const sx = scale.x, sy = scale.y, sz = scale.z;
- te[ 0 ] = ( 1 - ( yy + zz ) ) * sx;
- te[ 1 ] = ( xy + wz ) * sx;
- te[ 2 ] = ( xz - wy ) * sx;
- te[ 3 ] = 0;
- te[ 4 ] = ( xy - wz ) * sy;
- te[ 5 ] = ( 1 - ( xx + zz ) ) * sy;
- te[ 6 ] = ( yz + wx ) * sy;
- te[ 7 ] = 0;
- te[ 8 ] = ( xz + wy ) * sz;
- te[ 9 ] = ( yz - wx ) * sz;
- te[ 10 ] = ( 1 - ( xx + yy ) ) * sz;
- te[ 11 ] = 0;
- te[ 12 ] = position.x;
- te[ 13 ] = position.y;
- te[ 14 ] = position.z;
- te[ 15 ] = 1;
- return this;
- }
- decompose( position, quaternion, scale ) {
- const te = this.elements;
- let sx = _v1$1.set( te[ 0 ], te[ 1 ], te[ 2 ] ).length();
- const sy = _v1$1.set( te[ 4 ], te[ 5 ], te[ 6 ] ).length();
- const sz = _v1$1.set( te[ 8 ], te[ 9 ], te[ 10 ] ).length();
- // if determine is negative, we need to invert one scale
- const det = this.determinant();
- if ( det < 0 ) sx = - sx;
- position.x = te[ 12 ];
- position.y = te[ 13 ];
- position.z = te[ 14 ];
- // scale the rotation part
- _m1.copy( this );
- const invSX = 1 / sx;
- const invSY = 1 / sy;
- const invSZ = 1 / sz;
- _m1.elements[ 0 ] *= invSX;
- _m1.elements[ 1 ] *= invSX;
- _m1.elements[ 2 ] *= invSX;
- _m1.elements[ 4 ] *= invSY;
- _m1.elements[ 5 ] *= invSY;
- _m1.elements[ 6 ] *= invSY;
- _m1.elements[ 8 ] *= invSZ;
- _m1.elements[ 9 ] *= invSZ;
- _m1.elements[ 10 ] *= invSZ;
- quaternion.setFromRotationMatrix( _m1 );
- scale.x = sx;
- scale.y = sy;
- scale.z = sz;
- return this;
- }
- makePerspective( left, right, top, bottom, near, far ) {
- if ( far === undefined ) {
- console.warn( 'THREE.Matrix4: .makePerspective() has been redefined and has a new signature. Please check the docs.' );
- }
- const te = this.elements;
- const x = 2 * near / ( right - left );
- const y = 2 * near / ( top - bottom );
- const a = ( right + left ) / ( right - left );
- const b = ( top + bottom ) / ( top - bottom );
- const c = - ( far + near ) / ( far - near );
- const d = - 2 * far * near / ( far - near );
- te[ 0 ] = x; te[ 4 ] = 0; te[ 8 ] = a; te[ 12 ] = 0;
- te[ 1 ] = 0; te[ 5 ] = y; te[ 9 ] = b; te[ 13 ] = 0;
- te[ 2 ] = 0; te[ 6 ] = 0; te[ 10 ] = c; te[ 14 ] = d;
- te[ 3 ] = 0; te[ 7 ] = 0; te[ 11 ] = - 1; te[ 15 ] = 0;
- return this;
- }
- makeOrthographic( left, right, top, bottom, near, far ) {
- const te = this.elements;
- const w = 1.0 / ( right - left );
- const h = 1.0 / ( top - bottom );
- const p = 1.0 / ( far - near );
- const x = ( right + left ) * w;
- const y = ( top + bottom ) * h;
- const z = ( far + near ) * p;
- te[ 0 ] = 2 * w; te[ 4 ] = 0; te[ 8 ] = 0; te[ 12 ] = - x;
- te[ 1 ] = 0; te[ 5 ] = 2 * h; te[ 9 ] = 0; te[ 13 ] = - y;
- te[ 2 ] = 0; te[ 6 ] = 0; te[ 10 ] = - 2 * p; te[ 14 ] = - z;
- te[ 3 ] = 0; te[ 7 ] = 0; te[ 11 ] = 0; te[ 15 ] = 1;
- return this;
- }
- equals( matrix ) {
- const te = this.elements;
- const me = matrix.elements;
- for ( let i = 0; i < 16; i ++ ) {
- if ( te[ i ] !== me[ i ] ) return false;
- }
- return true;
- }
- fromArray( array, offset = 0 ) {
- for ( let i = 0; i < 16; i ++ ) {
- this.elements[ i ] = array[ i + offset ];
- }
- return this;
- }
- toArray( array = [], offset = 0 ) {
- const te = this.elements;
- array[ offset ] = te[ 0 ];
- array[ offset + 1 ] = te[ 1 ];
- array[ offset + 2 ] = te[ 2 ];
- array[ offset + 3 ] = te[ 3 ];
- array[ offset + 4 ] = te[ 4 ];
- array[ offset + 5 ] = te[ 5 ];
- array[ offset + 6 ] = te[ 6 ];
- array[ offset + 7 ] = te[ 7 ];
- array[ offset + 8 ] = te[ 8 ];
- array[ offset + 9 ] = te[ 9 ];
- array[ offset + 10 ] = te[ 10 ];
- array[ offset + 11 ] = te[ 11 ];
- array[ offset + 12 ] = te[ 12 ];
- array[ offset + 13 ] = te[ 13 ];
- array[ offset + 14 ] = te[ 14 ];
- array[ offset + 15 ] = te[ 15 ];
- return array;
- }
- }
- const _v1$1 = /*@__PURE__*/ new Vector3();
- const _m1 = /*@__PURE__*/ new Matrix4();
- const _zero = /*@__PURE__*/ new Vector3( 0, 0, 0 );
- const _one = /*@__PURE__*/ new Vector3( 1, 1, 1 );
- const _x = /*@__PURE__*/ new Vector3();
- const _y = /*@__PURE__*/ new Vector3();
- const _z = /*@__PURE__*/ new Vector3();
- class Euler {
- constructor( x = 0, y = 0, z = 0, order = Euler.DefaultOrder ) {
- Object.defineProperty( this, 'isEuler', { value: true } );
- this._x = x;
- this._y = y;
- this._z = z;
- this._order = order;
- }
- get x() {
- return this._x;
- }
- set x( value ) {
- this._x = value;
- this._onChangeCallback();
- }
- get y() {
- return this._y;
- }
- set y( value ) {
- this._y = value;
- this._onChangeCallback();
- }
- get z() {
- return this._z;
- }
- set z( value ) {
- this._z = value;
- this._onChangeCallback();
- }
- get order() {
- return this._order;
- }
- set order( value ) {
- this._order = value;
- this._onChangeCallback();
- }
- set( x, y, z, order ) {
- this._x = x;
- this._y = y;
- this._z = z;
- this._order = order || this._order;
- this._onChangeCallback();
- return this;
- }
- clone() {
- return new this.constructor( this._x, this._y, this._z, this._order );
- }
- copy( euler ) {
- this._x = euler._x;
- this._y = euler._y;
- this._z = euler._z;
- this._order = euler._order;
- this._onChangeCallback();
- return this;
- }
- setFromRotationMatrix( m, order, update ) {
- const clamp = MathUtils.clamp;
- // assumes the upper 3x3 of m is a pure rotation matrix (i.e, unscaled)
- const te = m.elements;
- const m11 = te[ 0 ], m12 = te[ 4 ], m13 = te[ 8 ];
- const m21 = te[ 1 ], m22 = te[ 5 ], m23 = te[ 9 ];
- const m31 = te[ 2 ], m32 = te[ 6 ], m33 = te[ 10 ];
- order = order || this._order;
- switch ( order ) {
- case 'XYZ':
- this._y = Math.asin( clamp( m13, - 1, 1 ) );
- if ( Math.abs( m13 ) < 0.9999999 ) {
- this._x = Math.atan2( - m23, m33 );
- this._z = Math.atan2( - m12, m11 );
- } else {
- this._x = Math.atan2( m32, m22 );
- this._z = 0;
- }
- break;
- case 'YXZ':
- this._x = Math.asin( - clamp( m23, - 1, 1 ) );
- if ( Math.abs( m23 ) < 0.9999999 ) {
- this._y = Math.atan2( m13, m33 );
- this._z = Math.atan2( m21, m22 );
- } else {
- this._y = Math.atan2( - m31, m11 );
- this._z = 0;
- }
- break;
- case 'ZXY':
- this._x = Math.asin( clamp( m32, - 1, 1 ) );
- if ( Math.abs( m32 ) < 0.9999999 ) {
- this._y = Math.atan2( - m31, m33 );
- this._z = Math.atan2( - m12, m22 );
- } else {
- this._y = 0;
- this._z = Math.atan2( m21, m11 );
- }
- break;
- case 'ZYX':
- this._y = Math.asin( - clamp( m31, - 1, 1 ) );
- if ( Math.abs( m31 ) < 0.9999999 ) {
- this._x = Math.atan2( m32, m33 );
- this._z = Math.atan2( m21, m11 );
- } else {
- this._x = 0;
- this._z = Math.atan2( - m12, m22 );
- }
- break;
- case 'YZX':
- this._z = Math.asin( clamp( m21, - 1, 1 ) );
- if ( Math.abs( m21 ) < 0.9999999 ) {
- this._x = Math.atan2( - m23, m22 );
- this._y = Math.atan2( - m31, m11 );
- } else {
- this._x = 0;
- this._y = Math.atan2( m13, m33 );
- }
- break;
- case 'XZY':
- this._z = Math.asin( - clamp( m12, - 1, 1 ) );
- if ( Math.abs( m12 ) < 0.9999999 ) {
- this._x = Math.atan2( m32, m22 );
- this._y = Math.atan2( m13, m11 );
- } else {
- this._x = Math.atan2( - m23, m33 );
- this._y = 0;
- }
- break;
- default:
- console.warn( 'THREE.Euler: .setFromRotationMatrix() encountered an unknown order: ' + order );
- }
- this._order = order;
- if ( update !== false ) this._onChangeCallback();
- return this;
- }
- setFromQuaternion( q, order, update ) {
- _matrix.makeRotationFromQuaternion( q );
- return this.setFromRotationMatrix( _matrix, order, update );
- }
- setFromVector3( v, order ) {
- return this.set( v.x, v.y, v.z, order || this._order );
- }
- reorder( newOrder ) {
- // WARNING: this discards revolution information -bhouston
- _quaternion$1.setFromEuler( this );
- return this.setFromQuaternion( _quaternion$1, newOrder );
- }
- equals( euler ) {
- return ( euler._x === this._x ) && ( euler._y === this._y ) && ( euler._z === this._z ) && ( euler._order === this._order );
- }
- fromArray( array ) {
- this._x = array[ 0 ];
- this._y = array[ 1 ];
- this._z = array[ 2 ];
- if ( array[ 3 ] !== undefined ) this._order = array[ 3 ];
- this._onChangeCallback();
- return this;
- }
- toArray( array = [], offset = 0 ) {
- array[ offset ] = this._x;
- array[ offset + 1 ] = this._y;
- array[ offset + 2 ] = this._z;
- array[ offset + 3 ] = this._order;
- return array;
- }
- toVector3( optionalResult ) {
- if ( optionalResult ) {
- return optionalResult.set( this._x, this._y, this._z );
- } else {
- return new Vector3( this._x, this._y, this._z );
- }
- }
- _onChange( callback ) {
- this._onChangeCallback = callback;
- return this;
- }
- _onChangeCallback() {}
- }
- Euler.DefaultOrder = 'XYZ';
- Euler.RotationOrders = [ 'XYZ', 'YZX', 'ZXY', 'XZY', 'YXZ', 'ZYX' ];
- const _matrix = /*@__PURE__*/ new Matrix4();
- const _quaternion$1 = /*@__PURE__*/ new Quaternion();
- class Layers {
- constructor() {
- this.mask = 1 | 0;
- }
- set( channel ) {
- this.mask = 1 << channel | 0;
- }
- enable( channel ) {
- this.mask |= 1 << channel | 0;
- }
- enableAll() {
- this.mask = 0xffffffff | 0;
- }
- toggle( channel ) {
- this.mask ^= 1 << channel | 0;
- }
- disable( channel ) {
- this.mask &= ~ ( 1 << channel | 0 );
- }
- disableAll() {
- this.mask = 0;
- }
- test( layers ) {
- return ( this.mask & layers.mask ) !== 0;
- }
- }
- let _object3DId = 0;
- const _v1$2 = new Vector3();
- const _q1 = new Quaternion();
- const _m1$1 = new Matrix4();
- const _target = new Vector3();
- const _position = new Vector3();
- const _scale = new Vector3();
- const _quaternion$2 = new Quaternion();
- const _xAxis = new Vector3( 1, 0, 0 );
- const _yAxis = new Vector3( 0, 1, 0 );
- const _zAxis = new Vector3( 0, 0, 1 );
- const _addedEvent = { type: 'added' };
- const _removedEvent = { type: 'removed' };
- function Object3D() {
- Object.defineProperty( this, 'id', { value: _object3DId ++ } );
- this.uuid = MathUtils.generateUUID();
- this.name = '';
- this.type = 'Object3D';
- this.parent = null;
- this.children = [];
- this.up = Object3D.DefaultUp.clone();
- const position = new Vector3();
- const rotation = new Euler();
- const quaternion = new Quaternion();
- const scale = new Vector3( 1, 1, 1 );
- function onRotationChange() {
- quaternion.setFromEuler( rotation, false );
- }
- function onQuaternionChange() {
- rotation.setFromQuaternion( quaternion, undefined, false );
- }
- rotation._onChange( onRotationChange );
- quaternion._onChange( onQuaternionChange );
- Object.defineProperties( this, {
- position: {
- configurable: true,
- enumerable: true,
- value: position
- },
- rotation: {
- configurable: true,
- enumerable: true,
- value: rotation
- },
- quaternion: {
- configurable: true,
- enumerable: true,
- value: quaternion
- },
- scale: {
- configurable: true,
- enumerable: true,
- value: scale
- },
- modelViewMatrix: {
- value: new Matrix4()
- },
- normalMatrix: {
- value: new Matrix3()
- }
- } );
- this.matrix = new Matrix4();
- this.matrixWorld = new Matrix4();
- this.matrixAutoUpdate = Object3D.DefaultMatrixAutoUpdate;
- this.matrixWorldNeedsUpdate = false;
- this.layers = new Layers();
- this.visible = true;
- this.castShadow = false;
- this.receiveShadow = false;
- this.frustumCulled = true;
- this.renderOrder = 0;
- this.animations = [];
- this.userData = {};
- }
- Object3D.DefaultUp = new Vector3( 0, 1, 0 );
- Object3D.DefaultMatrixAutoUpdate = true;
- Object3D.prototype = Object.assign( Object.create( EventDispatcher.prototype ), {
- constructor: Object3D,
- isObject3D: true,
- onBeforeRender: function () {},
- onAfterRender: function () {},
- applyMatrix4: function ( matrix ) {
- if ( this.matrixAutoUpdate ) this.updateMatrix();
- this.matrix.premultiply( matrix );
- this.matrix.decompose( this.position, this.quaternion, this.scale );
- },
- applyQuaternion: function ( q ) {
- this.quaternion.premultiply( q );
- return this;
- },
- setRotationFromAxisAngle: function ( axis, angle ) {
- // assumes axis is normalized
- this.quaternion.setFromAxisAngle( axis, angle );
- },
- setRotationFromEuler: function ( euler ) {
- this.quaternion.setFromEuler( euler, true );
- },
- setRotationFromMatrix: function ( m ) {
- // assumes the upper 3x3 of m is a pure rotation matrix (i.e, unscaled)
- this.quaternion.setFromRotationMatrix( m );
- },
- setRotationFromQuaternion: function ( q ) {
- // assumes q is normalized
- this.quaternion.copy( q );
- },
- rotateOnAxis: function ( axis, angle ) {
- // rotate object on axis in object space
- // axis is assumed to be normalized
- _q1.setFromAxisAngle( axis, angle );
- this.quaternion.multiply( _q1 );
- return this;
- },
- rotateOnWorldAxis: function ( axis, angle ) {
- // rotate object on axis in world space
- // axis is assumed to be normalized
- // method assumes no rotated parent
- _q1.setFromAxisAngle( axis, angle );
- this.quaternion.premultiply( _q1 );
- return this;
- },
- rotateX: function ( angle ) {
- return this.rotateOnAxis( _xAxis, angle );
- },
- rotateY: function ( angle ) {
- return this.rotateOnAxis( _yAxis, angle );
- },
- rotateZ: function ( angle ) {
- return this.rotateOnAxis( _zAxis, angle );
- },
- translateOnAxis: function ( axis, distance ) {
- // translate object by distance along axis in object space
- // axis is assumed to be normalized
- _v1$2.copy( axis ).applyQuaternion( this.quaternion );
- this.position.add( _v1$2.multiplyScalar( distance ) );
- return this;
- },
- translateX: function ( distance ) {
- return this.translateOnAxis( _xAxis, distance );
- },
- translateY: function ( distance ) {
- return this.translateOnAxis( _yAxis, distance );
- },
- translateZ: function ( distance ) {
- return this.translateOnAxis( _zAxis, distance );
- },
- localToWorld: function ( vector ) {
- return vector.applyMatrix4( this.matrixWorld );
- },
- worldToLocal: function ( vector ) {
- return vector.applyMatrix4( _m1$1.copy( this.matrixWorld ).invert() );
- },
- lookAt: function ( x, y, z ) {
- // This method does not support objects having non-uniformly-scaled parent(s)
- if ( x.isVector3 ) {
- _target.copy( x );
- } else {
- _target.set( x, y, z );
- }
- const parent = this.parent;
- this.updateWorldMatrix( true, false );
- _position.setFromMatrixPosition( this.matrixWorld );
- if ( this.isCamera || this.isLight ) {
- _m1$1.lookAt( _position, _target, this.up );
- } else {
- _m1$1.lookAt( _target, _position, this.up );
- }
- this.quaternion.setFromRotationMatrix( _m1$1 );
- if ( parent ) {
- _m1$1.extractRotation( parent.matrixWorld );
- _q1.setFromRotationMatrix( _m1$1 );
- this.quaternion.premultiply( _q1.invert() );
- }
- },
- add: function ( object ) {
- if ( arguments.length > 1 ) {
- for ( let i = 0; i < arguments.length; i ++ ) {
- this.add( arguments[ i ] );
- }
- return this;
- }
- if ( object === this ) {
- console.error( 'THREE.Object3D.add: object can\'t be added as a child of itself.', object );
- return this;
- }
- if ( object && object.isObject3D ) {
- if ( object.parent !== null ) {
- object.parent.remove( object );
- }
- object.parent = this;
- this.children.push( object );
- object.dispatchEvent( _addedEvent );
- } else {
- console.error( 'THREE.Object3D.add: object not an instance of THREE.Object3D.', object );
- }
- return this;
- },
- remove: function ( object ) {
- if ( arguments.length > 1 ) {
- for ( let i = 0; i < arguments.length; i ++ ) {
- this.remove( arguments[ i ] );
- }
- return this;
- }
- const index = this.children.indexOf( object );
- if ( index !== - 1 ) {
- object.parent = null;
- this.children.splice( index, 1 );
- object.dispatchEvent( _removedEvent );
- }
- return this;
- },
- clear: function () {
- for ( let i = 0; i < this.children.length; i ++ ) {
- const object = this.children[ i ];
- object.parent = null;
- object.dispatchEvent( _removedEvent );
- }
- this.children.length = 0;
- return this;
- },
- attach: function ( object ) {
- // adds object as a child of this, while maintaining the object's world transform
- this.updateWorldMatrix( true, false );
- _m1$1.copy( this.matrixWorld ).invert();
- if ( object.parent !== null ) {
- object.parent.updateWorldMatrix( true, false );
- _m1$1.multiply( object.parent.matrixWorld );
- }
- object.applyMatrix4( _m1$1 );
- object.updateWorldMatrix( false, false );
- this.add( object );
- return this;
- },
- getObjectById: function ( id ) {
- return this.getObjectByProperty( 'id', id );
- },
- getObjectByName: function ( name ) {
- return this.getObjectByProperty( 'name', name );
- },
- getObjectByProperty: function ( name, value ) {
- if ( this[ name ] === value ) return this;
- for ( let i = 0, l = this.children.length; i < l; i ++ ) {
- const child = this.children[ i ];
- const object = child.getObjectByProperty( name, value );
- if ( object !== undefined ) {
- return object;
- }
- }
- return undefined;
- },
- getWorldPosition: function ( target ) {
- if ( target === undefined ) {
- console.warn( 'THREE.Object3D: .getWorldPosition() target is now required' );
- target = new Vector3();
- }
- this.updateWorldMatrix( true, false );
- return target.setFromMatrixPosition( this.matrixWorld );
- },
- getWorldQuaternion: function ( target ) {
- if ( target === undefined ) {
- console.warn( 'THREE.Object3D: .getWorldQuaternion() target is now required' );
- target = new Quaternion();
- }
- this.updateWorldMatrix( true, false );
- this.matrixWorld.decompose( _position, target, _scale );
- return target;
- },
- getWorldScale: function ( target ) {
- if ( target === undefined ) {
- console.warn( 'THREE.Object3D: .getWorldScale() target is now required' );
- target = new Vector3();
- }
- this.updateWorldMatrix( true, false );
- this.matrixWorld.decompose( _position, _quaternion$2, target );
- return target;
- },
- getWorldDirection: function ( target ) {
- if ( target === undefined ) {
- console.warn( 'THREE.Object3D: .getWorldDirection() target is now required' );
- target = new Vector3();
- }
- this.updateWorldMatrix( true, false );
- const e = this.matrixWorld.elements;
- return target.set( e[ 8 ], e[ 9 ], e[ 10 ] ).normalize();
- },
- raycast: function () {},
- traverse: function ( callback ) {
- callback( this );
- const children = this.children;
- for ( let i = 0, l = children.length; i < l; i ++ ) {
- children[ i ].traverse( callback );
- }
- },
- traverseVisible: function ( callback ) {
- if ( this.visible === false ) return;
- callback( this );
- const children = this.children;
- for ( let i = 0, l = children.length; i < l; i ++ ) {
- children[ i ].traverseVisible( callback );
- }
- },
- traverseAncestors: function ( callback ) {
- const parent = this.parent;
- if ( parent !== null ) {
- callback( parent );
- parent.traverseAncestors( callback );
- }
- },
- updateMatrix: function () {
- this.matrix.compose( this.position, this.quaternion, this.scale );
- this.matrixWorldNeedsUpdate = true;
- },
- updateMatrixWorld: function ( force ) {
- if ( this.matrixAutoUpdate ) this.updateMatrix();
- if ( this.matrixWorldNeedsUpdate || force ) {
- if ( this.parent === null ) {
- this.matrixWorld.copy( this.matrix );
- } else {
- this.matrixWorld.multiplyMatrices( this.parent.matrixWorld, this.matrix );
- }
- this.matrixWorldNeedsUpdate = false;
- force = true;
- }
- // update children
- const children = this.children;
- for ( let i = 0, l = children.length; i < l; i ++ ) {
- children[ i ].updateMatrixWorld( force );
- }
- },
- updateWorldMatrix: function ( updateParents, updateChildren ) {
- const parent = this.parent;
- if ( updateParents === true && parent !== null ) {
- parent.updateWorldMatrix( true, false );
- }
- if ( this.matrixAutoUpdate ) this.updateMatrix();
- if ( this.parent === null ) {
- this.matrixWorld.copy( this.matrix );
- } else {
- this.matrixWorld.multiplyMatrices( this.parent.matrixWorld, this.matrix );
- }
- // update children
- if ( updateChildren === true ) {
- const children = this.children;
- for ( let i = 0, l = children.length; i < l; i ++ ) {
- children[ i ].updateWorldMatrix( false, true );
- }
- }
- },
- toJSON: function ( meta ) {
- // meta is a string when called from JSON.stringify
- const isRootObject = ( meta === undefined || typeof meta === 'string' );
- const output = {};
- // meta is a hash used to collect geometries, materials.
- // not providing it implies that this is the root object
- // being serialized.
- if ( isRootObject ) {
- // initialize meta obj
- meta = {
- geometries: {},
- materials: {},
- textures: {},
- images: {},
- shapes: {},
- skeletons: {},
- animations: {}
- };
- output.metadata = {
- version: 4.5,
- type: 'Object',
- generator: 'Object3D.toJSON'
- };
- }
- // standard Object3D serialization
- const object = {};
- object.uuid = this.uuid;
- object.type = this.type;
- if ( this.name !== '' ) object.name = this.name;
- if ( this.castShadow === true ) object.castShadow = true;
- if ( this.receiveShadow === true ) object.receiveShadow = true;
- if ( this.visible === false ) object.visible = false;
- if ( this.frustumCulled === false ) object.frustumCulled = false;
- if ( this.renderOrder !== 0 ) object.renderOrder = this.renderOrder;
- if ( JSON.stringify( this.userData ) !== '{}' ) object.userData = this.userData;
- object.layers = this.layers.mask;
- object.matrix = this.matrix.toArray();
- if ( this.matrixAutoUpdate === false ) object.matrixAutoUpdate = false;
- // object specific properties
- if ( this.isInstancedMesh ) {
- object.type = 'InstancedMesh';
- object.count = this.count;
- object.instanceMatrix = this.instanceMatrix.toJSON();
- }
- //
- function serialize( library, element ) {
- if ( library[ element.uuid ] === undefined ) {
- library[ element.uuid ] = element.toJSON( meta );
- }
- return element.uuid;
- }
- if ( this.isMesh || this.isLine || this.isPoints ) {
- object.geometry = serialize( meta.geometries, this.geometry );
- const parameters = this.geometry.parameters;
- if ( parameters !== undefined && parameters.shapes !== undefined ) {
- const shapes = parameters.shapes;
- if ( Array.isArray( shapes ) ) {
- for ( let i = 0, l = shapes.length; i < l; i ++ ) {
- const shape = shapes[ i ];
- serialize( meta.shapes, shape );
- }
- } else {
- serialize( meta.shapes, shapes );
- }
- }
- }
- if ( this.isSkinnedMesh ) {
- object.bindMode = this.bindMode;
- object.bindMatrix = this.bindMatrix.toArray();
- if ( this.skeleton !== undefined ) {
- serialize( meta.skeletons, this.skeleton );
- object.skeleton = this.skeleton.uuid;
- }
- }
- if ( this.material !== undefined ) {
- if ( Array.isArray( this.material ) ) {
- const uuids = [];
- for ( let i = 0, l = this.material.length; i < l; i ++ ) {
- uuids.push( serialize( meta.materials, this.material[ i ] ) );
- }
- object.material = uuids;
- } else {
- object.material = serialize( meta.materials, this.material );
- }
- }
- //
- if ( this.children.length > 0 ) {
- object.children = [];
- for ( let i = 0; i < this.children.length; i ++ ) {
- object.children.push( this.children[ i ].toJSON( meta ).object );
- }
- }
- //
- if ( this.animations.length > 0 ) {
- object.animations = [];
- for ( let i = 0; i < this.animations.length; i ++ ) {
- const animation = this.animations[ i ];
- object.animations.push( serialize( meta.animations, animation ) );
- }
- }
- if ( isRootObject ) {
- const geometries = extractFromCache( meta.geometries );
- const materials = extractFromCache( meta.materials );
- const textures = extractFromCache( meta.textures );
- const images = extractFromCache( meta.images );
- const shapes = extractFromCache( meta.shapes );
- const skeletons = extractFromCache( meta.skeletons );
- const animations = extractFromCache( meta.animations );
- if ( geometries.length > 0 ) output.geometries = geometries;
- if ( materials.length > 0 ) output.materials = materials;
- if ( textures.length > 0 ) output.textures = textures;
- if ( images.length > 0 ) output.images = images;
- if ( shapes.length > 0 ) output.shapes = shapes;
- if ( skeletons.length > 0 ) output.skeletons = skeletons;
- if ( animations.length > 0 ) output.animations = animations;
- }
- output.object = object;
- return output;
- // extract data from the cache hash
- // remove metadata on each item
- // and return as array
- function extractFromCache( cache ) {
- const values = [];
- for ( const key in cache ) {
- const data = cache[ key ];
- delete data.metadata;
- values.push( data );
- }
- return values;
- }
- },
- clone: function ( recursive ) {
- return new this.constructor().copy( this, recursive );
- },
- copy: function ( source, recursive = true ) {
- this.name = source.name;
- this.up.copy( source.up );
- this.position.copy( source.position );
- this.rotation.order = source.rotation.order;
- this.quaternion.copy( source.quaternion );
- this.scale.copy( source.scale );
- this.matrix.copy( source.matrix );
- this.matrixWorld.copy( source.matrixWorld );
- this.matrixAutoUpdate = source.matrixAutoUpdate;
- this.matrixWorldNeedsUpdate = source.matrixWorldNeedsUpdate;
- this.layers.mask = source.layers.mask;
- this.visible = source.visible;
- this.castShadow = source.castShadow;
- this.receiveShadow = source.receiveShadow;
- this.frustumCulled = source.frustumCulled;
- this.renderOrder = source.renderOrder;
- this.userData = JSON.parse( JSON.stringify( source.userData ) );
- if ( recursive === true ) {
- for ( let i = 0; i < source.children.length; i ++ ) {
- const child = source.children[ i ];
- this.add( child.clone() );
- }
- }
- return this;
- }
- } );
- const _vector1 = /*@__PURE__*/ new Vector3();
- const _vector2 = /*@__PURE__*/ new Vector3();
- const _normalMatrix = /*@__PURE__*/ new Matrix3();
- class Plane {
- constructor( normal, constant ) {
- Object.defineProperty( this, 'isPlane', { value: true } );
- // normal is assumed to be normalized
- this.normal = ( normal !== undefined ) ? normal : new Vector3( 1, 0, 0 );
- this.constant = ( constant !== undefined ) ? constant : 0;
- }
- set( normal, constant ) {
- this.normal.copy( normal );
- this.constant = constant;
- return this;
- }
- setComponents( x, y, z, w ) {
- this.normal.set( x, y, z );
- this.constant = w;
- return this;
- }
- setFromNormalAndCoplanarPoint( normal, point ) {
- this.normal.copy( normal );
- this.constant = - point.dot( this.normal );
- return this;
- }
- setFromCoplanarPoints( a, b, c ) {
- const normal = _vector1.subVectors( c, b ).cross( _vector2.subVectors( a, b ) ).normalize();
- // Q: should an error be thrown if normal is zero (e.g. degenerate plane)?
- this.setFromNormalAndCoplanarPoint( normal, a );
- return this;
- }
- clone() {
- return new this.constructor().copy( this );
- }
- copy( plane ) {
- this.normal.copy( plane.normal );
- this.constant = plane.constant;
- return this;
- }
- normalize() {
- // Note: will lead to a divide by zero if the plane is invalid.
- const inverseNormalLength = 1.0 / this.normal.length();
- this.normal.multiplyScalar( inverseNormalLength );
- this.constant *= inverseNormalLength;
- return this;
- }
- negate() {
- this.constant *= - 1;
- this.normal.negate();
- return this;
- }
- distanceToPoint( point ) {
- return this.normal.dot( point ) + this.constant;
- }
- distanceToSphere( sphere ) {
- return this.distanceToPoint( sphere.center ) - sphere.radius;
- }
- projectPoint( point, target ) {
- if ( target === undefined ) {
- console.warn( 'THREE.Plane: .projectPoint() target is now required' );
- target = new Vector3();
- }
- return target.copy( this.normal ).multiplyScalar( - this.distanceToPoint( point ) ).add( point );
- }
- intersectLine( line, target ) {
- if ( target === undefined ) {
- console.warn( 'THREE.Plane: .intersectLine() target is now required' );
- target = new Vector3();
- }
- const direction = line.delta( _vector1 );
- const denominator = this.normal.dot( direction );
- if ( denominator === 0 ) {
- // line is coplanar, return origin
- if ( this.distanceToPoint( line.start ) === 0 ) {
- return target.copy( line.start );
- }
- // Unsure if this is the correct method to handle this case.
- return undefined;
- }
- const t = - ( line.start.dot( this.normal ) + this.constant ) / denominator;
- if ( t < 0 || t > 1 ) {
- return undefined;
- }
- return target.copy( direction ).multiplyScalar( t ).add( line.start );
- }
- intersectsLine( line ) {
- // Note: this tests if a line intersects the plane, not whether it (or its end-points) are coplanar with it.
- const startSign = this.distanceToPoint( line.start );
- const endSign = this.distanceToPoint( line.end );
- return ( startSign < 0 && endSign > 0 ) || ( endSign < 0 && startSign > 0 );
- }
- intersectsBox( box ) {
- return box.intersectsPlane( this );
- }
- intersectsSphere( sphere ) {
- return sphere.intersectsPlane( this );
- }
- coplanarPoint( target ) {
- if ( target === undefined ) {
- console.warn( 'THREE.Plane: .coplanarPoint() target is now required' );
- target = new Vector3();
- }
- return target.copy( this.normal ).multiplyScalar( - this.constant );
- }
- applyMatrix4( matrix, optionalNormalMatrix ) {
- const normalMatrix = optionalNormalMatrix || _normalMatrix.getNormalMatrix( matrix );
- const referencePoint = this.coplanarPoint( _vector1 ).applyMatrix4( matrix );
- const normal = this.normal.applyMatrix3( normalMatrix ).normalize();
- this.constant = - referencePoint.dot( normal );
- return this;
- }
- translate( offset ) {
- this.constant -= offset.dot( this.normal );
- return this;
- }
- equals( plane ) {
- return plane.normal.equals( this.normal ) && ( plane.constant === this.constant );
- }
- }
- const _v0$1 = /*@__PURE__*/ new Vector3();
- const _v1$3 = /*@__PURE__*/ new Vector3();
- const _v2$1 = /*@__PURE__*/ new Vector3();
- const _v3 = /*@__PURE__*/ new Vector3();
- const _vab = /*@__PURE__*/ new Vector3();
- const _vac = /*@__PURE__*/ new Vector3();
- const _vbc = /*@__PURE__*/ new Vector3();
- const _vap = /*@__PURE__*/ new Vector3();
- const _vbp = /*@__PURE__*/ new Vector3();
- const _vcp = /*@__PURE__*/ new Vector3();
- class Triangle {
- constructor( a, b, c ) {
- this.a = ( a !== undefined ) ? a : new Vector3();
- this.b = ( b !== undefined ) ? b : new Vector3();
- this.c = ( c !== undefined ) ? c : new Vector3();
- }
- static getNormal( a, b, c, target ) {
- if ( target === undefined ) {
- console.warn( 'THREE.Triangle: .getNormal() target is now required' );
- target = new Vector3();
- }
- target.subVectors( c, b );
- _v0$1.subVectors( a, b );
- target.cross( _v0$1 );
- const targetLengthSq = target.lengthSq();
- if ( targetLengthSq > 0 ) {
- return target.multiplyScalar( 1 / Math.sqrt( targetLengthSq ) );
- }
- return target.set( 0, 0, 0 );
- }
- // static/instance method to calculate barycentric coordinates
- // based on: http://www.blackpawn.com/texts/pointinpoly/default.html
- static getBarycoord( point, a, b, c, target ) {
- _v0$1.subVectors( c, a );
- _v1$3.subVectors( b, a );
- _v2$1.subVectors( point, a );
- const dot00 = _v0$1.dot( _v0$1 );
- const dot01 = _v0$1.dot( _v1$3 );
- const dot02 = _v0$1.dot( _v2$1 );
- const dot11 = _v1$3.dot( _v1$3 );
- const dot12 = _v1$3.dot( _v2$1 );
- const denom = ( dot00 * dot11 - dot01 * dot01 );
- if ( target === undefined ) {
- console.warn( 'THREE.Triangle: .getBarycoord() target is now required' );
- target = new Vector3();
- }
- // collinear or singular triangle
- if ( denom === 0 ) {
- // arbitrary location outside of triangle?
- // not sure if this is the best idea, maybe should be returning undefined
- return target.set( - 2, - 1, - 1 );
- }
- const invDenom = 1 / denom;
- const u = ( dot11 * dot02 - dot01 * dot12 ) * invDenom;
- const v = ( dot00 * dot12 - dot01 * dot02 ) * invDenom;
- // barycentric coordinates must always sum to 1
- return target.set( 1 - u - v, v, u );
- }
- static containsPoint( point, a, b, c ) {
- this.getBarycoord( point, a, b, c, _v3 );
- return ( _v3.x >= 0 ) && ( _v3.y >= 0 ) && ( ( _v3.x + _v3.y ) <= 1 );
- }
- static getUV( point, p1, p2, p3, uv1, uv2, uv3, target ) {
- this.getBarycoord( point, p1, p2, p3, _v3 );
- target.set( 0, 0 );
- target.addScaledVector( uv1, _v3.x );
- target.addScaledVector( uv2, _v3.y );
- target.addScaledVector( uv3, _v3.z );
- return target;
- }
- static isFrontFacing( a, b, c, direction ) {
- _v0$1.subVectors( c, b );
- _v1$3.subVectors( a, b );
- // strictly front facing
- return ( _v0$1.cross( _v1$3 ).dot( direction ) < 0 ) ? true : false;
- }
- set( a, b, c ) {
- this.a.copy( a );
- this.b.copy( b );
- this.c.copy( c );
- return this;
- }
- setFromPointsAndIndices( points, i0, i1, i2 ) {
- this.a.copy( points[ i0 ] );
- this.b.copy( points[ i1 ] );
- this.c.copy( points[ i2 ] );
- return this;
- }
- clone() {
- return new this.constructor().copy( this );
- }
- copy( triangle ) {
- this.a.copy( triangle.a );
- this.b.copy( triangle.b );
- this.c.copy( triangle.c );
- return this;
- }
- getArea() {
- _v0$1.subVectors( this.c, this.b );
- _v1$3.subVectors( this.a, this.b );
- return _v0$1.cross( _v1$3 ).length() * 0.5;
- }
- getMidpoint( target ) {
- if ( target === undefined ) {
- console.warn( 'THREE.Triangle: .getMidpoint() target is now required' );
- target = new Vector3();
- }
- return target.addVectors( this.a, this.b ).add( this.c ).multiplyScalar( 1 / 3 );
- }
- getNormal( target ) {
- return Triangle.getNormal( this.a, this.b, this.c, target );
- }
- getPlane( target ) {
- if ( target === undefined ) {
- console.warn( 'THREE.Triangle: .getPlane() target is now required' );
- target = new Plane();
- }
- return target.setFromCoplanarPoints( this.a, this.b, this.c );
- }
- getBarycoord( point, target ) {
- return Triangle.getBarycoord( point, this.a, this.b, this.c, target );
- }
- getUV( point, uv1, uv2, uv3, target ) {
- return Triangle.getUV( point, this.a, this.b, this.c, uv1, uv2, uv3, target );
- }
- containsPoint( point ) {
- return Triangle.containsPoint( point, this.a, this.b, this.c );
- }
- isFrontFacing( direction ) {
- return Triangle.isFrontFacing( this.a, this.b, this.c, direction );
- }
- intersectsBox( box ) {
- return box.intersectsTriangle( this );
- }
- closestPointToPoint( p, target ) {
- if ( target === undefined ) {
- console.warn( 'THREE.Triangle: .closestPointToPoint() target is now required' );
- target = new Vector3();
- }
- const a = this.a, b = this.b, c = this.c;
- let v, w;
- // algorithm thanks to Real-Time Collision Detection by Christer Ericson,
- // published by Morgan Kaufmann Publishers, (c) 2005 Elsevier Inc.,
- // under the accompanying license; see chapter 5.1.5 for detailed explanation.
- // basically, we're distinguishing which of the voronoi regions of the triangle
- // the point lies in with the minimum amount of redundant computation.
- _vab.subVectors( b, a );
- _vac.subVectors( c, a );
- _vap.subVectors( p, a );
- const d1 = _vab.dot( _vap );
- const d2 = _vac.dot( _vap );
- if ( d1 <= 0 && d2 <= 0 ) {
- // vertex region of A; barycentric coords (1, 0, 0)
- return target.copy( a );
- }
- _vbp.subVectors( p, b );
- const d3 = _vab.dot( _vbp );
- const d4 = _vac.dot( _vbp );
- if ( d3 >= 0 && d4 <= d3 ) {
- // vertex region of B; barycentric coords (0, 1, 0)
- return target.copy( b );
- }
- const vc = d1 * d4 - d3 * d2;
- if ( vc <= 0 && d1 >= 0 && d3 <= 0 ) {
- v = d1 / ( d1 - d3 );
- // edge region of AB; barycentric coords (1-v, v, 0)
- return target.copy( a ).addScaledVector( _vab, v );
- }
- _vcp.subVectors( p, c );
- const d5 = _vab.dot( _vcp );
- const d6 = _vac.dot( _vcp );
- if ( d6 >= 0 && d5 <= d6 ) {
- // vertex region of C; barycentric coords (0, 0, 1)
- return target.copy( c );
- }
- const vb = d5 * d2 - d1 * d6;
- if ( vb <= 0 && d2 >= 0 && d6 <= 0 ) {
- w = d2 / ( d2 - d6 );
- // edge region of AC; barycentric coords (1-w, 0, w)
- return target.copy( a ).addScaledVector( _vac, w );
- }
- const va = d3 * d6 - d5 * d4;
- if ( va <= 0 && ( d4 - d3 ) >= 0 && ( d5 - d6 ) >= 0 ) {
- _vbc.subVectors( c, b );
- w = ( d4 - d3 ) / ( ( d4 - d3 ) + ( d5 - d6 ) );
- // edge region of BC; barycentric coords (0, 1-w, w)
- return target.copy( b ).addScaledVector( _vbc, w ); // edge region of BC
- }
- // face region
- const denom = 1 / ( va + vb + vc );
- // u = va * denom
- v = vb * denom;
- w = vc * denom;
- return target.copy( a ).addScaledVector( _vab, v ).addScaledVector( _vac, w );
- }
- equals( triangle ) {
- return triangle.a.equals( this.a ) && triangle.b.equals( this.b ) && triangle.c.equals( this.c );
- }
- }
- const _colorKeywords = { 'aliceblue': 0xF0F8FF, 'antiquewhite': 0xFAEBD7, 'aqua': 0x00FFFF, 'aquamarine': 0x7FFFD4, 'azure': 0xF0FFFF,
- 'beige': 0xF5F5DC, 'bisque': 0xFFE4C4, 'black': 0x000000, 'blanchedalmond': 0xFFEBCD, 'blue': 0x0000FF, 'blueviolet': 0x8A2BE2,
- 'brown': 0xA52A2A, 'burlywood': 0xDEB887, 'cadetblue': 0x5F9EA0, 'chartreuse': 0x7FFF00, 'chocolate': 0xD2691E, 'coral': 0xFF7F50,
- 'cornflowerblue': 0x6495ED, 'cornsilk': 0xFFF8DC, 'crimson': 0xDC143C, 'cyan': 0x00FFFF, 'darkblue': 0x00008B, 'darkcyan': 0x008B8B,
- 'darkgoldenrod': 0xB8860B, 'darkgray': 0xA9A9A9, 'darkgreen': 0x006400, 'darkgrey': 0xA9A9A9, 'darkkhaki': 0xBDB76B, 'darkmagenta': 0x8B008B,
- 'darkolivegreen': 0x556B2F, 'darkorange': 0xFF8C00, 'darkorchid': 0x9932CC, 'darkred': 0x8B0000, 'darksalmon': 0xE9967A, 'darkseagreen': 0x8FBC8F,
- 'darkslateblue': 0x483D8B, 'darkslategray': 0x2F4F4F, 'darkslategrey': 0x2F4F4F, 'darkturquoise': 0x00CED1, 'darkviolet': 0x9400D3,
- 'deeppink': 0xFF1493, 'deepskyblue': 0x00BFFF, 'dimgray': 0x696969, 'dimgrey': 0x696969, 'dodgerblue': 0x1E90FF, 'firebrick': 0xB22222,
- 'floralwhite': 0xFFFAF0, 'forestgreen': 0x228B22, 'fuchsia': 0xFF00FF, 'gainsboro': 0xDCDCDC, 'ghostwhite': 0xF8F8FF, 'gold': 0xFFD700,
- 'goldenrod': 0xDAA520, 'gray': 0x808080, 'green': 0x008000, 'greenyellow': 0xADFF2F, 'grey': 0x808080, 'honeydew': 0xF0FFF0, 'hotpink': 0xFF69B4,
- 'indianred': 0xCD5C5C, 'indigo': 0x4B0082, 'ivory': 0xFFFFF0, 'khaki': 0xF0E68C, 'lavender': 0xE6E6FA, 'lavenderblush': 0xFFF0F5, 'lawngreen': 0x7CFC00,
- 'lemonchiffon': 0xFFFACD, 'lightblue': 0xADD8E6, 'lightcoral': 0xF08080, 'lightcyan': 0xE0FFFF, 'lightgoldenrodyellow': 0xFAFAD2, 'lightgray': 0xD3D3D3,
- 'lightgreen': 0x90EE90, 'lightgrey': 0xD3D3D3, 'lightpink': 0xFFB6C1, 'lightsalmon': 0xFFA07A, 'lightseagreen': 0x20B2AA, 'lightskyblue': 0x87CEFA,
- 'lightslategray': 0x778899, 'lightslategrey': 0x778899, 'lightsteelblue': 0xB0C4DE, 'lightyellow': 0xFFFFE0, 'lime': 0x00FF00, 'limegreen': 0x32CD32,
- 'linen': 0xFAF0E6, 'magenta': 0xFF00FF, 'maroon': 0x800000, 'mediumaquamarine': 0x66CDAA, 'mediumblue': 0x0000CD, 'mediumorchid': 0xBA55D3,
- 'mediumpurple': 0x9370DB, 'mediumseagreen': 0x3CB371, 'mediumslateblue': 0x7B68EE, 'mediumspringgreen': 0x00FA9A, 'mediumturquoise': 0x48D1CC,
- 'mediumvioletred': 0xC71585, 'midnightblue': 0x191970, 'mintcream': 0xF5FFFA, 'mistyrose': 0xFFE4E1, 'moccasin': 0xFFE4B5, 'navajowhite': 0xFFDEAD,
- 'navy': 0x000080, 'oldlace': 0xFDF5E6, 'olive': 0x808000, 'olivedrab': 0x6B8E23, 'orange': 0xFFA500, 'orangered': 0xFF4500, 'orchid': 0xDA70D6,
- 'palegoldenrod': 0xEEE8AA, 'palegreen': 0x98FB98, 'paleturquoise': 0xAFEEEE, 'palevioletred': 0xDB7093, 'papayawhip': 0xFFEFD5, 'peachpuff': 0xFFDAB9,
- 'peru': 0xCD853F, 'pink': 0xFFC0CB, 'plum': 0xDDA0DD, 'powderblue': 0xB0E0E6, 'purple': 0x800080, 'rebeccapurple': 0x663399, 'red': 0xFF0000, 'rosybrown': 0xBC8F8F,
- 'royalblue': 0x4169E1, 'saddlebrown': 0x8B4513, 'salmon': 0xFA8072, 'sandybrown': 0xF4A460, 'seagreen': 0x2E8B57, 'seashell': 0xFFF5EE,
- 'sienna': 0xA0522D, 'silver': 0xC0C0C0, 'skyblue': 0x87CEEB, 'slateblue': 0x6A5ACD, 'slategray': 0x708090, 'slategrey': 0x708090, 'snow': 0xFFFAFA,
- 'springgreen': 0x00FF7F, 'steelblue': 0x4682B4, 'tan': 0xD2B48C, 'teal': 0x008080, 'thistle': 0xD8BFD8, 'tomato': 0xFF6347, 'turquoise': 0x40E0D0,
- 'violet': 0xEE82EE, 'wheat': 0xF5DEB3, 'white': 0xFFFFFF, 'whitesmoke': 0xF5F5F5, 'yellow': 0xFFFF00, 'yellowgreen': 0x9ACD32 };
- const _hslA = { h: 0, s: 0, l: 0 };
- const _hslB = { h: 0, s: 0, l: 0 };
- function hue2rgb( p, q, t ) {
- if ( t < 0 ) t += 1;
- if ( t > 1 ) t -= 1;
- if ( t < 1 / 6 ) return p + ( q - p ) * 6 * t;
- if ( t < 1 / 2 ) return q;
- if ( t < 2 / 3 ) return p + ( q - p ) * 6 * ( 2 / 3 - t );
- return p;
- }
- function SRGBToLinear( c ) {
- return ( c < 0.04045 ) ? c * 0.0773993808 : Math.pow( c * 0.9478672986 + 0.0521327014, 2.4 );
- }
- function LinearToSRGB( c ) {
- return ( c < 0.0031308 ) ? c * 12.92 : 1.055 * ( Math.pow( c, 0.41666 ) ) - 0.055;
- }
- class Color {
- constructor( r, g, b ) {
- Object.defineProperty( this, 'isColor', { value: true } );
- if ( g === undefined && b === undefined ) {
- // r is THREE.Color, hex or string
- return this.set( r );
- }
- return this.setRGB( r, g, b );
- }
- set( value ) {
- if ( value && value.isColor ) {
- this.copy( value );
- } else if ( typeof value === 'number' ) {
- this.setHex( value );
- } else if ( typeof value === 'string' ) {
- this.setStyle( value );
- }
- return this;
- }
- setScalar( scalar ) {
- this.r = scalar;
- this.g = scalar;
- this.b = scalar;
- return this;
- }
- setHex( hex ) {
- hex = Math.floor( hex );
- this.r = ( hex >> 16 & 255 ) / 255;
- this.g = ( hex >> 8 & 255 ) / 255;
- this.b = ( hex & 255 ) / 255;
- return this;
- }
- setRGB( r, g, b ) {
- this.r = r;
- this.g = g;
- this.b = b;
- return this;
- }
- setHSL( h, s, l ) {
- // h,s,l ranges are in 0.0 - 1.0
- h = MathUtils.euclideanModulo( h, 1 );
- s = MathUtils.clamp( s, 0, 1 );
- l = MathUtils.clamp( l, 0, 1 );
- if ( s === 0 ) {
- this.r = this.g = this.b = l;
- } else {
- const p = l <= 0.5 ? l * ( 1 + s ) : l + s - ( l * s );
- const q = ( 2 * l ) - p;
- this.r = hue2rgb( q, p, h + 1 / 3 );
- this.g = hue2rgb( q, p, h );
- this.b = hue2rgb( q, p, h - 1 / 3 );
- }
- return this;
- }
- setStyle( style ) {
- function handleAlpha( string ) {
- if ( string === undefined ) return;
- if ( parseFloat( string ) < 1 ) {
- console.warn( 'THREE.Color: Alpha component of ' + style + ' will be ignored.' );
- }
- }
- let m;
- if ( m = /^((?:rgb|hsl)a?)\(\s*([^\)]*)\)/.exec( style ) ) {
- // rgb / hsl
- let color;
- const name = m[ 1 ];
- const components = m[ 2 ];
- switch ( name ) {
- case 'rgb':
- case 'rgba':
- if ( color = /^(\d+)\s*,\s*(\d+)\s*,\s*(\d+)\s*(?:,\s*(\d*\.?\d+)\s*)?$/.exec( components ) ) {
- // rgb(255,0,0) rgba(255,0,0,0.5)
- this.r = Math.min( 255, parseInt( color[ 1 ], 10 ) ) / 255;
- this.g = Math.min( 255, parseInt( color[ 2 ], 10 ) ) / 255;
- this.b = Math.min( 255, parseInt( color[ 3 ], 10 ) ) / 255;
- handleAlpha( color[ 4 ] );
- return this;
- }
- if ( color = /^(\d+)\%\s*,\s*(\d+)\%\s*,\s*(\d+)\%\s*(?:,\s*(\d*\.?\d+)\s*)?$/.exec( components ) ) {
- // rgb(100%,0%,0%) rgba(100%,0%,0%,0.5)
- this.r = Math.min( 100, parseInt( color[ 1 ], 10 ) ) / 100;
- this.g = Math.min( 100, parseInt( color[ 2 ], 10 ) ) / 100;
- this.b = Math.min( 100, parseInt( color[ 3 ], 10 ) ) / 100;
- handleAlpha( color[ 4 ] );
- return this;
- }
- break;
- case 'hsl':
- case 'hsla':
- if ( color = /^(\d*\.?\d+)\s*,\s*(\d+)\%\s*,\s*(\d+)\%\s*(?:,\s*(\d*\.?\d+)\s*)?$/.exec( components ) ) {
- // hsl(120,50%,50%) hsla(120,50%,50%,0.5)
- const h = parseFloat( color[ 1 ] ) / 360;
- const s = parseInt( color[ 2 ], 10 ) / 100;
- const l = parseInt( color[ 3 ], 10 ) / 100;
- handleAlpha( color[ 4 ] );
- return this.setHSL( h, s, l );
- }
- break;
- }
- } else if ( m = /^\#([A-Fa-f\d]+)$/.exec( style ) ) {
- // hex color
- const hex = m[ 1 ];
- const size = hex.length;
- if ( size === 3 ) {
- // #ff0
- this.r = parseInt( hex.charAt( 0 ) + hex.charAt( 0 ), 16 ) / 255;
- this.g = parseInt( hex.charAt( 1 ) + hex.charAt( 1 ), 16 ) / 255;
- this.b = parseInt( hex.charAt( 2 ) + hex.charAt( 2 ), 16 ) / 255;
- return this;
- } else if ( size === 6 ) {
- // #ff0000
- this.r = parseInt( hex.charAt( 0 ) + hex.charAt( 1 ), 16 ) / 255;
- this.g = parseInt( hex.charAt( 2 ) + hex.charAt( 3 ), 16 ) / 255;
- this.b = parseInt( hex.charAt( 4 ) + hex.charAt( 5 ), 16 ) / 255;
- return this;
- }
- }
- if ( style && style.length > 0 ) {
- return this.setColorName( style );
- }
- return this;
- }
- setColorName( style ) {
- // color keywords
- const hex = _colorKeywords[ style ];
- if ( hex !== undefined ) {
- // red
- this.setHex( hex );
- } else {
- // unknown color
- console.warn( 'THREE.Color: Unknown color ' + style );
- }
- return this;
- }
- clone() {
- return new this.constructor( this.r, this.g, this.b );
- }
- copy( color ) {
- this.r = color.r;
- this.g = color.g;
- this.b = color.b;
- return this;
- }
- copyGammaToLinear( color, gammaFactor = 2.0 ) {
- this.r = Math.pow( color.r, gammaFactor );
- this.g = Math.pow( color.g, gammaFactor );
- this.b = Math.pow( color.b, gammaFactor );
- return this;
- }
- copyLinearToGamma( color, gammaFactor = 2.0 ) {
- const safeInverse = ( gammaFactor > 0 ) ? ( 1.0 / gammaFactor ) : 1.0;
- this.r = Math.pow( color.r, safeInverse );
- this.g = Math.pow( color.g, safeInverse );
- this.b = Math.pow( color.b, safeInverse );
- return this;
- }
- convertGammaToLinear( gammaFactor ) {
- this.copyGammaToLinear( this, gammaFactor );
- return this;
- }
- convertLinearToGamma( gammaFactor ) {
- this.copyLinearToGamma( this, gammaFactor );
- return this;
- }
- copySRGBToLinear( color ) {
- this.r = SRGBToLinear( color.r );
- this.g = SRGBToLinear( color.g );
- this.b = SRGBToLinear( color.b );
- return this;
- }
- copyLinearToSRGB( color ) {
- this.r = LinearToSRGB( color.r );
- this.g = LinearToSRGB( color.g );
- this.b = LinearToSRGB( color.b );
- return this;
- }
- convertSRGBToLinear() {
- this.copySRGBToLinear( this );
- return this;
- }
- convertLinearToSRGB() {
- this.copyLinearToSRGB( this );
- return this;
- }
- getHex() {
- return ( this.r * 255 ) << 16 ^ ( this.g * 255 ) << 8 ^ ( this.b * 255 ) << 0;
- }
- getHexString() {
- return ( '000000' + this.getHex().toString( 16 ) ).slice( - 6 );
- }
- getHSL( target ) {
- // h,s,l ranges are in 0.0 - 1.0
- if ( target === undefined ) {
- console.warn( 'THREE.Color: .getHSL() target is now required' );
- target = { h: 0, s: 0, l: 0 };
- }
- const r = this.r, g = this.g, b = this.b;
- const max = Math.max( r, g, b );
- const min = Math.min( r, g, b );
- let hue, saturation;
- const lightness = ( min + max ) / 2.0;
- if ( min === max ) {
- hue = 0;
- saturation = 0;
- } else {
- const delta = max - min;
- saturation = lightness <= 0.5 ? delta / ( max + min ) : delta / ( 2 - max - min );
- switch ( max ) {
- case r: hue = ( g - b ) / delta + ( g < b ? 6 : 0 ); break;
- case g: hue = ( b - r ) / delta + 2; break;
- case b: hue = ( r - g ) / delta + 4; break;
- }
- hue /= 6;
- }
- target.h = hue;
- target.s = saturation;
- target.l = lightness;
- return target;
- }
- getStyle() {
- return 'rgb(' + ( ( this.r * 255 ) | 0 ) + ',' + ( ( this.g * 255 ) | 0 ) + ',' + ( ( this.b * 255 ) | 0 ) + ')';
- }
- offsetHSL( h, s, l ) {
- this.getHSL( _hslA );
- _hslA.h += h; _hslA.s += s; _hslA.l += l;
- this.setHSL( _hslA.h, _hslA.s, _hslA.l );
- return this;
- }
- add( color ) {
- this.r += color.r;
- this.g += color.g;
- this.b += color.b;
- return this;
- }
- addColors( color1, color2 ) {
- this.r = color1.r + color2.r;
- this.g = color1.g + color2.g;
- this.b = color1.b + color2.b;
- return this;
- }
- addScalar( s ) {
- this.r += s;
- this.g += s;
- this.b += s;
- return this;
- }
- sub( color ) {
- this.r = Math.max( 0, this.r - color.r );
- this.g = Math.max( 0, this.g - color.g );
- this.b = Math.max( 0, this.b - color.b );
- return this;
- }
- multiply( color ) {
- this.r *= color.r;
- this.g *= color.g;
- this.b *= color.b;
- return this;
- }
- multiplyScalar( s ) {
- this.r *= s;
- this.g *= s;
- this.b *= s;
- return this;
- }
- lerp( color, alpha ) {
- this.r += ( color.r - this.r ) * alpha;
- this.g += ( color.g - this.g ) * alpha;
- this.b += ( color.b - this.b ) * alpha;
- return this;
- }
- lerpHSL( color, alpha ) {
- this.getHSL( _hslA );
- color.getHSL( _hslB );
- const h = MathUtils.lerp( _hslA.h, _hslB.h, alpha );
- const s = MathUtils.lerp( _hslA.s, _hslB.s, alpha );
- const l = MathUtils.lerp( _hslA.l, _hslB.l, alpha );
- this.setHSL( h, s, l );
- return this;
- }
- equals( c ) {
- return ( c.r === this.r ) && ( c.g === this.g ) && ( c.b === this.b );
- }
- fromArray( array, offset = 0 ) {
- this.r = array[ offset ];
- this.g = array[ offset + 1 ];
- this.b = array[ offset + 2 ];
- return this;
- }
- toArray( array = [], offset = 0 ) {
- array[ offset ] = this.r;
- array[ offset + 1 ] = this.g;
- array[ offset + 2 ] = this.b;
- return array;
- }
- fromBufferAttribute( attribute, index ) {
- this.r = attribute.getX( index );
- this.g = attribute.getY( index );
- this.b = attribute.getZ( index );
- if ( attribute.normalized === true ) {
- // assuming Uint8Array
- this.r /= 255;
- this.g /= 255;
- this.b /= 255;
- }
- return this;
- }
- toJSON() {
- return this.getHex();
- }
- }
- Color.NAMES = _colorKeywords;
- Color.prototype.r = 1;
- Color.prototype.g = 1;
- Color.prototype.b = 1;
- class Face3 {
- constructor( a, b, c, normal, color, materialIndex = 0 ) {
- this.a = a;
- this.b = b;
- this.c = c;
- this.normal = ( normal && normal.isVector3 ) ? normal : new Vector3();
- this.vertexNormals = Array.isArray( normal ) ? normal : [];
- this.color = ( color && color.isColor ) ? color : new Color();
- this.vertexColors = Array.isArray( color ) ? color : [];
- this.materialIndex = materialIndex;
- }
- clone() {
- return new this.constructor().copy( this );
- }
- copy( source ) {
- this.a = source.a;
- this.b = source.b;
- this.c = source.c;
- this.normal.copy( source.normal );
- this.color.copy( source.color );
- this.materialIndex = source.materialIndex;
- for ( let i = 0, il = source.vertexNormals.length; i < il; i ++ ) {
- this.vertexNormals[ i ] = source.vertexNormals[ i ].clone();
- }
- for ( let i = 0, il = source.vertexColors.length; i < il; i ++ ) {
- this.vertexColors[ i ] = source.vertexColors[ i ].clone();
- }
- return this;
- }
- }
- let materialId = 0;
- function Material() {
- Object.defineProperty( this, 'id', { value: materialId ++ } );
- this.uuid = MathUtils.generateUUID();
- this.name = '';
- this.type = 'Material';
- this.fog = true;
- this.blending = NormalBlending;
- this.side = FrontSide;
- this.flatShading = false;
- this.vertexColors = false;
- this.opacity = 1;
- this.transparent = false;
- this.blendSrc = SrcAlphaFactor;
- this.blendDst = OneMinusSrcAlphaFactor;
- this.blendEquation = AddEquation;
- this.blendSrcAlpha = null;
- this.blendDstAlpha = null;
- this.blendEquationAlpha = null;
- this.depthFunc = LessEqualDepth;
- this.depthTest = true;
- this.depthWrite = true;
- this.stencilWriteMask = 0xff;
- this.stencilFunc = AlwaysStencilFunc;
- this.stencilRef = 0;
- this.stencilFuncMask = 0xff;
- this.stencilFail = KeepStencilOp;
- this.stencilZFail = KeepStencilOp;
- this.stencilZPass = KeepStencilOp;
- this.stencilWrite = false;
- this.clippingPlanes = null;
- this.clipIntersection = false;
- this.clipShadows = false;
- this.shadowSide = null;
- this.colorWrite = true;
- this.precision = null; // override the renderer's default precision for this material
- this.polygonOffset = false;
- this.polygonOffsetFactor = 0;
- this.polygonOffsetUnits = 0;
- this.dithering = false;
- this.alphaTest = 0;
- this.premultipliedAlpha = false;
- this.visible = true;
- this.toneMapped = true;
- this.userData = {};
- this.version = 0;
- }
- Material.prototype = Object.assign( Object.create( EventDispatcher.prototype ), {
- constructor: Material,
- isMaterial: true,
- onBeforeCompile: function ( /* shaderobject, renderer */ ) {},
- customProgramCacheKey: function () {
- return this.onBeforeCompile.toString();
- },
- setValues: function ( values ) {
- if ( values === undefined ) return;
- for ( const key in values ) {
- const newValue = values[ key ];
- if ( newValue === undefined ) {
- console.warn( 'THREE.Material: \'' + key + '\' parameter is undefined.' );
- continue;
- }
- // for backward compatability if shading is set in the constructor
- if ( key === 'shading' ) {
- console.warn( 'THREE.' + this.type + ': .shading has been removed. Use the boolean .flatShading instead.' );
- this.flatShading = ( newValue === FlatShading ) ? true : false;
- continue;
- }
- const currentValue = this[ key ];
- if ( currentValue === undefined ) {
- console.warn( 'THREE.' + this.type + ': \'' + key + '\' is not a property of this material.' );
- continue;
- }
- if ( currentValue && currentValue.isColor ) {
- currentValue.set( newValue );
- } else if ( ( currentValue && currentValue.isVector3 ) && ( newValue && newValue.isVector3 ) ) {
- currentValue.copy( newValue );
- } else {
- this[ key ] = newValue;
- }
- }
- },
- toJSON: function ( meta ) {
- const isRoot = ( meta === undefined || typeof meta === 'string' );
- if ( isRoot ) {
- meta = {
- textures: {},
- images: {}
- };
- }
- const data = {
- metadata: {
- version: 4.5,
- type: 'Material',
- generator: 'Material.toJSON'
- }
- };
- // standard Material serialization
- data.uuid = this.uuid;
- data.type = this.type;
- if ( this.name !== '' ) data.name = this.name;
- if ( this.color && this.color.isColor ) data.color = this.color.getHex();
- if ( this.roughness !== undefined ) data.roughness = this.roughness;
- if ( this.metalness !== undefined ) data.metalness = this.metalness;
- if ( this.sheen && this.sheen.isColor ) data.sheen = this.sheen.getHex();
- if ( this.emissive && this.emissive.isColor ) data.emissive = this.emissive.getHex();
- if ( this.emissiveIntensity && this.emissiveIntensity !== 1 ) data.emissiveIntensity = this.emissiveIntensity;
- if ( this.specular && this.specular.isColor ) data.specular = this.specular.getHex();
- if ( this.shininess !== undefined ) data.shininess = this.shininess;
- if ( this.clearcoat !== undefined ) data.clearcoat = this.clearcoat;
- if ( this.clearcoatRoughness !== undefined ) data.clearcoatRoughness = this.clearcoatRoughness;
- if ( this.clearcoatMap && this.clearcoatMap.isTexture ) {
- data.clearcoatMap = this.clearcoatMap.toJSON( meta ).uuid;
- }
- if ( this.clearcoatRoughnessMap && this.clearcoatRoughnessMap.isTexture ) {
- data.clearcoatRoughnessMap = this.clearcoatRoughnessMap.toJSON( meta ).uuid;
- }
- if ( this.clearcoatNormalMap && this.clearcoatNormalMap.isTexture ) {
- data.clearcoatNormalMap = this.clearcoatNormalMap.toJSON( meta ).uuid;
- data.clearcoatNormalScale = this.clearcoatNormalScale.toArray();
- }
- if ( this.map && this.map.isTexture ) data.map = this.map.toJSON( meta ).uuid;
- if ( this.matcap && this.matcap.isTexture ) data.matcap = this.matcap.toJSON( meta ).uuid;
- if ( this.alphaMap && this.alphaMap.isTexture ) data.alphaMap = this.alphaMap.toJSON( meta ).uuid;
- if ( this.lightMap && this.lightMap.isTexture ) data.lightMap = this.lightMap.toJSON( meta ).uuid;
- if ( this.aoMap && this.aoMap.isTexture ) {
- data.aoMap = this.aoMap.toJSON( meta ).uuid;
- data.aoMapIntensity = this.aoMapIntensity;
- }
- if ( this.bumpMap && this.bumpMap.isTexture ) {
- data.bumpMap = this.bumpMap.toJSON( meta ).uuid;
- data.bumpScale = this.bumpScale;
- }
- if ( this.normalMap && this.normalMap.isTexture ) {
- data.normalMap = this.normalMap.toJSON( meta ).uuid;
- data.normalMapType = this.normalMapType;
- data.normalScale = this.normalScale.toArray();
- }
- if ( this.displacementMap && this.displacementMap.isTexture ) {
- data.displacementMap = this.displacementMap.toJSON( meta ).uuid;
- data.displacementScale = this.displacementScale;
- data.displacementBias = this.displacementBias;
- }
- if ( this.roughnessMap && this.roughnessMap.isTexture ) data.roughnessMap = this.roughnessMap.toJSON( meta ).uuid;
- if ( this.metalnessMap && this.metalnessMap.isTexture ) data.metalnessMap = this.metalnessMap.toJSON( meta ).uuid;
- if ( this.emissiveMap && this.emissiveMap.isTexture ) data.emissiveMap = this.emissiveMap.toJSON( meta ).uuid;
- if ( this.specularMap && this.specularMap.isTexture ) data.specularMap = this.specularMap.toJSON( meta ).uuid;
- if ( this.envMap && this.envMap.isTexture ) {
- data.envMap = this.envMap.toJSON( meta ).uuid;
- data.reflectivity = this.reflectivity; // Scale behind envMap
- data.refractionRatio = this.refractionRatio;
- if ( this.combine !== undefined ) data.combine = this.combine;
- if ( this.envMapIntensity !== undefined ) data.envMapIntensity = this.envMapIntensity;
- }
- if ( this.gradientMap && this.gradientMap.isTexture ) {
- data.gradientMap = this.gradientMap.toJSON( meta ).uuid;
- }
- if ( this.size !== undefined ) data.size = this.size;
- if ( this.sizeAttenuation !== undefined ) data.sizeAttenuation = this.sizeAttenuation;
- if ( this.blending !== NormalBlending ) data.blending = this.blending;
- if ( this.flatShading === true ) data.flatShading = this.flatShading;
- if ( this.side !== FrontSide ) data.side = this.side;
- if ( this.vertexColors ) data.vertexColors = true;
- if ( this.opacity < 1 ) data.opacity = this.opacity;
- if ( this.transparent === true ) data.transparent = this.transparent;
- data.depthFunc = this.depthFunc;
- data.depthTest = this.depthTest;
- data.depthWrite = this.depthWrite;
- data.stencilWrite = this.stencilWrite;
- data.stencilWriteMask = this.stencilWriteMask;
- data.stencilFunc = this.stencilFunc;
- data.stencilRef = this.stencilRef;
- data.stencilFuncMask = this.stencilFuncMask;
- data.stencilFail = this.stencilFail;
- data.stencilZFail = this.stencilZFail;
- data.stencilZPass = this.stencilZPass;
- // rotation (SpriteMaterial)
- if ( this.rotation && this.rotation !== 0 ) data.rotation = this.rotation;
- if ( this.polygonOffset === true ) data.polygonOffset = true;
- if ( this.polygonOffsetFactor !== 0 ) data.polygonOffsetFactor = this.polygonOffsetFactor;
- if ( this.polygonOffsetUnits !== 0 ) data.polygonOffsetUnits = this.polygonOffsetUnits;
- if ( this.lineWidth && this.lineWidth !== 1 ) data.lineWidth = this.lineWidth;
- if ( this.dashSize !== undefined ) data.dashSize = this.dashSize;
- if ( this.gapSize !== undefined ) data.gapSize = this.gapSize;
- if ( this.scale !== undefined ) data.scale = this.scale;
- if ( this.dithering === true ) data.dithering = true;
- if ( this.alphaTest > 0 ) data.alphaTest = this.alphaTest;
- if ( this.premultipliedAlpha === true ) data.premultipliedAlpha = this.premultipliedAlpha;
- if ( this.wireframe === true ) data.wireframe = this.wireframe;
- if ( this.wireframelineWidth > 1 ) data.wireframelineWidth = this.wireframelineWidth;
- if ( this.wireframeLinecap !== 'round' ) data.wireframeLinecap = this.wireframeLinecap;
- if ( this.wireframeLinejoin !== 'round' ) data.wireframeLinejoin = this.wireframeLinejoin;
- if ( this.morphTargets === true ) data.morphTargets = true;
- if ( this.morphNormals === true ) data.morphNormals = true;
- if ( this.skinning === true ) data.skinning = true;
- if ( this.visible === false ) data.visible = false;
- if ( this.toneMapped === false ) data.toneMapped = false;
- if ( JSON.stringify( this.userData ) !== '{}' ) data.userData = this.userData;
- // TODO: Copied from Object3D.toJSON
- function extractFromCache( cache ) {
- const values = [];
- for ( const key in cache ) {
- const data = cache[ key ];
- delete data.metadata;
- values.push( data );
- }
- return values;
- }
- if ( isRoot ) {
- const textures = extractFromCache( meta.textures );
- const images = extractFromCache( meta.images );
- if ( textures.length > 0 ) data.textures = textures;
- if ( images.length > 0 ) data.images = images;
- }
- return data;
- },
- clone: function () {
- return new this.constructor().copy( this );
- },
- copy: function ( source ) {
- this.name = source.name;
- this.fog = source.fog;
- this.blending = source.blending;
- this.side = source.side;
- this.flatShading = source.flatShading;
- this.vertexColors = source.vertexColors;
- this.opacity = source.opacity;
- this.transparent = source.transparent;
- this.blendSrc = source.blendSrc;
- this.blendDst = source.blendDst;
- this.blendEquation = source.blendEquation;
- this.blendSrcAlpha = source.blendSrcAlpha;
- this.blendDstAlpha = source.blendDstAlpha;
- this.blendEquationAlpha = source.blendEquationAlpha;
- this.depthFunc = source.depthFunc;
- this.depthTest = source.depthTest;
- this.depthWrite = source.depthWrite;
- this.stencilWriteMask = source.stencilWriteMask;
- this.stencilFunc = source.stencilFunc;
- this.stencilRef = source.stencilRef;
- this.stencilFuncMask = source.stencilFuncMask;
- this.stencilFail = source.stencilFail;
- this.stencilZFail = source.stencilZFail;
- this.stencilZPass = source.stencilZPass;
- this.stencilWrite = source.stencilWrite;
- const srcPlanes = source.clippingPlanes;
- let dstPlanes = null;
- if ( srcPlanes !== null ) {
- const n = srcPlanes.length;
- dstPlanes = new Array( n );
- for ( let i = 0; i !== n; ++ i ) {
- dstPlanes[ i ] = srcPlanes[ i ].clone();
- }
- }
- this.clippingPlanes = dstPlanes;
- this.clipIntersection = source.clipIntersection;
- this.clipShadows = source.clipShadows;
- this.shadowSide = source.shadowSide;
- this.colorWrite = source.colorWrite;
- this.precision = source.precision;
- this.polygonOffset = source.polygonOffset;
- this.polygonOffsetFactor = source.polygonOffsetFactor;
- this.polygonOffsetUnits = source.polygonOffsetUnits;
- this.dithering = source.dithering;
- this.alphaTest = source.alphaTest;
- this.premultipliedAlpha = source.premultipliedAlpha;
- this.visible = source.visible;
- this.toneMapped = source.toneMapped;
- this.userData = JSON.parse( JSON.stringify( source.userData ) );
- return this;
- },
- dispose: function () {
- this.dispatchEvent( { type: 'dispose' } );
- }
- } );
- Object.defineProperty( Material.prototype, 'needsUpdate', {
- set: function ( value ) {
- if ( value === true ) this.version ++;
- }
- } );
- /**
- * parameters = {
- * color: <hex>,
- * opacity: <float>,
- * map: new THREE.Texture( <Image> ),
- *
- * lightMap: new THREE.Texture( <Image> ),
- * lightMapIntensity: <float>
- *
- * aoMap: new THREE.Texture( <Image> ),
- * aoMapIntensity: <float>
- *
- * specularMap: new THREE.Texture( <Image> ),
- *
- * alphaMap: new THREE.Texture( <Image> ),
- *
- * envMap: new THREE.CubeTexture( [posx, negx, posy, negy, posz, negz] ),
- * combine: THREE.Multiply,
- * reflectivity: <float>,
- * refractionRatio: <float>,
- *
- * depthTest: <bool>,
- * depthWrite: <bool>,
- *
- * wireframe: <boolean>,
- * wireframelineWidth: <float>,
- *
- * skinning: <bool>,
- * morphTargets: <bool>
- * }
- */
- function MeshBasicMaterial( parameters ) {
- Material.call( this );
- this.type = 'MeshBasicMaterial';
- this.color = new Color( 0xffffff ); // emissive
- this.map = null;
- this.lightMap = null;
- this.lightMapIntensity = 1.0;
- this.aoMap = null;
- this.aoMapIntensity = 1.0;
- this.specularMap = null;
- this.alphaMap = null;
- this.envMap = null;
- this.combine = MultiplyOperation;
- this.reflectivity = 1;
- this.refractionRatio = 0.98;
- this.wireframe = false;
- this.wireframelineWidth = 1;
- this.wireframeLinecap = 'round';
- this.wireframeLinejoin = 'round';
- this.skinning = false;
- this.morphTargets = false;
- this.setValues( parameters );
- }
- MeshBasicMaterial.prototype = Object.create( Material.prototype );
- MeshBasicMaterial.prototype.constructor = MeshBasicMaterial;
- MeshBasicMaterial.prototype.isMeshBasicMaterial = true;
- MeshBasicMaterial.prototype.copy = function ( source ) {
- Material.prototype.copy.call( this, source );
- this.color.copy( source.color );
- this.map = source.map;
- this.lightMap = source.lightMap;
- this.lightMapIntensity = source.lightMapIntensity;
- this.aoMap = source.aoMap;
- this.aoMapIntensity = source.aoMapIntensity;
- this.specularMap = source.specularMap;
- this.alphaMap = source.alphaMap;
- this.envMap = source.envMap;
- this.combine = source.combine;
- this.reflectivity = source.reflectivity;
- this.refractionRatio = source.refractionRatio;
- this.wireframe = source.wireframe;
- this.wireframelineWidth = source.wireframelineWidth;
- this.wireframeLinecap = source.wireframeLinecap;
- this.wireframeLinejoin = source.wireframeLinejoin;
- this.skinning = source.skinning;
- this.morphTargets = source.morphTargets;
- return this;
- };
- const _vector$3 = new Vector3();
- const _vector2$1 = new Vector2$1();
- function BufferAttribute( array, itemSize, normalized ) {
- if ( Array.isArray( array ) ) {
- throw new TypeError( 'THREE.BufferAttribute: array should be a Typed Array.' );
- }
- this.name = '';
- this.array = array;
- this.itemSize = itemSize;
- this.count = array !== undefined ? array.length / itemSize : 0;
- this.normalized = normalized === true;
- this.usage = StaticDrawUsage;
- this.updateRange = { offset: 0, count: - 1 };
- this.version = 0;
- }
- Object.defineProperty( BufferAttribute.prototype, 'needsUpdate', {
- set: function ( value ) {
- if ( value === true ) this.version ++;
- }
- } );
- Object.assign( BufferAttribute.prototype, {
- isBufferAttribute: true,
- onUploadCallback: function () {},
- setUsage: function ( value ) {
- this.usage = value;
- return this;
- },
- copy: function ( source ) {
- this.name = source.name;
- this.array = new source.array.constructor( source.array );
- this.itemSize = source.itemSize;
- this.count = source.count;
- this.normalized = source.normalized;
- this.usage = source.usage;
- return this;
- },
- copyAt: function ( index1, attribute, index2 ) {
- index1 *= this.itemSize;
- index2 *= attribute.itemSize;
- for ( let i = 0, l = this.itemSize; i < l; i ++ ) {
- this.array[ index1 + i ] = attribute.array[ index2 + i ];
- }
- return this;
- },
- copyArray: function ( array ) {
- this.array.set( array );
- return this;
- },
- copyColorsArray: function ( colors ) {
- const array = this.array;
- let offset = 0;
- for ( let i = 0, l = colors.length; i < l; i ++ ) {
- let color = colors[ i ];
- if ( color === undefined ) {
- console.warn( 'THREE.BufferAttribute.copyColorsArray(): color is undefined', i );
- color = new Color();
- }
- array[ offset ++ ] = color.r;
- array[ offset ++ ] = color.g;
- array[ offset ++ ] = color.b;
- }
- return this;
- },
- copyVector2sArray: function ( vectors ) {
- const array = this.array;
- let offset = 0;
- for ( let i = 0, l = vectors.length; i < l; i ++ ) {
- let vector = vectors[ i ];
- if ( vector === undefined ) {
- console.warn( 'THREE.BufferAttribute.copyVector2sArray(): vector is undefined', i );
- vector = new Vector2$1();
- }
- array[ offset ++ ] = vector.x;
- array[ offset ++ ] = vector.y;
- }
- return this;
- },
- copyVector3sArray: function ( vectors ) {
- const array = this.array;
- let offset = 0;
- for ( let i = 0, l = vectors.length; i < l; i ++ ) {
- let vector = vectors[ i ];
- if ( vector === undefined ) {
- console.warn( 'THREE.BufferAttribute.copyVector3sArray(): vector is undefined', i );
- vector = new Vector3();
- }
- array[ offset ++ ] = vector.x;
- array[ offset ++ ] = vector.y;
- array[ offset ++ ] = vector.z;
- }
- return this;
- },
- copyVector4sArray: function ( vectors ) {
- const array = this.array;
- let offset = 0;
- for ( let i = 0, l = vectors.length; i < l; i ++ ) {
- let vector = vectors[ i ];
- if ( vector === undefined ) {
- console.warn( 'THREE.BufferAttribute.copyVector4sArray(): vector is undefined', i );
- vector = new Vector4();
- }
- array[ offset ++ ] = vector.x;
- array[ offset ++ ] = vector.y;
- array[ offset ++ ] = vector.z;
- array[ offset ++ ] = vector.w;
- }
- return this;
- },
- applyMatrix3: function ( m ) {
- if ( this.itemSize === 2 ) {
- for ( let i = 0, l = this.count; i < l; i ++ ) {
- _vector2$1.fromBufferAttribute( this, i );
- _vector2$1.applyMatrix3( m );
- this.setXY( i, _vector2$1.x, _vector2$1.y );
- }
- } else if ( this.itemSize === 3 ) {
- for ( let i = 0, l = this.count; i < l; i ++ ) {
- _vector$3.fromBufferAttribute( this, i );
- _vector$3.applyMatrix3( m );
- this.setXYZ( i, _vector$3.x, _vector$3.y, _vector$3.z );
- }
- }
- return this;
- },
- applyMatrix4: function ( m ) {
- for ( let i = 0, l = this.count; i < l; i ++ ) {
- _vector$3.x = this.getX( i );
- _vector$3.y = this.getY( i );
- _vector$3.z = this.getZ( i );
- _vector$3.applyMatrix4( m );
- this.setXYZ( i, _vector$3.x, _vector$3.y, _vector$3.z );
- }
- return this;
- },
- applyNormalMatrix: function ( m ) {
- for ( let i = 0, l = this.count; i < l; i ++ ) {
- _vector$3.x = this.getX( i );
- _vector$3.y = this.getY( i );
- _vector$3.z = this.getZ( i );
- _vector$3.applyNormalMatrix( m );
- this.setXYZ( i, _vector$3.x, _vector$3.y, _vector$3.z );
- }
- return this;
- },
- transformDirection: function ( m ) {
- for ( let i = 0, l = this.count; i < l; i ++ ) {
- _vector$3.x = this.getX( i );
- _vector$3.y = this.getY( i );
- _vector$3.z = this.getZ( i );
- _vector$3.transformDirection( m );
- this.setXYZ( i, _vector$3.x, _vector$3.y, _vector$3.z );
- }
- return this;
- },
- set: function ( value, offset = 0 ) {
- this.array.set( value, offset );
- return this;
- },
- getX: function ( index ) {
- return this.array[ index * this.itemSize ];
- },
- setX: function ( index, x ) {
- this.array[ index * this.itemSize ] = x;
- return this;
- },
- getY: function ( index ) {
- return this.array[ index * this.itemSize + 1 ];
- },
- setY: function ( index, y ) {
- this.array[ index * this.itemSize + 1 ] = y;
- return this;
- },
- getZ: function ( index ) {
- return this.array[ index * this.itemSize + 2 ];
- },
- setZ: function ( index, z ) {
- this.array[ index * this.itemSize + 2 ] = z;
- return this;
- },
- getW: function ( index ) {
- return this.array[ index * this.itemSize + 3 ];
- },
- setW: function ( index, w ) {
- this.array[ index * this.itemSize + 3 ] = w;
- return this;
- },
- setXY: function ( index, x, y ) {
- index *= this.itemSize;
- this.array[ index + 0 ] = x;
- this.array[ index + 1 ] = y;
- return this;
- },
- setXYZ: function ( index, x, y, z ) {
- index *= this.itemSize;
- this.array[ index + 0 ] = x;
- this.array[ index + 1 ] = y;
- this.array[ index + 2 ] = z;
- return this;
- },
- setXYZW: function ( index, x, y, z, w ) {
- index *= this.itemSize;
- this.array[ index + 0 ] = x;
- this.array[ index + 1 ] = y;
- this.array[ index + 2 ] = z;
- this.array[ index + 3 ] = w;
- return this;
- },
- onUpload: function ( callback ) {
- this.onUploadCallback = callback;
- return this;
- },
- clone: function () {
- return new this.constructor( this.array, this.itemSize ).copy( this );
- },
- toJSON: function () {
- return {
- itemSize: this.itemSize,
- type: this.array.constructor.name,
- array: Array.prototype.slice.call( this.array ),
- normalized: this.normalized
- };
- }
- } );
- //
- function Int8BufferAttribute( array, itemSize, normalized ) {
- BufferAttribute.call( this, new Int8Array( array ), itemSize, normalized );
- }
- Int8BufferAttribute.prototype = Object.create( BufferAttribute.prototype );
- Int8BufferAttribute.prototype.constructor = Int8BufferAttribute;
- function Uint8BufferAttribute( array, itemSize, normalized ) {
- BufferAttribute.call( this, new Uint8Array( array ), itemSize, normalized );
- }
- Uint8BufferAttribute.prototype = Object.create( BufferAttribute.prototype );
- Uint8BufferAttribute.prototype.constructor = Uint8BufferAttribute;
- function Uint8ClampedBufferAttribute( array, itemSize, normalized ) {
- BufferAttribute.call( this, new Uint8ClampedArray( array ), itemSize, normalized );
- }
- Uint8ClampedBufferAttribute.prototype = Object.create( BufferAttribute.prototype );
- Uint8ClampedBufferAttribute.prototype.constructor = Uint8ClampedBufferAttribute;
- function Int16BufferAttribute( array, itemSize, normalized ) {
- BufferAttribute.call( this, new Int16Array( array ), itemSize, normalized );
- }
- Int16BufferAttribute.prototype = Object.create( BufferAttribute.prototype );
- Int16BufferAttribute.prototype.constructor = Int16BufferAttribute;
- function Uint16BufferAttribute( array, itemSize, normalized ) {
- BufferAttribute.call( this, new Uint16Array( array ), itemSize, normalized );
- }
- Uint16BufferAttribute.prototype = Object.create( BufferAttribute.prototype );
- Uint16BufferAttribute.prototype.constructor = Uint16BufferAttribute;
- function Int32BufferAttribute( array, itemSize, normalized ) {
- BufferAttribute.call( this, new Int32Array( array ), itemSize, normalized );
- }
- Int32BufferAttribute.prototype = Object.create( BufferAttribute.prototype );
- Int32BufferAttribute.prototype.constructor = Int32BufferAttribute;
- function Uint32BufferAttribute( array, itemSize, normalized ) {
- BufferAttribute.call( this, new Uint32Array( array ), itemSize, normalized );
- }
- Uint32BufferAttribute.prototype = Object.create( BufferAttribute.prototype );
- Uint32BufferAttribute.prototype.constructor = Uint32BufferAttribute;
- function Float16BufferAttribute( array, itemSize, normalized ) {
- BufferAttribute.call( this, new Uint16Array( array ), itemSize, normalized );
- }
- Float16BufferAttribute.prototype = Object.create( BufferAttribute.prototype );
- Float16BufferAttribute.prototype.constructor = Float16BufferAttribute;
- Float16BufferAttribute.prototype.isFloat16BufferAttribute = true;
- function Float32BufferAttribute( array, itemSize, normalized ) {
- BufferAttribute.call( this, new Float32Array( array ), itemSize, normalized );
- }
- Float32BufferAttribute.prototype = Object.create( BufferAttribute.prototype );
- Float32BufferAttribute.prototype.constructor = Float32BufferAttribute;
- function Float64BufferAttribute( array, itemSize, normalized ) {
- BufferAttribute.call( this, new Float64Array( array ), itemSize, normalized );
- }
- Float64BufferAttribute.prototype = Object.create( BufferAttribute.prototype );
- Float64BufferAttribute.prototype.constructor = Float64BufferAttribute;
- class DirectGeometry {
- constructor() {
- this.vertices = [];
- this.normals = [];
- this.colors = [];
- this.uvs = [];
- this.uvs2 = [];
- this.groups = [];
- this.morphTargets = {};
- this.skinWeights = [];
- this.skinIndices = [];
- // this.lineDistances = [];
- this.boundingBox = null;
- this.boundingSphere = null;
- // update flags
- this.verticesNeedUpdate = false;
- this.normalsNeedUpdate = false;
- this.colorsNeedUpdate = false;
- this.uvsNeedUpdate = false;
- this.groupsNeedUpdate = false;
- }
- computeGroups( geometry ) {
- const groups = [];
- let group, i;
- let materialIndex = undefined;
- const faces = geometry.faces;
- for ( i = 0; i < faces.length; i ++ ) {
- const face = faces[ i ];
- // materials
- if ( face.materialIndex !== materialIndex ) {
- materialIndex = face.materialIndex;
- if ( group !== undefined ) {
- group.count = ( i * 3 ) - group.start;
- groups.push( group );
- }
- group = {
- start: i * 3,
- materialIndex: materialIndex
- };
- }
- }
- if ( group !== undefined ) {
- group.count = ( i * 3 ) - group.start;
- groups.push( group );
- }
- this.groups = groups;
- }
- fromGeometry( geometry ) {
- const faces = geometry.faces;
- const vertices = geometry.vertices;
- const faceVertexUvs = geometry.faceVertexUvs;
- const hasFaceVertexUv = faceVertexUvs[ 0 ] && faceVertexUvs[ 0 ].length > 0;
- const hasFaceVertexUv2 = faceVertexUvs[ 1 ] && faceVertexUvs[ 1 ].length > 0;
- // morphs
- const morphTargets = geometry.morphTargets;
- const morphTargetsLength = morphTargets.length;
- let morphTargetsPosition;
- if ( morphTargetsLength > 0 ) {
- morphTargetsPosition = [];
- for ( let i = 0; i < morphTargetsLength; i ++ ) {
- morphTargetsPosition[ i ] = {
- name: morphTargets[ i ].name,
- data: []
- };
- }
- this.morphTargets.position = morphTargetsPosition;
- }
- const morphNormals = geometry.morphNormals;
- const morphNormalsLength = morphNormals.length;
- let morphTargetsNormal;
- if ( morphNormalsLength > 0 ) {
- morphTargetsNormal = [];
- for ( let i = 0; i < morphNormalsLength; i ++ ) {
- morphTargetsNormal[ i ] = {
- name: morphNormals[ i ].name,
- data: []
- };
- }
- this.morphTargets.normal = morphTargetsNormal;
- }
- // skins
- const skinIndices = geometry.skinIndices;
- const skinWeights = geometry.skinWeights;
- const hasSkinIndices = skinIndices.length === vertices.length;
- const hasSkinWeights = skinWeights.length === vertices.length;
- //
- if ( vertices.length > 0 && faces.length === 0 ) {
- console.error( 'THREE.DirectGeometry: Faceless geometries are not supported.' );
- }
- for ( let i = 0; i < faces.length; i ++ ) {
- const face = faces[ i ];
- this.vertices.push( vertices[ face.a ], vertices[ face.b ], vertices[ face.c ] );
- const vertexNormals = face.vertexNormals;
- if ( vertexNormals.length === 3 ) {
- this.normals.push( vertexNormals[ 0 ], vertexNormals[ 1 ], vertexNormals[ 2 ] );
- } else {
- const normal = face.normal;
- this.normals.push( normal, normal, normal );
- }
- const vertexColors = face.vertexColors;
- if ( vertexColors.length === 3 ) {
- this.colors.push( vertexColors[ 0 ], vertexColors[ 1 ], vertexColors[ 2 ] );
- } else {
- const color = face.color;
- this.colors.push( color, color, color );
- }
- if ( hasFaceVertexUv === true ) {
- const vertexUvs = faceVertexUvs[ 0 ][ i ];
- if ( vertexUvs !== undefined ) {
- this.uvs.push( vertexUvs[ 0 ], vertexUvs[ 1 ], vertexUvs[ 2 ] );
- } else {
- console.warn( 'THREE.DirectGeometry.fromGeometry(): Undefined vertexUv ', i );
- this.uvs.push( new Vector2$1(), new Vector2$1(), new Vector2$1() );
- }
- }
- if ( hasFaceVertexUv2 === true ) {
- const vertexUvs = faceVertexUvs[ 1 ][ i ];
- if ( vertexUvs !== undefined ) {
- this.uvs2.push( vertexUvs[ 0 ], vertexUvs[ 1 ], vertexUvs[ 2 ] );
- } else {
- console.warn( 'THREE.DirectGeometry.fromGeometry(): Undefined vertexUv2 ', i );
- this.uvs2.push( new Vector2$1(), new Vector2$1(), new Vector2$1() );
- }
- }
- // morphs
- for ( let j = 0; j < morphTargetsLength; j ++ ) {
- const morphTarget = morphTargets[ j ].vertices;
- morphTargetsPosition[ j ].data.push( morphTarget[ face.a ], morphTarget[ face.b ], morphTarget[ face.c ] );
- }
- for ( let j = 0; j < morphNormalsLength; j ++ ) {
- const morphNormal = morphNormals[ j ].vertexNormals[ i ];
- morphTargetsNormal[ j ].data.push( morphNormal.a, morphNormal.b, morphNormal.c );
- }
- // skins
- if ( hasSkinIndices ) {
- this.skinIndices.push( skinIndices[ face.a ], skinIndices[ face.b ], skinIndices[ face.c ] );
- }
- if ( hasSkinWeights ) {
- this.skinWeights.push( skinWeights[ face.a ], skinWeights[ face.b ], skinWeights[ face.c ] );
- }
- }
- this.computeGroups( geometry );
- this.verticesNeedUpdate = geometry.verticesNeedUpdate;
- this.normalsNeedUpdate = geometry.normalsNeedUpdate;
- this.colorsNeedUpdate = geometry.colorsNeedUpdate;
- this.uvsNeedUpdate = geometry.uvsNeedUpdate;
- this.groupsNeedUpdate = geometry.groupsNeedUpdate;
- if ( geometry.boundingSphere !== null ) {
- this.boundingSphere = geometry.boundingSphere.clone();
- }
- if ( geometry.boundingBox !== null ) {
- this.boundingBox = geometry.boundingBox.clone();
- }
- return this;
- }
- }
- function arrayMax( array ) {
- if ( array.length === 0 ) return - Infinity;
- let max = array[ 0 ];
- for ( let i = 1, l = array.length; i < l; ++ i ) {
- if ( array[ i ] > max ) max = array[ i ];
- }
- return max;
- }
- const TYPED_ARRAYS = {
- Int8Array: Int8Array,
- Uint8Array: Uint8Array,
- // Workaround for IE11 pre KB2929437. See #11440
- Uint8ClampedArray: typeof Uint8ClampedArray !== 'undefined' ? Uint8ClampedArray : Uint8Array,
- Int16Array: Int16Array,
- Uint16Array: Uint16Array,
- Int32Array: Int32Array,
- Uint32Array: Uint32Array,
- Float32Array: Float32Array,
- Float64Array: Float64Array
- };
- function getTypedArray( type, buffer ) {
- return new TYPED_ARRAYS[ type ]( buffer );
- }
- let _bufferGeometryId = 1; // BufferGeometry uses odd numbers as Id
- const _m1$2 = new Matrix4();
- const _obj = new Object3D();
- const _offset = new Vector3();
- const _box$2 = new Box3();
- const _boxMorphTargets = new Box3();
- const _vector$4 = new Vector3();
- function BufferGeometry() {
- Object.defineProperty( this, 'id', { value: _bufferGeometryId += 2 } );
- this.uuid = MathUtils.generateUUID();
- this.name = '';
- this.type = 'BufferGeometry';
- this.index = null;
- this.attributes = {};
- this.morphAttributes = {};
- this.morphTargetsRelative = false;
- this.groups = [];
- this.boundingBox = null;
- this.boundingSphere = null;
- this.drawRange = { start: 0, count: Infinity };
- this.userData = {};
- }
- BufferGeometry.prototype = Object.assign( Object.create( EventDispatcher.prototype ), {
- constructor: BufferGeometry,
- isBufferGeometry: true,
- getIndex: function () {
- return this.index;
- },
- setIndex: function ( index ) {
- if ( Array.isArray( index ) ) {
- this.index = new ( arrayMax( index ) > 65535 ? Uint32BufferAttribute : Uint16BufferAttribute )( index, 1 );
- } else {
- this.index = index;
- }
- return this;
- },
- getAttribute: function ( name ) {
- return this.attributes[ name ];
- },
- setAttribute: function ( name, attribute ) {
- this.attributes[ name ] = attribute;
- return this;
- },
- deleteAttribute: function ( name ) {
- delete this.attributes[ name ];
- return this;
- },
- hasAttribute: function ( name ) {
- return this.attributes[ name ] !== undefined;
- },
- addGroup: function ( start, count, materialIndex = 0 ) {
- this.groups.push( {
- start: start,
- count: count,
- materialIndex: materialIndex
- } );
- },
- clearGroups: function () {
- this.groups = [];
- },
- setDrawRange: function ( start, count ) {
- this.drawRange.start = start;
- this.drawRange.count = count;
- },
- applyMatrix4: function ( matrix ) {
- const position = this.attributes.position;
- if ( position !== undefined ) {
- position.applyMatrix4( matrix );
- position.needsUpdate = true;
- }
- const normal = this.attributes.normal;
- if ( normal !== undefined ) {
- const normalMatrix = new Matrix3().getNormalMatrix( matrix );
- normal.applyNormalMatrix( normalMatrix );
- normal.needsUpdate = true;
- }
- const tangent = this.attributes.tangent;
- if ( tangent !== undefined ) {
- tangent.transformDirection( matrix );
- tangent.needsUpdate = true;
- }
- if ( this.boundingBox !== null ) {
- this.computeBoundingBox();
- }
- if ( this.boundingSphere !== null ) {
- this.computeBoundingSphere();
- }
- return this;
- },
- rotateX: function ( angle ) {
- // rotate geometry around world x-axis
- _m1$2.makeRotationX( angle );
- this.applyMatrix4( _m1$2 );
- return this;
- },
- rotateY: function ( angle ) {
- // rotate geometry around world y-axis
- _m1$2.makeRotationY( angle );
- this.applyMatrix4( _m1$2 );
- return this;
- },
- rotateZ: function ( angle ) {
- // rotate geometry around world z-axis
- _m1$2.makeRotationZ( angle );
- this.applyMatrix4( _m1$2 );
- return this;
- },
- translate: function ( x, y, z ) {
- // translate geometry
- _m1$2.makeTranslation( x, y, z );
- this.applyMatrix4( _m1$2 );
- return this;
- },
- scale: function ( x, y, z ) {
- // scale geometry
- _m1$2.makeScale( x, y, z );
- this.applyMatrix4( _m1$2 );
- return this;
- },
- lookAt: function ( vector ) {
- _obj.lookAt( vector );
- _obj.updateMatrix();
- this.applyMatrix4( _obj.matrix );
- return this;
- },
- center: function () {
- this.computeBoundingBox();
- this.boundingBox.getCenter( _offset ).negate();
- this.translate( _offset.x, _offset.y, _offset.z );
- return this;
- },
- setFromObject: function ( object ) {
- // console.log( 'THREE.BufferGeometry.setFromObject(). Converting', object, this );
- const geometry = object.geometry;
- if ( object.isPoints || object.isLine ) {
- const positions = new Float32BufferAttribute( geometry.vertices.length * 3, 3 );
- const colors = new Float32BufferAttribute( geometry.colors.length * 3, 3 );
- this.setAttribute( 'position', positions.copyVector3sArray( geometry.vertices ) );
- this.setAttribute( 'color', colors.copyColorsArray( geometry.colors ) );
- if ( geometry.lineDistances && geometry.lineDistances.length === geometry.vertices.length ) {
- const lineDistances = new Float32BufferAttribute( geometry.lineDistances.length, 1 );
- this.setAttribute( 'lineDistance', lineDistances.copyArray( geometry.lineDistances ) );
- }
- if ( geometry.boundingSphere !== null ) {
- this.boundingSphere = geometry.boundingSphere.clone();
- }
- if ( geometry.boundingBox !== null ) {
- this.boundingBox = geometry.boundingBox.clone();
- }
- } else if ( object.isMesh ) {
- if ( geometry && geometry.isGeometry ) {
- this.fromGeometry( geometry );
- }
- }
- return this;
- },
- setFromPoints: function ( points ) {
- const position = [];
- for ( let i = 0, l = points.length; i < l; i ++ ) {
- const point = points[ i ];
- position.push( point.x, point.y, point.z || 0 );
- }
- this.setAttribute( 'position', new Float32BufferAttribute( position, 3 ) );
- return this;
- },
- updateFromObject: function ( object ) {
- let geometry = object.geometry;
- if ( object.isMesh ) {
- let direct = geometry.__directGeometry;
- if ( geometry.elementsNeedUpdate === true ) {
- direct = undefined;
- geometry.elementsNeedUpdate = false;
- }
- if ( direct === undefined ) {
- return this.fromGeometry( geometry );
- }
- direct.verticesNeedUpdate = geometry.verticesNeedUpdate;
- direct.normalsNeedUpdate = geometry.normalsNeedUpdate;
- direct.colorsNeedUpdate = geometry.colorsNeedUpdate;
- direct.uvsNeedUpdate = geometry.uvsNeedUpdate;
- direct.groupsNeedUpdate = geometry.groupsNeedUpdate;
- geometry.verticesNeedUpdate = false;
- geometry.normalsNeedUpdate = false;
- geometry.colorsNeedUpdate = false;
- geometry.uvsNeedUpdate = false;
- geometry.groupsNeedUpdate = false;
- geometry = direct;
- }
- if ( geometry.verticesNeedUpdate === true ) {
- const attribute = this.attributes.position;
- if ( attribute !== undefined ) {
- attribute.copyVector3sArray( geometry.vertices );
- attribute.needsUpdate = true;
- }
- geometry.verticesNeedUpdate = false;
- }
- if ( geometry.normalsNeedUpdate === true ) {
- const attribute = this.attributes.normal;
- if ( attribute !== undefined ) {
- attribute.copyVector3sArray( geometry.normals );
- attribute.needsUpdate = true;
- }
- geometry.normalsNeedUpdate = false;
- }
- if ( geometry.colorsNeedUpdate === true ) {
- const attribute = this.attributes.color;
- if ( attribute !== undefined ) {
- attribute.copyColorsArray( geometry.colors );
- attribute.needsUpdate = true;
- }
- geometry.colorsNeedUpdate = false;
- }
- if ( geometry.uvsNeedUpdate ) {
- const attribute = this.attributes.uv;
- if ( attribute !== undefined ) {
- attribute.copyVector2sArray( geometry.uvs );
- attribute.needsUpdate = true;
- }
- geometry.uvsNeedUpdate = false;
- }
- if ( geometry.lineDistancesNeedUpdate ) {
- const attribute = this.attributes.lineDistance;
- if ( attribute !== undefined ) {
- attribute.copyArray( geometry.lineDistances );
- attribute.needsUpdate = true;
- }
- geometry.lineDistancesNeedUpdate = false;
- }
- if ( geometry.groupsNeedUpdate ) {
- geometry.computeGroups( object.geometry );
- this.groups = geometry.groups;
- geometry.groupsNeedUpdate = false;
- }
- return this;
- },
- fromGeometry: function ( geometry ) {
- geometry.__directGeometry = new DirectGeometry().fromGeometry( geometry );
- return this.fromDirectGeometry( geometry.__directGeometry );
- },
- fromDirectGeometry: function ( geometry ) {
- const positions = new Float32Array( geometry.vertices.length * 3 );
- this.setAttribute( 'position', new BufferAttribute( positions, 3 ).copyVector3sArray( geometry.vertices ) );
- if ( geometry.normals.length > 0 ) {
- const normals = new Float32Array( geometry.normals.length * 3 );
- this.setAttribute( 'normal', new BufferAttribute( normals, 3 ).copyVector3sArray( geometry.normals ) );
- }
- if ( geometry.colors.length > 0 ) {
- const colors = new Float32Array( geometry.colors.length * 3 );
- this.setAttribute( 'color', new BufferAttribute( colors, 3 ).copyColorsArray( geometry.colors ) );
- }
- if ( geometry.uvs.length > 0 ) {
- const uvs = new Float32Array( geometry.uvs.length * 2 );
- this.setAttribute( 'uv', new BufferAttribute( uvs, 2 ).copyVector2sArray( geometry.uvs ) );
- }
- if ( geometry.uvs2.length > 0 ) {
- const uvs2 = new Float32Array( geometry.uvs2.length * 2 );
- this.setAttribute( 'uv2', new BufferAttribute( uvs2, 2 ).copyVector2sArray( geometry.uvs2 ) );
- }
- // groups
- this.groups = geometry.groups;
- // morphs
- for ( const name in geometry.morphTargets ) {
- const array = [];
- const morphTargets = geometry.morphTargets[ name ];
- for ( let i = 0, l = morphTargets.length; i < l; i ++ ) {
- const morphTarget = morphTargets[ i ];
- const attribute = new Float32BufferAttribute( morphTarget.data.length * 3, 3 );
- attribute.name = morphTarget.name;
- array.push( attribute.copyVector3sArray( morphTarget.data ) );
- }
- this.morphAttributes[ name ] = array;
- }
- // skinning
- if ( geometry.skinIndices.length > 0 ) {
- const skinIndices = new Float32BufferAttribute( geometry.skinIndices.length * 4, 4 );
- this.setAttribute( 'skinIndex', skinIndices.copyVector4sArray( geometry.skinIndices ) );
- }
- if ( geometry.skinWeights.length > 0 ) {
- const skinWeights = new Float32BufferAttribute( geometry.skinWeights.length * 4, 4 );
- this.setAttribute( 'skinWeight', skinWeights.copyVector4sArray( geometry.skinWeights ) );
- }
- //
- if ( geometry.boundingSphere !== null ) {
- this.boundingSphere = geometry.boundingSphere.clone();
- }
- if ( geometry.boundingBox !== null ) {
- this.boundingBox = geometry.boundingBox.clone();
- }
- return this;
- },
- computeBoundingBox: function () {
- if ( this.boundingBox === null ) {
- this.boundingBox = new Box3();
- }
- const position = this.attributes.position;
- const morphAttributesPosition = this.morphAttributes.position;
- if ( position && position.isGLBufferAttribute ) {
- console.error( 'THREE.BufferGeometry.computeBoundingBox(): GLBufferAttribute requires a manual bounding box. Alternatively set "mesh.frustumCulled" to "false".', this );
- this.boundingBox.set(
- new Vector3( - Infinity, - Infinity, - Infinity ),
- new Vector3( + Infinity, + Infinity, + Infinity )
- );
- return;
- }
- if ( position !== undefined ) {
- this.boundingBox.setFromBufferAttribute( position );
- // process morph attributes if present
- if ( morphAttributesPosition ) {
- for ( let i = 0, il = morphAttributesPosition.length; i < il; i ++ ) {
- const morphAttribute = morphAttributesPosition[ i ];
- _box$2.setFromBufferAttribute( morphAttribute );
- if ( this.morphTargetsRelative ) {
- _vector$4.addVectors( this.boundingBox.min, _box$2.min );
- this.boundingBox.expandByPoint( _vector$4 );
- _vector$4.addVectors( this.boundingBox.max, _box$2.max );
- this.boundingBox.expandByPoint( _vector$4 );
- } else {
- this.boundingBox.expandByPoint( _box$2.min );
- this.boundingBox.expandByPoint( _box$2.max );
- }
- }
- }
- } else {
- this.boundingBox.makeEmpty();
- }
- if ( isNaN( this.boundingBox.min.x ) || isNaN( this.boundingBox.min.y ) || isNaN( this.boundingBox.min.z ) ) {
- console.error( 'THREE.BufferGeometry.computeBoundingBox(): Computed min/max have NaN values. The "position" attribute is likely to have NaN values.', this );
- }
- },
- computeBoundingSphere: function () {
- if ( this.boundingSphere === null ) {
- this.boundingSphere = new Sphere();
- }
- const position = this.attributes.position;
- const morphAttributesPosition = this.morphAttributes.position;
- if ( position && position.isGLBufferAttribute ) {
- console.error( 'THREE.BufferGeometry.computeBoundingSphere(): GLBufferAttribute requires a manual bounding sphere. Alternatively set "mesh.frustumCulled" to "false".', this );
- this.boundingSphere.set( new Vector3(), Infinity );
- return;
- }
- if ( position ) {
- // first, find the center of the bounding sphere
- const center = this.boundingSphere.center;
- _box$2.setFromBufferAttribute( position );
- // process morph attributes if present
- if ( morphAttributesPosition ) {
- for ( let i = 0, il = morphAttributesPosition.length; i < il; i ++ ) {
- const morphAttribute = morphAttributesPosition[ i ];
- _boxMorphTargets.setFromBufferAttribute( morphAttribute );
- if ( this.morphTargetsRelative ) {
- _vector$4.addVectors( _box$2.min, _boxMorphTargets.min );
- _box$2.expandByPoint( _vector$4 );
- _vector$4.addVectors( _box$2.max, _boxMorphTargets.max );
- _box$2.expandByPoint( _vector$4 );
- } else {
- _box$2.expandByPoint( _boxMorphTargets.min );
- _box$2.expandByPoint( _boxMorphTargets.max );
- }
- }
- }
- _box$2.getCenter( center );
- // second, try to find a boundingSphere with a radius smaller than the
- // boundingSphere of the boundingBox: sqrt(3) smaller in the best case
- let maxRadiusSq = 0;
- for ( let i = 0, il = position.count; i < il; i ++ ) {
- _vector$4.fromBufferAttribute( position, i );
- maxRadiusSq = Math.max( maxRadiusSq, center.distanceToSquared( _vector$4 ) );
- }
- // process morph attributes if present
- if ( morphAttributesPosition ) {
- for ( let i = 0, il = morphAttributesPosition.length; i < il; i ++ ) {
- const morphAttribute = morphAttributesPosition[ i ];
- const morphTargetsRelative = this.morphTargetsRelative;
- for ( let j = 0, jl = morphAttribute.count; j < jl; j ++ ) {
- _vector$4.fromBufferAttribute( morphAttribute, j );
- if ( morphTargetsRelative ) {
- _offset.fromBufferAttribute( position, j );
- _vector$4.add( _offset );
- }
- maxRadiusSq = Math.max( maxRadiusSq, center.distanceToSquared( _vector$4 ) );
- }
- }
- }
- this.boundingSphere.radius = Math.sqrt( maxRadiusSq );
- if ( isNaN( this.boundingSphere.radius ) ) {
- console.error( 'THREE.BufferGeometry.computeBoundingSphere(): Computed radius is NaN. The "position" attribute is likely to have NaN values.', this );
- }
- }
- },
- computeFaceNormals: function () {
- // backwards compatibility
- },
- computeVertexNormals: function () {
- const index = this.index;
- const positionAttribute = this.getAttribute( 'position' );
- if ( positionAttribute !== undefined ) {
- let normalAttribute = this.getAttribute( 'normal' );
- if ( normalAttribute === undefined ) {
- normalAttribute = new BufferAttribute( new Float32Array( positionAttribute.count * 3 ), 3 );
- this.setAttribute( 'normal', normalAttribute );
- } else {
- // reset existing normals to zero
- for ( let i = 0, il = normalAttribute.count; i < il; i ++ ) {
- normalAttribute.setXYZ( i, 0, 0, 0 );
- }
- }
- const pA = new Vector3(), pB = new Vector3(), pC = new Vector3();
- const nA = new Vector3(), nB = new Vector3(), nC = new Vector3();
- const cb = new Vector3(), ab = new Vector3();
- // indexed elements
- if ( index ) {
- for ( let i = 0, il = index.count; i < il; i += 3 ) {
- const vA = index.getX( i + 0 );
- const vB = index.getX( i + 1 );
- const vC = index.getX( i + 2 );
- pA.fromBufferAttribute( positionAttribute, vA );
- pB.fromBufferAttribute( positionAttribute, vB );
- pC.fromBufferAttribute( positionAttribute, vC );
- cb.subVectors( pC, pB );
- ab.subVectors( pA, pB );
- cb.cross( ab );
- nA.fromBufferAttribute( normalAttribute, vA );
- nB.fromBufferAttribute( normalAttribute, vB );
- nC.fromBufferAttribute( normalAttribute, vC );
- nA.add( cb );
- nB.add( cb );
- nC.add( cb );
- normalAttribute.setXYZ( vA, nA.x, nA.y, nA.z );
- normalAttribute.setXYZ( vB, nB.x, nB.y, nB.z );
- normalAttribute.setXYZ( vC, nC.x, nC.y, nC.z );
- }
- } else {
- // non-indexed elements (unconnected triangle soup)
- for ( let i = 0, il = positionAttribute.count; i < il; i += 3 ) {
- pA.fromBufferAttribute( positionAttribute, i + 0 );
- pB.fromBufferAttribute( positionAttribute, i + 1 );
- pC.fromBufferAttribute( positionAttribute, i + 2 );
- cb.subVectors( pC, pB );
- ab.subVectors( pA, pB );
- cb.cross( ab );
- normalAttribute.setXYZ( i + 0, cb.x, cb.y, cb.z );
- normalAttribute.setXYZ( i + 1, cb.x, cb.y, cb.z );
- normalAttribute.setXYZ( i + 2, cb.x, cb.y, cb.z );
- }
- }
- this.normalizeNormals();
- normalAttribute.needsUpdate = true;
- }
- },
- merge: function ( geometry, offset ) {
- if ( ! ( geometry && geometry.isBufferGeometry ) ) {
- console.error( 'THREE.BufferGeometry.merge(): geometry not an instance of THREE.BufferGeometry.', geometry );
- return;
- }
- if ( offset === undefined ) {
- offset = 0;
- console.warn(
- 'THREE.BufferGeometry.merge(): Overwriting original geometry, starting at offset=0. '
- + 'Use BufferGeometryUtils.mergeBufferGeometries() for lossless merge.'
- );
- }
- const attributes = this.attributes;
- for ( const key in attributes ) {
- if ( geometry.attributes[ key ] === undefined ) continue;
- const attribute1 = attributes[ key ];
- const attributeArray1 = attribute1.array;
- const attribute2 = geometry.attributes[ key ];
- const attributeArray2 = attribute2.array;
- const attributeOffset = attribute2.itemSize * offset;
- const length = Math.min( attributeArray2.length, attributeArray1.length - attributeOffset );
- for ( let i = 0, j = attributeOffset; i < length; i ++, j ++ ) {
- attributeArray1[ j ] = attributeArray2[ i ];
- }
- }
- return this;
- },
- normalizeNormals: function () {
- const normals = this.attributes.normal;
- for ( let i = 0, il = normals.count; i < il; i ++ ) {
- _vector$4.fromBufferAttribute( normals, i );
- _vector$4.normalize();
- normals.setXYZ( i, _vector$4.x, _vector$4.y, _vector$4.z );
- }
- },
- toNonIndexed: function () {
- function convertBufferAttribute( attribute, indices ) {
- const array = attribute.array;
- const itemSize = attribute.itemSize;
- const normalized = attribute.normalized;
- const array2 = new array.constructor( indices.length * itemSize );
- let index = 0, index2 = 0;
- for ( let i = 0, l = indices.length; i < l; i ++ ) {
- index = indices[ i ] * itemSize;
- for ( let j = 0; j < itemSize; j ++ ) {
- array2[ index2 ++ ] = array[ index ++ ];
- }
- }
- return new BufferAttribute( array2, itemSize, normalized );
- }
- //
- if ( this.index === null ) {
- console.warn( 'THREE.BufferGeometry.toNonIndexed(): Geometry is already non-indexed.' );
- return this;
- }
- const geometry2 = new BufferGeometry();
- const indices = this.index.array;
- const attributes = this.attributes;
- // attributes
- for ( const name in attributes ) {
- const attribute = attributes[ name ];
- const newAttribute = convertBufferAttribute( attribute, indices );
- geometry2.setAttribute( name, newAttribute );
- }
- // morph attributes
- const morphAttributes = this.morphAttributes;
- for ( const name in morphAttributes ) {
- const morphArray = [];
- const morphAttribute = morphAttributes[ name ]; // morphAttribute: array of Float32BufferAttributes
- for ( let i = 0, il = morphAttribute.length; i < il; i ++ ) {
- const attribute = morphAttribute[ i ];
- const newAttribute = convertBufferAttribute( attribute, indices );
- morphArray.push( newAttribute );
- }
- geometry2.morphAttributes[ name ] = morphArray;
- }
- geometry2.morphTargetsRelative = this.morphTargetsRelative;
- // groups
- const groups = this.groups;
- for ( let i = 0, l = groups.length; i < l; i ++ ) {
- const group = groups[ i ];
- geometry2.addGroup( group.start, group.count, group.materialIndex );
- }
- return geometry2;
- },
- toJSON: function () {
- const data = {
- metadata: {
- version: 4.5,
- type: 'BufferGeometry',
- generator: 'BufferGeometry.toJSON'
- }
- };
- // standard BufferGeometry serialization
- data.uuid = this.uuid;
- data.type = this.type;
- if ( this.name !== '' ) data.name = this.name;
- if ( Object.keys( this.userData ).length > 0 ) data.userData = this.userData;
- if ( this.parameters !== undefined ) {
- const parameters = this.parameters;
- for ( const key in parameters ) {
- if ( parameters[ key ] !== undefined ) data[ key ] = parameters[ key ];
- }
- return data;
- }
- data.data = { attributes: {} };
- const index = this.index;
- if ( index !== null ) {
- data.data.index = {
- type: index.array.constructor.name,
- array: Array.prototype.slice.call( index.array )
- };
- }
- const attributes = this.attributes;
- for ( const key in attributes ) {
- const attribute = attributes[ key ];
- const attributeData = attribute.toJSON( data.data );
- if ( attribute.name !== '' ) attributeData.name = attribute.name;
- data.data.attributes[ key ] = attributeData;
- }
- const morphAttributes = {};
- let hasMorphAttributes = false;
- for ( const key in this.morphAttributes ) {
- const attributeArray = this.morphAttributes[ key ];
- const array = [];
- for ( let i = 0, il = attributeArray.length; i < il; i ++ ) {
- const attribute = attributeArray[ i ];
- const attributeData = attribute.toJSON( data.data );
- if ( attribute.name !== '' ) attributeData.name = attribute.name;
- array.push( attributeData );
- }
- if ( array.length > 0 ) {
- morphAttributes[ key ] = array;
- hasMorphAttributes = true;
- }
- }
- if ( hasMorphAttributes ) {
- data.data.morphAttributes = morphAttributes;
- data.data.morphTargetsRelative = this.morphTargetsRelative;
- }
- const groups = this.groups;
- if ( groups.length > 0 ) {
- data.data.groups = JSON.parse( JSON.stringify( groups ) );
- }
- const boundingSphere = this.boundingSphere;
- if ( boundingSphere !== null ) {
- data.data.boundingSphere = {
- center: boundingSphere.center.toArray(),
- radius: boundingSphere.radius
- };
- }
- return data;
- },
- clone: function () {
- /*
- // Handle primitives
- const parameters = this.parameters;
- if ( parameters !== undefined ) {
- const values = [];
- for ( const key in parameters ) {
- values.push( parameters[ key ] );
- }
- const geometry = Object.create( this.constructor.prototype );
- this.constructor.apply( geometry, values );
- return geometry;
- }
- return new this.constructor().copy( this );
- */
- return new BufferGeometry().copy( this );
- },
- copy: function ( source ) {
- // reset
- this.index = null;
- this.attributes = {};
- this.morphAttributes = {};
- this.groups = [];
- this.boundingBox = null;
- this.boundingSphere = null;
- // used for storing cloned, shared data
- const data = {};
- // name
- this.name = source.name;
- // index
- const index = source.index;
- if ( index !== null ) {
- this.setIndex( index.clone( data ) );
- }
- // attributes
- const attributes = source.attributes;
- for ( const name in attributes ) {
- const attribute = attributes[ name ];
- this.setAttribute( name, attribute.clone( data ) );
- }
- // morph attributes
- const morphAttributes = source.morphAttributes;
- for ( const name in morphAttributes ) {
- const array = [];
- const morphAttribute = morphAttributes[ name ]; // morphAttribute: array of Float32BufferAttributes
- for ( let i = 0, l = morphAttribute.length; i < l; i ++ ) {
- array.push( morphAttribute[ i ].clone( data ) );
- }
- this.morphAttributes[ name ] = array;
- }
- this.morphTargetsRelative = source.morphTargetsRelative;
- // groups
- const groups = source.groups;
- for ( let i = 0, l = groups.length; i < l; i ++ ) {
- const group = groups[ i ];
- this.addGroup( group.start, group.count, group.materialIndex );
- }
- // bounding box
- const boundingBox = source.boundingBox;
- if ( boundingBox !== null ) {
- this.boundingBox = boundingBox.clone();
- }
- // bounding sphere
- const boundingSphere = source.boundingSphere;
- if ( boundingSphere !== null ) {
- this.boundingSphere = boundingSphere.clone();
- }
- // draw range
- this.drawRange.start = source.drawRange.start;
- this.drawRange.count = source.drawRange.count;
- // user data
- this.userData = source.userData;
- return this;
- },
- dispose: function () {
- this.dispatchEvent( { type: 'dispose' } );
- }
- } );
- const _inverseMatrix = new Matrix4();
- const _ray = new Ray();
- const _sphere = new Sphere();
- const _vA = new Vector3();
- const _vB = new Vector3();
- const _vC = new Vector3();
- const _tempA = new Vector3();
- const _tempB = new Vector3();
- const _tempC = new Vector3();
- const _morphA = new Vector3();
- const _morphB = new Vector3();
- const _morphC = new Vector3();
- const _uvA = new Vector2$1();
- const _uvB = new Vector2$1();
- const _uvC = new Vector2$1();
- const _intersectionPoint = new Vector3();
- const _intersectionPointWorld = new Vector3();
- function Mesh( geometry = new BufferGeometry(), material = new MeshBasicMaterial() ) {
- Object3D.call( this );
- this.type = 'Mesh';
- this.geometry = geometry;
- this.material = material;
- this.updateMorphTargets();
- }
- Mesh.prototype = Object.assign( Object.create( Object3D.prototype ), {
- constructor: Mesh,
- isMesh: true,
- copy: function ( source ) {
- Object3D.prototype.copy.call( this, source );
- if ( source.morphTargetInfluences !== undefined ) {
- this.morphTargetInfluences = source.morphTargetInfluences.slice();
- }
- if ( source.morphTargetDictionary !== undefined ) {
- this.morphTargetDictionary = Object.assign( {}, source.morphTargetDictionary );
- }
- this.material = source.material;
- this.geometry = source.geometry;
- return this;
- },
- updateMorphTargets: function () {
- const geometry = this.geometry;
- if ( geometry.isBufferGeometry ) {
- const morphAttributes = geometry.morphAttributes;
- const keys = Object.keys( morphAttributes );
- if ( keys.length > 0 ) {
- const morphAttribute = morphAttributes[ keys[ 0 ] ];
- if ( morphAttribute !== undefined ) {
- this.morphTargetInfluences = [];
- this.morphTargetDictionary = {};
- for ( let m = 0, ml = morphAttribute.length; m < ml; m ++ ) {
- const name = morphAttribute[ m ].name || String( m );
- this.morphTargetInfluences.push( 0 );
- this.morphTargetDictionary[ name ] = m;
- }
- }
- }
- } else {
- const morphTargets = geometry.morphTargets;
- if ( morphTargets !== undefined && morphTargets.length > 0 ) {
- console.error( 'THREE.Mesh.updateMorphTargets() no longer supports THREE.Geometry. Use THREE.BufferGeometry instead.' );
- }
- }
- },
- raycast: function ( raycaster, intersects ) {
- const geometry = this.geometry;
- const material = this.material;
- const matrixWorld = this.matrixWorld;
- if ( material === undefined ) return;
- // Checking boundingSphere distance to ray
- if ( geometry.boundingSphere === null ) geometry.computeBoundingSphere();
- _sphere.copy( geometry.boundingSphere );
- _sphere.applyMatrix4( matrixWorld );
- if ( raycaster.ray.intersectsSphere( _sphere ) === false ) return;
- //
- _inverseMatrix.copy( matrixWorld ).invert();
- _ray.copy( raycaster.ray ).applyMatrix4( _inverseMatrix );
- // Check boundingBox before continuing
- if ( geometry.boundingBox !== null ) {
- if ( _ray.intersectsBox( geometry.boundingBox ) === false ) return;
- }
- let intersection;
- if ( geometry.isBufferGeometry ) {
- const index = geometry.index;
- const position = geometry.attributes.position;
- const morphPosition = geometry.morphAttributes.position;
- const morphTargetsRelative = geometry.morphTargetsRelative;
- const uv = geometry.attributes.uv;
- const uv2 = geometry.attributes.uv2;
- const groups = geometry.groups;
- const drawRange = geometry.drawRange;
- if ( index !== null ) {
- // indexed buffer geometry
- if ( Array.isArray( material ) ) {
- for ( let i = 0, il = groups.length; i < il; i ++ ) {
- const group = groups[ i ];
- const groupMaterial = material[ group.materialIndex ];
- const start = Math.max( group.start, drawRange.start );
- const end = Math.min( ( group.start + group.count ), ( drawRange.start + drawRange.count ) );
- for ( let j = start, jl = end; j < jl; j += 3 ) {
- const a = index.getX( j );
- const b = index.getX( j + 1 );
- const c = index.getX( j + 2 );
- intersection = checkBufferGeometryIntersection( this, groupMaterial, raycaster, _ray, position, morphPosition, morphTargetsRelative, uv, uv2, a, b, c );
- if ( intersection ) {
- intersection.faceIndex = Math.floor( j / 3 ); // triangle number in indexed buffer semantics
- intersection.face.materialIndex = group.materialIndex;
- intersects.push( intersection );
- }
- }
- }
- } else {
- const start = Math.max( 0, drawRange.start );
- const end = Math.min( index.count, ( drawRange.start + drawRange.count ) );
- for ( let i = start, il = end; i < il; i += 3 ) {
- const a = index.getX( i );
- const b = index.getX( i + 1 );
- const c = index.getX( i + 2 );
- intersection = checkBufferGeometryIntersection( this, material, raycaster, _ray, position, morphPosition, morphTargetsRelative, uv, uv2, a, b, c );
- if ( intersection ) {
- intersection.faceIndex = Math.floor( i / 3 ); // triangle number in indexed buffer semantics
- intersects.push( intersection );
- }
- }
- }
- } else if ( position !== undefined ) {
- // non-indexed buffer geometry
- if ( Array.isArray( material ) ) {
- for ( let i = 0, il = groups.length; i < il; i ++ ) {
- const group = groups[ i ];
- const groupMaterial = material[ group.materialIndex ];
- const start = Math.max( group.start, drawRange.start );
- const end = Math.min( ( group.start + group.count ), ( drawRange.start + drawRange.count ) );
- for ( let j = start, jl = end; j < jl; j += 3 ) {
- const a = j;
- const b = j + 1;
- const c = j + 2;
- intersection = checkBufferGeometryIntersection( this, groupMaterial, raycaster, _ray, position, morphPosition, morphTargetsRelative, uv, uv2, a, b, c );
- if ( intersection ) {
- intersection.faceIndex = Math.floor( j / 3 ); // triangle number in non-indexed buffer semantics
- intersection.face.materialIndex = group.materialIndex;
- intersects.push( intersection );
- }
- }
- }
- } else {
- const start = Math.max( 0, drawRange.start );
- const end = Math.min( position.count, ( drawRange.start + drawRange.count ) );
- for ( let i = start, il = end; i < il; i += 3 ) {
- const a = i;
- const b = i + 1;
- const c = i + 2;
- intersection = checkBufferGeometryIntersection( this, material, raycaster, _ray, position, morphPosition, morphTargetsRelative, uv, uv2, a, b, c );
- if ( intersection ) {
- intersection.faceIndex = Math.floor( i / 3 ); // triangle number in non-indexed buffer semantics
- intersects.push( intersection );
- }
- }
- }
- }
- } else if ( geometry.isGeometry ) {
- const isMultiMaterial = Array.isArray( material );
- const vertices = geometry.vertices;
- const faces = geometry.faces;
- let uvs;
- const faceVertexUvs = geometry.faceVertexUvs[ 0 ];
- if ( faceVertexUvs.length > 0 ) uvs = faceVertexUvs;
- for ( let f = 0, fl = faces.length; f < fl; f ++ ) {
- const face = faces[ f ];
- const faceMaterial = isMultiMaterial ? material[ face.materialIndex ] : material;
- if ( faceMaterial === undefined ) continue;
- const fvA = vertices[ face.a ];
- const fvB = vertices[ face.b ];
- const fvC = vertices[ face.c ];
- intersection = checkIntersection( this, faceMaterial, raycaster, _ray, fvA, fvB, fvC, _intersectionPoint );
- if ( intersection ) {
- if ( uvs && uvs[ f ] ) {
- const uvs_f = uvs[ f ];
- _uvA.copy( uvs_f[ 0 ] );
- _uvB.copy( uvs_f[ 1 ] );
- _uvC.copy( uvs_f[ 2 ] );
- intersection.uv = Triangle.getUV( _intersectionPoint, fvA, fvB, fvC, _uvA, _uvB, _uvC, new Vector2$1() );
- }
- intersection.face = face;
- intersection.faceIndex = f;
- intersects.push( intersection );
- }
- }
- }
- }
- } );
- function checkIntersection( object, material, raycaster, ray, pA, pB, pC, point ) {
- let intersect;
- if ( material.side === BackSide ) {
- intersect = ray.intersectTriangle( pC, pB, pA, true, point );
- } else {
- intersect = ray.intersectTriangle( pA, pB, pC, material.side !== DoubleSide, point );
- }
- if ( intersect === null ) return null;
- _intersectionPointWorld.copy( point );
- _intersectionPointWorld.applyMatrix4( object.matrixWorld );
- const distance = raycaster.ray.origin.distanceTo( _intersectionPointWorld );
- if ( distance < raycaster.near || distance > raycaster.far ) return null;
- return {
- distance: distance,
- point: _intersectionPointWorld.clone(),
- object: object
- };
- }
- function checkBufferGeometryIntersection( object, material, raycaster, ray, position, morphPosition, morphTargetsRelative, uv, uv2, a, b, c ) {
- _vA.fromBufferAttribute( position, a );
- _vB.fromBufferAttribute( position, b );
- _vC.fromBufferAttribute( position, c );
- const morphInfluences = object.morphTargetInfluences;
- if ( material.morphTargets && morphPosition && morphInfluences ) {
- _morphA.set( 0, 0, 0 );
- _morphB.set( 0, 0, 0 );
- _morphC.set( 0, 0, 0 );
- for ( let i = 0, il = morphPosition.length; i < il; i ++ ) {
- const influence = morphInfluences[ i ];
- const morphAttribute = morphPosition[ i ];
- if ( influence === 0 ) continue;
- _tempA.fromBufferAttribute( morphAttribute, a );
- _tempB.fromBufferAttribute( morphAttribute, b );
- _tempC.fromBufferAttribute( morphAttribute, c );
- if ( morphTargetsRelative ) {
- _morphA.addScaledVector( _tempA, influence );
- _morphB.addScaledVector( _tempB, influence );
- _morphC.addScaledVector( _tempC, influence );
- } else {
- _morphA.addScaledVector( _tempA.sub( _vA ), influence );
- _morphB.addScaledVector( _tempB.sub( _vB ), influence );
- _morphC.addScaledVector( _tempC.sub( _vC ), influence );
- }
- }
- _vA.add( _morphA );
- _vB.add( _morphB );
- _vC.add( _morphC );
- }
- if ( object.isSkinnedMesh ) {
- object.boneTransform( a, _vA );
- object.boneTransform( b, _vB );
- object.boneTransform( c, _vC );
- }
- const intersection = checkIntersection( object, material, raycaster, ray, _vA, _vB, _vC, _intersectionPoint );
- if ( intersection ) {
- if ( uv ) {
- _uvA.fromBufferAttribute( uv, a );
- _uvB.fromBufferAttribute( uv, b );
- _uvC.fromBufferAttribute( uv, c );
- intersection.uv = Triangle.getUV( _intersectionPoint, _vA, _vB, _vC, _uvA, _uvB, _uvC, new Vector2$1() );
- }
- if ( uv2 ) {
- _uvA.fromBufferAttribute( uv2, a );
- _uvB.fromBufferAttribute( uv2, b );
- _uvC.fromBufferAttribute( uv2, c );
- intersection.uv2 = Triangle.getUV( _intersectionPoint, _vA, _vB, _vC, _uvA, _uvB, _uvC, new Vector2$1() );
- }
- const face = new Face3( a, b, c );
- Triangle.getNormal( _vA, _vB, _vC, face.normal );
- intersection.face = face;
- }
- return intersection;
- }
- class BoxBufferGeometry extends BufferGeometry {
- constructor( width = 1, height = 1, depth = 1, widthSegments = 1, heightSegments = 1, depthSegments = 1 ) {
- super();
- this.type = 'BoxBufferGeometry';
- this.parameters = {
- width: width,
- height: height,
- depth: depth,
- widthSegments: widthSegments,
- heightSegments: heightSegments,
- depthSegments: depthSegments
- };
- const scope = this;
- // segments
- widthSegments = Math.floor( widthSegments );
- heightSegments = Math.floor( heightSegments );
- depthSegments = Math.floor( depthSegments );
- // buffers
- const indices = [];
- const vertices = [];
- const normals = [];
- const uvs = [];
- // helper variables
- let numberOfVertices = 0;
- let groupStart = 0;
- // build each side of the box geometry
- buildPlane( 'z', 'y', 'x', - 1, - 1, depth, height, width, depthSegments, heightSegments, 0 ); // px
- buildPlane( 'z', 'y', 'x', 1, - 1, depth, height, - width, depthSegments, heightSegments, 1 ); // nx
- buildPlane( 'x', 'z', 'y', 1, 1, width, depth, height, widthSegments, depthSegments, 2 ); // py
- buildPlane( 'x', 'z', 'y', 1, - 1, width, depth, - height, widthSegments, depthSegments, 3 ); // ny
- buildPlane( 'x', 'y', 'z', 1, - 1, width, height, depth, widthSegments, heightSegments, 4 ); // pz
- buildPlane( 'x', 'y', 'z', - 1, - 1, width, height, - depth, widthSegments, heightSegments, 5 ); // nz
- // build geometry
- this.setIndex( indices );
- this.setAttribute( 'position', new Float32BufferAttribute( vertices, 3 ) );
- this.setAttribute( 'normal', new Float32BufferAttribute( normals, 3 ) );
- this.setAttribute( 'uv', new Float32BufferAttribute( uvs, 2 ) );
- function buildPlane( u, v, w, udir, vdir, width, height, depth, gridX, gridY, materialIndex ) {
- const segmentWidth = width / gridX;
- const segmentHeight = height / gridY;
- const widthHalf = width / 2;
- const heightHalf = height / 2;
- const depthHalf = depth / 2;
- const gridX1 = gridX + 1;
- const gridY1 = gridY + 1;
- let vertexCounter = 0;
- let groupCount = 0;
- const vector = new Vector3();
- // generate vertices, normals and uvs
- for ( let iy = 0; iy < gridY1; iy ++ ) {
- const y = iy * segmentHeight - heightHalf;
- for ( let ix = 0; ix < gridX1; ix ++ ) {
- const x = ix * segmentWidth - widthHalf;
- // set values to correct vector component
- vector[ u ] = x * udir;
- vector[ v ] = y * vdir;
- vector[ w ] = depthHalf;
- // now apply vector to vertex buffer
- vertices.push( vector.x, vector.y, vector.z );
- // set values to correct vector component
- vector[ u ] = 0;
- vector[ v ] = 0;
- vector[ w ] = depth > 0 ? 1 : - 1;
- // now apply vector to normal buffer
- normals.push( vector.x, vector.y, vector.z );
- // uvs
- uvs.push( ix / gridX );
- uvs.push( 1 - ( iy / gridY ) );
- // counters
- vertexCounter += 1;
- }
- }
- // indices
- // 1. you need three indices to draw a single face
- // 2. a single segment consists of two faces
- // 3. so we need to generate six (2*3) indices per segment
- for ( let iy = 0; iy < gridY; iy ++ ) {
- for ( let ix = 0; ix < gridX; ix ++ ) {
- const a = numberOfVertices + ix + gridX1 * iy;
- const b = numberOfVertices + ix + gridX1 * ( iy + 1 );
- const c = numberOfVertices + ( ix + 1 ) + gridX1 * ( iy + 1 );
- const d = numberOfVertices + ( ix + 1 ) + gridX1 * iy;
- // faces
- indices.push( a, b, d );
- indices.push( b, c, d );
- // increase counter
- groupCount += 6;
- }
- }
- // add a group to the geometry. this will ensure multi material support
- scope.addGroup( groupStart, groupCount, materialIndex );
- // calculate new start value for groups
- groupStart += groupCount;
- // update total number of vertices
- numberOfVertices += vertexCounter;
- }
- }
- }
- /**
- * Uniform Utilities
- */
- function cloneUniforms( src ) {
- const dst = {};
- for ( const u in src ) {
- dst[ u ] = {};
- for ( const p in src[ u ] ) {
- const property = src[ u ][ p ];
- if ( property && ( property.isColor ||
- property.isMatrix3 || property.isMatrix4 ||
- property.isVector2 || property.isVector3 || property.isVector4 ||
- property.isTexture ) ) {
- dst[ u ][ p ] = property.clone();
- } else if ( Array.isArray( property ) ) {
- dst[ u ][ p ] = property.slice();
- } else {
- dst[ u ][ p ] = property;
- }
- }
- }
- return dst;
- }
- function mergeUniforms( uniforms ) {
- const merged = {};
- for ( let u = 0; u < uniforms.length; u ++ ) {
- const tmp = cloneUniforms( uniforms[ u ] );
- for ( const p in tmp ) {
- merged[ p ] = tmp[ p ];
- }
- }
- return merged;
- }
- // Legacy
- const UniformsUtils = { clone: cloneUniforms, merge: mergeUniforms };
- var default_vertex = "void main() {\n\tgl_Position = projectionMatrix * modelViewMatrix * vec4( position, 1.0 );\n}";
- var default_fragment = "void main() {\n\tgl_FragColor = vec4( 1.0, 0.0, 0.0, 1.0 );\n}";
- /**
- * parameters = {
- * defines: { "label" : "value" },
- * uniforms: { "parameter1": { value: 1.0 }, "parameter2": { value2: 2 } },
- *
- * fragmentShader: <string>,
- * vertexShader: <string>,
- *
- * wireframe: <boolean>,
- * wireframelineWidth: <float>,
- *
- * lights: <bool>,
- *
- * skinning: <bool>,
- * morphTargets: <bool>,
- * morphNormals: <bool>
- * }
- */
- function ShaderMaterial( parameters ) {
- Material.call( this );
- this.type = 'ShaderMaterial';
- this.defines = {};
- this.uniforms = {};
- this.vertexShader = default_vertex;
- this.fragmentShader = default_fragment;
- this.lineWidth = 1;
- this.wireframe = false;
- this.wireframelineWidth = 1;
- this.fog = false; // set to use scene fog
- this.lights = false; // set to use scene lights
- this.clipping = false; // set to use user-defined clipping planes
- this.skinning = false; // set to use skinning attribute streams
- this.morphTargets = false; // set to use morph targets
- this.morphNormals = false; // set to use morph normals
- this.extensions = {
- derivatives: false, // set to use derivatives
- fragDepth: false, // set to use fragment depth values
- drawBuffers: false, // set to use draw buffers
- shaderTextureLOD: false // set to use shader texture LOD
- };
- // When rendered geometry doesn't include these attributes but the material does,
- // use these default values in WebGL. This avoids errors when buffer data is missing.
- this.defaultAttributeValues = {
- 'color': [ 1, 1, 1 ],
- 'uv': [ 0, 0 ],
- 'uv2': [ 0, 0 ]
- };
- this.index0AttributeName = undefined;
- this.uniformsNeedUpdate = false;
- this.glslVersion = null;
- if ( parameters !== undefined ) {
- if ( parameters.attributes !== undefined ) {
- console.error( 'THREE.ShaderMaterial: attributes should now be defined in THREE.BufferGeometry instead.' );
- }
- this.setValues( parameters );
- }
- }
- ShaderMaterial.prototype = Object.create( Material.prototype );
- ShaderMaterial.prototype.constructor = ShaderMaterial;
- ShaderMaterial.prototype.isShaderMaterial = true;
- ShaderMaterial.prototype.copy = function ( source ) {
- Material.prototype.copy.call( this, source );
- this.fragmentShader = source.fragmentShader;
- this.vertexShader = source.vertexShader;
- this.uniforms = cloneUniforms( source.uniforms );
- this.defines = Object.assign( {}, source.defines );
- this.wireframe = source.wireframe;
- this.wireframelineWidth = source.wireframelineWidth;
- this.lights = source.lights;
- this.clipping = source.clipping;
- this.skinning = source.skinning;
- this.morphTargets = source.morphTargets;
- this.morphNormals = source.morphNormals;
- this.extensions = Object.assign( {}, source.extensions );
- this.glslVersion = source.glslVersion;
- return this;
- };
- ShaderMaterial.prototype.toJSON = function ( meta ) {
- const data = Material.prototype.toJSON.call( this, meta );
- data.glslVersion = this.glslVersion;
- data.uniforms = {};
- for ( const name in this.uniforms ) {
- const uniform = this.uniforms[ name ];
- const value = uniform.value;
- if ( value && value.isTexture ) {
- data.uniforms[ name ] = {
- type: 't',
- value: value.toJSON( meta ).uuid
- };
- } else if ( value && value.isColor ) {
- data.uniforms[ name ] = {
- type: 'c',
- value: value.getHex()
- };
- } else if ( value && value.isVector2 ) {
- data.uniforms[ name ] = {
- type: 'v2',
- value: value.toArray()
- };
- } else if ( value && value.isVector3 ) {
- data.uniforms[ name ] = {
- type: 'v3',
- value: value.toArray()
- };
- } else if ( value && value.isVector4 ) {
- data.uniforms[ name ] = {
- type: 'v4',
- value: value.toArray()
- };
- } else if ( value && value.isMatrix3 ) {
- data.uniforms[ name ] = {
- type: 'm3',
- value: value.toArray()
- };
- } else if ( value && value.isMatrix4 ) {
- data.uniforms[ name ] = {
- type: 'm4',
- value: value.toArray()
- };
- } else {
- data.uniforms[ name ] = {
- value: value
- };
- // note: the array variants v2v, v3v, v4v, m4v and tv are not supported so far
- }
- }
- if ( Object.keys( this.defines ).length > 0 ) data.defines = this.defines;
- data.vertexShader = this.vertexShader;
- data.fragmentShader = this.fragmentShader;
- const extensions = {};
- for ( const key in this.extensions ) {
- if ( this.extensions[ key ] === true ) extensions[ key ] = true;
- }
- if ( Object.keys( extensions ).length > 0 ) data.extensions = extensions;
- return data;
- };
- function Camera() {
- Object3D.call( this );
- this.type = 'Camera';
- this.matrixWorldInverse = new Matrix4();
- this.projectionMatrix = new Matrix4();
- this.projectionMatrixInverse = new Matrix4();
- }
- Camera.prototype = Object.assign( Object.create( Object3D.prototype ), {
- constructor: Camera,
- isCamera: true,
- copy: function ( source, recursive ) {
- Object3D.prototype.copy.call( this, source, recursive );
- this.matrixWorldInverse.copy( source.matrixWorldInverse );
- this.projectionMatrix.copy( source.projectionMatrix );
- this.projectionMatrixInverse.copy( source.projectionMatrixInverse );
- return this;
- },
- getWorldDirection: function ( target ) {
- if ( target === undefined ) {
- console.warn( 'THREE.Camera: .getWorldDirection() target is now required' );
- target = new Vector3();
- }
- this.updateWorldMatrix( true, false );
- const e = this.matrixWorld.elements;
- return target.set( - e[ 8 ], - e[ 9 ], - e[ 10 ] ).normalize();
- },
- updateMatrixWorld: function ( force ) {
- Object3D.prototype.updateMatrixWorld.call( this, force );
- this.matrixWorldInverse.copy( this.matrixWorld ).invert();
- },
- updateWorldMatrix: function ( updateParents, updateChildren ) {
- Object3D.prototype.updateWorldMatrix.call( this, updateParents, updateChildren );
- this.matrixWorldInverse.copy( this.matrixWorld ).invert();
- },
- clone: function () {
- return new this.constructor().copy( this );
- }
- } );
- function PerspectiveCamera( fov = 50, aspect = 1, near = 0.1, far = 2000 ) {
- Camera.call( this );
- this.type = 'PerspectiveCamera';
- this.fov = fov;
- this.zoom = 1;
- this.near = near;
- this.far = far;
- this.focus = 10;
- this.aspect = aspect;
- this.view = null;
- this.filmGauge = 35; // width of the film (default in millimeters)
- this.filmOffset = 0; // horizontal film offset (same unit as gauge)
- this.updateProjectionMatrix();
- }
- PerspectiveCamera.prototype = Object.assign( Object.create( Camera.prototype ), {
- constructor: PerspectiveCamera,
- isPerspectiveCamera: true,
- copy: function ( source, recursive ) {
- Camera.prototype.copy.call( this, source, recursive );
- this.fov = source.fov;
- this.zoom = source.zoom;
- this.near = source.near;
- this.far = source.far;
- this.focus = source.focus;
- this.aspect = source.aspect;
- this.view = source.view === null ? null : Object.assign( {}, source.view );
- this.filmGauge = source.filmGauge;
- this.filmOffset = source.filmOffset;
- return this;
- },
- /**
- * Sets the FOV by focal length in respect to the current .filmGauge.
- *
- * The default film gauge is 35, so that the focal length can be specified for
- * a 35mm (full frame) camera.
- *
- * Values for focal length and film gauge must have the same unit.
- */
- setFocalLength: function ( focalLength ) {
- // see http://www.bobatkins.com/photography/technical/field_of_view.html
- const vExtentSlope = 0.5 * this.getFilmHeight() / focalLength;
- this.fov = MathUtils.RAD2DEG * 2 * Math.atan( vExtentSlope );
- this.updateProjectionMatrix();
- },
- /**
- * Calculates the focal length from the current .fov and .filmGauge.
- */
- getFocalLength: function () {
- const vExtentSlope = Math.tan( MathUtils.DEG2RAD * 0.5 * this.fov );
- return 0.5 * this.getFilmHeight() / vExtentSlope;
- },
- getEffectiveFOV: function () {
- return MathUtils.RAD2DEG * 2 * Math.atan(
- Math.tan( MathUtils.DEG2RAD * 0.5 * this.fov ) / this.zoom );
- },
- getFilmWidth: function () {
- // film not completely covered in portrait format (aspect < 1)
- return this.filmGauge * Math.min( this.aspect, 1 );
- },
- getFilmHeight: function () {
- // film not completely covered in landscape format (aspect > 1)
- return this.filmGauge / Math.max( this.aspect, 1 );
- },
- /**
- * Sets an offset in a larger frustum. This is useful for multi-window or
- * multi-monitor/multi-machine setups.
- *
- * For example, if you have 3x2 monitors and each monitor is 1920x1080 and
- * the monitors are in grid like this
- *
- * +---+---+---+
- * | A | B | C |
- * +---+---+---+
- * | D | E | F |
- * +---+---+---+
- *
- * then for each monitor you would call it like this
- *
- * const w = 1920;
- * const h = 1080;
- * const fullWidth = w * 3;
- * const fullHeight = h * 2;
- *
- * --A--
- * camera.setViewOffset( fullWidth, fullHeight, w * 0, h * 0, w, h );
- * --B--
- * camera.setViewOffset( fullWidth, fullHeight, w * 1, h * 0, w, h );
- * --C--
- * camera.setViewOffset( fullWidth, fullHeight, w * 2, h * 0, w, h );
- * --D--
- * camera.setViewOffset( fullWidth, fullHeight, w * 0, h * 1, w, h );
- * --E--
- * camera.setViewOffset( fullWidth, fullHeight, w * 1, h * 1, w, h );
- * --F--
- * camera.setViewOffset( fullWidth, fullHeight, w * 2, h * 1, w, h );
- *
- * Note there is no reason monitors have to be the same size or in a grid.
- */
- setViewOffset: function ( fullWidth, fullHeight, x, y, width, height ) {
- this.aspect = fullWidth / fullHeight;
- if ( this.view === null ) {
- this.view = {
- enabled: true,
- fullWidth: 1,
- fullHeight: 1,
- offsetX: 0,
- offsetY: 0,
- width: 1,
- height: 1
- };
- }
- this.view.enabled = true;
- this.view.fullWidth = fullWidth;
- this.view.fullHeight = fullHeight;
- this.view.offsetX = x;
- this.view.offsetY = y;
- this.view.width = width;
- this.view.height = height;
- this.updateProjectionMatrix();
- },
- clearViewOffset: function () {
- if ( this.view !== null ) {
- this.view.enabled = false;
- }
- this.updateProjectionMatrix();
- },
- updateProjectionMatrix: function () {
- const near = this.near;
- let top = near * Math.tan( MathUtils.DEG2RAD * 0.5 * this.fov ) / this.zoom;
- let height = 2 * top;
- let width = this.aspect * height;
- let left = - 0.5 * width;
- const view = this.view;
- if ( this.view !== null && this.view.enabled ) {
- const fullWidth = view.fullWidth,
- fullHeight = view.fullHeight;
- left += view.offsetX * width / fullWidth;
- top -= view.offsetY * height / fullHeight;
- width *= view.width / fullWidth;
- height *= view.height / fullHeight;
- }
- const skew = this.filmOffset;
- if ( skew !== 0 ) left += near * skew / this.getFilmWidth();
- this.projectionMatrix.makePerspective( left, left + width, top, top - height, near, this.far );
- this.projectionMatrixInverse.copy( this.projectionMatrix ).invert();
- },
- toJSON: function ( meta ) {
- const data = Object3D.prototype.toJSON.call( this, meta );
- data.object.fov = this.fov;
- data.object.zoom = this.zoom;
- data.object.near = this.near;
- data.object.far = this.far;
- data.object.focus = this.focus;
- data.object.aspect = this.aspect;
- if ( this.view !== null ) data.object.view = Object.assign( {}, this.view );
- data.object.filmGauge = this.filmGauge;
- data.object.filmOffset = this.filmOffset;
- return data;
- }
- } );
- const fov = 90, aspect = 1;
- function CubeCamera( near, far, renderTarget ) {
- Object3D.call( this );
- this.type = 'CubeCamera';
- if ( renderTarget.isWebGLCubeRenderTarget !== true ) {
- console.error( 'THREE.CubeCamera: The constructor now expects an instance of WebGLCubeRenderTarget as third parameter.' );
- return;
- }
- this.renderTarget = renderTarget;
- const cameraPX = new PerspectiveCamera( fov, aspect, near, far );
- cameraPX.layers = this.layers;
- cameraPX.up.set( 0, - 1, 0 );
- cameraPX.lookAt( new Vector3( 1, 0, 0 ) );
- this.add( cameraPX );
- const cameraNX = new PerspectiveCamera( fov, aspect, near, far );
- cameraNX.layers = this.layers;
- cameraNX.up.set( 0, - 1, 0 );
- cameraNX.lookAt( new Vector3( - 1, 0, 0 ) );
- this.add( cameraNX );
- const cameraPY = new PerspectiveCamera( fov, aspect, near, far );
- cameraPY.layers = this.layers;
- cameraPY.up.set( 0, 0, 1 );
- cameraPY.lookAt( new Vector3( 0, 1, 0 ) );
- this.add( cameraPY );
- const cameraNY = new PerspectiveCamera( fov, aspect, near, far );
- cameraNY.layers = this.layers;
- cameraNY.up.set( 0, 0, - 1 );
- cameraNY.lookAt( new Vector3( 0, - 1, 0 ) );
- this.add( cameraNY );
- const cameraPZ = new PerspectiveCamera( fov, aspect, near, far );
- cameraPZ.layers = this.layers;
- cameraPZ.up.set( 0, - 1, 0 );
- cameraPZ.lookAt( new Vector3( 0, 0, 1 ) );
- this.add( cameraPZ );
- const cameraNZ = new PerspectiveCamera( fov, aspect, near, far );
- cameraNZ.layers = this.layers;
- cameraNZ.up.set( 0, - 1, 0 );
- cameraNZ.lookAt( new Vector3( 0, 0, - 1 ) );
- this.add( cameraNZ );
- this.update = function ( renderer, scene ) {
- if ( this.parent === null ) this.updateMatrixWorld();
- const currentXrEnabled = renderer.xr.enabled;
- const currentRenderTarget = renderer.getRenderTarget();
- renderer.xr.enabled = false;
- const generateMipmaps = renderTarget.texture.generateMipmaps;
- renderTarget.texture.generateMipmaps = false;
- renderer.setRenderTarget( renderTarget, 0 );
- renderer.render( scene, cameraPX );
- renderer.setRenderTarget( renderTarget, 1 );
- renderer.render( scene, cameraNX );
- renderer.setRenderTarget( renderTarget, 2 );
- renderer.render( scene, cameraPY );
- renderer.setRenderTarget( renderTarget, 3 );
- renderer.render( scene, cameraNY );
- renderer.setRenderTarget( renderTarget, 4 );
- renderer.render( scene, cameraPZ );
- renderTarget.texture.generateMipmaps = generateMipmaps;
- renderer.setRenderTarget( renderTarget, 5 );
- renderer.render( scene, cameraNZ );
- renderer.setRenderTarget( currentRenderTarget );
- renderer.xr.enabled = currentXrEnabled;
- };
- }
- CubeCamera.prototype = Object.create( Object3D.prototype );
- CubeCamera.prototype.constructor = CubeCamera;
- function CubeTexture( images, mapping, wrapS, wrapT, magFilter, minFilter, format, type, anisotropy, encoding ) {
- images = images !== undefined ? images : [];
- mapping = mapping !== undefined ? mapping : CubeReflectionMapping;
- format = format !== undefined ? format : RGBFormat;
- Texture.call( this, images, mapping, wrapS, wrapT, magFilter, minFilter, format, type, anisotropy, encoding );
- this.flipY = false;
- // Why CubeTexture._needsFlipEnvMap is necessary:
- //
- // By convention -- likely based on the RenderMan spec from the 1990's -- cube maps are specified by WebGL (and three.js)
- // in a coordinate system in which positive-x is to the right when looking up the positive-z axis -- in other words,
- // in a left-handed coordinate system. By continuing this convention, preexisting cube maps continued to render correctly.
- // three.js uses a right-handed coordinate system. So environment maps used in three.js appear to have px and nx swapped
- // and the flag _needsFlipEnvMap controls this conversion. The flip is not required (and thus _needsFlipEnvMap is set to false)
- // when using WebGLCubeRenderTarget.texture as a cube texture.
- this._needsFlipEnvMap = true;
- }
- CubeTexture.prototype = Object.create( Texture.prototype );
- CubeTexture.prototype.constructor = CubeTexture;
- CubeTexture.prototype.isCubeTexture = true;
- Object.defineProperty( CubeTexture.prototype, 'images', {
- get: function () {
- return this.image;
- },
- set: function ( value ) {
- this.image = value;
- }
- } );
- function WebGLCubeRenderTarget( size, options, dummy ) {
- if ( Number.isInteger( options ) ) {
- console.warn( 'THREE.WebGLCubeRenderTarget: constructor signature is now WebGLCubeRenderTarget( size, options )' );
- options = dummy;
- }
- WebGLRenderTarget.call( this, size, size, options );
- options = options || {};
- this.texture = new CubeTexture( undefined, options.mapping, options.wrapS, options.wrapT, options.magFilter, options.minFilter, options.format, options.type, options.anisotropy, options.encoding );
- this.texture._needsFlipEnvMap = false;
- }
- WebGLCubeRenderTarget.prototype = Object.create( WebGLRenderTarget.prototype );
- WebGLCubeRenderTarget.prototype.constructor = WebGLCubeRenderTarget;
- WebGLCubeRenderTarget.prototype.isWebGLCubeRenderTarget = true;
- WebGLCubeRenderTarget.prototype.fromEquirectangularTexture = function ( renderer, texture ) {
- this.texture.type = texture.type;
- this.texture.format = RGBAFormat; // see #18859
- this.texture.encoding = texture.encoding;
- this.texture.generateMipmaps = texture.generateMipmaps;
- this.texture.minFilter = texture.minFilter;
- this.texture.magFilter = texture.magFilter;
- const shader = {
- uniforms: {
- tEquirect: { value: null },
- },
- vertexShader: /* glsl */`
- varying vec3 vWorldDirection;
- vec3 transformDirection( in vec3 dir, in mat4 matrix ) {
- return normalize( ( matrix * vec4( dir, 0.0 ) ).xyz );
- }
- void main() {
- vWorldDirection = transformDirection( position, modelMatrix );
- #include <begin_vertex>
- #include <project_vertex>
- }
- `,
- fragmentShader: /* glsl */`
- uniform sampler2D tEquirect;
- varying vec3 vWorldDirection;
- #include <common>
- void main() {
- vec3 direction = normalize( vWorldDirection );
- vec2 sampleUV = equirectUv( direction );
- gl_FragColor = texture2D( tEquirect, sampleUV );
- }
- `
- };
- const geometry = new BoxBufferGeometry( 5, 5, 5 );
- const material = new ShaderMaterial( {
- name: 'CubemapFromEquirect',
- uniforms: cloneUniforms( shader.uniforms ),
- vertexShader: shader.vertexShader,
- fragmentShader: shader.fragmentShader,
- side: BackSide,
- blending: NoBlending
- } );
- material.uniforms.tEquirect.value = texture;
- const mesh = new Mesh( geometry, material );
- const currentMinFilter = texture.minFilter;
- // Avoid blurred poles
- if ( texture.minFilter === LinearMipmapLinearFilter ) texture.minFilter = LinearFilter;
- const camera = new CubeCamera( 1, 10, this );
- camera.update( renderer, mesh );
- texture.minFilter = currentMinFilter;
- mesh.geometry.dispose();
- mesh.material.dispose();
- return this;
- };
- WebGLCubeRenderTarget.prototype.clear = function ( renderer, color, depth, stencil ) {
- const currentRenderTarget = renderer.getRenderTarget();
- for ( let i = 0; i < 6; i ++ ) {
- renderer.setRenderTarget( this, i );
- renderer.clear( color, depth, stencil );
- }
- renderer.setRenderTarget( currentRenderTarget );
- };
- function DataTexture( data, width, height, format, type, mapping, wrapS, wrapT, magFilter, minFilter, anisotropy, encoding ) {
- Texture.call( this, null, mapping, wrapS, wrapT, magFilter, minFilter, format, type, anisotropy, encoding );
- this.image = { data: data || null, width: width || 1, height: height || 1 };
- this.magFilter = magFilter !== undefined ? magFilter : NearestFilter;
- this.minFilter = minFilter !== undefined ? minFilter : NearestFilter;
- this.generateMipmaps = false;
- this.flipY = false;
- this.unpackAlignment = 1;
- this.needsUpdate = true;
- }
- DataTexture.prototype = Object.create( Texture.prototype );
- DataTexture.prototype.constructor = DataTexture;
- DataTexture.prototype.isDataTexture = true;
- const _sphere$1 = /*@__PURE__*/ new Sphere();
- const _vector$5 = /*@__PURE__*/ new Vector3();
- class Frustum {
- constructor( p0, p1, p2, p3, p4, p5 ) {
- this.planes = [
- ( p0 !== undefined ) ? p0 : new Plane(),
- ( p1 !== undefined ) ? p1 : new Plane(),
- ( p2 !== undefined ) ? p2 : new Plane(),
- ( p3 !== undefined ) ? p3 : new Plane(),
- ( p4 !== undefined ) ? p4 : new Plane(),
- ( p5 !== undefined ) ? p5 : new Plane()
- ];
- }
- set( p0, p1, p2, p3, p4, p5 ) {
- const planes = this.planes;
- planes[ 0 ].copy( p0 );
- planes[ 1 ].copy( p1 );
- planes[ 2 ].copy( p2 );
- planes[ 3 ].copy( p3 );
- planes[ 4 ].copy( p4 );
- planes[ 5 ].copy( p5 );
- return this;
- }
- clone() {
- return new this.constructor().copy( this );
- }
- copy( frustum ) {
- const planes = this.planes;
- for ( let i = 0; i < 6; i ++ ) {
- planes[ i ].copy( frustum.planes[ i ] );
- }
- return this;
- }
- setFromProjectionMatrix( m ) {
- const planes = this.planes;
- const me = m.elements;
- const me0 = me[ 0 ], me1 = me[ 1 ], me2 = me[ 2 ], me3 = me[ 3 ];
- const me4 = me[ 4 ], me5 = me[ 5 ], me6 = me[ 6 ], me7 = me[ 7 ];
- const me8 = me[ 8 ], me9 = me[ 9 ], me10 = me[ 10 ], me11 = me[ 11 ];
- const me12 = me[ 12 ], me13 = me[ 13 ], me14 = me[ 14 ], me15 = me[ 15 ];
- planes[ 0 ].setComponents( me3 - me0, me7 - me4, me11 - me8, me15 - me12 ).normalize();
- planes[ 1 ].setComponents( me3 + me0, me7 + me4, me11 + me8, me15 + me12 ).normalize();
- planes[ 2 ].setComponents( me3 + me1, me7 + me5, me11 + me9, me15 + me13 ).normalize();
- planes[ 3 ].setComponents( me3 - me1, me7 - me5, me11 - me9, me15 - me13 ).normalize();
- planes[ 4 ].setComponents( me3 - me2, me7 - me6, me11 - me10, me15 - me14 ).normalize();
- planes[ 5 ].setComponents( me3 + me2, me7 + me6, me11 + me10, me15 + me14 ).normalize();
- return this;
- }
- intersectsObject( object ) {
- const geometry = object.geometry;
- if ( geometry.boundingSphere === null ) geometry.computeBoundingSphere();
- _sphere$1.copy( geometry.boundingSphere ).applyMatrix4( object.matrixWorld );
- return this.intersectsSphere( _sphere$1 );
- }
- intersectsSprite( sprite ) {
- _sphere$1.center.set( 0, 0, 0 );
- _sphere$1.radius = 0.7071067811865476;
- _sphere$1.applyMatrix4( sprite.matrixWorld );
- return this.intersectsSphere( _sphere$1 );
- }
- intersectsSphere( sphere ) {
- const planes = this.planes;
- const center = sphere.center;
- const negRadius = - sphere.radius;
- for ( let i = 0; i < 6; i ++ ) {
- const distance = planes[ i ].distanceToPoint( center );
- if ( distance < negRadius ) {
- return false;
- }
- }
- return true;
- }
- intersectsBox( box ) {
- const planes = this.planes;
- for ( let i = 0; i < 6; i ++ ) {
- const plane = planes[ i ];
- // corner at max distance
- _vector$5.x = plane.normal.x > 0 ? box.max.x : box.min.x;
- _vector$5.y = plane.normal.y > 0 ? box.max.y : box.min.y;
- _vector$5.z = plane.normal.z > 0 ? box.max.z : box.min.z;
- if ( plane.distanceToPoint( _vector$5 ) < 0 ) {
- return false;
- }
- }
- return true;
- }
- containsPoint( point ) {
- const planes = this.planes;
- for ( let i = 0; i < 6; i ++ ) {
- if ( planes[ i ].distanceToPoint( point ) < 0 ) {
- return false;
- }
- }
- return true;
- }
- }
- function WebGLAnimation() {
- let context = null;
- let isAnimating = false;
- let animationLoop = null;
- let requestId = null;
- function onAnimationFrame( time, frame ) {
- animationLoop( time, frame );
- requestId = context.requestAnimationFrame( onAnimationFrame );
- }
- return {
- start: function () {
- if ( isAnimating === true ) return;
- if ( animationLoop === null ) return;
- requestId = context.requestAnimationFrame( onAnimationFrame );
- isAnimating = true;
- },
- stop: function () {
- context.cancelAnimationFrame( requestId );
- isAnimating = false;
- },
- setAnimationLoop: function ( callback ) {
- animationLoop = callback;
- },
- setContext: function ( value ) {
- context = value;
- }
- };
- }
- function WebGLAttributes( gl, capabilities ) {
- const isWebGL2 = capabilities.isWebGL2;
- const buffers = new WeakMap();
- function createBuffer( attribute, bufferType ) {
- const array = attribute.array;
- const usage = attribute.usage;
- const buffer = gl.createBuffer();
- gl.bindBuffer( bufferType, buffer );
- gl.bufferData( bufferType, array, usage );
- attribute.onUploadCallback();
- let type = 5126;
- if ( array instanceof Float32Array ) {
- type = 5126;
- } else if ( array instanceof Float64Array ) {
- console.warn( 'THREE.WebGLAttributes: Unsupported data buffer format: Float64Array.' );
- } else if ( array instanceof Uint16Array ) {
- if ( attribute.isFloat16BufferAttribute ) {
- if ( isWebGL2 ) {
- type = 5131;
- } else {
- console.warn( 'THREE.WebGLAttributes: Usage of Float16BufferAttribute requires WebGL2.' );
- }
- } else {
- type = 5123;
- }
- } else if ( array instanceof Int16Array ) {
- type = 5122;
- } else if ( array instanceof Uint32Array ) {
- type = 5125;
- } else if ( array instanceof Int32Array ) {
- type = 5124;
- } else if ( array instanceof Int8Array ) {
- type = 5120;
- } else if ( array instanceof Uint8Array ) {
- type = 5121;
- }
- return {
- buffer: buffer,
- type: type,
- bytesPerElement: array.BYTES_PER_ELEMENT,
- version: attribute.version
- };
- }
- function updateBuffer( buffer, attribute, bufferType ) {
- const array = attribute.array;
- const updateRange = attribute.updateRange;
- gl.bindBuffer( bufferType, buffer );
- if ( updateRange.count === - 1 ) {
- // Not using update ranges
- gl.bufferSubData( bufferType, 0, array );
- } else {
- if ( isWebGL2 ) {
- gl.bufferSubData( bufferType, updateRange.offset * array.BYTES_PER_ELEMENT,
- array, updateRange.offset, updateRange.count );
- } else {
- gl.bufferSubData( bufferType, updateRange.offset * array.BYTES_PER_ELEMENT,
- array.subarray( updateRange.offset, updateRange.offset + updateRange.count ) );
- }
- updateRange.count = - 1; // reset range
- }
- }
- //
- function get( attribute ) {
- if ( attribute.isInterleavedBufferAttribute ) attribute = attribute.data;
- return buffers.get( attribute );
- }
- function remove( attribute ) {
- if ( attribute.isInterleavedBufferAttribute ) attribute = attribute.data;
- const data = buffers.get( attribute );
- if ( data ) {
- gl.deleteBuffer( data.buffer );
- buffers.delete( attribute );
- }
- }
- function update( attribute, bufferType ) {
- if ( attribute.isGLBufferAttribute ) {
- const cached = buffers.get( attribute );
- if ( ! cached || cached.version < attribute.version ) {
- buffers.set( attribute, {
- buffer: attribute.buffer,
- type: attribute.type,
- bytesPerElement: attribute.elementSize,
- version: attribute.version
- } );
- }
- return;
- }
- if ( attribute.isInterleavedBufferAttribute ) attribute = attribute.data;
- const data = buffers.get( attribute );
- if ( data === undefined ) {
- buffers.set( attribute, createBuffer( attribute, bufferType ) );
- } else if ( data.version < attribute.version ) {
- updateBuffer( data.buffer, attribute, bufferType );
- data.version = attribute.version;
- }
- }
- return {
- get: get,
- remove: remove,
- update: update
- };
- }
- class PlaneBufferGeometry extends BufferGeometry {
- constructor( width = 1, height = 1, widthSegments = 1, heightSegments = 1 ) {
- super();
- this.type = 'PlaneBufferGeometry';
- this.parameters = {
- width: width,
- height: height,
- widthSegments: widthSegments,
- heightSegments: heightSegments
- };
- const width_half = width / 2;
- const height_half = height / 2;
- const gridX = Math.floor( widthSegments );
- const gridY = Math.floor( heightSegments );
- const gridX1 = gridX + 1;
- const gridY1 = gridY + 1;
- const segment_width = width / gridX;
- const segment_height = height / gridY;
- //
- const indices = [];
- const vertices = [];
- const normals = [];
- const uvs = [];
- for ( let iy = 0; iy < gridY1; iy ++ ) {
- const y = iy * segment_height - height_half;
- for ( let ix = 0; ix < gridX1; ix ++ ) {
- const x = ix * segment_width - width_half;
- vertices.push( x, - y, 0 );
- normals.push( 0, 0, 1 );
- uvs.push( ix / gridX );
- uvs.push( 1 - ( iy / gridY ) );
- }
- }
- for ( let iy = 0; iy < gridY; iy ++ ) {
- for ( let ix = 0; ix < gridX; ix ++ ) {
- const a = ix + gridX1 * iy;
- const b = ix + gridX1 * ( iy + 1 );
- const c = ( ix + 1 ) + gridX1 * ( iy + 1 );
- const d = ( ix + 1 ) + gridX1 * iy;
- indices.push( a, b, d );
- indices.push( b, c, d );
- }
- }
- this.setIndex( indices );
- this.setAttribute( 'position', new Float32BufferAttribute( vertices, 3 ) );
- this.setAttribute( 'normal', new Float32BufferAttribute( normals, 3 ) );
- this.setAttribute( 'uv', new Float32BufferAttribute( uvs, 2 ) );
- }
- }
- var alphamap_fragment = "#ifdef USE_ALPHAMAP\n\tdiffuseColor.a *= texture2D( alphaMap, vUv ).g;\n#endif";
- var alphamap_pars_fragment = "#ifdef USE_ALPHAMAP\n\tuniform sampler2D alphaMap;\n#endif";
- var alphatest_fragment = "#ifdef ALPHATEST\n\tif ( diffuseColor.a < ALPHATEST ) discard;\n#endif";
- var aomap_fragment = "#ifdef USE_AOMAP\n\tfloat ambientOcclusion = ( texture2D( aoMap, vUv2 ).r - 1.0 ) * aoMapIntensity + 1.0;\n\treflectedLight.indirectDiffuse *= ambientOcclusion;\n\t#if defined( USE_ENVMAP ) && defined( STANDARD )\n\t\tfloat dotNV = saturate( dot( geometry.normal, geometry.viewDir ) );\n\t\treflectedLight.indirectSpecular *= computeSpecularOcclusion( dotNV, ambientOcclusion, material.specularRoughness );\n\t#endif\n#endif";
- var aomap_pars_fragment = "#ifdef USE_AOMAP\n\tuniform sampler2D aoMap;\n\tuniform float aoMapIntensity;\n#endif";
- var begin_vertex = "vec3 transformed = vec3( position );";
- var beginnormal_vertex = "vec3 objectNormal = vec3( normal );\n#ifdef USE_TANGENT\n\tvec3 objectTangent = vec3( tangent.xyz );\n#endif";
- var bsdfs = "vec2 integrateSpecularBRDF( const in float dotNV, const in float roughness ) {\n\tconst vec4 c0 = vec4( - 1, - 0.0275, - 0.572, 0.022 );\n\tconst vec4 c1 = vec4( 1, 0.0425, 1.04, - 0.04 );\n\tvec4 r = roughness * c0 + c1;\n\tfloat a004 = min( r.x * r.x, exp2( - 9.28 * dotNV ) ) * r.x + r.y;\n\treturn vec2( -1.04, 1.04 ) * a004 + r.zw;\n}\nfloat punctualLightIntensityToIrradianceFactor( const in float lightDistance, const in float cutoffDistance, const in float decayExponent ) {\n#if defined ( PHYSICALLY_CORRECT_LIGHTS )\n\tfloat distanceFalloff = 1.0 / max( pow( lightDistance, decayExponent ), 0.01 );\n\tif( cutoffDistance > 0.0 ) {\n\t\tdistanceFalloff *= pow2( saturate( 1.0 - pow4( lightDistance / cutoffDistance ) ) );\n\t}\n\treturn distanceFalloff;\n#else\n\tif( cutoffDistance > 0.0 && decayExponent > 0.0 ) {\n\t\treturn pow( saturate( -lightDistance / cutoffDistance + 1.0 ), decayExponent );\n\t}\n\treturn 1.0;\n#endif\n}\nvec3 BRDF_Diffuse_Lambert( const in vec3 diffuseColor ) {\n\treturn RECIPROCAL_PI * diffuseColor;\n}\nvec3 F_Schlick( const in vec3 specularColor, const in float dotLH ) {\n\tfloat fresnel = exp2( ( -5.55473 * dotLH - 6.98316 ) * dotLH );\n\treturn ( 1.0 - specularColor ) * fresnel + specularColor;\n}\nvec3 F_Schlick_RoughnessDependent( const in vec3 F0, const in float dotNV, const in float roughness ) {\n\tfloat fresnel = exp2( ( -5.55473 * dotNV - 6.98316 ) * dotNV );\n\tvec3 Fr = max( vec3( 1.0 - roughness ), F0 ) - F0;\n\treturn Fr * fresnel + F0;\n}\nfloat G_GGX_Smith( const in float alpha, const in float dotNL, const in float dotNV ) {\n\tfloat a2 = pow2( alpha );\n\tfloat gl = dotNL + sqrt( a2 + ( 1.0 - a2 ) * pow2( dotNL ) );\n\tfloat gv = dotNV + sqrt( a2 + ( 1.0 - a2 ) * pow2( dotNV ) );\n\treturn 1.0 / ( gl * gv );\n}\nfloat G_GGX_SmithCorrelated( const in float alpha, const in float dotNL, const in float dotNV ) {\n\tfloat a2 = pow2( alpha );\n\tfloat gv = dotNL * sqrt( a2 + ( 1.0 - a2 ) * pow2( dotNV ) );\n\tfloat gl = dotNV * sqrt( a2 + ( 1.0 - a2 ) * pow2( dotNL ) );\n\treturn 0.5 / max( gv + gl, EPSILON );\n}\nfloat D_GGX( const in float alpha, const in float dotNH ) {\n\tfloat a2 = pow2( alpha );\n\tfloat denom = pow2( dotNH ) * ( a2 - 1.0 ) + 1.0;\n\treturn RECIPROCAL_PI * a2 / pow2( denom );\n}\nvec3 BRDF_Specular_GGX( const in IncidentLight incidentLight, const in vec3 viewDir, const in vec3 normal, const in vec3 specularColor, const in float roughness ) {\n\tfloat alpha = pow2( roughness );\n\tvec3 halfDir = normalize( incidentLight.direction + viewDir );\n\tfloat dotNL = saturate( dot( normal, incidentLight.direction ) );\n\tfloat dotNV = saturate( dot( normal, viewDir ) );\n\tfloat dotNH = saturate( dot( normal, halfDir ) );\n\tfloat dotLH = saturate( dot( incidentLight.direction, halfDir ) );\n\tvec3 F = F_Schlick( specularColor, dotLH );\n\tfloat G = G_GGX_SmithCorrelated( alpha, dotNL, dotNV );\n\tfloat D = D_GGX( alpha, dotNH );\n\treturn F * ( G * D );\n}\nvec2 LTC_Uv( const in vec3 N, const in vec3 V, const in float roughness ) {\n\tconst float LUT_SIZE = 64.0;\n\tconst float LUT_SCALE = ( LUT_SIZE - 1.0 ) / LUT_SIZE;\n\tconst float LUT_BIAS = 0.5 / LUT_SIZE;\n\tfloat dotNV = saturate( dot( N, V ) );\n\tvec2 uv = vec2( roughness, sqrt( 1.0 - dotNV ) );\n\tuv = uv * LUT_SCALE + LUT_BIAS;\n\treturn uv;\n}\nfloat LTC_ClippedSphereFormFactor( const in vec3 f ) {\n\tfloat l = length( f );\n\treturn max( ( l * l + f.z ) / ( l + 1.0 ), 0.0 );\n}\nvec3 LTC_EdgeVectorFormFactor( const in vec3 v1, const in vec3 v2 ) {\n\tfloat x = dot( v1, v2 );\n\tfloat y = abs( x );\n\tfloat a = 0.8543985 + ( 0.4965155 + 0.0145206 * y ) * y;\n\tfloat b = 3.4175940 + ( 4.1616724 + y ) * y;\n\tfloat v = a / b;\n\tfloat theta_sintheta = ( x > 0.0 ) ? v : 0.5 * inversesqrt( max( 1.0 - x * x, 1e-7 ) ) - v;\n\treturn cross( v1, v2 ) * theta_sintheta;\n}\nvec3 LTC_Evaluate( const in vec3 N, const in vec3 V, const in vec3 P, const in mat3 mInv, const in vec3 rectCoords[ 4 ] ) {\n\tvec3 v1 = rectCoords[ 1 ] - rectCoords[ 0 ];\n\tvec3 v2 = rectCoords[ 3 ] - rectCoords[ 0 ];\n\tvec3 lightNormal = cross( v1, v2 );\n\tif( dot( lightNormal, P - rectCoords[ 0 ] ) < 0.0 ) return vec3( 0.0 );\n\tvec3 T1, T2;\n\tT1 = normalize( V - N * dot( V, N ) );\n\tT2 = - cross( N, T1 );\n\tmat3 mat = mInv * transposeMat3( mat3( T1, T2, N ) );\n\tvec3 coords[ 4 ];\n\tcoords[ 0 ] = mat * ( rectCoords[ 0 ] - P );\n\tcoords[ 1 ] = mat * ( rectCoords[ 1 ] - P );\n\tcoords[ 2 ] = mat * ( rectCoords[ 2 ] - P );\n\tcoords[ 3 ] = mat * ( rectCoords[ 3 ] - P );\n\tcoords[ 0 ] = normalize( coords[ 0 ] );\n\tcoords[ 1 ] = normalize( coords[ 1 ] );\n\tcoords[ 2 ] = normalize( coords[ 2 ] );\n\tcoords[ 3 ] = normalize( coords[ 3 ] );\n\tvec3 vectorFormFactor = vec3( 0.0 );\n\tvectorFormFactor += LTC_EdgeVectorFormFactor( coords[ 0 ], coords[ 1 ] );\n\tvectorFormFactor += LTC_EdgeVectorFormFactor( coords[ 1 ], coords[ 2 ] );\n\tvectorFormFactor += LTC_EdgeVectorFormFactor( coords[ 2 ], coords[ 3 ] );\n\tvectorFormFactor += LTC_EdgeVectorFormFactor( coords[ 3 ], coords[ 0 ] );\n\tfloat result = LTC_ClippedSphereFormFactor( vectorFormFactor );\n\treturn vec3( result );\n}\nvec3 BRDF_Specular_GGX_Environment( const in vec3 viewDir, const in vec3 normal, const in vec3 specularColor, const in float roughness ) {\n\tfloat dotNV = saturate( dot( normal, viewDir ) );\n\tvec2 brdf = integrateSpecularBRDF( dotNV, roughness );\n\treturn specularColor * brdf.x + brdf.y;\n}\nvoid BRDF_Specular_Multiscattering_Environment( const in GeometricContext geometry, const in vec3 specularColor, const in float roughness, inout vec3 singleScatter, inout vec3 multiScatter ) {\n\tfloat dotNV = saturate( dot( geometry.normal, geometry.viewDir ) );\n\tvec3 F = F_Schlick_RoughnessDependent( specularColor, dotNV, roughness );\n\tvec2 brdf = integrateSpecularBRDF( dotNV, roughness );\n\tvec3 FssEss = F * brdf.x + brdf.y;\n\tfloat Ess = brdf.x + brdf.y;\n\tfloat Ems = 1.0 - Ess;\n\tvec3 Favg = specularColor + ( 1.0 - specularColor ) * 0.047619;\tvec3 Fms = FssEss * Favg / ( 1.0 - Ems * Favg );\n\tsingleScatter += FssEss;\n\tmultiScatter += Fms * Ems;\n}\nfloat G_BlinnPhong_Implicit( ) {\n\treturn 0.25;\n}\nfloat D_BlinnPhong( const in float shininess, const in float dotNH ) {\n\treturn RECIPROCAL_PI * ( shininess * 0.5 + 1.0 ) * pow( dotNH, shininess );\n}\nvec3 BRDF_Specular_BlinnPhong( const in IncidentLight incidentLight, const in GeometricContext geometry, const in vec3 specularColor, const in float shininess ) {\n\tvec3 halfDir = normalize( incidentLight.direction + geometry.viewDir );\n\tfloat dotNH = saturate( dot( geometry.normal, halfDir ) );\n\tfloat dotLH = saturate( dot( incidentLight.direction, halfDir ) );\n\tvec3 F = F_Schlick( specularColor, dotLH );\n\tfloat G = G_BlinnPhong_Implicit( );\n\tfloat D = D_BlinnPhong( shininess, dotNH );\n\treturn F * ( G * D );\n}\nfloat GGXRoughnessToBlinnExponent( const in float ggxRoughness ) {\n\treturn ( 2.0 / pow2( ggxRoughness + 0.0001 ) - 2.0 );\n}\nfloat BlinnExponentToGGXRoughness( const in float blinnExponent ) {\n\treturn sqrt( 2.0 / ( blinnExponent + 2.0 ) );\n}\n#if defined( USE_SHEEN )\nfloat D_Charlie(float roughness, float NoH) {\n\tfloat invAlpha = 1.0 / roughness;\n\tfloat cos2h = NoH * NoH;\n\tfloat sin2h = max(1.0 - cos2h, 0.0078125);\treturn (2.0 + invAlpha) * pow(sin2h, invAlpha * 0.5) / (2.0 * PI);\n}\nfloat V_Neubelt(float NoV, float NoL) {\n\treturn saturate(1.0 / (4.0 * (NoL + NoV - NoL * NoV)));\n}\nvec3 BRDF_Specular_Sheen( const in float roughness, const in vec3 L, const in GeometricContext geometry, vec3 specularColor ) {\n\tvec3 N = geometry.normal;\n\tvec3 V = geometry.viewDir;\n\tvec3 H = normalize( V + L );\n\tfloat dotNH = saturate( dot( N, H ) );\n\treturn specularColor * D_Charlie( roughness, dotNH ) * V_Neubelt( dot(N, V), dot(N, L) );\n}\n#endif";
- var bumpmap_pars_fragment = "#ifdef USE_BUMPMAP\n\tuniform sampler2D bumpMap;\n\tuniform float bumpScale;\n\tvec2 dHdxy_fwd() {\n\t\tvec2 dSTdx = dFdx( vUv );\n\t\tvec2 dSTdy = dFdy( vUv );\n\t\tfloat Hll = bumpScale * texture2D( bumpMap, vUv ).x;\n\t\tfloat dBx = bumpScale * texture2D( bumpMap, vUv + dSTdx ).x - Hll;\n\t\tfloat dBy = bumpScale * texture2D( bumpMap, vUv + dSTdy ).x - Hll;\n\t\treturn vec2( dBx, dBy );\n\t}\n\tvec3 perturbNormalArb( vec3 surf_pos, vec3 surf_norm, vec2 dHdxy ) {\n\t\tvec3 vSigmaX = vec3( dFdx( surf_pos.x ), dFdx( surf_pos.y ), dFdx( surf_pos.z ) );\n\t\tvec3 vSigmaY = vec3( dFdy( surf_pos.x ), dFdy( surf_pos.y ), dFdy( surf_pos.z ) );\n\t\tvec3 vN = surf_norm;\n\t\tvec3 R1 = cross( vSigmaY, vN );\n\t\tvec3 R2 = cross( vN, vSigmaX );\n\t\tfloat fDet = dot( vSigmaX, R1 );\n\t\tfDet *= ( float( gl_FrontFacing ) * 2.0 - 1.0 );\n\t\tvec3 vGrad = sign( fDet ) * ( dHdxy.x * R1 + dHdxy.y * R2 );\n\t\treturn normalize( abs( fDet ) * surf_norm - vGrad );\n\t}\n#endif";
- var clipping_planes_fragment = "#if NUM_CLIPPING_PLANES > 0\n\tvec4 plane;\n\t#pragma unroll_loop_start\n\tfor ( int i = 0; i < UNION_CLIPPING_PLANES; i ++ ) {\n\t\tplane = clippingPlanes[ i ];\n\t\tif ( dot( vClipPosition, plane.xyz ) > plane.w ) discard;\n\t}\n\t#pragma unroll_loop_end\n\t#if UNION_CLIPPING_PLANES < NUM_CLIPPING_PLANES\n\t\tbool clipped = true;\n\t\t#pragma unroll_loop_start\n\t\tfor ( int i = UNION_CLIPPING_PLANES; i < NUM_CLIPPING_PLANES; i ++ ) {\n\t\t\tplane = clippingPlanes[ i ];\n\t\t\tclipped = ( dot( vClipPosition, plane.xyz ) > plane.w ) && clipped;\n\t\t}\n\t\t#pragma unroll_loop_end\n\t\tif ( clipped ) discard;\n\t#endif\n#endif";
- var clipping_planes_pars_fragment = "#if NUM_CLIPPING_PLANES > 0\n\tvarying vec3 vClipPosition;\n\tuniform vec4 clippingPlanes[ NUM_CLIPPING_PLANES ];\n#endif";
- var clipping_planes_pars_vertex = "#if NUM_CLIPPING_PLANES > 0\n\tvarying vec3 vClipPosition;\n#endif";
- var clipping_planes_vertex = "#if NUM_CLIPPING_PLANES > 0\n\tvClipPosition = - mvPosition.xyz;\n#endif";
- var color_fragment = "#ifdef USE_COLOR\n\tdiffuseColor.rgb *= vColor;\n#endif";
- var color_pars_fragment = "#ifdef USE_COLOR\n\tvarying vec3 vColor;\n#endif";
- var color_pars_vertex = "#if defined( USE_COLOR ) || defined( USE_INSTANCING_COLOR )\n\tvarying vec3 vColor;\n#endif";
- var color_vertex = "#if defined( USE_COLOR ) || defined( USE_INSTANCING_COLOR )\n\tvColor = vec3( 1.0 );\n#endif\n#ifdef USE_COLOR\n\tvColor.xyz *= color.xyz;\n#endif\n#ifdef USE_INSTANCING_COLOR\n\tvColor.xyz *= instanceColor.xyz;\n#endif";
- var common = "#define PI 3.141592653589793\n#define PI2 6.283185307179586\n#define PI_HALF 1.5707963267948966\n#define RECIPROCAL_PI 0.3183098861837907\n#define RECIPROCAL_PI2 0.15915494309189535\n#define EPSILON 1e-6\n#ifndef saturate\n#define saturate(a) clamp( a, 0.0, 1.0 )\n#endif\n#define whiteComplement(a) ( 1.0 - saturate( a ) )\nfloat pow2( const in float x ) { return x*x; }\nfloat pow3( const in float x ) { return x*x*x; }\nfloat pow4( const in float x ) { float x2 = x*x; return x2*x2; }\nfloat average( const in vec3 color ) { return dot( color, vec3( 0.3333 ) ); }\nhighp float rand( const in vec2 uv ) {\n\tconst highp float a = 12.9898, b = 78.233, c = 43758.5453;\n\thighp float dt = dot( uv.xy, vec2( a,b ) ), sn = mod( dt, PI );\n\treturn fract(sin(sn) * c);\n}\n#ifdef HIGH_PRECISION\n\tfloat precisionSafeLength( vec3 v ) { return length( v ); }\n#else\n\tfloat max3( vec3 v ) { return max( max( v.x, v.y ), v.z ); }\n\tfloat precisionSafeLength( vec3 v ) {\n\t\tfloat maxComponent = max3( abs( v ) );\n\t\treturn length( v / maxComponent ) * maxComponent;\n\t}\n#endif\nstruct IncidentLight {\n\tvec3 color;\n\tvec3 direction;\n\tbool visible;\n};\nstruct ReflectedLight {\n\tvec3 directDiffuse;\n\tvec3 directSpecular;\n\tvec3 indirectDiffuse;\n\tvec3 indirectSpecular;\n};\nstruct GeometricContext {\n\tvec3 position;\n\tvec3 normal;\n\tvec3 viewDir;\n#ifdef CLEARCOAT\n\tvec3 clearcoatNormal;\n#endif\n};\nvec3 transformDirection( in vec3 dir, in mat4 matrix ) {\n\treturn normalize( ( matrix * vec4( dir, 0.0 ) ).xyz );\n}\nvec3 inverseTransformDirection( in vec3 dir, in mat4 matrix ) {\n\treturn normalize( ( vec4( dir, 0.0 ) * matrix ).xyz );\n}\nvec3 projectOnPlane(in vec3 point, in vec3 pointOnPlane, in vec3 planeNormal ) {\n\tfloat distance = dot( planeNormal, point - pointOnPlane );\n\treturn - distance * planeNormal + point;\n}\nfloat sideOfPlane( in vec3 point, in vec3 pointOnPlane, in vec3 planeNormal ) {\n\treturn sign( dot( point - pointOnPlane, planeNormal ) );\n}\nvec3 linePlaneIntersect( in vec3 pointOnLine, in vec3 lineDirection, in vec3 pointOnPlane, in vec3 planeNormal ) {\n\treturn lineDirection * ( dot( planeNormal, pointOnPlane - pointOnLine ) / dot( planeNormal, lineDirection ) ) + pointOnLine;\n}\nmat3 transposeMat3( const in mat3 m ) {\n\tmat3 tmp;\n\ttmp[ 0 ] = vec3( m[ 0 ].x, m[ 1 ].x, m[ 2 ].x );\n\ttmp[ 1 ] = vec3( m[ 0 ].y, m[ 1 ].y, m[ 2 ].y );\n\ttmp[ 2 ] = vec3( m[ 0 ].z, m[ 1 ].z, m[ 2 ].z );\n\treturn tmp;\n}\nfloat linearToRelativeLuminance( const in vec3 color ) {\n\tvec3 weights = vec3( 0.2126, 0.7152, 0.0722 );\n\treturn dot( weights, color.rgb );\n}\nbool isPerspectiveMatrix( mat4 m ) {\n\treturn m[ 2 ][ 3 ] == - 1.0;\n}\nvec2 equirectUv( in vec3 dir ) {\n\tfloat u = atan( dir.z, dir.x ) * RECIPROCAL_PI2 + 0.5;\n\tfloat v = asin( clamp( dir.y, - 1.0, 1.0 ) ) * RECIPROCAL_PI + 0.5;\n\treturn vec2( u, v );\n}";
- var cube_uv_reflection_fragment = "#ifdef ENVMAP_TYPE_CUBE_UV\n\t#define cubeUV_maxMipLevel 8.0\n\t#define cubeUV_minMipLevel 4.0\n\t#define cubeUV_maxTileSize 256.0\n\t#define cubeUV_minTileSize 16.0\n\tfloat getFace( vec3 direction ) {\n\t\tvec3 absDirection = abs( direction );\n\t\tfloat face = - 1.0;\n\t\tif ( absDirection.x > absDirection.z ) {\n\t\t\tif ( absDirection.x > absDirection.y )\n\t\t\t\tface = direction.x > 0.0 ? 0.0 : 3.0;\n\t\t\telse\n\t\t\t\tface = direction.y > 0.0 ? 1.0 : 4.0;\n\t\t} else {\n\t\t\tif ( absDirection.z > absDirection.y )\n\t\t\t\tface = direction.z > 0.0 ? 2.0 : 5.0;\n\t\t\telse\n\t\t\t\tface = direction.y > 0.0 ? 1.0 : 4.0;\n\t\t}\n\t\treturn face;\n\t}\n\tvec2 getUV( vec3 direction, float face ) {\n\t\tvec2 uv;\n\t\tif ( face == 0.0 ) {\n\t\t\tuv = vec2( direction.z, direction.y ) / abs( direction.x );\n\t\t} else if ( face == 1.0 ) {\n\t\t\tuv = vec2( - direction.x, - direction.z ) / abs( direction.y );\n\t\t} else if ( face == 2.0 ) {\n\t\t\tuv = vec2( - direction.x, direction.y ) / abs( direction.z );\n\t\t} else if ( face == 3.0 ) {\n\t\t\tuv = vec2( - direction.z, direction.y ) / abs( direction.x );\n\t\t} else if ( face == 4.0 ) {\n\t\t\tuv = vec2( - direction.x, direction.z ) / abs( direction.y );\n\t\t} else {\n\t\t\tuv = vec2( direction.x, direction.y ) / abs( direction.z );\n\t\t}\n\t\treturn 0.5 * ( uv + 1.0 );\n\t}\n\tvec3 bilinearCubeUV( sampler2D envMap, vec3 direction, float mipInt ) {\n\t\tfloat face = getFace( direction );\n\t\tfloat filterInt = max( cubeUV_minMipLevel - mipInt, 0.0 );\n\t\tmipInt = max( mipInt, cubeUV_minMipLevel );\n\t\tfloat faceSize = exp2( mipInt );\n\t\tfloat texelSize = 1.0 / ( 3.0 * cubeUV_maxTileSize );\n\t\tvec2 uv = getUV( direction, face ) * ( faceSize - 1.0 );\n\t\tvec2 f = fract( uv );\n\t\tuv += 0.5 - f;\n\t\tif ( face > 2.0 ) {\n\t\t\tuv.y += faceSize;\n\t\t\tface -= 3.0;\n\t\t}\n\t\tuv.x += face * faceSize;\n\t\tif ( mipInt < cubeUV_maxMipLevel ) {\n\t\t\tuv.y += 2.0 * cubeUV_maxTileSize;\n\t\t}\n\t\tuv.y += filterInt * 2.0 * cubeUV_minTileSize;\n\t\tuv.x += 3.0 * max( 0.0, cubeUV_maxTileSize - 2.0 * faceSize );\n\t\tuv *= texelSize;\n\t\tvec3 tl = envMapTexelToLinear( texture2D( envMap, uv ) ).rgb;\n\t\tuv.x += texelSize;\n\t\tvec3 tr = envMapTexelToLinear( texture2D( envMap, uv ) ).rgb;\n\t\tuv.y += texelSize;\n\t\tvec3 br = envMapTexelToLinear( texture2D( envMap, uv ) ).rgb;\n\t\tuv.x -= texelSize;\n\t\tvec3 bl = envMapTexelToLinear( texture2D( envMap, uv ) ).rgb;\n\t\tvec3 tm = mix( tl, tr, f.x );\n\t\tvec3 bm = mix( bl, br, f.x );\n\t\treturn mix( tm, bm, f.y );\n\t}\n\t#define r0 1.0\n\t#define v0 0.339\n\t#define m0 - 2.0\n\t#define r1 0.8\n\t#define v1 0.276\n\t#define m1 - 1.0\n\t#define r4 0.4\n\t#define v4 0.046\n\t#define m4 2.0\n\t#define r5 0.305\n\t#define v5 0.016\n\t#define m5 3.0\n\t#define r6 0.21\n\t#define v6 0.0038\n\t#define m6 4.0\n\tfloat roughnessToMip( float roughness ) {\n\t\tfloat mip = 0.0;\n\t\tif ( roughness >= r1 ) {\n\t\t\tmip = ( r0 - roughness ) * ( m1 - m0 ) / ( r0 - r1 ) + m0;\n\t\t} else if ( roughness >= r4 ) {\n\t\t\tmip = ( r1 - roughness ) * ( m4 - m1 ) / ( r1 - r4 ) + m1;\n\t\t} else if ( roughness >= r5 ) {\n\t\t\tmip = ( r4 - roughness ) * ( m5 - m4 ) / ( r4 - r5 ) + m4;\n\t\t} else if ( roughness >= r6 ) {\n\t\t\tmip = ( r5 - roughness ) * ( m6 - m5 ) / ( r5 - r6 ) + m5;\n\t\t} else {\n\t\t\tmip = - 2.0 * log2( 1.16 * roughness );\t\t}\n\t\treturn mip;\n\t}\n\tvec4 textureCubeUV( sampler2D envMap, vec3 sampleDir, float roughness ) {\n\t\tfloat mip = clamp( roughnessToMip( roughness ), m0, cubeUV_maxMipLevel );\n\t\tfloat mipF = fract( mip );\n\t\tfloat mipInt = floor( mip );\n\t\tvec3 color0 = bilinearCubeUV( envMap, sampleDir, mipInt );\n\t\tif ( mipF == 0.0 ) {\n\t\t\treturn vec4( color0, 1.0 );\n\t\t} else {\n\t\t\tvec3 color1 = bilinearCubeUV( envMap, sampleDir, mipInt + 1.0 );\n\t\t\treturn vec4( mix( color0, color1, mipF ), 1.0 );\n\t\t}\n\t}\n#endif";
- var defaultnormal_vertex = "vec3 transformedNormal = objectNormal;\n#ifdef USE_INSTANCING\n\tmat3 m = mat3( instanceMatrix );\n\ttransformedNormal /= vec3( dot( m[ 0 ], m[ 0 ] ), dot( m[ 1 ], m[ 1 ] ), dot( m[ 2 ], m[ 2 ] ) );\n\ttransformedNormal = m * transformedNormal;\n#endif\ntransformedNormal = normalMatrix * transformedNormal;\n#ifdef FLIP_SIDED\n\ttransformedNormal = - transformedNormal;\n#endif\n#ifdef USE_TANGENT\n\tvec3 transformedTangent = ( modelViewMatrix * vec4( objectTangent, 0.0 ) ).xyz;\n\t#ifdef FLIP_SIDED\n\t\ttransformedTangent = - transformedTangent;\n\t#endif\n#endif";
- var displacementmap_pars_vertex = "#ifdef USE_DISPLACEMENTMAP\n\tuniform sampler2D displacementMap;\n\tuniform float displacementScale;\n\tuniform float displacementBias;\n#endif";
- var displacementmap_vertex = "#ifdef USE_DISPLACEMENTMAP\n\ttransformed += normalize( objectNormal ) * ( texture2D( displacementMap, vUv ).x * displacementScale + displacementBias );\n#endif";
- var emissivemap_fragment = "#ifdef USE_EMISSIVEMAP\n\tvec4 emissiveColor = texture2D( emissiveMap, vUv );\n\temissiveColor.rgb = emissiveMapTexelToLinear( emissiveColor ).rgb;\n\ttotalEmissiveRadiance *= emissiveColor.rgb;\n#endif";
- var emissivemap_pars_fragment = "#ifdef USE_EMISSIVEMAP\n\tuniform sampler2D emissiveMap;\n#endif";
- var encodings_fragment = "gl_FragColor = linearToOutputTexel( gl_FragColor );";
- var encodings_pars_fragment = "\nvec4 LinearToLinear( in vec4 value ) {\n\treturn value;\n}\nvec4 GammaToLinear( in vec4 value, in float gammaFactor ) {\n\treturn vec4( pow( value.rgb, vec3( gammaFactor ) ), value.a );\n}\nvec4 LinearToGamma( in vec4 value, in float gammaFactor ) {\n\treturn vec4( pow( value.rgb, vec3( 1.0 / gammaFactor ) ), value.a );\n}\nvec4 sRGBToLinear( in vec4 value ) {\n\treturn vec4( mix( pow( value.rgb * 0.9478672986 + vec3( 0.0521327014 ), vec3( 2.4 ) ), value.rgb * 0.0773993808, vec3( lessThanEqual( value.rgb, vec3( 0.04045 ) ) ) ), value.a );\n}\nvec4 LinearTosRGB( in vec4 value ) {\n\treturn vec4( mix( pow( value.rgb, vec3( 0.41666 ) ) * 1.055 - vec3( 0.055 ), value.rgb * 12.92, vec3( lessThanEqual( value.rgb, vec3( 0.0031308 ) ) ) ), value.a );\n}\nvec4 RGBEToLinear( in vec4 value ) {\n\treturn vec4( value.rgb * exp2( value.a * 255.0 - 128.0 ), 1.0 );\n}\nvec4 LinearToRGBE( in vec4 value ) {\n\tfloat maxComponent = max( max( value.r, value.g ), value.b );\n\tfloat fExp = clamp( ceil( log2( maxComponent ) ), -128.0, 127.0 );\n\treturn vec4( value.rgb / exp2( fExp ), ( fExp + 128.0 ) / 255.0 );\n}\nvec4 RGBMToLinear( in vec4 value, in float maxRange ) {\n\treturn vec4( value.rgb * value.a * maxRange, 1.0 );\n}\nvec4 LinearToRGBM( in vec4 value, in float maxRange ) {\n\tfloat maxRGB = max( value.r, max( value.g, value.b ) );\n\tfloat M = clamp( maxRGB / maxRange, 0.0, 1.0 );\n\tM = ceil( M * 255.0 ) / 255.0;\n\treturn vec4( value.rgb / ( M * maxRange ), M );\n}\nvec4 RGBDToLinear( in vec4 value, in float maxRange ) {\n\treturn vec4( value.rgb * ( ( maxRange / 255.0 ) / value.a ), 1.0 );\n}\nvec4 LinearToRGBD( in vec4 value, in float maxRange ) {\n\tfloat maxRGB = max( value.r, max( value.g, value.b ) );\n\tfloat D = max( maxRange / maxRGB, 1.0 );\n\tD = clamp( floor( D ) / 255.0, 0.0, 1.0 );\n\treturn vec4( value.rgb * ( D * ( 255.0 / maxRange ) ), D );\n}\nconst mat3 cLogLuvM = mat3( 0.2209, 0.3390, 0.4184, 0.1138, 0.6780, 0.7319, 0.0102, 0.1130, 0.2969 );\nvec4 LinearToLogLuv( in vec4 value ) {\n\tvec3 Xp_Y_XYZp = cLogLuvM * value.rgb;\n\tXp_Y_XYZp = max( Xp_Y_XYZp, vec3( 1e-6, 1e-6, 1e-6 ) );\n\tvec4 vResult;\n\tvResult.xy = Xp_Y_XYZp.xy / Xp_Y_XYZp.z;\n\tfloat Le = 2.0 * log2(Xp_Y_XYZp.y) + 127.0;\n\tvResult.w = fract( Le );\n\tvResult.z = ( Le - ( floor( vResult.w * 255.0 ) ) / 255.0 ) / 255.0;\n\treturn vResult;\n}\nconst mat3 cLogLuvInverseM = mat3( 6.0014, -2.7008, -1.7996, -1.3320, 3.1029, -5.7721, 0.3008, -1.0882, 5.6268 );\nvec4 LogLuvToLinear( in vec4 value ) {\n\tfloat Le = value.z * 255.0 + value.w;\n\tvec3 Xp_Y_XYZp;\n\tXp_Y_XYZp.y = exp2( ( Le - 127.0 ) / 2.0 );\n\tXp_Y_XYZp.z = Xp_Y_XYZp.y / value.y;\n\tXp_Y_XYZp.x = value.x * Xp_Y_XYZp.z;\n\tvec3 vRGB = cLogLuvInverseM * Xp_Y_XYZp.rgb;\n\treturn vec4( max( vRGB, 0.0 ), 1.0 );\n}";
- var envmap_fragment = "#ifdef USE_ENVMAP\n\t#ifdef ENV_WORLDPOS\n\t\tvec3 cameraToFrag;\n\t\tif ( isOrthographic ) {\n\t\t\tcameraToFrag = normalize( vec3( - viewMatrix[ 0 ][ 2 ], - viewMatrix[ 1 ][ 2 ], - viewMatrix[ 2 ][ 2 ] ) );\n\t\t} else {\n\t\t\tcameraToFrag = normalize( vWorldPosition - cameraPosition );\n\t\t}\n\t\tvec3 worldNormal = inverseTransformDirection( normal, viewMatrix );\n\t\t#ifdef ENVMAP_MODE_REFLECTION\n\t\t\tvec3 reflectVec = reflect( cameraToFrag, worldNormal );\n\t\t#else\n\t\t\tvec3 reflectVec = refract( cameraToFrag, worldNormal, refractionRatio );\n\t\t#endif\n\t#else\n\t\tvec3 reflectVec = vReflect;\n\t#endif\n\t#ifdef ENVMAP_TYPE_CUBE\n\t\tvec4 envColor = textureCube( envMap, vec3( flipEnvMap * reflectVec.x, reflectVec.yz ) );\n\t#elif defined( ENVMAP_TYPE_CUBE_UV )\n\t\tvec4 envColor = textureCubeUV( envMap, reflectVec, 0.0 );\n\t#else\n\t\tvec4 envColor = vec4( 0.0 );\n\t#endif\n\t#ifndef ENVMAP_TYPE_CUBE_UV\n\t\tenvColor = envMapTexelToLinear( envColor );\n\t#endif\n\t#ifdef ENVMAP_BLENDING_MULTIPLY\n\t\toutgoingLight = mix( outgoingLight, outgoingLight * envColor.xyz, specularStrength * reflectivity );\n\t#elif defined( ENVMAP_BLENDING_MIX )\n\t\toutgoingLight = mix( outgoingLight, envColor.xyz, specularStrength * reflectivity );\n\t#elif defined( ENVMAP_BLENDING_ADD )\n\t\toutgoingLight += envColor.xyz * specularStrength * reflectivity;\n\t#endif\n#endif";
- var envmap_common_pars_fragment = "#ifdef USE_ENVMAP\n\tuniform float envMapIntensity;\n\tuniform float flipEnvMap;\n\tuniform int maxMipLevel;\n\t#ifdef ENVMAP_TYPE_CUBE\n\t\tuniform samplerCube envMap;\n\t#else\n\t\tuniform sampler2D envMap;\n\t#endif\n\t\n#endif";
- var envmap_pars_fragment = "#ifdef USE_ENVMAP\n\tuniform float reflectivity;\n\t#if defined( USE_BUMPMAP ) || defined( USE_NORMALMAP ) || defined( PHONG )\n\t\t#define ENV_WORLDPOS\n\t#endif\n\t#ifdef ENV_WORLDPOS\n\t\tvarying vec3 vWorldPosition;\n\t\tuniform float refractionRatio;\n\t#else\n\t\tvarying vec3 vReflect;\n\t#endif\n#endif";
- var envmap_pars_vertex = "#ifdef USE_ENVMAP\n\t#if defined( USE_BUMPMAP ) || defined( USE_NORMALMAP ) ||defined( PHONG )\n\t\t#define ENV_WORLDPOS\n\t#endif\n\t#ifdef ENV_WORLDPOS\n\t\t\n\t\tvarying vec3 vWorldPosition;\n\t#else\n\t\tvarying vec3 vReflect;\n\t\tuniform float refractionRatio;\n\t#endif\n#endif";
- var envmap_vertex = "#ifdef USE_ENVMAP\n\t#ifdef ENV_WORLDPOS\n\t\tvWorldPosition = worldPosition.xyz;\n\t#else\n\t\tvec3 cameraToVertex;\n\t\tif ( isOrthographic ) {\n\t\t\tcameraToVertex = normalize( vec3( - viewMatrix[ 0 ][ 2 ], - viewMatrix[ 1 ][ 2 ], - viewMatrix[ 2 ][ 2 ] ) );\n\t\t} else {\n\t\t\tcameraToVertex = normalize( worldPosition.xyz - cameraPosition );\n\t\t}\n\t\tvec3 worldNormal = inverseTransformDirection( transformedNormal, viewMatrix );\n\t\t#ifdef ENVMAP_MODE_REFLECTION\n\t\t\tvReflect = reflect( cameraToVertex, worldNormal );\n\t\t#else\n\t\t\tvReflect = refract( cameraToVertex, worldNormal, refractionRatio );\n\t\t#endif\n\t#endif\n#endif";
- var fog_vertex = "#ifdef USE_FOG\n\tfogDepth = - mvPosition.z;\n#endif";
- var fog_pars_vertex = "#ifdef USE_FOG\n\tvarying float fogDepth;\n#endif";
- var fog_fragment = "#ifdef USE_FOG\n\t#ifdef FOG_EXP2\n\t\tfloat fogFactor = 1.0 - exp( - fogDensity * fogDensity * fogDepth * fogDepth );\n\t#else\n\t\tfloat fogFactor = smoothstep( fogNear, fogFar, fogDepth );\n\t#endif\n\tgl_FragColor.rgb = mix( gl_FragColor.rgb, fogColor, fogFactor );\n#endif";
- var fog_pars_fragment = "#ifdef USE_FOG\n\tuniform vec3 fogColor;\n\tvarying float fogDepth;\n\t#ifdef FOG_EXP2\n\t\tuniform float fogDensity;\n\t#else\n\t\tuniform float fogNear;\n\t\tuniform float fogFar;\n\t#endif\n#endif";
- var gradientmap_pars_fragment = "#ifdef USE_GRADIENTMAP\n\tuniform sampler2D gradientMap;\n#endif\nvec3 getGradientIrradiance( vec3 normal, vec3 lightDirection ) {\n\tfloat dotNL = dot( normal, lightDirection );\n\tvec2 coord = vec2( dotNL * 0.5 + 0.5, 0.0 );\n\t#ifdef USE_GRADIENTMAP\n\t\treturn texture2D( gradientMap, coord ).rgb;\n\t#else\n\t\treturn ( coord.x < 0.7 ) ? vec3( 0.7 ) : vec3( 1.0 );\n\t#endif\n}";
- var lightmap_fragment = "#ifdef USE_LIGHTMAP\n\tvec4 lightMapTexel= texture2D( lightMap, vUv2 );\n\treflectedLight.indirectDiffuse += PI * lightMapTexelToLinear( lightMapTexel ).rgb * lightMapIntensity;\n#endif";
- var lightmap_pars_fragment = "#ifdef USE_LIGHTMAP\n\tuniform sampler2D lightMap;\n\tuniform float lightMapIntensity;\n#endif";
- var lights_lambert_vertex = "vec3 diffuse = vec3( 1.0 );\nGeometricContext geometry;\ngeometry.position = mvPosition.xyz;\ngeometry.normal = normalize( transformedNormal );\ngeometry.viewDir = ( isOrthographic ) ? vec3( 0, 0, 1 ) : normalize( -mvPosition.xyz );\nGeometricContext backGeometry;\nbackGeometry.position = geometry.position;\nbackGeometry.normal = -geometry.normal;\nbackGeometry.viewDir = geometry.viewDir;\nvLightFront = vec3( 0.0 );\nvIndirectFront = vec3( 0.0 );\n#ifdef DOUBLE_SIDED\n\tvLightBack = vec3( 0.0 );\n\tvIndirectBack = vec3( 0.0 );\n#endif\nIncidentLight directLight;\nfloat dotNL;\nvec3 directLightColor_Diffuse;\nvIndirectFront += getAmbientLightIrradiance( ambientLightColor );\nvIndirectFront += getLightProbeIrradiance( lightProbe, geometry );\n#ifdef DOUBLE_SIDED\n\tvIndirectBack += getAmbientLightIrradiance( ambientLightColor );\n\tvIndirectBack += getLightProbeIrradiance( lightProbe, backGeometry );\n#endif\n#if NUM_POINT_LIGHTS > 0\n\t#pragma unroll_loop_start\n\tfor ( int i = 0; i < NUM_POINT_LIGHTS; i ++ ) {\n\t\tgetPointDirectLightIrradiance( pointLights[ i ], geometry, directLight );\n\t\tdotNL = dot( geometry.normal, directLight.direction );\n\t\tdirectLightColor_Diffuse = PI * directLight.color;\n\t\tvLightFront += saturate( dotNL ) * directLightColor_Diffuse;\n\t\t#ifdef DOUBLE_SIDED\n\t\t\tvLightBack += saturate( -dotNL ) * directLightColor_Diffuse;\n\t\t#endif\n\t}\n\t#pragma unroll_loop_end\n#endif\n#if NUM_SPOT_LIGHTS > 0\n\t#pragma unroll_loop_start\n\tfor ( int i = 0; i < NUM_SPOT_LIGHTS; i ++ ) {\n\t\tgetSpotDirectLightIrradiance( spotLights[ i ], geometry, directLight );\n\t\tdotNL = dot( geometry.normal, directLight.direction );\n\t\tdirectLightColor_Diffuse = PI * directLight.color;\n\t\tvLightFront += saturate( dotNL ) * directLightColor_Diffuse;\n\t\t#ifdef DOUBLE_SIDED\n\t\t\tvLightBack += saturate( -dotNL ) * directLightColor_Diffuse;\n\t\t#endif\n\t}\n\t#pragma unroll_loop_end\n#endif\n#if NUM_DIR_LIGHTS > 0\n\t#pragma unroll_loop_start\n\tfor ( int i = 0; i < NUM_DIR_LIGHTS; i ++ ) {\n\t\tgetDirectionalDirectLightIrradiance( directionalLights[ i ], geometry, directLight );\n\t\tdotNL = dot( geometry.normal, directLight.direction );\n\t\tdirectLightColor_Diffuse = PI * directLight.color;\n\t\tvLightFront += saturate( dotNL ) * directLightColor_Diffuse;\n\t\t#ifdef DOUBLE_SIDED\n\t\t\tvLightBack += saturate( -dotNL ) * directLightColor_Diffuse;\n\t\t#endif\n\t}\n\t#pragma unroll_loop_end\n#endif\n#if NUM_HEMI_LIGHTS > 0\n\t#pragma unroll_loop_start\n\tfor ( int i = 0; i < NUM_HEMI_LIGHTS; i ++ ) {\n\t\tvIndirectFront += getHemisphereLightIrradiance( hemisphereLights[ i ], geometry );\n\t\t#ifdef DOUBLE_SIDED\n\t\t\tvIndirectBack += getHemisphereLightIrradiance( hemisphereLights[ i ], backGeometry );\n\t\t#endif\n\t}\n\t#pragma unroll_loop_end\n#endif";
- var lights_pars_begin = "uniform bool receiveShadow;\nuniform vec3 ambientLightColor;\nuniform vec3 lightProbe[ 9 ];\nvec3 shGetIrradianceAt( in vec3 normal, in vec3 shCoefficients[ 9 ] ) {\n\tfloat x = normal.x, y = normal.y, z = normal.z;\n\tvec3 result = shCoefficients[ 0 ] * 0.886227;\n\tresult += shCoefficients[ 1 ] * 2.0 * 0.511664 * y;\n\tresult += shCoefficients[ 2 ] * 2.0 * 0.511664 * z;\n\tresult += shCoefficients[ 3 ] * 2.0 * 0.511664 * x;\n\tresult += shCoefficients[ 4 ] * 2.0 * 0.429043 * x * y;\n\tresult += shCoefficients[ 5 ] * 2.0 * 0.429043 * y * z;\n\tresult += shCoefficients[ 6 ] * ( 0.743125 * z * z - 0.247708 );\n\tresult += shCoefficients[ 7 ] * 2.0 * 0.429043 * x * z;\n\tresult += shCoefficients[ 8 ] * 0.429043 * ( x * x - y * y );\n\treturn result;\n}\nvec3 getLightProbeIrradiance( const in vec3 lightProbe[ 9 ], const in GeometricContext geometry ) {\n\tvec3 worldNormal = inverseTransformDirection( geometry.normal, viewMatrix );\n\tvec3 irradiance = shGetIrradianceAt( worldNormal, lightProbe );\n\treturn irradiance;\n}\nvec3 getAmbientLightIrradiance( const in vec3 ambientLightColor ) {\n\tvec3 irradiance = ambientLightColor;\n\t#ifndef PHYSICALLY_CORRECT_LIGHTS\n\t\tirradiance *= PI;\n\t#endif\n\treturn irradiance;\n}\n#if NUM_DIR_LIGHTS > 0\n\tstruct DirectionalLight {\n\t\tvec3 direction;\n\t\tvec3 color;\n\t};\n\tuniform DirectionalLight directionalLights[ NUM_DIR_LIGHTS ];\n\tvoid getDirectionalDirectLightIrradiance( const in DirectionalLight directionalLight, const in GeometricContext geometry, out IncidentLight directLight ) {\n\t\tdirectLight.color = directionalLight.color;\n\t\tdirectLight.direction = directionalLight.direction;\n\t\tdirectLight.visible = true;\n\t}\n#endif\n#if NUM_POINT_LIGHTS > 0\n\tstruct PointLight {\n\t\tvec3 position;\n\t\tvec3 color;\n\t\tfloat distance;\n\t\tfloat decay;\n\t};\n\tuniform PointLight pointLights[ NUM_POINT_LIGHTS ];\n\tvoid getPointDirectLightIrradiance( const in PointLight pointLight, const in GeometricContext geometry, out IncidentLight directLight ) {\n\t\tvec3 lVector = pointLight.position - geometry.position;\n\t\tdirectLight.direction = normalize( lVector );\n\t\tfloat lightDistance = length( lVector );\n\t\tdirectLight.color = pointLight.color;\n\t\tdirectLight.color *= punctualLightIntensityToIrradianceFactor( lightDistance, pointLight.distance, pointLight.decay );\n\t\tdirectLight.visible = ( directLight.color != vec3( 0.0 ) );\n\t}\n#endif\n#if NUM_SPOT_LIGHTS > 0\n\tstruct SpotLight {\n\t\tvec3 position;\n\t\tvec3 direction;\n\t\tvec3 color;\n\t\tfloat distance;\n\t\tfloat decay;\n\t\tfloat coneCos;\n\t\tfloat penumbraCos;\n\t};\n\tuniform SpotLight spotLights[ NUM_SPOT_LIGHTS ];\n\tvoid getSpotDirectLightIrradiance( const in SpotLight spotLight, const in GeometricContext geometry, out IncidentLight directLight ) {\n\t\tvec3 lVector = spotLight.position - geometry.position;\n\t\tdirectLight.direction = normalize( lVector );\n\t\tfloat lightDistance = length( lVector );\n\t\tfloat angleCos = dot( directLight.direction, spotLight.direction );\n\t\tif ( angleCos > spotLight.coneCos ) {\n\t\t\tfloat spotEffect = smoothstep( spotLight.coneCos, spotLight.penumbraCos, angleCos );\n\t\t\tdirectLight.color = spotLight.color;\n\t\t\tdirectLight.color *= spotEffect * punctualLightIntensityToIrradianceFactor( lightDistance, spotLight.distance, spotLight.decay );\n\t\t\tdirectLight.visible = true;\n\t\t} else {\n\t\t\tdirectLight.color = vec3( 0.0 );\n\t\t\tdirectLight.visible = false;\n\t\t}\n\t}\n#endif\n#if NUM_RECT_AREA_LIGHTS > 0\n\tstruct RectAreaLight {\n\t\tvec3 color;\n\t\tvec3 position;\n\t\tvec3 halfWidth;\n\t\tvec3 halfHeight;\n\t};\n\tuniform sampler2D ltc_1;\tuniform sampler2D ltc_2;\n\tuniform RectAreaLight rectAreaLights[ NUM_RECT_AREA_LIGHTS ];\n#endif\n#if NUM_HEMI_LIGHTS > 0\n\tstruct HemisphereLight {\n\t\tvec3 direction;\n\t\tvec3 skyColor;\n\t\tvec3 groundColor;\n\t};\n\tuniform HemisphereLight hemisphereLights[ NUM_HEMI_LIGHTS ];\n\tvec3 getHemisphereLightIrradiance( const in HemisphereLight hemiLight, const in GeometricContext geometry ) {\n\t\tfloat dotNL = dot( geometry.normal, hemiLight.direction );\n\t\tfloat hemiDiffuseWeight = 0.5 * dotNL + 0.5;\n\t\tvec3 irradiance = mix( hemiLight.groundColor, hemiLight.skyColor, hemiDiffuseWeight );\n\t\t#ifndef PHYSICALLY_CORRECT_LIGHTS\n\t\t\tirradiance *= PI;\n\t\t#endif\n\t\treturn irradiance;\n\t}\n#endif";
- var envmap_physical_pars_fragment = "#if defined( USE_ENVMAP )\n\t#ifdef ENVMAP_MODE_REFRACTION\n\t\tuniform float refractionRatio;\n\t#endif\n\tvec3 getLightProbeIndirectIrradiance( const in GeometricContext geometry, const in int maxMIPLevel ) {\n\t\tvec3 worldNormal = inverseTransformDirection( geometry.normal, viewMatrix );\n\t\t#ifdef ENVMAP_TYPE_CUBE\n\t\t\tvec3 queryVec = vec3( flipEnvMap * worldNormal.x, worldNormal.yz );\n\t\t\t#ifdef TEXTURE_LOD_EXT\n\t\t\t\tvec4 envMapColor = textureCubeLodEXT( envMap, queryVec, float( maxMIPLevel ) );\n\t\t\t#else\n\t\t\t\tvec4 envMapColor = textureCube( envMap, queryVec, float( maxMIPLevel ) );\n\t\t\t#endif\n\t\t\tenvMapColor.rgb = envMapTexelToLinear( envMapColor ).rgb;\n\t\t#elif defined( ENVMAP_TYPE_CUBE_UV )\n\t\t\tvec4 envMapColor = textureCubeUV( envMap, worldNormal, 1.0 );\n\t\t#else\n\t\t\tvec4 envMapColor = vec4( 0.0 );\n\t\t#endif\n\t\treturn PI * envMapColor.rgb * envMapIntensity;\n\t}\n\tfloat getSpecularMIPLevel( const in float roughness, const in int maxMIPLevel ) {\n\t\tfloat maxMIPLevelScalar = float( maxMIPLevel );\n\t\tfloat sigma = PI * roughness * roughness / ( 1.0 + roughness );\n\t\tfloat desiredMIPLevel = maxMIPLevelScalar + log2( sigma );\n\t\treturn clamp( desiredMIPLevel, 0.0, maxMIPLevelScalar );\n\t}\n\tvec3 getLightProbeIndirectRadiance( const in vec3 viewDir, const in vec3 normal, const in float roughness, const in int maxMIPLevel ) {\n\t\t#ifdef ENVMAP_MODE_REFLECTION\n\t\t\tvec3 reflectVec = reflect( -viewDir, normal );\n\t\t\treflectVec = normalize( mix( reflectVec, normal, roughness * roughness) );\n\t\t#else\n\t\t\tvec3 reflectVec = refract( -viewDir, normal, refractionRatio );\n\t\t#endif\n\t\treflectVec = inverseTransformDirection( reflectVec, viewMatrix );\n\t\tfloat specularMIPLevel = getSpecularMIPLevel( roughness, maxMIPLevel );\n\t\t#ifdef ENVMAP_TYPE_CUBE\n\t\t\tvec3 queryReflectVec = vec3( flipEnvMap * reflectVec.x, reflectVec.yz );\n\t\t\t#ifdef TEXTURE_LOD_EXT\n\t\t\t\tvec4 envMapColor = textureCubeLodEXT( envMap, queryReflectVec, specularMIPLevel );\n\t\t\t#else\n\t\t\t\tvec4 envMapColor = textureCube( envMap, queryReflectVec, specularMIPLevel );\n\t\t\t#endif\n\t\t\tenvMapColor.rgb = envMapTexelToLinear( envMapColor ).rgb;\n\t\t#elif defined( ENVMAP_TYPE_CUBE_UV )\n\t\t\tvec4 envMapColor = textureCubeUV( envMap, reflectVec, roughness );\n\t\t#endif\n\t\treturn envMapColor.rgb * envMapIntensity;\n\t}\n#endif";
- var lights_toon_fragment = "ToonMaterial material;\nmaterial.diffuseColor = diffuseColor.rgb;";
- var lights_toon_pars_fragment = "varying vec3 vViewPosition;\n#ifndef FLAT_SHADED\n\tvarying vec3 vNormal;\n#endif\nstruct ToonMaterial {\n\tvec3 diffuseColor;\n};\nvoid RE_Direct_Toon( const in IncidentLight directLight, const in GeometricContext geometry, const in ToonMaterial material, inout ReflectedLight reflectedLight ) {\n\tvec3 irradiance = getGradientIrradiance( geometry.normal, directLight.direction ) * directLight.color;\n\t#ifndef PHYSICALLY_CORRECT_LIGHTS\n\t\tirradiance *= PI;\n\t#endif\n\treflectedLight.directDiffuse += irradiance * BRDF_Diffuse_Lambert( material.diffuseColor );\n}\nvoid RE_IndirectDiffuse_Toon( const in vec3 irradiance, const in GeometricContext geometry, const in ToonMaterial material, inout ReflectedLight reflectedLight ) {\n\treflectedLight.indirectDiffuse += irradiance * BRDF_Diffuse_Lambert( material.diffuseColor );\n}\n#define RE_Direct\t\t\t\tRE_Direct_Toon\n#define RE_IndirectDiffuse\t\tRE_IndirectDiffuse_Toon\n#define Material_LightProbeLOD( material )\t(0)";
- var lights_phong_fragment = "BlinnPhongMaterial material;\nmaterial.diffuseColor = diffuseColor.rgb;\nmaterial.specularColor = specular;\nmaterial.specularShininess = shininess;\nmaterial.specularStrength = specularStrength;";
- var lights_phong_pars_fragment = "varying vec3 vViewPosition;\n#ifndef FLAT_SHADED\n\tvarying vec3 vNormal;\n#endif\nstruct BlinnPhongMaterial {\n\tvec3 diffuseColor;\n\tvec3 specularColor;\n\tfloat specularShininess;\n\tfloat specularStrength;\n};\nvoid RE_Direct_BlinnPhong( const in IncidentLight directLight, const in GeometricContext geometry, const in BlinnPhongMaterial material, inout ReflectedLight reflectedLight ) {\n\tfloat dotNL = saturate( dot( geometry.normal, directLight.direction ) );\n\tvec3 irradiance = dotNL * directLight.color;\n\t#ifndef PHYSICALLY_CORRECT_LIGHTS\n\t\tirradiance *= PI;\n\t#endif\n\treflectedLight.directDiffuse += irradiance * BRDF_Diffuse_Lambert( material.diffuseColor );\n\treflectedLight.directSpecular += irradiance * BRDF_Specular_BlinnPhong( directLight, geometry, material.specularColor, material.specularShininess ) * material.specularStrength;\n}\nvoid RE_IndirectDiffuse_BlinnPhong( const in vec3 irradiance, const in GeometricContext geometry, const in BlinnPhongMaterial material, inout ReflectedLight reflectedLight ) {\n\treflectedLight.indirectDiffuse += irradiance * BRDF_Diffuse_Lambert( material.diffuseColor );\n}\n#define RE_Direct\t\t\t\tRE_Direct_BlinnPhong\n#define RE_IndirectDiffuse\t\tRE_IndirectDiffuse_BlinnPhong\n#define Material_LightProbeLOD( material )\t(0)";
- var lights_physical_fragment = "PhysicalMaterial material;\nmaterial.diffuseColor = diffuseColor.rgb * ( 1.0 - metalnessFactor );\nvec3 dxy = max( abs( dFdx( geometryNormal ) ), abs( dFdy( geometryNormal ) ) );\nfloat geometryRoughness = max( max( dxy.x, dxy.y ), dxy.z );\nmaterial.specularRoughness = max( roughnessFactor, 0.0525 );material.specularRoughness += geometryRoughness;\nmaterial.specularRoughness = min( material.specularRoughness, 1.0 );\n#ifdef REFLECTIVITY\n\tmaterial.specularColor = mix( vec3( MAXIMUM_SPECULAR_COEFFICIENT * pow2( reflectivity ) ), diffuseColor.rgb, metalnessFactor );\n#else\n\tmaterial.specularColor = mix( vec3( DEFAULT_SPECULAR_COEFFICIENT ), diffuseColor.rgb, metalnessFactor );\n#endif\n#ifdef CLEARCOAT\n\tmaterial.clearcoat = clearcoat;\n\tmaterial.clearcoatRoughness = clearcoatRoughness;\n\t#ifdef USE_CLEARCOATMAP\n\t\tmaterial.clearcoat *= texture2D( clearcoatMap, vUv ).x;\n\t#endif\n\t#ifdef USE_CLEARCOAT_ROUGHNESSMAP\n\t\tmaterial.clearcoatRoughness *= texture2D( clearcoatRoughnessMap, vUv ).y;\n\t#endif\n\tmaterial.clearcoat = saturate( material.clearcoat );\tmaterial.clearcoatRoughness = max( material.clearcoatRoughness, 0.0525 );\n\tmaterial.clearcoatRoughness += geometryRoughness;\n\tmaterial.clearcoatRoughness = min( material.clearcoatRoughness, 1.0 );\n#endif\n#ifdef USE_SHEEN\n\tmaterial.sheenColor = sheen;\n#endif";
- var lights_physical_pars_fragment = "struct PhysicalMaterial {\n\tvec3 diffuseColor;\n\tfloat specularRoughness;\n\tvec3 specularColor;\n#ifdef CLEARCOAT\n\tfloat clearcoat;\n\tfloat clearcoatRoughness;\n#endif\n#ifdef USE_SHEEN\n\tvec3 sheenColor;\n#endif\n};\n#define MAXIMUM_SPECULAR_COEFFICIENT 0.16\n#define DEFAULT_SPECULAR_COEFFICIENT 0.04\nfloat clearcoatDHRApprox( const in float roughness, const in float dotNL ) {\n\treturn DEFAULT_SPECULAR_COEFFICIENT + ( 1.0 - DEFAULT_SPECULAR_COEFFICIENT ) * ( pow( 1.0 - dotNL, 5.0 ) * pow( 1.0 - roughness, 2.0 ) );\n}\n#if NUM_RECT_AREA_LIGHTS > 0\n\tvoid RE_Direct_RectArea_Physical( const in RectAreaLight rectAreaLight, const in GeometricContext geometry, const in PhysicalMaterial material, inout ReflectedLight reflectedLight ) {\n\t\tvec3 normal = geometry.normal;\n\t\tvec3 viewDir = geometry.viewDir;\n\t\tvec3 position = geometry.position;\n\t\tvec3 lightPos = rectAreaLight.position;\n\t\tvec3 halfWidth = rectAreaLight.halfWidth;\n\t\tvec3 halfHeight = rectAreaLight.halfHeight;\n\t\tvec3 lightColor = rectAreaLight.color;\n\t\tfloat roughness = material.specularRoughness;\n\t\tvec3 rectCoords[ 4 ];\n\t\trectCoords[ 0 ] = lightPos + halfWidth - halfHeight;\t\trectCoords[ 1 ] = lightPos - halfWidth - halfHeight;\n\t\trectCoords[ 2 ] = lightPos - halfWidth + halfHeight;\n\t\trectCoords[ 3 ] = lightPos + halfWidth + halfHeight;\n\t\tvec2 uv = LTC_Uv( normal, viewDir, roughness );\n\t\tvec4 t1 = texture2D( ltc_1, uv );\n\t\tvec4 t2 = texture2D( ltc_2, uv );\n\t\tmat3 mInv = mat3(\n\t\t\tvec3( t1.x, 0, t1.y ),\n\t\t\tvec3( 0, 1, 0 ),\n\t\t\tvec3( t1.z, 0, t1.w )\n\t\t);\n\t\tvec3 fresnel = ( material.specularColor * t2.x + ( vec3( 1.0 ) - material.specularColor ) * t2.y );\n\t\treflectedLight.directSpecular += lightColor * fresnel * LTC_Evaluate( normal, viewDir, position, mInv, rectCoords );\n\t\treflectedLight.directDiffuse += lightColor * material.diffuseColor * LTC_Evaluate( normal, viewDir, position, mat3( 1.0 ), rectCoords );\n\t}\n#endif\nvoid RE_Direct_Physical( const in IncidentLight directLight, const in GeometricContext geometry, const in PhysicalMaterial material, inout ReflectedLight reflectedLight ) {\n\tfloat dotNL = saturate( dot( geometry.normal, directLight.direction ) );\n\tvec3 irradiance = dotNL * directLight.color;\n\t#ifndef PHYSICALLY_CORRECT_LIGHTS\n\t\tirradiance *= PI;\n\t#endif\n\t#ifdef CLEARCOAT\n\t\tfloat ccDotNL = saturate( dot( geometry.clearcoatNormal, directLight.direction ) );\n\t\tvec3 ccIrradiance = ccDotNL * directLight.color;\n\t\t#ifndef PHYSICALLY_CORRECT_LIGHTS\n\t\t\tccIrradiance *= PI;\n\t\t#endif\n\t\tfloat clearcoatDHR = material.clearcoat * clearcoatDHRApprox( material.clearcoatRoughness, ccDotNL );\n\t\treflectedLight.directSpecular += ccIrradiance * material.clearcoat * BRDF_Specular_GGX( directLight, geometry.viewDir, geometry.clearcoatNormal, vec3( DEFAULT_SPECULAR_COEFFICIENT ), material.clearcoatRoughness );\n\t#else\n\t\tfloat clearcoatDHR = 0.0;\n\t#endif\n\t#ifdef USE_SHEEN\n\t\treflectedLight.directSpecular += ( 1.0 - clearcoatDHR ) * irradiance * BRDF_Specular_Sheen(\n\t\t\tmaterial.specularRoughness,\n\t\t\tdirectLight.direction,\n\t\t\tgeometry,\n\t\t\tmaterial.sheenColor\n\t\t);\n\t#else\n\t\treflectedLight.directSpecular += ( 1.0 - clearcoatDHR ) * irradiance * BRDF_Specular_GGX( directLight, geometry.viewDir, geometry.normal, material.specularColor, material.specularRoughness);\n\t#endif\n\treflectedLight.directDiffuse += ( 1.0 - clearcoatDHR ) * irradiance * BRDF_Diffuse_Lambert( material.diffuseColor );\n}\nvoid RE_IndirectDiffuse_Physical( const in vec3 irradiance, const in GeometricContext geometry, const in PhysicalMaterial material, inout ReflectedLight reflectedLight ) {\n\treflectedLight.indirectDiffuse += irradiance * BRDF_Diffuse_Lambert( material.diffuseColor );\n}\nvoid RE_IndirectSpecular_Physical( const in vec3 radiance, const in vec3 irradiance, const in vec3 clearcoatRadiance, const in GeometricContext geometry, const in PhysicalMaterial material, inout ReflectedLight reflectedLight) {\n\t#ifdef CLEARCOAT\n\t\tfloat ccDotNV = saturate( dot( geometry.clearcoatNormal, geometry.viewDir ) );\n\t\treflectedLight.indirectSpecular += clearcoatRadiance * material.clearcoat * BRDF_Specular_GGX_Environment( geometry.viewDir, geometry.clearcoatNormal, vec3( DEFAULT_SPECULAR_COEFFICIENT ), material.clearcoatRoughness );\n\t\tfloat ccDotNL = ccDotNV;\n\t\tfloat clearcoatDHR = material.clearcoat * clearcoatDHRApprox( material.clearcoatRoughness, ccDotNL );\n\t#else\n\t\tfloat clearcoatDHR = 0.0;\n\t#endif\n\tfloat clearcoatInv = 1.0 - clearcoatDHR;\n\tvec3 singleScattering = vec3( 0.0 );\n\tvec3 multiScattering = vec3( 0.0 );\n\tvec3 cosineWeightedIrradiance = irradiance * RECIPROCAL_PI;\n\tBRDF_Specular_Multiscattering_Environment( geometry, material.specularColor, material.specularRoughness, singleScattering, multiScattering );\n\tvec3 diffuse = material.diffuseColor * ( 1.0 - ( singleScattering + multiScattering ) );\n\treflectedLight.indirectSpecular += clearcoatInv * radiance * singleScattering;\n\treflectedLight.indirectSpecular += multiScattering * cosineWeightedIrradiance;\n\treflectedLight.indirectDiffuse += diffuse * cosineWeightedIrradiance;\n}\n#define RE_Direct\t\t\t\tRE_Direct_Physical\n#define RE_Direct_RectArea\t\tRE_Direct_RectArea_Physical\n#define RE_IndirectDiffuse\t\tRE_IndirectDiffuse_Physical\n#define RE_IndirectSpecular\t\tRE_IndirectSpecular_Physical\nfloat computeSpecularOcclusion( const in float dotNV, const in float ambientOcclusion, const in float roughness ) {\n\treturn saturate( pow( dotNV + ambientOcclusion, exp2( - 16.0 * roughness - 1.0 ) ) - 1.0 + ambientOcclusion );\n}";
- var lights_fragment_begin = "\nGeometricContext geometry;\ngeometry.position = - vViewPosition;\ngeometry.normal = normal;\ngeometry.viewDir = ( isOrthographic ) ? vec3( 0, 0, 1 ) : normalize( vViewPosition );\n#ifdef CLEARCOAT\n\tgeometry.clearcoatNormal = clearcoatNormal;\n#endif\nIncidentLight directLight;\n#if ( NUM_POINT_LIGHTS > 0 ) && defined( RE_Direct )\n\tPointLight pointLight;\n\t#if defined( USE_SHADOWMAP ) && NUM_POINT_LIGHT_SHADOWS > 0\n\tPointLightShadow pointLightShadow;\n\t#endif\n\t#pragma unroll_loop_start\n\tfor ( int i = 0; i < NUM_POINT_LIGHTS; i ++ ) {\n\t\tpointLight = pointLights[ i ];\n\t\tgetPointDirectLightIrradiance( pointLight, geometry, directLight );\n\t\t#if defined( USE_SHADOWMAP ) && ( UNROLLED_LOOP_INDEX < NUM_POINT_LIGHT_SHADOWS )\n\t\tpointLightShadow = pointLightShadows[ i ];\n\t\tdirectLight.color *= all( bvec2( directLight.visible, receiveShadow ) ) ? getPointShadow( pointShadowMap[ i ], pointLightShadow.shadowMapSize, pointLightShadow.shadowBias, pointLightShadow.shadowRadius, vPointShadowCoord[ i ], pointLightShadow.shadowCameraNear, pointLightShadow.shadowCameraFar ) : 1.0;\n\t\t#endif\n\t\tRE_Direct( directLight, geometry, material, reflectedLight );\n\t}\n\t#pragma unroll_loop_end\n#endif\n#if ( NUM_SPOT_LIGHTS > 0 ) && defined( RE_Direct )\n\tSpotLight spotLight;\n\t#if defined( USE_SHADOWMAP ) && NUM_SPOT_LIGHT_SHADOWS > 0\n\tSpotLightShadow spotLightShadow;\n\t#endif\n\t#pragma unroll_loop_start\n\tfor ( int i = 0; i < NUM_SPOT_LIGHTS; i ++ ) {\n\t\tspotLight = spotLights[ i ];\n\t\tgetSpotDirectLightIrradiance( spotLight, geometry, directLight );\n\t\t#if defined( USE_SHADOWMAP ) && ( UNROLLED_LOOP_INDEX < NUM_SPOT_LIGHT_SHADOWS )\n\t\tspotLightShadow = spotLightShadows[ i ];\n\t\tdirectLight.color *= all( bvec2( directLight.visible, receiveShadow ) ) ? getShadow( spotShadowMap[ i ], spotLightShadow.shadowMapSize, spotLightShadow.shadowBias, spotLightShadow.shadowRadius, vSpotShadowCoord[ i ] ) : 1.0;\n\t\t#endif\n\t\tRE_Direct( directLight, geometry, material, reflectedLight );\n\t}\n\t#pragma unroll_loop_end\n#endif\n#if ( NUM_DIR_LIGHTS > 0 ) && defined( RE_Direct )\n\tDirectionalLight directionalLight;\n\t#if defined( USE_SHADOWMAP ) && NUM_DIR_LIGHT_SHADOWS > 0\n\tDirectionalLightShadow directionalLightShadow;\n\t#endif\n\t#pragma unroll_loop_start\n\tfor ( int i = 0; i < NUM_DIR_LIGHTS; i ++ ) {\n\t\tdirectionalLight = directionalLights[ i ];\n\t\tgetDirectionalDirectLightIrradiance( directionalLight, geometry, directLight );\n\t\t#if defined( USE_SHADOWMAP ) && ( UNROLLED_LOOP_INDEX < NUM_DIR_LIGHT_SHADOWS )\n\t\tdirectionalLightShadow = directionalLightShadows[ i ];\n\t\tdirectLight.color *= all( bvec2( directLight.visible, receiveShadow ) ) ? getShadow( directionalShadowMap[ i ], directionalLightShadow.shadowMapSize, directionalLightShadow.shadowBias, directionalLightShadow.shadowRadius, vDirectionalShadowCoord[ i ] ) : 1.0;\n\t\t#endif\n\t\tRE_Direct( directLight, geometry, material, reflectedLight );\n\t}\n\t#pragma unroll_loop_end\n#endif\n#if ( NUM_RECT_AREA_LIGHTS > 0 ) && defined( RE_Direct_RectArea )\n\tRectAreaLight rectAreaLight;\n\t#pragma unroll_loop_start\n\tfor ( int i = 0; i < NUM_RECT_AREA_LIGHTS; i ++ ) {\n\t\trectAreaLight = rectAreaLights[ i ];\n\t\tRE_Direct_RectArea( rectAreaLight, geometry, material, reflectedLight );\n\t}\n\t#pragma unroll_loop_end\n#endif\n#if defined( RE_IndirectDiffuse )\n\tvec3 iblIrradiance = vec3( 0.0 );\n\tvec3 irradiance = getAmbientLightIrradiance( ambientLightColor );\n\tirradiance += getLightProbeIrradiance( lightProbe, geometry );\n\t#if ( NUM_HEMI_LIGHTS > 0 )\n\t\t#pragma unroll_loop_start\n\t\tfor ( int i = 0; i < NUM_HEMI_LIGHTS; i ++ ) {\n\t\t\tirradiance += getHemisphereLightIrradiance( hemisphereLights[ i ], geometry );\n\t\t}\n\t\t#pragma unroll_loop_end\n\t#endif\n#endif\n#if defined( RE_IndirectSpecular )\n\tvec3 radiance = vec3( 0.0 );\n\tvec3 clearcoatRadiance = vec3( 0.0 );\n#endif";
- var lights_fragment_maps = "#if defined( RE_IndirectDiffuse )\n\t#ifdef USE_LIGHTMAP\n\t\tvec4 lightMapTexel= texture2D( lightMap, vUv2 );\n\t\tvec3 lightMapIrradiance = lightMapTexelToLinear( lightMapTexel ).rgb * lightMapIntensity;\n\t\t#ifndef PHYSICALLY_CORRECT_LIGHTS\n\t\t\tlightMapIrradiance *= PI;\n\t\t#endif\n\t\tirradiance += lightMapIrradiance;\n\t#endif\n\t#if defined( USE_ENVMAP ) && defined( STANDARD ) && defined( ENVMAP_TYPE_CUBE_UV )\n\t\tiblIrradiance += getLightProbeIndirectIrradiance( geometry, maxMipLevel );\n\t#endif\n#endif\n#if defined( USE_ENVMAP ) && defined( RE_IndirectSpecular )\n\tradiance += getLightProbeIndirectRadiance( geometry.viewDir, geometry.normal, material.specularRoughness, maxMipLevel );\n\t#ifdef CLEARCOAT\n\t\tclearcoatRadiance += getLightProbeIndirectRadiance( geometry.viewDir, geometry.clearcoatNormal, material.clearcoatRoughness, maxMipLevel );\n\t#endif\n#endif";
- var lights_fragment_end = "#if defined( RE_IndirectDiffuse )\n\tRE_IndirectDiffuse( irradiance, geometry, material, reflectedLight );\n#endif\n#if defined( RE_IndirectSpecular )\n\tRE_IndirectSpecular( radiance, iblIrradiance, clearcoatRadiance, geometry, material, reflectedLight );\n#endif";
- var logdepthbuf_fragment = "#if defined( USE_LOGDEPTHBUF ) && defined( USE_LOGDEPTHBUF_EXT )\n\tgl_FragDepthEXT = vIsPerspective == 0.0 ? gl_FragCoord.z : log2( vFragDepth ) * logDepthBufFC * 0.5;\n#endif";
- var logdepthbuf_pars_fragment = "#if defined( USE_LOGDEPTHBUF ) && defined( USE_LOGDEPTHBUF_EXT )\n\tuniform float logDepthBufFC;\n\tvarying float vFragDepth;\n\tvarying float vIsPerspective;\n#endif";
- var logdepthbuf_pars_vertex = "#ifdef USE_LOGDEPTHBUF\n\t#ifdef USE_LOGDEPTHBUF_EXT\n\t\tvarying float vFragDepth;\n\t\tvarying float vIsPerspective;\n\t#else\n\t\tuniform float logDepthBufFC;\n\t#endif\n#endif";
- var logdepthbuf_vertex = "#ifdef USE_LOGDEPTHBUF\n\t#ifdef USE_LOGDEPTHBUF_EXT\n\t\tvFragDepth = 1.0 + gl_Position.w;\n\t\tvIsPerspective = float( isPerspectiveMatrix( projectionMatrix ) );\n\t#else\n\t\tif ( isPerspectiveMatrix( projectionMatrix ) ) {\n\t\t\tgl_Position.z = log2( max( EPSILON, gl_Position.w + 1.0 ) ) * logDepthBufFC - 1.0;\n\t\t\tgl_Position.z *= gl_Position.w;\n\t\t}\n\t#endif\n#endif";
- var map_fragment = "#ifdef USE_MAP\n\tvec4 texelColor = texture2D( map, vUv );\n\ttexelColor = mapTexelToLinear( texelColor );\n\tdiffuseColor *= texelColor;\n#endif";
- var map_pars_fragment = "#ifdef USE_MAP\n\tuniform sampler2D map;\n#endif";
- var map_particle_fragment = "#if defined( USE_MAP ) || defined( USE_ALPHAMAP )\n\tvec2 uv = ( uvTransform * vec3( gl_PointCoord.x, 1.0 - gl_PointCoord.y, 1 ) ).xy;\n#endif\n#ifdef USE_MAP\n\tvec4 mapTexel = texture2D( map, uv );\n\tdiffuseColor *= mapTexelToLinear( mapTexel );\n#endif\n#ifdef USE_ALPHAMAP\n\tdiffuseColor.a *= texture2D( alphaMap, uv ).g;\n#endif";
- var map_particle_pars_fragment = "#if defined( USE_MAP ) || defined( USE_ALPHAMAP )\n\tuniform mat3 uvTransform;\n#endif\n#ifdef USE_MAP\n\tuniform sampler2D map;\n#endif\n#ifdef USE_ALPHAMAP\n\tuniform sampler2D alphaMap;\n#endif";
- var metalnessmap_fragment = "float metalnessFactor = metalness;\n#ifdef USE_METALNESSMAP\n\tvec4 texelMetalness = texture2D( metalnessMap, vUv );\n\tmetalnessFactor *= texelMetalness.b;\n#endif";
- var metalnessmap_pars_fragment = "#ifdef USE_METALNESSMAP\n\tuniform sampler2D metalnessMap;\n#endif";
- var morphnormal_vertex = "#ifdef USE_MORPHNORMALS\n\tobjectNormal *= morphTargetBaseInfluence;\n\tobjectNormal += morphNormal0 * morphTargetInfluences[ 0 ];\n\tobjectNormal += morphNormal1 * morphTargetInfluences[ 1 ];\n\tobjectNormal += morphNormal2 * morphTargetInfluences[ 2 ];\n\tobjectNormal += morphNormal3 * morphTargetInfluences[ 3 ];\n#endif";
- var morphtarget_pars_vertex = "#ifdef USE_MORPHTARGETS\n\tuniform float morphTargetBaseInfluence;\n\t#ifndef USE_MORPHNORMALS\n\t\tuniform float morphTargetInfluences[ 8 ];\n\t#else\n\t\tuniform float morphTargetInfluences[ 4 ];\n\t#endif\n#endif";
- var morphtarget_vertex = "#ifdef USE_MORPHTARGETS\n\ttransformed *= morphTargetBaseInfluence;\n\ttransformed += morphTarget0 * morphTargetInfluences[ 0 ];\n\ttransformed += morphTarget1 * morphTargetInfluences[ 1 ];\n\ttransformed += morphTarget2 * morphTargetInfluences[ 2 ];\n\ttransformed += morphTarget3 * morphTargetInfluences[ 3 ];\n\t#ifndef USE_MORPHNORMALS\n\t\ttransformed += morphTarget4 * morphTargetInfluences[ 4 ];\n\t\ttransformed += morphTarget5 * morphTargetInfluences[ 5 ];\n\t\ttransformed += morphTarget6 * morphTargetInfluences[ 6 ];\n\t\ttransformed += morphTarget7 * morphTargetInfluences[ 7 ];\n\t#endif\n#endif";
- var normal_fragment_begin = "#ifdef FLAT_SHADED\n\tvec3 fdx = vec3( dFdx( vViewPosition.x ), dFdx( vViewPosition.y ), dFdx( vViewPosition.z ) );\n\tvec3 fdy = vec3( dFdy( vViewPosition.x ), dFdy( vViewPosition.y ), dFdy( vViewPosition.z ) );\n\tvec3 normal = normalize( cross( fdx, fdy ) );\n#else\n\tvec3 normal = normalize( vNormal );\n\t#ifdef DOUBLE_SIDED\n\t\tnormal = normal * ( float( gl_FrontFacing ) * 2.0 - 1.0 );\n\t#endif\n\t#ifdef USE_TANGENT\n\t\tvec3 tangent = normalize( vTangent );\n\t\tvec3 bitangent = normalize( vBitangent );\n\t\t#ifdef DOUBLE_SIDED\n\t\t\ttangent = tangent * ( float( gl_FrontFacing ) * 2.0 - 1.0 );\n\t\t\tbitangent = bitangent * ( float( gl_FrontFacing ) * 2.0 - 1.0 );\n\t\t#endif\n\t\t#if defined( TANGENTSPACE_NORMALMAP ) || defined( USE_CLEARCOAT_NORMALMAP )\n\t\t\tmat3 vTBN = mat3( tangent, bitangent, normal );\n\t\t#endif\n\t#endif\n#endif\nvec3 geometryNormal = normal;";
- var normal_fragment_maps = "#ifdef OBJECTSPACE_NORMALMAP\n\tnormal = texture2D( normalMap, vUv ).xyz * 2.0 - 1.0;\n\t#ifdef FLIP_SIDED\n\t\tnormal = - normal;\n\t#endif\n\t#ifdef DOUBLE_SIDED\n\t\tnormal = normal * ( float( gl_FrontFacing ) * 2.0 - 1.0 );\n\t#endif\n\tnormal = normalize( normalMatrix * normal );\n#elif defined( TANGENTSPACE_NORMALMAP )\n\tvec3 mapN = texture2D( normalMap, vUv ).xyz * 2.0 - 1.0;\n\tmapN.xy *= normalScale;\n\t#ifdef USE_TANGENT\n\t\tnormal = normalize( vTBN * mapN );\n\t#else\n\t\tnormal = perturbNormal2Arb( -vViewPosition, normal, mapN );\n\t#endif\n#elif defined( USE_BUMPMAP )\n\tnormal = perturbNormalArb( -vViewPosition, normal, dHdxy_fwd() );\n#endif";
- var normalmap_pars_fragment = "#ifdef USE_NORMALMAP\n\tuniform sampler2D normalMap;\n\tuniform vec2 normalScale;\n#endif\n#ifdef OBJECTSPACE_NORMALMAP\n\tuniform mat3 normalMatrix;\n#endif\n#if ! defined ( USE_TANGENT ) && ( defined ( TANGENTSPACE_NORMALMAP ) || defined ( USE_CLEARCOAT_NORMALMAP ) )\n\tvec3 perturbNormal2Arb( vec3 eye_pos, vec3 surf_norm, vec3 mapN ) {\n\t\tvec3 q0 = vec3( dFdx( eye_pos.x ), dFdx( eye_pos.y ), dFdx( eye_pos.z ) );\n\t\tvec3 q1 = vec3( dFdy( eye_pos.x ), dFdy( eye_pos.y ), dFdy( eye_pos.z ) );\n\t\tvec2 st0 = dFdx( vUv.st );\n\t\tvec2 st1 = dFdy( vUv.st );\n\t\tfloat scale = sign( st1.t * st0.s - st0.t * st1.s );\n\t\tvec3 S = normalize( ( q0 * st1.t - q1 * st0.t ) * scale );\n\t\tvec3 T = normalize( ( - q0 * st1.s + q1 * st0.s ) * scale );\n\t\tvec3 N = normalize( surf_norm );\n\t\tmat3 tsn = mat3( S, T, N );\n\t\tmapN.xy *= ( float( gl_FrontFacing ) * 2.0 - 1.0 );\n\t\treturn normalize( tsn * mapN );\n\t}\n#endif";
- var clearcoat_normal_fragment_begin = "#ifdef CLEARCOAT\n\tvec3 clearcoatNormal = geometryNormal;\n#endif";
- var clearcoat_normal_fragment_maps = "#ifdef USE_CLEARCOAT_NORMALMAP\n\tvec3 clearcoatMapN = texture2D( clearcoatNormalMap, vUv ).xyz * 2.0 - 1.0;\n\tclearcoatMapN.xy *= clearcoatNormalScale;\n\t#ifdef USE_TANGENT\n\t\tclearcoatNormal = normalize( vTBN * clearcoatMapN );\n\t#else\n\t\tclearcoatNormal = perturbNormal2Arb( - vViewPosition, clearcoatNormal, clearcoatMapN );\n\t#endif\n#endif";
- var clearcoat_pars_fragment = "#ifdef USE_CLEARCOATMAP\n\tuniform sampler2D clearcoatMap;\n#endif\n#ifdef USE_CLEARCOAT_ROUGHNESSMAP\n\tuniform sampler2D clearcoatRoughnessMap;\n#endif\n#ifdef USE_CLEARCOAT_NORMALMAP\n\tuniform sampler2D clearcoatNormalMap;\n\tuniform vec2 clearcoatNormalScale;\n#endif";
- var packing = "vec3 packNormalToRGB( const in vec3 normal ) {\n\treturn normalize( normal ) * 0.5 + 0.5;\n}\nvec3 unpackRGBToNormal( const in vec3 rgb ) {\n\treturn 2.0 * rgb.xyz - 1.0;\n}\nconst float PackUpscale = 256. / 255.;const float UnpackDownscale = 255. / 256.;\nconst vec3 PackFactors = vec3( 256. * 256. * 256., 256. * 256., 256. );\nconst vec4 UnpackFactors = UnpackDownscale / vec4( PackFactors, 1. );\nconst float ShiftRight8 = 1. / 256.;\nvec4 packDepthToRGBA( const in float v ) {\n\tvec4 r = vec4( fract( v * PackFactors ), v );\n\tr.yzw -= r.xyz * ShiftRight8;\treturn r * PackUpscale;\n}\nfloat unpackRGBAToDepth( const in vec4 v ) {\n\treturn dot( v, UnpackFactors );\n}\nvec4 pack2HalfToRGBA( vec2 v ) {\n\tvec4 r = vec4( v.x, fract( v.x * 255.0 ), v.y, fract( v.y * 255.0 ));\n\treturn vec4( r.x - r.y / 255.0, r.y, r.z - r.w / 255.0, r.w);\n}\nvec2 unpackRGBATo2Half( vec4 v ) {\n\treturn vec2( v.x + ( v.y / 255.0 ), v.z + ( v.w / 255.0 ) );\n}\nfloat viewZToOrthographicDepth( const in float viewZ, const in float near, const in float far ) {\n\treturn ( viewZ + near ) / ( near - far );\n}\nfloat orthographicDepthToViewZ( const in float linearClipZ, const in float near, const in float far ) {\n\treturn linearClipZ * ( near - far ) - near;\n}\nfloat viewZToPerspectiveDepth( const in float viewZ, const in float near, const in float far ) {\n\treturn (( near + viewZ ) * far ) / (( far - near ) * viewZ );\n}\nfloat perspectiveDepthToViewZ( const in float invClipZ, const in float near, const in float far ) {\n\treturn ( near * far ) / ( ( far - near ) * invClipZ - far );\n}";
- var premultiplied_alpha_fragment = "#ifdef PREMULTIPLIED_ALPHA\n\tgl_FragColor.rgb *= gl_FragColor.a;\n#endif";
- var project_vertex = "vec4 mvPosition = vec4( transformed, 1.0 );\n#ifdef USE_INSTANCING\n\tmvPosition = instanceMatrix * mvPosition;\n#endif\nmvPosition = modelViewMatrix * mvPosition;\ngl_Position = projectionMatrix * mvPosition;";
- var dithering_fragment = "#ifdef DITHERING\n\tgl_FragColor.rgb = dithering( gl_FragColor.rgb );\n#endif";
- var dithering_pars_fragment = "#ifdef DITHERING\n\tvec3 dithering( vec3 color ) {\n\t\tfloat grid_position = rand( gl_FragCoord.xy );\n\t\tvec3 dither_shift_RGB = vec3( 0.25 / 255.0, -0.25 / 255.0, 0.25 / 255.0 );\n\t\tdither_shift_RGB = mix( 2.0 * dither_shift_RGB, -2.0 * dither_shift_RGB, grid_position );\n\t\treturn color + dither_shift_RGB;\n\t}\n#endif";
- var roughnessmap_fragment = "float roughnessFactor = roughness;\n#ifdef USE_ROUGHNESSMAP\n\tvec4 texelRoughness = texture2D( roughnessMap, vUv );\n\troughnessFactor *= texelRoughness.g;\n#endif";
- var roughnessmap_pars_fragment = "#ifdef USE_ROUGHNESSMAP\n\tuniform sampler2D roughnessMap;\n#endif";
- var shadowmap_pars_fragment = "#ifdef USE_SHADOWMAP\n\t#if NUM_DIR_LIGHT_SHADOWS > 0\n\t\tuniform sampler2D directionalShadowMap[ NUM_DIR_LIGHT_SHADOWS ];\n\t\tvarying vec4 vDirectionalShadowCoord[ NUM_DIR_LIGHT_SHADOWS ];\n\t\tstruct DirectionalLightShadow {\n\t\t\tfloat shadowBias;\n\t\t\tfloat shadowNormalBias;\n\t\t\tfloat shadowRadius;\n\t\t\tvec2 shadowMapSize;\n\t\t};\n\t\tuniform DirectionalLightShadow directionalLightShadows[ NUM_DIR_LIGHT_SHADOWS ];\n\t#endif\n\t#if NUM_SPOT_LIGHT_SHADOWS > 0\n\t\tuniform sampler2D spotShadowMap[ NUM_SPOT_LIGHT_SHADOWS ];\n\t\tvarying vec4 vSpotShadowCoord[ NUM_SPOT_LIGHT_SHADOWS ];\n\t\tstruct SpotLightShadow {\n\t\t\tfloat shadowBias;\n\t\t\tfloat shadowNormalBias;\n\t\t\tfloat shadowRadius;\n\t\t\tvec2 shadowMapSize;\n\t\t};\n\t\tuniform SpotLightShadow spotLightShadows[ NUM_SPOT_LIGHT_SHADOWS ];\n\t#endif\n\t#if NUM_POINT_LIGHT_SHADOWS > 0\n\t\tuniform sampler2D pointShadowMap[ NUM_POINT_LIGHT_SHADOWS ];\n\t\tvarying vec4 vPointShadowCoord[ NUM_POINT_LIGHT_SHADOWS ];\n\t\tstruct PointLightShadow {\n\t\t\tfloat shadowBias;\n\t\t\tfloat shadowNormalBias;\n\t\t\tfloat shadowRadius;\n\t\t\tvec2 shadowMapSize;\n\t\t\tfloat shadowCameraNear;\n\t\t\tfloat shadowCameraFar;\n\t\t};\n\t\tuniform PointLightShadow pointLightShadows[ NUM_POINT_LIGHT_SHADOWS ];\n\t#endif\n\tfloat texture2DCompare( sampler2D depths, vec2 uv, float compare ) {\n\t\treturn step( compare, unpackRGBAToDepth( texture2D( depths, uv ) ) );\n\t}\n\tvec2 texture2DDistribution( sampler2D shadow, vec2 uv ) {\n\t\treturn unpackRGBATo2Half( texture2D( shadow, uv ) );\n\t}\n\tfloat VSMShadow (sampler2D shadow, vec2 uv, float compare ){\n\t\tfloat occlusion = 1.0;\n\t\tvec2 distribution = texture2DDistribution( shadow, uv );\n\t\tfloat hard_shadow = step( compare , distribution.x );\n\t\tif (hard_shadow != 1.0 ) {\n\t\t\tfloat distance = compare - distribution.x ;\n\t\t\tfloat variance = max( 0.00000, distribution.y * distribution.y );\n\t\t\tfloat softness_probability = variance / (variance + distance * distance );\t\t\tsoftness_probability = clamp( ( softness_probability - 0.3 ) / ( 0.95 - 0.3 ), 0.0, 1.0 );\t\t\tocclusion = clamp( max( hard_shadow, softness_probability ), 0.0, 1.0 );\n\t\t}\n\t\treturn occlusion;\n\t}\n\tfloat getShadow( sampler2D shadowMap, vec2 shadowMapSize, float shadowBias, float shadowRadius, vec4 shadowCoord ) {\n\t\tfloat shadow = 1.0;\n\t\tshadowCoord.xyz /= shadowCoord.w;\n\t\tshadowCoord.z += shadowBias;\n\t\tbvec4 inFrustumVec = bvec4 ( shadowCoord.x >= 0.0, shadowCoord.x <= 1.0, shadowCoord.y >= 0.0, shadowCoord.y <= 1.0 );\n\t\tbool inFrustum = all( inFrustumVec );\n\t\tbvec2 frustumTestVec = bvec2( inFrustum, shadowCoord.z <= 1.0 );\n\t\tbool frustumTest = all( frustumTestVec );\n\t\tif ( frustumTest ) {\n\t\t#if defined( SHADOWMAP_TYPE_PCF )\n\t\t\tvec2 texelSize = vec2( 1.0 ) / shadowMapSize;\n\t\t\tfloat dx0 = - texelSize.x * shadowRadius;\n\t\t\tfloat dy0 = - texelSize.y * shadowRadius;\n\t\t\tfloat dx1 = + texelSize.x * shadowRadius;\n\t\t\tfloat dy1 = + texelSize.y * shadowRadius;\n\t\t\tfloat dx2 = dx0 / 2.0;\n\t\t\tfloat dy2 = dy0 / 2.0;\n\t\t\tfloat dx3 = dx1 / 2.0;\n\t\t\tfloat dy3 = dy1 / 2.0;\n\t\t\tshadow = (\n\t\t\t\ttexture2DCompare( shadowMap, shadowCoord.xy + vec2( dx0, dy0 ), shadowCoord.z ) +\n\t\t\t\ttexture2DCompare( shadowMap, shadowCoord.xy + vec2( 0.0, dy0 ), shadowCoord.z ) +\n\t\t\t\ttexture2DCompare( shadowMap, shadowCoord.xy + vec2( dx1, dy0 ), shadowCoord.z ) +\n\t\t\t\ttexture2DCompare( shadowMap, shadowCoord.xy + vec2( dx2, dy2 ), shadowCoord.z ) +\n\t\t\t\ttexture2DCompare( shadowMap, shadowCoord.xy + vec2( 0.0, dy2 ), shadowCoord.z ) +\n\t\t\t\ttexture2DCompare( shadowMap, shadowCoord.xy + vec2( dx3, dy2 ), shadowCoord.z ) +\n\t\t\t\ttexture2DCompare( shadowMap, shadowCoord.xy + vec2( dx0, 0.0 ), shadowCoord.z ) +\n\t\t\t\ttexture2DCompare( shadowMap, shadowCoord.xy + vec2( dx2, 0.0 ), shadowCoord.z ) +\n\t\t\t\ttexture2DCompare( shadowMap, shadowCoord.xy, shadowCoord.z ) +\n\t\t\t\ttexture2DCompare( shadowMap, shadowCoord.xy + vec2( dx3, 0.0 ), shadowCoord.z ) +\n\t\t\t\ttexture2DCompare( shadowMap, shadowCoord.xy + vec2( dx1, 0.0 ), shadowCoord.z ) +\n\t\t\t\ttexture2DCompare( shadowMap, shadowCoord.xy + vec2( dx2, dy3 ), shadowCoord.z ) +\n\t\t\t\ttexture2DCompare( shadowMap, shadowCoord.xy + vec2( 0.0, dy3 ), shadowCoord.z ) +\n\t\t\t\ttexture2DCompare( shadowMap, shadowCoord.xy + vec2( dx3, dy3 ), shadowCoord.z ) +\n\t\t\t\ttexture2DCompare( shadowMap, shadowCoord.xy + vec2( dx0, dy1 ), shadowCoord.z ) +\n\t\t\t\ttexture2DCompare( shadowMap, shadowCoord.xy + vec2( 0.0, dy1 ), shadowCoord.z ) +\n\t\t\t\ttexture2DCompare( shadowMap, shadowCoord.xy + vec2( dx1, dy1 ), shadowCoord.z )\n\t\t\t) * ( 1.0 / 17.0 );\n\t\t#elif defined( SHADOWMAP_TYPE_PCF_SOFT )\n\t\t\tvec2 texelSize = vec2( 1.0 ) / shadowMapSize;\n\t\t\tfloat dx = texelSize.x;\n\t\t\tfloat dy = texelSize.y;\n\t\t\tvec2 uv = shadowCoord.xy;\n\t\t\tvec2 f = fract( uv * shadowMapSize + 0.5 );\n\t\t\tuv -= f * texelSize;\n\t\t\tshadow = (\n\t\t\t\ttexture2DCompare( shadowMap, uv, shadowCoord.z ) +\n\t\t\t\ttexture2DCompare( shadowMap, uv + vec2( dx, 0.0 ), shadowCoord.z ) +\n\t\t\t\ttexture2DCompare( shadowMap, uv + vec2( 0.0, dy ), shadowCoord.z ) +\n\t\t\t\ttexture2DCompare( shadowMap, uv + texelSize, shadowCoord.z ) +\n\t\t\t\tmix( texture2DCompare( shadowMap, uv + vec2( -dx, 0.0 ), shadowCoord.z ), \n\t\t\t\t\t texture2DCompare( shadowMap, uv + vec2( 2.0 * dx, 0.0 ), shadowCoord.z ),\n\t\t\t\t\t f.x ) +\n\t\t\t\tmix( texture2DCompare( shadowMap, uv + vec2( -dx, dy ), shadowCoord.z ), \n\t\t\t\t\t texture2DCompare( shadowMap, uv + vec2( 2.0 * dx, dy ), shadowCoord.z ),\n\t\t\t\t\t f.x ) +\n\t\t\t\tmix( texture2DCompare( shadowMap, uv + vec2( 0.0, -dy ), shadowCoord.z ), \n\t\t\t\t\t texture2DCompare( shadowMap, uv + vec2( 0.0, 2.0 * dy ), shadowCoord.z ),\n\t\t\t\t\t f.y ) +\n\t\t\t\tmix( texture2DCompare( shadowMap, uv + vec2( dx, -dy ), shadowCoord.z ), \n\t\t\t\t\t texture2DCompare( shadowMap, uv + vec2( dx, 2.0 * dy ), shadowCoord.z ),\n\t\t\t\t\t f.y ) +\n\t\t\t\tmix( mix( texture2DCompare( shadowMap, uv + vec2( -dx, -dy ), shadowCoord.z ), \n\t\t\t\t\t\t texture2DCompare( shadowMap, uv + vec2( 2.0 * dx, -dy ), shadowCoord.z ),\n\t\t\t\t\t\t f.x ),\n\t\t\t\t\t mix( texture2DCompare( shadowMap, uv + vec2( -dx, 2.0 * dy ), shadowCoord.z ), \n\t\t\t\t\t\t texture2DCompare( shadowMap, uv + vec2( 2.0 * dx, 2.0 * dy ), shadowCoord.z ),\n\t\t\t\t\t\t f.x ),\n\t\t\t\t\t f.y )\n\t\t\t) * ( 1.0 / 9.0 );\n\t\t#elif defined( SHADOWMAP_TYPE_VSM )\n\t\t\tshadow = VSMShadow( shadowMap, shadowCoord.xy, shadowCoord.z );\n\t\t#else\n\t\t\tshadow = texture2DCompare( shadowMap, shadowCoord.xy, shadowCoord.z );\n\t\t#endif\n\t\t}\n\t\treturn shadow;\n\t}\n\tvec2 cubeToUV( vec3 v, float texelSizeY ) {\n\t\tvec3 absV = abs( v );\n\t\tfloat scaleToCube = 1.0 / max( absV.x, max( absV.y, absV.z ) );\n\t\tabsV *= scaleToCube;\n\t\tv *= scaleToCube * ( 1.0 - 2.0 * texelSizeY );\n\t\tvec2 planar = v.xy;\n\t\tfloat almostATexel = 1.5 * texelSizeY;\n\t\tfloat almostOne = 1.0 - almostATexel;\n\t\tif ( absV.z >= almostOne ) {\n\t\t\tif ( v.z > 0.0 )\n\t\t\t\tplanar.x = 4.0 - v.x;\n\t\t} else if ( absV.x >= almostOne ) {\n\t\t\tfloat signX = sign( v.x );\n\t\t\tplanar.x = v.z * signX + 2.0 * signX;\n\t\t} else if ( absV.y >= almostOne ) {\n\t\t\tfloat signY = sign( v.y );\n\t\t\tplanar.x = v.x + 2.0 * signY + 2.0;\n\t\t\tplanar.y = v.z * signY - 2.0;\n\t\t}\n\t\treturn vec2( 0.125, 0.25 ) * planar + vec2( 0.375, 0.75 );\n\t}\n\tfloat getPointShadow( sampler2D shadowMap, vec2 shadowMapSize, float shadowBias, float shadowRadius, vec4 shadowCoord, float shadowCameraNear, float shadowCameraFar ) {\n\t\tvec2 texelSize = vec2( 1.0 ) / ( shadowMapSize * vec2( 4.0, 2.0 ) );\n\t\tvec3 lightToPosition = shadowCoord.xyz;\n\t\tfloat dp = ( length( lightToPosition ) - shadowCameraNear ) / ( shadowCameraFar - shadowCameraNear );\t\tdp += shadowBias;\n\t\tvec3 bd3D = normalize( lightToPosition );\n\t\t#if defined( SHADOWMAP_TYPE_PCF ) || defined( SHADOWMAP_TYPE_PCF_SOFT ) || defined( SHADOWMAP_TYPE_VSM )\n\t\t\tvec2 offset = vec2( - 1, 1 ) * shadowRadius * texelSize.y;\n\t\t\treturn (\n\t\t\t\ttexture2DCompare( shadowMap, cubeToUV( bd3D + offset.xyy, texelSize.y ), dp ) +\n\t\t\t\ttexture2DCompare( shadowMap, cubeToUV( bd3D + offset.yyy, texelSize.y ), dp ) +\n\t\t\t\ttexture2DCompare( shadowMap, cubeToUV( bd3D + offset.xyx, texelSize.y ), dp ) +\n\t\t\t\ttexture2DCompare( shadowMap, cubeToUV( bd3D + offset.yyx, texelSize.y ), dp ) +\n\t\t\t\ttexture2DCompare( shadowMap, cubeToUV( bd3D, texelSize.y ), dp ) +\n\t\t\t\ttexture2DCompare( shadowMap, cubeToUV( bd3D + offset.xxy, texelSize.y ), dp ) +\n\t\t\t\ttexture2DCompare( shadowMap, cubeToUV( bd3D + offset.yxy, texelSize.y ), dp ) +\n\t\t\t\ttexture2DCompare( shadowMap, cubeToUV( bd3D + offset.xxx, texelSize.y ), dp ) +\n\t\t\t\ttexture2DCompare( shadowMap, cubeToUV( bd3D + offset.yxx, texelSize.y ), dp )\n\t\t\t) * ( 1.0 / 9.0 );\n\t\t#else\n\t\t\treturn texture2DCompare( shadowMap, cubeToUV( bd3D, texelSize.y ), dp );\n\t\t#endif\n\t}\n#endif";
- var shadowmap_pars_vertex = "#ifdef USE_SHADOWMAP\n\t#if NUM_DIR_LIGHT_SHADOWS > 0\n\t\tuniform mat4 directionalShadowMatrix[ NUM_DIR_LIGHT_SHADOWS ];\n\t\tvarying vec4 vDirectionalShadowCoord[ NUM_DIR_LIGHT_SHADOWS ];\n\t\tstruct DirectionalLightShadow {\n\t\t\tfloat shadowBias;\n\t\t\tfloat shadowNormalBias;\n\t\t\tfloat shadowRadius;\n\t\t\tvec2 shadowMapSize;\n\t\t};\n\t\tuniform DirectionalLightShadow directionalLightShadows[ NUM_DIR_LIGHT_SHADOWS ];\n\t#endif\n\t#if NUM_SPOT_LIGHT_SHADOWS > 0\n\t\tuniform mat4 spotShadowMatrix[ NUM_SPOT_LIGHT_SHADOWS ];\n\t\tvarying vec4 vSpotShadowCoord[ NUM_SPOT_LIGHT_SHADOWS ];\n\t\tstruct SpotLightShadow {\n\t\t\tfloat shadowBias;\n\t\t\tfloat shadowNormalBias;\n\t\t\tfloat shadowRadius;\n\t\t\tvec2 shadowMapSize;\n\t\t};\n\t\tuniform SpotLightShadow spotLightShadows[ NUM_SPOT_LIGHT_SHADOWS ];\n\t#endif\n\t#if NUM_POINT_LIGHT_SHADOWS > 0\n\t\tuniform mat4 pointShadowMatrix[ NUM_POINT_LIGHT_SHADOWS ];\n\t\tvarying vec4 vPointShadowCoord[ NUM_POINT_LIGHT_SHADOWS ];\n\t\tstruct PointLightShadow {\n\t\t\tfloat shadowBias;\n\t\t\tfloat shadowNormalBias;\n\t\t\tfloat shadowRadius;\n\t\t\tvec2 shadowMapSize;\n\t\t\tfloat shadowCameraNear;\n\t\t\tfloat shadowCameraFar;\n\t\t};\n\t\tuniform PointLightShadow pointLightShadows[ NUM_POINT_LIGHT_SHADOWS ];\n\t#endif\n#endif";
- var shadowmap_vertex = "#ifdef USE_SHADOWMAP\n\t#if NUM_DIR_LIGHT_SHADOWS > 0 || NUM_SPOT_LIGHT_SHADOWS > 0 || NUM_POINT_LIGHT_SHADOWS > 0\n\t\tvec3 shadowWorldNormal = inverseTransformDirection( transformedNormal, viewMatrix );\n\t\tvec4 shadowWorldPosition;\n\t#endif\n\t#if NUM_DIR_LIGHT_SHADOWS > 0\n\t#pragma unroll_loop_start\n\tfor ( int i = 0; i < NUM_DIR_LIGHT_SHADOWS; i ++ ) {\n\t\tshadowWorldPosition = worldPosition + vec4( shadowWorldNormal * directionalLightShadows[ i ].shadowNormalBias, 0 );\n\t\tvDirectionalShadowCoord[ i ] = directionalShadowMatrix[ i ] * shadowWorldPosition;\n\t}\n\t#pragma unroll_loop_end\n\t#endif\n\t#if NUM_SPOT_LIGHT_SHADOWS > 0\n\t#pragma unroll_loop_start\n\tfor ( int i = 0; i < NUM_SPOT_LIGHT_SHADOWS; i ++ ) {\n\t\tshadowWorldPosition = worldPosition + vec4( shadowWorldNormal * spotLightShadows[ i ].shadowNormalBias, 0 );\n\t\tvSpotShadowCoord[ i ] = spotShadowMatrix[ i ] * shadowWorldPosition;\n\t}\n\t#pragma unroll_loop_end\n\t#endif\n\t#if NUM_POINT_LIGHT_SHADOWS > 0\n\t#pragma unroll_loop_start\n\tfor ( int i = 0; i < NUM_POINT_LIGHT_SHADOWS; i ++ ) {\n\t\tshadowWorldPosition = worldPosition + vec4( shadowWorldNormal * pointLightShadows[ i ].shadowNormalBias, 0 );\n\t\tvPointShadowCoord[ i ] = pointShadowMatrix[ i ] * shadowWorldPosition;\n\t}\n\t#pragma unroll_loop_end\n\t#endif\n#endif";
- var shadowmask_pars_fragment = "float getShadowMask() {\n\tfloat shadow = 1.0;\n\t#ifdef USE_SHADOWMAP\n\t#if NUM_DIR_LIGHT_SHADOWS > 0\n\tDirectionalLightShadow directionalLight;\n\t#pragma unroll_loop_start\n\tfor ( int i = 0; i < NUM_DIR_LIGHT_SHADOWS; i ++ ) {\n\t\tdirectionalLight = directionalLightShadows[ i ];\n\t\tshadow *= receiveShadow ? getShadow( directionalShadowMap[ i ], directionalLight.shadowMapSize, directionalLight.shadowBias, directionalLight.shadowRadius, vDirectionalShadowCoord[ i ] ) : 1.0;\n\t}\n\t#pragma unroll_loop_end\n\t#endif\n\t#if NUM_SPOT_LIGHT_SHADOWS > 0\n\tSpotLightShadow spotLight;\n\t#pragma unroll_loop_start\n\tfor ( int i = 0; i < NUM_SPOT_LIGHT_SHADOWS; i ++ ) {\n\t\tspotLight = spotLightShadows[ i ];\n\t\tshadow *= receiveShadow ? getShadow( spotShadowMap[ i ], spotLight.shadowMapSize, spotLight.shadowBias, spotLight.shadowRadius, vSpotShadowCoord[ i ] ) : 1.0;\n\t}\n\t#pragma unroll_loop_end\n\t#endif\n\t#if NUM_POINT_LIGHT_SHADOWS > 0\n\tPointLightShadow pointLight;\n\t#pragma unroll_loop_start\n\tfor ( int i = 0; i < NUM_POINT_LIGHT_SHADOWS; i ++ ) {\n\t\tpointLight = pointLightShadows[ i ];\n\t\tshadow *= receiveShadow ? getPointShadow( pointShadowMap[ i ], pointLight.shadowMapSize, pointLight.shadowBias, pointLight.shadowRadius, vPointShadowCoord[ i ], pointLight.shadowCameraNear, pointLight.shadowCameraFar ) : 1.0;\n\t}\n\t#pragma unroll_loop_end\n\t#endif\n\t#endif\n\treturn shadow;\n}";
- var skinbase_vertex = "#ifdef USE_SKINNING\n\tmat4 boneMatX = getBoneMatrix( skinIndex.x );\n\tmat4 boneMatY = getBoneMatrix( skinIndex.y );\n\tmat4 boneMatZ = getBoneMatrix( skinIndex.z );\n\tmat4 boneMatW = getBoneMatrix( skinIndex.w );\n#endif";
- var skinning_pars_vertex = "#ifdef USE_SKINNING\n\tuniform mat4 bindMatrix;\n\tuniform mat4 bindMatrixInverse;\n\t#ifdef BONE_TEXTURE\n\t\tuniform highp sampler2D boneTexture;\n\t\tuniform int boneTextureSize;\n\t\tmat4 getBoneMatrix( const in float i ) {\n\t\t\tfloat j = i * 4.0;\n\t\t\tfloat x = mod( j, float( boneTextureSize ) );\n\t\t\tfloat y = floor( j / float( boneTextureSize ) );\n\t\t\tfloat dx = 1.0 / float( boneTextureSize );\n\t\t\tfloat dy = 1.0 / float( boneTextureSize );\n\t\t\ty = dy * ( y + 0.5 );\n\t\t\tvec4 v1 = texture2D( boneTexture, vec2( dx * ( x + 0.5 ), y ) );\n\t\t\tvec4 v2 = texture2D( boneTexture, vec2( dx * ( x + 1.5 ), y ) );\n\t\t\tvec4 v3 = texture2D( boneTexture, vec2( dx * ( x + 2.5 ), y ) );\n\t\t\tvec4 v4 = texture2D( boneTexture, vec2( dx * ( x + 3.5 ), y ) );\n\t\t\tmat4 bone = mat4( v1, v2, v3, v4 );\n\t\t\treturn bone;\n\t\t}\n\t#else\n\t\tuniform mat4 boneMatrices[ MAX_BONES ];\n\t\tmat4 getBoneMatrix( const in float i ) {\n\t\t\tmat4 bone = boneMatrices[ int(i) ];\n\t\t\treturn bone;\n\t\t}\n\t#endif\n#endif";
- var skinning_vertex = "#ifdef USE_SKINNING\n\tvec4 skinVertex = bindMatrix * vec4( transformed, 1.0 );\n\tvec4 skinned = vec4( 0.0 );\n\tskinned += boneMatX * skinVertex * skinWeight.x;\n\tskinned += boneMatY * skinVertex * skinWeight.y;\n\tskinned += boneMatZ * skinVertex * skinWeight.z;\n\tskinned += boneMatW * skinVertex * skinWeight.w;\n\ttransformed = ( bindMatrixInverse * skinned ).xyz;\n#endif";
- var skinnormal_vertex = "#ifdef USE_SKINNING\n\tmat4 skinMatrix = mat4( 0.0 );\n\tskinMatrix += skinWeight.x * boneMatX;\n\tskinMatrix += skinWeight.y * boneMatY;\n\tskinMatrix += skinWeight.z * boneMatZ;\n\tskinMatrix += skinWeight.w * boneMatW;\n\tskinMatrix = bindMatrixInverse * skinMatrix * bindMatrix;\n\tobjectNormal = vec4( skinMatrix * vec4( objectNormal, 0.0 ) ).xyz;\n\t#ifdef USE_TANGENT\n\t\tobjectTangent = vec4( skinMatrix * vec4( objectTangent, 0.0 ) ).xyz;\n\t#endif\n#endif";
- var specularmap_fragment = "float specularStrength;\n#ifdef USE_SPECULARMAP\n\tvec4 texelSpecular = texture2D( specularMap, vUv );\n\tspecularStrength = texelSpecular.r;\n#else\n\tspecularStrength = 1.0;\n#endif";
- var specularmap_pars_fragment = "#ifdef USE_SPECULARMAP\n\tuniform sampler2D specularMap;\n#endif";
- var tonemapping_fragment = "#if defined( TONE_MAPPING )\n\tgl_FragColor.rgb = toneMapping( gl_FragColor.rgb );\n#endif";
- var tonemapping_pars_fragment = "#ifndef saturate\n#define saturate(a) clamp( a, 0.0, 1.0 )\n#endif\nuniform float toneMappingExposure;\nvec3 LinearToneMapping( vec3 color ) {\n\treturn toneMappingExposure * color;\n}\nvec3 ReinhardToneMapping( vec3 color ) {\n\tcolor *= toneMappingExposure;\n\treturn saturate( color / ( vec3( 1.0 ) + color ) );\n}\nvec3 OptimizedCineonToneMapping( vec3 color ) {\n\tcolor *= toneMappingExposure;\n\tcolor = max( vec3( 0.0 ), color - 0.004 );\n\treturn pow( ( color * ( 6.2 * color + 0.5 ) ) / ( color * ( 6.2 * color + 1.7 ) + 0.06 ), vec3( 2.2 ) );\n}\nvec3 RRTAndODTFit( vec3 v ) {\n\tvec3 a = v * ( v + 0.0245786 ) - 0.000090537;\n\tvec3 b = v * ( 0.983729 * v + 0.4329510 ) + 0.238081;\n\treturn a / b;\n}\nvec3 ACESFilmicToneMapping( vec3 color ) {\n\tconst mat3 ACESInputMat = mat3(\n\t\tvec3( 0.59719, 0.07600, 0.02840 ),\t\tvec3( 0.35458, 0.90834, 0.13383 ),\n\t\tvec3( 0.04823, 0.01566, 0.83777 )\n\t);\n\tconst mat3 ACESOutputMat = mat3(\n\t\tvec3( 1.60475, -0.10208, -0.00327 ),\t\tvec3( -0.53108, 1.10813, -0.07276 ),\n\t\tvec3( -0.07367, -0.00605, 1.07602 )\n\t);\n\tcolor *= toneMappingExposure / 0.6;\n\tcolor = ACESInputMat * color;\n\tcolor = RRTAndODTFit( color );\n\tcolor = ACESOutputMat * color;\n\treturn saturate( color );\n}\nvec3 CustomToneMapping( vec3 color ) { return color; }";
- var transmissionmap_fragment = "#ifdef USE_TRANSMISSIONMAP\n\ttotalTransmission *= texture2D( transmissionMap, vUv ).r;\n#endif";
- var transmissionmap_pars_fragment = "#ifdef USE_TRANSMISSIONMAP\n\tuniform sampler2D transmissionMap;\n#endif";
- var uv_pars_fragment = "#if ( defined( USE_UV ) && ! defined( UVS_VERTEX_ONLY ) )\n\tvarying vec2 vUv;\n#endif";
- var uv_pars_vertex = "#ifdef USE_UV\n\t#ifdef UVS_VERTEX_ONLY\n\t\tvec2 vUv;\n\t#else\n\t\tvarying vec2 vUv;\n\t#endif\n\tuniform mat3 uvTransform;\n#endif";
- var uv_vertex = "#ifdef USE_UV\n\tvUv = ( uvTransform * vec3( uv, 1 ) ).xy;\n#endif";
- var uv2_pars_fragment = "#if defined( USE_LIGHTMAP ) || defined( USE_AOMAP )\n\tvarying vec2 vUv2;\n#endif";
- var uv2_pars_vertex = "#if defined( USE_LIGHTMAP ) || defined( USE_AOMAP )\n\tattribute vec2 uv2;\n\tvarying vec2 vUv2;\n\tuniform mat3 uv2Transform;\n#endif";
- var uv2_vertex = "#if defined( USE_LIGHTMAP ) || defined( USE_AOMAP )\n\tvUv2 = ( uv2Transform * vec3( uv2, 1 ) ).xy;\n#endif";
- var worldpos_vertex = "#if defined( USE_ENVMAP ) || defined( DISTANCE ) || defined ( USE_SHADOWMAP )\n\tvec4 worldPosition = vec4( transformed, 1.0 );\n\t#ifdef USE_INSTANCING\n\t\tworldPosition = instanceMatrix * worldPosition;\n\t#endif\n\tworldPosition = modelMatrix * worldPosition;\n#endif";
- var background_frag = "uniform sampler2D t2D;\nvarying vec2 vUv;\nvoid main() {\n\tvec4 texColor = texture2D( t2D, vUv );\n\tgl_FragColor = mapTexelToLinear( texColor );\n\t#include <tonemapping_fragment>\n\t#include <encodings_fragment>\n}";
- var background_vert = "varying vec2 vUv;\nuniform mat3 uvTransform;\nvoid main() {\n\tvUv = ( uvTransform * vec3( uv, 1 ) ).xy;\n\tgl_Position = vec4( position.xy, 1.0, 1.0 );\n}";
- var cube_frag = "#include <envmap_common_pars_fragment>\nuniform float opacity;\nvarying vec3 vWorldDirection;\n#include <cube_uv_reflection_fragment>\nvoid main() {\n\tvec3 vReflect = vWorldDirection;\n\t#include <envmap_fragment>\n\tgl_FragColor = envColor;\n\tgl_FragColor.a *= opacity;\n\t#include <tonemapping_fragment>\n\t#include <encodings_fragment>\n}";
- var cube_vert = "varying vec3 vWorldDirection;\n#include <common>\nvoid main() {\n\tvWorldDirection = transformDirection( position, modelMatrix );\n\t#include <begin_vertex>\n\t#include <project_vertex>\n\tgl_Position.z = gl_Position.w;\n}";
- var depth_frag = "#if DEPTH_PACKING == 3200\n\tuniform float opacity;\n#endif\n#include <common>\n#include <packing>\n#include <uv_pars_fragment>\n#include <map_pars_fragment>\n#include <alphamap_pars_fragment>\n#include <logdepthbuf_pars_fragment>\n#include <clipping_planes_pars_fragment>\nvarying vec2 vHighPrecisionZW;\nvoid main() {\n\t#include <clipping_planes_fragment>\n\tvec4 diffuseColor = vec4( 1.0 );\n\t#if DEPTH_PACKING == 3200\n\t\tdiffuseColor.a = opacity;\n\t#endif\n\t#include <map_fragment>\n\t#include <alphamap_fragment>\n\t#include <alphatest_fragment>\n\t#include <logdepthbuf_fragment>\n\tfloat fragCoordZ = 0.5 * vHighPrecisionZW[0] / vHighPrecisionZW[1] + 0.5;\n\t#if DEPTH_PACKING == 3200\n\t\tgl_FragColor = vec4( vec3( 1.0 - fragCoordZ ), opacity );\n\t#elif DEPTH_PACKING == 3201\n\t\tgl_FragColor = packDepthToRGBA( fragCoordZ );\n\t#endif\n}";
- var depth_vert = "#include <common>\n#include <uv_pars_vertex>\n#include <displacementmap_pars_vertex>\n#include <morphtarget_pars_vertex>\n#include <skinning_pars_vertex>\n#include <logdepthbuf_pars_vertex>\n#include <clipping_planes_pars_vertex>\nvarying vec2 vHighPrecisionZW;\nvoid main() {\n\t#include <uv_vertex>\n\t#include <skinbase_vertex>\n\t#ifdef USE_DISPLACEMENTMAP\n\t\t#include <beginnormal_vertex>\n\t\t#include <morphnormal_vertex>\n\t\t#include <skinnormal_vertex>\n\t#endif\n\t#include <begin_vertex>\n\t#include <morphtarget_vertex>\n\t#include <skinning_vertex>\n\t#include <displacementmap_vertex>\n\t#include <project_vertex>\n\t#include <logdepthbuf_vertex>\n\t#include <clipping_planes_vertex>\n\tvHighPrecisionZW = gl_Position.zw;\n}";
- var distanceRGBA_frag = "#define DISTANCE\nuniform vec3 referencePosition;\nuniform float nearDistance;\nuniform float farDistance;\nvarying vec3 vWorldPosition;\n#include <common>\n#include <packing>\n#include <uv_pars_fragment>\n#include <map_pars_fragment>\n#include <alphamap_pars_fragment>\n#include <clipping_planes_pars_fragment>\nvoid main () {\n\t#include <clipping_planes_fragment>\n\tvec4 diffuseColor = vec4( 1.0 );\n\t#include <map_fragment>\n\t#include <alphamap_fragment>\n\t#include <alphatest_fragment>\n\tfloat dist = length( vWorldPosition - referencePosition );\n\tdist = ( dist - nearDistance ) / ( farDistance - nearDistance );\n\tdist = saturate( dist );\n\tgl_FragColor = packDepthToRGBA( dist );\n}";
- var distanceRGBA_vert = "#define DISTANCE\nvarying vec3 vWorldPosition;\n#include <common>\n#include <uv_pars_vertex>\n#include <displacementmap_pars_vertex>\n#include <morphtarget_pars_vertex>\n#include <skinning_pars_vertex>\n#include <clipping_planes_pars_vertex>\nvoid main() {\n\t#include <uv_vertex>\n\t#include <skinbase_vertex>\n\t#ifdef USE_DISPLACEMENTMAP\n\t\t#include <beginnormal_vertex>\n\t\t#include <morphnormal_vertex>\n\t\t#include <skinnormal_vertex>\n\t#endif\n\t#include <begin_vertex>\n\t#include <morphtarget_vertex>\n\t#include <skinning_vertex>\n\t#include <displacementmap_vertex>\n\t#include <project_vertex>\n\t#include <worldpos_vertex>\n\t#include <clipping_planes_vertex>\n\tvWorldPosition = worldPosition.xyz;\n}";
- var equirect_frag = "uniform sampler2D tEquirect;\nvarying vec3 vWorldDirection;\n#include <common>\nvoid main() {\n\tvec3 direction = normalize( vWorldDirection );\n\tvec2 sampleUV = equirectUv( direction );\n\tvec4 texColor = texture2D( tEquirect, sampleUV );\n\tgl_FragColor = mapTexelToLinear( texColor );\n\t#include <tonemapping_fragment>\n\t#include <encodings_fragment>\n}";
- var equirect_vert = "varying vec3 vWorldDirection;\n#include <common>\nvoid main() {\n\tvWorldDirection = transformDirection( position, modelMatrix );\n\t#include <begin_vertex>\n\t#include <project_vertex>\n}";
- var linedashed_frag = "uniform vec3 diffuse;\nuniform float opacity;\nuniform float dashSize;\nuniform float totalSize;\nvarying float vLineDistance;\n#include <common>\n#include <color_pars_fragment>\n#include <fog_pars_fragment>\n#include <logdepthbuf_pars_fragment>\n#include <clipping_planes_pars_fragment>\nvoid main() {\n\t#include <clipping_planes_fragment>\n\tif ( mod( vLineDistance, totalSize ) > dashSize ) {\n\t\tdiscard;\n\t}\n\tvec3 outgoingLight = vec3( 0.0 );\n\tvec4 diffuseColor = vec4( diffuse, opacity );\n\t#include <logdepthbuf_fragment>\n\t#include <color_fragment>\n\toutgoingLight = diffuseColor.rgb;\n\tgl_FragColor = vec4( outgoingLight, diffuseColor.a );\n\t#include <tonemapping_fragment>\n\t#include <encodings_fragment>\n\t#include <fog_fragment>\n\t#include <premultiplied_alpha_fragment>\n}";
- var linedashed_vert = "uniform float scale;\nattribute float lineDistance;\nvarying float vLineDistance;\n#include <common>\n#include <color_pars_vertex>\n#include <fog_pars_vertex>\n#include <morphtarget_pars_vertex>\n#include <logdepthbuf_pars_vertex>\n#include <clipping_planes_pars_vertex>\nvoid main() {\n\tvLineDistance = scale * lineDistance;\n\t#include <color_vertex>\n\t#include <begin_vertex>\n\t#include <morphtarget_vertex>\n\t#include <project_vertex>\n\t#include <logdepthbuf_vertex>\n\t#include <clipping_planes_vertex>\n\t#include <fog_vertex>\n}";
- var meshbasic_frag = "uniform vec3 diffuse;\nuniform float opacity;\n#ifndef FLAT_SHADED\n\tvarying vec3 vNormal;\n#endif\n#include <common>\n#include <dithering_pars_fragment>\n#include <color_pars_fragment>\n#include <uv_pars_fragment>\n#include <uv2_pars_fragment>\n#include <map_pars_fragment>\n#include <alphamap_pars_fragment>\n#include <aomap_pars_fragment>\n#include <lightmap_pars_fragment>\n#include <envmap_common_pars_fragment>\n#include <envmap_pars_fragment>\n#include <cube_uv_reflection_fragment>\n#include <fog_pars_fragment>\n#include <specularmap_pars_fragment>\n#include <logdepthbuf_pars_fragment>\n#include <clipping_planes_pars_fragment>\nvoid main() {\n\t#include <clipping_planes_fragment>\n\tvec4 diffuseColor = vec4( diffuse, opacity );\n\t#include <logdepthbuf_fragment>\n\t#include <map_fragment>\n\t#include <color_fragment>\n\t#include <alphamap_fragment>\n\t#include <alphatest_fragment>\n\t#include <specularmap_fragment>\n\tReflectedLight reflectedLight = ReflectedLight( vec3( 0.0 ), vec3( 0.0 ), vec3( 0.0 ), vec3( 0.0 ) );\n\t#ifdef USE_LIGHTMAP\n\t\n\t\tvec4 lightMapTexel= texture2D( lightMap, vUv2 );\n\t\treflectedLight.indirectDiffuse += lightMapTexelToLinear( lightMapTexel ).rgb * lightMapIntensity;\n\t#else\n\t\treflectedLight.indirectDiffuse += vec3( 1.0 );\n\t#endif\n\t#include <aomap_fragment>\n\treflectedLight.indirectDiffuse *= diffuseColor.rgb;\n\tvec3 outgoingLight = reflectedLight.indirectDiffuse;\n\t#include <envmap_fragment>\n\tgl_FragColor = vec4( outgoingLight, diffuseColor.a );\n\t#include <tonemapping_fragment>\n\t#include <encodings_fragment>\n\t#include <fog_fragment>\n\t#include <premultiplied_alpha_fragment>\n\t#include <dithering_fragment>\n}";
- var meshbasic_vert = "#include <common>\n#include <uv_pars_vertex>\n#include <uv2_pars_vertex>\n#include <envmap_pars_vertex>\n#include <color_pars_vertex>\n#include <fog_pars_vertex>\n#include <morphtarget_pars_vertex>\n#include <skinning_pars_vertex>\n#include <logdepthbuf_pars_vertex>\n#include <clipping_planes_pars_vertex>\nvoid main() {\n\t#include <uv_vertex>\n\t#include <uv2_vertex>\n\t#include <color_vertex>\n\t#include <skinbase_vertex>\n\t#ifdef USE_ENVMAP\n\t#include <beginnormal_vertex>\n\t#include <morphnormal_vertex>\n\t#include <skinnormal_vertex>\n\t#include <defaultnormal_vertex>\n\t#endif\n\t#include <begin_vertex>\n\t#include <morphtarget_vertex>\n\t#include <skinning_vertex>\n\t#include <project_vertex>\n\t#include <logdepthbuf_vertex>\n\t#include <worldpos_vertex>\n\t#include <clipping_planes_vertex>\n\t#include <envmap_vertex>\n\t#include <fog_vertex>\n}";
- var meshlambert_frag = "uniform vec3 diffuse;\nuniform vec3 emissive;\nuniform float opacity;\nvarying vec3 vLightFront;\nvarying vec3 vIndirectFront;\n#ifdef DOUBLE_SIDED\n\tvarying vec3 vLightBack;\n\tvarying vec3 vIndirectBack;\n#endif\n#include <common>\n#include <packing>\n#include <dithering_pars_fragment>\n#include <color_pars_fragment>\n#include <uv_pars_fragment>\n#include <uv2_pars_fragment>\n#include <map_pars_fragment>\n#include <alphamap_pars_fragment>\n#include <aomap_pars_fragment>\n#include <lightmap_pars_fragment>\n#include <emissivemap_pars_fragment>\n#include <envmap_common_pars_fragment>\n#include <envmap_pars_fragment>\n#include <cube_uv_reflection_fragment>\n#include <bsdfs>\n#include <lights_pars_begin>\n#include <fog_pars_fragment>\n#include <shadowmap_pars_fragment>\n#include <shadowmask_pars_fragment>\n#include <specularmap_pars_fragment>\n#include <logdepthbuf_pars_fragment>\n#include <clipping_planes_pars_fragment>\nvoid main() {\n\t#include <clipping_planes_fragment>\n\tvec4 diffuseColor = vec4( diffuse, opacity );\n\tReflectedLight reflectedLight = ReflectedLight( vec3( 0.0 ), vec3( 0.0 ), vec3( 0.0 ), vec3( 0.0 ) );\n\tvec3 totalEmissiveRadiance = emissive;\n\t#include <logdepthbuf_fragment>\n\t#include <map_fragment>\n\t#include <color_fragment>\n\t#include <alphamap_fragment>\n\t#include <alphatest_fragment>\n\t#include <specularmap_fragment>\n\t#include <emissivemap_fragment>\n\t#ifdef DOUBLE_SIDED\n\t\treflectedLight.indirectDiffuse += ( gl_FrontFacing ) ? vIndirectFront : vIndirectBack;\n\t#else\n\t\treflectedLight.indirectDiffuse += vIndirectFront;\n\t#endif\n\t#include <lightmap_fragment>\n\treflectedLight.indirectDiffuse *= BRDF_Diffuse_Lambert( diffuseColor.rgb );\n\t#ifdef DOUBLE_SIDED\n\t\treflectedLight.directDiffuse = ( gl_FrontFacing ) ? vLightFront : vLightBack;\n\t#else\n\t\treflectedLight.directDiffuse = vLightFront;\n\t#endif\n\treflectedLight.directDiffuse *= BRDF_Diffuse_Lambert( diffuseColor.rgb ) * getShadowMask();\n\t#include <aomap_fragment>\n\tvec3 outgoingLight = reflectedLight.directDiffuse + reflectedLight.indirectDiffuse + totalEmissiveRadiance;\n\t#include <envmap_fragment>\n\tgl_FragColor = vec4( outgoingLight, diffuseColor.a );\n\t#include <tonemapping_fragment>\n\t#include <encodings_fragment>\n\t#include <fog_fragment>\n\t#include <premultiplied_alpha_fragment>\n\t#include <dithering_fragment>\n}";
- var meshlambert_vert = "#define LAMBERT\nvarying vec3 vLightFront;\nvarying vec3 vIndirectFront;\n#ifdef DOUBLE_SIDED\n\tvarying vec3 vLightBack;\n\tvarying vec3 vIndirectBack;\n#endif\n#include <common>\n#include <uv_pars_vertex>\n#include <uv2_pars_vertex>\n#include <envmap_pars_vertex>\n#include <bsdfs>\n#include <lights_pars_begin>\n#include <color_pars_vertex>\n#include <fog_pars_vertex>\n#include <morphtarget_pars_vertex>\n#include <skinning_pars_vertex>\n#include <shadowmap_pars_vertex>\n#include <logdepthbuf_pars_vertex>\n#include <clipping_planes_pars_vertex>\nvoid main() {\n\t#include <uv_vertex>\n\t#include <uv2_vertex>\n\t#include <color_vertex>\n\t#include <beginnormal_vertex>\n\t#include <morphnormal_vertex>\n\t#include <skinbase_vertex>\n\t#include <skinnormal_vertex>\n\t#include <defaultnormal_vertex>\n\t#include <begin_vertex>\n\t#include <morphtarget_vertex>\n\t#include <skinning_vertex>\n\t#include <project_vertex>\n\t#include <logdepthbuf_vertex>\n\t#include <clipping_planes_vertex>\n\t#include <worldpos_vertex>\n\t#include <envmap_vertex>\n\t#include <lights_lambert_vertex>\n\t#include <shadowmap_vertex>\n\t#include <fog_vertex>\n}";
- var meshmatcap_frag = "#define MATCAP\nuniform vec3 diffuse;\nuniform float opacity;\nuniform sampler2D matcap;\nvarying vec3 vViewPosition;\n#ifndef FLAT_SHADED\n\tvarying vec3 vNormal;\n#endif\n#include <common>\n#include <dithering_pars_fragment>\n#include <color_pars_fragment>\n#include <uv_pars_fragment>\n#include <map_pars_fragment>\n#include <alphamap_pars_fragment>\n#include <fog_pars_fragment>\n#include <bumpmap_pars_fragment>\n#include <normalmap_pars_fragment>\n#include <logdepthbuf_pars_fragment>\n#include <clipping_planes_pars_fragment>\nvoid main() {\n\t#include <clipping_planes_fragment>\n\tvec4 diffuseColor = vec4( diffuse, opacity );\n\t#include <logdepthbuf_fragment>\n\t#include <map_fragment>\n\t#include <color_fragment>\n\t#include <alphamap_fragment>\n\t#include <alphatest_fragment>\n\t#include <normal_fragment_begin>\n\t#include <normal_fragment_maps>\n\tvec3 viewDir = normalize( vViewPosition );\n\tvec3 x = normalize( vec3( viewDir.z, 0.0, - viewDir.x ) );\n\tvec3 y = cross( viewDir, x );\n\tvec2 uv = vec2( dot( x, normal ), dot( y, normal ) ) * 0.495 + 0.5;\n\t#ifdef USE_MATCAP\n\t\tvec4 matcapColor = texture2D( matcap, uv );\n\t\tmatcapColor = matcapTexelToLinear( matcapColor );\n\t#else\n\t\tvec4 matcapColor = vec4( 1.0 );\n\t#endif\n\tvec3 outgoingLight = diffuseColor.rgb * matcapColor.rgb;\n\tgl_FragColor = vec4( outgoingLight, diffuseColor.a );\n\t#include <tonemapping_fragment>\n\t#include <encodings_fragment>\n\t#include <fog_fragment>\n\t#include <premultiplied_alpha_fragment>\n\t#include <dithering_fragment>\n}";
- var meshmatcap_vert = "#define MATCAP\nvarying vec3 vViewPosition;\n#ifndef FLAT_SHADED\n\tvarying vec3 vNormal;\n#endif\n#include <common>\n#include <uv_pars_vertex>\n#include <color_pars_vertex>\n#include <displacementmap_pars_vertex>\n#include <fog_pars_vertex>\n#include <morphtarget_pars_vertex>\n#include <skinning_pars_vertex>\n#include <logdepthbuf_pars_vertex>\n#include <clipping_planes_pars_vertex>\nvoid main() {\n\t#include <uv_vertex>\n\t#include <color_vertex>\n\t#include <beginnormal_vertex>\n\t#include <morphnormal_vertex>\n\t#include <skinbase_vertex>\n\t#include <skinnormal_vertex>\n\t#include <defaultnormal_vertex>\n\t#ifndef FLAT_SHADED\n\t\tvNormal = normalize( transformedNormal );\n\t#endif\n\t#include <begin_vertex>\n\t#include <morphtarget_vertex>\n\t#include <skinning_vertex>\n\t#include <displacementmap_vertex>\n\t#include <project_vertex>\n\t#include <logdepthbuf_vertex>\n\t#include <clipping_planes_vertex>\n\t#include <fog_vertex>\n\tvViewPosition = - mvPosition.xyz;\n}";
- var meshtoon_frag = "#define TOON\nuniform vec3 diffuse;\nuniform vec3 emissive;\nuniform float opacity;\n#include <common>\n#include <packing>\n#include <dithering_pars_fragment>\n#include <color_pars_fragment>\n#include <uv_pars_fragment>\n#include <uv2_pars_fragment>\n#include <map_pars_fragment>\n#include <alphamap_pars_fragment>\n#include <aomap_pars_fragment>\n#include <lightmap_pars_fragment>\n#include <emissivemap_pars_fragment>\n#include <gradientmap_pars_fragment>\n#include <fog_pars_fragment>\n#include <bsdfs>\n#include <lights_pars_begin>\n#include <lights_toon_pars_fragment>\n#include <shadowmap_pars_fragment>\n#include <bumpmap_pars_fragment>\n#include <normalmap_pars_fragment>\n#include <logdepthbuf_pars_fragment>\n#include <clipping_planes_pars_fragment>\nvoid main() {\n\t#include <clipping_planes_fragment>\n\tvec4 diffuseColor = vec4( diffuse, opacity );\n\tReflectedLight reflectedLight = ReflectedLight( vec3( 0.0 ), vec3( 0.0 ), vec3( 0.0 ), vec3( 0.0 ) );\n\tvec3 totalEmissiveRadiance = emissive;\n\t#include <logdepthbuf_fragment>\n\t#include <map_fragment>\n\t#include <color_fragment>\n\t#include <alphamap_fragment>\n\t#include <alphatest_fragment>\n\t#include <normal_fragment_begin>\n\t#include <normal_fragment_maps>\n\t#include <emissivemap_fragment>\n\t#include <lights_toon_fragment>\n\t#include <lights_fragment_begin>\n\t#include <lights_fragment_maps>\n\t#include <lights_fragment_end>\n\t#include <aomap_fragment>\n\tvec3 outgoingLight = reflectedLight.directDiffuse + reflectedLight.indirectDiffuse + totalEmissiveRadiance;\n\tgl_FragColor = vec4( outgoingLight, diffuseColor.a );\n\t#include <tonemapping_fragment>\n\t#include <encodings_fragment>\n\t#include <fog_fragment>\n\t#include <premultiplied_alpha_fragment>\n\t#include <dithering_fragment>\n}";
- var meshtoon_vert = "#define TOON\nvarying vec3 vViewPosition;\n#ifndef FLAT_SHADED\n\tvarying vec3 vNormal;\n#endif\n#include <common>\n#include <uv_pars_vertex>\n#include <uv2_pars_vertex>\n#include <displacementmap_pars_vertex>\n#include <color_pars_vertex>\n#include <fog_pars_vertex>\n#include <morphtarget_pars_vertex>\n#include <skinning_pars_vertex>\n#include <shadowmap_pars_vertex>\n#include <logdepthbuf_pars_vertex>\n#include <clipping_planes_pars_vertex>\nvoid main() {\n\t#include <uv_vertex>\n\t#include <uv2_vertex>\n\t#include <color_vertex>\n\t#include <beginnormal_vertex>\n\t#include <morphnormal_vertex>\n\t#include <skinbase_vertex>\n\t#include <skinnormal_vertex>\n\t#include <defaultnormal_vertex>\n#ifndef FLAT_SHADED\n\tvNormal = normalize( transformedNormal );\n#endif\n\t#include <begin_vertex>\n\t#include <morphtarget_vertex>\n\t#include <skinning_vertex>\n\t#include <displacementmap_vertex>\n\t#include <project_vertex>\n\t#include <logdepthbuf_vertex>\n\t#include <clipping_planes_vertex>\n\tvViewPosition = - mvPosition.xyz;\n\t#include <worldpos_vertex>\n\t#include <shadowmap_vertex>\n\t#include <fog_vertex>\n}";
- var meshphong_frag = "#define PHONG\nuniform vec3 diffuse;\nuniform vec3 emissive;\nuniform vec3 specular;\nuniform float shininess;\nuniform float opacity;\n#include <common>\n#include <packing>\n#include <dithering_pars_fragment>\n#include <color_pars_fragment>\n#include <uv_pars_fragment>\n#include <uv2_pars_fragment>\n#include <map_pars_fragment>\n#include <alphamap_pars_fragment>\n#include <aomap_pars_fragment>\n#include <lightmap_pars_fragment>\n#include <emissivemap_pars_fragment>\n#include <envmap_common_pars_fragment>\n#include <envmap_pars_fragment>\n#include <cube_uv_reflection_fragment>\n#include <fog_pars_fragment>\n#include <bsdfs>\n#include <lights_pars_begin>\n#include <lights_phong_pars_fragment>\n#include <shadowmap_pars_fragment>\n#include <bumpmap_pars_fragment>\n#include <normalmap_pars_fragment>\n#include <specularmap_pars_fragment>\n#include <logdepthbuf_pars_fragment>\n#include <clipping_planes_pars_fragment>\nvoid main() {\n\t#include <clipping_planes_fragment>\n\tvec4 diffuseColor = vec4( diffuse, opacity );\n\tReflectedLight reflectedLight = ReflectedLight( vec3( 0.0 ), vec3( 0.0 ), vec3( 0.0 ), vec3( 0.0 ) );\n\tvec3 totalEmissiveRadiance = emissive;\n\t#include <logdepthbuf_fragment>\n\t#include <map_fragment>\n\t#include <color_fragment>\n\t#include <alphamap_fragment>\n\t#include <alphatest_fragment>\n\t#include <specularmap_fragment>\n\t#include <normal_fragment_begin>\n\t#include <normal_fragment_maps>\n\t#include <emissivemap_fragment>\n\t#include <lights_phong_fragment>\n\t#include <lights_fragment_begin>\n\t#include <lights_fragment_maps>\n\t#include <lights_fragment_end>\n\t#include <aomap_fragment>\n\tvec3 outgoingLight = reflectedLight.directDiffuse + reflectedLight.indirectDiffuse + reflectedLight.directSpecular + reflectedLight.indirectSpecular + totalEmissiveRadiance;\n\t#include <envmap_fragment>\n\tgl_FragColor = vec4( outgoingLight, diffuseColor.a );\n\t#include <tonemapping_fragment>\n\t#include <encodings_fragment>\n\t#include <fog_fragment>\n\t#include <premultiplied_alpha_fragment>\n\t#include <dithering_fragment>\n}";
- var meshphong_vert = "#define PHONG\nvarying vec3 vViewPosition;\n#ifndef FLAT_SHADED\n\tvarying vec3 vNormal;\n#endif\n#include <common>\n#include <uv_pars_vertex>\n#include <uv2_pars_vertex>\n#include <displacementmap_pars_vertex>\n#include <envmap_pars_vertex>\n#include <color_pars_vertex>\n#include <fog_pars_vertex>\n#include <morphtarget_pars_vertex>\n#include <skinning_pars_vertex>\n#include <shadowmap_pars_vertex>\n#include <logdepthbuf_pars_vertex>\n#include <clipping_planes_pars_vertex>\nvoid main() {\n\t#include <uv_vertex>\n\t#include <uv2_vertex>\n\t#include <color_vertex>\n\t#include <beginnormal_vertex>\n\t#include <morphnormal_vertex>\n\t#include <skinbase_vertex>\n\t#include <skinnormal_vertex>\n\t#include <defaultnormal_vertex>\n#ifndef FLAT_SHADED\n\tvNormal = normalize( transformedNormal );\n#endif\n\t#include <begin_vertex>\n\t#include <morphtarget_vertex>\n\t#include <skinning_vertex>\n\t#include <displacementmap_vertex>\n\t#include <project_vertex>\n\t#include <logdepthbuf_vertex>\n\t#include <clipping_planes_vertex>\n\tvViewPosition = - mvPosition.xyz;\n\t#include <worldpos_vertex>\n\t#include <envmap_vertex>\n\t#include <shadowmap_vertex>\n\t#include <fog_vertex>\n}";
- var meshphysical_frag = "#define STANDARD\n#ifdef PHYSICAL\n\t#define REFLECTIVITY\n\t#define CLEARCOAT\n\t#define TRANSMISSION\n#endif\nuniform vec3 diffuse;\nuniform vec3 emissive;\nuniform float roughness;\nuniform float metalness;\nuniform float opacity;\n#ifdef TRANSMISSION\n\tuniform float transmission;\n#endif\n#ifdef REFLECTIVITY\n\tuniform float reflectivity;\n#endif\n#ifdef CLEARCOAT\n\tuniform float clearcoat;\n\tuniform float clearcoatRoughness;\n#endif\n#ifdef USE_SHEEN\n\tuniform vec3 sheen;\n#endif\nvarying vec3 vViewPosition;\n#ifndef FLAT_SHADED\n\tvarying vec3 vNormal;\n\t#ifdef USE_TANGENT\n\t\tvarying vec3 vTangent;\n\t\tvarying vec3 vBitangent;\n\t#endif\n#endif\n#include <common>\n#include <packing>\n#include <dithering_pars_fragment>\n#include <color_pars_fragment>\n#include <uv_pars_fragment>\n#include <uv2_pars_fragment>\n#include <map_pars_fragment>\n#include <alphamap_pars_fragment>\n#include <aomap_pars_fragment>\n#include <lightmap_pars_fragment>\n#include <emissivemap_pars_fragment>\n#include <transmissionmap_pars_fragment>\n#include <bsdfs>\n#include <cube_uv_reflection_fragment>\n#include <envmap_common_pars_fragment>\n#include <envmap_physical_pars_fragment>\n#include <fog_pars_fragment>\n#include <lights_pars_begin>\n#include <lights_physical_pars_fragment>\n#include <shadowmap_pars_fragment>\n#include <bumpmap_pars_fragment>\n#include <normalmap_pars_fragment>\n#include <clearcoat_pars_fragment>\n#include <roughnessmap_pars_fragment>\n#include <metalnessmap_pars_fragment>\n#include <logdepthbuf_pars_fragment>\n#include <clipping_planes_pars_fragment>\nvoid main() {\n\t#include <clipping_planes_fragment>\n\tvec4 diffuseColor = vec4( diffuse, opacity );\n\tReflectedLight reflectedLight = ReflectedLight( vec3( 0.0 ), vec3( 0.0 ), vec3( 0.0 ), vec3( 0.0 ) );\n\tvec3 totalEmissiveRadiance = emissive;\n\t#ifdef TRANSMISSION\n\t\tfloat totalTransmission = transmission;\n\t#endif\n\t#include <logdepthbuf_fragment>\n\t#include <map_fragment>\n\t#include <color_fragment>\n\t#include <alphamap_fragment>\n\t#include <alphatest_fragment>\n\t#include <roughnessmap_fragment>\n\t#include <metalnessmap_fragment>\n\t#include <normal_fragment_begin>\n\t#include <normal_fragment_maps>\n\t#include <clearcoat_normal_fragment_begin>\n\t#include <clearcoat_normal_fragment_maps>\n\t#include <emissivemap_fragment>\n\t#include <transmissionmap_fragment>\n\t#include <lights_physical_fragment>\n\t#include <lights_fragment_begin>\n\t#include <lights_fragment_maps>\n\t#include <lights_fragment_end>\n\t#include <aomap_fragment>\n\tvec3 outgoingLight = reflectedLight.directDiffuse + reflectedLight.indirectDiffuse + reflectedLight.directSpecular + reflectedLight.indirectSpecular + totalEmissiveRadiance;\n\t#ifdef TRANSMISSION\n\t\tdiffuseColor.a *= mix( saturate( 1. - totalTransmission + linearToRelativeLuminance( reflectedLight.directSpecular + reflectedLight.indirectSpecular ) ), 1.0, metalness );\n\t#endif\n\tgl_FragColor = vec4( outgoingLight, diffuseColor.a );\n\t#include <tonemapping_fragment>\n\t#include <encodings_fragment>\n\t#include <fog_fragment>\n\t#include <premultiplied_alpha_fragment>\n\t#include <dithering_fragment>\n}";
- var meshphysical_vert = "#define STANDARD\nvarying vec3 vViewPosition;\n#ifndef FLAT_SHADED\n\tvarying vec3 vNormal;\n\t#ifdef USE_TANGENT\n\t\tvarying vec3 vTangent;\n\t\tvarying vec3 vBitangent;\n\t#endif\n#endif\n#include <common>\n#include <uv_pars_vertex>\n#include <uv2_pars_vertex>\n#include <displacementmap_pars_vertex>\n#include <color_pars_vertex>\n#include <fog_pars_vertex>\n#include <morphtarget_pars_vertex>\n#include <skinning_pars_vertex>\n#include <shadowmap_pars_vertex>\n#include <logdepthbuf_pars_vertex>\n#include <clipping_planes_pars_vertex>\nvoid main() {\n\t#include <uv_vertex>\n\t#include <uv2_vertex>\n\t#include <color_vertex>\n\t#include <beginnormal_vertex>\n\t#include <morphnormal_vertex>\n\t#include <skinbase_vertex>\n\t#include <skinnormal_vertex>\n\t#include <defaultnormal_vertex>\n#ifndef FLAT_SHADED\n\tvNormal = normalize( transformedNormal );\n\t#ifdef USE_TANGENT\n\t\tvTangent = normalize( transformedTangent );\n\t\tvBitangent = normalize( cross( vNormal, vTangent ) * tangent.w );\n\t#endif\n#endif\n\t#include <begin_vertex>\n\t#include <morphtarget_vertex>\n\t#include <skinning_vertex>\n\t#include <displacementmap_vertex>\n\t#include <project_vertex>\n\t#include <logdepthbuf_vertex>\n\t#include <clipping_planes_vertex>\n\tvViewPosition = - mvPosition.xyz;\n\t#include <worldpos_vertex>\n\t#include <shadowmap_vertex>\n\t#include <fog_vertex>\n}";
- var normal_frag = "#define NORMAL\nuniform float opacity;\n#if defined( FLAT_SHADED ) || defined( USE_BUMPMAP ) || defined( TANGENTSPACE_NORMALMAP )\n\tvarying vec3 vViewPosition;\n#endif\n#ifndef FLAT_SHADED\n\tvarying vec3 vNormal;\n\t#ifdef USE_TANGENT\n\t\tvarying vec3 vTangent;\n\t\tvarying vec3 vBitangent;\n\t#endif\n#endif\n#include <packing>\n#include <uv_pars_fragment>\n#include <bumpmap_pars_fragment>\n#include <normalmap_pars_fragment>\n#include <logdepthbuf_pars_fragment>\n#include <clipping_planes_pars_fragment>\nvoid main() {\n\t#include <clipping_planes_fragment>\n\t#include <logdepthbuf_fragment>\n\t#include <normal_fragment_begin>\n\t#include <normal_fragment_maps>\n\tgl_FragColor = vec4( packNormalToRGB( normal ), opacity );\n}";
- var normal_vert = "#define NORMAL\n#if defined( FLAT_SHADED ) || defined( USE_BUMPMAP ) || defined( TANGENTSPACE_NORMALMAP )\n\tvarying vec3 vViewPosition;\n#endif\n#ifndef FLAT_SHADED\n\tvarying vec3 vNormal;\n\t#ifdef USE_TANGENT\n\t\tvarying vec3 vTangent;\n\t\tvarying vec3 vBitangent;\n\t#endif\n#endif\n#include <common>\n#include <uv_pars_vertex>\n#include <displacementmap_pars_vertex>\n#include <morphtarget_pars_vertex>\n#include <skinning_pars_vertex>\n#include <logdepthbuf_pars_vertex>\n#include <clipping_planes_pars_vertex>\nvoid main() {\n\t#include <uv_vertex>\n\t#include <beginnormal_vertex>\n\t#include <morphnormal_vertex>\n\t#include <skinbase_vertex>\n\t#include <skinnormal_vertex>\n\t#include <defaultnormal_vertex>\n#ifndef FLAT_SHADED\n\tvNormal = normalize( transformedNormal );\n\t#ifdef USE_TANGENT\n\t\tvTangent = normalize( transformedTangent );\n\t\tvBitangent = normalize( cross( vNormal, vTangent ) * tangent.w );\n\t#endif\n#endif\n\t#include <begin_vertex>\n\t#include <morphtarget_vertex>\n\t#include <skinning_vertex>\n\t#include <displacementmap_vertex>\n\t#include <project_vertex>\n\t#include <logdepthbuf_vertex>\n\t#include <clipping_planes_vertex>\n#if defined( FLAT_SHADED ) || defined( USE_BUMPMAP ) || defined( TANGENTSPACE_NORMALMAP )\n\tvViewPosition = - mvPosition.xyz;\n#endif\n}";
- var points_frag = "uniform vec3 diffuse;\nuniform float opacity;\n#include <common>\n#include <color_pars_fragment>\n#include <map_particle_pars_fragment>\n#include <fog_pars_fragment>\n#include <logdepthbuf_pars_fragment>\n#include <clipping_planes_pars_fragment>\nvoid main() {\n\t#include <clipping_planes_fragment>\n\tvec3 outgoingLight = vec3( 0.0 );\n\tvec4 diffuseColor = vec4( diffuse, opacity );\n\t#include <logdepthbuf_fragment>\n\t#include <map_particle_fragment>\n\t#include <color_fragment>\n\t#include <alphatest_fragment>\n\toutgoingLight = diffuseColor.rgb;\n\tgl_FragColor = vec4( outgoingLight, diffuseColor.a );\n\t#include <tonemapping_fragment>\n\t#include <encodings_fragment>\n\t#include <fog_fragment>\n\t#include <premultiplied_alpha_fragment>\n}";
- var points_vert = "uniform float size;\nuniform float scale;\n#include <common>\n#include <color_pars_vertex>\n#include <fog_pars_vertex>\n#include <morphtarget_pars_vertex>\n#include <logdepthbuf_pars_vertex>\n#include <clipping_planes_pars_vertex>\nvoid main() {\n\t#include <color_vertex>\n\t#include <begin_vertex>\n\t#include <morphtarget_vertex>\n\t#include <project_vertex>\n\tgl_PointSize = size;\n\t#ifdef USE_SIZEATTENUATION\n\t\tbool isPerspective = isPerspectiveMatrix( projectionMatrix );\n\t\tif ( isPerspective ) gl_PointSize *= ( scale / - mvPosition.z );\n\t#endif\n\t#include <logdepthbuf_vertex>\n\t#include <clipping_planes_vertex>\n\t#include <worldpos_vertex>\n\t#include <fog_vertex>\n}";
- var shadow_frag = "uniform vec3 color;\nuniform float opacity;\n#include <common>\n#include <packing>\n#include <fog_pars_fragment>\n#include <bsdfs>\n#include <lights_pars_begin>\n#include <shadowmap_pars_fragment>\n#include <shadowmask_pars_fragment>\nvoid main() {\n\tgl_FragColor = vec4( color, opacity * ( 1.0 - getShadowMask() ) );\n\t#include <tonemapping_fragment>\n\t#include <encodings_fragment>\n\t#include <fog_fragment>\n}";
- var shadow_vert = "#include <common>\n#include <fog_pars_vertex>\n#include <shadowmap_pars_vertex>\nvoid main() {\n\t#include <begin_vertex>\n\t#include <project_vertex>\n\t#include <worldpos_vertex>\n\t#include <beginnormal_vertex>\n\t#include <morphnormal_vertex>\n\t#include <skinbase_vertex>\n\t#include <skinnormal_vertex>\n\t#include <defaultnormal_vertex>\n\t#include <shadowmap_vertex>\n\t#include <fog_vertex>\n}";
- var sprite_frag = "uniform vec3 diffuse;\nuniform float opacity;\n#include <common>\n#include <uv_pars_fragment>\n#include <map_pars_fragment>\n#include <alphamap_pars_fragment>\n#include <fog_pars_fragment>\n#include <logdepthbuf_pars_fragment>\n#include <clipping_planes_pars_fragment>\nvoid main() {\n\t#include <clipping_planes_fragment>\n\tvec3 outgoingLight = vec3( 0.0 );\n\tvec4 diffuseColor = vec4( diffuse, opacity );\n\t#include <logdepthbuf_fragment>\n\t#include <map_fragment>\n\t#include <alphamap_fragment>\n\t#include <alphatest_fragment>\n\toutgoingLight = diffuseColor.rgb;\n\tgl_FragColor = vec4( outgoingLight, diffuseColor.a );\n\t#include <tonemapping_fragment>\n\t#include <encodings_fragment>\n\t#include <fog_fragment>\n}";
- var sprite_vert = "uniform float rotation;\nuniform vec2 center;\n#include <common>\n#include <uv_pars_vertex>\n#include <fog_pars_vertex>\n#include <logdepthbuf_pars_vertex>\n#include <clipping_planes_pars_vertex>\nvoid main() {\n\t#include <uv_vertex>\n\tvec4 mvPosition = modelViewMatrix * vec4( 0.0, 0.0, 0.0, 1.0 );\n\tvec2 scale;\n\tscale.x = length( vec3( modelMatrix[ 0 ].x, modelMatrix[ 0 ].y, modelMatrix[ 0 ].z ) );\n\tscale.y = length( vec3( modelMatrix[ 1 ].x, modelMatrix[ 1 ].y, modelMatrix[ 1 ].z ) );\n\t#ifndef USE_SIZEATTENUATION\n\t\tbool isPerspective = isPerspectiveMatrix( projectionMatrix );\n\t\tif ( isPerspective ) scale *= - mvPosition.z;\n\t#endif\n\tvec2 alignedPosition = ( position.xy - ( center - vec2( 0.5 ) ) ) * scale;\n\tvec2 rotatedPosition;\n\trotatedPosition.x = cos( rotation ) * alignedPosition.x - sin( rotation ) * alignedPosition.y;\n\trotatedPosition.y = sin( rotation ) * alignedPosition.x + cos( rotation ) * alignedPosition.y;\n\tmvPosition.xy += rotatedPosition;\n\tgl_Position = projectionMatrix * mvPosition;\n\t#include <logdepthbuf_vertex>\n\t#include <clipping_planes_vertex>\n\t#include <fog_vertex>\n}";
- const ShaderChunk = {
- alphamap_fragment: alphamap_fragment,
- alphamap_pars_fragment: alphamap_pars_fragment,
- alphatest_fragment: alphatest_fragment,
- aomap_fragment: aomap_fragment,
- aomap_pars_fragment: aomap_pars_fragment,
- begin_vertex: begin_vertex,
- beginnormal_vertex: beginnormal_vertex,
- bsdfs: bsdfs,
- bumpmap_pars_fragment: bumpmap_pars_fragment,
- clipping_planes_fragment: clipping_planes_fragment,
- clipping_planes_pars_fragment: clipping_planes_pars_fragment,
- clipping_planes_pars_vertex: clipping_planes_pars_vertex,
- clipping_planes_vertex: clipping_planes_vertex,
- color_fragment: color_fragment,
- color_pars_fragment: color_pars_fragment,
- color_pars_vertex: color_pars_vertex,
- color_vertex: color_vertex,
- common: common,
- cube_uv_reflection_fragment: cube_uv_reflection_fragment,
- defaultnormal_vertex: defaultnormal_vertex,
- displacementmap_pars_vertex: displacementmap_pars_vertex,
- displacementmap_vertex: displacementmap_vertex,
- emissivemap_fragment: emissivemap_fragment,
- emissivemap_pars_fragment: emissivemap_pars_fragment,
- encodings_fragment: encodings_fragment,
- encodings_pars_fragment: encodings_pars_fragment,
- envmap_fragment: envmap_fragment,
- envmap_common_pars_fragment: envmap_common_pars_fragment,
- envmap_pars_fragment: envmap_pars_fragment,
- envmap_pars_vertex: envmap_pars_vertex,
- envmap_physical_pars_fragment: envmap_physical_pars_fragment,
- envmap_vertex: envmap_vertex,
- fog_vertex: fog_vertex,
- fog_pars_vertex: fog_pars_vertex,
- fog_fragment: fog_fragment,
- fog_pars_fragment: fog_pars_fragment,
- gradientmap_pars_fragment: gradientmap_pars_fragment,
- lightmap_fragment: lightmap_fragment,
- lightmap_pars_fragment: lightmap_pars_fragment,
- lights_lambert_vertex: lights_lambert_vertex,
- lights_pars_begin: lights_pars_begin,
- lights_toon_fragment: lights_toon_fragment,
- lights_toon_pars_fragment: lights_toon_pars_fragment,
- lights_phong_fragment: lights_phong_fragment,
- lights_phong_pars_fragment: lights_phong_pars_fragment,
- lights_physical_fragment: lights_physical_fragment,
- lights_physical_pars_fragment: lights_physical_pars_fragment,
- lights_fragment_begin: lights_fragment_begin,
- lights_fragment_maps: lights_fragment_maps,
- lights_fragment_end: lights_fragment_end,
- logdepthbuf_fragment: logdepthbuf_fragment,
- logdepthbuf_pars_fragment: logdepthbuf_pars_fragment,
- logdepthbuf_pars_vertex: logdepthbuf_pars_vertex,
- logdepthbuf_vertex: logdepthbuf_vertex,
- map_fragment: map_fragment,
- map_pars_fragment: map_pars_fragment,
- map_particle_fragment: map_particle_fragment,
- map_particle_pars_fragment: map_particle_pars_fragment,
- metalnessmap_fragment: metalnessmap_fragment,
- metalnessmap_pars_fragment: metalnessmap_pars_fragment,
- morphnormal_vertex: morphnormal_vertex,
- morphtarget_pars_vertex: morphtarget_pars_vertex,
- morphtarget_vertex: morphtarget_vertex,
- normal_fragment_begin: normal_fragment_begin,
- normal_fragment_maps: normal_fragment_maps,
- normalmap_pars_fragment: normalmap_pars_fragment,
- clearcoat_normal_fragment_begin: clearcoat_normal_fragment_begin,
- clearcoat_normal_fragment_maps: clearcoat_normal_fragment_maps,
- clearcoat_pars_fragment: clearcoat_pars_fragment,
- packing: packing,
- premultiplied_alpha_fragment: premultiplied_alpha_fragment,
- project_vertex: project_vertex,
- dithering_fragment: dithering_fragment,
- dithering_pars_fragment: dithering_pars_fragment,
- roughnessmap_fragment: roughnessmap_fragment,
- roughnessmap_pars_fragment: roughnessmap_pars_fragment,
- shadowmap_pars_fragment: shadowmap_pars_fragment,
- shadowmap_pars_vertex: shadowmap_pars_vertex,
- shadowmap_vertex: shadowmap_vertex,
- shadowmask_pars_fragment: shadowmask_pars_fragment,
- skinbase_vertex: skinbase_vertex,
- skinning_pars_vertex: skinning_pars_vertex,
- skinning_vertex: skinning_vertex,
- skinnormal_vertex: skinnormal_vertex,
- specularmap_fragment: specularmap_fragment,
- specularmap_pars_fragment: specularmap_pars_fragment,
- tonemapping_fragment: tonemapping_fragment,
- tonemapping_pars_fragment: tonemapping_pars_fragment,
- transmissionmap_fragment: transmissionmap_fragment,
- transmissionmap_pars_fragment: transmissionmap_pars_fragment,
- uv_pars_fragment: uv_pars_fragment,
- uv_pars_vertex: uv_pars_vertex,
- uv_vertex: uv_vertex,
- uv2_pars_fragment: uv2_pars_fragment,
- uv2_pars_vertex: uv2_pars_vertex,
- uv2_vertex: uv2_vertex,
- worldpos_vertex: worldpos_vertex,
- background_frag: background_frag,
- background_vert: background_vert,
- cube_frag: cube_frag,
- cube_vert: cube_vert,
- depth_frag: depth_frag,
- depth_vert: depth_vert,
- distanceRGBA_frag: distanceRGBA_frag,
- distanceRGBA_vert: distanceRGBA_vert,
- equirect_frag: equirect_frag,
- equirect_vert: equirect_vert,
- linedashed_frag: linedashed_frag,
- linedashed_vert: linedashed_vert,
- meshbasic_frag: meshbasic_frag,
- meshbasic_vert: meshbasic_vert,
- meshlambert_frag: meshlambert_frag,
- meshlambert_vert: meshlambert_vert,
- meshmatcap_frag: meshmatcap_frag,
- meshmatcap_vert: meshmatcap_vert,
- meshtoon_frag: meshtoon_frag,
- meshtoon_vert: meshtoon_vert,
- meshphong_frag: meshphong_frag,
- meshphong_vert: meshphong_vert,
- meshphysical_frag: meshphysical_frag,
- meshphysical_vert: meshphysical_vert,
- normal_frag: normal_frag,
- normal_vert: normal_vert,
- points_frag: points_frag,
- points_vert: points_vert,
- shadow_frag: shadow_frag,
- shadow_vert: shadow_vert,
- sprite_frag: sprite_frag,
- sprite_vert: sprite_vert
- };
- /**
- * Uniforms library for shared webgl shaders
- */
- const UniformsLib = {
- common: {
- diffuse: { value: new Color( 0xeeeeee ) },
- opacity: { value: 1.0 },
- map: { value: null },
- uvTransform: { value: new Matrix3() },
- uv2Transform: { value: new Matrix3() },
- alphaMap: { value: null },
- },
- specularmap: {
- specularMap: { value: null },
- },
- envmap: {
- envMap: { value: null },
- flipEnvMap: { value: - 1 },
- reflectivity: { value: 1.0 },
- refractionRatio: { value: 0.98 },
- maxMipLevel: { value: 0 }
- },
- aomap: {
- aoMap: { value: null },
- aoMapIntensity: { value: 1 }
- },
- lightmap: {
- lightMap: { value: null },
- lightMapIntensity: { value: 1 }
- },
- emissivemap: {
- emissiveMap: { value: null }
- },
- bumpmap: {
- bumpMap: { value: null },
- bumpScale: { value: 1 }
- },
- normalmap: {
- normalMap: { value: null },
- normalScale: { value: new Vector2$1( 1, 1 ) }
- },
- displacementmap: {
- displacementMap: { value: null },
- displacementScale: { value: 1 },
- displacementBias: { value: 0 }
- },
- roughnessmap: {
- roughnessMap: { value: null }
- },
- metalnessmap: {
- metalnessMap: { value: null }
- },
- gradientmap: {
- gradientMap: { value: null }
- },
- fog: {
- fogDensity: { value: 0.00025 },
- fogNear: { value: 1 },
- fogFar: { value: 2000 },
- fogColor: { value: new Color( 0xffffff ) }
- },
- lights: {
- ambientLightColor: { value: [] },
- lightProbe: { value: [] },
- directionalLights: { value: [], properties: {
- direction: {},
- color: {}
- } },
- directionalLightShadows: { value: [], properties: {
- shadowBias: {},
- shadowNormalBias: {},
- shadowRadius: {},
- shadowMapSize: {}
- } },
- directionalShadowMap: { value: [] },
- directionalShadowMatrix: { value: [] },
- spotLights: { value: [], properties: {
- color: {},
- position: {},
- direction: {},
- distance: {},
- coneCos: {},
- penumbraCos: {},
- decay: {}
- } },
- spotLightShadows: { value: [], properties: {
- shadowBias: {},
- shadowNormalBias: {},
- shadowRadius: {},
- shadowMapSize: {}
- } },
- spotShadowMap: { value: [] },
- spotShadowMatrix: { value: [] },
- pointLights: { value: [], properties: {
- color: {},
- position: {},
- decay: {},
- distance: {}
- } },
- pointLightShadows: { value: [], properties: {
- shadowBias: {},
- shadowNormalBias: {},
- shadowRadius: {},
- shadowMapSize: {},
- shadowCameraNear: {},
- shadowCameraFar: {}
- } },
- pointShadowMap: { value: [] },
- pointShadowMatrix: { value: [] },
- hemisphereLights: { value: [], properties: {
- direction: {},
- skyColor: {},
- groundColor: {}
- } },
- // TODO (abelnation): RectAreaLight BRDF data needs to be moved from example to main src
- rectAreaLights: { value: [], properties: {
- color: {},
- position: {},
- width: {},
- height: {}
- } },
- ltc_1: { value: null },
- ltc_2: { value: null }
- },
- points: {
- diffuse: { value: new Color( 0xeeeeee ) },
- opacity: { value: 1.0 },
- size: { value: 1.0 },
- scale: { value: 1.0 },
- map: { value: null },
- alphaMap: { value: null },
- uvTransform: { value: new Matrix3() }
- },
- sprite: {
- diffuse: { value: new Color( 0xeeeeee ) },
- opacity: { value: 1.0 },
- center: { value: new Vector2$1( 0.5, 0.5 ) },
- rotation: { value: 0.0 },
- map: { value: null },
- alphaMap: { value: null },
- uvTransform: { value: new Matrix3() }
- }
- };
- const ShaderLib = {
- basic: {
- uniforms: mergeUniforms( [
- UniformsLib.common,
- UniformsLib.specularmap,
- UniformsLib.envmap,
- UniformsLib.aomap,
- UniformsLib.lightmap,
- UniformsLib.fog
- ] ),
- vertexShader: ShaderChunk.meshbasic_vert,
- fragmentShader: ShaderChunk.meshbasic_frag
- },
- lambert: {
- uniforms: mergeUniforms( [
- UniformsLib.common,
- UniformsLib.specularmap,
- UniformsLib.envmap,
- UniformsLib.aomap,
- UniformsLib.lightmap,
- UniformsLib.emissivemap,
- UniformsLib.fog,
- UniformsLib.lights,
- {
- emissive: { value: new Color( 0x000000 ) }
- }
- ] ),
- vertexShader: ShaderChunk.meshlambert_vert,
- fragmentShader: ShaderChunk.meshlambert_frag
- },
- phong: {
- uniforms: mergeUniforms( [
- UniformsLib.common,
- UniformsLib.specularmap,
- UniformsLib.envmap,
- UniformsLib.aomap,
- UniformsLib.lightmap,
- UniformsLib.emissivemap,
- UniformsLib.bumpmap,
- UniformsLib.normalmap,
- UniformsLib.displacementmap,
- UniformsLib.fog,
- UniformsLib.lights,
- {
- emissive: { value: new Color( 0x000000 ) },
- specular: { value: new Color( 0x111111 ) },
- shininess: { value: 30 }
- }
- ] ),
- vertexShader: ShaderChunk.meshphong_vert,
- fragmentShader: ShaderChunk.meshphong_frag
- },
- standard: {
- uniforms: mergeUniforms( [
- UniformsLib.common,
- UniformsLib.envmap,
- UniformsLib.aomap,
- UniformsLib.lightmap,
- UniformsLib.emissivemap,
- UniformsLib.bumpmap,
- UniformsLib.normalmap,
- UniformsLib.displacementmap,
- UniformsLib.roughnessmap,
- UniformsLib.metalnessmap,
- UniformsLib.fog,
- UniformsLib.lights,
- {
- emissive: { value: new Color( 0x000000 ) },
- roughness: { value: 1.0 },
- metalness: { value: 0.0 },
- envMapIntensity: { value: 1 } // temporary
- }
- ] ),
- vertexShader: ShaderChunk.meshphysical_vert,
- fragmentShader: ShaderChunk.meshphysical_frag
- },
- toon: {
- uniforms: mergeUniforms( [
- UniformsLib.common,
- UniformsLib.aomap,
- UniformsLib.lightmap,
- UniformsLib.emissivemap,
- UniformsLib.bumpmap,
- UniformsLib.normalmap,
- UniformsLib.displacementmap,
- UniformsLib.gradientmap,
- UniformsLib.fog,
- UniformsLib.lights,
- {
- emissive: { value: new Color( 0x000000 ) }
- }
- ] ),
- vertexShader: ShaderChunk.meshtoon_vert,
- fragmentShader: ShaderChunk.meshtoon_frag
- },
- matcap: {
- uniforms: mergeUniforms( [
- UniformsLib.common,
- UniformsLib.bumpmap,
- UniformsLib.normalmap,
- UniformsLib.displacementmap,
- UniformsLib.fog,
- {
- matcap: { value: null }
- }
- ] ),
- vertexShader: ShaderChunk.meshmatcap_vert,
- fragmentShader: ShaderChunk.meshmatcap_frag
- },
- points: {
- uniforms: mergeUniforms( [
- UniformsLib.points,
- UniformsLib.fog
- ] ),
- vertexShader: ShaderChunk.points_vert,
- fragmentShader: ShaderChunk.points_frag
- },
- dashed: {
- uniforms: mergeUniforms( [
- UniformsLib.common,
- UniformsLib.fog,
- {
- scale: { value: 1 },
- dashSize: { value: 1 },
- totalSize: { value: 2 }
- }
- ] ),
- vertexShader: ShaderChunk.linedashed_vert,
- fragmentShader: ShaderChunk.linedashed_frag
- },
- depth: {
- uniforms: mergeUniforms( [
- UniformsLib.common,
- UniformsLib.displacementmap
- ] ),
- vertexShader: ShaderChunk.depth_vert,
- fragmentShader: ShaderChunk.depth_frag
- },
- normal: {
- uniforms: mergeUniforms( [
- UniformsLib.common,
- UniformsLib.bumpmap,
- UniformsLib.normalmap,
- UniformsLib.displacementmap,
- {
- opacity: { value: 1.0 }
- }
- ] ),
- vertexShader: ShaderChunk.normal_vert,
- fragmentShader: ShaderChunk.normal_frag
- },
- sprite: {
- uniforms: mergeUniforms( [
- UniformsLib.sprite,
- UniformsLib.fog
- ] ),
- vertexShader: ShaderChunk.sprite_vert,
- fragmentShader: ShaderChunk.sprite_frag
- },
- background: {
- uniforms: {
- uvTransform: { value: new Matrix3() },
- t2D: { value: null },
- },
- vertexShader: ShaderChunk.background_vert,
- fragmentShader: ShaderChunk.background_frag
- },
- /* -------------------------------------------------------------------------
- // Cube map shader
- ------------------------------------------------------------------------- */
- cube: {
- uniforms: mergeUniforms( [
- UniformsLib.envmap,
- {
- opacity: { value: 1.0 }
- }
- ] ),
- vertexShader: ShaderChunk.cube_vert,
- fragmentShader: ShaderChunk.cube_frag
- },
- equirect: {
- uniforms: {
- tEquirect: { value: null },
- },
- vertexShader: ShaderChunk.equirect_vert,
- fragmentShader: ShaderChunk.equirect_frag
- },
- distanceRGBA: {
- uniforms: mergeUniforms( [
- UniformsLib.common,
- UniformsLib.displacementmap,
- {
- referencePosition: { value: new Vector3() },
- nearDistance: { value: 1 },
- farDistance: { value: 1000 }
- }
- ] ),
- vertexShader: ShaderChunk.distanceRGBA_vert,
- fragmentShader: ShaderChunk.distanceRGBA_frag
- },
- shadow: {
- uniforms: mergeUniforms( [
- UniformsLib.lights,
- UniformsLib.fog,
- {
- color: { value: new Color( 0x00000 ) },
- opacity: { value: 1.0 }
- },
- ] ),
- vertexShader: ShaderChunk.shadow_vert,
- fragmentShader: ShaderChunk.shadow_frag
- }
- };
- ShaderLib.physical = {
- uniforms: mergeUniforms( [
- ShaderLib.standard.uniforms,
- {
- clearcoat: { value: 0 },
- clearcoatMap: { value: null },
- clearcoatRoughness: { value: 0 },
- clearcoatRoughnessMap: { value: null },
- clearcoatNormalScale: { value: new Vector2$1( 1, 1 ) },
- clearcoatNormalMap: { value: null },
- sheen: { value: new Color( 0x000000 ) },
- transmission: { value: 0 },
- transmissionMap: { value: null },
- }
- ] ),
- vertexShader: ShaderChunk.meshphysical_vert,
- fragmentShader: ShaderChunk.meshphysical_frag
- };
- function WebGLBackground( renderer, cubemaps, state, objects, premultipliedAlpha ) {
- const clearColor = new Color( 0x000000 );
- let clearAlpha = 0;
- let planeMesh;
- let boxMesh;
- let currentBackground = null;
- let currentBackgroundVersion = 0;
- let currentTonemapping = null;
- function render( renderList, scene, camera, forceClear ) {
- let background = scene.isScene === true ? scene.background : null;
- if ( background && background.isTexture ) {
- background = cubemaps.get( background );
- }
- // Ignore background in AR
- // TODO: Reconsider this.
- const xr = renderer.xr;
- const session = xr.getSession && xr.getSession();
- if ( session && session.environmentBlendMode === 'additive' ) {
- background = null;
- }
- if ( background === null ) {
- setClear( clearColor, clearAlpha );
- } else if ( background && background.isColor ) {
- setClear( background, 1 );
- forceClear = true;
- }
- if ( renderer.autoClear || forceClear ) {
- renderer.clear( renderer.autoClearColor, renderer.autoClearDepth, renderer.autoClearStencil );
- }
- if ( background && ( background.isCubeTexture || background.isWebGLCubeRenderTarget || background.mapping === CubeUVReflectionMapping ) ) {
- if ( boxMesh === undefined ) {
- boxMesh = new Mesh(
- new BoxBufferGeometry( 1, 1, 1 ),
- new ShaderMaterial( {
- name: 'BackgroundCubeMaterial',
- uniforms: cloneUniforms( ShaderLib.cube.uniforms ),
- vertexShader: ShaderLib.cube.vertexShader,
- fragmentShader: ShaderLib.cube.fragmentShader,
- side: BackSide,
- depthTest: false,
- depthWrite: false,
- fog: false
- } )
- );
- boxMesh.geometry.deleteAttribute( 'normal' );
- boxMesh.geometry.deleteAttribute( 'uv' );
- boxMesh.onBeforeRender = function ( renderer, scene, camera ) {
- this.matrixWorld.copyPosition( camera.matrixWorld );
- };
- // enable code injection for non-built-in material
- Object.defineProperty( boxMesh.material, 'envMap', {
- get: function () {
- return this.uniforms.envMap.value;
- }
- } );
- objects.update( boxMesh );
- }
- if ( background.isWebGLCubeRenderTarget ) {
- // TODO Deprecate
- background = background.texture;
- }
- boxMesh.material.uniforms.envMap.value = background;
- boxMesh.material.uniforms.flipEnvMap.value = ( background.isCubeTexture && background._needsFlipEnvMap ) ? - 1 : 1;
- if ( currentBackground !== background ||
- currentBackgroundVersion !== background.version ||
- currentTonemapping !== renderer.toneMapping ) {
- boxMesh.material.needsUpdate = true;
- currentBackground = background;
- currentBackgroundVersion = background.version;
- currentTonemapping = renderer.toneMapping;
- }
- // push to the pre-sorted opaque render list
- renderList.unshift( boxMesh, boxMesh.geometry, boxMesh.material, 0, 0, null );
- } else if ( background && background.isTexture ) {
- if ( planeMesh === undefined ) {
- planeMesh = new Mesh(
- new PlaneBufferGeometry( 2, 2 ),
- new ShaderMaterial( {
- name: 'BackgroundMaterial',
- uniforms: cloneUniforms( ShaderLib.background.uniforms ),
- vertexShader: ShaderLib.background.vertexShader,
- fragmentShader: ShaderLib.background.fragmentShader,
- side: FrontSide,
- depthTest: false,
- depthWrite: false,
- fog: false
- } )
- );
- planeMesh.geometry.deleteAttribute( 'normal' );
- // enable code injection for non-built-in material
- Object.defineProperty( planeMesh.material, 'map', {
- get: function () {
- return this.uniforms.t2D.value;
- }
- } );
- objects.update( planeMesh );
- }
- planeMesh.material.uniforms.t2D.value = background;
- if ( background.matrixAutoUpdate === true ) {
- background.updateMatrix();
- }
- planeMesh.material.uniforms.uvTransform.value.copy( background.matrix );
- if ( currentBackground !== background ||
- currentBackgroundVersion !== background.version ||
- currentTonemapping !== renderer.toneMapping ) {
- planeMesh.material.needsUpdate = true;
- currentBackground = background;
- currentBackgroundVersion = background.version;
- currentTonemapping = renderer.toneMapping;
- }
- // push to the pre-sorted opaque render list
- renderList.unshift( planeMesh, planeMesh.geometry, planeMesh.material, 0, 0, null );
- }
- }
- function setClear( color, alpha ) {
- state.buffers.color.setClear( color.r, color.g, color.b, alpha, premultipliedAlpha );
- }
- return {
- getClearColor: function () {
- return clearColor;
- },
- setClearColor: function ( color, alpha = 1 ) {
- clearColor.set( color );
- clearAlpha = alpha;
- setClear( clearColor, clearAlpha );
- },
- getClearAlpha: function () {
- return clearAlpha;
- },
- setClearAlpha: function ( alpha ) {
- clearAlpha = alpha;
- setClear( clearColor, clearAlpha );
- },
- render: render
- };
- }
- function WebGLBindingStates( gl, extensions, attributes, capabilities ) {
- const maxVertexAttributes = gl.getParameter( 34921 );
- const extension = capabilities.isWebGL2 ? null : extensions.get( 'OES_vertex_array_object' );
- const vaoAvailable = capabilities.isWebGL2 || extension !== null;
- const bindingStates = {};
- const defaultState = createBindingState( null );
- let currentState = defaultState;
- function setup( object, material, program, geometry, index ) {
- let updateBuffers = false;
- if ( vaoAvailable ) {
- const state = getBindingState( geometry, program, material );
- if ( currentState !== state ) {
- currentState = state;
- bindVertexArrayObject( currentState.object );
- }
- updateBuffers = needsUpdate( geometry, index );
- if ( updateBuffers ) saveCache( geometry, index );
- } else {
- const wireframe = ( material.wireframe === true );
- if ( currentState.geometry !== geometry.id ||
- currentState.program !== program.id ||
- currentState.wireframe !== wireframe ) {
- currentState.geometry = geometry.id;
- currentState.program = program.id;
- currentState.wireframe = wireframe;
- updateBuffers = true;
- }
- }
- if ( object.isInstancedMesh === true ) {
- updateBuffers = true;
- }
- if ( index !== null ) {
- attributes.update( index, 34963 );
- }
- if ( updateBuffers ) {
- setupVertexAttributes( object, material, program, geometry );
- if ( index !== null ) {
- gl.bindBuffer( 34963, attributes.get( index ).buffer );
- }
- }
- }
- function createVertexArrayObject() {
- if ( capabilities.isWebGL2 ) return gl.createVertexArray();
- return extension.createVertexArrayOES();
- }
- function bindVertexArrayObject( vao ) {
- if ( capabilities.isWebGL2 ) return gl.bindVertexArray( vao );
- return extension.bindVertexArrayOES( vao );
- }
- function deleteVertexArrayObject( vao ) {
- if ( capabilities.isWebGL2 ) return gl.deleteVertexArray( vao );
- return extension.deleteVertexArrayOES( vao );
- }
- function getBindingState( geometry, program, material ) {
- const wireframe = ( material.wireframe === true );
- let programMap = bindingStates[ geometry.id ];
- if ( programMap === undefined ) {
- programMap = {};
- bindingStates[ geometry.id ] = programMap;
- }
- let stateMap = programMap[ program.id ];
- if ( stateMap === undefined ) {
- stateMap = {};
- programMap[ program.id ] = stateMap;
- }
- let state = stateMap[ wireframe ];
- if ( state === undefined ) {
- state = createBindingState( createVertexArrayObject() );
- stateMap[ wireframe ] = state;
- }
- return state;
- }
- function createBindingState( vao ) {
- const newAttributes = [];
- const enabledAttributes = [];
- const attributeDivisors = [];
- for ( let i = 0; i < maxVertexAttributes; i ++ ) {
- newAttributes[ i ] = 0;
- enabledAttributes[ i ] = 0;
- attributeDivisors[ i ] = 0;
- }
- return {
- // for backward compatibility on non-VAO support browser
- geometry: null,
- program: null,
- wireframe: false,
- newAttributes: newAttributes,
- enabledAttributes: enabledAttributes,
- attributeDivisors: attributeDivisors,
- object: vao,
- attributes: {},
- index: null
- };
- }
- function needsUpdate( geometry, index ) {
- const cachedAttributes = currentState.attributes;
- const geometryAttributes = geometry.attributes;
- let attributesNum = 0;
- for ( const key in geometryAttributes ) {
- const cachedAttribute = cachedAttributes[ key ];
- const geometryAttribute = geometryAttributes[ key ];
- if ( cachedAttribute === undefined ) return true;
- if ( cachedAttribute.attribute !== geometryAttribute ) return true;
- if ( cachedAttribute.data !== geometryAttribute.data ) return true;
- attributesNum ++;
- }
- if ( currentState.attributesNum !== attributesNum ) return true;
- if ( currentState.index !== index ) return true;
- return false;
- }
- function saveCache( geometry, index ) {
- const cache = {};
- const attributes = geometry.attributes;
- let attributesNum = 0;
- for ( const key in attributes ) {
- const attribute = attributes[ key ];
- const data = {};
- data.attribute = attribute;
- if ( attribute.data ) {
- data.data = attribute.data;
- }
- cache[ key ] = data;
- attributesNum ++;
- }
- currentState.attributes = cache;
- currentState.attributesNum = attributesNum;
- currentState.index = index;
- }
- function initAttributes() {
- const newAttributes = currentState.newAttributes;
- for ( let i = 0, il = newAttributes.length; i < il; i ++ ) {
- newAttributes[ i ] = 0;
- }
- }
- function enableAttribute( attribute ) {
- enableAttributeAndDivisor( attribute, 0 );
- }
- function enableAttributeAndDivisor( attribute, meshPerAttribute ) {
- const newAttributes = currentState.newAttributes;
- const enabledAttributes = currentState.enabledAttributes;
- const attributeDivisors = currentState.attributeDivisors;
- newAttributes[ attribute ] = 1;
- if ( enabledAttributes[ attribute ] === 0 ) {
- gl.enableVertexAttribArray( attribute );
- enabledAttributes[ attribute ] = 1;
- }
- if ( attributeDivisors[ attribute ] !== meshPerAttribute ) {
- const extension = capabilities.isWebGL2 ? gl : extensions.get( 'ANGLE_instanced_arrays' );
- extension[ capabilities.isWebGL2 ? 'vertexAttribDivisor' : 'vertexAttribDivisorANGLE' ]( attribute, meshPerAttribute );
- attributeDivisors[ attribute ] = meshPerAttribute;
- }
- }
- function disableUnusedAttributes() {
- const newAttributes = currentState.newAttributes;
- const enabledAttributes = currentState.enabledAttributes;
- for ( let i = 0, il = enabledAttributes.length; i < il; i ++ ) {
- if ( enabledAttributes[ i ] !== newAttributes[ i ] ) {
- gl.disableVertexAttribArray( i );
- enabledAttributes[ i ] = 0;
- }
- }
- }
- function vertexAttribPointer( index, size, type, normalized, stride, offset ) {
- if ( capabilities.isWebGL2 === true && ( type === 5124 || type === 5125 ) ) {
- gl.vertexAttribIPointer( index, size, type, stride, offset );
- } else {
- gl.vertexAttribPointer( index, size, type, normalized, stride, offset );
- }
- }
- function setupVertexAttributes( object, material, program, geometry ) {
- if ( capabilities.isWebGL2 === false && ( object.isInstancedMesh || geometry.isInstancedBufferGeometry ) ) {
- if ( extensions.get( 'ANGLE_instanced_arrays' ) === null ) return;
- }
- initAttributes();
- const geometryAttributes = geometry.attributes;
- const programAttributes = program.getAttributes();
- const materialDefaultAttributeValues = material.defaultAttributeValues;
- for ( const name in programAttributes ) {
- const programAttribute = programAttributes[ name ];
- if ( programAttribute >= 0 ) {
- const geometryAttribute = geometryAttributes[ name ];
- if ( geometryAttribute !== undefined ) {
- const normalized = geometryAttribute.normalized;
- const size = geometryAttribute.itemSize;
- const attribute = attributes.get( geometryAttribute );
- // TODO Attribute may not be available on context restore
- if ( attribute === undefined ) continue;
- const buffer = attribute.buffer;
- const type = attribute.type;
- const bytesPerElement = attribute.bytesPerElement;
- if ( geometryAttribute.isInterleavedBufferAttribute ) {
- const data = geometryAttribute.data;
- const stride = data.stride;
- const offset = geometryAttribute.offset;
- if ( data && data.isInstancedInterleavedBuffer ) {
- enableAttributeAndDivisor( programAttribute, data.meshPerAttribute );
- if ( geometry._maxInstanceCount === undefined ) {
- geometry._maxInstanceCount = data.meshPerAttribute * data.count;
- }
- } else {
- enableAttribute( programAttribute );
- }
- gl.bindBuffer( 34962, buffer );
- vertexAttribPointer( programAttribute, size, type, normalized, stride * bytesPerElement, offset * bytesPerElement );
- } else {
- if ( geometryAttribute.isInstancedBufferAttribute ) {
- enableAttributeAndDivisor( programAttribute, geometryAttribute.meshPerAttribute );
- if ( geometry._maxInstanceCount === undefined ) {
- geometry._maxInstanceCount = geometryAttribute.meshPerAttribute * geometryAttribute.count;
- }
- } else {
- enableAttribute( programAttribute );
- }
- gl.bindBuffer( 34962, buffer );
- vertexAttribPointer( programAttribute, size, type, normalized, 0, 0 );
- }
- } else if ( name === 'instanceMatrix' ) {
- const attribute = attributes.get( object.instanceMatrix );
- // TODO Attribute may not be available on context restore
- if ( attribute === undefined ) continue;
- const buffer = attribute.buffer;
- const type = attribute.type;
- enableAttributeAndDivisor( programAttribute + 0, 1 );
- enableAttributeAndDivisor( programAttribute + 1, 1 );
- enableAttributeAndDivisor( programAttribute + 2, 1 );
- enableAttributeAndDivisor( programAttribute + 3, 1 );
- gl.bindBuffer( 34962, buffer );
- gl.vertexAttribPointer( programAttribute + 0, 4, type, false, 64, 0 );
- gl.vertexAttribPointer( programAttribute + 1, 4, type, false, 64, 16 );
- gl.vertexAttribPointer( programAttribute + 2, 4, type, false, 64, 32 );
- gl.vertexAttribPointer( programAttribute + 3, 4, type, false, 64, 48 );
- } else if ( name === 'instanceColor' ) {
- const attribute = attributes.get( object.instanceColor );
- // TODO Attribute may not be available on context restore
- if ( attribute === undefined ) continue;
- const buffer = attribute.buffer;
- const type = attribute.type;
- enableAttributeAndDivisor( programAttribute, 1 );
- gl.bindBuffer( 34962, buffer );
- gl.vertexAttribPointer( programAttribute, 3, type, false, 12, 0 );
- } else if ( materialDefaultAttributeValues !== undefined ) {
- const value = materialDefaultAttributeValues[ name ];
- if ( value !== undefined ) {
- switch ( value.length ) {
- case 2:
- gl.vertexAttrib2fv( programAttribute, value );
- break;
- case 3:
- gl.vertexAttrib3fv( programAttribute, value );
- break;
- case 4:
- gl.vertexAttrib4fv( programAttribute, value );
- break;
- default:
- gl.vertexAttrib1fv( programAttribute, value );
- }
- }
- }
- }
- }
- disableUnusedAttributes();
- }
- function dispose() {
- reset();
- for ( const geometryId in bindingStates ) {
- const programMap = bindingStates[ geometryId ];
- for ( const programId in programMap ) {
- const stateMap = programMap[ programId ];
- for ( const wireframe in stateMap ) {
- deleteVertexArrayObject( stateMap[ wireframe ].object );
- delete stateMap[ wireframe ];
- }
- delete programMap[ programId ];
- }
- delete bindingStates[ geometryId ];
- }
- }
- function releaseStatesOfGeometry( geometry ) {
- if ( bindingStates[ geometry.id ] === undefined ) return;
- const programMap = bindingStates[ geometry.id ];
- for ( const programId in programMap ) {
- const stateMap = programMap[ programId ];
- for ( const wireframe in stateMap ) {
- deleteVertexArrayObject( stateMap[ wireframe ].object );
- delete stateMap[ wireframe ];
- }
- delete programMap[ programId ];
- }
- delete bindingStates[ geometry.id ];
- }
- function releaseStatesOfProgram( program ) {
- for ( const geometryId in bindingStates ) {
- const programMap = bindingStates[ geometryId ];
- if ( programMap[ program.id ] === undefined ) continue;
- const stateMap = programMap[ program.id ];
- for ( const wireframe in stateMap ) {
- deleteVertexArrayObject( stateMap[ wireframe ].object );
- delete stateMap[ wireframe ];
- }
- delete programMap[ program.id ];
- }
- }
- function reset() {
- resetDefaultState();
- if ( currentState === defaultState ) return;
- currentState = defaultState;
- bindVertexArrayObject( currentState.object );
- }
- // for backward-compatilibity
- function resetDefaultState() {
- defaultState.geometry = null;
- defaultState.program = null;
- defaultState.wireframe = false;
- }
- return {
- setup: setup,
- reset: reset,
- resetDefaultState: resetDefaultState,
- dispose: dispose,
- releaseStatesOfGeometry: releaseStatesOfGeometry,
- releaseStatesOfProgram: releaseStatesOfProgram,
- initAttributes: initAttributes,
- enableAttribute: enableAttribute,
- disableUnusedAttributes: disableUnusedAttributes
- };
- }
- function WebGLBufferRenderer( gl, extensions, info, capabilities ) {
- const isWebGL2 = capabilities.isWebGL2;
- let mode;
- function setMode( value ) {
- mode = value;
- }
- function render( start, count ) {
- gl.drawArrays( mode, start, count );
- info.update( count, mode, 1 );
- }
- function renderInstances( start, count, primcount ) {
- if ( primcount === 0 ) return;
- let extension, methodName;
- if ( isWebGL2 ) {
- extension = gl;
- methodName = 'drawArraysInstanced';
- } else {
- extension = extensions.get( 'ANGLE_instanced_arrays' );
- methodName = 'drawArraysInstancedANGLE';
- if ( extension === null ) {
- console.error( 'THREE.WebGLBufferRenderer: using THREE.InstancedBufferGeometry but hardware does not support extension ANGLE_instanced_arrays.' );
- return;
- }
- }
- extension[ methodName ]( mode, start, count, primcount );
- info.update( count, mode, primcount );
- }
- //
- this.setMode = setMode;
- this.render = render;
- this.renderInstances = renderInstances;
- }
- function WebGLCapabilities( gl, extensions, parameters ) {
- let maxAnisotropy;
- function getMaxAnisotropy() {
- if ( maxAnisotropy !== undefined ) return maxAnisotropy;
- const extension = extensions.get( 'EXT_texture_filter_anisotropic' );
- if ( extension !== null ) {
- maxAnisotropy = gl.getParameter( extension.MAX_TEXTURE_MAX_ANISOTROPY_EXT );
- } else {
- maxAnisotropy = 0;
- }
- return maxAnisotropy;
- }
- function getMaxPrecision( precision ) {
- if ( precision === 'highp' ) {
- if ( gl.getShaderPrecisionFormat( 35633, 36338 ).precision > 0 &&
- gl.getShaderPrecisionFormat( 35632, 36338 ).precision > 0 ) {
- return 'highp';
- }
- precision = 'mediump';
- }
- if ( precision === 'mediump' ) {
- if ( gl.getShaderPrecisionFormat( 35633, 36337 ).precision > 0 &&
- gl.getShaderPrecisionFormat( 35632, 36337 ).precision > 0 ) {
- return 'mediump';
- }
- }
- return 'lowp';
- }
- /* eslint-disable no-undef */
- const isWebGL2 = ( typeof WebGL2RenderingContext !== 'undefined' && gl instanceof WebGL2RenderingContext ) ||
- ( typeof WebGL2ComputeRenderingContext !== 'undefined' && gl instanceof WebGL2ComputeRenderingContext );
- /* eslint-enable no-undef */
- let precision = parameters.precision !== undefined ? parameters.precision : 'highp';
- const maxPrecision = getMaxPrecision( precision );
- if ( maxPrecision !== precision ) {
- console.warn( 'THREE.WebGLRenderer:', precision, 'not supported, using', maxPrecision, 'instead.' );
- precision = maxPrecision;
- }
- const logarithmicDepthBuffer = parameters.logarithmicDepthBuffer === true;
- const maxTextures = gl.getParameter( 34930 );
- const maxVertexTextures = gl.getParameter( 35660 );
- const maxTextureSize = gl.getParameter( 3379 );
- const maxCubemapSize = gl.getParameter( 34076 );
- const maxAttributes = gl.getParameter( 34921 );
- const maxVertexUniforms = gl.getParameter( 36347 );
- const maxVaryings = gl.getParameter( 36348 );
- const maxFragmentUniforms = gl.getParameter( 36349 );
- const vertexTextures = maxVertexTextures > 0;
- const floatFragmentTextures = isWebGL2 || !! extensions.get( 'OES_texture_float' );
- const floatVertexTextures = vertexTextures && floatFragmentTextures;
- const maxSamples = isWebGL2 ? gl.getParameter( 36183 ) : 0;
- return {
- isWebGL2: isWebGL2,
- getMaxAnisotropy: getMaxAnisotropy,
- getMaxPrecision: getMaxPrecision,
- precision: precision,
- logarithmicDepthBuffer: logarithmicDepthBuffer,
- maxTextures: maxTextures,
- maxVertexTextures: maxVertexTextures,
- maxTextureSize: maxTextureSize,
- maxCubemapSize: maxCubemapSize,
- maxAttributes: maxAttributes,
- maxVertexUniforms: maxVertexUniforms,
- maxVaryings: maxVaryings,
- maxFragmentUniforms: maxFragmentUniforms,
- vertexTextures: vertexTextures,
- floatFragmentTextures: floatFragmentTextures,
- floatVertexTextures: floatVertexTextures,
- maxSamples: maxSamples
- };
- }
- function WebGLClipping( properties ) {
- const scope = this;
- let globalState = null,
- numGlobalPlanes = 0,
- localClippingEnabled = false,
- renderingShadows = false;
- const plane = new Plane(),
- viewNormalMatrix = new Matrix3(),
- uniform = { value: null, needsUpdate: false };
- this.uniform = uniform;
- this.numPlanes = 0;
- this.numIntersection = 0;
- this.init = function ( planes, enableLocalClipping, camera ) {
- const enabled =
- planes.length !== 0 ||
- enableLocalClipping ||
- // enable state of previous frame - the clipping code has to
- // run another frame in order to reset the state:
- numGlobalPlanes !== 0 ||
- localClippingEnabled;
- localClippingEnabled = enableLocalClipping;
- globalState = projectPlanes( planes, camera, 0 );
- numGlobalPlanes = planes.length;
- return enabled;
- };
- this.beginShadows = function () {
- renderingShadows = true;
- projectPlanes( null );
- };
- this.endShadows = function () {
- renderingShadows = false;
- resetGlobalState();
- };
- this.setState = function ( material, camera, useCache ) {
- const planes = material.clippingPlanes,
- clipIntersection = material.clipIntersection,
- clipShadows = material.clipShadows;
- const materialProperties = properties.get( material );
- if ( ! localClippingEnabled || planes === null || planes.length === 0 || renderingShadows && ! clipShadows ) {
- // there's no local clipping
- if ( renderingShadows ) {
- // there's no global clipping
- projectPlanes( null );
- } else {
- resetGlobalState();
- }
- } else {
- const nGlobal = renderingShadows ? 0 : numGlobalPlanes,
- lGlobal = nGlobal * 4;
- let dstArray = materialProperties.clippingState || null;
- uniform.value = dstArray; // ensure unique state
- dstArray = projectPlanes( planes, camera, lGlobal, useCache );
- for ( let i = 0; i !== lGlobal; ++ i ) {
- dstArray[ i ] = globalState[ i ];
- }
- materialProperties.clippingState = dstArray;
- this.numIntersection = clipIntersection ? this.numPlanes : 0;
- this.numPlanes += nGlobal;
- }
- };
- function resetGlobalState() {
- if ( uniform.value !== globalState ) {
- uniform.value = globalState;
- uniform.needsUpdate = numGlobalPlanes > 0;
- }
- scope.numPlanes = numGlobalPlanes;
- scope.numIntersection = 0;
- }
- function projectPlanes( planes, camera, dstOffset, skipTransform ) {
- const nPlanes = planes !== null ? planes.length : 0;
- let dstArray = null;
- if ( nPlanes !== 0 ) {
- dstArray = uniform.value;
- if ( skipTransform !== true || dstArray === null ) {
- const flatSize = dstOffset + nPlanes * 4,
- viewMatrix = camera.matrixWorldInverse;
- viewNormalMatrix.getNormalMatrix( viewMatrix );
- if ( dstArray === null || dstArray.length < flatSize ) {
- dstArray = new Float32Array( flatSize );
- }
- for ( let i = 0, i4 = dstOffset; i !== nPlanes; ++ i, i4 += 4 ) {
- plane.copy( planes[ i ] ).applyMatrix4( viewMatrix, viewNormalMatrix );
- plane.normal.toArray( dstArray, i4 );
- dstArray[ i4 + 3 ] = plane.constant;
- }
- }
- uniform.value = dstArray;
- uniform.needsUpdate = true;
- }
- scope.numPlanes = nPlanes;
- scope.numIntersection = 0;
- return dstArray;
- }
- }
- function WebGLCubeMaps( renderer ) {
- let cubemaps = new WeakMap();
- function mapTextureMapping( texture, mapping ) {
- if ( mapping === EquirectangularReflectionMapping ) {
- texture.mapping = CubeReflectionMapping;
- } else if ( mapping === EquirectangularRefractionMapping ) {
- texture.mapping = CubeRefractionMapping;
- }
- return texture;
- }
- function get( texture ) {
- if ( texture && texture.isTexture ) {
- const mapping = texture.mapping;
- if ( mapping === EquirectangularReflectionMapping || mapping === EquirectangularRefractionMapping ) {
- if ( cubemaps.has( texture ) ) {
- const cubemap = cubemaps.get( texture ).texture;
- return mapTextureMapping( cubemap, texture.mapping );
- } else {
- const image = texture.image;
- if ( image && image.height > 0 ) {
- const currentRenderList = renderer.getRenderList();
- const currentRenderTarget = renderer.getRenderTarget();
- const renderTarget = new WebGLCubeRenderTarget( image.height / 2 );
- renderTarget.fromEquirectangularTexture( renderer, texture );
- cubemaps.set( texture, renderTarget );
- renderer.setRenderTarget( currentRenderTarget );
- renderer.setRenderList( currentRenderList );
- texture.addEventListener( 'dispose', onTextureDispose );
- return mapTextureMapping( renderTarget.texture, texture.mapping );
- } else {
- // image not yet ready. try the conversion next frame
- return null;
- }
- }
- }
- }
- return texture;
- }
- function onTextureDispose( event ) {
- const texture = event.target;
- texture.removeEventListener( 'dispose', onTextureDispose );
- const cubemap = cubemaps.get( texture );
- if ( cubemap !== undefined ) {
- cubemaps.delete( texture );
- cubemap.dispose();
- }
- }
- function dispose() {
- cubemaps = new WeakMap();
- }
- return {
- get: get,
- dispose: dispose
- };
- }
- function WebGLExtensions( gl ) {
- const extensions = {};
- return {
- has: function ( name ) {
- if ( extensions[ name ] !== undefined ) {
- return extensions[ name ] !== null;
- }
- let extension;
- switch ( name ) {
- case 'WEBGL_depth_texture':
- extension = gl.getExtension( 'WEBGL_depth_texture' ) || gl.getExtension( 'MOZ_WEBGL_depth_texture' ) || gl.getExtension( 'WEBKIT_WEBGL_depth_texture' );
- break;
- case 'EXT_texture_filter_anisotropic':
- extension = gl.getExtension( 'EXT_texture_filter_anisotropic' ) || gl.getExtension( 'MOZ_EXT_texture_filter_anisotropic' ) || gl.getExtension( 'WEBKIT_EXT_texture_filter_anisotropic' );
- break;
- case 'WEBGL_compressed_texture_s3tc':
- extension = gl.getExtension( 'WEBGL_compressed_texture_s3tc' ) || gl.getExtension( 'MOZ_WEBGL_compressed_texture_s3tc' ) || gl.getExtension( 'WEBKIT_WEBGL_compressed_texture_s3tc' );
- break;
- case 'WEBGL_compressed_texture_pvrtc':
- extension = gl.getExtension( 'WEBGL_compressed_texture_pvrtc' ) || gl.getExtension( 'WEBKIT_WEBGL_compressed_texture_pvrtc' );
- break;
- default:
- extension = gl.getExtension( name );
- }
- extensions[ name ] = extension;
- return extension !== null;
- },
- get: function ( name ) {
- if ( ! this.has( name ) ) {
- console.warn( 'THREE.WebGLRenderer: ' + name + ' extension not supported.' );
- }
- return extensions[ name ];
- }
- };
- }
- function WebGLGeometries( gl, attributes, info, bindingStates ) {
- const geometries = new WeakMap();
- const wireframeAttributes = new WeakMap();
- function onGeometryDispose( event ) {
- const geometry = event.target;
- const buffergeometry = geometries.get( geometry );
- if ( buffergeometry.index !== null ) {
- attributes.remove( buffergeometry.index );
- }
- for ( const name in buffergeometry.attributes ) {
- attributes.remove( buffergeometry.attributes[ name ] );
- }
- geometry.removeEventListener( 'dispose', onGeometryDispose );
- geometries.delete( geometry );
- const attribute = wireframeAttributes.get( buffergeometry );
- if ( attribute ) {
- attributes.remove( attribute );
- wireframeAttributes.delete( buffergeometry );
- }
- bindingStates.releaseStatesOfGeometry( buffergeometry );
- if ( geometry.isInstancedBufferGeometry === true ) {
- delete geometry._maxInstanceCount;
- }
- //
- info.memory.geometries --;
- }
- function get( object, geometry ) {
- let buffergeometry = geometries.get( geometry );
- if ( buffergeometry ) return buffergeometry;
- geometry.addEventListener( 'dispose', onGeometryDispose );
- if ( geometry.isBufferGeometry ) {
- buffergeometry = geometry;
- } else if ( geometry.isGeometry ) {
- if ( geometry._bufferGeometry === undefined ) {
- geometry._bufferGeometry = new BufferGeometry().setFromObject( object );
- }
- buffergeometry = geometry._bufferGeometry;
- }
- geometries.set( geometry, buffergeometry );
- info.memory.geometries ++;
- return buffergeometry;
- }
- function update( geometry ) {
- const geometryAttributes = geometry.attributes;
- // Updating index buffer in VAO now. See WebGLBindingStates.
- for ( const name in geometryAttributes ) {
- attributes.update( geometryAttributes[ name ], 34962 );
- }
- // morph targets
- const morphAttributes = geometry.morphAttributes;
- for ( const name in morphAttributes ) {
- const array = morphAttributes[ name ];
- for ( let i = 0, l = array.length; i < l; i ++ ) {
- attributes.update( array[ i ], 34962 );
- }
- }
- }
- function updateWireframeAttribute( geometry ) {
- const indices = [];
- const geometryIndex = geometry.index;
- const geometryPosition = geometry.attributes.position;
- let version = 0;
- if ( geometryIndex !== null ) {
- const array = geometryIndex.array;
- version = geometryIndex.version;
- for ( let i = 0, l = array.length; i < l; i += 3 ) {
- const a = array[ i + 0 ];
- const b = array[ i + 1 ];
- const c = array[ i + 2 ];
- indices.push( a, b, b, c, c, a );
- }
- } else {
- const array = geometryPosition.array;
- version = geometryPosition.version;
- for ( let i = 0, l = ( array.length / 3 ) - 1; i < l; i += 3 ) {
- const a = i + 0;
- const b = i + 1;
- const c = i + 2;
- indices.push( a, b, b, c, c, a );
- }
- }
- const attribute = new ( arrayMax( indices ) > 65535 ? Uint32BufferAttribute : Uint16BufferAttribute )( indices, 1 );
- attribute.version = version;
- // Updating index buffer in VAO now. See WebGLBindingStates
- //
- const previousAttribute = wireframeAttributes.get( geometry );
- if ( previousAttribute ) attributes.remove( previousAttribute );
- //
- wireframeAttributes.set( geometry, attribute );
- }
- function getWireframeAttribute( geometry ) {
- const currentAttribute = wireframeAttributes.get( geometry );
- if ( currentAttribute ) {
- const geometryIndex = geometry.index;
- if ( geometryIndex !== null ) {
- // if the attribute is obsolete, create a new one
- if ( currentAttribute.version < geometryIndex.version ) {
- updateWireframeAttribute( geometry );
- }
- }
- } else {
- updateWireframeAttribute( geometry );
- }
- return wireframeAttributes.get( geometry );
- }
- return {
- get: get,
- update: update,
- getWireframeAttribute: getWireframeAttribute
- };
- }
- function WebGLIndexedBufferRenderer( gl, extensions, info, capabilities ) {
- const isWebGL2 = capabilities.isWebGL2;
- let mode;
- function setMode( value ) {
- mode = value;
- }
- let type, bytesPerElement;
- function setIndex( value ) {
- type = value.type;
- bytesPerElement = value.bytesPerElement;
- }
- function render( start, count ) {
- gl.drawElements( mode, count, type, start * bytesPerElement );
- info.update( count, mode, 1 );
- }
- function renderInstances( start, count, primcount ) {
- if ( primcount === 0 ) return;
- let extension, methodName;
- if ( isWebGL2 ) {
- extension = gl;
- methodName = 'drawElementsInstanced';
- } else {
- extension = extensions.get( 'ANGLE_instanced_arrays' );
- methodName = 'drawElementsInstancedANGLE';
- if ( extension === null ) {
- console.error( 'THREE.WebGLIndexedBufferRenderer: using THREE.InstancedBufferGeometry but hardware does not support extension ANGLE_instanced_arrays.' );
- return;
- }
- }
- extension[ methodName ]( mode, count, type, start * bytesPerElement, primcount );
- info.update( count, mode, primcount );
- }
- //
- this.setMode = setMode;
- this.setIndex = setIndex;
- this.render = render;
- this.renderInstances = renderInstances;
- }
- function WebGLInfo( gl ) {
- const memory = {
- geometries: 0,
- textures: 0
- };
- const render = {
- frame: 0,
- calls: 0,
- triangles: 0,
- points: 0,
- lines: 0
- };
- function update( count, mode, instanceCount ) {
- render.calls ++;
- switch ( mode ) {
- case 4:
- render.triangles += instanceCount * ( count / 3 );
- break;
- case 1:
- render.lines += instanceCount * ( count / 2 );
- break;
- case 3:
- render.lines += instanceCount * ( count - 1 );
- break;
- case 2:
- render.lines += instanceCount * count;
- break;
- case 0:
- render.points += instanceCount * count;
- break;
- default:
- console.error( 'THREE.WebGLInfo: Unknown draw mode:', mode );
- break;
- }
- }
- function reset() {
- render.frame ++;
- render.calls = 0;
- render.triangles = 0;
- render.points = 0;
- render.lines = 0;
- }
- return {
- memory: memory,
- render: render,
- programs: null,
- autoReset: true,
- reset: reset,
- update: update
- };
- }
- function numericalSort( a, b ) {
- return a[ 0 ] - b[ 0 ];
- }
- function absNumericalSort( a, b ) {
- return Math.abs( b[ 1 ] ) - Math.abs( a[ 1 ] );
- }
- function WebGLMorphtargets( gl ) {
- const influencesList = {};
- const morphInfluences = new Float32Array( 8 );
- const workInfluences = [];
- for ( let i = 0; i < 8; i ++ ) {
- workInfluences[ i ] = [ i, 0 ];
- }
- function update( object, geometry, material, program ) {
- const objectInfluences = object.morphTargetInfluences;
- // When object doesn't have morph target influences defined, we treat it as a 0-length array
- // This is important to make sure we set up morphTargetBaseInfluence / morphTargetInfluences
- const length = objectInfluences === undefined ? 0 : objectInfluences.length;
- let influences = influencesList[ geometry.id ];
- if ( influences === undefined ) {
- // initialise list
- influences = [];
- for ( let i = 0; i < length; i ++ ) {
- influences[ i ] = [ i, 0 ];
- }
- influencesList[ geometry.id ] = influences;
- }
- // Collect influences
- for ( let i = 0; i < length; i ++ ) {
- const influence = influences[ i ];
- influence[ 0 ] = i;
- influence[ 1 ] = objectInfluences[ i ];
- }
- influences.sort( absNumericalSort );
- for ( let i = 0; i < 8; i ++ ) {
- if ( i < length && influences[ i ][ 1 ] ) {
- workInfluences[ i ][ 0 ] = influences[ i ][ 0 ];
- workInfluences[ i ][ 1 ] = influences[ i ][ 1 ];
- } else {
- workInfluences[ i ][ 0 ] = Number.MAX_SAFE_INTEGER;
- workInfluences[ i ][ 1 ] = 0;
- }
- }
- workInfluences.sort( numericalSort );
- const morphTargets = material.morphTargets && geometry.morphAttributes.position;
- const morphNormals = material.morphNormals && geometry.morphAttributes.normal;
- let morphInfluencesSum = 0;
- for ( let i = 0; i < 8; i ++ ) {
- const influence = workInfluences[ i ];
- const index = influence[ 0 ];
- const value = influence[ 1 ];
- if ( index !== Number.MAX_SAFE_INTEGER && value ) {
- if ( morphTargets && geometry.getAttribute( 'morphTarget' + i ) !== morphTargets[ index ] ) {
- geometry.setAttribute( 'morphTarget' + i, morphTargets[ index ] );
- }
- if ( morphNormals && geometry.getAttribute( 'morphNormal' + i ) !== morphNormals[ index ] ) {
- geometry.setAttribute( 'morphNormal' + i, morphNormals[ index ] );
- }
- morphInfluences[ i ] = value;
- morphInfluencesSum += value;
- } else {
- if ( morphTargets && geometry.hasAttribute( 'morphTarget' + i ) === true ) {
- geometry.deleteAttribute( 'morphTarget' + i );
- }
- if ( morphNormals && geometry.hasAttribute( 'morphNormal' + i ) === true ) {
- geometry.deleteAttribute( 'morphNormal' + i );
- }
- morphInfluences[ i ] = 0;
- }
- }
- // GLSL shader uses formula baseinfluence * base + sum(target * influence)
- // This allows us to switch between absolute morphs and relative morphs without changing shader code
- // When baseinfluence = 1 - sum(influence), the above is equivalent to sum((target - base) * influence)
- const morphBaseInfluence = geometry.morphTargetsRelative ? 1 : 1 - morphInfluencesSum;
- program.getUniforms().setValue( gl, 'morphTargetBaseInfluence', morphBaseInfluence );
- program.getUniforms().setValue( gl, 'morphTargetInfluences', morphInfluences );
- }
- return {
- update: update
- };
- }
- function WebGLObjects( gl, geometries, attributes, info ) {
- let updateMap = new WeakMap();
- function update( object ) {
- const frame = info.render.frame;
- const geometry = object.geometry;
- const buffergeometry = geometries.get( object, geometry );
- // Update once per frame
- if ( updateMap.get( buffergeometry ) !== frame ) {
- if ( geometry.isGeometry ) {
- buffergeometry.updateFromObject( object );
- }
- geometries.update( buffergeometry );
- updateMap.set( buffergeometry, frame );
- }
- if ( object.isInstancedMesh ) {
- if ( object.hasEventListener( 'dispose', onInstancedMeshDispose ) === false ) {
- object.addEventListener( 'dispose', onInstancedMeshDispose );
- }
- attributes.update( object.instanceMatrix, 34962 );
- if ( object.instanceColor !== null ) {
- attributes.update( object.instanceColor, 34962 );
- }
- }
- return buffergeometry;
- }
- function dispose() {
- updateMap = new WeakMap();
- }
- function onInstancedMeshDispose( event ) {
- const instancedMesh = event.target;
- instancedMesh.removeEventListener( 'dispose', onInstancedMeshDispose );
- attributes.remove( instancedMesh.instanceMatrix );
- if ( instancedMesh.instanceColor !== null ) attributes.remove( instancedMesh.instanceColor );
- }
- return {
- update: update,
- dispose: dispose
- };
- }
- function DataTexture2DArray( data = null, width = 1, height = 1, depth = 1 ) {
- Texture.call( this, null );
- this.image = { data, width, height, depth };
- this.magFilter = NearestFilter;
- this.minFilter = NearestFilter;
- this.wrapR = ClampToEdgeWrapping;
- this.generateMipmaps = false;
- this.flipY = false;
- this.needsUpdate = true;
- }
- DataTexture2DArray.prototype = Object.create( Texture.prototype );
- DataTexture2DArray.prototype.constructor = DataTexture2DArray;
- DataTexture2DArray.prototype.isDataTexture2DArray = true;
- function DataTexture3D( data = null, width = 1, height = 1, depth = 1 ) {
- // We're going to add .setXXX() methods for setting properties later.
- // Users can still set in DataTexture3D directly.
- //
- // const texture = new THREE.DataTexture3D( data, width, height, depth );
- // texture.anisotropy = 16;
- //
- // See #14839
- Texture.call( this, null );
- this.image = { data, width, height, depth };
- this.magFilter = NearestFilter;
- this.minFilter = NearestFilter;
- this.wrapR = ClampToEdgeWrapping;
- this.generateMipmaps = false;
- this.flipY = false;
- this.needsUpdate = true;
- }
- DataTexture3D.prototype = Object.create( Texture.prototype );
- DataTexture3D.prototype.constructor = DataTexture3D;
- DataTexture3D.prototype.isDataTexture3D = true;
- /**
- * Uniforms of a program.
- * Those form a tree structure with a special top-level container for the root,
- * which you get by calling 'new WebGLUniforms( gl, program )'.
- *
- *
- * Properties of inner nodes including the top-level container:
- *
- * .seq - array of nested uniforms
- * .map - nested uniforms by name
- *
- *
- * Methods of all nodes except the top-level container:
- *
- * .setValue( gl, value, [textures] )
- *
- * uploads a uniform value(s)
- * the 'textures' parameter is needed for sampler uniforms
- *
- *
- * Static methods of the top-level container (textures factorizations):
- *
- * .upload( gl, seq, values, textures )
- *
- * sets uniforms in 'seq' to 'values[id].value'
- *
- * .seqWithValue( seq, values ) : filteredSeq
- *
- * filters 'seq' entries with corresponding entry in values
- *
- *
- * Methods of the top-level container (textures factorizations):
- *
- * .setValue( gl, name, value, textures )
- *
- * sets uniform with name 'name' to 'value'
- *
- * .setOptional( gl, obj, prop )
- *
- * like .set for an optional property of the object
- *
- */
- const emptyTexture = new Texture();
- const emptyTexture2dArray = new DataTexture2DArray();
- const emptyTexture3d = new DataTexture3D();
- const emptyCubeTexture = new CubeTexture();
- // --- Utilities ---
- // Array Caches (provide typed arrays for temporary by size)
- const arrayCacheF32 = [];
- const arrayCacheI32 = [];
- // Float32Array caches used for uploading Matrix uniforms
- const mat4array = new Float32Array( 16 );
- const mat3array = new Float32Array( 9 );
- const mat2array = new Float32Array( 4 );
- // Flattening for arrays of vectors and matrices
- function flatten( array, nBlocks, blockSize ) {
- const firstElem = array[ 0 ];
- if ( firstElem <= 0 || firstElem > 0 ) return array;
- // unoptimized: ! isNaN( firstElem )
- // see http://jacksondunstan.com/articles/983
- const n = nBlocks * blockSize;
- let r = arrayCacheF32[ n ];
- if ( r === undefined ) {
- r = new Float32Array( n );
- arrayCacheF32[ n ] = r;
- }
- if ( nBlocks !== 0 ) {
- firstElem.toArray( r, 0 );
- for ( let i = 1, offset = 0; i !== nBlocks; ++ i ) {
- offset += blockSize;
- array[ i ].toArray( r, offset );
- }
- }
- return r;
- }
- function arraysEqual( a, b ) {
- if ( a.length !== b.length ) return false;
- for ( let i = 0, l = a.length; i < l; i ++ ) {
- if ( a[ i ] !== b[ i ] ) return false;
- }
- return true;
- }
- function copyArray( a, b ) {
- for ( let i = 0, l = b.length; i < l; i ++ ) {
- a[ i ] = b[ i ];
- }
- }
- // Texture unit allocation
- function allocTexUnits( textures, n ) {
- let r = arrayCacheI32[ n ];
- if ( r === undefined ) {
- r = new Int32Array( n );
- arrayCacheI32[ n ] = r;
- }
- for ( let i = 0; i !== n; ++ i ) {
- r[ i ] = textures.allocateTextureUnit();
- }
- return r;
- }
- // --- Setters ---
- // Note: Defining these methods externally, because they come in a bunch
- // and this way their names minify.
- // Single scalar
- function setValueV1f( gl, v ) {
- const cache = this.cache;
- if ( cache[ 0 ] === v ) return;
- gl.uniform1f( this.addr, v );
- cache[ 0 ] = v;
- }
- // Single float vector (from flat array or THREE.VectorN)
- function setValueV2f( gl, v ) {
- const cache = this.cache;
- if ( v.x !== undefined ) {
- if ( cache[ 0 ] !== v.x || cache[ 1 ] !== v.y ) {
- gl.uniform2f( this.addr, v.x, v.y );
- cache[ 0 ] = v.x;
- cache[ 1 ] = v.y;
- }
- } else {
- if ( arraysEqual( cache, v ) ) return;
- gl.uniform2fv( this.addr, v );
- copyArray( cache, v );
- }
- }
- function setValueV3f( gl, v ) {
- const cache = this.cache;
- if ( v.x !== undefined ) {
- if ( cache[ 0 ] !== v.x || cache[ 1 ] !== v.y || cache[ 2 ] !== v.z ) {
- gl.uniform3f( this.addr, v.x, v.y, v.z );
- cache[ 0 ] = v.x;
- cache[ 1 ] = v.y;
- cache[ 2 ] = v.z;
- }
- } else if ( v.r !== undefined ) {
- if ( cache[ 0 ] !== v.r || cache[ 1 ] !== v.g || cache[ 2 ] !== v.b ) {
- gl.uniform3f( this.addr, v.r, v.g, v.b );
- cache[ 0 ] = v.r;
- cache[ 1 ] = v.g;
- cache[ 2 ] = v.b;
- }
- } else {
- if ( arraysEqual( cache, v ) ) return;
- gl.uniform3fv( this.addr, v );
- copyArray( cache, v );
- }
- }
- function setValueV4f( gl, v ) {
- const cache = this.cache;
- if ( v.x !== undefined ) {
- if ( cache[ 0 ] !== v.x || cache[ 1 ] !== v.y || cache[ 2 ] !== v.z || cache[ 3 ] !== v.w ) {
- gl.uniform4f( this.addr, v.x, v.y, v.z, v.w );
- cache[ 0 ] = v.x;
- cache[ 1 ] = v.y;
- cache[ 2 ] = v.z;
- cache[ 3 ] = v.w;
- }
- } else {
- if ( arraysEqual( cache, v ) ) return;
- gl.uniform4fv( this.addr, v );
- copyArray( cache, v );
- }
- }
- // Single matrix (from flat array or MatrixN)
- function setValueM2( gl, v ) {
- const cache = this.cache;
- const elements = v.elements;
- if ( elements === undefined ) {
- if ( arraysEqual( cache, v ) ) return;
- gl.uniformMatrix2fv( this.addr, false, v );
- copyArray( cache, v );
- } else {
- if ( arraysEqual( cache, elements ) ) return;
- mat2array.set( elements );
- gl.uniformMatrix2fv( this.addr, false, mat2array );
- copyArray( cache, elements );
- }
- }
- function setValueM3( gl, v ) {
- const cache = this.cache;
- const elements = v.elements;
- if ( elements === undefined ) {
- if ( arraysEqual( cache, v ) ) return;
- gl.uniformMatrix3fv( this.addr, false, v );
- copyArray( cache, v );
- } else {
- if ( arraysEqual( cache, elements ) ) return;
- mat3array.set( elements );
- gl.uniformMatrix3fv( this.addr, false, mat3array );
- copyArray( cache, elements );
- }
- }
- function setValueM4( gl, v ) {
- const cache = this.cache;
- const elements = v.elements;
- if ( elements === undefined ) {
- if ( arraysEqual( cache, v ) ) return;
- gl.uniformMatrix4fv( this.addr, false, v );
- copyArray( cache, v );
- } else {
- if ( arraysEqual( cache, elements ) ) return;
- mat4array.set( elements );
- gl.uniformMatrix4fv( this.addr, false, mat4array );
- copyArray( cache, elements );
- }
- }
- // Single texture (2D / Cube)
- function setValueT1( gl, v, textures ) {
- const cache = this.cache;
- const unit = textures.allocateTextureUnit();
- if ( cache[ 0 ] !== unit ) {
- gl.uniform1i( this.addr, unit );
- cache[ 0 ] = unit;
- }
- textures.safeSetTexture2D( v || emptyTexture, unit );
- }
- function setValueT2DArray1( gl, v, textures ) {
- const cache = this.cache;
- const unit = textures.allocateTextureUnit();
- if ( cache[ 0 ] !== unit ) {
- gl.uniform1i( this.addr, unit );
- cache[ 0 ] = unit;
- }
- textures.setTexture2DArray( v || emptyTexture2dArray, unit );
- }
- function setValueT3D1( gl, v, textures ) {
- const cache = this.cache;
- const unit = textures.allocateTextureUnit();
- if ( cache[ 0 ] !== unit ) {
- gl.uniform1i( this.addr, unit );
- cache[ 0 ] = unit;
- }
- textures.setTexture3D( v || emptyTexture3d, unit );
- }
- function setValueT6( gl, v, textures ) {
- const cache = this.cache;
- const unit = textures.allocateTextureUnit();
- if ( cache[ 0 ] !== unit ) {
- gl.uniform1i( this.addr, unit );
- cache[ 0 ] = unit;
- }
- textures.safeSetTextureCube( v || emptyCubeTexture, unit );
- }
- // Integer / Boolean vectors or arrays thereof (always flat arrays)
- function setValueV1i( gl, v ) {
- const cache = this.cache;
- if ( cache[ 0 ] === v ) return;
- gl.uniform1i( this.addr, v );
- cache[ 0 ] = v;
- }
- function setValueV2i( gl, v ) {
- const cache = this.cache;
- if ( arraysEqual( cache, v ) ) return;
- gl.uniform2iv( this.addr, v );
- copyArray( cache, v );
- }
- function setValueV3i( gl, v ) {
- const cache = this.cache;
- if ( arraysEqual( cache, v ) ) return;
- gl.uniform3iv( this.addr, v );
- copyArray( cache, v );
- }
- function setValueV4i( gl, v ) {
- const cache = this.cache;
- if ( arraysEqual( cache, v ) ) return;
- gl.uniform4iv( this.addr, v );
- copyArray( cache, v );
- }
- // uint
- function setValueV1ui( gl, v ) {
- const cache = this.cache;
- if ( cache[ 0 ] === v ) return;
- gl.uniform1ui( this.addr, v );
- cache[ 0 ] = v;
- }
- // Helper to pick the right setter for the singular case
- function getSingularSetter( type ) {
- switch ( type ) {
- case 0x1406: return setValueV1f; // FLOAT
- case 0x8b50: return setValueV2f; // _VEC2
- case 0x8b51: return setValueV3f; // _VEC3
- case 0x8b52: return setValueV4f; // _VEC4
- case 0x8b5a: return setValueM2; // _MAT2
- case 0x8b5b: return setValueM3; // _MAT3
- case 0x8b5c: return setValueM4; // _MAT4
- case 0x1404: case 0x8b56: return setValueV1i; // INT, BOOL
- case 0x8b53: case 0x8b57: return setValueV2i; // _VEC2
- case 0x8b54: case 0x8b58: return setValueV3i; // _VEC3
- case 0x8b55: case 0x8b59: return setValueV4i; // _VEC4
- case 0x1405: return setValueV1ui; // UINT
- case 0x8b5e: // SAMPLER_2D
- case 0x8d66: // SAMPLER_EXTERNAL_OES
- case 0x8dca: // INT_SAMPLER_2D
- case 0x8dd2: // UNSIGNED_INT_SAMPLER_2D
- case 0x8b62: // SAMPLER_2D_SHADOW
- return setValueT1;
- case 0x8b5f: // SAMPLER_3D
- case 0x8dcb: // INT_SAMPLER_3D
- case 0x8dd3: // UNSIGNED_INT_SAMPLER_3D
- return setValueT3D1;
- case 0x8b60: // SAMPLER_CUBE
- case 0x8dcc: // INT_SAMPLER_CUBE
- case 0x8dd4: // UNSIGNED_INT_SAMPLER_CUBE
- case 0x8dc5: // SAMPLER_CUBE_SHADOW
- return setValueT6;
- case 0x8dc1: // SAMPLER_2D_ARRAY
- case 0x8dcf: // INT_SAMPLER_2D_ARRAY
- case 0x8dd7: // UNSIGNED_INT_SAMPLER_2D_ARRAY
- case 0x8dc4: // SAMPLER_2D_ARRAY_SHADOW
- return setValueT2DArray1;
- }
- }
- // Array of scalars
- function setValueV1fArray( gl, v ) {
- gl.uniform1fv( this.addr, v );
- }
- // Integer / Boolean vectors or arrays thereof (always flat arrays)
- function setValueV1iArray( gl, v ) {
- gl.uniform1iv( this.addr, v );
- }
- function setValueV2iArray( gl, v ) {
- gl.uniform2iv( this.addr, v );
- }
- function setValueV3iArray( gl, v ) {
- gl.uniform3iv( this.addr, v );
- }
- function setValueV4iArray( gl, v ) {
- gl.uniform4iv( this.addr, v );
- }
- // Array of vectors (flat or from THREE classes)
- function setValueV2fArray( gl, v ) {
- const data = flatten( v, this.size, 2 );
- gl.uniform2fv( this.addr, data );
- }
- function setValueV3fArray( gl, v ) {
- const data = flatten( v, this.size, 3 );
- gl.uniform3fv( this.addr, data );
- }
- function setValueV4fArray( gl, v ) {
- const data = flatten( v, this.size, 4 );
- gl.uniform4fv( this.addr, data );
- }
- // Array of matrices (flat or from THREE clases)
- function setValueM2Array( gl, v ) {
- const data = flatten( v, this.size, 4 );
- gl.uniformMatrix2fv( this.addr, false, data );
- }
- function setValueM3Array( gl, v ) {
- const data = flatten( v, this.size, 9 );
- gl.uniformMatrix3fv( this.addr, false, data );
- }
- function setValueM4Array( gl, v ) {
- const data = flatten( v, this.size, 16 );
- gl.uniformMatrix4fv( this.addr, false, data );
- }
- // Array of textures (2D / Cube)
- function setValueT1Array( gl, v, textures ) {
- const n = v.length;
- const units = allocTexUnits( textures, n );
- gl.uniform1iv( this.addr, units );
- for ( let i = 0; i !== n; ++ i ) {
- textures.safeSetTexture2D( v[ i ] || emptyTexture, units[ i ] );
- }
- }
- function setValueT6Array( gl, v, textures ) {
- const n = v.length;
- const units = allocTexUnits( textures, n );
- gl.uniform1iv( this.addr, units );
- for ( let i = 0; i !== n; ++ i ) {
- textures.safeSetTextureCube( v[ i ] || emptyCubeTexture, units[ i ] );
- }
- }
- // Helper to pick the right setter for a pure (bottom-level) array
- function getPureArraySetter( type ) {
- switch ( type ) {
- case 0x1406: return setValueV1fArray; // FLOAT
- case 0x8b50: return setValueV2fArray; // _VEC2
- case 0x8b51: return setValueV3fArray; // _VEC3
- case 0x8b52: return setValueV4fArray; // _VEC4
- case 0x8b5a: return setValueM2Array; // _MAT2
- case 0x8b5b: return setValueM3Array; // _MAT3
- case 0x8b5c: return setValueM4Array; // _MAT4
- case 0x1404: case 0x8b56: return setValueV1iArray; // INT, BOOL
- case 0x8b53: case 0x8b57: return setValueV2iArray; // _VEC2
- case 0x8b54: case 0x8b58: return setValueV3iArray; // _VEC3
- case 0x8b55: case 0x8b59: return setValueV4iArray; // _VEC4
- case 0x8b5e: // SAMPLER_2D
- case 0x8d66: // SAMPLER_EXTERNAL_OES
- case 0x8dca: // INT_SAMPLER_2D
- case 0x8dd2: // UNSIGNED_INT_SAMPLER_2D
- case 0x8b62: // SAMPLER_2D_SHADOW
- return setValueT1Array;
- case 0x8b60: // SAMPLER_CUBE
- case 0x8dcc: // INT_SAMPLER_CUBE
- case 0x8dd4: // UNSIGNED_INT_SAMPLER_CUBE
- case 0x8dc5: // SAMPLER_CUBE_SHADOW
- return setValueT6Array;
- }
- }
- // --- Uniform Classes ---
- function SingleUniform( id, activeInfo, addr ) {
- this.id = id;
- this.addr = addr;
- this.cache = [];
- this.setValue = getSingularSetter( activeInfo.type );
- // this.path = activeInfo.name; // DEBUG
- }
- function PureArrayUniform( id, activeInfo, addr ) {
- this.id = id;
- this.addr = addr;
- this.cache = [];
- this.size = activeInfo.size;
- this.setValue = getPureArraySetter( activeInfo.type );
- // this.path = activeInfo.name; // DEBUG
- }
- PureArrayUniform.prototype.updateCache = function ( data ) {
- const cache = this.cache;
- if ( data instanceof Float32Array && cache.length !== data.length ) {
- this.cache = new Float32Array( data.length );
- }
- copyArray( cache, data );
- };
- function StructuredUniform( id ) {
- this.id = id;
- this.seq = [];
- this.map = {};
- }
- StructuredUniform.prototype.setValue = function ( gl, value, textures ) {
- const seq = this.seq;
- for ( let i = 0, n = seq.length; i !== n; ++ i ) {
- const u = seq[ i ];
- u.setValue( gl, value[ u.id ], textures );
- }
- };
- // --- Top-level ---
- // Parser - builds up the property tree from the path strings
- const RePathPart = /(\w+)(\])?(\[|\.)?/g;
- // extracts
- // - the identifier (member name or array index)
- // - followed by an optional right bracket (found when array index)
- // - followed by an optional left bracket or dot (type of subscript)
- //
- // Note: These portions can be read in a non-overlapping fashion and
- // allow straightforward parsing of the hierarchy that WebGL encodes
- // in the uniform names.
- function addUniform( container, uniformObject ) {
- container.seq.push( uniformObject );
- container.map[ uniformObject.id ] = uniformObject;
- }
- function parseUniform( activeInfo, addr, container ) {
- const path = activeInfo.name,
- pathLength = path.length;
- // reset RegExp object, because of the early exit of a previous run
- RePathPart.lastIndex = 0;
- while ( true ) {
- const match = RePathPart.exec( path ),
- matchEnd = RePathPart.lastIndex;
- let id = match[ 1 ];
- const idIsIndex = match[ 2 ] === ']',
- subscript = match[ 3 ];
- if ( idIsIndex ) id = id | 0; // convert to integer
- if ( subscript === undefined || subscript === '[' && matchEnd + 2 === pathLength ) {
- // bare name or "pure" bottom-level array "[0]" suffix
- addUniform( container, subscript === undefined ?
- new SingleUniform( id, activeInfo, addr ) :
- new PureArrayUniform( id, activeInfo, addr ) );
- break;
- } else {
- // step into inner node / create it in case it doesn't exist
- const map = container.map;
- let next = map[ id ];
- if ( next === undefined ) {
- next = new StructuredUniform( id );
- addUniform( container, next );
- }
- container = next;
- }
- }
- }
- // Root Container
- function WebGLUniforms( gl, program ) {
- this.seq = [];
- this.map = {};
- const n = gl.getProgramParameter( program, 35718 );
- for ( let i = 0; i < n; ++ i ) {
- const info = gl.getActiveUniform( program, i ),
- addr = gl.getUniformLocation( program, info.name );
- parseUniform( info, addr, this );
- }
- }
- WebGLUniforms.prototype.setValue = function ( gl, name, value, textures ) {
- const u = this.map[ name ];
- if ( u !== undefined ) u.setValue( gl, value, textures );
- };
- WebGLUniforms.prototype.setOptional = function ( gl, object, name ) {
- const v = object[ name ];
- if ( v !== undefined ) this.setValue( gl, name, v );
- };
- // Static interface
- WebGLUniforms.upload = function ( gl, seq, values, textures ) {
- for ( let i = 0, n = seq.length; i !== n; ++ i ) {
- const u = seq[ i ],
- v = values[ u.id ];
- if ( v.needsUpdate !== false ) {
- // note: always updating when .needsUpdate is undefined
- u.setValue( gl, v.value, textures );
- }
- }
- };
- WebGLUniforms.seqWithValue = function ( seq, values ) {
- const r = [];
- for ( let i = 0, n = seq.length; i !== n; ++ i ) {
- const u = seq[ i ];
- if ( u.id in values ) r.push( u );
- }
- return r;
- };
- function WebGLShader( gl, type, string ) {
- const shader = gl.createShader( type );
- gl.shaderSource( shader, string );
- gl.compileShader( shader );
- return shader;
- }
- let programIdCount = 0;
- function addLineNumbers( string ) {
- const lines = string.split( '\n' );
- for ( let i = 0; i < lines.length; i ++ ) {
- lines[ i ] = ( i + 1 ) + ': ' + lines[ i ];
- }
- return lines.join( '\n' );
- }
- function getEncodingComponents( encoding ) {
- switch ( encoding ) {
- case LinearEncoding:
- return [ 'Linear', '( value )' ];
- case sRGBEncoding:
- return [ 'sRGB', '( value )' ];
- case RGBEEncoding:
- return [ 'RGBE', '( value )' ];
- case RGBM7Encoding:
- return [ 'RGBM', '( value, 7.0 )' ];
- case RGBM16Encoding:
- return [ 'RGBM', '( value, 16.0 )' ];
- case RGBDEncoding:
- return [ 'RGBD', '( value, 256.0 )' ];
- case GammaEncoding:
- return [ 'Gamma', '( value, float( GAMMA_FACTOR ) )' ];
- case LogLuvEncoding:
- return [ 'LogLuv', '( value )' ];
- default:
- console.warn( 'THREE.WebGLProgram: Unsupported encoding:', encoding );
- return [ 'Linear', '( value )' ];
- }
- }
- function getShaderErrors( gl, shader, type ) {
- const status = gl.getShaderParameter( shader, 35713 );
- const log = gl.getShaderInfoLog( shader ).trim();
- if ( status && log === '' ) return '';
- // --enable-privileged-webgl-extension
- // console.log( '**' + type + '**', gl.getExtension( 'WEBGL_debug_shaders' ).getTranslatedShaderSource( shader ) );
- const source = gl.getShaderSource( shader );
- return 'THREE.WebGLShader: gl.getShaderInfoLog() ' + type + '\n' + log + addLineNumbers( source );
- }
- function getTexelDecodingFunction( functionName, encoding ) {
- const components = getEncodingComponents( encoding );
- return 'vec4 ' + functionName + '( vec4 value ) { return ' + components[ 0 ] + 'ToLinear' + components[ 1 ] + '; }';
- }
- function getTexelEncodingFunction( functionName, encoding ) {
- const components = getEncodingComponents( encoding );
- return 'vec4 ' + functionName + '( vec4 value ) { return LinearTo' + components[ 0 ] + components[ 1 ] + '; }';
- }
- function getToneMappingFunction( functionName, toneMapping ) {
- let toneMappingName;
- switch ( toneMapping ) {
- case LinearToneMapping:
- toneMappingName = 'Linear';
- break;
- case ReinhardToneMapping:
- toneMappingName = 'Reinhard';
- break;
- case CineonToneMapping:
- toneMappingName = 'OptimizedCineon';
- break;
- case ACESFilmicToneMapping:
- toneMappingName = 'ACESFilmic';
- break;
- case CustomToneMapping:
- toneMappingName = 'Custom';
- break;
- default:
- console.warn( 'THREE.WebGLProgram: Unsupported toneMapping:', toneMapping );
- toneMappingName = 'Linear';
- }
- return 'vec3 ' + functionName + '( vec3 color ) { return ' + toneMappingName + 'ToneMapping( color ); }';
- }
- function generateExtensions( parameters ) {
- const chunks = [
- ( parameters.extensionDerivatives || parameters.envMapCubeUV || parameters.bumpMap || parameters.tangentSpaceNormalMap || parameters.clearcoatNormalMap || parameters.flatShading || parameters.shaderID === 'physical' ) ? '#extension GL_OES_standard_derivatives : enable' : '',
- ( parameters.extensionFragDepth || parameters.logarithmicDepthBuffer ) && parameters.rendererExtensionFragDepth ? '#extension GL_EXT_frag_depth : enable' : '',
- ( parameters.extensionDrawBuffers && parameters.rendererExtensionDrawBuffers ) ? '#extension GL_EXT_draw_buffers : require' : '',
- ( parameters.extensionShaderTextureLOD || parameters.envMap ) && parameters.rendererExtensionShaderTextureLod ? '#extension GL_EXT_shader_texture_lod : enable' : ''
- ];
- return chunks.filter( filterEmptyLine ).join( '\n' );
- }
- function generateDefines( defines ) {
- const chunks = [];
- for ( const name in defines ) {
- const value = defines[ name ];
- if ( value === false ) continue;
- chunks.push( '#define ' + name + ' ' + value );
- }
- return chunks.join( '\n' );
- }
- function fetchAttributeLocations( gl, program ) {
- const attributes = {};
- const n = gl.getProgramParameter( program, 35721 );
- for ( let i = 0; i < n; i ++ ) {
- const info = gl.getActiveAttrib( program, i );
- const name = info.name;
- // console.log( 'THREE.WebGLProgram: ACTIVE VERTEX ATTRIBUTE:', name, i );
- attributes[ name ] = gl.getAttribLocation( program, name );
- }
- return attributes;
- }
- function filterEmptyLine( string ) {
- return string !== '';
- }
- function replaceLightNums( string, parameters ) {
- return string
- .replace( /NUM_DIR_LIGHTS/g, parameters.numDirLights )
- .replace( /NUM_SPOT_LIGHTS/g, parameters.numSpotLights )
- .replace( /NUM_RECT_AREA_LIGHTS/g, parameters.numRectAreaLights )
- .replace( /NUM_POINT_LIGHTS/g, parameters.numPointLights )
- .replace( /NUM_HEMI_LIGHTS/g, parameters.numHemiLights )
- .replace( /NUM_DIR_LIGHT_SHADOWS/g, parameters.numDirLightShadows )
- .replace( /NUM_SPOT_LIGHT_SHADOWS/g, parameters.numSpotLightShadows )
- .replace( /NUM_POINT_LIGHT_SHADOWS/g, parameters.numPointLightShadows );
- }
- function replaceClippingPlaneNums( string, parameters ) {
- return string
- .replace( /NUM_CLIPPING_PLANES/g, parameters.numClippingPlanes )
- .replace( /UNION_CLIPPING_PLANES/g, ( parameters.numClippingPlanes - parameters.numClipIntersection ) );
- }
- // Resolve Includes
- const includePattern = /^[ \t]*#include +<([\w\d./]+)>/gm;
- function resolveIncludes( string ) {
- return string.replace( includePattern, includeReplacer );
- }
- function includeReplacer( match, include ) {
- const string = ShaderChunk[ include ];
- if ( string === undefined ) {
- throw new Error( 'Can not resolve #include <' + include + '>' );
- }
- return resolveIncludes( string );
- }
- // Unroll Loops
- const deprecatedUnrollLoopPattern = /#pragma unroll_loop[\s]+?for \( int i \= (\d+)\; i < (\d+)\; i \+\+ \) \{([\s\S]+?)(?=\})\}/g;
- const unrollLoopPattern = /#pragma unroll_loop_start\s+for\s*\(\s*int\s+i\s*=\s*(\d+)\s*;\s*i\s*<\s*(\d+)\s*;\s*i\s*\+\+\s*\)\s*{([\s\S]+?)}\s+#pragma unroll_loop_end/g;
- function unrollLoops( string ) {
- return string
- .replace( unrollLoopPattern, loopReplacer )
- .replace( deprecatedUnrollLoopPattern, deprecatedLoopReplacer );
- }
- function deprecatedLoopReplacer( match, start, end, snippet ) {
- console.warn( 'WebGLProgram: #pragma unroll_loop shader syntax is deprecated. Please use #pragma unroll_loop_start syntax instead.' );
- return loopReplacer( match, start, end, snippet );
- }
- function loopReplacer( match, start, end, snippet ) {
- let string = '';
- for ( let i = parseInt( start ); i < parseInt( end ); i ++ ) {
- string += snippet
- .replace( /\[\s*i\s*\]/g, '[ ' + i + ' ]' )
- .replace( /UNROLLED_LOOP_INDEX/g, i );
- }
- return string;
- }
- //
- function generatePrecision( parameters ) {
- let precisionstring = 'precision ' + parameters.precision + ' float;\nprecision ' + parameters.precision + ' int;';
- if ( parameters.precision === 'highp' ) {
- precisionstring += '\n#define HIGH_PRECISION';
- } else if ( parameters.precision === 'mediump' ) {
- precisionstring += '\n#define MEDIUM_PRECISION';
- } else if ( parameters.precision === 'lowp' ) {
- precisionstring += '\n#define LOW_PRECISION';
- }
- return precisionstring;
- }
- function generateShadowMapTypeDefine( parameters ) {
- let shadowMapTypeDefine = 'SHADOWMAP_TYPE_BASIC';
- if ( parameters.shadowMapType === PCFShadowMap ) {
- shadowMapTypeDefine = 'SHADOWMAP_TYPE_PCF';
- } else if ( parameters.shadowMapType === PCFSoftShadowMap ) {
- shadowMapTypeDefine = 'SHADOWMAP_TYPE_PCF_SOFT';
- } else if ( parameters.shadowMapType === VSMShadowMap ) {
- shadowMapTypeDefine = 'SHADOWMAP_TYPE_VSM';
- }
- return shadowMapTypeDefine;
- }
- function generateEnvMapTypeDefine( parameters ) {
- let envMapTypeDefine = 'ENVMAP_TYPE_CUBE';
- if ( parameters.envMap ) {
- switch ( parameters.envMapMode ) {
- case CubeReflectionMapping:
- case CubeRefractionMapping:
- envMapTypeDefine = 'ENVMAP_TYPE_CUBE';
- break;
- case CubeUVReflectionMapping:
- case CubeUVRefractionMapping:
- envMapTypeDefine = 'ENVMAP_TYPE_CUBE_UV';
- break;
- }
- }
- return envMapTypeDefine;
- }
- function generateEnvMapModeDefine( parameters ) {
- let envMapModeDefine = 'ENVMAP_MODE_REFLECTION';
- if ( parameters.envMap ) {
- switch ( parameters.envMapMode ) {
- case CubeRefractionMapping:
- case CubeUVRefractionMapping:
- envMapModeDefine = 'ENVMAP_MODE_REFRACTION';
- break;
- }
- }
- return envMapModeDefine;
- }
- function generateEnvMapBlendingDefine( parameters ) {
- let envMapBlendingDefine = 'ENVMAP_BLENDING_NONE';
- if ( parameters.envMap ) {
- switch ( parameters.combine ) {
- case MultiplyOperation:
- envMapBlendingDefine = 'ENVMAP_BLENDING_MULTIPLY';
- break;
- case MixOperation:
- envMapBlendingDefine = 'ENVMAP_BLENDING_MIX';
- break;
- case AddOperation:
- envMapBlendingDefine = 'ENVMAP_BLENDING_ADD';
- break;
- }
- }
- return envMapBlendingDefine;
- }
- function WebGLProgram( renderer, cacheKey, parameters, bindingStates ) {
- const gl = renderer.getContext();
- const defines = parameters.defines;
- let vertexShader = parameters.vertexShader;
- let fragmentShader = parameters.fragmentShader;
- const shadowMapTypeDefine = generateShadowMapTypeDefine( parameters );
- const envMapTypeDefine = generateEnvMapTypeDefine( parameters );
- const envMapModeDefine = generateEnvMapModeDefine( parameters );
- const envMapBlendingDefine = generateEnvMapBlendingDefine( parameters );
- const gammaFactorDefine = ( renderer.gammaFactor > 0 ) ? renderer.gammaFactor : 1.0;
- const customExtensions = parameters.isWebGL2 ? '' : generateExtensions( parameters );
- const customDefines = generateDefines( defines );
- const program = gl.createProgram();
- let prefixVertex, prefixFragment;
- let versionString = parameters.glslVersion ? '#version ' + parameters.glslVersion + '\n' : '';
- if ( parameters.isRawShaderMaterial ) {
- prefixVertex = [
- customDefines
- ].filter( filterEmptyLine ).join( '\n' );
- if ( prefixVertex.length > 0 ) {
- prefixVertex += '\n';
- }
- prefixFragment = [
- customExtensions,
- customDefines
- ].filter( filterEmptyLine ).join( '\n' );
- if ( prefixFragment.length > 0 ) {
- prefixFragment += '\n';
- }
- } else {
- prefixVertex = [
- generatePrecision( parameters ),
- '#define SHADER_NAME ' + parameters.shaderName,
- customDefines,
- parameters.instancing ? '#define USE_INSTANCING' : '',
- parameters.instancingColor ? '#define USE_INSTANCING_COLOR' : '',
- parameters.supportsVertexTextures ? '#define VERTEX_TEXTURES' : '',
- '#define GAMMA_FACTOR ' + gammaFactorDefine,
- '#define MAX_BONES ' + parameters.maxBones,
- ( parameters.useFog && parameters.fog ) ? '#define USE_FOG' : '',
- ( parameters.useFog && parameters.fogExp2 ) ? '#define FOG_EXP2' : '',
- parameters.map ? '#define USE_MAP' : '',
- parameters.envMap ? '#define USE_ENVMAP' : '',
- parameters.envMap ? '#define ' + envMapModeDefine : '',
- parameters.lightMap ? '#define USE_LIGHTMAP' : '',
- parameters.aoMap ? '#define USE_AOMAP' : '',
- parameters.emissiveMap ? '#define USE_EMISSIVEMAP' : '',
- parameters.bumpMap ? '#define USE_BUMPMAP' : '',
- parameters.normalMap ? '#define USE_NORMALMAP' : '',
- ( parameters.normalMap && parameters.objectSpaceNormalMap ) ? '#define OBJECTSPACE_NORMALMAP' : '',
- ( parameters.normalMap && parameters.tangentSpaceNormalMap ) ? '#define TANGENTSPACE_NORMALMAP' : '',
- parameters.clearcoatMap ? '#define USE_CLEARCOATMAP' : '',
- parameters.clearcoatRoughnessMap ? '#define USE_CLEARCOAT_ROUGHNESSMAP' : '',
- parameters.clearcoatNormalMap ? '#define USE_CLEARCOAT_NORMALMAP' : '',
- parameters.displacementMap && parameters.supportsVertexTextures ? '#define USE_DISPLACEMENTMAP' : '',
- parameters.specularMap ? '#define USE_SPECULARMAP' : '',
- parameters.roughnessMap ? '#define USE_ROUGHNESSMAP' : '',
- parameters.metalnessMap ? '#define USE_METALNESSMAP' : '',
- parameters.alphaMap ? '#define USE_ALPHAMAP' : '',
- parameters.transmissionMap ? '#define USE_TRANSMISSIONMAP' : '',
- parameters.vertexTangents ? '#define USE_TANGENT' : '',
- parameters.vertexColors ? '#define USE_COLOR' : '',
- parameters.vertexUvs ? '#define USE_UV' : '',
- parameters.uvsVertexOnly ? '#define UVS_VERTEX_ONLY' : '',
- parameters.flatShading ? '#define FLAT_SHADED' : '',
- parameters.skinning ? '#define USE_SKINNING' : '',
- parameters.useVertexTexture ? '#define BONE_TEXTURE' : '',
- parameters.morphTargets ? '#define USE_MORPHTARGETS' : '',
- parameters.morphNormals && parameters.flatShading === false ? '#define USE_MORPHNORMALS' : '',
- parameters.doubleSided ? '#define DOUBLE_SIDED' : '',
- parameters.flipSided ? '#define FLIP_SIDED' : '',
- parameters.shadowMapEnabled ? '#define USE_SHADOWMAP' : '',
- parameters.shadowMapEnabled ? '#define ' + shadowMapTypeDefine : '',
- parameters.sizeAttenuation ? '#define USE_SIZEATTENUATION' : '',
- parameters.logarithmicDepthBuffer ? '#define USE_LOGDEPTHBUF' : '',
- ( parameters.logarithmicDepthBuffer && parameters.rendererExtensionFragDepth ) ? '#define USE_LOGDEPTHBUF_EXT' : '',
- 'uniform mat4 modelMatrix;',
- 'uniform mat4 modelViewMatrix;',
- 'uniform mat4 projectionMatrix;',
- 'uniform mat4 viewMatrix;',
- 'uniform mat3 normalMatrix;',
- 'uniform vec3 cameraPosition;',
- 'uniform bool isOrthographic;',
- '#ifdef USE_INSTANCING',
- ' attribute mat4 instanceMatrix;',
- '#endif',
- '#ifdef USE_INSTANCING_COLOR',
- ' attribute vec3 instanceColor;',
- '#endif',
- 'attribute vec3 position;',
- 'attribute vec3 normal;',
- 'attribute vec2 uv;',
- '#ifdef USE_TANGENT',
- ' attribute vec4 tangent;',
- '#endif',
- '#ifdef USE_COLOR',
- ' attribute vec3 color;',
- '#endif',
- '#ifdef USE_MORPHTARGETS',
- ' attribute vec3 morphTarget0;',
- ' attribute vec3 morphTarget1;',
- ' attribute vec3 morphTarget2;',
- ' attribute vec3 morphTarget3;',
- ' #ifdef USE_MORPHNORMALS',
- ' attribute vec3 morphNormal0;',
- ' attribute vec3 morphNormal1;',
- ' attribute vec3 morphNormal2;',
- ' attribute vec3 morphNormal3;',
- ' #else',
- ' attribute vec3 morphTarget4;',
- ' attribute vec3 morphTarget5;',
- ' attribute vec3 morphTarget6;',
- ' attribute vec3 morphTarget7;',
- ' #endif',
- '#endif',
- '#ifdef USE_SKINNING',
- ' attribute vec4 skinIndex;',
- ' attribute vec4 skinWeight;',
- '#endif',
- '\n'
- ].filter( filterEmptyLine ).join( '\n' );
- prefixFragment = [
- customExtensions,
- generatePrecision( parameters ),
- '#define SHADER_NAME ' + parameters.shaderName,
- customDefines,
- parameters.alphaTest ? '#define ALPHATEST ' + parameters.alphaTest + ( parameters.alphaTest % 1 ? '' : '.0' ) : '', // add '.0' if integer
- '#define GAMMA_FACTOR ' + gammaFactorDefine,
- ( parameters.useFog && parameters.fog ) ? '#define USE_FOG' : '',
- ( parameters.useFog && parameters.fogExp2 ) ? '#define FOG_EXP2' : '',
- parameters.map ? '#define USE_MAP' : '',
- parameters.matcap ? '#define USE_MATCAP' : '',
- parameters.envMap ? '#define USE_ENVMAP' : '',
- parameters.envMap ? '#define ' + envMapTypeDefine : '',
- parameters.envMap ? '#define ' + envMapModeDefine : '',
- parameters.envMap ? '#define ' + envMapBlendingDefine : '',
- parameters.lightMap ? '#define USE_LIGHTMAP' : '',
- parameters.aoMap ? '#define USE_AOMAP' : '',
- parameters.emissiveMap ? '#define USE_EMISSIVEMAP' : '',
- parameters.bumpMap ? '#define USE_BUMPMAP' : '',
- parameters.normalMap ? '#define USE_NORMALMAP' : '',
- ( parameters.normalMap && parameters.objectSpaceNormalMap ) ? '#define OBJECTSPACE_NORMALMAP' : '',
- ( parameters.normalMap && parameters.tangentSpaceNormalMap ) ? '#define TANGENTSPACE_NORMALMAP' : '',
- parameters.clearcoatMap ? '#define USE_CLEARCOATMAP' : '',
- parameters.clearcoatRoughnessMap ? '#define USE_CLEARCOAT_ROUGHNESSMAP' : '',
- parameters.clearcoatNormalMap ? '#define USE_CLEARCOAT_NORMALMAP' : '',
- parameters.specularMap ? '#define USE_SPECULARMAP' : '',
- parameters.roughnessMap ? '#define USE_ROUGHNESSMAP' : '',
- parameters.metalnessMap ? '#define USE_METALNESSMAP' : '',
- parameters.alphaMap ? '#define USE_ALPHAMAP' : '',
- parameters.sheen ? '#define USE_SHEEN' : '',
- parameters.transmissionMap ? '#define USE_TRANSMISSIONMAP' : '',
- parameters.vertexTangents ? '#define USE_TANGENT' : '',
- parameters.vertexColors || parameters.instancingColor ? '#define USE_COLOR' : '',
- parameters.vertexUvs ? '#define USE_UV' : '',
- parameters.uvsVertexOnly ? '#define UVS_VERTEX_ONLY' : '',
- parameters.gradientMap ? '#define USE_GRADIENTMAP' : '',
- parameters.flatShading ? '#define FLAT_SHADED' : '',
- parameters.doubleSided ? '#define DOUBLE_SIDED' : '',
- parameters.flipSided ? '#define FLIP_SIDED' : '',
- parameters.shadowMapEnabled ? '#define USE_SHADOWMAP' : '',
- parameters.shadowMapEnabled ? '#define ' + shadowMapTypeDefine : '',
- parameters.premultipliedAlpha ? '#define PREMULTIPLIED_ALPHA' : '',
- parameters.physicallyCorrectLights ? '#define PHYSICALLY_CORRECT_LIGHTS' : '',
- parameters.logarithmicDepthBuffer ? '#define USE_LOGDEPTHBUF' : '',
- ( parameters.logarithmicDepthBuffer && parameters.rendererExtensionFragDepth ) ? '#define USE_LOGDEPTHBUF_EXT' : '',
- ( ( parameters.extensionShaderTextureLOD || parameters.envMap ) && parameters.rendererExtensionShaderTextureLod ) ? '#define TEXTURE_LOD_EXT' : '',
- 'uniform mat4 viewMatrix;',
- 'uniform vec3 cameraPosition;',
- 'uniform bool isOrthographic;',
- ( parameters.toneMapping !== NoToneMapping ) ? '#define TONE_MAPPING' : '',
- ( parameters.toneMapping !== NoToneMapping ) ? ShaderChunk[ 'tonemapping_pars_fragment' ] : '', // this code is required here because it is used by the toneMapping() function defined below
- ( parameters.toneMapping !== NoToneMapping ) ? getToneMappingFunction( 'toneMapping', parameters.toneMapping ) : '',
- parameters.dithering ? '#define DITHERING' : '',
- ShaderChunk[ 'encodings_pars_fragment' ], // this code is required here because it is used by the various encoding/decoding function defined below
- parameters.map ? getTexelDecodingFunction( 'mapTexelToLinear', parameters.mapEncoding ) : '',
- parameters.matcap ? getTexelDecodingFunction( 'matcapTexelToLinear', parameters.matcapEncoding ) : '',
- parameters.envMap ? getTexelDecodingFunction( 'envMapTexelToLinear', parameters.envMapEncoding ) : '',
- parameters.emissiveMap ? getTexelDecodingFunction( 'emissiveMapTexelToLinear', parameters.emissiveMapEncoding ) : '',
- parameters.lightMap ? getTexelDecodingFunction( 'lightMapTexelToLinear', parameters.lightMapEncoding ) : '',
- getTexelEncodingFunction( 'linearToOutputTexel', parameters.outputEncoding ),
- parameters.depthPacking ? '#define DEPTH_PACKING ' + parameters.depthPacking : '',
- '\n'
- ].filter( filterEmptyLine ).join( '\n' );
- }
- vertexShader = resolveIncludes( vertexShader );
- vertexShader = replaceLightNums( vertexShader, parameters );
- vertexShader = replaceClippingPlaneNums( vertexShader, parameters );
- fragmentShader = resolveIncludes( fragmentShader );
- fragmentShader = replaceLightNums( fragmentShader, parameters );
- fragmentShader = replaceClippingPlaneNums( fragmentShader, parameters );
- vertexShader = unrollLoops( vertexShader );
- fragmentShader = unrollLoops( fragmentShader );
- if ( parameters.isWebGL2 && parameters.isRawShaderMaterial !== true ) {
- // GLSL 3.0 conversion for built-in materials and ShaderMaterial
- versionString = '#version 300 es\n';
- prefixVertex = [
- '#define attribute in',
- '#define varying out',
- '#define texture2D texture'
- ].join( '\n' ) + '\n' + prefixVertex;
- prefixFragment = [
- '#define varying in',
- ( parameters.glslVersion === GLSL3 ) ? '' : 'out highp vec4 pc_fragColor;',
- ( parameters.glslVersion === GLSL3 ) ? '' : '#define gl_FragColor pc_fragColor',
- '#define gl_FragDepthEXT gl_FragDepth',
- '#define texture2D texture',
- '#define textureCube texture',
- '#define texture2DProj textureProj',
- '#define texture2DLodEXT textureLod',
- '#define texture2DProjLodEXT textureProjLod',
- '#define textureCubeLodEXT textureLod',
- '#define texture2DGradEXT textureGrad',
- '#define texture2DProjGradEXT textureProjGrad',
- '#define textureCubeGradEXT textureGrad'
- ].join( '\n' ) + '\n' + prefixFragment;
- }
- const vertexGlsl = versionString + prefixVertex + vertexShader;
- const fragmentGlsl = versionString + prefixFragment + fragmentShader;
- // console.log( '*VERTEX*', vertexGlsl );
- // console.log( '*FRAGMENT*', fragmentGlsl );
- const glVertexShader = WebGLShader( gl, 35633, vertexGlsl );
- const glFragmentShader = WebGLShader( gl, 35632, fragmentGlsl );
- gl.attachShader( program, glVertexShader );
- gl.attachShader( program, glFragmentShader );
- // Force a particular attribute to index 0.
- if ( parameters.index0AttributeName !== undefined ) {
- gl.bindAttribLocation( program, 0, parameters.index0AttributeName );
- } else if ( parameters.morphTargets === true ) {
- // programs with morphTargets displace position out of attribute 0
- gl.bindAttribLocation( program, 0, 'position' );
- }
- gl.linkProgram( program );
- // check for link errors
- if ( renderer.debug.checkShaderErrors ) {
- const programLog = gl.getProgramInfoLog( program ).trim();
- const vertexLog = gl.getShaderInfoLog( glVertexShader ).trim();
- const fragmentLog = gl.getShaderInfoLog( glFragmentShader ).trim();
- let runnable = true;
- let haveDiagnostics = true;
- if ( gl.getProgramParameter( program, 35714 ) === false ) {
- runnable = false;
- const vertexErrors = getShaderErrors( gl, glVertexShader, 'vertex' );
- const fragmentErrors = getShaderErrors( gl, glFragmentShader, 'fragment' );
- console.error( 'THREE.WebGLProgram: shader error: ', gl.getError(), '35715', gl.getProgramParameter( program, 35715 ), 'gl.getProgramInfoLog', programLog, vertexErrors, fragmentErrors );
- //add:
- if(fragmentErrors){
- console.log(fragmentGlsl.split("\n").map((a, i) => `${i + 1}`.padEnd(5) + a).join("\n") );
- }else {
- console.log(vertexGlsl.split("\n").map((a, i) => `${i + 1}`.padEnd(5) + a).join("\n") );
- }
-
- } else if ( programLog !== '' ) {
- console.warn( 'THREE.WebGLProgram: gl.getProgramInfoLog()', programLog );
- } else if ( vertexLog === '' || fragmentLog === '' ) {
- haveDiagnostics = false;
- }
- if ( haveDiagnostics ) {
- this.diagnostics = {
- runnable: runnable,
- programLog: programLog,
- vertexShader: {
- log: vertexLog,
- prefix: prefixVertex
- },
- fragmentShader: {
- log: fragmentLog,
- prefix: prefixFragment
- }
- };
- }
- }
- // Clean up
- // Crashes in iOS9 and iOS10. #18402
- // gl.detachShader( program, glVertexShader );
- // gl.detachShader( program, glFragmentShader );
- gl.deleteShader( glVertexShader );
- gl.deleteShader( glFragmentShader );
- // set up caching for uniform locations
- let cachedUniforms;
- this.getUniforms = function () {
- if ( cachedUniforms === undefined ) {
- cachedUniforms = new WebGLUniforms( gl, program );
- }
- return cachedUniforms;
- };
- // set up caching for attribute locations
- let cachedAttributes;
- this.getAttributes = function () {
- if ( cachedAttributes === undefined ) {
- cachedAttributes = fetchAttributeLocations( gl, program );
- }
- return cachedAttributes;
- };
- // free resource
- this.destroy = function () {
- bindingStates.releaseStatesOfProgram( this );
- gl.deleteProgram( program );
- this.program = undefined;
- };
- //
- this.name = parameters.shaderName;
- this.id = programIdCount ++;
- this.cacheKey = cacheKey;
- this.usedTimes = 1;
- this.program = program;
- this.vertexShader = glVertexShader;
- this.fragmentShader = glFragmentShader;
- return this;
- }
- function WebGLPrograms( renderer, cubemaps, extensions, capabilities, bindingStates, clipping ) {
- const programs = [];
- const isWebGL2 = capabilities.isWebGL2;
- const logarithmicDepthBuffer = capabilities.logarithmicDepthBuffer;
- const floatVertexTextures = capabilities.floatVertexTextures;
- const maxVertexUniforms = capabilities.maxVertexUniforms;
- const vertexTextures = capabilities.vertexTextures;
- let precision = capabilities.precision;
- const shaderIDs = {
- MeshDepthMaterial: 'depth',
- MeshDistanceMaterial: 'distanceRGBA',
- MeshNormalMaterial: 'normal',
- MeshBasicMaterial: 'basic',
- MeshLambertMaterial: 'lambert',
- MeshPhongMaterial: 'phong',
- MeshToonMaterial: 'toon',
- MeshStandardMaterial: 'physical',
- MeshPhysicalMaterial: 'physical',
- MeshMatcapMaterial: 'matcap',
- LineBasicMaterial: 'basic',
- LineDashedMaterial: 'dashed',
- PointsMaterial: 'points',
- ShadowMaterial: 'shadow',
- SpriteMaterial: 'sprite'
- };
- const parameterNames = [
- 'precision', 'isWebGL2', 'supportsVertexTextures', 'outputEncoding', 'instancing', 'instancingColor',
- 'map', 'mapEncoding', 'matcap', 'matcapEncoding', 'envMap', 'envMapMode', 'envMapEncoding', 'envMapCubeUV',
- 'lightMap', 'lightMapEncoding', 'aoMap', 'emissiveMap', 'emissiveMapEncoding', 'bumpMap', 'normalMap', 'objectSpaceNormalMap', 'tangentSpaceNormalMap', 'clearcoatMap', 'clearcoatRoughnessMap', 'clearcoatNormalMap', 'displacementMap', 'specularMap',
- 'roughnessMap', 'metalnessMap', 'gradientMap',
- 'alphaMap', 'combine', 'vertexColors', 'vertexTangents', 'vertexUvs', 'uvsVertexOnly', 'fog', 'useFog', 'fogExp2',
- 'flatShading', 'sizeAttenuation', 'logarithmicDepthBuffer', 'skinning',
- 'maxBones', 'useVertexTexture', 'morphTargets', 'morphNormals',
- 'maxMorphTargets', 'maxMorphNormals', 'premultipliedAlpha',
- 'numDirLights', 'numPointLights', 'numSpotLights', 'numHemiLights', 'numRectAreaLights',
- 'numDirLightShadows', 'numPointLightShadows', 'numSpotLightShadows',
- 'shadowMapEnabled', 'shadowMapType', 'toneMapping', 'physicallyCorrectLights',
- 'alphaTest', 'doubleSided', 'flipSided', 'numClippingPlanes', 'numClipIntersection', 'depthPacking', 'dithering',
- 'sheen', 'transmissionMap'
- ];
- function getMaxBones( object ) {
- const skeleton = object.skeleton;
- const bones = skeleton.bones;
- if ( floatVertexTextures ) {
- return 1024;
- } else {
- // default for when object is not specified
- // ( for example when prebuilding shader to be used with multiple objects )
- //
- // - leave some extra space for other uniforms
- // - limit here is ANGLE's 254 max uniform vectors
- // (up to 54 should be safe)
- const nVertexUniforms = maxVertexUniforms;
- const nVertexMatrices = Math.floor( ( nVertexUniforms - 20 ) / 4 );
- const maxBones = Math.min( nVertexMatrices, bones.length );
- if ( maxBones < bones.length ) {
- console.warn( 'THREE.WebGLRenderer: Skeleton has ' + bones.length + ' bones. This GPU supports ' + maxBones + '.' );
- return 0;
- }
- return maxBones;
- }
- }
- function getTextureEncodingFromMap( map ) {
- let encoding;
- if ( map && map.isTexture ) {
- encoding = map.encoding;
- } else if ( map && map.isWebGLRenderTarget ) {
- console.warn( 'THREE.WebGLPrograms.getTextureEncodingFromMap: don\'t use render targets as textures. Use their .texture property instead.' );
- encoding = map.texture.encoding;
- } else {
- encoding = LinearEncoding;
- }
- return encoding;
- }
- function getParameters( material, lights, shadows, scene, object ) {
- const fog = scene.fog;
- const environment = material.isMeshStandardMaterial ? scene.environment : null;
- const envMap = cubemaps.get( material.envMap || environment );
- const shaderID = shaderIDs[ material.type ];
- // heuristics to create shader parameters according to lights in the scene
- // (not to blow over maxLights budget)
- const maxBones = object.isSkinnedMesh ? getMaxBones( object ) : 0;
- if ( material.precision !== null ) {
- precision = capabilities.getMaxPrecision( material.precision );
- if ( precision !== material.precision ) {
- console.warn( 'THREE.WebGLProgram.getParameters:', material.precision, 'not supported, using', precision, 'instead.' );
- }
- }
- let vertexShader, fragmentShader;
- if ( shaderID ) {
- const shader = ShaderLib[ shaderID ];
- vertexShader = shader.vertexShader;
- fragmentShader = shader.fragmentShader;
- } else {
- vertexShader = material.vertexShader;
- fragmentShader = material.fragmentShader;
- }
- const currentRenderTarget = renderer.getRenderTarget();
- const parameters = {
- isWebGL2: isWebGL2,
- shaderID: shaderID,
- shaderName: material.type,
- vertexShader: vertexShader,
- fragmentShader: fragmentShader,
- defines: material.defines,
- isRawShaderMaterial: material.isRawShaderMaterial === true,
- glslVersion: material.glslVersion,
- precision: precision,
- instancing: object.isInstancedMesh === true,
- instancingColor: object.isInstancedMesh === true && object.instanceColor !== null,
- supportsVertexTextures: vertexTextures,
- outputEncoding: ( currentRenderTarget !== null ) ? getTextureEncodingFromMap( currentRenderTarget.texture ) : renderer.outputEncoding,
- map: !! material.map,
- mapEncoding: getTextureEncodingFromMap( material.map ),
- matcap: !! material.matcap,
- matcapEncoding: getTextureEncodingFromMap( material.matcap ),
- envMap: !! envMap,
- envMapMode: envMap && envMap.mapping,
- envMapEncoding: getTextureEncodingFromMap( envMap ),
- envMapCubeUV: ( !! envMap ) && ( ( envMap.mapping === CubeUVReflectionMapping ) || ( envMap.mapping === CubeUVRefractionMapping ) ),
- lightMap: !! material.lightMap,
- lightMapEncoding: getTextureEncodingFromMap( material.lightMap ),
- aoMap: !! material.aoMap,
- emissiveMap: !! material.emissiveMap,
- emissiveMapEncoding: getTextureEncodingFromMap( material.emissiveMap ),
- bumpMap: !! material.bumpMap,
- normalMap: !! material.normalMap,
- objectSpaceNormalMap: material.normalMapType === ObjectSpaceNormalMap,
- tangentSpaceNormalMap: material.normalMapType === TangentSpaceNormalMap,
- clearcoatMap: !! material.clearcoatMap,
- clearcoatRoughnessMap: !! material.clearcoatRoughnessMap,
- clearcoatNormalMap: !! material.clearcoatNormalMap,
- displacementMap: !! material.displacementMap,
- roughnessMap: !! material.roughnessMap,
- metalnessMap: !! material.metalnessMap,
- specularMap: !! material.specularMap,
- alphaMap: !! material.alphaMap,
- gradientMap: !! material.gradientMap,
- sheen: !! material.sheen,
- transmissionMap: !! material.transmissionMap,
- combine: material.combine,
- vertexTangents: ( material.normalMap && material.vertexTangents ),
- vertexColors: material.vertexColors,
- vertexUvs: !! material.map || !! material.bumpMap || !! material.normalMap || !! material.specularMap || !! material.alphaMap || !! material.emissiveMap || !! material.roughnessMap || !! material.metalnessMap || !! material.clearcoatMap || !! material.clearcoatRoughnessMap || !! material.clearcoatNormalMap || !! material.displacementMap || !! material.transmissionMap,
- uvsVertexOnly: ! ( !! material.map || !! material.bumpMap || !! material.normalMap || !! material.specularMap || !! material.alphaMap || !! material.emissiveMap || !! material.roughnessMap || !! material.metalnessMap || !! material.clearcoatNormalMap || !! material.transmissionMap ) && !! material.displacementMap,
- fog: !! fog,
- useFog: material.fog,
- fogExp2: ( fog && fog.isFogExp2 ),
- flatShading: material.flatShading,
- sizeAttenuation: material.sizeAttenuation,
- logarithmicDepthBuffer: logarithmicDepthBuffer,
- skinning: material.skinning && maxBones > 0,
- maxBones: maxBones,
- useVertexTexture: floatVertexTextures,
- morphTargets: material.morphTargets,
- morphNormals: material.morphNormals,
- maxMorphTargets: renderer.maxMorphTargets,
- maxMorphNormals: renderer.maxMorphNormals,
- numDirLights: lights.directional.length,
- numPointLights: lights.point.length,
- numSpotLights: lights.spot.length,
- numRectAreaLights: lights.rectArea.length,
- numHemiLights: lights.hemi.length,
- numDirLightShadows: lights.directionalShadowMap.length,
- numPointLightShadows: lights.pointShadowMap.length,
- numSpotLightShadows: lights.spotShadowMap.length,
- numClippingPlanes: clipping.numPlanes,
- numClipIntersection: clipping.numIntersection,
- dithering: material.dithering,
- shadowMapEnabled: renderer.shadowMap.enabled && shadows.length > 0,
- shadowMapType: renderer.shadowMap.type,
- toneMapping: material.toneMapped ? renderer.toneMapping : NoToneMapping,
- physicallyCorrectLights: renderer.physicallyCorrectLights,
- premultipliedAlpha: material.premultipliedAlpha,
- alphaTest: material.alphaTest,
- doubleSided: material.side === DoubleSide,
- flipSided: material.side === BackSide,
- depthPacking: ( material.depthPacking !== undefined ) ? material.depthPacking : false,
- index0AttributeName: material.index0AttributeName,
- extensionDerivatives: material.extensions && material.extensions.derivatives,
- extensionFragDepth: material.extensions && material.extensions.fragDepth,
- extensionDrawBuffers: material.extensions && material.extensions.drawBuffers,
- extensionShaderTextureLOD: material.extensions && material.extensions.shaderTextureLOD,
- rendererExtensionFragDepth: isWebGL2 || extensions.has( 'EXT_frag_depth' ),
- rendererExtensionDrawBuffers: isWebGL2 || extensions.has( 'WEBGL_draw_buffers' ),
- rendererExtensionShaderTextureLod: isWebGL2 || extensions.has( 'EXT_shader_texture_lod' ),
- customProgramCacheKey: material.customProgramCacheKey()
- };
- return parameters;
- }
- function getProgramCacheKey( parameters ) {
- const array = [];
- if ( parameters.shaderID ) {
- array.push( parameters.shaderID );
- } else {
- array.push( parameters.fragmentShader );
- array.push( parameters.vertexShader );
- }
- if ( parameters.defines !== undefined ) {
- for ( const name in parameters.defines ) {
- array.push( name );
- array.push( parameters.defines[ name ] );
- }
- }
- if ( parameters.isRawShaderMaterial === false ) {
- for ( let i = 0; i < parameterNames.length; i ++ ) {
- array.push( parameters[ parameterNames[ i ] ] );
- }
- array.push( renderer.outputEncoding );
- array.push( renderer.gammaFactor );
- }
- array.push( parameters.customProgramCacheKey );
- return array.join();
- }
- function getUniforms( material ) {
- const shaderID = shaderIDs[ material.type ];
- let uniforms;
- if ( shaderID ) {
- const shader = ShaderLib[ shaderID ];
- uniforms = UniformsUtils.clone( shader.uniforms );
- } else {
- uniforms = material.uniforms;
- }
- return uniforms;
- }
- function acquireProgram( parameters, cacheKey ) {
- let program;
- // Check if code has been already compiled
- for ( let p = 0, pl = programs.length; p < pl; p ++ ) {
- const preexistingProgram = programs[ p ];
- if ( preexistingProgram.cacheKey === cacheKey ) {
- program = preexistingProgram;
- ++ program.usedTimes;
- break;
- }
- }
- if ( program === undefined ) {
- program = new WebGLProgram( renderer, cacheKey, parameters, bindingStates );
- programs.push( program );
- }
- return program;
- }
- function releaseProgram( program ) {
- if ( -- program.usedTimes === 0 ) {
- // Remove from unordered set
- const i = programs.indexOf( program );
- programs[ i ] = programs[ programs.length - 1 ];
- programs.pop();
- // Free WebGL resources
- program.destroy();
- }
- }
- return {
- getParameters: getParameters,
- getProgramCacheKey: getProgramCacheKey,
- getUniforms: getUniforms,
- acquireProgram: acquireProgram,
- releaseProgram: releaseProgram,
- // Exposed for resource monitoring & error feedback via renderer.info:
- programs: programs
- };
- }
- function WebGLProperties() {
- let properties = new WeakMap();
- function get( object ) {
- let map = properties.get( object );
- if ( map === undefined ) {
- map = {};
- properties.set( object, map );
- }
- return map;
- }
- function remove( object ) {
- properties.delete( object );
- }
- function update( object, key, value ) {
- properties.get( object )[ key ] = value;
- }
- function dispose() {
- properties = new WeakMap();
- }
- return {
- get: get,
- remove: remove,
- update: update,
- dispose: dispose
- };
- }
- function painterSortStable( a, b ) {
- if ( a.groupOrder !== b.groupOrder ) {
- return a.groupOrder - b.groupOrder;
- } else if ( a.renderOrder !== b.renderOrder ) {
- return a.renderOrder - b.renderOrder;
- } else if ( a.program !== b.program ) {
- return a.program.id - b.program.id;
- } else if ( a.material.id !== b.material.id ) {
- return a.material.id - b.material.id;
- } else if ( a.z !== b.z ) {
- return a.z - b.z;
- } else {
- return a.id - b.id;
- }
- }
- function reversePainterSortStable( a, b ) {
- if ( a.groupOrder !== b.groupOrder ) {
- return a.groupOrder - b.groupOrder;
- } else if ( a.renderOrder !== b.renderOrder ) {
- return a.renderOrder - b.renderOrder;
- } else if ( a.z !== b.z ) {
- return b.z - a.z;
- } else {
- return a.id - b.id;
- }
- }
- function WebGLRenderList( properties ) {
- const renderItems = [];
- let renderItemsIndex = 0;
- const opaque = [];
- const transparent = [];
- const defaultProgram = { id: - 1 };
- function init() {
- renderItemsIndex = 0;
- opaque.length = 0;
- transparent.length = 0;
- }
- function getNextRenderItem( object, geometry, material, groupOrder, z, group ) {
- let renderItem = renderItems[ renderItemsIndex ];
- const materialProperties = properties.get( material );
- if ( renderItem === undefined ) {
- renderItem = {
- id: object.id,
- object: object,
- geometry: geometry,
- material: material,
- program: materialProperties.program || defaultProgram,
- groupOrder: groupOrder,
- renderOrder: object.renderOrder,
- z: z,
- group: group
- };
- renderItems[ renderItemsIndex ] = renderItem;
- } else {
- renderItem.id = object.id;
- renderItem.object = object;
- renderItem.geometry = geometry;
- renderItem.material = material;
- renderItem.program = materialProperties.program || defaultProgram;
- renderItem.groupOrder = groupOrder;
- renderItem.renderOrder = object.renderOrder;
- renderItem.z = z;
- renderItem.group = group;
- }
- renderItemsIndex ++;
- return renderItem;
- }
- function push( object, geometry, material, groupOrder, z, group ) {
- const renderItem = getNextRenderItem( object, geometry, material, groupOrder, z, group );
- ( material.transparent === true ? transparent : opaque ).push( renderItem );
- }
- function unshift( object, geometry, material, groupOrder, z, group ) {
- const renderItem = getNextRenderItem( object, geometry, material, groupOrder, z, group );
- ( material.transparent === true ? transparent : opaque ).unshift( renderItem );
- }
- function sort( customOpaqueSort, customTransparentSort ) {
- if ( opaque.length > 1 ) opaque.sort( customOpaqueSort || painterSortStable );
- if ( transparent.length > 1 ) transparent.sort( customTransparentSort || reversePainterSortStable );
- }
- function finish() {
- // Clear references from inactive renderItems in the list
- for ( let i = renderItemsIndex, il = renderItems.length; i < il; i ++ ) {
- const renderItem = renderItems[ i ];
- if ( renderItem.id === null ) break;
- renderItem.id = null;
- renderItem.object = null;
- renderItem.geometry = null;
- renderItem.material = null;
- renderItem.program = null;
- renderItem.group = null;
- }
- }
- return {
- opaque: opaque,
- transparent: transparent,
- init: init,
- push: push,
- unshift: unshift,
- finish: finish,
- sort: sort
- };
- }
- function WebGLRenderLists( properties ) {
- let lists = new WeakMap();
- function get( scene, camera ) {
- const cameras = lists.get( scene );
- let list;
- if ( cameras === undefined ) {
- list = new WebGLRenderList( properties );
- lists.set( scene, new WeakMap() );
- lists.get( scene ).set( camera, list );
- } else {
- list = cameras.get( camera );
- if ( list === undefined ) {
- list = new WebGLRenderList( properties );
- cameras.set( camera, list );
- }
- }
- return list;
- }
- function dispose() {
- lists = new WeakMap();
- }
- return {
- get: get,
- dispose: dispose
- };
- }
- function UniformsCache() {
- const lights = {};
- return {
- get: function ( light ) {
- if ( lights[ light.id ] !== undefined ) {
- return lights[ light.id ];
- }
- let uniforms;
- switch ( light.type ) {
- case 'DirectionalLight':
- uniforms = {
- direction: new Vector3(),
- color: new Color()
- };
- break;
- case 'SpotLight':
- uniforms = {
- position: new Vector3(),
- direction: new Vector3(),
- color: new Color(),
- distance: 0,
- coneCos: 0,
- penumbraCos: 0,
- decay: 0
- };
- break;
- case 'PointLight':
- uniforms = {
- position: new Vector3(),
- color: new Color(),
- distance: 0,
- decay: 0
- };
- break;
- case 'HemisphereLight':
- uniforms = {
- direction: new Vector3(),
- skyColor: new Color(),
- groundColor: new Color()
- };
- break;
- case 'RectAreaLight':
- uniforms = {
- color: new Color(),
- position: new Vector3(),
- halfWidth: new Vector3(),
- halfHeight: new Vector3()
- };
- break;
- }
- lights[ light.id ] = uniforms;
- return uniforms;
- }
- };
- }
- function ShadowUniformsCache() {
- const lights = {};
- return {
- get: function ( light ) {
- if ( lights[ light.id ] !== undefined ) {
- return lights[ light.id ];
- }
- let uniforms;
- switch ( light.type ) {
- case 'DirectionalLight':
- uniforms = {
- shadowBias: 0,
- shadowNormalBias: 0,
- shadowRadius: 1,
- shadowMapSize: new Vector2$1()
- };
- break;
- case 'SpotLight':
- uniforms = {
- shadowBias: 0,
- shadowNormalBias: 0,
- shadowRadius: 1,
- shadowMapSize: new Vector2$1()
- };
- break;
- case 'PointLight':
- uniforms = {
- shadowBias: 0,
- shadowNormalBias: 0,
- shadowRadius: 1,
- shadowMapSize: new Vector2$1(),
- shadowCameraNear: 1,
- shadowCameraFar: 1000
- };
- break;
- // TODO (abelnation): set RectAreaLight shadow uniforms
- }
- lights[ light.id ] = uniforms;
- return uniforms;
- }
- };
- }
- let nextVersion = 0;
- function shadowCastingLightsFirst( lightA, lightB ) {
- return ( lightB.castShadow ? 1 : 0 ) - ( lightA.castShadow ? 1 : 0 );
- }
- function WebGLLights( extensions, capabilities ) {
- const cache = new UniformsCache();
- const shadowCache = ShadowUniformsCache();
- const state = {
- version: 0,
- hash: {
- directionalLength: - 1,
- pointLength: - 1,
- spotLength: - 1,
- rectAreaLength: - 1,
- hemiLength: - 1,
- numDirectionalShadows: - 1,
- numPointShadows: - 1,
- numSpotShadows: - 1
- },
- ambient: [ 0, 0, 0 ],
- probe: [],
- directional: [],
- directionalShadow: [],
- directionalShadowMap: [],
- directionalShadowMatrix: [],
- spot: [],
- spotShadow: [],
- spotShadowMap: [],
- spotShadowMatrix: [],
- rectArea: [],
- rectAreaLTC1: null,
- rectAreaLTC2: null,
- point: [],
- pointShadow: [],
- pointShadowMap: [],
- pointShadowMatrix: [],
- hemi: []
- };
- for ( let i = 0; i < 9; i ++ ) state.probe.push( new Vector3() );
- const vector3 = new Vector3();
- const matrix4 = new Matrix4();
- const matrix42 = new Matrix4();
- function setup( lights ) {
- let r = 0, g = 0, b = 0;
- for ( let i = 0; i < 9; i ++ ) state.probe[ i ].set( 0, 0, 0 );
- let directionalLength = 0;
- let pointLength = 0;
- let spotLength = 0;
- let rectAreaLength = 0;
- let hemiLength = 0;
- let numDirectionalShadows = 0;
- let numPointShadows = 0;
- let numSpotShadows = 0;
- lights.sort( shadowCastingLightsFirst );
- for ( let i = 0, l = lights.length; i < l; i ++ ) {
- const light = lights[ i ];
- const color = light.color;
- const intensity = light.intensity;
- const distance = light.distance;
- const shadowMap = ( light.shadow && light.shadow.map ) ? light.shadow.map.texture : null;
- if ( light.isAmbientLight ) {
- r += color.r * intensity;
- g += color.g * intensity;
- b += color.b * intensity;
- } else if ( light.isLightProbe ) {
- for ( let j = 0; j < 9; j ++ ) {
- state.probe[ j ].addScaledVector( light.sh.coefficients[ j ], intensity );
- }
- } else if ( light.isDirectionalLight ) {
- const uniforms = cache.get( light );
- uniforms.color.copy( light.color ).multiplyScalar( light.intensity );
- if ( light.castShadow ) {
- const shadow = light.shadow;
- const shadowUniforms = shadowCache.get( light );
- shadowUniforms.shadowBias = shadow.bias;
- shadowUniforms.shadowNormalBias = shadow.normalBias;
- shadowUniforms.shadowRadius = shadow.radius;
- shadowUniforms.shadowMapSize = shadow.mapSize;
- state.directionalShadow[ directionalLength ] = shadowUniforms;
- state.directionalShadowMap[ directionalLength ] = shadowMap;
- state.directionalShadowMatrix[ directionalLength ] = light.shadow.matrix;
- numDirectionalShadows ++;
- }
- state.directional[ directionalLength ] = uniforms;
- directionalLength ++;
- } else if ( light.isSpotLight ) {
- const uniforms = cache.get( light );
- uniforms.position.setFromMatrixPosition( light.matrixWorld );
- uniforms.color.copy( color ).multiplyScalar( intensity );
- uniforms.distance = distance;
- uniforms.coneCos = Math.cos( light.angle );
- uniforms.penumbraCos = Math.cos( light.angle * ( 1 - light.penumbra ) );
- uniforms.decay = light.decay;
- if ( light.castShadow ) {
- const shadow = light.shadow;
- const shadowUniforms = shadowCache.get( light );
- shadowUniforms.shadowBias = shadow.bias;
- shadowUniforms.shadowNormalBias = shadow.normalBias;
- shadowUniforms.shadowRadius = shadow.radius;
- shadowUniforms.shadowMapSize = shadow.mapSize;
- state.spotShadow[ spotLength ] = shadowUniforms;
- state.spotShadowMap[ spotLength ] = shadowMap;
- state.spotShadowMatrix[ spotLength ] = light.shadow.matrix;
- numSpotShadows ++;
- }
- state.spot[ spotLength ] = uniforms;
- spotLength ++;
- } else if ( light.isRectAreaLight ) {
- const uniforms = cache.get( light );
- // (a) intensity is the total visible light emitted
- //uniforms.color.copy( color ).multiplyScalar( intensity / ( light.width * light.height * Math.PI ) );
- // (b) intensity is the brightness of the light
- uniforms.color.copy( color ).multiplyScalar( intensity );
- uniforms.halfWidth.set( light.width * 0.5, 0.0, 0.0 );
- uniforms.halfHeight.set( 0.0, light.height * 0.5, 0.0 );
- state.rectArea[ rectAreaLength ] = uniforms;
- rectAreaLength ++;
- } else if ( light.isPointLight ) {
- const uniforms = cache.get( light );
- uniforms.color.copy( light.color ).multiplyScalar( light.intensity );
- uniforms.distance = light.distance;
- uniforms.decay = light.decay;
- if ( light.castShadow ) {
- const shadow = light.shadow;
- const shadowUniforms = shadowCache.get( light );
- shadowUniforms.shadowBias = shadow.bias;
- shadowUniforms.shadowNormalBias = shadow.normalBias;
- shadowUniforms.shadowRadius = shadow.radius;
- shadowUniforms.shadowMapSize = shadow.mapSize;
- shadowUniforms.shadowCameraNear = shadow.camera.near;
- shadowUniforms.shadowCameraFar = shadow.camera.far;
- state.pointShadow[ pointLength ] = shadowUniforms;
- state.pointShadowMap[ pointLength ] = shadowMap;
- state.pointShadowMatrix[ pointLength ] = light.shadow.matrix;
- numPointShadows ++;
- }
- state.point[ pointLength ] = uniforms;
- pointLength ++;
- } else if ( light.isHemisphereLight ) {
- const uniforms = cache.get( light );
- uniforms.skyColor.copy( light.color ).multiplyScalar( intensity );
- uniforms.groundColor.copy( light.groundColor ).multiplyScalar( intensity );
- state.hemi[ hemiLength ] = uniforms;
- hemiLength ++;
- }
- }
- if ( rectAreaLength > 0 ) {
- if ( capabilities.isWebGL2 ) {
- // WebGL 2
- state.rectAreaLTC1 = UniformsLib.LTC_FLOAT_1;
- state.rectAreaLTC2 = UniformsLib.LTC_FLOAT_2;
- } else {
- // WebGL 1
- if ( extensions.has( 'OES_texture_float_linear' ) === true ) {
- state.rectAreaLTC1 = UniformsLib.LTC_FLOAT_1;
- state.rectAreaLTC2 = UniformsLib.LTC_FLOAT_2;
- } else if ( extensions.has( 'OES_texture_half_float_linear' ) === true ) {
- state.rectAreaLTC1 = UniformsLib.LTC_HALF_1;
- state.rectAreaLTC2 = UniformsLib.LTC_HALF_2;
- } else {
- console.error( 'THREE.WebGLRenderer: Unable to use RectAreaLight. Missing WebGL extensions.' );
- }
- }
- }
- state.ambient[ 0 ] = r;
- state.ambient[ 1 ] = g;
- state.ambient[ 2 ] = b;
- const hash = state.hash;
- if ( hash.directionalLength !== directionalLength ||
- hash.pointLength !== pointLength ||
- hash.spotLength !== spotLength ||
- hash.rectAreaLength !== rectAreaLength ||
- hash.hemiLength !== hemiLength ||
- hash.numDirectionalShadows !== numDirectionalShadows ||
- hash.numPointShadows !== numPointShadows ||
- hash.numSpotShadows !== numSpotShadows ) {
- state.directional.length = directionalLength;
- state.spot.length = spotLength;
- state.rectArea.length = rectAreaLength;
- state.point.length = pointLength;
- state.hemi.length = hemiLength;
- state.directionalShadow.length = numDirectionalShadows;
- state.directionalShadowMap.length = numDirectionalShadows;
- state.pointShadow.length = numPointShadows;
- state.pointShadowMap.length = numPointShadows;
- state.spotShadow.length = numSpotShadows;
- state.spotShadowMap.length = numSpotShadows;
- state.directionalShadowMatrix.length = numDirectionalShadows;
- state.pointShadowMatrix.length = numPointShadows;
- state.spotShadowMatrix.length = numSpotShadows;
- hash.directionalLength = directionalLength;
- hash.pointLength = pointLength;
- hash.spotLength = spotLength;
- hash.rectAreaLength = rectAreaLength;
- hash.hemiLength = hemiLength;
- hash.numDirectionalShadows = numDirectionalShadows;
- hash.numPointShadows = numPointShadows;
- hash.numSpotShadows = numSpotShadows;
- state.version = nextVersion ++;
- }
- }
- function setupView( lights, camera ) {
- let directionalLength = 0;
- let pointLength = 0;
- let spotLength = 0;
- let rectAreaLength = 0;
- let hemiLength = 0;
- const viewMatrix = camera.matrixWorldInverse;
- for ( let i = 0, l = lights.length; i < l; i ++ ) {
- const light = lights[ i ];
- if ( light.isDirectionalLight ) {
- const uniforms = state.directional[ directionalLength ];
- uniforms.direction.setFromMatrixPosition( light.matrixWorld );
- vector3.setFromMatrixPosition( light.target.matrixWorld );
- uniforms.direction.sub( vector3 );
- uniforms.direction.transformDirection( viewMatrix );
- directionalLength ++;
- } else if ( light.isSpotLight ) {
- const uniforms = state.spot[ spotLength ];
- uniforms.position.setFromMatrixPosition( light.matrixWorld );
- uniforms.position.applyMatrix4( viewMatrix );
- uniforms.direction.setFromMatrixPosition( light.matrixWorld );
- vector3.setFromMatrixPosition( light.target.matrixWorld );
- uniforms.direction.sub( vector3 );
- uniforms.direction.transformDirection( viewMatrix );
- spotLength ++;
- } else if ( light.isRectAreaLight ) {
- const uniforms = state.rectArea[ rectAreaLength ];
- uniforms.position.setFromMatrixPosition( light.matrixWorld );
- uniforms.position.applyMatrix4( viewMatrix );
- // extract local rotation of light to derive width/height half vectors
- matrix42.identity();
- matrix4.copy( light.matrixWorld );
- matrix4.premultiply( viewMatrix );
- matrix42.extractRotation( matrix4 );
- uniforms.halfWidth.set( light.width * 0.5, 0.0, 0.0 );
- uniforms.halfHeight.set( 0.0, light.height * 0.5, 0.0 );
- uniforms.halfWidth.applyMatrix4( matrix42 );
- uniforms.halfHeight.applyMatrix4( matrix42 );
- rectAreaLength ++;
- } else if ( light.isPointLight ) {
- const uniforms = state.point[ pointLength ];
- uniforms.position.setFromMatrixPosition( light.matrixWorld );
- uniforms.position.applyMatrix4( viewMatrix );
- pointLength ++;
- } else if ( light.isHemisphereLight ) {
- const uniforms = state.hemi[ hemiLength ];
- uniforms.direction.setFromMatrixPosition( light.matrixWorld );
- uniforms.direction.transformDirection( viewMatrix );
- uniforms.direction.normalize();
- hemiLength ++;
- }
- }
- }
- return {
- setup: setup,
- setupView: setupView,
- state: state
- };
- }
- function WebGLRenderState( extensions, capabilities ) {
- const lights = new WebGLLights( extensions, capabilities );
- const lightsArray = [];
- const shadowsArray = [];
- function init() {
- lightsArray.length = 0;
- shadowsArray.length = 0;
- }
- function pushLight( light ) {
- lightsArray.push( light );
- }
- function pushShadow( shadowLight ) {
- shadowsArray.push( shadowLight );
- }
- function setupLights() {
- lights.setup( lightsArray );
- }
- function setupLightsView( camera ) {
- lights.setupView( lightsArray, camera );
- }
- const state = {
- lightsArray: lightsArray,
- shadowsArray: shadowsArray,
- lights: lights
- };
- return {
- init: init,
- state: state,
- setupLights: setupLights,
- setupLightsView: setupLightsView,
- pushLight: pushLight,
- pushShadow: pushShadow
- };
- }
- function WebGLRenderStates( extensions, capabilities ) {
- let renderStates = new WeakMap();
- function get( scene, renderCallDepth = 0 ) {
- let renderState;
- if ( renderStates.has( scene ) === false ) {
- renderState = new WebGLRenderState( extensions, capabilities );
- renderStates.set( scene, [] );
- renderStates.get( scene ).push( renderState );
- } else {
- if ( renderCallDepth >= renderStates.get( scene ).length ) {
- renderState = new WebGLRenderState( extensions, capabilities );
- renderStates.get( scene ).push( renderState );
- } else {
- renderState = renderStates.get( scene )[ renderCallDepth ];
- }
- }
- return renderState;
- }
- function dispose() {
- renderStates = new WeakMap();
- }
- return {
- get: get,
- dispose: dispose
- };
- }
- /**
- * parameters = {
- *
- * opacity: <float>,
- *
- * map: new THREE.Texture( <Image> ),
- *
- * alphaMap: new THREE.Texture( <Image> ),
- *
- * displacementMap: new THREE.Texture( <Image> ),
- * displacementScale: <float>,
- * displacementBias: <float>,
- *
- * wireframe: <boolean>,
- * wireframelineWidth: <float>
- * }
- */
- function MeshDepthMaterial( parameters ) {
- Material.call( this );
- this.type = 'MeshDepthMaterial';
- this.depthPacking = BasicDepthPacking;
- this.skinning = false;
- this.morphTargets = false;
- this.map = null;
- this.alphaMap = null;
- this.displacementMap = null;
- this.displacementScale = 1;
- this.displacementBias = 0;
- this.wireframe = false;
- this.wireframelineWidth = 1;
- this.fog = false;
- this.setValues( parameters );
- }
- MeshDepthMaterial.prototype = Object.create( Material.prototype );
- MeshDepthMaterial.prototype.constructor = MeshDepthMaterial;
- MeshDepthMaterial.prototype.isMeshDepthMaterial = true;
- MeshDepthMaterial.prototype.copy = function ( source ) {
- Material.prototype.copy.call( this, source );
- this.depthPacking = source.depthPacking;
- this.skinning = source.skinning;
- this.morphTargets = source.morphTargets;
- this.map = source.map;
- this.alphaMap = source.alphaMap;
- this.displacementMap = source.displacementMap;
- this.displacementScale = source.displacementScale;
- this.displacementBias = source.displacementBias;
- this.wireframe = source.wireframe;
- this.wireframelineWidth = source.wireframelineWidth;
- return this;
- };
- /**
- * parameters = {
- *
- * referencePosition: <float>,
- * nearDistance: <float>,
- * farDistance: <float>,
- *
- * skinning: <bool>,
- * morphTargets: <bool>,
- *
- * map: new THREE.Texture( <Image> ),
- *
- * alphaMap: new THREE.Texture( <Image> ),
- *
- * displacementMap: new THREE.Texture( <Image> ),
- * displacementScale: <float>,
- * displacementBias: <float>
- *
- * }
- */
- function MeshDistanceMaterial( parameters ) {
- Material.call( this );
- this.type = 'MeshDistanceMaterial';
- this.referencePosition = new Vector3();
- this.nearDistance = 1;
- this.farDistance = 1000;
- this.skinning = false;
- this.morphTargets = false;
- this.map = null;
- this.alphaMap = null;
- this.displacementMap = null;
- this.displacementScale = 1;
- this.displacementBias = 0;
- this.fog = false;
- this.setValues( parameters );
- }
- MeshDistanceMaterial.prototype = Object.create( Material.prototype );
- MeshDistanceMaterial.prototype.constructor = MeshDistanceMaterial;
- MeshDistanceMaterial.prototype.isMeshDistanceMaterial = true;
- MeshDistanceMaterial.prototype.copy = function ( source ) {
- Material.prototype.copy.call( this, source );
- this.referencePosition.copy( source.referencePosition );
- this.nearDistance = source.nearDistance;
- this.farDistance = source.farDistance;
- this.skinning = source.skinning;
- this.morphTargets = source.morphTargets;
- this.map = source.map;
- this.alphaMap = source.alphaMap;
- this.displacementMap = source.displacementMap;
- this.displacementScale = source.displacementScale;
- this.displacementBias = source.displacementBias;
- return this;
- };
- var vsm_frag = "uniform sampler2D shadow_pass;\nuniform vec2 resolution;\nuniform float radius;\n#include <packing>\nvoid main() {\n\tfloat mean = 0.0;\n\tfloat squared_mean = 0.0;\n\tfloat depth = unpackRGBAToDepth( texture2D( shadow_pass, ( gl_FragCoord.xy ) / resolution ) );\n\tfor ( float i = -1.0; i < 1.0 ; i += SAMPLE_RATE) {\n\t\t#ifdef HORIZONTAL_PASS\n\t\t\tvec2 distribution = unpackRGBATo2Half( texture2D( shadow_pass, ( gl_FragCoord.xy + vec2( i, 0.0 ) * radius ) / resolution ) );\n\t\t\tmean += distribution.x;\n\t\t\tsquared_mean += distribution.y * distribution.y + distribution.x * distribution.x;\n\t\t#else\n\t\t\tfloat depth = unpackRGBAToDepth( texture2D( shadow_pass, ( gl_FragCoord.xy + vec2( 0.0, i ) * radius ) / resolution ) );\n\t\t\tmean += depth;\n\t\t\tsquared_mean += depth * depth;\n\t\t#endif\n\t}\n\tmean = mean * HALF_SAMPLE_RATE;\n\tsquared_mean = squared_mean * HALF_SAMPLE_RATE;\n\tfloat std_dev = sqrt( squared_mean - mean * mean );\n\tgl_FragColor = pack2HalfToRGBA( vec2( mean, std_dev ) );\n}";
- var vsm_vert = "void main() {\n\tgl_Position = vec4( position, 1.0 );\n}";
- function WebGLShadowMap( _renderer, _objects, maxTextureSize ) {
- let _frustum = new Frustum();
- const _shadowMapSize = new Vector2$1(),
- _viewportSize = new Vector2$1(),
- _viewport = new Vector4(),
- _depthMaterials = [],
- _distanceMaterials = [],
- _materialCache = {};
- const shadowSide = { 0: BackSide, 1: FrontSide, 2: DoubleSide };
- const shadowMaterialVertical = new ShaderMaterial( {
- defines: {
- SAMPLE_RATE: 2.0 / 8.0,
- HALF_SAMPLE_RATE: 1.0 / 8.0
- },
- uniforms: {
- shadow_pass: { value: null },
- resolution: { value: new Vector2$1() },
- radius: { value: 4.0 }
- },
- vertexShader: vsm_vert,
- fragmentShader: vsm_frag
- } );
- const shadowMaterialHorizontal = shadowMaterialVertical.clone();
- shadowMaterialHorizontal.defines.HORIZONTAL_PASS = 1;
- const fullScreenTri = new BufferGeometry();
- fullScreenTri.setAttribute(
- 'position',
- new BufferAttribute(
- new Float32Array( [ - 1, - 1, 0.5, 3, - 1, 0.5, - 1, 3, 0.5 ] ),
- 3
- )
- );
- const fullScreenMesh = new Mesh( fullScreenTri, shadowMaterialVertical );
- const scope = this;
- this.enabled = false;
- this.autoUpdate = true;
- this.needsUpdate = false;
- this.type = PCFShadowMap;
- this.render = function ( lights, scene, camera ) {
- if ( scope.enabled === false ) return;
- if ( scope.autoUpdate === false && scope.needsUpdate === false ) return;
- if ( lights.length === 0 ) return;
- const currentRenderTarget = _renderer.getRenderTarget();
- const activeCubeFace = _renderer.getActiveCubeFace();
- const activeMipmapLevel = _renderer.getActiveMipmapLevel();
- const _state = _renderer.state;
- // Set GL state for depth map.
- _state.setBlending( NoBlending );
- _state.buffers.color.setClear( 1, 1, 1, 1 );
- _state.buffers.depth.setTest( true );
- _state.setScissorTest( false );
- // render depth map
- for ( let i = 0, il = lights.length; i < il; i ++ ) {
- const light = lights[ i ];
- const shadow = light.shadow;
- if ( shadow === undefined ) {
- console.warn( 'THREE.WebGLShadowMap:', light, 'has no shadow.' );
- continue;
- }
- if ( shadow.autoUpdate === false && shadow.needsUpdate === false ) continue;
- _shadowMapSize.copy( shadow.mapSize );
- const shadowFrameExtents = shadow.getFrameExtents();
- _shadowMapSize.multiply( shadowFrameExtents );
- _viewportSize.copy( shadow.mapSize );
- if ( _shadowMapSize.x > maxTextureSize || _shadowMapSize.y > maxTextureSize ) {
- if ( _shadowMapSize.x > maxTextureSize ) {
- _viewportSize.x = Math.floor( maxTextureSize / shadowFrameExtents.x );
- _shadowMapSize.x = _viewportSize.x * shadowFrameExtents.x;
- shadow.mapSize.x = _viewportSize.x;
- }
- if ( _shadowMapSize.y > maxTextureSize ) {
- _viewportSize.y = Math.floor( maxTextureSize / shadowFrameExtents.y );
- _shadowMapSize.y = _viewportSize.y * shadowFrameExtents.y;
- shadow.mapSize.y = _viewportSize.y;
- }
- }
- if ( shadow.map === null && ! shadow.isPointLightShadow && this.type === VSMShadowMap ) {
- const pars = { minFilter: LinearFilter, magFilter: LinearFilter, format: RGBAFormat };
- shadow.map = new WebGLRenderTarget( _shadowMapSize.x, _shadowMapSize.y, pars );
- shadow.map.texture.name = light.name + '.shadowMap';
- shadow.mapPass = new WebGLRenderTarget( _shadowMapSize.x, _shadowMapSize.y, pars );
- shadow.camera.updateProjectionMatrix();
- }
- if ( shadow.map === null ) {
- const pars = { minFilter: NearestFilter, magFilter: NearestFilter, format: RGBAFormat };
- shadow.map = new WebGLRenderTarget( _shadowMapSize.x, _shadowMapSize.y, pars );
- shadow.map.texture.name = light.name + '.shadowMap';
- shadow.camera.updateProjectionMatrix();
- }
- _renderer.setRenderTarget( shadow.map );
- _renderer.clear();
- const viewportCount = shadow.getViewportCount();
- for ( let vp = 0; vp < viewportCount; vp ++ ) {
- const viewport = shadow.getViewport( vp );
- _viewport.set(
- _viewportSize.x * viewport.x,
- _viewportSize.y * viewport.y,
- _viewportSize.x * viewport.z,
- _viewportSize.y * viewport.w
- );
- _state.viewport( _viewport );
- shadow.updateMatrices( light, vp );
- _frustum = shadow.getFrustum();
- renderObject( scene, camera, shadow.camera, light, this.type );
- }
- // do blur pass for VSM
- if ( ! shadow.isPointLightShadow && this.type === VSMShadowMap ) {
- VSMPass( shadow, camera );
- }
- shadow.needsUpdate = false;
- }
- scope.needsUpdate = false;
- _renderer.setRenderTarget( currentRenderTarget, activeCubeFace, activeMipmapLevel );
- };
- function VSMPass( shadow, camera ) {
- const geometry = _objects.update( fullScreenMesh );
- // vertical pass
- shadowMaterialVertical.uniforms.shadow_pass.value = shadow.map.texture;
- shadowMaterialVertical.uniforms.resolution.value = shadow.mapSize;
- shadowMaterialVertical.uniforms.radius.value = shadow.radius;
- _renderer.setRenderTarget( shadow.mapPass );
- _renderer.clear();
- _renderer.renderBufferDirect( camera, null, geometry, shadowMaterialVertical, fullScreenMesh, null );
- // horizontal pass
- shadowMaterialHorizontal.uniforms.shadow_pass.value = shadow.mapPass.texture;
- shadowMaterialHorizontal.uniforms.resolution.value = shadow.mapSize;
- shadowMaterialHorizontal.uniforms.radius.value = shadow.radius;
- _renderer.setRenderTarget( shadow.map );
- _renderer.clear();
- _renderer.renderBufferDirect( camera, null, geometry, shadowMaterialHorizontal, fullScreenMesh, null );
- }
- function getDepthMaterialVariant( useMorphing, useSkinning, useInstancing ) {
- const index = useMorphing << 0 | useSkinning << 1 | useInstancing << 2;
- let material = _depthMaterials[ index ];
- if ( material === undefined ) {
- material = new MeshDepthMaterial( {
- depthPacking: RGBADepthPacking,
- morphTargets: useMorphing,
- skinning: useSkinning
- } );
- _depthMaterials[ index ] = material;
- }
- return material;
- }
- function getDistanceMaterialVariant( useMorphing, useSkinning, useInstancing ) {
- const index = useMorphing << 0 | useSkinning << 1 | useInstancing << 2;
- let material = _distanceMaterials[ index ];
- if ( material === undefined ) {
- material = new MeshDistanceMaterial( {
- morphTargets: useMorphing,
- skinning: useSkinning
- } );
- _distanceMaterials[ index ] = material;
- }
- return material;
- }
- function getDepthMaterial( object, geometry, material, light, shadowCameraNear, shadowCameraFar, type ) {
- let result = null;
- let getMaterialVariant = getDepthMaterialVariant;
- let customMaterial = object.customDepthMaterial;
- if ( light.isPointLight === true ) {
- getMaterialVariant = getDistanceMaterialVariant;
- customMaterial = object.customDistanceMaterial;
- }
- if ( customMaterial === undefined ) {
- let useMorphing = false;
- if ( material.morphTargets === true ) {
- useMorphing = geometry.morphAttributes && geometry.morphAttributes.position && geometry.morphAttributes.position.length > 0;
- }
- let useSkinning = false;
- if ( object.isSkinnedMesh === true ) {
- if ( material.skinning === true ) {
- useSkinning = true;
- } else {
- console.warn( 'THREE.WebGLShadowMap: THREE.SkinnedMesh with material.skinning set to false:', object );
- }
- }
- const useInstancing = object.isInstancedMesh === true;
- result = getMaterialVariant( useMorphing, useSkinning, useInstancing );
- } else {
- result = customMaterial;
- }
- if ( _renderer.localClippingEnabled &&
- material.clipShadows === true &&
- material.clippingPlanes.length !== 0 ) {
- // in this case we need a unique material instance reflecting the
- // appropriate state
- const keyA = result.uuid, keyB = material.uuid;
- let materialsForVariant = _materialCache[ keyA ];
- if ( materialsForVariant === undefined ) {
- materialsForVariant = {};
- _materialCache[ keyA ] = materialsForVariant;
- }
- let cachedMaterial = materialsForVariant[ keyB ];
- if ( cachedMaterial === undefined ) {
- cachedMaterial = result.clone();
- materialsForVariant[ keyB ] = cachedMaterial;
- }
- result = cachedMaterial;
- }
- result.visible = material.visible;
- result.wireframe = material.wireframe;
- if ( type === VSMShadowMap ) {
- result.side = ( material.shadowSide !== null ) ? material.shadowSide : material.side;
- } else {
- result.side = ( material.shadowSide !== null ) ? material.shadowSide : shadowSide[ material.side ];
- }
- result.clipShadows = material.clipShadows;
- result.clippingPlanes = material.clippingPlanes;
- result.clipIntersection = material.clipIntersection;
- result.wireframelineWidth = material.wireframelineWidth;
- result.lineWidth = material.lineWidth;
- if ( light.isPointLight === true && result.isMeshDistanceMaterial === true ) {
- result.referencePosition.setFromMatrixPosition( light.matrixWorld );
- result.nearDistance = shadowCameraNear;
- result.farDistance = shadowCameraFar;
- }
- return result;
- }
- function renderObject( object, camera, shadowCamera, light, type ) {
- if ( object.visible === false ) return;
- const visible = object.layers.test( camera.layers );
- if ( visible && ( object.isMesh || object.isLine || object.isPoints ) ) {
- if ( ( object.castShadow || ( object.receiveShadow && type === VSMShadowMap ) ) && ( ! object.frustumCulled || _frustum.intersectsObject( object ) ) ) {
- object.modelViewMatrix.multiplyMatrices( shadowCamera.matrixWorldInverse, object.matrixWorld );
- const geometry = _objects.update( object );
- const material = object.material;
- if ( Array.isArray( material ) ) {
- const groups = geometry.groups;
- for ( let k = 0, kl = groups.length; k < kl; k ++ ) {
- const group = groups[ k ];
- const groupMaterial = material[ group.materialIndex ];
- if ( groupMaterial && groupMaterial.visible ) {
- const depthMaterial = getDepthMaterial( object, geometry, groupMaterial, light, shadowCamera.near, shadowCamera.far, type );
- _renderer.renderBufferDirect( shadowCamera, null, geometry, depthMaterial, object, group );
- }
- }
- } else if ( material.visible ) {
- const depthMaterial = getDepthMaterial( object, geometry, material, light, shadowCamera.near, shadowCamera.far, type );
- _renderer.renderBufferDirect( shadowCamera, null, geometry, depthMaterial, object, null );
- }
- }
- }
- const children = object.children;
- for ( let i = 0, l = children.length; i < l; i ++ ) {
- renderObject( children[ i ], camera, shadowCamera, light, type );
- }
- }
- }
- function WebGLState( gl, extensions, capabilities ) {
- const isWebGL2 = capabilities.isWebGL2;
- function ColorBuffer() {
- let locked = false;
- const color = new Vector4();
- let currentColorMask = null;
- const currentColorClear = new Vector4( 0, 0, 0, 0 );
- return {
- setMask: function ( colorMask ) {
- if ( currentColorMask !== colorMask && ! locked ) {
- gl.colorMask( colorMask, colorMask, colorMask, colorMask );
- currentColorMask = colorMask;
- }
- },
- setLocked: function ( lock ) {
- locked = lock;
- },
- setClear: function ( r, g, b, a, premultipliedAlpha ) {
- if ( premultipliedAlpha === true ) {
- r *= a; g *= a; b *= a;
- }
- color.set( r, g, b, a );
- if ( currentColorClear.equals( color ) === false ) {
- gl.clearColor( r, g, b, a );
- currentColorClear.copy( color );
- }
- },
- reset: function () {
- locked = false;
- currentColorMask = null;
- currentColorClear.set( - 1, 0, 0, 0 ); // set to invalid state
- }
- };
- }
- function DepthBuffer() {
- let locked = false;
- let currentDepthMask = null;
- let currentDepthFunc = null;
- let currentDepthClear = null;
- return {
- setTest: function ( depthTest ) {
- if ( depthTest ) {
- enable( 2929 );
- } else {
- disable( 2929 );
- }
- },
- setMask: function ( depthMask ) {
- if ( currentDepthMask !== depthMask && ! locked ) {
- gl.depthMask( depthMask );
- currentDepthMask = depthMask;
- }
- },
- setFunc: function ( depthFunc ) {
- if ( currentDepthFunc !== depthFunc ) {
- if ( depthFunc ) {
- switch ( depthFunc ) {
- case NeverDepth:
- gl.depthFunc( 512 );
- break;
- case AlwaysDepth:
- gl.depthFunc( 519 );
- break;
- case LessDepth:
- gl.depthFunc( 513 );
- break;
- case LessEqualDepth:
- gl.depthFunc( 515 );
- break;
- case EqualDepth:
- gl.depthFunc( 514 );
- break;
- case GreaterEqualDepth:
- gl.depthFunc( 518 );
- break;
- case GreaterDepth:
- gl.depthFunc( 516 );
- break;
- case NotEqualDepth:
- gl.depthFunc( 517 );
- break;
- default:
- gl.depthFunc( 515 );
- }
- } else {
- gl.depthFunc( 515 );
- }
- currentDepthFunc = depthFunc;
- }
- },
- setLocked: function ( lock ) {
- locked = lock;
- },
- setClear: function ( depth ) {
- if ( currentDepthClear !== depth ) {
- gl.clearDepth( depth );
- currentDepthClear = depth;
- }
- },
- reset: function () {
- locked = false;
- currentDepthMask = null;
- currentDepthFunc = null;
- currentDepthClear = null;
- }
- };
- }
- function StencilBuffer() {
- let locked = false;
- let currentStencilMask = null;
- let currentStencilFunc = null;
- let currentStencilRef = null;
- let currentStencilFuncMask = null;
- let currentStencilFail = null;
- let currentStencilZFail = null;
- let currentStencilZPass = null;
- let currentStencilClear = null;
- return {
- setTest: function ( stencilTest ) {
- if ( ! locked ) {
- if ( stencilTest ) {
- enable( 2960 );
- } else {
- disable( 2960 );
- }
- }
- },
- setMask: function ( stencilMask ) {
- if ( currentStencilMask !== stencilMask && ! locked ) {
- gl.stencilMask( stencilMask );
- currentStencilMask = stencilMask;
- }
- },
- setFunc: function ( stencilFunc, stencilRef, stencilMask ) {
- if ( currentStencilFunc !== stencilFunc ||
- currentStencilRef !== stencilRef ||
- currentStencilFuncMask !== stencilMask ) {
- gl.stencilFunc( stencilFunc, stencilRef, stencilMask );
- currentStencilFunc = stencilFunc;
- currentStencilRef = stencilRef;
- currentStencilFuncMask = stencilMask;
- }
- },
- setOp: function ( stencilFail, stencilZFail, stencilZPass ) {
- if ( currentStencilFail !== stencilFail ||
- currentStencilZFail !== stencilZFail ||
- currentStencilZPass !== stencilZPass ) {
- gl.stencilOp( stencilFail, stencilZFail, stencilZPass );
- currentStencilFail = stencilFail;
- currentStencilZFail = stencilZFail;
- currentStencilZPass = stencilZPass;
- }
- },
- setLocked: function ( lock ) {
- locked = lock;
- },
- setClear: function ( stencil ) {
- if ( currentStencilClear !== stencil ) {
- gl.clearStencil( stencil );
- currentStencilClear = stencil;
- }
- },
- reset: function () {
- locked = false;
- currentStencilMask = null;
- currentStencilFunc = null;
- currentStencilRef = null;
- currentStencilFuncMask = null;
- currentStencilFail = null;
- currentStencilZFail = null;
- currentStencilZPass = null;
- currentStencilClear = null;
- }
- };
- }
- //
- const colorBuffer = new ColorBuffer();
- const depthBuffer = new DepthBuffer();
- const stencilBuffer = new StencilBuffer();
- let enabledCapabilities = {};
- let currentProgram = null;
- let currentBlendingEnabled = null;
- let currentBlending = null;
- let currentBlendEquation = null;
- let currentBlendSrc = null;
- let currentBlendDst = null;
- let currentBlendEquationAlpha = null;
- let currentBlendSrcAlpha = null;
- let currentBlendDstAlpha = null;
- let currentPremultipledAlpha = false;
- let currentFlipSided = null;
- let currentCullFace = null;
- let currentlineWidth = null;
- let currentPolygonOffsetFactor = null;
- let currentPolygonOffsetUnits = null;
- const maxTextures = gl.getParameter( 35661 );
- let lineWidthAvailable = false;
- let version = 0;
- const glVersion = gl.getParameter( 7938 );
- if ( glVersion.indexOf( 'WebGL' ) !== - 1 ) {
- version = parseFloat( /^WebGL (\d)/.exec( glVersion )[ 1 ] );
- lineWidthAvailable = ( version >= 1.0 );
- } else if ( glVersion.indexOf( 'OpenGL ES' ) !== - 1 ) {
- version = parseFloat( /^OpenGL ES (\d)/.exec( glVersion )[ 1 ] );
- lineWidthAvailable = ( version >= 2.0 );
- }
- let currentTextureSlot = null;
- let currentBoundTextures = {};
- const currentScissor = new Vector4();
- const currentViewport = new Vector4();
- function createTexture( type, target, count ) {
- const data = new Uint8Array( 4 ); // 4 is required to match default unpack alignment of 4.
- const texture = gl.createTexture();
- gl.bindTexture( type, texture );
- gl.texParameteri( type, 10241, 9728 );
- gl.texParameteri( type, 10240, 9728 );
- for ( let i = 0; i < count; i ++ ) {
- gl.texImage2D( target + i, 0, 6408, 1, 1, 0, 6408, 5121, data );
- }
- return texture;
- }
- const emptyTextures = {};
- emptyTextures[ 3553 ] = createTexture( 3553, 3553, 1 );
- emptyTextures[ 34067 ] = createTexture( 34067, 34069, 6 );
- // init
- colorBuffer.setClear( 0, 0, 0, 1 );
- depthBuffer.setClear( 1 );
- stencilBuffer.setClear( 0 );
- enable( 2929 );
- depthBuffer.setFunc( LessEqualDepth );
- setFlipSided( false );
- setCullFace( CullFaceBack );
- enable( 2884 );
- setBlending( NoBlending );
- //
- function enable( id ) {
- if ( enabledCapabilities[ id ] !== true ) {
- gl.enable( id );
- enabledCapabilities[ id ] = true;
- }
- }
- function disable( id ) {
- if ( enabledCapabilities[ id ] !== false ) {
- gl.disable( id );
- enabledCapabilities[ id ] = false;
- }
- }
- function useProgram( program ) {
- if ( currentProgram !== program ) {
- gl.useProgram( program );
- currentProgram = program;
- return true;
- }
- return false;
- }
- const equationToGL = {
- [ AddEquation ]: 32774,
- [ SubtractEquation ]: 32778,
- [ ReverseSubtractEquation ]: 32779
- };
- if ( isWebGL2 ) {
- equationToGL[ MinEquation ] = 32775;
- equationToGL[ MaxEquation ] = 32776;
- } else {
- const extension = extensions.get( 'EXT_blend_minmax' );
- if ( extension !== null ) {
- equationToGL[ MinEquation ] = extension.MIN_EXT;
- equationToGL[ MaxEquation ] = extension.MAX_EXT;
- }
- }
- const factorToGL = {
- [ ZeroFactor ]: 0,
- [ OneFactor ]: 1,
- [ SrcColorFactor ]: 768,
- [ SrcAlphaFactor ]: 770,
- [ SrcAlphaSaturateFactor ]: 776,
- [ DstColorFactor ]: 774,
- [ DstAlphaFactor ]: 772,
- [ OneMinusSrcColorFactor ]: 769,
- [ OneMinusSrcAlphaFactor ]: 771,
- [ OneMinusDstColorFactor ]: 775,
- [ OneMinusDstAlphaFactor ]: 773
- };
- function setBlending( blending, blendEquation, blendSrc, blendDst, blendEquationAlpha, blendSrcAlpha, blendDstAlpha, premultipliedAlpha ) {
- if ( blending === NoBlending ) {
- if ( currentBlendingEnabled ) {
- disable( 3042 );
- currentBlendingEnabled = false;
- }
- return;
- }
- if ( ! currentBlendingEnabled ) {
- enable( 3042 );
- currentBlendingEnabled = true;
- }
- if ( blending !== CustomBlending ) {
- if ( blending !== currentBlending || premultipliedAlpha !== currentPremultipledAlpha ) {
- if ( currentBlendEquation !== AddEquation || currentBlendEquationAlpha !== AddEquation ) {
- gl.blendEquation( 32774 );
- currentBlendEquation = AddEquation;
- currentBlendEquationAlpha = AddEquation;
- }
- if ( premultipliedAlpha ) {
- switch ( blending ) {
- case NormalBlending:
- gl.blendFuncSeparate( 1, 771, 1, 771 );
- break;
- case AdditiveBlending:
- gl.blendFunc( 1, 1 );
- break;
- case SubtractiveBlending:
- gl.blendFuncSeparate( 0, 0, 769, 771 );
- break;
- case MultiplyBlending:
- gl.blendFuncSeparate( 0, 768, 0, 770 );
- break;
- default:
- console.error( 'THREE.WebGLState: Invalid blending: ', blending );
- break;
- }
- } else {
- switch ( blending ) {
- case NormalBlending:
- gl.blendFuncSeparate( 770, 771, 1, 771 );
- break;
- case AdditiveBlending:
- gl.blendFunc( 770, 1 );
- break;
- case SubtractiveBlending:
- gl.blendFunc( 0, 769 );
- break;
- case MultiplyBlending:
- gl.blendFunc( 0, 768 );
- break;
- default:
- console.error( 'THREE.WebGLState: Invalid blending: ', blending );
- break;
- }
- }
- currentBlendSrc = null;
- currentBlendDst = null;
- currentBlendSrcAlpha = null;
- currentBlendDstAlpha = null;
- currentBlending = blending;
- currentPremultipledAlpha = premultipliedAlpha;
- }
- return;
- }
- // custom blending
- blendEquationAlpha = blendEquationAlpha || blendEquation;
- blendSrcAlpha = blendSrcAlpha || blendSrc;
- blendDstAlpha = blendDstAlpha || blendDst;
- if ( blendEquation !== currentBlendEquation || blendEquationAlpha !== currentBlendEquationAlpha ) {
- gl.blendEquationSeparate( equationToGL[ blendEquation ], equationToGL[ blendEquationAlpha ] );
- currentBlendEquation = blendEquation;
- currentBlendEquationAlpha = blendEquationAlpha;
- }
- if ( blendSrc !== currentBlendSrc || blendDst !== currentBlendDst || blendSrcAlpha !== currentBlendSrcAlpha || blendDstAlpha !== currentBlendDstAlpha ) {
- gl.blendFuncSeparate( factorToGL[ blendSrc ], factorToGL[ blendDst ], factorToGL[ blendSrcAlpha ], factorToGL[ blendDstAlpha ] );
- currentBlendSrc = blendSrc;
- currentBlendDst = blendDst;
- currentBlendSrcAlpha = blendSrcAlpha;
- currentBlendDstAlpha = blendDstAlpha;
- }
- currentBlending = blending;
- currentPremultipledAlpha = null;
- }
- function setMaterial( material, frontFaceCW ) {
- material.side === DoubleSide
- ? disable( 2884 )
- : enable( 2884 );
- let flipSided = ( material.side === BackSide );
- if ( frontFaceCW ) flipSided = ! flipSided;
- setFlipSided( flipSided );
- ( material.blending === NormalBlending && material.transparent === false )
- ? setBlending( NoBlending )
- : setBlending( material.blending, material.blendEquation, material.blendSrc, material.blendDst, material.blendEquationAlpha, material.blendSrcAlpha, material.blendDstAlpha, material.premultipliedAlpha );
- depthBuffer.setFunc( material.depthFunc );
- depthBuffer.setTest( material.depthTest );
- depthBuffer.setMask( material.depthWrite );
- colorBuffer.setMask( material.colorWrite );
- const stencilWrite = material.stencilWrite;
- stencilBuffer.setTest( stencilWrite );
- if ( stencilWrite ) {
- stencilBuffer.setMask( material.stencilWriteMask );
- stencilBuffer.setFunc( material.stencilFunc, material.stencilRef, material.stencilFuncMask );
- stencilBuffer.setOp( material.stencilFail, material.stencilZFail, material.stencilZPass );
- }
- setPolygonOffset( material.polygonOffset, material.polygonOffsetFactor, material.polygonOffsetUnits );
- }
- //
- function setFlipSided( flipSided ) {
- if ( currentFlipSided !== flipSided ) {
- if ( flipSided ) {
- gl.frontFace( 2304 );
- } else {
- gl.frontFace( 2305 );
- }
- currentFlipSided = flipSided;
- }
- }
- function setCullFace( cullFace ) {
- if ( cullFace !== CullFaceNone ) {
- enable( 2884 );
- if ( cullFace !== currentCullFace ) {
- if ( cullFace === CullFaceBack ) {
- gl.cullFace( 1029 );
- } else if ( cullFace === CullFaceFront ) {
- gl.cullFace( 1028 );
- } else {
- gl.cullFace( 1032 );
- }
- }
- } else {
- disable( 2884 );
- }
- currentCullFace = cullFace;
- }
- function setlineWidth( width ) {
- if ( width !== currentlineWidth ) {
- if ( lineWidthAvailable ) gl.lineWidth( width );
- currentlineWidth = width;
- }
- }
- function setPolygonOffset( polygonOffset, factor, units ) {
- if ( polygonOffset ) {
- enable( 32823 );
- if ( currentPolygonOffsetFactor !== factor || currentPolygonOffsetUnits !== units ) {
- gl.polygonOffset( factor, units );
- currentPolygonOffsetFactor = factor;
- currentPolygonOffsetUnits = units;
- }
- } else {
- disable( 32823 );
- }
- }
- function setScissorTest( scissorTest ) {
- if ( scissorTest ) {
- enable( 3089 );
- } else {
- disable( 3089 );
- }
- }
- // texture
- function activeTexture( webglSlot ) {
- if ( webglSlot === undefined ) webglSlot = 33984 + maxTextures - 1;
- if ( currentTextureSlot !== webglSlot ) {
- gl.activeTexture( webglSlot );
- currentTextureSlot = webglSlot;
- }
- }
- function bindTexture( webglType, webglTexture ) {
- if ( currentTextureSlot === null ) {
- activeTexture();
- }
- let boundTexture = currentBoundTextures[ currentTextureSlot ];
- if ( boundTexture === undefined ) {
- boundTexture = { type: undefined, texture: undefined };
- currentBoundTextures[ currentTextureSlot ] = boundTexture;
- }
- if ( boundTexture.type !== webglType || boundTexture.texture !== webglTexture ) {
- gl.bindTexture( webglType, webglTexture || emptyTextures[ webglType ] );
- boundTexture.type = webglType;
- boundTexture.texture = webglTexture;
- }
- }
- function unbindTexture() {
- const boundTexture = currentBoundTextures[ currentTextureSlot ];
- if ( boundTexture !== undefined && boundTexture.type !== undefined ) {
- gl.bindTexture( boundTexture.type, null );
- boundTexture.type = undefined;
- boundTexture.texture = undefined;
- }
- }
- function compressedTexImage2D() {
- try {
- gl.compressedTexImage2D.apply( gl, arguments );
- } catch ( error ) {
- console.error( 'THREE.WebGLState:', error );
- }
- }
- function texImage2D() {
- try {
- gl.texImage2D.apply( gl, arguments );
- } catch ( error ) {
- console.error( 'THREE.WebGLState:', error );
- }
- }
- function texImage3D() {
- try {
- gl.texImage3D.apply( gl, arguments );
- } catch ( error ) {
- console.error( 'THREE.WebGLState:', error );
- }
- }
- //
- function scissor( scissor ) {
- if ( currentScissor.equals( scissor ) === false ) {
- gl.scissor( scissor.x, scissor.y, scissor.z, scissor.w );
- currentScissor.copy( scissor );
- }
- }
- function viewport( viewport ) {
- if ( currentViewport.equals( viewport ) === false ) {
- gl.viewport( viewport.x, viewport.y, viewport.z, viewport.w );
- currentViewport.copy( viewport );
- }
- }
- //
- function reset() {
- enabledCapabilities = {};
- currentTextureSlot = null;
- currentBoundTextures = {};
- currentProgram = null;
- currentBlendingEnabled = null;
- currentBlending = null;
- currentBlendEquation = null;
- currentBlendSrc = null;
- currentBlendDst = null;
- currentBlendEquationAlpha = null;
- currentBlendSrcAlpha = null;
- currentBlendDstAlpha = null;
- currentPremultipledAlpha = false;
- currentFlipSided = null;
- currentCullFace = null;
- currentlineWidth = null;
- currentPolygonOffsetFactor = null;
- currentPolygonOffsetUnits = null;
- colorBuffer.reset();
- depthBuffer.reset();
- stencilBuffer.reset();
- }
- return {
- buffers: {
- color: colorBuffer,
- depth: depthBuffer,
- stencil: stencilBuffer
- },
- enable: enable,
- disable: disable,
- useProgram: useProgram,
- setBlending: setBlending,
- setMaterial: setMaterial,
- setFlipSided: setFlipSided,
- setCullFace: setCullFace,
- setlineWidth: setlineWidth,
- setPolygonOffset: setPolygonOffset,
- setScissorTest: setScissorTest,
- activeTexture: activeTexture,
- bindTexture: bindTexture,
- unbindTexture: unbindTexture,
- compressedTexImage2D: compressedTexImage2D,
- texImage2D: texImage2D,
- texImage3D: texImage3D,
- scissor: scissor,
- viewport: viewport,
- reset: reset
- };
- }
- function WebGLTextures( _gl, extensions, state, properties, capabilities, utils, info ) {
- const isWebGL2 = capabilities.isWebGL2;
- const maxTextures = capabilities.maxTextures;
- const maxCubemapSize = capabilities.maxCubemapSize;
- const maxTextureSize = capabilities.maxTextureSize;
- const maxSamples = capabilities.maxSamples;
- const _videoTextures = new WeakMap();
- let _canvas;
- // cordova iOS (as of 5.0) still uses UIWebView, which provides OffscreenCanvas,
- // also OffscreenCanvas.getContext("webgl"), but not OffscreenCanvas.getContext("2d")!
- // Some implementations may only implement OffscreenCanvas partially (e.g. lacking 2d).
- let useOffscreenCanvas = false;
- try {
- useOffscreenCanvas = typeof OffscreenCanvas !== 'undefined'
- && ( new OffscreenCanvas( 1, 1 ).getContext( '2d' ) ) !== null;
- } catch ( err ) {
- // Ignore any errors
- }
- function createCanvas( width, height ) {
- // Use OffscreenCanvas when available. Specially needed in web workers
- return useOffscreenCanvas ?
- new OffscreenCanvas( width, height ) :
- document.createElementNS( 'http://www.w3.org/1999/xhtml', 'canvas' );
- }
- function resizeImage( image, needsPowerOfTwo, needsNewCanvas, maxSize ) {
- let scale = 1;
- // handle case if texture exceeds max size
- if ( image.width > maxSize || image.height > maxSize ) {
- scale = maxSize / Math.max( image.width, image.height );
- }
- // only perform resize if necessary
- if ( scale < 1 || needsPowerOfTwo === true ) {
- // only perform resize for certain image types
- if ( ( typeof HTMLImageElement !== 'undefined' && image instanceof HTMLImageElement ) ||
- ( typeof HTMLCanvasElement !== 'undefined' && image instanceof HTMLCanvasElement ) ||
- ( typeof ImageBitmap !== 'undefined' && image instanceof ImageBitmap ) ) {
- const floor = needsPowerOfTwo ? MathUtils.floorPowerOfTwo : Math.floor;
- const width = floor( scale * image.width );
- const height = floor( scale * image.height );
- if ( _canvas === undefined ) _canvas = createCanvas( width, height );
- // cube textures can't reuse the same canvas
- const canvas = needsNewCanvas ? createCanvas( width, height ) : _canvas;
- canvas.width = width;
- canvas.height = height;
- const context = canvas.getContext( '2d' );
- context.drawImage( image, 0, 0, width, height );
- console.warn( 'THREE.WebGLRenderer: Texture has been resized from (' + image.width + 'x' + image.height + ') to (' + width + 'x' + height + ').' );
- return canvas;
- } else {
- if ( 'data' in image ) {
- console.warn( 'THREE.WebGLRenderer: Image in DataTexture is too big (' + image.width + 'x' + image.height + ').' );
- }
- return image;
- }
- }
- return image;
- }
- function isPowerOfTwo( image ) {
- return MathUtils.isPowerOfTwo( image.width ) && MathUtils.isPowerOfTwo( image.height );
- }
- function textureNeedsPowerOfTwo( texture ) {
- if ( isWebGL2 ) return false;
- return ( texture.wrapS !== ClampToEdgeWrapping || texture.wrapT !== ClampToEdgeWrapping ) ||
- ( texture.minFilter !== NearestFilter && texture.minFilter !== LinearFilter );
- }
- function textureNeedsGenerateMipmaps( texture, supportsMips ) {
- return texture.generateMipmaps && supportsMips &&
- texture.minFilter !== NearestFilter && texture.minFilter !== LinearFilter;
- }
- function generateMipmap( target, texture, width, height ) {
- _gl.generateMipmap( target );
- const textureProperties = properties.get( texture );
- // Note: Math.log( x ) * Math.LOG2E used instead of Math.log2( x ) which is not supported by IE11
- textureProperties.__maxMipLevel = Math.log( Math.max( width, height ) ) * Math.LOG2E;
- }
- function getInternalFormat( internalFormatName, glFormat, glType ) {
- if ( isWebGL2 === false ) return glFormat;
- if ( internalFormatName !== null ) {
- if ( _gl[ internalFormatName ] !== undefined ) return _gl[ internalFormatName ];
- console.warn( 'THREE.WebGLRenderer: Attempt to use non-existing WebGL internal format \'' + internalFormatName + '\'' );
- }
- let internalFormat = glFormat;
- if ( glFormat === 6403 ) {
- if ( glType === 5126 ) internalFormat = 33326;
- if ( glType === 5131 ) internalFormat = 33325;
- if ( glType === 5121 ) internalFormat = 33321;
- }
- if ( glFormat === 6407 ) {
- if ( glType === 5126 ) internalFormat = 34837;
- if ( glType === 5131 ) internalFormat = 34843;
- if ( glType === 5121 ) internalFormat = 32849;
- }
- if ( glFormat === 6408 ) {
- if ( glType === 5126 ) internalFormat = 34836;
- if ( glType === 5131 ) internalFormat = 34842;
- if ( glType === 5121 ) internalFormat = 32856;
- }
- if ( internalFormat === 33325 || internalFormat === 33326 ||
- internalFormat === 34842 || internalFormat === 34836 ) {
- extensions.get( 'EXT_color_buffer_float' );
- }
- return internalFormat;
- }
- // Fallback filters for non-power-of-2 textures
- function filterFallback( f ) {
- if ( f === NearestFilter || f === NearestMipmapNearestFilter || f === NearestMipmapLinearFilter ) {
- return 9728;
- }
- return 9729;
- }
- //
- function onTextureDispose( event ) {
- const texture = event.target;
- texture.removeEventListener( 'dispose', onTextureDispose );
- deallocateTexture( texture );
- if ( texture.isVideoTexture ) {
- _videoTextures.delete( texture );
- }
- info.memory.textures --;
- }
- function onRenderTargetDispose( event ) {
- const renderTarget = event.target;
- renderTarget.removeEventListener( 'dispose', onRenderTargetDispose );
- deallocateRenderTarget( renderTarget );
- info.memory.textures --;
- }
- //
- function deallocateTexture( texture ) {
- const textureProperties = properties.get( texture );
- if ( textureProperties.__webglInit === undefined ) return;
- _gl.deleteTexture( textureProperties.__webglTexture );
- properties.remove( texture );
- }
- function deallocateRenderTarget( renderTarget ) {
- const renderTargetProperties = properties.get( renderTarget );
- const textureProperties = properties.get( renderTarget.texture );
- if ( ! renderTarget ) return;
- if ( textureProperties.__webglTexture !== undefined ) {
- _gl.deleteTexture( textureProperties.__webglTexture );
- }
- if ( renderTarget.depthTexture ) {
- renderTarget.depthTexture.dispose();
- }
- if ( renderTarget.isWebGLCubeRenderTarget ) {
- for ( let i = 0; i < 6; i ++ ) {
- _gl.deleteFramebuffer( renderTargetProperties.__webglFramebuffer[ i ] );
- if ( renderTargetProperties.__webglDepthbuffer ) _gl.deleteRenderbuffer( renderTargetProperties.__webglDepthbuffer[ i ] );
- }
- } else {
- _gl.deleteFramebuffer( renderTargetProperties.__webglFramebuffer );
- if ( renderTargetProperties.__webglDepthbuffer ) _gl.deleteRenderbuffer( renderTargetProperties.__webglDepthbuffer );
- if ( renderTargetProperties.__webglMultisampledFramebuffer ) _gl.deleteFramebuffer( renderTargetProperties.__webglMultisampledFramebuffer );
- if ( renderTargetProperties.__webglColorRenderbuffer ) _gl.deleteRenderbuffer( renderTargetProperties.__webglColorRenderbuffer );
- if ( renderTargetProperties.__webglDepthRenderbuffer ) _gl.deleteRenderbuffer( renderTargetProperties.__webglDepthRenderbuffer );
- }
- properties.remove( renderTarget.texture );
- properties.remove( renderTarget );
- }
- //
- let textureUnits = 0;
- function resetTextureUnits() {
- textureUnits = 0;
- }
- function allocateTextureUnit() {
- const textureUnit = textureUnits;
- if ( textureUnit >= maxTextures ) {
- console.warn( 'THREE.WebGLTextures: Trying to use ' + textureUnit + ' texture units while this GPU supports only ' + maxTextures );
- }
- textureUnits += 1;
- return textureUnit;
- }
- //
- function setTexture2D( texture, slot ) {
- const textureProperties = properties.get( texture );
- if ( texture.isVideoTexture ) updateVideoTexture( texture );
- if ( texture.version > 0 && textureProperties.__version !== texture.version ) {
- const image = texture.image;
- if ( image === undefined ) {
- console.warn( 'THREE.WebGLRenderer: Texture marked for update but image is undefined' );
- } else if ( image.complete === false ) {
- console.warn( 'THREE.WebGLRenderer: Texture marked for update but image is incomplete' );
- } else {
- uploadTexture( textureProperties, texture, slot );
- return;
- }
- }
- state.activeTexture( 33984 + slot );
- state.bindTexture( 3553, textureProperties.__webglTexture );
- }
- function setTexture2DArray( texture, slot ) {
- const textureProperties = properties.get( texture );
- if ( texture.version > 0 && textureProperties.__version !== texture.version ) {
- uploadTexture( textureProperties, texture, slot );
- return;
- }
- state.activeTexture( 33984 + slot );
- state.bindTexture( 35866, textureProperties.__webglTexture );
- }
- function setTexture3D( texture, slot ) {
- const textureProperties = properties.get( texture );
- if ( texture.version > 0 && textureProperties.__version !== texture.version ) {
- uploadTexture( textureProperties, texture, slot );
- return;
- }
- state.activeTexture( 33984 + slot );
- state.bindTexture( 32879, textureProperties.__webglTexture );
- }
- function setTextureCube( texture, slot ) {
- const textureProperties = properties.get( texture );
- if ( texture.version > 0 && textureProperties.__version !== texture.version ) {
- uploadCubeTexture( textureProperties, texture, slot );
- return;
- }
- state.activeTexture( 33984 + slot );
- state.bindTexture( 34067, textureProperties.__webglTexture );
- }
- const wrappingToGL = {
- [ RepeatWrapping ]: 10497,
- [ ClampToEdgeWrapping ]: 33071,
- [ MirroredRepeatWrapping ]: 33648
- };
- const filterToGL = {
- [ NearestFilter ]: 9728,
- [ NearestMipmapNearestFilter ]: 9984,
- [ NearestMipmapLinearFilter ]: 9986,
- [ LinearFilter ]: 9729,
- [ LinearMipmapNearestFilter ]: 9985,
- [ LinearMipmapLinearFilter ]: 9987
- };
- function setTextureParameters( textureType, texture, supportsMips ) {
- if ( supportsMips ) {
- _gl.texParameteri( textureType, 10242, wrappingToGL[ texture.wrapS ] );
- _gl.texParameteri( textureType, 10243, wrappingToGL[ texture.wrapT ] );
- if ( textureType === 32879 || textureType === 35866 ) {
- _gl.texParameteri( textureType, 32882, wrappingToGL[ texture.wrapR ] );
- }
- _gl.texParameteri( textureType, 10240, filterToGL[ texture.magFilter ] );
- _gl.texParameteri( textureType, 10241, filterToGL[ texture.minFilter ] );
- } else {
- _gl.texParameteri( textureType, 10242, 33071 );
- _gl.texParameteri( textureType, 10243, 33071 );
- if ( textureType === 32879 || textureType === 35866 ) {
- _gl.texParameteri( textureType, 32882, 33071 );
- }
- if ( texture.wrapS !== ClampToEdgeWrapping || texture.wrapT !== ClampToEdgeWrapping ) {
- console.warn( 'THREE.WebGLRenderer: Texture is not power of two. Texture.wrapS and Texture.wrapT should be set to THREE.ClampToEdgeWrapping.' );
- }
- _gl.texParameteri( textureType, 10240, filterFallback( texture.magFilter ) );
- _gl.texParameteri( textureType, 10241, filterFallback( texture.minFilter ) );
- if ( texture.minFilter !== NearestFilter && texture.minFilter !== LinearFilter ) {
- console.warn( 'THREE.WebGLRenderer: Texture is not power of two. Texture.minFilter should be set to THREE.NearestFilter or THREE.LinearFilter.' );
- }
- }
- const extension = extensions.get( 'EXT_texture_filter_anisotropic' );
- if ( extension ) {
- if ( texture.type === FloatType && extensions.get( 'OES_texture_float_linear' ) === null ) return;
- if ( texture.type === HalfFloatType && ( isWebGL2 || extensions.get( 'OES_texture_half_float_linear' ) ) === null ) return;
- if ( texture.anisotropy > 1 || properties.get( texture ).__currentAnisotropy ) {
- _gl.texParameterf( textureType, extension.TEXTURE_MAX_ANISOTROPY_EXT, Math.min( texture.anisotropy, capabilities.getMaxAnisotropy() ) );
- properties.get( texture ).__currentAnisotropy = texture.anisotropy;
- }
- }
- }
- function initTexture( textureProperties, texture ) {
- if ( textureProperties.__webglInit === undefined ) {
- textureProperties.__webglInit = true;
- texture.addEventListener( 'dispose', onTextureDispose );
- textureProperties.__webglTexture = _gl.createTexture();
- info.memory.textures ++;
- }
- }
- function uploadTexture( textureProperties, texture, slot ) {
- let textureType = 3553;
- if ( texture.isDataTexture2DArray ) textureType = 35866;
- if ( texture.isDataTexture3D ) textureType = 32879;
- initTexture( textureProperties, texture );
- state.activeTexture( 33984 + slot );
- state.bindTexture( textureType, textureProperties.__webglTexture );
- _gl.pixelStorei( 37440, texture.flipY );
- _gl.pixelStorei( 37441, texture.premultiplyAlpha );
- _gl.pixelStorei( 3317, texture.unpackAlignment );
- const needsPowerOfTwo = textureNeedsPowerOfTwo( texture ) && isPowerOfTwo( texture.image ) === false;
- const image = resizeImage( texture.image, needsPowerOfTwo, false, maxTextureSize );
- const supportsMips = isPowerOfTwo( image ) || isWebGL2,
- glFormat = utils.convert( texture.format );
- let glType = utils.convert( texture.type ),
- glInternalFormat = getInternalFormat( texture.internalFormat, glFormat, glType );
- setTextureParameters( textureType, texture, supportsMips );
- let mipmap;
- const mipmaps = texture.mipmaps;
- if ( texture.isDepthTexture ) {
- // populate depth texture with dummy data
- glInternalFormat = 6402;
- if ( isWebGL2 ) {
- if ( texture.type === FloatType ) {
- glInternalFormat = 36012;
- } else if ( texture.type === UnsignedIntType ) {
- glInternalFormat = 33190;
- } else if ( texture.type === UnsignedInt248Type$1 ) {
- glInternalFormat = 35056;
- } else {
- glInternalFormat = 33189; // WebGL2 requires sized internalformat for glTexImage2D
- }
- } else {
- if ( texture.type === FloatType ) {
- console.error( 'WebGLRenderer: Floating point depth texture requires WebGL2.' );
- }
- }
- // validation checks for WebGL 1
- if ( texture.format === DepthFormat && glInternalFormat === 6402 ) {
- // The error INVALID_OPERATION is generated by texImage2D if format and internalformat are
- // DEPTH_COMPONENT and type is not UNSIGNED_SHORT or UNSIGNED_INT
- // (https://www.khronos.org/registry/webgl/extensions/WEBGL_depth_texture/)
- if ( texture.type !== UnsignedShortType && texture.type !== UnsignedIntType ) {
- console.warn( 'THREE.WebGLRenderer: Use UnsignedShortType or UnsignedIntType for DepthFormat DepthTexture.' );
- texture.type = UnsignedShortType;
- glType = utils.convert( texture.type );
- }
- }
- if ( texture.format === DepthStencilFormat && glInternalFormat === 6402 ) {
- // Depth stencil textures need the DEPTH_STENCIL internal format
- // (https://www.khronos.org/registry/webgl/extensions/WEBGL_depth_texture/)
- glInternalFormat = 34041;
- // The error INVALID_OPERATION is generated by texImage2D if format and internalformat are
- // DEPTH_STENCIL and type is not UNSIGNED_INT_24_8_WEBGL.
- // (https://www.khronos.org/registry/webgl/extensions/WEBGL_depth_texture/)
- if ( texture.type !== UnsignedInt248Type$1 ) {
- console.warn( 'THREE.WebGLRenderer: Use UnsignedInt248Type for DepthStencilFormat DepthTexture.' );
- texture.type = UnsignedInt248Type$1;
- glType = utils.convert( texture.type );
- }
- }
- //
- state.texImage2D( 3553, 0, glInternalFormat, image.width, image.height, 0, glFormat, glType, null );
- } else if ( texture.isDataTexture ) {
- // use manually created mipmaps if available
- // if there are no manual mipmaps
- // set 0 level mipmap and then use GL to generate other mipmap levels
- if ( mipmaps.length > 0 && supportsMips ) {
- for ( let i = 0, il = mipmaps.length; i < il; i ++ ) {
- mipmap = mipmaps[ i ];
- state.texImage2D( 3553, i, glInternalFormat, mipmap.width, mipmap.height, 0, glFormat, glType, mipmap.data );
- }
- texture.generateMipmaps = false;
- textureProperties.__maxMipLevel = mipmaps.length - 1;
- } else {
- state.texImage2D( 3553, 0, glInternalFormat, image.width, image.height, 0, glFormat, glType, image.data );
- textureProperties.__maxMipLevel = 0;
- }
- } else if ( texture.isCompressedTexture ) {
- for ( let i = 0, il = mipmaps.length; i < il; i ++ ) {
- mipmap = mipmaps[ i ];
- if ( texture.format !== RGBAFormat && texture.format !== RGBFormat ) {
- if ( glFormat !== null ) {
- state.compressedTexImage2D( 3553, i, glInternalFormat, mipmap.width, mipmap.height, 0, mipmap.data );
- } else {
- console.warn( 'THREE.WebGLRenderer: Attempt to load unsupported compressed texture format in .uploadTexture()' );
- }
- } else {
- state.texImage2D( 3553, i, glInternalFormat, mipmap.width, mipmap.height, 0, glFormat, glType, mipmap.data );
- }
- }
- textureProperties.__maxMipLevel = mipmaps.length - 1;
- } else if ( texture.isDataTexture2DArray ) {
- state.texImage3D( 35866, 0, glInternalFormat, image.width, image.height, image.depth, 0, glFormat, glType, image.data );
- textureProperties.__maxMipLevel = 0;
- } else if ( texture.isDataTexture3D ) {
- state.texImage3D( 32879, 0, glInternalFormat, image.width, image.height, image.depth, 0, glFormat, glType, image.data );
- textureProperties.__maxMipLevel = 0;
- } else {
- // regular Texture (image, video, canvas)
- // use manually created mipmaps if available
- // if there are no manual mipmaps
- // set 0 level mipmap and then use GL to generate other mipmap levels
- if ( mipmaps.length > 0 && supportsMips ) {
- for ( let i = 0, il = mipmaps.length; i < il; i ++ ) {
- mipmap = mipmaps[ i ];
- state.texImage2D( 3553, i, glInternalFormat, glFormat, glType, mipmap );
- }
- texture.generateMipmaps = false;
- textureProperties.__maxMipLevel = mipmaps.length - 1;
- } else {
- state.texImage2D( 3553, 0, glInternalFormat, glFormat, glType, image );
- textureProperties.__maxMipLevel = 0;
- }
- }
- if ( textureNeedsGenerateMipmaps( texture, supportsMips ) ) {
- generateMipmap( textureType, texture, image.width, image.height );
- }
- textureProperties.__version = texture.version;
- if ( texture.onUpdate ) texture.onUpdate( texture );
- }
- function uploadCubeTexture( textureProperties, texture, slot ) {
- if ( texture.image.length !== 6 ) return;
- initTexture( textureProperties, texture );
- state.activeTexture( 33984 + slot );
- state.bindTexture( 34067, textureProperties.__webglTexture );
- _gl.pixelStorei( 37440, texture.flipY );
- const isCompressed = ( texture && ( texture.isCompressedTexture || texture.image[ 0 ].isCompressedTexture ) );
- const isDataTexture = ( texture.image[ 0 ] && texture.image[ 0 ].isDataTexture );
- const cubeImage = [];
- for ( let i = 0; i < 6; i ++ ) {
- if ( ! isCompressed && ! isDataTexture ) {
- cubeImage[ i ] = resizeImage( texture.image[ i ], false, true, maxCubemapSize );
- } else {
- cubeImage[ i ] = isDataTexture ? texture.image[ i ].image : texture.image[ i ];
- }
- }
- const image = cubeImage[ 0 ],
- supportsMips = isPowerOfTwo( image ) || isWebGL2,
- glFormat = utils.convert( texture.format ),
- glType = utils.convert( texture.type ),
- glInternalFormat = getInternalFormat( texture.internalFormat, glFormat, glType );
- setTextureParameters( 34067, texture, supportsMips );
- let mipmaps;
- if ( isCompressed ) {
- for ( let i = 0; i < 6; i ++ ) {
- mipmaps = cubeImage[ i ].mipmaps;
- for ( let j = 0; j < mipmaps.length; j ++ ) {
- const mipmap = mipmaps[ j ];
- if ( texture.format !== RGBAFormat && texture.format !== RGBFormat ) {
- if ( glFormat !== null ) {
- state.compressedTexImage2D( 34069 + i, j, glInternalFormat, mipmap.width, mipmap.height, 0, mipmap.data );
- } else {
- console.warn( 'THREE.WebGLRenderer: Attempt to load unsupported compressed texture format in .setTextureCube()' );
- }
- } else {
- state.texImage2D( 34069 + i, j, glInternalFormat, mipmap.width, mipmap.height, 0, glFormat, glType, mipmap.data );
- }
- }
- }
- textureProperties.__maxMipLevel = mipmaps.length - 1;
- } else {
- mipmaps = texture.mipmaps;
- for ( let i = 0; i < 6; i ++ ) {
- if ( isDataTexture ) {
- state.texImage2D( 34069 + i, 0, glInternalFormat, cubeImage[ i ].width, cubeImage[ i ].height, 0, glFormat, glType, cubeImage[ i ].data );
- for ( let j = 0; j < mipmaps.length; j ++ ) {
- const mipmap = mipmaps[ j ];
- const mipmapImage = mipmap.image[ i ].image;
- state.texImage2D( 34069 + i, j + 1, glInternalFormat, mipmapImage.width, mipmapImage.height, 0, glFormat, glType, mipmapImage.data );
- }
- } else {
- state.texImage2D( 34069 + i, 0, glInternalFormat, glFormat, glType, cubeImage[ i ] );
- for ( let j = 0; j < mipmaps.length; j ++ ) {
- const mipmap = mipmaps[ j ];
- state.texImage2D( 34069 + i, j + 1, glInternalFormat, glFormat, glType, mipmap.image[ i ] );
- }
- }
- }
- textureProperties.__maxMipLevel = mipmaps.length;
- }
- if ( textureNeedsGenerateMipmaps( texture, supportsMips ) ) {
- // We assume images for cube map have the same size.
- generateMipmap( 34067, texture, image.width, image.height );
- }
- textureProperties.__version = texture.version;
- if ( texture.onUpdate ) texture.onUpdate( texture );
- }
- // Render targets
- // Setup storage for target texture and bind it to correct framebuffer
- function setupFrameBufferTexture( framebuffer, renderTarget, attachment, textureTarget ) {
- const glFormat = utils.convert( renderTarget.texture.format );
- const glType = utils.convert( renderTarget.texture.type );
- const glInternalFormat = getInternalFormat( renderTarget.texture.internalFormat, glFormat, glType );
- state.texImage2D( textureTarget, 0, glInternalFormat, renderTarget.width, renderTarget.height, 0, glFormat, glType, null );
- _gl.bindFramebuffer( 36160, framebuffer );
- _gl.framebufferTexture2D( 36160, attachment, textureTarget, properties.get( renderTarget.texture ).__webglTexture, 0 );
- _gl.bindFramebuffer( 36160, null );
- }
- // Setup storage for internal depth/stencil buffers and bind to correct framebuffer
- function setupRenderBufferStorage( renderbuffer, renderTarget, isMultisample ) {
- _gl.bindRenderbuffer( 36161, renderbuffer );
- if ( renderTarget.depthBuffer && ! renderTarget.stencilBuffer ) {
- let glInternalFormat = 33189;
- if ( isMultisample ) {
- const depthTexture = renderTarget.depthTexture;
- if ( depthTexture && depthTexture.isDepthTexture ) {
- if ( depthTexture.type === FloatType ) {
- glInternalFormat = 36012;
- } else if ( depthTexture.type === UnsignedIntType ) {
- glInternalFormat = 33190;
- }
- }
- const samples = getRenderTargetSamples( renderTarget );
- _gl.renderbufferStorageMultisample( 36161, samples, glInternalFormat, renderTarget.width, renderTarget.height );
- } else {
- _gl.renderbufferStorage( 36161, glInternalFormat, renderTarget.width, renderTarget.height );
- }
- _gl.framebufferRenderbuffer( 36160, 36096, 36161, renderbuffer );
- } else if ( renderTarget.depthBuffer && renderTarget.stencilBuffer ) {
- if ( isMultisample ) {
- const samples = getRenderTargetSamples( renderTarget );
- _gl.renderbufferStorageMultisample( 36161, samples, 35056, renderTarget.width, renderTarget.height );
- } else {
- _gl.renderbufferStorage( 36161, 34041, renderTarget.width, renderTarget.height );
- }
- _gl.framebufferRenderbuffer( 36160, 33306, 36161, renderbuffer );
- } else {
- const glFormat = utils.convert( renderTarget.texture.format );
- const glType = utils.convert( renderTarget.texture.type );
- const glInternalFormat = getInternalFormat( renderTarget.texture.internalFormat, glFormat, glType );
- if ( isMultisample ) {
- const samples = getRenderTargetSamples( renderTarget );
- _gl.renderbufferStorageMultisample( 36161, samples, glInternalFormat, renderTarget.width, renderTarget.height );
- } else {
- _gl.renderbufferStorage( 36161, glInternalFormat, renderTarget.width, renderTarget.height );
- }
- }
- _gl.bindRenderbuffer( 36161, null );
- }
- // Setup resources for a Depth Texture for a FBO (needs an extension)
- function setupDepthTexture( framebuffer, renderTarget ) {
- const isCube = ( renderTarget && renderTarget.isWebGLCubeRenderTarget );
- if ( isCube ) throw new Error( 'Depth Texture with cube render targets is not supported' );
- _gl.bindFramebuffer( 36160, framebuffer );
- if ( ! ( renderTarget.depthTexture && renderTarget.depthTexture.isDepthTexture ) ) {
- throw new Error( 'renderTarget.depthTexture must be an instance of THREE.DepthTexture' );
- }
- // upload an empty depth texture with framebuffer size
- if ( ! properties.get( renderTarget.depthTexture ).__webglTexture ||
- renderTarget.depthTexture.image.width !== renderTarget.width ||
- renderTarget.depthTexture.image.height !== renderTarget.height ) {
- renderTarget.depthTexture.image.width = renderTarget.width;
- renderTarget.depthTexture.image.height = renderTarget.height;
- renderTarget.depthTexture.needsUpdate = true;
- }
- setTexture2D( renderTarget.depthTexture, 0 );
- const webglDepthTexture = properties.get( renderTarget.depthTexture ).__webglTexture;
- if ( renderTarget.depthTexture.format === DepthFormat ) {
- _gl.framebufferTexture2D( 36160, 36096, 3553, webglDepthTexture, 0 );
- } else if ( renderTarget.depthTexture.format === DepthStencilFormat ) {
- _gl.framebufferTexture2D( 36160, 33306, 3553, webglDepthTexture, 0 );
- } else {
- throw new Error( 'Unknown depthTexture format' );
- }
- }
- // Setup GL resources for a non-texture depth buffer
- function setupDepthRenderbuffer( renderTarget ) {
- const renderTargetProperties = properties.get( renderTarget );
- const isCube = ( renderTarget.isWebGLCubeRenderTarget === true );
- if ( renderTarget.depthTexture ) {
- if ( isCube ) throw new Error( 'target.depthTexture not supported in Cube render targets' );
- setupDepthTexture( renderTargetProperties.__webglFramebuffer, renderTarget );
- } else {
- if ( isCube ) {
- renderTargetProperties.__webglDepthbuffer = [];
- for ( let i = 0; i < 6; i ++ ) {
- _gl.bindFramebuffer( 36160, renderTargetProperties.__webglFramebuffer[ i ] );
- renderTargetProperties.__webglDepthbuffer[ i ] = _gl.createRenderbuffer();
- setupRenderBufferStorage( renderTargetProperties.__webglDepthbuffer[ i ], renderTarget, false );
- }
- } else {
- _gl.bindFramebuffer( 36160, renderTargetProperties.__webglFramebuffer );
- renderTargetProperties.__webglDepthbuffer = _gl.createRenderbuffer();
- setupRenderBufferStorage( renderTargetProperties.__webglDepthbuffer, renderTarget, false );
- }
- }
- _gl.bindFramebuffer( 36160, null );
- }
- // Set up GL resources for the render target
- function setupRenderTarget( renderTarget ) {
- const renderTargetProperties = properties.get( renderTarget );
- const textureProperties = properties.get( renderTarget.texture );
- renderTarget.addEventListener( 'dispose', onRenderTargetDispose );
- textureProperties.__webglTexture = _gl.createTexture();
- info.memory.textures ++;
- const isCube = ( renderTarget.isWebGLCubeRenderTarget === true );
- const isMultisample = ( renderTarget.isWebGLMultisampleRenderTarget === true );
- const supportsMips = isPowerOfTwo( renderTarget ) || isWebGL2;
- // Handles WebGL2 RGBFormat fallback - #18858
- if ( isWebGL2 && renderTarget.texture.format === RGBFormat && ( renderTarget.texture.type === FloatType || renderTarget.texture.type === HalfFloatType ) ) {
- renderTarget.texture.format = RGBAFormat;
- console.warn( 'THREE.WebGLRenderer: Rendering to textures with RGB format is not supported. Using RGBA format instead.' );
- }
- // Setup framebuffer
- if ( isCube ) {
- renderTargetProperties.__webglFramebuffer = [];
- for ( let i = 0; i < 6; i ++ ) {
- renderTargetProperties.__webglFramebuffer[ i ] = _gl.createFramebuffer();
- }
- } else {
- renderTargetProperties.__webglFramebuffer = _gl.createFramebuffer();
- if ( isMultisample ) {
- if ( isWebGL2 ) {
- renderTargetProperties.__webglMultisampledFramebuffer = _gl.createFramebuffer();
- renderTargetProperties.__webglColorRenderbuffer = _gl.createRenderbuffer();
- _gl.bindRenderbuffer( 36161, renderTargetProperties.__webglColorRenderbuffer );
- const glFormat = utils.convert( renderTarget.texture.format );
- const glType = utils.convert( renderTarget.texture.type );
- const glInternalFormat = getInternalFormat( renderTarget.texture.internalFormat, glFormat, glType );
- const samples = getRenderTargetSamples( renderTarget );
- _gl.renderbufferStorageMultisample( 36161, samples, glInternalFormat, renderTarget.width, renderTarget.height );
- _gl.bindFramebuffer( 36160, renderTargetProperties.__webglMultisampledFramebuffer );
- _gl.framebufferRenderbuffer( 36160, 36064, 36161, renderTargetProperties.__webglColorRenderbuffer );
- _gl.bindRenderbuffer( 36161, null );
- if ( renderTarget.depthBuffer ) {
- renderTargetProperties.__webglDepthRenderbuffer = _gl.createRenderbuffer();
- setupRenderBufferStorage( renderTargetProperties.__webglDepthRenderbuffer, renderTarget, true );
- }
- _gl.bindFramebuffer( 36160, null );
- } else {
- console.warn( 'THREE.WebGLRenderer: WebGLMultisampleRenderTarget can only be used with WebGL2.' );
- }
- }
- }
- // Setup color buffer
- if ( isCube ) {
- state.bindTexture( 34067, textureProperties.__webglTexture );
- setTextureParameters( 34067, renderTarget.texture, supportsMips );
- for ( let i = 0; i < 6; i ++ ) {
- setupFrameBufferTexture( renderTargetProperties.__webglFramebuffer[ i ], renderTarget, 36064, 34069 + i );
- }
- if ( textureNeedsGenerateMipmaps( renderTarget.texture, supportsMips ) ) {
- generateMipmap( 34067, renderTarget.texture, renderTarget.width, renderTarget.height );
- }
- state.bindTexture( 34067, null );
- } else {
- state.bindTexture( 3553, textureProperties.__webglTexture );
- setTextureParameters( 3553, renderTarget.texture, supportsMips );
- setupFrameBufferTexture( renderTargetProperties.__webglFramebuffer, renderTarget, 36064, 3553 );
- if ( textureNeedsGenerateMipmaps( renderTarget.texture, supportsMips ) ) {
- generateMipmap( 3553, renderTarget.texture, renderTarget.width, renderTarget.height );
- }
- state.bindTexture( 3553, null );
- }
- // Setup depth and stencil buffers
- if ( renderTarget.depthBuffer ) {
- setupDepthRenderbuffer( renderTarget );
- }
- }
- function updateRenderTargetMipmap( renderTarget ) {
- const texture = renderTarget.texture;
- const supportsMips = isPowerOfTwo( renderTarget ) || isWebGL2;
- if ( textureNeedsGenerateMipmaps( texture, supportsMips ) ) {
- const target = renderTarget.isWebGLCubeRenderTarget ? 34067 : 3553;
- const webglTexture = properties.get( texture ).__webglTexture;
- state.bindTexture( target, webglTexture );
- generateMipmap( target, texture, renderTarget.width, renderTarget.height );
- state.bindTexture( target, null );
- }
- }
- function updateMultisampleRenderTarget( renderTarget ) {
- if ( renderTarget.isWebGLMultisampleRenderTarget ) {
- if ( isWebGL2 ) {
- const renderTargetProperties = properties.get( renderTarget );
- _gl.bindFramebuffer( 36008, renderTargetProperties.__webglMultisampledFramebuffer );
- _gl.bindFramebuffer( 36009, renderTargetProperties.__webglFramebuffer );
- const width = renderTarget.width;
- const height = renderTarget.height;
- let mask = 16384;
- if ( renderTarget.depthBuffer ) mask |= 256;
- if ( renderTarget.stencilBuffer ) mask |= 1024;
- _gl.blitFramebuffer( 0, 0, width, height, 0, 0, width, height, mask, 9728 );
- _gl.bindFramebuffer( 36160, renderTargetProperties.__webglMultisampledFramebuffer ); // see #18905
- } else {
- console.warn( 'THREE.WebGLRenderer: WebGLMultisampleRenderTarget can only be used with WebGL2.' );
- }
- }
- }
- function getRenderTargetSamples( renderTarget ) {
- return ( isWebGL2 && renderTarget.isWebGLMultisampleRenderTarget ) ?
- Math.min( maxSamples, renderTarget.samples ) : 0;
- }
- function updateVideoTexture( texture ) {
- const frame = info.render.frame;
- // Check the last frame we updated the VideoTexture
- if ( _videoTextures.get( texture ) !== frame ) {
- _videoTextures.set( texture, frame );
- texture.update();
- }
- }
- // backwards compatibility
- let warnedTexture2D = false;
- let warnedTextureCube = false;
- function safeSetTexture2D( texture, slot ) {
- if ( texture && texture.isWebGLRenderTarget ) {
- if ( warnedTexture2D === false ) {
- console.warn( 'THREE.WebGLTextures.safeSetTexture2D: don\'t use render targets as textures. Use their .texture property instead.' );
- warnedTexture2D = true;
- }
- texture = texture.texture;
- }
- setTexture2D( texture, slot );
- }
- function safeSetTextureCube( texture, slot ) {
- if ( texture && texture.isWebGLCubeRenderTarget ) {
- if ( warnedTextureCube === false ) {
- console.warn( 'THREE.WebGLTextures.safeSetTextureCube: don\'t use cube render targets as textures. Use their .texture property instead.' );
- warnedTextureCube = true;
- }
- texture = texture.texture;
- }
- setTextureCube( texture, slot );
- }
- //
- this.allocateTextureUnit = allocateTextureUnit;
- this.resetTextureUnits = resetTextureUnits;
- this.setTexture2D = setTexture2D;
- this.setTexture2DArray = setTexture2DArray;
- this.setTexture3D = setTexture3D;
- this.setTextureCube = setTextureCube;
- this.setupRenderTarget = setupRenderTarget;
- this.updateRenderTargetMipmap = updateRenderTargetMipmap;
- this.updateMultisampleRenderTarget = updateMultisampleRenderTarget;
- this.safeSetTexture2D = safeSetTexture2D;
- this.safeSetTextureCube = safeSetTextureCube;
- }
- function WebGLUtils( gl, extensions, capabilities ) {
- const isWebGL2 = capabilities.isWebGL2;
- function convert( p ) {
- let extension;
- if ( p === UnsignedByteType ) return 5121;
- if ( p === UnsignedShort4444Type ) return 32819;
- if ( p === UnsignedShort5551Type ) return 32820;
- if ( p === UnsignedShort565Type ) return 33635;
- if ( p === ByteType ) return 5120;
- if ( p === ShortType ) return 5122;
- if ( p === UnsignedShortType ) return 5123;
- if ( p === IntType ) return 5124;
- if ( p === UnsignedIntType ) return 5125;
- if ( p === FloatType ) return 5126;
- if ( p === HalfFloatType ) {
- if ( isWebGL2 ) return 5131;
- extension = extensions.get( 'OES_texture_half_float' );
- if ( extension !== null ) {
- return extension.HALF_FLOAT_OES;
- } else {
- return null;
- }
- }
- if ( p === AlphaFormat ) return 6406;
- if ( p === RGBFormat ) return 6407;
- if ( p === RGBAFormat ) return 6408;
- if ( p === LuminanceFormat ) return 6409;
- if ( p === LuminanceAlphaFormat ) return 6410;
- if ( p === DepthFormat ) return 6402;
- if ( p === DepthStencilFormat ) return 34041;
- if ( p === RedFormat ) return 6403;
- // WebGL2 formats.
- if ( p === RedIntegerFormat ) return 36244;
- if ( p === RGFormat ) return 33319;
- if ( p === RGIntegerFormat ) return 33320;
- if ( p === RGBIntegerFormat ) return 36248;
- if ( p === RGBAIntegerFormat ) return 36249;
- if ( p === RGB_S3TC_DXT1_Format || p === RGBA_S3TC_DXT1_Format$1 ||
- p === RGBA_S3TC_DXT3_Format || p === RGBA_S3TC_DXT5_Format$1 ) {
- extension = extensions.get( 'WEBGL_compressed_texture_s3tc' );
- if ( extension !== null ) {
- if ( p === RGB_S3TC_DXT1_Format ) return extension.COMPRESSED_RGB_S3TC_DXT1_EXT;
- if ( p === RGBA_S3TC_DXT1_Format$1 ) return extension.COMPRESSED_RGBA_S3TC_DXT1_EXT;
- if ( p === RGBA_S3TC_DXT3_Format ) return extension.COMPRESSED_RGBA_S3TC_DXT3_EXT;
- if ( p === RGBA_S3TC_DXT5_Format$1 ) return extension.COMPRESSED_RGBA_S3TC_DXT5_EXT;
- } else {
- return null;
- }
- }
- if ( p === RGB_PVRTC_4BPPV1_Format || p === RGB_PVRTC_2BPPV1_Format ||
- p === RGBA_PVRTC_4BPPV1_Format || p === RGBA_PVRTC_2BPPV1_Format ) {
- extension = extensions.get( 'WEBGL_compressed_texture_pvrtc' );
- if ( extension !== null ) {
- if ( p === RGB_PVRTC_4BPPV1_Format ) return extension.COMPRESSED_RGB_PVRTC_4BPPV1_IMG;
- if ( p === RGB_PVRTC_2BPPV1_Format ) return extension.COMPRESSED_RGB_PVRTC_2BPPV1_IMG;
- if ( p === RGBA_PVRTC_4BPPV1_Format ) return extension.COMPRESSED_RGBA_PVRTC_4BPPV1_IMG;
- if ( p === RGBA_PVRTC_2BPPV1_Format ) return extension.COMPRESSED_RGBA_PVRTC_2BPPV1_IMG;
- } else {
- return null;
- }
- }
- if ( p === RGB_ETC1_Format ) {
- extension = extensions.get( 'WEBGL_compressed_texture_etc1' );
- if ( extension !== null ) {
- return extension.COMPRESSED_RGB_ETC1_WEBGL;
- } else {
- return null;
- }
- }
- if ( p === RGB_ETC2_Format || p === RGBA_ETC2_EAC_Format ) {
- extension = extensions.get( 'WEBGL_compressed_texture_etc' );
- if ( extension !== null ) {
- if ( p === RGB_ETC2_Format ) return extension.COMPRESSED_RGB8_ETC2;
- if ( p === RGBA_ETC2_EAC_Format ) return extension.COMPRESSED_RGBA8_ETC2_EAC;
- }
- }
- if ( p === RGBA_ASTC_4x4_Format || p === RGBA_ASTC_5x4_Format || p === RGBA_ASTC_5x5_Format ||
- p === RGBA_ASTC_6x5_Format || p === RGBA_ASTC_6x6_Format || p === RGBA_ASTC_8x5_Format ||
- p === RGBA_ASTC_8x6_Format || p === RGBA_ASTC_8x8_Format || p === RGBA_ASTC_10x5_Format ||
- p === RGBA_ASTC_10x6_Format || p === RGBA_ASTC_10x8_Format || p === RGBA_ASTC_10x10_Format ||
- p === RGBA_ASTC_12x10_Format || p === RGBA_ASTC_12x12_Format ||
- p === SRGB8_ALPHA8_ASTC_4x4_Format || p === SRGB8_ALPHA8_ASTC_5x4_Format || p === SRGB8_ALPHA8_ASTC_5x5_Format ||
- p === SRGB8_ALPHA8_ASTC_6x5_Format || p === SRGB8_ALPHA8_ASTC_6x6_Format || p === SRGB8_ALPHA8_ASTC_8x5_Format ||
- p === SRGB8_ALPHA8_ASTC_8x6_Format || p === SRGB8_ALPHA8_ASTC_8x8_Format || p === SRGB8_ALPHA8_ASTC_10x5_Format ||
- p === SRGB8_ALPHA8_ASTC_10x6_Format || p === SRGB8_ALPHA8_ASTC_10x8_Format || p === SRGB8_ALPHA8_ASTC_10x10_Format ||
- p === SRGB8_ALPHA8_ASTC_12x10_Format || p === SRGB8_ALPHA8_ASTC_12x12_Format ) {
- extension = extensions.get( 'WEBGL_compressed_texture_astc' );
- if ( extension !== null ) {
- // TODO Complete?
- return p;
- } else {
- return null;
- }
- }
- if ( p === RGBA_BPTC_Format ) {
- extension = extensions.get( 'EXT_texture_compression_bptc' );
- if ( extension !== null ) {
- // TODO Complete?
- return p;
- } else {
- return null;
- }
- }
- if ( p === UnsignedInt248Type$1 ) {
- if ( isWebGL2 ) return 34042;
- extension = extensions.get( 'WEBGL_depth_texture' );
- if ( extension !== null ) {
- return extension.UNSIGNED_INT_24_8_WEBGL;
- } else {
- return null;
- }
- }
- }
- return { convert: convert };
- }
- function ArrayCamera( array = [] ) {
- PerspectiveCamera.call( this );
- this.cameras = array;
- }
- ArrayCamera.prototype = Object.assign( Object.create( PerspectiveCamera.prototype ), {
- constructor: ArrayCamera,
- isArrayCamera: true
- } );
- function Group() {
- Object3D.call( this );
- this.type = 'Group';
- }
- Group.prototype = Object.assign( Object.create( Object3D.prototype ), {
- constructor: Group,
- isGroup: true
- } );
- function WebXRController() {
- this._targetRay = null;
- this._grip = null;
- this._hand = null;
- }
- Object.assign( WebXRController.prototype, {
- constructor: WebXRController,
- getHandSpace: function () {
- if ( this._hand === null ) {
- this._hand = new Group();
- this._hand.matrixAutoUpdate = false;
- this._hand.visible = false;
- this._hand.joints = [];
- this._hand.inputState = { pinching: false };
- if ( window.XRHand ) {
- for ( let i = 0; i <= window.XRHand.LITTLE_PHALANX_TIP; i ++ ) {
- // The transform of this joint will be updated with the joint pose on each frame
- const joint = new Group();
- joint.matrixAutoUpdate = false;
- joint.visible = false;
- this._hand.joints.push( joint );
- // ??
- this._hand.add( joint );
- }
- }
- }
- return this._hand;
- },
- getTargetRaySpace: function () {
- if ( this._targetRay === null ) {
- this._targetRay = new Group();
- this._targetRay.matrixAutoUpdate = false;
- this._targetRay.visible = false;
- }
- return this._targetRay;
- },
- getGripSpace: function () {
- if ( this._grip === null ) {
- this._grip = new Group();
- this._grip.matrixAutoUpdate = false;
- this._grip.visible = false;
- }
- return this._grip;
- },
- dispatchEvent: function ( event ) {
- if ( this._targetRay !== null ) {
- this._targetRay.dispatchEvent( event );
- }
- if ( this._grip !== null ) {
- this._grip.dispatchEvent( event );
- }
- if ( this._hand !== null ) {
- this._hand.dispatchEvent( event );
- }
- return this;
- },
- disconnect: function ( inputSource ) {
- this.dispatchEvent( { type: 'disconnected', data: inputSource } );
- if ( this._targetRay !== null ) {
- this._targetRay.visible = false;
- }
- if ( this._grip !== null ) {
- this._grip.visible = false;
- }
- if ( this._hand !== null ) {
- this._hand.visible = false;
- }
- return this;
- },
- update: function ( inputSource, frame, referenceSpace ) {
- let inputPose = null;
- let gripPose = null;
- let handPose = null;
- const targetRay = this._targetRay;
- const grip = this._grip;
- const hand = this._hand;
- if ( inputSource && frame.session.visibilityState !== 'visible-blurred' ) {
- if ( hand && inputSource.hand ) {
- handPose = true;
- for ( let i = 0; i <= window.XRHand.LITTLE_PHALANX_TIP; i ++ ) {
- if ( inputSource.hand[ i ] ) {
- // Update the joints groups with the XRJoint poses
- const jointPose = frame.getJointPose( inputSource.hand[ i ], referenceSpace );
- const joint = hand.joints[ i ];
- if ( jointPose !== null ) {
- joint.matrix.fromArray( jointPose.transform.matrix );
- joint.matrix.decompose( joint.position, joint.rotation, joint.scale );
- joint.jointRadius = jointPose.radius;
- }
- joint.visible = jointPose !== null;
- // Custom events
- // Check pinch
- const indexTip = hand.joints[ window.XRHand.INDEX_PHALANX_TIP ];
- const thumbTip = hand.joints[ window.XRHand.THUMB_PHALANX_TIP ];
- const distance = indexTip.position.distanceTo( thumbTip.position );
- const distanceToPinch = 0.02;
- const threshold = 0.005;
- if ( hand.inputState.pinching && distance > distanceToPinch + threshold ) {
- hand.inputState.pinching = false;
- this.dispatchEvent( {
- type: 'pinchend',
- handedness: inputSource.handedness,
- target: this
- } );
- } else if ( ! hand.inputState.pinching && distance <= distanceToPinch - threshold ) {
- hand.inputState.pinching = true;
- this.dispatchEvent( {
- type: 'pinchstart',
- handedness: inputSource.handedness,
- target: this
- } );
- }
- }
- }
- } else {
- if ( targetRay !== null ) {
- inputPose = frame.getPose( inputSource.targetRaySpace, referenceSpace );
- if ( inputPose !== null ) {
- targetRay.matrix.fromArray( inputPose.transform.matrix );
- targetRay.matrix.decompose( targetRay.position, targetRay.rotation, targetRay.scale );
- }
- }
- if ( grip !== null && inputSource.gripSpace ) {
- gripPose = frame.getPose( inputSource.gripSpace, referenceSpace );
- if ( gripPose !== null ) {
- grip.matrix.fromArray( gripPose.transform.matrix );
- grip.matrix.decompose( grip.position, grip.rotation, grip.scale );
- }
- }
- }
- }
- if ( targetRay !== null ) {
- targetRay.visible = ( inputPose !== null );
- }
- if ( grip !== null ) {
- grip.visible = ( gripPose !== null );
- }
- if ( hand !== null ) {
- hand.visible = ( handPose !== null );
- }
- return this;
- }
- } );
- function WebXRManager( renderer, gl ) {
- const scope = this;
- let session = null;
- let framebufferScaleFactor = 1.0;
- let referenceSpace = null;
- let referenceSpaceType = 'local-floor';
- let pose = null;
- const controllers = [];
- const inputSourcesMap = new Map();
- //
- const cameraL = new PerspectiveCamera();
- cameraL.layers.enable( 1 );
- cameraL.viewport = new Vector4();
- const cameraR = new PerspectiveCamera();
- cameraR.layers.enable( 2 );
- cameraR.viewport = new Vector4();
- const cameras = [ cameraL, cameraR ];
- const cameraVR = new ArrayCamera();
- cameraVR.layers.enable( 1 );
- cameraVR.layers.enable( 2 );
- let _currentDepthNear = null;
- let _currentDepthFar = null;
- //
- this.enabled = false;
- this.isPresenting = false;
- this.getController = function ( index ) {
- let controller = controllers[ index ];
- if ( controller === undefined ) {
- controller = new WebXRController();
- controllers[ index ] = controller;
- }
- return controller.getTargetRaySpace();
- };
- this.getControllerGrip = function ( index ) {
- let controller = controllers[ index ];
- if ( controller === undefined ) {
- controller = new WebXRController();
- controllers[ index ] = controller;
- }
- return controller.getGripSpace();
- };
- this.getHand = function ( index ) {
- let controller = controllers[ index ];
- if ( controller === undefined ) {
- controller = new WebXRController();
- controllers[ index ] = controller;
- }
- return controller.getHandSpace();
- };
- //
- function onSessionEvent( event ) {
- const controller = inputSourcesMap.get( event.inputSource );
- if ( controller ) {
- controller.dispatchEvent( { type: event.type, data: event.inputSource } );
- }
- }
- function onSessionEnd() {
- inputSourcesMap.forEach( function ( controller, inputSource ) {
- controller.disconnect( inputSource );
- } );
- inputSourcesMap.clear();
- //
- renderer.setFramebuffer( null );
- renderer.setRenderTarget( renderer.getRenderTarget() ); // Hack #15830
- animation.stop();
- scope.isPresenting = false;
- scope.dispatchEvent( { type: 'sessionend' } );
- }
- function onRequestReferenceSpace( value ) {
- referenceSpace = value;
- animation.setContext( session );
- animation.start();
- scope.isPresenting = true;
- scope.dispatchEvent( { type: 'sessionstart' } );
- }
- this.setFramebufferScaleFactor = function ( value ) {
- framebufferScaleFactor = value;
- if ( scope.isPresenting === true ) {
- console.warn( 'THREE.WebXRManager: Cannot change framebuffer scale while presenting.' );
- }
- };
- this.setReferenceSpaceType = function ( value ) {
- referenceSpaceType = value;
- if ( scope.isPresenting === true ) {
- console.warn( 'THREE.WebXRManager: Cannot change reference space type while presenting.' );
- }
- };
- this.getReferenceSpace = function () {
- return referenceSpace;
- };
- this.getSession = function () {
- return session;
- };
- this.setSession = function ( value ) {
- session = value;
- if ( session !== null ) {
- session.addEventListener( 'select', onSessionEvent );
- session.addEventListener( 'selectstart', onSessionEvent );
- session.addEventListener( 'selectend', onSessionEvent );
- session.addEventListener( 'squeeze', onSessionEvent );
- session.addEventListener( 'squeezestart', onSessionEvent );
- session.addEventListener( 'squeezeend', onSessionEvent );
- session.addEventListener( 'end', onSessionEnd );
- const attributes = gl.getContextAttributes();
- if ( attributes.xrCompatible !== true ) {
- gl.makeXRCompatible();
- }
- const layerInit = {
- antialias: attributes.antialias,
- alpha: attributes.alpha,
- depth: attributes.depth,
- stencil: attributes.stencil,
- framebufferScaleFactor: framebufferScaleFactor
- };
- // eslint-disable-next-line no-undef
- const baseLayer = new XRWebGLLayer( session, gl, layerInit );
- session.updateRenderState( { baseLayer: baseLayer } );
- session.requestReferenceSpace( referenceSpaceType ).then( onRequestReferenceSpace );
- //
- session.addEventListener( 'inputsourceschange', updateInputSources );
- }
- };
- function updateInputSources( event ) {
- const inputSources = session.inputSources;
- // Assign inputSources to available controllers
- for ( let i = 0; i < controllers.length; i ++ ) {
- inputSourcesMap.set( inputSources[ i ], controllers[ i ] );
- }
- // Notify disconnected
- for ( let i = 0; i < event.removed.length; i ++ ) {
- const inputSource = event.removed[ i ];
- const controller = inputSourcesMap.get( inputSource );
- if ( controller ) {
- controller.dispatchEvent( { type: 'disconnected', data: inputSource } );
- inputSourcesMap.delete( inputSource );
- }
- }
- // Notify connected
- for ( let i = 0; i < event.added.length; i ++ ) {
- const inputSource = event.added[ i ];
- const controller = inputSourcesMap.get( inputSource );
- if ( controller ) {
- controller.dispatchEvent( { type: 'connected', data: inputSource } );
- }
- }
- }
- //
- const cameraLPos = new Vector3();
- const cameraRPos = new Vector3();
- /**
- * Assumes 2 cameras that are parallel and share an X-axis, and that
- * the cameras' projection and world matrices have already been set.
- * And that near and far planes are identical for both cameras.
- * Visualization of this technique: https://computergraphics.stackexchange.com/a/4765
- */
- function setProjectionFromUnion( camera, cameraL, cameraR ) {
- cameraLPos.setFromMatrixPosition( cameraL.matrixWorld );
- cameraRPos.setFromMatrixPosition( cameraR.matrixWorld );
- const ipd = cameraLPos.distanceTo( cameraRPos );
- const projL = cameraL.projectionMatrix.elements;
- const projR = cameraR.projectionMatrix.elements;
- // VR systems will have identical far and near planes, and
- // most likely identical top and bottom frustum extents.
- // Use the left camera for these values.
- const near = projL[ 14 ] / ( projL[ 10 ] - 1 );
- const far = projL[ 14 ] / ( projL[ 10 ] + 1 );
- const topFov = ( projL[ 9 ] + 1 ) / projL[ 5 ];
- const bottomFov = ( projL[ 9 ] - 1 ) / projL[ 5 ];
- const leftFov = ( projL[ 8 ] - 1 ) / projL[ 0 ];
- const rightFov = ( projR[ 8 ] + 1 ) / projR[ 0 ];
- const left = near * leftFov;
- const right = near * rightFov;
- // Calculate the new camera's position offset from the
- // left camera. xOffset should be roughly half `ipd`.
- const zOffset = ipd / ( - leftFov + rightFov );
- const xOffset = zOffset * - leftFov;
- // TODO: Better way to apply this offset?
- cameraL.matrixWorld.decompose( camera.position, camera.quaternion, camera.scale );
- camera.translateX( xOffset );
- camera.translateZ( zOffset );
- camera.matrixWorld.compose( camera.position, camera.quaternion, camera.scale );
- camera.matrixWorldInverse.copy( camera.matrixWorld ).invert();
- // Find the union of the frustum values of the cameras and scale
- // the values so that the near plane's position does not change in world space,
- // although must now be relative to the new union camera.
- const near2 = near + zOffset;
- const far2 = far + zOffset;
- const left2 = left - xOffset;
- const right2 = right + ( ipd - xOffset );
- const top2 = topFov * far / far2 * near2;
- const bottom2 = bottomFov * far / far2 * near2;
- camera.projectionMatrix.makePerspective( left2, right2, top2, bottom2, near2, far2 );
- }
- function updateCamera( camera, parent ) {
- if ( parent === null ) {
- camera.matrixWorld.copy( camera.matrix );
- } else {
- camera.matrixWorld.multiplyMatrices( parent.matrixWorld, camera.matrix );
- }
- camera.matrixWorldInverse.copy( camera.matrixWorld ).invert();
- }
- this.getCamera = function ( camera ) {
- cameraVR.near = cameraR.near = cameraL.near = camera.near;
- cameraVR.far = cameraR.far = cameraL.far = camera.far;
- if ( _currentDepthNear !== cameraVR.near || _currentDepthFar !== cameraVR.far ) {
- // Note that the new renderState won't apply until the next frame. See #18320
- session.updateRenderState( {
- depthNear: cameraVR.near,
- depthFar: cameraVR.far
- } );
- _currentDepthNear = cameraVR.near;
- _currentDepthFar = cameraVR.far;
- }
- const parent = camera.parent;
- const cameras = cameraVR.cameras;
- updateCamera( cameraVR, parent );
- for ( let i = 0; i < cameras.length; i ++ ) {
- updateCamera( cameras[ i ], parent );
- }
- // update camera and its children
- camera.matrixWorld.copy( cameraVR.matrixWorld );
- const children = camera.children;
- for ( let i = 0, l = children.length; i < l; i ++ ) {
- children[ i ].updateMatrixWorld( true );
- }
- // update projection matrix for proper view frustum culling
- if ( cameras.length === 2 ) {
- setProjectionFromUnion( cameraVR, cameraL, cameraR );
- } else {
- // assume single camera setup (AR)
- cameraVR.projectionMatrix.copy( cameraL.projectionMatrix );
- }
- return cameraVR;
- };
- // Animation Loop
- let onAnimationFrameCallback = null;
- function onAnimationFrame( time, frame ) {
- pose = frame.getViewerPose( referenceSpace );
- if ( pose !== null ) {
- const views = pose.views;
- const baseLayer = session.renderState.baseLayer;
- renderer.setFramebuffer( baseLayer.framebuffer );
- let cameraVRNeedsUpdate = false;
- // check if it's necessary to rebuild cameraVR's camera list
- if ( views.length !== cameraVR.cameras.length ) {
- cameraVR.cameras.length = 0;
- cameraVRNeedsUpdate = true;
- }
- for ( let i = 0; i < views.length; i ++ ) {
- const view = views[ i ];
- const viewport = baseLayer.getViewport( view );
- const camera = cameras[ i ];
- camera.matrix.fromArray( view.transform.matrix );
- camera.projectionMatrix.fromArray( view.projectionMatrix );
- camera.viewport.set( viewport.x, viewport.y, viewport.width, viewport.height );
- if ( i === 0 ) {
- cameraVR.matrix.copy( camera.matrix );
- }
- if ( cameraVRNeedsUpdate === true ) {
- cameraVR.cameras.push( camera );
- }
- }
- }
- //
- const inputSources = session.inputSources;
- for ( let i = 0; i < controllers.length; i ++ ) {
- const controller = controllers[ i ];
- const inputSource = inputSources[ i ];
- controller.update( inputSource, frame, referenceSpace );
- }
- if ( onAnimationFrameCallback ) onAnimationFrameCallback( time, frame );
- }
- const animation = new WebGLAnimation();
- animation.setAnimationLoop( onAnimationFrame );
- this.setAnimationLoop = function ( callback ) {
- onAnimationFrameCallback = callback;
- };
- this.dispose = function () {};
- }
- Object.assign( WebXRManager.prototype, EventDispatcher.prototype );
- function WebGLMaterials( properties ) {
- function refreshFogUniforms( uniforms, fog ) {
- uniforms.fogColor.value.copy( fog.color );
- if ( fog.isFog ) {
- uniforms.fogNear.value = fog.near;
- uniforms.fogFar.value = fog.far;
- } else if ( fog.isFogExp2 ) {
- uniforms.fogDensity.value = fog.density;
- }
- }
- function refreshMaterialUniforms( uniforms, material, pixelRatio, height ) {
- if ( material.isMeshBasicMaterial ) {
- refreshUniformsCommon( uniforms, material );
- } else if ( material.isMeshLambertMaterial ) {
- refreshUniformsCommon( uniforms, material );
- refreshUniformsLambert( uniforms, material );
- } else if ( material.isMeshToonMaterial ) {
- refreshUniformsCommon( uniforms, material );
- refreshUniformsToon( uniforms, material );
- } else if ( material.isMeshPhongMaterial ) {
- refreshUniformsCommon( uniforms, material );
- refreshUniformsPhong( uniforms, material );
- } else if ( material.isMeshStandardMaterial ) {
- refreshUniformsCommon( uniforms, material );
- if ( material.isMeshPhysicalMaterial ) {
- refreshUniformsPhysical( uniforms, material );
- } else {
- refreshUniformsStandard( uniforms, material );
- }
- } else if ( material.isMeshMatcapMaterial ) {
- refreshUniformsCommon( uniforms, material );
- refreshUniformsMatcap( uniforms, material );
- } else if ( material.isMeshDepthMaterial ) {
- refreshUniformsCommon( uniforms, material );
- refreshUniformsDepth( uniforms, material );
- } else if ( material.isMeshDistanceMaterial ) {
- refreshUniformsCommon( uniforms, material );
- refreshUniformsDistance( uniforms, material );
- } else if ( material.isMeshNormalMaterial ) {
- refreshUniformsCommon( uniforms, material );
- refreshUniformsNormal( uniforms, material );
- } else if ( material.isLineBasicMaterial ) {
- refreshUniformsLine( uniforms, material );
- if ( material.isLineDashedMaterial ) {
- refreshUniformsDash( uniforms, material );
- }
- } else if ( material.isPointsMaterial ) {
- refreshUniformsPoints( uniforms, material, pixelRatio, height );
- } else if ( material.isSpriteMaterial ) {
- refreshUniformsSprites( uniforms, material );
- } else if ( material.isShadowMaterial ) {
- uniforms.color.value.copy( material.color );
- uniforms.opacity.value = material.opacity;
- } else if ( material.isShaderMaterial ) {
- material.uniformsNeedUpdate = false; // #15581
- }
- }
- function refreshUniformsCommon( uniforms, material ) {
- uniforms.opacity.value = material.opacity;
- if ( material.color ) {
- uniforms.diffuse.value.copy( material.color );
- }
- if ( material.emissive ) {
- uniforms.emissive.value.copy( material.emissive ).multiplyScalar( material.emissiveIntensity );
- }
- if ( material.map ) {
- uniforms.map.value = material.map;
- }
- if ( material.alphaMap ) {
- uniforms.alphaMap.value = material.alphaMap;
- }
- if ( material.specularMap ) {
- uniforms.specularMap.value = material.specularMap;
- }
- const envMap = properties.get( material ).envMap;
- if ( envMap ) {
- uniforms.envMap.value = envMap;
- uniforms.flipEnvMap.value = ( envMap.isCubeTexture && envMap._needsFlipEnvMap ) ? - 1 : 1;
- uniforms.reflectivity.value = material.reflectivity;
- uniforms.refractionRatio.value = material.refractionRatio;
- const maxMipLevel = properties.get( envMap ).__maxMipLevel;
- if ( maxMipLevel !== undefined ) {
- uniforms.maxMipLevel.value = maxMipLevel;
- }
- }
- if ( material.lightMap ) {
- uniforms.lightMap.value = material.lightMap;
- uniforms.lightMapIntensity.value = material.lightMapIntensity;
- }
- if ( material.aoMap ) {
- uniforms.aoMap.value = material.aoMap;
- uniforms.aoMapIntensity.value = material.aoMapIntensity;
- }
- // uv repeat and offset setting priorities
- // 1. color map
- // 2. specular map
- // 3. displacementMap map
- // 4. normal map
- // 5. bump map
- // 6. roughnessMap map
- // 7. metalnessMap map
- // 8. alphaMap map
- // 9. emissiveMap map
- // 10. clearcoat map
- // 11. clearcoat normal map
- // 12. clearcoat roughnessMap map
- let uvScaleMap;
- if ( material.map ) {
- uvScaleMap = material.map;
- } else if ( material.specularMap ) {
- uvScaleMap = material.specularMap;
- } else if ( material.displacementMap ) {
- uvScaleMap = material.displacementMap;
- } else if ( material.normalMap ) {
- uvScaleMap = material.normalMap;
- } else if ( material.bumpMap ) {
- uvScaleMap = material.bumpMap;
- } else if ( material.roughnessMap ) {
- uvScaleMap = material.roughnessMap;
- } else if ( material.metalnessMap ) {
- uvScaleMap = material.metalnessMap;
- } else if ( material.alphaMap ) {
- uvScaleMap = material.alphaMap;
- } else if ( material.emissiveMap ) {
- uvScaleMap = material.emissiveMap;
- } else if ( material.clearcoatMap ) {
- uvScaleMap = material.clearcoatMap;
- } else if ( material.clearcoatNormalMap ) {
- uvScaleMap = material.clearcoatNormalMap;
- } else if ( material.clearcoatRoughnessMap ) {
- uvScaleMap = material.clearcoatRoughnessMap;
- }
- if ( uvScaleMap !== undefined ) {
- // backwards compatibility
- if ( uvScaleMap.isWebGLRenderTarget ) {
- uvScaleMap = uvScaleMap.texture;
- }
- if ( uvScaleMap.matrixAutoUpdate === true ) {
- uvScaleMap.updateMatrix();
- }
- uniforms.uvTransform.value.copy( uvScaleMap.matrix );
- }
- // uv repeat and offset setting priorities for uv2
- // 1. ao map
- // 2. light map
- let uv2ScaleMap;
- if ( material.aoMap ) {
- uv2ScaleMap = material.aoMap;
- } else if ( material.lightMap ) {
- uv2ScaleMap = material.lightMap;
- }
- if ( uv2ScaleMap !== undefined ) {
- // backwards compatibility
- if ( uv2ScaleMap.isWebGLRenderTarget ) {
- uv2ScaleMap = uv2ScaleMap.texture;
- }
- if ( uv2ScaleMap.matrixAutoUpdate === true ) {
- uv2ScaleMap.updateMatrix();
- }
- uniforms.uv2Transform.value.copy( uv2ScaleMap.matrix );
- }
- }
- function refreshUniformsLine( uniforms, material ) {
- uniforms.diffuse.value.copy( material.color );
- uniforms.opacity.value = material.opacity;
- }
- function refreshUniformsDash( uniforms, material ) {
- uniforms.dashSize.value = material.dashSize;
- uniforms.totalSize.value = material.dashSize + material.gapSize;
- uniforms.scale.value = material.scale;
- }
- function refreshUniformsPoints( uniforms, material, pixelRatio, height ) {
- uniforms.diffuse.value.copy( material.color );
- uniforms.opacity.value = material.opacity;
- uniforms.size.value = material.size * pixelRatio;
- uniforms.scale.value = height * 0.5;
- if ( material.map ) {
- uniforms.map.value = material.map;
- }
- if ( material.alphaMap ) {
- uniforms.alphaMap.value = material.alphaMap;
- }
- // uv repeat and offset setting priorities
- // 1. color map
- // 2. alpha map
- let uvScaleMap;
- if ( material.map ) {
- uvScaleMap = material.map;
- } else if ( material.alphaMap ) {
- uvScaleMap = material.alphaMap;
- }
- if ( uvScaleMap !== undefined ) {
- if ( uvScaleMap.matrixAutoUpdate === true ) {
- uvScaleMap.updateMatrix();
- }
- uniforms.uvTransform.value.copy( uvScaleMap.matrix );
- }
- }
- function refreshUniformsSprites( uniforms, material ) {
- uniforms.diffuse.value.copy( material.color );
- uniforms.opacity.value = material.opacity;
- uniforms.rotation.value = material.rotation;
- if ( material.map ) {
- uniforms.map.value = material.map;
- }
- if ( material.alphaMap ) {
- uniforms.alphaMap.value = material.alphaMap;
- }
- // uv repeat and offset setting priorities
- // 1. color map
- // 2. alpha map
- let uvScaleMap;
- if ( material.map ) {
- uvScaleMap = material.map;
- } else if ( material.alphaMap ) {
- uvScaleMap = material.alphaMap;
- }
- if ( uvScaleMap !== undefined ) {
- if ( uvScaleMap.matrixAutoUpdate === true ) {
- uvScaleMap.updateMatrix();
- }
- uniforms.uvTransform.value.copy( uvScaleMap.matrix );
- }
- }
- function refreshUniformsLambert( uniforms, material ) {
- if ( material.emissiveMap ) {
- uniforms.emissiveMap.value = material.emissiveMap;
- }
- }
- function refreshUniformsPhong( uniforms, material ) {
- uniforms.specular.value.copy( material.specular );
- uniforms.shininess.value = Math.max( material.shininess, 1e-4 ); // to prevent pow( 0.0, 0.0 )
- if ( material.emissiveMap ) {
- uniforms.emissiveMap.value = material.emissiveMap;
- }
- if ( material.bumpMap ) {
- uniforms.bumpMap.value = material.bumpMap;
- uniforms.bumpScale.value = material.bumpScale;
- if ( material.side === BackSide ) uniforms.bumpScale.value *= - 1;
- }
- if ( material.normalMap ) {
- uniforms.normalMap.value = material.normalMap;
- uniforms.normalScale.value.copy( material.normalScale );
- if ( material.side === BackSide ) uniforms.normalScale.value.negate();
- }
- if ( material.displacementMap ) {
- uniforms.displacementMap.value = material.displacementMap;
- uniforms.displacementScale.value = material.displacementScale;
- uniforms.displacementBias.value = material.displacementBias;
- }
- }
- function refreshUniformsToon( uniforms, material ) {
- if ( material.gradientMap ) {
- uniforms.gradientMap.value = material.gradientMap;
- }
- if ( material.emissiveMap ) {
- uniforms.emissiveMap.value = material.emissiveMap;
- }
- if ( material.bumpMap ) {
- uniforms.bumpMap.value = material.bumpMap;
- uniforms.bumpScale.value = material.bumpScale;
- if ( material.side === BackSide ) uniforms.bumpScale.value *= - 1;
- }
- if ( material.normalMap ) {
- uniforms.normalMap.value = material.normalMap;
- uniforms.normalScale.value.copy( material.normalScale );
- if ( material.side === BackSide ) uniforms.normalScale.value.negate();
- }
- if ( material.displacementMap ) {
- uniforms.displacementMap.value = material.displacementMap;
- uniforms.displacementScale.value = material.displacementScale;
- uniforms.displacementBias.value = material.displacementBias;
- }
- }
- function refreshUniformsStandard( uniforms, material ) {
- uniforms.roughness.value = material.roughness;
- uniforms.metalness.value = material.metalness;
- if ( material.roughnessMap ) {
- uniforms.roughnessMap.value = material.roughnessMap;
- }
- if ( material.metalnessMap ) {
- uniforms.metalnessMap.value = material.metalnessMap;
- }
- if ( material.emissiveMap ) {
- uniforms.emissiveMap.value = material.emissiveMap;
- }
- if ( material.bumpMap ) {
- uniforms.bumpMap.value = material.bumpMap;
- uniforms.bumpScale.value = material.bumpScale;
- if ( material.side === BackSide ) uniforms.bumpScale.value *= - 1;
- }
- if ( material.normalMap ) {
- uniforms.normalMap.value = material.normalMap;
- uniforms.normalScale.value.copy( material.normalScale );
- if ( material.side === BackSide ) uniforms.normalScale.value.negate();
- }
- if ( material.displacementMap ) {
- uniforms.displacementMap.value = material.displacementMap;
- uniforms.displacementScale.value = material.displacementScale;
- uniforms.displacementBias.value = material.displacementBias;
- }
- const envMap = properties.get( material ).envMap;
- if ( envMap ) {
- //uniforms.envMap.value = material.envMap; // part of uniforms common
- uniforms.envMapIntensity.value = material.envMapIntensity;
- }
- }
- function refreshUniformsPhysical( uniforms, material ) {
- refreshUniformsStandard( uniforms, material );
- uniforms.reflectivity.value = material.reflectivity; // also part of uniforms common
- uniforms.clearcoat.value = material.clearcoat;
- uniforms.clearcoatRoughness.value = material.clearcoatRoughness;
- if ( material.sheen ) uniforms.sheen.value.copy( material.sheen );
- if ( material.clearcoatMap ) {
- uniforms.clearcoatMap.value = material.clearcoatMap;
- }
- if ( material.clearcoatRoughnessMap ) {
- uniforms.clearcoatRoughnessMap.value = material.clearcoatRoughnessMap;
- }
- if ( material.clearcoatNormalMap ) {
- uniforms.clearcoatNormalScale.value.copy( material.clearcoatNormalScale );
- uniforms.clearcoatNormalMap.value = material.clearcoatNormalMap;
- if ( material.side === BackSide ) {
- uniforms.clearcoatNormalScale.value.negate();
- }
- }
- uniforms.transmission.value = material.transmission;
- if ( material.transmissionMap ) {
- uniforms.transmissionMap.value = material.transmissionMap;
- }
- }
- function refreshUniformsMatcap( uniforms, material ) {
- if ( material.matcap ) {
- uniforms.matcap.value = material.matcap;
- }
- if ( material.bumpMap ) {
- uniforms.bumpMap.value = material.bumpMap;
- uniforms.bumpScale.value = material.bumpScale;
- if ( material.side === BackSide ) uniforms.bumpScale.value *= - 1;
- }
- if ( material.normalMap ) {
- uniforms.normalMap.value = material.normalMap;
- uniforms.normalScale.value.copy( material.normalScale );
- if ( material.side === BackSide ) uniforms.normalScale.value.negate();
- }
- if ( material.displacementMap ) {
- uniforms.displacementMap.value = material.displacementMap;
- uniforms.displacementScale.value = material.displacementScale;
- uniforms.displacementBias.value = material.displacementBias;
- }
- }
- function refreshUniformsDepth( uniforms, material ) {
- if ( material.displacementMap ) {
- uniforms.displacementMap.value = material.displacementMap;
- uniforms.displacementScale.value = material.displacementScale;
- uniforms.displacementBias.value = material.displacementBias;
- }
- }
- function refreshUniformsDistance( uniforms, material ) {
- if ( material.displacementMap ) {
- uniforms.displacementMap.value = material.displacementMap;
- uniforms.displacementScale.value = material.displacementScale;
- uniforms.displacementBias.value = material.displacementBias;
- }
- uniforms.referencePosition.value.copy( material.referencePosition );
- uniforms.nearDistance.value = material.nearDistance;
- uniforms.farDistance.value = material.farDistance;
- }
- function refreshUniformsNormal( uniforms, material ) {
- if ( material.bumpMap ) {
- uniforms.bumpMap.value = material.bumpMap;
- uniforms.bumpScale.value = material.bumpScale;
- if ( material.side === BackSide ) uniforms.bumpScale.value *= - 1;
- }
- if ( material.normalMap ) {
- uniforms.normalMap.value = material.normalMap;
- uniforms.normalScale.value.copy( material.normalScale );
- if ( material.side === BackSide ) uniforms.normalScale.value.negate();
- }
- if ( material.displacementMap ) {
- uniforms.displacementMap.value = material.displacementMap;
- uniforms.displacementScale.value = material.displacementScale;
- uniforms.displacementBias.value = material.displacementBias;
- }
- }
- return {
- refreshFogUniforms: refreshFogUniforms,
- refreshMaterialUniforms: refreshMaterialUniforms
- };
- }
- function createCanvasElement() {
- const canvas = document.createElementNS( 'http://www.w3.org/1999/xhtml', 'canvas' );
- canvas.style.display = 'block';
- return canvas;
- }
- function WebGLRenderer( parameters ) {
- parameters = parameters || {};
- const _canvas = parameters.canvas !== undefined ? parameters.canvas : createCanvasElement(),
- _context = parameters.context !== undefined ? parameters.context : null,
- _alpha = parameters.alpha !== undefined ? parameters.alpha : false,
- _depth = parameters.depth !== undefined ? parameters.depth : true,
- _stencil = parameters.stencil !== undefined ? parameters.stencil : true,
- _antialias = parameters.antialias !== undefined ? parameters.antialias : false,
- _premultipliedAlpha = parameters.premultipliedAlpha !== undefined ? parameters.premultipliedAlpha : true,
- _preserveDrawingBuffer = parameters.preserveDrawingBuffer !== undefined ? parameters.preserveDrawingBuffer : false,
- _powerPreference = parameters.powerPreference !== undefined ? parameters.powerPreference : 'default',
- _failIfMajorPerformanceCaveat = parameters.failIfMajorPerformanceCaveat !== undefined ? parameters.failIfMajorPerformanceCaveat : false;
- let currentRenderList = null;
- let currentRenderState = null;
- // render() can be called from within a callback triggered by another render.
- // We track this so that the nested render call gets its state isolated from the parent render call.
- const renderStateStack = [];
- // public properties
- this.domElement = _canvas;
- // Debug configuration container
- this.debug = {
- /**
- * Enables error checking and reporting when shader programs are being compiled
- * @type {boolean}
- */
- checkShaderErrors: true
- };
- // clearing
- this.autoClear = true;
- this.autoClearColor = true;
- this.autoClearDepth = true;
- this.autoClearStencil = true;
- // scene graph
- this.sortObjects = true;
- // user-defined clipping
- this.clippingPlanes = [];
- this.localClippingEnabled = false;
- // physically based shading
- this.gammaFactor = 2.0; // for backwards compatibility
- this.outputEncoding = LinearEncoding;
- // physical lights
- this.physicallyCorrectLights = false;
- // tone mapping
- this.toneMapping = NoToneMapping;
- this.toneMappingExposure = 1.0;
- // morphs
- this.maxMorphTargets = 8;
- this.maxMorphNormals = 4;
- // internal properties
- const _this = this;
- let _isContextLost = false;
- // internal state cache
- let _framebuffer = null;
- let _currentActiveCubeFace = 0;
- let _currentActiveMipmapLevel = 0;
- let _currentRenderTarget = null;
- let _currentFramebuffer = null;
- let _currentMaterialId = - 1;
- let _currentCamera = null;
- const _currentViewport = new Vector4();
- const _currentScissor = new Vector4();
- let _currentScissorTest = null;
- //
- let _width = _canvas.width;
- let _height = _canvas.height;
- let _pixelRatio = 1;
- let _opaqueSort = null;
- let _transparentSort = null;
- const _viewport = new Vector4( 0, 0, _width, _height );
- const _scissor = new Vector4( 0, 0, _width, _height );
- let _scissorTest = false;
- // frustum
- const _frustum = new Frustum();
- // clipping
- let _clippingEnabled = false;
- let _localClippingEnabled = false;
- // camera matrices cache
- const _projScreenMatrix = new Matrix4();
- const _vector3 = new Vector3();
- const _emptyScene = { background: null, fog: null, environment: null, overrideMaterial: null, isScene: true };
- function getTargetPixelRatio() {
- return _currentRenderTarget === null ? _pixelRatio : 1;
- }
- // initialize
- let _gl = _context;
- function getContext( contextNames, contextAttributes ) {
- for ( let i = 0; i < contextNames.length; i ++ ) {
- const contextName = contextNames[ i ];
- const context = _canvas.getContext( contextName, contextAttributes );
- if ( context !== null ) return context;
- }
- return null;
- }
- try {
- const contextAttributes = {
- alpha: _alpha,
- depth: _depth,
- stencil: _stencil,
- antialias: _antialias,
- premultipliedAlpha: _premultipliedAlpha,
- preserveDrawingBuffer: _preserveDrawingBuffer,
- powerPreference: _powerPreference,
- failIfMajorPerformanceCaveat: _failIfMajorPerformanceCaveat
- };
- // event listeners must be registered before WebGL context is created, see #12753
- _canvas.addEventListener( 'webglcontextlost', onContextLost, false );
- _canvas.addEventListener( 'webglcontextrestored', onContextRestore, false );
- if ( _gl === null ) {
- const contextNames = [ 'webgl2', 'webgl', 'experimental-webgl' ];
- if ( _this.isWebGL1Renderer === true ) {
- contextNames.shift();
- }
- _gl = getContext( contextNames, contextAttributes );
- if ( _gl === null ) {
- if ( getContext( contextNames ) ) {
- throw new Error( 'Error creating WebGL context with your selected attributes.' );
- } else {
- throw new Error( 'Error creating WebGL context.' );
- }
- }
- }
- // Some experimental-webgl implementations do not have getShaderPrecisionFormat
- if ( _gl.getShaderPrecisionFormat === undefined ) {
- _gl.getShaderPrecisionFormat = function () {
- return { 'rangeMin': 1, 'rangeMax': 1, 'precision': 1 };
- };
- }
- } catch ( error ) {
- console.error( 'THREE.WebGLRenderer: ' + error.message );
- throw error;
- }
- let extensions, capabilities, state, info;
- let properties, textures, cubemaps, attributes, geometries, objects;
- let programCache, materials, renderLists, renderStates, clipping;
- let background, morphtargets, bufferRenderer, indexedBufferRenderer;
- let utils, bindingStates;
- function initGLContext() {
- extensions = new WebGLExtensions( _gl );
- capabilities = new WebGLCapabilities( _gl, extensions, parameters );
- if ( capabilities.isWebGL2 === false ) {
- extensions.get( 'WEBGL_depth_texture' );
- extensions.get( 'OES_texture_float' );
- extensions.get( 'OES_texture_half_float' );
- extensions.get( 'OES_texture_half_float_linear' );
- extensions.get( 'OES_standard_derivatives' );
- extensions.get( 'OES_element_index_uint' );
- extensions.get( 'OES_vertex_array_object' );
- extensions.get( 'ANGLE_instanced_arrays' );
- }
- extensions.get( 'OES_texture_float_linear' );
- utils = new WebGLUtils( _gl, extensions, capabilities );
- state = new WebGLState( _gl, extensions, capabilities );
- state.scissor( _currentScissor.copy( _scissor ).multiplyScalar( _pixelRatio ).floor() );
- state.viewport( _currentViewport.copy( _viewport ).multiplyScalar( _pixelRatio ).floor() );
- info = new WebGLInfo( _gl );
- properties = new WebGLProperties();
- textures = new WebGLTextures( _gl, extensions, state, properties, capabilities, utils, info );
- cubemaps = new WebGLCubeMaps( _this );
- attributes = new WebGLAttributes( _gl, capabilities );
- bindingStates = new WebGLBindingStates( _gl, extensions, attributes, capabilities );
- geometries = new WebGLGeometries( _gl, attributes, info, bindingStates );
- objects = new WebGLObjects( _gl, geometries, attributes, info );
- morphtargets = new WebGLMorphtargets( _gl );
- clipping = new WebGLClipping( properties );
- programCache = new WebGLPrograms( _this, cubemaps, extensions, capabilities, bindingStates, clipping );
- materials = new WebGLMaterials( properties );
- renderLists = new WebGLRenderLists( properties );
- renderStates = new WebGLRenderStates( extensions, capabilities );
- background = new WebGLBackground( _this, cubemaps, state, objects, _premultipliedAlpha );
- bufferRenderer = new WebGLBufferRenderer( _gl, extensions, info, capabilities );
- indexedBufferRenderer = new WebGLIndexedBufferRenderer( _gl, extensions, info, capabilities );
- info.programs = programCache.programs;
- _this.capabilities = capabilities;
- _this.extensions = extensions;
- _this.properties = properties;
- _this.renderLists = renderLists;
- _this.state = state;
- _this.info = info;
- _this._textures = textures;//add
-
- }
- initGLContext();
- // xr
- const xr = new WebXRManager( _this, _gl );
- this.xr = xr;
- // shadow map
- const shadowMap = new WebGLShadowMap( _this, objects, capabilities.maxTextureSize );
- this.shadowMap = shadowMap;
- // API
- this.getContext = function () {
- return _gl;
- };
- this.getContextAttributes = function () {
- return _gl.getContextAttributes();
- };
- this.forceContextLoss = function () {
- const extension = extensions.get( 'WEBGL_lose_context' );
- if ( extension ) extension.loseContext();
- };
- this.forceContextRestore = function () {
- const extension = extensions.get( 'WEBGL_lose_context' );
- if ( extension ) extension.restoreContext();
- };
- this.getPixelRatio = function () {
- return _pixelRatio;
- };
- this.setPixelRatio = function ( value ) {
- if ( value === undefined ) return;
- _pixelRatio = value;
- this.setSize( _width, _height, false );
- };
- this.getSize = function ( target ) {
- if ( target === undefined ) {
- console.warn( 'WebGLRenderer: .getsize() now requires a Vector2 as an argument' );
- target = new Vector2$1();
- }
- return target.set( _width, _height );
- };
- this.setSize = function ( width, height, updateStyle, devicePixelRatio ) {//改
- if (devicePixelRatio != void 0) _pixelRatio = devicePixelRatio; //add
- if ( xr.isPresenting ) {
- console.warn( 'THREE.WebGLRenderer: Can\'t change size while VR device is presenting.' );
- return;
- }
- _width = width;
- _height = height;
-
- //if(!window.unableSetSize){
- _canvas.width = Math.floor( width * _pixelRatio );
- _canvas.height = Math.floor( height * _pixelRatio );
-
-
-
- if ( updateStyle !== false ) {
- _canvas.style.width = width + 'px';
- _canvas.style.height = height + 'px';
- }
-
-
- this.setViewport( 0, 0, width, height );
- //}
- };
- this.getDrawingBufferSize = function ( target ) {
- if ( target === undefined ) {
- console.warn( 'WebGLRenderer: .getdrawingBufferSize() now requires a Vector2 as an argument' );
- target = new Vector2$1();
- }
- return target.set( _width * _pixelRatio, _height * _pixelRatio ).floor();
- };
- this.setDrawingBufferSize = function ( width, height, pixelRatio ) {
- _width = width;
- _height = height;
- _pixelRatio = pixelRatio;
- _canvas.width = Math.floor( width * pixelRatio );
- _canvas.height = Math.floor( height * pixelRatio );
- this.setViewport( 0, 0, width, height );
- };
- this.getCurrentViewport = function ( target ) {
- if ( target === undefined ) {
- console.warn( 'WebGLRenderer: .getCurrentViewport() now requires a Vector4 as an argument' );
- target = new Vector4();
- }
- return target.copy( _currentViewport );
- };
- this.getViewport = function ( target ) {
- return target.copy( _viewport );
- };
- this.setViewport = function ( x, y, width, height ) {
- if ( x.isVector4 ) {
- _viewport.set( x.x, x.y, x.z, x.w );
- } else {
- _viewport.set( x, y, width, height );
- }
- state.viewport( _currentViewport.copy( _viewport ).multiplyScalar( _pixelRatio ).floor() );
- };
- this.getScissor = function ( target ) {
- return target.copy( _scissor );
- };
- this.setScissor = function ( x, y, width, height ) {
- if ( x.isVector4 ) {
- _scissor.set( x.x, x.y, x.z, x.w );
- } else {
- _scissor.set( x, y, width, height );
- }
- state.scissor( _currentScissor.copy( _scissor ).multiplyScalar( _pixelRatio ).floor() );
- };
- this.getScissorTest = function () {
- return _scissorTest;
- };
- this.setScissorTest = function ( boolean ) {
- state.setScissorTest( _scissorTest = boolean );
- };
- this.setOpaqueSort = function ( method ) {
- _opaqueSort = method;
- };
- this.setTransparentSort = function ( method ) {
- _transparentSort = method;
- };
- // Clearing
- this.getClearColor = function ( target ) {
- if ( target === undefined ) {
- console.warn( 'WebGLRenderer: .getClearColor() now requires a Color as an argument' );
- target = new Color();
- }
- return target.copy( background.getClearColor() );
- };
- this.setClearColor = function () {
- background.setClearColor.apply( background, arguments );
- };
- this.getClearAlpha = function () {
- return background.getClearAlpha();
- };
- this.setClearAlpha = function () {
- background.setClearAlpha.apply( background, arguments );
- };
- this.clear = function ( color, depth, stencil ) {
- let bits = 0;
- if ( color === undefined || color ) bits |= 16384;
- if ( depth === undefined || depth ) bits |= 256;
- if ( stencil === undefined || stencil ) bits |= 1024;
- _gl.clear( bits );
- };
- this.clearColor = function () {
- this.clear( true, false, false );
- };
- this.clearDepth = function () {
- this.clear( false, true, false );
- };
- this.clearStencil = function () {
- this.clear( false, false, true );
- };
- //
- this.dispose = function () {
- _canvas.removeEventListener( 'webglcontextlost', onContextLost, false );
- _canvas.removeEventListener( 'webglcontextrestored', onContextRestore, false );
- renderLists.dispose();
- renderStates.dispose();
- properties.dispose();
- cubemaps.dispose();
- objects.dispose();
- bindingStates.dispose();
- xr.dispose();
- animation.stop();
- };
- // Events
- function onContextLost( event ) {
- event.preventDefault();
- console.log( 'THREE.WebGLRenderer: Context Lost.' );
- _isContextLost = true;
- }
- function onContextRestore( /* event */ ) {
- console.log( 'THREE.WebGLRenderer: Context Restored.' );
- _isContextLost = false;
- initGLContext();
- }
- function onMaterialDispose( event ) {
- const material = event.target;
- material.removeEventListener( 'dispose', onMaterialDispose );
- deallocateMaterial( material );
- }
- // Buffer deallocation
- function deallocateMaterial( material ) {
- releaseMaterialProgramReference( material );
- properties.remove( material );
- }
- function releaseMaterialProgramReference( material ) {
- const programInfo = properties.get( material ).program;
- if ( programInfo !== undefined ) {
- programCache.releaseProgram( programInfo );
- }
- }
- // Buffer rendering
- function renderObjectImmediate( object, program ) {
- object.render( function ( object ) {
- _this.renderBufferImmediate( object, program );
- } );
- }
- this.renderBufferImmediate = function ( object, program ) {
- bindingStates.initAttributes();
- const buffers = properties.get( object );
- if ( object.hasPositions && ! buffers.position ) buffers.position = _gl.createBuffer();
- if ( object.hasNormals && ! buffers.normal ) buffers.normal = _gl.createBuffer();
- if ( object.hasUvs && ! buffers.uv ) buffers.uv = _gl.createBuffer();
- if ( object.hasColors && ! buffers.color ) buffers.color = _gl.createBuffer();
- const programAttributes = program.getAttributes();
- if ( object.hasPositions ) {
- _gl.bindBuffer( 34962, buffers.position );
- _gl.bufferData( 34962, object.positionArray, 35048 );
- bindingStates.enableAttribute( programAttributes.position );
- _gl.vertexAttribPointer( programAttributes.position, 3, 5126, false, 0, 0 );
- }
- if ( object.hasNormals ) {
- _gl.bindBuffer( 34962, buffers.normal );
- _gl.bufferData( 34962, object.normalArray, 35048 );
- bindingStates.enableAttribute( programAttributes.normal );
- _gl.vertexAttribPointer( programAttributes.normal, 3, 5126, false, 0, 0 );
- }
- if ( object.hasUvs ) {
- _gl.bindBuffer( 34962, buffers.uv );
- _gl.bufferData( 34962, object.uvArray, 35048 );
- bindingStates.enableAttribute( programAttributes.uv );
- _gl.vertexAttribPointer( programAttributes.uv, 2, 5126, false, 0, 0 );
- }
- if ( object.hasColors ) {
- _gl.bindBuffer( 34962, buffers.color );
- _gl.bufferData( 34962, object.colorArray, 35048 );
- bindingStates.enableAttribute( programAttributes.color );
- _gl.vertexAttribPointer( programAttributes.color, 3, 5126, false, 0, 0 );
- }
- bindingStates.disableUnusedAttributes();
- _gl.drawArrays( 4, 0, object.count );
- object.count = 0;
- };
- this.renderBufferDirect = function ( camera, scene, geometry, material, object, group ) {
- if ( scene === null ) scene = _emptyScene; // renderBufferDirect second parameter used to be fog (could be null)
- const frontFaceCW = ( object.isMesh && object.matrixWorld.determinant() < 0 );
- const program = setProgram( camera, scene, material, object );
- state.setMaterial( material, frontFaceCW );
- //
- let index = geometry.index;
- const position = geometry.attributes.position;
- //
- if ( index === null ) {
- if ( position === undefined || position.count === 0 ) return;
- } else if ( index.count === 0 ) {
- return;
- }
- //
- let rangeFactor = 1;
- if ( material.wireframe === true ) {
- index = geometries.getWireframeAttribute( geometry );
- rangeFactor = 2;
- }
- if ( material.morphTargets || material.morphNormals ) {
- morphtargets.update( object, geometry, material, program );
- }
- bindingStates.setup( object, material, program, geometry, index );
- let attribute;
- let renderer = bufferRenderer;
- if ( index !== null ) {
- attribute = attributes.get( index );
- renderer = indexedBufferRenderer;
- renderer.setIndex( attribute );
- }
- //
- const dataCount = ( index !== null ) ? index.count : position.count;
- const rangeStart = geometry.drawRange.start * rangeFactor;
- const rangeCount = geometry.drawRange.count * rangeFactor;
- const groupStart = group !== null ? group.start * rangeFactor : 0;
- const groupCount = group !== null ? group.count * rangeFactor : Infinity;
- const drawStart = Math.max( rangeStart, groupStart );
- const drawEnd = Math.min( dataCount, rangeStart + rangeCount, groupStart + groupCount ) - 1;
- const drawCount = Math.max( 0, drawEnd - drawStart + 1 );
- if ( drawCount === 0 ) return;
- //
- if ( object.isMesh ) {
- if ( material.wireframe === true ) {
- state.setlineWidth( material.wireframelineWidth * getTargetPixelRatio() );
- renderer.setMode( 1 );
- } else {
- renderer.setMode( 4 );
- }
- } else if ( object.isLine ) {
- let lineWidth = material.lineWidth;
- if ( lineWidth === undefined ) lineWidth = 1; // Not using Line*Material
- state.setlineWidth( lineWidth * getTargetPixelRatio() );
- if ( object.isLineSegments ) {
- renderer.setMode( 1 );
- } else if ( object.isLineLoop ) {
- renderer.setMode( 2 );
- } else {
- renderer.setMode( 3 );
- }
- } else if ( object.isPoints ) {
- renderer.setMode( 0 );
- } else if ( object.isSprite ) {
- renderer.setMode( 4 );
- }
- if ( object.isInstancedMesh ) {
- renderer.renderInstances( drawStart, drawCount, object.count );
- } else if ( geometry.isInstancedBufferGeometry ) {
- const instanceCount = Math.min( geometry.instanceCount, geometry._maxInstanceCount );
- renderer.renderInstances( drawStart, drawCount, instanceCount );
- } else {
- renderer.render( drawStart, drawCount );
- }
- };
- // Compile
- this.compile = function ( scene, camera ) {
- currentRenderState = renderStates.get( scene );
- currentRenderState.init();
- scene.traverseVisible( function ( object ) {
- if ( object.isLight && object.layers.test( camera.layers ) ) {
- currentRenderState.pushLight( object );
- if ( object.castShadow ) {
- currentRenderState.pushShadow( object );
- }
- }
- } );
- currentRenderState.setupLights();
- const compiled = new WeakMap();
- scene.traverse( function ( object ) {
- const material = object.material;
- if ( material ) {
- if ( Array.isArray( material ) ) {
- for ( let i = 0; i < material.length; i ++ ) {
- const material2 = material[ i ];
- if ( compiled.has( material2 ) === false ) {
- initMaterial( material2, scene, object );
- compiled.set( material2 );
- }
- }
- } else if ( compiled.has( material ) === false ) {
- initMaterial( material, scene, object );
- compiled.set( material );
- }
- }
- } );
- };
- // Animation Loop
- let onAnimationFrameCallback = null;
- function onAnimationFrame( time ) {
- if ( xr.isPresenting ) return;
- if ( onAnimationFrameCallback ) onAnimationFrameCallback( time );
- }
- const animation = new WebGLAnimation();
- animation.setAnimationLoop( onAnimationFrame );
- if ( typeof window !== 'undefined' ) animation.setContext( window );
- this.setAnimationLoop = function ( callback ) {
- onAnimationFrameCallback = callback;
- xr.setAnimationLoop( callback );
- ( callback === null ) ? animation.stop() : animation.start();
- };
- // Rendering
- this.render = function ( scene, camera ) {
- let renderTarget, forceClear;
- if ( arguments[ 2 ] !== undefined ) {
- console.warn( 'THREE.WebGLRenderer.render(): the renderTarget argument has been removed. Use .setRenderTarget() instead.' );
- renderTarget = arguments[ 2 ];
- }
- if ( arguments[ 3 ] !== undefined ) {
- console.warn( 'THREE.WebGLRenderer.render(): the forceClear argument has been removed. Use .clear() instead.' );
- forceClear = arguments[ 3 ];
- }
- if ( camera !== undefined && camera.isCamera !== true ) {
- console.error( 'THREE.WebGLRenderer.render: camera is not an instance of THREE.Camera.' );
- return;
- }
- if ( _isContextLost === true ) return;
- // reset caching for this frame
- bindingStates.resetDefaultState();
- _currentMaterialId = - 1;
- _currentCamera = null;
- // update scene graph
- if ( scene.autoUpdate === true ) scene.updateMatrixWorld();
- // update camera matrices and frustum
- if ( camera.parent === null ) camera.updateMatrixWorld();
- if ( xr.enabled === true && xr.isPresenting === true ) {
- camera = xr.getCamera( camera );
- }
- //
- if ( scene.isScene === true ) scene.onBeforeRender( _this, scene, camera, renderTarget || _currentRenderTarget );
- currentRenderState = renderStates.get( scene, renderStateStack.length );
- currentRenderState.init();
- renderStateStack.push( currentRenderState );
- _projScreenMatrix.multiplyMatrices( camera.projectionMatrix, camera.matrixWorldInverse );
- _frustum.setFromProjectionMatrix( _projScreenMatrix );
- _localClippingEnabled = this.localClippingEnabled;
- _clippingEnabled = clipping.init( this.clippingPlanes, _localClippingEnabled, camera );
- currentRenderList = renderLists.get( scene, camera );
- currentRenderList.init();
- projectObject( scene, camera, 0, _this.sortObjects );
- currentRenderList.finish();
- if ( _this.sortObjects === true ) {
- currentRenderList.sort( _opaqueSort, _transparentSort );
- }
- //
- if ( _clippingEnabled === true ) clipping.beginShadows();
- const shadowsArray = currentRenderState.state.shadowsArray;
- shadowMap.render( shadowsArray, scene, camera );
- currentRenderState.setupLights();
- currentRenderState.setupLightsView( camera );
- if ( _clippingEnabled === true ) clipping.endShadows();
- //
- if ( this.info.autoReset === true ) this.info.reset();
- if ( renderTarget !== undefined ) {
- this.setRenderTarget( renderTarget );
- }
- //
- background.render( currentRenderList, scene, camera, forceClear );
- // render scene
- const opaqueObjects = currentRenderList.opaque;
- const transparentObjects = currentRenderList.transparent;
- if ( opaqueObjects.length > 0 ) renderObjects( opaqueObjects, scene, camera );
- if ( transparentObjects.length > 0 ) renderObjects( transparentObjects, scene, camera );
- //
- if ( scene.isScene === true ) scene.onAfterRender( _this, scene, camera );
- //
- if ( _currentRenderTarget !== null ) {
- // Generate mipmap if we're using any kind of mipmap filtering
- textures.updateRenderTargetMipmap( _currentRenderTarget );
- // resolve multisample renderbuffers to a single-sample texture if necessary
- textures.updateMultisampleRenderTarget( _currentRenderTarget );
- }
- // Ensure depth buffer writing is enabled so it can be cleared on next render
- state.buffers.depth.setTest( true );
- state.buffers.depth.setMask( true );
- state.buffers.color.setMask( true );
- state.setPolygonOffset( false );
- // _gl.finish();
- renderStateStack.pop();
- if ( renderStateStack.length > 0 ) {
- currentRenderState = renderStateStack[ renderStateStack.length - 1 ];
- } else {
- currentRenderState = null;
- }
- currentRenderList = null;
- };
- function projectObject( object, camera, groupOrder, sortObjects ) {
- if ( object.visible === false ) return;
- const visible = object.layers.test( camera.layers );
- if ( visible ) {
- if ( object.isGroup ) {
- groupOrder = object.renderOrder;
- } else if ( object.isLOD ) {
- if ( object.autoUpdate === true ) object.update( camera );
- } else if ( object.isLight ) {
- currentRenderState.pushLight( object );
- if ( object.castShadow ) {
- currentRenderState.pushShadow( object );
- }
- } else if ( object.isSprite ) {
- if ( ! object.frustumCulled || _frustum.intersectsSprite( object ) ) {
- if ( sortObjects ) {
- _vector3.setFromMatrixPosition( object.matrixWorld )
- .applyMatrix4( _projScreenMatrix );
- }
- const geometry = objects.update( object );
- const material = object.material;
- if ( material.visible ) {
- currentRenderList.push( object, geometry, material, groupOrder, _vector3.z, null );
- }
- }
- } else if ( object.isImmediateRenderObject ) {
- if ( sortObjects ) {
- _vector3.setFromMatrixPosition( object.matrixWorld )
- .applyMatrix4( _projScreenMatrix );
- }
- currentRenderList.push( object, null, object.material, groupOrder, _vector3.z, null );
- } else if ( object.isMesh || object.isLine || object.isPoints ) {
- if ( object.isSkinnedMesh ) {
- // update skeleton only once in a frame
- if ( object.skeleton.frame !== info.render.frame ) {
- object.skeleton.update();
- object.skeleton.frame = info.render.frame;
- }
- }
- if ( ! object.frustumCulled || _frustum.intersectsObject( object ) ) {
- if ( sortObjects ) {
- _vector3.setFromMatrixPosition( object.matrixWorld )
- .applyMatrix4( _projScreenMatrix );
- }
- const geometry = objects.update( object );
- const material = object.material;
- if ( Array.isArray( material ) ) {
- const groups = geometry.groups;
- for ( let i = 0, l = groups.length; i < l; i ++ ) {
- const group = groups[ i ];
- const groupMaterial = material[ group.materialIndex ];
- if ( groupMaterial && groupMaterial.visible ) {
- currentRenderList.push( object, geometry, groupMaterial, groupOrder, _vector3.z, group );
- }
- }
- } else if ( material.visible ) {
- currentRenderList.push( object, geometry, material, groupOrder, _vector3.z, null );
- }
- }
- }
- }
- const children = object.children;
- for ( let i = 0, l = children.length; i < l; i ++ ) {
- projectObject( children[ i ], camera, groupOrder, sortObjects );
- }
- }
- function renderObjects( renderList, scene, camera ) {
- const overrideMaterial = scene.isScene === true ? scene.overrideMaterial : null;
- for ( let i = 0, l = renderList.length; i < l; i ++ ) {
- const renderItem = renderList[ i ];
- const object = renderItem.object;
- const geometry = renderItem.geometry;
- const material = overrideMaterial === null ? renderItem.material : overrideMaterial;
- const group = renderItem.group;
- if ( camera.isArrayCamera ) {
- const cameras = camera.cameras;
- for ( let j = 0, jl = cameras.length; j < jl; j ++ ) {
- const camera2 = cameras[ j ];
- if ( object.layers.test( camera2.layers ) ) {
- state.viewport( _currentViewport.copy( camera2.viewport ) );
- currentRenderState.setupLightsView( camera2 );
- renderObject( object, scene, camera2, geometry, material, group );
- }
- }
- } else {
- renderObject( object, scene, camera, geometry, material, group );
- }
- }
- }
- function renderObject( object, scene, camera, geometry, material, group ) {
- object.onBeforeRender( _this, scene, camera, geometry, material, group );
- object.modelViewMatrix.multiplyMatrices( camera.matrixWorldInverse, object.matrixWorld );
- object.normalMatrix.getNormalMatrix( object.modelViewMatrix );
- if ( object.isImmediateRenderObject ) {
- const program = setProgram( camera, scene, material, object );
- state.setMaterial( material );
- bindingStates.reset();
- renderObjectImmediate( object, program );
- } else {
- _this.renderBufferDirect( camera, scene, geometry, material, object, group );
- }
- object.onAfterRender( _this, scene, camera, geometry, material, group );
- }
- function initMaterial( material, scene, object ) {
- if ( scene.isScene !== true ) scene = _emptyScene; // scene could be a Mesh, Line, Points, ...
- const materialProperties = properties.get( material );
- const lights = currentRenderState.state.lights;
- const shadowsArray = currentRenderState.state.shadowsArray;
- const lightsStateVersion = lights.state.version;
- const parameters = programCache.getParameters( material, lights.state, shadowsArray, scene, object );
- const programCacheKey = programCache.getProgramCacheKey( parameters );
- let program = materialProperties.program;
- let programChange = true;
- if ( program === undefined ) {
- // new material
- material.addEventListener( 'dispose', onMaterialDispose );
- } else if ( program.cacheKey !== programCacheKey ) {
- // changed glsl or parameters
- releaseMaterialProgramReference( material );
- } else if ( materialProperties.lightsStateVersion !== lightsStateVersion ) {
- programChange = false;
- } else if ( parameters.shaderID !== undefined ) {
- // same glsl and uniform list, envMap still needs the update here to avoid a frame-late effect
- const environment = material.isMeshStandardMaterial ? scene.environment : null;
- materialProperties.envMap = cubemaps.get( material.envMap || environment );
- return;
- } else {
- // only rebuild uniform list
- programChange = false;
- }
- if ( programChange ) {
- parameters.uniforms = programCache.getUniforms( material );
- material.onBeforeCompile( parameters, _this );
- program = programCache.acquireProgram( parameters, programCacheKey );
- materialProperties.program = program;
- materialProperties.uniforms = parameters.uniforms;
- materialProperties.outputEncoding = parameters.outputEncoding;
- }
- const uniforms = materialProperties.uniforms;
- if ( ! material.isShaderMaterial &&
- ! material.isRawShaderMaterial ||
- material.clipping === true ) {
- materialProperties.numClippingPlanes = clipping.numPlanes;
- materialProperties.numIntersection = clipping.numIntersection;
- uniforms.clippingPlanes = clipping.uniform;
- }
- materialProperties.environment = material.isMeshStandardMaterial ? scene.environment : null;
- materialProperties.fog = scene.fog;
- materialProperties.envMap = cubemaps.get( material.envMap || materialProperties.environment );
- // store the light setup it was created for
- materialProperties.needsLights = materialNeedsLights( material );
- materialProperties.lightsStateVersion = lightsStateVersion;
- if ( materialProperties.needsLights ) {
- // wire up the material to this renderer's lighting state
- uniforms.ambientLightColor.value = lights.state.ambient;
- uniforms.lightProbe.value = lights.state.probe;
- uniforms.directionalLights.value = lights.state.directional;
- uniforms.directionalLightShadows.value = lights.state.directionalShadow;
- uniforms.spotLights.value = lights.state.spot;
- uniforms.spotLightShadows.value = lights.state.spotShadow;
- uniforms.rectAreaLights.value = lights.state.rectArea;
- uniforms.ltc_1.value = lights.state.rectAreaLTC1;
- uniforms.ltc_2.value = lights.state.rectAreaLTC2;
- uniforms.pointLights.value = lights.state.point;
- uniforms.pointLightShadows.value = lights.state.pointShadow;
- uniforms.hemisphereLights.value = lights.state.hemi;
- uniforms.directionalShadowMap.value = lights.state.directionalShadowMap;
- uniforms.directionalShadowMatrix.value = lights.state.directionalShadowMatrix;
- uniforms.spotShadowMap.value = lights.state.spotShadowMap;
- uniforms.spotShadowMatrix.value = lights.state.spotShadowMatrix;
- uniforms.pointShadowMap.value = lights.state.pointShadowMap;
- uniforms.pointShadowMatrix.value = lights.state.pointShadowMatrix;
- // TODO (abelnation): add area lights shadow info to uniforms
- }
- const progUniforms = materialProperties.program.getUniforms();
- const uniformsList = WebGLUniforms.seqWithValue( progUniforms.seq, uniforms );
- materialProperties.uniformsList = uniformsList;
- }
- function setProgram( camera, scene, material, object ) {
- if ( scene.isScene !== true ) scene = _emptyScene; // scene could be a Mesh, Line, Points, ...
- textures.resetTextureUnits();
- const fog = scene.fog;
- const environment = material.isMeshStandardMaterial ? scene.environment : null;
- const encoding = ( _currentRenderTarget === null ) ? _this.outputEncoding : _currentRenderTarget.texture.encoding;
- const envMap = cubemaps.get( material.envMap || environment );
- const materialProperties = properties.get( material );
- const lights = currentRenderState.state.lights;
- if ( _clippingEnabled === true ) {
- if ( _localClippingEnabled === true || camera !== _currentCamera ) {
- const useCache =
- camera === _currentCamera &&
- material.id === _currentMaterialId;
- // we might want to call this function with some ClippingGroup
- // object instead of the material, once it becomes feasible
- // (#8465, #8379)
- clipping.setState( material, camera, useCache );
- }
- }
- if ( material.version === materialProperties.__version ) {
- if ( material.fog && materialProperties.fog !== fog ) {
- initMaterial( material, scene, object );
- } else if ( materialProperties.environment !== environment ) {
- initMaterial( material, scene, object );
- } else if ( materialProperties.needsLights && ( materialProperties.lightsStateVersion !== lights.state.version ) ) {
- initMaterial( material, scene, object );
- } else if ( materialProperties.numClippingPlanes !== undefined &&
- ( materialProperties.numClippingPlanes !== clipping.numPlanes ||
- materialProperties.numIntersection !== clipping.numIntersection ) ) {
- initMaterial( material, scene, object );
- } else if ( materialProperties.outputEncoding !== encoding ) {
- initMaterial( material, scene, object );
- } else if ( materialProperties.envMap !== envMap ) {
- initMaterial( material, scene, object );
- }
- } else {
- initMaterial( material, scene, object );
- materialProperties.__version = material.version;
- }
- let refreshProgram = false;
- let refreshMaterial = false;
- let refreshLights = false;
- const program = materialProperties.program,
- p_uniforms = program.getUniforms(),
- m_uniforms = materialProperties.uniforms;
- if ( state.useProgram( program.program ) ) {
- refreshProgram = true;
- refreshMaterial = true;
- refreshLights = true;
- }
- if ( material.id !== _currentMaterialId ) {
- _currentMaterialId = material.id;
- refreshMaterial = true;
- }
- if ( refreshProgram || _currentCamera !== camera ) {
- p_uniforms.setValue( _gl, 'projectionMatrix', camera.projectionMatrix );
- if ( capabilities.logarithmicDepthBuffer ) {
- p_uniforms.setValue( _gl, 'logDepthBufFC',
- 2.0 / ( Math.log( camera.far + 1.0 ) / Math.LN2 ) );
- }
- if ( _currentCamera !== camera ) {
- _currentCamera = camera;
- // lighting uniforms depend on the camera so enforce an update
- // now, in case this material supports lights - or later, when
- // the next material that does gets activated:
- refreshMaterial = true; // set to true on material change
- refreshLights = true; // remains set until update done
- }
- // load material specific uniforms
- // (shader material also gets them for the sake of genericity)
- if ( material.isShaderMaterial ||
- material.isMeshPhongMaterial ||
- material.isMeshToonMaterial ||
- material.isMeshStandardMaterial ||
- material.envMap ) {
- const uCamPos = p_uniforms.map.cameraPosition;
- if ( uCamPos !== undefined ) {
- uCamPos.setValue( _gl,
- _vector3.setFromMatrixPosition( camera.matrixWorld ) );
- }
- }
- if ( material.isMeshPhongMaterial ||
- material.isMeshToonMaterial ||
- material.isMeshLambertMaterial ||
- material.isMeshBasicMaterial ||
- material.isMeshStandardMaterial ||
- material.isShaderMaterial ) {
- p_uniforms.setValue( _gl, 'isOrthographic', camera.isOrthographicCamera === true );
- }
- if ( material.isMeshPhongMaterial ||
- material.isMeshToonMaterial ||
- material.isMeshLambertMaterial ||
- material.isMeshBasicMaterial ||
- material.isMeshStandardMaterial ||
- material.isShaderMaterial ||
- material.isShadowMaterial ||
- material.skinning ) {
- p_uniforms.setValue( _gl, 'viewMatrix', camera.matrixWorldInverse );
- }
- }
- // skinning uniforms must be set even if material didn't change
- // auto-setting of texture unit for bone texture must go before other textures
- // otherwise textures used for skinning can take over texture units reserved for other material textures
- if ( material.skinning ) {
- p_uniforms.setOptional( _gl, object, 'bindMatrix' );
- p_uniforms.setOptional( _gl, object, 'bindMatrixInverse' );
- const skeleton = object.skeleton;
- if ( skeleton ) {
- const bones = skeleton.bones;
- if ( capabilities.floatVertexTextures ) {
- if ( skeleton.boneTexture === null ) {
- // layout (1 matrix = 4 pixels)
- // RGBA RGBA RGBA RGBA (=> column1, column2, column3, column4)
- // with 8x8 pixel texture max 16 bones * 4 pixels = (8 * 8)
- // 16x16 pixel texture max 64 bones * 4 pixels = (16 * 16)
- // 32x32 pixel texture max 256 bones * 4 pixels = (32 * 32)
- // 64x64 pixel texture max 1024 bones * 4 pixels = (64 * 64)
- let size = Math.sqrt( bones.length * 4 ); // 4 pixels needed for 1 matrix
- size = MathUtils.ceilPowerOfTwo( size );
- size = Math.max( size, 4 );
- const boneMatrices = new Float32Array( size * size * 4 ); // 4 floats per RGBA pixel
- boneMatrices.set( skeleton.boneMatrices ); // copy current values
- const boneTexture = new DataTexture( boneMatrices, size, size, RGBAFormat, FloatType );
- skeleton.boneMatrices = boneMatrices;
- skeleton.boneTexture = boneTexture;
- skeleton.boneTextureSize = size;
- }
- p_uniforms.setValue( _gl, 'boneTexture', skeleton.boneTexture, textures );
- p_uniforms.setValue( _gl, 'boneTextureSize', skeleton.boneTextureSize );
- } else {
- p_uniforms.setOptional( _gl, skeleton, 'boneMatrices' );
- }
- }
- }
- if ( refreshMaterial || materialProperties.receiveShadow !== object.receiveShadow ) {
- materialProperties.receiveShadow = object.receiveShadow;
- p_uniforms.setValue( _gl, 'receiveShadow', object.receiveShadow );
- }
- if ( refreshMaterial ) {
- p_uniforms.setValue( _gl, 'toneMappingExposure', _this.toneMappingExposure );
- if ( materialProperties.needsLights ) {
- // the current material requires lighting info
- // note: all lighting uniforms are always set correctly
- // they simply reference the renderer's state for their
- // values
- //
- // use the current material's .needsUpdate flags to set
- // the GL state when required
- markUniformsLightsNeedsUpdate( m_uniforms, refreshLights );
- }
- // refresh uniforms common to several materials
- if ( fog && material.fog ) {
- materials.refreshFogUniforms( m_uniforms, fog );
- }
- materials.refreshMaterialUniforms( m_uniforms, material, _pixelRatio, _height );
- WebGLUniforms.upload( _gl, materialProperties.uniformsList, m_uniforms, textures );
- }
- if ( material.isShaderMaterial && material.uniformsNeedUpdate === true ) {
- WebGLUniforms.upload( _gl, materialProperties.uniformsList, m_uniforms, textures );
- material.uniformsNeedUpdate = false;
- }
- if ( material.isSpriteMaterial ) {
- p_uniforms.setValue( _gl, 'center', object.center );
- }
- // common matrices
- p_uniforms.setValue( _gl, 'modelViewMatrix', object.modelViewMatrix );
- p_uniforms.setValue( _gl, 'normalMatrix', object.normalMatrix );
- p_uniforms.setValue( _gl, 'modelMatrix', object.matrixWorld );
- return program;
- }
- // If uniforms are marked as clean, they don't need to be loaded to the GPU.
- function markUniformsLightsNeedsUpdate( uniforms, value ) {
- uniforms.ambientLightColor.needsUpdate = value;
- uniforms.lightProbe.needsUpdate = value;
- uniforms.directionalLights.needsUpdate = value;
- uniforms.directionalLightShadows.needsUpdate = value;
- uniforms.pointLights.needsUpdate = value;
- uniforms.pointLightShadows.needsUpdate = value;
- uniforms.spotLights.needsUpdate = value;
- uniforms.spotLightShadows.needsUpdate = value;
- uniforms.rectAreaLights.needsUpdate = value;
- uniforms.hemisphereLights.needsUpdate = value;
- }
- function materialNeedsLights( material ) {
- return material.isMeshLambertMaterial || material.isMeshToonMaterial || material.isMeshPhongMaterial ||
- material.isMeshStandardMaterial || material.isShadowMaterial ||
- ( material.isShaderMaterial && material.lights === true );
- }
- //
- this.setFramebuffer = function ( value ) {
- if ( _framebuffer !== value && _currentRenderTarget === null ) _gl.bindFramebuffer( 36160, value );
- _framebuffer = value;
- };
- this.getActiveCubeFace = function () {
- return _currentActiveCubeFace;
- };
- this.getActiveMipmapLevel = function () {
- return _currentActiveMipmapLevel;
- };
- this.getRenderList = function () {
- return currentRenderList;
- };
- this.setRenderList = function ( renderList ) {
- currentRenderList = renderList;
- };
- this.getRenderTarget = function () {
- return _currentRenderTarget;
- };
- this.setRenderTarget = function ( renderTarget, activeCubeFace = 0, activeMipmapLevel = 0 ) {
- _currentRenderTarget = renderTarget;
- _currentActiveCubeFace = activeCubeFace;
- _currentActiveMipmapLevel = activeMipmapLevel;
- if ( renderTarget && properties.get( renderTarget ).__webglFramebuffer === undefined ) {
- textures.setupRenderTarget( renderTarget );
- }
- let framebuffer = _framebuffer;
- let isCube = false;
- if ( renderTarget ) {
- const __webglFramebuffer = properties.get( renderTarget ).__webglFramebuffer;
- if ( renderTarget.isWebGLCubeRenderTarget ) {
- framebuffer = __webglFramebuffer[ activeCubeFace ];
- isCube = true;
- } else if ( renderTarget.isWebGLMultisampleRenderTarget ) {
- framebuffer = properties.get( renderTarget ).__webglMultisampledFramebuffer;
- } else {
- framebuffer = __webglFramebuffer;
- }
- _currentViewport.copy( renderTarget.viewport );
- _currentScissor.copy( renderTarget.scissor );
- _currentScissorTest = renderTarget.scissorTest;
- } else {
- _currentViewport.copy( _viewport ).multiplyScalar( _pixelRatio ).floor();
- _currentScissor.copy( _scissor ).multiplyScalar( _pixelRatio ).floor();
- _currentScissorTest = _scissorTest;
- }
- if ( _currentFramebuffer !== framebuffer ) {
- _gl.bindFramebuffer( 36160, framebuffer );
- _currentFramebuffer = framebuffer;
- }
- state.viewport( _currentViewport );
- state.scissor( _currentScissor );
- state.setScissorTest( _currentScissorTest );
- if ( isCube ) {
- const textureProperties = properties.get( renderTarget.texture );
- _gl.framebufferTexture2D( 36160, 36064, 34069 + activeCubeFace, textureProperties.__webglTexture, activeMipmapLevel );
- }
- };
- this.readRenderTargetPixels = function ( renderTarget, x, y, width, height, buffer, activeCubeFaceIndex ) {
- if ( ! ( renderTarget && renderTarget.isWebGLRenderTarget ) ) {
- console.error( 'THREE.WebGLRenderer.readRenderTargetPixels: renderTarget is not THREE.WebGLRenderTarget.' );
- return;
- }
- let framebuffer = properties.get( renderTarget ).__webglFramebuffer;
- if ( renderTarget.isWebGLCubeRenderTarget && activeCubeFaceIndex !== undefined ) {
- framebuffer = framebuffer[ activeCubeFaceIndex ];
- }
- if ( framebuffer ) {
- let restore = false;
- if ( framebuffer !== _currentFramebuffer ) {
- _gl.bindFramebuffer( 36160, framebuffer );
- restore = true;
- }
- try {
- const texture = renderTarget.texture;
- const textureFormat = texture.format;
- const textureType = texture.type;
- if ( textureFormat !== RGBAFormat && utils.convert( textureFormat ) !== _gl.getParameter( 35739 ) ) {
- console.error( 'THREE.WebGLRenderer.readRenderTargetPixels: renderTarget is not in RGBA or implementation defined format.' );
- return;
- }
- if ( textureType !== UnsignedByteType && utils.convert( textureType ) !== _gl.getParameter( 35738 ) && // IE11, Edge and Chrome Mac < 52 (#9513)
- ! ( textureType === FloatType && ( capabilities.isWebGL2 || extensions.get( 'OES_texture_float' ) || extensions.get( 'WEBGL_color_buffer_float' ) ) ) && // Chrome Mac >= 52 and Firefox
- ! ( textureType === HalfFloatType && ( capabilities.isWebGL2 ? extensions.get( 'EXT_color_buffer_float' ) : extensions.get( 'EXT_color_buffer_half_float' ) ) ) ) {
- console.error( 'THREE.WebGLRenderer.readRenderTargetPixels: renderTarget is not in UnsignedByteType or implementation defined type.' );
- return;
- }
- if ( _gl.checkFramebufferStatus( 36160 ) === 36053 ) {
- // the following if statement ensures valid read requests (no out-of-bounds pixels, see #8604)
- if ( ( x >= 0 && x <= ( renderTarget.width - width ) ) && ( y >= 0 && y <= ( renderTarget.height - height ) ) ) {
- _gl.readPixels( x, y, width, height, utils.convert( textureFormat ), utils.convert( textureType ), buffer );
- }
- } else {
- console.error( 'THREE.WebGLRenderer.readRenderTargetPixels: readPixels from renderTarget failed. Framebuffer not complete.' );
- }
- } finally {
- if ( restore ) {
- _gl.bindFramebuffer( 36160, _currentFramebuffer );
- }
- }
- }
- };
- this.copyFramebufferToTexture = function ( position, texture, level = 0 ) {
- const levelScale = Math.pow( 2, - level );
- const width = Math.floor( texture.image.width * levelScale );
- const height = Math.floor( texture.image.height * levelScale );
- const glFormat = utils.convert( texture.format );
- textures.setTexture2D( texture, 0 );
- _gl.copyTexImage2D( 3553, level, glFormat, position.x, position.y, width, height, 0 );
- state.unbindTexture();
- };
- this.copyTextureToTexture = function ( position, srcTexture, dstTexture, level = 0 ) {
- const width = srcTexture.image.width;
- const height = srcTexture.image.height;
- const glFormat = utils.convert( dstTexture.format );
- const glType = utils.convert( dstTexture.type );
- textures.setTexture2D( dstTexture, 0 );
- // As another texture upload may have changed pixelStorei
- // parameters, make sure they are correct for the dstTexture
- _gl.pixelStorei( 37440, dstTexture.flipY );
- _gl.pixelStorei( 37441, dstTexture.premultiplyAlpha );
- _gl.pixelStorei( 3317, dstTexture.unpackAlignment );
- if ( srcTexture.isDataTexture ) {
- _gl.texSubImage2D( 3553, level, position.x, position.y, width, height, glFormat, glType, srcTexture.image.data );
- } else {
- if ( srcTexture.isCompressedTexture ) {
- _gl.compressedTexSubImage2D( 3553, level, position.x, position.y, srcTexture.mipmaps[ 0 ].width, srcTexture.mipmaps[ 0 ].height, glFormat, srcTexture.mipmaps[ 0 ].data );
- } else {
- _gl.texSubImage2D( 3553, level, position.x, position.y, glFormat, glType, srcTexture.image );
- }
- }
- // Generate mipmaps only when copying level 0
- if ( level === 0 && dstTexture.generateMipmaps ) _gl.generateMipmap( 3553 );
- state.unbindTexture();
- };
- this.initTexture = function ( texture ) {
- textures.setTexture2D( texture, 0 );
- state.unbindTexture();
- };
- this.resetState = function () {
- state.reset();
- bindingStates.reset();
- };
- if ( typeof __THREE_DEVTOOLS__ !== 'undefined' ) {
- __THREE_DEVTOOLS__.dispatchEvent( new CustomEvent( 'observe', { detail: this } ) ); // eslint-disable-line no-undef
- }
- }
- function WebGL1Renderer( parameters ) {
- WebGLRenderer.call( this, parameters );
- }
- WebGL1Renderer.prototype = Object.assign( Object.create( WebGLRenderer.prototype ), {
- constructor: WebGL1Renderer,
- isWebGL1Renderer: true
- } );
- class FogExp2 {
- constructor( color, density ) {
- Object.defineProperty( this, 'isFogExp2', { value: true } );
- this.name = '';
- this.color = new Color( color );
- this.density = ( density !== undefined ) ? density : 0.00025;
- }
- clone() {
- return new FogExp2( this.color, this.density );
- }
- toJSON( /* meta */ ) {
- return {
- type: 'FogExp2',
- color: this.color.getHex(),
- density: this.density
- };
- }
- }
- class Fog {
- constructor( color, near, far ) {
- Object.defineProperty( this, 'isFog', { value: true } );
- this.name = '';
- this.color = new Color( color );
- this.near = ( near !== undefined ) ? near : 1;
- this.far = ( far !== undefined ) ? far : 1000;
- }
- clone() {
- return new Fog( this.color, this.near, this.far );
- }
- toJSON( /* meta */ ) {
- return {
- type: 'Fog',
- color: this.color.getHex(),
- near: this.near,
- far: this.far
- };
- }
- }
- class Scene extends Object3D {
- constructor() {
- super();
- Object.defineProperty( this, 'isScene', { value: true } );
- this.type = 'Scene';
- this.background = null;
- this.environment = null;
- this.fog = null;
- this.overrideMaterial = null;
- this.autoUpdate = true; // checked by the renderer
- if ( typeof __THREE_DEVTOOLS__ !== 'undefined' ) {
- __THREE_DEVTOOLS__.dispatchEvent( new CustomEvent( 'observe', { detail: this } ) ); // eslint-disable-line no-undef
- }
- }
- copy( source, recursive ) {
- super.copy( source, recursive );
- if ( source.background !== null ) this.background = source.background.clone();
- if ( source.environment !== null ) this.environment = source.environment.clone();
- if ( source.fog !== null ) this.fog = source.fog.clone();
- if ( source.overrideMaterial !== null ) this.overrideMaterial = source.overrideMaterial.clone();
- this.autoUpdate = source.autoUpdate;
- this.matrixAutoUpdate = source.matrixAutoUpdate;
- return this;
- }
- toJSON( meta ) {
- const data = super.toJSON( meta );
- if ( this.background !== null ) data.object.background = this.background.toJSON( meta );
- if ( this.environment !== null ) data.object.environment = this.environment.toJSON( meta );
- if ( this.fog !== null ) data.object.fog = this.fog.toJSON();
- return data;
- }
- }
- function InterleavedBuffer( array, stride ) {
- this.array = array;
- this.stride = stride;
- this.count = array !== undefined ? array.length / stride : 0;
- this.usage = StaticDrawUsage;
- this.updateRange = { offset: 0, count: - 1 };
- this.version = 0;
- this.uuid = MathUtils.generateUUID();
- }
- Object.defineProperty( InterleavedBuffer.prototype, 'needsUpdate', {
- set: function ( value ) {
- if ( value === true ) this.version ++;
- }
- } );
- Object.assign( InterleavedBuffer.prototype, {
- isInterleavedBuffer: true,
- onUploadCallback: function () {},
- setUsage: function ( value ) {
- this.usage = value;
- return this;
- },
- copy: function ( source ) {
- this.array = new source.array.constructor( source.array );
- this.count = source.count;
- this.stride = source.stride;
- this.usage = source.usage;
- return this;
- },
- copyAt: function ( index1, attribute, index2 ) {
- index1 *= this.stride;
- index2 *= attribute.stride;
- for ( let i = 0, l = this.stride; i < l; i ++ ) {
- this.array[ index1 + i ] = attribute.array[ index2 + i ];
- }
- return this;
- },
- set: function ( value, offset = 0 ) {
- this.array.set( value, offset );
- return this;
- },
- clone: function ( data ) {
- if ( data.arrayBuffers === undefined ) {
- data.arrayBuffers = {};
- }
- if ( this.array.buffer._uuid === undefined ) {
- this.array.buffer._uuid = MathUtils.generateUUID();
- }
- if ( data.arrayBuffers[ this.array.buffer._uuid ] === undefined ) {
- data.arrayBuffers[ this.array.buffer._uuid ] = this.array.slice( 0 ).buffer;
- }
- const array = new this.array.constructor( data.arrayBuffers[ this.array.buffer._uuid ] );
- const ib = new InterleavedBuffer( array, this.stride );
- ib.setUsage( this.usage );
- return ib;
- },
- onUpload: function ( callback ) {
- this.onUploadCallback = callback;
- return this;
- },
- toJSON: function ( data ) {
- if ( data.arrayBuffers === undefined ) {
- data.arrayBuffers = {};
- }
- // generate UUID for array buffer if necessary
- if ( this.array.buffer._uuid === undefined ) {
- this.array.buffer._uuid = MathUtils.generateUUID();
- }
- if ( data.arrayBuffers[ this.array.buffer._uuid ] === undefined ) {
- data.arrayBuffers[ this.array.buffer._uuid ] = Array.prototype.slice.call( new Uint32Array( this.array.buffer ) );
- }
- //
- return {
- uuid: this.uuid,
- buffer: this.array.buffer._uuid,
- type: this.array.constructor.name,
- stride: this.stride
- };
- }
- } );
- const _vector$6 = new Vector3();
- function InterleavedBufferAttribute( interleavedBuffer, itemSize, offset, normalized ) {
- this.name = '';
- this.data = interleavedBuffer;
- this.itemSize = itemSize;
- this.offset = offset;
- this.normalized = normalized === true;
- }
- Object.defineProperties( InterleavedBufferAttribute.prototype, {
- count: {
- get: function () {
- return this.data.count;
- }
- },
- array: {
- get: function () {
- return this.data.array;
- }
- },
- needsUpdate: {
- set: function ( value ) {
- this.data.needsUpdate = value;
- }
- }
- } );
- Object.assign( InterleavedBufferAttribute.prototype, {
- isInterleavedBufferAttribute: true,
- applyMatrix4: function ( m ) {
- for ( let i = 0, l = this.data.count; i < l; i ++ ) {
- _vector$6.x = this.getX( i );
- _vector$6.y = this.getY( i );
- _vector$6.z = this.getZ( i );
- _vector$6.applyMatrix4( m );
- this.setXYZ( i, _vector$6.x, _vector$6.y, _vector$6.z );
- }
- return this;
- },
- setX: function ( index, x ) {
- this.data.array[ index * this.data.stride + this.offset ] = x;
- return this;
- },
- setY: function ( index, y ) {
- this.data.array[ index * this.data.stride + this.offset + 1 ] = y;
- return this;
- },
- setZ: function ( index, z ) {
- this.data.array[ index * this.data.stride + this.offset + 2 ] = z;
- return this;
- },
- setW: function ( index, w ) {
- this.data.array[ index * this.data.stride + this.offset + 3 ] = w;
- return this;
- },
- getX: function ( index ) {
- return this.data.array[ index * this.data.stride + this.offset ];
- },
- getY: function ( index ) {
- return this.data.array[ index * this.data.stride + this.offset + 1 ];
- },
- getZ: function ( index ) {
- return this.data.array[ index * this.data.stride + this.offset + 2 ];
- },
- getW: function ( index ) {
- return this.data.array[ index * this.data.stride + this.offset + 3 ];
- },
- setXY: function ( index, x, y ) {
- index = index * this.data.stride + this.offset;
- this.data.array[ index + 0 ] = x;
- this.data.array[ index + 1 ] = y;
- return this;
- },
- setXYZ: function ( index, x, y, z ) {
- index = index * this.data.stride + this.offset;
- this.data.array[ index + 0 ] = x;
- this.data.array[ index + 1 ] = y;
- this.data.array[ index + 2 ] = z;
- return this;
- },
- setXYZW: function ( index, x, y, z, w ) {
- index = index * this.data.stride + this.offset;
- this.data.array[ index + 0 ] = x;
- this.data.array[ index + 1 ] = y;
- this.data.array[ index + 2 ] = z;
- this.data.array[ index + 3 ] = w;
- return this;
- },
- clone: function ( data ) {
- if ( data === undefined ) {
- console.log( 'THREE.InterleavedBufferAttribute.clone(): Cloning an interlaved buffer attribute will deinterleave buffer data.' );
- const array = [];
- for ( let i = 0; i < this.count; i ++ ) {
- const index = i * this.data.stride + this.offset;
- for ( let j = 0; j < this.itemSize; j ++ ) {
- array.push( this.data.array[ index + j ] );
- }
- }
- return new BufferAttribute( new this.array.constructor( array ), this.itemSize, this.normalized );
- } else {
- if ( data.interleavedBuffers === undefined ) {
- data.interleavedBuffers = {};
- }
- if ( data.interleavedBuffers[ this.data.uuid ] === undefined ) {
- data.interleavedBuffers[ this.data.uuid ] = this.data.clone( data );
- }
- return new InterleavedBufferAttribute( data.interleavedBuffers[ this.data.uuid ], this.itemSize, this.offset, this.normalized );
- }
- },
- toJSON: function ( data ) {
- if ( data === undefined ) {
- console.log( 'THREE.InterleavedBufferAttribute.toJSON(): Serializing an interlaved buffer attribute will deinterleave buffer data.' );
- const array = [];
- for ( let i = 0; i < this.count; i ++ ) {
- const index = i * this.data.stride + this.offset;
- for ( let j = 0; j < this.itemSize; j ++ ) {
- array.push( this.data.array[ index + j ] );
- }
- }
- // deinterleave data and save it as an ordinary buffer attribute for now
- return {
- itemSize: this.itemSize,
- type: this.array.constructor.name,
- array: array,
- normalized: this.normalized
- };
- } else {
- // save as true interlaved attribtue
- if ( data.interleavedBuffers === undefined ) {
- data.interleavedBuffers = {};
- }
- if ( data.interleavedBuffers[ this.data.uuid ] === undefined ) {
- data.interleavedBuffers[ this.data.uuid ] = this.data.toJSON( data );
- }
- return {
- isInterleavedBufferAttribute: true,
- itemSize: this.itemSize,
- data: this.data.uuid,
- offset: this.offset,
- normalized: this.normalized
- };
- }
- }
- } );
- /**
- * parameters = {
- * color: <hex>,
- * map: new THREE.Texture( <Image> ),
- * alphaMap: new THREE.Texture( <Image> ),
- * rotation: <float>,
- * sizeAttenuation: <bool>
- * }
- */
- function SpriteMaterial( parameters ) {
- Material.call( this );
- this.type = 'SpriteMaterial';
- this.color = new Color( 0xffffff );
- this.map = null;
- this.alphaMap = null;
- this.rotation = 0;
- this.sizeAttenuation = true;
- this.transparent = true;
- this.setValues( parameters );
- }
- SpriteMaterial.prototype = Object.create( Material.prototype );
- SpriteMaterial.prototype.constructor = SpriteMaterial;
- SpriteMaterial.prototype.isSpriteMaterial = true;
- SpriteMaterial.prototype.copy = function ( source ) {
- Material.prototype.copy.call( this, source );
- this.color.copy( source.color );
- this.map = source.map;
- this.alphaMap = source.alphaMap;
- this.rotation = source.rotation;
- this.sizeAttenuation = source.sizeAttenuation;
- return this;
- };
- let _geometry;
- const _intersectPoint = new Vector3();
- const _worldScale = new Vector3();
- const _mvPosition = new Vector3();
- const _alignedPosition = new Vector2$1();
- const _rotatedPosition = new Vector2$1();
- const _viewWorldMatrix = new Matrix4();
- const _vA$1 = new Vector3();
- const _vB$1 = new Vector3();
- const _vC$1 = new Vector3();
- const _uvA$1 = new Vector2$1();
- const _uvB$1 = new Vector2$1();
- const _uvC$1 = new Vector2$1();
- function Sprite( material ) {
- Object3D.call( this );
- this.type = 'Sprite';
- if ( _geometry === undefined ) {
- _geometry = new BufferGeometry();
- const float32Array = new Float32Array( [
- - 0.5, - 0.5, 0, 0, 0,
- 0.5, - 0.5, 0, 1, 0,
- 0.5, 0.5, 0, 1, 1,
- - 0.5, 0.5, 0, 0, 1
- ] );
- const interleavedBuffer = new InterleavedBuffer( float32Array, 5 );
- _geometry.setIndex( [ 0, 1, 2, 0, 2, 3 ] );
- _geometry.setAttribute( 'position', new InterleavedBufferAttribute( interleavedBuffer, 3, 0, false ) );
- _geometry.setAttribute( 'uv', new InterleavedBufferAttribute( interleavedBuffer, 2, 3, false ) );
- }
- this.geometry = _geometry;
- this.material = ( material !== undefined ) ? material : new SpriteMaterial();
- this.center = new Vector2$1( 0.5, 0.5 );
- }
- Sprite.prototype = Object.assign( Object.create( Object3D.prototype ), {
- constructor: Sprite,
- isSprite: true,
- raycast: function ( raycaster, intersects ) {
- if ( raycaster.camera === null ) {
- console.error( 'THREE.Sprite: "Raycaster.camera" needs to be set in order to raycast against sprites.' );
- }
- _worldScale.setFromMatrixScale( this.matrixWorld );
- _viewWorldMatrix.copy( raycaster.camera.matrixWorld );
- this.modelViewMatrix.multiplyMatrices( raycaster.camera.matrixWorldInverse, this.matrixWorld );
- _mvPosition.setFromMatrixPosition( this.modelViewMatrix );
- if ( raycaster.camera.isPerspectiveCamera && this.material.sizeAttenuation === false ) {
- _worldScale.multiplyScalar( - _mvPosition.z );
- }
- const rotation = this.material.rotation;
- let sin, cos;
- if ( rotation !== 0 ) {
- cos = Math.cos( rotation );
- sin = Math.sin( rotation );
- }
- const center = this.center;
- transformVertex( _vA$1.set( - 0.5, - 0.5, 0 ), _mvPosition, center, _worldScale, sin, cos );
- transformVertex( _vB$1.set( 0.5, - 0.5, 0 ), _mvPosition, center, _worldScale, sin, cos );
- transformVertex( _vC$1.set( 0.5, 0.5, 0 ), _mvPosition, center, _worldScale, sin, cos );
- _uvA$1.set( 0, 0 );
- _uvB$1.set( 1, 0 );
- _uvC$1.set( 1, 1 );
- // check first triangle
- let intersect = raycaster.ray.intersectTriangle( _vA$1, _vB$1, _vC$1, false, _intersectPoint );
- if ( intersect === null ) {
- // check second triangle
- transformVertex( _vB$1.set( - 0.5, 0.5, 0 ), _mvPosition, center, _worldScale, sin, cos );
- _uvB$1.set( 0, 1 );
- intersect = raycaster.ray.intersectTriangle( _vA$1, _vC$1, _vB$1, false, _intersectPoint );
- if ( intersect === null ) {
- return;
- }
- }
- const distance = raycaster.ray.origin.distanceTo( _intersectPoint );
- if ( distance < raycaster.near || distance > raycaster.far ) return;
- intersects.push( {
- distance: distance,
- point: _intersectPoint.clone(),
- uv: Triangle.getUV( _intersectPoint, _vA$1, _vB$1, _vC$1, _uvA$1, _uvB$1, _uvC$1, new Vector2$1() ),
- face: null,
- object: this
- } );
- },
- copy: function ( source ) {
- Object3D.prototype.copy.call( this, source );
- if ( source.center !== undefined ) this.center.copy( source.center );
- this.material = source.material;
- return this;
- }
- } );
- function transformVertex( vertexPosition, mvPosition, center, scale, sin, cos ) {
- // compute position in camera space
- _alignedPosition.subVectors( vertexPosition, center ).addScalar( 0.5 ).multiply( scale );
- // to check if rotation is not zero
- if ( sin !== undefined ) {
- _rotatedPosition.x = ( cos * _alignedPosition.x ) - ( sin * _alignedPosition.y );
- _rotatedPosition.y = ( sin * _alignedPosition.x ) + ( cos * _alignedPosition.y );
- } else {
- _rotatedPosition.copy( _alignedPosition );
- }
- vertexPosition.copy( mvPosition );
- vertexPosition.x += _rotatedPosition.x;
- vertexPosition.y += _rotatedPosition.y;
- // transform to world space
- vertexPosition.applyMatrix4( _viewWorldMatrix );
- }
- const _v1$4 = new Vector3();
- const _v2$2 = new Vector3();
- function LOD() {
- Object3D.call( this );
- this._currentLevel = 0;
- this.type = 'LOD';
- Object.defineProperties( this, {
- levels: {
- enumerable: true,
- value: []
- }
- } );
- this.autoUpdate = true;
- }
- LOD.prototype = Object.assign( Object.create( Object3D.prototype ), {
- constructor: LOD,
- isLOD: true,
- copy: function ( source ) {
- Object3D.prototype.copy.call( this, source, false );
- const levels = source.levels;
- for ( let i = 0, l = levels.length; i < l; i ++ ) {
- const level = levels[ i ];
- this.addLevel( level.object.clone(), level.distance );
- }
- this.autoUpdate = source.autoUpdate;
- return this;
- },
- addLevel: function ( object, distance = 0 ) {
- distance = Math.abs( distance );
- const levels = this.levels;
- let l;
- for ( l = 0; l < levels.length; l ++ ) {
- if ( distance < levels[ l ].distance ) {
- break;
- }
- }
- levels.splice( l, 0, { distance: distance, object: object } );
- this.add( object );
- return this;
- },
- getCurrentLevel: function () {
- return this._currentLevel;
- },
- getObjectForDistance: function ( distance ) {
- const levels = this.levels;
- if ( levels.length > 0 ) {
- let i, l;
- for ( i = 1, l = levels.length; i < l; i ++ ) {
- if ( distance < levels[ i ].distance ) {
- break;
- }
- }
- return levels[ i - 1 ].object;
- }
- return null;
- },
- raycast: function ( raycaster, intersects ) {
- const levels = this.levels;
- if ( levels.length > 0 ) {
- _v1$4.setFromMatrixPosition( this.matrixWorld );
- const distance = raycaster.ray.origin.distanceTo( _v1$4 );
- this.getObjectForDistance( distance ).raycast( raycaster, intersects );
- }
- },
- update: function ( camera ) {
- const levels = this.levels;
- if ( levels.length > 1 ) {
- _v1$4.setFromMatrixPosition( camera.matrixWorld );
- _v2$2.setFromMatrixPosition( this.matrixWorld );
- const distance = _v1$4.distanceTo( _v2$2 ) / camera.zoom;
- levels[ 0 ].object.visible = true;
- let i, l;
- for ( i = 1, l = levels.length; i < l; i ++ ) {
- if ( distance >= levels[ i ].distance ) {
- levels[ i - 1 ].object.visible = false;
- levels[ i ].object.visible = true;
- } else {
- break;
- }
- }
- this._currentLevel = i - 1;
- for ( ; i < l; i ++ ) {
- levels[ i ].object.visible = false;
- }
- }
- },
- toJSON: function ( meta ) {
- const data = Object3D.prototype.toJSON.call( this, meta );
- if ( this.autoUpdate === false ) data.object.autoUpdate = false;
- data.object.levels = [];
- const levels = this.levels;
- for ( let i = 0, l = levels.length; i < l; i ++ ) {
- const level = levels[ i ];
- data.object.levels.push( {
- object: level.object.uuid,
- distance: level.distance
- } );
- }
- return data;
- }
- } );
- const _basePosition = new Vector3();
- const _skinIndex = new Vector4();
- const _skinWeight = new Vector4();
- const _vector$7 = new Vector3();
- const _matrix$1 = new Matrix4();
- function SkinnedMesh( geometry, material ) {
- if ( geometry && geometry.isGeometry ) {
- console.error( 'THREE.SkinnedMesh no longer supports THREE.Geometry. Use THREE.BufferGeometry instead.' );
- }
- Mesh.call( this, geometry, material );
- this.type = 'SkinnedMesh';
- this.bindMode = 'attached';
- this.bindMatrix = new Matrix4();
- this.bindMatrixInverse = new Matrix4();
- }
- SkinnedMesh.prototype = Object.assign( Object.create( Mesh.prototype ), {
- constructor: SkinnedMesh,
- isSkinnedMesh: true,
- copy: function ( source ) {
- Mesh.prototype.copy.call( this, source );
- this.bindMode = source.bindMode;
- this.bindMatrix.copy( source.bindMatrix );
- this.bindMatrixInverse.copy( source.bindMatrixInverse );
- this.skeleton = source.skeleton;
- return this;
- },
- bind: function ( skeleton, bindMatrix ) {
- this.skeleton = skeleton;
- if ( bindMatrix === undefined ) {
- this.updateMatrixWorld( true );
- this.skeleton.calculateInverses();
- bindMatrix = this.matrixWorld;
- }
- this.bindMatrix.copy( bindMatrix );
- this.bindMatrixInverse.copy( bindMatrix ).invert();
- },
- pose: function () {
- this.skeleton.pose();
- },
- normalizeSkinWeights: function () {
- const vector = new Vector4();
- const skinWeight = this.geometry.attributes.skinWeight;
- for ( let i = 0, l = skinWeight.count; i < l; i ++ ) {
- vector.x = skinWeight.getX( i );
- vector.y = skinWeight.getY( i );
- vector.z = skinWeight.getZ( i );
- vector.w = skinWeight.getW( i );
- const scale = 1.0 / vector.manhattanLength();
- if ( scale !== Infinity ) {
- vector.multiplyScalar( scale );
- } else {
- vector.set( 1, 0, 0, 0 ); // do something reasonable
- }
- skinWeight.setXYZW( i, vector.x, vector.y, vector.z, vector.w );
- }
- },
- updateMatrixWorld: function ( force ) {
- Mesh.prototype.updateMatrixWorld.call( this, force );
- if ( this.bindMode === 'attached' ) {
- this.bindMatrixInverse.copy( this.matrixWorld ).invert();
- } else if ( this.bindMode === 'detached' ) {
- this.bindMatrixInverse.copy( this.bindMatrix ).invert();
- } else {
- console.warn( 'THREE.SkinnedMesh: Unrecognized bindMode: ' + this.bindMode );
- }
- },
- boneTransform: function ( index, target ) {
- const skeleton = this.skeleton;
- const geometry = this.geometry;
- _skinIndex.fromBufferAttribute( geometry.attributes.skinIndex, index );
- _skinWeight.fromBufferAttribute( geometry.attributes.skinWeight, index );
- _basePosition.fromBufferAttribute( geometry.attributes.position, index ).applyMatrix4( this.bindMatrix );
- target.set( 0, 0, 0 );
- for ( let i = 0; i < 4; i ++ ) {
- const weight = _skinWeight.getComponent( i );
- if ( weight !== 0 ) {
- const boneIndex = _skinIndex.getComponent( i );
- _matrix$1.multiplyMatrices( skeleton.bones[ boneIndex ].matrixWorld, skeleton.boneInverses[ boneIndex ] );
- target.addScaledVector( _vector$7.copy( _basePosition ).applyMatrix4( _matrix$1 ), weight );
- }
- }
- return target.applyMatrix4( this.bindMatrixInverse );
- }
- } );
- function Bone() {
- Object3D.call( this );
- this.type = 'Bone';
- }
- Bone.prototype = Object.assign( Object.create( Object3D.prototype ), {
- constructor: Bone,
- isBone: true
- } );
- const _offsetMatrix = new Matrix4();
- const _identityMatrix = new Matrix4();
- function Skeleton( bones = [], boneInverses = [] ) {
- this.uuid = MathUtils.generateUUID();
- this.bones = bones.slice( 0 );
- this.boneInverses = boneInverses;
- this.boneMatrices = null;
- this.boneTexture = null;
- this.boneTextureSize = 0;
- this.frame = - 1;
- this.init();
- }
- Object.assign( Skeleton.prototype, {
- init: function () {
- const bones = this.bones;
- const boneInverses = this.boneInverses;
- this.boneMatrices = new Float32Array( bones.length * 16 );
- // calculate inverse bone matrices if necessary
- if ( boneInverses.length === 0 ) {
- this.calculateInverses();
- } else {
- // handle special case
- if ( bones.length !== boneInverses.length ) {
- console.warn( 'THREE.Skeleton: Number of inverse bone matrices does not match amount of bones.' );
- this.boneInverses = [];
- for ( let i = 0, il = this.bones.length; i < il; i ++ ) {
- this.boneInverses.push( new Matrix4() );
- }
- }
- }
- },
- calculateInverses: function () {
- this.boneInverses.length = 0;
- for ( let i = 0, il = this.bones.length; i < il; i ++ ) {
- const inverse = new Matrix4();
- if ( this.bones[ i ] ) {
- inverse.copy( this.bones[ i ].matrixWorld ).invert();
- }
- this.boneInverses.push( inverse );
- }
- },
- pose: function () {
- // recover the bind-time world matrices
- for ( let i = 0, il = this.bones.length; i < il; i ++ ) {
- const bone = this.bones[ i ];
- if ( bone ) {
- bone.matrixWorld.copy( this.boneInverses[ i ] ).invert();
- }
- }
- // compute the local matrices, positions, rotations and scales
- for ( let i = 0, il = this.bones.length; i < il; i ++ ) {
- const bone = this.bones[ i ];
- if ( bone ) {
- if ( bone.parent && bone.parent.isBone ) {
- bone.matrix.copy( bone.parent.matrixWorld ).invert();
- bone.matrix.multiply( bone.matrixWorld );
- } else {
- bone.matrix.copy( bone.matrixWorld );
- }
- bone.matrix.decompose( bone.position, bone.quaternion, bone.scale );
- }
- }
- },
- update: function () {
- const bones = this.bones;
- const boneInverses = this.boneInverses;
- const boneMatrices = this.boneMatrices;
- const boneTexture = this.boneTexture;
- // flatten bone matrices to array
- for ( let i = 0, il = bones.length; i < il; i ++ ) {
- // compute the offset between the current and the original transform
- const matrix = bones[ i ] ? bones[ i ].matrixWorld : _identityMatrix;
- _offsetMatrix.multiplyMatrices( matrix, boneInverses[ i ] );
- _offsetMatrix.toArray( boneMatrices, i * 16 );
- }
- if ( boneTexture !== null ) {
- boneTexture.needsUpdate = true;
- }
- },
- clone: function () {
- return new Skeleton( this.bones, this.boneInverses );
- },
- getBoneByName: function ( name ) {
- for ( let i = 0, il = this.bones.length; i < il; i ++ ) {
- const bone = this.bones[ i ];
- if ( bone.name === name ) {
- return bone;
- }
- }
- return undefined;
- },
- dispose: function ( ) {
- if ( this.boneTexture !== null ) {
- this.boneTexture.dispose();
- this.boneTexture = null;
- }
- },
- fromJSON: function ( json, bones ) {
- this.uuid = json.uuid;
- for ( let i = 0, l = json.bones.length; i < l; i ++ ) {
- const uuid = json.bones[ i ];
- let bone = bones[ uuid ];
- if ( bone === undefined ) {
- console.warn( 'THREE.Skeleton: No bone found with UUID:', uuid );
- bone = new Bone();
- }
- this.bones.push( bone );
- this.boneInverses.push( new Matrix4().fromArray( json.boneInverses[ i ] ) );
- }
- this.init();
- return this;
- },
- toJSON: function () {
- const data = {
- metadata: {
- version: 4.5,
- type: 'Skeleton',
- generator: 'Skeleton.toJSON'
- },
- bones: [],
- boneInverses: []
- };
- data.uuid = this.uuid;
- const bones = this.bones;
- const boneInverses = this.boneInverses;
- for ( let i = 0, l = bones.length; i < l; i ++ ) {
- const bone = bones[ i ];
- data.bones.push( bone.uuid );
- const boneInverse = boneInverses[ i ];
- data.boneInverses.push( boneInverse.toArray() );
- }
- return data;
- }
- } );
- const _instanceLocalMatrix = new Matrix4();
- const _instanceWorldMatrix = new Matrix4();
- const _instanceIntersects = [];
- const _mesh = new Mesh();
- function InstancedMesh( geometry, material, count ) {
- Mesh.call( this, geometry, material );
- this.instanceMatrix = new BufferAttribute( new Float32Array( count * 16 ), 16 );
- this.instanceColor = null;
- this.count = count;
- this.frustumCulled = false;
- }
- InstancedMesh.prototype = Object.assign( Object.create( Mesh.prototype ), {
- constructor: InstancedMesh,
- isInstancedMesh: true,
- copy: function ( source ) {
- Mesh.prototype.copy.call( this, source );
- this.instanceMatrix.copy( source.instanceMatrix );
- this.count = source.count;
- return this;
- },
- getColorAt: function ( index, color ) {
- color.fromArray( this.instanceColor.array, index * 3 );
- },
- getMatrixAt: function ( index, matrix ) {
- matrix.fromArray( this.instanceMatrix.array, index * 16 );
- },
- raycast: function ( raycaster, intersects ) {
- const matrixWorld = this.matrixWorld;
- const raycastTimes = this.count;
- _mesh.geometry = this.geometry;
- _mesh.material = this.material;
- if ( _mesh.material === undefined ) return;
- for ( let instanceId = 0; instanceId < raycastTimes; instanceId ++ ) {
- // calculate the world matrix for each instance
- this.getMatrixAt( instanceId, _instanceLocalMatrix );
- _instanceWorldMatrix.multiplyMatrices( matrixWorld, _instanceLocalMatrix );
- // the mesh represents this single instance
- _mesh.matrixWorld = _instanceWorldMatrix;
- _mesh.raycast( raycaster, _instanceIntersects );
- // process the result of raycast
- for ( let i = 0, l = _instanceIntersects.length; i < l; i ++ ) {
- const intersect = _instanceIntersects[ i ];
- intersect.instanceId = instanceId;
- intersect.object = this;
- intersects.push( intersect );
- }
- _instanceIntersects.length = 0;
- }
- },
- setColorAt: function ( index, color ) {
- if ( this.instanceColor === null ) {
- this.instanceColor = new BufferAttribute( new Float32Array( this.count * 3 ), 3 );
- }
- color.toArray( this.instanceColor.array, index * 3 );
- },
- setMatrixAt: function ( index, matrix ) {
- matrix.toArray( this.instanceMatrix.array, index * 16 );
- },
- updateMorphTargets: function () {
- },
- dispose: function () {
- this.dispatchEvent( { type: 'dispose' } );
- }
- } );
- /**
- * parameters = {
- * color: <hex>,
- * opacity: <float>,
- *
- * lineWidth: <float>,
- * linecap: "round",
- * linejoin: "round"
- * }
- */
- function LineBasicMaterial( parameters ) {
- Material.call( this );
- this.type = 'LineBasicMaterial';
- this.color = new Color( 0xffffff );
- this.lineWidth = 1;
- this.linecap = 'round';
- this.linejoin = 'round';
- this.morphTargets = false;
- this.setValues( parameters );
- }
- LineBasicMaterial.prototype = Object.create( Material.prototype );
- LineBasicMaterial.prototype.constructor = LineBasicMaterial;
- LineBasicMaterial.prototype.isLineBasicMaterial = true;
- LineBasicMaterial.prototype.copy = function ( source ) {
- Material.prototype.copy.call( this, source );
- this.color.copy( source.color );
- this.lineWidth = source.lineWidth;
- this.linecap = source.linecap;
- this.linejoin = source.linejoin;
- this.morphTargets = source.morphTargets;
- return this;
- };
- const _start = new Vector3();
- const _end = new Vector3();
- const _inverseMatrix$1 = new Matrix4();
- const _ray$1 = new Ray();
- const _sphere$2 = new Sphere();
- function Line( geometry = new BufferGeometry(), material = new LineBasicMaterial() ) {
- Object3D.call( this );
- this.type = 'Line';
- this.geometry = geometry;
- this.material = material;
- this.updateMorphTargets();
- }
- Line.prototype = Object.assign( Object.create( Object3D.prototype ), {
- constructor: Line,
- isLine: true,
- copy: function ( source ) {
- Object3D.prototype.copy.call( this, source );
- this.material = source.material;
- this.geometry = source.geometry;
- return this;
- },
- computeLineDistances: function () {
- const geometry = this.geometry;
- if ( geometry.isBufferGeometry ) {
- // we assume non-indexed geometry
- if ( geometry.index === null ) {
- const positionAttribute = geometry.attributes.position;
- const lineDistances = [ 0 ];
- for ( let i = 1, l = positionAttribute.count; i < l; i ++ ) {
- _start.fromBufferAttribute( positionAttribute, i - 1 );
- _end.fromBufferAttribute( positionAttribute, i );
- lineDistances[ i ] = lineDistances[ i - 1 ];
- lineDistances[ i ] += _start.distanceTo( _end );
- }
- geometry.setAttribute( 'lineDistance', new Float32BufferAttribute( lineDistances, 1 ) );
- } else {
- console.warn( 'THREE.Line.computeLineDistances(): Computation only possible with non-indexed BufferGeometry.' );
- }
- } else if ( geometry.isGeometry ) {
- const vertices = geometry.vertices;
- const lineDistances = geometry.lineDistances;
- lineDistances[ 0 ] = 0;
- for ( let i = 1, l = vertices.length; i < l; i ++ ) {
- lineDistances[ i ] = lineDistances[ i - 1 ];
- lineDistances[ i ] += vertices[ i - 1 ].distanceTo( vertices[ i ] );
- }
- }
- return this;
- },
- raycast: function ( raycaster, intersects ) {
- const geometry = this.geometry;
- const matrixWorld = this.matrixWorld;
- const threshold = raycaster.params.Line.threshold;
- // Checking boundingSphere distance to ray
- if ( geometry.boundingSphere === null ) geometry.computeBoundingSphere();
- _sphere$2.copy( geometry.boundingSphere );
- _sphere$2.applyMatrix4( matrixWorld );
- _sphere$2.radius += threshold;
- if ( raycaster.ray.intersectsSphere( _sphere$2 ) === false ) return;
- //
- _inverseMatrix$1.copy( matrixWorld ).invert();
- _ray$1.copy( raycaster.ray ).applyMatrix4( _inverseMatrix$1 );
- const localThreshold = threshold / ( ( this.scale.x + this.scale.y + this.scale.z ) / 3 );
- const localThresholdSq = localThreshold * localThreshold;
- const vStart = new Vector3();
- const vEnd = new Vector3();
- const interSegment = new Vector3();
- const interRay = new Vector3();
- const step = this.isLineSegments ? 2 : 1;
- if ( geometry.isBufferGeometry ) {
- const index = geometry.index;
- const attributes = geometry.attributes;
- const positionAttribute = attributes.position;
- if ( index !== null ) {
- const indices = index.array;
- for ( let i = 0, l = indices.length - 1; i < l; i += step ) {
- const a = indices[ i ];
- const b = indices[ i + 1 ];
- vStart.fromBufferAttribute( positionAttribute, a );
- vEnd.fromBufferAttribute( positionAttribute, b );
- const distSq = _ray$1.distanceSqToSegment( vStart, vEnd, interRay, interSegment );
- if ( distSq > localThresholdSq ) continue;
- interRay.applyMatrix4( this.matrixWorld ); //Move back to world space for distance calculation
- const distance = raycaster.ray.origin.distanceTo( interRay );
- if ( distance < raycaster.near || distance > raycaster.far ) continue;
- intersects.push( {
- distance: distance,
- // What do we want? intersection point on the ray or on the segment??
- // point: raycaster.ray.at( distance ),
- point: interSegment.clone().applyMatrix4( this.matrixWorld ),
- index: i,
- face: null,
- faceIndex: null,
- object: this
- } );
- }
- } else {
- for ( let i = 0, l = positionAttribute.count - 1; i < l; i += step ) {
- vStart.fromBufferAttribute( positionAttribute, i );
- vEnd.fromBufferAttribute( positionAttribute, i + 1 );
- const distSq = _ray$1.distanceSqToSegment( vStart, vEnd, interRay, interSegment );
- if ( distSq > localThresholdSq ) continue;
- interRay.applyMatrix4( this.matrixWorld ); //Move back to world space for distance calculation
- const distance = raycaster.ray.origin.distanceTo( interRay );
- if ( distance < raycaster.near || distance > raycaster.far ) continue;
- intersects.push( {
- distance: distance,
- // What do we want? intersection point on the ray or on the segment??
- // point: raycaster.ray.at( distance ),
- point: interSegment.clone().applyMatrix4( this.matrixWorld ),
- index: i,
- face: null,
- faceIndex: null,
- object: this
- } );
- }
- }
- } else if ( geometry.isGeometry ) {
- const vertices = geometry.vertices;
- const nbVertices = vertices.length;
- for ( let i = 0; i < nbVertices - 1; i += step ) {
- const distSq = _ray$1.distanceSqToSegment( vertices[ i ], vertices[ i + 1 ], interRay, interSegment );
- if ( distSq > localThresholdSq ) continue;
- interRay.applyMatrix4( this.matrixWorld ); //Move back to world space for distance calculation
- const distance = raycaster.ray.origin.distanceTo( interRay );
- if ( distance < raycaster.near || distance > raycaster.far ) continue;
- intersects.push( {
- distance: distance,
- // What do we want? intersection point on the ray or on the segment??
- // point: raycaster.ray.at( distance ),
- point: interSegment.clone().applyMatrix4( this.matrixWorld ),
- index: i,
- face: null,
- faceIndex: null,
- object: this
- } );
- }
- }
- },
- updateMorphTargets: function () {
- const geometry = this.geometry;
- if ( geometry.isBufferGeometry ) {
- const morphAttributes = geometry.morphAttributes;
- const keys = Object.keys( morphAttributes );
- if ( keys.length > 0 ) {
- const morphAttribute = morphAttributes[ keys[ 0 ] ];
- if ( morphAttribute !== undefined ) {
- this.morphTargetInfluences = [];
- this.morphTargetDictionary = {};
- for ( let m = 0, ml = morphAttribute.length; m < ml; m ++ ) {
- const name = morphAttribute[ m ].name || String( m );
- this.morphTargetInfluences.push( 0 );
- this.morphTargetDictionary[ name ] = m;
- }
- }
- }
- } else {
- const morphTargets = geometry.morphTargets;
- if ( morphTargets !== undefined && morphTargets.length > 0 ) {
- console.error( 'THREE.Line.updateMorphTargets() does not support THREE.Geometry. Use THREE.BufferGeometry instead.' );
- }
- }
- }
- } );
- const _start$1 = new Vector3();
- const _end$1 = new Vector3();
- function LineSegments( geometry, material ) {
- Line.call( this, geometry, material );
- this.type = 'LineSegments';
- }
- LineSegments.prototype = Object.assign( Object.create( Line.prototype ), {
- constructor: LineSegments,
- isLineSegments: true,
- computeLineDistances: function () {
- const geometry = this.geometry;
- if ( geometry.isBufferGeometry ) {
- // we assume non-indexed geometry
- if ( geometry.index === null ) {
- const positionAttribute = geometry.attributes.position;
- const lineDistances = [];
- for ( let i = 0, l = positionAttribute.count; i < l; i += 2 ) {
- _start$1.fromBufferAttribute( positionAttribute, i );
- _end$1.fromBufferAttribute( positionAttribute, i + 1 );
- lineDistances[ i ] = ( i === 0 ) ? 0 : lineDistances[ i - 1 ];
- lineDistances[ i + 1 ] = lineDistances[ i ] + _start$1.distanceTo( _end$1 );
- }
- geometry.setAttribute( 'lineDistance', new Float32BufferAttribute( lineDistances, 1 ) );
- } else {
- console.warn( 'THREE.LineSegments.computeLineDistances(): Computation only possible with non-indexed BufferGeometry.' );
- }
- } else if ( geometry.isGeometry ) {
- const vertices = geometry.vertices;
- const lineDistances = geometry.lineDistances;
- for ( let i = 0, l = vertices.length; i < l; i += 2 ) {
- _start$1.copy( vertices[ i ] );
- _end$1.copy( vertices[ i + 1 ] );
- lineDistances[ i ] = ( i === 0 ) ? 0 : lineDistances[ i - 1 ];
- lineDistances[ i + 1 ] = lineDistances[ i ] + _start$1.distanceTo( _end$1 );
- }
- }
- return this;
- }
- } );
- function LineLoop( geometry, material ) {
- Line.call( this, geometry, material );
- this.type = 'LineLoop';
- }
- LineLoop.prototype = Object.assign( Object.create( Line.prototype ), {
- constructor: LineLoop,
- isLineLoop: true,
- } );
- /**
- * parameters = {
- * color: <hex>,
- * opacity: <float>,
- * map: new THREE.Texture( <Image> ),
- * alphaMap: new THREE.Texture( <Image> ),
- *
- * size: <float>,
- * sizeAttenuation: <bool>
- *
- * morphTargets: <bool>
- * }
- */
- function PointsMaterial( parameters ) {
- Material.call( this );
- this.type = 'PointsMaterial';
- this.color = new Color( 0xffffff );
- this.map = null;
- this.alphaMap = null;
- this.size = 1;
- this.sizeAttenuation = true;
- this.morphTargets = false;
- this.setValues( parameters );
- }
- PointsMaterial.prototype = Object.create( Material.prototype );
- PointsMaterial.prototype.constructor = PointsMaterial;
- PointsMaterial.prototype.isPointsMaterial = true;
- PointsMaterial.prototype.copy = function ( source ) {
- Material.prototype.copy.call( this, source );
- this.color.copy( source.color );
- this.map = source.map;
- this.alphaMap = source.alphaMap;
- this.size = source.size;
- this.sizeAttenuation = source.sizeAttenuation;
- this.morphTargets = source.morphTargets;
- return this;
- };
- const _inverseMatrix$2 = new Matrix4();
- const _ray$2 = new Ray();
- const _sphere$3 = new Sphere();
- const _position$1 = new Vector3();
- function Points( geometry = new BufferGeometry(), material = new PointsMaterial() ) {
- Object3D.call( this );
- this.type = 'Points';
- this.geometry = geometry;
- this.material = material;
- this.updateMorphTargets();
- }
- Points.prototype = Object.assign( Object.create( Object3D.prototype ), {
- constructor: Points,
- isPoints: true,
- copy: function ( source ) {
- Object3D.prototype.copy.call( this, source );
- this.material = source.material;
- this.geometry = source.geometry;
- return this;
- },
- raycast: function ( raycaster, intersects ) {
- const geometry = this.geometry;
- const matrixWorld = this.matrixWorld;
- const threshold = raycaster.params.Points.threshold;
- // Checking boundingSphere distance to ray
- if ( geometry.boundingSphere === null ) geometry.computeBoundingSphere();
- _sphere$3.copy( geometry.boundingSphere );
- _sphere$3.applyMatrix4( matrixWorld );
- _sphere$3.radius += threshold;
- if ( raycaster.ray.intersectsSphere( _sphere$3 ) === false ) return;
- //
- _inverseMatrix$2.copy( matrixWorld ).invert();
- _ray$2.copy( raycaster.ray ).applyMatrix4( _inverseMatrix$2 );
- const localThreshold = threshold / ( ( this.scale.x + this.scale.y + this.scale.z ) / 3 );
- const localThresholdSq = localThreshold * localThreshold;
- if ( geometry.isBufferGeometry ) {
- const index = geometry.index;
- const attributes = geometry.attributes;
- const positionAttribute = attributes.position;
- if ( index !== null ) {
- const indices = index.array;
- for ( let i = 0, il = indices.length; i < il; i ++ ) {
- const a = indices[ i ];
- _position$1.fromBufferAttribute( positionAttribute, a );
- testPoint( _position$1, a, localThresholdSq, matrixWorld, raycaster, intersects, this );
- }
- } else {
- for ( let i = 0, l = positionAttribute.count; i < l; i ++ ) {
- _position$1.fromBufferAttribute( positionAttribute, i );
- testPoint( _position$1, i, localThresholdSq, matrixWorld, raycaster, intersects, this );
- }
- }
- } else {
- const vertices = geometry.vertices;
- for ( let i = 0, l = vertices.length; i < l; i ++ ) {
- testPoint( vertices[ i ], i, localThresholdSq, matrixWorld, raycaster, intersects, this );
- }
- }
- },
- updateMorphTargets: function () {
- const geometry = this.geometry;
- if ( geometry.isBufferGeometry ) {
- const morphAttributes = geometry.morphAttributes;
- const keys = Object.keys( morphAttributes );
- if ( keys.length > 0 ) {
- const morphAttribute = morphAttributes[ keys[ 0 ] ];
- if ( morphAttribute !== undefined ) {
- this.morphTargetInfluences = [];
- this.morphTargetDictionary = {};
- for ( let m = 0, ml = morphAttribute.length; m < ml; m ++ ) {
- const name = morphAttribute[ m ].name || String( m );
- this.morphTargetInfluences.push( 0 );
- this.morphTargetDictionary[ name ] = m;
- }
- }
- }
- } else {
- const morphTargets = geometry.morphTargets;
- if ( morphTargets !== undefined && morphTargets.length > 0 ) {
- console.error( 'THREE.Points.updateMorphTargets() does not support THREE.Geometry. Use THREE.BufferGeometry instead.' );
- }
- }
- }
- } );
- function testPoint( point, index, localThresholdSq, matrixWorld, raycaster, intersects, object ) {
- const rayPointDistanceSq = _ray$2.distanceSqToPoint( point );
- if ( rayPointDistanceSq < localThresholdSq ) {
- const intersectPoint = new Vector3();
- _ray$2.closestPointToPoint( point, intersectPoint );
- intersectPoint.applyMatrix4( matrixWorld );
- const distance = raycaster.ray.origin.distanceTo( intersectPoint );
- if ( distance < raycaster.near || distance > raycaster.far ) return;
- intersects.push( {
- distance: distance,
- distanceToRay: Math.sqrt( rayPointDistanceSq ),
- point: intersectPoint,
- index: index,
- face: null,
- object: object
- } );
- }
- }
- function VideoTexture( video, mapping, wrapS, wrapT, magFilter, minFilter, format, type, anisotropy ) {
- Texture.call( this, video, mapping, wrapS, wrapT, magFilter, minFilter, format, type, anisotropy );
- this.format = format !== undefined ? format : RGBFormat;
- this.minFilter = minFilter !== undefined ? minFilter : LinearFilter;
- this.magFilter = magFilter !== undefined ? magFilter : LinearFilter;
- this.generateMipmaps = false;
- const scope = this;
- function updateVideo() {
- scope.needsUpdate = true;
- video.requestVideoFrameCallback( updateVideo );
- }
- if ( 'requestVideoFrameCallback' in video ) {
- video.requestVideoFrameCallback( updateVideo );
- }
- }
- VideoTexture.prototype = Object.assign( Object.create( Texture.prototype ), {
- constructor: VideoTexture,
- clone: function () {
- return new this.constructor( this.image ).copy( this );
- },
- isVideoTexture: true,
- update: function () {
- const video = this.image;
- const hasVideoFrameCallback = 'requestVideoFrameCallback' in video;
- if ( hasVideoFrameCallback === false && video.readyState >= video.HAVE_CURRENT_DATA ) {
- this.needsUpdate = true;
- }
- }
- } );
- function CompressedTexture( mipmaps, width, height, format, type, mapping, wrapS, wrapT, magFilter, minFilter, anisotropy, encoding ) {
- Texture.call( this, null, mapping, wrapS, wrapT, magFilter, minFilter, format, type, anisotropy, encoding );
- this.image = { width: width, height: height };
- this.mipmaps = mipmaps;
- // no flipping for cube textures
- // (also flipping doesn't work for compressed textures )
- this.flipY = false;
- // can't generate mipmaps for compressed textures
- // mips must be embedded in DDS files
- this.generateMipmaps = false;
- }
- CompressedTexture.prototype = Object.create( Texture.prototype );
- CompressedTexture.prototype.constructor = CompressedTexture;
- CompressedTexture.prototype.isCompressedTexture = true;
- function CanvasTexture( canvas, mapping, wrapS, wrapT, magFilter, minFilter, format, type, anisotropy ) {
- Texture.call( this, canvas, mapping, wrapS, wrapT, magFilter, minFilter, format, type, anisotropy );
- this.needsUpdate = true;
- }
- CanvasTexture.prototype = Object.create( Texture.prototype );
- CanvasTexture.prototype.constructor = CanvasTexture;
- CanvasTexture.prototype.isCanvasTexture = true;
- function DepthTexture( width, height, type, mapping, wrapS, wrapT, magFilter, minFilter, anisotropy, format ) {
- format = format !== undefined ? format : DepthFormat;
- if ( format !== DepthFormat && format !== DepthStencilFormat ) {
- throw new Error( 'DepthTexture format must be either THREE.DepthFormat or THREE.DepthStencilFormat' );
- }
- if ( type === undefined && format === DepthFormat ) type = UnsignedShortType;
- if ( type === undefined && format === DepthStencilFormat ) type = UnsignedInt248Type$1;
- Texture.call( this, null, mapping, wrapS, wrapT, magFilter, minFilter, format, type, anisotropy );
- this.image = { width: width, height: height };
- this.magFilter = magFilter !== undefined ? magFilter : NearestFilter;
- this.minFilter = minFilter !== undefined ? minFilter : NearestFilter;
- this.flipY = false;
- this.generateMipmaps = false;
- }
- DepthTexture.prototype = Object.create( Texture.prototype );
- DepthTexture.prototype.constructor = DepthTexture;
- DepthTexture.prototype.isDepthTexture = true;
- let _geometryId = 0; // Geometry uses even numbers as Id
- const _m1$3 = new Matrix4();
- const _obj$1 = new Object3D();
- const _offset$1 = new Vector3();
- function Geometry() {
- Object.defineProperty( this, 'id', { value: _geometryId += 2 } );
- this.uuid = MathUtils.generateUUID();
- this.name = '';
- this.type = 'Geometry';
- this.vertices = [];
- this.colors = [];
- this.faces = [];
- this.faceVertexUvs = [[]];
- this.morphTargets = [];
- this.morphNormals = [];
- this.skinWeights = [];
- this.skinIndices = [];
- this.lineDistances = [];
- this.boundingBox = null;
- this.boundingSphere = null;
- // update flags
- this.elementsNeedUpdate = false;
- this.verticesNeedUpdate = false;
- this.uvsNeedUpdate = false;
- this.normalsNeedUpdate = false;
- this.colorsNeedUpdate = false;
- this.lineDistancesNeedUpdate = false;
- this.groupsNeedUpdate = false;
- }
- Geometry.prototype = Object.assign( Object.create( EventDispatcher.prototype ), {
- constructor: Geometry,
- isGeometry: true,
- applyMatrix4: function ( matrix ) {
- const normalMatrix = new Matrix3().getNormalMatrix( matrix );
- for ( let i = 0, il = this.vertices.length; i < il; i ++ ) {
- const vertex = this.vertices[ i ];
- vertex.applyMatrix4( matrix );
- }
- for ( let i = 0, il = this.faces.length; i < il; i ++ ) {
- const face = this.faces[ i ];
- face.normal.applyMatrix3( normalMatrix ).normalize();
- for ( let j = 0, jl = face.vertexNormals.length; j < jl; j ++ ) {
- face.vertexNormals[ j ].applyMatrix3( normalMatrix ).normalize();
- }
- }
- if ( this.boundingBox !== null ) {
- this.computeBoundingBox();
- }
- if ( this.boundingSphere !== null ) {
- this.computeBoundingSphere();
- }
- this.verticesNeedUpdate = true;
- this.normalsNeedUpdate = true;
- return this;
- },
- rotateX: function ( angle ) {
- // rotate geometry around world x-axis
- _m1$3.makeRotationX( angle );
- this.applyMatrix4( _m1$3 );
- return this;
- },
- rotateY: function ( angle ) {
- // rotate geometry around world y-axis
- _m1$3.makeRotationY( angle );
- this.applyMatrix4( _m1$3 );
- return this;
- },
- rotateZ: function ( angle ) {
- // rotate geometry around world z-axis
- _m1$3.makeRotationZ( angle );
- this.applyMatrix4( _m1$3 );
- return this;
- },
- translate: function ( x, y, z ) {
- // translate geometry
- _m1$3.makeTranslation( x, y, z );
- this.applyMatrix4( _m1$3 );
- return this;
- },
- scale: function ( x, y, z ) {
- // scale geometry
- _m1$3.makeScale( x, y, z );
- this.applyMatrix4( _m1$3 );
- return this;
- },
- lookAt: function ( vector ) {
- _obj$1.lookAt( vector );
- _obj$1.updateMatrix();
- this.applyMatrix4( _obj$1.matrix );
- return this;
- },
- fromBufferGeometry: function ( geometry ) {
- const scope = this;
- const index = geometry.index !== null ? geometry.index : undefined;
- const attributes = geometry.attributes;
- if ( attributes.position === undefined ) {
- console.error( 'THREE.Geometry.fromBufferGeometry(): Position attribute required for conversion.' );
- return this;
- }
- const position = attributes.position;
- const normal = attributes.normal;
- const color = attributes.color;
- const uv = attributes.uv;
- const uv2 = attributes.uv2;
- if ( uv2 !== undefined ) this.faceVertexUvs[ 1 ] = [];
- for ( let i = 0; i < position.count; i ++ ) {
- scope.vertices.push( new Vector3().fromBufferAttribute( position, i ) );
- if ( color !== undefined ) {
- scope.colors.push( new Color().fromBufferAttribute( color, i ) );
- }
- }
- function addFace( a, b, c, materialIndex ) {
- const vertexColors = ( color === undefined ) ? [] : [
- scope.colors[ a ].clone(),
- scope.colors[ b ].clone(),
- scope.colors[ c ].clone()
- ];
- const vertexNormals = ( normal === undefined ) ? [] : [
- new Vector3().fromBufferAttribute( normal, a ),
- new Vector3().fromBufferAttribute( normal, b ),
- new Vector3().fromBufferAttribute( normal, c )
- ];
- const face = new Face3( a, b, c, vertexNormals, vertexColors, materialIndex );
- scope.faces.push( face );
- if ( uv !== undefined ) {
- scope.faceVertexUvs[ 0 ].push( [
- new Vector2$1().fromBufferAttribute( uv, a ),
- new Vector2$1().fromBufferAttribute( uv, b ),
- new Vector2$1().fromBufferAttribute( uv, c )
- ] );
- }
- if ( uv2 !== undefined ) {
- scope.faceVertexUvs[ 1 ].push( [
- new Vector2$1().fromBufferAttribute( uv2, a ),
- new Vector2$1().fromBufferAttribute( uv2, b ),
- new Vector2$1().fromBufferAttribute( uv2, c )
- ] );
- }
- }
- const groups = geometry.groups;
- if ( groups.length > 0 ) {
- for ( let i = 0; i < groups.length; i ++ ) {
- const group = groups[ i ];
- const start = group.start;
- const count = group.count;
- for ( let j = start, jl = start + count; j < jl; j += 3 ) {
- if ( index !== undefined ) {
- addFace( index.getX( j ), index.getX( j + 1 ), index.getX( j + 2 ), group.materialIndex );
- } else {
- addFace( j, j + 1, j + 2, group.materialIndex );
- }
- }
- }
- } else {
- if ( index !== undefined ) {
- for ( let i = 0; i < index.count; i += 3 ) {
- addFace( index.getX( i ), index.getX( i + 1 ), index.getX( i + 2 ) );
- }
- } else {
- for ( let i = 0; i < position.count; i += 3 ) {
- addFace( i, i + 1, i + 2 );
- }
- }
- }
- this.computeFaceNormals();
- if ( geometry.boundingBox !== null ) {
- this.boundingBox = geometry.boundingBox.clone();
- }
- if ( geometry.boundingSphere !== null ) {
- this.boundingSphere = geometry.boundingSphere.clone();
- }
- return this;
- },
- center: function () {
- this.computeBoundingBox();
- this.boundingBox.getCenter( _offset$1 ).negate();
- this.translate( _offset$1.x, _offset$1.y, _offset$1.z );
- return this;
- },
- normalize: function () {
- this.computeBoundingSphere();
- const center = this.boundingSphere.center;
- const radius = this.boundingSphere.radius;
- const s = radius === 0 ? 1 : 1.0 / radius;
- const matrix = new Matrix4();
- matrix.set(
- s, 0, 0, - s * center.x,
- 0, s, 0, - s * center.y,
- 0, 0, s, - s * center.z,
- 0, 0, 0, 1
- );
- this.applyMatrix4( matrix );
- return this;
- },
- computeFaceNormals: function () {
- const cb = new Vector3(), ab = new Vector3();
- for ( let f = 0, fl = this.faces.length; f < fl; f ++ ) {
- const face = this.faces[ f ];
- const vA = this.vertices[ face.a ];
- const vB = this.vertices[ face.b ];
- const vC = this.vertices[ face.c ];
- cb.subVectors( vC, vB );
- ab.subVectors( vA, vB );
- cb.cross( ab );
- cb.normalize();
- face.normal.copy( cb );
- }
- },
- computeVertexNormals: function ( areaWeighted = true ) {
- const vertices = new Array( this.vertices.length );
- for ( let v = 0, vl = this.vertices.length; v < vl; v ++ ) {
- vertices[ v ] = new Vector3();
- }
- if ( areaWeighted ) {
- // vertex normals weighted by triangle areas
- // http://www.iquilezles.org/www/articles/normals/normals.htm
- const cb = new Vector3(), ab = new Vector3();
- for ( let f = 0, fl = this.faces.length; f < fl; f ++ ) {
- const face = this.faces[ f ];
- const vA = this.vertices[ face.a ];
- const vB = this.vertices[ face.b ];
- const vC = this.vertices[ face.c ];
- cb.subVectors( vC, vB );
- ab.subVectors( vA, vB );
- cb.cross( ab );
- vertices[ face.a ].add( cb );
- vertices[ face.b ].add( cb );
- vertices[ face.c ].add( cb );
- }
- } else {
- this.computeFaceNormals();
- for ( let f = 0, fl = this.faces.length; f < fl; f ++ ) {
- const face = this.faces[ f ];
- vertices[ face.a ].add( face.normal );
- vertices[ face.b ].add( face.normal );
- vertices[ face.c ].add( face.normal );
- }
- }
- for ( let v = 0, vl = this.vertices.length; v < vl; v ++ ) {
- vertices[ v ].normalize();
- }
- for ( let f = 0, fl = this.faces.length; f < fl; f ++ ) {
- const face = this.faces[ f ];
- const vertexNormals = face.vertexNormals;
- if ( vertexNormals.length === 3 ) {
- vertexNormals[ 0 ].copy( vertices[ face.a ] );
- vertexNormals[ 1 ].copy( vertices[ face.b ] );
- vertexNormals[ 2 ].copy( vertices[ face.c ] );
- } else {
- vertexNormals[ 0 ] = vertices[ face.a ].clone();
- vertexNormals[ 1 ] = vertices[ face.b ].clone();
- vertexNormals[ 2 ] = vertices[ face.c ].clone();
- }
- }
- if ( this.faces.length > 0 ) {
- this.normalsNeedUpdate = true;
- }
- },
- computeFlatVertexNormals: function () {
- this.computeFaceNormals();
- for ( let f = 0, fl = this.faces.length; f < fl; f ++ ) {
- const face = this.faces[ f ];
- const vertexNormals = face.vertexNormals;
- if ( vertexNormals.length === 3 ) {
- vertexNormals[ 0 ].copy( face.normal );
- vertexNormals[ 1 ].copy( face.normal );
- vertexNormals[ 2 ].copy( face.normal );
- } else {
- vertexNormals[ 0 ] = face.normal.clone();
- vertexNormals[ 1 ] = face.normal.clone();
- vertexNormals[ 2 ] = face.normal.clone();
- }
- }
- if ( this.faces.length > 0 ) {
- this.normalsNeedUpdate = true;
- }
- },
- computeMorphNormals: function () {
- // save original normals
- // - create temp variables on first access
- // otherwise just copy (for faster repeated calls)
- for ( let f = 0, fl = this.faces.length; f < fl; f ++ ) {
- const face = this.faces[ f ];
- if ( ! face.__originalFaceNormal ) {
- face.__originalFaceNormal = face.normal.clone();
- } else {
- face.__originalFaceNormal.copy( face.normal );
- }
- if ( ! face.__originalVertexNormals ) face.__originalVertexNormals = [];
- for ( let i = 0, il = face.vertexNormals.length; i < il; i ++ ) {
- if ( ! face.__originalVertexNormals[ i ] ) {
- face.__originalVertexNormals[ i ] = face.vertexNormals[ i ].clone();
- } else {
- face.__originalVertexNormals[ i ].copy( face.vertexNormals[ i ] );
- }
- }
- }
- // use temp geometry to compute face and vertex normals for each morph
- const tmpGeo = new Geometry();
- tmpGeo.faces = this.faces;
- for ( let i = 0, il = this.morphTargets.length; i < il; i ++ ) {
- // create on first access
- if ( ! this.morphNormals[ i ] ) {
- this.morphNormals[ i ] = {};
- this.morphNormals[ i ].faceNormals = [];
- this.morphNormals[ i ].vertexNormals = [];
- const dstNormalsFace = this.morphNormals[ i ].faceNormals;
- const dstNormalsVertex = this.morphNormals[ i ].vertexNormals;
- for ( let f = 0, fl = this.faces.length; f < fl; f ++ ) {
- const faceNormal = new Vector3();
- const vertexNormals = { a: new Vector3(), b: new Vector3(), c: new Vector3() };
- dstNormalsFace.push( faceNormal );
- dstNormalsVertex.push( vertexNormals );
- }
- }
- const morphNormals = this.morphNormals[ i ];
- // set vertices to morph target
- tmpGeo.vertices = this.morphTargets[ i ].vertices;
- // compute morph normals
- tmpGeo.computeFaceNormals();
- tmpGeo.computeVertexNormals();
- // store morph normals
- for ( let f = 0, fl = this.faces.length; f < fl; f ++ ) {
- const face = this.faces[ f ];
- const faceNormal = morphNormals.faceNormals[ f ];
- const vertexNormals = morphNormals.vertexNormals[ f ];
- faceNormal.copy( face.normal );
- vertexNormals.a.copy( face.vertexNormals[ 0 ] );
- vertexNormals.b.copy( face.vertexNormals[ 1 ] );
- vertexNormals.c.copy( face.vertexNormals[ 2 ] );
- }
- }
- // restore original normals
- for ( let f = 0, fl = this.faces.length; f < fl; f ++ ) {
- const face = this.faces[ f ];
- face.normal = face.__originalFaceNormal;
- face.vertexNormals = face.__originalVertexNormals;
- }
- },
- computeBoundingBox: function () {
- if ( this.boundingBox === null ) {
- this.boundingBox = new Box3();
- }
- this.boundingBox.setFromPoints( this.vertices );
- },
- computeBoundingSphere: function () {
- if ( this.boundingSphere === null ) {
- this.boundingSphere = new Sphere();
- }
- this.boundingSphere.setFromPoints( this.vertices );
- },
- merge: function ( geometry, matrix, materialIndexOffset = 0 ) {
- if ( ! ( geometry && geometry.isGeometry ) ) {
- console.error( 'THREE.Geometry.merge(): geometry not an instance of THREE.Geometry.', geometry );
- return;
- }
- let normalMatrix;
- const vertexOffset = this.vertices.length,
- vertices1 = this.vertices,
- vertices2 = geometry.vertices,
- faces1 = this.faces,
- faces2 = geometry.faces,
- colors1 = this.colors,
- colors2 = geometry.colors;
- if ( matrix !== undefined ) {
- normalMatrix = new Matrix3().getNormalMatrix( matrix );
- }
- // vertices
- for ( let i = 0, il = vertices2.length; i < il; i ++ ) {
- const vertex = vertices2[ i ];
- const vertexCopy = vertex.clone();
- if ( matrix !== undefined ) vertexCopy.applyMatrix4( matrix );
- vertices1.push( vertexCopy );
- }
- // colors
- for ( let i = 0, il = colors2.length; i < il; i ++ ) {
- colors1.push( colors2[ i ].clone() );
- }
- // faces
- for ( let i = 0, il = faces2.length; i < il; i ++ ) {
- const face = faces2[ i ];
- let normal, color;
- const faceVertexNormals = face.vertexNormals,
- faceVertexColors = face.vertexColors;
- const faceCopy = new Face3( face.a + vertexOffset, face.b + vertexOffset, face.c + vertexOffset );
- faceCopy.normal.copy( face.normal );
- if ( normalMatrix !== undefined ) {
- faceCopy.normal.applyMatrix3( normalMatrix ).normalize();
- }
- for ( let j = 0, jl = faceVertexNormals.length; j < jl; j ++ ) {
- normal = faceVertexNormals[ j ].clone();
- if ( normalMatrix !== undefined ) {
- normal.applyMatrix3( normalMatrix ).normalize();
- }
- faceCopy.vertexNormals.push( normal );
- }
- faceCopy.color.copy( face.color );
- for ( let j = 0, jl = faceVertexColors.length; j < jl; j ++ ) {
- color = faceVertexColors[ j ];
- faceCopy.vertexColors.push( color.clone() );
- }
- faceCopy.materialIndex = face.materialIndex + materialIndexOffset;
- faces1.push( faceCopy );
- }
- // uvs
- for ( let i = 0, il = geometry.faceVertexUvs.length; i < il; i ++ ) {
- const faceVertexUvs2 = geometry.faceVertexUvs[ i ];
- if ( this.faceVertexUvs[ i ] === undefined ) this.faceVertexUvs[ i ] = [];
- for ( let j = 0, jl = faceVertexUvs2.length; j < jl; j ++ ) {
- const uvs2 = faceVertexUvs2[ j ], uvsCopy = [];
- for ( let k = 0, kl = uvs2.length; k < kl; k ++ ) {
- uvsCopy.push( uvs2[ k ].clone() );
- }
- this.faceVertexUvs[ i ].push( uvsCopy );
- }
- }
- },
- mergeMesh: function ( mesh ) {
- if ( ! ( mesh && mesh.isMesh ) ) {
- console.error( 'THREE.Geometry.mergeMesh(): mesh not an instance of THREE.Mesh.', mesh );
- return;
- }
- if ( mesh.matrixAutoUpdate ) mesh.updateMatrix();
- this.merge( mesh.geometry, mesh.matrix );
- },
- /*
- * Checks for duplicate vertices with hashmap.
- * Duplicated vertices are removed
- * and faces' vertices are updated.
- */
- mergeVertices: function ( precisionPoints = 4 ) {
- const verticesMap = {}; // Hashmap for looking up vertices by position coordinates (and making sure they are unique)
- const unique = [], changes = [];
- const precision = Math.pow( 10, precisionPoints );
- for ( let i = 0, il = this.vertices.length; i < il; i ++ ) {
- const v = this.vertices[ i ];
- const key = Math.round( v.x * precision ) + '_' + Math.round( v.y * precision ) + '_' + Math.round( v.z * precision );
- if ( verticesMap[ key ] === undefined ) {
- verticesMap[ key ] = i;
- unique.push( this.vertices[ i ] );
- changes[ i ] = unique.length - 1;
- } else {
- //console.log('Duplicate vertex found. ', i, ' could be using ', verticesMap[key]);
- changes[ i ] = changes[ verticesMap[ key ] ];
- }
- }
- // if faces are completely degenerate after merging vertices, we
- // have to remove them from the geometry.
- const faceIndicesToRemove = [];
- for ( let i = 0, il = this.faces.length; i < il; i ++ ) {
- const face = this.faces[ i ];
- face.a = changes[ face.a ];
- face.b = changes[ face.b ];
- face.c = changes[ face.c ];
- const indices = [ face.a, face.b, face.c ];
- // if any duplicate vertices are found in a Face3
- // we have to remove the face as nothing can be saved
- for ( let n = 0; n < 3; n ++ ) {
- if ( indices[ n ] === indices[ ( n + 1 ) % 3 ] ) {
- faceIndicesToRemove.push( i );
- break;
- }
- }
- }
- for ( let i = faceIndicesToRemove.length - 1; i >= 0; i -- ) {
- const idx = faceIndicesToRemove[ i ];
- this.faces.splice( idx, 1 );
- for ( let j = 0, jl = this.faceVertexUvs.length; j < jl; j ++ ) {
- this.faceVertexUvs[ j ].splice( idx, 1 );
- }
- }
- // Use unique set of vertices
- const diff = this.vertices.length - unique.length;
- this.vertices = unique;
- return diff;
- },
- setFromPoints: function ( points ) {
- this.vertices = [];
- for ( let i = 0, l = points.length; i < l; i ++ ) {
- const point = points[ i ];
- this.vertices.push( new Vector3( point.x, point.y, point.z || 0 ) );
- }
- return this;
- },
- sortFacesByMaterialIndex: function () {
- const faces = this.faces;
- const length = faces.length;
- // tag faces
- for ( let i = 0; i < length; i ++ ) {
- faces[ i ]._id = i;
- }
- // sort faces
- function materialIndexSort( a, b ) {
- return a.materialIndex - b.materialIndex;
- }
- faces.sort( materialIndexSort );
- // sort uvs
- const uvs1 = this.faceVertexUvs[ 0 ];
- const uvs2 = this.faceVertexUvs[ 1 ];
- let newUvs1, newUvs2;
- if ( uvs1 && uvs1.length === length ) newUvs1 = [];
- if ( uvs2 && uvs2.length === length ) newUvs2 = [];
- for ( let i = 0; i < length; i ++ ) {
- const id = faces[ i ]._id;
- if ( newUvs1 ) newUvs1.push( uvs1[ id ] );
- if ( newUvs2 ) newUvs2.push( uvs2[ id ] );
- }
- if ( newUvs1 ) this.faceVertexUvs[ 0 ] = newUvs1;
- if ( newUvs2 ) this.faceVertexUvs[ 1 ] = newUvs2;
- },
- toJSON: function () {
- const data = {
- metadata: {
- version: 4.5,
- type: 'Geometry',
- generator: 'Geometry.toJSON'
- }
- };
- // standard Geometry serialization
- data.uuid = this.uuid;
- data.type = this.type;
- if ( this.name !== '' ) data.name = this.name;
- if ( this.parameters !== undefined ) {
- const parameters = this.parameters;
- for ( const key in parameters ) {
- if ( parameters[ key ] !== undefined ) data[ key ] = parameters[ key ];
- }
- return data;
- }
- const vertices = [];
- for ( let i = 0; i < this.vertices.length; i ++ ) {
- const vertex = this.vertices[ i ];
- vertices.push( vertex.x, vertex.y, vertex.z );
- }
- const faces = [];
- const normals = [];
- const normalsHash = {};
- const colors = [];
- const colorsHash = {};
- const uvs = [];
- const uvsHash = {};
- for ( let i = 0; i < this.faces.length; i ++ ) {
- const face = this.faces[ i ];
- const hasMaterial = true;
- const hasFaceUv = false; // deprecated
- const hasFaceVertexUv = this.faceVertexUvs[ 0 ][ i ] !== undefined;
- const hasFaceNormal = face.normal.length() > 0;
- const hasFaceVertexNormal = face.vertexNormals.length > 0;
- const hasFaceColor = face.color.r !== 1 || face.color.g !== 1 || face.color.b !== 1;
- const hasFaceVertexColor = face.vertexColors.length > 0;
- let faceType = 0;
- faceType = setBit( faceType, 0, 0 ); // isQuad
- faceType = setBit( faceType, 1, hasMaterial );
- faceType = setBit( faceType, 2, hasFaceUv );
- faceType = setBit( faceType, 3, hasFaceVertexUv );
- faceType = setBit( faceType, 4, hasFaceNormal );
- faceType = setBit( faceType, 5, hasFaceVertexNormal );
- faceType = setBit( faceType, 6, hasFaceColor );
- faceType = setBit( faceType, 7, hasFaceVertexColor );
- faces.push( faceType );
- faces.push( face.a, face.b, face.c );
- faces.push( face.materialIndex );
- if ( hasFaceVertexUv ) {
- const faceVertexUvs = this.faceVertexUvs[ 0 ][ i ];
- faces.push(
- getUvIndex( faceVertexUvs[ 0 ] ),
- getUvIndex( faceVertexUvs[ 1 ] ),
- getUvIndex( faceVertexUvs[ 2 ] )
- );
- }
- if ( hasFaceNormal ) {
- faces.push( getNormalIndex( face.normal ) );
- }
- if ( hasFaceVertexNormal ) {
- const vertexNormals = face.vertexNormals;
- faces.push(
- getNormalIndex( vertexNormals[ 0 ] ),
- getNormalIndex( vertexNormals[ 1 ] ),
- getNormalIndex( vertexNormals[ 2 ] )
- );
- }
- if ( hasFaceColor ) {
- faces.push( getColorIndex( face.color ) );
- }
- if ( hasFaceVertexColor ) {
- const vertexColors = face.vertexColors;
- faces.push(
- getColorIndex( vertexColors[ 0 ] ),
- getColorIndex( vertexColors[ 1 ] ),
- getColorIndex( vertexColors[ 2 ] )
- );
- }
- }
- function setBit( value, position, enabled ) {
- return enabled ? value | ( 1 << position ) : value & ( ~ ( 1 << position ) );
- }
- function getNormalIndex( normal ) {
- const hash = normal.x.toString() + normal.y.toString() + normal.z.toString();
- if ( normalsHash[ hash ] !== undefined ) {
- return normalsHash[ hash ];
- }
- normalsHash[ hash ] = normals.length / 3;
- normals.push( normal.x, normal.y, normal.z );
- return normalsHash[ hash ];
- }
- function getColorIndex( color ) {
- const hash = color.r.toString() + color.g.toString() + color.b.toString();
- if ( colorsHash[ hash ] !== undefined ) {
- return colorsHash[ hash ];
- }
- colorsHash[ hash ] = colors.length;
- colors.push( color.getHex() );
- return colorsHash[ hash ];
- }
- function getUvIndex( uv ) {
- const hash = uv.x.toString() + uv.y.toString();
- if ( uvsHash[ hash ] !== undefined ) {
- return uvsHash[ hash ];
- }
- uvsHash[ hash ] = uvs.length / 2;
- uvs.push( uv.x, uv.y );
- return uvsHash[ hash ];
- }
- data.data = {};
- data.data.vertices = vertices;
- data.data.normals = normals;
- if ( colors.length > 0 ) data.data.colors = colors;
- if ( uvs.length > 0 ) data.data.uvs = [ uvs ]; // temporal backward compatibility
- data.data.faces = faces;
- return data;
- },
- clone: function () {
- /*
- // Handle primitives
- const parameters = this.parameters;
- if ( parameters !== undefined ) {
- const values = [];
- for ( const key in parameters ) {
- values.push( parameters[ key ] );
- }
- const geometry = Object.create( this.constructor.prototype );
- this.constructor.apply( geometry, values );
- return geometry;
- }
- return new this.constructor().copy( this );
- */
- return new Geometry().copy( this );
- },
- copy: function ( source ) {
- // reset
- this.vertices = [];
- this.colors = [];
- this.faces = [];
- this.faceVertexUvs = [[]];
- this.morphTargets = [];
- this.morphNormals = [];
- this.skinWeights = [];
- this.skinIndices = [];
- this.lineDistances = [];
- this.boundingBox = null;
- this.boundingSphere = null;
- // name
- this.name = source.name;
- // vertices
- const vertices = source.vertices;
- for ( let i = 0, il = vertices.length; i < il; i ++ ) {
- this.vertices.push( vertices[ i ].clone() );
- }
- // colors
- const colors = source.colors;
- for ( let i = 0, il = colors.length; i < il; i ++ ) {
- this.colors.push( colors[ i ].clone() );
- }
- // faces
- const faces = source.faces;
- for ( let i = 0, il = faces.length; i < il; i ++ ) {
- this.faces.push( faces[ i ].clone() );
- }
- // face vertex uvs
- for ( let i = 0, il = source.faceVertexUvs.length; i < il; i ++ ) {
- const faceVertexUvs = source.faceVertexUvs[ i ];
- if ( this.faceVertexUvs[ i ] === undefined ) {
- this.faceVertexUvs[ i ] = [];
- }
- for ( let j = 0, jl = faceVertexUvs.length; j < jl; j ++ ) {
- const uvs = faceVertexUvs[ j ], uvsCopy = [];
- for ( let k = 0, kl = uvs.length; k < kl; k ++ ) {
- const uv = uvs[ k ];
- uvsCopy.push( uv.clone() );
- }
- this.faceVertexUvs[ i ].push( uvsCopy );
- }
- }
- // morph targets
- const morphTargets = source.morphTargets;
- for ( let i = 0, il = morphTargets.length; i < il; i ++ ) {
- const morphTarget = {};
- morphTarget.name = morphTargets[ i ].name;
- // vertices
- if ( morphTargets[ i ].vertices !== undefined ) {
- morphTarget.vertices = [];
- for ( let j = 0, jl = morphTargets[ i ].vertices.length; j < jl; j ++ ) {
- morphTarget.vertices.push( morphTargets[ i ].vertices[ j ].clone() );
- }
- }
- // normals
- if ( morphTargets[ i ].normals !== undefined ) {
- morphTarget.normals = [];
- for ( let j = 0, jl = morphTargets[ i ].normals.length; j < jl; j ++ ) {
- morphTarget.normals.push( morphTargets[ i ].normals[ j ].clone() );
- }
- }
- this.morphTargets.push( morphTarget );
- }
- // morph normals
- const morphNormals = source.morphNormals;
- for ( let i = 0, il = morphNormals.length; i < il; i ++ ) {
- const morphNormal = {};
- // vertex normals
- if ( morphNormals[ i ].vertexNormals !== undefined ) {
- morphNormal.vertexNormals = [];
- for ( let j = 0, jl = morphNormals[ i ].vertexNormals.length; j < jl; j ++ ) {
- const srcVertexNormal = morphNormals[ i ].vertexNormals[ j ];
- const destVertexNormal = {};
- destVertexNormal.a = srcVertexNormal.a.clone();
- destVertexNormal.b = srcVertexNormal.b.clone();
- destVertexNormal.c = srcVertexNormal.c.clone();
- morphNormal.vertexNormals.push( destVertexNormal );
- }
- }
- // face normals
- if ( morphNormals[ i ].faceNormals !== undefined ) {
- morphNormal.faceNormals = [];
- for ( let j = 0, jl = morphNormals[ i ].faceNormals.length; j < jl; j ++ ) {
- morphNormal.faceNormals.push( morphNormals[ i ].faceNormals[ j ].clone() );
- }
- }
- this.morphNormals.push( morphNormal );
- }
- // skin weights
- const skinWeights = source.skinWeights;
- for ( let i = 0, il = skinWeights.length; i < il; i ++ ) {
- this.skinWeights.push( skinWeights[ i ].clone() );
- }
- // skin indices
- const skinIndices = source.skinIndices;
- for ( let i = 0, il = skinIndices.length; i < il; i ++ ) {
- this.skinIndices.push( skinIndices[ i ].clone() );
- }
- // line distances
- const lineDistances = source.lineDistances;
- for ( let i = 0, il = lineDistances.length; i < il; i ++ ) {
- this.lineDistances.push( lineDistances[ i ] );
- }
- // bounding box
- const boundingBox = source.boundingBox;
- if ( boundingBox !== null ) {
- this.boundingBox = boundingBox.clone();
- }
- // bounding sphere
- const boundingSphere = source.boundingSphere;
- if ( boundingSphere !== null ) {
- this.boundingSphere = boundingSphere.clone();
- }
- // update flags
- this.elementsNeedUpdate = source.elementsNeedUpdate;
- this.verticesNeedUpdate = source.verticesNeedUpdate;
- this.uvsNeedUpdate = source.uvsNeedUpdate;
- this.normalsNeedUpdate = source.normalsNeedUpdate;
- this.colorsNeedUpdate = source.colorsNeedUpdate;
- this.lineDistancesNeedUpdate = source.lineDistancesNeedUpdate;
- this.groupsNeedUpdate = source.groupsNeedUpdate;
- return this;
- },
- dispose: function () {
- this.dispatchEvent( { type: 'dispose' } );
- }
- } );
- class BoxGeometry extends Geometry {
- constructor( width, height, depth, widthSegments, heightSegments, depthSegments ) {
- super();
- this.type = 'BoxGeometry';
- this.parameters = {
- width: width,
- height: height,
- depth: depth,
- widthSegments: widthSegments,
- heightSegments: heightSegments,
- depthSegments: depthSegments
- };
- this.fromBufferGeometry( new BoxBufferGeometry( width, height, depth, widthSegments, heightSegments, depthSegments ) );
- this.mergeVertices();
- }
- }
- class CircleBufferGeometry extends BufferGeometry {
- constructor( radius = 1, segments = 8, thetaStart = 0, thetaLength = Math.PI * 2 ) {
- super();
- this.type = 'CircleBufferGeometry';
- this.parameters = {
- radius: radius,
- segments: segments,
- thetaStart: thetaStart,
- thetaLength: thetaLength
- };
- segments = Math.max( 3, segments );
- // buffers
- const indices = [];
- const vertices = [];
- const normals = [];
- const uvs = [];
- // helper variables
- const vertex = new Vector3();
- const uv = new Vector2$1();
- // center point
- vertices.push( 0, 0, 0 );
- normals.push( 0, 0, 1 );
- uvs.push( 0.5, 0.5 );
- for ( let s = 0, i = 3; s <= segments; s ++, i += 3 ) {
- const segment = thetaStart + s / segments * thetaLength;
- // vertex
- vertex.x = radius * Math.cos( segment );
- vertex.y = radius * Math.sin( segment );
- vertices.push( vertex.x, vertex.y, vertex.z );
- // normal
- normals.push( 0, 0, 1 );
- // uvs
- uv.x = ( vertices[ i ] / radius + 1 ) / 2;
- uv.y = ( vertices[ i + 1 ] / radius + 1 ) / 2;
- uvs.push( uv.x, uv.y );
- }
- // indices
- for ( let i = 1; i <= segments; i ++ ) {
- indices.push( i, i + 1, 0 );
- }
- // build geometry
- this.setIndex( indices );
- this.setAttribute( 'position', new Float32BufferAttribute( vertices, 3 ) );
- this.setAttribute( 'normal', new Float32BufferAttribute( normals, 3 ) );
- this.setAttribute( 'uv', new Float32BufferAttribute( uvs, 2 ) );
- }
- }
- class CircleGeometry extends Geometry {
- constructor( radius, segments, thetaStart, thetaLength ) {
- super();
- this.type = 'CircleGeometry';
- this.parameters = {
- radius: radius,
- segments: segments,
- thetaStart: thetaStart,
- thetaLength: thetaLength
- };
- this.fromBufferGeometry( new CircleBufferGeometry( radius, segments, thetaStart, thetaLength ) );
- this.mergeVertices();
- }
- }
- class CylinderBufferGeometry extends BufferGeometry {
- constructor( radiusTop = 1, radiusBottom = 1, height = 1, radialSegments = 8, heightSegments = 1, openEnded = false, thetaStart = 0, thetaLength = Math.PI * 2 ) {
- super();
- this.type = 'CylinderBufferGeometry';
- this.parameters = {
- radiusTop: radiusTop,
- radiusBottom: radiusBottom,
- height: height,
- radialSegments: radialSegments,
- heightSegments: heightSegments,
- openEnded: openEnded,
- thetaStart: thetaStart,
- thetaLength: thetaLength
- };
- const scope = this;
- radialSegments = Math.floor( radialSegments );
- heightSegments = Math.floor( heightSegments );
- // buffers
- const indices = [];
- const vertices = [];
- const normals = [];
- const uvs = [];
- // helper variables
- let index = 0;
- const indexArray = [];
- const halfHeight = height / 2;
- let groupStart = 0;
- // generate geometry
- generateTorso();
- if ( openEnded === false ) {
- if ( radiusTop > 0 ) generateCap( true );
- if ( radiusBottom > 0 ) generateCap( false );
- }
- // build geometry
- this.setIndex( indices );
- this.setAttribute( 'position', new Float32BufferAttribute( vertices, 3 ) );
- this.setAttribute( 'normal', new Float32BufferAttribute( normals, 3 ) );
- this.setAttribute( 'uv', new Float32BufferAttribute( uvs, 2 ) );
- function generateTorso() {
- const normal = new Vector3();
- const vertex = new Vector3();
- let groupCount = 0;
- // this will be used to calculate the normal
- const slope = ( radiusBottom - radiusTop ) / height;
- // generate vertices, normals and uvs
- for ( let y = 0; y <= heightSegments; y ++ ) {
- const indexRow = [];
- const v = y / heightSegments;
- // calculate the radius of the current row
- const radius = v * ( radiusBottom - radiusTop ) + radiusTop;
- for ( let x = 0; x <= radialSegments; x ++ ) {
- const u = x / radialSegments;
- const theta = u * thetaLength + thetaStart;
- const sinTheta = Math.sin( theta );
- const cosTheta = Math.cos( theta );
- // vertex
- vertex.x = radius * sinTheta;
- vertex.y = - v * height + halfHeight;
- vertex.z = radius * cosTheta;
- vertices.push( vertex.x, vertex.y, vertex.z );
- // normal
- normal.set( sinTheta, slope, cosTheta ).normalize();
- normals.push( normal.x, normal.y, normal.z );
- // uv
- uvs.push( u, 1 - v );
- // save index of vertex in respective row
- indexRow.push( index ++ );
- }
- // now save vertices of the row in our index array
- indexArray.push( indexRow );
- }
- // generate indices
- for ( let x = 0; x < radialSegments; x ++ ) {
- for ( let y = 0; y < heightSegments; y ++ ) {
- // we use the index array to access the correct indices
- const a = indexArray[ y ][ x ];
- const b = indexArray[ y + 1 ][ x ];
- const c = indexArray[ y + 1 ][ x + 1 ];
- const d = indexArray[ y ][ x + 1 ];
- // faces
- indices.push( a, b, d );
- indices.push( b, c, d );
- // update group counter
- groupCount += 6;
- }
- }
- // add a group to the geometry. this will ensure multi material support
- scope.addGroup( groupStart, groupCount, 0 );
- // calculate new start value for groups
- groupStart += groupCount;
- }
- function generateCap( top ) {
- // save the index of the first center vertex
- const centerIndexStart = index;
- const uv = new Vector2$1();
- const vertex = new Vector3();
- let groupCount = 0;
- const radius = ( top === true ) ? radiusTop : radiusBottom;
- const sign = ( top === true ) ? 1 : - 1;
- // first we generate the center vertex data of the cap.
- // because the geometry needs one set of uvs per face,
- // we must generate a center vertex per face/segment
- for ( let x = 1; x <= radialSegments; x ++ ) {
- // vertex
- vertices.push( 0, halfHeight * sign, 0 );
- // normal
- normals.push( 0, sign, 0 );
- // uv
- uvs.push( 0.5, 0.5 );
- // increase index
- index ++;
- }
- // save the index of the last center vertex
- const centerIndexEnd = index;
- // now we generate the surrounding vertices, normals and uvs
- for ( let x = 0; x <= radialSegments; x ++ ) {
- const u = x / radialSegments;
- const theta = u * thetaLength + thetaStart;
- const cosTheta = Math.cos( theta );
- const sinTheta = Math.sin( theta );
- // vertex
- vertex.x = radius * sinTheta;
- vertex.y = halfHeight * sign;
- vertex.z = radius * cosTheta;
- vertices.push( vertex.x, vertex.y, vertex.z );
- // normal
- normals.push( 0, sign, 0 );
- // uv
- uv.x = ( cosTheta * 0.5 ) + 0.5;
- uv.y = ( sinTheta * 0.5 * sign ) + 0.5;
- uvs.push( uv.x, uv.y );
- // increase index
- index ++;
- }
- // generate indices
- for ( let x = 0; x < radialSegments; x ++ ) {
- const c = centerIndexStart + x;
- const i = centerIndexEnd + x;
- if ( top === true ) {
- // face top
- indices.push( i, i + 1, c );
- } else {
- // face bottom
- indices.push( i + 1, i, c );
- }
- groupCount += 3;
- }
- // add a group to the geometry. this will ensure multi material support
- scope.addGroup( groupStart, groupCount, top === true ? 1 : 2 );
- // calculate new start value for groups
- groupStart += groupCount;
- }
- }
- }
- class CylinderGeometry extends Geometry {
- constructor( radiusTop, radiusBottom, height, radialSegments, heightSegments, openEnded, thetaStart, thetaLength ) {
- super();
- this.type = 'CylinderGeometry';
- this.parameters = {
- radiusTop: radiusTop,
- radiusBottom: radiusBottom,
- height: height,
- radialSegments: radialSegments,
- heightSegments: heightSegments,
- openEnded: openEnded,
- thetaStart: thetaStart,
- thetaLength: thetaLength
- };
- this.fromBufferGeometry( new CylinderBufferGeometry( radiusTop, radiusBottom, height, radialSegments, heightSegments, openEnded, thetaStart, thetaLength ) );
- this.mergeVertices();
- }
- }
- class ConeGeometry extends CylinderGeometry {
- constructor( radius, height, radialSegments, heightSegments, openEnded, thetaStart, thetaLength ) {
- super( 0, radius, height, radialSegments, heightSegments, openEnded, thetaStart, thetaLength );
- this.type = 'ConeGeometry';
- this.parameters = {
- radius: radius,
- height: height,
- radialSegments: radialSegments,
- heightSegments: heightSegments,
- openEnded: openEnded,
- thetaStart: thetaStart,
- thetaLength: thetaLength
- };
- }
- }
- class ConeBufferGeometry extends CylinderBufferGeometry {
- constructor( radius = 1, height = 1, radialSegments = 8, heightSegments = 1, openEnded = false, thetaStart = 0, thetaLength = Math.PI * 2 ) {
- super( 0, radius, height, radialSegments, heightSegments, openEnded, thetaStart, thetaLength );
- this.type = 'ConeBufferGeometry';
- this.parameters = {
- radius: radius,
- height: height,
- radialSegments: radialSegments,
- heightSegments: heightSegments,
- openEnded: openEnded,
- thetaStart: thetaStart,
- thetaLength: thetaLength
- };
- }
- }
- class PolyhedronBufferGeometry extends BufferGeometry {
- constructor( vertices, indices, radius = 1, detail = 0 ) {
- super();
- this.type = 'PolyhedronBufferGeometry';
- this.parameters = {
- vertices: vertices,
- indices: indices,
- radius: radius,
- detail: detail
- };
- // default buffer data
- const vertexBuffer = [];
- const uvBuffer = [];
- // the subdivision creates the vertex buffer data
- subdivide( detail );
- // all vertices should lie on a conceptual sphere with a given radius
- applyRadius( radius );
- // finally, create the uv data
- generateUVs();
- // build non-indexed geometry
- this.setAttribute( 'position', new Float32BufferAttribute( vertexBuffer, 3 ) );
- this.setAttribute( 'normal', new Float32BufferAttribute( vertexBuffer.slice(), 3 ) );
- this.setAttribute( 'uv', new Float32BufferAttribute( uvBuffer, 2 ) );
- if ( detail === 0 ) {
- this.computeVertexNormals(); // flat normals
- } else {
- this.normalizeNormals(); // smooth normals
- }
- // helper functions
- function subdivide( detail ) {
- const a = new Vector3();
- const b = new Vector3();
- const c = new Vector3();
- // iterate over all faces and apply a subdivison with the given detail value
- for ( let i = 0; i < indices.length; i += 3 ) {
- // get the vertices of the face
- getVertexByIndex( indices[ i + 0 ], a );
- getVertexByIndex( indices[ i + 1 ], b );
- getVertexByIndex( indices[ i + 2 ], c );
- // perform subdivision
- subdivideFace( a, b, c, detail );
- }
- }
- function subdivideFace( a, b, c, detail ) {
- const cols = detail + 1;
- // we use this multidimensional array as a data structure for creating the subdivision
- const v = [];
- // construct all of the vertices for this subdivision
- for ( let i = 0; i <= cols; i ++ ) {
- v[ i ] = [];
- const aj = a.clone().lerp( c, i / cols );
- const bj = b.clone().lerp( c, i / cols );
- const rows = cols - i;
- for ( let j = 0; j <= rows; j ++ ) {
- if ( j === 0 && i === cols ) {
- v[ i ][ j ] = aj;
- } else {
- v[ i ][ j ] = aj.clone().lerp( bj, j / rows );
- }
- }
- }
- // construct all of the faces
- for ( let i = 0; i < cols; i ++ ) {
- for ( let j = 0; j < 2 * ( cols - i ) - 1; j ++ ) {
- const k = Math.floor( j / 2 );
- if ( j % 2 === 0 ) {
- pushVertex( v[ i ][ k + 1 ] );
- pushVertex( v[ i + 1 ][ k ] );
- pushVertex( v[ i ][ k ] );
- } else {
- pushVertex( v[ i ][ k + 1 ] );
- pushVertex( v[ i + 1 ][ k + 1 ] );
- pushVertex( v[ i + 1 ][ k ] );
- }
- }
- }
- }
- function applyRadius( radius ) {
- const vertex = new Vector3();
- // iterate over the entire buffer and apply the radius to each vertex
- for ( let i = 0; i < vertexBuffer.length; i += 3 ) {
- vertex.x = vertexBuffer[ i + 0 ];
- vertex.y = vertexBuffer[ i + 1 ];
- vertex.z = vertexBuffer[ i + 2 ];
- vertex.normalize().multiplyScalar( radius );
- vertexBuffer[ i + 0 ] = vertex.x;
- vertexBuffer[ i + 1 ] = vertex.y;
- vertexBuffer[ i + 2 ] = vertex.z;
- }
- }
- function generateUVs() {
- const vertex = new Vector3();
- for ( let i = 0; i < vertexBuffer.length; i += 3 ) {
- vertex.x = vertexBuffer[ i + 0 ];
- vertex.y = vertexBuffer[ i + 1 ];
- vertex.z = vertexBuffer[ i + 2 ];
- const u = azimuth( vertex ) / 2 / Math.PI + 0.5;
- const v = inclination( vertex ) / Math.PI + 0.5;
- uvBuffer.push( u, 1 - v );
- }
- correctUVs();
- correctSeam();
- }
- function correctSeam() {
- // handle case when face straddles the seam, see #3269
- for ( let i = 0; i < uvBuffer.length; i += 6 ) {
- // uv data of a single face
- const x0 = uvBuffer[ i + 0 ];
- const x1 = uvBuffer[ i + 2 ];
- const x2 = uvBuffer[ i + 4 ];
- const max = Math.max( x0, x1, x2 );
- const min = Math.min( x0, x1, x2 );
- // 0.9 is somewhat arbitrary
- if ( max > 0.9 && min < 0.1 ) {
- if ( x0 < 0.2 ) uvBuffer[ i + 0 ] += 1;
- if ( x1 < 0.2 ) uvBuffer[ i + 2 ] += 1;
- if ( x2 < 0.2 ) uvBuffer[ i + 4 ] += 1;
- }
- }
- }
- function pushVertex( vertex ) {
- vertexBuffer.push( vertex.x, vertex.y, vertex.z );
- }
- function getVertexByIndex( index, vertex ) {
- const stride = index * 3;
- vertex.x = vertices[ stride + 0 ];
- vertex.y = vertices[ stride + 1 ];
- vertex.z = vertices[ stride + 2 ];
- }
- function correctUVs() {
- const a = new Vector3();
- const b = new Vector3();
- const c = new Vector3();
- const centroid = new Vector3();
- const uvA = new Vector2$1();
- const uvB = new Vector2$1();
- const uvC = new Vector2$1();
- for ( let i = 0, j = 0; i < vertexBuffer.length; i += 9, j += 6 ) {
- a.set( vertexBuffer[ i + 0 ], vertexBuffer[ i + 1 ], vertexBuffer[ i + 2 ] );
- b.set( vertexBuffer[ i + 3 ], vertexBuffer[ i + 4 ], vertexBuffer[ i + 5 ] );
- c.set( vertexBuffer[ i + 6 ], vertexBuffer[ i + 7 ], vertexBuffer[ i + 8 ] );
- uvA.set( uvBuffer[ j + 0 ], uvBuffer[ j + 1 ] );
- uvB.set( uvBuffer[ j + 2 ], uvBuffer[ j + 3 ] );
- uvC.set( uvBuffer[ j + 4 ], uvBuffer[ j + 5 ] );
- centroid.copy( a ).add( b ).add( c ).divideScalar( 3 );
- const azi = azimuth( centroid );
- correctUV( uvA, j + 0, a, azi );
- correctUV( uvB, j + 2, b, azi );
- correctUV( uvC, j + 4, c, azi );
- }
- }
- function correctUV( uv, stride, vector, azimuth ) {
- if ( ( azimuth < 0 ) && ( uv.x === 1 ) ) {
- uvBuffer[ stride ] = uv.x - 1;
- }
- if ( ( vector.x === 0 ) && ( vector.z === 0 ) ) {
- uvBuffer[ stride ] = azimuth / 2 / Math.PI + 0.5;
- }
- }
- // Angle around the Y axis, counter-clockwise when looking from above.
- function azimuth( vector ) {
- return Math.atan2( vector.z, - vector.x );
- }
- // Angle above the XZ plane.
- function inclination( vector ) {
- return Math.atan2( - vector.y, Math.sqrt( ( vector.x * vector.x ) + ( vector.z * vector.z ) ) );
- }
- }
- }
- class DodecahedronBufferGeometry extends PolyhedronBufferGeometry {
- constructor( radius = 1, detail = 0 ) {
- const t = ( 1 + Math.sqrt( 5 ) ) / 2;
- const r = 1 / t;
- const vertices = [
- // (±1, ±1, ±1)
- - 1, - 1, - 1, - 1, - 1, 1,
- - 1, 1, - 1, - 1, 1, 1,
- 1, - 1, - 1, 1, - 1, 1,
- 1, 1, - 1, 1, 1, 1,
- // (0, ±1/φ, ±φ)
- 0, - r, - t, 0, - r, t,
- 0, r, - t, 0, r, t,
- // (±1/φ, ±φ, 0)
- - r, - t, 0, - r, t, 0,
- r, - t, 0, r, t, 0,
- // (±φ, 0, ±1/φ)
- - t, 0, - r, t, 0, - r,
- - t, 0, r, t, 0, r
- ];
- const indices = [
- 3, 11, 7, 3, 7, 15, 3, 15, 13,
- 7, 19, 17, 7, 17, 6, 7, 6, 15,
- 17, 4, 8, 17, 8, 10, 17, 10, 6,
- 8, 0, 16, 8, 16, 2, 8, 2, 10,
- 0, 12, 1, 0, 1, 18, 0, 18, 16,
- 6, 10, 2, 6, 2, 13, 6, 13, 15,
- 2, 16, 18, 2, 18, 3, 2, 3, 13,
- 18, 1, 9, 18, 9, 11, 18, 11, 3,
- 4, 14, 12, 4, 12, 0, 4, 0, 8,
- 11, 9, 5, 11, 5, 19, 11, 19, 7,
- 19, 5, 14, 19, 14, 4, 19, 4, 17,
- 1, 12, 14, 1, 14, 5, 1, 5, 9
- ];
- super( vertices, indices, radius, detail );
- this.type = 'DodecahedronBufferGeometry';
- this.parameters = {
- radius: radius,
- detail: detail
- };
- }
- }
- class DodecahedronGeometry extends Geometry {
- constructor( radius, detail ) {
- super();
- this.type = 'DodecahedronGeometry';
- this.parameters = {
- radius: radius,
- detail: detail
- };
- this.fromBufferGeometry( new DodecahedronBufferGeometry( radius, detail ) );
- this.mergeVertices();
- }
- }
- const _v0$2 = new Vector3();
- const _v1$5 = new Vector3();
- const _normal$1 = new Vector3();
- const _triangle = new Triangle();
- class EdgesGeometry extends BufferGeometry {
- constructor( geometry, thresholdAngle ) {
- super();
- this.type = 'EdgesGeometry';
- this.parameters = {
- thresholdAngle: thresholdAngle
- };
- thresholdAngle = ( thresholdAngle !== undefined ) ? thresholdAngle : 1;
- if ( geometry.isGeometry ) {
- geometry = new BufferGeometry().fromGeometry( geometry );
- }
- const precisionPoints = 4;
- const precision = Math.pow( 10, precisionPoints );
- const thresholdDot = Math.cos( MathUtils.DEG2RAD * thresholdAngle );
- const indexAttr = geometry.getIndex();
- const positionAttr = geometry.getAttribute( 'position' );
- const indexCount = indexAttr ? indexAttr.count : positionAttr.count;
- const indexArr = [ 0, 0, 0 ];
- const vertKeys = [ 'a', 'b', 'c' ];
- const hashes = new Array( 3 );
- const edgeData = {};
- const vertices = [];
- for ( let i = 0; i < indexCount; i += 3 ) {
- if ( indexAttr ) {
- indexArr[ 0 ] = indexAttr.getX( i );
- indexArr[ 1 ] = indexAttr.getX( i + 1 );
- indexArr[ 2 ] = indexAttr.getX( i + 2 );
- } else {
- indexArr[ 0 ] = i;
- indexArr[ 1 ] = i + 1;
- indexArr[ 2 ] = i + 2;
- }
- const { a, b, c } = _triangle;
- a.fromBufferAttribute( positionAttr, indexArr[ 0 ] );
- b.fromBufferAttribute( positionAttr, indexArr[ 1 ] );
- c.fromBufferAttribute( positionAttr, indexArr[ 2 ] );
- _triangle.getNormal( _normal$1 );
- // create hashes for the edge from the vertices
- hashes[ 0 ] = `${ Math.round( a.x * precision ) },${ Math.round( a.y * precision ) },${ Math.round( a.z * precision ) }`;
- hashes[ 1 ] = `${ Math.round( b.x * precision ) },${ Math.round( b.y * precision ) },${ Math.round( b.z * precision ) }`;
- hashes[ 2 ] = `${ Math.round( c.x * precision ) },${ Math.round( c.y * precision ) },${ Math.round( c.z * precision ) }`;
- // skip degenerate triangles
- if ( hashes[ 0 ] === hashes[ 1 ] || hashes[ 1 ] === hashes[ 2 ] || hashes[ 2 ] === hashes[ 0 ] ) {
- continue;
- }
- // iterate over every edge
- for ( let j = 0; j < 3; j ++ ) {
- // get the first and next vertex making up the edge
- const jNext = ( j + 1 ) % 3;
- const vecHash0 = hashes[ j ];
- const vecHash1 = hashes[ jNext ];
- const v0 = _triangle[ vertKeys[ j ] ];
- const v1 = _triangle[ vertKeys[ jNext ] ];
- const hash = `${ vecHash0 }_${ vecHash1 }`;
- const reverseHash = `${ vecHash1 }_${ vecHash0 }`;
- if ( reverseHash in edgeData && edgeData[ reverseHash ] ) {
- // if we found a sibling edge add it into the vertex array if
- // it meets the angle threshold and delete the edge from the map.
- if ( _normal$1.dot( edgeData[ reverseHash ].normal ) <= thresholdDot ) {
- vertices.push( v0.x, v0.y, v0.z );
- vertices.push( v1.x, v1.y, v1.z );
- }
- edgeData[ reverseHash ] = null;
- } else if ( ! ( hash in edgeData ) ) {
- // if we've already got an edge here then skip adding a new one
- edgeData[ hash ] = {
- index0: indexArr[ j ],
- index1: indexArr[ jNext ],
- normal: _normal$1.clone(),
- };
- }
- }
- }
- // iterate over all remaining, unmatched edges and add them to the vertex array
- for ( const key in edgeData ) {
- if ( edgeData[ key ] ) {
- const { index0, index1 } = edgeData[ key ];
- _v0$2.fromBufferAttribute( positionAttr, index0 );
- _v1$5.fromBufferAttribute( positionAttr, index1 );
- vertices.push( _v0$2.x, _v0$2.y, _v0$2.z );
- vertices.push( _v1$5.x, _v1$5.y, _v1$5.z );
- }
- }
- this.setAttribute( 'position', new Float32BufferAttribute( vertices, 3 ) );
- }
- }
- /**
- * Port from https://github.com/mapbox/earcut (v2.2.2)
- */
- const Earcut = {
- triangulate: function ( data, holeIndices, dim ) {
- dim = dim || 2;
- const hasHoles = holeIndices && holeIndices.length;
- const outerLen = hasHoles ? holeIndices[ 0 ] * dim : data.length;
- let outerNode = linkedList( data, 0, outerLen, dim, true );
- const triangles = [];
- if ( ! outerNode || outerNode.next === outerNode.prev ) return triangles;
- let minX, minY, maxX, maxY, x, y, invSize;
- if ( hasHoles ) outerNode = eliminateHoles( data, holeIndices, outerNode, dim );
- // if the shape is not too simple, we'll use z-order curve hash later; calculate polygon bbox
- if ( data.length > 80 * dim ) {
- minX = maxX = data[ 0 ];
- minY = maxY = data[ 1 ];
- for ( let i = dim; i < outerLen; i += dim ) {
- x = data[ i ];
- y = data[ i + 1 ];
- if ( x < minX ) minX = x;
- if ( y < minY ) minY = y;
- if ( x > maxX ) maxX = x;
- if ( y > maxY ) maxY = y;
- }
- // minX, minY and invSize are later used to transform coords into integers for z-order calculation
- invSize = Math.max( maxX - minX, maxY - minY );
- invSize = invSize !== 0 ? 1 / invSize : 0;
- }
- earcutLinked( outerNode, triangles, dim, minX, minY, invSize );
- return triangles;
- }
- };
- // create a circular doubly linked list from polygon points in the specified winding order
- function linkedList( data, start, end, dim, clockwise ) {
- let i, last;
- if ( clockwise === ( signedArea( data, start, end, dim ) > 0 ) ) {
- for ( i = start; i < end; i += dim ) last = insertNode( i, data[ i ], data[ i + 1 ], last );
- } else {
- for ( i = end - dim; i >= start; i -= dim ) last = insertNode( i, data[ i ], data[ i + 1 ], last );
- }
- if ( last && equals( last, last.next ) ) {
- removeNode( last );
- last = last.next;
- }
- return last;
- }
- // eliminate colinear or duplicate points
- function filterPoints( start, end ) {
- if ( ! start ) return start;
- if ( ! end ) end = start;
- let p = start,
- again;
- do {
- again = false;
- if ( ! p.steiner && ( equals( p, p.next ) || area( p.prev, p, p.next ) === 0 ) ) {
- removeNode( p );
- p = end = p.prev;
- if ( p === p.next ) break;
- again = true;
- } else {
- p = p.next;
- }
- } while ( again || p !== end );
- return end;
- }
- // main ear slicing loop which triangulates a polygon (given as a linked list)
- function earcutLinked( ear, triangles, dim, minX, minY, invSize, pass ) {
- if ( ! ear ) return;
- // interlink polygon nodes in z-order
- if ( ! pass && invSize ) indexCurve( ear, minX, minY, invSize );
- let stop = ear,
- prev, next;
- // iterate through ears, slicing them one by one
- while ( ear.prev !== ear.next ) {
- prev = ear.prev;
- next = ear.next;
- if ( invSize ? isEarHashed( ear, minX, minY, invSize ) : isEar( ear ) ) {
- // cut off the triangle
- triangles.push( prev.i / dim );
- triangles.push( ear.i / dim );
- triangles.push( next.i / dim );
- removeNode( ear );
- // skipping the next vertex leads to less sliver triangles
- ear = next.next;
- stop = next.next;
- continue;
- }
- ear = next;
- // if we looped through the whole remaining polygon and can't find any more ears
- if ( ear === stop ) {
- // try filtering points and slicing again
- if ( ! pass ) {
- earcutLinked( filterPoints( ear ), triangles, dim, minX, minY, invSize, 1 );
- // if this didn't work, try curing all small self-intersections locally
- } else if ( pass === 1 ) {
- ear = cureLocalIntersections( filterPoints( ear ), triangles, dim );
- earcutLinked( ear, triangles, dim, minX, minY, invSize, 2 );
- // as a last resort, try splitting the remaining polygon into two
- } else if ( pass === 2 ) {
- splitEarcut( ear, triangles, dim, minX, minY, invSize );
- }
- break;
- }
- }
- }
- // check whether a polygon node forms a valid ear with adjacent nodes
- function isEar( ear ) {
- const a = ear.prev,
- b = ear,
- c = ear.next;
- if ( area( a, b, c ) >= 0 ) return false; // reflex, can't be an ear
- // now make sure we don't have other points inside the potential ear
- let p = ear.next.next;
- while ( p !== ear.prev ) {
- if ( pointInTriangle( a.x, a.y, b.x, b.y, c.x, c.y, p.x, p.y ) &&
- area( p.prev, p, p.next ) >= 0 ) return false;
- p = p.next;
- }
- return true;
- }
- function isEarHashed( ear, minX, minY, invSize ) {
- const a = ear.prev,
- b = ear,
- c = ear.next;
- if ( area( a, b, c ) >= 0 ) return false; // reflex, can't be an ear
- // triangle bbox; min & max are calculated like this for speed
- const minTX = a.x < b.x ? ( a.x < c.x ? a.x : c.x ) : ( b.x < c.x ? b.x : c.x ),
- minTY = a.y < b.y ? ( a.y < c.y ? a.y : c.y ) : ( b.y < c.y ? b.y : c.y ),
- maxTX = a.x > b.x ? ( a.x > c.x ? a.x : c.x ) : ( b.x > c.x ? b.x : c.x ),
- maxTY = a.y > b.y ? ( a.y > c.y ? a.y : c.y ) : ( b.y > c.y ? b.y : c.y );
- // z-order range for the current triangle bbox;
- const minZ = zOrder( minTX, minTY, minX, minY, invSize ),
- maxZ = zOrder( maxTX, maxTY, minX, minY, invSize );
- let p = ear.prevZ,
- n = ear.nextZ;
- // look for points inside the triangle in both directions
- while ( p && p.z >= minZ && n && n.z <= maxZ ) {
- if ( p !== ear.prev && p !== ear.next &&
- pointInTriangle( a.x, a.y, b.x, b.y, c.x, c.y, p.x, p.y ) &&
- area( p.prev, p, p.next ) >= 0 ) return false;
- p = p.prevZ;
- if ( n !== ear.prev && n !== ear.next &&
- pointInTriangle( a.x, a.y, b.x, b.y, c.x, c.y, n.x, n.y ) &&
- area( n.prev, n, n.next ) >= 0 ) return false;
- n = n.nextZ;
- }
- // look for remaining points in decreasing z-order
- while ( p && p.z >= minZ ) {
- if ( p !== ear.prev && p !== ear.next &&
- pointInTriangle( a.x, a.y, b.x, b.y, c.x, c.y, p.x, p.y ) &&
- area( p.prev, p, p.next ) >= 0 ) return false;
- p = p.prevZ;
- }
- // look for remaining points in increasing z-order
- while ( n && n.z <= maxZ ) {
- if ( n !== ear.prev && n !== ear.next &&
- pointInTriangle( a.x, a.y, b.x, b.y, c.x, c.y, n.x, n.y ) &&
- area( n.prev, n, n.next ) >= 0 ) return false;
- n = n.nextZ;
- }
- return true;
- }
- // go through all polygon nodes and cure small local self-intersections
- function cureLocalIntersections( start, triangles, dim ) {
- let p = start;
- do {
- const a = p.prev,
- b = p.next.next;
- if ( ! equals( a, b ) && intersects( a, p, p.next, b ) && locallyInside( a, b ) && locallyInside( b, a ) ) {
- triangles.push( a.i / dim );
- triangles.push( p.i / dim );
- triangles.push( b.i / dim );
- // remove two nodes involved
- removeNode( p );
- removeNode( p.next );
- p = start = b;
- }
- p = p.next;
- } while ( p !== start );
- return filterPoints( p );
- }
- // try splitting polygon into two and triangulate them independently
- function splitEarcut( start, triangles, dim, minX, minY, invSize ) {
- // look for a valid diagonal that divides the polygon into two
- let a = start;
- do {
- let b = a.next.next;
- while ( b !== a.prev ) {
- if ( a.i !== b.i && isValidDiagonal( a, b ) ) {
- // split the polygon in two by the diagonal
- let c = splitPolygon( a, b );
- // filter colinear points around the cuts
- a = filterPoints( a, a.next );
- c = filterPoints( c, c.next );
- // run earcut on each half
- earcutLinked( a, triangles, dim, minX, minY, invSize );
- earcutLinked( c, triangles, dim, minX, minY, invSize );
- return;
- }
- b = b.next;
- }
- a = a.next;
- } while ( a !== start );
- }
- // link every hole into the outer loop, producing a single-ring polygon without holes
- function eliminateHoles( data, holeIndices, outerNode, dim ) {
- const queue = [];
- let i, len, start, end, list;
- for ( i = 0, len = holeIndices.length; i < len; i ++ ) {
- start = holeIndices[ i ] * dim;
- end = i < len - 1 ? holeIndices[ i + 1 ] * dim : data.length;
- list = linkedList( data, start, end, dim, false );
- if ( list === list.next ) list.steiner = true;
- queue.push( getLeftmost( list ) );
- }
- queue.sort( compareX );
- // process holes from left to right
- for ( i = 0; i < queue.length; i ++ ) {
- eliminateHole( queue[ i ], outerNode );
- outerNode = filterPoints( outerNode, outerNode.next );
- }
- return outerNode;
- }
- function compareX( a, b ) {
- return a.x - b.x;
- }
- // find a bridge between vertices that connects hole with an outer ring and and link it
- function eliminateHole( hole, outerNode ) {
- outerNode = findHoleBridge( hole, outerNode );
- if ( outerNode ) {
- const b = splitPolygon( outerNode, hole );
- // filter collinear points around the cuts
- filterPoints( outerNode, outerNode.next );
- filterPoints( b, b.next );
- }
- }
- // David Eberly's algorithm for finding a bridge between hole and outer polygon
- function findHoleBridge( hole, outerNode ) {
- let p = outerNode;
- const hx = hole.x;
- const hy = hole.y;
- let qx = - Infinity, m;
- // find a segment intersected by a ray from the hole's leftmost point to the left;
- // segment's endpoint with lesser x will be potential connection point
- do {
- if ( hy <= p.y && hy >= p.next.y && p.next.y !== p.y ) {
- const x = p.x + ( hy - p.y ) * ( p.next.x - p.x ) / ( p.next.y - p.y );
- if ( x <= hx && x > qx ) {
- qx = x;
- if ( x === hx ) {
- if ( hy === p.y ) return p;
- if ( hy === p.next.y ) return p.next;
- }
- m = p.x < p.next.x ? p : p.next;
- }
- }
- p = p.next;
- } while ( p !== outerNode );
- if ( ! m ) return null;
- if ( hx === qx ) return m; // hole touches outer segment; pick leftmost endpoint
- // look for points inside the triangle of hole point, segment intersection and endpoint;
- // if there are no points found, we have a valid connection;
- // otherwise choose the point of the minimum angle with the ray as connection point
- const stop = m,
- mx = m.x,
- my = m.y;
- let tanMin = Infinity, tan;
- p = m;
- do {
- if ( hx >= p.x && p.x >= mx && hx !== p.x &&
- pointInTriangle( hy < my ? hx : qx, hy, mx, my, hy < my ? qx : hx, hy, p.x, p.y ) ) {
- tan = Math.abs( hy - p.y ) / ( hx - p.x ); // tangential
- if ( locallyInside( p, hole ) && ( tan < tanMin || ( tan === tanMin && ( p.x > m.x || ( p.x === m.x && sectorContainsSector( m, p ) ) ) ) ) ) {
- m = p;
- tanMin = tan;
- }
- }
- p = p.next;
- } while ( p !== stop );
- return m;
- }
- // whether sector in vertex m contains sector in vertex p in the same coordinates
- function sectorContainsSector( m, p ) {
- return area( m.prev, m, p.prev ) < 0 && area( p.next, m, m.next ) < 0;
- }
- // interlink polygon nodes in z-order
- function indexCurve( start, minX, minY, invSize ) {
- let p = start;
- do {
- if ( p.z === null ) p.z = zOrder( p.x, p.y, minX, minY, invSize );
- p.prevZ = p.prev;
- p.nextZ = p.next;
- p = p.next;
- } while ( p !== start );
- p.prevZ.nextZ = null;
- p.prevZ = null;
- sortLinked( p );
- }
- // Simon Tatham's linked list merge sort algorithm
- // http://www.chiark.greenend.org.uk/~sgtatham/algorithms/listsort.html
- function sortLinked( list ) {
- let i, p, q, e, tail, numMerges, pSize, qSize,
- inSize = 1;
- do {
- p = list;
- list = null;
- tail = null;
- numMerges = 0;
- while ( p ) {
- numMerges ++;
- q = p;
- pSize = 0;
- for ( i = 0; i < inSize; i ++ ) {
- pSize ++;
- q = q.nextZ;
- if ( ! q ) break;
- }
- qSize = inSize;
- while ( pSize > 0 || ( qSize > 0 && q ) ) {
- if ( pSize !== 0 && ( qSize === 0 || ! q || p.z <= q.z ) ) {
- e = p;
- p = p.nextZ;
- pSize --;
- } else {
- e = q;
- q = q.nextZ;
- qSize --;
- }
- if ( tail ) tail.nextZ = e;
- else list = e;
- e.prevZ = tail;
- tail = e;
- }
- p = q;
- }
- tail.nextZ = null;
- inSize *= 2;
- } while ( numMerges > 1 );
- return list;
- }
- // z-order of a point given coords and inverse of the longer side of data bbox
- function zOrder( x, y, minX, minY, invSize ) {
- // coords are transformed into non-negative 15-bit integer range
- x = 32767 * ( x - minX ) * invSize;
- y = 32767 * ( y - minY ) * invSize;
- x = ( x | ( x << 8 ) ) & 0x00FF00FF;
- x = ( x | ( x << 4 ) ) & 0x0F0F0F0F;
- x = ( x | ( x << 2 ) ) & 0x33333333;
- x = ( x | ( x << 1 ) ) & 0x55555555;
- y = ( y | ( y << 8 ) ) & 0x00FF00FF;
- y = ( y | ( y << 4 ) ) & 0x0F0F0F0F;
- y = ( y | ( y << 2 ) ) & 0x33333333;
- y = ( y | ( y << 1 ) ) & 0x55555555;
- return x | ( y << 1 );
- }
- // find the leftmost node of a polygon ring
- function getLeftmost( start ) {
- let p = start,
- leftmost = start;
- do {
- if ( p.x < leftmost.x || ( p.x === leftmost.x && p.y < leftmost.y ) ) leftmost = p;
- p = p.next;
- } while ( p !== start );
- return leftmost;
- }
- // check if a point lies within a convex triangle
- function pointInTriangle( ax, ay, bx, by, cx, cy, px, py ) {
- return ( cx - px ) * ( ay - py ) - ( ax - px ) * ( cy - py ) >= 0 &&
- ( ax - px ) * ( by - py ) - ( bx - px ) * ( ay - py ) >= 0 &&
- ( bx - px ) * ( cy - py ) - ( cx - px ) * ( by - py ) >= 0;
- }
- // check if a diagonal between two polygon nodes is valid (lies in polygon interior)
- function isValidDiagonal( a, b ) {
- return a.next.i !== b.i && a.prev.i !== b.i && ! intersectsPolygon( a, b ) && // dones't intersect other edges
- ( locallyInside( a, b ) && locallyInside( b, a ) && middleInside( a, b ) && // locally visible
- ( area( a.prev, a, b.prev ) || area( a, b.prev, b ) ) || // does not create opposite-facing sectors
- equals( a, b ) && area( a.prev, a, a.next ) > 0 && area( b.prev, b, b.next ) > 0 ); // special zero-length case
- }
- // signed area of a triangle
- function area( p, q, r ) {
- return ( q.y - p.y ) * ( r.x - q.x ) - ( q.x - p.x ) * ( r.y - q.y );
- }
- // check if two points are equal
- function equals( p1, p2 ) {
- return p1.x === p2.x && p1.y === p2.y;
- }
- // check if two segments intersect
- function intersects( p1, q1, p2, q2 ) {
- const o1 = sign( area( p1, q1, p2 ) );
- const o2 = sign( area( p1, q1, q2 ) );
- const o3 = sign( area( p2, q2, p1 ) );
- const o4 = sign( area( p2, q2, q1 ) );
- if ( o1 !== o2 && o3 !== o4 ) return true; // general case
- if ( o1 === 0 && onSegment( p1, p2, q1 ) ) return true; // p1, q1 and p2 are collinear and p2 lies on p1q1
- if ( o2 === 0 && onSegment( p1, q2, q1 ) ) return true; // p1, q1 and q2 are collinear and q2 lies on p1q1
- if ( o3 === 0 && onSegment( p2, p1, q2 ) ) return true; // p2, q2 and p1 are collinear and p1 lies on p2q2
- if ( o4 === 0 && onSegment( p2, q1, q2 ) ) return true; // p2, q2 and q1 are collinear and q1 lies on p2q2
- return false;
- }
- // for collinear points p, q, r, check if point q lies on segment pr
- function onSegment( p, q, r ) {
- return q.x <= Math.max( p.x, r.x ) && q.x >= Math.min( p.x, r.x ) && q.y <= Math.max( p.y, r.y ) && q.y >= Math.min( p.y, r.y );
- }
- function sign( num ) {
- return num > 0 ? 1 : num < 0 ? - 1 : 0;
- }
- // check if a polygon diagonal intersects any polygon segments
- function intersectsPolygon( a, b ) {
- let p = a;
- do {
- if ( p.i !== a.i && p.next.i !== a.i && p.i !== b.i && p.next.i !== b.i &&
- intersects( p, p.next, a, b ) ) return true;
- p = p.next;
- } while ( p !== a );
- return false;
- }
- // check if a polygon diagonal is locally inside the polygon
- function locallyInside( a, b ) {
- return area( a.prev, a, a.next ) < 0 ?
- area( a, b, a.next ) >= 0 && area( a, a.prev, b ) >= 0 :
- area( a, b, a.prev ) < 0 || area( a, a.next, b ) < 0;
- }
- // check if the middle point of a polygon diagonal is inside the polygon
- function middleInside( a, b ) {
- let p = a,
- inside = false;
- const px = ( a.x + b.x ) / 2,
- py = ( a.y + b.y ) / 2;
- do {
- if ( ( ( p.y > py ) !== ( p.next.y > py ) ) && p.next.y !== p.y &&
- ( px < ( p.next.x - p.x ) * ( py - p.y ) / ( p.next.y - p.y ) + p.x ) )
- inside = ! inside;
- p = p.next;
- } while ( p !== a );
- return inside;
- }
- // link two polygon vertices with a bridge; if the vertices belong to the same ring, it splits polygon into two;
- // if one belongs to the outer ring and another to a hole, it merges it into a single ring
- function splitPolygon( a, b ) {
- const a2 = new Node( a.i, a.x, a.y ),
- b2 = new Node( b.i, b.x, b.y ),
- an = a.next,
- bp = b.prev;
- a.next = b;
- b.prev = a;
- a2.next = an;
- an.prev = a2;
- b2.next = a2;
- a2.prev = b2;
- bp.next = b2;
- b2.prev = bp;
- return b2;
- }
- // create a node and optionally link it with previous one (in a circular doubly linked list)
- function insertNode( i, x, y, last ) {
- const p = new Node( i, x, y );
- if ( ! last ) {
- p.prev = p;
- p.next = p;
- } else {
- p.next = last.next;
- p.prev = last;
- last.next.prev = p;
- last.next = p;
- }
- return p;
- }
- function removeNode( p ) {
- p.next.prev = p.prev;
- p.prev.next = p.next;
- if ( p.prevZ ) p.prevZ.nextZ = p.nextZ;
- if ( p.nextZ ) p.nextZ.prevZ = p.prevZ;
- }
- function Node( i, x, y ) {
- // vertex index in coordinates array
- this.i = i;
- // vertex coordinates
- this.x = x;
- this.y = y;
- // previous and next vertex nodes in a polygon ring
- this.prev = null;
- this.next = null;
- // z-order curve value
- this.z = null;
- // previous and next nodes in z-order
- this.prevZ = null;
- this.nextZ = null;
- // indicates whether this is a steiner point
- this.steiner = false;
- }
- function signedArea( data, start, end, dim ) {
- let sum = 0;
- for ( let i = start, j = end - dim; i < end; i += dim ) {
- sum += ( data[ j ] - data[ i ] ) * ( data[ i + 1 ] + data[ j + 1 ] );
- j = i;
- }
- return sum;
- }
- const ShapeUtils = {
- // calculate area of the contour polygon
- area: function ( contour ) {
- const n = contour.length;
- let a = 0.0;
- for ( let p = n - 1, q = 0; q < n; p = q ++ ) {
- a += contour[ p ].x * contour[ q ].y - contour[ q ].x * contour[ p ].y;
- }
- return a * 0.5;
- },
- isClockWise: function ( pts ) {
- return ShapeUtils.area( pts ) < 0;
- },
- triangulateShape: function ( contour, holes ) {
- const vertices = []; // flat array of vertices like [ x0,y0, x1,y1, x2,y2, ... ]
- const holeIndices = []; // array of hole indices
- const faces = []; // final array of vertex indices like [ [ a,b,d ], [ b,c,d ] ]
- removeDupEndPts( contour );
- addContour( vertices, contour );
- //
- let holeIndex = contour.length;
- holes.forEach( removeDupEndPts );
- for ( let i = 0; i < holes.length; i ++ ) {
- holeIndices.push( holeIndex );
- holeIndex += holes[ i ].length;
- addContour( vertices, holes[ i ] );
- }
- //
- const triangles = Earcut.triangulate( vertices, holeIndices );
- //
- for ( let i = 0; i < triangles.length; i += 3 ) {
- faces.push( triangles.slice( i, i + 3 ) );
- }
- return faces;
- }
- };
- function removeDupEndPts( points ) {
- const l = points.length;
- if ( l > 2 && points[ l - 1 ].equals( points[ 0 ] ) ) {
- points.pop();
- }
- }
- function addContour( vertices, contour ) {
- for ( let i = 0; i < contour.length; i ++ ) {
- vertices.push( contour[ i ].x );
- vertices.push( contour[ i ].y );
- }
- }
- /**
- * Creates extruded geometry from a path shape.
- *
- * parameters = {
- *
- * curveSegments: <int>, // number of points on the curves
- * steps: <int>, // number of points for z-side extrusions / used for subdividing segments of extrude spline too
- * depth: <float>, // Depth to extrude the shape
- *
- * bevelEnabled: <bool>, // turn on bevel
- * bevelThickness: <float>, // how deep into the original shape bevel goes
- * bevelSize: <float>, // how far from shape outline (including bevelOffset) is bevel
- * bevelOffset: <float>, // how far from shape outline does bevel start
- * bevelSegments: <int>, // number of bevel layers
- *
- * extrudePath: <THREE.Curve> // curve to extrude shape along
- *
- * UVGenerator: <Object> // object that provides UV generator functions
- *
- * }
- */
- class ExtrudeBufferGeometry extends BufferGeometry {
- constructor( shapes, options ) {
- super();
- this.type = 'ExtrudeBufferGeometry';
- this.parameters = {
- shapes: shapes,
- options: options
- };
- shapes = Array.isArray( shapes ) ? shapes : [ shapes ];
- const scope = this;
- const verticesArray = [];
- const uvArray = [];
- for ( let i = 0, l = shapes.length; i < l; i ++ ) {
- const shape = shapes[ i ];
- addShape( shape );
- }
- // build geometry
- this.setAttribute( 'position', new Float32BufferAttribute( verticesArray, 3 ) );
- this.setAttribute( 'uv', new Float32BufferAttribute( uvArray, 2 ) );
- this.computeVertexNormals();
- // functions
- function addShape( shape ) {
- const placeholder = [];
- // options
- const curveSegments = options.curveSegments !== undefined ? options.curveSegments : 12;
- const steps = options.steps !== undefined ? options.steps : 1;
- let depth = options.depth !== undefined ? options.depth : 100;
- let bevelEnabled = options.bevelEnabled !== undefined ? options.bevelEnabled : true;
- let bevelThickness = options.bevelThickness !== undefined ? options.bevelThickness : 6;
- let bevelSize = options.bevelSize !== undefined ? options.bevelSize : bevelThickness - 2;
- let bevelOffset = options.bevelOffset !== undefined ? options.bevelOffset : 0;
- let bevelSegments = options.bevelSegments !== undefined ? options.bevelSegments : 3;
- const extrudePath = options.extrudePath;
- const uvgen = options.UVGenerator !== undefined ? options.UVGenerator : WorldUVGenerator;
- // deprecated options
- if ( options.amount !== undefined ) {
- console.warn( 'THREE.ExtrudeBufferGeometry: amount has been renamed to depth.' );
- depth = options.amount;
- }
- //
- let extrudePts, extrudeByPath = false;
- let splineTube, binormal, normal, position2;
- if ( extrudePath ) {
- extrudePts = extrudePath.getSpacedPoints( steps );
- extrudeByPath = true;
- bevelEnabled = false; // bevels not supported for path extrusion
- // SETUP TNB variables
- // TODO1 - have a .isClosed in spline?
- splineTube = extrudePath.computeFrenetFrames( steps, false );
- // console.log(splineTube, 'splineTube', splineTube.normals.length, 'steps', steps, 'extrudePts', extrudePts.length);
- binormal = new Vector3();
- normal = new Vector3();
- position2 = new Vector3();
- }
- // Safeguards if bevels are not enabled
- if ( ! bevelEnabled ) {
- bevelSegments = 0;
- bevelThickness = 0;
- bevelSize = 0;
- bevelOffset = 0;
- }
- // Variables initialization
- const shapePoints = shape.extractPoints( curveSegments );
- let vertices = shapePoints.shape;
- const holes = shapePoints.holes;
- const reverse = ! ShapeUtils.isClockWise( vertices );
- if ( reverse ) {
- vertices = vertices.reverse();
- // Maybe we should also check if holes are in the opposite direction, just to be safe ...
- for ( let h = 0, hl = holes.length; h < hl; h ++ ) {
- const ahole = holes[ h ];
- if ( ShapeUtils.isClockWise( ahole ) ) {
- holes[ h ] = ahole.reverse();
- }
- }
- }
- const faces = ShapeUtils.triangulateShape( vertices, holes );
- /* Vertices */
- const contour = vertices; // vertices has all points but contour has only points of circumference
- for ( let h = 0, hl = holes.length; h < hl; h ++ ) {
- const ahole = holes[ h ];
- vertices = vertices.concat( ahole );
- }
- function scalePt2( pt, vec, size ) {
- if ( ! vec ) console.error( 'THREE.ExtrudeGeometry: vec does not exist' );
- return vec.clone().multiplyScalar( size ).add( pt );
- }
- const vlen = vertices.length, flen = faces.length;
- // Find directions for point movement
- function getBevelVec( inPt, inPrev, inNext ) {
- // computes for inPt the corresponding point inPt' on a new contour
- // shifted by 1 unit (length of normalized vector) to the left
- // if we walk along contour clockwise, this new contour is outside the old one
- //
- // inPt' is the intersection of the two lines parallel to the two
- // adjacent edges of inPt at a distance of 1 unit on the left side.
- let v_trans_x, v_trans_y, shrink_by; // resulting translation vector for inPt
- // good reading for geometry algorithms (here: line-line intersection)
- // http://geomalgorithms.com/a05-_intersect-1.html
- const v_prev_x = inPt.x - inPrev.x,
- v_prev_y = inPt.y - inPrev.y;
- const v_next_x = inNext.x - inPt.x,
- v_next_y = inNext.y - inPt.y;
- const v_prev_lensq = ( v_prev_x * v_prev_x + v_prev_y * v_prev_y );
- // check for collinear edges
- const collinear0 = ( v_prev_x * v_next_y - v_prev_y * v_next_x );
- if ( Math.abs( collinear0 ) > Number.EPSILON ) {
- // not collinear
- // length of vectors for normalizing
- const v_prev_len = Math.sqrt( v_prev_lensq );
- const v_next_len = Math.sqrt( v_next_x * v_next_x + v_next_y * v_next_y );
- // shift adjacent points by unit vectors to the left
- const ptPrevShift_x = ( inPrev.x - v_prev_y / v_prev_len );
- const ptPrevShift_y = ( inPrev.y + v_prev_x / v_prev_len );
- const ptNextShift_x = ( inNext.x - v_next_y / v_next_len );
- const ptNextShift_y = ( inNext.y + v_next_x / v_next_len );
- // scaling factor for v_prev to intersection point
- const sf = ( ( ptNextShift_x - ptPrevShift_x ) * v_next_y -
- ( ptNextShift_y - ptPrevShift_y ) * v_next_x ) /
- ( v_prev_x * v_next_y - v_prev_y * v_next_x );
- // vector from inPt to intersection point
- v_trans_x = ( ptPrevShift_x + v_prev_x * sf - inPt.x );
- v_trans_y = ( ptPrevShift_y + v_prev_y * sf - inPt.y );
- // Don't normalize!, otherwise sharp corners become ugly
- // but prevent crazy spikes
- const v_trans_lensq = ( v_trans_x * v_trans_x + v_trans_y * v_trans_y );
- if ( v_trans_lensq <= 2 ) {
- return new Vector2$1( v_trans_x, v_trans_y );
- } else {
- shrink_by = Math.sqrt( v_trans_lensq / 2 );
- }
- } else {
- // handle special case of collinear edges
- let direction_eq = false; // assumes: opposite
- if ( v_prev_x > Number.EPSILON ) {
- if ( v_next_x > Number.EPSILON ) {
- direction_eq = true;
- }
- } else {
- if ( v_prev_x < - Number.EPSILON ) {
- if ( v_next_x < - Number.EPSILON ) {
- direction_eq = true;
- }
- } else {
- if ( Math.sign( v_prev_y ) === Math.sign( v_next_y ) ) {
- direction_eq = true;
- }
- }
- }
- if ( direction_eq ) {
- // console.log("Warning: lines are a straight sequence");
- v_trans_x = - v_prev_y;
- v_trans_y = v_prev_x;
- shrink_by = Math.sqrt( v_prev_lensq );
- } else {
- // console.log("Warning: lines are a straight spike");
- v_trans_x = v_prev_x;
- v_trans_y = v_prev_y;
- shrink_by = Math.sqrt( v_prev_lensq / 2 );
- }
- }
- return new Vector2$1( v_trans_x / shrink_by, v_trans_y / shrink_by );
- }
- const contourMovements = [];
- for ( let i = 0, il = contour.length, j = il - 1, k = i + 1; i < il; i ++, j ++, k ++ ) {
- if ( j === il ) j = 0;
- if ( k === il ) k = 0;
- // (j)---(i)---(k)
- // console.log('i,j,k', i, j , k)
- contourMovements[ i ] = getBevelVec( contour[ i ], contour[ j ], contour[ k ] );
- }
- const holesMovements = [];
- let oneHoleMovements, verticesMovements = contourMovements.concat();
- for ( let h = 0, hl = holes.length; h < hl; h ++ ) {
- const ahole = holes[ h ];
- oneHoleMovements = [];
- for ( let i = 0, il = ahole.length, j = il - 1, k = i + 1; i < il; i ++, j ++, k ++ ) {
- if ( j === il ) j = 0;
- if ( k === il ) k = 0;
- // (j)---(i)---(k)
- oneHoleMovements[ i ] = getBevelVec( ahole[ i ], ahole[ j ], ahole[ k ] );
- }
- holesMovements.push( oneHoleMovements );
- verticesMovements = verticesMovements.concat( oneHoleMovements );
- }
- // Loop bevelSegments, 1 for the front, 1 for the back
- for ( let b = 0; b < bevelSegments; b ++ ) {
- //for ( b = bevelSegments; b > 0; b -- ) {
- const t = b / bevelSegments;
- const z = bevelThickness * Math.cos( t * Math.PI / 2 );
- const bs = bevelSize * Math.sin( t * Math.PI / 2 ) + bevelOffset;
- // contract shape
- for ( let i = 0, il = contour.length; i < il; i ++ ) {
- const vert = scalePt2( contour[ i ], contourMovements[ i ], bs );
- v( vert.x, vert.y, - z );
- }
- // expand holes
- for ( let h = 0, hl = holes.length; h < hl; h ++ ) {
- const ahole = holes[ h ];
- oneHoleMovements = holesMovements[ h ];
- for ( let i = 0, il = ahole.length; i < il; i ++ ) {
- const vert = scalePt2( ahole[ i ], oneHoleMovements[ i ], bs );
- v( vert.x, vert.y, - z );
- }
- }
- }
- const bs = bevelSize + bevelOffset;
- // Back facing vertices
- for ( let i = 0; i < vlen; i ++ ) {
- const vert = bevelEnabled ? scalePt2( vertices[ i ], verticesMovements[ i ], bs ) : vertices[ i ];
- if ( ! extrudeByPath ) {
- v( vert.x, vert.y, 0 );
- } else {
- // v( vert.x, vert.y + extrudePts[ 0 ].y, extrudePts[ 0 ].x );
- normal.copy( splineTube.normals[ 0 ] ).multiplyScalar( vert.x );
- binormal.copy( splineTube.binormals[ 0 ] ).multiplyScalar( vert.y );
- position2.copy( extrudePts[ 0 ] ).add( normal ).add( binormal );
- v( position2.x, position2.y, position2.z );
- }
- }
- // Add stepped vertices...
- // Including front facing vertices
- for ( let s = 1; s <= steps; s ++ ) {
- for ( let i = 0; i < vlen; i ++ ) {
- const vert = bevelEnabled ? scalePt2( vertices[ i ], verticesMovements[ i ], bs ) : vertices[ i ];
- if ( ! extrudeByPath ) {
- v( vert.x, vert.y, depth / steps * s );
- } else {
- // v( vert.x, vert.y + extrudePts[ s - 1 ].y, extrudePts[ s - 1 ].x );
- normal.copy( splineTube.normals[ s ] ).multiplyScalar( vert.x );
- binormal.copy( splineTube.binormals[ s ] ).multiplyScalar( vert.y );
- position2.copy( extrudePts[ s ] ).add( normal ).add( binormal );
- v( position2.x, position2.y, position2.z );
- }
- }
- }
- // Add bevel segments planes
- //for ( b = 1; b <= bevelSegments; b ++ ) {
- for ( let b = bevelSegments - 1; b >= 0; b -- ) {
- const t = b / bevelSegments;
- const z = bevelThickness * Math.cos( t * Math.PI / 2 );
- const bs = bevelSize * Math.sin( t * Math.PI / 2 ) + bevelOffset;
- // contract shape
- for ( let i = 0, il = contour.length; i < il; i ++ ) {
- const vert = scalePt2( contour[ i ], contourMovements[ i ], bs );
- v( vert.x, vert.y, depth + z );
- }
- // expand holes
- for ( let h = 0, hl = holes.length; h < hl; h ++ ) {
- const ahole = holes[ h ];
- oneHoleMovements = holesMovements[ h ];
- for ( let i = 0, il = ahole.length; i < il; i ++ ) {
- const vert = scalePt2( ahole[ i ], oneHoleMovements[ i ], bs );
- if ( ! extrudeByPath ) {
- v( vert.x, vert.y, depth + z );
- } else {
- v( vert.x, vert.y + extrudePts[ steps - 1 ].y, extrudePts[ steps - 1 ].x + z );
- }
- }
- }
- }
- /* Faces */
- // Top and bottom faces
- buildLidFaces();
- // Sides faces
- buildSideFaces();
- ///// Internal functions
- function buildLidFaces() {
- const start = verticesArray.length / 3;
- if ( bevelEnabled ) {
- let layer = 0; // steps + 1
- let offset = vlen * layer;
- // Bottom faces
- for ( let i = 0; i < flen; i ++ ) {
- const face = faces[ i ];
- f3( face[ 2 ] + offset, face[ 1 ] + offset, face[ 0 ] + offset );
- }
- layer = steps + bevelSegments * 2;
- offset = vlen * layer;
- // Top faces
- for ( let i = 0; i < flen; i ++ ) {
- const face = faces[ i ];
- f3( face[ 0 ] + offset, face[ 1 ] + offset, face[ 2 ] + offset );
- }
- } else {
- // Bottom faces
- for ( let i = 0; i < flen; i ++ ) {
- const face = faces[ i ];
- f3( face[ 2 ], face[ 1 ], face[ 0 ] );
- }
- // Top faces
- for ( let i = 0; i < flen; i ++ ) {
- const face = faces[ i ];
- f3( face[ 0 ] + vlen * steps, face[ 1 ] + vlen * steps, face[ 2 ] + vlen * steps );
- }
- }
- scope.addGroup( start, verticesArray.length / 3 - start, 0 );
- }
- // Create faces for the z-sides of the shape
- function buildSideFaces() {
- const start = verticesArray.length / 3;
- let layeroffset = 0;
- sidewalls( contour, layeroffset );
- layeroffset += contour.length;
- for ( let h = 0, hl = holes.length; h < hl; h ++ ) {
- const ahole = holes[ h ];
- sidewalls( ahole, layeroffset );
- //, true
- layeroffset += ahole.length;
- }
- scope.addGroup( start, verticesArray.length / 3 - start, 1 );
- }
- function sidewalls( contour, layeroffset ) {
- let i = contour.length;
- while ( -- i >= 0 ) {
- const j = i;
- let k = i - 1;
- if ( k < 0 ) k = contour.length - 1;
- //console.log('b', i,j, i-1, k,vertices.length);
- for ( let s = 0, sl = ( steps + bevelSegments * 2 ); s < sl; s ++ ) {
- const slen1 = vlen * s;
- const slen2 = vlen * ( s + 1 );
- const a = layeroffset + j + slen1,
- b = layeroffset + k + slen1,
- c = layeroffset + k + slen2,
- d = layeroffset + j + slen2;
- f4( a, b, c, d );
- }
- }
- }
- function v( x, y, z ) {
- placeholder.push( x );
- placeholder.push( y );
- placeholder.push( z );
- }
- function f3( a, b, c ) {
- addVertex( a );
- addVertex( b );
- addVertex( c );
- const nextIndex = verticesArray.length / 3;
- const uvs = uvgen.generateTopUV( scope, verticesArray, nextIndex - 3, nextIndex - 2, nextIndex - 1 );
- addUV( uvs[ 0 ] );
- addUV( uvs[ 1 ] );
- addUV( uvs[ 2 ] );
- }
- function f4( a, b, c, d ) {
- addVertex( a );
- addVertex( b );
- addVertex( d );
- addVertex( b );
- addVertex( c );
- addVertex( d );
- const nextIndex = verticesArray.length / 3;
- const uvs = uvgen.generateSideWallUV( scope, verticesArray, nextIndex - 6, nextIndex - 3, nextIndex - 2, nextIndex - 1 );
- addUV( uvs[ 0 ] );
- addUV( uvs[ 1 ] );
- addUV( uvs[ 3 ] );
- addUV( uvs[ 1 ] );
- addUV( uvs[ 2 ] );
- addUV( uvs[ 3 ] );
- }
- function addVertex( index ) {
- verticesArray.push( placeholder[ index * 3 + 0 ] );
- verticesArray.push( placeholder[ index * 3 + 1 ] );
- verticesArray.push( placeholder[ index * 3 + 2 ] );
- }
- function addUV( vector2 ) {
- uvArray.push( vector2.x );
- uvArray.push( vector2.y );
- }
- }
- }
- toJSON() {
- const data = BufferGeometry.prototype.toJSON.call( this );
- const shapes = this.parameters.shapes;
- const options = this.parameters.options;
- return toJSON( shapes, options, data );
- }
- }
- const WorldUVGenerator = {
- generateTopUV: function ( geometry, vertices, indexA, indexB, indexC ) {
- const a_x = vertices[ indexA * 3 ];
- const a_y = vertices[ indexA * 3 + 1 ];
- const b_x = vertices[ indexB * 3 ];
- const b_y = vertices[ indexB * 3 + 1 ];
- const c_x = vertices[ indexC * 3 ];
- const c_y = vertices[ indexC * 3 + 1 ];
- return [
- new Vector2$1( a_x, a_y ),
- new Vector2$1( b_x, b_y ),
- new Vector2$1( c_x, c_y )
- ];
- },
- generateSideWallUV: function ( geometry, vertices, indexA, indexB, indexC, indexD ) {
- const a_x = vertices[ indexA * 3 ];
- const a_y = vertices[ indexA * 3 + 1 ];
- const a_z = vertices[ indexA * 3 + 2 ];
- const b_x = vertices[ indexB * 3 ];
- const b_y = vertices[ indexB * 3 + 1 ];
- const b_z = vertices[ indexB * 3 + 2 ];
- const c_x = vertices[ indexC * 3 ];
- const c_y = vertices[ indexC * 3 + 1 ];
- const c_z = vertices[ indexC * 3 + 2 ];
- const d_x = vertices[ indexD * 3 ];
- const d_y = vertices[ indexD * 3 + 1 ];
- const d_z = vertices[ indexD * 3 + 2 ];
- if ( Math.abs( a_y - b_y ) < 0.01 ) {
- return [
- new Vector2$1( a_x, 1 - a_z ),
- new Vector2$1( b_x, 1 - b_z ),
- new Vector2$1( c_x, 1 - c_z ),
- new Vector2$1( d_x, 1 - d_z )
- ];
- } else {
- return [
- new Vector2$1( a_y, 1 - a_z ),
- new Vector2$1( b_y, 1 - b_z ),
- new Vector2$1( c_y, 1 - c_z ),
- new Vector2$1( d_y, 1 - d_z )
- ];
- }
- }
- };
- function toJSON( shapes, options, data ) {
- data.shapes = [];
- if ( Array.isArray( shapes ) ) {
- for ( let i = 0, l = shapes.length; i < l; i ++ ) {
- const shape = shapes[ i ];
- data.shapes.push( shape.uuid );
- }
- } else {
- data.shapes.push( shapes.uuid );
- }
- if ( options.extrudePath !== undefined ) data.options.extrudePath = options.extrudePath.toJSON();
- return data;
- }
- /**
- * Creates extruded geometry from a path shape.
- *
- * parameters = {
- *
- * curveSegments: <int>, // number of points on the curves
- * steps: <int>, // number of points for z-side extrusions / used for subdividing segments of extrude spline too
- * depth: <float>, // Depth to extrude the shape
- *
- * bevelEnabled: <bool>, // turn on bevel
- * bevelThickness: <float>, // how deep into the original shape bevel goes
- * bevelSize: <float>, // how far from shape outline (including bevelOffset) is bevel
- * bevelOffset: <float>, // how far from shape outline does bevel start
- * bevelSegments: <int>, // number of bevel layers
- *
- * extrudePath: <THREE.Curve> // curve to extrude shape along
- *
- * UVGenerator: <Object> // object that provides UV generator functions
- *
- * }
- */
- class ExtrudeGeometry extends Geometry {
- constructor( shapes, options ) {
- super();
- this.type = 'ExtrudeGeometry';
- this.parameters = {
- shapes: shapes,
- options: options
- };
- this.fromBufferGeometry( new ExtrudeBufferGeometry( shapes, options ) );
- this.mergeVertices();
- }
- toJSON() {
- const data = super.toJSON();
- const shapes = this.parameters.shapes;
- const options = this.parameters.options;
- return toJSON$1( shapes, options, data );
- }
- }
- function toJSON$1( shapes, options, data ) {
- data.shapes = [];
- if ( Array.isArray( shapes ) ) {
- for ( let i = 0, l = shapes.length; i < l; i ++ ) {
- const shape = shapes[ i ];
- data.shapes.push( shape.uuid );
- }
- } else {
- data.shapes.push( shapes.uuid );
- }
- if ( options.extrudePath !== undefined ) data.options.extrudePath = options.extrudePath.toJSON();
- return data;
- }
- class IcosahedronBufferGeometry extends PolyhedronBufferGeometry {
- constructor( radius = 1, detail = 0 ) {
- const t = ( 1 + Math.sqrt( 5 ) ) / 2;
- const vertices = [
- - 1, t, 0, 1, t, 0, - 1, - t, 0, 1, - t, 0,
- 0, - 1, t, 0, 1, t, 0, - 1, - t, 0, 1, - t,
- t, 0, - 1, t, 0, 1, - t, 0, - 1, - t, 0, 1
- ];
- const indices = [
- 0, 11, 5, 0, 5, 1, 0, 1, 7, 0, 7, 10, 0, 10, 11,
- 1, 5, 9, 5, 11, 4, 11, 10, 2, 10, 7, 6, 7, 1, 8,
- 3, 9, 4, 3, 4, 2, 3, 2, 6, 3, 6, 8, 3, 8, 9,
- 4, 9, 5, 2, 4, 11, 6, 2, 10, 8, 6, 7, 9, 8, 1
- ];
- super( vertices, indices, radius, detail );
- this.type = 'IcosahedronBufferGeometry';
- this.parameters = {
- radius: radius,
- detail: detail
- };
- }
- }
- class IcosahedronGeometry extends Geometry {
- constructor( radius, detail ) {
- super();
- this.type = 'IcosahedronGeometry';
- this.parameters = {
- radius: radius,
- detail: detail
- };
- this.fromBufferGeometry( new IcosahedronBufferGeometry( radius, detail ) );
- this.mergeVertices();
- }
- }
- class LatheBufferGeometry extends BufferGeometry {
- constructor( points, segments = 12, phiStart = 0, phiLength = Math.PI * 2 ) {
- super();
- this.type = 'LatheBufferGeometry';
- this.parameters = {
- points: points,
- segments: segments,
- phiStart: phiStart,
- phiLength: phiLength
- };
- segments = Math.floor( segments );
- // clamp phiLength so it's in range of [ 0, 2PI ]
- phiLength = MathUtils.clamp( phiLength, 0, Math.PI * 2 );
- // buffers
- const indices = [];
- const vertices = [];
- const uvs = [];
- // helper variables
- const inverseSegments = 1.0 / segments;
- const vertex = new Vector3();
- const uv = new Vector2$1();
- // generate vertices and uvs
- for ( let i = 0; i <= segments; i ++ ) {
- const phi = phiStart + i * inverseSegments * phiLength;
- const sin = Math.sin( phi );
- const cos = Math.cos( phi );
- for ( let j = 0; j <= ( points.length - 1 ); j ++ ) {
- // vertex
- vertex.x = points[ j ].x * sin;
- vertex.y = points[ j ].y;
- vertex.z = points[ j ].x * cos;
- vertices.push( vertex.x, vertex.y, vertex.z );
- // uv
- uv.x = i / segments;
- uv.y = j / ( points.length - 1 );
- uvs.push( uv.x, uv.y );
- }
- }
- // indices
- for ( let i = 0; i < segments; i ++ ) {
- for ( let j = 0; j < ( points.length - 1 ); j ++ ) {
- const base = j + i * points.length;
- const a = base;
- const b = base + points.length;
- const c = base + points.length + 1;
- const d = base + 1;
- // faces
- indices.push( a, b, d );
- indices.push( b, c, d );
- }
- }
- // build geometry
- this.setIndex( indices );
- this.setAttribute( 'position', new Float32BufferAttribute( vertices, 3 ) );
- this.setAttribute( 'uv', new Float32BufferAttribute( uvs, 2 ) );
- // generate normals
- this.computeVertexNormals();
- // if the geometry is closed, we need to average the normals along the seam.
- // because the corresponding vertices are identical (but still have different UVs).
- if ( phiLength === Math.PI * 2 ) {
- const normals = this.attributes.normal.array;
- const n1 = new Vector3();
- const n2 = new Vector3();
- const n = new Vector3();
- // this is the buffer offset for the last line of vertices
- const base = segments * points.length * 3;
- for ( let i = 0, j = 0; i < points.length; i ++, j += 3 ) {
- // select the normal of the vertex in the first line
- n1.x = normals[ j + 0 ];
- n1.y = normals[ j + 1 ];
- n1.z = normals[ j + 2 ];
- // select the normal of the vertex in the last line
- n2.x = normals[ base + j + 0 ];
- n2.y = normals[ base + j + 1 ];
- n2.z = normals[ base + j + 2 ];
- // average normals
- n.addVectors( n1, n2 ).normalize();
- // assign the new values to both normals
- normals[ j + 0 ] = normals[ base + j + 0 ] = n.x;
- normals[ j + 1 ] = normals[ base + j + 1 ] = n.y;
- normals[ j + 2 ] = normals[ base + j + 2 ] = n.z;
- }
- }
- }
- }
- class LatheGeometry extends Geometry {
- constructor( points, segments, phiStart, phiLength ) {
- super();
- this.type = 'LatheGeometry';
- this.parameters = {
- points: points,
- segments: segments,
- phiStart: phiStart,
- phiLength: phiLength
- };
- this.fromBufferGeometry( new LatheBufferGeometry( points, segments, phiStart, phiLength ) );
- this.mergeVertices();
- }
- }
- class OctahedronBufferGeometry extends PolyhedronBufferGeometry {
- constructor( radius = 1, detail = 0 ) {
- const vertices = [
- 1, 0, 0, - 1, 0, 0, 0, 1, 0,
- 0, - 1, 0, 0, 0, 1, 0, 0, - 1
- ];
- const indices = [
- 0, 2, 4, 0, 4, 3, 0, 3, 5,
- 0, 5, 2, 1, 2, 5, 1, 5, 3,
- 1, 3, 4, 1, 4, 2
- ];
- super( vertices, indices, radius, detail );
- this.type = 'OctahedronBufferGeometry';
- this.parameters = {
- radius: radius,
- detail: detail
- };
- }
- }
- class OctahedronGeometry extends Geometry {
- constructor( radius, detail ) {
- super();
- this.type = 'OctahedronGeometry';
- this.parameters = {
- radius: radius,
- detail: detail
- };
- this.fromBufferGeometry( new OctahedronBufferGeometry( radius, detail ) );
- this.mergeVertices();
- }
- }
- /**
- * Parametric Surfaces Geometry
- * based on the brilliant article by @prideout https://prideout.net/blog/old/blog/index.html@p=44.html
- */
- function ParametricBufferGeometry( func, slices, stacks ) {
- BufferGeometry.call( this );
- this.type = 'ParametricBufferGeometry';
- this.parameters = {
- func: func,
- slices: slices,
- stacks: stacks
- };
- // buffers
- const indices = [];
- const vertices = [];
- const normals = [];
- const uvs = [];
- const EPS = 0.00001;
- const normal = new Vector3();
- const p0 = new Vector3(), p1 = new Vector3();
- const pu = new Vector3(), pv = new Vector3();
- if ( func.length < 3 ) {
- console.error( 'THREE.ParametricGeometry: Function must now modify a Vector3 as third parameter.' );
- }
- // generate vertices, normals and uvs
- const sliceCount = slices + 1;
- for ( let i = 0; i <= stacks; i ++ ) {
- const v = i / stacks;
- for ( let j = 0; j <= slices; j ++ ) {
- const u = j / slices;
- // vertex
- func( u, v, p0 );
- vertices.push( p0.x, p0.y, p0.z );
- // normal
- // approximate tangent vectors via finite differences
- if ( u - EPS >= 0 ) {
- func( u - EPS, v, p1 );
- pu.subVectors( p0, p1 );
- } else {
- func( u + EPS, v, p1 );
- pu.subVectors( p1, p0 );
- }
- if ( v - EPS >= 0 ) {
- func( u, v - EPS, p1 );
- pv.subVectors( p0, p1 );
- } else {
- func( u, v + EPS, p1 );
- pv.subVectors( p1, p0 );
- }
- // cross product of tangent vectors returns surface normal
- normal.crossVectors( pu, pv ).normalize();
- normals.push( normal.x, normal.y, normal.z );
- // uv
- uvs.push( u, v );
- }
- }
- // generate indices
- for ( let i = 0; i < stacks; i ++ ) {
- for ( let j = 0; j < slices; j ++ ) {
- const a = i * sliceCount + j;
- const b = i * sliceCount + j + 1;
- const c = ( i + 1 ) * sliceCount + j + 1;
- const d = ( i + 1 ) * sliceCount + j;
- // faces one and two
- indices.push( a, b, d );
- indices.push( b, c, d );
- }
- }
- // build geometry
- this.setIndex( indices );
- this.setAttribute( 'position', new Float32BufferAttribute( vertices, 3 ) );
- this.setAttribute( 'normal', new Float32BufferAttribute( normals, 3 ) );
- this.setAttribute( 'uv', new Float32BufferAttribute( uvs, 2 ) );
- }
- ParametricBufferGeometry.prototype = Object.create( BufferGeometry.prototype );
- ParametricBufferGeometry.prototype.constructor = ParametricBufferGeometry;
- /**
- * Parametric Surfaces Geometry
- * based on the brilliant article by @prideout https://prideout.net/blog/old/blog/index.html@p=44.html
- */
- function ParametricGeometry( func, slices, stacks ) {
- Geometry.call( this );
- this.type = 'ParametricGeometry';
- this.parameters = {
- func: func,
- slices: slices,
- stacks: stacks
- };
- this.fromBufferGeometry( new ParametricBufferGeometry( func, slices, stacks ) );
- this.mergeVertices();
- }
- ParametricGeometry.prototype = Object.create( Geometry.prototype );
- ParametricGeometry.prototype.constructor = ParametricGeometry;
- class PlaneGeometry extends Geometry {
- constructor( width, height, widthSegments, heightSegments ) {
- super();
- this.type = 'PlaneGeometry';
- this.parameters = {
- width: width,
- height: height,
- widthSegments: widthSegments,
- heightSegments: heightSegments
- };
- this.fromBufferGeometry( new PlaneBufferGeometry( width, height, widthSegments, heightSegments ) );
- this.mergeVertices();
- }
- }
- class PolyhedronGeometry extends Geometry {
- constructor( vertices, indices, radius, detail ) {
- super();
- this.type = 'PolyhedronGeometry';
- this.parameters = {
- vertices: vertices,
- indices: indices,
- radius: radius,
- detail: detail
- };
- this.fromBufferGeometry( new PolyhedronBufferGeometry( vertices, indices, radius, detail ) );
- this.mergeVertices();
- }
- }
- class RingBufferGeometry extends BufferGeometry {
- constructor( innerRadius = 0.5, outerRadius = 1, thetaSegments = 8, phiSegments = 1, thetaStart = 0, thetaLength = Math.PI * 2 ) {
- super();
- this.type = 'RingBufferGeometry';
- this.parameters = {
- innerRadius: innerRadius,
- outerRadius: outerRadius,
- thetaSegments: thetaSegments,
- phiSegments: phiSegments,
- thetaStart: thetaStart,
- thetaLength: thetaLength
- };
- thetaSegments = Math.max( 3, thetaSegments );
- phiSegments = Math.max( 1, phiSegments );
- // buffers
- const indices = [];
- const vertices = [];
- const normals = [];
- const uvs = [];
- // some helper variables
- let radius = innerRadius;
- const radiusStep = ( ( outerRadius - innerRadius ) / phiSegments );
- const vertex = new Vector3();
- const uv = new Vector2$1();
- // generate vertices, normals and uvs
- for ( let j = 0; j <= phiSegments; j ++ ) {
- for ( let i = 0; i <= thetaSegments; i ++ ) {
- // values are generate from the inside of the ring to the outside
- const segment = thetaStart + i / thetaSegments * thetaLength;
- // vertex
- vertex.x = radius * Math.cos( segment );
- vertex.y = radius * Math.sin( segment );
- vertices.push( vertex.x, vertex.y, vertex.z );
- // normal
- normals.push( 0, 0, 1 );
- // uv
- uv.x = ( vertex.x / outerRadius + 1 ) / 2;
- uv.y = ( vertex.y / outerRadius + 1 ) / 2;
- uvs.push( uv.x, uv.y );
- }
- // increase the radius for next row of vertices
- radius += radiusStep;
- }
- // indices
- for ( let j = 0; j < phiSegments; j ++ ) {
- const thetaSegmentLevel = j * ( thetaSegments + 1 );
- for ( let i = 0; i < thetaSegments; i ++ ) {
- const segment = i + thetaSegmentLevel;
- const a = segment;
- const b = segment + thetaSegments + 1;
- const c = segment + thetaSegments + 2;
- const d = segment + 1;
- // faces
- indices.push( a, b, d );
- indices.push( b, c, d );
- }
- }
- // build geometry
- this.setIndex( indices );
- this.setAttribute( 'position', new Float32BufferAttribute( vertices, 3 ) );
- this.setAttribute( 'normal', new Float32BufferAttribute( normals, 3 ) );
- this.setAttribute( 'uv', new Float32BufferAttribute( uvs, 2 ) );
- }
- }
- class RingGeometry extends Geometry {
- constructor( innerRadius, outerRadius, thetaSegments, phiSegments, thetaStart, thetaLength ) {
- super();
- this.type = 'RingGeometry';
- this.parameters = {
- innerRadius: innerRadius,
- outerRadius: outerRadius,
- thetaSegments: thetaSegments,
- phiSegments: phiSegments,
- thetaStart: thetaStart,
- thetaLength: thetaLength
- };
- this.fromBufferGeometry( new RingBufferGeometry( innerRadius, outerRadius, thetaSegments, phiSegments, thetaStart, thetaLength ) );
- this.mergeVertices();
- }
- }
- class ShapeBufferGeometry extends BufferGeometry {
- constructor( shapes, curveSegments = 12 ) {
- super();
- this.type = 'ShapeBufferGeometry';
- this.parameters = {
- shapes: shapes,
- curveSegments: curveSegments
- };
- // buffers
- const indices = [];
- const vertices = [];
- const normals = [];
- const uvs = [];
- // helper variables
- let groupStart = 0;
- let groupCount = 0;
- // allow single and array values for "shapes" parameter
- if ( Array.isArray( shapes ) === false ) {
- addShape( shapes );
- } else {
- for ( let i = 0; i < shapes.length; i ++ ) {
- addShape( shapes[ i ] );
- this.addGroup( groupStart, groupCount, i ); // enables MultiMaterial support
- groupStart += groupCount;
- groupCount = 0;
- }
- }
- // build geometry
- this.setIndex( indices );
- this.setAttribute( 'position', new Float32BufferAttribute( vertices, 3 ) );
- this.setAttribute( 'normal', new Float32BufferAttribute( normals, 3 ) );
- this.setAttribute( 'uv', new Float32BufferAttribute( uvs, 2 ) );
- // helper functions
- function addShape( shape ) {
- const indexOffset = vertices.length / 3;
- const points = shape.extractPoints( curveSegments );
- let shapeVertices = points.shape;
- const shapeHoles = points.holes;
- // check direction of vertices
- if ( ShapeUtils.isClockWise( shapeVertices ) === false ) {
- shapeVertices = shapeVertices.reverse();
- }
- for ( let i = 0, l = shapeHoles.length; i < l; i ++ ) {
- const shapeHole = shapeHoles[ i ];
- if ( ShapeUtils.isClockWise( shapeHole ) === true ) {
- shapeHoles[ i ] = shapeHole.reverse();
- }
- }
- const faces = ShapeUtils.triangulateShape( shapeVertices, shapeHoles );
- // join vertices of inner and outer paths to a single array
- for ( let i = 0, l = shapeHoles.length; i < l; i ++ ) {
- const shapeHole = shapeHoles[ i ];
- shapeVertices = shapeVertices.concat( shapeHole );
- }
- // vertices, normals, uvs
- for ( let i = 0, l = shapeVertices.length; i < l; i ++ ) {
- const vertex = shapeVertices[ i ];
- vertices.push( vertex.x, vertex.y, 0 );
- normals.push( 0, 0, 1 );
- uvs.push( vertex.x, vertex.y ); // world uvs
- }
- // incides
- for ( let i = 0, l = faces.length; i < l; i ++ ) {
- const face = faces[ i ];
- const a = face[ 0 ] + indexOffset;
- const b = face[ 1 ] + indexOffset;
- const c = face[ 2 ] + indexOffset;
- indices.push( a, b, c );
- groupCount += 3;
- }
- }
- }
- toJSON() {
- const data = BufferGeometry.prototype.toJSON.call( this );
- const shapes = this.parameters.shapes;
- return toJSON$2( shapes, data );
- }
- }
- function toJSON$2( shapes, data ) {
- data.shapes = [];
- if ( Array.isArray( shapes ) ) {
- for ( let i = 0, l = shapes.length; i < l; i ++ ) {
- const shape = shapes[ i ];
- data.shapes.push( shape.uuid );
- }
- } else {
- data.shapes.push( shapes.uuid );
- }
- return data;
- }
- class ShapeGeometry extends Geometry {
- constructor( shapes, curveSegments ) {
- super();
- this.type = 'ShapeGeometry';
- if ( typeof curveSegments === 'object' ) {
- console.warn( 'THREE.ShapeGeometry: Options parameter has been removed.' );
- curveSegments = curveSegments.curveSegments;
- }
- this.parameters = {
- shapes: shapes,
- curveSegments: curveSegments
- };
- this.fromBufferGeometry( new ShapeBufferGeometry( shapes, curveSegments ) );
- this.mergeVertices();
- }
- toJSON() {
- const data = Geometry.prototype.toJSON.call( this );
- const shapes = this.parameters.shapes;
- return toJSON$3( shapes, data );
- }
- }
- function toJSON$3( shapes, data ) {
- data.shapes = [];
- if ( Array.isArray( shapes ) ) {
- for ( let i = 0, l = shapes.length; i < l; i ++ ) {
- const shape = shapes[ i ];
- data.shapes.push( shape.uuid );
- }
- } else {
- data.shapes.push( shapes.uuid );
- }
- return data;
- }
- class SphereBufferGeometry extends BufferGeometry {
- constructor( radius = 1, widthSegments = 8, heightSegments = 6, phiStart = 0, phiLength = Math.PI * 2, thetaStart = 0, thetaLength = Math.PI ) {
- super();
- this.type = 'SphereBufferGeometry';
- this.parameters = {
- radius: radius,
- widthSegments: widthSegments,
- heightSegments: heightSegments,
- phiStart: phiStart,
- phiLength: phiLength,
- thetaStart: thetaStart,
- thetaLength: thetaLength
- };
- widthSegments = Math.max( 3, Math.floor( widthSegments ) );
- heightSegments = Math.max( 2, Math.floor( heightSegments ) );
- const thetaEnd = Math.min( thetaStart + thetaLength, Math.PI );
- let index = 0;
- const grid = [];
- const vertex = new Vector3();
- const normal = new Vector3();
- // buffers
- const indices = [];
- const vertices = [];
- const normals = [];
- const uvs = [];
- // generate vertices, normals and uvs
- for ( let iy = 0; iy <= heightSegments; iy ++ ) {
- const verticesRow = [];
- const v = iy / heightSegments;
- // special case for the poles
- let uOffset = 0;
- if ( iy == 0 && thetaStart == 0 ) {
- uOffset = 0.5 / widthSegments;
- } else if ( iy == heightSegments && thetaEnd == Math.PI ) {
- uOffset = - 0.5 / widthSegments;
- }
- for ( let ix = 0; ix <= widthSegments; ix ++ ) {
- const u = ix / widthSegments;
- // vertex
- vertex.x = - radius * Math.cos( phiStart + u * phiLength ) * Math.sin( thetaStart + v * thetaLength );
- vertex.y = radius * Math.cos( thetaStart + v * thetaLength );
- vertex.z = radius * Math.sin( phiStart + u * phiLength ) * Math.sin( thetaStart + v * thetaLength );
- vertices.push( vertex.x, vertex.y, vertex.z );
- // normal
- normal.copy( vertex ).normalize();
- normals.push( normal.x, normal.y, normal.z );
- // uv
- uvs.push( u + uOffset, 1 - v );
- verticesRow.push( index ++ );
- }
- grid.push( verticesRow );
- }
- // indices
- for ( let iy = 0; iy < heightSegments; iy ++ ) {
- for ( let ix = 0; ix < widthSegments; ix ++ ) {
- const a = grid[ iy ][ ix + 1 ];
- const b = grid[ iy ][ ix ];
- const c = grid[ iy + 1 ][ ix ];
- const d = grid[ iy + 1 ][ ix + 1 ];
- if ( iy !== 0 || thetaStart > 0 ) indices.push( a, b, d );
- if ( iy !== heightSegments - 1 || thetaEnd < Math.PI ) indices.push( b, c, d );
- }
- }
- // build geometry
- this.setIndex( indices );
- this.setAttribute( 'position', new Float32BufferAttribute( vertices, 3 ) );
- this.setAttribute( 'normal', new Float32BufferAttribute( normals, 3 ) );
- this.setAttribute( 'uv', new Float32BufferAttribute( uvs, 2 ) );
- }
- }
- class SphereGeometry extends Geometry {
- constructor( radius, widthSegments, heightSegments, phiStart, phiLength, thetaStart, thetaLength ) {
- super();
- this.type = 'SphereGeometry';
- this.parameters = {
- radius: radius,
- widthSegments: widthSegments,
- heightSegments: heightSegments,
- phiStart: phiStart,
- phiLength: phiLength,
- thetaStart: thetaStart,
- thetaLength: thetaLength
- };
- this.fromBufferGeometry( new SphereBufferGeometry( radius, widthSegments, heightSegments, phiStart, phiLength, thetaStart, thetaLength ) );
- this.mergeVertices();
- }
- }
- class TetrahedronBufferGeometry extends PolyhedronBufferGeometry {
- constructor( radius = 1, detail = 0 ) {
- const vertices = [
- 1, 1, 1, - 1, - 1, 1, - 1, 1, - 1, 1, - 1, - 1
- ];
- const indices = [
- 2, 1, 0, 0, 3, 2, 1, 3, 0, 2, 3, 1
- ];
- super( vertices, indices, radius, detail );
- this.type = 'TetrahedronBufferGeometry';
- this.parameters = {
- radius: radius,
- detail: detail
- };
- }
- }
- class TetrahedronGeometry extends Geometry {
- constructor( radius, detail ) {
- super();
- this.type = 'TetrahedronGeometry';
- this.parameters = {
- radius: radius,
- detail: detail
- };
- this.fromBufferGeometry( new TetrahedronBufferGeometry( radius, detail ) );
- this.mergeVertices();
- }
- }
- /**
- * Text = 3D Text
- *
- * parameters = {
- * font: <THREE.Font>, // font
- *
- * size: <float>, // size of the text
- * height: <float>, // thickness to extrude text
- * curveSegments: <int>, // number of points on the curves
- *
- * bevelEnabled: <bool>, // turn on bevel
- * bevelThickness: <float>, // how deep into text bevel goes
- * bevelSize: <float>, // how far from text outline (including bevelOffset) is bevel
- * bevelOffset: <float> // how far from text outline does bevel start
- * }
- */
- class TextBufferGeometry extends ExtrudeBufferGeometry {
- constructor( text, parameters = {} ) {
- const font = parameters.font;
- if ( ! ( font && font.isFont ) ) {
- console.error( 'THREE.TextGeometry: font parameter is not an instance of THREE.Font.' );
- return new BufferGeometry();
- }
- const shapes = font.generateShapes( text, parameters.size );
- // translate parameters to ExtrudeGeometry API
- parameters.depth = parameters.height !== undefined ? parameters.height : 50;
- // defaults
- if ( parameters.bevelThickness === undefined ) parameters.bevelThickness = 10;
- if ( parameters.bevelSize === undefined ) parameters.bevelSize = 8;
- if ( parameters.bevelEnabled === undefined ) parameters.bevelEnabled = false;
- super( shapes, parameters );
- this.type = 'TextBufferGeometry';
- }
- }
- /**
- * Text = 3D Text
- *
- * parameters = {
- * font: <THREE.Font>, // font
- *
- * size: <float>, // size of the text
- * height: <float>, // thickness to extrude text
- * curveSegments: <int>, // number of points on the curves
- *
- * bevelEnabled: <bool>, // turn on bevel
- * bevelThickness: <float>, // how deep into text bevel goes
- * bevelSize: <float>, // how far from text outline (including bevelOffset) is bevel
- * bevelOffset: <float> // how far from text outline does bevel start
- * }
- */
- class TextGeometry extends Geometry {
- constructor( text, parameters ) {
- super();
- this.type = 'TextGeometry';
- this.parameters = {
- text: text,
- parameters: parameters
- };
- this.fromBufferGeometry( new TextBufferGeometry( text, parameters ) );
- this.mergeVertices();
- }
- }
- class TorusBufferGeometry extends BufferGeometry {
- constructor( radius = 1, tube = 0.4, radialSegments = 8, tubularSegments = 6, arc = Math.PI * 2 ) {
- super();
- this.type = 'TorusBufferGeometry';
- this.parameters = {
- radius: radius,
- tube: tube,
- radialSegments: radialSegments,
- tubularSegments: tubularSegments,
- arc: arc
- };
- radialSegments = Math.floor( radialSegments );
- tubularSegments = Math.floor( tubularSegments );
- // buffers
- const indices = [];
- const vertices = [];
- const normals = [];
- const uvs = [];
- // helper variables
- const center = new Vector3();
- const vertex = new Vector3();
- const normal = new Vector3();
- // generate vertices, normals and uvs
- for ( let j = 0; j <= radialSegments; j ++ ) {
- for ( let i = 0; i <= tubularSegments; i ++ ) {
- const u = i / tubularSegments * arc;
- const v = j / radialSegments * Math.PI * 2;
- // vertex
- vertex.x = ( radius + tube * Math.cos( v ) ) * Math.cos( u );
- vertex.y = ( radius + tube * Math.cos( v ) ) * Math.sin( u );
- vertex.z = tube * Math.sin( v );
- vertices.push( vertex.x, vertex.y, vertex.z );
- // normal
- center.x = radius * Math.cos( u );
- center.y = radius * Math.sin( u );
- normal.subVectors( vertex, center ).normalize();
- normals.push( normal.x, normal.y, normal.z );
- // uv
- uvs.push( i / tubularSegments );
- uvs.push( j / radialSegments );
- }
- }
- // generate indices
- for ( let j = 1; j <= radialSegments; j ++ ) {
- for ( let i = 1; i <= tubularSegments; i ++ ) {
- // indices
- const a = ( tubularSegments + 1 ) * j + i - 1;
- const b = ( tubularSegments + 1 ) * ( j - 1 ) + i - 1;
- const c = ( tubularSegments + 1 ) * ( j - 1 ) + i;
- const d = ( tubularSegments + 1 ) * j + i;
- // faces
- indices.push( a, b, d );
- indices.push( b, c, d );
- }
- }
- // build geometry
- this.setIndex( indices );
- this.setAttribute( 'position', new Float32BufferAttribute( vertices, 3 ) );
- this.setAttribute( 'normal', new Float32BufferAttribute( normals, 3 ) );
- this.setAttribute( 'uv', new Float32BufferAttribute( uvs, 2 ) );
- }
- }
- class TorusGeometry extends Geometry {
- constructor( radius, tube, radialSegments, tubularSegments, arc ) {
- super();
- this.type = 'TorusGeometry';
- this.parameters = {
- radius: radius,
- tube: tube,
- radialSegments: radialSegments,
- tubularSegments: tubularSegments,
- arc: arc
- };
- this.fromBufferGeometry( new TorusBufferGeometry( radius, tube, radialSegments, tubularSegments, arc ) );
- this.mergeVertices();
- }
- }
- class TorusKnotBufferGeometry extends BufferGeometry {
- constructor( radius = 1, tube = 0.4, tubularSegments = 64, radialSegments = 8, p = 2, q = 3 ) {
- super();
- this.type = 'TorusKnotBufferGeometry';
- this.parameters = {
- radius: radius,
- tube: tube,
- tubularSegments: tubularSegments,
- radialSegments: radialSegments,
- p: p,
- q: q
- };
- tubularSegments = Math.floor( tubularSegments );
- radialSegments = Math.floor( radialSegments );
- // buffers
- const indices = [];
- const vertices = [];
- const normals = [];
- const uvs = [];
- // helper variables
- const vertex = new Vector3();
- const normal = new Vector3();
- const P1 = new Vector3();
- const P2 = new Vector3();
- const B = new Vector3();
- const T = new Vector3();
- const N = new Vector3();
- // generate vertices, normals and uvs
- for ( let i = 0; i <= tubularSegments; ++ i ) {
- // the radian "u" is used to calculate the position on the torus curve of the current tubular segement
- const u = i / tubularSegments * p * Math.PI * 2;
- // now we calculate two points. P1 is our current position on the curve, P2 is a little farther ahead.
- // these points are used to create a special "coordinate space", which is necessary to calculate the correct vertex positions
- calculatePositionOnCurve( u, p, q, radius, P1 );
- calculatePositionOnCurve( u + 0.01, p, q, radius, P2 );
- // calculate orthonormal basis
- T.subVectors( P2, P1 );
- N.addVectors( P2, P1 );
- B.crossVectors( T, N );
- N.crossVectors( B, T );
- // normalize B, N. T can be ignored, we don't use it
- B.normalize();
- N.normalize();
- for ( let j = 0; j <= radialSegments; ++ j ) {
- // now calculate the vertices. they are nothing more than an extrusion of the torus curve.
- // because we extrude a shape in the xy-plane, there is no need to calculate a z-value.
- const v = j / radialSegments * Math.PI * 2;
- const cx = - tube * Math.cos( v );
- const cy = tube * Math.sin( v );
- // now calculate the final vertex position.
- // first we orient the extrusion with our basis vectos, then we add it to the current position on the curve
- vertex.x = P1.x + ( cx * N.x + cy * B.x );
- vertex.y = P1.y + ( cx * N.y + cy * B.y );
- vertex.z = P1.z + ( cx * N.z + cy * B.z );
- vertices.push( vertex.x, vertex.y, vertex.z );
- // normal (P1 is always the center/origin of the extrusion, thus we can use it to calculate the normal)
- normal.subVectors( vertex, P1 ).normalize();
- normals.push( normal.x, normal.y, normal.z );
- // uv
- uvs.push( i / tubularSegments );
- uvs.push( j / radialSegments );
- }
- }
- // generate indices
- for ( let j = 1; j <= tubularSegments; j ++ ) {
- for ( let i = 1; i <= radialSegments; i ++ ) {
- // indices
- const a = ( radialSegments + 1 ) * ( j - 1 ) + ( i - 1 );
- const b = ( radialSegments + 1 ) * j + ( i - 1 );
- const c = ( radialSegments + 1 ) * j + i;
- const d = ( radialSegments + 1 ) * ( j - 1 ) + i;
- // faces
- indices.push( a, b, d );
- indices.push( b, c, d );
- }
- }
- // build geometry
- this.setIndex( indices );
- this.setAttribute( 'position', new Float32BufferAttribute( vertices, 3 ) );
- this.setAttribute( 'normal', new Float32BufferAttribute( normals, 3 ) );
- this.setAttribute( 'uv', new Float32BufferAttribute( uvs, 2 ) );
- // this function calculates the current position on the torus curve
- function calculatePositionOnCurve( u, p, q, radius, position ) {
- const cu = Math.cos( u );
- const su = Math.sin( u );
- const quOverP = q / p * u;
- const cs = Math.cos( quOverP );
- position.x = radius * ( 2 + cs ) * 0.5 * cu;
- position.y = radius * ( 2 + cs ) * su * 0.5;
- position.z = radius * Math.sin( quOverP ) * 0.5;
- }
- }
- }
- class TorusKnotGeometry extends Geometry {
- constructor( radius, tube, tubularSegments, radialSegments, p, q, heightScale ) {
- super();
- this.type = 'TorusKnotGeometry';
- this.parameters = {
- radius: radius,
- tube: tube,
- tubularSegments: tubularSegments,
- radialSegments: radialSegments,
- p: p,
- q: q
- };
- if ( heightScale !== undefined ) console.warn( 'THREE.TorusKnotGeometry: heightScale has been deprecated. Use .scale( x, y, z ) instead.' );
- this.fromBufferGeometry( new TorusKnotBufferGeometry( radius, tube, tubularSegments, radialSegments, p, q ) );
- this.mergeVertices();
- }
- }
- class TubeBufferGeometry extends BufferGeometry {
- constructor( path, tubularSegments = 64, radius = 1, radialSegments = 8, closed = false ) {
- super();
- this.type = 'TubeBufferGeometry';
- this.parameters = {
- path: path,
- tubularSegments: tubularSegments,
- radius: radius,
- radialSegments: radialSegments,
- closed: closed
- };
- const frames = path.computeFrenetFrames( tubularSegments, closed );
- // expose internals
- this.tangents = frames.tangents;
- this.normals = frames.normals;
- this.binormals = frames.binormals;
- // helper variables
- const vertex = new Vector3();
- const normal = new Vector3();
- const uv = new Vector2$1();
- let P = new Vector3();
- // buffer
- const vertices = [];
- const normals = [];
- const uvs = [];
- const indices = [];
- // create buffer data
- generateBufferData();
- // build geometry
- this.setIndex( indices );
- this.setAttribute( 'position', new Float32BufferAttribute( vertices, 3 ) );
- this.setAttribute( 'normal', new Float32BufferAttribute( normals, 3 ) );
- this.setAttribute( 'uv', new Float32BufferAttribute( uvs, 2 ) );
- // functions
- function generateBufferData() {
- for ( let i = 0; i < tubularSegments; i ++ ) {
- generateSegment( i );
- }
- // if the geometry is not closed, generate the last row of vertices and normals
- // at the regular position on the given path
- //
- // if the geometry is closed, duplicate the first row of vertices and normals (uvs will differ)
- generateSegment( ( closed === false ) ? tubularSegments : 0 );
- // uvs are generated in a separate function.
- // this makes it easy compute correct values for closed geometries
- generateUVs();
- // finally create faces
- generateIndices();
- }
- function generateSegment( i ) {
- // we use getPointAt to sample evenly distributed points from the given path
- P = path.getPointAt( i / tubularSegments, P );
- // retrieve corresponding normal and binormal
- const N = frames.normals[ i ];
- const B = frames.binormals[ i ];
- // generate normals and vertices for the current segment
- for ( let j = 0; j <= radialSegments; j ++ ) {
- const v = j / radialSegments * Math.PI * 2;
- const sin = Math.sin( v );
- const cos = - Math.cos( v );
- // normal
- normal.x = ( cos * N.x + sin * B.x );
- normal.y = ( cos * N.y + sin * B.y );
- normal.z = ( cos * N.z + sin * B.z );
- normal.normalize();
- normals.push( normal.x, normal.y, normal.z );
- // vertex
- vertex.x = P.x + radius * normal.x;
- vertex.y = P.y + radius * normal.y;
- vertex.z = P.z + radius * normal.z;
- vertices.push( vertex.x, vertex.y, vertex.z );
- }
- }
- function generateIndices() {
- for ( let j = 1; j <= tubularSegments; j ++ ) {
- for ( let i = 1; i <= radialSegments; i ++ ) {
- const a = ( radialSegments + 1 ) * ( j - 1 ) + ( i - 1 );
- const b = ( radialSegments + 1 ) * j + ( i - 1 );
- const c = ( radialSegments + 1 ) * j + i;
- const d = ( radialSegments + 1 ) * ( j - 1 ) + i;
- // faces
- indices.push( a, b, d );
- indices.push( b, c, d );
- }
- }
- }
- function generateUVs() {
- for ( let i = 0; i <= tubularSegments; i ++ ) {
- for ( let j = 0; j <= radialSegments; j ++ ) {
- uv.x = i / tubularSegments;
- uv.y = j / radialSegments;
- uvs.push( uv.x, uv.y );
- }
- }
- }
- }
- toJSON() {
- const data = BufferGeometry.prototype.toJSON.call( this );
- data.path = this.parameters.path.toJSON();
- return data;
- }
- }
- class TubeGeometry extends Geometry {
- constructor( path, tubularSegments, radius, radialSegments, closed, taper ) {
- super();
- this.type = 'TubeGeometry';
- this.parameters = {
- path: path,
- tubularSegments: tubularSegments,
- radius: radius,
- radialSegments: radialSegments,
- closed: closed
- };
- if ( taper !== undefined ) console.warn( 'THREE.TubeGeometry: taper has been removed.' );
- const bufferGeometry = new TubeBufferGeometry( path, tubularSegments, radius, radialSegments, closed );
- // expose internals
- this.tangents = bufferGeometry.tangents;
- this.normals = bufferGeometry.normals;
- this.binormals = bufferGeometry.binormals;
- // create geometry
- this.fromBufferGeometry( bufferGeometry );
- this.mergeVertices();
- }
- }
- class WireframeGeometry extends BufferGeometry {
- constructor( geometry ) {
- super();
- this.type = 'WireframeGeometry';
- // buffer
- const vertices = [];
- // helper variables
- const edge = [ 0, 0 ], edges = {};
- const keys = [ 'a', 'b', 'c' ];
- // different logic for Geometry and BufferGeometry
- if ( geometry && geometry.isGeometry ) {
- // create a data structure that contains all edges without duplicates
- const faces = geometry.faces;
- for ( let i = 0, l = faces.length; i < l; i ++ ) {
- const face = faces[ i ];
- for ( let j = 0; j < 3; j ++ ) {
- const edge1 = face[ keys[ j ] ];
- const edge2 = face[ keys[ ( j + 1 ) % 3 ] ];
- edge[ 0 ] = Math.min( edge1, edge2 ); // sorting prevents duplicates
- edge[ 1 ] = Math.max( edge1, edge2 );
- const key = edge[ 0 ] + ',' + edge[ 1 ];
- if ( edges[ key ] === undefined ) {
- edges[ key ] = { index1: edge[ 0 ], index2: edge[ 1 ] };
- }
- }
- }
- // generate vertices
- for ( const key in edges ) {
- const e = edges[ key ];
- let vertex = geometry.vertices[ e.index1 ];
- vertices.push( vertex.x, vertex.y, vertex.z );
- vertex = geometry.vertices[ e.index2 ];
- vertices.push( vertex.x, vertex.y, vertex.z );
- }
- } else if ( geometry && geometry.isBufferGeometry ) {
- const vertex = new Vector3();
- if ( geometry.index !== null ) {
- // indexed BufferGeometry
- const position = geometry.attributes.position;
- const indices = geometry.index;
- let groups = geometry.groups;
- if ( groups.length === 0 ) {
- groups = [ { start: 0, count: indices.count, materialIndex: 0 } ];
- }
- // create a data structure that contains all eges without duplicates
- for ( let o = 0, ol = groups.length; o < ol; ++ o ) {
- const group = groups[ o ];
- const start = group.start;
- const count = group.count;
- for ( let i = start, l = ( start + count ); i < l; i += 3 ) {
- for ( let j = 0; j < 3; j ++ ) {
- const edge1 = indices.getX( i + j );
- const edge2 = indices.getX( i + ( j + 1 ) % 3 );
- edge[ 0 ] = Math.min( edge1, edge2 ); // sorting prevents duplicates
- edge[ 1 ] = Math.max( edge1, edge2 );
- const key = edge[ 0 ] + ',' + edge[ 1 ];
- if ( edges[ key ] === undefined ) {
- edges[ key ] = { index1: edge[ 0 ], index2: edge[ 1 ] };
- }
- }
- }
- }
- // generate vertices
- for ( const key in edges ) {
- const e = edges[ key ];
- vertex.fromBufferAttribute( position, e.index1 );
- vertices.push( vertex.x, vertex.y, vertex.z );
- vertex.fromBufferAttribute( position, e.index2 );
- vertices.push( vertex.x, vertex.y, vertex.z );
- }
- } else {
- // non-indexed BufferGeometry
- const position = geometry.attributes.position;
- for ( let i = 0, l = ( position.count / 3 ); i < l; i ++ ) {
- for ( let j = 0; j < 3; j ++ ) {
- // three edges per triangle, an edge is represented as (index1, index2)
- // e.g. the first triangle has the following edges: (0,1),(1,2),(2,0)
- const index1 = 3 * i + j;
- vertex.fromBufferAttribute( position, index1 );
- vertices.push( vertex.x, vertex.y, vertex.z );
- const index2 = 3 * i + ( ( j + 1 ) % 3 );
- vertex.fromBufferAttribute( position, index2 );
- vertices.push( vertex.x, vertex.y, vertex.z );
- }
- }
- }
- }
- // build geometry
- this.setAttribute( 'position', new Float32BufferAttribute( vertices, 3 ) );
- }
- }
- var Geometries = /*#__PURE__*/Object.freeze({
- __proto__: null,
- BoxGeometry: BoxGeometry,
- BoxBufferGeometry: BoxBufferGeometry,
- CircleGeometry: CircleGeometry,
- CircleBufferGeometry: CircleBufferGeometry,
- ConeGeometry: ConeGeometry,
- ConeBufferGeometry: ConeBufferGeometry,
- CylinderGeometry: CylinderGeometry,
- CylinderBufferGeometry: CylinderBufferGeometry,
- DodecahedronGeometry: DodecahedronGeometry,
- DodecahedronBufferGeometry: DodecahedronBufferGeometry,
- EdgesGeometry: EdgesGeometry,
- ExtrudeGeometry: ExtrudeGeometry,
- ExtrudeBufferGeometry: ExtrudeBufferGeometry,
- IcosahedronGeometry: IcosahedronGeometry,
- IcosahedronBufferGeometry: IcosahedronBufferGeometry,
- LatheGeometry: LatheGeometry,
- LatheBufferGeometry: LatheBufferGeometry,
- OctahedronGeometry: OctahedronGeometry,
- OctahedronBufferGeometry: OctahedronBufferGeometry,
- ParametricGeometry: ParametricGeometry,
- ParametricBufferGeometry: ParametricBufferGeometry,
- PlaneGeometry: PlaneGeometry,
- PlaneBufferGeometry: PlaneBufferGeometry,
- PolyhedronGeometry: PolyhedronGeometry,
- PolyhedronBufferGeometry: PolyhedronBufferGeometry,
- RingGeometry: RingGeometry,
- RingBufferGeometry: RingBufferGeometry,
- ShapeGeometry: ShapeGeometry,
- ShapeBufferGeometry: ShapeBufferGeometry,
- SphereGeometry: SphereGeometry,
- SphereBufferGeometry: SphereBufferGeometry,
- TetrahedronGeometry: TetrahedronGeometry,
- TetrahedronBufferGeometry: TetrahedronBufferGeometry,
- TextGeometry: TextGeometry,
- TextBufferGeometry: TextBufferGeometry,
- TorusGeometry: TorusGeometry,
- TorusBufferGeometry: TorusBufferGeometry,
- TorusKnotGeometry: TorusKnotGeometry,
- TorusKnotBufferGeometry: TorusKnotBufferGeometry,
- TubeGeometry: TubeGeometry,
- TubeBufferGeometry: TubeBufferGeometry,
- WireframeGeometry: WireframeGeometry
- });
- /**
- * parameters = {
- * color: <THREE.Color>
- * }
- */
- function ShadowMaterial( parameters ) {
- Material.call( this );
- this.type = 'ShadowMaterial';
- this.color = new Color( 0x000000 );
- this.transparent = true;
- this.setValues( parameters );
- }
- ShadowMaterial.prototype = Object.create( Material.prototype );
- ShadowMaterial.prototype.constructor = ShadowMaterial;
- ShadowMaterial.prototype.isShadowMaterial = true;
- ShadowMaterial.prototype.copy = function ( source ) {
- Material.prototype.copy.call( this, source );
- this.color.copy( source.color );
- return this;
- };
- function RawShaderMaterial( parameters ) {
- ShaderMaterial.call( this, parameters );
- this.type = 'RawShaderMaterial';
- }
- RawShaderMaterial.prototype = Object.create( ShaderMaterial.prototype );
- RawShaderMaterial.prototype.constructor = RawShaderMaterial;
- RawShaderMaterial.prototype.isRawShaderMaterial = true;
- /**
- * parameters = {
- * color: <hex>,
- * roughness: <float>,
- * metalness: <float>,
- * opacity: <float>,
- *
- * map: new THREE.Texture( <Image> ),
- *
- * lightMap: new THREE.Texture( <Image> ),
- * lightMapIntensity: <float>
- *
- * aoMap: new THREE.Texture( <Image> ),
- * aoMapIntensity: <float>
- *
- * emissive: <hex>,
- * emissiveIntensity: <float>
- * emissiveMap: new THREE.Texture( <Image> ),
- *
- * bumpMap: new THREE.Texture( <Image> ),
- * bumpScale: <float>,
- *
- * normalMap: new THREE.Texture( <Image> ),
- * normalMapType: THREE.TangentSpaceNormalMap,
- * normalScale: <Vector2>,
- *
- * displacementMap: new THREE.Texture( <Image> ),
- * displacementScale: <float>,
- * displacementBias: <float>,
- *
- * roughnessMap: new THREE.Texture( <Image> ),
- *
- * metalnessMap: new THREE.Texture( <Image> ),
- *
- * alphaMap: new THREE.Texture( <Image> ),
- *
- * envMap: new THREE.CubeTexture( [posx, negx, posy, negy, posz, negz] ),
- * envMapIntensity: <float>
- *
- * refractionRatio: <float>,
- *
- * wireframe: <boolean>,
- * wireframelineWidth: <float>,
- *
- * skinning: <bool>,
- * morphTargets: <bool>,
- * morphNormals: <bool>
- * }
- */
- function MeshStandardMaterial( parameters ) {
- Material.call( this );
- this.defines = { 'STANDARD': '' };
- this.type = 'MeshStandardMaterial';
- this.color = new Color( 0xffffff ); // diffuse
- this.roughness = 1.0;
- this.metalness = 0.0;
- this.map = null;
- this.lightMap = null;
- this.lightMapIntensity = 1.0;
- this.aoMap = null;
- this.aoMapIntensity = 1.0;
- this.emissive = new Color( 0x000000 );
- this.emissiveIntensity = 1.0;
- this.emissiveMap = null;
- this.bumpMap = null;
- this.bumpScale = 1;
- this.normalMap = null;
- this.normalMapType = TangentSpaceNormalMap;
- this.normalScale = new Vector2$1( 1, 1 );
- this.displacementMap = null;
- this.displacementScale = 1;
- this.displacementBias = 0;
- this.roughnessMap = null;
- this.metalnessMap = null;
- this.alphaMap = null;
- this.envMap = null;
- this.envMapIntensity = 1.0;
- this.refractionRatio = 0.98;
- this.wireframe = false;
- this.wireframelineWidth = 1;
- this.wireframeLinecap = 'round';
- this.wireframeLinejoin = 'round';
- this.skinning = false;
- this.morphTargets = false;
- this.morphNormals = false;
- this.vertexTangents = false;
- this.setValues( parameters );
- }
- MeshStandardMaterial.prototype = Object.create( Material.prototype );
- MeshStandardMaterial.prototype.constructor = MeshStandardMaterial;
- MeshStandardMaterial.prototype.isMeshStandardMaterial = true;
- MeshStandardMaterial.prototype.copy = function ( source ) {
- Material.prototype.copy.call( this, source );
- this.defines = { 'STANDARD': '' };
- this.color.copy( source.color );
- this.roughness = source.roughness;
- this.metalness = source.metalness;
- this.map = source.map;
- this.lightMap = source.lightMap;
- this.lightMapIntensity = source.lightMapIntensity;
- this.aoMap = source.aoMap;
- this.aoMapIntensity = source.aoMapIntensity;
- this.emissive.copy( source.emissive );
- this.emissiveMap = source.emissiveMap;
- this.emissiveIntensity = source.emissiveIntensity;
- this.bumpMap = source.bumpMap;
- this.bumpScale = source.bumpScale;
- this.normalMap = source.normalMap;
- this.normalMapType = source.normalMapType;
- this.normalScale.copy( source.normalScale );
- this.displacementMap = source.displacementMap;
- this.displacementScale = source.displacementScale;
- this.displacementBias = source.displacementBias;
- this.roughnessMap = source.roughnessMap;
- this.metalnessMap = source.metalnessMap;
- this.alphaMap = source.alphaMap;
- this.envMap = source.envMap;
- this.envMapIntensity = source.envMapIntensity;
- this.refractionRatio = source.refractionRatio;
- this.wireframe = source.wireframe;
- this.wireframelineWidth = source.wireframelineWidth;
- this.wireframeLinecap = source.wireframeLinecap;
- this.wireframeLinejoin = source.wireframeLinejoin;
- this.skinning = source.skinning;
- this.morphTargets = source.morphTargets;
- this.morphNormals = source.morphNormals;
- this.vertexTangents = source.vertexTangents;
- return this;
- };
- /**
- * parameters = {
- * clearcoat: <float>,
- * clearcoatMap: new THREE.Texture( <Image> ),
- * clearcoatRoughness: <float>,
- * clearcoatRoughnessMap: new THREE.Texture( <Image> ),
- * clearcoatNormalScale: <Vector2>,
- * clearcoatNormalMap: new THREE.Texture( <Image> ),
- *
- * reflectivity: <float>,
- * ior: <float>,
- *
- * sheen: <Color>,
- *
- * transmission: <float>,
- * transmissionMap: new THREE.Texture( <Image> )
- * }
- */
- function MeshPhysicalMaterial( parameters ) {
- MeshStandardMaterial.call( this );
- this.defines = {
- 'STANDARD': '',
- 'PHYSICAL': ''
- };
- this.type = 'MeshPhysicalMaterial';
- this.clearcoat = 0.0;
- this.clearcoatMap = null;
- this.clearcoatRoughness = 0.0;
- this.clearcoatRoughnessMap = null;
- this.clearcoatNormalScale = new Vector2$1( 1, 1 );
- this.clearcoatNormalMap = null;
- this.reflectivity = 0.5; // maps to F0 = 0.04
- Object.defineProperty( this, 'ior', {
- get: function () {
- return ( 1 + 0.4 * this.reflectivity ) / ( 1 - 0.4 * this.reflectivity );
- },
- set: function ( ior ) {
- this.reflectivity = MathUtils.clamp( 2.5 * ( ior - 1 ) / ( ior + 1 ), 0, 1 );
- }
- } );
- this.sheen = null; // null will disable sheen bsdf
- this.transmission = 0.0;
- this.transmissionMap = null;
- this.setValues( parameters );
- }
- MeshPhysicalMaterial.prototype = Object.create( MeshStandardMaterial.prototype );
- MeshPhysicalMaterial.prototype.constructor = MeshPhysicalMaterial;
- MeshPhysicalMaterial.prototype.isMeshPhysicalMaterial = true;
- MeshPhysicalMaterial.prototype.copy = function ( source ) {
- MeshStandardMaterial.prototype.copy.call( this, source );
- this.defines = {
- 'STANDARD': '',
- 'PHYSICAL': ''
- };
- this.clearcoat = source.clearcoat;
- this.clearcoatMap = source.clearcoatMap;
- this.clearcoatRoughness = source.clearcoatRoughness;
- this.clearcoatRoughnessMap = source.clearcoatRoughnessMap;
- this.clearcoatNormalMap = source.clearcoatNormalMap;
- this.clearcoatNormalScale.copy( source.clearcoatNormalScale );
- this.reflectivity = source.reflectivity;
- if ( source.sheen ) {
- this.sheen = ( this.sheen || new Color() ).copy( source.sheen );
- } else {
- this.sheen = null;
- }
- this.transmission = source.transmission;
- this.transmissionMap = source.transmissionMap;
- return this;
- };
- /**
- * parameters = {
- * color: <hex>,
- * specular: <hex>,
- * shininess: <float>,
- * opacity: <float>,
- *
- * map: new THREE.Texture( <Image> ),
- *
- * lightMap: new THREE.Texture( <Image> ),
- * lightMapIntensity: <float>
- *
- * aoMap: new THREE.Texture( <Image> ),
- * aoMapIntensity: <float>
- *
- * emissive: <hex>,
- * emissiveIntensity: <float>
- * emissiveMap: new THREE.Texture( <Image> ),
- *
- * bumpMap: new THREE.Texture( <Image> ),
- * bumpScale: <float>,
- *
- * normalMap: new THREE.Texture( <Image> ),
- * normalMapType: THREE.TangentSpaceNormalMap,
- * normalScale: <Vector2>,
- *
- * displacementMap: new THREE.Texture( <Image> ),
- * displacementScale: <float>,
- * displacementBias: <float>,
- *
- * specularMap: new THREE.Texture( <Image> ),
- *
- * alphaMap: new THREE.Texture( <Image> ),
- *
- * envMap: new THREE.CubeTexture( [posx, negx, posy, negy, posz, negz] ),
- * combine: THREE.MultiplyOperation,
- * reflectivity: <float>,
- * refractionRatio: <float>,
- *
- * wireframe: <boolean>,
- * wireframelineWidth: <float>,
- *
- * skinning: <bool>,
- * morphTargets: <bool>,
- * morphNormals: <bool>
- * }
- */
- function MeshPhongMaterial( parameters ) {
- Material.call( this );
- this.type = 'MeshPhongMaterial';
- this.color = new Color( 0xffffff ); // diffuse
- this.specular = new Color( 0x111111 );
- this.shininess = 30;
- this.map = null;
- this.lightMap = null;
- this.lightMapIntensity = 1.0;
- this.aoMap = null;
- this.aoMapIntensity = 1.0;
- this.emissive = new Color( 0x000000 );
- this.emissiveIntensity = 1.0;
- this.emissiveMap = null;
- this.bumpMap = null;
- this.bumpScale = 1;
- this.normalMap = null;
- this.normalMapType = TangentSpaceNormalMap;
- this.normalScale = new Vector2$1( 1, 1 );
- this.displacementMap = null;
- this.displacementScale = 1;
- this.displacementBias = 0;
- this.specularMap = null;
- this.alphaMap = null;
- this.envMap = null;
- this.combine = MultiplyOperation;
- this.reflectivity = 1;
- this.refractionRatio = 0.98;
- this.wireframe = false;
- this.wireframelineWidth = 1;
- this.wireframeLinecap = 'round';
- this.wireframeLinejoin = 'round';
- this.skinning = false;
- this.morphTargets = false;
- this.morphNormals = false;
- this.setValues( parameters );
- }
- MeshPhongMaterial.prototype = Object.create( Material.prototype );
- MeshPhongMaterial.prototype.constructor = MeshPhongMaterial;
- MeshPhongMaterial.prototype.isMeshPhongMaterial = true;
- MeshPhongMaterial.prototype.copy = function ( source ) {
- Material.prototype.copy.call( this, source );
- this.color.copy( source.color );
- this.specular.copy( source.specular );
- this.shininess = source.shininess;
- this.map = source.map;
- this.lightMap = source.lightMap;
- this.lightMapIntensity = source.lightMapIntensity;
- this.aoMap = source.aoMap;
- this.aoMapIntensity = source.aoMapIntensity;
- this.emissive.copy( source.emissive );
- this.emissiveMap = source.emissiveMap;
- this.emissiveIntensity = source.emissiveIntensity;
- this.bumpMap = source.bumpMap;
- this.bumpScale = source.bumpScale;
- this.normalMap = source.normalMap;
- this.normalMapType = source.normalMapType;
- this.normalScale.copy( source.normalScale );
- this.displacementMap = source.displacementMap;
- this.displacementScale = source.displacementScale;
- this.displacementBias = source.displacementBias;
- this.specularMap = source.specularMap;
- this.alphaMap = source.alphaMap;
- this.envMap = source.envMap;
- this.combine = source.combine;
- this.reflectivity = source.reflectivity;
- this.refractionRatio = source.refractionRatio;
- this.wireframe = source.wireframe;
- this.wireframelineWidth = source.wireframelineWidth;
- this.wireframeLinecap = source.wireframeLinecap;
- this.wireframeLinejoin = source.wireframeLinejoin;
- this.skinning = source.skinning;
- this.morphTargets = source.morphTargets;
- this.morphNormals = source.morphNormals;
- return this;
- };
- /**
- * parameters = {
- * color: <hex>,
- *
- * map: new THREE.Texture( <Image> ),
- * gradientMap: new THREE.Texture( <Image> ),
- *
- * lightMap: new THREE.Texture( <Image> ),
- * lightMapIntensity: <float>
- *
- * aoMap: new THREE.Texture( <Image> ),
- * aoMapIntensity: <float>
- *
- * emissive: <hex>,
- * emissiveIntensity: <float>
- * emissiveMap: new THREE.Texture( <Image> ),
- *
- * bumpMap: new THREE.Texture( <Image> ),
- * bumpScale: <float>,
- *
- * normalMap: new THREE.Texture( <Image> ),
- * normalMapType: THREE.TangentSpaceNormalMap,
- * normalScale: <Vector2>,
- *
- * displacementMap: new THREE.Texture( <Image> ),
- * displacementScale: <float>,
- * displacementBias: <float>,
- *
- * alphaMap: new THREE.Texture( <Image> ),
- *
- * wireframe: <boolean>,
- * wireframelineWidth: <float>,
- *
- * skinning: <bool>,
- * morphTargets: <bool>,
- * morphNormals: <bool>
- * }
- */
- function MeshToonMaterial( parameters ) {
- Material.call( this );
- this.defines = { 'TOON': '' };
- this.type = 'MeshToonMaterial';
- this.color = new Color( 0xffffff );
- this.map = null;
- this.gradientMap = null;
- this.lightMap = null;
- this.lightMapIntensity = 1.0;
- this.aoMap = null;
- this.aoMapIntensity = 1.0;
- this.emissive = new Color( 0x000000 );
- this.emissiveIntensity = 1.0;
- this.emissiveMap = null;
- this.bumpMap = null;
- this.bumpScale = 1;
- this.normalMap = null;
- this.normalMapType = TangentSpaceNormalMap;
- this.normalScale = new Vector2$1( 1, 1 );
- this.displacementMap = null;
- this.displacementScale = 1;
- this.displacementBias = 0;
- this.alphaMap = null;
- this.wireframe = false;
- this.wireframelineWidth = 1;
- this.wireframeLinecap = 'round';
- this.wireframeLinejoin = 'round';
- this.skinning = false;
- this.morphTargets = false;
- this.morphNormals = false;
- this.setValues( parameters );
- }
- MeshToonMaterial.prototype = Object.create( Material.prototype );
- MeshToonMaterial.prototype.constructor = MeshToonMaterial;
- MeshToonMaterial.prototype.isMeshToonMaterial = true;
- MeshToonMaterial.prototype.copy = function ( source ) {
- Material.prototype.copy.call( this, source );
- this.color.copy( source.color );
- this.map = source.map;
- this.gradientMap = source.gradientMap;
- this.lightMap = source.lightMap;
- this.lightMapIntensity = source.lightMapIntensity;
- this.aoMap = source.aoMap;
- this.aoMapIntensity = source.aoMapIntensity;
- this.emissive.copy( source.emissive );
- this.emissiveMap = source.emissiveMap;
- this.emissiveIntensity = source.emissiveIntensity;
- this.bumpMap = source.bumpMap;
- this.bumpScale = source.bumpScale;
- this.normalMap = source.normalMap;
- this.normalMapType = source.normalMapType;
- this.normalScale.copy( source.normalScale );
- this.displacementMap = source.displacementMap;
- this.displacementScale = source.displacementScale;
- this.displacementBias = source.displacementBias;
- this.alphaMap = source.alphaMap;
- this.wireframe = source.wireframe;
- this.wireframelineWidth = source.wireframelineWidth;
- this.wireframeLinecap = source.wireframeLinecap;
- this.wireframeLinejoin = source.wireframeLinejoin;
- this.skinning = source.skinning;
- this.morphTargets = source.morphTargets;
- this.morphNormals = source.morphNormals;
- return this;
- };
- /**
- * parameters = {
- * opacity: <float>,
- *
- * bumpMap: new THREE.Texture( <Image> ),
- * bumpScale: <float>,
- *
- * normalMap: new THREE.Texture( <Image> ),
- * normalMapType: THREE.TangentSpaceNormalMap,
- * normalScale: <Vector2>,
- *
- * displacementMap: new THREE.Texture( <Image> ),
- * displacementScale: <float>,
- * displacementBias: <float>,
- *
- * wireframe: <boolean>,
- * wireframelineWidth: <float>
- *
- * skinning: <bool>,
- * morphTargets: <bool>,
- * morphNormals: <bool>
- * }
- */
- function MeshNormalMaterial( parameters ) {
- Material.call( this );
- this.type = 'MeshNormalMaterial';
- this.bumpMap = null;
- this.bumpScale = 1;
- this.normalMap = null;
- this.normalMapType = TangentSpaceNormalMap;
- this.normalScale = new Vector2$1( 1, 1 );
- this.displacementMap = null;
- this.displacementScale = 1;
- this.displacementBias = 0;
- this.wireframe = false;
- this.wireframelineWidth = 1;
- this.fog = false;
- this.skinning = false;
- this.morphTargets = false;
- this.morphNormals = false;
- this.setValues( parameters );
- }
- MeshNormalMaterial.prototype = Object.create( Material.prototype );
- MeshNormalMaterial.prototype.constructor = MeshNormalMaterial;
- MeshNormalMaterial.prototype.isMeshNormalMaterial = true;
- MeshNormalMaterial.prototype.copy = function ( source ) {
- Material.prototype.copy.call( this, source );
- this.bumpMap = source.bumpMap;
- this.bumpScale = source.bumpScale;
- this.normalMap = source.normalMap;
- this.normalMapType = source.normalMapType;
- this.normalScale.copy( source.normalScale );
- this.displacementMap = source.displacementMap;
- this.displacementScale = source.displacementScale;
- this.displacementBias = source.displacementBias;
- this.wireframe = source.wireframe;
- this.wireframelineWidth = source.wireframelineWidth;
- this.skinning = source.skinning;
- this.morphTargets = source.morphTargets;
- this.morphNormals = source.morphNormals;
- return this;
- };
- /**
- * parameters = {
- * color: <hex>,
- * opacity: <float>,
- *
- * map: new THREE.Texture( <Image> ),
- *
- * lightMap: new THREE.Texture( <Image> ),
- * lightMapIntensity: <float>
- *
- * aoMap: new THREE.Texture( <Image> ),
- * aoMapIntensity: <float>
- *
- * emissive: <hex>,
- * emissiveIntensity: <float>
- * emissiveMap: new THREE.Texture( <Image> ),
- *
- * specularMap: new THREE.Texture( <Image> ),
- *
- * alphaMap: new THREE.Texture( <Image> ),
- *
- * envMap: new THREE.CubeTexture( [posx, negx, posy, negy, posz, negz] ),
- * combine: THREE.Multiply,
- * reflectivity: <float>,
- * refractionRatio: <float>,
- *
- * wireframe: <boolean>,
- * wireframelineWidth: <float>,
- *
- * skinning: <bool>,
- * morphTargets: <bool>,
- * morphNormals: <bool>
- * }
- */
- function MeshLambertMaterial( parameters ) {
- Material.call( this );
- this.type = 'MeshLambertMaterial';
- this.color = new Color( 0xffffff ); // diffuse
- this.map = null;
- this.lightMap = null;
- this.lightMapIntensity = 1.0;
- this.aoMap = null;
- this.aoMapIntensity = 1.0;
- this.emissive = new Color( 0x000000 );
- this.emissiveIntensity = 1.0;
- this.emissiveMap = null;
- this.specularMap = null;
- this.alphaMap = null;
- this.envMap = null;
- this.combine = MultiplyOperation;
- this.reflectivity = 1;
- this.refractionRatio = 0.98;
- this.wireframe = false;
- this.wireframelineWidth = 1;
- this.wireframeLinecap = 'round';
- this.wireframeLinejoin = 'round';
- this.skinning = false;
- this.morphTargets = false;
- this.morphNormals = false;
- this.setValues( parameters );
- }
- MeshLambertMaterial.prototype = Object.create( Material.prototype );
- MeshLambertMaterial.prototype.constructor = MeshLambertMaterial;
- MeshLambertMaterial.prototype.isMeshLambertMaterial = true;
- MeshLambertMaterial.prototype.copy = function ( source ) {
- Material.prototype.copy.call( this, source );
- this.color.copy( source.color );
- this.map = source.map;
- this.lightMap = source.lightMap;
- this.lightMapIntensity = source.lightMapIntensity;
- this.aoMap = source.aoMap;
- this.aoMapIntensity = source.aoMapIntensity;
- this.emissive.copy( source.emissive );
- this.emissiveMap = source.emissiveMap;
- this.emissiveIntensity = source.emissiveIntensity;
- this.specularMap = source.specularMap;
- this.alphaMap = source.alphaMap;
- this.envMap = source.envMap;
- this.combine = source.combine;
- this.reflectivity = source.reflectivity;
- this.refractionRatio = source.refractionRatio;
- this.wireframe = source.wireframe;
- this.wireframelineWidth = source.wireframelineWidth;
- this.wireframeLinecap = source.wireframeLinecap;
- this.wireframeLinejoin = source.wireframeLinejoin;
- this.skinning = source.skinning;
- this.morphTargets = source.morphTargets;
- this.morphNormals = source.morphNormals;
- return this;
- };
- /**
- * parameters = {
- * color: <hex>,
- * opacity: <float>,
- *
- * matcap: new THREE.Texture( <Image> ),
- *
- * map: new THREE.Texture( <Image> ),
- *
- * bumpMap: new THREE.Texture( <Image> ),
- * bumpScale: <float>,
- *
- * normalMap: new THREE.Texture( <Image> ),
- * normalMapType: THREE.TangentSpaceNormalMap,
- * normalScale: <Vector2>,
- *
- * displacementMap: new THREE.Texture( <Image> ),
- * displacementScale: <float>,
- * displacementBias: <float>,
- *
- * alphaMap: new THREE.Texture( <Image> ),
- *
- * skinning: <bool>,
- * morphTargets: <bool>,
- * morphNormals: <bool>
- * }
- */
- function MeshMatcapMaterial( parameters ) {
- Material.call( this );
- this.defines = { 'MATCAP': '' };
- this.type = 'MeshMatcapMaterial';
- this.color = new Color( 0xffffff ); // diffuse
- this.matcap = null;
- this.map = null;
- this.bumpMap = null;
- this.bumpScale = 1;
- this.normalMap = null;
- this.normalMapType = TangentSpaceNormalMap;
- this.normalScale = new Vector2$1( 1, 1 );
- this.displacementMap = null;
- this.displacementScale = 1;
- this.displacementBias = 0;
- this.alphaMap = null;
- this.skinning = false;
- this.morphTargets = false;
- this.morphNormals = false;
- this.setValues( parameters );
- }
- MeshMatcapMaterial.prototype = Object.create( Material.prototype );
- MeshMatcapMaterial.prototype.constructor = MeshMatcapMaterial;
- MeshMatcapMaterial.prototype.isMeshMatcapMaterial = true;
- MeshMatcapMaterial.prototype.copy = function ( source ) {
- Material.prototype.copy.call( this, source );
- this.defines = { 'MATCAP': '' };
- this.color.copy( source.color );
- this.matcap = source.matcap;
- this.map = source.map;
- this.bumpMap = source.bumpMap;
- this.bumpScale = source.bumpScale;
- this.normalMap = source.normalMap;
- this.normalMapType = source.normalMapType;
- this.normalScale.copy( source.normalScale );
- this.displacementMap = source.displacementMap;
- this.displacementScale = source.displacementScale;
- this.displacementBias = source.displacementBias;
- this.alphaMap = source.alphaMap;
- this.skinning = source.skinning;
- this.morphTargets = source.morphTargets;
- this.morphNormals = source.morphNormals;
- return this;
- };
- /**
- * parameters = {
- * color: <hex>,
- * opacity: <float>,
- *
- * lineWidth: <float>,
- *
- * scale: <float>,
- * dashSize: <float>,
- * gapSize: <float>
- * }
- */
- function LineDashedMaterial( parameters ) {
- LineBasicMaterial.call( this );
- this.type = 'LineDashedMaterial';
- this.scale = 1;
- this.dashSize = 3;
- this.gapSize = 1;
- this.setValues( parameters );
- }
- LineDashedMaterial.prototype = Object.create( LineBasicMaterial.prototype );
- LineDashedMaterial.prototype.constructor = LineDashedMaterial;
- LineDashedMaterial.prototype.isLineDashedMaterial = true;
- LineDashedMaterial.prototype.copy = function ( source ) {
- LineBasicMaterial.prototype.copy.call( this, source );
- this.scale = source.scale;
- this.dashSize = source.dashSize;
- this.gapSize = source.gapSize;
- return this;
- };
- var Materials = /*#__PURE__*/Object.freeze({
- __proto__: null,
- ShadowMaterial: ShadowMaterial,
- SpriteMaterial: SpriteMaterial,
- RawShaderMaterial: RawShaderMaterial,
- ShaderMaterial: ShaderMaterial,
- PointsMaterial: PointsMaterial,
- MeshPhysicalMaterial: MeshPhysicalMaterial,
- MeshStandardMaterial: MeshStandardMaterial,
- MeshPhongMaterial: MeshPhongMaterial,
- MeshToonMaterial: MeshToonMaterial,
- MeshNormalMaterial: MeshNormalMaterial,
- MeshLambertMaterial: MeshLambertMaterial,
- MeshDepthMaterial: MeshDepthMaterial,
- MeshDistanceMaterial: MeshDistanceMaterial,
- MeshBasicMaterial: MeshBasicMaterial,
- MeshMatcapMaterial: MeshMatcapMaterial,
- LineDashedMaterial: LineDashedMaterial,
- LineBasicMaterial: LineBasicMaterial,
- Material: Material
- });
- const AnimationUtils = {
- // same as Array.prototype.slice, but also works on typed arrays
- arraySlice: function ( array, from, to ) {
- if ( AnimationUtils.isTypedArray( array ) ) {
- // in ios9 array.subarray(from, undefined) will return empty array
- // but array.subarray(from) or array.subarray(from, len) is correct
- return new array.constructor( array.subarray( from, to !== undefined ? to : array.length ) );
- }
- return array.slice( from, to );
- },
- // converts an array to a specific type
- convertArray: function ( array, type, forceClone ) {
- if ( ! array || // let 'undefined' and 'null' pass
- ! forceClone && array.constructor === type ) return array;
- if ( typeof type.BYTES_PER_ELEMENT === 'number' ) {
- return new type( array ); // create typed array
- }
- return Array.prototype.slice.call( array ); // create Array
- },
- isTypedArray: function ( object ) {
- return ArrayBuffer.isView( object ) &&
- ! ( object instanceof DataView );
- },
- // returns an array by which times and values can be sorted
- getKeyframeOrder: function ( times ) {
- function compareTime( i, j ) {
- return times[ i ] - times[ j ];
- }
- const n = times.length;
- const result = new Array( n );
- for ( let i = 0; i !== n; ++ i ) result[ i ] = i;
- result.sort( compareTime );
- return result;
- },
- // uses the array previously returned by 'getKeyframeOrder' to sort data
- sortedArray: function ( values, stride, order ) {
- const nValues = values.length;
- const result = new values.constructor( nValues );
- for ( let i = 0, dstOffset = 0; dstOffset !== nValues; ++ i ) {
- const srcOffset = order[ i ] * stride;
- for ( let j = 0; j !== stride; ++ j ) {
- result[ dstOffset ++ ] = values[ srcOffset + j ];
- }
- }
- return result;
- },
- // function for parsing AOS keyframe formats
- flattenJSON: function ( jsonKeys, times, values, valuePropertyName ) {
- let i = 1, key = jsonKeys[ 0 ];
- while ( key !== undefined && key[ valuePropertyName ] === undefined ) {
- key = jsonKeys[ i ++ ];
- }
- if ( key === undefined ) return; // no data
- let value = key[ valuePropertyName ];
- if ( value === undefined ) return; // no data
- if ( Array.isArray( value ) ) {
- do {
- value = key[ valuePropertyName ];
- if ( value !== undefined ) {
- times.push( key.time );
- values.push.apply( values, value ); // push all elements
- }
- key = jsonKeys[ i ++ ];
- } while ( key !== undefined );
- } else if ( value.toArray !== undefined ) {
- // ...assume THREE.Math-ish
- do {
- value = key[ valuePropertyName ];
- if ( value !== undefined ) {
- times.push( key.time );
- value.toArray( values, values.length );
- }
- key = jsonKeys[ i ++ ];
- } while ( key !== undefined );
- } else {
- // otherwise push as-is
- do {
- value = key[ valuePropertyName ];
- if ( value !== undefined ) {
- times.push( key.time );
- values.push( value );
- }
- key = jsonKeys[ i ++ ];
- } while ( key !== undefined );
- }
- },
- subclip: function ( sourceClip, name, startFrame, endFrame, fps = 30 ) {
- const clip = sourceClip.clone();
- clip.name = name;
- const tracks = [];
- for ( let i = 0; i < clip.tracks.length; ++ i ) {
- const track = clip.tracks[ i ];
- const valueSize = track.getValueSize();
- const times = [];
- const values = [];
- for ( let j = 0; j < track.times.length; ++ j ) {
- const frame = track.times[ j ] * fps;
- if ( frame < startFrame || frame >= endFrame ) continue;
- times.push( track.times[ j ] );
- for ( let k = 0; k < valueSize; ++ k ) {
- values.push( track.values[ j * valueSize + k ] );
- }
- }
- if ( times.length === 0 ) continue;
- track.times = AnimationUtils.convertArray( times, track.times.constructor );
- track.values = AnimationUtils.convertArray( values, track.values.constructor );
- tracks.push( track );
- }
- clip.tracks = tracks;
- // find minimum .times value across all tracks in the trimmed clip
- let minStartTime = Infinity;
- for ( let i = 0; i < clip.tracks.length; ++ i ) {
- if ( minStartTime > clip.tracks[ i ].times[ 0 ] ) {
- minStartTime = clip.tracks[ i ].times[ 0 ];
- }
- }
- // shift all tracks such that clip begins at t=0
- for ( let i = 0; i < clip.tracks.length; ++ i ) {
- clip.tracks[ i ].shift( - 1 * minStartTime );
- }
- clip.resetDuration();
- return clip;
- },
- makeClipAdditive: function ( targetClip, referenceFrame = 0, referenceClip = targetClip, fps = 30 ) {
- if ( fps <= 0 ) fps = 30;
- const numTracks = referenceClip.tracks.length;
- const referenceTime = referenceFrame / fps;
- // Make each track's values relative to the values at the reference frame
- for ( let i = 0; i < numTracks; ++ i ) {
- const referenceTrack = referenceClip.tracks[ i ];
- const referenceTrackType = referenceTrack.ValueTypeName;
- // Skip this track if it's non-numeric
- if ( referenceTrackType === 'bool' || referenceTrackType === 'string' ) continue;
- // Find the track in the target clip whose name and type matches the reference track
- const targetTrack = targetClip.tracks.find( function ( track ) {
- return track.name === referenceTrack.name
- && track.ValueTypeName === referenceTrackType;
- } );
- if ( targetTrack === undefined ) continue;
- let referenceOffset = 0;
- const referenceValueSize = referenceTrack.getValueSize();
- if ( referenceTrack.createInterpolant.isInterpolantFactoryMethodGLTFCubicSpline ) {
- referenceOffset = referenceValueSize / 3;
- }
- let targetOffset = 0;
- const targetValueSize = targetTrack.getValueSize();
- if ( targetTrack.createInterpolant.isInterpolantFactoryMethodGLTFCubicSpline ) {
- targetOffset = targetValueSize / 3;
- }
- const lastIndex = referenceTrack.times.length - 1;
- let referenceValue;
- // Find the value to subtract out of the track
- if ( referenceTime <= referenceTrack.times[ 0 ] ) {
- // Reference frame is earlier than the first keyframe, so just use the first keyframe
- const startIndex = referenceOffset;
- const endIndex = referenceValueSize - referenceOffset;
- referenceValue = AnimationUtils.arraySlice( referenceTrack.values, startIndex, endIndex );
- } else if ( referenceTime >= referenceTrack.times[ lastIndex ] ) {
- // Reference frame is after the last keyframe, so just use the last keyframe
- const startIndex = lastIndex * referenceValueSize + referenceOffset;
- const endIndex = startIndex + referenceValueSize - referenceOffset;
- referenceValue = AnimationUtils.arraySlice( referenceTrack.values, startIndex, endIndex );
- } else {
- // Interpolate to the reference value
- const interpolant = referenceTrack.createInterpolant();
- const startIndex = referenceOffset;
- const endIndex = referenceValueSize - referenceOffset;
- interpolant.evaluate( referenceTime );
- referenceValue = AnimationUtils.arraySlice( interpolant.resultBuffer, startIndex, endIndex );
- }
- // Conjugate the quaternion
- if ( referenceTrackType === 'quaternion' ) {
- const referenceQuat = new Quaternion().fromArray( referenceValue ).normalize().conjugate();
- referenceQuat.toArray( referenceValue );
- }
- // Subtract the reference value from all of the track values
- const numTimes = targetTrack.times.length;
- for ( let j = 0; j < numTimes; ++ j ) {
- const valueStart = j * targetValueSize + targetOffset;
- if ( referenceTrackType === 'quaternion' ) {
- // Multiply the conjugate for quaternion track types
- Quaternion.multiplyQuaternionsFlat(
- targetTrack.values,
- valueStart,
- referenceValue,
- 0,
- targetTrack.values,
- valueStart
- );
- } else {
- const valueEnd = targetValueSize - targetOffset * 2;
- // Subtract each value for all other numeric track types
- for ( let k = 0; k < valueEnd; ++ k ) {
- targetTrack.values[ valueStart + k ] -= referenceValue[ k ];
- }
- }
- }
- }
- targetClip.blendMode = AdditiveAnimationBlendMode;
- return targetClip;
- }
- };
- /**
- * Abstract base class of interpolants over parametric samples.
- *
- * The parameter domain is one dimensional, typically the time or a path
- * along a curve defined by the data.
- *
- * The sample values can have any dimensionality and derived classes may
- * apply special interpretations to the data.
- *
- * This class provides the interval seek in a Template Method, deferring
- * the actual interpolation to derived classes.
- *
- * Time complexity is O(1) for linear access crossing at most two points
- * and O(log N) for random access, where N is the number of positions.
- *
- * References:
- *
- * http://www.oodesign.com/template-method-pattern.html
- *
- */
- function Interpolant( parameterPositions, sampleValues, sampleSize, resultBuffer ) {
- this.parameterPositions = parameterPositions;
- this._cachedIndex = 0;
- this.resultBuffer = resultBuffer !== undefined ?
- resultBuffer : new sampleValues.constructor( sampleSize );
- this.sampleValues = sampleValues;
- this.valueSize = sampleSize;
- }
- Object.assign( Interpolant.prototype, {
- evaluate: function ( t ) {
- const pp = this.parameterPositions;
- let i1 = this._cachedIndex,
- t1 = pp[ i1 ],
- t0 = pp[ i1 - 1 ];
- validate_interval: {
- seek: {
- let right;
- linear_scan: {
- //- See http://jsperf.com/comparison-to-undefined/3
- //- slower code:
- //-
- //- if ( t >= t1 || t1 === undefined ) {
- forward_scan: if ( ! ( t < t1 ) ) {
- for ( let giveUpAt = i1 + 2; ; ) {
- if ( t1 === undefined ) {
- if ( t < t0 ) break forward_scan;
- // after end
- i1 = pp.length;
- this._cachedIndex = i1;
- return this.afterEnd_( i1 - 1, t, t0 );
- }
- if ( i1 === giveUpAt ) break; // this loop
- t0 = t1;
- t1 = pp[ ++ i1 ];
- if ( t < t1 ) {
- // we have arrived at the sought interval
- break seek;
- }
- }
- // prepare binary search on the right side of the index
- right = pp.length;
- break linear_scan;
- }
- //- slower code:
- //- if ( t < t0 || t0 === undefined ) {
- if ( ! ( t >= t0 ) ) {
- // looping?
- const t1global = pp[ 1 ];
- if ( t < t1global ) {
- i1 = 2; // + 1, using the scan for the details
- t0 = t1global;
- }
- // linear reverse scan
- for ( let giveUpAt = i1 - 2; ; ) {
- if ( t0 === undefined ) {
- // before start
- this._cachedIndex = 0;
- return this.beforeStart_( 0, t, t1 );
- }
- if ( i1 === giveUpAt ) break; // this loop
- t1 = t0;
- t0 = pp[ -- i1 - 1 ];
- if ( t >= t0 ) {
- // we have arrived at the sought interval
- break seek;
- }
- }
- // prepare binary search on the left side of the index
- right = i1;
- i1 = 0;
- break linear_scan;
- }
- // the interval is valid
- break validate_interval;
- } // linear scan
- // binary search
- while ( i1 < right ) {
- const mid = ( i1 + right ) >>> 1;
- if ( t < pp[ mid ] ) {
- right = mid;
- } else {
- i1 = mid + 1;
- }
- }
- t1 = pp[ i1 ];
- t0 = pp[ i1 - 1 ];
- // check boundary cases, again
- if ( t0 === undefined ) {
- this._cachedIndex = 0;
- return this.beforeStart_( 0, t, t1 );
- }
- if ( t1 === undefined ) {
- i1 = pp.length;
- this._cachedIndex = i1;
- return this.afterEnd_( i1 - 1, t0, t );
- }
- } // seek
- this._cachedIndex = i1;
- this.intervalChanged_( i1, t0, t1 );
- } // validate_interval
- return this.interpolate_( i1, t0, t, t1 );
- },
- settings: null, // optional, subclass-specific settings structure
- // Note: The indirection allows central control of many interpolants.
- // --- Protected interface
- DefaultSettings_: {},
- getSettings_: function () {
- return this.settings || this.DefaultSettings_;
- },
- copySampleValue_: function ( index ) {
- // copies a sample value to the result buffer
- const result = this.resultBuffer,
- values = this.sampleValues,
- stride = this.valueSize,
- offset = index * stride;
- for ( let i = 0; i !== stride; ++ i ) {
- result[ i ] = values[ offset + i ];
- }
- return result;
- },
- // Template methods for derived classes:
- interpolate_: function ( /* i1, t0, t, t1 */ ) {
- throw new Error( 'call to abstract method' );
- // implementations shall return this.resultBuffer
- },
- intervalChanged_: function ( /* i1, t0, t1 */ ) {
- // empty
- }
- } );
- // DECLARE ALIAS AFTER assign prototype
- Object.assign( Interpolant.prototype, {
- //( 0, t, t0 ), returns this.resultBuffer
- beforeStart_: Interpolant.prototype.copySampleValue_,
- //( N-1, tN-1, t ), returns this.resultBuffer
- afterEnd_: Interpolant.prototype.copySampleValue_,
- } );
- /**
- * Fast and simple cubic spline interpolant.
- *
- * It was derived from a Hermitian construction setting the first derivative
- * at each sample position to the linear slope between neighboring positions
- * over their parameter interval.
- */
- function CubicInterpolant( parameterPositions, sampleValues, sampleSize, resultBuffer ) {
- Interpolant.call( this, parameterPositions, sampleValues, sampleSize, resultBuffer );
- this._weightPrev = - 0;
- this._offsetPrev = - 0;
- this._weightNext = - 0;
- this._offsetNext = - 0;
- }
- CubicInterpolant.prototype = Object.assign( Object.create( Interpolant.prototype ), {
- constructor: CubicInterpolant,
- DefaultSettings_: {
- endingStart: ZeroCurvatureEnding,
- endingEnd: ZeroCurvatureEnding
- },
- intervalChanged_: function ( i1, t0, t1 ) {
- const pp = this.parameterPositions;
- let iPrev = i1 - 2,
- iNext = i1 + 1,
- tPrev = pp[ iPrev ],
- tNext = pp[ iNext ];
- if ( tPrev === undefined ) {
- switch ( this.getSettings_().endingStart ) {
- case ZeroSlopeEnding:
- // f'(t0) = 0
- iPrev = i1;
- tPrev = 2 * t0 - t1;
- break;
- case WrapAroundEnding:
- // use the other end of the curve
- iPrev = pp.length - 2;
- tPrev = t0 + pp[ iPrev ] - pp[ iPrev + 1 ];
- break;
- default: // ZeroCurvatureEnding
- // f''(t0) = 0 a.k.a. Natural Spline
- iPrev = i1;
- tPrev = t1;
- }
- }
- if ( tNext === undefined ) {
- switch ( this.getSettings_().endingEnd ) {
- case ZeroSlopeEnding:
- // f'(tN) = 0
- iNext = i1;
- tNext = 2 * t1 - t0;
- break;
- case WrapAroundEnding:
- // use the other end of the curve
- iNext = 1;
- tNext = t1 + pp[ 1 ] - pp[ 0 ];
- break;
- default: // ZeroCurvatureEnding
- // f''(tN) = 0, a.k.a. Natural Spline
- iNext = i1 - 1;
- tNext = t0;
- }
- }
- const halfDt = ( t1 - t0 ) * 0.5,
- stride = this.valueSize;
- this._weightPrev = halfDt / ( t0 - tPrev );
- this._weightNext = halfDt / ( tNext - t1 );
- this._offsetPrev = iPrev * stride;
- this._offsetNext = iNext * stride;
- },
- interpolate_: function ( i1, t0, t, t1 ) {
- const result = this.resultBuffer,
- values = this.sampleValues,
- stride = this.valueSize,
- o1 = i1 * stride, o0 = o1 - stride,
- oP = this._offsetPrev, oN = this._offsetNext,
- wP = this._weightPrev, wN = this._weightNext,
- p = ( t - t0 ) / ( t1 - t0 ),
- pp = p * p,
- ppp = pp * p;
- // evaluate polynomials
- const sP = - wP * ppp + 2 * wP * pp - wP * p;
- const s0 = ( 1 + wP ) * ppp + ( - 1.5 - 2 * wP ) * pp + ( - 0.5 + wP ) * p + 1;
- const s1 = ( - 1 - wN ) * ppp + ( 1.5 + wN ) * pp + 0.5 * p;
- const sN = wN * ppp - wN * pp;
- // combine data linearly
- for ( let i = 0; i !== stride; ++ i ) {
- result[ i ] =
- sP * values[ oP + i ] +
- s0 * values[ o0 + i ] +
- s1 * values[ o1 + i ] +
- sN * values[ oN + i ];
- }
- return result;
- }
- } );
- function LinearInterpolant( parameterPositions, sampleValues, sampleSize, resultBuffer ) {
- Interpolant.call( this, parameterPositions, sampleValues, sampleSize, resultBuffer );
- }
- LinearInterpolant.prototype = Object.assign( Object.create( Interpolant.prototype ), {
- constructor: LinearInterpolant,
- interpolate_: function ( i1, t0, t, t1 ) {
- const result = this.resultBuffer,
- values = this.sampleValues,
- stride = this.valueSize,
- offset1 = i1 * stride,
- offset0 = offset1 - stride,
- weight1 = ( t - t0 ) / ( t1 - t0 ),
- weight0 = 1 - weight1;
- for ( let i = 0; i !== stride; ++ i ) {
- result[ i ] =
- values[ offset0 + i ] * weight0 +
- values[ offset1 + i ] * weight1;
- }
- return result;
- }
- } );
- /**
- *
- * Interpolant that evaluates to the sample value at the position preceeding
- * the parameter.
- */
- function DiscreteInterpolant( parameterPositions, sampleValues, sampleSize, resultBuffer ) {
- Interpolant.call( this, parameterPositions, sampleValues, sampleSize, resultBuffer );
- }
- DiscreteInterpolant.prototype = Object.assign( Object.create( Interpolant.prototype ), {
- constructor: DiscreteInterpolant,
- interpolate_: function ( i1 /*, t0, t, t1 */ ) {
- return this.copySampleValue_( i1 - 1 );
- }
- } );
- function KeyframeTrack( name, times, values, interpolation ) {
- if ( name === undefined ) throw new Error( 'THREE.KeyframeTrack: track name is undefined' );
- if ( times === undefined || times.length === 0 ) throw new Error( 'THREE.KeyframeTrack: no keyframes in track named ' + name );
- this.name = name;
- this.times = AnimationUtils.convertArray( times, this.TimeBufferType );
- this.values = AnimationUtils.convertArray( values, this.ValueBufferType );
- this.setInterpolation( interpolation || this.DefaultInterpolation );
- }
- // Static methods
- Object.assign( KeyframeTrack, {
- // Serialization (in static context, because of constructor invocation
- // and automatic invocation of .toJSON):
- toJSON: function ( track ) {
- const trackType = track.constructor;
- let json;
- // derived classes can define a static toJSON method
- if ( trackType.toJSON !== undefined ) {
- json = trackType.toJSON( track );
- } else {
- // by default, we assume the data can be serialized as-is
- json = {
- 'name': track.name,
- 'times': AnimationUtils.convertArray( track.times, Array ),
- 'values': AnimationUtils.convertArray( track.values, Array )
- };
- const interpolation = track.getInterpolation();
- if ( interpolation !== track.DefaultInterpolation ) {
- json.interpolation = interpolation;
- }
- }
- json.type = track.ValueTypeName; // mandatory
- return json;
- }
- } );
- Object.assign( KeyframeTrack.prototype, {
- constructor: KeyframeTrack,
- TimeBufferType: Float32Array,
- ValueBufferType: Float32Array,
- DefaultInterpolation: InterpolateLinear,
- InterpolantFactoryMethodDiscrete: function ( result ) {
- return new DiscreteInterpolant( this.times, this.values, this.getValueSize(), result );
- },
- InterpolantFactoryMethodLinear: function ( result ) {
- return new LinearInterpolant( this.times, this.values, this.getValueSize(), result );
- },
- InterpolantFactoryMethodSmooth: function ( result ) {
- return new CubicInterpolant( this.times, this.values, this.getValueSize(), result );
- },
- setInterpolation: function ( interpolation ) {
- let factoryMethod;
- switch ( interpolation ) {
- case InterpolateDiscrete:
- factoryMethod = this.InterpolantFactoryMethodDiscrete;
- break;
- case InterpolateLinear:
- factoryMethod = this.InterpolantFactoryMethodLinear;
- break;
- case InterpolateSmooth:
- factoryMethod = this.InterpolantFactoryMethodSmooth;
- break;
- }
- if ( factoryMethod === undefined ) {
- const message = 'unsupported interpolation for ' +
- this.ValueTypeName + ' keyframe track named ' + this.name;
- if ( this.createInterpolant === undefined ) {
- // fall back to default, unless the default itself is messed up
- if ( interpolation !== this.DefaultInterpolation ) {
- this.setInterpolation( this.DefaultInterpolation );
- } else {
- throw new Error( message ); // fatal, in this case
- }
- }
- console.warn( 'THREE.KeyframeTrack:', message );
- return this;
- }
- this.createInterpolant = factoryMethod;
- return this;
- },
- getInterpolation: function () {
- switch ( this.createInterpolant ) {
- case this.InterpolantFactoryMethodDiscrete:
- return InterpolateDiscrete;
- case this.InterpolantFactoryMethodLinear:
- return InterpolateLinear;
- case this.InterpolantFactoryMethodSmooth:
- return InterpolateSmooth;
- }
- },
- getValueSize: function () {
- return this.values.length / this.times.length;
- },
- // move all keyframes either forwards or backwards in time
- shift: function ( timeOffset ) {
- if ( timeOffset !== 0.0 ) {
- const times = this.times;
- for ( let i = 0, n = times.length; i !== n; ++ i ) {
- times[ i ] += timeOffset;
- }
- }
- return this;
- },
- // scale all keyframe times by a factor (useful for frame <-> seconds conversions)
- scale: function ( timeScale ) {
- if ( timeScale !== 1.0 ) {
- const times = this.times;
- for ( let i = 0, n = times.length; i !== n; ++ i ) {
- times[ i ] *= timeScale;
- }
- }
- return this;
- },
- // removes keyframes before and after animation without changing any values within the range [startTime, endTime].
- // IMPORTANT: We do not shift around keys to the start of the track time, because for interpolated keys this will change their values
- trim: function ( startTime, endTime ) {
- const times = this.times,
- nKeys = times.length;
- let from = 0,
- to = nKeys - 1;
- while ( from !== nKeys && times[ from ] < startTime ) {
- ++ from;
- }
- while ( to !== - 1 && times[ to ] > endTime ) {
- -- to;
- }
- ++ to; // inclusive -> exclusive bound
- if ( from !== 0 || to !== nKeys ) {
- // empty tracks are forbidden, so keep at least one keyframe
- if ( from >= to ) {
- to = Math.max( to, 1 );
- from = to - 1;
- }
- const stride = this.getValueSize();
- this.times = AnimationUtils.arraySlice( times, from, to );
- this.values = AnimationUtils.arraySlice( this.values, from * stride, to * stride );
- }
- return this;
- },
- // ensure we do not get a GarbageInGarbageOut situation, make sure tracks are at least minimally viable
- validate: function () {
- let valid = true;
- const valueSize = this.getValueSize();
- if ( valueSize - Math.floor( valueSize ) !== 0 ) {
- console.error( 'THREE.KeyframeTrack: Invalid value size in track.', this );
- valid = false;
- }
- const times = this.times,
- values = this.values,
- nKeys = times.length;
- if ( nKeys === 0 ) {
- console.error( 'THREE.KeyframeTrack: Track is empty.', this );
- valid = false;
- }
- let prevTime = null;
- for ( let i = 0; i !== nKeys; i ++ ) {
- const currTime = times[ i ];
- if ( typeof currTime === 'number' && isNaN( currTime ) ) {
- console.error( 'THREE.KeyframeTrack: Time is not a valid number.', this, i, currTime );
- valid = false;
- break;
- }
- if ( prevTime !== null && prevTime > currTime ) {
- console.error( 'THREE.KeyframeTrack: Out of order keys.', this, i, currTime, prevTime );
- valid = false;
- break;
- }
- prevTime = currTime;
- }
- if ( values !== undefined ) {
- if ( AnimationUtils.isTypedArray( values ) ) {
- for ( let i = 0, n = values.length; i !== n; ++ i ) {
- const value = values[ i ];
- if ( isNaN( value ) ) {
- console.error( 'THREE.KeyframeTrack: Value is not a valid number.', this, i, value );
- valid = false;
- break;
- }
- }
- }
- }
- return valid;
- },
- // removes equivalent sequential keys as common in morph target sequences
- // (0,0,0,0,1,1,1,0,0,0,0,0,0,0) --> (0,0,1,1,0,0)
- optimize: function () {
- // times or values may be shared with other tracks, so overwriting is unsafe
- const times = AnimationUtils.arraySlice( this.times ),
- values = AnimationUtils.arraySlice( this.values ),
- stride = this.getValueSize(),
- smoothInterpolation = this.getInterpolation() === InterpolateSmooth,
- lastIndex = times.length - 1;
- let writeIndex = 1;
- for ( let i = 1; i < lastIndex; ++ i ) {
- let keep = false;
- const time = times[ i ];
- const timeNext = times[ i + 1 ];
- // remove adjacent keyframes scheduled at the same time
- if ( time !== timeNext && ( i !== 1 || time !== time[ 0 ] ) ) {
- if ( ! smoothInterpolation ) {
- // remove unnecessary keyframes same as their neighbors
- const offset = i * stride,
- offsetP = offset - stride,
- offsetN = offset + stride;
- for ( let j = 0; j !== stride; ++ j ) {
- const value = values[ offset + j ];
- if ( value !== values[ offsetP + j ] ||
- value !== values[ offsetN + j ] ) {
- keep = true;
- break;
- }
- }
- } else {
- keep = true;
- }
- }
- // in-place compaction
- if ( keep ) {
- if ( i !== writeIndex ) {
- times[ writeIndex ] = times[ i ];
- const readOffset = i * stride,
- writeOffset = writeIndex * stride;
- for ( let j = 0; j !== stride; ++ j ) {
- values[ writeOffset + j ] = values[ readOffset + j ];
- }
- }
- ++ writeIndex;
- }
- }
- // flush last keyframe (compaction looks ahead)
- if ( lastIndex > 0 ) {
- times[ writeIndex ] = times[ lastIndex ];
- for ( let readOffset = lastIndex * stride, writeOffset = writeIndex * stride, j = 0; j !== stride; ++ j ) {
- values[ writeOffset + j ] = values[ readOffset + j ];
- }
- ++ writeIndex;
- }
- if ( writeIndex !== times.length ) {
- this.times = AnimationUtils.arraySlice( times, 0, writeIndex );
- this.values = AnimationUtils.arraySlice( values, 0, writeIndex * stride );
- } else {
- this.times = times;
- this.values = values;
- }
- return this;
- },
- clone: function () {
- const times = AnimationUtils.arraySlice( this.times, 0 );
- const values = AnimationUtils.arraySlice( this.values, 0 );
- const TypedKeyframeTrack = this.constructor;
- const track = new TypedKeyframeTrack( this.name, times, values );
- // Interpolant argument to constructor is not saved, so copy the factory method directly.
- track.createInterpolant = this.createInterpolant;
- return track;
- }
- } );
- /**
- * A Track of Boolean keyframe values.
- */
- function BooleanKeyframeTrack( name, times, values ) {
- KeyframeTrack.call( this, name, times, values );
- }
- BooleanKeyframeTrack.prototype = Object.assign( Object.create( KeyframeTrack.prototype ), {
- constructor: BooleanKeyframeTrack,
- ValueTypeName: 'bool',
- ValueBufferType: Array,
- DefaultInterpolation: InterpolateDiscrete,
- InterpolantFactoryMethodLinear: undefined,
- InterpolantFactoryMethodSmooth: undefined
- // Note: Actually this track could have a optimized / compressed
- // representation of a single value and a custom interpolant that
- // computes "firstValue ^ isOdd( index )".
- } );
- /**
- * A Track of keyframe values that represent color.
- */
- function ColorKeyframeTrack( name, times, values, interpolation ) {
- KeyframeTrack.call( this, name, times, values, interpolation );
- }
- ColorKeyframeTrack.prototype = Object.assign( Object.create( KeyframeTrack.prototype ), {
- constructor: ColorKeyframeTrack,
- ValueTypeName: 'color'
- // ValueBufferType is inherited
- // DefaultInterpolation is inherited
- // Note: Very basic implementation and nothing special yet.
- // However, this is the place for color space parameterization.
- } );
- /**
- * A Track of numeric keyframe values.
- */
- function NumberKeyframeTrack( name, times, values, interpolation ) {
- KeyframeTrack.call( this, name, times, values, interpolation );
- }
- NumberKeyframeTrack.prototype = Object.assign( Object.create( KeyframeTrack.prototype ), {
- constructor: NumberKeyframeTrack,
- ValueTypeName: 'number'
- // ValueBufferType is inherited
- // DefaultInterpolation is inherited
- } );
- /**
- * Spherical linear unit quaternion interpolant.
- */
- function QuaternionLinearInterpolant( parameterPositions, sampleValues, sampleSize, resultBuffer ) {
- Interpolant.call( this, parameterPositions, sampleValues, sampleSize, resultBuffer );
- }
- QuaternionLinearInterpolant.prototype = Object.assign( Object.create( Interpolant.prototype ), {
- constructor: QuaternionLinearInterpolant,
- interpolate_: function ( i1, t0, t, t1 ) {
- const result = this.resultBuffer,
- values = this.sampleValues,
- stride = this.valueSize,
- alpha = ( t - t0 ) / ( t1 - t0 );
- let offset = i1 * stride;
- for ( let end = offset + stride; offset !== end; offset += 4 ) {
- Quaternion.slerpFlat( result, 0, values, offset - stride, values, offset, alpha );
- }
- return result;
- }
- } );
- /**
- * A Track of quaternion keyframe values.
- */
- function QuaternionKeyframeTrack( name, times, values, interpolation ) {
- KeyframeTrack.call( this, name, times, values, interpolation );
- }
- QuaternionKeyframeTrack.prototype = Object.assign( Object.create( KeyframeTrack.prototype ), {
- constructor: QuaternionKeyframeTrack,
- ValueTypeName: 'quaternion',
- // ValueBufferType is inherited
- DefaultInterpolation: InterpolateLinear,
- InterpolantFactoryMethodLinear: function ( result ) {
- return new QuaternionLinearInterpolant( this.times, this.values, this.getValueSize(), result );
- },
- InterpolantFactoryMethodSmooth: undefined // not yet implemented
- } );
- /**
- * A Track that interpolates Strings
- */
- function StringKeyframeTrack( name, times, values, interpolation ) {
- KeyframeTrack.call( this, name, times, values, interpolation );
- }
- StringKeyframeTrack.prototype = Object.assign( Object.create( KeyframeTrack.prototype ), {
- constructor: StringKeyframeTrack,
- ValueTypeName: 'string',
- ValueBufferType: Array,
- DefaultInterpolation: InterpolateDiscrete,
- InterpolantFactoryMethodLinear: undefined,
- InterpolantFactoryMethodSmooth: undefined
- } );
- /**
- * A Track of vectored keyframe values.
- */
- function VectorKeyframeTrack( name, times, values, interpolation ) {
- KeyframeTrack.call( this, name, times, values, interpolation );
- }
- VectorKeyframeTrack.prototype = Object.assign( Object.create( KeyframeTrack.prototype ), {
- constructor: VectorKeyframeTrack,
- ValueTypeName: 'vector'
- // ValueBufferType is inherited
- // DefaultInterpolation is inherited
- } );
- function AnimationClip( name, duration = - 1, tracks, blendMode = NormalAnimationBlendMode ) {
- this.name = name;
- this.tracks = tracks;
- this.duration = duration;
- this.blendMode = blendMode;
- this.uuid = MathUtils.generateUUID();
- // this means it should figure out its duration by scanning the tracks
- if ( this.duration < 0 ) {
- this.resetDuration();
- }
- }
- function getTrackTypeForValueTypeName( typeName ) {
- switch ( typeName.toLowerCase() ) {
- case 'scalar':
- case 'double':
- case 'float':
- case 'number':
- case 'integer':
- return NumberKeyframeTrack;
- case 'vector':
- case 'vector2':
- case 'vector3':
- case 'vector4':
- return VectorKeyframeTrack;
- case 'color':
- return ColorKeyframeTrack;
- case 'quaternion':
- return QuaternionKeyframeTrack;
- case 'bool':
- case 'boolean':
- return BooleanKeyframeTrack;
- case 'string':
- return StringKeyframeTrack;
- }
- throw new Error( 'THREE.KeyframeTrack: Unsupported typeName: ' + typeName );
- }
- function parseKeyframeTrack( json ) {
- if ( json.type === undefined ) {
- throw new Error( 'THREE.KeyframeTrack: track type undefined, can not parse' );
- }
- const trackType = getTrackTypeForValueTypeName( json.type );
- if ( json.times === undefined ) {
- const times = [], values = [];
- AnimationUtils.flattenJSON( json.keys, times, values, 'value' );
- json.times = times;
- json.values = values;
- }
- // derived classes can define a static parse method
- if ( trackType.parse !== undefined ) {
- return trackType.parse( json );
- } else {
- // by default, we assume a constructor compatible with the base
- return new trackType( json.name, json.times, json.values, json.interpolation );
- }
- }
- Object.assign( AnimationClip, {
- parse: function ( json ) {
- const tracks = [],
- jsonTracks = json.tracks,
- frameTime = 1.0 / ( json.fps || 1.0 );
- for ( let i = 0, n = jsonTracks.length; i !== n; ++ i ) {
- tracks.push( parseKeyframeTrack( jsonTracks[ i ] ).scale( frameTime ) );
- }
- const clip = new AnimationClip( json.name, json.duration, tracks, json.blendMode );
- clip.uuid = json.uuid;
- return clip;
- },
- toJSON: function ( clip ) {
- const tracks = [],
- clipTracks = clip.tracks;
- const json = {
- 'name': clip.name,
- 'duration': clip.duration,
- 'tracks': tracks,
- 'uuid': clip.uuid,
- 'blendMode': clip.blendMode
- };
- for ( let i = 0, n = clipTracks.length; i !== n; ++ i ) {
- tracks.push( KeyframeTrack.toJSON( clipTracks[ i ] ) );
- }
- return json;
- },
- CreateFromMorphTargetSequence: function ( name, morphTargetSequence, fps, noLoop ) {
- const numMorphTargets = morphTargetSequence.length;
- const tracks = [];
- for ( let i = 0; i < numMorphTargets; i ++ ) {
- let times = [];
- let values = [];
- times.push(
- ( i + numMorphTargets - 1 ) % numMorphTargets,
- i,
- ( i + 1 ) % numMorphTargets );
- values.push( 0, 1, 0 );
- const order = AnimationUtils.getKeyframeOrder( times );
- times = AnimationUtils.sortedArray( times, 1, order );
- values = AnimationUtils.sortedArray( values, 1, order );
- // if there is a key at the first frame, duplicate it as the
- // last frame as well for perfect loop.
- if ( ! noLoop && times[ 0 ] === 0 ) {
- times.push( numMorphTargets );
- values.push( values[ 0 ] );
- }
- tracks.push(
- new NumberKeyframeTrack(
- '.morphTargetInfluences[' + morphTargetSequence[ i ].name + ']',
- times, values
- ).scale( 1.0 / fps ) );
- }
- return new AnimationClip( name, - 1, tracks );
- },
- findByName: function ( objectOrClipArray, name ) {
- let clipArray = objectOrClipArray;
- if ( ! Array.isArray( objectOrClipArray ) ) {
- const o = objectOrClipArray;
- clipArray = o.geometry && o.geometry.animations || o.animations;
- }
- for ( let i = 0; i < clipArray.length; i ++ ) {
- if ( clipArray[ i ].name === name ) {
- return clipArray[ i ];
- }
- }
- return null;
- },
- CreateClipsFromMorphTargetSequences: function ( morphTargets, fps, noLoop ) {
- const animationToMorphTargets = {};
- // tested with https://regex101.com/ on trick sequences
- // such flamingo_flyA_003, flamingo_run1_003, crdeath0059
- const pattern = /^([\w-]*?)([\d]+)$/;
- // sort morph target names into animation groups based
- // patterns like Walk_001, Walk_002, Run_001, Run_002
- for ( let i = 0, il = morphTargets.length; i < il; i ++ ) {
- const morphTarget = morphTargets[ i ];
- const parts = morphTarget.name.match( pattern );
- if ( parts && parts.length > 1 ) {
- const name = parts[ 1 ];
- let animationMorphTargets = animationToMorphTargets[ name ];
- if ( ! animationMorphTargets ) {
- animationToMorphTargets[ name ] = animationMorphTargets = [];
- }
- animationMorphTargets.push( morphTarget );
- }
- }
- const clips = [];
- for ( const name in animationToMorphTargets ) {
- clips.push( AnimationClip.CreateFromMorphTargetSequence( name, animationToMorphTargets[ name ], fps, noLoop ) );
- }
- return clips;
- },
- // parse the animation.hierarchy format
- parseAnimation: function ( animation, bones ) {
- if ( ! animation ) {
- console.error( 'THREE.AnimationClip: No animation in JSONLoader data.' );
- return null;
- }
- const addNonemptyTrack = function ( trackType, trackName, animationKeys, propertyName, destTracks ) {
- // only return track if there are actually keys.
- if ( animationKeys.length !== 0 ) {
- const times = [];
- const values = [];
- AnimationUtils.flattenJSON( animationKeys, times, values, propertyName );
- // empty keys are filtered out, so check again
- if ( times.length !== 0 ) {
- destTracks.push( new trackType( trackName, times, values ) );
- }
- }
- };
- const tracks = [];
- const clipName = animation.name || 'default';
- const fps = animation.fps || 30;
- const blendMode = animation.blendMode;
- // automatic length determination in AnimationClip.
- let duration = animation.length || - 1;
- const hierarchyTracks = animation.hierarchy || [];
- for ( let h = 0; h < hierarchyTracks.length; h ++ ) {
- const animationKeys = hierarchyTracks[ h ].keys;
- // skip empty tracks
- if ( ! animationKeys || animationKeys.length === 0 ) continue;
- // process morph targets
- if ( animationKeys[ 0 ].morphTargets ) {
- // figure out all morph targets used in this track
- const morphTargetNames = {};
- let k;
- for ( k = 0; k < animationKeys.length; k ++ ) {
- if ( animationKeys[ k ].morphTargets ) {
- for ( let m = 0; m < animationKeys[ k ].morphTargets.length; m ++ ) {
- morphTargetNames[ animationKeys[ k ].morphTargets[ m ] ] = - 1;
- }
- }
- }
- // create a track for each morph target with all zero
- // morphTargetInfluences except for the keys in which
- // the morphTarget is named.
- for ( const morphTargetName in morphTargetNames ) {
- const times = [];
- const values = [];
- for ( let m = 0; m !== animationKeys[ k ].morphTargets.length; ++ m ) {
- const animationKey = animationKeys[ k ];
- times.push( animationKey.time );
- values.push( ( animationKey.morphTarget === morphTargetName ) ? 1 : 0 );
- }
- tracks.push( new NumberKeyframeTrack( '.morphTargetInfluence[' + morphTargetName + ']', times, values ) );
- }
- duration = morphTargetNames.length * ( fps || 1.0 );
- } else {
- // ...assume skeletal animation
- const boneName = '.bones[' + bones[ h ].name + ']';
- addNonemptyTrack(
- VectorKeyframeTrack, boneName + '.position',
- animationKeys, 'pos', tracks );
- addNonemptyTrack(
- QuaternionKeyframeTrack, boneName + '.quaternion',
- animationKeys, 'rot', tracks );
- addNonemptyTrack(
- VectorKeyframeTrack, boneName + '.scale',
- animationKeys, 'scl', tracks );
- }
- }
- if ( tracks.length === 0 ) {
- return null;
- }
- const clip = new AnimationClip( clipName, duration, tracks, blendMode );
- return clip;
- }
- } );
- Object.assign( AnimationClip.prototype, {
- resetDuration: function () {
- const tracks = this.tracks;
- let duration = 0;
- for ( let i = 0, n = tracks.length; i !== n; ++ i ) {
- const track = this.tracks[ i ];
- duration = Math.max( duration, track.times[ track.times.length - 1 ] );
- }
- this.duration = duration;
- return this;
- },
- trim: function () {
- for ( let i = 0; i < this.tracks.length; i ++ ) {
- this.tracks[ i ].trim( 0, this.duration );
- }
- return this;
- },
- validate: function () {
- let valid = true;
- for ( let i = 0; i < this.tracks.length; i ++ ) {
- valid = valid && this.tracks[ i ].validate();
- }
- return valid;
- },
- optimize: function () {
- for ( let i = 0; i < this.tracks.length; i ++ ) {
- this.tracks[ i ].optimize();
- }
- return this;
- },
- clone: function () {
- const tracks = [];
- for ( let i = 0; i < this.tracks.length; i ++ ) {
- tracks.push( this.tracks[ i ].clone() );
- }
- return new AnimationClip( this.name, this.duration, tracks, this.blendMode );
- },
- toJSON: function () {
- return AnimationClip.toJSON( this );
- }
- } );
- const Cache = {
- enabled: false,
- files: {},
- add: function ( key, file ) {
- if ( this.enabled === false ) return;
- // console.log( 'THREE.Cache', 'Adding key:', key );
- this.files[ key ] = file;
- },
- get: function ( key ) {
- if ( this.enabled === false ) return;
- // console.log( 'THREE.Cache', 'Checking key:', key );
- return this.files[ key ];
- },
- remove: function ( key ) {
- delete this.files[ key ];
- },
- clear: function () {
- this.files = {};
- }
- };
- function LoadingManager( onLoad, onProgress, onError ) {
- const scope = this;
- let isLoading = false;
- let itemsLoaded = 0;
- let itemsTotal = 0;
- let urlModifier = undefined;
- const handlers = [];
- // Refer to #5689 for the reason why we don't set .onStart
- // in the constructor
- this.onStart = undefined;
- this.onLoad = onLoad;
- this.onProgress = onProgress;
- this.onError = onError;
- this.itemStart = function ( url ) {
- itemsTotal ++;
- if ( isLoading === false ) {
- if ( scope.onStart !== undefined ) {
- scope.onStart( url, itemsLoaded, itemsTotal );
- }
- }
- isLoading = true;
- };
- this.itemEnd = function ( url ) {
- itemsLoaded ++;
- if ( scope.onProgress !== undefined ) {
- scope.onProgress( url, itemsLoaded, itemsTotal );
- }
- if ( itemsLoaded === itemsTotal ) {
- isLoading = false;
- if ( scope.onLoad !== undefined ) {
- scope.onLoad();
- }
- }
- };
- this.itemError = function ( url ) {
- if ( scope.onError !== undefined ) {
- scope.onError( url );
- }
- };
- this.resolveURL = function ( url ) {
- if ( urlModifier ) {
- return urlModifier( url );
- }
- return url;
- };
- this.setURLModifier = function ( transform ) {
- urlModifier = transform;
- return this;
- };
- this.addHandler = function ( regex, loader ) {
- handlers.push( regex, loader );
- return this;
- };
- this.removeHandler = function ( regex ) {
- const index = handlers.indexOf( regex );
- if ( index !== - 1 ) {
- handlers.splice( index, 2 );
- }
- return this;
- };
- this.getHandler = function ( file ) {
- for ( let i = 0, l = handlers.length; i < l; i += 2 ) {
- const regex = handlers[ i ];
- const loader = handlers[ i + 1 ];
- if ( regex.global ) regex.lastIndex = 0; // see #17920
- if ( regex.test( file ) ) {
- return loader;
- }
- }
- return null;
- };
- }
- const DefaultLoadingManager = new LoadingManager();
- function Loader( manager ) {
- this.manager = ( manager !== undefined ) ? manager : DefaultLoadingManager;
- this.crossOrigin = 'anonymous';
- this.withCredentials = false;
- this.path = '';
- this.resourcePath = '';
- this.requestHeader = {};
- }
- Object.assign( Loader.prototype, {
- load: function ( /* url, onLoad, onProgress, onError */ ) {},
- loadAsync: function ( url, onProgress ) {
- const scope = this;
- return new Promise( function ( resolve, reject ) {
- scope.load( url, resolve, onProgress, reject );
- } );
- },
- parse: function ( /* data */ ) {},
- setCrossOrigin: function ( crossOrigin ) {
- this.crossOrigin = crossOrigin;
- return this;
- },
- setWithCredentials: function ( value ) {
- this.withCredentials = value;
- return this;
- },
- setPath: function ( path ) {
- this.path = path;
- return this;
- },
- setResourcePath: function ( resourcePath ) {
- this.resourcePath = resourcePath;
- return this;
- },
- setRequestHeader: function ( requestHeader ) {
- this.requestHeader = requestHeader;
- return this;
- }
- } );
- const loading = {};
- function FileLoader( manager ) {
- Loader.call( this, manager );
- }
- FileLoader.prototype = Object.assign( Object.create( Loader.prototype ), {
- constructor: FileLoader,
- load: function ( url, onLoad, onProgress, onError ) {
- if ( url === undefined ) url = '';
- if ( this.path !== undefined ) url = this.path + url;
- url = this.manager.resolveURL( url );
- const scope = this;
- const cached = Cache.get( url );
- if ( cached !== undefined ) {
- scope.manager.itemStart( url );
- setTimeout( function () {
- if ( onLoad ) onLoad( cached );
- scope.manager.itemEnd( url );
- }, 0 );
- return cached;
- }
- // Check if request is duplicate
- if ( loading[ url ] !== undefined ) {
- loading[ url ].push( {
- onLoad: onLoad,
- onProgress: onProgress,
- onError: onError
- } );
- return;
- }
- // Check for data: URI
- const dataUriRegex = /^data:(.*?)(;base64)?,(.*)$/;
- const dataUriRegexResult = url.match( dataUriRegex );
- let request;
- // Safari can not handle Data URIs through XMLHttpRequest so process manually
- if ( dataUriRegexResult ) {
- const mimeType = dataUriRegexResult[ 1 ];
- const isBase64 = !! dataUriRegexResult[ 2 ];
- let data = dataUriRegexResult[ 3 ];
- data = decodeURIComponent( data );
- if ( isBase64 ) data = atob( data );
- try {
- let response;
- const responseType = ( this.responseType || '' ).toLowerCase();
- switch ( responseType ) {
- case 'arraybuffer':
- case 'blob':
- const view = new Uint8Array( data.length );
- for ( let i = 0; i < data.length; i ++ ) {
- view[ i ] = data.charCodeAt( i );
- }
- if ( responseType === 'blob' ) {
- response = new Blob( [ view.buffer ], { type: mimeType } );
- } else {
- response = view.buffer;
- }
- break;
- case 'document':
- const parser = new DOMParser();
- response = parser.parseFromString( data, mimeType );
- break;
- case 'json':
- response = JSON.parse( data );
- break;
- default: // 'text' or other
- response = data;
- break;
- }
- // Wait for next browser tick like standard XMLHttpRequest event dispatching does
- setTimeout( function () {
- if ( onLoad ) onLoad( response );
- scope.manager.itemEnd( url );
- }, 0 );
- } catch ( error ) {
- // Wait for next browser tick like standard XMLHttpRequest event dispatching does
- setTimeout( function () {
- if ( onError ) onError( error );
- scope.manager.itemError( url );
- scope.manager.itemEnd( url );
- }, 0 );
- }
- } else {
- // Initialise array for duplicate requests
- loading[ url ] = [];
- loading[ url ].push( {
- onLoad: onLoad,
- onProgress: onProgress,
- onError: onError
- } );
- request = new XMLHttpRequest();
- request.open( 'GET', url, true );
- request.addEventListener( 'load', function ( event ) {
- const response = this.response;
- const callbacks = loading[ url ];
- delete loading[ url ];
- if ( this.status === 200 || this.status === 0 ) {
- // Some browsers return HTTP Status 0 when using non-http protocol
- // e.g. 'file://' or 'data://'. Handle as success.
- if ( this.status === 0 ) console.warn( 'THREE.FileLoader: HTTP Status 0 received.' );
- // Add to cache only on HTTP success, so that we do not cache
- // error response bodies as proper responses to requests.
- Cache.add( url, response );
- for ( let i = 0, il = callbacks.length; i < il; i ++ ) {
- const callback = callbacks[ i ];
- if ( callback.onLoad ) callback.onLoad( response );
- }
- scope.manager.itemEnd( url );
- } else {
- for ( let i = 0, il = callbacks.length; i < il; i ++ ) {
- const callback = callbacks[ i ];
- if ( callback.onError ) callback.onError( event );
- }
- scope.manager.itemError( url );
- scope.manager.itemEnd( url );
- }
- }, false );
- request.addEventListener( 'progress', function ( event ) {
- const callbacks = loading[ url ];
- for ( let i = 0, il = callbacks.length; i < il; i ++ ) {
- const callback = callbacks[ i ];
- if ( callback.onProgress ) callback.onProgress( event );
- }
- }, false );
- request.addEventListener( 'error', function ( event ) {
- const callbacks = loading[ url ];
- delete loading[ url ];
- for ( let i = 0, il = callbacks.length; i < il; i ++ ) {
- const callback = callbacks[ i ];
- if ( callback.onError ) callback.onError( event );
- }
- scope.manager.itemError( url );
- scope.manager.itemEnd( url );
- }, false );
- request.addEventListener( 'abort', function ( event ) {
- const callbacks = loading[ url ];
- delete loading[ url ];
- for ( let i = 0, il = callbacks.length; i < il; i ++ ) {
- const callback = callbacks[ i ];
- if ( callback.onError ) callback.onError( event );
- }
- scope.manager.itemError( url );
- scope.manager.itemEnd( url );
- }, false );
- if ( this.responseType !== undefined ) request.responseType = this.responseType;
- if ( this.withCredentials !== undefined ) request.withCredentials = this.withCredentials;
- if ( request.overrideMimeType ) request.overrideMimeType( this.mimeType !== undefined ? this.mimeType : 'text/plain' );
- for ( const header in this.requestHeader ) {
- request.setRequestHeader( header, this.requestHeader[ header ] );
- }
- request.send( null );
- }
- scope.manager.itemStart( url );
- return request;
- },
- setResponseType: function ( value ) {
- this.responseType = value;
- return this;
- },
- setMimeType: function ( value ) {
- this.mimeType = value;
- return this;
- }
- } );
- function AnimationLoader( manager ) {
- Loader.call( this, manager );
- }
- AnimationLoader.prototype = Object.assign( Object.create( Loader.prototype ), {
- constructor: AnimationLoader,
- load: function ( url, onLoad, onProgress, onError ) {
- const scope = this;
- const loader = new FileLoader( scope.manager );
- loader.setPath( scope.path );
- loader.setRequestHeader( scope.requestHeader );
- loader.setWithCredentials( scope.withCredentials );
- loader.load( url, function ( text ) {
- try {
- onLoad( scope.parse( JSON.parse( text ) ) );
- } catch ( e ) {
- if ( onError ) {
- onError( e );
- } else {
- console.error( e );
- }
- scope.manager.itemError( url );
- }
- }, onProgress, onError );
- },
- parse: function ( json ) {
- const animations = [];
- for ( let i = 0; i < json.length; i ++ ) {
- const clip = AnimationClip.parse( json[ i ] );
- animations.push( clip );
- }
- return animations;
- }
- } );
- /**
- * Abstract Base class to block based textures loader (dds, pvr, ...)
- *
- * Sub classes have to implement the parse() method which will be used in load().
- */
- function CompressedTextureLoader( manager ) {
- Loader.call( this, manager );
- }
- CompressedTextureLoader.prototype = Object.assign( Object.create( Loader.prototype ), {
- constructor: CompressedTextureLoader,
- load: function ( url, onLoad, onProgress, onError ) {
- const scope = this;
- const images = [];
- const texture = new CompressedTexture();
- const loader = new FileLoader( this.manager );
- loader.setPath( this.path );
- loader.setResponseType( 'arraybuffer' );
- loader.setRequestHeader( this.requestHeader );
- loader.setWithCredentials( scope.withCredentials );
- let loaded = 0;
- function loadTexture( i ) {
- loader.load( url[ i ], function ( buffer ) {
- const texDatas = scope.parse( buffer, true );
- images[ i ] = {
- width: texDatas.width,
- height: texDatas.height,
- format: texDatas.format,
- mipmaps: texDatas.mipmaps
- };
- loaded += 1;
- if ( loaded === 6 ) {
- if ( texDatas.mipmapCount === 1 ) texture.minFilter = LinearFilter;
- texture.image = images;
- texture.format = texDatas.format;
- texture.needsUpdate = true;
- if ( onLoad ) onLoad( texture );
- }
- }, onProgress, onError );
- }
- if ( Array.isArray( url ) ) {
- for ( let i = 0, il = url.length; i < il; ++ i ) {
- loadTexture( i );
- }
- } else {
- // compressed cubemap texture stored in a single DDS file
- loader.load( url, function ( buffer ) {
- const texDatas = scope.parse( buffer, true );
- if ( texDatas.isCubemap ) {
- const faces = texDatas.mipmaps.length / texDatas.mipmapCount;
- for ( let f = 0; f < faces; f ++ ) {
- images[ f ] = { mipmaps: [] };
- for ( let i = 0; i < texDatas.mipmapCount; i ++ ) {
- images[ f ].mipmaps.push( texDatas.mipmaps[ f * texDatas.mipmapCount + i ] );
- images[ f ].format = texDatas.format;
- images[ f ].width = texDatas.width;
- images[ f ].height = texDatas.height;
- }
- }
- texture.image = images;
- } else {
- texture.image.width = texDatas.width;
- texture.image.height = texDatas.height;
- texture.mipmaps = texDatas.mipmaps;
- }
- if ( texDatas.mipmapCount === 1 ) {
- texture.minFilter = LinearFilter;
- }
- texture.format = texDatas.format;
- texture.needsUpdate = true;
- if ( onLoad ) onLoad( texture );
- }, onProgress, onError );
- }
- return texture;
- }
- } );
- function ImageLoader( manager ) {
- Loader.call( this, manager );
- }
- ImageLoader.prototype = Object.assign( Object.create( Loader.prototype ), {
- constructor: ImageLoader,
- load: function ( url, onLoad, onProgress, onError ) {
- if ( this.path !== undefined ) url = this.path + url;
- url = this.manager.resolveURL( url );
- const scope = this;
- const cached = Cache.get( url );
- if ( cached !== undefined ) {
- scope.manager.itemStart( url );
- setTimeout( function () {
- if ( onLoad ) onLoad( cached );
- scope.manager.itemEnd( url );
- }, 0 );
- return cached;
- }
- const image = document.createElementNS( 'http://www.w3.org/1999/xhtml', 'img' );
- function onImageLoad() {
- image.removeEventListener( 'load', onImageLoad, false );
- image.removeEventListener( 'error', onImageError, false );
- Cache.add( url, this );
- if ( onLoad ) onLoad( this );
- scope.manager.itemEnd( url );
- }
- function onImageError( event ) {
- image.removeEventListener( 'load', onImageLoad, false );
- image.removeEventListener( 'error', onImageError, false );
- if ( onError ) onError( event );
- scope.manager.itemError( url );
- scope.manager.itemEnd( url );
- }
- image.addEventListener( 'load', onImageLoad, false );
- image.addEventListener( 'error', onImageError, false );
- if ( url.substr( 0, 5 ) !== 'data:' ) {
- if ( this.crossOrigin !== undefined ) image.crossOrigin = this.crossOrigin;
- }
- scope.manager.itemStart( url );
- image.src = url;
- return image;
- }
- } );
- function CubeTextureLoader( manager ) {
- Loader.call( this, manager );
- }
- CubeTextureLoader.prototype = Object.assign( Object.create( Loader.prototype ), {
- constructor: CubeTextureLoader,
- load: function ( urls, onLoad, onProgress, onError ) {
- const texture = new CubeTexture();
- const loader = new ImageLoader( this.manager );
- loader.setCrossOrigin( this.crossOrigin );
- loader.setPath( this.path );
- let loaded = 0;
- function loadTexture( i ) {
- loader.load( urls[ i ], function ( image ) {
- texture.images[ i ] = image;
- loaded ++;
- if ( loaded === 6 ) {
- texture.needsUpdate = true;
- if ( onLoad ) onLoad( texture );
- }
- }, undefined, onError );
- }
- for ( let i = 0; i < urls.length; ++ i ) {
- loadTexture( i );
- }
- return texture;
- }
- } );
- /**
- * Abstract Base class to load generic binary textures formats (rgbe, hdr, ...)
- *
- * Sub classes have to implement the parse() method which will be used in load().
- */
- function DataTextureLoader( manager ) {
- Loader.call( this, manager );
- }
- DataTextureLoader.prototype = Object.assign( Object.create( Loader.prototype ), {
- constructor: DataTextureLoader,
- load: function ( url, onLoad, onProgress, onError ) {
- const scope = this;
- const texture = new DataTexture();
- const loader = new FileLoader( this.manager );
- loader.setResponseType( 'arraybuffer' );
- loader.setRequestHeader( this.requestHeader );
- loader.setPath( this.path );
- loader.setWithCredentials( scope.withCredentials );
- loader.load( url, function ( buffer ) {
- const texData = scope.parse( buffer );
- if ( ! texData ) return;
- if ( texData.image !== undefined ) {
- texture.image = texData.image;
- } else if ( texData.data !== undefined ) {
- texture.image.width = texData.width;
- texture.image.height = texData.height;
- texture.image.data = texData.data;
- }
- texture.wrapS = texData.wrapS !== undefined ? texData.wrapS : ClampToEdgeWrapping;
- texture.wrapT = texData.wrapT !== undefined ? texData.wrapT : ClampToEdgeWrapping;
- texture.magFilter = texData.magFilter !== undefined ? texData.magFilter : LinearFilter;
- texture.minFilter = texData.minFilter !== undefined ? texData.minFilter : LinearFilter;
- texture.anisotropy = texData.anisotropy !== undefined ? texData.anisotropy : 1;
- if ( texData.format !== undefined ) {
- texture.format = texData.format;
- }
- if ( texData.type !== undefined ) {
- texture.type = texData.type;
- }
- if ( texData.mipmaps !== undefined ) {
- texture.mipmaps = texData.mipmaps;
- texture.minFilter = LinearMipmapLinearFilter; // presumably...
- }
- if ( texData.mipmapCount === 1 ) {
- texture.minFilter = LinearFilter;
- }
- texture.needsUpdate = true;
- if ( onLoad ) onLoad( texture, texData );
- }, onProgress, onError );
- return texture;
- }
- } );
- function TextureLoader( manager ) {
- Loader.call( this, manager );
- }
- TextureLoader.prototype = Object.assign( Object.create( Loader.prototype ), {
- constructor: TextureLoader,
- load: function ( url, onLoad, onProgress, onError ) {
- const texture = new Texture();
- const loader = new ImageLoader( this.manager );
- loader.setCrossOrigin( this.crossOrigin );
- loader.setPath( this.path );
- loader.load( url, function ( image ) {
- texture.image = image;
- // JPEGs can't have an alpha channel, so memory can be saved by storing them as RGB.
- const isJPEG = url.search( /\.jpe?g($|\?)/i ) > 0 || url.search( /^data\:image\/jpeg/ ) === 0;
- texture.format = isJPEG ? RGBFormat : RGBAFormat;
- texture.needsUpdate = true;
- if ( onLoad !== undefined ) {
- onLoad( texture );
- }
- }, onProgress, onError );
- return texture;
- }
- } );
- /**
- * Extensible curve object.
- *
- * Some common of curve methods:
- * .getPoint( t, optionalTarget ), .getTangent( t, optionalTarget )
- * .getPointAt( u, optionalTarget ), .getTangentAt( u, optionalTarget )
- * .getPoints(), .getSpacedPoints()
- * .getLength()
- * .updateArcLengths()
- *
- * This following curves inherit from THREE.Curve:
- *
- * -- 2D curves --
- * THREE.ArcCurve
- * THREE.CubicBezierCurve
- * THREE.EllipseCurve
- * THREE.LineCurve
- * THREE.QuadraticBezierCurve
- * THREE.SplineCurve
- *
- * -- 3D curves --
- * THREE.CatmullRomCurve3
- * THREE.CubicBezierCurve3
- * THREE.LineCurve3
- * THREE.QuadraticBezierCurve3
- *
- * A series of curves can be represented as a THREE.CurvePath.
- *
- **/
- function Curve() {
- this.type = 'Curve';
- this.arcLengthDivisions = 200;
- }
- Object.assign( Curve.prototype, {
- // Virtual base class method to overwrite and implement in subclasses
- // - t [0 .. 1]
- getPoint: function ( /* t, optionalTarget */ ) {
- console.warn( 'THREE.Curve: .getPoint() not implemented.' );
- return null;
- },
- // Get point at relative position in curve according to arc length
- // - u [0 .. 1]
- getPointAt: function ( u, optionalTarget ) {
- const t = this.getUtoTmapping( u );
- return this.getPoint( t, optionalTarget );
- },
- // Get sequence of points using getPoint( t )
- getPoints: function ( divisions = 5 ) {
- const points = [];
- for ( let d = 0; d <= divisions; d ++ ) {
- points.push( this.getPoint( d / divisions ) );
- }
- return points;
- },
- // Get sequence of points using getPointAt( u )
- getSpacedPoints: function ( divisions = 5 ) {
- const points = [];
- for ( let d = 0; d <= divisions; d ++ ) {
- points.push( this.getPointAt( d / divisions ) );
- }
- return points;
- },
- // Get total curve arc length
- getLength: function () {
- const lengths = this.getLengths();
- return lengths[ lengths.length - 1 ];
- },
- // Get list of cumulative segment lengths
- getLengths: function ( divisions ) {
- if ( divisions === undefined ) divisions = this.arcLengthDivisions;
- if ( this.cacheArcLengths &&
- ( this.cacheArcLengths.length === divisions + 1 ) &&
- ! this.needsUpdate ) {
- return this.cacheArcLengths;
- }
- this.needsUpdate = false;
- const cache = [];
- let current, last = this.getPoint( 0 );
- let sum = 0;
- cache.push( 0 );
- for ( let p = 1; p <= divisions; p ++ ) {
- current = this.getPoint( p / divisions );
- sum += current.distanceTo( last );
- cache.push( sum );
- last = current;
- }
- this.cacheArcLengths = cache;
- return cache; // { sums: cache, sum: sum }; Sum is in the last element.
- },
- updateArcLengths: function () {
- this.needsUpdate = true;
- this.getLengths();
- },
- // Given u ( 0 .. 1 ), get a t to find p. This gives you points which are equidistant
- getUtoTmapping: function ( u, distance ) {
- const arcLengths = this.getLengths();
- let i = 0;
- const il = arcLengths.length;
- let targetArcLength; // The targeted u distance value to get
- if ( distance ) {
- targetArcLength = distance;
- } else {
- targetArcLength = u * arcLengths[ il - 1 ];
- }
- // binary search for the index with largest value smaller than target u distance
- let low = 0, high = il - 1, comparison;
- while ( low <= high ) {
- i = Math.floor( low + ( high - low ) / 2 ); // less likely to overflow, though probably not issue here, JS doesn't really have integers, all numbers are floats
- comparison = arcLengths[ i ] - targetArcLength;
- if ( comparison < 0 ) {
- low = i + 1;
- } else if ( comparison > 0 ) {
- high = i - 1;
- } else {
- high = i;
- break;
- // DONE
- }
- }
- i = high;
- if ( arcLengths[ i ] === targetArcLength ) {
- return i / ( il - 1 );
- }
- // we could get finer grain at lengths, or use simple interpolation between two points
- const lengthBefore = arcLengths[ i ];
- const lengthAfter = arcLengths[ i + 1 ];
- const segmentLength = lengthAfter - lengthBefore;
- // determine where we are between the 'before' and 'after' points
- const segmentFraction = ( targetArcLength - lengthBefore ) / segmentLength;
- // add that fractional amount to t
- const t = ( i + segmentFraction ) / ( il - 1 );
- return t;
- },
- // Returns a unit vector tangent at t
- // In case any sub curve does not implement its tangent derivation,
- // 2 points a small delta apart will be used to find its gradient
- // which seems to give a reasonable approximation
- getTangent: function ( t, optionalTarget ) {
- const delta = 0.0001;
- let t1 = t - delta;
- let t2 = t + delta;
- // Capping in case of danger
- if ( t1 < 0 ) t1 = 0;
- if ( t2 > 1 ) t2 = 1;
- const pt1 = this.getPoint( t1 );
- const pt2 = this.getPoint( t2 );
- const tangent = optionalTarget || ( ( pt1.isVector2 ) ? new Vector2$1() : new Vector3() );
- tangent.copy( pt2 ).sub( pt1 ).normalize();
- return tangent;
- },
- getTangentAt: function ( u, optionalTarget ) {
- const t = this.getUtoTmapping( u );
- return this.getTangent( t, optionalTarget );
- },
- computeFrenetFrames: function ( segments, closed ) {
- // see http://www.cs.indiana.edu/pub/techreports/TR425.pdf
- const normal = new Vector3();
- const tangents = [];
- const normals = [];
- const binormals = [];
- const vec = new Vector3();
- const mat = new Matrix4();
- // compute the tangent vectors for each segment on the curve
- for ( let i = 0; i <= segments; i ++ ) {
- const u = i / segments;
- tangents[ i ] = this.getTangentAt( u, new Vector3() );
- tangents[ i ].normalize();
- }
- // select an initial normal vector perpendicular to the first tangent vector,
- // and in the direction of the minimum tangent xyz component
- normals[ 0 ] = new Vector3();
- binormals[ 0 ] = new Vector3();
- let min = Number.MAX_VALUE;
- const tx = Math.abs( tangents[ 0 ].x );
- const ty = Math.abs( tangents[ 0 ].y );
- const tz = Math.abs( tangents[ 0 ].z );
- if ( tx <= min ) {
- min = tx;
- normal.set( 1, 0, 0 );
- }
- if ( ty <= min ) {
- min = ty;
- normal.set( 0, 1, 0 );
- }
- if ( tz <= min ) {
- normal.set( 0, 0, 1 );
- }
- vec.crossVectors( tangents[ 0 ], normal ).normalize();
- normals[ 0 ].crossVectors( tangents[ 0 ], vec );
- binormals[ 0 ].crossVectors( tangents[ 0 ], normals[ 0 ] );
- // compute the slowly-varying normal and binormal vectors for each segment on the curve
- for ( let i = 1; i <= segments; i ++ ) {
- normals[ i ] = normals[ i - 1 ].clone();
- binormals[ i ] = binormals[ i - 1 ].clone();
- vec.crossVectors( tangents[ i - 1 ], tangents[ i ] );
- if ( vec.length() > Number.EPSILON ) {
- vec.normalize();
- const theta = Math.acos( MathUtils.clamp( tangents[ i - 1 ].dot( tangents[ i ] ), - 1, 1 ) ); // clamp for floating pt errors
- normals[ i ].applyMatrix4( mat.makeRotationAxis( vec, theta ) );
- }
- binormals[ i ].crossVectors( tangents[ i ], normals[ i ] );
- }
- // if the curve is closed, postprocess the vectors so the first and last normal vectors are the same
- if ( closed === true ) {
- let theta = Math.acos( MathUtils.clamp( normals[ 0 ].dot( normals[ segments ] ), - 1, 1 ) );
- theta /= segments;
- if ( tangents[ 0 ].dot( vec.crossVectors( normals[ 0 ], normals[ segments ] ) ) > 0 ) {
- theta = - theta;
- }
- for ( let i = 1; i <= segments; i ++ ) {
- // twist a little...
- normals[ i ].applyMatrix4( mat.makeRotationAxis( tangents[ i ], theta * i ) );
- binormals[ i ].crossVectors( tangents[ i ], normals[ i ] );
- }
- }
- return {
- tangents: tangents,
- normals: normals,
- binormals: binormals
- };
- },
- clone: function () {
- return new this.constructor().copy( this );
- },
- copy: function ( source ) {
- this.arcLengthDivisions = source.arcLengthDivisions;
- return this;
- },
- toJSON: function () {
- const data = {
- metadata: {
- version: 4.5,
- type: 'Curve',
- generator: 'Curve.toJSON'
- }
- };
- data.arcLengthDivisions = this.arcLengthDivisions;
- data.type = this.type;
- return data;
- },
- fromJSON: function ( json ) {
- this.arcLengthDivisions = json.arcLengthDivisions;
- return this;
- }
- } );
- function EllipseCurve( aX, aY, xRadius, yRadius, aStartAngle, aEndAngle, aClockwise, aRotation ) {
- Curve.call( this );
- this.type = 'EllipseCurve';
- this.aX = aX || 0;
- this.aY = aY || 0;
- this.xRadius = xRadius || 1;
- this.yRadius = yRadius || 1;
- this.aStartAngle = aStartAngle || 0;
- this.aEndAngle = aEndAngle || 2 * Math.PI;
- this.aClockwise = aClockwise || false;
- this.aRotation = aRotation || 0;
- }
- EllipseCurve.prototype = Object.create( Curve.prototype );
- EllipseCurve.prototype.constructor = EllipseCurve;
- EllipseCurve.prototype.isEllipseCurve = true;
- EllipseCurve.prototype.getPoint = function ( t, optionalTarget ) {
- const point = optionalTarget || new Vector2$1();
- const twoPi = Math.PI * 2;
- let deltaAngle = this.aEndAngle - this.aStartAngle;
- const samePoints = Math.abs( deltaAngle ) < Number.EPSILON;
- // ensures that deltaAngle is 0 .. 2 PI
- while ( deltaAngle < 0 ) deltaAngle += twoPi;
- while ( deltaAngle > twoPi ) deltaAngle -= twoPi;
- if ( deltaAngle < Number.EPSILON ) {
- if ( samePoints ) {
- deltaAngle = 0;
- } else {
- deltaAngle = twoPi;
- }
- }
- if ( this.aClockwise === true && ! samePoints ) {
- if ( deltaAngle === twoPi ) {
- deltaAngle = - twoPi;
- } else {
- deltaAngle = deltaAngle - twoPi;
- }
- }
- const angle = this.aStartAngle + t * deltaAngle;
- let x = this.aX + this.xRadius * Math.cos( angle );
- let y = this.aY + this.yRadius * Math.sin( angle );
- if ( this.aRotation !== 0 ) {
- const cos = Math.cos( this.aRotation );
- const sin = Math.sin( this.aRotation );
- const tx = x - this.aX;
- const ty = y - this.aY;
- // Rotate the point about the center of the ellipse.
- x = tx * cos - ty * sin + this.aX;
- y = tx * sin + ty * cos + this.aY;
- }
- return point.set( x, y );
- };
- EllipseCurve.prototype.copy = function ( source ) {
- Curve.prototype.copy.call( this, source );
- this.aX = source.aX;
- this.aY = source.aY;
- this.xRadius = source.xRadius;
- this.yRadius = source.yRadius;
- this.aStartAngle = source.aStartAngle;
- this.aEndAngle = source.aEndAngle;
- this.aClockwise = source.aClockwise;
- this.aRotation = source.aRotation;
- return this;
- };
- EllipseCurve.prototype.toJSON = function () {
- const data = Curve.prototype.toJSON.call( this );
- data.aX = this.aX;
- data.aY = this.aY;
- data.xRadius = this.xRadius;
- data.yRadius = this.yRadius;
- data.aStartAngle = this.aStartAngle;
- data.aEndAngle = this.aEndAngle;
- data.aClockwise = this.aClockwise;
- data.aRotation = this.aRotation;
- return data;
- };
- EllipseCurve.prototype.fromJSON = function ( json ) {
- Curve.prototype.fromJSON.call( this, json );
- this.aX = json.aX;
- this.aY = json.aY;
- this.xRadius = json.xRadius;
- this.yRadius = json.yRadius;
- this.aStartAngle = json.aStartAngle;
- this.aEndAngle = json.aEndAngle;
- this.aClockwise = json.aClockwise;
- this.aRotation = json.aRotation;
- return this;
- };
- function ArcCurve( aX, aY, aRadius, aStartAngle, aEndAngle, aClockwise ) {
- EllipseCurve.call( this, aX, aY, aRadius, aRadius, aStartAngle, aEndAngle, aClockwise );
- this.type = 'ArcCurve';
- }
- ArcCurve.prototype = Object.create( EllipseCurve.prototype );
- ArcCurve.prototype.constructor = ArcCurve;
- ArcCurve.prototype.isArcCurve = true;
- /**
- * Centripetal CatmullRom Curve - which is useful for avoiding
- * cusps and self-intersections in non-uniform catmull rom curves.
- * http://www.cemyuksel.com/research/catmullrom_param/catmullrom.pdf
- *
- * curve.type accepts centripetal(default), chordal and catmullrom
- * curve.tension is used for catmullrom which defaults to 0.5
- */
- /*
- Based on an optimized c++ solution in
- - http://stackoverflow.com/questions/9489736/catmull-rom-curve-with-no-cusps-and-no-self-intersections/
- - http://ideone.com/NoEbVM
- This CubicPoly class could be used for reusing some variables and calculations,
- but for three.js curve use, it could be possible inlined and flatten into a single function call
- which can be placed in CurveUtils.
- */
- function CubicPoly() {
- let c0 = 0, c1 = 0, c2 = 0, c3 = 0;
- /*
- * Compute coefficients for a cubic polynomial
- * p(s) = c0 + c1*s + c2*s^2 + c3*s^3
- * such that
- * p(0) = x0, p(1) = x1
- * and
- * p'(0) = t0, p'(1) = t1.
- */
- function init( x0, x1, t0, t1 ) {
- c0 = x0;
- c1 = t0;
- c2 = - 3 * x0 + 3 * x1 - 2 * t0 - t1;
- c3 = 2 * x0 - 2 * x1 + t0 + t1;
- }
- return {
- initCatmullRom: function ( x0, x1, x2, x3, tension ) {
- init( x1, x2, tension * ( x2 - x0 ), tension * ( x3 - x1 ) );
- },
- initNonuniformCatmullRom: function ( x0, x1, x2, x3, dt0, dt1, dt2 ) {
- // compute tangents when parameterized in [t1,t2]
- let t1 = ( x1 - x0 ) / dt0 - ( x2 - x0 ) / ( dt0 + dt1 ) + ( x2 - x1 ) / dt1;
- let t2 = ( x2 - x1 ) / dt1 - ( x3 - x1 ) / ( dt1 + dt2 ) + ( x3 - x2 ) / dt2;
- // rescale tangents for parametrization in [0,1]
- t1 *= dt1;
- t2 *= dt1;
- init( x1, x2, t1, t2 );
- },
- calc: function ( t ) {
- const t2 = t * t;
- const t3 = t2 * t;
- return c0 + c1 * t + c2 * t2 + c3 * t3;
- }
- };
- }
- //
- const tmp = new Vector3();
- const px = new CubicPoly(), py = new CubicPoly(), pz = new CubicPoly();
- function CatmullRomCurve3( points = [], closed = false, curveType = 'centripetal', tension = 0.5 ) {
- Curve.call( this );
- this.type = 'CatmullRomCurve3';
- this.points = points;
- this.closed = closed;
- this.curveType = curveType;
- this.tension = tension;
- }
- CatmullRomCurve3.prototype = Object.create( Curve.prototype );
- CatmullRomCurve3.prototype.constructor = CatmullRomCurve3;
- CatmullRomCurve3.prototype.isCatmullRomCurve3 = true;
- CatmullRomCurve3.prototype.getPoint = function ( t, optionalTarget = new Vector3() ) {
- const point = optionalTarget;
- const points = this.points;
- const l = points.length;
- const p = ( l - ( this.closed ? 0 : 1 ) ) * t;
- let intPoint = Math.floor( p );
- let weight = p - intPoint;
- if ( this.closed ) {
- intPoint += intPoint > 0 ? 0 : ( Math.floor( Math.abs( intPoint ) / l ) + 1 ) * l;
- } else if ( weight === 0 && intPoint === l - 1 ) {
- intPoint = l - 2;
- weight = 1;
- }
- let p0, p3; // 4 points (p1 & p2 defined below)
- if ( this.closed || intPoint > 0 ) {
- p0 = points[ ( intPoint - 1 ) % l ];
- } else {
- // extrapolate first point
- tmp.subVectors( points[ 0 ], points[ 1 ] ).add( points[ 0 ] );
- p0 = tmp;
- }
- const p1 = points[ intPoint % l ];
- const p2 = points[ ( intPoint + 1 ) % l ];
- if ( this.closed || intPoint + 2 < l ) {
- p3 = points[ ( intPoint + 2 ) % l ];
- } else {
- // extrapolate last point
- tmp.subVectors( points[ l - 1 ], points[ l - 2 ] ).add( points[ l - 1 ] );
- p3 = tmp;
- }
- if ( this.curveType === 'centripetal' || this.curveType === 'chordal' ) {
- // init Centripetal / Chordal Catmull-Rom
- const pow = this.curveType === 'chordal' ? 0.5 : 0.25;
- let dt0 = Math.pow( p0.distanceToSquared( p1 ), pow );
- let dt1 = Math.pow( p1.distanceToSquared( p2 ), pow );
- let dt2 = Math.pow( p2.distanceToSquared( p3 ), pow );
- // safety check for repeated points
- if ( dt1 < 1e-4 ) dt1 = 1.0;
- if ( dt0 < 1e-4 ) dt0 = dt1;
- if ( dt2 < 1e-4 ) dt2 = dt1;
- px.initNonuniformCatmullRom( p0.x, p1.x, p2.x, p3.x, dt0, dt1, dt2 );
- py.initNonuniformCatmullRom( p0.y, p1.y, p2.y, p3.y, dt0, dt1, dt2 );
- pz.initNonuniformCatmullRom( p0.z, p1.z, p2.z, p3.z, dt0, dt1, dt2 );
- } else if ( this.curveType === 'catmullrom' ) {
- px.initCatmullRom( p0.x, p1.x, p2.x, p3.x, this.tension );
- py.initCatmullRom( p0.y, p1.y, p2.y, p3.y, this.tension );
- pz.initCatmullRom( p0.z, p1.z, p2.z, p3.z, this.tension );
- }
- point.set(
- px.calc( weight ),
- py.calc( weight ),
- pz.calc( weight )
- );
- return point;
- };
- CatmullRomCurve3.prototype.copy = function ( source ) {
- Curve.prototype.copy.call( this, source );
- this.points = [];
- for ( let i = 0, l = source.points.length; i < l; i ++ ) {
- const point = source.points[ i ];
- this.points.push( point.clone() );
- }
- this.closed = source.closed;
- this.curveType = source.curveType;
- this.tension = source.tension;
- return this;
- };
- CatmullRomCurve3.prototype.toJSON = function () {
- const data = Curve.prototype.toJSON.call( this );
- data.points = [];
- for ( let i = 0, l = this.points.length; i < l; i ++ ) {
- const point = this.points[ i ];
- data.points.push( point.toArray() );
- }
- data.closed = this.closed;
- data.curveType = this.curveType;
- data.tension = this.tension;
- return data;
- };
- CatmullRomCurve3.prototype.fromJSON = function ( json ) {
- Curve.prototype.fromJSON.call( this, json );
- this.points = [];
- for ( let i = 0, l = json.points.length; i < l; i ++ ) {
- const point = json.points[ i ];
- this.points.push( new Vector3().fromArray( point ) );
- }
- this.closed = json.closed;
- this.curveType = json.curveType;
- this.tension = json.tension;
- return this;
- };
- /**
- * Bezier Curves formulas obtained from
- * http://en.wikipedia.org/wiki/Bézier_curve
- */
- function CatmullRom( t, p0, p1, p2, p3 ) {
- const v0 = ( p2 - p0 ) * 0.5;
- const v1 = ( p3 - p1 ) * 0.5;
- const t2 = t * t;
- const t3 = t * t2;
- return ( 2 * p1 - 2 * p2 + v0 + v1 ) * t3 + ( - 3 * p1 + 3 * p2 - 2 * v0 - v1 ) * t2 + v0 * t + p1;
- }
- //
- function QuadraticBezierP0( t, p ) {
- const k = 1 - t;
- return k * k * p;
- }
- function QuadraticBezierP1( t, p ) {
- return 2 * ( 1 - t ) * t * p;
- }
- function QuadraticBezierP2( t, p ) {
- return t * t * p;
- }
- function QuadraticBezier( t, p0, p1, p2 ) {
- return QuadraticBezierP0( t, p0 ) + QuadraticBezierP1( t, p1 ) +
- QuadraticBezierP2( t, p2 );
- }
- //
- function CubicBezierP0( t, p ) {
- const k = 1 - t;
- return k * k * k * p;
- }
- function CubicBezierP1( t, p ) {
- const k = 1 - t;
- return 3 * k * k * t * p;
- }
- function CubicBezierP2( t, p ) {
- return 3 * ( 1 - t ) * t * t * p;
- }
- function CubicBezierP3( t, p ) {
- return t * t * t * p;
- }
- function CubicBezier( t, p0, p1, p2, p3 ) {
- return CubicBezierP0( t, p0 ) + CubicBezierP1( t, p1 ) + CubicBezierP2( t, p2 ) +
- CubicBezierP3( t, p3 );
- }
- function CubicBezierCurve( v0 = new Vector2$1(), v1 = new Vector2$1(), v2 = new Vector2$1(), v3 = new Vector2$1() ) {
- Curve.call( this );
- this.type = 'CubicBezierCurve';
- this.v0 = v0;
- this.v1 = v1;
- this.v2 = v2;
- this.v3 = v3;
- }
- CubicBezierCurve.prototype = Object.create( Curve.prototype );
- CubicBezierCurve.prototype.constructor = CubicBezierCurve;
- CubicBezierCurve.prototype.isCubicBezierCurve = true;
- CubicBezierCurve.prototype.getPoint = function ( t, optionalTarget = new Vector2$1() ) {
- const point = optionalTarget;
- const v0 = this.v0, v1 = this.v1, v2 = this.v2, v3 = this.v3;
- point.set(
- CubicBezier( t, v0.x, v1.x, v2.x, v3.x ),
- CubicBezier( t, v0.y, v1.y, v2.y, v3.y )
- );
- return point;
- };
- CubicBezierCurve.prototype.copy = function ( source ) {
- Curve.prototype.copy.call( this, source );
- this.v0.copy( source.v0 );
- this.v1.copy( source.v1 );
- this.v2.copy( source.v2 );
- this.v3.copy( source.v3 );
- return this;
- };
- CubicBezierCurve.prototype.toJSON = function () {
- const data = Curve.prototype.toJSON.call( this );
- data.v0 = this.v0.toArray();
- data.v1 = this.v1.toArray();
- data.v2 = this.v2.toArray();
- data.v3 = this.v3.toArray();
- return data;
- };
- CubicBezierCurve.prototype.fromJSON = function ( json ) {
- Curve.prototype.fromJSON.call( this, json );
- this.v0.fromArray( json.v0 );
- this.v1.fromArray( json.v1 );
- this.v2.fromArray( json.v2 );
- this.v3.fromArray( json.v3 );
- return this;
- };
- function CubicBezierCurve3( v0 = new Vector3(), v1 = new Vector3(), v2 = new Vector3(), v3 = new Vector3() ) {
- Curve.call( this );
- this.type = 'CubicBezierCurve3';
- this.v0 = v0;
- this.v1 = v1;
- this.v2 = v2;
- this.v3 = v3;
- }
- CubicBezierCurve3.prototype = Object.create( Curve.prototype );
- CubicBezierCurve3.prototype.constructor = CubicBezierCurve3;
- CubicBezierCurve3.prototype.isCubicBezierCurve3 = true;
- CubicBezierCurve3.prototype.getPoint = function ( t, optionalTarget = new Vector3() ) {
- const point = optionalTarget;
- const v0 = this.v0, v1 = this.v1, v2 = this.v2, v3 = this.v3;
- point.set(
- CubicBezier( t, v0.x, v1.x, v2.x, v3.x ),
- CubicBezier( t, v0.y, v1.y, v2.y, v3.y ),
- CubicBezier( t, v0.z, v1.z, v2.z, v3.z )
- );
- return point;
- };
- CubicBezierCurve3.prototype.copy = function ( source ) {
- Curve.prototype.copy.call( this, source );
- this.v0.copy( source.v0 );
- this.v1.copy( source.v1 );
- this.v2.copy( source.v2 );
- this.v3.copy( source.v3 );
- return this;
- };
- CubicBezierCurve3.prototype.toJSON = function () {
- const data = Curve.prototype.toJSON.call( this );
- data.v0 = this.v0.toArray();
- data.v1 = this.v1.toArray();
- data.v2 = this.v2.toArray();
- data.v3 = this.v3.toArray();
- return data;
- };
- CubicBezierCurve3.prototype.fromJSON = function ( json ) {
- Curve.prototype.fromJSON.call( this, json );
- this.v0.fromArray( json.v0 );
- this.v1.fromArray( json.v1 );
- this.v2.fromArray( json.v2 );
- this.v3.fromArray( json.v3 );
- return this;
- };
- function LineCurve( v1 = new Vector2$1(), v2 = new Vector2$1() ) {
- Curve.call( this );
- this.type = 'LineCurve';
- this.v1 = v1;
- this.v2 = v2;
- }
- LineCurve.prototype = Object.create( Curve.prototype );
- LineCurve.prototype.constructor = LineCurve;
- LineCurve.prototype.isLineCurve = true;
- LineCurve.prototype.getPoint = function ( t, optionalTarget = new Vector2$1() ) {
- const point = optionalTarget;
- if ( t === 1 ) {
- point.copy( this.v2 );
- } else {
- point.copy( this.v2 ).sub( this.v1 );
- point.multiplyScalar( t ).add( this.v1 );
- }
- return point;
- };
- // Line curve is linear, so we can overwrite default getPointAt
- LineCurve.prototype.getPointAt = function ( u, optionalTarget ) {
- return this.getPoint( u, optionalTarget );
- };
- LineCurve.prototype.getTangent = function ( t, optionalTarget ) {
- const tangent = optionalTarget || new Vector2$1();
- tangent.copy( this.v2 ).sub( this.v1 ).normalize();
- return tangent;
- };
- LineCurve.prototype.copy = function ( source ) {
- Curve.prototype.copy.call( this, source );
- this.v1.copy( source.v1 );
- this.v2.copy( source.v2 );
- return this;
- };
- LineCurve.prototype.toJSON = function () {
- const data = Curve.prototype.toJSON.call( this );
- data.v1 = this.v1.toArray();
- data.v2 = this.v2.toArray();
- return data;
- };
- LineCurve.prototype.fromJSON = function ( json ) {
- Curve.prototype.fromJSON.call( this, json );
- this.v1.fromArray( json.v1 );
- this.v2.fromArray( json.v2 );
- return this;
- };
- function LineCurve3( v1 = new Vector3(), v2 = new Vector3() ) {
- Curve.call( this );
- this.type = 'LineCurve3';
- this.v1 = v1;
- this.v2 = v2;
- }
- LineCurve3.prototype = Object.create( Curve.prototype );
- LineCurve3.prototype.constructor = LineCurve3;
- LineCurve3.prototype.isLineCurve3 = true;
- LineCurve3.prototype.getPoint = function ( t, optionalTarget = new Vector3() ) {
- const point = optionalTarget;
- if ( t === 1 ) {
- point.copy( this.v2 );
- } else {
- point.copy( this.v2 ).sub( this.v1 );
- point.multiplyScalar( t ).add( this.v1 );
- }
- return point;
- };
- // Line curve is linear, so we can overwrite default getPointAt
- LineCurve3.prototype.getPointAt = function ( u, optionalTarget ) {
- return this.getPoint( u, optionalTarget );
- };
- LineCurve3.prototype.copy = function ( source ) {
- Curve.prototype.copy.call( this, source );
- this.v1.copy( source.v1 );
- this.v2.copy( source.v2 );
- return this;
- };
- LineCurve3.prototype.toJSON = function () {
- const data = Curve.prototype.toJSON.call( this );
- data.v1 = this.v1.toArray();
- data.v2 = this.v2.toArray();
- return data;
- };
- LineCurve3.prototype.fromJSON = function ( json ) {
- Curve.prototype.fromJSON.call( this, json );
- this.v1.fromArray( json.v1 );
- this.v2.fromArray( json.v2 );
- return this;
- };
- function QuadraticBezierCurve( v0 = new Vector2$1(), v1 = new Vector2$1(), v2 = new Vector2$1() ) {
- Curve.call( this );
- this.type = 'QuadraticBezierCurve';
- this.v0 = v0;
- this.v1 = v1;
- this.v2 = v2;
- }
- QuadraticBezierCurve.prototype = Object.create( Curve.prototype );
- QuadraticBezierCurve.prototype.constructor = QuadraticBezierCurve;
- QuadraticBezierCurve.prototype.isQuadraticBezierCurve = true;
- QuadraticBezierCurve.prototype.getPoint = function ( t, optionalTarget = new Vector2$1() ) {
- const point = optionalTarget;
- const v0 = this.v0, v1 = this.v1, v2 = this.v2;
- point.set(
- QuadraticBezier( t, v0.x, v1.x, v2.x ),
- QuadraticBezier( t, v0.y, v1.y, v2.y )
- );
- return point;
- };
- QuadraticBezierCurve.prototype.copy = function ( source ) {
- Curve.prototype.copy.call( this, source );
- this.v0.copy( source.v0 );
- this.v1.copy( source.v1 );
- this.v2.copy( source.v2 );
- return this;
- };
- QuadraticBezierCurve.prototype.toJSON = function () {
- const data = Curve.prototype.toJSON.call( this );
- data.v0 = this.v0.toArray();
- data.v1 = this.v1.toArray();
- data.v2 = this.v2.toArray();
- return data;
- };
- QuadraticBezierCurve.prototype.fromJSON = function ( json ) {
- Curve.prototype.fromJSON.call( this, json );
- this.v0.fromArray( json.v0 );
- this.v1.fromArray( json.v1 );
- this.v2.fromArray( json.v2 );
- return this;
- };
- function QuadraticBezierCurve3( v0 = new Vector3(), v1 = new Vector3(), v2 = new Vector3() ) {
- Curve.call( this );
- this.type = 'QuadraticBezierCurve3';
- this.v0 = v0;
- this.v1 = v1;
- this.v2 = v2;
- }
- QuadraticBezierCurve3.prototype = Object.create( Curve.prototype );
- QuadraticBezierCurve3.prototype.constructor = QuadraticBezierCurve3;
- QuadraticBezierCurve3.prototype.isQuadraticBezierCurve3 = true;
- QuadraticBezierCurve3.prototype.getPoint = function ( t, optionalTarget = new Vector3() ) {
- const point = optionalTarget;
- const v0 = this.v0, v1 = this.v1, v2 = this.v2;
- point.set(
- QuadraticBezier( t, v0.x, v1.x, v2.x ),
- QuadraticBezier( t, v0.y, v1.y, v2.y ),
- QuadraticBezier( t, v0.z, v1.z, v2.z )
- );
- return point;
- };
- QuadraticBezierCurve3.prototype.copy = function ( source ) {
- Curve.prototype.copy.call( this, source );
- this.v0.copy( source.v0 );
- this.v1.copy( source.v1 );
- this.v2.copy( source.v2 );
- return this;
- };
- QuadraticBezierCurve3.prototype.toJSON = function () {
- const data = Curve.prototype.toJSON.call( this );
- data.v0 = this.v0.toArray();
- data.v1 = this.v1.toArray();
- data.v2 = this.v2.toArray();
- return data;
- };
- QuadraticBezierCurve3.prototype.fromJSON = function ( json ) {
- Curve.prototype.fromJSON.call( this, json );
- this.v0.fromArray( json.v0 );
- this.v1.fromArray( json.v1 );
- this.v2.fromArray( json.v2 );
- return this;
- };
- function SplineCurve( points = [] ) {
- Curve.call( this );
- this.type = 'SplineCurve';
- this.points = points;
- }
- SplineCurve.prototype = Object.create( Curve.prototype );
- SplineCurve.prototype.constructor = SplineCurve;
- SplineCurve.prototype.isSplineCurve = true;
- SplineCurve.prototype.getPoint = function ( t, optionalTarget = new Vector2$1() ) {
- const point = optionalTarget;
- const points = this.points;
- const p = ( points.length - 1 ) * t;
- const intPoint = Math.floor( p );
- const weight = p - intPoint;
- const p0 = points[ intPoint === 0 ? intPoint : intPoint - 1 ];
- const p1 = points[ intPoint ];
- const p2 = points[ intPoint > points.length - 2 ? points.length - 1 : intPoint + 1 ];
- const p3 = points[ intPoint > points.length - 3 ? points.length - 1 : intPoint + 2 ];
- point.set(
- CatmullRom( weight, p0.x, p1.x, p2.x, p3.x ),
- CatmullRom( weight, p0.y, p1.y, p2.y, p3.y )
- );
- return point;
- };
- SplineCurve.prototype.copy = function ( source ) {
- Curve.prototype.copy.call( this, source );
- this.points = [];
- for ( let i = 0, l = source.points.length; i < l; i ++ ) {
- const point = source.points[ i ];
- this.points.push( point.clone() );
- }
- return this;
- };
- SplineCurve.prototype.toJSON = function () {
- const data = Curve.prototype.toJSON.call( this );
- data.points = [];
- for ( let i = 0, l = this.points.length; i < l; i ++ ) {
- const point = this.points[ i ];
- data.points.push( point.toArray() );
- }
- return data;
- };
- SplineCurve.prototype.fromJSON = function ( json ) {
- Curve.prototype.fromJSON.call( this, json );
- this.points = [];
- for ( let i = 0, l = json.points.length; i < l; i ++ ) {
- const point = json.points[ i ];
- this.points.push( new Vector2$1().fromArray( point ) );
- }
- return this;
- };
- var Curves = /*#__PURE__*/Object.freeze({
- __proto__: null,
- ArcCurve: ArcCurve,
- CatmullRomCurve3: CatmullRomCurve3,
- CubicBezierCurve: CubicBezierCurve,
- CubicBezierCurve3: CubicBezierCurve3,
- EllipseCurve: EllipseCurve,
- LineCurve: LineCurve,
- LineCurve3: LineCurve3,
- QuadraticBezierCurve: QuadraticBezierCurve,
- QuadraticBezierCurve3: QuadraticBezierCurve3,
- SplineCurve: SplineCurve
- });
- /**************************************************************
- * Curved Path - a curve path is simply a array of connected
- * curves, but retains the api of a curve
- **************************************************************/
- function CurvePath() {
- Curve.call( this );
- this.type = 'CurvePath';
- this.curves = [];
- this.autoClose = false; // Automatically closes the path
- }
- CurvePath.prototype = Object.assign( Object.create( Curve.prototype ), {
- constructor: CurvePath,
- add: function ( curve ) {
- this.curves.push( curve );
- },
- closePath: function () {
- // Add a line curve if start and end of lines are not connected
- const startPoint = this.curves[ 0 ].getPoint( 0 );
- const endPoint = this.curves[ this.curves.length - 1 ].getPoint( 1 );
- if ( ! startPoint.equals( endPoint ) ) {
- this.curves.push( new LineCurve( endPoint, startPoint ) );
- }
- },
- // To get accurate point with reference to
- // entire path distance at time t,
- // following has to be done:
- // 1. Length of each sub path have to be known
- // 2. Locate and identify type of curve
- // 3. Get t for the curve
- // 4. Return curve.getPointAt(t')
- getPoint: function ( t ) {
- const d = t * this.getLength();
- const curveLengths = this.getCurveLengths();
- let i = 0;
- // To think about boundaries points.
- while ( i < curveLengths.length ) {
- if ( curveLengths[ i ] >= d ) {
- const diff = curveLengths[ i ] - d;
- const curve = this.curves[ i ];
- const segmentLength = curve.getLength();
- const u = segmentLength === 0 ? 0 : 1 - diff / segmentLength;
- return curve.getPointAt( u );
- }
- i ++;
- }
- return null;
- // loop where sum != 0, sum > d , sum+1 <d
- },
- // We cannot use the default THREE.Curve getPoint() with getLength() because in
- // THREE.Curve, getLength() depends on getPoint() but in THREE.CurvePath
- // getPoint() depends on getLength
- getLength: function () {
- const lens = this.getCurveLengths();
- return lens[ lens.length - 1 ];
- },
- // cacheLengths must be recalculated.
- updateArcLengths: function () {
- this.needsUpdate = true;
- this.cacheLengths = null;
- this.getCurveLengths();
- },
- // Compute lengths and cache them
- // We cannot overwrite getLengths() because UtoT mapping uses it.
- getCurveLengths: function () {
- // We use cache values if curves and cache array are same length
- if ( this.cacheLengths && this.cacheLengths.length === this.curves.length ) {
- return this.cacheLengths;
- }
- // Get length of sub-curve
- // Push sums into cached array
- const lengths = [];
- let sums = 0;
- for ( let i = 0, l = this.curves.length; i < l; i ++ ) {
- sums += this.curves[ i ].getLength();
- lengths.push( sums );
- }
- this.cacheLengths = lengths;
- return lengths;
- },
- getSpacedPoints: function ( divisions = 40 ) {
- const points = [];
- for ( let i = 0; i <= divisions; i ++ ) {
- points.push( this.getPoint( i / divisions ) );
- }
- if ( this.autoClose ) {
- points.push( points[ 0 ] );
- }
- return points;
- },
- getPoints: function ( divisions = 12 ) {
- const points = [];
- let last;
- for ( let i = 0, curves = this.curves; i < curves.length; i ++ ) {
- const curve = curves[ i ];
- const resolution = ( curve && curve.isEllipseCurve ) ? divisions * 2
- : ( curve && ( curve.isLineCurve || curve.isLineCurve3 ) ) ? 1
- : ( curve && curve.isSplineCurve ) ? divisions * curve.points.length
- : divisions;
- const pts = curve.getPoints( resolution );
- for ( let j = 0; j < pts.length; j ++ ) {
- const point = pts[ j ];
- if ( last && last.equals( point ) ) continue; // ensures no consecutive points are duplicates
- points.push( point );
- last = point;
- }
- }
- if ( this.autoClose && points.length > 1 && ! points[ points.length - 1 ].equals( points[ 0 ] ) ) {
- points.push( points[ 0 ] );
- }
- return points;
- },
- copy: function ( source ) {
- Curve.prototype.copy.call( this, source );
- this.curves = [];
- for ( let i = 0, l = source.curves.length; i < l; i ++ ) {
- const curve = source.curves[ i ];
- this.curves.push( curve.clone() );
- }
- this.autoClose = source.autoClose;
- return this;
- },
- toJSON: function () {
- const data = Curve.prototype.toJSON.call( this );
- data.autoClose = this.autoClose;
- data.curves = [];
- for ( let i = 0, l = this.curves.length; i < l; i ++ ) {
- const curve = this.curves[ i ];
- data.curves.push( curve.toJSON() );
- }
- return data;
- },
- fromJSON: function ( json ) {
- Curve.prototype.fromJSON.call( this, json );
- this.autoClose = json.autoClose;
- this.curves = [];
- for ( let i = 0, l = json.curves.length; i < l; i ++ ) {
- const curve = json.curves[ i ];
- this.curves.push( new Curves[ curve.type ]().fromJSON( curve ) );
- }
- return this;
- }
- } );
- function Path( points ) {
- CurvePath.call( this );
- this.type = 'Path';
- this.currentPoint = new Vector2$1();
- if ( points ) {
- this.setFromPoints( points );
- }
- }
- Path.prototype = Object.assign( Object.create( CurvePath.prototype ), {
- constructor: Path,
- setFromPoints: function ( points ) {
- this.moveTo( points[ 0 ].x, points[ 0 ].y );
- for ( let i = 1, l = points.length; i < l; i ++ ) {
- this.lineTo( points[ i ].x, points[ i ].y );
- }
- return this;
- },
- moveTo: function ( x, y ) {
- this.currentPoint.set( x, y ); // TODO consider referencing vectors instead of copying?
- return this;
- },
- lineTo: function ( x, y ) {
- const curve = new LineCurve( this.currentPoint.clone(), new Vector2$1( x, y ) );
- this.curves.push( curve );
- this.currentPoint.set( x, y );
- return this;
- },
- quadraticCurveTo: function ( aCPx, aCPy, aX, aY ) {
- const curve = new QuadraticBezierCurve(
- this.currentPoint.clone(),
- new Vector2$1( aCPx, aCPy ),
- new Vector2$1( aX, aY )
- );
- this.curves.push( curve );
- this.currentPoint.set( aX, aY );
- return this;
- },
- bezierCurveTo: function ( aCP1x, aCP1y, aCP2x, aCP2y, aX, aY ) {
- const curve = new CubicBezierCurve(
- this.currentPoint.clone(),
- new Vector2$1( aCP1x, aCP1y ),
- new Vector2$1( aCP2x, aCP2y ),
- new Vector2$1( aX, aY )
- );
- this.curves.push( curve );
- this.currentPoint.set( aX, aY );
- return this;
- },
- splineThru: function ( pts /*Array of Vector*/ ) {
- const npts = [ this.currentPoint.clone() ].concat( pts );
- const curve = new SplineCurve( npts );
- this.curves.push( curve );
- this.currentPoint.copy( pts[ pts.length - 1 ] );
- return this;
- },
- arc: function ( aX, aY, aRadius, aStartAngle, aEndAngle, aClockwise ) {
- const x0 = this.currentPoint.x;
- const y0 = this.currentPoint.y;
- this.absarc( aX + x0, aY + y0, aRadius,
- aStartAngle, aEndAngle, aClockwise );
- return this;
- },
- absarc: function ( aX, aY, aRadius, aStartAngle, aEndAngle, aClockwise ) {
- this.absellipse( aX, aY, aRadius, aRadius, aStartAngle, aEndAngle, aClockwise );
- return this;
- },
- ellipse: function ( aX, aY, xRadius, yRadius, aStartAngle, aEndAngle, aClockwise, aRotation ) {
- const x0 = this.currentPoint.x;
- const y0 = this.currentPoint.y;
- this.absellipse( aX + x0, aY + y0, xRadius, yRadius, aStartAngle, aEndAngle, aClockwise, aRotation );
- return this;
- },
- absellipse: function ( aX, aY, xRadius, yRadius, aStartAngle, aEndAngle, aClockwise, aRotation ) {
- const curve = new EllipseCurve( aX, aY, xRadius, yRadius, aStartAngle, aEndAngle, aClockwise, aRotation );
- if ( this.curves.length > 0 ) {
- // if a previous curve is present, attempt to join
- const firstPoint = curve.getPoint( 0 );
- if ( ! firstPoint.equals( this.currentPoint ) ) {
- this.lineTo( firstPoint.x, firstPoint.y );
- }
- }
- this.curves.push( curve );
- const lastPoint = curve.getPoint( 1 );
- this.currentPoint.copy( lastPoint );
- return this;
- },
- copy: function ( source ) {
- CurvePath.prototype.copy.call( this, source );
- this.currentPoint.copy( source.currentPoint );
- return this;
- },
- toJSON: function () {
- const data = CurvePath.prototype.toJSON.call( this );
- data.currentPoint = this.currentPoint.toArray();
- return data;
- },
- fromJSON: function ( json ) {
- CurvePath.prototype.fromJSON.call( this, json );
- this.currentPoint.fromArray( json.currentPoint );
- return this;
- }
- } );
- function Shape( points ) {
- Path.call( this, points );
- this.uuid = MathUtils.generateUUID();
- this.type = 'Shape';
- this.holes = [];
- }
- Shape.prototype = Object.assign( Object.create( Path.prototype ), {
- constructor: Shape,
- getPointsHoles: function ( divisions ) {
- const holesPts = [];
- for ( let i = 0, l = this.holes.length; i < l; i ++ ) {
- holesPts[ i ] = this.holes[ i ].getPoints( divisions );
- }
- return holesPts;
- },
- // get points of shape and holes (keypoints based on segments parameter)
- extractPoints: function ( divisions ) {
- return {
- shape: this.getPoints( divisions ),
- holes: this.getPointsHoles( divisions )
- };
- },
- copy: function ( source ) {
- Path.prototype.copy.call( this, source );
- this.holes = [];
- for ( let i = 0, l = source.holes.length; i < l; i ++ ) {
- const hole = source.holes[ i ];
- this.holes.push( hole.clone() );
- }
- return this;
- },
- toJSON: function () {
- const data = Path.prototype.toJSON.call( this );
- data.uuid = this.uuid;
- data.holes = [];
- for ( let i = 0, l = this.holes.length; i < l; i ++ ) {
- const hole = this.holes[ i ];
- data.holes.push( hole.toJSON() );
- }
- return data;
- },
- fromJSON: function ( json ) {
- Path.prototype.fromJSON.call( this, json );
- this.uuid = json.uuid;
- this.holes = [];
- for ( let i = 0, l = json.holes.length; i < l; i ++ ) {
- const hole = json.holes[ i ];
- this.holes.push( new Path().fromJSON( hole ) );
- }
- return this;
- }
- } );
- function Light( color, intensity = 1 ) {
- Object3D.call( this );
- this.type = 'Light';
- this.color = new Color( color );
- this.intensity = intensity;
- }
- Light.prototype = Object.assign( Object.create( Object3D.prototype ), {
- constructor: Light,
- isLight: true,
- copy: function ( source ) {
- Object3D.prototype.copy.call( this, source );
- this.color.copy( source.color );
- this.intensity = source.intensity;
- return this;
- },
- toJSON: function ( meta ) {
- const data = Object3D.prototype.toJSON.call( this, meta );
- data.object.color = this.color.getHex();
- data.object.intensity = this.intensity;
- if ( this.groundColor !== undefined ) data.object.groundColor = this.groundColor.getHex();
- if ( this.distance !== undefined ) data.object.distance = this.distance;
- if ( this.angle !== undefined ) data.object.angle = this.angle;
- if ( this.decay !== undefined ) data.object.decay = this.decay;
- if ( this.penumbra !== undefined ) data.object.penumbra = this.penumbra;
- if ( this.shadow !== undefined ) data.object.shadow = this.shadow.toJSON();
- return data;
- }
- } );
- function HemisphereLight( skyColor, groundColor, intensity ) {
- Light.call( this, skyColor, intensity );
- this.type = 'HemisphereLight';
- this.position.copy( Object3D.DefaultUp );
- this.updateMatrix();
- this.groundColor = new Color( groundColor );
- }
- HemisphereLight.prototype = Object.assign( Object.create( Light.prototype ), {
- constructor: HemisphereLight,
- isHemisphereLight: true,
- copy: function ( source ) {
- Light.prototype.copy.call( this, source );
- this.groundColor.copy( source.groundColor );
- return this;
- }
- } );
- function LightShadow( camera ) {
- this.camera = camera;
- this.bias = 0;
- this.normalBias = 0;
- this.radius = 1;
- this.mapSize = new Vector2$1( 512, 512 );
- this.map = null;
- this.mapPass = null;
- this.matrix = new Matrix4();
- this.autoUpdate = true;
- this.needsUpdate = false;
- this._frustum = new Frustum();
- this._frameExtents = new Vector2$1( 1, 1 );
- this._viewportCount = 1;
- this._viewports = [
- new Vector4( 0, 0, 1, 1 )
- ];
- }
- Object.assign( LightShadow.prototype, {
- _projScreenMatrix: new Matrix4(),
- _lightPositionWorld: new Vector3(),
- _lookTarget: new Vector3(),
- getViewportCount: function () {
- return this._viewportCount;
- },
- getFrustum: function () {
- return this._frustum;
- },
- updateMatrices: function ( light ) {
- const shadowCamera = this.camera,
- shadowMatrix = this.matrix,
- projScreenMatrix = this._projScreenMatrix,
- lookTarget = this._lookTarget,
- lightPositionWorld = this._lightPositionWorld;
- lightPositionWorld.setFromMatrixPosition( light.matrixWorld );
- shadowCamera.position.copy( lightPositionWorld );
- lookTarget.setFromMatrixPosition( light.target.matrixWorld );
- shadowCamera.lookAt( lookTarget );
- shadowCamera.updateMatrixWorld();
- projScreenMatrix.multiplyMatrices( shadowCamera.projectionMatrix, shadowCamera.matrixWorldInverse );
- this._frustum.setFromProjectionMatrix( projScreenMatrix );
- shadowMatrix.set(
- 0.5, 0.0, 0.0, 0.5,
- 0.0, 0.5, 0.0, 0.5,
- 0.0, 0.0, 0.5, 0.5,
- 0.0, 0.0, 0.0, 1.0
- );
- shadowMatrix.multiply( shadowCamera.projectionMatrix );
- shadowMatrix.multiply( shadowCamera.matrixWorldInverse );
- },
- getViewport: function ( viewportIndex ) {
- return this._viewports[ viewportIndex ];
- },
- getFrameExtents: function () {
- return this._frameExtents;
- },
- copy: function ( source ) {
- this.camera = source.camera.clone();
- this.bias = source.bias;
- this.radius = source.radius;
- this.mapSize.copy( source.mapSize );
- return this;
- },
- clone: function () {
- return new this.constructor().copy( this );
- },
- toJSON: function () {
- const object = {};
- if ( this.bias !== 0 ) object.bias = this.bias;
- if ( this.normalBias !== 0 ) object.normalBias = this.normalBias;
- if ( this.radius !== 1 ) object.radius = this.radius;
- if ( this.mapSize.x !== 512 || this.mapSize.y !== 512 ) object.mapSize = this.mapSize.toArray();
- object.camera = this.camera.toJSON( false ).object;
- delete object.camera.matrix;
- return object;
- }
- } );
- function SpotLightShadow() {
- LightShadow.call( this, new PerspectiveCamera( 50, 1, 0.5, 500 ) );
- this.focus = 1;
- }
- SpotLightShadow.prototype = Object.assign( Object.create( LightShadow.prototype ), {
- constructor: SpotLightShadow,
- isSpotLightShadow: true,
- updateMatrices: function ( light ) {
- const camera = this.camera;
- const fov = MathUtils.RAD2DEG * 2 * light.angle * this.focus;
- const aspect = this.mapSize.width / this.mapSize.height;
- const far = light.distance || camera.far;
- if ( fov !== camera.fov || aspect !== camera.aspect || far !== camera.far ) {
- camera.fov = fov;
- camera.aspect = aspect;
- camera.far = far;
- camera.updateProjectionMatrix();
- }
- LightShadow.prototype.updateMatrices.call( this, light );
- }
- } );
- function SpotLight( color, intensity, distance, angle, penumbra, decay ) {
- Light.call( this, color, intensity );
- this.type = 'SpotLight';
- this.position.copy( Object3D.DefaultUp );
- this.updateMatrix();
- this.target = new Object3D();
- Object.defineProperty( this, 'power', {
- get: function () {
- // intensity = power per solid angle.
- // ref: equation (17) from https://seblagarde.files.wordpress.com/2015/07/course_notes_moving_frostbite_to_pbr_v32.pdf
- return this.intensity * Math.PI;
- },
- set: function ( power ) {
- // intensity = power per solid angle.
- // ref: equation (17) from https://seblagarde.files.wordpress.com/2015/07/course_notes_moving_frostbite_to_pbr_v32.pdf
- this.intensity = power / Math.PI;
- }
- } );
- this.distance = ( distance !== undefined ) ? distance : 0;
- this.angle = ( angle !== undefined ) ? angle : Math.PI / 3;
- this.penumbra = ( penumbra !== undefined ) ? penumbra : 0;
- this.decay = ( decay !== undefined ) ? decay : 1; // for physically correct lights, should be 2.
- this.shadow = new SpotLightShadow();
- }
- SpotLight.prototype = Object.assign( Object.create( Light.prototype ), {
- constructor: SpotLight,
- isSpotLight: true,
- copy: function ( source ) {
- Light.prototype.copy.call( this, source );
- this.distance = source.distance;
- this.angle = source.angle;
- this.penumbra = source.penumbra;
- this.decay = source.decay;
- this.target = source.target.clone();
- this.shadow = source.shadow.clone();
- return this;
- }
- } );
- function PointLightShadow() {
- LightShadow.call( this, new PerspectiveCamera( 90, 1, 0.5, 500 ) );
- this._frameExtents = new Vector2$1( 4, 2 );
- this._viewportCount = 6;
- this._viewports = [
- // These viewports map a cube-map onto a 2D texture with the
- // following orientation:
- //
- // xzXZ
- // y Y
- //
- // X - Positive x direction
- // x - Negative x direction
- // Y - Positive y direction
- // y - Negative y direction
- // Z - Positive z direction
- // z - Negative z direction
- // positive X
- new Vector4( 2, 1, 1, 1 ),
- // negative X
- new Vector4( 0, 1, 1, 1 ),
- // positive Z
- new Vector4( 3, 1, 1, 1 ),
- // negative Z
- new Vector4( 1, 1, 1, 1 ),
- // positive Y
- new Vector4( 3, 0, 1, 1 ),
- // negative Y
- new Vector4( 1, 0, 1, 1 )
- ];
- this._cubeDirections = [
- new Vector3( 1, 0, 0 ), new Vector3( - 1, 0, 0 ), new Vector3( 0, 0, 1 ),
- new Vector3( 0, 0, - 1 ), new Vector3( 0, 1, 0 ), new Vector3( 0, - 1, 0 )
- ];
- this._cubeUps = [
- new Vector3( 0, 1, 0 ), new Vector3( 0, 1, 0 ), new Vector3( 0, 1, 0 ),
- new Vector3( 0, 1, 0 ), new Vector3( 0, 0, 1 ), new Vector3( 0, 0, - 1 )
- ];
- }
- PointLightShadow.prototype = Object.assign( Object.create( LightShadow.prototype ), {
- constructor: PointLightShadow,
- isPointLightShadow: true,
- updateMatrices: function ( light, viewportIndex = 0 ) {
- const camera = this.camera,
- shadowMatrix = this.matrix,
- lightPositionWorld = this._lightPositionWorld,
- lookTarget = this._lookTarget,
- projScreenMatrix = this._projScreenMatrix;
- lightPositionWorld.setFromMatrixPosition( light.matrixWorld );
- camera.position.copy( lightPositionWorld );
- lookTarget.copy( camera.position );
- lookTarget.add( this._cubeDirections[ viewportIndex ] );
- camera.up.copy( this._cubeUps[ viewportIndex ] );
- camera.lookAt( lookTarget );
- camera.updateMatrixWorld();
- shadowMatrix.makeTranslation( - lightPositionWorld.x, - lightPositionWorld.y, - lightPositionWorld.z );
- projScreenMatrix.multiplyMatrices( camera.projectionMatrix, camera.matrixWorldInverse );
- this._frustum.setFromProjectionMatrix( projScreenMatrix );
- }
- } );
- function PointLight( color, intensity, distance, decay ) {
- Light.call( this, color, intensity );
- this.type = 'PointLight';
- Object.defineProperty( this, 'power', {
- get: function () {
- // intensity = power per solid angle.
- // ref: equation (15) from https://seblagarde.files.wordpress.com/2015/07/course_notes_moving_frostbite_to_pbr_v32.pdf
- return this.intensity * 4 * Math.PI;
- },
- set: function ( power ) {
- // intensity = power per solid angle.
- // ref: equation (15) from https://seblagarde.files.wordpress.com/2015/07/course_notes_moving_frostbite_to_pbr_v32.pdf
- this.intensity = power / ( 4 * Math.PI );
- }
- } );
- this.distance = ( distance !== undefined ) ? distance : 0;
- this.decay = ( decay !== undefined ) ? decay : 1; // for physically correct lights, should be 2.
- this.shadow = new PointLightShadow();
- }
- PointLight.prototype = Object.assign( Object.create( Light.prototype ), {
- constructor: PointLight,
- isPointLight: true,
- copy: function ( source ) {
- Light.prototype.copy.call( this, source );
- this.distance = source.distance;
- this.decay = source.decay;
- this.shadow = source.shadow.clone();
- return this;
- }
- } );
- function OrthographicCamera( left = - 1, right = 1, top = 1, bottom = - 1, near = 0.1, far = 2000 ) {
- Camera.call( this );
- this.type = 'OrthographicCamera';
- this.zoom = 1;
- this.view = null;
- this.left = left;
- this.right = right;
- this.top = top;
- this.bottom = bottom;
- this.near = near;
- this.far = far;
- this.updateProjectionMatrix();
- }
- OrthographicCamera.prototype = Object.assign( Object.create( Camera.prototype ), {
- constructor: OrthographicCamera,
- isOrthographicCamera: true,
- copy: function ( source, recursive ) {
- Camera.prototype.copy.call( this, source, recursive );
- this.left = source.left;
- this.right = source.right;
- this.top = source.top;
- this.bottom = source.bottom;
- this.near = source.near;
- this.far = source.far;
- this.zoom = source.zoom;
- this.view = source.view === null ? null : Object.assign( {}, source.view );
- return this;
- },
- setViewOffset: function ( fullWidth, fullHeight, x, y, width, height ) {
- if ( this.view === null ) {
- this.view = {
- enabled: true,
- fullWidth: 1,
- fullHeight: 1,
- offsetX: 0,
- offsetY: 0,
- width: 1,
- height: 1
- };
- }
- this.view.enabled = true;
- this.view.fullWidth = fullWidth;
- this.view.fullHeight = fullHeight;
- this.view.offsetX = x;
- this.view.offsetY = y;
- this.view.width = width;
- this.view.height = height;
- this.updateProjectionMatrix();
- },
- clearViewOffset: function () {
- if ( this.view !== null ) {
- this.view.enabled = false;
- }
- this.updateProjectionMatrix();
- },
- updateProjectionMatrix: function () {
- const dx = ( this.right - this.left ) / ( 2 * this.zoom );
- const dy = ( this.top - this.bottom ) / ( 2 * this.zoom );
- const cx = ( this.right + this.left ) / 2;
- const cy = ( this.top + this.bottom ) / 2;
- let left = cx - dx;
- let right = cx + dx;
- let top = cy + dy;
- let bottom = cy - dy;
- if ( this.view !== null && this.view.enabled ) {
- const scaleW = ( this.right - this.left ) / this.view.fullWidth / this.zoom;
- const scaleH = ( this.top - this.bottom ) / this.view.fullHeight / this.zoom;
- left += scaleW * this.view.offsetX;
- right = left + scaleW * this.view.width;
- top -= scaleH * this.view.offsetY;
- bottom = top - scaleH * this.view.height;
- }
- this.projectionMatrix.makeOrthographic( left, right, top, bottom, this.near, this.far );
- this.projectionMatrixInverse.copy( this.projectionMatrix ).invert();
- },
- toJSON: function ( meta ) {
- const data = Object3D.prototype.toJSON.call( this, meta );
- data.object.zoom = this.zoom;
- data.object.left = this.left;
- data.object.right = this.right;
- data.object.top = this.top;
- data.object.bottom = this.bottom;
- data.object.near = this.near;
- data.object.far = this.far;
- if ( this.view !== null ) data.object.view = Object.assign( {}, this.view );
- return data;
- }
- } );
- function DirectionalLightShadow() {
- LightShadow.call( this, new OrthographicCamera( - 5, 5, 5, - 5, 0.5, 500 ) );
- }
- DirectionalLightShadow.prototype = Object.assign( Object.create( LightShadow.prototype ), {
- constructor: DirectionalLightShadow,
- isDirectionalLightShadow: true,
- updateMatrices: function ( light ) {
- LightShadow.prototype.updateMatrices.call( this, light );
- }
- } );
- function DirectionalLight( color, intensity ) {
- Light.call( this, color, intensity );
- this.type = 'DirectionalLight';
- this.position.copy( Object3D.DefaultUp );
- this.updateMatrix();
- this.target = new Object3D();
- this.shadow = new DirectionalLightShadow();
- }
- DirectionalLight.prototype = Object.assign( Object.create( Light.prototype ), {
- constructor: DirectionalLight,
- isDirectionalLight: true,
- copy: function ( source ) {
- Light.prototype.copy.call( this, source );
- this.target = source.target.clone();
- this.shadow = source.shadow.clone();
- return this;
- }
- } );
- function AmbientLight( color, intensity ) {
- Light.call( this, color, intensity );
- this.type = 'AmbientLight';
- }
- AmbientLight.prototype = Object.assign( Object.create( Light.prototype ), {
- constructor: AmbientLight,
- isAmbientLight: true
- } );
- function RectAreaLight( color, intensity, width, height ) {
- Light.call( this, color, intensity );
- this.type = 'RectAreaLight';
- this.width = ( width !== undefined ) ? width : 10;
- this.height = ( height !== undefined ) ? height : 10;
- }
- RectAreaLight.prototype = Object.assign( Object.create( Light.prototype ), {
- constructor: RectAreaLight,
- isRectAreaLight: true,
- copy: function ( source ) {
- Light.prototype.copy.call( this, source );
- this.width = source.width;
- this.height = source.height;
- return this;
- },
- toJSON: function ( meta ) {
- const data = Light.prototype.toJSON.call( this, meta );
- data.object.width = this.width;
- data.object.height = this.height;
- return data;
- }
- } );
- /**
- * Primary reference:
- * https://graphics.stanford.edu/papers/envmap/envmap.pdf
- *
- * Secondary reference:
- * https://www.ppsloan.org/publications/StupidSH36.pdf
- */
- // 3-band SH defined by 9 coefficients
- class SphericalHarmonics3 {
- constructor() {
- Object.defineProperty( this, 'isSphericalHarmonics3', { value: true } );
- this.coefficients = [];
- for ( let i = 0; i < 9; i ++ ) {
- this.coefficients.push( new Vector3() );
- }
- }
- set( coefficients ) {
- for ( let i = 0; i < 9; i ++ ) {
- this.coefficients[ i ].copy( coefficients[ i ] );
- }
- return this;
- }
- zero() {
- for ( let i = 0; i < 9; i ++ ) {
- this.coefficients[ i ].set( 0, 0, 0 );
- }
- return this;
- }
- // get the radiance in the direction of the normal
- // target is a Vector3
- getAt( normal, target ) {
- // normal is assumed to be unit length
- const x = normal.x, y = normal.y, z = normal.z;
- const coeff = this.coefficients;
- // band 0
- target.copy( coeff[ 0 ] ).multiplyScalar( 0.282095 );
- // band 1
- target.addScaledVector( coeff[ 1 ], 0.488603 * y );
- target.addScaledVector( coeff[ 2 ], 0.488603 * z );
- target.addScaledVector( coeff[ 3 ], 0.488603 * x );
- // band 2
- target.addScaledVector( coeff[ 4 ], 1.092548 * ( x * y ) );
- target.addScaledVector( coeff[ 5 ], 1.092548 * ( y * z ) );
- target.addScaledVector( coeff[ 6 ], 0.315392 * ( 3.0 * z * z - 1.0 ) );
- target.addScaledVector( coeff[ 7 ], 1.092548 * ( x * z ) );
- target.addScaledVector( coeff[ 8 ], 0.546274 * ( x * x - y * y ) );
- return target;
- }
- // get the irradiance (radiance convolved with cosine lobe) in the direction of the normal
- // target is a Vector3
- // https://graphics.stanford.edu/papers/envmap/envmap.pdf
- getIrradianceAt( normal, target ) {
- // normal is assumed to be unit length
- const x = normal.x, y = normal.y, z = normal.z;
- const coeff = this.coefficients;
- // band 0
- target.copy( coeff[ 0 ] ).multiplyScalar( 0.886227 ); // π * 0.282095
- // band 1
- target.addScaledVector( coeff[ 1 ], 2.0 * 0.511664 * y ); // ( 2 * π / 3 ) * 0.488603
- target.addScaledVector( coeff[ 2 ], 2.0 * 0.511664 * z );
- target.addScaledVector( coeff[ 3 ], 2.0 * 0.511664 * x );
- // band 2
- target.addScaledVector( coeff[ 4 ], 2.0 * 0.429043 * x * y ); // ( π / 4 ) * 1.092548
- target.addScaledVector( coeff[ 5 ], 2.0 * 0.429043 * y * z );
- target.addScaledVector( coeff[ 6 ], 0.743125 * z * z - 0.247708 ); // ( π / 4 ) * 0.315392 * 3
- target.addScaledVector( coeff[ 7 ], 2.0 * 0.429043 * x * z );
- target.addScaledVector( coeff[ 8 ], 0.429043 * ( x * x - y * y ) ); // ( π / 4 ) * 0.546274
- return target;
- }
- add( sh ) {
- for ( let i = 0; i < 9; i ++ ) {
- this.coefficients[ i ].add( sh.coefficients[ i ] );
- }
- return this;
- }
- addScaledSH( sh, s ) {
- for ( let i = 0; i < 9; i ++ ) {
- this.coefficients[ i ].addScaledVector( sh.coefficients[ i ], s );
- }
- return this;
- }
- scale( s ) {
- for ( let i = 0; i < 9; i ++ ) {
- this.coefficients[ i ].multiplyScalar( s );
- }
- return this;
- }
- lerp( sh, alpha ) {
- for ( let i = 0; i < 9; i ++ ) {
- this.coefficients[ i ].lerp( sh.coefficients[ i ], alpha );
- }
- return this;
- }
- equals( sh ) {
- for ( let i = 0; i < 9; i ++ ) {
- if ( ! this.coefficients[ i ].equals( sh.coefficients[ i ] ) ) {
- return false;
- }
- }
- return true;
- }
- copy( sh ) {
- return this.set( sh.coefficients );
- }
- clone() {
- return new this.constructor().copy( this );
- }
- fromArray( array, offset = 0 ) {
- const coefficients = this.coefficients;
- for ( let i = 0; i < 9; i ++ ) {
- coefficients[ i ].fromArray( array, offset + ( i * 3 ) );
- }
- return this;
- }
- toArray( array = [], offset = 0 ) {
- const coefficients = this.coefficients;
- for ( let i = 0; i < 9; i ++ ) {
- coefficients[ i ].toArray( array, offset + ( i * 3 ) );
- }
- return array;
- }
- // evaluate the basis functions
- // shBasis is an Array[ 9 ]
- static getBasisAt( normal, shBasis ) {
- // normal is assumed to be unit length
- const x = normal.x, y = normal.y, z = normal.z;
- // band 0
- shBasis[ 0 ] = 0.282095;
- // band 1
- shBasis[ 1 ] = 0.488603 * y;
- shBasis[ 2 ] = 0.488603 * z;
- shBasis[ 3 ] = 0.488603 * x;
- // band 2
- shBasis[ 4 ] = 1.092548 * x * y;
- shBasis[ 5 ] = 1.092548 * y * z;
- shBasis[ 6 ] = 0.315392 * ( 3 * z * z - 1 );
- shBasis[ 7 ] = 1.092548 * x * z;
- shBasis[ 8 ] = 0.546274 * ( x * x - y * y );
- }
- }
- function LightProbe( sh, intensity ) {
- Light.call( this, undefined, intensity );
- this.type = 'LightProbe';
- this.sh = ( sh !== undefined ) ? sh : new SphericalHarmonics3();
- }
- LightProbe.prototype = Object.assign( Object.create( Light.prototype ), {
- constructor: LightProbe,
- isLightProbe: true,
- copy: function ( source ) {
- Light.prototype.copy.call( this, source );
- this.sh.copy( source.sh );
- return this;
- },
- fromJSON: function ( json ) {
- this.intensity = json.intensity; // TODO: Move this bit to Light.fromJSON();
- this.sh.fromArray( json.sh );
- return this;
- },
- toJSON: function ( meta ) {
- const data = Light.prototype.toJSON.call( this, meta );
- data.object.sh = this.sh.toArray();
- return data;
- }
- } );
- function MaterialLoader( manager ) {
- Loader.call( this, manager );
- this.textures = {};
- }
- MaterialLoader.prototype = Object.assign( Object.create( Loader.prototype ), {
- constructor: MaterialLoader,
- load: function ( url, onLoad, onProgress, onError ) {
- const scope = this;
- const loader = new FileLoader( scope.manager );
- loader.setPath( scope.path );
- loader.setRequestHeader( scope.requestHeader );
- loader.setWithCredentials( scope.withCredentials );
- loader.load( url, function ( text ) {
- try {
- onLoad( scope.parse( JSON.parse( text ) ) );
- } catch ( e ) {
- if ( onError ) {
- onError( e );
- } else {
- console.error( e );
- }
- scope.manager.itemError( url );
- }
- }, onProgress, onError );
- },
- parse: function ( json ) {
- const textures = this.textures;
- function getTexture( name ) {
- if ( textures[ name ] === undefined ) {
- console.warn( 'THREE.MaterialLoader: Undefined texture', name );
- }
- return textures[ name ];
- }
- const material = new Materials[ json.type ]();
- if ( json.uuid !== undefined ) material.uuid = json.uuid;
- if ( json.name !== undefined ) material.name = json.name;
- if ( json.color !== undefined && material.color !== undefined ) material.color.setHex( json.color );
- if ( json.roughness !== undefined ) material.roughness = json.roughness;
- if ( json.metalness !== undefined ) material.metalness = json.metalness;
- if ( json.sheen !== undefined ) material.sheen = new Color().setHex( json.sheen );
- if ( json.emissive !== undefined && material.emissive !== undefined ) material.emissive.setHex( json.emissive );
- if ( json.specular !== undefined && material.specular !== undefined ) material.specular.setHex( json.specular );
- if ( json.shininess !== undefined ) material.shininess = json.shininess;
- if ( json.clearcoat !== undefined ) material.clearcoat = json.clearcoat;
- if ( json.clearcoatRoughness !== undefined ) material.clearcoatRoughness = json.clearcoatRoughness;
- if ( json.fog !== undefined ) material.fog = json.fog;
- if ( json.flatShading !== undefined ) material.flatShading = json.flatShading;
- if ( json.blending !== undefined ) material.blending = json.blending;
- if ( json.combine !== undefined ) material.combine = json.combine;
- if ( json.side !== undefined ) material.side = json.side;
- if ( json.opacity !== undefined ) material.opacity = json.opacity;
- if ( json.transparent !== undefined ) material.transparent = json.transparent;
- if ( json.alphaTest !== undefined ) material.alphaTest = json.alphaTest;
- if ( json.depthTest !== undefined ) material.depthTest = json.depthTest;
- if ( json.depthWrite !== undefined ) material.depthWrite = json.depthWrite;
- if ( json.colorWrite !== undefined ) material.colorWrite = json.colorWrite;
- if ( json.stencilWrite !== undefined ) material.stencilWrite = json.stencilWrite;
- if ( json.stencilWriteMask !== undefined ) material.stencilWriteMask = json.stencilWriteMask;
- if ( json.stencilFunc !== undefined ) material.stencilFunc = json.stencilFunc;
- if ( json.stencilRef !== undefined ) material.stencilRef = json.stencilRef;
- if ( json.stencilFuncMask !== undefined ) material.stencilFuncMask = json.stencilFuncMask;
- if ( json.stencilFail !== undefined ) material.stencilFail = json.stencilFail;
- if ( json.stencilZFail !== undefined ) material.stencilZFail = json.stencilZFail;
- if ( json.stencilZPass !== undefined ) material.stencilZPass = json.stencilZPass;
- if ( json.wireframe !== undefined ) material.wireframe = json.wireframe;
- if ( json.wireframelineWidth !== undefined ) material.wireframelineWidth = json.wireframelineWidth;
- if ( json.wireframeLinecap !== undefined ) material.wireframeLinecap = json.wireframeLinecap;
- if ( json.wireframeLinejoin !== undefined ) material.wireframeLinejoin = json.wireframeLinejoin;
- if ( json.rotation !== undefined ) material.rotation = json.rotation;
- if ( json.lineWidth !== 1 ) material.lineWidth = json.lineWidth;
- if ( json.dashSize !== undefined ) material.dashSize = json.dashSize;
- if ( json.gapSize !== undefined ) material.gapSize = json.gapSize;
- if ( json.scale !== undefined ) material.scale = json.scale;
- if ( json.polygonOffset !== undefined ) material.polygonOffset = json.polygonOffset;
- if ( json.polygonOffsetFactor !== undefined ) material.polygonOffsetFactor = json.polygonOffsetFactor;
- if ( json.polygonOffsetUnits !== undefined ) material.polygonOffsetUnits = json.polygonOffsetUnits;
- if ( json.skinning !== undefined ) material.skinning = json.skinning;
- if ( json.morphTargets !== undefined ) material.morphTargets = json.morphTargets;
- if ( json.morphNormals !== undefined ) material.morphNormals = json.morphNormals;
- if ( json.dithering !== undefined ) material.dithering = json.dithering;
- if ( json.vertexTangents !== undefined ) material.vertexTangents = json.vertexTangents;
- if ( json.visible !== undefined ) material.visible = json.visible;
- if ( json.toneMapped !== undefined ) material.toneMapped = json.toneMapped;
- if ( json.userData !== undefined ) material.userData = json.userData;
- if ( json.vertexColors !== undefined ) {
- if ( typeof json.vertexColors === 'number' ) {
- material.vertexColors = ( json.vertexColors > 0 ) ? true : false;
- } else {
- material.vertexColors = json.vertexColors;
- }
- }
- // Shader Material
- if ( json.uniforms !== undefined ) {
- for ( const name in json.uniforms ) {
- const uniform = json.uniforms[ name ];
- material.uniforms[ name ] = {};
- switch ( uniform.type ) {
- case 't':
- material.uniforms[ name ].value = getTexture( uniform.value );
- break;
- case 'c':
- material.uniforms[ name ].value = new Color().setHex( uniform.value );
- break;
- case 'v2':
- material.uniforms[ name ].value = new Vector2$1().fromArray( uniform.value );
- break;
- case 'v3':
- material.uniforms[ name ].value = new Vector3().fromArray( uniform.value );
- break;
- case 'v4':
- material.uniforms[ name ].value = new Vector4().fromArray( uniform.value );
- break;
- case 'm3':
- material.uniforms[ name ].value = new Matrix3().fromArray( uniform.value );
- break;
- case 'm4':
- material.uniforms[ name ].value = new Matrix4().fromArray( uniform.value );
- break;
- default:
- material.uniforms[ name ].value = uniform.value;
- }
- }
- }
- if ( json.defines !== undefined ) material.defines = json.defines;
- if ( json.vertexShader !== undefined ) material.vertexShader = json.vertexShader;
- if ( json.fragmentShader !== undefined ) material.fragmentShader = json.fragmentShader;
- if ( json.extensions !== undefined ) {
- for ( const key in json.extensions ) {
- material.extensions[ key ] = json.extensions[ key ];
- }
- }
- // Deprecated
- if ( json.shading !== undefined ) material.flatShading = json.shading === 1; // THREE.FlatShading
- // for PointsMaterial
- if ( json.size !== undefined ) material.size = json.size;
- if ( json.sizeAttenuation !== undefined ) material.sizeAttenuation = json.sizeAttenuation;
- // maps
- if ( json.map !== undefined ) material.map = getTexture( json.map );
- if ( json.matcap !== undefined ) material.matcap = getTexture( json.matcap );
- if ( json.alphaMap !== undefined ) material.alphaMap = getTexture( json.alphaMap );
- if ( json.bumpMap !== undefined ) material.bumpMap = getTexture( json.bumpMap );
- if ( json.bumpScale !== undefined ) material.bumpScale = json.bumpScale;
- if ( json.normalMap !== undefined ) material.normalMap = getTexture( json.normalMap );
- if ( json.normalMapType !== undefined ) material.normalMapType = json.normalMapType;
- if ( json.normalScale !== undefined ) {
- let normalScale = json.normalScale;
- if ( Array.isArray( normalScale ) === false ) {
- // Blender exporter used to export a scalar. See #7459
- normalScale = [ normalScale, normalScale ];
- }
- material.normalScale = new Vector2$1().fromArray( normalScale );
- }
- if ( json.displacementMap !== undefined ) material.displacementMap = getTexture( json.displacementMap );
- if ( json.displacementScale !== undefined ) material.displacementScale = json.displacementScale;
- if ( json.displacementBias !== undefined ) material.displacementBias = json.displacementBias;
- if ( json.roughnessMap !== undefined ) material.roughnessMap = getTexture( json.roughnessMap );
- if ( json.metalnessMap !== undefined ) material.metalnessMap = getTexture( json.metalnessMap );
- if ( json.emissiveMap !== undefined ) material.emissiveMap = getTexture( json.emissiveMap );
- if ( json.emissiveIntensity !== undefined ) material.emissiveIntensity = json.emissiveIntensity;
- if ( json.specularMap !== undefined ) material.specularMap = getTexture( json.specularMap );
- if ( json.envMap !== undefined ) material.envMap = getTexture( json.envMap );
- if ( json.envMapIntensity !== undefined ) material.envMapIntensity = json.envMapIntensity;
- if ( json.reflectivity !== undefined ) material.reflectivity = json.reflectivity;
- if ( json.refractionRatio !== undefined ) material.refractionRatio = json.refractionRatio;
- if ( json.lightMap !== undefined ) material.lightMap = getTexture( json.lightMap );
- if ( json.lightMapIntensity !== undefined ) material.lightMapIntensity = json.lightMapIntensity;
- if ( json.aoMap !== undefined ) material.aoMap = getTexture( json.aoMap );
- if ( json.aoMapIntensity !== undefined ) material.aoMapIntensity = json.aoMapIntensity;
- if ( json.gradientMap !== undefined ) material.gradientMap = getTexture( json.gradientMap );
- if ( json.clearcoatMap !== undefined ) material.clearcoatMap = getTexture( json.clearcoatMap );
- if ( json.clearcoatRoughnessMap !== undefined ) material.clearcoatRoughnessMap = getTexture( json.clearcoatRoughnessMap );
- if ( json.clearcoatNormalMap !== undefined ) material.clearcoatNormalMap = getTexture( json.clearcoatNormalMap );
- if ( json.clearcoatNormalScale !== undefined ) material.clearcoatNormalScale = new Vector2$1().fromArray( json.clearcoatNormalScale );
- if ( json.transmission !== undefined ) material.transmission = json.transmission;
- if ( json.transmissionMap !== undefined ) material.transmissionMap = getTexture( json.transmissionMap );
- return material;
- },
- setTextures: function ( value ) {
- this.textures = value;
- return this;
- }
- } );
- const LoaderUtils = {
- decodeText: function ( array ) {
- if ( typeof TextDecoder !== 'undefined' ) {
- return new TextDecoder().decode( array );
- }
- // Avoid the String.fromCharCode.apply(null, array) shortcut, which
- // throws a "maximum call stack size exceeded" error for large arrays.
- let s = '';
- for ( let i = 0, il = array.length; i < il; i ++ ) {
- // Implicitly assumes little-endian.
- s += String.fromCharCode( array[ i ] );
- }
- try {
- // merges multi-byte utf-8 characters.
- return decodeURIComponent( escape( s ) );
- } catch ( e ) { // see #16358
- return s;
- }
- },
- extractUrlBase: function ( url ) {
- const index = url.lastIndexOf( '/' );
- if ( index === - 1 ) return './';
- return url.substr( 0, index + 1 );
- }
- };
- function InstancedBufferGeometry() {
- BufferGeometry.call( this );
- this.type = 'InstancedBufferGeometry';
- this.instanceCount = Infinity;
- }
- InstancedBufferGeometry.prototype = Object.assign( Object.create( BufferGeometry.prototype ), {
- constructor: InstancedBufferGeometry,
- isInstancedBufferGeometry: true,
- copy: function ( source ) {
- BufferGeometry.prototype.copy.call( this, source );
- this.instanceCount = source.instanceCount;
- return this;
- },
- clone: function () {
- return new this.constructor().copy( this );
- },
- toJSON: function () {
- const data = BufferGeometry.prototype.toJSON.call( this );
- data.instanceCount = this.instanceCount;
- data.isInstancedBufferGeometry = true;
- return data;
- }
- } );
- function InstancedBufferAttribute( array, itemSize, normalized, meshPerAttribute ) {
- if ( typeof ( normalized ) === 'number' ) {
- meshPerAttribute = normalized;
- normalized = false;
- console.error( 'THREE.InstancedBufferAttribute: The constructor now expects normalized as the third argument.' );
- }
- BufferAttribute.call( this, array, itemSize, normalized );
- this.meshPerAttribute = meshPerAttribute || 1;
- }
- InstancedBufferAttribute.prototype = Object.assign( Object.create( BufferAttribute.prototype ), {
- constructor: InstancedBufferAttribute,
- isInstancedBufferAttribute: true,
- copy: function ( source ) {
- BufferAttribute.prototype.copy.call( this, source );
- this.meshPerAttribute = source.meshPerAttribute;
- return this;
- },
- toJSON: function () {
- const data = BufferAttribute.prototype.toJSON.call( this );
- data.meshPerAttribute = this.meshPerAttribute;
- data.isInstancedBufferAttribute = true;
- return data;
- }
- } );
- function BufferGeometryLoader( manager ) {
- Loader.call( this, manager );
- }
- BufferGeometryLoader.prototype = Object.assign( Object.create( Loader.prototype ), {
- constructor: BufferGeometryLoader,
- load: function ( url, onLoad, onProgress, onError ) {
- const scope = this;
- const loader = new FileLoader( scope.manager );
- loader.setPath( scope.path );
- loader.setRequestHeader( scope.requestHeader );
- loader.setWithCredentials( scope.withCredentials );
- loader.load( url, function ( text ) {
- try {
- onLoad( scope.parse( JSON.parse( text ) ) );
- } catch ( e ) {
- if ( onError ) {
- onError( e );
- } else {
- console.error( e );
- }
- scope.manager.itemError( url );
- }
- }, onProgress, onError );
- },
- parse: function ( json ) {
- const interleavedBufferMap = {};
- const arrayBufferMap = {};
- function getInterleavedBuffer( json, uuid ) {
- if ( interleavedBufferMap[ uuid ] !== undefined ) return interleavedBufferMap[ uuid ];
- const interleavedBuffers = json.interleavedBuffers;
- const interleavedBuffer = interleavedBuffers[ uuid ];
- const buffer = getArrayBuffer( json, interleavedBuffer.buffer );
- const array = getTypedArray( interleavedBuffer.type, buffer );
- const ib = new InterleavedBuffer( array, interleavedBuffer.stride );
- ib.uuid = interleavedBuffer.uuid;
- interleavedBufferMap[ uuid ] = ib;
- return ib;
- }
- function getArrayBuffer( json, uuid ) {
- if ( arrayBufferMap[ uuid ] !== undefined ) return arrayBufferMap[ uuid ];
- const arrayBuffers = json.arrayBuffers;
- const arrayBuffer = arrayBuffers[ uuid ];
- const ab = new Uint32Array( arrayBuffer ).buffer;
- arrayBufferMap[ uuid ] = ab;
- return ab;
- }
- const geometry = json.isInstancedBufferGeometry ? new InstancedBufferGeometry() : new BufferGeometry();
- const index = json.data.index;
- if ( index !== undefined ) {
- const typedArray = getTypedArray( index.type, index.array );
- geometry.setIndex( new BufferAttribute( typedArray, 1 ) );
- }
- const attributes = json.data.attributes;
- for ( const key in attributes ) {
- const attribute = attributes[ key ];
- let bufferAttribute;
- if ( attribute.isInterleavedBufferAttribute ) {
- const interleavedBuffer = getInterleavedBuffer( json.data, attribute.data );
- bufferAttribute = new InterleavedBufferAttribute( interleavedBuffer, attribute.itemSize, attribute.offset, attribute.normalized );
- } else {
- const typedArray = getTypedArray( attribute.type, attribute.array );
- const bufferAttributeConstr = attribute.isInstancedBufferAttribute ? InstancedBufferAttribute : BufferAttribute;
- bufferAttribute = new bufferAttributeConstr( typedArray, attribute.itemSize, attribute.normalized );
- }
- if ( attribute.name !== undefined ) bufferAttribute.name = attribute.name;
- geometry.setAttribute( key, bufferAttribute );
- }
- const morphAttributes = json.data.morphAttributes;
- if ( morphAttributes ) {
- for ( const key in morphAttributes ) {
- const attributeArray = morphAttributes[ key ];
- const array = [];
- for ( let i = 0, il = attributeArray.length; i < il; i ++ ) {
- const attribute = attributeArray[ i ];
- let bufferAttribute;
- if ( attribute.isInterleavedBufferAttribute ) {
- const interleavedBuffer = getInterleavedBuffer( json.data, attribute.data );
- bufferAttribute = new InterleavedBufferAttribute( interleavedBuffer, attribute.itemSize, attribute.offset, attribute.normalized );
- } else {
- const typedArray = getTypedArray( attribute.type, attribute.array );
- bufferAttribute = new BufferAttribute( typedArray, attribute.itemSize, attribute.normalized );
- }
- if ( attribute.name !== undefined ) bufferAttribute.name = attribute.name;
- array.push( bufferAttribute );
- }
- geometry.morphAttributes[ key ] = array;
- }
- }
- const morphTargetsRelative = json.data.morphTargetsRelative;
- if ( morphTargetsRelative ) {
- geometry.morphTargetsRelative = true;
- }
- const groups = json.data.groups || json.data.drawcalls || json.data.offsets;
- if ( groups !== undefined ) {
- for ( let i = 0, n = groups.length; i !== n; ++ i ) {
- const group = groups[ i ];
- geometry.addGroup( group.start, group.count, group.materialIndex );
- }
- }
- const boundingSphere = json.data.boundingSphere;
- if ( boundingSphere !== undefined ) {
- const center = new Vector3();
- if ( boundingSphere.center !== undefined ) {
- center.fromArray( boundingSphere.center );
- }
- geometry.boundingSphere = new Sphere( center, boundingSphere.radius );
- }
- if ( json.name ) geometry.name = json.name;
- if ( json.userData ) geometry.userData = json.userData;
- return geometry;
- }
- } );
- class ObjectLoader extends Loader {
- constructor( manager ) {
- super( manager );
- }
- load( url, onLoad, onProgress, onError ) {
- const scope = this;
- const path = ( this.path === '' ) ? LoaderUtils.extractUrlBase( url ) : this.path;
- this.resourcePath = this.resourcePath || path;
- const loader = new FileLoader( this.manager );
- loader.setPath( this.path );
- loader.setRequestHeader( this.requestHeader );
- loader.setWithCredentials( this.withCredentials );
- loader.load( url, function ( text ) {
- let json = null;
- try {
- json = JSON.parse( text );
- } catch ( error ) {
- if ( onError !== undefined ) onError( error );
- console.error( 'THREE:ObjectLoader: Can\'t parse ' + url + '.', error.message );
- return;
- }
- const metadata = json.metadata;
- if ( metadata === undefined || metadata.type === undefined || metadata.type.toLowerCase() === 'geometry' ) {
- console.error( 'THREE.ObjectLoader: Can\'t load ' + url );
- return;
- }
- scope.parse( json, onLoad );
- }, onProgress, onError );
- }
- parse( json, onLoad ) {
- const animations = this.parseAnimations( json.animations );
- const shapes = this.parseShapes( json.shapes );
- const geometries = this.parseGeometries( json.geometries, shapes );
- const images = this.parseImages( json.images, function () {
- if ( onLoad !== undefined ) onLoad( object );
- } );
- const textures = this.parseTextures( json.textures, images );
- const materials = this.parseMaterials( json.materials, textures );
- const object = this.parseObject( json.object, geometries, materials, animations );
- const skeletons = this.parseSkeletons( json.skeletons, object );
- this.bindSkeletons( object, skeletons );
- //
- if ( onLoad !== undefined ) {
- let hasImages = false;
- for ( const uuid in images ) {
- if ( images[ uuid ] instanceof HTMLImageElement ) {
- hasImages = true;
- break;
- }
- }
- if ( hasImages === false ) onLoad( object );
- }
- return object;
- }
- parseShapes( json ) {
- const shapes = {};
- if ( json !== undefined ) {
- for ( let i = 0, l = json.length; i < l; i ++ ) {
- const shape = new Shape().fromJSON( json[ i ] );
- shapes[ shape.uuid ] = shape;
- }
- }
- return shapes;
- }
- parseSkeletons( json, object ) {
- const skeletons = {};
- const bones = {};
- // generate bone lookup table
- object.traverse( function ( child ) {
- if ( child.isBone ) bones[ child.uuid ] = child;
- } );
- // create skeletons
- if ( json !== undefined ) {
- for ( let i = 0, l = json.length; i < l; i ++ ) {
- const skeleton = new Skeleton().fromJSON( json[ i ], bones );
- skeletons[ skeleton.uuid ] = skeleton;
- }
- }
- return skeletons;
- }
- parseGeometries( json, shapes ) {
- const geometries = {};
- let geometryShapes;
- if ( json !== undefined ) {
- const bufferGeometryLoader = new BufferGeometryLoader();
- for ( let i = 0, l = json.length; i < l; i ++ ) {
- let geometry;
- const data = json[ i ];
- switch ( data.type ) {
- case 'PlaneGeometry':
- case 'PlaneBufferGeometry':
- geometry = new Geometries[ data.type ](
- data.width,
- data.height,
- data.widthSegments,
- data.heightSegments
- );
- break;
- case 'BoxGeometry':
- case 'BoxBufferGeometry':
- case 'CubeGeometry': // backwards compatible
- geometry = new Geometries[ data.type ](
- data.width,
- data.height,
- data.depth,
- data.widthSegments,
- data.heightSegments,
- data.depthSegments
- );
- break;
- case 'CircleGeometry':
- case 'CircleBufferGeometry':
- geometry = new Geometries[ data.type ](
- data.radius,
- data.segments,
- data.thetaStart,
- data.thetaLength
- );
- break;
- case 'CylinderGeometry':
- case 'CylinderBufferGeometry':
- geometry = new Geometries[ data.type ](
- data.radiusTop,
- data.radiusBottom,
- data.height,
- data.radialSegments,
- data.heightSegments,
- data.openEnded,
- data.thetaStart,
- data.thetaLength
- );
- break;
- case 'ConeGeometry':
- case 'ConeBufferGeometry':
- geometry = new Geometries[ data.type ](
- data.radius,
- data.height,
- data.radialSegments,
- data.heightSegments,
- data.openEnded,
- data.thetaStart,
- data.thetaLength
- );
- break;
- case 'SphereGeometry':
- case 'SphereBufferGeometry':
- geometry = new Geometries[ data.type ](
- data.radius,
- data.widthSegments,
- data.heightSegments,
- data.phiStart,
- data.phiLength,
- data.thetaStart,
- data.thetaLength
- );
- break;
- case 'DodecahedronGeometry':
- case 'DodecahedronBufferGeometry':
- case 'IcosahedronGeometry':
- case 'IcosahedronBufferGeometry':
- case 'OctahedronGeometry':
- case 'OctahedronBufferGeometry':
- case 'TetrahedronGeometry':
- case 'TetrahedronBufferGeometry':
- geometry = new Geometries[ data.type ](
- data.radius,
- data.detail
- );
- break;
- case 'RingGeometry':
- case 'RingBufferGeometry':
- geometry = new Geometries[ data.type ](
- data.innerRadius,
- data.outerRadius,
- data.thetaSegments,
- data.phiSegments,
- data.thetaStart,
- data.thetaLength
- );
- break;
- case 'TorusGeometry':
- case 'TorusBufferGeometry':
- geometry = new Geometries[ data.type ](
- data.radius,
- data.tube,
- data.radialSegments,
- data.tubularSegments,
- data.arc
- );
- break;
- case 'TorusKnotGeometry':
- case 'TorusKnotBufferGeometry':
- geometry = new Geometries[ data.type ](
- data.radius,
- data.tube,
- data.tubularSegments,
- data.radialSegments,
- data.p,
- data.q
- );
- break;
- case 'TubeGeometry':
- case 'TubeBufferGeometry':
- // This only works for built-in curves (e.g. CatmullRomCurve3).
- // User defined curves or instances of CurvePath will not be deserialized.
- geometry = new Geometries[ data.type ](
- new Curves[ data.path.type ]().fromJSON( data.path ),
- data.tubularSegments,
- data.radius,
- data.radialSegments,
- data.closed
- );
- break;
- case 'LatheGeometry':
- case 'LatheBufferGeometry':
- geometry = new Geometries[ data.type ](
- data.points,
- data.segments,
- data.phiStart,
- data.phiLength
- );
- break;
- case 'PolyhedronGeometry':
- case 'PolyhedronBufferGeometry':
- geometry = new Geometries[ data.type ](
- data.vertices,
- data.indices,
- data.radius,
- data.details
- );
- break;
- case 'ShapeGeometry':
- case 'ShapeBufferGeometry':
- geometryShapes = [];
- for ( let j = 0, jl = data.shapes.length; j < jl; j ++ ) {
- const shape = shapes[ data.shapes[ j ] ];
- geometryShapes.push( shape );
- }
- geometry = new Geometries[ data.type ](
- geometryShapes,
- data.curveSegments
- );
- break;
- case 'ExtrudeGeometry':
- case 'ExtrudeBufferGeometry':
- geometryShapes = [];
- for ( let j = 0, jl = data.shapes.length; j < jl; j ++ ) {
- const shape = shapes[ data.shapes[ j ] ];
- geometryShapes.push( shape );
- }
- const extrudePath = data.options.extrudePath;
- if ( extrudePath !== undefined ) {
- data.options.extrudePath = new Curves[ extrudePath.type ]().fromJSON( extrudePath );
- }
- geometry = new Geometries[ data.type ](
- geometryShapes,
- data.options
- );
- break;
- case 'BufferGeometry':
- case 'InstancedBufferGeometry':
- geometry = bufferGeometryLoader.parse( data );
- break;
- case 'Geometry':
- console.error( 'THREE.ObjectLoader: Loading "Geometry" is not supported anymore.' );
- break;
- default:
- console.warn( 'THREE.ObjectLoader: Unsupported geometry type "' + data.type + '"' );
- continue;
- }
- geometry.uuid = data.uuid;
- if ( data.name !== undefined ) geometry.name = data.name;
- if ( geometry.isBufferGeometry === true && data.userData !== undefined ) geometry.userData = data.userData;
- geometries[ data.uuid ] = geometry;
- }
- }
- return geometries;
- }
- parseMaterials( json, textures ) {
- const cache = {}; // MultiMaterial
- const materials = {};
- if ( json !== undefined ) {
- const loader = new MaterialLoader();
- loader.setTextures( textures );
- for ( let i = 0, l = json.length; i < l; i ++ ) {
- const data = json[ i ];
- if ( data.type === 'MultiMaterial' ) {
- // Deprecated
- const array = [];
- for ( let j = 0; j < data.materials.length; j ++ ) {
- const material = data.materials[ j ];
- if ( cache[ material.uuid ] === undefined ) {
- cache[ material.uuid ] = loader.parse( material );
- }
- array.push( cache[ material.uuid ] );
- }
- materials[ data.uuid ] = array;
- } else {
- if ( cache[ data.uuid ] === undefined ) {
- cache[ data.uuid ] = loader.parse( data );
- }
- materials[ data.uuid ] = cache[ data.uuid ];
- }
- }
- }
- return materials;
- }
- parseAnimations( json ) {
- const animations = {};
- if ( json !== undefined ) {
- for ( let i = 0; i < json.length; i ++ ) {
- const data = json[ i ];
- const clip = AnimationClip.parse( data );
- animations[ clip.uuid ] = clip;
- }
- }
- return animations;
- }
- parseImages( json, onLoad ) {
- const scope = this;
- const images = {};
- let loader;
- function loadImage( url ) {
- scope.manager.itemStart( url );
- return loader.load( url, function () {
- scope.manager.itemEnd( url );
- }, undefined, function () {
- scope.manager.itemError( url );
- scope.manager.itemEnd( url );
- } );
- }
- function deserializeImage( image ) {
- if ( typeof image === 'string' ) {
- const url = image;
- const path = /^(\/\/)|([a-z]+:(\/\/)?)/i.test( url ) ? url : scope.resourcePath + url;
- return loadImage( path );
- } else {
- if ( image.data ) {
- return {
- data: getTypedArray( image.type, image.data ),
- width: image.width,
- height: image.height
- };
- } else {
- return null;
- }
- }
- }
- if ( json !== undefined && json.length > 0 ) {
- const manager = new LoadingManager( onLoad );
- loader = new ImageLoader( manager );
- loader.setCrossOrigin( this.crossOrigin );
- for ( let i = 0, il = json.length; i < il; i ++ ) {
- const image = json[ i ];
- const url = image.url;
- if ( Array.isArray( url ) ) {
- // load array of images e.g CubeTexture
- images[ image.uuid ] = [];
- for ( let j = 0, jl = url.length; j < jl; j ++ ) {
- const currentUrl = url[ j ];
- const deserializedImage = deserializeImage( currentUrl );
- if ( deserializedImage !== null ) {
- if ( deserializedImage instanceof HTMLImageElement ) {
- images[ image.uuid ].push( deserializedImage );
- } else {
- // special case: handle array of data textures for cube textures
- images[ image.uuid ].push( new DataTexture( deserializedImage.data, deserializedImage.width, deserializedImage.height ) );
- }
- }
- }
- } else {
- // load single image
- const deserializedImage = deserializeImage( image.url );
- if ( deserializedImage !== null ) {
- images[ image.uuid ] = deserializedImage;
- }
- }
- }
- }
- return images;
- }
- parseTextures( json, images ) {
- function parseConstant( value, type ) {
- if ( typeof value === 'number' ) return value;
- console.warn( 'THREE.ObjectLoader.parseTexture: Constant should be in numeric form.', value );
- return type[ value ];
- }
- const textures = {};
- if ( json !== undefined ) {
- for ( let i = 0, l = json.length; i < l; i ++ ) {
- const data = json[ i ];
- if ( data.image === undefined ) {
- console.warn( 'THREE.ObjectLoader: No "image" specified for', data.uuid );
- }
- if ( images[ data.image ] === undefined ) {
- console.warn( 'THREE.ObjectLoader: Undefined image', data.image );
- }
- let texture;
- const image = images[ data.image ];
- if ( Array.isArray( image ) ) {
- texture = new CubeTexture( image );
- if ( image.length === 6 ) texture.needsUpdate = true;
- } else {
- if ( image && image.data ) {
- texture = new DataTexture( image.data, image.width, image.height );
- } else {
- texture = new Texture( image );
- }
- if ( image ) texture.needsUpdate = true; // textures can have undefined image data
- }
- texture.uuid = data.uuid;
- if ( data.name !== undefined ) texture.name = data.name;
- if ( data.mapping !== undefined ) texture.mapping = parseConstant( data.mapping, TEXTURE_MAPPING );
- if ( data.offset !== undefined ) texture.offset.fromArray( data.offset );
- if ( data.repeat !== undefined ) texture.repeat.fromArray( data.repeat );
- if ( data.center !== undefined ) texture.center.fromArray( data.center );
- if ( data.rotation !== undefined ) texture.rotation = data.rotation;
- if ( data.wrap !== undefined ) {
- texture.wrapS = parseConstant( data.wrap[ 0 ], TEXTURE_WRAPPING );
- texture.wrapT = parseConstant( data.wrap[ 1 ], TEXTURE_WRAPPING );
- }
- if ( data.format !== undefined ) texture.format = data.format;
- if ( data.type !== undefined ) texture.type = data.type;
- if ( data.encoding !== undefined ) texture.encoding = data.encoding;
- if ( data.minFilter !== undefined ) texture.minFilter = parseConstant( data.minFilter, TEXTURE_FILTER );
- if ( data.magFilter !== undefined ) texture.magFilter = parseConstant( data.magFilter, TEXTURE_FILTER );
- if ( data.anisotropy !== undefined ) texture.anisotropy = data.anisotropy;
- if ( data.flipY !== undefined ) texture.flipY = data.flipY;
- if ( data.premultiplyAlpha !== undefined ) texture.premultiplyAlpha = data.premultiplyAlpha;
- if ( data.unpackAlignment !== undefined ) texture.unpackAlignment = data.unpackAlignment;
- textures[ data.uuid ] = texture;
- }
- }
- return textures;
- }
- parseObject( data, geometries, materials, animations ) {
- let object;
- function getGeometry( name ) {
- if ( geometries[ name ] === undefined ) {
- console.warn( 'THREE.ObjectLoader: Undefined geometry', name );
- }
- return geometries[ name ];
- }
- function getMaterial( name ) {
- if ( name === undefined ) return undefined;
- if ( Array.isArray( name ) ) {
- const array = [];
- for ( let i = 0, l = name.length; i < l; i ++ ) {
- const uuid = name[ i ];
- if ( materials[ uuid ] === undefined ) {
- console.warn( 'THREE.ObjectLoader: Undefined material', uuid );
- }
- array.push( materials[ uuid ] );
- }
- return array;
- }
- if ( materials[ name ] === undefined ) {
- console.warn( 'THREE.ObjectLoader: Undefined material', name );
- }
- return materials[ name ];
- }
- let geometry, material;
- switch ( data.type ) {
- case 'Scene':
- object = new Scene();
- if ( data.background !== undefined ) {
- if ( Number.isInteger( data.background ) ) {
- object.background = new Color( data.background );
- }
- }
- if ( data.fog !== undefined ) {
- if ( data.fog.type === 'Fog' ) {
- object.fog = new Fog( data.fog.color, data.fog.near, data.fog.far );
- } else if ( data.fog.type === 'FogExp2' ) {
- object.fog = new FogExp2( data.fog.color, data.fog.density );
- }
- }
- break;
- case 'PerspectiveCamera':
- object = new PerspectiveCamera( data.fov, data.aspect, data.near, data.far );
- if ( data.focus !== undefined ) object.focus = data.focus;
- if ( data.zoom !== undefined ) object.zoom = data.zoom;
- if ( data.filmGauge !== undefined ) object.filmGauge = data.filmGauge;
- if ( data.filmOffset !== undefined ) object.filmOffset = data.filmOffset;
- if ( data.view !== undefined ) object.view = Object.assign( {}, data.view );
- break;
- case 'OrthographicCamera':
- object = new OrthographicCamera( data.left, data.right, data.top, data.bottom, data.near, data.far );
- if ( data.zoom !== undefined ) object.zoom = data.zoom;
- if ( data.view !== undefined ) object.view = Object.assign( {}, data.view );
- break;
- case 'AmbientLight':
- object = new AmbientLight( data.color, data.intensity );
- break;
- case 'DirectionalLight':
- object = new DirectionalLight( data.color, data.intensity );
- break;
- case 'PointLight':
- object = new PointLight( data.color, data.intensity, data.distance, data.decay );
- break;
- case 'RectAreaLight':
- object = new RectAreaLight( data.color, data.intensity, data.width, data.height );
- break;
- case 'SpotLight':
- object = new SpotLight( data.color, data.intensity, data.distance, data.angle, data.penumbra, data.decay );
- break;
- case 'HemisphereLight':
- object = new HemisphereLight( data.color, data.groundColor, data.intensity );
- break;
- case 'LightProbe':
- object = new LightProbe().fromJSON( data );
- break;
- case 'SkinnedMesh':
- geometry = getGeometry( data.geometry );
- material = getMaterial( data.material );
- object = new SkinnedMesh( geometry, material );
- if ( data.bindMode !== undefined ) object.bindMode = data.bindMode;
- if ( data.bindMatrix !== undefined ) object.bindMatrix.fromArray( data.bindMatrix );
- if ( data.skeleton !== undefined ) object.skeleton = data.skeleton;
- break;
- case 'Mesh':
- geometry = getGeometry( data.geometry );
- material = getMaterial( data.material );
- object = new Mesh( geometry, material );
- break;
- case 'InstancedMesh':
- geometry = getGeometry( data.geometry );
- material = getMaterial( data.material );
- const count = data.count;
- const instanceMatrix = data.instanceMatrix;
- object = new InstancedMesh( geometry, material, count );
- object.instanceMatrix = new BufferAttribute( new Float32Array( instanceMatrix.array ), 16 );
- break;
- case 'LOD':
- object = new LOD();
- break;
- case 'Line':
- object = new Line( getGeometry( data.geometry ), getMaterial( data.material ) );
- break;
- case 'LineLoop':
- object = new LineLoop( getGeometry( data.geometry ), getMaterial( data.material ) );
- break;
- case 'LineSegments':
- object = new LineSegments( getGeometry( data.geometry ), getMaterial( data.material ) );
- break;
- case 'PointCloud':
- case 'Points':
- object = new Points( getGeometry( data.geometry ), getMaterial( data.material ) );
- break;
- case 'Sprite':
- object = new Sprite( getMaterial( data.material ) );
- break;
- case 'Group':
- object = new Group();
- break;
- case 'Bone':
- object = new Bone();
- break;
- default:
- object = new Object3D();
- }
- object.uuid = data.uuid;
- if ( data.name !== undefined ) object.name = data.name;
- if ( data.matrix !== undefined ) {
- object.matrix.fromArray( data.matrix );
- if ( data.matrixAutoUpdate !== undefined ) object.matrixAutoUpdate = data.matrixAutoUpdate;
- if ( object.matrixAutoUpdate ) object.matrix.decompose( object.position, object.quaternion, object.scale );
- } else {
- if ( data.position !== undefined ) object.position.fromArray( data.position );
- if ( data.rotation !== undefined ) object.rotation.fromArray( data.rotation );
- if ( data.quaternion !== undefined ) object.quaternion.fromArray( data.quaternion );
- if ( data.scale !== undefined ) object.scale.fromArray( data.scale );
- }
- if ( data.castShadow !== undefined ) object.castShadow = data.castShadow;
- if ( data.receiveShadow !== undefined ) object.receiveShadow = data.receiveShadow;
- if ( data.shadow ) {
- if ( data.shadow.bias !== undefined ) object.shadow.bias = data.shadow.bias;
- if ( data.shadow.normalBias !== undefined ) object.shadow.normalBias = data.shadow.normalBias;
- if ( data.shadow.radius !== undefined ) object.shadow.radius = data.shadow.radius;
- if ( data.shadow.mapSize !== undefined ) object.shadow.mapSize.fromArray( data.shadow.mapSize );
- if ( data.shadow.camera !== undefined ) object.shadow.camera = this.parseObject( data.shadow.camera );
- }
- if ( data.visible !== undefined ) object.visible = data.visible;
- if ( data.frustumCulled !== undefined ) object.frustumCulled = data.frustumCulled;
- if ( data.renderOrder !== undefined ) object.renderOrder = data.renderOrder;
- if ( data.userData !== undefined ) object.userData = data.userData;
- if ( data.layers !== undefined ) object.layers.mask = data.layers;
- if ( data.children !== undefined ) {
- const children = data.children;
- for ( let i = 0; i < children.length; i ++ ) {
- object.add( this.parseObject( children[ i ], geometries, materials, animations ) );
- }
- }
- if ( data.animations !== undefined ) {
- const objectAnimations = data.animations;
- for ( let i = 0; i < objectAnimations.length; i ++ ) {
- const uuid = objectAnimations[ i ];
- object.animations.push( animations[ uuid ] );
- }
- }
- if ( data.type === 'LOD' ) {
- if ( data.autoUpdate !== undefined ) object.autoUpdate = data.autoUpdate;
- const levels = data.levels;
- for ( let l = 0; l < levels.length; l ++ ) {
- const level = levels[ l ];
- const child = object.getObjectByProperty( 'uuid', level.object );
- if ( child !== undefined ) {
- object.addLevel( child, level.distance );
- }
- }
- }
- return object;
- }
- bindSkeletons( object, skeletons ) {
- if ( Object.keys( skeletons ).length === 0 ) return;
- object.traverse( function ( child ) {
- if ( child.isSkinnedMesh === true && child.skeleton !== undefined ) {
- const skeleton = skeletons[ child.skeleton ];
- if ( skeleton === undefined ) {
- console.warn( 'THREE.ObjectLoader: No skeleton found with UUID:', child.skeleton );
- } else {
- child.bind( skeleton, child.bindMatrix );
- }
- }
- } );
- }
- /* DEPRECATED */
- setTexturePath( value ) {
- console.warn( 'THREE.ObjectLoader: .setTexturePath() has been renamed to .setResourcePath().' );
- return this.setResourcePath( value );
- }
- }
- const TEXTURE_MAPPING = {
- UVMapping: UVMapping,
- CubeReflectionMapping: CubeReflectionMapping,
- CubeRefractionMapping: CubeRefractionMapping,
- EquirectangularReflectionMapping: EquirectangularReflectionMapping,
- EquirectangularRefractionMapping: EquirectangularRefractionMapping,
- CubeUVReflectionMapping: CubeUVReflectionMapping,
- CubeUVRefractionMapping: CubeUVRefractionMapping
- };
- const TEXTURE_WRAPPING = {
- RepeatWrapping: RepeatWrapping,
- ClampToEdgeWrapping: ClampToEdgeWrapping,
- MirroredRepeatWrapping: MirroredRepeatWrapping
- };
- const TEXTURE_FILTER = {
- NearestFilter: NearestFilter,
- NearestMipmapNearestFilter: NearestMipmapNearestFilter,
- NearestMipmapLinearFilter: NearestMipmapLinearFilter,
- LinearFilter: LinearFilter,
- LinearMipmapNearestFilter: LinearMipmapNearestFilter,
- LinearMipmapLinearFilter: LinearMipmapLinearFilter
- };
- function ImageBitmapLoader( manager ) {
- if ( typeof createImageBitmap === 'undefined' ) {
- console.warn( 'THREE.ImageBitmapLoader: createImageBitmap() not supported.' );
- }
- if ( typeof fetch === 'undefined' ) {
- console.warn( 'THREE.ImageBitmapLoader: fetch() not supported.' );
- }
- Loader.call( this, manager );
- this.options = { premultiplyAlpha: 'none' };
- }
- ImageBitmapLoader.prototype = Object.assign( Object.create( Loader.prototype ), {
- constructor: ImageBitmapLoader,
- isImageBitmapLoader: true,
- setOptions: function setOptions( options ) {
- this.options = options;
- return this;
- },
- load: function ( url, onLoad, onProgress, onError ) {
- if ( url === undefined ) url = '';
- if ( this.path !== undefined ) url = this.path + url;
- url = this.manager.resolveURL( url );
- const scope = this;
- const cached = Cache.get( url );
- if ( cached !== undefined ) {
- scope.manager.itemStart( url );
- setTimeout( function () {
- if ( onLoad ) onLoad( cached );
- scope.manager.itemEnd( url );
- }, 0 );
- return cached;
- }
- const fetchOptions = {};
- fetchOptions.credentials = ( this.crossOrigin === 'anonymous' ) ? 'same-origin' : 'include';
- fetch( url, fetchOptions ).then( function ( res ) {
- return res.blob();
- } ).then( function ( blob ) {
- return createImageBitmap( blob, scope.options );
- } ).then( function ( imageBitmap ) {
- Cache.add( url, imageBitmap );
- if ( onLoad ) onLoad( imageBitmap );
- scope.manager.itemEnd( url );
- } ).catch( function ( e ) {
- if ( onError ) onError( e );
- scope.manager.itemError( url );
- scope.manager.itemEnd( url );
- } );
- scope.manager.itemStart( url );
- }
- } );
- function ShapePath() {
- this.type = 'ShapePath';
- this.color = new Color();
- this.subPaths = [];
- this.currentPath = null;
- }
- Object.assign( ShapePath.prototype, {
- moveTo: function ( x, y ) {
- this.currentPath = new Path();
- this.subPaths.push( this.currentPath );
- this.currentPath.moveTo( x, y );
- return this;
- },
- lineTo: function ( x, y ) {
- this.currentPath.lineTo( x, y );
- return this;
- },
- quadraticCurveTo: function ( aCPx, aCPy, aX, aY ) {
- this.currentPath.quadraticCurveTo( aCPx, aCPy, aX, aY );
- return this;
- },
- bezierCurveTo: function ( aCP1x, aCP1y, aCP2x, aCP2y, aX, aY ) {
- this.currentPath.bezierCurveTo( aCP1x, aCP1y, aCP2x, aCP2y, aX, aY );
- return this;
- },
- splineThru: function ( pts ) {
- this.currentPath.splineThru( pts );
- return this;
- },
- toShapes: function ( isCCW, noHoles ) {
- function toShapesNoHoles( inSubpaths ) {
- const shapes = [];
- for ( let i = 0, l = inSubpaths.length; i < l; i ++ ) {
- const tmpPath = inSubpaths[ i ];
- const tmpShape = new Shape();
- tmpShape.curves = tmpPath.curves;
- shapes.push( tmpShape );
- }
- return shapes;
- }
- function isPointInsidePolygon( inPt, inPolygon ) {
- const polyLen = inPolygon.length;
- // inPt on polygon contour => immediate success or
- // toggling of inside/outside at every single! intersection point of an edge
- // with the horizontal line through inPt, left of inPt
- // not counting lowerY endpoints of edges and whole edges on that line
- let inside = false;
- for ( let p = polyLen - 1, q = 0; q < polyLen; p = q ++ ) {
- let edgeLowPt = inPolygon[ p ];
- let edgeHighPt = inPolygon[ q ];
- let edgeDx = edgeHighPt.x - edgeLowPt.x;
- let edgeDy = edgeHighPt.y - edgeLowPt.y;
- if ( Math.abs( edgeDy ) > Number.EPSILON ) {
- // not parallel
- if ( edgeDy < 0 ) {
- edgeLowPt = inPolygon[ q ]; edgeDx = - edgeDx;
- edgeHighPt = inPolygon[ p ]; edgeDy = - edgeDy;
- }
- if ( ( inPt.y < edgeLowPt.y ) || ( inPt.y > edgeHighPt.y ) ) continue;
- if ( inPt.y === edgeLowPt.y ) {
- if ( inPt.x === edgeLowPt.x ) return true; // inPt is on contour ?
- // continue; // no intersection or edgeLowPt => doesn't count !!!
- } else {
- const perpEdge = edgeDy * ( inPt.x - edgeLowPt.x ) - edgeDx * ( inPt.y - edgeLowPt.y );
- if ( perpEdge === 0 ) return true; // inPt is on contour ?
- if ( perpEdge < 0 ) continue;
- inside = ! inside; // true intersection left of inPt
- }
- } else {
- // parallel or collinear
- if ( inPt.y !== edgeLowPt.y ) continue; // parallel
- // edge lies on the same horizontal line as inPt
- if ( ( ( edgeHighPt.x <= inPt.x ) && ( inPt.x <= edgeLowPt.x ) ) ||
- ( ( edgeLowPt.x <= inPt.x ) && ( inPt.x <= edgeHighPt.x ) ) ) return true; // inPt: Point on contour !
- // continue;
- }
- }
- return inside;
- }
- const isClockWise = ShapeUtils.isClockWise;
- const subPaths = this.subPaths;
- if ( subPaths.length === 0 ) return [];
- if ( noHoles === true ) return toShapesNoHoles( subPaths );
- let solid, tmpPath, tmpShape;
- const shapes = [];
- if ( subPaths.length === 1 ) {
- tmpPath = subPaths[ 0 ];
- tmpShape = new Shape();
- tmpShape.curves = tmpPath.curves;
- shapes.push( tmpShape );
- return shapes;
- }
- let holesFirst = ! isClockWise( subPaths[ 0 ].getPoints() );
- holesFirst = isCCW ? ! holesFirst : holesFirst;
- // console.log("Holes first", holesFirst);
- const betterShapeHoles = [];
- const newShapes = [];
- let newShapeHoles = [];
- let mainIdx = 0;
- let tmpPoints;
- newShapes[ mainIdx ] = undefined;
- newShapeHoles[ mainIdx ] = [];
- for ( let i = 0, l = subPaths.length; i < l; i ++ ) {
- tmpPath = subPaths[ i ];
- tmpPoints = tmpPath.getPoints();
- solid = isClockWise( tmpPoints );
- solid = isCCW ? ! solid : solid;
- if ( solid ) {
- if ( ( ! holesFirst ) && ( newShapes[ mainIdx ] ) ) mainIdx ++;
- newShapes[ mainIdx ] = { s: new Shape(), p: tmpPoints };
- newShapes[ mainIdx ].s.curves = tmpPath.curves;
- if ( holesFirst ) mainIdx ++;
- newShapeHoles[ mainIdx ] = [];
- //console.log('cw', i);
- } else {
- newShapeHoles[ mainIdx ].push( { h: tmpPath, p: tmpPoints[ 0 ] } );
- //console.log('ccw', i);
- }
- }
- // only Holes? -> probably all Shapes with wrong orientation
- if ( ! newShapes[ 0 ] ) return toShapesNoHoles( subPaths );
- if ( newShapes.length > 1 ) {
- let ambiguous = false;
- const toChange = [];
- for ( let sIdx = 0, sLen = newShapes.length; sIdx < sLen; sIdx ++ ) {
- betterShapeHoles[ sIdx ] = [];
- }
- for ( let sIdx = 0, sLen = newShapes.length; sIdx < sLen; sIdx ++ ) {
- const sho = newShapeHoles[ sIdx ];
- for ( let hIdx = 0; hIdx < sho.length; hIdx ++ ) {
- const ho = sho[ hIdx ];
- let hole_unassigned = true;
- for ( let s2Idx = 0; s2Idx < newShapes.length; s2Idx ++ ) {
- if ( isPointInsidePolygon( ho.p, newShapes[ s2Idx ].p ) ) {
- if ( sIdx !== s2Idx ) toChange.push( { froms: sIdx, tos: s2Idx, hole: hIdx } );
- if ( hole_unassigned ) {
- hole_unassigned = false;
- betterShapeHoles[ s2Idx ].push( ho );
- } else {
- ambiguous = true;
- }
- }
- }
- if ( hole_unassigned ) {
- betterShapeHoles[ sIdx ].push( ho );
- }
- }
- }
- // console.log("ambiguous: ", ambiguous);
- if ( toChange.length > 0 ) {
- // console.log("to change: ", toChange);
- if ( ! ambiguous ) newShapeHoles = betterShapeHoles;
- }
- }
- let tmpHoles;
- for ( let i = 0, il = newShapes.length; i < il; i ++ ) {
- tmpShape = newShapes[ i ].s;
- shapes.push( tmpShape );
- tmpHoles = newShapeHoles[ i ];
- for ( let j = 0, jl = tmpHoles.length; j < jl; j ++ ) {
- tmpShape.holes.push( tmpHoles[ j ].h );
- }
- }
- //console.log("shape", shapes);
- return shapes;
- }
- } );
- function Font( data ) {
- this.type = 'Font';
- this.data = data;
- }
- Object.assign( Font.prototype, {
- isFont: true,
- generateShapes: function ( text, size = 100 ) {
- const shapes = [];
- const paths = createPaths( text, size, this.data );
- for ( let p = 0, pl = paths.length; p < pl; p ++ ) {
- Array.prototype.push.apply( shapes, paths[ p ].toShapes() );
- }
- return shapes;
- }
- } );
- function createPaths( text, size, data ) {
- const chars = Array.from ? Array.from( text ) : String( text ).split( '' ); // workaround for IE11, see #13988
- const scale = size / data.resolution;
- const line_height = ( data.boundingBox.yMax - data.boundingBox.yMin + data.underlineThickness ) * scale;
- const paths = [];
- let offsetX = 0, offsetY = 0;
- for ( let i = 0; i < chars.length; i ++ ) {
- const char = chars[ i ];
- if ( char === '\n' ) {
- offsetX = 0;
- offsetY -= line_height;
- } else {
- const ret = createPath( char, scale, offsetX, offsetY, data );
- offsetX += ret.offsetX;
- paths.push( ret.path );
- }
- }
- return paths;
- }
- function createPath( char, scale, offsetX, offsetY, data ) {
- const glyph = data.glyphs[ char ] || data.glyphs[ '?' ];
- if ( ! glyph ) {
- console.error( 'THREE.Font: character "' + char + '" does not exists in font family ' + data.familyName + '.' );
- return;
- }
- const path = new ShapePath();
- let x, y, cpx, cpy, cpx1, cpy1, cpx2, cpy2;
- if ( glyph.o ) {
- const outline = glyph._cachedOutline || ( glyph._cachedOutline = glyph.o.split( ' ' ) );
- for ( let i = 0, l = outline.length; i < l; ) {
- const action = outline[ i ++ ];
- switch ( action ) {
- case 'm': // moveTo
- x = outline[ i ++ ] * scale + offsetX;
- y = outline[ i ++ ] * scale + offsetY;
- path.moveTo( x, y );
- break;
- case 'l': // lineTo
- x = outline[ i ++ ] * scale + offsetX;
- y = outline[ i ++ ] * scale + offsetY;
- path.lineTo( x, y );
- break;
- case 'q': // quadraticCurveTo
- cpx = outline[ i ++ ] * scale + offsetX;
- cpy = outline[ i ++ ] * scale + offsetY;
- cpx1 = outline[ i ++ ] * scale + offsetX;
- cpy1 = outline[ i ++ ] * scale + offsetY;
- path.quadraticCurveTo( cpx1, cpy1, cpx, cpy );
- break;
- case 'b': // bezierCurveTo
- cpx = outline[ i ++ ] * scale + offsetX;
- cpy = outline[ i ++ ] * scale + offsetY;
- cpx1 = outline[ i ++ ] * scale + offsetX;
- cpy1 = outline[ i ++ ] * scale + offsetY;
- cpx2 = outline[ i ++ ] * scale + offsetX;
- cpy2 = outline[ i ++ ] * scale + offsetY;
- path.bezierCurveTo( cpx1, cpy1, cpx2, cpy2, cpx, cpy );
- break;
- }
- }
- }
- return { offsetX: glyph.ha * scale, path: path };
- }
- function FontLoader( manager ) {
- Loader.call( this, manager );
- }
- FontLoader.prototype = Object.assign( Object.create( Loader.prototype ), {
- constructor: FontLoader,
- load: function ( url, onLoad, onProgress, onError ) {
- const scope = this;
- const loader = new FileLoader( this.manager );
- loader.setPath( this.path );
- loader.setRequestHeader( this.requestHeader );
- loader.setWithCredentials( scope.withCredentials );
- loader.load( url, function ( text ) {
- let json;
- try {
- json = JSON.parse( text );
- } catch ( e ) {
- console.warn( 'THREE.FontLoader: typeface.js support is being deprecated. Use typeface.json instead.' );
- json = JSON.parse( text.substring( 65, text.length - 2 ) );
- }
- const font = scope.parse( json );
- if ( onLoad ) onLoad( font );
- }, onProgress, onError );
- },
- parse: function ( json ) {
- return new Font( json );
- }
- } );
- let _context;
- const AudioContext = {
- getContext: function () {
- if ( _context === undefined ) {
- _context = new ( window.AudioContext || window.webkitAudioContext )();
- }
- return _context;
- },
- setContext: function ( value ) {
- _context = value;
- }
- };
- function AudioLoader( manager ) {
- Loader.call( this, manager );
- }
- AudioLoader.prototype = Object.assign( Object.create( Loader.prototype ), {
- constructor: AudioLoader,
- load: function ( url, onLoad, onProgress, onError ) {
- const scope = this;
- const loader = new FileLoader( scope.manager );
- loader.setResponseType( 'arraybuffer' );
- loader.setPath( scope.path );
- loader.setRequestHeader( scope.requestHeader );
- loader.setWithCredentials( scope.withCredentials );
- loader.load( url, function ( buffer ) {
- try {
- // Create a copy of the buffer. The `decodeAudioData` method
- // detaches the buffer when complete, preventing reuse.
- const bufferCopy = buffer.slice( 0 );
- const context = AudioContext.getContext();
- context.decodeAudioData( bufferCopy, function ( audioBuffer ) {
- onLoad( audioBuffer );
- } );
- } catch ( e ) {
- if ( onError ) {
- onError( e );
- } else {
- console.error( e );
- }
- scope.manager.itemError( url );
- }
- }, onProgress, onError );
- }
- } );
- function HemisphereLightProbe( skyColor, groundColor, intensity ) {
- LightProbe.call( this, undefined, intensity );
- const color1 = new Color().set( skyColor );
- const color2 = new Color().set( groundColor );
- const sky = new Vector3( color1.r, color1.g, color1.b );
- const ground = new Vector3( color2.r, color2.g, color2.b );
- // without extra factor of PI in the shader, should = 1 / Math.sqrt( Math.PI );
- const c0 = Math.sqrt( Math.PI );
- const c1 = c0 * Math.sqrt( 0.75 );
- this.sh.coefficients[ 0 ].copy( sky ).add( ground ).multiplyScalar( c0 );
- this.sh.coefficients[ 1 ].copy( sky ).sub( ground ).multiplyScalar( c1 );
- }
- HemisphereLightProbe.prototype = Object.assign( Object.create( LightProbe.prototype ), {
- constructor: HemisphereLightProbe,
- isHemisphereLightProbe: true,
- copy: function ( source ) { // modifying colors not currently supported
- LightProbe.prototype.copy.call( this, source );
- return this;
- },
- toJSON: function ( meta ) {
- const data = LightProbe.prototype.toJSON.call( this, meta );
- // data.sh = this.sh.toArray(); // todo
- return data;
- }
- } );
- function AmbientLightProbe( color, intensity ) {
- LightProbe.call( this, undefined, intensity );
- const color1 = new Color().set( color );
- // without extra factor of PI in the shader, would be 2 / Math.sqrt( Math.PI );
- this.sh.coefficients[ 0 ].set( color1.r, color1.g, color1.b ).multiplyScalar( 2 * Math.sqrt( Math.PI ) );
- }
- AmbientLightProbe.prototype = Object.assign( Object.create( LightProbe.prototype ), {
- constructor: AmbientLightProbe,
- isAmbientLightProbe: true,
- copy: function ( source ) { // modifying color not currently supported
- LightProbe.prototype.copy.call( this, source );
- return this;
- },
- toJSON: function ( meta ) {
- const data = LightProbe.prototype.toJSON.call( this, meta );
- // data.sh = this.sh.toArray(); // todo
- return data;
- }
- } );
- const _eyeRight = new Matrix4();
- const _eyeLeft = new Matrix4();
- function StereoCamera() {
- this.type = 'StereoCamera';
- this.aspect = 1;
- this.eyeSep = 0.064;
- this.cameraL = new PerspectiveCamera();
- this.cameraL.layers.enable( 1 );
- this.cameraL.matrixAutoUpdate = false;
- this.cameraR = new PerspectiveCamera();
- this.cameraR.layers.enable( 2 );
- this.cameraR.matrixAutoUpdate = false;
- this._cache = {
- focus: null,
- fov: null,
- aspect: null,
- near: null,
- far: null,
- zoom: null,
- eyeSep: null
- };
- }
- Object.assign( StereoCamera.prototype, {
- update: function ( camera ) {
- const cache = this._cache;
- const needsUpdate = cache.focus !== camera.focus || cache.fov !== camera.fov ||
- cache.aspect !== camera.aspect * this.aspect || cache.near !== camera.near ||
- cache.far !== camera.far || cache.zoom !== camera.zoom || cache.eyeSep !== this.eyeSep;
- if ( needsUpdate ) {
- cache.focus = camera.focus;
- cache.fov = camera.fov;
- cache.aspect = camera.aspect * this.aspect;
- cache.near = camera.near;
- cache.far = camera.far;
- cache.zoom = camera.zoom;
- cache.eyeSep = this.eyeSep;
- // Off-axis stereoscopic effect based on
- // http://paulbourke.net/stereographics/stereorender/
- const projectionMatrix = camera.projectionMatrix.clone();
- const eyeSepHalf = cache.eyeSep / 2;
- const eyeSepOnProjection = eyeSepHalf * cache.near / cache.focus;
- const ymax = ( cache.near * Math.tan( MathUtils.DEG2RAD * cache.fov * 0.5 ) ) / cache.zoom;
- let xmin, xmax;
- // translate xOffset
- _eyeLeft.elements[ 12 ] = - eyeSepHalf;
- _eyeRight.elements[ 12 ] = eyeSepHalf;
- // for left eye
- xmin = - ymax * cache.aspect + eyeSepOnProjection;
- xmax = ymax * cache.aspect + eyeSepOnProjection;
- projectionMatrix.elements[ 0 ] = 2 * cache.near / ( xmax - xmin );
- projectionMatrix.elements[ 8 ] = ( xmax + xmin ) / ( xmax - xmin );
- this.cameraL.projectionMatrix.copy( projectionMatrix );
- // for right eye
- xmin = - ymax * cache.aspect - eyeSepOnProjection;
- xmax = ymax * cache.aspect - eyeSepOnProjection;
- projectionMatrix.elements[ 0 ] = 2 * cache.near / ( xmax - xmin );
- projectionMatrix.elements[ 8 ] = ( xmax + xmin ) / ( xmax - xmin );
- this.cameraR.projectionMatrix.copy( projectionMatrix );
- }
- this.cameraL.matrixWorld.copy( camera.matrixWorld ).multiply( _eyeLeft );
- this.cameraR.matrixWorld.copy( camera.matrixWorld ).multiply( _eyeRight );
- }
- } );
- class Clock {
- constructor( autoStart ) {
- this.autoStart = ( autoStart !== undefined ) ? autoStart : true;
- this.startTime = 0;
- this.oldTime = 0;
- this.elapsedTime = 0;
- this.running = false;
- }
- start() {
- this.startTime = now();
- this.oldTime = this.startTime;
- this.elapsedTime = 0;
- this.running = true;
- }
- stop() {
- this.getElapsedTime();
- this.running = false;
- this.autoStart = false;
- }
- getElapsedTime() {
- this.getDelta();
- return this.elapsedTime;
- }
- getDelta() {
- let diff = 0;
- if ( this.autoStart && ! this.running ) {
- this.start();
- return 0;
- }
- if ( this.running ) {
- const newTime = now();
- diff = ( newTime - this.oldTime ) / 1000;
- this.oldTime = newTime;
- this.elapsedTime += diff;
- }
- return diff;
- }
- }
- function now() {
- return ( typeof performance === 'undefined' ? Date : performance ).now(); // see #10732
- }
- const _position$2 = /*@__PURE__*/ new Vector3();
- const _quaternion$3 = /*@__PURE__*/ new Quaternion();
- const _scale$1 = /*@__PURE__*/ new Vector3();
- const _orientation = /*@__PURE__*/ new Vector3();
- class AudioListener extends Object3D {
- constructor() {
- super();
- this.type = 'AudioListener';
- this.context = AudioContext.getContext();
- this.gain = this.context.createGain();
- this.gain.connect( this.context.destination );
- this.filter = null;
- this.timeDelta = 0;
- // private
- this._clock = new Clock();
- }
- getInput() {
- return this.gain;
- }
- removeFilter() {
- if ( this.filter !== null ) {
- this.gain.disconnect( this.filter );
- this.filter.disconnect( this.context.destination );
- this.gain.connect( this.context.destination );
- this.filter = null;
- }
- return this;
- }
- getFilter() {
- return this.filter;
- }
- setFilter( value ) {
- if ( this.filter !== null ) {
- this.gain.disconnect( this.filter );
- this.filter.disconnect( this.context.destination );
- } else {
- this.gain.disconnect( this.context.destination );
- }
- this.filter = value;
- this.gain.connect( this.filter );
- this.filter.connect( this.context.destination );
- return this;
- }
- getMasterVolume() {
- return this.gain.gain.value;
- }
- setMasterVolume( value ) {
- this.gain.gain.setTargetAtTime( value, this.context.currentTime, 0.01 );
- return this;
- }
- updateMatrixWorld( force ) {
- super.updateMatrixWorld( force );
- const listener = this.context.listener;
- const up = this.up;
- this.timeDelta = this._clock.getDelta();
- this.matrixWorld.decompose( _position$2, _quaternion$3, _scale$1 );
- _orientation.set( 0, 0, - 1 ).applyQuaternion( _quaternion$3 );
- if ( listener.positionX ) {
- // code path for Chrome (see #14393)
- const endTime = this.context.currentTime + this.timeDelta;
- listener.positionX.linearRampToValueAtTime( _position$2.x, endTime );
- listener.positionY.linearRampToValueAtTime( _position$2.y, endTime );
- listener.positionZ.linearRampToValueAtTime( _position$2.z, endTime );
- listener.forwardX.linearRampToValueAtTime( _orientation.x, endTime );
- listener.forwardY.linearRampToValueAtTime( _orientation.y, endTime );
- listener.forwardZ.linearRampToValueAtTime( _orientation.z, endTime );
- listener.upX.linearRampToValueAtTime( up.x, endTime );
- listener.upY.linearRampToValueAtTime( up.y, endTime );
- listener.upZ.linearRampToValueAtTime( up.z, endTime );
- } else {
- listener.setPosition( _position$2.x, _position$2.y, _position$2.z );
- listener.setOrientation( _orientation.x, _orientation.y, _orientation.z, up.x, up.y, up.z );
- }
- }
- }
- class Audio extends Object3D {
- constructor( listener ) {
- super();
- this.type = 'Audio';
- this.listener = listener;
- this.context = listener.context;
- this.gain = this.context.createGain();
- this.gain.connect( listener.getInput() );
- this.autoplay = false;
- this.buffer = null;
- this.detune = 0;
- this.loop = false;
- this.loopStart = 0;
- this.loopEnd = 0;
- this.offset = 0;
- this.duration = undefined;
- this.playbackRate = 1;
- this.isPlaying = false;
- this.hasPlaybackControl = true;
- this.source = null;
- this.sourceType = 'empty';
- this._startedAt = 0;
- this._progress = 0;
- this._connected = false;
- this.filters = [];
- }
- getOutput() {
- return this.gain;
- }
- setNodeSource( audioNode ) {
- this.hasPlaybackControl = false;
- this.sourceType = 'audioNode';
- this.source = audioNode;
- this.connect();
- return this;
- }
- setMediaElementSource( mediaElement ) {
- this.hasPlaybackControl = false;
- this.sourceType = 'mediaNode';
- this.source = this.context.createMediaElementSource( mediaElement );
- this.connect();
- return this;
- }
- setMediaStreamSource( mediaStream ) {
- this.hasPlaybackControl = false;
- this.sourceType = 'mediaStreamNode';
- this.source = this.context.createMediaStreamSource( mediaStream );
- this.connect();
- return this;
- }
- setBuffer( audioBuffer ) {
- this.buffer = audioBuffer;
- this.sourceType = 'buffer';
- if ( this.autoplay ) this.play();
- return this;
- }
- play( delay = 0 ) {
- if ( this.isPlaying === true ) {
- console.warn( 'THREE.Audio: Audio is already playing.' );
- return;
- }
- if ( this.hasPlaybackControl === false ) {
- console.warn( 'THREE.Audio: this Audio has no playback control.' );
- return;
- }
- this._startedAt = this.context.currentTime + delay;
- const source = this.context.createBufferSource();
- source.buffer = this.buffer;
- source.loop = this.loop;
- source.loopStart = this.loopStart;
- source.loopEnd = this.loopEnd;
- source.onended = this.onEnded.bind( this );
- source.start( this._startedAt, this._progress + this.offset, this.duration );
- this.isPlaying = true;
- this.source = source;
- this.setDetune( this.detune );
- this.setPlaybackRate( this.playbackRate );
- return this.connect();
- }
- pause() {
- if ( this.hasPlaybackControl === false ) {
- console.warn( 'THREE.Audio: this Audio has no playback control.' );
- return;
- }
- if ( this.isPlaying === true ) {
- // update current progress
- this._progress += Math.max( this.context.currentTime - this._startedAt, 0 ) * this.playbackRate;
- if ( this.loop === true ) {
- // ensure _progress does not exceed duration with looped audios
- this._progress = this._progress % ( this.duration || this.buffer.duration );
- }
- this.source.stop();
- this.source.onended = null;
- this.isPlaying = false;
- }
- return this;
- }
- stop() {
- if ( this.hasPlaybackControl === false ) {
- console.warn( 'THREE.Audio: this Audio has no playback control.' );
- return;
- }
- this._progress = 0;
- this.source.stop();
- this.source.onended = null;
- this.isPlaying = false;
- return this;
- }
- connect() {
- if ( this.filters.length > 0 ) {
- this.source.connect( this.filters[ 0 ] );
- for ( let i = 1, l = this.filters.length; i < l; i ++ ) {
- this.filters[ i - 1 ].connect( this.filters[ i ] );
- }
- this.filters[ this.filters.length - 1 ].connect( this.getOutput() );
- } else {
- this.source.connect( this.getOutput() );
- }
- this._connected = true;
- return this;
- }
- disconnect() {
- if ( this.filters.length > 0 ) {
- this.source.disconnect( this.filters[ 0 ] );
- for ( let i = 1, l = this.filters.length; i < l; i ++ ) {
- this.filters[ i - 1 ].disconnect( this.filters[ i ] );
- }
- this.filters[ this.filters.length - 1 ].disconnect( this.getOutput() );
- } else {
- this.source.disconnect( this.getOutput() );
- }
- this._connected = false;
- return this;
- }
- getFilters() {
- return this.filters;
- }
- setFilters( value ) {
- if ( ! value ) value = [];
- if ( this._connected === true ) {
- this.disconnect();
- this.filters = value.slice();
- this.connect();
- } else {
- this.filters = value.slice();
- }
- return this;
- }
- setDetune( value ) {
- this.detune = value;
- if ( this.source.detune === undefined ) return; // only set detune when available
- if ( this.isPlaying === true ) {
- this.source.detune.setTargetAtTime( this.detune, this.context.currentTime, 0.01 );
- }
- return this;
- }
- getDetune() {
- return this.detune;
- }
- getFilter() {
- return this.getFilters()[ 0 ];
- }
- setFilter( filter ) {
- return this.setFilters( filter ? [ filter ] : [] );
- }
- setPlaybackRate( value ) {
- if ( this.hasPlaybackControl === false ) {
- console.warn( 'THREE.Audio: this Audio has no playback control.' );
- return;
- }
- this.playbackRate = value;
- if ( this.isPlaying === true ) {
- this.source.playbackRate.setTargetAtTime( this.playbackRate, this.context.currentTime, 0.01 );
- }
- return this;
- }
- getPlaybackRate() {
- return this.playbackRate;
- }
- onEnded() {
- this.isPlaying = false;
- }
- getLoop() {
- if ( this.hasPlaybackControl === false ) {
- console.warn( 'THREE.Audio: this Audio has no playback control.' );
- return false;
- }
- return this.loop;
- }
- setLoop( value ) {
- if ( this.hasPlaybackControl === false ) {
- console.warn( 'THREE.Audio: this Audio has no playback control.' );
- return;
- }
- this.loop = value;
- if ( this.isPlaying === true ) {
- this.source.loop = this.loop;
- }
- return this;
- }
- setLoopStart( value ) {
- this.loopStart = value;
- return this;
- }
- setLoopEnd( value ) {
- this.loopEnd = value;
- return this;
- }
- getVolume() {
- return this.gain.gain.value;
- }
- setVolume( value ) {
- this.gain.gain.setTargetAtTime( value, this.context.currentTime, 0.01 );
- return this;
- }
- }
- const _position$3 = /*@__PURE__*/ new Vector3();
- const _quaternion$4 = /*@__PURE__*/ new Quaternion();
- const _scale$2 = /*@__PURE__*/ new Vector3();
- const _orientation$1 = /*@__PURE__*/ new Vector3();
- class PositionalAudio extends Audio {
- constructor( listener ) {
- super( listener );
- this.panner = this.context.createPanner();
- this.panner.panningModel = 'HRTF';
- this.panner.connect( this.gain );
- }
- getOutput() {
- return this.panner;
- }
- getRefDistance() {
- return this.panner.refDistance;
- }
- setRefDistance( value ) {
- this.panner.refDistance = value;
- return this;
- }
- getRolloffFactor() {
- return this.panner.rolloffFactor;
- }
- setRolloffFactor( value ) {
- this.panner.rolloffFactor = value;
- return this;
- }
- getDistanceModel() {
- return this.panner.distanceModel;
- }
- setDistanceModel( value ) {
- this.panner.distanceModel = value;
- return this;
- }
- getMaxDistance() {
- return this.panner.maxDistance;
- }
- setMaxDistance( value ) {
- this.panner.maxDistance = value;
- return this;
- }
- setDirectionalCone( coneInnerAngle, coneOuterAngle, coneOuterGain ) {
- this.panner.coneInnerAngle = coneInnerAngle;
- this.panner.coneOuterAngle = coneOuterAngle;
- this.panner.coneOuterGain = coneOuterGain;
- return this;
- }
- updateMatrixWorld( force ) {
- super.updateMatrixWorld( force );
- if ( this.hasPlaybackControl === true && this.isPlaying === false ) return;
- this.matrixWorld.decompose( _position$3, _quaternion$4, _scale$2 );
- _orientation$1.set( 0, 0, 1 ).applyQuaternion( _quaternion$4 );
- const panner = this.panner;
- if ( panner.positionX ) {
- // code path for Chrome and Firefox (see #14393)
- const endTime = this.context.currentTime + this.listener.timeDelta;
- panner.positionX.linearRampToValueAtTime( _position$3.x, endTime );
- panner.positionY.linearRampToValueAtTime( _position$3.y, endTime );
- panner.positionZ.linearRampToValueAtTime( _position$3.z, endTime );
- panner.orientationX.linearRampToValueAtTime( _orientation$1.x, endTime );
- panner.orientationY.linearRampToValueAtTime( _orientation$1.y, endTime );
- panner.orientationZ.linearRampToValueAtTime( _orientation$1.z, endTime );
- } else {
- panner.setPosition( _position$3.x, _position$3.y, _position$3.z );
- panner.setOrientation( _orientation$1.x, _orientation$1.y, _orientation$1.z );
- }
- }
- }
- class AudioAnalyser {
- constructor( audio, fftSize = 2048 ) {
- this.analyser = audio.context.createAnalyser();
- this.analyser.fftSize = fftSize;
- this.data = new Uint8Array( this.analyser.frequencyBinCount );
- audio.getOutput().connect( this.analyser );
- }
- getFrequencyData() {
- this.analyser.getByteFrequencyData( this.data );
- return this.data;
- }
- getAverageFrequency() {
- let value = 0;
- const data = this.getFrequencyData();
- for ( let i = 0; i < data.length; i ++ ) {
- value += data[ i ];
- }
- return value / data.length;
- }
- }
- function PropertyMixer( binding, typeName, valueSize ) {
- this.binding = binding;
- this.valueSize = valueSize;
- let mixFunction,
- mixFunctionAdditive,
- setIdentity;
- // buffer layout: [ incoming | accu0 | accu1 | orig | addAccu | (optional work) ]
- //
- // interpolators can use .buffer as their .result
- // the data then goes to 'incoming'
- //
- // 'accu0' and 'accu1' are used frame-interleaved for
- // the cumulative result and are compared to detect
- // changes
- //
- // 'orig' stores the original state of the property
- //
- // 'add' is used for additive cumulative results
- //
- // 'work' is optional and is only present for quaternion types. It is used
- // to store intermediate quaternion multiplication results
- switch ( typeName ) {
- case 'quaternion':
- mixFunction = this._slerp;
- mixFunctionAdditive = this._slerpAdditive;
- setIdentity = this._setAdditiveIdentityQuaternion;
- this.buffer = new Float64Array( valueSize * 6 );
- this._workIndex = 5;
- break;
- case 'string':
- case 'bool':
- mixFunction = this._select;
- // Use the regular mix function and for additive on these types,
- // additive is not relevant for non-numeric types
- mixFunctionAdditive = this._select;
- setIdentity = this._setAdditiveIdentityOther;
- this.buffer = new Array( valueSize * 5 );
- break;
- default:
- mixFunction = this._lerp;
- mixFunctionAdditive = this._lerpAdditive;
- setIdentity = this._setAdditiveIdentityNumeric;
- this.buffer = new Float64Array( valueSize * 5 );
- }
- this._mixBufferRegion = mixFunction;
- this._mixBufferRegionAdditive = mixFunctionAdditive;
- this._setIdentity = setIdentity;
- this._origIndex = 3;
- this._addIndex = 4;
- this.cumulativeWeight = 0;
- this.cumulativeWeightAdditive = 0;
- this.useCount = 0;
- this.referenceCount = 0;
- }
- Object.assign( PropertyMixer.prototype, {
- // accumulate data in the 'incoming' region into 'accu<i>'
- accumulate: function ( accuIndex, weight ) {
- // note: happily accumulating nothing when weight = 0, the caller knows
- // the weight and shouldn't have made the call in the first place
- const buffer = this.buffer,
- stride = this.valueSize,
- offset = accuIndex * stride + stride;
- let currentWeight = this.cumulativeWeight;
- if ( currentWeight === 0 ) {
- // accuN := incoming * weight
- for ( let i = 0; i !== stride; ++ i ) {
- buffer[ offset + i ] = buffer[ i ];
- }
- currentWeight = weight;
- } else {
- // accuN := accuN + incoming * weight
- currentWeight += weight;
- const mix = weight / currentWeight;
- this._mixBufferRegion( buffer, offset, 0, mix, stride );
- }
- this.cumulativeWeight = currentWeight;
- },
- // accumulate data in the 'incoming' region into 'add'
- accumulateAdditive: function ( weight ) {
- const buffer = this.buffer,
- stride = this.valueSize,
- offset = stride * this._addIndex;
- if ( this.cumulativeWeightAdditive === 0 ) {
- // add = identity
- this._setIdentity();
- }
- // add := add + incoming * weight
- this._mixBufferRegionAdditive( buffer, offset, 0, weight, stride );
- this.cumulativeWeightAdditive += weight;
- },
- // apply the state of 'accu<i>' to the binding when accus differ
- apply: function ( accuIndex ) {
- const stride = this.valueSize,
- buffer = this.buffer,
- offset = accuIndex * stride + stride,
- weight = this.cumulativeWeight,
- weightAdditive = this.cumulativeWeightAdditive,
- binding = this.binding;
- this.cumulativeWeight = 0;
- this.cumulativeWeightAdditive = 0;
- if ( weight < 1 ) {
- // accuN := accuN + original * ( 1 - cumulativeWeight )
- const originalValueOffset = stride * this._origIndex;
- this._mixBufferRegion(
- buffer, offset, originalValueOffset, 1 - weight, stride );
- }
- if ( weightAdditive > 0 ) {
- // accuN := accuN + additive accuN
- this._mixBufferRegionAdditive( buffer, offset, this._addIndex * stride, 1, stride );
- }
- for ( let i = stride, e = stride + stride; i !== e; ++ i ) {
- if ( buffer[ i ] !== buffer[ i + stride ] ) {
- // value has changed -> update scene graph
- binding.setValue( buffer, offset );
- break;
- }
- }
- },
- // remember the state of the bound property and copy it to both accus
- saveOriginalState: function () {
- const binding = this.binding;
- const buffer = this.buffer,
- stride = this.valueSize,
- originalValueOffset = stride * this._origIndex;
- binding.getValue( buffer, originalValueOffset );
- // accu[0..1] := orig -- initially detect changes against the original
- for ( let i = stride, e = originalValueOffset; i !== e; ++ i ) {
- buffer[ i ] = buffer[ originalValueOffset + ( i % stride ) ];
- }
- // Add to identity for additive
- this._setIdentity();
- this.cumulativeWeight = 0;
- this.cumulativeWeightAdditive = 0;
- },
- // apply the state previously taken via 'saveOriginalState' to the binding
- restoreOriginalState: function () {
- const originalValueOffset = this.valueSize * 3;
- this.binding.setValue( this.buffer, originalValueOffset );
- },
- _setAdditiveIdentityNumeric: function () {
- const startIndex = this._addIndex * this.valueSize;
- const endIndex = startIndex + this.valueSize;
- for ( let i = startIndex; i < endIndex; i ++ ) {
- this.buffer[ i ] = 0;
- }
- },
- _setAdditiveIdentityQuaternion: function () {
- this._setAdditiveIdentityNumeric();
- this.buffer[ this._addIndex * this.valueSize + 3 ] = 1;
- },
- _setAdditiveIdentityOther: function () {
- const startIndex = this._origIndex * this.valueSize;
- const targetIndex = this._addIndex * this.valueSize;
- for ( let i = 0; i < this.valueSize; i ++ ) {
- this.buffer[ targetIndex + i ] = this.buffer[ startIndex + i ];
- }
- },
- // mix functions
- _select: function ( buffer, dstOffset, srcOffset, t, stride ) {
- if ( t >= 0.5 ) {
- for ( let i = 0; i !== stride; ++ i ) {
- buffer[ dstOffset + i ] = buffer[ srcOffset + i ];
- }
- }
- },
- _slerp: function ( buffer, dstOffset, srcOffset, t ) {
- Quaternion.slerpFlat( buffer, dstOffset, buffer, dstOffset, buffer, srcOffset, t );
- },
- _slerpAdditive: function ( buffer, dstOffset, srcOffset, t, stride ) {
- const workOffset = this._workIndex * stride;
- // Store result in intermediate buffer offset
- Quaternion.multiplyQuaternionsFlat( buffer, workOffset, buffer, dstOffset, buffer, srcOffset );
- // Slerp to the intermediate result
- Quaternion.slerpFlat( buffer, dstOffset, buffer, dstOffset, buffer, workOffset, t );
- },
- _lerp: function ( buffer, dstOffset, srcOffset, t, stride ) {
- const s = 1 - t;
- for ( let i = 0; i !== stride; ++ i ) {
- const j = dstOffset + i;
- buffer[ j ] = buffer[ j ] * s + buffer[ srcOffset + i ] * t;
- }
- },
- _lerpAdditive: function ( buffer, dstOffset, srcOffset, t, stride ) {
- for ( let i = 0; i !== stride; ++ i ) {
- const j = dstOffset + i;
- buffer[ j ] = buffer[ j ] + buffer[ srcOffset + i ] * t;
- }
- }
- } );
- // Characters [].:/ are reserved for track binding syntax.
- const _RESERVED_CHARS_RE = '\\[\\]\\.:\\/';
- const _reservedRe = new RegExp( '[' + _RESERVED_CHARS_RE + ']', 'g' );
- // Attempts to allow node names from any language. ES5's `\w` regexp matches
- // only latin characters, and the unicode \p{L} is not yet supported. So
- // instead, we exclude reserved characters and match everything else.
- const _wordChar = '[^' + _RESERVED_CHARS_RE + ']';
- const _wordCharOrDot = '[^' + _RESERVED_CHARS_RE.replace( '\\.', '' ) + ']';
- // Parent directories, delimited by '/' or ':'. Currently unused, but must
- // be matched to parse the rest of the track name.
- const _directoryRe = /((?:WC+[\/:])*)/.source.replace( 'WC', _wordChar );
- // Target node. May contain word characters (a-zA-Z0-9_) and '.' or '-'.
- const _nodeRe = /(WCOD+)?/.source.replace( 'WCOD', _wordCharOrDot );
- // Object on target node, and accessor. May not contain reserved
- // characters. Accessor may contain any character except closing bracket.
- const _objectRe = /(?:\.(WC+)(?:\[(.+)\])?)?/.source.replace( 'WC', _wordChar );
- // Property and accessor. May not contain reserved characters. Accessor may
- // contain any non-bracket characters.
- const _propertyRe = /\.(WC+)(?:\[(.+)\])?/.source.replace( 'WC', _wordChar );
- const _trackRe = new RegExp( ''
- + '^'
- + _directoryRe
- + _nodeRe
- + _objectRe
- + _propertyRe
- + '$'
- );
- const _supportedObjectNames = [ 'material', 'materials', 'bones' ];
- function Composite( targetGroup, path, optionalParsedPath ) {
- const parsedPath = optionalParsedPath || PropertyBinding.parseTrackName( path );
- this._targetGroup = targetGroup;
- this._bindings = targetGroup.subscribe_( path, parsedPath );
- }
- Object.assign( Composite.prototype, {
- getValue: function ( array, offset ) {
- this.bind(); // bind all binding
- const firstValidIndex = this._targetGroup.nCachedObjects_,
- binding = this._bindings[ firstValidIndex ];
- // and only call .getValue on the first
- if ( binding !== undefined ) binding.getValue( array, offset );
- },
- setValue: function ( array, offset ) {
- const bindings = this._bindings;
- for ( let i = this._targetGroup.nCachedObjects_, n = bindings.length; i !== n; ++ i ) {
- bindings[ i ].setValue( array, offset );
- }
- },
- bind: function () {
- const bindings = this._bindings;
- for ( let i = this._targetGroup.nCachedObjects_, n = bindings.length; i !== n; ++ i ) {
- bindings[ i ].bind();
- }
- },
- unbind: function () {
- const bindings = this._bindings;
- for ( let i = this._targetGroup.nCachedObjects_, n = bindings.length; i !== n; ++ i ) {
- bindings[ i ].unbind();
- }
- }
- } );
- function PropertyBinding( rootNode, path, parsedPath ) {
- this.path = path;
- this.parsedPath = parsedPath || PropertyBinding.parseTrackName( path );
- this.node = PropertyBinding.findNode( rootNode, this.parsedPath.nodeName ) || rootNode;
- this.rootNode = rootNode;
- }
- Object.assign( PropertyBinding, {
- Composite: Composite,
- create: function ( root, path, parsedPath ) {
- if ( ! ( root && root.isAnimationObjectGroup ) ) {
- return new PropertyBinding( root, path, parsedPath );
- } else {
- return new PropertyBinding.Composite( root, path, parsedPath );
- }
- },
- /**
- * Replaces spaces with underscores and removes unsupported characters from
- * node names, to ensure compatibility with parseTrackName().
- *
- * @param {string} name Node name to be sanitized.
- * @return {string}
- */
- sanitizeNodeName: function ( name ) {
- return name.replace( /\s/g, '_' ).replace( _reservedRe, '' );
- },
- parseTrackName: function ( trackName ) {
- const matches = _trackRe.exec( trackName );
- if ( ! matches ) {
- throw new Error( 'PropertyBinding: Cannot parse trackName: ' + trackName );
- }
- const results = {
- // directoryName: matches[ 1 ], // (tschw) currently unused
- nodeName: matches[ 2 ],
- objectName: matches[ 3 ],
- objectIndex: matches[ 4 ],
- propertyName: matches[ 5 ], // required
- propertyIndex: matches[ 6 ]
- };
- const lastDot = results.nodeName && results.nodeName.lastIndexOf( '.' );
- if ( lastDot !== undefined && lastDot !== - 1 ) {
- const objectName = results.nodeName.substring( lastDot + 1 );
- // Object names must be checked against an allowlist. Otherwise, there
- // is no way to parse 'foo.bar.baz': 'baz' must be a property, but
- // 'bar' could be the objectName, or part of a nodeName (which can
- // include '.' characters).
- if ( _supportedObjectNames.indexOf( objectName ) !== - 1 ) {
- results.nodeName = results.nodeName.substring( 0, lastDot );
- results.objectName = objectName;
- }
- }
- if ( results.propertyName === null || results.propertyName.length === 0 ) {
- throw new Error( 'PropertyBinding: can not parse propertyName from trackName: ' + trackName );
- }
- return results;
- },
- findNode: function ( root, nodeName ) {
- if ( ! nodeName || nodeName === '' || nodeName === '.' || nodeName === - 1 || nodeName === root.name || nodeName === root.uuid ) {
- return root;
- }
- // search into skeleton bones.
- if ( root.skeleton ) {
- const bone = root.skeleton.getBoneByName( nodeName );
- if ( bone !== undefined ) {
- return bone;
- }
- }
- // search into node subtree.
- if ( root.children ) {
- const searchNodeSubtree = function ( children ) {
- for ( let i = 0; i < children.length; i ++ ) {
- const childNode = children[ i ];
- if ( childNode.name === nodeName || childNode.uuid === nodeName ) {
- return childNode;
- }
- const result = searchNodeSubtree( childNode.children );
- if ( result ) return result;
- }
- return null;
- };
- const subTreeNode = searchNodeSubtree( root.children );
- if ( subTreeNode ) {
- return subTreeNode;
- }
- }
- return null;
- }
- } );
- Object.assign( PropertyBinding.prototype, { // prototype, continued
- // these are used to "bind" a nonexistent property
- _getValue_unavailable: function () {},
- _setValue_unavailable: function () {},
- BindingType: {
- Direct: 0,
- EntireArray: 1,
- ArrayElement: 2,
- HasFromToArray: 3
- },
- Versioning: {
- None: 0,
- NeedsUpdate: 1,
- MatrixWorldNeedsUpdate: 2
- },
- GetterByBindingType: [
- function getValue_direct( buffer, offset ) {
- buffer[ offset ] = this.node[ this.propertyName ];
- },
- function getValue_array( buffer, offset ) {
- const source = this.resolvedProperty;
- for ( let i = 0, n = source.length; i !== n; ++ i ) {
- buffer[ offset ++ ] = source[ i ];
- }
- },
- function getValue_arrayElement( buffer, offset ) {
- buffer[ offset ] = this.resolvedProperty[ this.propertyIndex ];
- },
- function getValue_toArray( buffer, offset ) {
- this.resolvedProperty.toArray( buffer, offset );
- }
- ],
- SetterByBindingTypeAndVersioning: [
- [
- // Direct
- function setValue_direct( buffer, offset ) {
- this.targetObject[ this.propertyName ] = buffer[ offset ];
- },
- function setValue_direct_setNeedsUpdate( buffer, offset ) {
- this.targetObject[ this.propertyName ] = buffer[ offset ];
- this.targetObject.needsUpdate = true;
- },
- function setValue_direct_setMatrixWorldNeedsUpdate( buffer, offset ) {
- this.targetObject[ this.propertyName ] = buffer[ offset ];
- this.targetObject.matrixWorldNeedsUpdate = true;
- }
- ], [
- // EntireArray
- function setValue_array( buffer, offset ) {
- const dest = this.resolvedProperty;
- for ( let i = 0, n = dest.length; i !== n; ++ i ) {
- dest[ i ] = buffer[ offset ++ ];
- }
- },
- function setValue_array_setNeedsUpdate( buffer, offset ) {
- const dest = this.resolvedProperty;
- for ( let i = 0, n = dest.length; i !== n; ++ i ) {
- dest[ i ] = buffer[ offset ++ ];
- }
- this.targetObject.needsUpdate = true;
- },
- function setValue_array_setMatrixWorldNeedsUpdate( buffer, offset ) {
- const dest = this.resolvedProperty;
- for ( let i = 0, n = dest.length; i !== n; ++ i ) {
- dest[ i ] = buffer[ offset ++ ];
- }
- this.targetObject.matrixWorldNeedsUpdate = true;
- }
- ], [
- // ArrayElement
- function setValue_arrayElement( buffer, offset ) {
- this.resolvedProperty[ this.propertyIndex ] = buffer[ offset ];
- },
- function setValue_arrayElement_setNeedsUpdate( buffer, offset ) {
- this.resolvedProperty[ this.propertyIndex ] = buffer[ offset ];
- this.targetObject.needsUpdate = true;
- },
- function setValue_arrayElement_setMatrixWorldNeedsUpdate( buffer, offset ) {
- this.resolvedProperty[ this.propertyIndex ] = buffer[ offset ];
- this.targetObject.matrixWorldNeedsUpdate = true;
- }
- ], [
- // HasToFromArray
- function setValue_fromArray( buffer, offset ) {
- this.resolvedProperty.fromArray( buffer, offset );
- },
- function setValue_fromArray_setNeedsUpdate( buffer, offset ) {
- this.resolvedProperty.fromArray( buffer, offset );
- this.targetObject.needsUpdate = true;
- },
- function setValue_fromArray_setMatrixWorldNeedsUpdate( buffer, offset ) {
- this.resolvedProperty.fromArray( buffer, offset );
- this.targetObject.matrixWorldNeedsUpdate = true;
- }
- ]
- ],
- getValue: function getValue_unbound( targetArray, offset ) {
- this.bind();
- this.getValue( targetArray, offset );
- // Note: This class uses a State pattern on a per-method basis:
- // 'bind' sets 'this.getValue' / 'setValue' and shadows the
- // prototype version of these methods with one that represents
- // the bound state. When the property is not found, the methods
- // become no-ops.
- },
- setValue: function getValue_unbound( sourceArray, offset ) {
- this.bind();
- this.setValue( sourceArray, offset );
- },
- // create getter / setter pair for a property in the scene graph
- bind: function () {
- let targetObject = this.node;
- const parsedPath = this.parsedPath;
- const objectName = parsedPath.objectName;
- const propertyName = parsedPath.propertyName;
- let propertyIndex = parsedPath.propertyIndex;
- if ( ! targetObject ) {
- targetObject = PropertyBinding.findNode( this.rootNode, parsedPath.nodeName ) || this.rootNode;
- this.node = targetObject;
- }
- // set fail state so we can just 'return' on error
- this.getValue = this._getValue_unavailable;
- this.setValue = this._setValue_unavailable;
- // ensure there is a value node
- if ( ! targetObject ) {
- console.error( 'THREE.PropertyBinding: Trying to update node for track: ' + this.path + ' but it wasn\'t found.' );
- return;
- }
- if ( objectName ) {
- let objectIndex = parsedPath.objectIndex;
- // special cases were we need to reach deeper into the hierarchy to get the face materials....
- switch ( objectName ) {
- case 'materials':
- if ( ! targetObject.material ) {
- console.error( 'THREE.PropertyBinding: Can not bind to material as node does not have a material.', this );
- return;
- }
- if ( ! targetObject.material.materials ) {
- console.error( 'THREE.PropertyBinding: Can not bind to material.materials as node.material does not have a materials array.', this );
- return;
- }
- targetObject = targetObject.material.materials;
- break;
- case 'bones':
- if ( ! targetObject.skeleton ) {
- console.error( 'THREE.PropertyBinding: Can not bind to bones as node does not have a skeleton.', this );
- return;
- }
- // potential future optimization: skip this if propertyIndex is already an integer
- // and convert the integer string to a true integer.
- targetObject = targetObject.skeleton.bones;
- // support resolving morphTarget names into indices.
- for ( let i = 0; i < targetObject.length; i ++ ) {
- if ( targetObject[ i ].name === objectIndex ) {
- objectIndex = i;
- break;
- }
- }
- break;
- default:
- if ( targetObject[ objectName ] === undefined ) {
- console.error( 'THREE.PropertyBinding: Can not bind to objectName of node undefined.', this );
- return;
- }
- targetObject = targetObject[ objectName ];
- }
- if ( objectIndex !== undefined ) {
- if ( targetObject[ objectIndex ] === undefined ) {
- console.error( 'THREE.PropertyBinding: Trying to bind to objectIndex of objectName, but is undefined.', this, targetObject );
- return;
- }
- targetObject = targetObject[ objectIndex ];
- }
- }
- // resolve property
- const nodeProperty = targetObject[ propertyName ];
- if ( nodeProperty === undefined ) {
- const nodeName = parsedPath.nodeName;
- console.error( 'THREE.PropertyBinding: Trying to update property for track: ' + nodeName +
- '.' + propertyName + ' but it wasn\'t found.', targetObject );
- return;
- }
- // determine versioning scheme
- let versioning = this.Versioning.None;
- this.targetObject = targetObject;
- if ( targetObject.needsUpdate !== undefined ) { // material
- versioning = this.Versioning.NeedsUpdate;
- } else if ( targetObject.matrixWorldNeedsUpdate !== undefined ) { // node transform
- versioning = this.Versioning.MatrixWorldNeedsUpdate;
- }
- // determine how the property gets bound
- let bindingType = this.BindingType.Direct;
- if ( propertyIndex !== undefined ) {
- // access a sub element of the property array (only primitives are supported right now)
- if ( propertyName === 'morphTargetInfluences' ) {
- // potential optimization, skip this if propertyIndex is already an integer, and convert the integer string to a true integer.
- // support resolving morphTarget names into indices.
- if ( ! targetObject.geometry ) {
- console.error( 'THREE.PropertyBinding: Can not bind to morphTargetInfluences because node does not have a geometry.', this );
- return;
- }
- if ( targetObject.geometry.isBufferGeometry ) {
- if ( ! targetObject.geometry.morphAttributes ) {
- console.error( 'THREE.PropertyBinding: Can not bind to morphTargetInfluences because node does not have a geometry.morphAttributes.', this );
- return;
- }
- if ( targetObject.morphTargetDictionary[ propertyIndex ] !== undefined ) {
- propertyIndex = targetObject.morphTargetDictionary[ propertyIndex ];
- }
- } else {
- console.error( 'THREE.PropertyBinding: Can not bind to morphTargetInfluences on THREE.Geometry. Use THREE.BufferGeometry instead.', this );
- return;
- }
- }
- bindingType = this.BindingType.ArrayElement;
- this.resolvedProperty = nodeProperty;
- this.propertyIndex = propertyIndex;
- } else if ( nodeProperty.fromArray !== undefined && nodeProperty.toArray !== undefined ) {
- // must use copy for Object3D.Euler/Quaternion
- bindingType = this.BindingType.HasFromToArray;
- this.resolvedProperty = nodeProperty;
- } else if ( Array.isArray( nodeProperty ) ) {
- bindingType = this.BindingType.EntireArray;
- this.resolvedProperty = nodeProperty;
- } else {
- this.propertyName = propertyName;
- }
- // select getter / setter
- this.getValue = this.GetterByBindingType[ bindingType ];
- this.setValue = this.SetterByBindingTypeAndVersioning[ bindingType ][ versioning ];
- },
- unbind: function () {
- this.node = null;
- // back to the prototype version of getValue / setValue
- // note: avoiding to mutate the shape of 'this' via 'delete'
- this.getValue = this._getValue_unbound;
- this.setValue = this._setValue_unbound;
- }
- } );
- // DECLARE ALIAS AFTER assign prototype
- Object.assign( PropertyBinding.prototype, {
- // initial state of these methods that calls 'bind'
- _getValue_unbound: PropertyBinding.prototype.getValue,
- _setValue_unbound: PropertyBinding.prototype.setValue,
- } );
- /**
- *
- * A group of objects that receives a shared animation state.
- *
- * Usage:
- *
- * - Add objects you would otherwise pass as 'root' to the
- * constructor or the .clipAction method of AnimationMixer.
- *
- * - Instead pass this object as 'root'.
- *
- * - You can also add and remove objects later when the mixer
- * is running.
- *
- * Note:
- *
- * Objects of this class appear as one object to the mixer,
- * so cache control of the individual objects must be done
- * on the group.
- *
- * Limitation:
- *
- * - The animated properties must be compatible among the
- * all objects in the group.
- *
- * - A single property can either be controlled through a
- * target group or directly, but not both.
- */
- function AnimationObjectGroup() {
- this.uuid = MathUtils.generateUUID();
- // cached objects followed by the active ones
- this._objects = Array.prototype.slice.call( arguments );
- this.nCachedObjects_ = 0; // threshold
- // note: read by PropertyBinding.Composite
- const indices = {};
- this._indicesByUUID = indices; // for bookkeeping
- for ( let i = 0, n = arguments.length; i !== n; ++ i ) {
- indices[ arguments[ i ].uuid ] = i;
- }
- this._paths = []; // inside: string
- this._parsedPaths = []; // inside: { we don't care, here }
- this._bindings = []; // inside: Array< PropertyBinding >
- this._bindingsIndicesByPath = {}; // inside: indices in these arrays
- const scope = this;
- this.stats = {
- objects: {
- get total() {
- return scope._objects.length;
- },
- get inUse() {
- return this.total - scope.nCachedObjects_;
- }
- },
- get bindingsPerObject() {
- return scope._bindings.length;
- }
- };
- }
- Object.assign( AnimationObjectGroup.prototype, {
- isAnimationObjectGroup: true,
- add: function () {
- const objects = this._objects,
- indicesByUUID = this._indicesByUUID,
- paths = this._paths,
- parsedPaths = this._parsedPaths,
- bindings = this._bindings,
- nBindings = bindings.length;
- let knownObject = undefined,
- nObjects = objects.length,
- nCachedObjects = this.nCachedObjects_;
- for ( let i = 0, n = arguments.length; i !== n; ++ i ) {
- const object = arguments[ i ],
- uuid = object.uuid;
- let index = indicesByUUID[ uuid ];
- if ( index === undefined ) {
- // unknown object -> add it to the ACTIVE region
- index = nObjects ++;
- indicesByUUID[ uuid ] = index;
- objects.push( object );
- // accounting is done, now do the same for all bindings
- for ( let j = 0, m = nBindings; j !== m; ++ j ) {
- bindings[ j ].push( new PropertyBinding( object, paths[ j ], parsedPaths[ j ] ) );
- }
- } else if ( index < nCachedObjects ) {
- knownObject = objects[ index ];
- // move existing object to the ACTIVE region
- const firstActiveIndex = -- nCachedObjects,
- lastCachedObject = objects[ firstActiveIndex ];
- indicesByUUID[ lastCachedObject.uuid ] = index;
- objects[ index ] = lastCachedObject;
- indicesByUUID[ uuid ] = firstActiveIndex;
- objects[ firstActiveIndex ] = object;
- // accounting is done, now do the same for all bindings
- for ( let j = 0, m = nBindings; j !== m; ++ j ) {
- const bindingsForPath = bindings[ j ],
- lastCached = bindingsForPath[ firstActiveIndex ];
- let binding = bindingsForPath[ index ];
- bindingsForPath[ index ] = lastCached;
- if ( binding === undefined ) {
- // since we do not bother to create new bindings
- // for objects that are cached, the binding may
- // or may not exist
- binding = new PropertyBinding( object, paths[ j ], parsedPaths[ j ] );
- }
- bindingsForPath[ firstActiveIndex ] = binding;
- }
- } else if ( objects[ index ] !== knownObject ) {
- console.error( 'THREE.AnimationObjectGroup: Different objects with the same UUID ' +
- 'detected. Clean the caches or recreate your infrastructure when reloading scenes.' );
- } // else the object is already where we want it to be
- } // for arguments
- this.nCachedObjects_ = nCachedObjects;
- },
- remove: function () {
- const objects = this._objects,
- indicesByUUID = this._indicesByUUID,
- bindings = this._bindings,
- nBindings = bindings.length;
- let nCachedObjects = this.nCachedObjects_;
- for ( let i = 0, n = arguments.length; i !== n; ++ i ) {
- const object = arguments[ i ],
- uuid = object.uuid,
- index = indicesByUUID[ uuid ];
- if ( index !== undefined && index >= nCachedObjects ) {
- // move existing object into the CACHED region
- const lastCachedIndex = nCachedObjects ++,
- firstActiveObject = objects[ lastCachedIndex ];
- indicesByUUID[ firstActiveObject.uuid ] = index;
- objects[ index ] = firstActiveObject;
- indicesByUUID[ uuid ] = lastCachedIndex;
- objects[ lastCachedIndex ] = object;
- // accounting is done, now do the same for all bindings
- for ( let j = 0, m = nBindings; j !== m; ++ j ) {
- const bindingsForPath = bindings[ j ],
- firstActive = bindingsForPath[ lastCachedIndex ],
- binding = bindingsForPath[ index ];
- bindingsForPath[ index ] = firstActive;
- bindingsForPath[ lastCachedIndex ] = binding;
- }
- }
- } // for arguments
- this.nCachedObjects_ = nCachedObjects;
- },
- // remove & forget
- uncache: function () {
- const objects = this._objects,
- indicesByUUID = this._indicesByUUID,
- bindings = this._bindings,
- nBindings = bindings.length;
- let nCachedObjects = this.nCachedObjects_,
- nObjects = objects.length;
- for ( let i = 0, n = arguments.length; i !== n; ++ i ) {
- const object = arguments[ i ],
- uuid = object.uuid,
- index = indicesByUUID[ uuid ];
- if ( index !== undefined ) {
- delete indicesByUUID[ uuid ];
- if ( index < nCachedObjects ) {
- // object is cached, shrink the CACHED region
- const firstActiveIndex = -- nCachedObjects,
- lastCachedObject = objects[ firstActiveIndex ],
- lastIndex = -- nObjects,
- lastObject = objects[ lastIndex ];
- // last cached object takes this object's place
- indicesByUUID[ lastCachedObject.uuid ] = index;
- objects[ index ] = lastCachedObject;
- // last object goes to the activated slot and pop
- indicesByUUID[ lastObject.uuid ] = firstActiveIndex;
- objects[ firstActiveIndex ] = lastObject;
- objects.pop();
- // accounting is done, now do the same for all bindings
- for ( let j = 0, m = nBindings; j !== m; ++ j ) {
- const bindingsForPath = bindings[ j ],
- lastCached = bindingsForPath[ firstActiveIndex ],
- last = bindingsForPath[ lastIndex ];
- bindingsForPath[ index ] = lastCached;
- bindingsForPath[ firstActiveIndex ] = last;
- bindingsForPath.pop();
- }
- } else {
- // object is active, just swap with the last and pop
- const lastIndex = -- nObjects,
- lastObject = objects[ lastIndex ];
- if ( lastIndex > 0 ) {
- indicesByUUID[ lastObject.uuid ] = index;
- }
- objects[ index ] = lastObject;
- objects.pop();
- // accounting is done, now do the same for all bindings
- for ( let j = 0, m = nBindings; j !== m; ++ j ) {
- const bindingsForPath = bindings[ j ];
- bindingsForPath[ index ] = bindingsForPath[ lastIndex ];
- bindingsForPath.pop();
- }
- } // cached or active
- } // if object is known
- } // for arguments
- this.nCachedObjects_ = nCachedObjects;
- },
- // Internal interface used by befriended PropertyBinding.Composite:
- subscribe_: function ( path, parsedPath ) {
- // returns an array of bindings for the given path that is changed
- // according to the contained objects in the group
- const indicesByPath = this._bindingsIndicesByPath;
- let index = indicesByPath[ path ];
- const bindings = this._bindings;
- if ( index !== undefined ) return bindings[ index ];
- const paths = this._paths,
- parsedPaths = this._parsedPaths,
- objects = this._objects,
- nObjects = objects.length,
- nCachedObjects = this.nCachedObjects_,
- bindingsForPath = new Array( nObjects );
- index = bindings.length;
- indicesByPath[ path ] = index;
- paths.push( path );
- parsedPaths.push( parsedPath );
- bindings.push( bindingsForPath );
- for ( let i = nCachedObjects, n = objects.length; i !== n; ++ i ) {
- const object = objects[ i ];
- bindingsForPath[ i ] = new PropertyBinding( object, path, parsedPath );
- }
- return bindingsForPath;
- },
- unsubscribe_: function ( path ) {
- // tells the group to forget about a property path and no longer
- // update the array previously obtained with 'subscribe_'
- const indicesByPath = this._bindingsIndicesByPath,
- index = indicesByPath[ path ];
- if ( index !== undefined ) {
- const paths = this._paths,
- parsedPaths = this._parsedPaths,
- bindings = this._bindings,
- lastBindingsIndex = bindings.length - 1,
- lastBindings = bindings[ lastBindingsIndex ],
- lastBindingsPath = path[ lastBindingsIndex ];
- indicesByPath[ lastBindingsPath ] = index;
- bindings[ index ] = lastBindings;
- bindings.pop();
- parsedPaths[ index ] = parsedPaths[ lastBindingsIndex ];
- parsedPaths.pop();
- paths[ index ] = paths[ lastBindingsIndex ];
- paths.pop();
- }
- }
- } );
- class AnimationAction {
- constructor( mixer, clip, localRoot = null, blendMode = clip.blendMode ) {
- this._mixer = mixer;
- this._clip = clip;
- this._localRoot = localRoot;
- this.blendMode = blendMode;
- const tracks = clip.tracks,
- nTracks = tracks.length,
- interpolants = new Array( nTracks );
- const interpolantSettings = {
- endingStart: ZeroCurvatureEnding,
- endingEnd: ZeroCurvatureEnding
- };
- for ( let i = 0; i !== nTracks; ++ i ) {
- const interpolant = tracks[ i ].createInterpolant( null );
- interpolants[ i ] = interpolant;
- interpolant.settings = interpolantSettings;
- }
- this._interpolantSettings = interpolantSettings;
- this._interpolants = interpolants; // bound by the mixer
- // inside: PropertyMixer (managed by the mixer)
- this._propertyBindings = new Array( nTracks );
- this._cacheIndex = null; // for the memory manager
- this._byClipCacheIndex = null; // for the memory manager
- this._timeScaleInterpolant = null;
- this._weightInterpolant = null;
- this.loop = LoopRepeat;
- this._loopCount = - 1;
- // global mixer time when the action is to be started
- // it's set back to 'null' upon start of the action
- this._startTime = null;
- // scaled local time of the action
- // gets clamped or wrapped to 0..clip.duration according to loop
- this.time = 0;
- this.timeScale = 1;
- this._effectiveTimeScale = 1;
- this.weight = 1;
- this._effectiveWeight = 1;
- this.repetitions = Infinity; // no. of repetitions when looping
- this.paused = false; // true -> zero effective time scale
- this.enabled = true; // false -> zero effective weight
- this.clampWhenFinished = false;// keep feeding the last frame?
- this.zeroSlopeAtStart = true;// for smooth interpolation w/o separate
- this.zeroSlopeAtEnd = true;// clips for start, loop and end
- }
- // State & Scheduling
- play() {
- this._mixer._activateAction( this );
- return this;
- }
- stop() {
- this._mixer._deactivateAction( this );
- return this.reset();
- }
- reset() {
- this.paused = false;
- this.enabled = true;
- this.time = 0; // restart clip
- this._loopCount = - 1;// forget previous loops
- this._startTime = null;// forget scheduling
- return this.stopFading().stopWarping();
- }
- isRunning() {
- return this.enabled && ! this.paused && this.timeScale !== 0 &&
- this._startTime === null && this._mixer._isActiveAction( this );
- }
- // return true when play has been called
- isScheduled() {
- return this._mixer._isActiveAction( this );
- }
- startAt( time ) {
- this._startTime = time;
- return this;
- }
- setLoop( mode, repetitions ) {
- this.loop = mode;
- this.repetitions = repetitions;
- return this;
- }
- // Weight
- // set the weight stopping any scheduled fading
- // although .enabled = false yields an effective weight of zero, this
- // method does *not* change .enabled, because it would be confusing
- setEffectiveWeight( weight ) {
- this.weight = weight;
- // note: same logic as when updated at runtime
- this._effectiveWeight = this.enabled ? weight : 0;
- return this.stopFading();
- }
- // return the weight considering fading and .enabled
- getEffectiveWeight() {
- return this._effectiveWeight;
- }
- fadeIn( duration ) {
- return this._scheduleFading( duration, 0, 1 );
- }
- fadeOut( duration ) {
- return this._scheduleFading( duration, 1, 0 );
- }
- crossFadeFrom( fadeOutAction, duration, warp ) {
- fadeOutAction.fadeOut( duration );
- this.fadeIn( duration );
- if ( warp ) {
- const fadeInDuration = this._clip.duration,
- fadeOutDuration = fadeOutAction._clip.duration,
- startEndRatio = fadeOutDuration / fadeInDuration,
- endStartRatio = fadeInDuration / fadeOutDuration;
- fadeOutAction.warp( 1.0, startEndRatio, duration );
- this.warp( endStartRatio, 1.0, duration );
- }
- return this;
- }
- crossFadeTo( fadeInAction, duration, warp ) {
- return fadeInAction.crossFadeFrom( this, duration, warp );
- }
- stopFading() {
- const weightInterpolant = this._weightInterpolant;
- if ( weightInterpolant !== null ) {
- this._weightInterpolant = null;
- this._mixer._takeBackControlInterpolant( weightInterpolant );
- }
- return this;
- }
- // Time Scale Control
- // set the time scale stopping any scheduled warping
- // although .paused = true yields an effective time scale of zero, this
- // method does *not* change .paused, because it would be confusing
- setEffectiveTimeScale( timeScale ) {
- this.timeScale = timeScale;
- this._effectiveTimeScale = this.paused ? 0 : timeScale;
- return this.stopWarping();
- }
- // return the time scale considering warping and .paused
- getEffectiveTimeScale() {
- return this._effectiveTimeScale;
- }
- setDuration( duration ) {
- this.timeScale = this._clip.duration / duration;
- return this.stopWarping();
- }
- syncWith( action ) {
- this.time = action.time;
- this.timeScale = action.timeScale;
- return this.stopWarping();
- }
- halt( duration ) {
- return this.warp( this._effectiveTimeScale, 0, duration );
- }
- warp( startTimeScale, endTimeScale, duration ) {
- const mixer = this._mixer,
- now = mixer.time,
- timeScale = this.timeScale;
- let interpolant = this._timeScaleInterpolant;
- if ( interpolant === null ) {
- interpolant = mixer._lendControlInterpolant();
- this._timeScaleInterpolant = interpolant;
- }
- const times = interpolant.parameterPositions,
- values = interpolant.sampleValues;
- times[ 0 ] = now;
- times[ 1 ] = now + duration;
- values[ 0 ] = startTimeScale / timeScale;
- values[ 1 ] = endTimeScale / timeScale;
- return this;
- }
- stopWarping() {
- const timeScaleInterpolant = this._timeScaleInterpolant;
- if ( timeScaleInterpolant !== null ) {
- this._timeScaleInterpolant = null;
- this._mixer._takeBackControlInterpolant( timeScaleInterpolant );
- }
- return this;
- }
- // Object Accessors
- getMixer() {
- return this._mixer;
- }
- getClip() {
- return this._clip;
- }
- getRoot() {
- return this._localRoot || this._mixer._root;
- }
- // Interna
- _update( time, deltaTime, timeDirection, accuIndex ) {
- // called by the mixer
- if ( ! this.enabled ) {
- // call ._updateWeight() to update ._effectiveWeight
- this._updateWeight( time );
- return;
- }
- const startTime = this._startTime;
- if ( startTime !== null ) {
- // check for scheduled start of action
- const timeRunning = ( time - startTime ) * timeDirection;
- if ( timeRunning < 0 || timeDirection === 0 ) {
- return; // yet to come / don't decide when delta = 0
- }
- // start
- this._startTime = null; // unschedule
- deltaTime = timeDirection * timeRunning;
- }
- // apply time scale and advance time
- deltaTime *= this._updateTimeScale( time );
- const clipTime = this._updateTime( deltaTime );
- // note: _updateTime may disable the action resulting in
- // an effective weight of 0
- const weight = this._updateWeight( time );
- if ( weight > 0 ) {
- const interpolants = this._interpolants;
- const propertyMixers = this._propertyBindings;
- switch ( this.blendMode ) {
- case AdditiveAnimationBlendMode:
- for ( let j = 0, m = interpolants.length; j !== m; ++ j ) {
- interpolants[ j ].evaluate( clipTime );
- propertyMixers[ j ].accumulateAdditive( weight );
- }
- break;
- case NormalAnimationBlendMode:
- default:
- for ( let j = 0, m = interpolants.length; j !== m; ++ j ) {
- interpolants[ j ].evaluate( clipTime );
- propertyMixers[ j ].accumulate( accuIndex, weight );
- }
- }
- }
- }
- _updateWeight( time ) {
- let weight = 0;
- if ( this.enabled ) {
- weight = this.weight;
- const interpolant = this._weightInterpolant;
- if ( interpolant !== null ) {
- const interpolantValue = interpolant.evaluate( time )[ 0 ];
- weight *= interpolantValue;
- if ( time > interpolant.parameterPositions[ 1 ] ) {
- this.stopFading();
- if ( interpolantValue === 0 ) {
- // faded out, disable
- this.enabled = false;
- }
- }
- }
- }
- this._effectiveWeight = weight;
- return weight;
- }
- _updateTimeScale( time ) {
- let timeScale = 0;
- if ( ! this.paused ) {
- timeScale = this.timeScale;
- const interpolant = this._timeScaleInterpolant;
- if ( interpolant !== null ) {
- const interpolantValue = interpolant.evaluate( time )[ 0 ];
- timeScale *= interpolantValue;
- if ( time > interpolant.parameterPositions[ 1 ] ) {
- this.stopWarping();
- if ( timeScale === 0 ) {
- // motion has halted, pause
- this.paused = true;
- } else {
- // warp done - apply final time scale
- this.timeScale = timeScale;
- }
- }
- }
- }
- this._effectiveTimeScale = timeScale;
- return timeScale;
- }
- _updateTime( deltaTime ) {
- const duration = this._clip.duration;
- const loop = this.loop;
- let time = this.time + deltaTime;
- let loopCount = this._loopCount;
- const pingPong = ( loop === LoopPingPong );
- if ( deltaTime === 0 ) {
- if ( loopCount === - 1 ) return time;
- return ( pingPong && ( loopCount & 1 ) === 1 ) ? duration - time : time;
- }
- if ( loop === LoopOnce ) {
- if ( loopCount === - 1 ) {
- // just started
- this._loopCount = 0;
- this._setEndings( true, true, false );
- }
- handle_stop: {
- if ( time >= duration ) {
- time = duration;
- } else if ( time < 0 ) {
- time = 0;
- } else {
- this.time = time;
- break handle_stop;
- }
- if ( this.clampWhenFinished ) this.paused = true;
- else this.enabled = false;
- this.time = time;
- this._mixer.dispatchEvent( {
- type: 'finished', action: this,
- direction: deltaTime < 0 ? - 1 : 1
- } );
- }
- } else { // repetitive Repeat or PingPong
- if ( loopCount === - 1 ) {
- // just started
- if ( deltaTime >= 0 ) {
- loopCount = 0;
- this._setEndings( true, this.repetitions === 0, pingPong );
- } else {
- // when looping in reverse direction, the initial
- // transition through zero counts as a repetition,
- // so leave loopCount at -1
- this._setEndings( this.repetitions === 0, true, pingPong );
- }
- }
- if ( time >= duration || time < 0 ) {
- // wrap around
- const loopDelta = Math.floor( time / duration ); // signed
- time -= duration * loopDelta;
- loopCount += Math.abs( loopDelta );
- const pending = this.repetitions - loopCount;
- if ( pending <= 0 ) {
- // have to stop (switch state, clamp time, fire event)
- if ( this.clampWhenFinished ) this.paused = true;
- else this.enabled = false;
- time = deltaTime > 0 ? duration : 0;
- this.time = time;
- this._mixer.dispatchEvent( {
- type: 'finished', action: this,
- direction: deltaTime > 0 ? 1 : - 1
- } );
- } else {
- // keep running
- if ( pending === 1 ) {
- // entering the last round
- const atStart = deltaTime < 0;
- this._setEndings( atStart, ! atStart, pingPong );
- } else {
- this._setEndings( false, false, pingPong );
- }
- this._loopCount = loopCount;
- this.time = time;
- this._mixer.dispatchEvent( {
- type: 'loop', action: this, loopDelta: loopDelta
- } );
- }
- } else {
- this.time = time;
- }
- if ( pingPong && ( loopCount & 1 ) === 1 ) {
- // invert time for the "pong round"
- return duration - time;
- }
- }
- return time;
- }
- _setEndings( atStart, atEnd, pingPong ) {
- const settings = this._interpolantSettings;
- if ( pingPong ) {
- settings.endingStart = ZeroSlopeEnding;
- settings.endingEnd = ZeroSlopeEnding;
- } else {
- // assuming for LoopOnce atStart == atEnd == true
- if ( atStart ) {
- settings.endingStart = this.zeroSlopeAtStart ? ZeroSlopeEnding : ZeroCurvatureEnding;
- } else {
- settings.endingStart = WrapAroundEnding;
- }
- if ( atEnd ) {
- settings.endingEnd = this.zeroSlopeAtEnd ? ZeroSlopeEnding : ZeroCurvatureEnding;
- } else {
- settings.endingEnd = WrapAroundEnding;
- }
- }
- }
- _scheduleFading( duration, weightNow, weightThen ) {
- const mixer = this._mixer, now = mixer.time;
- let interpolant = this._weightInterpolant;
- if ( interpolant === null ) {
- interpolant = mixer._lendControlInterpolant();
- this._weightInterpolant = interpolant;
- }
- const times = interpolant.parameterPositions,
- values = interpolant.sampleValues;
- times[ 0 ] = now;
- values[ 0 ] = weightNow;
- times[ 1 ] = now + duration;
- values[ 1 ] = weightThen;
- return this;
- }
- }
- function AnimationMixer( root ) {
- this._root = root;
- this._initMemoryManager();
- this._accuIndex = 0;
- this.time = 0;
- this.timeScale = 1.0;
- }
- AnimationMixer.prototype = Object.assign( Object.create( EventDispatcher.prototype ), {
- constructor: AnimationMixer,
- _bindAction: function ( action, prototypeAction ) {
- const root = action._localRoot || this._root,
- tracks = action._clip.tracks,
- nTracks = tracks.length,
- bindings = action._propertyBindings,
- interpolants = action._interpolants,
- rootUuid = root.uuid,
- bindingsByRoot = this._bindingsByRootAndName;
- let bindingsByName = bindingsByRoot[ rootUuid ];
- if ( bindingsByName === undefined ) {
- bindingsByName = {};
- bindingsByRoot[ rootUuid ] = bindingsByName;
- }
- for ( let i = 0; i !== nTracks; ++ i ) {
- const track = tracks[ i ],
- trackName = track.name;
- let binding = bindingsByName[ trackName ];
- if ( binding !== undefined ) {
- bindings[ i ] = binding;
- } else {
- binding = bindings[ i ];
- if ( binding !== undefined ) {
- // existing binding, make sure the cache knows
- if ( binding._cacheIndex === null ) {
- ++ binding.referenceCount;
- this._addInactiveBinding( binding, rootUuid, trackName );
- }
- continue;
- }
- const path = prototypeAction && prototypeAction.
- _propertyBindings[ i ].binding.parsedPath;
- binding = new PropertyMixer(
- PropertyBinding.create( root, trackName, path ),
- track.ValueTypeName, track.getValueSize() );
- ++ binding.referenceCount;
- this._addInactiveBinding( binding, rootUuid, trackName );
- bindings[ i ] = binding;
- }
- interpolants[ i ].resultBuffer = binding.buffer;
- }
- },
- _activateAction: function ( action ) {
- if ( ! this._isActiveAction( action ) ) {
- if ( action._cacheIndex === null ) {
- // this action has been forgotten by the cache, but the user
- // appears to be still using it -> rebind
- const rootUuid = ( action._localRoot || this._root ).uuid,
- clipUuid = action._clip.uuid,
- actionsForClip = this._actionsByClip[ clipUuid ];
- this._bindAction( action,
- actionsForClip && actionsForClip.knownActions[ 0 ] );
- this._addInactiveAction( action, clipUuid, rootUuid );
- }
- const bindings = action._propertyBindings;
- // increment reference counts / sort out state
- for ( let i = 0, n = bindings.length; i !== n; ++ i ) {
- const binding = bindings[ i ];
- if ( binding.useCount ++ === 0 ) {
- this._lendBinding( binding );
- binding.saveOriginalState();
- }
- }
- this._lendAction( action );
- }
- },
- _deactivateAction: function ( action ) {
- if ( this._isActiveAction( action ) ) {
- const bindings = action._propertyBindings;
- // decrement reference counts / sort out state
- for ( let i = 0, n = bindings.length; i !== n; ++ i ) {
- const binding = bindings[ i ];
- if ( -- binding.useCount === 0 ) {
- binding.restoreOriginalState();
- this._takeBackBinding( binding );
- }
- }
- this._takeBackAction( action );
- }
- },
- // Memory manager
- _initMemoryManager: function () {
- this._actions = []; // 'nActiveActions' followed by inactive ones
- this._nActiveActions = 0;
- this._actionsByClip = {};
- // inside:
- // {
- // knownActions: Array< AnimationAction > - used as prototypes
- // actionByRoot: AnimationAction - lookup
- // }
- this._bindings = []; // 'nActiveBindings' followed by inactive ones
- this._nActiveBindings = 0;
- this._bindingsByRootAndName = {}; // inside: Map< name, PropertyMixer >
- this._controlInterpolants = []; // same game as above
- this._nActiveControlInterpolants = 0;
- const scope = this;
- this.stats = {
- actions: {
- get total() {
- return scope._actions.length;
- },
- get inUse() {
- return scope._nActiveActions;
- }
- },
- bindings: {
- get total() {
- return scope._bindings.length;
- },
- get inUse() {
- return scope._nActiveBindings;
- }
- },
- controlInterpolants: {
- get total() {
- return scope._controlInterpolants.length;
- },
- get inUse() {
- return scope._nActiveControlInterpolants;
- }
- }
- };
- },
- // Memory management for AnimationAction objects
- _isActiveAction: function ( action ) {
- const index = action._cacheIndex;
- return index !== null && index < this._nActiveActions;
- },
- _addInactiveAction: function ( action, clipUuid, rootUuid ) {
- const actions = this._actions,
- actionsByClip = this._actionsByClip;
- let actionsForClip = actionsByClip[ clipUuid ];
- if ( actionsForClip === undefined ) {
- actionsForClip = {
- knownActions: [ action ],
- actionByRoot: {}
- };
- action._byClipCacheIndex = 0;
- actionsByClip[ clipUuid ] = actionsForClip;
- } else {
- const knownActions = actionsForClip.knownActions;
- action._byClipCacheIndex = knownActions.length;
- knownActions.push( action );
- }
- action._cacheIndex = actions.length;
- actions.push( action );
- actionsForClip.actionByRoot[ rootUuid ] = action;
- },
- _removeInactiveAction: function ( action ) {
- const actions = this._actions,
- lastInactiveAction = actions[ actions.length - 1 ],
- cacheIndex = action._cacheIndex;
- lastInactiveAction._cacheIndex = cacheIndex;
- actions[ cacheIndex ] = lastInactiveAction;
- actions.pop();
- action._cacheIndex = null;
- const clipUuid = action._clip.uuid,
- actionsByClip = this._actionsByClip,
- actionsForClip = actionsByClip[ clipUuid ],
- knownActionsForClip = actionsForClip.knownActions,
- lastKnownAction =
- knownActionsForClip[ knownActionsForClip.length - 1 ],
- byClipCacheIndex = action._byClipCacheIndex;
- lastKnownAction._byClipCacheIndex = byClipCacheIndex;
- knownActionsForClip[ byClipCacheIndex ] = lastKnownAction;
- knownActionsForClip.pop();
- action._byClipCacheIndex = null;
- const actionByRoot = actionsForClip.actionByRoot,
- rootUuid = ( action._localRoot || this._root ).uuid;
- delete actionByRoot[ rootUuid ];
- if ( knownActionsForClip.length === 0 ) {
- delete actionsByClip[ clipUuid ];
- }
- this._removeInactiveBindingsForAction( action );
- },
- _removeInactiveBindingsForAction: function ( action ) {
- const bindings = action._propertyBindings;
- for ( let i = 0, n = bindings.length; i !== n; ++ i ) {
- const binding = bindings[ i ];
- if ( -- binding.referenceCount === 0 ) {
- this._removeInactiveBinding( binding );
- }
- }
- },
- _lendAction: function ( action ) {
- // [ active actions | inactive actions ]
- // [ active actions >| inactive actions ]
- // s a
- // <-swap->
- // a s
- const actions = this._actions,
- prevIndex = action._cacheIndex,
- lastActiveIndex = this._nActiveActions ++,
- firstInactiveAction = actions[ lastActiveIndex ];
- action._cacheIndex = lastActiveIndex;
- actions[ lastActiveIndex ] = action;
- firstInactiveAction._cacheIndex = prevIndex;
- actions[ prevIndex ] = firstInactiveAction;
- },
- _takeBackAction: function ( action ) {
- // [ active actions | inactive actions ]
- // [ active actions |< inactive actions ]
- // a s
- // <-swap->
- // s a
- const actions = this._actions,
- prevIndex = action._cacheIndex,
- firstInactiveIndex = -- this._nActiveActions,
- lastActiveAction = actions[ firstInactiveIndex ];
- action._cacheIndex = firstInactiveIndex;
- actions[ firstInactiveIndex ] = action;
- lastActiveAction._cacheIndex = prevIndex;
- actions[ prevIndex ] = lastActiveAction;
- },
- // Memory management for PropertyMixer objects
- _addInactiveBinding: function ( binding, rootUuid, trackName ) {
- const bindingsByRoot = this._bindingsByRootAndName,
- bindings = this._bindings;
- let bindingByName = bindingsByRoot[ rootUuid ];
- if ( bindingByName === undefined ) {
- bindingByName = {};
- bindingsByRoot[ rootUuid ] = bindingByName;
- }
- bindingByName[ trackName ] = binding;
- binding._cacheIndex = bindings.length;
- bindings.push( binding );
- },
- _removeInactiveBinding: function ( binding ) {
- const bindings = this._bindings,
- propBinding = binding.binding,
- rootUuid = propBinding.rootNode.uuid,
- trackName = propBinding.path,
- bindingsByRoot = this._bindingsByRootAndName,
- bindingByName = bindingsByRoot[ rootUuid ],
- lastInactiveBinding = bindings[ bindings.length - 1 ],
- cacheIndex = binding._cacheIndex;
- lastInactiveBinding._cacheIndex = cacheIndex;
- bindings[ cacheIndex ] = lastInactiveBinding;
- bindings.pop();
- delete bindingByName[ trackName ];
- if ( Object.keys( bindingByName ).length === 0 ) {
- delete bindingsByRoot[ rootUuid ];
- }
- },
- _lendBinding: function ( binding ) {
- const bindings = this._bindings,
- prevIndex = binding._cacheIndex,
- lastActiveIndex = this._nActiveBindings ++,
- firstInactiveBinding = bindings[ lastActiveIndex ];
- binding._cacheIndex = lastActiveIndex;
- bindings[ lastActiveIndex ] = binding;
- firstInactiveBinding._cacheIndex = prevIndex;
- bindings[ prevIndex ] = firstInactiveBinding;
- },
- _takeBackBinding: function ( binding ) {
- const bindings = this._bindings,
- prevIndex = binding._cacheIndex,
- firstInactiveIndex = -- this._nActiveBindings,
- lastActiveBinding = bindings[ firstInactiveIndex ];
- binding._cacheIndex = firstInactiveIndex;
- bindings[ firstInactiveIndex ] = binding;
- lastActiveBinding._cacheIndex = prevIndex;
- bindings[ prevIndex ] = lastActiveBinding;
- },
- // Memory management of Interpolants for weight and time scale
- _lendControlInterpolant: function () {
- const interpolants = this._controlInterpolants,
- lastActiveIndex = this._nActiveControlInterpolants ++;
- let interpolant = interpolants[ lastActiveIndex ];
- if ( interpolant === undefined ) {
- interpolant = new LinearInterpolant(
- new Float32Array( 2 ), new Float32Array( 2 ),
- 1, this._controlInterpolantsResultBuffer );
- interpolant.__cacheIndex = lastActiveIndex;
- interpolants[ lastActiveIndex ] = interpolant;
- }
- return interpolant;
- },
- _takeBackControlInterpolant: function ( interpolant ) {
- const interpolants = this._controlInterpolants,
- prevIndex = interpolant.__cacheIndex,
- firstInactiveIndex = -- this._nActiveControlInterpolants,
- lastActiveInterpolant = interpolants[ firstInactiveIndex ];
- interpolant.__cacheIndex = firstInactiveIndex;
- interpolants[ firstInactiveIndex ] = interpolant;
- lastActiveInterpolant.__cacheIndex = prevIndex;
- interpolants[ prevIndex ] = lastActiveInterpolant;
- },
- _controlInterpolantsResultBuffer: new Float32Array( 1 ),
- // return an action for a clip optionally using a custom root target
- // object (this method allocates a lot of dynamic memory in case a
- // previously unknown clip/root combination is specified)
- clipAction: function ( clip, optionalRoot, blendMode ) {
- const root = optionalRoot || this._root,
- rootUuid = root.uuid;
- let clipObject = typeof clip === 'string' ? AnimationClip.findByName( root, clip ) : clip;
- const clipUuid = clipObject !== null ? clipObject.uuid : clip;
- const actionsForClip = this._actionsByClip[ clipUuid ];
- let prototypeAction = null;
- if ( blendMode === undefined ) {
- if ( clipObject !== null ) {
- blendMode = clipObject.blendMode;
- } else {
- blendMode = NormalAnimationBlendMode;
- }
- }
- if ( actionsForClip !== undefined ) {
- const existingAction = actionsForClip.actionByRoot[ rootUuid ];
- if ( existingAction !== undefined && existingAction.blendMode === blendMode ) {
- return existingAction;
- }
- // we know the clip, so we don't have to parse all
- // the bindings again but can just copy
- prototypeAction = actionsForClip.knownActions[ 0 ];
- // also, take the clip from the prototype action
- if ( clipObject === null )
- clipObject = prototypeAction._clip;
- }
- // clip must be known when specified via string
- if ( clipObject === null ) return null;
- // allocate all resources required to run it
- const newAction = new AnimationAction( this, clipObject, optionalRoot, blendMode );
- this._bindAction( newAction, prototypeAction );
- // and make the action known to the memory manager
- this._addInactiveAction( newAction, clipUuid, rootUuid );
- return newAction;
- },
- // get an existing action
- existingAction: function ( clip, optionalRoot ) {
- const root = optionalRoot || this._root,
- rootUuid = root.uuid,
- clipObject = typeof clip === 'string' ?
- AnimationClip.findByName( root, clip ) : clip,
- clipUuid = clipObject ? clipObject.uuid : clip,
- actionsForClip = this._actionsByClip[ clipUuid ];
- if ( actionsForClip !== undefined ) {
- return actionsForClip.actionByRoot[ rootUuid ] || null;
- }
- return null;
- },
- // deactivates all previously scheduled actions
- stopAllAction: function () {
- const actions = this._actions,
- nActions = this._nActiveActions;
- for ( let i = nActions - 1; i >= 0; -- i ) {
- actions[ i ].stop();
- }
- return this;
- },
- // advance the time and update apply the animation
- update: function ( deltaTime ) {
- deltaTime *= this.timeScale;
- const actions = this._actions,
- nActions = this._nActiveActions,
- time = this.time += deltaTime,
- timeDirection = Math.sign( deltaTime ),
- accuIndex = this._accuIndex ^= 1;
- // run active actions
- for ( let i = 0; i !== nActions; ++ i ) {
- const action = actions[ i ];
- action._update( time, deltaTime, timeDirection, accuIndex );
- }
- // update scene graph
- const bindings = this._bindings,
- nBindings = this._nActiveBindings;
- for ( let i = 0; i !== nBindings; ++ i ) {
- bindings[ i ].apply( accuIndex );
- }
- return this;
- },
- // Allows you to seek to a specific time in an animation.
- setTime: function ( timeInSeconds ) {
- this.time = 0; // Zero out time attribute for AnimationMixer object;
- for ( let i = 0; i < this._actions.length; i ++ ) {
- this._actions[ i ].time = 0; // Zero out time attribute for all associated AnimationAction objects.
- }
- return this.update( timeInSeconds ); // Update used to set exact time. Returns "this" AnimationMixer object.
- },
- // return this mixer's root target object
- getRoot: function () {
- return this._root;
- },
- // free all resources specific to a particular clip
- uncacheClip: function ( clip ) {
- const actions = this._actions,
- clipUuid = clip.uuid,
- actionsByClip = this._actionsByClip,
- actionsForClip = actionsByClip[ clipUuid ];
- if ( actionsForClip !== undefined ) {
- // note: just calling _removeInactiveAction would mess up the
- // iteration state and also require updating the state we can
- // just throw away
- const actionsToRemove = actionsForClip.knownActions;
- for ( let i = 0, n = actionsToRemove.length; i !== n; ++ i ) {
- const action = actionsToRemove[ i ];
- this._deactivateAction( action );
- const cacheIndex = action._cacheIndex,
- lastInactiveAction = actions[ actions.length - 1 ];
- action._cacheIndex = null;
- action._byClipCacheIndex = null;
- lastInactiveAction._cacheIndex = cacheIndex;
- actions[ cacheIndex ] = lastInactiveAction;
- actions.pop();
- this._removeInactiveBindingsForAction( action );
- }
- delete actionsByClip[ clipUuid ];
- }
- },
- // free all resources specific to a particular root target object
- uncacheRoot: function ( root ) {
- const rootUuid = root.uuid,
- actionsByClip = this._actionsByClip;
- for ( const clipUuid in actionsByClip ) {
- const actionByRoot = actionsByClip[ clipUuid ].actionByRoot,
- action = actionByRoot[ rootUuid ];
- if ( action !== undefined ) {
- this._deactivateAction( action );
- this._removeInactiveAction( action );
- }
- }
- const bindingsByRoot = this._bindingsByRootAndName,
- bindingByName = bindingsByRoot[ rootUuid ];
- if ( bindingByName !== undefined ) {
- for ( const trackName in bindingByName ) {
- const binding = bindingByName[ trackName ];
- binding.restoreOriginalState();
- this._removeInactiveBinding( binding );
- }
- }
- },
- // remove a targeted clip from the cache
- uncacheAction: function ( clip, optionalRoot ) {
- const action = this.existingAction( clip, optionalRoot );
- if ( action !== null ) {
- this._deactivateAction( action );
- this._removeInactiveAction( action );
- }
- }
- } );
- class Uniform {
- constructor( value ) {
- if ( typeof value === 'string' ) {
- console.warn( 'THREE.Uniform: Type parameter is no longer needed.' );
- value = arguments[ 1 ];
- }
- this.value = value;
- }
- clone() {
- return new Uniform( this.value.clone === undefined ? this.value : this.value.clone() );
- }
- }
- function InstancedInterleavedBuffer( array, stride, meshPerAttribute ) {
- InterleavedBuffer.call( this, array, stride );
- this.meshPerAttribute = meshPerAttribute || 1;
- }
- InstancedInterleavedBuffer.prototype = Object.assign( Object.create( InterleavedBuffer.prototype ), {
- constructor: InstancedInterleavedBuffer,
- isInstancedInterleavedBuffer: true,
- copy: function ( source ) {
- InterleavedBuffer.prototype.copy.call( this, source );
- this.meshPerAttribute = source.meshPerAttribute;
- return this;
- },
- clone: function ( data ) {
- const ib = InterleavedBuffer.prototype.clone.call( this, data );
- ib.meshPerAttribute = this.meshPerAttribute;
- return ib;
- },
- toJSON: function ( data ) {
- const json = InterleavedBuffer.prototype.toJSON.call( this, data );
- json.isInstancedInterleavedBuffer = true;
- json.meshPerAttribute = this.meshPerAttribute;
- return json;
- }
- } );
- function GLBufferAttribute( buffer, type, itemSize, elementSize, count ) {
- this.buffer = buffer;
- this.type = type;
- this.itemSize = itemSize;
- this.elementSize = elementSize;
- this.count = count;
- this.version = 0;
- }
- Object.defineProperty( GLBufferAttribute.prototype, 'needsUpdate', {
- set: function ( value ) {
- if ( value === true ) this.version ++;
- }
- } );
- Object.assign( GLBufferAttribute.prototype, {
- isGLBufferAttribute: true,
- setBuffer: function ( buffer ) {
- this.buffer = buffer;
- return this;
- },
- setType: function ( type, elementSize ) {
- this.type = type;
- this.elementSize = elementSize;
- return this;
- },
- setItemSize: function ( itemSize ) {
- this.itemSize = itemSize;
- return this;
- },
- setCount: function ( count ) {
- this.count = count;
- return this;
- },
- } );
- function Raycaster( origin, direction, near, far ) {
- this.ray = new Ray( origin, direction );
- // direction is assumed to be normalized (for accurate distance calculations)
- this.near = near || 0;
- this.far = far || Infinity;
- this.camera = null;
- this.layers = new Layers();
- this.params = {
- Mesh: {},
- Line: { threshold: 1 },
- LOD: {},
- Points: { threshold: 1 },
- Sprite: {}
- };
- Object.defineProperties( this.params, {
- PointCloud: {
- get: function () {
- console.warn( 'THREE.Raycaster: params.PointCloud has been renamed to params.Points.' );
- return this.Points;
- }
- }
- } );
- }
- function ascSort( a, b ) {
- return a.distance - b.distance;
- }
- function intersectObject( object, raycaster, intersects, recursive ) {
- if ( object.layers.test( raycaster.layers ) ) {
- object.raycast( raycaster, intersects );
- }
- if ( recursive === true ) {
- const children = object.children;
- for ( let i = 0, l = children.length; i < l; i ++ ) {
- intersectObject( children[ i ], raycaster, intersects, true );
- }
- }
- }
- Object.assign( Raycaster.prototype, {
- set: function ( origin, direction ) {
- // direction is assumed to be normalized (for accurate distance calculations)
- this.ray.set( origin, direction );
- },
- setFromCamera: function ( coords, camera ) {
- if ( camera && camera.isPerspectiveCamera ) {
- this.ray.origin.setFromMatrixPosition( camera.matrixWorld );
- this.ray.direction.set( coords.x, coords.y, 0.5 ).unproject( camera ).sub( this.ray.origin ).normalize();
- this.camera = camera;
- } else if ( camera && camera.isOrthographicCamera ) {
- this.ray.origin.set( coords.x, coords.y, ( camera.near + camera.far ) / ( camera.near - camera.far ) ).unproject( camera ); // set origin in plane of camera
- this.ray.direction.set( 0, 0, - 1 ).transformDirection( camera.matrixWorld );
- this.camera = camera;
- } else {
- console.error( 'THREE.Raycaster: Unsupported camera type: ' + camera.type );
- }
- },
- intersectObject: function ( object, recursive, optionalTarget ) {
- const intersects = optionalTarget || [];
- intersectObject( object, this, intersects, recursive );
- intersects.sort( ascSort );
- return intersects;
- },
- intersectObjects: function ( objects, recursive, optionalTarget ) {
- const intersects = optionalTarget || [];
- if ( Array.isArray( objects ) === false ) {
- console.warn( 'THREE.Raycaster.intersectObjects: objects is not an Array.' );
- return intersects;
- }
- for ( let i = 0, l = objects.length; i < l; i ++ ) {
- intersectObject( objects[ i ], this, intersects, recursive );
- }
- intersects.sort( ascSort );
- return intersects;
- }
- } );
- /**
- * Ref: https://en.wikipedia.org/wiki/Spherical_coordinate_system
- *
- * The polar angle (phi) is measured from the positive y-axis. The positive y-axis is up.
- * The azimuthal angle (theta) is measured from the positive z-axis.
- */
- class Spherical {
- constructor( radius = 1, phi = 0, theta = 0 ) {
- this.radius = radius;
- this.phi = phi; // polar angle
- this.theta = theta; // azimuthal angle
- return this;
- }
- set( radius, phi, theta ) {
- this.radius = radius;
- this.phi = phi;
- this.theta = theta;
- return this;
- }
- clone() {
- return new this.constructor().copy( this );
- }
- copy( other ) {
- this.radius = other.radius;
- this.phi = other.phi;
- this.theta = other.theta;
- return this;
- }
- // restrict phi to be betwee EPS and PI-EPS
- makeSafe() {
- const EPS = 0.000001;
- this.phi = Math.max( EPS, Math.min( Math.PI - EPS, this.phi ) );
- return this;
- }
- setFromVector3( v ) {
- return this.setFromCartesianCoords( v.x, v.y, v.z );
- }
- setFromCartesianCoords( x, y, z ) {
- this.radius = Math.sqrt( x * x + y * y + z * z );
- if ( this.radius === 0 ) {
- this.theta = 0;
- this.phi = 0;
- } else {
- this.theta = Math.atan2( x, z );
- this.phi = Math.acos( MathUtils.clamp( y / this.radius, - 1, 1 ) );
- }
- return this;
- }
- }
- /**
- * Ref: https://en.wikipedia.org/wiki/Cylindrical_coordinate_system
- */
- class Cylindrical {
- constructor( radius, theta, y ) {
- this.radius = ( radius !== undefined ) ? radius : 1.0; // distance from the origin to a point in the x-z plane
- this.theta = ( theta !== undefined ) ? theta : 0; // counterclockwise angle in the x-z plane measured in radians from the positive z-axis
- this.y = ( y !== undefined ) ? y : 0; // height above the x-z plane
- return this;
- }
- set( radius, theta, y ) {
- this.radius = radius;
- this.theta = theta;
- this.y = y;
- return this;
- }
- clone() {
- return new this.constructor().copy( this );
- }
- copy( other ) {
- this.radius = other.radius;
- this.theta = other.theta;
- this.y = other.y;
- return this;
- }
- setFromVector3( v ) {
- return this.setFromCartesianCoords( v.x, v.y, v.z );
- }
- setFromCartesianCoords( x, y, z ) {
- this.radius = Math.sqrt( x * x + z * z );
- this.theta = Math.atan2( x, z );
- this.y = y;
- return this;
- }
- }
- const _vector$8 = /*@__PURE__*/ new Vector2$1();
- class Box2 {
- constructor( min, max ) {
- Object.defineProperty( this, 'isBox2', { value: true } );
- this.min = ( min !== undefined ) ? min : new Vector2$1( + Infinity, + Infinity );
- this.max = ( max !== undefined ) ? max : new Vector2$1( - Infinity, - Infinity );
- }
- set( min, max ) {
- this.min.copy( min );
- this.max.copy( max );
- return this;
- }
- setFromPoints( points ) {
- this.makeEmpty();
- for ( let i = 0, il = points.length; i < il; i ++ ) {
- this.expandByPoint( points[ i ] );
- }
- return this;
- }
- setFromCenterAndSize( center, size ) {
- const halfSize = _vector$8.copy( size ).multiplyScalar( 0.5 );
- this.min.copy( center ).sub( halfSize );
- this.max.copy( center ).add( halfSize );
- return this;
- }
- clone() {
- return new this.constructor().copy( this );
- }
- copy( box ) {
- this.min.copy( box.min );
- this.max.copy( box.max );
- return this;
- }
- makeEmpty() {
- this.min.x = this.min.y = + Infinity;
- this.max.x = this.max.y = - Infinity;
- return this;
- }
- isEmpty() {
- // this is a more robust check for empty than ( volume <= 0 ) because volume can get positive with two negative axes
- return ( this.max.x < this.min.x ) || ( this.max.y < this.min.y );
- }
- getCenter( target ) {
- if ( target === undefined ) {
- console.warn( 'THREE.Box2: .getCenter() target is now required' );
- target = new Vector2$1();
- }
- return this.isEmpty() ? target.set( 0, 0 ) : target.addVectors( this.min, this.max ).multiplyScalar( 0.5 );
- }
- getSize( target ) {
- if ( target === undefined ) {
- console.warn( 'THREE.Box2: .getSize() target is now required' );
- target = new Vector2$1();
- }
- return this.isEmpty() ? target.set( 0, 0 ) : target.subVectors( this.max, this.min );
- }
- expandByPoint( point ) {
- this.min.min( point );
- this.max.max( point );
- return this;
- }
- expandByVector( vector ) {
- this.min.sub( vector );
- this.max.add( vector );
- return this;
- }
- expandByScalar( scalar ) {
- this.min.addScalar( - scalar );
- this.max.addScalar( scalar );
- return this;
- }
- containsPoint( point ) {
- return point.x < this.min.x || point.x > this.max.x ||
- point.y < this.min.y || point.y > this.max.y ? false : true;
- }
- containsBox( box ) {
- return this.min.x <= box.min.x && box.max.x <= this.max.x &&
- this.min.y <= box.min.y && box.max.y <= this.max.y;
- }
- getParameter( point, target ) {
- // This can potentially have a divide by zero if the box
- // has a size dimension of 0.
- if ( target === undefined ) {
- console.warn( 'THREE.Box2: .getParameter() target is now required' );
- target = new Vector2$1();
- }
- return target.set(
- ( point.x - this.min.x ) / ( this.max.x - this.min.x ),
- ( point.y - this.min.y ) / ( this.max.y - this.min.y )
- );
- }
- intersectsBox( box ) {
- // using 4 splitting planes to rule out intersections
- return box.max.x < this.min.x || box.min.x > this.max.x ||
- box.max.y < this.min.y || box.min.y > this.max.y ? false : true;
- }
- clampPoint( point, target ) {
- if ( target === undefined ) {
- console.warn( 'THREE.Box2: .clampPoint() target is now required' );
- target = new Vector2$1();
- }
- return target.copy( point ).clamp( this.min, this.max );
- }
- distanceToPoint( point ) {
- const clampedPoint = _vector$8.copy( point ).clamp( this.min, this.max );
- return clampedPoint.sub( point ).length();
- }
- intersect( box ) {
- this.min.max( box.min );
- this.max.min( box.max );
- return this;
- }
- union( box ) {
- this.min.min( box.min );
- this.max.max( box.max );
- return this;
- }
- translate( offset ) {
- this.min.add( offset );
- this.max.add( offset );
- return this;
- }
- equals( box ) {
- return box.min.equals( this.min ) && box.max.equals( this.max );
- }
- }
- const _startP = /*@__PURE__*/ new Vector3();
- const _startEnd = /*@__PURE__*/ new Vector3();
- class Line3 {
- constructor( start, end ) {
- this.start = ( start !== undefined ) ? start : new Vector3();
- this.end = ( end !== undefined ) ? end : new Vector3();
- }
- set( start, end ) {
- this.start.copy( start );
- this.end.copy( end );
- return this;
- }
- clone() {
- return new this.constructor().copy( this );
- }
- copy( line ) {
- this.start.copy( line.start );
- this.end.copy( line.end );
- return this;
- }
- getCenter( target ) {
- if ( target === undefined ) {
- console.warn( 'THREE.Line3: .getCenter() target is now required' );
- target = new Vector3();
- }
- return target.addVectors( this.start, this.end ).multiplyScalar( 0.5 );
- }
- delta( target ) {
- if ( target === undefined ) {
- console.warn( 'THREE.Line3: .delta() target is now required' );
- target = new Vector3();
- }
- return target.subVectors( this.end, this.start );
- }
- distanceSq() {
- return this.start.distanceToSquared( this.end );
- }
- distance() {
- return this.start.distanceTo( this.end );
- }
- at( t, target ) {
- if ( target === undefined ) {
- console.warn( 'THREE.Line3: .at() target is now required' );
- target = new Vector3();
- }
- return this.delta( target ).multiplyScalar( t ).add( this.start );
- }
- closestPointToPointParameter( point, clampToLine ) {
- _startP.subVectors( point, this.start );
- _startEnd.subVectors( this.end, this.start );
- const startEnd2 = _startEnd.dot( _startEnd );
- const startEnd_startP = _startEnd.dot( _startP );
- let t = startEnd_startP / startEnd2;
- if ( clampToLine ) {
- t = MathUtils.clamp( t, 0, 1 );
- }
- return t;
- }
- closestPointToPoint( point, clampToLine, target ) {
- const t = this.closestPointToPointParameter( point, clampToLine );
- if ( target === undefined ) {
- console.warn( 'THREE.Line3: .closestPointToPoint() target is now required' );
- target = new Vector3();
- }
- return this.delta( target ).multiplyScalar( t ).add( this.start );
- }
- applyMatrix4( matrix ) {
- this.start.applyMatrix4( matrix );
- this.end.applyMatrix4( matrix );
- return this;
- }
- equals( line ) {
- return line.start.equals( this.start ) && line.end.equals( this.end );
- }
- }
- function ImmediateRenderObject( material ) {
- Object3D.call( this );
- this.material = material;
- this.render = function ( /* renderCallback */ ) {};
- this.hasPositions = false;
- this.hasNormals = false;
- this.hasColors = false;
- this.hasUvs = false;
- this.positionArray = null;
- this.normalArray = null;
- this.colorArray = null;
- this.uvArray = null;
- this.count = 0;
- }
- ImmediateRenderObject.prototype = Object.create( Object3D.prototype );
- ImmediateRenderObject.prototype.constructor = ImmediateRenderObject;
- ImmediateRenderObject.prototype.isImmediateRenderObject = true;
- const _vector$9 = /*@__PURE__*/ new Vector3();
- class SpotLightHelper extends Object3D {
- constructor( light, color ) {
- super();
- this.light = light;
- this.light.updateMatrixWorld();
- this.matrix = light.matrixWorld;
- this.matrixAutoUpdate = false;
- this.color = color;
- const geometry = new BufferGeometry();
- const positions = [
- 0, 0, 0, 0, 0, 1,
- 0, 0, 0, 1, 0, 1,
- 0, 0, 0, - 1, 0, 1,
- 0, 0, 0, 0, 1, 1,
- 0, 0, 0, 0, - 1, 1
- ];
- for ( let i = 0, j = 1, l = 32; i < l; i ++, j ++ ) {
- const p1 = ( i / l ) * Math.PI * 2;
- const p2 = ( j / l ) * Math.PI * 2;
- positions.push(
- Math.cos( p1 ), Math.sin( p1 ), 1,
- Math.cos( p2 ), Math.sin( p2 ), 1
- );
- }
- geometry.setAttribute( 'position', new Float32BufferAttribute( positions, 3 ) );
- const material = new LineBasicMaterial( { fog: false, toneMapped: false } );
- this.cone = new LineSegments( geometry, material );
- this.add( this.cone );
- this.update();
- }
- dispose() {
- this.cone.geometry.dispose();
- this.cone.material.dispose();
- }
- update() {
- this.light.updateMatrixWorld();
- const coneLength = this.light.distance ? this.light.distance : 1000;
- const coneWidth = coneLength * Math.tan( this.light.angle );
- this.cone.scale.set( coneWidth, coneWidth, coneLength );
- _vector$9.setFromMatrixPosition( this.light.target.matrixWorld );
- this.cone.lookAt( _vector$9 );
- if ( this.color !== undefined ) {
- this.cone.material.color.set( this.color );
- } else {
- this.cone.material.color.copy( this.light.color );
- }
- }
- }
- const _vector$a = /*@__PURE__*/ new Vector3();
- const _boneMatrix = /*@__PURE__*/ new Matrix4();
- const _matrixWorldInv = /*@__PURE__*/ new Matrix4();
- class SkeletonHelper extends LineSegments {
- constructor( object ) {
- const bones = getBoneList( object );
- const geometry = new BufferGeometry();
- const vertices = [];
- const colors = [];
- const color1 = new Color( 0, 0, 1 );
- const color2 = new Color( 0, 1, 0 );
- for ( let i = 0; i < bones.length; i ++ ) {
- const bone = bones[ i ];
- if ( bone.parent && bone.parent.isBone ) {
- vertices.push( 0, 0, 0 );
- vertices.push( 0, 0, 0 );
- colors.push( color1.r, color1.g, color1.b );
- colors.push( color2.r, color2.g, color2.b );
- }
- }
- geometry.setAttribute( 'position', new Float32BufferAttribute( vertices, 3 ) );
- geometry.setAttribute( 'color', new Float32BufferAttribute( colors, 3 ) );
- const material = new LineBasicMaterial( { vertexColors: true, depthTest: false, depthWrite: false, toneMapped: false, transparent: true } );
- super( geometry, material );
- this.type = 'SkeletonHelper';
- this.isSkeletonHelper = true;
- this.root = object;
- this.bones = bones;
- this.matrix = object.matrixWorld;
- this.matrixAutoUpdate = false;
- }
- updateMatrixWorld( force ) {
- const bones = this.bones;
- const geometry = this.geometry;
- const position = geometry.getAttribute( 'position' );
- _matrixWorldInv.copy( this.root.matrixWorld ).invert();
- for ( let i = 0, j = 0; i < bones.length; i ++ ) {
- const bone = bones[ i ];
- if ( bone.parent && bone.parent.isBone ) {
- _boneMatrix.multiplyMatrices( _matrixWorldInv, bone.matrixWorld );
- _vector$a.setFromMatrixPosition( _boneMatrix );
- position.setXYZ( j, _vector$a.x, _vector$a.y, _vector$a.z );
- _boneMatrix.multiplyMatrices( _matrixWorldInv, bone.parent.matrixWorld );
- _vector$a.setFromMatrixPosition( _boneMatrix );
- position.setXYZ( j + 1, _vector$a.x, _vector$a.y, _vector$a.z );
- j += 2;
- }
- }
- geometry.getAttribute( 'position' ).needsUpdate = true;
- super.updateMatrixWorld( force );
- }
- }
- function getBoneList( object ) {
- const boneList = [];
- if ( object && object.isBone ) {
- boneList.push( object );
- }
- for ( let i = 0; i < object.children.length; i ++ ) {
- boneList.push.apply( boneList, getBoneList( object.children[ i ] ) );
- }
- return boneList;
- }
- class PointLightHelper extends Mesh {
- constructor( light, sphereSize, color ) {
- const geometry = new SphereBufferGeometry( sphereSize, 4, 2 );
- const material = new MeshBasicMaterial( { wireframe: true, fog: false, toneMapped: false } );
- super( geometry, material );
- this.light = light;
- this.light.updateMatrixWorld();
- this.color = color;
- this.type = 'PointLightHelper';
- this.matrix = this.light.matrixWorld;
- this.matrixAutoUpdate = false;
- this.update();
- /*
- // TODO: delete this comment?
- const distanceGeometry = new THREE.IcosahedronBufferGeometry( 1, 2 );
- const distanceMaterial = new THREE.MeshBasicMaterial( { color: hexColor, fog: false, wireframe: true, opacity: 0.1, transparent: true } );
- this.lightSphere = new THREE.Mesh( bulbGeometry, bulbMaterial );
- this.lightDistance = new THREE.Mesh( distanceGeometry, distanceMaterial );
- const d = light.distance;
- if ( d === 0.0 ) {
- this.lightDistance.visible = false;
- } else {
- this.lightDistance.scale.set( d, d, d );
- }
- this.add( this.lightDistance );
- */
- }
- dispose() {
- this.geometry.dispose();
- this.material.dispose();
- }
- update() {
- if ( this.color !== undefined ) {
- this.material.color.set( this.color );
- } else {
- this.material.color.copy( this.light.color );
- }
- /*
- const d = this.light.distance;
- if ( d === 0.0 ) {
- this.lightDistance.visible = false;
- } else {
- this.lightDistance.visible = true;
- this.lightDistance.scale.set( d, d, d );
- }
- */
- }
- }
- const _vector$b = /*@__PURE__*/ new Vector3();
- const _color1 = /*@__PURE__*/ new Color();
- const _color2 = /*@__PURE__*/ new Color();
- class HemisphereLightHelper extends Object3D {
- constructor( light, size, color ) {
- super();
- this.light = light;
- this.light.updateMatrixWorld();
- this.matrix = light.matrixWorld;
- this.matrixAutoUpdate = false;
- this.color = color;
- const geometry = new OctahedronBufferGeometry( size );
- geometry.rotateY( Math.PI * 0.5 );
- this.material = new MeshBasicMaterial( { wireframe: true, fog: false, toneMapped: false } );
- if ( this.color === undefined ) this.material.vertexColors = true;
- const position = geometry.getAttribute( 'position' );
- const colors = new Float32Array( position.count * 3 );
- geometry.setAttribute( 'color', new BufferAttribute( colors, 3 ) );
- this.add( new Mesh( geometry, this.material ) );
- this.update();
- }
- dispose() {
- this.children[ 0 ].geometry.dispose();
- this.children[ 0 ].material.dispose();
- }
- update() {
- const mesh = this.children[ 0 ];
- if ( this.color !== undefined ) {
- this.material.color.set( this.color );
- } else {
- const colors = mesh.geometry.getAttribute( 'color' );
- _color1.copy( this.light.color );
- _color2.copy( this.light.groundColor );
- for ( let i = 0, l = colors.count; i < l; i ++ ) {
- const color = ( i < ( l / 2 ) ) ? _color1 : _color2;
- colors.setXYZ( i, color.r, color.g, color.b );
- }
- colors.needsUpdate = true;
- }
- mesh.lookAt( _vector$b.setFromMatrixPosition( this.light.matrixWorld ).negate() );
- }
- }
- class GridHelper extends LineSegments {
- constructor( size = 10, divisions = 10, color1 = 0x444444, color2 = 0x888888 ) {
- color1 = new Color( color1 );
- color2 = new Color( color2 );
- const center = divisions / 2;
- const step = size / divisions;
- const halfSize = size / 2;
- const vertices = [], colors = [];
- for ( let i = 0, j = 0, k = - halfSize; i <= divisions; i ++, k += step ) {
- vertices.push( - halfSize, 0, k, halfSize, 0, k );
- vertices.push( k, 0, - halfSize, k, 0, halfSize );
- const color = i === center ? color1 : color2;
- color.toArray( colors, j ); j += 3;
- color.toArray( colors, j ); j += 3;
- color.toArray( colors, j ); j += 3;
- color.toArray( colors, j ); j += 3;
- }
- const geometry = new BufferGeometry();
- geometry.setAttribute( 'position', new Float32BufferAttribute( vertices, 3 ) );
- geometry.setAttribute( 'color', new Float32BufferAttribute( colors, 3 ) );
- const material = new LineBasicMaterial( { vertexColors: true, toneMapped: false } );
- super( geometry, material );
- this.type = 'GridHelper';
- }
- }
- class PolarGridHelper extends LineSegments {
- constructor( radius = 10, radials = 16, circles = 8, divisions = 64, color1 = 0x444444, color2 = 0x888888 ) {
- color1 = new Color( color1 );
- color2 = new Color( color2 );
- const vertices = [];
- const colors = [];
- // create the radials
- for ( let i = 0; i <= radials; i ++ ) {
- const v = ( i / radials ) * ( Math.PI * 2 );
- const x = Math.sin( v ) * radius;
- const z = Math.cos( v ) * radius;
- vertices.push( 0, 0, 0 );
- vertices.push( x, 0, z );
- const color = ( i & 1 ) ? color1 : color2;
- colors.push( color.r, color.g, color.b );
- colors.push( color.r, color.g, color.b );
- }
- // create the circles
- for ( let i = 0; i <= circles; i ++ ) {
- const color = ( i & 1 ) ? color1 : color2;
- const r = radius - ( radius / circles * i );
- for ( let j = 0; j < divisions; j ++ ) {
- // first vertex
- let v = ( j / divisions ) * ( Math.PI * 2 );
- let x = Math.sin( v ) * r;
- let z = Math.cos( v ) * r;
- vertices.push( x, 0, z );
- colors.push( color.r, color.g, color.b );
- // second vertex
- v = ( ( j + 1 ) / divisions ) * ( Math.PI * 2 );
- x = Math.sin( v ) * r;
- z = Math.cos( v ) * r;
- vertices.push( x, 0, z );
- colors.push( color.r, color.g, color.b );
- }
- }
- const geometry = new BufferGeometry();
- geometry.setAttribute( 'position', new Float32BufferAttribute( vertices, 3 ) );
- geometry.setAttribute( 'color', new Float32BufferAttribute( colors, 3 ) );
- const material = new LineBasicMaterial( { vertexColors: true, toneMapped: false } );
- super( geometry, material );
- this.type = 'PolarGridHelper';
- }
- }
- const _v1$6 = /*@__PURE__*/ new Vector3();
- const _v2$3 = /*@__PURE__*/ new Vector3();
- const _v3$1 = /*@__PURE__*/ new Vector3();
- class DirectionalLightHelper extends Object3D {
- constructor( light, size, color ) {
- super();
- this.light = light;
- this.light.updateMatrixWorld();
- this.matrix = light.matrixWorld;
- this.matrixAutoUpdate = false;
- this.color = color;
- if ( size === undefined ) size = 1;
- let geometry = new BufferGeometry();
- geometry.setAttribute( 'position', new Float32BufferAttribute( [
- - size, size, 0,
- size, size, 0,
- size, - size, 0,
- - size, - size, 0,
- - size, size, 0
- ], 3 ) );
- const material = new LineBasicMaterial( { fog: false, toneMapped: false } );
- this.lightPlane = new Line( geometry, material );
- this.add( this.lightPlane );
- geometry = new BufferGeometry();
- geometry.setAttribute( 'position', new Float32BufferAttribute( [ 0, 0, 0, 0, 0, 1 ], 3 ) );
- this.targetLine = new Line( geometry, material );
- this.add( this.targetLine );
- this.update();
- }
- dispose() {
- this.lightPlane.geometry.dispose();
- this.lightPlane.material.dispose();
- this.targetLine.geometry.dispose();
- this.targetLine.material.dispose();
- }
- update() {
- _v1$6.setFromMatrixPosition( this.light.matrixWorld );
- _v2$3.setFromMatrixPosition( this.light.target.matrixWorld );
- _v3$1.subVectors( _v2$3, _v1$6 );
- this.lightPlane.lookAt( _v2$3 );
- if ( this.color !== undefined ) {
- this.lightPlane.material.color.set( this.color );
- this.targetLine.material.color.set( this.color );
- } else {
- this.lightPlane.material.color.copy( this.light.color );
- this.targetLine.material.color.copy( this.light.color );
- }
- this.targetLine.lookAt( _v2$3 );
- this.targetLine.scale.z = _v3$1.length();
- }
- }
- const _vector$c = /*@__PURE__*/ new Vector3();
- const _camera = /*@__PURE__*/ new Camera();
- /**
- * - shows frustum, line of sight and up of the camera
- * - suitable for fast updates
- * - based on frustum visualization in lightgl.js shadowmap example
- * http://evanw.github.com/lightgl.js/tests/shadowmap.html
- */
- class CameraHelper extends LineSegments {
- constructor( camera ) {
- const geometry = new BufferGeometry();
- const material = new LineBasicMaterial( { color: 0xffffff, vertexColors: true, toneMapped: false } );
- const vertices = [];
- const colors = [];
- const pointMap = {};
- // colors
- const colorFrustum = new Color( 0xffaa00 );
- const colorCone = new Color( 0xff0000 );
- const colorUp = new Color( 0x00aaff );
- const colorTarget = new Color( 0xffffff );
- const colorCross = new Color( 0x333333 );
- // near
- addLine( 'n1', 'n2', colorFrustum );
- addLine( 'n2', 'n4', colorFrustum );
- addLine( 'n4', 'n3', colorFrustum );
- addLine( 'n3', 'n1', colorFrustum );
- // far
- addLine( 'f1', 'f2', colorFrustum );
- addLine( 'f2', 'f4', colorFrustum );
- addLine( 'f4', 'f3', colorFrustum );
- addLine( 'f3', 'f1', colorFrustum );
- // sides
- addLine( 'n1', 'f1', colorFrustum );
- addLine( 'n2', 'f2', colorFrustum );
- addLine( 'n3', 'f3', colorFrustum );
- addLine( 'n4', 'f4', colorFrustum );
- // cone
- addLine( 'p', 'n1', colorCone );
- addLine( 'p', 'n2', colorCone );
- addLine( 'p', 'n3', colorCone );
- addLine( 'p', 'n4', colorCone );
- // up
- addLine( 'u1', 'u2', colorUp );
- addLine( 'u2', 'u3', colorUp );
- addLine( 'u3', 'u1', colorUp );
- // target
- addLine( 'c', 't', colorTarget );
- addLine( 'p', 'c', colorCross );
- // cross
- addLine( 'cn1', 'cn2', colorCross );
- addLine( 'cn3', 'cn4', colorCross );
- addLine( 'cf1', 'cf2', colorCross );
- addLine( 'cf3', 'cf4', colorCross );
- function addLine( a, b, color ) {
- addPoint( a, color );
- addPoint( b, color );
- }
- function addPoint( id, color ) {
- vertices.push( 0, 0, 0 );
- colors.push( color.r, color.g, color.b );
- if ( pointMap[ id ] === undefined ) {
- pointMap[ id ] = [];
- }
- pointMap[ id ].push( ( vertices.length / 3 ) - 1 );
- }
- geometry.setAttribute( 'position', new Float32BufferAttribute( vertices, 3 ) );
- geometry.setAttribute( 'color', new Float32BufferAttribute( colors, 3 ) );
- super( geometry, material );
- this.type = 'CameraHelper';
- this.camera = camera;
- if ( this.camera.updateProjectionMatrix ) this.camera.updateProjectionMatrix();
- this.matrix = camera.matrixWorld;
- this.matrixAutoUpdate = false;
- this.pointMap = pointMap;
- this.update();
- }
- update() {
- const geometry = this.geometry;
- const pointMap = this.pointMap;
- const w = 1, h = 1;
- // we need just camera projection matrix inverse
- // world matrix must be identity
- _camera.projectionMatrixInverse.copy( this.camera.projectionMatrixInverse );
- // center / target
- setPoint( 'c', pointMap, geometry, _camera, 0, 0, - 1 );
- setPoint( 't', pointMap, geometry, _camera, 0, 0, 1 );
- // near
- setPoint( 'n1', pointMap, geometry, _camera, - w, - h, - 1 );
- setPoint( 'n2', pointMap, geometry, _camera, w, - h, - 1 );
- setPoint( 'n3', pointMap, geometry, _camera, - w, h, - 1 );
- setPoint( 'n4', pointMap, geometry, _camera, w, h, - 1 );
- // far
- setPoint( 'f1', pointMap, geometry, _camera, - w, - h, 1 );
- setPoint( 'f2', pointMap, geometry, _camera, w, - h, 1 );
- setPoint( 'f3', pointMap, geometry, _camera, - w, h, 1 );
- setPoint( 'f4', pointMap, geometry, _camera, w, h, 1 );
- // up
- setPoint( 'u1', pointMap, geometry, _camera, w * 0.7, h * 1.1, - 1 );
- setPoint( 'u2', pointMap, geometry, _camera, - w * 0.7, h * 1.1, - 1 );
- setPoint( 'u3', pointMap, geometry, _camera, 0, h * 2, - 1 );
- // cross
- setPoint( 'cf1', pointMap, geometry, _camera, - w, 0, 1 );
- setPoint( 'cf2', pointMap, geometry, _camera, w, 0, 1 );
- setPoint( 'cf3', pointMap, geometry, _camera, 0, - h, 1 );
- setPoint( 'cf4', pointMap, geometry, _camera, 0, h, 1 );
- setPoint( 'cn1', pointMap, geometry, _camera, - w, 0, - 1 );
- setPoint( 'cn2', pointMap, geometry, _camera, w, 0, - 1 );
- setPoint( 'cn3', pointMap, geometry, _camera, 0, - h, - 1 );
- setPoint( 'cn4', pointMap, geometry, _camera, 0, h, - 1 );
- geometry.getAttribute( 'position' ).needsUpdate = true;
- }
- }
- function setPoint( point, pointMap, geometry, camera, x, y, z ) {
- _vector$c.set( x, y, z ).unproject( camera );
- const points = pointMap[ point ];
- if ( points !== undefined ) {
- const position = geometry.getAttribute( 'position' );
- for ( let i = 0, l = points.length; i < l; i ++ ) {
- position.setXYZ( points[ i ], _vector$c.x, _vector$c.y, _vector$c.z );
- }
- }
- }
- const _box$3 = /*@__PURE__*/ new Box3();
- class BoxHelper extends LineSegments {
- constructor( object, color = 0xffff00 ) {
- const indices = new Uint16Array( [ 0, 1, 1, 2, 2, 3, 3, 0, 4, 5, 5, 6, 6, 7, 7, 4, 0, 4, 1, 5, 2, 6, 3, 7 ] );
- const positions = new Float32Array( 8 * 3 );
- const geometry = new BufferGeometry();
- geometry.setIndex( new BufferAttribute( indices, 1 ) );
- geometry.setAttribute( 'position', new BufferAttribute( positions, 3 ) );
- super( geometry, new LineBasicMaterial( { color: color, toneMapped: false } ) );
- this.object = object;
- this.type = 'BoxHelper';
- this.matrixAutoUpdate = false;
- this.update();
- }
- update( object ) {
- if ( object !== undefined ) {
- console.warn( 'THREE.BoxHelper: .update() has no longer arguments.' );
- }
- if ( this.object !== undefined ) {
- _box$3.setFromObject( this.object );
- }
- if ( _box$3.isEmpty() ) return;
- const min = _box$3.min;
- const max = _box$3.max;
- /*
- 5____4
- 1/___0/|
- | 6__|_7
- 2/___3/
- 0: max.x, max.y, max.z
- 1: min.x, max.y, max.z
- 2: min.x, min.y, max.z
- 3: max.x, min.y, max.z
- 4: max.x, max.y, min.z
- 5: min.x, max.y, min.z
- 6: min.x, min.y, min.z
- 7: max.x, min.y, min.z
- */
- const position = this.geometry.attributes.position;
- const array = position.array;
- array[ 0 ] = max.x; array[ 1 ] = max.y; array[ 2 ] = max.z;
- array[ 3 ] = min.x; array[ 4 ] = max.y; array[ 5 ] = max.z;
- array[ 6 ] = min.x; array[ 7 ] = min.y; array[ 8 ] = max.z;
- array[ 9 ] = max.x; array[ 10 ] = min.y; array[ 11 ] = max.z;
- array[ 12 ] = max.x; array[ 13 ] = max.y; array[ 14 ] = min.z;
- array[ 15 ] = min.x; array[ 16 ] = max.y; array[ 17 ] = min.z;
- array[ 18 ] = min.x; array[ 19 ] = min.y; array[ 20 ] = min.z;
- array[ 21 ] = max.x; array[ 22 ] = min.y; array[ 23 ] = min.z;
- position.needsUpdate = true;
- this.geometry.computeBoundingSphere();
- }
- setFromObject( object ) {
- this.object = object;
- this.update();
- return this;
- }
- copy( source ) {
- LineSegments.prototype.copy.call( this, source );
- this.object = source.object;
- return this;
- }
- }
- class Box3Helper extends LineSegments {
- constructor( box, color = 0xffff00 ) {
- const indices = new Uint16Array( [ 0, 1, 1, 2, 2, 3, 3, 0, 4, 5, 5, 6, 6, 7, 7, 4, 0, 4, 1, 5, 2, 6, 3, 7 ] );
- const positions = [ 1, 1, 1, - 1, 1, 1, - 1, - 1, 1, 1, - 1, 1, 1, 1, - 1, - 1, 1, - 1, - 1, - 1, - 1, 1, - 1, - 1 ];
- const geometry = new BufferGeometry();
- geometry.setIndex( new BufferAttribute( indices, 1 ) );
- geometry.setAttribute( 'position', new Float32BufferAttribute( positions, 3 ) );
- super( geometry, new LineBasicMaterial( { color: color, toneMapped: false } ) );
- this.box = box;
- this.type = 'Box3Helper';
- this.geometry.computeBoundingSphere();
- }
- updateMatrixWorld( force ) {
- const box = this.box;
- if ( box.isEmpty() ) return;
- box.getCenter( this.position );
- box.getSize( this.scale );
- this.scale.multiplyScalar( 0.5 );
- super.updateMatrixWorld( force );
- }
- }
- class PlaneHelper extends Line {
- constructor( plane, size = 1, hex = 0xffff00 ) {
- const color = hex;
- const positions = [ 1, - 1, 1, - 1, 1, 1, - 1, - 1, 1, 1, 1, 1, - 1, 1, 1, - 1, - 1, 1, 1, - 1, 1, 1, 1, 1, 0, 0, 1, 0, 0, 0 ];
- const geometry = new BufferGeometry();
- geometry.setAttribute( 'position', new Float32BufferAttribute( positions, 3 ) );
- geometry.computeBoundingSphere();
- super( geometry, new LineBasicMaterial( { color: color, toneMapped: false } ) );
- this.type = 'PlaneHelper';
- this.plane = plane;
- this.size = size;
- const positions2 = [ 1, 1, 1, - 1, 1, 1, - 1, - 1, 1, 1, 1, 1, - 1, - 1, 1, 1, - 1, 1 ];
- const geometry2 = new BufferGeometry();
- geometry2.setAttribute( 'position', new Float32BufferAttribute( positions2, 3 ) );
- geometry2.computeBoundingSphere();
- this.add( new Mesh( geometry2, new MeshBasicMaterial( { color: color, opacity: 0.2, transparent: true, depthWrite: false, toneMapped: false } ) ) );
- }
- updateMatrixWorld( force ) {
- let scale = - this.plane.constant;
- if ( Math.abs( scale ) < 1e-8 ) scale = 1e-8; // sign does not matter
- this.scale.set( 0.5 * this.size, 0.5 * this.size, scale );
- this.children[ 0 ].material.side = ( scale < 0 ) ? BackSide : FrontSide; // renderer flips side when determinant < 0; flipping not wanted here
- this.lookAt( this.plane.normal );
- super.updateMatrixWorld( force );
- }
- }
- const _axis = /*@__PURE__*/ new Vector3();
- let _lineGeometry, _coneGeometry;
- class ArrowHelper extends Object3D {
- constructor( dir, origin, length, color, headLength, headWidth ) {
- super();
- // dir is assumed to be normalized
- this.type = 'ArrowHelper';
- if ( dir === undefined ) dir = new Vector3( 0, 0, 1 );
- if ( origin === undefined ) origin = new Vector3( 0, 0, 0 );
- if ( length === undefined ) length = 1;
- if ( color === undefined ) color = 0xffff00;
- if ( headLength === undefined ) headLength = 0.2 * length;
- if ( headWidth === undefined ) headWidth = 0.2 * headLength;
- if ( _lineGeometry === undefined ) {
- _lineGeometry = new BufferGeometry();
- _lineGeometry.setAttribute( 'position', new Float32BufferAttribute( [ 0, 0, 0, 0, 1, 0 ], 3 ) );
- _coneGeometry = new CylinderBufferGeometry( 0, 0.5, 1, 5, 1 );
- _coneGeometry.translate( 0, - 0.5, 0 );
- }
- this.position.copy( origin );
- this.line = new Line( _lineGeometry, new LineBasicMaterial( { color: color, toneMapped: false } ) );
- this.line.matrixAutoUpdate = false;
- this.add( this.line );
- this.cone = new Mesh( _coneGeometry, new MeshBasicMaterial( { color: color, toneMapped: false } ) );
- this.cone.matrixAutoUpdate = false;
- this.add( this.cone );
- this.setDirection( dir );
- this.setLength( length, headLength, headWidth );
- }
- setDirection( dir ) {
- // dir is assumed to be normalized
- if ( dir.y > 0.99999 ) {
- this.quaternion.set( 0, 0, 0, 1 );
- } else if ( dir.y < - 0.99999 ) {
- this.quaternion.set( 1, 0, 0, 0 );
- } else {
- _axis.set( dir.z, 0, - dir.x ).normalize();
- const radians = Math.acos( dir.y );
- this.quaternion.setFromAxisAngle( _axis, radians );
- }
- }
- setLength( length, headLength, headWidth ) {
- if ( headLength === undefined ) headLength = 0.2 * length;
- if ( headWidth === undefined ) headWidth = 0.2 * headLength;
- this.line.scale.set( 1, Math.max( 0.0001, length - headLength ), 1 ); // see #17458
- this.line.updateMatrix();
- this.cone.scale.set( headWidth, headLength, headWidth );
- this.cone.position.y = length;
- this.cone.updateMatrix();
- }
- setColor( color ) {
- this.line.material.color.set( color );
- this.cone.material.color.set( color );
- }
- copy( source ) {
- super.copy( source, false );
- this.line.copy( source.line );
- this.cone.copy( source.cone );
- return this;
- }
- }
- class AxesHelper extends LineSegments {
- constructor( size = 1 ) {
- const vertices = [
- 0, 0, 0, size, 0, 0,
- 0, 0, 0, 0, size, 0,
- 0, 0, 0, 0, 0, size
- ];
- const colors = [
- 1, 0, 0, 1, 0.6, 0,
- 0, 1, 0, 0.6, 1, 0,
- 0, 0, 1, 0, 0.6, 1
- ];
- const geometry = new BufferGeometry();
- geometry.setAttribute( 'position', new Float32BufferAttribute( vertices, 3 ) );
- geometry.setAttribute( 'color', new Float32BufferAttribute( colors, 3 ) );
- const material = new LineBasicMaterial( { vertexColors: true, toneMapped: false } );
- super( geometry, material );
- this.type = 'AxesHelper';
- }
- }
- const _floatView = new Float32Array( 1 );
- const _int32View = new Int32Array( _floatView.buffer );
- const DataUtils = {
- // Converts float32 to float16 (stored as uint16 value).
- toHalfFloat: function ( val ) {
- // Source: http://gamedev.stackexchange.com/questions/17326/conversion-of-a-number-from-single-precision-floating-point-representation-to-a/17410#17410
- /* This method is faster than the OpenEXR implementation (very often
- * used, eg. in Ogre), with the additional benefit of rounding, inspired
- * by James Tursa?s half-precision code. */
- _floatView[ 0 ] = val;
- const x = _int32View[ 0 ];
- let bits = ( x >> 16 ) & 0x8000; /* Get the sign */
- let m = ( x >> 12 ) & 0x07ff; /* Keep one extra bit for rounding */
- const e = ( x >> 23 ) & 0xff; /* Using int is faster here */
- /* If zero, or denormal, or exponent underflows too much for a denormal
- * half, return signed zero. */
- if ( e < 103 ) return bits;
- /* If NaN, return NaN. If Inf or exponent overflow, return Inf. */
- if ( e > 142 ) {
- bits |= 0x7c00;
- /* If exponent was 0xff and one mantissa bit was set, it means NaN,
- * not Inf, so make sure we set one mantissa bit too. */
- bits |= ( ( e == 255 ) ? 0 : 1 ) && ( x & 0x007fffff );
- return bits;
- }
- /* If exponent underflows but not too much, return a denormal */
- if ( e < 113 ) {
- m |= 0x0800;
- /* Extra rounding may overflow and set mantissa to 0 and exponent
- * to 1, which is OK. */
- bits |= ( m >> ( 114 - e ) ) + ( ( m >> ( 113 - e ) ) & 1 );
- return bits;
- }
- bits |= ( ( e - 112 ) << 10 ) | ( m >> 1 );
- /* Extra rounding. An overflow will set mantissa to 0 and increment
- * the exponent, which is OK. */
- bits += m & 1;
- return bits;
- }
- };
- const LOD_MIN = 4;
- const LOD_MAX = 8;
- const SIZE_MAX = Math.pow( 2, LOD_MAX );
- // The standard deviations (radians) associated with the extra mips. These are
- // chosen to approximate a Trowbridge-Reitz distribution function times the
- // geometric shadowing function. These sigma values squared must match the
- // variance #defines in cube_uv_reflection_fragment.glsl.js.
- const EXTRA_LOD_SIGMA = [ 0.125, 0.215, 0.35, 0.446, 0.526, 0.582 ];
- const TOTAL_LODS = LOD_MAX - LOD_MIN + 1 + EXTRA_LOD_SIGMA.length;
- // The maximum length of the blur for loop. Smaller sigmas will use fewer
- // samples and exit early, but not recompile the shader.
- const MAX_SAMPLES = 20;
- const ENCODINGS = {
- [ LinearEncoding ]: 0,
- [ sRGBEncoding ]: 1,
- [ RGBEEncoding ]: 2,
- [ RGBM7Encoding ]: 3,
- [ RGBM16Encoding ]: 4,
- [ RGBDEncoding ]: 5,
- [ GammaEncoding ]: 6
- };
- const _flatCamera = /*@__PURE__*/ new OrthographicCamera();
- const { _lodPlanes, _sizeLods, _sigmas } = /*@__PURE__*/ _createPlanes();
- const _clearColor = /*@__PURE__*/ new Color();
- let _oldTarget = null;
- // Golden Ratio
- const PHI = ( 1 + Math.sqrt( 5 ) ) / 2;
- const INV_PHI = 1 / PHI;
- // Vertices of a dodecahedron (except the opposites, which represent the
- // same axis), used as axis directions evenly spread on a sphere.
- const _axisDirections = [
- /*@__PURE__*/ new Vector3( 1, 1, 1 ),
- /*@__PURE__*/ new Vector3( - 1, 1, 1 ),
- /*@__PURE__*/ new Vector3( 1, 1, - 1 ),
- /*@__PURE__*/ new Vector3( - 1, 1, - 1 ),
- /*@__PURE__*/ new Vector3( 0, PHI, INV_PHI ),
- /*@__PURE__*/ new Vector3( 0, PHI, - INV_PHI ),
- /*@__PURE__*/ new Vector3( INV_PHI, 0, PHI ),
- /*@__PURE__*/ new Vector3( - INV_PHI, 0, PHI ),
- /*@__PURE__*/ new Vector3( PHI, INV_PHI, 0 ),
- /*@__PURE__*/ new Vector3( - PHI, INV_PHI, 0 ) ];
- /**
- * This class generates a Prefiltered, Mipmapped Radiance Environment Map
- * (PMREM) from a cubeMap environment texture. This allows different levels of
- * blur to be quickly accessed based on material roughness. It is packed into a
- * special CubeUV format that allows us to perform custom interpolation so that
- * we can support nonlinear formats such as RGBE. Unlike a traditional mipmap
- * chain, it only goes down to the LOD_MIN level (above), and then creates extra
- * even more filtered 'mips' at the same LOD_MIN resolution, associated with
- * higher roughness levels. In this way we maintain resolution to smoothly
- * interpolate diffuse lighting while limiting sampling computation.
- */
- class PMREMGenerator {
- constructor( renderer ) {
- this._renderer = renderer;
- this._pingPongRenderTarget = null;
- this._blurMaterial = _getBlurShader( MAX_SAMPLES );
- this._equirectShader = null;
- this._cubemapShader = null;
- this._compileMaterial( this._blurMaterial );
- }
- /**
- * Generates a PMREM from a supplied Scene, which can be faster than using an
- * image if networking bandwidth is low. Optional sigma specifies a blur radius
- * in radians to be applied to the scene before PMREM generation. Optional near
- * and far planes ensure the scene is rendered in its entirety (the cubeCamera
- * is placed at the origin).
- */
- fromScene( scene, sigma = 0, near = 0.1, far = 100 ) {
- _oldTarget = this._renderer.getRenderTarget();
- const cubeUVRenderTarget = this._allocateTargets();
- this._sceneToCubeUV( scene, near, far, cubeUVRenderTarget );
- if ( sigma > 0 ) {
- this._blur( cubeUVRenderTarget, 0, 0, sigma );
- }
- this._applyPMREM( cubeUVRenderTarget );
- this._cleanup( cubeUVRenderTarget );
- return cubeUVRenderTarget;
- }
- /**
- * Generates a PMREM from an equirectangular texture, which can be either LDR
- * (RGBFormat) or HDR (RGBEFormat). The ideal input image size is 1k (1024 x 512),
- * as this matches best with the 256 x 256 cubemap output.
- */
- fromEquirectangular( equirectangular ) {
- return this._fromTexture( equirectangular );
- }
- /**
- * Generates a PMREM from an cubemap texture, which can be either LDR
- * (RGBFormat) or HDR (RGBEFormat). The ideal input cube size is 256 x 256,
- * as this matches best with the 256 x 256 cubemap output.
- */
- fromCubemap( cubemap ) {
- return this._fromTexture( cubemap );
- }
- /**
- * Pre-compiles the cubemap shader. You can get faster start-up by invoking this method during
- * your texture's network fetch for increased concurrency.
- */
- compileCubemapShader() {
- if ( this._cubemapShader === null ) {
- this._cubemapShader = _getCubemapShader();
- this._compileMaterial( this._cubemapShader );
- }
- }
- /**
- * Pre-compiles the equirectangular shader. You can get faster start-up by invoking this method during
- * your texture's network fetch for increased concurrency.
- */
- compileEquirectangularShader() {
- if ( this._equirectShader === null ) {
- this._equirectShader = _getEquirectShader();
- this._compileMaterial( this._equirectShader );
- }
- }
- /**
- * Disposes of the PMREMGenerator's internal memory. Note that PMREMGenerator is a static class,
- * so you should not need more than one PMREMGenerator object. If you do, calling dispose() on
- * one of them will cause any others to also become unusable.
- */
- dispose() {
- this._blurMaterial.dispose();
- if ( this._cubemapShader !== null ) this._cubemapShader.dispose();
- if ( this._equirectShader !== null ) this._equirectShader.dispose();
- for ( let i = 0; i < _lodPlanes.length; i ++ ) {
- _lodPlanes[ i ].dispose();
- }
- }
- // private interface
- _cleanup( outputTarget ) {
- this._pingPongRenderTarget.dispose();
- this._renderer.setRenderTarget( _oldTarget );
- outputTarget.scissorTest = false;
- _setViewport( outputTarget, 0, 0, outputTarget.width, outputTarget.height );
- }
- _fromTexture( texture ) {
- _oldTarget = this._renderer.getRenderTarget();
- const cubeUVRenderTarget = this._allocateTargets( texture );
- this._textureToCubeUV( texture, cubeUVRenderTarget );
- this._applyPMREM( cubeUVRenderTarget );
- this._cleanup( cubeUVRenderTarget );
- return cubeUVRenderTarget;
- }
- _allocateTargets( texture ) { // warning: null texture is valid
- const params = {
- magFilter: NearestFilter,
- minFilter: NearestFilter,
- generateMipmaps: false,
- type: UnsignedByteType,
- format: RGBEFormat,
- encoding: _isLDR( texture ) ? texture.encoding : RGBEEncoding,
- depthBuffer: false
- };
- const cubeUVRenderTarget = _createRenderTarget( params );
- cubeUVRenderTarget.depthBuffer = texture ? false : true;
- this._pingPongRenderTarget = _createRenderTarget( params );
- return cubeUVRenderTarget;
- }
- _compileMaterial( material ) {
- const tmpMesh = new Mesh( _lodPlanes[ 0 ], material );
- this._renderer.compile( tmpMesh, _flatCamera );
- }
- _sceneToCubeUV( scene, near, far, cubeUVRenderTarget ) {
- const fov = 90;
- const aspect = 1;
- const cubeCamera = new PerspectiveCamera( fov, aspect, near, far );
- const upSign = [ 1, - 1, 1, 1, 1, 1 ];
- const forwardSign = [ 1, 1, 1, - 1, - 1, - 1 ];
- const renderer = this._renderer;
- const outputEncoding = renderer.outputEncoding;
- const toneMapping = renderer.toneMapping;
- renderer.getClearColor( _clearColor );
- const clearAlpha = renderer.getClearAlpha();
- renderer.toneMapping = NoToneMapping;
- renderer.outputEncoding = LinearEncoding;
- let background = scene.background;
- if ( background && background.isColor ) {
- background.convertSRGBToLinear();
- // Convert linear to RGBE
- const maxComponent = Math.max( background.r, background.g, background.b );
- const fExp = Math.min( Math.max( Math.ceil( Math.log2( maxComponent ) ), - 128.0 ), 127.0 );
- background = background.multiplyScalar( Math.pow( 2.0, - fExp ) );
- const alpha = ( fExp + 128.0 ) / 255.0;
- renderer.setClearColor( background, alpha );
- scene.background = null;
- }
- for ( let i = 0; i < 6; i ++ ) {
- const col = i % 3;
- if ( col == 0 ) {
- cubeCamera.up.set( 0, upSign[ i ], 0 );
- cubeCamera.lookAt( forwardSign[ i ], 0, 0 );
- } else if ( col == 1 ) {
- cubeCamera.up.set( 0, 0, upSign[ i ] );
- cubeCamera.lookAt( 0, forwardSign[ i ], 0 );
- } else {
- cubeCamera.up.set( 0, upSign[ i ], 0 );
- cubeCamera.lookAt( 0, 0, forwardSign[ i ] );
- }
- _setViewport( cubeUVRenderTarget,
- col * SIZE_MAX, i > 2 ? SIZE_MAX : 0, SIZE_MAX, SIZE_MAX );
- renderer.setRenderTarget( cubeUVRenderTarget );
- renderer.render( scene, cubeCamera );
- }
- renderer.toneMapping = toneMapping;
- renderer.outputEncoding = outputEncoding;
- renderer.setClearColor( _clearColor, clearAlpha );
- }
- _textureToCubeUV( texture, cubeUVRenderTarget ) {
- const renderer = this._renderer;
- if ( texture.isCubeTexture ) {
- if ( this._cubemapShader == null ) {
- this._cubemapShader = _getCubemapShader();
- }
- } else {
- if ( this._equirectShader == null ) {
- this._equirectShader = _getEquirectShader();
- }
- }
- const material = texture.isCubeTexture ? this._cubemapShader : this._equirectShader;
- const mesh = new Mesh( _lodPlanes[ 0 ], material );
- const uniforms = material.uniforms;
- uniforms[ 'envMap' ].value = texture;
- if ( ! texture.isCubeTexture ) {
- uniforms[ 'texelSize' ].value.set( 1.0 / texture.image.width, 1.0 / texture.image.height );
- }
- uniforms[ 'inputEncoding' ].value = ENCODINGS[ texture.encoding ];
- uniforms[ 'outputEncoding' ].value = ENCODINGS[ cubeUVRenderTarget.texture.encoding ];
- _setViewport( cubeUVRenderTarget, 0, 0, 3 * SIZE_MAX, 2 * SIZE_MAX );
- renderer.setRenderTarget( cubeUVRenderTarget );
- renderer.render( mesh, _flatCamera );
- }
- _applyPMREM( cubeUVRenderTarget ) {
- const renderer = this._renderer;
- const autoClear = renderer.autoClear;
- renderer.autoClear = false;
- for ( let i = 1; i < TOTAL_LODS; i ++ ) {
- const sigma = Math.sqrt( _sigmas[ i ] * _sigmas[ i ] - _sigmas[ i - 1 ] * _sigmas[ i - 1 ] );
- const poleAxis = _axisDirections[ ( i - 1 ) % _axisDirections.length ];
- this._blur( cubeUVRenderTarget, i - 1, i, sigma, poleAxis );
- }
- renderer.autoClear = autoClear;
- }
- /**
- * This is a two-pass Gaussian blur for a cubemap. Normally this is done
- * vertically and horizontally, but this breaks down on a cube. Here we apply
- * the blur latitudinally (around the poles), and then longitudinally (towards
- * the poles) to approximate the orthogonally-separable blur. It is least
- * accurate at the poles, but still does a decent job.
- */
- _blur( cubeUVRenderTarget, lodIn, lodOut, sigma, poleAxis ) {
- const pingPongRenderTarget = this._pingPongRenderTarget;
- this._halfBlur(
- cubeUVRenderTarget,
- pingPongRenderTarget,
- lodIn,
- lodOut,
- sigma,
- 'latitudinal',
- poleAxis );
- this._halfBlur(
- pingPongRenderTarget,
- cubeUVRenderTarget,
- lodOut,
- lodOut,
- sigma,
- 'longitudinal',
- poleAxis );
- }
- _halfBlur( targetIn, targetOut, lodIn, lodOut, sigmaRadians, direction, poleAxis ) {
- const renderer = this._renderer;
- const blurMaterial = this._blurMaterial;
- if ( direction !== 'latitudinal' && direction !== 'longitudinal' ) {
- console.error(
- 'blur direction must be either latitudinal or longitudinal!' );
- }
- // Number of standard deviations at which to cut off the discrete approximation.
- const STANDARD_DEVIATIONS = 3;
- const blurMesh = new Mesh( _lodPlanes[ lodOut ], blurMaterial );
- const blurUniforms = blurMaterial.uniforms;
- const pixels = _sizeLods[ lodIn ] - 1;
- const radiansPerPixel = isFinite( sigmaRadians ) ? Math.PI / ( 2 * pixels ) : 2 * Math.PI / ( 2 * MAX_SAMPLES - 1 );
- const sigmaPixels = sigmaRadians / radiansPerPixel;
- const samples = isFinite( sigmaRadians ) ? 1 + Math.floor( STANDARD_DEVIATIONS * sigmaPixels ) : MAX_SAMPLES;
- if ( samples > MAX_SAMPLES ) {
- console.warn( `sigmaRadians, ${
- sigmaRadians}, is too large and will clip, as it requested ${
- samples} samples when the maximum is set to ${MAX_SAMPLES}` );
- }
- const weights = [];
- let sum = 0;
- for ( let i = 0; i < MAX_SAMPLES; ++ i ) {
- const x = i / sigmaPixels;
- const weight = Math.exp( - x * x / 2 );
- weights.push( weight );
- if ( i == 0 ) {
- sum += weight;
- } else if ( i < samples ) {
- sum += 2 * weight;
- }
- }
- for ( let i = 0; i < weights.length; i ++ ) {
- weights[ i ] = weights[ i ] / sum;
- }
- blurUniforms[ 'envMap' ].value = targetIn.texture;
- blurUniforms[ 'samples' ].value = samples;
- blurUniforms[ 'weights' ].value = weights;
- blurUniforms[ 'latitudinal' ].value = direction === 'latitudinal';
- if ( poleAxis ) {
- blurUniforms[ 'poleAxis' ].value = poleAxis;
- }
- blurUniforms[ 'dTheta' ].value = radiansPerPixel;
- blurUniforms[ 'mipInt' ].value = LOD_MAX - lodIn;
- blurUniforms[ 'inputEncoding' ].value = ENCODINGS[ targetIn.texture.encoding ];
- blurUniforms[ 'outputEncoding' ].value = ENCODINGS[ targetIn.texture.encoding ];
- const outputSize = _sizeLods[ lodOut ];
- const x = 3 * Math.max( 0, SIZE_MAX - 2 * outputSize );
- const y = ( lodOut === 0 ? 0 : 2 * SIZE_MAX ) + 2 * outputSize * ( lodOut > LOD_MAX - LOD_MIN ? lodOut - LOD_MAX + LOD_MIN : 0 );
- _setViewport( targetOut, x, y, 3 * outputSize, 2 * outputSize );
- renderer.setRenderTarget( targetOut );
- renderer.render( blurMesh, _flatCamera );
- }
- }
- function _isLDR( texture ) {
- if ( texture === undefined || texture.type !== UnsignedByteType ) return false;
- return texture.encoding === LinearEncoding || texture.encoding === sRGBEncoding || texture.encoding === GammaEncoding;
- }
- function _createPlanes() {
- const _lodPlanes = [];
- const _sizeLods = [];
- const _sigmas = [];
- let lod = LOD_MAX;
- for ( let i = 0; i < TOTAL_LODS; i ++ ) {
- const sizeLod = Math.pow( 2, lod );
- _sizeLods.push( sizeLod );
- let sigma = 1.0 / sizeLod;
- if ( i > LOD_MAX - LOD_MIN ) {
- sigma = EXTRA_LOD_SIGMA[ i - LOD_MAX + LOD_MIN - 1 ];
- } else if ( i == 0 ) {
- sigma = 0;
- }
- _sigmas.push( sigma );
- const texelSize = 1.0 / ( sizeLod - 1 );
- const min = - texelSize / 2;
- const max = 1 + texelSize / 2;
- const uv1 = [ min, min, max, min, max, max, min, min, max, max, min, max ];
- const cubeFaces = 6;
- const vertices = 6;
- const positionSize = 3;
- const uvSize = 2;
- const faceIndexSize = 1;
- const position = new Float32Array( positionSize * vertices * cubeFaces );
- const uv = new Float32Array( uvSize * vertices * cubeFaces );
- const faceIndex = new Float32Array( faceIndexSize * vertices * cubeFaces );
- for ( let face = 0; face < cubeFaces; face ++ ) {
- const x = ( face % 3 ) * 2 / 3 - 1;
- const y = face > 2 ? 0 : - 1;
- const coordinates = [
- x, y, 0,
- x + 2 / 3, y, 0,
- x + 2 / 3, y + 1, 0,
- x, y, 0,
- x + 2 / 3, y + 1, 0,
- x, y + 1, 0
- ];
- position.set( coordinates, positionSize * vertices * face );
- uv.set( uv1, uvSize * vertices * face );
- const fill = [ face, face, face, face, face, face ];
- faceIndex.set( fill, faceIndexSize * vertices * face );
- }
- const planes = new BufferGeometry();
- planes.setAttribute( 'position', new BufferAttribute( position, positionSize ) );
- planes.setAttribute( 'uv', new BufferAttribute( uv, uvSize ) );
- planes.setAttribute( 'faceIndex', new BufferAttribute( faceIndex, faceIndexSize ) );
- _lodPlanes.push( planes );
- if ( lod > LOD_MIN ) {
- lod --;
- }
- }
- return { _lodPlanes, _sizeLods, _sigmas };
- }
- function _createRenderTarget( params ) {
- const cubeUVRenderTarget = new WebGLRenderTarget( 3 * SIZE_MAX, 3 * SIZE_MAX, params );
- cubeUVRenderTarget.texture.mapping = CubeUVReflectionMapping;
- cubeUVRenderTarget.texture.name = 'PMREM.cubeUv';
- cubeUVRenderTarget.scissorTest = true;
- return cubeUVRenderTarget;
- }
- function _setViewport( target, x, y, width, height ) {
- target.viewport.set( x, y, width, height );
- target.scissor.set( x, y, width, height );
- }
- function _getBlurShader( maxSamples ) {
- const weights = new Float32Array( maxSamples );
- const poleAxis = new Vector3( 0, 1, 0 );
- const shaderMaterial = new RawShaderMaterial( {
- name: 'SphericalGaussianBlur',
- defines: { 'n': maxSamples },
- uniforms: {
- 'envMap': { value: null },
- 'samples': { value: 1 },
- 'weights': { value: weights },
- 'latitudinal': { value: false },
- 'dTheta': { value: 0 },
- 'mipInt': { value: 0 },
- 'poleAxis': { value: poleAxis },
- 'inputEncoding': { value: ENCODINGS[ LinearEncoding ] },
- 'outputEncoding': { value: ENCODINGS[ LinearEncoding ] }
- },
- vertexShader: _getCommonVertexShader(),
- fragmentShader: /* glsl */`
- precision mediump float;
- precision mediump int;
- varying vec3 vOutputDirection;
- uniform sampler2D envMap;
- uniform int samples;
- uniform float weights[ n ];
- uniform bool latitudinal;
- uniform float dTheta;
- uniform float mipInt;
- uniform vec3 poleAxis;
- ${ _getEncodings() }
- #define ENVMAP_TYPE_CUBE_UV
- #include <cube_uv_reflection_fragment>
- vec3 getSample( float theta, vec3 axis ) {
- float cosTheta = cos( theta );
- // Rodrigues' axis-angle rotation
- vec3 sampleDirection = vOutputDirection * cosTheta
- + cross( axis, vOutputDirection ) * sin( theta )
- + axis * dot( axis, vOutputDirection ) * ( 1.0 - cosTheta );
- return bilinearCubeUV( envMap, sampleDirection, mipInt );
- }
- void main() {
- vec3 axis = latitudinal ? poleAxis : cross( poleAxis, vOutputDirection );
- if ( all( equal( axis, vec3( 0.0 ) ) ) ) {
- axis = vec3( vOutputDirection.z, 0.0, - vOutputDirection.x );
- }
- axis = normalize( axis );
- gl_FragColor = vec4( 0.0, 0.0, 0.0, 1.0 );
- gl_FragColor.rgb += weights[ 0 ] * getSample( 0.0, axis );
- for ( int i = 1; i < n; i++ ) {
- if ( i >= samples ) {
- break;
- }
- float theta = dTheta * float( i );
- gl_FragColor.rgb += weights[ i ] * getSample( -1.0 * theta, axis );
- gl_FragColor.rgb += weights[ i ] * getSample( theta, axis );
- }
- gl_FragColor = linearToOutputTexel( gl_FragColor );
- }
- `,
- blending: NoBlending,
- depthTest: false,
- depthWrite: false
- } );
- return shaderMaterial;
- }
- function _getEquirectShader() {
- const texelSize = new Vector2$1( 1, 1 );
- const shaderMaterial = new RawShaderMaterial( {
- name: 'EquirectangularToCubeUV',
- uniforms: {
- 'envMap': { value: null },
- 'texelSize': { value: texelSize },
- 'inputEncoding': { value: ENCODINGS[ LinearEncoding ] },
- 'outputEncoding': { value: ENCODINGS[ LinearEncoding ] }
- },
- vertexShader: _getCommonVertexShader(),
- fragmentShader: /* glsl */`
- precision mediump float;
- precision mediump int;
- varying vec3 vOutputDirection;
- uniform sampler2D envMap;
- uniform vec2 texelSize;
- ${ _getEncodings() }
- #include <common>
- void main() {
- gl_FragColor = vec4( 0.0, 0.0, 0.0, 1.0 );
- vec3 outputDirection = normalize( vOutputDirection );
- vec2 uv = equirectUv( outputDirection );
- vec2 f = fract( uv / texelSize - 0.5 );
- uv -= f * texelSize;
- vec3 tl = envMapTexelToLinear( texture2D ( envMap, uv ) ).rgb;
- uv.x += texelSize.x;
- vec3 tr = envMapTexelToLinear( texture2D ( envMap, uv ) ).rgb;
- uv.y += texelSize.y;
- vec3 br = envMapTexelToLinear( texture2D ( envMap, uv ) ).rgb;
- uv.x -= texelSize.x;
- vec3 bl = envMapTexelToLinear( texture2D ( envMap, uv ) ).rgb;
- vec3 tm = mix( tl, tr, f.x );
- vec3 bm = mix( bl, br, f.x );
- gl_FragColor.rgb = mix( tm, bm, f.y );
- gl_FragColor = linearToOutputTexel( gl_FragColor );
- }
- `,
- blending: NoBlending,
- depthTest: false,
- depthWrite: false
- } );
- return shaderMaterial;
- }
- function _getCubemapShader() {
- const shaderMaterial = new RawShaderMaterial( {
- name: 'CubemapToCubeUV',
- uniforms: {
- 'envMap': { value: null },
- 'inputEncoding': { value: ENCODINGS[ LinearEncoding ] },
- 'outputEncoding': { value: ENCODINGS[ LinearEncoding ] }
- },
- vertexShader: _getCommonVertexShader(),
- fragmentShader: /* glsl */`
- precision mediump float;
- precision mediump int;
- varying vec3 vOutputDirection;
- uniform samplerCube envMap;
- ${ _getEncodings() }
- void main() {
- gl_FragColor = vec4( 0.0, 0.0, 0.0, 1.0 );
- gl_FragColor.rgb = envMapTexelToLinear( textureCube( envMap, vec3( - vOutputDirection.x, vOutputDirection.yz ) ) ).rgb;
- gl_FragColor = linearToOutputTexel( gl_FragColor );
- }
- `,
- blending: NoBlending,
- depthTest: false,
- depthWrite: false
- } );
- return shaderMaterial;
- }
- function _getCommonVertexShader() {
- return /* glsl */`
- precision mediump float;
- precision mediump int;
- attribute vec3 position;
- attribute vec2 uv;
- attribute float faceIndex;
- varying vec3 vOutputDirection;
- // RH coordinate system; PMREM face-indexing convention
- vec3 getDirection( vec2 uv, float face ) {
- uv = 2.0 * uv - 1.0;
- vec3 direction = vec3( uv, 1.0 );
- if ( face == 0.0 ) {
- direction = direction.zyx; // ( 1, v, u ) pos x
- } else if ( face == 1.0 ) {
- direction = direction.xzy;
- direction.xz *= -1.0; // ( -u, 1, -v ) pos y
- } else if ( face == 2.0 ) {
- direction.x *= -1.0; // ( -u, v, 1 ) pos z
- } else if ( face == 3.0 ) {
- direction = direction.zyx;
- direction.xz *= -1.0; // ( -1, v, -u ) neg x
- } else if ( face == 4.0 ) {
- direction = direction.xzy;
- direction.xy *= -1.0; // ( -u, -1, v ) neg y
- } else if ( face == 5.0 ) {
- direction.z *= -1.0; // ( u, v, -1 ) neg z
- }
- return direction;
- }
- void main() {
- vOutputDirection = getDirection( uv, faceIndex );
- gl_Position = vec4( position, 1.0 );
- }
- `;
- }
- function _getEncodings() {
- return /* glsl */`
- uniform int inputEncoding;
- uniform int outputEncoding;
- #include <encodings_pars_fragment>
- vec4 inputTexelToLinear( vec4 value ) {
- if ( inputEncoding == 0 ) {
- return value;
- } else if ( inputEncoding == 1 ) {
- return sRGBToLinear( value );
- } else if ( inputEncoding == 2 ) {
- return RGBEToLinear( value );
- } else if ( inputEncoding == 3 ) {
- return RGBMToLinear( value, 7.0 );
- } else if ( inputEncoding == 4 ) {
- return RGBMToLinear( value, 16.0 );
- } else if ( inputEncoding == 5 ) {
- return RGBDToLinear( value, 256.0 );
- } else {
- return GammaToLinear( value, 2.2 );
- }
- }
- vec4 linearToOutputTexel( vec4 value ) {
- if ( outputEncoding == 0 ) {
- return value;
- } else if ( outputEncoding == 1 ) {
- return LinearTosRGB( value );
- } else if ( outputEncoding == 2 ) {
- return LinearToRGBE( value );
- } else if ( outputEncoding == 3 ) {
- return LinearToRGBM( value, 7.0 );
- } else if ( outputEncoding == 4 ) {
- return LinearToRGBM( value, 16.0 );
- } else if ( outputEncoding == 5 ) {
- return LinearToRGBD( value, 256.0 );
- } else {
- return LinearToGamma( value, 2.2 );
- }
- }
- vec4 envMapTexelToLinear( vec4 color ) {
- return inputTexelToLinear( color );
- }
- `;
- }
- function Face4( a, b, c, d, normal, color, materialIndex ) {
- console.warn( 'THREE.Face4 has been removed. A THREE.Face3 will be created instead.' );
- return new Face3( a, b, c, normal, color, materialIndex );
- }
- const LineStrip = 0;
- const LinePieces = 1;
- const NoColors = 0;
- const FaceColors = 1;
- const VertexColors = 2;
- function MeshFaceMaterial( materials ) {
- console.warn( 'THREE.MeshFaceMaterial has been removed. Use an Array instead.' );
- return materials;
- }
- function MultiMaterial( materials = [] ) {
- console.warn( 'THREE.MultiMaterial has been removed. Use an Array instead.' );
- materials.isMultiMaterial = true;
- materials.materials = materials;
- materials.clone = function () {
- return materials.slice();
- };
- return materials;
- }
- function PointCloud( geometry, material ) {
- console.warn( 'THREE.PointCloud has been renamed to THREE.Points.' );
- return new Points( geometry, material );
- }
- function Particle( material ) {
- console.warn( 'THREE.Particle has been renamed to THREE.Sprite.' );
- return new Sprite( material );
- }
- function ParticleSystem( geometry, material ) {
- console.warn( 'THREE.ParticleSystem has been renamed to THREE.Points.' );
- return new Points( geometry, material );
- }
- function PointCloudMaterial( parameters ) {
- console.warn( 'THREE.PointCloudMaterial has been renamed to THREE.PointsMaterial.' );
- return new PointsMaterial( parameters );
- }
- function ParticleBasicMaterial( parameters ) {
- console.warn( 'THREE.ParticleBasicMaterial has been renamed to THREE.PointsMaterial.' );
- return new PointsMaterial( parameters );
- }
- function ParticleSystemMaterial( parameters ) {
- console.warn( 'THREE.ParticleSystemMaterial has been renamed to THREE.PointsMaterial.' );
- return new PointsMaterial( parameters );
- }
- function Vertex( x, y, z ) {
- console.warn( 'THREE.Vertex has been removed. Use THREE.Vector3 instead.' );
- return new Vector3( x, y, z );
- }
- //
- function DynamicBufferAttribute( array, itemSize ) {
- console.warn( 'THREE.DynamicBufferAttribute has been removed. Use new THREE.BufferAttribute().setUsage( THREE.DynamicDrawUsage ) instead.' );
- return new BufferAttribute( array, itemSize ).setUsage( DynamicDrawUsage );
- }
- function Int8Attribute( array, itemSize ) {
- console.warn( 'THREE.Int8Attribute has been removed. Use new THREE.Int8BufferAttribute() instead.' );
- return new Int8BufferAttribute( array, itemSize );
- }
- function Uint8Attribute( array, itemSize ) {
- console.warn( 'THREE.Uint8Attribute has been removed. Use new THREE.Uint8BufferAttribute() instead.' );
- return new Uint8BufferAttribute( array, itemSize );
- }
- function Uint8ClampedAttribute( array, itemSize ) {
- console.warn( 'THREE.Uint8ClampedAttribute has been removed. Use new THREE.Uint8ClampedBufferAttribute() instead.' );
- return new Uint8ClampedBufferAttribute( array, itemSize );
- }
- function Int16Attribute( array, itemSize ) {
- console.warn( 'THREE.Int16Attribute has been removed. Use new THREE.Int16BufferAttribute() instead.' );
- return new Int16BufferAttribute( array, itemSize );
- }
- function Uint16Attribute( array, itemSize ) {
- console.warn( 'THREE.Uint16Attribute has been removed. Use new THREE.Uint16BufferAttribute() instead.' );
- return new Uint16BufferAttribute( array, itemSize );
- }
- function Int32Attribute( array, itemSize ) {
- console.warn( 'THREE.Int32Attribute has been removed. Use new THREE.Int32BufferAttribute() instead.' );
- return new Int32BufferAttribute( array, itemSize );
- }
- function Uint32Attribute( array, itemSize ) {
- console.warn( 'THREE.Uint32Attribute has been removed. Use new THREE.Uint32BufferAttribute() instead.' );
- return new Uint32BufferAttribute( array, itemSize );
- }
- function Float32Attribute( array, itemSize ) {
- console.warn( 'THREE.Float32Attribute has been removed. Use new THREE.Float32BufferAttribute() instead.' );
- return new Float32BufferAttribute( array, itemSize );
- }
- function Float64Attribute( array, itemSize ) {
- console.warn( 'THREE.Float64Attribute has been removed. Use new THREE.Float64BufferAttribute() instead.' );
- return new Float64BufferAttribute( array, itemSize );
- }
- //
- Curve.create = function ( construct, getPoint ) {
- console.log( 'THREE.Curve.create() has been deprecated' );
- construct.prototype = Object.create( Curve.prototype );
- construct.prototype.constructor = construct;
- construct.prototype.getPoint = getPoint;
- return construct;
- };
- //
- Object.assign( CurvePath.prototype, {
- createPointsGeometry: function ( divisions ) {
- console.warn( 'THREE.CurvePath: .createPointsGeometry() has been removed. Use new THREE.Geometry().setFromPoints( points ) instead.' );
- // generate geometry from path points (for Line or Points objects)
- const pts = this.getPoints( divisions );
- return this.createGeometry( pts );
- },
- createSpacedPointsGeometry: function ( divisions ) {
- console.warn( 'THREE.CurvePath: .createSpacedPointsGeometry() has been removed. Use new THREE.Geometry().setFromPoints( points ) instead.' );
- // generate geometry from equidistant sampling along the path
- const pts = this.getSpacedPoints( divisions );
- return this.createGeometry( pts );
- },
- createGeometry: function ( points ) {
- console.warn( 'THREE.CurvePath: .createGeometry() has been removed. Use new THREE.Geometry().setFromPoints( points ) instead.' );
- const geometry = new Geometry();
- for ( let i = 0, l = points.length; i < l; i ++ ) {
- const point = points[ i ];
- geometry.vertices.push( new Vector3( point.x, point.y, point.z || 0 ) );
- }
- return geometry;
- }
- } );
- //
- Object.assign( Path.prototype, {
- fromPoints: function ( points ) {
- console.warn( 'THREE.Path: .fromPoints() has been renamed to .setFromPoints().' );
- return this.setFromPoints( points );
- }
- } );
- //
- function ClosedSplineCurve3( points ) {
- console.warn( 'THREE.ClosedSplineCurve3 has been deprecated. Use THREE.CatmullRomCurve3 instead.' );
- CatmullRomCurve3.call( this, points );
- this.type = 'catmullrom';
- this.closed = true;
- }
- ClosedSplineCurve3.prototype = Object.create( CatmullRomCurve3.prototype );
- //
- function SplineCurve3( points ) {
- console.warn( 'THREE.SplineCurve3 has been deprecated. Use THREE.CatmullRomCurve3 instead.' );
- CatmullRomCurve3.call( this, points );
- this.type = 'catmullrom';
- }
- SplineCurve3.prototype = Object.create( CatmullRomCurve3.prototype );
- //
- function Spline( points ) {
- console.warn( 'THREE.Spline has been removed. Use THREE.CatmullRomCurve3 instead.' );
- CatmullRomCurve3.call( this, points );
- this.type = 'catmullrom';
- }
- Spline.prototype = Object.create( CatmullRomCurve3.prototype );
- Object.assign( Spline.prototype, {
- initFromArray: function ( /* a */ ) {
- console.error( 'THREE.Spline: .initFromArray() has been removed.' );
- },
- getControlPointsArray: function ( /* optionalTarget */ ) {
- console.error( 'THREE.Spline: .getControlPointsArray() has been removed.' );
- },
- reparametrizeByArcLength: function ( /* samplingCoef */ ) {
- console.error( 'THREE.Spline: .reparametrizeByArcLength() has been removed.' );
- }
- } );
- //
- function AxisHelper( size ) {
- console.warn( 'THREE.AxisHelper has been renamed to THREE.AxesHelper.' );
- return new AxesHelper( size );
- }
- function BoundingBoxHelper( object, color ) {
- console.warn( 'THREE.BoundingBoxHelper has been deprecated. Creating a THREE.BoxHelper instead.' );
- return new BoxHelper( object, color );
- }
- function EdgesHelper( object, hex ) {
- console.warn( 'THREE.EdgesHelper has been removed. Use THREE.EdgesGeometry instead.' );
- return new LineSegments( new EdgesGeometry( object.geometry ), new LineBasicMaterial( { color: hex !== undefined ? hex : 0xffffff } ) );
- }
- GridHelper.prototype.setColors = function () {
- console.error( 'THREE.GridHelper: setColors() has been deprecated, pass them in the constructor instead.' );
- };
- SkeletonHelper.prototype.update = function () {
- console.error( 'THREE.SkeletonHelper: update() no longer needs to be called.' );
- };
- function WireframeHelper( object, hex ) {
- console.warn( 'THREE.WireframeHelper has been removed. Use THREE.WireframeGeometry instead.' );
- return new LineSegments( new WireframeGeometry( object.geometry ), new LineBasicMaterial( { color: hex !== undefined ? hex : 0xffffff } ) );
- }
- //
- Object.assign( Loader.prototype, {
- extractUrlBase: function ( url ) {
- console.warn( 'THREE.Loader: .extractUrlBase() has been deprecated. Use THREE.LoaderUtils.extractUrlBase() instead.' );
- return LoaderUtils.extractUrlBase( url );
- }
- } );
- Loader.Handlers = {
- add: function ( /* regex, loader */ ) {
- console.error( 'THREE.Loader: Handlers.add() has been removed. Use LoadingManager.addHandler() instead.' );
- },
- get: function ( /* file */ ) {
- console.error( 'THREE.Loader: Handlers.get() has been removed. Use LoadingManager.getHandler() instead.' );
- }
- };
- function XHRLoader( manager ) {
- console.warn( 'THREE.XHRLoader has been renamed to THREE.FileLoader.' );
- return new FileLoader( manager );
- }
- function BinaryTextureLoader( manager ) {
- console.warn( 'THREE.BinaryTextureLoader has been renamed to THREE.DataTextureLoader.' );
- return new DataTextureLoader( manager );
- }
- //
- Object.assign( Box2.prototype, {
- center: function ( optionalTarget ) {
- console.warn( 'THREE.Box2: .center() has been renamed to .getCenter().' );
- return this.getCenter( optionalTarget );
- },
- empty: function () {
- console.warn( 'THREE.Box2: .empty() has been renamed to .isEmpty().' );
- return this.isEmpty();
- },
- isIntersectionBox: function ( box ) {
- console.warn( 'THREE.Box2: .isIntersectionBox() has been renamed to .intersectsBox().' );
- return this.intersectsBox( box );
- },
- size: function ( optionalTarget ) {
- console.warn( 'THREE.Box2: .size() has been renamed to .getSize().' );
- return this.getSize( optionalTarget );
- }
- } );
- Object.assign( Box3.prototype, {
- center: function ( optionalTarget ) {
- console.warn( 'THREE.Box3: .center() has been renamed to .getCenter().' );
- return this.getCenter( optionalTarget );
- },
- empty: function () {
- console.warn( 'THREE.Box3: .empty() has been renamed to .isEmpty().' );
- return this.isEmpty();
- },
- isIntersectionBox: function ( box ) {
- console.warn( 'THREE.Box3: .isIntersectionBox() has been renamed to .intersectsBox().' );
- return this.intersectsBox( box );
- },
- isIntersectionSphere: function ( sphere ) {
- console.warn( 'THREE.Box3: .isIntersectionSphere() has been renamed to .intersectsSphere().' );
- return this.intersectsSphere( sphere );
- },
- size: function ( optionalTarget ) {
- console.warn( 'THREE.Box3: .size() has been renamed to .getSize().' );
- return this.getSize( optionalTarget );
- }
- } );
- Object.assign( Sphere.prototype, {
- empty: function () {
- console.warn( 'THREE.Sphere: .empty() has been renamed to .isEmpty().' );
- return this.isEmpty();
- },
- } );
- Frustum.prototype.setFromMatrix = function ( m ) {
- console.warn( 'THREE.Frustum: .setFromMatrix() has been renamed to .setFromProjectionMatrix().' );
- return this.setFromProjectionMatrix( m );
- };
- Line3.prototype.center = function ( optionalTarget ) {
- console.warn( 'THREE.Line3: .center() has been renamed to .getCenter().' );
- return this.getCenter( optionalTarget );
- };
- Object.assign( MathUtils, {
- random16: function () {
- console.warn( 'THREE.Math: .random16() has been deprecated. Use Math.random() instead.' );
- return Math.random();
- },
- nearestPowerOfTwo: function ( value ) {
- console.warn( 'THREE.Math: .nearestPowerOfTwo() has been renamed to .floorPowerOfTwo().' );
- return MathUtils.floorPowerOfTwo( value );
- },
- nextPowerOfTwo: function ( value ) {
- console.warn( 'THREE.Math: .nextPowerOfTwo() has been renamed to .ceilPowerOfTwo().' );
- return MathUtils.ceilPowerOfTwo( value );
- }
- } );
- Object.assign( Matrix3.prototype, {
- flattenToArrayOffset: function ( array, offset ) {
- console.warn( 'THREE.Matrix3: .flattenToArrayOffset() has been deprecated. Use .toArray() instead.' );
- return this.toArray( array, offset );
- },
- multiplyVector3: function ( vector ) {
- console.warn( 'THREE.Matrix3: .multiplyVector3() has been removed. Use vector.applyMatrix3( matrix ) instead.' );
- return vector.applyMatrix3( this );
- },
- multiplyVector3Array: function ( /* a */ ) {
- console.error( 'THREE.Matrix3: .multiplyVector3Array() has been removed.' );
- },
- applyToBufferAttribute: function ( attribute ) {
- console.warn( 'THREE.Matrix3: .applyToBufferAttribute() has been removed. Use attribute.applyMatrix3( matrix ) instead.' );
- return attribute.applyMatrix3( this );
- },
- applyToVector3Array: function ( /* array, offset, length */ ) {
- console.error( 'THREE.Matrix3: .applyToVector3Array() has been removed.' );
- },
- getInverse: function ( matrix ) {
- console.warn( 'THREE.Matrix3: .getInverse() has been removed. Use matrixInv.copy( matrix ).invert(); instead.' );
- return this.copy( matrix ).invert();
- }
- } );
- Object.assign( Matrix4.prototype, {
- extractPosition: function ( m ) {
- console.warn( 'THREE.Matrix4: .extractPosition() has been renamed to .copyPosition().' );
- return this.copyPosition( m );
- },
- flattenToArrayOffset: function ( array, offset ) {
- console.warn( 'THREE.Matrix4: .flattenToArrayOffset() has been deprecated. Use .toArray() instead.' );
- return this.toArray( array, offset );
- },
- getPosition: function () {
- console.warn( 'THREE.Matrix4: .getPosition() has been removed. Use Vector3.setFromMatrixPosition( matrix ) instead.' );
- return new Vector3().setFromMatrixColumn( this, 3 );
- },
- setRotationFromQuaternion: function ( q ) {
- console.warn( 'THREE.Matrix4: .setRotationFromQuaternion() has been renamed to .makeRotationFromQuaternion().' );
- return this.makeRotationFromQuaternion( q );
- },
- multiplyToArray: function () {
- console.warn( 'THREE.Matrix4: .multiplyToArray() has been removed.' );
- },
- multiplyVector3: function ( vector ) {
- console.warn( 'THREE.Matrix4: .multiplyVector3() has been removed. Use vector.applyMatrix4( matrix ) instead.' );
- return vector.applyMatrix4( this );
- },
- multiplyVector4: function ( vector ) {
- console.warn( 'THREE.Matrix4: .multiplyVector4() has been removed. Use vector.applyMatrix4( matrix ) instead.' );
- return vector.applyMatrix4( this );
- },
- multiplyVector3Array: function ( /* a */ ) {
- console.error( 'THREE.Matrix4: .multiplyVector3Array() has been removed.' );
- },
- rotateAxis: function ( v ) {
- console.warn( 'THREE.Matrix4: .rotateAxis() has been removed. Use Vector3.transformDirection( matrix ) instead.' );
- v.transformDirection( this );
- },
- crossVector: function ( vector ) {
- console.warn( 'THREE.Matrix4: .crossVector() has been removed. Use vector.applyMatrix4( matrix ) instead.' );
- return vector.applyMatrix4( this );
- },
- translate: function () {
- console.error( 'THREE.Matrix4: .translate() has been removed.' );
- },
- rotateX: function () {
- console.error( 'THREE.Matrix4: .rotateX() has been removed.' );
- },
- rotateY: function () {
- console.error( 'THREE.Matrix4: .rotateY() has been removed.' );
- },
- rotateZ: function () {
- console.error( 'THREE.Matrix4: .rotateZ() has been removed.' );
- },
- rotateByAxis: function () {
- console.error( 'THREE.Matrix4: .rotateByAxis() has been removed.' );
- },
- applyToBufferAttribute: function ( attribute ) {
- console.warn( 'THREE.Matrix4: .applyToBufferAttribute() has been removed. Use attribute.applyMatrix4( matrix ) instead.' );
- return attribute.applyMatrix4( this );
- },
- applyToVector3Array: function ( /* array, offset, length */ ) {
- console.error( 'THREE.Matrix4: .applyToVector3Array() has been removed.' );
- },
- makeFrustum: function ( left, right, bottom, top, near, far ) {
- console.warn( 'THREE.Matrix4: .makeFrustum() has been removed. Use .makePerspective( left, right, top, bottom, near, far ) instead.' );
- return this.makePerspective( left, right, top, bottom, near, far );
- },
- getInverse: function ( matrix ) {
- console.warn( 'THREE.Matrix4: .getInverse() has been removed. Use matrixInv.copy( matrix ).invert(); instead.' );
- return this.copy( matrix ).invert();
- }
- } );
- Plane.prototype.isIntersectionLine = function ( line ) {
- console.warn( 'THREE.Plane: .isIntersectionLine() has been renamed to .intersectsLine().' );
- return this.intersectsLine( line );
- };
- Object.assign( Quaternion.prototype, {
- multiplyVector3: function ( vector ) {
- console.warn( 'THREE.Quaternion: .multiplyVector3() has been removed. Use is now vector.applyQuaternion( quaternion ) instead.' );
- return vector.applyQuaternion( this );
- },
- inverse: function ( ) {
- console.warn( 'THREE.Quaternion: .inverse() has been renamed to invert().' );
- return this.invert();
- }
- } );
- Object.assign( Ray.prototype, {
- isIntersectionBox: function ( box ) {
- console.warn( 'THREE.Ray: .isIntersectionBox() has been renamed to .intersectsBox().' );
- return this.intersectsBox( box );
- },
- isIntersectionPlane: function ( plane ) {
- console.warn( 'THREE.Ray: .isIntersectionPlane() has been renamed to .intersectsPlane().' );
- return this.intersectsPlane( plane );
- },
- isIntersectionSphere: function ( sphere ) {
- console.warn( 'THREE.Ray: .isIntersectionSphere() has been renamed to .intersectsSphere().' );
- return this.intersectsSphere( sphere );
- }
- } );
- Object.assign( Triangle.prototype, {
- area: function () {
- console.warn( 'THREE.Triangle: .area() has been renamed to .getArea().' );
- return this.getArea();
- },
- barycoordFromPoint: function ( point, target ) {
- console.warn( 'THREE.Triangle: .barycoordFromPoint() has been renamed to .getBarycoord().' );
- return this.getBarycoord( point, target );
- },
- midpoint: function ( target ) {
- console.warn( 'THREE.Triangle: .midpoint() has been renamed to .getMidpoint().' );
- return this.getMidpoint( target );
- },
- normal: function ( target ) {
- console.warn( 'THREE.Triangle: .normal() has been renamed to .getNormal().' );
- return this.getNormal( target );
- },
- plane: function ( target ) {
- console.warn( 'THREE.Triangle: .plane() has been renamed to .getPlane().' );
- return this.getPlane( target );
- }
- } );
- Object.assign( Triangle, {
- barycoordFromPoint: function ( point, a, b, c, target ) {
- console.warn( 'THREE.Triangle: .barycoordFromPoint() has been renamed to .getBarycoord().' );
- return Triangle.getBarycoord( point, a, b, c, target );
- },
- normal: function ( a, b, c, target ) {
- console.warn( 'THREE.Triangle: .normal() has been renamed to .getNormal().' );
- return Triangle.getNormal( a, b, c, target );
- }
- } );
- Object.assign( Shape.prototype, {
- extractAllPoints: function ( divisions ) {
- console.warn( 'THREE.Shape: .extractAllPoints() has been removed. Use .extractPoints() instead.' );
- return this.extractPoints( divisions );
- },
- extrude: function ( options ) {
- console.warn( 'THREE.Shape: .extrude() has been removed. Use ExtrudeGeometry() instead.' );
- return new ExtrudeGeometry( this, options );
- },
- makeGeometry: function ( options ) {
- console.warn( 'THREE.Shape: .makeGeometry() has been removed. Use ShapeGeometry() instead.' );
- return new ShapeGeometry( this, options );
- }
- } );
- Object.assign( Vector2$1.prototype, {
- fromAttribute: function ( attribute, index, offset ) {
- console.warn( 'THREE.Vector2: .fromAttribute() has been renamed to .fromBufferAttribute().' );
- return this.fromBufferAttribute( attribute, index, offset );
- },
- distanceToManhattan: function ( v ) {
- console.warn( 'THREE.Vector2: .distanceToManhattan() has been renamed to .manhattanDistanceTo().' );
- return this.manhattanDistanceTo( v );
- },
- lengthManhattan: function () {
- console.warn( 'THREE.Vector2: .lengthManhattan() has been renamed to .manhattanLength().' );
- return this.manhattanLength();
- }
- } );
- Object.assign( Vector3.prototype, {
- setEulerFromRotationMatrix: function () {
- console.error( 'THREE.Vector3: .setEulerFromRotationMatrix() has been removed. Use Euler.setFromRotationMatrix() instead.' );
- },
- setEulerFromQuaternion: function () {
- console.error( 'THREE.Vector3: .setEulerFromQuaternion() has been removed. Use Euler.setFromQuaternion() instead.' );
- },
- getPositionFromMatrix: function ( m ) {
- console.warn( 'THREE.Vector3: .getPositionFromMatrix() has been renamed to .setFromMatrixPosition().' );
- return this.setFromMatrixPosition( m );
- },
- getScaleFromMatrix: function ( m ) {
- console.warn( 'THREE.Vector3: .getScaleFromMatrix() has been renamed to .setFromMatrixScale().' );
- return this.setFromMatrixScale( m );
- },
- getColumnFromMatrix: function ( index, matrix ) {
- console.warn( 'THREE.Vector3: .getColumnFromMatrix() has been renamed to .setFromMatrixColumn().' );
- return this.setFromMatrixColumn( matrix, index );
- },
- applyProjection: function ( m ) {
- console.warn( 'THREE.Vector3: .applyProjection() has been removed. Use .applyMatrix4( m ) instead.' );
- return this.applyMatrix4( m );
- },
- fromAttribute: function ( attribute, index, offset ) {
- console.warn( 'THREE.Vector3: .fromAttribute() has been renamed to .fromBufferAttribute().' );
- return this.fromBufferAttribute( attribute, index, offset );
- },
- distanceToManhattan: function ( v ) {
- console.warn( 'THREE.Vector3: .distanceToManhattan() has been renamed to .manhattanDistanceTo().' );
- return this.manhattanDistanceTo( v );
- },
- lengthManhattan: function () {
- console.warn( 'THREE.Vector3: .lengthManhattan() has been renamed to .manhattanLength().' );
- return this.manhattanLength();
- }
- } );
- Object.assign( Vector4.prototype, {
- fromAttribute: function ( attribute, index, offset ) {
- console.warn( 'THREE.Vector4: .fromAttribute() has been renamed to .fromBufferAttribute().' );
- return this.fromBufferAttribute( attribute, index, offset );
- },
- lengthManhattan: function () {
- console.warn( 'THREE.Vector4: .lengthManhattan() has been renamed to .manhattanLength().' );
- return this.manhattanLength();
- }
- } );
- //
- Object.assign( Geometry.prototype, {
- computeTangents: function () {
- console.error( 'THREE.Geometry: .computeTangents() has been removed.' );
- },
- computeLineDistances: function () {
- console.error( 'THREE.Geometry: .computeLineDistances() has been removed. Use THREE.Line.computeLineDistances() instead.' );
- },
- applyMatrix: function ( matrix ) {
- console.warn( 'THREE.Geometry: .applyMatrix() has been renamed to .applyMatrix4().' );
- return this.applyMatrix4( matrix );
- }
- } );
- Object.assign( Object3D.prototype, {
- getChildByName: function ( name ) {
- console.warn( 'THREE.Object3D: .getChildByName() has been renamed to .getObjectByName().' );
- return this.getObjectByName( name );
- },
- renderDepth: function () {
- console.warn( 'THREE.Object3D: .renderDepth has been removed. Use .renderOrder, instead.' );
- },
- translate: function ( distance, axis ) {
- console.warn( 'THREE.Object3D: .translate() has been removed. Use .translateOnAxis( axis, distance ) instead.' );
- return this.translateOnAxis( axis, distance );
- },
- getWorldRotation: function () {
- console.error( 'THREE.Object3D: .getWorldRotation() has been removed. Use THREE.Object3D.getWorldQuaternion( target ) instead.' );
- },
- applyMatrix: function ( matrix ) {
- console.warn( 'THREE.Object3D: .applyMatrix() has been renamed to .applyMatrix4().' );
- return this.applyMatrix4( matrix );
- }
- } );
- Object.defineProperties( Object3D.prototype, {
- eulerOrder: {
- get: function () {
- console.warn( 'THREE.Object3D: .eulerOrder is now .rotation.order.' );
- return this.rotation.order;
- },
- set: function ( value ) {
- console.warn( 'THREE.Object3D: .eulerOrder is now .rotation.order.' );
- this.rotation.order = value;
- }
- },
- useQuaternion: {
- get: function () {
- console.warn( 'THREE.Object3D: .useQuaternion has been removed. The library now uses quaternions by default.' );
- },
- set: function () {
- console.warn( 'THREE.Object3D: .useQuaternion has been removed. The library now uses quaternions by default.' );
- }
- }
- } );
- Object.assign( Mesh.prototype, {
- setDrawMode: function () {
- console.error( 'THREE.Mesh: .setDrawMode() has been removed. The renderer now always assumes THREE.TrianglesDrawMode. Transform your geometry via BufferGeometryUtils.toTrianglesDrawMode() if necessary.' );
- },
- } );
- Object.defineProperties( Mesh.prototype, {
- drawMode: {
- get: function () {
- console.error( 'THREE.Mesh: .drawMode has been removed. The renderer now always assumes THREE.TrianglesDrawMode.' );
- return TrianglesDrawMode;
- },
- set: function () {
- console.error( 'THREE.Mesh: .drawMode has been removed. The renderer now always assumes THREE.TrianglesDrawMode. Transform your geometry via BufferGeometryUtils.toTrianglesDrawMode() if necessary.' );
- }
- }
- } );
- Object.defineProperties( LOD.prototype, {
- objects: {
- get: function () {
- console.warn( 'THREE.LOD: .objects has been renamed to .levels.' );
- return this.levels;
- }
- }
- } );
- Object.defineProperty( Skeleton.prototype, 'useVertexTexture', {
- get: function () {
- console.warn( 'THREE.Skeleton: useVertexTexture has been removed.' );
- },
- set: function () {
- console.warn( 'THREE.Skeleton: useVertexTexture has been removed.' );
- }
- } );
- SkinnedMesh.prototype.initBones = function () {
- console.error( 'THREE.SkinnedMesh: initBones() has been removed.' );
- };
- Object.defineProperty( Curve.prototype, '__arcLengthDivisions', {
- get: function () {
- console.warn( 'THREE.Curve: .__arcLengthDivisions is now .arcLengthDivisions.' );
- return this.arcLengthDivisions;
- },
- set: function ( value ) {
- console.warn( 'THREE.Curve: .__arcLengthDivisions is now .arcLengthDivisions.' );
- this.arcLengthDivisions = value;
- }
- } );
- //
- PerspectiveCamera.prototype.setLens = function ( focalLength, filmGauge ) {
- console.warn( 'THREE.PerspectiveCamera.setLens is deprecated. ' +
- 'Use .setFocalLength and .filmGauge for a photographic setup.' );
- if ( filmGauge !== undefined ) this.filmGauge = filmGauge;
- this.setFocalLength( focalLength );
- };
- //
- Object.defineProperties( Light.prototype, {
- onlyShadow: {
- set: function () {
- console.warn( 'THREE.Light: .onlyShadow has been removed.' );
- }
- },
- shadowCameraFov: {
- set: function ( value ) {
- console.warn( 'THREE.Light: .shadowCameraFov is now .shadow.camera.fov.' );
- this.shadow.camera.fov = value;
- }
- },
- shadowCameraLeft: {
- set: function ( value ) {
- console.warn( 'THREE.Light: .shadowCameraLeft is now .shadow.camera.left.' );
- this.shadow.camera.left = value;
- }
- },
- shadowCameraRight: {
- set: function ( value ) {
- console.warn( 'THREE.Light: .shadowCameraRight is now .shadow.camera.right.' );
- this.shadow.camera.right = value;
- }
- },
- shadowCameraTop: {
- set: function ( value ) {
- console.warn( 'THREE.Light: .shadowCameraTop is now .shadow.camera.top.' );
- this.shadow.camera.top = value;
- }
- },
- shadowCameraBottom: {
- set: function ( value ) {
- console.warn( 'THREE.Light: .shadowCameraBottom is now .shadow.camera.bottom.' );
- this.shadow.camera.bottom = value;
- }
- },
- shadowCameraNear: {
- set: function ( value ) {
- console.warn( 'THREE.Light: .shadowCameraNear is now .shadow.camera.near.' );
- this.shadow.camera.near = value;
- }
- },
- shadowCameraFar: {
- set: function ( value ) {
- console.warn( 'THREE.Light: .shadowCameraFar is now .shadow.camera.far.' );
- this.shadow.camera.far = value;
- }
- },
- shadowCameraVisible: {
- set: function () {
- console.warn( 'THREE.Light: .shadowCameraVisible has been removed. Use new THREE.CameraHelper( light.shadow.camera ) instead.' );
- }
- },
- shadowBias: {
- set: function ( value ) {
- console.warn( 'THREE.Light: .shadowBias is now .shadow.bias.' );
- this.shadow.bias = value;
- }
- },
- shadowDarkness: {
- set: function () {
- console.warn( 'THREE.Light: .shadowDarkness has been removed.' );
- }
- },
- shadowMapWidth: {
- set: function ( value ) {
- console.warn( 'THREE.Light: .shadowMapWidth is now .shadow.mapSize.width.' );
- this.shadow.mapSize.width = value;
- }
- },
- shadowMapHeight: {
- set: function ( value ) {
- console.warn( 'THREE.Light: .shadowMapHeight is now .shadow.mapSize.height.' );
- this.shadow.mapSize.height = value;
- }
- }
- } );
- //
- Object.defineProperties( BufferAttribute.prototype, {
- length: {
- get: function () {
- console.warn( 'THREE.BufferAttribute: .length has been deprecated. Use .count instead.' );
- return this.array.length;
- }
- },
- dynamic: {
- get: function () {
- console.warn( 'THREE.BufferAttribute: .dynamic has been deprecated. Use .usage instead.' );
- return this.usage === DynamicDrawUsage;
- },
- set: function ( /* value */ ) {
- console.warn( 'THREE.BufferAttribute: .dynamic has been deprecated. Use .usage instead.' );
- this.setUsage( DynamicDrawUsage );
- }
- }
- } );
- Object.assign( BufferAttribute.prototype, {
- setDynamic: function ( value ) {
- //console.warn( 'THREE.BufferAttribute: .setDynamic() has been deprecated. Use .setUsage() instead.' );
- this.setUsage( value === true ? DynamicDrawUsage : StaticDrawUsage );
- return this;
- },
- copyIndicesArray: function ( /* indices */ ) {
- console.error( 'THREE.BufferAttribute: .copyIndicesArray() has been removed.' );
- },
- setArray: function ( /* array */ ) {
- console.error( 'THREE.BufferAttribute: .setArray has been removed. Use BufferGeometry .setAttribute to replace/resize attribute buffers' );
- }
- } );
- Object.assign( BufferGeometry.prototype, {
- addIndex: function ( index ) {
- console.warn( 'THREE.BufferGeometry: .addIndex() has been renamed to .setIndex().' );
- this.setIndex( index );
- },
- addAttribute: function ( name, attribute ) {
- console.warn( 'THREE.BufferGeometry: .addAttribute() has been renamed to .setAttribute().' );
- if ( ! ( attribute && attribute.isBufferAttribute ) && ! ( attribute && attribute.isInterleavedBufferAttribute ) ) {
- console.warn( 'THREE.BufferGeometry: .addAttribute() now expects ( name, attribute ).' );
- return this.setAttribute( name, new BufferAttribute( arguments[ 1 ], arguments[ 2 ] ) );
- }
- if ( name === 'index' ) {
- console.warn( 'THREE.BufferGeometry.addAttribute: Use .setIndex() for index attribute.' );
- this.setIndex( attribute );
- return this;
- }
- return this.setAttribute( name, attribute );
- },
- addDrawCall: function ( start, count, indexOffset ) {
- if ( indexOffset !== undefined ) {
- console.warn( 'THREE.BufferGeometry: .addDrawCall() no longer supports indexOffset.' );
- }
- console.warn( 'THREE.BufferGeometry: .addDrawCall() is now .addGroup().' );
- this.addGroup( start, count );
- },
- clearDrawCalls: function () {
- console.warn( 'THREE.BufferGeometry: .clearDrawCalls() is now .clearGroups().' );
- this.clearGroups();
- },
- computeTangents: function () {
- console.warn( 'THREE.BufferGeometry: .computeTangents() has been removed.' );
- },
- computeOffsets: function () {
- console.warn( 'THREE.BufferGeometry: .computeOffsets() has been removed.' );
- },
- removeAttribute: function ( name ) {
- console.warn( 'THREE.BufferGeometry: .removeAttribute() has been renamed to .deleteAttribute().' );
- return this.deleteAttribute( name );
- },
- applyMatrix: function ( matrix ) {
- console.warn( 'THREE.BufferGeometry: .applyMatrix() has been renamed to .applyMatrix4().' );
- return this.applyMatrix4( matrix );
- }
- } );
- Object.defineProperties( BufferGeometry.prototype, {
- drawcalls: {
- get: function () {
- console.error( 'THREE.BufferGeometry: .drawcalls has been renamed to .groups.' );
- return this.groups;
- }
- },
- offsets: {
- get: function () {
- console.warn( 'THREE.BufferGeometry: .offsets has been renamed to .groups.' );
- return this.groups;
- }
- }
- } );
- Object.defineProperties( InstancedBufferGeometry.prototype, {
- maxInstancedCount: {
- get: function () {
- console.warn( 'THREE.InstancedBufferGeometry: .maxInstancedCount has been renamed to .instanceCount.' );
- return this.instanceCount;
- },
- set: function ( value ) {
- console.warn( 'THREE.InstancedBufferGeometry: .maxInstancedCount has been renamed to .instanceCount.' );
- this.instanceCount = value;
- }
- }
- } );
- Object.defineProperties( Raycaster.prototype, {
- linePrecision: {
- get: function () {
- console.warn( 'THREE.Raycaster: .linePrecision has been deprecated. Use .params.Line.threshold instead.' );
- return this.params.Line.threshold;
- },
- set: function ( value ) {
- console.warn( 'THREE.Raycaster: .linePrecision has been deprecated. Use .params.Line.threshold instead.' );
- this.params.Line.threshold = value;
- }
- }
- } );
- Object.defineProperties( InterleavedBuffer.prototype, {
- dynamic: {
- get: function () {
- console.warn( 'THREE.InterleavedBuffer: .length has been deprecated. Use .usage instead.' );
- return this.usage === DynamicDrawUsage;
- },
- set: function ( value ) {
- console.warn( 'THREE.InterleavedBuffer: .length has been deprecated. Use .usage instead.' );
- this.setUsage( value );
- }
- }
- } );
- Object.assign( InterleavedBuffer.prototype, {
- setDynamic: function ( value ) {
- console.warn( 'THREE.InterleavedBuffer: .setDynamic() has been deprecated. Use .setUsage() instead.' );
- this.setUsage( value === true ? DynamicDrawUsage : StaticDrawUsage );
- return this;
- },
- setArray: function ( /* array */ ) {
- console.error( 'THREE.InterleavedBuffer: .setArray has been removed. Use BufferGeometry .setAttribute to replace/resize attribute buffers' );
- }
- } );
- //
- Object.assign( ExtrudeBufferGeometry.prototype, {
- getArrays: function () {
- console.error( 'THREE.ExtrudeBufferGeometry: .getArrays() has been removed.' );
- },
- addShapeList: function () {
- console.error( 'THREE.ExtrudeBufferGeometry: .addShapeList() has been removed.' );
- },
- addShape: function () {
- console.error( 'THREE.ExtrudeBufferGeometry: .addShape() has been removed.' );
- }
- } );
- //
- Object.assign( Scene.prototype, {
- dispose: function () {
- console.error( 'THREE.Scene: .dispose() has been removed.' );
- }
- } );
- //
- Object.defineProperties( Uniform.prototype, {
- dynamic: {
- set: function () {
- console.warn( 'THREE.Uniform: .dynamic has been removed. Use object.onBeforeRender() instead.' );
- }
- },
- onUpdate: {
- value: function () {
- console.warn( 'THREE.Uniform: .onUpdate() has been removed. Use object.onBeforeRender() instead.' );
- return this;
- }
- }
- } );
- //
- Object.defineProperties( Material.prototype, {
- wrapAround: {
- get: function () {
- console.warn( 'THREE.Material: .wrapAround has been removed.' );
- },
- set: function () {
- console.warn( 'THREE.Material: .wrapAround has been removed.' );
- }
- },
- overdraw: {
- get: function () {
- console.warn( 'THREE.Material: .overdraw has been removed.' );
- },
- set: function () {
- console.warn( 'THREE.Material: .overdraw has been removed.' );
- }
- },
- wrapRGB: {
- get: function () {
- console.warn( 'THREE.Material: .wrapRGB has been removed.' );
- return new Color();
- }
- },
- shading: {
- get: function () {
- console.error( 'THREE.' + this.type + ': .shading has been removed. Use the boolean .flatShading instead.' );
- },
- set: function ( value ) {
- console.warn( 'THREE.' + this.type + ': .shading has been removed. Use the boolean .flatShading instead.' );
- this.flatShading = ( value === FlatShading );
- }
- },
- stencilMask: {
- get: function () {
- console.warn( 'THREE.' + this.type + ': .stencilMask has been removed. Use .stencilFuncMask instead.' );
- return this.stencilFuncMask;
- },
- set: function ( value ) {
- console.warn( 'THREE.' + this.type + ': .stencilMask has been removed. Use .stencilFuncMask instead.' );
- this.stencilFuncMask = value;
- }
- }
- } );
- Object.defineProperties( MeshPhongMaterial.prototype, {
- metal: {
- get: function () {
- console.warn( 'THREE.MeshPhongMaterial: .metal has been removed. Use THREE.MeshStandardMaterial instead.' );
- return false;
- },
- set: function () {
- console.warn( 'THREE.MeshPhongMaterial: .metal has been removed. Use THREE.MeshStandardMaterial instead' );
- }
- }
- } );
- Object.defineProperties( MeshPhysicalMaterial.prototype, {
- transparency: {
- get: function () {
- console.warn( 'THREE.MeshPhysicalMaterial: .transparency has been renamed to .transmission.' );
- return this.transmission;
- },
- set: function ( value ) {
- console.warn( 'THREE.MeshPhysicalMaterial: .transparency has been renamed to .transmission.' );
- this.transmission = value;
- }
- }
- } );
- Object.defineProperties( ShaderMaterial.prototype, {
- derivatives: {
- get: function () {
- console.warn( 'THREE.ShaderMaterial: .derivatives has been moved to .extensions.derivatives.' );
- return this.extensions.derivatives;
- },
- set: function ( value ) {
- console.warn( 'THREE. ShaderMaterial: .derivatives has been moved to .extensions.derivatives.' );
- this.extensions.derivatives = value;
- }
- }
- } );
- //
- Object.assign( WebGLRenderer.prototype, {
- clearTarget: function ( renderTarget, color, depth, stencil ) {
- console.warn( 'THREE.WebGLRenderer: .clearTarget() has been deprecated. Use .setRenderTarget() and .clear() instead.' );
- this.setRenderTarget( renderTarget );
- this.clear( color, depth, stencil );
- },
- animate: function ( callback ) {
- console.warn( 'THREE.WebGLRenderer: .animate() is now .setAnimationLoop().' );
- this.setAnimationLoop( callback );
- },
- getCurrentRenderTarget: function () {
- console.warn( 'THREE.WebGLRenderer: .getCurrentRenderTarget() is now .getRenderTarget().' );
- return this.getRenderTarget();
- },
- getMaxAnisotropy: function () {
- console.warn( 'THREE.WebGLRenderer: .getMaxAnisotropy() is now .capabilities.getMaxAnisotropy().' );
- return this.capabilities.getMaxAnisotropy();
- },
- getPrecision: function () {
- console.warn( 'THREE.WebGLRenderer: .getPrecision() is now .capabilities.precision.' );
- return this.capabilities.precision;
- },
- resetGLState: function () {
- console.warn( 'THREE.WebGLRenderer: .resetGLState() is now .state.reset().' );
- return this.state.reset();
- },
- supportsFloatTextures: function () {
- console.warn( 'THREE.WebGLRenderer: .supportsFloatTextures() is now .extensions.get( \'OES_texture_float\' ).' );
- return this.extensions.get( 'OES_texture_float' );
- },
- supportsHalfFloatTextures: function () {
- console.warn( 'THREE.WebGLRenderer: .supportsHalfFloatTextures() is now .extensions.get( \'OES_texture_half_float\' ).' );
- return this.extensions.get( 'OES_texture_half_float' );
- },
- supportsStandardDerivatives: function () {
- console.warn( 'THREE.WebGLRenderer: .supportsStandardDerivatives() is now .extensions.get( \'OES_standard_derivatives\' ).' );
- return this.extensions.get( 'OES_standard_derivatives' );
- },
- supportsCompressedTextureS3TC: function () {
- console.warn( 'THREE.WebGLRenderer: .supportsCompressedTextureS3TC() is now .extensions.get( \'WEBGL_compressed_texture_s3tc\' ).' );
- return this.extensions.get( 'WEBGL_compressed_texture_s3tc' );
- },
- supportsCompressedTexturePVRTC: function () {
- console.warn( 'THREE.WebGLRenderer: .supportsCompressedTexturePVRTC() is now .extensions.get( \'WEBGL_compressed_texture_pvrtc\' ).' );
- return this.extensions.get( 'WEBGL_compressed_texture_pvrtc' );
- },
- supportsBlendMinMax: function () {
- console.warn( 'THREE.WebGLRenderer: .supportsBlendMinMax() is now .extensions.get( \'EXT_blend_minmax\' ).' );
- return this.extensions.get( 'EXT_blend_minmax' );
- },
- supportsVertexTextures: function () {
- console.warn( 'THREE.WebGLRenderer: .supportsVertexTextures() is now .capabilities.vertexTextures.' );
- return this.capabilities.vertexTextures;
- },
- supportsInstancedArrays: function () {
- console.warn( 'THREE.WebGLRenderer: .supportsInstancedArrays() is now .extensions.get( \'ANGLE_instanced_arrays\' ).' );
- return this.extensions.get( 'ANGLE_instanced_arrays' );
- },
- enableScissorTest: function ( boolean ) {
- console.warn( 'THREE.WebGLRenderer: .enableScissorTest() is now .setScissorTest().' );
- this.setScissorTest( boolean );
- },
- initMaterial: function () {
- console.warn( 'THREE.WebGLRenderer: .initMaterial() has been removed.' );
- },
- addPrePlugin: function () {
- console.warn( 'THREE.WebGLRenderer: .addPrePlugin() has been removed.' );
- },
- addPostPlugin: function () {
- console.warn( 'THREE.WebGLRenderer: .addPostPlugin() has been removed.' );
- },
- updateShadowMap: function () {
- console.warn( 'THREE.WebGLRenderer: .updateShadowMap() has been removed.' );
- },
- setFaceCulling: function () {
- console.warn( 'THREE.WebGLRenderer: .setFaceCulling() has been removed.' );
- },
- allocTextureUnit: function () {
- console.warn( 'THREE.WebGLRenderer: .allocTextureUnit() has been removed.' );
- },
- setTexture: function () {
- console.warn( 'THREE.WebGLRenderer: .setTexture() has been removed.' );
- },
- setTexture2D: function () {
- console.warn( 'THREE.WebGLRenderer: .setTexture2D() has been removed.' );
- },
- setTextureCube: function () {
- console.warn( 'THREE.WebGLRenderer: .setTextureCube() has been removed.' );
- },
- getActiveMipMapLevel: function () {
- console.warn( 'THREE.WebGLRenderer: .getActiveMipMapLevel() is now .getActiveMipmapLevel().' );
- return this.getActiveMipmapLevel();
- }
- } );
- Object.defineProperties( WebGLRenderer.prototype, {
- shadowMapEnabled: {
- get: function () {
- return this.shadowMap.enabled;
- },
- set: function ( value ) {
- console.warn( 'THREE.WebGLRenderer: .shadowMapEnabled is now .shadowMap.enabled.' );
- this.shadowMap.enabled = value;
- }
- },
- shadowMapType: {
- get: function () {
- return this.shadowMap.type;
- },
- set: function ( value ) {
- console.warn( 'THREE.WebGLRenderer: .shadowMapType is now .shadowMap.type.' );
- this.shadowMap.type = value;
- }
- },
- shadowMapCullFace: {
- get: function () {
- console.warn( 'THREE.WebGLRenderer: .shadowMapCullFace has been removed. Set Material.shadowSide instead.' );
- return undefined;
- },
- set: function ( /* value */ ) {
- console.warn( 'THREE.WebGLRenderer: .shadowMapCullFace has been removed. Set Material.shadowSide instead.' );
- }
- },
- context: {
- get: function () {
- console.warn( 'THREE.WebGLRenderer: .context has been removed. Use .getContext() instead.' );
- return this.getContext();
- }
- },
- vr: {
- get: function () {
- console.warn( 'THREE.WebGLRenderer: .vr has been renamed to .xr' );
- return this.xr;
- }
- },
- gammaInput: {
- get: function () {
- console.warn( 'THREE.WebGLRenderer: .gammaInput has been removed. Set the encoding for textures via Texture.encoding instead.' );
- return false;
- },
- set: function () {
- console.warn( 'THREE.WebGLRenderer: .gammaInput has been removed. Set the encoding for textures via Texture.encoding instead.' );
- }
- },
- gammaOutput: {
- get: function () {
- console.warn( 'THREE.WebGLRenderer: .gammaOutput has been removed. Set WebGLRenderer.outputEncoding instead.' );
- return false;
- },
- set: function ( value ) {
- console.warn( 'THREE.WebGLRenderer: .gammaOutput has been removed. Set WebGLRenderer.outputEncoding instead.' );
- this.outputEncoding = ( value === true ) ? sRGBEncoding : LinearEncoding;
- }
- },
- toneMappingWhitePoint: {
- get: function () {
- console.warn( 'THREE.WebGLRenderer: .toneMappingWhitePoint has been removed.' );
- return 1.0;
- },
- set: function () {
- console.warn( 'THREE.WebGLRenderer: .toneMappingWhitePoint has been removed.' );
- }
- },
- } );
- Object.defineProperties( WebGLShadowMap.prototype, {
- cullFace: {
- get: function () {
- console.warn( 'THREE.WebGLRenderer: .shadowMap.cullFace has been removed. Set Material.shadowSide instead.' );
- return undefined;
- },
- set: function ( /* cullFace */ ) {
- console.warn( 'THREE.WebGLRenderer: .shadowMap.cullFace has been removed. Set Material.shadowSide instead.' );
- }
- },
- renderReverseSided: {
- get: function () {
- console.warn( 'THREE.WebGLRenderer: .shadowMap.renderReverseSided has been removed. Set Material.shadowSide instead.' );
- return undefined;
- },
- set: function () {
- console.warn( 'THREE.WebGLRenderer: .shadowMap.renderReverseSided has been removed. Set Material.shadowSide instead.' );
- }
- },
- renderSingleSided: {
- get: function () {
- console.warn( 'THREE.WebGLRenderer: .shadowMap.renderSingleSided has been removed. Set Material.shadowSide instead.' );
- return undefined;
- },
- set: function () {
- console.warn( 'THREE.WebGLRenderer: .shadowMap.renderSingleSided has been removed. Set Material.shadowSide instead.' );
- }
- }
- } );
- function WebGLRenderTargetCube( width, height, options ) {
- console.warn( 'THREE.WebGLRenderTargetCube( width, height, options ) is now WebGLCubeRenderTarget( size, options ).' );
- return new WebGLCubeRenderTarget( width, options );
- }
- //
- Object.defineProperties( WebGLRenderTarget.prototype, {
- wrapS: {
- get: function () {
- console.warn( 'THREE.WebGLRenderTarget: .wrapS is now .texture.wrapS.' );
- return this.texture.wrapS;
- },
- set: function ( value ) {
- console.warn( 'THREE.WebGLRenderTarget: .wrapS is now .texture.wrapS.' );
- this.texture.wrapS = value;
- }
- },
- wrapT: {
- get: function () {
- console.warn( 'THREE.WebGLRenderTarget: .wrapT is now .texture.wrapT.' );
- return this.texture.wrapT;
- },
- set: function ( value ) {
- console.warn( 'THREE.WebGLRenderTarget: .wrapT is now .texture.wrapT.' );
- this.texture.wrapT = value;
- }
- },
- magFilter: {
- get: function () {
- console.warn( 'THREE.WebGLRenderTarget: .magFilter is now .texture.magFilter.' );
- return this.texture.magFilter;
- },
- set: function ( value ) {
- console.warn( 'THREE.WebGLRenderTarget: .magFilter is now .texture.magFilter.' );
- this.texture.magFilter = value;
- }
- },
- minFilter: {
- get: function () {
- console.warn( 'THREE.WebGLRenderTarget: .minFilter is now .texture.minFilter.' );
- return this.texture.minFilter;
- },
- set: function ( value ) {
- console.warn( 'THREE.WebGLRenderTarget: .minFilter is now .texture.minFilter.' );
- this.texture.minFilter = value;
- }
- },
- anisotropy: {
- get: function () {
- console.warn( 'THREE.WebGLRenderTarget: .anisotropy is now .texture.anisotropy.' );
- return this.texture.anisotropy;
- },
- set: function ( value ) {
- console.warn( 'THREE.WebGLRenderTarget: .anisotropy is now .texture.anisotropy.' );
- this.texture.anisotropy = value;
- }
- },
- offset: {
- get: function () {
- console.warn( 'THREE.WebGLRenderTarget: .offset is now .texture.offset.' );
- return this.texture.offset;
- },
- set: function ( value ) {
- console.warn( 'THREE.WebGLRenderTarget: .offset is now .texture.offset.' );
- this.texture.offset = value;
- }
- },
- repeat: {
- get: function () {
- console.warn( 'THREE.WebGLRenderTarget: .repeat is now .texture.repeat.' );
- return this.texture.repeat;
- },
- set: function ( value ) {
- console.warn( 'THREE.WebGLRenderTarget: .repeat is now .texture.repeat.' );
- this.texture.repeat = value;
- }
- },
- format: {
- get: function () {
- console.warn( 'THREE.WebGLRenderTarget: .format is now .texture.format.' );
- return this.texture.format;
- },
- set: function ( value ) {
- console.warn( 'THREE.WebGLRenderTarget: .format is now .texture.format.' );
- this.texture.format = value;
- }
- },
- type: {
- get: function () {
- console.warn( 'THREE.WebGLRenderTarget: .type is now .texture.type.' );
- return this.texture.type;
- },
- set: function ( value ) {
- console.warn( 'THREE.WebGLRenderTarget: .type is now .texture.type.' );
- this.texture.type = value;
- }
- },
- generateMipmaps: {
- get: function () {
- console.warn( 'THREE.WebGLRenderTarget: .generateMipmaps is now .texture.generateMipmaps.' );
- return this.texture.generateMipmaps;
- },
- set: function ( value ) {
- console.warn( 'THREE.WebGLRenderTarget: .generateMipmaps is now .texture.generateMipmaps.' );
- this.texture.generateMipmaps = value;
- }
- }
- } );
- //
- Object.defineProperties( Audio.prototype, {
- load: {
- value: function ( file ) {
- console.warn( 'THREE.Audio: .load has been deprecated. Use THREE.AudioLoader instead.' );
- const scope = this;
- const audioLoader = new AudioLoader();
- audioLoader.load( file, function ( buffer ) {
- scope.setBuffer( buffer );
- } );
- return this;
- }
- },
- startTime: {
- set: function () {
- console.warn( 'THREE.Audio: .startTime is now .play( delay ).' );
- }
- }
- } );
- AudioAnalyser.prototype.getData = function () {
- console.warn( 'THREE.AudioAnalyser: .getData() is now .getFrequencyData().' );
- return this.getFrequencyData();
- };
- //
- CubeCamera.prototype.updateCubeMap = function ( renderer, scene ) {
- console.warn( 'THREE.CubeCamera: .updateCubeMap() is now .update().' );
- return this.update( renderer, scene );
- };
- CubeCamera.prototype.clear = function ( renderer, color, depth, stencil ) {
- console.warn( 'THREE.CubeCamera: .clear() is now .renderTarget.clear().' );
- return this.renderTarget.clear( renderer, color, depth, stencil );
- };
- //
- const GeometryUtils = {
- merge: function ( geometry1, geometry2, materialIndexOffset ) {
- console.warn( 'THREE.GeometryUtils: .merge() has been moved to Geometry. Use geometry.merge( geometry2, matrix, materialIndexOffset ) instead.' );
- let matrix;
- if ( geometry2.isMesh ) {
- geometry2.matrixAutoUpdate && geometry2.updateMatrix();
- matrix = geometry2.matrix;
- geometry2 = geometry2.geometry;
- }
- geometry1.merge( geometry2, matrix, materialIndexOffset );
- },
- center: function ( geometry ) {
- console.warn( 'THREE.GeometryUtils: .center() has been moved to Geometry. Use geometry.center() instead.' );
- return geometry.center();
- }
- };
- ImageUtils.crossOrigin = undefined;
- ImageUtils.loadTexture = function ( url, mapping, onLoad, onError ) {
- console.warn( 'THREE.ImageUtils.loadTexture has been deprecated. Use THREE.TextureLoader() instead.' );
- const loader = new TextureLoader();
- loader.setCrossOrigin( this.crossOrigin );
- const texture = loader.load( url, onLoad, undefined, onError );
- if ( mapping ) texture.mapping = mapping;
- return texture;
- };
- ImageUtils.loadTextureCube = function ( urls, mapping, onLoad, onError ) {
- console.warn( 'THREE.ImageUtils.loadTextureCube has been deprecated. Use THREE.CubeTextureLoader() instead.' );
- const loader = new CubeTextureLoader();
- loader.setCrossOrigin( this.crossOrigin );
- const texture = loader.load( urls, onLoad, undefined, onError );
- if ( mapping ) texture.mapping = mapping;
- return texture;
- };
- ImageUtils.loadCompressedTexture = function () {
- console.error( 'THREE.ImageUtils.loadCompressedTexture has been removed. Use THREE.DDSLoader instead.' );
- };
- ImageUtils.loadCompressedTextureCube = function () {
- console.error( 'THREE.ImageUtils.loadCompressedTextureCube has been removed. Use THREE.DDSLoader instead.' );
- };
- //
- function CanvasRenderer() {
- console.error( 'THREE.CanvasRenderer has been removed' );
- }
- //
- function JSONLoader() {
- console.error( 'THREE.JSONLoader has been removed.' );
- }
- //
- const SceneUtils = {
- createMultiMaterialObject: function ( /* geometry, materials */ ) {
- console.error( 'THREE.SceneUtils has been moved to /examples/jsm/utils/SceneUtils.js' );
- },
- detach: function ( /* child, parent, scene */ ) {
- console.error( 'THREE.SceneUtils has been moved to /examples/jsm/utils/SceneUtils.js' );
- },
- attach: function ( /* child, scene, parent */ ) {
- console.error( 'THREE.SceneUtils has been moved to /examples/jsm/utils/SceneUtils.js' );
- }
- };
- //
- function LensFlare() {
- console.error( 'THREE.LensFlare has been moved to /examples/jsm/objects/Lensflare.js' );
- }
- if ( typeof __THREE_DEVTOOLS__ !== 'undefined' ) {
- /* eslint-disable no-undef */
- __THREE_DEVTOOLS__.dispatchEvent( new CustomEvent( 'register', { detail: {
- revision: REVISION,
- } } ) );
- /* eslint-enable no-undef */
- }
- var THREE$1 = /*#__PURE__*/Object.freeze({
- __proto__: null,
- ACESFilmicToneMapping: ACESFilmicToneMapping,
- AddEquation: AddEquation,
- AddOperation: AddOperation,
- AdditiveAnimationBlendMode: AdditiveAnimationBlendMode,
- AdditiveBlending: AdditiveBlending,
- AlphaFormat: AlphaFormat,
- AlwaysDepth: AlwaysDepth,
- AlwaysStencilFunc: AlwaysStencilFunc,
- AmbientLight: AmbientLight,
- AmbientLightProbe: AmbientLightProbe,
- AnimationClip: AnimationClip,
- AnimationLoader: AnimationLoader,
- AnimationMixer: AnimationMixer,
- AnimationObjectGroup: AnimationObjectGroup,
- AnimationUtils: AnimationUtils,
- ArcCurve: ArcCurve,
- ArrayCamera: ArrayCamera,
- ArrowHelper: ArrowHelper,
- Audio: Audio,
- AudioAnalyser: AudioAnalyser,
- AudioContext: AudioContext,
- AudioListener: AudioListener,
- AudioLoader: AudioLoader,
- AxesHelper: AxesHelper,
- AxisHelper: AxisHelper,
- BackSide: BackSide,
- BasicDepthPacking: BasicDepthPacking,
- BasicShadowMap: BasicShadowMap,
- BinaryTextureLoader: BinaryTextureLoader,
- Bone: Bone,
- BooleanKeyframeTrack: BooleanKeyframeTrack,
- BoundingBoxHelper: BoundingBoxHelper,
- Box2: Box2,
- Box3: Box3,
- Box3Helper: Box3Helper,
- BoxBufferGeometry: BoxBufferGeometry,
- BoxGeometry: BoxGeometry,
- BoxHelper: BoxHelper,
- BufferAttribute: BufferAttribute,
- BufferGeometry: BufferGeometry,
- BufferGeometryLoader: BufferGeometryLoader,
- ByteType: ByteType,
- Cache: Cache,
- Camera: Camera,
- CameraHelper: CameraHelper,
- CanvasRenderer: CanvasRenderer,
- CanvasTexture: CanvasTexture,
- CatmullRomCurve3: CatmullRomCurve3,
- CineonToneMapping: CineonToneMapping,
- CircleBufferGeometry: CircleBufferGeometry,
- CircleGeometry: CircleGeometry,
- ClampToEdgeWrapping: ClampToEdgeWrapping,
- Clock: Clock,
- ClosedSplineCurve3: ClosedSplineCurve3,
- Color: Color,
- ColorKeyframeTrack: ColorKeyframeTrack,
- CompressedTexture: CompressedTexture,
- CompressedTextureLoader: CompressedTextureLoader,
- ConeBufferGeometry: ConeBufferGeometry,
- ConeGeometry: ConeGeometry,
- CubeCamera: CubeCamera,
- CubeGeometry: BoxGeometry,
- CubeReflectionMapping: CubeReflectionMapping,
- CubeRefractionMapping: CubeRefractionMapping,
- CubeTexture: CubeTexture,
- CubeTextureLoader: CubeTextureLoader,
- CubeUVReflectionMapping: CubeUVReflectionMapping,
- CubeUVRefractionMapping: CubeUVRefractionMapping,
- CubicBezierCurve: CubicBezierCurve,
- CubicBezierCurve3: CubicBezierCurve3,
- CubicInterpolant: CubicInterpolant,
- CullFaceBack: CullFaceBack,
- CullFaceFront: CullFaceFront,
- CullFaceFrontBack: CullFaceFrontBack,
- CullFaceNone: CullFaceNone,
- Curve: Curve,
- CurvePath: CurvePath,
- CustomBlending: CustomBlending,
- CustomToneMapping: CustomToneMapping,
- CylinderBufferGeometry: CylinderBufferGeometry,
- CylinderGeometry: CylinderGeometry,
- Cylindrical: Cylindrical,
- DataTexture: DataTexture,
- DataTexture2DArray: DataTexture2DArray,
- DataTexture3D: DataTexture3D,
- DataTextureLoader: DataTextureLoader,
- DataUtils: DataUtils,
- DecrementStencilOp: DecrementStencilOp,
- DecrementWrapStencilOp: DecrementWrapStencilOp,
- DefaultLoadingManager: DefaultLoadingManager,
- DepthFormat: DepthFormat,
- DepthStencilFormat: DepthStencilFormat,
- DepthTexture: DepthTexture,
- DirectionalLight: DirectionalLight,
- DirectionalLightHelper: DirectionalLightHelper,
- DiscreteInterpolant: DiscreteInterpolant,
- DodecahedronBufferGeometry: DodecahedronBufferGeometry,
- DodecahedronGeometry: DodecahedronGeometry,
- DoubleSide: DoubleSide,
- DstAlphaFactor: DstAlphaFactor,
- DstColorFactor: DstColorFactor,
- DynamicBufferAttribute: DynamicBufferAttribute,
- DynamicCopyUsage: DynamicCopyUsage,
- DynamicDrawUsage: DynamicDrawUsage,
- DynamicReadUsage: DynamicReadUsage,
- EdgesGeometry: EdgesGeometry,
- EdgesHelper: EdgesHelper,
- EllipseCurve: EllipseCurve,
- EqualDepth: EqualDepth,
- EqualStencilFunc: EqualStencilFunc,
- EquirectangularReflectionMapping: EquirectangularReflectionMapping,
- EquirectangularRefractionMapping: EquirectangularRefractionMapping,
- Euler: Euler,
- EventDispatcher: EventDispatcher,
- ExtrudeBufferGeometry: ExtrudeBufferGeometry,
- ExtrudeGeometry: ExtrudeGeometry,
- Face3: Face3,
- Face4: Face4,
- FaceColors: FaceColors,
- FileLoader: FileLoader,
- FlatShading: FlatShading,
- Float16BufferAttribute: Float16BufferAttribute,
- Float32Attribute: Float32Attribute,
- Float32BufferAttribute: Float32BufferAttribute,
- Float64Attribute: Float64Attribute,
- Float64BufferAttribute: Float64BufferAttribute,
- FloatType: FloatType,
- Fog: Fog,
- FogExp2: FogExp2,
- Font: Font,
- FontLoader: FontLoader,
- FrontSide: FrontSide,
- Frustum: Frustum,
- GLBufferAttribute: GLBufferAttribute,
- GLSL1: GLSL1,
- GLSL3: GLSL3,
- GammaEncoding: GammaEncoding,
- Geometry: Geometry,
- GeometryUtils: GeometryUtils,
- GreaterDepth: GreaterDepth,
- GreaterEqualDepth: GreaterEqualDepth,
- GreaterEqualStencilFunc: GreaterEqualStencilFunc,
- GreaterStencilFunc: GreaterStencilFunc,
- GridHelper: GridHelper,
- Group: Group,
- HalfFloatType: HalfFloatType,
- HemisphereLight: HemisphereLight,
- HemisphereLightHelper: HemisphereLightHelper,
- HemisphereLightProbe: HemisphereLightProbe,
- IcosahedronBufferGeometry: IcosahedronBufferGeometry,
- IcosahedronGeometry: IcosahedronGeometry,
- ImageBitmapLoader: ImageBitmapLoader,
- ImageLoader: ImageLoader,
- ImageUtils: ImageUtils,
- ImmediateRenderObject: ImmediateRenderObject,
- IncrementStencilOp: IncrementStencilOp,
- IncrementWrapStencilOp: IncrementWrapStencilOp,
- InstancedBufferAttribute: InstancedBufferAttribute,
- InstancedBufferGeometry: InstancedBufferGeometry,
- InstancedInterleavedBuffer: InstancedInterleavedBuffer,
- InstancedMesh: InstancedMesh,
- Int16Attribute: Int16Attribute,
- Int16BufferAttribute: Int16BufferAttribute,
- Int32Attribute: Int32Attribute,
- Int32BufferAttribute: Int32BufferAttribute,
- Int8Attribute: Int8Attribute,
- Int8BufferAttribute: Int8BufferAttribute,
- IntType: IntType,
- InterleavedBuffer: InterleavedBuffer,
- InterleavedBufferAttribute: InterleavedBufferAttribute,
- Interpolant: Interpolant,
- InterpolateDiscrete: InterpolateDiscrete,
- InterpolateLinear: InterpolateLinear,
- InterpolateSmooth: InterpolateSmooth,
- InvertStencilOp: InvertStencilOp,
- JSONLoader: JSONLoader,
- KeepStencilOp: KeepStencilOp,
- KeyframeTrack: KeyframeTrack,
- LOD: LOD,
- LatheBufferGeometry: LatheBufferGeometry,
- LatheGeometry: LatheGeometry,
- Layers: Layers,
- LensFlare: LensFlare,
- LessDepth: LessDepth,
- LessEqualDepth: LessEqualDepth,
- LessEqualStencilFunc: LessEqualStencilFunc,
- LessStencilFunc: LessStencilFunc,
- Light: Light,
- LightProbe: LightProbe,
- Line: Line,
- Line3: Line3,
- LineBasicMaterial: LineBasicMaterial,
- LineCurve: LineCurve,
- LineCurve3: LineCurve3,
- LineDashedMaterial: LineDashedMaterial,
- LineLoop: LineLoop,
- LinePieces: LinePieces,
- LineSegments: LineSegments,
- LineStrip: LineStrip,
- LinearEncoding: LinearEncoding,
- LinearFilter: LinearFilter,
- LinearInterpolant: LinearInterpolant,
- LinearMipMapLinearFilter: LinearMipMapLinearFilter,
- LinearMipMapNearestFilter: LinearMipMapNearestFilter,
- LinearMipmapLinearFilter: LinearMipmapLinearFilter,
- LinearMipmapNearestFilter: LinearMipmapNearestFilter,
- LinearToneMapping: LinearToneMapping,
- Loader: Loader,
- LoaderUtils: LoaderUtils,
- LoadingManager: LoadingManager,
- LogLuvEncoding: LogLuvEncoding,
- LoopOnce: LoopOnce,
- LoopPingPong: LoopPingPong,
- LoopRepeat: LoopRepeat,
- LuminanceAlphaFormat: LuminanceAlphaFormat,
- LuminanceFormat: LuminanceFormat,
- MOUSE: MOUSE,
- Material: Material,
- MaterialLoader: MaterialLoader,
- Math: MathUtils,
- MathUtils: MathUtils,
- Matrix3: Matrix3,
- Matrix4: Matrix4,
- MaxEquation: MaxEquation,
- Mesh: Mesh,
- MeshBasicMaterial: MeshBasicMaterial,
- MeshDepthMaterial: MeshDepthMaterial,
- MeshDistanceMaterial: MeshDistanceMaterial,
- MeshFaceMaterial: MeshFaceMaterial,
- MeshLambertMaterial: MeshLambertMaterial,
- MeshMatcapMaterial: MeshMatcapMaterial,
- MeshNormalMaterial: MeshNormalMaterial,
- MeshPhongMaterial: MeshPhongMaterial,
- MeshPhysicalMaterial: MeshPhysicalMaterial,
- MeshStandardMaterial: MeshStandardMaterial,
- MeshToonMaterial: MeshToonMaterial,
- MinEquation: MinEquation,
- MirroredRepeatWrapping: MirroredRepeatWrapping,
- MixOperation: MixOperation,
- MultiMaterial: MultiMaterial,
- MultiplyBlending: MultiplyBlending,
- MultiplyOperation: MultiplyOperation,
- NearestFilter: NearestFilter,
- NearestMipMapLinearFilter: NearestMipMapLinearFilter,
- NearestMipMapNearestFilter: NearestMipMapNearestFilter,
- NearestMipmapLinearFilter: NearestMipmapLinearFilter,
- NearestMipmapNearestFilter: NearestMipmapNearestFilter,
- NeverDepth: NeverDepth,
- NeverStencilFunc: NeverStencilFunc,
- NoBlending: NoBlending,
- NoColors: NoColors,
- NoToneMapping: NoToneMapping,
- NormalAnimationBlendMode: NormalAnimationBlendMode,
- NormalBlending: NormalBlending,
- NotEqualDepth: NotEqualDepth,
- NotEqualStencilFunc: NotEqualStencilFunc,
- NumberKeyframeTrack: NumberKeyframeTrack,
- Object3D: Object3D,
- ObjectLoader: ObjectLoader,
- ObjectSpaceNormalMap: ObjectSpaceNormalMap,
- OctahedronBufferGeometry: OctahedronBufferGeometry,
- OctahedronGeometry: OctahedronGeometry,
- OneFactor: OneFactor,
- OneMinusDstAlphaFactor: OneMinusDstAlphaFactor,
- OneMinusDstColorFactor: OneMinusDstColorFactor,
- OneMinusSrcAlphaFactor: OneMinusSrcAlphaFactor,
- OneMinusSrcColorFactor: OneMinusSrcColorFactor,
- OrthographicCamera: OrthographicCamera,
- PCFShadowMap: PCFShadowMap,
- PCFSoftShadowMap: PCFSoftShadowMap,
- PMREMGenerator: PMREMGenerator,
- ParametricBufferGeometry: ParametricBufferGeometry,
- ParametricGeometry: ParametricGeometry,
- Particle: Particle,
- ParticleBasicMaterial: ParticleBasicMaterial,
- ParticleSystem: ParticleSystem,
- ParticleSystemMaterial: ParticleSystemMaterial,
- Path: Path,
- PerspectiveCamera: PerspectiveCamera,
- Plane: Plane,
- PlaneBufferGeometry: PlaneBufferGeometry,
- PlaneGeometry: PlaneGeometry,
- PlaneHelper: PlaneHelper,
- PointCloud: PointCloud,
- PointCloudMaterial: PointCloudMaterial,
- PointLight: PointLight,
- PointLightHelper: PointLightHelper,
- Points: Points,
- PointsMaterial: PointsMaterial,
- PolarGridHelper: PolarGridHelper,
- PolyhedronBufferGeometry: PolyhedronBufferGeometry,
- PolyhedronGeometry: PolyhedronGeometry,
- PositionalAudio: PositionalAudio,
- PropertyBinding: PropertyBinding,
- PropertyMixer: PropertyMixer,
- QuadraticBezierCurve: QuadraticBezierCurve,
- QuadraticBezierCurve3: QuadraticBezierCurve3,
- Quaternion: Quaternion,
- QuaternionKeyframeTrack: QuaternionKeyframeTrack,
- QuaternionLinearInterpolant: QuaternionLinearInterpolant,
- REVISION: REVISION,
- RGBADepthPacking: RGBADepthPacking,
- RGBAFormat: RGBAFormat,
- RGBAIntegerFormat: RGBAIntegerFormat,
- RGBA_ASTC_10x10_Format: RGBA_ASTC_10x10_Format,
- RGBA_ASTC_10x5_Format: RGBA_ASTC_10x5_Format,
- RGBA_ASTC_10x6_Format: RGBA_ASTC_10x6_Format,
- RGBA_ASTC_10x8_Format: RGBA_ASTC_10x8_Format,
- RGBA_ASTC_12x10_Format: RGBA_ASTC_12x10_Format,
- RGBA_ASTC_12x12_Format: RGBA_ASTC_12x12_Format,
- RGBA_ASTC_4x4_Format: RGBA_ASTC_4x4_Format,
- RGBA_ASTC_5x4_Format: RGBA_ASTC_5x4_Format,
- RGBA_ASTC_5x5_Format: RGBA_ASTC_5x5_Format,
- RGBA_ASTC_6x5_Format: RGBA_ASTC_6x5_Format,
- RGBA_ASTC_6x6_Format: RGBA_ASTC_6x6_Format,
- RGBA_ASTC_8x5_Format: RGBA_ASTC_8x5_Format,
- RGBA_ASTC_8x6_Format: RGBA_ASTC_8x6_Format,
- RGBA_ASTC_8x8_Format: RGBA_ASTC_8x8_Format,
- RGBA_BPTC_Format: RGBA_BPTC_Format,
- RGBA_ETC2_EAC_Format: RGBA_ETC2_EAC_Format,
- RGBA_PVRTC_2BPPV1_Format: RGBA_PVRTC_2BPPV1_Format,
- RGBA_PVRTC_4BPPV1_Format: RGBA_PVRTC_4BPPV1_Format,
- RGBA_S3TC_DXT1_Format: RGBA_S3TC_DXT1_Format$1,
- RGBA_S3TC_DXT3_Format: RGBA_S3TC_DXT3_Format,
- RGBA_S3TC_DXT5_Format: RGBA_S3TC_DXT5_Format$1,
- RGBDEncoding: RGBDEncoding,
- RGBEEncoding: RGBEEncoding,
- RGBEFormat: RGBEFormat,
- RGBFormat: RGBFormat,
- RGBIntegerFormat: RGBIntegerFormat,
- RGBM16Encoding: RGBM16Encoding,
- RGBM7Encoding: RGBM7Encoding,
- RGB_ETC1_Format: RGB_ETC1_Format,
- RGB_ETC2_Format: RGB_ETC2_Format,
- RGB_PVRTC_2BPPV1_Format: RGB_PVRTC_2BPPV1_Format,
- RGB_PVRTC_4BPPV1_Format: RGB_PVRTC_4BPPV1_Format,
- RGB_S3TC_DXT1_Format: RGB_S3TC_DXT1_Format,
- RGFormat: RGFormat,
- RGIntegerFormat: RGIntegerFormat,
- RawShaderMaterial: RawShaderMaterial,
- Ray: Ray,
- Raycaster: Raycaster,
- RectAreaLight: RectAreaLight,
- RedFormat: RedFormat,
- RedIntegerFormat: RedIntegerFormat,
- ReinhardToneMapping: ReinhardToneMapping,
- RepeatWrapping: RepeatWrapping,
- ReplaceStencilOp: ReplaceStencilOp,
- ReverseSubtractEquation: ReverseSubtractEquation,
- RingBufferGeometry: RingBufferGeometry,
- RingGeometry: RingGeometry,
- SRGB8_ALPHA8_ASTC_10x10_Format: SRGB8_ALPHA8_ASTC_10x10_Format,
- SRGB8_ALPHA8_ASTC_10x5_Format: SRGB8_ALPHA8_ASTC_10x5_Format,
- SRGB8_ALPHA8_ASTC_10x6_Format: SRGB8_ALPHA8_ASTC_10x6_Format,
- SRGB8_ALPHA8_ASTC_10x8_Format: SRGB8_ALPHA8_ASTC_10x8_Format,
- SRGB8_ALPHA8_ASTC_12x10_Format: SRGB8_ALPHA8_ASTC_12x10_Format,
- SRGB8_ALPHA8_ASTC_12x12_Format: SRGB8_ALPHA8_ASTC_12x12_Format,
- SRGB8_ALPHA8_ASTC_4x4_Format: SRGB8_ALPHA8_ASTC_4x4_Format,
- SRGB8_ALPHA8_ASTC_5x4_Format: SRGB8_ALPHA8_ASTC_5x4_Format,
- SRGB8_ALPHA8_ASTC_5x5_Format: SRGB8_ALPHA8_ASTC_5x5_Format,
- SRGB8_ALPHA8_ASTC_6x5_Format: SRGB8_ALPHA8_ASTC_6x5_Format,
- SRGB8_ALPHA8_ASTC_6x6_Format: SRGB8_ALPHA8_ASTC_6x6_Format,
- SRGB8_ALPHA8_ASTC_8x5_Format: SRGB8_ALPHA8_ASTC_8x5_Format,
- SRGB8_ALPHA8_ASTC_8x6_Format: SRGB8_ALPHA8_ASTC_8x6_Format,
- SRGB8_ALPHA8_ASTC_8x8_Format: SRGB8_ALPHA8_ASTC_8x8_Format,
- Scene: Scene,
- SceneUtils: SceneUtils,
- ShaderChunk: ShaderChunk,
- ShaderLib: ShaderLib,
- ShaderMaterial: ShaderMaterial,
- ShadowMaterial: ShadowMaterial,
- Shape: Shape,
- ShapeBufferGeometry: ShapeBufferGeometry,
- ShapeGeometry: ShapeGeometry,
- ShapePath: ShapePath,
- ShapeUtils: ShapeUtils,
- ShortType: ShortType,
- Skeleton: Skeleton,
- SkeletonHelper: SkeletonHelper,
- SkinnedMesh: SkinnedMesh,
- SmoothShading: SmoothShading,
- Sphere: Sphere,
- SphereBufferGeometry: SphereBufferGeometry,
- SphereGeometry: SphereGeometry,
- Spherical: Spherical,
- SphericalHarmonics3: SphericalHarmonics3,
- Spline: Spline,
- SplineCurve: SplineCurve,
- SplineCurve3: SplineCurve3,
- SpotLight: SpotLight,
- SpotLightHelper: SpotLightHelper,
- Sprite: Sprite,
- SpriteMaterial: SpriteMaterial,
- SrcAlphaFactor: SrcAlphaFactor,
- SrcAlphaSaturateFactor: SrcAlphaSaturateFactor,
- SrcColorFactor: SrcColorFactor,
- StaticCopyUsage: StaticCopyUsage,
- StaticDrawUsage: StaticDrawUsage,
- StaticReadUsage: StaticReadUsage,
- StereoCamera: StereoCamera,
- StreamCopyUsage: StreamCopyUsage,
- StreamDrawUsage: StreamDrawUsage,
- StreamReadUsage: StreamReadUsage,
- StringKeyframeTrack: StringKeyframeTrack,
- SubtractEquation: SubtractEquation,
- SubtractiveBlending: SubtractiveBlending,
- TOUCH: TOUCH,
- TangentSpaceNormalMap: TangentSpaceNormalMap,
- TetrahedronBufferGeometry: TetrahedronBufferGeometry,
- TetrahedronGeometry: TetrahedronGeometry,
- TextBufferGeometry: TextBufferGeometry,
- TextGeometry: TextGeometry,
- Texture: Texture,
- TextureLoader: TextureLoader,
- TorusBufferGeometry: TorusBufferGeometry,
- TorusGeometry: TorusGeometry,
- TorusKnotBufferGeometry: TorusKnotBufferGeometry,
- TorusKnotGeometry: TorusKnotGeometry,
- Triangle: Triangle,
- TriangleFanDrawMode: TriangleFanDrawMode,
- TriangleStripDrawMode: TriangleStripDrawMode,
- TrianglesDrawMode: TrianglesDrawMode,
- TubeBufferGeometry: TubeBufferGeometry,
- TubeGeometry: TubeGeometry,
- UVMapping: UVMapping,
- Uint16Attribute: Uint16Attribute,
- Uint16BufferAttribute: Uint16BufferAttribute,
- Uint32Attribute: Uint32Attribute,
- Uint32BufferAttribute: Uint32BufferAttribute,
- Uint8Attribute: Uint8Attribute,
- Uint8BufferAttribute: Uint8BufferAttribute,
- Uint8ClampedAttribute: Uint8ClampedAttribute,
- Uint8ClampedBufferAttribute: Uint8ClampedBufferAttribute,
- Uniform: Uniform,
- UniformsLib: UniformsLib,
- UniformsUtils: UniformsUtils,
- UnsignedByteType: UnsignedByteType,
- UnsignedInt248Type: UnsignedInt248Type$1,
- UnsignedIntType: UnsignedIntType,
- UnsignedShort4444Type: UnsignedShort4444Type,
- UnsignedShort5551Type: UnsignedShort5551Type,
- UnsignedShort565Type: UnsignedShort565Type,
- UnsignedShortType: UnsignedShortType,
- VSMShadowMap: VSMShadowMap,
- Vector2: Vector2$1,
- Vector3: Vector3,
- Vector4: Vector4,
- VectorKeyframeTrack: VectorKeyframeTrack,
- Vertex: Vertex,
- VertexColors: VertexColors,
- VideoTexture: VideoTexture,
- WebGL1Renderer: WebGL1Renderer,
- WebGLCubeRenderTarget: WebGLCubeRenderTarget,
- WebGLMultisampleRenderTarget: WebGLMultisampleRenderTarget,
- WebGLRenderTarget: WebGLRenderTarget,
- WebGLRenderTargetCube: WebGLRenderTargetCube,
- WebGLRenderer: WebGLRenderer,
- WebGLUtils: WebGLUtils,
- WireframeGeometry: WireframeGeometry,
- WireframeHelper: WireframeHelper,
- WrapAroundEnding: WrapAroundEnding,
- XHRLoader: XHRLoader,
- ZeroCurvatureEnding: ZeroCurvatureEnding,
- ZeroFactor: ZeroFactor,
- ZeroSlopeEnding: ZeroSlopeEnding,
- ZeroStencilOp: ZeroStencilOp,
- sRGBEncoding: sRGBEncoding
- });
- var points = [];
- var lines = [];
- var rings = [];
-
- var precision = 0.1; //容错精度 //正常是0.01 但是在编辑时容易出现交错的线看不出来,导致需要getSliceLines 然后多出新增点
-
-
- var getPoint = function(o, type){
- var point;
- if(typeof o == "string" || typeof o == "number")point = points.find(p=> p.ids.includes(o));
- else {
- point = points.find(p=> math.closeTo(p.x , o.x, precision) && math.closeTo(p.y , o.y, precision) );
- if(!point) point = new Point(o.x, o.y,{record:true, id:o.id}, type);
- else {
- //console.log('addPoint', point, o)
- point.addPoint(o.id);
- }
- }
- if(!point){
- console.log("no point!");
- }
-
-
- return point
- };
- var getLine = function(id){
- return lines.find(line=> line.ids.includes(id));
- };
- var getAngleInfo = function(points){
- var info = {};
- info.angle = points[1].clone().sub(points[0]).angle();
- if(math.closeTo(info.angle, Math.PI*2)){ //如360-0.01
- info.angle -= Math.PI*2; //有可能得到负数-0.001
- }else if(info.angle > Math.PI || math.closeTo(info.angle, Math.PI)){//如180+-0.01
- info.angle -= Math.PI;
- info.reverse = true;
- }
- return info //结果大约是 0 - 3.14
- };
- class Point extends Vector2$1{
- constructor(x, y, o={}){
- super(x, y);
-
- if(o.record){
- this.id = o.id;
- if(this.id == void 0) this.id = "add_"+points.length;
- this.ids = [this.id] ;//存储拥有该坐标的点原始数据的id
- points.push(this);
- }
-
- this.type = o.type || "";
- this.lines = [];
-
- }
-
- addPoint(id){
- this.ids.push(id);
- }
-
- searchLineByFactor(dir, type, comeLine){
-
- var lines = this.lines.filter(line=>line.searchTime<2);
-
- if(lines.length==0)return;
- else if(lines.length==1)return lines[0];
- else lines = lines.filter(line=>line!=comeLine);
-
- if(lines.length==1)return lines[0];
-
- var result;
- lines.forEach(line=>{
- var vec = line.getVector();
- if(line.points[1] == this) vec.negate();
- var factor = math.getVec2Angle(dir, vec);
-
- if(new Vector3(dir.x, dir.y, 0).cross(new Vector3(vec.x, vec.y, 0)).z<0) factor*= -1; /////
-
- if(!result){
- result = {line, factor};
- }
- else {
- if(type == "min" && factor<result.factor || type == "max" && factor>result.factor) result = {line, factor};
- }
- });
- return result.line;
- }
- }
-
- var lineLen=0;
- class Line$1{
- constructor(o){
- if(o.points[0] == o.points[1])return;
- this.points = o.points;
- this.type = o.type || 'line';
-
- if(this.type == 'line'){
- var oldLine = lines.find(line=>line.points.includes(o.points[0]) && line.points.includes(o.points[1]));
- if(oldLine){
- o.id != void 0 && oldLine.ids.push(o.id);
- return oldLine;
- }
- this.id = o.id == void 0 ? ("line"+lineLen ++) : o.id;
- this.ids = [this.id];
-
- o.dontWriteToPoint || this.points.forEach((point)=>{point.lines.push(this);});
- o.isChild || lines.push(this);
- this.searchTime = 0; // 最多两次
- }
-
- this.children = [];//分割
- this.parents = [];//分割
- this.match = [];
-
-
- }
-
-
- getAngleInfo(){
- var angleInfo = getAngleInfo(this.points);
- this.angle = angleInfo.angle;
- this.reverse = angleInfo.reverse;
- }
-
- getIntersectWithLine(line, precision){
- var joint = line.points.find(point=>this.points.includes(point));
- if(joint)return {point:joint, type:"joint"};
-
- var intersect = math.isLineIntersect( line.points , this.points , false, precision );
- if(intersect) return {point: intersect, type:"intersect"};
-
-
- }
-
- writeToPoint(){
- this.points.forEach((point)=>{point.lines.includes(this) || point.lines.push(this);});
- }
-
- checkIfParent(line){
- if(this == line){
- return true;//原因就是slice的点和端点很近 误差导致
- }
- else return this.parents.find(e=>e.checkIfParent(line))
-
- }
-
- splitByPoint(point){
- var line1 = new Line$1({points:[point, this.points[0]], dontWriteToPoint:true, hasntsure:true});
- var line2 = new Line$1({points:[point, this.points[1]], dontWriteToPoint:true, hasntsure:true});
-
- if(!line1.points || !line2.points){//有至少一个是点相同的,没写到group.lines里
-
- console.warn('splitByPoint 线有点相同');
- return;
- }
-
- if(this.checkIfParent(line1)||this.checkIfParent(line2) || line1.checkIfParent(this) || line2.checkIfParent(this)){
- console.warn("splitByPoint 发现parent和children一样");//,请检查getSliceWalls,尤其 if(math.closeTo(line1.angle,line2.angle)){ 处
- return;
- }
- var deal = (line)=>{
- this.children.push(line);
- line.parents.push(this);
-
- if(!lines.includes(line))lines.push(line);
- line.writeToPoint();
-
- };
- deal(line1);
- deal(line2);
-
- var index = this.points[0].lines.indexOf(this);
- index > -1 && this.points[0].lines.splice(index,1);
- var index = this.points[1].lines.indexOf(this);
- index > -1 && this.points[1].lines.splice(index,1);
-
-
- return [line1,line2]
- }
- splitByPoints(points){
- points = points.map(point=>{return {dis:point.distanceTo(this.points[0]), point:point}});
- points.sort((point1, point2)=>{return point1.dis - point2.dis});
- var children = [];
-
-
- points.forEach((point, index)=>{
- var line1 = new Line$1({points:[point.point, index==0?this.points[0]:points[index-1].point ],group:this.group , dontWriteToPoint:true, hasntsure:true});
- children.push(line1);
- });
- var line2 = new Line$1({points:[points[points.length-1].point, this.points[1] ],group:this.group , dontWriteToPoint:true, hasntsure:true});
- children.push(line2);
-
-
-
- var a = children.find(line=> !line.points || this.checkIfParent(line) || line.checkIfParent(this));
- if(a){
- console.error("splitByPoints return");
- return;
- }
-
-
- children.forEach(line=>{
- this.children.push(line);
- line.parents.push(this);
-
- if(!lines.includes(line))lines.push(line);
- line.writeToPoint();
- line.writeToPoint();
- });
-
- var index = this.points[0].lines.indexOf(this);
- index > -1 && this.points[0].lines.splice(index,1);
- var index = this.points[1].lines.indexOf(this);
- index > -1 && this.points[1].lines.splice(index,1);
-
-
- }
-
-
- getAllSlices(){//如果有被分割的片段 就返回片段,否则返回自身
- var children = [];
- var traverse = function(elem){
- if(elem.children.length == 0) children.push(elem);
- else elem.children.forEach(traverse);
- };
- traverse(this);
- return children
- }
- getVector(){
- return this.points[1].clone().sub(this.points[0]);
- }
-
- getLength(){
- return this.points[0].distanceTo(this.points[1])
- }
-
- getCenter(){
- return this.points[1].clone().add(this.points[0]).multiplyScalar(.5);
- }
- }
- var getMixedSet = function(arr1, arr2){//交集
- return arr1.filter(item=>arr2.includes(item));
- };
- var getUnionSet = function(arr1, arr2){//并集
- return arr1.concat(arr2.filter(item=>!arr1.includes(item)))
- };
- var getDifferenceSet = function(arr1, arr2){//差集
- var arr11 = arr1.filter(item=>!arr2.includes(item));
- var arr22 = arr2.filter(item=>!arr1.includes(item));
- return arr11.concat(arr22)
- };
- var getDifferenceSetMuti = function(arr){//收集绝对没有重复的元素,也就是判断出现次数=1的
- var set = [];
- arr.forEach(arr1=>{
- arr1.forEach(item=>{
- var index = set.indexOf(item);
- if(index>-1){
- set.splice(index, 1);
- }else {
- set.push(item);
- }
- });
- });
- return set;
- };
- function DoorAtWhichLine(points, lines){
- var mid = points[0].clone().add(points[1]).multiplyScalar(0.5);
- lines = lines.filter(line=>math.ifPointAtLineBound(mid, line.points, precision));
- if(lines.length == 0)return
- var result = {line:null, dis:Infinity};
- lines.forEach(line=>{
- var foot = math.getFootPoint(mid, line.points[0], line.points[1] );
- var dis = foot.distanceTo(mid);
- if(dis<result.dis){
- result.line = line; result.dis = dis;
- }
- });
- return result
-
- }
- var ringLen = 0;
- class Ring{
- constructor(o){
- this.id = ringLen ++;
- this.type = o.type || 'normal';
- this.points = o.points;
- this.lines = o.lines;
- rings.push(this);
- this.child = [];//包含的环
- this.parent = [];//被包含的环
- this.smallNeibours = [];//相邻最小环(存在和它有一个以上的相同边的最小环)
-
- var area = math.getArea(this.points);
- this.area = Math.abs(area);
- this.isClockwise = area<0;//是否逆时针。一般都是逆时针得到的,如果是顺时针,可能是贪吃蛇的情况,可能不是最小环,需要去掉。
- }
-
- }
- var findLine = function(p1,p2){
- return lines.find(line=>line.points.includes(p1) && line.points.includes(p2) )
- };
- var ifSamePart = function(checkPart , part){//checkPart中所包含的part片段是否和基准part的顺序一样(逆序也可以, 中间有其他数也可以,起始不同也行。比如 01234和204一样的)
- var axis, startIndex, newCheckPart=[];
-
-
- for(var j=0,len1 = checkPart.length; j<len1; j++){//将checkPart中比part多的数除去,使两个数组中包含的数完全相同。
- if(part.indexOf(checkPart[j])>-1)newCheckPart.push(checkPart[j]);
- }
- for(var i=0,len = part.length; i<len; i++){
- var index = newCheckPart.indexOf(part[i]);
- if(index == -1)return false;
- if(i == 0) startIndex = index;//标记第一个查找点对应的index
- else if(i == 1){//标记查找顺序是正还是逆
- axis = index - startIndex;
- if(axis == len - 1) axis = -1;//刚好是首和尾
- else if(axis == 1- len) axis = 1;
-
- if(axis != -1 && axis != 1){
- return false
- }
- }else {//判断是否是按顺序的
- if(index != ((startIndex+axis * i + len) % len) ) return false;
- }
- }
- return {sameAxis:axis>0}; //如果一样的话返回正逆是否相同
- };
- //或者判断是否有相同边(但是相同点是可以组成不同环)
- var ifSameRing = function(ring1, ring2){//判断两个环是否相等。 除了可以逆向外顺序要对
- if(ring1 instanceof Ring)ring1 = ring1.points;
- if(ring2 instanceof Ring)ring2 = ring2.points;
- if(ring1.length != ring2.length)return false;
- if(ring1.lines && ring2.lines){
- if(getDifferenceSet(ring1.lines , ring2.lines).length == 0)return true;//差集个数为0
- }else {
- if(ifSamePart(ring1, ring2))return true
- }
-
- };
-
-
- var atWhichChildLine = function(point, line, precision){
- if(line.children.length == 0){//这里可能要放低精度 保证能找到
- if(math.ifPointAtLineBound(point, line.points, precision)) return line;
-
- }else {
- for(var i=0;i<line.children.length;i++){
- var at = atWhichChildLine(point, line.children[i], precision);
- if(at)return at
- }
- }
- };
- function getSliceLines(){
- var len = lines.length;
-
-
- var deal = function(line1,line2){
- if(line1 == line2)return;
-
- if(line1.angle == void 0) line1.getAngleInfo();
- if(line2.angle == void 0) line2.getAngleInfo();
-
- var intersect = line1.getIntersectWithLine(line2, precision);
- if(intersect){
- var point; //得到交点
- if(intersect.type == "intersect"){
- point = getPoint(intersect.point, "whenGetSliceLines");
-
- var line1_ = atWhichChildLine(point, line1);
- var line2_ = atWhichChildLine(point, line2);
- //重合的情况还没考虑(平行)
- if(!line1_) line1_ = atWhichChildLine(point, line1, precision);//降低精度
- if(!line1_) line1_ = atWhichChildLine(point, line1, precision*2);//降低精度
- if(!line2_) line2_ = atWhichChildLine(point, line2, precision);
- if(!line2_) line2_ = atWhichChildLine(point, line2, precision*2);//降低精度
- //拆分线条:
-
- //如果还报错,找不到ChildLine,就直接返回吧 或者搞个循环 逐渐降低精度
- if(!line1_ || !line2_){
- console.warn("atWhichChildLine仍旧找不到 :" + line1.id + ',' + line2.id + ", pointId: "+point.id);
- line1_ || console.warn("找不到line1");
- line2_ || console.warn("找不到line2");
- return;
- }
-
- if(line1_.points.find(p=>p == point) && line2_.points.find(p=>p == point)){//这个点是line1_、 line2_端点,不做处理
- //console.log("joint型 "+point.id)
- }else if(line1_.points.find(p=>p == point)){//T型交叉
- line2_.splitByPoint(point);//加入到母线中,之后还先用母线判断交点
- //console.log("T型交叉1 "+point.id)
- }else if(line2_.points.find(p=>p == point)){//T型交叉
- line1_.splitByPoint(point);
- //console.log("T型交叉2 "+point.id)
- }else {//十字交叉
- line1_.splitByPoint(point);
- line2_.splitByPoint(point);
- }
- }else {
- point = intersect.point;//交点是端点
- if(math.closeTo(line1.angle,line2.angle)){ //重合一部分
- var children1 = line1.getAllSlices();
- var children2 = line2.getAllSlices();
- if(children1.length>1 || children2.length>1){ //使用最小分割片段来比较
- children1.forEach(child1=>{
- children2.forEach(child2=>{
- deal(child1, child2);
- });
- });
- return;
- }
-
- var anotherPoint1 = line1.points.find(point_=>point_!=point);
- var anotherPoint2 = line2.points.find(point_=>point_!=point);
- if(math.ifPointAtLineBound(anotherPoint1, line2.points)){
- line2.splitByPoint(anotherPoint1);
- }else if(math.ifPointAtLineBound(anotherPoint2, line1.points)){
- line1.splitByPoint(anotherPoint2);
- }
- }
-
- }
-
- }else if(math.closeTo(line1.angle,line2.angle)){
- var vec1 = line1.getVector();
- var vec = line1.points[0].clone().sub(line2.points[0]);
- var cos = math.getVec2Cos(vec1, vec);
- if(math.closeTo(cos, -1, 1e-4) || math.closeTo(cos, 1, 1e-4)){ //共线
-
- var children1 = line1.getAllSlices();
- var children2 = line2.getAllSlices();
- if(children1.length>1 || children2.length>1){ //使用最小分割片段来比较
- children1.forEach(child1=>{
- children2.forEach(child2=>{
- deal(child1, child2);
- });
- });
- return;
- }
-
- //判断是否重叠
- var A = line1.points[0];
- var C = line1.reverse == line2.reverse ? line2.points[0] : line2.points[1];
-
- var B = line1.points[1];
- var D = line1.reverse == line2.reverse ? line2.points[1] : line2.points[0];
-
- var BC = C.clone().sub(B);
- var AD = D.clone().sub(A);
- if(BC.length()<AD.length()){
- var BA = A.clone().sub(B);
- if(math.getVec2Angle(BC, BA) >= 1.57 )return;//没有重叠部分
- }else {
- var AB = B.clone().sub(A);
- if(math.getVec2Angle(AD, AB) >= 1.57 )return;
- }
-
-
-
- var f = function(line1,line2){
- var one = math.ifPointAtLineBound(line1.points[0], line2.points);
- var two = math.ifPointAtLineBound(line1.points[1], line2.points);
- if(one && two){//line1在line2上
- line2.splitByPoints( line1.points );
- return true
- }else if(one || two){//错开
- var point1 = one ? line1.points[0] : line1.points[1];
- var anotherPoint1 = one ? line1.points[1] : line1.points[0];
- var dis1 = line2.points[0].distanceTo(anotherPoint1);
- var dis2 = line2.points[1].distanceTo(anotherPoint1);
- var point2 = dis1 < dis2 ? line2.points[0] : line2.points[1];
- line1.splitByPoint(point2);
- line2.splitByPoint(point1);
- return true
- }
- };
- f(line1, line2) || f(line2, line1);
- }
- }
-
- };
-
- for(let i=0;i<len;i++){
- let line1 = lines[i];
- for(let j=i+1;j<len;j++){
- let line2 = lines[j];
- deal(line1,line2);
-
- }
- }
-
- //console.log("原有线条个数:"+len)
-
- //lines = lines.filter((line)=>{return line.children.length == 0})
-
- //console.log("现有线条个数:"+lines.length)
-
- }
- var bound = new Box2();
- var build = function(o){
- //融合了相近点
- //根据bound 处理precision
- o.points.forEach(p=>{
- bound.expandByPoint(new Vector2$1(p.x,p.y));
- });
-
-
- if(o.precision != void 0){
- precision = o.precision;
- }else {
- var boundSize = bound.getSize(new Vector2$1);
- precision = MathUtils.clamp(Math.max(boundSize.x, boundSize.y) / 70, 0.2, 2);
- }
-
-
-
-
-
-
-
- o.points.forEach(point=>getPoint(point));//{x:..,y:..}
-
-
- o.lines.forEach(line=>{ //{p1:id1. p2:id2}
- new Line$1({points:[getPoint(line.p1), getPoint(line.p2)], id:line.id });
- });
- //注意:不能出现一条线的两个点坐标一致,否则寻路时方向出错。 所以手动融合下相近点。
- };
-
-
- var searchRings = function(o={}){
- points = [];
- lines = [];
- rings = [];
- lineLen = ringLen = 0;
- o.points = o.points || [];
- o.lines = o.lines || [];
-
-
- build(o);
-
- if(!o.dontSliceLines){
- getSliceLines();
- }
-
-
-
-
-
- //查找最小回路:
- //参考: 引入方向因子的最小回路、最大回路搜索算法.pdf
- //方法: 逆时针寻找(标记)最外层大环 -->从走过的点开始逆时针寻找最小环(直到所有可走的路被走过两次)-->逆时针寻找最外层大环(直到所有可走的路被走过两次)-->..
- //其中找大环时选择方向因子最小的路, 而小环则相反(但只有开始第一条路是一样的, 都是选择最左边的点的因子最小的路)。
- //标记方法: 每条线需要被搜索两次才算完毕。搜索完毕的线退出搜索。(依据:搜索完全部最小回路后 , 在无向图中删除搜索过 2 次的边及孤立节点得到退化图 , 恰好构成最大回路。)
- var searchTime = 0;
- var addRingJudgeCount = 0;
- var addRingJudge = function(ring, lines, connectedLines, type){// 处理拣出的片段
- addRingJudgeCount++;
-
- //console.log("addRingJudge points("+ type+"):"+ ring.map(point=>point.id) )
- if(o.onlyGetOutRing && type == "small")return
-
- if(type == "small" || o.onlyGetOutRing){//挑出回路:
- var newRings = [];
- while(ring.length){
- var road = [];
- var turnBack = false;
- for(let i=0;i<ring.length;i++){
- if(road.includes(ring[i])){//如果走到方才的点,可能形成回路。 无论是不是回路都要摘去这段。
- var index = road.indexOf(ring[i]);
- var pointArr = ring.slice(index, i);
- var linesArr = lines.slice(index, i);
- ring.splice(index,i-index);
- lines.splice(index,i-index);
- if(pointArr.length>2){// 如果只有两个数,代表原路返回, 如 1->2(->1)
- if( !rings.find(ring_=>ifSameRing(pointArr, ring_))) newRings.push( new Ring({points: pointArr, lines:linesArr}) );
- }
- turnBack = true;
- break;
- }else {
- road.push(ring[i]);
- turnBack = false;
- }
- }
- if(!turnBack){//没有重复的点,那么就直接处理整条。
- if(ring.length>2){// 如果只有两个数,代表原路返回, 如 1->2(->1)
- if( !rings.find(ring_=>ifSameRing(ring, ring_))) newRings.push( new Ring({points: ring, lines}) );
- }
- break;
- }
- }
-
- if(type != 'small'){
- newRings.forEach(e=>e.isOutRing = true);
- }
-
-
- //console.log(newRings)
- }else {
- return ring
- }
- };
-
-
- var search = function(point2d, comeRoad, type, connectedLines){
- searchTime++;
- var goLine;
- var direction;
- if(type.includes("big")){
- if(!comeRoad){
- if(type.includes("Left")){//逆时针
- direction = new Vector2$1(1,0);
- }else {
- direction = new Vector2$1(-1,0);
- }
- goLine = point2d.searchLineByFactor(direction,"min");
- }else {
- var lastPoint = comeRoad.points[comeRoad.points.length-1];
- direction = point2d.clone().sub(lastPoint);
- goLine = point2d.searchLineByFactor(direction,"min", findLine(point2d, lastPoint));
- }
-
- }else {
- if(!comeRoad){
- //似乎找最小环时,第一条线也是找最小的因子,这样才能保证逆时针(除非只有顺时针一条路)
- direction = new Vector2$1(1,0);
- goLine = point2d.searchLineByFactor(direction,"min");
-
- }else {
- var lastPoint = comeRoad.points[comeRoad.points.length-1];
- direction = point2d.clone().sub(lastPoint);
- goLine = point2d.searchLineByFactor(direction,"max", findLine(point2d, lastPoint));
- }
- }
- if(!goLine)return
-
-
- goLine.searchTime++;
- connectedLines.includes(goLine) || connectedLines.push(goLine);
-
-
- var nextPoint = goLine.points.find( point => point2d!=point );
-
- //if( comeRoad && comeRoad.points[comeRoad.points.length - 1] == nextPoint ) return;//不能查找来时的方向(反方向)
- //走不通就原路返回
-
- var roadPoints = comeRoad ? comeRoad.points.concat([point2d]) : [point2d];//每个分叉都能构成一条新的road
- var roadLines = comeRoad ? comeRoad.lines.concat([goLine]) : [goLine];
-
-
-
-
- //走到第一个点就算停止,这时候可能得到一个环、或者一段走了两遍的线、或者一条线上带了些环。
- if(nextPoint == roadPoints[0]) return addRingJudge(roadPoints, roadLines, connectedLines, type) //形成环
- else {
- /* var len = roadPoints.indexOf(nextPoint);
- if( len > -1){ //走到走过的路的某一点 构成这段路的回路
- var points = roadPoints.slice(len, roadPoints.length);
- var lines = roadLines.slice(len, roadPoints.length);
- addRingJudge(points, lines)
- }else{ */
- return search(nextPoint, {lines:roadLines, points:roadPoints}, type, connectedLines);//继续寻路
- //}
- }
-
- };
-
- while(1){//搜寻一次大环
- var connectedLines = [];//被搜寻过的且searchTime<2的线。一旦全部搜完就说明该连通区域搜寻完毕,继续查下一个连通区域。
- var startPoint = null;
- points.forEach(point=>{//找出x最小的点
- if(!point.lines.find(line=>line.searchTime<2))return;
- if(!startPoint)startPoint = point;
- else if(point.x < startPoint.x)startPoint = point;
- });
- if(!startPoint)break; //说明全部找完
-
-
- var ring = search(startPoint, null, "bigLeft", connectedLines);//逆时针
- //search(startPoint, null, "bigRight", connectedLines);//顺时针(为了防止最外层不是回路之前写了顺时针,但如果是回路就会走重复。后来发现只要逆时针即可,因为走完后剩下的可以再次找大环)
-
- connectedLines = connectedLines.filter(line=>line.searchTime<2);
-
-
-
-
- while(connectedLines.length>0){//目标是顺着connectedLines把所有连通的小环都找到
-
- let points_ = [];//connectedLines中所有的点
- connectedLines.forEach(line=>line.points.forEach(point=>{if(!points_.includes(point))points_.push(point); }));
- var startPoint = null;
- points_.forEach(point=>{//找出x最小的点
- if(!point.lines.find(line=>line.searchTime<2))return;
- if(!startPoint)startPoint = point;
- else if(point.x < startPoint.x)startPoint = point;
- });
- if(!startPoint)break;
-
-
- search(startPoint, null, "small", connectedLines);
-
- connectedLines = connectedLines.filter(line=>line.searchTime<2);
- }
- }
-
-
-
- /* if(o.onlyGetOutRing){
- rings = rings.filter(e=>e.isOutRing)
- } */
-
- //console.log("searchTime "+searchTime + ", addRingJudgeCount " +addRingJudgeCount)
-
-
-
-
- //找出所有的相邻关系,包括公共边
- var len = rings.length;
- for(let i=0; i<len; i++){
- let ring1 = rings[i];
- for(let j=i+1; j<len; j++){
- let ring2 = rings[j];
- var bothHasLines = getMixedSet(ring1.lines, ring2.lines);
- if(bothHasLines.length){//ring1oíring2?àáú
- ring1.smallNeibours.push(ring2);
- ring2.smallNeibours.push(ring1);
- }else {
- }
- }
- }
- rings.forEach(ring1=>{
- for(let i=0; i<len; i++){
- var ring2 = rings[i];
- if(ring1 == ring2 || ring1.smallNeibours.includes(ring2))continue;
-
- let inside;
- for(let u=0;u<ring1.points.length;u++){
- inside = math.isPointInArea(ring2.points, null, ring1.points[u]);
- if(!inside)break
- else if(inside && !inside.atLine){
- break
- }
- }
-
-
- if(inside){ //只要其中一个点在ring2内,就说明ring1是内环
- if(inside.atLine){//(还是会存在点全在线上的情况,这时候判断中心点)
- var center = math.getCenterOfGravityPoint(ring1.points);
- let inside1 = math.isPointInArea(ring2.points, null, center);
- if(!inside1){
- continue
- }
- }
-
- ring2.child.push(ring1);
- ring1.parent.push(ring2);
- }
- }
- });
- //去除非最小的ring 是否应该检测parent child?
- /*
- like this:
- |———————————————————————|
- |———|———————|———————| |
- | | | | |
- | |———————|———————| |
- |———————————————————————|
- */
- var wiseRings = rings.filter(r=>!r.isClockwise);//一般都是逆时针得到的,如果是顺时针,可能是贪吃蛇的情况,可能不是最小环,需要去掉。
- if(wiseRings.length > 0){
- //console.log('%c存在非最小的ring! 进行处理:',"color:#00f");
- wiseRings.forEach(ring=>{ //(此案例验证出smallNeibours就是它的最小构成,可以再看看别的案例)
- if(ring.smallNeibours.length>0){//另:如果内部只有一个,说明它是最小环,不需要处理
- var is = false;
- var difference = getDifferenceSet(ring.lines , getDifferenceSetMuti(ring.smallNeibours.concat(ring.child).map(ring=>ring.lines)));//获取所有smallNeibours和child的边中没有重复过的边(就是outline) 和该ring的线比较
-
-
- is = difference.every(line=> ring.child.find(r=>r.lines.includes(line)) ); //多出的线只能是child中的线
-
- if(is){
- console.log('%c删除非最小环 ring'+ring.id,"color:#00f");
- console.log(ring);
- rings.splice(rings.indexOf(ring), 1);
- ring.child.forEach(c=>{var index = c.parent.indexOf(ring);index>-1 && c.parent.splice(index,1);});
- ring.parent.forEach(c=>{var index = c.child.indexOf(ring);index>-1 && c.child.splice(index,1);});
- ring.smallNeibours.forEach(c=>{var index = c.smallNeibours.indexOf(ring);index>-1 && c.smallNeibours.splice(index,1);});
- }
-
- }
-
- });
- }
-
-
-
- /* rings = rings.filter(ring=>{
- rings = rings.filter(ring=>{
- var enoughSize = ring.area > 0.5
- if(!enoughSize){console.log('因面积过小去除ring '+ring.id + " , area: "+ring.area)}
- return enoughSize
- })
- rings.forEach(ring=>{
- if(ring.closetChilds){
- ring.closetChilds = ring.closetChilds.filter(e=>rings.includes(e))
- }
- })
-
- return rings
-
- }) */ //在dealRings前不能随意删除rings,因为判断是否是最小环时需要全部的环
-
- rings.forEach(ring=>{ //这里和cad中的不太一样, cad中双数个parent算外环,单数内环; 这里不分内外, 只看有无parent child
- if(ring.parent.length){
- ring.closetParent = ring.parent.find(ring_ => ring_.parent.length == ring.parent.length - 1);//最近一层的大环就是比它的parent个数少一的
- ring.closetParent.closetChilds || (ring.closetParent.closetChilds = []);//内环可能多个
- ring.closetParent.closetChilds.push(ring);
- }
- });
-
-
-
- //console.log(rings)
-
-
-
-
-
- var _ring = rings.map(ring=>{
- var data = {
- id: ring.id,
- points: ring.points.map(point=>{return {id: point.ids[0], x:point.x, y:point.y}}),
- /* doors : o.doors.filter(door=>{
- if(ring.closetChilds){
- var childOutLines = getDifferenceSetMuti(ring.closetChilds.map(ring=>ring.lines)) //最近子环的外边
- return ring.lines.concat(childOutLines).includes(door.atLine)
- }else{
- return ring.lines.includes(door.atLine)
- }
- }), */
- area:ring.area,
- closetParent : ring.closetParent && ring.closetParent.id,
- closetChilds : ring.closetChilds && ring.closetChilds.map(e=>e.id)
- };
-
- return data
- });
-
- //console.log(JSON.stringify(_ring))
- return _ring
-
-
-
-
-
- };
- var math = {
- getBaseLog(x, y) {//返回以 x 为底 y 的对数(即 logx y) . Math.log 返回一个数的自然对数
- return Math.log(y) / Math.log(x);
- }
- ,
- convertVector : {
- ZupToYup: function(e){//navvis -> 4dkk
- return new Vector3(e.x,e.z,-e.y)
- },
- YupToZup: function(e){//4dkk -> navvis
- return new Vector3(e.x,-e.z,e.y)
- },
-
-
- },
- convertQuaternion: {
- ZupToYup: function(e){//navvis -> 4dkk //不同于convertVisionQuaternion
- let rotation = new Euler(-Math.PI/2,0,0);
- let quaternion = new Quaternion().setFromEuler(rotation);
- return e.clone().premultiply(quaternion)
- //return new THREE.Quaternion(e.x,e.z,-e.y,e.w).multiply((new THREE.Quaternion).setFromAxisAngle(new THREE.Vector3(1,0,0), THREE.Math.degToRad(90)))
- },
- YupToZup: function(e){//4dkk -> navvis
- let rotation = new Euler(Math.PI/2,0,0);
- let quaternion = new Quaternion().setFromEuler(rotation);
- return e.clone().premultiply(quaternion)
- },
-
-
- },
-
- convertVisionQuaternion: function(e) {
- return new Quaternion(e.x,e.z,-e.y,e.w).multiply((new Quaternion).setFromAxisAngle(new Vector3(0,1,0), MathUtils.degToRad(90)))
- },
- invertVisionQuaternion : function(e) {//反转给算法部
- var a = e.clone().multiply((new Quaternion).setFromAxisAngle(new Vector3(0,1,0), MathUtils.degToRad(-90)));
- return new Quaternion(a.x,-a.z,a.y,a.w)
- },
- //------------
-
- getVec2Angle : function(dir1,dir2){
- return Math.acos( MathUtils.clamp(this.getVec2Cos(dir1,dir2), -1,1) )
- },
- getVec2Cos : function(dir1,dir2){
- return dir1.dot(dir2) / dir1.length() / dir2.length()
- },
- getAngle:function(vec1, vec2, axis){//带方向的角度 vector3
- var angle = vec1.angleTo(vec2);
- var axis_ = vec1.clone().cross(vec2);
- if(axis_[axis] < 0){
- angle *= -1;
- }
- return angle
- },
-
- closeTo : function(a,b, precision=1e-6){
- let f = (a,b)=>{
- return Math.abs(a-b) < precision;
- };
-
- if(typeof (a) == 'number'){
- return f(a, b);
- }else {
- let judge = (name)=>{
- if(a[name] == void 0)return true //有值就判断,没值就不判断
- else return f(a[name],b[name])
- };
- return judge('x') && judge('y') && judge('z') && judge('w')
- }
-
- },
-
-
- toPrecision: function (e, t) {//xzw change 保留小数
- var f = function (e, t) {
- var i = Math.pow(10, t);
- return Math.round(e * i) / i
- };
- if (e instanceof Array) {
- for (var s = 0; s < e.length; s++) {
- e[s] = f(e[s], t);
- }
- return e;
- } else if (e instanceof Object) {
- for (var s in e) {
- e[s] = f(e[s], t);
- }
- return e;
- } else return f(e, t)
- },
- isEmptyQuaternion: function(e) {
- return 0 === Math.abs(e.x) && 0 === Math.abs(e.y) && 0 === Math.abs(e.z) && 0 === Math.abs(e.w)
- },
- projectPositionToCanvas: function(e, t, i) {
- i = i || new Vector3,
- i.copy(e);
- var r = .5 * $('#player').width()
- , o = .5 * $('#player').height();
- return i.project(t),
- i.x = i.x * r + r,
- i.y = -(i.y * o) + o,
- i
- },
-
-
- handelPadResize:false,
- /* handelPadding : function () { //去除player左边和上面的宽高,因为pc的player左上有其他element 许钟文
-
- var pads = [];//记录下来避免反复计算
- var index = [];
- var resetPad = function(){
- pads = [];
- index = [];
- math.handelPadResize = false; //switchview时resized为true
- }
-
- if(config.isEdit && !config.isMobile){
- window.addEventListener('resize',resetPad);
- }
- return function(x, y, domE){
- if(!config.isEdit || config.isMobile) {
- return {
- x: x,
- y: y
- }
- }
-
- if(this.handelPadResize)resetPad();
- domE = domE || $('#player')[0];
- var pad;
- var i = index.indexOf(domE);
- if (i == -1){
- index.push(domE);
- pad = {
- x: this.getOffset("left", domE),
- y: this.getOffset("top", domE)
- }
- pads.push(pad)
- }
- else pad = pads[i];
- return {
- x: x - pad.x,
- y: y - pad.y
- }
- }
-
- }(), */
-
- getOffset: function (type, element, parent) {//获取元素的边距 许钟文
- var offset = (type == "left") ? element.offsetLeft : element.offsetTop;
- if (!parent) parent = $("body")[0];
- while (element = element.offsetParent) {
- if (element == parent) break;
- offset += (type == "left") ? element.offsetLeft : element.offsetTop;
- }
- return offset;
- }
- ,
- constrainedTurn: function(e) {
- var t = e % (2 * Math.PI);
- return t = t > Math.PI ? t -= 2 * Math.PI : t < -Math.PI ? t += 2 * Math.PI : t
- },
- getFOVDotThreshold: function(e) {
- return Math.cos(MathUtils.degToRad(e / 2))
- },
- transform2DForwardVectorByCubeFace: function(e, t, i, n) {
- switch (e) {
- case GLCubeFaces.GL_TEXTURE_CUBE_MAP_POSITIVE_X:
- i.set(1, t.y, t.x);
- break;
- case GLCubeFaces.GL_TEXTURE_CUBE_MAP_NEGATIVE_X:
- i.set(-1, t.y, -t.x);
- break;
- case GLCubeFaces.GL_TEXTURE_CUBE_MAP_POSITIVE_Y:
- i.set(-t.x, 1, -t.y);
- break;
- case GLCubeFaces.GL_TEXTURE_CUBE_MAP_NEGATIVE_Y:
- i.set(-t.x, -1, t.y);
- break;
- case GLCubeFaces.GL_TEXTURE_CUBE_MAP_POSITIVE_Z:
- i.set(-t.x, t.y, 1);
- break;
- case GLCubeFaces.GL_TEXTURE_CUBE_MAP_NEGATIVE_Z:
- i.set(t.x, t.y, -1);
- }
- n && i.normalize();
- },
-
-
-
- getFootPoint : function(oldPos, p1, p2, restricInline){ //找oldPos在线段p1, p2上的垂足
- /* if(isWorld){//输出全局坐标 需要考虑meshGroup.position
- p1 = p1.clone();
- p2 = p2.clone();
- p1.y += mainDesign.meshGroup.position.y;
- p2.y += mainDesign.meshGroup.position.y;
- } */
- if(p1.equals(p2))return p1.clone()
- var op1 = oldPos.clone().sub(p1);
- var p1p2 = p1.clone().sub(p2);
- var p1p2Len = p1p2.length();
- var leftLen = op1.dot(p1p2) / p1p2Len;
- var pos = p1.clone().add(p1p2.multiplyScalar( leftLen/p1p2Len ));
-
- if(restricInline && pos.clone().sub(p1).dot( pos.clone().sub(p2) ) > 0){//foot不在线段上
- if(pos.distanceTo(p1) < pos.distanceTo(p2)) pos = p1.clone();
- else pos = p2.clone();
- }
-
- return pos;
- },
-
-
-
-
-
- /**
- * 计算多边形的重心
- * @param {*} points
- */
- getCenterOfGravityPoint : function(mPoints){
- var area = 0.0;//多边形面积
- var Gx = 0.0, Gy = 0.0;// 重心的x、y
- for (var i = 1; i <= mPoints.length; i++) {
- var ix = mPoints[i % mPoints.length].x;
- var iy = mPoints[i % mPoints.length].y;
- var nx = mPoints[i - 1].x;
- var ny = mPoints[i - 1].y;
- var temp = (ix * ny - iy * nx) / 2.0;
- area += temp;
- Gx += temp * (ix + nx) / 3.0;
- Gy += temp * (iy + ny) / 3.0;
- }
- Gx = Gx / area;
- Gy = Gy / area;
- return { x: Gx, y: Gy };
- },
-
- getBound : function(ring){
- var bound = new Box2();
- for(var j=0,len = ring.length; j<len; j++){
- bound.expandByPoint(ring[j]);
- }
- return bound;
- },
- isPointInArea : function(ring, holes, point, ifAtLine){//判断点是否在某个环内, 若传递了holes代表还要不能在内环内
- var bound = this.getBound(ring);
- if(point.x < bound.min.x || point.x > bound.max.x || point.y < bound.min.y || point.y > bound.max.y)return false;
-
-
- var inside = false;
- var x = point.x,
- y = point.y;
- for (var i = 0, j = ring.length - 1; i < ring.length; j = i++) {
- var xi = ring[i].x,
- yi = ring[i].y;
- var xj = ring[j].x,
- yj = ring[j].y;
-
- if((xi - x)*(yj - y) == (xi - x)*(yi - y) && x>=Math.min(xi,xj) && x<=Math.max(xi,xj)//xzw add
- && y>=Math.min(yi,yj) && y<=Math.max(yi,yj)
- ){
- //return !!ifAtLine;//在线段上,则判断为…… (默认在外)
- return {atLine:true}
- }
-
- if (((yi > y) != (yj > y)) &&
- (x < (xj - xi) * (y - yi) / (yj - yi) + xi)
- ) {
- inside = !inside;
- }
-
- }
-
- if(inside && holes){
- return !holes.some(ring=>this.isPointInArea(ring, null, point, ifAtLine) ) //不能存在于任何一个二级内环内
- }else {
- return inside;
- }
-
-
- },
-
- getArea : function (ring) { //求面积 顺时针为正 来自three shape
- for (var t = ring.length, i = 0, n = t - 1, r = 0; r < t; n = r++)
- i += ring[n].x * ring[r].y - ring[r].x * ring[n].y;
- return -.5 * i
- },
- isInBetween : function(a, b, c, precision) {
- // 如果b几乎等于a或c,返回false.为了避免浮点运行时两值几乎相等,但存在相差0.00000...0001的这种情况出现使用下面方式进行避免
- /* if (Math.abs(a - b) < 0.000001 || Math.abs(b - c) < 0.000001) {
- return false;
- }
-
- return (a <= b && b <= c) || (c <= b && b <= a);*/
-
-
- //更改:如果b和a或c中一个接近 就算在a和c之间
- return (a <= b && b <= c) || (c <= b && b <= a) || this.closeTo(a,b,precision) || this.closeTo(b,c,precision);
-
- },
-
-
- ifPointAtLineBound:function(point, linePoints, precision){
- //待验证 横线和竖线比较特殊
- return math.isInBetween(linePoints[0].x, point.x, linePoints[1].x, precision) && math.isInBetween(linePoints[0].y, point.y, linePoints[1].y, precision)
- }
-
- ,
-
- isLineIntersect: function (line1, line2, notSegment, precision) {//线段和线段是否有交点. notSegment代表是直线而不是线段
- var a1 = line1[1].y - line1[0].y;
- var b1 = line1[0].x - line1[1].x;
- var c1 = a1 * line1[0].x + b1 * line1[0].y;
- //转换成一般式: Ax+By = C
- var a2 = line2[1].y - line2[0].y;
- var b2 = line2[0].x - line2[1].x;
- var c2 = a2 * line2[0].x + b2 * line2[0].y;
- // 计算交点
- var d = a1 * b2 - a2 * b1;
- // 当d==0时,两线平行
- if (d == 0) {
- return false;
- } else {
- var x = (b2 * c1 - b1 * c2) / d;
- var y = (a1 * c2 - a2 * c1) / d;
- // 检测交点是否在两条线段上
- /* if (notSegment || (isInBetween(line1[0].x, x, line1[1].x) || isInBetween(line1[0].y, y, line1[1].y)) &&
- (isInBetween(line2[0].x, x, line2[1].x) || isInBetween(line2[0].y, y, line2[1].y))) {
- return {x,y};
- } */
- if (notSegment || math.ifPointAtLineBound({x,y}, line1, precision) && math.ifPointAtLineBound({x,y}, line2, precision)){
- return {x,y};
- }
- }
- },
-
- getNormal2d : function(o={} ){//获取二维法向量 方向向内
- var x,y, x1,y1;
- //line2d的向量
- if(o.vec){
- x1 = o.vec.x; y1 = o.vec.y;
- }else {
- x1 = o.p1.x - o.p2.x;
- y1 = o.p1.y - o.p2.y;
- }
-
- //假设法向量的x或y固定为1或-1
- if(y1 != 0){
- x = 1;
- y = - (x1 * x) / y1;
- }else if(x1 != 0){//y如果为0,正常情况x不会是0
- y = 1;
- x = - (y1 * y) / x1;
- }else {
- console.log("两个点一样");
- return null;
- }
-
- //判断方向里或者外:
- var vNormal = new Vector3(x, 0, y);
- var vLine = new Vector3(x1, 0, y1);
- var vDir = vNormal.cross(vLine);
- if(vDir.y>0){
- x *= -1;
- y *= -1;
- }
- return new Vector2$1(x, y).normalize();
- },
-
- getQuaBetween2Vector:function(oriVec, newVec, upVec){ //获取从oriVec旋转到newVec可以应用的quaternion
- var angle = oriVec.angleTo(newVec);
- var axis = oriVec.clone().cross( newVec).normalize();//两个up之间
- if(axis.length() == 0){//当夹角为180 或 0 度时,得到的axis为(0,0,0),故使用备用的指定upVec
- return new Quaternion().setFromAxisAngle( upVec, angle );
- }
- return new Quaternion().setFromAxisAngle( axis, angle );
- }
- /* ,
- getQuaBetween2Vector2 : function(oriVec, newVec ){//not camera
- var _ = (new THREE.Matrix4).lookAt( oriVec, new THREE.Vector3, new THREE.Vector3(0,1,0))
- var aimQua = (new THREE.Quaternion).setFromRotationMatrix(_)
- var _2 = (new THREE.Matrix4).lookAt( newVec, new THREE.Vector3, new THREE.Vector3(0,1,0))
- var aimQua2 = (new THREE.Quaternion).setFromRotationMatrix(_2)
-
- return aimQua2.multiply(aimQua.clone().inverse())
-
- } */
-
-
- ,
-
- getScaleForConstantSize : function(){ //获得规定二维大小的mesh的scale值。可以避免因camera的projection造成的mesh视觉大小改变。 来源:tag.updateDisc
- var w;
- var i = new Vector3, o = new Vector3, l = new Vector3, c = new Vector3, h = new Vector3;
- return function(op={}){
- if(op.width2d) w = op.width2d; //如果恒定二维宽度
- else {//否则考虑上距离,加一丢丢近大远小的效果
- var currentDis, nearBound, farBound;
- if(op.camera.type == "OrthographicCamera"){
- currentDis = 200 / op.camera.zoom; //(op.camera.right - op.camera.left) / op.camera.zoom
- }else {
- currentDis = op.position.distanceTo(op.camera.position);
- }
- w = op.maxSize - ( op.maxSize - op.minSize) * MathUtils.smoothstep(currentDis, op.nearBound, op.farBound);
- //maxSize : mesh要表现的最大像素宽度; nearBound: 最近距离,若比nearBound近,则使用maxSize
- }
- i.copy(op.position).project(op.camera), //tag中心在屏幕上的二维坐标
- o.set(op.resolution.x / 2, op.resolution.y / 2, 1).multiply(i), //转化成px -w/2 到 w/2的范围
- l.set(w / 2, 0, 0).add(o), //加上tag宽度的一半
- c.set(2 / op.resolution.x, 2 / op.resolution.y, 1).multiply(l), //再转回 -1 到 1的范围
- h.copy(c).unproject(op.camera);//再转成三维坐标,求得tag边缘的位置
- var g = h.distanceTo(op.position);//就能得到tag的三维半径
- //这里使用的都是resolution2, 好处是手机端不会太小, 坏处是pc更改网页显示百分比时显示的大小会变(或许可以自己算出设备真实的deviceRatio, 因window.screen是不会改变的),但考虑到用户可以自行调节字大小也许是好的
- return g //可能NAN 当相机和position重叠时
- }
- }()
- ,
-
- //W , H, left, top分别是rect的宽、高、左、上
- getCrossPointAtRect : function(p1, aim, W , H, left, top){//求射线p1-aim在rect边界上的交点,其中aim在rect范围内,p1则不一定(交点在aim这边的延长线上)
-
- var x,y, borderX;
- var r = (aim.x - p1.x) / (aim.y - p1.y);//根据相似三角形原理先求出这个比值
- var getX = function(y){
- return r * (y-p1.y) + p1.x;
- };
- var getY = function(x){
- return 1/r * (x-p1.x) + p1.y;
- };
- if(aim.x >= p1.x){
- borderX = W+left;
- }else {
- borderX = left;
- }
- x = borderX;
- y = getY(x);
- if(y < top || y > top+H){
- if(y < top){
- y = top;
- }else {
- y = top+H;
- }
- x = getX(y);
- }
- return new Vector2$1(x, y);
- },
- getDirFromUV : function(uv){ //获取dir 反向计算 - - 二维转三维比较麻烦
- var dirB; //所求 单位向量
-
-
-
- var y = Math.cos(uv.y * Math.PI); //uv中纵向可以直接确定y, 根据上面getUVfromDir的反向计算
- // 故 uv.y * Math.PI 就是到垂直线(向上)的夹角
- var angle = 2 * Math.PI * uv.x - Math.PI; //x/z代表的是角度
- var axisX, axisZ; //axis为1代表是正,-1是负数
- if (-Math.PI <= angle && angle < 0) {
- axisX = -1; //下半圆
- } else {
- axisX = 1; //上半圆
- }
- if (-Math.PI / 2 <= angle && angle < Math.PI / 2) {
- axisZ = 1; //右半圆
- } else {
- axisZ = -1; //左半圆
- }
- var XDivideZ = Math.tan(angle);
- var z = Math.sqrt((1 - y * y) / (1 + XDivideZ * XDivideZ));
- var x = XDivideZ * z;
- if (z * axisZ < 0) { //异号
- z *= -1;
- x *= -1;
- if (x * axisX < 0) {
- // console.log("wrong!!!!!??????????")
- }
- }
- x *= -1; //计算完成后这里不能漏掉 *= -1
- dirB = this.convertVector.YupToZup(new Vector3(x, y, z));
- //理想状态下x和z和anotherDir相同
- return dirB
- },
-
- getUVfromDir : function(dir) { //获取UV 同shader里的计算
- var dir = this.convertVector.ZupToYup(dir);
- dir.x *= -1; //计算前这里不能漏掉 *= -1 见shader
- var tx = Math.atan2(dir.x, dir.z) / (Math.PI * 2.0) + 0.5; //atan2(y,x) 返回从 X 轴正向逆时针旋转到点 (x,y) 时经过的角度。区间是-PI 到 PI 之间的值
- var ty = Math.acos(dir.y) / Math.PI;
- return new Vector2$1(tx, ty)
- //理想状态下tx相同
- },
-
- getDirByLonLat : function(lon,lat){
- var dir = new Vector3;
- var phi = MathUtils.degToRad(90 - lat);
- var theta = MathUtils.degToRad(lon);
- dir.x = Math.sin(phi) * Math.cos(theta);
- dir.y = Math.cos(phi);
- dir.z = Math.sin(phi) * Math.sin(theta);
- return dir
- } //0,0 => (1,0,0) 270=>(0,0,-1)
- ,
- projectPointAtPlane:function(o={}){//获取一个点在一个面上的投影 {facePoints:[a,b,c], point:}
- var plane = new Plane().setFromCoplanarPoints(...o.facePoints);
- return plane.projectPoint(o.point, new Vector3() )
- }
- ,
-
- getPolygonsMixedRings:function( polygons, onlyGetOutRing){//{points:[vector2,...],holes:[[],[]]}
-
-
- let points = [];
- let lines = [];
- let i = 0;
-
- polygons.forEach(e=> points.push(...e.map(a=>new Vector2$1().copy(a) )) );
- polygons.forEach((ps,j)=>{
- let length = ps.length;
- let index = 0;
- while(index<length){
- lines.push({p1:index+i,p2:(index+1)%length+i});
- index ++;
- }
- i+=length;
- });
-
-
- points.forEach((p,j)=>{p.id = j;});
-
- let rings = searchRings({
- points,
- lines,
- onlyGetOutRing
- });
- //console.log(rings)
-
- rings = rings.filter(e=>e.closetParent == void 0);// 子环不加,被外环包含了
-
- return rings
-
- },
- getQuaFromPosAim( position, target ){
- let matrix = (new Matrix4).lookAt(position, target, new Vector3(0,0,1));
- return (new Quaternion).setFromRotationMatrix(matrix)
-
- },
-
-
- getBoundByPoints(points, minSize){
- var bound = new Box3;
- points.forEach(point=>{
- bound.expandByPoint(point);
- });
- let center = bound.getCenter(new Vector3);
- if(minSize){
- let minBound = (new Box3()).setFromCenterAndSize(center, minSize);
- bound.union(minBound);
- }
- return {
- bounding:bound,
- size: bound.getSize(new Vector3),
- center,
- }
- },
- };
-
- Potree.math = math;
- !function() {
- if ("performance"in window == 0 && (window.performance = {}),
- "now"in window.performance == 0) {
- var e = Date.now();
- performance.timing && performance.timing.navigationStart && (e = performance.timing.navigationStart),
- window.performance.now = function() {
- return Date.now() - e
- };
- }
- }(),
- WebGLRenderer.prototype.paramThreeToGL = function(e) {
- var t, i = this.extensions, r = this.getContext();//context;
- if (e === RepeatWrapping)
- return r.REPEAT;
- if (e === ClampToEdgeWrapping)
- return r.CLAMP_TO_EDGE;
- if (e === MirroredRepeatWrapping)
- return r.MIRRORED_REPEAT;
- if (e === NearestFilter)
- return r.NEAREST;
- if (e === NearestMipMapNearestFilter)
- return r.NEAREST_MIPMAP_NEAREST;
- if (e === NearestMipMapLinearFilter)
- return r.NEAREST_MIPMAP_LINEAR;
- if (e === LinearFilter)
- return r.LINEAR;
- if (e === LinearMipMapNearestFilter)
- return r.LINEAR_MIPMAP_NEAREST;
- if (e === LinearMipMapLinearFilter)
- return r.LINEAR_MIPMAP_LINEAR;
- if (e === UnsignedByteType)
- return r.UNSIGNED_BYTE;
- if (e === UnsignedShort4444Type)
- return r.UNSIGNED_SHORT_4_4_4_4;
- if (e === UnsignedShort5551Type)
- return r.UNSIGNED_SHORT_5_5_5_1;
- if (e === UnsignedShort565Type)
- return r.UNSIGNED_SHORT_5_6_5;
- if (e === ByteType)
- return r.BYTE;
- if (e === ShortType)
- return r.SHORT;
- if (e === UnsignedShortType)
- return r.UNSIGNED_SHORT;
- if (e === IntType)
- return r.INT;
- if (e === UnsignedIntType)
- return r.UNSIGNED_INT;
- if (e === FloatType)
- return r.FLOAT;
- if (t = i.get("OES_texture_half_float"),
- null !== t && e === HalfFloatType)
- return t.HALF_FLOAT_OES;
- if (e === AlphaFormat)
- return r.ALPHA;
- if (e === RGBFormat)
- return r.RGB;
- if (e === RGBAFormat)
- return r.RGBA;
- if (e === LuminanceFormat)
- return r.LUMINANCE;
- if (e === LuminanceAlphaFormat)
- return r.LUMINANCE_ALPHA;
- if (e === AddEquation)
- return r.FUNC_ADD;
- if (e === SubtractEquation)
- return r.FUNC_SUBTRACT;
- if (e === ReverseSubtractEquation)
- return r.FUNC_REVERSE_SUBTRACT;
- if (e === ZeroFactor)
- return r.ZERO;
- if (e === OneFactor)
- return r.ONE;
- if (e === SrcColorFactor)
- return r.SRC_COLOR;
- if (e === OneMinusSrcColorFactor)
- return r.ONE_MINUS_SRC_COLOR;
- if (e === SrcAlphaFactor)
- return r.SRC_ALPHA;
- if (e === OneMinusSrcAlphaFactor)
- return r.ONE_MINUS_SRC_ALPHA;
- if (e === DstAlphaFactor)
- return r.DST_ALPHA;
- if (e === OneMinusDstAlphaFactor)
- return r.ONE_MINUS_DST_ALPHA;
- if (e === DstColorFactor)
- return r.DST_COLOR;
- if (e === OneMinusDstColorFactor)
- return r.ONE_MINUS_DST_COLOR;
- if (e === SrcAlphaSaturateFactor)
- return r.SRC_ALPHA_SATURATE;
- if (t = i.get("WEBGL_compressed_texture_s3tc"),
- null !== t) {
- if (e === RGB_S3TC_DXT1_Format)
- return t.COMPRESSED_RGB_S3TC_DXT1_EXT;
- if (e === RGBA_S3TC_DXT1_Format$1)
- return t.COMPRESSED_RGBA_S3TC_DXT1_EXT;
- if (e === RGBA_S3TC_DXT3_Format)
- return t.COMPRESSED_RGBA_S3TC_DXT3_EXT;
- if (e === RGBA_S3TC_DXT5_Format$1)
- return t.COMPRESSED_RGBA_S3TC_DXT5_EXT
- }
- if (t = i.get("WEBGL_compressed_texture_pvrtc"),
- null !== t) {
- if (e === RGB_PVRTC_4BPPV1_Format)
- return t.COMPRESSED_RGB_PVRTC_4BPPV1_IMG;
- if (e === RGB_PVRTC_2BPPV1_Format)
- return t.COMPRESSED_RGB_PVRTC_2BPPV1_IMG;
- if (e === RGBA_PVRTC_4BPPV1_Format)
- return t.COMPRESSED_RGBA_PVRTC_4BPPV1_IMG;
- if (e === RGBA_PVRTC_2BPPV1_Format)
- return t.COMPRESSED_RGBA_PVRTC_2BPPV1_IMG
- }
- if (t = i.get("WEBGL_compressed_texture_etc1"),
- null !== t && e === RGB_ETC1_Format)
- return t.COMPRESSED_RGB_ETC1_WEBGL;
- if (t = i.get("EXT_blend_minmax"),
- null !== t) {
- if (e === MinEquation)
- return t.MIN_EXT;
- if (e === MaxEquation)
- return t.MAX_EXT
- }
- return 0
- };
- /* ,
- THREE.WebGLState = function(e, t, i) {
- var r = this
- , o = new THREE.Vector4
- , a = e.getParameter(e.MAX_VERTEX_ATTRIBS)
- , s = new Uint8Array(a)
- , l = new Uint8Array(a)
- , c = new Uint8Array(a)
- , h = {}
- , u = null
- , d = null
- , p = null
- , f = null
- , g = null
- , m = null
- , v = null
- , A = null
- , y = !1
- , C = null
- , I = null
- , E = null
- , b = null
- , w = null
- , _ = null
- , T = null
- , x = null
- , S = null
- , M = null
- , R = null
- , P = null
- , O = null
- , L = null
- , D = null
- , N = e.getParameter(e.MAX_TEXTURE_IMAGE_UNITS)
- , B = void 0
- , F = {}
- , V = new THREE.Vector4
- , U = null
- , k = null
- , H = new THREE.Vector4
- , G = new THREE.Vector4;
- this.init = function() {
- this.clearColor(0, 0, 0, 1),
- this.clearDepth(1),
- this.clearStencil(0),
- this.enable(e.DEPTH_TEST),
- e.depthFunc(e.LEQUAL),
- e.frontFace(e.CCW),
- e.cullFace(e.BACK),
- this.enable(e.CULL_FACE),
- this.enable(e.BLEND),
- e.blendEquation(e.FUNC_ADD),
- e.blendFunc(e.SRC_ALPHA, e.ONE_MINUS_SRC_ALPHA)
- }
- ,
- this.initAttributes = function() {
- for (var e = 0, t = s.length; e < t; e++)
- s[e] = 0
- }
- ,
- this.enableAttribute = function(i) {
- if (s[i] = 1,
- 0 === l[i] && (e.enableVertexAttribArray(i),
- l[i] = 1),
- 0 !== c[i]) {
- var n = t.get("ANGLE_instanced_arrays");
- n.vertexAttribDivisorANGLE(i, 0),
- c[i] = 0
- }
- }
- ,
- this.enableAttributeAndDivisor = function(t, i, n) {
- s[t] = 1,
- 0 === l[t] && (e.enableVertexAttribArray(t),
- l[t] = 1),
- c[t] !== i && (n.vertexAttribDivisorANGLE(t, i),
- c[t] = i)
- }
- ,
- this.disableUnusedAttributes = function() {
- for (var t = 0, i = l.length; t < i; t++)
- l[t] !== s[t] && (e.disableVertexAttribArray(t),
- l[t] = 0)
- }
- ,
- this.enable = function(t) {
- h[t] !== !0 && (e.enable(t),
- h[t] = !0)
- }
- ,
- this.disable = function(t) {
- h[t] !== !1 && (e.disable(t),
- h[t] = !1)
- }
- ,
- this.getCompressedTextureFormats = function() {
- if (null === u && (u = [],
- t.get("WEBGL_compressed_texture_pvrtc") || t.get("WEBGL_compressed_texture_s3tc") || t.get("WEBGL_compressed_texture_etc1")))
- for (var i = e.getParameter(e.COMPRESSED_TEXTURE_FORMATS), n = 0; n < i.length; n++)
- u.push(i[n]);
- return u
- }
- ,
- this.setBlending = function(t, r, o, a, s, l, c, h) {
- t === THREE.NoBlending ? this.disable(e.BLEND) : this.enable(e.BLEND),
- t === d && h === y || (t === THREE.AdditiveBlending ? h ? (e.blendEquationSeparate(e.FUNC_ADD, e.FUNC_ADD),
- e.blendFuncSeparate(e.ONE, e.ONE, e.ONE, e.ONE)) : (e.blendEquation(e.FUNC_ADD),
- e.blendFunc(e.SRC_ALPHA, e.ONE)) : t === THREE.SubtractiveBlending ? h ? (e.blendEquationSeparate(e.FUNC_ADD, e.FUNC_ADD),
- e.blendFuncSeparate(e.ZERO, e.ZERO, e.ONE_MINUS_SRC_COLOR, e.ONE_MINUS_SRC_ALPHA)) : (e.blendEquation(e.FUNC_ADD),
- e.blendFunc(e.ZERO, e.ONE_MINUS_SRC_COLOR)) : t === THREE.MultiplyBlending ? h ? (e.blendEquationSeparate(e.FUNC_ADD, e.FUNC_ADD),
- e.blendFuncSeparate(e.ZERO, e.ZERO, e.SRC_COLOR, e.SRC_ALPHA)) : (e.blendEquation(e.FUNC_ADD),
- e.blendFunc(e.ZERO, e.SRC_COLOR)) : h ? (e.blendEquationSeparate(e.FUNC_ADD, e.FUNC_ADD),
- e.blendFuncSeparate(e.ONE, e.ONE_MINUS_SRC_ALPHA, e.ONE, e.ONE_MINUS_SRC_ALPHA)) : (e.blendEquationSeparate(e.FUNC_ADD, e.FUNC_ADD),
- e.blendFuncSeparate(e.SRC_ALPHA, e.ONE_MINUS_SRC_ALPHA, e.ONE, e.ONE_MINUS_SRC_ALPHA)),
- d = t,
- y = h),
- t === THREE.CustomBlending ? (s = s || r,
- l = l || o,
- c = c || a,
- r === p && s === m || (e.blendEquationSeparate(i(r), i(s)),
- p = r,
- m = s),
- o === f && a === g && l === v && c === A || (e.blendFuncSeparate(i(o), i(a), i(l), i(c)),
- f = o,
- g = a,
- v = l,
- A = c)) : (p = null,
- f = null,
- g = null,
- m = null,
- v = null,
- A = null)
- }
- ,
- this.setDepthFunc = function(t) {
- if (C !== t) {
- if (t)
- switch (t) {
- case THREE.NeverDepth:
- e.depthFunc(e.NEVER);
- break;
- case THREE.AlwaysDepth:
- e.depthFunc(e.ALWAYS);
- break;
- case THREE.LessDepth:
- e.depthFunc(e.LESS);
- break;
- case THREE.LessEqualDepth:
- e.depthFunc(e.LEQUAL);
- break;
- case THREE.EqualDepth:
- e.depthFunc(e.EQUAL);
- break;
- case THREE.GreaterEqualDepth:
- e.depthFunc(e.GEQUAL);
- break;
- case THREE.GreaterDepth:
- e.depthFunc(e.GREATER);
- break;
- case THREE.NotEqualDepth:
- e.depthFunc(e.NOTEQUAL);
- break;
- default:
- e.depthFunc(e.LEQUAL)
- }
- else
- e.depthFunc(e.LEQUAL);
- C = t
- }
- }
- ,
- this.setDepthTest = function(t) {
- t ? this.enable(e.DEPTH_TEST) : this.disable(e.DEPTH_TEST)
- }
- ,
- this.setDepthWrite = function(t) {
- I !== t && (e.depthMask(t),
- I = t)
- }
- ,
- this.setColorWrite = function(t) {
- E !== t && (e.colorMask(t, t, t, t),
- E = t)
- }
- ,
- this.setStencilFunc = function(t, i, n) {
- w === t && _ === i && T === n || (e.stencilFunc(t, i, n),
- w = t,
- _ = i,
- T = n)
- }
- ,
- this.setStencilOp = function(t, i, n) {
- x === t && S === i && M === n || (e.stencilOp(t, i, n),
- x = t,
- S = i,
- M = n)
- }
- ,
- this.setStencilTest = function(t) {
- t ? this.enable(e.STENCIL_TEST) : this.disable(e.STENCIL_TEST)
- }
- ,
- this.setStencilWrite = function(t) {
- b !== t && (e.stencilMask(t),
- b = t)
- }
- ,
- this.setFlipSided = function(t) {
- R !== t && (t ? e.frontFace(e.CW) : e.frontFace(e.CCW),
- R = t)
- }
- ,
- this.setLineWidth = function(t) {
- t !== P && (e.lineWidth(t),
- P = t)
- }
- ,
- this.setPolygonOffset = function(t, i, n) {
- t ? this.enable(e.POLYGON_OFFSET_FILL) : this.disable(e.POLYGON_OFFSET_FILL),
- !t || O === i && L === n || (e.polygonOffset(i, n),
- O = i,
- L = n)
- }
- ,
- this.getScissorTest = function() {
- return D
- }
- ,
- this.setScissorTest = function(t) {
- D = t,
- t ? this.enable(e.SCISSOR_TEST) : this.disable(e.SCISSOR_TEST)
- }
- ,
- this.activeTexture = function(t) {
- void 0 === t && (t = e.TEXTURE0 + N - 1),
- B !== t && (e.activeTexture(t),
- B = t)
- }
- ,
- this.bindTexture = function(t, i) {
- void 0 === B && r.activeTexture();
- var n = F[B];
- void 0 === n && (n = {
- type: void 0,
- texture: void 0
- },
- F[B] = n),
- n.type === t && n.texture === i || (e.bindTexture(t, i),
- n.type = t,
- n.texture = i)
- }
- ,
- this.compressedTexImage2D = function() {
- try {
- e.compressedTexImage2D.apply(e, arguments)
- } catch (e) {
- console.error(e)
- }
- }
- ,
- this.texImage2D = function() {
- try {
- e.texImage2D.apply(e, arguments)
- } catch (e) {
- console.error(e)
- }
- }
- ,
- this.clearColor = function(t, i, n, r) {
- o.set(t, i, n, r),
- V.equals(o) === !1 && (e.clearColor(t, i, n, r),
- V.copy(o))
- }
- ,
- this.clearDepth = function(t) {
- U !== t && (e.clearDepth(t),
- U = t)
- }
- ,
- this.clearStencil = function(t) {
- k !== t && (e.clearStencil(t),
- k = t)
- }
- ,
- this.scissor = function(t) {
- H.equals(t) === !1 && (e.scissor(t.x, t.y, t.z, t.w),
- H.copy(t))
- }
- ,
- this.viewport = function(t) {
- G.equals(t) === !1 && (e.viewport(t.x, t.y, t.z, t.w),
- G.copy(t))
- }
- ,
- this.reset = function() {
- for (var t = 0; t < l.length; t++)
- 1 === l[t] && (e.disableVertexAttribArray(t),
- l[t] = 0);
- h = {},
- u = null,
- B = void 0,
- F = {},
- d = null,
- E = null,
- I = null,
- b = null,
- R = null
- }
- } */
- const XHRFactory = {
- config: {
- withCredentials: false,
- customHeaders: [
- { header: null, value: null }
- ]
- },
- createXMLHttpRequest: function () {
- let xhr = new XMLHttpRequest();
- if (this.config.customHeaders &&
- Array.isArray(this.config.customHeaders) &&
- this.config.customHeaders.length > 0) {
- let baseOpen = xhr.open;
- let customHeaders = this.config.customHeaders;
- xhr.open = function () {
- baseOpen.apply(this, [].slice.call(arguments));
- customHeaders.forEach(function (customHeader) {
- if (!!customHeader.header && !!customHeader.value) {
- xhr.setRequestHeader(customHeader.header, customHeader.value);
- }
- });
- };
- }
- return xhr;
- }
- };
- let Shaders = {};
- Shaders["pointcloud.vs"] = `
- precision highp float;
- precision highp int;
- #define max_clip_polygons 8
- #define PI 3.141592653589793
-
- #if defined(usePanoMap)
-
- uniform samplerCube pano0Map; //随便设置一个samplerCube去使用都会让点云消失
- uniform samplerCube pano1Map;
-
- uniform float progress;
- uniform float easeInOutRatio;
-
- uniform vec3 pano0Position;
- uniform mat4 pano0Matrix;
- uniform vec3 pano1Position;
- uniform mat4 pano1Matrix;
- /*
- varying vec3 vWorldPosition0;
- varying vec3 vWorldPosition1;
- */
- #endif
-
- //--------------
- attribute vec3 position;
- attribute vec3 color;
- attribute float intensity;
- attribute float classification;
- attribute float returnNumber;
- attribute float numberOfReturns;
- attribute float pointSourceID;
- attribute vec4 indices; //每个点的index
- attribute float spacing;
- attribute float gpsTime;
- attribute vec3 normal;
- attribute float aExtra;
- uniform mat4 modelMatrix;
- uniform mat4 modelViewMatrix;
- uniform mat4 projectionMatrix;
- uniform mat4 viewMatrix;
- uniform mat4 uViewInv;
- //uniform float uScreenWidth;
- //uniform float uScreenHeight;
- uniform vec2 resolution;
- uniform float fov;
- uniform float near;
- uniform float far;
- uniform bool uDebug;
- uniform bool uUseOrthographicCamera;
- uniform float uOrthoWidth;
- uniform float uOrthoHeight;
- #define CLIPTASK_NONE 0
- #define CLIPTASK_HIGHLIGHT 1
- #define CLIPTASK_SHOW_INSIDE 2
- #define CLIPTASK_SHOW_OUTSIDE 3
- #define CLIPMETHOD_INSIDE_ANY 0
- #define CLIPMETHOD_INSIDE_ALL 1
- uniform int clipTask;
- uniform int clipMethod;
- #if defined(num_clipboxes) && num_clipboxes > 0
- uniform mat4 clipBoxes[num_clipboxes];
- #endif
- #if defined(num_clipspheres) && num_clipspheres > 0
- uniform mat4 uClipSpheres[num_clipspheres];
- #endif
- #if defined(num_clippolygons) && num_clippolygons > 0
- uniform int uClipPolygonVCount[num_clippolygons];
- uniform vec3 uClipPolygonVertices[num_clippolygons * 8];
- uniform mat4 uClipPolygonWVP[num_clippolygons];
- #endif
- uniform float size;
- uniform float minSize;
- uniform float maxSize;
- uniform float uPCIndex;
- uniform float uOctreeSpacing;
- uniform float uNodeSpacing;
- uniform float uOctreeSize;
- uniform vec3 uBBSize;
- uniform float uLevel;
- uniform float uVNStart;
- uniform bool uIsLeafNode;
- uniform vec3 uColor;
- uniform float uOpacity;
- varying float vOpacity; //add
- uniform vec2 elevationRange;
- uniform vec2 intensityRange;
- uniform vec2 uFilterReturnNumberRange;
- uniform vec2 uFilterNumberOfReturnsRange;
- uniform vec2 uFilterPointSourceIDClipRange;
- uniform vec2 uFilterGPSTimeClipRange;
- //uniform float ufilterByNormalThreshold;
- uniform float uGpsScale;
- uniform float uGpsOffset;
- uniform vec2 uNormalizedGpsBufferRange;
- uniform vec3 uIntensity_gbc;
- uniform vec3 uRGB_gbc;
- uniform vec3 uExtra_gbc;
- uniform float uTransition;
- uniform float wRGB;
- uniform float wIntensity;
- uniform float wElevation;
- uniform float wClassification;
- uniform float wReturnNumber;
- uniform float wSourceID;
- uniform vec2 uExtraNormalizedRange;
- uniform vec2 uExtraRange;
- uniform float uExtraScale;
- uniform float uExtraOffset;
- uniform vec3 uShadowColor;
- uniform sampler2D visibleNodes;
- uniform sampler2D gradient;
- uniform sampler2D classificationLUT;
- #if defined(color_type_matcap)
- uniform sampler2D matcapTextureUniform;
- #endif
- uniform bool backfaceCulling;
- #if defined(num_shadowmaps) && num_shadowmaps > 0
- uniform sampler2D uShadowMap[num_shadowmaps];
- uniform mat4 uShadowWorldView[num_shadowmaps];
- uniform mat4 uShadowProj[num_shadowmaps];
- #endif
- varying vec3 vColor;
- varying float vLogDepth;
- varying vec3 vViewPosition;
- varying float vRadius;
- varying float vPointSize;
- float round(float number){
- return floor(number + 0.5);
- }
- //
- // ### ######## ### ######## ######## #### ## ## ######## ###### #### ######## ######## ######
- // ## ## ## ## ## ## ## ## ## ## ## ## ## ## ## ## ## ## ## ##
- // ## ## ## ## ## ## ## ## ## ## ## ## ## ## ## ## ## ##
- // ## ## ## ## ## ## ######## ## ## ## ## ###### ###### ## ## ###### ######
- // ######### ## ## ######### ## ## ## ## ## ## ## ## ## ## ##
- // ## ## ## ## ## ## ## ## ## ## ## ## ## ## ## ## ## ## ##
- // ## ## ######## ## ## ## ## #### ### ######## ###### #### ######## ######## ######
- //
- // ---------------------
- // OCTREE
- // ---------------------
- #if (defined(adaptive_point_size) || defined(color_type_level_of_detail)) && defined(tree_type_octree)
- /**
- * number of 1-bits up to inclusive index position
- * number is treated as if it were an integer in the range 0-255
- *
- */
- int numberOfOnes(int number, int index){
- int numOnes = 0;
- int tmp = 128;
- for(int i = 7; i >= 0; i--){
-
- if(number >= tmp){
- number = number - tmp;
- if(i <= index){
- numOnes++;
- }
- }
-
- tmp = tmp / 2;
- }
- return numOnes;
- }
- /**
- * checks whether the bit at index is 1
- * number is treated as if it were an integer in the range 0-255
- *
- */
- bool isBitSet(int number, int index){
- // weird multi else if due to lack of proper array, int and bitwise support in WebGL 1.0
- int powi = 1;
- if(index == 0){
- powi = 1;
- }else if(index == 1){
- powi = 2;
- }else if(index == 2){
- powi = 4;
- }else if(index == 3){
- powi = 8;
- }else if(index == 4){
- powi = 16;
- }else if(index == 5){
- powi = 32;
- }else if(index == 6){
- powi = 64;
- }else if(index == 7){
- powi = 128;
- }else{
- return false;
- }
- int ndp = number / powi;
- return mod(float(ndp), 2.0) != 0.0;
- }
- /**
- * find the LOD at the point position
- */
- float getLOD(){//////
-
- vec3 offset = vec3(0.0, 0.0, 0.0);
- int iOffset = int(uVNStart);
- float depth = uLevel;
- for(float i = 0.0; i <= 30.0; i++){
- float nodeSizeAtLevel = uOctreeSize / pow(2.0, i + uLevel + 0.0);
-
- vec3 index3d = (position-offset) / nodeSizeAtLevel;
- index3d = floor(index3d + 0.5);
- int index = int(round(4.0 * index3d.x + 2.0 * index3d.y + index3d.z));
-
- vec4 value = texture2D(visibleNodes, vec2(float(iOffset) / 2048.0, 0.0));
- int mask = int(round(value.r * 255.0));
- if(isBitSet(mask, index)){
- // there are more visible child nodes at this position
- int advanceG = int(round(value.g * 255.0)) * 256;
- int advanceB = int(round(value.b * 255.0));
- int advanceChild = numberOfOnes(mask, index - 1);
- int advance = advanceG + advanceB + advanceChild;
- iOffset = iOffset + advance;
-
- depth++;
- }else{
- // no more visible child nodes at this position
- //return value.a * 255.0;
- float lodOffset = (255.0 * value.a) / 10.0 - 10.0;
- return depth + lodOffset;
- }
-
- offset = offset + (vec3(1.0, 1.0, 1.0) * nodeSizeAtLevel * 0.5) * index3d;
- }
-
- return depth;
- }
- float getSpacing(){
- vec3 offset = vec3(0.0, 0.0, 0.0);
- int iOffset = int(uVNStart);
- float depth = uLevel;
- float spacing = uNodeSpacing;
- for(float i = 0.0; i <= 30.0; i++){
- float nodeSizeAtLevel = uOctreeSize / pow(2.0, i + uLevel + 0.0);
-
- vec3 index3d = (position-offset) / nodeSizeAtLevel;
- index3d = floor(index3d + 0.5);
- int index = int(round(4.0 * index3d.x + 2.0 * index3d.y + index3d.z));
-
- vec4 value = texture2D(visibleNodes, vec2(float(iOffset) / 2048.0, 0.0));
- int mask = int(round(value.r * 255.0));
- float spacingFactor = value.a;
- if(i > 0.0){
- spacing = spacing / (255.0 * spacingFactor);
- }
-
- if(isBitSet(mask, index)){
- // there are more visible child nodes at this position
- int advanceG = int(round(value.g * 255.0)) * 256;
- int advanceB = int(round(value.b * 255.0));
- int advanceChild = numberOfOnes(mask, index - 1);
- int advance = advanceG + advanceB + advanceChild;
- iOffset = iOffset + advance;
- //spacing = spacing / (255.0 * spacingFactor);
- //spacing = spacing / 3.0;
-
- depth++;
- }else{
- // no more visible child nodes at this position
- return spacing;
- }
-
- offset = offset + (vec3(1.0, 1.0, 1.0) * nodeSizeAtLevel * 0.5) * index3d;
- }
-
- return spacing;
- }
- float getPointSizeAttenuation(){
- return pow(2.0, getLOD());
- }
- #endif
- // ---------------------
- // KD-TREE
- // ---------------------
- #if (defined(adaptive_point_size) || defined(color_type_level_of_detail)) && defined(tree_type_kdtree)
- float getLOD(){
- vec3 offset = vec3(0.0, 0.0, 0.0);
- float iOffset = 0.0;
- float depth = 0.0;
-
-
- vec3 size = uBBSize;
- vec3 pos = position;
-
- for(float i = 0.0; i <= 1000.0; i++){
-
- vec4 value = texture2D(visibleNodes, vec2(iOffset / 2048.0, 0.0));
-
- int children = int(value.r * 255.0);
- float next = value.g * 255.0;
- int split = int(value.b * 255.0);
-
- if(next == 0.0){
- return depth;
- }
-
- vec3 splitv = vec3(0.0, 0.0, 0.0);
- if(split == 1){
- splitv.x = 1.0;
- }else if(split == 2){
- splitv.y = 1.0;
- }else if(split == 4){
- splitv.z = 1.0;
- }
-
- iOffset = iOffset + next;
-
- float factor = length(pos * splitv / size);
- if(factor < 0.5){
- // left
- if(children == 0 || children == 2){
- return depth;
- }
- }else{
- // right
- pos = pos - size * splitv * 0.5;
- if(children == 0 || children == 1){
- return depth;
- }
- if(children == 3){
- iOffset = iOffset + 1.0;
- }
- }
- size = size * ((1.0 - (splitv + 1.0) / 2.0) + 0.5);
-
- depth++;
- }
-
-
- return depth;
- }
- float getPointSizeAttenuation(){
- return 0.5 * pow(1.3, getLOD());
- }
- #endif
- //
- // ### ######## ######## ######## #### ######## ## ## ######## ######## ######
- // ## ## ## ## ## ## ## ## ## ## ## ## ## ## ##
- // ## ## ## ## ## ## ## ## ## ## ## ## ## ##
- // ## ## ## ## ######## ## ######## ## ## ## ###### ######
- // ######### ## ## ## ## ## ## ## ## ## ## ## ##
- // ## ## ## ## ## ## ## ## ## ## ## ## ## ## ##
- // ## ## ## ## ## ## #### ######## ####### ## ######## ######
- //
- // formula adapted from: http://www.dfstudios.co.uk/articles/programming/image-programming-algorithms/image-processing-algorithms-part-5-contrast-adjustment/
- float getContrastFactor(float contrast){
- return (1.0158730158730156 * (contrast + 1.0)) / (1.0158730158730156 - contrast);
- }
- vec3 getRGB(){
- vec3 rgb = color;
-
- rgb = pow(rgb, vec3(uRGB_gbc.x));
- rgb = rgb + uRGB_gbc.y;
- rgb = (rgb - 0.5) * getContrastFactor(uRGB_gbc.z) + 0.5;
- rgb = clamp(rgb, 0.0, 1.0);
- return rgb;
- }
- float getIntensity(){
- float w = (intensity - intensityRange.x) / (intensityRange.y - intensityRange.x);
- w = pow(w, uIntensity_gbc.x);
- w = w + uIntensity_gbc.y;
- w = (w - 0.5) * getContrastFactor(uIntensity_gbc.z) + 0.5;
- w = clamp(w, 0.0, 1.0);
- return w;
- }
- vec3 getGpsTime(){
- float w = (gpsTime + uGpsOffset) * uGpsScale;
- vec3 c = texture2D(gradient, vec2(w, 1.0 - w)).rgb;
- // vec2 r = uNormalizedGpsBufferRange;
- // float w = gpsTime * (r.y - r.x) + r.x;
- // w = clamp(w, 0.0, 1.0);
- // vec3 c = texture2D(gradient, vec2(w,1.0-w)).rgb;
-
- return c;
- }
- vec3 getElevation(){
- vec4 world = modelMatrix * vec4( position, 1.0 );
- float w = (world.z - elevationRange.x) / (elevationRange.y - elevationRange.x);
- vec3 cElevation = texture2D(gradient, vec2(w,1.0-w)).rgb;
-
- return cElevation;
- }
- vec4 getClassification(){
- vec2 uv = vec2(classification / 255.0, 0.5);
- vec4 classColor = texture2D(classificationLUT, uv);
-
- return classColor;
- }
- vec3 getReturns(){
- // 0b 00_000_111
- float rn = mod(returnNumber, 8.0);
- // 0b 00_111_000
- float nr = mod(returnNumber / 8.0, 8.0);
- if(nr <= 1.0){
- return vec3(1.0, 0.0, 0.0);
- }else{
- return vec3(0.0, 1.0, 0.0);
- }
- // return vec3(nr / 4.0, 0.0, 0.0);
- // if(nr == 1.0){
- // return vec3(1.0, 1.0, 0.0);
- // }else{
- // if(rn == 1.0){
- // return vec3(1.0, 0.0, 0.0);
- // }else if(rn == nr){
- // return vec3(0.0, 0.0, 1.0);
- // }else{
- // return vec3(0.0, 1.0, 0.0);
- // }
- // }
- // if(numberOfReturns == 1.0){
- // return vec3(1.0, 1.0, 0.0);
- // }else{
- // if(returnNumber == 1.0){
- // return vec3(1.0, 0.0, 0.0);
- // }else if(returnNumber == numberOfReturns){
- // return vec3(0.0, 0.0, 1.0);
- // }else{
- // return vec3(0.0, 1.0, 0.0);
- // }
- // }
- }
- vec3 getReturnNumber(){
- if(numberOfReturns == 1.0){
- return vec3(1.0, 1.0, 0.0);
- }else{
- if(returnNumber == 1.0){
- return vec3(1.0, 0.0, 0.0);
- }else if(returnNumber == numberOfReturns){
- return vec3(0.0, 0.0, 1.0);
- }else{
- return vec3(0.0, 1.0, 0.0);
- }
- }
- }
- vec3 getNumberOfReturns(){
- float value = numberOfReturns;
- float w = value / 6.0;
- vec3 color = texture2D(gradient, vec2(w, 1.0 - w)).rgb;
- return color;
- }
- vec3 getSourceID(){
- float w = mod(pointSourceID, 10.0) / 10.0;
- return texture2D(gradient, vec2(w,1.0 - w)).rgb;
- }
- vec3 getCompositeColor(){
- vec3 c;
- float w;
- c += wRGB * getRGB();
- w += wRGB;
-
- c += wIntensity * getIntensity() * vec3(1.0, 1.0, 1.0);
- w += wIntensity;
-
- c += wElevation * getElevation();
- w += wElevation;
-
- c += wReturnNumber * getReturnNumber();
- w += wReturnNumber;
-
- c += wSourceID * getSourceID();
- w += wSourceID;
-
- vec4 cl = wClassification * getClassification();
- c += cl.a * cl.rgb;
- w += wClassification * cl.a;
- c = c / w;
-
- if(w == 0.0){
- //c = color;
- gl_Position = vec4(100.0, 100.0, 100.0, 0.0);
- }
-
- return c;
- }
- vec3 getNormal(){
- //vec3 n_hsv = vec3( modelMatrix * vec4( normal, 0.0 )) * 0.5 + 0.5; // (n_world.xyz + vec3(1.,1.,1.)) / 2.;
- vec3 n_view = normalize( vec3(modelViewMatrix * vec4( normal, 0.0 )) );
- return n_view;
- }
- bool applyBackfaceCulling() {
- // Black not facing vertices / Backface culling
-
- vec3 e = normalize(vec3(modelViewMatrix * vec4( position, 1. )));
- vec3 n = getNormal(); // normalize( vec3(modelViewMatrix * vec4( normal, 0.0 )) );
- if((uUseOrthographicCamera && n.z <= 0.) || (!uUseOrthographicCamera && dot( n, e ) >= 0.)) {
- return true;
- } else {
- return false;
- }
- }
- #if defined(color_type_matcap)
- // Matcap Material
- vec3 getMatcap(){
- vec3 eye = normalize( vec3( modelViewMatrix * vec4( position, 1. ) ) );
- if(uUseOrthographicCamera) {
- eye = vec3(0., 0., -1.);
- }
- vec3 r_en = reflect( eye, getNormal() ); // or r_en = e - 2. * dot( n, e ) * n;
- float m = 2. * sqrt(pow( r_en.x, 2. ) + pow( r_en.y, 2. ) + pow( r_en.z + 1., 2. ));
- vec2 vN = r_en.xy / m + .5;
- return texture2D(matcapTextureUniform, vN).rgb;
- }
- #endif
- vec3 getExtra(){
- float w = (aExtra + uExtraOffset) * uExtraScale;
- w = clamp(w, 0.0, 1.0);
- vec3 color = texture2D(gradient, vec2(w,1.0-w)).rgb;
- // vec2 r = uExtraNormalizedRange;
- // float w = aExtra * (r.y - r.x) + r.x;
- // w = (w - uExtraRange.x) / (uExtraRange.y - uExtraRange.x);
- // w = clamp(w, 0.0, 1.0);
- // vec3 color = texture2D(gradient, vec2(w,1.0-w)).rgb;
- return color;
- }
- vec3 getColor(){
- vec3 color;
-
- #ifdef color_type_rgba
- color = getRGB();
-
-
- #elif defined color_type_height || defined color_type_elevation
- color = getElevation();
- #elif defined color_type_rgb_height
- vec3 cHeight = getElevation();
- color = (1.0 - uTransition) * getRGB() + uTransition * cHeight;
- #elif defined color_type_depth
- float linearDepth = gl_Position.w;
- float expDepth = (gl_Position.z / gl_Position.w) * 0.5 + 0.5;
- color = vec3(linearDepth, expDepth, 0.0);
- //color = vec3(1.0, 0.5, 0.3);
- #elif defined color_type_intensity
- float w = getIntensity();
- color = vec3(w, w, w);
- #elif defined color_type_gps_time
- color = getGpsTime();
- #elif defined color_type_intensity_gradient
- float w = getIntensity();
- color = texture2D(gradient, vec2(w,1.0-w)).rgb;
- #elif defined color_type_color
- color = uColor;
- #elif defined color_type_level_of_detail
- float depth = getLOD();
- float w = depth / 10.0;
- color = texture2D(gradient, vec2(w,1.0-w)).rgb;
- #elif defined color_type_indices
- color = indices.rgb;
- #elif defined color_type_classification
- vec4 cl = getClassification();
- color = cl.rgb;
- #elif defined color_type_return_number
- color = getReturnNumber();
- #elif defined color_type_returns
- color = getReturns();
- #elif defined color_type_number_of_returns
- color = getNumberOfReturns();
- #elif defined color_type_source_id
- color = getSourceID();
- #elif defined color_type_point_source_id
- color = getSourceID();
- #elif defined color_type_normal
- color = (modelMatrix * vec4(normal, 0.0)).xyz;
- #elif defined color_type_phong
- color = color;
- #elif defined color_type_composite
- color = getCompositeColor();
- #elif defined color_type_matcap
- color = getMatcap();
- #else
- color = getExtra();
- #endif
-
- if (backfaceCulling && applyBackfaceCulling()){
- //color = vec3(0.);
- }
- //applyBackfaceCulling直接返回false或者注释color = vec3(0.);都没问题
- return color;
- }
- float getPointSize(){
- float pointSize = 1.0;
-
- float slope = tan(fov / 2.0);
- float projFactor = -0.5 * resolution.y / (slope * vViewPosition.z);
-
-
-
- /*
- float scale = length(
- modelViewMatrix * vec4(0, 0, 0, 1) -
- modelViewMatrix * vec4(uOctreeSpacing, 0, 0, 1)
- ) / uOctreeSpacing;
-
- projFactor = projFactor * scale;
- */
-
-
- float r = uOctreeSpacing * 1.7;
- //vRadius = r;
-
-
- #if defined fixed_point_size
- pointSize = size;
- #elif defined attenuated_point_size
- if(uUseOrthographicCamera){
- pointSize = size * 10.0; //加个乘数
- }else{ //近大远小,模拟真实mesh,边缘放大
- //pointSize = size * spacing * projFactor; //spacing是attribute 为空 如果有这个值就能更自适应填补
- //pointSize = size * uOctreeSpacing * projFactor / 18.0; //直接用cloud的spacing里,不过因为都一样所以可能没有什么意义
- //pointSize = pointSize * projFactor;
- pointSize = size * projFactor ;
- }
- #elif defined adaptive_point_size
- if(uUseOrthographicCamera) {
- float worldSpaceSize = 1.0 * size * r / getPointSizeAttenuation();
- pointSize = (worldSpaceSize / uOrthoWidth) * resolution.x; //uScreenWidth;
- } else {
- float worldSpaceSize = 1.0 * size * r / getPointSizeAttenuation();
- pointSize = worldSpaceSize * projFactor;
- }
- #endif
- pointSize = max(minSize, pointSize);
- pointSize = min(maxSize, pointSize);
-
- vRadius = pointSize / projFactor;
- return pointSize;
- }
- #if defined(num_clippolygons) && num_clippolygons > 0
- bool pointInClipPolygon(vec3 point, int polyIdx) {
- mat4 wvp = uClipPolygonWVP[polyIdx];
- //vec4 screenClipPos = uClipPolygonVP[polyIdx] * modelMatrix * vec4(point, 1.0);
- //screenClipPos.xy = screenClipPos.xy / screenClipPos.w * 0.5 + 0.5;
- vec4 pointNDC = wvp * vec4(point, 1.0);
- pointNDC.xy = pointNDC.xy / pointNDC.w;
- int j = uClipPolygonVCount[polyIdx] - 1;
- bool c = false;
- for(int i = 0; i < 8; i++) {
- if(i == uClipPolygonVCount[polyIdx]) {
- break;
- }
- //vec4 verti = wvp * vec4(uClipPolygonVertices[polyIdx * 8 + i], 1);
- //vec4 vertj = wvp * vec4(uClipPolygonVertices[polyIdx * 8 + j], 1);
- //verti.xy = verti.xy / verti.w;
- //vertj.xy = vertj.xy / vertj.w;
- //verti.xy = verti.xy / verti.w * 0.5 + 0.5;
- //vertj.xy = vertj.xy / vertj.w * 0.5 + 0.5;
- vec3 verti = uClipPolygonVertices[polyIdx * 8 + i];
- vec3 vertj = uClipPolygonVertices[polyIdx * 8 + j];
- if( ((verti.y > pointNDC.y) != (vertj.y > pointNDC.y)) &&
- (pointNDC.x < (vertj.x-verti.x) * (pointNDC.y-verti.y) / (vertj.y-verti.y) + verti.x) ) {
- c = !c;
- }
- j = i;
- }
- return c;
- }
- #endif
- void doClipping(){
- {
- vec4 cl = getClassification();
- if(cl.a == 0.0){
- gl_Position = vec4(100.0, 100.0, 100.0, 0.0);
-
- return;
- }
- }
- #if defined(clip_return_number_enabled)
- { // return number filter
- vec2 range = uFilterReturnNumberRange;
- if(returnNumber < range.x || returnNumber > range.y){
- gl_Position = vec4(100.0, 100.0, 100.0, 0.0);
-
- return;
- }
- }
- #endif
- #if defined(clip_number_of_returns_enabled)
- { // number of return filter
- vec2 range = uFilterNumberOfReturnsRange;
- if(numberOfReturns < range.x || numberOfReturns > range.y){
- gl_Position = vec4(100.0, 100.0, 100.0, 0.0);
-
- return;
- }
- }
- #endif
- #if defined(clip_gps_enabled)
- { // GPS time filter
- float time = (gpsTime + uGpsOffset) * uGpsScale;
- vec2 range = uFilterGPSTimeClipRange;
- if(time < range.x || time > range.y){
- gl_Position = vec4(100.0, 100.0, 100.0, 0.0);
-
- return;
- }
- }
- #endif
- #if defined(clip_point_source_id_enabled)
- { // point source id filter
- vec2 range = uFilterPointSourceIDClipRange;
- if(pointSourceID < range.x || pointSourceID > range.y){
- gl_Position = vec4(100.0, 100.0, 100.0, 0.0);
-
- return;
- }
- }
- #endif
- int clipVolumesCount = 0;
- int insideCount = 0;
- #if defined(num_clipboxes) && num_clipboxes > 0
- for(int i = 0; i < num_clipboxes; i++){
- vec4 clipPosition = clipBoxes[i] * modelMatrix * vec4( position, 1.0 );
- bool inside = -0.5 <= clipPosition.x && clipPosition.x <= 0.5;
- inside = inside && -0.5 <= clipPosition.y && clipPosition.y <= 0.5;
- inside = inside && -0.5 <= clipPosition.z && clipPosition.z <= 0.5;
- insideCount = insideCount + (inside ? 1 : 0);
- clipVolumesCount++;
- }
- #endif
- #if defined(num_clippolygons) && num_clippolygons > 0
- for(int i = 0; i < num_clippolygons; i++) {
- bool inside = pointInClipPolygon(position, i);
- insideCount = insideCount + (inside ? 1 : 0);
- clipVolumesCount++;
- }
- #endif
- bool insideAny = insideCount > 0;
- bool insideAll = (clipVolumesCount > 0) && (clipVolumesCount == insideCount);
- if(clipMethod == CLIPMETHOD_INSIDE_ANY){
- if(insideAny && clipTask == CLIPTASK_HIGHLIGHT){
- vColor.r += 0.5;
- }else if(!insideAny && clipTask == CLIPTASK_SHOW_INSIDE){
- gl_Position = vec4(100.0, 100.0, 100.0, 1.0);
- }else if(insideAny && clipTask == CLIPTASK_SHOW_OUTSIDE){
- gl_Position = vec4(100.0, 100.0, 100.0, 1.0);
- }
- }else if(clipMethod == CLIPMETHOD_INSIDE_ALL){
- if(insideAll && clipTask == CLIPTASK_HIGHLIGHT){
- vColor.r += 0.5;
- }else if(!insideAll && clipTask == CLIPTASK_SHOW_INSIDE){
- gl_Position = vec4(100.0, 100.0, 100.0, 1.0);
- }else if(insideAll && clipTask == CLIPTASK_SHOW_OUTSIDE){
- gl_Position = vec4(100.0, 100.0, 100.0, 1.0);
- }
- }
- }
- //
- // ## ## ### #### ## ##
- // ### ### ## ## ## ### ##
- // #### #### ## ## ## #### ##
- // ## ### ## ## ## ## ## ## ##
- // ## ## ######### ## ## ####
- // ## ## ## ## ## ## ###
- // ## ## ## ## #### ## ##
- //
- vec2 getSamplerCoord( vec3 direction )
- {
- direction = normalize(direction);
- float tx=atan(direction.x,-direction.y)/(PI*2.0)+0.5;
- float ty=acos(direction.z)/PI;
- return vec2(tx,ty);
- }
- vec3 transformAxis( vec3 direction ) //navvis->4dkk
- {
- float y = direction.y;
- direction.y = direction.z;
- direction.z = -y;
- return direction;
- }
- void main() {
- //bool filtered_by_normal = false;
- float normalZ = 0.0;
- #ifdef use_filter_by_normal
- /*if(abs(getNormal().z) > 0.4) { //ufilterByNormalThreshold 暂定 3
- // Move point outside clip space space to discard it.
- //gl_Position = vec4(0.0, 0.0, 2.0, 1.0); //gl_Position的可视区域是 x,y,z都是[-1,1]
- //return;
- //filtered_by_normal = true; //标记一下。不直接不绘制,因为有的法线都是垂直向上
-
- }*/
-
- normalZ = abs(getNormal().z);
- #endif
-
- vec4 mvPosition = modelViewMatrix * vec4(position, 1.0 );
- vViewPosition = mvPosition.xyz;
- gl_Position = projectionMatrix * mvPosition;
- vLogDepth = log2(-mvPosition.z);
-
-
-
- // COLOR
- //加-------------------
- #if defined(usePanoMap)
- vec4 worldPosition = modelMatrix * vec4(position, 1.0);
-
- vec3 positionLocalToPanoCenter0 = worldPosition.xyz - pano0Position;
- vec3 vWorldPosition0 = (vec4(positionLocalToPanoCenter0, 1.0) * pano0Matrix).xyz;
- vWorldPosition0.x *= -1.0;
- vWorldPosition0 = transformAxis(vWorldPosition0);
-
- vec3 positionLocalToPanoCenter1 = worldPosition.xyz - pano1Position;
- vec3 vWorldPosition1 = (vec4(positionLocalToPanoCenter1, 1.0) * pano1Matrix).xyz;
- vWorldPosition1.x *= -1.0;
- vWorldPosition1 = transformAxis(vWorldPosition1);
-
- /*
- vec2 samplerCoord0 = getSamplerCoord(vWorldPosition0.xyz);
- vec2 samplerCoord1 = getSamplerCoord(vWorldPosition1.xyz);
- vec4 colorFromPano0 = texture2D(pano0Map,samplerCoord0);
- vec4 colorFromPano1 = texture2D(pano1Map,samplerCoord1);
- */
-
-
-
-
- vec4 colorFromPano0=textureCube(pano0Map,vWorldPosition0.xyz);
- vec4 colorFromPano1=textureCube(pano1Map,vWorldPosition1.xyz);
- vColor = mix(colorFromPano0,colorFromPano1,progress).xyz;
-
- //float easeInOutRatio = 0.0; //缓冲,渐变点云到贴图的颜色
- if(progress < easeInOutRatio){
- float easeProgress = (easeInOutRatio - progress) / easeInOutRatio;
- vec3 vColor1 = getColor();
- vColor = mix(vColor,vColor1,easeProgress);
- }else if(progress > 1.0 - easeInOutRatio){
- float easeProgress = (progress - (1.0 - easeInOutRatio) ) / easeInOutRatio;
- vec3 vColor1 = getColor();
- vColor = mix(vColor,vColor1,easeProgress);
- }
-
- #else
-
- vColor = getColor();
-
- #endif
-
-
- //-------------------
-
- #ifdef attenuated_opacity
- //zoom不会改变z 所以这并不是用在分屏时候的
- //vOpacity = uOpacity * exp(-length(-mvPosition.xyz) / 1000.0); // e为底的指数函数 opacityAttenuation = 1000
- vOpacity = uOpacity * exp(gl_Position.z/50.0);
- vOpacity = clamp(vOpacity, 0.001, 1.0);
- /*if(filtered_by_normal){//垂直朝相机时降低透明度
- vOpacity *= 0.2;
- vOpacity = clamp(vOpacity, 0.0001, 0.1);
- } */
- #else
- vOpacity = uOpacity;
- /*if(filtered_by_normal){//垂直朝相机时降低透明度
- /*if(filtered_by_normal){//垂直朝相机时降低透明度
- vOpacity *= 0.3;
- vOpacity = clamp(vOpacity, 0.0001, 0.1);
- }*/
-
- vOpacity *= max(0.1, (1.0 - normalZ));
- #endif
-
- // POINT SIZE
- float pointSize = getPointSize();
-
- gl_PointSize = pointSize;
- vPointSize = pointSize;
-
-
-
-
- // only for "replacing" approaches
- // if(getLOD() != uLevel){
- // gl_Position = vec4(10.0, 10.0, 10.0, 1.0);
- // }
- #if defined hq_depth_pass
- float originalDepth = gl_Position.w;
- float adjustedDepth = originalDepth + 2.0 * vRadius;
- float adjust = adjustedDepth / originalDepth;
- mvPosition.xyz = mvPosition.xyz * adjust;
- gl_Position = projectionMatrix * mvPosition;
- #endif
- // CLIPPING
- doClipping();
- #if defined(num_clipspheres) && num_clipspheres > 0
- for(int i = 0; i < num_clipspheres; i++){
- vec4 sphereLocal = uClipSpheres[i] * mvPosition;
- float distance = length(sphereLocal.xyz);
- if(distance < 1.0){
- float w = distance;
- vec3 cGradient = texture2D(gradient, vec2(w, 1.0 - w)).rgb;
-
- vColor = cGradient;
- //vColor = cGradient * 0.7 + vColor * 0.3;
- }
- }
- #endif
- #if defined(num_shadowmaps) && num_shadowmaps > 0
- const float sm_near = 0.1;
- const float sm_far = 10000.0;
- for(int i = 0; i < num_shadowmaps; i++){
- vec3 viewPos = (uShadowWorldView[i] * vec4(position, 1.0)).xyz;
- float distanceToLight = abs(viewPos.z);
-
- vec4 projPos = uShadowProj[i] * uShadowWorldView[i] * vec4(position, 1);
- vec3 nc = projPos.xyz / projPos.w;
-
- float u = nc.x * 0.5 + 0.5;
- float v = nc.y * 0.5 + 0.5;
- vec2 sampleStep = vec2(1.0 / (2.0*1024.0), 1.0 / (2.0*1024.0)) * 1.5;
- vec2 sampleLocations[9];
- sampleLocations[0] = vec2(0.0, 0.0);
- sampleLocations[1] = sampleStep;
- sampleLocations[2] = -sampleStep;
- sampleLocations[3] = vec2(sampleStep.x, -sampleStep.y);
- sampleLocations[4] = vec2(-sampleStep.x, sampleStep.y);
- sampleLocations[5] = vec2(0.0, sampleStep.y);
- sampleLocations[6] = vec2(0.0, -sampleStep.y);
- sampleLocations[7] = vec2(sampleStep.x, 0.0);
- sampleLocations[8] = vec2(-sampleStep.x, 0.0);
- float visibleSamples = 0.0;
- float numSamples = 0.0;
- float bias = vRadius * 2.0;
- for(int j = 0; j < 9; j++){
- vec4 depthMapValue = texture2D(uShadowMap[i], vec2(u, v) + sampleLocations[j]);
- float linearDepthFromSM = depthMapValue.x + bias;
- float linearDepthFromViewer = distanceToLight;
- if(linearDepthFromSM > linearDepthFromViewer){
- visibleSamples += 1.0;
- }
- numSamples += 1.0;
- }
- float visibility = visibleSamples / numSamples;
- if(u < 0.0 || u > 1.0 || v < 0.0 || v > 1.0 || nc.x < -1.0 || nc.x > 1.0 || nc.y < -1.0 || nc.y > 1.0 || nc.z < -1.0 || nc.z > 1.0){
- //vColor = vec3(0.0, 0.0, 0.2);
- }else{
- //vColor = vec3(1.0, 1.0, 1.0) * visibility + vec3(1.0, 1.0, 1.0) * vec3(0.5, 0.0, 0.0) * (1.0 - visibility);
- vColor = vColor * visibility + vColor * uShadowColor * (1.0 - visibility);
- }
- }
- #endif
-
-
- }
- `;
- Shaders["pointcloud.fs"] = `
- #if defined paraboloid_point_shape
- #extension GL_EXT_frag_depth : enable
- #endif
- #define PI 3.141592653589793
- precision highp float;
- precision highp int;
- /*
- #if defined(usePanoMap)
-
- uniform samplerCube pano0Map; //随便设置一个samplerCube去使用都会让点云消失
- uniform samplerCube pano1Map;
-
- uniform float progress;
- uniform float easeInOutRatio;
-
- uniform vec3 pano0Position;
- uniform mat4 pano0Matrix;
- uniform vec3 pano1Position;
- uniform mat4 pano1Matrix;
- varying vec3 vWorldPosition0;
- varying vec3 vWorldPosition1;
- #endif
- */
- //------------
- uniform mat4 viewMatrix;
- uniform mat4 uViewInv;
- uniform mat4 uProjInv;
- uniform vec3 cameraPosition;
- uniform mat4 projectionMatrix;
- //uniform float uOpacity;
- varying float vOpacity; //add
- uniform float blendHardness;
- uniform float blendDepthSupplement;
- uniform float fov;
- uniform float uSpacing;
- uniform float near;
- uniform float far;
- uniform float uPCIndex;
- uniform float uScreenWidth;
- uniform float uScreenHeight;
- varying vec3 vColor;
- varying float vLogDepth;
- varying vec3 vViewPosition;
- varying float vRadius;
- varying float vPointSize;
- varying vec3 vPosition;
- float specularStrength = 1.0;
- vec2 getSamplerCoord( vec3 direction )
- {
- direction = normalize(direction);
- float tx=atan(direction.x,-direction.y)/(PI*2.0)+0.5;
- float ty=acos(direction.z)/PI;
- return vec2(tx,ty);
- }
- void main() {
- vec3 color = vColor;
-
-
- /*#if defined(usePanoMap) //加 经测试,即使全部写在fragment里也是无论pointsize多大都是一个点一个颜色,所以干脆写在vectex里
-
-
- vec4 colorFromPano0=textureCube(pano0Map,vWorldPosition0.xyz);
- vec4 colorFromPano1=textureCube(pano1Map,vWorldPosition1.xyz);
-
- color = mix(colorFromPano0,colorFromPano1,progress).xyz;
-
-
- //float easeInOutRatio = 0.0; //缓冲,渐变点云到贴图的颜色
- if(progress < easeInOutRatio){
- float easeProgress = (easeInOutRatio - progress) / easeInOutRatio;
- color = mix(color,vColor,easeProgress);
- }else if(progress > 1.0 - easeInOutRatio){
- float easeProgress = (progress - (1.0 - easeInOutRatio) ) / easeInOutRatio;
- color = mix(color,vColor,easeProgress);
- }
-
-
- #else
- color = vColor;
- #endif*/
-
-
-
- float depth = gl_FragCoord.z;
- #if defined(circle_point_shape) || defined(paraboloid_point_shape)
- float u = 2.0 * gl_PointCoord.x - 1.0;
- float v = 2.0 * gl_PointCoord.y - 1.0;
- #endif
-
- #if defined(circle_point_shape)
- float cc = u*u + v*v;
- if(cc > 1.0){
- discard;
- }
- #endif
-
-
-
-
- #if defined color_type_indices //pick point recognize
- gl_FragColor = vec4(color, uPCIndex / 255.0); //uPCIndex : node Index
- #else
- gl_FragColor = vec4(color, vOpacity);
- #endif
-
-
-
-
-
-
-
-
- #if defined paraboloid_point_shape
- float wi = 0.0 - ( u*u + v*v);
- vec4 pos = vec4(vViewPosition, 1.0);
- pos.z += wi * vRadius;
- float linearDepth = -pos.z;
- pos = projectionMatrix * pos;
- pos = pos / pos.w;
- float expDepth = pos.z;
- depth = (pos.z + 1.0) / 2.0;
- gl_FragDepthEXT = depth;
-
- #if defined(color_type_depth)
- color.r = linearDepth;
- color.g = expDepth;
- #endif
-
- #if defined(use_edl)
- gl_FragColor.a = log2(linearDepth);
- #endif
-
- #else
- #if defined(use_edl)
- gl_FragColor.a = vLogDepth;
- #endif
- #endif
- #if defined(weighted_splats)
- float distance = 2.0 * length(gl_PointCoord.xy - 0.5);
- float weight = max(0.0, 1.0 - distance);
- weight = pow(weight, 1.5);
- gl_FragColor.a = weight;
- gl_FragColor.xyz = gl_FragColor.xyz * weight;
- #endif
- //gl_FragColor = vec4(0.0, 0.7, 0.0, 1.0);
-
- }
- `;
- Shaders["pointcloud_sm.vs"] = `
- precision mediump float;
- precision mediump int;
- attribute vec3 position;
- attribute vec3 color;
- uniform mat4 modelMatrix;
- uniform mat4 modelViewMatrix;
- uniform mat4 projectionMatrix;
- uniform mat4 viewMatrix;
- uniform float uScreenWidth;
- uniform float uScreenHeight;
- uniform float near;
- uniform float far;
- uniform float uSpacing;
- uniform float uOctreeSize;
- uniform float uLevel;
- uniform float uVNStart;
- uniform sampler2D visibleNodes;
- varying float vLinearDepth;
- varying vec3 vColor;
- #define PI 3.141592653589793
- // ---------------------
- // OCTREE
- // ---------------------
- #if defined(adaptive_point_size)
- /**
- * number of 1-bits up to inclusive index position
- * number is treated as if it were an integer in the range 0-255
- *
- */
- float numberOfOnes(float number, float index){
- float tmp = mod(number, pow(2.0, index + 1.0));
- float numOnes = 0.0;
- for(float i = 0.0; i < 8.0; i++){
- if(mod(tmp, 2.0) != 0.0){
- numOnes++;
- }
- tmp = floor(tmp / 2.0);
- }
- return numOnes;
- }
- /**
- * checks whether the bit at index is 1
- * number is treated as if it were an integer in the range 0-255
- *
- */
- bool isBitSet(float number, float index){
- return mod(floor(number / pow(2.0, index)), 2.0) != 0.0;
- }
- /**
- * find the LOD at the point position
- */
- float getLOD(){
-
- vec3 offset = vec3(0.0, 0.0, 0.0);
- float iOffset = uVNStart;
- float depth = uLevel;
- for(float i = 0.0; i <= 30.0; i++){
- float nodeSizeAtLevel = uOctreeSize / pow(2.0, i + uLevel + 0.0);
-
- vec3 index3d = (position-offset) / nodeSizeAtLevel;
- index3d = floor(index3d + 0.5);
- float index = 4.0 * index3d.x + 2.0 * index3d.y + index3d.z;
-
- vec4 value = texture2D(visibleNodes, vec2(iOffset / 2048.0, 0.0));
- float mask = value.r * 255.0;
- if(isBitSet(mask, index)){
- // there are more visible child nodes at this position
- iOffset = iOffset + value.g * 255.0 * 256.0 + value.b * 255.0 + numberOfOnes(mask, index - 1.0);
- depth++;
- }else{
- // no more visible child nodes at this position
- return depth;
- }
-
- offset = offset + (vec3(1.0, 1.0, 1.0) * nodeSizeAtLevel * 0.5) * index3d;
- }
-
- return depth;
- }
- #endif
- float getPointSize(){
- float pointSize = 1.0;
-
- float slope = tan(fov / 2.0);
- float projFactor = -0.5 * uScreenHeight / (slope * vViewPosition.z);
-
- float r = uOctreeSpacing * 1.5;
- vRadius = r;
- #if defined fixed_point_size
- pointSize = size;
- #elif defined attenuated_point_size
- if(uUseOrthographicCamera){
- pointSize = size;
- }else{
- pointSize = pointSize * projFactor;
- }
- #elif defined adaptive_point_size
- if(uUseOrthographicCamera) {
- float worldSpaceSize = 1.5 * size * r / getPointSizeAttenuation();
- pointSize = (worldSpaceSize / uOrthoWidth) * uScreenWidth;
- } else {
- float worldSpaceSize = 1.5 * size * r / getPointSizeAttenuation();
- pointSize = worldSpaceSize * projFactor;
- }
- #endif
- pointSize = max(minSize, pointSize);
- pointSize = min(maxSize, pointSize);
-
- vRadius = pointSize / projFactor;
- return pointSize;
- }
- void main() {
- vec4 mvPosition = modelViewMatrix * vec4( position, 1.0 );
- vLinearDepth = gl_Position.w;
- float pointSize = getPointSize();
- gl_PointSize = pointSize;
- }
- `;
- Shaders["pointcloud_sm.fs"] = `
- precision mediump float;
- precision mediump int;
- varying vec3 vColor;
- varying float vLinearDepth;
- void main() {
- //gl_FragColor = vec4(1.0, 0.0, 0.0, 1.0);
- //gl_FragColor = vec4(vColor, 1.0);
- //gl_FragColor = vec4(vLinearDepth, pow(vLinearDepth, 2.0), 0.0, 1.0);
- gl_FragColor = vec4(vLinearDepth, vLinearDepth / 30.0, vLinearDepth / 30.0, 1.0);
-
- }
- `;
- Shaders["normalize.vs"] = `
- precision mediump float;
- precision mediump int;
- attribute vec3 position;
- attribute vec2 uv;
- uniform mat4 projectionMatrix;
- uniform mat4 modelViewMatrix;
- varying vec2 vUv;
- void main() {
- vUv = uv;
- gl_Position = projectionMatrix * modelViewMatrix * vec4(position,1.0);
- }`;
- Shaders["normalize.fs"] = `
- #extension GL_EXT_frag_depth : enable
- precision mediump float;
- precision mediump int;
- uniform sampler2D uWeightMap;
- uniform sampler2D uDepthMap;
- varying vec2 vUv;
- void main() {
- float depth = texture2D(uDepthMap, vUv).r;
-
- if(depth >= 1.0){
- discard;
- }
- gl_FragColor = vec4(depth, 1.0, 0.0, 1.0);
- vec4 color = texture2D(uWeightMap, vUv);
- color = color / color.w;
-
- gl_FragColor = vec4(color.xyz, 1.0);
-
- gl_FragDepthEXT = depth;
- }`;
- Shaders["normalize_and_edl.fs"] = `
- #extension GL_EXT_frag_depth : enable
- //
- // adapted from the EDL shader code from Christian Boucheny in cloud compare:
- // https://github.com/cloudcompare/trunk/tree/master/plugins/qEDL/shaders/EDL
- //
- precision mediump float;
- precision mediump int;
- uniform sampler2D uWeightMap;
- uniform sampler2D uEDLMap;
- uniform sampler2D uDepthMap;
- uniform float screenWidth;
- uniform float screenHeight;
- uniform vec2 neighbours[NEIGHBOUR_COUNT];
- uniform float edlStrength;
- uniform float radius;
- varying vec2 vUv;
- float response(float depth){
- vec2 uvRadius = radius / vec2(screenWidth, screenHeight);
-
- float sum = 0.0;
-
- for(int i = 0; i < NEIGHBOUR_COUNT; i++){
- vec2 uvNeighbor = vUv + uvRadius * neighbours[i];
-
- float neighbourDepth = texture2D(uEDLMap, uvNeighbor).a;
- if(neighbourDepth != 0.0){
- if(depth == 0.0){
- sum += 100.0;
- }else{
- sum += max(0.0, depth - neighbourDepth);
- }
- }
- }
-
- return sum / float(NEIGHBOUR_COUNT);
- }
- void main() {
- float edlDepth = texture2D(uEDLMap, vUv).a;
- float res = response(edlDepth);
- float shade = exp(-res * 300.0 * edlStrength);
- float depth = texture2D(uDepthMap, vUv).r;
- if(depth >= 1.0 && res == 0.0){
- discard;
- }
-
- vec4 color = texture2D(uWeightMap, vUv);
- color = color / color.w;
- color = color * shade;
- gl_FragColor = vec4(color.xyz, 1.0);
- gl_FragDepthEXT = depth;
- }`;
- Shaders["edl.vs"] = `
- precision mediump float;
- precision mediump int;
- attribute vec3 position;
- attribute vec2 uv;
- uniform mat4 projectionMatrix;
- uniform mat4 modelViewMatrix;
- varying vec2 vUv;
- void main() {
- vUv = uv;
-
- vec4 mvPosition = modelViewMatrix * vec4(position,1.0);
- gl_Position = projectionMatrix * mvPosition;
- }`;
- Shaders["edl.fs"] = `
- #extension GL_EXT_frag_depth : enable
- //
- // adapted from the EDL shader code from Christian Boucheny in cloud compare:
- // https://github.com/cloudcompare/trunk/tree/master/plugins/qEDL/shaders/EDL
- //
- precision mediump float;
- precision mediump int;
- //uniform float screenWidth;
- //uniform float screenHeight;
- uniform vec2 resolution;
- uniform vec2 neighbours[NEIGHBOUR_COUNT];
- uniform float edlStrength;
- uniform float radius;
- uniform float opacity;
- //uniform float uNear;
- //uniform float uFar;
- uniform mat4 uProj;
- uniform sampler2D uEDLColor;
- uniform sampler2D uEDLDepth;
- varying vec2 vUv;
- uniform int useEDL;
- float response(float depth){
- vec2 uvRadius = radius / resolution; //vec2(screenWidth, screenHeight);
-
- float sum = 0.0;
-
- for(int i = 0; i < NEIGHBOUR_COUNT; i++){
- vec2 uvNeighbor = vUv + uvRadius * neighbours[i];
- //获取周围八个格子的值
- float neighbourDepth = texture2D(uEDLColor, uvNeighbor).a;
- neighbourDepth = (neighbourDepth == 1.0) ? 0.0 : neighbourDepth;
- if(neighbourDepth != 0.0){
- //if(depth == 0.0){
- // sum += 100.0;
- //}else{
- sum += max(0.0, depth - neighbourDepth); //获取差值
- //}
- }
- }
-
- return sum / float(NEIGHBOUR_COUNT);
- }
- void main(){
- vec4 cEDL = texture2D(uEDLColor, vUv);
-
- float depth = cEDL.a;
- depth = (depth == 1.0) ? 0.0 : depth;
-
- if(depth == 0.0){ //去掉这句就能在无点云像素的地方渲染outline,但会遮住其他mesh
- discard;
- }
-
-
- if(useEDL == 1){
- float res = response(depth);
-
- //if(depth == 0.0 && res == 0.0){ //test
- // discard;
- //}
-
- float shade = exp(-res * 300.0 * edlStrength); //自然常数e为底的指数函数
- gl_FragColor = vec4(cEDL.rgb * shade, opacity);
-
- //const vec3 outlineColor = vec3(1.0,0.0,0.0);//test -outline
- //gl_FragColor = vec4(mix(cEDL.rgb, outlineColor, -res), opacity );
- }else{//加 不改颜色的情况
- gl_FragColor = vec4(cEDL.rgb, opacity);
- }
-
-
- { // write regular hyperbolic depth values to depth buffer 修改深度
- float dl = pow(2.0, depth);
- vec4 dp = uProj * vec4(0.0, 0.0, -dl, 1.0);
- float pz = dp.z / dp.w;
- float fragDepth = (pz + 1.0) / 2.0;
- gl_FragDepthEXT = fragDepth;
- }
-
- }
- `;
- Shaders["blur.vs"] = `
- varying vec2 vUv;
- void main() {
- vUv = uv;
- gl_Position = projectionMatrix * modelViewMatrix * vec4(position,1.0);
- }`;
- Shaders["blur.fs"] = `
- uniform mat4 projectionMatrix;
- uniform float screenWidth;
- uniform float screenHeight;
- uniform float near;
- uniform float far;
- uniform sampler2D map;
- varying vec2 vUv;
- void main() {
- float dx = 1.0 / screenWidth;
- float dy = 1.0 / screenHeight;
- vec3 color = vec3(0.0, 0.0, 0.0);
- color += texture2D(map, vUv + vec2(-dx, -dy)).rgb;
- color += texture2D(map, vUv + vec2( 0, -dy)).rgb;
- color += texture2D(map, vUv + vec2(+dx, -dy)).rgb;
- color += texture2D(map, vUv + vec2(-dx, 0)).rgb;
- color += texture2D(map, vUv + vec2( 0, 0)).rgb;
- color += texture2D(map, vUv + vec2(+dx, 0)).rgb;
- color += texture2D(map, vUv + vec2(-dx, dy)).rgb;
- color += texture2D(map, vUv + vec2( 0, dy)).rgb;
- color += texture2D(map, vUv + vec2(+dx, dy)).rgb;
- color = color / 9.0;
-
- gl_FragColor = vec4(color, 1.0);
- }`;
- Shaders["depthBasic.vs"] = `
-
- varying vec2 vUv;
- void main() {
-
- vUv = uv;
- gl_Position = projectionMatrix * modelViewMatrix * vec4(position, 1.0);
- }
- `;
- Shaders["depthBasic.fs"] = `varying vec2 vUv;
- uniform float opacity;
- uniform vec3 baseColor;
- uniform vec3 backColor;
- uniform float occlusionDistance;
- uniform float clipDistance;
- uniform float maxClipFactor;
- #if defined use_map
- uniform sampler2D map;
- #endif
-
- #if defined(GL_EXT_frag_depth) && defined(useDepth)
- //似乎通过gl.getExtension('EXT_frag_depth')得到的GL_EXT_frag_depth
-
- uniform sampler2D depthTexture;
- uniform float nearPlane;
- uniform float farPlane;
- uniform vec2 resolution;
- uniform vec2 viewportOffset; // viewportOffset 范围从0-整个画布的像素
-
- float convertToLinear(float zValue)
- {
- float z = zValue * 2.0 - 1.0;
- return (2.0 * nearPlane * farPlane) / (farPlane + nearPlane - z * (farPlane - nearPlane));
- }
- #endif
-
- void main() {
-
-
- vec4 color = vec4(baseColor, opacity);
-
-
-
- #if defined(GL_EXT_frag_depth) && defined(useDepth)
- // mixFactor and clipFactor define the color mixing proportion between the states of
- // full visibility and occluded visibility
- // and
- // full visibility and total invisibility
-
- float mixFactor = 0.0;
- float clipFactor = 0.0;
-
-
- // The linear depth value of the current fragment
- float fragDepth = convertToLinear(gl_FragCoord.z);
- // The coordinates of the current fragment in the depth texture
- vec2 depthTxtCoords = vec2(gl_FragCoord.x-viewportOffset.x, gl_FragCoord.y - viewportOffset.y) / resolution;
-
- // The linear depth value of the pixel occupied by this fragment in the depth buffer
- float textureDepth = convertToLinear(texture2D(depthTexture, depthTxtCoords).r);
- // The difference between the two depths
- float delta = fragDepth - textureDepth;
- if (delta > 0.0)//差距
- {
- // occlusionDistance and clipDistance define the width of the respective zones and
- // mixFactor and clipFactor express the interpolation between the two colors depending on the position
- // of the current fragment withing those zones.
-
-
- mixFactor = clamp(delta / occlusionDistance, 0.0, 1.0);
- clipFactor = clamp(delta / clipDistance, 0.0, maxClipFactor);
- }
-
- // If the fragment is totally transparent, don't bother drawing it
- if (clipFactor == 1.0)
- {
- discard;
- }else{
-
- #if defined use_map
- color = texture2D(map, vUv) * color;
- #endif
-
-
-
- color = vec4(mix(color.rgb, backColor, mixFactor), color.a * (1.0 - clipFactor));
- }
-
- #else
- #if defined use_map
- color = texture2D(map, vUv) * color;
- #endif
- #endif
-
- gl_FragColor = color;
-
- }
- `;
- Shaders["copyCubeMap.vs"] = `varying vec3 vWorldPos;
- vec3 transformAxis( vec3 direction ) //navvis->4dkk
- {
- float y = direction.y;
- direction.y = direction.z;
- direction.z = -y;
- return direction;
- }
- void main() {
- vWorldPos = vec3(-position.x, -position.y, position.z);
- //vWorldPos = transformAxis(vWorldPos);
- gl_Position = projectionMatrix * modelViewMatrix * vec4(position, 1.0);
- }
- `;
- Shaders["copyCubeMap.fs"] = `varying vec3 vWorldPos;
- uniform float alpha;
- uniform samplerCube tDiffuse;
- void main() {
- vec4 texColor = textureCube(tDiffuse, vWorldPos);
- gl_FragColor = vec4(texColor.rgb, texColor.a * alpha);
- }
- `;
- Shaders["basicTextured.vs"] = `varying vec2 vUv;
- void main() {
- vUv = uv;
- gl_Position = projectionMatrix * modelViewMatrix * vec4(position, 1.0);
- }
- `;
- Shaders["basicTextured.fs"] = `varying vec2 vUv;
- uniform float alpha;
- uniform sampler2D tDiffuse;
- void main() {
- vec4 texColor = texture2D(tDiffuse, vUv);
- gl_FragColor = vec4(texColor.rgb, texColor.a * alpha);
- }
- `;
- let ftCanvas = document.createElement('canvas');
- const Features = (function () {
- let gl = ftCanvas.getContext('webgl') || ftCanvas.getContext('experimental-webgl');
- if (gl === null){
- return null;
- }
- // -- code taken from THREE.WebGLRenderer --
- let _vertexShaderPrecisionHighpFloat = gl.getShaderPrecisionFormat(gl.VERTEX_SHADER, gl.HIGH_FLOAT);
- let _vertexShaderPrecisionMediumpFloat = gl.getShaderPrecisionFormat(gl.VERTEX_SHADER, gl.MEDIUM_FLOAT);
- // Unused: let _vertexShaderPrecisionLowpFloat = gl.getShaderPrecisionFormat(gl.VERTEX_SHADER, gl.LOW_FLOAT);
- let _fragmentShaderPrecisionHighpFloat = gl.getShaderPrecisionFormat(gl.FRAGMENT_SHADER, gl.HIGH_FLOAT);
- let _fragmentShaderPrecisionMediumpFloat = gl.getShaderPrecisionFormat(gl.FRAGMENT_SHADER, gl.MEDIUM_FLOAT);
- // Unused: let _fragmentShaderPrecisionLowpFloat = gl.getShaderPrecisionFormat(gl.FRAGMENT_SHADER, gl.LOW_FLOAT);
- let highpAvailable = _vertexShaderPrecisionHighpFloat.precision > 0 && _fragmentShaderPrecisionHighpFloat.precision > 0;
- let mediumpAvailable = _vertexShaderPrecisionMediumpFloat.precision > 0 && _fragmentShaderPrecisionMediumpFloat.precision > 0;
- // -----------------------------------------
- let precision;
- if (highpAvailable) {
- precision = 'highp';
- } else if (mediumpAvailable) {
- precision = 'mediump';
- } else {
- precision = 'lowp';
- }
- return {
- SHADER_INTERPOLATION: {
- isSupported: function () {
- let supported = true;
- supported = supported && gl.getExtension('EXT_frag_depth');
- supported = supported && gl.getParameter(gl.MAX_VARYING_VECTORS) >= 8;
- return supported;
- }
- },
- SHADER_SPLATS: {
- isSupported: function () {
- let supported = true;
- supported = supported && gl.getExtension('EXT_frag_depth');
- supported = supported && gl.getExtension('OES_texture_float');
- supported = supported && gl.getParameter(gl.MAX_VARYING_VECTORS) >= 8;
- return supported;
- }
- },
- SHADER_EDL: {
- isSupported: function () {
- let supported = true;
- supported = supported && gl.getExtension('EXT_frag_depth');
- supported = supported && gl.getExtension('OES_texture_float');
- supported = supported && gl.getParameter(gl.MAX_VARYING_VECTORS) >= 8;
- //supported = supported || (gl instanceof WebGL2RenderingContext);
- return supported;
- }
- },
- //add:
- EXT_DEPTH:{
- isSupported: function () {
- if(browser.detectIOS()){
- let {major,minor,patch} = browser.iosVersion();
- if(major == 15 && minor == 4 && patch == 1){
- console.warn('检测到是ios15.4.1, 关闭EXT_frag_depth');//该版本ext_depth有问题,导致clear错乱。没有解决办法先关闭。
- return false
- }
- }
- return gl.getExtension('EXT_frag_depth'); //shader中的GL_EXT_frag_depth需要判断一下detectIOS吗。。
- }
- },
-
-
-
- //WEBGL2: {
- // isSupported: function(){
- // return gl instanceof WebGL2RenderingContext;
- // }
- //},
- precision: precision
- };
- }());
- class DepthBasicMaterial extends ShaderMaterial{
- constructor(o={}){
- let {width, height} = viewer.renderer.getSize(new Vector2$1());
-
- let uniforms = {
- resolution: { type: 'v2', value: new Vector2$1(width, height ) },
- viewportOffset: { type: 'v2', value: new Vector2$1(0, 0 ) }, //left, top
- nearPlane: { type: 'f', value: 0.1 },
- farPlane: { type: 'f', value: 10000 },
- depthTexture: { type: 't', value: null },
- opacity: { type: 'f', value: 1 },
- map: { type: 't', value: o.map },
- baseColor: {type:'v3', value: o.color ? new Color(o.color) : new Color("#ffffff")},
- backColor: {type:'v3', value: o.backColor ? new Color(o.backColor) : new Color("#ddd")},
- clipDistance : { type: 'f', value:o.clipDistance || 4}, //消失距离
- occlusionDistance : { type: 'f', value: o.occlusionDistance || 1 }, //变为backColor距离
- maxClipFactor : { type: 'f', value: o.maxClipFactor || 1 }, //0-1
-
- };
-
- let defines = {};
- if(o.useDepth && Features.EXT_DEPTH.isSupported())defines.useDepth = '';
- if(o.map)defines.use_map = '';
- super({
- uniforms,
- vertexShader: Shaders['depthBasic.vs'],
- fragmentShader: Shaders['depthBasic.fs'],
- depthWrite: !1,
- depthTest: !1,
- transparent: o.transparent == void 0 ? true : o.transparent,
- side: o.side || 0 /* THREE.DoubleSide */,
- defines,
- });
- if(o.opacity != void 0){
- this.opacity = o.opacity;
- }
-
- if(o.useDepth && Features.EXT_DEPTH.isSupported()) this.useDepth_ = true;
-
-
- let setSize = (e)=>{//如果出现横条状的异常,往往是viewportOffset出错
- let viewport = e.viewport;
- let viewportOffset = viewport.offset || new Vector2$1();
- this.uniforms.resolution.value.copy(viewport.resolution2);
- this.uniforms.viewportOffset.value.copy(viewportOffset);
-
- //console.log('depth '+viewportOffset.toArray())
- };
-
- let viewport = viewer.mainViewport;
-
- setSize( {viewport} );
-
- viewer.addEventListener('resize',(e)=>{
- if(!e.viewport || e.viewport.camera.isPerspectiveCamera){//地图不需要
- setSize(e);
- //console.log(this.name + viewportOffset.toArray())
- }
- });
-
-
- if(this.useDepth){
-
- /* viewer.addEventListener('camera_changed', (e)=>{
- if(e.viewport.name != 'mapViewport') this.updateDepthParams(e)
- }) */
-
- viewer.addEventListener("render.begin", (e)=>{//before render 如果有大于两个viewport的话,不同viewport用不同的depthTex
- if(e.viewport.camera.isPerspectiveCamera) this.updateDepthParams(e);
- });
-
- this.updateDepthParams();
- }
-
-
- //点云变化时要一直触发updateDepthParams??
- //viewer.once("render.pass.end",this.updateDepthParams.bind(this))
- }
-
- updateDepthParams(e={}){//主要用于点云遮住mesh
- if(this.useDepth){
- var viewport = e.viewport || viewer.mainViewport;
- var camera = viewport.camera;
- /* if(Potree.settings.displayMode == 'showPanos' && viewer.images360.currentPano.depthTex){
- this.uniforms.depthTexture.value = viewer.images360.currentPano.depthTex
- }else{ */
- this.uniforms.depthTexture.value = viewer.getPRenderer().getRtEDL(viewport).depthTexture; //其实只赋值一次就行
- //}
- this.uniforms.nearPlane.value = camera.near;
- this.uniforms.farPlane.value = camera.far;
-
- }
- }
- set map(map){
- this.uniforms.map.value = map;
- }
-
- get useDepth(){
- return this.useDepth_
- }
-
- set useDepth(value){//如果不支持 EXT_DEPTH 的话会失效
- if(this.useDepth_ != value){
- if(value && Features.EXT_DEPTH.isSupported()){
- this.defines.useDepth = '';
- this.updateDepthParams();
- }else {
- delete this.defines.useDepth;
- }
- this.useDepth_ = value;
- this.needsUpdate = true;
- }
- }
-
-
- get opacity(){
- return this.uniforms.opacity.value
- }
- set opacity(o){
- this.uniforms && (this.uniforms.opacity.value = o);
- }
-
- /* dispose(){
- super.dispose()
- viewer.depthBasic
- } */
-
- }
- const geo = new PlaneBufferGeometry(1,1);
- class Sprite$1 extends Mesh{
-
- constructor(options){
- super(geo, options.mat || new DepthBasicMaterial({map:options.map, useDepth:options.useDepth}));
-
- this.root = options.root || this;
- this.renderOrder = options.renderOrder != void 0 ? options.renderOrder : 4;
- this.sizeInfo = options.sizeInfo;
- this.dontFixOrient = options.dontFixOrient;
-
- this.root.matrixAutoUpdate = false;
- this.matrixMap = new Map();
- this.name = options.name || 'sprite';
- this.useViewport = null;
- this.viewports = options.viewports;//指定更新的viewports
- this.visible_ = true;
-
-
- let update = (e)=>{
- this.update(e);
- };
- viewer.mapViewer && viewer.mapViewer.addEventListener("camera_changed", update);
- viewer.addEventListener("camera_changed", update);
- /* if(viewer.viewports.length == 1){//直接更新。如果有多个不在这更新,在"render.begin"
- this.update(e)
- } */
-
-
- let applyMatrix = (e)=>{
- this.applyMatrix(e);
- };
- viewer.addEventListener("raycaster", applyMatrix); //before render
- viewer.addEventListener("render.begin", applyMatrix); //before render //magnifier时要禁止吗
-
- this.addEventListener('dispose', ()=>{
- viewer.mapViewer && viewer.mapViewer.removeEventListener("camera_changed", update);
- viewer.removeEventListener("camera_changed", update);
- viewer.removeEventListener("raycaster", applyMatrix); //before render
- viewer.removeEventListener("render.begin", applyMatrix);
-
- this.dispose();
- });
-
- }
-
- set visible(v){
- this.visible_ = v;
- if(v){
- this.update();
- }
- }
- get visible(){
- return this.visible_
- }
-
- update(e){
- if(!e){
- (this.viewports || viewer.viewports).forEach(view=>{
- this.update({viewport:view});
- });
- return;
- }
- if(!this.visible || !this.root)return
- if(this.viewports && !this.viewports.includes(e.viewport) )return
- if(e.viewport.name == 'magnifier')return
-
- let camera = e.viewport.camera;
- //rotation
- this.dontFixOrient || this.root.quaternion.copy(camera.quaternion);
-
- //scale
- var info = this.sizeInfo;
- if(info){
- this.root.updateMatrix();//先更新,getWorldPosition才能得到正确的
- this.root.updateMatrixWorld(true);
-
-
- var scale;
- if(info.restricMeshScale){//仅限制最大或最小的话,不判断像素大小,直接限制mesh的scale
- var dis = camera.position.distanceTo(this.getWorldPosition(new Vector3()));
- if(dis < info.nearBound){
- scale = info.scale * dis / info.nearBound;
- }else {
- scale = info.scale;
- }
- }else {
-
- scale = math.getScaleForConstantSize($.extend(info,{//规定下最小最大像素
- camera , position:this.getWorldPosition(new Vector3()) ,
- resolution: e.viewport.resolution//2
- }));
-
- }
-
- if(!isNaN(scale)){
- this.root.scale.set(scale, scale, scale);
- }
- }
- this.root.updateMatrix();
- this.root.updateMatrixWorld(true);
- this.matrixMap.set(e.viewport, this.root.matrix.clone());
-
- this.useViewport = e.viewport;
- }
-
- applyMatrix(e){
- if(!e)e = {viewport:viewer.mainViewport};//随便写一个viewport
- if(e.viewport.name == 'magnifier')return
- if(this.viewports && !this.viewports.includes(e.viewport) )return
- if(!this.visible || !this.root)return
-
- var matrix = this.matrixMap.get(e.viewport);
-
- if(!matrix){
- this.update(e);
- matrix = this.matrixMap.get(e.viewport);
- }
-
- if(e.viewport == this.useViewport){
- return
- }
- this.useViewport = e.viewport;
- this.root.matrix.copy(matrix);
- this.root.updateMatrixWorld(true);
- //console.log(this.root.name + e.viewport.name + " : "+this.root.matrixWorld.elements)
- }
-
- setUniforms(name,value){
- this.material.setUniforms(name,value);
- }
-
-
- dispose(){
- this.removeAllListeners();
- this.parent && this.parent.remove(this);
- }
- }
- //可能还是要用html写,因为要加按钮和图片
- class TextSprite extends Object3D{
-
- constructor( options={}){
- super();
- let map = new Texture();
- map.minFilter = LinearFilter;
- map.magFilter = LinearFilter;
-
- this.sprite = new Sprite$1({
- sizeInfo:options.sizeInfo,
- renderOrder:options.renderOrder,
- useDepth: options.useDepth,
- map,
- root: this ,
- dontFixOrient: options.dontFixOrient
- });
- this.add(this.sprite);
-
-
- this.rectBorderThick = options.rectBorderThick || 0;
- this.textBorderThick = options.textBorderThick || 0;
- this.fontface = 'Arial';
- this.fontsize = options.fontsize || 16;
- this.textBorderColor = options.textBorderColor || { r: 0, g: 0, b: 0, a: 0.0 };
- this.backgroundColor = options.backgroundColor || { r: 255, g: 255, b: 255, a: 1.0 };
- this.textColor = options.textColor || {r: 0, g: 0, b: 0, a: 1.0};
- this.borderColor = options.borderColor || { r: 0, g: 0, b: 0, a: 0.0 };
- this.borderRadius = options.borderRadius || 6;
- if(options.text != void 0)this.setText(options.text);
- this.name = options.name;
-
- //this.setText(text);
-
-
- this.addEventListener('dispose', this.dispose.bind(this));
- }
- setText(text){
- if (this.text !== text){
- this.text = text + '';
- this.updateTexture();
- }
- }
- setTextColor(color){
- this.textColor = color;
- this.updateTexture();
- }
- setBorderColor(color){
- this.borderColor = color;
- this.updateTexture();
- }
- setBackgroundColor(color){
- this.backgroundColor = color;
- this.updateTexture();
- }
- setPos(pos){
- this.position.copy(pos);
- this.sprite.update();
- }
- update(){
- this.sprite.update();
- }
- setVisible(v){
- this.visible = v;
- }
- setUniforms(name,value){
- this.sprite.setUniforms(name,value);
- }
- updateTexture(){
- let canvas = document.createElement('canvas');
- let context = canvas.getContext('2d');
- context.font = 'Bold ' + this.fontsize + 'px ' + this.fontface;
-
- context["font-weight"] = 100; //语法与 CSS font 属性相同。
- // get size data (height depends only on font size)
-
- //this.text = '啊啊啊啊啊啊fag'
-
- let metrics = context.measureText(this.text );
- let textWidth = metrics.width;
- let margin = new Vector2$1(this.fontsize, this.fontsize*0.4);
- let spriteWidth = 2 * margin.x + textWidth + 2 * this.rectBorderThick;
- let spriteHeight = 2 * margin.y + this.fontsize + 2 * this.rectBorderThick;
- context.canvas.width = spriteWidth;
- context.canvas.height = spriteHeight;
- context.font = 'Bold ' + this.fontsize + 'px ' + this.fontface;
-
- let diff = 2;//针对英文大部分在baseLine之上所以降低一点(metrics.fontBoundingBoxAscent - metrics.fontBoundingBoxDescent) / 2
- context.textBaseline = "middle";
-
- // border color
- context.strokeStyle = 'rgba(' + this.borderColor.r + ',' + this.borderColor.g + ',' +
- this.borderColor.b + ',' + this.borderColor.a + ')';
-
- context.lineWidth = this.rectBorderThick;
- // background color
- context.fillStyle = 'rgba(' + this.backgroundColor.r + ',' + this.backgroundColor.g + ',' +
- this.backgroundColor.b + ',' + this.backgroundColor.a + ')';
- this.roundRect(context, this.rectBorderThick / 2, this.rectBorderThick / 2,
- spriteWidth - this.rectBorderThick, spriteHeight - this.rectBorderThick, this.borderRadius);
-
- // text color
- if(this.textBorderThick){
- context.strokeStyle = 'rgba(' + this.textBorderColor.r + ',' + this.textBorderColor.g + ',' +
- this.textBorderColor.b + ',' + this.textBorderColor.a + ')';
- context.lineWidth = this.textBorderThick;
- context.strokeText(this.text , this.rectBorderThick + margin.x,spriteHeight/2 + diff );
- }
-
- context.fillStyle = 'rgba(' + this.textColor.r + ',' + this.textColor.g + ',' +
- this.textColor.b + ',' + this.textColor.a + ')';
- context.fillText(this.text , this.rectBorderThick + margin.x, spriteHeight/2 + diff );//x,y
- let texture = new Texture(canvas);
- texture.minFilter = LinearFilter;
- texture.magFilter = LinearFilter;
- texture.needsUpdate = true;
- //this.material.needsUpdate = true;
-
- if(this.sprite.material.map){
- this.sprite.material.map.dispose();
- }
- this.sprite.material.map = texture;
-
-
-
- this.sprite.scale.set(spriteWidth * 0.01, spriteHeight * 0.01, 1.0);
- }
- roundRect(ctx, x, y, w, h, r){
- ctx.beginPath();
- ctx.moveTo(x + r, y);
- ctx.lineTo(x + w - r, y);
- ctx.arcTo(x + w, y, x + w, y + r, r );//圆弧。前四个参数同quadraticCurveTo
- //ctx.quadraticCurveTo(x + w, y, x + w, y + r); //二次贝塞尔曲线需要两个点。第一个点是用于二次贝塞尔计算中的控制点,第二个点是曲线的结束点。
- ctx.lineTo(x + w, y + h - r);
- ctx.arcTo(x + w, y + h, x + w - r, y + h, r );
- ctx.lineTo(x + r, y + h);
- ctx.arcTo(x, y + h, x, y + h - r, r );
- ctx.lineTo(x, y + r);
- ctx.arcTo(x, y, x + r, y, r );
- ctx.closePath();
- ctx.fill();
- ctx.stroke();
- }
-
- dispose(){
- this.sprite.material.uniforms.map.value.dispose();
- this.parent && this.parent.remove(this);
- this.sprite.dispatchEvent({type:'dispose'});
- this.removeAllListeners();
- }
- }
- class Volume extends Object3D {
- constructor (args = {}) {
- super();
- if(this.constructor.name === "Volume"){
- console.warn("Can't create object of class Volume directly. Use classes BoxVolume or SphereVolume instead.");
- }
- //console.log(this);
- //console.log(this.constructor);
- //console.log(this.constructor.name);
- this._clip = args.clip || false;
- this._visible = true;
-
- this._modifiable = args.modifiable || true;
-
- { // event listeners
- this.addEventListener('select', e => {});
- this.addEventListener('deselect', e => {});
- }
- }
- get visible(){
- return this._visible;
- }
- set visible(value){
- if(this._visible !== value){
- this._visible = value;
- this.dispatchEvent({type: "visibility_changed", object: this});
- }
- }
- getVolume () {
- console.warn("override this in subclass");
- }
- update () {
-
- };
- raycast (raycaster, intersects) {
- }
- get clip () {
- return this._clip;
- }
- set clip (value) {
- if(this._clip !== value){
- this._clip = value;
- this.update();
- this.dispatchEvent({
- type: "clip_changed",
- object: this
- });
- }
-
- }
- get modifieable () {
- return this._modifiable;
- }
- set modifieable (value) {
- this._modifiable = value;
- this.update();
- }
- };
- class BoxVolume extends Volume{
- constructor(args = {}){
- super(args);
- this.constructor.counter = (this.constructor.counter === undefined) ? 0 : this.constructor.counter + 1;
- this.name = 'box_' + this.constructor.counter;
- let boxGeometry = new BoxGeometry(1, 1, 1);
- boxGeometry.computeBoundingBox();
- let boxFrameGeometry = new Geometry();
- {
- let Vector3$1 = Vector3;
- boxFrameGeometry.vertices.push(
- // bottom
- new Vector3$1(-0.5, -0.5, 0.5),
- new Vector3$1(0.5, -0.5, 0.5),
- new Vector3$1(0.5, -0.5, 0.5),
- new Vector3$1(0.5, -0.5, -0.5),
- new Vector3$1(0.5, -0.5, -0.5),
- new Vector3$1(-0.5, -0.5, -0.5),
- new Vector3$1(-0.5, -0.5, -0.5),
- new Vector3$1(-0.5, -0.5, 0.5),
- // top
- new Vector3$1(-0.5, 0.5, 0.5),
- new Vector3$1(0.5, 0.5, 0.5),
- new Vector3$1(0.5, 0.5, 0.5),
- new Vector3$1(0.5, 0.5, -0.5),
- new Vector3$1(0.5, 0.5, -0.5),
- new Vector3$1(-0.5, 0.5, -0.5),
- new Vector3$1(-0.5, 0.5, -0.5),
- new Vector3$1(-0.5, 0.5, 0.5),
- // sides
- new Vector3$1(-0.5, -0.5, 0.5),
- new Vector3$1(-0.5, 0.5, 0.5),
- new Vector3$1(0.5, -0.5, 0.5),
- new Vector3$1(0.5, 0.5, 0.5),
- new Vector3$1(0.5, -0.5, -0.5),
- new Vector3$1(0.5, 0.5, -0.5),
- new Vector3$1(-0.5, -0.5, -0.5),
- new Vector3$1(-0.5, 0.5, -0.5),
- );
- }
- this.material = new MeshBasicMaterial({
- color: 0x00ff00,
- transparent: true,
- opacity: 0.3,
- depthTest: true,
- depthWrite: false});
- this.box = new Mesh(boxGeometry, this.material);
- this.box.geometry.computeBoundingBox();
- this.boundingBox = this.box.geometry.boundingBox;
- this.add(this.box);
- this.frame = new LineSegments(boxFrameGeometry, new LineBasicMaterial({color: 0x000000}));
- // this.frame.mode = THREE.Lines;
- this.add(this.frame);
-
- viewer.setObjectLayers(this, 'volume' );
- this.update();
- }
- update(){
- this.boundingBox = this.box.geometry.boundingBox;
- this.boundingSphere = this.boundingBox.getBoundingSphere(new Sphere());
- if (this._clip) {
- this.box.visible = false;
-
- } else {
- this.box.visible = true;
-
- }
- }
- raycast (raycaster, intersects) {
- let is = [];
- this.box.raycast(raycaster, is);
- if (is.length > 0) {
- let I = is[0];
- intersects.push({
- distance: I.distance,
- object: this,
- point: I.point.clone()
- });
- }
- }
- getVolume(){
- return Math.abs(this.scale.x * this.scale.y * this.scale.z);
- }
- };
- class SphereVolume extends Volume{
- constructor(args = {}){
- super(args);
- this.constructor.counter = (this.constructor.counter === undefined) ? 0 : this.constructor.counter + 1;
- this.name = 'sphere_' + this.constructor.counter;
- let sphereGeometry = new SphereGeometry(1, 32, 32);
- sphereGeometry.computeBoundingBox();
- this.material = new MeshBasicMaterial({
- color: 0x00ff00,
- transparent: true,
- opacity: 0.3,
- depthTest: true,
- depthWrite: false});
- this.sphere = new Mesh(sphereGeometry, this.material);
- this.sphere.visible = false;
- this.sphere.geometry.computeBoundingBox();
- this.boundingBox = this.sphere.geometry.boundingBox;
- this.add(this.sphere);
-
- let frameGeometry = new Geometry();
- {
- let steps = 64;
- let uSegments = 8;
- let vSegments = 5;
- let r = 1;
- for(let uSegment = 0; uSegment < uSegments; uSegment++){
- let alpha = (uSegment / uSegments) * Math.PI * 2;
- let dirx = Math.cos(alpha);
- let diry = Math.sin(alpha);
- for(let i = 0; i <= steps; i++){
- let v = (i / steps) * Math.PI * 2;
- let vNext = v + 2 * Math.PI / steps;
- let height = Math.sin(v);
- let xyAmount = Math.cos(v);
- let heightNext = Math.sin(vNext);
- let xyAmountNext = Math.cos(vNext);
- let vertex = new Vector3(dirx * xyAmount, diry * xyAmount, height);
- frameGeometry.vertices.push(vertex);
- let vertexNext = new Vector3(dirx * xyAmountNext, diry * xyAmountNext, heightNext);
- frameGeometry.vertices.push(vertexNext);
- }
- }
- // creates rings at poles, just because it's easier to implement
- for(let vSegment = 0; vSegment <= vSegments + 1; vSegment++){
- //let height = (vSegment / (vSegments + 1)) * 2 - 1; // -1 to 1
- let uh = (vSegment / (vSegments + 1)); // -1 to 1
- uh = (1 - uh) * (-Math.PI / 2) + uh *(Math.PI / 2);
- let height = Math.sin(uh);
- console.log(uh, height);
- for(let i = 0; i <= steps; i++){
- let u = (i / steps) * Math.PI * 2;
- let uNext = u + 2 * Math.PI / steps;
- let dirx = Math.cos(u);
- let diry = Math.sin(u);
- let dirxNext = Math.cos(uNext);
- let diryNext = Math.sin(uNext);
- let xyAmount = Math.sqrt(1 - height * height);
- let vertex = new Vector3(dirx * xyAmount, diry * xyAmount, height);
- frameGeometry.vertices.push(vertex);
- let vertexNext = new Vector3(dirxNext * xyAmount, diryNext * xyAmount, height);
- frameGeometry.vertices.push(vertexNext);
- }
- }
- }
- this.frame = new LineSegments(frameGeometry, new LineBasicMaterial({color: 0x000000}));
- this.add(this.frame);
- let frameMaterial = new MeshBasicMaterial({wireframe: true, color: 0x000000});
- this.frame = new Mesh(sphereGeometry, frameMaterial);
- //this.add(this.frame);
- //this.frame = new THREE.LineSegments(boxFrameGeometry, new THREE.LineBasicMaterial({color: 0x000000}));
- // this.frame.mode = THREE.Lines;
- //this.add(this.frame);
- this.update();
- }
- update(){
- this.boundingBox = this.sphere.geometry.boundingBox;
- this.boundingSphere = this.boundingBox.getBoundingSphere(new Sphere());
-
- }
- raycast (raycaster, intersects) {
- let is = [];
- this.sphere.raycast(raycaster, is);
- if (is.length > 0) {
- let I = is[0];
- intersects.push({
- distance: I.distance,
- object: this,
- point: I.point.clone()
- });
- }
- }
-
- // see https://en.wikipedia.org/wiki/Ellipsoid#Volume
- getVolume(){
- return (4 / 3) * Math.PI * this.scale.x * this.scale.y * this.scale.z;
- }
- };
- class Profile extends Object3D{
- constructor () {
- super();
- this.constructor.counter = (this.constructor.counter === undefined) ? 0 : this.constructor.counter + 1;
- this.name = 'Profile_' + this.constructor.counter;
- this.points = [];
- this.spheres = [];
- this.edges = [];
- this.boxes = [];
- this.width = 1;
- this.height = 20;
- this._modifiable = true;
- this.sphereGeometry = new SphereGeometry(0.4, 10, 10);
- this.color = new Color(0xff0000);
- this.lineColor = new Color(0xff0000);
- }
- createSphereMaterial () {
- let sphereMaterial = new MeshLambertMaterial({
- //shading: THREE.SmoothShading,
- color: 0xff0000,
- depthTest: false,
- depthWrite: false}
- );
- return sphereMaterial;
- };
- getSegments () {
- let segments = [];
- for (let i = 0; i < this.points.length - 1; i++) {
- let start = this.points[i].clone();
- let end = this.points[i + 1].clone();
- segments.push({start: start, end: end});
- }
- return segments;
- }
- getSegmentMatrices () {
- let segments = this.getSegments();
- let matrices = [];
- for (let segment of segments) {
- let {start, end} = segment;
- let box = new Object3D();
- let length;
- if(window.axisYup){
- length = start.clone().setY(0).distanceTo(end.clone().setY(0));
- box.scale.set(length, 10000, this.width); //???
- }else {
- length = start.clone().setZ(0).distanceTo(end.clone().setZ(0));
- box.scale.set(length, 10000, this.width);
- box.up.set(0, 0, 1);
- }
- let center = new Vector3().addVectors(start, end).multiplyScalar(0.5);
- let diff = new Vector3().subVectors(end, start);
- let target = new Vector3(diff.y, -diff.x, 0);
- box.position.set(0, 0, 0);
- box.lookAt(target);
- box.position.copy(center);
- box.updateMatrixWorld();
- matrices.push(box.matrixWorld);
- }
- return matrices;
- }
- addMarker (point) {
- this.points.push(point);
- let sphere = new Mesh(this.sphereGeometry, this.createSphereMaterial());
- this.add(sphere);
- this.spheres.push(sphere);
- // edges & boxes
- if (this.points.length > 1) {
- let lineGeometry = new Geometry();
- lineGeometry.vertices.push(new Vector3(), new Vector3());
- lineGeometry.colors.push(this.lineColor, this.lineColor, this.lineColor);
- let lineMaterial = new LineBasicMaterial({
- vertexColors: VertexColors,
- lineWidth: 2,
- transparent: true,
- opacity: 0.4
- });
- lineMaterial.depthTest = false;
- let edge = new Line(lineGeometry, lineMaterial);
- edge.visible = false;
- this.add(edge);
- this.edges.push(edge);
- let boxGeometry = new BoxGeometry(1, 1, 1);
- let boxMaterial = new MeshBasicMaterial({color: 0xff0000, transparent: true, opacity: 0.2});
- let box = new Mesh(boxGeometry, boxMaterial);
- box.visible = false;
- this.add(box);
- this.boxes.push(box);
- }
- { // event listeners
- let drag = (e) => {
- let I = Utils.getMousePointCloudIntersection(
- e.drag.end,
- e.viewer.scene.getActiveCamera(),
- e.viewer,
- e.viewer.scene.pointclouds);
- if (I) {
- let i = this.spheres.indexOf(e.drag.object);
- if (i !== -1) {
- this.setPosition(i, I.location);
- //this.dispatchEvent({
- // 'type': 'marker_moved',
- // 'profile': this,
- // 'index': i
- //});
- }
- }
- };
- let drop = e => {
- let i = this.spheres.indexOf(e.drag.object);
- if (i !== -1) {
- this.dispatchEvent({
- 'type': 'marker_dropped',
- 'profile': this,
- 'index': i
- });
- }
- };
- let mouseover = (e) => e.object.material.emissive.setHex(0x888888);
- let mouseleave = (e) => e.object.material.emissive.setHex(0x000000);
- sphere.addEventListener('drag', drag);
- sphere.addEventListener('drop', drop);
- sphere.addEventListener('mouseover', mouseover);
- sphere.addEventListener('mouseleave', mouseleave);
- }
- let event = {
- type: 'marker_added',
- profile: this,
- sphere: sphere
- };
- this.dispatchEvent(event);
- this.setPosition(this.points.length - 1, point);
- }
- removeMarker (index) {
- this.points.splice(index, 1);
- this.remove(this.spheres[index]);
- let edgeIndex = (index === 0) ? 0 : (index - 1);
- this.remove(this.edges[edgeIndex]);
- this.edges.splice(edgeIndex, 1);
- this.remove(this.boxes[edgeIndex]);
- this.boxes.splice(edgeIndex, 1);
- this.spheres.splice(index, 1);
- this.update();
- this.dispatchEvent({
- 'type': 'marker_removed',
- 'profile': this
- });
- }
- setPosition (index, position) {
- let point = this.points[index];
- point.copy(position);
- let event = {
- type: 'marker_moved',
- profile: this,
- index: index,
- position: point.clone()
- };
- this.dispatchEvent(event);
- this.update();
- }
- setWidth (width) {
- this.width = width;
- let event = {
- type: 'width_changed',
- profile: this,
- width: width
- };
- this.dispatchEvent(event);
- this.update();
- }
- getWidth () {
- return this.width;
- }
- update () {
- if (this.points.length === 0) {
- return;
- } else if (this.points.length === 1) {
- let point = this.points[0];
- this.spheres[0].position.copy(point);
- return;
- }
- let min = this.points[0].clone();
- let max = this.points[0].clone();
- let centroid = new Vector3();
- let lastIndex = this.points.length - 1;
- for (let i = 0; i <= lastIndex; i++) {
- let point = this.points[i];
- let sphere = this.spheres[i];
- let leftIndex = (i === 0) ? lastIndex : i - 1;
- // let rightIndex = (i === lastIndex) ? 0 : i + 1;
- let leftVertex = this.points[leftIndex];
- // let rightVertex = this.points[rightIndex];
- let leftEdge = this.edges[leftIndex];
- let rightEdge = this.edges[i];
- let leftBox = this.boxes[leftIndex];
- // rightBox = this.boxes[i];
- // let leftEdgeLength = point.distanceTo(leftVertex);
- // let rightEdgeLength = point.distanceTo(rightVertex);
- // let leftEdgeCenter = new THREE.Vector3().addVectors(leftVertex, point).multiplyScalar(0.5);
- // let rightEdgeCenter = new THREE.Vector3().addVectors(point, rightVertex).multiplyScalar(0.5);
- sphere.position.copy(point);
- if (this._modifiable) {
- sphere.visible = true;
- } else {
- sphere.visible = false;
- }
- if (leftEdge) {
- leftEdge.geometry.vertices[1].copy(point);
- leftEdge.geometry.verticesNeedUpdate = true;
- leftEdge.geometry.computeBoundingSphere();
- }
- if (rightEdge) {
- rightEdge.geometry.vertices[0].copy(point);
- rightEdge.geometry.verticesNeedUpdate = true;
- rightEdge.geometry.computeBoundingSphere();
- }
- if (leftBox) {
- let start = leftVertex;
- let end = point;
- let length;
- if(window.axisYup){
- length = start.clone().setY(0).distanceTo(end.clone().setY(0));
- }else {
- length = start.clone().setZ(0).distanceTo(end.clone().setZ(0));
- leftBox.up.set(0, 0, 1);
- }
-
- leftBox.scale.set(length, 1000000, this.width);
-
- let center = new Vector3().addVectors(start, end).multiplyScalar(0.5);
- let diff = new Vector3().subVectors(end, start);
- let target = new Vector3(diff.y, -diff.x, 0);
- leftBox.position.set(0, 0, 0);
- leftBox.lookAt(target);
- leftBox.position.copy(center);
- }
- centroid.add(point);
- min.min(point);
- max.max(point);
- }
- centroid.multiplyScalar(1 / this.points.length);
- for (let i = 0; i < this.boxes.length; i++) {
- let box = this.boxes[i];
- box.position.z = min.z + (max.z - min.z) / 2;
- }
- }
- raycast (raycaster, intersects) {
- for (let i = 0; i < this.points.length; i++) {
- let sphere = this.spheres[i];
- sphere.raycast(raycaster, intersects);
- }
- // recalculate distances because they are not necessarely correct
- // for scaled objects.
- // see https://github.com/mrdoob/three.js/issues/5827
- // TODO: remove this once the bug has been fixed
- for (let i = 0; i < intersects.length; i++) {
- let I = intersects[i];
- I.distance = raycaster.ray.origin.distanceTo(I.point);
- }
- intersects.sort(function (a, b) { return a.distance - b.distance; });
- };
- get modifiable () {
- return this._modifiable;
- }
- set modifiable (value) {
- this._modifiable = value;
- this.update();
- }
- }
- class Label extends EventDispatcher{
- constructor(o={}){
- super();
-
- this.position = o.pos;
- this.text = o.text || '';
- this.elem = $('<div class="hide"><a></a></div>');
- o.className && this.elem.addClass(o.className);
- this.elem.find('a').html(this.text);
- $("#potree_labels").append(this.elem);
- this.pos2d = new Vector3;
- this.dom = o.dom || viewer.renderArea;
- this.camera = o.camera || viewer.scene.getActiveCamera();
-
-
- let update = (e)=>{
- this.update(e);
- };
- viewer.addEventListener('camera_changed', update);
-
-
- this.addEventListener('dispose', ()=>{
- viewer.removeEventListener('camera_changed', update);
- this.dispose();
-
- });
-
- }
-
- update(){
- if(!this.position || this.elem.hasClass('unvisible'))return
- var p = Utils.getPos2d(this.position,this.camera,this.dom, viewer.mainViewport);
- if(!p.trueSide){
- this.elem.addClass("hide"); return;
- }
- this.elem.css({
- left: p.pos.x +'px',
- top: p.pos.y +'px'
- });
-
-
-
- this.elem.removeClass("hide");
- this.pos2d = p.vector;
-
-
-
-
-
- }
-
- setVisible(visi){
- if(!visi){
- this.elem.addClass("unvisible");
- }else {
- this.elem.removeClass("unvisible");
- this.update();
- }
- }
-
- setText(text){
- this.text = text || '';
- this.elem.find('a').html(this.text);
- }
- setPos(pos){
- this.position = pos;
- }
-
- dispose(){
- this.elem.remove();
- this.removeAllListeners();
- }
-
-
- }
- const _box$4 = new Box3();
- const _vector$d = new Vector3();
- class LineSegmentsGeometry extends InstancedBufferGeometry {
- constructor() {
- super();
- this.isLineSegmentsGeometry = true;
- this.type = 'LineSegmentsGeometry';
- const positions = [ - 1, 2, 0, 1, 2, 0, - 1, 1, 0, 1, 1, 0, - 1, 0, 0, 1, 0, 0, - 1, - 1, 0, 1, - 1, 0 ];
- const uvs = [ - 1, 2, 1, 2, - 1, 1, 1, 1, - 1, - 1, 1, - 1, - 1, - 2, 1, - 2 ];
- const index = [ 0, 2, 1, 2, 3, 1, 2, 4, 3, 4, 5, 3, 4, 6, 5, 6, 7, 5 ];
- this.setIndex( index );
- this.setAttribute( 'position', new Float32BufferAttribute( positions, 3 ) );
- this.setAttribute( 'uv', new Float32BufferAttribute( uvs, 2 ) );
- }
- applyMatrix4( matrix ) {
- const start = this.attributes.instanceStart;
- const end = this.attributes.instanceEnd;
- if ( start !== undefined ) {
- start.applyMatrix4( matrix );
- end.applyMatrix4( matrix );
- start.needsUpdate = true;
- }
- if ( this.boundingBox !== null ) {
- this.computeBoundingBox();
- }
- if ( this.boundingSphere !== null ) {
- this.computeBoundingSphere();
- }
- return this;
- }
- setPositions( array ) {
- let lineSegments;
- if ( array instanceof Float32Array ) {
- lineSegments = array;
- } else if ( Array.isArray( array ) ) {
- lineSegments = new Float32Array( array );
- }
- const instanceBuffer = new InstancedInterleavedBuffer( lineSegments, 6, 1 ); // xyz, xyz
- this.setAttribute( 'instanceStart', new InterleavedBufferAttribute( instanceBuffer, 3, 0 ) ); // xyz
- this.setAttribute( 'instanceEnd', new InterleavedBufferAttribute( instanceBuffer, 3, 3 ) ); // xyz
- //
- this.computeBoundingBox();
- this.computeBoundingSphere();
- return this;
- }
- setColors( array ) {
- let colors;
- if ( array instanceof Float32Array ) {
- colors = array;
- } else if ( Array.isArray( array ) ) {
- colors = new Float32Array( array );
- }
- const instanceColorBuffer = new InstancedInterleavedBuffer( colors, 6, 1 ); // rgb, rgb
- this.setAttribute( 'instanceColorStart', new InterleavedBufferAttribute( instanceColorBuffer, 3, 0 ) ); // rgb
- this.setAttribute( 'instanceColorEnd', new InterleavedBufferAttribute( instanceColorBuffer, 3, 3 ) ); // rgb
- return this;
- }
- fromWireframeGeometry( geometry ) {
- this.setPositions( geometry.attributes.position.array );
- return this;
- }
- fromEdgesGeometry( geometry ) {
- this.setPositions( geometry.attributes.position.array );
- return this;
- }
- fromMesh( mesh ) {
- this.fromWireframeGeometry( new WireframeGeometry( mesh.geometry ) );
- // set colors, maybe
- return this;
- }
- fromLineSegments( lineSegments ) {
- const geometry = lineSegments.geometry;
- this.setPositions( geometry.attributes.position.array ); // assumes non-indexed
- // set colors, maybe
- return this;
- }
- computeBoundingBox() {
- if ( this.boundingBox === null ) {
- this.boundingBox = new Box3();
- }
- const start = this.attributes.instanceStart;
- const end = this.attributes.instanceEnd;
- if ( start !== undefined && end !== undefined ) {
- this.boundingBox.setFromBufferAttribute( start );
- _box$4.setFromBufferAttribute( end );
- this.boundingBox.union( _box$4 );
- }
- }
- computeBoundingSphere() {
- if ( this.boundingSphere === null ) {
- this.boundingSphere = new Sphere();
- }
- if ( this.boundingBox === null ) {
- this.computeBoundingBox();
- }
- const start = this.attributes.instanceStart;
- const end = this.attributes.instanceEnd;
- if ( start !== undefined && end !== undefined ) {
- const center = this.boundingSphere.center;
- this.boundingBox.getCenter( center );
- let maxRadiusSq = 0;
- for ( let i = 0, il = start.count; i < il; i ++ ) {
- _vector$d.fromBufferAttribute( start, i );
- maxRadiusSq = Math.max( maxRadiusSq, center.distanceToSquared( _vector$d ) );
- _vector$d.fromBufferAttribute( end, i );
- maxRadiusSq = Math.max( maxRadiusSq, center.distanceToSquared( _vector$d ) );
- }
- this.boundingSphere.radius = Math.sqrt( maxRadiusSq );
- if ( isNaN( this.boundingSphere.radius ) ) {
- console.error( 'THREE.LineSegmentsGeometry.computeBoundingSphere(): Computed radius is NaN. The instanced position data is likely to have NaN values.', this );
- }
- }
- }
- toJSON() {
- // todo
- }
- applyMatrix( matrix ) {
- console.warn( 'THREE.LineSegmentsGeometry: applyMatrix() has been renamed to applyMatrix4().' );
- return this.applyMatrix4( matrix );
- }
- }
- /**
- * parameters = {
- * color: <hex>,
- * lineWidth: <float>,
- * dashed: <boolean>,
- * dashScale: <float>,
- * dashSize: <float>,
- * dashOffset: <float>,
- * gapSize: <float>,
- * resolution: <Vector2>, // to be set by renderer
- * }
- */
- UniformsLib.line = {
- /*
- worldUnits: { value: 1 },
- lineWidth: { value: 1 },
- resolution: { value: new Vector2( 1, 1 ) },
- dashOffset: { value: 0 },
- dashScale: { value: 1 },
- dashSize: { value: 1 },
- gapSize: { value: 1 } // todo FIX - maybe change to totalSize
- */
- worldUnits: { value: 1 },
- lineWidth: { value: 1 },
- resolution: { value: new Vector2$1( 1, 1 ) },
- viewportOffset: { value: new Vector2$1(0, 0 ) }, //left, top
- dashScale: { value: 1 },
- dashSize: { value: 1 },
- dashOffset: { value: 0 },
- gapSize: { value: 1 },
- opacity: { value: 1 },
-
- backColor: {type:'v3', value: new Color("#ddd")},
- clipDistance : { type: 'f', value: 4}, //消失距离
- occlusionDistance : { type: 'f', value: 1 }, //变为backColor距离
- maxClipFactor : { type: 'f', value: 1 }, //0-1
-
-
-
- depthTexture:{ value: null },
- nearPlane:{value: 0.1},
- farPlane:{value: 100000},
- };
- ShaderLib[ 'line' ] = {
- uniforms: UniformsUtils.merge( [
- UniformsLib.common,
- UniformsLib.fog,
- UniformsLib.line
- ] ),
- vertexShader:
- /* glsl */`
- #include <common>
- #include <color_pars_vertex>
- #include <fog_pars_vertex>
- #include <logdepthbuf_pars_vertex>
- #include <clipping_planes_pars_vertex>
- uniform float lineWidth;
- uniform vec2 resolution;
- attribute vec3 instanceStart;
- attribute vec3 instanceEnd;
- attribute vec3 instanceColorStart;
- attribute vec3 instanceColorEnd;
- #ifdef WORLD_UNITS
- varying vec4 worldPos;
- varying vec3 worldStart;
- varying vec3 worldEnd;
- #ifdef USE_DASH
- varying vec2 vUv;
- #endif
- #else
- varying vec2 vUv;
- #endif
- #ifdef USE_DASH
- uniform float dashScale;
- attribute float instanceDistanceStart;
- attribute float instanceDistanceEnd;
- varying float vLineDistance;
- #endif
- void trimSegment( const in vec4 start, inout vec4 end ) {
- // trim end segment so it terminates between the camera plane and the near plane
- // conservative estimate of the near plane
- float a = projectionMatrix[ 2 ][ 2 ]; // 3nd entry in 3th column
- float b = projectionMatrix[ 3 ][ 2 ]; // 3nd entry in 4th column
- float nearEstimate = - 0.5 * b / a;
- float alpha = ( nearEstimate - start.z ) / ( end.z - start.z );
- end.xyz = mix( start.xyz, end.xyz, alpha );
- }
- void main() {
- #ifdef USE_COLOR
- vColor.xyz = ( position.y < 0.5 ) ? instanceColorStart : instanceColorEnd;
- #endif
- #ifdef USE_DASH
- vLineDistance = ( position.y < 0.5 ) ? dashScale * instanceDistanceStart : dashScale * instanceDistanceEnd;
- vUv = uv;
- #endif
- float aspect = resolution.x / resolution.y;
- // camera space
- vec4 start = modelViewMatrix * vec4( instanceStart, 1.0 );
- vec4 end = modelViewMatrix * vec4( instanceEnd, 1.0 );
- #ifdef WORLD_UNITS
- worldStart = start.xyz;
- worldEnd = end.xyz;
- #else
- vUv = uv;
- #endif
- // special case for perspective projection, and segments that terminate either in, or behind, the camera plane
- // clearly the gpu firmware has a way of addressing this issue when projecting into ndc space
- // but we need to perform ndc-space calculations in the shader, so we must address this issue directly
- // perhaps there is a more elegant solution -- WestLangley
- bool perspective = ( projectionMatrix[ 2 ][ 3 ] == - 1.0 ); // 4th entry in the 3rd column
- if ( perspective ) {
- if ( start.z < 0.0 && end.z >= 0.0 ) {
- trimSegment( start, end );
- } else if ( end.z < 0.0 && start.z >= 0.0 ) {
- trimSegment( end, start );
- }
- }
- // clip space
- vec4 clipStart = projectionMatrix * start;
- vec4 clipEnd = projectionMatrix * end;
- // ndc space
- vec3 ndcStart = clipStart.xyz / clipStart.w;
- vec3 ndcEnd = clipEnd.xyz / clipEnd.w;
- // direction
- vec2 dir = ndcEnd.xy - ndcStart.xy;
- // account for clip-space aspect ratio
- dir.x *= aspect;
- dir = normalize( dir );
- #ifdef WORLD_UNITS
- // get the offset direction as perpendicular to the view vector
- vec3 worldDir = normalize( end.xyz - start.xyz );
- vec3 offset;
- if ( position.y < 0.5 ) {
- offset = normalize( cross( start.xyz, worldDir ) );
- } else {
- offset = normalize( cross( end.xyz, worldDir ) );
- }
- // sign flip
- if ( position.x < 0.0 ) offset *= - 1.0;
- float forwardOffset = dot( worldDir, vec3( 0.0, 0.0, 1.0 ) );
- // don't extend the line if we're rendering dashes because we
- // won't be rendering the endcaps
- #ifndef USE_DASH
- // extend the line bounds to encompass endcaps
- start.xyz += - worldDir * lineWidth * 0.5;
- end.xyz += worldDir * lineWidth * 0.5;
- // shift the position of the quad so it hugs the forward edge of the line
- offset.xy -= dir * forwardOffset;
- offset.z += 0.5;
- #endif
- // endcaps
- if ( position.y > 1.0 || position.y < 0.0 ) {
- offset.xy += dir * 2.0 * forwardOffset;
- }
- // adjust for lineWidth
- offset *= lineWidth * 0.5;
- // set the world position
- worldPos = ( position.y < 0.5 ) ? start : end;
- worldPos.xyz += offset;
- // project the worldpos
- vec4 clip = projectionMatrix * worldPos;
- // shift the depth of the projected points so the line
- // segments overlap neatly
- vec3 clipPose = ( position.y < 0.5 ) ? ndcStart : ndcEnd;
- clip.z = clipPose.z * clip.w;
- #else
- vec2 offset = vec2( dir.y, - dir.x );
- // undo aspect ratio adjustment
- dir.x /= aspect;
- offset.x /= aspect;
- // sign flip
- if ( position.x < 0.0 ) offset *= - 1.0;
- // endcaps
- if ( position.y < 0.0 ) {
- offset += - dir;
- } else if ( position.y > 1.0 ) {
- offset += dir;
- }
- // adjust for lineWidth
- offset *= lineWidth;
- // adjust for clip-space to screen-space conversion // maybe resolution should be based on viewport ...
- offset /= resolution.y;
- // select end
- vec4 clip = ( position.y < 0.5 ) ? clipStart : clipEnd;
- // back to clip space
- offset *= clip.w;
- clip.xy += offset;
- #endif
- gl_Position = clip;
- vec4 mvPosition = ( position.y < 0.5 ) ? start : end; // this is an approximation
- #include <logdepthbuf_vertex>
- #include <clipping_planes_vertex>
- #include <fog_vertex>
- }
- `,
- fragmentShader:
- /* glsl */`
- uniform vec3 diffuse;
- uniform float opacity;
- uniform float lineWidth;
- uniform vec3 backColor;
- uniform float occlusionDistance;
- uniform float clipDistance;
- uniform float maxClipFactor;
- #ifdef USE_DASH
- uniform float dashOffset;
- uniform float dashSize;
- uniform float gapSize;
- #endif
- //加
- #if defined(GL_EXT_frag_depth) && defined(useDepth)
- uniform sampler2D depthTexture;
- uniform float nearPlane;
- uniform float farPlane;
- uniform vec2 resolution;
- uniform vec2 viewportOffset;
- #endif
- varying float vLineDistance;
- #ifdef WORLD_UNITS
- varying vec4 worldPos;
- varying vec3 worldStart;
- varying vec3 worldEnd;
- #ifdef USE_DASH
- varying vec2 vUv;
- #endif
- #else
- varying vec2 vUv;
- #endif
- #include <common>
- #include <color_pars_fragment>
- #include <fog_pars_fragment>
- #include <logdepthbuf_pars_fragment>
- #include <clipping_planes_pars_fragment>
- #if defined(GL_EXT_frag_depth) && defined(useDepth)
- float convertToLinear(float zValue)
- {
- float z = zValue * 2.0 - 1.0;
- return (2.0 * nearPlane * farPlane) / (farPlane + nearPlane - z * (farPlane - nearPlane));
- }
- #endif
- vec2 closestLineToLine(vec3 p1, vec3 p2, vec3 p3, vec3 p4) {
- float mua;
- float mub;
- vec3 p13 = p1 - p3;
- vec3 p43 = p4 - p3;
- vec3 p21 = p2 - p1;
- float d1343 = dot( p13, p43 );
- float d4321 = dot( p43, p21 );
- float d1321 = dot( p13, p21 );
- float d4343 = dot( p43, p43 );
- float d2121 = dot( p21, p21 );
- float denom = d2121 * d4343 - d4321 * d4321;
- float numer = d1343 * d4321 - d1321 * d4343;
- mua = numer / denom;
- mua = clamp( mua, 0.0, 1.0 );
- mub = ( d1343 + d4321 * ( mua ) ) / d4343;
- mub = clamp( mub, 0.0, 1.0 );
- return vec2( mua, mub );
- }
- void main() {
- #include <clipping_planes_fragment>
- /*#ifdef USE_DASH
- if ( vUv.y < - 1.0 || vUv.y > 1.0 ) discard; // discard endcaps
- if ( mod( vLineDistance + dashOffset, dashSize + gapSize ) > dashSize ) discard; // todo - FIX
- #endif*/
-
- #ifdef USE_DASH
- if ( vUv.y < - 1.0 || vUv.y > 1.0 ) discard; // discard endcaps
-
-
- bool unvisible = mod( vLineDistance + dashOffset, dashSize + gapSize ) > dashSize;
- //加
- #ifdef DASH_with_depth
-
- #else
- if (unvisible) discard; // todo - FIX
- #endif
- #endif
-
- float alpha = opacity;
- #ifdef WORLD_UNITS
- // Find the closest points on the view ray and the line segment
- vec3 rayEnd = normalize( worldPos.xyz ) * 1e5;
- vec3 lineDir = worldEnd - worldStart;
- vec2 params = closestLineToLine( worldStart, worldEnd, vec3( 0.0, 0.0, 0.0 ), rayEnd );
- vec3 p1 = worldStart + lineDir * params.x;
- vec3 p2 = rayEnd * params.y;
- vec3 delta = p1 - p2;
- float len = length( delta );
- float norm = len / lineWidth;
- #ifndef USE_DASH
- #ifdef USE_ALPHA_TO_COVERAGE
- float dnorm = fwidth( norm );
- alpha = 1.0 - smoothstep( 0.5 - dnorm, 0.5 + dnorm, norm );
- #else
- if ( norm > 0.5 ) {
- discard;
- }
- #endif
- #endif
- #else
- #ifdef USE_ALPHA_TO_COVERAGE
- // artifacts appear on some hardware if a derivative is taken within a conditional
- float a = vUv.x;
- float b = ( vUv.y > 0.0 ) ? vUv.y - 1.0 : vUv.y + 1.0;
- float len2 = a * a + b * b;
- float dlen = fwidth( len2 );
- if ( abs( vUv.y ) > 1.0 ) {
- alpha = 1.0 - smoothstep( 1.0 - dlen, 1.0 + dlen, len2 );
- }
- #else
- if ( abs( vUv.y ) > 1.0 ) {
- float a = vUv.x;
- float b = ( vUv.y > 0.0 ) ? vUv.y - 1.0 : vUv.y + 1.0;
- float len2 = a * a + b * b;
- if ( len2 > 1.0 ) discard;
- }
- #endif
- #endif
- vec4 diffuseColor = vec4( diffuse, alpha );
- //加
- #if defined(GL_EXT_frag_depth) && defined(useDepth)
-
- float mixFactor = 0.0;
- float clipFactor = 0.0;
- float fragDepth = convertToLinear(gl_FragCoord.z);
- vec2 depthTxtCoords = vec2(gl_FragCoord.x - viewportOffset.x, gl_FragCoord.y - viewportOffset.y) / resolution;
- float textureDepth = convertToLinear(texture2D(depthTexture, depthTxtCoords).r);
- float delta = fragDepth - textureDepth;
- if (delta > 0.0)
- {
-
- mixFactor = clamp(delta / occlusionDistance, 0.0, 1.0);
- clipFactor = clamp(delta / clipDistance, 0.0, maxClipFactor);
- }
-
- if (clipFactor == 1.0)
- {
- discard;
- }
-
- vec4 backColor_ = vec4(backColor, opacity); //vec4(0.8,0.8,0.8, 0.8*opacity);
-
- #ifdef DASH_with_depth
- // 只在被遮住的部分显示虚线, 所以若同时是虚线不可见部分和被遮住时, a为0
- if(unvisible) backColor_.a = 0.0;
- #endif
-
- //vec4 diffuseColor = vec4(mix(diffuse, backColor_, mixFactor), opacity*(1.0 - clipFactor));
-
-
-
- diffuseColor = mix(diffuseColor, backColor_ , mixFactor);
-
-
- diffuseColor.a *= (1.0 - clipFactor);
-
- #endif
- #include <logdepthbuf_fragment>
- #include <color_fragment>
- //gl_FragColor = vec4( diffuseColor.rgb, alpha );
- gl_FragColor = vec4( diffuseColor.rgb, diffuseColor.a );
- #include <tonemapping_fragment>
- #include <encodings_fragment>
- #include <fog_fragment>
- #include <premultiplied_alpha_fragment>
- }
- `
- };
- class LineMaterial extends ShaderMaterial {
- constructor( parameters ) {
- super( {
- type: 'LineMaterial',
- uniforms: UniformsUtils.clone( ShaderLib[ 'line' ].uniforms ),
- vertexShader: ShaderLib[ 'line' ].vertexShader,
- fragmentShader: ShaderLib[ 'line' ].fragmentShader,
- clipping: true // required for clipping support
- } );
- this.isLineMaterial = true;
- this.lineWidth_ = 0;
- this.supportExtDepth = parameters.supportExtDepth;
- this.depthTestWhenPick = false; //pick时是否识别点云等
-
-
- if(parameters.color){
- this.color = new Color(parameters.color);
- }
- if(parameters.backColor){
- this.uniforms.backColor.value = new Color(parameters.backColor);
- }
- if(parameters.clipDistance){
- this.uniforms.clipDistance.value = parameters.clipDistance;
- }
- if(parameters.occlusionDistance){
- this.uniforms.occlusionDistance.value = parameters.occlusionDistance;
- }
- if(parameters.maxClipFactor){
- this.uniforms.maxClipFactor.value = parameters.maxClipFactor;
- }
-
- Object.defineProperties( this, {
- color: {
- enumerable: true,
- get: function () {
- return this.uniforms.diffuse.value;
- },
- set: function ( value ) {
- this.uniforms.diffuse.value = value;
- }
- },
- worldUnits: {
- enumerable: true,
- get: function () {
- return 'WORLD_UNITS' in this.defines;
- },
- set: function ( value ) {
- if ( value === true ) {
- this.defines.WORLD_UNITS = '';
- } else {
- delete this.defines.WORLD_UNITS;
- }
- }
- },
- lineWidth: {
- enumerable: true,
- get: function () {
- return this.lineWidth_;//this.uniforms.lineWidth.value;
- },
- set: function ( value ) {
- this.uniforms.lineWidth.value = value * window.devicePixelRatio;
- this.lineWidth_ = value;
- }
- },
- dashed: {
- enumerable: true,
- get: function () {
- return Boolean( 'USE_DASH' in this.defines );
- },
- set( value ) {
- if ( Boolean( value ) !== Boolean( 'USE_DASH' in this.defines ) ) {
- this.needsUpdate = true;
- }
- if ( value === true ) {
- this.defines.USE_DASH = '';
- } else {
- delete this.defines.USE_DASH;
- }
- }
- },
- dashScale: {
- enumerable: true,
- get: function () {
- return this.uniforms.dashScale.value;
- },
- set: function ( value ) {
- this.uniforms.dashScale.value = value;
- }
- },
- dashSize: {
- enumerable: true,
- get: function () {
- return this.uniforms.dashSize.value;
- },
- set: function ( value ) {
- this.uniforms.dashSize.value = value;
- }
- },
- dashOffset: {
- enumerable: true,
- get: function () {
- return this.uniforms.dashOffset.value;
- },
- set: function ( value ) {
- this.uniforms.dashOffset.value = value;
- }
- },
- gapSize: {
- enumerable: true,
- get: function () {
- return this.uniforms.gapSize.value;
- },
- set: function ( value ) {
- this.uniforms.gapSize.value = value;
- }
- },
- opacity: {
- enumerable: true,
- get: function () {
- return this.uniforms.opacity.value;
- },
- set: function ( value ) {
- this.uniforms.opacity.value = value;
- }
- },
- resolution: {
- enumerable: true,
- get: function () {
- return this.uniforms.resolution.value;
- },
- set: function ( value ) {
- this.uniforms.resolution.value.copy( value );
- }
- },
- alphaToCoverage: {
- enumerable: true,
- get: function () {
- return Boolean( 'USE_ALPHA_TO_COVERAGE' in this.defines );
- },
- set: function ( value ) {
- if ( Boolean( value ) !== Boolean( 'USE_ALPHA_TO_COVERAGE' in this.defines ) ) {
- this.needsUpdate = true;
- }
- if ( value === true ) {
- this.defines.USE_ALPHA_TO_COVERAGE = '';
- this.extensions.derivatives = true;
- } else {
- delete this.defines.USE_ALPHA_TO_COVERAGE;
- this.extensions.derivatives = false;
- }
- }
- }
- ,
-
- useDepth:{//add
- enumerable: true,
- get: function () {
- return 'useDepth' in this.defines
- },
- set: function ( value ) {
-
- value = value && !!this.supportExtDepth;
-
- if(value != this.useDepth){
- if(value){
- this.defines.useDepth = '';
- this.updateDepthParams();
- }else {
- delete this.defines.useDepth;
- }
- this.needsUpdate = true;
- }
- }
- },
-
- dashWithDepth:{//add
- enumerable: true,
- get: function () {
- return 'DASH_with_depth' in this.defines
- },
- set: function ( value ) {
-
- value = value && !!this.supportExtDepth;
-
-
- if(value != this.dashWithDepth){
- if(value){
- this.defines.DASH_with_depth = '';
- }else {
- delete this.defines.DASH_with_depth;
- }
- this.needsUpdate = true;
- }
- }
- },
-
-
-
- } );
- this.setValues( parameters );
-
-
-
- let setSize = (e)=>{
- let viewport = e.viewport;
- let viewportOffset = viewport.offset || new Vector2$1();
- this.uniforms.resolution.value.copy(viewport.resolution2);
- this.uniforms.viewportOffset.value.copy(viewportOffset);
- this.lineWidth = this.lineWidth_;
- };
-
- let viewport = viewer.mainViewport;
-
- setSize({viewport});
- viewer.addEventListener('resize',(e)=>{
- setSize(e);
- });
-
-
- if(this.supportExtDepth){
-
- //add
- this.updateDepthParams();
-
- /* viewer.addEventListener('camera_changed', (e)=>{
- if(e.viewport.name != 'mapViewport') this.updateDepthParams(e)
- }) */
-
-
- viewer.addEventListener("render.begin", (e)=>{//before render 如果有大于两个viewport的话,不同viewport用不同的depthTex
- if(e.viewport.camera.isPerspectiveCamera) this.updateDepthParams(e);
- });
- }
-
- }
-
- updateDepthParams(e={}){
-
- if(this.useDepth){
- var viewport = e.viewport || viewer.mainViewport;
- var camera = viewport.camera;
- this.uniforms.depthTexture.value = viewer.getPRenderer().getRtEDL(viewport).depthTexture; //viewer.getPRenderer().rtEDL.depthTexture
- this.uniforms.nearPlane.value = camera.near; //似乎因为这个所以对OrthographicCamera 无效
- this.uniforms.farPlane.value = camera.far;
- }
-
- }
- }
- const _start$2 = new Vector3();
- const _end$2 = new Vector3();
- const _start4 = new Vector4();
- const _end4 = new Vector4();
- const _ssOrigin = new Vector4();
- const _ssOrigin3 = new Vector3();
- const _mvMatrix = new Matrix4();
- const _line = new Line3();
- const _closestPoint = new Vector3();
- const _box$5 = new Box3();
- const _sphere$4 = new Sphere();
- const _clipToWorldVector = new Vector4();
- let _ray$3, _instanceStart, _instanceEnd, _lineWidth;
- // Returns the margin required to expand by in world space given the distance from the camera,
- // line width, resolution, and camera projection
- function getWorldSpaceHalfWidth( camera, distance, resolution ) {
- // transform into clip space, adjust the x and y values by the pixel width offset, then
- // transform back into world space to get world offset. Note clip space is [-1, 1] so full
- // width does not need to be halved.
- _clipToWorldVector.set( 0, 0, - distance, 1.0 ).applyMatrix4( camera.projectionMatrix );
- _clipToWorldVector.multiplyScalar( 1.0 / _clipToWorldVector.w );
- _clipToWorldVector.x = _lineWidth / resolution.width;
- _clipToWorldVector.y = _lineWidth / resolution.height;
- _clipToWorldVector.applyMatrix4( camera.projectionMatrixInverse );
- _clipToWorldVector.multiplyScalar( 1.0 / _clipToWorldVector.w );
- return Math.abs( Math.max( _clipToWorldVector.x, _clipToWorldVector.y ) );
- }
- function raycastWorldUnits( lineSegments, intersects ) {
- for ( let i = 0, l = _instanceStart.count; i < l; i ++ ) {
- _line.start.fromBufferAttribute( _instanceStart, i );
- _line.end.fromBufferAttribute( _instanceEnd, i );
- const pointOnLine = new Vector3();
- const point = new Vector3();
- _ray$3.distanceSqToSegment( _line.start, _line.end, point, pointOnLine );
- const isInside = point.distanceTo( pointOnLine ) < _lineWidth * 0.5;
- if ( isInside ) {
- intersects.push( {
- point,
- pointOnLine,
- distance: _ray$3.origin.distanceTo( point ),
- object: lineSegments,
- face: null,
- faceIndex: i,
- uv: null,
- uv2: null,
- } );
- }
- }
- }
- function raycastScreenSpace( lineSegments, camera, intersects ) {
- const projectionMatrix = camera.projectionMatrix;
- const material = lineSegments.material;
- const resolution = material.resolution;
- const matrixWorld = lineSegments.matrixWorld;
- const geometry = lineSegments.geometry;
- const instanceStart = geometry.attributes.instanceStart;
- const instanceEnd = geometry.attributes.instanceEnd;
- const near = - camera.near;
- //
- // pick a point 1 unit out along the ray to avoid the ray origin
- // sitting at the camera origin which will cause "w" to be 0 when
- // applying the projection matrix.
- _ray$3.at( 1, _ssOrigin );
- // ndc space [ - 1.0, 1.0 ]
- _ssOrigin.w = 1;
- _ssOrigin.applyMatrix4( camera.matrixWorldInverse );
- _ssOrigin.applyMatrix4( projectionMatrix );
- _ssOrigin.multiplyScalar( 1 / _ssOrigin.w );
- // screen space
- _ssOrigin.x *= resolution.x / 2;
- _ssOrigin.y *= resolution.y / 2;
- _ssOrigin.z = 0;
- _ssOrigin3.copy( _ssOrigin );
- _mvMatrix.multiplyMatrices( camera.matrixWorldInverse, matrixWorld );
- for ( let i = 0, l = instanceStart.count; i < l; i ++ ) {
- _start4.fromBufferAttribute( instanceStart, i );
- _end4.fromBufferAttribute( instanceEnd, i );
- _start4.w = 1;
- _end4.w = 1;
- // camera space
- _start4.applyMatrix4( _mvMatrix );
- _end4.applyMatrix4( _mvMatrix );
- // skip the segment if it's entirely behind the camera
- const isBehindCameraNear = _start4.z > near && _end4.z > near;
- if ( isBehindCameraNear ) {
- continue;
- }
- // trim the segment if it extends behind camera near
- if ( _start4.z > near ) {
- const deltaDist = _start4.z - _end4.z;
- const t = ( _start4.z - near ) / deltaDist;
- _start4.lerp( _end4, t );
- } else if ( _end4.z > near ) {
- const deltaDist = _end4.z - _start4.z;
- const t = ( _end4.z - near ) / deltaDist;
- _end4.lerp( _start4, t );
- }
- // clip space
- _start4.applyMatrix4( projectionMatrix );
- _end4.applyMatrix4( projectionMatrix );
- // ndc space [ - 1.0, 1.0 ]
- _start4.multiplyScalar( 1 / _start4.w );
- _end4.multiplyScalar( 1 / _end4.w );
- // screen space
- _start4.x *= resolution.x / 2;
- _start4.y *= resolution.y / 2;
- _end4.x *= resolution.x / 2;
- _end4.y *= resolution.y / 2;
- // create 2d segment
- _line.start.copy( _start4 );
- _line.start.z = 0;
- _line.end.copy( _end4 );
- _line.end.z = 0;
- // get closest point on ray to segment
- const param = _line.closestPointToPointParameter( _ssOrigin3, true );
- _line.at( param, _closestPoint );
- // check if the intersection point is within clip space
- const zPos = MathUtils.lerp( _start4.z, _end4.z, param );
- const isInClipSpace = zPos >= - 1 && zPos <= 1;
- const isInside = _ssOrigin3.distanceTo( _closestPoint ) < _lineWidth * 0.5;
- if ( isInClipSpace && isInside ) {
- _line.start.fromBufferAttribute( instanceStart, i );
- _line.end.fromBufferAttribute( instanceEnd, i );
- _line.start.applyMatrix4( matrixWorld );
- _line.end.applyMatrix4( matrixWorld );
- const pointOnLine = new Vector3();
- const point = new Vector3();
- _ray$3.distanceSqToSegment( _line.start, _line.end, point, pointOnLine );
- intersects.push( {
- point: point,
- pointOnLine: pointOnLine,
- distance: _ray$3.origin.distanceTo( point ),
- object: lineSegments,
- face: null,
- faceIndex: i,
- uv: null,
- uv2: null,
- } );
- }
- }
- }
- class LineSegments2 extends Mesh {
- constructor( geometry = new LineSegmentsGeometry(), material = new LineMaterial( { color: Math.random() * 0xffffff } ) ) {
- super( geometry, material );
- this.isLineSegments2 = true;
- this.type = 'LineSegments2';
- }
- // for backwards-compatibility, but could be a method of LineSegmentsGeometry...
- computeLineDistances() {
- const geometry = this.geometry;
- const instanceStart = geometry.attributes.instanceStart;
- const instanceEnd = geometry.attributes.instanceEnd;
- const lineDistances = new Float32Array( 2 * instanceStart.count );
- for ( let i = 0, j = 0, l = instanceStart.count; i < l; i ++, j += 2 ) {
- _start$2.fromBufferAttribute( instanceStart, i );
- _end$2.fromBufferAttribute( instanceEnd, i );
- lineDistances[ j ] = ( j === 0 ) ? 0 : lineDistances[ j - 1 ];
- lineDistances[ j + 1 ] = lineDistances[ j ] + _start$2.distanceTo( _end$2 );
- }
- const instanceDistanceBuffer = new InstancedInterleavedBuffer( lineDistances, 2, 1 ); // d0, d1
- geometry.setAttribute( 'instanceDistanceStart', new InterleavedBufferAttribute( instanceDistanceBuffer, 1, 0 ) ); // d0
- geometry.setAttribute( 'instanceDistanceEnd', new InterleavedBufferAttribute( instanceDistanceBuffer, 1, 1 ) ); // d1
- return this;
- }
- raycast( raycaster, intersects ) {
- const worldUnits = this.material.worldUnits;
- const camera = raycaster.camera;
- if ( camera === null && ! worldUnits ) {
- console.error( 'LineSegments2: "Raycaster.camera" needs to be set in order to raycast against LineSegments2 while worldUnits is set to false.' );
- }
- const threshold = ( raycaster.params.Line2 !== undefined ) ? raycaster.params.Line2.threshold || 0 : 0;
- _ray$3 = raycaster.ray;
- const matrixWorld = this.matrixWorld;
- const geometry = this.geometry;
- const material = this.material;
- _lineWidth = material.lineWidth + threshold;
- _instanceStart = geometry.attributes.instanceStart;
- _instanceEnd = geometry.attributes.instanceEnd;
- // check if we intersect the sphere bounds
- if ( geometry.boundingSphere === null ) {
- geometry.computeBoundingSphere();
- }
- _sphere$4.copy( geometry.boundingSphere ).applyMatrix4( matrixWorld );
- // increase the sphere bounds by the worst case line screen space width
- let sphereMargin;
- if ( worldUnits ) {
- sphereMargin = _lineWidth * 0.5;
- } else {
- const distanceToSphere = Math.max( camera.near, _sphere$4.distanceToPoint( _ray$3.origin ) );
- sphereMargin = getWorldSpaceHalfWidth( camera, distanceToSphere, material.resolution );
- }
- _sphere$4.radius += sphereMargin;
- if ( _ray$3.intersectsSphere( _sphere$4 ) === false ) {
- return;
- }
- // check if we intersect the box bounds
- if ( geometry.boundingBox === null ) {
- geometry.computeBoundingBox();
- }
- _box$5.copy( geometry.boundingBox ).applyMatrix4( matrixWorld );
- // increase the box bounds by the worst case line width
- let boxMargin;
- if ( worldUnits ) {
- boxMargin = _lineWidth * 0.5;
- } else {
- const distanceToBox = Math.max( camera.near, _box$5.distanceToPoint( _ray$3.origin ) );
- boxMargin = getWorldSpaceHalfWidth( camera, distanceToBox, material.resolution );
- }
- _box$5.expandByScalar( boxMargin );
- if ( _ray$3.intersectsBox( _box$5 ) === false ) {
- return;
- }
- if ( worldUnits ) {
- raycastWorldUnits( this, intersects );
- } else {
- raycastScreenSpace( this, camera, intersects );
- }
- }
- }
- class LineGeometry extends LineSegmentsGeometry {
- constructor() {
- super();
- this.isLineGeometry = true;
- this.type = 'LineGeometry';
- }
- setPositions( array ) {
- // converts [ x1, y1, z1, x2, y2, z2, ... ] to pairs format
- const length = array.length - 3;
- const points = new Float32Array( 2 * length );
- for ( let i = 0; i < length; i += 3 ) {
- points[ 2 * i ] = array[ i ];
- points[ 2 * i + 1 ] = array[ i + 1 ];
- points[ 2 * i + 2 ] = array[ i + 2 ];
- points[ 2 * i + 3 ] = array[ i + 3 ];
- points[ 2 * i + 4 ] = array[ i + 4 ];
- points[ 2 * i + 5 ] = array[ i + 5 ];
- }
- super.setPositions( points );
- return this;
- }
- setColors( array ) {
- // converts [ r1, g1, b1, r2, g2, b2, ... ] to pairs format
- const length = array.length - 3;
- const colors = new Float32Array( 2 * length );
- for ( let i = 0; i < length; i += 3 ) {
- colors[ 2 * i ] = array[ i ];
- colors[ 2 * i + 1 ] = array[ i + 1 ];
- colors[ 2 * i + 2 ] = array[ i + 2 ];
- colors[ 2 * i + 3 ] = array[ i + 3 ];
- colors[ 2 * i + 4 ] = array[ i + 4 ];
- colors[ 2 * i + 5 ] = array[ i + 5 ];
- }
- super.setColors( colors );
- return this;
- }
- fromLine( line ) {
- const geometry = line.geometry;
- this.setPositions( geometry.attributes.position.array ); // assumes non-indexed
- // set colors, maybe
- return this;
- }
- }
- class Line2 extends LineSegments2 {
- constructor( geometry = new LineGeometry(), material = new LineMaterial( { color: Math.random() * 0xffffff } ) ) {
- super( geometry, material );
- this.isLine2 = true;
- this.type = 'Line2';
- }
- }
- var defaultColor = new Color(1,1,1);//config.applicationName == "zhiHouse" ? Colors.zhiBlue : Colors.lightGreen;
- var LineDraw = {
-
- createLine: function (posArr, o={}) {
- //多段普通线 (第二个点和第三个点之间是没有线段的, 所以不用在意线段顺序)
- var mat;
- if(o.mat){
- mat = o.mat;
- }else {
- let prop = {
- lineWidth: o.lineWidth || 1,
- //windows无效。 似乎mac/ios上粗细有效 ?
- color: o.color || defaultColor,
- transparent: o.dontAlwaysSeen ? false : true,
- depthTest: o.dontAlwaysSeen ? true : false,
- };
- if(o.deshed ){
- prop.dashSize = o.dashSize || 0.1,
- prop.gapSize = o.gapSize || 0.1;
- }
- mat = new THREE$1[o.deshed ? "LineDashedMaterial" : "LineBasicMaterial"](prop);
- }
-
-
-
- var line = new LineSegments(new BufferGeometry, mat);
- line.renderOrder = o.renderOrder || 4;
-
- this.moveLine(line, posArr);
-
- return line;
- },
-
-
- moveLine: function (line, posArr) {
- if(posArr.length == 0)return
- let position = [];
- posArr.forEach(e=>position.push(e.x,e.y,e.z));
- line.geometry.setAttribute('position', new Float32BufferAttribute(/* new Float32Array( */position/* ) */, 3));
-
- line.geometry.attributes.position.needsUpdate = true;
- line.geometry.computeBoundingSphere();
- if(line.material instanceof LineDashedMaterial){
- line.computeLineDistances();
- //line.geometry.attributes.lineDistance.needsUpdate = true;
-
- line.geometry.verticesNeedUpdate = true; //没用
-
- }
- }
- ,
-
-
- createFatLineMat : function(o){
-
- var supportExtDepth = !!Features.EXT_DEPTH.isSupported();
-
- let params = $.extend({}, {
- //默认
- lineWidth : 5,
- color:0xffffff,
- transparent : true, depthWrite:false, depthTest:false,
- dashSize : 0.1, gapSize:0.1,
- }, o, {
- //修正覆盖:
- dashed: o.dashWithDepth ? supportExtDepth && !!o.dashed : !!o.dashed ,
- dashWithDepth:!!o.dashWithDepth,//只在被遮住的部分显示虚线
- useDepth: !!o.useDepth,
- supportExtDepth,
-
- });
-
-
-
- var mat = new LineMaterial(params);
-
-
- //if(o.dashed)(mat.defines.USE_DASH = "")
-
- return mat;
- },
-
- /*
- 创建可以改变粗细的线。
- */
- createFatLine : function(posArr, o){
- var geometry = new LineGeometry();
- geometry.setColors( o.color || [1,1,1]);
- var matLine = o.material || this.createFatLineMat(o);
- var line = new Line2( geometry, matLine );
- //line.computeLineDistances();
-
- line.scale.set( 1, 1, 1 );
- line.renderOrder = 2;
-
- this.moveFatLine(line, posArr);
-
- return line;
- },
-
-
-
- moveFatLine: function(line, posArr){
- var geometry = line.geometry;
- var positions = [];
-
- posArr.forEach(e=>{positions.push(...e.toArray());});
-
-
- if(positions.length > 0){
- if(!geometry){
- geometry = line.geometry = new LineGeometry();
- }
- if(geometry.attributes.instanceEnd && geometry.attributes.instanceEnd.data.array.length != positions.length){//positions个数改变会有部分显示不出来,所以重建
- geometry.dispose();
- geometry = new LineGeometry();
- line.geometry = geometry;
- }
- geometry.setPositions( positions );
-
- if(line.material.defines.USE_DASH != void 0){
- //line.geometry.verticesNeedUpdate = true; //没用
- line.geometry.computeBoundingSphere(); //for raycaster
- line.computeLineDistances();
- }
- }else {
- geometry.dispose();
- line.geometry = new LineGeometry();
-
- }
-
-
- },
-
- updateLine: function(line, posArr){
- if(line instanceof Line2){
- LineDraw.moveFatLine(line,posArr);
- }else {
- LineDraw.moveLine(line,posArr);
- }
- },
- /*
-
- 为line创建用于检测鼠标的透明mesh,实际是个1-2段圆台。
- 由于近大远小的原因,假设没有透视畸变、创建的是等粗的圆柱的话, 所看到的线上每个位置的粗细应该和距离成反比。所以将圆柱改为根据距离线性渐变其截面半径的圆台,在最近点(相机到线的垂足)最细。如果最近点在线段上,则分成两段圆台,否则一段。
- */
- createBoldLine:function(points, o){
- o = o || {};
- var cylinder = o && o.cylinder;
- var CD = points[1].clone().sub(points[0]);
-
- var rotate = function(){//根据端点旋转好模型
- cylinder.lastVector = CD;//记录本次的端点向量
- var AB = new Vector3(0,-1,0);
- var axisVec = AB.clone().cross(CD).normalize(); //得到垂直于它们的向量,也就是旋转轴
- var rotationAngle = AB.angleTo(CD);
- cylinder.quaternion.setFromAxisAngle( axisVec, rotationAngle );
- };
- if(o && o.type == "init"){
- cylinder = new Mesh();
- cylinder.material = o.mat;
- if(CD.length() == 0)return cylinder;
- rotate();
- }
-
- if(CD.length() == 0)return cylinder;
- if(o.type != "update"){
- var CDcenter = points[0].clone().add(points[1]).multiplyScalar(.5);
- cylinder.position.copy(CDcenter);
-
- if(!cylinder.lastVector || o.type == "moveAndRotate")rotate();
- else if(cylinder.lastVector && CD.angleTo(cylinder.lastVector)>0) rotate();//线方向改了or线反向了 重新旋转一下模型
- if(config.isEdit && !objects.mainDesign.editing )return cylinder;//节省初始加载时间?
- }
-
-
- //为了保证线段任何地方的可检测点击范围看起来一样大,更新圆台的结构(但是在镜头边缘会比中心看起来大)
- var height = points[0].distanceTo(points[1]);
- var standPos = o && o.standPos || objects.player.position;
- var k = config.isMobile ? 20 : 40;
- var dis1 = points[0].distanceTo(standPos);
- var dis2 = points[1].distanceTo(standPos);
-
- var foot = math.getFootPoint(standPos, points[0], points[1]);//垂足
-
- if(o.constantBold || objects.player.mode != "panorama"){
- var width = 0.1;//0.08;
- var pts = [new Vector2$1(width ,height/2),new Vector2$1(width ,-height/2)];
- }else if(foot.clone().sub(points[0]).dot( foot.clone().sub(points[1]) ) > 0){//foot不在线段上
- var pts = [new Vector2$1(dis1 / k,height/2),new Vector2$1(dis2 / k,-height/2)];
- }else {//在线段上的话,要在垂足这加一个节点,因它距离站位最近,而两端较远
- var dis3 = foot.distanceTo(standPos);
- var len = foot.distanceTo(points[0]);
- var pts = [new Vector2$1(dis1 / k,height/2), new Vector2$1(dis3 / k,height/2-len), new Vector2$1(dis2 / k,-height/2)];
- }
- cylinder.geometry && cylinder.geometry.dispose();//若不删除会占用内存
- cylinder.geometry = new LatheBufferGeometry( pts, 4/* Math.min(dis1,dis2)<10?4:3 */ );
- cylinder.renderOrder = 2;
-
- return cylinder;
- },
- updateBoldLine:function(cylinder, points, type, standPos, constantBold){
- this.createBoldLine(points,{type:type, cylinder : cylinder, standPos:standPos, constantBold}); //type:move:平移 会改长短 , type:update根据距离和角度更新 不改长短
- },
- };
- var MeshDraw = {
- getShape:function(points, holes){
- var shape = new Shape();
- shape.moveTo( points[0].x, points[0].y );
- for(var i=1,len=points.length; i<len; i++){
- shape.lineTo(points[i].x, points[i].y );
- }
-
- /* var holePath = new THREE.Path()
- .moveTo( 20, 10 )
- .absarc( 10, 10, 10, 0, Math.PI * 2, true )
- arcShape.holes.push( holePath );
- */
- if(holes){//挖空
- holes.forEach((points)=>{
- var holePath = new Path();
- holePath.moveTo( points[0].x, points[0].y );
- for(var i=1,len=points.length; i<len; i++){
- holePath.lineTo(points[i].x, points[i].y );
- }
- shape.holes.push( holePath );
- });
- }
- return shape
- },
- getShapeGeo: function(points, holes){//获取任意形状(多边形或弧形)的形状面 //quadraticCurveTo() 这是弧形的含函数
-
- var geometry = new ShapeBufferGeometry( this.getShape(points, holes) ); //ShapeGeometry
-
- /* var matrix = new THREE.Matrix4();//将竖直的面变为水平
- matrix.set(//z = y
- 1, 0, 0, 0,
- 0, 0, 0, 0,
- 0, 1, 0, 0,
- 0, 0, 0, 1
- )
- geometry.applyMatrix(matrix) */
- //geometry.computeVertexNormals();//对于光照需要的是点法线
-
- return geometry;
-
-
- },
-
-
- getExtrudeGeo: function(points, holes, options={}){//获得挤出棱柱,可以选择传递height,或者extrudePath
- var shape = this.getShape(points, holes); //points是横截面 [vector2,...]
-
- if(options.extrudePath ){// 路径 :[vector3,...]
-
- var length = extrudePath.reduce((total, currentValue, currentIndex, arr)=>{
- if(currentIndex == 0)return 0
- return total + currentValue.distanceTo(arr[currentIndex-1]);
- },0);
- options.extrudePath = new CatmullRomCurve3(extrudePath, options.closed , 'catmullrom' /* 'centripetal' */ , options.tension);
-
-
- }
-
- var extrudeSettings = $.extend(options,{
- steps: options.steps != void 0 ? options.steps : ( options.extrudePath ? Math.round(length/0.3) : 1),
- bevelEnabled: false, //不加的话,height为0时会有圆弧高度
- //depth
- });
- var geometry = new ExtrudeBufferGeometry( shape, extrudeSettings );
- return geometry;
- },
-
-
- getUnPosPlaneGeo : function(){//获取还没有赋值位置的plane geometry
- var e = new Uint16Array([0, 1, 2, 0, 2, 3])
- // , t = new Float32Array([-.5, -.5, 0, .5, -.5, 0, .5, .5, 0, -.5, .5, 0])
- , i = new Float32Array([0, 0, 1, 0, 1, 1, 0, 1])
- , g = new BufferGeometry;
- g.setIndex(new BufferAttribute(e, 1)),
- //g.addAttribute("position", new n.BufferAttribute(t, 3)),
- g.setAttribute("uv", new BufferAttribute(i, 2));
- return function(){
- return g
- }
- }(),
- getPlaneGeo : function(A,B,C,D){
- var geo = this.getUnPosPlaneGeo().clone();
- var pos = [
- A.x, A.y, A.z,
- B.x, B.y, B.z,
- C.x, C.y, C.z,
- D.x, D.y, D.z
- ];
- //geo.addAttribute("position", new THREE.BufferAttribute(pos, 3))
- geo.setAttribute('position', new Float32BufferAttribute(pos, 3));
-
-
- geo.computeVertexNormals();
- geo.computeBoundingSphere(); //for raycaster
- return geo;
- },
- drawPlane : function(A,B,C,D, material){
- var wall = new Mesh(this.getPlaneGeo(A,B,C,D), material);
- return wall;
-
- },
- movePlane: function(mesh, A,B,C,D){
- var pos = new Float32Array([
- A.x, A.y, A.z,
- B.x, B.y, B.z,
- C.x, C.y, C.z,
- D.x, D.y, D.z
- ]);
- mesh.geometry.addAttribute("position", new BufferAttribute(pos, 3));
- mesh.geometry.computeBoundingSphere();//for checkIntersect
- }
-
- ,
-
- createGeometry:function(posArr, faceArr, uvArr, normalArr ){//创建复杂mesh. faceArr:[[0,1,2],[0,2,3]]
- let geo = new BufferGeometry;
-
- let positions = [];
- posArr.forEach(p=>positions.push(p.x,p.y,p.z));
- geo.setAttribute('position', new Float32BufferAttribute(positions, 3));
-
- if(faceArr){
- let indice = [];
- faceArr.forEach(f=>indice.push(...f));
- geo.setIndex(indice); // auto set Uint16BufferAttribute or Uint32BufferAttribute
- }
-
- if(uvArr){
- let uvs = [];
- uvArr.forEach(uv=>uvs.push(uv.x,uv.y));
- geo.setAttribute("uv", new Float32BufferAttribute(uvs, 2));
- }
-
- if(normalArr){
- let normals = [];
- normalArr.forEach(n=>normals.push(n.x,n.y,n.z));
- geo.setAttribute("normal", new Float32BufferAttribute(normals, 3));
- }
- /*
- geo.computeVertexNormals()
- geo.computeBoundingSphere() //for raycaster
- */
- return geo
- },
-
-
- updateGeometry:function(geo, posArr, faceArr, uvArr, normalArr ){//创建复杂mesh. faceArr:[[0,1,2],[0,2,3]]
-
- let positions = [];
- posArr.forEach(p=>positions.push(p.x,p.y,p.z));
- geo.setAttribute('position', new Float32BufferAttribute(positions, 3));
- geo.attributes.position.needsUpdate = true;
-
- if(faceArr){
- let indice = [];
- faceArr.forEach(f=>indice.push(...f));
- geo.setIndex(indice); // auto set Uint16BufferAttribute or Uint32BufferAttribute
- }
-
- if(uvArr){
- let uvs = [];
- uvArr.forEach(uv=>uvs.push(uv.x,uv.y));
- geo.setAttribute("uv", new Float32BufferAttribute(uvs, 2));
- }
-
- if(normalArr){
- let normals = [];
- normalArr.forEach(n=>normals.push(n.x,n.y,n.z));
- geo.setAttribute("normal", new Float32BufferAttribute(normals, 3));
- }
- /*
- geo.computeVertexNormals()
-
- */
- geo.computeBoundingSphere(); //for raycaster and visi
- return geo
- }
- };
- const verticalLine = new Line3();
- //控制点和边的合集。具有可以拖拽修改的功能,拖拽时能防止线相交。
- class ctrlPolygon extends Object3D {
- constructor (type, prop) {
- super();
- this.type = type;
-
- this.maxMarkers = Number.MAX_SAFE_INTEGER;
-
-
- this.transformData(prop);
- for(let i in prop){
- this[i] = prop[i];
- }
-
- if(this.closed && this.dimension == '2d'){
- this.areaPlane = this.createAreaPlane();
- this.add(this.areaPlane);
- }
-
- //数据--刚开始一定是空的
- this.points = [];
- //mesh 不一定有
- this.markers = [];
- this.edges = [];
-
- this.center;
-
-
-
-
- }
-
- initData(prop){
- //开始加数据
- if(prop.points){
-
- for(const p of prop.points){
- const pos = new Vector3().copy(p);
- this.addMarker({point:pos});
- }
-
- if(this.datasetId != void 0){//初始化位置
- if(this.dataset_points){
- this.dataset_points = this.dataset_points.map(e=>{
- return e && new Vector3().copy(e)
- });
- this.transformByPointcloud(); //根据dataset_points生成points
- }
- }else {
- if(prop.dataset_points && prop.dataset_points.some(e=>e != void 0)){
- console.error('存在测量线的datasetId为空而dataset_points有值,请检查并删除:'+this.sid);//存在过的bug,原因未知,可能是后台处理dataset时替换的错误:http://192.168.0.21/index.php?m=bug&f=view&bugID=23601
- console.log(this);
- }
- }
-
-
-
-
- this.getFacePlane();
- this.getPoint2dInfo(this.points);
-
- this.update({ifUpdateMarkers:true});
- //this.dragChange(new THREE.Vector3().copy(prop.points[prop.points.length-1]), prop.points.length-1);
- this.setSelected(false );
- this.markers.forEach(marker=>{marker.dispatchEvent('addHoverEvent'); });
- return true
- }
- }
-
-
-
-
-
- addMarker(o={}){
- var index = o.index == void 0 ? this.points.length : o.index; //要当第几个
-
- this.points = [...this.points.slice(0,index), o.point, ...this.points.slice(index,this.points.length)];
- //this.points.push(o.point);
-
- if(o.marker){
- this.add(o.marker);
- this.markers = [...this.markers.slice(0,index), o.marker, ...this.markers.slice(index,this.markers.length)];
- this.updateMarker(o.marker, o.point);
- o.marker.addEventListener('drag', this.dragMarker.bind(this));
- o.marker.addEventListener('drop', this.dropMarker.bind(this));
-
-
- let addHoverEvent = (e)=>{
- let mouseover = (e) => {
- this.setMarkerSelected(e.object, 'hover', 'single');
- viewer.dispatchEvent({
- type : "CursorChange", action : "add", name:"markerMove"
- });
- };
- let mouseleave = (e) => {
- this.setMarkerSelected(e.object, 'unhover', 'single');
- viewer.dispatchEvent({
- type : "CursorChange", action : "remove", name:"markerMove"
- });
- };
- o.marker.addEventListener('mouseover', mouseover);
- o.marker.addEventListener('mouseleave', mouseleave);
- o.marker.removeEventListener('addHoverEvent',addHoverEvent);
- };
- o.marker.addEventListener('addHoverEvent',addHoverEvent);//当非isNew时才添加事件
- if(!this.isNew){
- o.marker.dispatchEvent('addHoverEvent');
- }
-
- }
-
- if(o.edge){
- this.add(o.edge);
- this.edges = [...this.edges.slice(0,index), o.edge, ...this.edges.slice(index,this.edges.length)];
- }
-
-
- }
-
-
-
-
- dragMarker(e){
-
- var I, atMap;
-
- if(e.hoverViewport != e.drag.dragViewport){//不能使用e.dragViewport,要使用drag中的,因为drag中存储的要一直继承下来,不因mouseup了而改变。
- viewer.dispatchEvent({
- type : "CursorChange", action : "add", name:"polygon_AtWrongPlace"
- });
- return
- }
-
- viewer.dispatchEvent({
- type : "CursorChange", action : "remove", name:"polygon_AtWrongPlace"
- });
-
- atMap = e.drag.dragViewport.name == 'mapViewport';
-
- if(atMap && this.unableDragAtMap){
- e.drag.object = null; //取消拖拽
- return
- }
- e.drag.object.isDragging = true;
-
-
- I = e.intersect && (e.intersect.orthoIntersect || e.intersect.location);
-
- //记录数据集
-
- //在三维中脱离点云(在map中拉到周围都没有点云的地方)的顶点,无法拖拽怎么办
-
- if (I) {
- let i = this.markers.indexOf(e.drag.object);
- if (i !== -1) {
- this.dragChange(I.clone(), i, atMap);
-
- /* if(this.points_datasets){
- if(e.intersectPoint.pointcloud) this.points_datasets[i] = e.intersectPoint.pointcloud.dataset_id
- else this.points_datasets[i] = null
- } */
- if(this.points_datasets){
- if(e.intersect.pointcloud) this.points_datasets[i] = e.intersect.pointcloud.dataset_id;
- else if(e.intersect.object) this.points_datasets[i] = e.intersect.object.dataset_id;
- else this.points_datasets[i] = null;
- }
- }
- this.editStateChange(true);
- return true
- }
-
-
-
-
- };
-
-
-
-
-
-
- dragChange(intersectPos, i, atMap){
- let len = this.markers.length;
- let oldPoint = this.points[i];
- if(atMap){
- intersectPos.setZ(oldPoint.z); //在地图上拖拽,不改变其高度。
- }
- let location = intersectPos.clone();
-
-
-
- if(this.faceDirection && this.maxMarkers == 2 && len == 2){//add 固定方向的点不直接拖拽
- var p1 = this.markers[0].position;
- if(this.faceDirection == 'horizontal'){
- var projectPos = location.clone().setZ(p1.z);
- }else {
- var projectPos = p1.clone().setZ(location.z);
- }
- //var p2 = p1.clone().add(this.direction)
- //var projectPos = math.getFootPoint(location, p1, p2)
-
-
- LineDraw.updateLine(this.guideLine, [location, projectPos]);
- location = projectPos;
- this.guideLine.visible = true;
- }else if( len > 1){
-
- var points = this.points.map(e=>e.clone());
- points[i].copy(location); //算normal需要提前确认point
-
- //若为定义了面朝向的矩形
- if(this.faceDirection == 'horizontal'){
- if(len == 2){
- location.setZ(points[0].z);
- }
- if(!this.facePlane){//一个点就能确定面
- this.facePlane = new Plane().setFromNormalAndCoplanarPoint( new Vector3(0,0,1), this.points[0] );
- }
- }else if(this.faceDirection == 'vertical'){//当有两个点时, 有两个方向的可能
- if(len == 2){
- if(this.isRect){
- let vec = points[0].clone().sub(location);
- if(Math.sqrt(vec.x*vec.x+vec.y*vec.y) > Math.abs(vec.z) ){//水平(高度差小于水平距离时)
- location.setZ(points[0].z);
- //this.cannotConfirmNormal = false;//能确定面为水平方向
- }else {//垂直 (当两点一样时也属于这种)
- location.setX(points[0].x);
- location.setY(points[0].y);
- //this.cannotConfirmNormal = true; //不能确定面,因第三点可绕着纵轴线自由移动
- }
- }
- }else {
- {//判断cannotConfirmNormal. 如果前几段都在竖直线上,就不能固定出面方向。
- this.cannotConfirmNormal = true;
- let max = this.isRect ? 1 : len-2;
- for(let i=0;i<max;i++){
- let p1 = points[i].clone();
- let p2 = points[i+1].clone();
- let vec = p1.sub(p2);
- if(vec.x != 0 || vec.y != 0){
- this.cannotConfirmNormal = false;
- break;
- }
- }
- }
-
- if(!this.facePlane || this.cannotConfirmNormal){//三个点且为水平方向时,计算面
-
- var points_ = points.map(e=>new Vector2$1(e.x,e.y));
- var points2 = getDifferentPoint(points_, 2);
- if(points2){
- let normal = math.getNormal2d({p1:points2[0], p2:points2[1]});
- normal = new Vector3(normal.x, normal.y, 0);
- this.facePlane = new Plane().setFromNormalAndCoplanarPoint( normal, this.points[0] );
- }
- }
- }
- }
- if(len > 2){//area
-
- if(!this.faceDirection){
- if(len == 3 || this.isRect) this.cannotConfirmNormal = true; //当第三个点固定后(有四个点时)才能固定面
- if(!this.facePlane || this.cannotConfirmNormal){
- var points3 = getDifferentPoint(points, 3);//只有找到三个不同的点算拥有面和area
- if(points3){
- this.facePlane = new Plane().setFromCoplanarPoints(...points3 );
- }
- }
- }
-
- if( this.facePlane && !this.cannotConfirmNormal ){//之后加的点一定要在面上
- if(atMap){
- //地图上用垂直线,得到和面的交点。
- verticalLine.set(location.clone().setZ(100000), location.clone().setZ(-100000));//确保长度范围覆盖所有测量面
- location = this.facePlane.intersectLine(verticalLine, new Vector3() );
- if(!location) return;
- }else {
- location = this.facePlane.projectPoint(intersectPos, new Vector3() );
- }
- }
-
-
- points[i].copy(location);//再copy确认一次
-
- if(this.isRect){ //是矩形 (即使没有faceDirection也能执行)
- //根据前两个点计算当前和下一个点
- var p1 = points[(i-2+len)%len];
- var p2 = points[(i-1+len)%len];
- if(p1.equals(p2)){//意外情况:重复点两次 ( bug点,改了好多遍)
- if(this.faceDirection == 'vertical'){
- p2.add(new Vector3(0,0,0.0001));
- }else {
- p2.add(new Vector3(0,0.0001,0));
- }
- }
- //p3 : location
- var foot = math.getFootPoint(location, p1, p2);//p2 修改p2到垂足的位置
- var vec = foot.clone().sub(location);
- var p4 = p1.clone().sub(vec);
-
-
- points[(i-1+len)%len].copy(foot);
- points[(i+1)%len].copy(p4);
- this.setPosition((i-1+len)%len, foot);//p2
- this.setPosition((i+1)%len, p4);
-
- }
-
- /* let points2d;
- if(this.facePlane){
- var originPoint0 = points[0].clone()
- var qua = math.getQuaBetween2Vector(this.facePlane.normal, new THREE.Vector3(0,0,1), new THREE.Vector3(0,0,1));
- points2d = points.map(e=>e.clone().applyQuaternion(qua))
- } */
- this.getPoint2dInfo(points);
-
- var isIntersectSelf = !this.isRect && len > 3 && this.intersectSelf(this.point2dInfo.points2d);//检测相交
- if(isIntersectSelf){
- //not-allowed
- viewer.dispatchEvent({
- type : "CursorChange", action : "add", name:"polygon_isIntersectSelf"
- });
- this.isIntersectSelf = true;
- return
- }else {
- this.isIntersectSelf = false;
- viewer.dispatchEvent({
- type : "CursorChange", action : "remove", name:"polygon_isIntersectSelf"
- });
- /* this.facePlane && (this.point2dInfo = {
- originPoint0 ,
- points2d,
- quaInverse : qua.clone().invert()
- }) */
- }
-
-
-
- }
-
- var showGuideLine = len>1 && (this.faceDirection || len > 3);
- if(showGuideLine && this.guideLine){
- LineDraw.updateLine(this.guideLine, [intersectPos, location]);
- this.guideLine.visible = true;
- }
-
- //console.log(this.points.map(e=>e.toArray()))
- }
-
-
- if(this.restrictArea){
- let holes = this.restrictArea.holes.concat(this.restrictArea.parentHoles);
- let holesPoints = holes.filter(e=>e!=this && e.points.length>2).map(e=>e.points);
- if(!math.isPointInArea(this.restrictArea.points, holesPoints, location)){
- viewer.dispatchEvent({
- type : "CursorChange", action : "add", name:"polygon_AtWrongPlace"
- });
- this.isAtWrongPlace = true;
- return
- }
- //就不处理相交线了。 有个缺点:floor上的hole可以限制room,但hole不受room限制,会导致room的marker被框在hole里而动不了。只能去调整hole了
- }
-
-
-
- viewer.dispatchEvent({
- type : "CursorChange", action : "remove", name:"polygon_AtWrongPlace"
- });
- this.isAtWrongPlace = false;
- this.setPosition(i, location);
- this.update();
-
- this.dispatchEvent({type:'dragChange', index:i});
-
-
-
- }
-
- dropMarker(e){
- //console.log('dropMarker')
- if (this.isNew && e.pressDistance>Potree.config.clickMaxDragDis){//拖拽的话返回
- return this.continueDrag(null,e)
- }
-
- if(e.isTouch){
- if(e.hoverViewport != viewer.mainViewport && this.unableDragAtMap){
- viewer.dispatchEvent({type:'reticule_forbit', v:true});
- //console.log('reticule_forbit',true)
- return this.continueDrag(null,e)
- }else {
- viewer.dispatchEvent({type:'reticule_forbit', v:false});
- //console.log('reticule_forbit',false)
- }
- this.dragMarker(e); //触屏时必须先更新下点
-
- }
-
-
- if (e.button != MOUSE.RIGHT && (//右键click的话继续执行,因为会停止
- this.isIntersectSelf && this.isNew //有线相交了
- || this.isAtWrongPlace && this.isNew
- || !e.isAtDomElement && this.isNew//如果是刚添加时在其他dom点击, 不要响应
- || e.hoverViewport != viewer.mainViewport && this.unableDragAtMap //垂直的测量线不允许在地图上放点
- || !getDifferentPoint(this.points, this.points.length) //不允许和之前的点相同
- )
- ){
- return this.continueDrag(null,e)
- }
-
- //console.log('drop marker' )
-
- let i = this.markers.indexOf(e.drag.object);
- if (i !== -1) {
- this.dispatchEvent({
- 'type': 'marker_dropped',
- 'index': i
- });
- if(this.markers.length>2 && this.facePlane)this.cannotConfirmNormal = false;
- this.guideLine &&(this.guideLine.visible = false);
- }
-
-
-
- this.setMarkerSelected(e.drag.object, 'unhover', 'single');
-
- this.editStateChange(false);
-
-
-
-
- e.drag.endDragFun && e.drag.endDragFun(e);// addmarker
-
- if(this.changeCallBack)this.changeCallBack();
-
- return true
- };
-
- getFacePlane(){//最普通一种get方法,根据顶点。且假设所有点已经共面,且不重合
- if(this.points.length<3) return
- this.facePlane = new Plane().setFromCoplanarPoints(...this.points.slice(0,3) );
-
- }
-
- getPoint2dInfo(points){ //在更新areaplane之前必须更新过point2dInfo
- if(this.facePlane){
- var originPoint0 = points[0].clone();
- var qua = math.getQuaBetween2Vector(this.facePlane.normal, new Vector3(0,0,1), new Vector3(0,0,1));
- let points2d = points.map(e=>e.clone().applyQuaternion(qua));
-
- this.point2dInfo = {
- originPoint0 ,
- points2d,
- quaInverse : qua.clone().invert()
- };
- }
- }
-
-
-
-
- setPosition (index, position) {//拖拽后设置位置
- let point = this.points[index];
- point.copy(position);
- if(this.datasetId){
- this.dataset_points[index] = Potree.Utils.datasetPosTransform({toDataset:true, datasetId:this.datasetId, position:point.clone()});
-
- }
- let marker = this.markers[index];
- this.updateMarker(marker, point);
-
-
- }
-
- updateMarker(marker, pos){
- marker.position.copy(pos);
- marker.update();
- }
-
-
- intersectSelf(points2d){//add
- var len = points2d.length;
- for(var i=0;i<len;i++){
- for(var j=i+2;j<len;j++){
- if(Math.abs(j-len-i)<2)continue;//不和邻边比
-
- var p1 = points2d[i];
- var p2 = points2d[i+1];
- var p3 = points2d[j];
- var p4 = points2d[(j+1)%len];
- if(p1.equals(p2) || p3.equals(p4) || p1.equals(p3) || p2.equals(p3) || p1.equals(p4) || p2.equals(p4))continue
-
-
- var line1 = [p1,p2];
- var line2 = [p3,p4];
- var intersect = math.isLineIntersect(line1, line2, false, 0.001);
- if(intersect){
- return true
- break
- }
- }
- }
-
- }
-
- removeMarker (index) {
-
- this.points.splice(index, 1);
-
- const marker = this.markers[index];
- //this.remove(marker);
- this.markers.splice(index, 1);
- marker.dispatchEvent({type:'dispose'});
-
-
- let edgeIndex = index; //(index === 0) ? 0 : (index - 1);
- const edge = this.edges[edgeIndex];
- if(edge){
- this.remove(edge);
- this.edges.splice(edgeIndex, 1);
- edge.dispatchEvent({type:'dispose'});
- }
- this.point2dInfo && this.point2dInfo.points2d.splice(index, 1); //add
- this.dispatchEvent({type:'removeMarker',index,marker});
-
-
- }
-
- createAreaPlane(mat){
- var geometry = new Geometry();
- var mesh = new Mesh(geometry, mat);
-
- return mesh
- }
-
- updateAreaPlane(){
- if(!this.point2dInfo)return
- this.areaPlane.geometry.dispose();
- if(this.points.length>2){
- this.areaPlane.geometry = MeshDraw.getShapeGeo(this.point2dInfo.points2d);
- var center = math.getCenterOfGravityPoint(this.point2dInfo.points2d); //重心
-
- var firstPos = this.point2dInfo.points2d[0].clone();
- firstPos.z = 0; //因为shape只读取了xy,所以位移下, 再算出最终位置,得到差距
- firstPos.applyQuaternion(this.point2dInfo.quaInverse);
- var vec = this.point2dInfo.originPoint0.clone().sub(firstPos);
- center = new Vector3(center.x, center.y, 0);
- center.applyQuaternion(this.point2dInfo.quaInverse);
- this.areaPlane.quaternion.copy(this.point2dInfo.quaInverse);
- this.areaPlane.position.copy(vec);
- center.add(vec);
- this.center = center;
- }else {
- this.areaPlane.geometry = new Geometry();
-
- }
-
-
- }
-
-
-
-
- update(options={}){
- if(this.points.length === 0){
- return;
- }
-
-
-
-
-
- let lastIndex = this.points.length - 1;
-
-
- for (let index = 0; index <= lastIndex; index++) {
-
- let nextIndex = (index + 1 > lastIndex) ? 0 : index + 1;
- let previousIndex = (index === 0) ? lastIndex : index - 1;
- let point = this.points[index];
- let nextPoint = this.points[nextIndex];
- let previousPoint = this.points[previousIndex];
- if(options.ifUpdateMarkers){
- this.updateMarker(this.markers[index], point);
- }
- { // edges
- let edge = this.edges[index];
- if(edge){
- LineDraw.updateLine(edge, [point, nextPoint]);
- //edge.visible = index < lastIndex || this.isRect || (this.closed && !this.isNew);
- }
-
-
- }
-
-
- }
-
- if(this.areaPlane){
- this.updateAreaPlane();
- }
-
- //this.dispatchEvent({type:'update'})
- viewer.mapViewer && viewer.mapViewer.dispatchEvent('content_changed'); //暂时先这么都通知
-
- }
-
- dispose(){//add
- this.parent.remove(this);
- this.markers.concat(this.edges).forEach(e=>e.dispatchEvent({type:'dispose'}));
- }
-
-
- reDraw(restMarkerCount=0){//重新开始画
- let pointCount = this.points.length - restMarkerCount; // restMarkerCount为需要留下的marker数量
- while(pointCount > 0){
- this.removeMarker(--pointCount);
- }
- this.point2dInfo = null;
- this.facePlane = null;
-
-
-
- }
-
-
- setMarkerSelected(){}
- editStateChange(state){
- if(!state){
- viewer.dispatchEvent({
- type : "CursorChange", action : "remove", name:"polygon_isIntersectSelf"
- });
- viewer.dispatchEvent({
- type : "CursorChange", action : "remove", name:"polygon_AtWrongPlace"
- });
- viewer.dispatchEvent({type:'reticule_forbit', v:false});
-
- this.markers.forEach(e=>e.isDragging = false );
- }
- }
-
- transformData(){}
- setSelected(){}
-
-
-
- continueDrag(marker, e){
- let object = marker || e.drag.object;
- object.isDragging = true;
- var timer = setTimeout(()=>{//等 drag=null之后 //右键拖拽结束后需要重新得到drag
- if(this.parent && object.isDragging){
- //console.log('continueDrag')
- viewer.inputHandler.startDragging( object ,
- {endDragFun: e.drag.endDragFun, notPressMouse:e.drag.notPressMouse, dragViewport:e.drag.dragViewport}
- );
- }
- },1);
- return timer
- }
-
-
- }
- function getDifferentPoint(points, count){//for facePlane
- var result = [];
- for(let i=0;i<points.length;i++){
- var p = points[i];
- if(result.find(e=>e.equals(p)))continue;
- else result.push(p);
- if(result.length == count)break
- }
- if(result.length == count)return result
- }
- let texLoader = new TextureLoader();
- let defaultColor$1 = new Color(config$1.measure.default.color);
- let highlightColor = new Color(config$1.measure.highlight.color);
- let color = new Color(config$1.measure.color);
- let textColor = new Color(config$1.measure.textColor);
- var markerMats;
- var lineMats;
- var planeMats;
-
- const lineDepthInfo = {
- clipDistance : 4,//消失距离
- occlusionDistance: 1,//变为backColor距离
- };
- const LabelDepthInfo = {
- clipDistance : 6,//消失距离
- occlusionDistance: 2,//变为backColor距离
- };
- /* const LabelDepthInfo = {
- clipDistance : 0.1,//消失距离
- occlusionDistance: 0.1,//变为backColor距离
- } */
- const markerSizeInfo = {
- minSize : 25 , maxSize : 65, nearBound : 0.2, farBound : 4,
- };
- const labelSizeInfo = {width2d:200};
- const mainLabelProp = {
- backgroundColor: {r: defaultColor$1.r*255, g: defaultColor$1.g*255, b: defaultColor$1.b*255, a:config$1.measure.default.opacity},
- textColor: {r: textColor.r*255, g: textColor.g*255, b: textColor.b*255, a: 1.0},
- fontsize:16,
- useDepth : true ,
- renderOrder : 5
- };
- const subLabelProp = {
- backgroundColor: {r: 255, g: 255, b: 255, a:1},
- textColor: {r: 0, g: 0, b:0, a: 1.0},
- fontsize:14,
- renderOrder : 4
- };
- const angle = MathUtils.degToRad(5);//显示水平垂直辅助线的最小角度
- const guideShowMinAngle = {min: angle, max: Math.PI/2 - angle};
-
-
-
- class Measure extends ctrlPolygon{
- constructor (prop) {
- prop.dimension = '2d';
-
- super('measure',prop);
- this.constructor.counter = (this.constructor.counter === undefined) ? 0 : this.constructor.counter + 1;
-
-
- this.name = this.measureType + this.constructor.counter; //'Measure_' + this.constructor.counter;
-
-
-
- this.markerLabels = [];
- this.edgeLabels = [];
- this.angleLabels = [];
- this.coordinateLabels = [];
- this.area = {value:0,string:''};
-
-
- if(this.closed/* this.showArea */){
- this.areaLabel = this.createAreaLabel();
- this.add(this.areaLabel);
- }
-
-
- //add:
- if(this.maxMarkers > 2 || this.faceDirection){
- this.createGuideLine();
- }
- if(this.measureType == 'Distance'){
- this.createHorVerGuideLine();
- }
-
-
- this.selectStates = {};
-
- this.setUnitSystem(prop.unit || viewer.unitConvert.UnitService.defaultSystem);
- viewer.setObjectLayers(this, 'measure' );
-
- //addMarkers:
-
- this.initData(prop);
-
-
- this.points_datasets || (this.points_datasets = []); //存每个点是哪个数据集
-
-
- this.addEventListener('marker_dropped',(e)=>{
- this.updateDatasetBelong();
- });
-
- this.addEventListener('isVisible', ()=>{
- viewer.mapViewer && viewer.mapViewer.dispatchEvent({type:'content_changed'});
- });
-
- }
-
-
-
- initData(prop){
- let makeIt = super.initData(prop);
- if(makeIt){
- this.edges.forEach(edge=>{edge.dispatchEvent('addHoverEvent'); });
- }
- }
-
-
- updateDatasetBelong(){//更新所属数据集
- let old = this.datasetId;
-
- let maxCount = {id:null,count:0};
- let datasets = {};
-
- this.points_datasets.forEach(e=>{
- if(e == void 0)return
- if(datasets[e]){
- datasets[e] ++;
- }else {
- datasets[e] = 1;
- }
- });
- for(let i in datasets) {
- if(datasets[i]>maxCount.count){
- maxCount = {id:i, count:datasets[i]};
- }
- }
- this.datasetId = maxCount.count > 0 ? maxCount.id : null;
-
- if(this.datasetId != old){
- this.dispatchEvent({type:'changeDatasetId'});
- if(this.datasetId == void 0){
- this.dataset_points = null; //可能为空或[null,null...]
- }else {
- this.dataset_points = this.points.map(e=>{
- return Potree.Utils.datasetPosTransform({toDataset:true,datasetId:this.datasetId, position:e.clone()})
- });
- }
- }
- }
-
-
-
-
- transformByPointcloud(){//每次移动点云 or 加载测量线时要获取一下当前position
- if(this.datasetId == void 0)return
- this.points = this.dataset_points.map(e=>{
- return Potree.Utils.datasetPosTransform({fromDataset:true, datasetId:this.datasetId, position:e.clone()})
- });
-
- this.getPoint2dInfo(this.points);
- this.update({ifUpdateMarkers:true});
- this.setSelected(false);//隐藏edgelabel
-
- }
-
- update(options={}) {
- super.update(options);
-
- if(this.showCoordinates && this.points.length>0){
- let position = this.points[0];
-
- this.markers[0].position.copy(position);
- { // coordinate labels
- let coordinateLabel = this.coordinateLabels[0];
-
- let lonlat = viewer.transform.lonlatToLocal.inverse(position.toArray());
- let EPSG4550 = viewer.transform.lonlatTo4550.forward(lonlat);
- let pos = [
- position.toArray(),
- lonlat,
- EPSG4550
- ];
- //let msg = position.toArray().map(p => Utils.addCommas(p.toFixed(2))).join(" / ");
- let msg = pos.map(a=>
- a.map(p => Utils.addCommas(p.toFixed(10))).join(", ")
- ).join("<br>");
- coordinateLabel.setText(msg);
- coordinateLabel.setPos(position);
- coordinateLabel.setVisible(true);//this.showCoordinates;
- }
- return
- }
-
-
-
- let setEdgeLabel = (label,p1,p2,distance)=>{//设置label位置和字
- let center = new Vector3().addVectors(p1,p2).multiplyScalar(0.5);
- label.setPos(center);
- distance = distance == void 0 ? p1.distanceTo(p2) : distance;
- var text = viewer.unitConvert.convert(distance, 'distance', void 0, this.unitSystem, 0.1 , true);//distance要传0.1 这个factor
- label.setText(text);
- return distance
- };
-
-
- let lastIndex = this.points.length - 1;
- for (let index = 0; index <= lastIndex; index++) {
-
- let nextIndex = (index + 1 > lastIndex) ? 0 : index + 1;
- let previousIndex = (index === 0) ? lastIndex : index - 1;
- let point = this.points[index];
- let nextPoint = this.points[nextIndex];
- let previousPoint = this.points[previousIndex];
-
- if(this.showDistances){ // edge labels
- let edgeLabel = this.edgeLabels[index];
- let distance = point.distanceTo(nextPoint);
- edgeLabel.shouldVisi = (index < lastIndex || this.isRect || this.closed && !this.isNew /* && this.points.length > 2 */) && distance>0;
- /* this.closed || */edgeLabel.setVisible(edgeLabel.shouldVisi);
- if(edgeLabel.visible){
- setEdgeLabel(edgeLabel,point,nextPoint,distance);
- }
- }
- }
- if(this.measureType == 'Distance' && this.points.length>1){//设置水平垂直辅助线
- var pTop, pBtm;
- if(this.points[0].z > this.points[1].z ){
- pTop = this.points[0];
- pBtm = this.points[1];
- }else {
- pTop = this.points[1];
- pBtm = this.points[0];
- }
- let projectPos = new Vector3(pTop.x, pTop.y, pBtm.z);//两条guideline的交点
-
- {//倾斜角度太小的时候不显示
- let tan = pTop.distanceTo(projectPos) / pBtm.distanceTo(projectPos);
- let angle = Math.atan(tan);
-
- this.shouldShowHorVerGuide = angle > guideShowMinAngle.min && angle < guideShowMinAngle.max;
- }
-
-
- LineDraw.updateLine(this.verGuideEdge, [pTop, projectPos]);
- LineDraw.updateLine(this.horGuideEdge, [pBtm, projectPos]);
- setEdgeLabel(this.verEdgeLabel,pTop,projectPos);
- setEdgeLabel(this.horEdgeLabel,pBtm,projectPos);
-
- this.verGuideEdge.visible = this.horGuideEdge.visible = this.shouldShowHorVerGuide;
- this.verEdgeLabel.visible = this.horEdgeLabel.visible = this.shouldShowHorVerGuide;
- }
-
-
- if(this.showArea && this.point2dInfo){ // update area
- /* if(this.points.length>2){
- this.area = {value:0};
- this.areaLabel.setVisible(false)
- }else{ */
- let area = Math.abs(math.getArea(this.point2dInfo.points2d));//this.getArea();
- let msg = viewer.unitConvert.convert(area, 'area', void 0, this.unitSystem/* , 0.1 */ );
- this.area = {value:area, string:msg};
-
- this.areaLabel.setPos(this.center);
- this.areaLabel.setText(msg);
- this.areaLabel.setVisible(true);
- //}
- }
-
-
-
- };
-
-
- addMarker (o={}) {
-
- let marker = new Sprite$1({mat:this.getMarkerMaterial('default'), sizeInfo: markerSizeInfo, name:"measure_point"} );
- viewer.setObjectLayers(marker, 'measure' );
- marker.renderOrder = 3;
- marker.markerSelectStates = {};
- marker.addEventListener('startDragging',(e)=>{
- if(e.drag.dragViewport.name == 'MainView')viewer.inputHandler.dispatchEvent( {type: 'isMeasuring',v:true, cause:'startDragging'});
- });
- marker.addEventListener('drop',(e)=>{
- viewer.inputHandler.dispatchEvent({type: 'isMeasuring', v:false, cause:'stopDragging'} );
- });
-
- let edge;
- { // edges
- edge = LineDraw.createFatLine( [ ],{material:this.getLineMat('edgeDefault')} );
- viewer.setObjectLayers(edge, 'measure' );
-
- let addHoverEvent = ()=>{ //当非isNew时才添加事件
-
- let mouseover = (e) => {this.setSelected(true, 'edge');};
- let mouseleave = (e) => {this.setSelected(false, 'edge');};
-
- edge.addEventListener('mouseover', mouseover);
- edge.addEventListener('mouseleave', mouseleave);
- edge.removeEventListener('addHoverEvent', addHoverEvent);
- };
- edge.addEventListener('addHoverEvent', addHoverEvent);
- }
-
- super.addMarker({point:o.point, marker:marker, edge});
-
-
- if(this.showEdges){ // edge labels
- const edgeLabel = this.createEdgeLabel('edgeLabel', !this.closed);
- this.edgeLabels.push(edgeLabel);
-
- }
-
- if(this.showCoordinates){ // coordinate labels
- let coordinateLabel = new Label({
- className:'measure_pointPos',
- camera: viewer.scene.getActiveCamera()
- });
- coordinateLabel.setVisible(false);
- this.coordinateLabels.push(coordinateLabel);
-
- }
-
-
-
-
-
- let event = {
- type: 'marker_added',
- measurement: this,
- marker: marker
- };
- this.dispatchEvent(event);
- //this.setMarker(this.points.length - 1, point);
- this.update();//更新一下倒数第二条线
- return marker;//add
- };
-
-
- editStateChange(state){ //主要针对edgeLabels显示切换,编辑时显示
- super.editStateChange(state);
- if(!state){
- this.editStateTimer = setTimeout(()=>{
- if(!this.isEditing){
- this.dispatchEvent({type:'editStateChange',state:false});
- this.setEdgesDisplay(false);
- }
- },100);
- }else {
- if(!this.isEditing){
- this.dispatchEvent({type:'editStateChange',state:true});
- this.setEdgesDisplay(true);
- clearTimeout(this.editStateTimer);
- }
- }
- this.isEditing = state;
- }
-
-
- setMarkerSelected(marker, state, hoverObject){
-
- //console.warn(marker.id , state, hoverObject)
-
- marker.markerSelectStates[hoverObject] = state;
- let absoluteState = false;
- for(var i in marker.markerSelectStates){
- if(marker.markerSelectStates[i] == 'hover'){
- absoluteState = true; break;
- }
- }
- if(absoluteState){
- marker.material = this.getMarkerMaterial('select');
- }else {
- marker.material = this.getMarkerMaterial('default');
- }
-
- marker.selected = absoluteState;
-
- viewer.mapViewer && viewer.mapViewer.dispatchEvent('content_changed');
- }
-
-
-
- setEdgesDisplay(state, ignoreGuideLine){
- this.closed && this.edgeLabels.forEach(e=>e.setVisible(!!(state && e.shouldVisi)) );
-
- if(!ignoreGuideLine && this.measureType == 'Distance'){
- this.horEdgeLabel.visible = this.verEdgeLabel.visible = this.horGuideEdge.visible = this.verGuideEdge.visible = !!(state && this.shouldShowHorVerGuide);
- }
- }
-
-
- setSelected(state, hoverObject){//add
-
- hoverObject && (this.selectStates[hoverObject] = state);
- let absoluteState = false;
- for(var i in this.selectStates){
- if(this.selectStates[i]){
- absoluteState = true; break;
- }
- }
-
-
- if(absoluteState){
- this.markers.forEach(e=>this.setMarkerSelected(e, 'hover', 'selectAll' ) );
-
- this.edges.forEach(e=>e.material = this.getLineMat('edgeSelect') );
-
- this.areaPlane && (this.areaPlane.material = planeMats.selected);
-
-
- //this.areaLabel && this.areaLabel.elem.addClass('highLight')
- //this.closed || this.edgeLabels.forEach(e=>e.elem.addClass('highLight') )
- this.setEdgesDisplay(true, hoverObject=="screenshot");
-
- this.areaLabel && setLabelHightState(this.areaLabel, true);
- this.closed || this.edgeLabels.forEach(e=>setLabelHightState(e, true) );
-
- }else {
- this.markers.forEach(e=>this.setMarkerSelected(e, 'unhover', 'selectAll' ));
- this.edges.forEach(e=>e.material = this.getLineMat('edgeDefault') );
- this.areaPlane && (this.areaPlane.material = planeMats.default);
- this.setEdgesDisplay(false, hoverObject=="screenshot");
- //this.areaLabel && this.areaLabel.elem.removeClass('highLight')
- //this.closed || this.edgeLabels.forEach(e=>e.elem.removeClass('highLight') )
- this.areaLabel && setLabelHightState(this.areaLabel, false);
- this.closed || this.edgeLabels.forEach(e=>setLabelHightState(e, false) );
-
- }
-
- this.selected = absoluteState;
- viewer.mapViewer && viewer.mapViewer.dispatchEvent('content_changed');
- if(hoverObject != 'byList'){
- this.bus && this.bus.emit('highlight', this.selected);//列表高亮
- }
- }
-
- removeMarker(index ){
- super.removeMarker(index);
-
- this.points_datasets.splice(index, 1);
- this.dataset_points && this.dataset_points.splice(index, 1);
- this.coordinateLabels.splice(index, 1);
-
- let edgeIndex = index;//(index === 0) ? 0 : (index - 1);
- if(this.edgeLabels[edgeIndex]){
- this.edgeLabels[edgeIndex].dispose();
- this.edgeLabels.splice(edgeIndex, 1);
- }
-
- this.update();
- this.dispatchEvent({type: 'marker_removed', measurement: this});
- }
-
- setPosition(index, position) {
- super.setPosition(index, position);
- let event = {
- type: 'marker_moved',
- measure: this,
- index: index,
- position: position.clone()
- };
- this.dispatchEvent(event);
- }
-
- dispose(){//add
- var labels = this.edgeLabels.concat(this.coordinateLabels);
- this.areaLabel && labels.push(this.areaLabel);
- labels.forEach(e=>e.dispatchEvent({type:'dispose'}));
- super.dispose();
- }
-
-
- getTotalDistance () {
- if (this.points.length === 0) {
- return 0;
- }
- let distance = 0;
- for (let i = 1; i < this.points.length; i++) {
- let prev = this.points[i - 1];
- let curr = this.points[i];
- let d = prev.distanceTo(curr);
- distance += d;
- }
- if (this.closed && this.points.length > 1) {
- let first = this.points[0];
- let last = this.points[this.points.length - 1];
- let d = last.distanceTo(first);
- distance += d;
- }
- return distance;
- }
- getAngleBetweenLines (cornerPoint, point1, point2) {
- let v1 = new Vector3().subVectors(point1, cornerPoint);
- let v2 = new Vector3().subVectors(point2, cornerPoint);
- // avoid the error printed by threejs if denominator is 0
- const denominator = Math.sqrt( v1.lengthSq() * v2.lengthSq() );
- if(denominator === 0){
- return 0;
- }else {
- return v1.angleTo(v2);
- }
- };
- getAngle (index) {
- if (this.points.length < 3 || index >= this.points.length) {
- return 0;
- }
- let previous = (index === 0) ? this.points[this.points.length - 1] : this.points[index - 1];
- let point = this.points[index];
- let next = this.points[(index + 1) % (this.points.length)];
- return this.getAngleBetweenLines(point, previous, next);
- }
-
- getCenter(/* update */){
- if(this.points.length>=3){
- return this.center.clone()
- }else if(this.points.length == 2){
- return this.points[0].clone().add((this.points[1])).multiplyScalar(0.5)
- }else return this.points[0].clone()
-
- }
-
- // updateAzimuth(){
- // // if(this.points.length !== 2){
- // // return;
- // // }
- // // const azimuth = this.azimuth;
- // // const [p0, p1] = this.points;
- // // const r = p0.distanceTo(p1);
-
- // }
-
-
-
-
- createGuideLine(){//add 辅助线
- var guideLine = LineDraw.createFatLine([ ],{material:this.getLineMat('guide')} );
- guideLine.visible = false;
- this.guideLine = guideLine;
- this.add(guideLine);
- }
- createHorVerGuideLine(){//创建水平与垂直辅助线,仅距离测量有。
- var verGuideEdge = LineDraw.createFatLine([ ],{material:this.getLineMat('guide')} );
- verGuideEdge.visible = false;
- this.verGuideEdge = verGuideEdge;
-
- var horGuideEdge = LineDraw.createFatLine([ ],{material:this.getLineMat('guide')} );
- horGuideEdge.visible = false;
- this.horGuideEdge = horGuideEdge;
-
- this.add(this.verGuideEdge);
- this.add(this.horGuideEdge);
-
-
- //label:
- this.verEdgeLabel = this.createEdgeLabel('verGuideEdge');
- this.horEdgeLabel = this.createEdgeLabel('horGuideEdge');
-
- }
-
- createEdgeLabel(name, hasHoverEvent){
- const edgeLabel = new TextSprite(
- $.extend(hasHoverEvent ? mainLabelProp : subLabelProp,{sizeInfo: labelSizeInfo, name:name||'edgeLabel'})
- );
- if(hasHoverEvent){
- edgeLabel.addEventListener('mouseover',()=>{
- this.setSelected(true, 'edgeLabel');
- });
- edgeLabel.addEventListener('mouseleave',()=>{
- this.setSelected(false, 'edgeLabel');
- });
- edgeLabel.addEventListener('click',()=>{
- viewer.focusOnObject(this, 'measure');
- });
- }
- edgeLabel.visible = false;
- edgeLabel.sprite.material.depthTestWhenPick = true;
- viewer.setObjectLayers(edgeLabel, 'measure' );
- this.add(edgeLabel);
- return edgeLabel
- }
-
- createAreaLabel(){
- /* const areaLabel = new Label({
- className:'measure_area',
-
- })
- areaLabel.elem.on('mouseover',()=>{
- this.setSelected(true, 'areaLabel')
- })
- areaLabel.elem.on('mouseout',()=>{
- this.setSelected(false, 'areaLabel')
- }) */
-
-
- const areaLabel = new TextSprite(
- $.extend(mainLabelProp,{sizeInfo: labelSizeInfo, name:'areaLabel_'} )
- );
-
- areaLabel.addEventListener('mouseover',()=>{
- this.setSelected(true, 'areaLabel');
- });
- areaLabel.addEventListener('mouseleave',()=>{
- this.setSelected(false, 'areaLabel');
- });
- areaLabel.addEventListener('click',()=>{
- viewer.focusOnObject(this, 'measure');
- });
- viewer.setObjectLayers(areaLabel, 'measure' );
- areaLabel.visible = false;
-
- return areaLabel;
-
-
- }
-
- getMarkerMaterial(type) {
- if(!markerMats){
- markerMats = {
- default: new DepthBasicMaterial($.extend({},lineDepthInfo,{
- transparent: !0,
- opacity: 1,
- map: texLoader.load(Potree.resourcePath+'/textures/pic_point_s32.png' ),
- useDepth:true
- })),
- select: new MeshBasicMaterial({
- transparent: !0,
- opacity: 1,
- depthTest:false,
- map: texLoader.load(Potree.resourcePath+'/textures/pic_point32.png'/* , null, null, { antialias: false } */),
-
- }),
- };
- Measure.markerMats = markerMats;
- }
- return markerMats[type]
-
- }
-
-
-
-
- getLineMat(type) {
- if(!Measure.lineMats){
- Measure.lineMats = {
- edgeDefault: LineDraw.createFatLineMat({
- color: config$1.measure.default.color,
- lineWidth: config$1.measure.lineWidth,
- useDepth :true,
- dashWithDepth :true, // 只在被遮住的部分显示虚线,因为实线容易挡住label
- dashed :true,
- dashSize : 0.04,
- gapSize: 0.04,
- transparent: true,
- opacity: config$1.measure.default.opacity,
- depthTestWhenPick:true,
- }),
- edgeSelect: LineDraw.createFatLineMat({
- color: config$1.measure.highlight.color,//'#f0ff00',
- dashSize: 0.5,
- gapSize: 0.2,
- lineWidth: config$1.measure.lineWidth ,
- transparent: true,
- opacity: config$1.measure.highlight.opacity
- }),
- guide: LineDraw.createFatLineMat({
- color:config$1.measure.guide.color,
- dashSize: 0.1,
- gapSize: 0.02,
- dashed: true,
- lineWidth: config$1.measure.lineWidth
- })
-
- };
- }
- return Measure.lineMats[type]
-
- }
-
-
- createAreaPlane(){
- planeMats || (planeMats = {
- default: new DepthBasicMaterial( $.extend({},LabelDepthInfo,{
- color:color,
- side:DoubleSide,
- opacity:0.2,
- transparent:true,
- useDepth:true
- })),
- selected: new MeshBasicMaterial({
- color: color ,
- side:DoubleSide,
- opacity:0.3,
- transparent:true,
- })
- },Measure.planeMats = planeMats);
- return super.createAreaPlane(planeMats.default)
- }
-
-
- raycast (raycaster, intersects) {
- for (let i = 0; i < this.points.length; i++) {
- let marker = this.markers[i];
- marker.raycast(raycaster, intersects);
- }
- // recalculate distances because they are not necessarely correct
- // for scaled objects.
- // see https://github.com/mrdoob/three.js/issues/5827
- // TODO: remove this once the bug has been fixed
- for (let i = 0; i < intersects.length; i++) {
- let I = intersects[i];
- I.distance = raycaster.ray.origin.distanceTo(I.point);
- }
- intersects.sort(function (a, b) { return a.distance - b.distance; });
- };
-
-
- transformData(prop){
- if(prop.measureType == 'Point'){
- prop.showCoordinates = true,
- prop.closed = true,
- prop.maxMarkers = 1,
- prop.minMarkers = 1;
- }else if(prop.measureType == 'Distance'){
- prop.showDistances = true,
- prop.closed = false,
- prop.showEdges = true,
- prop.maxMarkers = 2,
- prop.minMarkers = 2;
- }else if(prop.measureType == 'Ver Distance'){
- prop.showDistances = true,
- prop.closed = false,
- prop.showEdges = true,
- prop.maxMarkers = 2,
- prop.minMarkers = 2,
- prop.faceDirection = "vertical";
- prop.unableDragAtMap = true;
- }else if(prop.measureType == 'Hor Distance'){
- prop.showDistances = true,
- prop.closed = false,
- prop.showEdges = true,
- prop.maxMarkers = 2,
- prop.minMarkers = 2,
- prop.faceDirection = "horizontal";
-
- }else if(prop.measureType == 'Area'){
- prop.showDistances = true,
- prop.showArea = true,
- prop.showEdges = true,
- prop.closed = true,
- prop.minMarkers = 3;
- }else if(prop.measureType == 'Hor Area'){
- prop.showDistances = true,
- prop.showArea = true,
- prop.showEdges = true,
- prop.closed = true,
- prop.minMarkers = 3;
- prop.faceDirection = "horizontal";
-
- }else if(prop.measureType == 'Ver Area'){
- prop.showDistances = true,
- prop.showArea = true,
- prop.showEdges = true,
- prop.closed = true,
- prop.minMarkers = 3;
- prop.faceDirection = "vertical";
- prop.unableDragAtMap = true;
- }else if(prop.measureType == 'Rect Area'){
- prop.showDistances = true,
- prop.showArea = true,
- prop.showEdges = true,
- prop.closed = true,
- prop.minMarkers = 4;
- prop.maxMarkers = 4;
- }else if(prop.measureType == 'Hor Rect Area'){
- prop.showDistances = true,
- prop.showArea = true,
- prop.showEdges = true,
- prop.closed = true,
- prop.minMarkers = 4;
- prop.maxMarkers = 4;
- prop.isRect = true;
- prop.faceDirection = "horizontal";
- }else if(prop.measureType == 'Ver Rect Area'){
- prop.showDistances = true,
- prop.showArea = true,
- prop.showEdges = true,
- prop.closed = true,
- prop.minMarkers = 4;
- prop.maxMarkers = 4;
- prop.isRect = true;
- prop.faceDirection = "vertical";
- prop.unableDragAtMap = true;
- }
- }
- setUnitSystem(unitSystem){
- //console.log(this.name +':' +this.unitSystem)
- if(unitSystem != this.unitSystem){
- if(unitSystem == "metric"){
-
- }else if(unitSystem == 'imperial'){
-
- }
- this.unitSystem = unitSystem;
- this.update();
- }
- }
- reDraw(restMarkerCount=0){//重新开始画
- super.reDraw(restMarkerCount);
- if(this.measureType == 'Distance'){
- this.shouldShowHorVerGuide = false;
- this.setEdgesDisplay(false);
- }
- if(this.showArea){
- this.area = {value:0};
- this.areaLabel && this.areaLabel.setVisible(false);
- }
- viewer.inputHandler.dispatchEvent( {type:'isMeasuring', v:true, cause:'reDraw'} );
-
- }
-
- /* get showCoordinates () {
- return this._showCoordinates;
- }
- set showCoordinates (value) {
- this._showCoordinates = value;
- this.update();
- }
- get showAngles () {
- return this._showAngles;
- }
- set showAngles (value) {
- this._showAngles = value;
- this.update();
- }
- get showCircle () {
- return this._showCircle;
- }
- set showCircle (value) {
- this._showCircle = value;
- this.update();
- }
- get showAzimuth(){
- return this._showAzimuth;
- }
- set showAzimuth(value){
- this._showAzimuth = value;
- this.update();
- }
- get showEdges () {
- return this._showEdges;
- }
- set showEdges (value) {
- this._showEdges = value;
- this.update();
- }
- get showHeight () {
- return this._showHeight;
- }
- set showHeight (value) {
- this._showHeight = value;
- this.update();
- }
- get showArea () {
- return this._showArea;
- }
- set showArea (value) {
- this._showArea = value;
- this.update();
- }
- get closed () {
- return this._closed;
- }
- set closed (value) {
- this._closed = value;
- this.update();
- }
- get showDistances () {
- return this._showDistances;
- }
- set showDistances (value) {
- this._showDistances = value;
- this.update();
- } */
- }
- function setLabelHightState(label, state){
- if(state){
- label.backgroundColor = {r: highlightColor.r*255, g: highlightColor.g*255, b: highlightColor.b*255, a:config$1.measure.highlight.opacity},
- label.backgroundColor.a = config$1.measure.highlight.opacity;
- label.sprite.material.useDepth = false;
-
- }else {
- label.backgroundColor = mainLabelProp.backgroundColor;
- label.backgroundColor.a = config$1.measure.default.opacity;
- label.sprite.material.useDepth = true;
-
- }
- label.updateTexture();
- //label.sprite.material.needsUpdate = true
- }
- function createCircleRadiusLabel(){
- const circleRadiusLabel = new TextSprite("");
- circleRadiusLabel.setTextColor({r: 140, g: 250, b: 140, a: 1.0});
- circleRadiusLabel.setBorderColor({r: 0, g: 0, b: 0, a: 1.0});
- circleRadiusLabel.setBackgroundColor({r: 0, g: 0, b: 0, a: 1.0});
- circleRadiusLabel.fontsize = 16;
- circleRadiusLabel.material.depthTest = false;
- circleRadiusLabel.material.opacity = 1;
- circleRadiusLabel.visible = false;
-
- return circleRadiusLabel;
- }
- function createCircleRadiusLine(){
- /* const lineGeometry = new LineGeometry();
- lineGeometry.setPositions([
- 0, 0, 0,
- 0, 0, 0,
- ]);
- const lineMaterial = new LineMaterial({
- color: 0xff0000,
- lineWidth: 2,
- resolution: new THREE.Vector2(1000, 1000),
- gapSize: 1,
- dashed: true,
- });
- lineMaterial.depthTest = false;
- const circleRadiusLine = new Line2(lineGeometry, lineMaterial);*/
-
- var circleRadiusLine = LineDraw.createFatLine([ ],{
- color:0xff0000,
- dashSize: 0.5,
- gapSize: 0.2,
- lineWidth: config$1.measure.lineWidth
- });
- circleRadiusLine.visible = false;
- return circleRadiusLine;
- }
- function createCircleLine(){
- const coordinates = [];
- let n = 128;
- for(let i = 0; i <= n; i++){
- let u0 = 2 * Math.PI * (i / n);
- let u1 = 2 * Math.PI * (i + 1) / n;
- let p0 = new Vector3(
- Math.cos(u0),
- Math.sin(u0),
- 0
- );
- let p1 = new Vector3(
- Math.cos(u1),
- Math.sin(u1),
- 0
- );
- coordinates.push(
- p0,
- p1
- );
- }
- /* const geometry = new LineGeometry();
- geometry.setPositions(coordinates);
- const material = new LineMaterial({
- color: 0xff0000,
- dashSize: 5,
- gapSize: 2,
- lineWidth: 2,
- resolution: new THREE.Vector2(1000, 1000),
- });
- material.depthTest = false;
- const circleLine = new Line2(geometry, material);
- circleLine.visible = false;
- circleLine.computeLineDistances();*/
- var circleLine = LineDraw.createFatLine(coordinates,{
- color: 0xff0000,
- dashSize: 0.5,
- gapSize: 0.2,
- lineWidth: config$1.measure.lineWidth
- });
- return circleLine;
- }
- /* function createCircleCenter(){
- const sg = new THREE.markerGeometry(1, 32, 32);
- const sm = new THREE.MeshNormalMaterial();
-
- const circleCenter = new THREE.Mesh(sg, sm);
- circleCenter.visible = false;
- return circleCenter;
- } */
- function createLine(){
-
- const line = LineDraw.createFatLine([ ],{
- color: 0xff0000,
- dashSize: 0.5,
- gapSize: 0.2,
- lineWidth: config$1.measure.lineWidth
- });
-
-
-
- return line;
- }
- function createCircle(){
- const coordinates = [];
- let n = 128;
- for(let i = 0; i <= n; i++){
- let u0 = 2 * Math.PI * (i / n);
- let u1 = 2 * Math.PI * (i + 1) / n;
- let p0 = new Vector3(
- Math.cos(u0),
- Math.sin(u0),
- 0
- );
- let p1 = new Vector3(
- Math.cos(u1),
- Math.sin(u1),
- 0
- );
- coordinates.push(
- p0,
- p1
- );
- }
-
- var line = LineDraw.createFatLine(coordinates,{
- color: 0xff0000,
- dashSize: 0.5,
- gapSize: 0.2,
- lineWidth: config$1.measure.lineWidth
- });
- return line;
- }
- /* function createAzimuth(){
- const azimuth = {
- label: null,
- center: null,
- target: null,
- north: null,
- centerToNorth: null,
- centerToTarget: null,
- centerToTargetground: null,
- targetgroundToTarget: null,
- circle: null,
- node: null,
- };
- const sg = new THREE.markerGeometry(1, 32, 32);
- const sm = new THREE.MeshNormalMaterial();
- {
- const label = new TextSprite("");
- label.setTextColor({r: 140, g: 250, b: 140, a: 1.0});
- label.setBorderColor({r: 0, g: 0, b: 0, a: 1.0});
- label.setBackgroundColor({r: 0, g: 0, b: 0, a: 1.0});
- label.fontsize = 16;
- label.material.depthTest = false;
- label.material.opacity = 1;
- azimuth.label = label;
- }
- azimuth.center = new THREE.Mesh(sg, sm);
- azimuth.target = new THREE.Mesh(sg, sm);
- azimuth.north = new THREE.Mesh(sg, sm);
- azimuth.centerToNorth = createLine();
- azimuth.centerToTarget = createLine();
- azimuth.centerToTargetground = createLine();
- azimuth.targetgroundToTarget = createLine();
- azimuth.circle = createCircle();
- azimuth.node = new THREE.Object3D();
- azimuth.node.add(
- azimuth.centerToNorth,
- azimuth.centerToTarget,
- azimuth.centerToTargetground,
- azimuth.targetgroundToTarget,
- azimuth.circle,
- azimuth.label,
- azimuth.center,
- azimuth.target,
- azimuth.north,
- );
- return azimuth;
- } */
- /*
-
- */
- class PolygonClipVolume extends Object3D{
-
- constructor(camera){
- super();
- this.constructor.counter = (this.constructor.counter === undefined) ? 0 : this.constructor.counter + 1;
- this.name = "polygon_clip_volume_" + this.constructor.counter;
- this.camera = camera.clone();
- this.camera.rotation.set(...camera.rotation.toArray()); // [r85] workaround because camera.clone() doesn't work on rotation
- this.camera.rotation.order = camera.rotation.order;
- this.camera.updateMatrixWorld();
- this.camera.updateProjectionMatrix();
- this.camera.matrixWorldInverse.copy(this.camera.matrixWorld).invert();
- this.viewMatrix = this.camera.matrixWorldInverse.clone();
- this.projMatrix = this.camera.projectionMatrix.clone();
- // projected markers
- this.markers = [];
- this.initialized = false;
- }
- addMarker() {
- let marker = new Mesh();
- let cancel;
- let drag = e => {
- let size = e.viewer.renderer.getSize(new Vector2$1());
- let projectedPos = new Vector3(
- 2.0 * (e.drag.end.x / size.width) - 1.0,
- -2.0 * (e.drag.end.y / size.height) + 1.0,
- 0
- );
- marker.position.copy(projectedPos);
- };
-
- let drop = e => {
- cancel();
- };
-
- cancel = e => {
- marker.removeEventListener("drag", drag);
- marker.removeEventListener("drop", drop);
- };
-
- marker.addEventListener("drag", drag);
- marker.addEventListener("drop", drop);
- this.markers.push(marker);
- }
- removeLastMarker() {
- if(this.markers.length > 0) {
- this.markers.splice(this.markers.length - 1, 1);
- }
- }
- };
- class Utils {
- static async loadShapefileFeatures (file, callback) {
- let features = [];
- let handleFinish = () => {
- callback(features);
- };
- let source = await shapefile.open(file);
- while(true){
- let result = await source.read();
- if (result.done) {
- handleFinish();
- break;
- }
- if (result.value && result.value.type === 'Feature' && result.value.geometry !== undefined) {
- features.push(result.value);
- }
- }
- }
- static toString (value) {
- if (value.x != null) {
- return value.x.toFixed(2) + ', ' + value.y.toFixed(2) + ', ' + value.z.toFixed(2);
- } else {
- return '' + value + '';
- }
- }
- static normalizeURL (url) {
- let u = new URL(url);
- return u.protocol + '//' + u.hostname + u.pathname.replace(/\/+/g, '/');
- };
- static pathExists (url) {
- let req = XHRFactory.createXMLHttpRequest();
- req.open('GET', url, false);
- req.send(null);
- if (req.status !== 200) {
- return false;
- }
- return true;
- };
- static debugSphere(parent, position, scale, color){
- let geometry = new SphereGeometry(1, 8, 8);
- let material;
- if(color !== undefined){
- material = new MeshBasicMaterial({color: color});
- }else {
- material = new MeshNormalMaterial();
- }
- let sphere = new Mesh(geometry, material);
- sphere.position.copy(position);
- sphere.scale.set(scale, scale, scale);
- parent.add(sphere);
- return sphere;
- }
- static debugLine(parent, start, end, color){
- let material = new LineBasicMaterial({ color: color });
- let geometry = new Geometry();
- const p1 = new Vector3(0, 0, 0);
- const p2 = end.clone().sub(start);
- geometry.vertices.push(p1, p2);
- let tl = new Line( geometry, material );
- tl.position.copy(start);
- parent.add(tl);
- let line = {
- node: tl,
- set: (start, end) => {
- geometry.vertices[0].copy(start);
- geometry.vertices[1].copy(end);
- geometry.verticesNeedUpdate = true;
- },
- };
- return line;
- }
- static debugCircle(parent, center, radius, normal, color){
- let material = new LineBasicMaterial({ color: color });
- let geometry = new Geometry();
- let n = 32;
- for(let i = 0; i <= n; i++){
- let u0 = 2 * Math.PI * (i / n);
- let u1 = 2 * Math.PI * (i + 1) / n;
- let p0 = new Vector3(
- Math.cos(u0),
- Math.sin(u0),
- 0
- );
- let p1 = new Vector3(
- Math.cos(u1),
- Math.sin(u1),
- 0
- );
- geometry.vertices.push(p0, p1);
- }
- let tl = new Line( geometry, material );
- tl.position.copy(center);
- tl.scale.set(radius, radius, radius);
- parent.add(tl);
- }
- static debugBox(parent, box, transform = new Matrix4(), color = 0xFFFF00){
-
- let vertices = [
- [box.min.x, box.min.y, box.min.z],
- [box.min.x, box.min.y, box.max.z],
- [box.min.x, box.max.y, box.min.z],
- [box.min.x, box.max.y, box.max.z],
- [box.max.x, box.min.y, box.min.z],
- [box.max.x, box.min.y, box.max.z],
- [box.max.x, box.max.y, box.min.z],
- [box.max.x, box.max.y, box.max.z],
- ].map(v => new Vector3(...v));
- let edges = [
- [0, 4], [4, 5], [5, 1], [1, 0],
- [2, 6], [6, 7], [7, 3], [3, 2],
- [0, 2], [4, 6], [5, 7], [1, 3]
- ];
- let center = box.getCenter(new Vector3());
- let centroids = [
- {position: [box.min.x, center.y, center.z], color: 0xFF0000},
- {position: [box.max.x, center.y, center.z], color: 0x880000},
- {position: [center.x, box.min.y, center.z], color: 0x00FF00},
- {position: [center.x, box.max.y, center.z], color: 0x008800},
- {position: [center.x, center.y, box.min.z], color: 0x0000FF},
- {position: [center.x, center.y, box.max.z], color: 0x000088},
- ];
- for(let vertex of vertices){
- let pos = vertex.clone().applyMatrix4(transform);
- Utils.debugSphere(parent, pos, 0.1, 0xFF0000);
- }
- for(let edge of edges){
- let start = vertices[edge[0]].clone().applyMatrix4(transform);
- let end = vertices[edge[1]].clone().applyMatrix4(transform);
- Utils.debugLine(parent, start, end, color);
- }
- for(let centroid of centroids){
- let pos = new Vector3(...centroid.position).applyMatrix4(transform);
- Utils.debugSphere(parent, pos, 0.1, centroid.color);
- }
- }
- static debugPlane(parent, plane, size = 1, color = 0x0000FF){
- let planehelper = new PlaneHelper(plane, size, color);
- parent.add(planehelper);
- }
- /**
- * adapted from mhluska at https://github.com/mrdoob/three.js/issues/1561
- */
- static computeTransformedBoundingBox (box, transform) {
- let vertices = [
- new Vector3(box.min.x, box.min.y, box.min.z).applyMatrix4(transform),
- new Vector3(box.min.x, box.min.y, box.min.z).applyMatrix4(transform),
- new Vector3(box.max.x, box.min.y, box.min.z).applyMatrix4(transform),
- new Vector3(box.min.x, box.max.y, box.min.z).applyMatrix4(transform),
- new Vector3(box.min.x, box.min.y, box.max.z).applyMatrix4(transform),
- new Vector3(box.min.x, box.max.y, box.max.z).applyMatrix4(transform),
- new Vector3(box.max.x, box.max.y, box.min.z).applyMatrix4(transform),
- new Vector3(box.max.x, box.min.y, box.max.z).applyMatrix4(transform),
- new Vector3(box.max.x, box.max.y, box.max.z).applyMatrix4(transform)
- ];
- let boundingBox = new Box3();
- boundingBox.setFromPoints(vertices);
- return boundingBox;
- };
- /**
- * add separators to large numbers
- *
- * @param nStr
- * @returns
- */
- static addCommas (nStr) {
- nStr += '';
- let x = nStr.split('.');
- let x1 = x[0];
- let x2 = x.length > 1 ? '.' + x[1] : '';
- let rgx = /(\d+)(\d{3})/;
- while (rgx.test(x1)) {
- x1 = x1.replace(rgx, '$1' + ',' + '$2');
- }
- return x1 + x2;
- };
- static removeCommas (str) {
- return str.replace(/,/g, '');
- }
- /**
- * create worker from a string
- *
- * code from http://stackoverflow.com/questions/10343913/how-to-create-a-web-worker-from-a-string
- */
- static createWorker (code) {
- let blob = new Blob([code], {type: 'application/javascript'});
- let worker = new Worker(URL.createObjectURL(blob));
- return worker;
- };
- static moveTo(scene, endPosition, endTarget){
- let view = scene.view;
- let camera = scene.getActiveCamera();
- let animationDuration = 500;
- let easing = TWEEN.Easing.Quartic.Out;
- { // animate camera position
- let tween = new TWEEN.Tween(view.position).to(endPosition, animationDuration);
- tween.easing(easing);
- tween.start();
- }
- { // animate camera target
- let camTargetDistance = camera.position.distanceTo(endTarget);
- let target = new Vector3().addVectors(
- camera.position,
- camera.getWorldDirection(new Vector3()).clone().multiplyScalar(camTargetDistance)
- );
- let tween = new TWEEN.Tween(target).to(endTarget, animationDuration);
- tween.easing(easing);
- tween.onUpdate(() => {
- view.lookAt(target);
- });
- tween.onComplete(() => {
- view.lookAt(target);
- });
- tween.start();
- }
- }
- static loadSkybox (path) {
- let parent = new Object3D("skybox_root");
- let camera = new PerspectiveCamera(75, window.innerWidth / window.innerHeight, 1, 100000);
- if(!window.axisYup) camera.up.set(0, 0, 1);
- let scene = new Scene();
- let format = '.jpg';
- let urls = [
- path + 'px' + format, path + 'nx' + format,
- path + 'py' + format, path + 'ny' + format,
- path + 'pz' + format, path + 'nz' + format
- ];
- let materialArray = [];
- {
- for (let i = 0; i < 6; i++) {
- let material = new MeshBasicMaterial({
- map: null,
- side: BackSide,
- depthTest: false,
- depthWrite: false,
- color: 0x424556
- });
- materialArray.push(material);
- let loader = new TextureLoader();
- loader.load(urls[i],
- function loaded (texture) {
- material.map = texture;
- material.needsUpdate = true;
- material.color.setHex(0xffffff);
- }, function progress (xhr) {
- // console.log( (xhr.loaded / xhr.total * 100) + '% loaded' );
- }, function error (xhr) {
- console.log('An error happened', xhr);
- }
- );
- }
- }
- let skyGeometry = new BoxGeometry(700, 700, 700);
- let skybox = new Mesh(skyGeometry, materialArray);
- scene.add(skybox);
- scene.traverse(n => n.frustumCulled = false);
- // z up
- scene.rotation.x = Math.PI / 2;
- parent.children.push(camera);
- camera.parent = parent;
- return {camera, scene, parent};
- };
- static createGrid (width, length, spacing, color) {
- let material = new LineBasicMaterial({
- color: color || 0x888888
- });
- let geometry = new Geometry();
- for (let i = 0; i <= length; i++) {
- geometry.vertices.push(new Vector3(-(spacing * width) / 2, i * spacing - (spacing * length) / 2, 0));
- geometry.vertices.push(new Vector3(+(spacing * width) / 2, i * spacing - (spacing * length) / 2, 0));
- }
- for (let i = 0; i <= width; i++) {
- geometry.vertices.push(new Vector3(i * spacing - (spacing * width) / 2, -(spacing * length) / 2, 0));
- geometry.vertices.push(new Vector3(i * spacing - (spacing * width) / 2, +(spacing * length) / 2, 0));
- }
- let line = new LineSegments(geometry, material, LinePieces);
- line.receiveShadow = true;
- return line;
- }
- static createBackgroundTexture (width, height) {
- function gauss (x, y) {
- return (1 / (2 * Math.PI)) * Math.exp(-(x * x + y * y) / 2);
- };
- // map.magFilter = THREE.NearestFilter;
- let size = width * height;
- let data = new Uint8Array(3 * size);
- let chroma = [1, 1.5, 1.7];
- let max = gauss(0, 0);
- for (let x = 0; x < width; x++) {
- for (let y = 0; y < height; y++) {
- let u = 2 * (x / width) - 1;
- let v = 2 * (y / height) - 1;
- let i = x + width * y;
- let d = gauss(2 * u, 2 * v) / max;
- let r = (Math.random() + Math.random() + Math.random()) / 3;
- r = (d * 0.5 + 0.5) * r * 0.03;
- r = r * 0.4;
- // d = Math.pow(d, 0.6);
- data[3 * i + 0] = 255 * (d / 15 + 0.05 + r) * chroma[0];
- data[3 * i + 1] = 255 * (d / 15 + 0.05 + r) * chroma[1];
- data[3 * i + 2] = 255 * (d / 15 + 0.05 + r) * chroma[2];
- }
- }
- let texture = new DataTexture(data, width, height, RGBFormat);
- texture.needsUpdate = true;
- return texture;
- }
- static getMousePointCloudIntersection (viewport, mouse, pointer, camera, viewer, pointclouds, pickParams = {} ) {
- if(!pointclouds)return
-
- let renderer = viewer.renderer;
-
-
- if(viewport){ //转换到类似整个画面时
-
- /*let mouseInViewport = Utils.convertNDCToScreenPosition(pointer, null, viewport.resolution.x, viewport.resolution.y)
-
- pickParams.x = mouseInViewport.x //mouse.x / viewport.width;
- pickParams.y = mouseInViewport.y //renderer.domElement.clientHeight - mouse.y / viewport.height; */
- pickParams.x = mouse.x;
- pickParams.y = viewport.resolution.y - mouse.y;
- }else {
- pickParams.x = mouse.x;
- pickParams.y = renderer.domElement.clientHeight - mouse.y;
- }
-
- //console.log('getMousePointCloudIntersection')
-
-
-
- /* if(!raycaster){
- raycaster = new THREE.Raycaster();
- raycaster.setFromCamera(pointer, camera);
- } */
-
- let raycaster = new Raycaster();
- raycaster.setFromCamera(pointer, camera);
- let ray = raycaster.ray;
-
- let selectedPointcloud = null;
- let closestDistance = Infinity;
- let closestIntersection = null;
- let closestPoint = null;
-
-
- let density;
- let sizeType;
- let size = new Map();
- if(pickParams.isMeasuring || Potree.settings.displayMode == 'showPanos'){ //测量或全景模式提高精准度
- density = Potree.settings.pointDensity;
- Potree.settings.pointDensity = 'magnifier';
-
- pointclouds.forEach(e=>{//因为全景模式的pointSizeType是fixed所以要还原下
- size.set(e, e.temp.pointSize);
- sizeType = e.material.pointSizeType;
- e.material.pointSizeType = Potree.config.material.pointSizeType;
-
- e.changePointSize(Potree.config.material.realPointSize*2, true);//更改点云大小到能铺满为止,否则容易识别不到
- });
- Potree.updatePointClouds(pointclouds, camera, viewport.resolution );
- }else {
- if(viewer.viewports.filter(e=>!e.noPointcloud && e.active).length>1 || pickParams.cameraChanged){
- viewport.beforeRender && viewport.beforeRender();
- Potree.updatePointClouds(pointclouds, camera, viewport.resolution ); //不加这句的话hover久了会不准 因node是错的
- //但依旧需要camera真的移动到那个位置才能加载出点云
- }
-
- }
-
-
-
-
-
-
-
- let allPointclouds = [];
- for(let pointcloud of pointclouds){
-
- let point = pointcloud.pick(viewer, viewport, camera, ray, pickParams );
-
-
-
- if(!point){
- continue;
- }
- allPointclouds.push(pointcloud);
-
-
- let distance = camera.position.distanceTo(point.position);
- if (distance < closestDistance) {
- closestDistance = distance;
- selectedPointcloud = pointcloud;
- closestIntersection = point.position;
- closestPoint = point;
- }
- }
- if(pickParams.isMeasuring || Potree.settings.displayMode == 'showPanos'){
- Potree.settings.pointDensity = density;
-
- pointclouds.forEach(e=>{
- e.material.pointSizeType = sizeType;
- e.changePointSize(size.get(e));
-
- });
- }else {
- /* if(viewer.viewports.filter(e=>!e.noPointcloud).length>1){
- viewport.afterRender && viewport.afterRender()
- } */
- }
- if (selectedPointcloud) {
- return {
- location: closestIntersection,
- distance: closestDistance,
- pointcloud: selectedPointcloud,
- pointclouds: allPointclouds, //add
- point: closestPoint
- };
- } else {
- return null;
- }
-
- }
- static renderTargetToDataUrl(renderTarget, width, height, renderer, compressRatio = 0.7){
- let pixelCount = width * height;
- let buffer = new Uint8Array(4 * pixelCount);
- renderer.readRenderTargetPixels(renderTarget, 0, 0, width, height, buffer);
- var dataUrl = Potree.Utils.pixelsArrayToDataUrl(buffer, width, height, compressRatio);
- return dataUrl
-
- }
- static pixelsArrayToDataUrl(pixels, width, height, compressRatio = 0.7) {
- let canvas = document.createElement('canvas');
- canvas.width = width;
- canvas.height = height;
- let context = canvas.getContext('2d');
- pixels = new pixels.constructor(pixels);
- /* for (let i = 0; i < pixels.length; i++) {
- pixels[i * 4 + 3] = 255;
- } */
- // flip vertically
- let bytesPerLine = width * 4;
- for(let i = 0; i < parseInt(height / 2); i++){
- let j = height - i - 1;
- let lineI = pixels.slice(i * bytesPerLine, i * bytesPerLine + bytesPerLine);
- let lineJ = pixels.slice(j * bytesPerLine, j * bytesPerLine + bytesPerLine);
- pixels.set(lineJ, i * bytesPerLine);
- pixels.set(lineI, j * bytesPerLine);
- }
-
-
-
- let imageData = context.createImageData(width, height);
- imageData.data.set(pixels);
- context.putImageData(imageData, 0, 0);
- let dataURL = canvas.toDataURL(compressRatio);
- return dataURL;
- }
-
- static removeListeners(dispatcher, type){
- if (dispatcher._listeners === undefined) {
- return;
- }
- if (dispatcher._listeners[ type ]) {
- delete dispatcher._listeners[ type ];
- }
- }
- /* static mouseToRay(mouse, camera, width, height){
- let normalizedMouse = {
- x: (mouse.x / width) * 2 - 1,
- y: -(mouse.y / height) * 2 + 1
- };
- let vector = new THREE.Vector3(normalizedMouse.x, normalizedMouse.y, 0.5);
- let origin = camera.position.clone();
- vector.unproject(camera);
- let direction = new THREE.Vector3().subVectors(vector, origin).normalize();
- let ray = new THREE.Ray(origin, direction);
- return ray;
- } */
- static mouseToRay(pointer, camera ){
-
- let vector = new Vector3(pointer.x, pointer.y, 1);
- let origin = new Vector3(pointer.x, pointer.y, -1); //不能用camera.position,在orbitCamera时不准
- vector.unproject(camera);
- origin.unproject(camera);
- let direction = new Vector3().subVectors(vector, origin).normalize();
- let ray = new Ray(origin, direction);
- return ray;
- }
-
- static getPos2d(point, camera, dom, viewport){//获取一个三维坐标对应屏幕中的二维坐标
- var pos;
- if(math.closeTo(camera.position, point, 1e-5) ){ //和相机位置重合时显示会四处飘,看是要改成一直显示中间还是隐藏?
- pos = new Vector3(0,0,1.5); //1.5是为了不可见
- }else {
- pos = point.clone().project(camera); //比之前hotspot的计算方式写得简单 project用于3转2(求法同shader); unproject用于2转3 :new r.Vector3(e.x, e.y, -1).unproject(this.camera);
- }
-
-
- var x,y,left,top;
- x = (pos.x + 1) / 2 * dom.clientWidth * viewport.width;
- y = (1 - (pos.y + 1) / 2) * dom.clientHeight * viewport.height;
- left = viewport.left * dom.clientWidth;
- top = (1- viewport.bottom - viewport.height) * dom.clientHeight;
-
-
- var inSight = pos.x <= 1 && pos.x >= -1 //是否在屏幕中
- && pos.x <= 1 && pos.y >= -1;
-
-
- return {
- pos: new Vector2$1(left+x,top+y) ,// 屏幕像素坐标
- vector: pos, //(范围 -1 ~ 1)
- trueSide : pos.z<1, //trueSide为false时,即使在屏幕范围内可见,也是反方向的另一个不可以被渲染的点 参见Tag.update
- inSight : inSight, //在屏幕范围内可见,
- posInViewport: new Vector2$1(x,y)
- };
- }
-
- static projectedRadius(radius, camera, distance, screenWidth, screenHeight){
- if(camera instanceof OrthographicCamera){
- return Utils.projectedRadiusOrtho(radius, camera.projectionMatrix, screenWidth, screenHeight);
- }else if(camera instanceof PerspectiveCamera){
- return Utils.projectedRadiusPerspective(radius, camera.fov * Math.PI / 180, distance, screenHeight);
- }else {
- throw new Error("invalid parameters");
- }
- }
- static projectedRadiusPerspective(radius, fov, distance, screenHeight) {
- let projFactor = (1 / Math.tan(fov / 2)) / distance;
- projFactor = projFactor * screenHeight / 2;
- return radius * projFactor;
- }
- static projectedRadiusOrtho(radius, proj, screenWidth, screenHeight) {
- let p1 = new Vector4(0);
- let p2 = new Vector4(radius);
- p1.applyMatrix4(proj);
- p2.applyMatrix4(proj);
- p1 = new Vector3(p1.x, p1.y, p1.z);
- p2 = new Vector3(p2.x, p2.y, p2.z);
- p1.x = (p1.x + 1.0) * 0.5 * screenWidth;
- p1.y = (p1.y + 1.0) * 0.5 * screenHeight;
- p2.x = (p2.x + 1.0) * 0.5 * screenWidth;
- p2.y = (p2.y + 1.0) * 0.5 * screenHeight;
- return p1.distanceTo(p2);
- }
-
-
- static topView(camera, node){
- camera.position.set(0, 1, 0);
- camera.rotation.set(-Math.PI / 2, 0, 0);
- camera.zoomTo(node, 1);
- }
- static frontView (camera, node) {
- camera.position.set(0, 0, 1);
- camera.rotation.set(0, 0, 0);
- camera.zoomTo(node, 1);
- }
- static leftView (camera, node) {
- camera.position.set(-1, 0, 0);
- camera.rotation.set(0, -Math.PI / 2, 0);
- camera.zoomTo(node, 1);
- }
- static rightView (camera, node) {
- camera.position.set(1, 0, 0);
- camera.rotation.set(0, Math.PI / 2, 0);
- camera.zoomTo(node, 1);
- }
-
- static findClosestGpsTime(target, viewer){
- const start = performance.now();
- const nodes = [];
- for(const pc of viewer.scene.pointclouds){
- nodes.push(pc.root);
- for(const child of pc.root.children){
- if(child){
- nodes.push(child);
- }
- }
- }
- let closestNode = null;
- let closestIndex = Infinity;
- let closestDistance = Infinity;
- let closestValue = 0;
- for(const node of nodes){
- const isOkay = node.geometryNode != null
- && node.geometryNode.geometry != null
- && node.sceneNode != null;
- if(!isOkay){
- continue;
- }
- let geometry = node.geometryNode.geometry;
- let gpsTime = geometry.attributes["gps-time"];
- let range = gpsTime.potree.range;
- for(let i = 0; i < gpsTime.array.length; i++){
- let value = gpsTime.array[i];
- value = value * (range[1] - range[0]) + range[0];
- const distance = Math.abs(target - value);
- if(distance < closestDistance){
- closestIndex = i;
- closestDistance = distance;
- closestValue = value;
- closestNode = node;
- //console.log("found a closer one: " + value);
- }
- }
- }
- const geometry = closestNode.geometryNode.geometry;
- const position = new Vector3(
- geometry.attributes.position.array[3 * closestIndex + 0],
- geometry.attributes.position.array[3 * closestIndex + 1],
- geometry.attributes.position.array[3 * closestIndex + 2],
- );
- position.applyMatrix4(closestNode.sceneNode.matrixWorld);
- const end = performance.now();
- const duration = (end - start);
- console.log(`duration: ${duration.toFixed(3)}ms`);
- return {
- node: closestNode,
- index: closestIndex,
- position: position,
- };
- }
- /**
- *
- * 0: no intersection
- * 1: intersection
- * 2: fully inside
- */
- static frustumSphereIntersection (frustum, sphere) {
- let planes = frustum.planes;
- let center = sphere.center;
- let negRadius = -sphere.radius;
- let minDistance = Number.MAX_VALUE;
- for (let i = 0; i < 6; i++) {
- let distance = planes[ i ].distanceToPoint(center);
- if (distance < negRadius) {
- return 0;
- }
- minDistance = Math.min(minDistance, distance);
- }
- return (minDistance >= sphere.radius) ? 2 : 1;
- }
- // code taken from three.js
- // ImageUtils - generateDataTexture()
- static generateDataTexture (width, height, color) {
- let size = width * height;
- let data = new Uint8Array(4 * width * height);
- let r = Math.floor(color.r * 255);
- let g = Math.floor(color.g * 255);
- let b = Math.floor(color.b * 255);
- for (let i = 0; i < size; i++) {
- data[ i * 3 ] = r;
- data[ i * 3 + 1 ] = g;
- data[ i * 3 + 2 ] = b;
- }
- let texture = new DataTexture(data, width, height, RGBAFormat);
- texture.needsUpdate = true;
- texture.magFilter = NearestFilter;
- return texture;
- }
- // from http://stackoverflow.com/questions/901115/how-can-i-get-query-string-values-in-javascript
- static getParameterByName (name) {
- name = name.replace(/[[]/, '\\[').replace(/[\]]/, '\\]');
- let regex = new RegExp('[\\?&]' + name + '=([^&#]*)');
- let results = regex.exec(document.location.search);
- return results === null ? null : decodeURIComponent(results[1].replace(/\+/g, ' '));
- }
- static setParameter (name, value) {
- // value = encodeURIComponent(value);
- name = name.replace(/[[]/, '\\[').replace(/[\]]/, '\\]');
- let regex = new RegExp('([\\?&])(' + name + '=([^&#]*))');
- let results = regex.exec(document.location.search);
- let url = window.location.href;
- if (results === null) {
- if (window.location.search.length === 0) {
- url = url + '?';
- } else {
- url = url + '&';
- }
- url = url + name + '=' + value;
- } else {
- let newValue = name + '=' + value;
- url = url.replace(results[2], newValue);
- }
- window.history.replaceState({}, '', url);
- }
- static createChildAABB(aabb, index){
- let min = aabb.min.clone();
- let max = aabb.max.clone();
- let size = new Vector3().subVectors(max, min);
- if ((index & 0b0001) > 0) {
- min.z += size.z / 2;
- } else {
- max.z -= size.z / 2;
- }
- if ((index & 0b0010) > 0) {
- min.y += size.y / 2;
- } else {
- max.y -= size.y / 2;
- }
- if ((index & 0b0100) > 0) {
- min.x += size.x / 2;
- } else {
- max.x -= size.x / 2;
- }
- return new Box3(min, max);
- }
- // see https://stackoverflow.com/questions/400212/how-do-i-copy-to-the-clipboard-in-javascript
- static clipboardCopy(text){
- let textArea = document.createElement("textarea");
- textArea.style.position = 'fixed';
- textArea.style.top = 0;
- textArea.style.left = 0;
- textArea.style.width = '2em';
- textArea.style.height = '2em';
- textArea.style.padding = 0;
- textArea.style.border = 'none';
- textArea.style.outline = 'none';
- textArea.style.boxShadow = 'none';
- textArea.style.background = 'transparent';
- textArea.value = text;
- document.body.appendChild(textArea);
- textArea.select();
- try {
- let success = document.execCommand('copy');
- if(success){
- console.log("copied text to clipboard");
- }else {
- console.log("copy to clipboard failed");
- }
- } catch (err) {
- console.log("error while trying to copy to clipboard");
- }
- document.body.removeChild(textArea);
- }
- static getMeasurementIcon(measurement){
- if (measurement instanceof Measure) {
- if (measurement.showDistances && !measurement.showArea && !measurement.showAngles) {
- return `${Potree.resourcePath}/icons/distance.svg`;
- } else if (measurement.showDistances && measurement.showArea && !measurement.showAngles) {
- return `${Potree.resourcePath}/icons/area.svg`;
- } else if (measurement.maxMarkers === 1) {
- return `${Potree.resourcePath}/icons/point.svg`;
- } else if (!measurement.showDistances && !measurement.showArea && measurement.showAngles) {
- return `${Potree.resourcePath}/icons/angle.png`;
- } else if (measurement.showHeight) {
- return `${Potree.resourcePath}/icons/height.svg`;
- } else {
- return `${Potree.resourcePath}/icons/distance.svg`;
- }
- } else if (measurement instanceof Profile) {
- return `${Potree.resourcePath}/icons/profile.svg`;
- } else if (measurement instanceof Volume) {
- return `${Potree.resourcePath}/icons/volume.svg`;
- } else if (measurement instanceof PolygonClipVolume) {
- return `${Potree.resourcePath}/icons/clip-polygon.svg`;
- }
- }
- static lineToLineIntersection(P0, P1, P2, P3){
- const P = [P0, P1, P2, P3];
- const d = (m, n, o, p) => {
- let result =
- (P[m].x - P[n].x) * (P[o].x - P[p].x)
- + (P[m].y - P[n].y) * (P[o].y - P[p].y)
- + (P[m].z - P[n].z) * (P[o].z - P[p].z);
- return result;
- };
- const mua = (d(0, 2, 3, 2) * d(3, 2, 1, 0) - d(0, 2, 1, 0) * d(3, 2, 3, 2))
- /**-----------------------------------------------------------------**/ /
- (d(1, 0, 1, 0) * d(3, 2, 3, 2) - d(3, 2, 1, 0) * d(3, 2, 1, 0));
- const mub = (d(0, 2, 3, 2) + mua * d(3, 2, 1, 0))
- /**--------------------------------------**/ /
- d(3, 2, 3, 2);
- const P01 = P1.clone().sub(P0);
- const P23 = P3.clone().sub(P2);
-
- const Pa = P0.clone().add(P01.multiplyScalar(mua));
- const Pb = P2.clone().add(P23.multiplyScalar(mub));
- const center = Pa.clone().add(Pb).multiplyScalar(0.5);
- return center;
- }
- static computeCircleCenter(A, B, C){
- const AB = B.clone().sub(A);
- const AC = C.clone().sub(A);
- const N = AC.clone().cross(AB).normalize();
- const ab_dir = AB.clone().cross(N).normalize();
- const ac_dir = AC.clone().cross(N).normalize();
- const ab_origin = A.clone().add(B).multiplyScalar(0.5);
- const ac_origin = A.clone().add(C).multiplyScalar(0.5);
- const P0 = ab_origin;
- const P1 = ab_origin.clone().add(ab_dir);
- const P2 = ac_origin;
- const P3 = ac_origin.clone().add(ac_dir);
- const center = Utils.lineToLineIntersection(P0, P1, P2, P3);
- return center;
- // Potree.Utils.debugLine(viewer.scene.scene, P0, P1, 0x00ff00);
- // Potree.Utils.debugLine(viewer.scene.scene, P2, P3, 0x0000ff);
- // Potree.Utils.debugSphere(viewer.scene.scene, center, 0.03, 0xff00ff);
- // const radius = center.distanceTo(A);
- // Potree.Utils.debugCircle(viewer.scene.scene, center, radius, new THREE.Vector3(0, 0, 1), 0xff00ff);
- }
- static getNorthVec(p1, distance, projection){
- if(projection){
- // if there is a projection, transform coordinates to WGS84
- // and compute angle to north there
- proj4.defs("pointcloud", projection);
- const transform = proj4("pointcloud", "WGS84");
- const llP1 = transform.forward(p1.toArray());
- let llP2 = transform.forward([p1.x, p1.y + distance]);
- const polarRadius = Math.sqrt((llP2[0] - llP1[0]) ** 2 + (llP2[1] - llP1[1]) ** 2);
- llP2 = [llP1[0], llP1[1] + polarRadius];
- const northVec = transform.inverse(llP2);
-
- return new Vector3(...northVec, p1.z).sub(p1);
- }else {
- // if there is no projection, assume [0, 1, 0] as north direction
- const vec = new Vector3(0, 1, 0).multiplyScalar(distance);
-
- return vec;
- }
- }
- static computeAzimuth(p1, p2, projection){
- let azimuth = 0;
- if(projection){
- // if there is a projection, transform coordinates to WGS84
- // and compute angle to north there
- let transform;
- if (projection.includes('EPSG')) {
- transform = proj4(projection, "WGS84");
- } else {
- proj4.defs("pointcloud", projection);
- transform = proj4("pointcloud", "WGS84");
- }
- const llP1 = transform.forward(p1.toArray());
- const llP2 = transform.forward(p2.toArray());
- const dir = [
- llP2[0] - llP1[0],
- llP2[1] - llP1[1],
- ];
- azimuth = Math.atan2(dir[1], dir[0]) - Math.PI / 2;
- }else {
- // if there is no projection, assume [0, 1, 0] as north direction
- const dir = [p2.x - p1.x, p2.y - p1.y];
- azimuth = Math.atan2(dir[1], dir[0]) - Math.PI / 2;
- }
- // make clockwise
- azimuth = -azimuth;
- return azimuth;
- }
- static async loadScript(url){
- return new Promise( resolve => {
- const element = document.getElementById(url);
- if(element){
- resolve();
- }else {
- const script = document.createElement("script");
- script.id = url;
- script.onload = () => {
- resolve();
- };
- script.src = url;
- document.body.appendChild(script);
- }
- });
- }
- static createSvgGradient(scheme){
- // this is what we are creating:
- //
- //<svg width="1em" height="3em" xmlns="http://www.w3.org/2000/svg">
- // <defs>
- // <linearGradient id="gradientID" gradientTransform="rotate(90)">
- // <stop offset="0%" stop-color="rgb(93, 78, 162)" />
- // ...
- // <stop offset="100%" stop-color="rgb(157, 0, 65)" />
- // </linearGradient>
- // </defs>
- //
- // <rect width="100%" height="100%" fill="url('#myGradient')" stroke="black" stroke-width="0.1em"/>
- //</svg>
- const gradientId = `${Math.random()}_${Date.now()}`;
-
- const svgn = "http://www.w3.org/2000/svg";
- const svg = document.createElementNS(svgn, "svg");
- svg.setAttributeNS(null, "width", "2em");
- svg.setAttributeNS(null, "height", "3em");
-
- { // <defs>
- const defs = document.createElementNS(svgn, "defs");
-
- const linearGradient = document.createElementNS(svgn, "linearGradient");
- linearGradient.setAttributeNS(null, "id", gradientId);
- linearGradient.setAttributeNS(null, "gradientTransform", "rotate(90)");
- for(let i = scheme.length - 1; i >= 0; i--){
- const stopVal = scheme[i];
- const percent = parseInt(100 - stopVal[0] * 100);
- const [r, g, b] = stopVal[1].toArray().map(v => parseInt(v * 255));
- const stop = document.createElementNS(svgn, "stop");
- stop.setAttributeNS(null, "offset", `${percent}%`);
- stop.setAttributeNS(null, "stop-color", `rgb(${r}, ${g}, ${b})`);
- linearGradient.appendChild(stop);
- }
- defs.appendChild(linearGradient);
- svg.appendChild(defs);
- }
- const rect = document.createElementNS(svgn, "rect");
- rect.setAttributeNS(null, "width", `100%`);
- rect.setAttributeNS(null, "height", `100%`);
- rect.setAttributeNS(null, "fill", `url("#${gradientId}")`);
- rect.setAttributeNS(null, "stroke", `black`);
- rect.setAttributeNS(null, "stroke-width", `0.1em`);
- svg.appendChild(rect);
-
- return svg;
- }
- static async waitAny(promises){
-
- return new Promise( (resolve) => {
- promises.map( promise => {
- promise.then( () => {
- resolve();
- });
- });
- });
- }
- }
- Utils.screenPass = new function () {
- this.screenScene = new Scene();
- this.screenQuad = new Mesh(new PlaneBufferGeometry(2, 2, 1));
- this.screenQuad.material.depthTest = true;
- this.screenQuad.material.depthWrite = true;
- this.screenQuad.material.transparent = true;
- this.screenScene.add(this.screenQuad);
- this.camera = new Camera();
- this.render = function (renderer, material, target) {
- this.screenQuad.material = material;
- if (typeof target === 'undefined') {
- renderer.render(this.screenScene, this.camera);
- } else {
- renderer.setRenderTarget(target);
- renderer.render(this.screenScene, this.camera);
- }
- };
- }();
- //add
- Utils.computePointcloudsBound = function(pointclouds){
- var boundingBox = new Box3();
- pointclouds.forEach(pointcloud=>{
- pointcloud.updateBound();
- boundingBox.union(pointcloud.bound);
- });
- var boundSize = boundingBox.getSize(new Vector3);
- var center = boundingBox.getCenter(new Vector3);
- return {boundSize, center, boundingBox}
- };
- Utils.convertScreenPositionToNDC = function(pointer, mouse, width, height) {
- return pointer = pointer || new Vector2$1,
- pointer.x = mouse.x / width * 2 - 1,
- pointer.y = 2 * -(mouse.y / height) + 1,
- pointer
- };
- Utils.convertNDCToScreenPosition = function(pointer, mouse, width, height) {
- return mouse = mouse || new Vector2$1,
- mouse.x = Math.round((pointer.x + 1 ) / 2 * width),
- mouse.y = Math.round(-(pointer.y - 1 ) / 2 * height),
- mouse
- };
- Utils.getOrthoCameraMoveVec = function(pointerDelta, camera ){//获取当camera为Ortho型时 屏幕点1 到 屏幕点2 的三维距离
-
- let cameraViewWidth = camera.right / camera.zoom;
- let cameraViewHeight = camera.top / camera.zoom;
- let moveVec = new Vector3;
- moveVec.set( pointerDelta.x * cameraViewWidth , pointerDelta.y * cameraViewHeight , 0).applyQuaternion(camera.quaternion);
- return moveVec
- };
- Utils.VectorFactory = {
- fromArray : function(t) {
- if (t) {
- if (t.length < 2 || t.length > 3)
- console.error("Wrong number of ordinates for a point!");
- return 3 === t.length ? (new Vector3).fromArray(t) : (new Vector2$1).fromArray(t)
- }
- },
- fromArray3 : function(t) {
- if (t) {
- if (3 !== t.length)
- console.error("Wrong number of ordinates for a point!");
- return (new Vector3).fromArray(t)
- }
- },
- fromArray2 : function(t) {
- if (t) {
- if (2 !== t.length)
- console.error("Wrong number of ordinates for a point!");
- return (new Vector2$1).fromArray(t)
- }
- },
- toString : function(t) {
- return t.x.toFixed(8) + "," + t.y.toFixed(8) + "," + t.z.toFixed(3)
- }
- };
-
- Utils.QuaternionFactory = {
- rot90 : (new Quaternion).setFromAxisAngle(new Vector3(0,0,1), MathUtils.degToRad(-90)),
- fromArray : function(t) {
- if (t) {
- if (4 !== t.length)
- console.error("Wrong number of ordinates for a quaternion!");
- return new Quaternion(t[1],t[2],t[3],t[0]).multiply(this.rot90)
- }
- }
- ,
- toArray : function(t) {
- if (t) {
- var e = t.clone().multiply(a).toArray();
- return [e[3], e[0], e[1], e[2]]
- }
- }
- ,
- fromLonLat : function(t) {
- if (t)
- return (new Quaternion).setFromEuler(new Euler(t.lon,t.lat,0))
- }
- ,
- toLonLat : function(t) {
- if (t) {
- var e = (new Euler).setFromQuaternion(t);
- return {
- lon: e.x,
- lat: e.y
- }
- }
- }
-
-
- };
-
-
- Utils.datasetPosTransform = function(o={}){
-
- let pointcloud = o.pointcloud || viewer.scene.pointclouds.find(e=>e.dataset_id == o.datasetId);
- let tranMatrix;
- if(pointcloud){
- tranMatrix = o.fromDataset ? pointcloud.transformMatrix : pointcloud.transformInvMatrix;
- }else {
- if(Potree.settings.intersectOnObjs){
- let object = viewer.objs.children.find(e=>e.dataset_id == o.datasetId);
- if(object){
- tranMatrix = o.fromDataset ? object.matrixWorld : new Matrix4().copy(object.matrixWorld).invert();
- }
- }
- }
- if(tranMatrix){
- return (new Vector3).copy(o.position).applyMatrix4(tranMatrix)
- }else {
- if(o.datasetId != void 0){
- console.error(`datasetPosTransform找不到datasetId为${o.datasetId}的数据集,请检查(热点?测量线?)数据`);
- //很可能是旧的热点,需要删除
- }
- }
-
- };
- Utils.datasetRotTransform = function(o={}){
- let pointcloud = o.pointcloud || viewer.scene.pointclouds.find(e=>e.dataset_id == o.datasetId);
- if(pointcloud){
- var matrix, newMatrix, result;
-
- if(o.rotation){
- matrix = new Matrix4().makeRotationFromEuler(o.rotation);
- }else if(o.quaternion){
- matrix = new Matrix4().makeRotationFromQuaternion(o.quaternion);
- }else if(o.matrix){
- matrix = o.matrix.clone();
- }else {
- return
- }
- let rotateMatrix = o.fromDataset ? pointcloud.rotateMatrix : pointcloud.rotateInvMatrix;
- newMatrix = new Matrix4().multiplyMatrices(rotateMatrix, matrix );
-
- if(o.getRotation){
- result = new Euler().setFromRotationMatrix(newMatrix);
- }else if(o.getQuaternion){
- result = new Quaternion().setFromRotationMatrix(newMatrix);
- }else if(o.getMatrix){
- result = newMatrix;
- }
-
- return result
-
- }
-
- };
- Utils.isInsideFrustum = function(bounding, camera){// boundingBox在视野范围内有可见部分
- let frustumMatrix = new Matrix4;
- frustumMatrix.multiplyMatrices(camera.projectionMatrix, camera.matrixWorldInverse);
-
- let frustum = new Frustum();
- frustum.setFromProjectionMatrix(frustumMatrix);
-
- if(bounding instanceof Sphere){
- return frustum.intersectsSphere(bounding)
- }else {
- return frustum.intersectsBox(bounding)
- }
- };
- Utils.isInsideBox = function(object, boxMatrixInverse){//object可以是点或者bounding, box原为1*1*1,但可能形变
- let frustum = new Frustum();
- frustum.setFromProjectionMatrix(boxMatrixInverse);
-
- if(object instanceof Box3){
- return frustum.intersectsSphere(object)
- }else if(object instanceof Array){//点合集,先求Sphere setFromPoints
- let sphere = new Sphere();
- sphere.setFromPoints(object);
- return this.isInsideBox(sphere, boxMatrixInverse)
- }else if(object instanceof Sphere){
- return frustum.intersectsSphere(object)
- }else if(object instanceof Vector3){
- return frustum.containsPoint(object)
- }
- /* containsPoint: ƒ containsPoint( point )
- intersectsBox: ƒ intersectsBox( box )
- intersectsObject: ƒ intersectsObject( object )//geo
- intersectsSphere: ƒ intersectsSphere( sphere )
- intersectsSprite: ƒ intersectsSprite( sprite )
- */
- };
-
- Utils.getIntersect = function (camera, meshes, pointer, raycaster) {
- //获取鼠标和meshes交点
- camera.updateMatrixWorld();
- if(!raycaster){//getMouseIntersect
- raycaster = new Raycaster();
- var origin = new Vector3(pointer.x, pointer.y, -1).unproject(camera),
- end = new Vector3(pointer.x, pointer.y, 1).unproject(camera);
- var dir = end.sub(origin).normalize();
- raycaster.set(origin, dir);
- }
-
- meshes.forEach(e=>{
- raycaster.layers.enable(math.getBaseLog(e.layers.mask,2));
- });
- var n = raycaster.intersectObjects(meshes);
- if (0 === n.length) return null
- return n[0]
- };
- var start = function(dom, mapDom, number, fileServer, webSite){ //t-Zvd3w0m
- /* {
- let obj = JSON.parse(localStorage.getItem('setting'))
- for(let i in obj){
- console.log(i + ': ' + obj[i])
- }
- }
- */
- Potree.settings.number = number || 't-o5YMR13';// 't-iksBApb'// 写在viewer前
- Potree.fileServer = fileServer;
- webSite && (Potree.settings.webSite = webSite);
-
-
- let viewer = new Potree.Viewer(dom , mapDom);
-
- let Alignment = viewer.modules.Alignment;
-
-
- //let pointDensity = config.pointDensity.middle
- viewer.setEDLEnabled(false);
- viewer.setFOV(config$1.view.fov);
- //viewer.setPointBudget(pointDensity.pointBudget);
- viewer.loadSettingsFromURL();
-
-
-
- if(!Potree.settings.isOfficial){
- viewer.loadGUI(() => {
- viewer.setLanguage('en');
- //$("#menu_appearance").next().show();
- $("#menu_tools").next().show();
- $("#menu_scene").next().show();
- $("#siteModel").show();
- //$("#alignment").show();
- viewer.toggleSidebar();
- });
- Potree.settings.sizeFitToLevel = true;//当type为衰减模式时自动根据level调节大小。每长一级,大小就除以2
- }
- Potree.loadDatasetsCallback = function(data, ifReload){
- if(!data || data.length == 0)return console.error('getDataSet加载的数据为空')
-
- Potree.datasetData = data;
- viewer.transform = null;
- var datasetLength = data.length;
- var pointcloudLoaded = 0;
- var panosLoaded = 0;
- var pointcloudLoadDone = function(){//点云cloud.js加载完毕后
- viewer.updateModelBound();
- let {boundSize, center} = viewer.bound;
-
- Potree.Log(`中心点: ${math.toPrecision(center.toArray(),2)}, boundSize: ${math.toPrecision(boundSize.toArray(),2)} ` , null, 12);
-
- if(!Potree.settings.isOfficial){
- Potree.loadMapEntity('all'); //加载floorplan
- }
-
-
- if(!ifReload){
- viewer.scene.view.setView({
- position: center.clone().add(new Vector3(10,5,10)),
- target: center
- });
-
- viewer.dispatchEvent({type:'loadPointCloudDone'});
-
- if(!Potree.settings.UserPointDensity){
- Potree.settings.UserPointDensity = 'high';//'middle'
- }
-
- Potree.Log('loadPointCloudDone 点云加载完毕', null, 10);
- }
-
- };
-
-
- var panosLoadDone = function(){
-
-
- viewer.images360.loadDone();
- viewer.scene.add360Images(viewer.images360);
- viewer.mapViewer.addListener(viewer.images360);
-
-
- {//初始位置
- var urlFirstView = false;
- var panoId = browser.urlHasValue('pano',true);
- if(panoId !== ''){
- var pos;
- var pano = viewer.images360.panos.find(e=>e.id==panoId);
- if(pano){
- viewer.images360.focusPano({
- pano,
- duration:0,
- callback:()=>{/* Potree.settings.displayMode = 'showPanos' */}
- });
-
- }
- }else {//考虑到多数据集距离很远,或者像隧道那种场景,要使视野范围内一定能看到点云,最好初始点设置在漫游点上
-
- let {boundSize, center} = viewer.bound;
-
- let pano = viewer.images360.findNearestPano(center);
-
- /* pano && viewer.scene.view.setView({
- position: pano.position.clone().add(new THREE.Vector3(10,10,10)),
- target: pano.position
- }) */
-
- pano && viewer.images360.flyToPano({
- pano, duration:0,
- target : viewer.images360.bound.center
- });
-
-
- }
- }
-
-
-
- viewer.addVideo();//addFire()
-
- console.log('allLoaded');
- viewer.dispatchEvent('allLoaded');
- };
-
- var transformPointcloud = (pointcloud, dataset)=>{
- var locationLonLat = dataset.location.slice(0,2);
- //当只有一个dataset时,无论如何transform 点云和漫游点都能对应上。
- var location = viewer.transform.lonlatToLocal.forward(locationLonLat); //transform.inverse()
- //初始化位置
-
- viewer.sidebar && viewer.sidebar.addAlignmentButton(pointcloud);
-
- //dataset.orientation = 0
-
- Alignment.rotate(pointcloud, null, dataset.orientation);
- Alignment.translate(pointcloud, new Vector3(location[0], location[1], dataset.location[2]));
-
- pointcloud.updateMatrixWorld();
-
-
- Potree.Log(`点云${pointcloud.dataset_id}旋转值:${pointcloud.orientationUser}, 位置${math.toPrecision(pointcloud.translateUser.toArray(),3)}, 经纬度 ${locationLonLat}, spacing ${pointcloud.material.spacing}`, null, 17 );
-
-
- //-------------------
-
- //viewer.mapView.showSources(false);
- };
-
- if(!Potree.settings.originDatasetId)Potree.settings.originDatasetId = data[0].id;
- var originDataset = data.find(e=>e.id == Potree.settings.originDatasetId);
-
- {//拿初始数据集作为基准。它的位置是000
- var locationLonLat = originDataset.location.slice(0,2);
- proj4.defs("NAVVIS:TMERC", "+proj=tmerc +ellps=WGS84 +lon_0=" + locationLonLat[0].toPrecision(15) + " +lat_0=" + locationLonLat[1].toPrecision(15));
- proj4.defs("WGS84", "+proj=longlat +ellps=WGS84 +datum=WGS84 +no_defs");
-
- let transform1 = proj4("WGS84", "NAVVIS:TMERC"); //这个ok TMERC是展开的平面投影
- let transform2 = proj4("+proj=tmerc +lat_0=0 +lon_0=123 +k=1 +x_0=500000 +y_0=0 +ellps=GRS80 +units=m +no_defs;");
-
-
- viewer.transform = {
- lonlatToLocal : transform1,
- lonlatTo4550 : transform2 // 转大地坐标EPSG:4550
- };
-
- viewer.mapViewer && viewer.mapViewer.mapLayer.maps[0].updateProjection();
-
- }
-
-
- data.forEach((dataset,index)=>{
- if(!ifReload){
- var datasetCode = dataset.sceneCode || dataset.name; //对应4dkk的场景码
- var cloudPath = `${Potree.settings.urls.prefix}/${Potree.settings.webSite}/${datasetCode}/data/${datasetCode}/webcloud/cloud.js`;
- var timeStamp = dataset.createTime ? dataset.createTime.replace(/[^0-9]/ig,'') : ''; //每重算一次后缀随createTime更新一次
- //console.warn(dataset.name, 'timeStamp', timeStamp)
- Potree.loadPointCloud(cloudPath, dataset.name ,datasetCode, timeStamp, e => {
- let scene = viewer.scene;
- let pointcloud = e.pointcloud;
- let config = Potree.config.material;
- let material = pointcloud.material;
-
- pointcloud.hasDepthTex = Potree.settings.useDepthTex && (!!dataset.has_depth || Potree.settings.isLocalhost && Potree.settings.number == 'SS-t-7DUfWAUZ3V'); //test
- material.minSize = config.minSize;
- material.maxSize = config.maxSize;
- material.pointSizeType = config.pointSizeType; //Potree.PointSizeType[config.pointSizeType]//Potree.PointSizeType.ADAPTIVE;//FIXED
- pointcloud.changePointSize(config.realPointSize); //material.size = config.pointSize;
- pointcloud.changePointOpacity(1);
- material.shape = Potree.PointShape.SQUARE;
- pointcloud.color = pointcloud.material.color = dataset.color;
- pointcloud.dataset_id = dataset.id;//供漫游点找到属于的dataset点云
- pointcloud.timeStamp = timeStamp;
- transformPointcloud(pointcloud,dataset);
- scene.addPointCloud(pointcloud);
- pointcloudLoaded ++;
- if(pointcloudLoaded == datasetLength)pointcloudLoadDone();
-
- Potree.loadPanos(dataset.id, (data) => {
- //console.log('loadPanos',dataset.sceneCode, dataset.id, data)
- viewer.images360.addPanoData(data, dataset.id );
- panosLoaded ++;
- if(panosLoaded == datasetLength){
- panosLoadDone();
- }
- });
- });
- }else {
- let pointcloud = viewer.scene.pointclouds.find(p => p.dataset_id == dataset.id);
- if(!pointcloud){
- Potree.Log('数据集id变了,自动使用第一个','#500');
- pointcloud = viewer.scene.pointclouds[0];
- }
- //先归零
- Alignment.translate(pointcloud, pointcloud.translateUser.clone().negate());
- Alignment.rotate(pointcloud, null, - pointcloud.orientationUser);
-
- transformPointcloud(pointcloud, dataset);
-
- }
-
- });
-
- if(ifReload){
-
- //loadDone()
- }
-
-
- };
-
-
-
- Potree.loadDatasets(Potree.loadDatasetsCallback);
-
-
- window.testTransform = function(locationLonLat, location1, location2){
- proj4.defs("NAVVIS:test", "+proj=tmerc +ellps=WGS84 +lon_0=" + locationLonLat[0].toPrecision(15) + " +lat_0=" + locationLonLat[1].toPrecision(15));
-
- let transform = proj4("WGS84", "NAVVIS:test"); //这个ok navvis里也是这两种转换 见proj4Factory
- if(location1){//经纬度
- return transform.forward(location1)
- }else {
- return transform.inverse(location2)
- }
-
- };
- window.THREE = THREE$1;
- window.buttonFunction = function(){
-
-
-
- viewer.scene.pointclouds.forEach(e=>e.predictNodeMaxLevel());
-
-
-
- /*
- viewer.startScreenshot({type:'measure', measurement:viewer.scene.measurements[0]})
-
- viewer.modules.RouteGuider.routeStart = new THREE.Vector3(0,0,-1.3)
- viewer.modules.RouteGuider.routeEnd = new THREE.Vector3(-10,0,-1.3)
- */
-
- };
-
-
- if(Potree.settings.isLocalhost){
- let before = {};
- viewer.inputHandler.addEventListener('keydown',e=>{ //测试的代码
- if(e.event.key == 't'){
- viewer.images360.cube.visible = true;
- viewer.images360.cube.material.wireframe = true;
- }else if(e.event.key == 'y'){
- viewer.images360.cube.material.wireframe = false;
- viewer.images360.cube.visible = Potree.settings.displayMode == 'showPanos';
- }
- });
-
- }
-
-
- };
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
- //=======================================================================
- /*
- 漫游点编辑
- */
- //=======================================================================
- var panoEditStart = function(dom, number, fileServer, webSite){
- Potree.settings.editType = 'pano';
- Potree.settings.number = number;
-
- Potree.settings.unableNavigate = true;
-
-
- let viewer = new Potree.Viewer(dom);
- let Alignment = viewer.modules.Alignment;
- viewer.setEDLEnabled(false);
- viewer.setFOV(config$1.view.fov);
- viewer.loadSettingsFromURL();
- let datasetLoaded = 0;
-
- if(!Potree.settings.isOfficial){
- viewer.loadGUI(() => {
- viewer.setLanguage('en');
- $("#menu_tools").next().show();
- $("#panos").show();
- $("#alignment").show();
- viewer.toggleSidebar();
- });
- Potree.settings.sizeFitToLevel = true;
- }
-
- var pointcloudLoadDone = function( ){//所有点云cloud.js加载完毕后
-
-
-
- viewer.scene.pointclouds.forEach(c=>{
- transformPointcloud(c);
- });
- viewer.images360.loadDone();
- viewer.scene.add360Images(viewer.images360);
-
- viewer.updateModelBound();
- let {boundSize, center} = viewer.bound;
-
- Potree.Log(`中心点: ${math.toPrecision(center.toArray(),2)}, boundSize: ${math.toPrecision(boundSize.toArray(),2)} ` , null, 12);
-
- viewer.scene.view.setView({
- position: center.clone().add(new Vector3(10,5,10)),
- target: center
- });
-
- viewer.dispatchEvent({type:'loadPointCloudDone'});
-
- if(!Potree.settings.UserPointDensity){
- Potree.settings.UserPointDensity = 'panoEdit';//'middle'
- }
-
- Potree.Log('loadPointCloudDone 点云加载完毕', null, 10);
-
- viewer.dispatchEvent('allLoaded');
- };
-
-
- /* var transformPointcloud = (pointcloud )=>{ //初始化位置
- viewer.sidebar && viewer.sidebar.addAlignmentButton(pointcloud)
-
- let orientation = pointcloud.panos[0].dataRotation.z
- let location = pointcloud.panos[0].dataPosition.clone().negate()
- Alignment.rotate(pointcloud, null, orientation )
- Alignment.translate(pointcloud, location )
-
- pointcloud.updateMatrixWorld()
-
- } */
-
-
-
-
-
- let loadPanosDone = Potree.loadPanosDone = (datasetId, panoData)=>{ //一个数据集获取到它的panos后
-
- Potree.settings.datasetsPanos[datasetId] = {panoData, panos:[]};
-
- console.log('panoData', datasetId, panoData);
-
- let panoCount = panoData.length;
- let pointcloudLoaded = 0;
-
- let datasetsCount = Object.keys(Potree.settings.datasetsPanos).length;
-
-
- panoData.forEach((pano, index)=>{
- //let cloudPath = `${Potree.scriptPath}/data/panoEdit/uuidcloud/${pano.uuid}/cloud.js`
- let cloudPath = `https://laser-oss.4dkankan.com/testdata/${Potree.settings.number}/data/bundle_${Potree.settings.number}/building/uuidcloud/${pano.uuid}/cloud.js`;
-
- let name = datasetId + '-'+pano.uuid;
- let timeStamp = 0;
- pano.index = index; //注意:index不等于uuid,因为有的uuid缺失。但是visibles中存的是下标!
-
- Potree.loadPointCloud(cloudPath, name , name, timeStamp, e => { //开始加载点云
- let scene = viewer.scene;
- let pointcloud = e.pointcloud;
- let config = Potree.config.material;
- let material = pointcloud.material;
- material.minSize = config.minSize;
- material.maxSize = config.maxSize;
- material.pointSizeType = /* 'ADAPTIVE'// */config.pointSizeType; //Potree.PointSizeType[config.pointSizeType]//Potree.PointSizeType.ADAPTIVE;//FIXED
- pointcloud.changePointSize( 0.2 /* config.realPointSize */ ); //material.size = config.pointSize;
- pointcloud.changePointOpacity(1);
- material.shape = Potree.PointShape.SQUARE;
- pointcloud.color = config.pointColor;
- pointcloud.dataset_id = datasetId; //多个点云指向一个datasetId
- pointcloud.panoUuid = pano.uuid;
- pointcloud.timeStamp = timeStamp;
-
- //transformPointcloud(pointcloud, pano)
- scene.addPointCloud(pointcloud);
- pointcloudLoaded ++;
-
- if(pointcloudLoaded == panoCount ){
- datasetLoaded ++;
- viewer.images360.addPanoData(panoData , datasetId );
- if(datasetLoaded == datasetsCount){
- pointcloudLoadDone();
- }
-
- }
-
- });
-
- });
-
- };
-
- if(!Potree.settings.isOfficial){
- Potree.settings.datasetsPano = {'testDataset':null};
- Potree.loadPanosInfo( data=>{loadPanosDone('testDataset', data.sweepLocations);} );
-
- }
-
- };
-
- var mergeEditStart = function(dom){
- Potree.settings.editType = 'merge';
- Potree.settings.intersectOnObjs = true;
- Potree.settings.boundAddObjs = true;
-
- let viewer = new Potree.Viewer(dom );
-
- let Alignment = viewer.modules.Alignment;
-
- viewer.setEDLEnabled(false);
- viewer.setFOV(config$1.view.fov);
- viewer.loadSettingsFromURL();
- {
- viewer.mainViewport.view.position.set(100,100,200);
- viewer.mainViewport.view.lookAt(0,0,0);
-
- viewer.updateModelBound();//init
- //this.bound = new THREE.Box3(new THREE.Vector3(-1,-1,-1),new THREE.Vector3(1,1,1))
-
- viewer.transformationTool.setModeEnable('scale',false);
- viewer.ssaaRenderPass.sampleLevel = 1; //奇怪好像没啥锯齿? sampleLevel为1 的话,ground就不会
- }
-
- Potree.settings.sizeFitToLevel = true;//当type为衰减模式时自动根据level调节大小。每长一级,大小就除以2
- Potree.loadPointCloudScene = function(datasetCode, datasetName){//对应4dkk的场景码
-
- viewer.transform = null;
-
- var cloudPath = `${Potree.settings.urls.prefix}/${Potree.settings.webSite}/${datasetCode}/data/${datasetCode}/webcloud/cloud.js`;
- var timeStamp = dataset.createTime ? dataset.createTime.replace(/[^0-9]/ig,'') : ''; //每重算一次后缀随createTime更新一次
- //console.warn(dataset.name, 'timeStamp', timeStamp)
- Potree.loadPointCloud(cloudPath, datasetName ,datasetCode, timeStamp, e => {
- let scene = viewer.scene;
- let pointcloud = e.pointcloud;
- let config = Potree.config.material;
- let material = pointcloud.material;
-
- material.minSize = config.minSize;
- material.maxSize = config.maxSize;
- material.pointSizeType = config.pointSizeType; //Potree.PointSizeType[config.pointSizeType]//Potree.PointSizeType.ADAPTIVE;//FIXED
- pointcloud.changePointSize(config.realPointSize); //material.size = config.pointSize;
- pointcloud.changePointOpacity(1);
- material.shape = Potree.PointShape.SQUARE;
- pointcloud.color = pointcloud.material.color = dataset.color;
- pointcloud.dataset_id = datasetCode; //dataset.id;//供漫游点找到属于的dataset点云
- pointcloud.timeStamp = timeStamp;
- //transformPointcloud(pointcloud, dataset)
- scene.addPointCloud(pointcloud);
- {
-
- viewer.updateModelBound();
- let {boundSize, center} = viewer.bound;
- viewer.dispatchEvent({type:'loadPointCloudDone'});
- if(!Potree.settings.UserPointDensity){
- Potree.settings.UserPointDensity = 'high';//'middle'
- }
-
- Potree.Log('loadPointCloudDone 点云加载完毕', null, 10);
- }
-
- /* Potree.loadPanos(dataset.id, (data) => { //暂时不加载panos了,因为没有id
- //console.log('loadPanos',dataset.sceneCode, dataset.id, data)
- viewer.images360.addPanoData(data, dataset.id )
- viewer.images360.loadDone()
- viewer.scene.add360Images(viewer.images360); */
- viewer.dispatchEvent('allLoaded');
- });
- };
-
-
-
-
-
-
-
- let setMatrix = (pointcloud)=>{//为了漫游点变换,要算一下 类似setMatrix
-
- /* pointcloud.transformMatrix = new THREE.Matrix4().multiplyMatrices(pointcloud.matrix, pointcloud.pos1MatrixInvert)//还原一点位移
- pointcloud.transformInvMatrix.copy(pointcloud.transformMatrix).invert()
-
- pointcloud.rotateMatrix = new THREE.Matrix4().makeRotationFromEuler(pointcloud.rotation);
- pointcloud.rotateInvMatrix.copy(pointcloud.rotateMatrix).invert()
- pointcloud.panos.forEach(e=>e.transformByPointcloud()) */
- pointcloud.updateBound();
- pointcloud.getPanosBound();
- viewer.updateModelBound();
- };
- let moveModel = (e)=>{//根据鼠标移动的位置改变位置
-
- let camera = viewer.mainViewport.camera;
- var origin = new Vector3(e.pointer.x, e.pointer.y, -1).unproject(camera),
- end = new Vector3(e.pointer.x, e.pointer.y, 1).unproject(camera);
- var dir = end.sub(origin);
- let planeZ = 0;
- let r = (planeZ - origin.z)/dir.z;
- let x = r * dir.x + origin.x;
- let y = r * dir.y + origin.y;
-
- //过后改为根据intersect的点来设置底部高度;这样的话,需要发送高度
-
-
-
-
- if(modelType == 'laser'){
- /* modelEditing.translateUser.copy(pos)
- Alignment.setMatrix(modelEditing) */
- let pos = new Vector3(x,y,0/* planeZ */);
- modelEditing.position.copy(modelEditing.initialPosition).add(pos); //使位置居中在boundingBox
-
- }else {
- let pos = new Vector3(x,y, modelEditing.position.z /* planeZ */);
- modelEditing.position.copy(pos);
-
- }
- modelEditing.dispatchEvent("position_changed");
-
- };
- let cancelMove = ()=>{
- modelEditing = null;
- viewer.removeEventListener('global_mousemove', moveModel);
- viewer.removeEventListener('global_click', confirmPos);
- };
- let confirmPos = ()=>{
- MergeEditor.focusOnSelect(modelEditing);
- cancelMove();
- return {stopContinue:true}
- };
-
-
-
- let modelType, modelEditing, MergeEditor = viewer.modules.MergeEditor;
- Potree.addModel = function(prop, done, onprogress){ //加载模型
- let isFirstLoad = !prop.position; //在编辑时用户添加的
-
- let loadDone = (model)=>{
-
- if(isFirstLoad){
- MergeEditor.setModelBtmHeight(model, 0); //默认离地高度为0
- viewer.addEventListener('global_mousemove', moveModel);
- viewer.addEventListener('global_click', confirmPos, 3);
- }
- object.updateMatrixWorld();
- this.updateModelBound();
-
- done(modelEditing);
- };
-
- if(prop.type == 'laser'){
- Potree.loadPointCloudScene(prop.url, (pointcloud)=>{
- pointcloud.matrixAutoUpdate = true;
- pointcloud.initialPosition = pointcloud.position.clone();
-
-
- pointcloud.pos1MatrixInvert = new Matrix4().setPosition(pointcloud.initialPosition).invert();
- let maintainBtmZ = ()=>{
- MergeEditor.setModelBtmHeight(object);
- updateMatrix();
- };
- let updateMatrix = ()=>{
- setMatrix(pointcloud);
- };
- pointcloud.addEventListener('position_changed', updateMatrix );
- pointcloud.addEventListener("orientation_changed", maintainBtmZ );
- pointcloud.addEventListener("scale_changed", maintainBtmZ );
-
- loadDone();
- /* pointcloud.addEventListener('select',(e)=>{
- if(Potree.settings.displayMode == 'showPanos')return
- console.log('select',e)
- //viewer.setControls(viewer.orbitControls)
- MergeEditor.focusOnSelect(pointcloud)
-
- viewer.outlinePass.selectedObjects = [pointcloud]
- return {stopContinue:true}
- },1)
- pointcloud.addEventListener('deselect',(e)=>{
- console.log('deselect',e)
- //viewer.setControls(viewer.fpControls)
- viewer.outlinePass.selectedObjects = []
- }) */
-
- });
- }else {
-
-
- let callback = (object)=>{
- //focusOnSelect(object, 1000)
-
-
- modelEditing = object;
- object.isModel = true;
- object.dataset_id = Date.now(); //暂时
- /* object.addEventListener('select',(e)=>{
- if(Potree.settings.displayMode == 'showPanos')return
- console.log('select',e)
- viewer.setControls(viewer.orbitControls)
- MergeEditor.focusOnSelect(object)
-
- viewer.outlinePass.selectedObjects = [object]
- return {stopContinue:true}
- },1)
- object.addEventListener('deselect',(e)=>{
- console.log('deselect',e)
- viewer.setControls(viewer.fpControls)
- viewer.outlinePass.selectedObjects = []
- }) */
- let updateBound = ()=>{
- object.updateMatrixWorld();
- viewer.updateModelBound();
- };
- let maintainBtmZ = ()=>{
- MergeEditor.setModelBtmHeight(object);
- updateBound();
- };
- object.addEventListener('position_changed', updateBound );
- object.addEventListener("orientation_changed", maintainBtmZ );
- object.addEventListener("scale_changed", maintainBtmZ );
- loadDone();
- };
-
-
- viewer.loadModel({
- name: 'glb',
- glburl: prop.url, //0.3s
- transform : {
- rotation : prop.rotation,
- position : prop.position
- }
-
- },callback,onprogress);
-
- }
- };
- };
- /*
- 坐标转换问题:
- 由于控制点可以随便输入,所以本地和地理位置的转换也是可拉伸的。而navvis的转换是等比由中心展开,
- 所以对比两种转化方式时误差较大。
- 另外地理注册控制点是有参考数据集的,若参考数据集和我放置在0,0,0的数据集一致,就可直接使用,否则要转换。
- */
- class Action extends EventDispatcher {
- constructor (args = {}) {
- super();
- this.icon = args.icon || '';
- this.tooltip = args.tooltip;
- if (args.onclick !== undefined) {
- this.onclick = args.onclick;
- }
- }
- onclick (event) {
- }
- pairWith (object) {
- }
- setIcon (newIcon) {
- let oldIcon = this.icon;
- if (newIcon === oldIcon) {
- return;
- }
- this.icon = newIcon;
- this.dispatchEvent({
- type: 'icon_changed',
- action: this,
- icon: newIcon,
- oldIcon: oldIcon
- });
- }
- };
- //Potree.Actions = {};
- //
- //Potree.Actions.ToggleAnnotationVisibility = class ToggleAnnotationVisibility extends Potree.Action {
- // constructor (args = {}) {
- // super(args);
- //
- // this.icon = Potree.resourcePath + '/icons/eye.svg';
- // this.showIn = 'sidebar';
- // this.tooltip = 'toggle visibility';
- // }
- //
- // pairWith (annotation) {
- // if (annotation.visible) {
- // this.setIcon(Potree.resourcePath + '/icons/eye.svg');
- // } else {
- // this.setIcon(Potree.resourcePath + '/icons/eye_crossed.svg');
- // }
- //
- // annotation.addEventListener('visibility_changed', e => {
- // let annotation = e.annotation;
- //
- // if (annotation.visible) {
- // this.setIcon(Potree.resourcePath + '/icons/eye.svg');
- // } else {
- // this.setIcon(Potree.resourcePath + '/icons/eye_crossed.svg');
- // }
- // });
- // }
- //
- // onclick (event) {
- // let annotation = event.annotation;
- //
- // annotation.visible = !annotation.visible;
- //
- // if (annotation.visible) {
- // this.setIcon(Potree.resourcePath + '/icons/eye.svg');
- // } else {
- // this.setIcon(Potree.resourcePath + '/icons/eye_crossed.svg');
- // }
- // }
- //};
- class PathAnimation{
-
- constructor(path, start, end, speed, callback){
- this.path = path;
- this.length = this.path.spline.getLength();
- this.speed = speed;
- this.callback = callback;
- this.tween = null;
- this.startPoint = Math.max(start, 0);
- this.endPoint = Math.min(end, this.length);
- this.t = 0.0;
- }
- start(resume = false){
- if(this.tween){
- this.tween.stop();
- this.tween = null;
- }
-
- let tStart;
- if(resume){
- tStart = this.t;
- }else {
- tStart = this.startPoint / this.length;
- }
- let tEnd = this.endPoint / this.length;
- let animationDuration = (tEnd - tStart) * this.length * 1000 / this.speed;
-
- let progress = {t: tStart};
- this.tween = new TWEEN.Tween(progress).to({t: tEnd}, animationDuration);
- this.tween.easing(TWEEN.Easing.Linear.None);
- this.tween.onUpdate((e) => {
- this.t = progress.t;
- this.callback(progress.t);
- });
- this.tween.onComplete(() => {
- if(this.repeat){
- this.start();
- }
- });
- setTimeout(() => {
- this.tween.start();
- }, 0);
- }
- stop(){
- if(!this.tween){
- return;
- }
- this.tween.stop();
- this.tween = null;
- this.t = 0;
- }
- pause(){
- if(!this.tween){
- return;
- }
-
- this.tween.stop();
- TWEEN.remove(this.tween);
- this.tween = null;
- }
- resume(){
- this.start(true);
- }
- getPoint(t){
- return this.path.spline.getPoint(t);
- }
- }
- class AnimationPath{
- constructor (points = []) {
- this.points = points;
- this.spline = new CatmullRomCurve3(points);
- //this.spline.reparametrizeByArcLength(1 / this.spline.getLength().total);
- }
- get (t) {
- return this.spline.getPoint(t);
- }
- getLength () {
- return this.spline.getLength();
- }
- animate (start, end, speed, callback) {
- let animation = new PathAnimation(this, start, end, speed, callback);
- animation.start();
- return animation;
- }
- pause () {
- if (this.tween) {
- this.tween.stop();
- }
- }
- resume () {
- if (this.tween) {
- this.tween.start();
- }
- }
- getGeometry () {
- let geometry = new Geometry();
- let samples = 500;
- let i = 0;
- for (let u = 0; u <= 1; u += 1 / samples) {
- let position = this.spline.getPoint(u);
- geometry.vertices[i] = new Vector3(position.x, position.y, position.z);
- i++;
- }
- if(this.closed){
- let position = this.spline.getPoint(0);
- geometry.vertices[i] = new Vector3(position.x, position.y, position.z);
- }
- return geometry;
- }
- get closed(){
- return this.spline.closed;
- }
- set closed(value){
- this.spline.closed = value;
- }
- }
- class Annotation extends EventDispatcher {
- constructor (args = {}) {
- super();
- this.scene = null;
- this._title = args.title || 'No Title';
- this._description = args.description || '';
- this.offset = new Vector3();
- this.uuid = MathUtils.generateUUID();
- if (!args.position) {
- this.position = null;
- } else if (args.position.x != null) {
- this.position = args.position;
- } else {
- this.position = new Vector3(...args.position);
- }
- this.cameraPosition = (args.cameraPosition instanceof Array)
- ? new Vector3().fromArray(args.cameraPosition) : args.cameraPosition;
- this.cameraTarget = (args.cameraTarget instanceof Array)
- ? new Vector3().fromArray(args.cameraTarget) : args.cameraTarget;
-
- if(!this.cameraTarget && this.position){//add
- this.cameraTarget = this.position.clone();
- }
-
- this.radius = args.radius;
- this.view = args.view || null;
- this.keepOpen = false;
- this.descriptionVisible = false;
- this.showDescription = true;
- this.actions = args.actions || [];
- this.isHighlighted = false;
- this._visible = true;
- this.__visible = true;
- this._display = true;
- this._expand = false;
- this.collapseThreshold = [args.collapseThreshold, 100].find(e => e !== undefined);
- this.children = [];
- this.parent = null;
- this.boundingBox = new Box3();
- let iconClose = exports.resourcePath + '/icons/close.svg';
- this.domElement = $(`
- <div class="annotation" oncontextmenu="return false;">
- <div class="annotation-titlebar">
- <span class="annotation-label"></span>
- </div>
- <div class="annotation-description">
- <span class="annotation-description-close">
- <img src="${iconClose}" width="16px">
- </span>
- <span class="annotation-description-content">${this._description}</span>
- </div>
- </div>
- `);
- this.elTitlebar = this.domElement.find('.annotation-titlebar');
- this.elTitle = this.elTitlebar.find('.annotation-label');
- this.elTitle.append(this._title);
- this.elDescription = this.domElement.find('.annotation-description');
- this.elDescriptionClose = this.elDescription.find('.annotation-description-close');
- // this.elDescriptionContent = this.elDescription.find(".annotation-description-content");
- this.clickTitle = () => {
- //if(this.hasView()){
- this.moveHere(this.scene.getActiveCamera());
- //}
- this.dispatchEvent({type: 'click', target: this});
- viewer.renderer.domElement.focus();//add 使得方向键可用
- };
- this.elTitle.click(this.clickTitle);
- this.actions = this.actions.map(a => {
- if (a instanceof Action) {
- return a;
- } else {
- return new Action(a);
- }
- });
- for (let action of this.actions) {
- action.pairWith(this);
- }
- let actions = this.actions.filter(
- a => a.showIn === undefined || a.showIn.includes('scene'));
- for (let action of actions) {
- let elButton = $(`<img src="${action.icon}" class="annotation-action-icon">`);
- this.elTitlebar.append(elButton);
- elButton.click(() => action.onclick({annotation: this}));
- }
- this.elDescriptionClose.hover(
- e => this.elDescriptionClose.css('opacity', '1'),
- e => this.elDescriptionClose.css('opacity', '0.5')
- );
- this.elDescriptionClose.click(e => this.setHighlighted(false));
- // this.elDescriptionContent.html(this._description);
- this.domElement.mouseenter(e => this.setHighlighted(true));
- this.domElement.mouseleave(e => this.setHighlighted(false));
- this.domElement.on('touchstart', e => {
- this.setHighlighted(!this.isHighlighted);
- });
- this.display = false;
- //this.display = true;
- }
- installHandles(viewer){
- if(this.handles !== undefined){
- return;
- }
- let domElement = $(`
- <div style="position: absolute; left: 300; top: 200; pointer-events: none">
- <svg width="300" height="600">
- <line x1="0" y1="0" x2="1200" y2="200" style="stroke: black; stroke-width:2" />
- <circle cx="50" cy="50" r="4" stroke="black" stroke-width="2" fill="gray" />
- <circle cx="150" cy="50" r="4" stroke="black" stroke-width="2" fill="gray" />
- </svg>
- </div>
- `);
-
- let svg = domElement.find("svg")[0];
- let elLine = domElement.find("line")[0];
- let elStart = domElement.find("circle")[0];
- let elEnd = domElement.find("circle")[1];
- let setCoordinates = (start, end) => {
- elStart.setAttribute("cx", `${start.x}`);
- elStart.setAttribute("cy", `${start.y}`);
- elEnd.setAttribute("cx", `${end.x}`);
- elEnd.setAttribute("cy", `${end.y}`);
- elLine.setAttribute("x1", start.x);
- elLine.setAttribute("y1", start.y);
- elLine.setAttribute("x2", end.x);
- elLine.setAttribute("y2", end.y);
- let box = svg.getBBox();
- svg.setAttribute("width", `${box.width}`);
- svg.setAttribute("height", `${box.height}`);
- svg.setAttribute("viewBox", `${box.x} ${box.y} ${box.width} ${box.height}`);
- let ya = start.y - end.y;
- let xa = start.x - end.x;
- if(ya > 0){
- start.y = start.y - ya;
- }
- if(xa > 0){
- start.x = start.x - xa;
- }
- domElement.css("left", `${start.x}px`);
- domElement.css("top", `${start.y}px`);
- };
- $(viewer.renderArea).append(domElement);
- let annotationStartPos = this.position.clone();
- let annotationStartOffset = this.offset.clone();
- $(this.domElement).draggable({
- start: (event, ui) => {
- annotationStartPos = this.position.clone();
- annotationStartOffset = this.offset.clone();
- $(this.domElement).find(".annotation-titlebar").css("pointer-events", "none");
- console.log($(this.domElement).find(".annotation-titlebar"));
- },
- stop: () => {
- $(this.domElement).find(".annotation-titlebar").css("pointer-events", "");
- },
- drag: (event, ui ) => {
- let renderAreaWidth = viewer.renderer.getSize(new Vector2$1()).width;
- //let renderAreaHeight = viewer.renderer.getSize().height;
- let diff = {
- x: ui.originalPosition.left - ui.position.left,
- y: ui.originalPosition.top - ui.position.top
- };
- let nDiff = {
- x: -(diff.x / renderAreaWidth) * 2,
- y: (diff.y / renderAreaWidth) * 2
- };
- let camera = viewer.scene.getActiveCamera();
- let oldScreenPos = new Vector3()
- .addVectors(annotationStartPos, annotationStartOffset)
- .project(camera);
- let newScreenPos = oldScreenPos.clone();
- newScreenPos.x += nDiff.x;
- newScreenPos.y += nDiff.y;
- let newPos = newScreenPos.clone();
- newPos.unproject(camera);
- let newOffset = new Vector3().subVectors(newPos, this.position);
- this.offset.copy(newOffset);
- }
- });
- let updateCallback = () => {
- let position = this.position;
- let scene = viewer.scene;
- const renderAreaSize = viewer.renderer.getSize(new Vector2$1());
- let renderAreaWidth = renderAreaSize.width;
- let renderAreaHeight = renderAreaSize.height;
- let start = this.position.clone();
- let end = new Vector3().addVectors(this.position, this.offset);
- let toScreen = (position) => {
- let camera = scene.getActiveCamera();
- let screenPos = new Vector3();
- let worldView = new Matrix4().multiplyMatrices(camera.projectionMatrix, camera.matrixWorldInverse);
- let ndc = new Vector4(position.x, position.y, position.z, 1.0).applyMatrix4(worldView);
- // limit w to small positive value, in case position is behind the camera
- ndc.w = Math.max(ndc.w, 0.1);
- ndc.divideScalar(ndc.w);
- screenPos.copy(ndc);
- screenPos.x = renderAreaWidth * (screenPos.x + 1) / 2;
- screenPos.y = renderAreaHeight * (1 - (screenPos.y + 1) / 2);
- return screenPos;
- };
-
- start = toScreen(start);
- end = toScreen(end);
- setCoordinates(start, end);
- };
- viewer.addEventListener("update", updateCallback);
- this.handles = {
- domElement: domElement,
- setCoordinates: setCoordinates,
- updateCallback: updateCallback
- };
- }
- removeHandles(viewer){
- if(this.handles === undefined){
- return;
- }
- //$(viewer.renderArea).remove(this.handles.domElement);
- this.handles.domElement.remove();
- viewer.removeEventListener("update", this.handles.updateCallback);
- delete this.handles;
- }
- get visible () {
- return this._visible;
- }
- set visible (value) {
- if (this._visible === value) {
- return;
- }
- this._visible = value;
- //this.traverse(node => {
- // node.display = value;
- //});
- this.dispatchEvent({
- type: 'visibility_changed',
- annotation: this
- });
- }
- get display () {
- return this._display;
- }
- set display (display) {
- if (this._display === display) {
- return;
- }
- this._display = display;
- if (display) {
- // this.domElement.fadeIn(200);
- this.domElement.show();
- } else {
- // this.domElement.fadeOut(200);
- this.domElement.hide();
- }
- }
- get expand () {
- return this._expand;
- }
- set expand (expand) {
- if (this._expand === expand) {
- return;
- }
- if (expand) {
- this.display = false;
- } else {
- this.display = true;
- this.traverseDescendants(node => {
- node.display = false;
- });
- }
- this._expand = expand;
- }
- get title () {
- return this._title;
- }
- set title (title) {
- if (this._title === title) {
- return;
- }
- this._title = title;
- this.elTitle.empty();
- this.elTitle.append(this._title);
- this.dispatchEvent({
- type: "annotation_changed",
- annotation: this,
- });
- }
- get description () {
- return this._description;
- }
- set description (description) {
- if (this._description === description) {
- return;
- }
- this._description = description;
- const elDescriptionContent = this.elDescription.find(".annotation-description-content");
- elDescriptionContent.empty();
- elDescriptionContent.append(this._description);
- this.dispatchEvent({
- type: "annotation_changed",
- annotation: this,
- });
- }
- add (annotation) {
- if (!this.children.includes(annotation)) {
- this.children.push(annotation);
- annotation.parent = this;
- let descendants = [];
- annotation.traverse(a => { descendants.push(a); });
- for (let descendant of descendants) {
- let c = this;
- while (c !== null) {
- c.dispatchEvent({
- 'type': 'annotation_added',
- 'annotation': descendant
- });
- c = c.parent;
- }
- }
- }
- }
- level () {
- if (this.parent === null) {
- return 0;
- } else {
- return this.parent.level() + 1;
- }
- }
- hasChild(annotation) {
- return this.children.includes(annotation);
- }
- remove (annotation) {
- if (this.hasChild(annotation)) {
- annotation.removeAllChildren();
- annotation.dispose();
- this.children = this.children.filter(e => e !== annotation);
- annotation.parent = null;
- }
- }
- removeAllChildren() {
- this.children.forEach((child) => {
- if (child.children.length > 0) {
- child.removeAllChildren();
- }
- this.remove(child);
- });
- }
- updateBounds () {
- let box = new Box3();
- if (this.position) {
- box.expandByPoint(this.position);
- }
- for (let child of this.children) {
- child.updateBounds();
- box.union(child.boundingBox);
- }
- this.boundingBox.copy(box);
- }
- traverse (handler) {
- let expand = handler(this);
- if (expand === undefined || expand === true) {
- for (let child of this.children) {
- child.traverse(handler);
- }
- }
- }
- traverseDescendants (handler) {
- for (let child of this.children) {
- child.traverse(handler);
- }
- }
- flatten () {
- let annotations = [];
- this.traverse(annotation => {
- annotations.push(annotation);
- });
- return annotations;
- }
- descendants () {
- let annotations = [];
- this.traverse(annotation => {
- if (annotation !== this) {
- annotations.push(annotation);
- }
- });
- return annotations;
- }
- setHighlighted (highlighted) {
- if (highlighted) {
- this.domElement.css('opacity', '0.8');
- this.elTitlebar.css('box-shadow', '0 0 5px #fff');
- this.domElement.css('z-index', '1000');
- if (this._description) {
- this.descriptionVisible = true;
- this.elDescription.fadeIn(200);
- this.elDescription.css('position', 'relative');
- }
- } else {
- this.domElement.css('opacity', '0.5');
- this.elTitlebar.css('box-shadow', '');
- this.domElement.css('z-index', '100');
- this.descriptionVisible = false;
- this.elDescription.css('display', 'none');
- }
- this.isHighlighted = highlighted;
- }
- hasView () {
- let hasPosTargetView = this.cameraTarget.x != null;
- hasPosTargetView = hasPosTargetView && this.cameraPosition.x != null;
- let hasRadiusView = this.radius !== undefined;
- let hasView = hasPosTargetView || hasRadiusView;
- return hasView;
- };
- moveHere (camera) {
- if (!this.hasView()) {
- return;
- }
- let view = this.scene.view;
- let animationDuration = 500;
- let easing = TWEEN.Easing.Quartic.Out;
- let endTarget;
- if (this.cameraTarget) {
- endTarget = this.cameraTarget;
- } else if (this.position) {
- endTarget = this.position;
- } else {
- endTarget = this.boundingBox.getCenter(new Vector3());
- }
- if (this.cameraPosition) {
- let endPosition = this.cameraPosition;
- Utils.moveTo(this.scene, endPosition, endTarget);
- } else if (this.radius) {
- let direction = view.direction;
- let endPosition = endTarget.clone().add(direction.multiplyScalar(-this.radius));
- let startRadius = view.radius;
- let endRadius = this.radius;
- { // animate camera position
- let tween = new TWEEN.Tween(view.position).to(endPosition, animationDuration);
- tween.easing(easing);
- tween.start();
- }
- { // animate radius
- let t = {x: 0};
- let tween = new TWEEN.Tween(t)
- .to({x: 1}, animationDuration)
- .onUpdate(function () {
- view.radius = this.x * endRadius + (1 - this.x) * startRadius;
- });
- tween.easing(easing);
- tween.start();
- }
- }
- };
- dispose () {
- if (this.domElement.parentElement) {
- this.domElement.parentElement.removeChild(this.domElement);
- }
- };
- toString () {
- return 'Annotation: ' + this._title;
- }
- };
- class EnumItem{
- constructor(object){
- for(let key of Object.keys(object)){
- this[key] = object[key];
- }
- }
- inspect(){
- return `Enum(${this.name}: ${this.value})`;
- }
- };
- class Enum{//??????做什么用的
- constructor(object){
- this.object = object;
- for(let key of Object.keys(object)){
- let value = object[key];
- if(typeof value === "object"){
- value.name = key;
- }else {
- value = {name: key, value: value};
- }
-
- this[key] = new EnumItem(value);
- }
- }
- fromValue(value){
- for(let key of Object.keys(this.object)){
- if(this[key].value === value){
- return this[key];
- }
- }
- throw new Error(`No enum for value: ${value}`);
- }
-
- };
- const CameraMode = {
- ORTHOGRAPHIC: 0,
- PERSPECTIVE: 1,
- VR: 2,
- };
- const ClipTask = {
- NONE: 0,
- HIGHLIGHT: 1,
- SHOW_INSIDE: 2,
- SHOW_OUTSIDE: 3
- };
- const ClipMethod = {
- INSIDE_ANY: 0,
- INSIDE_ALL: 1
- };
- const ElevationGradientRepeat = {
- CLAMP: 0,
- REPEAT: 1,
- MIRRORED_REPEAT: 2,
- };
- const Buttons = {// MouseEvent.buttons
- //buttons,设置按下了鼠标哪些键,是一个3个比特位的二进制值,默认为0。1表示按下主键(通常是左键),2表示按下次要键(通常是右键),4表示按下辅助键(通常是中间的键)。
- NONE:0,//add
-
- LEFT: 0b0001,
- RIGHT: 0b0010,
- MIDDLE: 0b0100
- };
- /* 如果访问的是button, 用THREE.MOUSE来判断:
- button,设置按下了哪一个鼠标按键,默认为0。-1表示没有按键,0表示按下主键(通常是左键),1表示按下辅助键(通常是中间的键),2表示按下次要键(通常是右键)
- */
- const PointSizeType = {
- FIXED: 0,
- ATTENUATED: 1,
- ADAPTIVE: 2
- };
- const PointShape = {
- SQUARE: 0,
- CIRCLE: 1,
- PARABOLOID: 2
- };
- const TreeType = {
- OCTREE: 0,
- KDTREE: 1
- };
- const LengthUnits = {
- METER: {code: 'm', unitspermeter: 1.0},
- FEET: {code: 'ft', unitspermeter: 3.28084},
- INCH: {code: '\u2033', unitspermeter: 39.3701}
- };
- /////////// add //////////////////////////////////
-
- var GLCubeFaces$1 = {
- GL_TEXTURE_CUBE_MAP_POSITIVE_X: 0,
- GL_TEXTURE_CUBE_MAP_NEGATIVE_X: 1,
- GL_TEXTURE_CUBE_MAP_POSITIVE_Y: 2,
- GL_TEXTURE_CUBE_MAP_NEGATIVE_Y: 3,
- GL_TEXTURE_CUBE_MAP_POSITIVE_Z: 4,
- GL_TEXTURE_CUBE_MAP_NEGATIVE_Z: 5
- };
- var PanoSizeClass = {
- BASE: 1,
- STANDARD: 2,
- HIGH: 3,
- ULTRAHIGH: 4
- };
- var PanoRendererEvents = {
- PanoRenderComplete: "panorama.render.complete",
- TileRenderFailure: "panorama.tile.render.failed",
- TileRenderSuccess: "panorama.tile.render.success",
- TileUploadAttempted: "panorama.tile.upload.attempted",
- UploadAttemptedForAllTiles: "panorama.upload.attempted.all.tiles",
- ZoomLevelRenderStarted: "panorama.zoom.render.started"
- };
- var SceneRendererEvents = {
- ContextCreated: "scene-renderer-context-created",
- AfterRender: "after-render",
- MemoryUsageUpdated: "scene-renderer-memory-usage-updated"
- };
- var TileDownloaderEvents = {
- TileDownloadSuccess: "tiledownloader.download.success",
- TileDownloadFailure: "tiledownloader.download.failure",
- PanoDownloadComplete: "tiledownloader.pano.download.complete"
- };
- var Vectors = {
- UP: new Vector3(0,1,0),
- DOWN: new Vector3(0,-1,0),
- LEFT: new Vector3(-1,0,0),
- RIGHT: new Vector3(1,0,0),
- FORWARD: new Vector3(0,0,-1),
- BACK: new Vector3(0,0,1)
- };
- var Vectors2 = {};
- for(var i in Vectors){
- Vectors2[i] = math.convertVector.YupToZup(Vectors[i]);
- }
- var DownloadStatus = Object.freeze({
- None: 0,
- Queued: 1,
- ForceQueued: 2,
- Downloading: 3,
- Downloaded: 4,
- DownloadFailed: 5
- });
- var ModelManagerEvents = {
- ModelAdded: "model-added",
- ActiveModelChanged: "active-model-changed"
- };
- var PanoramaEvents = {
- Enter: 'panorama.enter',
- Exit: 'panorama.exit',
- LoadComplete: "panorama.load.complete",
- LoadFailed: "panorama.load.failed",
- TileLoaded: "panorama.tile.loaded",
- VideoRendered: "panorama.video.rendered"
- };
- /**
- * @author mrdoob / http://mrdoob.com/ https://github.com/mrdoob/eventdispatcher.js
- *
- * with slight modifications by mschuetz, http://potree.org
- *
- */
- // The MIT License
- //
- // Copyright (c) 2011 Mr.doob
- //
- // Permission is hereby granted, free of charge, to any person obtaining a copy
- // of this software and associated documentation files (the "Software"), to deal
- // in the Software without restriction, including without limitation the rights
- // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
- // copies of the Software, and to permit persons to whom the Software is
- // furnished to do so, subject to the following conditions:
- //
- // The above copyright notice and this permission notice shall be included in
- // all copies or substantial portions of the Software.
- //
- // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
- // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
- // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
- // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
- // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
- // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
- // THE SOFTWARE.
-
-
- class EventDispatcher$1{
- constructor(){
- this._listeners = {};
- }
- addEventListener(type, listener, importance=0 ){//add importance
- const listeners = this._listeners;
- if(listeners[type] === undefined){
- listeners[type] = [];
- }
- if(listeners[type].indexOf(listener) === - 1){
- listeners[type].push({ listener, importance});
- listeners[type] = listeners[type].sort((e,a)=> a.importance - e.importance);//add
- }
- }
- hasEventListener(type, listener){
- const listeners = this._listeners;
- return listeners[type] !== undefined && listeners[type].indexOf(listener) !== - 1;
- }
- removeEventListener(type, listener){
- let listeners = this._listeners;
- let listenerArray = listeners[type];
- if (listenerArray !== undefined){
- /* let index = listenerArray.indexOf(listener);
- if(index !== - 1){
- listenerArray.splice(index, 1);
- } */
- let item = listenerArray.find(e=>e.listener == listener);
- item && listenerArray.splice(listenerArray.indexOf(item), 1);
-
- }
- }
- removeEventListeners(type){
- if(this._listeners[type] !== undefined){
- delete this._listeners[type];
- }
- };
-
-
-
- dispatchEvent(event){
- if(typeof event == 'string'){//add
- event = {type:event};
- }
-
- let listeners = this._listeners;
- let listenerArray = listeners[event.type];
- if ( listenerArray !== undefined ) {
- event.target = this;
- for(let {listener} of listenerArray.slice(0)){
- let result = listener.call(this, event); //add stopContinue
- if(result && result.stopContinue){
- break
- }
- }
- }
- }
-
-
- //add
- /* emit(type){
- this.dispatchEvent({type, arguments: Array.from(arguments).slice(1, arguments.length) })
- }
- on(type, fun){
- this.addEventListener(type,(ev)=>{
- fun.apply(this, ev.arguments)
- })
- }
- off(type, fun){
- this.removeEventListener(type,)
- }
-
- once(type, fun) {
- function callback() {
- this.removeEventListener(type, callback),
- n || (n = !0, fun.apply(this, arguments))
- }
- var n = !1;
- return callback.listener = fun,
- this.on(type, callback),
- this
- } */
-
- removeAllListeners(){
-
- this._listeners = {};
-
- }
-
- }
- const KeyCodes = {
- LEFT: 37,
- UP: 38,
- RIGHT: 39,
- BOTTOM: 40,
- DELETE: 46,
- BACKSPACE:8,
-
- A: 'A'.charCodeAt(0),
- S: 'S'.charCodeAt(0),
- D: 'D'.charCodeAt(0),
- W: 'W'.charCodeAt(0),
- Q: 'Q'.charCodeAt(0),
- E: 'E'.charCodeAt(0),
- R: 'R'.charCodeAt(0),
- F: 'F'.charCodeAt(0)
-
- };
- class LRUItem{
- constructor(node){
- this.previous = null;
- this.next = null;
- this.node = node;
- }
- }
- /**
- *
- * @class A doubly-linked-list of the least recently used elements.
- */
- class LRU{
- constructor(){
- // the least recently used item
- this.first = null;
- // the most recently used item
- this.last = null;
- // a list of all items in the lru list
- this.items = {};
- this.elements = 0;
- this.numPoints = 0;
- }
- size(){
- return this.elements;
- }
- contains(node){
- return this.items[node.id] == null;
- }
- touch(node){
- if (!node.loaded) {
- return;
- }
- let item;
- if (this.items[node.id] == null) {
- // add to list
- item = new LRUItem(node);
- item.previous = this.last;
- this.last = item;
- if (item.previous !== null) {
- item.previous.next = item;
- }
- this.items[node.id] = item;
- this.elements++;
- if (this.first === null) {
- this.first = item;
- }
- this.numPoints += node.numPoints;
- } else {
- // update in list
- item = this.items[node.id];
- if (item.previous === null) {
- // handle touch on first element
- if (item.next !== null) {
- this.first = item.next;
- this.first.previous = null;
- item.previous = this.last;
- item.next = null;
- this.last = item;
- item.previous.next = item;
- }
- } else if (item.next === null) {
- // handle touch on last element
- } else {
- // handle touch on any other element
- item.previous.next = item.next;
- item.next.previous = item.previous;
- item.previous = this.last;
- item.next = null;
- this.last = item;
- item.previous.next = item;
- }
- }
- }
- remove(node){
- let lruItem = this.items[node.id];
- if (lruItem) {
- if (this.elements === 1) {
- this.first = null;
- this.last = null;
- } else {
- if (!lruItem.previous) {
- this.first = lruItem.next;
- this.first.previous = null;
- }
- if (!lruItem.next) {
- this.last = lruItem.previous;
- this.last.next = null;
- }
- if (lruItem.previous && lruItem.next) {
- lruItem.previous.next = lruItem.next;
- lruItem.next.previous = lruItem.previous;
- }
- }
- delete this.items[node.id];
- this.elements--;
- this.numPoints -= node.numPoints;
- }
- }
- getLRUItem(){
- if (this.first === null) {
- return null;
- }
- let lru = this.first;
- return lru.node;
- }
- toString(){
- let string = '{ ';
- let curr = this.first;
- while (curr !== null) {
- string += curr.node.id;
- if (curr.next !== null) {
- string += ', ';
- }
- curr = curr.next;
- }
- string += '}';
- string += '(' + this.size() + ')';
- return string;
- }
- freeMemory(){
- if (this.elements <= 1) {
- return;
- }
- /* while (this.numPoints > Potree.pointLoadLimit) {
- let element = this.first;
- let node = element.node;
- this.disposeDescendants(node);
- } */
-
- //改成navvis的,使用pointBudget,否则四屏点云闪烁。
-
-
- let max = /* this.pageVisible ? */viewer.viewports.length * 2 * Potree.pointBudget;// : 1000
-
-
-
- for (; this.numPoints > max; ) {//要根据屏幕数量来增加pointBudget
- var node = this.getLRUItem();
- node && this.disposeSubtree(node);
- }
-
-
- }
- disposeSubtree(t) {//add from navvis 25.js
- var e = [t];
- t.traverse((function(t) {
- t.loaded && e.push(t);
- }
- ));
- for (var n = 0, i = e; n < i.length; n++) {
- var o = i[n];
- o.dispose(),
- this.remove(o);
- }
- }
- disposeDescendants(node){
- let stack = [];
- stack.push(node);
- while (stack.length > 0) {
- let current = stack.pop();
- // console.log(current);
- current.dispose();
- this.remove(current);
- for (let key in current.children) {
- if (current.children.hasOwnProperty(key)) {
- let child = current.children[key];
- if (child.loaded) {
- stack.push(current.children[key]);
- }
- }
- }
- }
- }
- }
- class PointCloudTreeNode extends EventDispatcher{
- constructor(){
- super();
- this.needsTransformUpdate = true;
- }
- getChildren () {
- throw new Error('override function');
- }
- getBoundingBox () {
- throw new Error('override function');
- }
- isLoaded () {
- throw new Error('override function');
- }
- isGeometryNode () {
- throw new Error('override function');
- }
- isTreeNode () {
- throw new Error('override function');
- }
- getLevel () {
- throw new Error('override function');
- }
- getBoundingSphere () {
- throw new Error('override function');
- }
- };
- class PointCloudTree extends Object3D {
- constructor () {
- super();
- //this.spriteGroup = new THREE.Object3D //add
- }
- initialized () {
- return this.root !== null;
- }
- };
- /**
- * Some types of possible point attribute data formats
- *
- * @class
- */
- const PointAttributeTypes = {
- DATA_TYPE_DOUBLE: {ordinal: 0, name: "double", size: 8},
- DATA_TYPE_FLOAT: {ordinal: 1, name: "float", size: 4},
- DATA_TYPE_INT8: {ordinal: 2, name: "int8", size: 1},
- DATA_TYPE_UINT8: {ordinal: 3, name: "uint8", size: 1},
- DATA_TYPE_INT16: {ordinal: 4, name: "int16", size: 2},
- DATA_TYPE_UINT16: {ordinal: 5, name: "uint16", size: 2},
- DATA_TYPE_INT32: {ordinal: 6, name: "int32", size: 4},
- DATA_TYPE_UINT32: {ordinal: 7, name: "uint32", size: 4},
- DATA_TYPE_INT64: {ordinal: 8, name: "int64", size: 8},
- DATA_TYPE_UINT64: {ordinal: 9, name: "uint64", size: 8}
- };
- let i$1 = 0;
- for (let obj in PointAttributeTypes) {
- PointAttributeTypes[i$1] = PointAttributeTypes[obj];
- i$1++;
- }
- class PointAttribute{
-
- constructor(name, type, numElements){
- this.name = name;
- this.type = type;
- this.numElements = numElements;
- this.byteSize = this.numElements * this.type.size;
- this.description = "";
- this.range = [Infinity, -Infinity];
- }
- };
- //add
- const replacements = {
- "COLOR_PACKED": "rgba",
- "RGBA": "rgba",
- "INTENSITY": "intensity",
- "CLASSIFICATION": "classification",
- "GPS_TIME": "gps-time",
- };
- const replaceOldNames = (old) => {
- if(replacements[old]){
- return replacements[old];
- }else {
- return old;
- }
- };
- PointAttribute.POSITION_CARTESIAN = new PointAttribute(
- "POSITION_CARTESIAN", PointAttributeTypes.DATA_TYPE_FLOAT, 3);
- PointAttribute.RGBA_PACKED = new PointAttribute(
- replaceOldNames("COLOR_PACKED"), PointAttributeTypes.DATA_TYPE_INT8, 4);
- PointAttribute.COLOR_PACKED = PointAttribute.RGBA_PACKED;
- PointAttribute.RGB_PACKED = new PointAttribute(
- "COLOR_PACKED", PointAttributeTypes.DATA_TYPE_INT8, 3);
- PointAttribute.NORMAL_FLOATS = new PointAttribute(
- "NORMAL_FLOATS", PointAttributeTypes.DATA_TYPE_FLOAT, 3);
- PointAttribute.INTENSITY = new PointAttribute(
- replaceOldNames("INTENSITY"), PointAttributeTypes.DATA_TYPE_UINT16, 1);
- PointAttribute.CLASSIFICATION = new PointAttribute(
- replaceOldNames("CLASSIFICATION"), PointAttributeTypes.DATA_TYPE_UINT8, 1);
- PointAttribute.NORMAL_SPHEREMAPPED = new PointAttribute(
- "NORMAL_SPHEREMAPPED", PointAttributeTypes.DATA_TYPE_UINT8, 2);
- PointAttribute.NORMAL_OCT16 = new PointAttribute(
- "NORMAL_OCT16", PointAttributeTypes.DATA_TYPE_UINT8, 2);
- PointAttribute.NORMAL = new PointAttribute(
- "NORMAL", PointAttributeTypes.DATA_TYPE_FLOAT, 3);
-
- PointAttribute.RETURN_NUMBER = new PointAttribute(
- "RETURN_NUMBER", PointAttributeTypes.DATA_TYPE_UINT8, 1);
-
- PointAttribute.NUMBER_OF_RETURNS = new PointAttribute(
- "NUMBER_OF_RETURNS", PointAttributeTypes.DATA_TYPE_UINT8, 1);
-
- PointAttribute.SOURCE_ID = new PointAttribute(
- "SOURCE_ID", PointAttributeTypes.DATA_TYPE_UINT16, 1);
- PointAttribute.INDICES = new PointAttribute(
- "INDICES", PointAttributeTypes.DATA_TYPE_UINT32, 1);
- PointAttribute.SPACING = new PointAttribute(
- "SPACING", PointAttributeTypes.DATA_TYPE_FLOAT, 1);
- PointAttribute.GPS_TIME = new PointAttribute(
- replaceOldNames("GPS_TIME"), PointAttributeTypes.DATA_TYPE_DOUBLE, 1);
- class PointAttributes{
- constructor(pointAttributes){
- this.attributes = [];
- this.byteSize = 0;
- this.size = 0;
- this.vectors = [];
- if (pointAttributes != null) {
- for (let i = 0; i < pointAttributes.length; i++) {
- let pointAttributeName = pointAttributes[i];
- let pointAttribute = PointAttribute[pointAttributeName];
- this.attributes.push(pointAttribute);
- this.byteSize += pointAttribute.byteSize;
- this.size++;
- }
- }
- }
- add(pointAttribute){
- this.attributes.push(pointAttribute);
- this.byteSize += pointAttribute.byteSize;
- this.size++;
- };
- addVector(vector){
- this.vectors.push(vector);
- }
- hasNormals(){
- for (let name in this.attributes) {
- let pointAttribute = this.attributes[name];
- if (
- pointAttribute === PointAttribute.NORMAL_SPHEREMAPPED ||
- pointAttribute === PointAttribute.NORMAL_FLOATS ||
- pointAttribute === PointAttribute.NORMAL ||
- pointAttribute === PointAttribute.NORMAL_OCT16) {
- return true;
- }
- }
- return false;
- };
- }
- class U {
- static toVector3(v, offset) {
- return new Vector3().fromArray(v, offset || 0);
- }
- static toBox3(b) {
- return new Box3(U.toVector3(b), U.toVector3(b, 3));
- };
- static findDim(schema, name) {
- var dim = schema.find((dim) => dim.name == name);
- if (!dim) throw new Error('Failed to find ' + name + ' in schema');
- return dim;
- }
- static sphereFrom(b) {
- return b.getBoundingSphere(new Sphere());
- }
- };
- class PointCloudEptGeometry {
- constructor(url, info) {
- let version = info.version;
- let schema = info.schema;
- let bounds = info.bounds;
- let boundsConforming = info.boundsConforming;
- let xyz = [
- U.findDim(schema, 'X'),
- U.findDim(schema, 'Y'),
- U.findDim(schema, 'Z')
- ];
- let scale = xyz.map((d) => d.scale || 1);
- let offset = xyz.map((d) => d.offset || 0);
- this.eptScale = U.toVector3(scale);
- this.eptOffset = U.toVector3(offset);
- this.url = url;
- this.info = info;
- this.type = 'ept';
- this.schema = schema;
- this.span = info.span || info.ticks;
- this.boundingBox = U.toBox3(bounds);
- this.tightBoundingBox = U.toBox3(boundsConforming);
- this.offset = U.toVector3([0, 0, 0]);
- this.boundingSphere = U.sphereFrom(this.boundingBox);
- this.tightBoundingSphere = U.sphereFrom(this.tightBoundingBox);
- this.version = new Potree.Version('1.7');
- this.projection = null;
- this.fallbackProjection = null;
- if (info.srs && info.srs.horizontal) {
- this.projection = info.srs.authority + ':' + info.srs.horizontal;
- }
- if (info.srs.wkt) {
- if (!this.projection) this.projection = info.srs.wkt;
- else this.fallbackProjection = info.srs.wkt;
- }
- {
- // TODO [mschuetz]: named projections that proj4 can't handle seem to cause problems.
- // remove them for now
- try{
- proj4(this.projection);
- }catch(e){
- this.projection = null;
- }
-
- }
-
- {
- const attributes = new PointAttributes();
- attributes.add(PointAttribute.POSITION_CARTESIAN);
- attributes.add(new PointAttribute("rgba", PointAttributeTypes.DATA_TYPE_UINT8, 4));
- attributes.add(new PointAttribute("intensity", PointAttributeTypes.DATA_TYPE_UINT16, 1));
- attributes.add(new PointAttribute("classification", PointAttributeTypes.DATA_TYPE_UINT8, 1));
- attributes.add(new PointAttribute("gps-time", PointAttributeTypes.DATA_TYPE_DOUBLE, 1));
- attributes.add(new PointAttribute("returnNumber", PointAttributeTypes.DATA_TYPE_UINT8, 1));
- attributes.add(new PointAttribute("number of returns", PointAttributeTypes.DATA_TYPE_UINT8, 1));
- attributes.add(new PointAttribute("return number", PointAttributeTypes.DATA_TYPE_UINT8, 1));
- attributes.add(new PointAttribute("source id", PointAttributeTypes.DATA_TYPE_UINT16, 1));
- this.pointAttributes = attributes;
- }
- this.spacing =
- (this.boundingBox.max.x - this.boundingBox.min.x) / this.span;
- let hierarchyType = info.hierarchyType || 'json';
- const dataType = info.dataType;
- if (dataType == 'laszip') {
- this.loader = new Potree.EptLaszipLoader();
- }
- else if (dataType == 'binary') {
- this.loader = new Potree.EptBinaryLoader();
- }
- else if (dataType == 'zstandard') {
- this.loader = new Potree.EptZstandardLoader();
- }
- else {
- throw new Error('Could not read data type: ' + dataType);
- }
- }
- };
- class EptKey {
- constructor(ept, b, d, x, y, z) {
- this.ept = ept;
- this.b = b;
- this.d = d;
- this.x = x || 0;
- this.y = y || 0;
- this.z = z || 0;
- }
- name() {
- return this.d + '-' + this.x + '-' + this.y + '-' + this.z;
- }
- step(a, b, c) {
- let min = this.b.min.clone();
- let max = this.b.max.clone();
- let dst = new Vector3().subVectors(max, min);
- if (a) min.x += dst.x / 2;
- else max.x -= dst.x / 2;
- if (b) min.y += dst.y / 2;
- else max.y -= dst.y / 2;
- if (c) min.z += dst.z / 2;
- else max.z -= dst.z / 2;
- return new Potree.EptKey(
- this.ept,
- new Box3(min, max),
- this.d + 1,
- this.x * 2 + a,
- this.y * 2 + b,
- this.z * 2 + c);
- }
- children() {
- var result = [];
- for (var a = 0; a < 2; ++a) {
- for (var b = 0; b < 2; ++b) {
- for (var c = 0; c < 2; ++c) {
- var add = this.step(a, b, c).name();
- if (!result.includes(add)) result = result.concat(add);
- }
- }
- }
- return result;
- }
- }
- class PointCloudEptGeometryNode extends PointCloudTreeNode {
- constructor(ept, b, d, x, y, z) {
- super();
- this.ept = ept;
- this.key = new Potree.EptKey(
- this.ept,
- b || this.ept.boundingBox,
- d || 0,
- x,
- y,
- z);
- this.id = PointCloudEptGeometryNode.IDCount++;
- this.geometry = null;
- this.boundingBox = this.key.b;
- this.tightBoundingBox = this.boundingBox;
- this.spacing = this.ept.spacing / Math.pow(2, this.key.d);
- this.boundingSphere = U.sphereFrom(this.boundingBox);
- // These are set during hierarchy loading.
- this.hasChildren = false;
- this.children = { };
- this.numPoints = -1;
- this.level = this.key.d;
- this.loaded = false;
- this.loading = false;
- this.oneTimeDisposeHandlers = [];
- let k = this.key;
- this.name = this.toPotreeName(k.d, k.x, k.y, k.z);
- this.index = parseInt(this.name.charAt(this.name.length - 1));
- }
- isGeometryNode() { return true; }
- getLevel() { return this.level; }
- isTreeNode() { return false; }
- isLoaded() { return this.loaded; }
- getBoundingSphere() { return this.boundingSphere; }
- getBoundingBox() { return this.boundingBox; }
- url() { return this.ept.url + 'ept-data/' + this.filename(); }
- getNumPoints() { return this.numPoints; }
- filename() { return this.key.name(); }
- getChildren() {
- let children = [];
- for (let i = 0; i < 8; i++) {
- if (this.children[i]) {
- children.push(this.children[i]);
- }
- }
- return children;
- }
- addChild(child) {
- this.children[child.index] = child;
- child.parent = this;
- }
- load() {
- if (this.loaded || this.loading) return;
- if (Potree.numNodesLoading >= Potree.maxNodesLoading) return;
- this.loading = true;
- ++Potree.numNodesLoading;
- if (this.numPoints == -1) this.loadHierarchy();
- this.loadPoints();
- }
- loadPoints(){
- this.ept.loader.load(this);
- }
- async loadHierarchy() {
- let nodes = { };
- nodes[this.filename()] = this;
- this.hasChildren = false;
- let eptHierarchyFile =
- `${this.ept.url}ept-hierarchy/${this.filename()}.json`;
- let response = await fetch(eptHierarchyFile);
- let hier = await response.json();
- // Since we want to traverse top-down, and 10 comes
- // lexicographically before 9 (for example), do a deep sort.
- var keys = Object.keys(hier).sort((a, b) => {
- let [da, xa, ya, za] = a.split('-').map((n) => parseInt(n, 10));
- let [db, xb, yb, zb] = b.split('-').map((n) => parseInt(n, 10));
- if (da < db) return -1; if (da > db) return 1;
- if (xa < xb) return -1; if (xa > xb) return 1;
- if (ya < yb) return -1; if (ya > yb) return 1;
- if (za < zb) return -1; if (za > zb) return 1;
- return 0;
- });
- keys.forEach((v) => {
- let [d, x, y, z] = v.split('-').map((n) => parseInt(n, 10));
- let a = x & 1, b = y & 1, c = z & 1;
- let parentName =
- (d - 1) + '-' + (x >> 1) + '-' + (y >> 1) + '-' + (z >> 1);
- let parentNode = nodes[parentName];
- if (!parentNode) return;
- parentNode.hasChildren = true;
- let key = parentNode.key.step(a, b, c);
- let node = new Potree.PointCloudEptGeometryNode(
- this.ept,
- key.b,
- key.d,
- key.x,
- key.y,
- key.z);
- node.level = d;
- node.numPoints = hier[v];
- parentNode.addChild(node);
- nodes[key.name()] = node;
- });
- }
- doneLoading(bufferGeometry, tightBoundingBox, np, mean) {
- bufferGeometry.boundingBox = this.boundingBox;
- this.geometry = bufferGeometry;
- this.tightBoundingBox = tightBoundingBox;
- this.numPoints = np;
- this.mean = mean;
- this.loaded = true;
- this.loading = false;
- --Potree.numNodesLoading;
- }
- toPotreeName(d, x, y, z) {
- var name = 'r';
- for (var i = 0; i < d; ++i) {
- var shift = d - i - 1;
- var mask = 1 << shift;
- var step = 0;
- if (x & mask) step += 4;
- if (y & mask) step += 2;
- if (z & mask) step += 1;
- name += step;
- }
- return name;
- }
- dispose() {
- if (this.geometry && this.parent != null) {
- this.geometry.dispose();
- this.geometry = null;
- this.loaded = false;
- // this.dispatchEvent( { type: 'dispose' } );
- for (let i = 0; i < this.oneTimeDisposeHandlers.length; i++) {
- let handler = this.oneTimeDisposeHandlers[i];
- handler();
- }
- this.oneTimeDisposeHandlers = [];
- }
- }
- }
- PointCloudEptGeometryNode.IDCount = 0;
- class PointCloudOctreeGeometry extends EventDispatcher{
- constructor(){
- super();
- this.url = null;
- this.octreeDir = null;
- this.spacing = 0;
- this.boundingBox = null;
- this.root = null;
- this.nodes = null;
- this.pointAttributes = null;
- this.hierarchyStepSize = -1;
- this.loader = null;
- }
-
- }
- class PointCloudOctreeGeometryNode extends PointCloudTreeNode{
- constructor(name, pcoGeometry, boundingBox){
- super();
- this.id = PointCloudOctreeGeometryNode.IDCount++;
- this.name = name;
- this.index = parseInt(name.charAt(name.length - 1));
- this.pcoGeometry = pcoGeometry;
- this.geometry = null;
- this.boundingBox = boundingBox;
- this.boundingSphere = boundingBox.getBoundingSphere(new Sphere());
- this.children = {};
- this.numPoints = 0;
- this.level = null;
- this.loaded = false;
- this.oneTimeDisposeHandlers = [];
- }
- isGeometryNode(){
- return true;
- }
- getLevel(){
- return this.level;
- }
- isTreeNode(){
- return false;
- }
- isLoaded(){
- return this.loaded;
- }
- getBoundingSphere(){
- return this.boundingSphere;
- }
- getBoundingBox(){
- return this.boundingBox;
- }
- getChildren(){
- let children = [];
- for (let i = 0; i < 8; i++) {
- if (this.children[i]) {
- children.push(this.children[i]);
- }
- }
- return children;
- }
- getBoundingBox(){
- return this.boundingBox;
- }
- getURL(){
- let url = '';
- let version = this.pcoGeometry.loader.version;
- if (version.equalOrHigher('1.5')) {
- url = this.pcoGeometry.octreeDir + '/' + this.getHierarchyPath() + '/' + this.name;
- } else if (version.equalOrHigher('1.4')) {
- url = this.pcoGeometry.octreeDir + '/' + this.name;
- } else if (version.upTo('1.3')) {
- url = this.pcoGeometry.octreeDir + '/' + this.name;
- }
-
- return url;
- }
- getHierarchyPath(){
- let path = 'r/';
- let hierarchyStepSize = this.pcoGeometry.hierarchyStepSize;
- let indices = this.name.substr(1);
- let numParts = Math.floor(indices.length / hierarchyStepSize);
- for (let i = 0; i < numParts; i++) {
- path += indices.substr(i * hierarchyStepSize, hierarchyStepSize) + '/';
- }
- path = path.slice(0, -1);
- return path;
- }
- addChild(child) {
- this.children[child.index] = child;
- child.parent = this;
- }
- load(){
-
- if (this.loading === true || this.loaded === true || Potree.numNodesLoading >= Potree.maxNodesLoading) {
- return;
- }
- this.loading = true;
- Potree.numNodesLoading++;
- if (this.pcoGeometry.loader.version.equalOrHigher('1.5')) {
- if ((this.level % this.pcoGeometry.hierarchyStepSize) === 0 && this.hasChildren) {
- this.loadHierachyThenPoints();
- } else {
- this.loadPoints();
- }
- } else {
- this.loadPoints();
- }
- }
- loadPoints(){
- this.pcoGeometry.loader.load(this);
- }
- loadHierachyThenPoints(pointcloud){
- let node = this;
- // load hierarchy
- let callback = function (node, hbuffer) {
- let tStart = performance.now();
- let view = new DataView(hbuffer);
- let stack = [];
- let children = view.getUint8(0);
- let numPoints = view.getUint32(1, true);
- node.numPoints = numPoints;
- stack.push({children: children, numPoints: numPoints, name: node.name});
- let decoded = [];
- let offset = 5;
- while (stack.length > 0) {
- let snode = stack.shift();
- let mask = 1;
- for (let i = 0; i < 8; i++) {
- if ((snode.children & mask) !== 0) {
- let childName = snode.name + i;
- let childChildren = view.getUint8(offset);
- let childNumPoints = view.getUint32(offset + 1, true);
- stack.push({children: childChildren, numPoints: childNumPoints, name: childName});
- decoded.push({children: childChildren, numPoints: childNumPoints, name: childName});
- offset += 5;
- }
- mask = mask * 2;
- }
- if (offset === hbuffer.byteLength) {
- break;
- }
- }
- // console.log(decoded);
- let nodes = {};
- nodes[node.name] = node;
- let pco = node.pcoGeometry;
- for (let i = 0; i < decoded.length; i++) {
- let name = decoded[i].name;
- let decodedNumPoints = decoded[i].numPoints;
- let index = parseInt(name.charAt(name.length - 1));
- let parentName = name.substring(0, name.length - 1);
- let parentNode = nodes[parentName];
- let level = name.length - 1;
- pco.dispatchEvent({type:'updateNodeMaxLevel',level});//add
-
- let boundingBox = Utils.createChildAABB(parentNode.boundingBox, index);
- let currentNode = new PointCloudOctreeGeometryNode(name, pco, boundingBox);
- currentNode.level = level;
- currentNode.numPoints = decodedNumPoints;
- currentNode.hasChildren = decoded[i].children > 0;
- currentNode.spacing = pco.spacing / Math.pow(2, level);
- parentNode.addChild(currentNode);
- nodes[name] = currentNode;
- }
- let duration = performance.now() - tStart;
- if(duration > 5){
- /* let msg = `duration: ${duration}ms, numNodes: ${decoded.length}`;
- console.log(msg); */
- }
- node.loadPoints();
- };
- if ((node.level % node.pcoGeometry.hierarchyStepSize) === 0) {
- // let hurl = node.pcoGeometry.octreeDir + "/../hierarchy/" + node.name + ".hrc";
- let hurl = node.pcoGeometry.octreeDir + '/' + node.getHierarchyPath() + '/' + node.name + '.hrc';
- hurl += '?m='+node.pcoGeometry.timeStamp; //add
-
- let xhr = XHRFactory.createXMLHttpRequest();
- xhr.open('GET', hurl, true);
- xhr.responseType = 'arraybuffer';
- xhr.overrideMimeType('text/plain; charset=x-user-defined');
- xhr.onreadystatechange = () => {
- if (xhr.readyState === 4) {
- if (xhr.status === 200 || xhr.status === 0) {
- let hbuffer = xhr.response;
- callback(node, hbuffer);
- } else {
- console.log('Failed to load file! HTTP status: ' + xhr.status + ', file: ' + hurl);
- Potree.numNodesLoading--;
- }
- }
- };
- try {
- xhr.send(null);
- } catch (e) {
- console.log('fehler beim laden der punktwolke: ' + e);
- }
- }
- }
- getNumPoints(){
- return this.numPoints;
- }
-
- dispose(){
- if (this.geometry && this.parent != null) {
- this.geometry.dispose();
- this.geometry = null;
- this.loaded = false;
- this.dispatchEvent( { type: 'dispose' } );
-
- for (let i = 0; i < this.oneTimeDisposeHandlers.length; i++) {
- let handler = this.oneTimeDisposeHandlers[i];
- handler();
- }
- this.oneTimeDisposeHandlers = [];
- }
- }
-
- traverse(t, e){//add from navvis 25.js
- void 0 === e && (e = !0);
- for (var n, i = e ? [this] : []; void 0 !== (n = i.pop()); ) {
- t(n);
- for (var o = 0, r = n.children; o < r.length; o++) {
- var a = r[o];
- null !== a && i.push(a);
- }
- }
- }
- }
- PointCloudOctreeGeometryNode.IDCount = 0;
- // -------------------------------------------
- // to get a ready to use gradient array from a chroma.js gradient:
- // http://gka.github.io/chroma.js/
- // -------------------------------------------
- //
- // let stops = [];
- // for(let i = 0; i <= 10; i++){
- // let range = chroma.scale(['yellow', 'navy']).mode('lch').domain([10,0])(i)._rgb
- // .slice(0, 3)
- // .map(v => (v / 255).toFixed(4))
- // .join(", ");
- //
- // let line = `[${i / 10}, new THREE.Color(${range})],`;
- //
- // stops.push(line);
- // }
- // stops.join("\n");
- //
- //
- //
- // -------------------------------------------
- // to get a ready to use gradient array from matplotlib:
- // -------------------------------------------
- // import matplotlib.pyplot as plt
- // import matplotlib.colors as colors
- //
- // norm = colors.Normalize(vmin=0,vmax=1)
- // cmap = plt.cm.viridis
- //
- // for i in range(0,11):
- // u = i / 10
- // rgb = cmap(norm(u))[0:3]
- // rgb = ["{0:.3f}".format(v) for v in rgb]
- // rgb = "[" + str(u) + ", new THREE.Color(" + ", ".join(rgb) + ")],"
- // print(rgb)
- let Gradients = {
- // From chroma spectral http://gka.github.io/chroma.js/
- SPECTRAL: [
- [0, new Color(0.3686, 0.3098, 0.6353)],
- [0.1, new Color(0.1961, 0.5333, 0.7412)],
- [0.2, new Color(0.4000, 0.7608, 0.6471)],
- [0.3, new Color(0.6706, 0.8667, 0.6431)],
- [0.4, new Color(0.9020, 0.9608, 0.5961)],
- [0.5, new Color(1.0000, 1.0000, 0.7490)],
- [0.6, new Color(0.9961, 0.8784, 0.5451)],
- [0.7, new Color(0.9922, 0.6824, 0.3804)],
- [0.8, new Color(0.9569, 0.4275, 0.2627)],
- [0.9, new Color(0.8353, 0.2431, 0.3098)],
- [1, new Color(0.6196, 0.0039, 0.2588)]
- ],
- PLASMA: [
- [0.0, new Color(0.241, 0.015, 0.610)],
- [0.1, new Color(0.387, 0.001, 0.654)],
- [0.2, new Color(0.524, 0.025, 0.653)],
- [0.3, new Color(0.651, 0.125, 0.596)],
- [0.4, new Color(0.752, 0.227, 0.513)],
- [0.5, new Color(0.837, 0.329, 0.431)],
- [0.6, new Color(0.907, 0.435, 0.353)],
- [0.7, new Color(0.963, 0.554, 0.272)],
- [0.8, new Color(0.992, 0.681, 0.195)],
- [0.9, new Color(0.987, 0.822, 0.144)],
- [1.0, new Color(0.940, 0.975, 0.131)]
- ],
- YELLOW_GREEN: [
- [0, new Color(0.1647, 0.2824, 0.3451)],
- [0.1, new Color(0.1338, 0.3555, 0.4227)],
- [0.2, new Color(0.0610, 0.4319, 0.4864)],
- [0.3, new Color(0.0000, 0.5099, 0.5319)],
- [0.4, new Color(0.0000, 0.5881, 0.5569)],
- [0.5, new Color(0.1370, 0.6650, 0.5614)],
- [0.6, new Color(0.2906, 0.7395, 0.5477)],
- [0.7, new Color(0.4453, 0.8099, 0.5201)],
- [0.8, new Color(0.6102, 0.8748, 0.4850)],
- [0.9, new Color(0.7883, 0.9323, 0.4514)],
- [1, new Color(0.9804, 0.9804, 0.4314)]
- ],
- VIRIDIS: [
- [0.0, new Color(0.267, 0.005, 0.329)],
- [0.1, new Color(0.283, 0.141, 0.458)],
- [0.2, new Color(0.254, 0.265, 0.530)],
- [0.3, new Color(0.207, 0.372, 0.553)],
- [0.4, new Color(0.164, 0.471, 0.558)],
- [0.5, new Color(0.128, 0.567, 0.551)],
- [0.6, new Color(0.135, 0.659, 0.518)],
- [0.7, new Color(0.267, 0.749, 0.441)],
- [0.8, new Color(0.478, 0.821, 0.318)],
- [0.9, new Color(0.741, 0.873, 0.150)],
- [1.0, new Color(0.993, 0.906, 0.144)]
- ],
- INFERNO: [
- [0.0, new Color(0.077, 0.042, 0.206)],
- [0.1, new Color(0.225, 0.036, 0.388)],
- [0.2, new Color(0.373, 0.074, 0.432)],
- [0.3, new Color(0.522, 0.128, 0.420)],
- [0.4, new Color(0.665, 0.182, 0.370)],
- [0.5, new Color(0.797, 0.255, 0.287)],
- [0.6, new Color(0.902, 0.364, 0.184)],
- [0.7, new Color(0.969, 0.516, 0.063)],
- [0.8, new Color(0.988, 0.683, 0.072)],
- [0.9, new Color(0.961, 0.859, 0.298)],
- [1.0, new Color(0.988, 0.998, 0.645)]
- ],
- GRAYSCALE: [
- [0, new Color(0, 0, 0)],
- [1, new Color(1, 1, 1)]
- ],
- // 16 samples of the TURBU color scheme
- // values taken from: https://gist.github.com/mikhailov-work/ee72ba4191942acecc03fe6da94fc73f
- // original file licensed under Apache-2.0
- TURBO: [
- [0.00, new Color(0.18995, 0.07176, 0.23217)],
- [0.07, new Color(0.25107, 0.25237, 0.63374)],
- [0.13, new Color(0.27628, 0.42118, 0.89123)],
- [0.20, new Color(0.25862, 0.57958, 0.99876)],
- [0.27, new Color(0.15844, 0.73551, 0.92305)],
- [0.33, new Color(0.09267, 0.86554, 0.7623)],
- [0.40, new Color(0.19659, 0.94901, 0.59466)],
- [0.47, new Color(0.42778, 0.99419, 0.38575)],
- [0.53, new Color(0.64362, 0.98999, 0.23356)],
- [0.60, new Color(0.80473, 0.92452, 0.20459)],
- [0.67, new Color(0.93301, 0.81236, 0.22667)],
- [0.73, new Color(0.99314, 0.67408, 0.20348)],
- [0.80, new Color(0.9836, 0.49291, 0.12849)],
- [0.87, new Color(0.92105, 0.31489, 0.05475)],
- [0.93, new Color(0.81608, 0.18462, 0.01809)],
- [1.00, new Color(0.66449, 0.08436, 0.00424)],
- ],
- RAINBOW: [
- [0, new Color(0.278, 0, 0.714)],
- [1 / 6, new Color(0, 0, 1)],
- [2 / 6, new Color(0, 1, 1)],
- [3 / 6, new Color(0, 1, 0)],
- [4 / 6, new Color(1, 1, 0)],
- [5 / 6, new Color(1, 0.64, 0)],
- [1, new Color(1, 0, 0)]
- ],
- CONTOUR: [
- [0.00, new Color(0, 0, 0)],
- [0.03, new Color(0, 0, 0)],
- [0.04, new Color(1, 1, 1)],
- [1.00, new Color(1, 1, 1)]
- ],
- };
- const ClassificationScheme = {
- DEFAULT: {
- 0: { visible: true, name: 'never classified' , color: [0.5, 0.5, 0.5, 1.0] },
- 1: { visible: true, name: 'unclassified' , color: [0.5, 0.5, 0.5, 1.0] },
- 2: { visible: true, name: 'ground' , color: [0.63, 0.32, 0.18, 1.0] },
- 3: { visible: true, name: 'low vegetation' , color: [0.0, 1.0, 0.0, 1.0] },
- 4: { visible: true, name: 'medium vegetation' , color: [0.0, 0.8, 0.0, 1.0] },
- 5: { visible: true, name: 'high vegetation' , color: [0.0, 0.6, 0.0, 1.0] },
- 6: { visible: true, name: 'building' , color: [1.0, 0.66, 0.0, 1.0] },
- 7: { visible: true, name: 'low point(noise)' , color: [1.0, 0.0, 1.0, 1.0] },
- 8: { visible: true, name: 'key-point' , color: [1.0, 0.0, 0.0, 1.0] },
- 9: { visible: true, name: 'water' , color: [0.0, 0.0, 1.0, 1.0] },
- 12: { visible: true, name: 'overlap' , color: [1.0, 1.0, 0.0, 1.0] },
- DEFAULT: { visible: true, name: 'default' , color: [0.3, 0.6, 0.6, 0.5] },
- }
- };
- Object.defineProperty(ClassificationScheme, 'RANDOM', {
- get: function() {
- let scheme = {};
- for(let i = 0; i <= 255; i++){
- scheme[i] = new Vector4(Math.random(), Math.random(), Math.random());
- }
- scheme["DEFAULT"] = new Vector4(Math.random(), Math.random(), Math.random());
- return scheme;
- }
- });
- //
- // how to calculate the radius of a projected sphere in screen space
- // http://stackoverflow.com/questions/21648630/radius-of-projected-sphere-in-screen-space
- // http://stackoverflow.com/questions/3717226/radius-of-projected-sphere
- //
- class PointCloudMaterial$1 extends RawShaderMaterial {
- constructor (parameters = {}) {
- super();
-
-
-
-
- this.visibleNodesTexture = Utils.generateDataTexture(2048, 1, new Color(0xffffff));
- this.visibleNodesTexture.minFilter = NearestFilter;
- this.visibleNodesTexture.magFilter = NearestFilter;
- let getValid = (a, b) => {
- if(a !== undefined){
- return a;
- }else {
- return b;
- }
- };
- let pointSize = getValid(parameters.size, 1.0);
- let minSize = getValid(parameters.minSize, 2.0);
- let maxSize = getValid(parameters.maxSize, 1550.0);
- let treeType = getValid(parameters.treeType, TreeType.OCTREE);
- this._pointSizeType = PointSizeType.FIXED;
- this._shape = PointShape.SQUARE;
- this._useClipBox = false;
- this.clipBoxes = [];
- this.clipPolygons = [];
- this._weighted = false;
- this._gradient = Gradients.RAINBOW;//Gradients.SPECTRAL;//海拔贴图种类
- this.gradientTexture = PointCloudMaterial$1.generateGradientTexture(this._gradient);
- this._matcap = "matcap.jpg";
- this.matcapTexture = Potree.PointCloudMaterial.generateMatcapTexture(this._matcap);
- this.lights = false;
- this.fog = false;
- this._treeType = treeType;
- this._useEDL = false;
- this.defines = new Map();
- this.ranges = new Map();
- this._activeAttributeName = null;
- this._defaultIntensityRangeChanged = false;
- this._defaultElevationRangeChanged = false;
-
- {
- const [width, height] = [256, 1];
- let data = new Uint8Array(width * 4);
- let texture = new DataTexture(data, width, height, RGBAFormat);
- texture.magFilter = NearestFilter;
- texture.needsUpdate = true;
- this.classificationTexture = texture;
- }
- this.attributes = {
- position: { type: 'fv', value: [] },
- color: { type: 'fv', value: [] },
- normal: { type: 'fv', value: [] },
- intensity: { type: 'f', value: [] },
- classification: { type: 'f', value: [] },
- returnNumber: { type: 'f', value: [] },
- numberOfReturns: { type: 'f', value: [] },
- pointSourceID: { type: 'f', value: [] },
- indices: { type: 'fv', value: [] }
- };
- this.uniforms = {
- level: { type: "f", value: 0.0 },
- vnStart: { type: "f", value: 0.0 },
- spacing: { type: "f", value: 1.0 },
- blendHardness: { type: "f", value: 2.0 },
- blendDepthSupplement: { type: "f", value: 0.0 },
- fov: { type: "f", value: 1.0 },
- /* screenWidth: { type: "f", value: 1.0 },
- screenHeight: { type: "f", value: 1.0 }, */
- resolution: { type: 'v2', value: new Vector2$1() },
- near: { type: "f", value: 0.1 },
- far: { type: "f", value: 1.0 },
- uColor: { type: "c", value: new Color( 0xffffff ) },
- uOpacity: { type: "f", value: 1.0 },
- size: { type: "f", value: pointSize },
- minSize: { type: "f", value: minSize },
- maxSize: { type: "f", value: maxSize },
- octreeSize: { type: "f", value: 0 },
- bbSize: { type: "fv", value: [0, 0, 0] },
- elevationRange: { type: "2fv", value: [0, 0] },
- clipBoxCount: { type: "f", value: 0 },
- //clipSphereCount: { type: "f", value: 0 },
- clipPolygonCount: { type: "i", value: 0 },
- clipBoxes: { type: "Matrix4fv", value: [] },
- //clipSpheres: { type: "Matrix4fv", value: [] },
- clipPolygons: { type: "3fv", value: [] },
- clipPolygonVCount: { type: "iv", value: [] },
- clipPolygonVP: { type: "Matrix4fv", value: [] },
- visibleNodes: { type: "t", value: this.visibleNodesTexture },
- pcIndex: { type: "f", value: 0 },
- gradient: { type: "t", value: this.gradientTexture },
- classificationLUT: { type: "t", value: this.classificationTexture },
- uHQDepthMap: { type: "t", value: null },
- toModel: { type: "Matrix4f", value: [] },
- diffuse: { type: "fv", value: [1, 1, 1] },
- transition: { type: "f", value: 0.5 },
- intensityRange: { type: "fv", value: [Infinity, -Infinity] },
- intensity_gbc: { type: "fv", value: [1, 0, 0]},
- uRGB_gbc: { type: "fv", value: [1, 0, 0]},
- // intensityGamma: { type: "f", value: 1 },
- // intensityContrast: { type: "f", value: 0 },
- // intensityBrightness:{ type: "f", value: 0 },
- // rgbGamma: { type: "f", value: 1 },
- // rgbContrast: { type: "f", value: 0 },
- // rgbBrightness: { type: "f", value: 0 },
- wRGB: { type: "f", value: 1 },
- wIntensity: { type: "f", value: 0 },
- wElevation: { type: "f", value: 0 },
- wClassification: { type: "f", value: 0 },
- wReturnNumber: { type: "f", value: 0 },
- wSourceID: { type: "f", value: 0 },
- useOrthographicCamera: { type: "b", value: false },
- elevationGradientRepat: { type: "i", value: ElevationGradientRepeat.CLAMP },
- clipTask: { type: "i", value: 1 },
- clipMethod: { type: "i", value: 1 },
- uShadowColor: { type: "3fv", value: [0, 0, 0] },
- uExtraScale: { type: "f", value: 1},
- uExtraOffset: { type: "f", value: 0},
- uExtraRange: { type: "2fv", value: [0, 1] },
- uExtraGammaBrightContr: { type: "3fv", value: [1, 0, 0] },
- uFilterReturnNumberRange: { type: "fv", value: [0, 7]},
- uFilterNumberOfReturnsRange: { type: "fv", value: [0, 7]},
- uFilterGPSTimeClipRange: { type: "fv", value: [0, 7]},
- uFilterPointSourceIDClipRange: { type: "fv", value: [0, 65535]},
- matcapTextureUniform: { type: "t", value: this.matcapTexture },
- backfaceCulling: { type: "b", value: false },
-
- ////////////////add
- //usePanoMap:{ type: "i", value: 0 },
- progress: {
- type: "f",
- value: 0
- },
- easeInOutRatio:{
- type: "f",
- value: 0.3
- },
- pano0Map: {
- type: "t",
- value: null
- },
- pano0Position: {
- type: "v3",
- value: new Vector3
- },
- pano0Matrix: {
- type: "m4",
- value: new Matrix4
- },
- pano1Map: {
- type: "t",
- value: null
- },
- pano1Position: {
- type: "v3",
- value: new Vector3
- },
- pano1Matrix: {
- type: "m4",
- value: new Matrix4
- },
-
-
- };
- this.classification = ClassificationScheme.DEFAULT;
- this.defaultAttributeValues.normal = [0, 0, 0];
- this.defaultAttributeValues.classification = [0, 0, 0];
- this.defaultAttributeValues.indices = [0, 0, 0, 0];
- this.vertexShader = Shaders['pointcloud.vs'];
- this.fragmentShader = Shaders['pointcloud.fs'];
-
- this.vertexColors = VertexColors;
- this.updateShaderSource();
- }
- setDefine(key, value){
- if(value !== undefined && value !== null){
- if(this.defines.get(key) !== value){
- this.defines.set(key, value);
- this.updateShaderSource();
- }
- }else {
- this.removeDefine(key);
- }
- }
- removeDefine(key){
- this.defines.delete(key);
- }
- updateShaderSource () {
- let vs = Shaders['pointcloud.vs'];
- let fs = Shaders['pointcloud.fs'];
- let definesString = this.getDefines();
- let vsVersionIndex = vs.indexOf("#version ");
- let fsVersionIndex = fs.indexOf("#version ");
- if(vsVersionIndex >= 0){
- vs = vs.replace(/(#version .*)/, `$1\n${definesString}`);
- }else {
- vs = `${definesString}\n${vs}`;
- }
- if(fsVersionIndex >= 0){
- fs = fs.replace(/(#version .*)/, `$1\n${definesString}`);
- }else {
- fs = `${definesString}\n${fs}`;
- }
- this.vertexShader = vs;
- this.fragmentShader = fs;
- if (this.opacity === 1.0 && !this.useFilterByNormal) {//add useFilterByNormal
- this.blending = NoBlending;
- this.transparent = false;
- this.depthTest = true;
- this.depthWrite = true;
- this.depthFunc = LessEqualDepth;
- } else if ( (this.opacity < 1.0 ||this.useFilterByNormal) && !this.useEDL) {//add useFilterByNormal
- this.blending = AdditiveBlending;
- this.transparent = true;
- this.depthTest = false;
- this.depthWrite = true;
- this.depthFunc = AlwaysDepth;
- }
- if (this.weighted) {
- this.blending = AdditiveBlending;
- this.transparent = true;
- this.depthTest = true;
- this.depthWrite = false;
- }
- this.needsUpdate = true;
- }
- getDefines () {
- let defines = [];
-
- if (this.pointSizeType === PointSizeType.FIXED) {
- defines.push('#define fixed_point_size');
- } else if (this.pointSizeType === PointSizeType.ATTENUATED) {
- defines.push('#define attenuated_point_size');
- } else if (this.pointSizeType === PointSizeType.ADAPTIVE) {
- defines.push('#define adaptive_point_size');
- }
-
- if(!Features.EXT_DEPTH.isSupported() && this.shape === PointShape.PARABOLOID){
- this.shape = PointShape.SQUARE ;//强行替换
- }
-
-
- if (this.shape === PointShape.SQUARE) {
- defines.push('#define square_point_shape');
- } else if (this.shape === PointShape.CIRCLE) {
- defines.push('#define circle_point_shape');
- } else if (this.shape === PointShape.PARABOLOID) {
- defines.push('#define paraboloid_point_shape');
- }
- //console.log('this.shape PARABOLOID', this.shape, this.shape === PointShape.PARABOLOID)
- if (this._useEDL || this.fakeEDL) {
- defines.push('#define use_edl');
- }
- if(this.activeAttributeName){
- let attributeName = this.activeAttributeName.replace(/[^a-zA-Z0-9]/g, '_');
- defines.push(`#define color_type_${attributeName}`);
- }
-
- if(this._treeType === TreeType.OCTREE){
- defines.push('#define tree_type_octree');
- }else if(this._treeType === TreeType.KDTREE){
- defines.push('#define tree_type_kdtree');
- }
- if (this.weighted) {
- defines.push('#define weighted_splats');
- }
- for(let [key, value] of this.defines){
- defines.push(value);
- }
- return defines.join("\n");
- }
- setClipBoxes (clipBoxes) {
- if (!clipBoxes) {
- return;
- }
- let doUpdate = (this.clipBoxes.length !== clipBoxes.length) && (clipBoxes.length === 0 || this.clipBoxes.length === 0);
- this.uniforms.clipBoxCount.value = this.clipBoxes.length;
- this.clipBoxes = clipBoxes;
- if (doUpdate) {
- this.updateShaderSource();
- }
- this.uniforms.clipBoxes.value = new Float32Array(this.clipBoxes.length * 16);
- for (let i = 0; i < this.clipBoxes.length; i++) {
- let box = clipBoxes[i];
- this.uniforms.clipBoxes.value.set(box.inverse.elements, 16 * i);
- }
- for (let i = 0; i < this.uniforms.clipBoxes.value.length; i++) {
- if (Number.isNaN(this.uniforms.clipBoxes.value[i])) {
- this.uniforms.clipBoxes.value[i] = Infinity;
- }
- }
- }
- setClipPolygons(clipPolygons, maxPolygonVertices) {
- if(!clipPolygons){
- return;
- }
- this.clipPolygons = clipPolygons;
- let doUpdate = (this.clipPolygons.length !== clipPolygons.length);
- if(doUpdate){
- this.updateShaderSource();
- }
- }
-
- get gradient(){
- return this._gradient;
- }
- set gradient (value) {//海拔贴图
- if (this._gradient !== value) {
- this._gradient = value;
- this.gradientTexture = PointCloudMaterial$1.generateGradientTexture(this._gradient);
- this.uniforms.gradient.value = this.gradientTexture;
- }
- }
- get matcap(){
- return this._matcap;
- }
- set matcap (value) {
- if (this._matcap !== value) {
- this._matcap = value;
- this.matcapTexture = Potree.PointCloudMaterial.generateMatcapTexture(this._matcap);
- this.uniforms.matcapTextureUniform.value = this.matcapTexture;
- }
- }
- get useOrthographicCamera() {
- return this.uniforms.useOrthographicCamera.value;
- }
- set useOrthographicCamera(value) {
- if(this.uniforms.useOrthographicCamera.value !== value){
- this.uniforms.useOrthographicCamera.value = value;
- }
- }
- get backfaceCulling() {
- return this.uniforms.backfaceCulling.value;
- }
- set backfaceCulling(value) {
- if(this.uniforms.backfaceCulling.value !== value){
- this.uniforms.backfaceCulling.value = value;
- this.dispatchEvent({type: 'backface_changed', target: this});
- }
- }
- recomputeClassification () {
- const classification = this.classification;
- const data = this.classificationTexture.image.data;
- let width = 256;
- const black = [1, 1, 1, 1];
- let valuesChanged = false;
- for (let i = 0; i < width; i++) {
- let color;
- let visible = true;
- if (classification[i]) {
- color = classification[i].color;
- visible = classification[i].visible;
- } else if (classification[i % 32]) {
- color = classification[i % 32].color;
- visible = classification[i % 32].visible;
- } else if(classification.DEFAULT) {
- color = classification.DEFAULT.color;
- visible = classification.DEFAULT.visible;
- }else {
- color = black;
- }
- const r = parseInt(255 * color[0]);
- const g = parseInt(255 * color[1]);
- const b = parseInt(255 * color[2]);
- const a = visible ? parseInt(255 * color[3]) : 0;
- if(data[4 * i + 0] !== r){
- data[4 * i + 0] = r;
- valuesChanged = true;
- }
- if(data[4 * i + 1] !== g){
- data[4 * i + 1] = g;
- valuesChanged = true;
- }
- if(data[4 * i + 2] !== b){
- data[4 * i + 2] = b;
- valuesChanged = true;
- }
- if(data[4 * i + 3] !== a){
- data[4 * i + 3] = a;
- valuesChanged = true;
- }
- }
- if(valuesChanged){
- this.classificationTexture.needsUpdate = true;
- this.dispatchEvent({
- type: 'material_property_changed',
- target: this
- });
- }
- }
- get spacing () {
- return this.uniforms.spacing.value;
- }
- set spacing (value) {
- if (this.uniforms.spacing.value !== value) { // 即 uOctreeSpacing 来自cloud.js里
- this.uniforms.spacing.value = value;
- }
- }
- get useClipBox () {
- return this._useClipBox;
- }
- set useClipBox (value) {
- if (this._useClipBox !== value) {
- this._useClipBox = value;
- this.updateShaderSource();
- }
- }
- get clipTask(){
- return this.uniforms.clipTask.value;
- }
- set clipTask(mode){
- this.uniforms.clipTask.value = mode;
- }
- get elevationGradientRepat(){
- return this.uniforms.elevationGradientRepat.value;
- }
- set elevationGradientRepat(mode){
- this.uniforms.elevationGradientRepat.value = mode;
- }
- get clipMethod(){
- return this.uniforms.clipMethod.value;
- }
- set clipMethod(mode){
- this.uniforms.clipMethod.value = mode;
- }
- get weighted(){
- return this._weighted;
- }
- set weighted (value) {
- if (this._weighted !== value) {
- this._weighted = value;
- this.updateShaderSource();
- }
- }
- get fov () {
- return this.uniforms.fov.value;
- }
- set fov (value) {
- if (this.uniforms.fov.value !== value) {
- this.uniforms.fov.value = value;
- // this.updateShaderSource();
- }
- }
- /* get screenWidth () {
- return this.uniforms.screenWidth.value;
- }
- set screenWidth (value) {
- if (this.uniforms.screenWidth.value !== value) {
- this.uniforms.screenWidth.value = value;
- // this.updateShaderSource();
- }
- }
- get screenHeight () {
- return this.uniforms.screenHeight.value;
- }
- set screenHeight (value) {
- if (this.uniforms.screenHeight.value !== value) {
- this.uniforms.screenHeight.value = value;
- // this.updateShaderSource();
- }
- }*/
-
- //add--------------
- get resolution(){
- return this.uniforms.resolution.value
- }
- set resolution(value){
- this.uniforms.resolution.value.copy(value);
- }
- //--------------
-
-
-
- get near () {
- return this.uniforms.near.value;
- }
- set near (value) {
- if (this.uniforms.near.value !== value) {
- this.uniforms.near.value = value;
- }
- }
- get far () {
- return this.uniforms.far.value;
- }
- set far (value) {
- if (this.uniforms.far.value !== value) {
- this.uniforms.far.value = value;
- }
- }
-
- get opacity(){
- return this.uniforms.uOpacity.value;
- }
- set opacity (value) {
- if (this.uniforms && this.uniforms.uOpacity) {
- if (this.uniforms.uOpacity.value !== value) {
- this.uniforms.uOpacity.value = value;
- this.updateShaderSource();
- this.dispatchEvent({
- type: 'opacity_changed',
- target: this
- });
- this.dispatchEvent({
- type: 'material_property_changed',
- target: this
- });
- }
- }
- }
- get activeAttributeName(){
- return this._activeAttributeName;
- }
- set activeAttributeName(value){
- if (this._activeAttributeName !== value) {
- this._activeAttributeName = value;
- this.updateShaderSource();
- this.dispatchEvent({
- type: 'active_attribute_changed',
- target: this
- });
- this.dispatchEvent({
- type: 'material_property_changed',
- target: this
- });
- }
- }
- get pointSizeType () {
- return this._pointSizeType;
- }
- set pointSizeType (value) {
-
- if(typeof value == 'string' )value = PointSizeType[value];
-
- if (this._pointSizeType !== value) {
- this._pointSizeType = value;
- this.updateShaderSource(); //这句表明这个属性频繁更改会卡顿
- this.dispatchEvent({
- type: 'point_size_type_changed',
- target: this
- });
- this.dispatchEvent({
- type: 'material_property_changed',
- target: this
- });
- }
- }
- get useEDL(){
- return this._useEDL;
- }
- set useEDL (value) {
- if (this._useEDL !== value) {
- this._useEDL = value;
- this.updateShaderSource();
- }
- }
-
- get fakeEDL(){
- return this._fakeEDL;
- }
- set fakeEDL (value) {//add
- if (this._fakeEDL !== value) {
- this._fakeEDL = value;
- this.updateShaderSource();
- }
- }
- get color () {
- return this.uniforms.uColor.value;
- }
- set color (value) {//改
-
- if(value == this.color_)return
- let color = value;
- //if (!this.uniforms.uColor.value.equals(value)) {
- if(typeof value == 'string') {
- var colorArr = Potree.config.colors[value];
- if(!colorArr){
- //console.warn('没找到该颜色值'+ value)
- }else {
- color = new Color().fromArray(colorArr).multiplyScalar(1/255);
- }
-
- }
- this.uniforms.uColor.value.set(color);
- //this.uniforms.uColor.value.copy(value);
-
- this.dispatchEvent({
- type: 'color_changed',
- target: this
- });
- this.dispatchEvent({
- type: 'material_property_changed',
- target: this
- });
- //}
-
- this.color_ = value; //记录下str
- }
- get shape () {
- return this._shape;
- }
- set shape (value) {
- if (this._shape !== value) {
- this._shape = value;
- this.updateShaderSource();
- this.dispatchEvent({type: 'point_shape_changed', target: this});
- this.dispatchEvent({
- type: 'material_property_changed',
- target: this
- });
- }
- }
- get treeType () {
- return this._treeType;
- }
- set treeType (value) {
- if (this._treeType !== value) {
- this._treeType = value;
- this.updateShaderSource();
- }
- }
- get bbSize () {
- return this.uniforms.bbSize.value;
- }
- set bbSize (value) {
- this.uniforms.bbSize.value = value;
- }
- get size () {
- return this.uniforms.size.value;
- }
- set size (value) {
- if (this.uniforms.size.value !== value) {
- this.uniforms.size.value = value;
- this.dispatchEvent({
- type: 'point_size_changed',
- target: this
- });
- this.dispatchEvent({
- type: 'material_property_changed',
- target: this
- });
- }
- }
-
- get minSize(){
- return this.uniforms.minSize.value;
- }
- set minSize(value){
- if (this.uniforms.minSize.value !== value) {
- this.uniforms.minSize.value = value;
- this.dispatchEvent({
- type: 'point_size_changed',
- target: this
- });
- this.dispatchEvent({
- type: 'material_property_changed',
- target: this
- });
- }
- }
- get elevationRange () {
- return this.uniforms.elevationRange.value;
- }
- set elevationRange (value) {
- let changed = this.uniforms.elevationRange.value[0] !== value[0]
- || this.uniforms.elevationRange.value[1] !== value[1];
- if(changed){
- this.uniforms.elevationRange.value = value;
- this._defaultElevationRangeChanged = true;
- this.dispatchEvent({
- type: 'material_property_changed',
- target: this
- });
- }
- }
- get heightMin () {
- return this.uniforms.elevationRange.value[0];
- }
- set heightMin (value) {
- this.elevationRange = [value, this.elevationRange[1]];
- }
- get heightMax () {
- return this.uniforms.elevationRange.value[1];
- }
- set heightMax (value) {
- this.elevationRange = [this.elevationRange[0], value];
- }
- get transition () {
- return this.uniforms.transition.value;
- }
- set transition (value) {
- this.uniforms.transition.value = value;
- }
- get intensityRange () {
- return this.uniforms.intensityRange.value;
- }
- set intensityRange (value) {
- if (!(value instanceof Array && value.length === 2)) {
- return;
- }
- if (value[0] === this.uniforms.intensityRange.value[0] &&
- value[1] === this.uniforms.intensityRange.value[1]) {
- return;
- }
- this.uniforms.intensityRange.value = value;
- this._defaultIntensityRangeChanged = true;
- this.dispatchEvent({
- type: 'material_property_changed',
- target: this
- });
- }
- get intensityGamma () {
- return this.uniforms.intensity_gbc.value[0];
- }
- set intensityGamma (value) {
- if (this.uniforms.intensity_gbc.value[0] !== value) {
- this.uniforms.intensity_gbc.value[0] = value;
- this.dispatchEvent({
- type: 'material_property_changed',
- target: this
- });
- }
- }
- get intensityContrast () {
- return this.uniforms.intensity_gbc.value[2];
- }
- set intensityContrast (value) {
- if (this.uniforms.intensity_gbc.value[2] !== value) {
- this.uniforms.intensity_gbc.value[2] = value;
- this.dispatchEvent({
- type: 'material_property_changed',
- target: this
- });
- }
- }
- get intensityBrightness () {
- return this.uniforms.intensity_gbc.value[1];
- }
- set intensityBrightness (value) {
- if (this.uniforms.intensity_gbc.value[1] !== value) {
- this.uniforms.intensity_gbc.value[1] = value;
- this.dispatchEvent({
- type: 'material_property_changed',
- target: this
- });
- }
- }
- get rgbGamma () {
- return this.uniforms.uRGB_gbc.value[0];
- }
- set rgbGamma (value) {
- if (this.uniforms.uRGB_gbc.value[0] !== value) {
- this.uniforms.uRGB_gbc.value[0] = value;
- this.dispatchEvent({
- type: 'material_property_changed',
- target: this
- });
- }
- }
- get rgbContrast () {
- return this.uniforms.uRGB_gbc.value[2];
- }
- set rgbContrast (value) {
- if (this.uniforms.uRGB_gbc.value[2] !== value) {
- this.uniforms.uRGB_gbc.value[2] = value;
- this.dispatchEvent({
- type: 'material_property_changed',
- target: this
- });
- }
- }
- get rgbBrightness () {
- return this.uniforms.uRGB_gbc.value[1];
- }
- set rgbBrightness (value) {
- if (this.uniforms.uRGB_gbc.value[1] !== value) {
- this.uniforms.uRGB_gbc.value[1] = value;
- this.dispatchEvent({
- type: 'material_property_changed',
- target: this
- });
- }
- }
-
- get extraGamma () {
- return this.uniforms.uExtraGammaBrightContr.value[0];
- }
- set extraGamma (value) {
- if (this.uniforms.uExtraGammaBrightContr.value[0] !== value) {
- this.uniforms.uExtraGammaBrightContr.value[0] = value;
- this.dispatchEvent({
- type: 'material_property_changed',
- target: this
- });
- }
- }
- get extraBrightness () {
- return this.uniforms.uExtraGammaBrightContr.value[1];
- }
- set extraBrightness (value) {
- if (this.uniforms.uExtraGammaBrightContr.value[1] !== value) {
- this.uniforms.uExtraGammaBrightContr.value[1] = value;
- this.dispatchEvent({
- type: 'material_property_changed',
- target: this
- });
- }
- }
- get extraContrast () {
- return this.uniforms.uExtraGammaBrightContr.value[2];
- }
- set extraContrast (value) {
- if (this.uniforms.uExtraGammaBrightContr.value[2] !== value) {
- this.uniforms.uExtraGammaBrightContr.value[2] = value;
- this.dispatchEvent({
- type: 'material_property_changed',
- target: this
- });
- }
- }
- getRange(attributeName){
- return this.ranges.get(attributeName);
- }
- setRange(attributeName, newRange){
- let rangeChanged = false;
- let oldRange = this.ranges.get(attributeName);
- if(oldRange != null && newRange != null){
- rangeChanged = oldRange[0] !== newRange[0] || oldRange[1] !== newRange[1];
- }else {
- rangeChanged = true;
- }
- this.ranges.set(attributeName, newRange);
- if(rangeChanged){
- this.dispatchEvent({
- type: 'material_property_changed',
- target: this
- });
- }
- }
- get extraRange () {
- return this.uniforms.uExtraRange.value;
- }
- set extraRange (value) {
- if (!(value instanceof Array && value.length === 2)) {
- return;
- }
- if (value[0] === this.uniforms.uExtraRange.value[0] &&
- value[1] === this.uniforms.uExtraRange.value[1]) {
- return;
- }
- this.uniforms.uExtraRange.value = value;
- this._defaultExtraRangeChanged = true;
- this.dispatchEvent({
- type: 'material_property_changed',
- target: this
- });
- }
- get weightRGB () {
- return this.uniforms.wRGB.value;
- }
- set weightRGB (value) {
- if(this.uniforms.wRGB.value !== value){
- this.uniforms.wRGB.value = value;
- this.dispatchEvent({
- type: 'material_property_changed',
- target: this
- });
- }
- }
- get weightIntensity () {
- return this.uniforms.wIntensity.value;
- }
- set weightIntensity (value) {
- if(this.uniforms.wIntensity.value !== value){
- this.uniforms.wIntensity.value = value;
- this.dispatchEvent({
- type: 'material_property_changed',
- target: this
- });
- }
- }
- get weightElevation () {
- return this.uniforms.wElevation.value;
- }
- set weightElevation (value) {
- if(this.uniforms.wElevation.value !== value){
- this.uniforms.wElevation.value = value;
- this.dispatchEvent({
- type: 'material_property_changed',
- target: this
- });
- }
- }
- get weightClassification () {
- return this.uniforms.wClassification.value;
- }
- set weightClassification (value) {
- if(this.uniforms.wClassification.value !== value){
- this.uniforms.wClassification.value = value;
- this.dispatchEvent({
- type: 'material_property_changed',
- target: this
- });
- }
- }
- get weightReturnNumber () {
- return this.uniforms.wReturnNumber.value;
- }
- set weightReturnNumber (value) {
- if(this.uniforms.wReturnNumber.value !== value){
- this.uniforms.wReturnNumber.value = value;
- this.dispatchEvent({
- type: 'material_property_changed',
- target: this
- });
- }
- }
- get weightSourceID () {
- return this.uniforms.wSourceID.value;
- }
- set weightSourceID (value) {
- if(this.uniforms.wSourceID.value !== value){
- this.uniforms.wSourceID.value = value;
- this.dispatchEvent({
- type: 'material_property_changed',
- target: this
- });
- }
- }
- static generateGradientTexture (gradient) {
- let size = 64;
- // create canvas
- let canvas = document.createElement('canvas');
- canvas.width = size;
- canvas.height = size;
- // get context
- let context = canvas.getContext('2d');
- // draw gradient
- context.rect(0, 0, size, size);
- let ctxGradient = context.createLinearGradient(0, 0, size, size);
- for (let i = 0; i < gradient.length; i++) {
- let step = gradient[i];
- ctxGradient.addColorStop(step[0], '#' + step[1].getHexString());
- }
- context.fillStyle = ctxGradient;
- context.fill();
-
- //let texture = new THREE.Texture(canvas);
- let texture = new CanvasTexture(canvas);
- texture.needsUpdate = true;
-
- texture.minFilter = LinearFilter;
- texture.wrap = RepeatWrapping;
- texture.repeat = 2;
- // textureImage = texture.image;
- return texture;
- }
-
- static generateMatcapTexture (matcap) {
- var url = new URL(Potree.resourcePath + "/textures/matcap/" + matcap).href;
- let texture = new TextureLoader().load( url );
- texture.magFilter = texture.minFilter = LinearFilter;
- texture.needsUpdate = true;
- // PotreeConverter_1.6_2018_07_29_windows_x64\PotreeConverter.exe autzen_xyzrgbXYZ_ascii.xyz -f xyzrgbXYZ -a RGB NORMAL -o autzen_xyzrgbXYZ_ascii_a -p index --overwrite
- // Switch matcap texture on the fly : viewer.scene.pointclouds[0].material.matcap = 'matcap1.jpg';
- // For non power of 2, use LinearFilter and dont generate mipmaps, For power of 2, use NearestFilter and generate mipmaps : matcap2.jpg 1 2 8 11 12 13
- return texture;
- }
- disableEvents(){
- if(this._hiddenListeners === undefined){
- this._hiddenListeners = this._listeners;
- this._listeners = {};
- }
- };
- enableEvents(){
- this._listeners = this._hiddenListeners;
- this._hiddenListeners = undefined;
- };
- ////////////////////////add
- setProjectedPanos(pano0, pano1, progressValue, easeInOutRatio){
-
- //this.uniforms.usePanoMap.value = 1
- this.usePanoMap = true;
-
- progressValue!=void 0 && (this.uniforms.progress.value = progressValue);
- //pano0.ensureSkyboxReadyForRender();
- this.uniforms.pano0Map.value = pano0.getSkyboxTexture();
- this.uniforms.pano0Position.value.copy(pano0.position);
- this.uniforms.pano0Matrix.value.copy(pano0.panoMatrix );
- //pano1.ensureSkyboxReadyForRender();
-
- this.uniforms.easeInOutRatio.value = easeInOutRatio || 0; //之前做点云和全景混合时加的,为了让点云颜色柔和切换到全景颜色。如不混合就0
-
- this.uniforms.pano1Map.value = pano1.getSkyboxTexture();
- this.uniforms.pano1Position.value.copy(pano1.position);
- this.uniforms.pano1Matrix.value.copy(pano1.panoMatrix );
-
- //this.updateShaderSource()
- //this.needsUpdate = true;
- }
- stopProjectedPanos(){
- //this.uniforms.usePanoMap.value = 0
- this.usePanoMap = false;
- }
- // copyFrom(from){
- // var a = 10;
- // for(let name of Object.keys(this.uniforms)){
- // this.uniforms[name].value = from.uniforms[name].value;
- // }
- // }
- // copy(from){
- // this.copyFrom(from);
- // }
-
-
- }
- var Common = {
- sortByScore: function(list, request, rank){
- var i = request ? Common.filterAll(list, request) : list;
- return 0 === i.length ? null : i = i.map(function(e) {
- return {
- item: e,
- score: rank.reduce(function(t, i) {
- return t + i(e)
- }, 0)
- }
- }).sort(function(e, t) {
- return t.score - e.score;
- })
- }
- ,
-
- filterAll: function(e, t) {
- return e.filter(function (e) {
- return t.every(function (t) {
- return t(e)
- })
- })
- },
-
-
-
- //---------------
- find : function(list, request, rank, sortByScore ) {
- if(sortByScore){
- var r = this.sortByScore(list, request, rank);
- return r && r[0] && r[0].item
- }else {
- var i = request ? Common.filterAll(list, request) : list;
- return 0 === i.length ? null : (rank && rank.forEach(function(e) {
- i = Common.stableSort(i, e);
- }),
- i[0])
- }
-
- }
-
- ,
- stableSort: function(e, f) {//用到排序函数,涉及到两个item相减
- return e.map(function(e, i) {
- return {
- value: e,
- index: i
- }
- }).sort(function(e, u) {
- var n = f(e.value, u.value);
- return 0 !== n ? n : e.index - u.index //似乎就是加多了这一步:若差距为0,按照原顺序
- }).map(function(e) {
- return e.value
- })
- },
-
- average: function (e, t) {
- if (0 === e.length)
- return null;
- for (var i = 0, n = 0, r = 0; r < e.length; r++) {
- var o = t ? e[r][t] : e[r];
- i += o,
- n++;
- }
- return i / n
- },
-
-
- //---------------------------
-
-
- getMixedSet : function(arr1, arr2){//交集
- return arr1.filter(item=>arr2.includes(item));
- },
- getUnionSet : function(arr1, arr2){//并集
- return arr1.concat(arr2.filter(item=>!arr1.includes(item)))
- },
- getDifferenceSet : function(arr1, arr2){//差集 不能识别重复的,如getDifferenceSet([1,2,2],[1,1,2]) 为空
- var arr11 = arr1.filter(item=>!arr2.includes(item));
- var arr22 = arr2.filter(item=>!arr1.includes(item));
- return arr11.concat(arr22)
- },
- getDifferenceSetMuti : function(arr){//收集绝对没有重复的元素,也就是判断出现次数=1的
- var set = [];
- arr.forEach(arr1=>{
- arr1.forEach(item=>{
- var index = set.indexOf(item);
- if(index>-1){
- set.splice(index, 1);
- }else {
- set.push(item);
- }
- });
- });
- return set;
- }
- ,
-
-
- CloneJson : function(data){
- var str = JSON.stringify(data);
- return JSON.parse(str)
- }
-
- ,
-
- CloneObject : function(copyObj, result, isSimpleCopy, simpleCopyList=[]) {
- //isSimpleCopy 只复制最外层
- //复制json result的可能:普通数字或字符串、普通数组、复杂对象
-
- simpleCopyList.push(Object3D); //遇到simpleCopyList中的类直接使用不拷贝
-
- if(!copyObj || typeof copyObj == 'number' || typeof copyObj == 'string' || copyObj instanceof Function || simpleCopyList.some(className => copyObj instanceof className)){
- return copyObj
- }
-
- result = result || {};
- if (copyObj instanceof Array) {
- return copyObj.map(e=>{
- return this.CloneObject(e)
- })
- }else {
- if(copyObj.clone instanceof Function ){ //解决一部分
- return copyObj.clone()
- }
- }
- for (var key in copyObj) {
- if (copyObj[key] instanceof Object && !isSimpleCopy)
- result[key] = this.CloneObject(copyObj[key]);
- else
- result[key] = copyObj[key];
- //如果是函数类同基本数据,即复制引用
- }
- return result;
- }
- ,
- CloneClassObject :function(copyObj ){//复杂类对象
- var newobj = new copyObj.constructor();
- this.CopyClassObject(newobj, copyObj);
-
- return newobj
- }
-
- ,
-
- CopyClassObject :function(targetObj, copyObj){//复杂类对象
- for(let i in copyObj){
- if(i in copyObj.__proto__)break; //到函数了跳出
-
- targetObj[i] = this.CloneObject(copyObj[i], null );
-
-
-
- /* else if(copyObj[i].clone instanceof Function ){
- targetObj[i] = copyObj[i].clone()
- }else{
- targetObj[i] = copyObj[i];
- } */
- }
- }
- ,
-
- ifSame : function(object1, object2){
- if(object1 == object2 )return true // 0 != undefined , 0 == ''
- else if(!object1 || !object2) return false
- else if(object1.constructor != object2.constructor){
- return false
- }else if(object1 instanceof Array ) {
- if(object1.length != object2.length)return false;
- var _object2 = object2.slice(0);
-
- for(let i=0;i<object1.length;i++){
- var u = _object2.find(e=>ifSame(object1[i], e));
- if(u == void 0 && !_object2.includes(u) && !object1.includes(u))return false;
- else {
- let index = _object2.indexOf(u);
- _object2.splice(index,1);
- }
- }
-
- return true
- }else if(object1.equals instanceof Function ){//复杂数据仅支持这种,其他的可能卡住?
-
- return object1.equals(object2)
-
- }else if(typeof object1 == 'number' || typeof object1 == 'string'){
- if(isNaN(object1) && isNaN(object2))return true
- else return object1 == object2
-
- }else if(typeof object1 == "object"){
- var keys1 = Object.keys(object1);
- var keys2 = Object.keys(object2);
- if(!ifSame(keys1,keys2))return false;
-
- for(let i in object1){
- var same = ifSame(object1[i], object2[i]);
- if(!same)return false
- }
- return true
- }else {
- console.log('isSame出现例外');
- }
-
- }
- ,
- replaceAll : function (str, f, e) {
- //f全部替换成e
- var reg = new RegExp(f, "g"); //创建正则RegExp对象
- return str.replace(reg, e);
- }
- ,
- downloadFile : function(data, filename, cb) {
- var save_link = document.createElementNS('http://www.w3.org/1999/xhtml', 'a');
- save_link.href = data;
- save_link.download = filename;
- var event = document.createEvent('MouseEvents');
- event.initMouseEvent('click', true, false, window, 0, 0, 0, 0, 0, false, false, false, false, 0, null);
- save_link.dispatchEvent(event);
- cb && cb();
- },
-
- intervalTool:{ //延时update,防止卡顿
- list:[],
-
- isWaiting:function(name, func, delayTime){
- if(!this.list.includes(name)){ //如果没有该项, 则开始判断
- var needWait = func(); //触发了改变,则等待一段时间后再自动判断
- if(needWait){
- this.list.push(name);
- setTimeout(()=>{
- var a = this.list.indexOf(name);
- this.list.splice(a,1);
- this.isWaiting(name, func, delayTime); //循环
- },delayTime);
- }
- }
- },
- /* wait:function(name, delayTime){
- this.list.push(name);
- setTimeout(()=>{
-
- },delayTime)
- }, */
- }
- ,
- pushToGroupAuto : function(items, groups, recognizeFunction){//自动分组。 items是将分到一起的组合。items.length = 1 or 2.
-
- recognizeFunction = recognizeFunction || function(){};
-
- var atGroups = groups.filter(group=>group.find(
- item => items[0] == item || recognizeFunction(item, items[0]) || items[1] == item || items[1] && recognizeFunction(item, items[1])
-
- ));
- if(atGroups.length){//在不同组
- //因为items是一组的,所以先都放入组1
- items.forEach(item=> {if(!atGroups[0].includes(item)) atGroups[0].push(item);});
-
- if(atGroups.length>1){//如果在不同组,说明这两个组需要合并
- var combineGroup = [];
- atGroups.forEach(group=>{
- combineGroup = Common.getUnionSet(combineGroup, group);
- groups.splice(groups.indexOf(group),1);
-
- });
- groups.push(combineGroup);
-
- }
- }else {//直接加入为一组
- groups.push(items);
- }
- },
-
- addOrRemoveDefine(material, defineName, type, value=''){
- let defines = material.defines;
- if(type == 'add'){
- if(defines[defineName] != void 0 && defines[defineName] == value)return
- defines[defineName] = value;
- }else {
- if(defines[defineName] != void 0)return;
- delete defines[defineName];
- }
- material.needsUpdate = true;
- },
-
- makeTexDontResize(map){//避免贴图因非2的次方而缩小。小心使用
- if(!map || map.image && MathUtils.isPowerOfTwo(map.image.width ) && MathUtils.isPowerOfTwo(map.image.height ))return
- map.wrapS = map.wrapT = ClampToEdgeWrapping; //原默认 RepeatWrapping
- map.minFilter = LinearFilter; // or THREE.NearestFilter 原默认 LinearMipmapLinearFilter
- }
-
- };
- const planeGeo = new PlaneBufferGeometry(1,1);
-
- const planeMats$1 = new Map();
- let getPlaneMat = (group, removed)=>{
- let mat = planeMats$1.get(group);
- if(mat)return mat
-
-
- var planeMat = new MeshBasicMaterial({
- //color: level == 'removed' ? "#f00" : new THREE.Color(1 - level*0.2, 1,1),
- color: (new Color()).setHSL(Math.random(), 0.5, 0.9) ,
- //color: (new THREE.Color()).setHSL(removed ? 0 : 0.6, Math.random(), 0.9/* Math.random() */) ,
-
- transparent:true,
- side:2,
- opacity: removed ? 0.3 : 0.9,
- });
- planeMats$1.set(group, planeMat);
- return planeMat
- };
- class PointCloudOctreeNode extends PointCloudTreeNode {
- constructor () {
- super();
- //this.children = {};
- this.children = [];
- this.sceneNode = null;
- this.octree = null;
-
-
-
-
- }
-
- getNumPoints () {
- return this.geometryNode.numPoints;
- }
- isLoaded () {
- return true;
- }
- isTreeNode () {
- return true;
- }
- isGeometryNode () {
- return false;
- }
- getLevel () {
- return this.geometryNode.level;
- }
- getBoundingSphere () {
- return this.geometryNode.boundingSphere;
- }
- getBoundingBox () {
- return this.geometryNode.boundingBox;
- }
- getChildren () {
- let children = [];
- for (let i = 0; i < 8; i++) {
- if (this.children[i]) {
- children.push(this.children[i]);
- }
- }
- return children;
- }
- getPointsInBox(boxNode){
- if(!this.sceneNode){
- return null;
- }
- let buffer = this.geometryNode.buffer;
- let posOffset = buffer.offset("position");
- let stride = buffer.stride;
- let view = new DataView(buffer.data);
- let worldToBox = boxNode.matrixWorld.clone().invert();
- let objectToBox = new Matrix4().multiplyMatrices(worldToBox, this.sceneNode.matrixWorld);
- let inBox = [];
- let pos = new Vector4();
- for(let i = 0; i < buffer.numElements; i++){
- let x = view.getFloat32(i * stride + posOffset + 0, true);
- let y = view.getFloat32(i * stride + posOffset + 4, true);
- let z = view.getFloat32(i * stride + posOffset + 8, true);
- pos.set(x, y, z, 1);
- pos.applyMatrix4(objectToBox);
- if(-0.5 < pos.x && pos.x < 0.5){
- if(-0.5 < pos.y && pos.y < 0.5){
- if(-0.5 < pos.z && pos.z < 0.5){
- pos.set(x, y, z, 1).applyMatrix4(this.sceneNode.matrixWorld);
- inBox.push(new Vector3(pos.x, pos.y, pos.z));
- }
- }
- }
- }
- return inBox;
- }
- get name () {
- return this.geometryNode.name;
- }
- };
- class PointCloudOctree extends PointCloudTree {
- constructor (geometry, material) {
- super();
-
- //this.pointBudget = Infinity;
- this.pcoGeometry = geometry;
- this.boundingBox = this.pcoGeometry.tightBoundingBox;//this.pcoGeometry.boundingBox; //boundingBox是正方体,所以换掉
- this.boundingSphere = this.boundingBox.getBoundingSphere(new Sphere());
- this.material = material || new PointCloudMaterial$1();
- this.visiblePointsTarget = 2 * 1000 * 1000;
- this.minimumNodePixelSize = 150;
- this.level = 0;
- this.position.copy(geometry.offset);
- this.updateMatrix();
- this.nodeMaxLevel = 0;//add
- this.maxLevel = Infinity;
- this.temp = { sizeFitToLevel:{}, opacity:{}};//add
- //add
-
- this.panos = [];
- this.matrixAutoUpdate = false; //最好禁止updateMatrix 直接使用matrixWorld
- this.orientationUser = 0;
- this.translateUser = new Vector3;
-
- this.rotateMatrix = new Matrix4;
- this.transformMatrix = new Matrix4;// 数据集的变化矩阵
- this.transformInvMatrix = new Matrix4;
- this.rotateInvMatrix = new Matrix4;
-
-
- this.nodeMaxLevelPredict = this.predictNodeMaxLevel();//预测maxNodeLevel
- this.testMaxNodeCount = this.testMaxNodeCount2 = 0;
-
- this.material.spacing = this.pcoGeometry.spacing;//初始化一下 以便于设置pointsize
-
-
-
- {
- let priorityQueue = ["rgba", "rgb", "intensity", "classification"];
- let selected = "rgba";
- for(let attributeName of priorityQueue){
- let attribute = this.pcoGeometry.pointAttributes.attributes.find(a => a.name === attributeName);
- if(!attribute){
- continue;
- }
- let min = attribute.range[0].constructor.name === "Array" ? attribute.range[0] : [attribute.range[0]];
- let max = attribute.range[1].constructor.name === "Array" ? attribute.range[1] : [attribute.range[1]];
- let range_min = new Vector3(...min);
- let range_max = new Vector3(...max);
- let range = range_min.distanceTo(range_max);
- if(range === 0){
- continue;
- }
- selected = attributeName;
- break;
- }
- this.material.activeAttributeName = selected;
- }
- this.showBoundingBox = false;
- this.boundingBoxNodes = [];
- this.loadQueue = [];
- this.visibleBounds = new Box3();
- this.visibleNodes = [];
- this.visibleGeometry = [];
- this.generateDEM = false;
- this.profileRequests = [];
- this.name = '';
- this._visible = true;
- //this._isVisible = true//add
- //this.unvisibleReasons = []
-
- {
- let box = [this.pcoGeometry.tightBoundingBox, this.getBoundingBoxWorld()]
- .find(v => v !== undefined);
- this.updateMatrixWorld(true);
- box = Utils.computeTransformedBoundingBox(box, this.matrixWorld);
- let bMin = box.min.z;
- let bMax = box.max.z;
- this.material.heightMin = bMin;
- this.material.heightMax = bMax;
- }
- // TODO read projection from file instead
- this.projection = geometry.projection;
- this.fallbackProjection = geometry.fallbackProjection;
- this.root = this.pcoGeometry.root;
-
-
-
- this.pcoGeometry.addEventListener('updateNodeMaxLevel', this.updateNodeMaxLevel.bind(this));
- this.isPointcloud = true; //add
- }
-
- /*
-
- 注释:node的level从最大的box 0开始。
- 且加载任意一个node必定也会加载它的所有祖先。(如visibleNodes中有一个level为4,则一定有3,2,1,0)
- visibleNodes就是所有可见的node,比如:
- 如果相机在0这个位置朝下,这时候的visibleNodes中只有一个level为0的node;
- 而如果朝上看,上方的几个node如果在视野中占据足够大的位置的话,就会加载。
- 如果相机在2这个位置朝上,这时候的visibleNodes中所包含的level为: 0,1,2
-
- ________________
- | | | |
- |__2| | |
- | 1 | 1 |
- |_______|_______|
- | |
- | |
- | 0 |
- |_______________|
-
- 查看box可在potree中开启
- */
-
-
-
-
-
- updateNodeMaxLevel(e){//目前点云包含node的最高level
- var level = Math.max(e.level, this.nodeMaxLevel);
- if(level != this.nodeMaxLevel){
- this.nodeMaxLevel = level;
- //viewer.dispatchEvent({type:'updateNodeMaxLevel', pointcloud: this, nodeMaxLevel:level})
-
- console.log('updateNodeMaxLevel ' + this.dataset_id + " : "+ this.nodeMaxLevel);
-
- this.setPointLevel();//重新计算
-
- if(!Potree.settings.sizeFitToLevel){
- this.changePointSize();
- }
- }
- }//注:在没有加载到真实的 nodeMaxLevel之前,点云会显示得偏大
-
-
-
- //panoEdit时比预测值小很多?
-
- testMaxNodeLevel(){//手动使maxLevel达到最高,从而迫使updateNodeMaxLevel。 因为Potree.settings.pointDensity 不为 'high'时,maxLevel不是所加载的最高,就很容易加载不出下一个层级,导致无法知道nodeMaxLevel
- if(this.testMaxNodeLevelDone ) return
- //if(this.nodeMaxLevel > this.nodeMaxLevelPredict.min )return
-
-
- if( this.nodeMaxLevel==0 )return true
- if( !viewer.atDatasets.includes(this))return true //否则老远就count++
-
- let levels = this.visibleNodes.map(e=>e.getLevel());
- let actMaxLevel = Math.max.apply(null, levels); //实际加载到的最高的node level
- if(actMaxLevel < this.maxLevel)return true// 还没加载到能加载到的最高。 但在细节设置较低时,排除作用微弱。
-
-
- //尝试加载出更高级的level
- let old = this.maxLevel;
- this.maxLevel = 12;
- //var visibleNodes1 = this.visibleNodes.map(e=>e.getLevel())
- //console.log('visibleNodes1',visibleNodes1)
- Potree.updatePointClouds([this], viewer.scene.getActiveCamera(), viewer.mainViewport.resolution );
- //不在camera可视范围内还是加载不出来。即使临时修改位置
-
-
- var visibleNodes2 = this.visibleNodes.map(e=>e.getLevel());
- //console.log('visibleNodes2',visibleNodes2)
- this.maxLevel = old;
-
-
-
- this.testMaxNodeCount ++;
- if(this.testMaxNodeCount > 500){
- console.log('testMaxNodeLevel次数超出,强制结束:',this.dataset_id, this.nodeMaxLevel, this.nodeMaxLevelPredict.min);
- this.testMaxNodeLevelDone = 'moreThanMaxCount';
- return; //在可以看见点云的情况下,超时,有可能是预测的max是错的
- }
- if(this.nodeMaxLevel < this.nodeMaxLevelPredict.min) return true //仍需要继续testMaxNodeLevel
-
- this.testMaxNodeCount2 ++; // 已经> this.nodeMaxLevelPredict.min 后,开始计数。因为min可能低于真实nodeMaxLevel所以要再试几次
-
- /* if(this.name == 'SS-t-CWmVgzP4XU'){
- console.log('SS-t-CWmVgzP4XU count++')
- } */
-
- if(this.testMaxNodeCount2 < 50) return true //再试几次 ( 主要是细节调得低时需要多测几次才加载到
- this.testMaxNodeLevelDone = true;
-
-
-
-
-
- }
-
-
-
-
-
-
- setPointLevel(){
-
- var pointDensity = Potree.settings.pointDensity;
- var config = Potree.config.pointDensity[pointDensity];
- if(!config)return
-
-
- /* if(this.testingMaxLevel){
- this.maxLevel = 12;//先加载到最大的直到测试完毕。由于5个level为一组来加载,所以如果写4最高能加载到5,如果写5最高能加载到下一个级别的最高也就是10
- //console.log('maxLevel: '+e.maxLevel + ' testingMaxLevel中 ' )
- }else{ */
- let percent = config.percentByUser && Potree.settings.UserDensityPercent != void 0 ? Potree.settings.UserDensityPercent : config.maxLevelPercent;
- this.maxLevel = Math.round( percent * this.nodeMaxLevel);
- //console.log('maxLevel: '+e.maxLevel + ', density : '+Potree.settings.pointDensity, ", percent :"+percent);
-
- if(Potree.settings.sizeFitToLevel){
- this.changePointSize();
- }
- this.changePointOpacity();
- //}
- }
-
- //预测可能的nodeMaxLevel:
-
- predictNodeMaxLevel(){//预测maxNodeLevel。 可能只适用于我们相机拍的点云
- let spacing = {min:0.005, max:0.014};//最小节的两点间的距离 ,获得方法:spacing / Math.pow(2, nodeMaxLevel)。 目前观测的我们自己拍的这个数值的范围大概是这样
- let min = Math.log2(this.material.spacing / spacing.max); //有见过最大是0.01368
- let max = Math.log2(this.material.spacing / spacing.min); //大部分是 0.006
- //console.log('predictNodeMaxLevel:', this.name , min, max )
-
-
- return {min, max}
- }
-
- getHighestNodeSpacing(){
- return this.material.spacing / Math.pow(2, this.nodeMaxLevel) //前提是这个nodeMaxLevel是准确的
- }
-
- setName (name) {
- if (this.name !== name) {
- this.name = name;
- this.dispatchEvent({type: 'name_changed', name: name, pointcloud: this});
- }
- }
- getName () {
- return this.name;
- }
- getAttribute(name){
- const attribute = this.pcoGeometry.pointAttributes.attributes.find(a => a.name === name);
- if(attribute){
- return attribute;
- }else {
- return null;
- }
- }
- getAttributes(){
- return this.pcoGeometry.pointAttributes;
- }
- toTreeNode (geometryNode, parent) {
- let node = new PointCloudOctreeNode();
- // if(geometryNode.name === "r40206"){
- // console.log("creating node for r40206");
- // }
- let sceneNode = new Points(geometryNode.geometry, this.material); //好像没什么用
- sceneNode.name = geometryNode.name;
- sceneNode.position.copy(geometryNode.boundingBox.min);
- sceneNode.frustumCulled = false;
- sceneNode.onBeforeRender = (_this, scene, camera, geometry, material, group) => {
- if (material.program) {
- _this.getContext().useProgram(material.program.program);
- if (material.program.getUniforms().map.level) {
- let level = geometryNode.getLevel();
- material.uniforms.level.value = level;
- material.program.getUniforms().map.level.setValue(_this.getContext(), level);
- }
- if (this.visibleNodeTextureOffsets && material.program.getUniforms().map.vnStart) {
- let vnStart = this.visibleNodeTextureOffsets.get(node);
- material.uniforms.vnStart.value = vnStart;
- material.program.getUniforms().map.vnStart.setValue(_this.getContext(), vnStart);
- }
- if (material.program.getUniforms().map.pcIndex) {
- let i = node.pcIndex ? node.pcIndex : this.visibleNodes.indexOf(node);
- material.uniforms.pcIndex.value = i;
- material.program.getUniforms().map.pcIndex.setValue(_this.getContext(), i);
- }
- }
- };
- // { // DEBUG
- // let sg = new THREE.SphereGeometry(1, 16, 16);
- // let sm = new THREE.MeshNormalMaterial();
- // let s = new THREE.Mesh(sg, sm);
- // s.scale.set(5, 5, 5);
- // s.position.copy(geometryNode.mean)
- // .add(this.position)
- // .add(geometryNode.boundingBox.min);
- //
- // viewer.scene.scene.add(s);
- // }
- node.geometryNode = geometryNode;
- node.sceneNode = sceneNode;
- node.pointcloud = this;
- node.children = [];
- //for (let key in geometryNode.children) {
- // node.children[key] = geometryNode.children[key];
- //}
- for(let i = 0; i < 8; i++){
- node.children[i] = geometryNode.children[i];
- }
- if (!parent) {
- this.root = node;
- this.add(sceneNode);
- } else {
- let childIndex = parseInt(geometryNode.name[geometryNode.name.length - 1]);
- parent.sceneNode.add(sceneNode);
- parent.children[childIndex] = node;
- }
- let disposeListener = function () {
- let childIndex = parseInt(geometryNode.name[geometryNode.name.length - 1]);
- parent.sceneNode.remove(node.sceneNode);
- parent.children[childIndex] = geometryNode;
- };
- geometryNode.oneTimeDisposeHandlers.push(disposeListener);
-
-
- //this.buildTexMesh(geometryNode,sceneNode)
- return node;
- }
-
-
-
-
-
- buildTexMesh11(geometryNode,sceneNode){
- // viewer.scene.pointclouds.forEach(a=>a.areaPlanes.forEach(e=>e.material = viewer.images360.cube.material))
- //还是无法避免不在同一个平面的点被识别到同一个面上,然后法向都算错了。 另外还有就是破面太多。虽然能算出地板也不错。或者只去算水平面和垂直面。
- let startTime = Date.now();
-
-
- if(!this.splitSprites){
- let splitSprites = new Object3D;
- splitSprites.name = 'splitSprites_'+this.name;
- splitSprites.matrixAutoUpdate = false; //同pointcloud一样不自动更新,直接使用
- splitSprites.matrix.copy(this.matrix);
- splitSprites.matrixWorld.copy(this.matrixWorld);
- this.splitSprites = splitSprites;
- viewer.scene.scene.add(splitSprites);
-
-
- this.areaPlanes = [];
- }
-
-
- if(this.texMeshUseLevel == void 0 || geometryNode.level == this.texMeshUseLevel){//只需要某一层level的点
-
-
- let showPointLabel = true, showCenterLabel = false;
-
-
-
- //let spritesGeo = new THREE.BufferGeometry();
-
- let scaleRatio = 1.4; //稍微放大些,填满缝隙
- let spriteWidth1 = this.material.spacing / Math.pow(2, geometryNode.level);
- if(spriteWidth1 > 3)return
- let spriteWidth = spriteWidth1 * scaleRatio;
-
- if(this.texMeshUseLevel == void 0 ){
- this.texMeshUseLevel = geometryNode.level;
- console.log('texMeshUseLevel ',geometryNode.level);
- }
-
- let geometry = geometryNode.geometry;
- let count = geometry.attributes.position.count;
-
- let end = Math.min(count, 6000);
- let start = Math.min(5000, end);
- console.warn('check points count:', end-start);
-
-
- let position = geometry.attributes.position.array;
- let normal = geometry.attributes.normal.array;
- let i;
- let up = new Vector3(0,1,0) , zeroVec = new Vector3; //up写成z向上居然结果一样
-
- let positions = [];
- let normals = [];
- let newPositions = [];
- let newIndices = [];
- let groupMaxPointCount = 1000 ;//太多找环会崩
-
- let minDisSquare = Math.pow(spriteWidth1 * 1.8 , 2 ); //这个数太小就连不上啦 1.65太小
- let minDot = Math.cos(MathUtils.degToRad(5));//括号内是最小偏差角度, 20太大。太大的话会将立方体的相邻两面视为一个面。
-
- //根据相邻点位置和角度是否相近来分组。有风险:两大区域可能因为一个模棱两可中间点连接在一起。
-
- let closeGroups = [];
- let groupTolerateMaxPoint = 6;//组内点<=这个数的最后会被删除
- let removedCount = 0;
- let useGroupCount = 0;
-
- let splitSprites = new Object3D;
- splitSprites.name = 'sub_splitSprites_'+geometryNode.name;
- splitSprites.position.copy(sceneNode.position);
- splitSprites.rotation.copy(sceneNode.rotation);
- this.splitSprites.add(splitSprites);
-
- for(i=start;i<end;i++){
- let pos = new Vector3(position[3*i], position[3*i+1], position[3*i+2] );
- let nor = new Vector3(normal[3*i],normal[3*i+1],normal[3*i+2]);
- pos.nor = nor;
- pos.index = i;
- positions.push(pos);
- normals.push(nor);
-
-
- let groups = closeGroups.filter(group=>{
- if(group.length>groupMaxPointCount)return //满员了
- var hasClosed = group.some(p=>{
- var dis = p.distanceToSquared(pos);
- var dot = p.nor.dot(nor);
- if(dis<minDisSquare && dot>minDot){
- //return dis / minDisSquare + (Math.abs(p.nor.x - nor.x) + Math.abs(p.nor.y - nor.y) + Math.abs(p.nor.z - nor.z)) < 1.7
- return dis / minDisSquare - p.nor.dot(nor) < 0
- }
- });
- return hasClosed
- });
-
- if(groups.length == 0){//创建一个新的
- var newGroup = [];
- closeGroups.push(newGroup);
- groups = [newGroup];
- }
-
- if(groups.length == 1){//直接加入原有的
- pos.belongTo = groups[0];
- }else if(groups.length>1){ // comebine多个组成一个
- let newBigGroup = [];
- groups.forEach(e=>{
- newBigGroup.push(...e);
- let index = closeGroups.indexOf(e);
- closeGroups.splice(index, 1);
- });
- closeGroups.push(newBigGroup);
- pos.belongTo = newBigGroup;
- newBigGroup.forEach(e=>{e.belongTo = newBigGroup;});
- }
- pos.belongTo.push(pos);
-
- }
-
-
-
- closeGroups.forEach((points,index)=>{//建造面
- if(points.length <= groupTolerateMaxPoint ){
-
-
- /* let sprite = new THREE.Mesh(planeGeo, getPlaneMat(pos.belongTo, true))
- sprite.lookAt(nor);
- sprite.position.copy(pos)
- sprite.scale.set(spriteWidth,spriteWidth,spriteWidth)
- sprite.name = geometryNode.name+'_index'+i
- splitSprites.add(sprite)
- removedCount ++; */
-
-
- removedCount += points.length;
- return
- }
- useGroupCount ++;
-
- points.sort(function(a,b){
- return a.index - b.index
- });
-
- console.log(`开始解析 ${geometryNode.name} - 第${index}组,总第${points[0].index}个点,组内有${points.length}个点`);
-
- let planeMat = getPlaneMat(points, true);
-
-
- if(showPointLabel){
- points.forEach((p,i)=>{
- var label = new TextSprite({
- text : index+"-"+i+" ("+p.index+")"+geometryNode.name, dontFixOrient:true,
- backgroundColor: {r: planeMat.color.r*255, g: planeMat.color.g*255, b: planeMat.color.b*255, a:0.6},
- });
- label.lookAt(p.nor);
- label.position.copy(p);
- label.scale.set(spriteWidth/3, spriteWidth/3, spriteWidth/3);
- splitSprites.add(label);
-
- /* let sprite = new THREE.Mesh(planeGeo,planeMat)
- sprite.lookAt(p.nor);
- sprite.position.copy(p)
- sprite.scale.set(spriteWidth/3,spriteWidth/3,spriteWidth/3)
- sprite.name = geometryNode.name+'_group'+index+"_"+ i
- splitSprites.add(sprite) */
- });
-
- }
-
-
-
- var avePos = points.reduce(function(total, currentValue){
- return total.add(currentValue)
- }, new Vector3).multiplyScalar(1/points.length);
-
-
-
- let planeNormals = [];
-
-
-
- {//获得planeNormals
- //随机找两个距离远的点算normal。 按距离排序后, //抽取若干个点,然后算两两之间的法线,其中距离远的多抽取几个。
- var sortPoints = points.slice(0).sort(function(a,b){
- return a.distanceToSquared(avePos) - b.distanceToSquared(avePos)
- });//从小到大
- var pickPoints;
-
- var length = sortPoints.length;
- if(length>=7){
- var ratio = [0.02,0.15,0.4,0.55,0.7,0.86,0.99];
- var index = ratio.map(e=>Math.round(e * (length-1)));
- //console.log('index ',index)
- pickPoints = index.map(e=>sortPoints[e]);
- }else {
- pickPoints = sortPoints;
- }
- //console.log('pickPoints', pickPoints)
- let num = pickPoints.length;
-
- for(let i=0;i<num;i++){//任意一个三角形能算出一个normal
- for(let j=i+1;j<num;j++){
- for(let u=j+1;u<num;u++){
- var p1 = pickPoints[i];
- var p2 = pickPoints[j];
- var p3 = pickPoints[u];
-
- let vec1 = new Vector3().subVectors(p1,p3);
- let vec2 = new Vector3().subVectors(p2,p3);
- let nor = vec1.cross(vec2).normalize();
-
- if(planeNormals[0]){
- if(nor.dot(planeNormals[0])<0)nor.negate(); //反向下
- }
- console.log('nor',nor);
- planeNormals.push(nor);
-
- }
- }
- }
-
- }
-
-
-
-
- var aveNor = planeNormals.reduce(function(total, currentValue){
- return total.add(currentValue)
- }, new Vector3).normalize();
-
- console.log('aveNor',aveNor, 'avePos' ,avePos, index);
-
-
- if(showCenterLabel){
- var label = new TextSprite({
- //index+"-"+i+" ("+p.index+")"+geometryNode.name
-
- text : `我是${index}组 ${geometryNode.name} 中心点`, dontFixOrient:true,
- backgroundColor: {r: planeMat.color.r*255, g: planeMat.color.g*255, b: planeMat.color.b*255, a:0.6},
- });
- label.lookAt(aveNor);
- label.position.copy(avePos);
- label.scale.set(spriteWidth, spriteWidth, spriteWidth);
- splitSprites.add(label);
- }
-
- var facePlane = new Plane().setFromNormalAndCoplanarPoint(aveNor, avePos );
-
- var coplanarPoints = points.map(p=> facePlane.projectPoint(p, new Vector3() ));
-
-
-
- var originPoint0 = coplanarPoints[0].clone();
- var qua = math.getQuaBetween2Vector(facePlane.normal, new Vector3(0,0,1), new Vector3(0,0,1));
- let points2d = coplanarPoints.map(e=>e.clone().applyQuaternion(qua));
- let quaInverse = qua.clone().invert();
-
- //--------------------
-
- let lines = [];
-
- points2d.forEach((p,j)=>{
- p.id = j;
- for(let i=0;i<j;i++){
- if(p.distanceToSquared(points2d[i])<minDisSquare*1.5){
- lines.push({p1:i,p2:j});
- }
- }
- });
-
- console.log('points count:',points2d.length, 'lines:',lines);
- var rings = searchRings({
- points:points2d,
- lines,
- onlyGetOutRing:true,
- precision: Math.max(spriteWidth1/10, 0.01)
- });
- console.log( 'rings:', rings );//mesh间可能重叠 但换上贴图材质应该看不出(但只要searchRings时getSliceLines就不会重叠)
- if(!rings)return
- let planeMat2 = planeMat.clone(); planeMat2.opacity = 0.5;
- var firstPos = points2d[0].clone();
- firstPos.z = 0; //因为shape只读取了xy,所以位移下, 再算出最终位置,得到差距
- firstPos.applyQuaternion(quaInverse);
- var vec = originPoint0.clone().sub(firstPos);
-
- rings.forEach(ring=>{
- var shapeGeo = MeshDraw.getShapeGeo(ring.points);
- var areaPlane = new Mesh(shapeGeo, planeMat2);
-
-
- areaPlane.quaternion.copy(quaInverse);
- areaPlane.position.copy(vec);
- areaPlane.name = 'areaPlane_'+index;
- splitSprites.add(areaPlane);
- this.areaPlanes.push(areaPlane);
- });
-
-
-
- });
- console.log(geometryNode.name, '中:');
- console.log('removed point count: ', removedCount/* splitSprites.children.length */);
- console.log(closeGroups);
- console.log('comebine mesh Len:', useGroupCount);
-
-
- /*
- spritesGeo.setAttribute('position', new THREE.Float32BufferAttribute(new Float32Array(newPositions), 3));
- spritesGeo.setIndex( newIndices );
-
-
-
- let sprites = new THREE.Mesh(spritesGeo, getPlaneMat('use'))
- sprites.name = geometryNode.name
- sprites.position.copy(sceneNode.position)
- sprites.rotation.copy(sceneNode.rotation)
-
- sceneNode.sprites = sprites
- sprites.pointsNode = sceneNode
-
- if(geometryNode.level == 0){
- let root = new THREE.Object3D;
- root.name = 'spriteNodeRoot'
-
-
- root.matrixAutoUpdate = false //同pointcloud一样不自动更新,直接使用
- root.matrix.copy(this.matrix)
- root.matrixWorld.copy(this.matrixWorld)
- viewer.scene.scene.add(root)
- this.spriteNodeRoot = root
- }
- this.spriteNodeRoot.add(sprites) */
-
- console.log('computeTime: ' + (Date.now() - startTime));
- }
-
-
-
-
-
-
-
-
- //------------------
- /*
- viewer.scene.pointclouds[0].spriteNodeRoot.traverse(e=>e.material && (e.material = viewer.images360.cube.material))
- viewer.scene.scene.children[12].visible = false
-
- */
- }
-
- buildTexMesh(geometryNode,sceneNode){
-
- if(geometryNode.level <= 0){
- let startTime = Date.now();
- let splitSprites = new Object3D;
- splitSprites.name = 'splitSprites_'+geometryNode.name;
- splitSprites.matrixAutoUpdate = false; //同pointcloud一样不自动更新,直接使用
- splitSprites.matrix.copy(this.matrix);
- splitSprites.matrixWorld.copy(this.matrixWorld);
- viewer.scene.scene.add(splitSprites);
-
-
- let spritesGeo = new BufferGeometry();
-
- let scaleRatio = 1.4; //稍微放大些,填满缝隙
- let spriteWidth1 = this.material.spacing / Math.pow(2, geometryNode.level);
- let spriteWidth = spriteWidth1 * scaleRatio;
- console.log('spriteWidth:',spriteWidth);
-
-
-
- const removeChip = false;
-
-
- let geometry = geometryNode.geometry;
- let count = geometry.attributes.position.count;
-
- //count = 4000
-
-
- let position = geometry.attributes.position.array;
- let normal = geometry.attributes.normal.array;
- let i;
- let up = new Vector3(0,1,0) , zeroVec = new Vector3; //up写成z向上居然结果一样
-
- let positions = [];
- let normals = [];
- let newPositions = [];
- //let newNormals = [];
- let newIndices = [];
- let cornerPoints = [new Vector3(-1,1,0),new Vector3(1,1,0),new Vector3(-1,-1,0),new Vector3(1,-1,0) ];
- let indices = [0, 2, 1, 2, 3, 1];
- cornerPoints.forEach(e=>e.multiplyScalar(spriteWidth/2));
-
-
-
- let minDisSquare = spriteWidth1 * spriteWidth1 * 1.5;
-
-
- let closeGroups = [];
- let groupTolerateMaxPoint = 4;//组内点<=这个数的最后会被删除
- let removedCount = 0;
-
-
- for(i=0;i<count;i++){
- let pos = new Vector3(position[3*i], position[3*i+1], position[3*i+2] );
- let nor = new Vector3(normal[3*i],normal[3*i+1],normal[3*i+2]);
- pos.nor = nor;
- positions.push(pos);
- normals.push(nor);
-
- if(removeChip){
- let groups = closeGroups.filter(group=>{
- var hasClosed = group.some(p=>{
- var dis = p.distanceToSquared(pos);
- if(dis<minDisSquare){
- //return dis / minDisSquare + (Math.abs(p.nor.x - nor.x) + Math.abs(p.nor.y - nor.y) + Math.abs(p.nor.z - nor.z)) < 1.7
- return dis / minDisSquare - p.nor.dot(nor) < 0
-
-
- }
- });
- return hasClosed
- });
-
- if(groups.length == 0){//创建一个新的
- var newGroup = [];
- closeGroups.push(newGroup);
- groups = [newGroup];
- }
-
- if(groups.length == 1){//直接加入原有的
- pos.belongTo = groups[0];
- }else if(groups.length>1){ // comebine多个组成一个
- let newBigGroup = [];
- groups.forEach(e=>{
- newBigGroup.push(...e);
- let index = closeGroups.indexOf(e);
- closeGroups.splice(index, 1);
- });
- closeGroups.push(newBigGroup);
- pos.belongTo = newBigGroup;
- newBigGroup.forEach(e=>{e.belongTo = newBigGroup;});
- }
- pos.belongTo.push(pos);
- }
-
-
-
- }
-
- for(i=0;i<count;i++){
- let pos = positions[i];
- let nor = normals[i];
- if(Math.abs(nor.z)>0.2)continue
-
- if(removeChip){
- if(pos.belongTo.length <= groupTolerateMaxPoint){
-
- let sprite = new Mesh(planeGeo, getPlaneMat(pos.belongTo, true));
- sprite.lookAt(nor);
- sprite.position.copy(pos);
- sprite.scale.set(spriteWidth,spriteWidth,spriteWidth);
- sprite.name = geometryNode.name+'_index'+i;
- splitSprites.add(sprite);
- removedCount ++;
-
- continue
- }else {
- /*
- let sprite = new THREE.Mesh(planeGeo, getPlaneMat(pos.belongTo))
- sprite.lookAt(nor);
- sprite.position.copy(pos)
- sprite.scale.set(spriteWidth/3,spriteWidth/3,spriteWidth/3)
- sprite.name = geometryNode.name+'_index'+i
- splitSprites.add(sprite)
- */
-
- }
- }
-
-
-
- let matrix = (new Matrix4).lookAt( nor, zeroVec, up);
- matrix.elements[12] = pos.x;
- matrix.elements[13] = pos.y;
- matrix.elements[14] = pos.z;
-
-
- cornerPoints.forEach(p=>{
- let point = p.clone();
- point.applyMatrix4(matrix);
-
- newPositions.push(...point.toArray());
- //newNormals
- });
-
- indices.forEach(index=>{
- newIndices.push(index + i*4);
- });
-
-
-
- }
-
- /* closeGroups.forEach(e=>{
- if(e.length <= groupTolerateMaxPoint )return
-
-
-
- }) */
-
- console.log('removed count: ', removedCount/* splitSprites.children.length */);
- console.log(closeGroups);
- console.log('computeTime: ' + (Date.now() - startTime));
-
-
-
- spritesGeo.setAttribute('position', new Float32BufferAttribute(new Float32Array(newPositions), 3));
- spritesGeo.setIndex( newIndices );
-
-
-
- let sprites = new Mesh(spritesGeo, getPlaneMat('use'));
- sprites.name = geometryNode.name;
- sprites.position.copy(sceneNode.position);
- sprites.rotation.copy(sceneNode.rotation);
-
- sceneNode.sprites = sprites;
- sprites.pointsNode = sceneNode;
-
- if(geometryNode.level == 0){
- let root = new Object3D;
- root.name = 'spriteNodeRoot';
-
-
- root.matrixAutoUpdate = false; //同pointcloud一样不自动更新,直接使用
- root.matrix.copy(this.matrix);
- root.matrixWorld.copy(this.matrixWorld);
- viewer.scene.scene.add(root);
- this.spriteNodeRoot = root;
- }
- this.spriteNodeRoot.add(sprites);
- viewer.setObjectLayers(sprites,'sceneObjects');
-
-
- }
-
-
- }
-
-
-
-
- updateVisibleBounds () {
- let leafNodes = [];
- for (let i = 0; i < this.visibleNodes.length; i++) {
- let node = this.visibleNodes[i];
- let isLeaf = true;
- for (let j = 0; j < node.children.length; j++) {
- let child = node.children[j];
- if (child instanceof PointCloudOctreeNode) {
- isLeaf = isLeaf && !child.sceneNode.visible;
- } else if (child instanceof PointCloudOctreeGeometryNode) {
- isLeaf = true;
- }
- }
- if (isLeaf) {
- leafNodes.push(node);
- }
- }
- this.visibleBounds.min = new Vector3(Infinity, Infinity, Infinity);
- this.visibleBounds.max = new Vector3(-Infinity, -Infinity, -Infinity);
- for (let i = 0; i < leafNodes.length; i++) {
- let node = leafNodes[i];
- this.visibleBounds.expandByPoint(node.getBoundingBox().min);
- this.visibleBounds.expandByPoint(node.getBoundingBox().max);
- }
- }
- updateMaterial (material, visibleNodes, camera, renderer, resolution) {
- material.fov = camera.fov * (Math.PI / 180);
- /* material.screenWidth = renderer.domElement.clientWidth;
- material.screenHeight = renderer.domElement.clientHeight; */
- material.resolution = resolution;
-
-
- //material.spacing = this.pcoGeometry.spacing; // * Math.max(this.scale.x, this.scale.y, this.scale.z); //应该不需要
- material.near = camera.near;
- material.far = camera.far;
- material.uniforms.octreeSize.value = this.pcoGeometry.boundingBox.getSize(new Vector3()).x;
- }
- computeVisibilityTextureData(nodes, camera){
- if(Potree.measureTimings) performance.mark("computeVisibilityTextureData-start");
- let data = new Uint8Array(nodes.length * 4);
- let visibleNodeTextureOffsets = new Map();
- // copy array
- nodes = nodes.slice();
- // sort by level and index, e.g. r, r0, r3, r4, r01, r07, r30, ...
- let sort = function (a, b) {
- let na = a.geometryNode.name;
- let nb = b.geometryNode.name;
- if (na.length !== nb.length) return na.length - nb.length;
- if (na < nb) return -1;
- if (na > nb) return 1;
- return 0;
- };
- nodes.sort(sort);
- let worldDir = new Vector3();
- let nodeMap = new Map();
- let offsetsToChild = new Array(nodes.length).fill(Infinity);
- for(let i = 0; i < nodes.length; i++){
- let node = nodes[i];
- nodeMap.set(node.name, node);
- visibleNodeTextureOffsets.set(node, i);
- if(i > 0){
- let index = parseInt(node.name.slice(-1));
- let parentName = node.name.slice(0, -1);
- let parent = nodeMap.get(parentName);
- let parentOffset = visibleNodeTextureOffsets.get(parent);
- let parentOffsetToChild = (i - parentOffset);
- offsetsToChild[parentOffset] = Math.min(offsetsToChild[parentOffset], parentOffsetToChild);
- data[parentOffset * 4 + 0] = data[parentOffset * 4 + 0] | (1 << index);
- data[parentOffset * 4 + 1] = (offsetsToChild[parentOffset] >> 8);
- data[parentOffset * 4 + 2] = (offsetsToChild[parentOffset] % 256);
- }
- let density = node.geometryNode.density;
-
- if(typeof density === "number"){
- let lodOffset = Math.log2(density) / 2 - 1.5;
- let offsetUint8 = (lodOffset + 10) * 10;
- data[i * 4 + 3] = offsetUint8;
- }else {
- data[i * 4 + 3] = 100;
- }
- }
- if(Potree.measureTimings){
- performance.mark("computeVisibilityTextureData-end");
- performance.measure("render.computeVisibilityTextureData", "computeVisibilityTextureData-start", "computeVisibilityTextureData-end");
- }
- return {
- data: data,
- offsets: visibleNodeTextureOffsets
- };
- }
- nodeIntersectsProfile (node, profile) {
- let bbWorld = node.boundingBox.clone().applyMatrix4(this.matrixWorld);
- let bsWorld = bbWorld.getBoundingSphere(new Sphere());
- let intersects = false;
- for (let i = 0; i < profile.points.length - 1; i++) {
- let start = new Vector3(profile.points[i + 0].x, profile.points[i + 0].y, bsWorld.center.z);
- let end = new Vector3(profile.points[i + 1].x, profile.points[i + 1].y, bsWorld.center.z);
- let closest = new Line3(start, end).closestPointToPoint(bsWorld.center, true, new Vector3());
- let distance = closest.distanceTo(bsWorld.center);
- intersects = intersects || (distance < (bsWorld.radius + profile.width));
- }
- //console.log(`${node.name}: ${intersects}`);
- return intersects;
- }
- deepestNodeAt(position){
-
- const toObjectSpace = this.matrixWorld.clone().invert();
- const objPos = position.clone().applyMatrix4(toObjectSpace);
- let current = this.root;
- while(true){
- let containingChild = null;
- for(const child of current.children){
- if(child !== undefined){
- if(child.getBoundingBox().containsPoint(objPos)){
- containingChild = child;
- }
- }
- }
- if(containingChild !== null && containingChild instanceof PointCloudOctreeNode){
- current = containingChild;
- }else {
- break;
- }
- }
- const deepest = current;
- return deepest;
- }
- nodesOnRay (nodes, ray) {
- let nodesOnRay = [];
- let _ray = ray.clone();
- for (let i = 0; i < nodes.length; i++) {
- let node = nodes[i];
- let sphere = node.getBoundingSphere().clone().applyMatrix4(this.matrixWorld);
- if (_ray.intersectsSphere(sphere)) {
- nodesOnRay.push(node);
- }
- }
- return nodesOnRay;
- }
- updateMatrixWorld (force) {
- if (this.matrixAutoUpdate === true) this.updateMatrix();
- if (this.matrixWorldNeedsUpdate === true || force === true) {
- if (!this.parent) {
- this.matrixWorld.copy(this.matrix);
- } else {
- this.matrixWorld.multiplyMatrices(this.parent.matrixWorld, this.matrix);
- }
- this.matrixWorldNeedsUpdate = false;
- force = true;
- }
- }
- hideDescendants (object) {
- let stack = [];
- for (let i = 0; i < object.children.length; i++) {
- let child = object.children[i];
- if (child.visible) {
- stack.push(child);
- }
- }
- while (stack.length > 0) {
- let object = stack.shift();
- object.visible = false;
- for (let i = 0; i < object.children.length; i++) {
- let child = object.children[i];
- if (child.visible) {
- stack.push(child);
- }
- }
- }
- }
- moveToOrigin () {
- this.position.set(0, 0, 0);
- this.updateMatrixWorld(true);
- let box = this.boundingBox;
- let transform = this.matrixWorld;
- let tBox = Utils.computeTransformedBoundingBox(box, transform);
- this.position.set(0, 0, 0).sub(tBox.getCenter(new Vector3()));
- };
- moveToGroundPlane () {
- this.updateMatrixWorld(true);
- let box = this.boundingBox;
- let transform = this.matrixWorld;
- let tBox = Utils.computeTransformedBoundingBox(box, transform);
- this.position.y += -tBox.min.y;
- };
- getBoundingBoxWorld () {
- this.updateMatrixWorld(true);
- let box = this.boundingBox;
- let transform = this.matrixWorld;
- let tBox = Utils.computeTransformedBoundingBox(box, transform);
- return tBox;
- };
- /**
- * returns points inside the profile points
- *
- * maxDepth: search points up to the given octree depth
- *
- *
- * The return value is an array with all segments of the profile path
- * let segment = {
- * start: THREE.Vector3,
- * end: THREE.Vector3,
- * points: {}
- * project: function()
- * };
- *
- * The project() function inside each segment can be used to transform
- * that segments point coordinates to line up along the x-axis.
- *
- *
- */
- getPointsInProfile (profile, maxDepth, callback) {
- if (callback) {
- let request = new Potree.ProfileRequest(this, profile, maxDepth, callback);
- this.profileRequests.push(request);
- return request;
- }
- let points = {
- segments: [],
- boundingBox: new Box3(),
- projectedBoundingBox: new Box2()
- };
- // evaluate segments
- for (let i = 0; i < profile.points.length - 1; i++) {
- let start = profile.points[i];
- let end = profile.points[i + 1];
- let ps = this.getProfile(start, end, profile.width, maxDepth);
- let segment = {
- start: start,
- end: end,
- points: ps,
- project: null
- };
- points.segments.push(segment);
- points.boundingBox.expandByPoint(ps.boundingBox.min);
- points.boundingBox.expandByPoint(ps.boundingBox.max);
- }
- // add projection functions to the segments
- let mileage = new Vector3();
- for (let i = 0; i < points.segments.length; i++) {
- let segment = points.segments[i];
- let start = segment.start;
- let end = segment.end;
- let project = (function (_start, _end, _mileage, _boundingBox) {
- let start = _start;
- let end = _end;
- let mileage = _mileage;
- let boundingBox = _boundingBox;
- let xAxis = new Vector3(1, 0, 0);
- let dir = new Vector3().subVectors(end, start);
- dir.y = 0;
- dir.normalize();
- let alpha = Math.acos(xAxis.dot(dir));
- if (dir.z > 0) {
- alpha = -alpha;
- }
- return function (position) {
- let toOrigin = new Matrix4().makeTranslation(-start.x, -boundingBox.min.y, -start.z);
- let alignWithX = new Matrix4().makeRotationY(-alpha);
- let applyMileage = new Matrix4().makeTranslation(mileage.x, 0, 0);
- let pos = position.clone();
- pos.applyMatrix4(toOrigin);
- pos.applyMatrix4(alignWithX);
- pos.applyMatrix4(applyMileage);
- return pos;
- };
- }(start, end, mileage.clone(), points.boundingBox.clone()));
- segment.project = project;
- mileage.x += new Vector3(start.x, 0, start.z).distanceTo(new Vector3(end.x, 0, end.z));
- mileage.y += end.y - start.y;
- }
- points.projectedBoundingBox.min.x = 0;
- points.projectedBoundingBox.min.y = points.boundingBox.min.y;
- points.projectedBoundingBox.max.x = mileage.x;
- points.projectedBoundingBox.max.y = points.boundingBox.max.y;
- return points;
- }
- /**
- * returns points inside the given profile bounds.
- *
- * start:
- * end:
- * width:
- * depth: search points up to the given octree depth
- * callback: if specified, points are loaded before searching
- *
- *
- */
- getProfile (start, end, width, depth, callback) {
- let request = new Potree.ProfileRequest(start, end, width, depth, callback);
- this.profileRequests.push(request);
- };
- getVisibleExtent () {
- return this.visibleBounds.applyMatrix4(this.matrixWorld);
- };
- intersectsPoint(position){
- let rootAvailable = this.pcoGeometry.root && this.pcoGeometry.root.geometry;
- if(!rootAvailable){
- return false;
- }
- if(typeof this.signedDistanceField === "undefined"){
- const resolution = 32;
- const field = new Float32Array(resolution ** 3).fill(Infinity);
- const positions = this.pcoGeometry.root.geometry.attributes.position;
- const boundingBox = this.boundingBox;
- const n = positions.count;
- for(let i = 0; i < n; i = i + 3){
- const x = positions.array[3 * i + 0];
- const y = positions.array[3 * i + 1];
- const z = positions.array[3 * i + 2];
- const ix = parseInt(Math.min(resolution * (x / boundingBox.max.x), resolution - 1));
- const iy = parseInt(Math.min(resolution * (y / boundingBox.max.y), resolution - 1));
- const iz = parseInt(Math.min(resolution * (z / boundingBox.max.z), resolution - 1));
- const index = ix + iy * resolution + iz * resolution * resolution;
- field[index] = 0;
- }
- const sdf = {
- resolution: resolution,
- field: field,
- };
- this.signedDistanceField = sdf;
- }
- {
- const sdf = this.signedDistanceField;
- const boundingBox = this.boundingBox;
- const toObjectSpace = this.matrixWorld.clone().invert();
- const objPos = position.clone().applyMatrix4(toObjectSpace);
- const resolution = sdf.resolution;
- const ix = parseInt(resolution * (objPos.x / boundingBox.max.x));
- const iy = parseInt(resolution * (objPos.y / boundingBox.max.y));
- const iz = parseInt(resolution * (objPos.z / boundingBox.max.z));
- if(ix < 0 || iy < 0 || iz < 0){
- return false;
- }
- if(ix >= resolution || iy >= resolution || iz >= resolution){
- return false;
- }
- const index = ix + iy * resolution + iz * resolution * resolution;
- const value = sdf.field[index];
- if(value === 0){
- return true;
- }
- }
- return false;
- }
- /**
- *
- *
- *
- * params.pickWindowSize: Look for points inside a pixel window of this size.
- * Use odd values: 1, 3, 5, ...
- *
- *
- * TODO: only draw pixels that are actually read with readPixels().
- *
- */
- pick(viewer, viewport, camera, ray, params = {}){
-
- let renderer = viewer.renderer;
- let pRenderer = viewer.pRenderer;
- performance.mark("pick-start");
- let getVal = (a, b) => a != void 0 ? a : b;
-
-
- let pickWindowSize_ = MathUtils.clamp( Math.round((1.1-this.maxLevel/this.nodeMaxLevel)*80), 5, 100);
-
- let pickWindowSize = getVal(params.pickWindowSize, pickWindowSize_ ); /* 65 */ //拾取像素边长,越小越精准,但点云稀疏的话可能容易出现识别不到的情况。 另外左下侧会有缝隙无法识别到,缝隙大小和这个值有关
-
- let pickOutsideClipRegion = getVal(params.pickOutsideClipRegion, false);
- let size = viewport ? viewport.resolution : renderer.getSize(new Vector2$1());
- let width = Math.ceil(getVal(params.width, size.width)); //renderTarget大小。影响识别精度
- let height = Math.ceil(getVal(params.height, size.height));
-
- let screenshot = ()=>{
- if(window.testScreen){
- let dataUrl = Potree.Utils.renderTargetToDataUrl(pickState.renderTarget, width, height, renderer);
-
- Common.downloadFile(dataUrl, 'screenshot.jpg'); //为什么图片上不是只有pickWindowSize区域有颜色??
- window.testScreen = 0;
- }
- };
-
-
- let pointSizeType = getVal(params.pointSizeType, this.material.pointSizeType);
- let pointSize = getVal(params.pointSize, this.material.size);
- let nodes = this.nodesOnRay(this.visibleNodes, ray);
- if (nodes.length === 0) {
-
- return null;
- }
- //console.log('nodes.length != 0', this.name)
- if (!this.pickState) {
- let scene = new Scene();
- let material = new Potree.PointCloudMaterial();
- material.activeAttributeName = "indices";
- let renderTarget = new WebGLRenderTarget(
- 1, 1,
- { minFilter: LinearFilter,
- magFilter: NearestFilter,
- format: RGBAFormat }
- );
- this.pickState = {
- renderTarget: renderTarget,
- material: material,
- scene: scene
- };
- };
- let pickState = this.pickState;
- let pickMaterial = pickState.material;
- { // update pick material
- pickMaterial.pointSizeType = pointSizeType;
- //pickMaterial.shape = this.material.shape;
- pickMaterial.shape = Potree.PointShape.PARABOLOID;
- pickMaterial.uniforms.uFilterReturnNumberRange.value = this.material.uniforms.uFilterReturnNumberRange.value;
- pickMaterial.uniforms.uFilterNumberOfReturnsRange.value = this.material.uniforms.uFilterNumberOfReturnsRange.value;
- pickMaterial.uniforms.uFilterGPSTimeClipRange.value = this.material.uniforms.uFilterGPSTimeClipRange.value;
- pickMaterial.uniforms.uFilterPointSourceIDClipRange.value = this.material.uniforms.uFilterPointSourceIDClipRange.value;
- pickMaterial.activeAttributeName = "indices";
- pickMaterial.size = pointSize;
- pickMaterial.uniforms.minSize.value = this.material.uniforms.minSize.value;
- pickMaterial.uniforms.maxSize.value = this.material.uniforms.maxSize.value;
- pickMaterial.classification = this.material.classification;
- pickMaterial.recomputeClassification();
- if(params.pickClipped){
- pickMaterial.clipBoxes = this.material.clipBoxes;
- pickMaterial.uniforms.clipBoxes = this.material.uniforms.clipBoxes;
- if(this.material.clipTask === Potree.ClipTask.HIGHLIGHT){
- pickMaterial.clipTask = Potree.ClipTask.NONE;
- }else {
- pickMaterial.clipTask = this.material.clipTask;
- }
- pickMaterial.clipMethod = this.material.clipMethod;
- }else {
- pickMaterial.clipBoxes = [];
- }
- this.updateMaterial(pickMaterial, nodes, camera, renderer, new Vector2$1(width, height));
- }
- pickState.renderTarget.setSize(width, height); //仅绘制屏幕大小的,而不乘以devicePixelRatio
- let pixelPos = new Vector2$1(params.x, params.y);
- let gl = renderer.getContext();
- gl.enable(gl.SCISSOR_TEST);
- gl.scissor( //规定渲染范围,只渲染一小块
- parseInt(pixelPos.x - (pickWindowSize - 1) / 2),
- parseInt(pixelPos.y - (pickWindowSize - 1) / 2),
- parseInt(pickWindowSize), parseInt(pickWindowSize));
- renderer.state.buffers.depth.setTest(pickMaterial.depthTest);
- renderer.state.buffers.depth.setMask(pickMaterial.depthWrite);
- renderer.state.setBlending(NoBlending);
- { // RENDER
- renderer.setRenderTarget(pickState.renderTarget);
- gl.clearColor(0, 0, 0, 0);
- renderer.clear(true, true, true);
- let tmp = this.material;
- this.material = pickMaterial;
-
- pRenderer.renderOctree(this, nodes, camera, pickState.renderTarget);
- screenshot();
- this.material = tmp;
- }
- let clamp = (number, min, max) => Math.min(Math.max(min, number), max);
- let x = parseInt(clamp(pixelPos.x - (pickWindowSize - 1) / 2, 0, width));
- let y = parseInt(clamp(pixelPos.y - (pickWindowSize - 1) / 2, 0, height));
- /* let w = parseInt(Math.min(x + pickWindowSize, width) - x);
- let h = parseInt(Math.min(y + pickWindowSize, height) - y); */
-
- let pixelCount = pickWindowSize * pickWindowSize;//w * h;
- let buffer = new Uint8Array(4 * pixelCount);
- //w<pickWindowSize会报错
-
- gl.readPixels(x, y, pickWindowSize, pickWindowSize, gl.RGBA, gl.UNSIGNED_BYTE, buffer);
- renderer.setRenderTarget(null);
- renderer.state.reset();
- renderer.setScissorTest(false);
- gl.disable(gl.SCISSOR_TEST);
- let pixels = buffer;
- let ibuffer = new Uint32Array(buffer.buffer); //四个数整合成一个
- // find closest hit inside pixelWindow boundaries
- let min = Number.MAX_VALUE;
- let hits = [];
- for (let u = 0; u < pickWindowSize; u++) {
- for (let v = 0; v < pickWindowSize; v++) {
- let offset = (u + v * pickWindowSize);
- let distance = Math.pow(u - (pickWindowSize - 1) / 2, 2) + Math.pow(v - (pickWindowSize - 1) / 2, 2);
- let pcIndex = pixels[4 * offset + 3];//nodes index(第四位)
- pixels[4 * offset + 3] = 0; //去除nodes index后剩下的是index(前三位)
- let pIndex = ibuffer[offset]; //index
- if(!(pcIndex === 0 && pIndex === 0) && (pcIndex !== undefined) && (pIndex !== undefined)){
- let hit = {
- pIndex: pIndex,
- pcIndex: pcIndex,
- distanceToCenter: distance
- };
- if(params.all){
- hits.push(hit);
- }else {
- if(hits.length > 0){
- if(distance < hits[0].distanceToCenter){
- hits[0] = hit;
- }
- }else {
- hits.push(hit);
- }
- }
- }
- }
- }
-
- // { // DEBUG: show panel with pick image
- // let img = Utils.pixelsArrayToImage(buffer, w, h);
- // let screenshot = img.src;
-
- // if(!this.debugDIV){
- // this.debugDIV = $(`
- // <div id="pickDebug"
- // style="position: absolute;
- // right: 400px; width: 300px;
- // bottom: 44px; width: 300px;
- // z-index: 1000;
- // "></div>`);
- // $(document.body).append(this.debugDIV);
- // }
-
- // this.debugDIV.empty();
- // this.debugDIV.append($(`<img src="${screenshot}"
- // style="transform: scaleY(-1); width: 300px"/>`));
- // //$(this.debugWindow.document).append($(`<img src="${screenshot}"/>`));
- // //this.debugWindow.document.write('<img src="'+screenshot+'"/>');
- // }
- for(let hit of hits){
- let point = {};
- if (!nodes[hit.pcIndex]) {
- return null;
- }
- let node = nodes[hit.pcIndex];
- let pc = node.sceneNode;
- let geometry = node.geometryNode.geometry;
- for(let attributeName in geometry.attributes){
- let attribute = geometry.attributes[attributeName];
- if (attributeName === 'position') {
- let x = attribute.array[3 * hit.pIndex + 0];
- let y = attribute.array[3 * hit.pIndex + 1];
- let z = attribute.array[3 * hit.pIndex + 2];
- let position = new Vector3(x, y, z);
-
- position.applyMatrix4( pc.matrixWorld );
-
- point[attributeName] = position;
- } else if (attributeName === 'indices') {
- } else {
- let values = attribute.array.slice(attribute.itemSize * hit.pIndex, attribute.itemSize * (hit.pIndex + 1)) ;
- if(attribute.potree){
- const {scale, offset} = attribute.potree;
- values = values.map(v => v / scale + offset);
- }
- point[attributeName] = values;
- //debugger;
- //if (values.itemSize === 1) {
- // point[attribute.name] = values.array[hit.pIndex];
- //} else {
- // let value = [];
- // for (let j = 0; j < values.itemSize; j++) {
- // value.push(values.array[values.itemSize * hit.pIndex + j]);
- // }
- // point[attribute.name] = value;
- //}
- }
- }
- hit.point = point;
- }
- performance.mark("pick-end");
- performance.measure("pick", "pick-start", "pick-end");
- if(params.all){
- return hits.map(hit => hit.point);
- }else {
- if(hits.length === 0){
- return null;
- }else {
- return hits[0].point;
- //let sorted = hits.sort( (a, b) => a.distanceToCenter - b.distanceToCenter);
- //return sorted[0].point;
- }
- }
- };
- * getFittedBoxGen(boxNode){//???
- let start = performance.now();
- let shrinkedLocalBounds = new Box3();
- let worldToBox = boxNode.matrixWorld.clone().invert();
- for(let node of this.visibleNodes){
- if(!node.sceneNode){
- continue;
- }
- let buffer = node.geometryNode.buffer;
- let posOffset = buffer.offset("position");
- let stride = buffer.stride;
- let view = new DataView(buffer.data);
- let objectToBox = new Matrix4().multiplyMatrices(worldToBox, node.sceneNode.matrixWorld);
- let pos = new Vector4();
- for(let i = 0; i < buffer.numElements; i++){
- let x = view.getFloat32(i * stride + posOffset + 0, true);
- let y = view.getFloat32(i * stride + posOffset + 4, true);
- let z = view.getFloat32(i * stride + posOffset + 8, true);
- pos.set(x, y, z, 1);
- pos.applyMatrix4(objectToBox);
- if(-0.5 < pos.x && pos.x < 0.5){
- if(-0.5 < pos.y && pos.y < 0.5){
- if(-0.5 < pos.z && pos.z < 0.5){
- shrinkedLocalBounds.expandByPoint(pos);
- }
- }
- }
- }
- yield;
- }
- let fittedPosition = shrinkedLocalBounds.getCenter(new Vector3()).applyMatrix4(boxNode.matrixWorld);
- let fitted = new Object3D();
- fitted.position.copy(fittedPosition);
- fitted.scale.copy(boxNode.scale);
- fitted.rotation.copy(boxNode.rotation);
- let ds = new Vector3().subVectors(shrinkedLocalBounds.max, shrinkedLocalBounds.min);
- fitted.scale.multiply(ds);
- let duration = performance.now() - start;
- console.log("duration: ", duration);
- yield fitted;
- }
- getFittedBox(boxNode, maxLevel = Infinity){
- maxLevel = Infinity;
- let start = performance.now();
- let shrinkedLocalBounds = new Box3();
- let worldToBox = boxNode.matrixWorld.clone().invert();
- for(let node of this.visibleNodes){
- if(!node.sceneNode || node.getLevel() > maxLevel){
- continue;
- }
- let buffer = node.geometryNode.buffer;
- let posOffset = buffer.offset("position");
- let stride = buffer.stride;
- let view = new DataView(buffer.data);
- let objectToBox = new Matrix4().multiplyMatrices(worldToBox, node.sceneNode.matrixWorld);
- let pos = new Vector4();
- for(let i = 0; i < buffer.numElements; i++){
- let x = view.getFloat32(i * stride + posOffset + 0, true);
- let y = view.getFloat32(i * stride + posOffset + 4, true);
- let z = view.getFloat32(i * stride + posOffset + 8, true);
- pos.set(x, y, z, 1);
- pos.applyMatrix4(objectToBox);
- if(-0.5 < pos.x && pos.x < 0.5){
- if(-0.5 < pos.y && pos.y < 0.5){
- if(-0.5 < pos.z && pos.z < 0.5){
- shrinkedLocalBounds.expandByPoint(pos);
- }
- }
- }
- }
- }
- let fittedPosition = shrinkedLocalBounds.getCenter(new Vector3()).applyMatrix4(boxNode.matrixWorld);
- let fitted = new Object3D();
- fitted.position.copy(fittedPosition);
- fitted.scale.copy(boxNode.scale);
- fitted.rotation.copy(boxNode.rotation);
- let ds = new Vector3().subVectors(shrinkedLocalBounds.max, shrinkedLocalBounds.min);
- fitted.scale.multiply(ds);
- let duration = performance.now() - start;
- console.log("duration: ", duration);
- return fitted;
- }
- get progress () {
- return this.visibleNodes.length / this.visibleGeometry.length;
- }
- find(name){
- let node = null;
- for(let char of name){
- if(char === "r"){
- node = this.root;
- }else {
- node = node.children[char];
- }
- }
- return node;
- }
- get visible(){
- return this._visible;
- }
- set visible(value){
- if(value !== this._visible){
- this._visible = value;
- this.dispatchEvent({type: 'visibility_changed', pointcloud: this});
- }
- }
-
- // 设置点大小
- changePointSize(num, sizeFitToLevel) {
-
- if(this.material.pointSizeType != PointSizeType.ATTENUATED){
- return num && (this.material.size = num)
- }
- if (num == void 0) {
- num = this.temp.pointSize;
- } else {
- this.temp.pointSize = num;
-
- }
- num /= (Potree.config.material.realPointSize / Potree.config.material.pointSize); //兼容
-
-
-
-
- num = Math.pow(num, 1.05) * 6;
-
-
-
-
- if(sizeFitToLevel || Potree.settings.sizeFitToLevel){//按照点云质量来调整的版本: 近似将pointSizeType换成ADAPTIVE
- let str = this.temp.pointSize+':'+this.maxLevel+':'+this.nodeMaxLevel;
- let value = this.temp.sizeFitToLevel[str]; //储存。防止每次渲染(反复切换density)都要算。
- if(value){
- this.material.size = value;
- }else {
-
- let base = this.material.spacing / Math.pow(2, this.maxLevel); //点云大小在level为0时设置为spacing,每长一级,大小就除以2
- base *= this.nodeMaxLevel > 0 ? Math.max(0.1, Math.pow(this.maxLevel / this.nodeMaxLevel, 1.3)) : 0.1; //低质量的缩小点,因为视觉上看太大了。navvis是不铺满的,我们也留一点缝隙
- this.material.size = base * 3 * num;/* * window.devicePixelRatio */
- //在t-8BCqxQAr93 会议室 和 t-e2Kb2iU 隧道 两个场景里调节,因为它们的spacing相差较大,观察会议室墙壁的龟裂程度
- this.temp.sizeFitToLevel[str] = this.material.size;
- }
- }else {
-
- let base = 0.007; //let base = this.material.spacing / Math.pow(2, this.nodeMaxLevel) //点云大小在level为0时设置为spacing,每长一级,大小就除以2
- //base的数值理论上应该是右侧算出来的,但发现有的场景nodeMaxLevel和nodeMaxLevelPredict差别较大的点云显示也过大,而直接换成固定值反而可以适应所有场景。该固定值来源于 getHighestNodeSpacing 最小值,修改了下。(会不会是我们的相机其实该值是固定的,根据该值算出的spacing才是有误差的? 如果换了相机是否要改值?)
- this.material.size = base * 5 * num; /* * window.devicePixelRatio */
- }
-
-
- //console.log('changePointSize ' + this.dataset_id + ' , num : ' + num + ' , size : ' + this.material.size, this.material.spacing)
-
- }
-
-
-
- // 设置点透明度
- changePointOpacity(num, canMoreThanOne) {
- //num:0-1 navvis用的是亮度
- if (num == void 0) {
- num = this.temp.pointOpacity;
- } else {
- this.temp.pointOpacity = num;
- }
-
- if (num == 1) {
- this.material.opacity = 1;
- } else {
- let str = (Potree.settings.sizeFitToLevel?'sizeFit:':'')+ (canMoreThanOne ? 'canMoreThanOne:':'') +this.temp.pointOpacity+':'+this.maxLevel+':'+this.nodeMaxLevel;
- let value = this.temp.opacity[str]; //储存。防止每次渲染(反复切换density)都要算。
- if(value){
- this.material.opacity = value;
- }else {
- if(Potree.settings.sizeFitToLevel){//按照点云质量来调整的版本:
- let base = this.material.spacing / Math.pow(1.4, this.maxLevel); //随着level提高,点云重叠几率增多
- let minBase = this.material.spacing / Math.pow(1.4, this.nodeMaxLevel);
- let ratio = Math.min(1 / base, 1 / minBase / 3); //ratio为一个能使opacity不大于1 的 乘量,minBase要除以一个数,该数调越大减弱效果越强。level越低opacity和面板越接,level越高效果越弱,以减免过度重叠后的亮度。
- this.material.opacity = base * ratio * num;
- if(!canMoreThanOne){
- this.material.opacity = MathUtils.clamp(this.material.opacity, 0, 0.999); //到1就不透明了(可能出现一段一样)
- }
- }else {
- let base = this.material.spacing / Math.pow(1.8, this.maxLevel);
- let minBase = this.material.spacing / Math.pow(1.8, this.nodeMaxLevel);
- //console.log(1 / base, 1 / minBase / 6)
- let ratio = Math.min(1 / base, 1 / minBase / 6); //ratio为一个能使opacity不大于1 的 乘量,minBase要除以一个数,该数调越大减弱效果越强。level越低opacity和面板越接,level越高效果越弱,以减免过度重叠后的亮度。
- this.material.opacity = base * ratio * num;
- if(!canMoreThanOne){
- this.material.opacity = MathUtils.clamp(this.material.opacity, 0, 0.999); //到1就不透明了(可能出现一段一样)
- }
- }
- this.temp.opacity[str] = this.material.opacity;
- }
-
- //缺点:防止颜色过亮主要是相机离远时,当在漫游点处由于离点云太近,可能会导致高质量点云看起来很暗。
- }
- //console.log('changePointOpacity ' + this.dataset_id + ', num : ' + num + ' , opacity : ' + this.material.opacity) //检查是否做到了低质量时num==opacity,中质量opacity稍小于num,高质量更小
-
- }
-
- updateBound(){
- var boundingBox_ = this.pcoGeometry.tightBoundingBox.clone().applyMatrix4(this.matrixWorld);
- this.bound = boundingBox_;
- }
- getPanosBound(){
- if(this.panos.length > 0){
- let minSize = new Vector3(1,1,1);
- this.panosBound = math.getBoundByPoints(this.panos.map(e=>e.position), minSize);
- }else {
- this.panosBound = null;
- }
- }
-
- getUnrotBoundPoint(type){//获取没有旋转的tightBounding的水平四个点
- //如果alighment支持任意轴旋转,水平面上看到的可能就是六边形了,失去意义,到时候不能用这个。也可以若只绕z旋转, 使用tightBoundingBox,否则bound
- let bound = this.pcoGeometry.tightBoundingBox;
- if(type == 'all'){
- return [new Vector3(bound.min.x, bound.min.y, bound.min.z),
- new Vector3(bound.max.x, bound.min.y, bound.min.z),
- new Vector3(bound.max.x, bound.max.y,bound.min.z),
- new Vector3(bound.min.x, bound.max.y,bound.min.z),
- new Vector3(bound.min.x, bound.min.y, bound.max.z),
- new Vector3(bound.max.x, bound.min.y, bound.max.z),
- new Vector3(bound.max.x, bound.max.y,bound.max.z),
- new Vector3(bound.min.x, bound.max.y,bound.max.z),
- ].map(e=>e.applyMatrix4(this.matrixWorld))
- }else
- return [new Vector3(bound.min.x, bound.min.y,0),
- new Vector3(bound.max.x, bound.min.y,0),
- new Vector3(bound.max.x, bound.max.y,0),
- new Vector3(bound.min.x, bound.max.y,0),
- ].map(e=>e.applyMatrix4(this.matrixWorld))
- }
-
-
-
- ifContainsPoint(pos){
- if(!this.bound.containsPoint(pos))return
- var points = this.getUnrotBoundPoint();
- return math.isPointInArea(points, null, pos)
- }
-
- getVolume(){
- var points = this.getUnrotBoundPoint();
- var area = Math.abs(math.getArea(points));
- return area * (this.bound.max.z - this.bound.min.z)
- }
-
-
- //数据集的显示影响到其下的:点云、marker .不会影响地图上的显示
-
- /*
- updateVisible(reason, ifShow){//当所有加入的条件都不为false时才显示
- if(ifShow){
- var index = this.unvisibleReasons.indexOf(reason);
- index > -1 && this.unvisibleReasons.splice(index, 1);
- if(this.unvisibleReasons.length == 0){
- this.visible = true;
- this.dispatchEvent({
- type: 'isVisible'
- visible:true
- })
- }
- }else {
- if(!this.unvisibleReasons.includes(reason)) this.unvisibleReasons.push(reason);
- this.visible = false;
- this.dispatchEvent({
- type: 'isVisible'
- visible:false
- })
- }
-
- }
-
- getVisible(reason){//获取在某条件下是否可见. 注: 用户在数据集选择可不可见为"datasetSelection"
- if(this.visible)return true
- else{
- return !this.unvisibleReasons.includes(reason)
- }
- } */
-
-
- /* get isVisible(){//add 手动在数据集选择是否显示(和是否全景图、隐藏点云无关)
- return this._isVisible
- }
- set isVisible(visi){
-
- if(!visi)this.visible = false
-
-
-
- this._isVisible = visi
- } */
- }
- /*
- window.searchOutRing = function(points){
- var points = [{x:0,y:0},{x:1,y:1},{x:0,y:1},{x:1,y:0},{x:2,y:1},{x:1,y:2},{x:1,y:3},{x:2,y:3},
- {x:3,y:1},{x:3,y:2},{x:0,y:2},{x:0,y:3},{x:2,y:0},{x:-1,y:0},{x:-1,y:1},{x:-2,y:1},{x:3,y:3},
- {x:3,y:0},{x:2,y:-1},{x:0,y:-1},{x:1,y:0.5},{x:2,y:0.5}, ].map((e,index)=>{
- var a = new THREE.Vector2().copy(e)
- a.id = index;
- return a
- })
- var lines = []
- var minDis = 2*2
- points.forEach((p,j)=>{
- for(let i=0;i<j;i++){
- if(p.distanceToSquared(points[i])<minDis){
- lines.push({p1:i,p2:j})
- }
- }
- })
-
-
- searchRings({
- points,
- lines,
- onlyGetOutRing:true
- })
- }
- */
- class Points$1 {
-
- constructor () {
- this.boundingBox = new Box3();
- this.numPoints = 0;
- this.data = {};
- }
- add (points) {
- let currentSize = this.numPoints;
- let additionalSize = points.numPoints;
- let newSize = currentSize + additionalSize;
- let thisAttributes = Object.keys(this.data);
- let otherAttributes = Object.keys(points.data);
- let attributes = new Set([...thisAttributes, ...otherAttributes]);
- for (let attribute of attributes) {
- if (thisAttributes.includes(attribute) && otherAttributes.includes(attribute)) {
- // attribute in both, merge
- let Type = this.data[attribute].constructor;
- let merged = new Type(this.data[attribute].length + points.data[attribute].length);
- merged.set(this.data[attribute], 0);
- merged.set(points.data[attribute], this.data[attribute].length);
- this.data[attribute] = merged;
- } else if (thisAttributes.includes(attribute) && !otherAttributes.includes(attribute)) {
- // attribute only in this; take over this and expand to new size
- let elementsPerPoint = this.data[attribute].length / this.numPoints;
- let Type = this.data[attribute].constructor;
- let expanded = new Type(elementsPerPoint * newSize);
- expanded.set(this.data[attribute], 0);
- this.data[attribute] = expanded;
- } else if (!thisAttributes.includes(attribute) && otherAttributes.includes(attribute)) {
- // attribute only in points to be added; take over new points and expand to new size
- let elementsPerPoint = points.data[attribute].length / points.numPoints;
- let Type = points.data[attribute].constructor;
- let expanded = new Type(elementsPerPoint * newSize);
- expanded.set(points.data[attribute], elementsPerPoint * currentSize);
- this.data[attribute] = expanded;
- }
- }
- this.numPoints = newSize;
- this.boundingBox.union(points.boundingBox);
- }
- }
- /**
- *
- * code adapted from three.js BoxHelper.js
- * https://github.com/mrdoob/three.js/blob/dev/src/helpers/BoxHelper.js
- *
- * @author mrdoob / http://mrdoob.com/
- * @author Mugen87 / http://github.com/Mugen87
- * @author mschuetz / http://potree.org
- */
- class Box3Helper$1 extends LineSegments {
- constructor (box, color) {
- if (color === undefined) color = 0xffff00;
- let indices = new Uint16Array([ 0, 1, 1, 2, 2, 3, 3, 0, 4, 5, 5, 6, 6, 7, 7, 4, 0, 4, 1, 5, 2, 6, 3, 7 ]);
- let positions = new Float32Array([
- box.min.x, box.min.y, box.min.z,
- box.max.x, box.min.y, box.min.z,
- box.max.x, box.min.y, box.max.z,
- box.min.x, box.min.y, box.max.z,
- box.min.x, box.max.y, box.min.z,
- box.max.x, box.max.y, box.min.z,
- box.max.x, box.max.y, box.max.z,
- box.min.x, box.max.y, box.max.z
- ]);
- let geometry = new BufferGeometry();
- geometry.setIndex(new BufferAttribute(indices, 1));
- geometry.setAttribute('position', new BufferAttribute(positions, 3));
- let material = new LineBasicMaterial({ color: color });
- super(geometry, material);
- }
- }
- function updatePointClouds(pointclouds,camera, areaSize /* renderer */){
-
- for (let pointcloud of pointclouds) {
- let start = performance.now();
- for (let profileRequest of pointcloud.profileRequests) {
- profileRequest.update();
- let duration = performance.now() - start;
- if(duration > 5){
- break;
- }
- }
- let duration = performance.now() - start;
- }
-
-
- let result = updateVisibility(pointclouds, camera, areaSize );
- for (let pointcloud of pointclouds) {
- //pointcloud.updateMaterial(pointcloud.material, pointcloud.visibleNodes, camera, renderer);//转移到渲染时
- pointcloud.updateVisibleBounds();
- }
- exports.lru.freeMemory();//即Potree.lru 能看到所有在加载的node
- return result;
- };
- function updateVisibilityStructures(pointclouds, camera, areaSize) {
- let frustums = [];
- let camObjPositions = [];
- let priorityQueue = new BinaryHeap(function (x) { return 1 / x.weight; });
- for (let i = 0; i < pointclouds.length; i++) {
- let pointcloud = pointclouds[i];
- if (!pointcloud.initialized()) {
- continue;
- }
- pointcloud.numVisibleNodes = 0;
- pointcloud.numVisiblePoints = 0;
- pointcloud.deepestVisibleLevel = 0;
- pointcloud.visibleNodes = [];
- pointcloud.visibleGeometry = [];
- // frustum in object space
- camera.updateMatrixWorld();
- let frustum = new Frustum();
- let viewI = camera.matrixWorldInverse;
- let world = pointcloud.matrixWorld;
-
- // use close near plane for frustum intersection
- let frustumCam = camera.clone();
- frustumCam.near = Math.min(camera.near, 0.1);
- frustumCam.updateProjectionMatrix();
- let proj = camera.projectionMatrix;
- let fm = new Matrix4().multiply(proj).multiply(viewI).multiply(world);
- frustum.setFromProjectionMatrix(fm);
- frustums.push(frustum);
- // camera position in object space
- let view = camera.matrixWorld;
- let worldI = world.clone().invert();
- let camMatrixObject = new Matrix4().multiply(worldI).multiply(view);
- let camObjPos = new Vector3().setFromMatrixPosition(camMatrixObject);
- camObjPositions.push(camObjPos);
-
- // 因漫游模式而隐藏的话 依旧需要加入visibleNodes,因为pick需要
-
- /* viewer.getObjVisiByReason(pointcloud, 'datasetSelection') */
- if (pointcloud.visible || pointcloud.unvisibleReasons && pointcloud.unvisibleReasons.length == 1 && pointcloud.unvisibleReasons[0].reason == 'displayMode' && pointcloud.root !== null) {//改 visible ->
- priorityQueue.push({pointcloud: i, node: pointcloud.root, weight: Number.MAX_VALUE});
- }
- // hide all previously visible nodes
- // if(pointcloud.root instanceof PointCloudOctreeNode){
- // pointcloud.hideDescendants(pointcloud.root.sceneNode);
- // }
- if (pointcloud.root.isTreeNode()) {
- pointcloud.hideDescendants(pointcloud.root.sceneNode);
- }
- for (let j = 0; j < pointcloud.boundingBoxNodes.length; j++) {
- pointcloud.boundingBoxNodes[j].visible = false;
- }
- }
- return {
- 'frustums': frustums,
- 'camObjPositions': camObjPositions,
- 'priorityQueue': priorityQueue
- };
- };
- function updateVisibility(pointclouds, camera, areaSize){
- let numVisibleNodes = 0;
- let numVisiblePoints = 0;
- let numVisiblePointsInPointclouds = new Map(pointclouds.map(pc => [pc, 0]));
- let visibleNodes = [];
- let visibleGeometry = [];
- let unloadedGeometry = [];
- let lowestSpacing = Infinity;
- // calculate object space frustum and cam pos and setup priority queue
- let s = updateVisibilityStructures(pointclouds, camera, areaSize);//得到相机可见范围
- let frustums = s.frustums;
- let camObjPositions = s.camObjPositions;
- let priorityQueue = s.priorityQueue;
- let loadedToGPUThisFrame = 0;
-
- let domWidth = areaSize.x; //renderer.domElement.clientWidth;
- let domHeight = areaSize.y;//renderer.domElement.clientHeight;
- // check if pointcloud has been transformed
- // some code will only be executed if changes have been detected
- if(!Potree._pointcloudTransformVersion){
- Potree._pointcloudTransformVersion = new Map();
- }
- let pointcloudTransformVersion = Potree._pointcloudTransformVersion;
- for(let pointcloud of pointclouds){
- if(!viewer.getObjVisiByReason(pointcloud, 'datasetSelection')){//改 visible ->
- continue;
- }
- pointcloud.updateMatrixWorld();
- if(!pointcloudTransformVersion.has(pointcloud)){
- pointcloudTransformVersion.set(pointcloud, {number: 0, transform: pointcloud.matrixWorld.clone()});
- }else {
- let version = pointcloudTransformVersion.get(pointcloud);
- if(!version.transform.equals(pointcloud.matrixWorld)){
- version.number++;
- version.transform.copy(pointcloud.matrixWorld);
- pointcloud.dispatchEvent({
- type: "transformation_changed",
- target: pointcloud
- });
- }
- }
- }
- while (priorityQueue.size() > 0) {
- let element = priorityQueue.pop();//其实是拿第一个, 再把最后一个放到前面
-
- let node = element.node;
- let parent = element.parent;
- let pointcloud = pointclouds[element.pointcloud];
- // { // restrict to certain nodes for debugging
- // let allowedNodes = ["r", "r0", "r4"];
- // if(!allowedNodes.includes(node.name)){
- // continue;
- // }
- // }
- let box = node.getBoundingBox();
- let frustum = frustums[element.pointcloud];
- let camObjPos = camObjPositions[element.pointcloud];
- let insideFrustum = frustum.intersectsBox(box);
- let maxLevel = pointcloud.maxLevel == void 0 ? Infinity : pointcloud.maxLevel;
- let level = node.getLevel();
- let visible = insideFrustum;
- visible = visible && !(numVisiblePoints + node.getNumPoints() > Potree.pointBudget);
- visible = visible && !(numVisiblePointsInPointclouds.get(pointcloud) + node.getNumPoints() > pointcloud.pointBudget);
- visible = visible && level <= maxLevel; //< 改为 <=
- //visible = visible || node.getLevel() <= 2;
- let clipBoxes = pointcloud.material.clipBoxes;
- if(true && clipBoxes.length > 0){
- //node.debug = false;
- let numIntersecting = 0;
- let numIntersectionVolumes = 0;
- //if(node.name === "r60"){
- // var a = 10;
- //}
- for(let clipBox of clipBoxes){
- let pcWorldInverse = pointcloud.matrixWorld.clone().invert();
- let toPCObject = pcWorldInverse.multiply(clipBox.box.matrixWorld);
- let px = new Vector3(+0.5, 0, 0).applyMatrix4(pcWorldInverse);
- let nx = new Vector3(-0.5, 0, 0).applyMatrix4(pcWorldInverse);
- let py = new Vector3(0, +0.5, 0).applyMatrix4(pcWorldInverse);
- let ny = new Vector3(0, -0.5, 0).applyMatrix4(pcWorldInverse);
- let pz = new Vector3(0, 0, +0.5).applyMatrix4(pcWorldInverse);
- let nz = new Vector3(0, 0, -0.5).applyMatrix4(pcWorldInverse);
- let pxN = new Vector3().subVectors(nx, px).normalize();
- let nxN = pxN.clone().multiplyScalar(-1);
- let pyN = new Vector3().subVectors(ny, py).normalize();
- let nyN = pyN.clone().multiplyScalar(-1);
- let pzN = new Vector3().subVectors(nz, pz).normalize();
- let nzN = pzN.clone().multiplyScalar(-1);
- let pxPlane = new Plane().setFromNormalAndCoplanarPoint(pxN, px);
- let nxPlane = new Plane().setFromNormalAndCoplanarPoint(nxN, nx);
- let pyPlane = new Plane().setFromNormalAndCoplanarPoint(pyN, py);
- let nyPlane = new Plane().setFromNormalAndCoplanarPoint(nyN, ny);
- let pzPlane = new Plane().setFromNormalAndCoplanarPoint(pzN, pz);
- let nzPlane = new Plane().setFromNormalAndCoplanarPoint(nzN, nz);
- //if(window.debugdraw !== undefined && window.debugdraw === true && node.name === "r60"){
- // Potree.utils.debugPlane(viewer.scene.scene, pxPlane, 1, 0xFF0000);
- // Potree.utils.debugPlane(viewer.scene.scene, nxPlane, 1, 0x990000);
- // Potree.utils.debugPlane(viewer.scene.scene, pyPlane, 1, 0x00FF00);
- // Potree.utils.debugPlane(viewer.scene.scene, nyPlane, 1, 0x009900);
- // Potree.utils.debugPlane(viewer.scene.scene, pzPlane, 1, 0x0000FF);
- // Potree.utils.debugPlane(viewer.scene.scene, nzPlane, 1, 0x000099);
- // Potree.utils.debugBox(viewer.scene.scene, box, new THREE.Matrix4(), 0x00FF00);
- // Potree.utils.debugBox(viewer.scene.scene, box, pointcloud.matrixWorld, 0xFF0000);
- // Potree.utils.debugBox(viewer.scene.scene, clipBox.box.boundingBox, clipBox.box.matrixWorld, 0xFF0000);
- // window.debugdraw = false;
- //}
- let frustum = new Frustum(pxPlane, nxPlane, pyPlane, nyPlane, pzPlane, nzPlane);
- let intersects = frustum.intersectsBox(box);
- if(intersects){
- numIntersecting++;
- }
- numIntersectionVolumes++;
- }
- let insideAny = numIntersecting > 0;
- let insideAll = numIntersecting === numIntersectionVolumes;
- if(pointcloud.material.clipTask === ClipTask.SHOW_INSIDE){
- if(pointcloud.material.clipMethod === ClipMethod.INSIDE_ANY && insideAny){
- //node.debug = true
- }else if(pointcloud.material.clipMethod === ClipMethod.INSIDE_ALL && insideAll){
- //node.debug = true;
- }else {
- visible = false;
- }
- } else if(pointcloud.material.clipTask === ClipTask.SHOW_OUTSIDE){
- //if(pointcloud.material.clipMethod === ClipMethod.INSIDE_ANY && !insideAny){
- // //visible = true;
- // let a = 10;
- //}else if(pointcloud.material.clipMethod === ClipMethod.INSIDE_ALL && !insideAll){
- // //visible = true;
- // let a = 20;
- //}else{
- // visible = false;
- //}
- }
-
- }
- // visible = ["r", "r0", "r06", "r060"].includes(node.name);
- // visible = ["r"].includes(node.name);
- if (node.spacing) {
- lowestSpacing = Math.min(lowestSpacing, node.spacing);
- } else if (node.geometryNode && node.geometryNode.spacing) {
- lowestSpacing = Math.min(lowestSpacing, node.geometryNode.spacing);
- }
- if (numVisiblePoints + node.getNumPoints() > Potree.pointBudget) {
- break;
- }
- if (!visible) {
- continue;
- }
- // TODO: not used, same as the declaration?
- // numVisibleNodes++;
- numVisiblePoints += node.getNumPoints();
- let numVisiblePointsInPointcloud = numVisiblePointsInPointclouds.get(pointcloud);
- numVisiblePointsInPointclouds.set(pointcloud, numVisiblePointsInPointcloud + node.getNumPoints());
- pointcloud.numVisibleNodes++;
- pointcloud.numVisiblePoints += node.getNumPoints();
- if (node.isGeometryNode() && (!parent || parent.isTreeNode())) {
- if (node.isLoaded() && loadedToGPUThisFrame < 2) {
- node = pointcloud.toTreeNode(node, parent);
- loadedToGPUThisFrame++;
- } else {
- unloadedGeometry.push({pointcloud,node});
- visibleGeometry.push(node);
- }
- }
- if (node.isTreeNode()) {
- exports.lru.touch(node.geometryNode);
- node.sceneNode.visible = true;
- node.sceneNode.material = pointcloud.material;
- visibleNodes.push(node);
- pointcloud.visibleNodes.push(node);
- if(node._transformVersion === undefined){
- node._transformVersion = -1;
- }
- let transformVersion = pointcloudTransformVersion.get(pointcloud);
- if(node._transformVersion !== transformVersion.number){
- node.sceneNode.updateMatrix();
- //node.sceneNode.matrixWorld.multiplyMatrices(pointcloud.matrixWorld, node.sceneNode.matrix);
- node.sceneNode.matrixWorld.multiplyMatrices(pointcloud.matrixWorld, node.sceneNode.matrix);
-
- node._transformVersion = transformVersion.number;
-
- }
- if (pointcloud.showBoundingBox && !node.boundingBoxNode && node.getBoundingBox) {
- let boxHelper = new Box3Helper$1(node.getBoundingBox());
- boxHelper.matrixAutoUpdate = false;
- pointcloud.boundingBoxNodes.push(boxHelper);
- node.boundingBoxNode = boxHelper;
- node.boundingBoxNode.matrix.copy(pointcloud.matrixWorld);
- } else if (pointcloud.showBoundingBox) {
- node.boundingBoxNode.visible = true;
- node.boundingBoxNode.matrix.copy(pointcloud.matrixWorld);
- } else if (!pointcloud.showBoundingBox && node.boundingBoxNode) {
- node.boundingBoxNode.visible = false;
- }
- // if(node.boundingBoxNode !== undefined && exports.debug.allowedNodes !== undefined){
- // if(!exports.debug.allowedNodes.includes(node.name)){
- // node.boundingBoxNode.visible = false;
- // }
- // }
- }
- // add child nodes to priorityQueue
- let children = node.getChildren();
- for (let i = 0; i < children.length; i++) {
- let child = children[i];
- let weight = 0;
- if(camera.isPerspectiveCamera){
- let sphere = child.getBoundingSphere();
- let center = sphere.center;
- //let distance = sphere.center.distanceTo(camObjPos);
-
- let dx = camObjPos.x - center.x;
- let dy = camObjPos.y - center.y;
- let dz = camObjPos.z - center.z;
-
- let dd = dx * dx + dy * dy + dz * dz;
- let distance = Math.sqrt(dd);
-
-
- let radius = sphere.radius;
-
- let fov = (camera.fov * Math.PI) / 180;
- let slope = Math.tan(fov / 2);
- let projFactor = (0.5 * domHeight) / (slope * distance);
- let screenPixelRadius = radius * projFactor;
-
- if(screenPixelRadius < pointcloud.minimumNodePixelSize){
- continue;
- }
-
- weight = screenPixelRadius;
- if(distance - radius < 0){
- weight = Number.MAX_VALUE;
- }
- } else {
- // TODO ortho visibility
- let bb = child.getBoundingBox();
- let distance = child.getBoundingSphere().center.distanceTo(camObjPos);
- let diagonal = bb.max.clone().sub(bb.min).length();
- //weight = diagonal / distance;
- weight = diagonal;
- }
- priorityQueue.push({pointcloud: element.pointcloud, node: child, parent: node, weight: weight});
- }
- }// end priority queue loop
- { // update DEM 这是什么
- let maxDEMLevel = 4;
- let candidates = pointclouds.filter(p => (p.generateDEM && p.dem instanceof Potree.DEM));
- for (let pointcloud of candidates) {
- let updatingNodes = pointcloud.visibleNodes.filter(n => n.getLevel() <= maxDEMLevel);
- pointcloud.dem.update(updatingNodes);
- }
- }
- //加载点云
- for (let i = 0; i < Math.min(Potree.maxNodesLoading, unloadedGeometry.length); i++) {
- unloadedGeometry[i].node.load(unloadedGeometry[i].pointcloud.pcoGeometry);
- }
- return {
- visibleNodes: visibleNodes,
- numVisiblePoints: numVisiblePoints,
- lowestSpacing: lowestSpacing
- };
- };
- //console
- //viewer.scene.pointclouds[0].visibleNodes.map(e=> e && e.name )
- //viewer.scene.pointclouds[0].visibleNodes.map(e=>e.children.map(e=>e && e.name))
- class PointCloudArena4DNode extends PointCloudTreeNode {
- constructor () {
- super();
- this.left = null;
- this.right = null;
- this.sceneNode = null;
- this.kdtree = null;
- }
- getNumPoints () {
- return this.geometryNode.numPoints;
- }
- isLoaded () {
- return true;
- }
- isTreeNode () {
- return true;
- }
- isGeometryNode () {
- return false;
- }
- getLevel () {
- return this.geometryNode.level;
- }
- getBoundingSphere () {
- return this.geometryNode.boundingSphere;
- }
- getBoundingBox () {
- return this.geometryNode.boundingBox;
- }
- toTreeNode (child) {
- let geometryNode = null;
- if (this.left === child) {
- geometryNode = this.left;
- } else if (this.right === child) {
- geometryNode = this.right;
- }
- if (!geometryNode.loaded) {
- return;
- }
- let node = new PointCloudArena4DNode();
- let sceneNode = PointCloud(geometryNode.geometry, this.kdtree.material);
- sceneNode.visible = false;
- node.kdtree = this.kdtree;
- node.geometryNode = geometryNode;
- node.sceneNode = sceneNode;
- node.parent = this;
- node.left = this.geometryNode.left;
- node.right = this.geometryNode.right;
- }
- getChildren () {
- let children = [];
- if (this.left) {
- children.push(this.left);
- }
- if (this.right) {
- children.push(this.right);
- }
- return children;
- }
- };
- class PointCloudArena4D$1 extends PointCloudTree{
- constructor (geometry) {
- super();
- this.root = null;
- if (geometry.root) {
- this.root = geometry.root;
- } else {
- geometry.addEventListener('hierarchy_loaded', () => {
- this.root = geometry.root;
- });
- }
- this.visiblePointsTarget = 2 * 1000 * 1000;
- this.minimumNodePixelSize = 150;
- this.position.sub(geometry.offset);
- this.updateMatrix();
- this.numVisibleNodes = 0;
- this.numVisiblePoints = 0;
- this.boundingBoxNodes = [];
- this.loadQueue = [];
- this.visibleNodes = [];
- this.pcoGeometry = geometry;
- this.boundingBox = this.pcoGeometry.boundingBox;
- this.boundingSphere = this.pcoGeometry.boundingSphere;
- this.material = new PointCloudMaterial$1({vertexColors: VertexColors, size: 0.05, treeType: TreeType.KDTREE});
- this.material.sizeType = PointSizeType.ATTENUATED;
- this.material.size = 0.05;
- this.profileRequests = [];
- this.name = '';
- }
- getBoundingBoxWorld () {
- this.updateMatrixWorld(true);
- let box = this.boundingBox;
- let transform = this.matrixWorld;
- let tBox = Utils.computeTransformedBoundingBox(box, transform);
- return tBox;
- };
- setName (name) {
- if (this.name !== name) {
- this.name = name;
- this.dispatchEvent({type: 'name_changed', name: name, pointcloud: this});
- }
- }
- getName () {
- return this.name;
- }
- getLevel () {
- return this.level;
- }
- toTreeNode (geometryNode, parent) {
- let node = new PointCloudArena4DNode();
- let sceneNode = new Points(geometryNode.geometry, this.material);
- sceneNode.frustumCulled = false;
- sceneNode.onBeforeRender = (_this, scene, camera, geometry, material, group) => {
- if (material.program) {
- _this.getContext().useProgram(material.program.program);
- if (material.program.getUniforms().map.level) {
- let level = geometryNode.getLevel();
- material.uniforms.level.value = level;
- material.program.getUniforms().map.level.setValue(_this.getContext(), level);
- }
- if (this.visibleNodeTextureOffsets && material.program.getUniforms().map.vnStart) {
- let vnStart = this.visibleNodeTextureOffsets.get(node);
- material.uniforms.vnStart.value = vnStart;
- material.program.getUniforms().map.vnStart.setValue(_this.getContext(), vnStart);
- }
- if (material.program.getUniforms().map.pcIndex) {
- let i = node.pcIndex ? node.pcIndex : this.visibleNodes.indexOf(node);
- material.uniforms.pcIndex.value = i;
- material.program.getUniforms().map.pcIndex.setValue(_this.getContext(), i);
- }
- }
- };
- node.geometryNode = geometryNode;
- node.sceneNode = sceneNode;
- node.pointcloud = this;
- node.left = geometryNode.left;
- node.right = geometryNode.right;
- if (!parent) {
- this.root = node;
- this.add(sceneNode);
- } else {
- parent.sceneNode.add(sceneNode);
- if (parent.left === geometryNode) {
- parent.left = node;
- } else if (parent.right === geometryNode) {
- parent.right = node;
- }
- }
- let disposeListener = function () {
- parent.sceneNode.remove(node.sceneNode);
- if (parent.left === node) {
- parent.left = geometryNode;
- } else if (parent.right === node) {
- parent.right = geometryNode;
- }
- };
- geometryNode.oneTimeDisposeHandlers.push(disposeListener);
- return node;
- }
- updateMaterial (material, visibleNodes, camera, renderer) {
- material.fov = camera.fov * (Math.PI / 180);
- material.screenWidth = renderer.domElement.clientWidth;
- material.screenHeight = renderer.domElement.clientHeight;
- material.spacing = this.pcoGeometry.spacing;
- material.near = camera.near;
- material.far = camera.far;
- // reduce shader source updates by setting maxLevel slightly higher than actually necessary
- if (this.maxLevel > material.levels) {
- material.levels = this.maxLevel + 2;
- }
- // material.uniforms.octreeSize.value = this.boundingBox.size().x;
- let bbSize = this.boundingBox.getSize(new Vector3());
- material.bbSize = [bbSize.x, bbSize.y, bbSize.z];
- }
- updateVisibleBounds () {
- }
- hideDescendants (object) {
- let stack = [];
- for (let i = 0; i < object.children.length; i++) {
- let child = object.children[i];
- if (child.visible) {
- stack.push(child);
- }
- }
- while (stack.length > 0) {
- let child = stack.shift();
- child.visible = false;
- if (child.boundingBoxNode) {
- child.boundingBoxNode.visible = false;
- }
- for (let i = 0; i < child.children.length; i++) {
- let childOfChild = child.children[i];
- if (childOfChild.visible) {
- stack.push(childOfChild);
- }
- }
- }
- }
- updateMatrixWorld (force) {
- // node.matrixWorld.multiplyMatrices( node.parent.matrixWorld, node.matrix );
- if (this.matrixAutoUpdate === true) this.updateMatrix();
- if (this.matrixWorldNeedsUpdate === true || force === true) {
- if (this.parent === undefined) {
- this.matrixWorld.copy(this.matrix);
- } else {
- this.matrixWorld.multiplyMatrices(this.parent.matrixWorld, this.matrix);
- }
- this.matrixWorldNeedsUpdate = false;
- force = true;
- }
- }
- nodesOnRay (nodes, ray) {
- let nodesOnRay = [];
- let _ray = ray.clone();
- for (let i = 0; i < nodes.length; i++) {
- let node = nodes[i];
- let sphere = node.getBoundingSphere().clone().applyMatrix4(node.sceneNode.matrixWorld);
- // TODO Unused: let box = node.getBoundingBox().clone().applyMatrix4(node.sceneNode.matrixWorld);
- if (_ray.intersectsSphere(sphere)) {
- nodesOnRay.push(node);
- }
- // if(_ray.isIntersectionBox(box)){
- // nodesOnRay.push(node);
- // }
- }
- return nodesOnRay;
- }
- pick(viewer, camera, ray, params = {}){
- let renderer = viewer.renderer;
- let pRenderer = viewer.pRenderer;
- performance.mark("pick-start");
- let getVal = (a, b) => a !== undefined ? a : b;
- let pickWindowSize = getVal(params.pickWindowSize, 17);
- let pickOutsideClipRegion = getVal(params.pickOutsideClipRegion, false);
- let size = renderer.getSize(new Vector2$1());
- let width = Math.ceil(getVal(params.width, size.width));
- let height = Math.ceil(getVal(params.height, size.height));
- let pointSizeType = getVal(params.pointSizeType, this.material.pointSizeType);
- let pointSize = getVal(params.pointSize, this.material.size);
- let nodes = this.nodesOnRay(this.visibleNodes, ray);
- if (nodes.length === 0) {
- return null;
- }
- if (!this.pickState) {
- let scene = new Scene();
- let material = new PointCloudMaterial$1();
- material.activeAttributeName = "indices";
- let renderTarget = new WebGLRenderTarget(
- 1, 1,
- { minFilter: LinearFilter,
- magFilter: NearestFilter,
- format: RGBAFormat }
- );
- this.pickState = {
- renderTarget: renderTarget,
- material: material,
- scene: scene
- };
- };
- let pickState = this.pickState;
- let pickMaterial = pickState.material;
- { // update pick material
- pickMaterial.pointSizeType = pointSizeType;
- pickMaterial.shape = this.material.shape;
- pickMaterial.size = pointSize;
- pickMaterial.uniforms.minSize.value = this.material.uniforms.minSize.value;
- pickMaterial.uniforms.maxSize.value = this.material.uniforms.maxSize.value;
- pickMaterial.classification = this.material.classification;
- if(params.pickClipped){
- pickMaterial.clipBoxes = this.material.clipBoxes;
- if(this.material.clipTask === ClipTask.HIGHLIGHT){
- pickMaterial.clipTask = ClipTask.NONE;
- }else {
- pickMaterial.clipTask = this.material.clipTask;
- }
- }else {
- pickMaterial.clipBoxes = [];
- }
-
- this.updateMaterial(pickMaterial, nodes, camera, renderer);
- }
- pickState.renderTarget.setSize(width, height);
- let pixelPos = new Vector2$1(params.x, params.y);
-
- let gl = renderer.getContext();
- gl.enable(gl.SCISSOR_TEST);
- gl.scissor(
- parseInt(pixelPos.x - (pickWindowSize - 1) / 2),
- parseInt(pixelPos.y - (pickWindowSize - 1) / 2),
- parseInt(pickWindowSize), parseInt(pickWindowSize));
- renderer.state.buffers.depth.setTest(pickMaterial.depthTest);
- renderer.state.buffers.depth.setMask(pickMaterial.depthWrite);
- renderer.state.setBlending(NoBlending);
- renderer.clearTarget(pickState.renderTarget, true, true, true);
- { // RENDER
- renderer.setRenderTarget(pickState.renderTarget);
- gl.clearColor(0, 0, 0, 0);
- renderer.clearTarget( pickState.renderTarget, true, true, true );
-
- let tmp = this.material;
- this.material = pickMaterial;
-
- pRenderer.renderOctree(this, nodes, camera, pickState.renderTarget);
-
- this.material = tmp;
- }
- let clamp = (number, min, max) => Math.min(Math.max(min, number), max);
- let x = parseInt(clamp(pixelPos.x - (pickWindowSize - 1) / 2, 0, width));
- let y = parseInt(clamp(pixelPos.y - (pickWindowSize - 1) / 2, 0, height));
- let w = parseInt(Math.min(x + pickWindowSize, width) - x);
- let h = parseInt(Math.min(y + pickWindowSize, height) - y);
- let pixelCount = w * h;
- let buffer = new Uint8Array(4 * pixelCount);
-
- gl.readPixels(x, y, pickWindowSize, pickWindowSize, gl.RGBA, gl.UNSIGNED_BYTE, buffer);
-
- renderer.setRenderTarget(null);
- renderer.state.reset();
- renderer.setScissorTest(false);
- gl.disable(gl.SCISSOR_TEST);
-
- let pixels = buffer;
- let ibuffer = new Uint32Array(buffer.buffer);
- // find closest hit inside pixelWindow boundaries
- let min = Number.MAX_VALUE;
- let hits = [];
- for (let u = 0; u < pickWindowSize; u++) {
- for (let v = 0; v < pickWindowSize; v++) {
- let offset = (u + v * pickWindowSize);
- let distance = Math.pow(u - (pickWindowSize - 1) / 2, 2) + Math.pow(v - (pickWindowSize - 1) / 2, 2);
- let pcIndex = pixels[4 * offset + 3];
- pixels[4 * offset + 3] = 0;
- let pIndex = ibuffer[offset];
- if(!(pcIndex === 0 && pIndex === 0) && (pcIndex !== undefined) && (pIndex !== undefined)){
- let hit = {
- pIndex: pIndex,
- pcIndex: pcIndex,
- distanceToCenter: distance
- };
- if(params.all){
- hits.push(hit);
- }else {
- if(hits.length > 0){
- if(distance < hits[0].distanceToCenter){
- hits[0] = hit;
- }
- }else {
- hits.push(hit);
- }
- }
-
- }
- }
- }
- for(let hit of hits){
- let point = {};
-
- if (!nodes[hit.pcIndex]) {
- return null;
- }
-
- let node = nodes[hit.pcIndex];
- let pc = node.sceneNode;
- let geometry = node.geometryNode.geometry;
-
- for(let attributeName in geometry.attributes){
- let attribute = geometry.attributes[attributeName];
-
- if (attributeName === 'position') {
- let x = attribute.array[3 * hit.pIndex + 0];
- let y = attribute.array[3 * hit.pIndex + 1];
- let z = attribute.array[3 * hit.pIndex + 2];
-
- let position = new Vector3(x, y, z);
- position.applyMatrix4(pc.matrixWorld);
-
- point[attributeName] = position;
- } else if (attributeName === 'indices') {
-
- } else {
- //if (values.itemSize === 1) {
- // point[attribute.name] = values.array[hit.pIndex];
- //} else {
- // let value = [];
- // for (let j = 0; j < values.itemSize; j++) {
- // value.push(values.array[values.itemSize * hit.pIndex + j]);
- // }
- // point[attribute.name] = value;
- //}
- }
-
- }
- hit.point = point;
- }
- performance.mark("pick-end");
- performance.measure("pick", "pick-start", "pick-end");
- if(params.all){
- return hits.map(hit => hit.point);
- }else {
- if(hits.length === 0){
- return null;
- }else {
- return hits[0].point;
- }
- }
- }
- computeVisibilityTextureData(nodes){
- if(exports.measureTimings) performance.mark("computeVisibilityTextureData-start");
- let data = new Uint8Array(nodes.length * 3);
- let visibleNodeTextureOffsets = new Map();
- // copy array
- nodes = nodes.slice();
- // sort by level and number
- let sort = function (a, b) {
- let la = a.geometryNode.level;
- let lb = b.geometryNode.level;
- let na = a.geometryNode.number;
- let nb = b.geometryNode.number;
- if (la !== lb) return la - lb;
- if (na < nb) return -1;
- if (na > nb) return 1;
- return 0;
- };
- nodes.sort(sort);
- let visibleNodeNames = [];
- for (let i = 0; i < nodes.length; i++) {
- visibleNodeNames.push(nodes[i].geometryNode.number);
- }
- for (let i = 0; i < nodes.length; i++) {
- let node = nodes[i];
- visibleNodeTextureOffsets.set(node, i);
- let b1 = 0; // children
- let b2 = 0; // offset to first child
- let b3 = 0; // split
- if (node.geometryNode.left && visibleNodeNames.indexOf(node.geometryNode.left.number) > 0) {
- b1 += 1;
- b2 = visibleNodeNames.indexOf(node.geometryNode.left.number) - i;
- }
- if (node.geometryNode.right && visibleNodeNames.indexOf(node.geometryNode.right.number) > 0) {
- b1 += 2;
- b2 = (b2 === 0) ? visibleNodeNames.indexOf(node.geometryNode.right.number) - i : b2;
- }
- if (node.geometryNode.split === 'X') {
- b3 = 1;
- } else if (node.geometryNode.split === 'Y') {
- b3 = 2;
- } else if (node.geometryNode.split === 'Z') {
- b3 = 4;
- }
- data[i * 3 + 0] = b1;
- data[i * 3 + 1] = b2;
- data[i * 3 + 2] = b3;
- }
- if(exports.measureTimings){
- performance.mark("computeVisibilityTextureData-end");
- performance.measure("render.computeVisibilityTextureData", "computeVisibilityTextureData-start", "computeVisibilityTextureData-end");
- }
- return {
- data: data,
- offsets: visibleNodeTextureOffsets
- };
- }
- get progress () {
- if (this.pcoGeometry.root) {
- return exports.numNodesLoading > 0 ? 0 : 1;
- } else {
- return 0;
- }
- }
- };
- // Copied from three.js: WebGLRenderer.js
- function paramThreeToGL(_gl, p) {
- let extension;
- if (p === RepeatWrapping) return _gl.REPEAT;
- if (p === ClampToEdgeWrapping) return _gl.CLAMP_TO_EDGE;
- if (p === MirroredRepeatWrapping) return _gl.MIRRORED_REPEAT;
- if (p === NearestFilter) return _gl.NEAREST;
- if (p === NearestMipMapNearestFilter) return _gl.NEAREST_MIPMAP_NEAREST;
- if (p === NearestMipMapLinearFilter) return _gl.NEAREST_MIPMAP_LINEAR;
- if (p === LinearFilter) return _gl.LINEAR;
- if (p === LinearMipMapNearestFilter) return _gl.LINEAR_MIPMAP_NEAREST;
- if (p === LinearMipMapLinearFilter) return _gl.LINEAR_MIPMAP_LINEAR;
- if (p === UnsignedByteType) return _gl.UNSIGNED_BYTE;
- if (p === UnsignedShort4444Type) return _gl.UNSIGNED_SHORT_4_4_4_4;
- if (p === UnsignedShort5551Type) return _gl.UNSIGNED_SHORT_5_5_5_1;
- if (p === UnsignedShort565Type) return _gl.UNSIGNED_SHORT_5_6_5;
- if (p === ByteType) return _gl.BYTE;
- if (p === ShortType) return _gl.SHORT;
- if (p === UnsignedShortType) return _gl.UNSIGNED_SHORT;
- if (p === IntType) return _gl.INT;
- if (p === UnsignedIntType) return _gl.UNSIGNED_INT;
- if (p === FloatType) return _gl.FLOAT;
- if (p === HalfFloatType) {
- extension = extensions.get('OES_texture_half_float');
- if (extension !== null) return extension.HALF_FLOAT_OES;
- }
- if (p === AlphaFormat) return _gl.ALPHA;
- if (p === RGBFormat) return _gl.RGB;
- if (p === RGBAFormat) return _gl.RGBA;
- if (p === LuminanceFormat) return _gl.LUMINANCE;
- if (p === LuminanceAlphaFormat) return _gl.LUMINANCE_ALPHA;
- if (p === DepthFormat) return _gl.DEPTH_COMPONENT;
- if (p === DepthStencilFormat) return _gl.DEPTH_STENCIL;
- if (p === AddEquation) return _gl.FUNC_ADD;
- if (p === SubtractEquation) return _gl.FUNC_SUBTRACT;
- if (p === ReverseSubtractEquation) return _gl.FUNC_REVERSE_SUBTRACT;
- if (p === ZeroFactor) return _gl.ZERO;
- if (p === OneFactor) return _gl.ONE;
- if (p === SrcColorFactor) return _gl.SRC_COLOR;
- if (p === OneMinusSrcColorFactor) return _gl.ONE_MINUS_SRC_COLOR;
- if (p === SrcAlphaFactor) return _gl.SRC_ALPHA;
- if (p === OneMinusSrcAlphaFactor) return _gl.ONE_MINUS_SRC_ALPHA;
- if (p === DstAlphaFactor) return _gl.DST_ALPHA;
- if (p === OneMinusDstAlphaFactor) return _gl.ONE_MINUS_DST_ALPHA;
- if (p === DstColorFactor) return _gl.DST_COLOR;
- if (p === OneMinusDstColorFactor) return _gl.ONE_MINUS_DST_COLOR;
- if (p === SrcAlphaSaturateFactor) return _gl.SRC_ALPHA_SATURATE;
- if (p === RGB_S3TC_DXT1_Format || p === RGBA_S3TC_DXT1_Format ||
- p === RGBA_S3TC_DXT3_Format || p === RGBA_S3TC_DXT5_Format) {
- extension = extensions.get('WEBGL_compressed_texture_s3tc');
- if (extension !== null) {
- if (p === RGB_S3TC_DXT1_Format) return extension.COMPRESSED_RGB_S3TC_DXT1_EXT;
- if (p === RGBA_S3TC_DXT1_Format$1) return extension.COMPRESSED_RGBA_S3TC_DXT1_EXT;
- if (p === RGBA_S3TC_DXT3_Format) return extension.COMPRESSED_RGBA_S3TC_DXT3_EXT;
- if (p === RGBA_S3TC_DXT5_Format$1) return extension.COMPRESSED_RGBA_S3TC_DXT5_EXT;
- }
- }
- if (p === RGB_PVRTC_4BPPV1_Format || p === RGB_PVRTC_2BPPV1_Format ||
- p === RGBA_PVRTC_4BPPV1_Format || p === RGBA_PVRTC_2BPPV1_Format) {
- extension = extensions.get('WEBGL_compressed_texture_pvrtc');
- if (extension !== null) {
- if (p === RGB_PVRTC_4BPPV1_Format) return extension.COMPRESSED_RGB_PVRTC_4BPPV1_IMG;
- if (p === RGB_PVRTC_2BPPV1_Format) return extension.COMPRESSED_RGB_PVRTC_2BPPV1_IMG;
- if (p === RGBA_PVRTC_4BPPV1_Format) return extension.COMPRESSED_RGBA_PVRTC_4BPPV1_IMG;
- if (p === RGBA_PVRTC_2BPPV1_Format) return extension.COMPRESSED_RGBA_PVRTC_2BPPV1_IMG;
- }
- }
- if (p === RGB_ETC1_Format) {
- extension = extensions.get('WEBGL_compressed_texture_etc1');
- if (extension !== null) return extension.COMPRESSED_RGB_ETC1_WEBGL;
- }
- if (p === MinEquation || p === MaxEquation) {
- extension = extensions.get('EXT_blend_minmax');
- if (extension !== null) {
- if (p === MinEquation) return extension.MIN_EXT;
- if (p === MaxEquation) return extension.MAX_EXT;
- }
- }
- if (p === UnsignedInt248Type) {
- extension = extensions.get('WEBGL_depth_texture');
- if (extension !== null) return extension.UNSIGNED_INT_24_8_WEBGL;
- }
- return 0;
- };
- let attributeLocations = {
- "position": {name: "position", location: 0},
- "color": {name: "color", location: 1},
- "rgba": {name: "color", location: 1},
- "intensity": {name: "intensity", location: 2},
- "classification": {name: "classification", location: 3},
- "returnNumber": {name: "returnNumber", location: 4},
- "return number": {name: "returnNumber", location: 4},
- "returns": {name: "returnNumber", location: 4},
- "numberOfReturns": {name: "numberOfReturns", location: 5},
- "number of returns": {name: "numberOfReturns", location: 5},
- "pointSourceID": {name: "pointSourceID", location: 6},
- "source id": {name: "pointSourceID", location: 6},
- "point source id": {name: "pointSourceID", location: 6},
- "indices": {name: "indices", location: 7},
- "normal": {name: "normal", location: 8},
- "spacing": {name: "spacing", location: 9},
- "gps-time": {name: "gpsTime", location: 10},
- "aExtra": {name: "aExtra", location: 11},
- };
- class Shader {
- constructor(gl, name, vsSource, fsSource) {
- this.gl = gl;
- this.name = name;
- this.vsSource = vsSource;
- this.fsSource = fsSource;
- this.cache = new Map();
- this.vs = null;
- this.fs = null;
- this.program = null;
- this.uniformLocations = {};
- this.attributeLocations = {};
- this.uniformBlockIndices = {};
- this.uniformBlocks = {};
- this.uniforms = {};
- this.update(vsSource, fsSource);
- }
- update(vsSource, fsSource) {
- this.vsSource = vsSource;
- this.fsSource = fsSource;
- this.linkProgram();
- }
- compileShader(shader, source){
- let gl = this.gl;
- gl.shaderSource(shader, source);
- gl.compileShader(shader);
- let success = gl.getShaderParameter(shader, gl.COMPILE_STATUS);
- if (!success) {
- let info = gl.getShaderInfoLog(shader);
- let numberedSource = source.split("\n").map((a, i) => `${i + 1}`.padEnd(5) + a).join("\n");
- throw `could not compile shader ${this.name}: ${info}, \n${numberedSource}`;
- }
- }
- linkProgram() {
- const tStart = performance.now();
- let gl = this.gl;
- this.uniformLocations = {};
- this.attributeLocations = {};
- this.uniforms = {};
- gl.useProgram(null);
- let cached = this.cache.get(`${this.vsSource}, ${this.fsSource}`);
- if (cached) {
- this.program = cached.program;
- this.vs = cached.vs;
- this.fs = cached.fs;
- this.attributeLocations = cached.attributeLocations;
- this.uniformLocations = cached.uniformLocations;
- this.uniformBlocks = cached.uniformBlocks;
- this.uniforms = cached.uniforms;
- return;
- } else {
- this.vs = gl.createShader(gl.VERTEX_SHADER);
- this.fs = gl.createShader(gl.FRAGMENT_SHADER);
-
-
-
-
- this.program = gl.createProgram();
-
- if( !gl.isProgram(this.program )){//创建失败 开启多个页面可能会,原因是webglcontextlost
- //console.error('创建program失败');
- viewer.dispatchEvent('webglError', {msg: 'potreeRenderer创建program失败'});
- console.log(this.vs);
- console.log(this.fs);
-
- return;
- }
- for(let name of Object.keys(attributeLocations)){
- let location = attributeLocations[name].location;
- let glslName = attributeLocations[name].name;
- gl.bindAttribLocation(this.program, location, glslName);
- }
- this.compileShader(this.vs, this.vsSource);
- this.compileShader(this.fs, this.fsSource);
- let program = this.program;
- gl.attachShader(program, this.vs);
- gl.attachShader(program, this.fs);
- gl.linkProgram(program);
- gl.detachShader(program, this.vs);
- gl.detachShader(program, this.fs);
-
- // 检测当前程序链接状态
- let success = gl.getProgramParameter(program, gl.LINK_STATUS);
- if (!success) {
- let info = gl.getProgramInfoLog(program);
- throw `could not link program ${this.name}: ${info}`;
- }
- { // attribute locations
- let numAttributes = gl.getProgramParameter(program, gl.ACTIVE_ATTRIBUTES);
- for (let i = 0; i < numAttributes; i++) {
- let attribute = gl.getActiveAttrib(program, i);
- let location = gl.getAttribLocation(program, attribute.name);
- this.attributeLocations[attribute.name] = location;
- }
- }
- { // uniform locations
- let numUniforms = gl.getProgramParameter(program, gl.ACTIVE_UNIFORMS);
- for (let i = 0; i < numUniforms; i++) {
- let uniform = gl.getActiveUniform(program, i);
- let location = gl.getUniformLocation(program, uniform.name);
- this.uniformLocations[uniform.name] = location;
- this.uniforms[uniform.name] = {
- location: location,
- value: null,
- };
- }
- }
- // uniform blocks
- if( typeof WebGL2RenderingContext !== 'undefined' && gl instanceof WebGL2RenderingContext){ //WebGL2RenderingContext在mac的safari14以下是没有定义的
- let numBlocks = gl.getProgramParameter(program, gl.ACTIVE_UNIFORM_BLOCKS);
- for (let i = 0; i < numBlocks; i++) {
- let blockName = gl.getActiveUniformBlockName(program, i);
- let blockIndex = gl.getUniformBlockIndex(program, blockName);
- this.uniformBlockIndices[blockName] = blockIndex;
- gl.uniformBlockBinding(program, blockIndex, blockIndex);
- let dataSize = gl.getActiveUniformBlockParameter(program, blockIndex, gl.UNIFORM_BLOCK_DATA_SIZE);
- let uBuffer = gl.createBuffer();
- gl.bindBuffer(gl.UNIFORM_BUFFER, uBuffer);
- gl.bufferData(gl.UNIFORM_BUFFER, dataSize, gl.DYNAMIC_READ);
- gl.bindBufferBase(gl.UNIFORM_BUFFER, blockIndex, uBuffer);
- gl.bindBuffer(gl.UNIFORM_BUFFER, null);
- this.uniformBlocks[blockName] = {
- name: blockName,
- index: blockIndex,
- dataSize: dataSize,
- buffer: uBuffer
- };
- }
- }
- let cached = {
- program: this.program,
- vs: this.vs,
- fs: this.fs,
- attributeLocations: this.attributeLocations,
- uniformLocations: this.uniformLocations,
- uniforms: this.uniforms,
- uniformBlocks: this.uniformBlocks,
- };
- this.cache.set(`${this.vsSource}, ${this.fsSource}`, cached);
- }
- const tEnd = performance.now();
- const duration = tEnd - tStart;
- //console.log(`shader compile duration: ${duration.toFixed(3)}`);
- }
- setUniformMatrix4(name, value) {
- const gl = this.gl;
- const location = this.uniformLocations[name];
- if (location == null) {
- return;
- }
- let tmp = new Float32Array(value.elements);
- gl.uniformMatrix4fv(location, false, tmp);
- }
- setUniform1f(name, value) {
- const gl = this.gl;
- const uniform = this.uniforms[name];
- if (uniform === undefined) {
- return;
- }
- if(uniform.value === value){
- return;
- }
- uniform.value = value;
- gl.uniform1f(uniform.location, value);
- }
- setUniformBoolean(name, value) {
- const gl = this.gl;
- const uniform = this.uniforms[name];
- if (uniform === undefined) {
- return;
- }
- if(uniform.value === value){
- return;
- }
- uniform.value = value;
- gl.uniform1i(uniform.location, value);
- }
- setUniformTexture(name, value) {
- const gl = this.gl;
- const location = this.uniformLocations[name];
- if (location == null) {
- return;
- }
- gl.uniform1i(location, value);
- }
- setUniform2f(name, value) {
- const gl = this.gl;
- const location = this.uniformLocations[name];
- if (location == null) {
- return;
- }
- gl.uniform2f(location, value[0], value[1]);
- }
- setUniform3f(name, value) {
- const gl = this.gl;
- const location = this.uniformLocations[name];
- if (location == null) {
- return;
- }
- gl.uniform3f(location, value[0], value[1], value[2]);
- }
- setUniform(name, value) {
- if (value.constructor === Matrix4) {
- this.setUniformMatrix4(name, value);
- } else if (typeof value === "number") {
- this.setUniform1f(name, value);
- } else if (typeof value === "boolean") {
- this.setUniformBoolean(name, value);
- } else if (value instanceof WebGLTexture) {
- this.setUniformTexture(name, value);
- } else if (value instanceof Array) {
- if (value.length === 2) {
- this.setUniform2f(name, value);
- } else if (value.length === 3) {
- this.setUniform3f(name, value);
- }
- } else {
- console.error("unhandled uniform type: ", name, value);
- }
- }
- setUniform1i(name, value) {
- let gl = this.gl;
- let location = this.uniformLocations[name];
- if (location == null) {
- return;
- }
- gl.uniform1i(location, value);
- }
- };
- class WebGLTexture {
- constructor(gl, texture) {
- this.gl = gl;
- this.texture = texture;
- this.id = gl.createTexture();
- this.target = gl.TEXTURE_2D;
- this.version = -1;
- this.update(texture);
- }
- update() {
- if (!this.texture.image) {
- this.version = this.texture.version;
- return;
- }
- let gl = this.gl;
- let texture = this.texture;
- if (this.version === texture.version) {
- return;
- }
- this.target = gl.TEXTURE_2D;
- gl.bindTexture(this.target, this.id);
- let level = 0;
- let internalFormat = paramThreeToGL(gl, texture.format);
- let width = texture.image.width;
- let height = texture.image.height;
- let border = 0;
- let srcFormat = internalFormat;
- let srcType = paramThreeToGL(gl, texture.type);
- let data;
- gl.pixelStorei(gl.UNPACK_FLIP_Y_WEBGL, texture.flipY);
- gl.pixelStorei(gl.UNPACK_PREMULTIPLY_ALPHA_WEBGL, texture.premultiplyAlpha);
- gl.pixelStorei(gl.UNPACK_ALIGNMENT, texture.unpackAlignment);
- if (texture instanceof DataTexture) {
- data = texture.image.data;
- gl.texParameteri(this.target, gl.TEXTURE_WRAP_S, gl.CLAMP_TO_EDGE);
- gl.texParameteri(this.target, gl.TEXTURE_WRAP_T, gl.CLAMP_TO_EDGE);
- gl.texParameteri(this.target, gl.TEXTURE_MAG_FILTER, paramThreeToGL(gl, texture.magFilter));
- gl.texParameteri(this.target, gl.TEXTURE_MIN_FILTER, paramThreeToGL(gl, texture.minFilter));
- gl.texImage2D(this.target, level, internalFormat,
- width, height, border, srcFormat, srcType,
- data);
- }/* else if(texture instanceof THREE.CubeTexture){//add
- } */else if ((texture instanceof CanvasTexture) || (texture instanceof Texture)) {
- data = texture.image;
- gl.texParameteri(this.target, gl.TEXTURE_WRAP_S, paramThreeToGL(gl, texture.wrapS));
- gl.texParameteri(this.target, gl.TEXTURE_WRAP_T, paramThreeToGL(gl, texture.wrapT));
- gl.texParameteri(this.target, gl.TEXTURE_MAG_FILTER, paramThreeToGL(gl, texture.magFilter));
- gl.texParameteri(this.target, gl.TEXTURE_MIN_FILTER, paramThreeToGL(gl, texture.minFilter));
- gl.texImage2D(this.target, level, internalFormat,
- internalFormat, srcType, data);
- if (texture instanceof Texture) {gl.generateMipmap(gl.TEXTURE_2D);}
- }
- gl.bindTexture(this.target, null);
- this.version = texture.version;
- }
- };
- class WebGLBuffer {
- constructor() {
- this.numElements = 0;
- this.vao = null;
- this.vbos = new Map();
- }
- };
- class Renderer {
- constructor(threeRenderer) {
- this.threeRenderer = threeRenderer;
- this.gl = this.threeRenderer.getContext();
-
- this.buffers = new Map();
- this.shaders = new Map();
- this.textures = new Map();
- this.glTypeMapping = new Map();
- this.glTypeMapping.set(Float32Array, this.gl.FLOAT);
- this.glTypeMapping.set(Uint8Array, this.gl.UNSIGNED_BYTE);
- this.glTypeMapping.set(Uint16Array, this.gl.UNSIGNED_SHORT);
- this.toggle = 0;
- }
- deleteBuffer(geometry) {
- let gl = this.gl;
- let webglBuffer = this.buffers.get(geometry);
- if (webglBuffer != null) {
- for (let attributeName in geometry.attributes) {
- gl.deleteBuffer(webglBuffer.vbos.get(attributeName).handle);
- }
- this.buffers.delete(geometry);
- }
- }
- createBuffer(geometry){
- let gl = this.gl;
- let webglBuffer = new WebGLBuffer();
- webglBuffer.vao = gl.createVertexArray();
- webglBuffer.numElements = geometry.attributes.position.count;
- gl.bindVertexArray(webglBuffer.vao);
- for(let attributeName in geometry.attributes){
- let bufferAttribute = geometry.attributes[attributeName];
- let vbo = gl.createBuffer();
- gl.bindBuffer(gl.ARRAY_BUFFER, vbo);
- gl.bufferData(gl.ARRAY_BUFFER, bufferAttribute.array, gl.STATIC_DRAW);
- let normalized = bufferAttribute.normalized;
- let type = this.glTypeMapping.get(bufferAttribute.array.constructor);
- if(attributeLocations[attributeName] === undefined){
- //attributeLocation = attributeLocations["aExtra"];
- }else {
- let attributeLocation = attributeLocations[attributeName].location;
- gl.vertexAttribPointer(attributeLocation, bufferAttribute.itemSize, type, normalized, 0, 0);
- gl.enableVertexAttribArray(attributeLocation);
- }
- webglBuffer.vbos.set(attributeName, {
- handle: vbo,
- name: attributeName,
- count: bufferAttribute.count,
- itemSize: bufferAttribute.itemSize,
- type: geometry.attributes.position.array.constructor,
- version: 0
- });
- }
- gl.bindBuffer(gl.ARRAY_BUFFER, null);
- gl.bindVertexArray(null);
- let disposeHandler = (event) => {
- this.deleteBuffer(geometry);
- geometry.removeEventListener("dispose", disposeHandler);
- };
- geometry.addEventListener("dispose", disposeHandler);
- return webglBuffer;
- }
- updateBuffer(geometry){
- let gl = this.gl;
- let webglBuffer = this.buffers.get(geometry);
- gl.bindVertexArray(webglBuffer.vao);
- for(let attributeName in geometry.attributes){
- let bufferAttribute = geometry.attributes[attributeName];
- let normalized = bufferAttribute.normalized;
- let type = this.glTypeMapping.get(bufferAttribute.array.constructor);
- let vbo = null;
- if(!webglBuffer.vbos.has(attributeName)){
- vbo = gl.createBuffer();
- webglBuffer.vbos.set(attributeName, {
- handle: vbo,
- name: attributeName,
- count: bufferAttribute.count,
- itemSize: bufferAttribute.itemSize,
- type: geometry.attributes.position.array.constructor,
- version: bufferAttribute.version
- });
- }else {
- vbo = webglBuffer.vbos.get(attributeName).handle;
- webglBuffer.vbos.get(attributeName).version = bufferAttribute.version;
- }
- gl.bindBuffer(gl.ARRAY_BUFFER, vbo);
- gl.bufferData(gl.ARRAY_BUFFER, bufferAttribute.array, gl.STATIC_DRAW);
- if(attributeLocations[attributeName] === undefined){
- //attributeLocation = attributeLocations["aExtra"];
- }else {
- let attributeLocation = attributeLocations[attributeName].location;
-
- gl.vertexAttribPointer(attributeLocation, bufferAttribute.itemSize, type, normalized, 0, 0);
- gl.enableVertexAttribArray(attributeLocation);
- }
- }
- gl.bindBuffer(gl.ARRAY_BUFFER, null);
- gl.bindVertexArray(null);
- }
- traverse(scene) {
- let octrees = [];
- let stack = [scene];
- while (stack.length > 0) {
- let node = stack.pop();
- if (node instanceof PointCloudTree) {
- octrees.push(node);
- continue;
- }
- let visibleChildren = node.children.filter(c => c.visible);
- stack.push(...visibleChildren);
- }
- let result = {
- octrees: octrees
- };
- return result;
- }
- renderNodes(octree, nodes, visibilityTextureData, camera, target, shader, params) {
- if (exports.measureTimings) performance.mark("renderNodes-start");
- let gl = this.gl;
- let material = params.material ? params.material : octree.material;
- let shadowMaps = params.shadowMaps == null ? [] : params.shadowMaps;
- let view = camera.matrixWorldInverse;
- if(params.viewOverride){
- view = params.viewOverride;
- }
- let worldView = new Matrix4();
- let mat4holder = new Float32Array(16);
- let i = 0;
- for (let node of nodes) {
- if(exports.debug.allowedNodes !== undefined){
- if(!exports.debug.allowedNodes.includes(node.name)){
- continue;
- }
- }
- let world = node.sceneNode.matrixWorld;
- worldView.multiplyMatrices(view, world);
- if (visibilityTextureData) {
- let vnStart = visibilityTextureData.offsets.get(node);
- shader.setUniform1f("uVNStart", vnStart);
- }
- let level = node.getLevel();
- if(node.debug){
- shader.setUniform("uDebug", true);
- }else {
- shader.setUniform("uDebug", false);
- }
- // let isLeaf = false;
- // if(node instanceof PointCloudOctreeNode){
- // isLeaf = Object.keys(node.children).length === 0;
- // }else if(node instanceof PointCloudArena4DNode){
- // isLeaf = node.geometryNode.isLeaf;
- // }
- // shader.setUniform("uIsLeafNode", isLeaf);
- // let isLeaf = node.children.filter(n => n != null).length === 0;
- // if(!isLeaf){
- // continue;
- // }
- // TODO consider passing matrices in an array to avoid uniformMatrix4fv overhead
- const lModel = shader.uniformLocations["modelMatrix"];
- if (lModel) {
- mat4holder.set(world.elements);
- gl.uniformMatrix4fv(lModel, false, mat4holder);
- }
- const lModelView = shader.uniformLocations["modelViewMatrix"];
- //mat4holder.set(worldView.elements);
- // faster then set in chrome 63
- for(let j = 0; j < 16; j++){
- mat4holder[j] = worldView.elements[j];
- }
- gl.uniformMatrix4fv(lModelView, false, mat4holder);
- { // Clip Polygons
- if(material.clipPolygons && material.clipPolygons.length > 0){
- let clipPolygonVCount = [];
- let worldViewProjMatrices = [];
- for(let clipPolygon of material.clipPolygons){
- let view = clipPolygon.viewMatrix;
- let proj = clipPolygon.projMatrix;
- let worldViewProj = proj.clone().multiply(view).multiply(world);
- clipPolygonVCount.push(clipPolygon.markers.length);
- worldViewProjMatrices.push(worldViewProj);
- }
- let flattenedMatrices = [].concat(...worldViewProjMatrices.map(m => m.elements));
- let flattenedVertices = new Array(8 * 3 * material.clipPolygons.length);
- for(let i = 0; i < material.clipPolygons.length; i++){
- let clipPolygon = material.clipPolygons[i];
- for(let j = 0; j < clipPolygon.markers.length; j++){
- flattenedVertices[i * 24 + (j * 3 + 0)] = clipPolygon.markers[j].position.x;
- flattenedVertices[i * 24 + (j * 3 + 1)] = clipPolygon.markers[j].position.y;
- flattenedVertices[i * 24 + (j * 3 + 2)] = clipPolygon.markers[j].position.z;
- }
- }
- const lClipPolygonVCount = shader.uniformLocations["uClipPolygonVCount[0]"];
- gl.uniform1iv(lClipPolygonVCount, clipPolygonVCount);
- const lClipPolygonVP = shader.uniformLocations["uClipPolygonWVP[0]"];
- gl.uniformMatrix4fv(lClipPolygonVP, false, flattenedMatrices);
- const lClipPolygons = shader.uniformLocations["uClipPolygonVertices[0]"];
- gl.uniform3fv(lClipPolygons, flattenedVertices);
- }
- }
- //shader.setUniformMatrix4("modelMatrix", world);
- //shader.setUniformMatrix4("modelViewMatrix", worldView);
- shader.setUniform1f("uLevel", level);
- shader.setUniform1f("uNodeSpacing", node.geometryNode.estimatedSpacing);
- shader.setUniform1f("uPCIndex", i);
- // uBBSize
- if (shadowMaps.length > 0) {
- const lShadowMap = shader.uniformLocations["uShadowMap[0]"];
- shader.setUniform3f("uShadowColor", material.uniforms.uShadowColor.value);
- let bindingStart = 5;
- let bindingPoints = new Array(shadowMaps.length).fill(bindingStart).map((a, i) => (a + i));
- gl.uniform1iv(lShadowMap, bindingPoints);
- for (let i = 0; i < shadowMaps.length; i++) {
- let shadowMap = shadowMaps[i];
- let bindingPoint = bindingPoints[i];
- let glTexture = this.threeRenderer.properties.get(shadowMap.target.texture).__webglTexture;
- gl.activeTexture(gl[`TEXTURE${bindingPoint}`]);
- gl.bindTexture(gl.TEXTURE_2D, glTexture);
- }
- {
- let worldViewMatrices = shadowMaps
- .map(sm => sm.camera.matrixWorldInverse)
- .map(view => new Matrix4().multiplyMatrices(view, world));
- let flattenedMatrices = [].concat(...worldViewMatrices.map(c => c.elements));
- const lWorldView = shader.uniformLocations["uShadowWorldView[0]"];
- gl.uniformMatrix4fv(lWorldView, false, flattenedMatrices);
- }
- {
- let flattenedMatrices = [].concat(...shadowMaps.map(sm => sm.camera.projectionMatrix.elements));
- const lProj = shader.uniformLocations["uShadowProj[0]"];
- gl.uniformMatrix4fv(lProj, false, flattenedMatrices);
- }
- }
- const geometry = node.geometryNode.geometry;
- if(geometry.attributes["gps-time"]){
- const bufferAttribute = geometry.attributes["gps-time"];
- const attGPS = octree.getAttribute("gps-time");
- let initialRange = attGPS.initialRange;
- let initialRangeSize = initialRange[1] - initialRange[0];
- let globalRange = attGPS.range;
- let globalRangeSize = globalRange[1] - globalRange[0];
- let scale = initialRangeSize / globalRangeSize;
- let offset = -(globalRange[0] - initialRange[0]) / initialRangeSize;
- scale = Number.isNaN(scale) ? 1 : scale;
- offset = Number.isNaN(offset) ? 0 : offset;
- shader.setUniform1f("uGpsScale", scale);
- shader.setUniform1f("uGpsOffset", offset);
- //shader.setUniform2f("uFilterGPSTimeClipRange", [-Infinity, Infinity]);
- let uFilterGPSTimeClipRange = material.uniforms.uFilterGPSTimeClipRange.value;
- // let gpsCliPRangeMin = uFilterGPSTimeClipRange[0]
- // let gpsCliPRangeMax = uFilterGPSTimeClipRange[1]
- // shader.setUniform2f("uFilterGPSTimeClipRange", [gpsCliPRangeMin, gpsCliPRangeMax]);
- let normalizedClipRange = [
- (uFilterGPSTimeClipRange[0] - globalRange[0]) / globalRangeSize,
- (uFilterGPSTimeClipRange[1] - globalRange[0]) / globalRangeSize,
- ];
- shader.setUniform2f("uFilterGPSTimeClipRange", normalizedClipRange);
- // // ranges in full gps coordinate system
- // const globalRange = attGPS.range;
- // const bufferRange = bufferAttribute.potree.range;
- // // ranges in [0, 1]
- // // normalizedGlobalRange = [0, 1]
- // // normalizedBufferRange: norm buffer within norm global range e.g. [0.2, 0.8]
- // const globalWidth = globalRange[1] - globalRange[0];
- // const normalizedBufferRange = [
- // (bufferRange[0] - globalRange[0]) / globalWidth,
- // (bufferRange[1] - globalRange[0]) / globalWidth,
- // ];
- // shader.setUniform2f("uNormalizedGpsBufferRange", normalizedBufferRange);
- // let uFilterGPSTimeClipRange = material.uniforms.uFilterGPSTimeClipRange.value;
- // let gpsCliPRangeMin = uFilterGPSTimeClipRange[0]
- // let gpsCliPRangeMax = uFilterGPSTimeClipRange[1]
- // shader.setUniform2f("uFilterGPSTimeClipRange", [gpsCliPRangeMin, gpsCliPRangeMax]);
- // shader.setUniform1f("uGpsScale", bufferAttribute.potree.scale);
- // shader.setUniform1f("uGpsOffset", bufferAttribute.potree.offset);
- }
- {
- let uFilterReturnNumberRange = material.uniforms.uFilterReturnNumberRange.value;
- let uFilterNumberOfReturnsRange = material.uniforms.uFilterNumberOfReturnsRange.value;
- let uFilterPointSourceIDClipRange = material.uniforms.uFilterPointSourceIDClipRange.value;
-
-
-
- shader.setUniform2f("uFilterReturnNumberRange", uFilterReturnNumberRange);
- shader.setUniform2f("uFilterNumberOfReturnsRange", uFilterNumberOfReturnsRange);
- shader.setUniform2f("uFilterPointSourceIDClipRange", uFilterPointSourceIDClipRange);
- }
- let webglBuffer = null;
- if(!this.buffers.has(geometry)){
- webglBuffer = this.createBuffer(geometry);
- this.buffers.set(geometry, webglBuffer);
- }else {
- webglBuffer = this.buffers.get(geometry);
- for(let attributeName in geometry.attributes){
- let attribute = geometry.attributes[attributeName];
- if(attribute.version > webglBuffer.vbos.get(attributeName).version){
- this.updateBuffer(geometry);
- }
- }
- }
- gl.bindVertexArray(webglBuffer.vao);
- let isExtraAttribute =
- attributeLocations[material.activeAttributeName] === undefined
- && Object.keys(geometry.attributes).includes(material.activeAttributeName);
- if(isExtraAttribute){
- const attributeLocation = attributeLocations["aExtra"].location;
- for(const attributeName in geometry.attributes){
- const bufferAttribute = geometry.attributes[attributeName];
- const vbo = webglBuffer.vbos.get(attributeName);
-
- gl.bindBuffer(gl.ARRAY_BUFFER, vbo.handle);
- gl.disableVertexAttribArray(attributeLocation);
- }
- const attName = material.activeAttributeName;
- const bufferAttribute = geometry.attributes[attName];
- const vbo = webglBuffer.vbos.get(attName);
- if(bufferAttribute !== undefined && vbo !== undefined){
- let type = this.glTypeMapping.get(bufferAttribute.array.constructor);
- let normalized = bufferAttribute.normalized;
- gl.bindBuffer(gl.ARRAY_BUFFER, vbo.handle);
- gl.vertexAttribPointer(attributeLocation, bufferAttribute.itemSize, type, normalized, 0, 0);
- gl.enableVertexAttribArray(attributeLocation);
- }
- {
- const attExtra = octree.pcoGeometry.pointAttributes.attributes
- .find(a => a.name === attName);
- let range = material.getRange(attName);
- if(!range){
- range = attExtra.range;
- }
- if(!range){
- range = [0, 1];
- }
- let initialRange = attExtra.initialRange;
- let initialRangeSize = initialRange[1] - initialRange[0];
- let globalRange = range;
- let globalRangeSize = globalRange[1] - globalRange[0];
- let scale = initialRangeSize / globalRangeSize;
- let offset = -(globalRange[0] - initialRange[0]) / initialRangeSize;
- scale = Number.isNaN(scale) ? 1 : scale;
- offset = Number.isNaN(offset) ? 0 : offset;
- shader.setUniform1f("uExtraScale", scale);
- shader.setUniform1f("uExtraOffset", offset);
- }
- }else {
- for(const attributeName in geometry.attributes){
- const bufferAttribute = geometry.attributes[attributeName];
- const vbo = webglBuffer.vbos.get(attributeName);
- if(attributeLocations[attributeName] !== undefined){
- const attributeLocation = attributeLocations[attributeName].location;
- let type = this.glTypeMapping.get(bufferAttribute.array.constructor);
- let normalized = bufferAttribute.normalized;
-
- gl.bindBuffer(gl.ARRAY_BUFFER, vbo.handle);
- gl.vertexAttribPointer(attributeLocation, bufferAttribute.itemSize, type, normalized, 0, 0);
- gl.enableVertexAttribArray(attributeLocation);
-
- }
- }
- }
- let numPoints = webglBuffer.numElements;
-
- gl.drawArrays(gl.POINTS, 0, numPoints);
- //gl.drawArrays(gl.TRIANGLES, 0, numPoints);
-
-
- i++;
- }
- gl.bindVertexArray(null);
- if (exports.measureTimings) {
- performance.mark("renderNodes-end");
- performance.measure("render.renderNodes", "renderNodes-start", "renderNodes-end");
- }
- }
-
-
-
-
- renderOctree(octree, nodes, camera, target, params = {}){
- let gl = this.gl;
- let material = params.material ? params.material : octree.material;
- let shadowMaps = params.shadowMaps == null ? [] : params.shadowMaps;
- let view = camera.matrixWorldInverse;
- let viewInv = camera.matrixWorld;
- if(params.viewOverride){
- view = params.viewOverride;
- viewInv = view.clone().invert();
- }
- let proj = camera.projectionMatrix;
- let projInv = proj.clone().invert();
- //let worldView = new THREE.Matrix4();
- let shader = null;
- let visibilityTextureData = null;
- let currentTextureBindingPoint = 0;
- if (material.pointSizeType >= 0) {
- if (material.pointSizeType === PointSizeType.ADAPTIVE ||
- material.activeAttributeName === "level of detail") {
- let vnNodes = (params.vnTextureNodes != null) ? params.vnTextureNodes : nodes;
- visibilityTextureData = octree.computeVisibilityTextureData(vnNodes, camera);
- const vnt = material.visibleNodesTexture;
- const data = vnt.image.data;
- data.set(visibilityTextureData.data);
- vnt.needsUpdate = true;
- }
- }
- { // UPDATE SHADER AND TEXTURES
- if (!this.shaders.has(material)) {
- let [vs, fs] = [material.vertexShader, material.fragmentShader];
- let shader = new Shader(gl, "pointcloud", vs, fs);
- this.shaders.set(material, shader);
- }
-
- shader = this.shaders.get(material);
- //if(material.needsUpdate){
- {
- let [vs, fs] = [material.vertexShader, material.fragmentShader];
- let numSnapshots = material.snapEnabled ? material.numSnapshots : 0;
- let numClipBoxes = (material.clipBoxes && material.clipBoxes.length) ? material.clipBoxes.length : 0;
- let numClipSpheres = (params.clipSpheres && params.clipSpheres.length) ? params.clipSpheres.length : 0;
- let numClipPolygons = (material.clipPolygons && material.clipPolygons.length) ? material.clipPolygons.length : 0;
- let defines = [
- `#define num_shadowmaps ${shadowMaps.length}`,
- `#define num_snapshots ${numSnapshots}`,
- `#define num_clipboxes ${numClipBoxes}`,
- `#define num_clipspheres ${numClipSpheres}`,
- `#define num_clippolygons ${numClipPolygons}`,
- ];
-
- //add:-----------
- if(material.usePanoMap){
- defines.push("#define usePanoMap");
- }
-
- if(material.useFilterByNormal){
- defines.push("#define use_filter_by_normal");
- //Potree.settings.editType == 'pano' ? defines.push("#define attenuated_opacity2") : defines.push("#define attenuated_opacity");
-
- }
-
- //---------------
-
-
-
- if(octree.pcoGeometry.root.isLoaded()){
- let attributes = octree.pcoGeometry.root.geometry.attributes;
- if(attributes["gps-time"]){
- defines.push("#define clip_gps_enabled");
- }
- if(attributes["return number"]){
- defines.push("#define clip_return_number_enabled");
- }
- if(attributes["number of returns"]){
- defines.push("#define clip_number_of_returns_enabled");
- }
- if(attributes["source id"] || attributes["point source id"]){
- defines.push("#define clip_point_source_id_enabled");
- }
- }
- let definesString = defines.join("\n");
- let vsVersionIndex = vs.indexOf("#version ");
- let fsVersionIndex = fs.indexOf("#version ");
- if(vsVersionIndex >= 0){
- vs = vs.replace(/(#version .*)/, `$1\n${definesString}`);
- }else {
- vs = `${definesString}\n${vs}`;
- }
- if(fsVersionIndex >= 0){
- fs = fs.replace(/(#version .*)/, `$1\n${definesString}`);
- }else {
- fs = `${definesString}\n${fs}`;
- }
- shader.update(vs, fs);
- material.needsUpdate = false;
- }
-
- for (let uniformName of Object.keys(material.uniforms)) {
- let uniform = material.uniforms[uniformName];
- if (uniform.type == "t") {
- let texture = uniform.value;
- if (!texture) {
- continue;
- }
- //add
- if(uniformName == 'pano0Map' || uniformName == 'pano1Map' ){ //属于cubeTex,另外设置
- continue
- }
-
- if (!this.textures.has(texture)) {
- let webglTexture = new WebGLTexture(gl, texture);
- this.textures.set(texture, webglTexture);
- }
- let webGLTexture = this.textures.get(texture);
- webGLTexture.update();
- }
- }
- }
- gl.useProgram(shader.program);
- let transparent = false;
- if(params.transparent !== undefined){
- transparent = params.transparent && material.opacity < 1;
- }else {
- transparent = material.usePanoMap ? false : (material.useFilterByNormal || material.opacity < 1); //add useFilterByNormal
- }
- if (transparent){
- gl.enable(gl.BLEND);
- gl.blendFunc(gl.SRC_ALPHA, gl.ONE);
- gl.depthMask(false);
- gl.disable(gl.DEPTH_TEST);
- } else {
- gl.disable(gl.BLEND);
- gl.depthMask(true);
- gl.enable(gl.DEPTH_TEST);
- }
- if(params.blendFunc !== undefined){
- gl.enable(gl.BLEND);
- gl.blendFunc(...params.blendFunc);
- }
- if(params.depthTest !== undefined){
- if(params.depthTest === true){
- gl.enable(gl.DEPTH_TEST);
- }else {
- gl.disable(gl.DEPTH_TEST);
- }
- }
- if(params.depthWrite !== undefined){
- if(params.depthWrite === true){
- gl.depthMask(true);
- }else {
- gl.depthMask(false);
- }
-
- }
- { // UPDATE UNIFORMS
- shader.setUniformMatrix4("projectionMatrix", proj);
- shader.setUniformMatrix4("viewMatrix", view);
- shader.setUniformMatrix4("uViewInv", viewInv);
- shader.setUniformMatrix4("uProjInv", projInv);
- /* let screenWidth = target ? target.width : material.screenWidth;
- let screenHeight = target ? target.height : material.screenHeight;
- shader.setUniform1f("uScreenWidth", screenWidth);
- shader.setUniform1f("uScreenHeight", screenHeight); */
-
- shader.setUniform2f('resolution', material.resolution.toArray());
-
-
- shader.setUniform1f("fov", Math.PI * camera.fov / 180);
- shader.setUniform1f("near", camera.near);
- shader.setUniform1f("far", camera.far);
-
- if(camera instanceof OrthographicCamera){
- shader.setUniform("uUseOrthographicCamera", true);
- shader.setUniform("uOrthoWidth", camera.right - camera.left);
- shader.setUniform("uOrthoHeight", camera.top - camera.bottom);
- }else {
- shader.setUniform("uUseOrthographicCamera", false);
- }
- if(material.clipBoxes.length + material.clipPolygons.length === 0){
- shader.setUniform1i("clipTask", ClipTask.NONE);
- }else {
- shader.setUniform1i("clipTask", material.clipTask);
- }
- shader.setUniform1i("clipMethod", material.clipMethod);
- if (material.clipBoxes && material.clipBoxes.length > 0) {
- //let flattenedMatrices = [].concat(...material.clipBoxes.map(c => c.inverse.elements));
- //const lClipBoxes = shader.uniformLocations["clipBoxes[0]"];
- //gl.uniformMatrix4fv(lClipBoxes, false, flattenedMatrices);
- const lClipBoxes = shader.uniformLocations["clipBoxes[0]"];
- gl.uniformMatrix4fv(lClipBoxes, false, material.uniforms.clipBoxes.value);
- }
- // TODO CLIPSPHERES
- if(params.clipSpheres && params.clipSpheres.length > 0){
- let clipSpheres = params.clipSpheres;
- let matrices = [];
- for(let clipSphere of clipSpheres){
- //let mScale = new THREE.Matrix4().makeScale(...clipSphere.scale.toArray());
- //let mTranslate = new THREE.Matrix4().makeTranslation(...clipSphere.position.toArray());
- //let clipToWorld = new THREE.Matrix4().multiplyMatrices(mTranslate, mScale);
- let clipToWorld = clipSphere.matrixWorld;
- let viewToWorld = camera.matrixWorld;
- let worldToClip = clipToWorld.clone().invert();
- let viewToClip = new Matrix4().multiplyMatrices(worldToClip, viewToWorld);
- matrices.push(viewToClip);
- }
- let flattenedMatrices = [].concat(...matrices.map(matrix => matrix.elements));
- const lClipSpheres = shader.uniformLocations["uClipSpheres[0]"];
- gl.uniformMatrix4fv(lClipSpheres, false, flattenedMatrices);
-
- //const lClipSpheres = shader.uniformLocations["uClipSpheres[0]"];
- //gl.uniformMatrix4fv(lClipSpheres, false, material.uniforms.clipSpheres.value);
- }
- shader.setUniform1f("size", material.usePanoMap ? Potree.config.material.absolutePanoramaSize * Math.min(window.devicePixelRatio,2) : material.size);//usePanoMap时控制在不大不小的范围内感觉较好,考虑到有的点云稀疏,用大一点的点
- shader.setUniform1f("maxSize", material.uniforms.maxSize.value);
- shader.setUniform1f("minSize", material.uniforms.minSize.value);
- // uniform float uPCIndex
- shader.setUniform1f("uOctreeSpacing", material.spacing);
- shader.setUniform("uOctreeSize", material.uniforms.octreeSize.value);
- //uniform vec3 uColor;
- shader.setUniform3f("uColor", material.color.toArray());
- //uniform float opacity;
- shader.setUniform1f("uOpacity", material.usePanoMap ? 1: material.opacity);
- shader.setUniform2f("elevationRange", material.elevationRange);
- shader.setUniform2f("intensityRange", material.intensityRange);
- shader.setUniform3f("uIntensity_gbc", [
- material.intensityGamma,
- material.intensityBrightness,
- material.intensityContrast
- ]);
- shader.setUniform3f("uRGB_gbc", [
- material.rgbGamma,
- material.rgbBrightness,
- material.rgbContrast
- ]);
- shader.setUniform1f("uTransition", material.transition);
- shader.setUniform1f("wRGB", material.weightRGB);
- shader.setUniform1f("wIntensity", material.weightIntensity);
- shader.setUniform1f("wElevation", material.weightElevation);
- shader.setUniform1f("wClassification", material.weightClassification);
- shader.setUniform1f("wReturnNumber", material.weightReturnNumber);
- shader.setUniform1f("wSourceID", material.weightSourceID);
- shader.setUniform("backfaceCulling", material.uniforms.backfaceCulling.value);
-
-
- //==========================
- //gl.TEXTURE_CUBE_MAP: 34067
- //gl.TEXTURE0=33984 , vnWebGLTexture.target=gl.TEXTURE_2D = 3353
-
- let vnWebGLTexture = this.textures.get(material.visibleNodesTexture);
- if(vnWebGLTexture){
- shader.setUniform1i("visibleNodesTexture", currentTextureBindingPoint);
- gl.activeTexture(gl.TEXTURE0 + currentTextureBindingPoint);
- gl.bindTexture(vnWebGLTexture.target, vnWebGLTexture.id);
- currentTextureBindingPoint++;
- }
- let gradientTexture = this.textures.get(material.gradientTexture);
- shader.setUniform1i("gradient", currentTextureBindingPoint);
- gl.activeTexture(gl.TEXTURE0 + currentTextureBindingPoint);
- gl.bindTexture(gradientTexture.target, gradientTexture.id);
- const repeat = material.elevationGradientRepeat;
- if(repeat === ElevationGradientRepeat.REPEAT){
- gl.texParameteri(gradientTexture.target, gl.TEXTURE_WRAP_S, gl.REPEAT);
- gl.texParameteri(gradientTexture.target, gl.TEXTURE_WRAP_T, gl.REPEAT);
- }else if(repeat === ElevationGradientRepeat.MIRRORED_REPEAT){
- gl.texParameteri(gradientTexture.target, gl.TEXTURE_WRAP_S, gl.MIRRORED_REPEAT);
- gl.texParameteri(gradientTexture.target, gl.TEXTURE_WRAP_T, gl.MIRRORED_REPEAT);
- }else {
- gl.texParameteri(gradientTexture.target, gl.TEXTURE_WRAP_S, gl.CLAMP_TO_EDGE);
- gl.texParameteri(gradientTexture.target, gl.TEXTURE_WRAP_T, gl.CLAMP_TO_EDGE);
- }
- currentTextureBindingPoint++;
- let classificationTexture = this.textures.get(material.classificationTexture);
- shader.setUniform1i("classificationLUT", currentTextureBindingPoint);
- gl.activeTexture(gl.TEXTURE0 + currentTextureBindingPoint);
- gl.bindTexture(classificationTexture.target, classificationTexture.id);
- currentTextureBindingPoint++;
- let matcapTexture = this.textures.get(material.matcapTexture);
- shader.setUniform1i("matcapTextureUniform", currentTextureBindingPoint);
- gl.activeTexture(gl.TEXTURE0 + currentTextureBindingPoint);
- gl.bindTexture(matcapTexture.target, matcapTexture.id);
- currentTextureBindingPoint++;
- if (material.snapEnabled === true) {
- {
- const lSnapshot = shader.uniformLocations["uSnapshot[0]"];
- const lSnapshotDepth = shader.uniformLocations["uSnapshotDepth[0]"];
- let bindingStart = currentTextureBindingPoint;
- let lSnapshotBindingPoints = new Array(5).fill(bindingStart).map((a, i) => (a + i));
- let lSnapshotDepthBindingPoints = new Array(5)
- .fill(1 + Math.max(...lSnapshotBindingPoints))
- .map((a, i) => (a + i));
- currentTextureBindingPoint = 1 + Math.max(...lSnapshotDepthBindingPoints);
- gl.uniform1iv(lSnapshot, lSnapshotBindingPoints);
- gl.uniform1iv(lSnapshotDepth, lSnapshotDepthBindingPoints);
- for (let i = 0; i < 5; i++) {
- let texture = material.uniforms[`uSnapshot`].value[i];
- let textureDepth = material.uniforms[`uSnapshotDepth`].value[i];
- if (!texture) {
- break;
- }
- let snapTexture = this.threeRenderer.properties.get(texture).__webglTexture;
- let snapTextureDepth = this.threeRenderer.properties.get(textureDepth).__webglTexture;
- let bindingPoint = lSnapshotBindingPoints[i];
- let depthBindingPoint = lSnapshotDepthBindingPoints[i];
- gl.activeTexture(gl[`TEXTURE${bindingPoint}`]);
- gl.bindTexture(gl.TEXTURE_2D, snapTexture);
- gl.activeTexture(gl[`TEXTURE${depthBindingPoint}`]);
- gl.bindTexture(gl.TEXTURE_2D, snapTextureDepth);
- }
- }
- {
- let flattenedMatrices = [].concat(...material.uniforms.uSnapView.value.map(c => c.elements));
- const lSnapView = shader.uniformLocations["uSnapView[0]"];
- gl.uniformMatrix4fv(lSnapView, false, flattenedMatrices);
- }
- {
- let flattenedMatrices = [].concat(...material.uniforms.uSnapProj.value.map(c => c.elements));
- const lSnapProj = shader.uniformLocations["uSnapProj[0]"];
- gl.uniformMatrix4fv(lSnapProj, false, flattenedMatrices);
- }
- {
- let flattenedMatrices = [].concat(...material.uniforms.uSnapProjInv.value.map(c => c.elements));
- const lSnapProjInv = shader.uniformLocations["uSnapProjInv[0]"];
- gl.uniformMatrix4fv(lSnapProjInv, false, flattenedMatrices);
- }
- {
- let flattenedMatrices = [].concat(...material.uniforms.uSnapViewInv.value.map(c => c.elements));
- const lSnapViewInv = shader.uniformLocations["uSnapViewInv[0]"];
- gl.uniformMatrix4fv(lSnapViewInv, false, flattenedMatrices);
- }
- }
-
-
-
- //=============add===========
-
-
-
- if(material.usePanoMap){//为什么pointsize失效
- shader.setUniform1f("progress", material.uniforms.progress.value);
- shader.setUniform1f("easeInOutRatio", material.uniforms.easeInOutRatio.value);
- shader.setUniform3f("pano0Position", material.uniforms.pano0Position.value.toArray());
- shader.setUniform3f("pano1Position", material.uniforms.pano1Position.value.toArray());
- shader.setUniform('pano0Matrix', material.uniforms.pano0Matrix.value);
- shader.setUniform('pano1Matrix', material.uniforms.pano1Matrix.value);
-
- let pano0Map = material.uniforms.pano0Map.value;
- if(pano0Map){
- this.threeRenderer._textures.safeSetTextureCube( pano0Map, ++currentTextureBindingPoint );
-
- shader.setUniform1i('pano0Map', currentTextureBindingPoint);
- }
- let pano1Map = material.uniforms.pano1Map.value;
- if(pano1Map){
- this.threeRenderer._textures.safeSetTextureCube( pano1Map, ++currentTextureBindingPoint );
-
- shader.setUniform1i('pano1Map', currentTextureBindingPoint);
- }
-
- //注: three.js我添加了个 _textures, safeSetTextureCube里主要就是activeTexture和bindTexture
-
- }
-
-
-
-
-
- }
- this.renderNodes(octree, nodes, visibilityTextureData, camera, target, shader, params);
- gl.activeTexture(gl.TEXTURE2);
- gl.bindTexture(gl.TEXTURE_2D, null);
- gl.activeTexture(gl.TEXTURE0);
-
-
-
- //gl.bindTexture(gl.TEXTURE_2D, null); //add
-
-
-
-
- //add 恢复为不透明(否则renderToCubeMap时的贴图会被渲染成高亮的颜色)
- gl.disable(gl.BLEND);
- gl.depthMask(true);
- gl.enable(gl.DEPTH_TEST);
- //DEPTH_TEST等需要恢复吗
-
-
- }
-
-
-
-
-
-
-
-
-
-
-
- render(scene, camera, target = null, params = {}) {
-
- const gl = this.gl;
- // PREPARE
- if (target != null) {
- this.threeRenderer.setRenderTarget(target);
- }
- //camera.updateProjectionMatrix();
- // camera.matrixWorldInverse.invert(camera.matrixWorld);
- const traversalResult = this.traverse(scene);
- // RENDER
- for (const octree of traversalResult.octrees) {
- let nodes = octree.visibleNodes;
- this.renderOctree(octree, nodes, camera, target, params);
- }
- // CLEANUP
- gl.activeTexture(gl.TEXTURE1);
- gl.bindTexture(gl.TEXTURE_2D, null);
- gl.bindBuffer(gl.ARRAY_BUFFER, null);
- gl.bindVertexArray(null);
- this.threeRenderer.resetState();
- }
- };
- /*
- 中东的链接http://indoor.popsmart.cn:8094/zdoblh-yz/?vlon=5.14&vlat=-0.13&fov=100.0&pc=true&lon=121.61136592&lat=29.87855579&z=16.577
- geometry: 有的attributes: 属性是:
- classification:
- color:
- indices:
- normal:
- position:
- 最好有个spacing
- */
- class ProfileData {
- constructor (profile) {
- this.profile = profile;
- this.segments = [];
- this.boundingBox = new Box3();
- for (let i = 0; i < profile.points.length - 1; i++) {
- let start = profile.points[i];
- let end = profile.points[i + 1];
- let startGround = new Vector3(start.x, start.y, 0);
- let endGround = new Vector3(end.x, end.y, 0);
- let center = new Vector3().addVectors(endGround, startGround).multiplyScalar(0.5);
- let length = startGround.distanceTo(endGround);
- let side = new Vector3().subVectors(endGround, startGround).normalize();
- let up = new Vector3(0, 0, 1);
- let forward = new Vector3().crossVectors(side, up).normalize();
- let N = forward;
- let cutPlane = new Plane().setFromNormalAndCoplanarPoint(N, startGround);
- let halfPlane = new Plane().setFromNormalAndCoplanarPoint(side, center);
- let segment = {
- start: start,
- end: end,
- cutPlane: cutPlane,
- halfPlane: halfPlane,
- length: length,
- points: new Points$1()
- };
- this.segments.push(segment);
- }
- }
- size () {
- let size = 0;
- for (let segment of this.segments) {
- size += segment.points.numPoints;
- }
- return size;
- }
- };
- class ProfileRequest {
- constructor (pointcloud, profile, maxDepth, callback) {
- this.pointcloud = pointcloud;
- this.profile = profile;
- this.maxDepth = maxDepth || Number.MAX_VALUE;
- this.callback = callback;
- this.temporaryResult = new ProfileData(this.profile);
- this.pointsServed = 0;
- this.highestLevelServed = 0;
- this.priorityQueue = new BinaryHeap(function (x) { return 1 / x.weight; });
- this.initialize();
- }
- initialize () {
- this.priorityQueue.push({node: this.pointcloud.pcoGeometry.root, weight: Infinity});
- };
- // traverse the node and add intersecting descendants to queue
- traverse (node) {
- let stack = [];
- for (let i = 0; i < 8; i++) {
- let child = node.children[i];
- if (child && this.pointcloud.nodeIntersectsProfile(child, this.profile)) {
- stack.push(child);
- }
- }
- while (stack.length > 0) {
- let node = stack.pop();
- let weight = node.boundingSphere.radius;
- this.priorityQueue.push({node: node, weight: weight});
- // add children that intersect the cutting plane
- if (node.level < this.maxDepth) {
- for (let i = 0; i < 8; i++) {
- let child = node.children[i];
- if (child && this.pointcloud.nodeIntersectsProfile(child, this.profile)) {
- stack.push(child);
- }
- }
- }
- }
- }
- update(){
- if(!this.updateGeneratorInstance){
- this.updateGeneratorInstance = this.updateGenerator();
- }
- let result = this.updateGeneratorInstance.next();
- if(result.done){
- this.updateGeneratorInstance = null;
- }
- }
- * updateGenerator(){
- // load nodes in queue
- // if hierarchy expands, also load nodes from expanded hierarchy
- // once loaded, add data to this.points and remove node from queue
- // only evaluate 1-50 nodes per frame to maintain responsiveness
- let start = performance.now();
- let maxNodesPerUpdate = 1;
- let intersectedNodes = [];
- for (let i = 0; i < Math.min(maxNodesPerUpdate, this.priorityQueue.size()); i++) {
- let element = this.priorityQueue.pop();
- let node = element.node;
- if(node.level > this.maxDepth){
- continue;
- }
- if (node.loaded) {
- // add points to result
- intersectedNodes.push(node);
- exports.lru.touch(node);
- this.highestLevelServed = Math.max(node.getLevel(), this.highestLevelServed);
- var geom = node.pcoGeometry;
- var hierarchyStepSize = geom ? geom.hierarchyStepSize : 1;
- var doTraverse = node.getLevel() === 0 ||
- (node.level % hierarchyStepSize === 0 && node.hasChildren);
- if (doTraverse) {
- this.traverse(node);
- }
- } else {
- node.load();
- this.priorityQueue.push(element);
- }
- }
- if (intersectedNodes.length > 0) {
- for(let done of this.getPointsInsideProfile(intersectedNodes, this.temporaryResult)){
- if(!done){
- //console.log("updateGenerator yields");
- yield false;
- }
- }
- if (this.temporaryResult.size() > 100) {
- this.pointsServed += this.temporaryResult.size();
- this.callback.onProgress({request: this, points: this.temporaryResult});
- this.temporaryResult = new ProfileData(this.profile);
- }
- }
- if (this.priorityQueue.size() === 0) {
- // we're done! inform callback and remove from pending requests
- if (this.temporaryResult.size() > 0) {
- this.pointsServed += this.temporaryResult.size();
- this.callback.onProgress({request: this, points: this.temporaryResult});
- this.temporaryResult = new ProfileData(this.profile);
- }
- this.callback.onFinish({request: this});
- let index = this.pointcloud.profileRequests.indexOf(this);
- if (index >= 0) {
- this.pointcloud.profileRequests.splice(index, 1);
- }
- }
- yield true;
- };
- * getAccepted(numPoints, node, matrix, segment, segmentDir, points, totalMileage){
- let checkpoint = performance.now();
- let accepted = new Uint32Array(numPoints);
- let mileage = new Float64Array(numPoints);
- let acceptedPositions = new Float32Array(numPoints * 3);
- let numAccepted = 0;
- let pos = new Vector3();
- let svp = new Vector3();
- let view = new Float32Array(node.geometry.attributes.position.array);
- for (let i = 0; i < numPoints; i++) {
- pos.set(
- view[i * 3 + 0],
- view[i * 3 + 1],
- view[i * 3 + 2]);
- pos.applyMatrix4(matrix);
- let distance = Math.abs(segment.cutPlane.distanceToPoint(pos));
- let centerDistance = Math.abs(segment.halfPlane.distanceToPoint(pos));
- if (distance < this.profile.width / 2 && centerDistance < segment.length / 2) {
- svp.subVectors(pos, segment.start);
- let localMileage = segmentDir.dot(svp);
- accepted[numAccepted] = i;
- mileage[numAccepted] = localMileage + totalMileage;
- points.boundingBox.expandByPoint(pos);
- pos.sub(this.pointcloud.position);
- acceptedPositions[3 * numAccepted + 0] = pos.x;
- acceptedPositions[3 * numAccepted + 1] = pos.y;
- acceptedPositions[3 * numAccepted + 2] = pos.z;
- numAccepted++;
- }
- if((i % 1000) === 0){
- let duration = performance.now() - checkpoint;
- if(duration > 4){
- //console.log(`getAccepted yield after ${duration}ms`);
- yield false;
- checkpoint = performance.now();
- }
- }
- }
- accepted = accepted.subarray(0, numAccepted);
- mileage = mileage.subarray(0, numAccepted);
- acceptedPositions = acceptedPositions.subarray(0, numAccepted * 3);
- //let end = performance.now();
- //let duration = end - start;
- //console.log("accepted duration ", duration)
- //console.log(`getAccepted finished`);
- yield [accepted, mileage, acceptedPositions];
- }
- * getPointsInsideProfile(nodes, target){
- let checkpoint = performance.now();
- let totalMileage = 0;
- let pointsProcessed = 0;
- for (let segment of target.segments) {
- for (let node of nodes) {
- let numPoints = node.numPoints;
- let geometry = node.geometry;
- if(!numPoints){
- continue;
- }
- { // skip if current node doesn't intersect current segment
- let bbWorld = node.boundingBox.clone().applyMatrix4(this.pointcloud.matrixWorld);
- let bsWorld = bbWorld.getBoundingSphere(new Sphere());
- let start = new Vector3(segment.start.x, segment.start.y, bsWorld.center.z);
- let end = new Vector3(segment.end.x, segment.end.y, bsWorld.center.z);
- let closest = new Line3(start, end).closestPointToPoint(bsWorld.center, true, new Vector3());
- let distance = closest.distanceTo(bsWorld.center);
- let intersects = (distance < (bsWorld.radius + target.profile.width));
- if(!intersects){
- continue;
- }
- }
- //{// DEBUG
- // console.log(node.name);
- // let boxHelper = new Potree.Box3Helper(node.getBoundingBox());
- // boxHelper.matrixAutoUpdate = false;
- // boxHelper.matrix.copy(viewer.scene.pointclouds[0].matrixWorld);
- // viewer.scene.scene.add(boxHelper);
- //}
- let sv = new Vector3().subVectors(segment.end, segment.start).setZ(0);
- let segmentDir = sv.clone().normalize();
- let points = new Points$1();
- let nodeMatrix = new Matrix4().makeTranslation(...node.boundingBox.min.toArray());
- let matrix = new Matrix4().multiplyMatrices(
- this.pointcloud.matrixWorld, nodeMatrix);
- pointsProcessed = pointsProcessed + numPoints;
- let accepted = null;
- let mileage = null;
- let acceptedPositions = null;
- for(let result of this.getAccepted(numPoints, node, matrix, segment, segmentDir, points,totalMileage)){
- if(!result){
- let duration = performance.now() - checkpoint;
- //console.log(`getPointsInsideProfile yield after ${duration}ms`);
- yield false;
- checkpoint = performance.now();
- }else {
- [accepted, mileage, acceptedPositions] = result;
- }
- }
- let duration = performance.now() - checkpoint;
- if(duration > 4){
- //console.log(`getPointsInsideProfile yield after ${duration}ms`);
- yield false;
- checkpoint = performance.now();
- }
- points.data.position = acceptedPositions;
- let relevantAttributes = Object.keys(geometry.attributes).filter(a => !["position", "indices"].includes(a));
- for(let attributeName of relevantAttributes){
- let attribute = geometry.attributes[attributeName];
- let numElements = attribute.array.length / numPoints;
- if(numElements !== parseInt(numElements)){
- debugger;
- }
- let Type = attribute.array.constructor;
- let filteredBuffer = new Type(numElements * accepted.length);
- let source = attribute.array;
- let target = filteredBuffer;
- for(let i = 0; i < accepted.length; i++){
- let index = accepted[i];
- let start = index * numElements;
- let end = start + numElements;
- let sub = source.subarray(start, end);
- target.set(sub, i * numElements);
- }
- points.data[attributeName] = filteredBuffer;
- }
- points.data['mileage'] = mileage;
- points.numPoints = accepted.length;
- segment.points.add(points);
- }
- totalMileage += segment.length;
- }
- for (let segment of target.segments) {
- target.boundingBox.union(segment.points.boundingBox);
- }
- //console.log(`getPointsInsideProfile finished`);
- yield true;
- };
- finishLevelThenCancel () {
- if (this.cancelRequested) {
- return;
- }
- this.maxDepth = this.highestLevelServed;
- this.cancelRequested = true;
- //console.log(`maxDepth: ${this.maxDepth}`);
- };
- cancel () {
- this.callback.onCancel();
- this.priorityQueue = new BinaryHeap(function (x) { return 1 / x.weight; });
- let index = this.pointcloud.profileRequests.indexOf(this);
- if (index >= 0) {
- this.pointcloud.profileRequests.splice(index, 1);
- }
- };
- }
- class Version{
- constructor(version){
- this.version = version;
- let vmLength = (version.indexOf('.') === -1) ? version.length : version.indexOf('.');
- this.versionMajor = parseInt(version.substr(0, vmLength));
- this.versionMinor = parseInt(version.substr(vmLength + 1));
- if (this.versionMinor.length === 0) {
- this.versionMinor = 0;
- }
- }
- newerThan(version){
- let v = new Version(version);
- if (this.versionMajor > v.versionMajor) {
- return true;
- } else if (this.versionMajor === v.versionMajor && this.versionMinor > v.versionMinor) {
- return true;
- } else {
- return false;
- }
- }
- equalOrHigher(version){
- let v = new Version(version);
- if (this.versionMajor > v.versionMajor) {
- return true;
- } else if (this.versionMajor === v.versionMajor && this.versionMinor >= v.versionMinor) {
- return true;
- } else {
- return false;
- }
- }
- upTo(version){
- return !this.newerThan(version);
- }
- }
- class WorkerPool{
- constructor(){
- this.workers = {};
- }
- getWorker(url){
- if (!this.workers[url]){
- this.workers[url] = [];
- }
- if (this.workers[url].length === 0){
- let worker = new Worker(url);
- this.workers[url].push(worker);
- }
- let worker = this.workers[url].pop();
- return worker;
- }
- returnWorker(url, worker){
- this.workers[url].push(worker);
- }
- };
- //Potree.workerPool = new Potree.WorkerPool();
- function createPointcloudData(pointcloud) {
- let material = pointcloud.material;
- let ranges = [];
-
- for(let [name, value] of material.ranges){
- ranges.push({
- name: name,
- value: value,
- });
- }
- if(typeof material.elevationRange[0] === "number"){
- ranges.push({
- name: "elevationRange",
- value: material.elevationRange,
- });
- }
- if(typeof material.intensityRange[0] === "number"){
- ranges.push({
- name: "intensityRange",
- value: material.intensityRange,
- });
- }
- let pointSizeTypeName = Object.entries(Potree.PointSizeType).find(e => e[1] === material.pointSizeType)[0];
- let jsonMaterial = {
- activeAttributeName: material.activeAttributeName,
- ranges: ranges,
- size: material.size,
- minSize: material.minSize,
- pointSizeType: pointSizeTypeName,
- matcap: material.matcap,
- };
- const pcdata = {
- name: pointcloud.name,
- url: pointcloud.pcoGeometry.url,
- position: pointcloud.position.toArray(),
- rotation: pointcloud.rotation.toArray(),
- scale: pointcloud.scale.toArray(),
- material: jsonMaterial,
- };
- return pcdata;
- }
- function createProfileData(profile){
- const data = {
- uuid: profile.uuid,
- name: profile.name,
- points: profile.points.map(p => p.toArray()),
- height: profile.height,
- width: profile.width,
- };
- return data;
- }
- function createVolumeData(volume){
- const data = {
- uuid: volume.uuid,
- type: volume.constructor.name,
- name: volume.name,
- position: volume.position.toArray(),
- rotation: volume.rotation.toArray(),
- scale: volume.scale.toArray(),
- visible: volume.visible,
- clip: volume.clip,
- };
- return data;
- }
- function createCameraAnimationData(animation){
- const controlPoints = animation.controlPoints.map( cp => {
- const cpdata = {
- position: cp.position.toArray(),
- target: cp.target.toArray(),
- };
- return cpdata;
- });
- const data = {
- uuid: animation.uuid,
- name: animation.name,
- duration: animation.duration,
- t: animation.t,
- curveType: animation.curveType,
- visible: animation.visible,
- controlPoints: controlPoints,
- };
- return data;
- }
- function createMeasurementData(measurement){
- /* const data = {
- uuid: measurement.uuid,
- name: measurement.name,
- measureType:measurement.measureType, //add
- direction:measurement.direction,//add
- minMarkers:measurement.minMarkers,//add
- maxMarkers: measurement.maxMarkers,//add
- isRect: measurement.isRect,//add
- faceDirection: measurement.faceDirection,//add
-
- points: measurement.points.map(p => p.position.toArray()),
- showDistances: measurement.showDistances,
- showCoordinates: measurement.showCoordinates,
- showArea: measurement.showArea,
- closed: measurement.closed,
- showAngles: measurement.showAngles,
- showHeight: measurement.showHeight,
- showCircle: measurement.showCircle,
- showAzimuth: measurement.showAzimuth,
- showEdges: measurement.showEdges,
- color: measurement.color.toArray(),
- }; */
- const data = {
- uuid: measurement.uuid,
- name: measurement.name,
- measureType:measurement.measureType, //add
- points: measurement.points.map(p => p.toArray()),
-
- };
- return data;
- }
- function createOrientedImagesData(images){
- const data = {
- cameraParamsPath: images.cameraParamsPath,
- imageParamsPath: images.imageParamsPath,
- };
- return data;
- }
- function createGeopackageData(geopackage){
- const data = {
- path: geopackage.path,
- };
- return data;
- }
- function createAnnotationData(annotation){
- const data = {
- uuid: annotation.uuid,
- title: annotation.title.toString(),
- description: annotation.description,
- position: annotation.position.toArray(),
- offset: annotation.offset.toArray(),
- children: [],
- };
- if(annotation.cameraPosition){
- data.cameraPosition = annotation.cameraPosition.toArray();
- }
- if(annotation.cameraTarget){
- data.cameraTarget = annotation.cameraTarget.toArray();
- }
- if(typeof annotation.radius !== "undefined"){
- data.radius = annotation.radius;
- }
- return data;
- }
- function createAnnotationsData(viewer){
-
- const map = new Map();
- viewer.scene.annotations.traverseDescendants(a => {
- const aData = createAnnotationData(a);
- map.set(a, aData);
- });
- for(const [annotation, data] of map){
- for(const child of annotation.children){
- const childData = map.get(child);
- data.children.push(childData);
- }
- }
- const annotations = viewer.scene.annotations.children.map(a => map.get(a));
- return annotations;
- }
- function createSettingsData(viewer){
- return {
- pointBudget: viewer.getPointBudget(),
- fov: viewer.getFOV(),
- edlEnabled: viewer.getEDLEnabled(),
- edlRadius: viewer.getEDLRadius(),
- edlStrength: viewer.getEDLStrength(),
- background: viewer.getBackground(),
- minNodeSize: viewer.getMinNodeSize(),
- showBoundingBoxes: viewer.getShowBoundingBox(),
- };
- }
- function createSceneContentData(viewer){
- const data = [];
- const potreeObjects = [];
- viewer.scene.scene.traverse(node => {
- if(node.potree){
- potreeObjects.push(node);
- }
- });
- for(const object of potreeObjects){
-
- if(object.potree.file){
- const saveObject = {
- file: object.potree.file,
- };
- data.push(saveObject);
- }
- }
- return data;
- }
- function createViewData(viewer){
- const view = viewer.scene.view;
- const data = {
- position: view.position.toArray(),
- target: view.getPivot().toArray(),
- };
- return data;
- }
- function createClassificationData(viewer){
- const classifications = viewer.classifications;
- const data = classifications;
- return data;
- }
- function saveProject(viewer) {
- const scene = viewer.scene;
- const data = {
- type: "Potree",
- version: 1.7,
- settings: createSettingsData(viewer),
- view: createViewData(viewer),
- classification: createClassificationData(viewer),
- pointclouds: scene.pointclouds.map(createPointcloudData),
- measurements: scene.measurements.map(createMeasurementData),
- volumes: scene.volumes.map(createVolumeData),
- cameraAnimations: scene.cameraAnimations.map(createCameraAnimationData),
- profiles: scene.profiles.map(createProfileData),
- annotations: createAnnotationsData(viewer),
- orientedImages: scene.orientedImages.map(createOrientedImagesData),
- geopackages: scene.geopackages.map(createGeopackageData),
- // objects: createSceneContentData(viewer),
- };
- return data;
- }
- class HandleSvg extends EventDispatcher{
- constructor(position, color){
- super();
- this.position = position;
- this.color = '#'+new Color(color).getHexString();
- this.svg = this.create();
- this.visible_ = true;
-
- let update = ()=>{
- this.update();
- };
- viewer.addEventListener("camera_changed", update);
-
- this.addEventListener('dispose', ()=>{
- viewer.removeEventListener("camera_changed", update);
- });
- }
-
-
- create(){
-
- const svgns = "http://www.w3.org/2000/svg";
- const svg = document.createElementNS(svgns, "svg");
- svg.setAttribute("width", "2em");
- svg.setAttribute("height", "2em");
- svg.setAttribute("position", "absolute");
- svg.style.left = "50px";
- svg.style.top = "50px";
- svg.style.position = "absolute";
- svg.style.zIndex = "10000";
- svg.style.cursor = 'grab';
- svg.style.transform = 'translate(-50%,-50%)';
-
- const circle = document.createElementNS(svgns, 'circle');
- circle.setAttributeNS(null, 'cx', "1em");
- circle.setAttributeNS(null, 'cy', "1em");
- circle.setAttributeNS(null, 'r', "0.5em");
- circle.setAttributeNS(null, 'style', 'fill: '+this.color+'; stroke: black; stroke-width: 0.2em;' );
- svg.appendChild(circle);
-
- const element = viewer.renderer.domElement.parentElement;
- element.appendChild(svg);
- const startDrag = (evt) => {
- /* if(evt.button === THREE.MOUSE.RIGHT){
- return
- } */
- this.selectedElement = svg;
- document.addEventListener("mousemove", drag);
- };
- const endDrag = (evt) => {
- this.selectedElement = null;
- document.removeEventListener("mousemove", drag);
- };
- const drag = (evt) => {
- if (this.selectedElement) {
-
- evt.preventDefault();
- const rect = viewer.renderer.domElement.getBoundingClientRect();
- const x = evt.clientX - rect.x;
- const y = evt.clientY - rect.y;
- const {width, height} = viewer.renderer.getSize(new Vector2$1());
- const camera = viewer.scene.getActiveCamera();
-
- const projected = this.position.clone().project(camera);
-
- projected.x = ((x / width) - 0.5) / 0.5;
- projected.y = (-(y - height) / height - 0.5) / 0.5;
- const unprojected = projected.clone().unproject(camera);
- this.position.set(unprojected.x, unprojected.y, unprojected.z);
-
- this.update();
- this.dispatchEvent({type:'dragged', position: this.position});
-
- }
- };
- svg.addEventListener('mousedown', startDrag);
- svg.addEventListener('mouseup', endDrag);
- svg.style.display = this.visible ? "" : "none";
-
-
- this.addEventListener('dispose',()=>{
- svg.removeEventListener('mousedown', startDrag);
- svg.removeEventListener('mouseup', endDrag);
- });
-
-
-
- return svg
- }
-
-
-
-
-
- set visible(v){
- this.visible_ = v;
- if(v){
- this.update();
- }else {
- this.svg.style.display = "none";
- }
- }
- get visible(){
- return this.visible_
- }
-
-
- update(){
- if(!this.visible)return
-
- let camera = viewer.scene.getActiveCamera();
-
-
- var p = Potree.Utils.getPos2d( this.position, camera , viewer.renderArea, viewer.mainViewport);
- if(!p.trueSide){
- return this.svg.style.display = 'none';
- }
- this.svg.style.left = p.posInViewport.x;
- this.svg.style.top = p.posInViewport.y;
-
-
-
- this.svg.style.display = '';
-
- }
-
-
-
- dispose(){
- this.svg.remove();
- this.dispatchEvent('dispose');
- }
- }
- /*
- 两种拖拽方式:
- 1 只依附在点云上
- 2 平行于镜头view移动 */
- const geo$1 = new PlaneBufferGeometry(1,1);
- class HandleSprite extends Sprite$1{
- constructor(position,options={}){
-
- options.sizeInfo = {width2d:60};
- super(options);
- this.position.copy(position);
-
- this.dragStyle = options.dragStyle || 'default'; //'default'||'onPointCloud'
-
-
- this.bindEvent();
-
-
- }
-
-
- bindEvent(){
- let projectedStart, pointerStart;
-
- const drag = (e)=>{
- /* if(e.hoverViewport != e.drag.dragViewport){//不能使用e.dragViewport,要使用drag中的,因为drag中存储的要一直继承下来,不因mouseup了而改变。
- viewer.dispatchEvent({
- type : "CursorChange", action : "add", name:"polygon_AtWrongPlace"
- })
- return
- } */
- const camera = viewer.scene.getActiveCamera();
- if(projectedStart){
- let move2d = new Vector2$1().subVectors(e.pointer, pointerStart);
- let projectNow = projectedStart.clone();
- projectNow.x += move2d.x;
- projectNow.y += move2d.y;
- let unprojected = projectNow.clone().unproject(camera);
- this.position.set(unprojected.x, unprojected.y, unprojected.z);
- }else {
- projectedStart = this.position.clone().project(camera);
- pointerStart = e.pointer.clone();
- }
-
- this.update();
- this.dispatchEvent({type:'dragged', position: this.position });
- };
-
- const drop = (e)=>{
- projectedStart = null, pointerStart = null;
- };
-
- const mouseover = (e) => {
- viewer.dispatchEvent({
- type : "CursorChange", action : "add", name:"markerMove"
- });
- };
- const mouseleave = (e) => {
- viewer.dispatchEvent({
- type : "CursorChange", action : "remove", name:"markerMove"
- });
- };
-
- this.addEventListener('drag', drag);
- this.addEventListener('drop', drop);
- this.addEventListener('mouseover', mouseover);
- this.addEventListener('mouseleave', mouseleave);
- }
-
- }
- const sphere = new Mesh(new SphereBufferGeometry(0.08,0.08,3,2), new MeshBasicMaterial({color:'#f88'}));
- class CurveCtrl extends Object3D {
-
- constructor(points, lineMat, color, name, options={}){
- super();
- this.curve = new CatmullRomCurve3(points, false, "centripetal" /* , tension */);
- this.name = name || 'curveNode';
- this.handleMat = options.handleMat;
- this.lineMat = lineMat;
- this.createPath();
- this.color = color;
- this.handles = [];
- this.wholeLength = 0;
- this.viewports = options.viewports || [viewer.mainViewport]; //for HandleSprite
- for(let i=0,j=this.points.length; i<j;i++){
- this.handles.push(this.createHandle(this.points[i]));
- }
- this.visible_ = true;
-
-
-
- if(Potree.settings.isTest){
- this.spheres = new Object3D;
- /* let i = Count+1;
- while(i>0){
- this.spheres.add(sphere.clone());
- i--;
- } */
- this.add(this.spheres);
- }
-
- this.updatePath();
-
-
- }
-
-
- addPoint(position, index, ifUpdate){
- let length = this.points.length;
-
- if(index == void 0 ){
- index = length;
- }
-
- let handle = this.createHandle(position);
-
- this.handles = [...this.handles.slice(0,index), handle, ...this.handles.slice(index,length)];
-
- this.points = [...this.points.slice(0,index), position, ...this.points.slice(index,length)];
-
- ifUpdate && (this.updatePath(), this.updateHandle(index));
-
- }
- removePoint(index){
- let handle = this.handles[index];
- handle.dispose();
-
- this.handles.splice(index,1);
- this.points.splice(index,1);
- this.updatePath();
- }
-
- createPath(){
-
- const line = LineDraw.createFatLine( [ ],this.lineMat);
- this.line = line;
- this.add(line);
- }
-
-
- updatePath(){
- this.curve.needsUpdate = true; //如果不更新,得到的点不均匀,开头点少。
-
- let points, length = this.points.length;
-
- this.wholeLength = this.points.reduce((total, currentValue, currentIndex, arr)=>{ //所有端点的距离总和
- if(currentIndex == 0)return 0
- return total + currentValue.distanceTo(arr[currentIndex-1]);
- },0);
-
- if(length > 1){
- const count = MathUtils.clamp(Math.ceil(this.wholeLength * 5), 30, 500);
-
- points = this.curve.getSpacedPoints( count );
-
-
- if(this.needsPercent){ //获取每个节点在整条中的百分比,便于定位(但不精确)
- this.pointsPercent = [0];
- let sums = [0];
- let sum = 0, last = points[0];
- for(let i=1;i<length;i++){
- let point = this.points[i];
- sum += point.distanceTo(last); //参考getLengths函数,根据长度得到百分比
- last = point;
- sums.push(sum);
- }
- for(let i=1;i<length;i++){
- this.pointsPercent.push(sum == 0 ? i/length : sums[i] / sum);
- }
-
-
- }
-
-
-
-
-
- if(Potree.settings.isTest){
- this.spheres.children.forEach(e=>e.visible = false);
- points.forEach((e,i)=>{
- let sphere1 = this.spheres.children[i];
- if(!sphere1){
- sphere1 = sphere.clone();
- this.spheres.add(sphere1);
- }
- sphere1.position.copy(e);
- sphere1.visible = true;
- });
- }
- }else {
- points = [];
- }
-
-
- LineDraw.updateLine(this.line, points);
-
-
- this.dispatchEvent('updatePath');
-
- }
-
-
- createHandle(position){
- if(this.handleMat){
- var handle = new HandleSprite(position, {mat:this.handleMat, viewports:this.viewports});
- this.add(handle);
- }else {
- var handle = new HandleSvg(position, this.color);
- }
-
- handle.visible = this.visible;
- handle.addEventListener('dragged',(e)=>{
- let index = this.handles.indexOf(handle);
- this.points[index].copy(e.position);
-
- this.updatePath();
-
- this.dispatchEvent({type:'dragCurvePoint', index});
- });
-
- return handle
- }
-
-
-
- updateHandle(index){
- if(!this.visible)return
-
- this.handles[index].update();
-
- }
-
- updateHandles(){
-
- this.handles.forEach((handle,index)=>{
- this.updateHandle(index);
- });
- }
-
- update(){
- this.updateHandles();
- this.updatePath();
- }
-
- set visible(v){
- if(v != this.visible_ ){
- this.visible_ = v;
- this.visible = v;
- if(this.handles){
- this.handles.forEach(e=>e.visible = v );
- if(v) this.updateHandles(); //因为不可见时没更新位置
- }
- }
- }
- get visible(){
- return this.visible_
- }
-
-
- /* set visible(v){
- this.handles.forEach(e=>e.svg.style.display = v ? "" : "none" )
- if(v) this.updateHandles() //因为不可见时没更新位置
- } */
- get points(){
- return this.curve.points;
- }
- set points(points){
- this.curve.points = points;
- }
- getPointAt(t){
- return this.curve.getPointAt(t)
- }
- getSpacedPoints(t){
- return this.curve.getSpacedPoints(t)
- }
- dispose(){
- this.parent && this.parent.remove(this);
-
- this.handles.forEach(e=>e.dispose() );
-
- this.line.geometry && this.line.geometry.dispose();
-
- }
-
-
-
- }
- var easing = {};
- //渐变曲线函数,反应加速度的变化
- //currentTime:x轴当前时间(从0-到duration), startY:起始点, duration:总时长, wholeY:路程 (即endY-startY)
- //参数基本是 x, 0, 1, 1
- /*
- easeOut 基本是y= m * (x-dur)^k + n, 若k为偶数,m<0, 若k为奇数,m>0; (因为偶数的话必须开口向下才能获得斜率递减的递增的那段,而奇数是对称的,单调递增. )
- 根据x=0时y=0, x=dur时y=S , 得 n = S,m = -S/(-dur)^k
- */
-
- easing.getEaseOut = function(k){// k 是>=2的整数. 越大变化率越大, 相同初始速度所需要时间越久
- let easeFun;
- k = Math.round(k);
-
- if(k<2){
- k = Math.PI / 2;
- easeFun = easing.easeOutSine;
- }else {
- easeFun = function(currentTime, startY, wholeY, duration) {
- if(k>2){
- console.log(k);
- }
- return -wholeY/Math.pow(-duration, k) * Math.pow(currentTime-duration, k) + wholeY
- };
- }
-
- return {
- k,
- easeFun
- }
- };
- easing.linearTween = function(currentTime, startY, wholeY, duration) {
- return wholeY * currentTime / duration + startY
- }
- ,
- easing.easeInQuad = function(currentTime, startY, wholeY, duration) {
- return currentTime /= duration,
- wholeY * currentTime * currentTime + startY
- }
- ,
- easing.easeOutQuad = function(currentTime, startY, wholeY, duration) { // 如套上实际的距离S和时长dur, y = - S / dur *(x^2-2x) 当s为1,dur为1时,是 y = -(x-1)^2 + 1 , 在0-1中是斜率递减的递增函数. 导数- S / dur *(2x-2 ) 可求出实时速度 故在0这一时刻,速度为 2S/dur
- return currentTime /= duration,
- -wholeY * currentTime * (currentTime - 2) + startY
- }
- ,
- easing.easeInOutQuad = function(currentTime, startY, wholeY, duration) {
- return currentTime /= duration / 2,
- currentTime < 1 ? wholeY / 2 * currentTime * currentTime + startY : (currentTime--,
- -wholeY / 2 * (currentTime * (currentTime - 2) - 1) + startY)
- }
- ,
- easing.easeInCubic = function(currentTime, startY, wholeY, duration) {
- return currentTime /= duration,
- wholeY * currentTime * currentTime * currentTime + startY
- }
- ,
- easing.easeOutCubic = function(currentTime, startY, wholeY, duration) {// y = S / dur^3 *(x-dur)^3 + S,对称中心是(dur,S),从0-dur是 斜率递减的递增函数,导数为3S/dur^3 * (x-dur)^2, 0时速度为3S/dur
- return currentTime /= duration,
- currentTime--,
- wholeY * (currentTime * currentTime * currentTime + 1) + startY
- }
- ,
- easing.easeInOutCubic = function(currentTime, startY, wholeY, duration) {
- return currentTime /= duration / 2,
- currentTime < 1 ? wholeY / 2 * currentTime * currentTime * currentTime + startY : (currentTime -= 2,
- wholeY / 2 * (currentTime * currentTime * currentTime + 2) + startY)
- }
- ,
- easing.easeInQuart = function(currentTime, startY, wholeY, duration) {
- return currentTime /= duration,
- wholeY * currentTime * currentTime * currentTime * currentTime + startY
- }
- ,
- easing.easeOutQuart = function(currentTime, startY, wholeY, duration) {//根据上面的计算,估计0时速度应该是4S/dur吧……
- return currentTime /= duration,
- currentTime--,
- -wholeY * (currentTime * currentTime * currentTime * currentTime - 1) + startY
- }
- ,
- easing.easeInOutQuart = function(currentTime, startY, wholeY, duration) {
- return currentTime /= duration / 2,
- currentTime < 1 ? wholeY / 2 * currentTime * currentTime * currentTime * currentTime + startY : (currentTime -= 2,
- -wholeY / 2 * (currentTime * currentTime * currentTime * currentTime - 2) + startY)
- }
- ,
- easing.easeInQuint = function(currentTime, startY, wholeY, duration) {
- return currentTime /= duration,
- wholeY * currentTime * currentTime * currentTime * currentTime * currentTime + startY
- }
- ,
- easing.easeOutQuint = function(currentTime, startY, wholeY, duration) {
- return currentTime /= duration,
- currentTime--,
- wholeY * (currentTime * currentTime * currentTime * currentTime * currentTime + 1) + startY
- }
- ,
- easing.easeInOutQuint = function(currentTime, startY, wholeY, duration) {
- return currentTime /= duration / 2,
- currentTime < 1 ? wholeY / 2 * currentTime * currentTime * currentTime * currentTime * currentTime + startY : (currentTime -= 2,
- wholeY / 2 * (currentTime * currentTime * currentTime * currentTime * currentTime + 2) + startY)
- }
- ,
- easing.easeInSine = function(currentTime, startY, wholeY, duration) {
- return -wholeY * Math.cos(currentTime / duration * (Math.PI / 2)) + wholeY + startY
- }
- ,
- easing.easeOutSine = function(currentTime, startY, wholeY, duration) {// y' = S * PI / 2 / dur * cos(PI/2/dur * x)
- console.log('easeOutSine');
- return wholeY * Math.sin(currentTime / duration * (Math.PI / 2)) + startY
- }
- ,
- easing.easeInOutSine = function(currentTime, startY, wholeY, duration) {
- return -wholeY / 2 * (Math.cos(Math.PI * currentTime / duration) - 1) + startY
- }
- ,
- easing.easeInExpo = function(currentTime, startY, wholeY, duration) {
- return wholeY * Math.pow(2, 10 * (currentTime / duration - 1)) + startY
- }
- ,
- easing.easeOutExpo = function(currentTime, startY, wholeY, duration) {
- return wholeY * (-Math.pow(2, -10 * currentTime / duration) + 1) + startY
- }
- ,
- easing.easeInOutExpo = function(currentTime, startY, wholeY, duration) {
- return currentTime /= duration / 2,
- currentTime < 1 ? wholeY / 2 * Math.pow(2, 10 * (currentTime - 1)) + startY : (currentTime--,
- wholeY / 2 * (-Math.pow(2, -10 * currentTime) + 2) + startY)
- }
- ,
- easing.easeInCirc = function(currentTime, startY, wholeY, duration) {
- return currentTime /= duration,
- -wholeY * (Math.sqrt(1 - currentTime * currentTime) - 1) + startY
- }
- ,
- easing.easeOutCirc = function(currentTime, startY, wholeY, duration) {
- return currentTime /= duration,
- currentTime--,
- wholeY * Math.sqrt(1 - currentTime * currentTime) + startY
- }
- ,
- easing.easeInOutCirc = function(currentTime, startY, wholeY, duration) {
- return currentTime /= duration / 2,
- currentTime < 1 ? -wholeY / 2 * (Math.sqrt(1 - currentTime * currentTime) - 1) + startY : (currentTime -= 2,
- wholeY / 2 * (Math.sqrt(1 - currentTime * currentTime) + 1) + startY)
- }
- ,
- easing.easeInElastic = function(currentTime, startY, wholeY, duration) {
- var r = 1.70158
- , o = 0
- , a = wholeY;
- return 0 === currentTime ? startY : 1 === (currentTime /= duration) ? startY + wholeY : (o || (o = .3 * duration),
- a < Math.abs(wholeY) ? (a = wholeY,
- r = o / 4) : r = o / (2 * Math.PI) * Math.asin(wholeY / a),
- -(a * Math.pow(2, 10 * (currentTime -= 1)) * Math.sin((currentTime * duration - r) * (2 * Math.PI) / o)) + startY)
- }
- ,
- easing.easeOutElastic = function(currentTime, startY, wholeY, duration) {
- var r = 1.70158
- , o = 0
- , a = wholeY;
- return 0 === currentTime ? startY : 1 === (currentTime /= duration) ? startY + wholeY : (o || (o = .3 * duration),
- a < Math.abs(wholeY) ? (a = wholeY,
- r = o / 4) : r = o / (2 * Math.PI) * Math.asin(wholeY / a),
- a * Math.pow(2, -10 * currentTime) * Math.sin((currentTime * duration - r) * (2 * Math.PI) / o) + wholeY + startY)
- }
- ,
- easing.easeInOutElastic = function(currentTime, startY, wholeY, duration) {
- var r = 1.70158
- , o = 0
- , a = wholeY;
- return 0 === currentTime ? startY : 2 === (currentTime /= duration / 2) ? startY + wholeY : (o || (o = duration * (.3 * 1.5)),
- a < Math.abs(wholeY) ? (a = wholeY,
- r = o / 4) : r = o / (2 * Math.PI) * Math.asin(wholeY / a),
- currentTime < 1 ? -.5 * (a * Math.pow(2, 10 * (currentTime -= 1)) * Math.sin((currentTime * duration - r) * (2 * Math.PI) / o)) + startY : a * Math.pow(2, -10 * (currentTime -= 1)) * Math.sin((currentTime * duration - r) * (2 * Math.PI) / o) * .5 + wholeY + startY)
- }
- ,
- easing.easeInBack = function(currentTime, startY, wholeY, duration, r) {
- return void 0 === r && (r = 1.70158),
- wholeY * (currentTime /= duration) * currentTime * ((r + 1) * currentTime - r) + startY
- }
- ,
- easing.easeOutBack = function(currentTime, startY, wholeY, duration, r) {
- return void 0 === r && (r = 1.70158),
- wholeY * ((currentTime = currentTime / duration - 1) * currentTime * ((r + 1) * currentTime + r) + 1) + startY
- }
- ,
- easing.easeInOutBack = function(currentTime, startY, wholeY, duration, r) {
- return void 0 === r && (r = 1.70158),
- (currentTime /= duration / 2) < 1 ? wholeY / 2 * (currentTime * currentTime * (((r *= 1.525) + 1) * currentTime - r)) + startY : wholeY / 2 * ((currentTime -= 2) * currentTime * (((r *= 1.525) + 1) * currentTime + r) + 2) + startY
- }
- ,
- easing.easeOutBounce = function(currentTime, startY, wholeY, duration) {
- return (currentTime /= duration) < 1 / 2.75 ? wholeY * (7.5625 * currentTime * currentTime) + startY : currentTime < 2 / 2.75 ? wholeY * (7.5625 * (currentTime -= 1.5 / 2.75) * currentTime + .75) + startY : currentTime < 2.5 / 2.75 ? wholeY * (7.5625 * (currentTime -= 2.25 / 2.75) * currentTime + .9375) + startY : wholeY * (7.5625 * (currentTime -= 2.625 / 2.75) * currentTime + .984375) + startY
- }
- ,
- easing.easeInBounce = function(currentTime, startY, wholeY, r) {
- return wholeY - easing.easeOutBounce(r - currentTime, 0, wholeY, r) + startY
- }
- ,
- easing.easeInOutBounce = function(currentTime, startY, wholeY, r) {
- return currentTime < r / 2 ? .5 * easing.easeInBounce(2 * currentTime, 0, wholeY, r) + startY : .5 * easing.easeOutBounce(x, 2 * currentTime - r, 0, wholeY, r) + .5 * wholeY + startY
- };
-
- var lerp = {
- /* vector: function(currentTime, startY, f) {//xzw change, add f
- var wholeY = currentTime.clone();
- return startY = startY.clone(),
- function(duration) {
- currentTime.set(wholeY.x * (1 - duration) + startY.x * duration, wholeY.y * (1 - duration) + startY.y * duration, wholeY.z * (1 - duration) + startY.z * duration)
- f && f(currentTime,duration);
- }
- },
- quaternion: function(currentTime, startY, f) {//xzw change, add f
- var wholeY = currentTime.clone();
- return function(duration) {
- currentTime.copy(wholeY).slerp(startY, duration);
- f && f(currentTime,duration);
- }
- },
- property: function(currentTime, startY, wholeY, duration) {
- var r = currentTime[startY];
- return function(o) {
- currentTime[startY] = r * (1 - o) + wholeY * o,
- duration && duration(currentTime[startY])
- }
- },
- uniform: function(currentTime, startY, wholeY) {
- var duration = currentTime.material.uniforms[startY].value;
- return function(r) {
- try{
- currentTime.material.uniforms[startY] && (currentTime.material.uniforms[startY].value = duration * (1 - r) + wholeY * r)
- }catch(currentTime){
- console.log(1)
- }
-
- }
- },
- matrix4: function(currentTime, startY) {
- var wholeY = currentTime.clone();
- return function(duration) {
- for (var r = currentTime.elements, o = wholeY.elements, a = startY.elements, s = 0; s < 16; s++)
- r[s] = o[s] * (1 - duration) + a[s] * duration
- }
- },
- allUniforms: function(currentTime, startY, wholeY) {
- var duration = currentTime.map(function(currentTime) {
- return this.uniform(currentTime, startY, wholeY)
- }
- .bind(this));
- return function(currentTime) {
- duration.forEach(function(startY) {
- startY(currentTime)
- })
- }
- } */
-
-
-
- vector: function(t, i, f) {//xzw change, add f
- var n = t.clone();
- return i = i.clone(),
- function(e) {
- t.set(n.x * (1 - e) + i.x * e, n.y * (1 - e) + i.y * e, n.z * (1 - e) + i.z * e);
- f && f(t,e);
- }
- },
- quaternion: function(t, i, f) {//xzw change, add f
- var n = t.clone();
- return function(e) {
- t.copy(n).slerp(i, e);
- f && f(t,e);
- }
- },
- property: function(t, i, n, r) {
- var o = t[i];
- return function(e) {
- t[i] = o * (1 - e) + n * e,
- r && r(t[i]);
- }
- },
- uniform: function(t, i, n) {
- var r = t.material.uniforms[i].value;
- return function(e) {
- t.material.uniforms[i] && (t.material.uniforms[i].value = r * (1 - e) + n * e);
- }
- },
- matrix4: function(o, a) {
- var s = o.clone();
- return function(e) {
- for (var t = o.elements, i = s.elements, n = a.elements, r = 0; r < 16; r++)
- t[r] = i[r] * (1 - e) + n[r] * e;
- }
- },
- allUniforms: function(e, t, i) {
- var n = e.map(function(e) {
- return this.uniform(e, t, i)
- }
- .bind(this));
- return function(t) {
- n.forEach(function(e) {
- e(t);
- });
- }
- }
- };
-
- /*
- 渐变
-
- */
- var transitions = {
- globalDone: null,
- funcs: [],
- counter: 0,
- uniqueID: 0,
- start: function(func, duration, done, delay, ease, name, id, cancelFun) {
- return delay = delay || 0,
- this.funcs.push({
- func: func,
- current: -delay * Math.abs(duration), //当前时间
- duration: (1 - Math.max(delay, 0)) * Math.abs(duration), //总时长
- done: done,
- easing: ease || easing.linearTween, //渐变曲线
- cycling: duration < 0,
- running: !0,
- debug: delay < 0,
- name: name || "T" + this.counter,
- id: void 0 === id ? this.counter : id,
- paused: !1,
- cancelFun : cancelFun, //取消时执行的函数
- }),
- func(0, 16),
- this.counter += 1,
- func
- },
- trigger: function(e) {
- var t = void 0 === e.delayRatio ? 0 : e.delayRatio
- , u = e.func || function() {}
- , r = void 0 === e.duration ? 0 : e.duration;
- void 0 !== e.cycling && e.cycling && (r = -Math.abs(r));
- var o = e.done || null
- , a = e.easing || easing.linearTween
- , s = e.name || "R" + this.counter
- , l = void 0 === e.id ? this.counter : e.id;
- return this.start(u, r, o, t, a, s, l)
- },
- setTimeout: function(e, t, u) {
- var duration = void 0 === u ? this.counter : u;
- return this.trigger({
- done: e,
- duration: void 0 === t ? 0 : t,
- name: "O" + this.counter,
- id: duration
- })
- },
- pause: function() {
- this.paused = !0;
- },
- resume: function() {
- this.paused = !1;
- },
- update: function(e) {
- this.funcs.forEach(function(t) {
- if (!(t.paused || (t.current += 1e3 * e,
- t.current < 0)))
- if (t.current >= t.duration && !t.cycling) {
- var u = t.easing(1, 0, 1, 1);
- t.func(u, 1e3 * e),
- t.done && t.done(),
- t.running = !1;
- } else {
- var duration = t.easing(t.current % t.duration / t.duration, 0, 1, 1)
- , r = t.func(duration, 1e3 * e) || !1;
- r && (t.done && t.done(),
- t.running = !1);
- }
- });
- var t = this.funcs.length;
- this.funcs = this.funcs.filter(function(e) {
- return e.running
- });
- var u = this.funcs.length;
- if (t > 0 && 0 === u && this.globalDone) {
- var duration = this.globalDone;
- this.globalDone = null,
- duration();
- }
- },
- adjustSpeed: function(e, t) {
- for (var u = this.getById(e), duration = 0; duration < u.length; duration++) {
- var r = u[duration];
- r.duration /= t,
- r.current /= t;
- }
- },
- getById: function(e) {
- return this.funcs.filter(function(t) {
- return e === t.id
- })
- },
- get: function(e) {
- for (var t = 0; t < this.funcs.length; t += 1)
- if (this.funcs[t].func === e)
- return this.funcs[t];
- return null
- },
- isRunning: function(e) {
- var t = this.get(e);
- return null !== t && t.running
- },
- countActive: function() {
- for (var e = 0, t = 0; t < this.funcs.length; t += 1)
- e += this.funcs[t].running;
- return e
- },
- listActive: function() {
- for (var e = [], t = 0; t < this.funcs.length; t += 1)
- this.funcs[t].running && e.push(this.funcs[t].name);
- return e
- },
- done: function(e) {
- this.globalDone = e;
- },
- cancelById: function(e, dealCancelFun) { //xzw add dealDone
- var t = void 0 === e ? 0 : e;
- let cancels = [];
- this.funcs = this.funcs.filter(function(e) {
- var is = e.id == t;
-
- if(is && dealCancelFun){
- e.cancelFun && cancels.push(e.cancelFun);
- //e.cancelFun && e.cancelFun()
- }
- return !is
- });
-
- cancels.forEach(e=>{e();}); //先从funcs中去除后再执行
-
-
- },
- cancel: function(e) {
- this.funcs = this.funcs.filter(function(t) {
- return t.func !== e
- });
- },
- getUniqueId: function() {
- return this.uniqueID -= 1,
- this.uniqueID
- }
- };
- const colors = {
- position: 'red',
- target : 'blue'
- };
-
- let lineMats$1;
- const getLineMat = function(name){
- if(!lineMats$1){
- lineMats$1 = {
- position: LineDraw.createFatLineMat({
- color: colors.position,
- lineWidth: 3
- }),
- target : LineDraw.createFatLineMat({
- color: colors.target,
- lineWidth: 3
- }),
- frustum: LineDraw.createFatLineMat({
- color: colors.position,
- lineWidth: 2
- }),
- aimAtTarget: new LineBasicMaterial({color:colors.target})
- };
- }
- return lineMats$1[name]
- };
-
- class CameraAnimation extends EventDispatcher{
- constructor(viewer){
- super();
-
- this.viewer = viewer;
- this.selectedElement = null;
- //this.controlPoints = [];
- this.uuid = MathUtils.generateUUID();
- this.node = new Object3D();
- this.node.name = "camera animation";
- this.viewer.scene.scene.add(this.node);
- this.frustum = this.createFrustum();
- this.node.add(this.frustum);
-
- this.name = "Camera Animation";
-
- // "centripetal", "chordal", "catmullrom"
- this.curveType = "centripetal";
- this.visible = true;
- this.targets = [];
- this.createPath();
- this.duration = 5;
- this.percent = 0;
- this.currentIndex = 0;
- this.quaternions = [];
-
- if(!Potree.settings.isTest){
- this.setVisible(false);
- }
-
-
-
- this.addEventListener('dispose', ()=>{
- this.dispose();
- });
-
-
- this.targetLines = new Object3D;
- this.node.add(this.targetLines);
-
-
-
-
-
- }
- static defaultFromView(viewer){
- const animation = new CameraAnimation(viewer);
- const camera = viewer.scene.getActiveCamera();
- const target = viewer.scene.view.getPivot();
- const cpCenter = new Vector3(
- 0.3 * camera.position.x + 0.7 * target.x,
- 0.3 * camera.position.y + 0.7 * target.y,
- 0.3 * camera.position.z + 0.7 * target.z,
- );
- const targetCenter = new Vector3(
- 0.05 * camera.position.x + 0.95 * target.x,
- 0.05 * camera.position.y + 0.95 * target.y,
- 0.05 * camera.position.z + 0.95 * target.z,
- );
- const r = 2;//camera.position.distanceTo(target) * 0.3;
- //const dir = target.clone().sub(camera.position).normalize();
- const angle = Utils.computeAzimuth(camera.position, target);
- const n = 5;
- for(let i = 0; i < n; i++){
- let u = 1.5 * Math.PI * (i / n) + angle;
- const dx = r * Math.cos(u);
- const dy = r * Math.sin(u);
- const cpPos = new Vector3(
- cpCenter.x + dx,
- cpCenter.y + dy,
- cpCenter.z,
- );
- const targetPos = new Vector3(
- targetCenter.x + dx * 0.1,
- targetCenter.y + dy * 0.1,
- targetCenter.z,
- );
- animation.createControlPoint(null,{position:cpPos, target:targetPos});
-
-
- }
- animation.changeCallback();
- return animation;
- }
-
- createControlPoint(index, posInfo ){
- const length = this.posCurve.points.length;
- const position = new Vector3;
- const target = new Vector3;
-
- if(index == void 0 ){
- index = length;
- }
-
- if(!posInfo){
-
- if(length >= 2 && index === 0){
- const dir = new Vector3().subVectors(this.posCurve.points[0], this.posCurve.points[1] );
- position.copy(this.posCurve.points[0]).add(dir);
-
- const tDir = new Vector3().subVectors(this.targets[0].position, this.targets[1].position );
- target.copy(this.targets[0].position).add(dir);
-
- }else if(length >= 2 && index === length){
- const dir = new Vector3().subVectors(this.posCurve.points[length-1], this.posCurve.points[length-2] );
- position.copy(this.posCurve.points[length-2]).add(dir);
-
- const tDir = new Vector3().subVectors(this.targets[length-1].position, this.targets[length-2].position );
- target.copy(this.targets[length-2].position).add(dir);
-
- }else if(length >= 2){
- position.copy(this.posCurve.points[index-1].clone().add(this.posCurve.points[index]).multiplyScalar(0.5));
- target.copy(this.targets[length-1].position.clone().add(this.targets[length]).multiplyScalar(0.5));
- }
- }else {
- position.copy(posInfo.position);
- target.copy(posInfo.target);
- }
-
- this.posCurve.addPoint(position, index/* , true */);
- //this.targetCurve.addPoint(target, index/* , true */)
- let targetSvg = new HandleSvg(target, colors.target);
- targetSvg.visible = this.visible;
- this.targets = [...this.targets.slice(0,index), targetSvg, ...this.targets.slice(index,length)];
-
-
- this.dispatchEvent({
- type: "controlpoint_added",
- index
- });
-
- {
- let targetLine = LineDraw.createLine([position,target] ,{mat: getLineMat('aimAtTarget')});
- this.targetLines.children = [...this.targetLines.children.slice(0,index), targetLine, ...this.targetLines.children.slice(index,length)];
-
-
-
- this.targets[index].addEventListener('dragged', (e)=>{
- this.updatePathCallback();
- this.dragPointCallback(e);
- });
- }
- }
-
- dragPointCallback(e){
-
- let index = e.index;
- if(e.index == void 0){
- index = this.targets.indexOf(e.target);
- }
- LineDraw.moveLine(this.targetLines.children[index], [this.posCurve.points[index], this.targets[index].position] );
-
- this.updateFrustum();
- }
-
- updatePathCallback(){
- {
- this.quaternions = [];
- let length = this.posCurve.points.length;
- for(let i=0; i<length; i++){
- let quaternion = math.getQuaFromPosAim(this.posCurve.points[i], this.targets[i].position);
- this.quaternions.push( quaternion);
- }
- }
- this.reMapCurvePercent();
- }
-
-
- removeControlPoint(index){
- this.posCurve.removePoint(index);
- //this.targetCurve.removePoint(index)
- this.targets[index].dispose();
- this.targets.splice(index, 1);
-
- this.dispatchEvent({
- type: "controlpoint_removed",
- index
- });
- this.targetLines.remove(this.targetLines.children[index]);
-
-
- }
- createPath(){
- this.posCurve = new CurveCtrl([],getLineMat('position'), colors.position, 'posCurve');
- //this.targetCurve = new CurveCtrl([], getLineMat('target'), colors.target, 'targetCurve', {noLine:true});
- this.posCurve.needsPercent = true;
- this.node.add(this.posCurve);
- //this.node.add(this.targetCurve)
-
- this.posCurve.addEventListener('dragCurvePoint', this.dragPointCallback.bind(this));
- this.posCurve.addEventListener('updatePath', this.updatePathCallback.bind(this));
-
-
-
-
- }
-
- createFrustum(){
- const f = 0.3;
- const positions = [
- new Vector3( 0, 0, 0),
- new Vector3(-f, -f, +1),
- new Vector3( 0, 0, 0),
- new Vector3( f, -f, +1),
- new Vector3( 0, 0, 0),
- new Vector3( f, f, +1),
- new Vector3( 0, 0, 0),
- new Vector3(-f, f, +1),
- new Vector3(-f, -f, +1),
- new Vector3( f, -f, +1),
- new Vector3( f, -f, +1),
- new Vector3( f, f, +1),
- new Vector3( f, f, +1),
- new Vector3(-f, f, +1),
- new Vector3(-f, f, +1),
- new Vector3(-f, -f, +1),
- ];
-
- positions.forEach(e=>e.z *= -1); //因为得到的rotation是camera的,作用在物体上要反向,所以这里反向一下
-
- //geometry.computeBoundingSphere();//?
- const line = LineDraw.createFatLine( positions, {material:getLineMat('frustum')});
-
- //line.scale.set(20, 20, 20);
- line.visible = false;
- return line;
- }
- reMapCurvePercent(){ //因在不同点在相同位置旋转,由于间隔仅和位置距离相关,导致时间间隔为0,采取重新调整间隔的策略。
- var length = this.posCurve.points.length;
-
- if(length<2){
- return this.newPointsPercents = []
- }
- const maxSpaceDur = this.duration / length; //每两点之间修改间隔时间后,最大时间
- const durPerRad = 0.8; //每弧度应该占用的时间
- const minSpaceDur = Math.min(0.8, maxSpaceDur);//每两点之间修改间隔时间后,最小时间
- const maxAngleSpaceDur = MathUtils.clamp(durPerRad * Math.PI, minSpaceDur, maxSpaceDur );// 最大可能差距是180度
-
-
-
- var percents = this.posCurve.pointsPercent;
- var newPercents = [0];
-
-
- for(let i=1;i<length;i++){
- let diff = (percents[i] - percents[i-1]) * this.duration; //间隔时间
- let percent;
- let curMin = minSpaceDur;
- if(diff < maxAngleSpaceDur){ //若小于最大旋转时间
- let rad = this.quaternions[i].angleTo(this.quaternions[i-1]);
- curMin = MathUtils.clamp(rad * durPerRad, minSpaceDur, maxSpaceDur);
-
- }
- diff = Math.max(diff, curMin);
- percent = newPercents[i-1] + (diff / this.duration); //得到新的percent
-
-
- newPercents.push(percent);
- }
-
- let maxPercent = newPercents[length-1]; //最后一个,若扩充过时间,就会>1
- if( !math.closeTo(maxPercent, 1)){
- let scale = 1 / maxPercent; //需要压缩的比例 <1 这一步会让实际得到的间隔更小
- newPercents = newPercents.map(e=> e*=scale );
- }
- this.newPointsPercents = newPercents;
- //console.log(newPercents)
- }
- at(originPercent, delta, transitionRatio){
-
- originPercent = MathUtils.clamp(originPercent, 0, 1);
- //修改第一层:起始时间
- let percent = originPercent;
- /* const easePercent = 0.3; //缓动占比 //如果能在所有从静止到运动的中间加缓动就好了呀:lastPos * 0.9 + currentPos * 0.1 ?
- if(originPercent < easePercent){
- console.log('easeIn')
- percent = easing.easeInSine(originPercent, 0, easePercent, easePercent) //currentTime, startY, wholeY, duration 选了一个衔接时接近斜率1的缓动函数
- }else if(originPercent > 1-easePercent){
- console.log('easeOut')
- percent = easing.easeOutSine(originPercent-(1-easePercent), 1-easePercent, easePercent, easePercent)
- } */
-
-
-
-
- let quaternion;
-
- if(percent < 1){
- //修改第二层:使用每个点的重定位的 newPointsPercents
-
- this.currentIndex = this.newPointsPercents.findIndex(e=> e>percent ) - 1;
- //假设每个节点的百分比是精确的,那么:
- let curIndexPercent = this.newPointsPercents[this.currentIndex];
- let nextIndexPercent = this.newPointsPercents[this.currentIndex+1];
- let progress = (percent - curIndexPercent) / (nextIndexPercent - curIndexPercent);//在这两个节点间的百分比
-
- //投影到原本的 posCurve.pointsPercent上:
- let curIndexOriPercent = this.posCurve.pointsPercent[this.currentIndex];
- let nextIndexOriPercent = this.posCurve.pointsPercent[this.currentIndex+1];
- percent = curIndexOriPercent + (nextIndexOriPercent - curIndexOriPercent) * progress;
-
- let endQuaternion = this.quaternions[this.currentIndex+1];
- let startQuaternion = this.quaternions[this.currentIndex];
- quaternion = (new Quaternion()).copy(startQuaternion);
- lerp.quaternion(quaternion, endQuaternion)(progress);
-
-
- }else {
-
-
- this.currentIndex = this.posCurve.points.length - 1;
-
- quaternion = math.getQuaFromPosAim(this.posCurve.points[this.currentIndex], this.targets[this.currentIndex].position);
- }
-
-
- const position = this.posCurve.getPointAt(percent); // 需要this.posCurve.points.length>1 否则报错
-
-
- //console.log(this.currentIndex, originPercent)
- //缓动:
- var aimQua, aimPos;
- if(delta != void 0 ){
- if(Potree.settings.tourTestCameraMove){
- aimQua = this.frustum.quaternion.clone();
- aimPos = this.frustum.position.clone();
- }else {
- var camera = viewer.scene.getActiveCamera();
- aimQua = camera.quaternion.clone();
- aimPos = camera.position.clone();
- }
-
- transitionRatio = transitionRatio || 1 / Potree.settings.cameraAniSmoothRatio;//渐变系数,越小缓动程度越高,越平滑
- transitionRatio *= delta * 60; //假设标准帧率为60fps,当帧率低时(delta大时)要降低缓动
- //console.log(transitionRatio, delta) //画面ui变化会使delta变大
- transitionRatio = MathUtils.clamp(transitionRatio, 0, 1);
- lerp.quaternion(aimQua, quaternion)(transitionRatio); //每次只改变一点点
- lerp.vector(aimPos, position)(transitionRatio);
-
- }else {
- aimQua = quaternion; aimPos = position;
- }
-
-
-
- let rotation = new Euler().setFromQuaternion(aimQua );
-
-
- const frame = {
- position: aimPos,
- rotation
- };
- return frame;
- }
- set(percent){
- this.percent = percent;
- }
-
- setVisible(visible){
- this.node.visible = visible;
-
- this.posCurve.visible = visible;
- this.targets.forEach(e=>e.visible = visible );
- this.visible = visible;
- }
- setDuration(duration){
- if(duration != this.duration){
- this.duration = duration;
- if(this.quaternions.length == this.posCurve.points.length)this.reMapCurvePercent();
-
- }
- }
- getDuration(duration){
- return this.duration;
- }
- play(startOptions={}){
- if(this.onUpdate){
- return console.error('已经开始播放')
- }
-
-
-
- let startPercent = 0, currentIndex = 0;
- if(startOptions.percent != void 0 ){
- startPercent = startOptions.percent;
- }else if(startOptions.index){
- currentIndex = index;
- //startPercent = index/(this.posCurve.points.length-1)
-
- startPercent = this.posCurve.pointsPercent[index];
- }
-
-
- //const tStart = performance.now();
-
-
- const duration = this.duration;
- this.originalyVisible = this.visible;
- Potree.settings.tourTestCameraMove || this.setVisible(false);
-
-
- let tStart, startTransitionRatio = 0.2;
- let startDelay = 1/startTransitionRatio / 20 ;//因为缓动所以延迟开始,前面前都是at(0),使过渡到开始点位(但是依旧不能准确停在起始点,因为缓动是乘百分比有残留。所以直接平滑衔接到开始后的位置)
- let hasPlayedTime = 0;
-
- let finishDelay = Potree.settings.cameraAniSmoothRatio / 60 * 3;//结束后还需要多久时间才能大致达到缓动的最终目标
- let hasStoppedTime = 0;
- this.onUpdate = (e) => {
- if(this.posCurve.points.length<2){
- if(this.posCurve.points.length == 1){
- viewer.scene.view.position.copy(this.posCurve.points[0]);
- viewer.scene.view.rotation = new Euler().setFromQuaternion(this.quaternions[0]);
- }
- this.pause();
- return
- }
-
- let percent, transitionRatio;
-
- if(tStart){
- let tNow = performance.now();
- let elapsed = (tNow - tStart) / 1000;
- percent = elapsed / duration + startPercent;
- }else {//从当前位置过渡到开始位置
- percent = 0;
- hasPlayedTime += e.delta;
- transitionRatio = startTransitionRatio;
- console.log('延迟开始');
- if(hasPlayedTime > startDelay){
- tStart = performance.now();
- }
-
- }
-
- this.set(percent);
-
- const frame = this.at(percent, e.delta, transitionRatio);
-
- if(currentIndex != this.currentIndex){
- currentIndex = this.currentIndex;
- console.log('updateCurrentIndex', currentIndex);
- this.dispatchEvent({type:'updateCurrentIndex', currentIndex });
- }
-
-
-
- if(!Potree.settings.tourTestCameraMove){
- viewer.scene.view.position.copy(frame.position);
- //viewer.scene.view.lookAt(frame.target);
- viewer.scene.view.rotation = frame.rotation;
- }
- this.updateFrustum(frame);
-
-
-
- if(percent >= 1){
- if(hasStoppedTime > finishDelay){
- this.pause();
- }else {
- hasStoppedTime += e.delta;
- console.log('延迟结束');
- }
-
- }
-
-
-
- };
- this.viewer.addEventListener("update", this.onUpdate);
-
-
- }
- pause(){
- this.setVisible(this.originalyVisible);
- this.viewer.removeEventListener("update", this.onUpdate);
- this.dispatchEvent('playDone');
- this.onUpdate = null;
- }
- updateFrustum(frame){
- const frustum = this.frustum;
- if(this.posCurve.points.length>1){
- frustum.visible = true;
- }else {
- frustum.visible = false;
- return
- }
-
- frame = frame || this.at(this.percent);
- frustum.position.copy(frame.position);
- //frustum.lookAt(...frame.target.toArray());
- frustum.rotation.copy(frame.rotation);
- }
- changeCallback(){
- this.posCurve.update();
- //this.targetCurve.update()
- this.targets.forEach(e=>e.update());
- this.updateFrustum();
- }
- dispose(){//add
- this.posCurve.dispose();
- //this.targetCurve.dispatchEvent({type:'dispose'})
- this.targets.forEach(e=>e.dispose());
-
- this.node.parent.remove(this.node);
- }
- }
- //scene.removeCameraAnimation
- //修改:不使用targetCurve作为target曲线,因为播放时posCuve的节点和targetCurve并没有对应,且使用target的曲线会使角度变化大的情况过渡生硬。
- // 改完旋转了。但是位置也有问题。速度完全和路程相关,当在同一位置设置多点时,这段的总时长为0. (是否要设置最小时长?不过也做不到 - -)
- function loadPointCloud(viewer, data){
- let loadMaterial = (target) => {
- if(data.material){
- if(data.material.activeAttributeName != null){
- target.activeAttributeName = data.material.activeAttributeName;
- }
- if(data.material.ranges != null){
- for(let range of data.material.ranges){
- if(range.name === "elevationRange"){
- target.elevationRange = range.value;
- }else if(range.name === "intensityRange"){
- target.intensityRange = range.value;
- }else {
- target.setRange(range.name, range.value);
- }
- }
- }
- if(data.material.size != null){
- target.size = data.material.size;
- }
- if(data.material.minSize != null){
- target.minSize = data.material.minSize;
- }
- if(data.material.pointSizeType != null){
- target.pointSizeType = PointSizeType[data.material.pointSizeType];
- }
- if(data.material.matcap != null){
- target.matcap = data.material.matcap;
- }
- }else if(data.activeAttributeName != null){
- target.activeAttributeName = data.activeAttributeName;
- }else {
- // no material data
- }
- };
- const promise = new Promise((resolve) => {
- const names = viewer.scene.pointclouds.map(p => p.name);
- const alreadyExists = names.includes(data.name);
- if(alreadyExists){
- resolve();
- return;
- }
- Potree.loadPointCloud(data.url, data.name, (e) => {
- const {pointcloud} = e;
- pointcloud.position.set(...data.position);
- pointcloud.rotation.set(...data.rotation);
- pointcloud.scale.set(...data.scale);
- loadMaterial(pointcloud.material);
- viewer.scene.addPointCloud(pointcloud);
- resolve(pointcloud);
- });
- });
- return promise;
- }
- function loadMeasurement(viewer, data){
- const duplicate = viewer.scene.measurements.find(measure => measure.uuid === data.uuid);
- if(duplicate){
- return;
- }
-
- data.points = data.points.map(point=>new Vector3(...point));
-
- const measure = viewer.measuringTool.createMeasureFromData(data);
-
- }
- function loadVolume(viewer, data){
- const duplicate = viewer.scene.volumes.find(volume => volume.uuid === data.uuid);
- if(duplicate){
- return;
- }
- let volume = new Potree[data.type];
- volume.uuid = data.uuid;
- volume.name = data.name;
- volume.position.set(...data.position);
- volume.rotation.set(...data.rotation);
- volume.scale.set(...data.scale);
- volume.visible = data.visible;
- volume.clip = data.clip;
- viewer.scene.addVolume(volume);
- }
- function loadCameraAnimation(viewer, data){
- const duplicate = viewer.scene.cameraAnimations.find(a => a.uuid === data.uuid);
- if(duplicate){
- return;
- }
- const animation = new CameraAnimation(viewer);
- animation.uuid = data.uuid;
- animation.name = data.name;
- animation.duration = data.duration;
- animation.t = data.t;
- animation.curveType = data.curveType;
- animation.visible = data.visible;
- animation.controlPoints = [];
- for(const cpdata of data.controlPoints){
- const cp = animation.createControlPoint();
- cp.position.set(...cpdata.position);
- cp.target.set(...cpdata.target);
- }
- viewer.scene.addCameraAnimation(animation);
- }
- function loadOrientedImages(viewer, images){
- const {cameraParamsPath, imageParamsPath} = images;
- const duplicate = viewer.scene.orientedImages.find(i => i.imageParamsPath === imageParamsPath);
- if(duplicate){
- return;
- }
- Potree.OrientedImageLoader.load(cameraParamsPath, imageParamsPath, viewer).then( images => {
- viewer.scene.addOrientedImages(images);
- });
- }
- function loadGeopackage(viewer, geopackage){
- const path = geopackage.path;
- const duplicate = viewer.scene.geopackages.find(i => i.path === path);
- if(duplicate){
- return;
- }
- const projection = viewer.getProjection();
- proj4.defs("WGS84", "+proj=longlat +ellps=WGS84 +datum=WGS84 +no_defs");
- proj4.defs("pointcloud", projection);
- const transform = proj4("WGS84", "pointcloud");
- const params = {
- transform: transform,
- };
- Potree.GeoPackageLoader.loadUrl(path, params).then(data => {
- viewer.scene.addGeopackage(data);
- });
-
- }
- function loadSettings(viewer, data){
- if(!data){
- return;
- }
- viewer.setPointBudget(data.pointBudget);
- viewer.setFOV(data.fov);
- viewer.setEDLEnabled(data.edlEnabled);
- viewer.setEDLRadius(data.edlRadius);
- viewer.setEDLStrength(data.edlStrength);
- viewer.setBackground(data.background);
- viewer.setMinNodeSize(data.minNodeSize);
- viewer.setShowBoundingBox(data.showBoundingBoxes);
- }
- function loadView(viewer, view){
- viewer.scene.view.position.set(...view.position);
- viewer.scene.view.lookAt(...view.target);
- }
- function loadAnnotationItem(item){
- const annotation = new Annotation({
- position: item.position,
- title: item.title,
- cameraPosition: item.cameraPosition,
- cameraTarget: item.cameraTarget,
- });
- annotation.description = item.description;
- annotation.uuid = item.uuid;
- if(item.offset){
- annotation.offset.set(...item.offset);
- }
- return annotation;
- }
- function loadAnnotations(viewer, data){
- if(!data){
- return;
- }
- const findDuplicate = (item) => {
- let duplicate = null;
- viewer.scene.annotations.traverse( a => {
- if(a.uuid === item.uuid){
- duplicate = a;
- }
- });
- return duplicate;
- };
- const traverse = (item, parent) => {
- const duplicate = findDuplicate(item);
- if(duplicate){
- return;
- }
- const annotation = loadAnnotationItem(item);
- for(const childItem of item.children){
- traverse(childItem, annotation);
- }
- parent.add(annotation);
- };
- for(const item of data){
- traverse(item, viewer.scene.annotations);
- }
- }
- function loadProfile(viewer, data){
-
- const {name, points} = data;
- const duplicate = viewer.scene.profiles.find(profile => profile.uuid === data.uuid);
- if(duplicate){
- return;
- }
- let profile = new Potree.Profile();
- profile.name = name;
- profile.uuid = data.uuid;
- profile.setWidth(data.width);
- for(const point of points){
- profile.addMarker(new Vector3(...point));
- }
-
- viewer.scene.addProfile(profile);
- }
- function loadClassification(viewer, data){
- if(!data){
- return;
- }
- const classifications = data;
- viewer.setClassifications(classifications);
- }
- async function loadProject(viewer, data, done){
- if(data.type !== "Potree"){
- console.error("not a valid Potree project");
- return;
- }
- loadSettings(viewer, data.settings);
- loadView(viewer, data.view);
- /* const pointcloudPromises = [];
- for(const pointcloud of data.pointclouds){
- const promise = loadPointCloud(viewer, pointcloud);
- pointcloudPromises.push(promise);
- } */
- for(const measure of data.measurements){
- loadMeasurement(viewer, measure);
- }
- for(const volume of data.volumes){
- loadVolume(viewer, volume);
- }
- for(const animation of data.cameraAnimations){
- loadCameraAnimation(viewer, animation);
- }
- for(const profile of data.profiles){
- loadProfile(viewer, profile);
- }
- if(data.orientedImages){
- for(const images of data.orientedImages){
- loadOrientedImages(viewer, images);
- }
- }
- loadAnnotations(viewer, data.annotations);
- loadClassification(viewer, data.classification);
- done && done();
- // need to load at least one point cloud that defines the scene projection,
- // before we can load stuff in other projections such as geopackages
- //await Promise.any(pointcloudPromises); // (not yet supported)
- /* Utils.waitAny(pointcloudPromises).then( () => {
- if(data.geopackages){
- for(const geopackage of data.geopackages){
- loadGeopackage(viewer, geopackage);
- }
- }
- });
- await Promise.all(pointcloudPromises); */
- }
- //
- // Algorithm by Christian Boucheny
- // shader code taken and adapted from CloudCompare
- //
- // see
- // https://github.com/cloudcompare/trunk/tree/master/plugins/qEDL/shaders/EDL
- // http://www.kitware.com/source/home/post/9
- // https://tel.archives-ouvertes.fr/tel-00438464/document p. 115+ (french)
- class EyeDomeLightingMaterial extends RawShaderMaterial{
- constructor(parameters = {}){
- super();
- let uniforms = {
- /* screenWidth: { type: 'f', value: 0 },
- screenHeight: { type: 'f', value: 0 }, */
- resolution: { type: 'v2', value: new Vector2$1() },
- edlStrength: { type: 'f', value: 1.0 },
- uNear: { type: 'f', value: 1.0 },
- uFar: { type: 'f', value: 1.0 },
- radius: { type: 'f', value: 1.0 },
- neighbours: { type: '2fv', value: [] },
- depthMap: { type: 't', value: null },
- uEDLColor: { type: 't', value: null },
- uEDLDepth: { type: 't', value: null },
- opacity: { type: 'f', value: 1.0 },
- uProj: { type: "Matrix4fv", value: [] },
-
-
- useEDL: { type: 'i', value: 1 }//add
- };
- this.setValues({
- uniforms: uniforms,
- vertexShader: this.getDefines() + Shaders['edl.vs'],
- fragmentShader: this.getDefines() + Shaders['edl.fs'],
- lights: false
- });
- this.neighbourCount = 8;
- }
- getDefines() {
- let defines = '';
- defines += '#define NEIGHBOUR_COUNT ' + this.neighbourCount + '\n';
- return defines;
- }
- updateShaderSource() {
- let vs = this.getDefines() + Shaders['edl.vs'];
- let fs = this.getDefines() + Shaders['edl.fs'];
- this.setValues({
- vertexShader: vs,
- fragmentShader: fs
- });
- this.uniforms.neighbours.value = this.neighbours;
- this.needsUpdate = true;
- }
- get neighbourCount(){
- return this._neighbourCount;
- }
- set neighbourCount(value){
- if (this._neighbourCount !== value) { //周围八个格子
- this._neighbourCount = value;
- this.neighbours = new Float32Array(this._neighbourCount * 2);
- for (let c = 0; c < this._neighbourCount; c++) {
- this.neighbours[2 * c + 0] = Math.cos(2 * c * Math.PI / this._neighbourCount);
- this.neighbours[2 * c + 1] = Math.sin(2 * c * Math.PI / this._neighbourCount);
- }
- this.updateShaderSource();
- }
- }
-
- }
- class NormalizationEDLMaterial extends RawShaderMaterial{
- constructor(parameters = {}){
- super();
- let uniforms = {
- screenWidth: { type: 'f', value: 0 },
- screenHeight: { type: 'f', value: 0 },
- edlStrength: { type: 'f', value: 1.0 },
- radius: { type: 'f', value: 1.0 },
- neighbours: { type: '2fv', value: [] },
- uEDLMap: { type: 't', value: null },
- uDepthMap: { type: 't', value: null },
- uWeightMap: { type: 't', value: null },
- };
- this.setValues({
- uniforms: uniforms,
- vertexShader: this.getDefines() + Shaders['normalize.vs'],
- fragmentShader: this.getDefines() + Shaders['normalize_and_edl.fs'],
- });
- this.neighbourCount = 8;
- }
- getDefines() {
- let defines = '';
- defines += '#define NEIGHBOUR_COUNT ' + this.neighbourCount + '\n';
- return defines;
- }
- updateShaderSource() {
- let vs = this.getDefines() + Shaders['normalize.vs'];
- let fs = this.getDefines() + Shaders['normalize_and_edl.fs'];
- this.setValues({
- vertexShader: vs,
- fragmentShader: fs
- });
- this.uniforms.neighbours.value = this.neighbours;
- this.needsUpdate = true;
- }
- get neighbourCount(){
- return this._neighbourCount;
- }
- set neighbourCount(value){
- if (this._neighbourCount !== value) {
- this._neighbourCount = value;
- this.neighbours = new Float32Array(this._neighbourCount * 2);
- for (let c = 0; c < this._neighbourCount; c++) {
- this.neighbours[2 * c + 0] = Math.cos(2 * c * Math.PI / this._neighbourCount);
- this.neighbours[2 * c + 1] = Math.sin(2 * c * Math.PI / this._neighbourCount);
- }
- this.updateShaderSource();
- }
- }
-
- }
- class NormalizationMaterial extends RawShaderMaterial{
- constructor(parameters = {}){
- super();
- let uniforms = {
- uDepthMap: { type: 't', value: null },
- uWeightMap: { type: 't', value: null },
- };
- this.setValues({
- uniforms: uniforms,
- vertexShader: this.getDefines() + Shaders['normalize.vs'],
- fragmentShader: this.getDefines() + Shaders['normalize.fs'],
- });
- }
- getDefines() {
- let defines = '';
- return defines;
- }
- updateShaderSource() {
- let vs = this.getDefines() + Shaders['normalize.vs'];
- let fs = this.getDefines() + Shaders['normalize.fs'];
- this.setValues({
- vertexShader: vs,
- fragmentShader: fs
- });
- this.needsUpdate = true;
- }
- }
- /**
- * laslaz code taken and adapted from plas.io js-laslaz
- * http://plas.io/
- * https://github.com/verma/plasio
- *
- * Thanks to Uday Verma and Howard Butler
- *
- */
- class LasLazLoader {
- constructor (version, extension) {
- if (typeof (version) === 'string') {
- this.version = new Version(version);
- } else {
- this.version = version;
- }
- this.extension = extension;
- }
- static progressCB () {
- }
- load (node) {
- if (node.loaded) {
- return;
- }
- let url = node.getURL();
- if (this.version.equalOrHigher('1.4')) {
- url += `.${this.extension}`;
- }
- let xhr = XHRFactory.createXMLHttpRequest();
- xhr.open('GET', url, true);
- xhr.responseType = 'arraybuffer';
- xhr.overrideMimeType('text/plain; charset=x-user-defined');
- xhr.onreadystatechange = () => {
- if (xhr.readyState === 4) {
- if (xhr.status === 200 || xhr.status === 0) {
- let buffer = xhr.response;
- this.parse(node, buffer);
- } else {
- console.log('Failed to load file! HTTP status: ' + xhr.status + ', file: ' + url);
- }
- }
- };
- xhr.send(null);
- }
- async parse(node, buffer){
- let lf = new LASFile(buffer);
- let handler = new LasLazBatcher(node);
- try{
- await lf.open();
- lf.isOpen = true;
- }catch(e){
- console.log("failed to open file. :(");
- return;
- }
- let header = await lf.getHeader();
- let skip = 1;
- let totalRead = 0;
- let totalToRead = (skip <= 1 ? header.pointsCount : header.pointsCount / skip);
- let hasMoreData = true;
- while(hasMoreData){
- let data = await lf.readData(1000 * 1000, 0, skip);
- handler.push(new LASDecoder(data.buffer,
- header.pointsFormatId,
- header.pointsStructSize,
- data.count,
- header.scale,
- header.offset,
- header.mins, header.maxs));
- totalRead += data.count;
- LasLazLoader.progressCB(totalRead / totalToRead);
- hasMoreData = data.hasMoreData;
- }
- header.totalRead = totalRead;
- header.versionAsString = lf.versionAsString;
- header.isCompressed = lf.isCompressed;
- LasLazLoader.progressCB(1);
- try{
- await lf.close();
- lf.isOpen = false;
- }catch(e){
- console.error("failed to close las/laz file!!!");
-
- throw e;
- }
- }
- handle (node, url) {
- }
- };
- class LasLazBatcher{
- constructor (node) {
- this.node = node;
- }
- push (lasBuffer) {
- const workerPath = Potree.scriptPath + '/workers/LASDecoderWorker.js';
- const worker = Potree.workerPool.getWorker(workerPath);
- const node = this.node;
- const pointAttributes = node.pcoGeometry.pointAttributes;
- worker.onmessage = (e) => {
- let geometry = new BufferGeometry();
- let numPoints = lasBuffer.pointsCount;
- let positions = new Float32Array(e.data.position);
- let colors = new Uint8Array(e.data.color);
- let intensities = new Float32Array(e.data.intensity);
- let classifications = new Uint8Array(e.data.classification);
- let returnNumbers = new Uint8Array(e.data.returnNumber);
- let numberOfReturns = new Uint8Array(e.data.numberOfReturns);
- let pointSourceIDs = new Uint16Array(e.data.pointSourceID);
- let indices = new Uint8Array(e.data.indices);
- geometry.setAttribute('position', new BufferAttribute(positions, 3));
- geometry.setAttribute('color', new BufferAttribute(colors, 4, true));
- geometry.setAttribute('intensity', new BufferAttribute(intensities, 1));
- geometry.setAttribute('classification', new BufferAttribute(classifications, 1));
- geometry.setAttribute('return number', new BufferAttribute(returnNumbers, 1));
- geometry.setAttribute('number of returns', new BufferAttribute(numberOfReturns, 1));
- geometry.setAttribute('source id', new BufferAttribute(pointSourceIDs, 1));
- geometry.setAttribute('indices', new BufferAttribute(indices, 4));
- geometry.attributes.indices.normalized = true;
- for(const key in e.data.ranges){
- const range = e.data.ranges[key];
- const attribute = pointAttributes.attributes.find(a => a.name === key);
- attribute.range[0] = Math.min(attribute.range[0], range[0]);
- attribute.range[1] = Math.max(attribute.range[1], range[1]);
- }
- let tightBoundingBox = new Box3(
- new Vector3().fromArray(e.data.tightBoundingBox.min),
- new Vector3().fromArray(e.data.tightBoundingBox.max)
- );
- geometry.boundingBox = this.node.boundingBox;
- this.node.tightBoundingBox = tightBoundingBox;
- this.node.geometry = geometry;
- this.node.numPoints = numPoints;
- this.node.loaded = true;
- this.node.loading = false;
- Potree.numNodesLoading--;
- this.node.mean = new Vector3(...e.data.mean);
- Potree.workerPool.returnWorker(workerPath, worker);
- };
- let message = {
- buffer: lasBuffer.arrayb,
- numPoints: lasBuffer.pointsCount,
- pointSize: lasBuffer.pointSize,
- pointFormatID: 2,
- scale: lasBuffer.scale,
- offset: lasBuffer.offset,
- mins: lasBuffer.mins,
- maxs: lasBuffer.maxs
- };
- worker.postMessage(message, [message.buffer]);
- };
- }
- //加载 解析点云
- class BinaryLoader{
- constructor(version, boundingBox, scale){
- if (typeof (version) === 'string') {
- this.version = new Version(version);
- } else {
- this.version = version;
- }
- this.boundingBox = boundingBox;
- this.scale = scale;
- }
- load(node){
- if (node.loaded) {
- return;
- }
- let url = node.getURL();
- if (this.version.equalOrHigher('1.4')) {
- url += '.bin';
- }
- url += '?m='+node.pcoGeometry.timeStamp; //add
-
- let xhr = XHRFactory.createXMLHttpRequest();
- xhr.open('GET', url, true);
- xhr.responseType = 'arraybuffer';
- xhr.overrideMimeType('text/plain; charset=x-user-defined');
- xhr.onreadystatechange = () => {
- if (xhr.readyState === 4) {
- if((xhr.status === 200 || xhr.status === 0) && xhr.response !== null){
- let buffer = xhr.response;
- this.parse(node, buffer);
- } else {
- //console.error(`Failed to load file! HTTP status: ${xhr.status}, file: ${url}`);
- throw new Error(`Failed to load file! HTTP status: ${xhr.status}, file: ${url}`);
- }
- }
- };
-
- try {
- xhr.send(null);
- } catch (e) {
- console.log('fehler beim laden der punktwolke: ' + e);
- }
- };
- parse(node, buffer){ //解析点云
- let pointAttributes = node.pcoGeometry.pointAttributes;
- let numPoints = buffer.byteLength / node.pcoGeometry.pointAttributes.byteSize;
- if (this.version.upTo('1.5')) {
- node.numPoints = numPoints;
- }
- let workerPath = Potree.scriptPath + '/workers/BinaryDecoderWorker.js';
- let worker = Potree.workerPool.getWorker(workerPath);
- worker.onmessage = function (e) {
- let data = e.data;
- let buffers = data.attributeBuffers;
- let tightBoundingBox = new Box3(
- new Vector3().fromArray(data.tightBoundingBox.min),
- new Vector3().fromArray(data.tightBoundingBox.max)
- );
- Potree.workerPool.returnWorker(workerPath, worker);
- let geometry = new BufferGeometry();
- for(let property in buffers){
- let buffer = buffers[property].buffer;
- let batchAttribute = buffers[property].attribute;
- if (property === "POSITION_CARTESIAN") {
- geometry.setAttribute('position', new BufferAttribute(new Float32Array(buffer), 3));
- } else if (property === "rgba") {
- geometry.setAttribute("rgba", new BufferAttribute(new Uint8Array(buffer), 4, true));
- } else if (property === "NORMAL_SPHEREMAPPED") {
- geometry.setAttribute('normal', new BufferAttribute(new Float32Array(buffer), 3));
- } else if (property === "NORMAL_OCT16") {
- geometry.setAttribute('normal', new BufferAttribute(new Float32Array(buffer), 3));
- } else if (property === "NORMAL") {
- geometry.setAttribute('normal', new BufferAttribute(new Float32Array(buffer), 3));
- } else if (property === "INDICES") {
- let bufferAttribute = new BufferAttribute(new Uint8Array(buffer), 4);
- bufferAttribute.normalized = true;
- geometry.setAttribute('indices', bufferAttribute);
- } else if (property === "SPACING") {
- let bufferAttribute = new BufferAttribute(new Float32Array(buffer), 1);
- geometry.setAttribute('spacing', bufferAttribute);
- } else {
- const bufferAttribute = new BufferAttribute(new Float32Array(buffer), 1);
- bufferAttribute.potree = {
- offset: buffers[property].offset,
- scale: buffers[property].scale,
- preciseBuffer: buffers[property].preciseBuffer,
- range: batchAttribute.range,
- };
- geometry.setAttribute(property, bufferAttribute);
- const attribute = pointAttributes.attributes.find(a => a.name === batchAttribute.name);
- attribute.range[0] = Math.min(attribute.range[0], batchAttribute.range[0]);
- attribute.range[1] = Math.max(attribute.range[1], batchAttribute.range[1]);
- if(node.getLevel() === 0){
- attribute.initialRange = batchAttribute.range;
- }
- }
- }
- tightBoundingBox.max.sub(tightBoundingBox.min);
- tightBoundingBox.min.set(0, 0, 0);
- let numPoints = e.data.buffer.byteLength / pointAttributes.byteSize;
-
- node.numPoints = numPoints;
- node.geometry = geometry;
- node.mean = new Vector3(...data.mean);
- node.tightBoundingBox = tightBoundingBox;
- node.loaded = true;
- node.loading = false;
- node.estimatedSpacing = data.estimatedSpacing;
- Potree.numNodesLoading--;
-
-
-
-
-
-
-
-
-
-
-
- };
-
- let message = {
- buffer: buffer,
- pointAttributes: pointAttributes,
- version: this.version.version,
- min: [ node.boundingBox.min.x, node.boundingBox.min.y, node.boundingBox.min.z ],
- offset: [node.pcoGeometry.offset.x, node.pcoGeometry.offset.y, node.pcoGeometry.offset.z],
- scale: this.scale,
- spacing: node.spacing,
- hasChildren: node.hasChildren,
- name: node.name
- };
- worker.postMessage(message, [message.buffer]);
- };
-
- }
- var transformFrom4dkk = function(name){
- var jsAttribute = PointAttribute[name];
- return jsAttribute
- /* if(name == "POSITION_CARTESIAN"){
- jsAttribute = {
- description: "",
- elementSize: 4,
- elements: 3,
- name: "POSITION_CARTESIAN",
- size: 12,
- type: "int32",
- }
- }else if(name == "COLOR_PACKED"){
- jsAttribute = {
- description: "" ,
- elementSize: 1,
- elements: 4,
- name: "RGBA",
- size: 4,
- type: 'int8',
- }
- }else if(name == "NORMAL_OCT16"){
-
- jsAttribute = {
- description: "" ,
- elementSize: 4,
- elements: 2,
- name: "NORMAL_OCT16",
- size: 12,
- type: "uint8",
- }
- } */
- /* var Q = q(L.COLOR_PACKED, K.DATA_TYPE_INT8, 4)
- POSITION_CARTESIAN: q(L.POSITION_CARTESIAN, K.DATA_TYPE_FLOAT, 3),
- RGBA_PACKED: Q,
- COLOR_PACKED: Q,
- RGB_PACKED: q(L.COLOR_PACKED, K.DATA_TYPE_INT8, 3),
- NORMAL_FLOATS: q(L.NORMAL_FLOATS, K.DATA_TYPE_FLOAT, 3),
- FILLER_1B: q(L.FILLER, K.DATA_TYPE_UINT8, 1),
- INTENSITY: q(L.INTENSITY, K.DATA_TYPE_UINT16, 1),
- CLASSIFICATION: q(L.CLASSIFICATION, K.DATA_TYPE_UINT8, 1),
- NORMAL_SPHEREMAPPED: q(L.NORMAL_SPHEREMAPPED, K.DATA_TYPE_UINT8, 2),
- NORMAL_OCT16: q(L.NORMAL_OCT16, K.DATA_TYPE_UINT8, 2),
- NORMAL: q(L.NORMAL, K.DATA_TYPE_FLOAT, 3) */
-
- };
-
- function parseAttributes(cloudjs){
- let version = new Version(cloudjs.version);
- const replacements = {
- "COLOR_PACKED": "rgba",
- "RGBA": "rgba",
- "INTENSITY": "intensity",
- "CLASSIFICATION": "classification",
- "GPS_TIME": "gps-time",
- };
- const replaceOldNames = (old) => {
- if(replacements[old]){
- return replacements[old];
- }else {
- return old;
- }
- };
- const pointAttributes = [];
- if(version.upTo('1.7')){
-
- for(let attributeName of cloudjs.pointAttributes){
- const oldAttribute = PointAttribute[attributeName];
- const attribute = {
- name: oldAttribute.name,
- size: oldAttribute.byteSize,
- elements: oldAttribute.numElements,
- elementSize: oldAttribute.byteSize / oldAttribute.numElements,
- type: oldAttribute.type.name,
- description: "",
- };
- pointAttributes.push(attribute);
- }
- }else {
- pointAttributes.push(...cloudjs.pointAttributes);
- }
- {
- const attributes = new PointAttributes();
- const typeConversion = {
- int8: PointAttributeTypes.DATA_TYPE_INT8,
- int16: PointAttributeTypes.DATA_TYPE_INT16,
- int32: PointAttributeTypes.DATA_TYPE_INT32,
- int64: PointAttributeTypes.DATA_TYPE_INT64,
- uint8: PointAttributeTypes.DATA_TYPE_UINT8,
- uint16: PointAttributeTypes.DATA_TYPE_UINT16,
- uint32: PointAttributeTypes.DATA_TYPE_UINT32,
- uint64: PointAttributeTypes.DATA_TYPE_UINT64,
- double: PointAttributeTypes.DATA_TYPE_DOUBLE,
- float: PointAttributeTypes.DATA_TYPE_FLOAT,
- };
- for(let jsAttribute of pointAttributes){
-
- if(jsAttribute.name == void 0){//是来自四维看看的数据
- //attribute = transformFrom4dkk(jsAttribute)
- var attribute_ = PointAttribute[jsAttribute];
- attributes.add(attribute_);
- continue;
- }
-
- const name = replaceOldNames(jsAttribute.name);
- const type = typeConversion[jsAttribute.type];
- const numElements = jsAttribute.elements;
- const description = jsAttribute.description;
- const attribute = new PointAttribute(name, type, numElements);
- attributes.add(attribute);
- }
- {
- // check if it has normals
- let hasNormals =
- pointAttributes.find(a => a.name === "NormalX") !== undefined &&
- pointAttributes.find(a => a.name === "NormalY") !== undefined &&
- pointAttributes.find(a => a.name === "NormalZ") !== undefined;
- if(hasNormals){
- let vector = {
- name: "NORMAL",
- attributes: ["NormalX", "NormalY", "NormalZ"],
- };
- attributes.addVector(vector);
- }
- }
- return attributes;
- }
- }
- function lasLazAttributes(fMno){
- const attributes = new PointAttributes();
- attributes.add(PointAttribute.POSITION_CARTESIAN);
- attributes.add(new PointAttribute("rgba", PointAttributeTypes.DATA_TYPE_UINT8, 4));
- attributes.add(new PointAttribute("intensity", PointAttributeTypes.DATA_TYPE_UINT16, 1));
- attributes.add(new PointAttribute("classification", PointAttributeTypes.DATA_TYPE_UINT8, 1));
- attributes.add(new PointAttribute("gps-time", PointAttributeTypes.DATA_TYPE_DOUBLE, 1));
- attributes.add(new PointAttribute("number of returns", PointAttributeTypes.DATA_TYPE_UINT8, 1));
- attributes.add(new PointAttribute("return number", PointAttributeTypes.DATA_TYPE_UINT8, 1));
- attributes.add(new PointAttribute("source id", PointAttributeTypes.DATA_TYPE_UINT16, 1));
- //attributes.add(new PointAttribute("pointSourceID", PointAttributeTypes.DATA_TYPE_INT8, 4));
- return attributes;
- }
- class POCLoader {
- static load(url, timeStamp, callback){ //add timeStamp
- try {
- let pco = new PointCloudOctreeGeometry();
- pco.timeStamp = timeStamp;
-
- pco.url = url;
- let xhr = XHRFactory.createXMLHttpRequest();
- xhr.open('GET', url+'?m='+timeStamp, true);
- xhr.onreadystatechange = function () {
- if (xhr.readyState === 4 && (xhr.status === 200 || xhr.status === 0)) {
- let fMno = JSON.parse(xhr.responseText);
- let version = new Version(fMno.version);
- // assume octreeDir is absolute if it starts with http
- if (fMno.octreeDir.indexOf('http') === 0) {
- pco.octreeDir = fMno.octreeDir;
- } else {
- pco.octreeDir = url + '/../' + fMno.octreeDir;
- }
- pco.spacing = fMno.spacing;
- pco.hierarchyStepSize = fMno.hierarchyStepSize;
- pco.pointAttributes = fMno.pointAttributes;
- let min = new Vector3(fMno.boundingBox.lx, fMno.boundingBox.ly, fMno.boundingBox.lz);
- let max = new Vector3(fMno.boundingBox.ux, fMno.boundingBox.uy, fMno.boundingBox.uz);
- let boundingBox = new Box3(min, max);
- let tightBoundingBox = boundingBox.clone();
- if (fMno.tightBoundingBox) {//这个才是真实的bounding,前面那个bounding的size是个正方体,似乎取了最长边作为边长
- tightBoundingBox.min.copy(new Vector3(fMno.tightBoundingBox.lx, fMno.tightBoundingBox.ly, fMno.tightBoundingBox.lz));
- tightBoundingBox.max.copy(new Vector3(fMno.tightBoundingBox.ux, fMno.tightBoundingBox.uy, fMno.tightBoundingBox.uz));
- }
-
- let offset = min.clone(); //将成为点云的position,被我用作旋转中心(但在点云中不那么居中,navvis也是这样, 这样可能是为了让模型在这数据的bounding上)
- boundingBox.min.sub(offset); //点云的真实坐标的min都是0,0,0吗(我看案例是,因绕角落旋转,也就是原点)
-
- boundingBox.max.sub(offset);
- tightBoundingBox.min.sub(offset);
- tightBoundingBox.max.sub(offset);
- //改
- //pco.projection = fMno.projection || "+proj=somerc +lat_0=46.95240555555556 +lon_0=7.439583333333333 +k_0=1 +x_0=2600000 +y_0=1200000 +ellps=bessel +towgs84=674.374,15.056,405.346,0,0,0,0 +units=m +no_defs ",
- //"+proj=longlat +ellps=WGS84 +datum=WGS84 +no_defs" //给地图
-
- pco.boundingBox = boundingBox;
- pco.tightBoundingBox = tightBoundingBox;
- pco.boundingSphere = boundingBox.getBoundingSphere(new Sphere());
- pco.tightBoundingSphere = tightBoundingBox.getBoundingSphere(new Sphere());
- pco.offset = offset;
- if (fMno.pointAttributes === 'LAS') {
- pco.loader = new LasLazLoader(fMno.version, "las");
- pco.pointAttributes = lasLazAttributes(fMno);
- } else if (fMno.pointAttributes === 'LAZ') {
- pco.loader = new LasLazLoader(fMno.version, "laz");
- pco.pointAttributes = lasLazAttributes(fMno);
- } else {
- pco.loader = new BinaryLoader(fMno.version, boundingBox, fMno.scale);
- pco.pointAttributes = parseAttributes(fMno);
- }
- let nodes = {};
- { // load root
- let name = 'r';
- let root = new PointCloudOctreeGeometryNode(name, pco, boundingBox);
- root.level = 0;
- root.hasChildren = true;
- root.spacing = pco.spacing;
- if (version.upTo('1.5')) {
- root.numPoints = fMno.hierarchy[0][1];
- } else {
- root.numPoints = 0;
- }
- pco.root = root;
- pco.root.load();
- nodes[name] = root;
- }
- // load remaining hierarchy
- if (version.upTo('1.4')) {
- for (let i = 1; i < fMno.hierarchy.length; i++) {
- let name = fMno.hierarchy[i][0];
- let numPoints = fMno.hierarchy[i][1];
- let index = parseInt(name.charAt(name.length - 1));
- let parentName = name.substring(0, name.length - 1);
- let parentNode = nodes[parentName];
- let level = name.length - 1;
- //let boundingBox = POCLoader.createChildAABB(parentNode.boundingBox, index);
- let boundingBox = Utils.createChildAABB(parentNode.boundingBox, index);
- let node = new PointCloudOctreeGeometryNode(name, pco, boundingBox);
- node.level = level;
- node.numPoints = numPoints;
- node.spacing = pco.spacing / Math.pow(2, level);
- parentNode.addChild(node);
- nodes[name] = node;
- }
- }
- pco.nodes = nodes;
- callback(pco);
- }
- };
- xhr.send(null);
- } catch (e) {
- console.log("loading failed: '" + url + "'");
- console.log(e);
- callback();
- }
- }
- loadPointAttributes(mno){
- let fpa = mno.pointAttributes;
- let pa = new PointAttributes();
- for (let i = 0; i < fpa.length; i++) {
- let pointAttribute = PointAttribute[fpa[i]];
- pa.add(pointAttribute);
- }
- return pa;
- }
- createChildAABB(aabb, index){
- let min = aabb.min.clone();
- let max = aabb.max.clone();
- let size = new Vector3().subVectors(max, min);
- if ((index & 0b0001) > 0) {
- min.z += size.z / 2;
- } else {
- max.z -= size.z / 2;
- }
- if ((index & 0b0010) > 0) {
- min.y += size.y / 2;
- } else {
- max.y -= size.y / 2;
- }
- if ((index & 0b0100) > 0) {
- min.x += size.x / 2;
- } else {
- max.x -= size.x / 2;
- }
- return new Box3(min, max);
- }
- }
- /**
- * @author Connor Manning
- */
- class EptLoader {
- static async load(file, callback) {
- let response = await fetch(file);
- let json = await response.json();
- let url = file.substr(0, file.lastIndexOf('ept.json'));
- let geometry = new Potree.PointCloudEptGeometry(url, json);
- let root = new Potree.PointCloudEptGeometryNode(geometry);
- geometry.root = root;
- geometry.root.load();
- callback(geometry);
- }
- };
- class EptBinaryLoader {
- extension() {
- return '.bin';
- }
- workerPath() {
- return Potree.scriptPath + '/workers/EptBinaryDecoderWorker.js';
- }
- load(node) {
- if (node.loaded) return;
- let url = node.url() + this.extension();
- let xhr = XHRFactory.createXMLHttpRequest();
- xhr.open('GET', url, true);
- xhr.responseType = 'arraybuffer';
- xhr.overrideMimeType('text/plain; charset=x-user-defined');
- xhr.onreadystatechange = () => {
- if (xhr.readyState === 4) {
- if (xhr.status === 200) {
- let buffer = xhr.response;
- this.parse(node, buffer);
- } else {
- console.log('Failed ' + url + ': ' + xhr.status);
- }
- }
- };
- try {
- xhr.send(null);
- }
- catch (e) {
- console.log('Failed request: ' + e);
- }
- }
- parse(node, buffer) {
- let workerPath = this.workerPath();
- let worker = Potree.workerPool.getWorker(workerPath);
- worker.onmessage = function(e) {
- let g = new BufferGeometry();
- let numPoints = e.data.numPoints;
- let position = new Float32Array(e.data.position);
- g.setAttribute('position', new BufferAttribute(position, 3));
- let indices = new Uint8Array(e.data.indices);
- g.setAttribute('indices', new BufferAttribute(indices, 4));
- if (e.data.color) {
- let color = new Uint8Array(e.data.color);
- g.setAttribute('color', new BufferAttribute(color, 4, true));
- }
- if (e.data.intensity) {
- let intensity = new Float32Array(e.data.intensity);
- g.setAttribute('intensity',
- new BufferAttribute(intensity, 1));
- }
- if (e.data.classification) {
- let classification = new Uint8Array(e.data.classification);
- g.setAttribute('classification',
- new BufferAttribute(classification, 1));
- }
- if (e.data.returnNumber) {
- let returnNumber = new Uint8Array(e.data.returnNumber);
- g.setAttribute('return number',
- new BufferAttribute(returnNumber, 1));
- }
- if (e.data.numberOfReturns) {
- let numberOfReturns = new Uint8Array(e.data.numberOfReturns);
- g.setAttribute('number of returns',
- new BufferAttribute(numberOfReturns, 1));
- }
- if (e.data.pointSourceId) {
- let pointSourceId = new Uint16Array(e.data.pointSourceId);
- g.setAttribute('source id',
- new BufferAttribute(pointSourceId, 1));
- }
- g.attributes.indices.normalized = true;
- let tightBoundingBox = new Box3(
- new Vector3().fromArray(e.data.tightBoundingBox.min),
- new Vector3().fromArray(e.data.tightBoundingBox.max)
- );
- node.doneLoading(
- g,
- tightBoundingBox,
- numPoints,
- new Vector3(...e.data.mean));
- Potree.workerPool.returnWorker(workerPath, worker);
- };
- let toArray = (v) => [v.x, v.y, v.z];
- let message = {
- buffer: buffer,
- schema: node.ept.schema,
- scale: node.ept.eptScale,
- offset: node.ept.eptOffset,
- mins: toArray(node.key.b.min)
- };
- worker.postMessage(message, [message.buffer]);
- }
- };
- /**
- * laslaz code taken and adapted from plas.io js-laslaz
- * http://plas.io/
- * https://github.com/verma/plasio
- *
- * Thanks to Uday Verma and Howard Butler
- *
- */
- class EptLaszipLoader {
- load(node) {
- if (node.loaded) return;
- let url = node.url() + '.laz';
- let xhr = XHRFactory.createXMLHttpRequest();
- xhr.open('GET', url, true);
- xhr.responseType = 'arraybuffer';
- xhr.overrideMimeType('text/plain; charset=x-user-defined');
- xhr.onreadystatechange = () => {
- if (xhr.readyState === 4) {
- if (xhr.status === 200) {
- let buffer = xhr.response;
- this.parse(node, buffer);
- } else {
- console.log('Failed ' + url + ': ' + xhr.status);
- }
- }
- };
- xhr.send(null);
- }
- async parse(node, buffer){
- let lf = new LASFile(buffer);
- let handler = new EptLazBatcher(node);
- try{
- await lf.open();
- lf.isOpen = true;
- const header = await lf.getHeader();
- {
- let i = 0;
- let toArray = (v) => [v.x, v.y, v.z];
- let mins = toArray(node.key.b.min);
- let maxs = toArray(node.key.b.max);
- let hasMoreData = true;
- while(hasMoreData){
- const data = await lf.readData(1000000, 0, 1);
- let d = new LASDecoder(
- data.buffer,
- header.pointsFormatId,
- header.pointsStructSize,
- data.count,
- header.scale,
- header.offset,
- mins,
- maxs);
- d.extraBytes = header.extraBytes;
- d.pointsFormatId = header.pointsFormatId;
- handler.push(d);
- i += data.count;
- hasMoreData = data.hasMoreData;
- }
- header.totalRead = i;
- header.versionAsString = lf.versionAsString;
- header.isCompressed = lf.isCompressed;
- await lf.close();
- lf.isOpen = false;
- }
- }catch(err){
- console.error('Error reading LAZ:', err);
-
- if (lf.isOpen) {
- await lf.close();
- lf.isOpen = false;
- }
-
- throw err;
- }
- }
- };
- class EptLazBatcher {
- constructor(node) { this.node = node; }
- push(las) {
- let workerPath = Potree.scriptPath +
- '/workers/EptLaszipDecoderWorker.js';
- let worker = Potree.workerPool.getWorker(workerPath);
- worker.onmessage = (e) => {
- let g = new BufferGeometry();
- let numPoints = las.pointsCount;
- let positions = new Float32Array(e.data.position);
- let colors = new Uint8Array(e.data.color);
- let intensities = new Float32Array(e.data.intensity);
- let classifications = new Uint8Array(e.data.classification);
- let returnNumbers = new Uint8Array(e.data.returnNumber);
- let numberOfReturns = new Uint8Array(e.data.numberOfReturns);
- let pointSourceIDs = new Uint16Array(e.data.pointSourceID);
- let indices = new Uint8Array(e.data.indices);
- let gpsTime = new Float32Array(e.data.gpsTime);
- g.setAttribute('position',
- new BufferAttribute(positions, 3));
- g.setAttribute('rgba',
- new BufferAttribute(colors, 4, true));
- g.setAttribute('intensity',
- new BufferAttribute(intensities, 1));
- g.setAttribute('classification',
- new BufferAttribute(classifications, 1));
- g.setAttribute('return number',
- new BufferAttribute(returnNumbers, 1));
- g.setAttribute('number of returns',
- new BufferAttribute(numberOfReturns, 1));
- g.setAttribute('source id',
- new BufferAttribute(pointSourceIDs, 1));
- g.setAttribute('indices',
- new BufferAttribute(indices, 4));
- g.setAttribute('gpsTime',
- new BufferAttribute(gpsTime, 1));
- this.node.gpsTime = e.data.gpsMeta;
- g.attributes.indices.normalized = true;
- let tightBoundingBox = new Box3(
- new Vector3().fromArray(e.data.tightBoundingBox.min),
- new Vector3().fromArray(e.data.tightBoundingBox.max)
- );
- this.node.doneLoading(
- g,
- tightBoundingBox,
- numPoints,
- new Vector3(...e.data.mean));
- Potree.workerPool.returnWorker(workerPath, worker);
- };
- let message = {
- buffer: las.arrayb,
- numPoints: las.pointsCount,
- pointSize: las.pointSize,
- pointFormatID: las.pointsFormatId,
- scale: las.scale,
- offset: las.offset,
- mins: las.mins,
- maxs: las.maxs
- };
- worker.postMessage(message, [message.buffer]);
- };
- };
- class EptZstandardLoader extends EptBinaryLoader {
- extension() {
- return '.zst';
- }
- workerPath() {
- return Potree.scriptPath + '/workers/EptZstandardDecoderWorker.js';
- }
- };
- class ShapefileLoader{
- constructor(){
- this.transform = null;
- }
- async load(path){
- const matLine = new LineMaterial( {
- color: 0xff0000,
- lineWidth: 3, // in pixels
- resolution: new Vector2$1(1000, 1000),
- dashed: false
- } );
- const features = await this.loadShapefileFeatures(path);
- const node = new Object3D();
-
- for(const feature of features){
- const fnode = this.featureToSceneNode(feature, matLine);
- node.add(fnode);
- }
- let setResolution = (x, y) => {
- matLine.resolution.set(x, y);
- };
- const result = {
- features: features,
- node: node,
- setResolution: setResolution,
- };
- return result;
- }
- featureToSceneNode(feature, matLine){
- let geometry = feature.geometry;
-
- let color = new Color(1, 1, 1);
- let transform = this.transform;
- if(transform === null){
- transform = {forward: (v) => v};
- }
-
- if(feature.geometry.type === "Point"){
- let sg = new SphereGeometry(1, 18, 18);
- let sm = new MeshNormalMaterial();
- let s = new Mesh(sg, sm);
-
- let [long, lat] = geometry.coordinates;
- let pos = transform.forward([long, lat]);
-
- s.position.set(...pos, 20);
-
- s.scale.set(10, 10, 10);
-
- return s;
- }else if(geometry.type === "LineString"){
- let coordinates = [];
-
- let min = new Vector3(Infinity, Infinity, Infinity);
- for(let i = 0; i < geometry.coordinates.length; i++){
- let [long, lat] = geometry.coordinates[i];
- let pos = transform.forward([long, lat]);
-
- min.x = Math.min(min.x, pos[0]);
- min.y = Math.min(min.y, pos[1]);
- min.z = Math.min(min.z, 20);
-
- coordinates.push(...pos, 20);
- if(i > 0 && i < geometry.coordinates.length - 1){
- coordinates.push(...pos, 20);
- }
- }
-
- for(let i = 0; i < coordinates.length; i += 3){
- coordinates[i+0] -= min.x;
- coordinates[i+1] -= min.y;
- coordinates[i+2] -= min.z;
- }
-
- const lineGeometry = new LineGeometry();
- lineGeometry.setPositions( coordinates );
- const line = new Line2( lineGeometry, matLine );
- line.computeLineDistances();
- line.scale.set( 1, 1, 1 );
- line.position.copy(min);
-
- return line;
- }else if(geometry.type === "Polygon"){
- for(let pc of geometry.coordinates){
- let coordinates = [];
-
- let min = new Vector3(Infinity, Infinity, Infinity);
- for(let i = 0; i < pc.length; i++){
- let [long, lat] = pc[i];
- let pos = transform.forward([long, lat]);
-
- min.x = Math.min(min.x, pos[0]);
- min.y = Math.min(min.y, pos[1]);
- min.z = Math.min(min.z, 20);
-
- coordinates.push(...pos, 20);
- if(i > 0 && i < pc.length - 1){
- coordinates.push(...pos, 20);
- }
- }
-
- for(let i = 0; i < coordinates.length; i += 3){
- coordinates[i+0] -= min.x;
- coordinates[i+1] -= min.y;
- coordinates[i+2] -= min.z;
- }
- const lineGeometry = new LineGeometry();
- lineGeometry.setPositions( coordinates );
- const line = new Line2( lineGeometry, matLine );
- line.computeLineDistances();
- line.scale.set( 1, 1, 1 );
- line.position.copy(min);
-
- return line;
- }
- }else {
- console.log("unhandled feature: ", feature);
- }
- }
- async loadShapefileFeatures(file){
- let features = [];
- let source = await shapefile.open(file);
- while(true){
- let result = await source.read();
- if (result.done) {
- break;
- }
- if (result.value && result.value.type === 'Feature' && result.value.geometry !== undefined) {
- features.push(result.value);
- }
- }
- return features;
- }
- };
- const defaultColors = {
- "landuse": [0.5, 0.5, 0.5],
- "natural": [0.0, 1.0, 0.0],
- "places": [1.0, 0.0, 1.0],
- "points": [0.0, 1.0, 1.0],
- "roads": [1.0, 1.0, 0.0],
- "waterways": [0.0, 0.0, 1.0],
- "default": [0.9, 0.6, 0.1],
- };
- function getColor(feature){
- let color = defaultColors[feature];
- if(!color){
- color = defaultColors["default"];
- }
- return color;
- }
- class Geopackage$1{
- constructor(){
- this.path = null;
- this.node = null;
- }
- };
- class GeoPackageLoader{
- constructor(){
- }
- static async loadUrl(url, params){
- await Promise.all([
- Utils.loadScript(`${Potree.scriptPath}/lazylibs/geopackage/geopackage.js`),
- Utils.loadScript(`${Potree.scriptPath}/lazylibs/sql.js/sql-wasm.js`),
- ]);
-
- const result = await fetch(url);
- const buffer = await result.arrayBuffer();
- params = params || {};
- params.source = url;
- return GeoPackageLoader.loadBuffer(buffer, params);
- }
- static async loadBuffer(buffer, params){
- await Promise.all([
- Utils.loadScript(`${Potree.scriptPath}/lazylibs/geopackage/geopackage.js`),
- Utils.loadScript(`${Potree.scriptPath}/lazylibs/sql.js/sql-wasm.js`),
- ]);
- params = params || {};
- const resolver = async (resolve) => {
-
- let transform = params.transform;
- if(!transform){
- transform = {forward: (arg) => arg};
- }
- const wasmPath = `${Potree.scriptPath}/lazylibs/sql.js/sql-wasm.wasm`;
- const SQL = await initSqlJs({ locateFile: filename => wasmPath});
- const u8 = new Uint8Array(buffer);
- const data = await geopackage.open(u8);
- window.data = data;
- const geopackageNode = new Object3D();
- geopackageNode.name = params.source;
- geopackageNode.potree = {
- source: params.source,
- };
- const geo = new Geopackage$1();
- geo.path = params.source;
- geo.node = geopackageNode;
- const tables = data.getTables();
- for(const table of tables.features){
- const dao = data.getFeatureDao(table);
- let boundingBox = dao.getBoundingBox();
- boundingBox = boundingBox.projectBoundingBox(dao.projection, 'EPSG:4326');
- const geoJson = data.queryForGeoJSONFeaturesInTable(table, boundingBox);
- const matLine = new LineMaterial( {
- color: new Color().setRGB(...getColor(table)),
- lineWidth: 2,
- resolution: new Vector2$1(1000, 1000),
- dashed: false
- } );
- const node = new Object3D();
- node.name = table;
- geo.node.add(node);
- for(const [index, feature] of Object.entries(geoJson)){
- //const featureNode = GeoPackageLoader.featureToSceneNode(feature, matLine, transform);
- const featureNode = GeoPackageLoader.featureToSceneNode(feature, matLine, dao.projection, transform);
- node.add(featureNode);
- }
- }
- resolve(geo);
- };
- return new Promise(resolver);
- }
- static featureToSceneNode(feature, matLine, geopackageProjection, transform){
- let geometry = feature.geometry;
-
- let color = new Color(1, 1, 1);
-
- if(feature.geometry.type === "Point"){
- let sg = new SphereGeometry(1, 18, 18);
- let sm = new MeshNormalMaterial();
- let s = new Mesh(sg, sm);
-
- let [long, lat] = geometry.coordinates;
- let pos = transform.forward(geopackageProjection.forward([long, lat]));
-
- s.position.set(...pos, 20);
-
- s.scale.set(10, 10, 10);
-
- return s;
- }else if(geometry.type === "LineString"){
- let coordinates = [];
-
- let min = new Vector3(Infinity, Infinity, Infinity);
- for(let i = 0; i < geometry.coordinates.length; i++){
- let [long, lat] = geometry.coordinates[i];
- let pos = transform.forward(geopackageProjection.forward([long, lat]));
-
- min.x = Math.min(min.x, pos[0]);
- min.y = Math.min(min.y, pos[1]);
- min.z = Math.min(min.z, 20);
-
- coordinates.push(...pos, 20);
- if(i > 0 && i < geometry.coordinates.length - 1){
- coordinates.push(...pos, 20);
- }
- }
-
- for(let i = 0; i < coordinates.length; i += 3){
- coordinates[i+0] -= min.x;
- coordinates[i+1] -= min.y;
- coordinates[i+2] -= min.z;
- }
-
- const lineGeometry = new LineGeometry();
- lineGeometry.setPositions( coordinates );
- const line = new Line2( lineGeometry, matLine );
- line.computeLineDistances();
- line.scale.set( 1, 1, 1 );
- line.position.copy(min);
-
- return line;
- }else if(geometry.type === "Polygon"){
- for(let pc of geometry.coordinates){
- let coordinates = [];
-
- let min = new Vector3(Infinity, Infinity, Infinity);
- for(let i = 0; i < pc.length; i++){
- let [long, lat] = pc[i];
-
- let pos = transform.forward(geopackageProjection.forward([long, lat]));
-
- min.x = Math.min(min.x, pos[0]);
- min.y = Math.min(min.y, pos[1]);
- min.z = Math.min(min.z, 20);
-
- coordinates.push(...pos, 20);
- if(i > 0 && i < pc.length - 1){
- coordinates.push(...pos, 20);
- }
- }
-
- for(let i = 0; i < coordinates.length; i += 3){
- coordinates[i+0] -= min.x;
- coordinates[i+1] -= min.y;
- coordinates[i+2] -= min.z;
- }
- const lineGeometry = new LineGeometry();
- lineGeometry.setPositions( coordinates );
- const line = new Line2( lineGeometry, matLine );
- line.computeLineDistances();
- line.scale.set( 1, 1, 1 );
- line.position.copy(min);
-
- return line;
- }
- }else {
- console.log("unhandled feature: ", feature);
- }
- }
- };
- class ClipVolume extends Object3D{
-
- constructor(args={}){
- super();
-
- this.constructor.counter = (this.constructor.counter === undefined) ? 0 : this.constructor.counter + 1;
- this.name = "clip_volume_" + this.constructor.counter;
- let alpha = args.alpha || 0;
- let beta = args.beta || 0;
- let gamma = args.gamma || 0;
- this.rotation.x = alpha;
- this.rotation.y = beta;
- this.rotation.z = gamma;
- this.clipOffset = 0.001;
- this.clipRotOffset = 1;
-
- let boxGeometry = new BoxGeometry(1, 1, 1);
- boxGeometry.computeBoundingBox();
-
- let boxFrameGeometry = new Geometry();
- {
- // bottom
- boxFrameGeometry.vertices.push(new Vector3(-0.5, -0.5, 0.5));
- boxFrameGeometry.vertices.push(new Vector3(0.5, -0.5, 0.5));
- boxFrameGeometry.vertices.push(new Vector3(0.5, -0.5, 0.5));
- boxFrameGeometry.vertices.push(new Vector3(0.5, -0.5, -0.5));
- boxFrameGeometry.vertices.push(new Vector3(0.5, -0.5, -0.5));
- boxFrameGeometry.vertices.push(new Vector3(-0.5, -0.5, -0.5));
- boxFrameGeometry.vertices.push(new Vector3(-0.5, -0.5, -0.5));
- boxFrameGeometry.vertices.push(new Vector3(-0.5, -0.5, 0.5));
- // top
- boxFrameGeometry.vertices.push(new Vector3(-0.5, 0.5, 0.5));
- boxFrameGeometry.vertices.push(new Vector3(0.5, 0.5, 0.5));
- boxFrameGeometry.vertices.push(new Vector3(0.5, 0.5, 0.5));
- boxFrameGeometry.vertices.push(new Vector3(0.5, 0.5, -0.5));
- boxFrameGeometry.vertices.push(new Vector3(0.5, 0.5, -0.5));
- boxFrameGeometry.vertices.push(new Vector3(-0.5, 0.5, -0.5));
- boxFrameGeometry.vertices.push(new Vector3(-0.5, 0.5, -0.5));
- boxFrameGeometry.vertices.push(new Vector3(-0.5, 0.5, 0.5));
- // sides
- boxFrameGeometry.vertices.push(new Vector3(-0.5, -0.5, 0.5));
- boxFrameGeometry.vertices.push(new Vector3(-0.5, 0.5, 0.5));
- boxFrameGeometry.vertices.push(new Vector3(0.5, -0.5, 0.5));
- boxFrameGeometry.vertices.push(new Vector3(0.5, 0.5, 0.5));
- boxFrameGeometry.vertices.push(new Vector3(0.5, -0.5, -0.5));
- boxFrameGeometry.vertices.push(new Vector3(0.5, 0.5, -0.5));
- boxFrameGeometry.vertices.push(new Vector3(-0.5, -0.5, -0.5));
- boxFrameGeometry.vertices.push(new Vector3(-0.5, 0.5, -0.5));
- boxFrameGeometry.colors.push(new Vector3(1, 1, 1));
- }
- let planeFrameGeometry = new Geometry();
- {
- // middle line
- planeFrameGeometry.vertices.push(new Vector3(-0.5, -0.5, 0.0));
- planeFrameGeometry.vertices.push(new Vector3(-0.5, 0.5, 0.0));
- planeFrameGeometry.vertices.push(new Vector3(0.5, 0.5, 0.0));
- planeFrameGeometry.vertices.push(new Vector3(0.5, -0.5, 0.0));
- planeFrameGeometry.vertices.push(new Vector3(-0.5, 0.5, 0.0));
- planeFrameGeometry.vertices.push(new Vector3(0.5, 0.5, 0.0));
- planeFrameGeometry.vertices.push(new Vector3(-0.5, -0.5, 0.0));
- planeFrameGeometry.vertices.push(new Vector3(0.5, -0.5, 0.0));
- }
- this.dimension = new Vector3(1, 1, 1);
- this.material = new MeshBasicMaterial( {
- color: 0x00ff00,
- transparent: true,
- opacity: 0.3,
- depthTest: true,
- depthWrite: false} );
- this.box = new Mesh(boxGeometry, this.material);
- this.box.geometry.computeBoundingBox();
- this.boundingBox = this.box.geometry.boundingBox;
- this.add(this.box);
-
- this.frame = new LineSegments( boxFrameGeometry, new LineBasicMaterial({color: 0x000000}));
- this.add(this.frame);
- this.planeFrame = new LineSegments( planeFrameGeometry, new LineBasicMaterial({color: 0xff0000}));
- this.add(this.planeFrame);
- // set default thickness
- this.setScaleZ(0.1);
- // create local coordinate system
- let createArrow = (name, direction, color) => {
- let material = new MeshBasicMaterial({
- color: color,
- depthTest: false,
- depthWrite: false});
-
- let shaftGeometry = new Geometry();
- shaftGeometry.vertices.push(new Vector3(0, 0, 0));
- shaftGeometry.vertices.push(new Vector3(0, 1, 0));
-
- let shaftMaterial = new LineBasicMaterial({
- color: color,
- depthTest: false,
- depthWrite: false,
- transparent: true
- });
- let shaft = new Line(shaftGeometry, shaftMaterial);
- shaft.name = name + "_shaft";
-
- let headGeometry = new CylinderGeometry(0, 0.04, 0.1, 10, 1, false);
- let headMaterial = material;
- let head = new Mesh(headGeometry, headMaterial);
- head.name = name + "_head";
- head.position.y = 1;
-
- let arrow = new Object3D();
- arrow.name = name;
- arrow.add(shaft);
- arrow.add(head);
- return arrow;
- };
-
- this.arrowX = createArrow("arrow_x", new Vector3(1, 0, 0), 0xFF0000);
- this.arrowY = createArrow("arrow_y", new Vector3(0, 1, 0), 0x00FF00);
- this.arrowZ = createArrow("arrow_z", new Vector3(0, 0, 1), 0x0000FF);
-
- this.arrowX.rotation.z = -Math.PI / 2;
- this.arrowZ.rotation.x = Math.PI / 2;
- this.arrowX.visible = false;
- this.arrowY.visible = false;
- this.arrowZ.visible = false;
- this.add(this.arrowX);
- this.add(this.arrowY);
- this.add(this.arrowZ);
-
- { // event listeners
- this.addEventListener("ui_select", e => {
- this.arrowX.visible = true;
- this.arrowY.visible = true;
- this.arrowZ.visible = true;
- });
- this.addEventListener("ui_deselect", e => {
- this.arrowX.visible = false;
- this.arrowY.visible = false;
- this.arrowZ.visible = false;
- });
- this.addEventListener("select", e => {
- let scene_header = $("#" + this.name + " .scene_header");
- if(!scene_header.next().is(":visible")) {
- scene_header.click();
- }
- });
- this.addEventListener("deselect", e => {
- let scene_header = $("#" + this.name + " .scene_header");
- if(scene_header.next().is(":visible")) {
- scene_header.click();
- }
- });
- }
-
- this.update();
- };
- setClipOffset(offset) {
- this.clipOffset = offset;
- }
- setClipRotOffset(offset) {
- this.clipRotOffset = offset;
- }
- setScaleX(x) {
- this.box.scale.x = x;
- this.frame.scale.x = x;
- this.planeFrame.scale.x = x;
- }
- setScaleY(y) {
- this.box.scale.y = y;
- this.frame.scale.y = y;
- this.planeFrame.scale.y = y;
- }
- setScaleZ(z) {
- this.box.scale.z = z;
- this.frame.scale.z = z;
- this.planeFrame.scale.z = z;
- }
- offset(args) {
- let cs = args.cs || null;
- let axis = args.axis || null;
- let dir = args.dir || null;
- if(!cs || !axis || !dir) return;
- if(axis === "x") {
- if(cs === "local") {
- this.position.add(this.localX.clone().multiplyScalar(dir * this.clipOffset));
- } else if(cs === "global") {
- this.position.x = this.position.x + dir * this.clipOffset;
- }
- }else if(axis === "y") {
- if(cs === "local") {
- this.position.add(this.localY.clone().multiplyScalar(dir * this.clipOffset));
- } else if(cs === "global") {
- this.position.y = this.position.y + dir * this.clipOffset;
- }
- }else if(axis === "z") {
- if(cs === "local") {
- this.position.add(this.localZ.clone().multiplyScalar(dir * this.clipOffset));
- } else if(cs === "global") {
- this.position.z = this.position.z + dir * this.clipOffset;
- }
- }
- this.dispatchEvent({"type": "clip_volume_changed", "viewer": viewer, "volume": this});
- }
- rotate(args) {
- let cs = args.cs || null;
- let axis = args.axis || null;
- let dir = args.dir || null;
- if(!cs || !axis || !dir) return;
- if(cs === "local") {
- if(axis === "x") {
- this.rotateOnAxis(new Vector3(1, 0, 0), dir * this.clipRotOffset * Math.PI / 180);
- } else if(axis === "y") {
- this.rotateOnAxis(new Vector3(0, 1, 0), dir * this.clipRotOffset * Math.PI / 180);
- } else if(axis === "z") {
- this.rotateOnAxis(new Vector3(0, 0, 1), dir * this.clipRotOffset * Math.PI / 180);
- }
- } else if(cs === "global") {
- let rotaxis = new Vector4(1, 0, 0, 0);
- if(axis === "y") {
- rotaxis = new Vector4(0, 1, 0, 0);
- } else if(axis === "z") {
- rotaxis = new Vector4(0, 0, 1, 0);
- }
- this.updateMatrixWorld();
- let invM = newthis.matrixWorld.clone().invert();
- rotaxis = rotaxis.applyMatrix4(invM).normalize();
- rotaxis = new Vector3(rotaxis.x, rotaxis.y, rotaxis.z);
- this.rotateOnAxis(rotaxis, dir * this.clipRotOffset * Math.PI / 180);
- }
- this.updateLocalSystem();
- this.dispatchEvent({"type": "clip_volume_changed", "viewer": viewer, "volume": this});
- }
- update(){
- this.boundingBox = this.box.geometry.boundingBox;
- this.boundingSphere = this.boundingBox.getBoundingSphere(new Sphere());
-
- this.box.visible = false;
- this.updateLocalSystem();
- };
- updateLocalSystem() {
- // extract local coordinate axes
- let rotQuat = this.getWorldQuaternion();
- this.localX = new Vector3(1, 0, 0).applyQuaternion(rotQuat).normalize();
- this.localY = new Vector3(0, 1, 0).applyQuaternion(rotQuat).normalize();
- this.localZ = new Vector3(0, 0, 1).applyQuaternion(rotQuat).normalize();
- }
-
- raycast(raycaster, intersects){
-
- let is = [];
- this.box.raycast(raycaster, is);
-
- if(is.length > 0){
- let I = is[0];
- intersects.push({
- distance: I.distance,
- object: this,
- point: I.point.clone()
- });
- }
- };
- };
- class ClippingTool extends EventDispatcher{
- constructor(viewer){
- super();
- this.viewer = viewer;
- this.maxPolygonVertices = 8;
-
- this.addEventListener("start_inserting_clipping_volume", e => {
- this.viewer.dispatchEvent({
- type: "cancel_insertions"
- });
- });
- this.sceneMarker = new Scene();
- this.sceneVolume = new Scene();
- this.sceneVolume.name = "scene_clip_volume";
- this.viewer.inputHandler.registerInteractiveScene(this.sceneVolume);
- this.onRemove = e => {
- this.sceneVolume.remove(e.volume);
- };
-
- this.onAdd = e => {
- this.sceneVolume.add(e.volume);
- };
-
- this.viewer.inputHandler.addEventListener("delete", e => {
- let volumes = e.selection.filter(e => (e instanceof ClipVolume));
- volumes.forEach(e => this.viewer.scene.removeClipVolume(e));
- let polyVolumes = e.selection.filter(e => (e instanceof PolygonClipVolume));
- polyVolumes.forEach(e => this.viewer.scene.removePolygonClipVolume(e));
- });
-
-
-
-
-
-
-
- //----
-
- /* var a = new ClipVolume()
- viewer.scene.addVolume(a);
- viewer.setObjectLayers(a, 'volume' ) */
- }
- setScene(scene){
- if(this.scene === scene){
- return;
- }
-
- if(this.scene){
- this.scene.removeEventListeners("clip_volume_added", this.onAdd);
- this.scene.removeEventListeners("clip_volume_removed", this.onRemove);
- this.scene.removeEventListeners("polygon_clip_volume_added", this.onAdd);
- this.scene.removeEventListeners("polygon_clip_volume_removed", this.onRemove);
- }
-
- this.scene = scene;
-
- this.scene.addEventListener("clip_volume_added", this.onAdd);
- this.scene.addEventListener("clip_volume_removed", this.onRemove);
- this.scene.addEventListener("polygon_clip_volume_added", this.onAdd);
- this.scene.addEventListener("polygon_clip_volume_removed", this.onRemove);
- }
- startInsertion(args = {}) {
- let type = args.type || null;
- if(!type) return null;
- let domElement = this.viewer.renderer.domElement;
- let canvasSize = this.viewer.renderer.getSize(new Vector2$1());
- let svg = $(`
- <svg height="${canvasSize.height}" width="${canvasSize.width}" style="position:absolute; pointer-events: none">
- <defs>
- <marker id="diamond" markerWidth="24" markerHeight="24" refX="12" refY="12"
- markerUnits="userSpaceOnUse">
- <circle cx="12" cy="12" r="6" fill="white" stroke="black" stroke-width="3"/>
- </marker>
- </defs>
- <polyline fill="none" stroke="black"
- style="stroke:rgb(0, 0, 0);
- stroke-width:6;"
- stroke-dasharray="9, 6"
- stroke-dashoffset="2"
- />
- <polyline fill="none" stroke="black"
- style="stroke:rgb(255, 255, 255);
- stroke-width:2;"
- stroke-dasharray="5, 10"
- marker-start="url(#diamond)"
- marker-mid="url(#diamond)"
- marker-end="url(#diamond)"
- />
- </svg>`);
- $(domElement.parentElement).append(svg);
- let polyClipVol = new PolygonClipVolume(this.viewer.scene.getActiveCamera().clone());
- this.dispatchEvent({"type": "start_inserting_clipping_volume"});
- this.viewer.scene.addPolygonClipVolume(polyClipVol);
- this.sceneMarker.add(polyClipVol);
- let cancel = {
- callback: null
- };
- let insertionCallback = (e) => {
- if(e.button === MOUSE.LEFT){
-
- polyClipVol.addMarker();
- // SVC Screen Line
- svg.find("polyline").each((index, target) => {
- let newPoint = svg[0].createSVGPoint();
- newPoint.x = e.offsetX;
- newPoint.y = e.offsetY;
- let polyline = target.points.appendItem(newPoint);
- });
-
-
- if(polyClipVol.markers.length > this.maxPolygonVertices){
- cancel.callback();
- }
-
- this.viewer.inputHandler.startDragging(
- polyClipVol.markers[polyClipVol.markers.length - 1]);
- }else if(e.button === MOUSE.RIGHT){
- cancel.callback(e);
- }
- };
-
- cancel.callback = e => {
- //let first = svg.find("polyline")[0].points[0];
- //svg.find("polyline").each((index, target) => {
- // let newPoint = svg[0].createSVGPoint();
- // newPoint.x = first.x;
- // newPoint.y = first.y;
- // let polyline = target.points.appendItem(newPoint);
- //});
- svg.remove();
- if(polyClipVol.markers.length > 3) {
- polyClipVol.removeLastMarker();
- polyClipVol.initialized = true;
- } else {
- this.viewer.scene.removePolygonClipVolume(polyClipVol);
- }
- this.viewer.renderer.domElement.removeEventListener("mouseup", insertionCallback, true);
- this.viewer.removeEventListener("cancel_insertions", cancel.callback);
- this.viewer.inputHandler.enabled = true;
- };
-
- this.viewer.addEventListener("cancel_insertions", cancel.callback);
- this.viewer.renderer.domElement.addEventListener("mouseup", insertionCallback , true);
- this.viewer.inputHandler.enabled = false;
-
- polyClipVol.addMarker();
- this.viewer.inputHandler.startDragging(
- polyClipVol.markers[polyClipVol.markers.length - 1]);
- return polyClipVol;
- }
- update() {
- }
- };
- var GeoTIFF = (function (exports) {
- 'use strict';
- const Endianness = new Enum({
- LITTLE: "II",
- BIG: "MM",
- });
- const Type = new Enum({
- BYTE: {value: 1, bytes: 1},
- ASCII: {value: 2, bytes: 1},
- SHORT: {value: 3, bytes: 2},
- LONG: {value: 4, bytes: 4},
- RATIONAL: {value: 5, bytes: 8},
- SBYTE: {value: 6, bytes: 1},
- UNDEFINED: {value: 7, bytes: 1},
- SSHORT: {value: 8, bytes: 2},
- SLONG: {value: 9, bytes: 4},
- SRATIONAL: {value: 10, bytes: 8},
- FLOAT: {value: 11, bytes: 4},
- DOUBLE: {value: 12, bytes: 8},
- });
- const Tag = new Enum({
- IMAGE_WIDTH: 256,
- IMAGE_HEIGHT: 257,
- BITS_PER_SAMPLE: 258,
- COMPRESSION: 259,
- PHOTOMETRIC_INTERPRETATION: 262,
- STRIP_OFFSETS: 273,
- ORIENTATION: 274,
- SAMPLES_PER_PIXEL: 277,
- ROWS_PER_STRIP: 278,
- STRIP_BYTE_COUNTS: 279,
- X_RESOLUTION: 282,
- Y_RESOLUTION: 283,
- PLANAR_CONFIGURATION: 284,
- RESOLUTION_UNIT: 296,
- SOFTWARE: 305,
- COLOR_MAP: 320,
- SAMPLE_FORMAT: 339,
- MODEL_PIXEL_SCALE: 33550, // [GeoTIFF] TYPE: double N: 3
- MODEL_TIEPOINT: 33922, // [GeoTIFF] TYPE: double N: 6 * NUM_TIEPOINTS
- GEO_KEY_DIRECTORY: 34735, // [GeoTIFF] TYPE: short N: >= 4
- GEO_DOUBLE_PARAMS: 34736, // [GeoTIFF] TYPE: short N: variable
- GEO_ASCII_PARAMS: 34737, // [GeoTIFF] TYPE: ascii N: variable
- });
- const typeMapping = new Map([
- [Type.BYTE, Uint8Array],
- [Type.ASCII, Uint8Array],
- [Type.SHORT, Uint16Array],
- [Type.LONG, Uint32Array],
- [Type.RATIONAL, Uint32Array],
- [Type.SBYTE, Int8Array],
- [Type.UNDEFINED, Uint8Array],
- [Type.SSHORT, Int16Array],
- [Type.SLONG, Int32Array],
- [Type.SRATIONAL, Int32Array],
- [Type.FLOAT, Float32Array],
- [Type.DOUBLE, Float64Array],
- ]);
- class IFDEntry{
- constructor(tag, type, count, offset, value){
- this.tag = tag;
- this.type = type;
- this.count = count;
- this.offset = offset;
- this.value = value;
- }
- }
- class Image{
- constructor(){
- this.width = 0;
- this.height = 0;
- this.buffer = null;
- this.metadata = [];
- }
- }
- class Reader{
- constructor(){
- }
- static read(data){
- let endiannessTag = String.fromCharCode(...Array.from(data.slice(0, 2)));
- let endianness = Endianness.fromValue(endiannessTag);
- let tiffCheckTag = data.readUInt8(2);
- if(tiffCheckTag !== 42){
- throw new Error("not a valid tiff file");
- }
- let offsetToFirstIFD = data.readUInt32LE(4);
- console.log("offsetToFirstIFD", offsetToFirstIFD);
- let ifds = [];
- let IFDsRead = false;
- let currentIFDOffset = offsetToFirstIFD;
- let i = 0;
- while(IFDsRead || i < 100){
- console.log("currentIFDOffset", currentIFDOffset);
- let numEntries = data.readUInt16LE(currentIFDOffset);
- let nextIFDOffset = data.readUInt32LE(currentIFDOffset + 2 + numEntries * 12);
- console.log("next offset: ", currentIFDOffset + 2 + numEntries * 12);
- let entryBuffer = data.slice(currentIFDOffset + 2, currentIFDOffset + 2 + 12 * numEntries);
- for(let i = 0; i < numEntries; i++){
- let tag = Tag.fromValue(entryBuffer.readUInt16LE(i * 12));
- let type = Type.fromValue(entryBuffer.readUInt16LE(i * 12 + 2));
- let count = entryBuffer.readUInt32LE(i * 12 + 4);
- let offsetOrValue = entryBuffer.readUInt32LE(i * 12 + 8);
- let valueBytes = type.bytes * count;
- let value;
- if(valueBytes <= 4){
- value = offsetOrValue;
- }else {
- let valueBuffer = new Uint8Array(valueBytes);
- valueBuffer.set(data.slice(offsetOrValue, offsetOrValue + valueBytes));
-
- let ArrayType = typeMapping.get(type);
- value = new ArrayType(valueBuffer.buffer);
- if(type === Type.ASCII){
- value = String.fromCharCode(...value);
- }
- }
- let ifd = new IFDEntry(tag, type, count, offsetOrValue, value);
- ifds.push(ifd);
- }
- console.log("nextIFDOffset", nextIFDOffset);
- if(nextIFDOffset === 0){
- break;
- }
- currentIFDOffset = nextIFDOffset;
- i++;
- }
- let ifdForTag = (tag) => {
- for(let entry of ifds){
- if(entry.tag === tag){
- return entry;
- }
- }
- return null;
- };
- let width = ifdForTag(Tag.IMAGE_WIDTH, ifds).value;
- let height = ifdForTag(Tag.IMAGE_HEIGHT, ifds).value;
- let compression = ifdForTag(Tag.COMPRESSION, ifds).value;
- let rowsPerStrip = ifdForTag(Tag.ROWS_PER_STRIP, ifds).value;
- let ifdStripOffsets = ifdForTag(Tag.STRIP_OFFSETS, ifds);
- let ifdStripByteCounts = ifdForTag(Tag.STRIP_BYTE_COUNTS, ifds);
- let numStrips = Math.ceil(height / rowsPerStrip);
- let stripByteCounts = [];
- for(let i = 0; i < ifdStripByteCounts.count; i++){
- let type = ifdStripByteCounts.type;
- let offset = ifdStripByteCounts.offset + i * type.bytes;
- let value;
- if(type === Type.SHORT){
- value = data.readUInt16LE(offset);
- }else if(type === Type.LONG){
- value = data.readUInt32LE(offset);
- }
- stripByteCounts.push(value);
- }
- let stripOffsets = [];
- for(let i = 0; i < ifdStripOffsets.count; i++){
- let type = ifdStripOffsets.type;
- let offset = ifdStripOffsets.offset + i * type.bytes;
- let value;
- if(type === Type.SHORT){
- value = data.readUInt16LE(offset);
- }else if(type === Type.LONG){
- value = data.readUInt32LE(offset);
- }
- stripOffsets.push(value);
- }
- let imageBuffer = new Uint8Array(width * height * 3);
-
- let linesProcessed = 0;
- for(let i = 0; i < numStrips; i++){
- let stripOffset = stripOffsets[i];
- let stripBytes = stripByteCounts[i];
- let stripData = data.slice(stripOffset, stripOffset + stripBytes);
- let lineBytes = width * 3;
- for(let y = 0; y < rowsPerStrip; y++){
- let line = stripData.slice(y * lineBytes, y * lineBytes + lineBytes);
- imageBuffer.set(line, linesProcessed * lineBytes);
-
- if(line.length === lineBytes){
- linesProcessed++;
- }else {
- break;
- }
- }
- }
- console.log(`width: ${width}`);
- console.log(`height: ${height}`);
- console.log(`numStrips: ${numStrips}`);
- console.log("stripByteCounts", stripByteCounts.join(", "));
- console.log("stripOffsets", stripOffsets.join(", "));
- let image = new Image();
- image.width = width;
- image.height = height;
- image.buffer = imageBuffer;
- image.metadata = ifds;
- return image;
- }
- }
- class Exporter{
- constructor(){
- }
- static toTiffBuffer(image, params = {}){
- let offsetToFirstIFD = 8;
-
- let headerBuffer = new Uint8Array([0x49, 0x49, 42, 0, offsetToFirstIFD, 0, 0, 0]);
- let [width, height] = [image.width, image.height];
- let ifds = [
- new IFDEntry(Tag.IMAGE_WIDTH, Type.SHORT, 1, null, width),
- new IFDEntry(Tag.IMAGE_HEIGHT, Type.SHORT, 1, null, height),
- new IFDEntry(Tag.BITS_PER_SAMPLE, Type.SHORT, 4, null, new Uint16Array([8, 8, 8, 8])),
- new IFDEntry(Tag.COMPRESSION, Type.SHORT, 1, null, 1),
- new IFDEntry(Tag.PHOTOMETRIC_INTERPRETATION, Type.SHORT, 1, null, 2),
- new IFDEntry(Tag.ORIENTATION, Type.SHORT, 1, null, 1),
- new IFDEntry(Tag.SAMPLES_PER_PIXEL, Type.SHORT, 1, null, 4),
- new IFDEntry(Tag.ROWS_PER_STRIP, Type.LONG, 1, null, height),
- new IFDEntry(Tag.STRIP_BYTE_COUNTS, Type.LONG, 1, null, width * height * 3),
- new IFDEntry(Tag.PLANAR_CONFIGURATION, Type.SHORT, 1, null, 1),
- new IFDEntry(Tag.RESOLUTION_UNIT, Type.SHORT, 1, null, 1),
- new IFDEntry(Tag.SOFTWARE, Type.ASCII, 6, null, "......"),
- new IFDEntry(Tag.STRIP_OFFSETS, Type.LONG, 1, null, null),
- new IFDEntry(Tag.X_RESOLUTION, Type.RATIONAL, 1, null, new Uint32Array([1, 1])),
- new IFDEntry(Tag.Y_RESOLUTION, Type.RATIONAL, 1, null, new Uint32Array([1, 1])),
- ];
- if(params.ifdEntries){
- ifds.push(...params.ifdEntries);
- }
- let valueOffset = offsetToFirstIFD + 2 + ifds.length * 12 + 4;
- // create 12 byte buffer for each ifd and variable length buffers for ifd values
- let ifdEntryBuffers = new Map();
- let ifdValueBuffers = new Map();
- for(let ifd of ifds){
- let entryBuffer = new ArrayBuffer(12);
- let entryView = new DataView(entryBuffer);
- let valueBytes = ifd.type.bytes * ifd.count;
- entryView.setUint16(0, ifd.tag.value, true);
- entryView.setUint16(2, ifd.type.value, true);
- entryView.setUint32(4, ifd.count, true);
- if(ifd.count === 1 && ifd.type.bytes <= 4){
- entryView.setUint32(8, ifd.value, true);
- }else {
- entryView.setUint32(8, valueOffset, true);
- let valueBuffer = new Uint8Array(ifd.count * ifd.type.bytes);
- if(ifd.type === Type.ASCII){
- valueBuffer.set(new Uint8Array(ifd.value.split("").map(c => c.charCodeAt(0))));
- }else {
- valueBuffer.set(new Uint8Array(ifd.value.buffer));
- }
- ifdValueBuffers.set(ifd.tag, valueBuffer);
- valueOffset = valueOffset + valueBuffer.byteLength;
- }
- ifdEntryBuffers.set(ifd.tag, entryBuffer);
- }
- let imageBufferOffset = valueOffset;
- new DataView(ifdEntryBuffers.get(Tag.STRIP_OFFSETS)).setUint32(8, imageBufferOffset, true);
- let concatBuffers = (buffers) => {
- let totalLength = buffers.reduce( (sum, buffer) => (sum + buffer.byteLength), 0);
- let merged = new Uint8Array(totalLength);
- let offset = 0;
- for(let buffer of buffers){
- merged.set(new Uint8Array(buffer), offset);
- offset += buffer.byteLength;
- }
- return merged;
- };
-
- let ifdBuffer = concatBuffers([
- new Uint16Array([ifds.length]),
- ...ifdEntryBuffers.values(),
- new Uint32Array([0])]);
- let ifdValueBuffer = concatBuffers([...ifdValueBuffers.values()]);
- let tiffBuffer = concatBuffers([
- headerBuffer,
- ifdBuffer,
- ifdValueBuffer,
- image.buffer
- ]);
- return {width: width, height: height, buffer: tiffBuffer};
- }
- }
- exports.Tag = Tag;
- exports.Type = Type;
- exports.IFDEntry = IFDEntry;
- exports.Image = Image;
- exports.Reader = Reader;
- exports.Exporter = Exporter;
- return exports;
- }({}));
- function updateAzimuth(viewer, measure){
- if(!measure.showAzimuth)return
- const azimuth = measure.azimuth;
- const isOkay = measure.points.length === 2;
- azimuth.node.visible = isOkay;
- if(!azimuth.node.visible){
- return;
- }
- const camera = viewer.scene.getActiveCamera();
- const renderAreaSize = viewer.renderer.getSize(new Vector2$1());
- const width = renderAreaSize.width;
- const height = renderAreaSize.height;
-
- const [p0, p1] = measure.points;
- const r = p0.position.distanceTo(p1.position);
- const northVec = Utils.getNorthVec(p0.position, r, viewer.getProjection());
- const northPos = p0.position.clone().add(northVec);
- azimuth.center.position.copy(p0.position);
- azimuth.center.scale.set(2, 2, 2);
-
- azimuth.center.visible = false;
- // azimuth.target.visible = false;
- { // north
- azimuth.north.position.copy(northPos);
- azimuth.north.scale.set(2, 2, 2);
- let distance = azimuth.north.position.distanceTo(camera.position);
- let pr = Utils.projectedRadius(1, camera, distance, width, height);
- let scale = (5 / pr);
- azimuth.north.scale.set(scale, scale, scale);
- }
- { // target
- azimuth.target.position.copy(p1.position);
- azimuth.target.position.z = azimuth.north.position.z;
- let distance = azimuth.target.position.distanceTo(camera.position);
- let pr = Utils.projectedRadius(1, camera, distance, width, height);
- let scale = (5 / pr);
- azimuth.target.scale.set(scale, scale, scale);
- }
-
- azimuth.circle.position.copy(p0.position);
- azimuth.circle.scale.set(r, r, r);
- azimuth.circle.material.resolution.set(width, height);
- // to target
- azimuth.centerToTarget.geometry.setPositions([
- 0, 0, 0,
- ...p1.position.clone().sub(p0.position).toArray(),
- ]);
- azimuth.centerToTarget.position.copy(p0.position);
- azimuth.centerToTarget.geometry.verticesNeedUpdate = true;
- azimuth.centerToTarget.geometry.computeBoundingSphere();
- azimuth.centerToTarget.computeLineDistances();
- azimuth.centerToTarget.material.resolution.set(width, height);
- // to target ground
- azimuth.centerToTargetground.geometry.setPositions([
- 0, 0, 0,
- p1.position.x - p0.position.x,
- p1.position.y - p0.position.y,
- 0,
- ]);
- azimuth.centerToTargetground.position.copy(p0.position);
- azimuth.centerToTargetground.geometry.verticesNeedUpdate = true;
- azimuth.centerToTargetground.geometry.computeBoundingSphere();
- azimuth.centerToTargetground.computeLineDistances();
- azimuth.centerToTargetground.material.resolution.set(width, height);
- // to north
- azimuth.centerToNorth.geometry.setPositions([
- 0, 0, 0,
- northPos.x - p0.position.x,
- northPos.y - p0.position.y,
- 0,
- ]);
- azimuth.centerToNorth.position.copy(p0.position);
- azimuth.centerToNorth.geometry.verticesNeedUpdate = true;
- azimuth.centerToNorth.geometry.computeBoundingSphere();
- azimuth.centerToNorth.computeLineDistances();
- azimuth.centerToNorth.material.resolution.set(width, height);
- // label
- const radians = Utils.computeAzimuth(p0.position, p1.position, viewer.getProjection());
- let degrees = MathUtils.radToDeg(radians);
- if(degrees < 0){
- degrees = 360 + degrees;
- }
- const txtDegrees = `${degrees.toFixed(2)}°`;
- const labelDir = northPos.clone().add(p1.position).multiplyScalar(0.5).sub(p0.position);
- if(labelDir.length() > 0){
- labelDir.z = 0;
- labelDir.normalize();
- const labelVec = labelDir.clone().multiplyScalar(r);
- const labelPos = p0.position.clone().add(labelVec);
- azimuth.label.position.copy(labelPos);
- }
- azimuth.label.setText(txtDegrees);
- let distance = azimuth.label.position.distanceTo(camera.position);
- let pr = Utils.projectedRadius(1, camera, distance, width, height);
- let scale = (70 / pr);
- azimuth.label.scale.set(scale, scale, scale);
- }
- class MeasuringTool extends EventDispatcher{
- constructor (viewer) {
- super();
- this.viewer = viewer;
- this.renderer = viewer.renderer;
- this.viewer.addEventListener('start_inserting_measurement', e => {
- this.viewer.dispatchEvent({
- type: 'cancel_insertions'
- });
- });
- this.showLabels = true;
- this.scene = new Scene();
- this.scene.name = 'scene_measurement';
- //this.light = new THREE.PointLight(0xffffff, 1.0);
- //this.scene.add(this.light);
- this.viewer.inputHandler.registerInteractiveScene(this.scene);
-
-
- //this.scene = viewer.overlay//
-
-
- this.onRemove = (e) => { e.measurement.dispose();/* this.scene.remove(e.measurement); */};
- this.onAdd = e => {this.scene.add(e.measurement);};
- for(let measurement of viewer.scene.measurements){
- this.onAdd({measurement: measurement});
- }
-
- viewer.addEventListener('camera_changed',(e)=>{
- if(e.viewport == viewer.mainViewport ) this.update();
- });
-
-
- //viewer.addEventListener("update", this.update.bind(this));
- viewer.addEventListener("render.pass.perspective_overlay", this.render.bind(this));
- viewer.addEventListener("scene_changed", this.onSceneChange.bind(this));
- viewer.scene.addEventListener('measurement_added', this.onAdd);
- viewer.scene.addEventListener('measurement_removed', this.onRemove);
-
- viewer.addEventListener('resize',this.setSize.bind(this));
-
- }
- onSceneChange(e){
- if(e.oldScene){
- e.oldScene.removeEventListener('measurement_added', this.onAdd);
- e.oldScene.removeEventListener('measurement_removed', this.onRemove);
- }
- e.scene.addEventListener('measurement_added', this.onAdd);
- e.scene.addEventListener('measurement_removed', this.onRemove);
- }
-
-
- createMeasureFromData(data){//add
- const measure = new Measure(data);
-
- viewer.scene.addMeasurement(measure);
-
- if(measure.guideLine)measure.guideLine.visible = false;
- return measure
- }
-
-
- update(){
- return;
-
-
-
-
- let camera = this.viewer.scene.getActiveCamera();
- let domElement = this.renderer.domElement;
- let measurements = this.viewer.scene.measurements;
-
- // make size independant of distance
- let mainLabels = [], subLabels = [];
-
-
-
- for (let measure of measurements) {
- measure.lengthUnit = this.viewer.lengthUnit;
- measure.lengthUnitDisplay = this.viewer.lengthUnitDisplay;
- //measure.update();
- updateAzimuth(this.viewer, measure);
-
- /* [...measure.markers, ...measure.edgeLabels, measure.areaLabel].forEach(e=>{
- e && e.update()
- }); */
-
- // labels
- /* let labels = measure.edgeLabels.concat(measure.angleLabels);
- for(let label of labels){
- label.update()
- if(label.elem.hasClass('sub')){
- subLabels.push(label)
- }else{
- mainLabels.push(label)
- }
- }
- // coordinate labels
- for (let j = 0; j < measure.coordinateLabels.length; j++) {
- let label = measure.coordinateLabels[j];
- label.update()
- mainLabels.push(label)
- }
-
- if(measure.showArea){ // area label
- let label = measure.areaLabel;
- label.update()
- mainLabels.push(label)
- } */
-
-
- /* if(measure.showCircle){ // radius label
- let label = measure.circleRadiusLabel;
- let distance = label.position.distanceTo(camera.position);
- let pr = Utils.projectedRadius(1, camera, distance, clientWidth, clientHeight);
- let scale = (70 / pr);
- label.scale.set(scale, scale, scale);
- } */
- if(!this.showLabels){
- const labels = [
- ...measure.sphereLabels,
- ...measure.angleLabels,
- measure.circleRadiusLabel,
- ];
- for(const label of labels){
- label.visible = false;
- }
- }
- }
- //this.updateLabelZIndex([{labels:subLabels},{labels:mainLabels}])
-
- }
- setSize(e){ //e.resolution
- /* if(Measure.lineMats){
- for(var m in Measure.lineMats){
- Measure.lineMats[m].resolution.set(e.canvasWidth, e.canvasHeight);
- }
- }
- if(Measure.sphereMats){
- for(var s in Measure.sphereMats){
- Measure.sphereMats[s].uniforms.resolution.value.set(e.canvasWidth, e.canvasHeight);
- }
- }
- for (let measure of this.viewer.scene.measurements) {
- measure.edgeLabels.concat(measure.areaLabel).forEach(label=>{
- label.sprite.material.uniforms.resolution.value.set(e.canvasWidth, e.canvasHeight);
- })
- } */
- }
-
- updateLabelZIndex(group){//[{labels:[]},{}] 顺序按照z-index低到高
-
- group.forEach((e,i)=>{
- e.base = group[i-1] ? group[i-1].base + group[i-1].labels.length : 0;
-
- var labels = e.labels.sort((a,b)=>{
- return b.pos2d.z - a.pos2d.z
- });
- labels.forEach((label,index)=>{
- $(label.elem).css('z-index', e.base+index);
- });
- });
-
- }
-
-
-
-
- editStateChange(e){
- //console.log("editStateChange" , e.state)
- let state = e.state;
- if(!state){
- state = viewer.scene.measurements.some(e=>e.isEditing);
- }
-
- if(state){
- viewer.dispatchEvent({type:"measureMovePoint"});
- }else {
- viewer.dispatchEvent({type:"endMeasureMove"});
- }
-
-
- //this.editing =
- }
-
-
-
- startInsertion (args = {}, callback, cancelFun) {
-
-
- let domElement = this.viewer.renderer.domElement;
-
- const pick = (defaul, alternative) => {
- if(defaul != null){
- return defaul;
- }else {
- return alternative;
- }
- };
- args.showDistances = (args.showDistances === null) ? true : args.showDistances;
- args.showArea = pick(args.showArea, false);
- args.showAngles = pick(args.showAngles, false);
- args.showCoordinates = pick(args.showCoordinates, false);
- args.showHeight = pick(args.showHeight, false);
- args.showCircle = pick(args.showCircle, false);
- args.showAzimuth = pick(args.showAzimuth, false);
- args.showEdges = pick(args.showEdges, true);
- args.closed = pick(args.closed, false);
- args.maxMarkers = pick(args.maxMarkers, Infinity);
- args.direction = args.direction;//add
- args.type = args.type; /* || 'Measurement'; */
- args.showGuideLine = pick(args.showGuideLine, false);
- args.isRect = pick(args.isRect, false);
-
-
- let measure = new Measure(args);
- this.scene.add(measure);
- measure.isNew = true;
-
-
- this.viewer.dispatchEvent({
- type: 'start_inserting_measurement',
- measure: measure
- });
-
- measure.addEventListener('editStateChange', this.editStateChange.bind(this));
- measure.editStateChange(true);
-
- let timer;
-
- let endDragFun = (e) => {
- let length = measure.points.length;
- if (e.button == MOUSE.LEFT || e.isTouch) {
- if (length >= measure.maxMarkers) {
- end({finish:true});
- }else {
- var marker = measure.addMarker({point:measure.points[length - 1].clone()});
-
- if(args.isRect && measure.markers.length == 3){//marker全可见
- measure.addMarker({point:measure.points[0].clone()});
-
- }else {
- measure.markers[length].visible = false;
- measure.edges[length].visible = false;
- }
- measure.edges[length-1].visible = true;
-
- measure.markers[length-1].visible = true;
-
- measure.editStateChange(true); //重新激活reticule状态
-
- marker.isDragging = true;
- measure.continueDrag(marker, e);
- }
-
- } else if (e.button === MOUSE.RIGHT ) { //触屏怎么取消?
- if(e.pressDistance < Potree.config.clickMaxDragDis )end(e);//非拖拽的话
- else measure.continueDrag(null, e);
-
- }
- };
- let end = (e={}) => {//确定、结束
- if(!measure.isNew)return
- if(args.minMarkers != void 0){
- if(!e.finish && measure.markers.length<=args.minMarkers){//右键 当个数不够时取消
- //this.viewer.scene.removeMeasurement(measure)
- //cancelFun && cancelFun()
- //重新开始画
- measure.markers[0].removeEventListener('mousedown',end);
- measure.reDraw();
-
-
- this.viewer.addEventListener('global_click', click, 10);
-
- measure.editStateChange(true);
- return
-
- /* if(!Potree.settings.isOfficial) this.viewer.scene.removeMeasurement(measure)
- else if(e.drag){ //正式版本不允许右键退出, 继续
- continueDrag(e.drag.object)
- measure.editStateChange(true)
- return
- } */
- }
- }
- if (!e.finish && measure.markers.length > 3) {
- measure.removeMarker(measure.points.length - 1);
- measure.markers[0].removeEventListener('mouseover', mouseover);
- measure.markers[0].removeEventListener('mouseleave', mouseleave);
- measure.markers[0].removeEventListener('click'/* 'mousedown' */,Exit);
- }
- measure.isNew = false;
- let length = measure.points.length;
- if(length){
- measure.markers[length-1].visible = true;
- measure.edges[length-1].visible = true;
-
- measure.markers.forEach(marker=>{marker.dispatchEvent('addHoverEvent'); });
- measure.edges.forEach(edge=>{edge.dispatchEvent('addHoverEvent'); });
-
- }
- clearTimeout(timer);
- this.viewer.removeEventListener('cancel_insertions', Exit);
- //pressExit && this.viewer.inputHandler.removeEventListener('keydown', pressExit);
- this.viewer.removeEventListener('global_click', click);
- this.viewer.removeEventListener('global_mousemove', ifAtWrongPlace);
- viewer.dispatchEvent({
- type : "CursorChange", action : "remove", name:"polygon_AtWrongPlace"
- });
-
- viewer.inputHandler.dispatchEvent({type:'isMeasuring', v:false, cause:'stopInsertion'} );
-
- e.remove || callback && callback();
- /* this.viewer.dispatchEvent({
- type: 'finish_inserting_measurement',
- measure: measure
- }); */
- };
-
- let Exit = (e)=>{//强制退出
-
- if(e.measure && e.measure != measure){
- return;//若指定了退出的measure但和该measure不一致,就返回
- }
- console.log('Exit: ' + measure.id);
- if(e.remove){
- viewer.scene.removeMeasurement(measure);
- }
-
- measure.editStateChange(false);
- if(this.viewer.inputHandler.drag && !e.remove){//还未触发drop的话
- this.viewer.inputHandler.drag.object.dispatchEvent({
- type: 'drop',
- drag: this.viewer.inputHandler.drag,
- viewer: this.viewer,
- pressDistance:0,
- button : MOUSE.RIGHT
- });
-
- }else {
- end({finish:true, remove:e.remove}); //未结束时添加新的measure时会触发
- }
- this.viewer.inputHandler.drag = null;
-
- };
- this.viewer.addEventListener('cancel_insertions', Exit);
-
- /*let pressExit
- if(!Potree.settings.isOfficial){
- pressExit = (e)=>{
- if(e.keyCode == 27){//Esc
- //Exit()
- //怎么模拟右键???//现由前端发出
- }
- }
- this.viewer.inputHandler.addEventListener('keydown', pressExit)
- } */
- let mouseover = (e) => {
- measure.setMarkerSelected(e.object, 'hover', 'single');
-
- };
- let mouseleave = (e) => {
- measure.setMarkerSelected(e.object, 'unhover', 'single');
- };
-
-
- let click = (e)=>{//一旦点击就立刻增加两marker
-
- if(ifAtWrongPlace(e))return
-
-
-
- if(e.button === MOUSE.RIGHT)return
-
- //console.log('measure clicked33', !!e.intersectPoint)
-
- //var I = e.intersectPoint && (e.intersectPoint.orthoIntersect || e.intersectPoint.location)
- var I = e.intersect && (e.intersect.orthoIntersect || e.intersect.location);
- if(!I){
- return measure.dispatchEvent('intersectNoPointcloud')
- }
- var atMap = e.drag.dragViewport.name == 'mapViewport';
- //在地图上测量的首个点按楼层高度(暂时先只按mainViewport相机高度吧,但navvis是按楼层,画在楼层的地面上,可能因为平面图显示的是楼层近地面),
-
- if(atMap){
- I = I.clone().setZ(viewer.mainViewport.camera.position.z );
- }
-
- var marker = measure.addMarker({point:I});
- marker.isDragging = true;
- this.viewer.inputHandler.startDragging(marker , {endDragFun, notPressMouse:true} ); //notPressMouse代表不是通过按下鼠标来拖拽
- e.drag = this.viewer.inputHandler.drag;
- e.drag.endDragFun = endDragFun;
- e.drag.notPressMouse = true;
-
- //if(!measure.dragMarker(e) || !measure.dropMarker(e))return
-
- measure.dragMarker(e);
- measure.dropMarker(e);
-
- if(measure.maxMarkers > 1 ){
- measure.markers[1].visible = false;
- measure.edges[1].visible = false;
- }
- if(measure.maxMarkers>2 && !measure.isRect){
- measure.markers[0].addEventListener('mouseover', mouseover);
- measure.markers[0].addEventListener('mouseleave', mouseleave);
- measure.markers[0].addEventListener('click'/* 'mousedown' */,Exit); //点击到第一个marker就结束
- }
-
-
- this.viewer.removeEventListener('global_click', click);///* global_drop */
-
-
-
- //console.log('measure clicked')
-
-
- return {stopContinue:true}//防止继续执行别的侦听,如flytopano
- };
-
- //点击第n下拥有n+1个marker, n>0
-
- viewer.inputHandler.dispatchEvent({type: 'isMeasuring', v: true, cause:'startInsertion'});
-
- this.viewer.addEventListener('global_click', click, 10);//add importance:10
-
- let ifAtWrongPlace = (e)=>{
- if(measure.unableDragAtMap && e.hoverViewport.name == 'mapViewport' ){
- if(e.isTouch){
- viewer.dispatchEvent({type:'reticule_forbit', v:true});
- }else {
- viewer.dispatchEvent({
- type : "CursorChange", action : "add", name:"polygon_AtWrongPlace"
- });
- }
- return true
- }else {
- if(e.isTouch){
- viewer.dispatchEvent({type:'reticule_forbit',v:false});
- }else {
- viewer.dispatchEvent({
- type : "CursorChange", action : "remove", name:"polygon_AtWrongPlace"
- });
- }
- }
- };
-
-
-
- if(measure.unableDragAtMap){
- this.viewer.addEventListener('global_mousemove', ifAtWrongPlace);
- }
-
-
- this.viewer.scene.addMeasurement(measure);
-
- return measure;
- }
-
-
- render(o={}){
- viewer.setCameraLayers(o.camera, ['measure']);
-
- if(o.screenshot){ //抗锯齿
- this.viewer.ssaaRenderPass.sampleLevel = 4;
- this.viewer.composer.render(this.scene, o.camera );
- /* viewer.scene.measurements.forEach(e=>{ //隐藏除了label以外的
- e.children.forEach((c)=>{
- if(!(c instanceof TextSprite)){
- c.visible = false
- }
- })
- }) */
- }else {
- this.viewer.renderer.render(this.scene, o.camera );
- }
- }
- };
- class Message{
- constructor(content){
- this.content = content;
- let closeIcon = `${exports.resourcePath}/icons/close.svg`;
- this.element = $(`
- <div class="potree_message">
- <span name="content_container" style="flex-grow: 1; padding: 5px"></span>
- <img name="close" src="${closeIcon}" class="button-icon" style="width: 16px; height: 16px;">
- </div>`);
- this.elClose = this.element.find("img[name=close]");
- this.elContainer = this.element.find("span[name=content_container]");
- if(typeof content === "string"){
- this.elContainer.append($(`<span>${content}</span>`));
- }else {
- this.elContainer.append(content);
- }
- }
- setMessage(content){
- this.elContainer.empty();
- if(typeof content === "string"){
- this.elContainer.append($(`<span>${content}</span>`));
- }else {
- this.elContainer.append(content);
- }
- }
- }
- class PointCloudSM{//shadow material ?
- constructor(potreeRenderer){
- this.potreeRenderer = potreeRenderer;
- this.threeRenderer = this.potreeRenderer.threeRenderer;
- this.target = new WebGLRenderTarget(2 * 1024, 2 * 1024, {
- minFilter: LinearFilter,
- magFilter: LinearFilter,
- format: RGBAFormat,
- type: FloatType,
- });
- this.target.depthTexture = new DepthTexture();
- this.target.depthTexture.type = UnsignedIntType;
- //this.threeRenderer.setClearColor(0x000000, 1);
- this.threeRenderer.setClearColor(0xff0000, 1);
- //HACK? removed while moving to three.js 109
- //this.threeRenderer.clearTarget(this.target, true, true, true);
- {
- const oldTarget = this.threeRenderer.getRenderTarget();
- this.threeRenderer.setRenderTarget(this.target);
- //this.threeRenderer.clear(true, true, true); //救命,为什么iphoneX ios13.6执行这一句clear会崩溃?即使把target的大小改为1*1.
- this.threeRenderer.setRenderTarget(oldTarget);
- }
- }
- setLight(light){
- this.light = light;
- let fov = (180 * light.angle) / Math.PI;
- let aspect = light.shadow.mapSize.width / light.shadow.mapSize.height;
- let near = 0.1;
- let far = light.distance === 0 ? 10000 : light.distance;
- this.camera = new PerspectiveCamera(fov, aspect, near, far);
- this.camera.up.set(0, 0, 1);
- this.camera.position.copy(light.position);
- let target = new Vector3().subVectors(light.position, light.getWorldDirection(new Vector3()));
- this.camera.lookAt(target);
- this.camera.updateProjectionMatrix();
- this.camera.updateMatrix();
- this.camera.updateMatrixWorld();
- this.camera.matrixWorldInverse.copy(this.camera.matrixWorld).invert();
- }
- setSize(width, height){
- if(this.target.width !== width || this.target.height !== height){
- this.target.dispose();
- }
- this.target.setSize(width, height);
- }
- render(scene, camera){
- this.threeRenderer.setClearColor(0x000000, 1);
-
- const oldTarget = this.threeRenderer.getRenderTarget();
- this.threeRenderer.setRenderTarget(this.target);
- this.threeRenderer.clear(true, true, true);
- this.potreeRenderer.render(scene, this.camera, this.target, {});
- this.threeRenderer.setRenderTarget(oldTarget);
- }
- }
- class ProfileTool extends EventDispatcher {
- constructor (viewer) {
- super();
- this.viewer = viewer;
- this.renderer = viewer.renderer;
- this.addEventListener('start_inserting_profile', e => {
- this.viewer.dispatchEvent({
- type: 'cancel_insertions'
- });
- });
- this.scene = new Scene();
- this.scene.name = 'scene_profile';
- this.light = new PointLight(0xffffff, 1.0);
- this.scene.add(this.light);
- this.viewer.inputHandler.registerInteractiveScene(this.scene);
- this.onRemove = e => this.scene.remove(e.profile);
- this.onAdd = e => this.scene.add(e.profile);
- for(let profile of viewer.scene.profiles){
- this.onAdd({profile: profile});
- }
- viewer.addEventListener("update", this.update.bind(this));
- viewer.addEventListener("render.pass.perspective_overlay", this.render.bind(this));
- viewer.addEventListener("scene_changed", this.onSceneChange.bind(this));
- viewer.scene.addEventListener('profile_added', this.onAdd);
- viewer.scene.addEventListener('profile_removed', this.onRemove);
- }
- onSceneChange(e){
- if(e.oldScene){
- e.oldScene.removeEventListeners('profile_added', this.onAdd);
- e.oldScene.removeEventListeners('profile_removed', this.onRemove);
- }
- e.scene.addEventListener('profile_added', this.onAdd);
- e.scene.addEventListener('profile_removed', this.onRemove);
- }
- startInsertion (args = {}) {
- let domElement = this.viewer.renderer.domElement;
- let profile = new Profile();
- profile.name = args.name || 'Profile';
- this.dispatchEvent({
- type: 'start_inserting_profile',
- profile: profile
- });
- this.scene.add(profile);
- let cancel = {
- callback: null
- };
- let insertionCallback = (e) => {
- if(e.button === MOUSE.LEFT){
- if(profile.points.length <= 1){
- let camera = this.viewer.scene.getActiveCamera();
- let distance = camera.position.distanceTo(profile.points[0]);
- let clientSize = this.viewer.renderer.getSize(new Vector2$1());
- let pr = Utils.projectedRadius(1, camera, distance, clientSize.width, clientSize.height);
- let width = (10 / pr);
- profile.setWidth(width);
- }
- profile.addMarker(profile.points[profile.points.length - 1].clone());
- this.viewer.inputHandler.startDragging(
- profile.spheres[profile.spheres.length - 1]);
- } else if (e.button === MOUSE.RIGHT) {
- cancel.callback();
- }
- };
- cancel.callback = e => {
- profile.removeMarker(profile.points.length - 1);
- domElement.removeEventListener('mouseup', insertionCallback, false);
- this.viewer.removeEventListener('cancel_insertions', cancel.callback);
- };
- this.viewer.addEventListener('cancel_insertions', cancel.callback);
- domElement.addEventListener('mouseup', insertionCallback, false);
- profile.addMarker(new Vector3(0, 0, 0));
- this.viewer.inputHandler.startDragging(
- profile.spheres[profile.spheres.length - 1]);
- this.viewer.scene.addProfile(profile);
- return profile;
- }
-
- update(){
- let camera = this.viewer.scene.getActiveCamera();
- let profiles = this.viewer.scene.profiles;
- let renderAreaSize = this.viewer.renderer.getSize(new Vector2$1());
- let clientWidth = renderAreaSize.width;
- let clientHeight = renderAreaSize.height;
- this.light.position.copy(camera.position);
- // make size independant of distance
- for(let profile of profiles){
- for(let sphere of profile.spheres){
- let distance = camera.position.distanceTo(sphere.getWorldPosition(new Vector3()));
- let pr = Utils.projectedRadius(1, camera, distance, clientWidth, clientHeight);
- let scale = (15 / pr);
- sphere.scale.set(scale, scale, scale);
- }
- }
- }
- render(){
- this.viewer.renderer.render(this.scene, this.viewer.scene.getActiveCamera());
- }
- }
- class ScreenBoxSelectTool extends EventDispatcher{
- constructor(viewer){
- super();
- this.viewer = viewer;
- this.scene = new Scene();
- viewer.addEventListener("update", this.update.bind(this));
- viewer.addEventListener("render.pass.perspective_overlay", this.render.bind(this));
- viewer.addEventListener("scene_changed", this.onSceneChange.bind(this));
- }
- onSceneChange(scene){
- console.log("scene changed");
- }
- startInsertion(){
- let domElement = this.viewer.renderer.domElement;
- let volume = new BoxVolume();
- volume.position.set(12345, 12345, 12345);
- volume.showVolumeLabel = false;
- volume.visible = false;
- volume.update();
- this.viewer.scene.addVolume(volume);
- this.importance = 10;
- let selectionBox = $(`<div style="position: absolute; border: 2px solid white; pointer-events: none; border-style:dashed"></div>`);
- $(domElement.parentElement).append(selectionBox);
- selectionBox.css("right", "10px");
- selectionBox.css("bottom", "10px");
- let drag = e =>{
- volume.visible = true;
- let mStart = e.drag.start;
- let mEnd = e.drag.end;
- let box2D = new Box2();
- box2D.expandByPoint(mStart);
- box2D.expandByPoint(mEnd);
- selectionBox.css("left", `${box2D.min.x}px`);
- selectionBox.css("top", `${box2D.min.y}px`);
- selectionBox.css("width", `${box2D.max.x - box2D.min.x}px`);
- selectionBox.css("height", `${box2D.max.y - box2D.min.y}px`);
- let camera = e.viewer.scene.getActiveCamera();
- let size = e.viewer.renderer.getSize(new Vector2$1());
- let frustumSize = new Vector2$1(
- camera.right - camera.left,
- camera.top - camera.bottom);
- let screenCentroid = new Vector2$1().addVectors(e.drag.end, e.drag.start).multiplyScalar(0.5);
- let pointer = new Vector3(0, 0, 0.5);
- Utils.convertScreenPositionToNDC(pointer, screenCentroid, size.width, size.height);
-
- pointer.unproject(camera);
-
-
- //let ray = Utils.mouseToRay( pointer, camera, size.width, size.height);
- let diff = new Vector2$1().subVectors(e.drag.end, e.drag.start);
- diff.divide(size).multiply(frustumSize);
-
-
- volume.position.copy(pointer);
- //volume.position.copy(ray.origin); //orthographicCamera不能用这种方式 而且也该是用end
- volume.up.copy(camera.up);
- volume.rotation.copy(camera.rotation);
- volume.scale.set(diff.x, diff.y, 1000 * 100);
- e.consume();
- };
- let drop = e => {
- this.importance = 0;
- $(selectionBox).remove();
- this.viewer.inputHandler.deselectAll();
- this.viewer.inputHandler.toggleSelection(volume);
- let camera = e.viewer.scene.getActiveCamera();
- let size = e.viewer.renderer.getSize(new Vector2$1());
- let screenCentroid = new Vector2$1().addVectors(e.drag.end, e.drag.start).multiplyScalar(0.5);
-
-
-
- let pointer = new Vector3(0, 0, 0.5);
- Utils.convertScreenPositionToNDC(pointer, screenCentroid, size.width, size.height);
-
-
-
- let ray = Utils.mouseToRay(pointer/* screenCentroid */, camera, size.width, size.height);
- //let line = new THREE.Line3(ray.origin, new THREE.Vector3().addVectors(ray.origin, ray.direction));
- this.removeEventListener("drag", drag);
- this.removeEventListener("drop", drop);
- let allPointsNear = [];
- let allPointsFar = [];
- // TODO support more than one point cloud
- for(let pointcloud of this.viewer.scene.pointclouds){
- if(!pointcloud.visible){
- continue;
- }
- let volCam = camera.clone();
- volCam.left = -volume.scale.x / 2;
- volCam.right = +volume.scale.x / 2;
- volCam.top = +volume.scale.y / 2;
- volCam.bottom = -volume.scale.y / 2;
- volCam.near = -volume.scale.z / 2;
- volCam.far = +volume.scale.z / 2;
- volCam.rotation.copy(volume.rotation);
- volCam.position.copy(volume.position);
- volCam.updateMatrix();
- volCam.updateMatrixWorld();
- volCam.updateProjectionMatrix();
- volCam.matrixWorldInverse.copy(volCam.matrixWorld).invert();
- let ray = new Ray(volCam.getWorldPosition(new Vector3()), volCam.getWorldDirection(new Vector3()));
- let rayInverse = new Ray(
- ray.origin.clone().add(ray.direction.clone().multiplyScalar(volume.scale.z)),
- ray.direction.clone().multiplyScalar(-1));
-
-
-
- let diff = new Vector2$1().subVectors(e.drag.end, e.drag.start);
- let pickerSettings = {
- width: 16, //renderTarget大小,数值越大识别精度越高 原本的8太低了
- height: 16,
- pickWindowSize: 16, //??????
-
- all: true,
- pickClipped: true,
- pointSizeType: PointSizeType.FIXED,
- pointSize: 1
- };
-
-
- let pointsNear = pointcloud.pick(viewer, null, volCam, ray, pickerSettings);
- volCam.rotateX(Math.PI);
- volCam.updateMatrix();
- volCam.updateMatrixWorld();
- volCam.updateProjectionMatrix();
- volCam.matrixWorldInverse.copy(volCam.matrixWorld).invert();
- let pointsFar = pointcloud.pick(viewer,null, volCam, rayInverse, pickerSettings);
- pointsNear && allPointsNear.push(...pointsNear);
- pointsFar && allPointsFar.push(...pointsFar);
- }
- /* if(allPointsNear.length > 0 && allPointsFar.length > 0){
- let viewLine = new THREE.Line3(ray.origin, new THREE.Vector3().addVectors(ray.origin, ray.direction));
- let closestOnLine = allPointsNear.map(p => viewLine.closestPointToPoint(p.position, false, new THREE.Vector3()));
- let closest = closestOnLine.sort( (a, b) => ray.origin.distanceTo(a) - ray.origin.distanceTo(b))[0];
- let farthestOnLine = allPointsFar.map(p => viewLine.closestPointToPoint(p.position, false, new THREE.Vector3()));
- let farthest = farthestOnLine.sort( (a, b) => ray.origin.distanceTo(b) - ray.origin.distanceTo(a))[0];
- let distance = closest.distanceTo(farthest);
- let centroid = new THREE.Vector3().addVectors(closest, farthest).multiplyScalar(0.5);
- volume.scale.z = distance * 1.1;
- volume.position.copy(centroid);
- } */
- if(allPointsNear.length > 0 || allPointsFar.length > 0){
- let viewLine = new Line3(ray.origin, new Vector3().addVectors(ray.origin, ray.direction));
- let closestOnLine = allPointsNear.map(p => viewLine.closestPointToPoint(p.position, false, new Vector3()));
- let closests = closestOnLine.sort( (a, b) => ray.origin.distanceTo(a) - ray.origin.distanceTo(b));//[0];
- let farthestOnLine = allPointsFar.map(p => viewLine.closestPointToPoint(p.position, false, new Vector3()));
- let farthests = farthestOnLine.sort( (a, b) => ray.origin.distanceTo(b) - ray.origin.distanceTo(a));//[0];
- let closest, farthest;
- if(closests.length == 0){
- closest = farthests[farthests.length-1];
- farthest = farthests[0];
- }else if(farthests.length == 0){
- closest = closests[closests.length-1];
- farthest = closests[0];
- }else {
- closest = closests[0];
- farthest = farthests[0];
- }
- let distance = closest.distanceTo(farthest);
- let centroid = new Vector3().addVectors(closest, farthest).multiplyScalar(0.5);
- volume.scale.z = distance * 1.1;
- //volume.position.copy(centroid);
-
- var pos2d = centroid.project(camera); //改变深度
- pointer.setZ(pos2d.z);
- pointer.unproject(camera);
- volume.position.copy(pointer);
-
-
- }
- volume.clip = true;
- };
- this.addEventListener("drag", drag);
- this.addEventListener("drop", drop);
- viewer.inputHandler.addInputListener(this);
- return volume;
- }
- update(e){
- //console.log(e.delta)
- }
- render(){
- this.viewer.renderer.render(this.scene, this.viewer.scene.getActiveCamera());
- }
- }
- class SpotLightHelper$1 extends Object3D{
- constructor(light, color){
- super();
- this.light = light;
- this.color = color;
- //this.up.set(0, 0, 1);
- this.updateMatrix();
- this.updateMatrixWorld();
- { // SPHERE
- let sg = new SphereGeometry(1, 32, 32);
- let sm = new MeshNormalMaterial();
- this.sphere = new Mesh(sg, sm);
- this.sphere.scale.set(0.5, 0.5, 0.5);
- this.add(this.sphere);
- }
- { // LINES
-
- let positions = new Float32Array([
- +0, +0, +0, +0, +0, -1,
- +0, +0, +0, -1, -1, -1,
- +0, +0, +0, +1, -1, -1,
- +0, +0, +0, +1, +1, -1,
- +0, +0, +0, -1, +1, -1,
- -1, -1, -1, +1, -1, -1,
- +1, -1, -1, +1, +1, -1,
- +1, +1, -1, -1, +1, -1,
- -1, +1, -1, -1, -1, -1,
- ]);
- let geometry = new BufferGeometry();
- geometry.setAttribute("position", new BufferAttribute(positions, 3));
- let material = new LineBasicMaterial();
- this.frustum = new LineSegments(geometry, material);
- this.add(this.frustum);
- }
- this.update();
- }
- update(){
- this.light.updateMatrix();
- this.light.updateMatrixWorld();
- let position = this.light.position;
- let target = new Vector3().addVectors(
- this.light.position, this.light.getWorldDirection(new Vector3()).multiplyScalar(-1));
-
- let quat = new Quaternion().setFromRotationMatrix(
- new Matrix4().lookAt( position, target, new Vector3( 0, 0, 1 ) )
- );
- this.setRotationFromQuaternion(quat);
- this.position.copy(position);
- let coneLength = (this.light.distance > 0) ? this.light.distance : 1000;
- let coneWidth = coneLength * Math.tan( this.light.angle * 0.5 );
- this.frustum.scale.set(coneWidth, coneWidth, coneLength);
- }
- }
- //add-------------------------------------
- const OpaWhenNotSelect = 0.75;
- const ScaleRatio = 4;
- const OutlineColor = 0x666666;
- //----------------------------------------
- const hideFocusHandles = true;//add
- class TransformationTool {
- constructor(viewer) {
- this.viewer = viewer;
- this.scene = new Scene();
- this.selection = [];
- this.pivot = new Vector3();
- this.dragging = false;
- this.showPickVolumes = false;
- this.viewer.inputHandler.registerInteractiveScene(this.scene);
- this.viewer.inputHandler.addEventListener('selection_changed', (e) => {
- for(let selected of this.selection){
- this.viewer.inputHandler.blacklist.delete(selected);
- }
- this.selection = e.selection;
- for(let selected of this.selection){
- this.viewer.inputHandler.blacklist.add(selected);
- }
- });
- this.viewer.addEventListener('global_touchstart',(e)=>{ //add
- this.update();
- });
- let red = Potree.config.axis.x.color;
- let green = Potree.config.axis.y.color;
- let blue = Potree.config.axis.z.color;
-
- this.activeHandle = null;
- this.scaleHandles = {
- "scale.x+": {name: "scale.x+", node: new Object3D(), color: red, alignment: [+1, +0, +0]},
- "scale.x-": {name: "scale.x-", node: new Object3D(), color: red, alignment: [-1, +0, +0]},
- "scale.y+": {name: "scale.y+", node: new Object3D(), color: green, alignment: [+0, +1, +0]},
- "scale.y-": {name: "scale.y-", node: new Object3D(), color: green, alignment: [+0, -1, +0]},
- "scale.z+": {name: "scale.z+", node: new Object3D(), color: blue, alignment: [+0, +0, +1]},
- "scale.z-": {name: "scale.z-", node: new Object3D(), color: blue, alignment: [+0, +0, -1]},
- };
- this.focusHandles = {
- "focus.x+": {name: "focus.x+", node: new Object3D(), color: red, alignment: [+1, +0, +0]},
- "focus.x-": {name: "focus.x-", node: new Object3D(), color: red, alignment: [-1, +0, +0]},
- "focus.y+": {name: "focus.y+", node: new Object3D(), color: green, alignment: [+0, +1, +0]},
- "focus.y-": {name: "focus.y-", node: new Object3D(), color: green, alignment: [+0, -1, +0]},
- "focus.z+": {name: "focus.z+", node: new Object3D(), color: blue, alignment: [+0, +0, +1]},
- "focus.z-": {name: "focus.z-", node: new Object3D(), color: blue, alignment: [+0, +0, -1]},
- };
- this.translationHandles = {
- "translation.x": {name: "translation.x", node: new Object3D(), color: red, alignment: [1, 0, 0]},
- "translation.y": {name: "translation.y", node: new Object3D(), color: green, alignment: [0, 1, 0]},
- "translation.z": {name: "translation.z", node: new Object3D(), color: blue, alignment: [0, 0, 1]},
- };
- this.rotationHandles = {
- "rotation.x": {name: "rotation.x", node: new Object3D(), color: red, alignment: [1, 0, 0]},
- "rotation.y": {name: "rotation.y", node: new Object3D(), color: green, alignment: [0, 1, 0]},
- "rotation.z": {name: "rotation.z", node: new Object3D(), color: blue, alignment: [0, 0, 1]},
- };
- this.handles = Object.assign({}, this.scaleHandles, hideFocusHandles?{}:this.focusHandles, this.translationHandles, this.rotationHandles);
- this.pickVolumes = [];
- this.initializeScaleHandles();
- this.initializeFocusHandles();
- this.initializeTranslationHandles();
- this.initializeRotationHandles();
- let boxFrameGeometry = new Geometry();
- {
- // bottom
- boxFrameGeometry.vertices.push(new Vector3(-0.5, -0.5, 0.5));
- boxFrameGeometry.vertices.push(new Vector3(0.5, -0.5, 0.5));
- boxFrameGeometry.vertices.push(new Vector3(0.5, -0.5, 0.5));
- boxFrameGeometry.vertices.push(new Vector3(0.5, -0.5, -0.5));
- boxFrameGeometry.vertices.push(new Vector3(0.5, -0.5, -0.5));
- boxFrameGeometry.vertices.push(new Vector3(-0.5, -0.5, -0.5));
- boxFrameGeometry.vertices.push(new Vector3(-0.5, -0.5, -0.5));
- boxFrameGeometry.vertices.push(new Vector3(-0.5, -0.5, 0.5));
- // top
- boxFrameGeometry.vertices.push(new Vector3(-0.5, 0.5, 0.5));
- boxFrameGeometry.vertices.push(new Vector3(0.5, 0.5, 0.5));
- boxFrameGeometry.vertices.push(new Vector3(0.5, 0.5, 0.5));
- boxFrameGeometry.vertices.push(new Vector3(0.5, 0.5, -0.5));
- boxFrameGeometry.vertices.push(new Vector3(0.5, 0.5, -0.5));
- boxFrameGeometry.vertices.push(new Vector3(-0.5, 0.5, -0.5));
- boxFrameGeometry.vertices.push(new Vector3(-0.5, 0.5, -0.5));
- boxFrameGeometry.vertices.push(new Vector3(-0.5, 0.5, 0.5));
- // sides
- boxFrameGeometry.vertices.push(new Vector3(-0.5, -0.5, 0.5));
- boxFrameGeometry.vertices.push(new Vector3(-0.5, 0.5, 0.5));
- boxFrameGeometry.vertices.push(new Vector3(0.5, -0.5, 0.5));
- boxFrameGeometry.vertices.push(new Vector3(0.5, 0.5, 0.5));
- boxFrameGeometry.vertices.push(new Vector3(0.5, -0.5, -0.5));
- boxFrameGeometry.vertices.push(new Vector3(0.5, 0.5, -0.5));
- boxFrameGeometry.vertices.push(new Vector3(-0.5, -0.5, -0.5));
- boxFrameGeometry.vertices.push(new Vector3(-0.5, 0.5, -0.5));
- }
- this.frame = new LineSegments(boxFrameGeometry, new LineBasicMaterial({color: 0xffff00}));
- this.scene.add(this.frame);
- viewer.setObjectLayers(this.scene, 'transformationTool' );
-
- }
-
-
- setModeEnable(mode,enable){//xzw add
- let handels = this[mode + 'Handles'];
- if(!handels)return
- for(let o in handels){
- handels[o].node.visible = !!enable;
- }
- }
-
-
- initializeScaleHandles(){
- let sgSphere = new SphereGeometry(1, 32, 32);
- let sgLowPolySphere = new SphereGeometry(1, 16, 16);
- for(let handleName of Object.keys(this.scaleHandles)){
- let handle = this.scaleHandles[handleName];
- let node = handle.node;
- this.scene.add(node);
- node.position.set(...handle.alignment).multiplyScalar(0.5);
- let material = new MeshBasicMaterial({
- color: handle.color,
- opacity: OpaWhenNotSelect,
- transparent: true
- });
- let outlineMaterial = new MeshBasicMaterial({
- color: OutlineColor,
- side: BackSide,
- opacity: OpaWhenNotSelect,
- transparent: true});
- let pickMaterial = new MeshNormalMaterial({
- opacity: 0.2,
- transparent: true,
- visible: this.showPickVolumes});
- let sphere = new Mesh(sgSphere, material);
- sphere.scale.set(2, 2, 2 );
- sphere.name = `${handleName}.handle`;
- node.add(sphere);
-
- let outline = new Mesh(sgSphere, outlineMaterial);
- outline.scale.set(1.1, 1.1, 1.1);
- outline.name = `${handleName}.outline`;
- sphere.add(outline);
- let pickSphere = new Mesh(sgLowPolySphere, pickMaterial);
- pickSphere.name = `${handleName}.pick_volume`;
- pickSphere.scale.set(2, 2, 2);
- sphere.add(pickSphere);
- pickSphere.handle = handleName;
- this.pickVolumes.push(pickSphere);
- node.setOpacity = (target) => {
- let opacity = {x: material.opacity};
- let t = new TWEEN.Tween(opacity).to({x: target}, 100);
- t.onUpdate(() => {
- sphere.visible = opacity.x > 0;
- pickSphere.visible = opacity.x > 0;
- material.opacity = opacity.x;
- outlineMaterial.opacity = opacity.x;
- pickSphere.material.opacity = opacity.x * 0.5;
- });
- t.start();
- };
- pickSphere.addEventListener("drag", (e) => this.dragScaleHandle(e));
- pickSphere.addEventListener("drop", (e) => this.dropScaleHandle(e));
- pickSphere.addEventListener("mouseover", e => {
- //node.setOpacity(1);
- });
- pickSphere.addEventListener("click", e => {
- e.consume();
- });
- pickSphere.addEventListener("mouseleave", e => {
- //node.setOpacity(OpaWhenNotSelect);
- });
- }
- }
- initializeFocusHandles(){
- if(hideFocusHandles)return//add
- //let sgBox = new THREE.BoxGeometry(1, 1, 1);
- let sgPlane = new PlaneGeometry(4, 4, 1, 1);
- let sgLowPolySphere = new SphereGeometry(1, 16, 16);
- let texture = new TextureLoader().load(`${exports.resourcePath}/icons/eye_2.png`);
- for(let handleName of Object.keys(this.focusHandles)){
- let handle = this.focusHandles[handleName];
- let node = handle.node;
- this.scene.add(node);
- let align = handle.alignment;
- //node.lookAt(new THREE.Vector3().addVectors(node.position, new THREE.Vector3(...align)));
- node.lookAt(new Vector3(...align));
- let off = 0.8;
- if(align[0] === 1){
- node.position.set(1, off, -off).multiplyScalar(0.5);
- node.rotation.z = Math.PI / 2;
- }else if(align[0] === -1){
- node.position.set(-1, -off, -off).multiplyScalar(0.5);
- node.rotation.z = Math.PI / 2;
- }else if(align[1] === 1){
- node.position.set(-off, 1, -off).multiplyScalar(0.5);
- node.rotation.set(Math.PI / 2, Math.PI, 0.0);
- }else if(align[1] === -1){
- node.position.set(off, -1, -off).multiplyScalar(0.5);
- node.rotation.set(Math.PI / 2, 0.0, 0.0);
- }else if(align[2] === 1){
- node.position.set(off, off, 1).multiplyScalar(0.5);
- }else if(align[2] === -1){
- node.position.set(-off, off, -1).multiplyScalar(0.5);
- }
- let material = new MeshBasicMaterial({
- color: handle.color,
- opacity: 0,
- transparent: true,
- map: texture
- });
- //let outlineMaterial = new THREE.MeshBasicMaterial({
- // color: 0x000000,
- // side: THREE.BackSide,
- // opacity: 0,
- // transparent: true});
- let pickMaterial = new MeshNormalMaterial({
- //opacity: 0,
- transparent: true,
- visible: this.showPickVolumes});
- let box = new Mesh(sgPlane, material);
- box.name = `${handleName}.handle`;
- box.scale.set(1.5, 1.5, 1.5);
- box.position.set(0, 0, 0);
- box.visible = false;
- node.add(box);
- //handle.focusNode = box;
-
- //let outline = new THREE.Mesh(sgPlane, outlineMaterial);
- //outline.scale.set(1.4, 1.4, 1.4);
- //outline.name = `${handleName}.outline`;
- //box.add(outline);
- let pickSphere = new Mesh(sgLowPolySphere, pickMaterial);
- pickSphere.name = `${handleName}.pick_volume`;
- pickSphere.scale.set(2, 2, 2);
- box.add(pickSphere);
- pickSphere.handle = handleName;
- this.pickVolumes.push(pickSphere);
- node.setOpacity = (target) => {
- let opacity = {x: material.opacity};
- let t = new TWEEN.Tween(opacity).to({x: target}, 100);
- t.onUpdate(() => {
- pickSphere.visible = opacity.x > 0;
- box.visible = opacity.x > 0;
- material.opacity = opacity.x;
- //outlineMaterial.opacity = opacity.x;
- pickSphere.material.opacity = opacity.x * 0.5;
- });
- t.start();
- };
- //pickSphere.addEventListener("drag", e => {});
- pickSphere.addEventListener("mouseup", e => {
- e.consume();
- });
- pickSphere.addEventListener("mousedown", e => {
- e.consume();
- });
- pickSphere.addEventListener("click", e => {
- e.consume();
- let selected = this.selection[0];
- let maxScale = Math.max(...selected.scale.toArray());
- let minScale = Math.min(...selected.scale.toArray());
- let handleLength = Math.abs(selected.scale.dot(new Vector3(...handle.alignment)));
- let alignment = new Vector3(...handle.alignment).multiplyScalar(2 * maxScale / handleLength);
- alignment.applyMatrix4(selected.matrixWorld);
- let newCamPos = alignment;
- let newCamTarget = selected.getWorldPosition(new Vector3());
- Utils.moveTo(this.viewer.scene, newCamPos, newCamTarget);
- });
- pickSphere.addEventListener("mouseover", e => {
- //box.setOpacity(1);
- });
- pickSphere.addEventListener("mouseleave", e => {
- //box.setOpacity(OpaWhenNotSelect);
- });
- }
- }
- initializeTranslationHandles(){
- let boxGeometry = new BoxGeometry(1, 1, 1);
- for(let handleName of Object.keys(this.translationHandles)){
- let handle = this.handles[handleName];
- let node = handle.node;
- this.scene.add(node);
- let material = new MeshBasicMaterial({
- color: handle.color,
- opacity: OpaWhenNotSelect,
- transparent: true});
- let outlineMaterial = new MeshBasicMaterial({
- color: OutlineColor,
- side: BackSide,
- opacity: OpaWhenNotSelect,
- transparent: true});
- let pickMaterial = new MeshNormalMaterial({
- opacity: 0.2,
- transparent: true,
- visible: this.showPickVolumes
- });
- let box = new Mesh(boxGeometry, material);
- box.name = `${handleName}.handle`;
- box.scale.set(1, 1, 36);
- box.lookAt(new Vector3(...handle.alignment));
- box.renderOrder = 10;
- node.add(box);
- handle.translateNode = box;
- let outline = new Mesh(boxGeometry, outlineMaterial);
- outline.name = `${handleName}.outline`;
- outline.scale.set(1.3, 1.3, 1.01);
- outline.renderOrder = 0;
- box.add(outline);
- let pickVolume = new Mesh(boxGeometry, pickMaterial);
- pickVolume.name = `${handleName}.pick_volume`;
- pickVolume.scale.set(4, 4, 1.1);
- pickVolume.handle = handleName;
- box.add(pickVolume);
- this.pickVolumes.push(pickVolume);
- node.setOpacity = (target) => {
- let opacity = {x: material.opacity};
- let t = new TWEEN.Tween(opacity).to({x: target}, 100);
- t.onUpdate(() => {
- box.visible = opacity.x > 0;
- pickVolume.visible = opacity.x > 0;
- material.opacity = opacity.x;
- outlineMaterial.opacity = opacity.x;
- pickMaterial.opacity = opacity.x * 0.5;
- });
- t.start();
- };
- pickVolume.addEventListener("drag", (e) => {this.dragTranslationHandle(e);});
- pickVolume.addEventListener("drop", (e) => {this.dropTranslationHandle(e);});
- }
- }
- initializeRotationHandles(){
- let adjust = 1.5;
- let torusGeometry = new TorusGeometry(1, adjust * 0.015, 8, 64, Math.PI / 2);
- let outlineGeometry = new TorusGeometry(1, adjust * 0.018, 8, 64, Math.PI / 2);
- let pickGeometry = new TorusGeometry(1, adjust * 0.04, 6, 4, Math.PI / 2);
- for(let handleName of Object.keys(this.rotationHandles)){
- let handle = this.handles[handleName];
- let node = handle.node;
- this.scene.add(node);
- let material = new MeshBasicMaterial({
- color: handle.color,
- opacity: OpaWhenNotSelect,
- transparent: true
- });
- let outlineMaterial = new MeshBasicMaterial({
- color: OutlineColor,
- side: BackSide,
- opacity: OpaWhenNotSelect,
- transparent: true
- });
- let pickMaterial = new MeshNormalMaterial({
- opacity: 0.2,
- transparent: true,
- visible: this.showPickVolumes
- });
- let box = new Mesh(torusGeometry, material);
- box.name = `${handleName}.handle`;
- box.scale.set(30, 30, 30);
- box.lookAt(new Vector3(...handle.alignment));
- node.add(box);
- handle.translateNode = box;
- let outline = new Mesh(outlineGeometry, outlineMaterial);
- outline.name = `${handleName}.outline`;
- outline.scale.set(1, 1, 1);
- outline.renderOrder = 0;
- box.add(outline);
- let pickVolume = new Mesh(pickGeometry, pickMaterial);
- pickVolume.name = `${handleName}.pick_volume`;
- pickVolume.scale.set(1, 1, 1);
- pickVolume.handle = handleName;
- box.add(pickVolume);
- this.pickVolumes.push(pickVolume);
- node.setOpacity = (target) => {
- let opacity = {x: material.opacity};
- let t = new TWEEN.Tween(opacity).to({x: target}, 100);
- t.onUpdate(() => {
- box.visible = opacity.x > 0;
- pickVolume.visible = opacity.x > 0;
- material.opacity = opacity.x;
- outlineMaterial.opacity = opacity.x;
- pickMaterial.opacity = opacity.x * 0.5;
- });
- t.start();
- };
- //pickVolume.addEventListener("mouseover", (e) => {
- // //let a = this.viewer.scene.getActiveCamera().getWorldDirection(new THREE.Vector3()).dot(pickVolume.getWorldDirection(new THREE.Vector3()));
- // console.log(pickVolume.getWorldDirection(new THREE.Vector3()));
- //});
-
- pickVolume.addEventListener("drag", (e) => {this.dragRotationHandle(e);});
- pickVolume.addEventListener("drop", (e) => {this.dropRotationHandle(e);});
- }
- }
- dragRotationHandle(e){
- let drag = e.drag;
- let handle = this.activeHandle;
- let camera = this.viewer.scene.getActiveCamera();
- if(!handle){
- return
- };
- let localNormal = new Vector3(...handle.alignment);
- let n = new Vector3();
- n.copy(new Vector4(...localNormal.toArray(), 0).applyMatrix4(handle.node.matrixWorld));
- n.normalize();
- if (!drag.intersectionStart){
- //this.viewer.scene.scene.remove(this.debug);
- //this.debug = new THREE.Object3D();
- //this.viewer.scene.scene.add(this.debug);
- //Utils.debugSphere(this.debug, drag.location, 3, 0xaaaaaa);
- //let debugEnd = drag.location.clone().add(n.clone().multiplyScalar(20));
- //Utils.debugLine(this.debug, drag.location, debugEnd, 0xff0000);
- drag.intersectionStart = drag.location;
- drag.objectStart = drag.object.getWorldPosition(new Vector3());
- drag.handle = handle;
- let plane = new Plane().setFromNormalAndCoplanarPoint(n, drag.intersectionStart);
- drag.dragPlane = plane;
- drag.pivot = drag.intersectionStart;
- }else {
- handle = drag.handle;
- }
- this.dragging = true;
- let pointer = this.viewer.inputHandler.pointer;
- let domElement = this.viewer.renderer.domElement;
- let ray = Utils.mouseToRay(pointer, camera, domElement.clientWidth, domElement.clientHeight);
-
- let I = ray.intersectPlane(drag.dragPlane, new Vector3());
- if (I) {
- let center = this.scene.getWorldPosition(new Vector3());
- let from = drag.pivot;
- let to = I;
- let v1 = from.clone().sub(center).normalize();
- let v2 = to.clone().sub(center).normalize();
- let angle = Math.acos(v1.dot(v2));
- let sign = Math.sign(v1.cross(v2).dot(n));
- angle = angle * sign;
- if (Number.isNaN(angle)) {
- return;
- }
- let normal = new Vector3(...handle.alignment);
- for (let selection of this.selection) {
- selection.rotateOnAxis(normal, angle);
- selection.dispatchEvent({
- type: "orientation_changed",
- object: selection
- });
- }
- drag.pivot = I;
- }
- }
- dropRotationHandle(e){
- this.dragging = false;
- this.setActiveHandle(null);
- }
- dragTranslationHandle(e){
- let drag = e.drag;
- let handle = this.activeHandle;
- let camera = this.viewer.scene.getActiveCamera();
-
- if(!drag.intersectionStart && handle){
- drag.intersectionStart = drag.location;
- drag.objectStart = drag.object.getWorldPosition(new Vector3());
- let start = drag.intersectionStart;
- let dir = new Vector4(...handle.alignment, 0).applyMatrix4(this.scene.matrixWorld);
- let end = new Vector3().addVectors(start, dir);
- let line = new Line3(start.clone(), end.clone());
- drag.line = line;
- let camOnLine = line.closestPointToPoint(camera.position, false, new Vector3());
- let normal = new Vector3().subVectors(camera.position, camOnLine);
- let plane = new Plane().setFromNormalAndCoplanarPoint(normal, drag.intersectionStart);
- drag.dragPlane = plane;
- drag.pivot = drag.intersectionStart;
- }else {
- handle = drag.handle;
- }
- this.dragging = true;
- {
- let pointer = this.viewer.inputHandler.pointer;
- let domElement = this.viewer.renderer.domElement;
- let ray = Utils.mouseToRay(pointer, camera, domElement.clientWidth, domElement.clientHeight);
- let I = ray.intersectPlane(drag.dragPlane, new Vector3());
- if (I) {
- let iOnLine = drag.line.closestPointToPoint(I, false, new Vector3());
- let diff = new Vector3().subVectors(iOnLine, drag.pivot);
- for (let selection of this.selection) {
- selection.position.add(diff);
- selection.dispatchEvent({
- type: "position_changed",
- object: selection
- });
- }
- drag.pivot = drag.pivot.add(diff);
- }
- }
- }
- dropTranslationHandle(e){
- this.dragging = false;
- this.setActiveHandle(null);
- }
- dropScaleHandle(e){
- this.dragging = false;
- this.setActiveHandle(null);
- }
- dragScaleHandle(e){
- let drag = e.drag;
- let handle = this.activeHandle;
- let camera = this.viewer.scene.getActiveCamera();
- if(!drag.intersectionStart){
- drag.intersectionStart = drag.location;
- drag.objectStart = drag.object.getWorldPosition(new Vector3());
- drag.handle = handle;
- let start = drag.intersectionStart;
- let dir = new Vector4(...handle.alignment, 0).applyMatrix4(this.scene.matrixWorld);
- let end = new Vector3().addVectors(start, dir);
- let line = new Line3(start.clone(), end.clone());
- drag.line = line;
- let camOnLine = line.closestPointToPoint(camera.position, false, new Vector3());
- let normal = new Vector3().subVectors(camera.position, camOnLine);
- let plane = new Plane().setFromNormalAndCoplanarPoint(normal, drag.intersectionStart);
- drag.dragPlane = plane;
- drag.pivot = drag.intersectionStart;
- //Utils.debugSphere(viewer.scene.scene, drag.pivot, 0.05);
- }else {
- handle = drag.handle;
- }
- this.dragging = true;
- {
- let pointer = this.viewer.inputHandler.pointer;
- let domElement = this.viewer.renderer.domElement;
- let ray = Utils.mouseToRay(pointer, camera, domElement.clientWidth, domElement.clientHeight);
- let I = ray.intersectPlane(drag.dragPlane, new Vector3());
- if (I) {
- let iOnLine = drag.line.closestPointToPoint(I, false, new Vector3());
- let direction = handle.alignment.reduce( (a, v) => a + v, 0);
- let toObjectSpace = this.selection[0].matrixWorld.clone().invert();
- let iOnLineOS = iOnLine.clone().applyMatrix4(toObjectSpace);
- let pivotOS = drag.pivot.clone().applyMatrix4(toObjectSpace);
- let diffOS = new Vector3().subVectors(iOnLineOS, pivotOS);
- let dragDirectionOS = diffOS.clone().normalize();
- if(iOnLine.distanceTo(drag.pivot) === 0){
- dragDirectionOS.set(0, 0, 0);
- }
- let dragDirection = dragDirectionOS.dot(new Vector3(...handle.alignment));
- let diff = new Vector3().subVectors(iOnLine, drag.pivot);
- let diffScale = new Vector3(...handle.alignment).multiplyScalar(diff.length() * direction * dragDirection);
- let diffPosition = diff.clone().multiplyScalar(0.5);
-
-
-
- for (let selection of this.selection) {
- //xzw 改:否则不跟手
- let diffScale_ = diffScale.clone().divide(selection.boundingBox.getSize(new Vector3));
- selection.scale.add(diffScale_);
- //selection.scale.add(diffScale);
- selection.scale.x = Math.max(0.1, selection.scale.x);
- selection.scale.y = Math.max(0.1, selection.scale.y);
- selection.scale.z = Math.max(0.1, selection.scale.z);
- selection.position.add(diffPosition);
-
-
- selection.dispatchEvent({
- type: "position_changed",
- object: selection
- });
- selection.dispatchEvent({
- type: "scale_changed",
- object: selection
- });
- }
- drag.pivot.copy(iOnLine);
- //Utils.debugSphere(viewer.scene.scene, drag.pivot, 0.05);
- }
- }
- }
- setActiveHandle(handle){
- if(this.dragging){
- return;
- }
- if(this.activeHandle === handle){
- return;
- }
- this.activeHandle = handle;
- if(handle === null){
- for(let handleName of Object.keys(this.handles)){
- let handle = this.handles[handleName];
- handle.node.setOpacity(0);
- }
- }
- if(!hideFocusHandles){
- for(let handleName of Object.keys(this.focusHandles)){
- let handle = this.focusHandles[handleName];
- if(this.activeHandle === handle){
- handle.node.setOpacity(1.0);
- }else {
- handle.node.setOpacity(OpaWhenNotSelect);
- }
- }
- }
- for(let handleName of Object.keys(this.translationHandles)){
- let handle = this.translationHandles[handleName];
- if(this.activeHandle === handle){
- handle.node.setOpacity(1.0);
- }else {
- handle.node.setOpacity(OpaWhenNotSelect);
- }
- }
- for(let handleName of Object.keys(this.rotationHandles)){
- let handle = this.rotationHandles[handleName];
- //if(this.activeHandle === handle){
- // handle.node.setOpacity(1.0);
- //}else{
- // handle.node.setOpacity(OpaWhenNotSelect)
- //}
- handle.node.setOpacity(OpaWhenNotSelect);
- }
- for(let handleName of Object.keys(this.scaleHandles)){
- let handle = this.scaleHandles[handleName];
- if(this.activeHandle === handle){
- handle.node.setOpacity(1.0);
- if(!hideFocusHandles){
- let relatedFocusHandle = this.focusHandles[handle.name.replace("scale", "focus")];
- let relatedFocusNode = relatedFocusHandle.node;
- relatedFocusNode.setOpacity(OpaWhenNotSelect);
- }
- for(let translationHandleName of Object.keys(this.translationHandles)){
- let translationHandle = this.translationHandles[translationHandleName];
- translationHandle.node.setOpacity(OpaWhenNotSelect);
- }
- //let relatedTranslationHandle = this.translationHandles[
- // handle.name.replace("scale", "translation").replace(/[+-]/g, "")];
- //let relatedTranslationNode = relatedTranslationHandle.node;
- //relatedTranslationNode.setOpacity(OpaWhenNotSelect);
- }else {
- handle.node.setOpacity(OpaWhenNotSelect);
- }
- }
-
- if(handle){
- handle.node.setOpacity(1.0);
- }
-
- }
- update () {
- if(this.selection.length === 1){
- this.scene.visible = true;
- this.scene.updateMatrix();
- this.scene.updateMatrixWorld();
- let selected = this.selection[0];
- let world = selected.matrixWorld;
- let camera = this.viewer.scene.getActiveCamera();
- let domElement = this.viewer.renderer.domElement;
- let pointer = this.viewer.inputHandler.pointer;
- let center = selected.boundingBox.getCenter(new Vector3()).clone().applyMatrix4(selected.matrixWorld);
- this.scene.scale.copy(selected.boundingBox.getSize(new Vector3()).multiply(selected.scale));
- this.scene.position.copy(center);
- this.scene.rotation.copy(selected.rotation);
- this.scene.updateMatrixWorld();
- {
- // adjust rotation handles
- if(!this.dragging){
- let tWorld = this.scene.matrixWorld;
- let tObject = tWorld.clone().invert();
- let camObjectPos = camera.getWorldPosition(new Vector3()).applyMatrix4(tObject);
- let x = this.rotationHandles["rotation.x"].node.rotation;
- let y = this.rotationHandles["rotation.y"].node.rotation;
- let z = this.rotationHandles["rotation.z"].node.rotation;
- x.order = "ZYX";
- y.order = "ZYX";
- let above = camObjectPos.z > 0;
- let below = !above;
- let PI_HALF = Math.PI / 2;
- if(above){
- if(camObjectPos.x > 0 && camObjectPos.y > 0){
- x.x = 1 * PI_HALF;
- y.y = 3 * PI_HALF;
- z.z = 0 * PI_HALF;
- }else if(camObjectPos.x < 0 && camObjectPos.y > 0){
- x.x = 1 * PI_HALF;
- y.y = 2 * PI_HALF;
- z.z = 1 * PI_HALF;
- }else if(camObjectPos.x < 0 && camObjectPos.y < 0){
- x.x = 2 * PI_HALF;
- y.y = 2 * PI_HALF;
- z.z = 2 * PI_HALF;
- }else if(camObjectPos.x > 0 && camObjectPos.y < 0){
- x.x = 2 * PI_HALF;
- y.y = 3 * PI_HALF;
- z.z = 3 * PI_HALF;
- }
- }else if(below){
- if(camObjectPos.x > 0 && camObjectPos.y > 0){
- x.x = 0 * PI_HALF;
- y.y = 0 * PI_HALF;
- z.z = 0 * PI_HALF;
- }else if(camObjectPos.x < 0 && camObjectPos.y > 0){
- x.x = 0 * PI_HALF;
- y.y = 1 * PI_HALF;
- z.z = 1 * PI_HALF;
- }else if(camObjectPos.x < 0 && camObjectPos.y < 0){
- x.x = 3 * PI_HALF;
- y.y = 1 * PI_HALF;
- z.z = 2 * PI_HALF;
- }else if(camObjectPos.x > 0 && camObjectPos.y < 0){
- x.x = 3 * PI_HALF;
- y.y = 0 * PI_HALF;
- z.z = 3 * PI_HALF;
- }
-
- }
-
- }
- // adjust scale of components
- for(let handleName of Object.keys(this.handles)){
- let handle = this.handles[handleName];
- let node = handle.node;
- let handlePos = node.getWorldPosition(new Vector3());
- let distance = handlePos.distanceTo(camera.position);
- let pr = Utils.projectedRadius(1, camera, distance, domElement.clientWidth, domElement.clientHeight);
- let ws = node.parent.getWorldScale(new Vector3());
- let s = (ScaleRatio / pr);
- let scale = new Vector3(s, s, s).divide(ws);
-
- let rot = new Matrix4().makeRotationFromEuler(node.rotation); //需要使用到旋转,所以我把设置scale的移到旋转后了,否则在视图上下旋转的分界线处rotateHandel会被拉长从而闪烁。
- let rotInv = rot.clone().invert();
- scale.applyMatrix4(rotInv);
- scale.x = Math.abs(scale.x);
- scale.y = Math.abs(scale.y);
- scale.z = Math.abs(scale.z);
- node.scale.copy(scale);
- }
- {
- let ray = Utils.mouseToRay(pointer, camera, domElement.clientWidth, domElement.clientHeight);
- let raycaster = new Raycaster(ray.origin, ray.direction);
- raycaster.layers.enableAll();//add
-
- let intersects = raycaster.intersectObjects(this.pickVolumes.filter(v => v.visible), true);
-
-
-
-
- if(intersects.length > 0){
- let I = intersects[0];
- let handleName = I.object.handle;
- this.setActiveHandle(this.handles[handleName]);
- }else {
- this.setActiveHandle(null);
- }
- }
- //
- for(let handleName of Object.keys(this.scaleHandles)){
- let handle = this.handles[handleName];
- let node = handle.node;
- let alignment = handle.alignment;
-
- }
- }
- }else {
- this.scene.visible = false;
- }
-
- }
- };
- /*
- note:
-
- transformationTool.scene会跟随选中物体,其scale就是boundingbox的大小。因此transformationTool.frame这个框也会跟着缩放
-
-
- */
- class VolumeTool extends EventDispatcher{
- constructor (viewer) {
- super();
- this.viewer = viewer;
- this.renderer = viewer.renderer;
- this.addEventListener('start_inserting_volume', e => {
- this.viewer.dispatchEvent({
- type: 'cancel_insertions'
- });
- });
- this.scene = new Scene();
- this.scene.name = 'scene_volume';
- this.viewer.inputHandler.registerInteractiveScene(this.scene);
- this.onRemove = e => {
- this.scene.remove(e.volume);
- };
- this.onAdd = e => {
- this.scene.add(e.volume);
- };
- for(let volume of viewer.scene.volumes){
- this.onAdd({volume: volume});
- }
- this.viewer.inputHandler.addEventListener('delete', e => {
- let volumes = e.selection.filter(e => (e instanceof Volume));
- volumes.forEach(e => this.viewer.scene.removeVolume(e));
- });
- viewer.addEventListener("update", this.update.bind(this));
- viewer.addEventListener("render.pass.scene", e => this.render(e));
- viewer.addEventListener("scene_changed", this.onSceneChange.bind(this));
- viewer.scene.addEventListener('volume_added', this.onAdd);
- viewer.scene.addEventListener('volume_removed', this.onRemove);
- }
- onSceneChange(e){
- if(e.oldScene){
- e.oldScene.removeEventListeners('volume_added', this.onAdd);
- e.oldScene.removeEventListeners('volume_removed', this.onRemove);
- }
- e.scene.addEventListener('volume_added', this.onAdd);
- e.scene.addEventListener('volume_removed', this.onRemove);
- }
- startInsertion (args = {}) {
- let volume;
- if(args.type){
- volume = new args.type();
- }else {
- volume = new BoxVolume();
- }
-
- volume.clip = args.clip || false;
- volume.name = args.name || 'Volume';
- this.dispatchEvent({
- type: 'start_inserting_volume',
- volume: volume
- });
- this.viewer.scene.addVolume(volume);
- //this.scene.add(volume);
- let cancel = {
- callback: null
- };
- let drag = e => {
- let camera = this.viewer.scene.getActiveCamera();
-
-
- var I = e.intersectPoint;
- if (I) {
- volume.position.copy(I.location);
- let wp = volume.getWorldPosition(new Vector3()).applyMatrix4(camera.matrixWorldInverse);
- // let pp = new THREE.Vector4(wp.x, wp.y, wp.z).applyMatrix4(camera.projectionMatrix);
- let w = Math.abs((wp.z / 5));
- volume.scale.set(w, w, w);
- }
- };
- let drop = e => {
- volume.removeEventListener('drag', drag);
- volume.removeEventListener('drop', drop);
- cancel.callback();
- };
- cancel.callback = e => {
- volume.removeEventListener('drag', drag);
- volume.removeEventListener('drop', drop);
- this.viewer.removeEventListener('cancel_insertions', cancel.callback);
- };
- volume.addEventListener('drag', drag);
- volume.addEventListener('drop', drop);
- this.viewer.addEventListener('cancel_insertions', cancel.callback);
- this.viewer.inputHandler.startDragging(volume);
- return volume;
- }
- update(){
- if (!this.viewer.scene) {
- return;
- }
-
- let camera = this.viewer.scene.getActiveCamera();
- let renderAreaSize = this.viewer.renderer.getSize(new Vector2$1());
- let clientWidth = renderAreaSize.width;
- let clientHeight = renderAreaSize.height;
- let volumes = this.viewer.scene.volumes;
- for (let volume of volumes) {
-
- }
- }
- render(params){
- const renderer = this.viewer.renderer;
- const oldTarget = renderer.getRenderTarget();
-
- if(params.renderTarget){
- renderer.setRenderTarget(params.renderTarget);
- }
- renderer.render(this.scene, this.viewer.scene.getActiveCamera());
- renderer.setRenderTarget(oldTarget);
- }
- }
- class Compass{
- constructor(viewer){
- this.viewer = viewer;
- this.visible = false;
- this.dom = this.createElement();
- viewer.addEventListener("update", () => {
- const direction = viewer.scene.view.direction.clone();
- direction.z = 0;
- direction.normalize();
- const camera = viewer.scene.getActiveCamera();
- const p1 = camera.getWorldPosition(new Vector3());
- const p2 = p1.clone().add(direction);
- const projection = viewer.getProjection();
- const azimuth = Utils.computeAzimuth(p1, p2, projection);
-
- this.dom.css("transform", `rotateZ(${-azimuth}rad)`);
- });
- this.dom.click( () => {
- viewer.setTopView();
- });
- const renderArea = $(viewer.renderArea);
- renderArea.append(this.dom);
- this.setVisible(this.visible);
- }
- setVisible(visible){
- this.visible = visible;
- const value = visible ? "" : "none";
- this.dom.css("display", value);
- }
- isVisible(){
- return this.visible;
- }
- createElement(){
- const style = `style="position: absolute; top: 10px; right: 10px; z-index: 10000; width: 64px;"`;
- const img = $(`<img src="${Potree.resourcePath}/images/compas.svg" ${style} />`);
- return img;
- }
- };
- class PotreeRenderer {
- constructor (viewer) {
- this.viewer = viewer;
- this.renderer = viewer.renderer;
- {
- let dummyScene = new Scene();
- let geometry = new SphereGeometry(0.001, 2, 2);
- let mesh = new Mesh(geometry, new MeshBasicMaterial());
- mesh.position.set(36453, 35163, 764712);
- dummyScene.add(mesh);
- this.dummyMesh = mesh;
- this.dummyScene = dummyScene;
- }
- }
- clearTargets(params){//add
- const viewer = this.viewer;
- const {renderer} = viewer;
- const oldTarget = renderer.getRenderTarget();
-
- if(params.target){//add
- renderer.setRenderTarget( params.target);
- renderer.clear();
- }
- if(params.rtEDL){
- renderer.setRenderTarget( params.rtEDL);
- renderer.clear();
- }else {
- renderer.setRenderTarget( this.rtEDL );
- renderer.clear( true, true, true );
- }
- /* renderer.setRenderTarget( this.rtRegular );
- renderer.clear( true, true, false ); */
- renderer.setRenderTarget(oldTarget);
- }
- clear(params={}){
- let {viewer, renderer} = this;
- // render skybox
- if(viewer.background === "skybox"){
- renderer.setClearColor(0xff0000, 1);
- }else if(viewer.background === "gradient"){
- renderer.setClearColor(0x00ff00, 1);
- }else if(viewer.background === "black"){
- renderer.setClearColor(0x000000, 1);
- }else if(viewer.background === "white"){
- renderer.setClearColor(0xFFFFFF, 1);
- }else {
- renderer.setClearColor(0x000000, 0);
- }
-
- params.target || renderer.clear();
-
- renderer.clear();
- this.clearTargets(params);
- }
-
- render(params={}){
- let {viewer, renderer} = this;
- const camera = params.camera ? params.camera : viewer.scene.getActiveCamera();
- viewer.dispatchEvent({type: "render.pass.begin",viewer: viewer});
- const renderAreaSize = renderer.getSize(new Vector2$1());
- const width = params.width ? params.width : params.viewport ? params.viewport[2] : renderAreaSize.x;
- const height = params.height ? params.height : params.viewport ? params.viewport[3] : renderAreaSize.y;
- // render skybox
- if(viewer.background === "skybox"){
- viewer.skybox.camera.rotation.copy(viewer.scene.cameraP.rotation);
- viewer.skybox.camera.fov = viewer.scene.cameraP.fov;
- viewer.skybox.camera.aspect = viewer.scene.cameraP.aspect;
-
- viewer.skybox.parent.rotation.x = 0;
- viewer.skybox.parent.updateMatrixWorld();
- viewer.skybox.camera.updateProjectionMatrix();
- renderer.render(viewer.skybox.scene, viewer.skybox.camera);
- }else if(viewer.background === "gradient"){
-
- if(params.target){//add
- renderer.setRenderTarget(params.target);
- renderer.render(viewer.scene.sceneBG, viewer.scene.cameraBG);
- renderer.setRenderTarget(null);
- }else {
- renderer.render(viewer.scene.sceneBG, viewer.scene.cameraBG);
- }
-
- }
-
- for(let pointcloud of this.viewer.scene.pointclouds){
- const {material} = pointcloud;
- material.useEDL = false;
- }
-
-
- viewer.pRenderer.render(viewer.scene.scenePointCloud, camera, params.target || null, {
- clipSpheres: viewer.scene.volumes.filter(v => (v instanceof Potree.SphereVolume)),
- });
-
-
-
- // render scene
- viewer.dispatchEvent({type: "render.pass.scene",viewer: viewer});
- renderer.setRenderTarget(params.target ? params.target : null);//add
- renderer.render(viewer.scene.scene, camera);
-
-
- renderer.render(viewer.scene.sceneOverlay, camera);// add 透明贴图层
-
-
- renderer.clearDepth();
- viewer.transformationTool.update();
-
-
- if(!params.target){
-
- //测量线
- viewer.dispatchEvent({type: "render.pass.perspective_overlay", viewer: viewer, camera});
- viewer.renderer.render(viewer.overlay, camera);//从 viewer.renderDefault搬过来,为了reticule不遮住测量线
-
- }
-
-
- viewer.clippingTool.update();
-
- renderer.render(viewer.clippingTool.sceneMarker, viewer.scene.cameraScreenSpace); //viewer.scene.cameraScreenSpace);
- renderer.render(viewer.clippingTool.sceneVolume, camera);
- renderer.render(viewer.controls.sceneControls, camera);
- viewer.renderer.render(viewer.transformationTool.scene, camera);
- //viewer.renderer.render(viewer.transformationTool.scene, camera);
-
-
-
-
-
-
- // renderer.render(viewer.controls.sceneControls, camera);
- // renderer.render(viewer.clippingTool.sceneVolume, camera);
- // renderer.render(viewer.transformationTool.scene, camera);
-
- // renderer.setViewport(width - viewer.navigationCube.width,
- // height - viewer.navigationCube.width,
- // viewer.navigationCube.width, viewer.navigationCube.width);
- // renderer.render(viewer.navigationCube, viewer.navigationCube.camera);
- // renderer.setViewport(0, 0, width, height);
-
- viewer.dispatchEvent({type: "render.pass.end",viewer: viewer});
- }
- }
- const copyShader = {
- uniforms: {
- tDiffuse: {
- type: "t",
- value: null
- },
- opacity: {
- type: "f",
- value: 1
- }
- },
- vertexShader: `
- varying vec2 vUv;
- void main() {
- vUv = uv;
- gl_Position = projectionMatrix * modelViewMatrix * vec4( position, 1.0 );
- }
- `,
- fragmentShader: `
- uniform float opacity;
- uniform sampler2D tDiffuse;
- varying vec2 vUv;
- void main() {
- vec4 texel = texture2D( tDiffuse, vUv );
- gl_FragColor = opacity * texel;
- }
- `
- };
- class EDLRenderer{//Eye-Dome Lighting 眼罩照明
- constructor(viewer){
- this.viewer = viewer;
- this.edlMaterial = null;
- //this.rtRegular;
- this.rtEDLs = new Map;
- this.gl = viewer.renderer.getContext();
- //反正也没用到,注释了:
- //this.shadowMap = new PointCloudSM(this.viewer.pRenderer);
-
- viewer.addEventListener('resize',this.resize.bind(this));
- this.initEDL(viewer);
- }
- initEDL(viewer){
- if (this.edlMaterial != null) {
- return;
- }
- this.edlMaterial = new EyeDomeLightingMaterial();
- this.edlMaterial.depthTest = true;
- this.edlMaterial.depthWrite = true;
- this.edlMaterial.transparent = true;
-
-
- /* this.rtRegular = new THREE.WebGLRenderTarget(viewer.mainViewport.resolution2.x, viewer.mainViewport.resolution2.y, {
- minFilter: THREE.NearestFilter,
- magFilter: THREE.NearestFilter,
- format: THREE.RGBAFormat,
- depthTexture: new THREE.DepthTexture(undefined, undefined, THREE.UnsignedIntType)
- });
- */
-
-
-
-
- /* let copyUniforms = THREE.UniformsUtils.clone( copyShader.uniforms );
-
- this.copyMaterial = new THREE.ShaderMaterial( {
- uniforms: copyUniforms,
- vertexShader: copyShader.vertexShader,
- fragmentShader: copyShader.fragmentShader,
- //premultipliedAlpha: true,
- transparent: true,
- //blending: THREE.AdditiveBlending,
- depthTest: false,
- depthWrite: false
- }); */
-
- };
- resize(e ){
- if(Features.EXT_DEPTH.isSupported()){
- let viewport = e.viewport;
-
- this.getRtEDL(viewport).setSize(viewport.resolution2.x, viewport.resolution2.y);
- }
- }
-
- clearTargets(params={}){
- const viewer = this.viewer;
- const {renderer} = viewer;
- const oldTarget = renderer.getRenderTarget();
-
- if(params.target){//add
- renderer.setRenderTarget( params.target);
- renderer.clear();
- }
-
-
-
- if(Features.EXT_DEPTH.isSupported()){
- if(params.rtEDL){
- renderer.setRenderTarget( params.rtEDL);
- renderer.clear();
- }else {
- var rtEDL = this.getRtEDL(params.viewport);
- if(rtEDL){
- renderer.setRenderTarget( rtEDL );
- renderer.clear( true, true, true );
- }
-
- }
- }
-
-
-
- //renderer.setRenderTarget( this.rtRegular );
- //renderer.clear( true, true, false );
- renderer.setRenderTarget(oldTarget);
- }
-
-
- getRtEDL(viewport){//根据不同viewport返回rtEDL的texture
- if(!viewport){
- console.warn('getRtEDL没传viewport!!!! !!!!!!!!!!');
- viewport = viewer.mainViewport;
- }
- var rtEDL = this.rtEDLs.get(viewport);
- if(!rtEDL){
- if(Features.EXT_DEPTH.isSupported()){
- rtEDL = new WebGLRenderTarget(viewport.resolution2.x, viewport.resolution2.y, {
- minFilter: NearestFilter,
- magFilter: NearestFilter,
- format: RGBAFormat,
- type: FloatType,
- depthTexture: new DepthTexture(undefined, undefined, UnsignedIntType)
- });
- //注: 部分手机在resize时会崩溃,经检验去掉rtEDL的resize可以解决,所以更应该注释掉这个
-
-
- this.rtEDLs.set(viewport, rtEDL);
- }
- }
-
- return rtEDL
- }
-
- renderShadowMap(visiblePointClouds, camera, lights){
- const {viewer} = this;
- const doShadows = lights.length > 0 && !(lights[0].disableShadowUpdates);
- if(doShadows){
- let light = lights[0];
-
- this.shadowMap.setLight(light);
- let originalAttributes = new Map();
- for(let pointcloud of viewer.scene.pointclouds){
- // TODO IMPORTANT !!! check
- originalAttributes.set(pointcloud, pointcloud.material.activeAttributeName);
- pointcloud.material.disableEvents();
- pointcloud.material.activeAttributeName = "depth";
- //pointcloud.material.pointColorType = PointColorType.DEPTH;
- }
- this.shadowMap.render(viewer.scene.scenePointCloud, camera);
- for(let pointcloud of visiblePointClouds){
- let originalAttribute = originalAttributes.get(pointcloud);
- // TODO IMPORTANT !!! check
- pointcloud.material.activeAttributeName = originalAttribute;
- pointcloud.material.enableEvents();
- }
- viewer.shadowTestCam.updateMatrixWorld();
- viewer.shadowTestCam.matrixWorldInverse.copy(viewer.shadowTestCam.matrixWorld).invert();
- viewer.shadowTestCam.updateProjectionMatrix();
- }
- }
- render(params={}){
-
-
- /*
- 渲染顺序:
- 底层:背景 -> skybox(也可中间)
- 中间层(含有深度信息):1 点云、marker等mesh,
- 2 测量线(现在被做成借用depthTex
- 顶层:maginifier
- magnifier的贴图渲染不需要顶层、中间层只需要点云。
- */
-
- const viewer = this.viewer;
- let camera = params.camera ? params.camera : viewer.scene.getActiveCamera();
- const resolution = params.viewport ? params.viewport.resolution2 : this.viewer.renderer.getSize(new Vector2$1());
-
-
- //viewer.dispatchEvent({type: "render.pass.begin",viewer: viewer});
-
-
-
-
-
-
- let lights = [];
- /* viewer.scene.scene.traverse(node => {
- if(node.type === "SpotLight"){
- lights.push(node);
- }
- }); */
- viewer.renderer.setRenderTarget(params.target || null);
- let background = params.background || viewer.background;
- let backgroundOpacity = params.backgroundOpacity == void 0 ? viewer.backgroundOpacity : params.backgroundOpacity;//如果想完全透明,只需要backgroundOpacity为0
- if(backgroundOpacity != 0){//绘制背景
- if(background === "skybox"){
- viewer.skybox.camera.rotation.copy(viewer.scene.cameraP.rotation);
- viewer.skybox.camera.fov = viewer.scene.cameraP.fov;
- viewer.skybox.camera.aspect = viewer.scene.cameraP.aspect;
- viewer.skybox.parent.rotation.x = 0;
- viewer.skybox.parent.updateMatrixWorld();
- viewer.skybox.camera.updateProjectionMatrix();
-
- viewer.renderer.render(viewer.skybox.scene, viewer.skybox.camera);
- }else if(background === 'gradient'){
- viewer.scene.cameraBG.layers.set(Potree.config.renderLayers.bg);
- viewer.renderer.render(viewer.scene.scene, viewer.scene.cameraBG);
- }else if(background === 'overlayColor'){//在不clear的前提下加一层背景色
- viewer.scene.bg2.material.color.copy(params.backgroundColor);
- viewer.scene.bg2.material.opacity = params.backgroundOpacity;
- viewer.scene.cameraBG.layers.set(Potree.config.renderLayers.bg2);
- viewer.renderer.render(viewer.scene.scene, viewer.scene.cameraBG);
- }
-
- }
-
- //skybox 全景图
- if(!params.magnifier){
- viewer.setCameraLayers(camera, ['skybox']);
- let useDepthTex;
- if(Potree.settings.displayMode == 'showPanos' && viewer.images360.currentPano.pointcloud.hasDepthTex && Features.EXT_DEPTH.isSupported()){//渲染深度图
- useDepthTex = true;
- viewer.renderer.setRenderTarget(params.rtEDL || this.getRtEDL(params.viewport)); //将带有深度图的skybox画在rtEDL一下,这样就不需要绘制后边的点云了
- viewer.renderer.render(viewer.scene.scene, camera);
- viewer.renderer.setRenderTarget(params.target || null);
- }
- viewer.renderer.render(viewer.scene.scene, camera);
- if(useDepthTex)return
- }
-
- if(viewer.scene.pointclouds.length == 0)return
-
-
- //const visiblePointClouds = viewer.scene.pointclouds.filter(pc => pc.visible );
- const visiblePointClouds2 = viewer.scene.pointclouds.filter(pc => viewer.getObjVisiByReason(pc,'datasetSelection') );
- const showPointClouds = viewer.scene.pointclouds.some(e=>e.visible);
-
- viewer.scene.pointclouds.forEach(e=>{//为了绘制到depthTexture,先显示(展示全景图时隐藏了点云,所以需要显示下。且放大镜需要绘制点云)
- e.oldVisi = e.visible;
- if(viewer.getObjVisiByReason(e, 'datasetSelection') ) e.visible = true;
- });
-
- //pointcloud
- viewer.setCameraLayers(camera, ['pointcloud']);
- camera.layers.set(Potree.config.renderLayers.pointcloud);
-
- //TODO adapt to multiple lights
- //this.renderShadowMap(visiblePointClouds2, camera, lights); //???????
- { //scenePointCloud COLOR & DEPTH PASS
- for (let pointcloud of visiblePointClouds2) {
-
- let material = pointcloud.material;
- let octreeSize = pointcloud.pcoGeometry.boundingBox.getSize(new Vector3()).x;
- material.fov = MathUtils.degToRad(camera.fov);
- /* material.screenWidth = width;
- material.screenHeight = height; */
- material.resolution = resolution;
-
-
-
- material.spacing = pointcloud.pcoGeometry.spacing; // * Math.max(this.scale.x, this.scale.y, this.scale.z);
- material.near = camera.near;
- material.far = camera.far;
- material.uniforms.octreeSize.value = octreeSize;
-
- if(viewer.useEDL && Potree.settings.displayMode != 'showPanos'){
- /* material.weighted = false;
- material.useLogarithmicDepthBuffer = false; */
- material.useEDL = true;
- //material.fakeEDL = false; //add
- }else {
- material.useEDL = false;
- //material.fakeEDL = true; //add 使也输出深度
- }
-
- }
-
-
- if(Features.EXT_DEPTH.isSupported() && !params.dontRenderRtEDL){
- //借用rtEDL存储深度信息
- viewer.renderer.setRenderTarget(params.rtEDL || this.getRtEDL(params.viewport)/* this.rtEDL */);
-
- //pointDensity已经使用的是panorama模式,在画面边缘点云会稀疏,遮挡效果差
-
- /* if(Potree.settings.displayMode == "showPanos"){ //还原成点云模式的材质,且可能要更大些,因点云变稀疏
- var matBefore = {
- pointSizeType : new Map(),
- size : new Map(),
- usePanoMap : new Map(),
- }
- for (let pointcloud of visiblePointClouds2) {
- matBefore.usePanoMap.set(pointcloud, pointcloud.material.usePanoMap)
- //matBefore.pointSizeType.set(pointcloud, pointcloud.material.pointSizeType)
- matBefore.size.set(pointcloud, pointcloud.temp.pointSize)
-
- //pointcloud.material.pointSizeType = Potree.config.material.pointSizeType //不能修改这项,因为是define,会卡。
- //pointcloud.changePointSize(pointcloud.temp.pointSize * (1+Potree.config.material.sizeAddAtPanoRtEDL))
-
- //pointcloud.changePointSize(Potree.config.material.sizeAtPanoRtEDL, true)
- pointcloud.material.usePanoMap = false //禁止使用absolutePanoramaSize大小
-
- }
- } */
- //渲染scenePointCloud到rtEDL
- viewer.pRenderer.render(viewer.scene.scenePointCloud, camera, params.rtEDL || this.getRtEDL(params.viewport), {
- shadowMaps: lights.length > 0 ? [this.shadowMap] : null,
- clipSpheres: viewer.scene.volumes.filter(v => (v instanceof SphereVolume)),
- transparent: false,
- });
-
-
- //test
- /* {
- viewer.objs.traverse((obj)=>{
- if(obj.material){
- obj.material = obj.depthMat
- }
- })
- viewer.setCameraLayers(camera, ['sceneObjects'])
- viewer.renderer.render(viewer.scene.scene, camera)
- viewer.objs.traverse((obj)=>{
- if(obj.material){
- obj.material = obj.standardMat
- }
- })
- } */
-
-
- /* if(Potree.settings.displayMode == "showPanos"){
- for (let pointcloud of visiblePointClouds2) {
-
- //pointcloud.material.pointSizeType = matBefore.pointSizeType.get(pointcloud)
- pointcloud.material.usePanoMap = matBefore.usePanoMap.get(pointcloud)
- //pointcloud.changePointSize(matBefore.size.get(pointcloud))
- }
- } */
- }
- }
-
-
-
-
- //渲染到rtEDL完毕
- viewer.dispatchEvent({type: "render.pass.scene", viewer: viewer/* , renderTarget: this.rtRegular */});
- viewer.renderer.setRenderTarget(params.target || null);
-
- if(!params.magnifier)viewer.scene.pointclouds.forEach(e=>{//放大镜显示点云
- e.visible = e.oldVisi;
- });
-
-
-
-
- //设置edlMaterial
- if(viewer.useEDL && showPointClouds /* || params.magnifier */) {
- //Features.EXT_DEPTH不支持的话不会到这一块
-
- const uniforms = this.edlMaterial.uniforms;
- //if(viewer.useEDL){
- /* uniforms.screenWidth.value = width;
- uniforms.screenHeight.value = height; */
- uniforms.resolution.value.copy(resolution);
-
- uniforms.edlStrength.value = viewer.edlStrength;
- uniforms.radius.value = viewer.edlRadius;
- uniforms.useEDL.value = 1;//add
- /* }else{
- uniforms.useEDL.value = 0;//add
- } */
-
- let proj = camera.projectionMatrix;
- let projArray = new Float32Array(16);
- projArray.set(proj.elements);
- uniforms.uProj.value = projArray;
-
- uniforms.uEDLColor.value = (params.rtEDL || this.getRtEDL(params.viewport)).texture;
- //uniforms.uEDLDepth.value = (params.rtEDL || this.getRtEDL(params.viewport)).depthTexture; //其实没用到
-
- uniforms.opacity.value = viewer.edlOpacity; // HACK
-
-
- Utils.screenPass.render(viewer.renderer, this.edlMaterial, params.target);
- }else {
- //渲染点云 (直接用rtEDL上的会失去抗锯齿)
- viewer.pRenderer.render(viewer.scene.scenePointCloud, camera, null , {
- shadowMaps: lights.length > 0 ? [this.shadowMap] : null,
- clipSpheres: viewer.scene.volumes.filter(v => (v instanceof SphereVolume))
- });
- }
-
- //viewer.dispatchEvent({type: "render.pass.end",viewer: viewer});
- viewer.scene.pointclouds.forEach(e=>{
- e.visible = e.oldVisi;
- });
-
- }
- }
- class HQSplatRenderer{
-
-
- //rtAttribute 估计需要给magnifer复制一份
-
- constructor(viewer){
- this.viewer = viewer;
- this.depthMaterials = new Map();
- this.attributeMaterials = new Map();
- this.normalizationMaterial = null;
- this.rtDepth = null;
- this.rtAttribute = null;
- this.gl = viewer.renderer.getContext();
- this.initialized = false;
-
- viewer.addEventListener('resize',this.resize.bind(this));
- }
- init(){
- if (this.initialized) {
- return;
- }
- this.normalizationMaterial = new NormalizationMaterial();
- this.normalizationMaterial.depthTest = true;
- this.normalizationMaterial.depthWrite = true;
- this.normalizationMaterial.transparent = true;
- this.normalizationEDLMaterial = new NormalizationEDLMaterial();
- this.normalizationEDLMaterial.depthTest = true;
- this.normalizationEDLMaterial.depthWrite = true;
- this.normalizationEDLMaterial.transparent = true;
- this.rtDepth = new WebGLRenderTarget(1024, 1024, {
- minFilter: NearestFilter,
- magFilter: NearestFilter,
- format: RGBAFormat,
- type: FloatType,
- depthTexture: new DepthTexture(undefined, undefined, UnsignedIntType)
- });
- this.rtAttribute = new WebGLRenderTarget(1024, 1024, {
- minFilter: NearestFilter,
- magFilter: NearestFilter,
- format: RGBAFormat,
- type: FloatType,
- depthTexture: this.rtDepth.depthTexture,
- });
- this.initialized = true;
- };
- resize(e/* width, height */){
- this.rtDepth.setSize(e.canvasWidth, e.canvasHeight);
- this.rtAttribute.setSize(e.canvasWidth, e.canvasHeight);
- }
- clearTargets(params){
- const viewer = this.viewer;
- const {renderer} = viewer;
- const oldTarget = renderer.getRenderTarget();
-
-
- if(params.target){//add
- renderer.setRenderTarget( params.target);
- renderer.clear();
- }
- renderer.setClearColor(0x000000, 0);
- renderer.setRenderTarget( this.rtDepth );
- renderer.clear( true, true, true );
- renderer.setRenderTarget( this.rtAttribute );
- renderer.clear( true, true, true );
- renderer.setRenderTarget(oldTarget);
- }
- clear(params={}){
- this.init();
- const {renderer, background} = this.viewer;
- if(background === "skybox"){
- renderer.setClearColor(0x000000, 0);
- } else if (background === 'gradient') {
- renderer.setClearColor(0x000000, 0);
- } else if (background === 'black') {
- renderer.setClearColor(0x000000, 1);
- } else if (background === 'white') {
- renderer.setClearColor(0xFFFFFF, 1);
- } else {
- renderer.setClearColor(0x000000, 0);
- }
- params.target || renderer.clear();
- this.clearTargets(params);
- }
- render(params={}) {
- this.init();
- const viewer = this.viewer;
- const camera = params.camera ? params.camera : viewer.scene.getActiveCamera();
- const {width, height} = params.width ? params : this.viewer.renderer.getSize(new Vector2$1());
- viewer.renderer.setRenderTarget(params.target||null);
- viewer.dispatchEvent({type: "render.pass.begin",viewer: viewer});
- //params.target || this.resize(width, height);
- const visiblePointClouds = viewer.scene.pointclouds.filter(pc => pc.visible);
- const originalMaterials = new Map();
- for(let pointcloud of visiblePointClouds){
- originalMaterials.set(pointcloud, pointcloud.material);
- if(!this.attributeMaterials.has(pointcloud)){
- let attributeMaterial = new PointCloudMaterial$1();
- this.attributeMaterials.set(pointcloud, attributeMaterial);
- }
- if(!this.depthMaterials.has(pointcloud)){
- let depthMaterial = new PointCloudMaterial$1();
- depthMaterial.setDefine("depth_pass", "#define hq_depth_pass");
- depthMaterial.setDefine("use_edl", "#define use_edl");
- this.depthMaterials.set(pointcloud, depthMaterial);
- }
- }
- { // DEPTH PASS
- for (let pointcloud of visiblePointClouds) {
- let octreeSize = pointcloud.pcoGeometry.boundingBox.getSize(new Vector3()).x;
- let material = originalMaterials.get(pointcloud);
- let depthMaterial = this.depthMaterials.get(pointcloud);
- depthMaterial.size = material.size;
- depthMaterial.minSize = material.minSize;
- depthMaterial.maxSize = material.maxSize;
- depthMaterial.pointSizeType = material.pointSizeType;
- depthMaterial.visibleNodesTexture = material.visibleNodesTexture;
- depthMaterial.weighted = false;
- depthMaterial.screenWidth = width;
- depthMaterial.shape = PointShape.CIRCLE;
- depthMaterial.screenHeight = height;
- depthMaterial.uniforms.visibleNodes.value = material.visibleNodesTexture;
- depthMaterial.uniforms.octreeSize.value = octreeSize;
- depthMaterial.spacing = pointcloud.pcoGeometry.spacing; // * Math.max(...pointcloud.scale.toArray());
- depthMaterial.classification = material.classification;
- depthMaterial.uniforms.classificationLUT.value.image.data = material.uniforms.classificationLUT.value.image.data;
- depthMaterial.classificationTexture.needsUpdate = true;
- depthMaterial.uniforms.uFilterReturnNumberRange.value = material.uniforms.uFilterReturnNumberRange.value;
- depthMaterial.uniforms.uFilterNumberOfReturnsRange.value = material.uniforms.uFilterNumberOfReturnsRange.value;
- depthMaterial.uniforms.uFilterGPSTimeClipRange.value = material.uniforms.uFilterGPSTimeClipRange.value;
- depthMaterial.uniforms.uFilterPointSourceIDClipRange.value = material.uniforms.uFilterPointSourceIDClipRange.value;
- depthMaterial.clipTask = material.clipTask;
- depthMaterial.clipMethod = material.clipMethod;
- depthMaterial.setClipBoxes(material.clipBoxes);
- depthMaterial.setClipPolygons(material.clipPolygons);
- pointcloud.material = depthMaterial;
- }
-
- viewer.pRenderer.render(viewer.scene.scenePointCloud, camera, (params.rtEDL || this.rtDepth), {
- clipSpheres: viewer.scene.volumes.filter(v => (v instanceof SphereVolume)),
- });
- }
- { // ATTRIBUTE PASS
- for (let pointcloud of visiblePointClouds) {
- let octreeSize = pointcloud.pcoGeometry.boundingBox.getSize(new Vector3()).x;
- let material = originalMaterials.get(pointcloud);
- let attributeMaterial = this.attributeMaterials.get(pointcloud);
- attributeMaterial.size = material.size;
- attributeMaterial.minSize = material.minSize;
- attributeMaterial.maxSize = material.maxSize;
- attributeMaterial.pointSizeType = material.pointSizeType;
- attributeMaterial.activeAttributeName = material.activeAttributeName;
- attributeMaterial.visibleNodesTexture = material.visibleNodesTexture;
- attributeMaterial.weighted = true;
- attributeMaterial.screenWidth = width;
- attributeMaterial.screenHeight = height;
- attributeMaterial.shape = PointShape.CIRCLE;
- attributeMaterial.uniforms.visibleNodes.value = material.visibleNodesTexture;
- attributeMaterial.uniforms.octreeSize.value = octreeSize;
- attributeMaterial.spacing = pointcloud.pcoGeometry.spacing; // * Math.max(...pointcloud.scale.toArray());
- attributeMaterial.classification = material.classification;
- attributeMaterial.uniforms.classificationLUT.value.image.data = material.uniforms.classificationLUT.value.image.data;
- attributeMaterial.classificationTexture.needsUpdate = true;
- attributeMaterial.uniforms.uFilterReturnNumberRange.value = material.uniforms.uFilterReturnNumberRange.value;
- attributeMaterial.uniforms.uFilterNumberOfReturnsRange.value = material.uniforms.uFilterNumberOfReturnsRange.value;
- attributeMaterial.uniforms.uFilterGPSTimeClipRange.value = material.uniforms.uFilterGPSTimeClipRange.value;
- attributeMaterial.uniforms.uFilterPointSourceIDClipRange.value = material.uniforms.uFilterPointSourceIDClipRange.value;
- attributeMaterial.elevationGradientRepeat = material.elevationGradientRepeat;
- attributeMaterial.elevationRange = material.elevationRange;
- attributeMaterial.gradient = material.gradient;
- attributeMaterial.matcap = material.matcap;
- attributeMaterial.intensityRange = material.intensityRange;
- attributeMaterial.intensityGamma = material.intensityGamma;
- attributeMaterial.intensityContrast = material.intensityContrast;
- attributeMaterial.intensityBrightness = material.intensityBrightness;
- attributeMaterial.rgbGamma = material.rgbGamma;
- attributeMaterial.rgbContrast = material.rgbContrast;
- attributeMaterial.rgbBrightness = material.rgbBrightness;
- attributeMaterial.weightRGB = material.weightRGB;
- attributeMaterial.weightIntensity = material.weightIntensity;
- attributeMaterial.weightElevation = material.weightElevation;
- attributeMaterial.weightRGB = material.weightRGB;
- attributeMaterial.weightClassification = material.weightClassification;
- attributeMaterial.weightReturnNumber = material.weightReturnNumber;
- attributeMaterial.weightSourceID = material.weightSourceID;
- attributeMaterial.color = material.color;
- attributeMaterial.clipTask = material.clipTask;
- attributeMaterial.clipMethod = material.clipMethod;
- attributeMaterial.setClipBoxes(material.clipBoxes);
- attributeMaterial.setClipPolygons(material.clipPolygons);
- pointcloud.material = attributeMaterial;
- }
-
- let gl = this.gl;
- //viewer.renderer.setRenderTarget(null);
- viewer.pRenderer.render(viewer.scene.scenePointCloud, camera, this.rtAttribute, {
- clipSpheres: viewer.scene.volumes.filter(v => (v instanceof SphereVolume)),
- //material: this.attributeMaterial,
- blendFunc: [gl.SRC_ALPHA, gl.ONE],
- //depthTest: false,
- depthWrite: false
- });
- }
- for(let [pointcloud, material] of originalMaterials){
- pointcloud.material = material;
- }
-
-
-
-
-
- if(viewer.background === "skybox"){
- viewer.renderer.setClearColor(0x000000, 0);
- viewer.renderer.clear();
- viewer.skybox.camera.rotation.copy(viewer.scene.cameraP.rotation);
- viewer.skybox.camera.fov = viewer.scene.cameraP.fov;
- viewer.skybox.camera.aspect = viewer.scene.cameraP.aspect;
-
- viewer.skybox.parent.rotation.x = 0;
- viewer.skybox.parent.updateMatrixWorld();
- viewer.skybox.camera.updateProjectionMatrix();
- viewer.renderer.render(viewer.skybox.scene, viewer.skybox.camera);
- } else if (viewer.background === 'gradient') {
- viewer.renderer.setClearColor(0x000000, 0);
- viewer.renderer.clear();
- viewer.renderer.render(viewer.scene.sceneBG, viewer.scene.cameraBG);
- } else if (viewer.background === 'black') {
- viewer.renderer.setClearColor(0x000000, 1);
- viewer.renderer.clear();
- } else if (viewer.background === 'white') {
- viewer.renderer.setClearColor(0xFFFFFF, 1);
- viewer.renderer.clear();
- } else {
- viewer.renderer.setClearColor(0x000000, 0);
- viewer.renderer.clear();
- }
-
- { // NORMALIZATION PASS
- let normalizationMaterial = this.useEDL ? this.normalizationEDLMaterial : this.normalizationMaterial;
- if(this.useEDL){
- normalizationMaterial.uniforms.edlStrength.value = viewer.edlStrength;
- normalizationMaterial.uniforms.radius.value = viewer.edlRadius;
- normalizationMaterial.uniforms.screenWidth.value = width;
- normalizationMaterial.uniforms.screenHeight.value = height;
- normalizationMaterial.uniforms.uEDLMap.value = (params.rtEDL || this.rtDepth).texture;
- }
- normalizationMaterial.uniforms.uWeightMap.value = this.rtAttribute.texture;
- normalizationMaterial.uniforms.uDepthMap.value = this.rtAttribute.depthTexture;
-
- Utils.screenPass.render(viewer.renderer, normalizationMaterial);
- }
- viewer.renderer.render(viewer.scene.scene, camera);
- viewer.dispatchEvent({type: "render.pass.scene", viewer: viewer});
- viewer.renderer.render(viewer.scene.sceneOverlay, camera);// add 透明贴图层
- viewer.renderer.clearDepth();
- viewer.transformationTool.update();
- if(!params.target){
-
- //测量线
- viewer.dispatchEvent({type: "render.pass.perspective_overlay",viewer: viewer, camera});
- viewer.renderer.render(viewer.overlay, camera);//从 viewer.renderDefault搬过来,为了reticule不遮住测量线
- }
- viewer.renderer.render(viewer.controls.sceneControls, camera);
- viewer.renderer.render(viewer.clippingTool.sceneVolume, camera);
- viewer.renderer.render(viewer.transformationTool.scene, camera);
- viewer.renderer.setViewport(width - viewer.navigationCube.width,
- height - viewer.navigationCube.width,
- viewer.navigationCube.width, viewer.navigationCube.width);
- viewer.renderer.render(viewer.navigationCube, viewer.navigationCube.camera);
- viewer.renderer.setViewport(0, 0, width, height);
-
- viewer.dispatchEvent({type: "render.pass.end",viewer: viewer});
- viewer.renderer.setRenderTarget(null);
- }
- }
- let sid = 0;
-
- class View extends EventDispatcher{
- constructor () {
- super();
- this.position = new Vector3(0, 0, 0);
- this.yaw = 0;//Math.PI / 4; // = 4dkk lon + 90
- this._pitch = 0;//-Math.PI / 4; //上下旋转 = 4dkk lat
- this.radius = 1;
- this.maxPitch = Math.PI / 2;
- this.minPitch = -Math.PI / 2;
-
- this.sid = sid++;
- this.LookTransition = 'LookTransition'+this.sid;
-
- }
- //add------
- applyToCamera(camera){
- camera.position.copy(this.position);
- camera.rotation.copy(this.rotation);
-
-
- camera.updateMatrix();
- camera.updateMatrixWorld();
- //camera.matrixWorldInverse.copy(camera.matrixWorld).invert();
-
-
- }
-
- get rotation(){
- var rotation = new Euler;
- rotation.order = "ZXY";
- rotation.x = Math.PI / 2 + this.pitch;
- rotation.z = this.yaw;
- return rotation
- }
-
- set rotation(rotation){
- //因为 rotation的y不一定是0 , 所以不能直接逆着写。
- this.direction = new Vector3(0,0,-1).applyEuler(rotation);
- }
-
-
-
-
- copy(a){
- Common.CopyClassObject(this, a);
- }
-
- clone () {
- /* let c = new View();
- c.yaw = this.yaw;
- c._pitch = this.pitch;
- c.radius = this.radius;
- c.maxPitch = this.maxPitch;
- c.minPitch = this.minPitch;
- return c; */
-
- return Common.CloneClassObject(this)
- }
-
- //----------
-
-
-
- get pitch () {
- return this._pitch;
- }
- set pitch (angle) {
- this._pitch = Math.max(Math.min(angle, this.maxPitch), this.minPitch);
- }
- get direction () {
- let dir = new Vector3(0, 1, 0);
- dir.applyAxisAngle(new Vector3(1, 0, 0), this.pitch);
- dir.applyAxisAngle(new Vector3(0, 0, 1), this.yaw);
- return dir;
- }
- set direction (dir) {
- //if(dir.x === dir.y){
- if(dir.x === 0 && dir.y === 0){
- this.pitch = Math.PI / 2 * Math.sign(dir.z);
- }else {
- let yaw = Math.atan2(dir.y, dir.x) - Math.PI / 2;
- let pitch = Math.atan2(dir.z, Math.sqrt(dir.x * dir.x + dir.y * dir.y));
- this.yaw = yaw;
- this.pitch = pitch;
- }
-
- }
- lookAt(t){//setPivot
- let V;
- if(arguments.length === 1){
- V = new Vector3().subVectors(t, this.position);
- }else if(arguments.length === 3){
- V = new Vector3().subVectors(new Vector3(...arguments), this.position);
- }
- let radius = V.length();
- let dir = V.normalize();
- this.radius = radius;
- this.direction = dir;
- }
- getPivot () {
- return new Vector3().addVectors(this.position, this.direction.multiplyScalar(this.radius));
- }
- getSide () {
- let side = new Vector3(1, 0, 0);
- side.applyAxisAngle(new Vector3(0, 0, 1), this.yaw);
- return side;
- }
- /* pan (x, y) {
- let dir = new THREE.Vector3(0, 1, 0);
- dir.applyAxisAngle(new THREE.Vector3(1, 0, 0), this.pitch);
- dir.applyAxisAngle(new THREE.Vector3(0, 0, 1), this.yaw);
- // let side = new THREE.Vector3(1, 0, 0);
- // side.applyAxisAngle(new THREE.Vector3(0, 0, 1), this.yaw);
- let side = this.getSide();
- let up = side.clone().cross(dir);
- let pan = side.multiplyScalar(x).add(up.multiplyScalar(y));
- this.position = this.position.add(pan);
- // this.target = this.target.add(pan);
- } */
- pan (x, y) { //发现pan其实就是translate
- this.translate(x, 0, y);
- }
- translate (x, y, z) {
- let dir = new Vector3(0, 1, 0);
- dir.applyAxisAngle(new Vector3(1, 0, 0), this.pitch);
- dir.applyAxisAngle(new Vector3(0, 0, 1), this.yaw);
- let side = new Vector3(1, 0, 0);
- side.applyAxisAngle(new Vector3(0, 0, 1), this.yaw);
- let up = side.clone().cross(dir);
- let t = side.multiplyScalar(x)
- .add(dir.multiplyScalar(y))
- .add(up.multiplyScalar(z));
-
- if(this.fixZWhenPan) t.setZ(0); //在水平面上平移
-
- this.position = this.position.add(t);
- this.restrictPos();
- }
- translateWorld (x, y, z) {
- this.position.x += x;
- this.position.y += y;
- this.position.z += z;
- this.restrictPos();
- }
- restrictPos(){//add
- if(this.limitBound){
- this.position.clamp(this.limitBound.min, this.limitBound.max);
- }
- }
- setCubeView(dir) {
-
- switch(dir) {
- case "front":
- this.yaw = 0;
- this.pitch = 0;
- break;
- case "back":
- this.yaw = Math.PI;
- this.pitch = 0;
- break;
- case "left":
- this.yaw = -Math.PI / 2;
- this.pitch = 0;
- break;
- case "right":
- this.yaw = Math.PI / 2;
- this.pitch = 0;
- break;
- case "top":
- this.yaw = 0;
- this.pitch = -Math.PI / 2;
- break;
- case "bottom":
- this.yaw = -Math.PI;
- this.pitch = Math.PI / 2;
- break;
- }
- }
-
-
-
- isFlying(){
- return transitions.getById(this.LookTransition).length > 0
- }
-
- cancelFlying(){//外界只能通过这个来cancel
- transitions.cancelById(this.LookTransition, true );
- }
-
- setView( info = {}){
- // position, target, duration = 0, callback = null, onUpdate = null, Easing='', cancelFun
- this.cancelFlying();
-
- let done = ()=>{
- if(endTarget){
- this.lookAt(endTarget); //compute radius for orbitcontrol
- }else if(endQuaternion){
- this.rotation = new Euler().setFromQuaternion(endQuaternion);
- }
-
- let f = ()=>{
- info.callback && info.callback();
- this.dispatchEvent('flyingDone');
- };
- if(info.duration){
- setTimeout(f,1);//延迟是为了使isFlying先为false
- }else {
- f(); //有的需要迅速执行回调
- }
-
-
- };
-
- let endPosition = new Vector3().copy(info.position);
-
- const startPosition = this.position.clone();
- const startTarget = this.getPivot();
- let startQuaternion = math.getQuaFromPosAim(startPosition,startTarget);
-
- let endTarget = null, endQuaternion ;
-
-
-
-
- if(info.target ){
- endTarget = new Vector3().copy(info.target);
- endQuaternion = math.getQuaFromPosAim(endPosition,endTarget);
- }else if(info.quaternion){
- endQuaternion = info.quaternion.clone();
- }
-
-
- if(!info.duration){
- this.position.copy(endPosition);
- this.restrictPos();
-
- info.onUpdate && info.onUpdate(1);
- done();
-
- }else {
- transitions.start(lerp.vector(this.position, endPosition, (pos, progress)=>{
- let t = progress;
-
- if(endQuaternion){
- let quaternion = (new Quaternion()).copy(startQuaternion);
- lerp.quaternion(quaternion, endQuaternion)(progress),
- this.rotation = new Euler().setFromQuaternion(quaternion);
- }
- this.restrictPos();
-
- info.onUpdate && info.onUpdate(t);//add
- }), info.duration, done, 0, info.Easing ? easing[info.Easing] : easing.easeInOutSine /*easeInOutQuad */,null, this.LookTransition, info.cancelFun);
- }
- }
-
-
- //平移Ortho相机
- moveOrthoCamera(viewport, info, duration, easeName){//boundSize优先于endZoom。
- let camera = viewport.camera;
-
- let startZoom = camera.zoom;
- let endPosition = info.endPosition;
- let boundSize = info.boundSize;
- let endZoom = info.endZoom;
- let margin = info.margin || {x:0,y:0};/* 200 */ //像素
-
- if(info.bound){//需要修改boundSize以适应相机的旋转,当相机不在xy水平面上朝向z时
- endPosition = endPosition || info.bound.getCenter(new Vector3());
-
- let matrixRot = new Matrix4().makeRotationFromEuler(this.rotation).invert();
- let boundingBox = info.bound.clone().applyMatrix4(matrixRot);
- boundSize = boundingBox.getSize(new Vector3());
-
- }
-
- if(boundSize && boundSize.x == 0 && boundSize.y == 0){
- boundSize.set(1,1); //避免infinity
- }
-
- this.setView({ position:endPosition, duration,
- callback:()=>{//done
-
- },
- onUpdate:(progress)=>{
- if(boundSize || endZoom){
- if(boundSize){
- let aspect = boundSize.x / boundSize.y;
- let w, h;
-
- if(camera.aspect > aspect){//视野更宽则用bound的纵向来决定
- h = boundSize.y;
- endZoom = (viewport.resolution.y - margin.x) / h; //注意,要在resolution不为0时执行
- }else {
- w = boundSize.x;
- endZoom = (viewport.resolution.x - margin.y) / w;
- }
- //onUpdate时更新endzoom是因为画布大小可能更改
- }
-
- camera.zoom = endZoom * progress + startZoom * (1 - progress);
- camera.updateProjectionMatrix();
- }
- },
-
- Easing:easeName
-
- });
-
-
- }
-
-
-
- zoomOrthoCamera(camera, endZoom, pointer, duration, onProgress){//定点缩放
-
- let startZoom = camera.zoom;
-
- let pointerPos = new Vector3(pointer.x, pointer.y,0.5);
-
-
- transitions.start(( progress)=>{
- let oldPos = pointerPos.clone().unproject(camera);
-
- camera.zoom = endZoom * progress + startZoom * (1 - progress);
- camera.updateProjectionMatrix();
-
-
- let newPos = pointerPos.clone().unproject(camera);
-
- //定点缩放, 恢复一下鼠标所在位置的位置改变量
- let moveVec = new Vector3().subVectors(newPos, oldPos);
-
- camera.position.sub(moveVec);
- this.position.copy(camera.position);
-
- onProgress && onProgress();
-
- } , duration, null/* done */, 0, easing.easeInOutSine, null, "zoomInView"/* , info.cancelFun */);
-
-
-
-
- }
-
-
-
- };
- /*
- z
- |
- |
- |
- |
- x <-------| 中心为点云position加boudingbox中心
- /
- /
- y
- */
-
- var lineLen$1 = 2, stemLen = 4, arrowLen = 2, lineDisToStem = 5;
- var opacity = 0.5;
- class Axis extends Object3D {// 坐标轴
- constructor (position) {
- super();
- this.getArrow();
- this.createArrows();
- //this.position.copy(position) 点云的中心点就是在(0,0,0)
- //this.scale.set(2,2,2)
- }
- getArrow(){
- var arrowGroup = new Object3D();
-
-
-
- var line = LineDraw.createLine([new Vector3, new Vector3(0,0,lineLen$1)]);
- var stem = new Mesh(new BoxGeometry(0.3, 0.3, stemLen));
- stem.position.set(0,0,lineLen$1+lineDisToStem+stemLen/2);
- var arrow = new Mesh(new CylinderBufferGeometry( 0, 0.6, arrowLen, 12, 1, false ));//radiusTop = 1, radiusBottom = 1, height = 1, radialSegments = 8, heightSegments = 1, openEnded = false, thetaStart = 0, thetaLength = Math.PI * 2
- arrow.position.set(0,0,lineLen$1+lineDisToStem+stemLen + arrowLen/2);
- arrow.rotation.set(Math.PI/2,0,0);
-
- arrowGroup.add(stem);
- arrowGroup.add(line);
- arrowGroup.add(arrow);
-
- this.arrowGroup = arrowGroup;
-
- }
-
-
-
- createArrows(){
- var material = new MeshBasicMaterial({color:"#00d7df",side:2,transparent:true,opacity:0.8, depthWrite:false});
- let axis = Object.keys(Potree.config.axis);
- axis.forEach((axisText)=>{
- let color = Potree.config.axis[axisText].color;
- var group = this.arrowGroup.clone();
-
- group.children.forEach(e=>{
- e.material = e.material.clone();
- /* e.material.opacity = opacity
- e.material.transparent = true */
- e.material.color.set(color);
- });
-
- var label = this.createLabel(axisText, color);
- label.position.set(0, 0, lineLen$1 + stemLen + arrowLen + lineDisToStem + 3);
- group.add(label);
-
- if(axisText == 'y'){
- group.rotation.x = -Math.PI / 2;
- }else if(axisText == 'x'){
- group.rotation.y = Math.PI / 2;
- }
-
- this.add(group);
- });
-
- }
- createLabel(text,color){
- var canvas = document.createElement("canvas");
- var context = canvas.getContext("2d");
- canvas.width = 256,
- canvas.height = 256;
- var fontSize = 120;
- context.fillStyle = color; //"#00ffee";
- context.font = "normal " + fontSize + "px 微软雅黑";
- var textWidth = context.measureText(text).width;
- context.clearRect(0,0,canvas.width,canvas.height);
- context.fillText(text, (canvas.width - textWidth) / 2 , (canvas.height + fontSize) / 2);
-
- var tex = new Texture(canvas);
- tex.needsUpdate = true;
- tex.minFilter = NearestFilter;//防止边缘发黑
- tex.magFilter = NearestFilter;//防止边缘发黑
- var sprite = new Sprite(new SpriteMaterial({
- map: tex , // depthWrite:false,
-
- }));
-
- sprite.renderOrder = 1;//防止在透明后还是出现白矩形挡住其他mesh
- sprite.scale.set(3,3,3);
- return sprite
-
- }
-
- }
- class Scene$1 extends EventDispatcher{
- constructor(){
- super();
- this.annotations = new Annotation();
-
- this.scene = new Scene();
- //this.sceneBG = new THREE.Scene(); //用来放skybox
- //this.sceneOverlay = new THREE.Scene();
-
- this.scenePointCloud = new Scene();
-
-
- this.cameraP = new PerspectiveCamera(this.fov, 1, Potree.config.view.near, Potree.config.view.near);
- this.cameraO = new OrthographicCamera(-1, 1, 1, -1, Potree.config.view.near, Potree.settings.cameraFar);
- this.cameraP.limitFar = true;//add
-
- this.cameraVR = new PerspectiveCamera();
- this.cameraBG = new Camera();
- this.cameraScreenSpace = new OrthographicCamera(-1, 1, 1, -1, 0.1, 10);
- this.cameraMode = CameraMode.PERSPECTIVE;
- this.overrideCamera = null;
- this.pointclouds = [];
- this.measurements = [];
- this.profiles = [];
- this.volumes = [];
- this.polygonClipVolumes = [];
- this.cameraAnimations = [];
- this.orientedImages = [];
- this.images360 = [];
- this.geopackages = [];
-
- this.fpControls = null;
- this.orbitControls = null;
- this.earthControls = null;
- this.geoControls = null;
- this.deviceControls = null;
- this.inputHandler = null;
- this.view = new View();
- this.directionalLight = null;
- this.initialize();
-
-
- //-------------
- this.axisArrow = new Axis();
- this.scene.add(this.axisArrow);
- if(!Potree.settings.isDebug)this.axisArrow.visible = false;
- viewer.setObjectLayers(this.axisArrow, 'bothMapAndScene' );
-
-
-
- }
- estimateHeightAt (position) {
- let height = null;
- let fromSpacing = Infinity;
- for (let pointcloud of this.pointclouds) {
- if (pointcloud.root.geometryNode === undefined) {
- continue;
- }
- let pHeight = null;
- let pFromSpacing = Infinity;
- let lpos = position.clone().sub(pointcloud.position);
- lpos.z = 0;
- let ray = new Ray(lpos, new Vector3(0, 0, 1));
- let stack = [pointcloud.root];
- while (stack.length > 0) {
- let node = stack.pop();
- let box = node.getBoundingBox();
- let inside = ray.intersectBox(box);
- if (!inside) {
- continue;
- }
- let h = node.geometryNode.mean.z +
- pointcloud.position.z +
- node.geometryNode.boundingBox.min.z;
- if (node.geometryNode.spacing <= pFromSpacing) {
- pHeight = h;
- pFromSpacing = node.geometryNode.spacing;
- }
- for (let index of Object.keys(node.children)) {
- let child = node.children[index];
- if (child.geometryNode) {
- stack.push(node.children[index]);
- }
- }
- }
- if (height === null || pFromSpacing < fromSpacing) {
- height = pHeight;
- fromSpacing = pFromSpacing;
- }
- }
- return height;
- }
-
- getBoundingBox(pointclouds = this.pointclouds){
- let box = new Box3();
- this.scenePointCloud.updateMatrixWorld(true);
- this.referenceFrame.updateMatrixWorld(true);
- for (let pointcloud of pointclouds) {
- pointcloud.updateMatrixWorld(true);
- let pointcloudBox = pointcloud.pcoGeometry.tightBoundingBox ? pointcloud.pcoGeometry.tightBoundingBox : pointcloud.boundingBox;
- let boxWorld = Utils.computeTransformedBoundingBox(pointcloudBox, pointcloud.matrixWorld);
- box.union(boxWorld);
- }
- return box;
- }
- addPointCloud (pointcloud) {
- this.pointclouds.push(pointcloud);
- this.scenePointCloud.add(pointcloud);
- this.dispatchEvent({
- type: 'pointcloud_added',
- pointcloud: pointcloud
- });
- }
- //add:
- removePointCloud (pointcloud) {
- let index = this.pointclouds.indexOf(pointcloud);
- if(index == -1)return
- this.pointclouds.splice(index, 1);
- this.scenePointCloud.remove(pointcloud);
-
- pointcloud.panos.forEach(pano=>{
- pano.dispose();
- });
-
-
- }
-
- addVolume (volume) {
- this.volumes.push(volume);
- this.dispatchEvent({
- 'type': 'volume_added',
- 'scene': this,
- 'volume': volume
- });
- }
- addOrientedImages(images){
- this.orientedImages.push(images);
- this.scene.add(images.node);
- this.dispatchEvent({
- 'type': 'oriented_images_added',
- 'scene': this,
- 'images': images
- });
- };
- removeOrientedImages(images){
- let index = this.orientedImages.indexOf(images);
- if (index > -1) {
- this.orientedImages.splice(index, 1);
- this.dispatchEvent({
- 'type': 'oriented_images_removed',
- 'scene': this,
- 'images': images
- });
- }
- };
- add360Images(images){
- this.images360.push(images);
- this.scene.add(images.node);
- //this.sceneOverlay.add(images.node);//add
-
-
- this.dispatchEvent({
- 'type': '360_images_added',
- 'scene': this,
- 'images': images
- });
- }
- remove360Images(images){
- let index = this.images360.indexOf(images);
- if (index > -1) {
- this.images360.splice(index, 1);
- this.dispatchEvent({
- 'type': '360_images_removed',
- 'scene': this,
- 'images': images
- });
- }
- }
- addGeopackage(geopackage){
- this.geopackages.push(geopackage);
- this.scene.add(geopackage.node);
- this.dispatchEvent({
- 'type': 'geopackage_added',
- 'scene': this,
- 'geopackage': geopackage
- });
- };
- removeGeopackage(geopackage){
- let index = this.geopackages.indexOf(geopackage);
- if (index > -1) {
- this.geopackages.splice(index, 1);
- this.dispatchEvent({
- 'type': 'geopackage_removed',
- 'scene': this,
- 'geopackage': geopackage
- });
- }
- };
- removeVolume (volume) {
- let index = this.volumes.indexOf(volume);
- if (index > -1) {
- this.volumes.splice(index, 1);
- this.dispatchEvent({
- 'type': 'volume_removed',
- 'scene': this,
- 'volume': volume
- });
- }
- };
- addCameraAnimation(animation) {
- this.cameraAnimations.push(animation);
- this.dispatchEvent({
- 'type': 'camera_animation_added',
- 'scene': this,
- 'animation': animation
- });
- };
- removeCameraAnimation(animation){
- let index = this.cameraAnimations.indexOf(animation);
- if (index > -1) {
- this.cameraAnimations.splice(index, 1);
- this.dispatchEvent({
- 'type': 'camera_animation_removed',
- 'scene': this,
- 'animation': animation
- });
- }
- };
- addPolygonClipVolume(volume){
- this.polygonClipVolumes.push(volume);
- this.dispatchEvent({
- "type": "polygon_clip_volume_added",
- "scene": this,
- "volume": volume
- });
- };
-
- removePolygonClipVolume(volume){
- let index = this.polygonClipVolumes.indexOf(volume);
- if (index > -1) {
- this.polygonClipVolumes.splice(index, 1);
- this.dispatchEvent({
- "type": "polygon_clip_volume_removed",
- "scene": this,
- "volume": volume
- });
- }
- };
-
- addMeasurement(measurement){
- measurement.lengthUnit = this.lengthUnit;
- measurement.lengthUnitDisplay = this.lengthUnitDisplay;
- this.measurements.push(measurement);
- this.dispatchEvent({
- 'type': 'measurement_added',
- 'scene': this,
- 'measurement': measurement
- });
- };
- removeMeasurement (measurement) {
- let index = this.measurements.indexOf(measurement);
- if (index > -1) {
- this.measurements.splice(index, 1);
- this.dispatchEvent({
- 'type': 'measurement_removed',
- 'scene': this,
- 'measurement': measurement
- });
- }
- }
- addProfile (profile) {
- this.profiles.push(profile);
- this.dispatchEvent({
- 'type': 'profile_added',
- 'scene': this,
- 'profile': profile
- });
- }
- removeProfile (profile) {
- let index = this.profiles.indexOf(profile);
- if (index > -1) {
- this.profiles.splice(index, 1);
- this.dispatchEvent({
- 'type': 'profile_removed',
- 'scene': this,
- 'profile': profile
- });
- }
- }
- removeAllMeasurements () {
- while (this.measurements.length > 0) {
- this.removeMeasurement(this.measurements[0]);
- }
- while (this.profiles.length > 0) {
- this.removeProfile(this.profiles[0]);
- }
- while (this.volumes.length > 0) {
- this.removeVolume(this.volumes[0]);
- }
- }
- removeAllClipVolumes(){
- let clipVolumes = this.volumes.filter(volume => volume.clip === true);
- for(let clipVolume of clipVolumes){
- this.removeVolume(clipVolume);
- }
- while(this.polygonClipVolumes.length > 0){
- this.removePolygonClipVolume(this.polygonClipVolumes[0]);
- }
- }
- getActiveCamera() {
- return viewer.mainViewport.camera
-
-
-
- if(this.overrideCamera){
- return this.overrideCamera;
- }
- if(this.cameraMode === CameraMode.PERSPECTIVE){
- return this.cameraP;
- }else if(this.cameraMode === CameraMode.ORTHOGRAPHIC){
- return this.cameraO;
- }else if(this.cameraMode === CameraMode.VR){
- return this.cameraVR;
- }
- return null;
- }
-
- initialize(){
-
- this.referenceFrame = new Object3D();
- this.referenceFrame.matrixAutoUpdate = false;
- this.scenePointCloud.add(this.referenceFrame);
- if(window.axisYup){
-
- }else {
- this.cameraP.up.set(0, 0, 1);
- this.cameraO.up.set(0, 0, 1);
-
- }
- this.cameraP.position.set(1000, 1000, 1000);
- this.cameraO.position.set(1000, 1000, 1000);
- //this.camera.rotation.y = -Math.PI / 4;
- //this.camera.rotation.x = -Math.PI / 6;
- this.cameraScreenSpace.lookAt(new Vector3(0, 0, 0), new Vector3(0, 0, -1), new Vector3(0, 1, 0));
-
- this.directionalLight = new DirectionalLight( 0xffffff, 0.5 );
- this.directionalLight.position.set( 10, 10, 10 );
- this.directionalLight.lookAt( new Vector3(0, 0, 0));
- this.scenePointCloud.add( this.directionalLight );
-
- let light = new AmbientLight( 0x555555 ); // soft white light
- this.scenePointCloud.add( light );
-
-
-
- //add:------给空间模型的box 或其他obj------
- /* let light2 = new THREE.AmbientLight( 16777215, 1 );
- viewer.setObjectLayers(light2, 'bothMapAndScene')
- this.scene.add(light2)
- let light3 = new THREE.DirectionalLight( 16777215, 1);
- light3.position.set( 10, 10, 10 );
- light3.lookAt( new THREE.Vector3(0, 0, 0));
- viewer.setObjectLayers(light3, 'bothMapAndScene')
- this.scene.add(light3) */
- //--------------------------------------------
- { // background
- let texture = Utils.createBackgroundTexture(512, 512);
- texture.minFilter = texture.magFilter = NearestFilter;
- texture.minFilter = texture.magFilter = LinearFilter;
- let bg = new Mesh(
- new PlaneBufferGeometry(2, 2, 1),
- new MeshBasicMaterial({
- map: texture
- })
- );
- bg.material.depthTest = false;
- bg.material.depthWrite = false;
- bg.name = 'bg';
- //this.sceneBG.add(bg);
- this.scene.add(bg);
- bg.layers.set(Potree.config.renderLayers.bg);
-
- }
- { // background color
-
- let bg2 = new Mesh(
- new PlaneBufferGeometry(2, 2, 1),
- new MeshBasicMaterial({
- transparent : true
- })
- );
- bg2.material.depthTest = false;
- bg2.material.depthWrite = false;
- bg2.name = 'bg2';
- this.scene.add(bg2);
- bg2.layers.set(Potree.config.renderLayers.bg2);
- this.bg2 = bg2;
- }
- // { // lights
- // {
- // let light = new THREE.DirectionalLight(0xffffff);
- // light.position.set(10, 10, 1);
- // light.target.position.set(0, 0, 0);
- // this.scene.add(light);
- // }
- // {
- // let light = new THREE.DirectionalLight(0xffffff);
- // light.position.set(-10, 10, 1);
- // light.target.position.set(0, 0, 0);
- // this.scene.add(light);
- // }
- // {
- // let light = new THREE.DirectionalLight(0xffffff);
- // light.position.set(0, -10, 20);
- // light.target.position.set(0, 0, 0);
- // this.scene.add(light);
- // }
- // }
- }
-
- /* switchBg(type){//add
- let bg,;
- if(type == 'gradient'){
- bg
- }else if(type == 'overlayColor'){
-
- }
- } */
-
-
- addAnnotation(position, args = {}){
- if(position instanceof Array){
- args.position = new Vector3().fromArray(position);
- } else if (position.x != null) {
- args.position = position;
- }
- let annotation = new Annotation(args);
- this.annotations.add(annotation);
- return annotation;
- }
- getAnnotations () {
- return this.annotations;
- };
- removeAnnotation(annotationToRemove) {
- this.annotations.remove(annotationToRemove);
- }
- };
- // http://epsg.io/
- //see http://openlayers.org/
- proj4.defs([
- ['UTM10N', '+proj=utm +zone=10 +ellps=GRS80 +datum=NAD83 +units=m +no_defs'],
- ['EPSG:6339', '+proj=utm +zone=10 +ellps=GRS80 +units=m +no_defs'],
- ['EPSG:6340', '+proj=utm +zone=11 +ellps=GRS80 +units=m +no_defs'],
- ['EPSG:6341', '+proj=utm +zone=12 +ellps=GRS80 +units=m +no_defs'],
- ['EPSG:6342', '+proj=utm +zone=13 +ellps=GRS80 +units=m +no_defs'],
- ['EPSG:6343', '+proj=utm +zone=14 +ellps=GRS80 +units=m +no_defs'],
- ['EPSG:6344', '+proj=utm +zone=15 +ellps=GRS80 +units=m +no_defs'],
- ['EPSG:6345', '+proj=utm +zone=16 +ellps=GRS80 +units=m +no_defs'],
- ['EPSG:6346', '+proj=utm +zone=17 +ellps=GRS80 +units=m +no_defs'],
- ['EPSG:6347', '+proj=utm +zone=18 +ellps=GRS80 +units=m +no_defs'],
- ['EPSG:6348', '+proj=utm +zone=19 +ellps=GRS80 +units=m +no_defs'],
- ['EPSG:26910', '+proj=utm +zone=10 +ellps=GRS80 +towgs84=0,0,0,0,0,0,0 +units=m +no_defs '],
- ['EPSG:26911', '+proj=utm +zone=11 +ellps=GRS80 +towgs84=0,0,0,0,0,0,0 +units=m +no_defs '],
- ['EPSG:26912', '+proj=utm +zone=12 +ellps=GRS80 +towgs84=0,0,0,0,0,0,0 +units=m +no_defs '],
- ['EPSG:26913', '+proj=utm +zone=13 +ellps=GRS80 +towgs84=0,0,0,0,0,0,0 +units=m +no_defs '],
- ['EPSG:26914', '+proj=utm +zone=14 +ellps=GRS80 +towgs84=0,0,0,0,0,0,0 +units=m +no_defs '],
- ['EPSG:26915', '+proj=utm +zone=15 +ellps=GRS80 +towgs84=0,0,0,0,0,0,0 +units=m +no_defs '],
- ['EPSG:26916', '+proj=utm +zone=16 +ellps=GRS80 +towgs84=0,0,0,0,0,0,0 +units=m +no_defs '],
- ['EPSG:26917', '+proj=utm +zone=17 +ellps=GRS80 +towgs84=0,0,0,0,0,0,0 +units=m +no_defs '],
- ['EPSG:26918', '+proj=utm +zone=18 +ellps=GRS80 +towgs84=0,0,0,0,0,0,0 +units=m +no_defs '],
- ['EPSG:26919', '+proj=utm +zone=19 +ellps=GRS80 +towgs84=0,0,0,0,0,0,0 +units=m +no_defs '],
- ]);
- class MapView{
- constructor (viewer) {
- this.viewer = viewer;
- this.webMapService = 'WMTS';
- this.mapProjectionName = 'EPSG:3857'; //navvis也是这个
- this.mapProjection = proj4.defs(this.mapProjectionName);
- this.sceneProjection = null;
- this.extentsLayer = null;
- this.cameraLayer = null;
- this.toolLayer = null;
- this.sourcesLayer = null;
- this.sourcesLabelLayer = null;
- this.images360Layer = null;
- this.enabled = false;
- this.createAnnotationStyle = (text) => {
- return [
- new ol.style.Style({
- image: new ol.style.Circle({
- radius: 10,
- stroke: new ol.style.Stroke({
- color: [255, 255, 255, 0.5],
- width: 2
- }),
- fill: new ol.style.Fill({
- color: [0, 0, 0, 0.5]
- })
- })
- })
- ];
- };
- this.createLabelStyle = (text) => {
- let style = new ol.style.Style({
- image: new ol.style.Circle({
- radius: 6,
- stroke: new ol.style.Stroke({
- color: 'white',
- width: 2
- }),
- fill: new ol.style.Fill({
- color: 'green'
- })
- }),
- text: new ol.style.Text({
- font: '12px helvetica,sans-serif',
- text: text,
- fill: new ol.style.Fill({
- color: '#000'
- }),
- stroke: new ol.style.Stroke({
- color: '#fff',
- width: 2
- })
- })
- });
- return style;
- };
- }
- showSources (show) {
- this.sourcesLayer.setVisible(show);
- this.sourcesLabelLayer.setVisible(show);
- }
- init () {
- if(typeof ol === "undefined"){
- return;
- }
- this.elMap = $('#potree_map');
- this.elMap.draggable({ handle: $('#potree_map_header') });
- this.elMap.resizable();
- this.elTooltip = $(`<div style="position: relative; z-index: 100"></div>`);
- this.elMap.append(this.elTooltip);
- let extentsLayer = this.getExtentsLayer();
- let cameraLayer = this.getCameraLayer();
- this.getToolLayer();
- let sourcesLayer = this.getSourcesLayer();
- this.images360Layer = this.getImages360Layer();
- this.getSourcesLabelLayer();
- this.getAnnotationsLayer();
- let mousePositionControl = new ol.control.MousePosition({
- coordinateFormat: ol.coordinate.createStringXY(5),
- projection: 'EPSG:4326',
- undefinedHTML: ' '
- });
- let _this = this;
- let DownloadSelectionControl = function (optOptions) {
- let options = optOptions || {};
- // TOGGLE TILES
- let btToggleTiles = document.createElement('button');
- btToggleTiles.innerHTML = 'T';
- btToggleTiles.addEventListener('click', () => {
- let visible = sourcesLayer.getVisible();
- _this.showSources(!visible);
- }, false);
- btToggleTiles.style.float = 'left';
- btToggleTiles.title = 'show / hide tiles';
- // DOWNLOAD SELECTED TILES
- let link = document.createElement('a');
- link.href = '#';
- link.download = 'list.txt';
- link.style.float = 'left';
- let button = document.createElement('button');
- button.innerHTML = 'D';
- link.appendChild(button);
- let handleDownload = (e) => {
- let features = selectedFeatures.getArray();
- let url = [document.location.protocol, '//', document.location.host, document.location.pathname].join('');
- if (features.length === 0) {
- alert('No tiles were selected. Select area with ctrl + left mouse button!');
- e.preventDefault();
- e.stopImmediatePropagation();
- return false;
- } else if (features.length === 1) {
- let feature = features[0];
- if (feature.source) {
- let cloudjsurl = feature.pointcloud.pcoGeometry.url;
- let sourceurl = new URL(url + '/../' + cloudjsurl + '/../source/' + feature.source.name);
- link.href = sourceurl.href;
- link.download = feature.source.name;
- }
- } else {
- let content = '';
- for (let i = 0; i < features.length; i++) {
- let feature = features[i];
- if (feature.source) {
- let cloudjsurl = feature.pointcloud.pcoGeometry.url;
- let sourceurl = new URL(url + '/../' + cloudjsurl + '/../source/' + feature.source.name);
- content += sourceurl.href + '\n';
- }
- }
- let uri = 'data:application/octet-stream;base64,' + btoa(content);
- link.href = uri;
- link.download = 'list_of_files.txt';
- }
- };
- button.addEventListener('click', handleDownload, false);
- // assemble container
- let element = document.createElement('div');
- element.className = 'ol-unselectable ol-control';
- element.appendChild(link);
- element.appendChild(btToggleTiles);
- element.style.bottom = '0.5em';
- element.style.left = '0.5em';
- element.title = 'Download file or list of selected tiles. Select tile with left mouse button or area using ctrl + left mouse.';
- ol.control.Control.call(this, {
- element: element,
- target: options.target
- });
- };
- ol.inherits(DownloadSelectionControl, ol.control.Control);
- this.map = new ol.Map({
- controls: ol.control.defaults({
- attributionOptions: ({
- collapsible: false
- })
- }).extend([
- // this.controls.zoomToExtent,
- new DownloadSelectionControl(),
- mousePositionControl
- ]),
- layers: [
- new ol.layer.Tile({source: new ol.source.OSM()}),
- this.toolLayer,
- this.annotationsLayer,
- this.sourcesLayer,
- this.sourcesLabelLayer,
- this.images360Layer,
- extentsLayer,
- cameraLayer
- ],
- target: 'potree_map_content',
- view: new ol.View({
- center: this.olCenter,
- zoom: 9
- })
- });
- // DRAGBOX / SELECTION
- this.dragBoxLayer = new ol.layer.Vector({
- source: new ol.source.Vector({}),
- style: new ol.style.Style({
- stroke: new ol.style.Stroke({
- color: 'rgba(0, 0, 255, 1)',
- width: 2
- })
- })
- });
- this.map.addLayer(this.dragBoxLayer);
- let select = new ol.interaction.Select();
- this.map.addInteraction(select);
- let selectedFeatures = select.getFeatures();
- let dragBox = new ol.interaction.DragBox({
- condition: ol.events.condition.platformModifierKeyOnly
- });
- this.map.addInteraction(dragBox);
- // this.map.on('pointermove', evt => {
- // let pixel = evt.pixel;
- // let feature = this.map.forEachFeatureAtPixel(pixel, function (feature) {
- // return feature;
- // });
- // // console.log(feature);
- // // this.elTooltip.css("display", feature ? '' : 'none');
- // this.elTooltip.css('display', 'none');
- // if (feature && feature.onHover) {
- // feature.onHover(evt);
- // // overlay.setPosition(evt.coordinate);
- // // tooltip.innerHTML = feature.get('name');
- // }
- // });
- this.map.on('click', evt => {
- let pixel = evt.pixel;
- let feature = this.map.forEachFeatureAtPixel(pixel, function (feature) {
- return feature;
- });
- if (feature && feature.onClick) {
- feature.onClick(evt);
- }
- });
- dragBox.on('boxend', (e) => {
- // features that intersect the box are added to the collection of
- // selected features, and their names are displayed in the "info"
- // div
- let extent = dragBox.getGeometry().getExtent();
- this.getSourcesLayer().getSource().forEachFeatureIntersectingExtent(extent, (feature) => {
- selectedFeatures.push(feature);
- });
- });
- // clear selection when drawing a new box and when clicking on the map
- dragBox.on('boxstart', (e) => {
- selectedFeatures.clear();
- });
- this.map.on('click', () => {
- selectedFeatures.clear();
- });
- this.viewer.addEventListener('scene_changed', e => {
- this.setScene(e.scene);
- });
- this.onPointcloudAdded = e => {
- this.load(e.pointcloud);
- };
- this.on360ImagesAdded = e => {
- this.addImages360(e.images);
- };
- this.onAnnotationAdded = e => {
- if (!this.sceneProjection) {
- return;
- }
- let annotation = e.annotation;
- let position = annotation.position;
- let mapPos = this.toMap.forward([position.x, position.y]);
- let feature = new ol.Feature({
- geometry: new ol.geom.Point(mapPos),
- name: annotation.title
- });
- feature.setStyle(this.createAnnotationStyle(annotation.title));
- feature.onHover = evt => {
- let coordinates = feature.getGeometry().getCoordinates();
- let p = this.map.getPixelFromCoordinate(coordinates);
- this.elTooltip.html(annotation.title);
- this.elTooltip.css('display', '');
- this.elTooltip.css('left', `${p[0]}px`);
- this.elTooltip.css('top', `${p[1]}px`);
- };
- feature.onClick = evt => {
- annotation.clickTitle();
- };
- this.getAnnotationsLayer().getSource().addFeature(feature);
- };
- this.setScene(this.viewer.scene);
- }
- setScene (scene) {
- if (this.scene === scene) {
- return;
- };
- if (this.scene) {
- this.scene.removeEventListener('pointcloud_added', this.onPointcloudAdded);
- this.scene.removeEventListener('360_images_added', this.on360ImagesAdded);
- this.scene.annotations.removeEventListener('annotation_added', this.onAnnotationAdded);
- }
- this.scene = scene;
- this.scene.addEventListener('pointcloud_added', this.onPointcloudAdded);
- this.scene.addEventListener('360_images_added', this.on360ImagesAdded);
- this.scene.annotations.addEventListener('annotation_added', this.onAnnotationAdded);
- for (let pointcloud of this.viewer.scene.pointclouds) {
- this.load(pointcloud);
- }
- this.viewer.scene.annotations.traverseDescendants(annotation => {
- this.onAnnotationAdded({annotation: annotation});
- });
- for(let images of this.viewer.scene.images360){
- this.on360ImagesAdded({images: images});
- }
- }
- getExtentsLayer () {
- if (this.extentsLayer) {
- return this.extentsLayer;
- }
- this.gExtent = new ol.geom.LineString([[0, 0], [0, 0]]);
- let feature = new ol.Feature(this.gExtent);
- let featureVector = new ol.source.Vector({
- features: [feature]
- });
- this.extentsLayer = new ol.layer.Vector({
- source: featureVector,
- style: new ol.style.Style({
- fill: new ol.style.Fill({
- color: 'rgba(255, 255, 255, 0.2)'
- }),
- stroke: new ol.style.Stroke({
- color: '#0000ff',
- width: 2
- }),
- image: new ol.style.Circle({
- radius: 3,
- fill: new ol.style.Fill({
- color: '#0000ff'
- })
- })
- })
- });
- return this.extentsLayer;
- }
- getAnnotationsLayer () {
- if (this.annotationsLayer) {
- return this.annotationsLayer;
- }
- this.annotationsLayer = new ol.layer.Vector({
- source: new ol.source.Vector({
- }),
- style: new ol.style.Style({
- fill: new ol.style.Fill({
- color: 'rgba(255, 0, 0, 1)'
- }),
- stroke: new ol.style.Stroke({
- color: 'rgba(255, 0, 0, 1)',
- width: 2
- })
- })
- });
- return this.annotationsLayer;
- }
- getCameraLayer () {
- if (this.cameraLayer) {
- return this.cameraLayer;
- }
- // CAMERA LAYER
- this.gCamera = new ol.geom.LineString([[0, 0], [0, 0], [0, 0], [0, 0]]);
- let feature = new ol.Feature(this.gCamera);
- let featureVector = new ol.source.Vector({
- features: [feature]
- });
- this.cameraLayer = new ol.layer.Vector({
- source: featureVector,
- style: new ol.style.Style({
- stroke: new ol.style.Stroke({
- color: '#0000ff',
- width: 2
- })
- })
- });
- return this.cameraLayer;
- }
- getToolLayer () {
- if (this.toolLayer) {
- return this.toolLayer;
- }
- this.toolLayer = new ol.layer.Vector({
- source: new ol.source.Vector({
- }),
- style: new ol.style.Style({
- fill: new ol.style.Fill({
- color: 'rgba(255, 0, 0, 1)'
- }),
- stroke: new ol.style.Stroke({
- color: 'rgba(255, 0, 0, 1)',
- width: 2
- })
- })
- });
- return this.toolLayer;
- }
- getImages360Layer(){
- if(this.images360Layer){
- return this.images360Layer;
- }
- let style = new ol.style.Style({
- image: new ol.style.Circle({
- radius: 4,
- stroke: new ol.style.Stroke({
- color: [255, 0, 0, 1],
- width: 2
- }),
- fill: new ol.style.Fill({
- color: [255, 100, 100, 1]
- })
- })
- });
-
- let layer = new ol.layer.Vector({
- source: new ol.source.Vector({}),
- style: style,
- });
- this.images360Layer = layer;
- return this.images360Layer;
- }
- getSourcesLayer () {
- if (this.sourcesLayer) {
- return this.sourcesLayer;
- }
- this.sourcesLayer = new ol.layer.Vector({
- source: new ol.source.Vector({}),
- style: new ol.style.Style({
- fill: new ol.style.Fill({
- color: 'rgba(0, 0, 150, 0.1)'
- }),
- stroke: new ol.style.Stroke({
- color: 'rgba(0, 0, 150, 1)',
- width: 1
- })
- })
- });
- return this.sourcesLayer;
- }
- getSourcesLabelLayer () {
- if (this.sourcesLabelLayer) {
- return this.sourcesLabelLayer;
- }
- this.sourcesLabelLayer = new ol.layer.Vector({
- source: new ol.source.Vector({
- }),
- style: new ol.style.Style({
- fill: new ol.style.Fill({
- color: 'rgba(255, 0, 0, 0.1)'
- }),
- stroke: new ol.style.Stroke({
- color: 'rgba(255, 0, 0, 1)',
- width: 2
- })
- }),
- minResolution: 0.01,
- maxResolution: 20
- });
- return this.sourcesLabelLayer;
- }
- setSceneProjection (sceneProjection) {
- this.sceneProjection = sceneProjection;
- this.toMap = proj4(this.sceneProjection, this.mapProjection);
- this.toScene = proj4(this.mapProjection, this.sceneProjection);
- };
- getMapExtent () {
- let bb = this.viewer.getBoundingBox();
- let bottomLeft = this.toMap.forward([bb.min.x, bb.min.y]);
- let bottomRight = this.toMap.forward([bb.max.x, bb.min.y]);
- let topRight = this.toMap.forward([bb.max.x, bb.max.y]);
- let topLeft = this.toMap.forward([bb.min.x, bb.max.y]);
- let extent = {
- bottomLeft: bottomLeft,
- bottomRight: bottomRight,
- topRight: topRight,
- topLeft: topLeft
- };
- return extent;
- };
- getMapCenter () {
- let mapExtent = this.getMapExtent();
- let mapCenter = [
- (mapExtent.bottomLeft[0] + mapExtent.topRight[0]) / 2,
- (mapExtent.bottomLeft[1] + mapExtent.topRight[1]) / 2
- ];
- return mapCenter;
- };
- updateToolDrawings () {
- this.toolLayer.getSource().clear();
- let profiles = this.viewer.profileTool.profiles;
- for (let i = 0; i < profiles.length; i++) {
- let profile = profiles[i];
- let coordinates = [];
- for (let j = 0; j < profile.points.length; j++) {
- let point = profile.points[j];
- let pointMap = this.toMap.forward([point.x, point.y]);
- coordinates.push(pointMap);
- }
- let line = new ol.geom.LineString(coordinates);
- let feature = new ol.Feature(line);
- this.toolLayer.getSource().addFeature(feature);
- }
- let measurements = this.viewer.measuringTool.measurements;
- for (let i = 0; i < measurements.length; i++) {
- let measurement = measurements[i];
- let coordinates = [];
- for (let j = 0; j < measurement.points.length; j++) {
- let point = measurement.points[j].position;
- let pointMap = this.toMap.forward([point.x, point.y]);
- coordinates.push(pointMap);
- }
- if (measurement.closed && measurement.points.length > 0) {
- coordinates.push(coordinates[0]);
- }
- let line = new ol.geom.LineString(coordinates);
- let feature = new ol.Feature(line);
- this.toolLayer.getSource().addFeature(feature);
- }
- }
- addImages360(images){
- let transform = this.toMap.forward;
- let layer = this.getImages360Layer();
- for(let pano of images.panos){
- let p = transform([pano.position.x, pano.position.y]);
- let feature = new ol.Feature({
- 'geometry': new ol.geom.Point(p),
- });
- feature.onClick = () => {
- //images.focus(pano);
-
- images.flyToPano({
- pano
- });
-
- };
- layer.getSource().addFeature(feature);
- }
- }
- async load (pointcloud) {
- if (!pointcloud) {
- return;
- }
- /* if (!pointcloud.projection) {
- return;
- }
- */
- if (!this.sceneProjection) {
- try {
- this.setSceneProjection(proj4.defs("NAVVIS:TMERC") || pointcloud.projection);
- }catch (e) {
- console.log('Failed projection:', e);
- if (pointcloud.fallbackProjection) {
- try {
- console.log('Trying fallback projection...');
- this.setSceneProjection(pointcloud.fallbackProjection);
- console.log('Set projection from fallback');
- }catch (e) {
- console.log('Failed fallback projection:', e);
- return;
- }
- }else {
- return;
- };
- }
- }
- let mapExtent = this.getMapExtent();
- let mapCenter = this.getMapCenter();
- let view = this.map.getView();
- view.setCenter(mapCenter);
- this.gExtent.setCoordinates([
- mapExtent.bottomLeft,
- mapExtent.bottomRight,
- mapExtent.topRight,
- mapExtent.topLeft,
- mapExtent.bottomLeft
- ]);
- view.fit(this.gExtent, [300, 300], {
- constrainResolution: false
- });
- if (pointcloud.pcoGeometry.type == 'ept'){
- return;
- }
- let url = `${pointcloud.pcoGeometry.url}/../sources.json`;
- //let response = await fetch(url);
- fetch(url).then(async (response) => {
- let data = await response.json();
-
- let sources = data.sources;
- for (let i = 0; i < sources.length; i++) {
- let source = sources[i];
- let name = source.name;
- let bounds = source.bounds;
- let mapBounds = {
- min: this.toMap.forward([bounds.min[0], bounds.min[1]]),
- max: this.toMap.forward([bounds.max[0], bounds.max[1]])
- };
- let mapCenter = [
- (mapBounds.min[0] + mapBounds.max[0]) / 2,
- (mapBounds.min[1] + mapBounds.max[1]) / 2
- ];
- let p1 = this.toMap.forward([bounds.min[0], bounds.min[1]]);
- let p2 = this.toMap.forward([bounds.max[0], bounds.min[1]]);
- let p3 = this.toMap.forward([bounds.max[0], bounds.max[1]]);
- let p4 = this.toMap.forward([bounds.min[0], bounds.max[1]]);
- // let feature = new ol.Feature({
- // 'geometry': new ol.geom.LineString([p1, p2, p3, p4, p1])
- // });
- let feature = new ol.Feature({
- 'geometry': new ol.geom.Polygon([[p1, p2, p3, p4, p1]])
- });
- feature.source = source;
- feature.pointcloud = pointcloud;
- this.getSourcesLayer().getSource().addFeature(feature);
- feature = new ol.Feature({
- geometry: new ol.geom.Point(mapCenter),
- name: name
- });
- feature.setStyle(this.createLabelStyle(name));
- this.sourcesLabelLayer.getSource().addFeature(feature);
- }
- }).catch(() => {
-
- });
- }
- toggle () {
- if (this.elMap.is(':visible')) {
- this.elMap.css('display', 'none');
- this.enabled = false;
- } else {
- this.elMap.css('display', 'block');
- this.enabled = true;
- }
- }
- update (delta) {
- if (!this.sceneProjection) {
- return;
- }
- let pm = $('#potree_map');
- if (!this.enabled) {
- return;
- }
- // resize
- let mapSize = this.map.getSize();
- let resized = (pm.width() !== mapSize[0] || pm.height() !== mapSize[1]);
- if (resized) {
- this.map.updateSize();
- }
- //
- let camera = this.viewer.scene.getActiveCamera();
- let scale = this.map.getView().getResolution();
- let campos = camera.position;
- let camdir = camera.getWorldDirection(new Vector3());
- let sceneLookAt = camdir.clone().multiplyScalar(30 * scale).add(campos);
- let geoPos = camera.position;
- let geoLookAt = sceneLookAt;
- let mapPos = new Vector2$1().fromArray(this.toMap.forward([geoPos.x, geoPos.y]));
- let mapLookAt = new Vector2$1().fromArray(this.toMap.forward([geoLookAt.x, geoLookAt.y]));
- let mapDir = new Vector2$1().subVectors(mapLookAt, mapPos).normalize();
- mapLookAt = mapPos.clone().add(mapDir.clone().multiplyScalar(30 * scale));
- let mapLength = mapPos.distanceTo(mapLookAt);
- let mapSide = new Vector2$1(-mapDir.y, mapDir.x);
- let p1 = mapPos.toArray();
- let p2 = mapLookAt.clone().sub(mapSide.clone().multiplyScalar(0.3 * mapLength)).toArray();
- let p3 = mapLookAt.clone().add(mapSide.clone().multiplyScalar(0.3 * mapLength)).toArray();
- this.gCamera.setCoordinates([p1, p2, p3, p1]);
- }
- get sourcesVisible () {
- return this.getSourcesLayer().getVisible();
- }
- set sourcesVisible (value) {
- this.getSourcesLayer().setVisible(value);
- }
- }
- let texLoader$1 = new TextureLoader();
- texLoader$1.crossOrigin = "anonymous";
- let createErrorMaterial = function() {
- var t = new MeshBasicMaterial({
- transparent: !0,
- depthWrite: !1,
- depthTest: !0,
- opacity: 1,
- side: DoubleSide
- });
- return t.color = new Color(3355443),
- t
- };
- let tempVector = new Vector3, //sharedata
- face1 = new Face3(0,1,2),
- face2 = new Face3(2,3,0),
- errorMaterial = createErrorMaterial(),
- uv00 = new Vector2$1(0,0),
- uv01 = new Vector2$1(0,1),
- uv10 = new Vector2$1(1,0),
- uv11 = new Vector2$1(1,1),
- face1UV = [uv00, uv10, uv11],
- face2UV = [uv11, uv01, uv00];
- const HALF_WORLD_SIZE = 21e6;
- const MAX_VERTICAL_DIST = 2;
- const MAX_VERTICAL_DIST_TO_BEST = 1;
-
- //高德坐标拾取工具 : https://lbs.amap.com/tools/picker
- class MapLayer extends EventDispatcher{ // 包括了 MapLayerBase SceneLayer
- constructor(viewer_, viewport){
- super();
- this.sceneGroup = new Object3D;
- this.sceneGroup.name = "MapLayer";
-
-
- this.loadingInProgress = 0;
- this.maps = [];
- this.frustum = new Frustum;
- this.frustumMatrix = new Matrix4;
- this.tileColor = /* i && i.tileColor ? i.tileColor : */new Color(16777215);
- this.viewport = viewport;
- this.changeViewer(viewer_);
- //添加地图
- var map = new TiledMapOpenStreetMap(this, this.tileColor );
- this.addMap(map);
-
- //map.setEnable(false)
-
-
-
-
- /* this.on('needUpdate',()=>{
- this.mapLayer.update()
- })
- */
- }
-
- addMapEntity(data, datasetId){
-
- if(!data || !data[0]){
- Potree.Log('平面图无数据','red');
- return
- }
-
- var floorplan = new TiledMapFromEntity(this, this.tileColor, data[0] );//[0]?
- if(floorplan){
- floorplan.name += "_"+ datasetId;
- this.addMap(floorplan);
- floorplan.updateProjection();
- floorplan.updateObjectGroup();
-
- let visible = false;
- if(datasetId in Potree.settings.floorplanEnables){
- visible = Potree.settings.floorplanEnables[datasetId];
- }else {
- visible = Potree.settings.floorplanEnable;
- }
- if(visible){
- this.needUpdate = true;
- }else {
- floorplan.setEnable(false);
- }
-
- this.dispatchEvent({type:'floorplanLoaded', floorplan});
- }
- return floorplan
- }
-
-
- getFloorplan(datasetId){
- return this.maps.find(e=>e.name == 'floorplan'+"_"+ datasetId )
- }
-
-
- addMap(t){
- this.maps.push(t);
- //this.view.invalidateScene()
- this.needUpdate = true;
- }
-
- removeMap(t){
- var e = this.maps.indexOf(t);
- if(e >= 0){
- t.removeFromSceneGroup(this.sceneGroup);
- this.maps.splice(e, 1);
- }
-
- /* this.view.invalidateScene() */
- this.needUpdate = true;
- this.viewer.dispatchEvent({
- type:'content_changed'
- });
- }
-
-
-
-
- changeViewer(viewer_){//add
- this.viewer = viewer_;
- }
-
- initProjection(){
- this.maps.forEach(map=>{
- map.updateProjection();
- map.updateObjectGroup();
- });
- }
-
- visibilityChanged(){
- if (!this.visible)
- for (var t = 0, e = this.maps; t < e.length; t++){
- e[t].removeFromSceneGroup(this.sceneGroup);
- }
- }
-
- onAfterRenderViewport(e){
- var n = this;
-
- /* this.isVisibleInViewport(e) && (this.updateTimer || this.loadingInProgress || (this.updateTimer = window.setTimeout((function(){
- n.update(e).then((function (t){
- t && n.loadComplete.emit(!0)
- }
- )).catch(u.handleWarning)
- }
- ), 100))) */
- }
-
-
-
- update(){
- this.needUpdate = false;
- if(this.disabled || !this.maps.find(e=>!e.disabled) || !this.maps.find(e=>e.objectGroup.visible) )return //add
-
-
- var e, n, i, r, o;
-
- this.updateTimer = void 0,
- e = this.viewport.camera,
- n = e.projectionMatrix.clone(),
-
- n.elements[0] /= 1.5,
- n.elements[5] /= 1.5,
- this.frustumMatrix.multiplyMatrices(n, e.matrixWorldInverse),
- this.frustum.setFromProjectionMatrix(this.frustumMatrix),
- this.frustum.planes[4].setComponents(0, 0, 0, 0),
- this.frustum.planes[5].setComponents(0, 0, 0, 0),
- i = !0;
-
-
- for (r = 0; r < this.maps.length; r++){
- var map = this.maps[r];
- i = map.update(this.frustum, this.sceneGroup) && i;
- }
-
- return [2, i]
-
-
- }
-
- getAttributions(){
- for (var t = {}, e = 0, n = this.maps; e < n.length; e++){
- n[e].fillAttributions(t);
- }
- return t
- }
-
- updateProjection(){
- for (var t = 0, e = this.maps; t < e.length; t++){
- var n = e[t];
- n.clearProjection(),
- n.updateObjectGroup();
- }
- }
- }
-
-
-
-
-
-
-
-
-
-
-
- class TiledMapBase extends EventDispatcher{
- constructor(/* t, */name, mapLayer, tileColor, projection){
- super();
- this.name = name;
- //this.TransformService = t,
- this.mapLayer = mapLayer,
- this.tileColor = tileColor,
- this.bias = 0;
- this.zIndex = -1;
- this.objectGroup = new Object3D;
- this.objectGroup.name = name;
- this.objectGroupAdded = !1,
- this.baseTile = new MapTile(this,/* this.mapLayer, */this.objectGroup,this.tileColor),
- this.isTileVisibleBox = new Box3,
- this.isTileVisibleVec = new Vector3;
-
-
- this.projection = projection;
- this._zoomLevel = 0;//1-20
-
- }
-
- get zoomLevel(){
- return this._zoomLevel
- }
- set zoomLevel(zoomLevel){
- if(this._zoomLevel != zoomLevel){
- this._zoomLevel = zoomLevel;
- //this.dispatchEvent('zoomLevelChange',zoomLevel)
-
- //if(this.name == 'map')console.log(zoomLevel,viewer.mapViewer.camera.zoom)
- }
- }
-
-
- updateObjectGroup(){
- this.position && this.objectGroup.position.copy(this.position),
- this.quaternion && this.objectGroup.quaternion.copy(this.quaternion),
- this.objectGroup.updateMatrixWorld(!0);
- }
-
- updateProjection(){
- //this.transformMapToLocal || (this.transformMapToLocal = this.TransformService.getTransform(this.projection, this.TransformService.crsLocal))
- if(!this.transformMapToLocal){
- if(proj4.defs("NAVVIS:TMERC")){
- if(this.projection == "EPSG:4550"){
- this.transformMapToLocal = {
- forward:(e)=>{
- var a = viewer.transform.lonlatTo4550.inverse(e);
- return viewer.transform.lonlatToLocal.forward(a)
- },
- };
-
-
- }else {
- this.transformMapToLocal = proj4(this.projection, "NAVVIS:TMERC");
- }
- //this.transformMapToLocal = proj4(this.projection, "NAVVIS:TMERC")
-
-
- }
- }
-
- }
-
- setEnable(enable){//add
- if(!this.disabled == enable)return
- if(enable){
- console.log('setEnable',true);
- }
- this.disabled = !enable;
-
- viewer.updateVisible(this.objectGroup, 'setEnable', enable);
-
- if(!enable){
- this.baseTile.remove();
- }else {
- this.mapLayer.needUpdate = true;
- }
-
- this.mapLayer.viewer.dispatchEvent({
- type:'content_changed'
- });
-
-
- }
-
-
-
- /* clearProjection(){
- this.transformMapToLocal = void 0,
- this.projection.name !== this.TransformService.NAVVIS_LOCAL && this.baseTile.remove()
- } */
-
- update(e, n){
-
- var unavailable = (this.disabled || !this.objectGroup.visible);//地图即使不显示也要获得zoomlevel
- if(this.name != 'map' && unavailable)return
-
- this.updateProjection();
-
- if(!this.transformMapToLocal)return
-
- if (!this.isTileVisible(new Vector3(0,0,0), this.mapSizeM, e))
- return this.removeFromSceneGroup(n), !0;
-
- let viewport = this.mapLayer.viewport;
-
-
- var i = new Vector3(-.5 * this.mapSizeM,0,0);
- i.applyMatrix4(this.objectGroup.matrixWorld),
- i.project(viewport.camera);
- var o = new Vector3(.5 * this.mapSizeM,0,0);
- o.applyMatrix4(this.objectGroup.matrixWorld),
- o.project(viewport.camera);
- var a = viewport.resolution.x
- , s = viewport.resolution.y;
- if (a <= 0 || s <= 0 || isNaN(i.x) || isNaN(o.x)) return !1;
- i.sub(o),
- i.x *= a / 2,
- i.y *= s / 2;
-
- var c = this.tileSizePx / i.length()
- , level = Math.ceil(-Math.log(c) / Math.log(2) - this.bias);
- level = Math.max(level, 0);
- level = Math.min(level, void 0 === this.maxDepth ? 1 / 0 : this.maxDepth);
- this.zoomLevel = level;//add
- //console.log(level)
- if(!unavailable){
- this.addToSceneGroup(n);
- return this.baseTile.update(this, e, level, this.mapSizeM, 0, 0, "")
- }
- }
-
-
-
-
- isTileVisible(e, n, i){
- if (n > HALF_WORLD_SIZE) return !0;
- var r = .5 * n;
- this.transformMapToLocal.forward(e);
- this.isTileVisibleBox.makeEmpty();
- this.isTileVisibleVec.set(e.x - r, e.y - r, e.z).applyMatrix4(this.objectGroup.matrixWorld);
- this.isTileVisibleBox.expandByPoint(this.isTileVisibleVec);
- this.isTileVisibleVec.set(e.x - r, e.y + r, e.z).applyMatrix4(this.objectGroup.matrixWorld);
- this.isTileVisibleBox.expandByPoint(this.isTileVisibleVec);
- this.isTileVisibleVec.set(e.x + r, e.y - r, e.z).applyMatrix4(this.objectGroup.matrixWorld);
- this.isTileVisibleBox.expandByPoint(this.isTileVisibleVec);
- this.isTileVisibleVec.set(e.x + r, e.y + r, e.z).applyMatrix4(this.objectGroup.matrixWorld);
- this.isTileVisibleBox.expandByPoint(this.isTileVisibleVec);
- return i.intersectsBox(this.isTileVisibleBox)
- }
-
- addToSceneGroup(t){
- this.objectGroupAdded || (t.add(this.objectGroup),
- this.objectGroupAdded = !0);
- }
-
- removeFromSceneGroup(t){
- this.baseTile.remove(),
- this.objectGroupAdded && (t.remove(this.objectGroup),
- this.objectGroupAdded = !1);
- }
-
-
- }
- class MapTile{
- constructor(map,/* t, */ e, n){
- this.map = map;
- //this.mapLayer = t,
- this.objectGroup = e,
- this.tileColor = n,
- this.meshAdded = !1,
- this.textureLoaded = !1,
- this.children = [];
- }
- update(e, n, i, r, o, a, s){
- return !!this.doesNotContainTilesToBeDisplayed(e) || (0 === i ? this.updateTile(e, r, o, a) : this.updateSubTiles(e, n, i, r, o, a, s))
- }
-
- doesNotContainTilesToBeDisplayed(t){
- return t.tilePresenceMap && t.tilePresenceMap.empty
- }
-
- updateTile(t, e, n, i){
- if(!this.mesh){
- this.createTileObject(t, e, n, i);
- }
- if(!this.meshAdded){
- this.objectGroup.add(this.mesh);
- this.meshAdded = !0;
- }
- if(this.textureLoaded){
- this.removeChildren();
- }
-
- return this.textureLoaded
- }
-
- updateSubTiles(entity, n, level, o, a, s, c){
- for (var l = !0, u = [-.25 * o, .25 * o, -.25 * o, .25 * o], d = [.25 * o, .25 * o, -.25 * o, -.25 * o], p = 0; p < 4; ++p){
- var h = c + p.toString(10);
- //一级(512):0 1 2 3分别为左上、右上、左下、右下。二级(1024)就是把一级的每一块分裂,如00 01 02 03分别是0的左上、右上、左下、右下……
- /* if(entity.name == 'floorplan'){
- console.log(1)
- } */
-
-
- if (!entity.tilePresenceMap || entity.tilePresenceMap[h]){
- //去掉判断,直接显示
- var f = a + u[p]
- , m = s + d[p];
- tempVector.set(f, m, 0);
- if (entity.isTileVisible(tempVector, .5 * o, n)){
-
- this.children[p] || (this.children[p] = new MapTile(this.map, this.objectGroup,this.tileColor));
- l = this.children[p].update(entity, n, level - 1, .5 * o, f, m, h) && l;
- } else {
- if (this.children[p]){
- this.children[p].remove();
- delete this.children[p];
- }
- }
-
- }
- }
- return l && this.removeObject3D(),
- l
- }
-
- createTileObject(t, e, n, a){
- var s = this;
- this.mesh = this.createMesh(t.transformMapToLocal, e, n, a),
- this.textureLoaded = !1;
- var c = t.mapSizeM / e
- , l = Math.log(c) / Math.log(2)
- , u = n / e + .5 * (c - 1)
- , d = -a / e + .5 * (c - 1)
- , p = t.getTileUrl(Math.round(l), Math.round(u), Math.round(d));
- viewer.setObjectLayers(this.mesh, 'map' );
- this.mesh.renderOrder = -(1e6 - l - 100 * (t.zIndex || 0));
- var h = this.mesh.material;
-
-
- var loadDone = ()=>{
- this.map.mapLayer.loadingInProgress--;
- if(this.map.mapLayer.loadingInProgress == 0){
- this.map.mapLayer.dispatchEvent('loadDone');
- }
- };
-
- h.map = texLoader$1.load(p, (tex)=>{
- if(this.mesh){//如果还要显示的话
- this.textureLoaded = true;
- this.mesh.material.opacity = 1;
- //this.mapLayer.view.invalidateScene()
-
- this.map.mapLayer.viewer.dispatchEvent({
- type:'content_changed'
- });
- this.map.mapLayer.needUpdate = true; //表示还要继续update(以removeChildren)
- }else {
- tex.dispose();
- }
- loadDone();
- } , void 0, (()=>{//error
- this.textureLoaded = !0;
- if(this.mesh){
- this.mesh.material.dispose(); //o.disposeMeshMaterial(this.mesh)
- this.mesh.material = errorMaterial;
- //this.map.mapLayer.view.invalidateScene())
- this.map.mapLayer.viewer.dispatchEvent({
- type:'content_changed'
- });
- }
- loadDone();
- }));
-
- h.map.anisotropy = 0,
- h.map.generateMipmaps = !1,
- h.map.minFilter = LinearFilter,
- h.map.magFilter = LinearFilter,
- this.map.mapLayer.loadingInProgress++;
- }
-
- createMesh(t, e, n, o){
- var a = new Geometry;
- return tempVector.set(n - e / 2, o - e / 2, 0),
- a.vertices.push(new Vector3().copy(t.forward(tempVector))),
- tempVector.set(n + e / 2, o - e / 2, 0),
- a.vertices.push(new Vector3().copy(t.forward(tempVector))),
- tempVector.set(n + e / 2, o + e / 2, 0),
- a.vertices.push(new Vector3().copy(t.forward(tempVector))),
- tempVector.set(n - e / 2, o + e / 2, 0),
- a.vertices.push(new Vector3().copy(t.forward(tempVector))),
- a.faces.push(face1),
- a.faces.push(face2),
- a.faceVertexUvs[0].push(face1UV),
- a.faceVertexUvs[0].push(face2UV),
- new Mesh(a,this.createMaterial())
- }
-
- createMaterial(){
- var t = new MeshBasicMaterial({
- transparent: !0,
- depthWrite: !1,
- depthTest: !0,
- opacity: 0,
- side: DoubleSide
- });
- return t.color = this.tileColor ? this.tileColor : new Color(16777215),
- t
- }
-
- remove(){
- this.removeObject3D(),
- this.removeChildren();
- }
-
- removeObject3D(){
- if (this.mesh){
- if (this.objectGroup.remove(this.mesh),
- this.textureLoaded){
- var t = this.mesh.material.map;
- t && t.dispose();
- }
- this.mesh.material.dispose(); //o.disposeMeshMaterial(this.mesh),
- this.mesh.geometry.dispose();
- this.mesh = void 0;
- }
- this.meshAdded = !1,
- this.textureLoaded = !1;
-
- }
-
- removeChildren(){
- for (var t = 0, e = this.children; t < e.length; t++){
- var n = e[t];
- n && (n.removeObject3D(),
- n.removeChildren());
- }
- this.children.length = 0;
- }
-
- }
- proj4.defs("EPSG:3857", "+title=WGS 84 / Pseudo-Mercator +proj=merc +a=6378137 +b=6378137 +lat_ts=0.0 +lon_0=0.0 +x_0=0.0 +y_0=0 +k=1.0 +units=m +nadgrids=@null +no_defs");
- //这里地图世界的中心是不是lon:0,lat:0
- class TiledMapOpenStreetMap extends TiledMapBase{
- constructor(mapLayer, tileColor){
- super('map', mapLayer, tileColor, /* "EPSG:4550" */ "EPSG:3857" ); //EPSG projection
- //this.baseUrl = "https://wprd03.is.autonavi.com/appmaptile?style=7&x=${x}&y=${y}&z=${z}",
- //this.baseUrl = "https://webrd01.is.autonavi.com/appmaptile?lang=zh_cn&size=1&scale=1&style=7&x=${x}&y=${y}&z=${z}" //最高只到18 level
- this.baseUrl = "https://wprd04.is.autonavi.com/appmaptile?lang=zh_cn&style=7&x=${x}&y=${y}&z=${z}"; //最高只到19
-
- this.attribution = "© PopSmart, © 高德地图",
- this.tileSizePx = 256;
- this.mapSizeM = 40075017;
- this.maxDepth = 19;//20
- this.bias = 0.5;
-
-
-
- }
-
- getTileUrl(t, e, n){
- return this.baseUrl.replace(/\${z}/, t.toString(10)).replace(/\${x}/, e.toString(10)).replace(/\${y}/, n.toString(10))
- }
-
- fillAttributions(t){
- t[this.attribution] = {
- score: 50
- };
- }
-
-
-
- }
- class TiledMapFromEntity extends TiledMapBase{
- constructor(mapLayer, tileColor, data){
- super('floorplan', mapLayer, tileColor, "NAVVIS:TMERC" /* "EPSG:3857" *//* "WGS84" */); //直接就是本地坐标,没有projec
-
-
- let entity = this.tiledMapEntity = this.fillFromData(data);
- let time = entity.updateTime || entity.createTime;
-
- this.tileSizePx = entity.tileSizePx,
- this.mapSizeM = entity.mapSizeM,
- this.maxDepth = entity.maxDepth;
- this.postStamp = time ? time.replace(/[^0-9]/ig,'') : (new Date).getTime();
- //this.projection = n.crsLocal,
- this.zIndex = 0,
- this.tilePresenceMap = this.decodeBitStream(this.tiledMapEntity.quadtree); //包含tile分裂信息,如果写错了会造成tile显示不全
-
- }
-
- fillFromData(e){
- let data = {};
-
- data.id = e.id;
- data.globalLocation = Potree.Utils.VectorFactory.fromArray3(e.location),
- data.orientation = Potree.Utils.QuaternionFactory.fromArray(e.orientation);
- if(Potree.fileServer){
- data.filePath = `${Potree.settings.urls.prefix}${e.file_path}`;
- }else {
- data.filePath = `${Potree.settings.urls.prefix}/data/${Potree.settings.number}/${e.file_path}`;
- }
-
- //if(!data.filePath.includes('building_1'))data.filePath = data.filePath.replace('building','building_1')//暂时
- data.fileName = '$DEPTH/$X/$Y.png',//e.file_name,
- data.type = e.type,
- data.mapSizeM = e.map_size_m,
- data.tileSizePx = e.tile_size_px,
- data.maxDepth = e.max_depth,
- data.quadtree = e.quadtree,
- data.floorId = e.floor_id,
- data.bundleId = e.bundle_id;
- //this.computeLocalCoordinates()
- return data
-
- }
-
-
-
- computeLocalCoordinates(){
- if(proj4.defs("NAVVIS:TMERC")){
- this.tiledMapEntity.location = new Vector3().copy(viewer.transform.lonlatToLocal.forward(this.tiledMapEntity.globalLocation));
- }
- }
-
- updateProjection() {
- super.updateProjection();
- if(!this.position){
- this.computeLocalCoordinates();
- }
- /* this.projection = this.TransformService.crsLocal,
- t.prototype.updateProjection.call(this) */
- }
-
-
- get position(){
- return this.tiledMapEntity.location
- /* enumerable: !0,
- configurable: !0 */
- }
- get quaternion(){
- return this.tiledMapEntity.orientation
- /* enumerable: !0,
- configurable: !0 */
- }
-
- getTileUrl(t, e, n) {
- var i = (this.tiledMapEntity.filePath + "/" + this.tiledMapEntity.fileName).replace(/\$DEPTH/g, t.toString(10)).replace(/\$X/g, e.toString(10)).replace(/\$Y/g, n.toString(10));
- return i += "?t=" + this.postStamp
- //this.RestService.addAuthorizationQueryParameter(i) //????
- }
-
- fillAttributions(t) {
- t.NavVis = {
- score: 100
- };
- }
-
-
-
-
- decodeBitStream(t) {
- if (!t)
- return {
- empty: !0
- };
- for (var e = {}, n = [e], i = 0; i < t.length; i++) {
- var r = n.shift()
- , o = parseInt(t.substr(i, 1), 16);
- if (1 & o) {
- var a = {};
- r[0] = a,
- n.push(a);
- }
- 2 & o && (a = {},
- r[1] = a,
- n.push(a)),
- 4 & o && (a = {},
- r[2] = a,
- n.push(a)),
- 8 & o && (a = {},
- r[3] = a,
- n.push(a));
- }
- var s = {
- empty: !0
- };
- return this.computeHashes(s, e, ""),
- s
- }
-
- computeHashes(t, e, n) {
- for (var i = 0; i < 4; i++)
- e[i] && (t[n + i.toString(10)] = !0,
- t.empty = !1,
- this.computeHashes(t, e[i], n + i.toString(10)));
- }
-
-
- }
- /* {
- "bundle_id": 1, //t-CwfhfqJ
- "file_name": "$DEPTH/$X/$Y.png",
- "file_path": "data/bundle_t-CwfhfqJ/building_1/map_tiles/11",
- "floor_id": 11,
- "id": 1,
- "location": [
- 113.5957510575092,
- 22.366605927999239,
- 0.0
- ],
- "map_size_m": 61.44,
- "max_depth": 3,
- "orientation": [
- 0.7071067811865476,
- 0.0,
- 0.0,
- 0.7071067811865475
- ],
- "quadtree": "fe5f7c7fcffff7f53",
- "sceneCode": "t-CwfhfqJ",
- "tile_size_px": 256,
- "type": "TILED_PYRAMID"
- }
- */
- var MathLight = {};
- MathLight.RADIANS_PER_DEGREE = Math.PI / 180;
- MathLight.DEGREES_PER_RADIAN = 180 / Math.PI;
- MathLight.Vector3 = function(e, t, i) {
- this.x = e || 0,
- this.y = t || 0,
- this.z = i || 0;
- };
- MathLight.Matrix4 = function() {
- this.elements = new Float32Array([1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1]),
- arguments.length > 0 && console.error("MathLight.Matrix4: the constructor no longer reads arguments. use .set() instead.");
- };
- MathLight.Matrix4.prototype = {
- identity: function() {
- return this.set(1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1),
- this
- },
- copy: function(e) {
- return this.elements.set(e.elements),
- this
- },
- applyToVector3: function(e) {
- var t = e.x
- , i = e.y
- , n = e.z
- , r = this.elements;
- return e.x = r[0] * t + r[4] * i + r[8] * n + r[12],
- e.y = r[1] * t + r[5] * i + r[9] * n + r[13],
- e.z = r[2] * t + r[6] * i + r[10] * n + r[14],
- this
- },
- getInverse: function(e, t) {
- var i = this.elements
- , n = e.elements
- , r = n[0]
- , o = n[1]
- , a = n[2]
- , s = n[3]
- , l = n[4]
- , c = n[5]
- , h = n[6]
- , u = n[7]
- , d = n[8]
- , p = n[9]
- , f = n[10]
- , g = n[11]
- , m = n[12]
- , v = n[13]
- , A = n[14]
- , y = n[15]
- , C = p * A * u - v * f * u + v * h * g - c * A * g - p * h * y + c * f * y
- , I = m * f * u - d * A * u - m * h * g + l * A * g + d * h * y - l * f * y
- , E = d * v * u - m * p * u + m * c * g - l * v * g - d * c * y + l * p * y
- , b = m * p * h - d * v * h - m * c * f + l * v * f + d * c * A - l * p * A
- , w = r * C + o * I + a * E + s * b;
- if (0 === w) {
- var _ = "MathLight.Matrix4.getInverse(): can't invert matrix, determinant is 0";
- if (t)
- throw new Error(_);
- return console.warn(_),
- this.identity()
- }
- var T = 1 / w;
- return i[0] = C * T,
- i[1] = (v * f * s - p * A * s - v * a * g + o * A * g + p * a * y - o * f * y) * T,
- i[2] = (c * A * s - v * h * s + v * a * u - o * A * u - c * a * y + o * h * y) * T,
- i[3] = (p * h * s - c * f * s - p * a * u + o * f * u + c * a * g - o * h * g) * T,
- i[4] = I * T,
- i[5] = (d * A * s - m * f * s + m * a * g - r * A * g - d * a * y + r * f * y) * T,
- i[6] = (m * h * s - l * A * s - m * a * u + r * A * u + l * a * y - r * h * y) * T,
- i[7] = (l * f * s - d * h * s + d * a * u - r * f * u - l * a * g + r * h * g) * T,
- i[8] = E * T,
- i[9] = (m * p * s - d * v * s - m * o * g + r * v * g + d * o * y - r * p * y) * T,
- i[10] = (l * v * s - m * c * s + m * o * u - r * v * u - l * o * y + r * c * y) * T,
- i[11] = (d * c * s - l * p * s - d * o * u + r * p * u + l * o * g - r * c * g) * T,
- i[12] = b * T,
- i[13] = (d * v * a - m * p * a + m * o * f - r * v * f - d * o * A + r * p * A) * T,
- i[14] = (m * c * a - l * v * a - m * o * h + r * v * h + l * o * A - r * c * A) * T,
- i[15] = (l * p * a - d * c * a + d * o * h - r * p * h - l * o * f + r * c * f) * T,
- this
- },
- makeRotationFromQuaternion: function(e) {
- var t = this.elements
- , i = e.x
- , n = e.y
- , r = e.z
- , o = e.w
- , a = i + i
- , s = n + n
- , l = r + r
- , c = i * a
- , h = i * s
- , u = i * l
- , d = n * s
- , p = n * l
- , f = r * l
- , g = o * a
- , m = o * s
- , v = o * l;
- return t[0] = 1 - (d + f),
- t[4] = h - v,
- t[8] = u + m,
- t[1] = h + v,
- t[5] = 1 - (c + f),
- t[9] = p - g,
- t[2] = u - m,
- t[6] = p + g,
- t[10] = 1 - (c + d),
- t[3] = 0,
- t[7] = 0,
- t[11] = 0,
- t[12] = 0,
- t[13] = 0,
- t[14] = 0,
- t[15] = 1,
- this
- }
- };
- MathLight.Quaternion = function(e, t, i, n) {
- this._x = e || 0,
- this._y = t || 0,
- this._z = i || 0,
- this._w = void 0 !== n ? n : 1;
- };
- MathLight.Quaternion.prototype = {
- get x() {
- return this._x
- },
- set x(e) {
- this._x = e;
- },
- get y() {
- return this._y
- },
- set y(e) {
- this._y = e;
- },
- get z() {
- return this._z
- },
- set z(e) {
- this._z = e;
- },
- get w() {
- return this._w
- },
- set w(e) {
- this._w = e;
- },
- copy: function(e) {
- this._x = e.x,
- this._y = e.y,
- this._z = e.z,
- this._w = e.w;
- },
- inverse: function() {
- return this.conjugate().normalize()
- },
- conjugate: function() {
- return this._x *= -1,
- this._y *= -1,
- this._z *= -1,
- this
- },
- length: function() {
- return Math.sqrt(this._x * this._x + this._y * this._y + this._z * this._z + this._w * this._w)
- },
- normalize: function() {
- var e = this.length();
- return 0 === e ? (this._x = 0,
- this._y = 0,
- this._z = 0,
- this._w = 1) : (e = 1 / e,
- this._x = this._x * e,
- this._y = this._y * e,
- this._z = this._z * e,
- this._w = this._w * e),
- this
- },
- setFromAxisAngle: function(e, t) {
- var i = t / 2
- , n = Math.sin(i);
- return this._x = e.x * n,
- this._y = e.y * n,
- this._z = e.z * n,
- this._w = Math.cos(i),
- this
- },
- setFromUnitVectors: function() {
- var e, t, i = 1e-6;
- return function(n, o) {
- return void 0 === e && (e = new MathLight.Vector3),
- t = MathLight.dot(n, o) + 1,
- t < i ? (t = 0,
- Math.abs(n.x) > Math.abs(n.z) ? MathLight.setVector(e, -n.y, n.x, 0) : MathLight.setVector(e, 0, -n.z, n.y)) : MathLight.cross(n, o, e),
- this._x = e.x,
- this._y = e.y,
- this._z = e.z,
- this._w = t,
- this.normalize()
- }
- }(),
- multiply: function(e) {
- return this.multiplyQuaternions(this, e)
- },
- premultiply: function(e) {
- return this.multiplyQuaternions(e, this)
- },
- multiplyQuaternions: function(e, t) {
- var i = e._x
- , n = e._y
- , r = e._z
- , o = e._w
- , a = t._x
- , s = t._y
- , l = t._z
- , c = t._w;
- return this._x = i * c + o * a + n * l - r * s,
- this._y = n * c + o * s + r * a - i * l,
- this._z = r * c + o * l + i * s - n * a,
- this._w = o * c - i * a - n * s - r * l,
- this
- }
- };
- MathLight.convertWorkshopVector = function(e) {
- return new MathLight.Vector3(-e.x,e.y,e.z)
- };
- MathLight.convertWorkshopQuaternion = function(e) {
- return new MathLight.Quaternion(-e.x,e.y,e.z,-e.w).multiply(new MathLight.Quaternion(Math.sqrt(2) / 2,Math.sqrt(2) / 2,0,0))
- };
- MathLight.convertWorkshopOrthoZoom = function(e) {
- //return e === -1 ? -1 : e / 16 * ($('#player').width() / $('#player').height()) / n.workshopApsect
- return e === -1 ? -1 : e * ($("#player").width() / $("#player").height()) ;
- };
- MathLight.convertWorkshopPanoramaQuaternion = function(e) {
- return new MathLight.Quaternion(e.x,-e.y,-e.z,e.w).normalize().multiply((new MathLight.Quaternion).setFromAxisAngle(new MathLight.Vector3(0,1,0), 270 * MathLight.RADIANS_PER_DEGREE))
- };
- MathLight.normalize = function(e) {
- var t = e.x * e.x + e.y * e.y + e.z * e.z
- , i = Math.sqrt(t);
- e.x /= i,
- e.y /= i,
- e.z /= i;
- };
- MathLight.dot = function(e, t) {
- return e.x * t.x + e.y * t.y + e.z * t.z
- };
- MathLight.cross = function(e, t, i) {
- var n = e.x
- , r = e.y
- , o = e.z;
- i.x = r * t.z - o * t.y,
- i.y = o * t.x - n * t.z,
- i.z = n * t.y - r * t.x;
- };
- MathLight.setVector = function(e, t, i, n) {
- e.x = t,
- e.y = i,
- e.z = n;
- };
- MathLight.copyVector = function(e, t) {
- t.x = e.x,
- t.y = e.y,
- t.z = e.z;
- };
- MathLight.addVector = function(e, t) {
- e.x += t.x,
- e.y += t.y,
- e.z += t.z;
- };
- MathLight.subVector = function(e, t) {
- e.x -= t.x,
- e.y -= t.y,
- e.z -= t.z;
- };
- MathLight.applyQuaternionToVector = function(e, t) {
- var i = t.x
- , n = t.y
- , r = t.z
- , o = e.x
- , a = e.y
- , s = e.z
- , l = e.w
- , c = l * i + a * r - s * n
- , h = l * n + s * i - o * r
- , u = l * r + o * n - a * i
- , d = -o * i - a * n - s * r;
- t.x = c * l + d * -o + h * -s - u * -a,
- t.y = h * l + d * -a + u * -o - c * -s,
- t.z = u * l + d * -s + c * -a - h * -o;
- };
- MathLight.angleBetweenVectors = function(e, t) {
- return Math.acos(MathLight.dot(e, t))
- };
- var cameraLight$1 = {
- clampVFOV: function(currentFov, maxHFov, width, height) {//限制currentFov, 使之造成的横向fov不大于指定值maxHFov
- var r = cameraLight$1.getHFOVFromVFOV(currentFov, width, height);
- return r > maxHFov ? cameraLight$1.getVFOVFromHFOV(maxHFov, width, height) : currentFov
- },
- getHFOVForCamera: function(camera, getRad) {
- return cameraLight$1.getHFOVByScreenPrecent(camera.fov, camera.aspect, getRad)
- },
- //add
- getHFOVByScreenPrecent: function(fov, percent, getRad) { //当fov为占比百分百时,percent代表在屏幕上从中心到边缘的占比
- let rad = 2 * Math.atan(percent * Math.tan(fov * MathLight.RADIANS_PER_DEGREE / 2));
- if(getRad)return rad
- else return rad * MathLight.DEGREES_PER_RADIAN;
- }
- };
- /**
- * @author mschuetz / http://mschuetz.at
- *
- * adapted from THREE.OrbitControls by
- *
- * @author qiao / https://github.com/qiao
- * @author mrdoob / http://mrdoob.com
- * @author alteredq / http://alteredqualia.com/
- * @author WestLangley / http://github.com/WestLangley
- * @author erich666 / http://erichaines.com
- *
- *
- *
- */
-
- class FirstPersonControls extends EventDispatcher {
- constructor (viewer, viewport) {
- super();
-
- this.viewer = viewer;
- this.renderer = viewer.renderer;
- this.scene = viewer.scene;
-
- this.rotationSpeed = 200;
- this.moveSpeed = 10;
-
-
- this.setCurrentViewport({hoverViewport:viewport, force:true}); //this.currentViewport = viewport
-
-
-
- this.keys = {
- FORWARD: ['W'.charCodeAt(0), 38],
- BACKWARD: ['S'.charCodeAt(0), 40],
- LEFT: ['A'.charCodeAt(0), 37],
- RIGHT: ['D'.charCodeAt(0), 39],
- UP: ['Q'.charCodeAt(0)],
- DOWN: ['E'.charCodeAt(0)],
-
- //SHIFT : [16],
- ALT : [18],
- Rotate_LEFT : ['L'.charCodeAt(0)],
- Rotate_RIGHT : ['J'.charCodeAt(0)],
- Rotate_UP : ['K'.charCodeAt(0)],
- Rotate_DOWN : ['I'.charCodeAt(0)],
- };
- this.fadeFactor = 20;
- this.yawDelta = 0;
- this.pitchDelta = 0;
- this.translationDelta = new Vector3(0, 0, 0);
- this.translationWorldDelta = new Vector3(0, 0, 0);
- this.tweens = [];
- this.dollyStart = new Vector2$1;
- this.dollyEnd = new Vector2$1;
- //this.enableChangePos = true
-
- this.viewer.addEventListener('camera_changed',(e)=>{
- this.setFPCMoveSpeed(e.viewport);
- });
-
- let drag = (e) => {
- if(!this.enabled)return
- let viewport = e.dragViewport;
- if(!viewport)return
- let camera = viewport.camera;
- let mode;
- if(e.isTouch){
- if(e.touches.length == 1){
- mode = (!e.dragViewport || e.dragViewport.name == 'MainView') ? 'rotate' : 'pan';
- }else if(e.touches.length == 2){
- mode = 'scale';
- }else {
- mode = (!e.dragViewport || e.dragViewport.name == 'MainView') ? 'pan' : 'scale';
- }
- }else {
- //mode = e.buttons === Buttons.LEFT && (!e.dragViewport || e.dragViewport.name == 'MainView') ? 'rotate' : 'pan'
- mode = e.buttons === Buttons.LEFT && camera.type != 'OrthographicCamera' ? 'rotate' : 'pan';
- }
- //console.log('mode ', mode )
- let moveSpeed = this.currentViewport.getMoveSpeed();
- if (e.drag.startHandled === undefined) {///???????
- e.drag.startHandled = true;
- this.dispatchEvent({type: 'start'});
- }
-
-
- if (mode.includes('rotate')) {//旋转
-
- //来自panoramaControl updateRotation
- if(!this.pointerDragStart){
- return this.pointerDragStart = e.pointer.clone()
- }
-
-
-
- let view = this.scene.view;
- if(Potree.settings.rotAroundPoint && this.intersectStart && this.canMovePos(viewport) && !viewer.images360.isAtPano() && !this.viewer.inputHandler.pressedKeys[17]){//定点旋转: 以当前intersect的点为target旋转,不改点在屏幕中的位置
- let distance = camera.position.distanceTo(this.intersectStart.location); //不按下ctrl的话
-
- //按照orbitControl的方式旋转:
- let rotationSpeed = 2.5;
-
- this.yawDelta -= e.drag.pointerDelta.x * rotationSpeed;
- this.pitchDelta += e.drag.pointerDelta.y * rotationSpeed;
-
- //先更新一下相机:
- this.update();
- view.applyToCamera(camera);
-
- //然后得到新的相机角度下,原先点在屏幕中的位置所对应的3d点现在的坐标。只需要平移一下新旧坐标差值即可。
- let newPointerDir = viewer.inputHandler.getMouseDirection(this.intersectStart.pointer).direction.clone().multiplyScalar(distance);
- let pivot = new Vector3().addVectors(camera.position, newPointerDir); //新的3d点
-
- let moveVec = new Vector3().subVectors(pivot, this.intersectStart.location);
-
- this.translationWorldDelta.copy(moveVec.negate());
- //立即更新下,防止因update和此drag频率不同而打滑。
- this.update();
- view.applyToCamera(camera);
-
-
- }else {
-
-
- let _matrixWorld = camera.matrixWorld;
- camera.matrixWorld = new Matrix4;//unproject 前先把相机置于原点
-
- var e1 = new Vector3(this.pointerDragStart.x,this.pointerDragStart.y,-1).unproject(camera)
- , t = new Vector3(e.pointer.x,e.pointer.y,-1).unproject(camera)
- , i = Math.sqrt(e1.x * e1.x + e1.z * e1.z)
- , n = Math.sqrt(t.x * t.x + t.z * t.z)
- , o = Math.atan2(e1.y, i)
- , a = Math.atan2(t.y, n);
-
- this.pitchDelta += o - a; //上下旋转
- e1.y = 0,
- t.y = 0;
-
- var s = Math.acos(e1.dot(t) / e1.length() / t.length());
-
- if(!isNaN(s)){
- var yawDelta = s; //左右旋转
- this.pointerDragStart.x > e.pointer.x && (yawDelta *= -1);
- this.yawDelta += yawDelta;
- }
-
-
- //console.log('rotate:', this.pitchDelta, e.pointer.toArray(), this.pointerDragStart.toArray())
-
-
- this.pointerDragStart.copy(e.pointer);
-
- camera.matrixWorld = _matrixWorld ;
-
-
-
- }
- }
-
- if (mode.includes('pan')) {//平移
- if(!this.canMovePos(viewport)){
- return
- }
-
- if(camera.type == "OrthographicCamera"){
-
- //console.log(e.drag.pointerDelta, e.pointer, e.drag.end)
- let moveVec = Utils.getOrthoCameraMoveVec(e.drag.pointerDelta, camera );//最近一次移动向量
-
- let pointclouds;
- let Alignment = window.viewer.modules.Alignment;
- let handleState = Alignment.handleState;
-
- let a = e.buttons === Buttons.LEFT && viewport.alignment && handleState && viewport.alignment[handleState];
- if(Potree.settings.editType == 'pano'){//右键平移视图、左键操作点云
- let PanoEditor = window.viewer.modules.PanoEditor;
-
- if(a && PanoEditor.selectedPano){
- if(!PanoEditor.selectedGroup || !PanoEditor.checkIfAllLinked({group:PanoEditor.selectedGroup}) ){
- if(handleState == 'translate' && ( e.drag.intersectStart.pointclouds && Common.getMixedSet(PanoEditor.selectedClouds, e.drag.intersectStart.pointclouds).length || PanoEditor.selectedPano.hovered)//拖拽到点云上 或 circle
- || handleState == 'rotate' )
- {
- pointclouds = PanoEditor.selectedClouds;
- }
- }else {
- console.warn('选中的漫游点连通了整个数据集,不允许移动');
- }
- }
-
- if(!pointclouds && e.buttons === Buttons.LEFT && viewport.alignment.rotateSide){
- return PanoEditor.rotateSideCamera(e.drag.pointerDelta.x)
- }
- }else {
- /* if(Alignment.selectedClouds && Alignment.selectedClouds.length){
- pointclouds = a && e.drag.intersectStart.pointclouds && Common.getMixedSet(Alignment.selectedClouds, e.drag.intersectStart.pointclouds).length && Alignment.selectedClouds
-
- }else{ */
- pointclouds = a && e.drag.intersectStart.pointcloud && [e.drag.intersectStart.pointcloud];
- //}
-
- }
-
- if(pointclouds){
- this.dispatchEvent({
- type : "transformPointcloud",
- intersectPoint: e.intersectPoint.orthoIntersect,
- intersectStart: e.drag.intersectStart.orthoIntersect,
- moveVec,
- pointclouds,
- camera
- });
- }else {
-
- this.translationWorldDelta.add(moveVec.negate());
-
- }
-
-
-
- }else {
- if(e.drag.intersectStart){//如果拖拽着点云
-
- if(e.drag.z == void 0){//拖拽开始
- let pointerStartPos2d = e.drag.intersectStart.location.clone().project(camera);//识别到的点云点的位置
- e.drag.z = pointerStartPos2d.z; //记录z,保持拖拽物体到屏幕距离不变,所以z深度不变
- e.drag.projectionMatrixInverse = camera.projectionMatrixInverse.clone();
- //防止吸附到最近点上(因为鼠标所在位置并非识别到的点云点的位置,需要得到鼠标所在位置的3d坐标。)
- let pointerStartPos2dReal = new Vector3(this.pointerDragStart.x,this.pointerDragStart.y, e.drag.z);
- e.drag.translateStartPos = pointerStartPos2dReal.clone().unproject(camera);
- /* this.viewer.dispatchEvent({
- type: 'dragPanBegin',
- projectionMatrixInverse : e.drag.projectionMatrixInverse
- }); */
- //console.log('开始拖拽', e.pointer.clone())
- }
- //拖拽的过程中将projectionMatrixInverse替换成开始拖拽时的,因为near、far一直在变,会导致unproject计算出的3d坐标改变很大而闪烁。
- var _projectionMatrixInverse = camera.projectionMatrixInverse;
- camera.projectionMatrixInverse = e.drag.projectionMatrixInverse;
-
-
- let newPos2d = new Vector3(e.pointer.x,e.pointer.y, e.drag.z );
- let newPos3d = newPos2d.clone().unproject(camera);
- let moveVec = newPos3d.clone().sub( e.drag.translateStartPos /* e.drag.intersectStart.location */ );//移动相机,保持鼠标下的位置永远不变,所以用鼠标下的新位置减去鼠标下的原始位置
-
-
- camera.projectionMatrixInverse = _projectionMatrixInverse;
- this.translationWorldDelta.copy(moveVec.negate()); //这里没法用add,原因未知,会跳动
- //console.log('pan 1', this.translationWorldDelta.clone())
-
-
-
- //四指松开剩三指时会偏移一下,暂不知道哪里的问题,或许跟开头防止点云吸附有关?
-
-
-
-
- }else { //如果鼠标没有找到和点云的交点,就假设移动整个模型(也可以去扩大范围寻找最近点云)
-
- /* let center = viewer.scene.pointclouds[0].position;
- let radius = camera.position.distanceTo(center);
- let ratio = radius * Math.tan(THREE.Math.degToRad(camera.fov)/2) / 1000 */
-
-
- /* let speed = this.currentViewport.getMoveSpeed()
- if(FirstPersonControls.boundPlane){
- speed = FirstPersonControls.boundPlane.distanceToPoint(this.currentViewport.position)
- speed = Math.max(1 , speed)
- } */
- let lastIntersect = viewport.lastIntersect ? (viewport.lastIntersect.location || viewport.lastIntersect) : viewer.bound.center; //该viewport的最近一次鼠标和点云的交点
- let speed = camera.position.distanceTo(lastIntersect);
- let fov = cameraLight$1.getHFOVForCamera(camera, true);
- let ratio = speed * Math.tan(fov/2);
- this.translationDelta.x -= e.drag.pointerDelta.x * ratio;
- this.translationDelta.z -= e.drag.pointerDelta.y * ratio;
- //console.log('pan2', e.drag.pointerDelta)
- }
- }
- this.useAttenuation = false;
- }
-
-
- if(mode.includes('scale')){
-
- this.dollyEnd.subVectors(e.touches[0].pointer, e.touches[1].pointer);
- //if(!this.dollyStart)return
- var scale = this.dollyEnd.length() / this.dollyStart.length();
- //console.log('scale ',scale)
-
- let pointer = new Vector2$1().addVectors(e.touches[0].pointer, e.touches[1].pointer).multiplyScalar(0.5);//两个指头的中心点
-
- dolly({
- pointer,
- scale, camera
- });
- this.dollyStart.copy(this.dollyEnd);
-
- }
- //最好按ctrl可以变为dollhouse的那种旋转
- };
- let drop = e => {
- if(!this.enabled)return
- this.dispatchEvent({type: 'end'});
-
- };
- let dolly = (e={})=>{
-
- if(Potree.settings.displayMode == 'showPanos' && this.currentViewport == viewer.mainViewport/* this.currentViewport.unableChangePos */){//全景时
- this.dispatchEvent({type:'dollyStopCauseUnable',delta:e.delta, scale:e.scale});
- return
- }
-
- let camera = e.camera;
-
-
- if(camera.type == "OrthographicCamera"){
- let ratio;
- if(e.delta != void 0){//滚轮缩放
- if(e.delta == 0){//mac
- return
- }else if (e.delta < 0) {
- ratio = 0.9;
- } else if (e.delta > 0) {
- ratio = 1.1;
- }
- }else {
- ratio = e.scale; //触屏缩放
- }
-
- let zoom = camera.zoom * ratio;
- let limit = camera.zoomLimit;
- if(limit) zoom = MathUtils.clamp(zoom, limit.min,limit.max );
-
-
- let pointerPos = new Vector3(e.pointer.x, e.pointer.y,0.5);
- let oldPos = pointerPos.clone().unproject(camera);
-
- if(camera.zoom != zoom){
- camera.zoom = zoom;
- camera.updateProjectionMatrix();
- }
- let newPos = pointerPos.clone().unproject(camera);
-
- //定点缩放, 恢复一下鼠标所在位置的位置改变量
- let moveVec = new Vector3().subVectors(newPos,oldPos);
- this.translationWorldDelta.add(moveVec.negate());
- this.useAttenuation = false;
- }else {
- let speed , direction;
-
-
- if(e.delta != void 0){//滚轮缩放
- speed = this.currentViewport.getMoveSpeed() * 15;
-
- //var direction = this.currentViewport.view.direction.clone();
- direction = this.viewer.inputHandler.getMouseDirection().direction; //定点缩放
-
-
- if(e.delta == 0){//mac
- return
- }else if (e.delta < 0) {
- speed *= -1;
- }
- }else {
- const constantDis = this.currentViewport.getMoveSpeed() * 200; //constantDis = 10;//常量系数,当放大一倍时前进的距离。可以调整
- speed = (e.scale-1)*constantDis; //触屏缩放
- //pointer = new THREE.Vector2().addVectors().multiplyScalar(0.5);//两个指头的中心点
- direction = this.viewer.inputHandler.getMouseDirection(e.pointer).direction; //定点缩放
- }
-
- this.useAttenuation = true;
- var vec = direction.multiplyScalar(speed );
- this.translationWorldDelta.copy(vec);
-
- }
- };
- let scroll = (e) => {
- if(!this.enabled)return
- this.setCurrentViewport(e);
-
-
- e.camera = e.hoverViewport.camera;
- dolly(e);
- };
- let dblclick = (e) => {
- if(!this.enabled)return
-
- if(!Potree.settings.dblToFocusPoint)return;//调试时才可双击
-
- if(Potree.settings.displayMode == 'showPointCloud'/* !viewer.images360.isAtPano() */) this.zoomToLocation(e.mouse);
- };
- this.viewer.addEventListener('global_drag', drag);
- /* this.viewer.addEventListener('global_touchmove', (e)=>{
- if(!this.enabled)return
- if(e.touches.length>1){//单指的就触发上一句
- //console.log('global_touchmove' )
- drag(e)
- }
- }); */
- this.viewer.addEventListener('global_drop', drop);
- this.viewer.addEventListener('global_mousewheel', scroll);
- this.viewer.addEventListener('global_dblclick', dblclick);
-
-
-
- let prepareScale = (e)=>{//触屏的scale
- this.dollyStart.subVectors(e.touches[0].pointer, e.touches[1].pointer);
- };
- let prepareRotate = (e)=>{
- this.pointerDragStart = e.pointer.clone();
- this.intersectStart = e.intersectPoint && e.intersectPoint.location && {
- location : e.intersectPoint.location,
- pointer : e.intersectPoint.location.clone().project(e.dragViewport.camera) //intersect点在屏幕中的位置
- };
- //console.log('prepareRotate' )
- };
- let preparePan = (e)=>{//触屏的pan点云 还是会偏移
- this.pointerDragStart = e.pointer.clone();
-
- e.drag.z = void 0; //清空
- drag(e); //触屏点击时更新的pointer直接用一次drag
- //console.log('preparePan ' )
- };
-
- this.viewer.addEventListener('global_mousedown'/* 'startDragging' */, (e)=>{
- if(!this.enabled)return
- this.setCurrentViewport(e);
- prepareRotate(e);
- });
-
-
- //注意,每次增减指头都会修改pointer,需要更新下状态
- this.viewer.addEventListener('global_touchstart', (e)=>{
- if(!this.enabled)return
- if(e.touches.length==2){//只监听开头两个指头
- prepareScale(e);
- }else if(e.touches.length>=3){
- preparePan(e);
- }
- });
- this.viewer.addEventListener('global_touchend', (e)=>{
- if(!this.enabled)return
- if(e.touches.length==2){//停止平移,开始scale
- prepareScale(e);
- }else if(e.touches.length==1){//停止scale,开始rotate
- prepareRotate(e);
- }else if(e.touches.length>=3){//重新准备下平移(因为抬起的指头可能包含平移使用的数据),否则抬起时漂移
- preparePan(e);
- }
- });
-
-
-
-
- /* this.viewer.addEventListener('enableChangePos', (e)=>{
- if(!this.enabled)return
- this.enableChangePos = e.canLeavePano
- }) */
-
- }
- canMovePos(viewport){
- if(viewport == viewer.mainViewport && (Potree.settings.displayMode == 'showPanos'
- || viewer.images360.bumping || viewer.images360.latestToPano))return false
- else return true
- }
- setEnable(enabled){
- this.enabled = enabled;
-
- }
- setFPCMoveSpeed(viewport){
- if(viewport.camera.type == 'OrthographicCamera'){
- let s = 1 / viewport.camera.zoom;
- viewport.setMoveSpeed(s);
-
- }else {
- if(viewport == viewer.mainViewport && FirstPersonControls.boundPlane){
- let s = FirstPersonControls.boundPlane.distanceToPoint(viewer.mainViewport.view.position);
- s = Math.sqrt(s) / 10;
- s = Math.max(FirstPersonControls.standardSpeed , s);
- s *= Potree.config.moveSpeedAdujust;
- viewer.setMoveSpeed(s);
- }
-
- }
-
- }
- setCurrentViewport(o={}){//add
- if(!this.enabled && !o.force )return
- if(o.hoverViewport && this.currentViewport != o.hoverViewport ){
- this.currentViewport = o.hoverViewport;
- //this.viewer.setMoveSpeed(this.currentViewport.radius/100);
- this.setFPCMoveSpeed(this.currentViewport);
- }
- if(this.currentViewport.camera.type == 'OrthographicCamera'){
- this.lockElevationOri = true;
- this.lockRotation = true;
- }else {
- this.lockElevationOri = false;
- this.lockRotation = false;
- }
- }
-
- setScene (scene) {
- this.scene = scene;
-
- }
- stop(){
- this.yawDelta = 0;
- this.pitchDelta = 0;
- this.translationDelta.set(0, 0, 0);
- }
-
-
-
- zoomToLocation(mouse){
- if(!this.enabled)return
- let camera = this.scene.getActiveCamera();
-
- /* let I = Utils.getMousePointCloudIntersection(
- mouse,
- camera,
- this.viewer,
- this.scene.pointclouds); */
- var I = this.viewer.inputHandler.intersectPoint;
- if (!I) {
- return;
- }
- let targetRadius = 0;
- {
- let minimumJumpDistance = 0.2;
- let domElement = this.renderer.domElement;
-
- let ray = Utils.mouseToRay(this.viewer.inputHandler.pointer, camera);
- let {origin, direction} = this.viewer.inputHandler.getMouseDirection();
- let raycaster = new Raycaster();
- raycaster.ray.set(origin, direction);
-
- let nodes = I.pointcloud.nodesOnRay(I.pointcloud.visibleNodes, ray);
-
- let nodes2 = I.pointcloud.nodesOnRay(I.pointcloud.visibleNodes, raycaster.ray);
-
-
- let lastNode = nodes[nodes.length - 1];
- let radius = lastNode.getBoundingSphere(new Sphere()).radius;
- targetRadius = Math.min(this.scene.view.radius, radius);
- targetRadius = Math.max(minimumJumpDistance, targetRadius);
- }
- let d = this.scene.view.direction.multiplyScalar(-1);
- let cameraTargetPosition = new Vector3().addVectors(I.location, d.multiplyScalar(targetRadius));
- // TODO Unused: let controlsTargetPosition = I.location;
- let animationDuration = 600;
- let easing = TWEEN.Easing.Quartic.Out;
- { // animate
- let value = {x: 0};
- let tween = new TWEEN.Tween(value).to({x: 1}, animationDuration);
- tween.easing(easing);
- this.tweens.push(tween);
- let startPos = this.scene.view.position.clone();
- let targetPos = cameraTargetPosition.clone();
- let startRadius = this.scene.view.radius;
- let targetRadius = cameraTargetPosition.distanceTo(I.location);
- tween.onUpdate(() => {
- let t = value.x;
- this.scene.view.position.x = (1 - t) * startPos.x + t * targetPos.x;
- this.scene.view.position.y = (1 - t) * startPos.y + t * targetPos.y;
- this.scene.view.position.z = (1 - t) * startPos.z + t * targetPos.z;
- this.scene.view.radius = (1 - t) * startRadius + t * targetRadius;
- this.viewer.setMoveSpeed(this.scene.view.radius / 2.5);
- });
- tween.onComplete(() => {
- this.tweens = this.tweens.filter(e => e !== tween);
- });
- tween.start();
- }
- }
- update (delta=1) {
- if(!this.enabled)return
-
- //console.log('update')
- let view = this.currentViewport.view;
- { // cancel move animations on user input
- let changes = [ this.yawDelta,
- this.pitchDelta,
- this.translationDelta.length(),
- this.translationWorldDelta.length() ];
- let changeHappens = changes.some(e => Math.abs(e) > 0.001);
- if (changeHappens && this.tweens.length > 0) {
- this.tweens.forEach(e => e.stop());
- this.tweens = [];
- }
- }
- { // accelerate while input is given
- let ih = this.viewer.inputHandler;
- let moveForward = this.keys.FORWARD.some(e => ih.pressedKeys[e]);
- let moveBackward = this.keys.BACKWARD.some(e => ih.pressedKeys[e]);
- let moveLeft = this.keys.LEFT.some(e => ih.pressedKeys[e]);
- let moveRight = this.keys.RIGHT.some(e => ih.pressedKeys[e]);
- let moveUp = this.keys.UP.some(e => ih.pressedKeys[e]);
- let moveDown = this.keys.DOWN.some(e => ih.pressedKeys[e]);
-
- let rotateLeft = this.keys.Rotate_LEFT.some(e => ih.pressedKeys[e]);
- let rotateRight = this.keys.Rotate_RIGHT.some(e => ih.pressedKeys[e]);
- let rotateUp = this.keys.Rotate_UP.some(e => ih.pressedKeys[e]);
- let rotateDown = this.keys.Rotate_DOWN.some(e => ih.pressedKeys[e]);
-
- this.lockElevation = this.lockElevationOri || this.keys.ALT.some(e => ih.pressedKeys[e]);
-
-
-
- if(!this.lockRotation){
- if(rotateLeft){
- this.yawDelta -= 0.01;
- }else if(rotateRight){
- this.yawDelta += 0.01;
- }
- if(rotateUp){
- this.pitchDelta -= 0.01;
- }else if(rotateDown){
- this.pitchDelta += 0.01;
- }
- }
-
- if(this.canMovePos(this.currentViewport) ){
- if(this.lockElevation){
- let dir = view.direction;
- dir.z = 0;
- dir.normalize();
- if (moveForward && moveBackward) {
- this.translationWorldDelta.set(0, 0, 0);
- } else if (moveForward) {
- this.translationWorldDelta.copy(dir.multiplyScalar(this.currentViewport.getMoveSpeed()));
- } else if (moveBackward) {
- this.translationWorldDelta.copy(dir.multiplyScalar(-this.currentViewport.getMoveSpeed()));
- }
- }else {
- if (moveForward && moveBackward) {
- this.translationDelta.y = 0;
- } else if (moveForward) {
- this.translationDelta.y = this.currentViewport.getMoveSpeed();
- } else if (moveBackward) {
- this.translationDelta.y = -this.currentViewport.getMoveSpeed();
- }
- }
- if (moveLeft && moveRight) {
- this.translationDelta.x = 0;
- } else if (moveLeft) {
- this.translationDelta.x = -this.currentViewport.getMoveSpeed();
- } else if (moveRight) {
- this.translationDelta.x = this.currentViewport.getMoveSpeed();
- }
- if (moveUp && moveDown) {
- this.translationWorldDelta.z = 0;
- } else if (moveUp) {
- this.translationWorldDelta.z = this.currentViewport.getMoveSpeed();
- } else if (moveDown) {
- this.translationWorldDelta.z = -this.currentViewport.getMoveSpeed();
- }
-
-
- if(moveUp || moveDown || moveForward || moveBackward){
- this.useAttenuation = false;
- }
-
- }
- }
- { // apply rotation
- let yaw = view.yaw;
- let pitch = view.pitch;
-
-
- yaw += this.yawDelta; /* * delta; */
- pitch += this.pitchDelta;/* * delta; */
- view.yaw = yaw;
- view.pitch = pitch;
-
-
- this.yawDelta = 0;
- this.pitchDelta = 0;
- }
- if(this.translationWorldDelta.length()>0) {
- // console.log('translationDelta')
- }
- { // apply translation
- view.translate(
- this.translationDelta.x, /* * delta, */
- this.translationDelta.y, /* * delta, */
- this.translationDelta.z, /* * delta */
- );
- this.translationDelta.set(0,0,0);
- //if(this.translationWorldDelta.length())console.log(translationWorldDelta)
-
- view.translateWorld(
- this.translationWorldDelta.x /* * delta */,
- this.translationWorldDelta.y /* * delta */,
- this.translationWorldDelta.z /* * delta */
- );
-
-
-
- //this.translationWorldDelta.set(0,0,0)
- }
- { // set view target according to speed
- //view.radius = 1 * this.currentViewport.getMoveSpeed();
-
- /* if(viewer.bound) view.radius = view.position.distanceTo(viewer.bound.center)
- let speed = view.radius/100;
- this.viewer.setMoveSpeed(speed); */
- //this.setMoveSpeed()
-
-
- }
-
- if(this.useAttenuation){ //只有滚轮缩放时开启
- let attenuation = Math.max(0, 1 - this.fadeFactor * delta);
-
- /*this.yawDelta *= attenuation;
- this.pitchDelta *= attenuation;
- this.translationDelta.multiplyScalar(attenuation);*/
- this.translationWorldDelta.multiplyScalar(attenuation);
- }else {
-
- this.translationWorldDelta.set(0,0,0);
-
- }
- }
- };
- class Viewport{
-
- constructor( view, camera, prop={}){//目前不支持换camera
-
- this.left = prop.left;
- this.bottom = prop.bottom;
- this.width = prop.width;
- this.height = prop.height;
- this.name = prop.name;
- this.view = view;
- this.camera = camera;
- this.active = true;
- this.unableChangePos = false;
- this.noPointcloud;
- //this.keys = [...] firstPersonCtl....
- this.resolution = new Vector2$1;
- this.resolution2 = new Vector2$1;
- this.offset = new Vector2$1; //viewportOffset 范围从0-整个画布的像素
- this.extraEnableLayers = prop.extraEnableLayers || [];//额外可展示的层
- this.cameraLayers = prop.cameraLayers;
- this.pixelRatio = prop.pixelRatio; //如果规定pixelRatio的话要传,这样就覆盖devicePicelRatio, 如magnifier
- }
-
-
- clone(){
- return Common.CloneClassObject(this)
-
- }
-
- getMoveSpeed(){
- return this.moveSpeed
- }
- setMoveSpeed(e){
- this.moveSpeed = e;
- }
-
- layersAdd(name){
- this.extraEnableLayers.includes(name) || this.extraEnableLayers.push(name);
-
- }
- layersRemove(name){
- let index = this.extraEnableLayers.indexOf(name);
- if(index > -1){
- this.extraEnableLayers.splice(index, 1);
- }
- }
-
- cameraChanged() {
- var copy = ()=>{
- this.previousState = {
- projectionMatrix: this.camera.projectionMatrix.clone(),//worldMatrix在this.control时归零了所以不用了吧,用position和qua也一样
- position: this.camera.position.clone(),
- quaternion: this.camera.quaternion.clone()
-
- };
- };
- let projectionChanged = true;
- let positionChanged = true;
- let quaternionChanged = true;
- let getChanged = ()=>{
- return {
- projectionChanged,positionChanged,quaternionChanged,
- changed:projectionChanged || positionChanged || quaternionChanged
- }
- };
- if (this.previousState){
- projectionChanged = !this.camera.projectionMatrix.equals(this.previousState.projectionMatrix);
- positionChanged = !this.camera.position.equals(this.previousState.position);
- quaternionChanged = !this.camera.quaternion.equals(this.previousState.quaternion);
- }
- copy();
-
- return getChanged()
- }
-
- setResolution(w,h, wholeW=0, wholeH=0){
- this.resolution.set(w,h);//是client的width height
-
- this.resolution2.copy(this.resolution).multiplyScalar(this.pixelRatio || window.devicePixelRatio);
-
- this.offset.set(wholeW,wholeH).multiply(new Vector2$1(this.left,this.bottom)).multiplyScalar(window.devicePixelRatio);
- }
- }
- /**
- * @author mschuetz / http://mschuetz.at
- *
- *
- */
- class InputHandler extends EventDispatcher {
- constructor (viewer,scene) {
- super();
- this.viewer = viewer;
- this.renderer = viewer.renderer;
- this.domElement = this.renderer.domElement;
- this.enabled = true;
-
- this.scene = scene;
- this.interactiveScenes = [];
- this.interactiveObjects = new Set();
- this.inputListeners = [];
- this.blacklist = new Set();
- this.drag = null;
- this.mouse = new Vector2$1(0, 0);
- //add:
- this.pointer = new Vector2$1(0, 0); //交互点的屏幕坐标,有别于DOM坐标,在此存放NDC坐标。(NDC,三维常用坐标系,二维坐标,整个屏幕映射范围(-1,1),屏幕中心为原点,+Y朝上,+X朝右)
- this.mouseDownMouse = new Vector2$1(0, 0);
-
- this.selection = [];
- this.hoveredElements = [];
- this.pressedKeys = {};
-
- this.wheelDelta = 0;
- this.speed = 1;
- this.logMessages = false;
- if (this.domElement.tabIndex === -1) {
- this.domElement.tabIndex = 2222;
- }
-
-
- this.touches = [];
- this.hoverViewport = viewer.viewports[0];
-
-
-
- this.domElement.addEventListener('contextmenu', (event) => { event.preventDefault(); }, false);
- this.domElement.addEventListener('click', this.onMouseClick.bind(this), false);
- this.domElement.addEventListener('mousedown', this.onMouseDown.bind(this), false);
- window.addEventListener('mouseup', this.onMouseUp.bind(this), false);
- this.domElement.addEventListener('mousemove', this.onMouseMove.bind(this), false);
- //add
- /* this.domElement.addEventListener("pointerout", this.onMouseUp.bind(this)),
- this.domElement.addEventListener("pointercancel", this.onMouseUp.bind(this)),
-
- */
-
- this.domElement.addEventListener('mousewheel', this.onMouseWheel.bind(this), false);
- this.domElement.addEventListener('DOMMouseScroll', this.onMouseWheel.bind(this), false); // Firefox
-
- this.domElement.addEventListener('dblclick', this.onDoubleClick.bind(this));
-
- this.domElement.addEventListener('keydown', this.onKeyDown.bind(this));
- window.addEventListener('keyup', this.onKeyUp.bind(this));
-
- //window.addEventListener('focus',()=>{
- window.addEventListener('blur',this.onKeyUp.bind(this)); //add
-
-
-
- this.domElement.addEventListener('touchstart', this.onTouchStart.bind(this));
- this.domElement.addEventListener('touchend', this.onTouchEnd.bind(this));
- this.domElement.addEventListener('touchmove', this.onTouchMove.bind(this));
-
-
- {
- this.addEventListener('isMeasuring',(e)=>{
- //console.log('isMeasuring',e.v,e.cause)
- this.isMeasuring = e.v;
- });
- }
-
-
- }
- /* addInputListener (listener) {
- this.inputListeners.push(listener);
- }
- removeInputListener (listener) {
- this.inputListeners = this.inputListeners.filter(e => e !== listener);
- }
- getSortedListeners(){
- return this.inputListeners.sort( (a, b) => {
- let ia = (a.importance !== undefined) ? a.importance : 0;
- let ib = (b.importance !== undefined) ? b.importance : 0;
- return ib - ia;
- });
- } */
- //统一跟第一个触碰的viewport相同
- updateTouchesInfo(e){
- var viewport, pointer, camera;
- let oldTouches = this.touches;
- let changedTouches = Array.from(e.changedTouches);
- let touches = Array.from(e.touches);
- this.touches = touches.map(touch=>{
- let touch_ = oldTouches.find(a=>a.touch.identifier == touch.identifier);
- let pointer = touch_ && touch_.pointer; //复制原先的值
- return {
- touch, pointer,
- }
- });
- if(e.touches.length > 0){
-
- let newTouches = touches.filter(e=>!
- oldTouches.some(a=>a.touch.identifier == e.identifier) && !changedTouches.some(a=>a.identifier == e.identifier)
- ); //从按钮处划过时e.touches中会出现this.touches和changedTouches中都没有的identifier
- if(newTouches.length>0){
- console.warn('has new',newTouches.map(e=>e.identifier));
- }
-
- newTouches.concat(changedTouches).forEach(touch=>{ //修改changedTouches的
- let touch_ = this.touches.find(a=>a.touch.identifier == touch.identifier);
- if(touch_){
- let a = this.getPointerInViewport(touch.pageX, touch.pageY, this.dragViewport||viewport, new Vector2$1);
- touch_.pointer = a.pointer.clone();
- viewport = a.viewport; camera = a.camera;
- }
- });
-
-
-
- //使用当前touches的平均
- if(e.touches.length > 1){
- let pageX = Common.average(e.touches, "pageX");
- let pageY = Common.average(e.touches, "pageY");
- let a = this.getPointerInViewport(pageX, pageY, viewport, new Vector2$1);
- this.pointer.copy(a.pointer);
- //console.log('updateTouchesInfo', this.pointer.clone())
-
- }else {
- this.pointer = this.touches[0].pointer.clone(); //更新,使用当前touches中的第一个
- }
-
-
- /* if(this.touches.find(e=>!e.pointer)){
- console.error(' touches has no pointer', oldTouches.map(e=>e.touch.identifier),
- Array.from(e.touches).map(e=>e.identifier), Array.from(e.changedTouches).map(e=>e.identifier) )
- } */
- //console.log(this.touches)
-
- //console.log('更新pointer1',this.pointer.toArray())
- return {viewport, camera/* , pointer:this.pointer */}
- }
-
-
- }
-
- onTouchStart (e) {
- if (this.logMessages) console.log(this.constructor.name + ': onTouchStart');
- e.preventDefault();
-
- /* if (e.touches.length === 1 || !this.drag) { //!this.drag代表一次性下了两个指头
- let rect = this.domElement.getBoundingClientRect();
- let x = e.touches[0].pageX
- let y = e.touches[0].pageY
- this.dealPointerDown(x,y,e,true)
- }else{
- this.updateTouchesInfo(e)
- this.drag.end.copy(this.pointer)
- } */
-
-
- this.dealPointerDown(e,true);
-
-
- this.viewer.dispatchEvent($.extend(
- this.getEventDesc(e,true),
- {
- type: 'global_' + e.type,
- changedTouches: e.changedTouches
- }
- ));
-
- /* console.log('targetTouches :', Array.from(e.targetTouches).map(e=>'| identifier: '+ e.identifier),
- 'changedTouches :', Array.from(e.changedTouches).map(e=>'| identifier: '+ e.identifier)
- ) */
- //console.log('')
- }
-
-
-
-
- onTouchMove (e) {
- if (this.logMessages) console.log(this.constructor.name + ': onTouchMove');
- e.preventDefault();
-
- /* if (e.touches.length === 1) {
- let rect = this.domElement.getBoundingClientRect();
- let x = e.touches[0].pageX;
- let y = e.touches[0].pageY;
-
-
-
- }else{
- this.updateTouchesInfo(e)
- this.drag.pointerDelta.subVectors(this.pointer, this.drag.end)
- this.drag.end.copy(this.pointer)
- }
- */
-
- this.dealPointerMove(e, true);
-
-
-
-
- this.viewer.dispatchEvent($.extend(
- this.getEventDesc(e,true),
- {
- type: 'global_' + e.type,
- changedTouches: e.changedTouches
- }
- ));
-
-
- /* console.log('targetTouches :', Array.from(e.targetTouches).map(e=>'| identifier: '+ e.identifier),
- 'changedTouches :', Array.from(e.changedTouches).map(e=>'| identifier: '+ e.identifier)
- ) */
- }
-
-
- onTouchEnd (e) {
- if (this.logMessages) console.log(this.constructor.name + ': onTouchEnd');
- e.preventDefault();
- //console.log('onTouchEnd')
- this.updateTouchesInfo(e);
-
- /* if (e.touches.length === 0) {
- let rect = this.domElement.getBoundingClientRect();
- let x = e.changedTouches[0].pageX //万一一次松开两个指头的怎么办
- let y = e.changedTouches[0].pageY
-
- this.dealPointerUp(x,y,e,true)
-
- }else {
- this.drag.end.copy(this.pointer)
- } */
- this.dealPointerUp(e,true);
-
-
-
- this.viewer.dispatchEvent($.extend(
- this.getEventDesc(e,true),
- {
- type: 'global_' + e.type,
- }
- ));
- //console.log('touchend length '+e.touches.length, this.touches.length)
- }
-
- onKeyDown (e) {
- if (this.logMessages) console.log(this.constructor.name + ': onKeyDown');
- // DELETE
- if (e.keyCode === KeyCodes.DELETE && this.selection.length > 0) {
- this.dispatchEvent({
- type: 'delete',
- selection: this.selection
- });
- this.deselectAll();
- }
- this.dispatchEvent({
- type: 'keydown',
- keyCode: e.keyCode,
- event: e
- });
- // for(let l of this.getSortedListeners()){
- // l.dispatchEvent({
- // type: "keydown",
- // keyCode: e.keyCode,
- // event: e
- // });
- // }
- this.pressedKeys[e.keyCode] = true;
- // e.preventDefault();
- }
- onKeyUp (e) {
- if (this.logMessages) console.log(this.constructor.name + ': onKeyUp');
-
- if(e.keyCode != void 0){
- delete this.pressedKeys[e.keyCode];
- }else {
- this.pressedKeys = {};
- }
-
-
- e.preventDefault();
- }
-
- onDoubleClick (e) {
- if (this.logMessages) console.log(this.constructor.name + ': onDoubleClick');
- let consumed = false;
- for (let hovered of this.hoveredElements) {
- if (hovered._listeners && hovered._listeners['dblclick']) {
- hovered.object.dispatchEvent({
- type: 'dblclick',
- mouse: this.mouse,
- object: hovered.object
- });
- consumed = true;
- break;
- }
- }
- if (!consumed) {
- /* for (let inputListener of this.getSortedListeners()) {
- inputListener. */this.viewer.dispatchEvent({
- type: 'global_dblclick',
- mouse: this.mouse,
- object: null
- });
- //}
- }
- e.preventDefault();
- }
- onMouseClick (e) {
- if (this.logMessages) console.log(this.constructor.name + ': onMouseClick');
- e.preventDefault();
- }
- dealPointerDown(e,isTouch){
- e.preventDefault();
-
- //重新获取一下pointer, 因点击了浏览器的按钮展开列表时 move回来不会触发onmousemove,所以pointer是旧的
-
- if(isTouch){
- var { camera, viewport } = this.updateTouchesInfo(e);
- if(this.drag){
- //因为触屏在按下前缺少pointermove所以要更新下
- this.drag.end = this.pointer.clone();
- }
-
-
- }else {
-
- var { camera, viewport } = this.getPointerInViewport(e.clientX, e.clientY );
- }
-
-
- this.dragViewport = this.hoverViewport = viewport;
-
-
- if(isTouch){
- this.hoveredElements = this.getHoveredElements();
-
- let intersectPoint = this.getIntersect(viewport );
-
-
- }
-
- if(!viewport)return
-
- if(!isTouch || e.touches.length == 1){
-
- let consumed = false;
- let consume = () => { return consumed = true; };
- //if (this.hoveredElements.length === 0) {
- this.viewer.dispatchEvent($.extend(
- this.getEventDesc(e,isTouch),
- {
- type: 'global_mousedown'
- }
- ));
-
-
- for(let hovered of this.hoveredElements){
- let object = hovered.object;
- object.dispatchEvent({
- type: 'mousedown',
- viewer: this.viewer,
- consume: consume
- });
- if(consumed){
- break;
- }
- }
- }
-
-
- if (!this.drag) {
- let target = (isTouch||e.button == MOUSE.LEFT) && this.hoveredElements.find(el => (//只有左键能拖拽
- el.object._listeners &&
- el.object._listeners['drag'] &&
- el.object._listeners['drag'].length > 0));
- if (target) {
- this.startDragging(target.object, {location: target.point});
- } else {
- this.startDragging(null);
- }
- }
-
-
- this.drag.intersectStart = this.intersectPoint;
- this.mouseDownMouse = this.mouse.clone();
-
- this.pointerDownTime = Date.now();
- }
- onMouseDown (e) {
-
- if (this.logMessages) console.log(this.constructor.name + ': onMouseDown');
- this.dealPointerDown(e);
-
- }
- getWholeIntersect(){//add
- if(Potree.settings.intersectOnObjs && this.hoveredElements[0] && this.hoveredElements[0].object.isModel){
- return {//模拟点云的intersectPoint的结构写法
- hoveredElement : this.hoveredElements[0] ,
- location: this.hoveredElements[0].point,
- point: {normal: this.hoveredElements[0].face.normal },
- distance: this.hoveredElements[0].distance,
- object: this.hoveredElements[0].object
- }
- }else return this.intersectPoint
- }
- getEventDesc(e,isTouch){//搜集dispatchEvent要给的一般数据
- let o = {
- viewer: this.viewer,
- mouse: this.mouse,
- pointer:this.pointer,
- drag :this.drag,
- isTouch,
- dragViewport : this.dragViewport,
- hoverViewport: this.hoverViewport,
- // button: isTouch ? 0 : e.button,
- intersectPoint:this.intersectPoint,
- hoveredElement: this.hoveredElements[0],
- intersect: this.getWholeIntersect() , //可能包含mesh上的,针对融合页面
- };
-
-
-
- if(e){
- o.isAtDomElement = e.target == this.domElement;
- }
- if(isTouch){
- o.touches = this.touches;
- }else if(e){
- o.button = e.button;
- o.buttons = e.buttons;
- }
- return o;
- }
- dealPointerUp(e,isTouch){
-
- if(!this.drag){// 在canvas外mousedown
- return
- }
-
- this.drag.end.copy(this.pointer);
-
- if(isTouch && e.touches.length >= 1){
- return
- }
-
-
- if (this.logMessages) console.log(this.constructor.name + ': onMouseUp');
- e.preventDefault();
-
- let pressDistance = this.mouseDownMouse.distanceTo(this.mouse);
- let pressTime = Date.now() - this.pointerDownTime;
-
- let noMovement = this.drag.pointerDelta.length() == 0;//this.getNormalizedDrag().length() === 0;
-
- let consumed = false;
- let consume = () => { return consumed = true; };
- //if (this.hoveredElements.length === 0) {
- /* for (let inputListener of this.getSortedListeners()) {
- inputListener */this.viewer.dispatchEvent($.extend(
- this.getEventDesc(e,isTouch),
- {
- type: 'global_mouseup',
- pressDistance,
- consume,
- }
- ));
- /* if(consumed){//??
- break;
- } */
- //}
- //}
- if (this.hoveredElements.length > 0) {
- let hovered = this.hoveredElements
- .map(e => e.object)
- .find(e => (e._listeners && e._listeners['mouseup']));
- if(hovered){
- hovered.dispatchEvent({
- type: 'mouseup',
- viewer: this.viewer,
- consume: consume
- });
- }
- }
-
-
-
- if (this.drag) {
- //拖拽结束
-
- if (this.drag.object/* && e.button == THREE.MOUSE.LEFT */) {//add LEFT
- if (this.logMessages) console.log(`${this.constructor.name}: drop ${this.drag.object.name}`);
-
- this.drag.object.dispatchEvent($.extend(
- this.getEventDesc(e,isTouch),
- {
- type: 'drop',
- pressDistance,
- }
- ));
-
-
- } else {
- this.viewer.dispatchEvent($.extend(
- this.getEventDesc(e,isTouch),
- {
- type: 'global_drop',
- pressDistance
- }
- ));
-
-
- }
- // check for a click
-
- if(pressDistance < Potree.config.clickMaxDragDis && pressTime<Potree.config.clickMaxPressTime){
- let clickElement;
- if(this.hoveredElements){
- clickElement = this.hoveredElements.find(e=>e.object._listeners['click']);
- if(clickElement){
- if (this.logMessages) console.log(`${this.constructor.name}: click ${clickObject.name}`);
- clickElement.object.dispatchEvent($.extend(
- this.getEventDesc(e,isTouch),
- {
- type: 'click',
- pressDistance
- }
- ));
- }
- }
-
- if(!clickElement){
- this.viewer.dispatchEvent($.extend(
- this.getEventDesc(e,isTouch),
- {
- type: 'global_click',
- pressDistance
- }
- ));
- }
- }
-
-
- this.drag = null;
-
- }
- this.dragViewport = null;
- if(!consumed && !this.fixSelection){
- if (e.button === MOUSE.LEFT) {
- if (noMovement) {
- let selectable = this.hoveredElements
- .find(el => el.object._listeners && el.object._listeners['select']);
- if (selectable) {
- selectable = selectable.object;
- if (this.isSelected(selectable)) {
- this.selection
- .filter(e => e !== selectable)
- .forEach(e => this.toggleSelection(e));
- } else {
- this.deselectAll();
- this.toggleSelection(selectable);
- }
- } else {
- this.deselectAll();
- }
- }
- } else if ((e.button === MOUSE.RIGHT) && noMovement) {
- this.deselectAll();
- }
- }
- }
-
- onMouseUp (e) {
- this.dealPointerUp( e );
- }
- getPointerInViewport(clientX, clientY, viewForceAt, pointer ){
- let rect = this.domElement.getBoundingClientRect();
- let x = clientX - rect.left;
- let y = clientY - rect.top;
- let camera;
- let viewport;
- pointer = pointer ||this.pointer;
- //if(this.viewer.viewports || viewForceAt){
- var getDimension = (view)=>{
- var left = Math.ceil(this.domElement.clientWidth * view.left)
- , bottom = Math.ceil(this.domElement.clientHeight * view.bottom)
- , width = Math.ceil(this.domElement.clientWidth * view.width)
- , height = Math.ceil(this.domElement.clientHeight * view.height)
- , top = this.domElement.clientHeight - bottom - height;
- return {left, bottom, width, height, top}
- };
- var getView = (view, left, bottom, width, height, top)=>{
- this.mouse.set(x-left, y - top );
- Utils.convertScreenPositionToNDC(pointer, this.mouse, width, height);
- //console.log('更新pointer2',this.pointer.toArray())
- camera = view.camera;
- viewport = view;
- };
-
- if(viewForceAt){
- let {left, bottom, width, height, top} = getDimension(viewForceAt);
- getView(viewForceAt, left, bottom, width, height, top);
-
-
- }else {
- var length = this.viewer.viewports.length;
- //var getif = false
- for(var i=0;i<length;i++){
- var view = this.viewer.viewports[i];
- if(!view.active)continue
- var {left, bottom, width, height, top} = getDimension(view);
-
-
- if(x >= left && x <= left + width && y >= top && y <= top + height){
- getView(view, left, bottom, width, height, top);
-
- //getif = true
- break;
- }
-
- }
-
-
- }
-
- return {
- camera, viewport, pointer
- }
- }
- ifBlockedByIntersect(point, margin=0, usePointcloud, cameraPos, pickWindowSize, pano){//某点是否被遮挡(不允许camera修改位置, 因为depthTex不好置换)
-
- if(cameraPos){
- usePointcloud = true; //只有使用点云才允许换位置
- }
-
- let intersectPoint = this.getIntersect(this.hoverViewport, true, pickWindowSize, null, usePointcloud, {point, cameraPos, pano});
- let cameraPos_ = (!usePointcloud && pano) ? pano.position : (cameraPos||this.hoverViewport.view.position);
- if(intersectPoint && intersectPoint.distance+margin <= point.distanceTo(cameraPos_)){
- return intersectPoint //被遮挡
- }
- //点云模式,对没加载出的点云不准确。 尤其是需要修改相机位置时,因临时修改并不能使点云加载。
- }
- getIntersect(viewport, onlyGetIntersect, pickWindowSize, dontIntersectPointcloud, usePointcloud, prop={}){
- let intersectPoint;
- let camera = viewport.camera;
-
- if(Potree.settings.displayMode == 'showPanos' && viewer.images360.currentPano.pointcloud.hasDepthTex && !usePointcloud && !this.isMeasuring && viewport == viewer.mainViewport ){
- let raycaster;
- /* if(prop.point){
- raycaster = new THREE.Raycaster()
- var dir = new THREE.Vector3().subVectors(prop.point, camera.position).normalize()
- raycaster.set(camera.position, dir) //var origin = new THREE.Vector3(pointer.x, pointer.y, -1).unproject(camera),
- } */
- let intersect;
- if(prop.point){
- let cameraPos = prop.pano ? prop.pano.position : camera.position;
- let dir = new Vector3().subVectors(prop.point, cameraPos).normalize();
- intersect = {dir};
- }else {
- intersect = Utils.getIntersect(camera, [viewer.images360.cube], this.pointer, raycaster);
- }
- intersectPoint = viewer.images360.depthSampler.sample(intersect, prop.pano, !!prop.point); //可能不准确, 因pano可能未加载depthTex
-
- }else {
- if(prop.point){
- prop.cameraPos && camera.position.copy(prop.cameraPos);
- camera.lookAt(prop.point);
- camera.updateMatrixWorld();
- prop.pointer = this.pointer.clone();
- prop.mouse = this.mouse.clone();
- this.pointer.set(0,0); //画布中心
- this.mouse.set(Math.round(viewport.resolution.x/2), Math.round(viewport.resolution.y/2));
- }
-
- intersectPoint = (viewport.noPointcloud || dontIntersectPointcloud)? null : Utils.getMousePointCloudIntersection(
- viewport,
- this.mouse,
- this.pointer,
- camera,
- this.viewer,
- this.viewer.scene.pointclouds,
- {pickClipped: true, isMeasuring: this.isMeasuring, pickWindowSize, cameraChanged: !!prop.point }
-
- );
- //恢复
- if(prop.point){
- viewport.view.applyToCamera(camera);
- this.pointer.copy(prop.pointer);
- this.mouse.copy(prop.mouse);
- }
- }
-
- //console.log(viewport.name , intersectPoint && intersectPoint.location )
-
- if(viewport.camera.type == 'OrthographicCamera'/* == 'mapViewport' */){
- let pos3d = new Vector3(this.pointer.x,this.pointer.y,-1).unproject(viewport.camera); //z:-1朝外
-
- if(!intersectPoint){
- intersectPoint = {};
- }
- intersectPoint.orthoIntersect = pos3d.clone();
- }
- if(onlyGetIntersect){
- return intersectPoint
- }
-
- if (intersectPoint) {
- if(viewer.showCoordType){ //显示坐标位置时
- let pos = intersectPoint.point.position.toArray();
- if(viewer.showCoordType == "local"){
-
- }else if(viewer.showCoordType == "lonlat"){
- pos = viewer.transform.lonlatToLocal.inverse(pos);
- }else {
- pos = viewer.transform.lonlatToLocal.inverse(pos);
- pos = viewer.transform.lonlatTo4550.forward(pos);
- }
-
- viewer.dispatchEvent({
- type : "coordinateChange", pos
- });
- }
- }
- //console.log('getIntersect', !!intersectPoint)
-
- this.intersectPoint = intersectPoint;
-
- intersectPoint && (this.hoverViewport.lastIntersect = intersectPoint);
-
- return intersectPoint
- }
-
-
-
-
-
- onMouseMove (e) {
- return this.dealPointerMove( e )
- }
- dealPointerMove(e, isTouch){
- if(isTouch){
- var { camera, viewport } = this.updateTouchesInfo(e);
- }else {
- var { camera, viewport } = this.getPointerInViewport(e.clientX, e.clientY, this.dragViewport);
- }
-
- this.hoverViewport = viewport;
- if(!viewport)return//刚变化viewport时会找不到
-
-
- let intersectPoint;
-
-
- if(e.onlyGetIntersect || !this.drag || this.drag.object || viewport.alignment ){ //没有拖拽物体,但按下鼠标了的话,不intersect
-
- let dontIntersectPointcloud = this.drag && viewport.alignment && Potree.settings.editType == 'pano' || viewer.images360.flying; // flying 时可能卡顿
- //console.log('dontIntersectPointcloud',dontIntersectPointcloud)
- intersectPoint = this.getIntersect(viewport, e.onlyGetIntersect, e.pickWindowSize, dontIntersectPointcloud, e.whichPointcloud); //数据集多的时候卡顿
- //console.log('intersectPoint', intersectPoint)
- }
-
- if(e.onlyGetIntersect){
- return intersectPoint
- }
- e.preventDefault();
-
-
- let hoveredElements = [];
-
-
- /* if(intersectPoint && intersectPoint.pointcloud){
- console.log(intersectPoint.pointcloud.name)
- } */
-
-
- if (this.drag) {//有拖拽(不一定拖拽了物体, 也不一定按下了鼠标)
- this.drag.mouse = isTouch ? 1 : e.buttons;
- //add:
- //this.drag.pointer = this.pointer.clone();
- //this.drag.hoverViewport = this.hoverViewport
- this.drag.pointerDelta.subVectors(this.pointer, this.drag.end);
- this.drag.end.copy(this.pointer);
-
-
- if (this.drag.object && (e.buttons == Buttons.NONE || !this.drag.notPressMouse )){//如果是本不需要按鼠标的拖拽,但按下了鼠标,就不执行这段(改为拖拽场景,如添加测量时突然拖拽画面)
- if (this.logMessages) console.log(this.constructor.name + ': drag: ' + this.drag.object.name);
-
- this.drag.object.dispatchEvent($.extend(
- this.getEventDesc(e,isTouch),
- {
- type: 'drag', //拖拽物体
- }
- ));
-
-
- } else {
-
-
-
- if (this.logMessages) console.log(this.constructor.name + ': drag: ');
- let dragConsumed = false;
- this.viewer.dispatchEvent($.extend(
- this.getEventDesc(e,isTouch),
- {
- type: 'global_drag', //拖拽画面
- consume: () => {dragConsumed = true;}
- }
- ));
-
-
- }
- }
-
-
- if(!isTouch || e.touches.length == 1){
-
- if(!this.drag || this.drag.notPressMouse ){
- hoveredElements = this.getHoveredElements();
- if(hoveredElements.length > 0){
- let names = hoveredElements.map(h => h.object.name).join(", ");
- if (this.logMessages) console.log(`${this.constructor.name}: onMouseMove; hovered: '${names}'`);
- }
-
- let curr = hoveredElements.map(a => a.object).find(a => true);//只取第一个
- let prev = this.lastMouseoverElement; //this.hoveredElements.map(a => a.object).find(a => true);
- if(curr !== prev){
- if(curr){
- if (this.logMessages) console.log(`${this.constructor.name}: mouseover: ${curr.name}`);
- curr.dispatchEvent({
- type: 'mouseover',
- object: curr,
- });
- }
- if(prev){
- if (this.logMessages) console.log(`${this.constructor.name}: mouseleave: ${prev.name}`);
- prev.dispatchEvent({
- type: 'mouseleave',
- object: prev,
- });
- }
-
- this.lastMouseoverElement = curr;
- }
- if(hoveredElements.length > 0){
- let object = hoveredElements
- .map(e => e.object)
- .find(e => (e._listeners && e._listeners['mousemove']));
-
- if(object){
- object.dispatchEvent({
- type: 'mousemove',
- object: object
- });
- }
- }
- }
-
-
- this.viewer.dispatchEvent($.extend(
- this.getEventDesc(e,isTouch),
- {
- type: 'global_mousemove',
-
- }
- ));
- this.hoveredElements = hoveredElements;
-
- }
-
-
-
- }
-
- onMouseWheel(e){
- if(!this.enabled) return;
- if(this.logMessages) console.log(this.constructor.name + ": onMouseWheel");
-
- e.preventDefault();
- let delta = 0;
- if (e.wheelDelta !== undefined) { // WebKit / Opera / Explorer 9
- delta = e.wheelDelta;
- } else if (e.detail !== undefined) { // Firefox
- delta = -e.detail;
- }
- let ndelta = Math.sign(delta);
- // this.wheelDelta += Math.sign(delta);
-
-
- if(!this.hoverViewport){//调试手机版时会无
- var { viewport } = this.getPointerInViewport(e.clientX, e.clientY );
- this.hoverViewport = viewport;
- }
-
- if (this.hoveredElement) {
- this.hoveredElement.object.dispatchEvent($.extend(
- this.getEventDesc(e,isTouch),
- {
- type: 'mousewheel',
- delta: ndelta,
- object: this.hoveredElement.object
- }
- ));
-
- } else {
- this.viewer.dispatchEvent($.extend(
- this.getEventDesc(e),
- {
- type: 'global_mousewheel',
- delta: ndelta,
- }
- ));
-
- }
- }
- startDragging (object, args = null) {
- let name = object ? object.name : "no name";
- if (this.logMessages) console.log(`${this.constructor.name}: startDragging: '${name}'`);
- this.drag = {
- start: this.pointer.clone(),
- end: this.pointer.clone(),
- pointerDelta: new Vector2$1(0, 0),
- object: object,
- hoverViewport: this.hoverViewport, //会变化
- dragViewport: this.hoverViewport, //不变
- };
- if (args) {
- for (let key of Object.keys(args)) {
- this.drag[key] = args[key];
- }
- }
-
- if(object){
- object.dispatchEvent($.extend(
- this.getEventDesc(),
- {
- type: 'startDragging'
- }
- ));
- }
-
-
- }
- /* getMousePointCloudIntersection (mouse) {
- return Utils.getMousePointCloudIntersection(
- this.mouse,
- this.scene.getActiveCamera(),
- this.viewer,
- this.scene.pointclouds);
- } */
- toggleSelection (object) {
- let oldSelection = this.selection;
- let index = this.selection.indexOf(object);
- if (index === -1) {
- this.selection.push(object);
- object.dispatchEvent({
- type: 'select'
- });
- } else {
- this.selection.splice(index, 1);
- object.dispatchEvent({
- type: 'deselect'
- });
- }
- this.dispatchEvent({
- type: 'selection_changed',
- oldSelection: oldSelection,
- selection: this.selection
- });
- }
- deselect(object){
- let oldSelection = this.selection;
- let index = this.selection.indexOf(object);
- if(index >= 0){
- this.selection.splice(index, 1);
- object.dispatchEvent({
- type: 'deselect'
- });
- this.dispatchEvent({
- type: 'selection_changed',
- oldSelection: oldSelection,
- selection: this.selection
- });
- }
- }
- deselectAll () {
- for (let object of this.selection) {
- object.dispatchEvent({
- type: 'deselect'
- });
- }
- let oldSelection = this.selection;
- if (this.selection.length > 0) {
- this.selection = [];
- this.dispatchEvent({
- type: 'selection_changed',
- oldSelection: oldSelection,
- selection: this.selection
- });
- }
- }
- isSelected (object) {
- let index = this.selection.indexOf(object);
- return index !== -1;
- }
- registerInteractiveObject(object){
- this.interactiveObjects.add(object);
- }
- removeInteractiveObject(object){
- this.interactiveObjects.delete(object);
- }
- registerInteractiveScene (scene) {
- let index = this.interactiveScenes.indexOf(scene);
- if (index === -1) {
- this.interactiveScenes.push(scene);
- }
- }
- unregisterInteractiveScene (scene) {
- let index = this.interactiveScenes.indexOf(scene);
- if (index > -1) {
- this.interactiveScenes.splice(index, 1);
- }
- }
- getHoveredElement () {
- let hoveredElements = this.getHoveredElements();
- if (hoveredElements.length > 0) {
- return hoveredElements[0];
- } else {
- return null;
- }
- }
-
- getHoveredElements () {
- let scenes = this.hoverViewport.interactiveScenes || this.interactiveScenes.concat(this.scene);
- let interactableListeners = ['mouseup', 'mousemove', 'mouseover', 'mouseleave', 'drag', 'drop', 'click', 'select', 'deselect'];
- let interactables = [];
- for (let scene of scenes) {
- scene.traverseVisible(node => {//检测加了侦听的object
- if (node._listeners && node.visible && (Potree.settings.intersectOnObjs || !this.blacklist.has(node))) {
- let hasInteractableListener = interactableListeners.filter((e) => {
- return node._listeners[e] !== undefined;
- }).length > 0;
- if (hasInteractableListener) {
- interactables.push(node);
- }
- }
- });
- }
-
- let camera = this.hoverViewport.camera;
- let ray = Utils.mouseToRay(this.pointer, camera );
-
- let raycaster = new Raycaster();
- raycaster.ray.set(ray.origin, ray.direction);
- raycaster.camera = camera; //add
-
-
- if(camera.type == "OrthographicCamera"){//使无论多远,threshold区域都是一样宽的
- raycaster.params.Line.threshold = 20/camera.zoom;
- }else {
- raycaster.params.Line.threshold = 0.2;
- }
- raycaster.params.Line2 = {threshold :20 }; //拓宽的lineWidth
-
-
-
- //raycaster.layers.enableAll()//add
- viewer.setCameraLayers(raycaster, //设置能识别到的layers(如空间模型里只有mapViewer能识别到marker)
- ['sceneObjects','mapObjects','measure', 'transformationTool'],
- this.hoverViewport && this.hoverViewport.extraEnableLayers
- );
-
-
- viewer.dispatchEvent( {type:'raycaster', viewport: this.hoverViewport});//add
- let intersections = raycaster.intersectObjects(interactables.filter(o => o.visible), true); //原本是false 检测不到children
- if(this.intersectPoint && this.intersectPoint.distance != void 0){//add
- intersections = intersections.filter(e=>{
- let material = e.object.material;
-
- return (material.depthTest == false || material.depthWrite == false) && !material.useDepth //!material.depthTestWhenPick
- || ( material instanceof DepthBasicMaterial ? e.distance < this.intersectPoint.distance + material.uniforms.occlusionDistance.value : e.distance < this.intersectPoint.distance )
- });
- }
- intersections = intersections.map(e=>{//add 转化为interactables
- var object = e.object;
- do{
- if(interactables.includes(object)) {
- e.oriObject = e.object;
- e.object = object;
- break
- }
- object = object.parent;
- }while(object)
-
- return e
- });
-
- //add for测量线,在检测到sphere时优先选中sphere而非线
- intersections = intersections.sort(function(a,b){return b.object.renderOrder-a.object.renderOrder}); // 降序
-
- return intersections;
- }
- /* setScene (scene) {
- this.deselectAll();
- this.scene = scene;
- } */
- update (delta) {
- }
- /*getNormalizedDrag () {
- if (!this.drag) {
- return new THREE.Vector2(0, 0);
- }
- let diff = new THREE.Vector2().subVectors(this.drag.end, this.drag.start);
- diff.x = diff.x / this.domElement.clientWidth;
- diff.y = diff.y / this.domElement.clientHeight;
- return diff;
- }
- getNormalizedLastDrag () {
- if (!this.drag) {
- return new THREE.Vector2(0, 0);
- }
- let mouseDelta = this.drag.mouseDelta.clone();
- mouseDelta.x = mouseDelta.x / this.domElement.clientWidth;
- mouseDelta.y = mouseDelta.y / this.domElement.clientHeight;
- return mouseDelta;
- } */
-
- getMouseDirection(pointer) {//add
- pointer = pointer || this.pointer;
- let camera = this.hoverViewport.camera;
- var t = new Vector3(pointer.x, pointer.y, -1).unproject(camera),
- i = new Vector3(pointer.x, pointer.y, 1).unproject(camera);
-
- return {origin: t, direction:i.clone().sub(t).normalize() }
-
- }
-
-
- }
- class ViewerBase extends EventDispatcher{
- constructor(domElement, args = {}){
- super();
- this.name = args.name;
- this.renderArea = domElement;
- this.oldResolution = new Vector2$1();
-
- this.screenSizeInfo = {
- W:0, H:0, pixelRatio:1 , windowWidth:0, windowHeight:0
- };
-
- this.initContext(args);
-
-
- this.addEventListener('content_changed', ()=>{//画面改变,需要渲染
- this.needRender = true;
- });
-
-
- }
-
-
-
-
-
- initContext(args){
- //console.log(`initializing three.js ${THREE.REVISION}`);
- let width = this.renderArea.clientWidth;
- let height = this.renderArea.clientHeight;
- let contextAttributes = {
- alpha: true,
- depth: true,
- stencil: false,
- antialias: true,
- preserveDrawingBuffer: true,
- powerPreference: "high-performance",
- };
- let canvas = document.createElement("canvas");
- let context = canvas.getContext('webgl', contextAttributes );
- this.renderer = new WebGLRenderer({
- alpha: true, //支持透明
- premultipliedAlpha: false,
- canvas: canvas,
- context: context,
- });
-
- this.renderer.sortObjects = true; //原先false 打开了renderOrder才奏效
- //this.renderer.setSize(width, height);
- this.renderer.autoClear = args.autoClear || false;
- //args.clearColor = args.clearColor || '#aa0033'
- args.clearColor && this.renderer.setClearColor(args.clearColor);
- this.renderArea.appendChild(this.renderer.domElement);
- this.renderer.domElement.tabIndex = '2222';
- this.renderer.domElement.style.position = 'absolute';
- this.renderer.domElement.addEventListener('mousedown', () => {
- this.renderer.domElement.focus();
- });
- //this.renderer.domElement.focus();
- // NOTE: If extension errors occur, pass the string into this.renderer.extensions.get(x) before enabling
- // enable frag_depth extension for the interpolation shader, if available
- let gl = this.renderer.getContext();
-
-
- gl.getExtension('EXT_frag_depth');
- gl.getExtension('WEBGL_depth_texture');
- gl.getExtension('WEBGL_color_buffer_float'); // Enable explicitly for more portability, EXT_color_buffer_float is the proper name in WebGL 2
-
- if(gl.createVertexArray == null){
- let extVAO = gl.getExtension('OES_vertex_array_object');
- if(!extVAO){
- throw new Error("OES_vertex_array_object extension not supported");
- }
- gl.createVertexArray = extVAO.createVertexArrayOES.bind(extVAO);
- gl.bindVertexArray = extVAO.bindVertexArrayOES.bind(extVAO);
- }
-
- /* let oldClear = gl.clear;
- gl.clear = (bits)=>{
- console.error('clear')
- }
- */
-
- }
-
-
-
-
- updateScreenSize(o={}) { //有可能需要让viewport来判断,当窗口大小不变但viewport大小变时
-
- var render = false, ratio, w, h;
- //记录应当render的大小
- if (o.width != void 0 && o.height != void 0) {
- w = o.width;
- h = o.height;
- render = true;
- ratio = 1;
- }else {
- w = this.renderArea.clientWidth;
- h = this.renderArea.clientHeight;
-
-
- if(w !== this.screenSizeInfo.W || h !== this.screenSizeInfo.H || o.forceUpdateSize || this.screenSizeInfo.pixelRatio != window.devicePixelRatio){
- this.screenSizeInfo.W = w;
- this.screenSizeInfo.H = h;
- render = true;
- this.screenSizeInfo.pixelRatio = window.devicePixelRatio; //如果player放在小窗口了,也要监测devicePixelRatio,因为缩放时client宽高不会改变
- //config.isMobile ? (ratio = Math.min(window.devicePixelRatio, 2)) : (ratio = window.devicePixelRatio)
- ratio = window.devicePixelRatio;
- }
- }
- if (render) {
- this.setSize(w, h, ratio);
- }
- }
- /* updateScreenSize(o={}) { //容易出错。。
-
- var render = false, ratio, w, h;
- //记录应当render的大小
- if (o.width != void 0 && o.height != void 0) {
- w = o.width
- h = o.height
- render = true
- ratio = 1
- }else {
- w = this.renderArea.clientWidth;
- h = this.renderArea.clientHeight
- let refreshWin = ()=>{
- this.screenSizeInfo.windowWidth = window.innerWidth
- this.screenSizeInfo.windowHeight = window.innerHeight
- }
-
- let prepareToRender = ()=>{
- w = this.renderArea.clientWidth;
- h = this.renderArea.clientHeight
- this.screenSizeInfo.W = w
- this.screenSizeInfo.H = h
- refreshWin() //render后才refreshWin
- render = true
- this.screenSizeInfo.pixelRatio = window.devicePixelRatio //如果player放在小窗口了,也要监测devicePixelRatio,因为缩放时client宽高不会改变
- //config.isMobile ? (ratio = Math.min(window.devicePixelRatio, 2)) : (ratio = window.devicePixelRatio)
- ratio = window.devicePixelRatio
- //console.log('setSize11', w)
- o.forceUpdateSize = false ;//防止再次render
- }
- let ifNeedUpdate = ()=>{
- w = this.renderArea.clientWidth;
- h = this.renderArea.clientHeight
- return o.forceUpdateSize || w !== this.screenSizeInfo.W || h !== this.screenSizeInfo.H || this.screenSizeInfo.pixelRatio != window.devicePixelRatio
- }
-
- if(ifNeedUpdate()){
- //当只有一个有效viewport时,且因为改变整个窗口大小而触发的话,延时
- let canInterval = !o.forceUpdateSize && this.viewports.filter(e=>e.active).length==1 && (this.screenSizeInfo.windowWidth != window.innerWidth || this.screenSizeInfo.windowHeight != window.innerHeight )
-
- if(canInterval){
- Common.intervalTool.isWaiting('updateScreenSize', ()=>{ //延时update,防止崩溃 , 未到时间就拦截(第一次直接执行)
- if(ifNeedUpdate()){
- prepareToRender()
- return true
- }
- }, 500)
- }else{
- //console.log('soon', window.innerWidth, this.screenSizeInfo.windowWidth)
- prepareToRender()
- }
-
- }
-
- }
- if (render) {
- this.setSize(w, h, ratio);
- }
- } */
-
- setSize(width, height, devicePixelRatio, onlyForTarget){
- //console.log('setSize', width)
- if(!onlyForTarget){//onlyForTarget表示不更改当前renderer,只是为了rendertarget才要改变viewport
- this.renderer.setSize(width, height, null, devicePixelRatio); // resize之后会自动clear(似乎因为setScissor ),所以一定要立刻绘制,所以setSize要在cameraChanged、update之前
- }
-
- this.composer && this.composer.setSize(width, height);
-
- if(this.viewports){
- this.viewports.forEach((view,i)=>{
- if(!view.active)return
-
- var width_ = width * view.width;
- var height_ = height * view.height;
-
- if(height_ == 0)return //avoid NAN
-
- view.setResolution(Math.ceil(width_), Math.ceil(height_), width, height ); //本来应该是floor,但是这样奇数时会少一个像素,导致向左移一个像素且宽度少1。现在则多绘制1个像素,超出的1个像素应该不会绘制出来(但不知道其他地方是否有偏差,比如pick时)
- let aspect = width_ / height_; //camera的参数精确些,不用视口的归整的resolution像素值,否则hasChange无法为true, 导致canvasResize了但map没update从而闪烁
- view.camera.aspect = aspect;
-
- if(view.camera.type == "OrthographicCamera"){
-
- /* //不改宽度 同4dkk
- var heightHalf = view.camera.right / aspect
- view.camera.top = heightHalf
- view.camera.bottom = -heightHalf */
- //高宽都改 使大小不随视口大小改变 navvis (直接和视口大小一致即可,通过zoom来定大小)
-
- view.camera.left = -width_/2;
- view.camera.right = width_/2;
- view.camera.bottom = -height_/2;
- view.camera.top = height_/2;
-
-
- }else {
-
-
- }
-
- view.camera.updateProjectionMatrix();
- });
- }
-
-
- if(!onlyForTarget){//因为onlyForTarget不传递devicePixelRatio所以不发送了
- this.emitResizeMsg({viewport:this.viewports[0], deviceRatio:devicePixelRatio});
- }
-
- }
-
- emitResizeMsg(e){//切换viewport渲染时就发送一次, 通知一些材质更新resolution。
- if(!e.viewport.resolution.equals(this.oldResolution)){
- this.dispatchEvent($.extend(e, {type:'resize'}));
- this.oldResolution.copy(e.viewport.resolution);
- }
-
-
- }
-
-
-
- cameraChanged() {//判断相机是否改变
- var changed = false;
- /* if(this.needRender){
- this.needRender = false
- return true
- } */
- for(let i=0,j=this.viewports.length;i<j;i++){
- let changeInfo = this.viewports[i].cameraChanged();
- if(changeInfo.changed){
- changed = true;
- //if(!this.changeTime ||this.changeTime<100){
- this.dispatchEvent({
- type: "camera_changed",
- camera: this.viewports[i].camera,
- viewport : this.viewports[i],
- changeInfo
- });
- //this.changeTime = (this.changeTime || 0) +1
- //}
- }
- }
- return changed
- }
-
-
-
-
-
- makeScreenshot( size, viewports, compressRatio){//暂时不要指定viewports渲染,但也可以
-
-
- let {width, height} = size;
-
-
-
-
- /* let oldBudget = Potree.pointBudget;
- Potree.pointBudget = Math.max(10 * 1000 * 1000, 2 * oldBudget);
- let result = Potree.updatePointClouds(this.scene.pointclouds, camera, size );
- Potree.pointBudget = oldBudget;
-
-
- this.dispatchEvent({ //resize everything such as lines targets
- type: 'resize',
- resolution: new THREE.Vector2(width,height),
- });*/
-
- let target = new WebGLRenderTarget(width, height, {
- format: RGBAFormat,
- });
-
-
- this.setSize(width, height,1,true);
-
- this.render({
- target ,
- //camera ,
- viewports: viewports || this.viewports,
- screenshot : true,
- width ,
- height,
- resize :true //需要resize
- });
- let dataUrl = Potree.Utils.renderTargetToDataUrl(target, width, height, this.renderer, compressRatio);
-
-
- /* let pixelCount = width * height;
- let buffer = new Uint8Array(4 * pixelCount);
- this.renderer.readRenderTargetPixels(target, 0, 0, width, height, buffer);
- let dataUrl = Potree.Utils.pixelsArrayToDataUrl(buffer, width, height, compressRatio) */
-
- target.dispose();
-
- //resize back
- //this.updateScreenSize({forceUpdateSize:true})
-
- return {
- width,
- height,
- dataUrl
- };
- }
-
- }
- const prefixVertex ="precision highp float;\nprecision highp int;\n\nuniform mat4 modelMatrix;\nuniform mat4 modelViewMatrix;\nuniform mat4 projectionMatrix;\nuniform mat4 viewMatrix;\nuniform mat3 normalMatrix;\nuniform vec3 cameraPosition;\nattribute vec3 position;\nattribute vec3 normal;\nattribute vec2 uv;\n";
- const prefixFragment ="precision highp float;\nprecision highp int;\n\nuniform mat4 viewMatrix;\nuniform vec3 cameraPosition;\n";
-
- let shader = {
-
- uniforms: {
-
- opacity: {
- type: "f",
- value: 1
- },
- progress: {
- type: "f",
- value: 0
- },
-
- pano0Map: {
- type: "t",
- value: null
- },
- pano1Map: {
- type: "t",
- value: null
- },
- depthMap0: {
- type: "t",
- value: null
- },
- depthMap1: {
- type: "t",
- value: null
- },
- pano0Position: {
- type: "v3",
- value: new Vector3
- },
- pano0Matrix: {
- type: "m4",
- value: new Matrix4
- },
-
- pano1Position: {
- type: "v3",
- value: new Vector3
- },
- pano1Matrix: {
- type: "m4",
- value: new Matrix4
- },
- /* pano1Matrix2: {
- type: "m4",
- value: new THREE.Matrix4
- },
- */
-
- inverseProjectionMatrix: {
- value: new Matrix4
- },
- /* projectionMatrix:{//需要再写一遍吗
- value: new THREE.Matrix4
- }, */
- viewport: {
- value: new Vector4
- },
- //如 {x: 0, y: 0, z: 428, w: 969} xy应该是offset, zw是宽高
- cameraHeight0: {
- type: "f",
- value: 1
- },
- cameraHeight1: {
- type: "f",
- value: 1
- },
-
- },
-
- vertexShader: prefixVertex + `
- uniform vec3 pano0Position;
- uniform mat4 pano0Matrix;
-
- uniform vec3 pano1Position;
- uniform mat4 pano1Matrix;
- //uniform mat4 pano1Matrix2;
-
- varying vec2 vUv;
- varying vec3 vWorldPosition0;
- varying vec3 vWorldPosition1;
- varying vec3 vWorldPosition12;
-
- vec3 transformAxis( vec3 direction ) //navvis->4dkk
- {
- float y = direction.y;
- direction.y = direction.z;
- direction.z = -y;
- return direction;
- }
-
-
- void main() {
-
- vUv = uv;
- vec4 worldPosition = modelMatrix * vec4(position, 1.0);
-
-
-
- vec3 positionLocalToPanoCenter0 = worldPosition.xyz - pano0Position;
- vWorldPosition0 = (vec4(positionLocalToPanoCenter0, 1.0) * pano0Matrix).xyz;
- vWorldPosition0.x *= -1.0;
- vWorldPosition0 = transformAxis(vWorldPosition0);
-
- vec3 positionLocalToPanoCenter1 = worldPosition.xyz - pano1Position;
- vWorldPosition1 = (vec4(positionLocalToPanoCenter1, 1.0) * pano1Matrix).xyz;
- vWorldPosition1.x *= -1.0;
- vWorldPosition1 = transformAxis(vWorldPosition1);
-
- /*
- vec3 positionLocalToPanoCenter12 = worldPosition.xyz - pano1Position;
- vWorldPosition12 = (vec4(positionLocalToPanoCenter12, 1.0) * pano1Matrix2).xyz;
- vWorldPosition12.x *= -1.0;
- vWorldPosition12 = transformAxis(vWorldPosition12);
- */
-
-
-
- gl_Position = projectionMatrix * modelViewMatrix * vec4( position, 1.0 );
-
- }
- `,
- fragmentShader: prefixFragment + `
-
- #define PI 3.141592653
-
-
- uniform float modelAlpha;
- uniform float opacity;
- uniform float progress;
- uniform int blackout;
- uniform vec3 pano0Position;
- uniform vec3 pano1Position;
- uniform float maxDistance;
- uniform float minDistance;
- uniform float minOpa;
-
- uniform float cameraHeight0;
- uniform float cameraHeight1;
-
-
- /* uniform sampler2D pano0Map;
- uniform sampler2D pano1Map; */
- uniform samplerCube pano0Map;
- uniform samplerCube pano1Map;
-
-
- varying vec2 vUv;
- varying vec3 vWorldPosition0;
- varying vec3 vWorldPosition1;
- //varying vec3 vWorldPosition12;
-
- /* vec2 getSamplerCoord( vec3 direction )
- {
- direction = normalize(direction);
- float tx=atan(direction.x,-direction.y)/(PI*2.0)+0.5;
- float ty=acos(direction.z)/PI;
- return vec2(tx,ty);
- } */
- vec2 getSamplerCoord2( vec3 direction )
- {
- direction = normalize(direction);
- float tx=atan(direction.x,direction.z)/(PI*2.0)+0.5;
- float ty=acos(direction.y)/PI;
- return vec2(tx,ty);
- }
-
- #extension GL_EXT_frag_depth : enable
- #if defined(GL_EXT_frag_depth) && defined(hasDepthTex)
- uniform sampler2D depthMap0;
- uniform sampler2D depthMap1;
- uniform mat4 inverseProjectionMatrix;
- uniform mat4 projectionMatrix;
- uniform vec4 viewport;
-
- vec2 getDepth(vec3 dir, sampler2D depthMap, float height, vec4 eyePos){
- vec2 depthValue = vec2(0.0, 0.0);
- vec2 uv2 = getSamplerCoord2(/* vWorldPosition12 */dir.xyz); //暂时只用基于目标漫游点的方向
- uv2.x -= 0.25; //全景图和Cube的水平采样起始坐标相差90度,这里矫正 0.25 个采样偏移
- vec4 depth = texture2D(depthMap, uv2);
- //float distance = depth.r + 256. * (depth.g + 256. * depth.b);
- //distance *= 255. * .001; // distance is now in meters
-
- //更改
- float distance = (depth.g + depth.r / 256.) * 255.; //为什么要乘以255
-
- if(distance == 0.0){//漫游点底部识别不到的区域,给一个地板高度
- if(uv2.y > 0.75)distance = height / dir.y;
- else distance = 100000.0;//给个超级远的值
- }
- depthValue.x = distance;
-
- // return r[1] + r[0] / 256
- distance += .1; // add a safety margin
- vec4 eyePos2 = vec4(normalize(eyePos.xyz) * distance, 1.);
- vec4 clipPos2 = projectionMatrix * eyePos2;
- vec4 ndcPos2 = clipPos2 * 1. / clipPos2.w;
-
- depthValue.y = 0.5 * ((gl_DepthRange.far - gl_DepthRange.near) * ndcPos2.z
- + gl_DepthRange.near + gl_DepthRange.far);
- return depthValue;
- }
- //注:未加载好的话,depth为0,导致第一次漫游过去的时候许多mesh会立刻被遮挡,所以要确保加载完
- #endif
-
- void main()
- {
-
- /* vec2 samplerCoord0 = getSamplerCoord(vWorldPosition0.xyz);
- vec2 samplerCoord1 = getSamplerCoord(vWorldPosition1.xyz);
- vec4 colorFromPano0=texture2D(pano0Map,samplerCoord0);
- vec4 colorFromPano1=texture2D(pano1Map,samplerCoord1); */
-
- vec4 colorFromPano0 = vec4(0.0,0.0,0.0,0.0);
- if(progress < 1.0){//通常是1
- colorFromPano0=textureCube(pano0Map,vWorldPosition0.xyz);
- }
- vec4 colorFromPano1=textureCube(pano1Map,vWorldPosition1.xyz);
-
- gl_FragColor=mix(colorFromPano0,colorFromPano1,progress);
-
-
-
-
- //深度图修改深度
-
- #if defined(GL_EXT_frag_depth) && defined(hasDepthTex)
- vec4 ndcPos;
- ndcPos.xy = ((2.0 * gl_FragCoord.xy) - (2.0 * viewport.xy)) / (viewport.zw) - 1.;
- ndcPos.z = (2.0 * gl_FragCoord.z - gl_DepthRange.near - gl_DepthRange.far) /
- (gl_DepthRange.far - gl_DepthRange.near);
- ndcPos.w = 1.0;
- vec4 clipPos = ndcPos / gl_FragCoord.w;
- vec4 eyePos = inverseProjectionMatrix * clipPos;
- vec2 depth0 = vec2(0.0,0.0);
- if(progress < 1.0){
- depth0 = getDepth(vWorldPosition0, depthMap0, cameraHeight0, eyePos);
- }
- vec2 depth1 = getDepth(vWorldPosition1, depthMap1, cameraHeight1, eyePos);
-
- /* if(progress < 1.0 && depth1.x == 0.0 && depth0.x > 0.0){
- gl_FragDepthEXT = depth0.y;
- }else{ */
- gl_FragDepthEXT = mix(depth0.y,depth1.y,progress);
- //}
-
-
- #endif
-
- }
- `
- };
-
-
- class ModelTextureMaterial extends RawShaderMaterial {
- constructor( ){
-
- let defines = {};
-
-
-
- super({
- fragmentShader: shader.fragmentShader,
- vertexShader: shader.vertexShader,
- uniforms: UniformsUtils.clone(shader.uniforms),
- side:DoubleSide,
- name: "ModelTextureMaterial",
- defines
- });
-
-
-
- let setSize = (e)=>{
- let viewport = e.viewport;
- let viewportOffset = viewport.offset || new Vector2();
- let resolution = viewport.resolution2;
- this.uniforms.viewport.value.set(viewportOffset.x, viewportOffset.y, resolution.x, resolution.y);
- };
- let viewport = viewer.mainViewport;
-
- setSize({viewport});
- viewer.addEventListener('resize',(e)=>{
- setSize(e);
- });
-
-
- //var supportExtDepth = !!Features.EXT_DEPTH.isSupported()
- {
-
- //add
-
- viewer.addEventListener('camera_changed', (e)=>{
- //this.uniforms.projectionMatrix.value.copy(e.camera.projectionMatrix)
- this.uniforms.inverseProjectionMatrix.value.copy(e.camera.projectionMatrixInverse);
- });
- }
-
- //-------------------------------------
- }
- /**
- *
- * @param {Panorama} pano0
- * @param {Panorama} pano1
- * @param {boolean} flag
-
- 更新全景图的材质uniforms
-
- */
-
-
-
- setProjectedPanos(pano0, pano1, progressValue ){
-
- progressValue!=void 0 && (this.uniforms.progress.value = progressValue);
- //pano0.ensureSkyboxReadyForRender();
-
-
- if(pano0){
- this.uniforms.pano0Map.value = pano0.getSkyboxTexture();//pano0.texture
- this.uniforms.pano0Position.value.copy(pano0.position);
- this.uniforms.pano0Matrix.value.copy(pano0.panoMatrix/* pano0.mesh.matrixWorld */ );
- this.uniforms.cameraHeight0.value = pano0.floorPosition.distanceTo(pano0.position);
-
- //pano1.ensureSkyboxReadyForRender();
- }
-
-
- this.uniforms.pano1Map.value = pano1.getSkyboxTexture();//pano1.texture;
- this.uniforms.pano1Position.value.copy(pano1.position);
- this.uniforms.pano1Matrix.value.copy(pano1.panoMatrix /* pano1.mesh.matrixWorld */ );
- this.uniforms.cameraHeight1.value = pano1.floorPosition.distanceTo(pano1.position);
- this.pano0 = pano0;
- this.pano1 = pano1;
-
- this.updateDepthTex(pano0);
- this.updateDepthTex(pano1);
-
-
- //console.log('setProjectedPanos', pano0&&pano0.id, pano1&&pano1.id)
- this.needsUpdate = true;
- }
-
-
-
- updateDepthTex(pano){
- if( !Potree.settings.useDepthTex || !pano || !pano.depthTex || pano!=this.pano0 && pano!=this.pano1)return
- //console.log('updateDepthTex', pano.id, this.pano0 && this.pano0.id, this.pano1 && this.pano1.id)
- this.uniforms.depthMap0.value = this.pano0 && this.pano0.depthTex;
- this.uniforms.depthMap1.value = this.pano1 && this.pano1.depthTex;
- this.updateDepthTexEnable();
- }
-
- updateDepthTexEnable(){
- let hasDepthTex = this.pano0 && this.pano1 && this.pano0.pointcloud.hasDepthTex && this.pano1.pointcloud.hasDepthTex; //暂时不知道一个有图一个没图怎么写所以
-
- Common.addOrRemoveDefine(this, 'hasDepthTex', hasDepthTex?'add':'remove' );
-
-
- }
-
- /* EnableDepthTex(){//开启DepthTex
- if(this.defines['hasDepthTex']){
- return
- }
- this.defines['hasDepthTex'] = ''
- this.needsUpdate = true;
- } */
- }
- var TileUtils = {};
- TileUtils.TILE_SIZE = 512,
- TileUtils.FACES_PER_PANO = 6,
- TileUtils.LocationOnTile = {
- Center: 0,
- UpperLeft: 1,
- UpperRight: 2,
- LowerRight: 3,
- LowerLeft: 4
- },
- /*
- * 获取某tile在cube中的方向 direction (向量起点在cube中心,终点在tile图的指定位置)。spherical通过先求uv,再直接得到dir
- * @param {*} size 面分辨率
- * @param {*} cubeFace 所在面
- * @param {*} Center 在tile上的目标位置,默认为中心,其他位置就是四个顶点
- * @param {*} c 似乎是在tile的缩进百分比,根据所在面的不同,分别向不同方向缩进,但都是向tile的中心
- * @param {*} dir 所求方向
- */
- TileUtils.getTileVector = function() {//获取某tile在cube中的方向 direction (向量起点在cube中心,终点在tile图的中心)
- return function(size, tileSize, cubeFace, tileX, tileY, Center, c, dir) {//c似乎是缩进百分比
-
- Center = Center || TileUtils.LocationOnTile.Center;
-
- //假设该cube边长为2:
- var u = size / tileSize
- , d = tileX / u;
- tileY = -tileY + (u - 1);
- var p = tileY / u
- , f = tileSize / size
- , g = 2 * f //一个tile的宽度 (乘以2是因为cube边长是2)
- , m = g / 2
- , v = 2 * d - 1 + m
- , A = 2 * p - 1 + m;
-
- switch (Center) {//计算在tile中指定位置带来的偏移
- case TileUtils.LocationOnTile.UpperLeft: //1
- v -= m,
- A += m,
- v += c * g; //似乎是向内缩进
- break;
- case TileUtils.LocationOnTile.UpperRight:
- v += m,
- A += m,
- A -= c * g;
- break;
- case TileUtils.LocationOnTile.LowerRight:
- v += m,
- A -= m,
- v -= c * g;
- break;
- case TileUtils.LocationOnTile.LowerLeft:
- v -= m,
- A -= m,
- A += c * g;
- break;
- case TileUtils.LocationOnTile.Center: //0
- }
- switch (cubeFace) {
- case GLCubeFaces$1.GL_TEXTURE_CUBE_MAP_POSITIVE_X:
- MathLight.setVector(dir, -1, A, -v);
- break;
- case GLCubeFaces$1.GL_TEXTURE_CUBE_MAP_NEGATIVE_X:
- MathLight.setVector(dir, 1, A, v);
- break;
- case GLCubeFaces$1.GL_TEXTURE_CUBE_MAP_POSITIVE_Y: //顶面
- MathLight.setVector(dir, -v, 1, -A);
- break;
- case GLCubeFaces$1.GL_TEXTURE_CUBE_MAP_NEGATIVE_Y:
- MathLight.setVector(dir, -v, -1, A);
- break;
- case GLCubeFaces$1.GL_TEXTURE_CUBE_MAP_POSITIVE_Z:
- MathLight.setVector(dir, -v, A, 1);
- break;
- case GLCubeFaces$1.GL_TEXTURE_CUBE_MAP_NEGATIVE_Z:
- MathLight.setVector(dir, v, A, -1);
- }
- MathLight.normalize(dir);
- }
- }(),
- /*
- * 获取该tile在第几个面(简易装载法)
- */
- TileUtils.getFaceForTile = function(size, index) {//获取该tile在第几个面
- var tileSize = TileUtils.TILE_SIZE;
- size < TileUtils.TILE_SIZE && (tileSize = size);
- var n = Math.floor(size / tileSize)
- , sum = n * n; //得每个面tile总数
- return Math.floor(index / sum)
- }
- ,
- TileUtils.getTileLocation = function(size, t, result) {
- var tileSize = TileUtils.TILE_SIZE;
- size < TileUtils.TILE_SIZE && (tileSize = size);
- var r = TileUtils.getFaceForTile(size, t)
- , a = Math.floor(size / tileSize)
- , s = a * a
- , l = t - r * s;
- result.tileX = l % a;
- result.tileY = Math.floor(l / a);
- result.face = r;
- result.faceTileIndex = l;
- return result
- }
- ,
- /*
- * 求size分辨率需要多少张tile
- */
- TileUtils.getTileCountForSize = function(e) {
- if (e <= TileUtils.TILE_SIZE)
- return TileUtils.FACES_PER_PANO;
- var t = Math.floor(e / TileUtils.TILE_SIZE)
- , i = t * t
- , n = i * TileUtils.FACES_PER_PANO;
- return n
- }
- ,
- TileUtils.getRelativeDirection = function() {
- var e = new MathLight.Matrix4
- , t = new MathLight.Quaternion;
- return function(i, n) {//i是pano.quaternion, n是camera的direction
- t.copy(i),
- t.inverse(),
- e.makeRotationFromQuaternion(t),
- e.applyToVector3(n),
- MathLight.normalize(n);
- }
- }(),
- /*
- * 根据方向寻找合适的tile加载
- */
- TileUtils.matchingTilesInDirection = function() {
- var e = new MathLight.Vector3
- , t = new MathLight.Vector3(0,0,-1)
- , i = new MathLight.Quaternion
- , n = function(e, t) {
- e.push({
- face: t.face,
- faceTileIndex: t.faceTileIndex,
- tileX: t.tileX,
- tileY: t.tileY
- });
- }
- , a = function() {
- var e = {
- face: -1,
- faceTileIndex: -1,
- tileX: -1,
- tileY: -1
- };
- return function(size, i, r) {
- for (var a = TileUtils.getTileCountForSize(size), s = 0, l = 0; l < a; l++)
- TileUtils.getTileLocation(size, l, e),
- i && !i(e) || (s++,
- r && n(r, e));
- return s
- }
- }();
- return function(pano, size, dir, hFov, vFov, result) {
- var d = size < TileUtils.TILE_SIZE ? size : TileUtils.TILE_SIZE;
- //TileUtils.getTileCountForSize(size);
- if (!hFov && !vFov)
- return a(size, null, result);
- var p = !!vFov;
- vFov = vFov || hFov,
- vFov = Math.max(0, Math.min(vFov, 360)),
- hFov = Math.max(0, Math.min(hFov, 360)),
- MathLight.copyVector(dir, e),
- TileUtils.getRelativeDirection(pano.quaternion4dkk, e);
- if(p){//如果有vFov hFov
- i.setFromUnitVectors(e, t);
- var f = function(e) {
- return TileUtils.isTileWithinFrustum(size, d, e.face, e.tileX, e.tileY, i, hFov, vFov)//在视野中的
- };
- return a(size, f, result)
- }
- var g = function(t) {//如果仅有hFov
- return TileUtils.isTileWithinFOV(size, d, t.face, t.tileX, t.tileY, e, hFov)
- };
- return a(size, g, result)
- }
- }(),
- /*
- * 是否在屏幕范围内
- */
- TileUtils.isTileWithinFrustum = function() {
- var e = new MathLight.Vector3
- , t = 1e-5;
- return function(i, n, a, s, l, c, h, u) {
- for (var d = Math.tan(.5 * u * MathLight.RADIANS_PER_DEGREE), p = -d, f = Math.tan(.5 * h * MathLight.RADIANS_PER_DEGREE), g = -f, m = TileUtils.mapFaceToCubemapFace(a), v = 0, A = 0, y = 0, C = 0, I = 0, E = 0, b = TileUtils.LocationOnTile.Center; b <= TileUtils.LocationOnTile.LowerLeft; b++){
- TileUtils.getTileVector(i, n, m, s, l, b, 0, e),//get e // size, tileSize, cubeFace, tileX, tileY, Center, c, dir
- MathLight.applyQuaternionToVector(c, e);
- if (e.z >= -t)//似乎是在相机背面
- I++;
- else {
- var w = -1 / e.z
- , _ = e.x * w
- , T = e.y * w;
- T > d ? v++ : T < p && A++, //这四种似乎代表在这个画框之外,如在左、在上、在下、在右
- _ > f ? y++ : _ < g && C++,
- E++;
- }
- }
- return A !== E && v !== E && y !== E && C !== E //如果有一项和E相等代表要么是在相机背面要么是tile的四个顶点都画在画布的同一边,所以肯定不在画布上
- }
- }(),
- /*
- * 是否在FOV范围内
- */
- TileUtils.isTileWithinFOV = function() {
- var e = new MathLight.Vector3
- , t = new MathLight.Vector3(0,1,0)
- , i = new MathLight.Vector3(1,0,0);
- return function(panoSize, tileSize, face, tileX, tileY, direction, fov) {//direction是作用了pano.quaternion的camera.direction
- var d = TileUtils.mapFaceToCubemapFace(face);
- MathLight.cross(direction, t, i); //get i 好像没用到
- TileUtils.getTileVector(panoSize, tileSize, d, tileX, tileY, TileUtils.LocationOnTile.Center, 0, e);
- if (TileUtils.isWithinFOV(e, direction, fov, null))//先判断tile中心在不在FOV内
- return !0;
- for (var p = fov / 360, f = Math.floor(1 / p), g = 0, m = 0; m < f; m++) {
- for (var v = TileUtils.LocationOnTile.UpperLeft; v <= TileUtils.LocationOnTile.LowerLeft; v++)
- if (TileUtils.getTileVector(panoSize, tileSize, d, tileX, tileY, v, g, e),
- TileUtils.isWithinFOV(e, direction, fov, null))
- return !0;
- g += p; //可能是考虑到有可能tile比fov覆盖了fov(虽然一般不可能,除非fov特别小),所以将tile分成若干段,取tile中的点再检测下
- }
- return !1
- }
- }(),
- TileUtils.isWithinFOV = function() {
- var e = new MathLight.Vector3
- , t = new MathLight.Vector3;
- return function(dir, cameraDir, fov, a) {
- if (MathLight.copyVector(dir, t),
- a) {
- MathLight.copyVector(a, e),
- MathLight.normalize(e);
- var s = MathLight.dot(e, dir);
- e.x *= s,
- e.y *= s,
- e.z *= s,
- MathLight.subVector(t, e);
- }
- var l = fov / 2 * MathLight.RADIANS_PER_DEGREE
- , c = Math.cos(l)
- , h = MathLight.dot(t, cameraDir);
- return h >= c
- }
- }(),
- TileUtils.mapFaceToCubemapFace = function() {
- var e = {
- 0: GLCubeFaces$1.GL_TEXTURE_CUBE_MAP_POSITIVE_Y,
- 1: GLCubeFaces$1.GL_TEXTURE_CUBE_MAP_POSITIVE_Z,
- 2: GLCubeFaces$1.GL_TEXTURE_CUBE_MAP_POSITIVE_X,
- 3: GLCubeFaces$1.GL_TEXTURE_CUBE_MAP_NEGATIVE_Z,
- 4: GLCubeFaces$1.GL_TEXTURE_CUBE_MAP_NEGATIVE_X,
- 5: GLCubeFaces$1.GL_TEXTURE_CUBE_MAP_NEGATIVE_Y
- };
- return function(t) {
- return e[t]
- }
- }();
- var texLoader$2 = new TextureLoader();
- const labelProp = {
- sizeInfo: {minSize : 200 , maxSize : 250, nearBound : 0.8, farBound : 10},
- backgroundColor:{r: 255, g: 255, b: 255, a: 0.4 },
- textColor:{r: 0, g: 0, b: 0, a: 1 },
- borderRadius: 15,
- renderOrder:10
- };
- let standardMarkerMat;
- let markerTex;
- let getMarerMat = function(){
- if(!markerTex) {
- markerTex = {
- default:texLoader$2.load( Potree.resourcePath+'/textures/marker.png' ),
- ring:texLoader$2.load( Potree.resourcePath+'/textures/marker2.png' )
- };
- markerTex.default.anisotropy = 4; // 各向异性过滤 .防止倾斜模糊
- markerTex.ring.anisotropy = 4;
- //有可能被点云遮住吗。
-
- }
- return new DepthBasicMaterial({opacity:0.7, side: DoubleSide , map:markerTex.default ,transparent:true,
- clipDistance: 2, occlusionDistance:1, //不能设置太短,因为过渡时深度不准确
- //depthTest: !!Potree.settings.useDepthTex,
- useDepth: !!Potree.settings.useDepthTex
- //改为DepthBasicMaterial是因为原Basic的材质过渡时会先隐藏后出现
- })
- };
- //显示全景图时marker没有被遮挡,如果需要,要换成depthBasicMaterial 或者直接把skybox的深度修改(拿到深度贴图后更如此)
- let planeGeo$1 = new PlaneBufferGeometry(0.2,0.2);
- let sg = new SphereGeometry(0.1, 8, 8);
- let smHovered = new MeshBasicMaterial({/* side: THREE.BackSide, */color: 0xff0000});
- let sm = new MeshBasicMaterial({/* side: THREE.BackSide */});
- var rot90 = new Quaternion().setFromAxisAngle(new Vector3(0,0,1), Math.PI/2 ); //使用的是刚好适合全景图的,给cube贴图需要转90°
- //var rot90 = new THREE.Quaternion().setFromAxisAngle(new THREE.Vector3(1,0,0), -Math.PI/2 ); //4dkk->navvis
- //var rot901 = new THREE.Quaternion().setFromAxisAngle(new THREE.Vector3(0,1,0), -Math.PI/2 ); //整张球幕图要旋转下
- //rot90 = new THREE.Quaternion().multiplyQuaternions( rot901, rot90)
- var old = null;
- /*
- 转成四维看看的axis:
- var a = new THREE.Quaternion().setFromAxisAngle(new THREE.Vector3(0,0,1), THREE.Math.degToRad(-90)) 因为四维的要绕y转90
- 这里的quaternion.multiply(a);
-
- 先乘再换顺序 w : q.w, x:q.x , y:-q.z, z:q.y
- */
- //暂时直接用4dkkconsole输出的数据
- class Panorama extends EventDispatcher{
- constructor(o, images360){//file, time, longitude, latitude, altitude, course, pitch, roll
- super();
- this.id = o.id; //唯一标识
- this.images360 = images360;
- this.visible = true; //for viewer updateVisible
- this.enabled = true;//是否可以走
- this.addEventListener('isVisible',(e)=>{//是否显示该点的mesh(不显示也能走)
- //console.log('pano isVisible', this.id, e.visible)
- viewer.updateVisible(this.marker, 'panoVisi', e.visible);
- Potree.settings.showPanoMesh && (this.mesh.visible = e.visible);
- if(e.reason == 'screenshot' || e.visible){
- this.label && (this.label.visible = e.visible);//截图时隐藏下
- }
- viewer.updateVisible(this.label2, 'panoVisi', e.visible);
- });
- /*
- 漫游点可见性:旧
- level reason 类型
- 2(最高)buildingChange(不在此楼层) unvisible
- 1 modeIsShowPanos(漫游模式) visible //不记得为什么加这个了,所以重写
- 0 pointcloudVisi(隐藏了数据集) unvisible
- */
-
- /*
- 漫游点可见性:新
- level reason 类型
- 2(最高)buildingChange(不在此楼层) unvisible
- 1 ifShowMarker(marker显示开关) unvisible
- 0 pointcloudVisi(隐藏了数据集) unvisible
- */
-
-
- if(Potree.settings.editType == 'pano'){//漫游点拼合编辑
- this.uuid = o.uuid; //因为有多个数据集 所以会重复
- this.index = o.index; //下标, 用于visibles
- this.panosData = o;
-
- //数据中原本的位置朝向
- this.dataPosition = new Vector3().copy(o.pose.translation);
- this.dataQuaternion = new Quaternion().copy(o.pose.rotation);
- this.dataRotation = new Euler().setFromQuaternion(this.dataQuaternion);
-
-
- //因为位置朝向随着点云位置改变,所以直接运用到点云上,这里清零
- this.originPosition = new Vector3(); //{x: 0, y: 0, z: 0}
- this.quaternion = new Quaternion(); //{w: 0, x: 0, y: 0, z: 1}
- //this.quaternion4dkk = math.convertVisionQuaternion(this.quaternion)//4dkk内使用的quaternion
- this.visibles = o.visibles;
- this.pointcloud = viewer.scene.pointclouds.find(e=>e.panoUuid == o.uuid);
- this.pointcloud.panos.push(this);
-
- const height = 1.4; //相机高度
- this.originFloorPosition = this.originPosition.clone();
- this.originFloorPosition.z -= height;
-
-
- /* this.originPosition = new THREE.Vector3().copy(o.pose.translation) //{x: 0, y: 0, z: 0}
- this.quaternion = new THREE.Quaternion().copy(o.pose.rotation) //{w: 0, x: 0, y: 0, z: 1}
- //this.quaternion4dkk = math.convertVisionQuaternion(this.quaternion)//4dkk内使用的quaternion
- this.visibles = o.visibles
- this.pointcloud = viewer.scene.pointclouds.find(e=>e.dataset_id == o.uuid)
- this.pointcloud.panos.push(this)
-
- const height = 1.5; //相机高度
- this.originFloorPosition = this.originPosition.clone()
- this.originFloorPosition.z -= height
- */
-
-
- }else {
- this.originPosition = new Vector3().fromArray(o.dataset_location);
- this.originFloorPosition = new Vector3().fromArray(o.dataset_floor_location);
-
- this.originID = parseInt(o.file_id);//"file_id":"00022"对应是原本的4dkk的id --来自vision.txt
-
- this.pointcloud = viewer.scene.pointclouds.find(e=>e.dataset_id == o.dataset_id) || viewer.scene.pointclouds[0];
- this.pointcloud.panos.push(this);
-
- this.sid = this.pointcloud.sceneCode + '|' + this.originID; //不会更改的标记
-
- //全景图和Cube的水平采样起始坐标相差90度
-
- /* if(from4dkk){
- var qua = o.dataset_orientation
-
- var quaternion = new THREE.Quaternion().fromArray(qua)
- quaternion = new THREE.Quaternion().multiplyQuaternions(quaternion, rot901);//整张球幕图要旋转下 因为在4dkk里转过,还原。如果是tiles的不用
- this.quaternion = new THREE.Quaternion(quaternion.x, -quaternion.z, quaternion.y, quaternion.w) //转化坐标
-
- }else{ */
-
-
- var qua = o.dataset_orientation;
- qua = [qua[1], qua[2], qua[3], qua[0]];
- this.quaternion = new Quaternion().fromArray(qua);
- this.quaternion4dkk = math.convertVisionQuaternion(this.quaternion);//4dkk内使用的quaternion
- this.quaternion2 = this.quaternion.clone();
- this.quaternion = new Quaternion().multiplyQuaternions(this.quaternion, rot90);//全景图和Cube的水平采样起始坐标相差90度,cubeTex转90度
-
- this.rotation4dkk = new Euler().setFromQuaternion(this.quaternion4dkk);
-
- //}
-
-
- //this.quaternion1 = Potree.Utils.QuaternionFactory.fromArray(o.dataset_orientation)
- //同quaternion
-
- //let xy = this.transform.forward([this.longitude, this.latitude]);
- this.file = `https://4dkk.4dage.com/images/images${Potree.settings.number}/pan/high/${this.id}.jpg`;
-
-
-
- }
- this.rotation = new Euler().setFromQuaternion(this.quaternion);
- this.build();
- this.transformByPointcloud(); //初始化位移
-
- {//tile
- this.minimumTiledPanoLoaded = !1;
- this.highestPartialTileRenderOpCompleted = 0;
- this.highestFullTileRenderOpCompleted = 0;
- this.shouldRedrawOnBaseLoaded = !1;
- this.resolutionPromise = {};
- this.tiledPanoRenderTarget = null;
- this.zoomed = !1;
-
-
-
-
- images360.panoRenderer.addEventListener(PanoRendererEvents.TileRenderSuccess, this.onTileRendered.bind(this));
- images360.panoRenderer.addEventListener(PanoRendererEvents.PanoRenderComplete, this.onPanoRendered.bind(this));
- images360.panoRenderer.addEventListener(PanoRendererEvents.TileRenderFailure, this.onTileRenderFail.bind(this));
- images360.panoRenderer.addEventListener(PanoRendererEvents.UploadAttemptedForAllTiles, this.onUploadAttemptedForAllTiles.bind(this));
-
- }
-
-
-
- this.addEventListener('hoverOn', (e)=>{//from Map
- if(!e.byMainView){
- this.hoverOn(e);
- }
- });
-
- this.addEventListener('hoverOff', (e)=>{
- if(!e.byMainView){
- this.hoverOff(e);
- }
- });
- }
-
- setEnable(enable){//是否可以走
- viewer.updateVisible(this, 'isEnabled', enable); //令所有marker不可见
- this.enabled = enable;
- //如果当前在全景模式且在这个点,需要切换显示吗? 目前用不到
- }
-
- loadDepthImg(){
- if(!this.pointcloud.hasDepthTex || this.depthTex || this.depthTexLoading)return
- this.depthTexLoading = true;
- let src = Potree.settings.number == 'SS-t-7DUfWAUZ3V' ? `${Potree.scriptPath}/data/${Potree.settings.number}/depthMap/${this.originID}.png`
- : `https://laser-oss.4dkankan.com/testdata/${Potree.settings.number}/data/${Potree.settings.number}/depthmap/${this.originID}.png`;
- let texture = texLoader$2.load( src, ()=>{
- this.depthTex = texture;
- this.images360.dispatchEvent({type:'loadedDepthImg', pano:this});
- this.depthTexLoading = false;
- });
- texture.wrapS = RepeatWrapping;
- texture.flipY = false;
- texture.magFilter = LinearFilter;
- texture.minFilter = LinearFilter;
- }
-
-
- build(){
-
- /* let mesh = new THREE.Mesh(sg, sm);
- mesh.scale.set(1, 1, 1);
- mesh.material.transparent = true;
- mesh.material.opacity = 0.75;
- mesh.pano = this;
- mesh.name = 'panoSphere'
- mesh.addEventListener('mouseover',(e)=>{
- mesh.material = smHovered
- })
- mesh.addEventListener('mouseleave',(e)=>{
- mesh.material = sm
- })
- mesh.addEventListener('click',(e)=>{
- this.images360.focusPano(this)
- })
- this.mesh = mesh;
- if(!Potree.settings.showPanoMesh) mesh.visible = false
- this.images360.node.add(mesh)
- */
-
- { // orientation
- //var {course, pitch, roll} = this;
- //mesh.quaternion.copy(this.quaternion)
-
- //add
- //var quaternion = new THREE.Quaternion().multiplyQuaternions(this.quaternion, rot901);//改 为球目全
- //quaternion.premultiply(rot90)
- this.panoMatrix = new Matrix4().makeRotationFromQuaternion(this.quaternion);
- this.oriPanoMatrix = this.panoMatrix.clone();
-
- if(this.quaternion2)this.oriPanoMatrix2 = new Matrix4().makeRotationFromQuaternion(this.quaternion2);
-
-
- //console.log(this.quaternion)
- //this.quaternion = quaternion
- }
-
-
-
- let marker = new Mesh(planeGeo$1, getMarerMat() );
- marker.up.set(0,0,1);
- marker.lookAt(marker.up);
- marker.scale.set(2,2,2);
- this.addEventListener('changeMarkerTex',(e)=>{
- marker.material.map = markerTex[e.name];
- });
-
- this.marker = marker;
- if(Potree.settings.editType == 'pano'){
- viewer.updateVisible(marker, 'panoEdit', false, 4);
- }
-
- this.images360.node.add(marker);
- Potree.settings.isTest && this.createTextLabel();
- this.createTextLabel2();
-
- /* let mouseover = (e)=>{
- if(!e.byMap){
- pano.mapMarker.material = panoMarkerMats.selected
- if(!e.byMainView) pano.dispatchEvent({type: "hoverOn", byMap:true})
- this.needRender = true
- }
- }
-
- let mouseleave = (e)=>{
- if(!e.byMap){
- pano.mapMarker.material = panoMarkerMats.default
- if(!e.byMainView) pano.dispatchEvent({type: "hoverOff", byMap:true})
- this.needRender = true
- }
- } */
-
-
- marker.addEventListener('mouseover', this.hoverOn.bind(this));
- marker.addEventListener('mouseleave', this.hoverOff.bind(this));
- }
-
-
-
-
- transformByPointcloud(){
-
- let position = this.originPosition.clone().applyMatrix4(this.pointcloud.transformMatrix);//也可以用datasetPosTransform算
- let floorPosition = this.originFloorPosition.clone().applyMatrix4(this.pointcloud.transformMatrix);
- this.setPosition(position, floorPosition);
- this.panoMatrix = new Matrix4().multiplyMatrices(this.pointcloud.rotateMatrix, this.oriPanoMatrix );
- //this.panoMatrix2 = Potree.Utils.datasetRotTransform({fromDataset:true, pointcloud:this.pointcloud, matrix:this.oriPanoMatrix, getMatrix:true}) //和上一行结果一样
- //quaternion也变下
- if(this.oriPanoMatrix2){
- this.panoMatrix2 = new Matrix4().multiplyMatrices(this.pointcloud.rotateMatrix, this.oriPanoMatrix2 );//供DepthImageSampler使用
- this.panoMatrix2Inverse = this.panoMatrix2.clone().invert();
- }
- this.dispatchEvent('rePos');
- }
-
- setPosition(position, floorPosition){
- this.position = position;
- this.floorPosition = floorPosition;
- //this.mesh.position.copy(this.position)
- this.marker.position.copy(this.floorPosition);
- this.marker.position.z+=0.04;//会被点云遮住
- if(this.label){
- if(Potree.settings.editType == 'pano'){
- this.label.position.copy(this.position);
- }else {
- this.label.position.copy(this.floorPosition);
- }
- this.label.position.z+=0.14;
- this.label.update();
- }
-
- if(this.label2){
- if(Potree.settings.editType == 'pano'){
- this.label2.position.copy(this.position);
- }else {
- this.label2.position.copy(this.floorPosition);
- }
- this.label2.position.copy(this.marker.position);
- this.label2.update();
- }
-
- }
-
-
-
-
-
-
-
-
- hoverOn(e={}) {
- //console.log("hoverOn " + this.id )
- transitions.start(lerp.property(this.marker.material, "opacity", 1), 250);
- if(!e.byMap) this.dispatchEvent({type:'hoverOn', byMainView:true});
- if(!e.byImages360) this.images360.dispatchEvent({type:'markerHover', hovered:true, pano:this});
- }
-
- hoverOff(e={}){
- //console.log("hoverOff " + this.id )
- transitions.start(lerp.property(this.marker.material, "opacity", 0.5), 250);
- if(!e.byMap) this.dispatchEvent({type:'hoverOff', byMainView:true});
- if(!e.byImages360) this.images360.dispatchEvent({type:'markerHover', hovered:false, pano:this});
- }
-
-
-
- setZoomed(zoomed){
- this.zoomed = zoomed;
- Potree.settings.displayMode == 'showPanos' && this.updateSkyboxForZoomLevel(); //放大后换成zoomTarget贴图
- viewer.dispatchEvent({type:'panoSetZoom', zoomed});
-
- }
-
-
- enter(){
- this.setZoomed(!1),
- viewer.dispatchEvent({type:PanoramaEvents.Enter, oldPano:old, newPano:this } );
- old = this;
- //console.log("enter pano "+ this.id)
- }
- exit(){
- /* if(this.tiled)
- { */
- this.clearWaitDeferreds();
- this.minimumTiledPanoLoaded = !1;
- this.tiledPanoRenderTarget = null;
- this.setZoomed(!1);
- this.images360.panoRenderer.deactivateTiledPano(this);
- this.highestPartialTileRenderOpCompleted = 0;
- this.highestFullTileRenderOpCompleted = 0;
- /*}
- else
- {
- this.solidSkybox.dispose();
- this.solidSkybox.loaded = !1;
- this.solidSkybox.version = 0;
- } */
-
- //console.log("exit pano "+ this.id)
-
- viewer.dispatchEvent({type:PanoramaEvents.Exit, pano:this});
- }
-
-
- updateSkyboxForZoomLevel(){
- if(this.minimumTiledPanoLoaded){
- this.images360.updateProjectedPanos();
- }
-
- }
-
- getSkyboxTexture(){
-
- if(this.minimumTiledPanoLoaded)
- {
- if(this.zoomed && this.images360.qualityManager.maxRenderTargetSize > this.images360.qualityManager.maxNavPanoSize)//change 如果放大后和不放大都是2k就不用这个
- {
- return this.images360.panoRenderer.zoomRenderTarget.texture;
- }
- else
- {
-
- this.tiledPanoRenderTarget.texture.mapping = UVMapping;//add
- return this.tiledPanoRenderTarget.texture;
- }
- }
- else
- {
- return null;
- }
-
- }
-
-
-
- isLoaded(e){
- if (e && "string" == typeof e)
- console.error("Wrong panoSize given to Panorama.isLoaded(); a tiled pano uses PanoSizeClass");
- return !!this.minimumTiledPanoLoaded && (!e || this.highestFullTileRenderOpCompleted >= e)//改:原本是:this.highestPartialTileRenderOpCompleted >= e, 希望这代表全部加载完
-
- }
- getWaitDeferred(size){//获取不同size的tile贴图的promiss
- var t = this.resolutionPromise[this.id];
- t || (t = {}, this.resolutionPromise[this.id] = t);
- var i = t[size];
- return i || (i = {
- deferred: $.Deferred(),
- active: !1
- },
- t[size] = i),
- i
- }
-
- clearWaitDeferreds(){
- var e = this.resolutionPromise[this.id];
- e || (e = {},
- this.resolutionPromise[this.id] = e);
- for (var t in e)
- if (e.hasOwnProperty(t)) {
- var i = e[t];
- i.active = !1,
- i.deferred = $.Deferred();
- }
- }
- resetWaitDeferred(e){
- var t = this.getWaitDeferred(e);
- t.active = !1;
- t.deferred = $.Deferred();
- }
- onTileRendered(ev){
- ev.id === this.id && this.dispatchEvent({
- type:PanoramaEvents.TileLoaded,
- size:ev.panoSize, index:ev.tileIndex, count:ev.totalTiles
- });
- }
- onPanoRendered(ev) {
- if(ev.id === this.id)
- {
- this.minimumTiledPanoLoaded = !0;
- this.updateSkyboxForZoomLevel();//更新贴图 setProjected
- ev.panoSize > this.highestPartialTileRenderOpCompleted && (this.highestPartialTileRenderOpCompleted = ev.panoSize);//应该是更新最高获取到的Partial size
- ev.updateFullComplete && ev.panoSize > this.highestFullTileRenderOpCompleted && (this.highestFullTileRenderOpCompleted = ev.panoSize); //应该是更新最高获取到的Full size
- //this.dispatchEvent("load", ev.panoSize);
- viewer.ifAllLoaded( this);
- this.dispatchEvent({type:PanoramaEvents.LoadComplete, size:ev.panoSize, count:ev.totalTiles});
- }
- }
-
- onTileRenderFail(ev) {
- ev.id === this.id && this.dispatchEvent({type:PanoramaEvents.LoadFailed });
- }
- onUploadAttemptedForAllTiles(ev) {
- if (ev.id === this.id) {
- var n = this.images360.qualityManager.getPanoSize(PanoSizeClass.BASE);
- if(ev.panoSize === n && this.shouldRedrawOnBaseLoaded) //shouldRedrawOnBaseLoaded一直是false。在4dkk里只有初始点在quickstart后变为true。
- {
- this.shouldRedrawOnBaseLoaded = !1;
- this.panoRenderer.resetRenderStatus(this.id, !0, !1);
- this.panoRenderer.renderPanoTiles(this.id, null, !0, !0);
- }
- }
- }
-
-
- createTextLabel(){
- this.removeTextLabel();
- this.label = new TextSprite(Object.assign({},
- labelProp, {text: this.id }) //{text: `id:${this.id}, dataset:${this.pointcloud.name}, 4dkkId:${this.originID}`}
- );
- this.images360.node.add(this.label);
- this.floorPosition && this.label.position.copy(this.floorPosition);
- }
-
- createTextLabel2(){
- let labelProp2 = {
- //sizeInfo: {minSize : 200 , maxSize : 250, nearBound : 0.8, farBound : 10},
- backgroundColor:{r: 255, g: 255, b: 255, a: 0 },
- textColor:{r:255 , g: 255, b: 255, a: 1 },
- textBorderColor:{r:30 , g:30, b: 30, a: 1 },
- textBorderThick:3,
- dontFixOrient:true,
- renderOrder:10,
- fontsize:30,
- };
- this.label2 = new TextSprite(Object.assign({},
- labelProp2, {text: /* this.originID */ parseInt(this.id)+1 }) //{text: `id:${this.id}, dataset:${this.pointcloud.name}, 4dkkId:${this.originID}`}
- );
- this.images360.node.add(this.label2);
- this.floorPosition && this.label2.position.copy(this.floorPosition);
- let s = 0.4;
- this.label2.scale.set(s,s,s);
- viewer.updateVisible(this.label2, 'notDisplay', false);
- }
-
- removeTextLabel(){
- if(this.label){
- this.label.parent.remove(this.label);
- }
- }
-
- dispose(){
-
- let i = viewer.images360.panos.indexOf(this);
- if(i==-1)return
-
- this.marker.parent.remove(this.marker);
-
-
- this.removeTextLabel();
- if(this.depthTex) this.depthTex.dispose();
- viewer.images360.panos.splice(i,1);
-
- this.dispatchEvent('dispose');
- //删除tile贴图、depthTex等以后再写
- }
-
- };
-
- Panorama.prototype.loadTiledPano = function() {
- //var downloads = [] , t = [];
- var downloaded = {} , eventAdded = {}, latestPartialRequest = {}; //每个pano对应一组这些
-
- return function(size, dirs, fov, o, a, download) {
- var dir = dirs.datasetsLocal.find(e=>e.datasetId == this.pointcloud.dataset_id).direction;
- //var dir = dirs
-
-
- null !== o && void 0 !== o || (o = !0),
- null !== a && void 0 !== a || (a = !0);
- var l = this.getWaitDeferred(size)
- , c = l.deferred
- , h = null
- , u = null;
- fov && ("number" == typeof fov ? h = fov : (h = fov.hFov, u = fov.vFov));
-
- if (!this.isLoaded(size)) {
- //console.log('loadTiledPano', this.id, size, fov)
- if (!l.active) {
- l.active = !0;
- let name = this.id + ":" + size;
- downloaded[name] = downloaded[name] || [];
- /*
- this.downloaded = downloaded
- this.latestPartialRequest = latestPartialRequest
- */
- latestPartialRequest[name] = null;
-
- if (fov) {
- let tileArr = [];//add
- var d = TileUtils.matchingTilesInDirection(this, size, dir, h, u, tileArr);
-
- latestPartialRequest[name] = tileArr;
- downloaded[name].forEach((e)=>{
- let item = latestPartialRequest[name].find(a=>e.faceTileIndex == a.faceTileIndex && e.face == a.face);
- if(item){
- item.loaded = true;
- }
- });
- if(!latestPartialRequest[name].some(e=>!e.loaded)){//所需要的全部加载成功
- //let total = TileUtils.getTileCountForSize(size)
- //this.onPanoRendered(this.id, size, total, !0);
- c.resolve(size/* , total */);
- this.resetWaitDeferred(size);
- //console.log('该部分早已经加载好了'+size, this.id)
- latestPartialRequest[name] = null;
- }
-
- //console.log("Loading partial pano: " + this.id + " with " + d + " tiles")
- }
- if(!eventAdded[this.id]) {
- eventAdded[this.id] = !0;
-
- this.addEventListener(PanoramaEvents.LoadComplete, function(ev/* e, t */) {//本次任务全部加载完毕
-
- //console.warn('点位(可能部分)下载完成 ', 'id:'+this.id, 'size:'+ev.size )
-
- var i = this.getWaitDeferred(ev.size).deferred;//"pending"为还未完成
- i && "pending" === i.state() && this.highestPartialTileRenderOpCompleted >= ev.size && (i.resolve(ev.size, ev.count),
- this.resetWaitDeferred(ev.size));//恢复active为false
- }.bind(this));
-
- this.addEventListener(PanoramaEvents.LoadFailed, function(ev) {
- var t = this.getWaitDeferred(e).deferred;
- t && "pending" === t.state() && this.highestPartialTileRenderOpCompleted >= ev.t && (t.reject(ev.t),
- this.resetWaitDeferred(ev.t));//恢复active为false
- }.bind(this));
-
- this.addEventListener(PanoramaEvents.TileLoaded, function(ev/* t, i, n */) {//每张加载完时
-
- //console.log('tileLoaded', 'id:'+this.id, 'size:'+ev.size, 'tileIndex:'+ev.index )
- let tileIndex = ev.index;
- let total = ev.count;
- let size = ev.size;
- let name = this.id + ":" + size;
- downloaded[name] = downloaded[name] || []; //不是所有的加载都是从loadTiledPano获取的所以会有未定义的情况
-
- let {faceTileIndex,face} = TileUtils.getTileLocation(size, tileIndex, {});
- downloaded[name].push({faceTileIndex,face});
- var r = this.getWaitDeferred(size).deferred;
- if (r && "pending" === r.state()) {
- r.notify(size, tileIndex, total);
- if(latestPartialRequest[name]){
- let item = latestPartialRequest[name].find(e=>e.faceTileIndex == faceTileIndex && e.face == face);
- item && (item.loaded = true );
-
- if(!latestPartialRequest[name].some(e=>!e.loaded)){//所需要的局部tiles全部加载成功
- this.onPanoRendered(this.id, size, total, !0); //onPanoRendered还会触发 PanoramaEvents.LoadComplete
- r.resolve(size, total);
- this.resetWaitDeferred(size);
- //console.log('该部分加载好了'+size, this.id)
- latestPartialRequest[name] = null;
- }
-
- }
- }
-
-
- /* var r = this.getWaitDeferred(ev.size).deferred;
- if (r && "pending" === r.state()) {
- r.notify(ev.size, ev.index, ev.count);
-
- var o = downloads[this.id + ":" + ev.size];
- if(o){//如果有规定下载哪些tile,只需要下载这些tile则LoadComplete
- o.tileCount++
-
- if(o.tileCount === o.targetTileCount){//达到下载目标数
- this.onPanoRendered(this.id, ev.size, ev.count, !0);
- r.resolve(ev.size, ev.count);
- this.resetWaitDeferred(ev.size)
- }
- }
- } */
- }.bind(this));
- }
- }
- this.images360.tileDownloader.clearForceQueue(),
- this.images360.tileDownloader.forceQueueTilesForPano(this, size, dir, h, u, download);
- this.tiledPanoRenderTarget = this.images360.panoRenderer.activateTiledPano(this, this.images360.qualityManager.getMaxNavPanoSize(), o);
- this.images360.panoRenderer.renderPanoTiles(this.id, dirs, a);
- }else {
- //console.log('早已经全加载好了' +size, this.id)
- c.resolve(size);
- }
- return c.promise()
- }
- }();
- class QualityManager {
- constructor(e, t, i) {
-
- this.maxNavPanoSize = -1;
- this.maxZoomPanoSize = -1;
- this.devicePixelDensity = e;
- this.deviceScreenSize = t;
- this.clientBandwidth = i;
- this.panoSizeClassMap = {};
- this.useHighResolutionPanos = !0; //是否能够使用2k及以上图
- this.useUltraHighResolutionPanos = !1;
- this.modelHasUltraHighPanos = !1;
- this.qualityManager = this;
-
- this.maxRenderTargetSize = browser.isMobile() ? 2048 : 4096; //add
- this.init();
- }
- init(e ) {
- //var metadata = store.getters['scene/metadata'] ;//有时候请求不到
- //if(metadata.sceneSource == 11 || metadata.sceneScheme == 12){
- /* if(config.tileClass == '1k'){
- this.useHighResolutionPanos = false //xzw add 只加载1k
- } */
-
-
- this.buildPanoSizeClassMap(this.devicePixelDensity, this.deviceScreenSize, this.clientBandwidth);
- this.ultraHighSize = this.getPanoSize(PanoSizeClass.ULTRAHIGH);
- this.highSize = this.getPanoSize(PanoSizeClass.HIGH);
- this.standardSize = this.getPanoSize(PanoSizeClass.STANDARD);
- this.baseSize = this.getPanoSize(PanoSizeClass.BASE);
- config$1.tiling.maxZoomPanoQuality && this.ultraHighSize <= config$1.tiling.maxZoomPanoQuality && (config$1.tiling.allowUltraHighResolution = !0);
- this.highQualityThreshold = browser.valueFromHash("threshold2k", config$1.windowHeightHighQualityThreshold);
- this.updateMaximums();
- //e.on(ModelManagerEvents.ActiveModelChanged, this.onModelChanged.bind(this));
- }
- updateFromModel(e) {
- //this.updateHighResolutionSettings(e)
- this.updateUltraHighResolutionSettings(e);
- }
- /* updateHighResolutionSettings(e) {
- this.useHighResolutionPanos = !0
- this.updateMaximums()
- } */
- updateUltraHighResolutionSettings(e) {
- if (config$1.tiling.allowUltraHighResolution && this.modelHasUltraHighPanos) {
- this.useUltraHighResolutionPanos = !0;
- } else {
- this.useUltraHighResolutionPanos = !1;
- }
- this.updateMaximums();
- }
- enableUltraHighQualityMode() {
- this.modelHasUltraHighPanos = !0;
- this.updateUltraHighResolutionSettings(null);
- }
- ultraHighQualityModeEnabled() {
- return this.modelHasUltraHighPanos
- }
- onModelChanged(e) {
- this.updateFromModel(e.model),
- this.updateMaximums();
- }
- updateMaximums() {
- this.maxNavPanoSize = config$1.tiling.maxNavPanoQuality || this.detectMaxNavPanoSize(),
- this.maxZoomPanoSize = config$1.tiling.maxZoomPanoQuality || this.detectMaxZoomPanoSize(),
- this.maxZoomPanoSize < this.maxNavPanoSize && (this.maxNavPanoSize = this.maxZoomPanoSize);
- }
- buildPanoSizeClassMap() {
- this.panoSizeClassMap[PanoSizeClass.BASE] = 512,
- this.panoSizeClassMap[PanoSizeClass.STANDARD] = 1024,
- this.panoSizeClassMap[PanoSizeClass.HIGH] = 2048,
- this.panoSizeClassMap[PanoSizeClass.ULTRAHIGH] = 4096;
- }
- getPanoSize(e) {
- return this.panoSizeClassMap[e]
- }
- getMaxPossiblePanoSize() {
- return this.getPanoSize(PanoSizeClass.ULTRAHIGH)
- }
- getMaxPanoSize() {
- return this.maxZoomPanoSize
- }
- getMaxNavPanoSize() {
- return this.maxNavPanoSize
- }
- getMaxZoomPanoSize() {
- return this.maxZoomPanoSize
- }
- detectMaxNavPanoSizeClass() {
- //return this.useHighResolutionPanos ? browser.isMobile() ? PanoSizeClass.STANDARD : window.innerHeight < this.highQualityThreshold ? PanoSizeClass.STANDARD : PanoSizeClass.HIGH : PanoSizeClass.STANDARD
- /* if(config.name == 'decor'){
- return PanoSizeClass.STANDARD
- }
- return PanoSizeClass.HIGH */
- switch(Potree.settings.navTileClass){
- case '1k':
- return PanoSizeClass.STANDARD;
- break;
- case '2k':
- default:
- return PanoSizeClass.HIGH;
- }
-
-
- }
- detectMaxNavPanoSize() {
- var e = this.detectMaxNavPanoSizeClass();
- return this.getPanoSize(e)
- }
- detectMaxZoomPanoSize() {
- if(this.zoomLevelResolution){
- if(this.zoomLevelResolution == '4k' && this.useUltraHighResolutionPanos){
- return this.getPanoSize(PanoSizeClass.ULTRAHIGH);
- }else if(this.zoomLevelResolution == '1k' || !this.useHighResolutionPanos){
- return this.getPanoSize(PanoSizeClass.STANDARD);
- }else {
- return this.getPanoSize(PanoSizeClass.HIGH);
- }
- }else {
- if (this.useHighResolutionPanos) {
- /* if (browser.isMobile()) {//手机版如果要2k的将这里去掉
- if (settings.tiling.mobileHighQualityOverride) {
- return this.getPanoSize(PanoSizeClass.HIGH);
- } else {
- return this.getPanoSize(PanoSizeClass.STANDARD);
- }
- } else */if (this.useUltraHighResolutionPanos ) {
- return this.getPanoSize(PanoSizeClass.ULTRAHIGH);
- } else {
- return this.getPanoSize(PanoSizeClass.HIGH);
- }
- } else {
- return this.getPanoSize(PanoSizeClass.STANDARD);
- }
-
-
- }
-
- }
-
-
-
-
-
- }
- var h = Object.freeze({
- None: 0,
- DirectionalFOV: 1
- });
- var u = function () {
- var e = function e(t, i) {
- var n = e._panoSpaceDir,
- r = e._fovThreshold,
- o = e._fovThresholdNarrow,
- a = Math.max(Math.min(n.dot(t.direction), 1), -1),
- s = Math.max(Math.min(n.dot(i.direction), 1), -1);
- return t._dot = a,
- i._dot = s,
- a >= r && s < r ? -1 : a < r && s >= r ? 1 : a >= o && s < o ? -1 : a < o && s >= o ? 1 : t.panoSize > i.panoSize ? 1 : i.panoSize > t.panoSize ? -1 : -(a - s)
- };
- return e._panoSpaceDir = new Vector3,
- e._fovThreshold = -1,
- e._fovThresholdNarrow = -1,
- e
- }();
- class TilePrioritizer {//优先级处理序列
- constructor(e,t, i, o, a) {
- this.qualityManager = e;
- this.maxNavQuality = this.qualityManager.getMaxNavPanoSize();
- this.maxZoomQuality = this.qualityManager.getMaxZoomPanoSize();
- this.baseSize = t;
- this.standardSize = i;
- this.highSize = o;
- this.ultraHighSize = a;
- this.priorityCriteria = new TilePrioritizer.PriorityCriteria(null, new Vector3(0, 0, 0), new Vector3(0, 0, -1), new Vector3(0, 0, -1));
- }
- updateCriteria(e, t, i, n) {//由player更新
- this.priorityCriteria.pano = e,
- this.priorityCriteria.cameraPosition.copy(t),
- //this.priorityCriteria.cameraDir.copy(i),
- this.priorityCriteria.cameraDirs = i;
-
- this.priorityCriteria.upcomingPanos = n,
- this.maxNavQuality = this.qualityManager.getMaxNavPanoSize(),
- this.maxZoomQuality = this.qualityManager.getMaxZoomPanoSize();
-
-
- }
- canDownloadSize(e) {
- return this.maxNavQuality >= e || this.maxZoomQuality >= e && this.zoomingActive
- }
-
- /* populateNeighborPanos(e, t, i) {
- i = i || [],
- i.length = 0;
- var n = t.getNeighbours(e);
- for (var r in n)
- if (n.hasOwnProperty(r)) {
- var o = t.get(r);
- if(!o){
- console.log(1)
- }
- i.push(o)
- }
- return i
- } */
- populateScoredPanos(e, t, i, dirs, a) {
- i = i || [],
- i.length = 0;
- var s = [Images360.filters.inPanoDirection(e.position, dirs, TilePrioritizer.DIRECTION_SCORE_STRICTNESS), Images360.filters.not(e)],
- l = [Images360.scoreFunctions.distanceSquared(e), Images360.scoreFunctions.direction(e.position, dirs)],
- c = Common.sortByScore(t, s, l);
- if (c)
- for (var h = 0; h < c.length && h < a; h++) {
- var u = c[h].item;
- i.push(u);
- }
- return i
- }
- queueTilesForPanos(e, t, i, n, r) {
- for (var o = 0, a = 0; a < t.length; a++) {
- var s = t[a],
- l = this.queueTilesForPano(e, i, s, n);
- if (o += l > 0 ? 1 : 0,
- r && o >= r)
- break
- }
- return o
- }
- /* queueTilesInDirectionForPanos(e, t, i, n, r, o, a, s) {//没用到
- for (var l = 0, c = 0; c < i.length; c++) {
- var h = i[c],
- u = this.queueTilesInDirectionForPano(e, t, h, n, o, a);
- if (l += u > 0 ? 1 : 0,
- s && l >= s)
- break
- }
- return l
- }
- */
- canIncludeDescriptor(e) {
- return e.status !== DownloadStatus.Downloading && e.status !== DownloadStatus.Downloaded
- }
- canIncludePano(e, t) {
- return !e.isLoaded(t)
- }
- getFOVDotThreshold(e) {
- return Math.cos(MathUtils.degToRad(e / 2))
- }
- setZoomingActive(e) {
- e !== this.zoomingActive && (this.zoomingActive = e);
- }
- }
- TilePrioritizer.PriorityCriteria = function (e, t, i, n, o) {
- this.pano = e,
- this.cameraPosition = (new Vector3).copy(t),
-
- //this.cameraDir = (new THREE.Vector3).copy(i),
- this.cameraDirs = [], //
-
- this.panoSpaceDir = (new Vector3).copy(n),
- this.upcomingPanos = o,
- this.copy = function (e) {
- this.pano = e.pano,
- this.cameraPosition.copy(e.cameraPosition),
- //this.cameraDir.copy(e.cameraDir),
- this.cameraDirs = e.cameraDirs;
-
- this.panoSpaceDir.copy(e.panoSpaceDir),
- this.upcomingPanos = o;
- },
- this.zoomingActive = !1;
- };
- TilePrioritizer.DIRECTIONAL_FOV = 180;
- TilePrioritizer.DIRECTIONAL_FOV_NARROW = 120;
- TilePrioritizer.MAX_SCORED_PANOS_TOCONSIDER = 6;
- TilePrioritizer.MAX_SCORED_PANOS_TOADD = 2;
- TilePrioritizer.MAX_UPCOMING_PANOS_TOADD = 3;
- TilePrioritizer.DIRECTION_SCORE_STRICTNESS = .75;
- TilePrioritizer.appendQueue = function (e, t) {
- if (e && t)
- for (var i = 0; i < t.length; i++){
- e.push(t[i]);
- //console.log(t[i])
- }
- };
- TilePrioritizer.sortPanoTiles = function (descriptors, pano, dir) {
- if(dir.datasetsLocal) dir = dir.datasetsLocal.find(e=>e.datasetId == pano.pointcloud.dataset_id).direction;//add
- u._panoSpaceDir.copy(dir);
- TileUtils.getRelativeDirection(pano.quaternion4dkk, u._panoSpaceDir); //应该是将dir根据quaternion转化下
- u._fovThresholdNarrow = math.getFOVDotThreshold(TilePrioritizer.DIRECTIONAL_FOV_NARROW);
- u._fovThreshold = math.getFOVDotThreshold(TilePrioritizer.DIRECTIONAL_FOV);
- descriptors.sort(u);
- };
- TilePrioritizer.insertSortedPanoTile = function (e, t, pano, dir) {
- if(dir.datasetsLocal) dir = dir.datasetsLocal.find(e=>e.datasetId == pano.pointcloud.dataset_id).direction;//add
- u._panoSpaceDir.copy(dir),
- TileUtils.getRelativeDirection(pano.quaternion4dkk, u._panoSpaceDir),
- u._fovThresholdNarrow = math.getFOVDotThreshold(TilePrioritizer.DIRECTIONAL_FOV_NARROW),
- u._fovThreshold = math.getFOVDotThreshold(TilePrioritizer.DIRECTIONAL_FOV);
- for (var o = -1, a = 0; a < e.length; a++) {
- var s = u(t, e[a]);
- if (s <= 0) {
- o = a;
- break
- }
- }
- if (o === -1)
- e[e.length] = t;
- else {
- for (var h = e.length; h > o; h--)
- e[h] = e[h - 1];
- e[o] = t;
- }
- };
- TilePrioritizer.prototype.filterDepthTex = function (panos ) {//仅下载depthTex
- if(!Potree.settings.useDepthTex || !this.priorityCriteria.pano)return
-
- let cameraDirLocals = this.priorityCriteria.cameraDirs.vectorForward;
- let t = [];
- //获得视野范围内的邻近点位序列t
- this.populateScoredPanos(this.priorityCriteria.pano, panos, t, cameraDirLocals , TilePrioritizer.MAX_SCORED_PANOS_TOCONSIDER);
-
- t.forEach(p=>p.loadDepthImg());
- };
- TilePrioritizer.prototype.filterAndPrioritize = function () {//挑选出优先加载的 pano和tile (有点复杂,没看很懂)
- var e = [],
- t = [],
- i = [];
- return function (queue, panos, tileDownloader) {
- //this.populateNeighborPanos(this.priorityCriteria.pano, panos, e);
-
- /* let cameraDirLocals = this.priorityCriteria.cameraDirs.map(e=>{ //add
- var dataset = viewer.scene.pointclouds.find(u=>u.dataset_id == e.datasetId)
- var matrix = new THREE.Matrix4().copy(dataset.rotateMatrix)
- var direction = math.convertVector.YupToZup(e.direction)
-
-
- return {
- datasetId:e.datasetId,
- direction: direction.clone().applyMatrix4(matrix)
- }
- }) */
- let cameraDirLocals = this.priorityCriteria.cameraDirs.vectorForward;
-
- //获得视野范围内的邻近点位序列t
- this.populateScoredPanos(this.priorityCriteria.pano, panos, t, cameraDirLocals , TilePrioritizer.MAX_SCORED_PANOS_TOCONSIDER);
-
- t.forEach(p=>p.loadDepthImg()); //add
-
- var s = this.baseSize //512
- ,
- l = this.standardSize //1024
- ,
- c = this.highSize //2048
- ,
- h = this.ultraHighSize; //4096
-
-
- this.queueTilesForPano(queue, tileDownloader, this.priorityCriteria.pano, s); //把当前pano的512下载了
-
-
- if (this.priorityCriteria.upcomingPanos) {// 添加即将走到的点(之前用于导览路线)512 tiles
- this.queueTilesForPanos(queue, this.priorityCriteria.upcomingPanos, tileDownloader, s, TilePrioritizer.MAX_UPCOMING_PANOS_TOADD);
- }
- i.length = 0;
-
- //把当前pano角度范围内的tile按照分辨率从低到高加入队列
-
- if (this.canDownloadSize(l)) {//1024如果在限制范围内的话
- this.queueTilesInDirectionForPano(i, tileDownloader, this.priorityCriteria.pano, l, this.priorityCriteria.cameraPosition, this.priorityCriteria.cameraDirs, TilePrioritizer.DIRECTIONAL_FOV_NARROW);
- }
- TilePrioritizer.sortPanoTiles(i, this.priorityCriteria.pano, this.priorityCriteria.cameraDirs); //排序
- TilePrioritizer.appendQueue(queue, i);
-
- //添加邻近点t 512的tiles
- this.queueTilesForPanos(queue, t, tileDownloader, s, TilePrioritizer.MAX_SCORED_PANOS_TOADD);
- i.length = 0;
-
-
- //NARROW :
- if (this.canDownloadSize(c)) {//2048
- this.queueTilesInDirectionForPano(i, tileDownloader, this.priorityCriteria.pano, c, this.priorityCriteria.cameraPosition, this.priorityCriteria.cameraDirs, TilePrioritizer.DIRECTIONAL_FOV_NARROW);
- }
- if (this.canDownloadSize(h)) {//4096
- this.queueTilesInDirectionForPano(i, tileDownloader, this.priorityCriteria.pano, h, this.priorityCriteria.cameraPosition, this.priorityCriteria.cameraDirs, TilePrioritizer.DIRECTIONAL_FOV_NARROW);
- }
- TilePrioritizer.sortPanoTiles(i, this.priorityCriteria.pano, this.priorityCriteria.cameraDirs);//排序
- TilePrioritizer.appendQueue(queue, i);
- i.length = 0;
- if (this.canDownloadSize(l)) {//1024
- this.queueTilesInDirectionForPano(i, tileDownloader, this.priorityCriteria.pano, l, this.priorityCriteria.cameraPosition, this.priorityCriteria.cameraDirs, TilePrioritizer.DIRECTIONAL_FOV);
- }
- if (this.canDownloadSize(c)) {//2048
- this.queueTilesInDirectionForPano(i, tileDownloader, this.priorityCriteria.pano, c, this.priorityCriteria.cameraPosition, this.priorityCriteria.cameraDirs, TilePrioritizer.DIRECTIONAL_FOV);
- }
- if (this.canDownloadSize(h)) {//4096
- this.queueTilesInDirectionForPano(i, tileDownloader, this.priorityCriteria.pano, h, this.priorityCriteria.cameraPosition, this.priorityCriteria.cameraDirs, TilePrioritizer.DIRECTIONAL_FOV);
- }
- TilePrioritizer.sortPanoTiles(i, this.priorityCriteria.pano, this.priorityCriteria.cameraDirs);//排序
- TilePrioritizer.appendQueue(queue, i);
-
-
-
- this.queueTilesForPanos(queue, e, tileDownloader, s); // 如果前面有populateNeighborPanos的话,这步就是加neibour
- }
- }();
- TilePrioritizer.prototype.queueTilesInDirectionForPano = function () {
- var e = {
- filter: h.DirectionalFOV,
- direction: new Vector3,
- fov: 60
- },
- t = new Vector3;
- return function (i, n, pano, o, a, dirs, c) {
-
- var dir = dirs.datasetsLocal.find(e=>e.datasetId == pano.pointcloud.dataset_id).direction;//add
- //var dir = dirs
-
- t.copy(dir);
-
- TileUtils.getRelativeDirection(pano.quaternion4dkk, t);
- e.direction.copy(t);
- e.fov = c;
- return this.filterAndQueueTileDownloadDescriptors(i, n, pano, o, e)
- }
- }();
- TilePrioritizer.prototype.filterAndQueueTileDownloadDescriptors = function () {
- var e = [];
- return function (t, i, n, r, o) {
- var a = i.getTileDownloadDescriptors(n, r);
- e.length = 0,
- this.filterTileDownloadDescriptors(n, a, e, o);
- for (var s = 0, l = 0; l < e.length; l++) {
- var c = e[l];
- if (c) {
- t.push(c);
- s++;
- }
- }
- return s
- }
- }();
- TilePrioritizer.prototype.filterTileDownloadDescriptors = function () {
- new Vector3;
- return function (e, t, i, n) {
- var r, o;
- switch (n.filter) {
- case h.DirectionalFOV:
- for (r = 0; r < t.length; r++)
- o = t[r],
- TileUtils.isTileWithinFOV(o.panoSize, o.tileSize, o.face, o.tileX, o.tileY, n.direction, n.fov) && i.push(o);
- break;
- default:
- for (r = 0; r < t.length; r++)
- o = t[r],
- i.push(o);
- }
- for (r = 0; r < i.length; r++)
- o = i[r],
- this.canIncludeDescriptor(o) || (i[r] = null);
- }
- }();
- TilePrioritizer.prototype.queueTilesForPano = function () {
- var e = {
- filter: h.None
- };
- return function (t, i, n, r) {
- return this.filterAndQueueTileDownloadDescriptors(t, i, n, r, e)
- }
- }();
- /* TilePrioritizer.prototype.queueTilesForPanosInDirection = function () { //没用到
- var e = new THREE.Vector3;
- return function (t, i, n, r, o, a, s, l) {
- for (var h = 0, u = 0; u < n.length; u++) {
- var d = n[u];
- e.copy(d.position),
- e.sub(o),
- e.normalize();
- var p = Math.max(Math.min(a.dot(e), 1), -1),
- f = c.getFOVDotThreshold(s);
- if (p >= f) {
- var g = this.queueTilesInDirectionForPano(t, i, d, r, o, a, s);
- if (h += g > 0 ? 1 : 0,
- l && h >= l)
- break
- }
- }
- return h
- }
- }() */
- //import { i18n } from "@/lang/index"
- // 媒体名称
- /* export const mediaTypes = {
- image: i18n.t("common.photo"),
- video: i18n.t("common.video"),
- audio: i18n.t("common.voice"),
- } */
- // 媒体扩展类型
- const mediaMimes = {
- image: ["jpg", "png", "jpeg", "bmp", "gif"],
- audio: ["mp3", "aac", "ogg", "wav" /* , "m4a" */],
- video: ["mp4", "mov", "quicktime", "webm" /* "rmvb", "wmv" */], //ios:mov
- };
- // 媒体大小显示(MB)
- const mediaMaxSize = {
- image: 10,
- video: 20,
- audio: 5,
- };
- /**
- * 获取媒体扩展类型
- * @param {Stirng} filename 文件名称
- */
- const getMime = filename => {
- if (!filename || filename.indexOf(".") === -1) {
- return ""
- }
- return filename
- .split(".")
- .pop()
- .toLowerCase()
- };
- /**
- * 在路径中获取文件名
- * @param {*} path
- */
- const getFilename = path => {
- const segment = (path || "").split("/");
- return segment[segment.length - 1]
- };
- /**
- * 检测媒体文件是否超过预设限制
- * @param {String} type 媒体类型
- * @param {Number} size 文件大小
- */
- const checkSizeLimit = (type, size) => {
- size = size / 1024 / 1024;
- return size <= mediaMaxSize[type]
- };
- const checkSizeLimitFree = (size, limit) => {
- size = size / 1024 / 1024;
- return size <= limit
- };
- /**
- * 检测媒体类型
- * @param {String} type 媒体类型
- * @param {String} filename 文件名称
- */
- const checkMediaMime = (type, filename) => {
- const mime = getMime(filename);
- const find = mediaMimes[type];
- if (!find) {
- return false
- }
- return find.indexOf(mime) !== -1
- };
- const checkMediaMimeByAccept = (accept, filename) => {
- let mime = getMime(filename);
- let type = accept;
- if (type && type.indexOf("jpg") == -1 && type.indexOf("jpeg") != -1) {
- type += ",image/jpg";
- }
- return (type || "").indexOf(mime) != -1
- };
- const base64ToBlob = base64 => {
- let arr = base64.split(","),
- mime = arr[0].match(/:(.*?);/)[1],
- bstr = atob(arr[1]),
- n = bstr.length,
- u8arr = new Uint8Array(n);
- while (n--) {
- u8arr[n] = bstr.charCodeAt(n);
- }
- return new Blob([u8arr], { type: mime })
- };
- const base64ToDataURL = base64 => {
- return window.URL.createObjectURL(base64ToBlob(base64))
- };
- const blobToBase64 = function(blob) {
- return new Promise(resolve => {
- var reader = new FileReader();
- reader.onload = function() {
- resolve(reader.result);
- };
- reader.readAsDataURL(blob);
- })
- };
- /*
- * @Author: Rindy
- * @Date: 2019-08-06 16:25:08
- * @LastEditors: Rindy
- * @LastEditTime: 2021-08-27 12:33:49
- * @Description: Request
- */
- //import { $alert, $confirm, $loginTips } from "@/components/shared/message"
- //import { $waiting } from "@/components/shared/loading"
- //import { checkLogin } from "@/api"
- //import { LoginDetector } from "@/core/starter"
- //import { i18n } from "@/lang"
- //import { password } from "@/utils/string"
- //import store from "../Store"
- // 空函数
- const noop = function() {};
- // 请求回调队列
- let postQueue = [];
- /**
- * @property {number} NEXT - 继续执行
- * @property {number} SUCCESS - 成功
- * @property {number} EXCEPTION - 异常错误
- * @property {number} FAILURE_CODE_3001 - 缺少必要参数
- * @property {number} FAILURE_CODE_3002 - 访问异常
- * @property {number} FAILURE_CODE_3003 - 非法访问
- * @property {number} FAILURE_CODE_3004 - 用户未登录
- * @property {number} FAILURE_CODE_3005 - 验证码已过期
- * @property {number} FAILURE_CODE_3006 - 验证码错误
- * @property {number} FAILURE_CODE_3007 - 昵称已存在
- * @property {number} FAILURE_CODE_3008 - 该手机已被注册
- * @property {number} FAILURE_CODE_3009 - 两次输入的密码不一致
- * @property {number} FAILURE_CODE_3010 - 昵称长度错误
- * @property {number} FAILURE_CODE_3011 - 密码长度错误
- * @property {number} FAILURE_CODE_3012 - 昵称包含敏感词
- * @property {number} FAILURE_CODE_3013 - 手机号码格式错误
- * @property {number} FAILURE_CODE_3014 - 账号或密码不正确
- * @property {number} FAILURE_CODE_3015 - 用户不存在
- * @property {number} FAILURE_CODE_3016 - 您没有权限,请联系管理员
- * @property {number} FAILURE_CODE_3017 - 空文件
- * @property {number} FAILURE_CODE_3018 - 需要上传或使用的文件不存在
- * @property {number} FAILURE_CODE_5010 - 找不到该场景对应的相机
- * @property {number} FAILURE_CODE_5012 - 数据不正常
- * @property {number} FAILURE_CODE_5014 - 无权操作该场景
- * @property {number} FAILURE_CODE_5005 - 场景不存在
- */
- const statusCode = {
- NEXT: -999,
- SUCCESS: 0,
- EXCEPTION: -1,
- FAILURE_CODE_3001: 3001,
- FAILURE_CODE_3002: 3002,
- FAILURE_CODE_3003: 3003,
- FAILURE_CODE_3004: 3004,
- FAILURE_CODE_3005: 3005,
- FAILURE_CODE_3006: 3006,
- FAILURE_CODE_3007: 3007,
- FAILURE_CODE_3008: 3008,
- FAILURE_CODE_3009: 3009,
- FAILURE_CODE_3010: 3010,
- FAILURE_CODE_3011: 3011,
- FAILURE_CODE_3012: 3012,
- FAILURE_CODE_3013: 3013,
- FAILURE_CODE_3014: 3014,
- FAILURE_CODE_3015: 3015,
- FAILURE_CODE_3016: 3016,
- FAILURE_CODE_3017: 3017,
- FAILURE_CODE_3018: 3018,
- FAILURE_CODE_5010: 5010,
- FAILURE_CODE_5012: 5012,
- FAILURE_CODE_5014: 5014,
- FAILURE_CODE_5005: 5005,
- };
-
- /**
- * 获取Token
- * @function
- */
- function getToken() {
- var urlToken = browser.urlHasValue("token", true);
- if (urlToken) {
- // 设置token共享给用户中心
- localStorage.setItem("token", urlToken);
- }
- return urlToken || localStorage.getItem("token") || ""
- }
- /**
- * 根据状态码的结果处理后续操作
- * @function
- * @param {number} code - 状态码
- * @param {function} callback - 回调
- */
- function statusCodesHandler(code, callback) {
- if (code == statusCode.EXCEPTION) {
- return $alert({ content: i18n.t("tips.exception") })
- }
- if (
- code == statusCode.FAILURE_CODE_3002 ||
- code == statusCode.FAILURE_CODE_3003 ||
- code == statusCode.FAILURE_CODE_3004
- ) {
- callback(code);
- return showLoginTips()
- }
- if (code == statusCode.FAILURE_CODE_3001) {
- callback(code);
- return $alert({ content: i18n.t("tips.params_notfound") })
- }
- if (code == statusCode.FAILURE_CODE_3017) {
- callback(code);
- return $alert({ content: i18n.t("tips.file_notfound") })
- }
- if (code == statusCode.FAILURE_CODE_5005) {
- /* if (!config.isEdit) {
- return (location.href = config.pages.NotFound)
- } */
- callback(code);
- return $alert({ content: i18n.t("tips.scene_notfound") })
- }
- if (code == statusCode.FAILURE_CODE_5010) {
- callback(code);
- return $alert({ content: i18n.t("tips.camera_notfound") })
- }
- if (code == statusCode.FAILURE_CODE_5012) {
- callback(code);
- return $alert({ content: i18n.t("tips.data_error") })
- }
- if (code == statusCode.FAILURE_CODE_5014) {
- callback(code);
- return $alert({ content: i18n.t("tips.auth_deny") })
- }
- return statusCode.NEXT
- }
- $.ajaxSetup({
- headers: {},
- beforeSend: function(xhr) {
- const token = getToken();
- if (token) {
- xhr.setRequestHeader("token", token);
- } else if (!token && this.url.indexOf("isLogin") != -1) {
- showLoginTips();
- }
- /* if (config.oem == "localshow") {
- // 本地版本兼容当前目录
- if (this.url.indexOf("http") == -1 && this.url.indexOf("/") == 0) {
- this.url = this.url.substr(1)
- }
- } */
- // if(this.url.indexOf('http')==-1 && this.url.indexOf('/') !=0){
- // this.url = '/'+this.url
- // }
- },
- error: function(xhr, status, error) {
- // 出错时默认的处理函数
- if (this.url.indexOf("/scene.json") != -1 && xhr.status == 404) {
- return $alert({ content: i18n.t("tips.scene_notfound") })
- } else if (this.type === "POST") {
- return $alert({ content: i18n.t("tips.network_error") })
- }
- },
- success: function(result) {},
- complete: function() {
- // Post类型请求无论成功或失败都关闭等待提示
- if (this.type === "POST") {
- http.__loading && $waiting.hide();
- }
- http.__loading = true;
- },
- });
- /**
- * @namespace http
- * @type {Object}
- */
- const http = {
- statusCode,
- __loading: true,
- __request(xhr, method, url, data, done, fail) {
- if (typeof done != "function") {
- done = noop;
- }
- if (typeof fail != "function") {
- fail = noop;
- }
- xhr.done(result => {
- if (typeof result.code !== "undefined") {
- const flag = statusCodesHandler(result.code, function(code) {
- // 需要登录的状态
- if (
- code == statusCode.FAILURE_CODE_3001 ||
- code == statusCode.FAILURE_CODE_3002 ||
- code == statusCode.FAILURE_CODE_3003 ||
- code == statusCode.FAILURE_CODE_3004
- ) {
- if (url.indexOf("isLogin") == -1 && url.indexOf("openSceneBykey") == -1) {
- postQueue.push(function() {
- http[method](url, data, done, fail);
- });
- }
- }
- fail();
- });
- if (flag === statusCode.NEXT) {
- done(result, result.code == 0);
- }
- } else {
- done(result);
- }
- });
- xhr.fail(fail);
- xhr.always(() => (xhr = null));
- return xhr
- },
- /**
- * Get请求
- * @param {String} url 请求地址
- * @param {Object?} data 请求参数
- * @param {Function?} done 成功回调
- * @param {Function?} fail 失败回调
- */
- get(url, data = {}, done, fail) {
- if (/\.json/.test(url)) {
- // json文件格式自动调用getJson方法
- return this.getJson(url, data, done, fail)
- }
- return this.__request($.get(url, data), "get", url, data, done, fail)
- },
- /**
- * Get Blob请求
- * @param {String} url 请求地址
- * @param {Object?} data 请求参数
- * @param {Function?} done 成功回调
- * @param {Function?} fail 失败回调
- */
- getText(url, data = {}, done, fail) {
- return this.__request(
- $.ajax({
- url: url,
- dataType: "text",
- }),
- "getText",
- url,
- data,
- done,
- fail
- )
- },
- /**
- * GetJson请求 读取json文件数据
- * @param {String} url 请求地址
- * @param {Object?} data 请求参数
- * @param {Function?} done 成功回调
- * @param {Function?} fail 失败回调
- */
- getJson(url, data = {}, done, fail) {
- return this.__request($.getJSON(url, data), "get", url, data, done, fail)
- },
- /**
- * Get Blob请求
- * @param {String} url 请求地址
- * @param {Object?} data 请求参数
- * @param {Function?} done 成功回调
- * @param {Function?} fail 失败回调
- */
- getBlob(url, data = {}, done, fail) {
- return this.__request(
- $.ajax({
- url: url,
- dataType: "blob",
- }),
- "getBlob",
- url,
- data,
- done,
- fail
- )
- },
- /**
- * Get Arraybuffer请求
- * @param {String} url 请求地址
- * @param {Object?} data 请求参数
- * @param {Function?} done 成功回调
- * @param {Function?} fail 失败回调
- */
- getArraybuffer(url, data = {}, done, fail) {
- return this.__request(
- $.ajax({
- url: url,
- dataType: "arraybuffer",
- }),
- "getArraybuffer",
- url,
- data,
- done,
- fail
- )
- },
- /**
- * Post 请求
- * @param {String} url 请求地址
- * @param {Object?} data 请求参数
- * @param {Function?} done 成功回调
- * @param {Function?} fail 失败回调
- */
- post(url, data = {}, done, fail) {
- if (url.indexOf("isLogin") == -1) {
- http.__loading && $waiting.show();
- }
- return this.__request($.post(url, data), "post", url, data, done, fail)
- },
- /**
- * PostJson 请求
- * @param {String} url 请求地址
- * @param {Object?} data 请求参数
- * @param {Function?} done 成功回调
- * @param {Function?} fail 失败回调
- */
- postJson(url, data = {}, done, fail) {
- http.__loading && $waiting.show();
- return this.__request(
- $.ajax({
- type: "POST",
- url: url,
- contentType: "application/json",
- data: JSON.stringify(data),
- }),
- "postJson",
- url,
- data,
- done,
- fail
- )
- },
- /**
- * Post 表单 支持文件上传
- * @param {String} url 请求地址
- * @param {FormData?} formData 请求参数
- * @param {Function?} done 成功回调
- * @param {Function?} fail 失败回调
- */
- postForm(url, formData, done, fail, onProgress) {
- if (typeof onProgress === "function") {
- return this.__request(
- $.ajax({
- type: "POST",
- url: url,
- processData: false,
- contentType: false,
- data: formData,
- xhr: function() {
- const xhr = new XMLHttpRequest();
- xhr.upload.addEventListener("progress", function(e) {
- onProgress((e.loaded / e.total) * 100 + "%");
- });
- return xhr
- },
- }),
- "postForm",
- url,
- formData,
- done,
- fail
- )
- } else {
- http.__loading && $waiting.show();
- return this.__request(
- $.ajax({
- type: "POST",
- url: url,
- processData: false,
- contentType: false,
- data: formData,
- }),
- "postForm",
- url,
- formData,
- done,
- fail
- )
- }
- },
- /**
- * 加载图片
- * @param {String} url 请求地址
- * @param {Number?} retry 重试次数,默认为3
- */
- loadImage(url, retry = 3) {
- const def = $.Deferred();
- const img = new Image();
- /* if (process.env.VUE_APP_REGION == "AWS" && url.indexOf("x-oss-process=image") != -1) {
- var arr = url.split("?")
- url = arr[0] + encodeURIComponent("?" + arr[1].replace(/\//g, "@"))
- } */
- const load = () => {
- console.warn("Retrying load image: " + url);
- this.loadImage(url, retry - 1)
- .done(def.resolve.bind(def))
- .progress(def.notify.bind(def))
- .fail(def.reject.bind(def));
- };
- img.onerror = function() {
- retry > 0 ? setTimeout(() => load(), 1e3) : def.reject(`[${url}]加载失败`);
- };
- img.onload = function() {
- def.resolve(img);
- };
- img.crossOrigin = "anonymous";
- img.src = url;
- return def
- },
- /**
- * 上传文件
- * @param {String} url 请求地址
- * @param {Object?} data 请求参数
- * @param {Function?} done 成功回调
- * @param {Function?} fail 失败回调
- */
- uploadFile(url, data = {}, done, fail, onProgress) {
- const form = new FormData();
- // if (file.needTransfer) { //ie和苹果都不支持dataURLtoFile得传送,所以只能用blob
- // form.append("file", common.dataURLtoBlob(file.file), file.name || file.file.name);
- // } else {
- // form.append("file", file.file, file.name || file.file.name);
- // }
- for (let key in data) {
- if (key == "file") {
- form.append("file", data[key], data.filename || data[key].name);
- } else if (key != "filename") {
- form.append(key, data[key]);
- }
- }
- return this.postForm(url, form, done, fail, onProgress)
- },
- /**
- * 上传文件
- * @param {String} url 请求地址
- * @param {Object?} data 请求参数 {file:'base64 string',filename:'image.jpg',...}
- * @param {Function?} done 成功回调
- * @param {Function?} fail 失败回调
- */
- uploadBlobFile(url, data = {}, done, fail) {
- const form = new FormData();
- for (let key in data) {
- if (key === "file") {
- form.append("file", base64ToBlob(data.file), data.filename);
- } else if (key != "filename") {
- form.append(key, data[key]);
- }
- }
- return this.postForm(url, form, done, fail)
- },
- };
- window.downloaded = {};
- window.startdownloads = [];
- class TileDownloader extends EventDispatcher{
- constructor( ) {
- super();
- this.panos = null;
- this.retryMinimumTime = 1e4;
- this.panoLoadCallbacks = {};
- this.downloadDescriptors = {};
- this.priorityQueue = [];
- this.forceQueue = [];
- this.activeDownloads = [];
- this.tilePrioritizer = null;
- this.refreshInterval = null;
- this.processPriorityQueue = !1;
- this.concurrentDownloads = 6;//e.concurrentDownloads || 1;
- this.downloadTestResults = {};
- this.freeze = Object.freeze({
- Testing: 1,
- Success: 2,
- Fail: 3
- });
-
- this.visible = true; //add 借用viewer.updateVisible来判断是否start
-
- viewer.addEventListener('pageVisible', (e)=>{//不可见时不refreshUpdateInterval
- //console.log('visibilitychange:', state)
- viewer.updateVisible(this, 'pageVisible', e.v);
- this.judgeStart();
- });
-
- }
- setPanoData(e, t /* , i */) {
- this.panos = e,
- this.imagePanos = t;
- // this.panoGroupId = i
- }
-
- start() {
- this.downloadCubeTex = true;
- if(!Potree.settings.useDepthTex){
- viewer.updateVisible(this,'pano', true );
- this.judgeStart();
- }else {
- this.refreshInterval || this.judgeStart();
- }
- }
- stop() {
- this.downloadCubeTex = false;
- if(!Potree.settings.useDepthTex){
- viewer.updateVisible(this,'pano', false );
- this.judgeStart();
- }
- }
- judgeStart(){//add
- if(this.visible){
- //console.log('judgeStart true')
- this.started = true;
- this.refreshUpdateInterval(0);
- }else {
- //console.log('judgeStart false')
- this.started = false;
- window.clearTimeout(this.refreshInterval);
- }
-
- }
- refreshUpdateInterval(e) {
- e || (e = 0),
- this.refreshInterval = window.setTimeout(function() {
- var e = this.update();
- e ? this.refreshUpdateInterval(TileDownloader.ACTIVE_REFRESH_DELAY) : this.refreshUpdateInterval(TileDownloader.IDLE_REFRESH_DELAY);
- }
- .bind(this), e);
- }
- update() {
- if(this.downloadCubeTex){ //可以下载贴图
- var e = this.forceQueue.length > 0;
- this.processQueueForDownloading(this.forceQueue);
- if (this.processPriorityQueue) {
- this.queuePrioritizedTilesForPanos(this.panos);
- this.priorityQueue.length > 0 && (e = !0);
- this.processQueueForDownloading(this.priorityQueue);
- }
- return e
- }else {//仅下载depthTex
- this.tilePrioritizer.filterDepthTex(this.panos);
- }
-
- }
-
- queuePrioritizedTilesForPanos(e) {
- this.tilePrioritizer && (this.clearQueue(this.priorityQueue),
- this.tilePrioritizer.filterAndPrioritize(this.priorityQueue, e, this),
- this.clearFromQueue(this.priorityQueue, DownloadStatus.None, !0), //去除state为DownloadStatus.None的(可能是去除已经在下载的)
- this.setStatusOrRemoveForAllDescriptors(this.priorityQueue, DownloadStatus.Queued));
- }
- clearQueue(e) {//停止下载并清空
- this.setStatusForAllDescriptors(e, DownloadStatus.None),
- e.length = 0;
- }
-
- clearForceQueue() {
- this.clearQueue(this.forceQueue);
- }
-
- clearFromQueue(e, t, i) {
- for (var n = 0; n < e.length; n++) {
- var r = e[n];
- r && (t === r.status && !i || t !== r.status && i) && (e[n] = null);
- }
- }
- setStatusForAllDescriptors(e, t) {
- for (var i = 0; i < e.length; i++) {
- var n = e[i];
- n && (n.status = t);
- }
- }
- setStatusOrRemoveForAllDescriptors(e, t) {
- for (var i = 0; i < e.length; i++) {
- var n = e[i];
- n && (n.status !== t ? n.status = t : e[i] = null);
- }
- }
- getTileDownloadDescriptors(pano, size) {//获取该pano的该size的全部的tile的descriptor
- var i = this.getAllTileDownloadDescriptorsForPano(pano),
- n = i[size];
- return n || (n = this.buildDownloadDescriptorArray(size),//创建的全部是空的
- i[size] = n,
- this.initTileDownloadDescriptors(n, pano, size)),//绑定到该pano size
- n
- }
- getAllTileDownloadDescriptorsForPano(pano) {//新建空Descriptors
- var t = this.downloadDescriptors[pano.id];
- return t || (t = {},
- this.downloadDescriptors[pano.id] = t),
- t
- }
- processQueueForDownloading(e, t) {//执行下载任务
- this.cleanupActiveDownloads();
- if (this.activeDownloads.length < this.concurrentDownloads || t) {
- var i = t ? e.length : this.concurrentDownloads - this.activeDownloads.length;
- for (var n = 0, r = 0; n < i && e.length > 0; r++) {
- var o = e.shift();
-
-
- if(o){
- //add 为了防止1024的在512前下载完,这里强行等待512下载完毕再开始下载
- if(o.panoSize > 512 && !this.isPanoDownloaded(o.pano, 512) ){
- //console.log('512的还没下载好呢!')
- e.push(o);
- break;//一般512的都是连续下载的,所以后面就都不是512了直接中断
- }
-
- this.startDownload(o);
- n++;
- }
-
- }
- }
- }
-
-
-
-
- testDownload(panoSize, tileSize, callback) {
- var n = this.downloadTestResults[panoSize];
- if (n)
- return void(n === this.freeze.Success ? callback(!0) : n === this.freeze.Fail && callback(!1));
- this.downloadTestResults[panoSize] = this.freeze.Testing;
- var r = this.panos[0],
- o = this.getTileUrl({pano:r, panoSize, tileSize, tileIndex:0} /* r.id, panoSize, tileSize, 0 */),
- a = function(t) {
- this.downloadTestResults[panoSize] = this.freeze.Success,
- callback(!0);
- }
- .bind(this),
- s = function() {
- this.downloadTestResults[panoSize] = this.freeze.Fail,
- callback(!1);
- }
- .bind(this);
- this.loadImage(o, 0, a, s);
- }
- startDownload(e) {//开始下载啦
- //console.log('startDownload')
-
- startdownloads.push(e);
-
- e.status = DownloadStatus.Downloading;
- var t = this.getTileUrl(e/* e.pano.id, e.panoSize, e.tileSize, e.tileIndex, e.pano.alignmentType */);//xzw add alignmentType
- if(!t)return;
- this.activeDownloads.push(e);
- this.loadImage(t, TileDownloader.DOWNLOAD_RETRIES, this.downloadComplete.bind(this, e), this.downloadFailed.bind(this, e));
- }
- downloadFailed(e, t) {}
- downloadComplete(e, t) {//下载成功时
- //if (e.panoGroupId === this.panoGroupId) {
- var i = this.getPanoLoadCallbacks(e.pano, e.panoSize);
- e.status = DownloadStatus.Downloaded,
- i && i.onProgress && i.onProgress(e.pano, e.panoSize);
- var n = {
- panoId: e.pano.id,
- image: t,
- tileSize: e.tileSize,
- panoSize: e.panoSize,
- tileIndex: e.tileIndex,
- faceTileIndex: e.faceTileIndex,
- totalTiles: e.totalTiles,
- face: e.face,
- tileX: e.tileX,
- tileY: e.tileY,
- direction: e.direction
- };
-
- downloaded[e.pano.id] || (downloaded[e.pano.id]={512:[],1024:[],2048:[]});
- downloaded[e.pano.id][e.panoSize] || (downloaded[e.pano.id][e.panoSize] = []);
- downloaded[e.pano.id][e.panoSize].push(e);
- if(e.panoSize != 512 && downloaded[e.pano.id][512].length<6){
- console.warn('没下完');
- }
-
-
-
- e.image = t,
- this.dispatchEvent({type:TileDownloaderEvents.TileDownloadSuccess, desc:n} );
- this.isPanoDownloaded(e.pano, e.panoSize) && (n = {
- panoId: e.pano.id,
- tileSize: e.tileSize,
- panoSize: e.panoSize
- },
- this.dispatchEvent({type:TileDownloaderEvents.PanoDownloadComplete, desc:n}),
- i && i.onLoad && i.onLoad(e.pano, e.panoSize));
- //}
- }
- isPanoDownloaded(e, t) {
- var i = this.getTileDownloadDescriptors(e, t);
- if (i.length <= 0)
- return !1;
- for (var n = 0; n < i.length; n++) {
- var r = i[n];
- if (r.status !== DownloadStatus.Downloaded)
- return !1
- }
- return !0
- }
- setPanoLoadCallbacks(e, t, i, n, r) {
- var o = e.id + ":" + this.qualityManager.getPanoSize(t);
- this.panoLoadCallbacks[o] = {
- onLoad: i,
- onFail: n,
- onProgress: r
- };
- }
- getPanoLoadCallbacks(e, t) {
- var i = e.id + ":" + t;
- return this.panoLoadCallbacks[i]
- }
- buildDownloadDescriptorArray(e) {
- for (var t = TileUtils.getTileCountForSize(e), i = [], n = 0; n < t; n++) {
- var r = this.buildDownloadDescriptor();
- i.push(r);
- }
- return i
- }
- buildDownloadDescriptor() {//Descriptor!
- var e = {
- panoGroupId: null,
- pano: null,
- panoSize: -1,
- tileSize: -1,
- tileIndex: -1,
- totalTiles: -1,
- faceTileIndex: -1,
- status: DownloadStatus.None,
- url: null,
- image: null,
- direction: new Vector3, //该tile在cube中的方向
- face: -1,
- cubeFace: -1,
- tileX: -1,
- tileY: -1
- };
- return e
- }
- initTileDownloadDescriptors(e, t, i) {
- for (var n = 0; n < e.length; n++) {
- var r = e[n];
- this.initTileDownloadDescriptor(r, t, i, n);
- }
- }
- initTileDownloadDescriptor(desc, pano, size, index) {
- var r = size >= TileUtils.TILE_SIZE ? TileUtils.TILE_SIZE : size;
- desc.face = TileUtils.getFaceForTile(size, index);//根据顺序得到的face的index
- desc.cubeFace = TileUtils.mapFaceToCubemapFace(desc.face);//为了贴图而转化的face index
- //desc.panoGroupId = this.panoGroupId;//就是场景号
- desc.pano = pano;
- desc.panoSize = size;
- desc.tileSize = r; //瓦片图size 512
- desc.tileIndex = index;
- desc.totalTiles = TileUtils.getTileCountForSize(size);
- desc.status = DownloadStatus.None;
- desc.image = null;
- TileUtils.getTileLocation(desc.panoSize, desc.tileIndex, desc);//得到该tile在这个face中的具体位置(tileX等)
- TileUtils.getTileVector(desc.panoSize, desc.tileSize, desc.cubeFace, desc.tileX, desc.tileY, TileUtils.LocationOnTile.Center, 0, desc.direction);
- }
-
- getTiles(d, sceneNum){
- return `${Potree.settings.urls.prefix3}/images/images${sceneNum}/${d}`
- }
- loadImage(e, t, i, n) {
- //自己修改了ajax,把getImage改成了loadImg
- http.loadImage(e, t).then(function(e) {
- i(e);
- }).fail(n);
- }
- }
- TileDownloader.prototype.forceQueueTilesForPano = function() {//根据条件开始加载tile
- var e = [],
- t = [];
- return function(pano, size, dir, hFov, vFov, download) {
- e.length = 0;
- for (var u = this.getTileDownloadDescriptors(pano, size), d = 0; d < u.length; d++) {
- var p = u[d];
- p.status !== DownloadStatus.None && p.status !== DownloadStatus.Queued || e.push(p);
- }
- if (dir && e.length > 0) {
- TilePrioritizer.sortPanoTiles(e, pano, dir); //按最佳方向排序e
- t.length = 0;
- TileUtils.matchingTilesInDirection(pano, size, dir, hFov, vFov, t);//得到在符合视野标准的集合t
-
-
-
- for (var f = 0, g = function(e) {
- return e.face === m.face && e.faceTileIndex === m.faceTileIndex
- }; f < e.length;) { //过滤掉不符合角度要求的
- var m = e[f],
- v = t.findIndex(g);
- v < 0 ? e.splice(f, 1) : f++;
- }
- }
- for (var A = 0; A < e.length; A++){
- this.forceQueue.push(e[A]); //装载
- }
- /* if(e.length){
- console.log(e)
- } */
-
- this.setStatusForAllDescriptors(this.forceQueue, DownloadStatus.ForceQueued);
- this.clearFromQueue(this.priorityQueue, DownloadStatus.ForceQueued, !1);
- download && this.processQueueForDownloading(this.forceQueue, !0);
- }
- }();
- TileDownloader.prototype.cleanupActiveDownloads = function() {
- var e = [];
- return function() {
- e.length = 0;
- for (var t = 0; t < this.activeDownloads.length; t++) {
- var i = this.activeDownloads[t];
- i.status !== DownloadStatus.Downloaded && i.status !== DownloadStatus.Failed && e.push(i);
- }
- this.activeDownloads.length = 0,
- this.activeDownloads.push.apply(this.activeDownloads, e);
- }
- }();
- TileDownloader.prototype.getTileUrl = function() {
- var e = {
- 256: "256",
- 512: "512",
- 1024: "1k",
- 2048: "2k",
- 4096: "4k"
- },
- t = {
- face: -1,
- faceTileIndex: -1,
- tileX: -1,
- tileY: -1
- };
-
- return function(o={} ) {
- var id = o.pano.originID, ////////
- panoSize = o.panoSize,
- tileSize = o.tileSize,
- tileIndex = o.tileIndex,
- sceneCode = o.pano.pointcloud.sceneCode;
- var metadata = {sceneScheme:10};
-
-
- TileUtils.getTileLocation(panoSize, tileIndex, t);
- var s = Math.floor(panoSize / tileSize),
- l = s * s,
- h = Math.floor(tileIndex / l),
- u = "",
- d = '', g = '';
-
-
-
- if(Potree.settings.isLocal){//原始规则
- //1 === config.tiling.customCompression && (u = "_" + config.tiling["q" + e[panoSize]]);
- //1 === o.tiling.customCompression && (u = "_" + o.tiling["q" + e[n]]);
- d = "tiles/" + id + "/" + e[panoSize] + u + "_face" + h + "_" + t.tileX + "_" + t.tileY + ".jpg";
- d = this.getTiles(d, sceneCode);
- g = "?";
-
- }else {//阿里云oss的规则 if (metadata.sceneScheme == 10)
-
- d = 'tiles/4k/' + id + '_skybox' + h + '.jpg?x-oss-process=';
- if (e[panoSize] == '512') {
- d += 'image/resize,h_512';
- } else {
- //4k的图,移动端是1k,pc端是2k,放大才是4k
- if (e[panoSize] == '1k' || e[panoSize] == '2k') { //https://4dkk.4dage.com/images/imagesx4iqYDG3/tiles/4k/122_skybox0.jpg?x-oss-process=image/resize,m_lfit,w_1024/crop,w_512,h_512,x_511,y_0
- d += 'image/resize,m_lfit,w_' + panoSize + '/crop,w_512,h_512,';
- } else {
- d = 'tiles/4k/' + id + '_skybox' + h + '.jpg?x-oss-process=image/crop,w_512,h_512,';
- }
- //起始位置
- if (t.tileX == 0) {
- d += 'x_0,';
- } else {
- d += 'x_' + (512 * t.tileX - 1) + ',';
- }
- if (t.tileY == 0) {
- d += 'y_0';
- } else {
- d += 'y_' + (512 * t.tileY - 1);
- }
- }
-
- d = this.getTiles(d, sceneCode);
- g = "&";
- }
-
- d += g + 'time='+o.pano.pointcloud.timeStamp; //加后缀
-
- return d;
- }
- }();
- TileDownloader.tilegen = true;
- TileDownloader.IDLE_REFRESH_DELAY = 500;
- TileDownloader.ACTIVE_REFRESH_DELAY = 16;
- TileDownloader.DOWNLOAD_RETRIES = 4;
- function Node$1(e, t) {
- this.tree = e, //所属树(TileTree)
- this.parent = t,
- this.children = [],
- this.id = ++u$1;
- }
- function o(e, t, i, r, a, s, l, h) {
- if (e) {
- l = l || TileTree.TraversalType.PreOrder;
- var u = r * c + i;
- if (l === TileTree.TraversalType.PreOrder && (a && a(e, t, u, i, r),
- s && s.push(e)),
- e.children && 0 !== e.children.length) {
- for (var d = r * c, p = i * c, f = 0; f < c; f++)
- for (var g = 0; g < c; g++)
- o(e.children[g * c + f], t + 1, p + f, d + g, a, s, l, h);
- l === TileTree.TraversalType.PostOrder && (a && a(e, t, u, i, r),
- s && s.push(e));
- }
- }
- }
- function Plant(seed) {
- seed.root = Branch(seed, null, 0);
- }
- function Branch(seed, parent, level) {
- if (level > seed.levels)
- return null;
- var node = new Node$1(seed, parent);
- seed.allNodes.push(node);
- for (var o = 0; o < h$1; o++)
- node.children[o] = Branch(seed, node, level + 1);
- return node
- }
- function l(parent, t, level, n, r) {
- if (!parent)
- return null;
- if (0 === level)
- return parent;
- if (!parent.children || 0 === parent.children.length)
- return null;
- var o = Math.pow(c, level),
- a = o / c,
- s = n % a,
- h = r % a,
- u = Math.floor(r / a),
- d = Math.floor(n / a),
- p = u * c + d,
- f = parent.children[p];
- return l(f, t + 1, level - 1, s, h)
- }
- /* cube每个面都有一个分层树 用于代表瓦片图的细分?
- 树4096的分为三层,每层有4个子节点。(最后一层的四个子节点都是null)
- */
-
-
- var c = 2,
- h$1 = c * c; //4个子节点
- var u$1 = 0;
- class TileTree {
- constructor(e, t) {
- this.levels = t,
- this.tileSize = e,
- this.root = null,
- this.allNodes = [],
- Plant(this);
- }
- getSubNode(e, t, i) {
- (!t || e < this.tileSize) && (t = 0),
- (!i || e < this.tileSize) && (i = 0),
- e < this.tileSize && (e = this.tileSize);
- var level = TileTree.getLevelCountForSize(this.tileSize, e),
- o = l(this.root, 0, level, t, i);
- return o
- }
- breadthFirst(e) {//广度优先搜索
- e = e || {};
- var t = !!e.nullLevelEnd,
- i = e.maxLevel,
- n = e.minLevel,
- r = e.callback,
- o = e.saveVisited,
- a = [],
- s = {},
- l = 0,
- c = 0;
- for (a.push(this.root),
- a.push(s); a.length > 0 && !(i && l > i);) {
- var h = a.shift();
- if (h === s)
- (!n || l >= n) && (r && t && r(null),
- o && t && o.push(null)),
- a.length > 0 && a.push(s),
- l++,
- c = 0;
- else {
- if (h.children)
- for (var u = 0; u < h.children.length; u++) {
- var d = h.children[u];
- d && a.push(h.children[u]);
- }
- var p = this.getFaceIndexFromNode(h);
- (!n || l >= n) && (r && r(h, l, p),
- o && o.push(h)),
- c++;
- }
- }
- }
- getFaceIndexFromNode(e) {
- if (!e)
- return -1;
- for (var t = 1, i = e, n = 0, r = 0;;) {
- var o = i.parent;
- if (!o)
- break;
- for (var a = -1, s = 0; s < o.children.length; s++)
- o.children[s] === i && (a = s);
- var l = a % c,
- h = Math.floor(a / c);
- n = l * t + n,
- r = h * t + r,
- t *= c,
- i = o;
- }
- return r * t + n
- }
- depthFirst(e, t, i) {
- o(this.root, 0, 0, 0, e, t, i, this.tileSize);
- }
- }
- TileTree.TraversalType = Object.freeze({
- PreOrder: 0,
- PostOrder: 1
- });
- TileTree.getLevelCountForSize = function(tileSize, size) {//512->0 2024->1
- var i = 0;
- for (size < tileSize && (size = tileSize);;) {
- if (size /= c,
- size < tileSize)
- break;
- i++;
- }
- return i
- };
- TileTree.getSizeForLevel = function(e, t) {
- return Math.pow(c, t) * e
- };
- function createDescriptor() {
- var e = {
- renderTarget: null,
- inUse: !1,
- size: -1,
- pano: null
- };
- return e
- }
-
-
- function c$1() {
- if (!this.uploadIntervalCancelled) {
- if (this.overlayTilesLoaded || !this.usingTileOverlay) {
- b = !0,
- this.updateUploadQueue(this.maxNonBaseUploadsPerFrame, this.maxBaseUploadsPerFrame),
- this.peekNextFromUploadQueue() ? this.refreshUploadInterval(w) : this.uploadInterval = null;
- } else {
- this.refreshUploadInterval(this.uploadIntervalDelay);
- }
- }
- }
- var b = !1,
- w = config$1.tiling.uploadIntervalDelay,
- _ = config$1.tiling.initialIntervalDelay,
- T = config$1.tiling.maxNonBaseUploadsPerFrame,
- x$1 = config$1.tiling.maxBaseUploadsPerFrame,
- S = {
- Base: 0,
- Remaining: 1
- };/* ,
- M = []; */
- class PanoRenderer extends EventDispatcher{
- constructor(viewer, tileDownloader, qualityManager) {
- super();
- this.tileDirectory = {};
- this.activeRenderTargetDescriptors = {};
- this.activePanos = [];
- this.panoLODDescriptors = {};
- this.panoDescriptors = {};
- this.tileTrees = {};
- this.forceQueue = [];
- this.uploadQueues = {};
- this.uploadInterval = null;
- this.uploadIntervalCancelled = !1;
- this.usingTileOverlay = !1;
- this.overlayTilesLoaded = !1;
- this.overlayTileBase = null;
- this.overlayTilesBasic = {};
- this.overlayTilesEnhanced = {};
- this.zoomRenderTarget = null; //用于缩放的rendertarget
- this.zoomPano = null;
- this.zoomingActive = !1;
- this.zoomPanoId = null;
- this.zoomPanoRenderingDisabled = !1;
- this.direction = [];//new THREE.Vector3;
-
- this.maxBaseUploadsPerFrame = x$1;
- this.maxNonBaseUploadsPerFrame = T;
-
-
-
- this.M = [];//move M to here 似乎列表里会有两个
- this.viewer = viewer;
-
- this.tileDownloader = tileDownloader;
- this.qualityManager = qualityManager;
-
- this.initTime = performance.now();
- this.bindEvents();
-
-
- }
-
-
-
- getActivePanoTextures(e) {
- e = e || [];
- for (var t = 0; t < M.length; t++) {
- var i = M[t];
- i.renderTarget && i.renderTarget.texture && e.push(i.renderTarget.texture);
- }
- }
- hasQueuedTiles() {
- var e = this.peekNextFromUploadQueue();
- return null !== e && void 0 !== e
- }
- getActiveRenderTargetDescriptor(e) {
- return this.activeRenderTargetDescriptors[e]
- }
- setActiveRenderTargetDescriptor(e, t) {
- this.activeRenderTargetDescriptors[e] = t;
- }
-
- bindEvents() {
- this.tileDownloader.addEventListener(TileDownloaderEvents.TileDownloadSuccess, this.onTileDownloaded.bind(this));
- }
- enableUltraHighQualityMode(e) {
- if(config$1.tileClass == "2k"||config$1.tileClass == "1k")return this.enableHighQuality(e)//xzw add 濡傛灉鏈€澶氬彧瑕?k鐨勮瘽
- if (!this.qualityManager.ultraHighQualityModeEnabled()) {
- var t = this.qualityManager.getPanoSize(PanoSizeClass.ULTRAHIGH);
- this.tileDownloader.testDownload(t, TileUtils.TILE_SIZE, function (t) {
- t && (this.qualityManager.enableUltraHighQualityMode(),
- this.setupZoomRenderTarget(),
- e());
- }
- .bind(this));
- }
- }
- activateTiledPano(pano, size, i) {
- i && this.clearAllQueuedUploads();
- for (var n = 0; n < TileUtils.FACES_PER_PANO; n++)
- this.initTileTree(pano.id, n, this.qualityManager.getMaxPossiblePanoSize()); //得到this.tileTrees[pano.id],arr[6]
- this.linkAllTilesAndNodes(pano);
- var r = this.getActiveRenderTargetDescriptor(pano.id),
- l = size;
-
- l > this.qualityManager.getMaxNavPanoSize() && (l = this.qualityManager.getMaxNavPanoSize());
- if ( !r || l !== r.size) {
- r && this.deactiveDescripor(r.renderTarget);
- r = this.activeDescripor(l);
- if (!r) {
- var ren = this.initTiledPano(l, !1);
- r = this.initDescriptor(ren.width);
- r.renderTarget = ren;
- }
- r.pano = pano;
- this.resetPanoDescriptor(pano.id);
- this.resetPanoLODDescriptors(pano.id);
- this.resetRenderStatus(pano.id, !0, !0);
- }
- this.setActiveRenderTargetDescriptor(pano.id, r);
- var h = i ? 0 : 1;
-
- this.updateActivePanos(pano, h);
-
- //console.log(`index:${this.viewer.index} ${r.renderTarget.texture.id} ${pano.id}`)
-
- return r.renderTarget
-
- }
- deactivateTiledPano(e) {
- var t = this.getActiveRenderTargetDescriptor(e.id);
- if(this.isRenderTargetDescriptorValid(t)){
- this.deactiveDescripor(t.renderTarget);
- this.setActiveRenderTargetDescriptor(e.id, null);
- }
- var i = this.getUploadQueueForPano(e.id);
- this.clearUploadQueue(i);
- this.updateActivePanos();
- }
- getActivePanoCount() {
- return this.activePanos.length
- }
- resetRenderStatus(e, t, i, n) {
- var r = null;
- n && (r = TileTree.getLevelCountForSize(TileUtils.TILE_SIZE, n) + 1);
- for (var o = function (e, n, r, o) {
- i && (n.tile.zoomUploaded = !1),
- t && (n.tile.uploaded = !1);
- }, a = 0; a < TileUtils.FACES_PER_PANO; a++) {
- var s = this.getTileTree(e, a);
- s.breadthFirst({
- callback: o.bind(this, a),
- minLevel: r
- });
- }
- }
- copyBaseRenderStatusToZoomed(e) {
- for (var t = TileTree.getLevelCountForSize(TileUtils.TILE_SIZE, this.qualityManager.getMaxNavPanoSize()), i = function (e, t, i, n) {
- t.tile.zoomUploaded = t.tile.uploaded,
- t.zoomCovered = t.covered;
- }, n = 0; n < TileUtils.FACES_PER_PANO; n++) {
- var r = this.getTileTree(e, n);
- r.breadthFirst({
- callback: i.bind(this, n),
- maxLevel: t
- });
- }
- }
- isRenderTargetDescriptorValid(e) {
- return e && e.renderTarget
- }
- isPanoActive(e) {
- var t = this.getActiveRenderTargetDescriptor(e);
- return this.isRenderTargetDescriptorValid(t)
- }
- isPanoZoomed(e) {
- return this.zoomingActive && this.zoomPanoId === e
- }
- initTileTree(e, t, i) {
- var n = this.tileTrees[e];
- n || (n = [],
- this.tileTrees[e] = n);
- var r = n[t];
- if (!r) {
- var o = TileTree.getLevelCountForSize(TileUtils.TILE_SIZE, i);
- r = new TileTree(TileUtils.TILE_SIZE, o),
- n[t] = r;
- }
- }
- getTileTree(e, t) {
- var i = this.tileTrees[e];
- if (!i)
- console.error("PanoRenderer.getTileTree() -> Tree array not yet initialized!");
- var n = i[t];
- if (!n)
- console.error("PanoRenderer.getTileTree() -> Tree not yet initialized!");
- return n
- }
-
- /*
- * 创建tile的renderTarget, 包括pano.tiledPanoRenderTarget和zoomRenderTarget
- * @param {number} size 当前的panoSize,每个面的分辨率
- */
-
- initTiledPano(size, t) {//创建 RenderTargetCube
- var renderer = this.viewer.renderer;
- var renderTarget, texture;
- renderTarget = new WebGLCubeRenderTarget(size,{ //THREE.WebGLRenderTargetCube(size, size, {
- stencilBuffer: !1
- });
- texture = new CubeTexture([]);
- texture.image = [null, null, null, null, null, null];
- texture.flipY = !0,
- texture.format = RGBAFormat;
-
- t ? (texture.generateMipmaps = !0,
- texture.magFilter = LinearFilter,
- texture.minFilter = LinearMipMapLinearFilter)
- : (texture.generateMipmaps = !1,
- texture.magFilter = LinearFilter,
- texture.minFilter = LinearFilter);
-
-
- renderer.setRenderTarget(renderTarget);
- renderer.setRenderTarget(null);
- var o = renderer.properties.get(texture);
- o.__image__webglTextureCube = o.__webglTexture;
- //window.tex[r.id] = {texture:r, index:this.viewer.index }
-
-
- return renderTarget
- }
- getUploadQueueForPano(e) {
- var t = this.uploadQueues[e];
- return t || (t = [],
- this.uploadQueues[e] = t),
- t
- }
- isTileUploaded(e) {
- return this.isPanoZoomed(e.panoId) ? e.zoomUploaded : e.uploaded
- }
- setUploaded(e, t) {
- this.isPanoZoomed(e.panoId) ? e.zoomUploaded = t : e.uploaded = t;
- }
- queueTileUpload(e, t, i) {
- var n = this.getActiveRenderTargetDescriptor(e.panoId);
-
- if (this.isRenderTargetDescriptorValid(n) && e.downloaded && !this.isTileUploaded(e) && (!e.uploadQueued || i)
- && (!(e.panoSize > this.qualityManager.getMaxNavPanoSize())|| this.zoomingActive)) {
-
- var r = this.getUploadQueueForPano(e.panoId);
-
- if(i){
- this.uploadTile(e, !1);//提交
- }else {
- if(this.shoulPushToFrontOfQueue(e)){//如果是512的优先
- this.forceQueue.push(e);
- }else {
- if(t && this.direction){
- TilePrioritizer.insertSortedPanoTile(r, e, n.pano, this.direction);
- }else r.push(e);
- }
- e.uploadQueued = !0;
- this.uploadInterval || this.uploadIntervalCancelled || this.refreshUploadInterval(0);
- }
-
- }
- }
- shoulPushToFrontOfQueue(e) {
- return 0 === TileTree.getLevelCountForSize(TileUtils.TILE_SIZE, e.panoSize)
- }
- getTopUploadQueue() {
- for (var e = null, t = null, i = S.Base; i <= S.Remaining; i++)
- for (var n = 0; n < this.activePanos.length; n++)
- if (e = this.activePanos[n],
- t = this.getUploadQueueForPano(e.id),
- t.length > 0)
- switch (i) {
- case S.Base:
- if (0 === t[0].level)
- return t;
- break;
- case S.Remaining:
- return t
- }
- return null
- }
- peekNextFromUploadQueue() {
- if (this.forceQueue.length > 0)
- return this.forceQueue[0];
- var e = this.getTopUploadQueue();
- return e && e.length > 0 ? e[0] : null
- }
- clearAllQueuedUploads() {
- this.clearAllUploadQueues(null, 0);
- }
- clearAllQueuedUploadsForPano(e) {
- this.clearAllUploadQueues(e, 0);
- }
- clearAllUploadQueues(e, t) {
- if (e)
- this.clearUploadQueue(this.getUploadQueueForPano(e), t),
- this.clearUploadQueue(this.forceQueue, t, e);
- else {
- for (var i = 0; i < this.activePanos.length; i++) {
- var n = this.activePanos[i];
- this.clearUploadQueue(this.getUploadQueueForPano(n.id), t);
- }
- this.clearUploadQueue(this.forceQueue, t);
- }
- }
- clearUploadQueue(e, t, i) {
- void 0 !== t && null !== t || (t = 0);
- for (var n = 0; n < e.length;) {
- var r = e[n];
- (!i || i && i === r.tile.panoId) && r.level >= t ? (r.uploadQueued = !1,
- e.splice(n, 1)) : n++;
- }
- //若报错, r.tile.panoId改为 r.panoId
- }
-
- updateUploadQueue(maxNPF,maxPF/* e, t */) {//参数是 maxNonBaseUploadsPerFrame and maxBaseUploadsPerFrame, 优先上传512
- maxNPF || (maxNPF = 1);
- for (var i = 0, n = 0;;) {
- let old = this.forceQueue.slice(0);
-
- if (n >= maxPF || i >= maxNPF)
- break;
- var r = this.getNextFromUploadQueue();
- if (!r)
- break;
- //r.panoSize <2048 && console.log('panoId', r.panoId, 'panoSize', r.panoSize , old)
- 0 !== r.level ? i++ : n++;
- if (!(r.panoSize > this.qualityManager.getMaxNavPanoSize()) || this.zoomingActive) {
- var o = this.getActiveRenderTargetDescriptor(r.panoId);
- this.isRenderTargetDescriptorValid(o) && this.uploadTile(r, r.forceUpload);
- }
- }
- }
- updateDirection(e) {
- if (e = e || this.direction) {
- this.direction = e;
- for (var t = 0; t < this.activePanos.length; t++) {
- var i = this.activePanos[t],
- n = this.getUploadQueueForPano(i.id);
- TilePrioritizer.sortPanoTiles(n, i, this.direction);
- }
- }
- }
-
- anyUploaded(e) {
- if (!e)
- return !1;
- if (e.tile && this.isTileUploaded(e.tile))
- return !0;
- if (e.children)
- for (var t = 0; t < e.children.length; t++) {
- var i = e.children[t];
- if (this.anyUploaded(i))
- return !0
- }
- return !1
- }
- setNodeCovered(e, t) {
- this.isPanoZoomed(e.tile.panoId) ? e.zoomCovered = t : e.covered = t;
- }
- isNodeCovered(e) {
- return !!e && (this.isPanoZoomed(e.tile.panoId) ? e.zoomCovered : e.covered)
- }
- addCoverageForNode(e) {
- if (this.setNodeCovered(e, !0),
- e.parent && e.covered) {
- var t = e.parent;
- this.nodeSubcovered(t) && this.addCoverageForNode(t, !0);
- }
- }
- calcFullCoverage(e) {
- var t = !1;
- if (e.children)
- for (var i = 0; i < e.children.length; i++) {
- var n = e.children[i];
- t = t || this.calcFullCoverage(n);
- }
- e.covered = e.tile.uploaded || t;
- }
- nodeSubcovered(e) {
- if (!e.children)
- return !1;
- for (var t = 0; t < e.children.length; t++)
- if (!e.children[t] || !this.isNodeCovered(e.children[t]))
- return !1;
- return !0
- }
- resetPanoDescriptor(e) {
- this.getPanoDescriptor(e);
- }
- getPanoDescriptor(e) {
- var t = this.panoDescriptors[e];
- return t || (t = {},
- this.panoDescriptors[e] = t),
- t
- }
- resetPanoLODDescriptors(e) {
- var t = this.getPanoLODDescriptors(e);
- for (var i in t)
- if (t.hasOwnProperty(i)) {
- var n = t[i];
- n.uploadCount = 0,
- n.uploadAttempts = 0;
- n.uploaded = [];
- }
- }
- getPanoLODDescriptor(e, t) {
- var i = this.getPanoLODDescriptors(e),
- n = i[t];
- return n || (n = {
- uploadCount: 0,
- uploadAttempts: 0,
- uploaded:[],//add
- },
- i[t] = n),
- n
- }
- getPanoLODDescriptors(e) {
- var t = this.panoLODDescriptors[e];
- return t || (t = {},
- this.panoLODDescriptors[e] = t),
- t
- }
- onTileDownloaded(o) {
- var e = o.desc;
- var t = TileTree.getLevelCountForSize(TileUtils.TILE_SIZE, e.panoSize),
- i = this.getTileDirectoryEntry(e.panoId, e.face, t, e.faceTileIndex);
- i.downloaded = !0;
- i.image = e.image;
- i.panoSize = e.panoSize;
- i.tileX = e.tileX;
- i.tileY = e.tileY;
- i.totalTiles = e.totalTiles;
- i.tileIndex = e.tileIndex;
- i.faceTileIndex = e.faceTileIndex;
- i.face = e.face;
- i.cubeFace = TileUtils.mapFaceToCubemapFace(e.face);
- i.panoId = e.panoId;
- i.tileSize = e.tileSize;
- i.direction = (new Vector3).copy(e.direction);
- i.node = null;
- i.level = TileTree.getLevelCountForSize(TileUtils.TILE_SIZE, i.panoSize);
- if (this.isPanoActive(i.panoId)) {
- var n = this.getTileTree(i.panoId, i.face);
- var r = n.getSubNode(i.panoSize, i.tileX, i.tileY);
- this.linkTileAndNode(i, r);
- this.queueTileUpload(i, !0);
- }
- }
- getTileDirectoryEntry(panoId, t, i, n) {
- var r = this.tileDirectory[panoId];
- r || (r = {}, this.tileDirectory[panoId] = r);
- var o = 16384 * t + 1024 * i + n, //t:4096级别
- a = r[o];
- return a || (a = {
- downloaded: !1,
- uploaded: !1,
- zoomUploaded: !1
- },
- r[o] = a),
- a._key = panoId + ":" + t + ":" + i + ":" + n,//panoId : face : level : faceTileIndex
- a._tileKey = o,
- a
- }
- setZoomingActive(active, pano, i) {//设置当前正在zoom的pano
- this.zoomPanoRenderingDisabled || active === this.zoomingActive && this.zoomPanoId === pano.id || (this.zoomingActive = active,
- this.zoomPanoId = pano.id,
- this.zoomingActive && (this.zoomPanoId !== pano.id || i) && this.updateZoomedPanoFromBase(pano));
- }
- updateZoomedPanoFromBase(pano) {//因更换pano所以将pano的rendertarget渲染到panoRenderer的zoomRenderTarget上
- if (!this.zoomPanoRenderingDisabled && this.zoomRenderTarget) {
- var t = this.getActiveRenderTargetDescriptor(pano.id);
- if (t && t.renderTarget ) {
- var i = Math.min(this.qualityManager.maxRenderTargetSize, this.qualityManager.getMaxZoomPanoSize()), //change
- n = t.renderTarget,
- r = t.size;
- this.copyCubeMap(n.texture, this.zoomRenderTarget, r, r, i, i),
- this.copyBaseRenderStatusToZoomed(pano.id);
- }
- }
- }
-
-
- add(e) {
- this.M.push(e);
- }
- initDescriptor(size) {
- var t = createDescriptor();
- t.inUse = !0;
- t.size = size;
- this.add(t);
- return t
- }
- activeDescripor(e) {
- for (var t = 0; t < this.M.length; t++) {
- var i = this.M[t];
- if (!i.inUse && i.size === e){
- i.inUse = !0;
- return i
- }
- }
- return null
- }
- deactiveDescripor(e) {
- for (var t = 0; t < this.M.length; t++) {
- var i = this.M[t];
- if (i.renderTarget === e){
- i.inUse = !1;
- return !0
- }
- }
- return !1
- }
-
- enableHighQuality(e){//xzw add 如果最多只要2k图的话enableUltraHighQualityMode替换成这个
- if (!this.qualityManager.highQualityModeStarted) {
- this.setupZoomRenderTarget();
- e();
- //this.qualityManager.updateHighResolutionSettings(e)////////?
- this.qualityManager.highQualityModeStarted = true;
- }
- }
-
-
- linkTileAndNode(e, t) {
- t.tile = e,
- e.node = t;
- }
- linkAllTilesAndNodes(e) {
- var t = function (t, i, n, r, o) {
- var a = this.getTileDirectoryEntry(e.id, i, r, o);
- this.linkTileAndNode(a, n);
- };
- for (var i = 0; i < TileUtils.FACES_PER_PANO; i++) {
- var n = this.getTileTree(e.id, i);
- n.breadthFirst({
- callback: t.bind(this, n, i)
- });
- }
- }
-
- //--------------sceneRenderer
-
- initSizedTexture2D(e, t, i) {
- var renderer = this.viewer.renderer,
- o = renderer.getContext(),
- a = renderer.state,
- s = new Texture(null);
- s.flipY = !1,
- i !== !0 && (i = !1),
- s.generateMipmaps = i;
- var l = renderer.paramThreeToGL(s.format),
- c = renderer.paramThreeToGL(s.type),
- h = renderer.properties.get(s),
- u = o.createTexture();
- a.bindTexture(o.TEXTURE_2D, u),
- o.pixelStorei(o.UNPACK_FLIP_Y_WEBGL, s.flipY),
- o.texImage2D(o.TEXTURE_2D, 0, l, e, e, 0, l, c, null),
- s.wrapS = t,
- s.wrapT = t;
- var d = renderer.paramThreeToGL(t);
- return o.texParameteri(o.TEXTURE_2D, o.TEXTURE_WRAP_S, d),
- o.texParameteri(o.TEXTURE_2D, o.TEXTURE_WRAP_T, d),
- i ? (s.magFilter = LinearFilter,
- s.minFilter = LinearMipMapLinearFilter,
- o.texParameteri(o.TEXTURE_2D, o.TEXTURE_MAG_FILTER, o.LINEAR),
- o.texParameteri(o.TEXTURE_2D, o.TEXTURE_MIN_FILTER, o.LINEAR_MIPMAP_NEAREST),
- o.generateMipmap(o.TEXTURE_2D)) : (s.magFilter = LinearFilter,
- s.minFilter = LinearFilter,
- o.texParameteri(o.TEXTURE_2D, o.TEXTURE_MAG_FILTER, o.LINEAR),
- o.texParameteri(o.TEXTURE_2D, o.TEXTURE_MIN_FILTER, o.LINEAR)),
- a.bindTexture(o.TEXTURE_2D, null),
- h.__webglTexture = u,
- s
- }
- deallocateCubeTexture(e) {
- var t = this.viewer.renderer,
- i = t.getContext(),
- renderer = t.properties.get(e);
- i.deleteTexture(renderer.__image__webglTextureCube);
- }
-
- uploadTexture2D(img, tex, startX, startY, width, height) {
- var renderer = this.viewer.renderer,
- gl = renderer.getContext(),
- webglState = renderer.state,
- c = renderer.properties.get(tex);
- webglState.bindTexture(gl.TEXTURE_2D, c.__webglTexture),
- gl.pixelStorei(gl.UNPACK_FLIP_Y_WEBGL, tex.flipY),
- gl.pixelStorei(gl.UNPACK_PREMULTIPLY_ALPHA_WEBGL, tex.premultiplyAlpha),
- gl.pixelStorei(gl.UNPACK_ALIGNMENT, tex.unpackAlignment),
- gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_S, renderer.paramThreeToGL(tex.wrapS)),
- gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_T, renderer.paramThreeToGL(tex.wrapT)),
- gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MAG_FILTER, renderer.paramThreeToGL(tex.magFilter)),
- gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MIN_FILTER, renderer.paramThreeToGL(tex.minFilter)),
- gl.texSubImage2D(gl.TEXTURE_2D, 0, startX, startY, gl.RGBA, gl.UNSIGNED_BYTE, img),
- tex.generateMipmaps && gl.generateMipmap(gl.TEXTURE_2D),
- webglState.bindTexture(gl.TEXTURE_2D, null);
- }
- getCubeOrientationForCubeFace(e, t) {
- switch (e) {
- case GLCubeFaces$1.GL_TEXTURE_CUBE_MAP_POSITIVE_X:
- t.set(0, -Math.PI / 2, 0);
- break;
- case GLCubeFaces$1.GL_TEXTURE_CUBE_MAP_NEGATIVE_X:
- t.set(0, Math.PI / 2, 0);
- break;
- case GLCubeFaces$1.GL_TEXTURE_CUBE_MAP_POSITIVE_Y:
- t.set(Math.PI / 2, Math.PI, 0);
- break;
- case GLCubeFaces$1.GL_TEXTURE_CUBE_MAP_NEGATIVE_Y:
- t.set(-Math.PI / 2, Math.PI, 0);
- break;
- case GLCubeFaces$1.GL_TEXTURE_CUBE_MAP_POSITIVE_Z:
- t.set(0, -Math.PI, 0);
- break;
- case GLCubeFaces$1.GL_TEXTURE_CUBE_MAP_NEGATIVE_Z:
- t.set(0, 0, 0);
- }
- }
-
-
- }
- PanoRenderer.prototype.setupZoomRenderTarget = function(){
- var targets = {};
- return function(){
- if(this.qualityManager.maxRenderTargetSize == '2k' && this.qualityManager.getMaxNavPanoSize()=='2k')return; //不使用zoomTarget 直接用pano的tiledPanoRenderTarget,防崩溃
-
-
-
- if (this.qualityManager.getMaxZoomPanoSize() >= this.qualityManager.getMaxNavPanoSize() /* && (config.tileClass != "2k" ||config.tileClass != "1k") */) {
- //部分手机2k时copyCubeMap会重载 , 所以如果没有超出当前分辨率,就不使用zoomRenderTarget。但在微信依旧会重载,只是优化了些,safari几乎不会。
- if (this.zoomRenderTarget && this.zoomRenderTarget.width === this.qualityManager.getMaxZoomPanoSize())
- return;
- var e = this.zoomRenderTarget;
-
-
- var size = this.qualityManager.getMaxZoomPanoSize();
- if(size > this.qualityManager.maxRenderTargetSize){
- return
- }
-
- if(targets[size]){
- this.zoomRenderTarget = targets[size];
- }else {
- this.zoomRenderTarget = this.initTiledPano(size, !1);
- targets[size] = this.zoomRenderTarget;
- }
-
- if (e) {//将旧的zoomRenderTarget渲染到新zoomRenderTarget上
- var t = e.width,
- i = this.zoomRenderTarget.width;
- this.copyCubeMap(e.texture, this.zoomRenderTarget, t, t, i, i),
- e.texture.dispose(),
- e.texture.loaded = !1,
- e.texture.version = 0,
- this.deallocateCubeTexture(e.texture),
- e.texture = null;
- }
- this.zoomPanoRenderingDisabled = !1;
- } else
- this.zoomPanoRenderingDisabled = !0;
- }
- }();
- PanoRenderer.prototype.updateActivePanos = function () {
- var e = [];
- return function (t, i) {
- e.length = 0;
- for (var n = 0; n < this.activePanos.length; n++) {
- t && e.length === i && e.push(t);
- var r = this.activePanos[n],
- o = this.getActiveRenderTargetDescriptor(r.id);
- t && r.id === t.id || !this.isRenderTargetDescriptorValid(o) || e.push(r);
- }
- t && i >= e.length && e.push(t),
- this.activePanos.length = 0,
- this.activePanos.push.apply(this.activePanos, e);
- }
- }();
- PanoRenderer.prototype.renderPanoTiles = function () {
- var e = [];
- return function (panoId, i, n, r) {
- this.zoomRenderTarget && this.zoomRenderTarget.width === this.qualityManager.getMaxZoomPanoSize() || this.zoomPanoRenderingDisabled || this.setupZoomRenderTarget(),//如果ZoomRenderTarget大小需要变动就重新创建
- i = i || this.direction || Vectors.FORWARD; // [{1:Vectors.FORWARD}]?
- var o = this.getActiveRenderTargetDescriptor(panoId);
- if (!this.isRenderTargetDescriptorValid(o))
- console.error("PanoRenderer.renderPanoTiles() -> Cannot render to a pano that is not activated.");
- for (var a = 0; a < TileUtils.FACES_PER_PANO; a++) {
- var s = this.getTileTree(panoId, a);
- e.length = 0;
- s.breadthFirst({//获取所有node? 85个
- saveVisited: e
- });
- for (var l = 0; l < e.length; l++) {
- var c = e[l];
- this.queueTileUpload(c.tile, !1, r || 0 === l && n);//为什么第0个会直接uploadTile??
- }
- }
- this.updateDirection(i);
- }
- }();
- PanoRenderer.prototype.getNextFromUploadQueue = function () {
- var e = function (e) {
- var t = e.shift();
- return t.uploadQueued = !1,
- t
- };
- return function () {
- if (this.forceQueue.length > 0)
- return e(this.forceQueue);
- var t = this.getTopUploadQueue();
- return t && t.length > 0 ? e(t) : null
- }
- }();
- PanoRenderer.prototype.refreshUploadInterval = function () {
- var e = null;
- return function (t) {
- this.uploadIntervalCancelled || (e || (e = c$1.bind(this)),
- null !== t && void 0 !== t || (t = w),
- b || (t = _),
- this.uploadInterval = window.setTimeout(e, t),
- this.uploadIntervalDelay = t);
- }
- }();
- PanoRenderer.prototype.update = function () {
- var e = performance.now(),
- t = 0;
- return function () {
- this.uploadIntervalCancelled = !0;
- window.clearTimeout(this.uploadInterval);
- this.uploadInterval = null;
- var i = performance.now() - e;
- //!(i > w || 0 === t) || !this.overlayTilesLoaded && this.usingTileOverlay || (this.updateUploadQueue(this.maxNonBaseUploadsPerFrame, this.maxBaseUploadsPerFrame),
- //e = performance.now()),
- if (!(i > w || 0 === t) || !this.overlayTilesLoaded && this.usingTileOverlay) {} else {
- this.updateUploadQueue(this.maxNonBaseUploadsPerFrame, this.maxBaseUploadsPerFrame);
- e = performance.now();
- }
- t++;
- }
- }();
- PanoRenderer.prototype.uploadTile = function () {//重写
- var collection = {},
- overlayStyle = config$1.tiling.overlayStyle;
-
- var failHistory = {};
-
- return function (info, n) {
-
- var id = info.panoId,
- img = info.image,
- tileSize = info.tileSize,
- panoSize = info.panoSize,
- tileIndex = info.tileIndex,
- totalTiles = info.totalTiles,
- tileX = info.tileX,
- tileY = info.tileY,
- p = !0,
- g = !1,
- ignore = false, //add
- LodDescripor = (this.getPanoDescriptor(id), this.getPanoLODDescriptor(id, panoSize)),
- activeDescripor = this.getActiveRenderTargetDescriptor(id),
- renderTarget = activeDescripor.renderTarget,
- size = activeDescripor.size;//当前要渲染的面的分辨率,也就是MaxNavPanoSize
-
-
- if (this.isPanoZoomed(id) && this.zoomRenderTarget) {
- renderTarget = this.zoomRenderTarget;
- size = this.zoomRenderTarget.width; //this.qualityManager.getMaxZoomPanoSize(); //放大后可能2048或4096
- }
-
- let done = ()=>{
- if(!LodDescripor.uploaded.includes(tileIndex)){//已经upload过(本来这时候直接返回,但发现缩放后这不会归零,导致清晰度不更新,所以还是redraw且emit吧)
- //console.log('try to reupload and return',tileIndex)
- LodDescripor.uploaded.push(tileIndex);
- LodDescripor.uploadCount++;
- }
- this.dispatchEvent({type:PanoRendererEvents.TileRenderSuccess, id, panoSize, tileIndex, totalTiles});
- LodDescripor.uploadCount === totalTiles && this.dispatchEvent({type:PanoRendererEvents.PanoRenderComplete, id, panoSize, totalTiles, updateFullComplete:true});
- this.setUploaded(info, !0);
- this.addCoverageForNode(info.node);
- };
-
-
-
- {//已经uploadTile过了不再uploadTile
- if(!this.isRenderTargetDescriptorValid(activeDescripor)){
- p = !1; g = !1;
- }
- if(!n){
- this.anyUploaded(info.node) && (p = !1, g = !0,ignore = true ); //包括子集也uploadTile了
- this.isTileUploaded(info) && (p = !1, g = !1,ignore = true ); //当前tile uploadTile了
- }
- }
-
-
- if (p) {
-
- /*if(failHistory[id+':'+ panoSize+ ':' +tileIndex]){
- console.log('uploadTile retry',id, panoSize, tileIndex)
- }
- console.log('uploadTile 成功', id, panoSize, tileIndex) */
- var C = tileX * tileSize,
- I = tileY * tileSize,
- E = tileSize / panoSize * size, // tile在renderTarget上渲染出的宽度
- b = C / panoSize * size, // tile在renderTarget上渲染的startX
- w = I / panoSize * size; // tile在renderTarget上渲染的startY
- collection[tileSize] || (collection[tileSize] = this.initSizedTexture2D(tileSize, ClampToEdgeWrapping));
-
-
- if(panoSize > this.qualityManager.maxRenderTargetSize ){ //4096 改
- var tex = this.initSizedTexture2D(tileSize, ClampToEdgeWrapping);
- var loaded = this.viewer.images360.isHighMapLoaded(info.cubeFace, tileX,tileY);
- }else {
- var tex = collection[tileSize];
- }
- this.uploadTexture2D(img, tex, 0, 0, tileSize, tileSize);//只替换tex对应的img,不新建
-
- if(panoSize > this.qualityManager.maxRenderTargetSize){
- loaded || this.viewer.images360.updateHighMap(tex, info.cubeFace, tileX,tileY);
-
- }else {
- if (1 === overlayStyle || 2 === overlayStyle) {
- var T = 1 === overlayStyle ? this.overlayTilesBasic : this.overlayTilesEnhanced;
- this.renderToCubeMap(tex, renderTarget, tileSize, tileSize, 0, 0, tileSize, tileSize, b, w, E, E, info.cubeFace);
- this.renderToCubeMap(T[panoSize], renderTarget, tileSize, tileSize, 0, 0, tileSize, tileSize, b, w, E, E, info.cubeFace, NormalBlending, !0, .5);
- } else {
- this.renderToCubeMap(tex, renderTarget, tileSize, tileSize, 0, 0, tileSize, tileSize, b, w, E, E, info.cubeFace);
- }
- }
- done();
-
- }else if(ignore){
- //console.log('finish because anyUploaded',id,panoSize,tileIndex)
- done(); //改: 如果因为这部分更高清的贴图已加载所以才不绘制的话,直接完成
-
- }else {
- //console.log('uploadTile 失败', id, panoSize, tileIndex)
- if(panoSize == 512){
- //console.log("!!!!!!!!!!!!!")
- }
-
- failHistory[id+':'+ panoSize+ ':' +tileIndex] = true;
- this.setUploaded(info, !1);
- }
- info.uploadAttempted || (LodDescripor.uploadAttempts++, this.dispatchEvent({type:PanoRendererEvents.TileUploadAttempted, id, panoSize, tileIndex, totalTiles})),
- info.uploadAttempted = !0;
- LodDescripor.uploadAttempts === totalTiles && this.dispatchEvent({type:PanoRendererEvents.UploadAttemptedForAllTiles, id, panoSize, totalTiles});
- return g;
- }
- }();
- /*
- 注:tileY的方向同UV,从下到上
- renderToCubeMap里的画布or镜头的xy范围是-0.5到0.5
- */
-
-
- PanoRenderer.prototype.renderToCubeMap = function() {
- var inited = !1,
- scene = null,
- camera = null,
- material = null,
- geo = null,
- plane = null,
- l = 1;
- return function(texture, renderTarget, tileWidth, tileHeight, startXinTile, startYinTile, widthinTile, heightinTile, startX, startY, width, height, cubeFace, E, b, w) {
-
-
- var renderer = this.viewer.renderer;
-
- inited || (camera = new OrthographicCamera(l / -2, l / 2, l / 2, l / -2, -200, 200),
- camera.position.z = 150,
- scene = new Scene,
- scene.add(camera),
- material = new ShaderMaterial({
- uniforms: {
- tDiffuse: {
- type: "scene",
- value: null
- },
- alpha: {
- type: "startYinTile",
- value: 1
- }
- },
- vertexShader: Shaders['basicTextured.vs'],
- fragmentShader: Shaders['basicTextured.fs'],
- depthWrite: !1,
- depthTest: !1,
- side: DoubleSide
- }),
- geo = new PlaneBufferGeometry(l, l),
- plane = new Mesh(geo, material),
- plane.position.z = 0,
- scene.add(plane),
- inited = !0);
- var uv = geo.getAttribute("uv");
- uv.setDynamic(!0), //setUsage
- uv.needsUpdate = !0;
-
- var uvArr = uv.array,
- S = startXinTile / tileWidth, //uv这几个值基本是固定的startXinTile:0,startYinTile:0,widthinTile:512,widthinTile:512,tileWidth:512,tileHeight:512 也就是说uv不会变、每张tile的有效范围是100%
- M = startYinTile / tileHeight,
- R = widthinTile / tileWidth,
- P = heightinTile / tileHeight;
- uvArr[0] = S,
- uvArr[1] = M + P,
- uvArr[2] = S + R,
- uvArr[3] = M + P,
- uvArr[4] = S,
- uvArr[5] = M,
- uvArr[6] = S + R,
- uvArr[7] = M;
-
-
- //修改posistion,使该plane只占据需要绘制的部分。类似拼图。
- //startX startY width height 都是在画布上的大小,比如画布大小为2048*2048,此tile为16分之一,tileX是1,tileY是1,则startX=2048/4,startY=2048/4
-
-
- var pos = geo.getAttribute("position");
- pos.setDynamic(!0),
- pos.needsUpdate = !0;
- var posArr = pos.array,
- D = startX / renderTarget.width - l / 2 // 起始x
- ,
- N = startY / renderTarget.height - l / 2 // 起始y
- ,
- B = width / renderTarget.width // 宽
- ,
- F = height / renderTarget.height; // 高
- posArr[0] = D,
- posArr[1] = N + F,
- posArr[3] = D + B,
- posArr[4] = N + F,
- posArr[6] = D,
- posArr[7] = N,
- posArr[9] = D + B,
- posArr[10] = N;
-
- renderer.properties.get(scene);
- material.uniforms.tDiffuse.value = texture;
- material.blending = E || NoBlending,
- material.transparent = !!b;
-
- void 0 !== w && null !== w || (w = 1),
- material.uniforms.alpha.value = w,
- material.needUpdate = !0;
- //renderTarget.activeCubeFace = cubeFace, //0-5 应该是指定渲染h中的面 失效
- /* renderer.setScissorTest(!0)
- //指定绘制区域,类似遮罩(相对于屏幕)
- renderer.setScissor(startX,startY,width,height) //加上这个会不会快一些,尤其是spherical
- //指定绘制视口位置和大小(相对于屏幕)
- */
- renderTarget.viewport.set(0, 0, renderTarget.width, renderTarget.height);
- var V = renderer.autoClear;
- var oldTarget = renderer.getRenderTarget();
- renderer.autoClear = !1;
-
-
-
-
-
- renderer.setRenderTarget(renderTarget, cubeFace);
- renderer.render(scene, camera/* , renderTarget, !1 */);
- renderer.setRenderTarget(oldTarget);
- renderer.autoClear = V;
- //renderer.setScissorTest(!1)
-
- /* this.renderer.render(scene, camera, this.planeTargets[cubeFace], !1),//针对有的场景app第一个点图加载不成功的问题
- console.log(`图index ${cubeFace} , ${startX}, ${startY}, ${width}, ${height}`)
- this.targetList[cubeFace] || (this.targetList[cubeFace] = [])
- this.targetList[cubeFace].push([startX,startY,width,height])*/
-
- }
- }();
- /*
- * 将texture渲染到zoomRenderTarget上(目的是复制贴图到zoomRenderTarget)
- */
- PanoRenderer.prototype.copyCubeMap = function() {//将texture渲染到zoomRenderTarget上
- var inited = !1,
- scene = null,
- camera = null,
- material = null,
- geo = null,
- mesh = null,
- testCube = null, //add
- c = new Euler;
-
- return function(texture, renderTarget, tWidth, tHeight, rWidth, rHeight, m, v, A) {
-
- if(rWidth > this.qualityManager.maxRenderTargetSize) return; //add
-
- if (!inited) {
- var w = 2;
- camera = new OrthographicCamera(w / -2, w / 2, w / 2, w / -2, 0, 200),
- camera.position.set(0, 0, 0),
- scene = new Scene,
- scene.add(camera);
- material = new ShaderMaterial({
- uniforms: {
- tDiffuse: {
- type: "t",
- value: null
- },
- alpha: {
- type: "f",
- value: 1
- }
- },
-
- vertexShader: Shaders['copyCubeMap.vs'],
- fragmentShader:Shaders['copyCubeMap.fs'],
- depthWrite: !1,
- depthTest: !1,
- side: DoubleSide
- });
- geo = new BoxGeometry(w, w, w),
- mesh = new Mesh(geo, material),
- scene.add(mesh);
- inited = !0;
-
-
- /* testCube = mesh.clone();
- viewer.scene.scene.add(testCube);
- viewer.setObjectLayers(testCube, 'sceneObjects')
- */
- }
- let autoClear = this.viewer.renderer.autoClear;
- let oldTarget = this.viewer.renderer.getRenderTarget();
- this.viewer.renderer.autoClear = false;
-
-
- material.uniforms.tDiffuse.value = texture;
- material.blending = m || NoBlending;
- material.transparent = !!v;
- void 0 !== A && null !== A || (A = 1);
- material.uniforms.alpha.value = A;
- material.needUpdate = !0;
-
-
-
- for (var C = 0; C < 6; C++){
- this.getCubeOrientationForCubeFace(C, c);
- mesh.rotation.copy(c);
- mesh.matrixWorldNeedsUpdate = !0;
- mesh.updateMatrixWorld();
- renderTarget.viewport.set(0, 0, rWidth, rHeight);
- this.viewer.renderer.setRenderTarget(renderTarget, C ); //renderTarget.activeCubeFace = C//失效
- this.viewer.renderer.render(scene, camera);
- }
- //console.warn('copyCubeMap' + rWidth)
- this.viewer.renderer.autoClear = autoClear;
- this.viewer.renderer.setRenderTarget(oldTarget);
- }
- }();
- class DepthImageSampler {
-
- constructor(){
- var canvas = document.createElement("canvas");
- this.canvas = canvas;
- this.context = canvas.getContext("2d");
-
-
- /* document.getElementsByTagName('body')[0].appendChild(canvas);
- canvas.style.position = 'fixed';
- canvas.style.width = '1024px';
- canvas.style.top = canvas.style.left = 0
- canvas.style['z-index'] = 100
- */
-
- }
-
-
- changeImg(img){
- if(this.img == img)return
- this.canvas.width = img.width;
- this.canvas.height = img.height;
- this.context.drawImage(img, 0, 0);
- this.img = img;
- }
-
-
- getDepth(UVx, UVy) {//根据图片像素获取深度值
- var x = Math.round(UVx * (this.canvas.width - 1))
- , y = Math.round(UVy * (this.canvas.height - 1));
- if (!(x < 0 || y < 0 || x >= this.width || y >= this.height)) {
- var r = this.context.getImageData(x, y, 1, 1).data;
- //console.log('color', r, x,y)
- return r[1] + r[0] / 256
- }
- }
-
-
- sample( intersect, currentPano, onlyPos ) {//通过和skybox的intersect得到真实的intersect的位置
- if(!intersect)return
- let location = new THREE.Vector3;
- let normal;
- currentPano = currentPano || viewer.images360.currentPano;
-
- if(currentPano != this.currentPano){
- if(!currentPano.depthTex/* || !currentPano.depthTex.image */) return //未加载
- this.changeImg(currentPano.depthTex.image);
- this.currentPano = currentPano;
- }
-
- let origin = currentPano.position;
- let dir = intersect.dir || new THREE.Vector3().subVectors(intersect.point, origin).normalize();
- //var uv = intersect.uv
- //let dirInPano = math.getNormalDir(dir, currentPano)//转化为考虑漫游点旋转的方向
-
- let dirInPano = dir.clone().applyMatrix4(currentPano.panoMatrix2Inverse).normalize(); //转化为考虑漫游点旋转的方向
- let uv = math.getUVfromDir(dirInPano);//转化为uv
-
- let distance = this.getDepth(uv.x, uv.y);
- //console.log('depth', depth, uv.y)
- if (!distance){
- if(uv.y > 0.75){//漫游点底部识别不到的区域,给一个地板高度
- //let height = origin.distanceTo(currentPano.floorPosition);
- const margin = 0.1;
- distance = (currentPano.floorPosition.z - origin.z - margin) / dir.z;
- location.copy(dir).multiplyScalar(distance).add(origin);
- let normal = new THREE.Vector3(0,0,1);
-
- return {location, normal, distance}
- }
- else return !1; //应该是天空或模型外 , 因为很少有漫游点的地方还拍不到地板
- }
-
- //this.mainDepth = depth
-
- location.copy(dir).multiplyScalar(distance).add(origin);
-
- if(!onlyPos){
-
- var pL = this.getNearbyPoint(origin, uv, -1, 0)
- , pR = this.getNearbyPoint(origin, uv, 1, 0)
- , pB = this.getNearbyPoint(origin, uv, 0, -1)
- , pT = this.getNearbyPoint(origin, uv, 0, 1);
-
- normal = this.planeFit(dir,location, pL,pR,pB,pT );
- }
-
-
- /* if(normal.x != normal.x ){
- console.log('NAN', normal)
- var pL = this.getNearbyPoint(origin, uv, -1, 0)
- , pR = this.getNearbyPoint(origin, uv, 1, 0)
- , pB = this.getNearbyPoint(origin, uv, 0, -1)
- , pT = this.getNearbyPoint(origin, uv, 0, 1);
-
- } */
-
- //console.log('normal',normal)
-
- return {location, normal, distance}
- }
-
-
- getNearbyPoint( origin, uv, x, y) { //获取附近的若干像素距离的点
- let uv2 = uv.clone();
- uv2.x += x/(this.canvas.width-1);
- uv2.x = this.clampUV(uv2.x);
-
- uv2.y += y/(this.canvas.height-1);
- uv2.y = this.clampUV(uv2.y);
-
- /* if(uv2.x < 0 || uv2.y < 0 || uv2.x > 1 || uv2.y > 1){
- console.log('will nan')
- } */
-
- let dir = math.getDirFromUV(uv2);//从uv获取到方向
- dir.applyMatrix4(viewer.images360.currentPano.panoMatrix2);
- let depth = this.getDepth(uv2.x, uv2.y);
- /* if(Math.abs(depth - this.mainDepth) > 0.3){
- console.log('Math.abs(depth - this.mainDepth) > 0.3')
- } */
-
- //let dir = new THREE.Vector3().subVectors(intersect.point, origin).normalize()
- let position = new THREE.Vector3().copy(dir).multiplyScalar(depth).add(origin);
-
- //console.log('getNearbyPoint', uv2, depth, dir, position )
-
- return position
- }
- clampUV(v){
- return (v + 1) % 1; // 使输出在 0-1
- }
-
- planeFit(dir, position, pL,pR,pB,pT ) {//求平均法线
- let normal = new THREE.Vector3;
-
-
- let plane = new THREE.Plane;
- function addNormal(p1, p2) {//根据临接的四个点,分别求法线,然后法线相加能得到平均法线
- if(!p1 || !p2)return
- plane.setFromCoplanarPoints(position, p1, p2);
-
- //console.log('normalSub', plane.normal)
-
- normal.addScaledVector(plane.normal, dir.dot(plane.normal) < 0 ? 1 : -1);//根据面的朝向判断加还是减
- }
- addNormal(pL, pB);
- addNormal(pL, pT);
- addNormal(pR, pB);
- addNormal(pR, pT);
-
- if(0 !== normal.x || 0 !== normal.y || 0 !== normal.z){
- normal.normalize();
- //console.log(normal)
- return normal
- }
-
-
- /* 四个面拼成一个菱形 */
-
- }
-
-
- /* makeUvToPosMap(intersect, matrix1, vec1, vec2) {
- var o = intersect.object.geometry
- , a = o.attributes.position.array
- , s = new THREE.Vector3(a[3 * intersect.face.a],a[3 * intersect.face.a + 1],a[3 * intersect.face.a + 2]).applyMatrix4(intersect.object.matrixWorld)
- , c = new THREE.Vector3(a[3 * intersect.face.b],a[3 * intersect.face.b + 1],a[3 * intersect.face.b + 2]).applyMatrix4(intersect.object.matrixWorld)
- , l = new THREE.Vector3(a[3 * intersect.face.c],a[3 * intersect.face.c + 1],a[3 * intersect.face.c + 2]).applyMatrix4(intersect.object.matrixWorld);
- vec1.subVectors(s, c),
- vec2.subVectors(l, c);
- var u = o.attributes.uv.array
- , d = new THREE.Vector2(u[2 * intersect.face.a],u[2 * intersect.face.a + 1])
- , p = new THREE.Vector2(u[2 * intersect.face.b],u[2 * intersect.face.b + 1])
- , h = new THREE.Vector2(u[2 * intersect.face.c],u[2 * intersect.face.c + 1])
- , f = d.sub(p)
- , g = h.sub(p);
- matrix1.set(f.x, g.x, 0, f.y, g.y, 0, 0, 0, 1),
- matrix1.getInverse(matrix1)
- } */
-
-
- /* getNearbyPoint( point, origin, uv, o, a, s, x, y) {
- var add = new THREE.Vector3(x, y , 0 )
- , depth = this.getDepth(uv.x + add.x, uv.y + add.y );
-
- if (void 0 !== depth) {
- var f = add.applyMatrix3(o);
- return (new THREE.Vector3).addScaledVector(a, f.x).addScaledVector(s, f.y).add(point).sub(origin).normalize().multiplyScalar(depth).add(origin)
- }
- } */
-
-
-
- }
- /* var i = n(4)
- , r = function() {
- function t(t) {
-
- }
- return t.prototype.getDepth = function(t, e) {
- var n = Math.round(t)
- , i = Math.round(e);
- if (!(n < 0 || i < 0 || n >= this.width || i >= this.height)) {
- var r = this.context.getImageData(n, i, 1, 1).data;
- return r[1] + r[0] / 256
- }
- }
- ,
- Object.defineProperty(t.prototype, "width", {
- get: function() {
- return this.context.canvas.width
- },
- enumerable: !0,
- configurable: !0
- }),
- Object.defineProperty(t.prototype, "height", {
- get: function() {
- return this.context.canvas.height
- },
- enumerable: !0,
- configurable: !0
- }),
- t
- }();
- e.CanvasDepthImage = r;
- var o = function() {
- function t() {}
- return t.sample = function(e, n, r, o, a) {
- var s = n.uv
- , c = s.x * (e.width - 1)
- , l = (1 - s.y) * (e.height - 1)
- , u = e.getDepth(c, l);
- if (!u)
- return !1;
- o.copy(n.point).sub(r).normalize().multiplyScalar(u).add(r);
- var d = new i.Matrix3
- , p = new i.Vector3
- , h = new i.Vector3;
- t.makeUvToPosMap(n, d, p, h);
- var f = this.getNearbyPoint(e, n.point, r, s, d, p, h, -1, 0)
- , g = this.getNearbyPoint(e, n.point, r, s, d, p, h, 1, 0)
- , m = this.getNearbyPoint(e, n.point, r, s, d, p, h, 0, -1)
- , v = this.getNearbyPoint(e, n.point, r, s, d, p, h, 0, 1);
- return this.planeFit(o, r, f, g, m, v, a)
- }
- ,
- t.makeUvToPosMap = function(t, e, n, r) {
- var o = t.object.geometry
- , a = o.attributes.position.array
- , s = new i.Vector3(a[3 * t.face.a],a[3 * t.face.a + 1],a[3 * t.face.a + 2]).applyMatrix4(t.object.matrixWorld)
- , c = new i.Vector3(a[3 * t.face.b],a[3 * t.face.b + 1],a[3 * t.face.b + 2]).applyMatrix4(t.object.matrixWorld)
- , l = new i.Vector3(a[3 * t.face.c],a[3 * t.face.c + 1],a[3 * t.face.c + 2]).applyMatrix4(t.object.matrixWorld);
- n.subVectors(s, c),
- r.subVectors(l, c);
- var u = o.attributes.uv.array
- , d = new i.Vector2(u[2 * t.face.a],u[2 * t.face.a + 1])
- , p = new i.Vector2(u[2 * t.face.b],u[2 * t.face.b + 1])
- , h = new i.Vector2(u[2 * t.face.c],u[2 * t.face.c + 1])
- , f = d.sub(p)
- , g = h.sub(p);
- e.set(f.x, g.x, 0, f.y, g.y, 0, 0, 0, 1),
- e.getInverse(e)
- }
- ,
- t.getNearbyPoint = function(t, e, n, r, o, a, s, c, l) {
- var u = new i.Vector3(c / (t.width - 1),l / (t.height - 1))
- , d = (r.x + u.x) * (t.width - 1)
- , p = (1 - (r.y + u.y)) * (t.height - 1)
- , h = t.getDepth(d, p);
- if (void 0 !== h) {
- var f = u.applyMatrix3(o);
- return (new i.Vector3).addScaledVector(a, f.x).addScaledVector(s, f.y).add(e).sub(n).normalize().multiplyScalar(h).add(n)
- }
- }
- ,
- t.planeFit = function(t, e, n, r, o, a, s) {
- s.set(0, 0, 0);
- var c = t.clone().sub(e)
- , l = new i.Plane;
- function u(e, n) {
- e && n && (l.setFromCoplanarPoints(t, e, n),
- s.addScaledVector(l.normal, c.dot(l.normal) < 0 ? 1 : -1))
- }
- return u(n, o),
- u(n, a),
- u(r, o),
- u(r, a),
- (0 !== s.x || 0 !== s.y || 0 !== s.z) && (s.normalize(),
- !0)
- }
- ,
- t
- }();
- */
- var rot90$1 = new Quaternion().setFromAxisAngle(new Vector3(0,0,1), Math.PI/2 ); //使用的是刚好适合全景图的,给cube贴图需要转90°
-
- let raycaster = new Raycaster();
- //let currentlyHovered = null;
- let texLoader$3 = new TextureLoader();
- let sm$1 = new MeshBasicMaterial({side: BackSide});
- let tileArr = [];
- let previousView = {
- controls: null,
- position: null,
- target: null,
- };
- const HighMapCubeWidth = 1;
-
-
- const directionFactor = 200; //原先10,几乎只往距离近的走了;设置太大楼梯上不去
-
-
- class Images360 extends EventDispatcher{
- constructor(viewer ){
- super();
- this.viewer = viewer;
- this.selectingEnabled = true;
- this.panos = [];
- this.neighbourMap = {};
-
-
- this.node = new Object3D();
- this.node.name = 'ImagesNode';
- //this.node2 = new THREE.Object3D();
-
- this.cubePanos = [];
-
-
-
- this.cube = new Mesh(new BoxBufferGeometry(1,1,1,1),new ModelTextureMaterial());
- viewer.updateVisible(this.cube,'showSkybox', false );
-
-
- this.cube.layers.set(Potree.config.renderLayers.skybox);
- this.cube.name = 'skyboxCube';
- viewer.scene.scene.add(this.cube);
-
-
- this._visible = true;
-
- this.currentPano = null;
- this.mouseLastMoveTime = Date.now();
- this.scrollZoomSpeed = 0.06;
- this.zoomLevel = 1;
-
- this.tileDownloader = new TileDownloader;
- this.qualityManager = new QualityManager;
- this.panoRenderer = new PanoRenderer(viewer, this.tileDownloader, this.qualityManager);
-
- this.basePanoSize = this.qualityManager.getPanoSize(PanoSizeClass.BASE);
- this.standardPanoSize = this.qualityManager.getPanoSize(PanoSizeClass.STANDARD);
- this.highPanoSize = this.qualityManager.getPanoSize(PanoSizeClass.HIGH);
- this.ultraHighPanoSize = this.qualityManager.getPanoSize(PanoSizeClass.ULTRAHIGH);
- this.tileDownloader.processPriorityQueue = !1;
- this.tileDownloader.tilePrioritizer = new TilePrioritizer(this.qualityManager, this.basePanoSize, this.standardPanoSize, this.highPanoSize, this.ultraHighPanoSize);
- this.flying_ = false;
-
- {//高分辨率cube 放大
- this.addHighMapCube();
- viewer.addEventListener(PanoramaEvents.Enter,(e)=>{
- this.setHighMap(e.newPano);
- });
- viewer.addEventListener('panoSetZoom',(e)=>{
- if(Potree.settings.displayMode == 'showPanos'){
- e.zoomed ? this.showHighMap() : this.hideHighMap(); //add
- }
- });
-
- }
-
-
-
- this.depthSampler = new DepthImageSampler();
- this.addEventListener('loadedDepthImg',(e)=>{
- this.updateDepthTex(e.pano);
- });
-
-
-
- let scroll = (e)=>{
- if(e.hoverViewport != viewer.mainViewport)return
-
- /* if(Potree.settings.displayMode == 'showPanos' && Potree.settings.zoom.enabled){
-
- } */
- };
-
- viewer.fpControls.addEventListener('dollyStopCauseUnable',(e)=>{
- if(/* e.hoverViewport != viewer.mainViewport || */!Potree.settings.zoom.enabled)return
-
- if(e.scale != void 0){//触屏
- this.zoomBy(e.scale, e.pointer);
- }else {//滚轮
- let zoom;
- if(e.delta > 0){
- zoom = 1 + this.scrollZoomSpeed;
- }else {
- zoom = 1 - this.scrollZoomSpeed;
- }
- e.delta != 0 && this.zoomBy(zoom);
- }
- });
-
-
- let click = (e) => {//不用"mouseup" 是因为 mouseup有drag object时也会触发
- if(Potree.settings.unableNavigate || this.flying || !e.isTouch && e.button != MOUSE.LEFT || e.drag && e.drag.object //拖拽结束时不算
- || Potree.settings.editType == 'pano' && viewer.modules.PanoEditor.activeViewName != 'mainView'
- || Potree.settings.editType == 'merge' && !e.intersectPoint || viewer.inputHandler.hoveredElements[0] && viewer.inputHandler.hoveredElements[0].isModel && e.intersectPoint.distance > viewer.inputHandler.hoveredElements[0].distance
- ) return
-
-
- if(Potree.settings.editType != 'pano' && Potree.settings.editType != 'merge'){
- if( e.hoverViewport == viewer.mapViewer.viewports[0]){
- return viewer.mapViewer.dispatchEvent(e/* {type:'global_click',e } */)
- }else if(e.hoverViewport != viewer.mainViewport){ //如数据集校准其他viewport
- return
- }
- }
-
-
- /* if(currentlyHovered && currentlyHovered.pano){
- this.focusPano(currentlyHovered.pano);
- }else{//add */
- if(!Potree.settings.dblToFocusPoint/* && this.currentPano */){//双击不会focus点云 或者 已经focusPano了
- this.flyToPanoClosestToMouse();
- }
- //}
- };
- viewer.addEventListener('global_click' , click);
-
- viewer.addEventListener("global_mousemove", (e) => {
- if(!Potree.settings.unableNavigate && Potree.settings.ifShowMarker && e.hoverViewport == viewer.mainViewport){//如果不显示marker,就在点击时再更新
- this.updateClosestPano(e.intersectPoint);
- }
- });
-
-
- this.addEventListener('markerHover',(e)=>{
- this.updateClosestPano(e.pano, e.hovered);
- });
-
- if(!Potree.settings.isOfficial){
- this.domRoot = viewer.renderer.domElement.parentElement;
- let elUnfocus = $("<input type='button' value='unfocus'></input>");
- elUnfocus.css({
- position : "absolute",
- right : '25%',
- bottom: '20px',
- zIndex: "10000",
- fontSize:'1em', color:"black",
- display:'none',
- background:'rgba(255,255,255,0.8)',
- });
- elUnfocus.on("click", () => this.unfocus());
- this.elUnfocus = elUnfocus;
- this.domRoot.appendChild(elUnfocus[0]);
-
- if(Potree.settings.editType != 'merge'){
-
- let elHide = $("<input type='button' value='隐藏点云'></input>");
- elHide.css({
- position : "absolute",
- right : '40%',
- bottom: '20px',
- zIndex: "10000",
- fontSize:'1em' ,color:"black",
- width : '100px',
- background:'rgba(255,255,255,0.8)',
- });
- this.domRoot.appendChild(elHide[0]);
- elHide.on("click", (e) => {
- let visi = viewer.getObjVisiByReason(viewer.scene.pointclouds[0], 'force');
- viewer.scene.pointclouds.forEach(e=>{
- viewer.updateVisible(e, 'force', !visi);
- });
- elHide.val(!visi ? "隐藏点云" : "显示点云");
- });
-
- }
-
- let elDisplayModel = $("<input type='button' value='>>全景'></input>");
- elDisplayModel.css({
- position : "absolute",
- right : '65%',
- bottom: '20px',
- zIndex: "10000",
- fontSize:'1em',color:"black",
- width : '100px',
- background:'rgba(255,255,255,0.8)',
- });
-
- this.domRoot.appendChild(elDisplayModel[0]);
- elDisplayModel.on("click", (e) => {
- if(Potree.settings.displayMode == 'showPointCloud' && this.panos.length == 0)return
- Potree.settings.displayMode = Potree.settings.displayMode == 'showPointCloud' ? 'showPanos' : 'showPointCloud';
- });
-
- this.elDisplayModel = elDisplayModel;
- }
-
-
-
- {//切换模式
- let displayMode = '';
- let latestRequestMode = '';//因为可能延迟,所以记录下每次的请求模式,延迟后判断这个
- Object.defineProperty(Potree.settings , "displayMode",{
- get: function() {
- return displayMode
- },
- set: (mode)=> {
- latestRequestMode = mode;
- console.warn('Request setMode: ' + mode);
-
- if(mode != displayMode){
- let config = Potree.config.displayMode[mode];
- let config2;
- let camera = viewer.scene.getActiveCamera();
- if(mode == 'showPanos' && viewer.mainViewport.view.isFlying()){//飞完才能切换全景
- let f = ()=>{
- if(latestRequestMode == mode){//如果ui还是停在这个模式的话
- Potree.settings.displayMode = mode;
- }
- viewer.mainViewport.view.removeEventListener('flyingDone', f);
- };
- viewer.mainViewport.view.addEventListener('flyingDone', f); //once
- return
- }
- if(this.isAtPano() ){//this.currentPano
- if(this.flying)config2 = config.transition;
- else config2 = config.atPano;
- }else {
- config2 = config.atPano;
- if(mode == 'showPanos'){//自动飞入一个pano
- //要改成飞进最近的。。。
- if(this.panos.length == 0)return
- //this.modeChanging = true //主要是因为到全景图不会立刻成功
- let wait = ()=>{
- this.removeEventListener('flyToPanoDone',wait);
- if(latestRequestMode == mode ){
- Potree.settings.displayMode = mode;
- }
- };
- this.flyToPano({
- pano: this.findNearestPano(),
- //dealDoneWhenCancel:true,
- /* callback: ()=>{
- setTimeout(()=>{ //防止循环,所以延迟
- if(latestRequestMode == mode ){
- Potree.settings.displayMode = mode
- }
- },1)
- } */
- });
- this.addEventListener('flyToPanoDone',wait); //等待飞行完毕。flyToPano的callback可能不执行所以换这个。但也可能被cancel
-
- return;
- }else {
-
- }
- }
-
-
- if(config2.showSkybox || config2.showPoint && config2.pointUsePanoTex){
- this.tileDownloader.start();
- }else {
- this.tileDownloader.stop();
- }
-
-
-
- if(config2.showSkybox || config2.pointUsePanoTex){
- let wait = ()=> {
- setTimeout( ()=>{
- if(latestRequestMode == mode ){
- Potree.settings.displayMode = mode;
- }
- },1);
- this.removeEventListener('loadedDepthImg', wait);
- };
- if(!this.currentPano.depthTex && this.currentPano.pointcloud.hasDepthTex){
- this.addEventListener('loadedDepthImg', wait);
- return this.currentPano.loadDepthImg()
- }
- //this.updateDepthTex()
- if(this.checkAndWaitForPanoLoad(this.currentPano, this.basePanoSize, wait)){
- return
- }
- }
-
-
-
- viewer.scene.pointclouds.forEach(e=>{
- viewer.updateVisible(e, 'displayMode', config2.showPoint, 2 );
- });
-
- if(config2.pointUsePanoTex){
- viewer.scene.pointclouds.forEach(e=>{
- e.material.setProjectedPanos(this.currentPano,this.currentPano, 1);
- });
- }else {
- viewer.scene.pointclouds.forEach(e=>{
- e.material.stopProjectedPanos();
- });
- }
-
- viewer.updateVisible(this.cube,'showSkybox',config2.showSkybox );// this.cube.visible = config2.showSkybox
-
- //this.cube.visible = config.atPano.showSkybox
- if(this.cube.visible){
- //this.cube.material.setProjectedPanos(this.currentPano, this.currentPano, 1)
-
- this.setProjectedPanos({
- progress:1,
- ifSkybox: true,
- ifPointcloud : false,
- easeInOutRatio : 0,
- pano0:this.currentPano,
- pano1:this.currentPano
- });
-
- }else {
- this.smoothZoomTo(1);
- this.resetHighMap();
- this.hideHighMap();
- }
-
-
-
- /* viewer.dispatchEvent({
- type: "enableChangePos",
- canLeavePano : config.canLeavePano ,
- viewport:
- }) */
-
- //viewer.mainViewport.unableChangePos = !config.canLeavePano
-
-
- displayMode = mode;
-
-
-
-
- if(mode == 'showPanos'){
- camera.far = viewer.farWhenShowPano; //修改far
- Potree.settings.pointDensity = 'panorama';
- if(Potree.config.displayMode.showPanos.transition.pointUsePanoTex){
- viewer.scene.pointclouds.forEach(e=>{
- e.material.pointSizeType = 'FIXED';
- });
- }
- this.updateCube(this.currentPano);
-
- }else {
- if(camera.limitFar) camera.far = Potree.settings.cameraFar;//修改far
- Potree.settings.pointDensity = Potree.settings.UserPointDensity;
- //Potree.sdk && Potree.sdk.scene.changePointOpacity()
- if(Potree.config.displayMode.showPanos.transition.pointUsePanoTex){
- viewer.scene.pointclouds.forEach(e=>{
- e.material.pointSizeType = Potree.config.material.pointSizeType;
- });
- }
- }
- camera.updateProjectionMatrix();
-
-
-
-
- if(this.elDisplayModel){
- this.elDisplayModel.val( mode == 'showPointCloud' ? ">>全景" : '>>点云');
- }
-
- /* this.panos.forEach(e=>{
- viewer.updateVisible(e, 'modeIsShowPanos', mode == 'showPanos', 1, mode == 'showPanos' ? 'add':'cancel') //
- }) */
-
-
- this.dispatchEvent({type:'endChangeMode',mode});
- console.log('setModeSuccess: ' + mode);
- }else {
-
- //this.dispatchEvent({type:'endChangeMode',mode})
- }
- }
- });
- Potree.settings.displayMode = 'showPointCloud';
- }// 切换模式 end
-
- {//
- let currentPano = null;
- Object.defineProperty(this , "currentPano",{
- get: function() {
- return currentPano
- },
- set: function(e) {
- if(e != currentPano){
-
- currentPano && currentPano.exit();
- e && e.enter();
- currentPano = e;
- }
-
- }
- });
- }
-
- {//是否显示marker
- let ifShowMarker = true;
- Object.defineProperty(Potree.settings, "ifShowMarker",{
- get: function() {
- return ifShowMarker
- },
- set: (show)=>{
- show = !!show;
- if(show != ifShowMarker){
- this.panos.forEach(pano=>{
- viewer.updateVisible(pano, 'ifShowMarker', show, 1 );
- });
- //this.emit('markersDisplayChange', show)
- ifShowMarker = show;
- }
-
- }
- });
- }
-
-
-
-
- viewer.addEventListener("update", () => {
- this.update(viewer);
- });
- //viewer.inputHandler.addInputListener(this);
-
-
-
- var keys = {
- FORWARD: ['W'.charCodeAt(0), 38],
- BACKWARD: ['S'.charCodeAt(0), 40],
- LEFT: ['A'.charCodeAt(0), 37],
- RIGHT: ['D'.charCodeAt(0), 39],
- };
- viewer.inputHandler.addEventListener('keydown',(e)=>{
- if(Potree.settings.displayMode == 'showPanos'){
- for(let i in keys){
- if(keys[i].some(a => a == e.keyCode)){
- switch(i){
- case 'FORWARD':
- this.flyLocalDirection(Vectors.FORWARD.clone());
- break;
- case 'BACKWARD':
- this.flyLocalDirection(Vectors.BACK.clone());
- break;
- case 'LEFT':
- this.flyLocalDirection(Vectors.LEFT.clone());
- break;
- case 'RIGHT':
- this.flyLocalDirection(Vectors.RIGHT.clone());
- break;
-
- }
-
- break;
- }
- }
- }
- });
-
- };
-
-
- updateDepthTex(pano){
- if(this.currentPano != pano || !pano.depthTex)return
- //this.depthSampler.changeImg(pano.depthTex.image); //pick sampler要飞到了才能切换图,而skybox贴图是随着全景图切换而切换的
- this.cube.material.updateDepthTex(pano); //确保一下
-
-
- }
-
- findNearestPano(pos){
- pos = pos ? new Vector3().copy(pos) : this.position;
- let result = Common.sortByScore(this.panos,[Images360.filters.isEnabled()],[e=>-e.position.distanceTo(pos)]);
- let pano = result && result[0] && result[0].item;
- return pano
-
- }
-
- /* set flying(v){//正在飞向pano
- this.flying_ = !!v
- //console.log('this.flying_ ', !!v )
- //this.emit('flying', this.flying_)
- let config = Potree.config.displayMode[Potree.settings.displayMode]
- viewer.mainViewport.unableChangePos = !config.canLeavePano || !!v
-
- }
- get flying(){
- return this.flying_
- } */
-
-
-
-
-
-
- flyLocalDirection(dir) {
- var direction = this.getDirection(dir),
- option1 = 1 === dir.y ? .4 : .75,
- option2 = 1 === Math.abs(dir.x);
- return this.flyDirection(direction, option1, option2)
- }
- getDirection(e) {
- if(!e){
- return viewer.scene.view.direction
- }else {
- return e = e ? e : (new Vector3).copy(Vectors.FORWARD),
- e.applyQuaternion(viewer.scene.getActiveCamera().quaternion)
- }
-
- }
- get position(){
- return this.viewer.scene.view.position.clone()
- }
-
-
- get visible(){
- return this._visible;
- }
- set visible(visible){
- if(this._visible === visible){
- return;
- }
- /* for(const image of this.panos){
- image.mesh.visible = visible && (this.currentPano == null);
- } */
- //this.sphere.visible = visible && (this.currentPano != null);
- this._visible = visible;
- this.dispatchEvent({
- type: "visibility_changed",
- panos: this,
- });
- }
-
-
-
- isAtPano(){//是否在某个漫游点上
- return this.currentPano && viewer.scene.view.position.equals(this.currentPano.position)
- }
-
-
-
-
- updateProjectedPanos(){//更新材质贴图
- //console.warn('updateProjectedPanos')
- this.projectedPano0 && this.projectedPano1 && this.setProjectedPanos({pano0:this.projectedPano0, pano1:this.projectedPano1});
- }
-
- setProjectedPanos(o={}){//设置cube和点云的材质贴图
- this.cube.material.setProjectedPanos(o.pano0, o.pano1, o.progress);
- if(o.ifPointcloud){
- viewer.scene.pointclouds.forEach(e=>{
- e.material.setProjectedPanos(o.pano0, o.pano1, o.progress, o.easeInOutRatio);
- });
- }
-
- //console.warn('setProjectedPanos ', o.pano0.id , o.pano1.id)
- this.projectedPano0 = o.pano0;
- this.projectedPano1 = o.pano1;
-
- }
- cancelFlyToPano(){//取消当前已有的飞行准备,前提是相机还未移动
- if(viewer.mainViewport.view.isFlying())return
- this.nextPano = null;
- this.latestToPano = null;
- //this.flying = false
- }
- flyToPano(toPano) { //飞向漫游点
- if(!toPano)return
- if(toPano instanceof Panorama){
- toPano = {pano: toPano};
- }
-
- if(!toPano.pano.enabled)return
-
- /* if(!this.currentPano){
- return this.focusPano(toPano)
- } */
-
- let done = (makeIt, disturb)=>{
- //console.log('flyToPano done ', toPano.pano.id, makeIt )
- if(makeIt || disturb) { // disturb已经开始飞行但中途取消
- toPano.callback && toPano.callback(makeIt);
- //this.flying = false
- this.cancelFlyToPano();
- this.updateClosestPano(this.closestPano,false); //飞行结束后取消点击漫游点时得到的closestPano
- }else {
- console.log('makeit fail');
- }
-
- this.dispatchEvent({type:'flyToPanoDone', makeIt});
- //this.dispatchEvent('cameraMoveDone')
- toPano.deferred && toPano.deferred.resolve(makeIt); //测量线截图时发现,resolve需要写在flying=false 后才行。
- };
- if(this.currentPano == toPano.pano && this.isAtPano() && !toPano.target ){
- this.dispatchEvent({type:'flyToPano', toPano});
- return done(true);
- }
- if(this.latestToPano && this.latestToPano != toPano){
- return done(false)
- }
-
- let target = toPano.target;
- let easeName = toPano.easeName || 'easeInOutQuad'; // 'easeInOutSine'//'easeOutSine'
-
- let config = Potree.config.displayMode[Potree.settings.displayMode];
- var pointcloudVisi = config.atPano.showPoint; //viewer.scene.pointclouds[0].visible
-
-
- var pano = toPano.pano;
- let T = Potree.config.transitionsTime;
- let maxTime = this.isAtPano() ? T.panoToPanoMax : T.flyIn;
- var duration = toPano.duration == void 0 ? (T.flyMinTime+Math.min(T.flytimeDistanceMultiplier * this.position.distanceTo(pano.position), maxTime)) : toPano.duration;
- toPano.duration = duration;
- //console.warn("flyto "+pano.id + ' duration: ' + duration )
-
- this.nextPano = pano;
- this.latestToPano = toPano;
- //this.flying = true //防止新的请求
-
-
-
- {//不飞的话是否不要执行这段?
-
- let wait = ()=> {
- if(this.latestToPano != toPano)return //如果取消了
- setTimeout(()=>{
- if(this.latestToPano != toPano)return
- this.flyToPano(toPano);
- },1);
- this.removeEventListener('loadedDepthImg', wait);
- };
- if(!pano.depthTex && pano.pointcloud.hasDepthTex){ //需要用到depthTex计算neighbour
- this.addEventListener('loadedDepthImg', wait);
- return pano.loadDepthImg()
- }
-
- if(config.atPano.showSkybox || config.atPano.pointUsePanoTex){
-
- this.updateCube(this.currentPano, toPano.pano);
-
- if(this.checkAndWaitForPanoLoad(pano, toPano.basePanoSize || this.basePanoSize, wait)){
- return
- }
- }
- }
- viewer.updateVisible(this.cube,'showSkybox', config.atPano.showSkybox ); // this.cube.visible = config.atPano.showSkybox
- /* this.cube.visible && this.cube.material.setProjectedPanos(this.currentPano, pano, 0)
-
- if(config.transition.showPoint){
- viewer.scene.pointclouds.forEach(e=>{
- viewer.updateVisible(e, 'displayMode', true)
- })
-
- if(config.transition.pointUsePanoTex){
- let easeInOutRatio = pointcloudVisi ? 0.3 : 0
- viewer.scene.pointclouds.forEach(e=>{
- e.material.setProjectedPanos(this.currentPano, pano, 0, easeInOutRatio)
- })
- }
-
- } */
- if(config.transition.showPoint){
- viewer.scene.pointclouds.forEach(e=>{
- viewer.updateVisible(e, 'displayMode', true);
- });
- }
-
- if(config.transition.showSkybox || config.transition.pointUsePanoTex){
- this.setProjectedPanos({
- progress:0,
- ifSkybox: this.cube.visible,
- ifPointcloud : config.transition.pointUsePanoTex,
- easeInOutRatio : pointcloudVisi ? 0.3 : 0,
- pano0:this.currentPano,
- pano1:pano
- });
- }
-
-
-
-
- const endPosition = pano.position.clone();
- {
- toPano.duration = duration;
- toPano.easeName = easeName;
- this.beforeFlyToPano(toPano);
- }
-
-
-
-
- let fly = ()=>{
-
- this.dispatchEvent({type:'flyToPano', toPano});
- viewer.scene.view.setView({position:endPosition, target, quaternion:toPano.quaternion , duration,
- callback:()=>{
-
- if(!config.atPano.pointUsePanoTex){
- viewer.scene.pointclouds.forEach(e=>{
- e.material.stopProjectedPanos();
- });
- }
- //this.currentPano.exit()
- //pano.enter()
- this.currentPano = pano;
-
- this.nextPano = null;
- if(Potree.settings.displayMode == 'showPanos'){
- viewer.scene.pointclouds.forEach(e=>{
- viewer.updateVisible(e, 'displayMode',pointcloudVisi);
- });
- }
- done(true);
- //this.updateCube(this.currentPano)
- this.updateDepthTex(this.currentPano);
-
-
-
- }, onUpdate:(progress)=>{
- this.cube.material.uniforms.progress.value = progress;
-
- viewer.scene.pointclouds.forEach(e=>{
- e.material.uniforms.progress.value = progress;
- });
- },
- cancelFun:()=>{ done(false, true); },
- Easing:easeName
- });
-
- //duration > 0 && (this.flying = true) //再写一遍 防止cancel其他项目导致flying为false
-
-
- };
-
- if(Potree.settings.displayMode == 'showPanos'){
- setTimeout(fly, 40); //更新geo后缓冲
- }else {
- fly();
- }
-
- //console.log('flyToPano:', toPano.pano.id)
- }
- beforeFlyToPano(toPano){
- if(this.currentPano != toPano.pano) {
- if(Potree.settings.displayMode == 'showPanos'){
- this.resetHighMap();
- }
-
- this.smoothZoomTo(1, toPano.duration / 2);
- }
-
-
-
- }
- /* focusPano(toPano ){
- if(this.currentPano !== null){
- return this.flyToPano(toPano);
- }
- if(toPano instanceof Panorama){
- toPano = {pano: toPano}
- }
- let done = (makeIt)=>{
- toPano.deferred && toPano.deferred.resolve(makeIt)
- makeIt && toPano.callback && toPano.callback()
- }
-
- var pano = toPano.pano
- let config = Potree.config.displayMode[Potree.settings.displayMode]
- previousView = {
- //controls: this.viewer.controls,
- position: this.position,
- target: viewer.scene.view.getPivot(),
- };
- //this.viewer.setControls(this.viewer.orbitControls);
- this.viewer.orbitControls.doubleClockZoomEnabled = false;
- for(let image of this.panos){
- image.mesh.visible = false;
- }
- this.selectingEnabled = false;
-
- //console.warn("flyto "+pano.file.split('/').pop() )
- var dur = toPano.duration == void 0 ? 700 : toPano.duration
-
- if(config.atPano.showSkybox){
- if ( this.checkAndWaitForPanoLoad(pano, this.basePanoSize, ()=> {
- setTimeout( ()=>{
- this.focusPano(toPano)
- },1)
- })
- ) {
- return ;
- }
- }
-
-
-
- this.cube.visible = config.atPano.showSkybox
-
- //if(config.transition.showSkybox || config.transition.pointUsePanoTex){
- this.setProjectedPanos({
- progress:0,
- ifSkybox: this.cube.visible,
- ifPointcloud : config.atPano.pointUsePanoTex,
- pano0:pano,
- pano1:pano
- })
- //}
-
- let newCamPos = pano.position.clone()
- let target = newCamPos.clone().add(viewer.scene.view.direction)
-
-
- viewer.scene.view.setView( {position:newCamPos, target, duration:dur ,
- callback:()=>{//done
- //pano.enter()
- this.flying = false
-
- this.currentPano = pano;
- this.updateCube(this.currentPano)
- done(true)
- },onUpdate:(progress)=>{
-
- },
- cancelFun:()=>{this.flying = false}
- })
- this.flying = true
- this.elUnfocus && (this.elUnfocus[0].style.display = "");
- }
- unfocus(o={}){
- this.selectingEnabled = true;
- Potree.settings.displayMode = 'showPointCloud'
-
- if(!this.currentPano && !o.position){
- return;
- }
-
- this.cube.visible = false
-
- viewer.orbitControls.doubleClockZoomEnabled = true;
-
-
- viewer.scene.view.setView($.extend({
- position: previousView.position,
- target:previousView.target,
- duration:500
- },o,{
- callback:()=>{ //done
- for(let pano of this.panos){
- pano.mesh.visible = true;
- //pano.marker.material.depthTest = true
- }
- o.callback && o.callback()
- }
- ,
- cancelFun:()=>{this.flying = false},
- }))
- //this.currentPano.exit()
- this.currentPano = null;
- this.elUnfocus && (this.elUnfocus[0].style.display = "none");
- } */
- updateCube(pano0, pano1){
-
- if(!viewer.scene.pointclouds.some(e=>!e.hasDepthTex)) return this.updateCube2(pano0, pano1) //都hasDepthTex的话
-
- if(Potree.settings.displayMode != 'showPanos')return
-
-
- let f = (bound, size)=>{
- size = size || bound.getSize(new Vector3);
- let center = bound.getCenter(new Vector3);
- size.max(new Vector3(HighMapCubeWidth,HighMapCubeWidth,HighMapCubeWidth));
- this.cube.scale.copy(size);
- this.cube.position.copy(center);
-
- };
-
- let getDis = (bound1, bound2)=>{ //获取bound1边界到bound2边界距离
- if(bound1.intersectsBox(bound2))return 0
- let center1 = bound1.getCenter(new Vector3);
- let center2 = bound2.getCenter(new Vector3);
- let dis = center1.distanceTo(center2);
- let dis1 = bound1.distanceToPoint(center2);
- let dis2 = bound2.distanceToPoint(center1);
- return dis1 + dis2 - dis
- };
-
- if(pano1){//过渡
- if(pano0.pointcloud == pano1.pointcloud){//同一个数据集内的过渡
- f(pano0.pointcloud.bound);
- //console.log('updateCube1' )
- }else {//非同一个数据集内的过渡
- let bound = pano0.pointcloud.bound.clone().union(pano1.pointcloud.bound);
- if(getDis(pano0.pointcloud.bound, pano1.pointcloud.bound) < 100){
- f(bound);
- }else {//如果两个数据集boundingbox距离大于这个距离,扩大一下,防止精度问题导致失真//对很远的数据集似乎没有什么用
- let size = bound.getSize(new Vector3);
- let max = Math.max(size.x, size.y, size.z);
- size.set(max,max,max);
- f(bound, size);
- //console.log('updateCube2', size)
- //far可能要修改下
- }
- }
- }else {
- f(pano0.pointcloud.bound); //假定一个数据集不会太大,否则会造成失真,且bump时移动距离看起来很小。或者可以考虑用固定大小
- //console.log('updateCube3' )
- }
-
- }
-
-
- /* updateCube(pano0, pano1){ //垂直上不分段、 每个pano只有5个方向的版本
- if(Potree.settings.displayMode != 'showPanos')return
- console.log('updateCube',pano0.id, pano1&&pano1.id)
-
- let f = (bound, size)=>{
- size = size || bound.getSize(new THREE.Vector3)
- let center = bound.getCenter(new THREE.Vector3)
- size.max(new THREE.Vector3(HighMapCubeWidth,HighMapCubeWidth,HighMapCubeWidth))
- this.cube.scale.copy(size)
- this.cube.position.copy(center)
-
- }
-
-
- this.cube.geometry.dispose();
-
- if(pano1){//过渡
-
- let add = (pano, dir, )=>{
-
- let minZ, maxZ
- minZ = pano.floorPosition.z
-
- {//天花板高度值
- //用三个间隔120度散开,和中心垂直线成一定夹角的三个向量去求 最高高度 (不求平均的原因:万一是0不好算)
- let rotMat = new THREE.Matrix4().makeRotationX(THREE.Math.degToRad(40))// 角度不能小于天花板中空的半径,大概就是0.2*Math.PI=36度
- let dir1 = new THREE.Vector3(0,0,1).applyMatrix4(rotMat)
- let rotMat1 = new THREE.Matrix4().makeRotationZ(Math.PI*2 / 3);
- let rotMat2 = new THREE.Matrix4().makeRotationZ(-Math.PI*2 / 3);
-
- let dir2 = dir1.clone().applyMatrix4(rotMat1)
- let dir3 = dir1.clone().applyMatrix4(rotMat2)
-
- let zs = [dir1,dir2,dir3].map(dir_=>{
- let intersect = this.depthSampler.sample( {dir: dir_}, pano, true )
- let z = intersect ? intersect.location.z : pano.position.z
- return z
- })
- zs.sort((a,b)=>{return b-a});//得最大值
- maxZ = zs[0]
-
- }
-
-
- const angle = Math.PI/6 //每次转30度
-
- let getDir = (angle_)=>{
- let rotMat = new THREE.Matrix4().makeRotationZ(angle_)
- return dir.clone().applyMatrix4(rotMat)
- }
-
- let dirs = [getDir(angle*2), getDir(angle),getDir(angle), dir.clone(), getDir(-angle), getDir(-angle*2)]; //总共五个方向,夹角总和180度
- dirs.forEach(dir=>{//获取在这个方向上和墙体intersect的最远距离,使用中、上、下三个方向
- //上下分别45度 刚好就是
- let dir0 = dir.clone();// 水平方向
- let dir1 = dir.clone().setZ(1).normalize() //上45度方向
- let dir2 = dir.clone().setZ(-1).normalize() // 下45度方向
-
- let disArr = [dir0,dir1,dir2].map(dir_ =>{
- let intersect = this.depthSampler.sample( {dir: dir_}, pano, true )
-
- let projectLen = intersect.distance ? dir_.dot(dir)*intersect.distance : 0 //得到project在dir的长度
-
- return projectLen
- })
-
- disArr.sort((a,b)=>{return b-a});//得最大值
- dir.multiplyScalar( disArr[0] );//得到水平方向上的最远向量
-
- [minZ,maxZ].forEach(z=>{ //0-9
-
- posArr.push(pano.position.clone().setZ(z).add(dir))
-
- })
-
- });
- [minZ,maxZ].forEach(z=>{ // 10,11
- posArr.push(pano.position.clone().setZ(z))
- })
-
- }
-
-
-
- let posArr = [];
- let faceArr = [[0,1,2],[1,2,3], [2,3,4],[3,4,5], [4,5,6],[5,6,7], [6,7,8],[7,8,9], //外侧四个面
- [0,2,10],[2,4,10],[4,6,10],[6,8,10],//底部扇形
- [1,3,11],[3,5,11],[5,7,11],[7,9,11],//顶部扇形
- ]
-
-
-
-
- //两点连线的水平向量
- let vec = new THREE.Vector3().subVectors(pano0.position, pano1.position).setZ(0).normalize()
-
- add(pano0, vec)
- add(pano1, vec.negate())
-
-
- let faceArr2 = faceArr.map(face=>face.map(i=>i+12))//加上pano2的部分
- faceArr.push(...faceArr2)
- //加上连接的部分,四个面
- faceArr.push([0,1, 8+12],[1, 9+12, 8+12], [8,9,0+12],[9,1+12,0+12],
- [0,8,0+12],[0,0+12,8+12], [1,9,1+12],[1,1+12,9+12],
- )
-
- let geo = MeshDraw.createGeometry(posArr, faceArr)
- this.cube.geometry = geo
- this.cube.scale.set(1,1,1);
- this.cube.position.set(0,0,0)
-
-
- }else{
- if(this.hasUpdateOnePano)return
- this.hasUpdateOnePano = true
- this.cube.geometry = new THREE.BoxBufferGeometry(1,1,1,1)
- f(pano0.pointcloud.bound)
- }
- } */
-
-
- /* updateCube(pano0, pano1){//增加细分的版本,且垂直方向上也分多个
- if(Potree.settings.displayMode != 'showPanos')return
- console.log('updateCube',pano0.id, pano1&&pano1.id)
-
- let f = (bound, size)=>{
- size = size || bound.getSize(new THREE.Vector3)
- let center = bound.getCenter(new THREE.Vector3)
- size.max(new THREE.Vector3(HighMapCubeWidth,HighMapCubeWidth,HighMapCubeWidth))
- this.cube.scale.copy(size)
- this.cube.position.copy(center)
-
- }
-
-
- this.cube.geometry.dispose();
-
- if(pano1){//过渡
- let count1, count2;
-
-
- let panoIndex = 0
- let add = (pano, dir )=>{
- let getPI = function(index, panoIndex_){
- if(panoIndex_ == void 0) panoIndex_ = panoIndex
- return (count1 * count2 + 2 ) * panoIndex_ + index + 2
- }
- let minZ, maxZ
- minZ = pano.floorPosition.z
-
- {//天花板高度值
- //用三个间隔120度散开,和中心垂直线成一定夹角的三个向量去求 最高高度 (不求平均的原因:万一是0不好算)
- let rotMat = new THREE.Matrix4().makeRotationX(THREE.Math.degToRad(40))// 角度不能小于天花板中空的半径,大概就是0.2*Math.PI=36度
- let dir1 = new THREE.Vector3(0,0,1).applyMatrix4(rotMat)
- let rotMat1 = new THREE.Matrix4().makeRotationZ(Math.PI*2 / 3);
- let rotMat2 = new THREE.Matrix4().makeRotationZ(-Math.PI*2 / 3);
-
- let dir2 = dir1.clone().applyMatrix4(rotMat1)
- let dir3 = dir1.clone().applyMatrix4(rotMat2)
-
- let zs = [dir1,dir2,dir3].map(dir_=>{
- let intersect = this.depthSampler.sample( {dir: dir_}, pano, true )
- let z = intersect ? intersect.location.z : pano.position.z
- return z
- })
- zs.sort((a,b)=>{return b-a});//得最大值
- maxZ = zs[0]
-
- let min = pano.position.z + 1
- if(maxZ < pano.position.z + 0.04){//户外
- maxZ = pano.position.z + 50
- }
- maxZ = Math.max(min,maxZ)
- console.log(pano.id, 'maxZ:',maxZ )
- console.log(pano.id, 'minZ:',minZ )
- }
- [maxZ, minZ ].forEach(z=>{
- posArr.push(pano.position.clone().setZ(z))
- })
-
- const angle = Math.PI/8
-
- let getDir = (angle_)=>{
- let rotMat = new THREE.Matrix4().makeRotationZ(angle_)
- return dir.clone().applyMatrix4(rotMat)
- }
-
- let dirs = [ getDir(angle*4), getDir(angle*3), getDir(angle*2), getDir(angle), dir.clone(), getDir(-angle) , getDir(-angle*2), getDir(-angle*3), getDir(-angle*4) ]; // 夹角总和180度
- count1 = dirs.length
- dirs.forEach((dir, index)=>{//获取在这个方向上和墙体intersect的最远距离,使用中、上、下三个方向
-
- let dirs_ = [
-
- dir.clone().setZ(Math.tan(THREE.Math.degToRad(30))).normalize(),
- dir.clone().setZ(Math.tan(THREE.Math.degToRad(15))).normalize(),
- dir.clone(), // 水平方向
- dir.clone().setZ(-Math.tan(THREE.Math.degToRad(15))).normalize(),
- dir.clone().setZ(-Math.tan(THREE.Math.degToRad(30))).normalize(),
-
-
- ];
- count2 = dirs_.length
- dirs_.forEach((dir_, i) =>{
- let intersect = this.depthSampler.sample( {dir: dir_}, pano, true )
- let location
- if(!intersect){ //超级远
- let distance = 100
- location = dir_.clone().multiplyScalar(distance).add(pano.position)
- intersect = {distance,location}
- }
- //intersect.distance
- location = intersect.location.clone()
- let shouldZ// = THREE.Math.clamp(intersect.location.z, minZ, maxZ);
- let needShrink = false
-
- if(i == 0){//最上方的点高度直接等于天花板
- shouldZ = maxZ
- if(location.z<maxZ)location.z = maxZ
- else needShrink = true
- }else if(i == count2-1){//最下方的点高度直接等于地板
- shouldZ = minZ
- if(location.z>minZ)location.z = minZ
- else needShrink = true
- }
- if(needShrink ){//需要在保持dir_不变的情况下修改z
- let len = (shouldZ - pano.position.z) / (intersect.location.z - pano.position.z) * intersect.distance
- location = dir_.clone().multiplyScalar(len).add(pano.position)
- }
-
- posArr.push(location)
-
- if(index!=0 && i!=0){//加入一块四方面
- let curIndex = getPI(count2 * index + i);
-
- faceArr.push([curIndex-1, curIndex-count2-1, curIndex-count2],
- [curIndex-1, curIndex, curIndex-count2])
- }
-
- })
-
- if(index!=0){
- let top = -2;
- let btm = -1
- faceArr.push([getPI(count2*(index-1) ), getPI(count2*index ), getPI(top) ])//天花板扇形
- faceArr.push([getPI(count2*index-1), getPI(count2*(index+1)-1), getPI(btm) ] )//地板扇形
- }else{
- //加入和另一个pano连接的其中一个侧边
- let panoIndex2 = (panoIndex + 1) % 2;
- for(let i=1;i<count2;i++){
- faceArr.push([getPI(i-1), getPI(i), getPI((count1-1)*count2 +i, panoIndex2)],
- [getPI(i-1), getPI((count1-1)*count2+i , panoIndex2) , getPI((count1-1)*count2+i-1 ,panoIndex2)])
- }
- faceArr.push([getPI(0), getPI((count1-1)*count2), getPI(0, panoIndex2)])//加入顶部的一半
- faceArr.push([getPI(count2-1), getPI(count1*count2-1), getPI(count2-1, panoIndex2)])//加入底部的一半
-
-
- }
-
- });
- panoIndex ++
-
- }
-
-
-
- let posArr = [];
- let faceArr = []
-
-
-
- //两点连线的水平向量
- let vec = new THREE.Vector3().subVectors(pano0.position, pano1.position).setZ(0).normalize()
-
- add(pano0, vec)
- add(pano1, vec.negate())
-
-
-
- let geo = MeshDraw.createGeometry(posArr, faceArr)
- this.cube.geometry = geo
- this.cube.scale.set(1,1,1);
- this.cube.position.set(0,0,0)
-
-
- }else{
- this.cube.geometry = new THREE.BoxBufferGeometry(1,1,1,1)
- f(pano0.pointcloud.bound)
- }
- } */
-
-
-
- updateCube3(pano0, pano1){//增加细分的版本,且垂直方向上取中位数... 侧边只有一条
- if(Potree.settings.displayMode != 'showPanos' || pano0 == pano1
- || this.cubePanos.includes(pano0) && this.cubePanos.includes(pano1)
- ) return
-
- this.cubePanos = [pano0, pano1];
-
- //console.log('updateCube',pano0.id, pano1&&pano1.id)
-
- let f = (bound, size)=>{
- size = size || bound.getSize(new Vector3);
- let center = bound.getCenter(new Vector3);
- size.max(new Vector3(HighMapCubeWidth,HighMapCubeWidth,HighMapCubeWidth));
- this.cube.scale.copy(size);
- this.cube.position.copy(center);
-
- };
-
-
- this.cube.geometry.dispose();
-
- if(pano1){//过渡
-
-
- const half = pano0.pointcloud.hasDepthTex && pano0.pointcloud.hasDepthTex ? ( browser.isMobile() ? 3 : 6 ) : (browser.isMobile() ? 2 : 3); //自行输入 (点云计算的慢,还不准)
- let count1 = 2*half;//偶数个 dir 个数
-
- //奇数个的好处:在窄空间内能探测到最远距离,坏处是前方有尖角。偶数个的坏处就是可能检测距离太近。
- let panoIndex = 0;
-
- let getIntersect = (pano, dir, origin)=>{
- if(pano && pano.pointcloud.hasDepthTex ){
- return this.depthSampler.sample( {dir }, pano, true )
- }else {
- origin = origin || pano.position;
- return viewer.inputHandler.getIntersect(viewer.inputHandler.hoverViewport, true, null, null, true, {
- point: origin.clone().add(dir),
- cameraPos: origin
- })
- }
- };
- let getDir = (angle_, vec)=>{ //旋转获得水平向量
- let rotMat = new Matrix4().makeRotationZ(angle_);
- return vec.clone().applyMatrix4(rotMat)
- };
- let getFar = (dir, pano, origin)=>{//获取在这个方向上和墙体intersect的最远距离
- let dirs_ = [
- //注意:角度太大会碰到天花板或地板,越远越容易碰到, 在地下停车场就会伸展不开。 户外时需要更多向上的方向,所以上方向多一个
- dir.clone().setZ(Math.tan(MathUtils.degToRad(30))).normalize(),
- dir.clone().setZ(Math.tan(MathUtils.degToRad(7))).normalize(),
- dir.clone(), // 水平方向
- dir.clone().setZ(-Math.tan(MathUtils.degToRad(5))).normalize(),
- //dir.clone().setZ(-Math.tan(THREE.Math.degToRad(30))).normalize(),
- ];
-
- let max = 50;
- let count2 = dirs_.length;
- let disArr = dirs_.map((dir_, i) =>{
- let intersect = getIntersect(pano, dir_, origin);
-
- let projectLen = intersect && intersect.distance ? dir_.dot(dir)*intersect.distance : max; //得到project在dir的长度
-
- return projectLen
- });
- disArr.sort((a,b)=>{return b-a}); //从大到小
- //console.log(pano ? pano.id : 'side','disArr', disArr)
- let dis = disArr[Math.floor(count2/2-0.5)]; //对半、取前(中位数)
-
-
- return dis
- };
- let add = (pano, vec )=>{
- let getPI = function(index, panoIndex_){
- if(panoIndex_ == void 0) panoIndex_ = panoIndex;
- return (count1*2 + 2 ) * panoIndex_ + index + 2
- };
- let minZ, maxZ;
- minZ = pano.floorPosition.z;
-
- if(pano.ceilZ != void 0){
- maxZ = pano.ceilZ;
- }else {//天花板高度值
- //用三个间隔120度散开,和中心垂直线成一定夹角的三个向量去求 最高高度 (不求平均的原因:万一是0不好算)
- let rotMat = new Matrix4().makeRotationX(MathUtils.degToRad(40));// 角度不能小于天花板中空的半径,大概就是0.2*Math.PI=36度
- let dir1 = new Vector3(0,0,1).applyMatrix4(rotMat);
- let rotMat1 = new Matrix4().makeRotationZ(Math.PI*2 / 3);
- let rotMat2 = new Matrix4().makeRotationZ(-Math.PI*2 / 3);
-
- let dir2 = dir1.clone().applyMatrix4(rotMat1);
- let dir3 = dir1.clone().applyMatrix4(rotMat2);
-
- let zs = [dir1,dir2,dir3].map(dir_=>{
- let intersect = getIntersect(pano, dir_);
-
- let z = intersect ? intersect.location.z : pano.position.z+50;
- return z
- });
- zs.sort((a,b)=>{return b-a});//得最大值
- maxZ = zs[0];
-
- let min = pano.position.z + 1;
- maxZ = Math.max(min,maxZ);
- pano.ceilZ = maxZ;
- //console.log(pano.id, 'maxZ:',maxZ )
- //console.log(pano.id, 'minZ:',minZ )
- }
- [maxZ, minZ ].forEach(z=>{
- posArr.push(pano.position.clone().setZ(z));
- });
-
- const angle = Math.PI/(count1-1);
-
-
- //在画面上线条从左往右数
- let dirs = [];
- for(let i=0;i<count1;i++){
- dirs.push(getDir(Math.PI/2-i*angle, vec));//正的在左边
- }
-
-
-
- let sideDis = [];
- dirs.forEach((dir, index)=>{
- let dis = getFar(dir, pano);
- if(index == 0 || index == count1-1){
- sideDis.push(dis);
- }
- dir.multiplyScalar( dis );
-
- [maxZ,minZ].forEach(z=>{
- posArr.push(pano.position.clone().setZ(z).add(dir));
- });
-
- if(index!=0){//加入一块四方面
- let m = index*2;
- faceArr.push([getPI(m), getPI(m-1), getPI(m-2)],
- [getPI(m), getPI(m+1), getPI(m-1)]);
-
- let top = -2;
- let btm = -1;
- faceArr.push([getPI(m), getPI(m-2), getPI(top) ]);//天花板扇形
- faceArr.push([getPI(btm), getPI(m-1), getPI(m+1) ] );//地板扇形
-
- }
- });
-
-
-
-
- let panoIndex2 = (panoIndex + 1) % 2;
- //let m = (count1-1)*2
- /* faceArr.push([getPI(0), getPI(1), getPI(count1*2-1, panoIndex2)],
- [getPI(0), getPI(count1*2-1,panoIndex2) , getPI(count1*2-2,panoIndex2)])
-
- faceArr.push([getPI(0, panoIndex2), getPI(count1*2-2), getPI(0)])//加入顶部的一半
- faceArr.push([getPI(1), getPI(count1*2-1), getPI(1, panoIndex2)])//加入底部的一半 */
-
-
-
- //加入和另一个pano连接
- let sideMid = count1*4+4; //侧边点start
- //侧边的一半
- faceArr.push([getPI(0), getPI(1), sideMid+panoIndex*2],
- [sideMid+panoIndex*2, getPI(1) , sideMid+panoIndex*2+1 ],
- [getPI(count1*2-2), sideMid+panoIndex2*2+1, getPI(count1*2-1)],
- [getPI(count1*2-2), sideMid+panoIndex2*2, sideMid+panoIndex2*2+1 ]
- );
-
- faceArr.push([getPI(0), sideMid+panoIndex2*2, getPI(count1*2-2)],
- [getPI(0),sideMid+panoIndex*2, sideMid+panoIndex2*2]
- );//加入顶部的一半
- faceArr.push([getPI(count1*2-1), sideMid+panoIndex2*2+1, getPI(1)],
- [sideMid+panoIndex2*2+1, sideMid+panoIndex*2+1, getPI(1)]
- );//加入底部的一半
-
-
-
- panoIndex ++;
- return sideDis;
- };
-
- let addSide = ()=>{//两个漫游点间两边各加一条侧线,为了适应四方形房间漫游点在对角方向的那种情况
- //侧边为了取中点,没办法用全景图获取了,用点云
- let sideMaxZ = (pano0.ceilZ + pano1.ceilZ)/2;
- let sideMinZ = (pano0.floorPosition.z+pano1.floorPosition.z)/2;
-
- let sideDis_ = [(sideDis0[0] + sideDis1[1])/2, (sideDis0[1] + sideDis1[0])/2];
- let sideDis = [Math.min(sideDis0[0] , sideDis1[1]), Math.min(sideDis0[1] , sideDis1[0])];//
-
- let mid = new Vector3().addVectors(pano0.position, pano1.position).multiplyScalar(0.5);
- let sideDirs = [getDir(-Math.PI/2, vec), getDir(Math.PI/2, vec)];
-
- if(pano0.pointcloud.hasDepthTex && pano0.pointcloud.hasDepthTex){
- let panos = [pano0,pano1];
- let vecs = [vec.negate(), vec];
- let axis = [[-1,1],[1,-1]];
- let dis2d = new Vector2$1().subVectors(pano0.position, pano1.position).length();//水平上的距离
- let angles = [MathUtils.degToRad(45), MathUtils.degToRad(60)];
- sideDirs.forEach((dir, index0)=>{
- let disToSides = [];
- panos.forEach((pano,index)=>{
- let dirs = [getDir(axis[index0][index]*angles[0], vecs[index]), getDir(axis[index0][index]*angles[1], vecs[index])];//一侧的若干角度
- dirs.forEach((dir_,i)=>{
- let dis1 = getFar(dir_, pano);
- //dir_.multiplyScalar( dis1 );
- let disToPano2d = dis1 * Math.cos(angles[i]);
- if(disToPano2d<dis2d){//超过的话都到另一半pano的半圆了,不计入
- let disToSide = dis1 * Math.sin(angles[i]);
- disToSides.push(disToSide);
- }
- });
- });
- let disToSide;
- if(disToSides.length){
- disToSides.sort((a,b)=>{return b-a});//从大到小
- disToSide = disToSides[0];
- console.log('disToSides', index0, disToSides);
- }else {
- disToSide = sideDis[index0];
- }
- dir.multiplyScalar( disToSide );
-
- [sideMaxZ,sideMinZ].forEach(z=>{
- posArr.push(mid.clone().setZ(z).add(dir)); //是直接使用最长dis的那个intersect点好还是mid
- });
- });
-
- }else {
-
- sideDirs.forEach((dir_ ,index)=>{
- let dis = getFar(dir_, null, mid);
- dir_.multiplyScalar( /* sideDis_[index] */ Math.max(dis, sideDis[index]) );
-
-
- [sideMaxZ,sideMinZ].forEach(z=>{
- posArr.push(mid.clone().setZ(z).add(dir_));
- });
- });
- }
- };
-
- let posArr = [];
- let faceArr = [];
-
-
-
- //两点连线的水平向量
- let vec = new Vector3().subVectors(pano0.position, pano1.position).setZ(0).normalize();
- let sideDis0 = add(pano0, vec);
- let sideDis1 = add(pano1, vec.negate());
- addSide();
-
-
- //MeshDraw.updateGeometry(this.cube.geometry, posArr, faceArr)
- let geo = MeshDraw.createGeometry(posArr, faceArr);
- this.cube.geometry = geo;
- this.cube.scale.set(1,1,1);
- this.cube.position.set(0,0,0);
-
-
- //this.cube.scale.set(100,100,100);
- //this.cube.position.copy(pano1.position).multiplyScalar(-100)
- }else {
- this.cube.geometry = new BoxBufferGeometry(1,1,1,1);
- f(pano0.pointcloud.bound);
- }
- }
-
-
-
-
- updateCube2(pano0, pano1){//增加细分的版本,且垂直方向上取中位数 侧边多条
- if(Potree.settings.displayMode != 'showPanos' || pano0 == pano1
- || this.cubePanos.includes(pano0) && this.cubePanos.includes(pano1)
- ) return
-
- this.cubePanos = [pano0, pano1];
-
- //console.log('updateCube',pano0.id, pano1&&pano1.id)
-
- let useBound = (bound, size)=>{
-
- size = size || bound.getSize(new Vector3);
- let center = bound.getCenter(new Vector3);
- size.max(new Vector3(HighMapCubeWidth,HighMapCubeWidth,HighMapCubeWidth));
- this.cube.geometry = new BoxBufferGeometry(1,1,1,1);
- this.cube.scale.copy(size);
- this.cube.position.copy(center);
- };
-
-
- this.cube.geometry.dispose();
-
- if(pano1){//过渡
-
- if(pano0.pointcloud != pano1.pointcloud){ //距离太远的数据集,过渡会畸变。所以扩大skybox
- let dis = pano0.position.distanceTo(pano1.position);
- if(dis > 100){
- let bound = pano0.pointcloud.bound.clone().union(pano1.pointcloud.bound);
- let size = bound.getSize(new Vector3);
- let max = Math.max(size.x, size.y, size.z);
- size.set(max,max,max);
- return useBound(bound, size)
- }
- }
-
-
- const half = pano0.pointcloud.hasDepthTex && pano0.pointcloud.hasDepthTex ? 6 : (browser.isMobile() ? 2 : 3); //自行输入 (点云计算的慢,还不准)
- let count1 = 2*half;//偶数个 每个pano向 外dir 个数
- //奇数个的好处:在窄空间内能探测到最远距离,坏处是前方有尖角。偶数个的坏处就是可能检测距离太近。
-
- //let panoIndex = 0
-
- let getIntersect = (pano, dir, origin)=>{
- if(pano && pano.pointcloud.hasDepthTex ){
- return this.depthSampler.sample( {dir }, pano, true )
- }else {
- origin = origin || pano.position;
- return viewer.inputHandler.getIntersect(viewer.inputHandler.hoverViewport, true, null, null, true, {
- point: origin.clone().add(dir),
- cameraPos: origin
- })
- }
- };
- let getDir = (angle_, vec)=>{ //旋转获得水平向量
- let rotMat = new Matrix4().makeRotationZ(angle_);
- return vec.clone().applyMatrix4(rotMat)
- };
- let getFar = (dir, pano, origin)=>{//获取在这个方向上和墙体intersect的距离
- //在垂直方向上分出多个方向,取一个最可能的接近真实的距离
- let dirs_ = [
- //注意:角度太大会碰到天花板或地板,越远越容易碰到, 在地下停车场就会伸展不开。 户外时需要更多向上的方向,所以上方向多一个
- dir.clone().setZ(Math.tan(MathUtils.degToRad(30))).normalize(),
- dir.clone().setZ(Math.tan(MathUtils.degToRad(7))).normalize(),
- dir.clone(), // 水平方向
- dir.clone().setZ(-Math.tan(MathUtils.degToRad(5))).normalize(),
- //dir.clone().setZ(-Math.tan(THREE.Math.degToRad(30))).normalize(),
- ];
-
- let max = 50;
- let count2 = dirs_.length;
- let disArr = dirs_.map((dir_, i) =>{
- let intersect = getIntersect(pano, dir_, origin);
- let projectLen = intersect && intersect.distance ? dir_.dot(dir)*intersect.distance : max; //得到project在dir的长度
- return projectLen //得水平距离
- });
- disArr.sort((a,b)=>{return b-a}); //从大到小
- //console.log(pano ? pano.id : 'side','disArr', disArr)
- let dis = disArr[Math.floor(count2/2-0.5)]; //对半、取前(中位数)
- return dis
- };
- let sideCount = [];
-
- let addPos = (pano, vec )=>{//添加这个pano这一侧向外半圆的顶点
- //添加pano位置对应的最高点最低点:
- let minZ, maxZ;
- minZ = pano.floorPosition.z;
-
- if(pano.ceilZ != void 0){
- maxZ = pano.ceilZ;
- }else {//天花板高度值
- //用三个间隔120度散开,和中心垂直线成一定夹角的三个向量去求 最高高度 (不求平均的原因:万一是0不好算)
- let rotMat = new Matrix4().makeRotationX(MathUtils.degToRad(40));// 角度不能小于天花板中空的半径,大概就是0.2*Math.PI=36度
- let dir1 = new Vector3(0,0,1).applyMatrix4(rotMat);
- let rotMat1 = new Matrix4().makeRotationZ(Math.PI*2 / 3);
- let rotMat2 = new Matrix4().makeRotationZ(-Math.PI*2 / 3);
-
- let dir2 = dir1.clone().applyMatrix4(rotMat1);
- let dir3 = dir1.clone().applyMatrix4(rotMat2);
- let skyHeight = 50;
- let zs = [dir1,dir2,dir3].map(dir_=>{
- let intersect = getIntersect(pano, dir_);
- let z = intersect ? intersect.location.z : pano.position.z+skyHeight; //没有intersect代表可能是天空
- return z
- });
- zs.sort((a,b)=>{return b-a});//得最大值 (不用中位数的原因:在屋檐处,如果仅有一个intersect是天空,因到了室外所以也用天空高度)
- maxZ = zs[0];
-
- let min = pano.position.z + 1; // 防止意外太低
- maxZ = Math.max(min,maxZ);
- pano.ceilZ = maxZ;
- //console.log(pano.id, 'maxZ:',maxZ )
- //console.log(pano.id, 'minZ:',minZ )
- }
- [maxZ, minZ ].forEach(z=>{
- posArr.push(pano.position.clone().setZ(z));
- });
-
-
-
- //在画面上线条从左往右数
- const angle = Math.PI/(count1-1);
- const dirs = []; //平分这半边180度
- for(let i=0;i<count1;i++){
- dirs.push(getDir(Math.PI/2-i*angle, vec));//正的在左边
- }
-
-
-
- // let sideDis = []
- dirs.forEach((dir, index)=>{
- let dis = getFar(dir, pano);
- // if(index == 0 || index == count1-1){
- // sideDis.push(dis)
- // }
- dir.multiplyScalar( dis );
-
- [maxZ,minZ].forEach(z=>{
- posArr.push(pano.position.clone().setZ(z).add(dir)); //获取到外墙点
- });
-
-
- });
-
- //panoIndex ++
- //return sideDis;
- };
-
-
-
- let addSide = ()=>{//两个漫游点间两边各加一些侧线
- //中点处的
- let midMaxZ = (pano0.ceilZ + pano1.ceilZ)/2;
- let midMinZ = (pano0.floorPosition.z+pano1.floorPosition.z)/2;
- let mid = new Vector3().addVectors(pano0.position, pano1.position).multiplyScalar(0.5);
-
- /* let sideDis_ = [(sideDis0[0] + sideDis1[1])/2, (sideDis0[1] + sideDis1[0])/2];
- let sideDis = [Math.min(sideDis0[0] , sideDis1[1]), Math.min(sideDis0[1] , sideDis1[0])]//
- */
-
-
- if(pano0.pointcloud.hasDepthTex && pano0.pointcloud.hasDepthTex){
- let panos = [pano0,pano1];
- let vecs = [vec.clone().negate(), vec];
- let axis = [[-1,1],[1,-1]];
- let dis2d = new Vector2$1().subVectors(pano0.position, pano1.position).length();//水平上的距离
- let angles = [MathUtils.degToRad(40), MathUtils.degToRad(70)]; //正的在左边 尽量能够平分中间这段墙体
- axis.forEach((axis_, index0)=>{
- let disToSides = [];
- let accordingPano = index0 == 0 ? pano0 : pano1; //根据离该点在vec方向上的距离顺序来存顶点
- panos.forEach((pano,index)=>{
- let dirs = [getDir(axis_[index]*angles[0], vecs[index]), getDir(axis_[index]*angles[1], vecs[index])];//一侧的若干角度
-
- dirs.forEach((dir_,i)=>{
- let dis1 = getFar(dir_, pano);
-
- let disToPano2d = dis1 * Math.cos(angles[i]);
- if(disToPano2d<dis2d){//超过的话都到另一半pano的半圆了,不计入
- let disToSide = dis1 * Math.sin(angles[i]);
-
- if(pano != accordingPano){
- disToPano2d = dis2d - disToPano2d; //反一下
- }
-
- dir_.multiplyScalar( dis1 );
- disToSides.push({disToSide,disToPano2d, pano, dir_});
- }
- });
- });
-
-
- sideCount[index0] = disToSides.length; //记录侧边个数
-
- if(disToSides.length){
- //disToSides.sort((a,b)=>{return b-a});//从大到小
- //由距离accordingPano的近到远:
- disToSides.sort((a,b)=>{return a.disToPano2d-b.disToPano2d});
-
- //console.log('disToSides', index0, disToSides)
-
-
- disToSides.forEach(e=>{//求z
- let ratio = e.disToPano2d / dis2d;
- let r = accordingPano == pano0 ? (1-ratio) : ratio;
- let sideMaxZ_ = pano0.ceilZ * r + pano1.ceilZ * (1-r);
- let sideMinZ_ = pano0.floorPosition.z * r + pano1.floorPosition.z * (1-r);
- [sideMaxZ_,sideMinZ_].forEach(z=>{
- posArr.push(e.pano.position.clone().setZ(z).add(e.dir_)); //是直接使用最长dis的那个intersect点好还是mid
- });
-
- });
-
- }
- });
-
- }else {
- //这段针对点云时,仅测试才会执行到
- sideCount = [1,1];
- let sideDirs = [getDir(Math.PI/2, vec), getDir(-Math.PI/2, vec)];
- sideDirs.forEach((dir_ ,index)=>{
- let dis = getFar(dir_, null, mid); //直接从中点求两侧的距离
- dir_.multiplyScalar( /* Math.max( */dis/* , sideDis[index]) */ );
- [midMaxZ,midMinZ].forEach(z=>{
- posArr.push(mid.clone().setZ(z).add(dir_));
- });
- });
- }
-
-
- //中心:
- [midMaxZ,midMinZ].forEach(z=>{
- posArr.push(mid.clone().setZ(z));
- });
- };
-
-
-
-
- //positions存放顺序:pano的每边的 zMax和zMin 、count1个dir的点 ;侧边的点 ;连接处顶底的中点
- let addFaces = ()=>{
-
- let getPI = function(index, posType, panoIndex){//获取顶点序号
- return 2 + (count1*2 + 2 ) * panoIndex + index*2 + (posType == 'top' ? 0 : 1)
- };
- let getSidePI = function(index, posType, panoIndex){//获取侧边顶点序号
- if(panoIndex == 1) index += sideCount[0];
- return getPI(index, posType, 2)-2
- };
- let getPanoPI = function(posType, panoIndex){//获取pano处对应的点序号
- return getPI(-1, posType, panoIndex)
- };
- let topCenter = posArr.length-2; //最后添加的两个中心点
- let btmCenter = posArr.length-1;
-
- for(let i=0;i<2;i++){
- for(let index=1; index<count1; index++){
- //pano外侧半圆围墙
- faceArr.push([getPI(index,'top',i), getPI(index-1,'btm',i), getPI(index-1,'top',i)],//加入一块四方面
- [getPI(index,'top',i), getPI(index,'btm',i), getPI(index-1,'btm',i)]);
-
- faceArr.push([getPI(index,'top',i), getPI(index-1,'top',i), getPanoPI('top',i)]);//天花板扇形
- faceArr.push([getPI(index,'btm',i), getPI(index-1,'btm',i), getPanoPI('btm',i)]);//地板扇形
-
- }
-
- let j = (i + 1) % 2; //另一个pano
-
-
- //侧边
-
- for(let u=0; u<=sideCount[i]; u++){
- //侧边每个四方的左上右上左下右下四个点
- let pLT = u == 0 ? getPI(0,'top',i) : getSidePI(u-1, 'top',i);
- let pRT = u == sideCount[i] ? getPI(count1-1,'top',j) : getSidePI(u, 'top',i);
- let pLB = u == 0 ? getPI(0,'btm',i) : getSidePI(u-1, 'btm',i);
- let pRB = u == sideCount[i] ? getPI(count1-1,'btm',j) : getSidePI(u, 'btm',i);
-
- faceArr.push([pLT,pLB,pRB],[pLT,pRB,pRT]);//外侧四方面
- faceArr.push([pLT,topCenter,pRT] ,[pLB,btmCenter,pRB] );//顶部和底部到整体中心的扇形(由于点的顺序是根据和两个pano的距离,因此到中心的夹角并没按顺序排列,所以可能会重叠)
-
- if(i==0){//只需要算一次
- //顶部和底部中心与两个pano边构成的三角形
- if(u == 0){
- faceArr.push([pLT,getPI(count1-1,'top',i),topCenter],
- [pLB,getPI(count1-1,'btm',i),btmCenter],
- );
- }
- //不能用else 因为当length==0时u==0也==sideCount[i],此时就是要两个面
- if(u == sideCount[i]){
- faceArr.push([pRT,getPI(0,'top',j),topCenter],
- [pRB,getPI(0,'btm',j),btmCenter],
- );
- }
- }
-
- }
-
- }
-
- };
-
-
-
-
- let posArr = [];
- let faceArr = [];
-
-
-
- //两点连线的水平向量
- let vec = new Vector3().subVectors(pano0.position, pano1.position).setZ(0).normalize();
-
- /* let sideDis0 = */addPos(pano0, vec);
- /* let sideDis1 = */addPos(pano1, vec.clone().negate());
- addSide();
- addFaces();
-
- //MeshDraw.updateGeometry(this.cube.geometry, posArr, faceArr)
- let geo = MeshDraw.createGeometry(posArr, faceArr);
- this.cube.geometry = geo;
- this.cube.scale.set(1,1,1);
- this.cube.position.set(0,0,0);
-
-
- //this.cube.scale.set(100,100,100);
- //this.cube.position.copy(pano1.position).multiplyScalar(-100)
- }else {
-
- useBound(pano0.pointcloud.bound);
- }
- }
-
- /*
- 注: 修改skybox,若不准的话,会遮住其他mesh,比如marker。尤其在没有深度贴图时。
-
-
- */
- /* updateCube(params){
- let size = params.boundSize
- this.cube.scale.set(Math.max(size.x, HighMapCubeWidth), Math.max(size.y, HighMapCubeWidth), Math.max(size.z, HighMapCubeWidth) )
- this.cube.position.copy(params.center)
-
- } */
- bump(direction) {//撞墙弹回效果
- if (!this.bumping && !this.latestToPano) {
-
- console.log('bump');
- let distance = Potree.settings.displayMode == 'showPanos' ? 0.3 : 0.2;//感觉点云模式比全景模式更明显,所以降低
- let currentPos = this.position.clone();
- let endPosition = new Vector3().addVectors(this.position, direction.clone().multiplyScalar(distance));
-
- let duration = 150;
- viewer.scene.view.setView({position:endPosition, duration,
- callback:()=>{
- viewer.scene.view.setView({position:currentPos, duration: duration*5,
- callback: ()=>{
- this.bumping = false;
- //this.dispatchEvent('cameraMoveDone')
- },
- Easing:'easeInOutSine',
- cancelFun:()=>{this.bumping = false;}
- });
- this.bumping = true;
- },
-
- cancelFun:()=>{this.bumping = false;},
- Easing:'easeInOutSine'
- });
- this.bumping = true;
- }
- //备注:将4dkk中的‘前后方向变化fov、左右方向移动镜头’ 都改为移动镜头。 因为这里无法判断左右离壁距离。
-
- }
- /* load(pano, ){
- return new Promise(resolve => {
- let texture = texLoader.load(pano.file, resolve);
- texture.wrapS = THREE.RepeatWrapping;
- texture.repeat.x = -1;
- texture.flipY = false//add
- pano.texture = texture;
-
- //add
- texture.wrapS = texture.wrapT = THREE.ClampToEdgeWrapping;
- texture.minFilter = THREE.LinearFilter;
- texture.magFilter = THREE.LinearFilter;
- texture.generateMipmaps = true;
-
- });
- } */
- flyToPanoClosestToMouse() {
- /* if (Date.now() - this.mouseLastMoveTime > 50) {
- //this.intersect = this.getMouseIntersect();
- this.intersect && this.updateClosestPano(this.intersect);
- } */
-
- if(!Potree.settings.ifShowMarker){//不显示marker的时候mousemove没更新鼠标最近点所以更新
- this.updateClosestPano(viewer.inputHandler.intersectPoint);
- }
- //console.log('flyToPanoClosestToMouse',this.closestPano)
-
- if (this.closestPano) {
- let pano = this.closestPano;
- return this.flyToPano({
- pano
- });
-
- }
- var direction = this.viewer.inputHandler.getMouseDirection().direction;
- this.flyDirection(direction);
- }
- flyDirection(direction, option1, option2) {
- var deferred = $.Deferred();
- //this.history.invalidate();
- var panoSet = this.closestPanoInDirection(direction, option1, option2);
- if (panoSet) {
- this.flyToPano({
- pano: panoSet,
- callback: deferred.resolve.bind(deferred, !0)
- } );
- } else {
- this.bump(direction);
- deferred.resolve(!1);
- }
- return deferred.promise();
- }
- closestPanoInDirection(direction, option1, option2) {
- return this.rankedPanoInDirection(0, direction, option1, option2)
- }
- rankedPanoInDirection(t, direction, option1, option2){
- var panoSet = {
- pano: null,
- candidates: [] //缓存顺序--如果需要打印的话
- };
- t || (t = 0);
- option1 = void 0 !== option1 ? option1 : .75;
- var o = option2 ? "angle" : "direction";
-
- var floor = viewer.modules.SiteModel.currentFloor;
- var entity = viewer.modules.SiteModel.inEntity;
- var getHeightDis = (pano)=>{
- if(floor && !floor.panos.includes(pano) && pano.position.z < this.position.z){ //若是上方的漫游点,就正常走。因为一般不会点击天花板。
- return this.position.z - pano.position.z
- }else {
- return 0
- }
-
- };
-
-
-
- var request = [//必要条件
- Images360.filters.inPanoDirection( this.position, direction, option1),
- //Images360.filters.isNeighbourPanoTo(this.currentPano),
- Images360.filters.not(this.currentPano),
- Images360.filters.isEnabled(),
-
- /* (pano)=>{ //防止不小心穿越地板到下一层, 尽量走楼梯,实在没有楼梯或楼梯漫游点稀疏的话就通过楼层按钮。
- let dis = getHeightDis(pano)
- //console.log('getHeightDis',pano.id,dis)
- if(dis < 3){//不能超过最大高度差( 最大高度差暂定为接近一层楼的高度。)(最好的解决方案是设置漫游可行)
- return true
- }else{
- return this.isNeighbour(this.currentPano, pano)
- }
- } */
- ];
-
-
- var list = [//决胜项目
- Images360.scoreFunctions.distanceSquared(this.position,1, true),
-
- Images360.scoreFunctions[o]( this.position, direction,true),
-
-
- (pano)=>{
- let neighbour = this.isNeighbour(this.currentPano, pano);
- //console.log('neighbour', pano.id, neighbour ? directionFactor*0.4 : 0)
- return neighbour ? directionFactor*1 : 0;
- }
-
-
- /* (pano)=>{//尽量不穿越地板到下一层
- let dis = getHeightDis(pano)
- return -dis * directionFactor * 0.1;
- }, */
-
- /* (pano)=>{ //尽量在一个建筑内行走,这样不易穿墙
- if(entity && !entity.panos.includes(pano) ){
- return - directionFactor * 0.05; //- directionFactor * 0.2;
- }else{ //不能设置太高,否则会走不进大房间的小房间中,因为容易穿过小房间的一个门到另一个门外
- return 0
- }
- } */
- ];
- if(viewer.inputHandler.intersectPoint && this.currentPano ){//方便上下楼, 考虑panos之间的角度差
- let pos1 = this.currentPano.floorPosition;
- let vec1 = new Vector3().subVectors(viewer.inputHandler.intersectPoint.location, pos1 ).normalize();//应该只有atPano时才会执行到这吧?
- list.push( function(pano) {
- var pos2 = pano.floorPosition;
- var vec2 = pos2.clone().sub(pos1).normalize();
- //console.log('direction2', pano.id, vec2.dot(vec1) * directionFactor * 1 );
- return vec2.dot(vec1) * directionFactor * 1
- });
- }
-
- this.findRankedByScore(t,request,list,panoSet);
-
- return panoSet.pano;
-
- }
- findRankedByScore(e, t, i, n) {
- n && (n.candidates = null, //candidates 缓存顺序--如果需要打印的话
- n.pano = null),
- e || (e = 0);
- var r = Common.sortByScore(this.panos, t, i);
- //console.log('findRankedByScore', r && r.map(u=>u.item.id + '|' + u.score))
-
-
- return !r || 0 === r.length || e >= r.length ? null : (n && (n.candidates = r,
- n.pano = r[e].item),
- r[e].item)
- }
-
- isNeighbour(pano0, pano1){//是否之间没有遮挡(在加载visibles之前,自己算) 最好pano0是currentPano
-
- let map0 = this.neighbourMap[pano0.id];
- if(!map0){
- map0 = {};
- this.neighbourMap[pano0.id] = map0;
- }
- let map1 = this.neighbourMap[pano1.id];
- if(!map1){
- map1 = {};
- this.neighbourMap[pano1.id] = map1;
- }
-
-
- let ifNeighbour = map0[pano1.id];//map0.get(pano1)
-
- if(ifNeighbour == void 0){
- ifNeighbour = true;
-
- let margin = 0.1;
-
- let usePointcloud = pano0.depthTex;
- if(pano0.depthTex || pano1.depthTex){
- let mainPano = pano0.depthTex ? pano0 : pano1;
- let subPano = pano0.depthTex ? pano1 : pano0;
-
- //暂时只判断到pano,不判断到marker的方向
- let dir = new Vector3().subVectors(subPano.position, mainPano.position).normalize();
- let intersectPoint = viewer.images360.depthSampler.sample({dir}, mainPano, true);
- if(intersectPoint && intersectPoint.distance+margin <= pano0.position.distanceTo(pano1.position)){
- ifNeighbour = false;
- }
- }else {
- //使用点云判断(有深度贴图时不会执行到这)
- ifNeighbour = !viewer.inputHandler.ifBlockedByIntersect(pano1.position, margin, true, pano0.position);
- console.log('使用点云判断');
- if(ifNeighbour){//点云模式下未加载的点云会判断为true
- let dir = new Vector3().subVectors(pano1.position,pano0.position).normalize();
- let dis = pano1.position.distanceTo(pano0.position);
- let hfov = cameraLight$1.getHFOVForCamera(viewer.mainViewport.camera , true );
- let max = Math.cos(MathUtils.degToRad(10));
- let min = Math.cos(MathUtils.degToRad(80));
- if(this.getDirection().dot(dir) < MathUtils.clamp(Math.cos(hfov/2/* THREE.Math.degToRad(40) */) * dis / 10, min, max )){//距离越远要求和视线角度越接近
- ifNeighbour = undefined; //不确定
- }
- }
- }
-
- map0[pano1.id] = ifNeighbour;//map0.set(pano1, ifNeighbour)
- map1[pano0.id] = ifNeighbour;//map1.set(pano0, ifNeighbour)
- }
- return ifNeighbour
- }
- updateClosestPano(intersect, state) {//hover到的pano 大多数时候是null
- /* if(this.isAtPano() ){
- filterFuncs.push(Images360.filters.not(this.currentPano));
-
- //当静止在漫游点时closestPano只限制在每个漫游点附近,而在观看整个模型时,范围夸大,识别为离鼠标最近的漫游点。 (故而要排除flying时)
- filterFuncs.push(Images360.filters.inFloorDirection(this.position, viewer.scene.view.direction, .25))//许钟文改
- filterFuncs.push(Images360.filters.isCloseEnoughTo(intersect, 0.35));
- filterFuncs.push(Images360.filters.isEnabled())
- }else{
-
- } */
-
-
- var pano;
- if(this.isAtPano() ){
- if(intersect instanceof Panorama){
- pano = state ? intersect : null;
- }else {
- return
- }
- }else {
- if(this.flying)return;
- var filterFuncs = [];
- intersect = intersect && intersect.location;
- if(!intersect)return
- let sortFuncs = Potree.settings.editType != 'pano'? [Images360.sortFunctions.floorDisSquaredToPoint(intersect)] : [Images360.sortFunctions.disSquaredToPoint(intersect)];
- pano = Common.find(this.panos, filterFuncs, sortFuncs);
- }
-
-
-
-
- if (pano != this.closestPano) {
- pano && (this.isPanoHover = !0);
-
- this.closestPanoChanging(this.closestPano, pano); // 高亮marker
- //console.log('closestPano '+ (pano ? pano.id : 'null' ))
- this.closestPano = pano;
- } else {
- this.isPanoHover = !1;
- }
- }
- closestPanoChanging(oldPano, newPano){
- if(!Potree.settings.ifShowMarker)return
-
- oldPano && oldPano.hoverOff({byImages360:true});
- newPano && newPano.hoverOn({byImages360:true});
-
-
- }
-
-
- getTileDirection(){//根据不同dataset的来存储
- var vectorForward = viewer.scene.view.direction.clone();
-
- var vectorForwards = viewer.scene.pointclouds.map(e=>{
- var inv = new Matrix4().copy(e.rotateMatrix).invert();//乘上dataset的旋转的反转
- var direction = vectorForward.clone().applyMatrix4(inv);
- return {
- datasetId: e.dataset_id,
- direction: math.convertVector.ZupToYup(direction)
- }
- });
- //return vectorForwards[0].direction
-
-
- return {
- datasetsLocal: vectorForwards,
- vectorForward
- }
-
-
- }
-
-
-
- update(){
- let {viewer} = this;
- /* if(currentlyHovered){
- currentlyHovered.material = sm;
- currentlyHovered = null;
- }
- if(this.selectingEnabled){
- this.handleHovering();
- } */
- if(this.tileDownloader.started){
-
- var vectorForwards = this.getTileDirection();
-
- //vectorForwards = vectorForwards[0].direction
-
- this.updateTileDownloader(tileArr, vectorForwards);
- this.updatePanoRenderer(vectorForwards);
- }
-
- this.updateZoomPano();
-
- }
-
-
-
-
-
-
- updateTileDownloader(t, vectorForward) {
- var i = this.nextPano || this.currentPano;
- if(i){
- this.tileDownloader.tilePrioritizer.updateCriteria(i, viewer.scene.view.position.clone() , vectorForward, t.length > 0 ? t : null),
- this.tileDownloader.processPriorityQueue = !0;
- }
- }
-
-
- updatePanoRenderer(vectorForward) {
- var i = this.nextPano || this.currentPano;
- if(i){
- if (this.panoRenderer.hasQueuedTiles() && i) {
- this.panoRenderer.updateDirection(vectorForward);
- }
- }
-
- }
-
-
-
-
-
-
- //等待部分加载完
- checkAndWaitForTiledPanoLoad(pano, basePanoSize, callback1, callback2, progressCallback, iswait, isclear, l) {
-
- if (!pano) {
- console.error("Player.checkAndWaitForTiledPanoLoad() -> Cannot load texture for null pano.");
- }
-
- var vectorForward = this.getTileDirection();
-
-
- if (!pano.isLoaded(basePanoSize)) {
- iswait && viewer.waitForLoad(pano, function() {//发送loading
- return pano.isLoaded(basePanoSize)
- });
-
-
-
- /* var fov = {//test for direction 预加载的边缘有一丢丢不准确,尤其在相机倾斜时(4dkk也是)。
- hFov: cameraLight.getHFOVForCamera(viewer.scene.getActiveCamera() ),
- vFov: viewer.scene.getActiveCamera().fov
- }//原先是null,不要求方向 */
- var fov = null; //若不为null的话,因为可能可见范围的tile下载过了从而无法触发下载,然后得不到下载成功的消息,怎么办
-
-
-
- pano.loadTiledPano(/* 1024 */ basePanoSize , vectorForward, fov, isclear, l, null).done(function(e, t) {
- callback1 && callback1(e, t);
- }
- .bind(this)).fail(function(msg) {
- callback2 && callback2(msg);
- }
- .bind(this)).progress(function(e, t, i) {
- progressCallback && progressCallback(e, t, i);
- }
- .bind(this));
- return !0;
- }
-
- }
- /* load(){//quickstart里的
-
- var lowSize = this.qualityManager.getPanoSize(PanoSizeClass.BASE),
- highSize = this.qualityManager.getPanoSize(PanoSizeClass.STANDARD),
- d = cameraLight.getHFOVForCamera(this.quickStartcamera, $('#player').width(), $('#player').height()),
- p = this.quickStartcamera.fov,
- r = Vectors.FORWARD.clone().applyQuaternion(this.view.quaternion)
-
- var promise1 = this.view.pano.loadTiledPano(highSize, r, {
- hFov: d,
- vFov: p
- }, !1, !1 , !0 )
- var promise2 = this.view.pano.loadTiledPano(lowSize, r.clone().negate(), null, !1, !1, !0)
-
- this.loadPromise = this.pano.hasVideo ? promise2 : promise1;
- //this.loadPromise = promise1;
-
- }
- */
- fitPanoTowardPoint(o){ //寻找最适合的点位
- var point = o.point, //相机最佳位置
- target = o.target, //实际要看的位置
- require = o.require || [],
- rank = o.rank || [],
- force = o.force,
- getAll = o.getAll,
- bestDistance = o.bestDistance || 0;
- let camera = viewer.scene.getActiveCamera();
- if(target){
- var vec = new Vector3().subVectors(target,point).normalize();
- }
-
-
- //if(o.floor)require.push(Panorama.filters.atFloor(o.floor))
-
-
- if(o.boundSphere){//只接受boundSphere
- let aspect = 1;//size.x / size.y
- let dis;
- if(camera.aspect > aspect){//视野更宽则用bound的纵向来决定
- dis = /* size.y */o.boundSphere.radius/* / 2 *// MathUtils.degToRad(camera.fov / 2);
- }else {
- let hfov = cameraLight$1.getHFOVForCamera(camera , true );
- dis = /* size.x */ o.boundSphere.radius /* / 2 */ / (hfov / 2);
- }
-
- bestDistance = dis;//*0.8
-
- }
-
-
- let bestDisSquared = bestDistance * bestDistance;
- rank.push((pano)=>{
- let dis1 = Math.abs(pano.position.distanceToSquared(point) - bestDisSquared); //距离最佳位置
- if(!target){
- return -dis1
- }else {
- let dis2 = pano.position.distanceToSquared(target); //距离目标点
- let vec2 = new Vector3().subVectors(target,pano.position).normalize();
- let cos = vec.dot(vec2);
- let result = (- dis1 - Math.pow(dis2 , 1.5)) / (cos + 2); // cos+2是为了调整到1-3, 尽量贴近最佳位置的角度;
- //console.log(pano.id, dis1,dis2, cos, result)
- return result
- }
- },(pano)=>{
- if(pano.depthTex && o.checkIntersect){ //没加载好的话,不管了
- let intersect = viewer.inputHandler.ifBlockedByIntersect(target, 0.1 , null, null, null, pano);
- if(intersect){
- //console.log('intersected', pano.id )
- return -10000
- }else return 0
- }else return 0
- });
-
- /* var temp = {position:point}
- rank.push(Panorama.scoreFunctions.distanceSquared(temp, -2)); */
-
- var g = Common.sortByScore(this.panos, require, rank);
-
- if(getAll)return g;
- return g && g.length > 0 && g[0].item
-
- }
- //---------------scroll zoom ------------------------------------------
-
-
- /* zoomIn = function() { //放大
- this.zoomBy(1 + this.zoomSpeed);
- }
- zoomOut = function() {//缩小
- this.zoomBy(1 - this.zoomSpeed);
- } */
- zoomBy(e, pointer) {//以倍数
- this.zoomTo(this.zoomLevel * e, pointer);
- }
- zoomTo(zoomLevel, pointer) {//缩放到某绝对zoomLevel
- let zoom = Potree.settings.zoom;
- if (zoom.enabled) {
-
-
- zoomLevel = MathUtils.clamp(zoomLevel, zoom.min, zoom.max);
- //console.log(zoomLevel)
-
- if(zoomLevel == this.zoomLevel) return;
-
- /* if (zoomLevel > this.zoomLevel) {
- this.emit(ZoomEvents.ZoomIn);
- zoomLevel === settings.zoom.max && this.emit(ZoomEvents.ZoomMax);
- } else if (zoomLevel < this.zoomLevel) {
- this.emit(ZoomEvents.ZoomOut);
- zoomLevel === settings.zoom.min && this.emit(ZoomEvents.ZoomMin);
- } */
-
- this.zoomLevel = zoomLevel;
- //定点缩放:使当前鼠标所在的位置缩放后不变
- let view = viewer.scene.view;
- let originDir = viewer.scene.view.direction;
- let oldPointerDir = viewer.inputHandler.getMouseDirection(pointer).direction;
- viewer.setFOV(Potree.config.view.fov * (1 / this.zoomLevel));
- let newPointerDir = viewer.inputHandler.getMouseDirection(pointer).direction;
-
- view.direction = oldPointerDir; //获取一下鼠标所在位置的yaw 和 pitch
- let oldPitch = view.pitch, oldYaw = view.yaw;
- view.direction = newPointerDir;
- let newPitch = view.pitch, newYaw = view.yaw;
-
- view.direction = originDir; //还原
- viewer.scene.view.pitch -= newPitch - oldPitch;
- viewer.scene.view.yaw -= newYaw - oldYaw;
- }
- }
-
-
-
-
- zoomFovTo( fov ) { //通过fov来算zoomLevel
- let zoomLevel = this.baseFov / fov;
- this.zoomTo( zoomLevel );
- }
- smoothZoomTo(aimLevel, dur=0) {
- var currentLevel = this.zoomLevel;
- if(currentLevel == aimLevel)return;
-
- var fun = (progress)=>{
- //progress > 1 && (progress = 1)
- let level = currentLevel * (1 - progress) + aimLevel * progress;
- this.zoomTo(level, !0);
- };
-
-
- transitions.start(fun, dur, null, null, 0 , easing['easeInOutQuad'] );
- }
-
-
- /* zoomDefault{
- this.zoomTo(1, !0)
- }
- smoothZoomToDefault(e, t) {
- var i, n = this.zoomLevel,
- r = function(e) {
- e > 1 && (e = 1),
- i = n * (1 - e) + e,
- this.zoomTo(i, !0)
- }
- .bind(this),
- o = function() {
- this.zoomDefault(),
- t && window.setTimeout(t, 50)
- }
- .bind(this);
- transitions.start(r, e, o, null, 0, easing[settings.transition.blendEasing])
- } */
-
- updateZoomPano() {
- if (!this.panoRenderer.zoomPanoRenderingDisabled && Potree.settings.displayMode == 'showPanos') {
- var currentPano = this.currentPano;
- if (currentPano) {
- var activationThreshold = Potree.settings.navTileClass == '2k' && Potree.settings.tileClass == '4k' ? 1.7 : Potree.settings.zoom.activationThreshold;//1.1
- var t = this.zoomLevel > activationThreshold,
- n = !(this.flying && this.nextPano && this.nextPano !== this.currentPano),
- r = t && n;
- this.tileDownloader.tilePrioritizer.setZoomingActive(r);
- this.panoRenderer.setZoomingActive(r, currentPano, !0);
-
- var o = (pano, zoomedFlag)=>{
- this.panoRenderer.resetRenderStatus(pano.id, !1, !0, this.qualityManager.getMaxNavPanoSize());
- this.panoRenderer.clearAllQueuedUploadsForPano(pano.id);
- this.panoRenderer.renderPanoTiles(pano.id, null, !1, !1);
- pano.setZoomed(zoomedFlag);
- };
-
-
- if (r && (!currentPano.zoomed || this.qualityManager.zoomLevelResolution && this.qualityManager.zoomLevelResolution != '4k')) {
- currentPano.zoomed || o(currentPano, !0);
-
- if(Potree.settings.navTileClass == '1k' && Potree.settings.tileClass != '1k' && this.zoomLevel < 2){
- this.panoRenderer.enableHighQuality( function() {//开启2k
- if(Potree.settings.tileClass != '4k') o(currentPano, !0);
- }.bind(this));
- }else {
- this.panoRenderer.enableUltraHighQualityMode(function() {//开启4k getMaxZoomPanoSize
- this.qualityManager.useUltraHighResolutionPanos && (Potree.settings.zoom.max = Potree.config.ultraHighQualityMaxZoom);
- o(currentPano, !0);
- }.bind(this));
- }
- } else {
- !t && currentPano.zoomed && o(currentPano, !1);
- }
-
-
-
- if(r && Potree.settings.navTileClass == '1k' && Potree.settings.tileClass == '4k' ){ //目前只有手机端navTileClass == '1k'
- var change = (zoomedFlag)=>{
- this.qualityManager.updateMaximums();//更新maxZoomPanoSize
- this.panoRenderer.setupZoomRenderTarget(); //更新renderTarget
- //currentPano.setZoomed(t);//更新uniforms贴图
- };
- this.qualityManager.zoomLevelResolution = this.zoomLevel >= 2 ? '4k' : this.zoomLevel > 1.1 ? '2k' : '1k';
-
- if(this.oldZoomLevel < 2 && this.zoomLevel >= 2){//1k/2k-4k
- change();
- o(currentPano, t);
- }else if(this.oldZoomLevel <= Potree.settings.zoom.activationThreshold && this.zoomLevel > Potree.settings.zoom.activationThreshold){//1k-2k
- change();
- }else if(this.oldZoomLevel > 2 && this.zoomLevel <= 2){//4k-2k/1k
- change();
- o(currentPano, t);
- }else if(this.oldZoomLevel > Potree.settings.zoom.activationThreshold && this.zoomLevel <= Potree.settings.zoom.activationThreshold){//2k-1k
- change();
- }
- this.oldZoomLevel = this.zoomLevel;
- }
- }
- }
- }
- //--------------------
- addHighMapCube(){//创建8*8的tile cube 主要因手机版崩溃 要在电脑端测试得设置maxRenderTargetSize
-
- if( Potree.settings.tileClass == '4k' && this.qualityManager.maxRenderTargetSize == 2048){
-
- var geo = new PlaneGeometry(1, 1, 1, 1);
- var cube = new Object3D;
- for(var cubeIndex=0; cubeIndex<6; cubeIndex++){
- var face = new Object3D;
- for(var i=0;i<8;i++){
- for(var j=0;j<8;j++){
- var tile = new Mesh(geo, new MeshBasicMaterial({
- //side:THREE.DoubleSide
- }));
- tile.position.set(i-3.5, j-3.5, -4);
-
- tile.material.opacity = 0.4;
- tile.material.transparent = true;
-
-
- if(Potree.settings.isTest){
- var colorHue = Math.random();
- tile.material.color = (new Color()).setHSL(colorHue, 0.5, 0.9);
- }
-
- tile.visible = false;
- face.add(tile);
- }
- }
- switch(cubeIndex){
- case GLCubeFaces$1.GL_TEXTURE_CUBE_MAP_POSITIVE_X:
- face.rotation.set(0,Math.PI/2,0);
- break;
- case GLCubeFaces$1.GL_TEXTURE_CUBE_MAP_NEGATIVE_X:
- face.rotation.set(0,-Math.PI/2,0);
- break;
- case GLCubeFaces$1.GL_TEXTURE_CUBE_MAP_POSITIVE_Y:
- var rot1 = new Quaternion().setFromAxisAngle(new Vector3(0,1,0),Math.PI);
- var rot2 = new Quaternion().setFromAxisAngle(new Vector3(1,0,0),Math.PI/2);
-
- face.quaternion.copy(rot1).multiply(rot2);
- //face.rotation.set(Math.PI/2,0,0);
- break;
- case GLCubeFaces$1.GL_TEXTURE_CUBE_MAP_NEGATIVE_Y:
- //face.rotation.set(-Math.PI/2,0,0);
- var rot1 = new Quaternion().setFromAxisAngle(new Vector3(0,1,0),Math.PI);
- var rot2 = new Quaternion().setFromAxisAngle(new Vector3(1,0,0),-Math.PI/2);
-
- face.quaternion.copy(rot1).multiply(rot2);
- break;
- case GLCubeFaces$1.GL_TEXTURE_CUBE_MAP_POSITIVE_Z:
- face.rotation.set(0,Math.PI,0);
- break;
- case GLCubeFaces$1.GL_TEXTURE_CUBE_MAP_NEGATIVE_Z:
- face.rotation.set(0,0,0);
-
- }
- face.scale.set(1,-1,1);
- cube.add(face);
- }
- cube.name = 'highMapCube';
- this.highMapCube = cube;
- viewer.scene.scene.add(cube);
- //cube.scale.set(0.01,0.01,0.01)
-
- this.highMapCube.visible = false;
- viewer.setObjectLayers(this.highMapCube, 'sceneObjects'/* 'skybox' */); //如果是skybox层,点云可见时会被遮住,depthTest为false呢? 但不会遮住场景物体
- //console.warn('addHighMapCube')
-
- }
-
- }
-
-
- isHighMapLoaded( cubeFace, tileX, tileY){
- var tile = this.highMapCube.children[cubeFace].children[tileX*8+tileY];
- return !!tile.material.map
- }
-
-
- updateHighMap(tex, cubeFace, tileX, tileY){
- //console.warn('updateHighMap')
- var tile = this.highMapCube.children[cubeFace].children[tileX*8+tileY];
-
- tile.material.map = tex;
-
- tile.material.opacity = 1;
- tile.material.transparent = false;
-
-
- tile.visible = true;
- tile.material.needsUpdate = true; //发现每次开始放大但还未放大到4k时也会把之前加载过的4k加载
- }
-
-
-
- resetHighMap(){
-
- if(!this.highMapCube) return
- //console.warn('resetHighMap')
- this.highMapCube.children.forEach(e=>e.children.forEach(tile=>{
- if(tile.material.map){
- tile.material.map.dispose();
- tile.material.map.loaded = !1;
- tile.material.map.version = 0;
-
- var h = viewer.renderer.properties.get(tile.material.map);
- viewer.renderer.getContext().deleteTexture(h.__webglTexture);
- //类似app.sceneRenderer.deallocateCubeTexture(tile.material.map)
-
- tile.material.map = null;
- /* tile.material.opacity = 0.4;
- tile.material.transparent = true */
- tile.material.needsUpdate = true;
- tile.visible = false;
- }
- }));
- this.highMapCube.visible = false;
- }
-
- setHighMap(pano){
- if(!this.highMapCube) return
- this.highMapCube.position.copy(pano.position);
-
- //可以利用第0个pano查看,其 rotation4dkk是(_x: 0, _y: -1.5707963267948966, _z: 0 )而手动旋转至(_x:1.5707963, _y: -1.57079, _z: 0)时才正确,说明要在4dkk的旋转基础上,绕x轴转90度,(也就是转成navvis坐标系), 然后得到YupToZup的函数写法的
-
- this.highMapCube.quaternion.copy( math.convertQuaternion.YupToZup( pano.quaternion4dkk ) );
-
-
- //乘上数据集整体的旋转:
- let modelRotQua = new Quaternion().setFromRotationMatrix(pano.pointcloud.rotateMatrix);
- this.highMapCube.quaternion.premultiply(modelRotQua);
-
-
- }
-
- showHighMap(){
- if(!this.highMapCube) return
- //console.warn('showHighMap')
- this.highMapCube.visible = true;
- }
- hideHighMap(){
- if(!this.highMapCube) return
- //console.warn('hideHighMap')
- this.highMapCube.visible = false;
- }
- //缩小后继续显示cube呢还是不显示? 不显示的话,就要把cube上的复制到renderTarget上……会不会又崩溃,or没加载的显示???
-
-
- addPanoData(data, datasetId ){
- //data[0].file_id = '00019'
-
- if(data.data) data = data.data;
- if(data.length == 0)console.error(datasetId + ' 没有漫游点');
- //data = data.sort(function(a,b){return a.id-b.id})
-
- data.forEach((info)=>{
- //if(Potree.fileServer){
- info.id = this.panos.length; //把info的id的一长串数字改简单点
- //}
- let pano = new Panorama( info, this );
-
- /* pano.mesh.layers.set(Potree.config.renderLayers.marker)
- pano.marker.layers.set(Potree.config.renderLayers.marker) */
-
-
- pano.addEventListener('dispose',(e)=>{
- if(this.closestPano == pano) this.closestPano = null;
- });
-
- this.panos.push(pano);
- if(Potree.settings.editType == 'pano'){
- Potree.settings.datasetsPanos[datasetId].panos.push(pano);
- }
- });
-
-
- }
-
-
- loadDone(){
- viewer.setObjectLayers(this.node, 'sceneObjects');
- //this.updateCube(/* viewer.bound */)
-
- this.panos.forEach(e=>{
- e.label && viewer.setObjectLayers(e.label, 'bothMapAndScene');
- });
-
- this.tileDownloader.setPanoData(this.panos, [] /* , Potree.settings.number */);
- {
- /* var panosBound = new THREE.Box3
- this.panos.forEach(pano=>{
- panosBound.expandByPoint(pano.position)
- })
- let center = panosBound.getCenter(new THREE.Vector3)
- let minBound = (new THREE.Box3()).setFromCenterAndSize(center, new THREE.Vector3(1,1,1))
- panosBound.union(minBound)
-
- this.bound = {
- bounding:panosBound,
- size: panosBound.getSize(new THREE.Vector3),
- center,
- } */
-
- let minSize = new Vector3(1,1,1);
- this.bound = math.getBoundByPoints(this.panos.map(e=>e.position), minSize);
-
-
- viewer.scene.pointclouds.forEach(pointcloud=>pointcloud.getPanosBound());
-
-
-
- }
-
-
-
- if(viewer.scene.pointclouds.some(e=>e.panos.length == 0)){
- //console.warn('存在数据集没有pano');
- viewer.hasNoPanoDataset = true;
- }
- }
-
- getPano(value, typeName='id'){ //默认找的是id,也可以是sid、uuid
- return this.panos.find(p=>p[typeName] == value)
- }
- };
-
- //判断当前点是否加载了全景图
- Images360.prototype.checkAndWaitForPanoLoad = function() {
- var isLoadedPanos = {},
- LoadedTimePanos = {},
- loadedCallback = {}, //add
- maxTime = 5e3;
- var withinTime = function() {
- for (var panoId in isLoadedPanos)
- if (isLoadedPanos.hasOwnProperty(panoId) && isLoadedPanos[panoId]) {
- var differTime = performance.now() - LoadedTimePanos[panoId];
- if (differTime < maxTime)
- return !0
- }
- return !1
- };
-
- return function(pano, basePanoSize, doneFun1, doneFun2, progressCallback, iswait, isclear, p ) {
- loadedCallback[pano.id] = doneFun1;//add 因为有可能之前请求的没加doneFun1, 如果加载好就执行最新的doneFun1
-
- if (withinTime()){//距离上次请求时间很近
-
- return !0; //这里感觉应该是!1
- }
-
-
-
- var callback1 = (param1, param2)=>{
- setTimeout(()=>{
- isLoadedPanos[pano.id] = !1;
- loadedCallback[pano.id] && loadedCallback[pano.id](param1, param2);
- },1);
- };
- var callback2 = (param)=>{//没有看到有传doneFun2的
- setTimeout(()=>{
- isLoadedPanos[pano.id] = !1;
- doneFun2 && doneFun2(param);
- },1);
- };
- try {
- null !== iswait && void 0 !== iswait || (iswait = !0);
-
- isLoadedPanos[pano.id] = this.checkAndWaitForTiledPanoLoad(pano, basePanoSize, callback1, callback2, progressCallback, iswait, isclear, p );
- //true代表没加载好
- isLoadedPanos[pano.id] && (LoadedTimePanos[pano.id] = performance.now());
- return isLoadedPanos[pano.id];
- } catch (msg) {
- isLoadedPanos[pano.id] = !1;
- LoadedTimePanos[pano.id] = performance.now() - maxTime;
- throw msg;
- }
- }
- }();
- Images360.filters = {
- inPanoDirection : function(pos, dir, i) {
- return function(pano) {
- var r = pano.floorPosition.clone().sub(pos).normalize();
- var o = pano.position.clone().sub(pos).normalize();
- return r.dot(dir) > i || o.dot(dir) > i
-
-
- }
- },
- inFloorDirection: function(pos, e, o) {//许钟文 改 for鱼眼
- return function(n) {
- var i = n.floorPosition.clone().sub(pos).setZ(0).normalize();//改成在xz方向上,否则点击墙面不会移动
- return i.dot(e) > o
- }
- },
- isNotBehindNormal: function(e, t) {
- var i = new Vector3;
- return t = t.clone(),
- function(n) {
- var r = i.copy(n.position).sub(e).normalize();
- return r.dot(t) > 0
- }
- },
- isCloseEnoughTo: function(e, t) {
- return function(i) {//因为marker可能比地面高,所以识别范围要比marker看起来更近一些。(因为投影到地板的位置比marker更近)
- return e.distanceTo(i.floorPosition) < t //许钟文
- }
- },
-
- not: function(e) {
- return function(t) {
- return t !== e
- }
- } ,
- isEnabled:function() {
- return function(t) {
- return t.enabled
- }
- },
- isVisible:function() {
- return function(t) {
- return t.visible
- }
- }
- };
- Images360.scoreFunctions = {
- direction: function(curPos, dir, ifLog) {
- return function(pano) {
- var pos1 = /* pano.floorPosition */ pano.position; //旧:改为权重放在marker上,这样对有斜坡的更准确,如上楼, 但这样近距离的pano角度就会向下了,以致于走不到
- var n = pos1.clone().sub(curPos).normalize();
- //ifLog && console.log('direction', pano.id, n.dot(dir) * directionFactor )
- return n.dot(dir) * directionFactor
- }
-
- },
-
- distanceSquared: function(pos1, r, ifLog) {
- if(pos1.position)pos1 = pos1.position;
- return function(pano) {//许钟文 改
- var pos2 = pano.position.clone();
- //ifLog && console.log('distanceSquared', pano.id, pos1.distanceToSquared(pos2) * -1 )
- return pos1.distanceToSquared(pos2) * -1 * r;
- }
- },
- angle: function(e, t) {
- return function(i) {
- var n = i.position.clone().sub(e).normalize();
- return n.angleTo(t) * Potree.config.navigation.angleFactor
- }
- },
- };
- Images360.sortFunctions = {//排序函数,涉及到两个item相减
- floorDisSquaredToPoint: function(e) {
- return function(t, i) {
- return t.floorPosition.distanceToSquared(e) - i.floorPosition.distanceToSquared(e)
- }
- },
- disSquaredToPoint: function(e) {
- return function(t, i) {
- return t.position.distanceToSquared(e) - i.position.distanceToSquared(e)
- }
- },
-
- };
- /* var centerCross = new THREE.Mesh(new THREE.SphereGeometry(1, 4, 4),new THREE.MeshBasicMaterial({
- transparent:true, color:"#ff0000", opacity:0.5
- })); */
- /* const mapHeight = -1000;//要比点云低。最低
- const cameraHeight = 1000; //最高 */
- const panosHeight = config$1.map.mapHeight + 100; //要比点云低 (marker)
- const cursorHeight = 0;//比地图高就行
- const routeLayerHeight = config$1.map.mapHeight + 105;
- const texLoader$4 = new TextureLoader();
- const planeGeo$2 = new PlaneBufferGeometry(1,1);
- const markerSize = 1;
- var initCameraFeildWidth = 50;
- var panoMarkerMats;
-
- class MapViewer extends ViewerBase{
- constructor(dom){
- super(dom, {
- clearColor: Potree.config.mapBG,
- name: 'mapViewer'
- });
- this.visible = true;
- this.initScene();
-
-
- this.mapLayer = new MapLayer(this, this.viewports[0]);
- this.scene.add(this.mapLayer.sceneGroup);
- this.mapLayer.sceneGroup.position.setZ(Potree.config.map.mapHeight);
- this.mapRatio = 0.5;
- this.splitDir = 'leftRight';
-
-
- //this.scene.add(centerCross)
-
-
- viewer.addEventListener("camera_changed", (e)=>{
- if(e.viewport == viewer.mainViewport) this.updateCursor();
- else this.updateWhenAtViewer();
- });
-
-
- //viewer.addEventListener("global_mousemove", this.updateWhenAtViewer.bind(this)) //鼠标移动时reticule也动,所以直接就needRender
- viewer.reticule.addEventListener('update',(e)=>{
- if(this.attachedToViewer)this.needRender = true;
- });
-
-
- viewer.scene.addEventListener("360_images_added", this.addPanos.bind(this));
-
-
- viewer.addEventListener("loadPointCloudDone", this.initProjection.bind(this));
-
- this.addEventListener('global_click',(e)=>{
- if(!e.isTouch && e.button != MOUSE.LEFT)return
- this.updateClosestPano(e.intersectPoint);
- });
-
-
-
- this.addEventListener('add',(e)=>{//添加其他mesh
- this.scene.add(e.object);
- if(e.name == 'route'){
- e.object.position.z = routeLayerHeight;
- }
- viewer.setObjectLayers(e.object, 'mapObjects' );
- });
-
-
-
-
-
- if(!Potree.settings.isOfficial){
- let domRoot = viewer.renderer.domElement.parentElement;
- let elAttach = $("<input type='button' value='map绑定'></input>");
- elAttach.css({
- position : "absolute",
- right : '80%',
- bottom: '20px',
- zIndex: "10000",
- fontSize:'1em', color:"black",
- background:'rgba(255,255,255,0.8)',
- });
- let state = false;
- elAttach.on("click", () => {
- state = !state;
- this.attachToMainViewer(state, 'measure');
- elAttach.val(state ? 'map分离':'map绑定');
- });
- domRoot.appendChild(elAttach[0]);
-
-
- }
-
- }
-
-
-
-
-
- waitLoadDone(callback){//确保加载完后执行
- if(this.mapLayer.loadingInProgress == 0){
- callback();
- }else {
- var f = ()=>{
- callback();
- this.mapLayer.removeEventListener('loadDone', f);
- };
- this.mapLayer.addEventListener('loadDone', f);
- }
-
- }
-
- addListener(images360){
- images360.addEventListener('flyToPano',e=>{
- let toPano = e.toPano;
- if(toPano.dontMoveMap) return
-
- /* transitions.start(lerp.vector(this.view.position, toPano.pano.position.clone().setZ(cameraHeight),
-
- (pos, progress)=>{
-
- }), toPano.duration, null, 0, easing[toPano.easeName] ); */
- let boundSize;// = new THREE.Vector2(10,10)
-
- this.moveTo(toPano.pano.position.clone().setZ(Potree.config.map.cameraHeight), boundSize, toPano.duration, toPano.easeName);
- });
-
-
- }
-
-
-
- initProjection(){
- this.started = true;
- this.mapLayer.initProjection();
-
- }
-
- initScene(){
- let w = initCameraFeildWidth;
- let width = this.renderArea.clientWidth;
- let height = this.renderArea.clientHeight;
- //let aspect = width / height
- this.camera = new OrthographicCamera(-width/2,width/2,height/2,-height/2/* -w/2, w/2, w/2/aspect, -w/2/aspect */, 0.01, 10000);
- this.camera.zoom = width / w; //zoom越大视野越小
- //this.camera.position.set(0,0,10);
- this.camera.up.set(0,0,1);
- //this.camera.lookAt(new THREE.Vector3())
- //this.camera.updateMatrixWorld()
-
- this.view = new View();
- this.view.position.set(0,0,Potree.config.map.cameraHeight);
- this.view.lookAt(0,0,0);
- this.setViewLimit('standard');
-
- let viewport = new Viewport( this.view, this.camera, {
- left:0, bottom:0, width:1, height: 1, name:'mapViewport'
- });
- viewport.axis = ["x","y"];
- viewport.axisSign = [1,1];
-
- viewport.noPointcloud = true; //不要渲染点云
- viewport.render = this.render.bind(this);//标志给mainView渲染
- //viewport.unableDepth = true //depthBasicMaterial等在此viewport中不开启depth
-
-
-
- this.viewports = [viewport];
-
-
-
-
- this.controls = new FirstPersonControls(this, this.viewports[0]);
- this.controls.setEnable(true);
- this.scene = new Scene();
-
-
-
- this.inputHandler = new InputHandler(this, this.scene);
- this.inputHandler.name = 'mapInputHandler';
- //this.inputHandler.addInputListener(this.controls);
- this.inputHandler.registerInteractiveScene(this.scene);//interactiveScenes
-
- this.viewports[0].interactiveScenes = this.inputHandler.interactiveScenes;//供viewer的inputHandler使用
-
- var cursor = new Mesh(planeGeo$2, new MeshBasicMaterial({
- transparent:true,
- opacity:0.9,
- depthTest : false, //防止透明冲突
- map: texLoader$4.load(Potree.resourcePath+'/textures/pic_location128.png' )
- }));
- cursor.position.set(0,0,cursorHeight);
-
-
- this.cursor = cursor;
-
- this.scene.add(cursor);
- viewer.setObjectLayers(this.scene, 'mapObjects' );
- }
-
- setViewLimit(state){//设置边界,当编辑空间模型等时要解禁
- let setting = Potree.config.OrthoCameraLimit[state];
- if(setting){
- this.view.limitBound = new Box3().copy(setting.posBound);
- this.camera.zoomLimit = $.extend({},setting.zoom);
- }else {
- this.view.limitBound = null;
- this.camera.zoomLimit = null;
- }
- }
-
- updateCursor(){
-
- var scale = math.getScaleForConstantSize( {//规定下最小最大像素
- minSize : 80, maxSize : 200, nearBound : initCameraFeildWidth*0.1 , farBound : initCameraFeildWidth*2,
- camera:this.camera , position: this.cursor.getWorldPosition(new Vector3()),
- resolution: this.viewports[0].resolution//2
- });
- this.cursor.scale.set(scale, scale, scale);//当地图缩放时
- this.cursor.position.copy(viewer.mainViewport.camera.position).setZ(cursorHeight);//当场景镜头旋转移动时
- this.cursor.rotation.z = viewer.mainViewport.view.yaw;
- this.needRender = true;
- }
-
- addPanos(e){
- var panosGroup = new Object3D;
- panoMarkerMats = {
- default: new MeshBasicMaterial({
- transparent:true,
- opacity: 0.5,
- map: texLoader$4.load(Potree.resourcePath+'/textures/map_marker.png' ),
- }),
- selected: new MeshBasicMaterial({
- transparent:true,
- opacity: 1,
- map: texLoader$4.load(Potree.resourcePath+'/textures/map_marker.png' ),
- })
- };
-
-
- e.images.panos.forEach(pano=>{
- pano.mapMarker = new Mesh(planeGeo$2, panoMarkerMats.default);
- pano.mapMarker.position.copy(pano.position).setZ(0);
- pano.mapMarker.scale.set(markerSize,markerSize,markerSize);
- pano.mapMarker.name = 'mapMarker';
- panosGroup.add(pano.mapMarker);
-
-
- let mouseover = (e)=>{
- if(!e.byMap){
- pano.mapMarker.material = panoMarkerMats.selected;
- if(!e.byMainView) pano.dispatchEvent({type: "hoverOn", byMap:true});
- this.needRender = true;
- }
- };
-
- let mouseleave = (e)=>{
- if(!e.byMap){
- pano.mapMarker.material = panoMarkerMats.default;
- if(!e.byMainView) pano.dispatchEvent({type: "hoverOff", byMap:true});
- this.needRender = true;
- }
- };
-
-
- pano.mapMarker.addEventListener('mouseover', mouseover);
- pano.mapMarker.addEventListener('mouseleave', mouseleave);
-
- pano.addEventListener('hoverOn', mouseover);
- pano.addEventListener('hoverOff', mouseleave);
-
- let onclick = (e)=>{
- viewer.images360.flyToPano(pano);
- };
- pano.mapMarker.addEventListener('click', onclick);
-
- pano.addEventListener('isVisible',(e)=>{//是否显示该点的mesh(不显示也能走)
- //console.log('panoMarker isVisible', pano.id, e.visible)
- viewer.updateVisible(pano.mapMarker, 'panoVisible', e.visible );
- this.needRender = true;
-
- });
- pano.addEventListener('rePos',(e)=>{
- pano.mapMarker.position.copy(pano.position).setZ(0);
- });
- });
- this.scene.add(panosGroup);
- panosGroup.position.z = panosHeight;
- this.panosGroup = panosGroup;
- viewer.setObjectLayers(panosGroup, 'mapObjects' );
-
-
- /* e.images.on('markersDisplayChange', (show)=>{
- panosGroup.visible = show
- this.needRender = true
- }) */
-
- //-------
-
- //this.fitPanosToViewport()
- this.initFitView();
- }
-
-
-
- updateClosestPano(intersect){
- if(viewer.images360.flying)return;
- intersect = intersect && intersect.orthoIntersect;
- if(!intersect)return
-
- intersect = intersect.clone().setZ(0);
-
- const minDis = 20; //距离鼠标不能太远
-
- var filterFuncs = [
- (pano)=>{
- return pano.position.clone().setZ(0).distanceTo(intersect) < minDis
- },
- Images360.filters.isEnabled(),
-
- Images360.filters.isVisible(),//只走显示的点,否则会走到别的层
- ];
-
-
-
-
-
-
- var pano = Common.find(viewer.images360.panos, filterFuncs, [Images360.sortFunctions.floorDisSquaredToPoint(intersect)]);
- if (pano && pano != viewer.images360.currentPano ) {
- viewer.images360.flyToPano(pano);
-
- }
-
- }
-
-
- fitPanosToViewport(){//使所有漫游点占满viewport
-
- //var w = viewer.bound.boundSize.x;
- var boundSize = viewer.images360.bound.size.clone().multiplyScalar(1.1);
- boundSize.max(new Vector3(4,4,4));
- let endPosition = viewer.images360.bound.center.clone();
- this.moveTo(endPosition, boundSize, 0);
- }
- fitToPointcloud(pointcloud, duration=400){
- var boundSize = pointcloud.bound.getSize(new Vector3);/* .multiplyScalar(1.1); */
- boundSize.max(new Vector3(4,4,4));
- let endPosition = pointcloud.bound.getCenter(new Vector3);
- this.moveTo(endPosition, boundSize, duration); //给点duration去变化 否则地图放大后所占的还是很小
- }
- initFitView(){
- let dis = 5 , px = 70; //地图上px像素长度代表的距离为dis //px是手动缩放到5m后发现是这个长度
- let zoom = px / dis;
- this.camera.zoom = zoom;
- this.moveTo(viewer.images360.position/* viewer.images360.bound.center */);
- this.camera.updateProjectionMatrix();
- }
-
- fitToDatasets(datasets){
- let bound = new Box3;
- datasets.forEach(e=>bound.union(e.bound));
- let center = bound.getCenter(new Vector3);
- let size = bound.getSize(new Vector3);
-
- this.moveTo(center, size, 200 ); //给duration是为了顺应视口大小改变,缓冲
- }
-
- moveTo(endPosition, boundSize, duration=0, easeName){//前两个参数有xy即可
- endPosition = new Vector3(endPosition.x,endPosition.y,Potree.config.map.cameraHeight);
- this.view.moveOrthoCamera(this.viewports[0], {endPosition, boundSize }, duration, easeName);
-
-
-
-
- /* let endZoom, startZoom = this.camera.zoom
-
- //修改相机为bound中心,这样能看到全部(宽度范围内)
-
- this.view.setView({ position:endPosition, duration,
- callback:()=>{//done
-
- },
- onUpdate:(progress)=>{
- if(boundSize){
- let aspect = boundSize.x / boundSize.y
- let w, h;
-
- if(this.camera.aspect > aspect){//视野更宽则用bound的纵向来决定
- h = boundSize.y
- //w = h * this.camera.aspect
- endZoom = this.viewports[0].resolution.y / h
- }else{
- w = boundSize.x;
- //h = w / this.camera.aspect
- endZoom = this.viewports[0].resolution.x / w
- }
- //onUpdate时更新endzoom是因为画布大小可能更改
-
-
- this.camera.zoom = endZoom * progress + startZoom * (1 - progress)
- this.camera.updateProjectionMatrix()
- }
- },
-
- Easing:easeName
-
- }) */
-
-
- }
-
-
-
-
- updateWhenAtViewer(e){
- if(this.attachedToViewer){
- if(this.started) this.mapLayer.update();
-
- this.updateCursor();
- this.needRender = true;
- }
- }
-
- update(delta, areaSize ){
- if(!this.visible && !this.attachedToViewer )return
-
-
- if(this.attachedToViewer){
- if(this.mapLayer.needUpdate){//必须更新。(较少触发)
- this.updateWhenAtViewer();
- }
- return
- }
-
-
- this.updateScreenSize();
-
- this.controls.update(delta);
- this.view.applyToCamera(this.camera);
-
-
- let changed = this.cameraChanged();
-
-
- if(this.started && (changed || this.mapLayer.needUpdate))this.mapLayer.update();
-
- if(changed /*|| || this.needRender */){
- this.dispatchEvent({
- type: "camera_changed",
- camera: this.camera,
- viewport : this.viewports[0]
- });
-
- this.needRender = true;
- this.updateCursor();//更改大小
- }
- this.render();
-
- }
-
- attachToMainViewer(state, desc, mapRatio=0.5, options={}){//转移到viewer中。测量时展示or截图需要
-
- if(!Potree.settings.isOfficial)this.renderArea.style.display = state ? 'none':'block';
-
- if(state){
- this.enabledOld = this.enabled;
- this.enabled = true;
-
- if(mapRatio != 'dontSet'){
- this.changeSplitScreenDir(options.dir, mapRatio);
-
- if(this.attachedToViewer){
- //this.fitPanosToViewport()
- viewer.updateScreenSize({forceUpdateSize:true});
- return
- }
- viewer.viewports = [viewer.mainViewport, viewer.mapViewer.viewports[0] ];//因为mainViewer的相机变化会触发map的变化,所以先渲染mainViewer
- }
-
-
- if(desc == 'measure') this.inputHandler.registerInteractiveScene(viewer.measuringTool.scene);//虽然用的是viewer的inputHandler,但借用了this.inputHandler的interactiveScenes
- else if(desc == 'split4Screens') {
- this.inputHandler.registerInteractiveScene(viewer.scene.scene);
- }
-
- }else {
- if(!this.attachedToViewer)return
-
- viewer.mainViewport.left = 0;
- viewer.mainViewport.bottom = 0;
- viewer.mainViewport.width = 1;
- viewer.mainViewport.height = 1;
-
- this.viewports[0].width = 1;
- this.viewports[0].bottom = 0;
- this.viewports[0].height = 1;
- this.viewports[0].left = 0;
-
- this.inputHandler.unregisterInteractiveScene(viewer.measuringTool.scene);
- this.inputHandler.unregisterInteractiveScene(viewer.scene.scene);
- viewer.viewports = [viewer.mainViewport];
- this.updateScreenSize({forceUpdateSize:true}); //更新相机projectionMatrix
- }
-
- //if(desc == 'measure')this.renderMeasure = state
- this.attachedToViewer = state;
-
-
-
-
-
- viewer.updateScreenSize({forceUpdateSize:true});
-
-
- //mapRatio != 'dontSet' && !options.dontFit && this.moveTo(...)//要写在updateScreenSize后面,因为要根据视图大小来fit
- if(options.moveToCurrentPos){
- let boundSize = new Vector2$1(10,10);
- let duration = 1000;
- this.moveTo(viewer.images360.position.clone(), boundSize, duration);
- }
- this.needRender = true;
- }
-
- changeSplitScreenDir(dir, mapRatio){//左右 | 上下
- //if(!dir || dir == this.dir)return
- if(dir )this.splitDir = dir;
- this.updateSplitSize(mapRatio);
- /* if(this.attachedToViewer){ //如果已经分屏了,中途修改方向的话……
- this.updateSplitSize()
- } */
- }
- updateSplitSize(mapRatio){
- if(mapRatio != void 0) this.mapRatio = mapRatio;
-
- //console.log(this.mapRatio)
-
- if(this.splitDir == 'leftRight'){//地图在左方
- viewer.mainViewport.left = this.mapRatio;
- viewer.mainViewport.width = 1-this.mapRatio;
- this.viewports[0].width = this.mapRatio;
-
- viewer.mainViewport.bottom = this.viewports[0].bottom = 0;
- viewer.mainViewport.height = this.viewports[0].height = 1;
-
- }else if(this.splitDir == 'upDown'){ //地图在下方
- viewer.mainViewport.bottom = this.mapRatio;
- viewer.mainViewport.height = 1-this.mapRatio;
- this.viewports[0].height = this.mapRatio;
-
- viewer.mainViewport.left = this.viewports[0].left = 0;
- viewer.mainViewport.width = this.viewports[0].width = 1;
-
- }
- if(this.attachedToViewer){
- viewer.updateScreenSize({forceUpdateSize:true});
- }
- }
-
- render(params={}){
-
- if(!this.visible && !this.attachedToViewer || !this.needRender && !params.force)return
-
- let renderer = params.renderer || this.renderer;
-
- if(params.target){
- renderer.setRenderTarget(params.target);
- }
- /* if(params.resize){
- this.emitResizeMsg(new THREE.Vector2(params.width,params.height, viewport:params.viewport))
- } */
- params.clear ? params.clear() : renderer.clear();
-
- if(!this.attachedToViewer){
- viewer.dispatchEvent({type: "render.begin", viewer: this, viewport:this.viewports[0], params });
- }
-
- viewer.setCameraLayers(this.camera, ['map','mapObjects' , 'bothMapAndScene' ]);
-
- if(this.mapGradientBG){//渲染背景
- viewer.scene.cameraBG.layers.set(Potree.config.renderLayers.bg);
- renderer.render(viewer.scene.scene, viewer.scene.cameraBG);
- }
-
- renderer.render(this.scene, this.camera);
- renderer.render(viewer.scene.scene, this.camera);
-
- //测量线等
- //params.renderOverlay && params.renderOverlay({camera:this.camera, isMap:true, viewport: this.viewports[0] })//其余如reticule 等场景层
- params.renderOverlay && params.renderOverlay( $.extend({}, params, { isMap:true }));
-
-
- renderer.setRenderTarget(null);
-
- this.needRender = false;
-
- }
-
-
- /* render(){
-
- let camera = viewer.scene.getActiveCamera();
- viewer.scene.view.position.x += 0.01
- viewer.setCameraLayers(camera, ['sceneObjects','marker','reticule','skybox' ])
- this.renderer.render(viewer.scene.scene, camera);
- } */
-
-
-
- }
- //本地调试地图白屏是因为代码自动更新了 但没刷新
- class CSVExporter {
- static toString (points) {
- let string = '';
- let attributes = Object.keys(points.data)
- .filter(a => a !== 'normal')
- .sort((a, b) => {
- if (a === 'position') return -1;
- if (b === 'position') return 1;
- if (a === 'rgba') return -1;
- if (b === 'rgba') return 1;
- });
- let headerValues = [];
- for (let attribute of attributes) {
- let itemSize = points.data[attribute].length / points.numPoints;
- if (attribute === 'position') {
- headerValues = headerValues.concat(['x', 'y', 'z']);
- } else if (attribute === 'rgba') {
- headerValues = headerValues.concat(['r', 'g', 'b', 'a']);
- } else if (itemSize > 1) {
- for (let i = 0; i < itemSize; i++) {
- headerValues.push(`${attribute}_${i}`);
- }
- } else {
- headerValues.push(attribute);
- }
- }
- string = headerValues.join(', ') + '\n';
- for (let i = 0; i < points.numPoints; i++) {
- let values = [];
- for (let attribute of attributes) {
- let itemSize = points.data[attribute].length / points.numPoints;
- let value = points.data[attribute]
- .subarray(itemSize * i, itemSize * i + itemSize)
- .join(', ');
- values.push(value);
- }
- string += values.join(', ') + '\n';
- }
- return string;
- }
- };
- class LASExporter {
- static toLAS (points) {
- // TODO Unused: let string = '';
- let boundingBox = points.boundingBox;
- let offset = boundingBox.min.clone();
- let diagonal = boundingBox.min.distanceTo(boundingBox.max);
- let scale = new Vector3(0.001, 0.001, 0.001);
- if (diagonal > 1000 * 1000) {
- scale = new Vector3(0.01, 0.01, 0.01);
- } else {
- scale = new Vector3(0.001, 0.001, 0.001);
- }
- let setString = function (string, offset, buffer) {
- let view = new Uint8Array(buffer);
- for (let i = 0; i < string.length; i++) {
- let charCode = string.charCodeAt(i);
- view[offset + i] = charCode;
- }
- };
- let buffer = new ArrayBuffer(227 + 28 * points.numPoints);
- let view = new DataView(buffer);
- let u8View = new Uint8Array(buffer);
- // let u16View = new Uint16Array(buffer);
- setString('LASF', 0, buffer);
- u8View[24] = 1;
- u8View[25] = 2;
- // system identifier o:26 l:32
- // generating software o:58 l:32
- setString('Potree 1.7', 58, buffer);
- // file creation day of year o:90 l:2
- // file creation year o:92 l:2
- // header size o:94 l:2
- view.setUint16(94, 227, true);
- // offset to point data o:96 l:4
- view.setUint32(96, 227, true);
- // number of letiable length records o:100 l:4
- // point data record format 104 1
- u8View[104] = 2;
- // point data record length 105 2
- view.setUint16(105, 28, true);
- // number of point records 107 4
- view.setUint32(107, points.numPoints, true);
- // number of points by return 111 20
- // x scale factor 131 8
- view.setFloat64(131, scale.x, true);
- // y scale factor 139 8
- view.setFloat64(139, scale.y, true);
- // z scale factor 147 8
- view.setFloat64(147, scale.z, true);
- // x offset 155 8
- view.setFloat64(155, offset.x, true);
- // y offset 163 8
- view.setFloat64(163, offset.y, true);
- // z offset 171 8
- view.setFloat64(171, offset.z, true);
- // max x 179 8
- view.setFloat64(179, boundingBox.max.x, true);
- // min x 187 8
- view.setFloat64(187, boundingBox.min.x, true);
- // max y 195 8
- view.setFloat64(195, boundingBox.max.y, true);
- // min y 203 8
- view.setFloat64(203, boundingBox.min.y, true);
- // max z 211 8
- view.setFloat64(211, boundingBox.max.z, true);
- // min z 219 8
- view.setFloat64(219, boundingBox.min.z, true);
- let boffset = 227;
- for (let i = 0; i < points.numPoints; i++) {
- let px = points.data.position[3 * i + 0];
- let py = points.data.position[3 * i + 1];
- let pz = points.data.position[3 * i + 2];
- let ux = parseInt((px - offset.x) / scale.x);
- let uy = parseInt((py - offset.y) / scale.y);
- let uz = parseInt((pz - offset.z) / scale.z);
- view.setUint32(boffset + 0, ux, true);
- view.setUint32(boffset + 4, uy, true);
- view.setUint32(boffset + 8, uz, true);
- if (points.data.intensity) {
- view.setUint16(boffset + 12, (points.data.intensity[i]), true);
- }
- let rt = 0;
- if (points.data.returnNumber) {
- rt += points.data.returnNumber[i];
- }
- if (points.data.numberOfReturns) {
- rt += (points.data.numberOfReturns[i] << 3);
- }
- view.setUint8(boffset + 14, rt);
- if (points.data.classification) {
- view.setUint8(boffset + 15, points.data.classification[i]);
- }
- // scan angle rank
- // user data
- // point source id
- if (points.data.pointSourceID) {
- view.setUint16(boffset + 18, points.data.pointSourceID[i]);
- }
- if (points.data.rgba) {
- let rgba = points.data.rgba;
- view.setUint16(boffset + 20, (rgba[4 * i + 0] * 255), true);
- view.setUint16(boffset + 22, (rgba[4 * i + 1] * 255), true);
- view.setUint16(boffset + 24, (rgba[4 * i + 2] * 255), true);
- }
- boffset += 28;
- }
- return buffer;
- }
-
- }
- function copyMaterial(source, target){
- for(let name of Object.keys(target.uniforms)){
- target.uniforms[name].value = source.uniforms[name].value;
- }
- target.gradientTexture = source.gradientTexture;
- target.visibleNodesTexture = source.visibleNodesTexture;
- target.classificationTexture = source.classificationTexture;
- target.matcapTexture = source.matcapTexture;
- target.activeAttributeName = source.activeAttributeName;
- target.ranges = source.ranges;
- //target.updateShaderSource();
- }
- class Batch{
- constructor(geometry, material){
- this.geometry = geometry;
- this.material = material;
- this.sceneNode = new Points(geometry, material);
- this.geometryNode = {
- estimatedSpacing: 1.0,
- geometry: geometry,
- };
- }
- getLevel(){
- return 0;
- }
- }
- class ProfileFakeOctree extends PointCloudTree{
- constructor(octree){
- super();
- this.trueOctree = octree;
- this.pcoGeometry = octree.pcoGeometry;
- this.points = [];
- this.visibleNodes = [];
-
- //this.material = this.trueOctree.material;
- this.material = new PointCloudMaterial$1();
- //this.material.copy(this.trueOctree.material);
- copyMaterial(this.trueOctree.material, this.material);
- this.material.pointSizeType = PointSizeType.FIXED;
- this.batchSize = 100 * 1000;
- this.currentBatch = null;
- }
- getAttribute(name){
- return this.trueOctree.getAttribute(name);
- }
- dispose(){
- for(let node of this.visibleNodes){
- node.geometry.dispose();
- }
- this.visibleNodes = [];
- this.currentBatch = null;
- this.points = [];
- }
- addPoints(data){
- // since each call to addPoints can deliver very very few points,
- // we're going to batch them into larger buffers for efficiency.
- if(this.currentBatch === null){
- this.currentBatch = this.createNewBatch(data);
- }
- this.points.push(data);
- let updateRange = {
- start: this.currentBatch.geometry.drawRange.count,
- count: 0
- };
- let projectedBox = new Box3();
- let truePos = new Vector3();
- for(let i = 0; i < data.numPoints; i++){
- if(updateRange.start + updateRange.count >= this.batchSize){
- // current batch full, start new batch
- for(let key of Object.keys(this.currentBatch.geometry.attributes)){
- let attribute = this.currentBatch.geometry.attributes[key];
- attribute.updateRange.offset = updateRange.start;
- attribute.updateRange.count = updateRange.count;
- attribute.needsUpdate = true;
- }
- this.currentBatch.geometry.computeBoundingBox();
- this.currentBatch.geometry.computeBoundingSphere();
- this.currentBatch = this.createNewBatch(data);
- updateRange = {
- start: 0,
- count: 0
- };
- }
- truePos.set(
- data.data.position[3 * i + 0] + this.trueOctree.position.x,
- data.data.position[3 * i + 1] + this.trueOctree.position.y,
- data.data.position[3 * i + 2] + this.trueOctree.position.z,
- );
- let x = data.data.mileage[i];
- let y = 0;
- let z = truePos.z;
- projectedBox.expandByPoint(new Vector3(x, y, z));
- let index = updateRange.start + updateRange.count;
- let geometry = this.currentBatch.geometry;
- for(let attributeName of Object.keys(data.data)){
- let source = data.data[attributeName];
- let target = geometry.attributes[attributeName];
- let numElements = target.itemSize;
-
- for(let item = 0; item < numElements; item++){
- target.array[numElements * index + item] = source[numElements * i + item];
- }
- }
- {
- let position = geometry.attributes.position;
- position.array[3 * index + 0] = x;
- position.array[3 * index + 1] = y;
- position.array[3 * index + 2] = z;
- }
- updateRange.count++;
- this.currentBatch.geometry.drawRange.count++;
- }
- for(let key of Object.keys(this.currentBatch.geometry.attributes)){
- let attribute = this.currentBatch.geometry.attributes[key];
- attribute.updateRange.offset = updateRange.start;
- attribute.updateRange.count = updateRange.count;
- attribute.needsUpdate = true;
- }
- data.projectedBox = projectedBox;
- this.projectedBox = this.points.reduce( (a, i) => a.union(i.projectedBox), new Box3());
- }
- createNewBatch(data){
- let geometry = new BufferGeometry();
- // create new batches with batch_size elements of the same type as the attribute
- for(let attributeName of Object.keys(data.data)){
- let buffer = data.data[attributeName];
- let numElements = buffer.length / data.numPoints; // 3 for pos, 4 for col, 1 for scalars
- let constructor = buffer.constructor;
- let normalized = false;
-
- if(this.trueOctree.root.sceneNode){
- if(this.trueOctree.root.sceneNode.geometry.attributes[attributeName]){
- normalized = this.trueOctree.root.sceneNode.geometry.attributes[attributeName].normalized;
- }
- }
-
- let batchBuffer = new constructor(numElements * this.batchSize);
- let bufferAttribute = new BufferAttribute(batchBuffer, numElements, normalized);
- bufferAttribute.potree = {
- range: [0, 1],
- };
- geometry.setAttribute(attributeName, bufferAttribute);
- }
- geometry.drawRange.start = 0;
- geometry.drawRange.count = 0;
- let batch = new Batch(geometry, this.material);
- this.visibleNodes.push(batch);
- return batch;
- }
-
- computeVisibilityTextureData(){
- let data = new Uint8Array(this.visibleNodes.length * 4);
- let offsets = new Map();
- for(let i = 0; i < this.visibleNodes.length; i++){
- let node = this.visibleNodes[i];
- offsets[node] = i;
- }
- return {
- data: data,
- offsets: offsets,
- };
- }
- }
- class ProfileWindow extends EventDispatcher {
- constructor (viewer) {
- super();
- this.viewer = viewer;
- this.elRoot = $('#profile_window');
- this.renderArea = this.elRoot.find('#profileCanvasContainer');
- this.svg = d3.select('svg#profileSVG');
- this.mouseIsDown = false;
- this.projectedBox = new Box3();
- this.pointclouds = new Map();
- this.numPoints = 0;
- this.lastAddPointsTimestamp = undefined;
- this.mouse = new Vector2$1(0, 0);
- this.scale = new Vector3(1, 1, 1);
- this.autoFitEnabled = true; // completely disable/enable
- this.autoFit = false; // internal
- let cwIcon = `${exports.resourcePath}/icons/arrow_cw.svg`;
- $('#potree_profile_rotate_cw').attr('src', cwIcon);
- let ccwIcon = `${exports.resourcePath}/icons/arrow_ccw.svg`;
- $('#potree_profile_rotate_ccw').attr('src', ccwIcon);
-
- let forwardIcon = `${exports.resourcePath}/icons/arrow_up.svg`;
- $('#potree_profile_move_forward').attr('src', forwardIcon);
- let backwardIcon = `${exports.resourcePath}/icons/arrow_down.svg`;
- $('#potree_profile_move_backward').attr('src', backwardIcon);
- let csvIcon = `${exports.resourcePath}/icons/file_csv_2d.svg`;
- $('#potree_download_csv_icon').attr('src', csvIcon);
- let lasIcon = `${exports.resourcePath}/icons/file_las_3d.svg`;
- $('#potree_download_las_icon').attr('src', lasIcon);
- let closeIcon = `${exports.resourcePath}/icons/close.svg`;
- $('#closeProfileContainer').attr("src", closeIcon);
- this.initTHREE();
- this.initSVG();
- this.initListeners();
- this.pRenderer = new Renderer(this.renderer);
- this.elRoot.i18n();
- }
- initListeners () {
- $(window).resize(() => {
- if (this.enabled) {
- this.render();
- }
- });
- this.renderArea.mousedown(e => {
- this.mouseIsDown = true;
- });
- this.renderArea.mouseup(e => {
- this.mouseIsDown = false;
- });
- let viewerPickSphereSizeHandler = () => {
- let camera = this.viewer.scene.getActiveCamera();
- let domElement = this.viewer.renderer.domElement;
- let distance = this.viewerPickSphere.position.distanceTo(camera.position);
- let pr = Utils.projectedRadius(1, camera, distance, domElement.clientWidth, domElement.clientHeight);
- let scale = (10 / pr);
- this.viewerPickSphere.scale.set(scale, scale, scale);
- };
- this.renderArea.mousemove(e => {
- if (this.pointclouds.size === 0) {
- return;
- }
- let rect = this.renderArea[0].getBoundingClientRect();
- let x = e.clientX - rect.left;
- let y = e.clientY - rect.top;
- let newMouse = new Vector2$1(x, y);
- if (this.mouseIsDown) {
- // DRAG
- this.autoFit = false;
- this.lastDrag = new Date().getTime();
- let cPos = [this.scaleX.invert(this.mouse.x), this.scaleY.invert(this.mouse.y)];
- let ncPos = [this.scaleX.invert(newMouse.x), this.scaleY.invert(newMouse.y)];
- this.camera.position.x -= ncPos[0] - cPos[0];
- this.camera.position.z -= ncPos[1] - cPos[1];
- this.render();
- } else if (this.pointclouds.size > 0) {
- // FIND HOVERED POINT
- let radius = Math.abs(this.scaleX.invert(0) - this.scaleX.invert(40));
- let mileage = this.scaleX.invert(newMouse.x);
- let elevation = this.scaleY.invert(newMouse.y);
- let closest = this.selectPoint(mileage, elevation, radius);
- if (closest) {
- let point = closest.point;
- let position = new Float64Array([
- point.position[0] + closest.pointcloud.position.x,
- point.position[1] + closest.pointcloud.position.y,
- point.position[2] + closest.pointcloud.position.z
- ]);
- this.elRoot.find('#profileSelectionProperties').fadeIn(200);
- this.pickSphere.visible = true;
- this.pickSphere.scale.set(0.5 * radius, 0.5 * radius, 0.5 * radius);
- this.pickSphere.position.set(point.mileage, 0, position[2]);
- this.viewerPickSphere.position.set(...position);
-
- if(!this.viewer.scene.scene.children.includes(this.viewerPickSphere)){
- this.viewer.scene.scene.add(this.viewerPickSphere);
- if(!this.viewer.hasEventListener("update", viewerPickSphereSizeHandler)){
- this.viewer.addEventListener("update", viewerPickSphereSizeHandler);
- }
- }
-
- let info = this.elRoot.find('#profileSelectionProperties');
- let html = '<table>';
- for (let attributeName of Object.keys(point)) {
- let value = point[attributeName];
- let attribute = closest.pointcloud.getAttribute(attributeName);
- let transform = value => value;
- if(attribute && attribute.type.size > 4){
- let range = attribute.initialRange;
- let scale = 1 / (range[1] - range[0]);
- let offset = range[0];
- transform = value => value / scale + offset;
- }
-
-
- if (attributeName === 'position') {
- let values = [...position].map(v => Utils.addCommas(v.toFixed(3)));
- html += `
- <tr>
- <td>x</td>
- <td>${values[0]}</td>
- </tr>
- <tr>
- <td>y</td>
- <td>${values[1]}</td>
- </tr>
- <tr>
- <td>z</td>
- <td>${values[2]}</td>
- </tr>`;
- } else if (attributeName === 'rgba') {
- html += `
- <tr>
- <td>${attributeName}</td>
- <td>${value.join(', ')}</td>
- </tr>`;
- } else if (attributeName === 'normal') {
- continue;
- } else if (attributeName === 'mileage') {
- html += `
- <tr>
- <td>${attributeName}</td>
- <td>${value.toFixed(3)}</td>
- </tr>`;
- } else {
- html += `
- <tr>
- <td>${attributeName}</td>
- <td>${transform(value)}</td>
- </tr>`;
- }
- }
- html += '</table>';
- info.html(html);
- this.selectedPoint = point;
- } else {
- // this.pickSphere.visible = false;
- // this.selectedPoint = null;
- this.viewer.scene.scene.add(this.viewerPickSphere);
- let index = this.viewer.scene.scene.children.indexOf(this.viewerPickSphere);
- if(index >= 0){
- this.viewer.scene.scene.children.splice(index, 1);
- }
- this.viewer.removeEventListener("update", viewerPickSphereSizeHandler);
-
- }
- this.render();
- }
- this.mouse.copy(newMouse);
- });
- let onWheel = e => {
- this.autoFit = false;
- let delta = 0;
- if (e.wheelDelta !== undefined) { // WebKit / Opera / Explorer 9
- delta = e.wheelDelta;
- } else if (e.detail !== undefined) { // Firefox
- delta = -e.detail;
- }
- let ndelta = Math.sign(delta);
- let cPos = [this.scaleX.invert(this.mouse.x), this.scaleY.invert(this.mouse.y)];
- if (ndelta > 0) {
- // + 10%
- this.scale.multiplyScalar(1.1);
- } else {
- // - 10%
- this.scale.multiplyScalar(100 / 110);
- }
- this.updateScales();
- let ncPos = [this.scaleX.invert(this.mouse.x), this.scaleY.invert(this.mouse.y)];
- this.camera.position.x -= ncPos[0] - cPos[0];
- this.camera.position.z -= ncPos[1] - cPos[1];
- this.render();
- this.updateScales();
- };
- $(this.renderArea)[0].addEventListener('mousewheel', onWheel, false);
- $(this.renderArea)[0].addEventListener('DOMMouseScroll', onWheel, false); // Firefox
- $('#closeProfileContainer').click(() => {
- this.hide();
- });
- let getProfilePoints = () => {
- let points = new Points$1();
-
- for(let [pointcloud, entry] of this.pointclouds){
- for(let pointSet of entry.points){
- let originPos = pointSet.data.position;
- let trueElevationPosition = new Float32Array(originPos);
- for(let i = 0; i < pointSet.numPoints; i++){
- trueElevationPosition[3 * i + 2] += pointcloud.position.z;
- }
- pointSet.data.position = trueElevationPosition;
- points.add(pointSet);
- pointSet.data.position = originPos;
- }
- }
- return points;
- };
- $('#potree_download_csv_icon').click(() => {
-
- let points = getProfilePoints();
- let string = CSVExporter.toString(points);
- let blob = new Blob([string], {type: "text/string"});
- $('#potree_download_profile_ortho_link').attr('href', URL.createObjectURL(blob));
- });
- $('#potree_download_las_icon').click(() => {
- let points = getProfilePoints();
- let buffer = LASExporter.toLAS(points);
- let blob = new Blob([buffer], {type: "application/octet-binary"});
- $('#potree_download_profile_link').attr('href', URL.createObjectURL(blob));
- });
- }
- selectPoint (mileage, elevation, radius) {
- let closest = {
- distance: Infinity,
- pointcloud: null,
- points: null,
- index: null
- };
- let pointBox = new Box2(
- new Vector2$1(mileage - radius, elevation - radius),
- new Vector2$1(mileage + radius, elevation + radius));
- let numTested = 0;
- let numSkipped = 0;
- let numTestedPoints = 0;
- let numSkippedPoints = 0;
- for (let [pointcloud, entry] of this.pointclouds) {
- for(let points of entry.points){
- let collisionBox = new Box2(
- new Vector2$1(points.projectedBox.min.x, points.projectedBox.min.z),
- new Vector2$1(points.projectedBox.max.x, points.projectedBox.max.z)
- );
- let intersects = collisionBox.intersectsBox(pointBox);
- if(!intersects){
- numSkipped++;
- numSkippedPoints += points.numPoints;
- continue;
- }
- numTested++;
- numTestedPoints += points.numPoints;
- for (let i = 0; i < points.numPoints; i++) {
- let m = points.data.mileage[i] - mileage;
- let e = points.data.position[3 * i + 2] - elevation + pointcloud.position.z;
- let r = Math.sqrt(m * m + e * e);
- const withinDistance = r < radius && r < closest.distance;
- let unfilteredClass = true;
- if(points.data.classification){
- const classification = pointcloud.material.classification;
- const pointClassID = points.data.classification[i];
- const pointClassValue = classification[pointClassID];
- if(pointClassValue && (!pointClassValue.visible || pointClassValue.color.w === 0)){
- unfilteredClass = false;
- }
- }
- if (withinDistance && unfilteredClass) {
- closest = {
- distance: r,
- pointcloud: pointcloud,
- points: points,
- index: i
- };
- }
- }
- }
- }
- //console.log(`nodes: ${numTested}, ${numSkipped} || points: ${numTestedPoints}, ${numSkippedPoints}`);
- if (closest.distance < Infinity) {
- let points = closest.points;
- let point = {};
- let attributes = Object.keys(points.data);
- for (let attribute of attributes) {
- let attributeData = points.data[attribute];
- let itemSize = attributeData.length / points.numPoints;
- let value = attributeData.subarray(itemSize * closest.index, itemSize * closest.index + itemSize);
- if (value.length === 1) {
- point[attribute] = value[0];
- } else {
- point[attribute] = value;
- }
- }
- closest.point = point;
- return closest;
- } else {
- return null;
- }
- }
- initTHREE () {
- this.renderer = new WebGLRenderer({alpha: true, premultipliedAlpha: false});
- this.renderer.setClearColor(0x000000, 0);
- this.renderer.setSize(10, 10);
- this.renderer.autoClear = false;
- this.renderArea.append($(this.renderer.domElement));
- this.renderer.domElement.tabIndex = '2222';
- $(this.renderer.domElement).css('width', '100%');
- $(this.renderer.domElement).css('height', '100%');
- {
- let gl = this.renderer.getContext();
- if(gl.createVertexArray == null){
- let extVAO = gl.getExtension('OES_vertex_array_object');
- if(!extVAO){
- throw new Error("OES_vertex_array_object extension not supported");
- }
- gl.createVertexArray = extVAO.createVertexArrayOES.bind(extVAO);
- gl.bindVertexArray = extVAO.bindVertexArrayOES.bind(extVAO);
- }
-
- }
- this.camera = new OrthographicCamera(-1000, 1000, 1000, -1000, -1000, 1000);
- /* */
-
- if(window.axisYup){
- }else {
- this.camera.up.set(0, 0, 1);
- this.camera.rotation.order = "ZXY";
- this.camera.rotation.x = Math.PI / 2.0;
- }
- this.scene = new Scene();
- this.profileScene = new Scene();
- let sg = new SphereGeometry(1, 16, 16);
- let sm = new MeshNormalMaterial();
- this.pickSphere = new Mesh(sg, sm);
- this.scene.add(this.pickSphere);
- this.viewerPickSphere = new Mesh(sg, sm);
- }
- initSVG () {
- let width = this.renderArea[0].clientWidth;
- let height = this.renderArea[0].clientHeight;
- let marginLeft = this.renderArea[0].offsetLeft;
- this.svg.selectAll('*').remove();
- this.scaleX = d3.scale.linear()
- .domain([this.camera.left + this.camera.position.x, this.camera.right + this.camera.position.x])
- .range([0, width]);
- this.scaleY = d3.scale.linear()
- .domain([this.camera.bottom + this.camera.position.z, this.camera.top + this.camera.position.z])
- .range([height, 0]);
- this.xAxis = d3.svg.axis()
- .scale(this.scaleX)
- .orient('bottom')
- .innerTickSize(-height)
- .outerTickSize(1)
- .tickPadding(10)
- .ticks(width / 50);
- this.yAxis = d3.svg.axis()
- .scale(this.scaleY)
- .orient('left')
- .innerTickSize(-width)
- .outerTickSize(1)
- .tickPadding(10)
- .ticks(height / 20);
- this.elXAxis = this.svg.append('g')
- .attr('class', 'x axis')
- .attr('transform', `translate(${marginLeft}, ${height})`)
- .call(this.xAxis);
- this.elYAxis = this.svg.append('g')
- .attr('class', 'y axis')
- .attr('transform', `translate(${marginLeft}, 0)`)
- .call(this.yAxis);
- }
- addPoints (pointcloud, points) {
- if(points.numPoints === 0){
- return;
- }
- let entry = this.pointclouds.get(pointcloud);
- if(!entry){
- entry = new ProfileFakeOctree(pointcloud);
- this.pointclouds.set(pointcloud, entry);
- this.profileScene.add(entry);
- let materialChanged = () => {
- this.render();
- };
- materialChanged();
- pointcloud.material.addEventListener('material_property_changed', materialChanged);
- this.addEventListener("on_reset_once", () => {
- pointcloud.material.removeEventListener('material_property_changed', materialChanged);
- });
- }
- entry.addPoints(points);
- this.projectedBox.union(entry.projectedBox);
- if (this.autoFit && this.autoFitEnabled) {
- let width = this.renderArea[0].clientWidth;
- let height = this.renderArea[0].clientHeight;
- let size = this.projectedBox.getSize(new Vector3());
- let sx = width / size.x;
- let sy = height / size.z;
- let scale = Math.min(sx, sy);
- let center = this.projectedBox.getCenter(new Vector3());
- this.scale.set(scale, scale, 1);
- this.camera.position.copy(center);
- //console.log("camera: ", this.camera.position.toArray().join(", "));
- }
- //console.log(entry);
- this.render();
- let numPoints = 0;
- for (let [key, value] of this.pointclouds.entries()) {
- numPoints += value.points.reduce( (a, i) => a + i.numPoints, 0);
- }
- $(`#profile_num_points`).html(Utils.addCommas(numPoints));
- }
- reset () {
- this.lastReset = new Date().getTime();
- this.dispatchEvent({type: "on_reset_once"});
- this.removeEventListeners("on_reset_once");
- this.autoFit = true;
- this.projectedBox = new Box3();
- for(let [key, entry] of this.pointclouds){
- entry.dispose();
- }
- this.pointclouds.clear();
- this.mouseIsDown = false;
- this.mouse.set(0, 0);
- if(this.autoFitEnabled){
- this.scale.set(1, 1, 1);
- }
- this.pickSphere.visible = false;
- this.elRoot.find('#profileSelectionProperties').hide();
- this.render();
- }
- show () {
- this.elRoot.fadeIn();
- this.enabled = true;
- }
- hide () {
- this.elRoot.fadeOut();
- this.enabled = false;
- }
- updateScales () {
- let width = this.renderArea[0].clientWidth;
- let height = this.renderArea[0].clientHeight;
- let left = (-width / 2) / this.scale.x;
- let right = (+width / 2) / this.scale.x;
- let top = (+height / 2) / this.scale.y;
- let bottom = (-height / 2) / this.scale.y;
- this.camera.left = left;
- this.camera.right = right;
- this.camera.top = top;
- this.camera.bottom = bottom;
- this.camera.updateProjectionMatrix();
- this.scaleX.domain([this.camera.left + this.camera.position.x, this.camera.right + this.camera.position.x])
- .range([0, width]);
- this.scaleY.domain([this.camera.bottom + this.camera.position.z, this.camera.top + this.camera.position.z])
- .range([height, 0]);
- let marginLeft = this.renderArea[0].offsetLeft;
- this.xAxis.scale(this.scaleX)
- .orient('bottom')
- .innerTickSize(-height)
- .outerTickSize(1)
- .tickPadding(10)
- .ticks(width / 50);
- this.yAxis.scale(this.scaleY)
- .orient('left')
- .innerTickSize(-width)
- .outerTickSize(1)
- .tickPadding(10)
- .ticks(height / 20);
- this.elXAxis
- .attr('transform', `translate(${marginLeft}, ${height})`)
- .call(this.xAxis);
- this.elYAxis
- .attr('transform', `translate(${marginLeft}, 0)`)
- .call(this.yAxis);
- }
- requestScaleUpdate(){
- let threshold = 100;
- let allowUpdate = ((this.lastReset === undefined) || (this.lastScaleUpdate === undefined))
- || ((new Date().getTime() - this.lastReset) > threshold && (new Date().getTime() - this.lastScaleUpdate) > threshold);
- if(allowUpdate){
- this.updateScales();
- this.lastScaleUpdate = new Date().getTime();
-
- this.scaleUpdatePending = false;
- }else if(!this.scaleUpdatePending) {
- setTimeout(this.requestScaleUpdate.bind(this), 100);
- this.scaleUpdatePending = true;
- }
-
- }
- render () {
- let width = this.renderArea[0].clientWidth;
- let height = this.renderArea[0].clientHeight;
- let {renderer, pRenderer, camera, profileScene, scene} = this;
- let {scaleX, pickSphere} = this;
- renderer.setSize(width, height);
- renderer.setClearColor(0x000000, 0);
- renderer.clear(true, true, false);
- for(let pointcloud of this.pointclouds.keys()){
- let source = pointcloud.material;
- let target = this.pointclouds.get(pointcloud).material;
-
- copyMaterial(source, target);
- target.size = 2;
- }
-
- pRenderer.render(profileScene, camera, null);
- let radius = Math.abs(scaleX.invert(0) - scaleX.invert(5));
- if (radius === 0) {
- pickSphere.visible = false;
- } else {
- pickSphere.scale.set(radius, radius, radius);
- pickSphere.visible = true;
- }
-
- renderer.render(scene, camera);
- this.requestScaleUpdate();
- }
- };
- class ProfileWindowController {
- constructor (viewer) {
- this.viewer = viewer;
- this.profileWindow = viewer.profileWindow;
- this.profile = null;
- this.numPoints = 0;
- this.threshold = 60 * 1000;
- this.rotateAmount = 10;
- this.scheduledRecomputeTime = null;
- this.enabled = true;
- this.requests = [];
- this._recompute = () => { this.recompute(); };
- this.viewer.addEventListener("scene_changed", e => {
- e.oldScene.removeEventListener("pointcloud_added", this._recompute);
- e.scene.addEventListener("pointcloud_added", this._recompute);
- });
- this.viewer.scene.addEventListener("pointcloud_added", this._recompute);
- $("#potree_profile_rotate_amount").val(parseInt(this.rotateAmount));
- $("#potree_profile_rotate_amount").on("input", (e) => {
- const str = $("#potree_profile_rotate_amount").val();
- if(!isNaN(str)){
- const value = parseFloat(str);
- this.rotateAmount = value;
- $("#potree_profile_rotate_amount").css("background-color", "");
- }else {
- $("#potree_profile_rotate_amount").css("background-color", "#ff9999");
- }
- });
- const rotate = (radians) => {
- const profile = this.profile;
- const points = profile.points;
- const start = points[0];
- const end = points[points.length - 1];
- const center = start.clone().add(end).multiplyScalar(0.5);
- const mMoveOrigin = new Matrix4().makeTranslation(-center.x, -center.y, -center.z);
- const mRotate = new Matrix4().makeRotationZ(radians);
- const mMoveBack = new Matrix4().makeTranslation(center.x, center.y, center.z);
- //const transform = mMoveOrigin.multiply(mRotate).multiply(mMoveBack);
- const transform = mMoveBack.multiply(mRotate).multiply(mMoveOrigin);
- const rotatedPoints = points.map( point => point.clone().applyMatrix4(transform) );
- this.profileWindow.autoFitEnabled = false;
- for(let i = 0; i < points.length; i++){
- profile.setPosition(i, rotatedPoints[i]);
- }
- };
- $("#potree_profile_rotate_cw").click( () => {
- const radians = MathUtils.degToRad(this.rotateAmount);
- rotate(-radians);
- });
- $("#potree_profile_rotate_ccw").click( () => {
- const radians = MathUtils.degToRad(this.rotateAmount);
- rotate(radians);
- });
- $("#potree_profile_move_forward").click( () => {
- const profile = this.profile;
- const points = profile.points;
- const start = points[0];
- const end = points[points.length - 1];
- const dir = end.clone().sub(start).normalize();
- const up = new Vector3(0, 0, 1);
- const forward = up.cross(dir);
- const move = forward.clone().multiplyScalar(profile.width / 2);
- this.profileWindow.autoFitEnabled = false;
- for(let i = 0; i < points.length; i++){
- profile.setPosition(i, points[i].clone().add(move));
- }
- });
- $("#potree_profile_move_backward").click( () => {
- const profile = this.profile;
- const points = profile.points;
- const start = points[0];
- const end = points[points.length - 1];
- const dir = end.clone().sub(start).normalize();
- const up = new Vector3(0, 0, 1);
- const forward = up.cross(dir);
- const move = forward.clone().multiplyScalar(-profile.width / 2);
- this.profileWindow.autoFitEnabled = false;
- for(let i = 0; i < points.length; i++){
- profile.setPosition(i, points[i].clone().add(move));
- }
- });
- }
- setProfile (profile) {
- if (this.profile !== null && this.profile !== profile) {
- this.profile.removeEventListener('marker_moved', this._recompute);
- this.profile.removeEventListener('marker_added', this._recompute);
- this.profile.removeEventListener('marker_removed', this._recompute);
- this.profile.removeEventListener('width_changed', this._recompute);
- }
- this.profile = profile;
- {
- this.profile.addEventListener('marker_moved', this._recompute);
- this.profile.addEventListener('marker_added', this._recompute);
- this.profile.addEventListener('marker_removed', this._recompute);
- this.profile.addEventListener('width_changed', this._recompute);
- }
- this.recompute();
- }
- reset () {
- this.profileWindow.reset();
- this.numPoints = 0;
- if (this.profile) {
- for (let request of this.requests) {
- request.cancel();
- }
- }
- }
- progressHandler (pointcloud, progress) {
- for (let segment of progress.segments) {
- this.profileWindow.addPoints(pointcloud, segment.points);
- this.numPoints += segment.points.numPoints;
- }
- }
- cancel () {
- for (let request of this.requests) {
- request.cancel();
- // request.finishLevelThenCancel();
- }
- this.requests = [];
- };
- finishLevelThenCancel(){
- for (let request of this.requests) {
- request.finishLevelThenCancel();
- }
- this.requests = [];
- }
- recompute () {
- if (!this.profile) {
- return;
- }
- if (this.scheduledRecomputeTime !== null && this.scheduledRecomputeTime > new Date().getTime()) {
- return;
- } else {
- this.scheduledRecomputeTime = new Date().getTime() + 100;
- }
- this.scheduledRecomputeTime = null;
- this.reset();
- for (let pointcloud of this.viewer.scene.pointclouds.filter(p => p.visible)) {
- let request = pointcloud.getPointsInProfile(this.profile, null, {
- 'onProgress': (event) => {
- if (!this.enabled) {
- return;
- }
- this.progressHandler(pointcloud, event.points);
- if (this.numPoints > this.threshold) {
- this.finishLevelThenCancel();
- }
- },
- 'onFinish': (event) => {
- if (!this.enabled) {
- }
- },
- 'onCancel': () => {
- if (!this.enabled) {
- }
- }
- });
- this.requests.push(request);
- }
- }
- };
- /**
- *
- * @author sigeom sa / http://sigeom.ch
- * @author Ioda-Net Sàrl / https://www.ioda-net.ch/
- * @author Markus Schütz / http://potree.org
- *
- */
- class GeoJSONExporter{
- static measurementToFeatures (measurement) {
- let coords = measurement.points.map(e => e.position.toArray());
- let features = [];
- if (coords.length === 1) {
- let feature = {
- type: 'Feature',
- geometry: {
- type: 'Point',
- coordinates: coords[0]
- },
- properties: {
- name: measurement.name
- }
- };
- features.push(feature);
- } else if (coords.length > 1 && !measurement.closed) {
- let object = {
- 'type': 'Feature',
- 'geometry': {
- 'type': 'LineString',
- 'coordinates': coords
- },
- 'properties': {
- name: measurement.name
- }
- };
- features.push(object);
- } else if (coords.length > 1 && measurement.closed) {
- let object = {
- 'type': 'Feature',
- 'geometry': {
- 'type': 'Polygon',
- 'coordinates': [[...coords, coords[0]]]
- },
- 'properties': {
- name: measurement.name
- }
- };
- features.push(object);
- }
- if (measurement.showDistances) {
- measurement.edgeLabels.forEach((label) => {
- let labelPoint = {
- type: 'Feature',
- geometry: {
- type: 'Point',
- coordinates: label.position.toArray()
- },
- properties: {
- distance: label.text
- }
- };
- features.push(labelPoint);
- });
- }
- if (measurement.showArea) {
- let point = measurement.areaLabel.position;
- let labelArea = {
- type: 'Feature',
- geometry: {
- type: 'Point',
- coordinates: point.toArray()
- },
- properties: {
- area: measurement.areaLabel.text
- }
- };
- features.push(labelArea);
- }
- return features;
- }
- static toString (measurements) {
- if (!(measurements instanceof Array)) {
- measurements = [measurements];
- }
- measurements = measurements.filter(m => m instanceof Measure);
- let features = [];
- for (let measure of measurements) {
- let f = GeoJSONExporter.measurementToFeatures(measure);
- features = features.concat(f);
- }
- let geojson = {
- 'type': 'FeatureCollection',
- 'features': features
- };
- return JSON.stringify(geojson, null, '\t');
- }
- }
- /**
- *
- * @author sigeom sa / http://sigeom.ch
- * @author Ioda-Net Sàrl / https://www.ioda-net.ch/
- * @author Markus Schuetz / http://potree.org
- *
- */
- class DXFExporter {
- static measurementPointSection (measurement) {
- let position = measurement.points[0].position;
- if (!position) {
- return '';
- }
- let dxfSection = `0
- CIRCLE
- 8
- layer_point
- 10
- ${position.x}
- 20
- ${position.y}
- 30
- ${position.z}
- 40
- 1.0
- `;
- return dxfSection;
- }
- static measurementPolylineSection (measurement) {
- // bit code for polygons/polylines:
- // https://www.autodesk.com/techpubs/autocad/acad2000/dxf/polyline_dxf_06.htm
- let geomCode = 8;
- if (measurement.closed) {
- geomCode += 1;
- }
- let dxfSection = `0
- POLYLINE
- 8
- layer_polyline
- 62
- 1
- 66
- 1
- 10
- 0.0
- 20
- 0.0
- 30
- 0.0
- 70
- ${geomCode}
- `;
- let xMax = 0.0;
- let yMax = 0.0;
- let zMax = 0.0;
- for (let point of measurement.points) {
- point = point.position;
- xMax = Math.max(xMax, point.x);
- yMax = Math.max(yMax, point.y);
- zMax = Math.max(zMax, point.z);
- dxfSection += `0
- VERTEX
- 8
- 0
- 10
- ${point.x}
- 20
- ${point.y}
- 30
- ${point.z}
- 70
- 32
- `;
- }
- dxfSection += `0
- SEQEND
- `;
- return dxfSection;
- }
- static measurementSection (measurement) {
- // if(measurement.points.length <= 1){
- // return "";
- // }
- if (measurement.points.length === 0) {
- return '';
- } else if (measurement.points.length === 1) {
- return DXFExporter.measurementPointSection(measurement);
- } else if (measurement.points.length >= 2) {
- return DXFExporter.measurementPolylineSection(measurement);
- }
- }
- static toString(measurements){
- if (!(measurements instanceof Array)) {
- measurements = [measurements];
- }
- measurements = measurements.filter(m => m instanceof Measure);
- let points = measurements.filter(m => (m instanceof Measure))
- .map(m => m.points)
- .reduce((a, v) => a.concat(v))
- .map(p => p.position);
- let min = new Vector3(Infinity, Infinity, Infinity);
- let max = new Vector3(-Infinity, -Infinity, -Infinity);
- for (let point of points) {
- min.min(point);
- max.max(point);
- }
- let dxfHeader = `999
- DXF created from potree
- 0
- SECTION
- 2
- HEADER
- 9
- $ACADVER
- 1
- AC1006
- 9
- $INSBASE
- 10
- 0.0
- 20
- 0.0
- 30
- 0.0
- 9
- $EXTMIN
- 10
- ${min.x}
- 20
- ${min.y}
- 30
- ${min.z}
- 9
- $EXTMAX
- 10
- ${max.x}
- 20
- ${max.y}
- 30
- ${max.z}
- 0
- ENDSEC
- `;
- let dxfBody = `0
- SECTION
- 2
- ENTITIES
- `;
- for (let measurement of measurements) {
- dxfBody += DXFExporter.measurementSection(measurement);
- }
- dxfBody += `0
- ENDSEC
- `;
- let dxf = dxfHeader + dxfBody + '0\nEOF';
- return dxf;
- }
- }
- class MeasurePanel{
- constructor(viewer, measurement, propertiesPanel){
- this.viewer = viewer;
- this.measurement = measurement;
- this.propertiesPanel = propertiesPanel;
- this._update = () => { this.update(); };
- }
- createCoordinatesTable(points){
- let table = $(`
- <table class="measurement_value_table">
- <tr>
- <th>x</th>
- <th>y</th>
- <th>z</th>
- <th></th>
- </tr>
- </table>
- `);
- let copyIconPath = Potree.resourcePath + '/icons/copy.svg';
- for (let point of points) {
- let x = Utils.addCommas(point.x.toFixed(3));
- let y = Utils.addCommas(point.y.toFixed(3));
- let z = Utils.addCommas(point.z.toFixed(3));
- let row = $(`
- <tr>
- <td><span>${x}</span></td>
- <td><span>${y}</span></td>
- <td><span>${z}</span></td>
- <td align="right" style="width: 25%">
- <img name="copy" title="copy" class="button-icon" src="${copyIconPath}" style="width: 16px; height: 16px"/>
- </td>
- </tr>
- `);
- this.elCopy = row.find("img[name=copy]");
- this.elCopy.click( () => {
- let msg = point.toArray().map(c => c.toFixed(3)).join(", ");
- Utils.clipboardCopy(msg);
- this.viewer.postMessage(
- `Copied value to clipboard: <br>'${msg}'`,
- {duration: 3000});
- });
- table.append(row);
- }
- return table;
- };
- createAttributesTable(){
- let elTable = $('<table class="measurement_value_table"></table>');
- let point = this.measurement.points[0];
-
- /* for(let attributeName of Object.keys(point)){
- if(attributeName === "position"){
-
- }else if(attributeName === "rgba"){
- let color = point.rgba;
- let text = color.join(', ');
- elTable.append($(`
- <tr>
- <td>rgb</td>
- <td>${text}</td>
- </tr>
- `));
- }else{
- let value = point[attributeName];
- let text = value.join(', ');
- elTable.append($(`
- <tr>
- <td>${attributeName}</td>
- <td>${text}</td>
- </tr>
- `));
- }
- } */
- return elTable;
- }
- update(){
- }
- };
- class DistancePanel extends MeasurePanel{
- constructor(viewer, measurement, propertiesPanel){
- super(viewer, measurement, propertiesPanel);
- let removeIconPath = Potree.resourcePath + '/icons/remove.svg';
- this.elContent = $(`
- <div class="measurement_content selectable">
- <span class="coordinates_table_container"></span>
- <br>
- <table id="distances_table" class="measurement_value_table"></table>
- <!-- ACTIONS -->
- <div style="display: flex; margin-top: 12px">
- <span>
- <input type="button" name="make_profile" value="profile from measure" />
- </span>
- <span style="flex-grow: 1"></span>
- <img name="remove" class="button-icon" src="${removeIconPath}" style="width: 16px; height: 16px"/>
- </div>
- </div>
- `);
- this.elRemove = this.elContent.find("img[name=remove]");
- this.elRemove.click( () => {
- this.viewer.scene.removeMeasurement(measurement);
- });
-
- this.elMakeProfile = this.elContent.find("input[name=make_profile]");
- this.elMakeProfile.click( () => {
- //measurement.points;
- const profile = new Profile();
- profile.name = measurement.name;
- profile.width = measurement.getTotalDistance() / 50;
- for(const point of measurement.points){
- profile.addMarker(point.position.clone());
- }
- this.viewer.scene.addProfile(profile);
- });
- this.propertiesPanel.addVolatileListener(measurement, "marker_added", this._update);
- this.propertiesPanel.addVolatileListener(measurement, "marker_removed", this._update);
- this.propertiesPanel.addVolatileListener(measurement, "marker_moved", this._update);
- this.update();
- }
- update(){
- let elCoordiantesContainer = this.elContent.find('.coordinates_table_container');
- elCoordiantesContainer.empty();
- elCoordiantesContainer.append(this.createCoordinatesTable(this.measurement.points));
- let positions = this.measurement.points;
- let distances = [];
- for (let i = 0; i < positions.length - 1; i++) {
- let d = positions[i].distanceTo(positions[i + 1]);
- distances.push(d.toFixed(3));
- }
- let totalDistance = this.measurement.getTotalDistance().toFixed(3);
- let elDistanceTable = this.elContent.find(`#distances_table`);
- elDistanceTable.empty();
- for (let i = 0; i < distances.length; i++) {
- let label = (i === 0) ? 'Distances: ' : '';
- let distance = distances[i];
- let elDistance = $(`
- <tr>
- <th>${label}</th>
- <td style="width: 100%; padding-left: 10px">${distance}</td>
- </tr>`);
- elDistanceTable.append(elDistance);
- }
- let elTotal = $(`
- <tr>
- <th>Total: </td><td style="width: 100%; padding-left: 10px">${totalDistance}</th>
- </tr>`);
- elDistanceTable.append(elTotal);
- }
- };
- class PointPanel extends MeasurePanel{
- constructor(viewer, measurement, propertiesPanel){
- super(viewer, measurement, propertiesPanel);
- let removeIconPath = Potree.resourcePath + '/icons/remove.svg';
- this.elContent = $(`
- <div class="measurement_content selectable">
- <span class="coordinates_table_container"></span>
- <br>
- <span class="attributes_table_container"></span>
- <!-- ACTIONS -->
- <div style="display: flex; margin-top: 12px">
- <span></span>
- <span style="flex-grow: 1"></span>
- <img name="remove" class="button-icon" src="${removeIconPath}" style="width: 16px; height: 16px"/>
- </div>
- </div>
- `);
- this.elRemove = this.elContent.find("img[name=remove]");
- this.elRemove.click( () => {
- this.viewer.scene.removeMeasurement(measurement);
- });
- this.propertiesPanel.addVolatileListener(measurement, "marker_added", this._update);
- this.propertiesPanel.addVolatileListener(measurement, "marker_removed", this._update);
- this.propertiesPanel.addVolatileListener(measurement, "marker_moved", this._update);
- this.update();
- }
- update(){
- let elCoordiantesContainer = this.elContent.find('.coordinates_table_container');
- elCoordiantesContainer.empty();
- elCoordiantesContainer.append(this.createCoordinatesTable(this.measurement.points));
- let elAttributesContainer = this.elContent.find('.attributes_table_container');
- elAttributesContainer.empty();
- elAttributesContainer.append(this.createAttributesTable());
- }
- };
- class AreaPanel extends MeasurePanel{
- constructor(viewer, measurement, propertiesPanel){
- super(viewer, measurement, propertiesPanel);
- let removeIconPath = Potree.resourcePath + '/icons/remove.svg';
- this.elContent = $(`
- <div class="measurement_content selectable">
- <span class="coordinates_table_container"></span>
- <br>
- <span style="font-weight: bold">Area: </span>
- <span id="measurement_area"></span>
- <!-- ACTIONS -->
- <div style="display: flex; margin-top: 12px">
- <span></span>
- <span style="flex-grow: 1"></span>
- <img name="remove" class="button-icon" src="${removeIconPath}" style="width: 16px; height: 16px"/>
- </div>
- </div>
- `);
- this.elRemove = this.elContent.find("img[name=remove]");
- this.elRemove.click( () => {
- this.viewer.scene.removeMeasurement(measurement);
- });
- this.propertiesPanel.addVolatileListener(measurement, "marker_added", this._update);
- this.propertiesPanel.addVolatileListener(measurement, "marker_removed", this._update);
- this.propertiesPanel.addVolatileListener(measurement, "marker_moved", this._update);
- this.update();
- }
- update(){
- let elCoordiantesContainer = this.elContent.find('.coordinates_table_container');
- elCoordiantesContainer.empty();
- elCoordiantesContainer.append(this.createCoordinatesTable(this.measurement.points));
- let elArea = this.elContent.find(`#measurement_area`);
- elArea.html(this.measurement.area.value.toFixed(3));
- }
- };
- class AnglePanel extends MeasurePanel{
- constructor(viewer, measurement, propertiesPanel){
- super(viewer, measurement, propertiesPanel);
- let removeIconPath = Potree.resourcePath + '/icons/remove.svg';
- this.elContent = $(`
- <div class="measurement_content selectable">
- <span class="coordinates_table_container"></span>
- <br>
- <table class="measurement_value_table">
- <tr>
- <th>\u03b1</th>
- <th>\u03b2</th>
- <th>\u03b3</th>
- </tr>
- <tr>
- <td align="center" id="angle_cell_alpha" style="width: 33%"></td>
- <td align="center" id="angle_cell_betta" style="width: 33%"></td>
- <td align="center" id="angle_cell_gamma" style="width: 33%"></td>
- </tr>
- </table>
- <!-- ACTIONS -->
- <div style="display: flex; margin-top: 12px">
- <span></span>
- <span style="flex-grow: 1"></span>
- <img name="remove" class="button-icon" src="${removeIconPath}" style="width: 16px; height: 16px"/>
- </div>
- </div>
- `);
- this.elRemove = this.elContent.find("img[name=remove]");
- this.elRemove.click( () => {
- this.viewer.scene.removeMeasurement(measurement);
- });
- this.propertiesPanel.addVolatileListener(measurement, "marker_added", this._update);
- this.propertiesPanel.addVolatileListener(measurement, "marker_removed", this._update);
- this.propertiesPanel.addVolatileListener(measurement, "marker_moved", this._update);
- this.update();
- }
- update(){
- let elCoordiantesContainer = this.elContent.find('.coordinates_table_container');
- elCoordiantesContainer.empty();
- elCoordiantesContainer.append(this.createCoordinatesTable(this.measurement.points.map(p => p.position)));
- let angles = [];
- for(let i = 0; i < this.measurement.points.length; i++){
- angles.push(this.measurement.getAngle(i) * (180.0 / Math.PI));
- }
- angles = angles.map(a => a.toFixed(1) + '\u00B0');
- let elAlpha = this.elContent.find(`#angle_cell_alpha`);
- let elBetta = this.elContent.find(`#angle_cell_betta`);
- let elGamma = this.elContent.find(`#angle_cell_gamma`);
- elAlpha.html(angles[0]);
- elBetta.html(angles[1]);
- elGamma.html(angles[2]);
- }
- };
- class CirclePanel extends MeasurePanel{
- constructor(viewer, measurement, propertiesPanel){
- super(viewer, measurement, propertiesPanel);
- let removeIconPath = Potree.resourcePath + '/icons/remove.svg';
- this.elContent = $(`
- <div class="measurement_content selectable">
- <span class="coordinates_table_container"></span>
- <br>
- <table id="infos_table" class="measurement_value_table"></table>
- <!-- ACTIONS -->
- <div style="display: flex; margin-top: 12px">
- <span></span>
- <span style="flex-grow: 1"></span>
- <img name="remove" class="button-icon" src="${removeIconPath}" style="width: 16px; height: 16px"/>
- </div>
- </div>
- `);
- this.elRemove = this.elContent.find("img[name=remove]");
- this.elRemove.click( () => {
- this.viewer.scene.removeMeasurement(measurement);
- });
- this.propertiesPanel.addVolatileListener(measurement, "marker_added", this._update);
- this.propertiesPanel.addVolatileListener(measurement, "marker_removed", this._update);
- this.propertiesPanel.addVolatileListener(measurement, "marker_moved", this._update);
- this.update();
- }
- update(){
- let elCoordiantesContainer = this.elContent.find('.coordinates_table_container');
- elCoordiantesContainer.empty();
- elCoordiantesContainer.append(this.createCoordinatesTable(this.measurement.points.map(p => p.position)));
- const elInfos = this.elContent.find(`#infos_table`);
- if(this.measurement.points.length !== 3){
- elInfos.empty();
-
- return;
- }
- const A = this.measurement.points[0].position;
- const B = this.measurement.points[1].position;
- const C = this.measurement.points[2].position;
- const center = Potree.Utils.computeCircleCenter(A, B, C);
- const radius = center.distanceTo(A);
- const circumference = 2 * Math.PI * radius;
-
- const format = (number) => {
- return Potree.Utils.addCommas(number.toFixed(3));
- };
-
- const txtCenter = `${format(center.x)} ${format(center.y)} ${format(center.z)}`;
- const txtRadius = format(radius);
- const txtCircumference = format(circumference);
- const thStyle = `style="text-align: left"`;
- const tdStyle = `style="width: 100%; padding: 5px;"`;
-
- elInfos.html(`
- <tr>
- <th ${thStyle}>Center: </th>
- <td ${tdStyle}></td>
- </tr>
- <tr>
- <td ${tdStyle} colspan="2">
- ${txtCenter}
- </td>
- </tr>
- <tr>
- <th ${thStyle}>Radius: </th>
- <td ${tdStyle}>${txtRadius}</td>
- </tr>
- <tr>
- <th ${thStyle}>Circumference: </th>
- <td ${tdStyle}>${txtCircumference}</td>
- </tr>
- `);
- }
- };
- class HeightPanel extends MeasurePanel{
- constructor(viewer, measurement, propertiesPanel){
- super(viewer, measurement, propertiesPanel);
- let removeIconPath = Potree.resourcePath + '/icons/remove.svg';
- this.elContent = $(`
- <div class="measurement_content selectable">
- <span class="coordinates_table_container"></span>
- <br>
- <span id="height_label">Height: </span><br>
- <!-- ACTIONS -->
- <div style="display: flex; margin-top: 12px">
- <span></span>
- <span style="flex-grow: 1"></span>
- <img name="remove" class="button-icon" src="${removeIconPath}" style="width: 16px; height: 16px"/>
- </div>
- </div>
- `);
- this.elRemove = this.elContent.find("img[name=remove]");
- this.elRemove.click( () => {
- this.viewer.scene.removeMeasurement(measurement);
- });
- this.propertiesPanel.addVolatileListener(measurement, "marker_added", this._update);
- this.propertiesPanel.addVolatileListener(measurement, "marker_removed", this._update);
- this.propertiesPanel.addVolatileListener(measurement, "marker_moved", this._update);
- this.update();
- }
- update(){
- let elCoordiantesContainer = this.elContent.find('.coordinates_table_container');
- elCoordiantesContainer.empty();
- elCoordiantesContainer.append(this.createCoordinatesTable(this.measurement.points.map(p => p.position)));
- {
- let points = this.measurement.points;
- let sorted = points.slice().sort((a, b) => a.position.z - b.position.z);
- let lowPoint = sorted[0].position.clone();
- let highPoint = sorted[sorted.length - 1].position.clone();
- let min = lowPoint.z;
- let max = highPoint.z;
- let height = max - min;
- height = height.toFixed(3);
- this.elHeightLabel = this.elContent.find(`#height_label`);
- this.elHeightLabel.html(`<b>Height:</b> ${height}`);
- }
- }
- };
- class VolumePanel extends MeasurePanel{
- constructor(viewer, measurement, propertiesPanel){
- super(viewer, measurement, propertiesPanel);
- let copyIconPath = Potree.resourcePath + '/icons/copy.svg';
- let removeIconPath = Potree.resourcePath + '/icons/remove.svg';
- let lblLengthText = new Map([
- [BoxVolume, "length"],
- [SphereVolume, "rx"],
- ]).get(measurement.constructor);
- let lblWidthText = new Map([
- [BoxVolume, "width"],
- [SphereVolume, "ry"],
- ]).get(measurement.constructor);
- let lblHeightText = new Map([
- [BoxVolume, "height"],
- [SphereVolume, "rz"],
- ]).get(measurement.constructor);
- this.elContent = $(`
- <div class="measurement_content selectable">
- <span class="coordinates_table_container"></span>
- <table class="measurement_value_table">
- <tr>
- <th>\u03b1</th>
- <th>\u03b2</th>
- <th>\u03b3</th>
- <th></th>
- </tr>
- <tr>
- <td align="center" id="angle_cell_alpha" style="width: 33%"></td>
- <td align="center" id="angle_cell_betta" style="width: 33%"></td>
- <td align="center" id="angle_cell_gamma" style="width: 33%"></td>
- <td align="right" style="width: 25%">
- <img name="copyRotation" title="copy" class="button-icon" src="${copyIconPath}" style="width: 16px; height: 16px"/>
- </td>
- </tr>
- </table>
- <table class="measurement_value_table">
- <tr>
- <th>${lblLengthText}</th>
- <th>${lblWidthText}</th>
- <th>${lblHeightText}</th>
- <th></th>
- </tr>
- <tr>
- <td align="center" id="cell_length" style="width: 33%"></td>
- <td align="center" id="cell_width" style="width: 33%"></td>
- <td align="center" id="cell_height" style="width: 33%"></td>
- <td align="right" style="width: 25%">
- <img name="copyScale" title="copy" class="button-icon" src="${copyIconPath}" style="width: 16px; height: 16px"/>
- </td>
- </tr>
- </table>
- <br>
- <span style="font-weight: bold">Volume: </span>
- <span id="measurement_volume"></span>
- <!--
- <li>
- <label style="whitespace: nowrap">
- <input id="volume_show" type="checkbox"/>
- <span>show volume</span>
- </label>
- </li>-->
- <li>
- <label style="whitespace: nowrap">
- <input id="volume_clip" type="checkbox"/>
- <span>make clip volume</span>
- </label>
- </li>
- <li style="margin-top: 10px">
- <input name="download_volume" type="button" value="prepare download" style="width: 100%" />
- <div name="download_message"></div>
- </li>
- <!-- ACTIONS -->
- <li style="display: grid; grid-template-columns: auto auto; grid-column-gap: 5px; margin-top: 10px">
- <input id="volume_reset_orientation" type="button" value="reset orientation"/>
- <input id="volume_make_uniform" type="button" value="make uniform"/>
- </li>
- <div style="display: flex; margin-top: 12px">
- <span></span>
- <span style="flex-grow: 1"></span>
- <img name="remove" class="button-icon" src="${removeIconPath}" style="width: 16px; height: 16px"/>
- </div>
- </div>
- `);
- { // download
- this.elDownloadButton = this.elContent.find("input[name=download_volume]");
- if(this.propertiesPanel.viewer.server){
- this.elDownloadButton.click(() => this.download());
- } else {
- this.elDownloadButton.hide();
- }
- }
- this.elCopyRotation = this.elContent.find("img[name=copyRotation]");
- this.elCopyRotation.click( () => {
- let rotation = this.measurement.rotation.toArray().slice(0, 3);
- let msg = rotation.map(c => c.toFixed(3)).join(", ");
- Utils.clipboardCopy(msg);
- this.viewer.postMessage(
- `Copied value to clipboard: <br>'${msg}'`,
- {duration: 3000});
- });
- this.elCopyScale = this.elContent.find("img[name=copyScale]");
- this.elCopyScale.click( () => {
- let scale = this.measurement.scale.toArray();
- let msg = scale.map(c => c.toFixed(3)).join(", ");
- Utils.clipboardCopy(msg);
- this.viewer.postMessage(
- `Copied value to clipboard: <br>'${msg}'`,
- {duration: 3000});
- });
- this.elRemove = this.elContent.find("img[name=remove]");
- this.elRemove.click( () => {
- this.viewer.scene.removeVolume(measurement);
- });
- this.elContent.find("#volume_reset_orientation").click(() => {
- measurement.rotation.set(0, 0, 0);
- });
- this.elContent.find("#volume_make_uniform").click(() => {
- let mean = (measurement.scale.x + measurement.scale.y + measurement.scale.z) / 3;
- measurement.scale.set(mean, mean, mean);
- });
- this.elCheckClip = this.elContent.find('#volume_clip');
- this.elCheckClip.click(event => {
- this.measurement.clip = event.target.checked;
- });
- this.elCheckShow = this.elContent.find('#volume_show');
- this.elCheckShow.click(event => {
- this.measurement.visible = event.target.checked;
- });
- this.propertiesPanel.addVolatileListener(measurement, "position_changed", this._update);
- this.propertiesPanel.addVolatileListener(measurement, "orientation_changed", this._update);
- this.propertiesPanel.addVolatileListener(measurement, "scale_changed", this._update);
- this.propertiesPanel.addVolatileListener(measurement, "clip_changed", this._update);
- this.update();
- }
- async download(){
- let clipBox = this.measurement;
- let regions = [];
- //for(let clipBox of boxes){
- {
- let toClip = clipBox.matrixWorld;
- let px = new Vector3(+0.5, 0, 0).applyMatrix4(toClip);
- let nx = new Vector3(-0.5, 0, 0).applyMatrix4(toClip);
- let py = new Vector3(0, +0.5, 0).applyMatrix4(toClip);
- let ny = new Vector3(0, -0.5, 0).applyMatrix4(toClip);
- let pz = new Vector3(0, 0, +0.5).applyMatrix4(toClip);
- let nz = new Vector3(0, 0, -0.5).applyMatrix4(toClip);
- let pxN = new Vector3().subVectors(nx, px).normalize();
- let nxN = pxN.clone().multiplyScalar(-1);
- let pyN = new Vector3().subVectors(ny, py).normalize();
- let nyN = pyN.clone().multiplyScalar(-1);
- let pzN = new Vector3().subVectors(nz, pz).normalize();
- let nzN = pzN.clone().multiplyScalar(-1);
- let planes = [
- new Plane().setFromNormalAndCoplanarPoint(pxN, px),
- new Plane().setFromNormalAndCoplanarPoint(nxN, nx),
- new Plane().setFromNormalAndCoplanarPoint(pyN, py),
- new Plane().setFromNormalAndCoplanarPoint(nyN, ny),
- new Plane().setFromNormalAndCoplanarPoint(pzN, pz),
- new Plane().setFromNormalAndCoplanarPoint(nzN, nz),
- ];
- let planeQueryParts = [];
- for(let plane of planes){
- let part = [plane.normal.toArray(), plane.constant].join(",");
- part = `[${part}]`;
- planeQueryParts.push(part);
- }
- let region = "[" + planeQueryParts.join(",") + "]";
- regions.push(region);
- }
- let regionsArg = regions.join(",");
- let pointcloudArgs = [];
- for(let pointcloud of this.viewer.scene.pointclouds){
- if(!pointcloud.visible){
- continue;
- }
- let offset = pointcloud.pcoGeometry.offset.clone();
- let negateOffset = new Matrix4().makeTranslation(...offset.multiplyScalar(-1).toArray());
- let matrixWorld = pointcloud.matrixWorld;
- let transform = new Matrix4().multiplyMatrices(matrixWorld, negateOffset);
- let path = `${window.location.pathname}/../${pointcloud.pcoGeometry.url}`;
- let arg = {
- path: path,
- transform: transform.elements,
- };
- let argString = JSON.stringify(arg);
- pointcloudArgs.push(argString);
- }
- let pointcloudsArg = pointcloudArgs.join(",");
- let elMessage = this.elContent.find("div[name=download_message]");
- let error = (message) => {
- elMessage.html(`<div style="color: #ff0000">ERROR: ${message}</div>`);
- };
- let info = (message) => {
- elMessage.html(`${message}`);
- };
- let handle = null;
- { // START FILTER
- let url = `${viewer.server}/create_regions_filter?pointclouds=[${pointcloudsArg}]®ions=[${regionsArg}]`;
-
- //console.log(url);
- info("estimating results ...");
- let response = await fetch(url);
- let jsResponse = await response.json();
- //console.log(jsResponse);
- if(!jsResponse.handle){
- error(jsResponse.message);
- return;
- }else {
- handle = jsResponse.handle;
- }
- }
- { // WAIT, CHECK PROGRESS, HANDLE FINISH
- let url = `${viewer.server}/check_regions_filter?handle=${handle}`;
- let sleep = (function(duration){
- return new Promise( (res, rej) => {
- setTimeout(() => {
- res();
- }, duration);
- });
- });
- let handleFiltering = (jsResponse) => {
- let {progress, estimate} = jsResponse;
- let progressFract = progress["processed points"] / estimate.points;
- let progressPercents = parseInt(progressFract * 100);
- info(`progress: ${progressPercents}%`);
- };
- let handleFinish = (jsResponse) => {
- let message = "downloads ready: <br>";
- message += "<ul>";
- for(let i = 0; i < jsResponse.pointclouds.length; i++){
- let url = `${viewer.server}/download_regions_filter_result?handle=${handle}&index=${i}`;
- message += `<li><a href="${url}">result_${i}.las</a> </li>\n`;
- }
- let reportURL = `${viewer.server}/download_regions_filter_report?handle=${handle}`;
- message += `<li> <a href="${reportURL}">report.json</a> </li>\n`;
- message += "</ul>";
- info(message);
- };
- let handleUnexpected = (jsResponse) => {
- let message = `Unexpected Response. <br>status: ${jsResponse.status} <br>message: ${jsResponse.message}`;
- info(message);
- };
- let handleError = (jsResponse) => {
- let message = `ERROR: ${jsResponse.message}`;
- error(message);
- throw new Error(message);
- };
- let start = Date.now();
- while(true){
- let response = await fetch(url);
- let jsResponse = await response.json();
- if(jsResponse.status === "ERROR"){
- handleError(jsResponse);
- }else if(jsResponse.status === "FILTERING"){
- handleFiltering(jsResponse);
- }else if(jsResponse.status === "FINISHED"){
- handleFinish(jsResponse);
- break;
- }else {
- handleUnexpected(jsResponse);
- }
- let durationS = (Date.now() - start) / 1000;
- let sleepAmountMS = durationS < 10 ? 100 : 1000;
- await sleep(sleepAmountMS);
- }
- }
- }
- update(){
- let elCoordiantesContainer = this.elContent.find('.coordinates_table_container');
- elCoordiantesContainer.empty();
- elCoordiantesContainer.append(this.createCoordinatesTable([this.measurement.position]));
- {
- let angles = this.measurement.rotation.toVector3();
- angles = angles.toArray();
- //angles = [angles.z, angles.x, angles.y];
- angles = angles.map(v => 180 * v / Math.PI);
- angles = angles.map(a => a.toFixed(1) + '\u00B0');
- let elAlpha = this.elContent.find(`#angle_cell_alpha`);
- let elBetta = this.elContent.find(`#angle_cell_betta`);
- let elGamma = this.elContent.find(`#angle_cell_gamma`);
- elAlpha.html(angles[0]);
- elBetta.html(angles[1]);
- elGamma.html(angles[2]);
- }
- {
- let dimensions = this.measurement.scale.toArray();
- dimensions = dimensions.map(v => Utils.addCommas(v.toFixed(2)));
- let elLength = this.elContent.find(`#cell_length`);
- let elWidth = this.elContent.find(`#cell_width`);
- let elHeight = this.elContent.find(`#cell_height`);
- elLength.html(dimensions[0]);
- elWidth.html(dimensions[1]);
- elHeight.html(dimensions[2]);
- }
- {
- let elVolume = this.elContent.find(`#measurement_volume`);
- let volume = this.measurement.getVolume();
- elVolume.html(Utils.addCommas(volume.toFixed(2)));
- }
- this.elCheckClip.prop("checked", this.measurement.clip);
- this.elCheckShow.prop("checked", this.measurement.visible);
- }
- };
- class ProfilePanel extends MeasurePanel{
- constructor(viewer, measurement, propertiesPanel){
- super(viewer, measurement, propertiesPanel);
- let removeIconPath = Potree.resourcePath + '/icons/remove.svg';
- this.elContent = $(`
- <div class="measurement_content selectable">
- <span class="coordinates_table_container"></span>
- <br>
- <span style="display:flex">
- <span style="display:flex; align-items: center; padding-right: 10px">Width: </span>
- <input id="sldProfileWidth" name="sldProfileWidth" value="5.06" style="flex-grow: 1; width:100%">
- </span>
- <br>
- <li style="margin-top: 10px">
- <input name="download_profile" type="button" value="prepare download" style="width: 100%" />
- <div name="download_message"></div>
- </li>
- <br>
- <input type="button" id="show_2d_profile" value="show 2d profile" style="width: 100%"/>
- <!-- ACTIONS -->
- <div style="display: flex; margin-top: 12px">
- <span></span>
- <span style="flex-grow: 1"></span>
- <img name="remove" class="button-icon" src="${removeIconPath}" style="width: 16px; height: 16px"/>
- </div>
- </div>
- `);
- this.elRemove = this.elContent.find("img[name=remove]");
- this.elRemove.click( () => {
- this.viewer.scene.removeProfile(measurement);
- });
- { // download
- this.elDownloadButton = this.elContent.find(`input[name=download_profile]`);
- if(this.propertiesPanel.viewer.server){
- this.elDownloadButton.click(() => this.download());
- } else {
- this.elDownloadButton.hide();
- }
- }
- { // width spinner
- let elWidthSlider = this.elContent.find(`#sldProfileWidth`);
- elWidthSlider.spinner({
- min: 0, max: 10 * 1000 * 1000, step: 0.01,
- numberFormat: 'n',
- start: () => {},
- spin: (event, ui) => {
- let value = elWidthSlider.spinner('value');
- measurement.setWidth(value);
- },
- change: (event, ui) => {
- let value = elWidthSlider.spinner('value');
- measurement.setWidth(value);
- },
- stop: (event, ui) => {
- let value = elWidthSlider.spinner('value');
- measurement.setWidth(value);
- },
- incremental: (count) => {
- let value = elWidthSlider.spinner('value');
- let step = elWidthSlider.spinner('option', 'step');
- let delta = value * 0.05;
- let increments = Math.max(1, parseInt(delta / step));
- return increments;
- }
- });
- elWidthSlider.spinner('value', measurement.getWidth());
- elWidthSlider.spinner('widget').css('width', '100%');
- let widthListener = (event) => {
- let value = elWidthSlider.spinner('value');
- if (value !== measurement.getWidth()) {
- elWidthSlider.spinner('value', measurement.getWidth());
- }
- };
- this.propertiesPanel.addVolatileListener(measurement, "width_changed", widthListener);
- }
- let elShow2DProfile = this.elContent.find(`#show_2d_profile`);
- elShow2DProfile.click(() => {
- this.propertiesPanel.viewer.profileWindow.show();
- this.propertiesPanel.viewer.profileWindowController.setProfile(measurement);
- });
- this.propertiesPanel.addVolatileListener(measurement, "marker_added", this._update);
- this.propertiesPanel.addVolatileListener(measurement, "marker_removed", this._update);
- this.propertiesPanel.addVolatileListener(measurement, "marker_moved", this._update);
- this.update();
- }
- update(){
- let elCoordiantesContainer = this.elContent.find('.coordinates_table_container');
- elCoordiantesContainer.empty();
- elCoordiantesContainer.append(this.createCoordinatesTable(this.measurement.points));
- }
- async download(){
- let profile = this.measurement;
- let regions = [];
- {
- let segments = profile.getSegments();
- let width = profile.width;
-
- for(let segment of segments){
- let start = segment.start.clone().multiply(new Vector3(1, 1, 0));
- let end = segment.end.clone().multiply(new Vector3(1, 1, 0));
- let center = new Vector3().addVectors(start, end).multiplyScalar(0.5);
-
- let startEndDir = new Vector3().subVectors(end, start).normalize();
- let endStartDir = new Vector3().subVectors(start, end).normalize();
- let upDir = new Vector3(0, 0, 1);
- let rightDir = new Vector3().crossVectors(startEndDir, upDir);
- let leftDir = new Vector3().crossVectors(endStartDir, upDir);
-
- console.log(leftDir);
-
- let right = rightDir.clone().multiplyScalar(width * 0.5).add(center);
- let left = leftDir.clone().multiplyScalar(width * 0.5).add(center);
-
- let planes = [
- new Plane().setFromNormalAndCoplanarPoint(startEndDir, start),
- new Plane().setFromNormalAndCoplanarPoint(endStartDir, end),
- new Plane().setFromNormalAndCoplanarPoint(leftDir, right),
- new Plane().setFromNormalAndCoplanarPoint(rightDir, left),
- ];
-
- let planeQueryParts = [];
- for(let plane of planes){
- let part = [plane.normal.toArray(), plane.constant].join(",");
- part = `[${part}]`;
- planeQueryParts.push(part);
- }
- let region = "[" + planeQueryParts.join(",") + "]";
- regions.push(region);
- }
- }
- let regionsArg = regions.join(",");
- let pointcloudArgs = [];
- for(let pointcloud of this.viewer.scene.pointclouds){
- if(!pointcloud.visible){
- continue;
- }
- let offset = pointcloud.pcoGeometry.offset.clone();
- let negateOffset = new Matrix4().makeTranslation(...offset.multiplyScalar(-1).toArray());
- let matrixWorld = pointcloud.matrixWorld;
- let transform = new Matrix4().multiplyMatrices(matrixWorld, negateOffset);
- let path = `${window.location.pathname}/../${pointcloud.pcoGeometry.url}`;
- let arg = {
- path: path,
- transform: transform.elements,
- };
- let argString = JSON.stringify(arg);
- pointcloudArgs.push(argString);
- }
- let pointcloudsArg = pointcloudArgs.join(",");
- let elMessage = this.elContent.find("div[name=download_message]");
- let error = (message) => {
- elMessage.html(`<div style="color: #ff0000">ERROR: ${message}</div>`);
- };
- let info = (message) => {
- elMessage.html(`${message}`);
- };
- let handle = null;
- { // START FILTER
- let url = `${viewer.server}/create_regions_filter?pointclouds=[${pointcloudsArg}]®ions=[${regionsArg}]`;
-
- //console.log(url);
- info("estimating results ...");
- let response = await fetch(url);
- let jsResponse = await response.json();
- //console.log(jsResponse);
- if(!jsResponse.handle){
- error(jsResponse.message);
- return;
- }else {
- handle = jsResponse.handle;
- }
- }
- { // WAIT, CHECK PROGRESS, HANDLE FINISH
- let url = `${viewer.server}/check_regions_filter?handle=${handle}`;
- let sleep = (function(duration){
- return new Promise( (res, rej) => {
- setTimeout(() => {
- res();
- }, duration);
- });
- });
- let handleFiltering = (jsResponse) => {
- let {progress, estimate} = jsResponse;
- let progressFract = progress["processed points"] / estimate.points;
- let progressPercents = parseInt(progressFract * 100);
- info(`progress: ${progressPercents}%`);
- };
- let handleFinish = (jsResponse) => {
- let message = "downloads ready: <br>";
- message += "<ul>";
- for(let i = 0; i < jsResponse.pointclouds.length; i++){
- let url = `${viewer.server}/download_regions_filter_result?handle=${handle}&index=${i}`;
- message += `<li><a href="${url}">result_${i}.las</a> </li>\n`;
- }
- let reportURL = `${viewer.server}/download_regions_filter_report?handle=${handle}`;
- message += `<li> <a href="${reportURL}">report.json</a> </li>\n`;
- message += "</ul>";
- info(message);
- };
- let handleUnexpected = (jsResponse) => {
- let message = `Unexpected Response. <br>status: ${jsResponse.status} <br>message: ${jsResponse.message}`;
- info(message);
- };
- let handleError = (jsResponse) => {
- let message = `ERROR: ${jsResponse.message}`;
- error(message);
- throw new Error(message);
- };
- let start = Date.now();
- while(true){
- let response = await fetch(url);
- let jsResponse = await response.json();
- if(jsResponse.status === "ERROR"){
- handleError(jsResponse);
- }else if(jsResponse.status === "FILTERING"){
- handleFiltering(jsResponse);
- }else if(jsResponse.status === "FINISHED"){
- handleFinish(jsResponse);
- break;
- }else {
- handleUnexpected(jsResponse);
- }
- let durationS = (Date.now() - start) / 1000;
- let sleepAmountMS = durationS < 10 ? 100 : 1000;
- await sleep(sleepAmountMS);
- }
- }
- }
- };
- class CameraPanel{
- constructor(viewer, propertiesPanel){
- this.viewer = viewer;
- this.propertiesPanel = propertiesPanel;
- this._update = () => { this.update(); };
- let copyIconPath = Potree.resourcePath + '/icons/copy.svg';
- this.elContent = $(`
- <div class="propertypanel_content">
- <table>
- <tr>
- <th colspan="3">position</th>
- <th></th>
- </tr>
- <tr>
- <td align="center" id="camera_position_x" style="width: 25%"></td>
- <td align="center" id="camera_position_y" style="width: 25%"></td>
- <td align="center" id="camera_position_z" style="width: 25%"></td>
- <td align="right" id="copy_camera_position" style="width: 25%">
- <img name="copyPosition" title="copy" class="button-icon" src="${copyIconPath}" style="width: 16px; height: 16px"/>
- </td>
- </tr>
- <tr>
- <th colspan="3">target</th>
- <th></th>
- </tr>
- <tr>
- <td align="center" id="camera_target_x" style="width: 25%"></td>
- <td align="center" id="camera_target_y" style="width: 25%"></td>
- <td align="center" id="camera_target_z" style="width: 25%"></td>
- <td align="right" id="copy_camera_target" style="width: 25%">
- <img name="copyTarget" title="copy" class="button-icon" src="${copyIconPath}" style="width: 16px; height: 16px"/>
- </td>
- </tr>
- </table>
- </div>
- `);
- this.elCopyPosition = this.elContent.find("img[name=copyPosition]");
- this.elCopyPosition.click( () => {
- let pos = this.viewer.scene.getActiveCamera().position.toArray();
- let msg = pos.map(c => c.toFixed(3)).join(", ");
- Utils.clipboardCopy(msg);
- this.viewer.postMessage(
- `Copied value to clipboard: <br>'${msg}'`,
- {duration: 3000});
- });
- this.elCopyTarget = this.elContent.find("img[name=copyTarget]");
- this.elCopyTarget.click( () => {
- let pos = this.viewer.scene.view.getPivot().toArray();
- let msg = pos.map(c => c.toFixed(3)).join(", ");
- Utils.clipboardCopy(msg);
- this.viewer.postMessage(
- `Copied value to clipboard: <br>'${msg}'`,
- {duration: 3000});
- });
- this.propertiesPanel.addVolatileListener(viewer, "camera_changed", this._update);
- this.update();
- }
- update(){
- //console.log("updating camera panel");
- let camera = this.viewer.scene.getActiveCamera();
- let view = this.viewer.scene.view;
- let pos = camera.position.toArray().map(c => Utils.addCommas(c.toFixed(3)));
- this.elContent.find("#camera_position_x").html(pos[0]);
- this.elContent.find("#camera_position_y").html(pos[1]);
- this.elContent.find("#camera_position_z").html(pos[2]);
- let target = view.getPivot().toArray().map(c => Utils.addCommas(c.toFixed(3)));
- this.elContent.find("#camera_target_x").html(target[0]);
- this.elContent.find("#camera_target_y").html(target[1]);
- this.elContent.find("#camera_target_z").html(target[2]);
- }
- };
- class AnnotationPanel{
- constructor(viewer, propertiesPanel, annotation){
- this.viewer = viewer;
- this.propertiesPanel = propertiesPanel;
- this.annotation = annotation;
- this._update = () => { this.update(); };
- let copyIconPath = `${Potree.resourcePath}/icons/copy.svg`;
- this.elContent = $(`
- <div class="propertypanel_content">
- <table>
- <tr>
- <th colspan="3">position</th>
- <th></th>
- </tr>
- <tr>
- <td align="center" id="annotation_position_x" style="width: 25%"></td>
- <td align="center" id="annotation_position_y" style="width: 25%"></td>
- <td align="center" id="annotation_position_z" style="width: 25%"></td>
- <td align="right" id="copy_annotation_position" style="width: 25%">
- <img name="copyPosition" title="copy" class="button-icon" src="${copyIconPath}" style="width: 16px; height: 16px"/>
- </td>
- </tr>
- </table>
- <div>
- <div class="heading">Title</div>
- <div id="annotation_title" contenteditable="true">
- Annotation Title
- </div>
- <div class="heading">Description</div>
- <div id="annotation_description" contenteditable="true">
- A longer description of this annotation.
- Can be multiple lines long. TODO: the user should be able
- to modify title and description.
- </div>
- </div>
- </div>
- `);
- this.elCopyPosition = this.elContent.find("img[name=copyPosition]");
- this.elCopyPosition.click( () => {
- let pos = this.annotation.position.toArray();
- let msg = pos.map(c => c.toFixed(3)).join(", ");
- Utils.clipboardCopy(msg);
- this.viewer.postMessage(
- `Copied value to clipboard: <br>'${msg}'`,
- {duration: 3000});
- });
- this.elTitle = this.elContent.find("#annotation_title").html(annotation.title);
- this.elDescription = this.elContent.find("#annotation_description").html(annotation.description);
- this.elTitle[0].addEventListener("input", () => {
- const title = this.elTitle.html();
- annotation.title = title;
- }, false);
- this.elDescription[0].addEventListener("input", () => {
- const description = this.elDescription.html();
- annotation.description = description;
- }, false);
- this.update();
- }
- update(){
- const {annotation, elContent, elTitle, elDescription} = this;
- let pos = annotation.position.toArray().map(c => Utils.addCommas(c.toFixed(3)));
- elContent.find("#annotation_position_x").html(pos[0]);
- elContent.find("#annotation_position_y").html(pos[1]);
- elContent.find("#annotation_position_z").html(pos[2]);
- elTitle.html(annotation.title);
- elDescription.html(annotation.description);
- }
- };
- class CameraAnimationPanel{
- constructor(viewer, propertiesPanel, animation){
- this.viewer = viewer;
- this.propertiesPanel = propertiesPanel;
- this.animation = animation;
- this.elContent = $(`
- <div class="propertypanel_content">
- <span id="animation_keyframes"></span>
- <span>
- <span style="display:flex">
- <span style="display:flex; align-items: center; padding-right: 10px">Duration: </span>
- <input name="spnDuration" value="5.0" style="flex-grow: 1; width:100%">
- </span>
- <span>Time: </span><span id="lblTime"></span> <div id="sldTime"></div>
- <input name="play" type="button" value="play"/>
- </span>
- </div>
- `);
- const elPlay = this.elContent.find("input[name=play]");
- elPlay.click( () => {
- animation.play();
- });
- const elSlider = this.elContent.find('#sldTime');
- elSlider.slider({
- value: 0,
- min: 0,
- max: 1,
- step: 0.001,
- slide: (event, ui) => {
- animation.set(ui.value);
- animation.updateFrustum();
- }
- });
- let elDuration = this.elContent.find(`input[name=spnDuration]`);
- elDuration.spinner({
- min: 0, max: 300, step: 0.01,
- numberFormat: 'n',
- start: () => {},
- spin: (event, ui) => {
- let value = elDuration.spinner('value');
- animation.setDuration(value);
- },
- change: (event, ui) => {
- let value = elDuration.spinner('value');
- animation.setDuration(value);
- },
- stop: (event, ui) => {
- let value = elDuration.spinner('value');
- animation.setDuration(value);
- },
- incremental: (count) => {
- let value = elDuration.spinner('value');
- let step = elDuration.spinner('option', 'step');
- let delta = value * 0.05;
- let increments = Math.max(1, parseInt(delta / step));
- return increments;
- }
- });
- elDuration.spinner('value', animation.getDuration());
- elDuration.spinner('widget').css('width', '100%');
- const elKeyframes = this.elContent.find("#animation_keyframes");
- const updateKeyframes = () => {
- elKeyframes.empty();
- //let index = 0;
- // <span style="flex-grow: 0;">
- // <img name="add" src="${Potree.resourcePath}/icons/add.svg" style="width: 1.5em; height: 1.5em"/>
- // </span>
- const addNewKeyframeItem = (index) => {
- let elNewKeyframe = $(`
- <div style="display: flex; margin: 0.2em 0em">
- <span style="flex-grow: 1"></span>
- <input type="button" name="add" value="insert control point" />
- <span style="flex-grow: 1"></span>
- </div>
- `);
- const elAdd = elNewKeyframe.find("input[name=add]");
- elAdd.click( () => {
- animation.createControlPoint(index);
- animation.changeCallback();
- });
- elKeyframes.append(elNewKeyframe);
- };
- const addKeyframeItem = (index) => {
- let elKeyframe = $(`
- <div style="display: flex; margin: 0.2em 0em">
- <span style="flex-grow: 0;">
- <img name="assign" src="${Potree.resourcePath}/icons/assign.svg" style="width: 1.5em; height: 1.5em"/>
- </span>
- <span style="flex-grow: 0;">
- <img name="move" src="${Potree.resourcePath}/icons/circled_dot.svg" style="width: 1.5em; height: 1.5em"/>
- </span>
- <span style="flex-grow: 0; width: 1.5em; height: 1.5em"></span>
- <span style="flex-grow: 0; font-size: 1.5em">keyframe</span>
- <span style="flex-grow: 1"></span>
- <span style="flex-grow: 0;">
- <img name="delete" src="${Potree.resourcePath}/icons/remove.svg" style="width: 1.5em; height: 1.5em"/>
- </span>
- </div>
- `);
- const elAssign = elKeyframe.find("img[name=assign]");
- const elMove = elKeyframe.find("img[name=move]");
- const elDelete = elKeyframe.find("img[name=delete]");
- elAssign.click( () => {
- animation.posCurve.points[index].copy(viewer.scene.view.position);
- animation.targetCurve.points[index].copy(viewer.scene.view.getPivot());
- animation.changeCallback();
-
- });
- elMove.click( () => {
- viewer.scene.view.position.copy(animation.posCurve.points[index]);
- viewer.scene.view.lookAt(animation.targetCurve.points[index]);
- });
- elDelete.click( () => {
- animation.removeControlPoint(index);
- animation.changeCallback();
- });
- elKeyframes.append(elKeyframe);
- };
- let index = 0;
- addNewKeyframeItem(index);
-
- animation.posCurve.points.forEach(e=>{
- addKeyframeItem(index);
- index++;
- addNewKeyframeItem(index);
- });
-
- };
- updateKeyframes();
- animation.addEventListener("controlpoint_added", updateKeyframes);
- animation.addEventListener("controlpoint_removed", updateKeyframes);
- // this._update = () => { this.update(); };
- // this.update();
- }
- update(){
-
- }
- };
- class PropertiesPanel{
- constructor(container, viewer){
- this.container = container;
- this.viewer = viewer;
- this.object = null;
- this.cleanupTasks = [];
- this.scene = null;
- }
- setScene(scene){
- this.scene = scene;
- }
- set(object){
- if(this.object === object){
- return;
- }
- this.object = object;
-
- for(let task of this.cleanupTasks){
- task();
- }
- this.cleanupTasks = [];
- this.container.empty();
- if(object instanceof PointCloudTree){
- this.setPointCloud(object);
- }else if(object instanceof Measure || object instanceof Profile || object instanceof Volume){
- this.setMeasurement(object);
- }else if(object instanceof Camera){
- this.setCamera(object);
- }else if(object instanceof Annotation){
- this.setAnnotation(object);
- }else if(object instanceof CameraAnimation){
- this.setCameraAnimation(object);
- }
-
- }
- //
- // Used for events that should be removed when the property object changes.
- // This is for listening to materials, scene, point clouds, etc.
- // not required for DOM listeners, since they are automatically cleared by removing the DOM subtree.
- //
- addVolatileListener(target, type, callback){
- target.addEventListener(type, callback);
- this.cleanupTasks.push(() => {
- target.removeEventListener(type, callback);
- });
- }
- setPointCloud(pointcloud){
- let material = pointcloud.material;
- let panel = $(`
- <div class="scene_content selectable">
- <ul class="pv-menu-list">
- <li>
- <span data-i18n="appearance.point_size"></span>: <span id="lblPointSize"></span> <div id="sldPointSize"></div>
- </li>
- <li>
- <span data-i18n="appearance.min_point_size"></span>: <span id="lblMinPointSize"></span> <div id="sldMinPointSize"></div>
- </li>
- <!-- SIZE TYPE -->
- <li>
- <label for="optPointSizing" class="pv-select-label" data-i18n="appearance.point_size_type">Point Sizing </label>
- <select id="optPointSizing" name="optPointSizing">
- <option>FIXED</option>
- <option>ATTENUATED</option>
- <option>ADAPTIVE</option>
- </select>
- </li>
- <!-- SHAPE -->
- <li>
- <label for="optShape" class="pv-select-label" data-i18n="appearance.point_shape"></label><br>
- <select id="optShape" name="optShape">
- <option>SQUARE</option>
- <option>CIRCLE</option>
- <option>PARABOLOID</option>
- </select>
- </li>
- <li id="materials_backface_container">
- <label><input id="set_backface_culling" type="checkbox" /><span data-i18n="appearance.backface_culling"></span></label>
- </li>
-
- <!-- OPACITY -->
- <li><span data-i18n="appearance.point_opacity"></span>:<span id="lblOpacity"></span><div id="sldOpacity"></div></li>
- <div class="divider">
- <span>Attribute</span>
- </div>
- <li>
- <select id="optMaterial" name="optMaterial"></select>
- </li>
- <div id="materials.composite_weight_container">
- <div class="divider">
- <span>Attribute Weights</span>
- </div>
- <li>RGB: <span id="lblWeightRGB"></span> <div id="sldWeightRGB"></div> </li>
- <li>Intensity: <span id="lblWeightIntensity"></span> <div id="sldWeightIntensity"></div> </li>
- <li>Elevation: <span id="lblWeightElevation"></span> <div id="sldWeightElevation"></div> </li>
- <li>Classification: <span id="lblWeightClassification"></span> <div id="sldWeightClassification"></div> </li>
- <li>Return Number: <span id="lblWeightReturnNumber"></span> <div id="sldWeightReturnNumber"></div> </li>
- <li>Source ID: <span id="lblWeightSourceID"></span> <div id="sldWeightSourceID"></div> </li>
- </div>
- <div id="materials.rgb_container">
- <div class="divider">
- <span>RGB</span>
- </div>
- <li>Gamma: <span id="lblRGBGamma"></span> <div id="sldRGBGamma"></div> </li>
- <li>Brightness: <span id="lblRGBBrightness"></span> <div id="sldRGBBrightness"></div> </li>
- <li>Contrast: <span id="lblRGBContrast"></span> <div id="sldRGBContrast"></div> </li>
- </div>
- <div id="materials.extra_container">
- <div class="divider">
- <span>Extra Attribute</span>
- </div>
- <li><span data-i18n="appearance.extra_range"></span>: <span id="lblExtraRange"></span> <div id="sldExtraRange"></div></li>
- <li>Gamma: <span id="lblExtraGamma"></span> <div id="sldExtraGamma"></div></li>
- <li>Brightness: <span id="lblExtraBrightness"></span> <div id="sldExtraBrightness"></div></li>
- <li>Contrast: <span id="lblExtraContrast"></span> <div id="sldExtraContrast"></div></li>
- </div>
-
- <div id="materials.matcap_container">
- <div class="divider">
- <span>MATCAP</span>
- </div>
- <li>
- <div id="matcap_scheme_selection" style="display: flex; flex-wrap: wrap;"> </div>
- </li>
- </div>
- <div id="materials.color_container">
- <div class="divider">
- <span>Color</span>
- </div>
- <input id="materials.color.picker" />
- </div>
- <div id="materials.elevation_container">
- <div class="divider">
- <span>Elevation</span>
- </div>
- <li><span data-i18n="appearance.elevation_range"></span>: <span id="lblHeightRange"></span> <div id="sldHeightRange"></div> </li>
- <li>
- <selectgroup id="gradient_repeat_option">
- <option id="gradient_repeat_clamp" value="CLAMP">Clamp</option>
- <option id="gradient_repeat_repeat" value="REPEAT">Repeat</option>
- <option id="gradient_repeat_mirrored_repeat" value="MIRRORED_REPEAT">Mirrored Repeat</option>
- </selectgroup>
- </li>
- <li>
- <span>Gradient Scheme:</span>
- <div id="elevation_gradient_scheme_selection" style="display: flex; padding: 1em 0em">
- </div>
- </li>
- </div>
- <div id="materials.transition_container">
- <div class="divider">
- <span>Transition</span>
- </div>
- <li>transition: <span id="lblTransition"></span> <div id="sldTransition"></div> </li>
- </div>
- <div id="materials.intensity_container">
- <div class="divider">
- <span>Intensity</span>
- </div>
- <li>Range: <span id="lblIntensityRange"></span> <div id="sldIntensityRange"></div> </li>
- <li>Gamma: <span id="lblIntensityGamma"></span> <div id="sldIntensityGamma"></div> </li>
- <li>Brightness: <span id="lblIntensityBrightness"></span> <div id="sldIntensityBrightness"></div> </li>
- <li>Contrast: <span id="lblIntensityContrast"></span> <div id="sldIntensityContrast"></div> </li>
- </div>
- <div id="materials.gpstime_container">
- <div class="divider">
- <span>GPS Time</span>
- </div>
- </div>
-
- <div id="materials.index_container">
- <div class="divider">
- <span>Indices</span>
- </div>
- </div>
- </ul>
- </div>
- `);
- panel.i18n();
- this.container.append(panel);
- { // POINT SIZE
- let sldPointSize = panel.find(`#sldPointSize`);
- let lblPointSize = panel.find(`#lblPointSize`);
- sldPointSize.slider({
- value: material.size,
- min: 0,
- max: 3,
- step: 0.01,
- slide: function (event, ui) { material.size = ui.value; }
- });
- let update = (e) => {
- lblPointSize.html(material.size.toFixed(2));
- sldPointSize.slider({value: material.size});
- };
- this.addVolatileListener(material, "point_size_changed", update);
-
- update();
- }
- { // MINIMUM POINT SIZE
- let sldMinPointSize = panel.find(`#sldMinPointSize`);
- let lblMinPointSize = panel.find(`#lblMinPointSize`);
- sldMinPointSize.slider({
- value: material.size,
- min: 0,
- max: 3,
- step: 0.01,
- slide: function (event, ui) { material.minSize = ui.value; }
- });
- let update = (e) => {
- lblMinPointSize.html(material.minSize.toFixed(2));
- sldMinPointSize.slider({value: material.minSize});
- };
- this.addVolatileListener(material, "point_size_changed", update);
-
- update();
- }
- { // POINT SIZING
- let strSizeType = Object.keys(PointSizeType)[material.pointSizeType];
- let opt = panel.find(`#optPointSizing`);
- opt.selectmenu();
- opt.val(strSizeType).selectmenu('refresh');
- opt.selectmenu({
- change: (event, ui) => {
- material.pointSizeType = PointSizeType[ui.item.value];
- }
- });
- }
- { // SHAPE
- let opt = panel.find(`#optShape`);
- opt.selectmenu({
- change: (event, ui) => {
- let value = ui.item.value;
- material.shape = PointShape[value];
- }
- });
- let update = () => {
- let typename = Object.keys(PointShape)[material.shape];
- opt.selectmenu().val(typename).selectmenu('refresh');
- };
- this.addVolatileListener(material, "point_shape_changed", update);
- update();
- }
- { // BACKFACE CULLING
-
- let opt = panel.find(`#set_backface_culling`);
- opt.click(() => {
- material.backfaceCulling = opt.prop("checked");
- });
- let update = () => {
- let value = material.backfaceCulling;
- opt.prop("checked", value);
- };
- this.addVolatileListener(material, "backface_changed", update);
- update();
- let blockBackface = $('#materials_backface_container');
- blockBackface.css('display', 'none');
- const pointAttributes = pointcloud.pcoGeometry.pointAttributes;
- const hasNormals = pointAttributes.hasNormals ? pointAttributes.hasNormals() : false;
- if(hasNormals) {
- blockBackface.css('display', 'block');
- }
- /*
- opt.checkboxradio({
- clicked: (event, ui) => {
- // let value = ui.item.value;
- let value = ui.item.checked;
- console.log(value);
- material.backfaceCulling = value; // $('#set_freeze').prop("checked");
- }
- });
- */
- }
- { // OPACITY
- let sldOpacity = panel.find(`#sldOpacity`);
- let lblOpacity = panel.find(`#lblOpacity`);
- sldOpacity.slider({
- value: material.opacity,
- min: 0,
- max: 1,
- step: 0.001,
- slide: function (event, ui) {
- material.opacity = ui.value;
- }
- });
- let update = (e) => {
- lblOpacity.html(material.opacity.toFixed(2));
- sldOpacity.slider({value: material.opacity});
- };
- this.addVolatileListener(material, "opacity_changed", update);
- update();
- }
- {
- const attributes = pointcloud.pcoGeometry.pointAttributes.attributes;
- let options = [];
- options.push(...attributes.map(a => a.name));
- const intensityIndex = options.indexOf("intensity");
- if(intensityIndex >= 0){
- options.splice(intensityIndex + 1, 0, "intensity gradient");
- }
- options.push(
- "elevation",
- "color",
- 'matcap',
- 'indices',
- 'level of detail',
- 'composite'
- );
- const blacklist = [
- "POSITION_CARTESIAN",
- "position",
- ];
- options = options.filter(o => !blacklist.includes(o));
- let attributeSelection = panel.find('#optMaterial');
- for(let option of options){
- let elOption = $(`<option>${option}</option>`);
- attributeSelection.append(elOption);
- }
- let updateMaterialPanel = (event, ui) => {
- let selectedValue = attributeSelection.selectmenu().val();
- material.activeAttributeName = selectedValue;
- let attribute = pointcloud.getAttribute(selectedValue);
- if(selectedValue === "intensity gradient"){
- attribute = pointcloud.getAttribute("intensity");
- }
- const isIntensity = attribute ? ["intensity", "intensity gradient"].includes(attribute.name) : false;
- if(isIntensity){
- if(pointcloud.material.intensityRange[0] === Infinity){
- pointcloud.material.intensityRange = attribute.range;
- }
- const [min, max] = attribute.range;
- panel.find('#sldIntensityRange').slider({
- range: true,
- min: min, max: max, step: 0.01,
- values: [min, max],
- slide: (event, ui) => {
- let min = ui.values[0];
- let max = ui.values[1];
- material.intensityRange = [min, max];
- }
- });
- } else if(attribute){
- let [min, max] = attribute.range;
-
- let selectedRange = material.getRange(attribute.name);
- if(!selectedRange){
- selectedRange = [...attribute.range];
- }
- let minMaxAreNumbers = typeof min === "number" && typeof max === "number";
- /* min = 0 ;max = 50
- selectedRange[0] =min; selectedRange[1] = max; */
- if(minMaxAreNumbers){
- panel.find('#sldExtraRange').slider({
- range: true,
- min: min,
- max: max,
- step: 0.01,
- values: selectedRange,
- slide: (event, ui) => {
- let [a, b] = ui.values;
- material.setRange(attribute.name, [a, b]);
- }
- });
- }
- }
- let blockWeights = $('#materials\\.composite_weight_container');
- let blockElevation = $('#materials\\.elevation_container');
- let blockRGB = $('#materials\\.rgb_container');
- let blockExtra = $('#materials\\.extra_container');
- let blockColor = $('#materials\\.color_container');
- let blockIntensity = $('#materials\\.intensity_container');
- let blockIndex = $('#materials\\.index_container');
- let blockTransition = $('#materials\\.transition_container');
- let blockGps = $('#materials\\.gpstime_container');
- let blockMatcap = $('#materials\\.matcap_container');
- blockIndex.css('display', 'none');
- blockIntensity.css('display', 'none');
- blockElevation.css('display', 'none');
- blockRGB.css('display', 'none');
- blockExtra.css('display', 'none');
- blockColor.css('display', 'none');
- blockWeights.css('display', 'none');
- blockTransition.css('display', 'none');
- blockMatcap.css('display', 'none');
- blockGps.css('display', 'none');
- if (selectedValue === 'composite') {
- blockWeights.css('display', 'block');
- blockElevation.css('display', 'block');
- blockRGB.css('display', 'block');
- blockIntensity.css('display', 'block');
- } else if (selectedValue === 'elevation') {
- blockElevation.css('display', 'block');
- } else if (selectedValue === 'RGB and Elevation') {
- blockRGB.css('display', 'block');
- blockElevation.css('display', 'block');
- } else if (selectedValue === 'rgba') {
- blockRGB.css('display', 'block');
- } else if (selectedValue === 'color') {
- blockColor.css('display', 'block');
- } else if (selectedValue === 'intensity') {
- blockIntensity.css('display', 'block');
- } else if (selectedValue === 'intensity gradient') {
- blockIntensity.css('display', 'block');
- } else if (selectedValue === "indices" ){
- blockIndex.css('display', 'block');
- } else if (selectedValue === "matcap" ){
- blockMatcap.css('display', 'block');
- } else if (selectedValue === "classification" ){
- // add classification color selctor?
- } else if (selectedValue === "gps-time" ){
- blockGps.css('display', 'block');
- } else if(selectedValue === "number of returns"){
-
- } else if(selectedValue === "return number"){
-
- } else if(["source id", "point source id"].includes(selectedValue)){
-
- } else {
- blockExtra.css('display', 'block');
- }
- };
- attributeSelection.selectmenu({change: updateMaterialPanel});
- let update = () => {
- attributeSelection.val(material.activeAttributeName).selectmenu('refresh');
- };
- this.addVolatileListener(material, "point_color_type_changed", update);
- this.addVolatileListener(material, "active_attribute_changed", update);
- update();
- updateMaterialPanel();
- }
- {
- const schemes = Object.keys(Potree.Gradients).map(name => ({name: name, values: Gradients[name]}));
- let elSchemeContainer = panel.find("#elevation_gradient_scheme_selection");
- for(let scheme of schemes){
- let elScheme = $(`
- <span style="flex-grow: 1;">
- </span>
- `);
- const svg = Potree.Utils.createSvgGradient(scheme.values);
- svg.setAttributeNS(null, "class", `button-icon`);
- elScheme.append($(svg));
- elScheme.click( () => {
- material.gradient = Gradients[scheme.name];
- });
- elSchemeContainer.append(elScheme);
- }
- }
- {
- let matcaps = [
- {name: "Normals", icon: `${Potree.resourcePath}/icons/matcap/check_normal+y.jpg`},
- {name: "Basic 1", icon: `${Potree.resourcePath}/icons/matcap/basic_1.jpg`},
- {name: "Basic 2", icon: `${Potree.resourcePath}/icons/matcap/basic_2.jpg`},
- {name: "Basic Dark", icon: `${Potree.resourcePath}/icons/matcap/basic_dark.jpg`},
- {name: "Basic Side", icon: `${Potree.resourcePath}/icons/matcap/basic_side.jpg`},
- {name: "Ceramic Dark", icon: `${Potree.resourcePath}/icons/matcap/ceramic_dark.jpg`},
- {name: "Ceramic Lightbulb", icon: `${Potree.resourcePath}/icons/matcap/ceramic_lightbulb.jpg`},
- {name: "Clay Brown", icon: `${Potree.resourcePath}/icons/matcap/clay_brown.jpg`},
- {name: "Clay Muddy", icon: `${Potree.resourcePath}/icons/matcap/clay_muddy.jpg`},
- {name: "Clay Studio", icon: `${Potree.resourcePath}/icons/matcap/clay_studio.jpg`},
- {name: "Resin", icon: `${Potree.resourcePath}/icons/matcap/resin.jpg`},
- {name: "Skin", icon: `${Potree.resourcePath}/icons/matcap/skin.jpg`},
- {name: "Jade", icon: `${Potree.resourcePath}/icons/matcap/jade.jpg`},
- {name: "Metal_ Anisotropic", icon: `${Potree.resourcePath}/icons/matcap/metal_anisotropic.jpg`},
- {name: "Metal Carpaint", icon: `${Potree.resourcePath}/icons/matcap/metal_carpaint.jpg`},
- {name: "Metal Lead", icon: `${Potree.resourcePath}/icons/matcap/metal_lead.jpg`},
- {name: "Metal Shiny", icon: `${Potree.resourcePath}/icons/matcap/metal_shiny.jpg`},
- {name: "Pearl", icon: `${Potree.resourcePath}/icons/matcap/pearl.jpg`},
- {name: "Toon", icon: `${Potree.resourcePath}/icons/matcap/toon.jpg`},
- {name: "Check Rim Light", icon: `${Potree.resourcePath}/icons/matcap/check_rim_light.jpg`},
- {name: "Check Rim Dark", icon: `${Potree.resourcePath}/icons/matcap/check_rim_dark.jpg`},
- {name: "Contours 1", icon: `${Potree.resourcePath}/icons/matcap/contours_1.jpg`},
- {name: "Contours 2", icon: `${Potree.resourcePath}/icons/matcap/contours_2.jpg`},
- {name: "Contours 3", icon: `${Potree.resourcePath}/icons/matcap/contours_3.jpg`},
- {name: "Reflection Check Horizontal", icon: `${Potree.resourcePath}/icons/matcap/reflection_check_horizontal.jpg`},
- {name: "Reflection Check Vertical", icon: `${Potree.resourcePath}/icons/matcap/reflection_check_vertical.jpg`},
- ];
- let elMatcapContainer = panel.find("#matcap_scheme_selection");
- for(let matcap of matcaps){
- let elMatcap = $(`
- <img src="${matcap.icon}" class="button-icon" style="width: 25%;" />
- `);
- elMatcap.click( () => {
- material.matcap = matcap.icon.substring(matcap.icon.lastIndexOf('/'));
- });
- elMatcapContainer.append(elMatcap);
- }
- }
- {
- panel.find('#sldRGBGamma').slider({
- value: material.rgbGamma,
- min: 0, max: 4, step: 0.01,
- slide: (event, ui) => {material.rgbGamma = ui.value;}
- });
- panel.find('#sldRGBContrast').slider({
- value: material.rgbContrast,
- min: -1, max: 1, step: 0.01,
- slide: (event, ui) => {material.rgbContrast = ui.value;}
- });
- panel.find('#sldRGBBrightness').slider({
- value: material.rgbBrightness,
- min: -1, max: 1, step: 0.01,
- slide: (event, ui) => {material.rgbBrightness = ui.value;}
- });
- panel.find('#sldExtraGamma').slider({
- value: material.extraGamma,
- min: 0, max: 4, step: 0.01,
- slide: (event, ui) => {material.extraGamma = ui.value;}
- });
- panel.find('#sldExtraBrightness').slider({
- value: material.extraBrightness,
- min: -1, max: 1, step: 0.01,
- slide: (event, ui) => {material.extraBrightness = ui.value;}
- });
- panel.find('#sldExtraContrast').slider({
- value: material.extraContrast,
- min: -1, max: 1, step: 0.01,
- slide: (event, ui) => {material.extraContrast = ui.value;}
- });
- panel.find('#sldHeightRange').slider({
- range: true,
- min: 0, max: 1000, step: 0.01,
- values: [0, 1000],
- slide: (event, ui) => {
- material.heightMin = ui.values[0];
- material.heightMax = ui.values[1];
- }
- });
- panel.find('#sldIntensityGamma').slider({
- value: material.intensityGamma,
- min: 0, max: 4, step: 0.01,
- slide: (event, ui) => {material.intensityGamma = ui.value;}
- });
- panel.find('#sldIntensityContrast').slider({
- value: material.intensityContrast,
- min: -1, max: 1, step: 0.01,
- slide: (event, ui) => {material.intensityContrast = ui.value;}
- });
- panel.find('#sldIntensityBrightness').slider({
- value: material.intensityBrightness,
- min: -1, max: 1, step: 0.01,
- slide: (event, ui) => {material.intensityBrightness = ui.value;}
- });
- panel.find('#sldWeightRGB').slider({
- value: material.weightRGB,
- min: 0, max: 1, step: 0.01,
- slide: (event, ui) => {material.weightRGB = ui.value;}
- });
- panel.find('#sldWeightIntensity').slider({
- value: material.weightIntensity,
- min: 0, max: 1, step: 0.01,
- slide: (event, ui) => {material.weightIntensity = ui.value;}
- });
- panel.find('#sldWeightElevation').slider({
- value: material.weightElevation,
- min: 0, max: 1, step: 0.01,
- slide: (event, ui) => {material.weightElevation = ui.value;}
- });
- panel.find('#sldWeightClassification').slider({
- value: material.weightClassification,
- min: 0, max: 1, step: 0.01,
- slide: (event, ui) => {material.weightClassification = ui.value;}
- });
- panel.find('#sldWeightReturnNumber').slider({
- value: material.weightReturnNumber,
- min: 0, max: 1, step: 0.01,
- slide: (event, ui) => {material.weightReturnNumber = ui.value;}
- });
- panel.find('#sldWeightSourceID').slider({
- value: material.weightSourceID,
- min: 0, max: 1, step: 0.01,
- slide: (event, ui) => {material.weightSourceID = ui.value;}
- });
- panel.find(`#materials\\.color\\.picker`).spectrum({
- flat: true,
- showInput: true,
- preferredFormat: 'rgb',
- cancelText: '',
- chooseText: 'Apply',
- color: `#${material.color.getHexString()}`,
- move: color => {
- let cRGB = color.toRgb();
- let tc = new Color().setRGB(cRGB.r / 255, cRGB.g / 255, cRGB.b / 255);
- material.color = tc;
- },
- change: color => {
- let cRGB = color.toRgb();
- let tc = new Color().setRGB(cRGB.r / 255, cRGB.g / 255, cRGB.b / 255);
- material.color = tc;
- }
- });
- this.addVolatileListener(material, "color_changed", () => {
- panel.find(`#materials\\.color\\.picker`)
- .spectrum('set', `#${material.color.getHexString()}`);
- });
- let updateHeightRange = function () {
-
- let aPosition = pointcloud.getAttribute("position");
- let bMin, bMax;
- if(aPosition){
- // for new format 2.0 and loader that contain precomputed min/max of attributes
- let min = aPosition.range[0][2];
- let max = aPosition.range[1][2];
- let width = max - min;
- bMin = min - 0.2 * width;
- bMax = max + 0.2 * width;
- }else {
- // for format up until exlusive 2.0
- let box = [pointcloud.pcoGeometry.tightBoundingBox, pointcloud.getBoundingBoxWorld()]
- .find(v => v !== undefined);
- pointcloud.updateMatrixWorld(true);
- box = Utils.computeTransformedBoundingBox(box, pointcloud.matrixWorld);
- let bWidth = box.max.z - box.min.z;
- bMin = box.min.z - 0.2 * bWidth;
- bMax = box.max.z + 0.2 * bWidth;
- }
- let range = material.elevationRange;
- panel.find('#lblHeightRange').html(`${range[0].toFixed(2)} to ${range[1].toFixed(2)}`);
- panel.find('#sldHeightRange').slider({min: bMin, max: bMax, values: range});
- };
- let updateExtraRange = function () {
- let attributeName = material.activeAttributeName;
- let attribute = pointcloud.getAttribute(attributeName);
- if(attribute == null){
- return;
- }
-
- let range = material.getRange(attributeName);
- if(range == null){
- range = attribute.range;
- }
- // currently only supporting scalar ranges.
- // rgba, normals, positions, etc have vector ranges, however
- let isValidRange = (typeof range[0] === "number") && (typeof range[1] === "number");
- if(!isValidRange){
- return;
- }
- if(range){
- let msg = `${range[0].toFixed(2)} to ${range[1].toFixed(2)}`;
- panel.find('#lblExtraRange').html(msg);
- }else {
- panel.find("could not deduce range");
- }
- };
- let updateIntensityRange = function () {
- let range = material.intensityRange;
- panel.find('#lblIntensityRange').html(`${parseInt(range[0])} to ${parseInt(range[1])}`);
- };
- {
- updateHeightRange();
- panel.find(`#sldHeightRange`).slider('option', 'min');
- panel.find(`#sldHeightRange`).slider('option', 'max');
- }
- {
- let elGradientRepeat = panel.find("#gradient_repeat_option");
- elGradientRepeat.selectgroup({title: "Gradient"});
- elGradientRepeat.find("input").click( (e) => {
- this.viewer.setElevationGradientRepeat(ElevationGradientRepeat[e.target.value]);
- });
- let current = Object.keys(ElevationGradientRepeat)
- .filter(key => ElevationGradientRepeat[key] === this.viewer.elevationGradientRepeat);
- elGradientRepeat.find(`input[value=${current}]`).trigger("click");
- }
- let onIntensityChange = () => {
- let gamma = material.intensityGamma;
- let contrast = material.intensityContrast;
- let brightness = material.intensityBrightness;
- updateIntensityRange();
- panel.find('#lblIntensityGamma').html(gamma.toFixed(2));
- panel.find('#lblIntensityContrast').html(contrast.toFixed(2));
- panel.find('#lblIntensityBrightness').html(brightness.toFixed(2));
- panel.find('#sldIntensityGamma').slider({value: gamma});
- panel.find('#sldIntensityContrast').slider({value: contrast});
- panel.find('#sldIntensityBrightness').slider({value: brightness});
- };
- let onRGBChange = () => {
- let gamma = material.rgbGamma;
- let contrast = material.rgbContrast;
- let brightness = material.rgbBrightness;
- panel.find('#lblRGBGamma').html(gamma.toFixed(2));
- panel.find('#lblRGBContrast').html(contrast.toFixed(2));
- panel.find('#lblRGBBrightness').html(brightness.toFixed(2));
- panel.find('#sldRGBGamma').slider({value: gamma});
- panel.find('#sldRGBContrast').slider({value: contrast});
- panel.find('#sldRGBBrightness').slider({value: brightness});
- };
- this.addVolatileListener(material, "material_property_changed", updateExtraRange);
- this.addVolatileListener(material, "material_property_changed", updateHeightRange);
- this.addVolatileListener(material, "material_property_changed", onIntensityChange);
- this.addVolatileListener(material, "material_property_changed", onRGBChange);
- updateExtraRange();
- updateHeightRange();
- onIntensityChange();
- onRGBChange();
- }
- }
-
- setMeasurement(object){
- let TYPE = {
- DISTANCE: {panel: DistancePanel},
- AREA: {panel: AreaPanel},
- POINT: {panel: PointPanel},
- ANGLE: {panel: AnglePanel},
- HEIGHT: {panel: HeightPanel},
- PROFILE: {panel: ProfilePanel},
- VOLUME: {panel: VolumePanel},
- CIRCLE: {panel: CirclePanel},
- OTHER: {panel: PointPanel},
- };
- let getType = (measurement) => {
- if (measurement instanceof Measure) {
- if (measurement.showDistances && !measurement.showArea && !measurement.showAngles) {
- return TYPE.DISTANCE;
- } else if (measurement.showDistances && measurement.showArea && !measurement.showAngles) {
- return TYPE.AREA;
- } else if (measurement.maxMarkers === 1) {
- return TYPE.POINT;
- } else if (!measurement.showDistances && !measurement.showArea && measurement.showAngles) {
- return TYPE.ANGLE;
- } else if (measurement.showHeight) {
- return TYPE.HEIGHT;
- } else if (measurement.showCircle) {
- return TYPE.CIRCLE;
- } else {
- return TYPE.OTHER;
- }
- } else if (measurement instanceof Profile) {
- return TYPE.PROFILE;
- } else if (measurement instanceof Volume) {
- return TYPE.VOLUME;
- }
- };
- //this.container.html("measurement");
- let type = getType(object);
- let Panel = type.panel;
- let panel = new Panel(this.viewer, object, this);
- this.container.append(panel.elContent);
- }
- setCamera(camera){
- let panel = new CameraPanel(this.viewer, this);
- this.container.append(panel.elContent);
- }
- setAnnotation(annotation){
- let panel = new AnnotationPanel(this.viewer, this, annotation);
- this.container.append(panel.elContent);
- }
- setCameraAnimation(animation){
- let panel = new CameraAnimationPanel(this.viewer, this, animation);
- this.container.append(panel.elContent);
- }
- }
- function addCommas(nStr){
- nStr += '';
- let x = nStr.split('.');
- let x1 = x[0];
- let x2 = x.length > 1 ? '.' + x[1] : '';
- let rgx = /(\d+)(\d{3})/;
- while (rgx.test(x1)) {
- x1 = x1.replace(rgx, '$1' + ',' + '$2');
- }
- return x1 + x2;
- };
- function format(value){
- return addCommas(value.toFixed(3));
- };
- class HierarchicalSlider{
- constructor(params = {}){
-
- this.element = document.createElement("div");
- this.labels = [];
- this.sliders = [];
- this.range = params.range != null ? params.range : [0, 1];
- this.slide = params.slide != null ? params.slide : null;
- this.step = params.step != null ? params.step : 0.0001;
- let levels = params.levels != null ? params.levels : 1;
- for(let level = 0; level < levels; level++){
- this.addLevel();
- }
- }
- setRange(range){
- this.range = [...range];
- { // root slider
- let slider = this.sliders[0];
- $(slider).slider({
- min: range[0],
- max: range[1],
- });
- }
- for(let i = 1; i < this.sliders.length; i++){
- let parentSlider = this.sliders[i - 1];
- let slider = this.sliders[i];
- let parentValues = $(parentSlider).slider("option", "values");
- let childRange = [...parentValues];
- $(slider).slider({
- min: childRange[0],
- max: childRange[1],
- });
- }
-
- this.updateLabels();
- }
- setValues(values){
- for(let slider of this.sliders){
- $(slider).slider({
- values: [...values],
- });
- }
- this.updateLabels();
- }
- addLevel(){
- const elLevel = document.createElement("li");
- const elRange = document.createTextNode("Range: ");
- const label = document.createElement("span");
- const slider = document.createElement("div");
- let level = this.sliders.length;
- let [min, max] = [0, 0];
- if(this.sliders.length === 0){
- [min, max] = this.range;
- }else {
- let parentSlider = this.sliders[this.sliders.length - 1];
- [min, max] = $(parentSlider).slider("option", "values");
- }
-
- $(slider).slider({
- range: true,
- min: min,
- max: max,
- step: this.step,
- values: [min, max],
- slide: (event, ui) => {
-
- // set all descendants to same range
- let levels = this.sliders.length;
- for(let i = level + 1; i < levels; i++){
- let descendant = this.sliders[i];
- $(descendant).slider({
- range: true,
- min: ui.values[0],
- max: ui.values[1],
- values: [...ui.values],
- });
- }
- if(this.slide){
- let values = [...ui.values];
- this.slide({
- target: this,
- range: this.range,
- values: values,
- });
- }
- this.updateLabels();
- },
- });
- elLevel.append(elRange, label, slider);
- this.sliders.push(slider);
- this.labels.push(label);
- this.element.append(elLevel);
- this.updateLabels();
- }
- removeLevel(){
- }
- updateSliders(){
- }
- updateLabels(){
- let levels = this.sliders.length;
- for(let i = 0; i < levels; i++){
- let slider = this.sliders[i];
- let label = this.labels[i];
- let [min, max] = $(slider).slider("option", "values");
- let strMin = format(min);
- let strMax = format(max);
- let strLabel = `${strMin} to ${strMax}`;
- label.innerHTML = strLabel;
- }
- }
- }
- class OrientedImageControls extends EventDispatcher{
-
- constructor(viewer){
- super();
-
- this.viewer = viewer;
- this.renderer = viewer.renderer;
- this.originalCam = viewer.scene.getActiveCamera();
- this.shearCam = viewer.scene.getActiveCamera().clone();
- this.shearCam.rotation.set(this.originalCam.rotation.toArray());
- this.shearCam.updateProjectionMatrix();
- this.shearCam.updateProjectionMatrix = () => {
- return this.shearCam.projectionMatrix;
- };
- this.image = null;
- this.fadeFactor = 20;
- this.fovDelta = 0;
- this.fovMin = 0.1;
- this.fovMax = 120;
- this.shear = [0, 0];
- // const style = ``;
- this.elUp = $(`<input type="button" value="🡅" style="position: absolute; top: 10px; left: calc(50%); z-index: 1000" />`);
- this.elRight = $(`<input type="button" value="🡆" style="position: absolute; top: calc(50%); right: 10px; z-index: 1000" />`);
- this.elDown = $(`<input type="button" value="🡇" style="position: absolute; bottom: 10px; left: calc(50%); z-index: 1000" />`);
- this.elLeft = $(`<input type="button" value="🡄" style="position: absolute; top: calc(50%); left: 10px; z-index: 1000" />`);
- this.elExit = $(`<input type="button" value="Back to 3D view" style="position: absolute; bottom: 10px; right: 10px; z-index: 1000" />`);
- this.elExit.click( () => {
- this.release();
- });
- this.elUp.click(() => {
- const fovY = viewer.getFOV();
- const top = Math.tan(MathUtils.degToRad(fovY / 2));
- this.shear[1] += 0.1 * top;
- });
- this.elRight.click(() => {
- const fovY = viewer.getFOV();
- const top = Math.tan(MathUtils.degToRad(fovY / 2));
- this.shear[0] += 0.1 * top;
- });
- this.elDown.click(() => {
- const fovY = viewer.getFOV();
- const top = Math.tan(MathUtils.degToRad(fovY / 2));
- this.shear[1] -= 0.1 * top;
- });
- this.elLeft.click(() => {
- const fovY = viewer.getFOV();
- const top = Math.tan(MathUtils.degToRad(fovY / 2));
- this.shear[0] -= 0.1 * top;
- });
- this.scene = null;
- this.sceneControls = new Scene();
- let scroll = (e) => {
- this.fovDelta += -e.delta * 1.0;
- };
- this.addEventListener('mousewheel', scroll);
- //this.addEventListener("mousemove", onMove);
- }
- hasSomethingCaptured(){
- return this.image !== null;
- }
- capture(image){
- if(this.hasSomethingCaptured()){
- return;
- }
- this.image = image;
- this.originalFOV = this.viewer.getFOV();
- this.originalControls = this.viewer.getControls();
- this.viewer.setControls(this);
- this.viewer.scene.overrideCamera = this.shearCam;
- const elCanvas = this.viewer.renderer.domElement;
- const elRoot = $(elCanvas.parentElement);
- this.shear = [0, 0];
- elRoot.append(this.elUp);
- elRoot.append(this.elRight);
- elRoot.append(this.elDown);
- elRoot.append(this.elLeft);
- elRoot.append(this.elExit);
- }
- release(){
- this.image = null;
- this.viewer.scene.overrideCamera = null;
- this.elUp.detach();
- this.elRight.detach();
- this.elDown.detach();
- this.elLeft.detach();
- this.elExit.detach();
- this.viewer.setFOV(this.originalFOV);
- this.viewer.setControls(this.originalControls);
- }
- setScene (scene) {
- this.scene = scene;
- }
- update (delta) {
- // const view = this.scene.view;
- // let prevTotal = this.shearCam.projectionMatrix.elements.reduce( (a, i) => a + i, 0);
- //const progression = Math.min(1, this.fadeFactor * delta);
- //const attenuation = Math.max(0, 1 - this.fadeFactor * delta);
- const progression = 1;
- const attenuation = 0;
- const oldFov = this.viewer.getFOV();
- let fovProgression = progression * this.fovDelta;
- let newFov = oldFov * ((1 + fovProgression / 10));
- newFov = Math.max(this.fovMin, newFov);
- newFov = Math.min(this.fovMax, newFov);
- let diff = newFov / oldFov;
- const mouse = this.viewer.inputHandler.mouse;
- const canvasSize = this.viewer.renderer.getSize(new Vector2$1());
- const uv = [
- (mouse.x / canvasSize.x),
- ((canvasSize.y - mouse.y) / canvasSize.y)
- ];
- const fovY = newFov;
- const aspect = canvasSize.x / canvasSize.y;
- const top = Math.tan(MathUtils.degToRad(fovY / 2));
- const height = 2 * top;
- const width = aspect * height;
- const shearRangeX = [
- this.shear[0] - 0.5 * width,
- this.shear[0] + 0.5 * width,
- ];
- const shearRangeY = [
- this.shear[1] - 0.5 * height,
- this.shear[1] + 0.5 * height,
- ];
- const shx = (1 - uv[0]) * shearRangeX[0] + uv[0] * shearRangeX[1];
- const shy = (1 - uv[1]) * shearRangeY[0] + uv[1] * shearRangeY[1];
- const shu = (1 - diff);
- const newShear = [
- (1 - shu) * this.shear[0] + shu * shx,
- (1 - shu) * this.shear[1] + shu * shy,
- ];
-
- this.shear = newShear;
- this.viewer.setFOV(newFov);
-
- const {originalCam, shearCam} = this;
- originalCam.fov = newFov;
- originalCam.updateMatrixWorld();
- originalCam.updateProjectionMatrix();
- shearCam.copy(originalCam);
- shearCam.rotation.set(...originalCam.rotation.toArray());
- shearCam.updateMatrixWorld();
- shearCam.projectionMatrix.copy(originalCam.projectionMatrix);
- const [sx, sy] = this.shear;
- const mShear = new Matrix4().set(
- 1, 0, sx, 0,
- 0, 1, sy, 0,
- 0, 0, 1, 0,
- 0, 0, 0, 1,
- );
- const proj = shearCam.projectionMatrix;
- proj.multiply(mShear);
- shearCam.projectionMatrixInverse.copy(proj).invert();
- let total = shearCam.projectionMatrix.elements.reduce( (a, i) => a + i, 0);
- this.fovDelta *= attenuation;
- }
- };
- // https://support.pix4d.com/hc/en-us/articles/205675256-How-are-yaw-pitch-roll-defined
- // https://support.pix4d.com/hc/en-us/articles/202558969-How-are-omega-phi-kappa-defined
- function createMaterial(){
- let vertexShader = `
- uniform float uNear;
- varying vec2 vUV;
- varying vec4 vDebug;
-
- void main(){
- vDebug = vec4(0.0, 1.0, 0.0, 1.0);
- vec4 modelViewPosition = modelViewMatrix * vec4(position, 1.0);
- // make sure that this mesh is at least in front of the near plane
- modelViewPosition.xyz += normalize(modelViewPosition.xyz) * uNear;
- gl_Position = projectionMatrix * modelViewPosition;
- vUV = uv;
- }
- `;
- let fragmentShader = `
- uniform sampler2D tColor;
- uniform float uOpacity;
- varying vec2 vUV;
- varying vec4 vDebug;
- void main(){
- vec4 color = texture2D(tColor, vUV);
- gl_FragColor = color;
- gl_FragColor.a = uOpacity;
- }
- `;
- const material = new ShaderMaterial( {
- uniforms: {
- // time: { value: 1.0 },
- // resolution: { value: new THREE.Vector2() }
- tColor: {value: new Texture() },
- uNear: {value: 0.0},
- uOpacity: {value: 1.0},
- },
- vertexShader: vertexShader,
- fragmentShader: fragmentShader,
- side: DoubleSide,
- } );
- material.side = DoubleSide;
- return material;
- }
- const planeGeometry = new PlaneGeometry(1, 1);
- const lineGeometry = new Geometry();
- lineGeometry.vertices.push(
- new Vector3(-0.5, -0.5, 0),
- new Vector3( 0.5, -0.5, 0),
- new Vector3( 0.5, 0.5, 0),
- new Vector3(-0.5, 0.5, 0),
- new Vector3(-0.5, -0.5, 0),
- );
- class OrientedImage{
- constructor(id){
- this.id = id;
- this.fov = 1.0;
- this.position = new Vector3();
- this.rotation = new Vector3();
- this.width = 0;
- this.height = 0;
- this.fov = 1.0;
- const material = createMaterial();
- const lineMaterial = new LineBasicMaterial( { color: 0x00ff00 } );
- this.mesh = new Mesh(planeGeometry, material);
- this.line = new Line(lineGeometry, lineMaterial);
- this.texture = null;
- this.mesh.orientedImage = this;
- }
- set(position, rotation, dimension, fov){
- let radians = rotation.map(MathUtils.degToRad);
- this.position.set(...position);
- this.mesh.position.set(...position);
- this.rotation.set(...radians);
- this.mesh.rotation.set(...radians);
- [this.width, this.height] = dimension;
- this.mesh.scale.set(this.width / this.height, 1, 1);
- this.fov = fov;
- this.updateTransform();
- }
- updateTransform(){
- let {mesh, line, fov} = this;
- mesh.updateMatrixWorld();
- const dir = mesh.getWorldDirection();
- const alpha = MathUtils.degToRad(fov / 2);
- const d = -0.5 / Math.tan(alpha);
- const move = dir.clone().multiplyScalar(d);
- mesh.position.add(move);
- line.position.copy(mesh.position);
- line.scale.copy(mesh.scale);
- line.rotation.copy(mesh.rotation);
- }
- };
- class OrientedImages extends EventDispatcher{
- constructor(){
- super();
- this.node = null;
- this.cameraParams = null;
- this.imageParams = null;
- this.images = null;
- this._visible = true;
- }
- set visible(visible){
- if(this._visible === visible){
- return;
- }
- for(const image of this.images){
- image.mesh.visible = visible;
- image.line.visible = visible;
- }
- this._visible = visible;
- this.dispatchEvent({
- type: "visibility_changed",
- images: this,
- });
- }
- get visible(){
- return this._visible;
- }
- };
- class OrientedImageLoader{
- static async loadCameraParams(path){
- const res = await fetch(path);
- const text = await res.text();
- const parser = new DOMParser();
- const doc = parser.parseFromString(text, "application/xml");
- const width = parseInt(doc.getElementsByTagName("width")[0].textContent);
- const height = parseInt(doc.getElementsByTagName("height")[0].textContent);
- const f = parseFloat(doc.getElementsByTagName("f")[0].textContent);
- let a = (height / 2) / f;
- let fov = 2 * MathUtils.radToDeg(Math.atan(a));
- const params = {
- path: path,
- width: width,
- height: height,
- f: f,
- fov: fov,
- };
- return params;
- }
- static async loadImageParams(path){
- const response = await fetch(path);
- if(!response.ok){
- console.error(`failed to load ${path}`);
- return;
- }
- const content = await response.text();
- const lines = content.split(/\r?\n/);
- const imageParams = [];
- for(let i = 1; i < lines.length; i++){
- const line = lines[i];
- if(line.startsWith("#")){
- continue;
- }
- const tokens = line.split(/\s+/);
- if(tokens.length < 6){
- continue;
- }
- const params = {
- id: tokens[0],
- x: Number.parseFloat(tokens[1]),
- y: Number.parseFloat(tokens[2]),
- z: Number.parseFloat(tokens[3]),
- omega: Number.parseFloat(tokens[4]),
- phi: Number.parseFloat(tokens[5]),
- kappa: Number.parseFloat(tokens[6]),
- };
- // const whitelist = ["47518.jpg"];
- // if(whitelist.includes(params.id)){
- // imageParams.push(params);
- // }
- imageParams.push(params);
- }
- // debug
- //return [imageParams[50]];
- return imageParams;
- }
- static async load(cameraParamsPath, imageParamsPath, viewer){
- const tStart = performance.now();
- const [cameraParams, imageParams] = await Promise.all([
- OrientedImageLoader.loadCameraParams(cameraParamsPath),
- OrientedImageLoader.loadImageParams(imageParamsPath),
- ]);
- const orientedImageControls = new OrientedImageControls(viewer);
- const raycaster = new Raycaster();
- const tEnd = performance.now();
- console.log(tEnd - tStart);
- // const sp = new THREE.PlaneGeometry(1, 1);
- // const lg = new THREE.Geometry();
- // lg.vertices.push(
- // new THREE.Vector3(-0.5, -0.5, 0),
- // new THREE.Vector3( 0.5, -0.5, 0),
- // new THREE.Vector3( 0.5, 0.5, 0),
- // new THREE.Vector3(-0.5, 0.5, 0),
- // new THREE.Vector3(-0.5, -0.5, 0),
- // );
- const {width, height} = cameraParams;
- const orientedImages = [];
- const sceneNode = new Object3D();
- sceneNode.name = "oriented_images";
- for(const params of imageParams){
- // const material = createMaterial();
- // const lm = new THREE.LineBasicMaterial( { color: 0x00ff00 } );
- // const mesh = new THREE.Mesh(sp, material);
- const {x, y, z, omega, phi, kappa} = params;
- // const [rx, ry, rz] = [omega, phi, kappa]
- // .map(THREE.Math.degToRad);
-
- // mesh.position.set(x, y, z);
- // mesh.scale.set(width / height, 1, 1);
- // mesh.rotation.set(rx, ry, rz);
- // {
- // mesh.updateMatrixWorld();
- // const dir = mesh.getWorldDirection();
- // const alpha = THREE.Math.degToRad(cameraParams.fov / 2);
- // const d = -0.5 / Math.tan(alpha);
- // const move = dir.clone().multiplyScalar(d);
- // mesh.position.add(move);
- // }
- // sceneNode.add(mesh);
- // const line = new THREE.Line(lg, lm);
- // line.position.copy(mesh.position);
- // line.scale.copy(mesh.scale);
- // line.rotation.copy(mesh.rotation);
- // sceneNode.add(line);
- let orientedImage = new OrientedImage(params.id);
- // orientedImage.setPosition(x, y, z);
- // orientedImage.setRotation(omega, phi, kappa);
- // orientedImage.setDimension(width, height);
- let position = [x, y, z];
- let rotation = [omega, phi, kappa];
- let dimension = [width, height];
- orientedImage.set(position, rotation, dimension, cameraParams.fov);
- sceneNode.add(orientedImage.mesh);
- sceneNode.add(orientedImage.line);
-
- orientedImages.push(orientedImage);
- }
- let hoveredElement = null;
- let clipVolume = null;
- const onMouseMove = (evt) => {
- const tStart = performance.now();
- if(hoveredElement){
- hoveredElement.line.material.color.setRGB(0, 1, 0);
- }
- evt.preventDefault();
- //var array = getMousePosition( container, evt.clientX, evt.clientY );
- const rect = viewer.renderer.domElement.getBoundingClientRect();
- const [x, y] = [evt.clientX, evt.clientY];
- const array = [
- ( x - rect.left ) / rect.width,
- ( y - rect.top ) / rect.height
- ];
- const onClickPosition = new Vector2$1(...array);
- //const intersects = getIntersects(onClickPosition, scene.children);
- const camera = viewer.scene.getActiveCamera();
- const mouse = new Vector3(
- + ( onClickPosition.x * 2 ) - 1,
- - ( onClickPosition.y * 2 ) + 1 );
- const objects = orientedImages.map(i => i.mesh);
- raycaster.setFromCamera( mouse, camera );
- const intersects = raycaster.intersectObjects( objects );
- let selectionChanged = false;
- if ( intersects.length > 0){
- //console.log(intersects);
- const intersection = intersects[0];
- const orientedImage = intersection.object.orientedImage;
- orientedImage.line.material.color.setRGB(1, 0, 0);
- selectionChanged = hoveredElement !== orientedImage;
- hoveredElement = orientedImage;
- }else {
- hoveredElement = null;
- }
- let shouldRemoveClipVolume = clipVolume !== null && hoveredElement === null;
- let shouldAddClipVolume = clipVolume === null && hoveredElement !== null;
- if(clipVolume !== null && (hoveredElement === null || selectionChanged)){
- // remove existing
- viewer.scene.removePolygonClipVolume(clipVolume);
- clipVolume = null;
- }
-
- if(shouldAddClipVolume || selectionChanged){
- const img = hoveredElement;
- const fov = cameraParams.fov;
- const aspect = cameraParams.width / cameraParams.height;
- const near = 1.0;
- const far = 1000 * 1000;
- const camera = new PerspectiveCamera(fov, aspect, near, far);
- camera.rotation.order = viewer.scene.getActiveCamera().rotation.order;
- camera.rotation.copy(img.mesh.rotation);
- {
- const mesh = img.mesh;
- const dir = mesh.getWorldDirection();
- const pos = mesh.position;
- const alpha = MathUtils.degToRad(fov / 2);
- const d = 0.5 / Math.tan(alpha);
- const newCamPos = pos.clone().add(dir.clone().multiplyScalar(d));
- const newCamDir = pos.clone().sub(newCamPos);
- const newCamTarget = new Vector3().addVectors(
- newCamPos,
- newCamDir.clone().multiplyScalar(viewer.getMoveSpeed()));
- camera.position.copy(newCamPos);
- }
- let volume = new Potree.PolygonClipVolume(camera);
- let m0 = new Mesh();
- let m1 = new Mesh();
- let m2 = new Mesh();
- let m3 = new Mesh();
- m0.position.set(-1, -1, 0);
- m1.position.set( 1, -1, 0);
- m2.position.set( 1, 1, 0);
- m3.position.set(-1, 1, 0);
- volume.markers.push(m0, m1, m2, m3);
- volume.initialized = true;
-
- viewer.scene.addPolygonClipVolume(volume);
- clipVolume = volume;
- }
- const tEnd = performance.now();
- //console.log(tEnd - tStart);
- };
- const moveToImage = (image) => {
- console.log("move to image " + image.id);
- const mesh = image.mesh;
- const newCamPos = image.position.clone();
- const newCamTarget = mesh.position.clone();
- viewer.scene.view.setView(newCamPos, newCamTarget, 500, () => {
- orientedImageControls.capture(image);
- });
- if(image.texture === null){
- const target = image;
- const tmpImagePath = `${Potree.resourcePath}/images/loading.jpg`;
- new TextureLoader().load(tmpImagePath,
- (texture) => {
- if(target.texture === null){
- target.texture = texture;
- target.mesh.material.uniforms.tColor.value = texture;
- mesh.material.needsUpdate = true;
- }
- }
- );
- const imagePath = `${imageParamsPath}/../${target.id}`;
- new TextureLoader().load(imagePath,
- (texture) => {
- target.texture = texture;
- target.mesh.material.uniforms.tColor.value = texture;
- mesh.material.needsUpdate = true;
- }
- );
-
- }
- };
- const onMouseClick = (evt) => {
- if(orientedImageControls.hasSomethingCaptured()){
- return;
- }
- if(hoveredElement){
- moveToImage(hoveredElement);
- }
- };
- viewer.renderer.domElement.addEventListener( 'mousemove', onMouseMove, false );
- viewer.renderer.domElement.addEventListener( 'mousedown', onMouseClick, false );
- viewer.addEventListener("update", () => {
- for(const image of orientedImages){
- const world = image.mesh.matrixWorld;
- const {width, height} = image;
- const aspect = width / height;
- const camera = viewer.scene.getActiveCamera();
- const imgPos = image.mesh.getWorldPosition(new Vector3());
- const camPos = camera.position;
- const d = camPos.distanceTo(imgPos);
- const minSize = 1; // in degrees of fov
- const a = MathUtils.degToRad(minSize);
- let r = d * Math.tan(a);
- r = Math.max(r, 1);
- image.mesh.scale.set(r * aspect, r, 1);
- image.line.scale.set(r * aspect, r, 1);
- image.mesh.material.uniforms.uNear.value = camera.near;
- }
- });
- const images = new OrientedImages();
- images.node = sceneNode;
- images.cameraParamsPath = cameraParamsPath;
- images.imageParamsPath = imageParamsPath;
- images.cameraParams = cameraParams;
- images.imageParams = imageParams;
- images.images = orientedImages;
- Potree.debug.moveToImage = moveToImage;
- return images;
- }
- }
- // This is a generated file. Do not edit.
- var Space_Separator = /[\u1680\u2000-\u200A\u202F\u205F\u3000]/;
- var ID_Start = /[\xAA\xB5\xBA\xC0-\xD6\xD8-\xF6\xF8-\u02C1\u02C6-\u02D1\u02E0-\u02E4\u02EC\u02EE\u0370-\u0374\u0376\u0377\u037A-\u037D\u037F\u0386\u0388-\u038A\u038C\u038E-\u03A1\u03A3-\u03F5\u03F7-\u0481\u048A-\u052F\u0531-\u0556\u0559\u0561-\u0587\u05D0-\u05EA\u05F0-\u05F2\u0620-\u064A\u066E\u066F\u0671-\u06D3\u06D5\u06E5\u06E6\u06EE\u06EF\u06FA-\u06FC\u06FF\u0710\u0712-\u072F\u074D-\u07A5\u07B1\u07CA-\u07EA\u07F4\u07F5\u07FA\u0800-\u0815\u081A\u0824\u0828\u0840-\u0858\u0860-\u086A\u08A0-\u08B4\u08B6-\u08BD\u0904-\u0939\u093D\u0950\u0958-\u0961\u0971-\u0980\u0985-\u098C\u098F\u0990\u0993-\u09A8\u09AA-\u09B0\u09B2\u09B6-\u09B9\u09BD\u09CE\u09DC\u09DD\u09DF-\u09E1\u09F0\u09F1\u09FC\u0A05-\u0A0A\u0A0F\u0A10\u0A13-\u0A28\u0A2A-\u0A30\u0A32\u0A33\u0A35\u0A36\u0A38\u0A39\u0A59-\u0A5C\u0A5E\u0A72-\u0A74\u0A85-\u0A8D\u0A8F-\u0A91\u0A93-\u0AA8\u0AAA-\u0AB0\u0AB2\u0AB3\u0AB5-\u0AB9\u0ABD\u0AD0\u0AE0\u0AE1\u0AF9\u0B05-\u0B0C\u0B0F\u0B10\u0B13-\u0B28\u0B2A-\u0B30\u0B32\u0B33\u0B35-\u0B39\u0B3D\u0B5C\u0B5D\u0B5F-\u0B61\u0B71\u0B83\u0B85-\u0B8A\u0B8E-\u0B90\u0B92-\u0B95\u0B99\u0B9A\u0B9C\u0B9E\u0B9F\u0BA3\u0BA4\u0BA8-\u0BAA\u0BAE-\u0BB9\u0BD0\u0C05-\u0C0C\u0C0E-\u0C10\u0C12-\u0C28\u0C2A-\u0C39\u0C3D\u0C58-\u0C5A\u0C60\u0C61\u0C80\u0C85-\u0C8C\u0C8E-\u0C90\u0C92-\u0CA8\u0CAA-\u0CB3\u0CB5-\u0CB9\u0CBD\u0CDE\u0CE0\u0CE1\u0CF1\u0CF2\u0D05-\u0D0C\u0D0E-\u0D10\u0D12-\u0D3A\u0D3D\u0D4E\u0D54-\u0D56\u0D5F-\u0D61\u0D7A-\u0D7F\u0D85-\u0D96\u0D9A-\u0DB1\u0DB3-\u0DBB\u0DBD\u0DC0-\u0DC6\u0E01-\u0E30\u0E32\u0E33\u0E40-\u0E46\u0E81\u0E82\u0E84\u0E87\u0E88\u0E8A\u0E8D\u0E94-\u0E97\u0E99-\u0E9F\u0EA1-\u0EA3\u0EA5\u0EA7\u0EAA\u0EAB\u0EAD-\u0EB0\u0EB2\u0EB3\u0EBD\u0EC0-\u0EC4\u0EC6\u0EDC-\u0EDF\u0F00\u0F40-\u0F47\u0F49-\u0F6C\u0F88-\u0F8C\u1000-\u102A\u103F\u1050-\u1055\u105A-\u105D\u1061\u1065\u1066\u106E-\u1070\u1075-\u1081\u108E\u10A0-\u10C5\u10C7\u10CD\u10D0-\u10FA\u10FC-\u1248\u124A-\u124D\u1250-\u1256\u1258\u125A-\u125D\u1260-\u1288\u128A-\u128D\u1290-\u12B0\u12B2-\u12B5\u12B8-\u12BE\u12C0\u12C2-\u12C5\u12C8-\u12D6\u12D8-\u1310\u1312-\u1315\u1318-\u135A\u1380-\u138F\u13A0-\u13F5\u13F8-\u13FD\u1401-\u166C\u166F-\u167F\u1681-\u169A\u16A0-\u16EA\u16EE-\u16F8\u1700-\u170C\u170E-\u1711\u1720-\u1731\u1740-\u1751\u1760-\u176C\u176E-\u1770\u1780-\u17B3\u17D7\u17DC\u1820-\u1877\u1880-\u1884\u1887-\u18A8\u18AA\u18B0-\u18F5\u1900-\u191E\u1950-\u196D\u1970-\u1974\u1980-\u19AB\u19B0-\u19C9\u1A00-\u1A16\u1A20-\u1A54\u1AA7\u1B05-\u1B33\u1B45-\u1B4B\u1B83-\u1BA0\u1BAE\u1BAF\u1BBA-\u1BE5\u1C00-\u1C23\u1C4D-\u1C4F\u1C5A-\u1C7D\u1C80-\u1C88\u1CE9-\u1CEC\u1CEE-\u1CF1\u1CF5\u1CF6\u1D00-\u1DBF\u1E00-\u1F15\u1F18-\u1F1D\u1F20-\u1F45\u1F48-\u1F4D\u1F50-\u1F57\u1F59\u1F5B\u1F5D\u1F5F-\u1F7D\u1F80-\u1FB4\u1FB6-\u1FBC\u1FBE\u1FC2-\u1FC4\u1FC6-\u1FCC\u1FD0-\u1FD3\u1FD6-\u1FDB\u1FE0-\u1FEC\u1FF2-\u1FF4\u1FF6-\u1FFC\u2071\u207F\u2090-\u209C\u2102\u2107\u210A-\u2113\u2115\u2119-\u211D\u2124\u2126\u2128\u212A-\u212D\u212F-\u2139\u213C-\u213F\u2145-\u2149\u214E\u2160-\u2188\u2C00-\u2C2E\u2C30-\u2C5E\u2C60-\u2CE4\u2CEB-\u2CEE\u2CF2\u2CF3\u2D00-\u2D25\u2D27\u2D2D\u2D30-\u2D67\u2D6F\u2D80-\u2D96\u2DA0-\u2DA6\u2DA8-\u2DAE\u2DB0-\u2DB6\u2DB8-\u2DBE\u2DC0-\u2DC6\u2DC8-\u2DCE\u2DD0-\u2DD6\u2DD8-\u2DDE\u2E2F\u3005-\u3007\u3021-\u3029\u3031-\u3035\u3038-\u303C\u3041-\u3096\u309D-\u309F\u30A1-\u30FA\u30FC-\u30FF\u3105-\u312E\u3131-\u318E\u31A0-\u31BA\u31F0-\u31FF\u3400-\u4DB5\u4E00-\u9FEA\uA000-\uA48C\uA4D0-\uA4FD\uA500-\uA60C\uA610-\uA61F\uA62A\uA62B\uA640-\uA66E\uA67F-\uA69D\uA6A0-\uA6EF\uA717-\uA71F\uA722-\uA788\uA78B-\uA7AE\uA7B0-\uA7B7\uA7F7-\uA801\uA803-\uA805\uA807-\uA80A\uA80C-\uA822\uA840-\uA873\uA882-\uA8B3\uA8F2-\uA8F7\uA8FB\uA8FD\uA90A-\uA925\uA930-\uA946\uA960-\uA97C\uA984-\uA9B2\uA9CF\uA9E0-\uA9E4\uA9E6-\uA9EF\uA9FA-\uA9FE\uAA00-\uAA28\uAA40-\uAA42\uAA44-\uAA4B\uAA60-\uAA76\uAA7A\uAA7E-\uAAAF\uAAB1\uAAB5\uAAB6\uAAB9-\uAABD\uAAC0\uAAC2\uAADB-\uAADD\uAAE0-\uAAEA\uAAF2-\uAAF4\uAB01-\uAB06\uAB09-\uAB0E\uAB11-\uAB16\uAB20-\uAB26\uAB28-\uAB2E\uAB30-\uAB5A\uAB5C-\uAB65\uAB70-\uABE2\uAC00-\uD7A3\uD7B0-\uD7C6\uD7CB-\uD7FB\uF900-\uFA6D\uFA70-\uFAD9\uFB00-\uFB06\uFB13-\uFB17\uFB1D\uFB1F-\uFB28\uFB2A-\uFB36\uFB38-\uFB3C\uFB3E\uFB40\uFB41\uFB43\uFB44\uFB46-\uFBB1\uFBD3-\uFD3D\uFD50-\uFD8F\uFD92-\uFDC7\uFDF0-\uFDFB\uFE70-\uFE74\uFE76-\uFEFC\uFF21-\uFF3A\uFF41-\uFF5A\uFF66-\uFFBE\uFFC2-\uFFC7\uFFCA-\uFFCF\uFFD2-\uFFD7\uFFDA-\uFFDC]|\uD800[\uDC00-\uDC0B\uDC0D-\uDC26\uDC28-\uDC3A\uDC3C\uDC3D\uDC3F-\uDC4D\uDC50-\uDC5D\uDC80-\uDCFA\uDD40-\uDD74\uDE80-\uDE9C\uDEA0-\uDED0\uDF00-\uDF1F\uDF2D-\uDF4A\uDF50-\uDF75\uDF80-\uDF9D\uDFA0-\uDFC3\uDFC8-\uDFCF\uDFD1-\uDFD5]|\uD801[\uDC00-\uDC9D\uDCB0-\uDCD3\uDCD8-\uDCFB\uDD00-\uDD27\uDD30-\uDD63\uDE00-\uDF36\uDF40-\uDF55\uDF60-\uDF67]|\uD802[\uDC00-\uDC05\uDC08\uDC0A-\uDC35\uDC37\uDC38\uDC3C\uDC3F-\uDC55\uDC60-\uDC76\uDC80-\uDC9E\uDCE0-\uDCF2\uDCF4\uDCF5\uDD00-\uDD15\uDD20-\uDD39\uDD80-\uDDB7\uDDBE\uDDBF\uDE00\uDE10-\uDE13\uDE15-\uDE17\uDE19-\uDE33\uDE60-\uDE7C\uDE80-\uDE9C\uDEC0-\uDEC7\uDEC9-\uDEE4\uDF00-\uDF35\uDF40-\uDF55\uDF60-\uDF72\uDF80-\uDF91]|\uD803[\uDC00-\uDC48\uDC80-\uDCB2\uDCC0-\uDCF2]|\uD804[\uDC03-\uDC37\uDC83-\uDCAF\uDCD0-\uDCE8\uDD03-\uDD26\uDD50-\uDD72\uDD76\uDD83-\uDDB2\uDDC1-\uDDC4\uDDDA\uDDDC\uDE00-\uDE11\uDE13-\uDE2B\uDE80-\uDE86\uDE88\uDE8A-\uDE8D\uDE8F-\uDE9D\uDE9F-\uDEA8\uDEB0-\uDEDE\uDF05-\uDF0C\uDF0F\uDF10\uDF13-\uDF28\uDF2A-\uDF30\uDF32\uDF33\uDF35-\uDF39\uDF3D\uDF50\uDF5D-\uDF61]|\uD805[\uDC00-\uDC34\uDC47-\uDC4A\uDC80-\uDCAF\uDCC4\uDCC5\uDCC7\uDD80-\uDDAE\uDDD8-\uDDDB\uDE00-\uDE2F\uDE44\uDE80-\uDEAA\uDF00-\uDF19]|\uD806[\uDCA0-\uDCDF\uDCFF\uDE00\uDE0B-\uDE32\uDE3A\uDE50\uDE5C-\uDE83\uDE86-\uDE89\uDEC0-\uDEF8]|\uD807[\uDC00-\uDC08\uDC0A-\uDC2E\uDC40\uDC72-\uDC8F\uDD00-\uDD06\uDD08\uDD09\uDD0B-\uDD30\uDD46]|\uD808[\uDC00-\uDF99]|\uD809[\uDC00-\uDC6E\uDC80-\uDD43]|[\uD80C\uD81C-\uD820\uD840-\uD868\uD86A-\uD86C\uD86F-\uD872\uD874-\uD879][\uDC00-\uDFFF]|\uD80D[\uDC00-\uDC2E]|\uD811[\uDC00-\uDE46]|\uD81A[\uDC00-\uDE38\uDE40-\uDE5E\uDED0-\uDEED\uDF00-\uDF2F\uDF40-\uDF43\uDF63-\uDF77\uDF7D-\uDF8F]|\uD81B[\uDF00-\uDF44\uDF50\uDF93-\uDF9F\uDFE0\uDFE1]|\uD821[\uDC00-\uDFEC]|\uD822[\uDC00-\uDEF2]|\uD82C[\uDC00-\uDD1E\uDD70-\uDEFB]|\uD82F[\uDC00-\uDC6A\uDC70-\uDC7C\uDC80-\uDC88\uDC90-\uDC99]|\uD835[\uDC00-\uDC54\uDC56-\uDC9C\uDC9E\uDC9F\uDCA2\uDCA5\uDCA6\uDCA9-\uDCAC\uDCAE-\uDCB9\uDCBB\uDCBD-\uDCC3\uDCC5-\uDD05\uDD07-\uDD0A\uDD0D-\uDD14\uDD16-\uDD1C\uDD1E-\uDD39\uDD3B-\uDD3E\uDD40-\uDD44\uDD46\uDD4A-\uDD50\uDD52-\uDEA5\uDEA8-\uDEC0\uDEC2-\uDEDA\uDEDC-\uDEFA\uDEFC-\uDF14\uDF16-\uDF34\uDF36-\uDF4E\uDF50-\uDF6E\uDF70-\uDF88\uDF8A-\uDFA8\uDFAA-\uDFC2\uDFC4-\uDFCB]|\uD83A[\uDC00-\uDCC4\uDD00-\uDD43]|\uD83B[\uDE00-\uDE03\uDE05-\uDE1F\uDE21\uDE22\uDE24\uDE27\uDE29-\uDE32\uDE34-\uDE37\uDE39\uDE3B\uDE42\uDE47\uDE49\uDE4B\uDE4D-\uDE4F\uDE51\uDE52\uDE54\uDE57\uDE59\uDE5B\uDE5D\uDE5F\uDE61\uDE62\uDE64\uDE67-\uDE6A\uDE6C-\uDE72\uDE74-\uDE77\uDE79-\uDE7C\uDE7E\uDE80-\uDE89\uDE8B-\uDE9B\uDEA1-\uDEA3\uDEA5-\uDEA9\uDEAB-\uDEBB]|\uD869[\uDC00-\uDED6\uDF00-\uDFFF]|\uD86D[\uDC00-\uDF34\uDF40-\uDFFF]|\uD86E[\uDC00-\uDC1D\uDC20-\uDFFF]|\uD873[\uDC00-\uDEA1\uDEB0-\uDFFF]|\uD87A[\uDC00-\uDFE0]|\uD87E[\uDC00-\uDE1D]/;
- var ID_Continue = /[\xAA\xB5\xBA\xC0-\xD6\xD8-\xF6\xF8-\u02C1\u02C6-\u02D1\u02E0-\u02E4\u02EC\u02EE\u0300-\u0374\u0376\u0377\u037A-\u037D\u037F\u0386\u0388-\u038A\u038C\u038E-\u03A1\u03A3-\u03F5\u03F7-\u0481\u0483-\u0487\u048A-\u052F\u0531-\u0556\u0559\u0561-\u0587\u0591-\u05BD\u05BF\u05C1\u05C2\u05C4\u05C5\u05C7\u05D0-\u05EA\u05F0-\u05F2\u0610-\u061A\u0620-\u0669\u066E-\u06D3\u06D5-\u06DC\u06DF-\u06E8\u06EA-\u06FC\u06FF\u0710-\u074A\u074D-\u07B1\u07C0-\u07F5\u07FA\u0800-\u082D\u0840-\u085B\u0860-\u086A\u08A0-\u08B4\u08B6-\u08BD\u08D4-\u08E1\u08E3-\u0963\u0966-\u096F\u0971-\u0983\u0985-\u098C\u098F\u0990\u0993-\u09A8\u09AA-\u09B0\u09B2\u09B6-\u09B9\u09BC-\u09C4\u09C7\u09C8\u09CB-\u09CE\u09D7\u09DC\u09DD\u09DF-\u09E3\u09E6-\u09F1\u09FC\u0A01-\u0A03\u0A05-\u0A0A\u0A0F\u0A10\u0A13-\u0A28\u0A2A-\u0A30\u0A32\u0A33\u0A35\u0A36\u0A38\u0A39\u0A3C\u0A3E-\u0A42\u0A47\u0A48\u0A4B-\u0A4D\u0A51\u0A59-\u0A5C\u0A5E\u0A66-\u0A75\u0A81-\u0A83\u0A85-\u0A8D\u0A8F-\u0A91\u0A93-\u0AA8\u0AAA-\u0AB0\u0AB2\u0AB3\u0AB5-\u0AB9\u0ABC-\u0AC5\u0AC7-\u0AC9\u0ACB-\u0ACD\u0AD0\u0AE0-\u0AE3\u0AE6-\u0AEF\u0AF9-\u0AFF\u0B01-\u0B03\u0B05-\u0B0C\u0B0F\u0B10\u0B13-\u0B28\u0B2A-\u0B30\u0B32\u0B33\u0B35-\u0B39\u0B3C-\u0B44\u0B47\u0B48\u0B4B-\u0B4D\u0B56\u0B57\u0B5C\u0B5D\u0B5F-\u0B63\u0B66-\u0B6F\u0B71\u0B82\u0B83\u0B85-\u0B8A\u0B8E-\u0B90\u0B92-\u0B95\u0B99\u0B9A\u0B9C\u0B9E\u0B9F\u0BA3\u0BA4\u0BA8-\u0BAA\u0BAE-\u0BB9\u0BBE-\u0BC2\u0BC6-\u0BC8\u0BCA-\u0BCD\u0BD0\u0BD7\u0BE6-\u0BEF\u0C00-\u0C03\u0C05-\u0C0C\u0C0E-\u0C10\u0C12-\u0C28\u0C2A-\u0C39\u0C3D-\u0C44\u0C46-\u0C48\u0C4A-\u0C4D\u0C55\u0C56\u0C58-\u0C5A\u0C60-\u0C63\u0C66-\u0C6F\u0C80-\u0C83\u0C85-\u0C8C\u0C8E-\u0C90\u0C92-\u0CA8\u0CAA-\u0CB3\u0CB5-\u0CB9\u0CBC-\u0CC4\u0CC6-\u0CC8\u0CCA-\u0CCD\u0CD5\u0CD6\u0CDE\u0CE0-\u0CE3\u0CE6-\u0CEF\u0CF1\u0CF2\u0D00-\u0D03\u0D05-\u0D0C\u0D0E-\u0D10\u0D12-\u0D44\u0D46-\u0D48\u0D4A-\u0D4E\u0D54-\u0D57\u0D5F-\u0D63\u0D66-\u0D6F\u0D7A-\u0D7F\u0D82\u0D83\u0D85-\u0D96\u0D9A-\u0DB1\u0DB3-\u0DBB\u0DBD\u0DC0-\u0DC6\u0DCA\u0DCF-\u0DD4\u0DD6\u0DD8-\u0DDF\u0DE6-\u0DEF\u0DF2\u0DF3\u0E01-\u0E3A\u0E40-\u0E4E\u0E50-\u0E59\u0E81\u0E82\u0E84\u0E87\u0E88\u0E8A\u0E8D\u0E94-\u0E97\u0E99-\u0E9F\u0EA1-\u0EA3\u0EA5\u0EA7\u0EAA\u0EAB\u0EAD-\u0EB9\u0EBB-\u0EBD\u0EC0-\u0EC4\u0EC6\u0EC8-\u0ECD\u0ED0-\u0ED9\u0EDC-\u0EDF\u0F00\u0F18\u0F19\u0F20-\u0F29\u0F35\u0F37\u0F39\u0F3E-\u0F47\u0F49-\u0F6C\u0F71-\u0F84\u0F86-\u0F97\u0F99-\u0FBC\u0FC6\u1000-\u1049\u1050-\u109D\u10A0-\u10C5\u10C7\u10CD\u10D0-\u10FA\u10FC-\u1248\u124A-\u124D\u1250-\u1256\u1258\u125A-\u125D\u1260-\u1288\u128A-\u128D\u1290-\u12B0\u12B2-\u12B5\u12B8-\u12BE\u12C0\u12C2-\u12C5\u12C8-\u12D6\u12D8-\u1310\u1312-\u1315\u1318-\u135A\u135D-\u135F\u1380-\u138F\u13A0-\u13F5\u13F8-\u13FD\u1401-\u166C\u166F-\u167F\u1681-\u169A\u16A0-\u16EA\u16EE-\u16F8\u1700-\u170C\u170E-\u1714\u1720-\u1734\u1740-\u1753\u1760-\u176C\u176E-\u1770\u1772\u1773\u1780-\u17D3\u17D7\u17DC\u17DD\u17E0-\u17E9\u180B-\u180D\u1810-\u1819\u1820-\u1877\u1880-\u18AA\u18B0-\u18F5\u1900-\u191E\u1920-\u192B\u1930-\u193B\u1946-\u196D\u1970-\u1974\u1980-\u19AB\u19B0-\u19C9\u19D0-\u19D9\u1A00-\u1A1B\u1A20-\u1A5E\u1A60-\u1A7C\u1A7F-\u1A89\u1A90-\u1A99\u1AA7\u1AB0-\u1ABD\u1B00-\u1B4B\u1B50-\u1B59\u1B6B-\u1B73\u1B80-\u1BF3\u1C00-\u1C37\u1C40-\u1C49\u1C4D-\u1C7D\u1C80-\u1C88\u1CD0-\u1CD2\u1CD4-\u1CF9\u1D00-\u1DF9\u1DFB-\u1F15\u1F18-\u1F1D\u1F20-\u1F45\u1F48-\u1F4D\u1F50-\u1F57\u1F59\u1F5B\u1F5D\u1F5F-\u1F7D\u1F80-\u1FB4\u1FB6-\u1FBC\u1FBE\u1FC2-\u1FC4\u1FC6-\u1FCC\u1FD0-\u1FD3\u1FD6-\u1FDB\u1FE0-\u1FEC\u1FF2-\u1FF4\u1FF6-\u1FFC\u203F\u2040\u2054\u2071\u207F\u2090-\u209C\u20D0-\u20DC\u20E1\u20E5-\u20F0\u2102\u2107\u210A-\u2113\u2115\u2119-\u211D\u2124\u2126\u2128\u212A-\u212D\u212F-\u2139\u213C-\u213F\u2145-\u2149\u214E\u2160-\u2188\u2C00-\u2C2E\u2C30-\u2C5E\u2C60-\u2CE4\u2CEB-\u2CF3\u2D00-\u2D25\u2D27\u2D2D\u2D30-\u2D67\u2D6F\u2D7F-\u2D96\u2DA0-\u2DA6\u2DA8-\u2DAE\u2DB0-\u2DB6\u2DB8-\u2DBE\u2DC0-\u2DC6\u2DC8-\u2DCE\u2DD0-\u2DD6\u2DD8-\u2DDE\u2DE0-\u2DFF\u2E2F\u3005-\u3007\u3021-\u302F\u3031-\u3035\u3038-\u303C\u3041-\u3096\u3099\u309A\u309D-\u309F\u30A1-\u30FA\u30FC-\u30FF\u3105-\u312E\u3131-\u318E\u31A0-\u31BA\u31F0-\u31FF\u3400-\u4DB5\u4E00-\u9FEA\uA000-\uA48C\uA4D0-\uA4FD\uA500-\uA60C\uA610-\uA62B\uA640-\uA66F\uA674-\uA67D\uA67F-\uA6F1\uA717-\uA71F\uA722-\uA788\uA78B-\uA7AE\uA7B0-\uA7B7\uA7F7-\uA827\uA840-\uA873\uA880-\uA8C5\uA8D0-\uA8D9\uA8E0-\uA8F7\uA8FB\uA8FD\uA900-\uA92D\uA930-\uA953\uA960-\uA97C\uA980-\uA9C0\uA9CF-\uA9D9\uA9E0-\uA9FE\uAA00-\uAA36\uAA40-\uAA4D\uAA50-\uAA59\uAA60-\uAA76\uAA7A-\uAAC2\uAADB-\uAADD\uAAE0-\uAAEF\uAAF2-\uAAF6\uAB01-\uAB06\uAB09-\uAB0E\uAB11-\uAB16\uAB20-\uAB26\uAB28-\uAB2E\uAB30-\uAB5A\uAB5C-\uAB65\uAB70-\uABEA\uABEC\uABED\uABF0-\uABF9\uAC00-\uD7A3\uD7B0-\uD7C6\uD7CB-\uD7FB\uF900-\uFA6D\uFA70-\uFAD9\uFB00-\uFB06\uFB13-\uFB17\uFB1D-\uFB28\uFB2A-\uFB36\uFB38-\uFB3C\uFB3E\uFB40\uFB41\uFB43\uFB44\uFB46-\uFBB1\uFBD3-\uFD3D\uFD50-\uFD8F\uFD92-\uFDC7\uFDF0-\uFDFB\uFE00-\uFE0F\uFE20-\uFE2F\uFE33\uFE34\uFE4D-\uFE4F\uFE70-\uFE74\uFE76-\uFEFC\uFF10-\uFF19\uFF21-\uFF3A\uFF3F\uFF41-\uFF5A\uFF66-\uFFBE\uFFC2-\uFFC7\uFFCA-\uFFCF\uFFD2-\uFFD7\uFFDA-\uFFDC]|\uD800[\uDC00-\uDC0B\uDC0D-\uDC26\uDC28-\uDC3A\uDC3C\uDC3D\uDC3F-\uDC4D\uDC50-\uDC5D\uDC80-\uDCFA\uDD40-\uDD74\uDDFD\uDE80-\uDE9C\uDEA0-\uDED0\uDEE0\uDF00-\uDF1F\uDF2D-\uDF4A\uDF50-\uDF7A\uDF80-\uDF9D\uDFA0-\uDFC3\uDFC8-\uDFCF\uDFD1-\uDFD5]|\uD801[\uDC00-\uDC9D\uDCA0-\uDCA9\uDCB0-\uDCD3\uDCD8-\uDCFB\uDD00-\uDD27\uDD30-\uDD63\uDE00-\uDF36\uDF40-\uDF55\uDF60-\uDF67]|\uD802[\uDC00-\uDC05\uDC08\uDC0A-\uDC35\uDC37\uDC38\uDC3C\uDC3F-\uDC55\uDC60-\uDC76\uDC80-\uDC9E\uDCE0-\uDCF2\uDCF4\uDCF5\uDD00-\uDD15\uDD20-\uDD39\uDD80-\uDDB7\uDDBE\uDDBF\uDE00-\uDE03\uDE05\uDE06\uDE0C-\uDE13\uDE15-\uDE17\uDE19-\uDE33\uDE38-\uDE3A\uDE3F\uDE60-\uDE7C\uDE80-\uDE9C\uDEC0-\uDEC7\uDEC9-\uDEE6\uDF00-\uDF35\uDF40-\uDF55\uDF60-\uDF72\uDF80-\uDF91]|\uD803[\uDC00-\uDC48\uDC80-\uDCB2\uDCC0-\uDCF2]|\uD804[\uDC00-\uDC46\uDC66-\uDC6F\uDC7F-\uDCBA\uDCD0-\uDCE8\uDCF0-\uDCF9\uDD00-\uDD34\uDD36-\uDD3F\uDD50-\uDD73\uDD76\uDD80-\uDDC4\uDDCA-\uDDCC\uDDD0-\uDDDA\uDDDC\uDE00-\uDE11\uDE13-\uDE37\uDE3E\uDE80-\uDE86\uDE88\uDE8A-\uDE8D\uDE8F-\uDE9D\uDE9F-\uDEA8\uDEB0-\uDEEA\uDEF0-\uDEF9\uDF00-\uDF03\uDF05-\uDF0C\uDF0F\uDF10\uDF13-\uDF28\uDF2A-\uDF30\uDF32\uDF33\uDF35-\uDF39\uDF3C-\uDF44\uDF47\uDF48\uDF4B-\uDF4D\uDF50\uDF57\uDF5D-\uDF63\uDF66-\uDF6C\uDF70-\uDF74]|\uD805[\uDC00-\uDC4A\uDC50-\uDC59\uDC80-\uDCC5\uDCC7\uDCD0-\uDCD9\uDD80-\uDDB5\uDDB8-\uDDC0\uDDD8-\uDDDD\uDE00-\uDE40\uDE44\uDE50-\uDE59\uDE80-\uDEB7\uDEC0-\uDEC9\uDF00-\uDF19\uDF1D-\uDF2B\uDF30-\uDF39]|\uD806[\uDCA0-\uDCE9\uDCFF\uDE00-\uDE3E\uDE47\uDE50-\uDE83\uDE86-\uDE99\uDEC0-\uDEF8]|\uD807[\uDC00-\uDC08\uDC0A-\uDC36\uDC38-\uDC40\uDC50-\uDC59\uDC72-\uDC8F\uDC92-\uDCA7\uDCA9-\uDCB6\uDD00-\uDD06\uDD08\uDD09\uDD0B-\uDD36\uDD3A\uDD3C\uDD3D\uDD3F-\uDD47\uDD50-\uDD59]|\uD808[\uDC00-\uDF99]|\uD809[\uDC00-\uDC6E\uDC80-\uDD43]|[\uD80C\uD81C-\uD820\uD840-\uD868\uD86A-\uD86C\uD86F-\uD872\uD874-\uD879][\uDC00-\uDFFF]|\uD80D[\uDC00-\uDC2E]|\uD811[\uDC00-\uDE46]|\uD81A[\uDC00-\uDE38\uDE40-\uDE5E\uDE60-\uDE69\uDED0-\uDEED\uDEF0-\uDEF4\uDF00-\uDF36\uDF40-\uDF43\uDF50-\uDF59\uDF63-\uDF77\uDF7D-\uDF8F]|\uD81B[\uDF00-\uDF44\uDF50-\uDF7E\uDF8F-\uDF9F\uDFE0\uDFE1]|\uD821[\uDC00-\uDFEC]|\uD822[\uDC00-\uDEF2]|\uD82C[\uDC00-\uDD1E\uDD70-\uDEFB]|\uD82F[\uDC00-\uDC6A\uDC70-\uDC7C\uDC80-\uDC88\uDC90-\uDC99\uDC9D\uDC9E]|\uD834[\uDD65-\uDD69\uDD6D-\uDD72\uDD7B-\uDD82\uDD85-\uDD8B\uDDAA-\uDDAD\uDE42-\uDE44]|\uD835[\uDC00-\uDC54\uDC56-\uDC9C\uDC9E\uDC9F\uDCA2\uDCA5\uDCA6\uDCA9-\uDCAC\uDCAE-\uDCB9\uDCBB\uDCBD-\uDCC3\uDCC5-\uDD05\uDD07-\uDD0A\uDD0D-\uDD14\uDD16-\uDD1C\uDD1E-\uDD39\uDD3B-\uDD3E\uDD40-\uDD44\uDD46\uDD4A-\uDD50\uDD52-\uDEA5\uDEA8-\uDEC0\uDEC2-\uDEDA\uDEDC-\uDEFA\uDEFC-\uDF14\uDF16-\uDF34\uDF36-\uDF4E\uDF50-\uDF6E\uDF70-\uDF88\uDF8A-\uDFA8\uDFAA-\uDFC2\uDFC4-\uDFCB\uDFCE-\uDFFF]|\uD836[\uDE00-\uDE36\uDE3B-\uDE6C\uDE75\uDE84\uDE9B-\uDE9F\uDEA1-\uDEAF]|\uD838[\uDC00-\uDC06\uDC08-\uDC18\uDC1B-\uDC21\uDC23\uDC24\uDC26-\uDC2A]|\uD83A[\uDC00-\uDCC4\uDCD0-\uDCD6\uDD00-\uDD4A\uDD50-\uDD59]|\uD83B[\uDE00-\uDE03\uDE05-\uDE1F\uDE21\uDE22\uDE24\uDE27\uDE29-\uDE32\uDE34-\uDE37\uDE39\uDE3B\uDE42\uDE47\uDE49\uDE4B\uDE4D-\uDE4F\uDE51\uDE52\uDE54\uDE57\uDE59\uDE5B\uDE5D\uDE5F\uDE61\uDE62\uDE64\uDE67-\uDE6A\uDE6C-\uDE72\uDE74-\uDE77\uDE79-\uDE7C\uDE7E\uDE80-\uDE89\uDE8B-\uDE9B\uDEA1-\uDEA3\uDEA5-\uDEA9\uDEAB-\uDEBB]|\uD869[\uDC00-\uDED6\uDF00-\uDFFF]|\uD86D[\uDC00-\uDF34\uDF40-\uDFFF]|\uD86E[\uDC00-\uDC1D\uDC20-\uDFFF]|\uD873[\uDC00-\uDEA1\uDEB0-\uDFFF]|\uD87A[\uDC00-\uDFE0]|\uD87E[\uDC00-\uDE1D]|\uDB40[\uDD00-\uDDEF]/;
- var unicode = {
- Space_Separator: Space_Separator,
- ID_Start: ID_Start,
- ID_Continue: ID_Continue
- };
- var util = {
- isSpaceSeparator (c) {
- return typeof c === 'string' && unicode.Space_Separator.test(c)
- },
- isIdStartChar (c) {
- return typeof c === 'string' && (
- (c >= 'a' && c <= 'z') ||
- (c >= 'A' && c <= 'Z') ||
- (c === '$') || (c === '_') ||
- unicode.ID_Start.test(c)
- )
- },
- isIdContinueChar (c) {
- return typeof c === 'string' && (
- (c >= 'a' && c <= 'z') ||
- (c >= 'A' && c <= 'Z') ||
- (c >= '0' && c <= '9') ||
- (c === '$') || (c === '_') ||
- (c === '\u200C') || (c === '\u200D') ||
- unicode.ID_Continue.test(c)
- )
- },
- isDigit (c) {
- return typeof c === 'string' && /[0-9]/.test(c)
- },
- isHexDigit (c) {
- return typeof c === 'string' && /[0-9A-Fa-f]/.test(c)
- },
- };
- let source;
- let parseState;
- let stack;
- let pos;
- let line;
- let column;
- let token;
- let key;
- let root;
- var parse = function parse (text, reviver) {
- source = String(text);
- parseState = 'start';
- stack = [];
- pos = 0;
- line = 1;
- column = 0;
- token = undefined;
- key = undefined;
- root = undefined;
- do {
- token = lex();
- // This code is unreachable.
- // if (!parseStates[parseState]) {
- // throw invalidParseState()
- // }
- parseStates[parseState]();
- } while (token.type !== 'eof')
- if (typeof reviver === 'function') {
- return internalize({'': root}, '', reviver)
- }
- return root
- };
- function internalize (holder, name, reviver) {
- const value = holder[name];
- if (value != null && typeof value === 'object') {
- for (const key in value) {
- const replacement = internalize(value, key, reviver);
- if (replacement === undefined) {
- delete value[key];
- } else {
- value[key] = replacement;
- }
- }
- }
- return reviver.call(holder, name, value)
- }
- let lexState;
- let buffer;
- let doubleQuote;
- let sign$1;
- let c$2;
- function lex () {
- lexState = 'default';
- buffer = '';
- doubleQuote = false;
- sign$1 = 1;
- for (;;) {
- c$2 = peek();
- // This code is unreachable.
- // if (!lexStates[lexState]) {
- // throw invalidLexState(lexState)
- // }
- const token = lexStates[lexState]();
- if (token) {
- return token
- }
- }
- }
- function peek () {
- if (source[pos]) {
- return String.fromCodePoint(source.codePointAt(pos))
- }
- }
- function read () {
- const c = peek();
- if (c === '\n') {
- line++;
- column = 0;
- } else if (c) {
- column += c.length;
- } else {
- column++;
- }
- if (c) {
- pos += c.length;
- }
- return c
- }
- const lexStates = {
- default () {
- switch (c$2) {
- case '\t':
- case '\v':
- case '\f':
- case ' ':
- case '\u00A0':
- case '\uFEFF':
- case '\n':
- case '\r':
- case '\u2028':
- case '\u2029':
- read();
- return
- case '/':
- read();
- lexState = 'comment';
- return
- case undefined:
- read();
- return newToken('eof')
- }
- if (util.isSpaceSeparator(c$2)) {
- read();
- return
- }
- // This code is unreachable.
- // if (!lexStates[parseState]) {
- // throw invalidLexState(parseState)
- // }
- return lexStates[parseState]()
- },
- comment () {
- switch (c$2) {
- case '*':
- read();
- lexState = 'multiLineComment';
- return
- case '/':
- read();
- lexState = 'singleLineComment';
- return
- }
- throw invalidChar(read())
- },
- multiLineComment () {
- switch (c$2) {
- case '*':
- read();
- lexState = 'multiLineCommentAsterisk';
- return
- case undefined:
- throw invalidChar(read())
- }
- read();
- },
- multiLineCommentAsterisk () {
- switch (c$2) {
- case '*':
- read();
- return
- case '/':
- read();
- lexState = 'default';
- return
- case undefined:
- throw invalidChar(read())
- }
- read();
- lexState = 'multiLineComment';
- },
- singleLineComment () {
- switch (c$2) {
- case '\n':
- case '\r':
- case '\u2028':
- case '\u2029':
- read();
- lexState = 'default';
- return
- case undefined:
- read();
- return newToken('eof')
- }
- read();
- },
- value () {
- switch (c$2) {
- case '{':
- case '[':
- return newToken('punctuator', read())
- case 'n':
- read();
- literal('ull');
- return newToken('null', null)
- case 't':
- read();
- literal('rue');
- return newToken('boolean', true)
- case 'f':
- read();
- literal('alse');
- return newToken('boolean', false)
- case '-':
- case '+':
- if (read() === '-') {
- sign$1 = -1;
- }
- lexState = 'sign';
- return
- case '.':
- buffer = read();
- lexState = 'decimalPointLeading';
- return
- case '0':
- buffer = read();
- lexState = 'zero';
- return
- case '1':
- case '2':
- case '3':
- case '4':
- case '5':
- case '6':
- case '7':
- case '8':
- case '9':
- buffer = read();
- lexState = 'decimalInteger';
- return
- case 'I':
- read();
- literal('nfinity');
- return newToken('numeric', Infinity)
- case 'N':
- read();
- literal('aN');
- return newToken('numeric', NaN)
- case '"':
- case "'":
- doubleQuote = (read() === '"');
- buffer = '';
- lexState = 'string';
- return
- }
- throw invalidChar(read())
- },
- identifierNameStartEscape () {
- if (c$2 !== 'u') {
- throw invalidChar(read())
- }
- read();
- const u = unicodeEscape();
- switch (u) {
- case '$':
- case '_':
- break
- default:
- if (!util.isIdStartChar(u)) {
- throw invalidIdentifier()
- }
- break
- }
- buffer += u;
- lexState = 'identifierName';
- },
- identifierName () {
- switch (c$2) {
- case '$':
- case '_':
- case '\u200C':
- case '\u200D':
- buffer += read();
- return
- case '\\':
- read();
- lexState = 'identifierNameEscape';
- return
- }
- if (util.isIdContinueChar(c$2)) {
- buffer += read();
- return
- }
- return newToken('identifier', buffer)
- },
- identifierNameEscape () {
- if (c$2 !== 'u') {
- throw invalidChar(read())
- }
- read();
- const u = unicodeEscape();
- switch (u) {
- case '$':
- case '_':
- case '\u200C':
- case '\u200D':
- break
- default:
- if (!util.isIdContinueChar(u)) {
- throw invalidIdentifier()
- }
- break
- }
- buffer += u;
- lexState = 'identifierName';
- },
- sign () {
- switch (c$2) {
- case '.':
- buffer = read();
- lexState = 'decimalPointLeading';
- return
- case '0':
- buffer = read();
- lexState = 'zero';
- return
- case '1':
- case '2':
- case '3':
- case '4':
- case '5':
- case '6':
- case '7':
- case '8':
- case '9':
- buffer = read();
- lexState = 'decimalInteger';
- return
- case 'I':
- read();
- literal('nfinity');
- return newToken('numeric', sign$1 * Infinity)
- case 'N':
- read();
- literal('aN');
- return newToken('numeric', NaN)
- }
- throw invalidChar(read())
- },
- zero () {
- switch (c$2) {
- case '.':
- buffer += read();
- lexState = 'decimalPoint';
- return
- case 'e':
- case 'E':
- buffer += read();
- lexState = 'decimalExponent';
- return
- case 'x':
- case 'X':
- buffer += read();
- lexState = 'hexadecimal';
- return
- }
- return newToken('numeric', sign$1 * 0)
- },
- decimalInteger () {
- switch (c$2) {
- case '.':
- buffer += read();
- lexState = 'decimalPoint';
- return
- case 'e':
- case 'E':
- buffer += read();
- lexState = 'decimalExponent';
- return
- }
- if (util.isDigit(c$2)) {
- buffer += read();
- return
- }
- return newToken('numeric', sign$1 * Number(buffer))
- },
- decimalPointLeading () {
- if (util.isDigit(c$2)) {
- buffer += read();
- lexState = 'decimalFraction';
- return
- }
- throw invalidChar(read())
- },
- decimalPoint () {
- switch (c$2) {
- case 'e':
- case 'E':
- buffer += read();
- lexState = 'decimalExponent';
- return
- }
- if (util.isDigit(c$2)) {
- buffer += read();
- lexState = 'decimalFraction';
- return
- }
- return newToken('numeric', sign$1 * Number(buffer))
- },
- decimalFraction () {
- switch (c$2) {
- case 'e':
- case 'E':
- buffer += read();
- lexState = 'decimalExponent';
- return
- }
- if (util.isDigit(c$2)) {
- buffer += read();
- return
- }
- return newToken('numeric', sign$1 * Number(buffer))
- },
- decimalExponent () {
- switch (c$2) {
- case '+':
- case '-':
- buffer += read();
- lexState = 'decimalExponentSign';
- return
- }
- if (util.isDigit(c$2)) {
- buffer += read();
- lexState = 'decimalExponentInteger';
- return
- }
- throw invalidChar(read())
- },
- decimalExponentSign () {
- if (util.isDigit(c$2)) {
- buffer += read();
- lexState = 'decimalExponentInteger';
- return
- }
- throw invalidChar(read())
- },
- decimalExponentInteger () {
- if (util.isDigit(c$2)) {
- buffer += read();
- return
- }
- return newToken('numeric', sign$1 * Number(buffer))
- },
- hexadecimal () {
- if (util.isHexDigit(c$2)) {
- buffer += read();
- lexState = 'hexadecimalInteger';
- return
- }
- throw invalidChar(read())
- },
- hexadecimalInteger () {
- if (util.isHexDigit(c$2)) {
- buffer += read();
- return
- }
- return newToken('numeric', sign$1 * Number(buffer))
- },
- string () {
- switch (c$2) {
- case '\\':
- read();
- buffer += escape$1();
- return
- case '"':
- if (doubleQuote) {
- read();
- return newToken('string', buffer)
- }
- buffer += read();
- return
- case "'":
- if (!doubleQuote) {
- read();
- return newToken('string', buffer)
- }
- buffer += read();
- return
- case '\n':
- case '\r':
- throw invalidChar(read())
- case '\u2028':
- case '\u2029':
- separatorChar(c$2);
- break
- case undefined:
- throw invalidChar(read())
- }
- buffer += read();
- },
- start () {
- switch (c$2) {
- case '{':
- case '[':
- return newToken('punctuator', read())
- // This code is unreachable since the default lexState handles eof.
- // case undefined:
- // return newToken('eof')
- }
- lexState = 'value';
- },
- beforePropertyName () {
- switch (c$2) {
- case '$':
- case '_':
- buffer = read();
- lexState = 'identifierName';
- return
- case '\\':
- read();
- lexState = 'identifierNameStartEscape';
- return
- case '}':
- return newToken('punctuator', read())
- case '"':
- case "'":
- doubleQuote = (read() === '"');
- lexState = 'string';
- return
- }
- if (util.isIdStartChar(c$2)) {
- buffer += read();
- lexState = 'identifierName';
- return
- }
- throw invalidChar(read())
- },
- afterPropertyName () {
- if (c$2 === ':') {
- return newToken('punctuator', read())
- }
- throw invalidChar(read())
- },
- beforePropertyValue () {
- lexState = 'value';
- },
- afterPropertyValue () {
- switch (c$2) {
- case ',':
- case '}':
- return newToken('punctuator', read())
- }
- throw invalidChar(read())
- },
- beforeArrayValue () {
- if (c$2 === ']') {
- return newToken('punctuator', read())
- }
- lexState = 'value';
- },
- afterArrayValue () {
- switch (c$2) {
- case ',':
- case ']':
- return newToken('punctuator', read())
- }
- throw invalidChar(read())
- },
- end () {
- // This code is unreachable since it's handled by the default lexState.
- // if (c === undefined) {
- // read()
- // return newToken('eof')
- // }
- throw invalidChar(read())
- },
- };
- function newToken (type, value) {
- return {
- type,
- value,
- line,
- column,
- }
- }
- function literal (s) {
- for (const c of s) {
- const p = peek();
- if (p !== c) {
- throw invalidChar(read())
- }
- read();
- }
- }
- function escape$1 () {
- const c = peek();
- switch (c) {
- case 'b':
- read();
- return '\b'
- case 'f':
- read();
- return '\f'
- case 'n':
- read();
- return '\n'
- case 'r':
- read();
- return '\r'
- case 't':
- read();
- return '\t'
- case 'v':
- read();
- return '\v'
- case '0':
- read();
- if (util.isDigit(peek())) {
- throw invalidChar(read())
- }
- return '\0'
- case 'x':
- read();
- return hexEscape()
- case 'u':
- read();
- return unicodeEscape()
- case '\n':
- case '\u2028':
- case '\u2029':
- read();
- return ''
- case '\r':
- read();
- if (peek() === '\n') {
- read();
- }
- return ''
- case '1':
- case '2':
- case '3':
- case '4':
- case '5':
- case '6':
- case '7':
- case '8':
- case '9':
- throw invalidChar(read())
- case undefined:
- throw invalidChar(read())
- }
- return read()
- }
- function hexEscape () {
- let buffer = '';
- let c = peek();
- if (!util.isHexDigit(c)) {
- throw invalidChar(read())
- }
- buffer += read();
- c = peek();
- if (!util.isHexDigit(c)) {
- throw invalidChar(read())
- }
- buffer += read();
- return String.fromCodePoint(parseInt(buffer, 16))
- }
- function unicodeEscape () {
- let buffer = '';
- let count = 4;
- while (count-- > 0) {
- const c = peek();
- if (!util.isHexDigit(c)) {
- throw invalidChar(read())
- }
- buffer += read();
- }
- return String.fromCodePoint(parseInt(buffer, 16))
- }
- const parseStates = {
- start () {
- if (token.type === 'eof') {
- throw invalidEOF()
- }
- push();
- },
- beforePropertyName () {
- switch (token.type) {
- case 'identifier':
- case 'string':
- key = token.value;
- parseState = 'afterPropertyName';
- return
- case 'punctuator':
- // This code is unreachable since it's handled by the lexState.
- // if (token.value !== '}') {
- // throw invalidToken()
- // }
- pop();
- return
- case 'eof':
- throw invalidEOF()
- }
- // This code is unreachable since it's handled by the lexState.
- // throw invalidToken()
- },
- afterPropertyName () {
- // This code is unreachable since it's handled by the lexState.
- // if (token.type !== 'punctuator' || token.value !== ':') {
- // throw invalidToken()
- // }
- if (token.type === 'eof') {
- throw invalidEOF()
- }
- parseState = 'beforePropertyValue';
- },
- beforePropertyValue () {
- if (token.type === 'eof') {
- throw invalidEOF()
- }
- push();
- },
- beforeArrayValue () {
- if (token.type === 'eof') {
- throw invalidEOF()
- }
- if (token.type === 'punctuator' && token.value === ']') {
- pop();
- return
- }
- push();
- },
- afterPropertyValue () {
- // This code is unreachable since it's handled by the lexState.
- // if (token.type !== 'punctuator') {
- // throw invalidToken()
- // }
- if (token.type === 'eof') {
- throw invalidEOF()
- }
- switch (token.value) {
- case ',':
- parseState = 'beforePropertyName';
- return
- case '}':
- pop();
- }
- // This code is unreachable since it's handled by the lexState.
- // throw invalidToken()
- },
- afterArrayValue () {
- // This code is unreachable since it's handled by the lexState.
- // if (token.type !== 'punctuator') {
- // throw invalidToken()
- // }
- if (token.type === 'eof') {
- throw invalidEOF()
- }
- switch (token.value) {
- case ',':
- parseState = 'beforeArrayValue';
- return
- case ']':
- pop();
- }
- // This code is unreachable since it's handled by the lexState.
- // throw invalidToken()
- },
- end () {
- // This code is unreachable since it's handled by the lexState.
- // if (token.type !== 'eof') {
- // throw invalidToken()
- // }
- },
- };
- function push () {
- let value;
- switch (token.type) {
- case 'punctuator':
- switch (token.value) {
- case '{':
- value = {};
- break
- case '[':
- value = [];
- break
- }
- break
- case 'null':
- case 'boolean':
- case 'numeric':
- case 'string':
- value = token.value;
- break
- // This code is unreachable.
- // default:
- // throw invalidToken()
- }
- if (root === undefined) {
- root = value;
- } else {
- const parent = stack[stack.length - 1];
- if (Array.isArray(parent)) {
- parent.push(value);
- } else {
- parent[key] = value;
- }
- }
- if (value !== null && typeof value === 'object') {
- stack.push(value);
- if (Array.isArray(value)) {
- parseState = 'beforeArrayValue';
- } else {
- parseState = 'beforePropertyName';
- }
- } else {
- const current = stack[stack.length - 1];
- if (current == null) {
- parseState = 'end';
- } else if (Array.isArray(current)) {
- parseState = 'afterArrayValue';
- } else {
- parseState = 'afterPropertyValue';
- }
- }
- }
- function pop () {
- stack.pop();
- const current = stack[stack.length - 1];
- if (current == null) {
- parseState = 'end';
- } else if (Array.isArray(current)) {
- parseState = 'afterArrayValue';
- } else {
- parseState = 'afterPropertyValue';
- }
- }
- // This code is unreachable.
- // function invalidParseState () {
- // return new Error(`JSON5: invalid parse state '${parseState}'`)
- // }
- // This code is unreachable.
- // function invalidLexState (state) {
- // return new Error(`JSON5: invalid lex state '${state}'`)
- // }
- function invalidChar (c) {
- if (c === undefined) {
- return syntaxError(`JSON5: invalid end of input at ${line}:${column}`)
- }
- return syntaxError(`JSON5: invalid character '${formatChar(c)}' at ${line}:${column}`)
- }
- function invalidEOF () {
- return syntaxError(`JSON5: invalid end of input at ${line}:${column}`)
- }
- // This code is unreachable.
- // function invalidToken () {
- // if (token.type === 'eof') {
- // return syntaxError(`JSON5: invalid end of input at ${line}:${column}`)
- // }
- // const c = String.fromCodePoint(token.value.codePointAt(0))
- // return syntaxError(`JSON5: invalid character '${formatChar(c)}' at ${line}:${column}`)
- // }
- function invalidIdentifier () {
- column -= 5;
- return syntaxError(`JSON5: invalid identifier character at ${line}:${column}`)
- }
- function separatorChar (c) {
- console.warn(`JSON5: '${formatChar(c)}' in strings is not valid ECMAScript; consider escaping`);
- }
- function formatChar (c) {
- const replacements = {
- "'": "\\'",
- '"': '\\"',
- '\\': '\\\\',
- '\b': '\\b',
- '\f': '\\f',
- '\n': '\\n',
- '\r': '\\r',
- '\t': '\\t',
- '\v': '\\v',
- '\0': '\\0',
- '\u2028': '\\u2028',
- '\u2029': '\\u2029',
- };
- if (replacements[c]) {
- return replacements[c]
- }
- if (c < ' ') {
- const hexString = c.charCodeAt(0).toString(16);
- return '\\x' + ('00' + hexString).substring(hexString.length)
- }
- return c
- }
- function syntaxError (message) {
- const err = new SyntaxError(message);
- err.lineNumber = line;
- err.columnNumber = column;
- return err
- }
- var stringify = function stringify (value, replacer, space) {
- const stack = [];
- let indent = '';
- let propertyList;
- let replacerFunc;
- let gap = '';
- let quote;
- if (
- replacer != null &&
- typeof replacer === 'object' &&
- !Array.isArray(replacer)
- ) {
- space = replacer.space;
- quote = replacer.quote;
- replacer = replacer.replacer;
- }
- if (typeof replacer === 'function') {
- replacerFunc = replacer;
- } else if (Array.isArray(replacer)) {
- propertyList = [];
- for (const v of replacer) {
- let item;
- if (typeof v === 'string') {
- item = v;
- } else if (
- typeof v === 'number' ||
- v instanceof String ||
- v instanceof Number
- ) {
- item = String(v);
- }
- if (item !== undefined && propertyList.indexOf(item) < 0) {
- propertyList.push(item);
- }
- }
- }
- if (space instanceof Number) {
- space = Number(space);
- } else if (space instanceof String) {
- space = String(space);
- }
- if (typeof space === 'number') {
- if (space > 0) {
- space = Math.min(10, Math.floor(space));
- gap = ' '.substr(0, space);
- }
- } else if (typeof space === 'string') {
- gap = space.substr(0, 10);
- }
- return serializeProperty('', {'': value})
- function serializeProperty (key, holder) {
- let value = holder[key];
- if (value != null) {
- if (typeof value.toJSON5 === 'function') {
- value = value.toJSON5(key);
- } else if (typeof value.toJSON === 'function') {
- value = value.toJSON(key);
- }
- }
- if (replacerFunc) {
- value = replacerFunc.call(holder, key, value);
- }
- if (value instanceof Number) {
- value = Number(value);
- } else if (value instanceof String) {
- value = String(value);
- } else if (value instanceof Boolean) {
- value = value.valueOf();
- }
- switch (value) {
- case null: return 'null'
- case true: return 'true'
- case false: return 'false'
- }
- if (typeof value === 'string') {
- return quoteString(value, false)
- }
- if (typeof value === 'number') {
- return String(value)
- }
- if (typeof value === 'object') {
- return Array.isArray(value) ? serializeArray(value) : serializeObject(value)
- }
- return undefined
- }
- function quoteString (value) {
- const quotes = {
- "'": 0.1,
- '"': 0.2,
- };
- const replacements = {
- "'": "\\'",
- '"': '\\"',
- '\\': '\\\\',
- '\b': '\\b',
- '\f': '\\f',
- '\n': '\\n',
- '\r': '\\r',
- '\t': '\\t',
- '\v': '\\v',
- '\0': '\\0',
- '\u2028': '\\u2028',
- '\u2029': '\\u2029',
- };
- let product = '';
- for (let i = 0; i < value.length; i++) {
- const c = value[i];
- switch (c) {
- case "'":
- case '"':
- quotes[c]++;
- product += c;
- continue
- case '\0':
- if (util.isDigit(value[i + 1])) {
- product += '\\x00';
- continue
- }
- }
- if (replacements[c]) {
- product += replacements[c];
- continue
- }
- if (c < ' ') {
- let hexString = c.charCodeAt(0).toString(16);
- product += '\\x' + ('00' + hexString).substring(hexString.length);
- continue
- }
- product += c;
- }
- const quoteChar = quote || Object.keys(quotes).reduce((a, b) => (quotes[a] < quotes[b]) ? a : b);
- product = product.replace(new RegExp(quoteChar, 'g'), replacements[quoteChar]);
- return quoteChar + product + quoteChar
- }
- function serializeObject (value) {
- if (stack.indexOf(value) >= 0) {
- throw TypeError('Converting circular structure to JSON5')
- }
- stack.push(value);
- let stepback = indent;
- indent = indent + gap;
- let keys = propertyList || Object.keys(value);
- let partial = [];
- for (const key of keys) {
- const propertyString = serializeProperty(key, value);
- if (propertyString !== undefined) {
- let member = serializeKey(key) + ':';
- if (gap !== '') {
- member += ' ';
- }
- member += propertyString;
- partial.push(member);
- }
- }
- let final;
- if (partial.length === 0) {
- final = '{}';
- } else {
- let properties;
- if (gap === '') {
- properties = partial.join(',');
- final = '{' + properties + '}';
- } else {
- let separator = ',\n' + indent;
- properties = partial.join(separator);
- final = '{\n' + indent + properties + ',\n' + stepback + '}';
- }
- }
- stack.pop();
- indent = stepback;
- return final
- }
- function serializeKey (key) {
- if (key.length === 0) {
- return quoteString(key, true)
- }
- const firstChar = String.fromCodePoint(key.codePointAt(0));
- if (!util.isIdStartChar(firstChar)) {
- return quoteString(key, true)
- }
- for (let i = firstChar.length; i < key.length; i++) {
- if (!util.isIdContinueChar(String.fromCodePoint(key.codePointAt(i)))) {
- return quoteString(key, true)
- }
- }
- return key
- }
- function serializeArray (value) {
- if (stack.indexOf(value) >= 0) {
- throw TypeError('Converting circular structure to JSON5')
- }
- stack.push(value);
- let stepback = indent;
- indent = indent + gap;
- let partial = [];
- for (let i = 0; i < value.length; i++) {
- const propertyString = serializeProperty(String(i), value);
- partial.push((propertyString !== undefined) ? propertyString : 'null');
- }
- let final;
- if (partial.length === 0) {
- final = '[]';
- } else {
- if (gap === '') {
- let properties = partial.join(',');
- final = '[' + properties + ']';
- } else {
- let separator = ',\n' + indent;
- let properties = partial.join(separator);
- final = '[\n' + indent + properties + ',\n' + stepback + ']';
- }
- }
- stack.pop();
- indent = stepback;
- return final
- }
- };
- const JSON5 = {
- parse,
- stringify,
- };
- var lib = JSON5;
- class Sidebar{
- constructor(viewer){
- this.viewer = viewer;
- this.measuringTool = viewer.measuringTool;
- this.profileTool = viewer.profileTool;
- this.volumeTool = viewer.volumeTool;
- this.dom = $("#sidebar_root");
- }
- createToolIcon(icon, title, callback){
- let element = $(`
- <img src="${icon}"
- style="width: 32px; height: 32px"
- class="button-icon"
- data-i18n="${title}" />
- `);
- element.click(callback);
- return element;
- }
- init(){
- if(Potree.settings.editType == 'merge'){
- this.initMergeBar();
- this.initToolbar();
- this.initScene();
- this.initNavigation();
- }else {
- this.initAccordion();
- this.initAppearance();
- this.initToolbar();
- this.initScene();
- this.initNavigation();
- this.initFilters();
- this.initClippingTool();
- this.initSettings();
-
- if(Potree.settings.editType != 'pano'){
- this.initAlignment();
- this.initClipModel();
- this.initSiteModel();
- this.initParitcle();
-
- }else {
- this.initPanosEdit();
- }
-
- }
-
-
-
-
-
-
- $('#potree_version_number').html(Potree.version.major + "." + Potree.version.minor + Potree.version.suffix);
- }
-
- initAlignment(){
- let Alignment = viewer.modules.Alignment;
- var pannel = $('#alignment');
- var buttons = pannel.find('[name="transform"] button');
- var applyToPointcloud = (fun, value)=>{
- return function(){
- var selected = $('#alignment li[name="selectPointCloud"] input:checked' );
- Array.from(selected).forEach(e=>{
- var pointcloud = viewer.scene.pointclouds.find(p=>p.name == e.name);
- fun(pointcloud, value);
- });
-
-
- }
- };
- //逆时针是正数
- buttons.eq(0).on('click', applyToPointcloud(Alignment.rotate, 10));
-
- //viewer.scene.pointclouds[0].rotation.z += THREE.Math.degToRad(10)
-
- buttons.eq(1).on('click' ,applyToPointcloud(Alignment.rotate, 1));
- buttons.eq(2).on('click', applyToPointcloud(Alignment.rotate, 0.1));
-
- buttons.eq(3).on('click', applyToPointcloud(Alignment.rotate, -10));
- buttons.eq(4).on('click',applyToPointcloud(Alignment.rotate, -1));
- buttons.eq(5).on('click',applyToPointcloud(Alignment.rotate, -0.1));
-
-
- buttons.eq(6).on('click', applyToPointcloud(Alignment.translate, new Vector3(-1,0,0)));
- buttons.eq(7).on('click', applyToPointcloud(Alignment.translate, new Vector3(1,0,0)));
- buttons.eq(8).on('click', applyToPointcloud(Alignment.translate, new Vector3(0,-1,0)));
- buttons.eq(9).on('click', applyToPointcloud(Alignment.translate, new Vector3(0,1,0)));
- buttons.eq(10).on('click', applyToPointcloud(Alignment.translate, new Vector3(0,0,-1)));
- buttons.eq(11).on('click', applyToPointcloud(Alignment.translate, new Vector3(0,0,1)));
- pannel.find('#startAlignment').on('click', ()=>{
- Alignment.enter();
- });
- pannel.find('#exitAlignment').on('click', ()=>{
- Alignment.save();
- Alignment.leave();
- });
- pannel.find('#rotTool').on('click', ()=>{
- Alignment.switchHandle('rotate');
- });
- pannel.find('#moveTool').on('click', ()=>{
- Alignment.switchHandle('translate');
- });
-
-
-
- }
-
-
- initMergeBar(){//多元融合模块
- var pannel = $('#mergeModel');
- var buttons = pannel.find('button');
- let MergeEditor = viewer.modules.MergeEditor;
- let loading = false;
-
- pannel.find('ul[name="model"] li button').on('click',(e)=>{
- if(loading)return console.log('还在加载', loading)
- let $elem = $(e.target);
-
-
- let parent = $elem.parent();
- let name = parent.attr('name');
-
- if($elem.attr('name') == 'select'){
- return Potree.selectModel(name)
- }
-
- if($elem.text() == '添加'){
- let startTime = Date.now();
- Potree.addModel(name,()=>{
- loading = false;
- $elem.text('删除');
- let now = Date.now();
- console.log('加载完毕', name, '用时', (now-startTime)/1000, 's');
- });
- loading = name;
-
- }else {
- Potree.removeModel(name);
- $elem.text('添加');
- }
-
-
- });
-
- pannel.find('li button[name="splitScreen"]').on('click',(e)=>{
- let $elem = $(e.target);
- if($elem.text() == '分屏'){
- $elem.text('恢复');
- MergeEditor.enterSplit();
- }else {
- $elem.text('分屏');
- MergeEditor.leaveSplit();
- }
-
- });
- }
-
-
-
-
- addAlignmentButton(pointcloud){
- var pannel = $('#alignment li[name="selectPointCloud"]>div');
- var option = $(` <input name="${pointcloud.name}" class="editCheckbox" type="checkbox" >
-
- <label for="showingLabels">${pointcloud.name}</label>`);
-
- pannel.append(option);
- /* option.find("input").on('change',function(){
- })
- */
- }
- initClipModel(){
- let Clip = viewer.modules.Clip;
- var pannel = $('#clipModel');
- var buttons = pannel.find('button');
- buttons.eq(0).on('click', Clip.enter.bind(Clip));
- buttons.eq(1).on('click', Clip.download.bind(Clip));
- buttons.eq(2).on('click', Clip.leave.bind(Clip));
- }
- initSiteModel(){
- let SiteModel = viewer.modules.SiteModel;
- var pannel = $('#siteModel');
- pannel.find('button[name="start"] ').on('click', SiteModel.enter.bind(SiteModel));
- pannel.find('button[name="exit"] ').on('click', SiteModel.leave.bind(SiteModel));
- pannel.find('button[name="building"] ').on('click', SiteModel.startInsertion.bind(SiteModel,'building'));
- pannel.find('button[name="floor"] ').on('click', ()=>{
- SiteModel.addFloor(SiteModel.buildings[0], 'top');
- } );
- pannel.find('button[name="room"] ').on('click', ()=>{
- SiteModel.startInsertion('room', SiteModel.buildings[0].buildChildren[0]);
- });
- pannel.find('button[name="digHole"] ').on('click', ()=>{
- SiteModel.selected && SiteModel.startInsertion('hole', SiteModel.selected);
- });
-
-
-
- pannel.find('button[name="selectBuilding"] ').on('click', ()=>{
- SiteModel.selectEntity(SiteModel.buildings[0] );
- } );
- pannel.find('button[name="selectFloor"] ').on('click', ()=>{
- SiteModel.selectEntity(SiteModel.buildings[0].buildChildren[0]);
- } );
- pannel.find('button[name="selectRoom"] ').on('click', ()=>{
- SiteModel.selectEntity(SiteModel.buildings[0].buildChildren[0].buildChildren[0]);
- });
-
-
- pannel.find('button[name="removeFirstBuilding"] ').on('click', ()=>{
- SiteModel.removeEntity(SiteModel.buildings[0]);
- });
- pannel.find('button[name="removeFirstFloor"] ').on('click', ()=>{
- SiteModel.removeEntity(SiteModel.buildings[0].buildChildren[0]);
- });
- pannel.find('button[name="removeFirstRoom"] ').on('click', ()=>{
- SiteModel.removeEntity(SiteModel.buildings[0].buildChildren[0].buildChildren[0]);
- });
- pannel.find('button[name="removeFirstHole"] ').on('click', ()=>{
- SiteModel.selected.removeHole(SiteModel.selected.holes[0]);
- });
-
-
-
- pannel.find('button[name="removeFirstMarker"] ').on('click', ()=>{
- //SiteModel.removeMarker(SiteModel.selected.markers[0])
- SiteModel.selected.removeMarker(0);
- });
-
- }
- initParitcle(){
- let ParticleEditor = viewer.modules.ParticleEditor;
- var pannel = $('#particle');
- pannel.find('button[name="addFire"] ').on('click', ()=>{
- ParticleEditor.startInsertion('fire+smoke');
- });
- pannel.find('button[name="addExplode"] ').on('click', ()=>{
- ParticleEditor.startInsertion('explode');
- });
-
- }
-
- initPanosEdit(){
- let PanoEditor = viewer.modules.PanoEditor;
- let Alignment = viewer.modules.Alignment;
-
- var pannel = $('#panos');
- pannel.find('button[name="save"] ').on('click', ()=>{
- console.log('saveData',PanoEditor.exportSavingData());
- });
- pannel.find('button[name="translate"] ').on('click', ()=>{
- Alignment.switchHandle('translate');
- });
- pannel.find('button[name="rotate"] ').on('click', ()=>{
- Alignment.switchHandle('rotate');
- });
- pannel.find('button[name="topView"] ').on('click', ()=>{
- PanoEditor.switchView('top');
- });
- pannel.find('button[name="sideView"] ').on('click', ()=>{
- PanoEditor.switchView('right');
- });
- pannel.find('button[name="3DView"] ').on('click', ()=>{
- PanoEditor.switchView('mainView');
- });
-
- pannel.find('button[name="addLink"] ').on('click', ()=>{
- PanoEditor.setLinkOperateState('addLink', true);
- });
- pannel.find('button[name="removeLink"] ').on('click', ()=>{
- PanoEditor.setLinkOperateState('removeLink', true);
- });
-
- pannel.find('button[name="getCloser"] ').on('click', ()=>{
- PanoEditor.setZoomInState(true);
- });
-
-
- }
-
-
-
-
-
- initToolbar(){
- // ANGLE
- let elToolbar = $('#tools');
- elToolbar.append(this.createToolIcon(
- Potree.resourcePath + '/icons/angle.png',
- '[title]tt.angle_measurement',
- () => {
- $('#menu_measurements').next().slideDown();
- let measurement = this.measuringTool.startInsertion({
- showDistances: false,
- showAngles: true,
- showArea: false,
- closed: true,
- maxMarkers: 3,
- minMarkers:3,
- measureType: 'Angle'});
- let measurementsRoot = $("#jstree_scene").jstree().get_json("measurements");
- let jsonNode = measurementsRoot.children.find(child => child.data.uuid === measurement.uuid);
- $.jstree.reference(jsonNode.id).deselect_all();
- $.jstree.reference(jsonNode.id).select_node(jsonNode.id);
- }
- ));
- // POINT
- elToolbar.append(this.createToolIcon(
- Potree.resourcePath + '/icons/point.svg',
- '[title]tt.point_measurement',
- () => {
- $('#menu_measurements').next().slideDown();
- let measurement = this.measuringTool.startInsertion({
- showDistances: false,
- showAngles: false,
- showCoordinates: true,
- showEdges:false,
- showArea: false,
- closed: true,
- maxMarkers: 1,
- minMarkers:1,
- measureType: 'Point'});
- let measurementsRoot = $("#jstree_scene").jstree().get_json("measurements");
- let jsonNode = measurementsRoot.children.find(child => child.data.uuid === measurement.uuid);
- $.jstree.reference(jsonNode.id).deselect_all();
- $.jstree.reference(jsonNode.id).select_node(jsonNode.id);
- }
- ));
- // DISTANCE
- elToolbar.append(this.createToolIcon(
- Potree.resourcePath + '/icons/distance.svg',
- '[title]tt.distance_measurement',
- () => {
- $('#menu_measurements').next().slideDown();
- let measurement = this.measuringTool.startInsertion({
- showDistances: true,
- showArea: false,
- closed: false,
- minMarkers:2,
- maxMarkers: 2,
- measureType: 'Distance'
- });
- let measurementsRoot = $("#jstree_scene").jstree().get_json("measurements");
- let jsonNode = measurementsRoot.children.find(child => child.data.uuid === measurement.uuid);
- $.jstree.reference(jsonNode.id).deselect_all();
- $.jstree.reference(jsonNode.id).select_node(jsonNode.id);
- }
- ));
- // HEIGHT
- elToolbar.append(this.createToolIcon(
- Potree.resourcePath + '/icons/height.svg',
- '[title]tt.height_measurement',
- () => {
- $('#menu_measurements').next().slideDown();
- let measurement = this.measuringTool.startInsertion({
- showDistances: true,//false,
- showHeight: true,
- showArea: false,
- closed: false,
- maxMarkers: 2,
- minMarkers:2,
- //showGuideLine: true: true
- measureType: 'Ver Distance',
- });
- let measurementsRoot = $("#jstree_scene").jstree().get_json("measurements");
- let jsonNode = measurementsRoot.children.find(child => child.data.uuid === measurement.uuid);
- $.jstree.reference(jsonNode.id).deselect_all();
- $.jstree.reference(jsonNode.id).select_node(jsonNode.id);
- }
- ));
- // CIRCLE
- elToolbar.append(this.createToolIcon(
- Potree.resourcePath + '/icons/circle.svg',
- '[title]tt.circle_measurement',
- () => {
- $('#menu_measurements').next().slideDown();
- let measurement = this.measuringTool.startInsertion({
- showDistances: false,
- showHeight: false,
- showArea: false,
- showCircle: true,
- showEdges: false,
- closed: false,
- maxMarkers: 3,
- minMarkers:3,
- measureType: 'Circle'
- });
- let measurementsRoot = $("#jstree_scene").jstree().get_json("measurements");
- let jsonNode = measurementsRoot.children.find(child => child.data.uuid === measurement.uuid);
- $.jstree.reference(jsonNode.id).deselect_all();
- $.jstree.reference(jsonNode.id).select_node(jsonNode.id);
- }
- ));
- // AZIMUTH
- elToolbar.append(this.createToolIcon(
- Potree.resourcePath + '/icons/azimuth.svg',
- 'Azimuth',
- () => {
- $('#menu_measurements').next().slideDown();
- let measurement = this.measuringTool.startInsertion({
- showDistances: false,
- showHeight: false,
- showArea: false,
- showCircle: false,
- showEdges: false,
- showAzimuth: true,
- closed: false,
- maxMarkers: 2,
- minMarkers:2,
- measureType: 'Azimuth'});
- let measurementsRoot = $("#jstree_scene").jstree().get_json("measurements");
- let jsonNode = measurementsRoot.children.find(child => child.data.uuid === measurement.uuid);
- $.jstree.reference(jsonNode.id).deselect_all();
- $.jstree.reference(jsonNode.id).select_node(jsonNode.id);
- }
- ));
- // AREA
- elToolbar.append(this.createToolIcon(
- Potree.resourcePath + '/icons/area.svg',
- '[title]tt.area_measurement',
- () => {
- $('#menu_measurements').next().slideDown();
- let measurement = this.measuringTool.startInsertion({
- showDistances: true,
- showArea: true,
- closed: true,
- minMarkers:3,
- //showGuideLine: true: true,
- measureType: 'Area'});
- let measurementsRoot = $("#jstree_scene").jstree().get_json("measurements");
- let jsonNode = measurementsRoot.children.find(child => child.data.uuid === measurement.uuid);
- $.jstree.reference(jsonNode.id).deselect_all();
- $.jstree.reference(jsonNode.id).select_node(jsonNode.id);
- }
- ));
-
-
- //Hor AREA
- elToolbar.append(this.createToolIcon(
- Potree.resourcePath + '/icons/area.svg',
- '[title]tt.Hor Area',
- () => {
- $('#menu_measurements').next().slideDown();
- let measurement = this.measuringTool.startInsertion({
- showDistances: true,
- showArea: true,
- closed: true,
- minMarkers:3,
- measureType: 'Hor Area'});
- let measurementsRoot = $("#jstree_scene").jstree().get_json("measurements");
- let jsonNode = measurementsRoot.children.find(child => child.data.uuid === measurement.uuid);
- $.jstree.reference(jsonNode.id).deselect_all();
- $.jstree.reference(jsonNode.id).select_node(jsonNode.id);
- }
- ));
-
-
- // Ver Area
- elToolbar.append(this.createToolIcon(
- Potree.resourcePath + '/icons/area.svg',
- '[title]tt.Ver Area',
- () => {
- $('#menu_measurements').next().slideDown();
- let measurement = this.measuringTool.startInsertion({
- showDistances: true,
- showArea: true,
- closed: true,
- minMarkers:3,
- measureType: 'Ver Area'});
- let measurementsRoot = $("#jstree_scene").jstree().get_json("measurements");
- let jsonNode = measurementsRoot.children.find(child => child.data.uuid === measurement.uuid);
- $.jstree.reference(jsonNode.id).deselect_all();
- $.jstree.reference(jsonNode.id).select_node(jsonNode.id);
- }
- ));
-
- // rect area freedom direction
- elToolbar.append(this.createToolIcon(
- Potree.resourcePath + '/icons/area.svg',
- '[title]tt.area_freedom_rect',
- () => {
- $('#menu_measurements').next().slideDown();
- let measurement = this.measuringTool.startInsertion({
- showDistances: true,
- showArea: true,
- closed: true,
- minMarkers:4,
- maxMarkers:4,
- //showGuideLine: true: true,
- isRect:true,
- measureType: 'Rect Area'});
- let measurementsRoot = $("#jstree_scene").jstree().get_json("measurements");
- let jsonNode = measurementsRoot.children.find(child => child.data.uuid === measurement.uuid);
- $.jstree.reference(jsonNode.id).deselect_all();
- $.jstree.reference(jsonNode.id).select_node(jsonNode.id);
- }
- ));
- // rect area horizontal
- elToolbar.append(this.createToolIcon(
- Potree.resourcePath + '/icons/area.svg',
- '[title]tt.area_horizontal_rect',
- () => {
- $('#menu_measurements').next().slideDown();
- let measurement = this.measuringTool.startInsertion({
- showDistances: true,
- showArea: true,
- closed: true,
- minMarkers:4,
- maxMarkers:4,
- //showGuideLine: true: true,
- isRect:true,
- faceDirection:"horizontal",
- measureType: 'Hor Rect Area'});
- let measurementsRoot = $("#jstree_scene").jstree().get_json("measurements");
- let jsonNode = measurementsRoot.children.find(child => child.data.uuid === measurement.uuid);
- $.jstree.reference(jsonNode.id).deselect_all();
- $.jstree.reference(jsonNode.id).select_node(jsonNode.id);
- }
- ));
- // rect area vertical
- elToolbar.append(this.createToolIcon(
- Potree.resourcePath + '/icons/area.svg',
- '[title]tt.area_vertical_rect',
- () => {
- $('#menu_measurements').next().slideDown();
- let measurement = this.measuringTool.startInsertion({
- showDistances: true,
- showArea: true,
- closed: true,
- minMarkers:4,
- maxMarkers:4,
- //showGuideLine: true: true,
- isRect:true,
- faceDirection:"vertical",
- measureType: 'Ver Rect Area'});
- let measurementsRoot = $("#jstree_scene").jstree().get_json("measurements");
- let jsonNode = measurementsRoot.children.find(child => child.data.uuid === measurement.uuid);
- $.jstree.reference(jsonNode.id).deselect_all();
- $.jstree.reference(jsonNode.id).select_node(jsonNode.id);
- }
- ));
-
-
-
- // VOLUME
- elToolbar.append(this.createToolIcon(
- Potree.resourcePath + '/icons/volume.svg',
- '[title]tt.volume_measurement',
- () => {
- let volume = this.volumeTool.startInsertion();
- let measurementsRoot = $("#jstree_scene").jstree().get_json("measurements");
- let jsonNode = measurementsRoot.children.find(child => child.data.uuid === volume.uuid);
- $.jstree.reference(jsonNode.id).deselect_all();
- $.jstree.reference(jsonNode.id).select_node(jsonNode.id);
- }
- ));
- // SPHERE VOLUME
- elToolbar.append(this.createToolIcon(
- Potree.resourcePath + '/icons/sphere_distances.svg',
- '[title]tt.volume_measurement',
- () => {
- let volume = this.volumeTool.startInsertion({type: SphereVolume});
- let measurementsRoot = $("#jstree_scene").jstree().get_json("measurements");
- let jsonNode = measurementsRoot.children.find(child => child.data.uuid === volume.uuid);
- $.jstree.reference(jsonNode.id).deselect_all();
- $.jstree.reference(jsonNode.id).select_node(jsonNode.id);
- }
- ));
- // PROFILE
- elToolbar.append(this.createToolIcon(
- Potree.resourcePath + '/icons/profile.svg',
- '[title]tt.height_profile',
- () => {
- $('#menu_measurements').next().slideDown(); ;
- let profile = this.profileTool.startInsertion();
- let measurementsRoot = $("#jstree_scene").jstree().get_json("measurements");
- let jsonNode = measurementsRoot.children.find(child => child.data.uuid === profile.uuid);
- $.jstree.reference(jsonNode.id).deselect_all();
- $.jstree.reference(jsonNode.id).select_node(jsonNode.id);
- }
- ));
- // ANNOTATION
- elToolbar.append(this.createToolIcon(
- Potree.resourcePath + '/icons/annotation.svg',
- '[title]tt.annotation',
- () => {
- $('#menu_measurements').next().slideDown(); ;
- let annotation = this.viewer.annotationTool.startInsertion();
- let annotationsRoot = $("#jstree_scene").jstree().get_json("annotations");
- let jsonNode = annotationsRoot.children.find(child => child.data.uuid === annotation.uuid);
- $.jstree.reference(jsonNode.id).deselect_all();
- $.jstree.reference(jsonNode.id).select_node(jsonNode.id);
- }
- ));
- // REMOVE ALL
- elToolbar.append(this.createToolIcon(
- Potree.resourcePath + '/icons/reset_tools.svg',
- '[title]tt.remove_all_measurement',
- () => {
- this.viewer.scene.removeAllMeasurements();
- }
- ));
- { // SHOW / HIDE Measurements
- let elShow = $("#measurement_options_show");
- elShow.selectgroup({title: "Show/Hide labels"});
- elShow.find("input").click( (e) => {
- const show = e.target.value === "SHOW";
- this.measuringTool.showLabels = show;
- });
- let currentShow = this.measuringTool.showLabels ? "SHOW" : "HIDE";
- elShow.find(`input[value=${currentShow}]`).trigger("click");
- }
- }
- initScene(){
- let elScene = $("#menu_scene");
- let elObjects = elScene.next().find("#scene_objects");
- let elProperties = elScene.next().find("#scene_object_properties");
-
- {
- let elExport = elScene.next().find("#scene_export");
- let geoJSONIcon = `${Potree.resourcePath}/icons/file_geojson.svg`;
- let dxfIcon = `${Potree.resourcePath}/icons/file_dxf.svg`;
- let potreeIcon = `${Potree.resourcePath}/icons/file_potree.svg`;
- elExport.append(`
- Export: <br>
- <a href="#" download="measure.json"><img name="geojson_export_button" src="${geoJSONIcon}" class="button-icon" style="height: 24px" /></a>
- <a href="#" download="measure.dxf"><img name="dxf_export_button" src="${dxfIcon}" class="button-icon" style="height: 24px" /></a>
- <a href="#" download="potree.json5"><img name="potree_export_button" src="${potreeIcon}" class="button-icon" style="height: 24px" /></a>
- `);
- let elDownloadJSON = elExport.find("img[name=geojson_export_button]").parent();
- elDownloadJSON.click( (event) => {
- let scene = this.viewer.scene;
- let measurements = [...scene.measurements, ...scene.profiles, ...scene.volumes];
- if(measurements.length > 0){
- let geoJson = GeoJSONExporter.toString(measurements);
- let url = window.URL.createObjectURL(new Blob([geoJson], {type: 'data:application/octet-stream'}));
- elDownloadJSON.attr('href', url);
- }else {
- this.viewer.postError("nothing to export");
- event.preventDefault();
- }
- });
- let elDownloadDXF = elExport.find("img[name=dxf_export_button]").parent();
- elDownloadDXF.click( (event) => {
- let scene = this.viewer.scene;
- let measurements = [...scene.measurements, ...scene.profiles, ...scene.volumes];
- if(measurements.length > 0){
- let dxf = DXFExporter.toString(measurements);
- let url = window.URL.createObjectURL(new Blob([dxf], {type: 'data:application/octet-stream'}));
- elDownloadDXF.attr('href', url);
- }else {
- this.viewer.postError("no measurements to export");
- event.preventDefault();
- }
- });
- let elDownloadPotree = elExport.find("img[name=potree_export_button]").parent();
- elDownloadPotree.click( (event) => {
- let data = Potree.saveProject(this.viewer);
- let dataString = lib.stringify(data, null, "\t");
- let url = window.URL.createObjectURL(new Blob([dataString], {type: 'data:application/octet-stream'}));
- elDownloadPotree.attr('href', url);
- });
- }
- let propertiesPanel = new PropertiesPanel(elProperties, this.viewer);
- propertiesPanel.setScene(this.viewer.scene);
-
- localStorage.removeItem('jstree');
- let tree = $(`<div id="jstree_scene"></div>`);
- elObjects.append(tree);
- tree.jstree({
- 'plugins': ["checkbox", "state"],
- 'core': {
- "dblclick_toggle": false,
- "state": {
- "checked" : true
- },
- 'check_callback': true,
- "expand_selected_onload": true
- },
- "checkbox" : {
- "keep_selected_style": true,
- "three_state": false,
- "whole_node": false,
- "tie_selection": false,
- },
- });
- let createNode = (parent, text, icon, object) => {
- let nodeID = tree.jstree('create_node', parent, {
- "text": text,
- "icon": icon,
- "data": object
- },
- "last", false, false);
-
- if(object.visible){
- tree.jstree('check_node', nodeID);
- }else {
- tree.jstree('uncheck_node', nodeID);
- }
- return nodeID;
- };
- let pcID = tree.jstree('create_node', "#", { "text": "<b>Point Clouds</b>", "id": "pointclouds"}, "last", false, false);
- let measurementID = tree.jstree('create_node', "#", { "text": "<b>Measurements</b>", "id": "measurements" }, "last", false, false);
- let annotationsID = tree.jstree('create_node', "#", { "text": "<b>Annotations</b>", "id": "annotations" }, "last", false, false);
- let otherID = tree.jstree('create_node', "#", { "text": "<b>Other</b>", "id": "other" }, "last", false, false);
- let vectorsID = tree.jstree('create_node', "#", { "text": "<b>Vectors</b>", "id": "vectors" }, "last", false, false);
- let imagesID = tree.jstree('create_node', "#", { "text": "<b> Images</b>", "id": "images" }, "last", false, false);
- tree.jstree("check_node", pcID);
- tree.jstree("check_node", measurementID);
- tree.jstree("check_node", annotationsID);
- tree.jstree("check_node", otherID);
- tree.jstree("check_node", vectorsID);
- tree.jstree("check_node", imagesID);
- tree.on('create_node.jstree', (e, data) => {
- tree.jstree("open_all");
- });
- tree.on("select_node.jstree", (e, data) => {
- let object = data.node.data;
- propertiesPanel.set(object);
- this.viewer.inputHandler.deselectAll();
- if(object instanceof Volume){
- this.viewer.inputHandler.toggleSelection(object);
- }
- $(this.viewer.renderer.domElement).focus();
- });
- tree.on("deselect_node.jstree", (e, data) => {
- propertiesPanel.set(null);
- });
- tree.on("delete_node.jstree", (e, data) => {
- propertiesPanel.set(null);
- });
- tree.on('dblclick','.jstree-anchor', (e) => {
- let instance = $.jstree.reference(e.target);
- let node = instance.get_node(e.target);
- let object = node.data;
- // ignore double click on checkbox
- if(e.target.classList.contains("jstree-checkbox")){
- return;
- }
- if(object instanceof PointCloudTree){
- let box = this.viewer.getBoundingBox([object]);
- let node = new Object3D();
- node.boundingBox = box;
- this.viewer.zoomTo(node, 1, 500);
- }else if(object instanceof Measure){
- let points = object.points.map(p => p.position);
- let box = new Box3().setFromPoints(points);
-
- if(box.getSize(new Vector3()).length() == 0){
- box.min = box.max.clone();//禁止相同
- box.expandByVector(new Vector3(1,1,1));
- }
-
- let node = new Object3D();
- node.boundingBox = box;
- this.viewer.zoomTo(node, 2, 500);
-
- }else if(object instanceof Profile){
- let points = object.points;
- let box = new Box3().setFromPoints(points);
- if(box.getSize(new Vector3()).length() > 0){
- let node = new Object3D();
- node.boundingBox = box;
- this.viewer.zoomTo(node, 1, 500);
- }
- }else if(object instanceof Volume){
-
- let box = object.boundingBox.clone().applyMatrix4(object.matrixWorld);
- if(box.getSize(new Vector3()).length() > 0){
- let node = new Object3D();
- node.boundingBox = box;
- this.viewer.zoomTo(node, 1, 500);
- }
- }else if(object instanceof Annotation){
- object.moveHere(this.viewer.scene.getActiveCamera());
- }else if(object instanceof PolygonClipVolume){
- let dir = object.camera.getWorldDirection(new Vector3());
- let target;
- if(object.camera instanceof OrthographicCamera){
- dir.multiplyScalar(object.camera.right);
- target = new Vector3().addVectors(object.camera.position, dir);
- this.viewer.setCameraMode(CameraMode.ORTHOGRAPHIC);
- }else if(object.camera instanceof PerspectiveCamera){
- dir.multiplyScalar(this.viewer.scene.view.radius);
- target = new Vector3().addVectors(object.camera.position, dir);
- this.viewer.setCameraMode(CameraMode.PERSPECTIVE);
- }
-
- this.viewer.scene.view.position.copy(object.camera.position);
- this.viewer.scene.view.lookAt(target);
- }else if(object.type === "SpotLight"){
- let distance = (object.distance > 0) ? object.distance / 4 : 5 * 1000;
- let position = object.position;
- let target = new Vector3().addVectors(
- position,
- object.getWorldDirection(new Vector3()).multiplyScalar(distance));
- this.viewer.scene.view.position.copy(object.position);
- this.viewer.scene.view.lookAt(target);
- }else if(object instanceof Object3D){
- let box = new Box3().setFromObject(object);
- if(box.getSize(new Vector3()).length() > 0){
- let node = new Object3D();
- node.boundingBox = box;
- this.viewer.zoomTo(node, 1, 500);
- }
- }else if(object instanceof OrientedImage){
- // TODO zoom to images
- // let box = new THREE.Box3().setFromObject(object);
- // if(box.getSize(new THREE.Vector3()).length() > 0){
- // let node = new THREE.Object3D();
- // node.boundingBox = box;
- // this.viewer.zoomTo(node, 1, 500);
- // }
- }else if(object instanceof Images360){
- // TODO
- }else if(object instanceof Geopackage){
- // TODO
- }
- });
- tree.on("uncheck_node.jstree", (e, data) => {
- let object = data.node.data;
- if(object){
- object.visible = false;
- }
- });
- tree.on("check_node.jstree", (e, data) => {
- let object = data.node.data;
- if(object){
- object.visible = true;
- }
- });
- let onPointCloudAdded = (e) => {
- let pointcloud = e.pointcloud;
- let cloudIcon = `${Potree.resourcePath}/icons/cloud.svg`;
- let node = createNode(pcID, pointcloud.name, cloudIcon, pointcloud);
- pointcloud.addEventListener("visibility_changed", () => {
- if(pointcloud.visible){
- tree.jstree('check_node', node);
- }else {
- tree.jstree('uncheck_node', node);
- }
- });
- };
- let onMeasurementAdded = (e) => {
- let measurement = e.measurement;
- let icon = Utils.getMeasurementIcon(measurement);
- createNode(measurementID, measurement.name, icon, measurement);
- };
- let onVolumeAdded = (e) => {
- let volume = e.volume;
- let icon = Utils.getMeasurementIcon(volume);
- let node = createNode(measurementID, volume.name, icon, volume);
- volume.addEventListener("visibility_changed", () => {
- if(volume.visible){
- tree.jstree('check_node', node);
- }else {
- tree.jstree('uncheck_node', node);
- }
- });
- };
- let onProfileAdded = (e) => {
- let profile = e.profile;
- let icon = Utils.getMeasurementIcon(profile);
- createNode(measurementID, profile.name, icon, profile);
- };
- let onAnnotationAdded = (e) => {
- let annotation = e.annotation;
- let annotationIcon = `${Potree.resourcePath}/icons/annotation.svg`;
- let parentID = this.annotationMapping.get(annotation.parent);
- let annotationID = createNode(parentID, annotation.title, annotationIcon, annotation);
- this.annotationMapping.set(annotation, annotationID);
- annotation.addEventListener("annotation_changed", (e) => {
- let annotationsRoot = $("#jstree_scene").jstree().get_json("annotations");
- let jsonNode = annotationsRoot.children.find(child => child.data.uuid === annotation.uuid);
-
- $.jstree.reference(jsonNode.id).rename_node(jsonNode.id, annotation.title);
- });
- };
- let onCameraAnimationAdded = (e) => {
- const animation = e.animation;
- const animationIcon = `${Potree.resourcePath}/icons/camera_animation.svg`;
- createNode(otherID, "animation", animationIcon, animation);
- };
- let onOrientedImagesAdded = (e) => {
- const images = e.images;
- const imagesIcon = `${Potree.resourcePath}/icons/picture.svg`;
- const node = createNode(imagesID, "images", imagesIcon, images);
- images.addEventListener("visibility_changed", () => {
- if(images.visible){
- tree.jstree('check_node', node);
- }else {
- tree.jstree('uncheck_node', node);
- }
- });
- };
- let onImages360Added = (e) => {
- const images = e.images;
- const imagesIcon = `${Potree.resourcePath}/icons/picture.svg`;
- const node = createNode(imagesID, "360° images", imagesIcon, images);
- images.addEventListener("visibility_changed", () => {
- if(images.visible){
- tree.jstree('check_node', node);
- }else {
- tree.jstree('uncheck_node', node);
- }
- });
- };
- const onGeopackageAdded = (e) => {
- const geopackage = e.geopackage;
- const geopackageIcon = `${Potree.resourcePath}/icons/triangle.svg`;
- const tree = $(`#jstree_scene`);
- const parentNode = "vectors";
- for(const layer of geopackage.node.children){
- const name = layer.name;
- let shpPointsID = tree.jstree('create_node', parentNode, {
- "text": name,
- "icon": geopackageIcon,
- "object": layer,
- "data": layer,
- },
- "last", false, false);
- tree.jstree(layer.visible ? "check_node" : "uncheck_node", shpPointsID);
- }
- };
- this.viewer.scene.addEventListener("pointcloud_added", onPointCloudAdded);
- this.viewer.scene.addEventListener("measurement_added", onMeasurementAdded);
- this.viewer.scene.addEventListener("profile_added", onProfileAdded);
- this.viewer.scene.addEventListener("volume_added", onVolumeAdded);
- this.viewer.scene.addEventListener("camera_animation_added", onCameraAnimationAdded);
- this.viewer.scene.addEventListener("oriented_images_added", onOrientedImagesAdded);
- this.viewer.scene.addEventListener("360_images_added", onImages360Added);
- this.viewer.scene.addEventListener("geopackage_added", onGeopackageAdded);
- this.viewer.scene.addEventListener("polygon_clip_volume_added", onVolumeAdded);
- this.viewer.scene.annotations.addEventListener("annotation_added", onAnnotationAdded);
- let onMeasurementRemoved = (e) => {
- let measurementsRoot = $("#jstree_scene").jstree().get_json("measurements");
- let jsonNode = measurementsRoot.children.find(child => child.data.uuid === e.measurement.uuid);
-
- tree.jstree("delete_node", jsonNode.id);
- };
- let onVolumeRemoved = (e) => {
- let measurementsRoot = $("#jstree_scene").jstree().get_json("measurements");
- let jsonNode = measurementsRoot.children.find(child => child.data.uuid === e.volume.uuid);
-
- tree.jstree("delete_node", jsonNode.id);
- };
- let onPolygonClipVolumeRemoved = (e) => {
- let measurementsRoot = $("#jstree_scene").jstree().get_json("measurements");
- let jsonNode = measurementsRoot.children.find(child => child.data.uuid === e.volume.uuid);
-
- tree.jstree("delete_node", jsonNode.id);
- };
- let onProfileRemoved = (e) => {
- let measurementsRoot = $("#jstree_scene").jstree().get_json("measurements");
- let jsonNode = measurementsRoot.children.find(child => child.data.uuid === e.profile.uuid);
-
- tree.jstree("delete_node", jsonNode.id);
- };
- this.viewer.scene.addEventListener("measurement_removed", onMeasurementRemoved);
- this.viewer.scene.addEventListener("volume_removed", onVolumeRemoved);
- this.viewer.scene.addEventListener("polygon_clip_volume_removed", onPolygonClipVolumeRemoved);
- this.viewer.scene.addEventListener("profile_removed", onProfileRemoved);
- {
- let annotationIcon = `${Potree.resourcePath}/icons/annotation.svg`;
- this.annotationMapping = new Map();
- this.annotationMapping.set(this.viewer.scene.annotations, annotationsID);
- this.viewer.scene.annotations.traverseDescendants(annotation => {
- let parentID = this.annotationMapping.get(annotation.parent);
- let annotationID = createNode(parentID, annotation.title, annotationIcon, annotation);
- this.annotationMapping.set(annotation, annotationID);
- });
- }
- const scene = this.viewer.scene;
- for(let pointcloud of scene.pointclouds){
- onPointCloudAdded({pointcloud: pointcloud});
- }
- for(let measurement of scene.measurements){
- onMeasurementAdded({measurement: measurement});
- }
- for(let volume of [...scene.volumes, ...scene.polygonClipVolumes]){
- onVolumeAdded({volume: volume});
- }
- for(let animation of scene.cameraAnimations){
- onCameraAnimationAdded({animation: animation});
- }
- for(let images of scene.orientedImages){
- onOrientedImagesAdded({images: images});
- }
- for(let images of scene.images360){
- onImages360Added({images: images});
- }
- for(const geopackage of scene.geopackages){
- onGeopackageAdded({geopackage: geopackage});
- }
- for(let profile of scene.profiles){
- onProfileAdded({profile: profile});
- }
- {
- createNode(otherID, "Camera", null, new Camera());
- }
- this.viewer.addEventListener("scene_changed", (e) => {
- propertiesPanel.setScene(e.scene);
- e.oldScene.removeEventListener("pointcloud_added", onPointCloudAdded);
- e.oldScene.removeEventListener("measurement_added", onMeasurementAdded);
- e.oldScene.removeEventListener("profile_added", onProfileAdded);
- e.oldScene.removeEventListener("volume_added", onVolumeAdded);
- e.oldScene.removeEventListener("polygon_clip_volume_added", onVolumeAdded);
- e.oldScene.removeEventListener("measurement_removed", onMeasurementRemoved);
- e.scene.addEventListener("pointcloud_added", onPointCloudAdded);
- e.scene.addEventListener("measurement_added", onMeasurementAdded);
- e.scene.addEventListener("profile_added", onProfileAdded);
- e.scene.addEventListener("volume_added", onVolumeAdded);
- e.scene.addEventListener("polygon_clip_volume_added", onVolumeAdded);
- e.scene.addEventListener("measurement_removed", onMeasurementRemoved);
- });
- }
- initClippingTool(){
- this.viewer.addEventListener("cliptask_changed", (event) => {
- console.log("TODO");
- });
- this.viewer.addEventListener("clipmethod_changed", (event) => {
- console.log("TODO");
- });
- {
- let elClipTask = $("#cliptask_options");
- elClipTask.selectgroup({title: "Clip Task"});
- elClipTask.find("input").click( (e) => {
- this.viewer.setClipTask(ClipTask[e.target.value]);
- });
- let currentClipTask = Object.keys(ClipTask)
- .filter(key => ClipTask[key] === this.viewer.clipTask);
- elClipTask.find(`input[value=${currentClipTask}]`).trigger("click");
- }
- {
- let elClipMethod = $("#clipmethod_options");
- elClipMethod.selectgroup({title: "Clip Method"});
- elClipMethod.find("input").click( (e) => {
- this.viewer.setClipMethod(ClipMethod[e.target.value]);
- });
- let currentClipMethod = Object.keys(ClipMethod)
- .filter(key => ClipMethod[key] === this.viewer.clipMethod);
- elClipMethod.find(`input[value=${currentClipMethod}]`).trigger("click");
- }
- let clippingToolBar = $("#clipping_tools");
- // CLIP VOLUME
- clippingToolBar.append(this.createToolIcon(
- Potree.resourcePath + '/icons/clip_volume.svg',
- '[title]tt.clip_volume',
- () => {
- let item = this.volumeTool.startInsertion({clip: true});
- let measurementsRoot = $("#jstree_scene").jstree().get_json("measurements");
- let jsonNode = measurementsRoot.children.find(child => child.data.uuid === item.uuid);
- $.jstree.reference(jsonNode.id).deselect_all();
- $.jstree.reference(jsonNode.id).select_node(jsonNode.id);
- }
- ));
- // CLIP POLYGON
- clippingToolBar.append(this.createToolIcon(
- Potree.resourcePath + "/icons/clip-polygon.svg",
- "[title]tt.clip_polygon",
- () => {
- let item = this.viewer.clippingTool.startInsertion({type: "polygon"});
- let measurementsRoot = $("#jstree_scene").jstree().get_json("measurements");
- let jsonNode = measurementsRoot.children.find(child => child.data.uuid === item.uuid);
- $.jstree.reference(jsonNode.id).deselect_all();
- $.jstree.reference(jsonNode.id).select_node(jsonNode.id);
- }
- ));
- {// SCREEN BOX SELECT
- let boxSelectTool = new ScreenBoxSelectTool(this.viewer);
- clippingToolBar.append(this.createToolIcon(
- Potree.resourcePath + "/icons/clip-screen.svg",
- "[title]tt.screen_clip_box",
- () => {
- if(!(this.viewer.scene.getActiveCamera() instanceof OrthographicCamera)){
- this.viewer.postMessage(`Switch to Orthographic Camera Mode before using the Screen-Box-Select tool.`,
- {duration: 2000});
- return;
- }
-
- let item = boxSelectTool.startInsertion();
- let measurementsRoot = $("#jstree_scene").jstree().get_json("measurements");
- let jsonNode = measurementsRoot.children.find(child => child.data.uuid === item.uuid);
- $.jstree.reference(jsonNode.id).deselect_all();
- $.jstree.reference(jsonNode.id).select_node(jsonNode.id);
- }
- ));
- }
- { // REMOVE CLIPPING TOOLS
- clippingToolBar.append(this.createToolIcon(
- Potree.resourcePath + "/icons/remove.svg",
- "[title]tt.remove_all_clipping_volumes",
- () => {
- this.viewer.scene.removeAllClipVolumes();
- }
- ));
- }
- }
- initFilters(){
- this.initClassificationList();
- this.initReturnFilters();
- this.initGPSTimeFilters();
- this.initPointSourceIDFilters();
- }
- initReturnFilters(){
- let elReturnFilterPanel = $('#return_filter_panel');
- { // RETURN NUMBER
- let sldReturnNumber = elReturnFilterPanel.find('#sldReturnNumber');
- let lblReturnNumber = elReturnFilterPanel.find('#lblReturnNumber');
- sldReturnNumber.slider({
- range: true,
- min: 0, max: 7, step: 1,
- values: [0, 7],
- slide: (event, ui) => {
- this.viewer.setFilterReturnNumberRange(ui.values[0], ui.values[1]);
- }
- });
- let onReturnNumberChanged = (event) => {
- let [from, to] = this.viewer.filterReturnNumberRange;
- lblReturnNumber[0].innerHTML = `${from} to ${to}`;
- sldReturnNumber.slider({values: [from, to]});
- };
- this.viewer.addEventListener('filter_return_number_range_changed', onReturnNumberChanged);
- onReturnNumberChanged();
- }
- { // NUMBER OF RETURNS
- let sldNumberOfReturns = elReturnFilterPanel.find('#sldNumberOfReturns');
- let lblNumberOfReturns = elReturnFilterPanel.find('#lblNumberOfReturns');
- sldNumberOfReturns.slider({
- range: true,
- min: 0, max: 7, step: 1,
- values: [0, 7],
- slide: (event, ui) => {
- this.viewer.setFilterNumberOfReturnsRange(ui.values[0], ui.values[1]);
- }
- });
- let onNumberOfReturnsChanged = (event) => {
- let [from, to] = this.viewer.filterNumberOfReturnsRange;
- lblNumberOfReturns[0].innerHTML = `${from} to ${to}`;
- sldNumberOfReturns.slider({values: [from, to]});
- };
- this.viewer.addEventListener('filter_number_of_returns_range_changed', onNumberOfReturnsChanged);
- onNumberOfReturnsChanged();
- }
- }
- initGPSTimeFilters(){
- let elGPSTimeFilterPanel = $('#gpstime_filter_panel');
- {
- let slider = new HierarchicalSlider({
- levels: 4,
- slide: (event) => {
- this.viewer.setFilterGPSTimeRange(...event.values);
- },
- });
- let initialized = false;
- let initialize = () => {
-
- let elRangeContainer = $("#gpstime_multilevel_range_container");
- elRangeContainer[0].prepend(slider.element);
- let extent = this.viewer.getGpsTimeExtent();
- slider.setRange(extent);
- slider.setValues(extent);
- initialized = true;
- };
- this.viewer.addEventListener("update", (e) => {
- let extent = this.viewer.getGpsTimeExtent();
- let gpsTimeAvailable = extent[0] !== Infinity;
- if(!initialized && gpsTimeAvailable){
- initialize();
- }
- slider.setRange(extent);
- });
- }
- {
-
- const txtGpsTime = elGPSTimeFilterPanel.find("#txtGpsTime");
- const btnFindGpsTime = elGPSTimeFilterPanel.find("#btnFindGpsTime");
- let targetTime = null;
- txtGpsTime.on("input", (e) => {
- const str = txtGpsTime.val();
- if(!isNaN(str)){
- const value = parseFloat(str);
- targetTime = value;
- txtGpsTime.css("background-color", "");
- }else {
- targetTime = null;
- txtGpsTime.css("background-color", "#ff9999");
- }
- });
- btnFindGpsTime.click( () => {
-
- if(targetTime !== null){
- viewer.moveToGpsTimeVicinity(targetTime);
- }
- });
- }
- }
- initPointSourceIDFilters() {
- let elPointSourceIDFilterPanel = $('#pointsourceid_filter_panel');
- {
- let slider = new HierarchicalSlider({
- levels: 4,
- range: [0, 65535],
- precision: 1,
- slide: (event) => {
- let values = event.values;
- this.viewer.setFilterPointSourceIDRange(values[0], values[1]);
- }
- });
- let initialized = false;
- let initialize = () => {
- elPointSourceIDFilterPanel[0].prepend(slider.element);
- initialized = true;
- };
- this.viewer.addEventListener("update", (e) => {
- let extent = this.viewer.filterPointSourceIDRange;
- if(!initialized){
- initialize();
- slider.setValues(extent);
- }
-
- });
- }
- // let lblPointSourceID = elPointSourceIDFilterPanel.find("#lblPointSourceID");
- // let elPointSourceID = elPointSourceIDFilterPanel.find("#spnPointSourceID");
- // let slider = new ZoomableSlider();
- // elPointSourceID[0].appendChild(slider.element);
- // slider.update();
- // slider.change( () => {
- // let range = slider.chosenRange;
- // this.viewer.setFilterPointSourceIDRange(range[0], range[1]);
- // });
- // let onPointSourceIDExtentChanged = (event) => {
- // let range = this.viewer.filterPointSourceIDExtent;
- // slider.setVisibleRange(range);
- // };
- // let onPointSourceIDChanged = (event) => {
- // let range = this.viewer.filterPointSourceIDRange;
- // let precision = 1;
- // let from = `${Utils.addCommas(range[0].toFixed(precision))}`;
- // let to = `${Utils.addCommas(range[1].toFixed(precision))}`;
- // lblPointSourceID[0].innerHTML = `${from} to ${to}`;
- // slider.setRange(range);
- // };
- // this.viewer.addEventListener('filter_point_source_id_range_changed', onPointSourceIDChanged);
- // this.viewer.addEventListener('filter_point_source_id_extent_changed', onPointSourceIDExtentChanged);
- }
- initClassificationList(){
- let elClassificationList = $('#classificationList');
- let addClassificationItem = (code, name) => {
- const classification = this.viewer.classifications[code];
- const inputID = 'chkClassification_' + code;
- const colorPickerID = 'colorPickerClassification_' + code;
- const checked = classification.visible ? "checked" : "";
- let element = $(`
- <li>
- <label style="whitespace: nowrap; display: flex">
- <input id="${inputID}" type="checkbox" ${checked}/>
- <span style="flex-grow: 1">${name}</span>
- <input id="${colorPickerID}" style="zoom: 0.5" />
- </label>
- </li>
- `);
- const elInput = element.find('input');
- const elColorPicker = element.find(`#${colorPickerID}`);
- elInput.click(event => {
- this.viewer.setClassificationVisibility(code, event.target.checked);
- });
- let defaultColor = classification.color.map(c => c * 255).join(", ");
- defaultColor = `rgb(${defaultColor})`;
- elColorPicker.spectrum({
- // flat: true,
- color: defaultColor,
- showInput: true,
- preferredFormat: 'rgb',
- cancelText: '',
- chooseText: 'Apply',
- move: color => {
- let rgb = color.toRgb();
- const c = [rgb.r / 255, rgb.g / 255, rgb.b / 255, 1];
- classification.color = c;
- },
- change: color => {
- let rgb = color.toRgb();
- const c = [rgb.r / 255, rgb.g / 255, rgb.b / 255, 1];
- classification.color = c;
- }
- });
- elClassificationList.append(element);
- };
- const addToggleAllButton = () => { // toggle all button
- const element = $(`
- <li>
- <label style="whitespace: nowrap">
- <input id="toggleClassificationFilters" type="checkbox" checked/>
- <span>show/hide all</span>
- </label>
- </li>
- `);
- let elInput = element.find('input');
- elInput.click(event => {
- this.viewer.toggleAllClassificationsVisibility();
- });
- elClassificationList.append(element);
- };
- const addInvertButton = () => {
- const element = $(`
- <li>
- <input type="button" value="invert" />
- </li>
- `);
- let elInput = element.find('input');
- elInput.click( () => {
- const classifications = this.viewer.classifications;
-
- for(let key of Object.keys(classifications)){
- let value = classifications[key];
- this.viewer.setClassificationVisibility(key, !value.visible);
- }
- });
- elClassificationList.append(element);
- };
- const populate = () => {
- addToggleAllButton();
- for (let classID in this.viewer.classifications) {
- addClassificationItem(classID, this.viewer.classifications[classID].name);
- }
- addInvertButton();
- };
- populate();
- this.viewer.addEventListener("classifications_changed", () => {
- elClassificationList.empty();
- populate();
- });
- this.viewer.addEventListener("classification_visibility_changed", () => {
- { // set checked state of classification buttons
- for(const classID of Object.keys(this.viewer.classifications)){
- const classValue = this.viewer.classifications[classID];
- let elItem = elClassificationList.find(`#chkClassification_${classID}`);
- elItem.prop("checked", classValue.visible);
- }
- }
- { // set checked state of toggle button based on state of all other buttons
- let numVisible = 0;
- let numItems = 0;
- for(const key of Object.keys(this.viewer.classifications)){
- if(this.viewer.classifications[key].visible){
- numVisible++;
- }
- numItems++;
- }
- const allVisible = numVisible === numItems;
- let elToggle = elClassificationList.find("#toggleClassificationFilters");
- elToggle.prop("checked", allVisible);
- }
- });
- }
- initAccordion(){
- $('.accordion > h3').each(function(){
- let header = $(this);
- let content = $(this).next();
- //header.addClass('accordion-header ui-widget');
- //content.addClass('accordion-content ui-widget');
- content.hide();
- header.click(() => {
- content.slideToggle();
- });
- });
- let languages = [
- ["EN", "en"],
- ["FR", "fr"],
- ["DE", "de"],
- ["JP", "jp"],
- ["ES", "es"],
- ["SE", "se"],
- ["ZH", "zh"]
- ];
- let elLanguages = $('#potree_languages');
- for(let i = 0; i < languages.length; i++){
- let [key, value] = languages[i];
- let element = $(`<a>${key}</a>`);
- element.click(() => this.viewer.setLanguage(value));
- if(i === 0){
- element.css("margin-left", "30px");
- }
-
- elLanguages.append(element);
- if(i < languages.length - 1){
- elLanguages.append($(document.createTextNode(' - ')));
- }
- }
- // to close all, call
- // $(".accordion > div").hide()
- // to open the, for example, tool menu, call:
- // $("#menu_tools").next().show()
- }
- initAppearance(){
- const sldPointBudget = this.dom.find('#sldPointBudget');
- sldPointBudget.slider({
- value: this.viewer.getPointBudget(),
- min: 100 * 1000,
- max: 10 * 1000 * 1000,
- step: 1000,
- slide: (event, ui) => { this.viewer.setPointBudget(ui.value); }
- });
- this.dom.find('#sldFOV').slider({
- value: this.viewer.getFOV(),
- min: 20,
- max: 100,
- step: 1,
- slide: (event, ui) => { this.viewer.setFOV(ui.value); }
- });
- $('#sldEDLRadius').slider({
- value: this.viewer.getEDLRadius(),
- min: 1,
- max: 4,
- step: 0.01,
- slide: (event, ui) => { this.viewer.setEDLRadius(ui.value); }
- });
- $('#sldEDLStrength').slider({
- value: this.viewer.getEDLStrength(),
- min: 0,
- max: 5,
- step: 0.01,
- slide: (event, ui) => { this.viewer.setEDLStrength(ui.value); }
- });
- $('#sldEDLOpacity').slider({
- value: this.viewer.getEDLOpacity(),
- min: 0,
- max: 1,
- step: 0.01,
- slide: (event, ui) => { this.viewer.setEDLOpacity(ui.value); }
- });
- this.viewer.addEventListener('point_budget_changed', (event) => {
- $('#lblPointBudget')[0].innerHTML = Utils.addCommas(this.viewer.getPointBudget());
- sldPointBudget.slider({value: this.viewer.getPointBudget()});
- });
- this.viewer.addEventListener('fov_changed', (event) => {
- $('#lblFOV')[0].innerHTML = parseInt(this.viewer.getFOV());
- $('#sldFOV').slider({value: this.viewer.getFOV()});
- });
- this.viewer.addEventListener('use_edl_changed', (event) => {
- $('#chkEDLEnabled')[0].checked = this.viewer.getEDLEnabled();
- });
- this.viewer.addEventListener('edl_radius_changed', (event) => {
- $('#lblEDLRadius')[0].innerHTML = this.viewer.getEDLRadius().toFixed(1);
- $('#sldEDLRadius').slider({value: this.viewer.getEDLRadius()});
- });
- this.viewer.addEventListener('edl_strength_changed', (event) => {
- $('#lblEDLStrength')[0].innerHTML = this.viewer.getEDLStrength().toFixed(1);
- $('#sldEDLStrength').slider({value: this.viewer.getEDLStrength()});
- });
- this.viewer.addEventListener('background_changed', (event) => {
- $("input[name=background][value='" + this.viewer.getBackground() + "']").prop('checked', true);
- });
- $('#lblPointBudget')[0].innerHTML = Utils.addCommas(this.viewer.getPointBudget());
- $('#lblFOV')[0].innerHTML = parseInt(this.viewer.getFOV());
- $('#lblEDLRadius')[0].innerHTML = this.viewer.getEDLRadius().toFixed(1);
- $('#lblEDLStrength')[0].innerHTML = this.viewer.getEDLStrength().toFixed(1);
- $('#chkEDLEnabled')[0].checked = this.viewer.getEDLEnabled();
-
- {
- let elBackground = $(`#background_options`);
- elBackground.selectgroup();
- elBackground.find("input").click( (e) => {
- this.viewer.setBackground(e.target.value);
- });
- let currentBackground = this.viewer.getBackground();
- try{
- $(`input[name=background_options][value=${currentBackground}]`).trigger("click");
- }catch(e){}
- }
- $('#chkEDLEnabled').click( () => {
- this.viewer.setEDLEnabled($('#chkEDLEnabled').prop("checked"));
- });
- }
- initNavigation(){
- let elNavigation = $('#navigation');
- let sldMoveSpeed = $('#sldMoveSpeed');
- let lblMoveSpeed = $('#lblMoveSpeed');
- elNavigation.append(this.createToolIcon(
- Potree.resourcePath + '/icons/earth_controls_1.png',
- '[title]tt.earth_control',
- () => { this.viewer.setControls(this.viewer.earthControls); }
- ));
- elNavigation.append(this.createToolIcon(
- Potree.resourcePath + '/icons/fps_controls.svg',
- '[title]tt.flight_control',
- () => {
- this.viewer.setControls(this.viewer.fpControls);
- this.viewer.fpControls.lockElevation = false;
- }
- ));
- elNavigation.append(this.createToolIcon(
- Potree.resourcePath + '/icons/helicopter_controls.svg',
- '[title]tt.heli_control',
- () => {
- this.viewer.setControls(this.viewer.fpControls);
- this.viewer.fpControls.lockElevation = true;
- }
- ));
- elNavigation.append(this.createToolIcon(
- Potree.resourcePath + '/icons/orbit_controls.svg',
- '[title]tt.orbit_control',
- () => { this.viewer.setControls(this.viewer.orbitControls); }
- ));
- elNavigation.append(this.createToolIcon(
- Potree.resourcePath + '/icons/focus.svg',
- '[title]tt.focus_control',
- () => { this.viewer.fitToScreen(); }
- ));
- elNavigation.append(this.createToolIcon(
- Potree.resourcePath + "/icons/navigation_cube.svg",
- "[title]tt.navigation_cube_control",
- () => {this.viewer.toggleNavigationCube();}
- ));
- elNavigation.append(this.createToolIcon(
- Potree.resourcePath + "/images/compas.svg",
- "[title]tt.compass",
- () => {
- const visible = !this.viewer.compass.isVisible();
- this.viewer.compass.setVisible(visible);
- }
- ));
- elNavigation.append(this.createToolIcon(
- Potree.resourcePath + "/icons/camera_animation.svg",
- "[title]tt.camera_animation",
- () => {
- const animation = CameraAnimation.defaultFromView(this.viewer);
- viewer.scene.addCameraAnimation(animation);
- }
- ));
- elNavigation.append("<br>");
- elNavigation.append(this.createToolIcon(
- Potree.resourcePath + "/icons/left.svg",
- "[title]tt.left_view_control",
- () => {this.viewer.setLeftView();}
- ));
- elNavigation.append(this.createToolIcon(
- Potree.resourcePath + "/icons/right.svg",
- "[title]tt.right_view_control",
- () => {this.viewer.setRightView();}
- ));
- elNavigation.append(this.createToolIcon(
- Potree.resourcePath + "/icons/front.svg",
- "[title]tt.front_view_control",
- () => {this.viewer.setFrontView();}
- ));
- elNavigation.append(this.createToolIcon(
- Potree.resourcePath + "/icons/back.svg",
- "[title]tt.back_view_control",
- () => {this.viewer.setBackView();}
- ));
- elNavigation.append(this.createToolIcon(
- Potree.resourcePath + "/icons/top.svg",
- "[title]tt.top_view_control",
- () => {this.viewer.setTopView();}
- ));
- elNavigation.append(this.createToolIcon(
- Potree.resourcePath + "/icons/bottom.svg",
- "[title]tt.bottom_view_control",
- () => {this.viewer.setBottomView();}
- ));
- let elCameraProjection = $(`
- <selectgroup id="camera_projection_options">
- <option id="camera_projection_options_perspective" value="PERSPECTIVE">Perspective</option>
- <option id="camera_projection_options_orthigraphic" value="ORTHOGRAPHIC">Orthographic</option>
- </selectgroup>
- `);
- elNavigation.append(elCameraProjection);
- elCameraProjection.selectgroup({title: "Camera Projection"});
- elCameraProjection.find("input").click( (e) => {
- this.viewer.setCameraMode(CameraMode[e.target.value]);
- });
- let cameraMode = Object.keys(CameraMode)
- .filter(key => CameraMode[key] === this.viewer.scene.cameraMode);
- elCameraProjection.find(`input[value=${cameraMode}]`).trigger("click");
- let speedRange = new Vector2$1(1, 10 * 1000);
- let toLinearSpeed = (value) => {
- return Math.pow(value, 4) * speedRange.y + speedRange.x;
- };
- let toExpSpeed = (value) => {
- return Math.pow((value - speedRange.x) / speedRange.y, 1 / 4);
- };
- sldMoveSpeed.slider({
- value: toExpSpeed(this.viewer.getMoveSpeed()),
- min: 0,
- max: 1,
- step: 0.01,
- slide: (event, ui) => { this.viewer.setMoveSpeed(toLinearSpeed(ui.value)); }
- });
- this.viewer.addEventListener('move_speed_changed', (event) => {
- lblMoveSpeed.html(this.viewer.getMoveSpeed().toFixed(1));
- sldMoveSpeed.slider({value: toExpSpeed(this.viewer.getMoveSpeed())});
- });
- lblMoveSpeed.html(this.viewer.getMoveSpeed().toFixed(1));
- }
- initSettings(){
- {
- $('#sldMinNodeSize').slider({
- value: this.viewer.getMinNodeSize(),
- min: 0,
- max: 1000,
- step: 0.01,
- slide: (event, ui) => { this.viewer.setMinNodeSize(ui.value); }
- });
- this.viewer.addEventListener('minnodesize_changed', (event) => {
- $('#lblMinNodeSize').html(parseInt(this.viewer.getMinNodeSize()));
- $('#sldMinNodeSize').slider({value: this.viewer.getMinNodeSize()});
- });
- $('#lblMinNodeSize').html(parseInt(this.viewer.getMinNodeSize()));
- }
- {
- let elSplatQuality = $("#splat_quality_options");
- elSplatQuality.selectgroup({title: "Splat Quality"});
- elSplatQuality.find("input").click( (e) => {
- if(e.target.value === "standard"){
- this.viewer.useHQ = false;
- }else if(e.target.value === "hq"){
- this.viewer.useHQ = true;
- }
- });
- let currentQuality = this.viewer.useHQ ? "hq" : "standard";
- elSplatQuality.find(`input[value=${currentQuality}]`).trigger("click");
- }
- $('#show_bounding_box').click(() => {
- this.viewer.setShowBoundingBox($('#show_bounding_box').prop("checked"));
- });
- $('#set_freeze').click(() => {
- this.viewer.setFreeze($('#set_freeze').prop("checked"));
- });
- }
- }
- class AnnotationTool extends EventDispatcher{
- constructor (viewer) {
- super();
- this.viewer = viewer;
- this.renderer = viewer.renderer;
- this.sg = new SphereGeometry(0.1);
- this.sm = new MeshNormalMaterial();
- this.s = new Mesh(this.sg, this.sm);
- }
- startInsertion (args = {}) {
- let domElement = this.viewer.renderer.domElement;
- let annotation = new Annotation({
- position: [589748.270, 231444.540, 753.675],
- title: "Annotation Title",
- description: `Annotation Description`,
- radius: 1,//add
- cameraPosition: [589748.270, 231444.540, 753.675],//add
- });
- this.dispatchEvent({type: 'start_inserting_annotation', annotation: annotation});
- const annotations = this.viewer.scene.annotations;
- annotations.add(annotation);
- let callbacks = {
- cancel: null,
- finish: null,
- };
- let insertionCallback = (e) => {
- if (e.button === MOUSE.LEFT) {
- callbacks.finish();
- } else if (e.button === MOUSE.RIGHT) {
- callbacks.cancel();
- }
- };
- callbacks.cancel = e => {
- annotations.remove(annotation);
- domElement.removeEventListener('mouseup', insertionCallback, true);
- };
- callbacks.finish = e => {
- domElement.removeEventListener('mouseup', insertionCallback, true);
- };
- domElement.addEventListener('mouseup', insertionCallback, true);
- let drag = (e) => {
- let camera = e.viewer.scene.getActiveCamera();
- /* let I = Utils.getMousePointCloudIntersection(
- e.drag.end,
- camera,
- e.viewer,
- e.viewer.scene.pointclouds,
- {pickClipped: true}); */
- let I = e.viewer.inputHandler.intersectPoint;
- if (I) {
- this.s.position.copy(I.location);
- annotation.position.copy(I.location);
-
- annotation.cameraTarget.copy(I.location);//add
- annotation.cameraPosition.copy(camera.position);//add
- }
- };
- let drop = (e) => {
- viewer.scene.scene.remove(this.s);
- this.s.removeEventListener("drag", drag);
- this.s.removeEventListener("drop", drop);
- };
- this.s.addEventListener('drag', drag);
- this.s.addEventListener('drop', drop);
- this.viewer.scene.scene.add(this.s);
- this.viewer.inputHandler.startDragging(this.s);
- return annotation;
- }
-
- update(){
- // let camera = this.viewer.scene.getActiveCamera();
- // let domElement = this.renderer.domElement;
- // let measurements = this.viewer.scene.measurements;
- // const renderAreaSize = this.renderer.getSize(new THREE.Vector2());
- // let clientWidth = renderAreaSize.width;
- // let clientHeight = renderAreaSize.height;
- }
- render(){
- //this.viewer.renderer.render(this.scene, this.viewer.scene.getActiveCamera());
- }
- };
- class NavigationCube extends Object3D {
- constructor(viewer){
- super();
- this.viewer = viewer;
- let createPlaneMaterial = (img) => {
- let material = new MeshBasicMaterial( {
- depthTest: true,
- depthWrite: true,
- side: DoubleSide
- });
- new TextureLoader().load(
- exports.resourcePath + '/textures/navigation/' + img,
- function(texture) {
- texture.anisotropy = viewer.renderer.capabilities.getMaxAnisotropy();
- material.map = texture;
- material.needsUpdate = true;
- });
- return material;
- };
- let planeGeometry = new PlaneGeometry(1, 1);
- this.front = new Mesh(planeGeometry, createPlaneMaterial('F.png'));
- this.front.position.y = -0.5;
- this.front.rotation.x = Math.PI / 2.0;
- this.front.updateMatrixWorld();
- this.front.name = "F";
- this.add(this.front);
- this.back = new Mesh(planeGeometry, createPlaneMaterial('B.png'));
- this.back.position.y = 0.5;
- this.back.rotation.x = Math.PI / 2.0;
- this.back.updateMatrixWorld();
- this.back.name = "B";
- this.add(this.back);
- this.left = new Mesh(planeGeometry, createPlaneMaterial('L.png'));
- this.left.position.x = -0.5;
- this.left.rotation.y = Math.PI / 2.0;
- this.left.updateMatrixWorld();
- this.left.name = "L";
- this.add(this.left);
- this.right = new Mesh(planeGeometry, createPlaneMaterial('R.png'));
- this.right.position.x = 0.5;
- this.right.rotation.y = Math.PI / 2.0;
- this.right.updateMatrixWorld();
- this.right.name = "R";
- this.add(this.right);
- this.bottom = new Mesh(planeGeometry, createPlaneMaterial('D.png'));
- this.bottom.position.z = -0.5;
- this.bottom.updateMatrixWorld();
- this.bottom.name = "D";
- this.add(this.bottom);
- this.top = new Mesh(planeGeometry, createPlaneMaterial('U.png'));
- this.top.position.z = 0.5;
- this.top.updateMatrixWorld();
- this.top.name = "U";
- this.add(this.top);
- this.width = 150; // in px
- this.camera = new OrthographicCamera(-1, 1, 1, -1, -1, 1);
- this.camera.position.copy(new Vector3(0, 0, 0));
- this.camera.lookAt(new Vector3(0, 1, 0));
- this.camera.updateMatrixWorld();
- this.camera.rotation.order = "ZXY";
- let onMouseDown = (event) => {
- if (!this.visible) {
- return;
- }
-
- this.pickedFace = null;
- let mouse = new Vector2$1();
- mouse.x = event.clientX - (window.innerWidth - this.width);
- mouse.y = event.clientY;
- if(mouse.x < 0 || mouse.y > this.width) return;
- mouse.x = (mouse.x / this.width) * 2 - 1;
- mouse.y = -(mouse.y / this.width) * 2 + 1;
- let raycaster = new Raycaster();
- raycaster.setFromCamera(mouse, this.camera);
- raycaster.ray.origin.sub(this.camera.getWorldDirection(new Vector3()));
- let intersects = raycaster.intersectObjects(this.children);
- let minDistance = 1000;
- for (let i = 0; i < intersects.length; i++) {
- if(intersects[i].distance < minDistance) {
- this.pickedFace = intersects[i].object.name;
- minDistance = intersects[i].distance;
- }
- }
-
- if(this.pickedFace) {
- this.viewer.setView(this.pickedFace);
- }
- };
- this.viewer.renderer.domElement.addEventListener('mousedown', onMouseDown, false);
- }
- update(rotation) {
- this.camera.rotation.copy(rotation);
- this.camera.updateMatrixWorld();
- }
- }
- /**
- * @author mschuetz / http://mschuetz.at
- *
- * adapted from THREE.OrbitControls by
- *
- * @author qiao / https://github.com/qiao
- * @author mrdoob / http://mrdoob.com
- * @author alteredq / http://alteredqualia.com/
- * @author WestLangley / http://github.com/WestLangley
- * @author erich666 / http://erichaines.com
- *
- *
- *
- */
-
- class OrbitControls extends EventDispatcher{
-
- constructor(viewer){
- super();
-
- this.viewer = viewer;
- this.renderer = viewer.renderer;
- this.scene = null;
- this.sceneControls = new Scene();
- this.rotationSpeed = 3; //旋转速度
-
-
- this.fadeFactor = 100;
- this.yawDelta = 0;
- this.pitchDelta = 0;
- this.panDelta = new Vector2$1(0, 0);
- this.radiusDelta = 0;
- this.doubleClockZoomEnabled = true;
- this.tweens = [];
- this.dollyStart = new Vector2$1;
- this.dollyEnd = new Vector2$1;
-
- let drag = (e) => {
- if(!this.enabled)return
-
- let viewport = e.dragViewport;
- if(!viewport || viewport.camera.type == "OrthographicCamera" )return
- //let camera = viewport.camera
-
- if (e.drag.object !== null) {
- return;
- }
- let mode;
-
- if(e.isTouch){
-
- if(e.touches.length == 1){
- mode = 'rotate';
- }else {
- mode = 'scale-pan';
- }
- }else {
- mode = e.buttons === Buttons.LEFT ? 'rotate' : 'pan';
- }
-
- if (e.drag.startHandled === undefined) {
- e.drag.startHandled = true;
- this.dispatchEvent({type: 'start'});
- }
-
- let ndrag = e.drag.pointerDelta.clone();//.add(new THREE.Vector2(1,1)).multiplyScalar(0.5)
- ndrag.y *= -1;
- if (mode == 'rotate') {
-
- this.yawDelta += ndrag.x * this.rotationSpeed;
- this.pitchDelta += ndrag.y * this.rotationSpeed;
-
- } else if(mode == 'pan'){
-
- this.panDelta.x += ndrag.x;
- this.panDelta.y += ndrag.y;
-
- }else if(mode == 'scale-pan'){ //add
- this.dollyEnd.subVectors(e.touches[0].pointer, e.touches[1].pointer);
- var scale = this.dollyEnd.length() / this.dollyStart.length();
-
- this.dollyStart.copy(this.dollyEnd);
- this.radiusDelta = (1-scale) * this.scene.view.radius;
-
- //------------------------
- //平移
- let pointer = new Vector2$1().addVectors(e.touches[0].pointer, e.touches[1].pointer).multiplyScalar(0.5);//两个指头的中心点
-
- let delta = new Vector2$1().subVectors(pointer, this.lastScalePointer);
- delta.y *= -1;
- this.panDelta.add(delta);
-
- this.lastScalePointer = pointer.clone();
-
-
-
- //console.log('scale ',scale, this.radiusDelta)
-
- }
-
- this.stopTweens();
-
-
- };
-
-
-
-
-
-
- let drop = e => {
- if(!this.enabled)return
- this.dispatchEvent({type: 'end'});
- };
- let scroll = (e) => {
- if(!this.enabled)return
- let resolvedRadius = this.scene.view.radius + this.radiusDelta;
- this.radiusDelta += -e.delta * resolvedRadius * 0.1;
- this.stopTweens();
- };
- let dblclick = (e) => {
- if(!this.enabled)return
- if(this.doubleClockZoomEnabled){
- this.zoomToLocation(e.mouse);
- }
- };
- let previousTouch = null;
- let touchStart = e => {
- previousTouch = e;
- };
- let touchEnd = e => {
- previousTouch = e;
- };
- let touchMove = e => {
- if(!this.enabled)return
- if (e.touches.length === 2 && previousTouch.touches.length === 2){
- let prev = previousTouch;
- let curr = e;
- let prevDX = prev.touches[0].pageX - prev.touches[1].pageX;
- let prevDY = prev.touches[0].pageY - prev.touches[1].pageY;
- let prevDist = Math.sqrt(prevDX * prevDX + prevDY * prevDY);
- let currDX = curr.touches[0].pageX - curr.touches[1].pageX;
- let currDY = curr.touches[0].pageY - curr.touches[1].pageY;
- let currDist = Math.sqrt(currDX * currDX + currDY * currDY);
- let delta = currDist / prevDist;
- let resolvedRadius = this.scene.view.radius + this.radiusDelta;
- let newRadius = resolvedRadius / delta;
- this.radiusDelta = newRadius - resolvedRadius;
- this.stopTweens();
- }else if(e.touches.length === 3 && previousTouch.touches.length === 3){
- let prev = previousTouch;
- let curr = e;
- let prevMeanX = (prev.touches[0].pageX + prev.touches[1].pageX + prev.touches[2].pageX) / 3;
- let prevMeanY = (prev.touches[0].pageY + prev.touches[1].pageY + prev.touches[2].pageY) / 3;
- let currMeanX = (curr.touches[0].pageX + curr.touches[1].pageX + curr.touches[2].pageX) / 3;
- let currMeanY = (curr.touches[0].pageY + curr.touches[1].pageY + curr.touches[2].pageY) / 3;
- let delta = {
- x: (currMeanX - prevMeanX) / this.renderer.domElement.clientWidth,
- y: (currMeanY - prevMeanY) / this.renderer.domElement.clientHeight
- };
- this.panDelta.x += delta.x;
- this.panDelta.y += delta.y;
- this.stopTweens();
- }
- previousTouch = e;
- };
- this.addEventListener('touchstart', touchStart);
- this.addEventListener('touchend', touchEnd);
- this.addEventListener('touchmove', touchMove);
- this.viewer.addEventListener('global_drag', drag);
- this.viewer.addEventListener('global_drop', drop);
- this.viewer.addEventListener('global_mousewheel', scroll);
- this.viewer.addEventListener('global_dblclick', dblclick);
- this.viewer.addEventListener('global_touchmove', (e)=>{
- if(e.touches.length>1){//单指的就触发上一句
- //console.log('global_touchmove' )
- drag(e);
- }
- });
- let prepareScale = (e)=>{//触屏的scale
- this.dollyStart.subVectors(e.touches[0].pointer, e.touches[1].pointer);
- this.lastScalePointer = new Vector2$1().addVectors(e.touches[0].pointer, e.touches[1].pointer).multiplyScalar(0.5);//两个指头的中心点
-
- };
-
- this.viewer.addEventListener('global_touchstart', (e)=>{
- if(this.enabled && e.touches.length==2){//只监听开头两个指头
- prepareScale(e);
- }
- });
- /* this.viewer.addEventListener('global_touchend', (e)=>{
- if(!this.enabled)return
- if(e.touches.length==1){//停止scale,开始rotate
- prepareRotate(e)
- //this.pointerDragStart = null
- //console.log('只剩一个', e.pointer.toArray())
- }
- }) */
-
-
-
-
- }
- setScene (scene) {
- this.scene = scene;
- }
- setEnable(enabled){
- this.enabled = enabled;
- }
- stop(){
- this.yawDelta = 0;
- this.pitchDelta = 0;
- this.radiusDelta = 0;
- this.panDelta.set(0, 0);
- }
-
- zoomToLocation(mouse){
- if(!this.enabled)return
- let camera = this.scene.getActiveCamera();
-
- let I = Utils.getMousePointCloudIntersection(
- null, mouse, this.viewer.inputHandler.pointer,
- camera,
- this.viewer,
- this.scene.pointclouds,
- {pickClipped: true});
- if (I === null) {
- return;
- }
- let targetRadius = 0;
- {
- let minimumJumpDistance = 0.2;
- let domElement = this.renderer.domElement;
- let ray = Utils.mouseToRay(this.viewer.inputHandler.pointer/* mouse */, camera, domElement.clientWidth, domElement.clientHeight);
- let nodes = I.pointcloud.nodesOnRay(I.pointcloud.visibleNodes, ray);
- let lastNode = nodes[nodes.length - 1];
- let radius = lastNode.getBoundingSphere(new Sphere()).radius;
- targetRadius = Math.min(this.scene.view.radius, radius);
- targetRadius = Math.max(minimumJumpDistance, targetRadius);
- }
- let d = this.scene.view.direction.multiplyScalar(-1);
- let cameraTargetPosition = new Vector3().addVectors(I.location, d.multiplyScalar(targetRadius));
- // TODO Unused: let controlsTargetPosition = I.location;
- let animationDuration = 600;
- let easing = TWEEN.Easing.Quartic.Out;
- { // animate
- let value = {x: 0};
- let tween = new TWEEN.Tween(value).to({x: 1}, animationDuration);
- tween.easing(easing);
- this.tweens.push(tween);
- let startPos = this.scene.view.position.clone();
- let targetPos = cameraTargetPosition.clone();
- let startRadius = this.scene.view.radius;
- let targetRadius = cameraTargetPosition.distanceTo(I.location);
- tween.onUpdate(() => {
- let t = value.x;
- this.scene.view.position.x = (1 - t) * startPos.x + t * targetPos.x;
- this.scene.view.position.y = (1 - t) * startPos.y + t * targetPos.y;
- this.scene.view.position.z = (1 - t) * startPos.z + t * targetPos.z;
- this.scene.view.radius = (1 - t) * startRadius + t * targetRadius;
- this.viewer.setMoveSpeed(this.scene.view.radius);
- });
- tween.onComplete(() => {
- this.tweens = this.tweens.filter(e => e !== tween);
- });
- tween.start();
- }
- }
- stopTweens () {
- this.tweens.forEach(e => e.stop());
- this.tweens = [];
- }
- update (delta) {
- if(!this.enabled)return
- let view = this.scene.view;
- { // apply rotation
- let progression = Math.min(1, this.fadeFactor * delta);
- let yaw = view.yaw;
- let pitch = view.pitch;
- let pivot = view.getPivot();
- yaw -= progression * this.yawDelta;
- pitch -= progression * this.pitchDelta;
- view.yaw = yaw;
- view.pitch = pitch;
- let V = this.scene.view.direction.multiplyScalar(-view.radius);
- let position = new Vector3().addVectors(pivot, V);
- view.position.copy(position);
- }
- { // apply pan
- /* let progression = Math.min(1, this.fadeFactor * delta);
- let panDistance = progression * view.radius * 3; */
- let camera = this.scene.getActiveCamera();
- let panDistance = 2 * view.radius * Math.tan(MathUtils.degToRad(camera.fov / 2));//参照4dkk。 平移target(也就是平移镜头位置),但还是难以保证跟手(navvis也不一定跟手,但是很奇怪在居中时中心点居然是跟手的,可能计算方式不同)
-
- let px = -this.panDelta.x * panDistance;
- let py = this.panDelta.y * panDistance;
- view.pan(px, py);
- }
- { // apply zoom
- let progression = 1;//Math.min(1, this.fadeFactor * delta);
- // let radius = view.radius + progression * this.radiusDelta * view.radius * 0.1;
- let radius = view.radius + progression * this.radiusDelta;
- let V = view.direction.multiplyScalar(-radius);
- let position = new Vector3().addVectors(view.getPivot(), V);
- view.radius = radius;
- view.position.copy(position);
- }
- {
- let speed = view.radius;
- this.viewer.setMoveSpeed(speed);
- }
- { // decelerate over time
- /* let progression = Math.min(1, this.fadeFactor * delta);
- let attenuation = Math.max(0, 1 - this.fadeFactor * delta);
- this.yawDelta *= attenuation;
- this.pitchDelta *= attenuation;
- this.panDelta.multiplyScalar(attenuation);
- // this.radiusDelta *= attenuation;
- this.radiusDelta -= progression * this.radiusDelta; */
-
- //取消衰减,直接stop
- this.stop();
- }
- }
- };
- class EarthControls extends EventDispatcher {
- constructor (viewer) {
- super(viewer);
- this.viewer = viewer;
- this.renderer = viewer.renderer;
- this.scene = null;
- this.sceneControls = new Scene();
- this.rotationSpeed = 10;
- this.fadeFactor = 20;
- this.wheelDelta = 0;
- this.zoomDelta = new Vector3();
- this.camStart = null;
- this.tweens = [];
- {
- let sg = new SphereGeometry(1, 16, 16);
- let sm = new MeshNormalMaterial();
- this.pivotIndicator = new Mesh(sg, sm);
- this.pivotIndicator.visible = false;
- this.sceneControls.add(this.pivotIndicator);
- }
- let drag = (e) => {
- if (e.drag.object !== null) {
- return;
- }
- if (!this.pivot) {
- return;
- }
- if (e.drag.startHandled === undefined) {
- e.drag.startHandled = true;
- this.dispatchEvent({type: 'start'});
- }
- let camStart = this.camStart;
- let camera = this.scene.getActiveCamera();
- let view = this.viewer.scene.view;
- // let camera = this.viewer.scene.camera;
- let Buttons = e.drag.end;
- let domElement = this.viewer.renderer.domElement;
- if (e.drag.Buttons === Buttons.LEFT) {
- let ray = Utils.ButtonsToRay(this.viewer.inputHandler.pointer/* Buttons */, camera, domElement.clientWidth, domElement.clientHeight);
- let plane = new Plane().setFromNormalAndCoplanarPoint(
- new Vector3(0, 0, 1),
- this.pivot);
- let distanceToPlane = ray.distanceToPlane(plane);
- if (distanceToPlane > 0) {
- let I = new Vector3().addVectors(
- camStart.position,
- ray.direction.clone().multiplyScalar(distanceToPlane));
- let movedBy = new Vector3().subVectors(
- I, this.pivot);
- let newCamPos = camStart.position.clone().sub(movedBy);
- view.position.copy(newCamPos);
- {
- let distance = newCamPos.distanceTo(this.pivot);
- view.radius = distance;
- let speed = view.radius / 2.5;
- this.viewer.setMoveSpeed(speed);
- }
- }
- } else if (e.drag.Buttons === Buttons.RIGHT) {
- let ndrag = {
- x: e.drag.ButtonsDelta.x / this.renderer.domElement.clientWidth,
- y: e.drag.ButtonsDelta.y / this.renderer.domElement.clientHeight
- };
- let yawDelta = -ndrag.x * this.rotationSpeed * 0.5;
- let pitchDelta = -ndrag.y * this.rotationSpeed * 0.2;
- let originalPitch = view.pitch;
- let tmpView = view.clone();
- tmpView.pitch = tmpView.pitch + pitchDelta;
- pitchDelta = tmpView.pitch - originalPitch;
- let pivotToCam = new Vector3().subVectors(view.position, this.pivot);
- let pivotToCamTarget = new Vector3().subVectors(view.getPivot(), this.pivot);
- let side = view.getSide();
- pivotToCam.applyAxisAngle(side, pitchDelta);
- pivotToCamTarget.applyAxisAngle(side, pitchDelta);
- pivotToCam.applyAxisAngle(new Vector3(0, 0, 1), yawDelta);
- pivotToCamTarget.applyAxisAngle(new Vector3(0, 0, 1), yawDelta);
- let newCam = new Vector3().addVectors(this.pivot, pivotToCam);
- // TODO: Unused: let newCamTarget = new THREE.Vector3().addVectors(this.pivot, pivotToCamTarget);
- view.position.copy(newCam);
- view.yaw += yawDelta;
- view.pitch += pitchDelta;
- }
- };
- let onButtonsDown = e => {
- let I = Utils.getButtonsPointCloudIntersection(
- e.Buttons,
- this.scene.getActiveCamera(),
- this.viewer,
- this.scene.pointclouds,
- {pickClipped: false});
- if (I) {
- this.pivot = I.location;
- this.camStart = this.scene.getActiveCamera().clone();
- this.pivotIndicator.visible = true;
- this.pivotIndicator.position.copy(I.location);
- }
- };
- let drop = e => {
- this.dispatchEvent({type: 'end'});
- };
- let onButtonsUp = e => {
- this.camStart = null;
- this.pivot = null;
- this.pivotIndicator.visible = false;
- };
- let scroll = (e) => {
- this.wheelDelta += e.delta;
- };
- let dblclick = (e) => {
- this.zoomToLocation(e.Buttons);
- };
- this.addEventListener('drag', drag);
- this.addEventListener('drop', drop);
- this.addEventListener('Buttonswheel', scroll);
- this.addEventListener('Buttonsdown', onButtonsDown);
- this.addEventListener('Buttonsup', onButtonsUp);
- this.addEventListener('dblclick', dblclick);
- }
- setScene (scene) {
- this.scene = scene;
- }
- stop(){
- this.wheelDelta = 0;
- this.zoomDelta.set(0, 0, 0);
- }
-
- zoomToLocation(Buttons){
- let camera = this.scene.getActiveCamera();
-
- let I = Utils.getButtonsPointCloudIntersection(
- Buttons,
- camera,
- this.viewer,
- this.scene.pointclouds);
- if (I === null) {
- return;
- }
- let targetRadius = 0;
- {
- let minimumJumpDistance = 0.2;
- let domElement = this.renderer.domElement;
- let ray = Utils.ButtonsToRay(this.viewer.inputHandler.pointer, camera, domElement.clientWidth, domElement.clientHeight);
- let nodes = I.pointcloud.nodesOnRay(I.pointcloud.visibleNodes, ray);
- let lastNode = nodes[nodes.length - 1];
- let radius = lastNode.getBoundingSphere(new Sphere()).radius;
- targetRadius = Math.min(this.scene.view.radius, radius);
- targetRadius = Math.max(minimumJumpDistance, targetRadius);
- }
- let d = this.scene.view.direction.multiplyScalar(-1);
- let cameraTargetPosition = new Vector3().addVectors(I.location, d.multiplyScalar(targetRadius));
- // TODO Unused: let controlsTargetPosition = I.location;
- let animationDuration = 600;
- let easing = TWEEN.Easing.Quartic.Out;
- { // animate
- let value = {x: 0};
- let tween = new TWEEN.Tween(value).to({x: 1}, animationDuration);
- tween.easing(easing);
- this.tweens.push(tween);
- let startPos = this.scene.view.position.clone();
- let targetPos = cameraTargetPosition.clone();
- let startRadius = this.scene.view.radius;
- let targetRadius = cameraTargetPosition.distanceTo(I.location);
- tween.onUpdate(() => {
- let t = value.x;
- this.scene.view.position.x = (1 - t) * startPos.x + t * targetPos.x;
- this.scene.view.position.y = (1 - t) * startPos.y + t * targetPos.y;
- this.scene.view.position.z = (1 - t) * startPos.z + t * targetPos.z;
- this.scene.view.radius = (1 - t) * startRadius + t * targetRadius;
- this.viewer.setMoveSpeed(this.scene.view.radius / 2.5);
- });
- tween.onComplete(() => {
- this.tweens = this.tweens.filter(e => e !== tween);
- });
- tween.start();
- }
- }
- update (delta) {
- let view = this.scene.view;
- let fade = Math.pow(0.5, this.fadeFactor * delta);
- let progression = 1 - fade;
- let camera = this.scene.getActiveCamera();
-
- // compute zoom
- if (this.wheelDelta !== 0) {
- let I = Utils.getButtonsPointCloudIntersection(
- this.viewer.inputHandler.Buttons,
- this.scene.getActiveCamera(),
- this.viewer,
- this.scene.pointclouds);
- if (I) {
- let resolvedPos = new Vector3().addVectors(view.position, this.zoomDelta);
- let distance = I.location.distanceTo(resolvedPos);
- let jumpDistance = distance * 0.2 * this.wheelDelta;
- let targetDir = new Vector3().subVectors(I.location, view.position);
- targetDir.normalize();
- resolvedPos.add(targetDir.multiplyScalar(jumpDistance));
- this.zoomDelta.subVectors(resolvedPos, view.position);
- {
- let distance = resolvedPos.distanceTo(I.location);
- view.radius = distance;
- let speed = view.radius / 2.5;
- this.viewer.setMoveSpeed(speed);
- }
- }
- }
- // apply zoom
- if (this.zoomDelta.length() !== 0) {
- let p = this.zoomDelta.clone().multiplyScalar(progression);
- let newPos = new Vector3().addVectors(view.position, p);
- view.position.copy(newPos);
- }
- if (this.pivotIndicator.visible) {
- let distance = this.pivotIndicator.position.distanceTo(view.position);
- let pixelwidth = this.renderer.domElement.clientwidth;
- let pixelHeight = this.renderer.domElement.clientHeight;
- let pr = Utils.projectedRadius(1, camera, distance, pixelwidth, pixelHeight);
- let scale = (10 / pr);
- this.pivotIndicator.scale.set(scale, scale, scale);
- }
- // decelerate over time
- {
- this.zoomDelta.multiplyScalar(fade);
- this.wheelDelta = 0;
- }
- }
- };
- /**
- * @author chrisl / Geodan
- *
- * adapted from Potree.FirstPersonControls by
- *
- * @author mschuetz / http://mschuetz.at
- *
- * and THREE.DeviceOrientationControls by
- *
- * @author richt / http://richt.me
- * @author WestLangley / http://github.com/WestLangley
- *
- *
- *
- */
- class DeviceOrientationControls extends EventDispatcher{
- constructor(viewer){
- super();
- this.viewer = viewer;
- this.renderer = viewer.renderer;
- this.scene = null;
- this.sceneControls = new Scene();
- this.screenOrientation = window.orientation || 0;
- let deviceOrientationChange = e => {
- this.deviceOrientation = e;
- };
- let screenOrientationChange = e => {
- this.screenOrientation = window.orientation || 0;
- };
- if ('ondeviceorientationabsolute' in window) {
- window.addEventListener('deviceorientationabsolute', deviceOrientationChange);
- } else if ('ondeviceorientation' in window) {
- window.addEventListener('deviceorientation', deviceOrientationChange);
- } else {
- console.warn("No device orientation found.");
- }
- // window.addEventListener('deviceorientation', deviceOrientationChange);
- window.addEventListener('orientationchange', screenOrientationChange);
- }
- setScene (scene) {
- this.scene = scene;
- }
- update (delta) {
- let computeQuaternion = function (alpha, beta, gamma, orient) {
- let quaternion = new Quaternion();
- let zee = new Vector3(0, 0, 1);
- let euler = new Euler();
- let q0 = new Quaternion();
- euler.set(beta, gamma, alpha, 'ZXY');
- quaternion.setFromEuler(euler);
- quaternion.multiply(q0.setFromAxisAngle(zee, -orient));
- return quaternion;
- };
- if (typeof this.deviceOrientation !== 'undefined') {
- let alpha = this.deviceOrientation.alpha ? MathUtils.degToRad(this.deviceOrientation.alpha) : 0;
- let beta = this.deviceOrientation.beta ? MathUtils.degToRad(this.deviceOrientation.beta) : 0;
- let gamma = this.deviceOrientation.gamma ? MathUtils.degToRad(this.deviceOrientation.gamma) : 0;
- let orient = this.screenOrientation ? MathUtils.degToRad(this.screenOrientation) : 0;
- let quaternion = computeQuaternion(alpha, beta, gamma, orient);
- viewer.scene.cameraP.quaternion.set(quaternion.x, quaternion.y, quaternion.z, quaternion.w);
- }
- }
- };
- const _taskCache = new WeakMap();
- class DRACOLoader extends Loader {
- constructor( manager ) {
- super( manager );
- this.decoderPath = '';
- this.decoderConfig = {};
- this.decoderBinary = null;
- this.decoderPending = null;
- this.workerLimit = 4;
- this.workerPool = [];
- this.workerNextTaskID = 1;
- this.workerSourceURL = '';
- this.defaultAttributeIDs = {
- position: 'POSITION',
- normal: 'NORMAL',
- color: 'COLOR',
- uv: 'TEX_COORD'
- };
- this.defaultAttributeTypes = {
- position: 'Float32Array',
- normal: 'Float32Array',
- color: 'Float32Array',
- uv: 'Float32Array'
- };
- }
- setDecoderPath( path ) {
- this.decoderPath = path;
- return this;
- }
- setDecoderConfig( config ) {
- this.decoderConfig = config;
- return this;
- }
- setWorkerLimit( workerLimit ) {
- this.workerLimit = workerLimit;
- return this;
- }
- load( url, onLoad, onProgress, onError ) {
- const loader = new FileLoader( this.manager );
- loader.setPath( this.path );
- loader.setResponseType( 'arraybuffer' );
- loader.setRequestHeader( this.requestHeader );
- loader.setWithCredentials( this.withCredentials );
- loader.load( url, ( buffer ) => {
- const taskConfig = {
- attributeIDs: this.defaultAttributeIDs,
- attributeTypes: this.defaultAttributeTypes,
- useUniqueIDs: false
- };
- this.decodeGeometry( buffer, taskConfig )
- .then( onLoad )
- .catch( onError );
- }, onProgress, onError );
- }
- /** @deprecated Kept for backward-compatibility with previous DRACOLoader versions. */
- decodeDracoFile( buffer, callback, attributeIDs, attributeTypes ) {
- const taskConfig = {
- attributeIDs: attributeIDs || this.defaultAttributeIDs,
- attributeTypes: attributeTypes || this.defaultAttributeTypes,
- useUniqueIDs: !! attributeIDs
- };
- this.decodeGeometry( buffer, taskConfig ).then( callback );
- }
- decodeGeometry( buffer, taskConfig ) {
- // TODO: For backward-compatibility, support 'attributeTypes' objects containing
- // references (rather than names) to typed array constructors. These must be
- // serialized before sending them to the worker.
- for ( const attribute in taskConfig.attributeTypes ) {
- const type = taskConfig.attributeTypes[ attribute ];
- if ( type.BYTES_PER_ELEMENT !== undefined ) {
- taskConfig.attributeTypes[ attribute ] = type.name;
- }
- }
- //
- const taskKey = JSON.stringify( taskConfig );
- // Check for an existing task using this buffer. A transferred buffer cannot be transferred
- // again from this thread.
- if ( _taskCache.has( buffer ) ) {
- const cachedTask = _taskCache.get( buffer );
- if ( cachedTask.key === taskKey ) {
- return cachedTask.promise;
- } else if ( buffer.byteLength === 0 ) {
- // Technically, it would be possible to wait for the previous task to complete,
- // transfer the buffer back, and decode again with the second configuration. That
- // is complex, and I don't know of any reason to decode a Draco buffer twice in
- // different ways, so this is left unimplemented.
- throw new Error(
- 'THREE.DRACOLoader: Unable to re-decode a buffer with different ' +
- 'settings. Buffer has already been transferred.'
- );
- }
- }
- //
- let worker;
- const taskID = this.workerNextTaskID ++;
- const taskCost = buffer.byteLength;
- // Obtain a worker and assign a task, and construct a geometry instance
- // when the task completes.
- const geometryPending = this._getWorker( taskID, taskCost )
- .then( ( _worker ) => {
- worker = _worker;
- return new Promise( ( resolve, reject ) => {
- worker._callbacks[ taskID ] = { resolve, reject };
- worker.postMessage( { type: 'decode', id: taskID, taskConfig, buffer }, [ buffer ] );
- // this.debug();
- } );
- } )
- .then( ( message ) => this._createGeometry( message.geometry ) );
- // Remove task from the task list.
- // Note: replaced '.finally()' with '.catch().then()' block - iOS 11 support (#19416)
- geometryPending
- .catch( () => true )
- .then( () => {
- if ( worker && taskID ) {
- this._releaseTask( worker, taskID );
- // this.debug();
- }
- } );
- // Cache the task result.
- _taskCache.set( buffer, {
- key: taskKey,
- promise: geometryPending
- } );
- return geometryPending;
- }
- _createGeometry( geometryData ) {
- const geometry = new BufferGeometry();
- if ( geometryData.index ) {
- geometry.setIndex( new BufferAttribute( geometryData.index.array, 1 ) );
- }
- for ( let i = 0; i < geometryData.attributes.length; i ++ ) {
- const attribute = geometryData.attributes[ i ];
- const name = attribute.name;
- const array = attribute.array;
- const itemSize = attribute.itemSize;
- geometry.setAttribute( name, new BufferAttribute( array, itemSize ) );
- }
- return geometry;
- }
- _loadLibrary( url, responseType ) {
- const loader = new FileLoader( this.manager );
- loader.setPath( this.decoderPath );
- loader.setResponseType( responseType );
- loader.setWithCredentials( this.withCredentials );
- return new Promise( ( resolve, reject ) => {
- loader.load( url, resolve, undefined, reject );
- } );
- }
- preload() {
- this._initDecoder();
- return this;
- }
- _initDecoder() {
- if ( this.decoderPending ) return this.decoderPending;
- const useJS = typeof WebAssembly !== 'object' || this.decoderConfig.type === 'js';
- const librariesPending = [];
- if ( useJS ) {
- librariesPending.push( this._loadLibrary( 'draco_decoder.js', 'text' ) );
- } else {
- librariesPending.push( this._loadLibrary( 'draco_wasm_wrapper.js', 'text' ) );
- librariesPending.push( this._loadLibrary( 'draco_decoder.wasm', 'arraybuffer' ) );
- }
- this.decoderPending = Promise.all( librariesPending )
- .then( ( libraries ) => {
- const jsContent = libraries[ 0 ];
- if ( ! useJS ) {
- this.decoderConfig.wasmBinary = libraries[ 1 ];
- }
- const fn = DRACOWorker.toString();
- const body = [
- '/* draco decoder */',
- jsContent,
- '',
- '/* worker */',
- fn.substring( fn.indexOf( '{' ) + 1, fn.lastIndexOf( '}' ) )
- ].join( '\n' );
- this.workerSourceURL = URL.createObjectURL( new Blob( [ body ] ) );
- } );
- return this.decoderPending;
- }
- _getWorker( taskID, taskCost ) {
- return this._initDecoder().then( () => {
- if ( this.workerPool.length < this.workerLimit ) {
- const worker = new Worker( this.workerSourceURL );
- worker._callbacks = {};
- worker._taskCosts = {};
- worker._taskLoad = 0;
- worker.postMessage( { type: 'init', decoderConfig: this.decoderConfig } );
- worker.onmessage = function ( e ) {
- const message = e.data;
- switch ( message.type ) {
- case 'decode':
- worker._callbacks[ message.id ].resolve( message );
- break;
- case 'error':
- worker._callbacks[ message.id ].reject( message );
- break;
- default:
- console.error( 'THREE.DRACOLoader: Unexpected message, "' + message.type + '"' );
- }
- };
- this.workerPool.push( worker );
- } else {
- this.workerPool.sort( function ( a, b ) {
- return a._taskLoad > b._taskLoad ? - 1 : 1;
- } );
- }
- const worker = this.workerPool[ this.workerPool.length - 1 ];
- worker._taskCosts[ taskID ] = taskCost;
- worker._taskLoad += taskCost;
- return worker;
- } );
- }
- _releaseTask( worker, taskID ) {
- worker._taskLoad -= worker._taskCosts[ taskID ];
- delete worker._callbacks[ taskID ];
- delete worker._taskCosts[ taskID ];
- }
- debug() {
- console.log( 'Task load: ', this.workerPool.map( ( worker ) => worker._taskLoad ) );
- }
- dispose() {
- for ( let i = 0; i < this.workerPool.length; ++ i ) {
- this.workerPool[ i ].terminate();
- }
- this.workerPool.length = 0;
- return this;
- }
- }
- /* WEB WORKER */
- function DRACOWorker() {
- let decoderConfig;
- let decoderPending;
- onmessage = function ( e ) {
- const message = e.data;
- switch ( message.type ) {
- case 'init':
- decoderConfig = message.decoderConfig;
- decoderPending = new Promise( function ( resolve/*, reject*/ ) {
- decoderConfig.onModuleLoaded = function ( draco ) {
- // Module is Promise-like. Wrap before resolving to avoid loop.
- resolve( { draco: draco } );
- };
- DracoDecoderModule( decoderConfig ); // eslint-disable-line no-undef
- } );
- break;
- case 'decode':
- const buffer = message.buffer;
- const taskConfig = message.taskConfig;
- decoderPending.then( ( module ) => {
- const draco = module.draco;
- const decoder = new draco.Decoder();
- const decoderBuffer = new draco.DecoderBuffer();
- decoderBuffer.Init( new Int8Array( buffer ), buffer.byteLength );
- try {
- const geometry = decodeGeometry( draco, decoder, decoderBuffer, taskConfig );
- const buffers = geometry.attributes.map( ( attr ) => attr.array.buffer );
- if ( geometry.index ) buffers.push( geometry.index.array.buffer );
- self.postMessage( { type: 'decode', id: message.id, geometry }, buffers );
- } catch ( error ) {
- console.error( error );
- self.postMessage( { type: 'error', id: message.id, error: error.message } );
- } finally {
- draco.destroy( decoderBuffer );
- draco.destroy( decoder );
- }
- } );
- break;
- }
- };
- function decodeGeometry( draco, decoder, decoderBuffer, taskConfig ) {
- const attributeIDs = taskConfig.attributeIDs;
- const attributeTypes = taskConfig.attributeTypes;
- let dracoGeometry;
- let decodingStatus;
- const geometryType = decoder.GetEncodedGeometryType( decoderBuffer );
- if ( geometryType === draco.TRIANGULAR_MESH ) {
- dracoGeometry = new draco.Mesh();
- decodingStatus = decoder.DecodeBufferToMesh( decoderBuffer, dracoGeometry );
- } else if ( geometryType === draco.POINT_CLOUD ) {
- dracoGeometry = new draco.PointCloud();
- decodingStatus = decoder.DecodeBufferToPointCloud( decoderBuffer, dracoGeometry );
- } else {
- throw new Error( 'THREE.DRACOLoader: Unexpected geometry type.' );
- }
- if ( ! decodingStatus.ok() || dracoGeometry.ptr === 0 ) {
- throw new Error( 'THREE.DRACOLoader: Decoding failed: ' + decodingStatus.error_msg() );
- }
- const geometry = { index: null, attributes: [] };
- // Gather all vertex attributes.
- for ( const attributeName in attributeIDs ) {
- const attributeType = self[ attributeTypes[ attributeName ] ];
- let attribute;
- let attributeID;
- // A Draco file may be created with default vertex attributes, whose attribute IDs
- // are mapped 1:1 from their semantic name (POSITION, NORMAL, ...). Alternatively,
- // a Draco file may contain a custom set of attributes, identified by known unique
- // IDs. glTF files always do the latter, and `.drc` files typically do the former.
- if ( taskConfig.useUniqueIDs ) {
- attributeID = attributeIDs[ attributeName ];
- attribute = decoder.GetAttributeByUniqueId( dracoGeometry, attributeID );
- } else {
- attributeID = decoder.GetAttributeId( dracoGeometry, draco[ attributeIDs[ attributeName ] ] );
- if ( attributeID === - 1 ) continue;
- attribute = decoder.GetAttribute( dracoGeometry, attributeID );
- }
- geometry.attributes.push( decodeAttribute( draco, decoder, dracoGeometry, attributeName, attributeType, attribute ) );
- }
- // Add index.
- if ( geometryType === draco.TRIANGULAR_MESH ) {
- geometry.index = decodeIndex( draco, decoder, dracoGeometry );
- }
- draco.destroy( dracoGeometry );
- return geometry;
- }
- function decodeIndex( draco, decoder, dracoGeometry ) {
- const numFaces = dracoGeometry.num_faces();
- const numIndices = numFaces * 3;
- const byteLength = numIndices * 4;
- const ptr = draco._malloc( byteLength );
- decoder.GetTrianglesUInt32Array( dracoGeometry, byteLength, ptr );
- const index = new Uint32Array( draco.HEAPF32.buffer, ptr, numIndices ).slice();
- draco._free( ptr );
- return { array: index, itemSize: 1 };
- }
- function decodeAttribute( draco, decoder, dracoGeometry, attributeName, attributeType, attribute ) {
- const numComponents = attribute.num_components();
- const numPoints = dracoGeometry.num_points();
- const numValues = numPoints * numComponents;
- const byteLength = numValues * attributeType.BYTES_PER_ELEMENT;
- const dataType = getDracoDataType( draco, attributeType );
- const ptr = draco._malloc( byteLength );
- decoder.GetAttributeDataArrayForAllPoints( dracoGeometry, attribute, dataType, byteLength, ptr );
- const array = new attributeType( draco.HEAPF32.buffer, ptr, numValues ).slice();
- draco._free( ptr );
- return {
- name: attributeName,
- array: array,
- itemSize: numComponents
- };
- }
- function getDracoDataType( draco, attributeType ) {
- switch ( attributeType ) {
- case Float32Array: return draco.DT_FLOAT32;
- case Int8Array: return draco.DT_INT8;
- case Int16Array: return draco.DT_INT16;
- case Int32Array: return draco.DT_INT32;
- case Uint8Array: return draco.DT_UINT8;
- case Uint16Array: return draco.DT_UINT16;
- case Uint32Array: return draco.DT_UINT32;
- }
- }
- }
- /**
- * @author Deepkolos / https://github.com/deepkolos
- */
- class WorkerPool$1 {
- constructor( pool = 4 ) {
- this.pool = pool;
- this.queue = [];
- this.workers = [];
- this.workersResolve = [];
- this.workerStatus = 0;
- }
- _initWorker( workerId ) {
- if ( ! this.workers[ workerId ] ) {
- const worker = this.workerCreator();
- worker.addEventListener( 'message', this._onMessage.bind( this, workerId ) );
- this.workers[ workerId ] = worker;
- }
- }
- _getIdleWorker() {
- for ( let i = 0; i < this.pool; i ++ )
- if ( ! ( this.workerStatus & ( 1 << i ) ) ) return i;
- return - 1;
- }
- _onMessage( workerId, msg ) {
- const resolve = this.workersResolve[ workerId ];
- resolve && resolve( msg );
- if ( this.queue.length ) {
- const { resolve, msg, transfer } = this.queue.shift();
- this.workersResolve[ workerId ] = resolve;
- this.workers[ workerId ].postMessage( msg, transfer );
- } else {
- this.workerStatus ^= 1 << workerId;
- }
- }
- setWorkerCreator( workerCreator ) {
- this.workerCreator = workerCreator;
- }
- setWorkerLimit( pool ) {
- this.pool = pool;
- }
- postMessage( msg, transfer ) {
- return new Promise( ( resolve ) => {
- const workerId = this._getIdleWorker();
- if ( workerId !== - 1 ) {
- this._initWorker( workerId );
- this.workerStatus |= 1 << workerId;
- this.workersResolve[ workerId ] = resolve;
- this.workers[ workerId ].postMessage( msg, transfer );
- } else {
- this.queue.push( { resolve, msg, transfer } );
- }
- } );
- }
- dispose() {
- this.workers.forEach( ( worker ) => worker.terminate() );
- this.workersResolve.length = 0;
- this.workers.length = 0;
- this.queue.length = 0;
- this.workerStatus = 0;
- }
- }
- const t=0,e$1=1,n$1=2,i$2=3,s=0,a$1=0,r=2,o$1=0,l$1=1,f=160,U$1=161,c$3=162,h$2=163,_$1=0,p=1,g=0,y=1,x$2=2,u$2=3,b$1=4,d=5,m=6,w$1=7,D=8,B=9,L=10,A=11,k=12,v=13,S$1=14,I=15,O=16,T$1=17,V=18,E=0,F=1,P=2,C=3,z=4,M$1=5,W=6,N=7,H=8,K=9,X=10,j=11,R=0,Y=1,q=2,G=13,J=14,Q=15,Z=128,$$1=64,tt=32,et=16,nt=0,it=1,st=2,at=3,rt=4,ot=5,lt=6,ft=7,Ut=8,ct=9,ht=10,_t=13,pt=14,gt=15,yt=16,xt=17,ut=20,bt=21,dt=22,mt=23,wt=24,Dt=27,Bt=28,Lt=29,At=30,kt=31,vt=34,St=35,It=36,Ot=37,Tt=38,Vt=41,Et=42,Ft=43,Pt=44,Ct=45,zt=48,Mt=49,Wt=50,Nt=58,Ht=59,Kt=62,Xt=63,jt=64,Rt=65,Yt=68,qt=69,Gt=70,Jt=71,Qt=74,Zt=75,$t=76,te=77,ee=78,ne=81,ie=82,se=83,ae=84,re=85,oe=88,le=89,fe=90,Ue=91,ce=92,he=95,_e=96,pe=97,ge=98,ye=99,xe=100,ue=101,be=102,de=103,me=104,we=105,De=106,Be=107,Le=108,Ae=109,ke=110,ve=111,Se=112,Ie=113,Oe=114,Te=115,Ve=116,Ee=117,Fe=118,Pe=119,Ce=120,ze=121,Me=122,We=123,Ne=124,He=125,Ke=126,Xe=127,je=128,Re=129,Ye=130,qe=131,Ge=132,Je=133,Qe=134,Ze=135,$e=136,tn=137,en=138,nn=139,sn=140,an=141,rn=142,on=143,ln=144,fn=145,Un=146,cn=147,hn=148,_n=149,pn=150,gn=151,yn=152,xn=153,un=154,bn=155,dn=156,mn=157,wn=158,Dn=159,Bn=160,Ln=161,An=162,kn=163,vn=164,Sn=165,In=166,On=167,Tn=168,Vn=169,En=170,Fn=171,Pn=172,Cn=173,zn=174,Mn=175,Wn=176,Nn=177,Hn=178,Kn=179,Xn=180,jn=181,Rn=182,Yn=183,qn=184,Gn=1000156007,Jn=1000156008,Qn=1000156009,Zn=1000156010,$n=1000156011,ti=1000156017,ei=1000156018,ni=1000156019,ii=1000156020,si=1000156021,ai=1000054e3,ri=1000054001,oi=1000054002,li=1000054003,fi=1000054004,Ui=1000054005,ci=1000054006,hi=1000054007,_i=1000066e3,pi=1000066001,gi=1000066002,yi=1000066003,xi=1000066004,ui=1000066005,bi=1000066006,di=1000066007,mi=1000066008,wi=1000066009,Di=1000066010,Bi=1000066011,Li=1000066012,Ai=1000066013,ki=100034e4,vi=1000340001;class Si{constructor(){this.vkFormat=0,this.typeSize=1,this.pixelWidth=0,this.pixelHeight=0,this.pixelDepth=0,this.layerCount=0,this.faceCount=1,this.supercompressionScheme=0,this.levels=[],this.dataFormatDescriptor=[{vendorId:0,descriptorType:0,descriptorBlockSize:0,versionNumber:2,colorModel:0,colorPrimaries:1,transferFunction:2,flags:0,texelBlockDimension:[0,0,0,0],bytesPlane:[0,0,0,0,0,0,0,0],samples:[]}],this.keyValue={},this.globalData=null;}}class Ii{constructor(t,e,n,i){this._dataView=new DataView(t.buffer,t.byteOffset+e,n),this._littleEndian=i,this._offset=0;}_nextUint8(){const t=this._dataView.getUint8(this._offset);return this._offset+=1,t}_nextUint16(){const t=this._dataView.getUint16(this._offset,this._littleEndian);return this._offset+=2,t}_nextUint32(){const t=this._dataView.getUint32(this._offset,this._littleEndian);return this._offset+=4,t}_nextUint64(){const t=this._dataView.getUint32(this._offset,this._littleEndian)+2**32*this._dataView.getUint32(this._offset+4,this._littleEndian);return this._offset+=8,t}_nextInt32(){const t=this._dataView.getInt32(this._offset,this._littleEndian);return this._offset+=4,t}_skip(t){return this._offset+=t,this}_scan(t,e=0){const n=this._offset;let i=0;for(;this._dataView.getUint8(this._offset)!==e&&i<t;)i++,this._offset++;return i<t&&this._offset++,new Uint8Array(this._dataView.buffer,this._dataView.byteOffset+n,i)}}const Oi=new Uint8Array([0]),Ti=[171,75,84,88,32,50,48,187,13,10,26,10];function Vi(t){return "undefined"!=typeof TextEncoder?(new TextEncoder).encode(t):Buffer.from(t)}function Ei(t){return "undefined"!=typeof TextDecoder?(new TextDecoder).decode(t):Buffer.from(t).toString("utf8")}function Fi(t){let e=0;for(const n of t)e+=n.byteLength;const n=new Uint8Array(e);let i=0;for(const e of t)n.set(new Uint8Array(e),i),i+=e.byteLength;return n}function Pi(t){const e=new Uint8Array(t.buffer,t.byteOffset,Ti.length);if(e[0]!==Ti[0]||e[1]!==Ti[1]||e[2]!==Ti[2]||e[3]!==Ti[3]||e[4]!==Ti[4]||e[5]!==Ti[5]||e[6]!==Ti[6]||e[7]!==Ti[7]||e[8]!==Ti[8]||e[9]!==Ti[9]||e[10]!==Ti[10]||e[11]!==Ti[11])throw new Error("Missing KTX 2.0 identifier.");const n=new Si,i=17*Uint32Array.BYTES_PER_ELEMENT,s=new Ii(t,Ti.length,i,!0);n.vkFormat=s._nextUint32(),n.typeSize=s._nextUint32(),n.pixelWidth=s._nextUint32(),n.pixelHeight=s._nextUint32(),n.pixelDepth=s._nextUint32(),n.layerCount=s._nextUint32(),n.faceCount=s._nextUint32();const a=s._nextUint32();n.supercompressionScheme=s._nextUint32();const r=s._nextUint32(),o=s._nextUint32(),l=s._nextUint32(),f=s._nextUint32(),U=s._nextUint64(),c=s._nextUint64(),h=new Ii(t,Ti.length+i,3*a*8,!0);for(let e=0;e<a;e++)n.levels.push({levelData:new Uint8Array(t.buffer,t.byteOffset+h._nextUint64(),h._nextUint64()),uncompressedByteLength:h._nextUint64()});const _=new Ii(t,r,o,!0),p={vendorId:_._skip(4)._nextUint16(),descriptorType:_._nextUint16(),versionNumber:_._nextUint16(),descriptorBlockSize:_._nextUint16(),colorModel:_._nextUint8(),colorPrimaries:_._nextUint8(),transferFunction:_._nextUint8(),flags:_._nextUint8(),texelBlockDimension:[_._nextUint8(),_._nextUint8(),_._nextUint8(),_._nextUint8()],bytesPlane:[_._nextUint8(),_._nextUint8(),_._nextUint8(),_._nextUint8(),_._nextUint8(),_._nextUint8(),_._nextUint8(),_._nextUint8()],samples:[]},g=(p.descriptorBlockSize/4-6)/4;for(let t=0;t<g;t++){const e={bitOffset:_._nextUint16(),bitLength:_._nextUint8(),channelType:_._nextUint8(),samplePosition:[_._nextUint8(),_._nextUint8(),_._nextUint8(),_._nextUint8()],sampleLower:-Infinity,sampleUpper:Infinity};64&e.channelType?(e.sampleLower=_._nextInt32(),e.sampleUpper=_._nextInt32()):(e.sampleLower=_._nextUint32(),e.sampleUpper=_._nextUint32()),p.samples[t]=e;}n.dataFormatDescriptor.length=0,n.dataFormatDescriptor.push(p);const y=new Ii(t,l,f,!0);for(;y._offset<f;){const t=y._nextUint32(),e=y._scan(t),i=Ei(e),s=y._scan(t-e.byteLength);n.keyValue[i]=i.match(/^ktx/i)?Ei(s):s,y._offset%4&&y._skip(4-y._offset%4);}if(c<=0)return n;const x=new Ii(t,U,c,!0),u=x._nextUint16(),b=x._nextUint16(),d=x._nextUint32(),m=x._nextUint32(),w=x._nextUint32(),D=x._nextUint32(),B=[];for(let t=0;t<a;t++)B.push({imageFlags:x._nextUint32(),rgbSliceByteOffset:x._nextUint32(),rgbSliceByteLength:x._nextUint32(),alphaSliceByteOffset:x._nextUint32(),alphaSliceByteLength:x._nextUint32()});const L=U+x._offset,A=L+d,k=A+m,v=k+w,S=new Uint8Array(t.buffer,t.byteOffset+L,d),I=new Uint8Array(t.buffer,t.byteOffset+A,m),O=new Uint8Array(t.buffer,t.byteOffset+k,w),T=new Uint8Array(t.buffer,t.byteOffset+v,D);return n.globalData={endpointCount:u,selectorCount:b,imageDescs:B,endpointsData:S,selectorsData:I,tablesData:O,extendedData:T},n}function Ci(){return (Ci=Object.assign||function(t){for(var e=1;e<arguments.length;e++){var n=arguments[e];for(var i in n)Object.prototype.hasOwnProperty.call(n,i)&&(t[i]=n[i]);}return t}).apply(this,arguments)}const zi={keepWriter:!1};function Mi(t,e={}){e=Ci({},zi,e);let n=new ArrayBuffer(0);if(t.globalData){const e=new ArrayBuffer(20+5*t.globalData.imageDescs.length*4),i=new DataView(e);i.setUint16(0,t.globalData.endpointCount,!0),i.setUint16(2,t.globalData.selectorCount,!0),i.setUint32(4,t.globalData.endpointsData.byteLength,!0),i.setUint32(8,t.globalData.selectorsData.byteLength,!0),i.setUint32(12,t.globalData.tablesData.byteLength,!0),i.setUint32(16,t.globalData.extendedData.byteLength,!0);for(let e=0;e<t.globalData.imageDescs.length;e++){const n=t.globalData.imageDescs[e];i.setUint32(20+5*e*4+0,n.imageFlags,!0),i.setUint32(20+5*e*4+4,n.rgbSliceByteOffset,!0),i.setUint32(20+5*e*4+8,n.rgbSliceByteLength,!0),i.setUint32(20+5*e*4+12,n.alphaSliceByteOffset,!0),i.setUint32(20+5*e*4+16,n.alphaSliceByteLength,!0);}n=Fi([e,t.globalData.endpointsData,t.globalData.selectorsData,t.globalData.tablesData,t.globalData.extendedData]);}const i=[];let s=t.keyValue;e.keepWriter||(s=Ci({},t.keyValue,{KTXwriter:"KTX-Parse v0.3.1"}));for(const t in s){const e=s[t],n=Vi(t),a="string"==typeof e?Vi(e):e,r=n.byteLength+1+a.byteLength+1,o=r%4?4-r%4:0;i.push(Fi([new Uint32Array([r]),n,Oi,a,Oi,new Uint8Array(o).fill(0)]));}const a=Fi(i);if(1!==t.dataFormatDescriptor.length||0!==t.dataFormatDescriptor[0].descriptorType)throw new Error("Only BASICFORMAT Data Format Descriptor output supported.");const r=t.dataFormatDescriptor[0],o=new ArrayBuffer(28+16*r.samples.length),l=new DataView(o),f=24+16*r.samples.length;if(l.setUint32(0,o.byteLength,!0),l.setUint16(4,r.vendorId,!0),l.setUint16(6,r.descriptorType,!0),l.setUint16(8,r.versionNumber,!0),l.setUint16(10,f,!0),l.setUint8(12,r.colorModel),l.setUint8(13,r.colorPrimaries),l.setUint8(14,r.transferFunction),l.setUint8(15,r.flags),!Array.isArray(r.texelBlockDimension))throw new Error("texelBlockDimension is now an array. For dimensionality `d`, set `d - 1`.");l.setUint8(16,r.texelBlockDimension[0]),l.setUint8(17,r.texelBlockDimension[1]),l.setUint8(18,r.texelBlockDimension[2]),l.setUint8(19,r.texelBlockDimension[3]);for(let t=0;t<8;t++)l.setUint8(20+t,r.bytesPlane[t]);for(let t=0;t<r.samples.length;t++){const e=r.samples[t],n=28+16*t;if(e.channelID)throw new Error("channelID has been renamed to channelType.");l.setUint16(n+0,e.bitOffset,!0),l.setUint8(n+2,e.bitLength),l.setUint8(n+3,e.channelType),l.setUint8(n+4,e.samplePosition[0]),l.setUint8(n+5,e.samplePosition[1]),l.setUint8(n+6,e.samplePosition[2]),l.setUint8(n+7,e.samplePosition[3]),64&e.channelType?(l.setInt32(n+8,e.sampleLower,!0),l.setInt32(n+12,e.sampleUpper,!0)):(l.setUint32(n+8,e.sampleLower,!0),l.setUint32(n+12,e.sampleUpper,!0));}const U=Ti.length+68+3*t.levels.length*8,c=U+o.byteLength;let h=n.byteLength>0?c+a.byteLength:0;h%8&&(h+=8-h%8);const _=[],p=new DataView(new ArrayBuffer(3*t.levels.length*8));let g=(h||c+a.byteLength)+n.byteLength;for(let e=0;e<t.levels.length;e++){const n=t.levels[e];_.push(n.levelData),p.setBigUint64(24*e+0,BigInt(g),!0),p.setBigUint64(24*e+8,BigInt(n.levelData.byteLength),!0),p.setBigUint64(24*e+16,BigInt(n.uncompressedByteLength),!0),g+=n.levelData.byteLength;}const y=new ArrayBuffer(68),x=new DataView(y);return x.setUint32(0,t.vkFormat,!0),x.setUint32(4,t.typeSize,!0),x.setUint32(8,t.pixelWidth,!0),x.setUint32(12,t.pixelHeight,!0),x.setUint32(16,t.pixelDepth,!0),x.setUint32(20,t.layerCount,!0),x.setUint32(24,t.faceCount,!0),x.setUint32(28,t.levels.length,!0),x.setUint32(32,t.supercompressionScheme,!0),x.setUint32(36,U,!0),x.setUint32(40,o.byteLength,!0),x.setUint32(44,c,!0),x.setUint32(48,a.byteLength,!0),x.setBigUint64(52,BigInt(n.byteLength>0?h:0),!0),x.setBigUint64(60,BigInt(n.byteLength),!0),new Uint8Array(Fi([new Uint8Array(Ti).buffer,y,p.buffer,o,a,h>0?new ArrayBuffer(h-(c+a.byteLength)):new ArrayBuffer(0),n,..._]))}
- var KTX = /*#__PURE__*/Object.freeze({
- __proto__: null,
- KHR_DF_CHANNEL_RGBSDA_ALPHA: Q,
- KHR_DF_CHANNEL_RGBSDA_BLUE: q,
- KHR_DF_CHANNEL_RGBSDA_DEPTH: J,
- KHR_DF_CHANNEL_RGBSDA_GREEN: Y,
- KHR_DF_CHANNEL_RGBSDA_RED: R,
- KHR_DF_CHANNEL_RGBSDA_STENCIL: G,
- KHR_DF_FLAG_ALPHA_PREMULTIPLIED: p,
- KHR_DF_FLAG_ALPHA_STRAIGHT: _$1,
- KHR_DF_KHR_DESCRIPTORTYPE_BASICFORMAT: s,
- KHR_DF_MODEL_ASTC: c$3,
- KHR_DF_MODEL_ETC1: f,
- KHR_DF_MODEL_ETC1S: h$2,
- KHR_DF_MODEL_ETC2: U$1,
- KHR_DF_MODEL_RGBSDA: l$1,
- KHR_DF_MODEL_UNSPECIFIED: o$1,
- KHR_DF_PRIMARIES_ACES: W,
- KHR_DF_PRIMARIES_ACESCC: N,
- KHR_DF_PRIMARIES_ADOBERGB: j,
- KHR_DF_PRIMARIES_BT2020: z,
- KHR_DF_PRIMARIES_BT601_EBU: P,
- KHR_DF_PRIMARIES_BT601_SMPTE: C,
- KHR_DF_PRIMARIES_BT709: F,
- KHR_DF_PRIMARIES_CIEXYZ: M$1,
- KHR_DF_PRIMARIES_DISPLAYP3: X,
- KHR_DF_PRIMARIES_NTSC1953: H,
- KHR_DF_PRIMARIES_PAL525: K,
- KHR_DF_PRIMARIES_UNSPECIFIED: E,
- KHR_DF_SAMPLE_DATATYPE_EXPONENT: tt,
- KHR_DF_SAMPLE_DATATYPE_FLOAT: Z,
- KHR_DF_SAMPLE_DATATYPE_LINEAR: et,
- KHR_DF_SAMPLE_DATATYPE_SIGNED: $$1,
- KHR_DF_TRANSFER_ACESCC: O,
- KHR_DF_TRANSFER_ACESCCT: T$1,
- KHR_DF_TRANSFER_ADOBERGB: V,
- KHR_DF_TRANSFER_BT1886: w$1,
- KHR_DF_TRANSFER_DCIP3: k,
- KHR_DF_TRANSFER_HLG_EOTF: B,
- KHR_DF_TRANSFER_HLG_OETF: D,
- KHR_DF_TRANSFER_ITU: u$2,
- KHR_DF_TRANSFER_LINEAR: y,
- KHR_DF_TRANSFER_NTSC: b$1,
- KHR_DF_TRANSFER_PAL625_EOTF: S$1,
- KHR_DF_TRANSFER_PAL_OETF: v,
- KHR_DF_TRANSFER_PQ_EOTF: L,
- KHR_DF_TRANSFER_PQ_OETF: A,
- KHR_DF_TRANSFER_SLOG: d,
- KHR_DF_TRANSFER_SLOG2: m,
- KHR_DF_TRANSFER_SRGB: x$2,
- KHR_DF_TRANSFER_ST240: I,
- KHR_DF_TRANSFER_UNSPECIFIED: g,
- KHR_DF_VENDORID_KHRONOS: a$1,
- KHR_DF_VERSION: r,
- KHR_SUPERCOMPRESSION_BASISLZ: e$1,
- KHR_SUPERCOMPRESSION_NONE: t,
- KHR_SUPERCOMPRESSION_ZLIB: i$2,
- KHR_SUPERCOMPRESSION_ZSTD: n$1,
- KTX2Container: Si,
- VK_FORMAT_A1R5G5B5_UNORM_PACK16: Ut,
- VK_FORMAT_A2B10G10R10_SINT_PACK32: qt,
- VK_FORMAT_A2B10G10R10_SNORM_PACK32: Rt,
- VK_FORMAT_A2B10G10R10_UINT_PACK32: Yt,
- VK_FORMAT_A2B10G10R10_UNORM_PACK32: jt,
- VK_FORMAT_A2R10G10B10_SINT_PACK32: Xt,
- VK_FORMAT_A2R10G10B10_SNORM_PACK32: Ht,
- VK_FORMAT_A2R10G10B10_UINT_PACK32: Kt,
- VK_FORMAT_A2R10G10B10_UNORM_PACK32: Nt,
- VK_FORMAT_A4B4G4R4_UNORM_PACK16_EXT: vi,
- VK_FORMAT_A4R4G4B4_UNORM_PACK16_EXT: ki,
- VK_FORMAT_ASTC_10x10_SFLOAT_BLOCK_EXT: Bi,
- VK_FORMAT_ASTC_10x10_SRGB_BLOCK: Xn,
- VK_FORMAT_ASTC_10x10_UNORM_BLOCK: Kn,
- VK_FORMAT_ASTC_10x5_SFLOAT_BLOCK_EXT: mi,
- VK_FORMAT_ASTC_10x5_SRGB_BLOCK: zn,
- VK_FORMAT_ASTC_10x5_UNORM_BLOCK: Cn,
- VK_FORMAT_ASTC_10x6_SFLOAT_BLOCK_EXT: wi,
- VK_FORMAT_ASTC_10x6_SRGB_BLOCK: Wn,
- VK_FORMAT_ASTC_10x6_UNORM_BLOCK: Mn,
- VK_FORMAT_ASTC_10x8_SFLOAT_BLOCK_EXT: Di,
- VK_FORMAT_ASTC_10x8_SRGB_BLOCK: Hn,
- VK_FORMAT_ASTC_10x8_UNORM_BLOCK: Nn,
- VK_FORMAT_ASTC_12x10_SFLOAT_BLOCK_EXT: Li,
- VK_FORMAT_ASTC_12x10_SRGB_BLOCK: Rn,
- VK_FORMAT_ASTC_12x10_UNORM_BLOCK: jn,
- VK_FORMAT_ASTC_12x12_SFLOAT_BLOCK_EXT: Ai,
- VK_FORMAT_ASTC_12x12_SRGB_BLOCK: qn,
- VK_FORMAT_ASTC_12x12_UNORM_BLOCK: Yn,
- VK_FORMAT_ASTC_4x4_SFLOAT_BLOCK_EXT: _i,
- VK_FORMAT_ASTC_4x4_SRGB_BLOCK: wn,
- VK_FORMAT_ASTC_4x4_UNORM_BLOCK: mn,
- VK_FORMAT_ASTC_5x4_SFLOAT_BLOCK_EXT: pi,
- VK_FORMAT_ASTC_5x4_SRGB_BLOCK: Bn,
- VK_FORMAT_ASTC_5x4_UNORM_BLOCK: Dn,
- VK_FORMAT_ASTC_5x5_SFLOAT_BLOCK_EXT: gi,
- VK_FORMAT_ASTC_5x5_SRGB_BLOCK: An,
- VK_FORMAT_ASTC_5x5_UNORM_BLOCK: Ln,
- VK_FORMAT_ASTC_6x5_SFLOAT_BLOCK_EXT: yi,
- VK_FORMAT_ASTC_6x5_SRGB_BLOCK: vn,
- VK_FORMAT_ASTC_6x5_UNORM_BLOCK: kn,
- VK_FORMAT_ASTC_6x6_SFLOAT_BLOCK_EXT: xi,
- VK_FORMAT_ASTC_6x6_SRGB_BLOCK: In,
- VK_FORMAT_ASTC_6x6_UNORM_BLOCK: Sn,
- VK_FORMAT_ASTC_8x5_SFLOAT_BLOCK_EXT: ui,
- VK_FORMAT_ASTC_8x5_SRGB_BLOCK: Tn,
- VK_FORMAT_ASTC_8x5_UNORM_BLOCK: On,
- VK_FORMAT_ASTC_8x6_SFLOAT_BLOCK_EXT: bi,
- VK_FORMAT_ASTC_8x6_SRGB_BLOCK: En,
- VK_FORMAT_ASTC_8x6_UNORM_BLOCK: Vn,
- VK_FORMAT_ASTC_8x8_SFLOAT_BLOCK_EXT: di,
- VK_FORMAT_ASTC_8x8_SRGB_BLOCK: Pn,
- VK_FORMAT_ASTC_8x8_UNORM_BLOCK: Fn,
- VK_FORMAT_B10G11R11_UFLOAT_PACK32: Me,
- VK_FORMAT_B10X6G10X6R10X6G10X6_422_UNORM_4PACK16: $n,
- VK_FORMAT_B12X4G12X4R12X4G12X4_422_UNORM_4PACK16: si,
- VK_FORMAT_B4G4R4A4_UNORM_PACK16: at,
- VK_FORMAT_B5G5R5A1_UNORM_PACK16: ft,
- VK_FORMAT_B5G6R5_UNORM_PACK16: ot,
- VK_FORMAT_B8G8R8A8_SINT: Mt,
- VK_FORMAT_B8G8R8A8_SNORM: Ct,
- VK_FORMAT_B8G8R8A8_SRGB: Wt,
- VK_FORMAT_B8G8R8A8_UINT: zt,
- VK_FORMAT_B8G8R8A8_UNORM: Pt,
- VK_FORMAT_B8G8R8_SINT: St,
- VK_FORMAT_B8G8R8_SNORM: kt,
- VK_FORMAT_B8G8R8_SRGB: It,
- VK_FORMAT_B8G8R8_UINT: vt,
- VK_FORMAT_B8G8R8_UNORM: At,
- VK_FORMAT_BC1_RGBA_SRGB_BLOCK: Qe,
- VK_FORMAT_BC1_RGBA_UNORM_BLOCK: Je,
- VK_FORMAT_BC1_RGB_SRGB_BLOCK: Ge,
- VK_FORMAT_BC1_RGB_UNORM_BLOCK: qe,
- VK_FORMAT_BC2_SRGB_BLOCK: $e,
- VK_FORMAT_BC2_UNORM_BLOCK: Ze,
- VK_FORMAT_BC3_SRGB_BLOCK: en,
- VK_FORMAT_BC3_UNORM_BLOCK: tn,
- VK_FORMAT_BC4_SNORM_BLOCK: sn,
- VK_FORMAT_BC4_UNORM_BLOCK: nn,
- VK_FORMAT_BC5_SNORM_BLOCK: rn,
- VK_FORMAT_BC5_UNORM_BLOCK: an,
- VK_FORMAT_BC6H_SFLOAT_BLOCK: ln,
- VK_FORMAT_BC6H_UFLOAT_BLOCK: on,
- VK_FORMAT_BC7_SRGB_BLOCK: Un,
- VK_FORMAT_BC7_UNORM_BLOCK: fn,
- VK_FORMAT_D16_UNORM: Ne,
- VK_FORMAT_D16_UNORM_S8_UINT: je,
- VK_FORMAT_D24_UNORM_S8_UINT: Re,
- VK_FORMAT_D32_SFLOAT: Ke,
- VK_FORMAT_D32_SFLOAT_S8_UINT: Ye,
- VK_FORMAT_E5B9G9R9_UFLOAT_PACK32: We,
- VK_FORMAT_EAC_R11G11_SNORM_BLOCK: dn,
- VK_FORMAT_EAC_R11G11_UNORM_BLOCK: bn,
- VK_FORMAT_EAC_R11_SNORM_BLOCK: un,
- VK_FORMAT_EAC_R11_UNORM_BLOCK: xn,
- VK_FORMAT_ETC2_R8G8B8A1_SRGB_BLOCK: pn,
- VK_FORMAT_ETC2_R8G8B8A1_UNORM_BLOCK: _n,
- VK_FORMAT_ETC2_R8G8B8A8_SRGB_BLOCK: yn,
- VK_FORMAT_ETC2_R8G8B8A8_UNORM_BLOCK: gn,
- VK_FORMAT_ETC2_R8G8B8_SRGB_BLOCK: hn,
- VK_FORMAT_ETC2_R8G8B8_UNORM_BLOCK: cn,
- VK_FORMAT_G10X6B10X6G10X6R10X6_422_UNORM_4PACK16: Zn,
- VK_FORMAT_G12X4B12X4G12X4R12X4_422_UNORM_4PACK16: ii,
- VK_FORMAT_PVRTC1_2BPP_SRGB_BLOCK_IMG: fi,
- VK_FORMAT_PVRTC1_2BPP_UNORM_BLOCK_IMG: ai,
- VK_FORMAT_PVRTC1_4BPP_SRGB_BLOCK_IMG: Ui,
- VK_FORMAT_PVRTC1_4BPP_UNORM_BLOCK_IMG: ri,
- VK_FORMAT_PVRTC2_2BPP_SRGB_BLOCK_IMG: ci,
- VK_FORMAT_PVRTC2_2BPP_UNORM_BLOCK_IMG: oi,
- VK_FORMAT_PVRTC2_4BPP_SRGB_BLOCK_IMG: hi,
- VK_FORMAT_PVRTC2_4BPP_UNORM_BLOCK_IMG: li,
- VK_FORMAT_R10X6G10X6B10X6A10X6_UNORM_4PACK16: Qn,
- VK_FORMAT_R10X6G10X6_UNORM_2PACK16: Jn,
- VK_FORMAT_R10X6_UNORM_PACK16: Gn,
- VK_FORMAT_R12X4G12X4B12X4A12X4_UNORM_4PACK16: ni,
- VK_FORMAT_R12X4G12X4_UNORM_2PACK16: ei,
- VK_FORMAT_R12X4_UNORM_PACK16: ti,
- VK_FORMAT_R16G16B16A16_SFLOAT: pe,
- VK_FORMAT_R16G16B16A16_SINT: _e,
- VK_FORMAT_R16G16B16A16_SNORM: ce,
- VK_FORMAT_R16G16B16A16_UINT: he,
- VK_FORMAT_R16G16B16A16_UNORM: Ue,
- VK_FORMAT_R16G16B16_SFLOAT: fe,
- VK_FORMAT_R16G16B16_SINT: le,
- VK_FORMAT_R16G16B16_SNORM: re,
- VK_FORMAT_R16G16B16_UINT: oe,
- VK_FORMAT_R16G16B16_UNORM: ae,
- VK_FORMAT_R16G16_SFLOAT: se,
- VK_FORMAT_R16G16_SINT: ie,
- VK_FORMAT_R16G16_SNORM: ee,
- VK_FORMAT_R16G16_UINT: ne,
- VK_FORMAT_R16G16_UNORM: te,
- VK_FORMAT_R16_SFLOAT: $t,
- VK_FORMAT_R16_SINT: Zt,
- VK_FORMAT_R16_SNORM: Jt,
- VK_FORMAT_R16_UINT: Qt,
- VK_FORMAT_R16_UNORM: Gt,
- VK_FORMAT_R32G32B32A32_SFLOAT: Ae,
- VK_FORMAT_R32G32B32A32_SINT: Le,
- VK_FORMAT_R32G32B32A32_UINT: Be,
- VK_FORMAT_R32G32B32_SFLOAT: De,
- VK_FORMAT_R32G32B32_SINT: we,
- VK_FORMAT_R32G32B32_UINT: me,
- VK_FORMAT_R32G32_SFLOAT: de,
- VK_FORMAT_R32G32_SINT: be,
- VK_FORMAT_R32G32_UINT: ue,
- VK_FORMAT_R32_SFLOAT: xe,
- VK_FORMAT_R32_SINT: ye,
- VK_FORMAT_R32_UINT: ge,
- VK_FORMAT_R4G4B4A4_UNORM_PACK16: st,
- VK_FORMAT_R4G4_UNORM_PACK8: it,
- VK_FORMAT_R5G5B5A1_UNORM_PACK16: lt,
- VK_FORMAT_R5G6B5_UNORM_PACK16: rt,
- VK_FORMAT_R64G64B64A64_SFLOAT: ze,
- VK_FORMAT_R64G64B64A64_SINT: Ce,
- VK_FORMAT_R64G64B64A64_UINT: Pe,
- VK_FORMAT_R64G64B64_SFLOAT: Fe,
- VK_FORMAT_R64G64B64_SINT: Ee,
- VK_FORMAT_R64G64B64_UINT: Ve,
- VK_FORMAT_R64G64_SFLOAT: Te,
- VK_FORMAT_R64G64_SINT: Oe,
- VK_FORMAT_R64G64_UINT: Ie,
- VK_FORMAT_R64_SFLOAT: Se,
- VK_FORMAT_R64_SINT: ve,
- VK_FORMAT_R64_UINT: ke,
- VK_FORMAT_R8G8B8A8_SINT: Et,
- VK_FORMAT_R8G8B8A8_SNORM: Tt,
- VK_FORMAT_R8G8B8A8_SRGB: Ft,
- VK_FORMAT_R8G8B8A8_UINT: Vt,
- VK_FORMAT_R8G8B8A8_UNORM: Ot,
- VK_FORMAT_R8G8B8_SINT: Bt,
- VK_FORMAT_R8G8B8_SNORM: wt,
- VK_FORMAT_R8G8B8_SRGB: Lt,
- VK_FORMAT_R8G8B8_UINT: Dt,
- VK_FORMAT_R8G8B8_UNORM: mt,
- VK_FORMAT_R8G8_SINT: bt,
- VK_FORMAT_R8G8_SNORM: xt,
- VK_FORMAT_R8G8_SRGB: dt,
- VK_FORMAT_R8G8_UINT: ut,
- VK_FORMAT_R8G8_UNORM: yt,
- VK_FORMAT_R8_SINT: pt,
- VK_FORMAT_R8_SNORM: ht,
- VK_FORMAT_R8_SRGB: gt,
- VK_FORMAT_R8_UINT: _t,
- VK_FORMAT_R8_UNORM: ct,
- VK_FORMAT_S8_UINT: Xe,
- VK_FORMAT_UNDEFINED: nt,
- VK_FORMAT_X8_D24_UNORM_PACK32: He,
- read: Pi,
- write: Mi
- });
- /**
- * Loader for KTX 2.0 GPU Texture containers.
- *
- * KTX 2.0 is a container format for various GPU texture formats. The loader
- * supports Basis Universal GPU textures, which can be quickly transcoded to
- * a wide variety of GPU texture compression formats, as well as some
- * uncompressed DataTexture and Data3DTexture formats.
- *
- * References:
- * - KTX: http://github.khronos.org/KTX-Specification/
- * - DFD: https://www.khronos.org/registry/DataFormat/specs/1.3/dataformat.1.3.html#basicdescriptor
- */
- const {
- read: read$1,
- KHR_DF_FLAG_ALPHA_PREMULTIPLIED,
- KHR_DF_TRANSFER_SRGB,
- VK_FORMAT_UNDEFINED,
- VK_FORMAT_R16_SFLOAT,
- VK_FORMAT_R16G16_SFLOAT,
- VK_FORMAT_R16G16B16A16_SFLOAT,
- VK_FORMAT_R32_SFLOAT,
- VK_FORMAT_R32G32_SFLOAT,
- VK_FORMAT_R32G32B32A32_SFLOAT,
- VK_FORMAT_R8_SRGB,
- VK_FORMAT_R8_UNORM,
- VK_FORMAT_R8G8_SRGB,
- VK_FORMAT_R8G8_UNORM,
- VK_FORMAT_R8G8B8A8_SRGB,
- VK_FORMAT_R8G8B8A8_UNORM,
- } = KTX; // eslint-disable-line no-undef
- const _taskCache$1 = new WeakMap();
- let _activeLoaders = 0;
- class KTX2Loader extends Loader {
- constructor( manager ) {
- super( manager );
- this.transcoderPath = '';
- this.transcoderBinary = null;
- this.transcoderPending = null;
- this.workerPool = new WorkerPool$1();
- this.workerSourceURL = '';
- this.workerConfig = null;
- if ( typeof MSC_TRANSCODER !== 'undefined' ) {
- console.warn(
- 'THREE.KTX2Loader: Please update to latest "basis_transcoder".'
- + ' "msc_basis_transcoder" is no longer supported in three.js r125+.'
- );
- }
- }
- setTranscoderPath( path ) {
- this.transcoderPath = path;
- return this;
- }
- setWorkerLimit( num ) {
- this.workerPool.setWorkerLimit( num );
- return this;
- }
- detectSupport( renderer ) {
- this.workerConfig = {
- astcSupported: renderer.extensions.has( 'WEBGL_compressed_texture_astc' ),
- etc1Supported: renderer.extensions.has( 'WEBGL_compressed_texture_etc1' ),
- etc2Supported: renderer.extensions.has( 'WEBGL_compressed_texture_etc' ),
- dxtSupported: renderer.extensions.has( 'WEBGL_compressed_texture_s3tc' ),
- bptcSupported: renderer.extensions.has( 'EXT_texture_compression_bptc' ),
- pvrtcSupported: renderer.extensions.has( 'WEBGL_compressed_texture_pvrtc' )
- || renderer.extensions.has( 'WEBKIT_WEBGL_compressed_texture_pvrtc' )
- };
- if ( renderer.capabilities.isWebGL2 ) {
- // https://github.com/mrdoob/three.js/pull/22928
- this.workerConfig.etc1Supported = false;
- }
- return this;
- }
- init() {
- if ( ! this.transcoderPending ) {
- // Load transcoder wrapper.
- const jsLoader = new FileLoader( this.manager );
- jsLoader.setPath( this.transcoderPath );
- jsLoader.setWithCredentials( this.withCredentials );
- const jsContent = jsLoader.loadAsync( 'basis_transcoder.js' );
- // Load transcoder WASM binary.
- const binaryLoader = new FileLoader( this.manager );
- binaryLoader.setPath( this.transcoderPath );
- binaryLoader.setResponseType( 'arraybuffer' );
- binaryLoader.setWithCredentials( this.withCredentials );
- const binaryContent = binaryLoader.loadAsync( 'basis_transcoder.wasm' );
- this.transcoderPending = Promise.all( [ jsContent, binaryContent ] )
- .then( ( [ jsContent, binaryContent ] ) => {
- const fn = KTX2Loader.BasisWorker.toString();
- const body = [
- '/* constants */',
- 'let _EngineFormat = ' + JSON.stringify( KTX2Loader.EngineFormat ),
- 'let _TranscoderFormat = ' + JSON.stringify( KTX2Loader.TranscoderFormat ),
- 'let _BasisFormat = ' + JSON.stringify( KTX2Loader.BasisFormat ),
- '/* basis_transcoder.js */',
- jsContent,
- '/* worker */',
- fn.substring( fn.indexOf( '{' ) + 1, fn.lastIndexOf( '}' ) )
- ].join( '\n' );
- this.workerSourceURL = URL.createObjectURL( new Blob( [ body ] ) );
- this.transcoderBinary = binaryContent;
- this.workerPool.setWorkerCreator( () => {
- const worker = new Worker( this.workerSourceURL );
- const transcoderBinary = this.transcoderBinary.slice( 0 );
- worker.postMessage( { type: 'init', config: this.workerConfig, transcoderBinary }, [ transcoderBinary ] );
- return worker;
- } );
- } );
- if ( _activeLoaders > 0 ) {
- // Each instance loads a transcoder and allocates workers, increasing network and memory cost.
- console.warn(
- 'THREE.KTX2Loader: Multiple active KTX2 loaders may cause performance issues.'
- + ' Use a single KTX2Loader instance, or call .dispose() on old instances.'
- );
- }
- _activeLoaders ++;
- }
- return this.transcoderPending;
- }
- load( url, onLoad, onProgress, onError ) {
- if ( this.workerConfig === null ) {
- throw new Error( 'THREE.KTX2Loader: Missing initialization with `.detectSupport( renderer )`.' );
- }
- const loader = new FileLoader( this.manager );
- loader.setResponseType( 'arraybuffer' );
- loader.setWithCredentials( this.withCredentials );
- loader.load( url, ( buffer ) => {
- // Check for an existing task using this buffer. A transferred buffer cannot be transferred
- // again from this thread.
- if ( _taskCache$1.has( buffer ) ) {
- const cachedTask = _taskCache$1.get( buffer );
- return cachedTask.promise.then( onLoad ).catch( onError );
- }
- this._createTexture( buffer )
- .then( ( texture ) => onLoad ? onLoad( texture ) : null )
- .catch( onError );
- }, onProgress, onError );
- }
- _createTextureFrom( transcodeResult ) {
- const { mipmaps, width, height, format, type, error, dfdTransferFn, dfdFlags } = transcodeResult;
- if ( type === 'error' ) return Promise.reject( error );
- const texture = new CompressedTexture( mipmaps, width, height, format, UnsignedByteType );
- texture.minFilter = mipmaps.length === 1 ? LinearFilter : LinearMipmapLinearFilter;
- texture.magFilter = LinearFilter;
- texture.generateMipmaps = false;
- texture.needsUpdate = true;
- texture.encoding = dfdTransferFn === KHR_DF_TRANSFER_SRGB ? sRGBEncoding : LinearEncoding;
- texture.premultiplyAlpha = !! ( dfdFlags & KHR_DF_FLAG_ALPHA_PREMULTIPLIED );
- return texture;
- }
- /**
- * @param {ArrayBuffer} buffer
- * @param {object?} config
- * @return {Promise<CompressedTexture|DataTexture|Data3DTexture>}
- */
- _createTexture( buffer, config = {} ) {
- const container = read$1( new Uint8Array( buffer ) );
- if ( container.vkFormat !== VK_FORMAT_UNDEFINED ) {
- return createDataTexture( container );
- }
- //
- const taskConfig = config;
- const texturePending = this.init().then( () => {
- return this.workerPool.postMessage( { type: 'transcode', buffer, taskConfig: taskConfig }, [ buffer ] );
- } ).then( ( e ) => this._createTextureFrom( e.data ) );
- // Cache the task result.
- _taskCache$1.set( buffer, { promise: texturePending } );
- return texturePending;
- }
- dispose() {
- this.workerPool.dispose();
- if ( this.workerSourceURL ) URL.revokeObjectURL( this.workerSourceURL );
- _activeLoaders --;
- return this;
- }
- }
- /* CONSTANTS */
- KTX2Loader.BasisFormat = {
- ETC1S: 0,
- UASTC_4x4: 1,
- };
- KTX2Loader.TranscoderFormat = {
- ETC1: 0,
- ETC2: 1,
- BC1: 2,
- BC3: 3,
- BC4: 4,
- BC5: 5,
- BC7_M6_OPAQUE_ONLY: 6,
- BC7_M5: 7,
- PVRTC1_4_RGB: 8,
- PVRTC1_4_RGBA: 9,
- ASTC_4x4: 10,
- ATC_RGB: 11,
- ATC_RGBA_INTERPOLATED_ALPHA: 12,
- RGBA32: 13,
- RGB565: 14,
- BGR565: 15,
- RGBA4444: 16,
- };
- KTX2Loader.EngineFormat = {
- RGBAFormat: RGBAFormat,
- RGBA_ASTC_4x4_Format: RGBA_ASTC_4x4_Format,
- RGBA_BPTC_Format: RGBA_BPTC_Format,
- RGBA_ETC2_EAC_Format: RGBA_ETC2_EAC_Format,
- RGBA_PVRTC_4BPPV1_Format: RGBA_PVRTC_4BPPV1_Format,
- RGBA_S3TC_DXT5_Format: RGBA_S3TC_DXT5_Format$1,
- RGB_ETC1_Format: RGB_ETC1_Format,
- RGB_ETC2_Format: RGB_ETC2_Format,
- RGB_PVRTC_4BPPV1_Format: RGB_PVRTC_4BPPV1_Format,
- RGB_S3TC_DXT1_Format: RGB_S3TC_DXT1_Format,
- };
- /* WEB WORKER */
- KTX2Loader.BasisWorker = function () {
- let config;
- let transcoderPending;
- let BasisModule;
- const EngineFormat = _EngineFormat; // eslint-disable-line no-undef
- const TranscoderFormat = _TranscoderFormat; // eslint-disable-line no-undef
- const BasisFormat = _BasisFormat; // eslint-disable-line no-undef
- self.addEventListener( 'message', function ( e ) {
- const message = e.data;
- switch ( message.type ) {
- case 'init':
- config = message.config;
- init( message.transcoderBinary );
- break;
- case 'transcode':
- transcoderPending.then( () => {
- try {
- const { width, height, hasAlpha, mipmaps, format, dfdTransferFn, dfdFlags } = transcode( message.buffer );
- const buffers = [];
- for ( let i = 0; i < mipmaps.length; ++ i ) {
- buffers.push( mipmaps[ i ].data.buffer );
- }
- self.postMessage( { type: 'transcode', id: message.id, width, height, hasAlpha, mipmaps, format, dfdTransferFn, dfdFlags }, buffers );
- } catch ( error ) {
- console.error( error );
- self.postMessage( { type: 'error', id: message.id, error: error.message } );
- }
- } );
- break;
- }
- } );
- function init( wasmBinary ) {
- transcoderPending = new Promise( ( resolve ) => {
- BasisModule = { wasmBinary, onRuntimeInitialized: resolve };
- BASIS( BasisModule ); // eslint-disable-line no-undef
- } ).then( () => {
- BasisModule.initializeBasis();
- if ( BasisModule.KTX2File === undefined ) {
- console.warn( 'THREE.KTX2Loader: Please update Basis Universal transcoder.' );
- }
- } );
- }
- function transcode( buffer ) {
- const ktx2File = new BasisModule.KTX2File( new Uint8Array( buffer ) );
- function cleanup() {
- ktx2File.close();
- ktx2File.delete();
- }
- if ( ! ktx2File.isValid() ) {
- cleanup();
- throw new Error( 'THREE.KTX2Loader: Invalid or unsupported .ktx2 file' );
- }
- const basisFormat = ktx2File.isUASTC() ? BasisFormat.UASTC_4x4 : BasisFormat.ETC1S;
- const width = ktx2File.getWidth();
- const height = ktx2File.getHeight();
- const levels = ktx2File.getLevels();
- const hasAlpha = ktx2File.getHasAlpha();
- const dfdTransferFn = ktx2File.getDFDTransferFunc();
- const dfdFlags = ktx2File.getDFDFlags();
- const { transcoderFormat, engineFormat } = getTranscoderFormat( basisFormat, width, height, hasAlpha );
- if ( ! width || ! height || ! levels ) {
- cleanup();
- throw new Error( 'THREE.KTX2Loader: Invalid texture' );
- }
- if ( ! ktx2File.startTranscoding() ) {
- cleanup();
- throw new Error( 'THREE.KTX2Loader: .startTranscoding failed' );
- }
- const mipmaps = [];
- for ( let mip = 0; mip < levels; mip ++ ) {
- const levelInfo = ktx2File.getImageLevelInfo( mip, 0, 0 );
- const mipWidth = levelInfo.origWidth;
- const mipHeight = levelInfo.origHeight;
- const dst = new Uint8Array( ktx2File.getImageTranscodedSizeInBytes( mip, 0, 0, transcoderFormat ) );
- const status = ktx2File.transcodeImage(
- dst,
- mip,
- 0,
- 0,
- transcoderFormat,
- 0,
- - 1,
- - 1,
- );
- if ( ! status ) {
- cleanup();
- throw new Error( 'THREE.KTX2Loader: .transcodeImage failed.' );
- }
- mipmaps.push( { data: dst, width: mipWidth, height: mipHeight } );
- }
- cleanup();
- return { width, height, hasAlpha, mipmaps, format: engineFormat, dfdTransferFn, dfdFlags };
- }
- //
- // Optimal choice of a transcoder target format depends on the Basis format (ETC1S or UASTC),
- // device capabilities, and texture dimensions. The list below ranks the formats separately
- // for ETC1S and UASTC.
- //
- // In some cases, transcoding UASTC to RGBA32 might be preferred for higher quality (at
- // significant memory cost) compared to ETC1/2, BC1/3, and PVRTC. The transcoder currently
- // chooses RGBA32 only as a last resort and does not expose that option to the caller.
- const FORMAT_OPTIONS = [
- {
- if: 'astcSupported',
- basisFormat: [ BasisFormat.UASTC_4x4 ],
- transcoderFormat: [ TranscoderFormat.ASTC_4x4, TranscoderFormat.ASTC_4x4 ],
- engineFormat: [ EngineFormat.RGBA_ASTC_4x4_Format, EngineFormat.RGBA_ASTC_4x4_Format ],
- priorityETC1S: Infinity,
- priorityUASTC: 1,
- needsPowerOfTwo: false,
- },
- {
- if: 'bptcSupported',
- basisFormat: [ BasisFormat.ETC1S, BasisFormat.UASTC_4x4 ],
- transcoderFormat: [ TranscoderFormat.BC7_M5, TranscoderFormat.BC7_M5 ],
- engineFormat: [ EngineFormat.RGBA_BPTC_Format, EngineFormat.RGBA_BPTC_Format ],
- priorityETC1S: 3,
- priorityUASTC: 2,
- needsPowerOfTwo: false,
- },
- {
- if: 'dxtSupported',
- basisFormat: [ BasisFormat.ETC1S, BasisFormat.UASTC_4x4 ],
- transcoderFormat: [ TranscoderFormat.BC1, TranscoderFormat.BC3 ],
- engineFormat: [ EngineFormat.RGB_S3TC_DXT1_Format, EngineFormat.RGBA_S3TC_DXT5_Format ],
- priorityETC1S: 4,
- priorityUASTC: 5,
- needsPowerOfTwo: false,
- },
- {
- if: 'etc2Supported',
- basisFormat: [ BasisFormat.ETC1S, BasisFormat.UASTC_4x4 ],
- transcoderFormat: [ TranscoderFormat.ETC1, TranscoderFormat.ETC2 ],
- engineFormat: [ EngineFormat.RGB_ETC2_Format, EngineFormat.RGBA_ETC2_EAC_Format ],
- priorityETC1S: 1,
- priorityUASTC: 3,
- needsPowerOfTwo: false,
- },
- {
- if: 'etc1Supported',
- basisFormat: [ BasisFormat.ETC1S, BasisFormat.UASTC_4x4 ],
- transcoderFormat: [ TranscoderFormat.ETC1 ],
- engineFormat: [ EngineFormat.RGB_ETC1_Format ],
- priorityETC1S: 2,
- priorityUASTC: 4,
- needsPowerOfTwo: false,
- },
- {
- if: 'pvrtcSupported',
- basisFormat: [ BasisFormat.ETC1S, BasisFormat.UASTC_4x4 ],
- transcoderFormat: [ TranscoderFormat.PVRTC1_4_RGB, TranscoderFormat.PVRTC1_4_RGBA ],
- engineFormat: [ EngineFormat.RGB_PVRTC_4BPPV1_Format, EngineFormat.RGBA_PVRTC_4BPPV1_Format ],
- priorityETC1S: 5,
- priorityUASTC: 6,
- needsPowerOfTwo: true,
- },
- ];
- const ETC1S_OPTIONS = FORMAT_OPTIONS.sort( function ( a, b ) {
- return a.priorityETC1S - b.priorityETC1S;
- } );
- const UASTC_OPTIONS = FORMAT_OPTIONS.sort( function ( a, b ) {
- return a.priorityUASTC - b.priorityUASTC;
- } );
- function getTranscoderFormat( basisFormat, width, height, hasAlpha ) {
- let transcoderFormat;
- let engineFormat;
- const options = basisFormat === BasisFormat.ETC1S ? ETC1S_OPTIONS : UASTC_OPTIONS;
- for ( let i = 0; i < options.length; i ++ ) {
- const opt = options[ i ];
- if ( ! config[ opt.if ] ) continue;
- if ( ! opt.basisFormat.includes( basisFormat ) ) continue;
- if ( hasAlpha && opt.transcoderFormat.length < 2 ) continue;
- if ( opt.needsPowerOfTwo && ! ( isPowerOfTwo( width ) && isPowerOfTwo( height ) ) ) continue;
- transcoderFormat = opt.transcoderFormat[ hasAlpha ? 1 : 0 ];
- engineFormat = opt.engineFormat[ hasAlpha ? 1 : 0 ];
- return { transcoderFormat, engineFormat };
- }
- console.warn( 'THREE.KTX2Loader: No suitable compressed texture format found. Decoding to RGBA32.' );
- transcoderFormat = TranscoderFormat.RGBA32;
- engineFormat = EngineFormat.RGBAFormat;
- return { transcoderFormat, engineFormat };
- }
- function isPowerOfTwo( value ) {
- if ( value <= 2 ) return true;
- return ( value & ( value - 1 ) ) === 0 && value !== 0;
- }
- };
- //
- // DataTexture and Data3DTexture parsing.
- const FORMAT_MAP = {
- [ VK_FORMAT_R32G32B32A32_SFLOAT ]: RGBAFormat,
- [ VK_FORMAT_R16G16B16A16_SFLOAT ]: RGBAFormat,
- [ VK_FORMAT_R8G8B8A8_UNORM ]: RGBAFormat,
- [ VK_FORMAT_R8G8B8A8_SRGB ]: RGBAFormat,
- [ VK_FORMAT_R32G32_SFLOAT ]: RGFormat,
- [ VK_FORMAT_R16G16_SFLOAT ]: RGFormat,
- [ VK_FORMAT_R8G8_UNORM ]: RGFormat,
- [ VK_FORMAT_R8G8_SRGB ]: RGFormat,
- [ VK_FORMAT_R32_SFLOAT ]: RedFormat,
- [ VK_FORMAT_R16_SFLOAT ]: RedFormat,
- [ VK_FORMAT_R8_SRGB ]: RedFormat,
- [ VK_FORMAT_R8_UNORM ]: RedFormat,
- };
- const TYPE_MAP = {
- [ VK_FORMAT_R32G32B32A32_SFLOAT ]: FloatType,
- [ VK_FORMAT_R16G16B16A16_SFLOAT ]: HalfFloatType,
- [ VK_FORMAT_R8G8B8A8_UNORM ]: UnsignedByteType,
- [ VK_FORMAT_R8G8B8A8_SRGB ]: UnsignedByteType,
- [ VK_FORMAT_R32G32_SFLOAT ]: FloatType,
- [ VK_FORMAT_R16G16_SFLOAT ]: HalfFloatType,
- [ VK_FORMAT_R8G8_UNORM ]: UnsignedByteType,
- [ VK_FORMAT_R8G8_SRGB ]: UnsignedByteType,
- [ VK_FORMAT_R32_SFLOAT ]: FloatType,
- [ VK_FORMAT_R16_SFLOAT ]: HalfFloatType,
- [ VK_FORMAT_R8_SRGB ]: UnsignedByteType,
- [ VK_FORMAT_R8_UNORM ]: UnsignedByteType,
- };
- const ENCODING_MAP = {
- [ VK_FORMAT_R8G8B8A8_SRGB ]: sRGBEncoding,
- [ VK_FORMAT_R8G8_SRGB ]: sRGBEncoding,
- [ VK_FORMAT_R8_SRGB ]: sRGBEncoding,
- };
- function createDataTexture( container ) {
- const { vkFormat, pixelWidth, pixelHeight, pixelDepth } = container;
- if ( FORMAT_MAP[ vkFormat ] === undefined ) {
- throw new Error( 'THREE.KTX2Loader: Unsupported vkFormat.' );
- }
- //
- let view;
- const levelData = container.levels[ 0 ].levelData;
- if ( TYPE_MAP[ vkFormat ] === FloatType ) {
- view = new Float32Array(
- levelData.buffer,
- levelData.byteOffset,
- levelData.byteLength / Float32Array.BYTES_PER_ELEMENT
- );
- } else if ( TYPE_MAP[ vkFormat ] === HalfFloatType ) {
- view = new Uint16Array(
- levelData.buffer,
- levelData.byteOffset,
- levelData.byteLength / Uint16Array.BYTES_PER_ELEMENT
- );
- } else {
- view = levelData;
- }
- //
- const texture = pixelDepth === 0
- ? new DataTexture( view, pixelWidth, pixelHeight )
- : new Data3DTexture( view, pixelWidth, pixelHeight, pixelDepth );
- texture.type = TYPE_MAP[ vkFormat ];
- texture.format = FORMAT_MAP[ vkFormat ];
- texture.encoding = ENCODING_MAP[ vkFormat ] || LinearEncoding;
- texture.needsUpdate = true;
- //
- return Promise.resolve( texture );
- }
- // This file is part of meshoptimizer library and is distributed under the terms of MIT License.
- // Copyright (C) 2016-2020, by Arseny Kapoulkine (arseny.kapoulkine@gmail.com)
- var MeshoptDecoder = (function() {
- "use strict";
- // Built with clang version 11.0.0 (https://github.com/llvm/llvm-project.git 0160ad802e899c2922bc9b29564080c22eb0908c)
- // Built from meshoptimizer 0.14
- var wasm_base = "B9h9z9tFBBBF8fL9gBB9gLaaaaaFa9gEaaaB9gFaFa9gEaaaFaEMcBFFFGGGEIIILF9wFFFLEFBFKNFaFCx/IFMO/LFVK9tv9t9vq95GBt9f9f939h9z9t9f9j9h9s9s9f9jW9vq9zBBp9tv9z9o9v9wW9f9kv9j9v9kv9WvqWv94h919m9mvqBF8Z9tv9z9o9v9wW9f9kv9j9v9kv9J9u9kv94h919m9mvqBGy9tv9z9o9v9wW9f9kv9j9v9kv9J9u9kv949TvZ91v9u9jvBEn9tv9z9o9v9wW9f9kv9j9v9kv69p9sWvq9P9jWBIi9tv9z9o9v9wW9f9kv9j9v9kv69p9sWvq9R919hWBLn9tv9z9o9v9wW9f9kv9j9v9kv69p9sWvq9F949wBKI9z9iqlBOc+x8ycGBM/qQFTa8jUUUUBCU/EBlHL8kUUUUBC9+RKGXAGCFJAI9LQBCaRKAE2BBC+gF9HQBALAEAIJHOAGlAGTkUUUBRNCUoBAG9uC/wgBZHKCUGAKCUG9JyRVAECFJRICBRcGXEXAcAF9PQFAVAFAclAcAVJAF9JyRMGXGXAG9FQBAMCbJHKC9wZRSAKCIrCEJCGrRQANCUGJRfCBRbAIRTEXGXAOATlAQ9PQBCBRISEMATAQJRIGXAS9FQBCBRtCBREEXGXAOAIlCi9PQBCBRISLMANCU/CBJAEJRKGXGXGXGXGXATAECKrJ2BBAtCKZrCEZfIBFGEBMAKhB83EBAKCNJhB83EBSEMAKAI2BIAI2BBHmCKrHYAYCE6HYy86BBAKCFJAICIJAYJHY2BBAmCIrCEZHPAPCE6HPy86BBAKCGJAYAPJHY2BBAmCGrCEZHPAPCE6HPy86BBAKCEJAYAPJHY2BBAmCEZHmAmCE6Hmy86BBAKCIJAYAmJHY2BBAI2BFHmCKrHPAPCE6HPy86BBAKCLJAYAPJHY2BBAmCIrCEZHPAPCE6HPy86BBAKCKJAYAPJHY2BBAmCGrCEZHPAPCE6HPy86BBAKCOJAYAPJHY2BBAmCEZHmAmCE6Hmy86BBAKCNJAYAmJHY2BBAI2BGHmCKrHPAPCE6HPy86BBAKCVJAYAPJHY2BBAmCIrCEZHPAPCE6HPy86BBAKCcJAYAPJHY2BBAmCGrCEZHPAPCE6HPy86BBAKCMJAYAPJHY2BBAmCEZHmAmCE6Hmy86BBAKCSJAYAmJHm2BBAI2BEHICKrHYAYCE6HYy86BBAKCQJAmAYJHm2BBAICIrCEZHYAYCE6HYy86BBAKCfJAmAYJHm2BBAICGrCEZHYAYCE6HYy86BBAKCbJAmAYJHK2BBAICEZHIAICE6HIy86BBAKAIJRISGMAKAI2BNAI2BBHmCIrHYAYCb6HYy86BBAKCFJAICNJAYJHY2BBAmCbZHmAmCb6Hmy86BBAKCGJAYAmJHm2BBAI2BFHYCIrHPAPCb6HPy86BBAKCEJAmAPJHm2BBAYCbZHYAYCb6HYy86BBAKCIJAmAYJHm2BBAI2BGHYCIrHPAPCb6HPy86BBAKCLJAmAPJHm2BBAYCbZHYAYCb6HYy86BBAKCKJAmAYJHm2BBAI2BEHYCIrHPAPCb6HPy86BBAKCOJAmAPJHm2BBAYCbZHYAYCb6HYy86BBAKCNJAmAYJHm2BBAI2BIHYCIrHPAPCb6HPy86BBAKCVJAmAPJHm2BBAYCbZHYAYCb6HYy86BBAKCcJAmAYJHm2BBAI2BLHYCIrHPAPCb6HPy86BBAKCMJAmAPJHm2BBAYCbZHYAYCb6HYy86BBAKCSJAmAYJHm2BBAI2BKHYCIrHPAPCb6HPy86BBAKCQJAmAPJHm2BBAYCbZHYAYCb6HYy86BBAKCfJAmAYJHm2BBAI2BOHICIrHYAYCb6HYy86BBAKCbJAmAYJHK2BBAICbZHIAICb6HIy86BBAKAIJRISFMAKAI8pBB83BBAKCNJAICNJ8pBB83BBAICTJRIMAtCGJRtAECTJHEAS9JQBMMGXAIQBCBRISEMGXAM9FQBANAbJ2BBRtCBRKAfREEXAEANCU/CBJAKJ2BBHTCFrCBATCFZl9zAtJHt86BBAEAGJREAKCFJHKAM9HQBMMAfCFJRfAIRTAbCFJHbAG9HQBMMABAcAG9sJANCUGJAMAG9sTkUUUBpANANCUGJAMCaJAG9sJAGTkUUUBpMAMCBAIyAcJRcAIQBMC9+RKSFMCBC99AOAIlAGCAAGCA9Ly6yRKMALCU/EBJ8kUUUUBAKM+OmFTa8jUUUUBCoFlHL8kUUUUBC9+RKGXAFCE9uHOCtJAI9LQBCaRKAE2BBHNC/wFZC/gF9HQBANCbZHVCF9LQBALCoBJCgFCUFT+JUUUBpALC84Jha83EBALC8wJha83EBALC8oJha83EBALCAJha83EBALCiJha83EBALCTJha83EBALha83ENALha83EBAEAIJC9wJRcAECFJHNAOJRMGXAF9FQBCQCbAVCF6yRSABRECBRVCBRQCBRfCBRICBRKEXGXAMAcuQBC9+RKSEMGXGXAN2BBHOC/vF9LQBALCoBJAOCIrCa9zAKJCbZCEWJHb8oGIRTAb8oGBRtGXAOCbZHbAS9PQBALAOCa9zAIJCbZCGWJ8oGBAVAbyROAb9FRbGXGXAGCG9HQBABAt87FBABCIJAO87FBABCGJAT87FBSFMAEAtjGBAECNJAOjGBAECIJATjGBMAVAbJRVALCoBJAKCEWJHmAOjGBAmATjGIALAICGWJAOjGBALCoBJAKCFJCbZHKCEWJHTAtjGBATAOjGIAIAbJRIAKCFJRKSGMGXGXAbCb6QBAQAbJAbC989zJCFJRQSFMAM1BBHbCgFZROGXGXAbCa9MQBAMCFJRMSFMAM1BFHbCgBZCOWAOCgBZqROGXAbCa9MQBAMCGJRMSFMAM1BGHbCgBZCfWAOqROGXAbCa9MQBAMCEJRMSFMAM1BEHbCgBZCdWAOqROGXAbCa9MQBAMCIJRMSFMAM2BIC8cWAOqROAMCLJRMMAOCFrCBAOCFZl9zAQJRQMGXGXAGCG9HQBABAt87FBABCIJAQ87FBABCGJAT87FBSFMAEAtjGBAECNJAQjGBAECIJATjGBMALCoBJAKCEWJHOAQjGBAOATjGIALAICGWJAQjGBALCoBJAKCFJCbZHKCEWJHOAtjGBAOAQjGIAICFJRIAKCFJRKSFMGXAOCDF9LQBALAIAcAOCbZJ2BBHbCIrHTlCbZCGWJ8oGBAVCFJHtATyROALAIAblCbZCGWJ8oGBAtAT9FHmJHtAbCbZHTyRbAT9FRTGXGXAGCG9HQBABAV87FBABCIJAb87FBABCGJAO87FBSFMAEAVjGBAECNJAbjGBAECIJAOjGBMALAICGWJAVjGBALCoBJAKCEWJHYAOjGBAYAVjGIALAICFJHICbZCGWJAOjGBALCoBJAKCFJCbZCEWJHYAbjGBAYAOjGIALAIAmJCbZHICGWJAbjGBALCoBJAKCGJCbZHKCEWJHOAVjGBAOAbjGIAKCFJRKAIATJRIAtATJRVSFMAVCBAM2BBHYyHTAOC/+F6HPJROAYCbZRtGXGXAYCIrHmQBAOCFJRbSFMAORbALAIAmlCbZCGWJ8oGBROMGXGXAtQBAbCFJRVSFMAbRVALAIAYlCbZCGWJ8oGBRbMGXGXAP9FQBAMCFJRYSFMAM1BFHYCgFZRTGXGXAYCa9MQBAMCGJRYSFMAM1BGHYCgBZCOWATCgBZqRTGXAYCa9MQBAMCEJRYSFMAM1BEHYCgBZCfWATqRTGXAYCa9MQBAMCIJRYSFMAM1BIHYCgBZCdWATqRTGXAYCa9MQBAMCLJRYSFMAMCKJRYAM2BLC8cWATqRTMATCFrCBATCFZl9zAQJHQRTMGXGXAmCb6QBAYRPSFMAY1BBHMCgFZROGXGXAMCa9MQBAYCFJRPSFMAY1BFHMCgBZCOWAOCgBZqROGXAMCa9MQBAYCGJRPSFMAY1BGHMCgBZCfWAOqROGXAMCa9MQBAYCEJRPSFMAY1BEHMCgBZCdWAOqROGXAMCa9MQBAYCIJRPSFMAYCLJRPAY2BIC8cWAOqROMAOCFrCBAOCFZl9zAQJHQROMGXGXAtCb6QBAPRMSFMAP1BBHMCgFZRbGXGXAMCa9MQBAPCFJRMSFMAP1BFHMCgBZCOWAbCgBZqRbGXAMCa9MQBAPCGJRMSFMAP1BGHMCgBZCfWAbqRbGXAMCa9MQBAPCEJRMSFMAP1BEHMCgBZCdWAbqRbGXAMCa9MQBAPCIJRMSFMAPCLJRMAP2BIC8cWAbqRbMAbCFrCBAbCFZl9zAQJHQRbMGXGXAGCG9HQBABAT87FBABCIJAb87FBABCGJAO87FBSFMAEATjGBAECNJAbjGBAECIJAOjGBMALCoBJAKCEWJHYAOjGBAYATjGIALAICGWJATjGBALCoBJAKCFJCbZCEWJHYAbjGBAYAOjGIALAICFJHICbZCGWJAOjGBALCoBJAKCGJCbZCEWJHOATjGBAOAbjGIALAIAm9FAmCb6qJHICbZCGWJAbjGBAIAt9FAtCb6qJRIAKCEJRKMANCFJRNABCKJRBAECSJREAKCbZRKAICbZRIAfCEJHfAF9JQBMMCBC99AMAc6yRKMALCoFJ8kUUUUBAKM/tIFGa8jUUUUBCTlRLC9+RKGXAFCLJAI9LQBCaRKAE2BBC/+FZC/QF9HQBALhB83ENAECFJRKAEAIJC98JREGXAF9FQBGXAGCG6QBEXGXAKAE9JQBC9+bMAK1BBHGCgFZRIGXGXAGCa9MQBAKCFJRKSFMAK1BFHGCgBZCOWAICgBZqRIGXAGCa9MQBAKCGJRKSFMAK1BGHGCgBZCfWAIqRIGXAGCa9MQBAKCEJRKSFMAK1BEHGCgBZCdWAIqRIGXAGCa9MQBAKCIJRKSFMAK2BIC8cWAIqRIAKCLJRKMALCNJAICFZCGWqHGAICGrCBAICFrCFZl9zAG8oGBJHIjGBABAIjGBABCIJRBAFCaJHFQBSGMMEXGXAKAE9JQBC9+bMAK1BBHGCgFZRIGXGXAGCa9MQBAKCFJRKSFMAK1BFHGCgBZCOWAICgBZqRIGXAGCa9MQBAKCGJRKSFMAK1BGHGCgBZCfWAIqRIGXAGCa9MQBAKCEJRKSFMAK1BEHGCgBZCdWAIqRIGXAGCa9MQBAKCIJRKSFMAK2BIC8cWAIqRIAKCLJRKMABAICGrCBAICFrCFZl9zALCNJAICFZCGWqHI8oGBJHG87FBAIAGjGBABCGJRBAFCaJHFQBMMCBC99AKAE6yRKMAKM+lLKFaF99GaG99FaG99GXGXAGCI9HQBAF9FQFEXGXGX9DBBB8/9DBBB+/ABCGJHG1BB+yAB1BBHE+yHI+L+TABCFJHL1BBHK+yHO+L+THN9DBBBB9gHVyAN9DBB/+hANAN+U9DBBBBANAVyHcAc+MHMAECa3yAI+SHIAI+UAcAMAKCa3yAO+SHcAc+U+S+S+R+VHO+U+SHN+L9DBBB9P9d9FQBAN+oRESFMCUUUU94REMAGAE86BBGXGX9DBBB8/9DBBB+/Ac9DBBBB9gyAcAO+U+SHN+L9DBBB9P9d9FQBAN+oRGSFMCUUUU94RGMALAG86BBGXGX9DBBB8/9DBBB+/AI9DBBBB9gyAIAO+U+SHN+L9DBBB9P9d9FQBAN+oRGSFMCUUUU94RGMABAG86BBABCIJRBAFCaJHFQBSGMMAF9FQBEXGXGX9DBBB8/9DBBB+/ABCIJHG8uFB+yAB8uFBHE+yHI+L+TABCGJHL8uFBHK+yHO+L+THN9DBBBB9gHVyAN9DB/+g6ANAN+U9DBBBBANAVyHcAc+MHMAECa3yAI+SHIAI+UAcAMAKCa3yAO+SHcAc+U+S+S+R+VHO+U+SHN+L9DBBB9P9d9FQBAN+oRESFMCUUUU94REMAGAE87FBGXGX9DBBB8/9DBBB+/Ac9DBBBB9gyAcAO+U+SHN+L9DBBB9P9d9FQBAN+oRGSFMCUUUU94RGMALAG87FBGXGX9DBBB8/9DBBB+/AI9DBBBB9gyAIAO+U+SHN+L9DBBB9P9d9FQBAN+oRGSFMCUUUU94RGMABAG87FBABCNJRBAFCaJHFQBMMM/SEIEaE99EaF99GXAF9FQBCBREABRIEXGXGX9D/zI818/AICKJ8uFBHLCEq+y+VHKAI8uFB+y+UHO9DB/+g6+U9DBBB8/9DBBB+/AO9DBBBB9gy+SHN+L9DBBB9P9d9FQBAN+oRVSFMCUUUU94RVMAICIJ8uFBRcAICGJ8uFBRMABALCFJCEZAEqCFWJAV87FBGXGXAKAM+y+UHN9DB/+g6+U9DBBB8/9DBBB+/AN9DBBBB9gy+SHS+L9DBBB9P9d9FQBAS+oRMSFMCUUUU94RMMABALCGJCEZAEqCFWJAM87FBGXGXAKAc+y+UHK9DB/+g6+U9DBBB8/9DBBB+/AK9DBBBB9gy+SHS+L9DBBB9P9d9FQBAS+oRcSFMCUUUU94RcMABALCaJCEZAEqCFWJAc87FBGXGX9DBBU8/AOAO+U+TANAN+U+TAKAK+U+THO9DBBBBAO9DBBBB9gy+R9DB/+g6+U9DBBB8/+SHO+L9DBBB9P9d9FQBAO+oRcSFMCUUUU94RcMABALCEZAEqCFWJAc87FBAICNJRIAECIJREAFCaJHFQBMMM9JBGXAGCGrAF9sHF9FQBEXABAB8oGBHGCNWCN91+yAGCi91CnWCUUU/8EJ+++U84GBABCIJRBAFCaJHFQBMMM9TFEaCBCB8oGUkUUBHFABCEJC98ZJHBjGUkUUBGXGXAB8/BCTWHGuQBCaREABAGlCggEJCTrXBCa6QFMAFREMAEM/lFFFaGXGXAFABqCEZ9FQBABRESFMGXGXAGCT9PQBABRESFMABREEXAEAF8oGBjGBAECIJAFCIJ8oGBjGBAECNJAFCNJ8oGBjGBAECSJAFCSJ8oGBjGBAECTJREAFCTJRFAGC9wJHGCb9LQBMMAGCI9JQBEXAEAF8oGBjGBAFCIJRFAECIJREAGC98JHGCE9LQBMMGXAG9FQBEXAEAF2BB86BBAECFJREAFCFJRFAGCaJHGQBMMABMoFFGaGXGXABCEZ9FQBABRESFMAFCgFZC+BwsN9sRIGXGXAGCT9PQBABRESFMABREEXAEAIjGBAECSJAIjGBAECNJAIjGBAECIJAIjGBAECTJREAGC9wJHGCb9LQBMMAGCI9JQBEXAEAIjGBAECIJREAGC98JHGCE9LQBMMGXAG9FQBEXAEAF86BBAECFJREAGCaJHGQBMMABMMMFBCUNMIT9kBB";
- var wasm_simd = "B9h9z9tFBBBFiI9gBB9gLaaaaaFa9gEaaaB9gFaFaEMcBBFBFFGGGEILF9wFFFLEFBFKNFaFCx/aFMO/LFVK9tv9t9vq95GBt9f9f939h9z9t9f9j9h9s9s9f9jW9vq9zBBp9tv9z9o9v9wW9f9kv9j9v9kv9WvqWv94h919m9mvqBG8Z9tv9z9o9v9wW9f9kv9j9v9kv9J9u9kv94h919m9mvqBIy9tv9z9o9v9wW9f9kv9j9v9kv9J9u9kv949TvZ91v9u9jvBLn9tv9z9o9v9wW9f9kv9j9v9kv69p9sWvq9P9jWBKi9tv9z9o9v9wW9f9kv9j9v9kv69p9sWvq9R919hWBOn9tv9z9o9v9wW9f9kv9j9v9kv69p9sWvq9F949wBNI9z9iqlBVc+N9IcIBTEM9+FLa8jUUUUBCTlRBCBRFEXCBRGCBREEXABCNJAGJAECUaAFAGrCFZHIy86BBAEAIJREAGCFJHGCN9HQBMAFCx+YUUBJAE86BBAFCEWCxkUUBJAB8pEN83EBAFCFJHFCUG9HQBMMk8lLbaE97F9+FaL978jUUUUBCU/KBlHL8kUUUUBC9+RKGXAGCFJAI9LQBCaRKAE2BBC+gF9HQBALAEAIJHOAGlAG/8cBBCUoBAG9uC/wgBZHKCUGAKCUG9JyRNAECFJRKCBRVGXEXAVAF9PQFANAFAVlAVANJAF9JyRcGXGXAG9FQBAcCbJHIC9wZHMCE9sRSAMCFWRQAICIrCEJCGrRfCBRbEXAKRTCBRtGXEXGXAOATlAf9PQBCBRKSLMALCU/CBJAtAM9sJRmATAfJRKCBREGXAMCoB9JQBAOAKlC/gB9JQBCBRIEXAmAIJREGXGXGXGXGXATAICKrJ2BBHYCEZfIBFGEBMAECBDtDMIBSEMAEAKDBBIAKDBBBHPCID+MFAPDQBTFtGmEYIPLdKeOnHPCGD+MFAPDQBTFtGmEYIPLdKeOnC0+G+MiDtD9OHdCEDbD8jHPAPDQBFGENVcMILKOSQfbHeD8dBh+BsxoxoUwN0AeD8dFhxoUwkwk+gUa0sHnhTkAnsHnhNkAnsHn7CgFZHiCEWCxkUUBJDBEBAiCx+YUUBJDBBBHeAeDQBBBBBBBBBBBBBBBBAnhAk7CgFZHiCEWCxkUUBJDBEBD9uDQBFGEILKOTtmYPdenDfAdAPD9SDMIBAKCIJAeDeBJAiCx+YUUBJ2BBJRKSGMAEAKDBBNAKDBBBHPCID+MFAPDQBTFtGmEYIPLdKeOnC+P+e+8/4BDtD9OHdCbDbD8jHPAPDQBFGENVcMILKOSQfbHeD8dBh+BsxoxoUwN0AeD8dFhxoUwkwk+gUa0sHnhTkAnsHnhNkAnsHn7CgFZHiCEWCxkUUBJDBEBAiCx+YUUBJDBBBHeAeDQBBBBBBBBBBBBBBBBAnhAk7CgFZHiCEWCxkUUBJDBEBD9uDQBFGEILKOTtmYPdenDfAdAPD9SDMIBAKCNJAeDeBJAiCx+YUUBJ2BBJRKSFMAEAKDBBBDMIBAKCTJRKMGXGXGXGXGXAYCGrCEZfIBFGEBMAECBDtDMITSEMAEAKDBBIAKDBBBHPCID+MFAPDQBTFtGmEYIPLdKeOnHPCGD+MFAPDQBTFtGmEYIPLdKeOnC0+G+MiDtD9OHdCEDbD8jHPAPDQBFGENVcMILKOSQfbHeD8dBh+BsxoxoUwN0AeD8dFhxoUwkwk+gUa0sHnhTkAnsHnhNkAnsHn7CgFZHiCEWCxkUUBJDBEBAiCx+YUUBJDBBBHeAeDQBBBBBBBBBBBBBBBBAnhAk7CgFZHiCEWCxkUUBJDBEBD9uDQBFGEILKOTtmYPdenDfAdAPD9SDMITAKCIJAeDeBJAiCx+YUUBJ2BBJRKSGMAEAKDBBNAKDBBBHPCID+MFAPDQBTFtGmEYIPLdKeOnC+P+e+8/4BDtD9OHdCbDbD8jHPAPDQBFGENVcMILKOSQfbHeD8dBh+BsxoxoUwN0AeD8dFhxoUwkwk+gUa0sHnhTkAnsHnhNkAnsHn7CgFZHiCEWCxkUUBJDBEBAiCx+YUUBJDBBBHeAeDQBBBBBBBBBBBBBBBBAnhAk7CgFZHiCEWCxkUUBJDBEBD9uDQBFGEILKOTtmYPdenDfAdAPD9SDMITAKCNJAeDeBJAiCx+YUUBJ2BBJRKSFMAEAKDBBBDMITAKCTJRKMGXGXGXGXGXAYCIrCEZfIBFGEBMAECBDtDMIASEMAEAKDBBIAKDBBBHPCID+MFAPDQBTFtGmEYIPLdKeOnHPCGD+MFAPDQBTFtGmEYIPLdKeOnC0+G+MiDtD9OHdCEDbD8jHPAPDQBFGENVcMILKOSQfbHeD8dBh+BsxoxoUwN0AeD8dFhxoUwkwk+gUa0sHnhTkAnsHnhNkAnsHn7CgFZHiCEWCxkUUBJDBEBAiCx+YUUBJDBBBHeAeDQBBBBBBBBBBBBBBBBAnhAk7CgFZHiCEWCxkUUBJDBEBD9uDQBFGEILKOTtmYPdenDfAdAPD9SDMIAAKCIJAeDeBJAiCx+YUUBJ2BBJRKSGMAEAKDBBNAKDBBBHPCID+MFAPDQBTFtGmEYIPLdKeOnC+P+e+8/4BDtD9OHdCbDbD8jHPAPDQBFGENVcMILKOSQfbHeD8dBh+BsxoxoUwN0AeD8dFhxoUwkwk+gUa0sHnhTkAnsHnhNkAnsHn7CgFZHiCEWCxkUUBJDBEBAiCx+YUUBJDBBBHeAeDQBBBBBBBBBBBBBBBBAnhAk7CgFZHiCEWCxkUUBJDBEBD9uDQBFGEILKOTtmYPdenDfAdAPD9SDMIAAKCNJAeDeBJAiCx+YUUBJ2BBJRKSFMAEAKDBBBDMIAAKCTJRKMGXGXGXGXGXAYCKrfIBFGEBMAECBDtDMI8wSEMAEAKDBBIAKDBBBHPCID+MFAPDQBTFtGmEYIPLdKeOnHPCGD+MFAPDQBTFtGmEYIPLdKeOnC0+G+MiDtD9OHdCEDbD8jHPAPDQBFGENVcMILKOSQfbHeD8dBh+BsxoxoUwN0AeD8dFhxoUwkwk+gUa0sHnhTkAnsHnhNkAnsHn7CgFZHYCEWCxkUUBJDBEBAYCx+YUUBJDBBBHeAeDQBBBBBBBBBBBBBBBBAnhAk7CgFZHYCEWCxkUUBJDBEBD9uDQBFGEILKOTtmYPdenDfAdAPD9SDMI8wAKCIJAeDeBJAYCx+YUUBJ2BBJRKSGMAEAKDBBNAKDBBBHPCID+MFAPDQBTFtGmEYIPLdKeOnC+P+e+8/4BDtD9OHdCbDbD8jHPAPDQBFGENVcMILKOSQfbHeD8dBh+BsxoxoUwN0AeD8dFhxoUwkwk+gUa0sHnhTkAnsHnhNkAnsHn7CgFZHYCEWCxkUUBJDBEBAYCx+YUUBJDBBBHeAeDQBBBBBBBBBBBBBBBBAnhAk7CgFZHYCEWCxkUUBJDBEBD9uDQBFGEILKOTtmYPdenDfAdAPD9SDMI8wAKCNJAeDeBJAYCx+YUUBJ2BBJRKSFMAEAKDBBBDMI8wAKCTJRKMAICoBJREAICUFJAM9LQFAERIAOAKlC/fB9LQBMMGXAEAM9PQBAECErRIEXGXAOAKlCi9PQBCBRKSOMAmAEJRYGXGXGXGXGXATAECKrJ2BBAICKZrCEZfIBFGEBMAYCBDtDMIBSEMAYAKDBBIAKDBBBHPCID+MFAPDQBTFtGmEYIPLdKeOnHPCGD+MFAPDQBTFtGmEYIPLdKeOnC0+G+MiDtD9OHdCEDbD8jHPAPDQBFGENVcMILKOSQfbHeD8dBh+BsxoxoUwN0AeD8dFhxoUwkwk+gUa0sHnhTkAnsHnhNkAnsHn7CgFZHiCEWCxkUUBJDBEBAiCx+YUUBJDBBBHeAeDQBBBBBBBBBBBBBBBBAnhAk7CgFZHiCEWCxkUUBJDBEBD9uDQBFGEILKOTtmYPdenDfAdAPD9SDMIBAKCIJAeDeBJAiCx+YUUBJ2BBJRKSGMAYAKDBBNAKDBBBHPCID+MFAPDQBTFtGmEYIPLdKeOnC+P+e+8/4BDtD9OHdCbDbD8jHPAPDQBFGENVcMILKOSQfbHeD8dBh+BsxoxoUwN0AeD8dFhxoUwkwk+gUa0sHnhTkAnsHnhNkAnsHn7CgFZHiCEWCxkUUBJDBEBAiCx+YUUBJDBBBHeAeDQBBBBBBBBBBBBBBBBAnhAk7CgFZHiCEWCxkUUBJDBEBD9uDQBFGEILKOTtmYPdenDfAdAPD9SDMIBAKCNJAeDeBJAiCx+YUUBJ2BBJRKSFMAYAKDBBBDMIBAKCTJRKMAICGJRIAECTJHEAM9JQBMMGXAK9FQBAKRTAtCFJHtCI6QGSFMMCBRKSEMGXAM9FQBALCUGJAbJREALAbJDBGBReCBRYEXAEALCU/CBJAYJHIDBIBHdCFD9tAdCFDbHPD9OD9hD9RHdAIAMJDBIBH8ZCFD9tA8ZAPD9OD9hD9RH8ZDQBTFtGmEYIPLdKeOnHpAIAQJDBIBHyCFD9tAyAPD9OD9hD9RHyAIASJDBIBH8cCFD9tA8cAPD9OD9hD9RH8cDQBTFtGmEYIPLdKeOnH8dDQBFTtGEmYILPdKOenHPAPDQBFGEBFGEBFGEBFGEAeD9uHeDyBjGBAEAGJHIAeAPAPDQILKOILKOILKOILKOD9uHeDyBjGBAIAGJHIAeAPAPDQNVcMNVcMNVcMNVcMD9uHeDyBjGBAIAGJHIAeAPAPDQSQfbSQfbSQfbSQfbD9uHeDyBjGBAIAGJHIAeApA8dDQNVi8ZcMpySQ8c8dfb8e8fHPAPDQBFGEBFGEBFGEBFGED9uHeDyBjGBAIAGJHIAeAPAPDQILKOILKOILKOILKOD9uHeDyBjGBAIAGJHIAeAPAPDQNVcMNVcMNVcMNVcMD9uHeDyBjGBAIAGJHIAeAPAPDQSQfbSQfbSQfbSQfbD9uHeDyBjGBAIAGJHIAeAdA8ZDQNiV8ZcpMyS8cQ8df8eb8fHdAyA8cDQNiV8ZcpMyS8cQ8df8eb8fH8ZDQBFTtGEmYILPdKOenHPAPDQBFGEBFGEBFGEBFGED9uHeDyBjGBAIAGJHIAeAPAPDQILKOILKOILKOILKOD9uHeDyBjGBAIAGJHIAeAPAPDQNVcMNVcMNVcMNVcMD9uHeDyBjGBAIAGJHIAeAPAPDQSQfbSQfbSQfbSQfbD9uHeDyBjGBAIAGJHIAeAdA8ZDQNVi8ZcMpySQ8c8dfb8e8fHPAPDQBFGEBFGEBFGEBFGED9uHeDyBjGBAIAGJHIAeAPAPDQILKOILKOILKOILKOD9uHeDyBjGBAIAGJHIAeAPAPDQNVcMNVcMNVcMNVcMD9uHeDyBjGBAIAGJHIAeAPAPDQSQfbSQfbSQfbSQfbD9uHeDyBjGBAIAGJREAYCTJHYAM9JQBMMAbCIJHbAG9JQBMMABAVAG9sJALCUGJAcAG9s/8cBBALALCUGJAcCaJAG9sJAG/8cBBMAcCBAKyAVJRVAKQBMC9+RKSFMCBC99AOAKlAGCAAGCA9Ly6yRKMALCU/KBJ8kUUUUBAKMNBT+BUUUBM+KmFTa8jUUUUBCoFlHL8kUUUUBC9+RKGXAFCE9uHOCtJAI9LQBCaRKAE2BBHNC/wFZC/gF9HQBANCbZHVCF9LQBALCoBJCgFCUF/8MBALC84Jha83EBALC8wJha83EBALC8oJha83EBALCAJha83EBALCiJha83EBALCTJha83EBALha83ENALha83EBAEAIJC9wJRcAECFJHNAOJRMGXAF9FQBCQCbAVCF6yRSABRECBRVCBRQCBRfCBRICBRKEXGXAMAcuQBC9+RKSEMGXGXAN2BBHOC/vF9LQBALCoBJAOCIrCa9zAKJCbZCEWJHb8oGIRTAb8oGBRtGXAOCbZHbAS9PQBALAOCa9zAIJCbZCGWJ8oGBAVAbyROAb9FRbGXGXAGCG9HQBABAt87FBABCIJAO87FBABCGJAT87FBSFMAEAtjGBAECNJAOjGBAECIJATjGBMAVAbJRVALCoBJAKCEWJHmAOjGBAmATjGIALAICGWJAOjGBALCoBJAKCFJCbZHKCEWJHTAtjGBATAOjGIAIAbJRIAKCFJRKSGMGXGXAbCb6QBAQAbJAbC989zJCFJRQSFMAM1BBHbCgFZROGXGXAbCa9MQBAMCFJRMSFMAM1BFHbCgBZCOWAOCgBZqROGXAbCa9MQBAMCGJRMSFMAM1BGHbCgBZCfWAOqROGXAbCa9MQBAMCEJRMSFMAM1BEHbCgBZCdWAOqROGXAbCa9MQBAMCIJRMSFMAM2BIC8cWAOqROAMCLJRMMAOCFrCBAOCFZl9zAQJRQMGXGXAGCG9HQBABAt87FBABCIJAQ87FBABCGJAT87FBSFMAEAtjGBAECNJAQjGBAECIJATjGBMALCoBJAKCEWJHOAQjGBAOATjGIALAICGWJAQjGBALCoBJAKCFJCbZHKCEWJHOAtjGBAOAQjGIAICFJRIAKCFJRKSFMGXAOCDF9LQBALAIAcAOCbZJ2BBHbCIrHTlCbZCGWJ8oGBAVCFJHtATyROALAIAblCbZCGWJ8oGBAtAT9FHmJHtAbCbZHTyRbAT9FRTGXGXAGCG9HQBABAV87FBABCIJAb87FBABCGJAO87FBSFMAEAVjGBAECNJAbjGBAECIJAOjGBMALAICGWJAVjGBALCoBJAKCEWJHYAOjGBAYAVjGIALAICFJHICbZCGWJAOjGBALCoBJAKCFJCbZCEWJHYAbjGBAYAOjGIALAIAmJCbZHICGWJAbjGBALCoBJAKCGJCbZHKCEWJHOAVjGBAOAbjGIAKCFJRKAIATJRIAtATJRVSFMAVCBAM2BBHYyHTAOC/+F6HPJROAYCbZRtGXGXAYCIrHmQBAOCFJRbSFMAORbALAIAmlCbZCGWJ8oGBROMGXGXAtQBAbCFJRVSFMAbRVALAIAYlCbZCGWJ8oGBRbMGXGXAP9FQBAMCFJRYSFMAM1BFHYCgFZRTGXGXAYCa9MQBAMCGJRYSFMAM1BGHYCgBZCOWATCgBZqRTGXAYCa9MQBAMCEJRYSFMAM1BEHYCgBZCfWATqRTGXAYCa9MQBAMCIJRYSFMAM1BIHYCgBZCdWATqRTGXAYCa9MQBAMCLJRYSFMAMCKJRYAM2BLC8cWATqRTMATCFrCBATCFZl9zAQJHQRTMGXGXAmCb6QBAYRPSFMAY1BBHMCgFZROGXGXAMCa9MQBAYCFJRPSFMAY1BFHMCgBZCOWAOCgBZqROGXAMCa9MQBAYCGJRPSFMAY1BGHMCgBZCfWAOqROGXAMCa9MQBAYCEJRPSFMAY1BEHMCgBZCdWAOqROGXAMCa9MQBAYCIJRPSFMAYCLJRPAY2BIC8cWAOqROMAOCFrCBAOCFZl9zAQJHQROMGXGXAtCb6QBAPRMSFMAP1BBHMCgFZRbGXGXAMCa9MQBAPCFJRMSFMAP1BFHMCgBZCOWAbCgBZqRbGXAMCa9MQBAPCGJRMSFMAP1BGHMCgBZCfWAbqRbGXAMCa9MQBAPCEJRMSFMAP1BEHMCgBZCdWAbqRbGXAMCa9MQBAPCIJRMSFMAPCLJRMAP2BIC8cWAbqRbMAbCFrCBAbCFZl9zAQJHQRbMGXGXAGCG9HQBABAT87FBABCIJAb87FBABCGJAO87FBSFMAEATjGBAECNJAbjGBAECIJAOjGBMALCoBJAKCEWJHYAOjGBAYATjGIALAICGWJATjGBALCoBJAKCFJCbZCEWJHYAbjGBAYAOjGIALAICFJHICbZCGWJAOjGBALCoBJAKCGJCbZCEWJHOATjGBAOAbjGIALAIAm9FAmCb6qJHICbZCGWJAbjGBAIAt9FAtCb6qJRIAKCEJRKMANCFJRNABCKJRBAECSJREAKCbZRKAICbZRIAfCEJHfAF9JQBMMCBC99AMAc6yRKMALCoFJ8kUUUUBAKM/tIFGa8jUUUUBCTlRLC9+RKGXAFCLJAI9LQBCaRKAE2BBC/+FZC/QF9HQBALhB83ENAECFJRKAEAIJC98JREGXAF9FQBGXAGCG6QBEXGXAKAE9JQBC9+bMAK1BBHGCgFZRIGXGXAGCa9MQBAKCFJRKSFMAK1BFHGCgBZCOWAICgBZqRIGXAGCa9MQBAKCGJRKSFMAK1BGHGCgBZCfWAIqRIGXAGCa9MQBAKCEJRKSFMAK1BEHGCgBZCdWAIqRIGXAGCa9MQBAKCIJRKSFMAK2BIC8cWAIqRIAKCLJRKMALCNJAICFZCGWqHGAICGrCBAICFrCFZl9zAG8oGBJHIjGBABAIjGBABCIJRBAFCaJHFQBSGMMEXGXAKAE9JQBC9+bMAK1BBHGCgFZRIGXGXAGCa9MQBAKCFJRKSFMAK1BFHGCgBZCOWAICgBZqRIGXAGCa9MQBAKCGJRKSFMAK1BGHGCgBZCfWAIqRIGXAGCa9MQBAKCEJRKSFMAK1BEHGCgBZCdWAIqRIGXAGCa9MQBAKCIJRKSFMAK2BIC8cWAIqRIAKCLJRKMABAICGrCBAICFrCFZl9zALCNJAICFZCGWqHI8oGBJHG87FBAIAGjGBABCGJRBAFCaJHFQBMMCBC99AKAE6yRKMAKM/dLEK97FaF97GXGXAGCI9HQBAF9FQFCBRGEXABABDBBBHECiD+rFCiD+sFD/6FHIAECND+rFCiD+sFD/6FAID/gFAECTD+rFCiD+sFD/6FHLD/gFD/kFD/lFHKCBDtD+2FHOAICUUUU94DtHND9OD9RD/kFHI9DBB/+hDYAIAID/mFAKAKD/mFALAOALAND9OD9RD/kFHIAID/mFD/kFD/kFD/jFD/nFHLD/mF9DBBX9LDYHOD/kFCgFDtD9OAECUUU94DtD9OD9QAIALD/mFAOD/kFCND+rFCU/+EDtD9OD9QAKALD/mFAOD/kFCTD+rFCUU/8ODtD9OD9QDMBBABCTJRBAGCIJHGAF9JQBSGMMAF9FQBCBRGEXABCTJHVAVDBBBHECBDtHOCUU98D8cFCUU98D8cEHND9OABDBBBHKAEDQILKOSQfbPden8c8d8e8fCggFDtD9OD/6FAKAEDQBFGENVcMTtmYi8ZpyHECTD+sFD/6FHID/gFAECTD+rFCTD+sFD/6FHLD/gFD/kFD/lFHE9DB/+g6DYALAEAOD+2FHOALCUUUU94DtHcD9OD9RD/kFHLALD/mFAEAED/mFAIAOAIAcD9OD9RD/kFHEAED/mFD/kFD/kFD/jFD/nFHID/mF9DBBX9LDYHOD/kFCTD+rFALAID/mFAOD/kFCggEDtD9OD9QHLAEAID/mFAOD/kFCaDbCBDnGCBDnECBDnKCBDnOCBDncCBDnMCBDnfCBDnbD9OHEDQNVi8ZcMpySQ8c8dfb8e8fD9QDMBBABAKAND9OALAEDQBFTtGEmYILPdKOenD9QDMBBABCAJRBAGCIJHGAF9JQBMMM/hEIGaF97FaL978jUUUUBCTlREGXAF9FQBCBRIEXAEABDBBBHLABCTJHKDBBBHODQILKOSQfbPden8c8d8e8fHNCTD+sFHVCID+rFDMIBAB9DBBU8/DY9D/zI818/DYAVCEDtD9QD/6FD/nFHVALAODQBFGENVcMTtmYi8ZpyHLCTD+rFCTD+sFD/6FD/mFHOAOD/mFAVALCTD+sFD/6FD/mFHcAcD/mFAVANCTD+rFCTD+sFD/6FD/mFHNAND/mFD/kFD/kFD/lFCBDtD+4FD/jF9DB/+g6DYHVD/mF9DBBX9LDYHLD/kFCggEDtHMD9OAcAVD/mFALD/kFCTD+rFD9QHcANAVD/mFALD/kFCTD+rFAOAVD/mFALD/kFAMD9OD9QHVDQBFTtGEmYILPdKOenHLD8dBAEDBIBDyB+t+J83EBABCNJALD8dFAEDBIBDyF+t+J83EBAKAcAVDQNVi8ZcMpySQ8c8dfb8e8fHVD8dBAEDBIBDyG+t+J83EBABCiJAVD8dFAEDBIBDyE+t+J83EBABCAJRBAICIJHIAF9JQBMMM9jFF97GXAGCGrAF9sHG9FQBCBRFEXABABDBBBHECND+rFCND+sFD/6FAECiD+sFCnD+rFCUUU/8EDtD+uFD/mFDMBBABCTJRBAFCIJHFAG9JQBMMM9TFEaCBCB8oGUkUUBHFABCEJC98ZJHBjGUkUUBGXGXAB8/BCTWHGuQBCaREABAGlCggEJCTrXBCa6QFMAFREMAEMMMFBCUNMIT9tBB";
- // Uses bulk-memory and simd extensions
- var detector = new Uint8Array([0,97,115,109,1,0,0,0,1,4,1,96,0,0,3,3,2,0,0,5,3,1,0,1,12,1,0,10,22,2,12,0,65,0,65,0,65,0,252,10,0,0,11,7,0,65,0,253,15,26,11]);
- // Used to unpack wasm
- var wasmpack = new Uint8Array([32,0,65,253,3,1,2,34,4,106,6,5,11,8,7,20,13,33,12,16,128,9,116,64,19,113,127,15,10,21,22,14,255,66,24,54,136,107,18,23,192,26,114,118,132,17,77,101,130,144,27,87,131,44,45,74,156,154,70,167]);
- if (typeof WebAssembly !== 'object') {
- // This module requires WebAssembly to function
- return {
- supported: false,
- };
- }
- var wasm = wasm_base;
- if (WebAssembly.validate(detector)) {
- wasm = wasm_simd;
- console.log("Warning: meshopt_decoder is using experimental SIMD support");
- }
- var instance;
- var promise =
- WebAssembly.instantiate(unpack(wasm), {})
- .then(function(result) {
- instance = result.instance;
- instance.exports.__wasm_call_ctors();
- });
- function unpack(data) {
- var result = new Uint8Array(data.length);
- for (var i = 0; i < data.length; ++i) {
- var ch = data.charCodeAt(i);
- result[i] = ch > 96 ? ch - 71 : ch > 64 ? ch - 65 : ch > 47 ? ch + 4 : ch > 46 ? 63 : 62;
- }
- var write = 0;
- for (var i = 0; i < data.length; ++i) {
- result[write++] = (result[i] < 60) ? wasmpack[result[i]] : (result[i] - 60) * 64 + result[++i];
- }
- return result.buffer.slice(0, write);
- }
- function decode(fun, target, count, size, source, filter) {
- var sbrk = instance.exports.sbrk;
- var count4 = (count + 3) & ~3; // pad for SIMD filter
- var tp = sbrk(count4 * size);
- var sp = sbrk(source.length);
- var heap = new Uint8Array(instance.exports.memory.buffer);
- heap.set(source, sp);
- var res = fun(tp, count, size, sp, source.length);
- if (res == 0 && filter) {
- filter(tp, count4, size);
- }
- target.set(heap.subarray(tp, tp + count * size));
- sbrk(tp - sbrk(0));
- if (res != 0) {
- throw new Error("Malformed buffer data: " + res);
- }
- };
- var filters = {
- // legacy index-based enums for glTF
- 0: "",
- 1: "meshopt_decodeFilterOct",
- 2: "meshopt_decodeFilterQuat",
- 3: "meshopt_decodeFilterExp",
- // string-based enums for glTF
- NONE: "",
- OCTAHEDRAL: "meshopt_decodeFilterOct",
- QUATERNION: "meshopt_decodeFilterQuat",
- EXPONENTIAL: "meshopt_decodeFilterExp",
- };
- var decoders = {
- // legacy index-based enums for glTF
- 0: "meshopt_decodeVertexBuffer",
- 1: "meshopt_decodeIndexBuffer",
- 2: "meshopt_decodeIndexSequence",
- // string-based enums for glTF
- ATTRIBUTES: "meshopt_decodeVertexBuffer",
- TRIANGLES: "meshopt_decodeIndexBuffer",
- INDICES: "meshopt_decodeIndexSequence",
- };
- return {
- ready: promise,
- supported: true,
- decodeVertexBuffer: function(target, count, size, source, filter) {
- decode(instance.exports.meshopt_decodeVertexBuffer, target, count, size, source, instance.exports[filters[filter]]);
- },
- decodeIndexBuffer: function(target, count, size, source) {
- decode(instance.exports.meshopt_decodeIndexBuffer, target, count, size, source);
- },
- decodeIndexSequence: function(target, count, size, source) {
- decode(instance.exports.meshopt_decodeIndexSequence, target, count, size, source);
- },
- decodeGltfBuffer: function(target, count, size, source, mode, filter) {
- decode(instance.exports[decoders[mode]], target, count, size, source, instance.exports[filters[filter]]);
- }
- };
- })();
- var DDSLoader = function ( manager ) {
- CompressedTextureLoader.call( this, manager );
- };
- DDSLoader.prototype = Object.assign( Object.create( CompressedTextureLoader.prototype ), {
- constructor: DDSLoader,
- parse: function ( buffer, loadMipmaps ) {
- var dds = { mipmaps: [], width: 0, height: 0, format: null, mipmapCount: 1 };
- // Adapted from @toji's DDS utils
- // https://github.com/toji/webgl-texture-utils/blob/master/texture-util/dds.js
- // All values and structures referenced from:
- // http://msdn.microsoft.com/en-us/library/bb943991.aspx/
- var DDS_MAGIC = 0x20534444;
- // var DDSD_CAPS = 0x1;
- // var DDSD_HEIGHT = 0x2;
- // var DDSD_WIDTH = 0x4;
- // var DDSD_PITCH = 0x8;
- // var DDSD_PIXELFORMAT = 0x1000;
- var DDSD_MIPMAPCOUNT = 0x20000;
- // var DDSD_LINEARSIZE = 0x80000;
- // var DDSD_DEPTH = 0x800000;
- // var DDSCAPS_COMPLEX = 0x8;
- // var DDSCAPS_MIPMAP = 0x400000;
- // var DDSCAPS_TEXTURE = 0x1000;
- var DDSCAPS2_CUBEMAP = 0x200;
- var DDSCAPS2_CUBEMAP_POSITIVEX = 0x400;
- var DDSCAPS2_CUBEMAP_NEGATIVEX = 0x800;
- var DDSCAPS2_CUBEMAP_POSITIVEY = 0x1000;
- var DDSCAPS2_CUBEMAP_NEGATIVEY = 0x2000;
- var DDSCAPS2_CUBEMAP_POSITIVEZ = 0x4000;
- var DDSCAPS2_CUBEMAP_NEGATIVEZ = 0x8000;
- // var DDSCAPS2_VOLUME = 0x200000;
- // var DDPF_ALPHAPIXELS = 0x1;
- // var DDPF_ALPHA = 0x2;
- var DDPF_FOURCC = 0x4;
- // var DDPF_RGB = 0x40;
- // var DDPF_YUV = 0x200;
- // var DDPF_LUMINANCE = 0x20000;
- function fourCCToInt32( value ) {
- return value.charCodeAt( 0 ) +
- ( value.charCodeAt( 1 ) << 8 ) +
- ( value.charCodeAt( 2 ) << 16 ) +
- ( value.charCodeAt( 3 ) << 24 );
- }
- function int32ToFourCC( value ) {
- return String.fromCharCode(
- value & 0xff,
- ( value >> 8 ) & 0xff,
- ( value >> 16 ) & 0xff,
- ( value >> 24 ) & 0xff
- );
- }
- function loadARGBMip( buffer, dataOffset, width, height ) {
- var dataLength = width * height * 4;
- var srcBuffer = new Uint8Array( buffer, dataOffset, dataLength );
- var byteArray = new Uint8Array( dataLength );
- var dst = 0;
- var src = 0;
- for ( var y = 0; y < height; y ++ ) {
- for ( var x = 0; x < width; x ++ ) {
- var b = srcBuffer[ src ]; src ++;
- var g = srcBuffer[ src ]; src ++;
- var r = srcBuffer[ src ]; src ++;
- var a = srcBuffer[ src ]; src ++;
- byteArray[ dst ] = r; dst ++; //r
- byteArray[ dst ] = g; dst ++; //g
- byteArray[ dst ] = b; dst ++; //b
- byteArray[ dst ] = a; dst ++; //a
- }
- }
- return byteArray;
- }
- var FOURCC_DXT1 = fourCCToInt32( 'DXT1' );
- var FOURCC_DXT3 = fourCCToInt32( 'DXT3' );
- var FOURCC_DXT5 = fourCCToInt32( 'DXT5' );
- var FOURCC_ETC1 = fourCCToInt32( 'ETC1' );
- var headerLengthInt = 31; // The header length in 32 bit ints
- // Offsets into the header array
- var off_magic = 0;
- var off_size = 1;
- var off_flags = 2;
- var off_height = 3;
- var off_width = 4;
- var off_mipmapCount = 7;
- var off_pfFlags = 20;
- var off_pfFourCC = 21;
- var off_RGBBitCount = 22;
- var off_RBitMask = 23;
- var off_GBitMask = 24;
- var off_BBitMask = 25;
- var off_ABitMask = 26;
- // var off_caps = 27;
- var off_caps2 = 28;
- // var off_caps3 = 29;
- // var off_caps4 = 30;
- // Parse header
- var header = new Int32Array( buffer, 0, headerLengthInt );
- if ( header[ off_magic ] !== DDS_MAGIC ) {
- console.error( 'THREE.DDSLoader.parse: Invalid magic number in DDS header.' );
- return dds;
- }
- if ( ! header[ off_pfFlags ] & DDPF_FOURCC ) {
- console.error( 'THREE.DDSLoader.parse: Unsupported format, must contain a FourCC code.' );
- return dds;
- }
- var blockBytes;
- var fourCC = header[ off_pfFourCC ];
- var isRGBAUncompressed = false;
- switch ( fourCC ) {
- case FOURCC_DXT1:
- blockBytes = 8;
- dds.format = RGB_S3TC_DXT1_Format;
- break;
- case FOURCC_DXT3:
- blockBytes = 16;
- dds.format = RGBA_S3TC_DXT3_Format;
- break;
- case FOURCC_DXT5:
- blockBytes = 16;
- dds.format = RGBA_S3TC_DXT5_Format$1;
- break;
- case FOURCC_ETC1:
- blockBytes = 8;
- dds.format = RGB_ETC1_Format;
- break;
- default:
- if ( header[ off_RGBBitCount ] === 32
- && header[ off_RBitMask ] & 0xff0000
- && header[ off_GBitMask ] & 0xff00
- && header[ off_BBitMask ] & 0xff
- && header[ off_ABitMask ] & 0xff000000 ) {
- isRGBAUncompressed = true;
- blockBytes = 64;
- dds.format = RGBAFormat;
- } else {
- console.error( 'THREE.DDSLoader.parse: Unsupported FourCC code ', int32ToFourCC( fourCC ) );
- return dds;
- }
- }
- dds.mipmapCount = 1;
- if ( header[ off_flags ] & DDSD_MIPMAPCOUNT && loadMipmaps !== false ) {
- dds.mipmapCount = Math.max( 1, header[ off_mipmapCount ] );
- }
- var caps2 = header[ off_caps2 ];
- dds.isCubemap = caps2 & DDSCAPS2_CUBEMAP ? true : false;
- if ( dds.isCubemap && (
- ! ( caps2 & DDSCAPS2_CUBEMAP_POSITIVEX ) ||
- ! ( caps2 & DDSCAPS2_CUBEMAP_NEGATIVEX ) ||
- ! ( caps2 & DDSCAPS2_CUBEMAP_POSITIVEY ) ||
- ! ( caps2 & DDSCAPS2_CUBEMAP_NEGATIVEY ) ||
- ! ( caps2 & DDSCAPS2_CUBEMAP_POSITIVEZ ) ||
- ! ( caps2 & DDSCAPS2_CUBEMAP_NEGATIVEZ )
- ) ) {
- console.error( 'THREE.DDSLoader.parse: Incomplete cubemap faces' );
- return dds;
- }
- dds.width = header[ off_width ];
- dds.height = header[ off_height ];
- var dataOffset = header[ off_size ] + 4;
- // Extract mipmaps buffers
- var faces = dds.isCubemap ? 6 : 1;
- for ( var face = 0; face < faces; face ++ ) {
- var width = dds.width;
- var height = dds.height;
- for ( var i = 0; i < dds.mipmapCount; i ++ ) {
- if ( isRGBAUncompressed ) {
- var byteArray = loadARGBMip( buffer, dataOffset, width, height );
- var dataLength = byteArray.length;
- } else {
- var dataLength = Math.max( 4, width ) / 4 * Math.max( 4, height ) / 4 * blockBytes;
- var byteArray = new Uint8Array( buffer, dataOffset, dataLength );
- }
- var mipmap = { 'data': byteArray, 'width': width, 'height': height };
- dds.mipmaps.push( mipmap );
- dataOffset += dataLength;
- width = Math.max( width >> 1, 1 );
- height = Math.max( height >> 1, 1 );
- }
- }
- return dds;
- }
- } );
- var GLTFLoader = ( function () {
- function GLTFLoader( manager, renderer ) {
-
- Loader.call( this, manager );
- /* this.dracoLoader = null;
- this.ddsLoader = null;
- this.ktx2Loader = null;
- this.meshoptDecoder = null; */
-
- //xzw add:
- this.dracoLoader = new DRACOLoader;
- this.ktx2Loader = new KTX2Loader;
- this.meshoptDecoder = MeshoptDecoder;
- this.ddsLoader = new DDSLoader; //这个没测过
-
- //路径相对于index.html
- this.dracoLoader.setDecoderPath( '../libs/three.js/loaders/draco/' /* 'static/lib/three.js/loaders/draco/' */); //这两个路径可以自己改。在laser的环境也要放一份这个路径
- this.ktx2Loader.setTranscoderPath('../libs/three.js/loaders/ktx/' /* 'static/lib/three.js/loaders/ktx/' */ ).detectSupport( renderer );
-
- //------------
- this.pluginCallbacks = [];
- this.register( function ( parser ) {
- return new GLTFMaterialsClearcoatExtension( parser );
- } );
- this.register( function ( parser ) {
- return new GLTFTextureBasisUExtension( parser );
- } );
- this.register( function ( parser ) {
- return new GLTFTextureWebPExtension( parser );
- } );
- this.register( function ( parser ) {
- return new GLTFMaterialsTransmissionExtension( parser );
- } );
- this.register( function ( parser ) {
- return new GLTFLightsExtension( parser );
- } );
- this.register( function ( parser ) {
- return new GLTFMeshoptCompression( parser );
- } );
- }
- GLTFLoader.prototype = Object.assign( Object.create( Loader.prototype ), {
- constructor: GLTFLoader,
- load: function ( url, onLoad, onProgress, onError ) {
- var scope = this;
- var resourcePath;
- if ( this.resourcePath !== '' ) {
- resourcePath = this.resourcePath;
- } else if ( this.path !== '' ) {
- resourcePath = this.path;
- } else {
- resourcePath = LoaderUtils.extractUrlBase( url );
- }
- // Tells the LoadingManager to track an extra item, which resolves after
- // the model is fully loaded. This means the count of items loaded will
- // be incorrect, but ensures manager.onLoad() does not fire early.
- this.manager.itemStart( url );
- var _onError = function ( e ) {
- if ( onError ) {
- onError( e );
- } else {
- console.error( e );
- }
- scope.manager.itemError( url );
- scope.manager.itemEnd( url );
- };
- var loader = new FileLoader( this.manager );
- loader.setPath( this.path );
- loader.setResponseType( 'arraybuffer' );
- loader.setRequestHeader( this.requestHeader );
- loader.setWithCredentials( this.withCredentials );
- loader.load( url, function ( data ) {
- try {
- scope.parse( data, resourcePath, function ( gltf ) {
- onLoad( gltf );
- scope.manager.itemEnd( url );
- }, _onError );
- } catch ( e ) {
- _onError( e );
- }
- }, onProgress, _onError );
- },
- setDRACOLoader: function ( dracoLoader ) {
- this.dracoLoader = dracoLoader;
- return this;
- },
- setDDSLoader: function ( ddsLoader ) {
- this.ddsLoader = ddsLoader;
- return this;
- },
- setKTX2Loader: function ( ktx2Loader ) {
- this.ktx2Loader = ktx2Loader;
- return this;
- },
- setMeshoptDecoder: function ( meshoptDecoder ) {
- this.meshoptDecoder = meshoptDecoder;
- return this;
- },
- register: function ( callback ) {
- if ( this.pluginCallbacks.indexOf( callback ) === - 1 ) {
- this.pluginCallbacks.push( callback );
- }
- return this;
- },
- unregister: function ( callback ) {
- if ( this.pluginCallbacks.indexOf( callback ) !== - 1 ) {
- this.pluginCallbacks.splice( this.pluginCallbacks.indexOf( callback ), 1 );
- }
- return this;
- },
- parse: function ( data, path, onLoad, onError ) {
- var content;
- var extensions = {};
- var plugins = {};
- if ( typeof data === 'string' ) {
- content = data;
- } else {
- var magic = LoaderUtils.decodeText( new Uint8Array( data, 0, 4 ) );
- if ( magic === BINARY_EXTENSION_HEADER_MAGIC ) {
- try {
- extensions[ EXTENSIONS.KHR_BINARY_GLTF ] = new GLTFBinaryExtension( data );
- } catch ( error ) {
- if ( onError ) onError( error );
- return;
- }
- content = extensions[ EXTENSIONS.KHR_BINARY_GLTF ].content;
- } else {
- content = LoaderUtils.decodeText( new Uint8Array( data ) );
- }
- }
- var json = JSON.parse( content );
- if ( json.asset === undefined || json.asset.version[ 0 ] < 2 ) {
- if ( onError ) onError( new Error( 'THREE.GLTFLoader: Unsupported asset. glTF versions >=2.0 are supported.' ) );
- return;
- }
- var parser = new GLTFParser( json, {
- path: path || this.resourcePath || '',
- crossOrigin: this.crossOrigin,
- manager: this.manager,
- ktx2Loader: this.ktx2Loader,
- meshoptDecoder: this.meshoptDecoder
- } );
- parser.fileLoader.setRequestHeader( this.requestHeader );
- for ( var i = 0; i < this.pluginCallbacks.length; i ++ ) {
- var plugin = this.pluginCallbacks[ i ]( parser );
- plugins[ plugin.name ] = plugin;
- // Workaround to avoid determining as unknown extension
- // in addUnknownExtensionsToUserData().
- // Remove this workaround if we move all the existing
- // extension handlers to plugin system
- extensions[ plugin.name ] = true;
- }
- if ( json.extensionsUsed ) {
- for ( var i = 0; i < json.extensionsUsed.length; ++ i ) {
- var extensionName = json.extensionsUsed[ i ];
- var extensionsRequired = json.extensionsRequired || [];
- switch ( extensionName ) {
- case EXTENSIONS.KHR_MATERIALS_UNLIT:
- extensions[ extensionName ] = new GLTFMaterialsUnlitExtension();
- break;
- case EXTENSIONS.KHR_MATERIALS_PBR_SPECULAR_GLOSSINESS:
- extensions[ extensionName ] = new GLTFMaterialsPbrSpecularGlossinessExtension();
- break;
- case EXTENSIONS.KHR_DRACO_MESH_COMPRESSION:
- extensions[ extensionName ] = new GLTFDracoMeshCompressionExtension( json, this.dracoLoader );
- break;
- case EXTENSIONS.MSFT_TEXTURE_DDS:
- extensions[ extensionName ] = new GLTFTextureDDSExtension( this.ddsLoader );
- break;
- case EXTENSIONS.KHR_TEXTURE_TRANSFORM:
- extensions[ extensionName ] = new GLTFTextureTransformExtension();
- break;
- case EXTENSIONS.KHR_MESH_QUANTIZATION:
- extensions[ extensionName ] = new GLTFMeshQuantizationExtension();
- break;
- default:
- if ( extensionsRequired.indexOf( extensionName ) >= 0 && plugins[ extensionName ] === undefined ) {
- console.warn( 'THREE.GLTFLoader: Unknown extension "' + extensionName + '".' );
- }
- }
- }
- }
- parser.setExtensions( extensions );
- parser.setPlugins( plugins );
- parser.parse( onLoad, onError );
- }
- } );
- /* GLTFREGISTRY */
- function GLTFRegistry() {
- var objects = {};
- return {
- get: function ( key ) {
- return objects[ key ];
- },
- add: function ( key, object ) {
- objects[ key ] = object;
- },
- remove: function ( key ) {
- delete objects[ key ];
- },
- removeAll: function () {
- objects = {};
- }
- };
- }
- /*********************************/
- /********** EXTENSIONS ***********/
- /*********************************/
- var EXTENSIONS = {
- KHR_BINARY_GLTF: 'KHR_binary_glTF',
- KHR_DRACO_MESH_COMPRESSION: 'KHR_draco_mesh_compression',
- KHR_LIGHTS_PUNCTUAL: 'KHR_lights_punctual',
- KHR_MATERIALS_CLEARCOAT: 'KHR_materials_clearcoat',
- KHR_MATERIALS_PBR_SPECULAR_GLOSSINESS: 'KHR_materials_pbrSpecularGlossiness',
- KHR_MATERIALS_TRANSMISSION: 'KHR_materials_transmission',
- KHR_MATERIALS_UNLIT: 'KHR_materials_unlit',
- KHR_TEXTURE_BASISU: 'KHR_texture_basisu',
- KHR_TEXTURE_TRANSFORM: 'KHR_texture_transform',
- KHR_MESH_QUANTIZATION: 'KHR_mesh_quantization',
- EXT_TEXTURE_WEBP: 'EXT_texture_webp',
- EXT_MESHOPT_COMPRESSION: 'EXT_meshopt_compression',
- MSFT_TEXTURE_DDS: 'MSFT_texture_dds'
- };
- /**
- * DDS Texture Extension
- *
- * Specification: https://github.com/KhronosGroup/glTF/tree/master/extensions/2.0/Vendor/MSFT_texture_dds
- *
- */
- function GLTFTextureDDSExtension( ddsLoader ) {
- if ( ! ddsLoader ) {
- throw new Error( 'THREE.GLTFLoader: Attempting to load .dds texture without importing DDSLoader' );
- }
- this.name = EXTENSIONS.MSFT_TEXTURE_DDS;
- this.ddsLoader = ddsLoader;
- }
- /**
- * Punctual Lights Extension
- *
- * Specification: https://github.com/KhronosGroup/glTF/tree/master/extensions/2.0/Khronos/KHR_lights_punctual
- */
- function GLTFLightsExtension( parser ) {
- this.parser = parser;
- this.name = EXTENSIONS.KHR_LIGHTS_PUNCTUAL;
- // Object3D instance caches
- this.cache = { refs: {}, uses: {} };
- }
- GLTFLightsExtension.prototype._markDefs = function () {
- var parser = this.parser;
- var nodeDefs = this.parser.json.nodes || [];
- for ( var nodeIndex = 0, nodeLength = nodeDefs.length; nodeIndex < nodeLength; nodeIndex ++ ) {
- var nodeDef = nodeDefs[ nodeIndex ];
- if ( nodeDef.extensions
- && nodeDef.extensions[ this.name ]
- && nodeDef.extensions[ this.name ].light !== undefined ) {
- parser._addNodeRef( this.cache, nodeDef.extensions[ this.name ].light );
- }
- }
- };
- GLTFLightsExtension.prototype._loadLight = function ( lightIndex ) {
- var parser = this.parser;
- var cacheKey = 'light:' + lightIndex;
- var dependency = parser.cache.get( cacheKey );
- if ( dependency ) return dependency;
- var json = parser.json;
- var extensions = ( json.extensions && json.extensions[ this.name ] ) || {};
- var lightDefs = extensions.lights || [];
- var lightDef = lightDefs[ lightIndex ];
- var lightNode;
- var color = new Color( 0xffffff );
- if ( lightDef.color !== undefined ) color.fromArray( lightDef.color );
- var range = lightDef.range !== undefined ? lightDef.range : 0;
- switch ( lightDef.type ) {
- case 'directional':
- lightNode = new DirectionalLight( color );
- lightNode.target.position.set( 0, 0, - 1 );
- lightNode.add( lightNode.target );
- break;
- case 'point':
- lightNode = new PointLight( color );
- lightNode.distance = range;
- break;
- case 'spot':
- lightNode = new SpotLight( color );
- lightNode.distance = range;
- // Handle spotlight properties.
- lightDef.spot = lightDef.spot || {};
- lightDef.spot.innerConeAngle = lightDef.spot.innerConeAngle !== undefined ? lightDef.spot.innerConeAngle : 0;
- lightDef.spot.outerConeAngle = lightDef.spot.outerConeAngle !== undefined ? lightDef.spot.outerConeAngle : Math.PI / 4.0;
- lightNode.angle = lightDef.spot.outerConeAngle;
- lightNode.penumbra = 1.0 - lightDef.spot.innerConeAngle / lightDef.spot.outerConeAngle;
- lightNode.target.position.set( 0, 0, - 1 );
- lightNode.add( lightNode.target );
- break;
- default:
- throw new Error( 'THREE.GLTFLoader: Unexpected light type: ' + lightDef.type );
- }
- // Some lights (e.g. spot) default to a position other than the origin. Reset the position
- // here, because node-level parsing will only override position if explicitly specified.
- lightNode.position.set( 0, 0, 0 );
- lightNode.decay = 2;
- if ( lightDef.intensity !== undefined ) lightNode.intensity = lightDef.intensity;
- lightNode.name = parser.createUniqueName( lightDef.name || ( 'light_' + lightIndex ) );
- dependency = Promise.resolve( lightNode );
- parser.cache.add( cacheKey, dependency );
- return dependency;
- };
- GLTFLightsExtension.prototype.createNodeAttachment = function ( nodeIndex ) {
- var self = this;
- var parser = this.parser;
- var json = parser.json;
- var nodeDef = json.nodes[ nodeIndex ];
- var lightDef = ( nodeDef.extensions && nodeDef.extensions[ this.name ] ) || {};
- var lightIndex = lightDef.light;
- if ( lightIndex === undefined ) return null;
- return this._loadLight( lightIndex ).then( function ( light ) {
- return parser._getNodeRef( self.cache, lightIndex, light );
- } );
- };
- /**
- * Unlit Materials Extension
- *
- * Specification: https://github.com/KhronosGroup/glTF/tree/master/extensions/2.0/Khronos/KHR_materials_unlit
- */
- function GLTFMaterialsUnlitExtension() {
- this.name = EXTENSIONS.KHR_MATERIALS_UNLIT;
- }
- GLTFMaterialsUnlitExtension.prototype.getMaterialType = function () {
- return MeshBasicMaterial;
- };
- GLTFMaterialsUnlitExtension.prototype.extendParams = function ( materialParams, materialDef, parser ) {
- var pending = [];
- materialParams.color = new Color( 1.0, 1.0, 1.0 );
- materialParams.opacity = 1.0;
- var metallicRoughness = materialDef.pbrMetallicRoughness;
- if ( metallicRoughness ) {
- if ( Array.isArray( metallicRoughness.baseColorFactor ) ) {
- var array = metallicRoughness.baseColorFactor;
- materialParams.color.fromArray( array );
- materialParams.opacity = array[ 3 ];
- }
- if ( metallicRoughness.baseColorTexture !== undefined ) {
- pending.push( parser.assignTexture( materialParams, 'map', metallicRoughness.baseColorTexture ) );
- }
- }
- return Promise.all( pending );
- };
- /**
- * Clearcoat Materials Extension
- *
- * Specification: https://github.com/KhronosGroup/glTF/tree/master/extensions/2.0/Khronos/KHR_materials_clearcoat
- */
- function GLTFMaterialsClearcoatExtension( parser ) {
- this.parser = parser;
- this.name = EXTENSIONS.KHR_MATERIALS_CLEARCOAT;
- }
- GLTFMaterialsClearcoatExtension.prototype.getMaterialType = function ( materialIndex ) {
- var parser = this.parser;
- var materialDef = parser.json.materials[ materialIndex ];
- if ( ! materialDef.extensions || ! materialDef.extensions[ this.name ] ) return null;
- return MeshPhysicalMaterial;
- };
- GLTFMaterialsClearcoatExtension.prototype.extendMaterialParams = function ( materialIndex, materialParams ) {
- var parser = this.parser;
- var materialDef = parser.json.materials[ materialIndex ];
- if ( ! materialDef.extensions || ! materialDef.extensions[ this.name ] ) {
- return Promise.resolve();
- }
- var pending = [];
- var extension = materialDef.extensions[ this.name ];
- if ( extension.clearcoatFactor !== undefined ) {
- materialParams.clearcoat = extension.clearcoatFactor;
- }
- if ( extension.clearcoatTexture !== undefined ) {
- pending.push( parser.assignTexture( materialParams, 'clearcoatMap', extension.clearcoatTexture ) );
- }
- if ( extension.clearcoatRoughnessFactor !== undefined ) {
- materialParams.clearcoatRoughness = extension.clearcoatRoughnessFactor;
- }
- if ( extension.clearcoatRoughnessTexture !== undefined ) {
- pending.push( parser.assignTexture( materialParams, 'clearcoatRoughnessMap', extension.clearcoatRoughnessTexture ) );
- }
- if ( extension.clearcoatNormalTexture !== undefined ) {
- pending.push( parser.assignTexture( materialParams, 'clearcoatNormalMap', extension.clearcoatNormalTexture ) );
- if ( extension.clearcoatNormalTexture.scale !== undefined ) {
- var scale = extension.clearcoatNormalTexture.scale;
- materialParams.clearcoatNormalScale = new Vector2$1( scale, scale );
- }
- }
- return Promise.all( pending );
- };
- /**
- * Transmission Materials Extension
- *
- * Specification: https://github.com/KhronosGroup/glTF/tree/master/extensions/2.0/Khronos/KHR_materials_transmission
- * Draft: https://github.com/KhronosGroup/glTF/pull/1698
- */
- function GLTFMaterialsTransmissionExtension( parser ) {
- this.parser = parser;
- this.name = EXTENSIONS.KHR_MATERIALS_TRANSMISSION;
- }
- GLTFMaterialsTransmissionExtension.prototype.getMaterialType = function ( materialIndex ) {
- var parser = this.parser;
- var materialDef = parser.json.materials[ materialIndex ];
- if ( ! materialDef.extensions || ! materialDef.extensions[ this.name ] ) return null;
- return MeshPhysicalMaterial;
- };
- GLTFMaterialsTransmissionExtension.prototype.extendMaterialParams = function ( materialIndex, materialParams ) {
- var parser = this.parser;
- var materialDef = parser.json.materials[ materialIndex ];
- if ( ! materialDef.extensions || ! materialDef.extensions[ this.name ] ) {
- return Promise.resolve();
- }
- var pending = [];
- var extension = materialDef.extensions[ this.name ];
- if ( extension.transmissionFactor !== undefined ) {
- materialParams.transmission = extension.transmissionFactor;
- }
- if ( extension.transmissionTexture !== undefined ) {
- pending.push( parser.assignTexture( materialParams, 'transmissionMap', extension.transmissionTexture ) );
- }
- return Promise.all( pending );
- };
- /**
- * BasisU Texture Extension
- *
- * Specification: https://github.com/KhronosGroup/glTF/tree/master/extensions/2.0/Khronos/KHR_texture_basisu
- */
- function GLTFTextureBasisUExtension( parser ) {
- this.parser = parser;
- this.name = EXTENSIONS.KHR_TEXTURE_BASISU;
- }
- GLTFTextureBasisUExtension.prototype.loadTexture = function ( textureIndex ) {
- var parser = this.parser;
- var json = parser.json;
- var textureDef = json.textures[ textureIndex ];
- if ( ! textureDef.extensions || ! textureDef.extensions[ this.name ] ) {
- return null;
- }
- var extension = textureDef.extensions[ this.name ];
- var source = json.images[ extension.source ];
- var loader = parser.options.ktx2Loader;
- if ( ! loader ) {
- if ( json.extensionsRequired && json.extensionsRequired.indexOf( this.name ) >= 0 ) {
- throw new Error( 'THREE.GLTFLoader: setKTX2Loader must be called before loading KTX2 textures' );
- } else {
- // Assumes that the extension is optional and that a fallback texture is present
- return null;
- }
- }
- return parser.loadTextureImage( textureIndex, source, loader );
- };
- /**
- * WebP Texture Extension
- *
- * Specification: https://github.com/KhronosGroup/glTF/tree/master/extensions/2.0/Vendor/EXT_texture_webp
- */
- function GLTFTextureWebPExtension( parser ) {
- this.parser = parser;
- this.name = EXTENSIONS.EXT_TEXTURE_WEBP;
- this.isSupported = null;
- }
- GLTFTextureWebPExtension.prototype.loadTexture = function ( textureIndex ) {
- var name = this.name;
- var parser = this.parser;
- var json = parser.json;
- var textureDef = json.textures[ textureIndex ];
- if ( ! textureDef.extensions || ! textureDef.extensions[ name ] ) {
- return null;
- }
- var extension = textureDef.extensions[ name ];
- var source = json.images[ extension.source ];
- var loader = source.uri ? parser.options.manager.getHandler( source.uri ) : parser.textureLoader;
- return this.detectSupport().then( function ( isSupported ) {
- if ( isSupported ) return parser.loadTextureImage( textureIndex, source, loader );
- if ( json.extensionsRequired && json.extensionsRequired.indexOf( name ) >= 0 ) {
- throw new Error( 'THREE.GLTFLoader: WebP required by asset but unsupported.' );
- }
- // Fall back to PNG or JPEG.
- return parser.loadTexture( textureIndex );
- } );
- };
- GLTFTextureWebPExtension.prototype.detectSupport = function () {
- if ( ! this.isSupported ) {
- this.isSupported = new Promise( function ( resolve ) {
- var image = new Image();
- // Lossy test image. Support for lossy images doesn't guarantee support for all
- // WebP images, unfortunately.
- image.src = 'data:image/webp;base64,UklGRiIAAABXRUJQVlA4IBYAAAAwAQCdASoBAAEADsD+JaQAA3AAAAAA';
- image.onload = image.onerror = function () {
- resolve( image.height === 1 );
- };
- } );
- }
- return this.isSupported;
- };
- /**
- * meshopt BufferView Compression Extension
- *
- * Specification: https://github.com/KhronosGroup/glTF/tree/master/extensions/2.0/Vendor/EXT_meshopt_compression
- */
- function GLTFMeshoptCompression( parser ) {
- this.name = EXTENSIONS.EXT_MESHOPT_COMPRESSION;
- this.parser = parser;
- }
- GLTFMeshoptCompression.prototype.loadBufferView = function ( index ) {
- var json = this.parser.json;
- var bufferView = json.bufferViews[ index ];
- if ( bufferView.extensions && bufferView.extensions[ this.name ] ) {
- var extensionDef = bufferView.extensions[ this.name ];
- var buffer = this.parser.getDependency( 'buffer', extensionDef.buffer );
- var decoder = this.parser.options.meshoptDecoder;
- if ( ! decoder || ! decoder.supported ) {
- if ( json.extensionsRequired && json.extensionsRequired.indexOf( this.name ) >= 0 ) {
- throw new Error( 'THREE.GLTFLoader: setMeshoptDecoder must be called before loading compressed files' );
- } else {
- // Assumes that the extension is optional and that fallback buffer data is present
- return null;
- }
- }
- return Promise.all( [ buffer, decoder.ready ] ).then( function ( res ) {
- var byteOffset = extensionDef.byteOffset || 0;
- var byteLength = extensionDef.byteLength || 0;
- var count = extensionDef.count;
- var stride = extensionDef.byteStride;
- var result = new ArrayBuffer( count * stride );
- var source = new Uint8Array( res[ 0 ], byteOffset, byteLength );
- decoder.decodeGltfBuffer( new Uint8Array( result ), count, stride, source, extensionDef.mode, extensionDef.filter );
- return result;
- } );
- } else {
- return null;
- }
- };
- /* BINARY EXTENSION */
- var BINARY_EXTENSION_HEADER_MAGIC = 'glTF';
- var BINARY_EXTENSION_HEADER_LENGTH = 12;
- var BINARY_EXTENSION_CHUNK_TYPES = { JSON: 0x4E4F534A, BIN: 0x004E4942 };
- function GLTFBinaryExtension( data ) {
- this.name = EXTENSIONS.KHR_BINARY_GLTF;
- this.content = null;
- this.body = null;
- var headerView = new DataView( data, 0, BINARY_EXTENSION_HEADER_LENGTH );
- this.header = {
- magic: LoaderUtils.decodeText( new Uint8Array( data.slice( 0, 4 ) ) ),
- version: headerView.getUint32( 4, true ),
- length: headerView.getUint32( 8, true )
- };
- if ( this.header.magic !== BINARY_EXTENSION_HEADER_MAGIC ) {
- throw new Error( 'THREE.GLTFLoader: Unsupported glTF-Binary header.' );
- } else if ( this.header.version < 2.0 ) {
- throw new Error( 'THREE.GLTFLoader: Legacy binary file detected.' );
- }
- var chunkView = new DataView( data, BINARY_EXTENSION_HEADER_LENGTH );
- var chunkIndex = 0;
- while ( chunkIndex < chunkView.byteLength ) {
- var chunkLength = chunkView.getUint32( chunkIndex, true );
- chunkIndex += 4;
- var chunkType = chunkView.getUint32( chunkIndex, true );
- chunkIndex += 4;
- if ( chunkType === BINARY_EXTENSION_CHUNK_TYPES.JSON ) {
- var contentArray = new Uint8Array( data, BINARY_EXTENSION_HEADER_LENGTH + chunkIndex, chunkLength );
- this.content = LoaderUtils.decodeText( contentArray );
- } else if ( chunkType === BINARY_EXTENSION_CHUNK_TYPES.BIN ) {
- var byteOffset = BINARY_EXTENSION_HEADER_LENGTH + chunkIndex;
- this.body = data.slice( byteOffset, byteOffset + chunkLength );
- }
- // Clients must ignore chunks with unknown types.
- chunkIndex += chunkLength;
- }
- if ( this.content === null ) {
- throw new Error( 'THREE.GLTFLoader: JSON content not found.' );
- }
- }
- /**
- * DRACO Mesh Compression Extension
- *
- * Specification: https://github.com/KhronosGroup/glTF/tree/master/extensions/2.0/Khronos/KHR_draco_mesh_compression
- */
- function GLTFDracoMeshCompressionExtension( json, dracoLoader ) {
- if ( ! dracoLoader ) {
-
- throw new Error( 'THREE.GLTFLoader: No DRACOLoader instance provided.' );
- }
- this.name = EXTENSIONS.KHR_DRACO_MESH_COMPRESSION;
- this.json = json;
- this.dracoLoader = dracoLoader;
- this.dracoLoader.preload();
- }
- GLTFDracoMeshCompressionExtension.prototype.decodePrimitive = function ( primitive, parser ) {
- var json = this.json;
- var dracoLoader = this.dracoLoader;
- var bufferViewIndex = primitive.extensions[ this.name ].bufferView;
- var gltfAttributeMap = primitive.extensions[ this.name ].attributes;
- var threeAttributeMap = {};
- var attributeNormalizedMap = {};
- var attributeTypeMap = {};
- for ( var attributeName in gltfAttributeMap ) {
- var threeAttributeName = ATTRIBUTES[ attributeName ] || attributeName.toLowerCase();
- threeAttributeMap[ threeAttributeName ] = gltfAttributeMap[ attributeName ];
- }
- for ( attributeName in primitive.attributes ) {
- var threeAttributeName = ATTRIBUTES[ attributeName ] || attributeName.toLowerCase();
- if ( gltfAttributeMap[ attributeName ] !== undefined ) {
- var accessorDef = json.accessors[ primitive.attributes[ attributeName ] ];
- var componentType = WEBGL_COMPONENT_TYPES[ accessorDef.componentType ];
- attributeTypeMap[ threeAttributeName ] = componentType;
- attributeNormalizedMap[ threeAttributeName ] = accessorDef.normalized === true;
- }
- }
- return parser.getDependency( 'bufferView', bufferViewIndex ).then( function ( bufferView ) {
- return new Promise( function ( resolve ) {
- dracoLoader.decodeDracoFile( bufferView, function ( geometry ) {
- for ( var attributeName in geometry.attributes ) {
- var attribute = geometry.attributes[ attributeName ];
- var normalized = attributeNormalizedMap[ attributeName ];
- if ( normalized !== undefined ) attribute.normalized = normalized;
- }
- resolve( geometry );
- }, threeAttributeMap, attributeTypeMap );
- } );
- } );
- };
- /**
- * Texture Transform Extension
- *
- * Specification: https://github.com/KhronosGroup/glTF/tree/master/extensions/2.0/Khronos/KHR_texture_transform
- */
- function GLTFTextureTransformExtension() {
- this.name = EXTENSIONS.KHR_TEXTURE_TRANSFORM;
- }
- GLTFTextureTransformExtension.prototype.extendTexture = function ( texture, transform ) {
- texture = texture.clone();
- if ( transform.offset !== undefined ) {
- texture.offset.fromArray( transform.offset );
- }
- if ( transform.rotation !== undefined ) {
- texture.rotation = transform.rotation;
- }
- if ( transform.scale !== undefined ) {
- texture.repeat.fromArray( transform.scale );
- }
- if ( transform.texCoord !== undefined ) {
- console.warn( 'THREE.GLTFLoader: Custom UV sets in "' + this.name + '" extension not yet supported.' );
- }
- texture.needsUpdate = true;
- return texture;
- };
- /**
- * Specular-Glossiness Extension
- *
- * Specification: https://github.com/KhronosGroup/glTF/tree/master/extensions/2.0/Khronos/KHR_materials_pbrSpecularGlossiness
- */
- /**
- * A sub class of StandardMaterial with some of the functionality
- * changed via the `onBeforeCompile` callback
- * @pailhead
- */
- function GLTFMeshStandardSGMaterial( params ) {
- MeshStandardMaterial.call( this );
- this.isGLTFSpecularGlossinessMaterial = true;
- //various chunks that need replacing
- var specularMapParsFragmentChunk = [
- '#ifdef USE_SPECULARMAP',
- ' uniform sampler2D specularMap;',
- '#endif'
- ].join( '\n' );
- var glossinessMapParsFragmentChunk = [
- '#ifdef USE_GLOSSINESSMAP',
- ' uniform sampler2D glossinessMap;',
- '#endif'
- ].join( '\n' );
- var specularMapFragmentChunk = [
- 'vec3 specularFactor = specular;',
- '#ifdef USE_SPECULARMAP',
- ' vec4 texelSpecular = texture2D( specularMap, vUv );',
- ' texelSpecular = sRGBToLinear( texelSpecular );',
- ' // reads channel RGB, compatible with a glTF Specular-Glossiness (RGBA) texture',
- ' specularFactor *= texelSpecular.rgb;',
- '#endif'
- ].join( '\n' );
- var glossinessMapFragmentChunk = [
- 'float glossinessFactor = glossiness;',
- '#ifdef USE_GLOSSINESSMAP',
- ' vec4 texelGlossiness = texture2D( glossinessMap, vUv );',
- ' // reads channel A, compatible with a glTF Specular-Glossiness (RGBA) texture',
- ' glossinessFactor *= texelGlossiness.a;',
- '#endif'
- ].join( '\n' );
- var lightPhysicalFragmentChunk = [
- 'PhysicalMaterial material;',
- 'material.diffuseColor = diffuseColor.rgb * ( 1. - max( specularFactor.r, max( specularFactor.g, specularFactor.b ) ) );',
- 'vec3 dxy = max( abs( dFdx( geometryNormal ) ), abs( dFdy( geometryNormal ) ) );',
- 'float geometryRoughness = max( max( dxy.x, dxy.y ), dxy.z );',
- 'material.specularRoughness = max( 1.0 - glossinessFactor, 0.0525 ); // 0.0525 corresponds to the base mip of a 256 cubemap.',
- 'material.specularRoughness += geometryRoughness;',
- 'material.specularRoughness = min( material.specularRoughness, 1.0 );',
- 'material.specularColor = specularFactor;',
- ].join( '\n' );
- var uniforms = {
- specular: { value: new Color().setHex( 0xffffff ) },
- glossiness: { value: 1 },
- specularMap: { value: null },
- glossinessMap: { value: null }
- };
- this._extraUniforms = uniforms;
- this.onBeforeCompile = function ( shader ) {
- for ( var uniformName in uniforms ) {
- shader.uniforms[ uniformName ] = uniforms[ uniformName ];
- }
- shader.fragmentShader = shader.fragmentShader
- .replace( 'uniform float roughness;', 'uniform vec3 specular;' )
- .replace( 'uniform float metalness;', 'uniform float glossiness;' )
- .replace( '#include <roughnessmap_pars_fragment>', specularMapParsFragmentChunk )
- .replace( '#include <metalnessmap_pars_fragment>', glossinessMapParsFragmentChunk )
- .replace( '#include <roughnessmap_fragment>', specularMapFragmentChunk )
- .replace( '#include <metalnessmap_fragment>', glossinessMapFragmentChunk )
- .replace( '#include <lights_physical_fragment>', lightPhysicalFragmentChunk );
- };
- Object.defineProperties( this, {
- specular: {
- get: function () {
- return uniforms.specular.value;
- },
- set: function ( v ) {
- uniforms.specular.value = v;
- }
- },
- specularMap: {
- get: function () {
- return uniforms.specularMap.value;
- },
- set: function ( v ) {
- uniforms.specularMap.value = v;
- if ( v ) {
- this.defines.USE_SPECULARMAP = ''; // USE_UV is set by the renderer for specular maps
- } else {
- delete this.defines.USE_SPECULARMAP;
- }
- }
- },
- glossiness: {
- get: function () {
- return uniforms.glossiness.value;
- },
- set: function ( v ) {
- uniforms.glossiness.value = v;
- }
- },
- glossinessMap: {
- get: function () {
- return uniforms.glossinessMap.value;
- },
- set: function ( v ) {
- uniforms.glossinessMap.value = v;
- if ( v ) {
- this.defines.USE_GLOSSINESSMAP = '';
- this.defines.USE_UV = '';
- } else {
- delete this.defines.USE_GLOSSINESSMAP;
- delete this.defines.USE_UV;
- }
- }
- }
- } );
- delete this.metalness;
- delete this.roughness;
- delete this.metalnessMap;
- delete this.roughnessMap;
- this.setValues( params );
- }
- GLTFMeshStandardSGMaterial.prototype = Object.create( MeshStandardMaterial.prototype );
- GLTFMeshStandardSGMaterial.prototype.constructor = GLTFMeshStandardSGMaterial;
- GLTFMeshStandardSGMaterial.prototype.copy = function ( source ) {
- MeshStandardMaterial.prototype.copy.call( this, source );
- this.specularMap = source.specularMap;
- this.specular.copy( source.specular );
- this.glossinessMap = source.glossinessMap;
- this.glossiness = source.glossiness;
- delete this.metalness;
- delete this.roughness;
- delete this.metalnessMap;
- delete this.roughnessMap;
- return this;
- };
- function GLTFMaterialsPbrSpecularGlossinessExtension() {
- return {
- name: EXTENSIONS.KHR_MATERIALS_PBR_SPECULAR_GLOSSINESS,
- specularGlossinessParams: [
- 'color',
- 'map',
- 'lightMap',
- 'lightMapIntensity',
- 'aoMap',
- 'aoMapIntensity',
- 'emissive',
- 'emissiveIntensity',
- 'emissiveMap',
- 'bumpMap',
- 'bumpScale',
- 'normalMap',
- 'normalMapType',
- 'displacementMap',
- 'displacementScale',
- 'displacementBias',
- 'specularMap',
- 'specular',
- 'glossinessMap',
- 'glossiness',
- 'alphaMap',
- 'envMap',
- 'envMapIntensity',
- 'refractionRatio',
- ],
- getMaterialType: function () {
- return GLTFMeshStandardSGMaterial;
- },
- extendParams: function ( materialParams, materialDef, parser ) {
- var pbrSpecularGlossiness = materialDef.extensions[ this.name ];
- materialParams.color = new Color( 1.0, 1.0, 1.0 );
- materialParams.opacity = 1.0;
- var pending = [];
- if ( Array.isArray( pbrSpecularGlossiness.diffuseFactor ) ) {
- var array = pbrSpecularGlossiness.diffuseFactor;
- materialParams.color.fromArray( array );
- materialParams.opacity = array[ 3 ];
- }
- if ( pbrSpecularGlossiness.diffuseTexture !== undefined ) {
- pending.push( parser.assignTexture( materialParams, 'map', pbrSpecularGlossiness.diffuseTexture ) );
- }
- materialParams.emissive = new Color( 0.0, 0.0, 0.0 );
- materialParams.glossiness = pbrSpecularGlossiness.glossinessFactor !== undefined ? pbrSpecularGlossiness.glossinessFactor : 1.0;
- materialParams.specular = new Color( 1.0, 1.0, 1.0 );
- if ( Array.isArray( pbrSpecularGlossiness.specularFactor ) ) {
- materialParams.specular.fromArray( pbrSpecularGlossiness.specularFactor );
- }
- if ( pbrSpecularGlossiness.specularGlossinessTexture !== undefined ) {
- var specGlossMapDef = pbrSpecularGlossiness.specularGlossinessTexture;
- pending.push( parser.assignTexture( materialParams, 'glossinessMap', specGlossMapDef ) );
- pending.push( parser.assignTexture( materialParams, 'specularMap', specGlossMapDef ) );
- }
- return Promise.all( pending );
- },
- createMaterial: function ( materialParams ) {
- var material = new GLTFMeshStandardSGMaterial( materialParams );
- material.fog = true;
- material.color = materialParams.color;
- material.map = materialParams.map === undefined ? null : materialParams.map;
- material.lightMap = null;
- material.lightMapIntensity = 1.0;
- material.aoMap = materialParams.aoMap === undefined ? null : materialParams.aoMap;
- material.aoMapIntensity = 1.0;
- material.emissive = materialParams.emissive;
- material.emissiveIntensity = 1.0;
- material.emissiveMap = materialParams.emissiveMap === undefined ? null : materialParams.emissiveMap;
- material.bumpMap = materialParams.bumpMap === undefined ? null : materialParams.bumpMap;
- material.bumpScale = 1;
- material.normalMap = materialParams.normalMap === undefined ? null : materialParams.normalMap;
- material.normalMapType = TangentSpaceNormalMap;
- if ( materialParams.normalScale ) material.normalScale = materialParams.normalScale;
- material.displacementMap = null;
- material.displacementScale = 1;
- material.displacementBias = 0;
- material.specularMap = materialParams.specularMap === undefined ? null : materialParams.specularMap;
- material.specular = materialParams.specular;
- material.glossinessMap = materialParams.glossinessMap === undefined ? null : materialParams.glossinessMap;
- material.glossiness = materialParams.glossiness;
- material.alphaMap = null;
- material.envMap = materialParams.envMap === undefined ? null : materialParams.envMap;
- material.envMapIntensity = 1.0;
- material.refractionRatio = 0.98;
- return material;
- },
- };
- }
- /**
- * Mesh Quantization Extension
- *
- * Specification: https://github.com/KhronosGroup/glTF/tree/master/extensions/2.0/Khronos/KHR_mesh_quantization
- */
- function GLTFMeshQuantizationExtension() {
- this.name = EXTENSIONS.KHR_MESH_QUANTIZATION;
- }
- /*********************************/
- /********** INTERPOLATION ********/
- /*********************************/
- // Spline Interpolation
- // Specification: https://github.com/KhronosGroup/glTF/blob/master/specification/2.0/README.md#appendix-c-spline-interpolation
- function GLTFCubicSplineInterpolant( parameterPositions, sampleValues, sampleSize, resultBuffer ) {
- Interpolant.call( this, parameterPositions, sampleValues, sampleSize, resultBuffer );
- }
- GLTFCubicSplineInterpolant.prototype = Object.create( Interpolant.prototype );
- GLTFCubicSplineInterpolant.prototype.constructor = GLTFCubicSplineInterpolant;
- GLTFCubicSplineInterpolant.prototype.copySampleValue_ = function ( index ) {
- // Copies a sample value to the result buffer. See description of glTF
- // CUBICSPLINE values layout in interpolate_() function below.
- var result = this.resultBuffer,
- values = this.sampleValues,
- valueSize = this.valueSize,
- offset = index * valueSize * 3 + valueSize;
- for ( var i = 0; i !== valueSize; i ++ ) {
- result[ i ] = values[ offset + i ];
- }
- return result;
- };
- GLTFCubicSplineInterpolant.prototype.beforeStart_ = GLTFCubicSplineInterpolant.prototype.copySampleValue_;
- GLTFCubicSplineInterpolant.prototype.afterEnd_ = GLTFCubicSplineInterpolant.prototype.copySampleValue_;
- GLTFCubicSplineInterpolant.prototype.interpolate_ = function ( i1, t0, t, t1 ) {
- var result = this.resultBuffer;
- var values = this.sampleValues;
- var stride = this.valueSize;
- var stride2 = stride * 2;
- var stride3 = stride * 3;
- var td = t1 - t0;
- var p = ( t - t0 ) / td;
- var pp = p * p;
- var ppp = pp * p;
- var offset1 = i1 * stride3;
- var offset0 = offset1 - stride3;
- var s2 = - 2 * ppp + 3 * pp;
- var s3 = ppp - pp;
- var s0 = 1 - s2;
- var s1 = s3 - pp + p;
- // Layout of keyframe output values for CUBICSPLINE animations:
- // [ inTangent_1, splineVertex_1, outTangent_1, inTangent_2, splineVertex_2, ... ]
- for ( var i = 0; i !== stride; i ++ ) {
- var p0 = values[ offset0 + i + stride ]; // splineVertex_k
- var m0 = values[ offset0 + i + stride2 ] * td; // outTangent_k * (t_k+1 - t_k)
- var p1 = values[ offset1 + i + stride ]; // splineVertex_k+1
- var m1 = values[ offset1 + i ] * td; // inTangent_k+1 * (t_k+1 - t_k)
- result[ i ] = s0 * p0 + s1 * m0 + s2 * p1 + s3 * m1;
- }
- return result;
- };
- /*********************************/
- /********** INTERNALS ************/
- /*********************************/
- /* CONSTANTS */
- var WEBGL_CONSTANTS = {
- FLOAT: 5126,
- //FLOAT_MAT2: 35674,
- FLOAT_MAT3: 35675,
- FLOAT_MAT4: 35676,
- FLOAT_VEC2: 35664,
- FLOAT_VEC3: 35665,
- FLOAT_VEC4: 35666,
- LINEAR: 9729,
- REPEAT: 10497,
- SAMPLER_2D: 35678,
- POINTS: 0,
- LINES: 1,
- LINE_LOOP: 2,
- LINE_STRIP: 3,
- TRIANGLES: 4,
- TRIANGLE_STRIP: 5,
- TRIANGLE_FAN: 6,
- UNSIGNED_BYTE: 5121,
- UNSIGNED_SHORT: 5123
- };
- var WEBGL_COMPONENT_TYPES = {
- 5120: Int8Array,
- 5121: Uint8Array,
- 5122: Int16Array,
- 5123: Uint16Array,
- 5125: Uint32Array,
- 5126: Float32Array
- };
- var WEBGL_FILTERS = {
- 9728: NearestFilter,
- 9729: LinearFilter,
- 9984: NearestMipmapNearestFilter,
- 9985: LinearMipmapNearestFilter,
- 9986: NearestMipmapLinearFilter,
- 9987: LinearMipmapLinearFilter
- };
- var WEBGL_WRAPPINGS = {
- 33071: ClampToEdgeWrapping,
- 33648: MirroredRepeatWrapping,
- 10497: RepeatWrapping
- };
- var WEBGL_TYPE_SIZES = {
- 'SCALAR': 1,
- 'VEC2': 2,
- 'VEC3': 3,
- 'VEC4': 4,
- 'MAT2': 4,
- 'MAT3': 9,
- 'MAT4': 16
- };
- var ATTRIBUTES = {
- POSITION: 'position',
- NORMAL: 'normal',
- TANGENT: 'tangent',
- TEXCOORD_0: 'uv',
- TEXCOORD_1: 'uv2',
- COLOR_0: 'color',
- WEIGHTS_0: 'skinWeight',
- JOINTS_0: 'skinIndex',
- };
- var PATH_PROPERTIES = {
- scale: 'scale',
- translation: 'position',
- rotation: 'quaternion',
- weights: 'morphTargetInfluences'
- };
- var INTERPOLATION = {
- CUBICSPLINE: undefined, // We use a custom interpolant (GLTFCubicSplineInterpolation) for CUBICSPLINE tracks. Each
- // keyframe track will be initialized with a default interpolation type, then modified.
- LINEAR: InterpolateLinear,
- STEP: InterpolateDiscrete
- };
- var ALPHA_MODES = {
- OPAQUE: 'OPAQUE',
- MASK: 'MASK',
- BLEND: 'BLEND'
- };
- /* UTILITY FUNCTIONS */
- function resolveURL( url, path ) {
- // Invalid URL
- if ( typeof url !== 'string' || url === '' ) return '';
- // Host Relative URL
- if ( /^https?:\/\//i.test( path ) && /^\//.test( url ) ) {
- path = path.replace( /(^https?:\/\/[^\/]+).*/i, '$1' );
- }
- // Absolute URL http://,https://,//
- if ( /^(https?:)?\/\//i.test( url ) ) return url;
- // Data URI
- if ( /^data:.*,.*$/i.test( url ) ) return url;
- // Blob URL
- if ( /^blob:.*$/i.test( url ) ) return url;
- // Relative URL
- return path + url;
- }
- /**
- * Specification: https://github.com/KhronosGroup/glTF/blob/master/specification/2.0/README.md#default-material
- */
- function createDefaultMaterial( cache ) {
- if ( cache[ 'DefaultMaterial' ] === undefined ) {
- cache[ 'DefaultMaterial' ] = new MeshStandardMaterial( {
- color: 0xFFFFFF,
- emissive: 0x000000,
- metalness: 1,
- roughness: 1,
- transparent: false,
- depthTest: true,
- side: FrontSide
- } );
- }
- return cache[ 'DefaultMaterial' ];
- }
- function addUnknownExtensionsToUserData( knownExtensions, object, objectDef ) {
- // Add unknown glTF extensions to an object's userData.
- for ( var name in objectDef.extensions ) {
- if ( knownExtensions[ name ] === undefined ) {
- object.userData.gltfExtensions = object.userData.gltfExtensions || {};
- object.userData.gltfExtensions[ name ] = objectDef.extensions[ name ];
- }
- }
- }
- /**
- * @param {Object3D|Material|BufferGeometry} object
- * @param {GLTF.definition} gltfDef
- */
- function assignExtrasToUserData( object, gltfDef ) {
- if ( gltfDef.extras !== undefined ) {
- if ( typeof gltfDef.extras === 'object' ) {
- Object.assign( object.userData, gltfDef.extras );
- } else {
- console.warn( 'THREE.GLTFLoader: Ignoring primitive type .extras, ' + gltfDef.extras );
- }
- }
- }
- /**
- * Specification: https://github.com/KhronosGroup/glTF/blob/master/specification/2.0/README.md#morph-targets
- *
- * @param {BufferGeometry} geometry
- * @param {Array<GLTF.Target>} targets
- * @param {GLTFParser} parser
- * @return {Promise<BufferGeometry>}
- */
- function addMorphTargets( geometry, targets, parser ) {
- var hasMorphPosition = false;
- var hasMorphNormal = false;
- for ( var i = 0, il = targets.length; i < il; i ++ ) {
- var target = targets[ i ];
- if ( target.POSITION !== undefined ) hasMorphPosition = true;
- if ( target.NORMAL !== undefined ) hasMorphNormal = true;
- if ( hasMorphPosition && hasMorphNormal ) break;
- }
- if ( ! hasMorphPosition && ! hasMorphNormal ) return Promise.resolve( geometry );
- var pendingPositionAccessors = [];
- var pendingNormalAccessors = [];
- for ( var i = 0, il = targets.length; i < il; i ++ ) {
- var target = targets[ i ];
- if ( hasMorphPosition ) {
- var pendingAccessor = target.POSITION !== undefined
- ? parser.getDependency( 'accessor', target.POSITION )
- : geometry.attributes.position;
- pendingPositionAccessors.push( pendingAccessor );
- }
- if ( hasMorphNormal ) {
- var pendingAccessor = target.NORMAL !== undefined
- ? parser.getDependency( 'accessor', target.NORMAL )
- : geometry.attributes.normal;
- pendingNormalAccessors.push( pendingAccessor );
- }
- }
- return Promise.all( [
- Promise.all( pendingPositionAccessors ),
- Promise.all( pendingNormalAccessors )
- ] ).then( function ( accessors ) {
- var morphPositions = accessors[ 0 ];
- var morphNormals = accessors[ 1 ];
- if ( hasMorphPosition ) geometry.morphAttributes.position = morphPositions;
- if ( hasMorphNormal ) geometry.morphAttributes.normal = morphNormals;
- geometry.morphTargetsRelative = true;
- return geometry;
- } );
- }
- /**
- * @param {Mesh} mesh
- * @param {GLTF.Mesh} meshDef
- */
- function updateMorphTargets( mesh, meshDef ) {
- mesh.updateMorphTargets();
- if ( meshDef.weights !== undefined ) {
- for ( var i = 0, il = meshDef.weights.length; i < il; i ++ ) {
- mesh.morphTargetInfluences[ i ] = meshDef.weights[ i ];
- }
- }
- // .extras has user-defined data, so check that .extras.targetNames is an array.
- if ( meshDef.extras && Array.isArray( meshDef.extras.targetNames ) ) {
- var targetNames = meshDef.extras.targetNames;
- if ( mesh.morphTargetInfluences.length === targetNames.length ) {
- mesh.morphTargetDictionary = {};
- for ( var i = 0, il = targetNames.length; i < il; i ++ ) {
- mesh.morphTargetDictionary[ targetNames[ i ] ] = i;
- }
- } else {
- console.warn( 'THREE.GLTFLoader: Invalid extras.targetNames length. Ignoring names.' );
- }
- }
- }
- function createPrimitiveKey( primitiveDef ) {
- var dracoExtension = primitiveDef.extensions && primitiveDef.extensions[ EXTENSIONS.KHR_DRACO_MESH_COMPRESSION ];
- var geometryKey;
- if ( dracoExtension ) {
- geometryKey = 'draco:' + dracoExtension.bufferView
- + ':' + dracoExtension.indices
- + ':' + createAttributesKey( dracoExtension.attributes );
- } else {
- geometryKey = primitiveDef.indices + ':' + createAttributesKey( primitiveDef.attributes ) + ':' + primitiveDef.mode;
- }
- return geometryKey;
- }
- function createAttributesKey( attributes ) {
- var attributesKey = '';
- var keys = Object.keys( attributes ).sort();
- for ( var i = 0, il = keys.length; i < il; i ++ ) {
- attributesKey += keys[ i ] + ':' + attributes[ keys[ i ] ] + ';';
- }
- return attributesKey;
- }
- /* GLTF PARSER */
- function GLTFParser( json, options ) {
- this.json = json || {};
- this.extensions = {};
- this.plugins = {};
- this.options = options || {};
- // loader object cache
- this.cache = new GLTFRegistry();
- // associations between Three.js objects and glTF elements
- this.associations = new Map();
- // BufferGeometry caching
- this.primitiveCache = {};
- // Object3D instance caches
- this.meshCache = { refs: {}, uses: {} };
- this.cameraCache = { refs: {}, uses: {} };
- this.lightCache = { refs: {}, uses: {} };
- // Track node names, to ensure no duplicates
- this.nodeNamesUsed = {};
- // Use an ImageBitmapLoader if imageBitmaps are supported. Moves much of the
- // expensive work of uploading a texture to the GPU off the main thread.
- if ( typeof createImageBitmap !== 'undefined' && /Firefox/.test( navigator.userAgent ) === false ) {
- this.textureLoader = new ImageBitmapLoader( this.options.manager );
- } else {
- this.textureLoader = new TextureLoader( this.options.manager );
- }
- this.textureLoader.setCrossOrigin( this.options.crossOrigin );
- this.fileLoader = new FileLoader( this.options.manager );
- this.fileLoader.setResponseType( 'arraybuffer' );
- if ( this.options.crossOrigin === 'use-credentials' ) {
- this.fileLoader.setWithCredentials( true );
- }
- }
- GLTFParser.prototype.setExtensions = function ( extensions ) {
- this.extensions = extensions;
- };
- GLTFParser.prototype.setPlugins = function ( plugins ) {
- this.plugins = plugins;
- };
- GLTFParser.prototype.parse = function ( onLoad, onError ) {
- var parser = this;
- var json = this.json;
- var extensions = this.extensions;
- // Clear the loader cache
- this.cache.removeAll();
- // Mark the special nodes/meshes in json for efficient parse
- this._invokeAll( function ( ext ) {
- return ext._markDefs && ext._markDefs();
- } );
- Promise.all( [
- this.getDependencies( 'scene' ),
- this.getDependencies( 'animation' ),
- this.getDependencies( 'camera' ),
- ] ).then( function ( dependencies ) {
- var result = {
- scene: dependencies[ 0 ][ json.scene || 0 ],
- scenes: dependencies[ 0 ],
- animations: dependencies[ 1 ],
- cameras: dependencies[ 2 ],
- asset: json.asset,
- parser: parser,
- userData: {}
- };
- addUnknownExtensionsToUserData( extensions, result, json );
- assignExtrasToUserData( result, json );
- onLoad( result );
- } ).catch( onError );
- };
- /**
- * Marks the special nodes/meshes in json for efficient parse.
- */
- GLTFParser.prototype._markDefs = function () {
- var nodeDefs = this.json.nodes || [];
- var skinDefs = this.json.skins || [];
- var meshDefs = this.json.meshes || [];
- // Nothing in the node definition indicates whether it is a Bone or an
- // Object3D. Use the skins' joint references to mark bones.
- for ( var skinIndex = 0, skinLength = skinDefs.length; skinIndex < skinLength; skinIndex ++ ) {
- var joints = skinDefs[ skinIndex ].joints;
- for ( var i = 0, il = joints.length; i < il; i ++ ) {
- nodeDefs[ joints[ i ] ].isBone = true;
- }
- }
- // Iterate over all nodes, marking references to shared resources,
- // as well as skeleton joints.
- for ( var nodeIndex = 0, nodeLength = nodeDefs.length; nodeIndex < nodeLength; nodeIndex ++ ) {
- var nodeDef = nodeDefs[ nodeIndex ];
- if ( nodeDef.mesh !== undefined ) {
- this._addNodeRef( this.meshCache, nodeDef.mesh );
- // Nothing in the mesh definition indicates whether it is
- // a SkinnedMesh or Mesh. Use the node's mesh reference
- // to mark SkinnedMesh if node has skin.
- if ( nodeDef.skin !== undefined ) {
- meshDefs[ nodeDef.mesh ].isSkinnedMesh = true;
- }
- }
- if ( nodeDef.camera !== undefined ) {
- this._addNodeRef( this.cameraCache, nodeDef.camera );
- }
- }
- };
- /**
- * Counts references to shared node / Object3D resources. These resources
- * can be reused, or "instantiated", at multiple nodes in the scene
- * hierarchy. Mesh, Camera, and Light instances are instantiated and must
- * be marked. Non-scenegraph resources (like Materials, Geometries, and
- * Textures) can be reused directly and are not marked here.
- *
- * Example: CesiumMilkTruck sample model reuses "Wheel" meshes.
- */
- GLTFParser.prototype._addNodeRef = function ( cache, index ) {
- if ( index === undefined ) return;
- if ( cache.refs[ index ] === undefined ) {
- cache.refs[ index ] = cache.uses[ index ] = 0;
- }
- cache.refs[ index ] ++;
- };
- /** Returns a reference to a shared resource, cloning it if necessary. */
- GLTFParser.prototype._getNodeRef = function ( cache, index, object ) {
- if ( cache.refs[ index ] <= 1 ) return object;
- var ref = object.clone();
- ref.name += '_instance_' + ( cache.uses[ index ] ++ );
- return ref;
- };
- GLTFParser.prototype._invokeOne = function ( func ) {
- var extensions = Object.values( this.plugins );
- extensions.push( this );
- for ( var i = 0; i < extensions.length; i ++ ) {
- var result = func( extensions[ i ] );
- if ( result ) return result;
- }
- };
- GLTFParser.prototype._invokeAll = function ( func ) {
- var extensions = Object.values( this.plugins );
- extensions.unshift( this );
- var pending = [];
- for ( var i = 0; i < extensions.length; i ++ ) {
- var result = func( extensions[ i ] );
- if ( result ) pending.push( result );
- }
- return pending;
- };
- /**
- * Requests the specified dependency asynchronously, with caching.
- * @param {string} type
- * @param {number} index
- * @return {Promise<Object3D|Material|THREE.Texture|AnimationClip|ArrayBuffer|Object>}
- */
- GLTFParser.prototype.getDependency = function ( type, index ) {
- var cacheKey = type + ':' + index;
- var dependency = this.cache.get( cacheKey );
- if ( ! dependency ) {
- switch ( type ) {
- case 'scene':
- dependency = this.loadScene( index );
- break;
- case 'node':
- dependency = this.loadNode( index );
- break;
- case 'mesh':
- dependency = this._invokeOne( function ( ext ) {
- return ext.loadMesh && ext.loadMesh( index );
- } );
- break;
- case 'accessor':
- dependency = this.loadAccessor( index );
- break;
- case 'bufferView':
- dependency = this._invokeOne( function ( ext ) {
- return ext.loadBufferView && ext.loadBufferView( index );
- } );
- break;
- case 'buffer':
- dependency = this.loadBuffer( index );
- break;
- case 'material':
- dependency = this._invokeOne( function ( ext ) {
- return ext.loadMaterial && ext.loadMaterial( index );
- } );
- break;
- case 'texture':
- dependency = this._invokeOne( function ( ext ) {
- return ext.loadTexture && ext.loadTexture( index );
- } );
- break;
- case 'skin':
- dependency = this.loadSkin( index );
- break;
- case 'animation':
- dependency = this.loadAnimation( index );
- break;
- case 'camera':
- dependency = this.loadCamera( index );
- break;
- default:
- throw new Error( 'Unknown type: ' + type );
- }
- this.cache.add( cacheKey, dependency );
- }
- return dependency;
- };
- /**
- * Requests all dependencies of the specified type asynchronously, with caching.
- * @param {string} type
- * @return {Promise<Array<Object>>}
- */
- GLTFParser.prototype.getDependencies = function ( type ) {
- var dependencies = this.cache.get( type );
- if ( ! dependencies ) {
- var parser = this;
- var defs = this.json[ type + ( type === 'mesh' ? 'es' : 's' ) ] || [];
- dependencies = Promise.all( defs.map( function ( def, index ) {
- return parser.getDependency( type, index );
- } ) );
- this.cache.add( type, dependencies );
- }
- return dependencies;
- };
- /**
- * Specification: https://github.com/KhronosGroup/glTF/blob/master/specification/2.0/README.md#buffers-and-buffer-views
- * @param {number} bufferIndex
- * @return {Promise<ArrayBuffer>}
- */
- GLTFParser.prototype.loadBuffer = function ( bufferIndex ) {
- var bufferDef = this.json.buffers[ bufferIndex ];
- var loader = this.fileLoader;
- if ( bufferDef.type && bufferDef.type !== 'arraybuffer' ) {
- throw new Error( 'THREE.GLTFLoader: ' + bufferDef.type + ' buffer type is not supported.' );
- }
- // If present, GLB container is required to be the first buffer.
- if ( bufferDef.uri === undefined && bufferIndex === 0 ) {
- return Promise.resolve( this.extensions[ EXTENSIONS.KHR_BINARY_GLTF ].body );
- }
- var options = this.options;
- return new Promise( function ( resolve, reject ) {
- loader.load( resolveURL( bufferDef.uri, options.path ), resolve, undefined, function () {
- reject( new Error( 'THREE.GLTFLoader: Failed to load buffer "' + bufferDef.uri + '".' ) );
- } );
- } );
- };
- /**
- * Specification: https://github.com/KhronosGroup/glTF/blob/master/specification/2.0/README.md#buffers-and-buffer-views
- * @param {number} bufferViewIndex
- * @return {Promise<ArrayBuffer>}
- */
- GLTFParser.prototype.loadBufferView = function ( bufferViewIndex ) {
- var bufferViewDef = this.json.bufferViews[ bufferViewIndex ];
- return this.getDependency( 'buffer', bufferViewDef.buffer ).then( function ( buffer ) {
- var byteLength = bufferViewDef.byteLength || 0;
- var byteOffset = bufferViewDef.byteOffset || 0;
- return buffer.slice( byteOffset, byteOffset + byteLength );
- } );
- };
- /**
- * Specification: https://github.com/KhronosGroup/glTF/blob/master/specification/2.0/README.md#accessors
- * @param {number} accessorIndex
- * @return {Promise<BufferAttribute|InterleavedBufferAttribute>}
- */
- GLTFParser.prototype.loadAccessor = function ( accessorIndex ) {
- var parser = this;
- var json = this.json;
- var accessorDef = this.json.accessors[ accessorIndex ];
- if ( accessorDef.bufferView === undefined && accessorDef.sparse === undefined ) {
- // Ignore empty accessors, which may be used to declare runtime
- // information about attributes coming from another source (e.g. Draco
- // compression extension).
- return Promise.resolve( null );
- }
- var pendingBufferViews = [];
- if ( accessorDef.bufferView !== undefined ) {
- pendingBufferViews.push( this.getDependency( 'bufferView', accessorDef.bufferView ) );
- } else {
- pendingBufferViews.push( null );
- }
- if ( accessorDef.sparse !== undefined ) {
- pendingBufferViews.push( this.getDependency( 'bufferView', accessorDef.sparse.indices.bufferView ) );
- pendingBufferViews.push( this.getDependency( 'bufferView', accessorDef.sparse.values.bufferView ) );
- }
- return Promise.all( pendingBufferViews ).then( function ( bufferViews ) {
- var bufferView = bufferViews[ 0 ];
- var itemSize = WEBGL_TYPE_SIZES[ accessorDef.type ];
- var TypedArray = WEBGL_COMPONENT_TYPES[ accessorDef.componentType ];
- // For VEC3: itemSize is 3, elementBytes is 4, itemBytes is 12.
- var elementBytes = TypedArray.BYTES_PER_ELEMENT;
- var itemBytes = elementBytes * itemSize;
- var byteOffset = accessorDef.byteOffset || 0;
- var byteStride = accessorDef.bufferView !== undefined ? json.bufferViews[ accessorDef.bufferView ].byteStride : undefined;
- var normalized = accessorDef.normalized === true;
- var array, bufferAttribute;
- // The buffer is not interleaved if the stride is the item size in bytes.
- if ( byteStride && byteStride !== itemBytes ) {
- // Each "slice" of the buffer, as defined by 'count' elements of 'byteStride' bytes, gets its own InterleavedBuffer
- // This makes sure that IBA.count reflects accessor.count properly
- var ibSlice = Math.floor( byteOffset / byteStride );
- var ibCacheKey = 'InterleavedBuffer:' + accessorDef.bufferView + ':' + accessorDef.componentType + ':' + ibSlice + ':' + accessorDef.count;
- var ib = parser.cache.get( ibCacheKey );
- if ( ! ib ) {
- array = new TypedArray( bufferView, ibSlice * byteStride, accessorDef.count * byteStride / elementBytes );
- // Integer parameters to IB/IBA are in array elements, not bytes.
- ib = new InterleavedBuffer( array, byteStride / elementBytes );
- parser.cache.add( ibCacheKey, ib );
- }
- bufferAttribute = new InterleavedBufferAttribute( ib, itemSize, ( byteOffset % byteStride ) / elementBytes, normalized );
- } else {
- if ( bufferView === null ) {
- array = new TypedArray( accessorDef.count * itemSize );
- } else {
- array = new TypedArray( bufferView, byteOffset, accessorDef.count * itemSize );
- }
- bufferAttribute = new BufferAttribute( array, itemSize, normalized );
- }
- // https://github.com/KhronosGroup/glTF/blob/master/specification/2.0/README.md#sparse-accessors
- if ( accessorDef.sparse !== undefined ) {
- var itemSizeIndices = WEBGL_TYPE_SIZES.SCALAR;
- var TypedArrayIndices = WEBGL_COMPONENT_TYPES[ accessorDef.sparse.indices.componentType ];
- var byteOffsetIndices = accessorDef.sparse.indices.byteOffset || 0;
- var byteOffsetValues = accessorDef.sparse.values.byteOffset || 0;
- var sparseIndices = new TypedArrayIndices( bufferViews[ 1 ], byteOffsetIndices, accessorDef.sparse.count * itemSizeIndices );
- var sparseValues = new TypedArray( bufferViews[ 2 ], byteOffsetValues, accessorDef.sparse.count * itemSize );
- if ( bufferView !== null ) {
- // Avoid modifying the original ArrayBuffer, if the bufferView wasn't initialized with zeroes.
- bufferAttribute = new BufferAttribute( bufferAttribute.array.slice(), bufferAttribute.itemSize, bufferAttribute.normalized );
- }
- for ( var i = 0, il = sparseIndices.length; i < il; i ++ ) {
- var index = sparseIndices[ i ];
- bufferAttribute.setX( index, sparseValues[ i * itemSize ] );
- if ( itemSize >= 2 ) bufferAttribute.setY( index, sparseValues[ i * itemSize + 1 ] );
- if ( itemSize >= 3 ) bufferAttribute.setZ( index, sparseValues[ i * itemSize + 2 ] );
- if ( itemSize >= 4 ) bufferAttribute.setW( index, sparseValues[ i * itemSize + 3 ] );
- if ( itemSize >= 5 ) throw new Error( 'THREE.GLTFLoader: Unsupported itemSize in sparse BufferAttribute.' );
- }
- }
- return bufferAttribute;
- } );
- };
- /**
- * Specification: https://github.com/KhronosGroup/glTF/tree/master/specification/2.0#textures
- * @param {number} textureIndex
- * @return {Promise<THREE.Texture>}
- */
- GLTFParser.prototype.loadTexture = function ( textureIndex ) {
- var parser = this;
- var json = this.json;
- var options = this.options;
- var textureDef = json.textures[ textureIndex ];
- var textureExtensions = textureDef.extensions || {};
- var source;
- if ( textureExtensions[ EXTENSIONS.MSFT_TEXTURE_DDS ] ) {
- source = json.images[ textureExtensions[ EXTENSIONS.MSFT_TEXTURE_DDS ].source ];
- } else {
- source = json.images[ textureDef.source ];
- }
- var loader;
- if ( source.uri ) {
- loader = options.manager.getHandler( source.uri );
- }
- if ( ! loader ) {
- loader = textureExtensions[ EXTENSIONS.MSFT_TEXTURE_DDS ]
- ? parser.extensions[ EXTENSIONS.MSFT_TEXTURE_DDS ].ddsLoader
- : this.textureLoader;
- }
- return this.loadTextureImage( textureIndex, source, loader );
- };
- GLTFParser.prototype.loadTextureImage = function ( textureIndex, source, loader ) {
- var parser = this;
- var json = this.json;
- var options = this.options;
- var textureDef = json.textures[ textureIndex ];
- var URL = self.URL || self.webkitURL;
- var sourceURI = source.uri;
- var isObjectURL = false;
- var hasAlpha = true;
- if ( source.mimeType === 'image/jpeg' ) hasAlpha = false;
- if ( source.bufferView !== undefined ) {
- // Load binary image data from bufferView, if provided.
- sourceURI = parser.getDependency( 'bufferView', source.bufferView ).then( function ( bufferView ) {
- if ( source.mimeType === 'image/png' ) {
- // Inspect the PNG 'IHDR' chunk to determine whether the image could have an
- // alpha channel. This check is conservative — the image could have an alpha
- // channel with all values == 1, and the indexed type (colorType == 3) only
- // sometimes contains alpha.
- //
- // https://en.wikipedia.org/wiki/Portable_Network_Graphics#File_header
- var colorType = new DataView( bufferView, 25, 1 ).getUint8( 0, false );
- hasAlpha = colorType === 6 || colorType === 4 || colorType === 3;
- }
- isObjectURL = true;
- var blob = new Blob( [ bufferView ], { type: source.mimeType } );
- sourceURI = URL.createObjectURL( blob );
- return sourceURI;
- } );
- }
- return Promise.resolve( sourceURI ).then( function ( sourceURI ) {
- return new Promise( function ( resolve, reject ) {
- var onLoad = resolve;
- if ( loader.isImageBitmapLoader === true ) {
- onLoad = function ( imageBitmap ) {
- resolve( new CanvasTexture( imageBitmap ) );
- };
- }
- loader.load( resolveURL( sourceURI, options.path ), onLoad, undefined, reject );
- } );
- } ).then( function ( texture ) {
- // Clean up resources and configure Texture.
- if ( isObjectURL === true ) {
- URL.revokeObjectURL( sourceURI );
- }
- texture.flipY = false;
- if ( textureDef.name ) texture.name = textureDef.name;
- // When there is definitely no alpha channel in the texture, set RGBFormat to save space.
- if ( ! hasAlpha ) texture.format = RGBFormat;
- var samplers = json.samplers || {};
- var sampler = samplers[ textureDef.sampler ] || {};
- texture.magFilter = WEBGL_FILTERS[ sampler.magFilter ] || LinearFilter;
- texture.minFilter = WEBGL_FILTERS[ sampler.minFilter ] || LinearMipmapLinearFilter;
- texture.wrapS = WEBGL_WRAPPINGS[ sampler.wrapS ] || RepeatWrapping;
- texture.wrapT = WEBGL_WRAPPINGS[ sampler.wrapT ] || RepeatWrapping;
- parser.associations.set( texture, {
- type: 'textures',
- index: textureIndex
- } );
- return texture;
- } );
- };
- /**
- * Asynchronously assigns a texture to the given material parameters.
- * @param {Object} materialParams
- * @param {string} mapName
- * @param {Object} mapDef
- * @return {Promise}
- */
- GLTFParser.prototype.assignTexture = function ( materialParams, mapName, mapDef ) {
- var parser = this;
- return this.getDependency( 'texture', mapDef.index ).then( function ( texture ) {
- // Materials sample aoMap from UV set 1 and other maps from UV set 0 - this can't be configured
- // However, we will copy UV set 0 to UV set 1 on demand for aoMap
- if ( mapDef.texCoord !== undefined && mapDef.texCoord != 0 && ! ( mapName === 'aoMap' && mapDef.texCoord == 1 ) ) {
- console.warn( 'THREE.GLTFLoader: Custom UV set ' + mapDef.texCoord + ' for texture ' + mapName + ' not yet supported.' );
- }
- if ( parser.extensions[ EXTENSIONS.KHR_TEXTURE_TRANSFORM ] ) {
- var transform = mapDef.extensions !== undefined ? mapDef.extensions[ EXTENSIONS.KHR_TEXTURE_TRANSFORM ] : undefined;
- if ( transform ) {
- var gltfReference = parser.associations.get( texture );
- texture = parser.extensions[ EXTENSIONS.KHR_TEXTURE_TRANSFORM ].extendTexture( texture, transform );
- parser.associations.set( texture, gltfReference );
- }
- }
- materialParams[ mapName ] = texture;
- } );
- };
- /**
- * Assigns final material to a Mesh, Line, or Points instance. The instance
- * already has a material (generated from the glTF material options alone)
- * but reuse of the same glTF material may require multiple threejs materials
- * to accomodate different primitive types, defines, etc. New materials will
- * be created if necessary, and reused from a cache.
- * @param {Object3D} mesh Mesh, Line, or Points instance.
- */
- GLTFParser.prototype.assignFinalMaterial = function ( mesh ) {
- var geometry = mesh.geometry;
- var material = mesh.material;
- var useVertexTangents = geometry.attributes.tangent !== undefined;
- var useVertexColors = geometry.attributes.color !== undefined;
- var useFlatShading = geometry.attributes.normal === undefined;
- var useSkinning = mesh.isSkinnedMesh === true;
- var useMorphTargets = Object.keys( geometry.morphAttributes ).length > 0;
- var useMorphNormals = useMorphTargets && geometry.morphAttributes.normal !== undefined;
- if ( mesh.isPoints ) {
- var cacheKey = 'PointsMaterial:' + material.uuid;
- var pointsMaterial = this.cache.get( cacheKey );
- if ( ! pointsMaterial ) {
- pointsMaterial = new PointsMaterial();
- Material.prototype.copy.call( pointsMaterial, material );
- pointsMaterial.color.copy( material.color );
- pointsMaterial.map = material.map;
- pointsMaterial.sizeAttenuation = false; // glTF spec says points should be 1px
- this.cache.add( cacheKey, pointsMaterial );
- }
- material = pointsMaterial;
- } else if ( mesh.isLine ) {
- var cacheKey = 'LineBasicMaterial:' + material.uuid;
- var lineMaterial = this.cache.get( cacheKey );
- if ( ! lineMaterial ) {
- lineMaterial = new LineBasicMaterial();
- Material.prototype.copy.call( lineMaterial, material );
- lineMaterial.color.copy( material.color );
- this.cache.add( cacheKey, lineMaterial );
- }
- material = lineMaterial;
- }
- // Clone the material if it will be modified
- if ( useVertexTangents || useVertexColors || useFlatShading || useSkinning || useMorphTargets ) {
- var cacheKey = 'ClonedMaterial:' + material.uuid + ':';
- if ( material.isGLTFSpecularGlossinessMaterial ) cacheKey += 'specular-glossiness:';
- if ( useSkinning ) cacheKey += 'skinning:';
- if ( useVertexTangents ) cacheKey += 'vertex-tangents:';
- if ( useVertexColors ) cacheKey += 'vertex-colors:';
- if ( useFlatShading ) cacheKey += 'flat-shading:';
- if ( useMorphTargets ) cacheKey += 'morph-targets:';
- if ( useMorphNormals ) cacheKey += 'morph-normals:';
- var cachedMaterial = this.cache.get( cacheKey );
- if ( ! cachedMaterial ) {
- cachedMaterial = material.clone();
- if ( useSkinning ) cachedMaterial.skinning = true;
- if ( useVertexTangents ) cachedMaterial.vertexTangents = true;
- if ( useVertexColors ) cachedMaterial.vertexColors = true;
- if ( useFlatShading ) cachedMaterial.flatShading = true;
- if ( useMorphTargets ) cachedMaterial.morphTargets = true;
- if ( useMorphNormals ) cachedMaterial.morphNormals = true;
- this.cache.add( cacheKey, cachedMaterial );
- this.associations.set( cachedMaterial, this.associations.get( material ) );
- }
- material = cachedMaterial;
- }
- // workarounds for mesh and geometry
- if ( material.aoMap && geometry.attributes.uv2 === undefined && geometry.attributes.uv !== undefined ) {
- geometry.setAttribute( 'uv2', geometry.attributes.uv );
- }
- // https://github.com/mrdoob/three.js/issues/11438#issuecomment-507003995
- if ( material.normalScale && ! useVertexTangents ) {
- material.normalScale.y = - material.normalScale.y;
- }
- if ( material.clearcoatNormalScale && ! useVertexTangents ) {
- material.clearcoatNormalScale.y = - material.clearcoatNormalScale.y;
- }
- mesh.material = material;
- };
- GLTFParser.prototype.getMaterialType = function ( /* materialIndex */ ) {
- return MeshStandardMaterial;
- };
- /**
- * Specification: https://github.com/KhronosGroup/glTF/blob/master/specification/2.0/README.md#materials
- * @param {number} materialIndex
- * @return {Promise<Material>}
- */
- GLTFParser.prototype.loadMaterial = function ( materialIndex ) {
- var parser = this;
- var json = this.json;
- var extensions = this.extensions;
- var materialDef = json.materials[ materialIndex ];
- var materialType;
- var materialParams = {};
- var materialExtensions = materialDef.extensions || {};
- var pending = [];
- if ( materialExtensions[ EXTENSIONS.KHR_MATERIALS_PBR_SPECULAR_GLOSSINESS ] ) {
- var sgExtension = extensions[ EXTENSIONS.KHR_MATERIALS_PBR_SPECULAR_GLOSSINESS ];
- materialType = sgExtension.getMaterialType();
- pending.push( sgExtension.extendParams( materialParams, materialDef, parser ) );
- } else if ( materialExtensions[ EXTENSIONS.KHR_MATERIALS_UNLIT ] ) {
- var kmuExtension = extensions[ EXTENSIONS.KHR_MATERIALS_UNLIT ];
- materialType = kmuExtension.getMaterialType();
- pending.push( kmuExtension.extendParams( materialParams, materialDef, parser ) );
- } else {
- // Specification:
- // https://github.com/KhronosGroup/glTF/tree/master/specification/2.0#metallic-roughness-material
- var metallicRoughness = materialDef.pbrMetallicRoughness || {};
- materialParams.color = new Color( 1.0, 1.0, 1.0 );
- materialParams.opacity = 1.0;
- if ( Array.isArray( metallicRoughness.baseColorFactor ) ) {
- var array = metallicRoughness.baseColorFactor;
- materialParams.color.fromArray( array );
- materialParams.opacity = array[ 3 ];
- }
- if ( metallicRoughness.baseColorTexture !== undefined ) {
- pending.push( parser.assignTexture( materialParams, 'map', metallicRoughness.baseColorTexture ) );
- }
- materialParams.metalness = metallicRoughness.metallicFactor !== undefined ? metallicRoughness.metallicFactor : 1.0;
- materialParams.roughness = metallicRoughness.roughnessFactor !== undefined ? metallicRoughness.roughnessFactor : 1.0;
- if ( metallicRoughness.metallicRoughnessTexture !== undefined ) {
- pending.push( parser.assignTexture( materialParams, 'metalnessMap', metallicRoughness.metallicRoughnessTexture ) );
- pending.push( parser.assignTexture( materialParams, 'roughnessMap', metallicRoughness.metallicRoughnessTexture ) );
- }
- materialType = this._invokeOne( function ( ext ) {
- return ext.getMaterialType && ext.getMaterialType( materialIndex );
- } );
- pending.push( Promise.all( this._invokeAll( function ( ext ) {
- return ext.extendMaterialParams && ext.extendMaterialParams( materialIndex, materialParams );
- } ) ) );
- }
- if ( materialDef.doubleSided === true ) {
- materialParams.side = DoubleSide;
- }
- var alphaMode = materialDef.alphaMode || ALPHA_MODES.OPAQUE;
- if ( alphaMode === ALPHA_MODES.BLEND ) {
- materialParams.transparent = true;
- // See: https://github.com/mrdoob/three.js/issues/17706
- materialParams.depthWrite = false;
- } else {
- materialParams.transparent = false;
- if ( alphaMode === ALPHA_MODES.MASK ) {
- materialParams.alphaTest = materialDef.alphaCutoff !== undefined ? materialDef.alphaCutoff : 0.5;
- }
- }
- if ( materialDef.normalTexture !== undefined && materialType !== MeshBasicMaterial ) {
- pending.push( parser.assignTexture( materialParams, 'normalMap', materialDef.normalTexture ) );
- materialParams.normalScale = new Vector2$1( 1, 1 );
- if ( materialDef.normalTexture.scale !== undefined ) {
- materialParams.normalScale.set( materialDef.normalTexture.scale, materialDef.normalTexture.scale );
- }
- }
- if ( materialDef.occlusionTexture !== undefined && materialType !== MeshBasicMaterial ) {
- pending.push( parser.assignTexture( materialParams, 'aoMap', materialDef.occlusionTexture ) );
- if ( materialDef.occlusionTexture.strength !== undefined ) {
- materialParams.aoMapIntensity = materialDef.occlusionTexture.strength;
- }
- }
- if ( materialDef.emissiveFactor !== undefined && materialType !== MeshBasicMaterial ) {
- materialParams.emissive = new Color().fromArray( materialDef.emissiveFactor );
- }
- if ( materialDef.emissiveTexture !== undefined && materialType !== MeshBasicMaterial ) {
- pending.push( parser.assignTexture( materialParams, 'emissiveMap', materialDef.emissiveTexture ) );
- }
- return Promise.all( pending ).then( function () {
- var material;
- if ( materialType === GLTFMeshStandardSGMaterial ) {
- material = extensions[ EXTENSIONS.KHR_MATERIALS_PBR_SPECULAR_GLOSSINESS ].createMaterial( materialParams );
- } else {
- material = new materialType( materialParams );
- }
- if ( materialDef.name ) material.name = materialDef.name;
- // baseColorTexture, emissiveTexture, and specularGlossinessTexture use sRGB encoding.
- if ( material.map ) material.map.encoding = sRGBEncoding;
- if ( material.emissiveMap ) material.emissiveMap.encoding = sRGBEncoding;
- assignExtrasToUserData( material, materialDef );
- parser.associations.set( material, { type: 'materials', index: materialIndex } );
- if ( materialDef.extensions ) addUnknownExtensionsToUserData( extensions, material, materialDef );
- return material;
- } );
- };
- /** When Object3D instances are targeted by animation, they need unique names. */
- GLTFParser.prototype.createUniqueName = function ( originalName ) {
- var sanitizedName = PropertyBinding.sanitizeNodeName( originalName || '' );
- var name = sanitizedName;
- for ( var i = 1; this.nodeNamesUsed[ name ]; ++ i ) {
- name = sanitizedName + '_' + i;
- }
- this.nodeNamesUsed[ name ] = true;
- return name;
- };
- /**
- * @param {BufferGeometry} geometry
- * @param {GLTF.Primitive} primitiveDef
- * @param {GLTFParser} parser
- */
- function computeBounds( geometry, primitiveDef, parser ) {
- var attributes = primitiveDef.attributes;
- var box = new Box3();
- if ( attributes.POSITION !== undefined ) {
- var accessor = parser.json.accessors[ attributes.POSITION ];
- var min = accessor.min;
- var max = accessor.max;
- // glTF requires 'min' and 'max', but VRM (which extends glTF) currently ignores that requirement.
- if ( min !== undefined && max !== undefined ) {
- box.set(
- new Vector3( min[ 0 ], min[ 1 ], min[ 2 ] ),
- new Vector3( max[ 0 ], max[ 1 ], max[ 2 ] ) );
- } else {
- console.warn( 'THREE.GLTFLoader: Missing min/max properties for accessor POSITION.' );
- return;
- }
- } else {
- return;
- }
- var targets = primitiveDef.targets;
- if ( targets !== undefined ) {
- var maxDisplacement = new Vector3();
- var vector = new Vector3();
- for ( var i = 0, il = targets.length; i < il; i ++ ) {
- var target = targets[ i ];
- if ( target.POSITION !== undefined ) {
- var accessor = parser.json.accessors[ target.POSITION ];
- var min = accessor.min;
- var max = accessor.max;
- // glTF requires 'min' and 'max', but VRM (which extends glTF) currently ignores that requirement.
- if ( min !== undefined && max !== undefined ) {
- // we need to get max of absolute components because target weight is [-1,1]
- vector.setX( Math.max( Math.abs( min[ 0 ] ), Math.abs( max[ 0 ] ) ) );
- vector.setY( Math.max( Math.abs( min[ 1 ] ), Math.abs( max[ 1 ] ) ) );
- vector.setZ( Math.max( Math.abs( min[ 2 ] ), Math.abs( max[ 2 ] ) ) );
- // Note: this assumes that the sum of all weights is at most 1. This isn't quite correct - it's more conservative
- // to assume that each target can have a max weight of 1. However, for some use cases - notably, when morph targets
- // are used to implement key-frame animations and as such only two are active at a time - this results in very large
- // boxes. So for now we make a box that's sometimes a touch too small but is hopefully mostly of reasonable size.
- maxDisplacement.max( vector );
- } else {
- console.warn( 'THREE.GLTFLoader: Missing min/max properties for accessor POSITION.' );
- }
- }
- }
- // As per comment above this box isn't conservative, but has a reasonable size for a very large number of morph targets.
- box.expandByVector( maxDisplacement );
- }
- geometry.boundingBox = box;
- var sphere = new Sphere();
- box.getCenter( sphere.center );
- sphere.radius = box.min.distanceTo( box.max ) / 2;
- geometry.boundingSphere = sphere;
- }
- /**
- * @param {BufferGeometry} geometry
- * @param {GLTF.Primitive} primitiveDef
- * @param {GLTFParser} parser
- * @return {Promise<BufferGeometry>}
- */
- function addPrimitiveAttributes( geometry, primitiveDef, parser ) {
- var attributes = primitiveDef.attributes;
- var pending = [];
- function assignAttributeAccessor( accessorIndex, attributeName ) {
- return parser.getDependency( 'accessor', accessorIndex )
- .then( function ( accessor ) {
- geometry.setAttribute( attributeName, accessor );
- } );
- }
- for ( var gltfAttributeName in attributes ) {
- var threeAttributeName = ATTRIBUTES[ gltfAttributeName ] || gltfAttributeName.toLowerCase();
- // Skip attributes already provided by e.g. Draco extension.
- if ( threeAttributeName in geometry.attributes ) continue;
- pending.push( assignAttributeAccessor( attributes[ gltfAttributeName ], threeAttributeName ) );
- }
- if ( primitiveDef.indices !== undefined && ! geometry.index ) {
- var accessor = parser.getDependency( 'accessor', primitiveDef.indices ).then( function ( accessor ) {
- geometry.setIndex( accessor );
- } );
- pending.push( accessor );
- }
- assignExtrasToUserData( geometry, primitiveDef );
- computeBounds( geometry, primitiveDef, parser );
- return Promise.all( pending ).then( function () {
- return primitiveDef.targets !== undefined
- ? addMorphTargets( geometry, primitiveDef.targets, parser )
- : geometry;
- } );
- }
- /**
- * @param {BufferGeometry} geometry
- * @param {Number} drawMode
- * @return {BufferGeometry}
- */
- function toTrianglesDrawMode( geometry, drawMode ) {
- var index = geometry.getIndex();
- // generate index if not present
- if ( index === null ) {
- var indices = [];
- var position = geometry.getAttribute( 'position' );
- if ( position !== undefined ) {
- for ( var i = 0; i < position.count; i ++ ) {
- indices.push( i );
- }
- geometry.setIndex( indices );
- index = geometry.getIndex();
- } else {
- console.error( 'THREE.GLTFLoader.toTrianglesDrawMode(): Undefined position attribute. Processing not possible.' );
- return geometry;
- }
- }
- //
- var numberOfTriangles = index.count - 2;
- var newIndices = [];
- if ( drawMode === TriangleFanDrawMode ) {
- // gl.TRIANGLE_FAN
- for ( var i = 1; i <= numberOfTriangles; i ++ ) {
- newIndices.push( index.getX( 0 ) );
- newIndices.push( index.getX( i ) );
- newIndices.push( index.getX( i + 1 ) );
- }
- } else {
- // gl.TRIANGLE_STRIP
- for ( var i = 0; i < numberOfTriangles; i ++ ) {
- if ( i % 2 === 0 ) {
- newIndices.push( index.getX( i ) );
- newIndices.push( index.getX( i + 1 ) );
- newIndices.push( index.getX( i + 2 ) );
- } else {
- newIndices.push( index.getX( i + 2 ) );
- newIndices.push( index.getX( i + 1 ) );
- newIndices.push( index.getX( i ) );
- }
- }
- }
- if ( ( newIndices.length / 3 ) !== numberOfTriangles ) {
- console.error( 'THREE.GLTFLoader.toTrianglesDrawMode(): Unable to generate correct amount of triangles.' );
- }
- // build final geometry
- var newGeometry = geometry.clone();
- newGeometry.setIndex( newIndices );
- return newGeometry;
- }
- /**
- * Specification: https://github.com/KhronosGroup/glTF/blob/master/specification/2.0/README.md#geometry
- *
- * Creates BufferGeometries from primitives.
- *
- * @param {Array<GLTF.Primitive>} primitives
- * @return {Promise<Array<BufferGeometry>>}
- */
- GLTFParser.prototype.loadGeometries = function ( primitives ) {
- var parser = this;
- var extensions = this.extensions;
- var cache = this.primitiveCache;
- function createDracoPrimitive( primitive ) {
- return extensions[ EXTENSIONS.KHR_DRACO_MESH_COMPRESSION ]
- .decodePrimitive( primitive, parser )
- .then( function ( geometry ) {
- return addPrimitiveAttributes( geometry, primitive, parser );
- } );
- }
- var pending = [];
- for ( var i = 0, il = primitives.length; i < il; i ++ ) {
- var primitive = primitives[ i ];
- var cacheKey = createPrimitiveKey( primitive );
- // See if we've already created this geometry
- var cached = cache[ cacheKey ];
- if ( cached ) {
- // Use the cached geometry if it exists
- pending.push( cached.promise );
- } else {
- var geometryPromise;
- if ( primitive.extensions && primitive.extensions[ EXTENSIONS.KHR_DRACO_MESH_COMPRESSION ] ) {
- // Use DRACO geometry if available
- geometryPromise = createDracoPrimitive( primitive );
- } else {
- // Otherwise create a new geometry
- geometryPromise = addPrimitiveAttributes( new BufferGeometry(), primitive, parser );
- }
- // Cache this geometry
- cache[ cacheKey ] = { primitive: primitive, promise: geometryPromise };
- pending.push( geometryPromise );
- }
- }
- return Promise.all( pending );
- };
- /**
- * Specification: https://github.com/KhronosGroup/glTF/blob/master/specification/2.0/README.md#meshes
- * @param {number} meshIndex
- * @return {Promise<Group|Mesh|SkinnedMesh>}
- */
- GLTFParser.prototype.loadMesh = function ( meshIndex ) {
- var parser = this;
- var json = this.json;
- var extensions = this.extensions;
- var meshDef = json.meshes[ meshIndex ];
- var primitives = meshDef.primitives;
- var pending = [];
- for ( var i = 0, il = primitives.length; i < il; i ++ ) {
- var material = primitives[ i ].material === undefined
- ? createDefaultMaterial( this.cache )
- : this.getDependency( 'material', primitives[ i ].material );
- pending.push( material );
- }
- pending.push( parser.loadGeometries( primitives ) );
- return Promise.all( pending ).then( function ( results ) {
- var materials = results.slice( 0, results.length - 1 );
- var geometries = results[ results.length - 1 ];
- var meshes = [];
- for ( var i = 0, il = geometries.length; i < il; i ++ ) {
- var geometry = geometries[ i ];
- var primitive = primitives[ i ];
- // 1. create Mesh
- var mesh;
- var material = materials[ i ];
- if ( primitive.mode === WEBGL_CONSTANTS.TRIANGLES ||
- primitive.mode === WEBGL_CONSTANTS.TRIANGLE_STRIP ||
- primitive.mode === WEBGL_CONSTANTS.TRIANGLE_FAN ||
- primitive.mode === undefined ) {
- // .isSkinnedMesh isn't in glTF spec. See ._markDefs()
- mesh = meshDef.isSkinnedMesh === true
- ? new SkinnedMesh( geometry, material )
- : new Mesh( geometry, material );
- if ( mesh.isSkinnedMesh === true && ! mesh.geometry.attributes.skinWeight.normalized ) {
- // we normalize floating point skin weight array to fix malformed assets (see #15319)
- // it's important to skip this for non-float32 data since normalizeSkinWeights assumes non-normalized inputs
- mesh.normalizeSkinWeights();
- }
- if ( primitive.mode === WEBGL_CONSTANTS.TRIANGLE_STRIP ) {
- mesh.geometry = toTrianglesDrawMode( mesh.geometry, TriangleStripDrawMode );
- } else if ( primitive.mode === WEBGL_CONSTANTS.TRIANGLE_FAN ) {
- mesh.geometry = toTrianglesDrawMode( mesh.geometry, TriangleFanDrawMode );
- }
- } else if ( primitive.mode === WEBGL_CONSTANTS.LINES ) {
- mesh = new LineSegments( geometry, material );
- } else if ( primitive.mode === WEBGL_CONSTANTS.LINE_STRIP ) {
- mesh = new Line( geometry, material );
- } else if ( primitive.mode === WEBGL_CONSTANTS.LINE_LOOP ) {
- mesh = new LineLoop( geometry, material );
- } else if ( primitive.mode === WEBGL_CONSTANTS.POINTS ) {
- mesh = new Points( geometry, material );
- } else {
- throw new Error( 'THREE.GLTFLoader: Primitive mode unsupported: ' + primitive.mode );
- }
- if ( Object.keys( mesh.geometry.morphAttributes ).length > 0 ) {
- updateMorphTargets( mesh, meshDef );
- }
- mesh.name = parser.createUniqueName( meshDef.name || ( 'mesh_' + meshIndex ) );
- assignExtrasToUserData( mesh, meshDef );
- if ( primitive.extensions ) addUnknownExtensionsToUserData( extensions, mesh, primitive );
- parser.assignFinalMaterial( mesh );
- meshes.push( mesh );
- }
- if ( meshes.length === 1 ) {
- return meshes[ 0 ];
- }
- var group = new Group();
- for ( var i = 0, il = meshes.length; i < il; i ++ ) {
- group.add( meshes[ i ] );
- }
- return group;
- } );
- };
- /**
- * Specification: https://github.com/KhronosGroup/glTF/tree/master/specification/2.0#cameras
- * @param {number} cameraIndex
- * @return {Promise<THREE.Camera>}
- */
- GLTFParser.prototype.loadCamera = function ( cameraIndex ) {
- var camera;
- var cameraDef = this.json.cameras[ cameraIndex ];
- var params = cameraDef[ cameraDef.type ];
- if ( ! params ) {
- console.warn( 'THREE.GLTFLoader: Missing camera parameters.' );
- return;
- }
- if ( cameraDef.type === 'perspective' ) {
- camera = new PerspectiveCamera( MathUtils.radToDeg( params.yfov ), params.aspectRatio || 1, params.znear || 1, params.zfar || 2e6 );
- } else if ( cameraDef.type === 'orthographic' ) {
- camera = new OrthographicCamera( - params.xmag, params.xmag, params.ymag, - params.ymag, params.znear, params.zfar );
- }
- if ( cameraDef.name ) camera.name = this.createUniqueName( cameraDef.name );
- assignExtrasToUserData( camera, cameraDef );
- return Promise.resolve( camera );
- };
- /**
- * Specification: https://github.com/KhronosGroup/glTF/tree/master/specification/2.0#skins
- * @param {number} skinIndex
- * @return {Promise<Object>}
- */
- GLTFParser.prototype.loadSkin = function ( skinIndex ) {
- var skinDef = this.json.skins[ skinIndex ];
- var skinEntry = { joints: skinDef.joints };
- if ( skinDef.inverseBindMatrices === undefined ) {
- return Promise.resolve( skinEntry );
- }
- return this.getDependency( 'accessor', skinDef.inverseBindMatrices ).then( function ( accessor ) {
- skinEntry.inverseBindMatrices = accessor;
- return skinEntry;
- } );
- };
- /**
- * Specification: https://github.com/KhronosGroup/glTF/tree/master/specification/2.0#animations
- * @param {number} animationIndex
- * @return {Promise<AnimationClip>}
- */
- GLTFParser.prototype.loadAnimation = function ( animationIndex ) {
- var json = this.json;
- var animationDef = json.animations[ animationIndex ];
- var pendingNodes = [];
- var pendingInputAccessors = [];
- var pendingOutputAccessors = [];
- var pendingSamplers = [];
- var pendingTargets = [];
- for ( var i = 0, il = animationDef.channels.length; i < il; i ++ ) {
- var channel = animationDef.channels[ i ];
- var sampler = animationDef.samplers[ channel.sampler ];
- var target = channel.target;
- var name = target.node !== undefined ? target.node : target.id; // NOTE: target.id is deprecated.
- var input = animationDef.parameters !== undefined ? animationDef.parameters[ sampler.input ] : sampler.input;
- var output = animationDef.parameters !== undefined ? animationDef.parameters[ sampler.output ] : sampler.output;
- pendingNodes.push( this.getDependency( 'node', name ) );
- pendingInputAccessors.push( this.getDependency( 'accessor', input ) );
- pendingOutputAccessors.push( this.getDependency( 'accessor', output ) );
- pendingSamplers.push( sampler );
- pendingTargets.push( target );
- }
- return Promise.all( [
- Promise.all( pendingNodes ),
- Promise.all( pendingInputAccessors ),
- Promise.all( pendingOutputAccessors ),
- Promise.all( pendingSamplers ),
- Promise.all( pendingTargets )
- ] ).then( function ( dependencies ) {
- var nodes = dependencies[ 0 ];
- var inputAccessors = dependencies[ 1 ];
- var outputAccessors = dependencies[ 2 ];
- var samplers = dependencies[ 3 ];
- var targets = dependencies[ 4 ];
- var tracks = [];
- for ( var i = 0, il = nodes.length; i < il; i ++ ) {
- var node = nodes[ i ];
- var inputAccessor = inputAccessors[ i ];
- var outputAccessor = outputAccessors[ i ];
- var sampler = samplers[ i ];
- var target = targets[ i ];
- if ( node === undefined ) continue;
- node.updateMatrix();
- node.matrixAutoUpdate = true;
- var TypedKeyframeTrack;
- switch ( PATH_PROPERTIES[ target.path ] ) {
- case PATH_PROPERTIES.weights:
- TypedKeyframeTrack = NumberKeyframeTrack;
- break;
- case PATH_PROPERTIES.rotation:
- TypedKeyframeTrack = QuaternionKeyframeTrack;
- break;
- case PATH_PROPERTIES.position:
- case PATH_PROPERTIES.scale:
- default:
- TypedKeyframeTrack = VectorKeyframeTrack;
- break;
- }
- var targetName = node.name ? node.name : node.uuid;
- var interpolation = sampler.interpolation !== undefined ? INTERPOLATION[ sampler.interpolation ] : InterpolateLinear;
- var targetNames = [];
- if ( PATH_PROPERTIES[ target.path ] === PATH_PROPERTIES.weights ) {
- // Node may be a Group (glTF mesh with several primitives) or a Mesh.
- node.traverse( function ( object ) {
- if ( object.isMesh === true && object.morphTargetInfluences ) {
- targetNames.push( object.name ? object.name : object.uuid );
- }
- } );
- } else {
- targetNames.push( targetName );
- }
- var outputArray = outputAccessor.array;
- if ( outputAccessor.normalized ) {
- var scale;
- if ( outputArray.constructor === Int8Array ) {
- scale = 1 / 127;
- } else if ( outputArray.constructor === Uint8Array ) {
- scale = 1 / 255;
- } else if ( outputArray.constructor == Int16Array ) {
- scale = 1 / 32767;
- } else if ( outputArray.constructor === Uint16Array ) {
- scale = 1 / 65535;
- } else {
- throw new Error( 'THREE.GLTFLoader: Unsupported output accessor component type.' );
- }
- var scaled = new Float32Array( outputArray.length );
- for ( var j = 0, jl = outputArray.length; j < jl; j ++ ) {
- scaled[ j ] = outputArray[ j ] * scale;
- }
- outputArray = scaled;
- }
- for ( var j = 0, jl = targetNames.length; j < jl; j ++ ) {
- var track = new TypedKeyframeTrack(
- targetNames[ j ] + '.' + PATH_PROPERTIES[ target.path ],
- inputAccessor.array,
- outputArray,
- interpolation
- );
- // Override interpolation with custom factory method.
- if ( sampler.interpolation === 'CUBICSPLINE' ) {
- track.createInterpolant = function InterpolantFactoryMethodGLTFCubicSpline( result ) {
- // A CUBICSPLINE keyframe in glTF has three output values for each input value,
- // representing inTangent, splineVertex, and outTangent. As a result, track.getValueSize()
- // must be divided by three to get the interpolant's sampleSize argument.
- return new GLTFCubicSplineInterpolant( this.times, this.values, this.getValueSize() / 3, result );
- };
- // Mark as CUBICSPLINE. `track.getInterpolation()` doesn't support custom interpolants.
- track.createInterpolant.isInterpolantFactoryMethodGLTFCubicSpline = true;
- }
- tracks.push( track );
- }
- }
- var name = animationDef.name ? animationDef.name : 'animation_' + animationIndex;
- return new AnimationClip( name, undefined, tracks );
- } );
- };
- /**
- * Specification: https://github.com/KhronosGroup/glTF/tree/master/specification/2.0#nodes-and-hierarchy
- * @param {number} nodeIndex
- * @return {Promise<Object3D>}
- */
- GLTFParser.prototype.loadNode = function ( nodeIndex ) {
- var json = this.json;
- var extensions = this.extensions;
- var parser = this;
- var nodeDef = json.nodes[ nodeIndex ];
- // reserve node's name before its dependencies, so the root has the intended name.
- var nodeName = nodeDef.name ? parser.createUniqueName( nodeDef.name ) : '';
- return ( function () {
- var pending = [];
- if ( nodeDef.mesh !== undefined ) {
- pending.push( parser.getDependency( 'mesh', nodeDef.mesh ).then( function ( mesh ) {
- var node = parser._getNodeRef( parser.meshCache, nodeDef.mesh, mesh );
- // if weights are provided on the node, override weights on the mesh.
- if ( nodeDef.weights !== undefined ) {
- node.traverse( function ( o ) {
- if ( ! o.isMesh ) return;
- for ( var i = 0, il = nodeDef.weights.length; i < il; i ++ ) {
- o.morphTargetInfluences[ i ] = nodeDef.weights[ i ];
- }
- } );
- }
- return node;
- } ) );
- }
- if ( nodeDef.camera !== undefined ) {
- pending.push( parser.getDependency( 'camera', nodeDef.camera ).then( function ( camera ) {
- return parser._getNodeRef( parser.cameraCache, nodeDef.camera, camera );
- } ) );
- }
- parser._invokeAll( function ( ext ) {
- return ext.createNodeAttachment && ext.createNodeAttachment( nodeIndex );
- } ).forEach( function ( promise ) {
- pending.push( promise );
- } );
- return Promise.all( pending );
- }() ).then( function ( objects ) {
- var node;
- // .isBone isn't in glTF spec. See ._markDefs
- if ( nodeDef.isBone === true ) {
- node = new Bone();
- } else if ( objects.length > 1 ) {
- node = new Group();
- } else if ( objects.length === 1 ) {
- node = objects[ 0 ];
- } else {
- node = new Object3D();
- }
- if ( node !== objects[ 0 ] ) {
- for ( var i = 0, il = objects.length; i < il; i ++ ) {
- node.add( objects[ i ] );
- }
- }
- if ( nodeDef.name ) {
- node.userData.name = nodeDef.name;
- node.name = nodeName;
- }
- assignExtrasToUserData( node, nodeDef );
- if ( nodeDef.extensions ) addUnknownExtensionsToUserData( extensions, node, nodeDef );
- if ( nodeDef.matrix !== undefined ) {
- var matrix = new Matrix4();
- matrix.fromArray( nodeDef.matrix );
- node.applyMatrix4( matrix );
- } else {
- if ( nodeDef.translation !== undefined ) {
- node.position.fromArray( nodeDef.translation );
- }
- if ( nodeDef.rotation !== undefined ) {
- node.quaternion.fromArray( nodeDef.rotation );
- }
- if ( nodeDef.scale !== undefined ) {
- node.scale.fromArray( nodeDef.scale );
- }
- }
- parser.associations.set( node, { type: 'nodes', index: nodeIndex } );
- return node;
- } );
- };
- /**
- * Specification: https://github.com/KhronosGroup/glTF/tree/master/specification/2.0#scenes
- * @param {number} sceneIndex
- * @return {Promise<Group>}
- */
- GLTFParser.prototype.loadScene = function () {
- // scene node hierachy builder
- function buildNodeHierachy( nodeId, parentObject, json, parser ) {
- var nodeDef = json.nodes[ nodeId ];
- return parser.getDependency( 'node', nodeId ).then( function ( node ) {
- if ( nodeDef.skin === undefined ) return node;
- // build skeleton here as well
- var skinEntry;
- return parser.getDependency( 'skin', nodeDef.skin ).then( function ( skin ) {
- skinEntry = skin;
- var pendingJoints = [];
- for ( var i = 0, il = skinEntry.joints.length; i < il; i ++ ) {
- pendingJoints.push( parser.getDependency( 'node', skinEntry.joints[ i ] ) );
- }
- return Promise.all( pendingJoints );
- } ).then( function ( jointNodes ) {
- node.traverse( function ( mesh ) {
- if ( ! mesh.isMesh ) return;
- var bones = [];
- var boneInverses = [];
- for ( var j = 0, jl = jointNodes.length; j < jl; j ++ ) {
- var jointNode = jointNodes[ j ];
- if ( jointNode ) {
- bones.push( jointNode );
- var mat = new Matrix4();
- if ( skinEntry.inverseBindMatrices !== undefined ) {
- mat.fromArray( skinEntry.inverseBindMatrices.array, j * 16 );
- }
- boneInverses.push( mat );
- } else {
- console.warn( 'THREE.GLTFLoader: Joint "%s" could not be found.', skinEntry.joints[ j ] );
- }
- }
- mesh.bind( new Skeleton( bones, boneInverses ), mesh.matrixWorld );
- } );
- return node;
- } );
- } ).then( function ( node ) {
- // build node hierachy
- parentObject.add( node );
- var pending = [];
- if ( nodeDef.children ) {
- var children = nodeDef.children;
- for ( var i = 0, il = children.length; i < il; i ++ ) {
- var child = children[ i ];
- pending.push( buildNodeHierachy( child, node, json, parser ) );
- }
- }
- return Promise.all( pending );
- } );
- }
- return function loadScene( sceneIndex ) {
- var json = this.json;
- var extensions = this.extensions;
- var sceneDef = this.json.scenes[ sceneIndex ];
- var parser = this;
- // Loader returns Group, not Scene.
- // See: https://github.com/mrdoob/three.js/issues/18342#issuecomment-578981172
- var scene = new Group();
- if ( sceneDef.name ) scene.name = parser.createUniqueName( sceneDef.name );
- assignExtrasToUserData( scene, sceneDef );
- if ( sceneDef.extensions ) addUnknownExtensionsToUserData( extensions, scene, sceneDef );
- var nodeIds = sceneDef.nodes || [];
- var pending = [];
- for ( var i = 0, il = nodeIds.length; i < il; i ++ ) {
- pending.push( buildNodeHierachy( nodeIds[ i ], scene, json, parser ) );
- }
- return Promise.all( pending ).then( function () {
- return scene;
- } );
- };
- }();
- return GLTFLoader;
- } )();
- /**
- * @webxr-input-profiles/motion-controllers 1.0.0 https://github.com/immersive-web/webxr-input-profiles
- */
- const Constants = {
- Handedness: Object.freeze({
- NONE: 'none',
- LEFT: 'left',
- RIGHT: 'right'
- }),
- ComponentState: Object.freeze({
- DEFAULT: 'default',
- TOUCHED: 'touched',
- PRESSED: 'pressed'
- }),
- ComponentProperty: Object.freeze({
- BUTTON: 'button',
- X_AXIS: 'xAxis',
- Y_AXIS: 'yAxis',
- STATE: 'state'
- }),
- ComponentType: Object.freeze({
- TRIGGER: 'trigger',
- SQUEEZE: 'squeeze',
- TOUCHPAD: 'touchpad',
- THUMBSTICK: 'thumbstick',
- BUTTON: 'button'
- }),
- ButtonTouchThreshold: 0.05,
- AxisTouchThreshold: 0.1,
- VisualResponseProperty: Object.freeze({
- TRANSFORM: 'transform',
- VISIBILITY: 'visibility'
- })
- };
- /**
- * @description Static helper function to fetch a JSON file and turn it into a JS object
- * @param {string} path - Path to JSON file to be fetched
- */
- async function fetchJsonFile(path) {
- const response = await fetch(path);
- if (!response.ok) {
- throw new Error(response.statusText);
- } else {
- return response.json();
- }
- }
- async function fetchProfilesList(basePath) {
- if (!basePath) {
- throw new Error('No basePath supplied');
- }
- const profileListFileName = 'profilesList.json';
- const profilesList = await fetchJsonFile(`${basePath}/${profileListFileName}`);
- return profilesList;
- }
- async function fetchProfile(xrInputSource, basePath, defaultProfile = null, getAssetPath = true) {
- if (!xrInputSource) {
- throw new Error('No xrInputSource supplied');
- }
- if (!basePath) {
- throw new Error('No basePath supplied');
- }
- // Get the list of profiles
- const supportedProfilesList = await fetchProfilesList(basePath);
- // Find the relative path to the first requested profile that is recognized
- let match;
- xrInputSource.profiles.some((profileId) => {
- const supportedProfile = supportedProfilesList[profileId];
- if (supportedProfile) {
- match = {
- profileId,
- profilePath: `${basePath}/${supportedProfile.path}`,
- deprecated: !!supportedProfile.deprecated
- };
- }
- return !!match;
- });
- if (!match) {
- if (!defaultProfile) {
- throw new Error('No matching profile name found');
- }
- const supportedProfile = supportedProfilesList[defaultProfile];
- if (!supportedProfile) {
- throw new Error(`No matching profile name found and default profile "${defaultProfile}" missing.`);
- }
- match = {
- profileId: defaultProfile,
- profilePath: `${basePath}/${supportedProfile.path}`,
- deprecated: !!supportedProfile.deprecated
- };
- }
- const profile = await fetchJsonFile(match.profilePath);
- let assetPath;
- if (getAssetPath) {
- let layout;
- if (xrInputSource.handedness === 'any') {
- layout = profile.layouts[Object.keys(profile.layouts)[0]];
- } else {
- layout = profile.layouts[xrInputSource.handedness];
- }
- if (!layout) {
- throw new Error(
- `No matching handedness, ${xrInputSource.handedness}, in profile ${match.profileId}`
- );
- }
- if (layout.assetPath) {
- assetPath = match.profilePath.replace('profile.json', layout.assetPath);
- }
- }
- return { profile, assetPath };
- }
- /** @constant {Object} */
- const defaultComponentValues = {
- xAxis: 0,
- yAxis: 0,
- button: 0,
- state: Constants.ComponentState.DEFAULT
- };
- /**
- * @description Converts an X, Y coordinate from the range -1 to 1 (as reported by the Gamepad
- * API) to the range 0 to 1 (for interpolation). Also caps the X, Y values to be bounded within
- * a circle. This ensures that thumbsticks are not animated outside the bounds of their physical
- * range of motion and touchpads do not report touch locations off their physical bounds.
- * @param {number} x The original x coordinate in the range -1 to 1
- * @param {number} y The original y coordinate in the range -1 to 1
- */
- function normalizeAxes(x = 0, y = 0) {
- let xAxis = x;
- let yAxis = y;
- // Determine if the point is outside the bounds of the circle
- // and, if so, place it on the edge of the circle
- const hypotenuse = Math.sqrt((x * x) + (y * y));
- if (hypotenuse > 1) {
- const theta = Math.atan2(y, x);
- xAxis = Math.cos(theta);
- yAxis = Math.sin(theta);
- }
- // Scale and move the circle so values are in the interpolation range. The circle's origin moves
- // from (0, 0) to (0.5, 0.5). The circle's radius scales from 1 to be 0.5.
- const result = {
- normalizedXAxis: (xAxis * 0.5) + 0.5,
- normalizedYAxis: (yAxis * 0.5) + 0.5
- };
- return result;
- }
- /**
- * Contains the description of how the 3D model should visually respond to a specific user input.
- * This is accomplished by initializing the object with the name of a node in the 3D model and
- * property that need to be modified in response to user input, the name of the nodes representing
- * the allowable range of motion, and the name of the input which triggers the change. In response
- * to the named input changing, this object computes the appropriate weighting to use for
- * interpolating between the range of motion nodes.
- */
- class VisualResponse {
- constructor(visualResponseDescription) {
- this.componentProperty = visualResponseDescription.componentProperty;
- this.states = visualResponseDescription.states;
- this.valueNodeName = visualResponseDescription.valueNodeName;
- this.valueNodeProperty = visualResponseDescription.valueNodeProperty;
- if (this.valueNodeProperty === Constants.VisualResponseProperty.TRANSFORM) {
- this.minNodeName = visualResponseDescription.minNodeName;
- this.maxNodeName = visualResponseDescription.maxNodeName;
- }
- // Initializes the response's current value based on default data
- this.value = 0;
- this.updateFromComponent(defaultComponentValues);
- }
- /**
- * Computes the visual response's interpolation weight based on component state
- * @param {Object} componentValues - The component from which to update
- * @param {number} xAxis - The reported X axis value of the component
- * @param {number} yAxis - The reported Y axis value of the component
- * @param {number} button - The reported value of the component's button
- * @param {string} state - The component's active state
- */
- updateFromComponent({
- xAxis, yAxis, button, state
- }) {
- const { normalizedXAxis, normalizedYAxis } = normalizeAxes(xAxis, yAxis);
- switch (this.componentProperty) {
- case Constants.ComponentProperty.X_AXIS:
- this.value = (this.states.includes(state)) ? normalizedXAxis : 0.5;
- break;
- case Constants.ComponentProperty.Y_AXIS:
- this.value = (this.states.includes(state)) ? normalizedYAxis : 0.5;
- break;
- case Constants.ComponentProperty.BUTTON:
- this.value = (this.states.includes(state)) ? button : 0;
- break;
- case Constants.ComponentProperty.STATE:
- if (this.valueNodeProperty === Constants.VisualResponseProperty.VISIBILITY) {
- this.value = (this.states.includes(state));
- } else {
- this.value = this.states.includes(state) ? 1.0 : 0.0;
- }
- break;
- default:
- throw new Error(`Unexpected visualResponse componentProperty ${this.componentProperty}`);
- }
- }
- }
- class Component {
- /**
- * @param {Object} componentId - Id of the component
- * @param {Object} componentDescription - Description of the component to be created
- */
- constructor(componentId, componentDescription) {
- if (!componentId
- || !componentDescription
- || !componentDescription.visualResponses
- || !componentDescription.gamepadIndices
- || Object.keys(componentDescription.gamepadIndices).length === 0) {
- throw new Error('Invalid arguments supplied');
- }
- this.id = componentId;
- this.type = componentDescription.type;
- this.rootNodeName = componentDescription.rootNodeName;
- this.touchPointNodeName = componentDescription.touchPointNodeName;
- // Build all the visual responses for this component
- this.visualResponses = {};
- Object.keys(componentDescription.visualResponses).forEach((responseName) => {
- const visualResponse = new VisualResponse(componentDescription.visualResponses[responseName]);
- this.visualResponses[responseName] = visualResponse;
- });
- // Set default values
- this.gamepadIndices = Object.assign({}, componentDescription.gamepadIndices);
- this.values = {
- state: Constants.ComponentState.DEFAULT,
- button: (this.gamepadIndices.button !== undefined) ? 0 : undefined,
- xAxis: (this.gamepadIndices.xAxis !== undefined) ? 0 : undefined,
- yAxis: (this.gamepadIndices.yAxis !== undefined) ? 0 : undefined
- };
- }
- get data() {
- const data = { id: this.id, ...this.values };
- return data;
- }
- /**
- * @description Poll for updated data based on current gamepad state
- * @param {Object} gamepad - The gamepad object from which the component data should be polled
- */
- updateFromGamepad(gamepad) {
- // Set the state to default before processing other data sources
- this.values.state = Constants.ComponentState.DEFAULT;
- // Get and normalize button
- if (this.gamepadIndices.button !== undefined
- && gamepad.buttons.length > this.gamepadIndices.button) {
- const gamepadButton = gamepad.buttons[this.gamepadIndices.button];
- this.values.button = gamepadButton.value;
- this.values.button = (this.values.button < 0) ? 0 : this.values.button;
- this.values.button = (this.values.button > 1) ? 1 : this.values.button;
- // Set the state based on the button
- if (gamepadButton.pressed || this.values.button === 1) {
- this.values.state = Constants.ComponentState.PRESSED;
- } else if (gamepadButton.touched || this.values.button > Constants.ButtonTouchThreshold) {
- this.values.state = Constants.ComponentState.TOUCHED;
- }
- }
- // Get and normalize x axis value
- if (this.gamepadIndices.xAxis !== undefined
- && gamepad.axes.length > this.gamepadIndices.xAxis) {
- this.values.xAxis = gamepad.axes[this.gamepadIndices.xAxis];
- this.values.xAxis = (this.values.xAxis < -1) ? -1 : this.values.xAxis;
- this.values.xAxis = (this.values.xAxis > 1) ? 1 : this.values.xAxis;
- // If the state is still default, check if the xAxis makes it touched
- if (this.values.state === Constants.ComponentState.DEFAULT
- && Math.abs(this.values.xAxis) > Constants.AxisTouchThreshold) {
- this.values.state = Constants.ComponentState.TOUCHED;
- }
- }
- // Get and normalize Y axis value
- if (this.gamepadIndices.yAxis !== undefined
- && gamepad.axes.length > this.gamepadIndices.yAxis) {
- this.values.yAxis = gamepad.axes[this.gamepadIndices.yAxis];
- this.values.yAxis = (this.values.yAxis < -1) ? -1 : this.values.yAxis;
- this.values.yAxis = (this.values.yAxis > 1) ? 1 : this.values.yAxis;
- // If the state is still default, check if the yAxis makes it touched
- if (this.values.state === Constants.ComponentState.DEFAULT
- && Math.abs(this.values.yAxis) > Constants.AxisTouchThreshold) {
- this.values.state = Constants.ComponentState.TOUCHED;
- }
- }
- // Update the visual response weights based on the current component data
- Object.values(this.visualResponses).forEach((visualResponse) => {
- visualResponse.updateFromComponent(this.values);
- });
- }
- }
- /**
- * @description Builds a motion controller with components and visual responses based on the
- * supplied profile description. Data is polled from the xrInputSource's gamepad.
- * @author Nell Waliczek / https://github.com/NellWaliczek
- */
- class MotionController {
- /**
- * @param {Object} xrInputSource - The XRInputSource to build the MotionController around
- * @param {Object} profile - The best matched profile description for the supplied xrInputSource
- * @param {Object} assetUrl
- */
- constructor(xrInputSource, profile, assetUrl) {
- if (!xrInputSource) {
- throw new Error('No xrInputSource supplied');
- }
- if (!profile) {
- throw new Error('No profile supplied');
- }
- this.xrInputSource = xrInputSource;
- this.assetUrl = assetUrl;
- this.id = profile.profileId;
- // Build child components as described in the profile description
- this.layoutDescription = profile.layouts[xrInputSource.handedness];
- this.components = {};
- Object.keys(this.layoutDescription.components).forEach((componentId) => {
- const componentDescription = this.layoutDescription.components[componentId];
- this.components[componentId] = new Component(componentId, componentDescription);
- });
- // Initialize components based on current gamepad state
- this.updateFromGamepad();
- }
- get gripSpace() {
- return this.xrInputSource.gripSpace;
- }
- get targetRaySpace() {
- return this.xrInputSource.targetRaySpace;
- }
- /**
- * @description Returns a subset of component data for simplified debugging
- */
- get data() {
- const data = [];
- Object.values(this.components).forEach((component) => {
- data.push(component.data);
- });
- return data;
- }
- /**
- * @description Poll for updated data based on current gamepad state
- */
- updateFromGamepad() {
- Object.values(this.components).forEach((component) => {
- component.updateFromGamepad(this.xrInputSource.gamepad);
- });
- }
- }
- const DEFAULT_PROFILES_PATH = 'https://cdn.jsdelivr.net/npm/@webxr-input-profiles/assets@1.0/dist/profiles';
- const DEFAULT_PROFILE = 'generic-trigger';
- function XRControllerModel( ) {
- Object3D.call( this );
- this.motionController = null;
- this.envMap = null;
- }
- XRControllerModel.prototype = Object.assign( Object.create( Object3D.prototype ), {
- constructor: XRControllerModel,
- setEnvironmentMap: function ( envMap ) {
- if ( this.envMap == envMap ) {
- return this;
- }
- this.envMap = envMap;
- this.traverse( ( child ) => {
- if ( child.isMesh ) {
- child.material.envMap = this.envMap;
- child.material.needsUpdate = true;
- }
- } );
- return this;
- },
- /**
- * Polls data from the XRInputSource and updates the model's components to match
- * the real world data
- */
- updateMatrixWorld: function ( force ) {
- Object3D.prototype.updateMatrixWorld.call( this, force );
- if ( ! this.motionController ) return;
- // Cause the MotionController to poll the Gamepad for data
- this.motionController.updateFromGamepad();
- // Update the 3D model to reflect the button, thumbstick, and touchpad state
- Object.values( this.motionController.components ).forEach( ( component ) => {
- // Update node data based on the visual responses' current states
- Object.values( component.visualResponses ).forEach( ( visualResponse ) => {
- const { valueNode, minNode, maxNode, value, valueNodeProperty } = visualResponse;
- // Skip if the visual response node is not found. No error is needed,
- // because it will have been reported at load time.
- if ( ! valueNode ) return;
- // Calculate the new properties based on the weight supplied
- if ( valueNodeProperty === Constants.VisualResponseProperty.VISIBILITY ) {
- valueNode.visible = value;
- } else if ( valueNodeProperty === Constants.VisualResponseProperty.TRANSFORM ) {
- Quaternion.slerp(
- minNode.quaternion,
- maxNode.quaternion,
- valueNode.quaternion,
- value
- );
- valueNode.position.lerpVectors(
- minNode.position,
- maxNode.position,
- value
- );
- }
- } );
- } );
- }
- } );
- /**
- * Walks the model's tree to find the nodes needed to animate the components and
- * saves them to the motionContoller components for use in the frame loop. When
- * touchpads are found, attaches a touch dot to them.
- */
- function findNodes( motionController, scene ) {
- // Loop through the components and find the nodes needed for each components' visual responses
- Object.values( motionController.components ).forEach( ( component ) => {
- const { type, touchPointNodeName, visualResponses } = component;
- if ( type === Constants.ComponentType.TOUCHPAD ) {
- component.touchPointNode = scene.getObjectByName( touchPointNodeName );
- if ( component.touchPointNode ) {
- // Attach a touch dot to the touchpad.
- const sphereGeometry = new SphereBufferGeometry( 0.001 );
- const material = new MeshBasicMaterial( { color: 0x0000FF } );
- const sphere = new Mesh( sphereGeometry, material );
- component.touchPointNode.add( sphere );
- } else {
- console.warn( `Could not find touch dot, ${component.touchPointNodeName}, in touchpad component ${component.id}` );
- }
- }
- // Loop through all the visual responses to be applied to this component
- Object.values( visualResponses ).forEach( ( visualResponse ) => {
- const { valueNodeName, minNodeName, maxNodeName, valueNodeProperty } = visualResponse;
- // If animating a transform, find the two nodes to be interpolated between.
- if ( valueNodeProperty === Constants.VisualResponseProperty.TRANSFORM ) {
- visualResponse.minNode = scene.getObjectByName( minNodeName );
- visualResponse.maxNode = scene.getObjectByName( maxNodeName );
- // If the extents cannot be found, skip this animation
- if ( ! visualResponse.minNode ) {
- console.warn( `Could not find ${minNodeName} in the model` );
- return;
- }
- if ( ! visualResponse.maxNode ) {
- console.warn( `Could not find ${maxNodeName} in the model` );
- return;
- }
- }
- // If the target node cannot be found, skip this animation
- visualResponse.valueNode = scene.getObjectByName( valueNodeName );
- if ( ! visualResponse.valueNode ) {
- console.warn( `Could not find ${valueNodeName} in the model` );
- }
- } );
- } );
- }
- function addAssetSceneToControllerModel( controllerModel, scene ) {
- // Find the nodes needed for animation and cache them on the motionController.
- findNodes( controllerModel.motionController, scene );
- // Apply any environment map that the mesh already has set.
- if ( controllerModel.envMap ) {
- scene.traverse( ( child ) => {
- if ( child.isMesh ) {
- child.material.envMap = controllerModel.envMap;
- child.material.needsUpdate = true;
- }
- } );
- }
- // Add the glTF scene to the controllerModel.
- controllerModel.add( scene );
- }
- var XRControllerModelFactory = ( function () {
- function XRControllerModelFactory( gltfLoader = null ) {
- this.gltfLoader = gltfLoader;
- this.path = DEFAULT_PROFILES_PATH;
- this._assetCache = {};
- // If a GLTFLoader wasn't supplied to the constructor create a new one.
- if ( ! this.gltfLoader ) {
- this.gltfLoader = new GLTFLoader();
- }
- }
- XRControllerModelFactory.prototype = {
- constructor: XRControllerModelFactory,
- createControllerModel: function ( controller ) {
- const controllerModel = new XRControllerModel();
- let scene = null;
- controller.addEventListener( 'connected', ( event ) => {
- const xrInputSource = event.data;
- if ( xrInputSource.targetRayMode !== 'tracked-pointer' || ! xrInputSource.gamepad ) return;
- fetchProfile( xrInputSource, this.path, DEFAULT_PROFILE ).then( ( { profile, assetPath } ) => {
- controllerModel.motionController = new MotionController(
- xrInputSource,
- profile,
- assetPath
- );
- const cachedAsset = this._assetCache[ controllerModel.motionController.assetUrl ];
- if ( cachedAsset ) {
- scene = cachedAsset.scene.clone();
- addAssetSceneToControllerModel( controllerModel, scene );
- } else {
- if ( ! this.gltfLoader ) {
- throw new Error( 'GLTFLoader not set.' );
- }
- this.gltfLoader.setPath( '' );
- this.gltfLoader.load( controllerModel.motionController.assetUrl, ( asset ) => {
- this._assetCache[ controllerModel.motionController.assetUrl ] = asset;
- scene = asset.scene.clone();
- addAssetSceneToControllerModel( controllerModel, scene );
- },
- null,
- () => {
- throw new Error( `Asset ${controllerModel.motionController.assetUrl} missing or malformed.` );
- } );
- }
- } ).catch( ( err ) => {
- console.warn( err );
- } );
- } );
- controller.addEventListener( 'disconnected', () => {
- controllerModel.motionController = null;
- controllerModel.remove( scene );
- scene = null;
- } );
- return controllerModel;
- }
- };
- return XRControllerModelFactory;
- } )();
- let fakeCam = new PerspectiveCamera();
- function toScene(vec, ref){
- let node = ref.clone();
- node.updateMatrix();
- node.updateMatrixWorld();
- let result = vec.clone().applyMatrix4(node.matrix);
- result.z -= 0.8 * node.scale.x;
- return result;
- };
- function computeMove(vrControls, controller){
- if(!controller || !controller.inputSource || !controller.inputSource.gamepad){
- return null;
- }
- let pad = controller.inputSource.gamepad;
- let axes = pad.axes;
- // [0,1] are for touchpad, [2,3] for thumbsticks?
- let y = 0;
- if(axes.length === 2){
- y = axes[1];
- }else if(axes.length === 4){
- y = axes[3];
- }
- y = Math.sign(y) * (2 * y) ** 2;
- let maxSize = 0;
- for(let pc of viewer.scene.pointclouds){
- let size = pc.boundingBox.min.distanceTo(pc.boundingBox.max);
- maxSize = Math.max(maxSize, size);
- }
- let multiplicator = Math.pow(maxSize, 0.5) / 2;
- let scale = vrControls.node.scale.x;
- let moveSpeed = viewer.getMoveSpeed();
- let amount = multiplicator * y * (moveSpeed ** 0.5) / scale;
- let rotation = new Quaternion().setFromEuler(controller.rotation);
- let dir = new Vector3(0, 0, -1);
- dir.applyQuaternion(rotation);
- let move = dir.clone().multiplyScalar(amount);
- let p1 = vrControls.toScene(controller.position);
- let p2 = vrControls.toScene(controller.position.clone().add(move));
- move = p2.clone().sub(p1);
-
- return move;
- };
- class FlyMode{
- constructor(vrControls){
- this.moveFactor = 1;
- this.dbgLabel = null;
- }
- start(vrControls){
- if(!this.dbgLabel){
- /* this.dbgLabel = new Potree.TextSprite("abc");
- this.dbgLabel.name = "debug label";
- vrControls.viewer.sceneVR.add(this.dbgLabel);
- this.dbgLabel.visible = false; */
- }
- }
-
- end(){
- }
- update(vrControls, delta){
- let primary = vrControls.cPrimary;
- let secondary = vrControls.cSecondary;
- let move1 = computeMove(vrControls, primary);
- let move2 = computeMove(vrControls, secondary);
- if(!move1){
- move1 = new Vector3();
- }
- if(!move2){
- move2 = new Vector3();
- }
- let move = move1.clone().add(move2);
- move.multiplyScalar(-delta * this.moveFactor);
- vrControls.node.position.add(move);
-
- let scale = vrControls.node.scale.x;
- let camVR = vrControls.viewer.renderer.xr.getCamera(fakeCam);
-
- let vrPos = camVR.getWorldPosition(new Vector3());
- let vrDir = camVR.getWorldDirection(new Vector3());
- let vrTarget = vrPos.clone().add(vrDir.multiplyScalar(scale));
- let scenePos = toScene(vrPos, vrControls.node);
- let sceneDir = toScene(vrPos.clone().add(vrDir), vrControls.node).sub(scenePos);
- sceneDir.normalize().multiplyScalar(scale);
- let sceneTarget = scenePos.clone().add(sceneDir);
- vrControls.viewer.scene.view.setView(scenePos, sceneTarget);
- if(Potree.debug.message){
- this.dbgLabel.visible = true;
- this.dbgLabel.setText(Potree.debug.message);
- this.dbgLabel.scale.set(0.1, 0.1, 0.1);
- this.dbgLabel.position.copy(primary.position);
- }
- }
- };
- class TranslationMode{
- constructor(){
- this.controller = null;
- this.startPos = null;
- this.debugLine = null;
- }
- start(vrControls){
- this.controller = vrControls.triggered.values().next().value;
- this.startPos = vrControls.node.position.clone();
- }
-
- end(vrControls){
- }
- update(vrControls, delta){
- let start = this.controller.start.position;
- let end = this.controller.position;
- start = vrControls.toScene(start);
- end = vrControls.toScene(end);
- let diff = end.clone().sub(start);
- diff.set(-diff.x, -diff.y, -diff.z);
- let pos = new Vector3().addVectors(this.startPos, diff);
- vrControls.node.position.copy(pos);
- }
- };
- class RotScaleMode{
- constructor(){
- this.line = null;
- this.startState = null;
- }
- start(vrControls){
- if(!this.line){
- this.line = Potree.Utils.debugLine(
- vrControls.viewer.sceneVR,
- new Vector3(0, 0, 0),
- new Vector3(0, 0, 0),
- 0xffff00,
- );
- this.dbgLabel = new Potree.TextSprite("abc");
- this.dbgLabel.scale.set(0.1, 0.1, 0.1);
- vrControls.viewer.sceneVR.add(this.dbgLabel);
- }
- this.line.node.visible = true;
- this.startState = vrControls.node.clone();
- }
- end(vrControls){
- this.line.node.visible = false;
- this.dbgLabel.visible = false;
- }
- update(vrControls, delta){
- let start_c1 = vrControls.cPrimary.start.position.clone();
- let start_c2 = vrControls.cSecondary.start.position.clone();
- let start_center = start_c1.clone().add(start_c2).multiplyScalar(0.5);
- let start_c1_c2 = start_c2.clone().sub(start_c1);
- let end_c1 = vrControls.cPrimary.position.clone();
- let end_c2 = vrControls.cSecondary.position.clone();
- let end_center = end_c1.clone().add(end_c2).multiplyScalar(0.5);
- let end_c1_c2 = end_c2.clone().sub(end_c1);
- let d1 = start_c1_c2.length();
- let d2 = end_c1_c2.length();
- let angleStart = new Vector2$1(start_c1_c2.x, start_c1_c2.z).angle();
- let angleEnd = new Vector2$1(end_c1_c2.x, end_c1_c2.z).angle();
- let angleDiff = angleEnd - angleStart;
-
- let scale = d2 / d1;
- let node = this.startState.clone();
- node.updateMatrix();
- node.matrixAutoUpdate = false;
- let mToOrigin = new Matrix4().makeTranslation(...toScene(start_center, this.startState).multiplyScalar(-1).toArray());
- let mToStart = new Matrix4().makeTranslation(...toScene(start_center, this.startState).toArray());
- let mRotate = new Matrix4().makeRotationZ(angleDiff);
- let mScale = new Matrix4().makeScale(1 / scale, 1 / scale, 1 / scale);
- node.applyMatrix4(mToOrigin);
- node.applyMatrix4(mRotate);
- node.applyMatrix4(mScale);
- node.applyMatrix4(mToStart);
- let oldScenePos = toScene(start_center, this.startState);
- let newScenePos = toScene(end_center, node);
- let toNew = oldScenePos.clone().sub(newScenePos);
- let mToNew = new Matrix4().makeTranslation(...toNew.toArray());
- node.applyMatrix4(mToNew);
- node.matrix.decompose(node.position, node.quaternion, node.scale );
- vrControls.node.position.copy(node.position);
- vrControls.node.quaternion.copy(node.quaternion);
- vrControls.node.scale.copy(node.scale);
- vrControls.node.updateMatrix();
- {
- let scale = vrControls.node.scale.x;
- let camVR = vrControls.viewer.renderer.xr.getCamera(fakeCam);
-
- let vrPos = camVR.getWorldPosition(new Vector3());
- let vrDir = camVR.getWorldDirection(new Vector3());
- let vrTarget = vrPos.clone().add(vrDir.multiplyScalar(scale));
- let scenePos = toScene(vrPos, this.startState);
- let sceneDir = toScene(vrPos.clone().add(vrDir), this.startState).sub(scenePos);
- sceneDir.normalize().multiplyScalar(scale);
- let sceneTarget = scenePos.clone().add(sceneDir);
- vrControls.viewer.scene.view.setView(scenePos, sceneTarget);
- vrControls.viewer.setMoveSpeed(scale);
- }
- { // update "GUI"
- this.line.set(end_c1, end_c2);
- let scale = vrControls.node.scale.x;
- this.dbgLabel.visible = true;
- this.dbgLabel.position.copy(end_center);
- this.dbgLabel.setText(`scale: 1 : ${scale.toFixed(2)}`);
- this.dbgLabel.scale.set(0.05, 0.05, 0.05);
- }
- }
- };
- class VRControls extends EventDispatcher{
- constructor(viewer){
- super(viewer);
- this.viewer = viewer;
- viewer.addEventListener("vr_start", this.onStart.bind(this));
- viewer.addEventListener("vr_end", this.onEnd.bind(this));
- this.node = new Object3D();
- this.node.up.set(0, 0, 1);
- this.triggered = new Set();
- let xr = viewer.renderer.xr;
- { // lights
-
- const light = new PointLight( 0xffffff, 5, 0, 1 );
- light.position.set(0, 2, 0);
- this.viewer.sceneVR.add(light);
- }
- this.menu = null;
- const controllerModelFactory = new XRControllerModelFactory();
- let sg = new SphereGeometry(1, 32, 32);
- let sm = new MeshNormalMaterial();
- { // setup primary controller
- let controller = xr.getController(0);
- let grip = xr.getControllerGrip(0);
- grip.name = "grip(0)";
- // ADD CONTROLLERMODEL
- grip.add( controllerModelFactory.createControllerModel( grip ) );
- this.viewer.sceneVR.add(grip);
- // ADD SPHERE
- let sphere = new Mesh(sg, sm);
- sphere.scale.set(0.005, 0.005, 0.005);
- controller.add(sphere);
- controller.visible = true;
- this.viewer.sceneVR.add(controller);
- { // ADD LINE
-
- let lineGeometry = new LineGeometry();
- lineGeometry.setPositions([
- 0, 0, -0.15,
- 0, 0, 0.05,
- ]);
- let lineMaterial = new LineMaterial({
- color: 0xff0000,
- lineWidth: 2,
- resolution: new Vector2$1(1000, 1000),
- });
- const line = new Line2(lineGeometry, lineMaterial);
-
- controller.add(line);
- }
- controller.addEventListener( 'connected', function ( event ) {
- const xrInputSource = event.data;
- controller.inputSource = xrInputSource;
- // initInfo(controller);
- });
- controller.addEventListener( 'selectstart', () => {this.onTriggerStart(controller);});
- controller.addEventListener( 'selectend', () => {this.onTriggerEnd(controller);});
- this.cPrimary = controller;
- }
- { // setup secondary controller
- let controller = xr.getController(1);
- let grip = xr.getControllerGrip(1);
- // ADD CONTROLLER MODEL
- let model = controllerModelFactory.createControllerModel( grip );
- grip.add(model);
- this.viewer.sceneVR.add( grip );
- // ADD SPHERE
- let sphere = new Mesh(sg, sm);
- sphere.scale.set(0.005, 0.005, 0.005);
- controller.add(sphere);
- controller.visible = true;
- this.viewer.sceneVR.add(controller);
- { // ADD LINE
-
- let lineGeometry = new LineGeometry();
- lineGeometry.setPositions([
- 0, 0, -0.15,
- 0, 0, 0.05,
- ]);
- let lineMaterial = new LineMaterial({
- color: 0xff0000,
- lineWidth: 2,
- resolution: new Vector2$1(1000, 1000),
- });
- const line = new Line2(lineGeometry, lineMaterial);
-
- controller.add(line);
- }
- controller.addEventListener( 'connected', (event) => {
- const xrInputSource = event.data;
- controller.inputSource = xrInputSource;
- this.initMenu(controller);
- });
- controller.addEventListener( 'selectstart', () => {this.onTriggerStart(controller);});
- controller.addEventListener( 'selectend', () => {this.onTriggerEnd(controller);});
- this.cSecondary = controller;
- }
- this.mode_fly = new FlyMode();
- this.mode_translate = new TranslationMode();
- this.mode_rotScale = new RotScaleMode();
- this.setMode(this.mode_fly);
- }
- createSlider(label, min, max){
- let sg = new SphereGeometry(1, 8, 8);
- let cg = new CylinderGeometry(1, 1, 1, 8);
- let matHandle = new MeshBasicMaterial({color: 0xff0000});
- let matScale = new MeshBasicMaterial({color: 0xff4444});
- let matValue = new MeshNormalMaterial();
- let node = new Object3D("slider");
- let nLabel = new Potree.TextSprite(`${label}: 0`);
- let nMax = new Mesh(sg, matHandle);
- let nMin = new Mesh(sg, matHandle);
- let nValue = new Mesh(sg, matValue);
- let nScale = new Mesh(cg, matScale);
- nLabel.scale.set(0.2, 0.2, 0.2);
- nLabel.position.set(0, 0.35, 0);
- nMax.scale.set(0.02, 0.02, 0.02);
- nMax.position.set(0, 0.25, 0);
- nMin.scale.set(0.02, 0.02, 0.02);
- nMin.position.set(0, -0.25, 0);
- nValue.scale.set(0.02, 0.02, 0.02);
- nValue.position.set(0, 0, 0);
- nScale.scale.set(0.005, 0.5, 0.005);
- node.add(nLabel);
- node.add(nMax);
- node.add(nMin);
- node.add(nValue);
- node.add(nScale);
- return node;
- }
- createInfo(){
- let texture = new TextureLoader().load(`${Potree.resourcePath}/images/vr_controller_help.jpg`);
- let plane = new PlaneBufferGeometry(1, 1, 1, 1);
- let infoMaterial = new MeshBasicMaterial({map: texture});
- let infoNode = new Mesh(plane, infoMaterial);
- return infoNode;
- }
- initMenu(controller){
- if(this.menu){
- return;
- }
- let node = new Object3D("vr menu");
- // let nSlider = this.createSlider("speed", 0, 1);
- // let nInfo = this.createInfo();
- // // node.add(nSlider);
- // node.add(nInfo);
- // {
- // node.rotation.set(-1.5, 0, 0)
- // node.scale.set(0.3, 0.3, 0.3);
- // node.position.set(-0.2, -0.002, -0.1)
- // // nInfo.position.set(0.5, 0, 0);
- // nInfo.scale.set(0.8, 0.6, 0);
- // // controller.add(node);
- // }
- // node.position.set(-0.3, 1.2, 0.2);
- // node.scale.set(0.3, 0.2, 0.3);
- // node.lookAt(new THREE.Vector3(0, 1.5, 0.1));
- // this.viewer.sceneVR.add(node);
- this.menu = node;
- // window.vrSlider = nSlider;
- window.vrMenu = node;
- }
- toScene(vec){
- let camVR = this.getCamera();
- let mat = camVR.matrixWorld;
- let result = vec.clone().applyMatrix4(mat);
- return result;
- }
- toVR(vec){
- let camVR = this.getCamera();
- let mat = camVR.matrixWorld.clone();
- mat.invert();
- let result = vec.clone().applyMatrix4(mat);
- return result;
- }
- setMode(mode){
- if(this.mode === mode){
- return;
- }
- if(this.mode){
- this.mode.end(this);
- }
- for(let controller of [this.cPrimary, this.cSecondary]){
- let start = {
- position: controller.position.clone(),
- rotation: controller.rotation.clone(),
- };
- controller.start = start;
- }
-
- this.mode = mode;
- this.mode.start(this);
- }
- onTriggerStart(controller){
- this.triggered.add(controller);
- if(this.triggered.size === 0){
- this.setMode(this.mode_fly);
- }else if(this.triggered.size === 1){
- this.setMode(this.mode_translate);
- }else if(this.triggered.size === 2){
- this.setMode(this.mode_rotScale);
- }
- }
- onTriggerEnd(controller){
- this.triggered.delete(controller);
- if(this.triggered.size === 0){
- this.setMode(this.mode_fly);
- }else if(this.triggered.size === 1){
- this.setMode(this.mode_translate);
- }else if(this.triggered.size === 2){
- this.setMode(this.mode_rotScale);
- }
- }
- onStart(){
- let position = this.viewer.scene.view.position.clone();
- let direction = this.viewer.scene.view.direction;
- direction.multiplyScalar(-1);
- let target = position.clone().add(direction);
- target.z = position.z;
- let scale = this.viewer.getMoveSpeed();
- this.node.position.copy(position);
- this.node.lookAt(target);
- this.node.scale.set(scale, scale, scale);
- this.node.updateMatrix();
- this.node.updateMatrixWorld();
- }
- onEnd(){
-
- }
- setScene(scene){
- this.scene = scene;
- }
- getCamera(){
- let reference = this.viewer.scene.getActiveCamera();
- let camera = new PerspectiveCamera();
- // let scale = this.node.scale.x;
- let scale = this.viewer.getMoveSpeed();
- //camera.near = 0.01 / scale;
- camera.near = 0.1;
- camera.far = 1000;
- // camera.near = reference.near / scale;
- // camera.far = reference.far / scale;
- camera.up.set(0, 0, 1);
- camera.lookAt(new Vector3(0, -1, 0));
- camera.updateMatrix();
- camera.updateMatrixWorld();
- camera.position.copy(this.node.position);
- camera.rotation.copy(this.node.rotation);
- camera.scale.set(scale, scale, scale);
- camera.updateMatrix();
- camera.updateMatrixWorld();
- camera.matrixAutoUpdate = false;
- camera.parent = camera;
- return camera;
- }
- update(delta){
-
- // if(this.mode === this.mode_fly){
- // let ray = new THREE.Ray(origin, direction);
-
- // for(let object of this.selectables){
- // if(object.intersectsRay(ray)){
- // object.onHit(ray);
- // }
- // }
- // }
- this.mode.update(this, delta);
-
- }
- };
- // Adapted from three.js VRButton
- class VRButton {
- constructor(){
- this.onStartListeners = [];
- this.onEndListeners = [];
- this.element = null;
- }
- onStart(callback){
- this.onStartListeners.push(callback);
- }
- onEnd(callback){
- this.onEndListeners.push(callback);
- }
- static async createButton( renderer, options ) {
- if ( options ) {
- console.error( 'THREE.VRButton: The "options" parameter has been removed. Please set the reference space type via renderer.xr.setReferenceSpaceType() instead.' );
- }
- const button = new VRButton();
- const element = document.createElement( 'button' );
- button.element = element;
- function setEnter(){
- button.element.innerHTML = `
- <div style="font-size: 0.5em;">ENTER</div>
- <div style="font-weight: bold;">VR</div>
- `;
- }
- function setExit(){
- button.element.innerHTML = `
- <div style="font-size: 0.5em;">EXIT</div>
- <div style="font-weight: bold;">VR</div>
- `;
- }
- function showEnterVR( /*device*/ ) {
- let currentSession = null;
- function onSessionStarted( session ) {
- session.addEventListener( 'end', onSessionEnded );
- for(let listener of button.onStartListeners){
- listener();
- }
- renderer.xr.setSession( session );
- setExit();
- currentSession = session;
- }
- function onSessionEnded( /*event*/ ) {
- currentSession.removeEventListener( 'end', onSessionEnded );
- for(let listener of button.onEndListeners){
- listener();
- }
- setEnter();
- currentSession = null;
- }
- //
- button.element.style.display = '';
- button.element.style.cursor = 'pointer';
- setEnter();
- button.element.onmouseenter = function () {
- button.element.style.opacity = '1.0';
- };
- button.element.onmouseleave = function () {
- button.element.style.opacity = '0.7';
- };
- button.element.onclick = function () {
- if ( currentSession === null ) {
- // WebXR's requestReferenceSpace only works if the corresponding feature
- // was requested at session creation time. For simplicity, just ask for
- // the interesting ones as optional features, but be aware that the
- // requestReferenceSpace call will fail if it turns out to be unavailable.
- // ('local' is always available for immersive sessions and doesn't need to
- // be requested separately.)
- const sessionInit = { optionalFeatures: [ 'local-floor', 'bounded-floor', 'hand-tracking' ] };
- navigator.xr.requestSession( 'immersive-vr', sessionInit ).then( onSessionStarted );
- } else {
- currentSession.end();
- }
- };
- }
- function stylizeElement( element ) {
- element.style.position = 'absolute';
- element.style.bottom = '20px';
- element.style.padding = '12px 6px';
- element.style.border = '1px solid #fff';
- element.style.borderRadius = '4px';
- element.style.background = 'rgba(0,0,0,0.1)';
- element.style.color = '#fff';
- element.style.font = 'normal 13px sans-serif';
- element.style.textAlign = 'center';
- element.style.opacity = '0.7';
- element.style.outline = 'none';
- element.style.zIndex = '999';
- }
- if ( 'xr' in navigator ) {
- button.element.id = 'VRButton';
- button.element.style.display = 'none';
- stylizeElement( button.element );
- let supported = await navigator.xr.isSessionSupported( 'immersive-vr' );
- if(supported){
- showEnterVR();
- return button;
- }else {
- return null;
- }
- } else {
- if ( window.isSecureContext === false ) {
- console.log("WEBXR NEEDS HTTPS");
- } else {
- console.log("WEBXR not available");
- }
- return null;
- }
- }
- }
- //处理cursor优先级
- var CursorDeal = {
- priorityEvent : [//在前面的优先级高
-
- {'hoverPano':'pointer'},
-
- {'connectPano':`url({Potree.resourcePath}/images/connect.png),auto`},
- {'disconnectPano':`url({Potree.resourcePath}/images/connect-dis.png),auto`},
-
- {'hoverLine':'pointer'},
- {'zoomInCloud':'zoom-in'},
-
-
- {"movePointcloud":'move'},
- {"polygon_isIntersectSelf":'not-allowed'},
- {"polygon_AtWrongPlace":'not-allowed'},
- {"markerMove":'grab'},
- {'mapClipMove':'move'},
- {'mapClipRotate':`url({Potree.resourcePath}/images/rotate-cursor.png),auto`},
- {'rotatePointcloud':`url({Potree.resourcePath}/images/rotate-cursor.png),auto`},
- {'siteModelFloorDrag':'row-resize'},
- {'addSth':'cell'},//or crosshair
-
- ],
- list:[], //当前存在的cursor状态
- currentCursorIndex:null,
-
- init : function(viewer, viewers){
-
- this.priorityEvent.forEach(e=>{//刚开始Potree.resourcePath没值,现在换
- for(let i in e){
- e[i] = Common.replaceAll(e[i],'{Potree.resourcePath}',Potree.resourcePath);
- }
- });
-
-
-
- this.domElements = viewers.map(e=>e.renderArea);
-
- viewer.addEventListener("CursorChange",(e)=>{
- if(e.action == 'add'){
- this.add(e.name);
- }else {
- this.remove(e.name);
- }
- });
-
-
- },
-
-
- add : function(name){
- var priorityItem = this.priorityEvent.find(e=>e[name]);
- if(!priorityItem){
- console.error('CursorDeal 未定义优先级 name:'+ name);
- return
- }
-
-
- if(!this.list.includes(name)){
-
- this.judge({addItem: priorityItem, name});
-
- this.list.push(name);
- }
-
- },
-
-
- remove : function(name){
- var index = this.list.indexOf(name);
- if(index > -1){
- this.list.splice(index, 1);
- this.judge();
- }
-
-
-
- },
-
- judge:function(o={}){
- //console.log(o,this.list)
- if(o.addItem){
- var addIndex = this.priorityEvent.indexOf(o.addItem);
- if(addIndex < this.currentCursorIndex || this.currentCursorIndex == void 0){
- this.domElements.forEach(e=>e.style.cursor = o.addItem[o.name] );
- this.currentCursorIndex = addIndex;
- }
- }else {
- var levelMax = {index:Infinity, cursor:null };
- this.list.forEach(name=>{
- var priorityItem = this.priorityEvent.find(e=>e[name]);
- var index = this.priorityEvent.indexOf(priorityItem);
- if(index < levelMax.index){
- levelMax.index = index;
- levelMax.cursor = priorityItem[name];
- }
- });
- this.currentCursorIndex = levelMax.index;
- this.domElements.forEach(e=>e.style.cursor = levelMax.cursor || '');
- }
-
- }
-
-
- };
- let texLoader$5 = new TextureLoader();
- let color$1 = new Color(config$1.clip.color);
- let markerMats$1;
- let markerSizeInfo$1 = {width2d:30};
- class mapClipBox extends ctrlPolygon {
- constructor (center, scale) {
- center = center.clone().setZ(0);//所有Z都为0
-
-
- let prop = {
- points : getPoints(center, scale, 0),
- closed : true,
- isRect : true,
- dimension : '2d'
- };
-
-
- super('mapClipBox', prop);
-
- this.angle = 0;
- this.createRotateBar();
- this.edgeMarkers = [];
-
- //addMarkers:
- this.initData(prop);
-
-
-
-
- {//areaPlane event 能拖动
- this.areaPlane.addEventListener('mouseover',()=>{
- viewer.dispatchEvent({
- type : "CursorChange", action : "add", name:"mapClipMove"
- });
- });
- this.areaPlane.addEventListener('mouseleave',()=>{
- viewer.dispatchEvent({
- type : "CursorChange", action : "remove", name:"mapClipMove"
- });
- });
-
- let lastPos;
- let drag = (e)=>{
- let intersectPoint = e.intersectPoint.orthoIntersect;
- if(lastPos){
- let moveVec = new Vector3().subVectors(intersectPoint, lastPos).setZ(0);
- this.center.add(moveVec);
- this.updatePoints();
- this.dispatchEvent({type:'repos'});
-
- }
- lastPos = intersectPoint.clone();
- };
- let drop = (e)=>{
- lastPos = null;
- };
- this.areaPlane.addEventListener('drag', drag);
- this.areaPlane.addEventListener('drop', drop);
-
- }
-
-
- /* this.addEventListener('dragChange',()=>{
- this.updateTwoMidMarker(index+1)
- }) */
-
-
-
- viewer.setObjectLayers(this, 'mapObjects' );
- }
-
-
-
-
-
- getScale(){
- return new Vector3(this.points[0].distanceTo(this.points[1]), this.points[1].distanceTo(this.points[2]) ,1)
- }
-
-
-
- addMarker(o={} ){
- let marker = new Sprite$1({mat:this.getMarkerMaterial('default'), sizeInfo: markerSizeInfo$1, dontFixOrient: true, viewports:viewer.mapViewer.viewports, name:"mapClipBox_marker", } );
-
- marker.renderOrder = 3;
- //marker.markerSelectStates = {}
-
- let edge = LineDraw.createLine([new Vector3,new Vector3],{color: color$1 });
- let edgeMarker = new Sprite$1({mat:this.getMarkerMaterial('default'), sizeInfo: markerSizeInfo$1, dontFixOrient: true, viewports:viewer.mapViewer.viewports, name:"mapClipBox_edgePoint"} );
- let mouseover = (e) => {
- this.setMarkerSelected(e.object, true, 'single');
- viewer.dispatchEvent({
- type : "CursorChange", action : "add", name:"markerMove"
- });
- };
- let mouseleave = (e) => {
- this.setMarkerSelected(e.object, false, 'single');
- viewer.dispatchEvent({
- type : "CursorChange", action : "remove", name:"markerMove"
- });
- };
- edgeMarker.addEventListener('mouseover', mouseover);
- edgeMarker.addEventListener('mouseleave', mouseleave);
- let dragInfo = {lastPos:null};
- edgeMarker.addEventListener('drag', this.dragEdge.bind(this,dragInfo));
- edgeMarker.addEventListener('drop', this.dropEdge.bind(this,dragInfo));
- this.edgeMarkers.push(edgeMarker);
- this.add(edgeMarker);
- marker.dispatchEvent('addHoverEvent');
-
- super.addMarker({point:o.point, marker:marker, edge});
-
- }
-
-
-
- dragEdge(dragInfo, e){//拖拽一个边(或一个边类型的marker),带动它的两个端点。 可以转化为拖拽marker往法线方向
- var I, atMap;
-
- atMap = e.dragViewport.name == 'mapViewport';
- I = e.intersectPoint.orthoIntersect;
-
- if (I && dragInfo.lastPos) {
- let i = this.edgeMarkers.indexOf(e.drag.object);
- if (i !== -1) {
- let lineNormal = math.getNormal2d({p1:this.points[i], p2:this.points[(i+1)%4]});
- let moveVec = new Vector3().subVectors(I, dragInfo.lastPos).setZ(0);//移动的向量
- let dragVec = moveVec.projectOnVector(lineNormal).setZ(0);//在法线上移动的向量
- let newPos = new Vector3().addVectors(this.points[i], dragVec); //marker应到的位置
- this.dragChange(newPos, i, atMap); //转化为这个marker的拖拽
- }
- }
- dragInfo.lastPos = I.clone();
- }
- dropEdge(dragInfo, e){
- dragInfo.lastPos = null;
- this.setMarkerSelected(e.drag.object, false, 'single'); //拖拽时似乎没有触发mouseout
- }
-
-
-
- createAreaPlane(){
- var planeMat = new MeshBasicMaterial({
- color: color$1,//"#00eeff",
- side:DoubleSide,
- opacity:0.3,
- transparent:true,
- depthTest:false
- });
-
- return super.createAreaPlane(planeMat)
- }
-
-
-
- getMarkerMaterial(type) {
- if(!markerMats$1){
- markerMats$1 = {
- default: new MeshBasicMaterial({
- transparent: !0,
- color: color$1,
- opacity: 0.8,
- map: texLoader$5.load(Potree.resourcePath+'/textures/whiteCircle.png' ),
- }),
- select: new MeshBasicMaterial({
- transparent: !0,
- color: color$1,
- opacity: 1,
- map: texLoader$5.load(Potree.resourcePath+'/textures/whiteCircle.png' ),
-
- }),
- };
- mapClipBox.markerMats = markerMats$1;
- }
- return markerMats$1[type]
-
- }
-
- setMarkerSelected(marker, state, hoverObject){
- //console.warn(marker.id , state, hoverObject)
- if(state == 'hover'){
- marker.material = this.getMarkerMaterial('select');
- }else {
- marker.material = this.getMarkerMaterial('default');
- }
-
- /* marker.markerSelectStates[hoverObject] = state
- let absoluteState = false
- for(var i in marker.markerSelectStates){
- if(marker.markerSelectStates[i]){
- absoluteState = true; break;
- }
- }
- if(absoluteState){
- marker.material = this.getMarkerMaterial('select')
- }else{
- marker.material = this.getMarkerMaterial('default')
- }
-
- marker.selected = absoluteState */
-
- viewer.mapViewer.dispatchEvent('content_changed');
- }
-
- createRotateBar(){
- //中心点在线的一端,整体初始在box上方
- const lineLen = 1.5, circleWidth = 2, barOpacity = 0.7;
-
- let object = new Object3D;
-
- let bar = new Sprite$1({mat:new MeshBasicMaterial({
- side:DoubleSide,
- opacity: barOpacity,
- transparent:true,
- depthTest:false,
- map: texLoader$5.load(Potree.resourcePath+'/textures/rotation_circle.png' ),
- }) ,
- root:object,
- sizeInfo: markerSizeInfo$1,
- dontFixOrient: true,
- viewports:viewer.mapViewer.viewports,
- name:"mapClipRotateBar"
- });
- bar.position.set(0,lineLen+circleWidth/2,0);
- bar.scale.set(circleWidth,circleWidth,circleWidth);
-
- bar.addEventListener('mouseover',()=>{
- bar.material.opacity = 1;
- viewer.dispatchEvent({
- type : "CursorChange", action : "add", name:"mapClipRotate"
- });
- viewer.mapViewer.dispatchEvent('content_changed');
- });
-
- let leave = ()=>{
- bar.material.opacity = barOpacity;
- viewer.dispatchEvent({
- type : "CursorChange", action : "remove", name:"mapClipRotate"
- });
- viewer.mapViewer.dispatchEvent('content_changed');
-
- };
- bar.addEventListener('mouseleave',leave);
- this.addEventListener('dispose', leave);
-
- let lastPos;
- bar.addEventListener('drag',(e)=>{
- var intersectPoint = e.intersectPoint.orthoIntersect;
- if(lastPos){
- let vec1 = new Vector3().subVectors(lastPos, this.center).setZ(0);
- let vec2 = new Vector3().subVectors(intersectPoint, this.center).setZ(0);
- let angle = math.getAngle(vec1,vec2,'z');
- this.angle += angle;
- this.rotateBar.rotation.z = this.angle;
- this.updatePoints();
- this.dispatchEvent({type:'rotate', angle: this.angle});
- }
- lastPos = intersectPoint.clone();
- });
- bar.addEventListener('drop',()=>{
- lastPos = null;
- viewer.dispatchEvent({
- type : "CursorChange", action : "remove", name:"mapClipRotate"
- });
- });
-
- let line = LineDraw.createLine([new Vector3, new Vector3(0,lineLen,0)],{color: color$1});
-
-
- object.add(bar);
- object.add(line);
- this.add(object);
- this.rotateBar = object;
- this.rotateBar.bar = bar;
- }
-
- updatePoints(scale){
- this.points = getPoints(this.center, scale || this.getScale(), this.angle);
- this.getPoint2dInfo(this.points);
- this.update({ifUpdateMarkers:true});
- }
-
- update(options={}){
- super.update(options);
-
- {//update rotateBar
- let center = new Vector3().addVectors(this.points[0], this.points[1]).multiplyScalar(0.5);
- this.rotateBar.position.copy(center);
- this.rotateBar.bar.update();//更新sprite matrix
- }
- {
- for(let i=0;i<4;i++){
- let current = this.points[i];
- let next = this.points[(i+1)%4];
- let mid = new Vector3().addVectors(current, next).multiplyScalar(0.5);
-
- this.updateMarker(this.edgeMarkers[i], mid);
- }
-
-
- }
-
-
- }
-
- dispose(){
- super.dispose();
- this.dispatchEvent('dispose');
-
- }
-
- }
- function getPoints(center, scale, angle=0){
- let points = [
- new Vector3(-scale.x/2, +scale.y/2, 0),
- new Vector3(+scale.x/2, +scale.y/2, 0),
- new Vector3(+scale.x/2, -scale.y/2, 0),
- new Vector3(-scale.x/2, -scale.y/2, 0),
- ];
- var rotMatrix = new Matrix4().makeRotationAxis(new Vector3(0,0,1), angle);//再旋转
- points.forEach(e=>{
- e.applyMatrix4(rotMatrix);
- e.add(center);
- });
- return points
- }
- const defaultBoxWidth = 6; //navvis: 10
- //navvis position: si {x: 0, y: 0, z: 0}
-
- var Clip = {
- bus : new EventDispatcher,
- selectedDatasets : [],
- changeCallback(force){
- if(Potree.settings.isOfficial){
- Common.intervalTool.isWaiting('clipSelectedDatasets', ()=>{ //延时update,防止卡顿
- let pointclouds = this.getIntersectPointcloud();
- if(force || Common.getDifferenceSet(pointclouds,this.selectedDatasets).length){
- this.selectedDatasets = pointclouds;
- //console.error('clipSelectedDatasets',selectedDatasets)
- this.bus.dispatchEvent({type:'updateSelectedDatasets', selectedDatasets:pointclouds.map(e=>e.dataset_id) });
- force = false;
- return true
- }
- }, 300);
- }
- },
- enter:function(){
- this.previousView = {
- position: viewer.images360.position,
- target: viewer.scene.view.getPivot(),
- displayMode : Potree.settings.displayMode,
- //---
- ifShowMarker : Potree.settings.ifShowMarker,
-
- };
- let pointcloud = this.getPointcloud();
- let bound = pointcloud.bound; //只选取其中一个数据集的bound,而非整体,是因为担心两个数据集中间有空隙,于是刚好落在没有点云的地方。
- let boundSize = bound.getSize(new Vector3());
- let target = this.getTarget(bound.getCenter(new Vector3())); //navvis的位置xy是用相机位置 this.ViewService.mainView.getCamera().position 我觉得也可以用第一个漫游点的,或者最接近bound中心的漫游点
- let scale = new Vector3(defaultBoxWidth,defaultBoxWidth, boundSize.z);//z和navvis一样
-
- let eyeDir = viewer.scene.view.direction.clone().setZ(0/* -boundSize.z/3 */).multiplyScalar(-defaultBoxWidth); //为了使所在楼层不变,不修改z
- //let eyeDir = scale.clone().setZ(boundSize.z/3).multiplyScalar(1.3)
- let position = new Vector3().addVectors(target, eyeDir);
-
- Potree.settings.displayMode = 'showPointCloud';
- viewer.setView({
- position ,
- target,
- duration:300,
- callback:function(){
- }
- });
- viewer.setControls(viewer.orbitControls);
- viewer.setLimitFar(false);
-
-
-
- {
- this.box = new BoxVolume({
- clip:true
-
- });
- this.box.name = "ClipBox";
- this.box.position.copy(target);
- this.box.scale.copy(scale);
- //带动mapBox
- this.box.addEventListener('position_changed',e=>{
- this.mapBox.center.setX(this.box.position.x);
- this.mapBox.center.setY(this.box.position.y);
- this.mapBox.updatePoints();
- this.changeCallback();
- });
- this.box.addEventListener('scale_changed',e=>{
- var scale = this.box.scale;
- this.mapBox.updatePoints(scale);
- this.changeCallback();
- });
- this.box.addEventListener('orientation_changed',e=>{
- this.mapBox.angle = this.box.rotation.z;
- this.mapBox.rotateBar.rotation.z = this.mapBox.angle;
- this.mapBox.updatePoints();
- this.changeCallback();
- });
- viewer.scene.addVolume(this.box);
-
- }
-
- {//map
- let boxRotateBack = ()=>{//不知道是不是这么写。 因为可能z的旋转不一定都在z
- this.box.rotation.x = 0;
- this.box.rotation.y = 0;
- };
- this.mapBox = new mapClipBox(target, scale);
- viewer.mapViewer.scene.add(this.mapBox);
- //带动box
- this.mapBox.addEventListener('repos',e=>{
- this.box.position.setX(this.mapBox.center.x);
- this.box.position.setY(this.mapBox.center.y);
- boxRotateBack();
- this.changeCallback();
- });
- this.mapBox.addEventListener('dragChange',e=>{
- var scale = this.mapBox.getScale();
- this.box.scale.setX(scale.x);
- this.box.scale.setY(scale.y);
- this.box.position.setX(this.mapBox.center.x);
- this.box.position.setY(this.mapBox.center.y);
- boxRotateBack();
- this.changeCallback();
- });
- this.mapBox.addEventListener('rotate',e=>{
- this.box.rotation.z = this.mapBox.angle;
- boxRotateBack();
- this.changeCallback();
- });
- }
-
-
-
-
- {
- viewer.setClipTask(ClipTask["SHOW_INSIDE"]);
- //viewer.setClipMethod(ClipMethod["INSIDE_ANY"])
- }
-
- Potree.settings.unableNavigate = true;
- Potree.settings.ifShowMarker = false;
- viewer.updateVisible(viewer.measuringTool.scene, 'clipModel', false);
- //viewer.updateVisible(viewer.mapViewer.cursor, 'clipModel', false)//隐藏地图游标
- viewer.inputHandler.toggleSelection(this.box);
- viewer.inputHandler.fixSelection = true;
- viewer.transformationTool.frame.material.color.set(Potree.config.clip.color);//navvis 15899953
- viewer.setPointStandardMat(true);
-
- {
- this.events = {
- flyToPos : (e)=>{
- let dis = 2;
- let target = e.position;
- //position = new THREE.Vector3().subVectors(target, this.scene.view.direction)
-
- //永远朝向框的中心
- /* let dir = new THREE.Vector3().subVectors(this.box.position, e.position).normalize()
- position = new THREE.Vector3().subVectors(target, dir) */
-
-
- target = this.box.position;
- position = e.position;
- //为了方便缩放操作,直接使用box中心作为target
-
-
- let duration = 1000;
- viewer.scene.view.setView({position, duration, target});
- }
- };
-
- this.bus.addEventListener('flyToPos',this.events.flyToPos);
- }
- this.editing = true;
-
- setTimeout(()=>{this.changeCallback(true);},1);
- },
-
- leave:function(){
- viewer.inputHandler.fixSelection = false;
- viewer.scene.removeVolume(this.box);
-
- this.mapBox.dispose();
- viewer.setControls(viewer.fpControls);
-
- Potree.settings.unableNavigate = false;
- Potree.settings.ifShowMarker = this.previousView.ifShowMarker;
- viewer.updateVisible(viewer.measuringTool.scene, 'clipModel', true);
- //viewer.updateVisible(viewer.mapViewer.cursor, 'clipModel', true)
- viewer.setView(this.previousView);
- viewer.setLimitFar(true);
- viewer.setPointStandardMat(false);
-
-
- {
- this.bus.removeEventListener('flyToPos',this.events.flyToPos);
- this.events = null;
- }
- this.editing = false;
- },
-
- getPointcloud:function(){ //找一个离当前最近的点云,且最好有漫游点
- let pointclouds = viewer.scene.pointclouds.filter(e=>e.panos.length>0);
- if(pointclouds.length == 0)pointclouds = viewer.scene.pointclouds;
- let result = Common.sortByScore(pointclouds,[],[e=>{
- let center = e.bound.getCenter(new Vector3);
- let size = e.bound.getSize(new Vector3).length() / 2;
- let posToCenter = viewer.images360.position.distanceTo(center);
- return size / posToCenter
- }]);
-
- return result[0].item
- },
-
- getTarget:function(boundCenter){//box位置。要找一个有点云的地方。方案1相机位置, 方案2接近相机的漫游点, 方案3接近中心的漫游点。选择方案2,因最大概率有点云
- var target = new Vector3();
- var cameraPos = viewer.images360.position;
- var pano = Common.find(viewer.images360.panos , [], [Images360.sortFunctions.floorDisSquaredToPoint(cameraPos)]);
- if(pano){
- target.copy(pano.position);
- target.setZ(boundCenter.z);
- }else {
- target.copy(boundCenter);
- }
-
- return target
- },
- /* switchMap:function(state){
-
-
- }, */
-
- download:function( ){
-
- if(this.getIntersectPointcloud().length == 0){
- return null
- }
-
-
- var visiPointclouds = viewer.scene.pointclouds.filter(e=> viewer.getObjVisiByReason(e, 'datasetSelection'));
- let data = {
- transformation_matrix: visiPointclouds.map((cloud)=>{
- let data = {
- id: cloud.dataset_id,
- matrix : this.getTransformationMatrix(cloud).elements,
- modelMatrix:(new Matrix4).copy(cloud.transformMatrix).transpose().elements
- };
- return data
- }) ,
- aabb: "b-0.5 -0.5 -0.5 0.5 0.5 0.5" //剪裁空间( 所有点在乘上这个矩阵后, 还能落在 1 * 1 * 1的box内的点就是所裁剪的
-
- };
-
- return data
- //https://testlaser.4dkankan.com/indoor/t-ia44BhY/api/pointcloud/crop
- },
-
-
-
- downloadNoCrop(){//不剪裁 下载整个点云
-
- var visiPointclouds = viewer.scene.pointclouds.filter(e=> viewer.getObjVisiByReason(e, 'datasetSelection'));
- let data = {
- transformation_matrix: visiPointclouds.map((cloud)=>{
- let data = {
- id: cloud.dataset_id,
- matrix : new Matrix4().elements, //固定值
- modelMatrix:(new Matrix4).copy(cloud.transformMatrix).transpose().elements
- };
- return data
- }) ,
- aabb: "b-12742000 -12742000 -12742000 12742000 12742000 12742000" //固定剪裁空间
-
- };
- console.log(data);
- return data
-
-
-
- },
-
-
- getTransformationMatrix:function(pointcloud) {//剪裁矩阵
- var invMatrix = new Matrix4().getInverse(this.box.matrixWorld);
- return (new Matrix4).multiplyMatrices(invMatrix, pointcloud.transformMatrix).transpose()
- },
- getIntersectPointcloud(){
- var boxBound = new Box3(
- new Vector3(-0.5,-0.5,-0.5), new Vector3(0.5,0.5,0.5),
- ).applyMatrix4(this.box.matrixWorld); //large boundingbox
-
- /* var boxTightPoints = this.box.children[0].geometry.vertices.map(e=>e.clone().applyMatrix4(this.matrixWorld))
- console.log(boxTightPoints)
- */
-
- let boxMatrixInverse = new Matrix4().copy(this.box.matrixWorld).invert();
- let boxPoints = [
- new Vector3(boxBound.min.x, boxBound.min.y,0),
- new Vector3(boxBound.max.x, boxBound.min.y,0),
- new Vector3(boxBound.max.x, boxBound.max.y,0),
- new Vector3(boxBound.min.x, boxBound.max.y,0)
- ];
- var intersect = (pointcloud)=>{
-
- if(!pointcloud.bound.intersectsBox(boxBound))return false
- //判断box和点云的tight bound是否相交(因为box可以任意旋转,且实在找不到三维中的立方体相交的函数,所以直接用boxBound)
- var points = pointcloud.getUnrotBoundPoint('all');
- let rings = math.getPolygonsMixedRings([points.slice(0,4), boxPoints] , true);
- //console.log(pointcloud.dataset_id, pointcloud.name, rings.length)
- if(rings.length > 1 )return false
- {//再用frustum和数据集的sphere相交试试,能排除一些错误
- let a = Potree.Utils.isInsideBox(points, boxMatrixInverse);
- if(!a){
- console.log('没能经过isInsideBox测试');
- }
- return a
- }
- return true
-
- };
-
- return viewer.scene.pointclouds.filter(e=>intersect(e))
-
-
- }
-
-
-
- /*
- 裁剪点云时,2D界面显示全部平面图,按楼层切换显示。
- */
-
- };
- class SplitScreen extends EventDispatcher{
- constructor (args = {}) {
- super();
-
- }
-
-
- splitStart(cameraProps){
- let viewports = [];
-
- let subViewports = [viewer.mainViewport];
- if(viewer.mapViewer){
- subViewports.push(viewer.mapViewer.viewports[0]);
- }
-
- let length = cameraProps.length;
- for(let i=0;i<length;i++){
- let prop = cameraProps[i];
- let viewport;
- let v = subViewports.find(e=>e.name == (prop.name2||prop.name));
- if(v){
- viewport = v;
- viewport.left = prop.left; viewport.bottom = prop.bottom; viewport.width = prop.width; viewport.height = prop.height;
- }
-
- if(!viewport){
- let view = new View();
- if(prop.limitBound)view.limitBound = prop.limitBound;
- viewport = new Viewport(view , this.getOrthoCamera(), prop );
- //viewport.unableDepth = true //depthBasicMaterial等在此viewport中不开启depth
- prop.direction && (view.direction = prop.direction);
- }
- if(viewport.camera.type == 'OrthographicCamera' ){
- viewport.targetPlane = new Plane();
- viewport.shiftTarget = new Vector3; //project在targetPlane上的位置
- }
- viewports.push(viewport);
- }
- viewer.viewports = viewports;
- viewer.updateScreenSize({forceUpdateSize:true});
- viewports.forEach(viewport=>{
- if(viewport.name == 'MainView')return
- this.viewportFitBound(viewport, viewer.bound.boundingBox , viewer.bound.center);
- });
- return viewports
- }
-
-
- unSplit(){
- this.unfocusViewport();
- viewer.viewports = [viewer.mainViewport];
- viewer.mainViewport.width = 1;
- viewer.mainViewport.height = 1;
- viewer.mainViewport.left = 0;
- viewer.mainViewport.bottom = 0;
- viewer.updateScreenSize({forceUpdateSize:true});
- }
-
- viewportFitBound(viewport, bound, center, duration=0){
- let view = viewport.view;
- let info = {bound};
-
- viewport.targetPlane.setFromNormalAndCoplanarPoint( view.direction.clone(), viewer.bound.center );
- viewport.targetPlane.projectPoint(center, viewport.shiftTarget); //target转换到过模型中心的平面,以保证镜头一定在模型外 this.shiftTarget是得到的
-
- info.endPosition = this.getPosOutOfModel(viewport.shiftTarget, view );
-
- //if(viewport.name == 'mapViewport')info.endPosition.z = Math.max(Potree.config.map.cameraHeight, info.endPosition.z)
-
-
-
-
- info.margin = {x:30, y:30};
- view.moveOrthoCamera(viewport, info , duration );
- }
-
- getPosOutOfModel(shiftTarget, view){
- let {boundSize, center} = viewer.bound;
- let expand = 10;
- let radius = boundSize.length() / 2;
- let position = shiftTarget.clone().sub(view.direction.clone().multiplyScalar(radius + expand));
-
- return position
- }
-
- updateCameraOutOfModel(){//因为移动模型导致模型超出相机外,所以更新位置
- viewer.viewports.forEach((viewport, i )=>{
- if(viewport != viewer.mainViewport){
- viewport.targetPlane.setFromNormalAndCoplanarPoint( viewport.view.direction.clone(), viewer.bound.center );
- viewport.targetPlane.projectPoint(viewport.view.position, viewport.shiftTarget); //target转换到过模型中心的平面,以保证镜头一定在模型外 this.shiftTarget是得到的
-
- let endPosition = this.getPosOutOfModel(viewport.shiftTarget, viewport.view );
- //if(viewport.name == 'mapViewport')endPosition.z = Math.max(Potree.config.map.cameraHeight, endPosition.z)
- viewport.view.position.copy(endPosition);
- }
- });
- }
-
-
-
- getOrthoCamera(){
- return new OrthographicCamera(-100, 100, 100, 100, 0.01, 10000)
- }
-
- focusOnViewport(name){//全屏
- viewer.viewports.forEach((viewport, i )=>{
- if(viewport.name == name){
- this.focusInfo = {
- name,
- left : viewport.left, bottom : viewport.bottom, height : viewport.height, width : viewport.width
- };
- viewport.left = 0; viewport.bottom = 0; viewport.height = 1; viewport.width = 1;
-
- }else {
- viewport.active = false;
- }
- });
-
- viewer.updateScreenSize({forceUpdateSize:true});
- }
-
- unfocusViewport(){
- if(!this.focusInfo)return
- viewer.viewports.forEach((viewport, i )=>{
- if(this.focusInfo.name == viewport.name){//全屏的恢复
- viewport.left = this.focusInfo.left;
- viewport.bottom = this.focusInfo.bottom;
- viewport.height = this.focusInfo.height;
- viewport.width = this.focusInfo.width;
- }
- viewport.active = true;
- });
- viewer.updateScreenSize({forceUpdateSize:true});
- this.focusInfo = null;
- }
-
- }
- const viewportProps = [
- {
- left:0.5,
- bottom:0.5,
- width: 0.5,height:0.5,
- name : 'MainView',
- //view: viewer.scene.view,
- active: true,
- },
- {
- left:0,
- bottom:0.5,
- width: 0.5,height:0.5,
- name : 'top',
- name2 : 'mapViewport',
- axis:["x","y"],
- direction : new Vector3(0,0,-1), //镜头朝向
- //axisSign:[1,1],
- active: true,
- //相机位置在z轴正向
- },
- {
- left:0.5,
- bottom:0,
- width: 0.5,height:0.5,
- name : 'right',
- axis:["y","z"],
- direction : new Vector3(1,0,0),
- //axisSign:[1,1],
- active: true,
- //相机位置在x轴负向 右下角屏
- },
- {
- left:0,
- bottom:0,
- width: 0.5,height:0.5,
- name : 'back',
- axis:["x","z"],
- direction : new Vector3(0,-1,0),
- //axisSign:[-1,1], // 从镜头方向看 x向左 所以取负
- active: true,
- //相机位置在y轴正向 左下角屏
- },
- ];
- var SplitScreen4Views = new SplitScreen();
-
-
- SplitScreen4Views.split = function(o={}){
- var defaultCamera = viewer.scene.getActiveCamera();
-
- let {boundSize, center} = viewer.bound;
-
- viewer.setLimitFar(false);
- viewer.mapViewer.attachToMainViewer(true,'split4Screens','dontSet');
-
- let viewports = this.splitStart(viewportProps);
-
- //覆盖在map上、点云等其他物体之下的一层背景
- let mapViewport = viewer.mapViewer.viewports[0];
- mapViewport.noPointcloud = false;
- //隐藏地图游标
- //viewer.updateVisible(viewer.mapViewer.cursor, 'split4Screens', false)
- /* viewer.images360.panos.forEach(pano=>{
- viewer.updateVisible(pano.mapMarker, 'split4Screens', false) //希望这时候mapMarker已经建好了吧
- }) */
-
-
-
-
- //材质
- this.statesBefore = {
- pointDensity : Potree.settings.pointDensity,
- displayMode : Potree.settings.displayMode,
-
- position: viewer.images360.position,
- target: viewer.scene.view.getPivot(),
-
-
- //---
- //ifShowMarker : Potree.settings.ifShowMarker,
- };
-
- viewer.setPointStandardMat(true,null,true); //切换到标准模式(主要为了mainViewport) 点云使用标准大小
-
- var matBefore = {
- opacity : new Map()
- };
- var newOpacityMap = new Map();
-
- viewer.scene.pointclouds.forEach(e=>{
- matBefore.opacity.set(e, e.temp.pointOpacity);
- matBefore.colorType = e.material.activeAttributeName;
-
- /* {
- var map = new Map()
- newOpacityMap.set(e, map )
- var size = e.bound.getSize()
- viewports.forEach(viewport=>{//根据bound设置opacity,越小的要靠越近,需要大的opacity。但似乎影响太大了
- if(viewport.name == 'MainView')return;
- var prop = viewportProps.find(v => viewport.name == v.name2||viewport.name == v.name)
- let axis = prop.axis
- var width = size[axis[0]]
- var height = size[axis[1]]
- var area = width * height
- map.set(viewport, 5000/area);
- })
-
- } */
- });
-
- let beforeRender = function(){
- viewer.scene.pointclouds.forEach(e=>{
- if(this.name == "MainView"){
- e.material.activeAttributeName = matBefore.colorType; // 'rgba'
-
- e.material.useFilterByNormal = false;
- e.changePointOpacity(matBefore.opacity.get(e)); //1 //恢复下 e.temp.pointOpacity 其实就是1
-
- Potree.settings.pointDensity = 'fourViewportsMain';/* 'fourViewports' */ //本来想比另外三屏高一点质量,结果发现会闪烁,因为点云加载需要时间 (navvis仿版也是一样,以后看看能否优化)
-
- }else {
- e.material.activeAttributeName = "color";
- e.material.useFilterByNormal = true;
-
- Potree.settings.pointDensity = 'fourViewports'; //强制降低点云质量
-
- e.changePointOpacity(0.6/* newOpacityMap.get(e).get(viewport), true */); //多数据集有的数据集很小,放大后显示特别淡
- //console.log(e.name, viewport.name, e.temp.pointOpacity, e.material.opacity)
- }
- });
- };
- viewports.forEach(viewport=>{viewport.beforeRender = beforeRender;});
-
-
-
- this.enableMap(false);
- this.enableFloorplan(false);
- viewer.mapViewer.setViewLimit('expand'); //多数据集距离远时可以任意远,所以不限制了。但是这样就会看到地图边界了怎么办?
- //viewer.dispatchEvent({'type': 'beginSplitView' })
- //viewer.updateScreenSize({forceUpdateSize:true})
-
-
-
- //this.viewportFitBound(mapViewport, boundSize, center)
- //Potree.settings.ifShowMarker = false
- Potree.settings.displayMode = 'showPointCloud';
- };
-
-
- SplitScreen4Views.recover = function(){
- this.unSplit();
-
- /* const {width, height} = viewer.renderer.getSize(new THREE.Vector2());
- viewer.renderer.setViewport(0,0,width,height)
- viewer.renderer.setScissorTest( false ); */
-
- viewer.setView({
- position: this.statesBefore.position,
- target: this.statesBefore.target,
- duration:300,
- callback:function(){
- }
- });
-
-
-
- viewer.mainViewport.beforeRender = null;
- viewer.setLimitFar(true);
-
- let mapViewport = viewer.mapViewer.viewports[0];
- viewer.mapViewer.attachToMainViewer(false);
- //viewer.updateVisible(viewer.mapViewer.cursor, 'split4Screens', true)
- /* viewer.images360.panos.forEach(pano=>{
- viewer.updateVisible(pano.mapMarker, 'split4Screens', true)
- }) */
- mapViewport.noPointcloud = true;
- {
- this.enableMap(Potree.settings.mapEnable);
- this.enableFloorplan(Potree.settings.floorplanEnable);
- if(this.floorplanListener){
- viewer.mapViewer.mapLayer.removeEventListener( 'floorplanLoaded', this.floorplanListener );
- this.floorplanListener = null;
- }
- }
-
- Potree.settings.pointDensity = this.statesBefore.pointDensity;
- if(!Potree.settings.isOfficial){
- Potree.settings.displayMode = this.statesBefore.displayMode;
- }
-
- viewer.scene.pointclouds.forEach(e=>{
- //e.material.color.set(this.statesBefore.mat.color)
- //e.material.activeAttributeName = this.statesBefore.mat.colorType
- e.material.useFilterByNormal = false;
- //e.material.opacity = this.statesBefore.mat.opacity
- });
- viewer.setPointStandardMat(false);
- viewer.mapViewer.setViewLimit('standard');
-
- //Potree.settings.ifShowMarker = this.statesBefore.ifShowMarker
- //viewer.dispatchEvent({'type': 'finishSplitView' })
- //viewer.updateScreenSize({forceUpdateSize:true})
-
- };
-
- SplitScreen4Views.updateMapViewerBG = function(){
- let mapViewport = viewer.mapViewer.viewports[0];
- if(this.floorplanEnabled || this.mapEnabled){
- mapViewport.background = 'overlayColor';
- mapViewport.backgroundColor = new Color(0,0,0);
- mapViewport.backgroundOpacity = 0.5;
- }else {
- mapViewport.background = null;
- mapViewport.backgroundColor = null;
- mapViewport.backgroundOpacity = null;
- }
- };
- SplitScreen4Views.setFloorplanDisplay = function(e, show=false){
- //viewer.updateVisible(e.floorplan.objectGroup, 'splitScreen', !!show)
- //e.floorplan.objectGroup.visible = !!show
- //viewer.mapViewer.mapLayer.needUpdate = true
- e.floorplan.setEnable(show);
- };
-
- SplitScreen4Views.enableMap = function(enable){
- let map = viewer.mapViewer.mapLayer.maps.find(e=>e.name == 'map');
- //viewer.updateVisible(map.objectGroup, 'splitScreen', !!enable)
- //map.objectGroup.visible = !!enable
- //if(enable)viewer.mapViewer.mapLayer.needUpdate = true //加载地图
- map.setEnable(!!enable);
-
-
- //viewer.mapViewer.mapGradientBG = viewer.background == 'gradient' && !enable
- this.mapEnabled = enable;
- this.updateMapViewerBG();
-
-
-
- },
- //直接覆盖原设置
- SplitScreen4Views.enableFloorplan = function(enable){ //是否让自定义的平面图显示
- let floorplans = viewer.mapViewer.mapLayer.maps.filter(e=>e.name.includes('floorplan'));
-
- if(this.floorplanListener){
- viewer.mapViewer.mapLayer.removeEventListener( 'floorplanLoaded', this.floorplanListener );
- }
- this.floorplanListener = (e)=>{
- this.setFloorplanDisplay(e, enable);
- };
-
- viewer.mapViewer.mapLayer.addEventListener( 'floorplanLoaded', this.floorplanListener ); //万一之后才加载
-
-
- if(!enable){
- //隐藏平面图
- floorplans.forEach(floorplan=>this.setFloorplanDisplay({floorplan},false));
-
- }else {
-
- floorplans.forEach(floorplan=>this.setFloorplanDisplay({floorplan},true));
-
- }
-
-
- if (enable && floorplans.length == 0) Potree.loadMapEntity('all',true);
-
- this.floorplanEnabled = enable;
- this.updateMapViewerBG();
- },
- /* viewportFitBound:function(viewport, boundSize, center){ //使一个viewport聚焦在某个范围
- var prop = viewportProps.find(v => viewport.name == v.name2||viewport.name == v.name)
- let axis = prop.axis
- let expand = 10;
- let position = center.clone()
- var moveAtAxis = ['x','y','z'].find(e=>!(axis.includes(e)))
-
- if(viewport.name == 'mapViewport'){
- let ori = viewport.view.position[moveAtAxis]
- position[moveAtAxis] = ori //不改变这个值,尤其是mapViewer中的z
- }else{
- position[moveAtAxis] += boundSize[moveAtAxis]/2+expand//移动到bounding边缘外
- }
-
- viewport.view.position.copy(position)
-
- var width = Math.max(boundSize[axis[0]], boundSize[axis[1]] * viewport.camera.aspect)//视口宽度(米)
- var margin = 50 //px
- viewport.camera.zoom = (viewport.resolution.x - margin) / width
- viewport.camera.updateProjectionMatrix()
- },
- */
-
- SplitScreen4Views.focusOnPointCloud = function(pointcloud){//三个屏都聚焦在这个点云
- var boundSize = pointcloud.bound.getSize(new Vector3);
- var center = pointcloud.bound.getCenter(new Vector3);
- let target = pointcloud.panosBound && pointcloud.panosBound.center; //看向pano集中的地方,也就是真正有点云的地方。(因为需要展示所有点云,所以没办法用这个做为center)
- this.focusOnObject(pointcloud.bound, center,target);
-
- viewer.flyToDataset({pointcloud, dontMoveMap:true, duration:0});
- };
- SplitScreen4Views.focusOnObject = function(bound, center, target, duration=0){
- viewer.viewports.forEach(e=>{
- if(e.name == 'MainView'){
- /* let len = boundSize.length()
- let distance = THREE.Math.clamp(e.view.position.distanceTo(center), len * 0.01, len*0.3 ) //距离限制
- //viewer.focusOnObject({position:center}, 'point', duration, {distance, direction: e.view.direction,dontMoveMap:true} )//平移镜头
- //为了方便定位,直接飞到中心位置:
- e.view.setView({
- position:center, duration, target
- }) */
- }else {
- this.viewportFitBound(e, bound, center);
- }
- });
- };
- var Alignment = {
- SplitScreen: SplitScreen4Views,
- handleState:null, //操作状态 'translate'|'rotate'
- bus: new EventDispatcher(),
- history:[],
- prepareRecord : true,
-
- writeToHistory(content){
- if(!this.prepareRecord)return;
- this.prepareRecord = false;
- this.history.push(content);
- },
-
-
- undo(){//撤销一步
- let last = this.history.pop();
- last && last.forEach(item=>{
- this.applyTemp(item);
- });
-
- },
-
- applyTemp(item){
- var pointcloud = viewer.scene.pointclouds.find(p=>p.dataset_id+p.name == item.sid);
- pointcloud.orientationUser = item.orientationUser;
- pointcloud.translateUser = item.translateUser;
- this.setMatrix( pointcloud );
- },
- getTemp(pointclouds){//记录最近一次保存后的状态,便于恢复
- pointclouds = pointclouds || viewer.scene.pointclouds;
- return pointclouds.map(e=>{
- return {
- sid : e.dataset_id+e.name,
- orientationUser : e.orientationUser,
- translateUser : e.translateUser.clone(),
- }
- } )
- },
-
-
-
- init:function(){
- let rotateInfo;
-
- viewer.fpControls.addEventListener("transformPointcloud",(e)=>{
- if(e.pointclouds[0].dataset_id == Potree.settings.originDatasetId){//禁止手动移动初始数据集
- return this.bus.dispatchEvent('forbitMoveOriginDataset')
- }
-
-
- this.writeToHistory(this.getTemp(e.pointclouds) );
-
-
- if(this.handleState == 'translate'){
- e.pointclouds.forEach(cloud=>Alignment.translate(cloud, e.moveVec));
-
-
- }else if(this.handleState == 'rotate'){
- if(Potree.settings.editType == 'pano'){
-
- let center = e.intersectStart; //旋转中心是mousedown的位置
- if(e.intersectPoint.equals(center))return
- if(!rotateInfo){
- rotateInfo = {
- orientationUser : e.pointclouds[0].orientationUser,
- //vecStart : e.moveVec, // 首次移动向量 只有八个方向,精度太小,所以延迟
- pointclouds: e.pointclouds
- };
- this.bus.dispatchEvent({type:'rotateStart', startPoint:center});
- return
- }else if(!rotateInfo.vecStart){
- let vec = new Vector3().subVectors(e.intersectPoint, center).setZ(0);
- if(vec.length() * e.camera.zoom > 30){ //在屏幕上距离初始点有一定距离后开始算
- //console.log('moveVec',vec)
- rotateInfo.vecStart = vec;
- }
- }else {
-
- let vec = new Vector3().subVectors(e.intersectPoint, center).setZ(0);
- let angle = math.getAngle(rotateInfo.vecStart,vec,'z');
- let diffAngle = rotateInfo.orientationUser + angle - rotateInfo.pointclouds[0].orientationUser;
-
- rotateInfo.pointclouds.forEach(cloud=>{
-
- /* let centerNoTranfrom = Potree.Utils.datasetPosTransform({ toDataset: true, pointcloud:cloud, position: center }) //中心点在数据集中的位置
- Alignment.rotate(cloud, null, diffAngle)
-
- let centerNow = Potree.Utils.datasetPosTransform({ fromDataset: true, pointcloud:cloud, position: centerNoTranfrom }) //中心点的现在位置
- let shift = new THREE.Vector3().subVectors( center, centerNow); //偏移量
- Alignment.translate(cloud,shift) //使center还保留在原位
- //let centerNow1 = Potree.Utils.datasetPosTransform({ fromDataset: true, pointcloud:rotateInfo.pointcloud, position: centerNoTranfrom }) //中心点的现在位置
- */
-
- Alignment.rotateAround(center, cloud, null, diffAngle);
-
-
- });
-
-
- }
- this.bus.dispatchEvent({type:'rotate', endPoint: e.intersectPoint});
-
- }else {
- let center = e.pointclouds[0].translateUser; //移动到的位置就是中心
- if(e.intersectPoint.equals(center))return
- if(!rotateInfo){
- rotateInfo = {
- orientationUser : e.pointclouds[0].orientationUser,
- vecStart : new Vector3().subVectors(e.intersectStart, center).setZ(0),
- //pointclouds: e.pointclouds
- pointcloud: e.pointclouds[0]
- };
- }else {
- let vec = new Vector3().subVectors(e.intersectPoint, center).setZ(0);
- let angle = math.getAngle(rotateInfo.vecStart,vec,'z');
- let diffAngle = rotateInfo.orientationUser + angle - rotateInfo.pointcloud.orientationUser;
- Alignment.rotate(rotateInfo.pointcloud, null, diffAngle);
- }
- }
- }
- });
-
-
- viewer.fpControls.addEventListener("end",(e)=>{
- rotateInfo = null;
- this.prepareRecord = true;
- });
-
- viewer.inputHandler.addEventListener('keydown',e=>{
- if(e.keyCode == 90 && e.event.ctrlKey){//Z
- this.undo();
- }
- });
-
-
- // cursor:
-
- let updateCursor = (e)=>{
- if(e.drag)return //仅在鼠标不按下时更新:
-
- let handleState = Alignment.handleState;
-
- if(e.hoverViewport.alignment && handleState && e.hoverViewport.alignment[handleState]){
- if(handleState == 'translate'){
- if( e.intersectPoint && e.intersectPoint.location ){
- viewer.dispatchEvent({
- type : "CursorChange", action : "add", name:"movePointcloud"
- });
- }else {
- viewer.dispatchEvent({
- type : "CursorChange", action : "remove", name:"movePointcloud"
- });
- }
- }else if(handleState == 'rotate'){
- if( e.intersectPoint && e.intersectPoint.location ){
- viewer.dispatchEvent({
- type : "CursorChange", action : "add", name:"rotatePointcloud"
- });
- }else {
- viewer.dispatchEvent({
- type : "CursorChange", action : "remove", name:"rotatePointcloud"
- });
- }
- }
- }else {
- //清空:
- viewer.dispatchEvent({
- type : "CursorChange", action : "remove", name:"movePointcloud"
- });
- viewer.dispatchEvent({
- type : "CursorChange", action : "remove", name:"rotatePointcloud"
- });
- }
- };
-
- if(Potree.settings.editType != 'pano'){
- viewer.addEventListener('global_mousemove',updateCursor);
- viewer.addEventListener('global_drop',updateCursor);//拖拽结束
- }
-
-
-
- viewer.addEventListener('updateModelBound', (e)=>{
- if(this.editing){
- this.SplitScreen.updateCameraOutOfModel();
- }
- });
-
-
- },
-
-
- setMatrix : function(pointcloud){
- var vec1 = pointcloud.position; //position为数据集内部的偏移,在navvis中对应的是dataset.pointCloudSceneNode的children[0].position
- var vec2 = pointcloud.translateUser;
- var angle = pointcloud.orientationUser;
- var pos1Matrix = new Matrix4().setPosition(vec1);//先移动到点云本身应该在的初始位置(在4dkk里和其他应用中都是在这个位置的,也能和漫游点对应上)
- var rotMatrix = new Matrix4().makeRotationAxis(new Vector3(0,0,1), angle);//再旋转
- var pos2Matrix = new Matrix4().setPosition(vec2);//最后是平移
-
- var matrix = new Matrix4().multiplyMatrices(pos2Matrix, rotMatrix);
- pointcloud.transformMatrix = matrix.clone();//为该数据集的变化矩阵。 对应navvis的m2w_
- pointcloud.transformInvMatrix.copy(matrix).invert();
- pointcloud.rotateMatrix = rotMatrix;
- pointcloud.rotateInvMatrix.copy(rotMatrix).invert();
-
-
- pointcloud.panos.forEach(e=>e.transformByPointcloud());
-
-
- matrix = new Matrix4().multiplyMatrices(matrix, pos1Matrix);
-
-
-
- pointcloud.matrix = matrix;
- //pointcloud.matrixWorldNeedsUpdate = true //更新matrixWorld (非计算,直接赋值)
- pointcloud.updateMatrixWorld(true);
-
- if(this.editing){
- Alignment.changeCallBack && Alignment.changeCallBack();
- }
-
- if(pointcloud.spriteNodeRoot){
- pointcloud.spriteNodeRoot.matrixWorld.copy(pointcloud.matrixWorld);//.multiplyMatrices(pointcloud.matrixWorld, pointcloud.matrixWorld);
- }
-
- pointcloud.updateBound();
- pointcloud.getPanosBound();
- viewer.updateModelBound();
- },
-
-
- rotateAround(center, pointcloud, deg, angle){//绕center点转动
- var angle = angle != void 0 ? angle : MathUtils.degToRad(deg);
- let vec1 = new Vector3().subVectors(pointcloud.translateUser, center);
- let rotMatrix = new Matrix4().makeRotationAxis(new Vector3(0,0,1), angle);
- let vec2 = vec1.clone().applyMatrix4(rotMatrix); //将到旋转中心的偏差也转动
- let vec3 = new Vector3().subVectors(vec2,vec1); //这个就是多出来的一步translateUser
- this.rotate(pointcloud, deg, angle);
- this.translate(pointcloud, vec3);
- //绕点转动就是比普通转动多一步移动到相对center的某个位置。 1 初始点云移动到自己的position; 2 移动一个vec1 3绕原点旋转 4再移动一个原本的translateUser。 绘制出来后发现移动量就是第二步vec旋转后的偏移
- },
-
-
- rotate:function(pointcloud, deg, angle){//绕各自中心转动(各自的position) 假设点云位移position后0,0,0就是它的中心了(根据navvis观察这样做是绕同一个点旋转的)
- var angle = angle != void 0 ? angle : MathUtils.degToRad(deg); //正逆负顺
- pointcloud.orientationUser += angle;
- Alignment.setMatrix(pointcloud);
- },
- translate:function(pointcloud, vec){
- pointcloud.translateUser.add(vec);
- Alignment.setMatrix(pointcloud);
- },
-
-
-
-
- enter:function(){
- //this.saveTemp()
- this.originData = this.getTemp();
-
- this.SplitScreen.split({alignment:true});
-
- viewer.images360.panos.forEach(pano=>{
- viewer.updateVisible(pano.mapMarker, 'split4Screens', false);
- });
-
- viewer.viewports.find(e=>e.name == 'mapViewport').alignment = {rotate:true,translate:true};
- viewer.viewports.find(e=>e.name == 'right').alignment = {translate:true};
- viewer.viewports.find(e=>e.name == 'back').alignment = {translate:true};
-
-
- this.editing = true;
-
- viewer.updateFpVisiDatasets();
-
-
-
- },
- leave:function(){
- this.switchHandle(null);
-
- /* this.originData.forEach(e=>{//恢复
- var pointcloud = viewer.scene.pointclouds.find(p=>p.dataset_id == e.id)
- this.translate(pointcloud, new THREE.Vector3().subVectors(e.translateUser , pointcloud.translateUser))
- this.rotate(pointcloud, null, e.orientationUser - pointcloud.orientationUser)
- }) */
- this.originData.forEach(e=>{//恢复
- this.applyTemp(e);
- });
-
-
- this.SplitScreen.recover();
- viewer.images360.panos.forEach(pano=>{
- viewer.updateVisible(pano.mapMarker, 'split4Screens', true);
- });
- this.editing = false;
- this.history.length = 0;
- viewer.updateFpVisiDatasets();
-
- }
-
- ,
- switchHandle:function(state){
- this.handleState = state;
- //清空:
- viewer.dispatchEvent({
- type : "CursorChange", action : "remove", name:"movePointcloud"
- });
- viewer.dispatchEvent({
- type : "CursorChange", action : "remove", name:"rotatePointcloud"
- });
-
- this.bus.dispatchEvent({type:'switchHandle' , state });
-
-
- },
-
-
- save: function(){//保存所有数据集的位置和旋转
- let callback = ()=>{//保存成功后
- this.originData = this.getTemp(); //this.saveTemp();
- //需要修改 测量线的position。漫游点已经实时修改了
-
- viewer.scene.measurements.forEach(e=>e.transformByPointcloud());
- viewer.images360.updateCube(viewer.bound);
- };
-
- var data = viewer.scene.pointclouds.map(e=>{
- let pos = viewer.transform.lonlatToLocal.inverse(e.translateUser.clone());
-
-
- return {
- id: e.dataset_id,
- orientation : e.orientationUser,
- location:[pos.x, pos.y, pos.z],
- //transformMatrix: e.transformMatrix.elements,
- }
- });
- //data = JSON.stringify(data)
-
- //test: 退出后保留结果
- if(!Potree.settings.isOfficial){
- callback();
- }
-
- return {data, callback}
- }
-
-
- };
- let texLoader$6 = new TextureLoader();
-
- let markerMats$2;
- let markerSizeInfo$2 = {width2d:35};
- let color$2 = new Color('#FFF');
- let faceMats;
- let getFaceMat = (name)=>{
- if(!faceMats){ //navvis材质可以搜gridTexture
- let gridTex = texLoader$6.load( Potree.resourcePath+'/textures/gridmap.png' );
- gridTex.wrapS = gridTex.wrapT = RepeatWrapping;
- //gridTex.repeat.set(0.5,0.5)//放大一些
- faceMats = {
- dataset: new MeshStandardMaterial({
- color:812922,
- side:DoubleSide,
- opacity:0.2,
- transparent:true,
- depthTest:false,
- wireframe:true
- }),
- building: new MeshStandardMaterial({
- color:812922, metalness: 0.2, roughness:0.8,
- side:DoubleSide,
- opacity:0.1,
- transparent:true,
- depthTest:true
- }),
- buildingSelect: new MeshStandardMaterial({
- color:36582, metalness: 0, roughness:1,
- side:DoubleSide,
- opacity:0.1,
- transparent:true,
- depthTest:true
- }),
- floor: new MeshStandardMaterial({
- color:11708469, metalness: 0.1, roughness:1,
- side:DoubleSide,//BackSide,
- opacity:0.05,
- transparent:true,
- depthTest:true,
- }),
-
- /* floorSelect: new THREE.MeshStandardMaterial({
- color:16707151, metalness: 0, roughness:1,
- side:THREE.DoubleSide,
- opacity:1,
- transparent:true,
- depthTest:true,
- polygonOffset : true,//是否开启多边形偏移
- polygonOffsetFactor : -0.75,//多边形偏移因子
- polygonOffsetUnits : -4.0,//多边形偏移单位
- map: gridTex,
- }), */
- floorSelect: new DepthBasicMaterial({
- map: gridTex,
- color:16707151,
- side:DoubleSide,//BackSide,
- opacity:1,
- transparent:true,
- useDepth : true,
- /* polygonOffset : true,//是否开启多边形偏移
- polygonOffsetFactor : -0.75,//多边形偏移因子
- polygonOffsetUnits : -4.0,//多边形偏移单位 */
-
- clipDistance : 1, occlusionDistance:1, /* occlusionDistance:变为backColor距离, clipDistance:opacity到达0或者1-maxClipFactor时的距离 */
- maxClipFactor:0.4, backColor:'#efe' //backColor:"#669988" ,
-
- }),
-
- room: new MeshStandardMaterial({
- color:"#ff44ee", metalness: 0, roughness:1,
- side:DoubleSide,//BackSide,
- opacity:0.08,
- transparent:true,
- depthTest:false,
- }),
- /* roomSelect: new THREE.MeshStandardMaterial({
- color:"#ff44ee", metalness: 0.3, roughness:1,
- side:THREE.DoubleSide,//BackSide,
- opacity:1,
- transparent:true,
- depthTest:true,
- polygonOffset : true,//是否开启多边形偏移.(开启是因为和floor重叠了会闪烁)
- polygonOffsetFactor : -0.75,//多边形偏移因子
- polygonOffsetUnits : -4.0,//多边形偏移单位
- map: gridTex,
- }), */
- roomSelect: new DepthBasicMaterial({
- map: gridTex,
- color:"#ff44ee",
- side:DoubleSide,//BackSide,
- opacity:1,
- transparent:true,
- useDepth : true,
- /* polygonOffset : true,//是否开启多边形偏移
- polygonOffsetFactor : -0.75,//多边形偏移因子
- polygonOffsetUnits : -4.0,//多边形偏移单位 */
-
- clipDistance : 1, occlusionDistance:0.5, /* occlusionDistance:变为backColor距离, clipDistance:opacity到达0或者1-maxClipFactor时的距离 */
- maxClipFactor:0.6, backColor:'#ff88dd'//"#cc99c2" ,
-
- })
- };
- }
- return faceMats[name]
- };
-
- class BuildingBox extends ctrlPolygon{//建筑实体,包括building, floor, room
- constructor(prop) {
- prop.dimension = '3d';
- //prop.name = Potree.config.siteModel.names[prop.buildType] +
-
- super('siteModel_'+prop.buildType, prop);
-
-
-
- this.midMarkers = [];
- this.buildChildren = [];//子实体
- this.holes = []; //在这创建的hole
- this.parentHoles = [];//floor从building那得到的当层holes
- this.mats = {}; //材质
-
- this.panos = this.panos || [];
- this.center; //中心点
-
- if(this.buildType=='floor'){
-
- this.points = prop.points = this.buildParent.points;//完全等于建筑的点
- this.buildParent.holes.forEach(hole=>{//从building获取holes
- let floorHole = new BuildingBox({
- buildType : 'hole',
- buildParent:this,
- originHole : hole, //整栋大楼在当层的hole
- ifDraw: this.ifDraw || Potree.settings.drawEntityData
- });
- this.parentHoles.push(floorHole);
- this.add(floorHole);
-
- floorHole.points = hole.points;//完全等于建筑的点
- });
- }
- if(this.buildType == 'room' || this.buildType == 'hole'){
- this.restrictArea = this.buildParent; //不能超出的区域
- }
-
- if(this.ifDraw){ //只存储空间模型信息,不绘制
- if(this.buildType != 'hole'){
- this.box = this.createBox();
- this.add(this.box);
- }
- {
- this.lineMesh = LineDraw.createLine([],{color: color$2});
- this.lineMesh.name = 'buildingLines';
- this.lineMesh.visible = false;
- this.add(this.lineMesh);
- viewer.setObjectLayers(this.lineMesh, 'bothMapAndScene' );
- }
-
-
- this.addEventListener('dragChange',(e)=>{ //修改中点
- this.updateTwoMidMarker(e.index);
- });
-
- }
-
- this.initData(prop);
-
-
- }
-
-
- initData(prop){
- if(prop.ifDraw){
- super.initData(prop);
- }else {
- if(prop.points){
- this.points = prop.points;
- }
-
-
- }
-
- }
-
- intersectPointcloudVolume(pointcloud){//和pointcloud的重叠体积
- var bound = this.getBound();
- let bound2 = pointcloud.bound;
- if(!bound.intersectsBox(bound2)) return 0;
-
-
- let {zMin , zMax} = this.getRealZ();
- let min = Math.min(zMin, bound2.min.z);
- let max = Math.max(zMax, bound2.max.z);
- let height1 = zMax - zMin;
- let height2 = bound2.max.z-bound2.min.z;
- let coverHeight = height1 + height2 - (max-min);//重叠高度 <=0是没重叠
-
- let boxPoints = pointcloud.getUnrotBoundPoint(); //获取tightBound的四个点。 如果是有旋转角度的点云,这个和pointcloud.bound的四个点是不一致的,覆盖面积小于pointcloud.bound
-
- let areaWhole = 0;
- let area1 = this.getArea();
- let area2 = Math.abs(math.getArea(boxPoints));
-
- {//计算points与点云总面积 (但是把hole也加入了面积)(并集,重叠部分只算一次)
- let rings = math.getPolygonsMixedRings([this.points, boxPoints] );
-
- rings.forEach(e=>{
- areaWhole+=e.area;
- });
- }
-
- let coverHoleArea = 0; //holes与数据集重叠的部分
- let holes = this.holes.concat(this.parentHoles);
- let holesArea = 0; //所有holes面积相加
- let areaHoleWithPointcloud = 0; //hole和点云的面积并集
-
- if(holes.length>0){//还要再扣除holes与数据集重叠的部分。其中holes为mix轮廓
- let outHoles = [];//没有重合的holes的外轮廓
- /* if(holes.length>=2){//合并holes。如果能在绘制时直接合并holes就好啦,这步就转移到那去,但是要删除hole好麻烦
-
- let holes_ = holes.map(e=>e.points)
-
- outHoles = math.getPolygonsMixedRings(holes_, true )
- outHoles.forEach(e=>{
- holesArea+=e.area
- })
- outHoles = outHoles.map(e=>e.points)
-
-
- }else{
- outHoles = holes.map(e=>e.points)
- outHoles.forEach(e=> holesArea += Math.abs(math.getArea(e)))
- } */
- holesArea = this.getHolesArea();
-
- //holes与数据集重叠的部分
-
- {
- let polygons = outHoles.concat([boxPoints]);
- let rings = math.getPolygonsMixedRings(polygons);
- rings.forEach(e=>{
- areaHoleWithPointcloud+=e.area;
- });
- coverHoleArea = holesArea + area2 - areaHoleWithPointcloud;//hole和点云的交集
- }
-
- }
-
-
- let coverArea = area1 + area2 - areaWhole - coverHoleArea; //重叠面积
-
- return coverArea * coverHeight
- }
-
-
- addHole(points=[]){
- let prop = {
- buildType : 'hole',
- zMin : this.zMin,
- zMax : this.zMax,
- points,
- buildParent:this,
- ifDraw: this.ifDraw || Potree.settings.drawEntityData
- };
- //hole的zMin zMax跟随buildParent
- var hole = new BuildingBox(prop);
- this.holes.push(hole);
-
- if(this.buildType == 'building'){//为每一层添加对应的hole
- this.buildChildren.forEach(floor=>{
- let floorHole = new BuildingBox({
- buildType : 'hole',
- zMin : this.zMin,
- zMax : this.zMax,
- buildParent:floor,
- originHole : hole, //整栋大楼在当层的hole
- ifDraw: this.ifDraw || Potree.settings.drawEntityData
- });
- floor.parentHoles.push(floorHole);
- floor.add(floorHole);
- floorHole.points = hole.points;//完全等于建筑的点
- });
-
- }
-
- this.add(hole);//直接加在这,不加meshGroup了
- this.update(); //update box mesh
- return hole
-
- //hole不创建box,只有它的buildParent需要更新box。 但有线条和marker. hole不在buildChildren里,但有buildParent
-
- }
-
-
- removeHole(hole){// 这个hole不会是parentHoles里的。
- hole.dispose();
-
- if(this.buildType == 'building'){ //若是整栋大楼的hole,在每层去除它的对应hole
- this.buildChildren.forEach(floor=>{
- let holeAtFloor = floor.parentHoles.find(e=>e.originHole == this );
- let index = floor.parentHoles.indexOf(holeAtFloor);
- index > -1 && floor.parentHoles.splice(index, 1);
- holeAtFloor.dispose();
- });
- }
-
- let index = this.holes.indexOf(hole);
- if(index>-1){
- this.holes.splice(index, 1);
- }
- this.remove(hole);
- this.update();
- }
-
-
-
-
- createBox(){
- var geometry = new Geometry();
-
-
-
- this.mats.boxDefault = getFaceMat(this.buildType);
- this.mats.boxSelected = getFaceMat(this.buildType+'Select');
-
-
- var mesh = new Mesh(geometry, this.mats.boxDefault);
- mesh.name = 'buildingBox';
- if(this.buildType == 'floor'){
- viewer.setObjectLayers(mesh, 'siteModelMapUnvisi' ); //楼层默认在地图不显示,为了不会叠加透明度
- }else {
- viewer.setObjectLayers(mesh, 'bothMapAndScene' );
- }
-
-
-
- //mesh.frustumCulled = false;
- return mesh
- }
-
-
-
- addMarker(o={} ){
- if(this.buildType=='floor')return; //楼层不需要marker
-
- let marker = new Sprite$1({mat:this.getMarkerMaterial('default'), renderOrder : 3, sizeInfo: markerSizeInfo$2, dontFixOrient: true, name:"building_marker"} );
-
-
- viewer.setObjectLayers(marker, 'siteModeOnlyMapVisi' );
-
- o.marker = marker;
- super.addMarker(o);
-
- if(!this.selected)viewer.updateVisible(marker,'select',false);
-
- let addClickEvent = (e)=>{
- let click = (e) => {
- this.dispatchEvent({type:'clickMarker', marker } ); //由entity发送给sitemodel统一处理
- };
- marker.addEventListener('click', click);
- marker.addEventListener('clickSelect', (e)=>{
- this.setMarkerSelected(marker, e.state ? 'select' : 'unselect' );
- });
- marker.removeEventListener('addHoverEvent',addClickEvent);
- };
- marker.addEventListener('addHoverEvent',addClickEvent);//当非isNew时才添加事件
- if(!this.isNew){
- marker.dispatchEvent('addHoverEvent');
- }
-
- return marker
- }
-
- removeMarker(index){
- super.removeMarker(index);
- if(!this.isNew){
- //重新添加midMarkers
- this.midMarkers.forEach(e=>this.remove(e));
- this.midMarkers = [];
- this.addMidMarkers();
- }
-
- this.update();
- if(this.points.length == 2 && this.box){//清除原先length>=3时候的
- this.box.geometry = new Geometry();
- }
-
-
- }
-
- addMidMarker(index, point){
- if(this.buildType=='floor')return; //楼层不需要marker
- let marker = new Sprite$1({mat:this.getMarkerMaterial('midPrepare'), sizeInfo: markerSizeInfo$2, dontFixOrient: true, name:"building_midMarker"} );
- this.midMarkers = [...this.midMarkers.slice(0,index), marker, ...this.midMarkers.slice(index,this.midMarkers.length)];
-
- marker.renderOrder = 3;
-
- viewer.setObjectLayers(marker, 'siteModeOnlyMapVisi' );
- { // Event Listeners
- let mouseover = (e) => {
- this.setMarkerSelected(e.object, 'hover', 'single');
- viewer.dispatchEvent({
- type : "CursorChange", action : "add", name:"markerMove"
- });
- };
- let mouseleave = (e) => {
- this.setMarkerSelected(e.object, 'unhover', 'single');
- viewer.dispatchEvent({
- type : "CursorChange", action : "remove", name:"markerMove"
- });
- };
- let drag = (e) => {
- let index = this.midMarkers.indexOf(marker);
- let newMarker = this.addMarker({index:(index+1), point:marker.position.clone() });
- this.addMidMarker(index+1, new Vector3 );
- this.updateTwoMidMarker(index+1);
- this.setMarkerSelected(marker, 'unhover');
- viewer.inputHandler.startDragging(newMarker , {/* dragViewport:viewer.mapViewer.viewports[0], */ } ); //notPressMouse代表不是通过按下鼠标来拖拽. dragViewport指定了只能在地图上拖拽
- };
- marker.addEventListener('drag', drag );
- //marker.addEventListener('drop', drop);
- marker.addEventListener('mouseover', mouseover);
- marker.addEventListener('mouseleave', mouseleave);
- }
- this.add(marker);
- this.updateMarker(marker, point);
- if(!this.selected)viewer.updateVisible(marker,'select',false);
- return marker
- }
-
-
-
-
-
- addMidMarkers(){//第一次画好所有marker后,一次性为线段增加中点marker
- let length = this.points.length;
- this.points.forEach((point,index)=>{
- let nextPoint = this.points[(index+1)%length];
- let midPoint = new Vector3().addVectors(point, nextPoint).multiplyScalar(0.5);
- this.addMidMarker(index, midPoint );
- });
- }
-
- updateTwoMidMarker(index){//更新第index个marker两边的midMarker
- if(!this.midMarkers.length)return
- let length = this.points.length;
- let last = this.points[(index-1+length)%length]; //它之前的marker位置
- let next = this.points[(index+1)%length];//它之后的marker位置
- let current = this.points[index];//当前位置
- let lastMid = new Vector3().addVectors(last, current).multiplyScalar(0.5);//上一个中点
- let nextMid = new Vector3().addVectors(next, current).multiplyScalar(0.5);//下一个中点
- let lastMidMarker = this.midMarkers[(index-1+length)%length];
- let nextMidMarker = this.midMarkers[index];
- this.updateMarker(lastMidMarker, lastMid);
- this.updateMarker(nextMidMarker, nextMid);
- }
-
-
-
-
-
- dispose(){//销毁geo、remove from parent
- super.dispose();
- this.box && this.box.geometry.dispose();
- this.lineMesh && this.lineMesh.geometry.dispose();
- this.holes.forEach(e=>e.dispose());
- this.parentHoles.forEach(e=>e.dispose());
- //this.buildChildren.forEach(e=>e.dispose())
- this.dispatchEvent('dispose');
- }
-
-
-
- updateBox(){
- if(!this.box)return
- this.box.geometry.dispose();
- var shrink = this.buildType == 'room' ? 0.11 : this.buildType == 'floor' ? 0.082 : 0.2 ;//防止mesh重叠冲突(给一个不寻常的数字) 但离远了还是会有点闪烁
- if(this.points.length >= 3){
- let holes = this.holes.concat(this.parentHoles);
- let holesPoints = holes.filter(e=>e.points.length>2).map(e=>e.points);
- this.box.geometry = MeshDraw.getExtrudeGeo(this.points, holesPoints, {
- depth:this.zMax-this.zMin-shrink,
- UVGenerator: new MetricUVGenerator()
- });
- if(this.buildType == 'building' ){
- this.box.position.z = this.zMin - shrink / 2;
- }else {
- this.box.position.z = this.zMin + shrink / 2;
- }
-
- }
- }
-
-
-
- update(options={}){
- super.update(this.buildType != 'floor' && options.ifUpdateMarkers);
- let length = this.points.length;
-
-
-
- {//确保一下一样
- if(this.originHole){
- this.points = this.originHole.points; //完全等于building的hole
- }
- if(this.buildType == 'hole'){
- this.zMin = this.buildParent.zMin;
- this.zMax = this.buildParent.zMax;
- }
- }
-
-
-
- if(!options.dontUpdateBox){
- let boxOwner;
- if(this.buildType == 'hole'){
- if(this.buildParent.buildType == 'building'){ //若是整栋大楼的hole,在每层都要更新下它的对应hole
- this.buildParent.buildChildren.forEach(floor=>{
- let holeAtFloor = floor.parentHoles.find(e=>e.originHole == this );
- holeAtFloor && holeAtFloor.update(); //刚开始创建时还没创建对应的 holeAtFloor会为null
- });
- }
- boxOwner = this.buildParent;
- }else {
- boxOwner = this;
- }
- boxOwner.updateBox();
- }
-
-
-
- {//update lines
-
- let positions = [];
-
- this.points.forEach((point, index)=>{
-
- //竖线:
- positions.push(point.clone().setZ(this.zMin), point.clone().setZ(this.zMax));
-
- //横线
- let nextPoint = this.points[(index+1)%length];
- if(!nextPoint)return;//when length==1
-
- positions.push(point.clone().setZ(this.zMax), nextPoint.clone().setZ(this.zMax));//上横线
- positions.push(point.clone().setZ(this.zMin), nextPoint.clone().setZ(this.zMin));//下横线
- });
-
- LineDraw.moveLine(this.lineMesh,positions);
-
- }
-
- if(!options.dontUpdateChildren){
- if(this.buildType == 'building' ){
- this.buildChildren.forEach(floor=>{
- floor.points = this.points;
- floor.update();
- });
-
- }
-
- {
- let holes = this.holes.concat(this.parentHoles);
- holes.forEach(hole=> {
-
- hole.update({dontUpdateBox:true});//父级更新了box,hole就不需要更新box了
- });
-
- }
-
- }
- }
-
-
-
-
-
-
- getHolesArea(){
- let holes = this.holes.concat(this.parentHoles);
- let outHoles, holesArea = 0;
- if(holes.length>=2){//合并holes。如果能在绘制时直接合并holes就好啦,这步就转移到那去,但是要删除hole好麻烦
-
- let holes_ = holes.map(e=>e.points);
-
- outHoles = math.getPolygonsMixedRings(holes_, true );
- outHoles.forEach(e=>{
- holesArea+=e.area;
- });
- outHoles = outHoles.map(e=>e.points);
-
-
- }else {
- outHoles = holes.map(e=>e.points);
- outHoles.forEach(e=> holesArea += Math.abs(math.getArea(e)));
- }
- return holesArea
-
- }
-
- getArea(ifRidOfHoles){//面积
- //不排除hole
- return Math.abs(math.getArea(this.points)) - (ifRidOfHoles ? this.getHolesArea() : 0)
- }
-
- getVolume(ifRidOfHoles){//体积
- let {zMin , zMax} = this.getRealZ();
- let height = zMax - zMin;
- if(isNaN(height))height = 0;
- return this.getArea(ifRidOfHoles) * height
-
- }
-
- getRealZ(){//求真实高度时用到的
- let zMin , zMax;
- if (this.buildType == 'building') {
- //building的zMax和zMin一样的所以要算
- let top = this.buildChildren[this.buildChildren.length - 1];
- let btm = this.buildChildren[0];
- zMin = btm ? btm.zMin : 0; //建好的建筑不加楼的话是0
- zMax = top ? top.zMax : 0;
- }else if(this.buildType == 'hole'){
- return this.buildParent.getRealZ()
- }else {
- zMin = this.zMin, zMax = this.zMax;
- }
- return {zMin,zMax}
- }
-
-
- /* getDrawZ(){ //画线和box时用到的z
- let zMin , zMax
- if(this.buildType == 'hole'){
- if(this.buildParent.buildType == 'building' && atFloor){
- zMin = atFloor.zMin, zMax = atFloor.zMax
- }else{
- zMin = this.buildParent.zMin, zMax = this.buildParent.zMax
- }
- }else{
- zMin = this.zMin, zMax = this.zMax
- }
-
- return {zMin, zMax}
-
- } */
-
-
-
- getBound(){
- let bound = new Box3;
-
- let {zMin , zMax} = this.getRealZ();
-
- let points = this.buildType == 'floor' ? this.buildParent.points : this.points;
- points.forEach(p=>{
- bound.expandByPoint(p.clone().setZ(zMin));
- bound.expandByPoint(p.clone().setZ(zMax));
- });
- return bound
- }
-
-
- getMarkerMaterial(type) {
- if(!markerMats$2){
- markerMats$2 = {
- default: new MeshBasicMaterial({
- transparent: !0,
- color: color$2,
- opacity: 0.8,
- map: texLoader$6.load(Potree.resourcePath+'/textures/whiteCircle.png' ),
- depthTest:false,
-
- }),
- midPrepare: new MeshBasicMaterial({ //线中心的半透明点
- transparent: !0,
- color: color$2,
- opacity: 0.4,
- map: texLoader$6.load(Potree.resourcePath+'/textures/whiteCircle.png' ),
- depthTest:false,
- }),
- hover: new MeshBasicMaterial({
- transparent: !0,
- color: color$2,
- opacity: 1,
- map: texLoader$6.load(Potree.resourcePath+'/textures/whiteCircle.png' ),
- depthTest:false,
-
- }),
- select: new MeshBasicMaterial({
- transparent: !0,
- color:new Color('#00C8AF'),
- opacity: 1,
- map: texLoader$6.load(Potree.resourcePath+'/textures/whiteCircle.png' ),
- depthTest:false,
-
- }),
- };
-
- }
- return markerMats$2[type]
-
- }
-
-
- setMarkerSelected(marker, state, hoverObject){
- //console.warn(marker.id , state, hoverObject)
-
-
- if(state == 'select'){
- marker.selected = true;
- marker.material = this.getMarkerMaterial('select');
- }else if(state == 'unselect'){
- marker.selected = false;
- marker.material = this.getMarkerMaterial('default');
- }else {
- if(marker.selected)return //选中时不允许修改为除了'unselect'以外的状态
-
- if(state == 'hover'){
- marker.material = this.getMarkerMaterial('hover');
- }else if(state == 'unhover'){
- if(marker.name.includes('mid')){
- marker.material = this.getMarkerMaterial('midPrepare');
- }else {
- marker.material = this.getMarkerMaterial('default');
- }
- }
- }
- }
-
-
- select(){
- //最多再显示一层子级的线,如building不会显示room中的hole的线
- //box是一直显示的,但会切换材质
-
- /*
- 选中 box 线
-
- building 自己(底盘)选中 自己, floor不带hole
-
- floor 自己选中 自己, room不带hole
-
- room 自己选中 自己
-
- */ //注:自己的就代表定包括hole,如果有parentHoles的也(building上的hole的对应)
-
-
- //console.log('select '+this.name, this.selected)
-
- if(this.selected)return
-
- if(this.box){
- this.box.material = this.mats.boxSelected;
- }
-
- if(this.buildType == 'building'|| this.buildType == 'floor'){
- this.buildChildren.forEach(e=>{
- e.lineMesh.visible = true;
- });
-
- if(this.buildType == 'floor'){
- viewer.setObjectLayers(this.box, 'bothMapAndScene' );
- viewer.setObjectLayers(this.buildParent.box, 'siteModelMapUnvisi' ); //当选中floor或room时,building在地图不可见
- }
- }else if(this.buildType == 'room'){
- viewer.setObjectLayers(this.buildParent.box, 'bothMapAndScene' );
- viewer.setObjectLayers(this.buildParent.buildParent.box, 'siteModelMapUnvisi' );
- }
-
-
-
-
-
- this.lineMesh.visible = true;
- this.markers && this.markers.forEach(e=>viewer.updateVisible(e,'select',true) );
- this.midMarkers && this.midMarkers.forEach(e=>e.visible = true);
-
- let holes = this.holes.concat(this.parentHoles);
- holes.forEach(e=>e.select());
-
- this.selected = true;
- this.dispatchEvent({type:'select'});
- }
-
-
- unselect(){
- if(!this.selected)return
- //console.log('unselect '+this.name )
- if(this.box){
- this.box.material = this.mats.boxDefault;
- }
-
- if(this.buildType == 'building' || this.buildType == 'floor'){
- this.buildChildren.forEach(e=>{ //(这里要保证选中前要先取消选中,否则如选中房间后取消了楼层,房间线就隐藏了)
- e.lineMesh.visible = false;
- });
-
- if(this.buildType == 'floor'){
- viewer.setObjectLayers(this.box, 'siteModelMapUnvisi' );
- viewer.setObjectLayers(this.buildParent.box, 'bothMapAndScene' );
- }
-
- }else if(this.buildType == 'room'){
- viewer.setObjectLayers(this.buildParent.box, 'siteModelMapUnvisi' );
- viewer.setObjectLayers(this.buildParent.buildParent.box, 'bothMapAndScene' );
- }
-
- this.lineMesh.visible = false;
- this.markers && this.markers.forEach(e=>viewer.updateVisible(e,'select',false) );
- this.midMarkers && this.midMarkers.forEach(e=>e.visible = false);
-
- let holes = this.holes.concat(this.parentHoles);
- holes.forEach(e=>e.unselect());
-
- this.selected = false;
- this.dispatchEvent({type:'unselect'});
- }
-
-
-
-
- ifContainsPoint(position){//看它所定义的空间是否包含某个坐标(要排除hole)
-
- let {zMin , zMax} = this.getRealZ();
- if(position.z < zMin || position.z > zMax ) return
-
- let holes = this.holes.concat(this.parentHoles);
- let holesPoints = holes.filter(e=>e!=this && e.points.length>2).map(e=>e.points);
- let inShape = math.isPointInArea(this.points, holesPoints, position);
-
-
- return !!inShape
- }
-
- }
- class MetricUVGenerator{
- constructor(){
- this.a = new Vector3,
- this.b = new Vector3,
- this.c = new Vector3,
- this.d = new Vector3;
- }
- generateTopUV(t, e, n, r, o) {
- return [new Vector2$1(e[3 * n],e[3 * n + 1]), new Vector2$1(e[3 * r],e[3 * r + 1]), new Vector2$1(e[3 * o],e[3 * o + 1])]
- }
-
- generateSideWallUV(t, e, n, r, o, a) {
- var s = e;
- this.a.set(s[3 * n], s[3 * n + 1], s[3 * n + 2]),
- this.b.set(s[3 * r], s[3 * r + 1], s[3 * r + 2]),
- this.c.set(s[3 * o], s[3 * o + 1], s[3 * o + 2]),
- this.d.set(s[3 * a], s[3 * a + 1], s[3 * a + 2]);
- var c = this.a.x !== this.b.x
- , l = c ? this.b : this.d
- , u = this.a.distanceTo(l)
- , d = l.distanceTo(this.c);
- return [new Vector2$1(this.a.x,0), c ? new Vector2$1(this.a.x + u,0) : new Vector2$1(this.a.x,d), new Vector2$1(this.a.x + u,d), c ? new Vector2$1(this.a.x,d) : new Vector2$1(this.a.x + u,0)]
- }
- }
- const minFloorHeight = 0.5;
- const ifDrawDatasetBound = true; //显示一下数据集的tightBound线框
- const minMarkers = 3;
- const Limit = {zMin:-config$1.map.cameraHeight, zMax:config$1.map.cameraHeight,}; //不能超过camera的高度,为了对称所以也限制了最低
- var SiteModel = {
- bus: new EventDispatcher(),
- entities:[], //所有实体
- buildings:[], //所有建筑父集
- meshGroup: new Object3D,
- inEntity : null,
- lastPos: new Vector3(Infinity,Infinity,Infinity),
-
-
- init: function(){
-
-
- viewer.scene.scene.add(this.meshGroup);
- this.meshGroup.name = 'siteModel';
- this.SplitScreen = SplitScreen4Views;
-
- if(Potree.settings.editType == 'pano'){
- return
- }
-
-
-
-
- this.createHeightPull();
-
- if(Potree.settings.isTest && ifDrawDatasetBound){
- viewer.addEventListener('allLoaded',()=>{
-
- viewer.scene.pointclouds.forEach(pointcloud=>{
- let boxPoints = pointcloud.getUnrotBoundPoint();
-
- let boundingBox = new BuildingBox({
- name: '数据集tightBound_'+pointcloud.dataset_id,
- points: boxPoints,
- buildType : 'dataset',
- zMax: pointcloud.bound.max.z,
- zMin: pointcloud.bound.min.z,
- ifDraw:true
- });
-
- this.meshGroup.add(boundingBox);
- //boundingBox.markers.forEach(e=>e.visible = false)
- });
- });
- }
- if(Potree.settings.isOfficial){
-
- viewer.addEventListener('camera_changed', e => {
- if(e.changeInfo.positionChanged){
- this.updateEntityAt();
- }
- });
- }
-
-
- {
- let pressDelete = (e)=>{
- if(e.keyCode == KeyCodes.BACKSPACE || e.keyCode == KeyCodes.DELETE){
- if(this.selectedMarker){
- let entity = this.selectedMarker.parent;
- let index = entity.markers.indexOf(this.selectedMarker);
- entity.removeMarker(index);
-
- if(entity.points.length<2){//删到只剩一个点时重新画(如果是hole的点,直接删除hole吧?)
- this.startInsertion('resume',entity);
- }
- }
- }
- };
- viewer.inputHandler.addEventListener('keydown', pressDelete);
-
-
- }
-
-
- },
-
-
-
- updateEntityAt(force){
- //if(!this.entities.length || this.editing) return //编辑时也要根据位置显示不同楼层的漫游点与cad
-
- let fun = ()=>{ //延时update,防止卡顿
- let currPos = viewer.mainViewport.view.position;
-
- //if(force || !currPos.equals(this.lastPos) ){
- //console.log('currPos ', currPos.toArray())
- this.lastPos.copy(currPos);
- let entity;
-
- let searchPos = Potree.settings.displayMode == 'showPanos' ? viewer.images360.currentPano : currPos;
- entity = this.pointInWhichEntity(searchPos, 'room');
-
- if(force || this.inEntity != entity ){
- let oldEntity = this.inEntity;
- this.inEntity = entity;
- //console.log('buildingChange', entity)
- this.bus.dispatchEvent({type:'buildingChange',entity});
- //this.updatePanosVisible(oldEntity, this.inEntity)
-
- let lastFloor = this.currentFloor; //oldEntity ? oldEntity.buildType == 'floor' ? oldEntity : oldEntity.buildType == 'room' ? oldEntity.buildParent : null : null; //基本只会是floor或room
- let currentFloor = entity ? entity.buildType == 'floor' ? entity : entity.buildType == 'room' ? entity.buildParent : null : null; //基本只会是floor或room
- if(force || currentFloor != lastFloor){
- //console.log('改变了floor',lastFloor,currentFloor)
- this.currentFloor = currentFloor;
- this.bus.dispatchEvent({type:'FloorChange',currentFloor});
- }
-
- }
- force = false;
- return true
- //}
- };
-
- if(force)fun();
- else Common.intervalTool.isWaiting('sitemodelCameraInterval', fun , 500);
-
-
-
- },
-
- enter:function(){
-
- Potree.Log('sitemodel enter');
- this.clear(); //确保全部清空
- this.editing = true;
- //this.updatePanosVisible(null, null, true)//show all
- viewer.updateFpVisiDatasets();
-
-
- let mapViewport = viewer.mapViewer.viewports[0];
- this.SplitScreen.split({siteModel:true/* , viewports:[{name:'Top',viewport : mapViewport }] */});
-
-
- viewer.viewports.forEach(e=>{
- if(e.name != 'mapViewport'){
- e.layersAdd('siteModelMapUnvisi');
- }
- if(e.name == 'right' || e.name == 'back'){
- e.layersAdd('siteModeSideVisi');
- }
- });
-
-
- viewer.images360.panos.forEach(pano=>{
- viewer.setObjectLayers(pano.marker, 'siteModelMapUnvisi' );
- });
- mapViewport.layersAdd('siteModeOnlyMapVisi'); //只有mapViewport能看到marker
-
-
-
- },
-
-
-
- leave:function(){
-
- Potree.Log('sitemodel leave');
-
- let mapViewport = viewer.mapViewer.viewports[0];
- this.SplitScreen.recover();
- viewer.viewports.forEach(e=>{
- if(e.name != 'mapViewport'){
- e.layersRemove('siteModelMapUnvisi');
- }
- if(e.name == 'right' || e.name == 'back'){
- e.layersRemove('siteModeSideVisi');
- }
- });
- viewer.images360.panos.forEach(pano=>{
- viewer.setObjectLayers(pano.marker, 'sceneObjects' );
- });
-
- mapViewport.layersRemove('siteModeOnlyMapVisi');
- this.clear();
- this.editing = false;
-
- this.updateEntityAt(true);
- viewer.updateFpVisiDatasets();
- } ,
-
-
-
-
-
- addFloor:function(parent, dirType, sid, name){//dirType:'top'|'bottom'在上方建还是下方。如果建筑中没有楼层,默认在基底建一个
- let buildType = 'floor';
- let zMin, zMax;
- if(parent.buildChildren.length == 0){
- zMin = parent.zMin;
- zMax = zMin + Potree.config.siteModel.floorHeightDefault;
- }else {
- if(dirType == 'bottom'){
- //var btm = Common.find(parent.buildChildren,null,[e=>e.zMin])
- var btm = parent.buildChildren[0];
- zMax = btm.zMin;
- zMin = zMax - Potree.config.siteModel.floorHeightDefault;
- }else {
- //var top = Common.find(parent.buildChildren,null,[e=>e.zMax])
- var top = parent.buildChildren[parent.buildChildren.length - 1];
- zMin = top.zMax;
- zMax = zMin + Potree.config.siteModel.floorHeightDefault;
- }
-
- }
-
-
- let prop = {
- buildType,
- //name : Potree.config.siteModel.names[buildType],
- zMin,
- zMax,
- buildParent:parent,
- sid, name,
- ifDraw:true
- };
- var floor = new BuildingBox(prop);
- /* parent.buildChildren.push(floor)
- this.meshGroup.add(floor);
- this.entities.push(floor) */
- floor.update();
- this.addEntity(floor,parent);
- //this.selectEntity(floor)
- if(this.selected == parent){//重新选择下,为了显示新楼层线框
- parent.unselect();
- parent.select();
- }
-
- return floor
- },
-
-
-
- startInsertion:function(buildType, parent, sid, name, callback, cancelFun){
-
- let zMin, zMax, entity, resume;
- let mapViewport = viewer.mapViewer.viewports[0];
-
- if(buildType == 'resume'){//继续画(使用最后一个点或者新加的点)
- resume = true;
- entity = parent;
- buildType = parent.buildType;
-
- //删除原先所有的点,因为它们已经添加了事件,会很麻烦:
- entity.reDraw(0);
- entity.isNew = true; //当作新的来画
- }
-
- if(!resume){
- if(buildType == 'hole' || buildType == 'room'){
- zMin = parent.zMin;
- zMax = parent.zMax;
- }else if(buildType == 'building'){
- parent = null;
- zMin = viewer.bound.boundingBox.min.z;
- zMax = viewer.bound.boundingBox.min.z;
- }
-
-
-
-
- if(buildType == 'hole'){
- entity = parent.addHole();
- entity.isNew = true;
- this.selectEntity(parent);
- entity.select();
- console.log('挖洞 ',entity.uuid);
- }else {
-
- let prop = {
- buildType,
- //name : Potree.config.siteModel.names[buildType],//'building',
- zMin,
- zMax,
- buildParent:parent,
- sid, name,
- ifDraw:true
- };
-
- entity = new BuildingBox(prop);
- entity.isNew = true;
- this.selectEntity(entity);
- }
-
-
-
- this.addEntity(entity, parent);
-
-
-
-
- }
-
-
-
-
- let timer;
-
-
- let endDragFun = (e) => {
- if (e.button == MOUSE.LEFT ) {
- var marker = entity.addMarker({point:entity.points[entity.points.length - 1].clone()});
-
- //entity.editStateChange(true) //重新激活reticule状态
- entity.continueDrag(marker, e);
- } else if (e.button === MOUSE.RIGHT ) {
- if(e.pressDistance < Potree.config.clickMaxDragDis )end(e);//非拖拽的话
- else entity.continueDrag(null, e/* .drag.object */);
-
- }
- };
- let finish = ()=>{//结束绘画
- viewer.removeEventListener('cancel_insertions', Exit);
- entity.removeEventListener('unselect', Exit);
- clearTimeout(timer);
- entity.editStateChange(false);
- //pressExit && viewer.inputHandler.removeEventListener('keydown', pressExit);
- callback && callback(entity);
- };
- let end = (e={}) => {//尝试结束
- /* 退出的三种形式:
- 1 普通:如果大于三个marker,结束且保留;否则重新画。()
- 2 删除:直接结束且删除。(remove)
- 3 结束:如果大于三个marker,结束且保留;否则结束且删除。 (finish)
- 4 保留:无论几个marker,都保留着,结束。(remain)
- */
-
- if(e.remove){
- finish();
- return this.removeEntity(entity)
- }
-
-
-
- if(!e.remain && !e.finish && !e.remove && entity.markers.length<=minMarkers){//右键 当个数不够时取消
- //重新开始画
- entity.reDraw(1);
-
- viewer.updateVisible(entity.markers[0],'unMove',false);
- var f = ()=>{
- viewer.updateVisible(entity.markers[0],'unMove',true);
- entity.removeEventListener('dragChange',f);
- };
- entity.addEventListener('dragChange',f);
-
- //console.log('waitcontinue')
- entity.continueDrag(entity.markers[0], e);
- return
- }
-
- finish();
-
- if (e.remain || !e.remove && entity.markers.length > 3) {//保留
- entity.removeMarker(entity.points.length - 1);
- entity.markers.forEach(marker=>{marker.dispatchEvent('addHoverEvent'); });
- if(buildType == 'room'){
- this.fitPullBox();
- }
- entity.isNew = false;
- entity.addMidMarkers();
-
- }else {
- this.removeEntity(entity); //直接删除没画好的,比较简单。这样就不用担心旧的continueDrag仍旧触发了
-
- }
-
- return entity
- };
-
- let Exit = (e)=>{ //强制结束
-
- entity.removeEventListener('unselect', Exit);
-
- if(viewer.inputHandler.drag){//还未触发drop的话
- viewer.inputHandler.drag.object.dispatchEvent({
- type: 'drop',
- drag: viewer.inputHandler.drag,
- viewer: viewer,
- pressDistance:0,
- button : MOUSE.RIGHT
- });
- viewer.inputHandler.drag = null;
- }else {
- end({remain:true});
- }
- viewer.inputHandler.drag = null;
- };
-
-
- viewer.dispatchEvent( 'cancel_insertions' );//取消之前的
- viewer.addEventListener('cancel_insertions', Exit);
- entity.addEventListener('unselect', Exit);
-
-
-
- var marker = entity.addMarker({point:new Vector3(0, 0, 0)});
- viewer.updateVisible(marker,'unMove',false);//这时候的位置是假的(0,0,0)所以先不可见
- var f = ()=>{
- viewer.updateVisible(marker,'unMove',true);
- entity.removeEventListener('dragChange',f);
- };
- entity.addEventListener('dragChange',f);
-
-
- marker.isDragging = true;
- viewer.inputHandler.startDragging(marker , {dragViewport:mapViewport, endDragFun, notPressMouse:true} ); //notPressMouse代表不是通过按下鼠标来拖拽. dragViewport指定了只能在地图上拖拽
-
-
- return entity;
-
-
-
-
- },
-
-
-
- getPreDealData(points, zMin, zMax, initial, buildType, parent){
- /* if( buildType == 'building' ){
- zMax = zMin //强制变得一样,作为基底。如果有必要,保存时再算真实的zMax。目前zMin没有保存所以数据是错的,会直接根据floor计算
- } */
-
- var bound = viewer.bound.boundingBox;
-
- if(buildType == 'building' && initial){//初始数据错的,要自己建(只有一个building和floor) 原posIsLonlat
- console.log('空间模型未编辑过, 初始化了一个');
-
- points = [
- new Vector3(bound.min.x, bound.min.y,0),
- new Vector3(bound.max.x, bound.min.y,0),
- new Vector3(bound.max.x, bound.max.y,0),
- new Vector3(bound.min.x, bound.max.y,0),
- ];
- zMin = bound.min.z;
- zMax = bound.max.z;
- /* points = points.map(e=>{
- return viewer.transform.lonlatToLocal.forward(e)
- }) */
-
- }else {//相对于初始数据集的模型内坐标
- points = points.map(e=> this.transform(e, 'fromDataset'));
- if(buildType == 'floor' && initial){
- zMin = bound.min.z;
- zMax = bound.max.z;
- }
- }
- return {points, zMax, zMin }
- },
-
-
-
- resetFromData:function(entity, points=[], holes=[], zMin, zMax ){
-
-
- var {points, zMax, zMin} = this.getPreDealData(points, zMin, zMax , this.autoBuild , entity.buildType, entity.buildParent );
-
- if(entity.buildType != 'floor' )entity.points = points;
-
-
- if(entity.buildType == 'room'){
- entity.zMin = zMin;
- entity.zMax = zMax;
- }else if(entity.buildType == 'floor'){//改楼高
- let height = zMax - zMin;
- let zMax2 = entity.zMin + height;
- SiteModel.changeZ(entity, 'zMax', zMax2);
- }
-
- {
- //删除旧的holes重新添加
- let holesOld = entity.holes;
- holesOld.forEach(e=>{
- entity.removeHole(e);
- });
-
- holes.forEach(points =>{
- let ps = points.map(e=> this.transform(e, 'fromDataset'));
- let hole = entity.addHole(ps);
- hole.addMidMarkers();
- });
- }
-
-
-
- entity.update();
- return entity
- }
-
-
- ,
-
- createFromData:function( buildType, parent ,sid, name, points=[], holes=[], zMin, zMax, initial,panos,flagPano){
- if(buildType != 'building' && buildType != 'floor' && buildType != 'room' ) return
-
-
- var {points, zMax, zMin} = this.getPreDealData(points, zMin, zMax , initial, buildType, parent );
-
-
-
- {
-
- let getPano = (id)=>{
- return viewer.images360.panos.find(pano=>pano.id == id)
- };
-
- panos = panos ? panos.map(e=>getPano(e)) : [];
- flagPano = flagPano != void 0 ? getPano(flagPano) : null ; //最中心的pano 或者 最靠近该实体的pano(当panos为空时)
-
- if(!this.editing && buildType == 'floor' && !flagPano){//没有的话可能是自动添加的floor,直接用parent的吧
- panos = parent.panos;
- flagPano = parent.flagPano;
- }
- }
-
-
-
-
-
- let prop = {
- buildType,
- points,
- name,
- sid,
- zMin,
- zMax,
- buildParent:parent,
- ifDraw:this.editing || Potree.settings.drawEntityData,
- panos, flagPano,
- autoBuild : initial
- };
-
- let entity = new BuildingBox(prop);
- SiteModel.addEntity(entity, parent );
- if(this.editing){
- if(buildType == 'building'|| buildType == 'room'){
- entity.addMidMarkers();
- }
- }
-
-
- holes.forEach(points =>{
- let ps = points.map(e=> this.transform(e, 'fromDataset'));
- let hole = entity.addHole(ps);
- this.editing && hole.addMidMarkers();
- });
-
-
- /* if(buildType == 'floor'){
- this.updateBuildingZ(parent)
- } */
-
-
- return entity
- },
-
- transform:function(pos, type){
- if(Potree.settings.editType == 'pano'){ // 模型不经转换
- return new Vector3().copy(pos).setZ(0);
- }
-
- if(type == 'toDataset'){
- let point = Potree.Utils.datasetPosTransform({ toDataset: true, position: pos.clone(), datasetId: Potree.settings.originDatasetId });
- return new Vector2$1().copy(point)
-
- }else {
- let position = new Vector3().copy(pos).setZ(0);
- return Potree.Utils.datasetPosTransform({ fromDataset: true, position, datasetId: Potree.settings.originDatasetId })
-
- }
- },
-
-
-
-
-
- addEntity:function(entity, parent){
- this.meshGroup.add(entity);
- this.entities.push(entity);
- if(entity.buildType == 'building'){
- this.buildings.push(entity);
- }else {
- parent.buildChildren.push(entity);
-
- }
-
-
- if(entity.buildType == 'room'){
- entity.addEventListener('marker_dropped',()=>{
- this.fitPullBox();
- });
- }else if(entity.buildType == 'floor'){
- this.updateBuildingZ(parent);
- parent.dispatchEvent({type:'addFloor'});
- }
-
- {//仅能存在一个marker被选中。选中的点可以被删除
- entity.addEventListener('clickMarker', (e)=>{
- if(this.selectedMarker == e.marker){
- this.selectedMarker.dispatchEvent({type:'clickSelect',state:false});
- this.selectedMarker = null;
- }else {
- if(this.selectedMarker){
- this.selectedMarker.dispatchEvent({type:'clickSelect',state:false});
- }
- this.selectedMarker = e.marker;
- this.selectedMarker.dispatchEvent({type:'clickSelect',state:true});
- }
- });
- entity.addEventListener('removeMarker', (e)=>{
- if(this.selectedMarker == e.marker){
- this.selectedMarker = null;
- }
- });
-
- let unselect = (e)=>{//取消选中实体或删除后
- if(this.selectedMarker && entity.markers.includes(this.selectedMarker)){
- this.selectedMarker.dispatchEvent({type:'clickSelect',state:false});
- this.selectedMarker = null;
- }
- };
- entity.addEventListener('dispose', unselect);
- entity.addEventListener('unselect', unselect);
-
-
- }
- //console.log('添加实体:', entity.buildType, entity.sid, entity.uuid)
-
- },
-
- removeEntity : function(entity){
- if(!this.entities.includes(entity))return
-
- console.log('删除实体:', entity.buildType, entity.sid);
-
- if(this.selected == entity){
- this.height_pull_box.visible = false;
- this.selectEntity(null);
- }
-
-
- if(entity.buildType == 'building'){
- var index = this.buildings.indexOf(entity);
- if(index>-1){
- this.buildings.splice(index,1);
- }
- }else {
- var index = entity.buildParent.buildChildren.indexOf(entity);
- if(index>-1){
- entity.buildParent.buildChildren.splice(index,1);
- }
- }
-
-
-
- var index = this.entities.indexOf(entity);
- if(index>-1){
- this.entities.splice(index,1);
- }
-
-
- entity.dispose();
- let buildChildren = entity.buildChildren.slice();
- buildChildren.forEach(e=>this.removeEntity(e));
-
-
- },
-
-
- updateBuildingZ:function(building){
-
- building.buildChildren = building.buildChildren.sort((e,a)=>e.zMin-a.zMin);//从低到高排序
- building.zMin = building.zMax = building.buildChildren[0].zMin; //基底高度
- //building.zMax = building.buildChildren[building.buildChildren.length-1].zMax
- if(this.editing) building.update({dontUpdateChildren:true});
- building.dispatchEvent('updateBuildingZ');
- },
-
-
-
- selectEntity : function(entity, state=true){
- if(state === false){
- entity.unselect();
- if(this.selected == entity)this.selected = null;
-
- return
- }
-
- if(this.selected == entity || entity && entity.buildType == 'hole')return
- //this.buildings.forEach(e=>e.unselect())
- this.selected && this.selected.unselect();
- this.height_pull_box.visible = false;
-
-
- if(entity){
- entity.select();
-
- }
-
-
- this.selected = entity;
-
-
- if(entity && (entity.buildType == 'floor' || entity.buildType == 'room' )){
- this.height_pull_box.visible = true;
- this.fitPullBox();
- }
-
- if(entity && !entity.isNew && (entity.buildType == 'building' || entity.buildType == 'room' ) && entity.points.length<2){
- this.startInsertion('resume',entity); //继续画
- }
- },
-
-
-
-
-
-
-
- fitPullBox: function(){ //自适应拖拽楼层的pullMesh
- if(!this.selected || this.selected.buildType!= 'floor' && this.selected.buildType!= 'room')return
- let bound = new Box3();
- bound.expandByObject(this.selected.box);
- let center = bound.getCenter(new Vector3() );
- let size = bound.getSize(new Vector3() );
- this.height_pull_box.scale.copy(size);
- this.height_pull_box.position.copy(center);
- },
-
-
-
-
-
- changeZ:function(entity, dirType, value){ // floor or room 修改zMin or zMax
- let max, min; //limit
-
- if(entity.buildType == 'floor'){//楼层
- let index = entity.buildParent.buildChildren.indexOf(entity);
- if(dirType == 'zMax'){
- let upper = entity.buildParent.buildChildren[index+1];
- entity.zMax = Math.min(Limit.zMax, value);
- min = entity.zMin + minFloorHeight;
- if(entity.zMax < min){
- entity.zMax = min;
- }else {
- if(upper){
- max = upper.zMax - minFloorHeight;
- if(entity.zMax > max){
- entity.zMax = max;
- }
- }
- }
- if(upper){
- upper.zMin = entity.zMax;
- upper.update();
- upper.dispatchEvent({type:'changeHeight'});
- }
- }else {
- let lower = entity.buildParent.buildChildren[index-1];
-
- entity.zMin = Math.max(Limit.zMin, value);
- max = entity.zMax - minFloorHeight;
- if(entity.zMin > max){
- entity.zMin = max;
- }else {
- if(lower){
- min = lower.zMin + minFloorHeight;
- if(entity.zMin < min){
- entity.zMin = min;
- }
- }
- }
- if(lower){
- lower.zMax = entity.zMin;
- lower.update();
- lower.dispatchEvent({type:'changeHeight'});
- }
- if(index == 0)this.updateBuildingZ(entity.buildParent);
- }
- }else if(entity.buildType == 'room'){//房间
- //按照navvis的是不一定限制在当前楼层,只要高度不超过当前楼层即可。
- let maxHeight = entity.buildParent.zMax - entity.buildParent.zMin;
-
-
- if(dirType == 'zMax'){
- min = entity.zMin + minFloorHeight;
- max = entity.zMin + maxHeight;
- entity.zMax = MathUtils.clamp(value, min, max);
- }else {
- min = entity.zMax - maxHeight;
- max = entity.zMax - minFloorHeight;
- entity.zMin = MathUtils.clamp(value, min, max);
- }
- }
- entity.update();
- entity.dispatchEvent({type:'changeHeight'});
- //this.selected.emit('update')
- this.fitPullBox();
- },
-
-
-
- createHeightPull:function(){ //拖拽楼层的bounding box
- let boxGeo = new BoxBufferGeometry( 1, 1, 1/4 );
- let boxMat = new MeshBasicMaterial({
- color:"#F00",
- opacity:0,
- transparent:true,
- depthTest:false,
- side:2
- });
-
- let height_pull_box_up = new Mesh(boxGeo,boxMat);
- let height_pull_box_down = new Mesh(boxGeo,boxMat);
- height_pull_box_up.name = 'height_pull_box_up';
- height_pull_box_down.name = 'height_pull_box_down';
- this.height_pull_box = new Object3D();
- this.height_pull_box.name = 'height_pull_box';
- this.height_pull_box.add(height_pull_box_up);
- this.height_pull_box.add(height_pull_box_down);
- this.height_pull_box.visible = false;
- this.meshGroup.add(this.height_pull_box);
- height_pull_box_up.position.set(0,0,1/2/* 3/8 */);
- height_pull_box_down.position.set(0,0,-1/2/* -3/8 */);
- viewer.setObjectLayers(this.height_pull_box, 'siteModeSideVisi' );
-
-
-
- let mouseover = (e)=>{
- viewer.dispatchEvent({
- type : "CursorChange", action : "add", name:"siteModelFloorDrag"
- });
- };
- let mouseleave = (e)=>{
- viewer.dispatchEvent({
- type : "CursorChange", action : "remove", name:"siteModelFloorDrag"
- });
- };
-
-
- let firstZ, firstIntersect;
- let drag = (e)=>{
- var intersectPoint = e.intersectPoint.orthoIntersect; //不要点云的intersect,只要orthocamera算出的平面intersect
-
- if(firstIntersect != void 0){
-
- let moveZ = intersectPoint.z - firstIntersect;
- if(this.selected.buildType == 'floor'){//楼层
- //限制高度不能超过上下
- if(e.target == height_pull_box_up){
- if(firstZ == void 0)firstZ = this.selected.zMax;
- this.changeZ(this.selected, 'zMax', firstZ + moveZ);
- }else {
- if(firstZ == void 0)firstZ = this.selected.zMin;
- this.changeZ(this.selected, 'zMin', firstZ + moveZ);
- }
- }else if(this.selected.buildType == 'room'){//房屋
- if(e.target == height_pull_box_up){
- if(firstZ == void 0)firstZ = this.selected.zMax;
- this.changeZ(this.selected, 'zMax', firstZ + moveZ);
- }else {
- if(firstZ == void 0)firstZ = this.selected.zMin;
- this.changeZ(this.selected, 'zMin', firstZ + moveZ);
- }
- }
- }else {
- firstIntersect = intersectPoint.z;
- }
- };
-
-
- let drop = (e)=>{
- firstZ = firstIntersect = null;
- };
-
- height_pull_box_up.addEventListener('mousemove',mouseover);
- height_pull_box_down.addEventListener('mousemove',mouseover);
- height_pull_box_up.addEventListener('mouseleave',mouseleave);
- height_pull_box_down.addEventListener('mouseleave',mouseleave);
- height_pull_box_up.addEventListener('drag',drag);
- height_pull_box_down.addEventListener('drag',drag);
- height_pull_box_up.addEventListener('drop',drop);
- height_pull_box_down.addEventListener('drop',drop);
- },
-
-
- pointInWhichEntity(location, buildType, ifIgnoreHole){//buildType是要找的建筑类型
- //location 可以是pano或者坐标
- //由于房间可能在building外,所以房间要另外单独识别。
-
- let lastResult; //最接近的上一层结果,如果没有result返回这个
- let result;
- let level = {
- building: 0, floor: 1, room: 2
- };
-
- let traverse = (parent, buildType)=>{//返回第一个符合标准的实体
- let contains;
- if(location instanceof Vector3){
- contains = parent.ifContainsPoint(location);
- }else {//is pano
- contains = parent.panos.includes(location);
- }
- if(contains){
- if(!lastResult || level[lastResult.buildType] < level[parent.buildType] )lastResult = parent;
-
-
- if(parent.buildType == buildType){
- return parent
- }else {
- for(let i=0,len=parent.buildChildren.length; i<len; i++){
- let result1 = traverse(parent.buildChildren[i]);
- if(result1) return result1
- }
- }
- }
-
- };
- //因为建筑可能重叠,所以需要先找到最接近其中心的建筑物
- result = Common.sortByScore(this.buildings, [(building)=>{
- return traverse(building, 'building') //在building中
-
- }], [(building)=>{ //写法类似pointInWhichPointcloud
- let boundingBox = building.getBound();
- let center = boundingBox.getCenter(new Vector3());
- let position = location instanceof Vector3 ? location : location.position;
- let dis = position.distanceTo(center);
- let size = boundingBox.getSize(new Vector3());
- let length = size.length() / 2;
- return length / dis
- }]);
-
- let building = result && result[0] && result[0].score > 1 && result[0].item;
- if(buildType == 'building' || !building)return building
- result = traverse(building, buildType);
-
-
- /* if(!result && buildType == 'room'){//如果要找的是room, 且按刚才的顺序找不到的话,就单独从所有rooms中找一遍。因为room可能不在floor和building内。
- let rooms = this.entities.filter(e=>e.buildType == 'room');
- result = rooms.find(e=>e.ifContainsPoint(position))
- }*/
- //虽然房间可以画到上级之外,但是为了方便起见,假定房间绝对在楼层之内。找不到的话要调整空间模型了。
-
-
- return result || lastResult
-
- }
- ,
-
-
- findPanos: function(){
-
- {
- this.entities.forEach(entity=>{
- //清空:
- entity.panos = [];
- entity.flagPano = null;
- viewer.images360.panos.forEach(pano=>{
- if(entity.ifContainsPoint(pano.position)){
- entity.panos.push(pano);
- }
- });
- });
- }
-
-
- /* viewer.images360.panos.forEach(pano=>{ //一个漫游点只对应一个实体的话
- let result = this.pointInWhichEntity(pano.position, 'room');
-
- {//get panos for every entities
- let entity = result
- while(entity){
- entity.panos.push(pano);
- entity = entity.buildParent
- }
- }
- }) */
-
- {//search center pano
- this.entities.forEach(entity=>{
- let panos = entity.panos;
- if(panos.length == 0)return
- let bound = entity.getBound();
- let center = bound.getCenter(new Vector3);
- let request = [];
- let rank = [
- Images360.scoreFunctions.distanceSquared({position: center})
- ];
- //let panos = entity.panos && entity.panos.length ? entity.panos : viewer.images360.panos //entity没有panos的话,就扩大到所有panos
-
- let r = Common.sortByScore(panos, request, rank);
- if(r && r.length){
- entity.flagPano = r[0].item;
- }else {
- console.error('no flagPano??');
- }
-
-
- });
-
- }
-
- }
- ,
-
-
-
- findEntityForDataset:function(){//为每一个数据集寻找它所属的最小实体
- /* var entities = this.entities.filter(e=>e.buildType == 'room' || e.buildType == 'floor' && e.buildChildren.length == 0)
- viewer.scene.pointclouds.forEach(pointcloud=>{
-
- let cloudVolume = pointcloud.getVolume()
- let scores = []
- entities.forEach(entity=>{
- let volume = entity.intersectPointcloudVolume(pointcloud)
- //注:默认已经findPanos过
- let panos = entity.panos.filter(e=>pointcloud.panos.includes(e));
- let panoCount = panos.length
-
- let score = volume / cloudVolume + panoCount / pointcloud.panos.length
-
- scores.push({entity, volume, panoCount, score})
-
- })
- scores.sort((a,b)=>{ return b.score-a.score })
-
- if(scores.length == 0 || scores[0].volume/cloudVolume < 0.0001 && scores[0].volume < 3 ){//如果约等于0
- pointcloud.belongToEntity = null
- }else{
- pointcloud.belongToEntity = scores[0].entity;
- }
- }) */
-
- let getScores = (pointcloud, entities, cloudVolume)=>{
- let scores = [];
- entities.forEach(entity=>{
- let volume = entity.intersectPointcloudVolume(pointcloud);
- //注:默认已经findPanos过
- let panos = entity.panos.filter(e=>pointcloud.panos.includes(e));
- let panoCount = panos.length;
-
- let score = volume / cloudVolume + panoCount / pointcloud.panos.length;
-
- scores.push({entity, volume, panoCount, score});
-
- });
- scores.sort((a,b)=>{ return b.score-a.score });
-
- return scores
- };
-
-
- viewer.scene.pointclouds.forEach(pointcloud=>{ //先判断父级,如果父集不通过就不判断子级。
- let cloudVolume = pointcloud.getVolume();
- let entities = this.buildings;
-
- while(1){
- let scores = getScores(pointcloud, entities, cloudVolume);
- if(scores.length == 0 || scores[0].volume/cloudVolume < 0.0001 && scores[0].volume < 3 ){//如果约等于0
- pointcloud.belongToEntity = null;
- break;
- }else {
- entities = scores[0].entity.buildChildren;
- if(entities.length == 0){
- pointcloud.belongToEntity = scores[0].entity;
- break;
- }
- }
- }
- });
-
-
-
-
-
- /*
- 旧版:
- 只需要考虑 floor 和 room, 因为building的只有一个基底没高度
- floor 和 room 在空间中没有完全的从属关系,因为room可以超出floor之外。所以直接混在一起来查找,但要排除有房间的楼层。
- (现在改为层层递进查找,否则数据集包含entity多的,会直接挂载到体积最大的房间里,即使看起来主体点云并不在该房间)
-
-
- 有的数据集虽然很高,但只有近地面的部分才是主体,这部分一般含有全部漫游点。为了防止上层的实体因体积较大而分数高,就把包含漫游点的个数也加入考虑。
-
- 重叠体积大、且包含漫游点最多的最小实体将会拥有该点云。
-
- 期望: 最好不挂载到最小子级,因为现在有房间都到房间里了。
- */
- }
-
-
-
- ,
- clear:function(){//清空
-
- /* entities:[], //所有实体
- buildings:[], //所有建筑父集
- meshGroup: new THREE.Object3D, */
- this.selectEntity(null);
-
- let length = this.entities.length;
- for(let i=0;i<length;i++){
- this.entities[i].dispose();
- }
-
- this.entities = [];
- this.buildings = [];
- this.inEntity = null;
-
-
- }
- ,
-
-
-
- gotoEntity(id, isNearBy, duration=1000) {
- var entity = this.entities.find(e => e.sid == id);
- let aimPano;
- if (!entity) {
- return console.error('没找到entity ')
- }
-
- if(Potree.settings.displayMode == 'showPanos'){
- if (isNearBy && entity.panos.length) {
- if(entity.panos.includes(viewer.images360.currentPano)) return 'posNoChange' //已在当前实体中
- let position = viewer.scene.getActiveCamera().position;
- let request = [];
- let rank = [Images360.scoreFunctions.distanceSquared({ position })];
- let r = Common.sortByScore(entity.panos, request, rank);
- aimPano = r[0].item;
- } else {
- if (!entity.flagPano) {
- return console.log('没有flagPano')
- }
- aimPano = entity.flagPano;
- }
- if(aimPano == viewer.images360.currentPano) return 'posNoChange'
- viewer.images360.flyToPano(aimPano);
- }else {
- if(isNearBy && entity.ifContainsPoint(viewer.images360.position) ){
- return 'posNoChange' //已在当前实体中
- }
- let boundingBox = entity.getBound();
- let position = boundingBox.getCenter(new Vector3()); //中心点不一定在entity中,比如半环形建筑(所以要不要改成到漫游点呢)
-
- if(viewer.modules.Clip && viewer.modules.Clip.editing){
- viewer.modules.Clip.bus.dispatchEvent({type:'flyToPos', position});
- }else {
- if(math.closeTo(position, viewer.images360.position)) return 'posNoChange'
- let size = boundingBox.getSize(new Vector3());
-
- viewer.scene.view.setView({position, duration});
- viewer.mapViewer.moveTo(position, size, duration);
- }
-
- }
- return true
- },
- focusEntity(id){
- var entity = this.entities.find(e => e.sid == id);
- let boundingBox = entity.getBound();
- //let boundSize = boundingBox.getSize(new THREE.Vector3())
- let center = boundingBox.getCenter(new Vector3());
- this.SplitScreen.focusOnObject(boundingBox, center);
-
- this.gotoEntity(id, false, 0);
-
- },
-
- removeIlligalArchi(){//删除marker数量小于3个的建筑,当保存时
- let needDelete = [];
-
- this.entities.forEach(e=>{
- if(e.points.length<3){
- needDelete.push(e);
- }
- });
-
- needDelete.forEach(e=>this.removeEntity(e));
-
- },
- };
- // Author: Fyrestar https://mevedia.com (https://github.com/Fyrestar/THREE.InfiniteGridHelper)
- class InfiniteGridHelper extends Mesh{
-
- constructor(size1, size2, color, distance, opacity1=0.2, opacity2=1){
-
- color = color || new Color('white');
- size1 = size1 || 10;
- size2 = size2 || 100;
- distance = distance || 8000; //可视距离?越远越模糊
- const geometry = new PlaneBufferGeometry(2, 2, 1, 1);
- const material = new ShaderMaterial({
- side: DoubleSide,
- uniforms: {
- uSize1: {
- value: size1
- },
- uSize2: {
- value: size2
- },
-
- opacity1:{//线条1的
- value: opacity1
- },
- opacity2:{//线条2的
- value: opacity2
- },
-
- uColor: {
- value: color
- },
- uDistance: {
- value: distance
- }
- },
- transparent: true,
- vertexShader: `
-
- varying vec3 worldPosition;
-
- uniform float uDistance;
-
- void main() {
-
- vec3 pos = position.xyz * uDistance;
- pos.xy += cameraPosition.xy;
-
- worldPosition = pos;
-
- gl_Position = projectionMatrix * modelViewMatrix * vec4(pos, 1.0);
-
- }
- `,
- fragmentShader: `
-
- varying vec3 worldPosition;
-
- uniform float uSize1;
- uniform float uSize2;
- uniform float opacity1;
- uniform float opacity2;
- uniform vec3 uColor;
- uniform float uDistance;
-
-
-
- float getGrid(float size) {
-
- vec2 r = worldPosition.xy / size;
-
-
- vec2 grid = abs(fract(r - 0.5) - 0.5) / fwidth(r);
- float line = min(grid.x, grid.y);
-
-
- return 1.0 - min(line, 1.0);
- }
- //为何侧面看不到线,因为mesh的正侧面都看不到?
- void main() {
-
-
- float d = 1.0 - min(distance(cameraPosition.xy, worldPosition.xy) / uDistance, 1.0);
-
- float g1 = getGrid(uSize1);
- float g2 = getGrid(uSize2);
-
-
- gl_FragColor = vec4(uColor.rgb, mix(g2, g1, g1) * pow(d, 3.0));
- //gl_FragColor.a = mix(0.5 * gl_FragColor.a, gl_FragColor.a, g2);
- gl_FragColor.a = mix(opacity1 * gl_FragColor.a, opacity2 * gl_FragColor.a, g2);
-
-
- if ( gl_FragColor.a <= 0.0 ) discard;
-
-
- }
-
- `,
- extensions: {
- derivatives: true
- }
-
-
-
- });
-
-
- super(geometry, material);
- this.frustumCulled = false;
- }
-
-
- };
- /*
- THREE.InfiniteGridHelper.prototype = {
- ...THREE.Mesh.prototype,
- ...THREE.Object3D.prototype,
- ...THREE.EventDispatcher.prototype
- };
- */
- const texLoader$7 = new TextureLoader();
- texLoader$7.crossOrigin = "anonymous";
-
- const viewportProps$1 = [{
- left:0,
- bottom:0,
- width: 0.5,height:1,
- name : 'top',
- axis:["x","y"],
- direction : new Vector3(0,0,-1), //镜头朝向
- active: true,
- //相机位置在z轴正向
- limitBound: new Box3(new Vector3(-Infinity,-Infinity, 1),new Vector3(Infinity,Infinity,5000)) //在地面以上
- },
- {
- left:0.5,
- bottom:0,
- width: 0.5,height:1,
- name : 'right',
- axis:["y","z"],
- direction : new Vector3(1,0,0),
- active: true,
- //相机位置在x轴负向 右下角屏
- } ];
-
- let MergeEditor = {
- bus:new EventDispatcher(),
-
-
- SplitScreen : new SplitScreen(),
-
- init(){
-
- {
- let ground = this.ground = new InfiniteGridHelper(1, 1000, new Color('#fff'), 1000, 0.2, 0.3);
- viewer.scene.scene.add(ground);
- //再加两条线否则在正侧边看不到
- let line1 = LineDraw.createLine([new Vector3(-1000, 0, 0),new Vector3(1000, 0, 0) ], {color:'#666'});
- let line2 = LineDraw.createLine([new Vector3(0, -1000, 0),new Vector3(0, 1000, 0) ], {color:'#666'});
- ground.renderOrder = line1.renderOrder + 1;
- ground.add(line1);
- ground.add(line2);
- }
-
-
- viewer.setControls(viewer.orbitControls);
- viewer.mainViewport.view.fixZWhenPan = true;
-
-
-
- viewer.addEventListener('click',(e)=>{
- if(e.intersect){
- let object = e.intersect.object || e.intersect.pointcloud;
- let objects = this.getAllObjects();
- if(objects.includes(object)){
- this.selectModel(object);
- }
- }else {
- this.selectModel(object);
- }
- });
-
- },
-
-
- enterSplit(){
- this.SplitScreen.splitStart(viewportProps$1);
-
-
- },
-
- leaveSplit(){
- this.SplitScreen.unSplit();
- },
-
-
-
- //---------------------------
-
-
- getAllObjects(){
- return viewer.objs.children.concat(viewer.scene.pointclouds)
- },
- selectModel(model, state, by2d){
- if(!model) {
- model = this.selected;
- state = false;
- }
-
- if(state){
- if(this.selected){
- if(this.selected == model) return
- else this.selectModel(this.selected, false, by2d);
- }
- this.selected = model;
- MergeEditor.focusOnSelect(model);
- viewer.outlinePass.selectedObjects = [pointcloud];
- }else {
- viewer.outlinePass.selectedObjects = [];
- }
-
- if(by2d && model){
- model.dispatchEvent({type:'changeSelect', selected : state});
- }
-
- },
-
-
- focusOnSelect(object, duration = 400){
- let boundingBox = object.boundingBox.clone().applyMatrix4(object.matrixWorld);
- let center = boundingBox.getCenter(new Vector3);
- let size = boundingBox.getSize(new Vector3);
- let maxSize = size.length(); //对角线长度
-
- if(object.isPointcloud){
- maxSize /= 2;
- }
-
- let hfov = cameraLight.getHFOVForCamera(viewer.mainViewport.camera,true);
- let minRadius = maxSize / Math.tan(hfov/2);
- //viewer.mainViewport.view.lookAt(center)
- viewer.mainViewport.view.setView({
- position: center.clone().sub(viewer.mainViewport.view.direction.clone().multiplyScalar(minRadius)),
- target: center,
- duration
- }); //setView can cancel bump
-
- },
-
-
- setModelBtmHeight(model,z ){
- //无论模型怎么缩放、旋转,都使最低点为z
- if(z == void 0) z = model.btmHeight; //离地高度
- else model.btmHeight = z;
-
- model.updateMatrixWorld();
- let boundingBox2 = model.boundingBox.clone().applyMatrix4(object.matrixWorld);
- let size = boundingBox2.getSize(new Vector3);
- let center = boundingBox2.getCenter(new Vector3);
-
- object.position.z = z + size.z / 2 - center.z;
- }
- };
- const texLoader$8 = new TextureLoader();
- const circleGeo = new CircleGeometry(1.45,100);
- const sphereGeo = new SphereBufferGeometry(0.018,10,10);
-
-
- const magDistance_ = 1;//相机离目标位置的距离的分界线,当离得远时要缩小fov以使看到的视野固定(望远镜效果)
- /* const radius_ = 0.2; //当相机离目标位置的距离>magDistance_时,希望看到的视野的半径
- const maxFov = THREE.Math.radToDeg(Math.atan(radius_ / magDistance_ )) * 2//提前计算出当相机离目标位置的距离<magDistance_时的fov,均使用=magDistance_时的fov。只要保证该fov大于主相机的fov就会有放大效果
- */
- let w$2 = 200/1.43;
- let maxPX = 1366*1024; //ipad pro. 大于这个分辨率的就直接用devicePixelRatio, 如macbook也是
- const width2dPX = Math.round(window.devicePixelRatio >= 2 ? ( window.screen.width * window.screen.height >= maxPX ? window.devicePixelRatio/1.2 : window.devicePixelRatio/1.5)*w$2 : w$2); //触屏或高分辨率的可能要放大些。但在手机上不能太大
- console.log('width2dPX', width2dPX);
- class Magnifier extends Object3D {//放大镜or望远镜
- constructor (viewer) {
- super();
- this.width = this.height = width2dPX/* * window.devicePixelRatio */;
- this.camera = new PerspectiveCamera(50, 1, 0.01, 10000); //fov aspect near far
- this.camera.up = new Vector3(0,0,1);
-
-
- this.viewport = new Viewport( null, this.camera, {
- left:0, bottom:0, width:1, height: 1, name:'magnifier' , cameraLayers:['magnifierContent'],
- pixelRatio:1
- });
- this.viewport.setResolution(this.width, this.height,0,0);
-
- {
- let density;
- let sizeType;
- let colorType;
- let opacityBefore = new Map();
- this.viewport.beforeRender = ()=>{
- viewer.scene.pointclouds.forEach(e=>{//因为更改pointDensity时会自动变opacity,所以这项最先获取
- opacityBefore.set(e,e.temp.pointOpacity);
- });
-
-
- //使放大镜里的pointDensity是'magnifier' 最高质量。
- density = Potree.settings.pointDensity;
- Potree.settings.pointDensity = 'magnifier';
-
- viewer.scene.pointclouds.forEach(e=>{//因为全景模式的pointSizeType是fixed所以要还原下
- sizeType = e.material.pointSizeType;
- e.material.pointSizeType = Potree.config.material.pointSizeType;
-
- //材质
- colorType = e.material.activeAttributeName;
- e.material.activeAttributeName = 'rgba';
- e.changePointOpacity(1);
-
- });
- };
-
-
- this.viewport.afterRender = ()=>{
- Potree.settings.pointDensity = density;
-
- viewer.scene.pointclouds.forEach(e=>{
- e.material.pointSizeType = sizeType;
- e.material.activeAttributeName = colorType;
- e.changePointOpacity(opacityBefore.get(e));
- });
- };
- }
-
-
-
-
- this.renderTarget = new WebGLRenderTarget(this.width,this.height, {
- minFilter: LinearFilter, magFilter: LinearFilter,
- format: RGBAFormat ,
- /* type: THREE.FloatType,
- minFilter: THREE.NearestFilter,
- magFilter: THREE.NearestFilter,
- */
- } );
-
- this.rtEDL = new WebGLRenderTarget(this.width, this.height, { //好像没用到? 因为这里不绘制测量线
- minFilter: NearestFilter,
- magFilter: NearestFilter,
- format: RGBAFormat,
- type: FloatType,
- depthTexture: new DepthTexture(undefined, undefined, UnsignedIntType)
- });
-
-
-
- this.mesh = new Mesh(circleGeo, new MeshBasicMaterial({
- side: DoubleSide ,
- map: this.renderTarget.texture ,
- transparent:true,
- depthTest: !1,
- //depthWrite: !1,
- }));
- this.overlayMesh = new Mesh(circleGeo, new MeshBasicMaterial({
- side: DoubleSide ,
- map:texLoader$8.load(Potree.resourcePath+'/textures/crosshair.png') ,
- transparent:true,
- depthTest: !1,
- //depthWrite: !1,
- }));
- this.targetPoint = new Object3D;
-
- this.targetPoint.add(new Mesh(sphereGeo, new MeshBasicMaterial({
- color:"#ff0000",
- transparent:true,
- opacity:0.5,
- })));
- this.targetPoint.add(new Mesh(sphereGeo, new MeshBasicMaterial({
- color:"#ff0000",
- transparent:true,
- opacity:0.2,
- depthTest:false //被遮挡层
- })));
-
- this.targetPoint.name = 'magnifierPointTarget';
- viewer.scene.scene.add(this.targetPoint);
- viewer.setObjectLayers(this.targetPoint, 'magnifierContent' );
-
- this.add(this.mesh);
- this.add(this.overlayMesh);
-
- this.position.set(-1000,-1000,-100000);//令它看不见
- this.mesh.renderOrder = 10;
- this.overlayMesh.renderOrder = 11;
- this.aimPos;
- viewer.setObjectLayers(this, 'magnifier' );
- //viewer.inputHandler.addInputListener(this)
-
-
-
-
- viewer.addEventListener('camera_changed',(e)=>{ // 平移、滚轮时更新
- if(e.viewport == viewer.mainViewport) this.update(); //不过intersectPoint没更新
- });
-
-
-
-
- this.mesh.layers.set(Potree.config.renderLayers.magnifier);
- this.overlayMesh.layers.set(Potree.config.renderLayers.magnifier);
- //this.layers.set(Potree.config.renderLayers.magnifier);//这句在外层写没用
-
- this.dontRender = false;
- viewer.addEventListener('global_drag', (e)=>{//拖拽时不渲染。主要是右键平移时渲染延迟了,会闪烁。
- this.dontRender = true;
- });
- viewer.addEventListener('global_drop', (e)=>{
- this.dontRender = false;
- });
- viewer.addEventListener('global_mouseup', (e)=>{//测量时拖拽场景再mouseup
- this.dontRender = false;
- });
-
- var updateVisi = (e)=>{
- if(e.hoverViewport == viewer.mainViewport){
- viewer.updateVisible(this,"atViewport", true);
- this.update(e.intersectPoint && e.intersectPoint.location);
- }else {
- viewer.updateVisible(this,"atViewport", false); //小地图不显示
- }
-
- };
-
- viewer.addEventListener('global_mousemove', updateVisi);
- viewer.addEventListener('global_touchstart', updateVisi);
-
-
- /* viewer.addEventListener("beginSplitView",()=>{
- this.updateVisible("splitView", false)
- })
- viewer.addEventListener("finishSplitView",()=>{
- this.updateVisible("splitView", true)
- }) */
-
-
- this.addEventListener("setEnable",(e)=>{
- viewer.updateVisible(this, "enable", e.value); //界面开关
- /* if(Potree.settings.displayMode == 'showPanos') && e.value){
- Potree.settings.pointDensity = 'magnifier'
- }else if() */
-
- });
-
-
- if(Potree.settings.isOfficial){
- viewer.updateVisible(this, "enable", false);
- }else {
- viewer.updateVisible(this, "measure", false);
- viewer.addEventListener("measureMovePoint",()=>{//测量开始
- viewer.updateVisible(this, "measure", true);
- });
- viewer.addEventListener("endMeasureMove",()=>{
- viewer.updateVisible(this, "measure", false);
- });
- }
-
-
- viewer.scene.view.addEventListener('flyingDone',()=>{
- if(!this.visible)return
- let pickWindowSize = 100;
- let intersect = viewer.inputHandler.getIntersect(viewer.mainViewport, viewer.mainViewport.camera, true, pickWindowSize );
- this.update(intersect && intersect.location);
- });
- }
-
-
-
-
- //注意:在鼠标没有移动的时候,无法获取到最新的intersect, 放大镜内的内容可能是错误的。全景模式下更奇怪,原因未知
- update(aimPos){//相机靠近 navvis的做法
- var dontRender = this.dontRender || !(aimPos instanceof Vector3) || Potree.settings.displayMode == 'showPanos' && viewer.images360.flying;
- aimPos = aimPos instanceof Vector3 ? aimPos : this.aimPos;
- if(!aimPos || !this.visible)return
-
-
-
- var playerCamera = viewer.scene.getActiveCamera();
- var playerPos = playerCamera.position;//viewer.scene.view.getPivot()
- var dis = playerPos.distanceTo(aimPos);
- var dirToCamera = new Vector3().subVectors(playerPos, aimPos ).normalize();
-
-
- //相机位置
- var finalDisToAim = dis>magDistance_ ? magDistance_ : dis / 2;
- this.camera.position.copy(aimPos).add(dirToCamera.multiplyScalar(finalDisToAim));
- this.camera.lookAt(aimPos);
- this.camera.fov = playerCamera.fov / 2;
- this.camera.updateProjectionMatrix();
-
-
-
- //自身位置
- //let pos2d = viewer.inputHandler.pointer.clone(); //跟随鼠标
- let pos2d = Potree.Utils.getPos2d(aimPos, playerCamera, viewer.renderArea, viewer.mainViewport).vector; //更新目标点的实时二维位置
- let margin = 0.4, maxY = 0.4;
- let screenPos = pos2d.clone().setY(pos2d.y + (pos2d.y>maxY ? -margin : margin ));
-
- let newPos = new Vector3(screenPos.x,screenPos.y,0.8).unproject(playerCamera); //z:-1朝外
- let dir = newPos.clone().sub(playerPos).normalize().multiplyScalar(10);//这个数值要大于playerCamera.near
- let s = dis>magDistance_ ? 1 : dis / magDistance_ ;
-
- this.position.copy(playerPos.clone().add(dir));
- this.quaternion.copy(playerCamera.quaternion);
- this.targetPoint.position.copy(aimPos);
- this.targetPoint.scale.set(s,s,s);
- this.aimPos = aimPos;
-
-
- var scale = math.getScaleForConstantSize({//
- width2d : width2dPX,
- camera:viewer.scene.getActiveCamera(), position: this.getWorldPosition(new Vector3()),
- resolution: viewer.mainViewport.resolution2
- });
- this.scale.set(scale, scale, scale);
-
- if(!dontRender){
- this.waitRender = true;
- }
-
- }
-
-
- /* update(aimPos){ //仅改fov的版本
-
- aimPos = aimPos instanceof THREE.Vector3 ? aimPos : this.aimPos
- if(!aimPos || !this.visible)return
-
-
- //相机位置
- var playerCamera = viewer.scene.getActiveCamera()
- var playerPos = playerCamera.position;//viewer.scene.view.getPivot()
- var dis = playerPos.distanceTo(aimPos);
-
-
- if(dis<magDistance_){
- this.camera.fov = maxFov
- }else{
- this.camera.fov = THREE.Math.radToDeg(Math.atan(radius_ / dis )) * 2 //radius_是能看到的范围半径。当dis大于magDistance_时就放大,否则维持fov为maxFov
- }
-
- this.camera.updateProjectionMatrix()
- this.camera.position.copy(playerPos)
- this.camera.lookAt(aimPos)
-
- this.quaternion.copy(playerCamera.quaternion);
-
- let pointer = viewer.inputHandler.pointer.clone();
- let margin = 0.4, maxY = 0.4
- let screenPos = pointer.clone().setY(pointer.y + (pointer.y>maxY ? -margin : margin ))
-
-
- let newPos = new THREE.Vector3(screenPos.x,screenPos.y,0.8).unproject(playerCamera); //z:-1朝外
- let dir = newPos.clone().sub(playerPos).normalize().multiplyScalar(10);//这个数值要大于playerCamera.near
-
- this.position.copy(playerPos.clone().add(dir))
-
- this.aimPos = aimPos
- this.targetPoint.position.copy(aimPos);
-
- var scale = math.getScaleForConstantSize({//
- width2d : width2dPX,
- camera:viewer.scene.getActiveCamera(), position: this.getWorldPosition(new THREE.Vector3()),
- resolution: viewer.mainViewport.resolution2
- })
- this.scale.set(scale, scale, scale);
-
- if(!this.dontRender){
- this.waitRender = true
- }
- }//位置需要计算,不仅仅是点云,所以需要深度图
- */
-
-
-
-
-
- render(){
-
- if(!this.waitRender)return
- //this.visible = false;//防止放大镜里有自己
- viewer.render({
- target : this.renderTarget,
- viewports : [this.viewport],
- camera : this.camera,
- magnifier : true,
- rtEDL: this.rtEDL
- /* width :this.renderTarget.width,
- height: this.renderTarget.height, */
- });
- //this.visible = true;
- this.waitRender = false;
-
-
-
-
- }
-
- }
- let texLoader$9 = new TextureLoader();
- let defaultOpacity = 0.7;
-
- //鼠标指示小圆片
- class Reticule extends Mesh{
- constructor(viewer){
- var defaultTex = texLoader$9.load(Potree.resourcePath+'/textures/whiteCircle.png'/* reticule-256x256.png' */);
- super(new PlaneBufferGeometry(0.11,0.11,1,1),new MeshBasicMaterial({
- side: DoubleSide ,
- map: defaultTex,
- transparent:true,
- depthTest: !1,
- opacity: defaultOpacity,
- //depthWrite: !1,
- }));
- this.name = 'reticule';
- this.defaultTex = defaultTex;
- this.crosshairTex = texLoader$9.load(Potree.resourcePath+'/textures/reticule_cross_hair.png');
- this.forbitTex = texLoader$9.load(Potree.resourcePath+'/textures/pic-forbid.png');
-
- //this.layers.set(0/* RenderLayers.RETICULE */);
- this.renderOrder = 0;
- this.layers.set(Potree.config.renderLayers.marker);
-
-
- this.direction = new Vector3;
- this.hidden = !0;
- this.mouseLastMoveTime = Date.now();
- this.hoverViewport;
- this.matrixMap = new Map;
- this.matrixAutoUpdate = false;
-
- this.hide(0);
- //viewer.inputHandler.addInputListener(this);
- viewer.addEventListener('global_mousemove',this.move.bind(this));
- //viewer.addEventListener('global_click',this.move.bind(this))
- viewer.addEventListener('global_mousedown',this.move.bind(this));//主要针对触屏
-
-
-
-
- this.state = {};
-
- viewer.addEventListener('measureMovePoint',()=>{
- this.state.cross = true;
- this.judgeTex();
- });
- viewer.addEventListener('endMeasureMove',()=>{
- this.state.cross = false;
- this.judgeTex();
- });
-
- viewer.addEventListener('reticule_forbit',(e)=>{
- if(this.state.forbit != e.v){
- console.log('change forbit ',e.v);
- }
- this.state.forbit = e.v;
- this.judgeTex();
- });
-
-
-
- viewer.setObjectLayers(this, 'sceneObjects' );
- }
- judgeTex(){
- if(this.state.forbit){
- this.material.map = this.forbitTex;
- }else if(this.state.cross){
- this.material.map = this.crosshairTex;
- }else {
- this.material.map = this.defaultTex;
- }
-
-
- viewer.mapViewer && viewer.mapViewer.dispatchEvent({type:'content_changed'});
- }
- move(e){
- if(e.type == "global_mousemove" && (e.isTouch || e.buttons != Buttons.NONE) && this.state != 'crosshair'){
- return//按下时不更新,除非拖拽测量
- }
-
- this.mouseLastMoveTime = Date.now();
-
- this.updatePosition(e.intersect, e.hoverViewport);
-
- }
- hide(duration = 500){
- if(this.hidden)return
-
-
-
- this.hidden = !0;
- transitions.start(lerp.property(this.material , "opacity", 0), duration);
-
- this.dispatchEvent({type:'update', visible:false});
-
- setTimeout(()=>{
- this.dispatchEvent({type:'update', visible:false});
- },duration);
-
- }
- show(duration = 300){
-
- if(!viewer.getObjVisiByReason(this, 'force'))return
- //console.log("show Reticule")
- this.hidden = !1;
-
- if(this.material.opacity <= 0){
- transitions.start(lerp.property(this.material, "opacity", defaultOpacity), duration);
-
- this.dispatchEvent({type:'update', visible:true});
-
- setTimeout(()=>{
- this.dispatchEvent({type:'update', visible:false});
- },duration);
-
- }
-
-
-
- }
- //鼠标静止一段时间它就会消失
- updateVisible(){
- Date.now() - this.mouseLastMoveTime > 1500 && !this.hidden && this.hide();
- }
-
-
- updateScale(viewport){
- let s, camera = viewport.camera;
- if(camera.type == "OrthographicCamera"){
-
- var sizeInfo = this.state.cross ? {width2d:500} : {minSize : 100, maxSize : 400, nearBound : 100 , farBound : 700};
- s = math.getScaleForConstantSize($.extend( sizeInfo ,
- {position:this.position, camera, resolution:viewport.resolution/* 2 */} ));
-
- }else {
-
- let n = camera.position.distanceTo(this.position);
- s = 1 + .01 * n;
- n < 1 && (s -= 1 - n);
- }
- this.scale.set(s, s, s);
-
- }
- updateAtViewports(viewport){//当多个viewports时更新。更新大小等
-
- if(viewport.name == 'magnifier' )return
-
- if(this.hoverViewport && this.hoverViewport.name == 'mapViewport' && viewport != this.hoverViewport){
- //若是在地图上更新,在其他viewport要隐藏。因为在地图上无法得知高度。
- viewer.updateVisible(this, 'hoverMap', false);
- return;
- }
- viewer.updateVisible(this, 'hoverMap', true);
-
- var matrix = this.matrixMap.get(viewport);
- if(!matrix){
- this.updateScale(viewport);
- this.updateMatrix();
- //this.updateMatrixWorld()
- this.matrixMap.set(viewport, this.matrix.clone());
- }else {
- this.matrix.copy(matrix);
- //this.updateMatrixWorld()
- }
-
- }
-
-
-
-
- updatePosition(intersect, viewport ){ //在地图(当地图融合到viewer时)和场景里都显示且完全相同(大小可能不同)
-
- if (viewer.getObjVisiByReason(this, 'force')) {//没有被强制隐藏,如进入某个页面后强制不显示
- if (!intersect /* || !intersect.point.normal */){
- return //this.hide();
- }
-
- var atMap = !intersect.location;
- let location = intersect.location || intersect.orthoIntersect.clone();
- let normal;
-
-
- //地图上要瞬间变化 , 因为要使needRender为true很麻烦
- this.show(atMap ? 0 : 300);
-
-
- if(atMap){
- normal = new Vector3(0,0,1);//地图无normal
- location.setZ(0);//低于相机高度即可
- this.direction = normal.clone();
- }else {
- if(intersect.point){
- if(intersect.pointcloud){
- normal = new Vector3().fromArray(intersect.point.normal ).applyMatrix4( intersect.pointcloud.rotateMatrix );
- }else {//mesh
- normal = new Vector3().copy(intersect.point.normal).applyQuaternion(intersect.object.quaternion);
- }
- }else {
- normal = intersect.normal; //when showPanos
- }
-
- if(normal){
- let ratio = /* Potree.settings.useDepthTex ? 1 : */ 0.2;
- this.direction = this.direction.multiplyScalar(1-ratio);
- this.direction.add(normal.clone().multiplyScalar(ratio));
- }
- //this.direction = normal.clone() //改为瞬间变化,否则刚hover上某个点时看起来不太对
- }
-
-
-
-
- this.position.copy(location);/* .add(normal.clone().multiplyScalar(.01)); */
- this.updateMatrix(); //lookAt之前要保证得到matrix
- this.lookAt(this.position.clone().add(this.direction));
-
-
- this.hoverViewport = viewport; //记录下最近一次hover过的viewport
- this.updateScale(viewport);
-
-
- {//存储matrix,节省计算
- this.updateMatrix();
- //this.updateMatrixWorld()
- this.matrixMap.clear();//重新计算
- this.matrixMap.set(viewport, this.matrix.clone());
- //别处会updateMatrixWorld
- }
-
- this.dispatchEvent({type:'update'});
-
- //为什么navvis在校准数据集时每个viewport里reticule的朝向都刚好垂直于屏幕,似乎限定在了一定范围内,还是在pick时就只pick范围内的点?
-
-
- }
- }
-
- //navvis在地图等地方看reticule是有厚度的
-
- /* updateMatrixWorld(force){
- console.log('updateMatrixWorld', force)
- super.updateMatrixWorld(force)
- } */
- }
- const FEET_TO_INCHES_FACTOR = 12;
- const EIGHTHS_SYMBOLS = ["", "⅛", "¼", "⅜", "½", "⅝", "¾", "⅞"];//eighths 八分之……
- class UnitOfMeasurement{//转化单位工具
- constructor(t, e, n, i){
- this.name = t,
- this.symbol = e,
- this.base = n,
- this.factor = i;
- }
- toBase(t) {//换算到base
- return t * this.factor
- }
-
- fromBase(t) {//换算到当前
- return t / this.factor
- }
- }
-
-
-
- /* var o = function t(e) {
- this.gettext = e,
- t.METRIC = this.gettext("metric", void 0, "measurement system"),
- t.IMPERIAL = this.gettext("imperial", void 0, "measurement system")
-
-
- };
- e.UoMSystem = o;
- let UoMSystem = {
-
- } */
- /* var MeasurementDomain = {
- DISTANCE : "DISTANCE",
- t.AREA = "AREA",
- t.VOLUME = "VOLUME",
- t.DATA = "DATA",
- t
- }
-
- */
-
-
-
-
-
-
-
- var UnitsOfMeasurement = {
-
- MILLIMETER : ["Millimeter", "mm"],
- CENTIMETER : ["Centimeter", "cm"],
- METER : ["Meter", "m"],
- KILOMETER : ["Kilometer", "km"],
- INCH : ["Inch", "in"],
- FOOT : ["Foot", "ft"],
- MILE : ["Mile", "mi"],
- SQUAREMETER : ["SquareMeter", "m²"],
- SQUAREFOOT : ["SquareFoot", "ft²"],
- CUBICMETER : ["CubicMeter", "m³"],
- CUBICFOOT : ["CubicFoot", "ft³"],
- BYTE : ["Byte", "B"],
- KILOBYTE : ["Kilobyte", "kB"],
- MEGABYTE : ["Megabyte", "MB"],
- GIGABYTE : ["Gigabyte", "GB"],
- TERABYTE : ["Terabyte", "TB"],
- PETABYTE : ["Petabyte", "PB"],
-
-
- init : function() {
- var e, n, i, a, s, c, l, u, d, p, h,
- f = new UnitOfMeasurement(UnitsOfMeasurement.METER[0],UnitsOfMeasurement.METER[1],void 0,1),
- g = new UnitOfMeasurement(UnitsOfMeasurement.SQUAREMETER[0],UnitsOfMeasurement.SQUAREMETER[1],void 0,1),
- m = new UnitOfMeasurement(UnitsOfMeasurement.CUBICMETER[0],UnitsOfMeasurement.CUBICMETER[1],void 0,1),
- v = new UnitOfMeasurement(UnitsOfMeasurement.BYTE[0],UnitsOfMeasurement.BYTE[1],void 0,1);
-
- UnitsOfMeasurement.DISTANCE = (
- (e = {})['metric'] = ((n = {})[UnitsOfMeasurement.MILLIMETER[0]] = new UnitOfMeasurement(UnitsOfMeasurement.MILLIMETER[0],UnitsOfMeasurement.MILLIMETER[1],f,.001),
- n[UnitsOfMeasurement.CENTIMETER[0]] = new UnitOfMeasurement(UnitsOfMeasurement.CENTIMETER[0],UnitsOfMeasurement.CENTIMETER[1],f,.01),
- n[UnitsOfMeasurement.METER[0]] = f,
- n[UnitsOfMeasurement.KILOMETER[0]] = new UnitOfMeasurement(UnitsOfMeasurement.KILOMETER[0],UnitsOfMeasurement.KILOMETER[1],f,1e3),
- n),
- e['imperial'] = ((i = {})[UnitsOfMeasurement.INCH[0]] = new UnitOfMeasurement(UnitsOfMeasurement.INCH[0],UnitsOfMeasurement.INCH[1],f,.0254),
- i[UnitsOfMeasurement.FOOT[0]] = new UnitOfMeasurement(UnitsOfMeasurement.FOOT[0],UnitsOfMeasurement.FOOT[1],f,.3048),
- i[UnitsOfMeasurement.MILE[0]] = new UnitOfMeasurement(UnitsOfMeasurement.MILE[0],UnitsOfMeasurement.MILE[1],f,1609.344),
- i),
- e);
-
- UnitsOfMeasurement.AREA = ((a = {})['metric'] = ((s = {})[UnitsOfMeasurement.SQUAREMETER[0]] = g,
- s),
- a['imperial'] = ((c = {})[UnitsOfMeasurement.SQUAREFOOT[0]] = new UnitOfMeasurement(UnitsOfMeasurement.SQUAREFOOT[0],UnitsOfMeasurement.SQUAREFOOT[1],g,.092903),
- c),
- a);
-
-
- UnitsOfMeasurement.VOLUME = ((l = {})['metric'] = ((u = {})[UnitsOfMeasurement.CUBICMETER[0]] = m,
- u),
- l['imperial'] = ((d = {})[UnitsOfMeasurement.CUBICFOOT[0]] = new UnitOfMeasurement(UnitsOfMeasurement.CUBICFOOT[0],UnitsOfMeasurement.CUBICFOOT[1],m,.0283168),
- d),
- l);
-
- //数据大小
- var y = ((p = {})[UnitsOfMeasurement.BYTE[0]] = v,
- p[UnitsOfMeasurement.KILOBYTE[0]] = new UnitOfMeasurement(UnitsOfMeasurement.KILOBYTE[0],UnitsOfMeasurement.KILOBYTE[1],v,1e3),
- p[UnitsOfMeasurement.MEGABYTE[0]] = new UnitOfMeasurement(UnitsOfMeasurement.MEGABYTE[0],UnitsOfMeasurement.MEGABYTE[1],v,1e6),
- p[UnitsOfMeasurement.GIGABYTE[0]] = new UnitOfMeasurement(UnitsOfMeasurement.GIGABYTE[0],UnitsOfMeasurement.GIGABYTE[1],v,1e9),
- p[UnitsOfMeasurement.TERABYTE[0]] = new UnitOfMeasurement(UnitsOfMeasurement.TERABYTE[0],UnitsOfMeasurement.TERABYTE[1],v,1e12),
- p[UnitsOfMeasurement.PETABYTE[0]] = new UnitOfMeasurement(UnitsOfMeasurement.PETABYTE[0],UnitsOfMeasurement.PETABYTE[1],v,1e15),
- p);
- UnitsOfMeasurement.DATA = ((h = {})['metric'] = y,
- h['imperial'] = y,
- h);
- }
- ,
- getUnitsOfMeasurementByDomain : function(e) {
-
- return this[e.toUpperCase()]
-
- /* switch (e.toUpperCase()) {
- case a.DISTANCE:
- return t.DISTANCE;
- case a.AREA:
- return t.AREA;
- case a.VOLUME:
- return t.VOLUME;
- case a.DATA:
- return t.DATA;
- default:
- console.error(e + " measurement domain is not supported.")
- } */
- }
- ,
- getUnitsOfMeasurementByDomainAndSystem : function(domain, system) {
- var r = this.getUnitsOfMeasurementByDomain(domain);
- if (r.hasOwnProperty(system.toLowerCase()))
- return r[system.toLowerCase()];
- console.error(n + " measurement system is not supported.");
- }
- ,
- getDefaultUnitByDomainAndSystem : function(e, n) {
- switch (e.toUpperCase()) {
- case 'DISTANCE':
- switch (n.toLowerCase()) {
- case 'metric':
- return this.DISTANCE['metric'][this.METER[0]];
- case 'imperial':
- return this.DISTANCE['imperial'][this.FOOT[0]];
- default:
- console.error(n + " measurement system is not supported.");
- }
- case 'AREA':
- switch (n.toLowerCase()) {
- case 'metric':
- return this.AREA['metric'][this.SQUAREMETER[0]];
- case 'imperial':
- return this.AREA['imperial'][this.SQUAREFOOT[0]];
- default:
- console.error(n + " measurement system is not supported.");
- }
- case 'VOLUME':
- switch (n.toLowerCase()) {
- case 'metric':
- return this.VOLUME['metric'][this.CUBICMETER[0]];
- case 'imperial':
- return this.VOLUME['imperial'][this.CUBICFOOT[0]];
- default:
- console.error(n + " measurement system is not supported.");
- }
- case 'DATA':
- switch (n.toLowerCase()) {
- case 'metric':
- return this.DATA['metric'][this.BYTE[0]];
- case 'imperial':
- return this.DATA['imperial'][this.BYTE[0]];
- default:
- console.error(n + " measurement system is not supported.");
- }
- default:
- console.error(e + " measurement domain is not supported.");
- }
- }
-
-
- };
-
-
-
-
- class UnitService{
-
- constructor(/* e, n, i */){
- //this.LanguageService = e,
- //this.localStorageService = n,
- //this.gettext = i,
- //this.unitChanged = new r.Signal,
- this.LOCAL_STORAGE_KEY = "iv_unit_key";//?
- UnitsOfMeasurement.init();
- this.unitSystems = ['metric', 'imperial'];//[o.UoMSystem.METRIC, o.UoMSystem.IMPERIAL],
- this.defaultSystem = 'metric';//'imperial'
- //var a = this.LanguageService.getBrowserLocaleString().toLowerCase();
- //this.defaultSystem = t.isLocaleImperial(a) ? o.UoMSystem.IMPERIAL : o.UoMSystem.METRIC,
-
- //this.initUnit()
-
- }
-
-
- /* initUnit() {
- var t = this.localStorageService.get(this.LOCAL_STORAGE_KEY);
- if (t)
- for (var e = 0, n = this.unitSystems; e < n.length; e++) {
- var i = n[e];
- if (i === t)
- return void this.setUnit(i, !0)
- }
- this.setUnit(this.defaultSystem, !1)
- }
- setUnit(t, e) {
- this.currentSystem !== t && (this.currentSystem = t,
- this.unitChanged.emit()),
- e && this.localStorageService.set(this.LOCAL_STORAGE_KEY, t)
- } */
-
-
-
- /*isLocaleImperial(e) {
- return t.IMPERIAL_LOCALES.indexOf(e.toLowerCase()) >= 0
- }
- ,
- t.IMPERIAL_LOCALES = ["en_us"],
- t.ɵfac(e) {
- return new (e || t)(c.ɵɵinject(l.LanguageService),
- c.ɵɵinject("localStorageService"),c.ɵɵinject("gettext"))
- }
- ,
- t.ɵprov = c.ɵɵdefineInjectable({
- token: t,
- factory: t.ɵfac,
- providedIn: "root"
- }), */
-
- }
-
- class UoMService{
- constructor(/* UnitService */){
- this.UnitService = new UnitService();/* UnitService */
-
- }
- scopedConvert (t, n, precision = 2, r, minFactor) {
- return this.convert(t, n, precision, r, minFactor)
- }
-
- convert(number, domain, precision = 2, system, minFactor, ifEighths = !1) {
- if (!number) return "";
- var s = this.getMostRelevantMeasurement(domain, system || this.UnitService.currentSystem, number, minFactor);
- return this.getFormattedMeasurementString(s[0], s[1], precision, ifEighths)
- }
-
- convertBack(number, domain, precision = 2, fromSystem, minFactor ) { //从英制转到'metric'
- if (!number) return "";
- var d = UnitsOfMeasurement.getDefaultUnitByDomainAndSystem(domain,'metric');
-
- var s = this.getMostRelevantMeasurement2(domain, fromSystem, number, minFactor);
- return this.getFormattedMeasurementString(s[0], d, precision )
-
-
- /* 栗子:
- viewer.unitConvert.convertBack(1, 'area', 5, 'imperial')
- '0.09290 m²'
- viewer.unitConvert.convertBack(1, 'Distance', 2, 'imperial')
- '0.03 m'
- */
- }
-
- getFormattedMeasurementString(number, unit, precision, ifEighths) {
- var result;
- if(ifEighths && unit.name === UnitsOfMeasurement.FOOT[0]){
- result = this.formatImperialDistance(number * FEET_TO_INCHES_FACTOR);
- }else if(ifEighths && unit.name === UnitsOfMeasurement.INCH[0]){
- result = this.formatImperialDistance(number);
-
- }else {
- result = number.toLocaleString(void 0, {
- minimumFractionDigits: precision,
- maximumFractionDigits: precision
- }) + " " + unit.symbol;
- }
-
- return result
- }
-
- formatImperialDistance(e) {
- var n = Math.round(8 * e)
- , i = Math.floor(n / 8)
- , r = Math.floor(i / FEET_TO_INCHES_FACTOR)
- , o = i - r * FEET_TO_INCHES_FACTOR
- , a = EIGHTHS_SYMBOLS[n % 8]
- , s = 0 === o && "" !== a ? "" : o;
-
- "" !== s && "" !== a && (a = " " + a);
-
- return 0 !== r ? r + "' " + s + a + '"' : "" + s + a + '"'
- }
-
- getMostRelevantMeasurement(domain, system, number, minFactor=0) {
- /* var a = r.values(UnitsOfMeasurement.getUnitsOfMeasurementByDomainAndSystem(domain, system))
- , s = r.filter(a, function(t) {
- return t.factor >= i
- })
- , c = r.reduce(s, function(t, e) {
- return e.fromBase(number) < t.fromBase(number) && e.fromBase(number) >= 1 ? e : t
- }); */
- let a = [];
- let u = UnitsOfMeasurement.getUnitsOfMeasurementByDomainAndSystem(domain, system);
- for(let i in u){a.push(u[i]);}
-
- let s = a.filter(m=>m.factor >= minFactor);
-
-
-
- let c = s.reduce(function(prev, currentValue) {//reduce最终值是最后一次return的值 ( 没看懂这句话作用)
- return currentValue.fromBase(number) < prev.fromBase(number) && currentValue.fromBase(number) >= 1 ? currentValue : prev
- });
-
- return c ? [c.fromBase(number), c] : void 0
- }
-
- getMostRelevantMeasurement2(domain, system, number, minFactor=0) {//add
- let a = [];
- let u = UnitsOfMeasurement.getUnitsOfMeasurementByDomainAndSystem(domain, system);
- for(let i in u){a.push(u[i]);}
- let s = a.filter(m=>m.factor >= minFactor);
- let c = s.reduce(function(prev, currentValue) {//reduce最终值是最后一次return的值 ( 没看懂这句话作用)
- return currentValue.toBase(number) < prev.toBase(number) && currentValue.toBase(number) >= 1 ? currentValue : prev
- });
- return c ? [c.toBase(number), c] : void 0
- }
- /* ɵfac(e){
- return new (e || t)(c.ɵɵinject(l.UnitService))
- }
-
- ɵprov = c.ɵɵdefineInjectable({
- token: t,
- factory: t.ɵfac,
- providedIn: "root"
- }) */
-
- }
- const texLoader$a = new TextureLoader();
- const arrowSpacing = 1; //间隔
- const arrowSize = arrowSpacing * 0.5;
- const planeGeo$3 = new PlaneBufferGeometry(1,1);
- const sphereSizeInfo = {
- nearBound : 2, scale:arrowSize, restricMeshScale : true,
- };
- //const arrowsShowingCount = 25; //场景里最多展示多少个箭头
- const arrowShowMinDis = 10;
- class RouteGuider extends EventDispatcher{
- constructor () {
- super();
-
- this.route = [];
- this.curve = [];
- this.scenePoints = [];
- this.sceneMeshGroup = new Object3D;
- this.mapMeshGroup = new Object3D;
- this.generateDeferred;
- viewer.addEventListener('loadPointCloudDone',this.init.bind(this));
-
- this.lastResult;//保存上一个的结果,以便于反向
- this.datasetIds = [];//起始和终点的datasetId
- }
- init(){
- if(this.inited) return;
-
- let zoom;
- viewer.mapViewer.addEventListener('camera_changed', e => {
- if(!this.routeStart || !this.routeEnd) return
- var camera = e.viewport.camera;
-
- Common.intervalTool.isWaiting('routeCameraInterval', ()=>{ //延时update,防止卡顿
- if(camera.zoom != zoom){
- //console.log('updateMapArrows')
- this.updateMapArrows(true);
- zoom = camera.zoom;
- return true
- }
- }, browser.isMobile()?500:200);
- });
-
-
-
-
- //let lastPos = new THREE.Vector3
- viewer.addEventListener('camera_changed', e => {
- if(!this.routeStart || !this.routeEnd || !e.changeInfo.positionChanged) return
- Common.intervalTool.isWaiting('routeCameraInterval', ()=>{ //延时update,防止卡顿
- //let currPos = viewer.scene.getActiveCamera().position
-
- //if(!currPos.equals(lastPos)){
- // lastPos.copy(currPos)
- this.updateArrowDisplay();
-
- return true
- //}
- }, 1000);
-
-
-
- });
-
-
-
- var polesMats = {
- shadowMat: new MeshBasicMaterial({
- transparent:true, depthTest:false,
- map: texLoader$a.load(Potree.resourcePath+'/textures/pano_instruction_bottomMarker.png' )
- }),
- sphereMat : new MeshBasicMaterial({
- transparent:true, depthTest:false,
- map: texLoader$a.load(Potree.resourcePath+'/textures/whiteCircle.png' )
- }),
- hatMats:{
- start: new MeshBasicMaterial({
- transparent:true, depthTest:false,
- map: texLoader$a.load(Potree.resourcePath+'/textures/pano_instruction_start_route.png' )
- }),
- end: new MeshBasicMaterial({
- transparent:true, depthTest:false,
- map: texLoader$a.load(Potree.resourcePath+'/textures/pano_instruction_target_reached.png' )
- })
- }
- };
- polesMats.shadowMat.map.anisotropy = 4;
-
- this.poleStart = this.createPole(polesMats, 'start');
- this.poleEnd = this.createPole(polesMats, 'end');
-
- this.sceneMeshGroup.add(this.poleStart);
- this.sceneMeshGroup.add(this.poleEnd);
-
-
- let map = texLoader$a.load(Potree.resourcePath+'/textures/routePoint_panorama.png' );
- map.anisotropy = 4; // 各向异性过滤 .防止倾斜模糊
- this.arrow = new Mesh(planeGeo$3, new MeshBasicMaterial({
- transparent:true,
- depthTest:false,
- map
- }));
- this.arrow.scale.set(arrowSize,arrowSize,arrowSize);
- viewer.setObjectLayers(this.arrow, 'sceneObjects' );
-
-
- /* this.testArrow = this.arrow.clone();
- this.testArrow.material = this.arrow.material.clone()
- this.testArrow.material.color = 'red' */
-
- this.arrows = new Object3D;
- this.sceneMeshGroup.add(this.arrows);
-
- viewer.setObjectLayers(this.sceneMeshGroup, 'sceneObjects' );
- //this.sceneMeshGroup.traverse(e=>e.renderOrder = 90)
-
-
- viewer.scene.scene.add(this.sceneMeshGroup);
- this.sceneMeshGroup.visible = /* this.poleStart.visibile = this.poleEnd.visibile = */ false;
-
- //-------------map---------------------
-
- /* this.mapMarkStart = new THREE.Mesh( planeGeo, new THREE.MeshBasicMaterial({
- transparent:true, depthTest:false,
- map: texLoader.load(Potree.resourcePath+'/textures/map_instruction_start_route.png' )
- }))
- this.mapMarkEnd = new THREE.Mesh( planeGeo, new THREE.MeshBasicMaterial({
- transparent:true, depthTest:false,
- map: texLoader.load(Potree.resourcePath+'/textures/map_instruction_target_reached.png' )
- }))
- this.mapMarkStart.renderOrder = this.mapMarkEnd.renderOrder = 2//在箭头之上 */
-
- let map2 = texLoader$a.load(Potree.resourcePath+'/textures/routePoint_map_fsna.png' );
- this.mapArrowMats = {
- default: new MeshBasicMaterial({
- transparent:true, depthTest:false,
- map:map2,
- }),
-
- fade: new MeshBasicMaterial({
- transparent:true, depthTest:false,
- map:map2,
- opacity:0.4
- }),
- };
-
-
-
- this.mapArrow = new Mesh( planeGeo$3, this.mapArrowMats.default);
- this.mapArrow.scale.set(arrowSize,arrowSize,arrowSize);
- this.mapArrows = new Object3D;
- this.mapArrows.name = 'mapArrows';
-
-
-
- this.mapMeshGroup.add(this.mapArrows);
- this.mapMeshGroup.name = 'mapRouteLayer';
- this.mapMeshGroup.visible = false;
-
- viewer.mapViewer.dispatchEvent({type:'add', object:this.mapMeshGroup, name:'route'});
- this.mapArrow.layers.mask = this.mapArrows.layers.mask; // 修改成和map中的layer一样的
-
-
-
- viewer.modules.SiteModel.bus.addEventListener('FloorChange',()=>{
- if(this.routeStart && this.routeEnd){
- this.updateOpacityAtMap();
- }
- });
- this.inited = true;
- }
-
- updateOpacityAtMap(){//只有当前楼层的透明度为1
- var currentFloor = viewer.modules.SiteModel.currentFloor;
- //console.log('updateOpacityAtMap', currentFloor && currentFloor.name)
- const lift = 0.3; // 因为发送请求时用的是floorPosition的高度,而它可能会到画好的floor之下,所以有误差
- this.mapArrows.children.forEach((arrow,index)=>{
- let pos = this.mapPoints[index].clone();
- pos.z += lift;
- let inSide = currentFloor && currentFloor.ifContainsPoint(pos);
- arrow.material = inSide ? this.mapArrowMats.default : this.mapArrowMats.fade;
- //console.log('arrow',index, arrow.material.opacity)
- });
-
- viewer.mapViewer.dispatchEvent('content_changed');
- }//但是如果楼层刚好只框柱相机位置而没框住地面位置就不好了……
-
-
-
-
-
- createPole(polesMats, name){
- const height = 1.5, sphereCount = 6, shadowSize = sphereSizeInfo.scale, sphereSize = 0.04;
-
- var group = new Object3D;
- group.name = 'pole_'+name;
- var shadow = new Mesh(planeGeo$3,polesMats.shadowMat);
- shadow.scale.set(shadowSize,shadowSize,shadowSize);
- var sliceDis = height / (sphereCount+1);
- group.add(shadow);
-
- for(let i=0;i<sphereCount;i++){
- var sphere = new Sprite$1({mat: polesMats.sphereMat});
- sphere.position.set(0,0,sliceDis*(i+1));
- sphere.scale.set(sphereSize,sphereSize,sphereSize);
- sphere.visible = false;
- group.add(sphere);
- }
-
- var hatSphere = new Sprite$1({mat: polesMats.hatMats[name], sizeInfo:sphereSizeInfo});
- sphere.visible = false;
- hatSphere.position.set(0,0,height);
- hatSphere.scale.copy(shadow.scale);
- group.add(hatSphere);
- return group
- }
-
-
- addTestArrow(){
-
- }
-
- addArrow(position){
- var arrow = this.arrow.clone();
- arrow.position.copy(position);
- this.arrows.add(arrow);
- }
- addMapArrow(position){
- var mapArrow = this.mapArrow.clone();
- mapArrow.position.copy(position).setZ(0);
- this.mapArrows.add(mapArrow);
- }
-
-
- setArrowDir(arrows,index){
- let arrow = arrows[index];
- var nextOne = arrows[index+1];
- var nextPos = nextOne ? nextOne.position : this.endPolePos; //routeEnd
- var direction = new Vector3().subVectors(arrow.position, nextPos).setZ(0);
- //direction.normalize();
- //console.log(direction.toArray())
- var angle = Math.atan2(direction.y, direction.x ) + Math.PI/2; //Math.PI/2是因为贴图本身箭头方向不朝x
- arrow.rotation.z = angle;
- //console.log(angle)
- }
-
-
-
-
-
- setRouteStart(pos, dealZ , datasetId ){
- if(this.routeStart && pos && this.routeStart.equals(pos)) return //可能重复设置
- this.routeStart = pos && new Vector3().copy(pos);
- if(dealZ && this.routeStart){
- this.routeStart.setZ(this.getZAtMap());
- this.bus && this.bus.emit('reposStartMarker', this.routeStart);
- }
- console.log('setRouteStart',this.routeStart&&this.routeStart.toArray());
-
- this.datasetIds[0] = datasetId;
-
- //this.setStartPole(pos)
-
- this.generateRoute();
-
-
- }
-
- setStartPole(pos){
- this.startPolePos = pos;
- this.bus && this.bus.emit('reposStartMarker', pos);
- }
-
-
- setRouteEnd(pos, dealZ , datasetId ){
- if(this.routeEnd && pos && this.routeEnd.equals(pos)) return
- this.routeEnd = pos && new Vector3().copy(pos);
- if(dealZ && this.routeEnd){
- this.routeEnd.setZ(this.getZAtMap());
- this.bus && this.bus.emit('reposEndMarker', this.routeEnd);
- }
- console.log('setRouteEnd',this.routeEnd&&this.routeEnd.toArray());
- this.datasetIds[1] = datasetId;
- //this.setEndPole(pos)
- this.generateRoute();
-
- }
-
-
- getZAtMap(){
-
- //找到position.z与当前高度最接近的漫游点
- let result = Common.sortByScore(viewer.images360.panos,[],[e=> -(Math.abs(e.position.z - viewer.images360.position.z)) ]);
- let pano = result && result[0] && result[0].item;
-
- return pano ? pano.floorPosition.z : viewer.bound.boundingBox.min.z + 1
- //若在平面图上画实在得不到当前楼层的,大概率是楼层画得不好,那就只能去获取当前楼层的了
-
- //navvis的高度取的是主视图所在楼层的中心高度(可能再高些)
-
- }
-
- setEndPole(pos){
- this.endPolePos = pos;
- this.bus && this.bus.emit('reposEndMarker', pos);
- }
-
- getSourceProjectionIndex(route) {//真正的起始
- var e = route.findIndex(function(t) {
- return t.instruction && t.instruction.type === 'source_projection_to_navgraph'
- });
- return e < 0 ? 0 : e
- }
- getDestinationProjectionIndex(route) {//真正的终点
- var e = route.findIndex(function(t) {
- return t.instruction && t.instruction.type === "destination_projection_to_navgraph"
- });
- return e < 0 ? route.length - 1 : e
- }
-
- generateRoute(){
- if(!this.routeStart || !this.routeEnd){
-
- return
- }
-
-
- //array.reduce(function(total, currentValue, currentIndex, arr), initialValue)
-
-
- let create = ()=>{
- this.routeLength = this.route.reduce((total, currentValue, currentIndex, arr)=>{
- if(currentIndex == 0)return 0
- return total + currentValue.distanceTo(arr[currentIndex-1]);
- },0);
- let count = Math.max(2,Math.round(this.routeLength / arrowSpacing));//点数
-
- const curve = new CatmullRomCurve3( this.route );
- curve.curveType = 'chordal';//'centripetal' 'catmullrom'这个可能会超出路径外
- this.curve = curve;
-
- const scenePoints = curve.getSpacedPoints( count );//更平均
- //const scenePoints = curve.getPoints( count );
- scenePoints.splice(0,1);//去掉首尾
- scenePoints.pop();
- this.scenePoints = scenePoints;
-
- this.updateMapArrows();
- this.displayRoute();
-
- {//map focus on this area
-
- const minBound = new Vector2$1(1,1);//针对垂直线,在地图上只有一个点
- let bound = new Box2;
- this.route.forEach(e=>{
- bound.expandByPoint(e);
- });
- let size = bound.getSize(new Vector2$1);
- let markerSize = new Vector2$1(115,40); //起始和终点的标识呈长方形
- let areaSize = viewer.mapViewer.viewports[0].resolution2;
- let areaArea = areaSize.x * areaSize.y;
- if(areaArea> 800 * 400){//是放大的
- markerSize.multiplyScalar(areaArea / (800 * 400) /* / (size.x * size.y) */);
- }
- let margin = size.clone().divide(viewer.mapViewer.viewports[0].resolution2).multiply(markerSize); ///边距 重点是起始和终点的标识占据较大
- size.add(margin);
- let center = bound.getCenter(new Vector2$1);
-
- size.x = Math.max(size.x, minBound.x );
- size.y = Math.max(size.y, minBound.y );
- let duration = 1000;
- viewer.mapViewer.moveTo(center, size, duration);
- }
-
- this.bus.emit('gotResult', {dis:this.routeLength});
- /* this.generateDeferred && this.generateDeferred.resolve({dis:this.routeLength})
- this.generateDeferred = null */
- };
-
-
- if(Potree.fileServer){
- let dealData = (data)=>{
-
- if(!data.data){
- console.log('没有数据');
- let result;
- if(data && data.code == 4002){
- result = data;//正被修改数据集
- }else if(this.routeStart.distanceTo(this.routeEnd) < 1){
- result = { code: 500, msg: '距离太短,无法规划路线' };
- }else {
- result = { code: 500, msg: '超出数据集范围,无法规划路线' };
- }
- this.clearRoute();
- this.setStartPole(this.routeStart);
- this.setEndPole(this.routeEnd);
-
- this.displayRoute(); //还是要显示一下起始
- this.bus && this.bus.emit('gotResult', result );
-
- return //this.generateDeferred && this.generateDeferred.resolve()
- }
-
-
- data = data.data;
-
- this.clearRoute();
- let length = data.length;
-
- if(length < 2){//可能距离太短
- console.log('路径点数为'+length+',直接取起点和终点连线');
- this.route = [this.routeStart, this.routeEnd];
- }else {
- let startIndex = this.getSourceProjectionIndex(data);
- let endIndex = this.getDestinationProjectionIndex(data);
-
-
- let effectiveItems = data.slice(startIndex, endIndex + 1 );//只要点云范围内的点
- effectiveItems.forEach((item,i)=>{
- let pos = viewer.transform.lonlatToLocal.forward(item.location.slice(0));
- pos = new Vector3().fromArray(pos);//.setZ(item.z)
- this.route.push(pos);
- });
-
- console.log(this.route);
-
-
- }
- this.setStartPole(this.route[0]);
- this.setEndPole(this.route[this.route.length-1]);
-
- create();
- /*
- distance: 0.17581000000000116
- distance_to_previous: 0.17581000000000116
- id: 567
- instruction: {type: 'source_projection_to_navgraph'}
- latitude: 22.366605927999238
- location: (3) [113.5957510575092, 22.366605927999238, -1.12419]
- longitude: 113.5957510575092
- z: -1.12419
- */
- };
-
-
-
-
- if(this.lastResult && (this.lastResult.data || this.lastResult.data.code != 4002)){//正被修改数据集的话要重新计算
- let data = Common.CloneObject(this.lastResult.data) , use; //直接用上次的结果
- if(this.lastResult.routeStart.equals(this.routeStart) && this.lastResult.routeEnd.equals(this.routeEnd)){//和上次请求相同
- use = true;
- }else if(this.lastResult.routeStart.equals(this.routeEnd) && this.lastResult.routeEnd.equals(this.routeStart)){//..反向
- use = true;
- if(data.data){
- data.data = this.lastResult.data.data.slice(0).reverse();
- }
- }
- if(use){
- console.log('直接用上次的结果');
- return setTimeout(()=>{dealData(data);}, 1)//延迟是为了等待获得 RouteGuider.generateDeferred
-
- }
-
- }
-
-
-
-
- let start = this.routeStart.clone();
- let end = this.routeEnd.clone();
- let startLonlat = viewer.transform.lonlatToLocal.inverse(start);
- let endLonlat = viewer.transform.lonlatToLocal.inverse(end);
-
- var query = {
- source_longitude: startLonlat.x,
- source_latitude: startLonlat.y,
- source_z: start.z,
- destination_longitude: endLonlat.x,
- destination_latitude: endLonlat.y,
- destination_z: end.z
- };
-
-
- //let url = `/laser/route/${Potree.settings.number}/getRoute/${this.datasetIds[0]}/${this.datasetIds[1]}?`
- let url = `/laser/route/${Potree.settings.number}/getRoute/${Potree.settings.originDatasetId}?`;
- for(let i in query){
- url+= (i + '='+ query[i] +'&');
- }
-
- Potree.fileServer.get(url).then((data)=>{
- console.log(data.data);
- if(!this.routeStart || !this.routeEnd)return
-
- this.lastResult = {//保存数据
- routeStart : this.routeStart.clone(),
- routeEnd: this.routeEnd.clone(),
- data,
-
- };
-
- dealData(data);
-
- });
-
-
- }else {
- //创个直线
- /* const sliceDis = 1
- let dis = this.routeStart.distanceTo(this.routeEnd);
- let count = Math.max(2,Math.round(dis / sliceDis))//点数
- let realSlideDis = dis / (count-1);
- let dir = new THREE.Vector3().subVectors(this.routeEnd, this.routeStart).normalize().multiplyScalar(realSlideDis);
- this.route = [this.routeStart];
- for(let i=0;i<count-1;i++){
- let lastOne = this.route[i];
- this.route.push(new THREE.Vector3().addVectors(lastOne,dir))
- }
- this.route.splice(0,1) //route不用包含收尾 */
- this.clearRoute();
- this.route = [this.routeStart, this.routeEnd];
- create();
-
- }
-
- }
-
- updateMapArrows(ifReset){
- if(this.route.length == 0)return
- var zoom = viewer.mapViewer.camera.zoom;
- let count = Math.max(2,Math.round(this.routeLength * zoom / arrowSpacing / 25));//点数
-
- if(count == this.mapPoints.length+1)return//没变
- const mapPoints = this.curve.getSpacedPoints( count );
- mapPoints.splice(0,1);//去掉首尾
- mapPoints.pop();
- this.mapPoints = mapPoints;
-
-
- var scale = 25/zoom;
- this.mapArrow.scale.set(scale*0.6,scale*0.6,scale*0.6);
- /* this.mapMarkStart.scale.set(scale,scale,scale)
- this.mapMarkEnd.scale.set(scale,scale,scale) */
-
-
- if(ifReset){//因为缩放而重新排布箭头
- this.clearRoute({resetMap:true});
- this.displayRoute({resetMap:true});
- }
- this.updateOpacityAtMap();
- }
-
-
- updateArrowDisplay(){//根据当前位置更新显示一定范围内的箭头'
-
- if(this.scenePoints.length == 0)return
-
- /* var a = Common.sortByScore(this.scenePoints , null, [(point)=>{ //是否还要再requires里限制最远距离?
- var playerPos = viewer.scene.getActiveCamera().position.clone().setZ(0)
-
- var pos = point.clone().setZ(0)
-
- return -pos.distanceTo(playerPos);
-
- }]);
- //获得展示的起始点
- let start = a[0].item
- let startIndex = this.scenePoints.indexOf(start)
- this.arrows.children.forEach((e,i)=>{
- if(i<startIndex || i>startIndex+arrowsShowingCount)e.visible = false
- else e.visible = true
- }) */
-
- let cameraPos = viewer.scene.getActiveCamera().position;
- this.arrows.children.forEach((e,i)=>{
- if(e.position.distanceTo(cameraPos) < arrowShowMinDis) e.visible = true;
- else e.visible = false;
- });
-
-
- }
-
-
- displayRoute(o={}){
- if(!o.resetMap){
-
- this.poleStart.position.copy(this.startPolePos || this.routeStart);
- this.poleEnd.position.copy(this.endPolePos || this.routeEnd);
- /* this.mapMarkStart.position.copy(this.routeStart).setZ(0)
- this.mapMarkEnd.position.copy(this.routeEnd).setZ(0) */
- this.scenePoints.forEach(e=>this.addArrow(e));
- this.arrows.children.forEach((e,i)=>this.setArrowDir(this.arrows.children,i));
- }
- this.sceneMeshGroup.traverse(e=>e.visible = true);
- this.mapMeshGroup.visible = true;
- this.mapPoints.forEach(e=>this.addMapArrow(e));
- this.mapArrows.children.forEach((e,i)=>this.setArrowDir(this.mapArrows.children,i));
- viewer.mapViewer.dispatchEvent({'type':'content_changed'});
- this.updateArrowDisplay();
- }
-
- clearRoute(o={}){
- if(!o.resetMap){
- this.routeLength = 0;
- this.route = [];
- this.scenePoints = [];
- this.mapPoints = [];
- let arrows = this.arrows.children.slice(0);
- arrows.forEach(e=>{
- this.arrows.remove(e);
- });
- }
-
- let mapArrows = this.mapArrows.children.slice(0);
- mapArrows.forEach(e=>{
- this.mapArrows.remove(e);
- });
-
- this.sceneMeshGroup.traverse(e=>e.visible = false); //包括sprite也要设置,防止update
- this.mapMeshGroup.visible = false;
- viewer.mapViewer.dispatchEvent({'type':'content_changed'});
- }
-
- clear(){//退出
- console.log('导航clear');
- this.routeStart = null;
- this.routeEnd = null;
- this.clearRoute();
-
- }
- }
- //大概每十米要花一秒
- /*
- 存在的问题:
- 路径不准确。起始点和终点偏移。
- */
- const vertexShader = `
- attribute float randam;
- attribute float sprite;
- attribute float centerHeight; //add
-
- //uniform float fireHeight; //add
- uniform float time;
- uniform float size;
- uniform float heightOfNearPlane;
-
-
-
-
- //varying float heightRatio;
- varying float vSprite;
- varying float vOpacity;
- float PI = 3.14;
- float quadraticIn( float t )
- {
- float tt = t * t;
- return tt * tt;
- //变化曲线 越来越快
- }
-
- void main() {
- float progress = fract( time + ( 2.0 * randam - 1.0 ) );
- float progressNeg = 1.0 - progress;
- float ease = quadraticIn( progress );
- float influence = sin( PI * ease );
- //vec3 newPosition = position * vec3( 1.0, 1.0 , ease);
- vec3 newPosition = position;
- newPosition.z = (newPosition.z - centerHeight) * ease + centerHeight;
-
- gl_Position = projectionMatrix * modelViewMatrix * vec4( newPosition, 1.0 );
- gl_PointSize = ( heightOfNearPlane * size ) / gl_Position.w;
- vOpacity = min( influence * 4.0, 1.0 ) * progressNeg;
- vSprite = sprite;
-
- //heightRatio = (newPosition.z - centerHeight) / fireHeight ;
-
- }
- `;
- const fragmentShader = `
- uniform vec3 color;
- uniform sampler2D u_sampler;
- varying float vSprite;
- varying float vOpacity;
- //varying float heightRatio;
- void main()
- {
-
-
- vec2 texCoord = vec2(gl_PointCoord.x * 0.25 + vSprite, gl_PointCoord.y);
-
- gl_FragColor = vec4( texture2D( u_sampler, texCoord ).xyz * color * vOpacity, 1.0 );
-
-
- }
- `;
- // import { vertexShader, fragmentShader } from './shaders'
- let ONE_SPRITE_ROW_LENGTH = 0.25; //对应着色器的0.25
- let texture;
-
- const getTexture = ()=>{
- if(!texture){
- texture = new TextureLoader().load( Potree.resourcePath+'/textures/fire.png');
- }
- return texture
- };
- const boxGeo = new BoxBufferGeometry(1,1,1,1);
- const boxMat = new MeshBasicMaterial({wireframe:true, color:"#ffffff"});
- class FireParticle extends Points{
-
- constructor (prop) {
- super();
-
- for ( var key in prop ){
- this[key] = prop[key];
- }
-
-
-
- this.strength = this.strength || 1;
-
-
-
- this.radius = prop.radius || 1;
- this.height = prop.height || 5;
-
- this.computeParams();
- this.geometry = this.createGeometry( this.radius, this.height, this.particleCount );
-
-
- if(this.color == void 0) this.color = 0xff3200;
- this.createMaterial( ); //小蓝火:0x00338f
-
-
-
- //---?:
- this.velocity = new Vector3();
- this.acceleration = new Vector3();
-
- this.angle = 0;
- this.angleVelocity = 0;
- this.angleAcceleration = 0;
- this.size = 16;
-
- this.opacity = 1;
- this.age = 0;
- this.alive = 0;
- this.sizeTween = null;
- this.colorTween = null;
- this.opacityTween = null;
-
-
- this.setSize({viewport:viewer.mainViewport});
- this.setFov(viewer.fov);
-
- let setSize = (e)=>{
- if(e.viewport.name != "MainView")return
- this.setSize(e);
- };
- let setFov = (e)=>{
- this.setFov(e.fov);
- };
-
- viewer.addEventListener('resize',setSize);
- viewer.addEventListener('fov_changed',setFov);
-
- this.addEventListener('dispose',()=>{
- viewer.removeEventListener('resize',setSize);
- viewer.removeEventListener('fov_changed',setFov);
- });
-
- }
-
- computeParams(){
- let length = (this.curve ? this.curve.wholeLength : 0) + this.radius * 2; //加上首尾的半径
-
-
- const minSize = 0.3, maxSize = 3, minRadiusBound = 0.3, maxRadiusBound = 10;
- this.size = minSize + (maxSize - minSize) * MathUtils.smoothstep(this.radius, minRadiusBound, maxRadiusBound);
- //console.log('fire material particle size:', size )
-
- this.particleCount = Math.ceil( length * Math.sqrt(this.strength * this.height ) * this.radius / (this.size * this.size) * 25 );
- //console.log('fire particleCount',this.particleCount)
- }
- getPointsForBound(){
- return this.boundPoints; //可以用于expand实时bound的点, 不含particle的size等边距
- }
- getBound(points){ // points为生成点(圆心)
- this.boundPoints = [];
- let boundingBox = new Box3();
-
-
- let margin = this.size * 0.13 + 0.3;
-
- points.forEach(bottom=>{
- let top = bottom.clone();
- top.z += this.height;
- boundingBox.expandByPoint(bottom);
- boundingBox.expandByPoint(top);
- this.boundPoints.push(bottom,top);
- });
- let xyExpand = this.radius+margin;
- boundingBox.expandByVector(new Vector3(xyExpand,xyExpand,margin));
- this.boundingBox = boundingBox;
-
- /* if(!this.debugBox){
- this.debugBox = new THREE.Mesh(boxGeo, boxMat)
- this.add(this.debugBox)
- }
-
- this.debugBox.scale.copy(boundingBox.getSize(new THREE.Vector3))
- this.debugBox.position.copy(boundingBox.getCenter(new THREE.Vector3)) */
-
- }
- createGeometry( radius, height, particleCount){
- let geometry = new BufferGeometry();
-
-
- let count , points;
- if(this.positions.length>1){
-
- const spaceDis = 0.2;//间隔距离
-
- count = Math.ceil(this.curve.wholeLength / spaceDis) + 1;
- //console.log('count', count)
- points = this.curve.getSpacedPoints( count ); //得到的数量会比count多一个
- count = points.length;
- //得到的点不太均匀,两端容易点少。
- this.getBound(points);
-
- }else {
- this.getBound(this.positions);
- }
-
-
- var position = new Float32Array(particleCount * 3);
- var randam = new Float32Array(particleCount);
- var sprite = new Float32Array(particleCount);
- var centerHeight = new Float32Array(particleCount);
-
- for (var i = 0; i < particleCount; i++) {
-
- var center = new Vector3().copy(this.positions.length>1 ? points[Math.floor(i/particleCount * count)] : this.positions[0]);
- centerHeight[i] = center.z;
-
- if (i === 0) {
- // to avoid going out of Frustum
- position[i * 3 + 0] = center.x;
- position[i * 3 + 1] = center.y;
- position[i * 3 + 2] = center.z;
- }else {
- var r = Math.sqrt(Math.random()) * radius;
- var angle = Math.random() * 2 * Math.PI;
- position[i * 3 + 0] = center.x + Math.cos(angle) * r;
- position[i * 3 + 1] = center.y + Math.sin(angle) * r;
- position[i * 3 + 2] = center.z + (radius - r) / radius * height/2 + height/2; //不太明白这句为什么能达到height高度
-
- sprite[i] = 0.25 * (Math.random() * 4 | 0);
- randam[i] = Math.random();
- //center在底部
- }
-
-
- }
-
- geometry.setAttribute('centerHeight', new BufferAttribute(centerHeight, 1));
- geometry.setAttribute('position', new BufferAttribute(position, 3));
- geometry.setAttribute('randam', new BufferAttribute(randam, 1));
- geometry.setAttribute('sprite', new BufferAttribute(sprite, 1));
- return geometry;
- }
-
-
-
-
- updateGeometry(){
- this.computeParams();
- this.geometry.dispose();
- this.geometry = this.createGeometry( this.radius, this.height, this.particleCount );
- this.material.uniforms.size.value = this.size;
- }
- createMaterial(){
-
-
- const material = new ShaderMaterial( {
- uniforms:{
- color: { type: "c", value: new Color(this.color) },
- size: { type: "f", value: this.size},
- u_sampler: { type: "t", value: getTexture() },
- time: { type: "f", value: 0.0 },
- heightOfNearPlane: { type: "f", value:0}, //相对far ,以确保画面缩放时点的大小也会缩放
- height :{ type: "f", value:this.height} ,
- },
- vertexShader,
- fragmentShader,
- blending: AdditiveBlending, //加法融合模式 glBlendFunc(GL_ONE, GL_ONE)
- depthTest: true,
- depthWrite: false,
- transparent: true
- } );
- this.material = material;
- this.setPerspective(this.fov, this.screenHeight);
- }
- setSize(e){
- let viewport = e.viewport;
- this.screenHeight = viewport.resolution.y;
- this.setPerspective(this.fov, this.screenHeight);
- }
-
- setFov(fov){
- this.fov = fov;
- this.setPerspective(this.fov, this.screenHeight);
- }
-
-
- setPerspective(fov, height){
- //this.uniforms.heightOfNearPlane.value = Math.abs(height / (2 * Math.tan(THREE.Math.degToRad(fov * 0.5))));
- let far = Math.abs(height / (2 * Math.tan(MathUtils.degToRad(fov * 0.5))));
- this.material.uniforms.heightOfNearPlane.value = far;
- }
-
-
- update(delta){
- if(!viewer.getObjVisiByReason(this,'force')){//被手动隐藏了
- return
- }
- if(!Potree.Utils.isInsideFrustum(this.boundingBox, viewer.scene.getActiveCamera())){
- viewer.updateVisible(this,'isInsideFrustum', false ); //不在视野范围
- //console.log('unvi')
- return
- }else {
- viewer.updateVisible(this,'isInsideFrustum', true );
- }
- delta *= 1;//更改速度
-
- this.material.uniforms.time.value = (this.material.uniforms.time.value + delta) % 1;
- }
-
- dispose(){
- this.geometry.dispose();
- this.material.dispose();
- this.dispatchEvent('dispose');
- }
- }
- class Tween {
- constructor(times, values) {
- this.times = times || [];
- this.values = values || [];
- }
-
- lerp(t) {
- if(this.times.length == 0) return
- let i = 0, n = this.times.length;
- while(i < n && t > this.times[i]) i++;
- if(i == 0) return this.values[0]
- if(i == n) return this.values[n-1]
- const ratio = (t - this.times[i-1]) / (this.times[i] - this.times[i-1]);
- if(this.values[0] instanceof Vector3) {
- return this.values[i-1].clone().lerp(this.values[i], ratio)
- } else {
- return this.values[i-1] + ratio * (this.values[i] - this.values[i-1])
- }
-
- }
-
-
- clone () {
- return Common.CloneClassObject(this)
- }
-
-
- }
- class Particle$1{
- constructor(prop={}){
- this.position = new Vector3();
- this.velocity = new Vector3(); // units per second
-
- this.angle = 0;
- this.angleVelocity = 0; // degrees per second
- this.angleAcceleration = 0; // degrees per second, per second
- this.size = 16.0;
-
- this.color = new Color();
- this.opacity = 1.0;
-
- this.age = 0;
- this.alive = 0; // use float instead of boolean for shader purposes
- this.lastChangeVage = 0; //add
- this.sizeTween = prop.sizeTween || new Tween( [0, 1], [32, 128] );
- this.opacityTween = prop.opacityTween || new Tween( [0.8, 2], [0.5, 0] );
- this.colorTween = prop.colorTween || new Tween( [0.4, 1], [ new Vector3(0,0,0.2), new Vector3(0, 0, 0.5) ] );
-
-
-
- }
- update(dt)
- {
- this.position.add(this.velocity.clone().multiplyScalar(dt));
- this.velocity.multiplyScalar( 1+this.acceleration*dt );
-
- // convert from degrees to radians: 0.01745329251 = Math.PI/180
- this.angle += this.angleVelocity * 0.01745329251 * dt;
- this.angleVelocity += this.angleAcceleration * 0.01745329251 * dt;
- this.age += dt;
-
- // if the tween for a given attribute is nonempty,
- // then use it to update the attribute's value
- if ( this.sizeTween.times.length > 0 )
- this.size = this.sizeTween.lerp( this.age/this.deathAge );
-
- if ( this.colorTween.times.length > 0 )
- {
- var colorHSL = this.colorTween.lerp( this.age/this.deathAge );
- this.color = new Color().setHSL( colorHSL.x, colorHSL.y, colorHSL.z );
- }
-
- if ( this.opacityTween.times.length > 0 )
- {
- this.opacity = this.opacityTween.lerp( this.age/this.deathAge);
- }
- }
- }
- const vertexShader$1 = `
- attribute vec3 customColor;
- attribute float customOpacity;
- attribute float customSize;
- attribute float customAngle;
- attribute float customVisible;
- uniform float heightOfNearPlane;
-
-
- varying vec4 vColor;
- varying float vAngle;
- void main()
- {
- if ( customVisible > 0.5 )
- vColor = vec4( customColor, customOpacity );
- else
- vColor = vec4(0.0, 0.0, 0.0, 0.0);
-
- vAngle = customAngle;
- vec4 mvPosition = modelViewMatrix * vec4( position, 1.0 );
- //gl_PointSize = customSize * ( 300.0 / length( mvPosition.xyz ) );
- gl_Position = projectionMatrix * mvPosition;
- gl_PointSize = ( heightOfNearPlane * customSize ) / gl_Position.w;
-
-
- }
- `;
- const fragmentShader$1 = `
- uniform sampler2D u_sampler;
- varying vec4 vColor;
- varying float vAngle;
- void main()
- {
- gl_FragColor = vColor;
-
- float c = cos(vAngle);
- float s = sin(vAngle);
- vec2 rotatedUV = vec2(c * (gl_PointCoord.x - 0.5) + s * (gl_PointCoord.y - 0.5) + 0.5, c * (gl_PointCoord.y - 0.5) - s * (gl_PointCoord.x - 0.5) + 0.5);
- vec4 rotatedTexture = texture2D( u_sampler, rotatedUV );
- gl_FragColor = gl_FragColor * rotatedTexture;
- }
- `;
- const Type = Object.freeze({ "CUBE":1, "SPHERE":2 });
- let particleTexture;
- const getTexture$1 = ()=>{
- if(!particleTexture){
- particleTexture = new TextureLoader().load( Potree.resourcePath+'/textures/smokeparticle.png');
- }
- return particleTexture
- };
- const boxGeo$1 = new BoxBufferGeometry(1,1,1,1);
- const boxMat$1 = new MeshBasicMaterial({wireframe:true, color:"#ffffff"});
- const defaults =
- {
- positions: [],
- positionStyle : "sphere",
- positionBase : new Vector3( 0, 0, 0 ),
-
- positionSpread : new Vector3( 1, 1, 0), //cube
-
- radius : 1, // sphere
-
- velocityStyle : 'cube',
-
- velocityBase : new Vector3( 0, 0, 0.5), // cube 基础速度
- velocitySpread : new Vector3( 1, 1, 0.3),
-
- accelerationBase : 0.3, //基础加速度
- accelerationSpread : 0.6,
-
- //没使用
- speedBase : 0.1, //sphere
- speedSpread : 0.5,
-
-
- angleBase : 0,
- angleSpread : 360,
- angleVelocityBase : 1,
- angleVelocitySpread : 30,
- angleAccelerationBase : 1,
- angleAccelerationSpread : 5,
-
- sizeBase : 0,
- sizeSpread : 0,
- sizeTween : [[0, 0.3, 1], [0.3, 1.4, 6 ]],
-
-
- colorBase : new Vector3(0.0, 1.0, 0.5),
- colorSpread : new Vector3(0.0, 0.0, 0.0),
- colorTween : new Tween( [0.2, 1], [ new Vector3(0,0,0.4), new Vector3(0, 0, 0.1) ] ),
- opacityBase : 0.1,//1.0,
- opacitySpread : 0.2,
- opacityTween :[ [0, 0.1, 0.9, 1], [0.1, 0.4 , 0.03, 0 ] ],
-
- //particlesPerSecond : 20,
- strength : 1,
- particleDeathAge : 3, //从底下升起后能持续的时间
- //emitterDeathAge : 60 // time (seconds) at which to stop creating particles.
- height : 3,
- };
- const debugSphere = new Mesh(new SphereBufferGeometry(0.03, 5,5), new MeshBasicMaterial({color:'white',depthTest:false}));
- class SmokeParticle extends Points{
- constructor(prop={}) {
- super();
-
-
- this.blendStyle = NormalBlending; // false;
- this.emitterAge = 0.0;
- //this.emitterAlive = true;
-
- prop = $.extend({}, defaults, prop);
- for ( var key in prop ){
- let value = prop[key];
- if(value instanceof Array && value[0] instanceof Array ) this[ key ] = new Tween(...value);
- else if(value instanceof Vector3 || value instanceof Color){
- this[ key ] = value.clone();
- }else {
- this[ key ] = value;
- }
- }
-
- this.defaultSizeTween = this.sizeTween.clone();
- this.defaultOpacityTween = this.opacityTween.clone();
-
-
- this.geometry = new BufferGeometry();
- this.computeParams();
- this.createMaterial();
- this.createGeometry();
-
- this.dynamic = true;
- this.sortParticles = true;
- this.frustumCulled = false;//似乎是禁止相机裁剪,否则会在某些角度消失。但是会不会更耗性能呢?
-
- prop.position && this.position.copy(prop.position);
-
-
-
-
- //---------------------------------------
- this.setSize({viewport:viewer.mainViewport});
- this.setFov(viewer.fov);
-
- let setSize = (e)=>{
- if(e.viewport.name != "MainView")return
- this.setSize(e);
- };
- let setFov = (e)=>{
- this.setFov(e.fov);
- };
- /* let reStart = (e)=>{
- if(e.v){//重新一个个放出粒子,否则会一股脑儿全部出来,因为同时大于粒子周期了一起重新生成出现。
- setTimeout(()=>{//会先update一次delta为pageUnvisile的时间才触发
- //console.log('归零')
- //this.reStart()
- },1)
- }
- } */
- viewer.addEventListener('resize',setSize);
- viewer.addEventListener('fov_changed',setFov);
- //viewer.addEventListener('pageVisible', reStart)
-
- this.addEventListener('dispose',()=>{
- viewer.removeEventListener('resize',setSize);
- viewer.removeEventListener('fov_changed',setFov);
- //viewer.removeEventListener('pageVisible', reStart)
- });
-
- }
-
-
-
- computeParams(){
-
- let length = (this.curve ? this.curve.wholeLength : 0) + this.radius * 2; //加上首尾的半径
- //注意:烟最低高度一米, 0<strength<1
- if(this.positionStyle == 'cube'){
- this.positionSpread.set(this.radius,this.radius,0);
- }
- this.velocityBase.set(0,0, (this.height - 0.5 * this.accelerationBase * this.particleDeathAge * this.particleDeathAge) / this.particleDeathAge );
- //let height = this.velocityBase.z * this.particleDeathAge + 0.5 * this.accelerationBase * this.particleDeathAge * this.particleDeathAge;//s = V0 * t + 0.5 * a * t*t ;
- this.velocityBase.z = Math.max(0,this.velocityBase.z);
- this.particleCount = Math.ceil( length * Math.sqrt(this.strength * this.height * this.radius ) );
- this.particleCount = Math.max(5,this.particleCount);
- {
- const minSize = 1, maxSize = 2, minBound = 0.01, maxBound = 1;
- let size = minSize + (maxSize - minSize) * MathUtils.smoothstep( this.strength, minBound, maxBound);
-
- this.sizeTween.values = this.defaultSizeTween.values.map(e=> e*size);
- }
- {
- const minSize = 1 , maxSize = 1.5, minBound = 0.01, maxBound = 1;
- let opac = minSize + (maxSize - minSize) * MathUtils.smoothstep( this.strength, minBound, maxBound);
-
- this.opacityTween.values = this.defaultOpacityTween.values.map(e=> e*opac );
- }
-
- //console.log('smoke particleCount',this.particleCount)
-
-
-
-
- }
- reStart(){
- this.emitterAge = 0;
- this.createGeometry();
- }
- updateGeometry(){
- this.computeParams();
- this.reStart();
-
- }
-
-
- createParticle(center)
- {
- var particle = new Particle$1({
- sizeTween : this.sizeTween,
- opacityTween : this.opacityTween,
- colorTween : this.colorTween,
- });
- particle.deathAge = this.particleDeathAge;
- particle.center = center;
-
-
- if (this.positionStyle == 'cube')
- particle.position = this.randomVector3( this.positionBase, this.positionSpread );
- if (this.positionStyle == 'sphere')
- {
- /* var z = 2 * Math.random() - 1
- var t = Math.PI * 2 * Math.random();
- var r = Math.sqrt( 1 - z*z ) ;
- var vec3 = new THREE.Vector3( r * Math.cos(t), r * Math.sin(t), z );
- particle.position = new THREE.Vector3().addVectors( this.positionBase, vec3.multiplyScalar( this.radius ) );
- */
- //怎么改半径
- let y = 2 * Math.random() - 1;
- let t = Math.PI * 2 * Math.random();
- let r = Math.sqrt( 1 - y*y ) ; //因为 r*r = 1-y*y = x*x + z*z = r*r(cos^2 + sin^2 );
- let lowDownRatio = 0.2; //压低近平面
- let vec3 = new Vector3( r * Math.cos(t), y, Math.abs(r * Math.sin(t) ) * lowDownRatio);
- particle.position = new Vector3().addVectors( this.positionBase, vec3.multiplyScalar( this.radius ) );
-
-
- }
-
- particle.position.add(center);//add
-
-
-
-
-
- if ( this.velocityStyle == 'cube' )
- {
- particle.velocity = this.randomVector3( this.velocityBase, this.velocitySpread );
- }
- if ( this.velocityStyle == 'sphere' )
- {
- //var direction = particle.position.clone()
- var direction = new Vector3(0,0,1); //烟应该都是向上的
- var speed = this.randomValue( this.speedBase, this.speedSpread );
- particle.velocity = direction.normalize().multiplyScalar( speed );
- }
-
- particle.acceleration = this.randomValue( this.accelerationBase, this.accelerationSpread );
- particle.angle = this.randomValue( this.angleBase, this.angleSpread );
- particle.angleVelocity = this.randomValue( this.angleVelocityBase, this.angleVelocitySpread );
- particle.angleAcceleration = this.randomValue( this.angleAccelerationBase, this.angleAccelerationSpread );
- particle.size = this.randomValue( this.sizeBase, this.sizeSpread );
- var color = this.randomVector3( this.colorBase, this.colorSpread );
- particle.color = new Color().setHSL( color.x, color.y, color.z );
-
- particle.opacity = this.randomValue( this.opacityBase, this.opacitySpread );
- particle.age = 0;
- particle.alive = 0; // particles initialize as inactive
- return particle;
- }
- getPointsForBound(){
- return this.boundPoints; //可以用于expand实时bound的点, 不含particle的size等边距
- }
- getBound(points){ // points为生成点(圆心)
- this.boundPoints = [];
- let boundingBox = new Box3();
-
-
- let maxSize = this.sizeTween.values.slice().sort((a,b)=>b-a)[0];
- let margin0 = maxSize * 0.11;
- let margin1 = margin0 + 0.5 ;//保守估计还会飘出这么多距离吧: size + 飘动
-
-
- points.forEach(bottom=>{
- let top = bottom.clone();
- top.z += this.height;
- boundingBox.expandByPoint(bottom);
- boundingBox.expandByPoint(top);
- this.boundPoints.push(bottom,top);
- });
- let xyExpand = this.radius+margin1;
- boundingBox.expandByVector(new Vector3(xyExpand,xyExpand,0));
- boundingBox.min.z -= margin0;
- boundingBox.max.z += margin1;
-
-
-
- this.boundingBox = boundingBox;
-
- /* if(!this.debugBox){
- this.debugBox = new THREE.Mesh(boxGeo, boxMat)
- this.add(this.debugBox)
- }
-
- this.debugBox.scale.copy(boundingBox.getSize(new THREE.Vector3))
- this.debugBox.position.copy(boundingBox.getCenter(new THREE.Vector3)) */
-
- }
- createGeometry(){
- this.particleArray = [];
- const positions = [];
- const colors = [];
- const alives = [];
- const opacitys = [];
- const sizes = [];
- const angles = [];
-
- let count, points;
- if(this.positions.length>1){
-
- const spaceDis = 0.6;//间隔距离
-
- count = Math.ceil(this.curve.wholeLength / spaceDis) + 1;
-
- points = this.curve.getSpacedPoints( count );
-
- count = points.length;
-
- /* points.forEach(e=> {
- var sphere = debugSphere.clone();
- sphere.position.copy(e)
- viewer.scene.scene.add(sphere)
- }) */
- let haventGetPoints = points.slice();
- var getRanPoints = function(i){
- var a = Math.random();
- let choseIndex = Math.floor(haventGetPoints.length * a);
- var point = haventGetPoints[choseIndex];
- if(haventGetPoints.length == 1){
- haventGetPoints = points.slice();
- }else {
- haventGetPoints.splice(choseIndex, 1);
- }
- return point
- };
-
-
- this.getBound(points);
- }else {
- this.getBound(this.positions);
- }
-
-
-
-
-
- for (var i = 0; i < this.particleCount; i++)
- {
- var center = new Vector3().copy(this.positions.length>1 ? getRanPoints(i) : this.positions[0]);
-
- //var center = new THREE.Vector3().copy(this.positions.length>1 ? points[Math.floor(i/this.particleCount * count)] : this.positions[0])
-
-
- // remove duplicate code somehow, here and in update function below.
- this.particleArray[i] = this.createParticle(center);
- positions[3*i] = this.particleArray[i].position.x;
- positions[3*i+1] = this.particleArray[i].position.y;
- positions[3*i+2] = this.particleArray[i].position.z;
- colors[3*i] = this.particleArray[i].color.r;
- colors[3*i+1] = this.particleArray[i].color.g;
- colors[3*i+2] = this.particleArray[i].color.b;
- alives[i] = this.particleArray[i].alive;
- opacitys[i] = this.particleArray[i].opacity;
- sizes[i] = this.particleArray[i].size;
- angles[i] = this.particleArray[i].angle;
- }
- this.geometry.setAttribute( 'position', new BufferAttribute( new Float32Array(positions), 3 ));
- this.geometry.setAttribute( 'customColor', new BufferAttribute( new Float32Array(colors), 3 ) );
- this.geometry.setAttribute( 'customVisible', new BufferAttribute( new Float32Array(alives), 1 ) );
- this.geometry.setAttribute( 'customOpacity', new BufferAttribute( new Float32Array(opacitys), 1 ) );
- this.geometry.setAttribute( 'customSize', new BufferAttribute( new Float32Array(sizes), 1 ) );
- this.geometry.setAttribute( 'customAngle', new BufferAttribute( new Float32Array(angles), 1 ) );
- }
-
- createMaterial(){
- this.material = new ShaderMaterial(
- {
- uniforms:
- {
- u_sampler: { type: "t", value: getTexture$1() },
- heightOfNearPlane: { type: "f", value:0} //相对far ,以确保画面缩放时点的大小也会缩放
- },
- vertexShader: vertexShader$1,vertexShader: vertexShader$1,
- fragmentShader: fragmentShader$1,
- transparent: true,
- alphaTest: 0.5, // if having transparency issues, try including: alphaTest: 0.5,
- blending: this.blendStyle,
- depthTest: this.blendStyle != NormalBlending
- });
-
-
- this.setPerspective(this.fov, this.screenHeight);
-
-
- }
-
- update(dt){
- if(!viewer.getObjVisiByReason(this,'force')){//被手动隐藏了
- return
- }
- if(!Potree.Utils.isInsideFrustum(this.boundingBox, viewer.scene.getActiveCamera())){
- viewer.updateVisible(this,'isInsideFrustum', false ); //不在视野范围
- //console.log('unvi')
- return
- }else {
- viewer.updateVisible(this,'isInsideFrustum', true );
- }
-
-
-
- if(dt > 1){
- console.log('update dt>1', dt);
- }
-
- //dt *= 0.5;
-
- const recycleIndices = [];
- const recycleAges = [];
-
-
- const positions = [];
- const colors = [];
- const alives = [];
- const opacitys = [];
- const sizes = [];
- const angles = [];
-
-
-
-
-
-
- for (var i = 0; i < this.particleCount; i++)
- {
- if ( this.particleArray[i].alive )
- {
-
- if ( this.velocityStyle == 'cube' )
- { //一定几率改变下方向
- let ratio = Math.random();
- if(this.particleArray[i].age - this.particleArray[i].lastChangeVage > this.particleDeathAge*ratio ){
-
- this.particleArray[i].velocity = this.randomVector3( this.velocityBase, this.velocitySpread );
-
- this.particleArray[i].lastChangeVage = this.particleArray[i].age;
- }
- }else {
- /* if(this.particleArray[i].age - this.particleArray[i].lastChangeVage > this.particleDeathAge*0.3 ){
- if( Math.random()>0.1){//一定几率改变下方向
- var speed = this.randomValue( this.speedBase, this.speedSpread );
- this.particleArray[i].velocity = this.randomVector3( new THREE.Vector3, new THREE.Vector3(1,1,1) );
- this.particleArray[i].velocity.normalize().multiplyScalar( speed );
- }
- this.particleArray[i].lastChangeVage = this.particleArray[i].age
- } */
-
-
- }
-
-
- this.particleArray[i].update(dt);
- // check if particle should expire
- // could also use: death by size<0 or alpha<0.
- if ( this.particleArray[i].age > this.particleDeathAge )
- {
- this.particleArray[i].alive = 0.0;
- recycleIndices.push(i);
- recycleAges.push((this.particleArray[i].age - this.particleDeathAge)%(this.particleDeathAge ));
- }
-
-
- // update particle properties in shader
- positions[3*i] = this.particleArray[i].position.x;
- positions[3*i+1] = this.particleArray[i].position.y;
- positions[3*i+2] = this.particleArray[i].position.z;
- colors[3*i] = this.particleArray[i].color.r;
- colors[3*i+1] = this.particleArray[i].color.g;
- colors[3*i+2] = this.particleArray[i].color.b;
- alives[i] = this.particleArray[i].alive;
- opacitys[i] = this.particleArray[i].opacity;
- sizes[i] = this.particleArray[i].size;
- angles[i] = this.particleArray[i].angle;
- }
- }
- // check if particle emitter is still running
- //if ( !this.emitterAlive ) return;
- this.geometry.setAttribute( 'position', new BufferAttribute( new Float32Array(positions), 3 ) );
- this.geometry.setAttribute( 'customColor', new BufferAttribute( new Float32Array(colors), 3 ) );
- this.geometry.setAttribute( 'customVisible', new BufferAttribute( new Float32Array(alives), 1 ) );
- this.geometry.setAttribute( 'customOpacity', new BufferAttribute( new Float32Array(opacitys), 1 ) );
- this.geometry.setAttribute( 'customSize', new BufferAttribute( new Float32Array(sizes), 1 ) );
- this.geometry.setAttribute( 'customAngle', new BufferAttribute( new Float32Array(angles), 1 ) );
- this.geometry.attributes.customColor.needsUpdate = true;
- this.geometry.attributes.customVisible.needsUpdate = true;
- this.geometry.attributes.customOpacity.needsUpdate = true;
- this.geometry.attributes.customSize.needsUpdate = true;
- this.geometry.attributes.customAngle.needsUpdate = true;
- // if no particles have died yet, then there are still particles to activate
- if ( this.emitterAge < this.particleDeathAge ) //开始时一个个放出来
- {
-
- let particlesPerSecond = this.particleCount / this.particleDeathAge;
- // determine indices of particles to activate
- var startIndex = Math.round( particlesPerSecond * (this.emitterAge + 0) );
- var endIndex = Math.round( particlesPerSecond * (this.emitterAge + dt) );
- if ( endIndex > this.particleCount )
- endIndex = this.particleCount;
-
- for (var i = startIndex; i < endIndex; i++)
- this.particleArray[i].alive = 1.0;
- }
- // if any particles have died while the emitter is still running, we imediately recycle them
- for (var j = 0; j < recycleIndices.length; j++)
- {
- var i = recycleIndices[j];
- this.particleArray[i] = this.createParticle(this.particleArray[i].center);
- this.particleArray[i].alive = 1.0; // activate right away
- this.particleArray[i].age = recycleAges[j];
- positions[3*i] = this.particleArray[i].position.x;
- positions[3*i+1] = this.particleArray[i].position.y;
- positions[3*i+2] = this.particleArray[i].position.z;
- }
- this.geometry.setAttribute( 'position', new BufferAttribute( new Float32Array(positions), 3 ) );
- this.geometry.attributes.position.needsUpdate = true;
- // stop emitter?
- this.emitterAge += dt;
- //if ( this.emitterAge > this.emitterDeathAge ) this.emitterAlive = false;
- }
- randomValue(base, spread)
- {
- //return base + spread * (Math.random() - 0.5);
- let p = Math.random();
- return base * p + spread * (1-p)
-
- }
- randomVector3(base, spread)
- {
- var rand3 = new Vector3( Math.random() - 0.5, Math.random() - 0.5, Math.random() - 0.5 );
- return new Vector3().addVectors( base, new Vector3().multiplyVectors( spread, rand3 ) );
- }
-
-
-
-
- setSize(e){
- let viewport = e.viewport;
- this.screenHeight = viewport.resolution.y;
- this.setPerspective(this.fov, this.screenHeight);
- }
-
- setFov(fov){
- this.fov = fov;
- this.setPerspective(this.fov, this.screenHeight);
- }
-
-
- setPerspective(fov, height){
- //this.uniforms.heightOfNearPlane.value = Math.abs(height / (2 * Math.tan(THREE.Math.degToRad(fov * 0.5))));
- let far = Math.abs(height / (2 * Math.tan(MathUtils.degToRad(fov * 0.5))));
- this.material.uniforms.heightOfNearPlane.value = far;
- }
-
- dispose(){
- this.geometry.dispose();
- this.material.dispose();
- this.dispatchEvent('dispose');
- }
- }
- /*
- 改进:如果有必要
-
- 根据curve中分成的点,分成多个簇,每个簇掌管该部分的可见性和particle的数量。
- 在camera_changed时根据远近修改每个簇的particle的数量,当然不会大于初始创建的个数。多出的随机隐藏。
- */
- const vertexShader$2 = `
- attribute vec3 color;
- attribute float size;
- attribute float angle;
- attribute float opacity;
- attribute float visible;
- varying vec4 vColor;
- varying float vAngle;
- uniform float heightOfNearPlane;
-
- void main() {
- if(visible > 0.5) {
- vColor = vec4(color, opacity);
- } else {
- vColor = vec4(0.0, 0.0, 0.0, 0.0);
- }
- vAngle = angle;
- vec4 mvPosition = modelViewMatrix * vec4(position, 1.0);
- gl_Position = projectionMatrix * mvPosition;
-
- gl_PointSize = ( heightOfNearPlane * size ) / gl_Position.w;
- }
- `;
- const fragmentShader$2 = `
- uniform sampler2D u_sampler;
- varying vec4 vColor;
- varying float vAngle;
- void main() {
- gl_FragColor = vColor;
- float u = cos(vAngle);
- float v = sin(vAngle);
- vec2 uv = vec2(
- u * (gl_PointCoord.x - 0.5) + v * (gl_PointCoord.y - 0.5) + 0.5,
- u * (gl_PointCoord.y - 0.5) - v * (gl_PointCoord.x - 0.5) + 0.5
- );
- vec4 texture = texture2D(u_sampler, uv);
- gl_FragColor = gl_FragColor * texture;
- }
- `;
- // import { vertexShader, fragmentShader } from './shader'
- const DEG2RAD = Math.PI / 180;
- class Particle$2 {
-
- constructor() {
-
- this.position = new Vector3();
- this.velocity = new Vector3();
-
-
- this.angle = 0;
- this.angleVelocity = 0;
- this.angleAcceleration = 0;
- this.size = 16;
- this.color = new Color();
- this.opacity = 1;
- this.rebornCount = 0;//重生次数
- this.age = 0;
- this.alive = 0; //注意,一开始时是未出生的
- this.deadAge = 0;//已死亡时间
- this.sizeTween = null;
- this.colorTween = null;
- this.opacityTween = null;
- }
- update(dt) {
- //s = s0 + (v0 + at) * t 或 lastS + delta(vt)
-
- this.position.add(this.velocity.clone().multiplyScalar(dt));
- this.velocity.multiplyScalar( 1+this.acceleration*dt );
-
- this.angle += this.angleVelocity * DEG2RAD * dt;
- this.angleVelocity += this.angleAcceleration * DEG2RAD * dt;
- this.age += dt;
- if(this.sizeTween.times.length > 0) {
- this.size = this.sizeTween.lerp(this.age/this.deathAge);
- }
- if(this.colorTween.times.length > 0) {
- const colorHSL = this.colorTween.lerp(this.age/this.deathAge);
- this.color = new Color().setHSL(colorHSL.x, colorHSL.y, colorHSL.z);
- }
- if(this.opacityTween.times.length > 0) {
- this.opacity = this.opacityTween.lerp(this.age/this.deathAge);
- }
- }
- }
- class Util {
- constructor() {}
- randomValue(min, max) {
- //return min + max * (Math.random() - 0.5)
- let p = Math.random();
- return min * p + max * (1-p)
- }
- randomVector3(min, max) {
- const rand3 = new Vector3(Math.random() - 0.5, Math.random() - 0.5, Math.random() - 0.5);
- return new Vector3().addVectors(min, new Vector3().multiplyVectors(max, rand3))
- }
- }
- const util$1 = new Util();
- const Shape$1 = {
- CUBE: 1,
- SPHERE: 2
- };
- let particleTexture$1;
- const getTexture$2 = ()=>{
- if (!particleTexture$1) {
- particleTexture$1 = new TextureLoader().load(Potree.resourcePath + '/textures/explode.png');
- }
- return particleTexture$1
- };
- const sphereGeo$1 = new SphereBufferGeometry(1, 10,4);
- const sphereMat = new MeshBasicMaterial({wireframe:true, color:"#ffffff"});
- const defaults$1 = {
- position: new Vector3(0,0,1),
- positionShape: Shape$1.SPHERE,
- positionRange: new Vector3(1,1,1),
- //cube
- radius: 1.3,
- //sphere
- velocityShape: Shape$1.SPHERE,
- velocity: new Vector3(0,0,2),
- //cube
- velocityRange: new Vector3(0,0,3),
-
- //sphere
- speed: 0.4,
- speedRange: 1,
- size: 0.4,
- sizeRange: 2,
- //sizeTween: new Tween( [0, 0.05, 0.3, 0.45], [0, 1, 3, 0.1] ),
- sizeTween: [[0, 0.04, 0.2, 1],[0.1, 1, 6, 8]] ,
- color: new Vector3(1.0,1.0,1.0),
- colorRange: new Vector3(0,0,0),
- colorTween: new Tween(),
- opacity: 1.0,
- opacityRange: 0.0,
- opacityTween: new Tween([0, 0.06, 0.3, 0.8, 1],[0, 1, 0.3, 0.05, 0]),
- blendMode: AdditiveBlending,
- acceleration: 0.5,
- accelerationRange: 0,
- angle: 0,
- angleRange: 0,
- angleVelocity: 0,
- angleVelocityRange: 0,
- angleAcceleration: 0,
- angleAccelerationRange: 0,
-
- strength:1,
-
- //particlesPerSecond: 8,
- particleDeathAge: 0.7 ,
- recycleTimes : 3 , //每个粒子在一次爆炸后循环次数,循环完毕进入particleSpaceTime,等待下一次爆炸.
- //爆炸时长: particleDeathAge * (recycleTimes+1)
- particleSpaceTime: 3, //间隔
-
-
- };
- class ExplodeParticle extends Points {
- constructor(params) {
- super();
-
- this.age = 0;
- this.alive = true;
- //this.deathAge = 60
- this.loop = true;
- this.blendMode = NormalBlending;
- this.setParameters(params);
- this.createParticles();
- this.frustumCulled = false;//似乎是禁止相机裁剪,否则会在某些角度消失。但是会不会更耗性能呢?
- //------------------------------------
- this.setSize({viewport:viewer.mainViewport});
- this.setFov(viewer.fov);
-
- let setSize = (e)=>{
- if(e.viewport.name != "MainView")return
- this.setSize(e);
- };
- let setFov = (e)=>{
- this.setFov(e.fov);
- };
-
- /* let reStart = (e)=>{
- if(e.v){//重新一个个放出粒子,否则会一股脑儿全部出来,因为同时大于粒子周期了一起重新生成出现。
- setTimeout(()=>{//会先update一次delta为pageUnvisile的时间才触发
- //console.log('归零')
- //this.reStart()
- },1)
- }
- } */
- viewer.addEventListener('resize',setSize);
- viewer.addEventListener('fov_changed',setFov);
- //viewer.addEventListener('pageVisible', reStart)
-
- this.addEventListener('dispose',()=>{
- viewer.removeEventListener('resize',setSize);
- viewer.removeEventListener('fov_changed',setFov);
- //viewer.removeEventListener('pageVisible', reStart)
- });
-
-
- }
-
-
- computeParams(){
- if(this.curve){
- this.position.copy(this.curve.points[0]);
- }
-
-
-
- const minSize = 0.8, maxSize = 10, minRadiusBound = 0.2, maxRadiusBound = 20;
- let size = minSize + (maxSize - minSize) * MathUtils.smoothstep(this.radius*this.strength , minRadiusBound, maxRadiusBound);
-
- this.sizeTween.values = this.defaultSizeTween.values.map(e=> e*size);
-
- this.particleCount = Math.ceil( this.strength * this.radius * 5 /* * this.radius * this.radius */ );
-
- this.speed = defaults$1.speed * this.radius;
- this.speedRange = defaults$1.speedRange * this.radius;
-
- console.log(this.particleCount);
-
- {
- this.boundPoints = [];
- this.boundPoints.push(this.position.clone());
- let maxSize = this.sizeTween.values.slice().sort((a,b)=>b-a)[0];
- let margin = maxSize * 0.35 + 0.5;
- let scale = this.radius+margin;
- let sphere = new Sphere(this.position, scale);//加上防止剪裁
- this.boundingSphere = sphere; //虽然还是会有一些后续移动的会超出
- this.boundingBox = new Box3().setFromCenterAndSize(this.position, new Vector3(scale*2,scale*2,scale*2));
- /* if(!this.debugSphere){
- this.debugSphere = new THREE.Mesh(sphereGeo, sphereMat)
- this.add(this.debugSphere)
- }
- this.debugSphere.scale.set(scale,scale,scale) */
- }
- }
- getPointsForBound(){
- return this.boundPoints; //可以用于expand实时bound的点, 不含particle的size等边距
- }
- reStart(){
- this.age = 0;
-
- this.createParticles();
- }
- setParameters(params) {
-
- params = $.extend({}, defaults$1, params);
-
-
- for (var key in params) {
- let value = params[key];
- if (key == 'position')
- this.position.copy(value);
- else if (value instanceof Array && value[0]instanceof Array){
- this[key] = new Tween(...value);
- }else if(value instanceof Vector3 || value instanceof Color){
- this[ key ] = value.clone();
- }else {
- this[key] = value;
- }
- }
-
-
- this.defaultSizeTween = this.sizeTween.clone();
- //Object.assign(this, params)
- this.particles = [];
- this.age = 0.0;
- this.alive = true;
-
- this.geometry = new BufferGeometry();
- this.computeParams();
- this.material = new ShaderMaterial({
- uniforms: {
- u_sampler: {
- value: this.texture || getTexture$2()
- },
- heightOfNearPlane: {
- type: "f",
- value: 0
- }//相对far ,以确保画面缩放时点的大小也会缩放
- },
- vertexShader: vertexShader$2,
- fragmentShader: fragmentShader$2,
- transparent: true,
- alphaTest: 0.5,
- depthTest: this.blendMode == NormalBlending,
- blending: this.blendMode
- });
-
-
- }
- createParticles() {
- this.particles = [];
- const count = this.particleCount;
- const positionArray = new Float32Array(count * 3);
- const colorArray = new Float32Array(count * 3);
- const sizeArray = new Float32Array(count);
- const angleArray = new Float32Array(count);
- const opacityArray = new Float32Array(count);
- const visibleArray = new Float32Array(count);
- for (let i = 0; i < count; i++) {
- const particle = this.createParticle();
- /* positionArray[i * 3] = particle.position.x
- positionArray[i * 3 + 1] = particle.position.y
- positionArray[i * 3 + 2] = particle.position.z
- colorArray[i * 3] = particle.color.r
- colorArray[i * 3 + 1] = particle.color.g
- colorArray[i * 3 + 2] = particle.color.b
- sizeArray[i] = particle.size
- angleArray[i] = particle.angel
- opacityArray[i] = particle.opacity
- visibleArray[i] = particle.alive */
- this.particles[i] = particle;
- }
- this.geometry.setAttribute('position', new BufferAttribute(positionArray,3));
- this.geometry.setAttribute('color', new BufferAttribute(colorArray,3));
- this.geometry.setAttribute('angle', new BufferAttribute(angleArray,1));
- this.geometry.setAttribute('size', new BufferAttribute(sizeArray,1));
- this.geometry.setAttribute('visible', new BufferAttribute(visibleArray,1));
- this.geometry.setAttribute('opacity', new BufferAttribute(opacityArray,1));
-
- }
-
-
-
-
- createParticle() {
- const particle = new Particle$2();
- particle.sizeTween = this.sizeTween;
- particle.colorTween = this.colorTween;
- particle.opacityTween = this.opacityTween;
- particle.deathAge = this.particleDeathAge;
- if (this.positionShape == Shape$1.CUBE) {
- particle.position = util$1.randomVector3(new Vector3, this.positionRange);
- }
- if (this.positionShape == Shape$1.SPHERE) {
- /* const z = 2 * Math.random() - 1
- const t = Math.PI * 2 * Math.random()
- const r = Math.sqrt(1 - z*z)
- const vec3 = new THREE.Vector3(r * Math.cos(t), r * Math.sin(t), z)
- particle.position = vec3.multiplyScalar(this.radius) */
- const y = 2 * Math.random() - 1;
- const t = Math.PI * 2 * Math.random();
- const r = Math.sqrt(1 - y * y);
- const vec3 = new Vector3(r * Math.cos(t),y,r * Math.sin(t));
- particle.position = vec3.multiplyScalar(this.radius);
- }
- if (this.velocityShape == Shape$1.CUBE) {
- particle.velocity = util$1.randomVector3(this.velocity, this.velocityRange);
- }
- if (this.velocityShape == Shape$1.SPHERE) {
- const direction = new Vector3().addVectors(particle.position, new Vector3(0,0,this.radius*2));//向上升?
- const speed = util$1.randomValue(this.speed, this.speedRange);
- particle.velocity = direction.normalize().multiplyScalar(speed);
- }
- particle.acceleration = util$1.randomValue(this.acceleration, this.accelerationRange);
- particle.angle = util$1.randomValue(this.angle, this.angleRange);
- particle.angleVelocity = util$1.randomValue(this.angleVelocity, this.angleVelocityRange);
- particle.angleAcceleration = util$1.randomValue(this.angleAcceleration, this.angleAccelerationRange);
- particle.size = util$1.randomValue(this.size, this.sizeRange);
- const color = util$1.randomVector3(this.color, this.colorRange);
- particle.color = new Color().setHSL(color.x, color.y, color.z);
- particle.opacity = util$1.randomValue(this.opacity, this.opacityRange);
-
- return particle
- }
- update(dt) {
- if(!viewer.getObjVisiByReason(this,'force')){//被手动隐藏了
- return
- }
- if(this.delayStartTime>0){ // 爆炸延迟
- return this.delayStartTime -= dt
- }
-
-
-
- if(!Potree.Utils.isInsideFrustum(this.boundingSphere, viewer.scene.getActiveCamera())){
- viewer.updateVisible(this,'isInsideFrustum', false ); //不在视野范围
- return
- }else {
- viewer.updateVisible(this,'isInsideFrustum', true );
- }
- //const timeRatio = 0.5
- if(dt > 1){
- console.log('update dt>1', dt);
- }
- //dt *= timeRatio
- let particleDeathAge = this.particleDeathAge;/* * timeRatio */
- let particleSpaceTime = this.particleSpaceTime; /* * timeRatio */
- const recycleIndices = [];
- const recycleAges = [];
- const recycleRebornCount = [];
-
- const positionArray = this.geometry.attributes.position.array;
- const opacityArray = this.geometry.attributes.opacity.array;
- const visibleArray = this.geometry.attributes.visible.array;
- const colorArray = this.geometry.attributes.color.array;
- const angleArray = this.geometry.attributes.angle.array;
- const sizeArray = this.geometry.attributes.size.array;
- for (let i = 0; i < this.particleCount; i++) {
- const particle = this.particles[i];
- if (particle.alive) {
- particle.update(dt);
- if (particle.age > particleDeathAge) {
- particle.alive = 0.0;
- if(particle.rebornCount >= this.recycleTimes){
- particle.deadAge = particle.age - particleDeathAge; //已死亡时间
- }else {//直接循环
- recycleIndices.push(i);
- recycleAges.push(/* ( */particle.age - particleDeathAge/* )%(this.particleDeathAge ) */);
- recycleRebornCount.push(particle.rebornCount+1);
- }
-
- }
- positionArray[i * 3] = particle.position.x;
- positionArray[i * 3 + 1] = particle.position.y;
- positionArray[i * 3 + 2] = particle.position.z;
- colorArray[i * 3] = particle.color.r;
- colorArray[i * 3 + 1] = particle.color.g;
- colorArray[i * 3 + 2] = particle.color.b;
- visibleArray[i] = particle.alive;
- opacityArray[i] = particle.opacity;
- angleArray[i] = particle.angle;
- sizeArray[i] = particle.size;
- }else {
- if(particle.rebornCount >= this.recycleTimes){
- if(particle.age > particleDeathAge) {//其他已经死亡的粒子的时间继续增加
- particle.deadAge += dt;
- }
- }
- }
-
-
-
- if (particle.rebornCount >= this.recycleTimes && particle.age > particleDeathAge) {//已经死亡
- if(particle.deadAge >= particleSpaceTime){//死亡时间超过设定的间隔时间后重启
- recycleIndices.push(i);
- let wholeTime = particleDeathAge * (this.recycleTimes+1) + particleSpaceTime;
- recycleAges.push((particle.deadAge - particleSpaceTime)% wholeTime ); //剩余时间就是重生后的age
- recycleRebornCount.push(0);
- }
- }
-
- }
-
-
-
- this.geometry.attributes.size.needsUpdate = true;
- this.geometry.attributes.color.needsUpdate = true;
- this.geometry.attributes.angle.needsUpdate = true;
- this.geometry.attributes.visible.needsUpdate = true;
- this.geometry.attributes.opacity.needsUpdate = true;
- this.geometry.attributes.position.needsUpdate = true;
- if (!this.alive)
- return
- if (this.age < particleDeathAge) {
- let startIndex = Math.round(this.particleCount * (this.age + 0)/ particleDeathAge);
- let endIndex = Math.round(this.particleCount * (this.age + dt)/ particleDeathAge);
- if (endIndex > this.particleCount) {
- endIndex = this.particleCount;
- }
- for (let i = startIndex; i < endIndex; i++) {
- this.particles[i].alive = 1.0;
- }
- }
- for (let j = 0; j < recycleIndices.length; j++) {
- let i = recycleIndices[j];
-
- this.particles[i] = this.createParticle();
- this.particles[i].alive = 1.0; //出生
- this.particles[i].age = recycleAges[j];
- this.particles[i].rebornCount= recycleRebornCount[j];
- /* if(this.particles[i].age < particleDeathAge){
- positionArray[i * 3] = this.particles[i].position.x
- positionArray[i * 3 + 1] = this.particles[i].position.y
- positionArray[i * 3 + 2] = this.particles[i].position.z
- visibleArray[i] = particle.alive?
- } */
- }
- this.geometry.attributes.position.needsUpdate = true;
- this.age += dt;
- if (this.age > this.deathAge && !this.loop) {
- this.alive = false;
- }
- }
- setSize(e) {
- let viewport = e.viewport;
- this.screenHeight = viewport.resolution.y;
- this.setPerspective(this.fov, this.screenHeight);
- }
- setFov(fov) {
- this.fov = fov;
- this.setPerspective(this.fov, this.screenHeight);
- }
- setPerspective(fov, height) {
- //this.uniforms.heightOfNearPlane.value = Math.abs(height / (2 * Math.tan(THREE.Math.degToRad(fov * 0.5))));
- let far = Math.abs(height / (2 * Math.tan(MathUtils.degToRad(fov * 0.5))));
- this.material.uniforms.heightOfNearPlane.value = far;
- }
- updateGeometry(){
- this.computeParams();
- this.reStart();
- }
- dispose(){
- this.geometry.dispose();
- this.material.dispose();
- this.dispatchEvent('dispose');
- }
- }
- const colors$1 = {
- 'fire+smoke':0xffffff,
- 'smoke': 0xffffff,
- 'explode':0xffffff,
- };
- let depthMatPrefix = {
- clipDistance : 100, occlusionDistance:60, /* 变为backColor距离 */
- maxClipFactor:0.5, backColor:"#777" ,
- useDepth:true, transparent: !0,
- };
- let lineMats$2;
- let getLineMat$1 = function(type){
- if(!lineMats$2){
- lineMats$2 = {
- 'fire+smoke':LineDraw.createFatLineMat($.extend(depthMatPrefix,{
- color: colors$1['fire+smoke'],
- lineWidth: 2
- })),
- 'smoke' :LineDraw.createFatLineMat($.extend(depthMatPrefix,{
- color: colors$1['smoke'],
- lineWidth: 2
- })),
- 'explode' :LineDraw.createFatLineMat($.extend(depthMatPrefix,{
- color: colors$1['explode'],
- lineWidth: 2
- })),
- };
- }
- return lineMats$2[type]
- };
- let handleMats;
- let getHandleMat = function(type){
- if(!handleMats){
- let texLoader = new TextureLoader();
-
- handleMats = {
- "fire+smoke" : new DepthBasicMaterial($.extend(depthMatPrefix,{
- map: texLoader.load(Potree.resourcePath+'/textures/icon-fire.png' ),
- color: colors$1['fire+smoke'],
- })),
- "smoke" : new DepthBasicMaterial($.extend(depthMatPrefix,{
- map: texLoader.load(Potree.resourcePath+'/textures/icon-smoke.png' ),
- color: colors$1['smoke'],
- })),
- "explode" : new DepthBasicMaterial($.extend(depthMatPrefix,{
- map: texLoader.load(Potree.resourcePath+'/textures/icon-explode.png' ),
- color: colors$1['explode'],
- })),
- };
- }
- return handleMats[type]
- };
- let ParticleEditor = {
-
- bus: new EventDispatcher$1,
- particleGroup : new Object3D ,
- curveGroup:new Object3D ,
- init:function(){
- this.particleGroup.name = 'particles';
- viewer.scene.scene.add( this.particleGroup );
-
-
- this.curveGroup.name = 'particles-curves';
- viewer.scene.scene.add( this.curveGroup );
-
-
- },
- addParticle : function(prop={}){
-
-
- let particle;
- if(prop.type == 'fire'){
- particle = new FireParticle(prop);
-
- }else if(prop.type == 'smoke'){
- particle = new SmokeParticle(prop);
-
- }else if(prop.type == 'explode'){
- particle = new ExplodeParticle(prop);
- }
-
- this.particleGroup.add(particle);
-
-
-
- return particle
- }
- ,
- removeParticle(particle){
- //particle.dispatchEvent('delete')
- particle.dispose();
- this.particleGroup.remove(particle);
- particle.curve.dispose();
- }
- ,
- update(delta){
- this.particleGroup.children.forEach(e=>e.update(delta));
- }
- ,
-
- startInsertion(type = 'fire', prop={}){ //viewer.modules.ParticleEditor.startInsertion()
- let deferred = $.Deferred();
- let particles = [];
-
- let finish = (ifDone)=>{
- if(ifDone){
- deferred.resolve(particles);
- }
- viewer.dispatchEvent({
- type : "CursorChange", action : "remove", name:"addSth"
- });
- viewer.removeEventListener('global_click', click);
- this.bus.removeEventListener('cancel_insertions',cancel);
- };
-
- let curve = new CurveCtrl([], getLineMat$1(type), colors$1[type], type+'_curve', {handleMat:getHandleMat(type)} );
- this.curveGroup.add(curve);
- prop.curve = curve;
- prop.type = type;
- //console.log('创建curve',type,curve.uuid)
-
- let cancel = ()=>{
- console.log('cancel_insertions', curve.uuid );
- curve.dispose();
- finish(false);
- };
- this.bus.dispatchEvent('cancel_insertions');//删除旧的
- this.bus.addEventListener('cancel_insertions',cancel);
-
- var click = (e)=>{
- if(e.button === MOUSE.RIGHT){
- if(curve.points.length>=1){ //if(type.includes('fire') || type.includes('smoke') ){
-
- particles = this.createFromData(prop);
- finish(true);
- }
- return
- }
-
-
- var I = e.intersectPoint && (e.intersectPoint.orthoIntersect || e.intersectPoint.location);
- if(!I)return
-
- curve.addPoint(I, null, true);
-
- if(type == 'explode'){
- particles = this.createFromData(prop);
-
- finish(true);
- }
-
- return {stopContinue:true}//防止继续执行别的侦听,如flytopano
- };
-
-
- viewer.addEventListener('global_click', click, 10);//add importance:10
- viewer.dispatchEvent({
- type : "CursorChange", action : "add", name:"addSth"
- });
-
- return deferred.promise()
- },
-
-
-
- createFromData(prop){
- const type = prop.type;
- var particles = [];
- let curve = prop.curve;
- if(!curve){
- curve = new CurveCtrl(prop.points, getLineMat$1(type), colors$1[type], type+'_curve', {handleMat:getHandleMat(type)} );
- this.curveGroup.add(curve);
- }
-
- if(type.includes('fire') || type.includes('smoke') ){
- if(type.includes('fire')){
- var fire = this.addParticle({
- type : 'fire',
- positions : curve.points,
- curve,
- radius : prop.radius,
- height: prop.height,
- strength : prop.strength,
- });
- particles.push(fire);
- }
- if(type.includes('smoke')){
- var smoke = this.addParticle({
- type : 'smoke',
- positions : curve.points,
- curve,
- positionStyle : 'sphere' ,
- strength : prop.smokeStrength,
- radius: prop.smokeRadius,
- height: prop.smokeHeight,
- });
- particles.push(smoke);
- }
-
-
- }else if(type == 'explode'){
- var explode = this.addParticle({
- type : 'explode',
- position : curve.points[0],
- strength: prop.strength,
- radius : prop.radius,
- particleSpaceTime: prop.particleSpaceTime,
- curve,
- delayStartTime:prop.delayStartTime,
- });
- particles.push(explode);
- }
- var geoNeedsUpdate;
- curve.addEventListener('dragCurvePoint',()=>{
- geoNeedsUpdate = true;
- Common.intervalTool.isWaiting('particlePointChange', ()=>{ //延时update,防止卡顿
- if(geoNeedsUpdate){
- particles.forEach(e=>e.updateGeometry());
- geoNeedsUpdate = false;
- curve.dispatchEvent('sendUpdatePoints');
- return true
- }
- }, 400);
- });
-
-
-
-
- return particles
- }
- };
- let CamAniEditor = {
-
-
-
-
- createAnimation(data){
- let animation = new CameraAnimation(viewer);
- if(data) {
- animation.name = data.name;
- animation.duration = data.duration;
- //animation.t = data.t;
- //animation.curveType = data.curveType;
- //animation.visible = data.visible;
- for(const cpdata of data.points){
- /* const position = Potree.Utils.datasetPosTransform({ fromDataset: true, position: cpdata.position, datasetId: Potree.settings.originDatasetId })
- const target = Potree.Utils.datasetPosTransform({ fromDataset: true, position: cpdata.target, datasetId: Potree.settings.originDatasetId })
- */
- const position = new THREE.Vector3().copy(cpdata.position);
- const target = new THREE.Vector3().copy(cpdata.target);
- const cp = animation.createControlPoint(null,{position, target});
- }
- }
-
- animation.changeCallback();
- viewer.scene.addCameraAnimation(animation);
-
- return animation
-
- },
-
-
- removeAnimation(animation){
- animation.dispatchEvent('dispose');
- viewer.scene.removeCameraAnimation(animation);
- }
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
- };
- let images360, Alignment$1, SiteModel$1;
- const texLoader$b = new TextureLoader();
- texLoader$b.crossOrigin = "anonymous";
-
- const lineMats$3 = {};
- const circleMats = {};
- const renderOrders = {
- circleSelected:3,
- circle:2,
- line:1,
- };
- const pointColor = {
- /* selected:"#c80",
- default:'#1ac' */
- selected:"#c60",
- default:'#17c'
- };
- const opacitys = {
- default:1.3,
- selected: 2.0
- };
- const cameraProps = [
- {
- name : 'top',
- axis:["x","y"],
- direction : new Vector3(0,0,-1), //镜头朝向
- openCount:0,
- },
- {
- name : 'right',
- axis:["y","z"],
- direction : new Vector3(1,0,0),
- openCount:0,
- }
- ];
- const targetPlane = new Plane();
- class PanoEditor extends EventDispatcher{
-
- constructor(){
- super();
- this.panoGroup = [], //分组
- this.viewports = {},
- this.panoLink = {},
- this.panoMeshs = new Object3D,
- this.lineMeshes = new Object3D;
- this.views = {};
- this.cameras = {};
- this.orthoCamera = new OrthographicCamera(-100, 100, 100, 100, 0.01, 10000);
- this.selectedPano;
- this.selectedGroup;
- this.operation;
- this.shiftTarget = new Vector3; //project在targetPlane上的位置
- this.visiblePanos = [];
- }
-
- init(){
-
- {//init lineMats
- lineMats$3.default = LineDraw.createFatLineMat({
- color: '#eeeeee',
- lineWidth: 2,
- depthTest:false
- });
- lineMats$3.hovered = LineDraw.createFatLineMat({
- color: '#00c8af',
- lineWidth: 2,
- depthTest:false
- });
- lineMats$3.selected = LineDraw.createFatLineMat({
- color: '#00c8af',
- lineWidth: 3,
- depthTest:false
- });
-
- }
-
-
- this.initViews();
-
-
- viewer.addEventListener('allLoaded',()=>{
- images360 = viewer.images360;
- Alignment$1 = viewer.modules.Alignment;
- SiteModel$1 = viewer.modules.SiteModel;
-
- this.panoMeshs.name = 'panoMeshs';
- viewer.scene.scene.add(this.panoMeshs);
- this.lineMeshes.name = 'lineMeshes';
- viewer.scene.scene.add(this.lineMeshes);
-
-
- this.initPanoLink();
-
- this.addPanoMesh();
-
-
- viewer.scene.pointclouds.forEach(e=>{
- e.material.color = pointColor.default;
-
- });
-
-
-
-
- this.switchView('top');
-
- SiteModel$1.bus.addEventListener('initDataDone',()=>{
- this.gotoFloor(SiteModel$1.entities.find(e=>e.buildType == 'floor')); //任意一层
- });
-
-
- Alignment$1.bus.addEventListener('switchHandle', this.updateCursor.bind(this));
-
-
- viewer.addEventListener('global_click',(e)=>{
- if(e.button === MOUSE.RIGHT){//取消旋转和平移
- console.log('right click',e);
- this.setLinkOperateState('addLink',false);
- this.setLinkOperateState('removeLink',false);
- }else if(this.clickToZoomInEnabled){
-
- this.zoomIn(e.intersectPoint.orthoIntersect, e.pointer);
-
- this.setZoomInState(false);
- }
- });
-
-
-
- {//旋转时的辅助线
- this.rotGuideLine = LineDraw.createLine([], {color:'#aaffee'});
- this.rotGuideLine.visible = false;
- this.rotGuideLine.name = 'rotGuideLine';
- this.rotGuideLine.renderOrder = renderOrders.line;
- viewer.scene.scene.add(this.rotGuideLine);
- let startPoint;
- Alignment$1.bus.addEventListener('rotateStart', (e)=>{
- startPoint = e.startPoint;
- });
- Alignment$1.bus.addEventListener('rotate', (e)=>{
- LineDraw.updateLine(this.rotGuideLine, [startPoint, e.endPoint] );
- this.rotGuideLine.visible = true;
- });
- viewer.fpControls.addEventListener("end",(e)=>{
- startPoint = null;
- this.rotGuideLine.visible = false;
- });
- }
-
- {//连接时的辅助线
- this.linkGuideLine = LineDraw.createLine([], {color:'#aaa', deshed:true, dashSize:0.1,gapSize:0.1,});
- this.linkGuideLine.visible = false;
- this.linkGuideLine.name = 'linkGuideLine';
- viewer.scene.scene.add(this.linkGuideLine);
- this.linkGuideLine.renderOrder = renderOrders.line;
- let update = (e)=>{
- if(this.operation != 'addLink' || this.activeViewName != 'top' || !this.selectedPano){
- return this.linkGuideLine.visible = false
- }
- LineDraw.updateLine(this.linkGuideLine, [this.selectedPano.position, e.intersectPoint.orthoIntersect.clone().setZ(this.selectedPano.position.z)] );
- this.linkGuideLine.visible = true;
- };
-
- viewer.addEventListener('global_mousemove', (e)=>{
- update(e);
- });
-
- this.addEventListener('updateLinkGuideLine', update);
- //为何打开调试时移动很卡
- }
-
- });
- }
-
-
-
-
-
-
- //////////////////////////////////
- initViews(){
- for(let i=0;i<2;i++){
- let prop = cameraProps[i];
- let view = new View();
- this.views[prop.name] = view;
- this.cameras[prop.name] = this.orthoCamera;
-
- view.direction = prop.direction;
- }
- this.views.mainView = viewer.mainViewport.view;
- this.cameras.mainView = viewer.mainViewport.camera;
-
-
- }
-
- switchView(name){//替换view和camera到mainViewport
- let view = this.views[name];
- let camera = this.cameras[name];
- let prop = cameraProps.find(e=>e.name == name);
-
- let {boundSize, center} = viewer.bound;
- this.lastViewName = this.activeViewName;
- this.activeViewName = name;
- let lastView = this.views[this.lastViewName];
- let lastCamera = this.cameras[this.lastViewName];
-
- //let aspect = viewer.mainViewport.camera.aspect
- viewer.mainViewport.view = view;
- viewer.mainViewport.camera = camera;
- //targetPlane.setFromNormalAndCoplanarPoint( prop.direction.clone(), center )
- targetPlane.setFromNormalAndCoplanarPoint( view.direction.clone(), center );
- targetPlane.projectPoint(view.position, this.shiftTarget ); //target转换到过模型中心的平面,以保证镜头一定在模型外
- view.position.copy(this.getPosOutOfModel());
-
- viewer.updateScreenSize({forceUpdateSize:true});//更新camera aspect left等
- this.updateCursor();
-
-
-
-
- if(name == 'mainView'){
- viewer.mainViewport.alignment = null;
- viewer.scene.pointclouds.forEach(e=>{
- e.material.activeAttributeName = 'rgba';
- e.material.useFilterByNormal = false;
- e.changePointOpacity(1,true);
-
- });
- viewer.updateVisible(viewer.reticule, 'force', true);
-
- if(lastView){
-
- view.copy(lastView);
- //view.position.
- let direction = view.direction;
- let panos = images360.panos.filter(e=>e.circle.visible);
- let nearestPano = Common.sortByScore(panos , [], [(pano)=>{
- let vec = new Vector3().subVectors(pano.position, view.position);
- return -vec.dot(direction);
- }], true);
-
- console.log('最近',nearestPano );
-
- if(nearestPano && nearestPano[0] ){
-
-
- let halfHeight = lastCamera.top/lastCamera.zoom;
- let dis = halfHeight / Math.tan( MathUtils.degToRad(camera.fov/2));
- view.position.add(direction.clone().multiplyScalar(-nearestPano[0].score - dis));
- console.log('getCloser', -nearestPano[0].score - dis);
- }
-
- }
- }else {
- if(prop.openCount == 0){//只需执行一次
- this.viewportFitBound(name, boundSize, center);
- }
- prop.openCount ++;
-
-
-
- viewer.scene.pointclouds.forEach(e=>{
- e.material.activeAttributeName = 'color';
- e.material.useFilterByNormal = true; //defines : use_filter_by_normal attenuated_opacity
-
-
- if(this.selectedPano && this.selectedClouds.includes(e) /* this.selectedPano.pointcloud == e */){
- e.changePointOpacity(opacitys.selected,true);
- }else {
- e.changePointOpacity(opacitys.default,true);
- }
-
- });
- viewer.updateVisible(viewer.reticule, 'force', false);
-
- if(name == 'top') viewer.mainViewport.alignment = {rotate:true,translate:true};
- else if(name == 'right') viewer.mainViewport.alignment = {translate:true, rotateSide:true};
-
- }
-
- this.setZoomInState(false); //取消放大模式
- }
-
-
- //new THREE.Plane().setFromNormalAndCoplanarPoint( normal, this.points[0] )
-
- viewportFitBound(name, boundSize_, target){ //使一个viewport聚焦在某个范围
- /* let viewport = viewer.mainViewport
- let {boundSize, center} = viewer.bound
-
- var prop = cameraProps.find(v => name == v.name )
-
- let expand = 10;
-
- targetPlane.setFromNormalAndCoplanarPoint( prop.direction.clone(), center )
- let shiftTarget = targetPlane.projectPoint(target, new THREE.Vector3() ) //target转换到过模型中心的平面,以保证镜头一定在模型外
-
- this.setCameraPose(shiftTarget, prop.direction)
-
-
- if(name == 'top'){
- let axis = prop.axis
- var width = Math.max(boundSize_[axis[0]], boundSize_[axis[1]] * viewport.camera.aspect)//视口宽度(米)
- }else{//因为侧面可能旋转
-
- let vec1 = new THREE.Vector3(boundSize_.x, 0,0);
- let vec2 = new THREE.Vector3(0,boundSize_.y,0);
- let v1 = vec1.projectOnPlane( prop.direction.clone() )
- let v2 = vec2.projectOnPlane( prop.direction.clone() )
-
-
- var width = Math.max(v1.length()+v2.length(), boundSize_.z * viewport.camera.aspect)//视口宽度(米)
- }
-
- var margin = 50 //px
- viewport.camera.zoom = (viewport.resolution.x - margin) / width
- viewport.camera.updateProjectionMatrix() */
-
- if(viewer.mainViewport.resolution.x == 0 || viewer.mainViewport.resolution.y == 0){
- return setTimeout(()=>{
- this.viewportFitBound(name, boundSize_, target);
- },10)
- }
-
- this.gotoFloor(this.currentFloor, true, 0, null, true);
-
-
- }
-
-
-
-
- rotateSideCamera(angle){//侧视图绕模型中心水平旋转
- var prop = cameraProps.find(v => v.name == 'right' );
-
- let {boundSize, center} = viewer.bound;
-
-
- //找到平移向量
- targetPlane.setFromNormalAndCoplanarPoint(viewer.mainViewport.view.direction /* prop.direction.clone() */, center );
- targetPlane.projectPoint(viewer.mainViewport.view.position, this.shiftTarget ); //target转换到过模型中心的平面,以保证镜头一定在模型外
- let vec = new Vector3().subVectors(center, this.shiftTarget);//相对于中心的偏移值,旋转后偏移值也旋转
-
- //旋转
- var rotMatrix = new Matrix4().makeRotationAxis(new Vector3(0,0,1), angle);
- //prop.direction.applyMatrix4(rotMatrix)
- viewer.mainViewport.view.direction = viewer.mainViewport.view.direction.applyMatrix4(rotMatrix);
-
-
- vec.applyMatrix4(rotMatrix);
- this.shiftTarget.subVectors(center,vec); //新的
-
-
- viewer.mainViewport.view.position = this.getPosOutOfModel();
- //this.setCameraPose(/* this.shiftTarget, prop.direction */)
-
- }
-
-
- getPosOutOfModel(){//已知shiftTarget和currentDir后
- let {boundSize, center} = viewer.bound;
- let expand = 10;
- let view = viewer.mainViewport.view;
- let radius = boundSize.length() / 2;
- let position = this.shiftTarget.clone().sub(view.direction.clone().multiplyScalar(radius + expand));
-
- return position
-
-
- }
-
- zoomIn(intersectPoint, pointer){
- let camera = viewer.mainViewport.camera;
- let endZoom = 700;
- //this.moveFit(intersectPoint, {endZoom:viewer.mainViewport.camera.zoom < aimZoom ? aimZoom : null} , 300)
- let startZoom = camera.zoom;
- if(startZoom >= endZoom){return}
-
- viewer.mainViewport.view.zoomOrthoCamera(camera, endZoom, pointer, 300);
-
- }
- /* getRadius(){
- let {boundSize, center} = viewer.bound
- let expand = 10;
- let radius = boundSize.length() / 2
- return radius + expand
- } */
- moveFit(pos, info, duration){
- targetPlane.projectPoint(pos, this.shiftTarget); //target转换到过模型中心的平面,以保证镜头一定在模型外 this.shiftTarget是得到的
- info.endPosition = this.getPosOutOfModel();
- info.margin = {x:200, y:230};
- let view = viewer.mainViewport.view;
- view.moveOrthoCamera(viewer.mainViewport, info , duration );
-
- }
-
- setZoomInState(state, informinformBy2d){//是否点击后可放大
- if(state && this.activeViewName == 'mainView')return console.log('3D不可放大')
- this.clickToZoomInEnabled = !!state;
-
-
- if(state){
- viewer.dispatchEvent({type : "CursorChange", action : "add", name:"zoomInCloud"} );
- }else {
- viewer.dispatchEvent({type : "CursorChange", action : "remove", name:"zoomInCloud" });
- }
-
- if(!state && !informinformBy2d){
- this.dispatchEvent({type:'operationCancel', operation: 'zoomIn'});
- }
-
-
- }
-
-
- gotoFloor(floor, force, duration = 600, informBy2d, fitBound){// 选择不同楼层, 切换点位显示。 'all'为全部显示
-
- floor = floor || 'all';
-
- if(this.currentFloor == floor && !force)return
-
- //let pointclouds = viewer.findPointcloudsAtFloor(floor)
- let panos = floor == 'all' ? viewer.images360.panos : floor.panos;
-
- viewer.images360.panos.forEach(pano=>{
- let v = panos.includes(pano);
- this.switchPanoVisible(pano,v);
- });
-
-
- this.updateLinesVisible();
-
-
- //切换楼层时清空选择状态
- if(this.selectedPano && floor != 'all' && !floor.panos.includes(this.selectedPano)){
- this.selectedPano.circle.dispatchEvent('click');
- }
-
- if(this.selectedLine){
- this.selectedLine.dispatchEvent('click');
- }
-
-
-
- let bound, center;
- if(floor == 'all'){
- bound = viewer.images360.bound.bounding;
- center = viewer.images360.bound.center;
- }else {
- bound = this.getPanosBound(floor);
- center = bound.getCenter(new Vector3());
- if(floor.panos.length == 0)console.log(floor.name, 'floor无漫游点' );
- }
-
- if(this.activeViewName != 'mainView' ){
- fitBound && this.moveFit(center, {bound}, duration);
- }else if(this.activeViewName == 'mainView'){
- if(floor != 'all'){ //切换一下位置,因为原处点云会消失
- viewer.scene.view.setView({position:center, duration });
- }
- }
-
- this.currentFloor = floor;
-
- if(!informBy2d){
- this.dispatchEvent({type:'changeFloor', floor});
- }
- }
-
- getPanosBound(floor){
- if(!floor.panosBound){
- if(floor.panos.length == 0){
- floor.panosBound = viewer.images360.bound.bounding.clone();
- }else {
- /* floor.panosBound = new THREE.Box3
-
- floor.panos.forEach(pano=>{
- floor.panosBound.expandByPoint(pano.position)
- })
- let center = floor.panosBound.getCenter(new THREE.Vector3)
- let minBound = (new THREE.Box3()).setFromCenterAndSize(center, new THREE.Vector3(5,5,5))
- floor.panosBound.union(minBound) */
-
- let minSize = new Vector3(5,5,5);
- let bound = math.getBoundByPoints(floor.panos.map(e=>e.position), minSize);
- floor.panosBound = bound.bounding;
- }
- }
-
- return floor.panosBound
- }
-
-
-
- switchPanoVisible(pano, v, informBy2d){
- pano.circle.visible = v;
- viewer.updateVisible(pano, 'panoEditor', v);
- viewer.updateVisible(pano.pointcloud, 'panoEditor', v);
- if(v){
- this.visiblePanos.includes(pano) || this.visiblePanos.push(pano);
- }else {
- let index = this.visiblePanos.indexOf(pano);
- index>-1 && this.visiblePanos.splice(index,1);
- }
-
- if(informBy2d){
- this.updateLinesVisible();
- }
-
- informBy2d || this.dispatchEvent({type:"switchPanoVisible", pano, v});
- }
-
-
- updateLinesVisible(){
- this.lineMeshes.children.forEach(line=>{
- let names = line.name.split('-');
- var pano0 = images360.getPano(names[0]);
- var pano1 = images360.getPano(names[1]);
- line.visible = this.visiblePanos.includes(pano0) || this.visiblePanos.includes(pano1);
- });
- }
-
- updateCursor(){
- let cursor;
-
-
- if(this.activeViewName == 'mainView' || !this.selectedPano){
- cursor = null;
- }else {
- cursor = Alignment$1.handleState;
- }
-
- if(cursor == 'rotate'){
- viewer.dispatchEvent({
- type : "CursorChange", action : "add", name:"rotatePointcloud"
- });
- viewer.dispatchEvent({
- type : "CursorChange", action : "remove", name:"movePointcloud"
- });
-
- }else if(cursor == 'translate'){
- viewer.dispatchEvent({
- type : "CursorChange", action : "add", name:"movePointcloud"
- });
- viewer.dispatchEvent({
- type : "CursorChange", action : "remove", name:"rotatePointcloud"
- });
- }else {
- viewer.dispatchEvent({
- type : "CursorChange", action : "remove", name:"movePointcloud"
- });
- viewer.dispatchEvent({
- type : "CursorChange", action : "remove", name:"rotatePointcloud"
- });
- }
- //this.cursorState = cursor
- }
-
-
-
- setLinkOperateState(name, state, informinformBy2d){
-
- if(state && name == this.operation || !state && name != this.operation)return
-
- let old = this.operation;
- this.operation = state ? name : null;
- if(this.selectedLine){
- this.selectedLine.dispatchEvent('click');//删除
- }
- if(this.operation != 'addLink'){
- this.linkGuideLine.visible = false;
- }
- if(!state && !informinformBy2d){
- this.dispatchEvent({type: "operationCancel", operation: old});
-
- }
-
- if(this.operation == 'addLink'){
- viewer.dispatchEvent({type : "CursorChange", action : "add", name:"connectPano"} );
- }else {
- viewer.dispatchEvent({type : "CursorChange", action : "remove", name:"connectPano"} );
- }
- if(this.operation == 'removeLink'){
- viewer.dispatchEvent({type : "CursorChange", action : "add", name:"disconnectPano"} );
- }else {
- viewer.dispatchEvent({type : "CursorChange", action : "remove", name:"disconnectPano"} );
- }
-
- }
-
-
-
-
- /////////////////////////////////
-
- initPanoLink(){
- images360.panos.forEach((pano)=>{
- this.panoLink[pano.id] = {};
- });
-
- images360.panos.forEach((pano)=>{
- pano.visibles.forEach(index=>{//visibles中存的是下标!
- this.linkChange(pano, images360.getPano(index,'index'), 'add');
- });
- });
-
- console.log('panoLink',this.panoLink);
- }
-
-
-
- groupChange(pano0, pano1, type){//修改group
- if(type == 'add'){
- Common.pushToGroupAuto([pano0, pano1], this.panoGroup );
- }else {
- let atGroup = this.panoGroup.find(e=>e.includes(pano0) && e.includes(pano1));//所在组
-
- if(!atGroup){
- return console.log('这两个pano原本就不在一个组', pano0.id, pano1.id)
- }
-
- //断开连接时,因为组内没有其他成员的连接信息,所以需要清除整组,并将剩余的一个个重新连接
- this.panoGroup.splice(this.panoGroup.indexOf(atGroup),1); //删除
-
-
- atGroup.forEach(pano=>{//然后再重新生成这两个和组的关系,各组分组
- if(pano == pano0 || pano == pano1)return
- for(let i in this.panoLink[pano.id]){
- if(this.panoLink[pano.id][i]){
- let pano_ = images360.getPano(i);
- Common.pushToGroupAuto([pano, pano_], this.panoGroup );
- }
- }
- });
-
-
-
- }
- }
-
- linkChange(pano0, pano1, type){//修改link
-
- if(type == 'add'){
- this.panoLink[pano0.id][pano1.id] = this.panoLink[pano0.id][pano1.id] || {};
- this.panoLink[pano1.id][pano0.id] = this.panoLink[pano1.id][pano0.id] || {};
- }else {
- this.panoLink[pano0.id][pano1.id] = false;
- this.panoLink[pano1.id][pano0.id] = false;
- }
- this.lineChange(pano0, pano1, type);
- this.groupChange(pano0, pano1, type);
-
- //this.updateSelectGroup()
- this.selectPano(this.selectedPano, false,true); //更新选中点云显示
- }
-
-
-
- lineChange(pano0, pano1, type){//修改line
- if(type == 'add'){
- if(this.panoLink[pano0.id][pano1.id].line) return
- let line = LineDraw.createFatLine([pano0.position, pano1.position], {material:lineMats$3.default});
- line.name = `${pano0.id}-${pano1.id}`;
- line.renderOrder = renderOrders.line;
- this.lineMeshes.add(line);
- this.panoLink[pano0.id][pano1.id].line = this.panoLink[pano1.id][pano0.id].line = line;
-
-
- line.addEventListener('mouseover', ()=>{
- if(this.activeViewName == 'mainView')return
- if(this.selectedLine != line)line.material = lineMats$3.hovered;
- viewer.dispatchEvent({
- type : "CursorChange", action : "add", name:"hoverLine"
- });
- });
- line.addEventListener('mouseleave', ()=>{
- //if(this.activeViewName == 'mainView')return
- if(this.selectedLine != line)line.material = lineMats$3.default;
- viewer.dispatchEvent({
- type : "CursorChange", action : "remove", name:"hoverLine"
- });
- });
- line.addEventListener('click', (e)=>{
- if(this.activeViewName == 'mainView')return
- if(this.operation == 'removeLink'){
- if(this.selectedLine == line) this.selectLine(null);
- return this.linkChange(pano0, pano1, 'remove')
- }
- this.selectLine(line);
- });
- }else {
- let line = this.lineMeshes.children.find(e=>e.name == `${pano0.id}-${pano1.id}` || e.name == `${pano1.id}-${pano0.id}` );
- if(line){
- this.lineMeshes.remove(line);
- line.geometry.dispose();
- }
-
- }
-
- }
-
-
-
-
- selectLine(line){
- if(this.selectedLine == line)return
- if(this.selectedLine){
- this.selectedLine.material = lineMats$3.default;
- }
- if(line){
- line.material = lineMats$3.selected;
- }
- this.selectedLine = line;
- }
-
-
-
- addPanoMesh(){
- let map = texLoader$b.load(Potree.resourcePath+'/textures/correct_n.png' );
- circleMats.default = new MeshBasicMaterial({
- map,
- color: 0xffffff,
- transparent: true,
- depthTest: false,
- depthWrite: false,
- });
- circleMats.hovered = new MeshBasicMaterial({
- map,
- color: 0xffff00,
- transparent: true,
- depthTest: false,
- depthWrite: false,
- });
- circleMats.selected = new MeshBasicMaterial({
- map: texLoader$b.load(Potree.resourcePath+'/textures/correct_s.png' ) ,
- color: 0xffffff,
- transparent: true,
- depthTest: false,
- depthWrite: false,
- });
-
- let setPos = (circle)=>{
- circle.position.copy(circle.pano.position);
-
- for(let id in this.panoLink[circle.pano.id]){
- let linkInfo = this.panoLink[circle.pano.id][id];
- if(linkInfo){
- LineDraw.updateLine(linkInfo.line, [circle.pano.position, images360.getPano(id).position] );
- }
- }
-
- circle.update(); //update sprite Matrix
-
- };
-
-
-
- images360.panos.forEach(pano=>{
- var circle = new Sprite$1({mat: circleMats.default, sizeInfo:{
- minSize : 50 , maxSize : 120, nearBound : 2, farBound : 10,
- },
- renderOrder : renderOrders.circle
- }); //new THREE.Sprite(circleMats.default)
-
- circle.name = 'panoCircle';
- circle.sid = pano.id;
- circle.pano = pano;
- pano.circle = circle;
-
- this.panoMeshs.add(circle);
-
-
-
- setPos(circle);
- pano.addEventListener('rePos', setPos.bind(this,circle));
-
- let drag = ()=>{
- if(this.activeViewName == 'mainView')return
- this.selectPano(circle.pano); //为了方便拖拽点云,拖动circle就直接选中
-
- viewer.inputHandler.drag.object = null; //取消拖拽状态,否则不触发点云拖动
- };
- circle.addEventListener('drag', drag);
-
- /* circle.addEventListener('drop', ()=>{
-
- }) */
-
- circle.addEventListener('mouseover', ()=>{
- this.hoverPano(pano,true);
- });
- circle.addEventListener('mouseleave', ()=>{
- this.hoverPano(pano,false);
- });
- circle.addEventListener('click', ()=>{
- if(this.activeViewName == 'mainView')return
- if(this.selectedPano == circle.pano) return this.selectPano(null)
- if(this.operation == 'addLink' && this.selectedPano){
- this.linkChange(this.selectedPano, circle.pano, 'add');
- //this.setLinkOperateState('addLink',false)
- return
- }
- //if(this.operation == 'removeLink' && this.selectedPano){ //和选择中心点冲突
- // this.linkChange(this.selectedPano, circle.pano, 'remove')
- // //this.setLinkOperateState('removeLink',false)
- // return
- // }
- this.selectPano(circle.pano);
- });
-
-
- });
- }
-
-
- hoverPano(pano, state){
- if(pano && state){ //在hover一个pano之前,一定会先取消已经hover的pano, 最多存在一个hovered的pano
- if(this.hoveredPano == pano)return
-
- if(this.hoveredPano){
- this.hoverPano(this.hoveredPano,false);
- }
-
- this.hoveredPano = pano;
- pano.hovered = true;
-
-
- if(this.activeViewName == 'mainView' || Alignment$1.handleState && this.selectedPano && this.selectedPano == pano)return
-
- if(this.operation != 'addLink' || !this.selectedPano || this.selectedPano == pano){ // this.selectedPano == pano?
- viewer.dispatchEvent({
- type : "CursorChange", action : "add", name:"hoverPano"
- });
- }
- if(this.selectedPano != pano) pano.circle.material = circleMats.hovered;
-
- }else if(pano && !state){//unhover
- if(this.hoveredPano != pano)return
- pano.hovered = false;
- viewer.dispatchEvent({
- type : "CursorChange", action : "remove", name:"hoverPano"
- });
- if(this.selectedPano != pano) pano.circle.material = circleMats.default;
- this.hoveredPano = null;
- }else {//unhover any
- if(this.hoveredPano){
- this.hoverPano(this.hoveredPano, false);
- }
- }
- }
-
- selectPano(pano, informinformBy2d, force){
- if(this.selectedPano == pano && !force)return
-
-
- if(this.selectedPano){
- this.selectedPano.circle.material = circleMats.default;
- this.selectedPano.circle.renderOrder = renderOrders.circle;
- if(this.activeViewName != 'mainView'){
- this.selectedClouds.forEach(e=>{
- e.changePointOpacity(opacitys.default,true);
- e.material.color = pointColor.default;
- });
-
- //this.selectedPano.pointcloud.changePointOpacity(opacitys.default,true)
- //this.selectedPano.pointcloud.material.color = Potree.config.material.pointColor
- }
-
- }
-
- this.selectedPano = pano || null;
-
- this.updateSelectGroup();
-
- if(pano){
- this.selectedPano.circle.material = circleMats.selected;
- this.selectedPano.circle.renderOrder = renderOrders.circleSelected; //侧视图能显示在最前
- //this.selectedPano.pointcloud.material.color = '#ff0000'
- //this.selectedPano.pointcloud.changePointOpacity(opacitys.selected,true)
- this.selectedClouds.forEach(e=>{
- e.changePointOpacity(opacitys.selected,true);
- e.material.color = pointColor.selected;
- });
-
-
-
-
-
- {//自动切换楼层
-
- let atFloor = SiteModel$1.entities.find(e=>e.buildType == 'floor' && e.panos.includes(pano));
- if(!atFloor){
- atFloor = 'all';
- }else {
-
- }
-
- this.gotoFloor(atFloor, false, 600 );
- }
-
-
- }
-
-
- this.updateCursor();
- informinformBy2d || this.dispatchEvent({type:'panoSelect', pano });
-
- }
-
-
-
-
- updateSelectGroup(){//更新选中的组
- this.selectedGroup = this.panoGroup.find(e=>e.includes(this.selectedPano));
- this.selectedClouds = this.selectedPano ? (this.selectedGroup || [this.selectedPano]).map(e=>e.pointcloud) : [];
- }
-
-
- checkIfCanSave(){//如果未全部相连,不能保存
- for(let datasetId in Potree.settings.datasetsPanos ) {
- if(!this.checkIfAllLinked({datasetId})){
- console.log('没有全部连通,不能保存。其中一个:', datasetId);
- return
- }
- }
- return true
-
- }
-
-
-
- checkIfAllLinked(o){//某个(or组所在的)数据集是否全部连通
-
- let datasetId, group;
-
- if(o.group){
- group = o.group;
- let pano = o.group[0];
- if(!pano)return //会有没有漫游点的点云来编辑吗
- datasetId = pano.pointcloud.dataset_id;
- }else if(o.datasetId){
- datasetId = o.datasetId;
- group = this.panoGroup.find(panos=>panos[0].pointcloud.dataset_id == datasetId );
- if(!group)return //要找的数据集的pano全部都孤立了
- }
-
- let panos = Potree.settings.datasetsPanos[datasetId].panos;
- return panos.length == group.length
- }
-
- exportSavingData(){//输出漫游点新的坐标和朝向、以及连接信息
- let sweepLocations = {};
- for(let datasetId in Potree.settings.datasetsPanos ) {
- let {panos} = Potree.settings.datasetsPanos[datasetId];
- let data = panos.map(pano=>{
- let visibles = [];
- for(let id in this.panoLink[pano.id]){
- if(this.panoLink[pano.id][id]){
- visibles.push(viewer.images360.getPano(id).index);
- }
- }
-
- return Object.assign({}, pano.panosData, {
- uuid: pano.uuid,
- pose:{
- translation: dealData(pano.position.clone().negate()),
- rotation: dealData(new Quaternion().setFromRotationMatrix(pano.panoMatrix) ),
- },
- visibles,
-
- //subgroup: 0,group: 1, "id_view":..
- })
- });
-
- sweepLocations[datasetId] = {sweepLocations:data};
-
- }
-
-
-
-
- /* this.lineMeshes.children.forEach(e=>{//从line中搜集连接信息,而不从linkInfo,这样visibles不会重复一次
- let names = e.name.split('-') //是不是该转成数字
- var pano0 = names[0]
- var pano1 = names[1]
- sweepLocations.find(s=>s.uuid == pano0).visibles.push(pano1)
- }) */
-
-
- function dealData(value){
- let v = math.toPrecision(value, 6);
- if(v instanceof Quaternion){
- return {x:v.x, y:v.y, z:v.z, w:v.w}
- }else if(v instanceof Vector3){
- return {x:v.x, y:v.y, z:v.z}
- }
- }
-
- console.log(sweepLocations);
- return sweepLocations
- }
- }
- /*
- 不同数据集之间不能连线
- 不同楼层可能也不能
- 如果楼层在不同建筑物怎么办? 楼层切换按钮只能在一个建筑内切换。
- 全部相连时不能移动和旋转
-
- 如果未全部相连,不能保存
-
- */
- var PanoEditor$1 = new PanoEditor();
- var OBJLoader = ( function () {
- // o object_name | g group_name
- var object_pattern = /^[og]\s*(.+)?/;
- // mtllib file_reference
- var material_library_pattern = /^mtllib /;
- // usemtl material_name
- var material_use_pattern = /^usemtl /;
- // usemap map_name
- var map_use_pattern = /^usemap /;
- var vA = new Vector3();
- var vB = new Vector3();
- var vC = new Vector3();
- var ab = new Vector3();
- var cb = new Vector3();
- function ParserState() {
- var state = {
- objects: [],
- object: {},
- vertices: [],
- normals: [],
- colors: [],
- uvs: [],
- materials: {},
- materialLibraries: [],
- startObject: function ( name, fromDeclaration ) {
- // If the current object (initial from reset) is not from a g/o declaration in the parsed
- // file. We need to use it for the first parsed g/o to keep things in sync.
- if ( this.object && this.object.fromDeclaration === false ) {
- this.object.name = name;
- this.object.fromDeclaration = ( fromDeclaration !== false );
- return;
- }
- var previousMaterial = ( this.object && typeof this.object.currentMaterial === 'function' ? this.object.currentMaterial() : undefined );
- if ( this.object && typeof this.object._finalize === 'function' ) {
- this.object._finalize( true );
- }
- this.object = {
- name: name || '',
- fromDeclaration: ( fromDeclaration !== false ),
- geometry: {
- vertices: [],
- normals: [],
- colors: [],
- uvs: [],
- hasUVIndices: false
- },
- materials: [],
- smooth: true,
- startMaterial: function ( name, libraries ) {
- var previous = this._finalize( false );
- // New usemtl declaration overwrites an inherited material, except if faces were declared
- // after the material, then it must be preserved for proper MultiMaterial continuation.
- if ( previous && ( previous.inherited || previous.groupCount <= 0 ) ) {
- this.materials.splice( previous.index, 1 );
- }
- var material = {
- index: this.materials.length,
- name: name || '',
- mtllib: ( Array.isArray( libraries ) && libraries.length > 0 ? libraries[ libraries.length - 1 ] : '' ),
- smooth: ( previous !== undefined ? previous.smooth : this.smooth ),
- groupStart: ( previous !== undefined ? previous.groupEnd : 0 ),
- groupEnd: - 1,
- groupCount: - 1,
- inherited: false,
- clone: function ( index ) {
- var cloned = {
- index: ( typeof index === 'number' ? index : this.index ),
- name: this.name,
- mtllib: this.mtllib,
- smooth: this.smooth,
- groupStart: 0,
- groupEnd: - 1,
- groupCount: - 1,
- inherited: false
- };
- cloned.clone = this.clone.bind( cloned );
- return cloned;
- }
- };
- this.materials.push( material );
- return material;
- },
- currentMaterial: function () {
- if ( this.materials.length > 0 ) {
- return this.materials[ this.materials.length - 1 ];
- }
- return undefined;
- },
- _finalize: function ( end ) {
- var lastMultiMaterial = this.currentMaterial();
- if ( lastMultiMaterial && lastMultiMaterial.groupEnd === - 1 ) {
- lastMultiMaterial.groupEnd = this.geometry.vertices.length / 3;
- lastMultiMaterial.groupCount = lastMultiMaterial.groupEnd - lastMultiMaterial.groupStart;
- lastMultiMaterial.inherited = false;
- }
- // Ignore objects tail materials if no face declarations followed them before a new o/g started.
- if ( end && this.materials.length > 1 ) {
- for ( var mi = this.materials.length - 1; mi >= 0; mi -- ) {
- if ( this.materials[ mi ].groupCount <= 0 ) {
- this.materials.splice( mi, 1 );
- }
- }
- }
- // Guarantee at least one empty material, this makes the creation later more straight forward.
- if ( end && this.materials.length === 0 ) {
- this.materials.push( {
- name: '',
- smooth: this.smooth
- } );
- }
- return lastMultiMaterial;
- }
- };
- // Inherit previous objects material.
- // Spec tells us that a declared material must be set to all objects until a new material is declared.
- // If a usemtl declaration is encountered while this new object is being parsed, it will
- // overwrite the inherited material. Exception being that there was already face declarations
- // to the inherited material, then it will be preserved for proper MultiMaterial continuation.
- if ( previousMaterial && previousMaterial.name && typeof previousMaterial.clone === 'function' ) {
- var declared = previousMaterial.clone( 0 );
- declared.inherited = true;
- this.object.materials.push( declared );
- }
- this.objects.push( this.object );
- },
- finalize: function () {
- if ( this.object && typeof this.object._finalize === 'function' ) {
- this.object._finalize( true );
- }
- },
- parseVertexIndex: function ( value, len ) {
- var index = parseInt( value, 10 );
- return ( index >= 0 ? index - 1 : index + len / 3 ) * 3;
- },
- parseNormalIndex: function ( value, len ) {
- var index = parseInt( value, 10 );
- return ( index >= 0 ? index - 1 : index + len / 3 ) * 3;
- },
- parseUVIndex: function ( value, len ) {
- var index = parseInt( value, 10 );
- return ( index >= 0 ? index - 1 : index + len / 2 ) * 2;
- },
- addVertex: function ( a, b, c ) {
- var src = this.vertices;
- var dst = this.object.geometry.vertices;
- dst.push( src[ a + 0 ], src[ a + 1 ], src[ a + 2 ] );
- dst.push( src[ b + 0 ], src[ b + 1 ], src[ b + 2 ] );
- dst.push( src[ c + 0 ], src[ c + 1 ], src[ c + 2 ] );
- },
- addVertexPoint: function ( a ) {
- var src = this.vertices;
- var dst = this.object.geometry.vertices;
- dst.push( src[ a + 0 ], src[ a + 1 ], src[ a + 2 ] );
- },
- addVertexLine: function ( a ) {
- var src = this.vertices;
- var dst = this.object.geometry.vertices;
- dst.push( src[ a + 0 ], src[ a + 1 ], src[ a + 2 ] );
- },
- addNormal: function ( a, b, c ) {
- var src = this.normals;
- var dst = this.object.geometry.normals;
- dst.push( src[ a + 0 ], src[ a + 1 ], src[ a + 2 ] );
- dst.push( src[ b + 0 ], src[ b + 1 ], src[ b + 2 ] );
- dst.push( src[ c + 0 ], src[ c + 1 ], src[ c + 2 ] );
- },
- addFaceNormal: function ( a, b, c ) {
- var src = this.vertices;
- var dst = this.object.geometry.normals;
- vA.fromArray( src, a );
- vB.fromArray( src, b );
- vC.fromArray( src, c );
- cb.subVectors( vC, vB );
- ab.subVectors( vA, vB );
- cb.cross( ab );
- cb.normalize();
- dst.push( cb.x, cb.y, cb.z );
- dst.push( cb.x, cb.y, cb.z );
- dst.push( cb.x, cb.y, cb.z );
- },
- addColor: function ( a, b, c ) {
- var src = this.colors;
- var dst = this.object.geometry.colors;
- if ( src[ a ] !== undefined ) dst.push( src[ a + 0 ], src[ a + 1 ], src[ a + 2 ] );
- if ( src[ b ] !== undefined ) dst.push( src[ b + 0 ], src[ b + 1 ], src[ b + 2 ] );
- if ( src[ c ] !== undefined ) dst.push( src[ c + 0 ], src[ c + 1 ], src[ c + 2 ] );
- },
- addUV: function ( a, b, c ) {
- var src = this.uvs;
- var dst = this.object.geometry.uvs;
- dst.push( src[ a + 0 ], src[ a + 1 ] );
- dst.push( src[ b + 0 ], src[ b + 1 ] );
- dst.push( src[ c + 0 ], src[ c + 1 ] );
- },
- addDefaultUV: function () {
- var dst = this.object.geometry.uvs;
- dst.push( 0, 0 );
- dst.push( 0, 0 );
- dst.push( 0, 0 );
- },
- addUVLine: function ( a ) {
- var src = this.uvs;
- var dst = this.object.geometry.uvs;
- dst.push( src[ a + 0 ], src[ a + 1 ] );
- },
- addFace: function ( a, b, c, ua, ub, uc, na, nb, nc ) {
- var vLen = this.vertices.length;
- var ia = this.parseVertexIndex( a, vLen );
- var ib = this.parseVertexIndex( b, vLen );
- var ic = this.parseVertexIndex( c, vLen );
- this.addVertex( ia, ib, ic );
- this.addColor( ia, ib, ic );
- // normals
- if ( na !== undefined && na !== '' ) {
- var nLen = this.normals.length;
- ia = this.parseNormalIndex( na, nLen );
- ib = this.parseNormalIndex( nb, nLen );
- ic = this.parseNormalIndex( nc, nLen );
- this.addNormal( ia, ib, ic );
- } else {
- this.addFaceNormal( ia, ib, ic );
- }
- // uvs
- if ( ua !== undefined && ua !== '' ) {
- var uvLen = this.uvs.length;
- ia = this.parseUVIndex( ua, uvLen );
- ib = this.parseUVIndex( ub, uvLen );
- ic = this.parseUVIndex( uc, uvLen );
- this.addUV( ia, ib, ic );
- this.object.geometry.hasUVIndices = true;
- } else {
- // add placeholder values (for inconsistent face definitions)
- this.addDefaultUV();
- }
- },
- addPointGeometry: function ( vertices ) {
- this.object.geometry.type = 'Points';
- var vLen = this.vertices.length;
- for ( var vi = 0, l = vertices.length; vi < l; vi ++ ) {
- var index = this.parseVertexIndex( vertices[ vi ], vLen );
- this.addVertexPoint( index );
- this.addColor( index );
- }
- },
- addLineGeometry: function ( vertices, uvs ) {
- this.object.geometry.type = 'Line';
- var vLen = this.vertices.length;
- var uvLen = this.uvs.length;
- for ( var vi = 0, l = vertices.length; vi < l; vi ++ ) {
- this.addVertexLine( this.parseVertexIndex( vertices[ vi ], vLen ) );
- }
- for ( var uvi = 0, l = uvs.length; uvi < l; uvi ++ ) {
- this.addUVLine( this.parseUVIndex( uvs[ uvi ], uvLen ) );
- }
- }
- };
- state.startObject( '', false );
- return state;
- }
- //
- function OBJLoader( manager ) {
- Loader.call( this, manager );
- this.materials = null;
- }
- OBJLoader.prototype = Object.assign( Object.create( Loader.prototype ), {
- constructor: OBJLoader,
- load: function ( url, onLoad, onProgress, onError ) {
- var scope = this;
- var loader = new FileLoader( this.manager );
- loader.setPath( this.path );
- loader.setRequestHeader( this.requestHeader );
- loader.setWithCredentials( this.withCredentials );
- loader.load( url, function ( text ) {
- try {
- onLoad( scope.parse( text ) );
- } catch ( e ) {
- if ( onError ) {
- onError( e );
- } else {
- console.error( e );
- }
- scope.manager.itemError( url );
- }
- }, onProgress, onError );
- },
- setMaterials: function ( materials ) {
- this.materials = materials;
- return this;
- },
- parse: function ( text ) {
- var state = new ParserState();
- if ( text.indexOf( '\r\n' ) !== - 1 ) {
- // This is faster than String.split with regex that splits on both
- text = text.replace( /\r\n/g, '\n' );
- }
- if ( text.indexOf( '\\\n' ) !== - 1 ) {
- // join lines separated by a line continuation character (\)
- text = text.replace( /\\\n/g, '' );
- }
- var lines = text.split( '\n' );
- var line = '', lineFirstChar = '';
- var lineLength = 0;
- var result = [];
- // Faster to just trim left side of the line. Use if available.
- var trimLeft = ( typeof ''.trimLeft === 'function' );
- for ( var i = 0, l = lines.length; i < l; i ++ ) {
- line = lines[ i ];
- line = trimLeft ? line.trimLeft() : line.trim();
- lineLength = line.length;
- if ( lineLength === 0 ) continue;
- lineFirstChar = line.charAt( 0 );
- // @todo invoke passed in handler if any
- if ( lineFirstChar === '#' ) continue;
- if ( lineFirstChar === 'v' ) {
- var data = line.split( /\s+/ );
- switch ( data[ 0 ] ) {
- case 'v':
- state.vertices.push(
- parseFloat( data[ 1 ] ),
- parseFloat( data[ 2 ] ),
- parseFloat( data[ 3 ] )
- );
- if ( data.length >= 7 ) {
- state.colors.push(
- parseFloat( data[ 4 ] ),
- parseFloat( data[ 5 ] ),
- parseFloat( data[ 6 ] )
- );
- } else {
- // if no colors are defined, add placeholders so color and vertex indices match
- state.colors.push( undefined, undefined, undefined );
- }
- break;
- case 'vn':
- state.normals.push(
- parseFloat( data[ 1 ] ),
- parseFloat( data[ 2 ] ),
- parseFloat( data[ 3 ] )
- );
- break;
- case 'vt':
- state.uvs.push(
- parseFloat( data[ 1 ] ),
- parseFloat( data[ 2 ] )
- );
- break;
- }
- } else if ( lineFirstChar === 'f' ) {
- var lineData = line.substr( 1 ).trim();
- var vertexData = lineData.split( /\s+/ );
- var faceVertices = [];
- // Parse the face vertex data into an easy to work with format
- for ( var j = 0, jl = vertexData.length; j < jl; j ++ ) {
- var vertex = vertexData[ j ];
- if ( vertex.length > 0 ) {
- var vertexParts = vertex.split( '/' );
- faceVertices.push( vertexParts );
- }
- }
- // Draw an edge between the first vertex and all subsequent vertices to form an n-gon
- var v1 = faceVertices[ 0 ];
- for ( var j = 1, jl = faceVertices.length - 1; j < jl; j ++ ) {
- var v2 = faceVertices[ j ];
- var v3 = faceVertices[ j + 1 ];
- state.addFace(
- v1[ 0 ], v2[ 0 ], v3[ 0 ],
- v1[ 1 ], v2[ 1 ], v3[ 1 ],
- v1[ 2 ], v2[ 2 ], v3[ 2 ]
- );
- }
- } else if ( lineFirstChar === 'l' ) {
- var lineParts = line.substring( 1 ).trim().split( ' ' );
- var lineVertices = [], lineUVs = [];
- if ( line.indexOf( '/' ) === - 1 ) {
- lineVertices = lineParts;
- } else {
- for ( var li = 0, llen = lineParts.length; li < llen; li ++ ) {
- var parts = lineParts[ li ].split( '/' );
- if ( parts[ 0 ] !== '' ) lineVertices.push( parts[ 0 ] );
- if ( parts[ 1 ] !== '' ) lineUVs.push( parts[ 1 ] );
- }
- }
- state.addLineGeometry( lineVertices, lineUVs );
- } else if ( lineFirstChar === 'p' ) {
- var lineData = line.substr( 1 ).trim();
- var pointData = lineData.split( ' ' );
- state.addPointGeometry( pointData );
- } else if ( ( result = object_pattern.exec( line ) ) !== null ) {
- // o object_name
- // or
- // g group_name
- // WORKAROUND: https://bugs.chromium.org/p/v8/issues/detail?id=2869
- // var name = result[ 0 ].substr( 1 ).trim();
- var name = ( ' ' + result[ 0 ].substr( 1 ).trim() ).substr( 1 );
- state.startObject( name );
- } else if ( material_use_pattern.test( line ) ) {
- // material
- state.object.startMaterial( line.substring( 7 ).trim(), state.materialLibraries );
- } else if ( material_library_pattern.test( line ) ) {
- // mtl file
- state.materialLibraries.push( line.substring( 7 ).trim() );
- } else if ( map_use_pattern.test( line ) ) {
- // the line is parsed but ignored since the loader assumes textures are defined MTL files
- // (according to https://www.okino.com/conv/imp_wave.htm, 'usemap' is the old-style Wavefront texture reference method)
- console.warn( 'THREE.OBJLoader: Rendering identifier "usemap" not supported. Textures must be defined in MTL files.' );
- } else if ( lineFirstChar === 's' ) {
- result = line.split( ' ' );
- // smooth shading
- // @todo Handle files that have varying smooth values for a set of faces inside one geometry,
- // but does not define a usemtl for each face set.
- // This should be detected and a dummy material created (later MultiMaterial and geometry groups).
- // This requires some care to not create extra material on each smooth value for "normal" obj files.
- // where explicit usemtl defines geometry groups.
- // Example asset: examples/models/obj/cerberus/Cerberus.obj
- /*
- * http://paulbourke.net/dataformats/obj/
- * or
- * http://www.cs.utah.edu/~boulos/cs3505/obj_spec.pdf
- *
- * From chapter "Grouping" Syntax explanation "s group_number":
- * "group_number is the smoothing group number. To turn off smoothing groups, use a value of 0 or off.
- * Polygonal elements use group numbers to put elements in different smoothing groups. For free-form
- * surfaces, smoothing groups are either turned on or off; there is no difference between values greater
- * than 0."
- */
- if ( result.length > 1 ) {
- var value = result[ 1 ].trim().toLowerCase();
- state.object.smooth = ( value !== '0' && value !== 'off' );
- } else {
- // ZBrush can produce "s" lines #11707
- state.object.smooth = true;
- }
- var material = state.object.currentMaterial();
- if ( material ) material.smooth = state.object.smooth;
- } else {
- // Handle null terminated files without exception
- if ( line === '\0' ) continue;
- console.warn( 'THREE.OBJLoader: Unexpected line: "' + line + '"' );
- }
- }
- state.finalize();
- var container = new Group();
- container.materialLibraries = [].concat( state.materialLibraries );
- var hasPrimitives = ! ( state.objects.length === 1 && state.objects[ 0 ].geometry.vertices.length === 0 );
- if ( hasPrimitives === true ) {
- for ( var i = 0, l = state.objects.length; i < l; i ++ ) {
- var object = state.objects[ i ];
- var geometry = object.geometry;
- var materials = object.materials;
- var isLine = ( geometry.type === 'Line' );
- var isPoints = ( geometry.type === 'Points' );
- var hasVertexColors = false;
- // Skip o/g line declarations that did not follow with any faces
- if ( geometry.vertices.length === 0 ) continue;
- var buffergeometry = new BufferGeometry();
- buffergeometry.setAttribute( 'position', new Float32BufferAttribute( geometry.vertices, 3 ) );
- if ( geometry.normals.length > 0 ) {
- buffergeometry.setAttribute( 'normal', new Float32BufferAttribute( geometry.normals, 3 ) );
- }
- if ( geometry.colors.length > 0 ) {
- hasVertexColors = true;
- buffergeometry.setAttribute( 'color', new Float32BufferAttribute( geometry.colors, 3 ) );
- }
- if ( geometry.hasUVIndices === true ) {
- buffergeometry.setAttribute( 'uv', new Float32BufferAttribute( geometry.uvs, 2 ) );
- }
- // Create materials
- var createdMaterials = [];
- for ( var mi = 0, miLen = materials.length; mi < miLen; mi ++ ) {
- var sourceMaterial = materials[ mi ];
- var materialHash = sourceMaterial.name + '_' + sourceMaterial.smooth + '_' + hasVertexColors;
- var material = state.materials[ materialHash ];
- if ( this.materials !== null ) {
- material = this.materials.create( sourceMaterial.name );
- // mtl etc. loaders probably can't create line materials correctly, copy properties to a line material.
- if ( isLine && material && ! ( material instanceof LineBasicMaterial ) ) {
- var materialLine = new LineBasicMaterial();
- Material.prototype.copy.call( materialLine, material );
- materialLine.color.copy( material.color );
- material = materialLine;
- } else if ( isPoints && material && ! ( material instanceof PointsMaterial ) ) {
- var materialPoints = new PointsMaterial( { size: 10, sizeAttenuation: false } );
- Material.prototype.copy.call( materialPoints, material );
- materialPoints.color.copy( material.color );
- materialPoints.map = material.map;
- material = materialPoints;
- }
- }
- if ( material === undefined ) {
- if ( isLine ) {
- material = new LineBasicMaterial();
- } else if ( isPoints ) {
- material = new PointsMaterial( { size: 1, sizeAttenuation: false } );
- } else {
- material = new MeshPhongMaterial();
- }
- material.name = sourceMaterial.name;
- material.flatShading = sourceMaterial.smooth ? false : true;
- material.vertexColors = hasVertexColors;
- state.materials[ materialHash ] = material;
- }
- createdMaterials.push( material );
- }
- // Create mesh
- var mesh;
- if ( createdMaterials.length > 1 ) {
- for ( var mi = 0, miLen = materials.length; mi < miLen; mi ++ ) {
- var sourceMaterial = materials[ mi ];
- buffergeometry.addGroup( sourceMaterial.groupStart, sourceMaterial.groupCount, mi );
- }
- if ( isLine ) {
- mesh = new LineSegments( buffergeometry, createdMaterials );
- } else if ( isPoints ) {
- mesh = new Points( buffergeometry, createdMaterials );
- } else {
- mesh = new Mesh( buffergeometry, createdMaterials );
- }
- } else {
- if ( isLine ) {
- mesh = new LineSegments( buffergeometry, createdMaterials[ 0 ] );
- } else if ( isPoints ) {
- mesh = new Points( buffergeometry, createdMaterials[ 0 ] );
- } else {
- mesh = new Mesh( buffergeometry, createdMaterials[ 0 ] );
- }
- }
- mesh.name = object.name;
- container.add( mesh );
- }
- } else {
- // if there is only the default parser state object with no geometry data, interpret data as point cloud
- if ( state.vertices.length > 0 ) {
- var material = new PointsMaterial( { size: 1, sizeAttenuation: false } );
- var buffergeometry = new BufferGeometry();
- buffergeometry.setAttribute( 'position', new Float32BufferAttribute( state.vertices, 3 ) );
- if ( state.colors.length > 0 && state.colors[ 0 ] !== undefined ) {
- buffergeometry.setAttribute( 'color', new Float32BufferAttribute( state.colors, 3 ) );
- material.vertexColors = true;
- }
- var points = new Points( buffergeometry, material );
- container.add( points );
- }
- }
- return container;
- }
- } );
- return OBJLoader;
- } )();
- /**
- * Loads a Wavefront .mtl file specifying materials
- */
- var MTLLoader = function ( manager ) {
- Loader.call( this, manager );
- };
- MTLLoader.prototype = Object.assign( Object.create( Loader.prototype ), {
- constructor: MTLLoader,
- /**
- * Loads and parses a MTL asset from a URL.
- *
- * @param {String} url - URL to the MTL file.
- * @param {Function} [onLoad] - Callback invoked with the loaded object.
- * @param {Function} [onProgress] - Callback for download progress.
- * @param {Function} [onError] - Callback for download errors.
- *
- * @see setPath setResourcePath
- *
- * @note In order for relative texture references to resolve correctly
- * you must call setResourcePath() explicitly prior to load.
- */
- load: function ( url, onLoad, onProgress, onError ) {
- var scope = this;
- var path = ( this.path === '' ) ? LoaderUtils.extractUrlBase( url ) : this.path;
- var loader = new FileLoader( this.manager );
- loader.setPath( this.path );
- loader.setRequestHeader( this.requestHeader );
- loader.setWithCredentials( this.withCredentials );
- loader.load( url, function ( text ) {
- try {
- onLoad( scope.parse( text, path ) );
- } catch ( e ) {
- if ( onError ) {
- onError( e );
- } else {
- console.error( e );
- }
- scope.manager.itemError( url );
- }
- }, onProgress, onError );
- },
- setMaterialOptions: function ( value ) {
- this.materialOptions = value;
- return this;
- },
- /**
- * Parses a MTL file.
- *
- * @param {String} text - Content of MTL file
- * @return {MTLLoader.MaterialCreator}
- *
- * @see setPath setResourcePath
- *
- * @note In order for relative texture references to resolve correctly
- * you must call setResourcePath() explicitly prior to parse.
- */
- parse: function ( text, path ) {
- var lines = text.split( '\n' );
- var info = {};
- var delimiter_pattern = /\s+/;
- var materialsInfo = {};
- for ( var i = 0; i < lines.length; i ++ ) {
- var line = lines[ i ];
- line = line.trim();
- if ( line.length === 0 || line.charAt( 0 ) === '#' ) {
- // Blank line or comment ignore
- continue;
- }
- var pos = line.indexOf( ' ' );
- var key = ( pos >= 0 ) ? line.substring( 0, pos ) : line;
- key = key.toLowerCase();
- var value = ( pos >= 0 ) ? line.substring( pos + 1 ) : '';
- value = value.trim();
- if ( key === 'newmtl' ) {
- // New material
- info = { name: value };
- materialsInfo[ value ] = info;
- } else {
- if ( key === 'ka' || key === 'kd' || key === 'ks' || key === 'ke' ) {
- var ss = value.split( delimiter_pattern, 3 );
- info[ key ] = [ parseFloat( ss[ 0 ] ), parseFloat( ss[ 1 ] ), parseFloat( ss[ 2 ] ) ];
- } else {
- info[ key ] = value;
- }
- }
- }
- var materialCreator = new MTLLoader.MaterialCreator( this.resourcePath || path, this.materialOptions );
- materialCreator.setCrossOrigin( this.crossOrigin );
- materialCreator.setManager( this.manager );
- materialCreator.setMaterials( materialsInfo );
- return materialCreator;
- }
- } );
- /**
- * Create a new MTLLoader.MaterialCreator
- * @param baseUrl - Url relative to which textures are loaded
- * @param options - Set of options on how to construct the materials
- * side: Which side to apply the material
- * FrontSide (default), THREE.BackSide, THREE.DoubleSide
- * wrap: What type of wrapping to apply for textures
- * RepeatWrapping (default), THREE.ClampToEdgeWrapping, THREE.MirroredRepeatWrapping
- * normalizeRGB: RGBs need to be normalized to 0-1 from 0-255
- * Default: false, assumed to be already normalized
- * ignoreZeroRGBs: Ignore values of RGBs (Ka,Kd,Ks) that are all 0's
- * Default: false
- * @constructor
- */
- MTLLoader.MaterialCreator = function ( baseUrl, options ) {
- this.baseUrl = baseUrl || '';
- this.options = options;
- this.materialsInfo = {};
- this.materials = {};
- this.materialsArray = [];
- this.nameLookup = {};
- this.side = ( this.options && this.options.side ) ? this.options.side : FrontSide;
- this.wrap = ( this.options && this.options.wrap ) ? this.options.wrap : RepeatWrapping;
- };
- MTLLoader.MaterialCreator.prototype = {
- constructor: MTLLoader.MaterialCreator,
- crossOrigin: 'anonymous',
- setCrossOrigin: function ( value ) {
- this.crossOrigin = value;
- return this;
- },
- setManager: function ( value ) {
- this.manager = value;
- },
- setMaterials: function ( materialsInfo ) {
- this.materialsInfo = this.convert( materialsInfo );
- this.materials = {};
- this.materialsArray = [];
- this.nameLookup = {};
- },
- convert: function ( materialsInfo ) {
- if ( ! this.options ) return materialsInfo;
- var converted = {};
- for ( var mn in materialsInfo ) {
- // Convert materials info into normalized form based on options
- var mat = materialsInfo[ mn ];
- var covmat = {};
- converted[ mn ] = covmat;
- for ( var prop in mat ) {
- var save = true;
- var value = mat[ prop ];
- var lprop = prop.toLowerCase();
- switch ( lprop ) {
- case 'kd':
- case 'ka':
- case 'ks':
- // Diffuse color (color under white light) using RGB values
- if ( this.options && this.options.normalizeRGB ) {
- value = [ value[ 0 ] / 255, value[ 1 ] / 255, value[ 2 ] / 255 ];
- }
- if ( this.options && this.options.ignoreZeroRGBs ) {
- if ( value[ 0 ] === 0 && value[ 1 ] === 0 && value[ 2 ] === 0 ) {
- // ignore
- save = false;
- }
- }
- break;
- default:
- break;
- }
- if ( save ) {
- covmat[ lprop ] = value;
- }
- }
- }
- return converted;
- },
- preload: function () {
- for ( var mn in this.materialsInfo ) {
- this.create( mn );
- }
- },
- getIndex: function ( materialName ) {
- return this.nameLookup[ materialName ];
- },
- getAsArray: function () {
- var index = 0;
- for ( var mn in this.materialsInfo ) {
- this.materialsArray[ index ] = this.create( mn );
- this.nameLookup[ mn ] = index;
- index ++;
- }
- return this.materialsArray;
- },
- create: function ( materialName ) {
- if ( this.materials[ materialName ] === undefined ) {
- this.createMaterial_( materialName );
- }
- return this.materials[ materialName ];
- },
- createMaterial_: function ( materialName ) {
- // Create material
- var scope = this;
- var mat = this.materialsInfo[ materialName ];
- var params = {
- name: materialName,
- side: this.side
- };
- function resolveURL( baseUrl, url ) {
- if ( typeof url !== 'string' || url === '' )
- return '';
- // Absolute URL
- if ( /^https?:\/\//i.test( url ) ) return url;
- return baseUrl + url;
- }
- function setMapForType( mapType, value ) {
- if ( params[ mapType ] ) return; // Keep the first encountered texture
- var texParams = scope.getTextureParams( value, params );
- var map = scope.loadTexture( resolveURL( scope.baseUrl, texParams.url ) );
- map.repeat.copy( texParams.scale );
- map.offset.copy( texParams.offset );
- map.wrapS = scope.wrap;
- map.wrapT = scope.wrap;
- params[ mapType ] = map;
- }
- for ( var prop in mat ) {
- var value = mat[ prop ];
- var n;
- if ( value === '' ) continue;
- switch ( prop.toLowerCase() ) {
- // Ns is material specular exponent
- case 'kd':
- // Diffuse color (color under white light) using RGB values
- params.color = new Color().fromArray( value );
- break;
- case 'ks':
- // Specular color (color when light is reflected from shiny surface) using RGB values
- //params.specular = new Color().fromArray( value );
- //console.log('specular',value)
- break;
- case 'ke':
- // Emissive using RGB values
- params.emissive = new Color().fromArray( value );
- break;
- case 'map_kd':
- // Diffuse texture map
- setMapForType( 'map', value );
- break;
- case 'map_ks':
- // Specular map
- setMapForType( 'specularMap', value );
- break;
- case 'map_ke':
- // Emissive map
- setMapForType( 'emissiveMap', value );
- break;
- case 'norm':
- setMapForType( 'normalMap', value );
- break;
- case 'map_bump':
- case 'bump':
- // Bump texture map
- setMapForType( 'bumpMap', value );
- break;
- case 'map_d':
- // Alpha map
- setMapForType( 'alphaMap', value );
- params.transparent = true;
- break;
- case 'ns':
- // The specular exponent (defines the focus of the specular highlight)
- // A high exponent results in a tight, concentrated highlight. Ns values normally range from 0 to 1000.
- //params.shininess = parseFloat( value );
- //console.log('shininess',value)
-
- break;
- case 'd':
- n = parseFloat( value );
- if ( n < 1 ) {
- params.opacity = n;
- params.transparent = true;
- }
- break;
- case 'tr':
- n = parseFloat( value );
- if ( this.options && this.options.invertTrProperty ) n = 1 - n;
- if ( n > 0 ) {
- params.opacity = 1 - n;
- params.transparent = true;
- }
- break;
- default:
- break;
- }
- }
- this.materials[ materialName ] = new MeshStandardMaterial( params );//MeshPhongMaterial( params );
- return this.materials[ materialName ];
- },
- getTextureParams: function ( value, matParams ) {
- var texParams = {
- scale: new Vector2$1( 1, 1 ),
- offset: new Vector2$1( 0, 0 )
- };
- var items = value.split( /\s+/ );
- var pos;
- pos = items.indexOf( '-bm' );
- if ( pos >= 0 ) {
- matParams.bumpScale = parseFloat( items[ pos + 1 ] );
- items.splice( pos, 2 );
- }
- pos = items.indexOf( '-s' );
- if ( pos >= 0 ) {
- texParams.scale.set( parseFloat( items[ pos + 1 ] ), parseFloat( items[ pos + 2 ] ) );
- items.splice( pos, 4 ); // we expect 3 parameters here!
- }
- pos = items.indexOf( '-o' );
- if ( pos >= 0 ) {
- texParams.offset.set( parseFloat( items[ pos + 1 ] ), parseFloat( items[ pos + 2 ] ) );
- items.splice( pos, 4 ); // we expect 3 parameters here!
- }
- texParams.url = items.join( ' ' ).trim();
- return texParams;
- },
- loadTexture: function ( url, mapping, onLoad, onProgress, onError ) {
- var texture;
- var manager = ( this.manager !== undefined ) ? this.manager : DefaultLoadingManager;
- var loader = manager.getHandler( url );
- if ( loader === null ) {
- loader = new TextureLoader( manager );
- }
- if ( loader.setCrossOrigin ) loader.setCrossOrigin( this.crossOrigin );
- texture = loader.load( url, onLoad, onProgress, onError );
- if ( mapping !== undefined ) texture.mapping = mapping;
- return texture;
- }
- };
- /**
- * @author alteredq / http://alteredqualia.com/
- */
-
- let Pass = function () {
- // if set to true, the pass is processed by the composer
- this.enabled = true;
- // if set to true, the pass indicates to swap read and write buffer after rendering
- this.needsSwap = true;
- // if set to true, the pass clears its buffer before rendering
- this.clear = false;
- // if set to true, the result of the pass is rendered to screen
- this.renderToScreen = false;
- };
- Object.assign( Pass.prototype, {
- setSize: function ( width, height ) {},
- render: function ( renderer, writeBuffer, readBuffer, delta, maskActive ) {
- console.error( 'THREE.Pass: .render() must be implemented in derived pass.' );
- }
- } );
- let ShaderPass = function ( shader, textureID ) {
- Pass.call( this );
- this.textureID = ( textureID !== undefined ) ? textureID : "tDiffuse";
- if ( shader instanceof ShaderMaterial ) {
- this.uniforms = shader.uniforms;
- this.material = shader;
- } else if ( shader ) {
- this.uniforms = UniformsUtils.clone( shader.uniforms );
- this.material = new ShaderMaterial( {
- defines: Object.assign( {}, shader.defines ),
- uniforms: this.uniforms,
- vertexShader: shader.vertexShader,
- fragmentShader: shader.fragmentShader,
- transparent:true,//add
-
- } );
- }
- this.camera = new OrthographicCamera( - 1, 1, 1, - 1, 0, 1 );
- this.scene = new Scene();
- this.quad = new Mesh( new PlaneBufferGeometry( 2, 2 ), null );
- this.quad.frustumCulled = false; // Avoid getting clipped
- this.scene.add( this.quad );
- };
- ShaderPass.prototype = Object.assign( Object.create( Pass.prototype ), {
- constructor: ShaderPass,
- render: function(scene,camera, renderer, writeBuffer, readBuffer, delta, maskActive ) {
- let oldTarget = renderer.getRenderTarget();
- /* if(this.readTarget){ //add
- readBuffer = oldTarget
- } */
- if ( this.uniforms[ this.textureID ] ) {
- this.uniforms[ this.textureID ].value = readBuffer.texture;
- }
- this.quad.material = this.material;
-
- if ( this.renderToScreen ) {
-
- renderer.render( this.scene, this.camera );
- } else {
- renderer.setRenderTarget(writeBuffer);
- if(this.clear) renderer.clear();
- renderer.render( this.scene, this.camera );
- renderer.setRenderTarget(oldTarget);
-
-
- }
- }
- } );
- /**
- * @author alteredq / http://alteredqualia.com/
- *
- * Full-screen textured quad shader
- */
- let CopyShader = {
- uniforms: {
- "tDiffuse": { value: null },
- "opacity": { value: 1.0 }
- },
- vertexShader: [
- "varying vec2 vUv;",
- "void main() {",
- "vUv = uv;",
- "gl_Position = projectionMatrix * modelViewMatrix * vec4( position, 1.0 );",
- "}"
- ].join( "\n" ),
- fragmentShader: [
- "uniform float opacity;",
- "uniform sampler2D tDiffuse;",
- "varying vec2 vUv;",
- "void main() {",
- "vec4 texel = texture2D( tDiffuse, vUv );", //如果开启premultipliedAlpha用这个,否则用注释的
- "gl_FragColor = opacity * texel;",
-
- //"gl_FragColor = texture2D( tDiffuse, vUv );",
- //"gl_FragColor.a *= opacity;",
- "}"
- ].join( "\n" )
- };
- /**
- *
- * Supersample Anti-Aliasing Render Pass
- *
- * @author bhouston / http://clara.io/
- *
- * This manual approach to SSAA re-renders the scene ones for each sample with camera jitter and accumulates the results.
- *
- * References: https://en.wikipedia.org/wiki/Supersampling
- *
- */
- //较为原始的一种抗锯齿 (超级采样抗锯齿)
- let SSAARenderPass = function ( clearColor, clearAlpha ) {
- Pass.call( this );
- //this.scene //= scene;
- //this.camera = camera;
- this.sampleLevel = 4; // specified as n, where the number of samples is 2^n, so sampleLevel = 4, is 2^4 samples, 16.
- this.unbiased = true;
- // as we need to clear the buffer in this pass, clearColor must be set to something, defaults to black.
- this.clearColor = ( clearColor !== undefined ) ? clearColor : 0x000000;
- this.clearAlpha = ( clearAlpha !== undefined ) ? clearAlpha : 0;
-
- this.renderUniforms = {
- bgTex : {value:null},
- outlineTex : {value:null},
- opacity : {value:1},
- };
-
- this.renderMat = new ShaderMaterial({
- uniforms: this.renderUniforms,
- vertexShader: CopyShader.vertexShader,
- /* fragmentShader: CopyShader.fragmentShader, */
- fragmentShader: `
- uniform sampler2D bgTex;
- uniform sampler2D outlineTex;
- uniform float opacity;
- varying vec2 vUv;
- void main() {
- vec4 color1 = texture2D( bgTex, vUv );
- vec4 color2 = texture2D( outlineTex, vUv );
- gl_FragColor = opacity * mix(color1, color2, color2.a) ;
-
-
- }
- `,
- premultipliedAlpha: true,
- blending: AdditiveBlending,
- depthTest: false,
- depthWrite: false,
- transparent: true
- });
-
-
- this.renderMat2 = new ShaderMaterial({
- uniforms: UniformsUtils.clone(CopyShader.uniforms) ,
- vertexShader: CopyShader.vertexShader,
- fragmentShader:`uniform float opacity;
- uniform sampler2D tDiffuse;
- varying vec2 vUv;
- void main() {
-
- vec4 texel = texture2D( tDiffuse, vUv );
-
- if(texel.r == 0.0 && texel.g == 0.0 && texel.b == 0.0){
- discard;
- }else{
- gl_FragColor = opacity * texel;
- }
- }
- ` ,
-
-
- depthTest: false,
- depthWrite: false,
- transparent: true
- });
-
-
-
-
-
-
- ////////////////////
- /* this.renderMat.blendSrc = THREE.OneFactor //即将写入缓冲区的颜色。
- this.renderMat.blendDst = THREE.OneFactor //缓冲区已经存在的颜色
- this.renderMat.blendEquation = THREE.AddEquation;
- this.renderMat.blendEquationAlpha = THREE.AddEquation;
- this.renderMat.blendDstAlpha = THREE.SrcAlphaFactor
- this.renderMat.blendSrcAlpha = THREE.SrcAlphaFactor */
- this.camera2 = new OrthographicCamera( - 1, 1, 1, - 1, 0, 1 );
- this.scene2 = new Scene();
- this.quad2 = new Mesh( new PlaneBufferGeometry( 2, 2 ), this.renderMat/* this.copyMaterial */ );
- this.quad2.frustumCulled = false; // Avoid getting clipped
- this.scene2.add( this.quad2 );
- this.copyPass = new ShaderPass( CopyShader );
- this.copyPass.renderToScreen = true;
- };
- SSAARenderPass.prototype = Object.assign( Object.create( Pass.prototype ), {
- constructor: SSAARenderPass,
- dispose: function () {
- if ( this.sampleRenderTarget ) {
- this.sampleRenderTarget.dispose();
- this.sampleRenderTarget = null;
- }
- },
- setSize: function ( width, height ) {
- if ( this.sampleRenderTarget ) this.sampleRenderTarget.setSize( width, height );
- this.childPass && this.childPass.setSize(width, height);
-
- },
- addPass: function (pass){
- this.childPass = pass;
- },
- render: function (scene, camera, renderer, writeBuffer, readBuffer, maskActive, renderFun ) {
- if(this.useCopy ){
- scene = this.copyPass.scene; camera = this.copyPass.camera;
- }
- if ( ! this.sampleRenderTarget ) {
- this.sampleRenderTarget = new WebGLRenderTarget( readBuffer.width, readBuffer.height, { minFilter: LinearFilter, magFilter: LinearFilter, format: RGBAFormat } );
- this.sampleRenderTarget.texture.name = "SSAARenderPass.sample";
- }
- var jitterOffsets = SSAARenderPass.JitterVectors[ Math.max( 0, Math.min( this.sampleLevel, 5 ) ) ];
- var autoClear = renderer.autoClear;
- renderer.autoClear = false;
- var oldClearColor = renderer.getClearColor(new Color).getHex();
- var oldClearAlpha = renderer.getClearAlpha();
- renderer.setClearColor( this.clearColor, this.clearAlpha );
-
- var baseSampleWeight = 1.0 / jitterOffsets.length;
- var roundingRange = 1 / 32;
- //this.copyUniforms[ "tDiffuse" ].value = this.sampleRenderTarget.texture;
-
- let oldTarget = renderer.getRenderTarget();
-
- if(oldTarget){
- if(oldTarget.scissorTest){
- var width = oldTarget.scissor.w, height = oldTarget.scissor.z;
- }else {
- var width = oldTarget.width, height = oldTarget.height;
- }
- }else {
- var width = readBuffer.width, height = readBuffer.height;
- }
-
- // render the scene multiple times, each slightly jitter offset from the last and accumulate the results.
-
- let opa = 0;
- for ( var i = 0; i < jitterOffsets.length; i ++ ) {
- var jitterOffset = jitterOffsets[ i ];
- if ( camera.setViewOffset ) {
- camera.setViewOffset( width, height,
- jitterOffset[ 0 ] * 0.0625 , jitterOffset[ 1 ] * 0.0625 , // 0.0625 = 1 / 16
- width, height );
- }
- var sampleWeight = baseSampleWeight;
- if ( this.unbiased ) {//更柔和
- var uniformCenteredDistribution = ( - 0.5 + ( i + 0.5 ) / jitterOffsets.length );
- sampleWeight += roundingRange * uniformCenteredDistribution;
- }
-
- renderer.setRenderTarget(this.sampleRenderTarget);
- renderer.clear();
- if(this.useCopy){
- this.copyPass.render(scene,camera, renderer, writeBuffer, readBuffer );
- }else {
- if(renderFun){
- renderFun({target : this.sampleRenderTarget});
- }else {
- renderer.render( scene, camera );
- }
- }
- renderer.setRenderTarget(oldTarget);
-
- //---------------------
- //获取outline tex
- let hasOutline = this.childPass && this.childPass.render(scene, camera, renderer, writeBuffer, readBuffer, null, renderFun );
-
-
- //合成到该材质
- this.renderUniforms[ "bgTex" ].value = this.sampleRenderTarget.texture;
- this.renderUniforms[ "outlineTex" ].value = hasOutline ? readBuffer.texture : null;
- this.renderUniforms[ "opacity" ].value = sampleWeight;
-
-
-
- /* console.log('sampleWeight', sampleWeight)
- opa += sampleWeight */
-
- if(!this.renderToScreen){
- renderer.setRenderTarget(writeBuffer);
- }
- if(i === 0 ){
- renderer.setClearColor( 0x000000, 0 ); //叠加前颜色必须0
- renderer.clear();
- }
- renderer.render( this.scene2, this.camera2); // , this.renderToScreen ? null : writeBuffer, ( i === 0 )
- if(!this.renderToScreen){
- renderer.setRenderTarget(oldTarget);
- }
- //if(i==2)break;
- }
- //console.log('sum:',opa)
- if ( camera.clearViewOffset )camera.clearViewOffset();
- //renderer.setRenderTarget(readBuffer)
- //renderer.setClearColor( 0x000000, 0 );
- //renderer.clear()
- /* this.quad2.material = this.renderMat2
- this.renderMat2.uniforms.tDiffuse.value = writeBuffer.texture;
- renderer.render( this.scene2, this.camera2);
- this.quad2.material = this.renderMat */
- //renderer.setRenderTarget(oldTarget)
-
-
-
- renderer.autoClear = autoClear;
- renderer.setClearColor( oldClearColor, oldClearAlpha );
-
-
-
-
- /* 试了好几次,测量线的透明度还是还原不了。 clearAlpha十分影响结果。
- 因为绘制测量线需要背景透明。 或许可以先全部绘制完后,再 copyshader中 抗锯齿?
-
-
- 另外会有黑边。
- */
- }
- } );
- // These jitter vectors are specified in integers because it is easier.
- // I am assuming a [-8,8) integer grid, but it needs to be mapped onto [-0.5,0.5)
- // before being used, thus these integers need to be scaled by 1/16.
- //
- // Sample patterns reference: https://msdn.microsoft.com/en-us/library/windows/desktop/ff476218%28v=vs.85%29.aspx?f=255&MSPPError=-2147217396
- SSAARenderPass.JitterVectors = [
- [
- [ 0, 0 ]
- ],
- [
- [ 4, 4 ], [ - 4, - 4 ]
- ],
- [
- [ - 2, - 6 ], [ 6, - 2 ], [ - 6, 2 ], [ 2, 6 ]
- ],
- [
- [ 1, - 3 ], [ - 1, 3 ], [ 5, 1 ], [ - 3, - 5 ],
- [ - 5, 5 ], [ - 7, - 1 ], [ 3, 7 ], [ 7, - 7 ]
- ],
- [
- [ 1, 1 ], [ - 1, - 3 ], [ - 3, 2 ], [ 4, - 1 ],
- [ - 5, - 2 ], [ 2, 5 ], [ 5, 3 ], [ 3, - 5 ],
- [ - 2, 6 ], [ 0, - 7 ], [ - 4, - 6 ], [ - 6, 4 ],
- [ - 8, 0 ], [ 7, - 4 ], [ 6, 7 ], [ - 7, - 8 ]
- ],
- [
- [ - 4, - 7 ], [ - 7, - 5 ], [ - 3, - 5 ], [ - 5, - 4 ],
- [ - 1, - 4 ], [ - 2, - 2 ], [ - 6, - 1 ], [ - 4, 0 ],
- [ - 7, 1 ], [ - 1, 2 ], [ - 6, 3 ], [ - 3, 3 ],
- [ - 7, 6 ], [ - 3, 6 ], [ - 5, 7 ], [ - 1, 7 ],
- [ 5, - 7 ], [ 1, - 6 ], [ 6, - 5 ], [ 4, - 4 ],
- [ 2, - 3 ], [ 7, - 2 ], [ 1, - 1 ], [ 4, - 1 ],
- [ 2, 1 ], [ 6, 2 ], [ 0, 4 ], [ 4, 4 ],
- [ 2, 5 ], [ 7, 5 ], [ 5, 6 ], [ 3, 7 ]
- ]
- ];
- class MaskPass extends Pass {
- constructor( scene, camera ) {
- super();
- this.scene = scene;
- this.camera = camera;
- this.clear = true;
- this.needsSwap = false;
- this.inverse = false;
- }
- render( renderer, writeBuffer, readBuffer /*, deltaTime, maskActive */ ) {
- const context = renderer.getContext();
- const state = renderer.state;
- // don't update color or depth
- state.buffers.color.setMask( false );
- state.buffers.depth.setMask( false );
- // lock buffers
- state.buffers.color.setLocked( true );
- state.buffers.depth.setLocked( true );
- // set up stencil
- let writeValue, clearValue;
- if ( this.inverse ) {
- writeValue = 0;
- clearValue = 1;
- } else {
- writeValue = 1;
- clearValue = 0;
- }
- state.buffers.stencil.setTest( true );
- state.buffers.stencil.setOp( context.REPLACE, context.REPLACE, context.REPLACE );
- state.buffers.stencil.setFunc( context.ALWAYS, writeValue, 0xffffffff );
- state.buffers.stencil.setClear( clearValue );
- state.buffers.stencil.setLocked( true );
- // draw into the stencil buffer
- renderer.setRenderTarget( readBuffer );
- if ( this.clear ) renderer.clear();
- renderer.render( this.scene, this.camera );
- renderer.setRenderTarget( writeBuffer );
- if ( this.clear ) renderer.clear();
- renderer.render( this.scene, this.camera );
- // unlock color and depth buffer for subsequent rendering
- state.buffers.color.setLocked( false );
- state.buffers.depth.setLocked( false );
- // only render where stencil is set to 1
- state.buffers.stencil.setLocked( false );
- state.buffers.stencil.setFunc( context.EQUAL, 1, 0xffffffff ); // draw if == 1
- state.buffers.stencil.setOp( context.KEEP, context.KEEP, context.KEEP );
- state.buffers.stencil.setLocked( true );
- }
- }
- class ClearMaskPass extends Pass {
- constructor() {
- super();
- this.needsSwap = false;
- }
- render( renderer /*, writeBuffer, readBuffer, deltaTime, maskActive */ ) {
- renderer.state.buffers.stencil.setLocked( false );
- renderer.state.buffers.stencil.setTest( false );
- }
- }
- /**
- * @author alteredq / http://alteredqualia.com/
- */
- var EffectComposer = function ( renderer, renderTarget ) {
- this.renderer = renderer;
- if ( renderTarget === undefined ) {
- var parameters = {
- minFilter: LinearFilter,
- magFilter: LinearFilter,
- format: RGBAFormat,
- stencilBuffer: false,
- };
- var size = renderer.getDrawingBufferSize();
- renderTarget = new WebGLRenderTarget( size.width, size.height , parameters );
- renderTarget.texture.name = 'EffectComposer.rt1';
- }
- this.renderTarget1 = renderTarget;
- this.renderTarget2 = renderTarget.clone();
- this.renderTarget2.texture.name = 'EffectComposer.rt2';
- this.writeBuffer = this.renderTarget1;
- this.readBuffer = this.renderTarget2;
- this.passes = [];
- // dependencies
- /* if ( THREE.CopyShader === undefined ) {
- console.error( 'THREE.EffectComposer relies on THREE.CopyShader' );
- }
- if ( THREE.ShaderPass === undefined ) {
- console.error( 'THREE.EffectComposer relies on THREE.ShaderPass' );
- } */
- this.copyPass = new ShaderPass( CopyShader );
-
- };
- Object.assign( EffectComposer.prototype, {
- swapBuffers: function () {
- var tmp = this.readBuffer;
- this.readBuffer = this.writeBuffer;
- this.writeBuffer = tmp;
- },
- addPass: function ( pass ) {
- this.passes.push( pass );
- var size = this.renderer.getDrawingBufferSize();
- pass.setSize( size.width, size.height );
- },
- insertPass: function ( pass, index ) {
- this.passes.splice( index, 0, pass );
- },
- render: function ( scene, camera, renderFun ) {
- var maskActive = false;
- var pass, i, il = this.passes.length;
- if(this.readTarget){ //add 使用当前renderTarget中的像素
- this.copyPass.render(scene,camera, this.renderer, this.readBuffer, this.renderer.getRenderTarget() );
- }
-
-
- for ( i = 0; i < il; i ++ ) {
- pass = this.passes[ i ];
- if ( pass.enabled === false ) continue;
- //if(i == il-1)pass.renderToScreen = true//
-
- pass.render( scene, camera, this.renderer, this.writeBuffer, this.readBuffer, maskActive, renderFun );
-
- if ( pass.needsSwap ) {
- if ( maskActive ) {
- var context = this.renderer.context;
- context.stencilFunc( context.NOTEQUAL, 1, 0xffffffff );
-
- this.copyPass.render( this.renderer, this.writeBuffer, this.readBuffer );// delta
- context.stencilFunc( context.EQUAL, 1, 0xffffffff );
- }
- this.swapBuffers();
- }
- if ( MaskPass !== undefined ) {
- if ( pass instanceof MaskPass ) {
- maskActive = true;
- } else if ( pass instanceof ClearMaskPass ) {
- maskActive = false;
- }
- }
- }
-
-
- //add
- if(!pass.renderToScreen){ //最后一个如果没有绘制到屏幕or target上
- this.copyPass.renderToScreen = true;
-
- this.copyPass.render(null,null, this.renderer, this.writeBuffer, this.readBuffer);
-
- }
-
- },
- reset: function ( renderTarget ) {
- if ( renderTarget === undefined ) {
- var size = this.renderer.getDrawingBufferSize();
- renderTarget = this.renderTarget1.clone();
- renderTarget.setSize( size.width, size.height );
- }
- this.renderTarget1.dispose();
- this.renderTarget2.dispose();
- this.renderTarget1 = renderTarget;
- this.renderTarget2 = renderTarget.clone();
- this.writeBuffer = this.renderTarget1;
- this.readBuffer = this.renderTarget2;
- },
- setSize: function ( width, height, scaleRatio ) {
- scaleRatio = scaleRatio || 1;
- this.renderTarget1.setSize( width * scaleRatio , height * scaleRatio );
- this.renderTarget2.setSize( width * scaleRatio, height * scaleRatio );
- for ( var i = 0; i < this.passes.length; i ++ ) {
- this.passes[ i ].setSize( width * scaleRatio, height * scaleRatio );
- }
- }
- } );
- /**
- * @author alteredq / http://alteredqualia.com/
- */
- class RenderPass extends Pass {
- constructor( overrideMaterial, clearColor, clearAlpha ) {
- super();
- /* this.scene = scene;
- this.camera = camera; */
- this.overrideMaterial = overrideMaterial;
- this.clearColor = clearColor;
- this.clearAlpha = ( clearAlpha !== undefined ) ? clearAlpha : 0;
- this.clear = true;
- this.clearDepth = false;
- this.needsSwap = false;
- this._oldClearColor = new Color();
- }
- render(scene, camera, renderer, writeBuffer, readBuffer /*, deltaTime, maskActive */ ) {
- const oldAutoClear = renderer.autoClear;
- renderer.autoClear = false;
- let oldClearAlpha, oldOverrideMaterial;
- if ( this.overrideMaterial !== undefined ) {
- oldOverrideMaterial = this.scene.overrideMaterial;
- scene.overrideMaterial = this.overrideMaterial;
- }
- if ( this.clearColor ) {
- renderer.getClearColor( this._oldClearColor );
- oldClearAlpha = renderer.getClearAlpha();
- renderer.setClearColor( this.clearColor, this.clearAlpha );
- }
- if ( this.clearDepth ) {
- renderer.clearDepth();
- }
- let oldTarget = renderer.getRenderTarget();
- if(!this.renderToScreen)renderer.setRenderTarget( readBuffer );
- // TODO: Avoid using autoClear properties, see https://github.com/mrdoob/three.js/pull/15571#issuecomment-465669600
- if ( this.clear ) renderer.clear( renderer.autoClearColor, renderer.autoClearDepth, renderer.autoClearStencil );
- renderer.render( scene, camera );
- if(!this.renderToScreen)renderer.setRenderTarget( oldTarget );
- if ( this.clearColor ) {
- renderer.setClearColor( this._oldClearColor, oldClearAlpha );
- }
- if ( this.overrideMaterial !== undefined ) {
- scene.overrideMaterial = oldOverrideMaterial;
- }
- renderer.autoClear = oldAutoClear;
- }
- }
- /**
- * NVIDIA FXAA by Timothy Lottes
- * https://developer.download.nvidia.com/assets/gamedev/files/sdk/11/FXAA_WhitePaper.pdf
- * - WebGL port by @supereggbert
- * http://www.glge.org/demos/fxaa/
- * Further improved by Daniel Sturk
- */
- const FXAAShader = {
- uniforms: {
- 'tDiffuse': { value: null },
- 'resolution': { value: new Vector2$1( 1 / 1024, 1 / 512 ) }
- },
- vertexShader: /* glsl */`
- varying vec2 vUv;
- void main() {
- vUv = uv;
- gl_Position = projectionMatrix * modelViewMatrix * vec4( position, 1.0 );
- }`,
- fragmentShader: `
- precision highp float;
- uniform sampler2D tDiffuse;
- uniform vec2 resolution;
- varying vec2 vUv;
- // FXAA 3.11 implementation by NVIDIA, ported to WebGL by Agost Biro (biro@archilogic.com)
- //----------------------------------------------------------------------------------
- // File: es3-kepler\FXAA\assets\shaders/FXAA_DefaultES.frag
- // SDK Version: v3.00
- // Email: gameworks@nvidia.com
- // Site: http://developer.nvidia.com/
- //
- // Copyright (c) 2014-2015, NVIDIA CORPORATION. All rights reserved.
- //
- // Redistribution and use in source and binary forms, with or without
- // modification, are permitted provided that the following conditions
- // are met:
- // * Redistributions of source code must retain the above copyright
- // notice, this list of conditions and the following disclaimer.
- // * Redistributions in binary form must reproduce the above copyright
- // notice, this list of conditions and the following disclaimer in the
- // documentation and/or other materials provided with the distribution.
- // * Neither the name of NVIDIA CORPORATION nor the names of its
- // contributors may be used to endorse or promote products derived
- // from this software without specific prior written permission.
- //
- // THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ''AS IS'' AND ANY
- // EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
- // IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
- // PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
- // CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
- // EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
- // PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
- // PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY
- // OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
- // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
- // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
- //
- //----------------------------------------------------------------------------------
- #ifndef FXAA_DISCARD
- //
- // Only valid for PC OpenGL currently.
- // Probably will not work when FXAA_GREEN_AS_LUMA = 1.
- //
- // 1 = Use discard on pixels which don't need AA.
- // For APIs which enable concurrent TEX+ROP from same surface.
- // 0 = Return unchanged color on pixels which don't need AA.
- //
- #define FXAA_DISCARD 0
- #endif
- /*--------------------------------------------------------------------------*/
- #define FxaaTexTop(t, p) texture2D(t, p, -100.0)
- #define FxaaTexOff(t, p, o, r) texture2D(t, p + (o * r), -100.0)
- /*--------------------------------------------------------------------------*/
- #define NUM_SAMPLES 5
- // assumes colors have premultipliedAlpha, so that the calculated color contrast is scaled by alpha
- float contrast( vec4 a, vec4 b ) {
- vec4 diff = abs( a - b );
- return max( max( max( diff.r, diff.g ), diff.b ), diff.a );
- }
- /*============================================================================
- FXAA3 QUALITY - PC
- ============================================================================*/
- /*--------------------------------------------------------------------------*/
- vec4 FxaaPixelShader(
- vec2 posM,
- sampler2D tex,
- vec2 fxaaQualityRcpFrame,
- float fxaaQualityEdgeThreshold,
- float fxaaQualityinvEdgeThreshold
- ) {
- vec4 rgbaM = FxaaTexTop(tex, posM);
- vec4 rgbaS = FxaaTexOff(tex, posM, vec2( 0.0, 1.0), fxaaQualityRcpFrame.xy);
- vec4 rgbaE = FxaaTexOff(tex, posM, vec2( 1.0, 0.0), fxaaQualityRcpFrame.xy);
- vec4 rgbaN = FxaaTexOff(tex, posM, vec2( 0.0,-1.0), fxaaQualityRcpFrame.xy);
- vec4 rgbaW = FxaaTexOff(tex, posM, vec2(-1.0, 0.0), fxaaQualityRcpFrame.xy);
- // . S .
- // W M E
- // . N .
- bool earlyExit = max( max( max(
- contrast( rgbaM, rgbaN ),
- contrast( rgbaM, rgbaS ) ),
- contrast( rgbaM, rgbaE ) ),
- contrast( rgbaM, rgbaW ) )
- < fxaaQualityEdgeThreshold;
- // . 0 .
- // 0 0 0
- // . 0 .
- #if (FXAA_DISCARD == 1)
- if(earlyExit) FxaaDiscard;
- #else
- if(earlyExit) return rgbaM;
- #endif
- float contrastN = contrast( rgbaM, rgbaN );
- float contrastS = contrast( rgbaM, rgbaS );
- float contrastE = contrast( rgbaM, rgbaE );
- float contrastW = contrast( rgbaM, rgbaW );
- float relativeVContrast = ( contrastN + contrastS ) - ( contrastE + contrastW );
- relativeVContrast *= fxaaQualityinvEdgeThreshold;
- bool horzSpan = relativeVContrast > 0.;
- // . 1 .
- // 0 0 0
- // . 1 .
- // 45 deg edge detection and corners of objects, aka V/H contrast is too similar
- if( abs( relativeVContrast ) < .3 ) {
- // locate the edge
- vec2 dirToEdge;
- dirToEdge.x = contrastE > contrastW ? 1. : -1.;
- dirToEdge.y = contrastS > contrastN ? 1. : -1.;
- // . 2 . . 1 .
- // 1 0 2 ~= 0 0 1
- // . 1 . . 0 .
- // tap 2 pixels and see which ones are "outside" the edge, to
- // determine if the edge is vertical or horizontal
- vec4 rgbaAlongH = FxaaTexOff(tex, posM, vec2( dirToEdge.x, -dirToEdge.y ), fxaaQualityRcpFrame.xy);
- float matchAlongH = contrast( rgbaM, rgbaAlongH );
- // . 1 .
- // 0 0 1
- // . 0 H
- vec4 rgbaAlongV = FxaaTexOff(tex, posM, vec2( -dirToEdge.x, dirToEdge.y ), fxaaQualityRcpFrame.xy);
- float matchAlongV = contrast( rgbaM, rgbaAlongV );
- // V 1 .
- // 0 0 1
- // . 0 .
- relativeVContrast = matchAlongV - matchAlongH;
- relativeVContrast *= fxaaQualityinvEdgeThreshold;
- if( abs( relativeVContrast ) < .3 ) { // 45 deg edge
- // 1 1 .
- // 0 0 1
- // . 0 1
- // do a simple blur
- return mix(
- rgbaM,
- (rgbaN + rgbaS + rgbaE + rgbaW) * .25,
- .4
- );
- }
- horzSpan = relativeVContrast > 0.;
- }
- if(!horzSpan) rgbaN = rgbaW;
- if(!horzSpan) rgbaS = rgbaE;
- // . 0 . 1
- // 1 0 1 -> 0
- // . 0 . 1
- bool pairN = contrast( rgbaM, rgbaN ) > contrast( rgbaM, rgbaS );
- if(!pairN) rgbaN = rgbaS;
- vec2 offNP;
- offNP.x = (!horzSpan) ? 0.0 : fxaaQualityRcpFrame.x;
- offNP.y = ( horzSpan) ? 0.0 : fxaaQualityRcpFrame.y;
- bool doneN = false;
- bool doneP = false;
- float nDist = 0.;
- float pDist = 0.;
- vec2 posN = posM;
- vec2 posP = posM;
- int iterationsUsed = 0;
- int iterationsUsedN = 0;
- int iterationsUsedP = 0;
- for( int i = 0; i < NUM_SAMPLES; i++ ) {
- iterationsUsed = i;
- float increment = float(i + 1);
- if(!doneN) {
- nDist += increment;
- posN = posM + offNP * nDist;
- vec4 rgbaEndN = FxaaTexTop(tex, posN.xy);
- doneN = contrast( rgbaEndN, rgbaM ) > contrast( rgbaEndN, rgbaN );
- iterationsUsedN = i;
- }
- if(!doneP) {
- pDist += increment;
- posP = posM - offNP * pDist;
- vec4 rgbaEndP = FxaaTexTop(tex, posP.xy);
- doneP = contrast( rgbaEndP, rgbaM ) > contrast( rgbaEndP, rgbaN );
- iterationsUsedP = i;
- }
- if(doneN || doneP) break;
- }
- if ( !doneP && !doneN ) return rgbaM; // failed to find end of edge
- float dist = min(
- doneN ? float( iterationsUsedN ) / float( NUM_SAMPLES - 1 ) : 1.,
- doneP ? float( iterationsUsedP ) / float( NUM_SAMPLES - 1 ) : 1.
- );
- // hacky way of reduces blurriness of mostly diagonal edges
- // but reduces AA quality
- dist = pow(dist, .5);
- dist = 1. - dist;
- return mix(
- rgbaM,
- rgbaN,
- dist * .5
- );
- }
- void main() {
- const float edgeDetectionQuality = .05 ; //越高,越保留细节;越低,越平滑 但模糊
- const float invEdgeDetectionQuality = 1. / edgeDetectionQuality;
- gl_FragColor = FxaaPixelShader(
- vUv,
- tDiffuse,
- resolution,
- edgeDetectionQuality, // [0,1] contrast needed, otherwise early discard
- invEdgeDetectionQuality
- );
- }
- `
- };
- /**
- * @author spidersharma / http://eduperiment.com/
- */
- let OutlinePass = function ( selectedObjects ) {
- /* scene = scene;
- camera = camera; */
- this.selectedObjects = selectedObjects !== undefined ? selectedObjects : [];
- this.visibleEdgeColor = new Color( 1, 1, 1 );
- this.hiddenEdgeColor = new Color( 0.1, 0.04, 0.02 );
- this.edgeGlow = 0.0;
- this.usePatternTexture = false;
- //this.edgeThickness = 1.0;
- this.edgeStrength = 3.0;
- this.downSampleRatio = 1;//2; // 抗锯齿 值越低renderTarget size越大,抗锯齿越强,线条可越细(或许可以把模糊化去掉?)
- this.pulsePeriod = 0;
- Pass.call( this );
- this.resolution = new Vector2$1( 256, 256 );
- var pars = { minFilter: LinearFilter, magFilter: LinearFilter, format: RGBAFormat };
- var resx = Math.round( this.resolution.x / this.downSampleRatio );
- var resy = Math.round( this.resolution.y / this.downSampleRatio );
- //this.maskBufferMaterial = new THREE.MeshBasicMaterial( { color: 0xffffff } );
- //this.maskBufferMaterial.side = THREE.DoubleSide;
- this.renderTargetMaskBuffer = new WebGLRenderTarget( this.resolution.x, this.resolution.y, pars );
- this.renderTargetMaskBuffer.texture.name = "OutlinePass.mask";
- this.renderTargetMaskBuffer.texture.generateMipmaps = false;
- this.depthMaterial = new MeshDepthMaterial();
- this.depthMaterial.side = DoubleSide;
- this.depthMaterial.depthPacking = RGBADepthPacking;
- this.depthMaterial.blending = NoBlending;
- this.prepareMaskMaterial = this.getPrepareMaskMaterial();
- this.prepareMaskMaterial.side = DoubleSide;
- //this.replaceDepthToViewZ( viewer.mainViewport.camera /* camera */ );
- this.renderTargetDepthBuffer = new WebGLRenderTarget( this.resolution.x, this.resolution.y, pars );
- this.renderTargetDepthBuffer.texture.name = "OutlinePass.depth";
- this.renderTargetDepthBuffer.texture.generateMipmaps = false;
- /* this.renderTargetMaskDownSampleBuffer = new THREE.WebGLRenderTarget( resx, resy, pars );
- this.renderTargetMaskDownSampleBuffer.texture.name = "OutlinePass.depthDownSample";
- this.renderTargetMaskDownSampleBuffer.texture.generateMipmaps = false;
- this.renderTargetBlurBuffer1 = new THREE.WebGLRenderTarget( resx, resy, pars );
- this.renderTargetBlurBuffer1.texture.name = "OutlinePass.blur1";
- this.renderTargetBlurBuffer1.texture.generateMipmaps = false;
- this.renderTargetBlurBuffer2 = new THREE.WebGLRenderTarget( Math.round( resx / 2 ), Math.round( resy / 2 ), pars );
- this.renderTargetBlurBuffer2.texture.name = "OutlinePass.blur2";
- this.renderTargetBlurBuffer2.texture.generateMipmaps = false; */
- this.edgeDetectionMaterial = this.getEdgeDetectionMaterial(this.edgeStrength);
- this.renderTargetEdgeBuffer1 = new WebGLRenderTarget( resx, resy, pars );
- this.renderTargetEdgeBuffer1.texture.name = "OutlinePass.edge1";
- this.renderTargetEdgeBuffer1.texture.generateMipmaps = false;
- /* this.renderTargetEdgeBuffer2 = new THREE.WebGLRenderTarget( Math.round( resx / 2 ), Math.round( resy / 2 ), pars );
- this.renderTargetEdgeBuffer2.texture.name = "OutlinePass.edge2";
- this.renderTargetEdgeBuffer2.texture.generateMipmaps = false;
- var MAX_EDGE_THICKNESS = 4;
- var MAX_EDGE_GLOW = 4;
- this.separableBlurMaterial1 = this.getSeperableBlurMaterial( MAX_EDGE_THICKNESS );
- this.separableBlurMaterial1.uniforms[ "texSize" ].value = new THREE.Vector2( resx, resy );
- this.separableBlurMaterial1.uniforms[ "kernelRadius" ].value = 1;
- this.separableBlurMaterial2 = this.getSeperableBlurMaterial( MAX_EDGE_GLOW );
- this.separableBlurMaterial2.uniforms[ "texSize" ].value = new THREE.Vector2( Math.round( resx / 2 ), Math.round( resy / 2 ) );
- this.separableBlurMaterial2.uniforms[ "kernelRadius" ].value = MAX_EDGE_GLOW;
- */
- // Overlay material
- this.overlayMaterial = this.getOverlayMaterial();
- // copy material
- this.copyUniforms = UniformsUtils.clone( CopyShader.uniforms );
- this.copyUniforms[ "opacity" ].value = 1.0;
- this.materialCopy = new ShaderMaterial( {
- uniforms: this.copyUniforms,
- vertexShader: CopyShader.vertexShader,
- fragmentShader: CopyShader.fragmentShader,
- blending: NoBlending,
- depthTest: false,
- depthWrite: false,
- transparent: true
- } );
-
- this.enabled = true;
- this.needsSwap = false;
- this.oldClearColor = new Color();
- this.oldClearAlpha = 1;
- this.camera = new OrthographicCamera( - 1, 1, 1, - 1, 0, 1 );
- this.scene = new Scene();
- this.quad = new Mesh( new PlaneBufferGeometry( 2, 2 ), null );
- this.quad.frustumCulled = false; // Avoid getting clipped
- this.scene.add( this.quad );
- /* this.tempPulseColor1 = new THREE.Color();
- this.tempPulseColor2 = new THREE.Color(); */
- this.textureMatrix = new Matrix4();
-
- };
- OutlinePass.prototype = Object.assign( Object.create( Pass.prototype ), {
- constructor: OutlinePass,
- dispose: function () {
- this.renderTargetMaskBuffer.dispose();
- this.renderTargetEdgeBuffer1.dispose();
- this.renderTargetDepthBuffer.dispose();
- },
-
- replaceDepthToViewZ( camera ) {
- var type = camera.isPerspectiveCamera ? 'perspective' : 'orthographic';
- if(type == this.lastCameraType )return
- this.lastCameraType = type;
- this.prepareMaskMaterial.fragmentShader = this.prepareMaskMaterial.fragmentShader.replace( /DEPTH_TO_VIEW_Z/g, type + 'DepthToViewZ' );
- this.prepareMaskMaterial.needsUpdate = true;
- },
- setSize: function ( width, height ) {
-
- this.renderTargetEdgeBuffer1.setSize( width, height );
- this.renderTargetMaskBuffer.setSize( width, height );
- this.resolution.set(width,height);
- },
- changeVisibilityOfSelectedObjects: function ( bVisible ) {
- function gatherSelectedMeshesCallBack( object ) {
- /* if ( object.isMesh ) { */
- if ( object.isPointcloud || object.isMesh || object.isLine || object.isSprite ) {
- /* if ( bVisible ) {
- object.visible = object.userData.oldVisible;
- delete object.userData.oldVisible;
- } else {
- object.userData.oldVisible = object.visible;
- object.visible = bVisible;
- } */
- viewer.updateVisible(object, 'overlinePass', bVisible);
- }
- }
- for ( var i = 0; i < this.selectedObjects.length; i ++ ) {
- var selectedObject = this.selectedObjects[ i ];
- selectedObject.traverse( gatherSelectedMeshesCallBack );
- }
- },
- changeVisibilityOfNonSelectedObjects: function ( bVisible , scenes) {
- var selectedMeshes = [];
- function gatherSelectedMeshesCallBack( object ) {
- //if ( object.isMesh ) selectedMeshes.push( object );
- if ( object.isPointcloud || object.isMesh || object.isLine || object.isSprite ) {
- selectedMeshes.push( object );
- }
-
- }
- for ( var i = 0; i < this.selectedObjects.length; i ++ ) {
- var selectedObject = this.selectedObjects[ i ];
- selectedObject.traverse( gatherSelectedMeshesCallBack );
- }
- function VisibilityChangeCallBack( object ) {
- if ( object.isPointcloud || object.isMesh || object.isLine || object.isSprite ) {
- var bFound = false;
- for ( var i = 0; i < selectedMeshes.length; i ++ ) {
- var selectedObjectId = selectedMeshes[ i ].id;
- if ( selectedObjectId === object.id ) {
- bFound = true;
- break;
- }
- }
- if ( ! bFound ) {
- /* var visibility = object.visible;
- if(object == viewer.reticule){
- console.log(visibility)
- }
- if ( ! bVisible || object.bVisible ) object.visible = bVisible;
- object.bVisible = visibility; */
-
- viewer.updateVisible(object, 'overlinePass', bVisible);
- //要确保其他所有mesh也都是通过updateVisible来设置visible=false的,否则会在这变为true
- }
- }
- }
- scenes.forEach(scene=>scene.traverse( VisibilityChangeCallBack ));
-
- },
- updateTextureMatrix: function (camera) {
- this.textureMatrix.set( 0.5, 0.0, 0.0, 0.5,
- 0.0, 0.5, 0.0, 0.5,
- 0.0, 0.0, 0.5, 0.5,
- 0.0, 0.0, 0.0, 1.0 );
- this.textureMatrix.multiply( camera.projectionMatrix );
- this.textureMatrix.multiply( camera.matrixWorldInverse );
- },
- render: function (scenes, camera, renderer, writeBuffer, readBuffer, maskActive, renderFun ) {
- if(!(scenes instanceof Array))scenes = [scenes];
- if ( this.selectedObjects.length > 0 ) {
-
- let render2 = (target)=>{
- if(renderFun){
- renderFun({target , dontRenderRtEDL:true});
- }else {
- renderer.setRenderTarget(target);
- renderer.clear();
- scenes.forEach(scene=>renderer.render( scene, camera));
- }
- };
-
- //add
- this.replaceDepthToViewZ( camera );
-
- this.oldClearColor.copy( renderer.getClearColor(new Color) );
- this.oldClearAlpha = renderer.getClearAlpha();
- var oldAutoClear = renderer.autoClear;
- renderer.autoClear = false;
- if ( maskActive ) renderer.context.disable( renderer.context.STENCIL_TEST );
- renderer.setClearColor( 0xffffff, 1 );
- // Make selected objects invisible
- this.changeVisibilityOfSelectedObjects( false );
-
- scenes.forEach(scene=>{
- scene.currentBackground = scene.background;
- scene.background = null;
- // 1. Draw Non Selected objects in the depth buffer
- scene.overrideMaterial = this.depthMaterial;
- });
-
-
-
- render2(this.renderTargetDepthBuffer);
- //renderer.setRenderTarget(this.renderTargetDepthBuffer)
- //renderer.clear()
- //renderer.render( scene, camera/* , this.renderTargetDepthBuffer, true */);
-
- // Make selected objects visible
- this.changeVisibilityOfSelectedObjects( true );
- // Update Texture Matrix for Depth compare
- this.updateTextureMatrix(camera);
- // Make non selected objects invisible, and draw only the selected objects, by comparing the depth buffer of non selected objects
- this.changeVisibilityOfNonSelectedObjects( false , scenes);
-
-
- scenes.forEach(scene=>{
- scene.overrideMaterial = this.prepareMaskMaterial;
- });
-
-
- this.prepareMaskMaterial.uniforms[ "cameraNearFar" ].value = new Vector2$1( camera.near, camera.far );
- this.prepareMaskMaterial.uniforms[ "depthTexture" ].value = this.renderTargetDepthBuffer.texture;
- this.prepareMaskMaterial.uniforms[ "textureMatrix" ].value = this.textureMatrix;
-
- //renderer.setRenderTarget(this.renderTargetMaskBuffer)
- //renderer.clear()
- //renderer.render( scene, camera/* , this.renderTargetMaskBuffer, true */ );
- render2(this.renderTargetMaskBuffer);
-
- this.changeVisibilityOfNonSelectedObjects( true , scenes);
-
-
- scenes.forEach(scene=>{
- scene.overrideMaterial = null;
- scene.background = scene.currentBackground;
- });
-
- // 2. Downsample to Half resolution
- /*this.quad.material = this.materialCopy;
- this.copyUniforms[ "tDiffuse" ].value = this.renderTargetMaskBuffer.texture;
- renderer.render( this.scene, this.camera, this.renderTargetMaskDownSampleBuffer, true );
- this.tempPulseColor1.copy( this.visibleEdgeColor );
- this.tempPulseColor2.copy( this.hiddenEdgeColor );
- if ( this.pulsePeriod > 0 ) {
- var scalar = ( 1 + 0.25 ) / 2 + Math.cos( performance.now() * 0.01 / this.pulsePeriod ) * ( 1.0 - 0.25 ) / 2;
- this.tempPulseColor1.multiplyScalar( scalar );
- this.tempPulseColor2.multiplyScalar( scalar );
- } */
- // 3. Apply Edge Detection Pass
- this.quad.material = this.edgeDetectionMaterial;
- this.edgeDetectionMaterial.uniforms[ "maskTexture" ].value = this.renderTargetMaskBuffer.texture;//this.renderTargetMaskDownSampleBuffer.texture;
- this.edgeDetectionMaterial.uniforms[ "texSize" ].value = new Vector2$1(this.resolution.x, this.resolution.y );//new THREE.Vector2( this.renderTargetMaskDownSampleBuffer.width, this.renderTargetMaskDownSampleBuffer.height );
- //this.edgeDetectionMaterial.uniforms[ "texSize" ].value = new THREE.Vector2(this.renderTargetMaskBuffer.width, this.renderTargetMaskBuffer.height)//new THREE.Vector2( this.renderTargetMaskDownSampleBuffer.width, this.renderTargetMaskDownSampleBuffer.height );
- this.edgeDetectionMaterial.uniforms[ "visibleEdgeColor" ].value = this.visibleEdgeColor;//this.tempPulseColor1;
- this.edgeDetectionMaterial.uniforms[ "hiddenEdgeColor" ].value = this.hiddenEdgeColor; //this.tempPulseColor2;
-
- renderer.setRenderTarget(this.renderTargetEdgeBuffer1);
- renderer.clear();
- renderer.render( this.scene, this.camera/* , this.renderTargetEdgeBuffer1, true */);
-
- /*
- // 4. Apply Blur on Half res
- this.quad.material = this.separableBlurMaterial1;
- this.separableBlurMaterial1.uniforms[ "colorTexture" ].value = this.renderTargetEdgeBuffer1.texture;
- this.separableBlurMaterial1.uniforms[ "direction" ].value = OutlinePass.BlurDirectionX;
- this.separableBlurMaterial1.uniforms[ "kernelRadius" ].value = this.edgeThickness;
- renderer.render( this.scene, this.camera, this.renderTargetBlurBuffer1, true );
- this.separableBlurMaterial1.uniforms[ "colorTexture" ].value = this.renderTargetBlurBuffer1.texture;
- this.separableBlurMaterial1.uniforms[ "direction" ].value = OutlinePass.BlurDirectionY;
- renderer.render( this.scene, this.camera, this.renderTargetEdgeBuffer1, true );
- // Apply Blur on quarter res
- this.quad.material = this.separableBlurMaterial2;
- this.separableBlurMaterial2.uniforms[ "colorTexture" ].value = this.renderTargetEdgeBuffer1.texture;
- this.separableBlurMaterial2.uniforms[ "direction" ].value = OutlinePass.BlurDirectionX;
- renderer.render( this.scene, this.camera, this.renderTargetBlurBuffer2, true );
- this.separableBlurMaterial2.uniforms[ "colorTexture" ].value = this.renderTargetBlurBuffer2.texture;
- this.separableBlurMaterial2.uniforms[ "direction" ].value = OutlinePass.BlurDirectionY;
- renderer.render( this.scene, this.camera, this.renderTargetEdgeBuffer2, true );
- */
-
- // Blend it additively over the input texture
- this.quad.material = this.overlayMaterial;
- //this.overlayMaterial.uniforms[ "maskTexture" ].value = this.renderTargetMaskBuffer.texture;
- this.overlayMaterial.uniforms[ "edgeTexture1" ].value = this.renderTargetEdgeBuffer1.texture;
- //this.overlayMaterial.uniforms[ "edgeTexture2" ].value = this.renderTargetEdgeBuffer2.texture;
- //this.overlayMaterial.uniforms[ "patternTexture" ].value = this.patternTexture;
- this.overlayMaterial.uniforms[ "edgeStrength" ].value = this.edgeStrength;
- //this.overlayMaterial.uniforms[ "edgeGlow" ].value = this.edgeGlow;
- //this.overlayMaterial.uniforms[ "usePatternTexture" ].value = this.usePatternTexture;
- if ( maskActive ) renderer.context.enable( renderer.context.STENCIL_TEST );
- //renderer.render( this.scene, camera, readBuffer, false );
-
- //改:清空readBuffer, 仅绘制出outline的部分
-
- renderer.setClearColor( 0x000000, 0 );
- renderer.setRenderTarget(readBuffer);
- renderer.clear();
- renderer.render( this.scene, this.camera/* , readBuffer, true */);
-
- renderer.setClearColor( this.oldClearColor, this.oldClearAlpha );
- renderer.autoClear = oldAutoClear;
- return true
- }
- if ( this.renderToScreen ) {
- this.quad.material = this.materialCopy;
- this.copyUniforms[ "tDiffuse" ].value = readBuffer.texture;
- renderer.render( this.scene , this.camera );
- }
- },
- getPrepareMaskMaterial: function () {
- return new ShaderMaterial( {
- uniforms: {
- "depthTexture": { value: null },
- "cameraNearFar": { value: new Vector2$1( 0.5, 0.5 ) },
- "textureMatrix": { value: new Matrix4() }
- },
- vertexShader: [
- 'varying vec4 projTexCoord;',
- 'varying vec4 vPosition;',
- 'uniform mat4 textureMatrix;',
- 'void main() {',
- ' vPosition = modelViewMatrix * vec4( position, 1.0 );',
- ' vec4 worldPosition = modelMatrix * vec4( position, 1.0 );',
- ' projTexCoord = textureMatrix * worldPosition;',
- ' gl_Position = projectionMatrix * modelViewMatrix * vec4( position, 1.0 );',
- '}'
- ].join( '\n' ),
- fragmentShader: [
- '#include <packing>',
- 'varying vec4 vPosition;',
- 'varying vec4 projTexCoord;',
- 'uniform sampler2D depthTexture;',
- 'uniform vec2 cameraNearFar;',
- 'void main() {',
- ' float depth = unpackRGBAToDepth(texture2DProj( depthTexture, projTexCoord ));',
- ' float viewZ = - DEPTH_TO_VIEW_Z( depth, cameraNearFar.x, cameraNearFar.y );',
- ' float depthTest = (-vPosition.z > viewZ) ? 1.0 : 0.0;',
- ' gl_FragColor = vec4(0.0, depthTest, 1.0, 1.0);',
- '}'
- ].join( '\n' )
- } );
- },
- getEdgeDetectionMaterial: function (thickness) {
- return new ShaderMaterial( {
- uniforms: {
- "thickness": { value: thickness },
- "maskTexture": { value: null },
- "texSize": { value: new Vector2$1( 10, 10 ) },
- "visibleEdgeColor": { value: new Vector3( 1.0, 1.0, 1.0 ) },
- "hiddenEdgeColor": { value: new Vector3( 1.0, 1.0, 1.0 ) },
- },
- vertexShader:
- "varying vec2 vUv;\n\
- void main() {\n\
- vUv = uv;\n\
- gl_Position = projectionMatrix * modelViewMatrix * vec4( position, 1.0 );\n\
- }",
- /* fragmentShader:
- "varying vec2 vUv;\
- uniform sampler2D maskTexture;\
- uniform vec2 texSize;\
- uniform vec3 visibleEdgeColor;\
- uniform vec3 hiddenEdgeColor;\
- \
- void main() {\n\
- vec2 invSize = 1.0 / texSize;\
- vec4 uvOffset = vec4(1.0, 0.0, 0.0, 1.0) * vec4(invSize, invSize);\
- vec4 c1 = texture2D( maskTexture, vUv + uvOffset.xy);\
- vec4 c2 = texture2D( maskTexture, vUv - uvOffset.xy);\
- vec4 c3 = texture2D( maskTexture, vUv + uvOffset.yw);\
- vec4 c4 = texture2D( maskTexture, vUv - uvOffset.yw);\
- float diff1 = (c1.r - c2.r)*0.5;\
- float diff2 = (c3.r - c4.r)*0.5;\
- float d = length( vec2(diff1, diff2) );\
- float a1 = min(c1.g, c2.g);\
- float a2 = min(c3.g, c4.g);\
- float visibilityFactor = min(a1, a2);\
- vec3 edgeColor = 1.0 - visibilityFactor > 0.001 ? visibleEdgeColor : hiddenEdgeColor;\
- gl_FragColor = vec4(edgeColor, 1.0) * vec4(d);\
- }" */
- fragmentShader:
- "varying vec2 vUv;\
- uniform sampler2D maskTexture;\
- uniform float thickness;\
- uniform vec2 texSize;\
- uniform vec3 visibleEdgeColor;\
- uniform vec3 hiddenEdgeColor;\
- \
- void main() {\n\
- vec2 invSize = 1.0 / texSize;\
- vec4 uvOffset = vec4(1.0, 0.0, 0.0, 1.0) * vec4(invSize, invSize);\
- vec4 c1 = texture2D( maskTexture, vUv + uvOffset.xy);\
- vec4 c2 = texture2D( maskTexture, vUv - uvOffset.xy);\
- vec4 c3 = texture2D( maskTexture, vUv + uvOffset.yw);\
- vec4 c4 = texture2D( maskTexture, vUv - uvOffset.yw);\
- float diff1 = (c1.r - c2.r)*0.5;\
- float diff2 = (c3.r - c4.r)*0.5;\
- float d = length( vec2(diff1, diff2) ) * thickness;\
- float a1 = min(c1.g, c2.g);\
- float a2 = min(c3.g, c4.g);\
- float visibilityFactor = min(a1, a2);\
- vec3 edgeColor = 1.0 - visibilityFactor > 0.001 ? visibleEdgeColor : hiddenEdgeColor;\
- gl_FragColor = vec4(edgeColor, 1.0) * vec4(d);\
- }"
- } );
- },
- getSeperableBlurMaterial: function ( maxRadius ) {
- return new ShaderMaterial( {
- defines: {
- "MAX_RADIUS": maxRadius,
- },
- uniforms: {
- "colorTexture": { value: null },
- "texSize": { value: new Vector2$1( 0.5, 0.5 ) },
- "direction": { value: new Vector2$1( 0.5, 0.5 ) },
- "kernelRadius": { value: 1.0 }
- },
- vertexShader:
- "varying vec2 vUv;\n\
- void main() {\n\
- vUv = uv;\n\
- gl_Position = projectionMatrix * modelViewMatrix * vec4( position, 1.0 );\n\
- }",
- fragmentShader:
- "#include <common>\
- varying vec2 vUv;\
- uniform sampler2D colorTexture;\
- uniform vec2 texSize;\
- uniform vec2 direction;\
- uniform float kernelRadius;\
- \
- float gaussianPdf(in float x, in float sigma) {\
- return 0.39894 * exp( -0.5 * x * x/( sigma * sigma))/sigma;\
- }\
- void main() {\
- vec2 invSize = 1.0 / texSize;\
- float weightSum = gaussianPdf(0.0, kernelRadius);\
- vec3 diffuseSum = texture2D( colorTexture, vUv).rgb * weightSum;\
- vec2 delta = direction * invSize * kernelRadius/float(MAX_RADIUS);\
- vec2 uvOffset = delta;\
- for( int i = 1; i <= MAX_RADIUS; i ++ ) {\
- float w = gaussianPdf(uvOffset.x, kernelRadius);\
- vec3 sample1 = texture2D( colorTexture, vUv + uvOffset).rgb;\
- vec3 sample2 = texture2D( colorTexture, vUv - uvOffset).rgb;\
- diffuseSum += ((sample1 + sample2) * w);\
- weightSum += (2.0 * w);\
- uvOffset += delta;\
- }\
- gl_FragColor = vec4(diffuseSum/weightSum, 1.0);\
- }"
-
- } );
- },
- getOverlayMaterial: function () {
- return new ShaderMaterial( {
- uniforms: {
- "maskTexture": { value: null },
- "edgeTexture1": { value: null },
- "edgeTexture2": { value: null },
- "patternTexture": { value: null },
- "edgeStrength": { value: 1.0 },
- "edgeGlow": { value: 1.0 },
- "usePatternTexture": { value: 0.0 }
- },
- vertexShader:
- "varying vec2 vUv;\n\
- void main() {\n\
- vUv = uv;\n\
- gl_Position = projectionMatrix * modelViewMatrix * vec4( position, 1.0 );\n\
- }",
- /* fragmentShader:
- "varying vec2 vUv;\
- uniform sampler2D maskTexture;\
- uniform sampler2D edgeTexture1;\
- uniform sampler2D edgeTexture2;\
- uniform sampler2D patternTexture;\
- uniform float edgeStrength;\
- uniform float edgeGlow;\
- uniform bool usePatternTexture;\
- \
- void main() {\
- vec4 edgeValue1 = texture2D(edgeTexture1, vUv);\
- vec4 edgeValue2 = texture2D(edgeTexture2, vUv);\
- vec4 maskColor = texture2D(maskTexture, vUv);\
- vec4 patternColor = texture2D(patternTexture, 6.0 * vUv);\
- float visibilityFactor = 1.0 - maskColor.g > 0.0 ? 1.0 : 0.5;\
- vec4 edgeValue = edgeValue1 + edgeValue2 * edgeGlow;\
- vec4 finalColor = edgeStrength * maskColor.r * edgeValue;\ // 删除 * maskColor.r 也就是去掉遮罩,使模型部分也有outline
- if(usePatternTexture)\
- finalColor += + visibilityFactor * (1.0 - maskColor.r) * (1.0 - patternColor.r);\
- gl_FragColor = finalColor;\
- }", */
- fragmentShader:
- `varying vec2 vUv;
- uniform sampler2D edgeTexture1;
- uniform float edgeStrength;
-
- void main() {
- gl_FragColor = edgeStrength * texture2D(edgeTexture1, vUv);
- }`,
- blending: AdditiveBlending,
- depthTest: false,
- depthWrite: false,
- transparent: true
- } );
- }
- } );
- OutlinePass.BlurDirectionX = new Vector2$1( 1.0, 0.0 );
- OutlinePass.BlurDirectionY = new Vector2$1( 0.0, 1.0 );
- /**
- * https://github.com/google/model-viewer/blob/master/packages/model-viewer/src/three-components/EnvironmentScene.ts
- */
- class RoomEnvironment extends Scene {
- constructor() {
- super();
- const geometry = new BoxGeometry();
- //geometry.deleteAttribute( 'uv' );//????????????
- const roomMaterial = new MeshStandardMaterial( { side: BackSide } );
- const boxMaterial = new MeshStandardMaterial();
- const mainLight = new PointLight( 0xffffff, 5.0, 28, 2 );
- mainLight.position.set( 0.418, 16.199, 0.300 );
- this.add( mainLight );
- const room = new Mesh( geometry, roomMaterial );
- room.position.set( - 0.757, 13.219, 0.717 );
- room.scale.set( 31.713, 28.305, 28.591 );
- this.add( room );
- const box1 = new Mesh( geometry, boxMaterial );
- box1.position.set( - 10.906, 2.009, 1.846 );
- box1.rotation.set( 0, - 0.195, 0 );
- box1.scale.set( 2.328, 7.905, 4.651 );
- this.add( box1 );
- const box2 = new Mesh( geometry, boxMaterial );
- box2.position.set( - 5.607, - 0.754, - 0.758 );
- box2.rotation.set( 0, 0.994, 0 );
- box2.scale.set( 1.970, 1.534, 3.955 );
- this.add( box2 );
- const box3 = new Mesh( geometry, boxMaterial );
- box3.position.set( 6.167, 0.857, 7.803 );
- box3.rotation.set( 0, 0.561, 0 );
- box3.scale.set( 3.927, 6.285, 3.687 );
- this.add( box3 );
- const box4 = new Mesh( geometry, boxMaterial );
- box4.position.set( - 2.017, 0.018, 6.124 );
- box4.rotation.set( 0, 0.333, 0 );
- box4.scale.set( 2.002, 4.566, 2.064 );
- this.add( box4 );
- const box5 = new Mesh( geometry, boxMaterial );
- box5.position.set( 2.291, - 0.756, - 2.621 );
- box5.rotation.set( 0, - 0.286, 0 );
- box5.scale.set( 1.546, 1.552, 1.496 );
- this.add( box5 );
- const box6 = new Mesh( geometry, boxMaterial );
- box6.position.set( - 2.193, - 0.369, - 5.547 );
- box6.rotation.set( 0, 0.516, 0 );
- box6.scale.set( 3.875, 3.487, 2.986 );
- this.add( box6 );
- // -x right
- const light1 = new Mesh( geometry, createAreaLightMaterial( 50 ) );
- light1.position.set( - 16.116, 14.37, 8.208 );
- light1.scale.set( 0.1, 2.428, 2.739 );
- this.add( light1 );
- // -x left
- const light2 = new Mesh( geometry, createAreaLightMaterial( 50 ) );
- light2.position.set( - 16.109, 18.021, - 8.207 );
- light2.scale.set( 0.1, 2.425, 2.751 );
- this.add( light2 );
- // +x
- const light3 = new Mesh( geometry, createAreaLightMaterial( 17 ) );
- light3.position.set( 14.904, 12.198, - 1.832 );
- light3.scale.set( 0.15, 4.265, 6.331 );
- this.add( light3 );
- // +z
- const light4 = new Mesh( geometry, createAreaLightMaterial( 43 ) );
- light4.position.set( - 0.462, 8.89, 14.520 );
- light4.scale.set( 4.38, 5.441, 0.088 );
- this.add( light4 );
- // -z
- const light5 = new Mesh( geometry, createAreaLightMaterial( 20 ) );
- light5.position.set( 3.235, 11.486, - 12.541 );
- light5.scale.set( 2.5, 2.0, 0.1 );
- this.add( light5 );
- // +y
- const light6 = new Mesh( geometry, createAreaLightMaterial( 100 ) );
- light6.position.set( 0.0, 20.0, 0.0 );
- light6.scale.set( 1.0, 0.1, 1.0 );
- this.add( light6 );
- }
- }
- function createAreaLightMaterial( intensity ) {
- const material = new MeshBasicMaterial();
- material.color.setScalar( intensity );
- return material;
- }
- const manager = new LoadingManager();
- let loaders = {};
- let mapArea;
- let blockedByIntersectHistory = new Map();
- class Viewer extends ViewerBase{
-
- constructor(domElement, mapArea_, args = {}){
- super(domElement, $.extend(args,{name:'mainViewer'}));
- window.viewer = this;
-
-
-
- if(Potree.settings.editType == "pano" || Potree.settings.editType == "merge"){
- this.modules = {
- Alignment,
- SiteModel
- };
- Potree.settings.useDepthTex = false;
-
- if(Potree.settings.editType == "pano" ){
- this.modules.PanoEditor = PanoEditor$1;
- }else if(Potree.settings.editType == "merge"){
- this.modules.MergeEditor = MergeEditor;
- }
-
- }else {
- this.modules = {
- Clip,
- Alignment,
- SiteModel,
- RouteGuider : new RouteGuider,
- ParticleEditor,
- CamAniEditor,
- };
- }
-
-
-
- console.log('create viewer');
-
- this.navigateMode = 'free'; // 'panorama'; 'free'自由模式是只显示点云或者未进入到漫游点,
- this.isEdit = true;
- this.waitQueue = [];
- this.unitConvert = new UoMService();
- mapArea = mapArea_;
- this.visible = true;
- this.fpVisiDatasets = [];
- this.atDatasets = [];
- this.objs = new Object3D;
-
-
- //this.lastPos = new THREE.Vector3(Infinity,Infinity,Infinity)
- //-------------
-
- var supportExtFragDepth = !!Features.EXT_DEPTH.isSupported() ;//iphoneX居然不支持
- //这意味着边缘增强和测量线遮挡失效
- if(!supportExtFragDepth)console.error('ExtFragDepth unsupported! 边缘增强和测量线遮挡失效');
-
-
- this.guiLoaded = false;
- this.guiLoadTasks = [];
- this.onVrListeners = [];
-
- this.messages = [];
- this.elMessages = $(`
- <div id="message_listing"
- style="position: absolute; z-index: 1000; left: 10px; bottom: 10px">
- </div>`);
- $(domElement).append(this.elMessages);
-
-
- this.paused;
-
- document.addEventListener('visibilitychange',(e)=>{
- //console.log('visibilitychange', !document.hidden )
- this.dispatchEvent({type:'pageVisible', v:!document.hidden} );
-
- });
-
-
-
- try{
-
- if(!Potree.settings.isOfficial)
- { // generate missing dom hierarchy
- if ($(domElement).find('#potree_map').length === 0) {
- let potreeMap = $(`
- <div id="potree_map" class="mapBox" style="position: absolute; left: 50px; top: 50px; width: 400px; height: 400px; display: none">
- <div id="potree_map_header" style="position: absolute; width: 100%; height: 25px; top: 0px; background-color: rgba(0,0,0,0.5); z-index: 1000; border-top-left-radius: 3px; border-top-right-radius: 3px;">
- </div>
- <div id="potree_map_content" class="map" style="position: absolute; z-index: 100; top: 25px; width: 100%; height: calc(100% - 25px); border: 2px solid rgba(0,0,0,0.5); box-sizing: border-box;"></div>
- </div>
- `);
- $(domElement).append(potreeMap);
- }
- if ($(domElement).find('#potree_description').length === 0) {
- let potreeDescription = $(`<div id="potree_description" class="potree_info_text"></div>`);
- $(domElement).append(potreeDescription);
- }
- if ($(domElement).find('#potree_annotations').length === 0) {
- let potreeAnnotationContainer = $(`
- <div id="potree_annotation_container"
- style="position: absolute; z-index: 100000; width: 100%; height: 100%; pointer-events: none;"></div>`);
- $(domElement).append(potreeAnnotationContainer);
- }
- if ($(domElement).find('#potree_quick_buttons').length === 0) {
- let potreeMap = $(`
- <div id="potree_quick_buttons" class="quick_buttons_container" style="">
- </div>
- `);
-
- $(domElement).append(potreeMap);
- }
-
- //add
- {
-
- if(!mapArea && Potree.settings.editType != 'merge'){
- $(domElement).append($("<div id='potree_labels'></div>"));
-
- mapArea = $("<div id='mapGaode'></div>");
- $(domElement).append(mapArea);
- mapArea = mapArea[0];
- }
-
- }
-
-
- /* let domRoot = this.renderer.domElement.parentElement;
- let elAttach = $("<input type='button' value='test'></input>");
- elAttach.css({
- position : "absolute",
- right : '10%',
- bottom: '20px',
- zIndex: "10000",
- fontSize:'1em', color:"black",
- background:'rgba(255,255,255,0.8)',
- })
- let state = false
- elAttach.on("click", () => {
- window.buttonFunction && window.buttonFunction()
- });
- domRoot.appendChild(elAttach[0]); */
-
-
-
-
- }
-
-
- this.pointCloudLoadedCallback = args.onPointCloudLoaded || function () {};
- // if( /Android|webOS|iPhone|iPad|iPod|BlackBerry|IEMobile|Opera Mini/i.test(navigator.userAgent) ) {
- // defaultSettings.navigation = "Orbit";
- // }
- this.server = null;
- this.fov = 60;
- this.isFlipYZ = false;
- this.useDEMCollisions = false;
- this.generateDEM = false;
- this.minNodeSize = 30;
- this.edlStrength = 1.0;
- this.edlRadius = 1.4;
- this.edlOpacity = 1.0;
- this.useEDL = false;
- this.description = "";
- this.classifications = ClassificationScheme.DEFAULT;
- this.moveSpeed = 10;
- this.lengthUnit = LengthUnits.METER;
- this.lengthUnitDisplay = LengthUnits.METER;
- this.showBoundingBox = false;
- this.showAnnotations = true;
- this.freeze = false;
- this.clipTask = ClipTask.HIGHLIGHT;
- this.clipMethod = ClipMethod.INSIDE_ANY;
- this.elevationGradientRepeat = ElevationGradientRepeat.CLAMP;
- this.filterReturnNumberRange = [0, 7];
- this.filterNumberOfReturnsRange = [0, 7];
- this.filterGPSTimeRange = [-Infinity, Infinity];
- this.filterPointSourceIDRange = [0, 65535];
- this.potreeRenderer = null;
- this.edlRenderer = null;
- this.pRenderer = null;
- this.scene = null;
- this.sceneVR = null;
- this.overlay = null;
- this.overlayCamera = null;
- this.inputHandler = null;
- this.controls = null;
- this.clippingTool = null;
- this.transformationTool = null;
- this.navigationCube = null;
- this.compass = null;
-
- this.skybox = null;
- this.clock = new Clock();
- this.background = null;
-
-
-
- if(args.noDragAndDrop){
-
- }else {
- this.initDragAndDrop();
- }
- if(typeof Stats !== "undefined"){
- this.stats = new Stats();
- this.stats.showPanel( 0 ); // 0: fps, 1: ms, 2: mb, 3+: custom
- document.body.appendChild( this.stats.dom );
- }
- {
- let canvas = this.renderer.domElement;
- canvas.addEventListener("webglcontextlost", (e) => {
- console.log(e);
- this.postMessage("WebGL context lost. \u2639");
- let gl = this.renderer.getContext();
- let error = gl.getError();
- console.log(error);
- this.dispatchEvent({type:'webglError', msg:'webglcontextlost'});
- }, false);
- }
- {
- this.overlay = new Scene();
- this.overlayCamera = new OrthographicCamera(
- 0, 1,
- 1, 0,
- -1000, 1000
- );
- }
-
- this.pRenderer = new Renderer(this.renderer);
-
- {
- let near = 2.5;
- let far = 10.0;
- let fov = 90;
-
- this.shadowTestCam = new PerspectiveCamera(90, 1, near, far);
- this.shadowTestCam.position.set(3.50, -2.80, 8.561);
- this.shadowTestCam.lookAt(new Vector3(0, 0, 4.87));
- }
-
-
-
- let scene = new Scene$1(this.renderer);
-
- { // create VR scene
- this.sceneVR = new Scene();
- // let texture = new THREE.TextureLoader().load(`${Potree.resourcePath}/images/vr_controller_help.jpg`);
- // let plane = new THREE.PlaneBufferGeometry(1, 1, 1, 1);
- // let infoMaterial = new THREE.MeshBasicMaterial({map: texture});
- // let infoNode = new THREE.Mesh(plane, infoMaterial);
- // infoNode.position.set(-0.5, 1, 0);
- // infoNode.scale.set(0.4, 0.3, 1);
- // infoNode.lookAt(0, 1, 0)
- // this.sceneVR.add(infoNode);
- // window.infoNode = infoNode;
- }
- this.setScene(scene);
-
-
- //add: for 截图时抗锯齿
- {
- this.composer = new EffectComposer( this.renderer );
- this.ssaaRenderPass = new SSAARenderPass(0x000000, 0);
- this.composer.addPass( this.ssaaRenderPass );
- //this.ssaaRenderPass.useCopy = true
- //this.ssaaRenderPass.renderToScreen = true;
- //this.ssaaRenderPass.needsSwap = false
- //见 https://threejs.org/examples/?q=AA#webgl_postprocessing_fxaa 效果和SSAA差不多,都对透明不太友好。
- /* const renderPass = new RenderPass();
- renderPass.clearColor = new THREE.Color( 0, 0, 0 );
- renderPass.clearAlpha = 0;
- this.composer.addPass( renderPass );
-
- this.fxaaPass = new ShaderPass( FXAAShader );
-
- this.composer.addPass( this.fxaaPass );
-
- this.fxaaPass.setSize = function(width, height){
- this.material.uniforms[ 'resolution' ].value.x = 1 / ( width );
- this.material.uniforms[ 'resolution' ].value.y = 1 / ( height );
- }
- this.fxaaPass.renderToScreen = true; */
-
- /* this.fxaaPass = new ShaderPass( FXAAShader );
- //this.fxaaPass.readTarget = true //add
- this.composer.addPass( this.fxaaPass );
- this.composer.readTarget = true
- this.fxaaPass.setSize = function(width, height){
- this.material.uniforms[ 'resolution' ].value.x = 1 / ( width );
- this.material.uniforms[ 'resolution' ].value.y = 1 / ( height );
- }
- this.fxaaPass.renderToScreen = true; */
-
- //for 融合页面
-
-
- let outlinePass = this.outlinePass = new OutlinePass( );
- //composer.addPass( outlinePass );
- outlinePass.edgeStrength = 10;
- outlinePass.edgeGlow = 0;
- outlinePass.visibleEdgeColor = new Color("#09a1b3");
- outlinePass.hiddenEdgeColor = new Color("#09a1b3");
- this.ssaaRenderPass.addPass(outlinePass);
-
- }
-
-
- {
-
-
- this.mainViewport = new Viewport( this.scene.view, this.scene.cameraP, {
- left:0, bottom:0, width:1, height: 1, name:'MainView'
- });
- this.viewports = [this.mainViewport];
-
- this.compass = new Compass(this);
- this.magnifier = new Magnifier(this);
- this.reticule = new Reticule(this);
- this.scene.scene.add(this.magnifier);
- this.scene.scene.add(this.reticule);
-
-
- if(Potree.settings.editType != "pano" && Potree.settings.editType != 'merge'){
- this.mapViewer = new MapViewer(mapArea/* $('#mapGaode')[0] */);
- }
-
- this.inputHandler = new InputHandler(this, this.scene.scene);
- //this.inputHandler.setScene(this.scene);
- //this.inputHandler.addInputListener(this);//add
-
- this.clippingTool = new ClippingTool(this);
- this.transformationTool = new TransformationTool(this);
- this.navigationCube = new NavigationCube(this);
- this.navigationCube.visible = false;
-
-
-
-
- this.createControls();
- this.clippingTool.setScene(this.scene);
-
- let onPointcloudAdded = (e) => {
- if (this.scene.pointclouds.length === 1) {
- let speed = e.pointcloud.boundingBox.getSize(new Vector3()).length();
- speed = speed / 2000;
- this.setMoveSpeed(speed);
- }
- };
- let onVolumeRemoved = (e) => {
- this.inputHandler.deselect(e.volume);
- };
- this.addEventListener('scene_changed', (e) => {
- this.inputHandler.setScene(e.scene);
- this.clippingTool.setScene(this.scene);
-
- if(!e.scene.hasEventListener("pointcloud_added", onPointcloudAdded)){
- e.scene.addEventListener("pointcloud_added", onPointcloudAdded);
- }
- if(!e.scene.hasEventListener("volume_removed", onPointcloudAdded)){
- e.scene.addEventListener("volume_removed", onVolumeRemoved);
- }
-
- });
- this.scene.addEventListener("volume_removed", onVolumeRemoved);
- this.scene.addEventListener('pointcloud_added', onPointcloudAdded);
- }
- { // set defaults
- this.setFOV(60);
- this.setEDLEnabled(false);
- this.setEDLRadius(1.4);
- this.setEDLStrength(0.4);
- this.setEDLOpacity(1.0);
- this.setClipTask(ClipTask.HIGHLIGHT);
- this.setClipMethod(ClipMethod.INSIDE_ANY);
- this.setPointBudget(1*1000*1000);
- this.setShowBoundingBox(false);
- this.setFreeze(false);
- this.setControls(this.fpControls/* orbitControls */);
- this.setBackground( new Color(Potree.config.background),1 /* 'gradient' */ );
-
- this.scaleFactor = 1;
- this.loadSettingsFromURL();
- }
- // start rendering!
- //if(args.useDefaultRenderLoop === undefined || args.useDefaultRenderLoop === true){
- //requestAnimationFrame(this.loop.bind(this));
- //}
-
- this.renderer.setAnimationLoop(this.loop.bind(this));
- this.loadGUI = this.loadGUI.bind(this);
- this.annotationTool = new AnnotationTool(this);
- this.measuringTool = new MeasuringTool(this);
- this.profileTool = new ProfileTool(this);
- this.volumeTool = new VolumeTool(this);
-
-
-
-
-
- //-----------
- CursorDeal.init(this, this.mapViewer ? [this, this.mapViewer] : [this]);//ADD
- if(Potree.settings.editType == "pano"){
- this.modules.PanoEditor.init();
- }else if(Potree.settings.editType == "merge"){
- this.modules.MergeEditor.init();
- }else {
- this.modules.SiteModel.init();
- this.modules.ParticleEditor.init();
- }
- this.modules.Alignment.init();
-
-
-
- this.images360 = new Images360(this);
-
- this.scene.scene.add(this.objs);
-
- loaders = {
- objLoader : new OBJLoader( manager ),
- mtlLoader : new MTLLoader( manager ),
- glbLoader : new GLTFLoader(undefined, this.renderer)
-
- };
- //add test
- const environment = new RoomEnvironment();
- const pmremGenerator = new PMREMGenerator( this.renderer );
- this.scene.scene.environment = pmremGenerator.fromScene( environment ).texture;
-
- //-----------
-
-
- }catch(e){
- this.onCrash(e);
- }
-
- //-----------------------add----------------------------------------------------
-
-
-
-
- /* {
- let ratio
- this.addEventListener('resize',(e)=>{
- if(ratio != e.deviceRatio){ //因为devicePixelRatio会影响到点云大小,所以改变时计算下点云大小
- viewer.scene.pointclouds.forEach(p => {
- p.changePointSize()
- })
- }
- ratio = e.deviceRatio
-
- })
- } */
-
- {
- let pointDensity = '';
- Object.defineProperty(Potree.settings , "pointDensity",{
- get: function() {
- return pointDensity
- },
- set: (density)=>{
- if(density && density != pointDensity){
- let pointBudget;
- var config = Potree.config.pointDensity[density];
-
- if(this.magnifier.visible){//放大镜打开时不要切换pointBudget,否则点云会闪烁。这时使用最高密度。
- pointBudget = Potree.config.pointDensity['magnifier'].pointBudget;
- }else {
- pointBudget = config.pointBudget;
- }
-
- viewer.setPointBudget(pointBudget );
- //Potree.maxPointLevel = config.maxLevel
-
- pointDensity = density;
-
- this.setPointLevels();
-
-
- }
- }
- });
-
- let UserPointDensity = '';
- Object.defineProperty(Potree.settings , "UserPointDensity",{
- get: function() {
- return UserPointDensity
- },
- set: (density)=>{
- if(UserPointDensity != density){
- if(Potree.settings.displayMode == 'showPointCloud' && this.viewports.length != 4){//漫游模式和四屏时都有自己的pointDensity
- Potree.settings.pointDensity = density;
- }
- UserPointDensity = density;
- }
- }
- });
-
-
- }
- {
-
- let cameraFar = Potree.settings.cameraFar;
- Object.defineProperty(Potree.settings , "cameraFar",{
- get: function() {
- return cameraFar
- },
- set: (far)=>{
- if(far != cameraFar){
- if(Potree.settings.displayMode != 'showPanos'){
- this.mainViewport.camera.far = far;
- this.mainViewport.camera.updateProjectionMatrix();
- }
- cameraFar = far;
- }
- }
- });
-
- }
-
-
- this.addEventListener('allLoaded', ()=>{
- setTimeout(this.testPointcloudsMaxLevel.bind(this), 2000); //延迟一丢丢,等画面出现
- });
-
-
-
-
- if(Potree.settings.editType != 'pano' && Potree.settings.editType != 'merge'){
-
-
-
-
- this.addEventListener('switchFloorplanSelect',(e)=>{//进入平面图设置后 切换选中的数据集
- this.selectedFloorplan = e.pointcloud; //绝对显示
- this.updateFpVisiDatasets();
- let pointclouds;
- if(e.pointcloud){
- pointclouds = [e.pointcloud];
- }else if(this.fpVisiDatasets.length){
- pointclouds = this.fpVisiDatasets;
- }
-
- pointclouds && this.mapViewer.fitToDatasets(pointclouds);
-
- });
-
-
-
-
-
- this.modules.SiteModel.bus.addEventListener('FloorChange',()=>{
- this.updateFpVisiDatasets();
- });
- this.mapViewer.mapLayer.addEventListener('floorplanLoaded',()=>{
- this.updateCadVisibles(this.fpVisiDatasets, true); //加载完成后重新更新下
- });
-
- /* this.modules.Clip.bus.addEventListener('updateSelectedDatasets',()=>{
- this.updateFpVisiDatasets()
- }) */
-
-
- }
-
-
- {
- //更新所在数据集
- this.addEventListener('camera_changed', e => {
- if(e.changeInfo.positionChanged){
- blockedByIntersectHistory.clear(); //清空
- this.updateDatasetAt();
- }
- });
- }
- }
-
-
- ifPointBlockedByIntersect(pos3d, object){//点是否被遮挡
- let ifShelter;
- let shelter = blockedByIntersectHistory.get(object || pos3d);
- if(shelter){
- ifShelter = shelter.ifShelter;
- }else {
- ifShelter = viewer.inputHandler.ifBlockedByIntersect(pos3d, 0.3, true);//由于热点都是使用点云得到的位置,所以判断遮挡时也用点云
- blockedByIntersectHistory.set(object || pos3d, {ifShelter});
- }
- return ifShelter
- }
-
- updateDatasetAt(force){//更新所在数据集
-
- let fun = ()=>{
- let currPos = viewer.mainViewport.view.position;
-
- //if(force || !currPos.equals(this.lastPos)){
- //this.lastPos.copy(currPos)
-
- var at = this.scene.pointclouds.filter(e=>e.ifContainsPoint(currPos));
-
- if(Common.getDifferenceSet(at, this.atDatasets).length){
- //console.log('atDatasets', at)
- this.atDatasets = at;
- if(Potree.settings.editType != 'pano' && Potree.settings.editType != 'merge')this.updateFpVisiDatasets();
- this.dispatchEvent({type:'pointcloudAtChange',pointclouds:at});
- }
- force = false;
- return true
- //}
- };
- if(force)fun();
- else Common.intervalTool.isWaiting('atWhichDataset', fun , 500);
-
- }
-
- updatePanosVisibles(currentFloor){//显示当前楼层的所有panos
- viewer.images360.panos.forEach(pano=>{
- let visible = currentFloor && currentFloor.panos.includes(pano);
- viewer.updateVisible(pano, 'buildingChange', visible, 2);
- });
- }
-
-
- updateFpVisiDatasets(){
-
- let Clip = this.modules.Clip;
- let SiteModel = this.modules.SiteModel;
- let Alignment = this.modules.Alignment;
- var currentFloor = SiteModel.currentFloor;
-
- /* if(Clip.editing){//下载页面已经改为和普通时一样,根据位置判断
-
- this.updateCadVisibles(Clip.selectedDatasets)
-
- }else */if(this.selectedFloorplan){//平面图设置中
- let pointclouds = [this.selectedFloorplan];
- this.updateCadVisibles(pointclouds);
- }else if(SiteModel.editing || Alignment.editing){//只显示勾选的,也就是显示的点云的
- let pointclouds = this.scene.pointclouds.filter(p => this.getObjVisiByReason(p,'datasetSelection') );
- this.updateCadVisibles(pointclouds);
- this.updatePanosVisibles(currentFloor/* , pointclouds */);
- }else {
- let pointclouds = currentFloor ? this.findPointcloudsAtFloor(currentFloor) : [];
-
- if(pointclouds.length == 0){//如果当前不在任何楼层或楼层中无数据集,就用当前所在数据集
- pointclouds = this.atDatasets;
- }
-
- this.updateCadVisibles(pointclouds);
- this.updatePanosVisibles(currentFloor/* , pointclouds */);
- }
- }
-
-
- findPointcloudsAtFloor(entity){//找当前楼层需要显示哪些数据集。
- //数据集的belongToEntity 在这个entity内(否则会出现点击数据集飞过去平面图却不显示)。or 如果数据集有漫游点的话,需要包含>20%的漫游点。 (防止重叠体积很大但其实一个漫游点都不包含)
- //重叠体积>50% 或 包含>50%的漫游点
-
-
- const ratio1 = 0.2, ratio2 = 0.5, ratio3 = 0.95;
-
- var lowScores = [];
-
-
-
- var pointclouds = viewer.scene.pointclouds.filter(e=>{
- let score = 0;
-
- if(e.belongToEntity && (e.belongToEntity == entity || e.belongToEntity.buildParent == entity)){//条件1 若该数据集挂载到该楼层 或 该数据集挂载到的房间属于该楼层(这样能显示该层所有房间)
- return true
- }
-
- if(e.panos.length){//条件2
- var insidePanos = e.panos.filter(a=>entity.ifContainsPoint(a.position));
- let panoCountRatio = insidePanos.length / e.panos.length;
-
- if(panoCountRatio > ratio2)return true
- if(panoCountRatio < ratio1){
- score += panoCountRatio;//return false
- }
-
- }
-
- //条件3
- let volume = entity.intersectPointcloudVolume(e);
- let volumeRatio = volume / entity.getVolume(true); //注:hole加入计算
- if(volumeRatio > ratio3){ //ratio3要高一些,因为点云bounding可能很大,包含很多无点云的空间。即使整个数据集包含entity都不一定看起来在数据集中。(千万要防止两层楼都显示了)
- return true
- }else {
- score += volumeRatio;
- }
-
- lowScores.push({score, pointcloud:e});
- });
-
- if(pointclouds.length == 0){//从低分项挑一个出来。
- lowScores.sort((a,b)=>{return a.score - b.score});
- if(lowScores[0].score > 0.4){
- pointclouds = [lowScores[0].pointcloud];
- }
- }
-
-
- return pointclouds
- }
- updateCadVisibles(visiClouds, force){
- let oldVisi = this.fpVisiDatasets;
- var visiClouds = this.fpVisiDatasets = visiClouds;
-
- if(!force){
- var difference = Common.getDifferenceSet(oldVisi , visiClouds);
- if(difference.length == 0)return
- }
- //console.log('visiClouds',visiClouds.map(e=>e.name))
-
- viewer.scene.pointclouds.forEach(pointcloud=>{
- var floorplan = viewer.mapViewer.mapLayer.getFloorplan(pointcloud.dataset_id);
- var visi = visiClouds.includes(pointcloud);
- if(floorplan){
- viewer.updateVisible(floorplan.objectGroup, 'buildingChange', visi);
- }/* else if(!visi){
- let changeVisi = (e)=>{
- viewer.updateVisible(e.floorplan.objectGroup, 'buildingChange', this.fpVisiDatasets.includes(pointcloud))
- viewer.mapViewer.mapLayer.removeEventListener('floorplanLoaded', changeVisi)
- console.log('updateCadVisibles加载后更改显示',e)
- }
- viewer.mapViewer.mapLayer.addEventListener('floorplanLoaded', changeVisi)
- } */
- //已经添加了全局的 floorplanLoaded后会updateCadVisibles,这段就删了
- });
- viewer.mapViewer.mapLayer.needUpdate = true; //可能需要更新加载的level程度
- viewer.mapViewer.needRender = true; //若上句不触发加载也要立即重新绘制
- }
-
- //促使点云加载出最高级别
- testPointcloudsMaxLevel(){ //所有点云都无需testMaxNodeLevel 就停止
- let camera_changed, count = 0;
- let test = ()=>{
- camera_changed = true;
-
-
- Common.intervalTool.isWaiting('testPointcloudsMaxLevel', ()=>{
- if(!camera_changed && count>20 )return //只有当camera_changed后才继续循环, 除了最开始几次需要连续加载下
- camera_changed = false;
- count ++;
- //console.log('testPointcloudsMaxLevel中')
-
- var success = true;
- viewer.scene.pointclouds.forEach(e=>{
- var wait = e.testMaxNodeLevel();
- if(wait){
- success = false;
- }
- });
-
- if(!success)return true //没有全部加载完,继续循环
- else {
- this.removeEventListener('camera_changed',test);
- console.log('testPointcloudsMaxLevel结束');
- }
-
- }, count<10 ? 150 : 500);
- };
- this.addEventListener('camera_changed',test);
- test();
-
- /* 检验:
- viewer.scene.pointclouds.sort((a,b)=>a.nodeMaxLevelPredict.min - b.nodeMaxLevelPredict.min).forEach(e=>console.log(e.nodeMaxLevel, e.nodeMaxLevelPredict.min))
-
- */
-
- }
-
-
-
- setPointLevels(){
- this.scene.pointclouds.forEach(e=>{
- e.setPointLevel();
- });
- }
- onCrash(error){
- $(this.renderArea).empty();
- if ($(this.renderArea).find('#potree_failpage').length === 0) {
- let elFailPage = $(`
- <div id="#potree_failpage" class="potree_failpage">
-
- <h1>Potree Encountered An Error </h1>
- <p>
- This may happen if your browser or graphics card is not supported.
- <br>
- We recommend to use
- <a href="https://www.google.com/chrome/browser" target="_blank" style="color:initial">Chrome</a>
- or
- <a href="https://www.mozilla.org/" target="_blank">Firefox</a>.
- </p>
- <p>
- Please also visit <a href="http://webglreport.com/" target="_blank">webglreport.com</a> and
- check whether your system supports WebGL.
- </p>
- <p>
- If you are already using one of the recommended browsers and WebGL is enabled,
- consider filing an issue report at <a href="https://github.com/potree/potree/issues" target="_blank">github</a>,<br>
- including your operating system, graphics card, browser and browser version, as well as the
- error message below.<br>
- Please do not report errors on unsupported browsers.
- </p>
- <pre id="potree_error_console" style="width: 100%; height: 100%"></pre>
-
- </div>`);
- let elErrorMessage = elFailPage.find('#potree_error_console');
- elErrorMessage.html(error.stack);
- $(this.renderArea).append(elFailPage);
- }
- throw error;
- }
- // ------------------------------------------------------------------------------------
- // Viewer API
- // ------------------------------------------------------------------------------------
- setScene (scene) {
- if (scene === this.scene) {
- return;
- }
- let oldScene = this.scene;
- this.scene = scene;
- this.dispatchEvent({
- type: 'scene_changed',
- oldScene: oldScene,
- scene: scene
- });
- { // Annotations
- $('.annotation').detach();
- // for(let annotation of this.scene.annotations){
- // this.renderArea.appendChild(annotation.domElement[0]);
- // }
- this.scene.annotations.traverse(annotation => {
- this.renderArea.appendChild(annotation.domElement[0]);
- });
- if (!this.onAnnotationAdded) {
- this.onAnnotationAdded = e => {
- // console.log("annotation added: " + e.annotation.title);
- e.annotation.traverse(node => {
- $("#potree_annotation_container").append(node.domElement);
- //this.renderArea.appendChild(node.domElement[0]);
- node.scene = this.scene;
- });
- };
- }
- if (oldScene) {
- oldScene.annotations.removeEventListener('annotation_added', this.onAnnotationAdded);
- }
- this.scene.annotations.addEventListener('annotation_added', this.onAnnotationAdded);
- }
- };
- setControls(controls/* , setSpeed */){
- if (controls !== this.controls) {
- if (this.controls) {
- this.controls.setEnable(false);
- //this.inputHandler.removeInputListener(this.controls);
- this.controls.moveSpeed = this.moveSpeed; //记录 (因为orbit的radius很大,转为firstPerson时要缩小)
- }
- this.controls = controls;
- controls.moveSpeed && this.setMoveSpeed(controls.moveSpeed); //add
-
- this.controls.setEnable(true);
-
- //this.inputHandler.addInputListener(this.controls);
- }
- }
- getControls () {
- if(this.renderer.xr.isPresenting){
- return this.vrControls;
- }else {
- return this.controls;
- }
-
- }
- getMinNodeSize () {
- return this.minNodeSize;
- };
- setMinNodeSize (value) {
- if (this.minNodeSize !== value) {
- this.minNodeSize = value;
- this.dispatchEvent({'type': 'minnodesize_changed', 'viewer': this});
- }
- };
- getBackground () {
- return this.background;
- }
- setBackground(bg){
- if (this.background === bg) {
- return;
- }
- if(bg === "skybox"){
- this.skybox = Utils.loadSkybox(new URL(Potree.resourcePath + '/textures/skybox2/').href);
- }
- this.background = bg;
- this.backgroundOpacity = 1;//add
- this.dispatchEvent({'type': 'background_changed', 'viewer': this});
- }
- setDescription (value) {
- this.description = value;
-
- $('#potree_description').html(value);
- //$('#potree_description').text(value);
- }
- getDescription(){
- return this.description;
- }
- setShowBoundingBox (value) {
- if (this.showBoundingBox !== value) {
- this.showBoundingBox = value;
- this.dispatchEvent({'type': 'show_boundingbox_changed', 'viewer': this});
- }
- };
- getShowBoundingBox () {
- return this.showBoundingBox;
- };
-
- setMoveSpeed (value) {
- if (this.getMoveSpeed() !== value) {
- this.mainViewport.setMoveSpeed(value);
- this.dispatchEvent({'type': 'move_speed_changed', 'viewer': this, 'speed': value});
- }
- };
- getMoveSpeed () {
- return this.mainViewport.moveSpeed;
- };
- setWeightClassification (w) {
- for (let i = 0; i < this.scene.pointclouds.length; i++) {
- this.scene.pointclouds[i].material.weightClassification = w;
- this.dispatchEvent({'type': 'attribute_weights_changed' + i, 'viewer': this});
- }
- };
- setFreeze (value) {
- value = Boolean(value);
- if (this.freeze !== value) {
- this.freeze = value;
- this.dispatchEvent({'type': 'freeze_changed', 'viewer': this});
- }
- };
- getFreeze () {
- return this.freeze;
- };
- getClipTask(){
- return this.clipTask;
- }
- getClipMethod(){
- return this.clipMethod;
- }
- setClipTask(value){
- if(this.clipTask !== value){
- this.clipTask = value;
- this.dispatchEvent({
- type: "cliptask_changed",
- viewer: this});
- }
- }
- setClipMethod(value){
- if(this.clipMethod !== value){
- this.clipMethod = value;
-
- this.dispatchEvent({
- type: "clipmethod_changed",
- viewer: this});
- }
- }
- setElevationGradientRepeat(value){
- if(this.elevationGradientRepeat !== value){
- this.elevationGradientRepeat = value;
- this.dispatchEvent({
- type: "elevation_gradient_repeat_changed",
- viewer: this});
- }
- }
- setPointBudget (value) {
- if (Potree.pointBudget !== value) {
- Potree.pointBudget = parseInt(value);
- this.dispatchEvent({'type': 'point_budget_changed', 'viewer': this});
- }
- };
- getPointBudget () {
- return Potree.pointBudget;
- };
- setShowAnnotations (value) {
- if (this.showAnnotations !== value) {
- this.showAnnotations = value;
- this.dispatchEvent({'type': 'show_annotations_changed', 'viewer': this});
- }
- }
- getShowAnnotations () {
- return this.showAnnotations;
- }
-
- setDEMCollisionsEnabled(value){
- if(this.useDEMCollisions !== value){
- this.useDEMCollisions = value;
- this.dispatchEvent({'type': 'use_demcollisions_changed', 'viewer': this});
- };
- };
- getDEMCollisionsEnabled () {
- return this.useDEMCollisions;
- };
- setEDLEnabled (value) {
- value = Boolean(value) && Features.SHADER_EDL.isSupported();
-
- if (this.useEDL !== value) {
- this.useEDL = value;
- this.dispatchEvent({'type': 'use_edl_changed', 'viewer': this});
- }
- };
- getEDLEnabled () {
- return this.useEDL;
- };
- setEDLRadius (value) {
- if (this.edlRadius !== value) {
- this.edlRadius = value;
- this.dispatchEvent({'type': 'edl_radius_changed', 'viewer': this});
- }
- };
- getEDLRadius () {
- return this.edlRadius;
- };
- setEDLStrength (value) {
- if (this.edlStrength !== value) {
- this.edlStrength = value;
- this.dispatchEvent({'type': 'edl_strength_changed', 'viewer': this});
- }
- };
- getEDLStrength () {
- return this.edlStrength;
- };
- setEDLOpacity (value) {
- if (this.edlOpacity !== value) {
- this.edlOpacity = value;
- this.dispatchEvent({'type': 'edl_opacity_changed', 'viewer': this});
- }
- };
- getEDLOpacity () {
- return this.edlOpacity;
- };
- setFOV (value) {
- if (this.fov !== value) {
- let oldFov = this.fov;
- this.fov = value;
- this.scene.cameraP.fov = this.fov;
- this.scene.cameraP.updateProjectionMatrix();
- this.dispatchEvent({'type': 'fov_changed', 'viewer': this, oldFov, fov:this.fov});
- }
- };
- getFOV () {
- return this.fov;
- };
- disableAnnotations () {
- this.scene.annotations.traverse(annotation => {
- annotation.domElement.css('pointer-events', 'none');
- // return annotation.visible;
- });
- };
- enableAnnotations () {
- this.scene.annotations.traverse(annotation => {
- annotation.domElement.css('pointer-events', 'auto');
- // return annotation.visible;
- });
- }
- setClassifications(classifications){
- this.classifications = classifications;
- this.dispatchEvent({'type': 'classifications_changed', 'viewer': this});
- }
- setClassificationVisibility (key, value) {
- if (!this.classifications[key]) {
- this.classifications[key] = {visible: value, name: 'no name'};
- this.dispatchEvent({'type': 'classification_visibility_changed', 'viewer': this});
- } else if (this.classifications[key].visible !== value) {
- this.classifications[key].visible = value;
- this.dispatchEvent({'type': 'classification_visibility_changed', 'viewer': this});
- }
- }
- toggleAllClassificationsVisibility(){
- let numVisible = 0;
- let numItems = 0;
- for(const key of Object.keys(this.classifications)){
- if(this.classifications[key].visible){
- numVisible++;
- }
- numItems++;
- }
- let visible = true;
- if(numVisible === numItems){
- visible = false;
- }
- let somethingChanged = false;
- for(const key of Object.keys(this.classifications)){
- if(this.classifications[key].visible !== visible){
- this.classifications[key].visible = visible;
- somethingChanged = true;
- }
- }
- if(somethingChanged){
- this.dispatchEvent({'type': 'classification_visibility_changed', 'viewer': this});
- }
- }
- setFilterReturnNumberRange(from, to){
- this.filterReturnNumberRange = [from, to];
- this.dispatchEvent({'type': 'filter_return_number_range_changed', 'viewer': this});
- }
- setFilterNumberOfReturnsRange(from, to){
- this.filterNumberOfReturnsRange = [from, to];
- this.dispatchEvent({'type': 'filter_number_of_returns_range_changed', 'viewer': this});
- }
- setFilterGPSTimeRange(from, to){
- this.filterGPSTimeRange = [from, to];
- this.dispatchEvent({'type': 'filter_gps_time_range_changed', 'viewer': this});
- }
- setFilterPointSourceIDRange(from, to){
- this.filterPointSourceIDRange = [from, to];
- this.dispatchEvent({'type': 'filter_point_source_id_range_changed', 'viewer': this});
- }
- setLengthUnit (value) {
- switch (value) {
- case 'm':
- this.lengthUnit = LengthUnits.METER;
- this.lengthUnitDisplay = LengthUnits.METER;
- break;
- case 'ft':
- this.lengthUnit = LengthUnits.FEET;
- this.lengthUnitDisplay = LengthUnits.FEET;
- break;
- case 'in':
- this.lengthUnit = LengthUnits.INCH;
- this.lengthUnitDisplay = LengthUnits.INCH;
- break;
- }
- this.dispatchEvent({ 'type': 'length_unit_changed', 'viewer': this, value: value});
- };
- setLengthUnitAndDisplayUnit(lengthUnitValue, lengthUnitDisplayValue) {
- switch (lengthUnitValue) {
- case 'm':
- this.lengthUnit = LengthUnits.METER;
- break;
- case 'ft':
- this.lengthUnit = LengthUnits.FEET;
- break;
- case 'in':
- this.lengthUnit = LengthUnits.INCH;
- break;
- }
- switch (lengthUnitDisplayValue) {
- case 'm':
- this.lengthUnitDisplay = LengthUnits.METER;
- break;
- case 'ft':
- this.lengthUnitDisplay = LengthUnits.FEET;
- break;
- case 'in':
- this.lengthUnitDisplay = LengthUnits.INCH;
- break;
- }
- this.dispatchEvent({ 'type': 'length_unit_changed', 'viewer': this, value: lengthUnitValue });
- };
- zoomTo(node, factor, animationDuration = 0){
- let view = this.scene.view;
- let camera = this.scene.cameraP.clone();
- camera.rotation.copy(this.scene.cameraP.rotation);
- camera.rotation.order = "ZXY";
- camera.rotation.x = Math.PI / 2 + view.pitch;
- camera.rotation.z = view.yaw;
- camera.updateMatrix();
- camera.updateMatrixWorld();
- camera.zoomTo(node, factor);
- let bs;
- if (node.boundingSphere) {
- bs = node.boundingSphere;
- } else if (node.geometry && node.geometry.boundingSphere) {
- bs = node.geometry.boundingSphere;
- } else {
- bs = node.boundingBox.getBoundingSphere(new Sphere());
- }
- bs = bs.clone().applyMatrix4(node.matrixWorld);
- let startPosition = view.position.clone();
- let endPosition = camera.position.clone();
- let startTarget = view.getPivot();
- let endTarget = bs.center;
- let startRadius = view.radius;
- let endRadius = endPosition.distanceTo(endTarget);
- let easing = TWEEN.Easing.Quartic.Out;
- { // animate camera position
- let pos = startPosition.clone();
- let tween = new TWEEN.Tween(pos).to(endPosition, animationDuration);
- tween.easing(easing);
- tween.onUpdate(() => {
- view.position.copy(pos);
- });
- tween.start();
- }
- { // animate camera target
- let target = startTarget.clone();
- let tween = new TWEEN.Tween(target).to(endTarget, animationDuration);
- tween.easing(easing);
- tween.onUpdate(() => {
- view.lookAt(target);
- });
- tween.onComplete(() => {
- view.lookAt(target);
- this.dispatchEvent({type: 'focusing_finished', target: this});
- });
- this.dispatchEvent({type: 'focusing_started', target: this});
- tween.start();
- }
- };
- moveToGpsTimeVicinity(time){
- const result = Potree.Utils.findClosestGpsTime(time, viewer);
- const box = result.node.pointcloud.deepestNodeAt(result.position).getBoundingBox();
- const diameter = box.min.distanceTo(box.max);
- const camera = this.scene.getActiveCamera();
- const offset = camera.getWorldDirection(new Vector3()).multiplyScalar(diameter);
- const newCamPos = result.position.clone().sub(offset);
- this.scene.view.position.copy(newCamPos);
- this.scene.view.lookAt(result.position);
- }
- showAbout () {
- $(function () {
- $('#about-panel').dialog();
- });
- };
-
- getGpsTimeExtent(){
- const range = [Infinity, -Infinity];
- for(const pointcloud of this.scene.pointclouds){
- const attributes = pointcloud.pcoGeometry.pointAttributes.attributes;
- const aGpsTime = attributes.find(a => a.name === "gps-time");
- if(aGpsTime){
- range[0] = Math.min(range[0], aGpsTime.range[0]);
- range[1] = Math.max(range[1], aGpsTime.range[1]);
- }
- }
- return range;
- }
- fitToScreen (factor = 1, animationDuration = 0) {
- let box = this.getBoundingBox(this.scene.pointclouds);
- let node = new Object3D();
- node.boundingBox = box;
- this.zoomTo(node, factor, animationDuration);
- this.controls.stop();
- };
- toggleNavigationCube() {
- this.navigationCube.visible = !this.navigationCube.visible;
- }
- /* setView(pos, view) {
- if(!pos) return;
-
- switch(pos) {
- case "F":
- this.setFrontView(view);
- break;
- case "B":
- this.setBackView(view);
- break;
- case "L":
- this.setLeftView(view);
- break;
- case "R":
- this.setRightView(view);
- break;
- case "U":
- this.setTopView(view);
- break;
- case "D":
- this.setBottomView(view);
- break;
- }
- } */
-
- setTopView(view){
- view = view || this.scene.view;
- view.setCubeView("top");
- this.fitToScreen();
- };
-
- setBottomView(){
- this.scene.view.yaw = -Math.PI;
- this.scene.view.pitch = Math.PI / 2;
-
- this.fitToScreen();
- };
- setFrontView(view){
- view = view || this.scene.view;
- view.yaw = 0;
- view.pitch = 0;
- this.fitToScreen();
- };
-
- setBackView(view){
- view = view || this.scene.view;
- view.yaw = Math.PI;
- view.pitch = 0;
-
- this.fitToScreen();
- };
- setLeftView(){
- this.scene.view.yaw = -Math.PI / 2;
- this.scene.view.pitch = 0;
- this.fitToScreen();
- };
- setRightView () {
- this.scene.view.yaw = Math.PI / 2;
- this.scene.view.pitch = 0;
- this.fitToScreen();
- };
- flipYZ () {
- this.isFlipYZ = !this.isFlipYZ;
- // TODO flipyz
- console.log('TODO');
- }
-
- setCameraMode(mode){
- this.scene.cameraMode = mode;
- for(let pointcloud of this.scene.pointclouds) {
- pointcloud.material.useOrthographicCamera = mode == CameraMode.ORTHOGRAPHIC;
- }
- }
- getProjection(){
- const pointcloud = this.scene.pointclouds[0];
- if(pointcloud){
- return pointcloud.projection;
- }else {
- return null;
- }
- }
- async loadProject(url,done){
- const response = await fetch(url);
- if(response.ok){
- const text = await response.text();
- const json = lib.parse(text);
- // const json = JSON.parse(text);
- if(json.type === "Potree"){
- Potree.loadProject(viewer, json, done);
- }
-
- }else {
- console.warn("未能加载:"+url );
- }
-
- }
- saveProject(){
- return Potree.saveProject(this);
- }
-
- loadSettingsFromURL(){
- if(Utils.getParameterByName("pointSize")){
- this.setPointSize(parseFloat(Utils.getParameterByName("pointSize")));
- }
-
- if(Utils.getParameterByName("FOV")){
- this.setFOV(parseFloat(Utils.getParameterByName("FOV")));
- }
-
- if(Utils.getParameterByName("opacity")){
- this.setOpacity(parseFloat(Utils.getParameterByName("opacity")));
- }
-
- if(Utils.getParameterByName("edlEnabled")){
- let enabled = Utils.getParameterByName("edlEnabled") === "true";
- this.setEDLEnabled(enabled);
- }
- if (Utils.getParameterByName('edlRadius')) {
- this.setEDLRadius(parseFloat(Utils.getParameterByName('edlRadius')));
- }
- if (Utils.getParameterByName('edlStrength')) {
- this.setEDLStrength(parseFloat(Utils.getParameterByName('edlStrength')));
- }
- if (Utils.getParameterByName('pointBudget')) {
- this.setPointBudget(parseFloat(Utils.getParameterByName('pointBudget')));
- }
- if (Utils.getParameterByName('showBoundingBox')) {
- let enabled = Utils.getParameterByName('showBoundingBox') === 'true';
- if (enabled) {
- this.setShowBoundingBox(true);
- } else {
- this.setShowBoundingBox(false);
- }
- }
- if (Utils.getParameterByName('material')) {
- let material = Utils.getParameterByName('material');
- this.setMaterial(material);
- }
- if (Utils.getParameterByName('pointSizing')) {
- let sizing = Utils.getParameterByName('pointSizing');
- this.setPointSizing(sizing);
- }
- if (Utils.getParameterByName('quality')) {
- let quality = Utils.getParameterByName('quality');
- this.setQuality(quality);
- }
- if (Utils.getParameterByName('position')) {
- let value = Utils.getParameterByName('position');
- value = value.replace('[', '').replace(']', '');
- let tokens = value.split(';');
- let x = parseFloat(tokens[0]);
- let y = parseFloat(tokens[1]);
- let z = parseFloat(tokens[2]);
- this.scene.view.position.set(x, y, z);
- }
- if (Utils.getParameterByName('target')) {
- let value = Utils.getParameterByName('target');
- value = value.replace('[', '').replace(']', '');
- let tokens = value.split(';');
- let x = parseFloat(tokens[0]);
- let y = parseFloat(tokens[1]);
- let z = parseFloat(tokens[2]);
- this.scene.view.lookAt(new Vector3(x, y, z));
- }
- if (Utils.getParameterByName('background')) {
- let value = Utils.getParameterByName('background');
- this.setBackground(value);
- }
- // if(Utils.getParameterByName("elevationRange")){
- // let value = Utils.getParameterByName("elevationRange");
- // value = value.replace("[", "").replace("]", "");
- // let tokens = value.split(";");
- // let x = parseFloat(tokens[0]);
- // let y = parseFloat(tokens[1]);
- //
- // this.setElevationRange(x, y);
- // //this.scene.view.target.set(x, y, z);
- // }
- };
- // ------------------------------------------------------------------------------------
- // Viewer Internals
- // ------------------------------------------------------------------------------------
- createControls () {
- { // create FIRST PERSON CONTROLS
- this.fpControls = new FirstPersonControls(this, this.mainViewport);
- this.fpControls.enabled = false;
- this.fpControls.addEventListener('start', this.disableAnnotations.bind(this));
- this.fpControls.addEventListener('end', this.enableAnnotations.bind(this));
- /* this.addEventListener("loadPointCloudDone", ()=>{
- let boundPlane = new THREE.Box3()
- boundPlane.expandByPoint(this.bound.boundingBox.min.clone())//最低高度为bound的最低
- boundPlane.expandByPoint(this.bound.boundingBox.max.clone().setZ(this.bound.center.z))//最高高度为bound的中心高度
- FirstPersonControls.boundPlane = boundPlane
- FirstPersonControls.standardSpeed = THREE.Math.clamp( Math.sqrt(this.bound.boundSize.length() )/ 100 , 0.02,0.5); //在这个boundPlane中的速度
- }) */
-
- }
- // { // create GEO CONTROLS
- // this.geoControls = new GeoControls(this.scene.camera, this.renderer.domElement);
- // this.geoControls.enabled = false;
- // this.geoControls.addEventListener("start", this.disableAnnotations.bind(this));
- // this.geoControls.addEventListener("end", this.enableAnnotations.bind(this));
- // this.geoControls.addEventListener("move_speed_changed", (event) => {
- // this.setMoveSpeed(this.geoControls.moveSpeed);
- // });
- // }
- { // create ORBIT CONTROLS
- this.orbitControls = new OrbitControls(this);
- this.orbitControls.enabled = false;
- this.orbitControls.addEventListener('start', this.disableAnnotations.bind(this));
- this.orbitControls.addEventListener('end', this.enableAnnotations.bind(this));
- }
- { // create EARTH CONTROLS
- this.earthControls = new EarthControls(this);
- this.earthControls.enabled = false;
- this.earthControls.addEventListener('start', this.disableAnnotations.bind(this));
- this.earthControls.addEventListener('end', this.enableAnnotations.bind(this));
- }
- { // create DEVICE ORIENTATION CONTROLS
- this.deviceControls = new DeviceOrientationControls(this);
- this.deviceControls.enabled = false;
- this.deviceControls.addEventListener('start', this.disableAnnotations.bind(this));
- this.deviceControls.addEventListener('end', this.enableAnnotations.bind(this));
- }
- /* { // create VR CONTROLS
- this.vrControls = new VRControls(this);
- this.vrControls.enabled = false;
- this.vrControls.addEventListener('start', this.disableAnnotations.bind(this));
- this.vrControls.addEventListener('end', this.enableAnnotations.bind(this));
- } */
- };
- toggleSidebar () {
- let renderArea = $('#potree_render_area');
- let isVisible = renderArea.css('left') !== '0px';
- if (isVisible) {
- renderArea.css('left', '0px');
- } else {
- renderArea.css('left', '300px');
- }
- };
- toggleMap () {
- // let map = $('#potree_map');
- // map.toggle(100);
- if (this.mapView) {
- this.mapView.toggle();
- }
- };
- onGUILoaded(callback){
- if(this.guiLoaded){
- callback();
- }else {
- this.guiLoadTasks.push(callback);
- }
- }
- promiseGuiLoaded(){
- return new Promise( resolve => {
- if(this.guiLoaded){
- resolve();
- }else {
- this.guiLoadTasks.push(resolve);
- }
-
- });
- }
- loadGUI(callback){
- if(callback){
- this.onGUILoaded(callback);
- }
- let viewer = this;
- let sidebarContainer = $('#potree_sidebar_container');
- sidebarContainer.load(new URL(Potree.scriptPath + '/' + (Potree.settings.sidebar || 'sidebar.html')).href, () => {
- sidebarContainer.css('width', '300px');
- sidebarContainer.css('height', '100%');
- let imgMenuToggle = document.createElement('img');
- imgMenuToggle.src = new URL(Potree.resourcePath + '/icons/menu_button.svg').href;
- imgMenuToggle.onclick = this.toggleSidebar;
- imgMenuToggle.classList.add('potree_menu_toggle');
- let imgMapToggle = document.createElement('img');
- imgMapToggle.src = new URL(Potree.resourcePath + '/icons/map_icon.png').href;
- imgMapToggle.style.display = 'none';
- imgMapToggle.onclick = e => { this.toggleMap(); };
- imgMapToggle.id = 'potree_map_toggle';
-
- let elButtons = $("#potree_quick_buttons").get(0);
- elButtons.append(imgMenuToggle);
- elButtons.append(imgMapToggle);
- /*
- VRButton.createButton(this.renderer).then(vrButton => {
- if(vrButton == null){
- console.log("VR not supported or active.");
- return;
- }
- this.renderer.xr.enabled = true;
- let element = vrButton.element;
- element.style.position = "";
- element.style.bottom = "";
- element.style.left = "";
- element.style.margin = "4px";
- element.style.fontSize = "100%";
- element.style.width = "2.5em";
- element.style.height = "2.5em";
- element.style.padding = "0";
- element.style.textShadow = "black 2px 2px 2px";
- element.style.display = "block";
- elButtons.append(element);
- vrButton.onStart(() => {
- this.dispatchEvent({type: "vr_start"});
- });
- vrButton.onEnd(() => {
- this.dispatchEvent({type: "vr_end"});
- });
- });
-
- this.mapView = new MapView(this);
- this.mapView.init(); */
-
- i18n.init({
- lng: 'en',
- resGetPath: Potree.resourcePath + '/lang/__lng__/__ns__.json',
- preload: ['en', 'fr', 'de', 'jp', 'se', 'es', 'zh'],
- getAsync: true,
- debug: false
- }, function (t) {
- // Start translation once everything is loaded
- $('body').i18n();
- });
- $(() => {
- //initSidebar(this);
- let sidebar = new Sidebar(this);
- sidebar.init();
- this.sidebar = sidebar;
- //if (callback) {
- // $(callback);
- //}
- let elProfile = $('<div>').load(new URL(Potree.scriptPath + '/profile.html').href, () => {
- $(document.body).append(elProfile.children());
- this.profileWindow = new ProfileWindow(this);
- this.profileWindowController = new ProfileWindowController(this);
- $('#profile_window').draggable({
- handle: $('#profile_titlebar'),
- containment: $(document.body)
- });
- $('#profile_window').resizable({
- containment: $(document.body),
- handles: 'n, e, s, w'
- });
- $(() => {
- this.guiLoaded = true;
- for(let task of this.guiLoadTasks){
- task();
- }
- });
- });
-
- });
-
- });
- return this.promiseGuiLoaded();
- }
- setLanguage (lang) {
- i18n.setLng(lang);
- $('body').i18n();
- }
- setServer (server) {
- this.server = server;
- }
- initDragAndDrop(){
- function allowDrag(e) {
- e.dataTransfer.dropEffect = 'copy';
- e.preventDefault();
- }
- let dropHandler = async (event) => {
- console.log(event);
- event.preventDefault();
- for(const item of event.dataTransfer.items){
- console.log(item);
- if(item.kind !== "file"){
- continue;
- }
- const file = item.getAsFile();
- const isJson = file.name.toLowerCase().endsWith(".json");
- const isGeoPackage = file.name.toLowerCase().endsWith(".gpkg");
- if(isJson){
- try{
- const text = await file.text();
- const json = JSON.parse(text);
- if(json.type === "Potree"){
- Potree.loadProject(viewer, json);
- }
- }catch(e){
- console.error("failed to parse the dropped file as JSON");
- console.error(e);
- }
- }else if(isGeoPackage){
- const hasPointcloud = viewer.scene.pointclouds.length > 0;
- if(!hasPointcloud){
- let msg = "At least one point cloud is needed that specifies the ";
- msg += "coordinate reference system before loading vector data.";
- console.error(msg);
- }else {
- proj4.defs("WGS84", "+proj=longlat +ellps=WGS84 +datum=WGS84 +no_defs");
- proj4.defs("pointcloud", this.getProjection());
- let transform = proj4("WGS84", "pointcloud");
- const buffer = await file.arrayBuffer();
- const params = {
- transform: transform,
- source: file.name,
- };
-
- const geo = await Potree.GeoPackageLoader.loadBuffer(buffer, params);
- viewer.scene.addGeopackage(geo);
- }
- }
-
- }
- };
- $("body")[0].addEventListener("dragenter", allowDrag);
- $("body")[0].addEventListener("dragover", allowDrag);
- $("body")[0].addEventListener("drop", dropHandler);
- }
-
-
-
- updateAnnotations () {
- if(!this.visibleAnnotations){
- this.visibleAnnotations = new Set();
- }
- this.scene.annotations.updateBounds();
- this.scene.cameraP.updateMatrixWorld();
- this.scene.cameraO.updateMatrixWorld();
-
- let distances = [];
- let renderAreaSize = this.renderer.getSize(new Vector2$1());
- let viewer = this;
- let visibleNow = [];
- this.scene.annotations.traverse(annotation => {
- if (annotation === this.scene.annotations) {
- return true;
- }
- if (!annotation.visible) {
- return false;
- }
- annotation.scene = this.scene;
- let element = annotation.domElement;
- let position = annotation.position.clone();
- position.add(annotation.offset);
- if (!position) {
- position = annotation.boundingBox.getCenter(new Vector3());
- }
- let distance = viewer.scene.cameraP.position.distanceTo(position);
- let radius = annotation.boundingBox.getBoundingSphere(new Sphere()).radius;
- let screenPos = new Vector3();
- let screenSize = 0;
- {
- // SCREEN POS
- screenPos.copy(position).project(this.scene.getActiveCamera());
- screenPos.x = renderAreaSize.x * (screenPos.x + 1) / 2;
- screenPos.y = renderAreaSize.y * (1 - (screenPos.y + 1) / 2);
- // SCREEN SIZE
- if(viewer.scene.cameraMode == CameraMode.PERSPECTIVE) {
- let fov = Math.PI * viewer.scene.cameraP.fov / 180;
- let slope = Math.tan(fov / 2.0);
- let projFactor = 0.5 * renderAreaSize.y / (slope * distance);
- screenSize = radius * projFactor;
- } else {
- screenSize = Utils.projectedRadiusOrtho(radius, viewer.scene.cameraO.projectionMatrix, renderAreaSize.x, renderAreaSize.y);
- }
- }
- element.css("left", screenPos.x + "px");
- element.css("top", screenPos.y + "px");
- //element.css("display", "block");
- let zIndex = 10000000 - distance * (10000000 / this.scene.cameraP.far);
- if(annotation.descriptionVisible){
- zIndex += 10000000;
- }
- element.css("z-index", parseInt(zIndex));
- if(annotation.children.length > 0){
- let expand = screenSize > annotation.collapseThreshold || annotation.boundingBox.containsPoint(this.scene.getActiveCamera().position);
- annotation.expand = expand;
- if (!expand) {
- //annotation.display = (screenPos.z >= -1 && screenPos.z <= 1);
- let inFrustum = (screenPos.z >= -1 && screenPos.z <= 1);
- if(inFrustum){
- visibleNow.push(annotation);
- }
- }
- return expand;
- } else {
- //annotation.display = (screenPos.z >= -1 && screenPos.z <= 1);
- let inFrustum = (screenPos.z >= -1 && screenPos.z <= 1);
- if(inFrustum){
- visibleNow.push(annotation);
- }
- }
-
- });
- let notVisibleAnymore = new Set(this.visibleAnnotations);
- for(let annotation of visibleNow){
- annotation.display = true;
-
- notVisibleAnymore.delete(annotation);
- }
- this.visibleAnnotations = visibleNow;
- for(let annotation of notVisibleAnymore){
- annotation.display = false;
- }
- }
- updateMaterialDefaults(pointcloud){
- // PROBLEM STATEMENT:
- // * [min, max] of intensity, source id, etc. are computed as point clouds are loaded
- // * the point cloud material won't know the range it should use until some data is loaded
- // * users can modify the range at runtime, but sensible default ranges should be
- // applied even if no GUI is present
- // * display ranges shouldn't suddenly change even if the actual range changes over time.
- // e.g. the root node has intensity range [1, 478]. One of the descendants increases range to
- // [0, 2047]. We should not automatically change to the new range because that would result
- // in sudden and drastic changes of brightness. We should adjust the min/max of the sidebar slider.
- const material = pointcloud.material;
- const attIntensity = pointcloud.getAttribute("intensity");
-
- if(attIntensity != null && material.intensityRange[0] === Infinity){
- material.intensityRange = [...attIntensity.range];
- }
- // const attIntensity = pointcloud.getAttribute("intensity");
- // if(attIntensity && material.intensityRange[0] === Infinity){
- // material.intensityRange = [...attIntensity.range];
- // }
- // let attributes = pointcloud.getAttributes();
- // for(let attribute of attributes.attributes){
- // if(attribute.range){
- // let range = [...attribute.range];
- // material.computedRange.set(attribute.name, range);
- // //material.setRange(attribute.name, range);
- // }
- // }
- }
- update(delta, timestamp){
- if(Potree.measureTimings) performance.mark("update-start");
- this.dispatchEvent({
- type: 'update_start',
- delta: delta,
- timestamp: timestamp});
-
-
- this.updateScreenSize(); //判断是否改变canvas大小
-
-
- const scene = this.scene;
- const camera = scene.getActiveCamera();
- const visiblePointClouds = this.scene.pointclouds.filter(pc => pc.visible);
-
- Potree.pointLoadLimit = Potree.pointBudget * 2;
- const lTarget = camera.position.clone().add(camera.getWorldDirection(new Vector3()).multiplyScalar(1000));
- this.scene.directionalLight.position.copy(camera.position);
- this.scene.directionalLight.lookAt(lTarget);
-
- for (let pointcloud of visiblePointClouds) {
- pointcloud.showBoundingBox = this.showBoundingBox;
- pointcloud.generateDEM = this.generateDEM;
- pointcloud.minimumNodePixelSize = this.minNodeSize;
- let material = pointcloud.material;
- material.uniforms.uFilterReturnNumberRange.value = this.filterReturnNumberRange;
- material.uniforms.uFilterNumberOfReturnsRange.value = this.filterNumberOfReturnsRange;
- material.uniforms.uFilterGPSTimeClipRange.value = this.filterGPSTimeRange;
- material.uniforms.uFilterPointSourceIDClipRange.value = this.filterPointSourceIDRange;
- material.classification = this.classifications;
- material.recomputeClassification();
- this.updateMaterialDefaults(pointcloud);
- }
- {
- if(this.showBoundingBox){
- let bbRoot = this.scene.scene.getObjectByName("potree_bounding_box_root");
- if(!bbRoot){
- let node = new Object3D();
- node.name = "potree_bounding_box_root";
- this.scene.scene.add(node);
- bbRoot = node;
- }
- let visibleBoxes = [];
- for(let pointcloud of this.scene.pointclouds){
- for(let node of pointcloud.visibleNodes.filter(vn => vn.boundingBoxNode !== undefined)){
- let box = node.boundingBoxNode;
- visibleBoxes.push(box);
- }
- }
- bbRoot.children = visibleBoxes;
- }
- }
- if (!this.freeze) {
-
- /*let cameraGroup = []
- let size = this.renderer.getSize(new THREE.Vector2())
- if(this.viewports){
- this.viewports.forEach(viewport=>{
- if(!viewport.active)return
- cameraGroup.push({camera:viewport.camera, areaSize:new THREE.Vector2(Math.floor(size.x * viewport.width), Math.floor(size.y * viewport.height))})
- })
- }else{
- cameraGroup.push({camera, areaSize:size})
- }
- let result = Potree.updatePointClouds(scene.pointclouds, cameraGroup );
- */
- // DEBUG - ONLY DISPLAY NODES THAT INTERSECT MOUSE
- //if(false){
- // let renderer = viewer.renderer;
- // let mouse = viewer.inputHandler.mouse;
- // let nmouse = {
- // x: (mouse.x / renderer.domElement.clientWidth) * 2 - 1,
- // y: -(mouse.y / renderer.domElement.clientHeight) * 2 + 1
- // };
- // let pickParams = {};
- // //if(params.pickClipped){
- // // pickParams.pickClipped = params.pickClipped;
- // //}
- // pickParams.x = mouse.x;
- // pickParams.y = renderer.domElement.clientHeight - mouse.y;
- // let raycaster = new THREE.Raycaster();
- // raycaster.setFromCamera(nmouse, camera);
- // let ray = raycaster.ray;
- // for(let pointcloud of scene.pointclouds){
- // let nodes = pointcloud.nodesOnRay(pointcloud.visibleNodes, ray);
- // pointcloud.visibleNodes = nodes;
- // }
- //}
- // const tStart = performance.now();
- // const worldPos = new THREE.Vector3();
- // const camPos = viewer.scene.getActiveCamera().getWorldPosition(new THREE.Vector3());
- // let lowestDistance = Infinity;
- // let numNodes = 0;
- // viewer.scene.scene.traverse(node => {
- // node.getWorldPosition(worldPos);
- // const distance = worldPos.distanceTo(camPos);
- // lowestDistance = Math.min(lowestDistance, distance);
- // numNodes++;
- // if(Number.isNaN(distance)){
- // console.error(":(");
- // }
- // });
- // const duration = (performance.now() - tStart).toFixed(2);
- // Potree.debug.computeNearDuration = duration;
- // Potree.debug.numNodes = numNodes;
- //console.log(lowestDistance.toString(2), duration);
- //搬走
- /* const tStart = performance.now();
- const campos = camera.position;
- let closestImage = Infinity;
- for(const images of this.scene.orientedImages){
- for(const image of images.images){
- const distance = image.mesh.position.distanceTo(campos);
- closestImage = Math.min(closestImage, distance);
- }
- }
- const tEnd = performance.now();
- if(result.lowestSpacing !== Infinity){
- let near = result.lowestSpacing * 10.0;
- let far = -this.getBoundingBox().applyMatrix4(camera.matrixWorldInverse).min.z;
- far = Math.max(far * 1.5, 10000);
- near = Math.min(100.0, Math.max(0.01, near));
- near = Math.min(near, closestImage);
- far = Math.max(far, near + 10000);
- if(near === Infinity){
- near = 0.1;
- }
-
- camera.near = near;
- camera.far = far;
- }else{
- // don't change near and far in this case
- }
- if(this.scene.cameraMode == CameraMode.ORTHOGRAPHIC) {
- camera.near = -camera.far;
- }*/
- }
-
-
-
-
-
-
-
-
-
-
- this.scene.cameraP.fov = this.fov;
-
- let controls = this.getControls();
- if (controls === this.deviceControls) {
- this.controls.setScene(scene);
- this.controls.update(delta);
- this.scene.cameraP.position.copy(scene.view.position);
- this.scene.cameraO.position.copy(scene.view.position);
- } else if (controls !== null) {
- controls.setScene(scene);
- controls.update(delta);
-
- //更新camera
- this.viewports.forEach(viewport=>{
- if(!viewport.active)return
- viewport.view.applyToCamera(viewport.camera);
-
- });
- }
-
- /* this.viewports.forEach(e=>{//判断camera画面是否改变
- if(e.cameraChanged()){
- this.dispatchEvent({
- type: "camera_changed",
- camera: e.camera,
- viewport : e
- })
-
- }
- }) */
-
- this.cameraChanged();//判断camera画面是否改变
-
- /* {//判断camera画面是否改变
- if(this._previousCamera === undefined){
- this._previousCamera = this.scene.getActiveCamera().clone();
- this._previousCamera.rotation.copy(this.scene.getActiveCamera().rotation);
- }
- if(!this._previousCamera.matrixWorld.equals(camera.matrixWorld) ||
- !this._previousCamera.projectionMatrix.equals(camera.projectionMatrix)
- ){
- this.dispatchEvent({
- type: "camera_changed",
- previous: this._previousCamera,
- camera: camera
- });
- }
- this._previousCamera = this.scene.getActiveCamera().clone();
- this._previousCamera.rotation.copy(this.scene.getActiveCamera().rotation);
- } */
-
- { // update clip boxes
- let boxes = [];
-
- // volumes with clipping enabled
- //boxes.push(...this.scene.volumes.filter(v => (v.clip)));
- boxes.push(...this.scene.volumes.filter(v => (v.clip && v instanceof BoxVolume)));
- // profile segments
- for(let profile of this.scene.profiles){
- boxes.push(...profile.boxes);
- }
-
- // Needed for .getInverse(), pre-empt a determinant of 0, see #815 / #816
- let degenerate = (box) => box.matrixWorld.determinant() !== 0;
-
- let clipBoxes = boxes.filter(degenerate).map( box => {
- box.updateMatrixWorld();
-
- let boxInverse = box.matrixWorld.clone().invert();
- let boxPosition = box.getWorldPosition(new Vector3());
- return {box: box, inverse: boxInverse, position: boxPosition};
- });
- let clipPolygons = this.scene.polygonClipVolumes.filter(vol => vol.initialized);
-
- // set clip volumes in material
- for(let pointcloud of visiblePointClouds){
- pointcloud.material.setClipBoxes(clipBoxes);
- pointcloud.material.setClipPolygons(clipPolygons, this.clippingTool.maxPolygonVertices);
- pointcloud.material.clipTask = this.clipTask;
- pointcloud.material.clipMethod = this.clipMethod;
- }
- }
- {
- for(let pointcloud of visiblePointClouds){
- pointcloud.material.elevationGradientRepeat = this.elevationGradientRepeat;
- }
- }
-
- { // update navigation cube
- this.navigationCube.update(camera.rotation);
- }
- this.updateAnnotations();
-
- if(this.mapView){
- this.mapView.update(delta);
- if(this.mapView.sceneProjection){
- $( "#potree_map_toggle" ).css("display", "block");
-
- }
- }
- TWEEN.update(timestamp);
- transitions.update(delta);
- this.transformationTool.update();
-
- if(Potree.settings.editType != 'pano' && Potree.settings.editType != 'merge'){
- this.modules.ParticleEditor.update(delta);
- this.mapViewer.update(delta);
- }
- this.dispatchEvent({
- type: 'update',
- delta: delta,
- timestamp: timestamp});
-
- if(Potree.measureTimings) {
- performance.mark("update-end");
- performance.measure("update", "update-start", "update-end");
- }
-
-
- //add ------
- this.reticule.updateVisible();
-
- }
-
-
- updateViewPointcloud(camera, areaSize, isViewport){
-
-
- let result = Potree.updatePointClouds(this.scene.pointclouds, camera, areaSize );
-
- //if(isViewport)return
- const tStart = performance.now();
- const campos = camera.position;
- let closestImage = Infinity;
- for(const images of this.scene.orientedImages){
- for(const image of images.images){
- const distance = image.mesh.position.distanceTo(campos);
- closestImage = Math.min(closestImage, distance);
- }
- }
- const tEnd = performance.now();
- //改:不根据点云修改视野near far
- var near = camera.near, far = camera.far;
-
- if(!camera.limitFar && result.lowestSpacing !== Infinity){
-
- //let near = result.lowestSpacing * 10.0;
- let far = -this.getBoundingBox().applyMatrix4(camera.matrixWorldInverse).min.z;
- far = Math.max(far * 1.5, 10000);
- //near = Math.min(100.0, Math.max(0.01, near));
- //near = Math.min(near, closestImage);
- far = Math.max(far, near + 10000);
- /* if(near === Infinity){
- near = 0.1;
- } */
-
- //camera.near = near; //为了其他物体的显示,不修改near
- camera.far = far;
- }
- /* if(this.scene.cameraMode == CameraMode.ORTHOGRAPHIC) {//???
- camera.near = -camera.far;
- } */
- if(/* near != camera.near || */far != camera.far){
- camera.updateProjectionMatrix();
- }
-
- //注:pointcloud.visibleNodes会随着near far自动更新
- }
-
-
-
- getPRenderer(){
- if(this.useHQ){
- if (!this.hqRenderer) {
- this.hqRenderer = new HQSplatRenderer(this);
- }
- this.hqRenderer.useEDL = this.useEDL;
- return this.hqRenderer;
- }else {
- /* if (this.useEDL && Features.SHADER_EDL.isSupported()) {
- if (!this.edlRenderer) {
- this.edlRenderer = new EDLRenderer(this);
- }
- return this.edlRenderer;
- } else {
- if (!this.potreeRenderer) {
- this.potreeRenderer = new PotreeRenderer(this);
- }
- return this.potreeRenderer;
- } */
-
- if (!this.edlRenderer) {
- this.edlRenderer = new EDLRenderer(this);
- }
- return this.edlRenderer;
-
- }
- }
- renderVR(){
- let renderer = this.renderer;
- renderer.setClearColor(0x550000, 0);
- renderer.clear();
- let xr = renderer.xr;
- let dbg = new PerspectiveCamera();
- let xrCameras = xr.getCamera(dbg);
- if(xrCameras.cameras.length !== 2){
- return;
- }
- let makeCam = this.vrControls.getCamera.bind(this.vrControls);
- { // clear framebuffer
- if(viewer.background === "skybox"){
- renderer.setClearColor(0xff0000, 1);
- }else if(viewer.background === "gradient"){
- renderer.setClearColor(0x112233, 1);
- }else if(viewer.background === "black"){
- renderer.setClearColor(0x000000, 1);
- }else if(viewer.background === "white"){
- renderer.setClearColor(0xFFFFFF, 1);
- }else {
- renderer.setClearColor(0x000000, 0);
- }
- renderer.clear();
- }
- // render background
- if(this.background === "skybox"){
- let {skybox} = this;
- let cam = makeCam();
- skybox.camera.rotation.copy(cam.rotation);
- skybox.camera.fov = cam.fov;
- skybox.camera.aspect = cam.aspect;
-
- // let dbg = new THREE.Object3D();
- let dbg = skybox.parent;
- // dbg.up.set(0, 0, 1);
- dbg.rotation.x = Math.PI / 2;
- // skybox.camera.parent = dbg;
- // dbg.children.push(skybox.camera);
- dbg.updateMatrix();
- dbg.updateMatrixWorld();
- skybox.camera.updateMatrix();
- skybox.camera.updateMatrixWorld();
- skybox.camera.updateProjectionMatrix();
- renderer.render(skybox.scene, skybox.camera);
- // renderer.render(skybox.scene, cam);
- }else if(this.background === "gradient"){
- // renderer.render(this.scene.sceneBG, this.scene.cameraBG);
- }
- this.renderer.xr.getSession().updateRenderState({
- depthNear: 0.1,
- depthFar: 10000
- });
-
- let cam = null;
- let view = null;
- { // render world scene
- cam = makeCam();
- cam.position.z -= 0.8 * cam.scale.x;
- cam.parent = null;
- // cam.near = 0.05;
- cam.near = viewer.scene.getActiveCamera().near;
- cam.far = viewer.scene.getActiveCamera().far;
- cam.updateMatrix();
- cam.updateMatrixWorld();
- this.scene.scene.updateMatrix();
- this.scene.scene.updateMatrixWorld();
- this.scene.scene.matrixAutoUpdate = false;
- let camWorld = cam.matrixWorld.clone();
- view = camWorld.clone().invert();
- this.scene.scene.matrix.copy(view);
- this.scene.scene.matrixWorld.copy(view);
- cam.matrix.identity();
- cam.matrixWorld.identity();
- cam.matrixWorldInverse.identity();
- renderer.render(this.scene.scene, cam);
- this.scene.scene.matrixWorld.identity();
- }
-
- for(let pointcloud of this.scene.pointclouds){
- let viewport = xrCameras.cameras[0].viewport;
- pointcloud.material.useEDL = false;
- pointcloud.screenHeight = viewport.height;
- pointcloud.screenWidth = viewport.width;
- // automatically switch to paraboloids because they cause far less flickering in VR,
- // when point sizes are larger than around 2 pixels
- // if(Features.SHADER_INTERPOLATION.isSupported()){
- // pointcloud.material.shape = Potree.PointShape.PARABOLOID;
- // }
- }
-
- // render point clouds
- for(let xrCamera of xrCameras.cameras){
- let v = xrCamera.viewport;
- renderer.setViewport(v.x, v.y, v.width, v.height);
- // xrCamera.fov = 90;
- { // estimate VR fov
- let proj = xrCamera.projectionMatrix;
- let inv = proj.clone().invert();
- let p1 = new Vector4(0, 1, -1, 1).applyMatrix4(inv);
- let rad = p1.y;
- let fov = 180 * (rad / Math.PI);
- xrCamera.fov = fov;
- }
- for(let pointcloud of this.scene.pointclouds){
- const {material} = pointcloud;
- material.useEDL = false;
- }
- let vrWorld = view.clone().invert();
- vrWorld.multiply(xrCamera.matrixWorld);
- let vrView = vrWorld.clone().invert();
- this.pRenderer.render(this.scene.scenePointCloud, xrCamera, null, {
- viewOverride: vrView,
- });
- }
- { // render VR scene
- let cam = makeCam();
- cam.parent = null;
- renderer.render(this.sceneVR, cam);
- }
- renderer.resetState();
- }
- clear(params={}){
- let background = params.background || this.background;
- let backgroundOpacity = params.backgroundOpacity == void 0 ? this.backgroundOpacity : params.backgroundOpacity;//如果想完全透明,只需要backgroundOpacity为0
- let renderer = this.renderer;
- //let gl = renderer.getContext()
-
- if(background instanceof Color){ //add
- renderer.setClearColor(background, backgroundOpacity);
- }else if(background === "skybox"){
- renderer.setClearColor(0x000000, 0);
- } else if (background === 'gradient') {
- renderer.setClearColor(0x000000, 0);
- } else if (background === 'black') {
- renderer.setClearColor(0x000000, 1);
- } else if (background === 'white') {
- renderer.setClearColor(0xFFFFFF, 1);
- } else {
- renderer.setClearColor(background, backgroundOpacity);
- }
-
- params.target || renderer.clear();
-
-
- }
-
- renderDefault(params_={}){
-
- if(!this.visible || this.paused )return
-
-
- /* if(this.outlinePass.selectedObjects.length){
- this.clear()
- this.composer.render(this.scene.scene, this.mainViewport.camera );
- return;
- } */
-
- let pRenderer = this.getPRenderer();
- let viewports = params_.viewports || this.viewports;
-
- let renderSize;
- if(params_.target){
- renderSize = new Vector2$1(params_.target.width, params_.target.height); //是画布大小
- //可能需要viewer.setSize
- }else {
- renderSize = this.renderer.getSize(new Vector2$1()); //是client大小
- }
-
-
-
- let needSResize = viewports.filter(e=>e.active).length > 1 || params_.resize;
-
-
-
- viewports.forEach(view=>{
- let params = $.extend({},params_);
- params.viewport = view;
- //if(!params.target){
- params.camera = params.camera || view.camera;
- params.extraEnableLayers = view.extraEnableLayers;
- params.cameraLayers = view.cameraLayers;
- //}
-
- if(!view.active)return
- var left,bottom,width,height;
- {
- left = Math.ceil(renderSize.x * view.left);
- bottom = Math.ceil(renderSize.y * view.bottom);
-
- if(params_.target){//有target时最好viewport是专门建出来的
- width = Math.ceil(renderSize.x * view.width); //target的大小可能和viewport不同,比如截图,这时会更改viewport大小
- height = Math.ceil(renderSize.y * view.height);
- }else {
- width = view.resolution.x; // 用的是client的width和height
- height = view.resolution.y;
- }
- if(width == 0 || height == 0)return
-
- let scissorTest = view.width<1 || view.height<1;
- if(params_.target){
- params_.target.viewport.set(left, bottom, width, height);
- scissorTest && params_.target.scissor.set(left, bottom, width, height);
- params_.target.scissorTest = scissorTest;
-
- }else {
- this.renderer.setViewport(left, bottom, width, height); //规定视口,影响图形变换(画布的使用范围)
- scissorTest && this.renderer.setScissor( left, bottom, width, height );//规定渲染范围
- this.renderer.setScissorTest( scissorTest );//开启WebGL剪裁测试功能,如果不开启,.setScissor方法设置的范围不起作用 | width==1且height==1时开启会只有鼠标的地方刷新,很奇怪
-
- }
-
- }
-
-
- if(needSResize){
- this.emitResizeMsg( { viewport:view} );
- }
-
- //needSResize && this.emitResizeMsg({resolution: params_.target ? new THREE.Vector2(width,height) : view.resolution2, left:view.left, bottom:view.bottom })//resize everything such as lines targets
-
-
-
- viewer.dispatchEvent({type: "render.begin", viewer: viewer, viewport:view, params });
-
-
- if(view.render){
- view.render($.extend({}, params, {
- renderer:this.renderer, clear:this.clear.bind(this), resize:null,
- renderOverlay: this.renderOverlay.bind(this), force:!view.noPointcloud //如果要渲染点云,必须也一直渲染地图,否则地图会被覆盖(点云目前未能获取是否改变,也可能有其他动态物体,所以还是一直渲染的好)
- }));
- }
-
- if(!view.noPointcloud ){
-
- //if(!params.target){
- //params.width = width; params.height = height;
-
- //}
- if(view.render){
- params.noBG = true;
- }
-
-
-
- view.beforeRender && view.beforeRender();
-
- this.updateViewPointcloud(params.camera, view.resolution2, true);
-
- params.background = view.background;
- params.backgroundColor = view.backgroundColor;
- params.backgroundOpacity = view.backgroundOpacity;
-
- view.render || this.clear(params);
- pRenderer.clearTargets(params);
- pRenderer.render(params);
-
- }
-
-
- view.render || this.renderOverlay(params);
-
-
-
- view.afterRender && view.afterRender();
- this.dispatchEvent({type: "render.end", viewer: this, viewport:view });
-
-
- });
-
-
-
- /* if(params_.screenshot){ //抗锯齿
- params_.target.viewport.set(0, 0, params_.target.width, params_.target.height);
- //scissorTest && params_.target.scissor.set(left, bottom, width, height);
- params_.target.scissorTest = false
-
- this.renderer.setRenderTarget(params_.target)
-
-
- this.composer.render();
- this.renderer.setRenderTarget(params_.target) //本想再画一层标签,但是viewport总是出错
-
-
- } */
-
-
- this.renderer.setRenderTarget(null);
-
-
- }
-
-
- renderOverlay(params){
-
- let camera = params.camera ? params.camera : this.scene.getActiveCamera();
-
- this.reticule.updateAtViewports(params.viewport);
-
-
- //为什么要在点云之后渲染,否则透明失效 、 会被点云覆盖
- let cameraLayers;
- if(params.cameraLayers) cameraLayers = params.cameraLayers;
- else {
- if(params.isMap)cameraLayers = ['bothMapAndScene'];
- else cameraLayers = ['sceneObjects', 'bothMapAndScene' ];
- }
-
-
- if(cameraLayers.length){
- this.setCameraLayers(camera, cameraLayers, params.extraEnableLayers); //透明贴图层 skybox 、reticule marker 不能遮住测量线
-
- /* if(this.outlinePass.selectedObjects.some(e=>e.isModel).length){
- this.composer.render(this.scene.scene, camera);
- }else{ */
- this.renderer.render(this.scene.scene, camera);
- //}
-
- }
-
- this.dispatchEvent({type: "render.pass.scene", viewer: viewer});
-
-
-
-
- //清除深度 !!!!
- this.renderer.clearDepth();
- //this.transformationTool.update();
-
-
- if(!params.magnifier){
- //测量线
- this.dispatchEvent({type: "render.pass.perspective_overlay", camera, screenshot:params.screenshot});
-
- if(!params.screenshot && !params.isMap){
- this.setCameraLayers(camera, ['magnifier']); //magnifier 遮住测量线
- this.renderer.render(this.scene.scene, camera);
- }
- }
-
- if(!params.isMap) {
- this.setCameraLayers(camera, ['volume','transformationTool']);
- this.renderer.render(this.clippingTool.sceneVolume, camera);
- this.renderer.render(this.transformationTool.scene, camera);
- }
-
- }
- /* renderDefault(){//测试 ios15.4.1
- //let pRenderer = this.getPRenderer();
- //this.clear()
- this.renderer.autoClear = false
- this.renderer.setRenderTarget(null)
- let camera = this.scene.getActiveCamera();
- this.setCameraLayers(camera, [ 'sceneObjects', 'marker' , 'reticule' ,'skybox' ])
- this.renderer.render(this.scene.scene, camera);
- // pRenderer.clearTargets( );
- //pRenderer.render( );
-
- } */
- setLimitFar(state){//切换是否limitFar
- viewer.mainViewport.camera.limitFar = !!state;
- if(state){
- viewer.mainViewport.camera.near = 0.1;
- viewer.mainViewport.camera.far = Potree.settings.displayMode == 'showPanos' ? viewer.farWhenShowPano : Potree.settings.cameraFar;
- viewer.mainViewport.camera.updateProjectionMatrix();
- }
- }
-
-
- setCameraLayers(camera, enableLayers, extraEnableLayers=[]){//add
- camera.layers.disableAll();
- enableLayers.concat(extraEnableLayers).forEach(e=>{
- let layer = Potree.config.renderLayers[e];
- if(layer == void 0){
- console.error('setCameraLayer没找到layer!');
- return
- }
- camera.layers.enable(layer);
- });
- }
- setObjectLayers(object, layerName){//add
- let layer = Potree.config.renderLayers[layerName];
- if(layer == void 0){
- console.error('setCameraLayer没找到layer!');
- return
- }
- object.traverse(e=>{
- e.layers.set(layer);
- });
- }
-
-
- /* updateVisible(object, reason, ifShow, force){//当所有加入的条件都不为false时才显示. reason='force'一般是强制、临时的
- if(!object.unvisibleReasons) object.unvisibleReasons = []; //如果length>0代表不可见
- if(!object.forceVisibleReasons) object.forceVisibleReasons = []; //只要有一项代表一定可见,优先级比unvisibleReasons高
-
- if(ifShow){
- if(force){
- object.forceVisibleReasons.includes(reason) || object.forceVisibleReasons.push(reason)
- object.visible = true;
- object.dispatchEvent({
- type: 'isVisible',
- visible:true,
- reason
- })
- }
- var index = object.unvisibleReasons.indexOf(reason)
- if(index > -1){
- object.unvisibleReasons.splice(index, 1);
- if(object.unvisibleReasons.length == 0){
- object.visible = true;
- //mapChange()
- object.dispatchEvent({
- type: 'isVisible',
- visible:true,
- reason
- })
- }
- }
-
- }else{
- var index = object.forceVisibleReasons.indexOf(reason)
- if(index > -1){//如果是forceVisibleReasons里的,就只是单纯取消之前设置的一定可见
- object.forceVisibleReasons.splice(index, 1);
- }else{
- if(!object.unvisibleReasons.includes(reason)) object.unvisibleReasons.push(reason)
- }
- if(object.forceVisibleReasons.length) return
- var visiBefore = object.visible
- if(object.unvisibleReasons.length && visiBefore){
- object.visible = false
- //mapChange()
- object.dispatchEvent({
- type: 'isVisible',
- visible:false,
- reason,
- })
- }else if(object.unvisibleReasons.length ==0 && !visiBefore){
- object.visible = true;
- //mapChange()
- object.dispatchEvent({
- type: 'isVisible',
- visible:true,
- reason
- })
- }
- }
-
- } */
-
-
- updateVisible(object, reason, ifShow, level=0, type){//当所有加入的条件都不为false时才显示. reason='force'一般是强制、临时的
- if(!object.unvisibleReasons) object.unvisibleReasons = []; //如果length>0代表不可见
- if(!object.visibleReasons) object.visibleReasons = []; //在同级时,优先可见
-
-
- var update = function(){
-
- //先按从高到低的level排列
- object.unvisibleReasons = object.unvisibleReasons.sort((a,b)=>b.level-a.level);
- object.visibleReasons = object.visibleReasons.sort((a,b)=>b.level-a.level);
- var maxVisiLevel = object.visibleReasons[0] ? object.visibleReasons[0].level : -1;
- var maxunVisiLevel = object.unvisibleReasons[0] ? object.unvisibleReasons[0].level : -1;
-
- var shouldVisi = maxVisiLevel >= maxunVisiLevel;
- var visiBefore = object.visible;
-
-
- if(visiBefore != shouldVisi){
- object.visible = shouldVisi;
- object.dispatchEvent({
- type: 'isVisible',
- visible: shouldVisi,
- reason,
- });
- }
-
-
- };
-
-
-
- if(ifShow){
- var index = object.unvisibleReasons.findIndex(e=>e.reason == reason);
- if(index > -1){
- type = 'cancel';
- object.unvisibleReasons.splice(index, 1);
- }
-
- if(type == 'add' ){
- if(!object.visibleReasons.some(e=>e.reason == reason)){
- object.visibleReasons.push({reason,level});
- }
- }
- }else {
- var index = object.visibleReasons.findIndex(e=>e.reason == reason);
- if(index > -1){
- type = 'cancel';
- object.visibleReasons.splice(index, 1);
- }
-
- if(type != 'cancel' ){
- if(!object.unvisibleReasons.some(e=>e.reason == reason)){
- object.unvisibleReasons.push({reason,level});
- }
- }
- }
-
-
-
- update();
-
-
-
-
- }
-
-
-
-
- getObjVisiByReason(object,reason){//获取在某条件下是否可见. 注: 用户在数据集选择可不可见为"datasetSelection"
- if(object.visible)return true
- else {
- return !object.unvisibleReasons || !object.unvisibleReasons.some(e=>e.reason == reason)
- }
- }
-
-
-
-
-
- /* 大规模WebGL应用引发浏览器崩溃的几种情况及解决办法
- https://blog.csdn.net/weixin_30378311/article/details/94846947 */
-
- render(params){//add params
- if(Potree.measureTimings) performance.mark("render-start");
- if(this.outlinePass.selectedObjects.length){
- this.composer.render(this.inputHandler.interactiveScenes.concat(this.scene.scene).concat(viewer.scene.scenePointCloud), this.mainViewport.camera, this.renderDefault.bind(this));
- }else {
- this.renderDefault(params);
- }
- if(Potree.measureTimings){
- performance.mark("render-end");
- performance.measure("render", "render-start", "render-end");
- }
- }
-
- startScreenshot(info={}, width=800, height=400, compressRatio){//add
- let deferred = info.deferred || $.Deferred();
- let viewerMaster = info.map ? this.mapViewer : this; //截图主体
- let useMap = info.type == 'measure' || info.map;
-
-
- if(this.images360.flying){//如果在飞,飞完再截图
- info.deferred = deferred;
- let f = ()=>{
- this.startScreenshot(info, width, height, compressRatio);
- this.images360.removeEventListener('cameraMoveDone', f);
- };
- this.images360.addEventListener('cameraMoveDone', f); //once
- return deferred.promise()
- }
-
- var sid = Date.now();
- //抗锯齿待加 1 post处理 2截图大张再抗锯齿缩小
-
- console.log('startScreenshot: '+sid);
-
- let updateCamera = ()=>{
- this.viewports.forEach(e=>{
- e.view.applyToCamera(e.camera); //因为fly时只更新了view所以要强制更新下camera
-
- this.dispatchEvent({ //update map and sprite
- type: "camera_changed",
- camera: e.camera,
- viewport : e,
- changeInfo:{positionChanged:true,changed:true}
- });
- });
- };
-
- let screenshot = ()=>{
-
- useMap && (viewer.mapViewer.needRender = true);
-
-
-
- let { dataUrl } = viewerMaster.makeScreenshot( new Vector2$1(width,height), null, compressRatio );
-
-
-
- if(!Potree.settings.isOfficial){
- Common.downloadFile(dataUrl, 'screenshot.jpg');
- }
-
-
-
- var finish = ()=>{
-
- oldStates.viewports.forEach(old=>{//恢复相机
- var viewport = [mapViewport, mainViewport].find(v=>v.name == old.name);
- viewport.left = old.left;
- viewport.width = old.width;
- viewport.view.copy(old.view);
- viewport.view.applyToCamera(viewport.camera);
-
- });
-
- viewer.updateScreenSize({forceUpdateSize:true});//更新像素
-
- /* oldStates.viewports.forEach(old=>{//恢复相机
- var viewport = [mapViewport, mainViewport].find(v=>v.name == old.name);
- this.dispatchEvent({ //update map
- type: "camera_changed",
- camera: viewport.camera,
- viewport : viewport
- })
- }) */
- updateCamera();
-
-
-
-
- deferred.resolve(dataUrl);
- console.log('screenshot done: '+sid);
- };
-
- {//恢复:
-
- if(info.type == 'measure'){
- this.scene.measurements.forEach(e=>this.updateVisible(e, 'screenshot',true));
- info.measurement.setSelected(false, 'screenshot');
- }
- this.images360.panos.forEach(pano=>{
- viewer.updateVisible(pano, 'screenshot', true);
- });
- viewer.updateVisible(this.reticule, 'screenshot', true);
- useMap && viewer.updateVisible(this.mapViewer.cursor, 'screenshot', true);
-
- if(oldStates.attachedToViewer != this.mapViewer.attachedToViewer){
- if(info.type == 'measure'){
- this.mapViewer.attachToMainViewer(false );
- }
- }
- mapViewport.camera.zoom = oldStates.mapZoom;
- mapViewport.camera.updateProjectionMatrix();
-
- if(Potree.settings.displayMode == 'showPanos') {
- viewer.images360.flyToPano({pano:oldStates.pano, duration:0, callback:()=>{
- finish();
- }});
- }else {
- finish();
- }
-
- }
-
-
- };// screenshot end
-
-
-
- let mapViewport = this.mapViewer.viewports[0];
- let mainViewport = this.mainViewport;
- let oldStates = {
- attachedToViewer : this.mapViewer.attachedToViewer,
- viewports : [mapViewport, mainViewport].map(e=>{
- return e.clone()
- }),
- mapZoom: mapViewport.camera.zoom,
- pano: Potree.settings.displayMode == 'showPanos' ? viewer.images360.currentPano : null,
- };
-
-
-
- if(info.hideMarkers){
- this.images360.panos.forEach(pano=>{//令漫游点不可见
- viewer.updateVisible(pano, 'screenshot', false);
- });
- }
- viewer.updateVisible(this.reticule, 'screenshot', false);//令reticule不可见
-
- viewer.updateVisible(this.mapViewer.cursor, 'screenshot', false);//令mapCursor不可见
-
-
-
- if(info.type == 'measure'){//要截图双屏
- this.scene.measurements.forEach(e=>this.updateVisible(e,'screenshot',e == info.measurement) );
- info.measurement.setSelected(true, 'screenshot');
-
-
- //因为分屏后位置才最终确定,才能确定是否显示出floorplan所以先分屏
- if(Potree.settings.floorplanEnable){
- this.mapViewer.attachToMainViewer(true, 'measure', 0.5 );
- }
- viewer.updateScreenSize({forceUpdateSize:true, width, height}); //更新viewports相机透视 使focusOnObject在此窗口大小下
-
- let begin = ()=>{
- useMap = this.mapViewer.attachedToViewer;
- updateCamera();
- let waitTime = Potree.settings.displayMode == 'showPointCloud' ? 500 : 0; //等点云加载 网速差的话还是加载稀疏 是否要用最高质量点云
- if(useMap){
- let waitMap = ()=>{
- //console.log('waitMap: '+sid)
- this.mapViewer.waitLoadDone(screenshot.bind(this));//等待地图所有加载完
- };
- setTimeout(waitMap.bind(this), waitTime);
- }else {
- setTimeout(screenshot.bind(this), waitTime);
-
- }
- };
-
- let {promise}= this.focusOnObject(info.measurement, 'measure', 0, {basePanoSize:1024} );//注意:不同角度截图 得到三维的会不一样,因为focusOnObject是根据方向的
- promise.done(()=>{
- //console.log('promise.done')
- //根据当前位置更新floorplan显示
- //console.log('view Pos ', this.mainViewport.view.position.toArray())
- this.updateDatasetAt(true);
- this.modules.SiteModel.updateEntityAt(true);
- //this.updateFpVisiDatasets()
-
- //console.log('currentFloor', this.modules.SiteModel.currentFloor, 'currentDataset', this.atDatasets )
-
- let floorplanShowed = this.mapViewer.mapLayer.maps.some(e => e.name.includes('floorplan') && e.objectGroup.visible);
- if(!floorplanShowed && this.mapViewer.attachedToViewer){
- this.mapViewer.attachToMainViewer(false); //取消分屏
- viewer.updateScreenSize({forceUpdateSize:true, width, height}); //更新viewports相机透视
- let {promise} = this.focusOnObject(info.measurement, 'measure', 0, {basePanoSize:1024} );//因画面比例更改,重新focus
- promise.done(()=>{
- begin();
- });
- }else {
- begin();
- }
-
- });
-
- }else {
- screenshot();
- }
-
- /*
- 测量线的截图因为要调用分屏的,会改变画面
- 但是普通截图的话,不会改变画面
- */
-
- return deferred.promise()
-
-
- }
-
- focusOnObject(object, type, duration, o={} ) {
- //飞向热点、测量线等 。
-
- console.log('focusOnObject: '+object.name, type);
-
- let deferred = o.deferred || $.Deferred();
- let target = new Vector3, //相机focus的位置
- position = new Vector3, //相机最终位置
- dis; //相机距离目标
- duration = duration == void 0 ? 1000 : duration;
- let camera = viewer.scene.getActiveCamera();
- let cameraPos = camera.position.clone();
-
-
-
- let getPosWithFullBound = (points, boundingBox, target, cameraPos )=>{//使boundingBox差不多占满屏幕时的相机到target的距离
- // points 和 boundingBox 至少有一个
-
- var cameraTemp = camera.clone();
- cameraTemp.position.copy(cameraPos);
- cameraTemp.lookAt(target);
- cameraTemp.updateMatrix();
- cameraTemp.updateMatrixWorld();
- //对镜头的bound
- var inv = cameraTemp.matrixWorldInverse;
- var bound = new Box3();
- if(points){//使用points得到的bound更小 //如果points和boundingbox的差别较大,尤其使target和points中心不一致,那么points不一定会刚好在boundingbox内
- points.forEach(e=>{
- var p = e.clone().applyMatrix4(inv);
- bound.expandByPoint(p);
- });
- }else {
- bound = boundingBox.applyMatrix4(inv);
- }
- let boundSize = bound.getSize(new Vector3);
-
-
-
-
-
- if(!this.boundBox){//调试
- this.boundBox = new Mesh(new BoxGeometry(1,1,1,1));
- this.boundBox.material.wireframe = true;
- this.boundBox.up.set(0,0,1);
- this.boundBox.visible = false; //打开以检查box
- this.setObjectLayers(this.boundBox,'sceneObjects');
- this.scene.scene.add(this.boundBox);
- }
-
-
- this.boundBox.position.copy(target);
- this.boundBox.scale.copy(boundSize);
- this.boundBox.lookAt(cameraPos);
-
-
- {
- let scale = 1.1; //稍微放大一些,不然会靠到屏幕边缘
- boundSize.x *= scale;
- boundSize.y *= scale;
-
- }
-
- let aspect = boundSize.x / boundSize.y;
- if(camera.aspect > aspect){//视野更宽则用bound的纵向来决定
- dis = boundSize.y/2/ Math.tan(MathUtils.degToRad(camera.fov / 2)) + boundSize.z/2;
- }else {
- let hfov = cameraLight$1.getHFOVForCamera(camera, true);
- dis = boundSize.x/2 / Math.tan(hfov / 2) + boundSize.z/2;
- }
- dis = Math.max(0.1,dis);
-
- //三个顶点以上的由于measure的中心不等于bound的中心,所以点会超出bound外。 且由于视椎近大远小,即使是两个点的,bound居中后线看上去仍旧不居中.
-
- //获得相机最佳位置
- let dir = new Vector3().subVectors(cameraPos, target).normalize();
- position.copy(target).add(dir.multiplyScalar(dis));
- return position
- };
-
-
- if(this.images360.flying){
- let f = ()=>{
- this.focusOnObject(object, type, duration, $.extend(o,{deferred}));
- this.images360.removeEventListener('cameraMoveDone',f);
- };
- this.images360.addEventListener('cameraMoveDone',f);
- return {promise: deferred.promise() }
- }
- if (type == 'measure') {
- target.copy(object.getCenter());
-
-
-
-
- //试试改变位置,直视测量线。能避免倾斜角度造成的非常不居中、以及看不到面的情况
- if(object.facePlane/* && window.focusMeasureFaceToIt */){
- let normal;
- if(object.facePlane){
- normal = object.facePlane.normal.clone();
- }
- let angle = this.scene.view.direction.angleTo(normal);
- let minDiff = MathUtils.degToRad(60);
- //console.log('angle',angle)
- if(angle>minDiff && angle<Math.PI-minDiff){//当几乎正对时就不执行
- if(angle<Math.PI/2){ //在背面
- normal.negate();
- }
- let dir = new Vector3().subVectors(camera.position, target).normalize();
- let newDir = new Vector3().addVectors(dir,normal);//两个角度的中间
- cameraPos.copy(target.clone().add(newDir));
- }
- }else if(object.points.length == 2){ //线段
- let lineDir = new Vector3().subVectors(object.points[0],object.points[1]).normalize();
- let angle = this.scene.view.direction.angleTo(lineDir);
- let maxDiff = Math.PI*0.25;// 45度
- if(angle<maxDiff || angle>Math.PI-maxDiff){//当几乎正对时就不执行
- if(angle>Math.PI/2){ //令dir和lineDir成钝角
- lineDir.negate();
- }
- let dir = new Vector3().subVectors(camera.position, target).normalize();
- let mid = new Vector3().addVectors(lineDir, dir).normalize(); //中间法向量(如果刚好dir和lineDir反向,那得到的为零向量,就不移动了,但一般不会酱紫吧)
- let newDir = new Vector3().addVectors(dir, mid);
- cameraPos.copy(target.clone().add(newDir));
- }
- }else {
- console.error('measure 没有facePlane points点数还不为2?');
- }
-
- position = getPosWithFullBound(object.points, null, target, cameraPos );
-
-
-
-
- if(this.mapViewer/* .attachedToViewer */){
- //console.log('mapFocusOn: '+target.toArray())
- const minBound = new Vector2$1(4,4);//针对垂直线,在地图上只有一个点
- //原始的bound
- let boundOri = new Box3();
- object.points.forEach(e=>{
- boundOri.expandByPoint(e);
- });
- let boundSizeOri = boundOri.getSize(new Vector3);
-
-
- let boundSizeMap = boundSizeOri.clone().multiplyScalar(2);
- boundSizeMap.x = Math.max(minBound.x, boundSizeMap.x );
- boundSizeMap.y = Math.max(minBound.y, boundSizeMap.y );
- this.mapViewer.moveTo(target.clone(), boundSizeMap, duration);
- }
-
-
-
-
- if(Potree.settings.displayMode == 'showPointCloud'){ //点云
- let minDis = 0.3;
-
- if(o.checkIntersect){
- let checkIntersect = ( )=>{
- let intersect = this.inputHandler.ifBlockedByIntersect(position, null , true, target);// 不一定准确
- if(intersect){
- let blockCount = 0, unblockCount = 0, visi;
- for(let i=0;i<object.points.length;i++){ //如果顶点超过一半不可见,就要更改位置
- let p = object.points[i];
- let blocked = this.inputHandler.ifBlockedByIntersect(p, 0.3 , true, position, 4);
- if(blocked){
- blockCount ++;
- if(blockCount / object.points.length >= 0.5){
- visi = false;
- break
- }
- }else {
- unblockCount ++;
- if(unblockCount / object.points.length > 0.5){
- visi = true;
- break
- }
- }
- }
-
- if(visi == void 0){
- visi = unblockCount / object.points.length > 0.5;
- }
- let shrink = ()=>{
- let dir = new Vector3().subVectors(position, target).normalize().multiplyScalar(intersect.distance);
- position.copy(target).add(dir);
- console.log('checkIntersect newPos', position.clone() );
-
- };
- if(!visi){//更改位置距离target如果小于最小距离,需要反向。 否则直接缩短距离。
- if(intersect.distance < minDis ){
- console.log('检测到intersect 反向', intersect.distance );
- let position1 = position.clone();
- let dir = new Vector3().subVectors(position, target);
- position.copy(target).sub(dir);
- let intersect2 = this.inputHandler.ifBlockedByIntersect(position, null , true, target);// 不一定准确
- if(intersect2){
- if(intersect2.distance < intersect.distance ){
- position.copy(position1);//恢复
- }
- shrink();
- }
- }else {
- shrink();
- }
- }
- }
- };
-
- checkIntersect();
- }
- }else if(Potree.settings.displayMode == 'showPanos'){//全景 (比较难校准)
- let pano = viewer.images360.fitPanoTowardPoint({
- /*point : target, //不使用目标点来判断是因为缺少measure角度的信息。比如虽然可以靠近线的中心,但是线朝向屏幕,那几乎就是一个点了。
- //bestDistance : dis * 0.5, //乘以小数是为了尽量靠近
- boundSphere: boundOri.getBoundingSphere(new THREE.Sphere), */
- target,
- point : position,
- bestDistance : 0 ,
- checkIntersect: o.checkIntersect
- });
- if(pano){
- viewer.images360.flyToPano({pano, target, duration, deferred, dontMoveMap:true , basePanoSize:o.basePanoSize});//dontMoveMap不要移动map,否则flytopano会自动在map中focus到漫游点的位置,而非测量线了
- }
- if(viewer.images360.currentPano == pano){
- let dis1 = viewer.images360.currentPano.position.distanceTo(target);
- let dis2 = position.distanceTo(target);
- console.log('dis1 / dis2',dis1 / dis2, 'dis1-dis2', dis1-dis2);
- return {mag: (dis1 / dis2 > 1.5 && dis1-dis2>10)? 'tooFar' : 'posNoChange', promise : deferred.promise() }
-
- }else {
- return {promise : deferred.promise()}
- }
-
- //出现过到达位置后测量线标签闪烁的情况
- }
-
- } else if (type == 'tag' || type == 'point') {
- //dimension = 1
- target.copy(object.position);
- let bestDistance = o.distance || 3;
-
- if(!o.dontMoveMap){
- //console.log('mapFocusOn: '+target.toArray())
- this.mapViewer.moveTo(target.clone(), null, duration);
- }
-
- if(Potree.settings.displayMode == 'showPointCloud'){
- dis = bestDistance;
- let dir = o.direction ? o.direction.clone().negate() : this.mainViewport.view.direction.negate();//new THREE.Vector3().subVectors(camera.position, target).normalize()
-
- position.copy(target).add(dir.multiplyScalar(dis));
-
- /* if(o.checkIntersect){//识别被点云遮住的话
- let ifShelter
-
- while(1){
- ifShelter = this.inputHandler.ifBlockedByIntersect(target, o.checkMargin, true, position)
- if(ifShelter){
- if(dis > 0.5){
- dis --
- dir.dot(ifShelter.normal)>0 ? dir.copy(ifShelter.normal).negate() : dir.copy(ifShelter.normal);
- position.copy(target).add(dir.multiplyScalar(dis))
- }
- }
- }
- } */
-
- }else if(Potree.settings.displayMode == 'showPanos'){
- let pano = viewer.images360.fitPanoTowardPoint({
- point : target,
- bestDistance //越近越好,但不要太近,bestDistance左右差不多
- });
- pano && viewer.images360.flyToPano({pano, target, duration, deferred, dontMoveMap:true , basePanoSize:o.basePanoSize });
- return {promise:deferred.promise() }
- }
- }else if(object.boundingBox && type == 'boundingBox'){//使屏幕刚好看全boundingBox
- target = object.boundingBox.getCenter(new Vector3);
- position = getPosWithFullBound(object.points, object.boundingBox, target, cameraPos );
- if(Potree.settings.displayMode == 'showPanos'){//全景 (比较难校准)
- let pano = viewer.images360.fitPanoTowardPoint({
- point : position,
- bestDistance : 0 ,
- });
-
- pano && viewer.images360.flyToPano({pano, target, duration, deferred, dontMoveMap:true , basePanoSize:o.basePanoSize});//dontMoveMap不要移动map,否则flytopano会自动在map中focus到漫游点的位置,而非测量线了
-
- if(!pano){
- console.error('no pano');
- }
- return {promise:deferred.promise() }
- //出现过到达位置后测量线标签闪烁的情况
- }
-
- }
-
-
-
- /*} else if(dimension == 2){//线
-
- }else if(dimension == 3){//面
-
- }else{//立体
-
- } */
- viewer.scene.view.setView({position, target, duration, callback:()=>{
- //console.log('focusOnObjectSuccess: '+object.name, type)
- deferred.resolve();
- }
- });
-
-
-
- return {promise:deferred.promise()}
- }
-
- flyToDataset(o={}){
- var pointcloud;
- if(o instanceof Object3D) pointcloud = o;
- else if(o.pointcloud) pointcloud = o.pointcloud;
- else pointcloud = this.scene.pointclouds.find(p => p.dataset_id == o.id);
-
- let duration = o.duration == void 0 ? 1000 : o.duration;
- var center = pointcloud.bound.getCenter(new Vector3);
- let position;
- let getPano = ()=>{//获取离中心最近的pano
- let request = [];
- let rank = [
- Images360.scoreFunctions.distanceSquared({position: center})
- ];
- let r = Common.sortByScore(pointcloud.panos, request, rank);
- if(r && r.length){
- return r[0].item
- }
- };
-
- if(Potree.settings.displayMode == 'showPanos'){
- let pano = getPano();
-
- if(pano){
- if(pano == this.images360.currentPano) return 'posNoChange'
- this.images360.flyToPano({
- pano
- });
- }else return false
- }else {
- let target;
- position = center;
- if(pointcloud.panosBound){
-
- let panosCenter = pointcloud.panosBound.center; //pano集中的地方,也就是真正有点云的地方
- position = panosCenter.clone();
- /* let ratio = 0.2
- position.z = center.z * ratio + panosCenter.z * (1-ratio) //因为panos一般比较低,为了不让相机朝下时看不到点云,加一丢丢中心高度
- */
- let pano = getPano();
- if(pano){
- target = pano.position; //针对像隧道一样的场景, 中心点还是panosCenter都在没有点云的地方,所以还是看向其中一个漫游点好。
- position.z = target.z; //水平, 避免朝上或朝下
- }
-
- }
-
-
- if(this.modules.Clip.editing){
- position.z = center.z; //剪裁时在中心高度,因为以点云为重点
- this.modules.Clip.bus.dispatchEvent({type:'flyToPos', position });
- }else {
- if(math.closeTo(position, this.images360.position)) return 'posNoChange'
-
- viewer.scene.view.setView({position, target, duration });
-
- o.dontMoveMap || viewer.mapViewer.moveTo(position.clone(), null , duration);
- }
- }
-
- return true
-
-
- }
- resolveTimings(timestamp){
- if(Potree.measureTimings){
- if(!this.toggle){
- this.toggle = timestamp;
- }
- let duration = timestamp - this.toggle;
- if(duration > 1000.0){
-
- let measures = performance.getEntriesByType("measure");
-
- let names = new Set();
- for(let measure of measures){
- names.add(measure.name);
- }
-
- let groups = new Map();
- for(let name of names){
- groups.set(name, {
- measures: [],
- sum: 0,
- n: 0,
- min: Infinity,
- max: -Infinity
- });
- }
-
- for(let measure of measures){
- let group = groups.get(measure.name);
- group.measures.push(measure);
- group.sum += measure.duration;
- group.n++;
- group.min = Math.min(group.min, measure.duration);
- group.max = Math.max(group.max, measure.duration);
- }
- let glQueries = Potree.resolveQueries(this.renderer.getContext());
- for(let [key, value] of glQueries){
- let group = {
- measures: value.map(v => {return {duration: v}}),
- sum: value.reduce( (a, i) => a + i, 0),
- n: value.length,
- min: Math.min(...value),
- max: Math.max(...value)
- };
- let groupname = `[tq] ${key}`;
- groups.set(groupname, group);
- names.add(groupname);
- }
-
- for(let [name, group] of groups){
- group.mean = group.sum / group.n;
- group.measures.sort( (a, b) => a.duration - b.duration );
-
- if(group.n === 1){
- group.median = group.measures[0].duration;
- }else if(group.n > 1){
- group.median = group.measures[parseInt(group.n / 2)].duration;
- }
-
- }
-
- let cn = Array.from(names).reduce( (a, i) => Math.max(a, i.length), 0) + 5;
- let cmin = 10;
- let cmed = 10;
- let cmax = 10;
- let csam = 6;
-
- let message = ` ${"NAME".padEnd(cn)} |`
- + ` ${"MIN".padStart(cmin)} |`
- + ` ${"MEDIAN".padStart(cmed)} |`
- + ` ${"MAX".padStart(cmax)} |`
- + ` ${"SAMPLES".padStart(csam)} \n`;
- message += ` ${"-".repeat(message.length) }\n`;
-
- names = Array.from(names).sort();
- for(let name of names){
- let group = groups.get(name);
- let min = group.min.toFixed(3);
- let median = group.median.toFixed(3);
- let max = group.max.toFixed(3);
- let n = group.n;
-
- message += ` ${name.padEnd(cn)} |`
- + ` ${min.padStart(cmin)} |`
- + ` ${median.padStart(cmed)} |`
- + ` ${max.padStart(cmax)} |`
- + ` ${n.toString().padStart(csam)}\n`;
- }
- message += `\n`;
- console.log(message);
-
- performance.clearMarks();
- performance.clearMeasures();
- this.toggle = timestamp;
- }
- }
- }
- loop(timestamp){
- if(this.stats){
- this.stats.begin();
- }
- if(Potree.measureTimings){
- performance.mark("loop-start");
- }
- this.update(this.clock.getDelta(), timestamp);
- this.magnifier.render();
- this.render();
-
-
-
- // let vrActive = viewer.renderer.xr.isPresenting;
- // if(vrActive){
- // this.update(this.clock.getDelta(), timestamp);
- // this.render();
- // }else{
- // this.update(this.clock.getDelta(), timestamp);
- // this.render();
- // }
- if(Potree.measureTimings){
- performance.mark("loop-end");
- performance.measure("loop", "loop-start", "loop-end");
- }
-
- this.resolveTimings(timestamp);
- Potree.framenumber++;
- if(this.stats){
- this.stats.end();
- }
- }
- postError(content, params = {}){
- let message = this.postMessage(content, params);
- message.element.addClass("potree_message_error");
- return message;
- }
- postMessage(content, params = {}){
- let message = new Message(content);
- let animationDuration = 100;
- message.element.css("display", "none");
- message.elClose.click( () => {
- message.element.slideToggle(animationDuration);
- let index = this.messages.indexOf(message);
- if(index >= 0){
- this.messages.splice(index, 1);
- }
- });
- this.elMessages.prepend(message.element);
- message.element.slideToggle(animationDuration);
- this.messages.push(message);
- if(params.duration !== undefined){
- let fadeDuration = 500;
- let slideOutDuration = 200;
- setTimeout(() => {
- message.element.animate({
- opacity: 0
- }, fadeDuration);
- message.element.slideToggle(slideOutDuration);
- }, params.duration);
- }
- return message;
- }
-
-
-
-
-
- getBoundingBox (pointclouds) {
- //可以直接返回viewer.bound
- if(!this.bound){
- this.updateModelBound();
- }
- return this.bound.boundingBox.clone()//this.scene.getBoundingBox(pointclouds);
- };
- updateModelBound(){
- this.bound = Utils.computePointcloudsBound(this.scene.pointclouds);
- if(Potree.settings.boundAddObjs){//加上obj的bound
- this.objs.children.forEach(e=>{
- this.bound.boundingBox.union(e.boundingBox.clone().applyMatrix4(e.matrixWorld));
- });
- this.bound.boundingBox.getSize(this.bound.boundSize);
- this.bound.boundingBox.getCenter(this.bound.center);
- }
-
- viewer.farWhenShowPano = this.bound.boundSize.length() * 10;//全景漫游时要能看到整个skybox 原本*2的但对于距离特远的数据集需要乘大一些否则会黑面
-
-
- let boundPlane = new Box3();
- boundPlane.expandByPoint(this.bound.boundingBox.min.clone());//最低高度为bound的最低
- boundPlane.expandByPoint(this.bound.boundingBox.max.clone().setZ(this.bound.center.z));//最高高度为bound的中心高度
- FirstPersonControls.boundPlane = boundPlane;
- FirstPersonControls.standardSpeed = MathUtils.clamp( Math.sqrt(this.bound.boundSize.length() )/ 100 , 0.02,0.5); //在这个boundPlane中的速度
-
- viewer.scene.pointclouds.forEach(e=>{//海拔范围
- e.material.heightMin = this.bound.boundingBox.min.z;
- e.material.heightMax = this.bound.boundingBox.max.z;
- });
- this.dispatchEvent({type:'updateModelBound'});
- }
-
- waitForLoad(object, isLoadedCallback){//等待加载时显示loading。主要是贴图
- this.waitQueue.push({
- object,
- isLoadedCallback,
- });
- 1 === this.waitQueue.length && this.dispatchEvent({type:"loading", show:true});
- }
- ifAllLoaded(object){
- if(this.waitQueue.length>0){
- this.waitQueue = this.waitQueue.filter(function(e) {
- return !e.isLoadedCallback()
- });
- }
-
- 0 === this.waitQueue.length && this.dispatchEvent({type:"loading", show:false});
- }
-
-
- setView(o={}){
- let callback = ()=>{
- if(o.displayMode){
- Potree.settings.displayMode = o.displayMode;
- }
- o.callback && o.callback();
- };
-
- if(o.pano != void 0){//pano 权重高于 position
- this.images360.flyToPano(o);
- }else {
- this.scene.view.setView($.extend({},o, {callback}));
- }
- }
-
-
-
- //设置点云为标准模式
- setPointStandardMat(state, pointDensity, fitPointsize){
- console.log('setPointStandardMat',state);
- if(state){
- if(this.pointStatesBefore){
- return console.error('已设置过pointStatesBefore!')
- }
- this.pointStatesBefore = {
- opacity : new Map(),
- size: new Map(),
- density:Potree.settings.pointDensity,
- useEDL:this.getEDLEnabled(),
- shape: viewer.scene.pointclouds[0].material.shape
- };
-
- viewer.scene.pointclouds.forEach(e=>{
- this.pointStatesBefore.opacity.set(e, e.temp.pointOpacity); //因为更改pointDensity时会自动变opacity,所以这项最先获取
- this.pointStatesBefore.colorType = e.material.activeAttributeName;
- fitPointsize && this.pointStatesBefore.size.set(e,e.temp.pointSize); //这项不一定有用,因为会被后期覆盖
- });
-
- if(pointDensity)Potree.settings.pointDensity = pointDensity; //万一之后切换到全景模式怎么办
- if(fitPointsize)Potree.settings.sizeFitToLevel = true;
-
- viewer.scene.pointclouds.forEach(e=>{
- e.material.activeAttributeName = 'rgba';
- e.material.shape = Potree.PointShape['SQUARE'];
- fitPointsize && e.changePointSize(Potree.config.material.realPointSize, true);
- e.changePointOpacity(1);
- });
-
- viewer.setEDLEnabled(false);
-
- }else {
- if(!this.pointStatesBefore){
- return console.error('未设置过pointStatesBefore!')
- }
- Potree.settings.sizeFitToLevel = false;
- if(pointDensity)Potree.settings.pointDensity = this.pointStatesBefore.pointDensity;
-
-
- viewer.scene.pointclouds.forEach(e=>{
- e.material.activeAttributeName = this.pointStatesBefore.colorType;
- e.changePointOpacity(this.pointStatesBefore.opacity.get(e));
- e.material.shape = this.pointStatesBefore.shape;
-
- let size = this.pointStatesBefore.size.get(e);
- if(size) e.changePointSize(size);
-
-
- });
- viewer.setEDLEnabled(this.pointStatesBefore.useEDL);
-
- this.pointStatesBefore = null;
-
-
-
-
- }
-
-
- }
-
-
-
- //调试时显示transformControl来调节object
- transformObject(object){
- if(!object.boundingBox){
- object.boundingBox = new Box3(); //任意大小 只是为了显示黄色外框
- //??? computeBoundingBox
- }
- if(!viewer.inputHandler.selection.includes(object)){
- viewer.inputHandler.toggleSelection(object);
- }
- }
-
- pointInWhichPointcloud(pos){//选择最接近中心的那个 使用boundSphere
- let result = Common.sortByScore(this.scene.pointclouds,[],[
- (pointcloud)=>{
- var size = pointcloud.pcoGeometry.tightBoundingBox.getSize(new Vector3);
- var center = pointcloud.bound.getCenter(new Vector3);
- var length = size.length() / 2;
- var dis = pos.distanceTo(center);
- return length / dis //到数据集中心的距离占数据集大小越小越好
- }
- ]);
- //若要求更准确的话,可以使用ifContainsPoint判断一下是否在bound中
- let r = result && result[0];
- return r.score > 1 ? result[0].item : null
- }
-
- /* addObjectTest1(){//加水管
-
- if(Potree.settings.number == 't-8KbK1JjubE'){
-
- let boundingBox = new THREE.Box3()
- boundingBox.min.set(-1,-1,-1); boundingBox.max.set(1,1,1)
-
-
- let radius = 0.08;
- let radialSegments = 5
- let radSegments = Math.PI*2 / radialSegments
- var circlePts = [];//横截面
- for(let i=0;i<radialSegments;i++){
- let angle = radSegments * i;
- circlePts.push(new THREE.Vector2(radius * Math.cos(angle), radius * Math.sin(angle) ))
- }
- var count = 0
- var addMesh = (color, path, height)=>{//height:在path之上的高度,负数代表在path之下
- var name = 'cylinder'+count
- var mat = new THREE.MeshStandardMaterial({color, depthTest:false, roughness:0.4,metalness:0.5})
- let linePath = path.map(e=>new THREE.Vector3().copy(e).setZ(e.z+height))
- let geo = MeshDraw.getExtrudeGeo( circlePts, null,{ extrudePath:linePath, tension:0.2} )
- var mesh = new THREE.Mesh(geo,mat);
- mesh.name = name
- window[name] = mesh
-
- mesh.boundingBox = boundingBox
- mesh.matrixAutoUpdate = false
- mesh.matrix.copy(viewer.scene.pointclouds[0].transformMatrix)
- mesh.matrixWorldNeedsUpdate = true
-
- this.scene.scene.add(mesh);
-
- count ++
- }
-
-
- let linePath, height
-
- //地上管子 黄色
- linePath = [{"x":-109.83,"y":-68.33,"z":-7.52},{"x":-95.17,"y":-59.3,"z":-7.38}, {"x":-38.75,"y":-24.01,"z":-6.01},{"x":0.5,"y":0.19,"z":-3.89},{"x":39.29,"y":24.41,"z":-1.31}
- ,{"x":43.58,"y":27.7,"z":-0.97},{"x":40.22,"y":35.37,"z":-0.67}// 拐弯向右
- , {"x":39.18,"y":36.71,"z":0.35},{"x":38.69,"y":36.04,"z":18.04} // 拐弯向上
- ]
- height = radius + 0.05;
- addMesh('#b86', linePath, height)
-
-
-
- //地下管子 藍色
- linePath = [{"x":-108.24,"y":-70.61,"z":-7.52}, {"x":-57.8,"y":-39.31,"z":-6.72},{"x":-18.8,"y":-15.35,"z":-5.01},{"x":55.87,"y":31.67,"z":-0.04},{"x":110.53,"y":66.48,"z":5.14}
- ]
- height = -0.5;
- addMesh('#48a', linePath, height)
-
-
-
- }
-
-
- }
- */
- /* createRoomEv(){
-
- const environment = new RoomEnvironment();
- const pmremGenerator = new THREE.PMREMGenerator( this.renderer );
- }
- */
- loadModel(fileInfo, done, onProgress_){
- let boundingBox = new Box3();
- if(!Potree.settings.boundAddObjs){
- boundingBox.min.set(-0.5,-0.5,-0.5); boundingBox.max.set(0.5,0.5,0.5);
- }
- let fileType = fileInfo.objurl ? 'obj' : 'glb';
- let loadDone = (object)=>{
- //object.scale.set(1,1,1);//先获取原始的大小时的boundingBox
- object.updateMatrixWorld();
-
-
- object.traverse( ( child )=>{
- if ( child instanceof Mesh ) {
- if(Potree.settings.boundAddObjs){
- child.geometry.computeBoundingBox();
- //console.log(child.matrixWorld.clone())
- boundingBox.union(child.geometry.boundingBox.clone().applyMatrix4(child.matrixWorld)); //但感觉如果最外层object大小不为1,要还原下scale再乘
- }//获取在scale为1时,表现出的大小
- //Common.makeTexDontResize(child.material.map)
- //console.log(child.name, 'roughness',child.material.roughness,'metalness',child.material.metalness)
- child.material.roughness = 0.6;
- child.material.metalness = 0.3;
-
- /* child.depthMat = child.material.clone()
- child.standardMat = child.material
- child.depthMat.onBeforeCompile = function ( shader ) {
- console.log('vertexShader',shader.vertexShader)
- console.log('fragmentShader',shader.fragmentShader)
- shader.fragmentShader = `
- ${shader.fragmentShader.replace(
- 'gl_FragColor = vec4( outgoingLight, diffuseColor.a );',
- 'diffuseColor.a = log(vViewPosition.z); gl_FragColor = vec4( outgoingLight, diffuseColor.a );'
- )}
- `;
- console.log('fragmentShader1',shader.fragmentShader)
- }; */
-
- //暂时用这种材质:
- if(fileInfo.unlit){
- let material = new MeshBasicMaterial({map:child.material.map});
- child.material = material;
- }
-
- }
- } );
-
- object.name = fileInfo.name != void 0 ? fileInfo.name : 'obj';
- this.objs.add(object);
- object.boundingBox = boundingBox;
-
- if(fileInfo.transform.rotation){
- object.rotation.fromArray(fileInfo.transform.rotation);
- }
- if(fileInfo.transform.position){
- object.position.fromArray(fileInfo.transform.position);
- }
- if(fileInfo.transform.scale){
- object.position.fromArray(fileInfo.transform.scale);
- }
-
- if(fileInfo.moveWithPointcloud){
- object.updateMatrix();
- object.matrixAutoUpdate = false;
- object.matrix.premultiply(viewer.scene.pointclouds[0].transformMatrix); //默认跟随第一个数据集
- object.matrixWorldNeedsUpdate = true;
- }
- done && done(object);
-
-
- };
-
-
- let onProgress = function ( xhr ) {
- if ( xhr.lengthComputable ) {
- let percentComplete = xhr.loaded / xhr.total * 100;
- console.log( Math.round(percentComplete, 2) + '% downloaded' );
- onProgress_ && onProgress_(percentComplete);
- }
- };
-
- if(fileType == 'obj'){
- /* manager.onProgress = function ( item, loaded, total ) {
- console.log( item, loaded, total );
- }; */
-
- let onError = function ( xhr ) {};
-
- loaders.mtlLoader.load( fileInfo.mtlurl , (materials)=>{
- materials.preload();
-
- loaders.objLoader.setMaterials( materials ).load(fileInfo.objurl, (object)=>{
- loadDone(object);
- });
- } , onProgress, /*onError */ );
-
- }else if(fileType == 'glb'){
- loaders.glbLoader.load(fileInfo.glburl, ( gltf )=>{ //.setPath( Potree.resourcePath + '/models/glb/' );
-
- console.log('loadGLTF', gltf);
- loadDone(gltf.scene);
- }, onProgress);
-
- }
-
-
-
-
-
- /* viewer.onGUILoaded(() => {
- // Add entries to object list in sidebar
- let tree = $(`#jstree_scene`);
- let parentNode = "other";
- let bunnyID = tree.jstree('create_node', parentNode, {
- text: "Bunny Textured",
- icon: `${Potree.resourcePath}/icons/triangle.svg`,
- data: object
- },
- "last", false, false);
- tree.jstree(object.visible ? "check_node" : "uncheck_node", bunnyID);
- //tree.jstree("open_node", parentNode);
- }); */
-
-
-
- }
-
- removeObj(object){
- this.objs.remove(object);
- if(Potree.settings.boundAddObjs){
- this.updateModelBound();
- }
- }
-
-
-
- loadGLTF(name='87b3a367bc3e4273832cb4fa398782e5.glb'){
-
- const loader = new GLTFLoader(undefined, this.renderer).setPath( Potree.resourcePath + '/models/glb/' );
-
- /*
- coffeemat.glb
- ModernJPHouseSofa44216499.glb
- ModernJPHouseSofa44105209.glb
- 87ecd10fb0374ea6b3e0bf24c6459e3c.glb
- 87b3a367bc3e4273832cb4fa398782e5.glb
- */
-
- loader.load(name, ( gltf )=>{
- console.log('loadGLTF', gltf);
- this.objs.add(gltf.scene);
- });
-
- }
-
-
- addFire(){
-
- if(Potree.settings.number == 't-CwfhfqJ'){
- let position = Potree.Utils.datasetPosTransform({
- pointcloud:viewer.scene.pointclouds[0],
- position: new Vector3(4.4318,-0.580291847759, -0.78),
- fromDataset:true
- });
-
- viewer.modules.ParticleEditor.addParticle( {
- type:'fire',
- positions:[position],
- radius:0.42,
- height:10,
- });
-
- viewer.modules.ParticleEditor.addParticle( {
- type:'smoke',
- positions: [ new Vector3().addVectors(position,new Vector3(0,0,0.3))],
- positionStyle : 'sphere' ,
- positionRadius : 0.3,
- sizeTween: [[0, 0.3, 0.9, 1], [0.05, 0.1, 1, 0.8]],
- opacityBase : 0.2,
- opacityTween :[ [0, 0.3, 0.7, 0.95, 1], [0, 0.2, 1 , 0.1, 0] ],
- velocityBase : new Vector3( 0, 0, 1),
- velocitySpread : new Vector3( 0.2, 0.2, -0.3),
- accelerationBase : 0.2,
- accelerationSpread : 0.7,
- radius:0,
- //particlesPerSecond : 30,
- particleDeathAge : 3.0,
- });
-
- viewer.modules.ParticleEditor.addParticle( {
- type:'explode',
- name:'fire splash',
- position: new Vector3().addVectors(position,new Vector3(0,0,0.3)),
- size: 0.1,
- sizeRange: 0.3,
- sizeTween:[[0, 0.05, 0.3, 0.45], [0, 0.02, 0.1, 0.05] ],
- opacityTween: [[0, 0.05, 0.3, 0.45], [1, 1, 0.5, 0]] ,
- speed : 1, //sphere
- speedRange : 4,
- radius: 0.1,
- acceleration : 0.3,
- accelerationRange : 1,
- particleSpaceTime:0,
- strength:4,
- });
- }
- }
-
-
-
- addVideo11(){
- if(Potree.settings.number != 'SS-fckI7CClKC')return
-
-
-
- var video = $(`<video controls="controls" loop autoplay x5-playsinline="" webkit-playsinline="true" playsinline="true" controlslist="nodownload"></video>`)[0];
- video.setAttribute("crossOrigin", 'Anonymous');
- //video.src = Potree.resourcePath+'/video/SS-fckI7CClKC/19.mp4'
-
- var map = new VideoTexture(video);
- var plane = this.videoPlane = new Mesh(new PlaneGeometry(1, 1, 1, 1), new MeshBasicMaterial({
- color:"#ffffff",
- transparent: !0,
- depthTest:false,
- opacity:0.7,
- side:2,
- map
- }));
- plane.visible = false;
-
- plane.geometry.computeBoundingBox();
- plane.boundingBox = plane.geometry.boundingBox.clone();//.applyMatrix4()
- plane.boundingBox.max.z = 1;
- plane.boundingBox.max.y = -0.4;
- plane.boundingBox.max.x = 1;
-
- /* plane.position.copy(this.images360.panos[19].position);
- plane.lookAt(plane.position.clone().setX(0))
-
-
-
-
- 9:
- viewer.videoPlane.rotation.set(-1.432978005954197, 1.2296264545169697, 3.0098547630197667)
- viewer.videoPlane.position.set( 6.532456676287381, -9.806373049095631, -0.024205281024294284)
- //viewer.transformObject(viewer.videoPlane)
-
- //19:
- viewer.videoPlane.rotation.set( 1.627167773445286, -1.172425902600188, 0.04682299709711613)
- viewer.videoPlane.position.set( -9.558613948539932,-1.042301166581578, 0.08159683876743667) */
-
-
-
- video.addEventListener('loadeddata', function(e) {
- video.play();
- //plane.scale.set(video.videoWidth/1000,video.videoHeight/1000,1) // 1080 * 1920
- console.log('video loadeddata');
- });
-
- plane.scale.set(1080/1000,1920/1000,1); // 1080 * 1920
-
-
-
- var startPlay = ()=>{
- video.play();
- //video.pause()
- //video.currentTime = 0.1;
- this.removeEventListener('global_mousedown', startPlay);
- };
-
- this.addEventListener('global_mousedown', startPlay);
-
-
- var videoInfo = {
- 9:{
- rotation:[-1.432978005954197, 1.2296264545169697, 3.0098547630197667],
- position:[6.532456676287381, -9.806373049095631, -0.024205281024294284]
- },
- 19:{
- rotation:[1.627167773445286, -1.172425902600188, 0.04682299709711613],
- position:[-9.558613948539932,-1.042301166581578, 0.08159683876743667]
- },
-
- };
-
- /* this.images360.addEventListener('cameraMoveDone',(e)=>{
- let info = videoInfo[this.images360.currentPano.id]
- if(info ){
- plane.visible = true;
- plane.material.opacity = 1;
- plane.position.fromArray(info.position)
- plane.rotation.fromArray(info.rotation)
- }
-
-
- }) */
-
-
- this.images360.addEventListener('flyToPano' ,(e)=>{//飞之前
- if(Potree.settings.displayMode != 'showPanos') return
- let info = videoInfo[e.toPano.pano.id];
- if(info ){ //出现
- setTimeout(()=>{
- plane.visible = true;
- plane.position.fromArray(info.position);
- plane.rotation.fromArray(info.rotation);
-
- video.src = Potree.resourcePath+`/video/${Potree.settings.number}/${e.toPano.pano.id}.mp4`;
- video.play();
- video.currentTime = 0;
- Potree.settings.zoom.enabled = false;
-
- transitions.start(lerp.property(plane.material, "opacity", 1/* , (e)=>{console.log('fadeIn',e)} */) , e.toPano.duration*0.4 , ()=>{
-
- }, 0, easing['easeInOutQuad']);
- }, e.toPano.duration*0.6); //时间上不能和消失的重叠 延迟
-
-
- }
-
- //消失
- transitions.start(lerp.property(plane.material, "opacity", 0, /* (e)=>{console.log('fadeOut',e)} */) , e.toPano.duration*0.4, ()=>{
- if(!info){
- plane.visible = false;
- video.pause();
- Potree.settings.zoom.enabled = true;
- }
- }, 0, easing['easeInOutQuad']);
-
-
- });
-
-
-
- this.images360.addEventListener('endChangeMode',(e)=>{ //暂时不处理初始加载时就在有视频的点位上的情况
- if(e.mode == 'showPanos'){
- let info = videoInfo[this.images360.currentPano.id];
- if(info ){ //出现
- plane.visible = true;
- plane.position.fromArray(info.position);
- plane.rotation.fromArray(info.rotation);
- plane.material.opacity = 0;
-
- video.src = Potree.resourcePath+`/video/${Potree.settings.number}/${this.images360.currentPano.id}.mp4`;
- video.play();
- video.currentTime = 0;
- Potree.settings.zoom.enabled = false;
-
- transitions.start(lerp.property(plane.material, "opacity", 1, (e)=>{console.log('fadeIn',e);}) , 300 , ()=>{
-
- }, 0, easing['easeInOutQuad']);
-
- }
- }else {
- plane.visible = false;
- Potree.settings.zoom.enabled = true;
- }
-
- });
-
- this.scene.scene.add(plane);
- }
- ////////////////////////
-
- addVideo(){
- if(Potree.settings.number != 'SS-t-P6zBR73Gke')return
- var geo = new PlaneGeometry(1, 1, 1, 1);
-
- var videoInfo = this.videoInfo = [
- /* {
- id: 45,
- url: 'https://laser-oss.4dkankan.com/testdata/SS-t-P6zBR73Gke/temp/poi/2022/05/09/c02a2c1e-8420-4f34-b951-5f7a07abe932.mp4',
- rotation:[-1.629007730553656, 0.042029565584517974, -3.1345506775116627],
- position:[ 9.467649296794061, -0.7596961214872837, -0.12477576310191862],
- scale:[4.52209111454416,3.3888400031207984,1],
-
- }, */
- {
- id: '40-2',
- url: 'https://laser-oss.4dkankan.com/testdata/SS-t-P6zBR73Gke/temp/poi/2022/05/10/0aabafee-36b8-455d-9c11-0780bf694786.mp4',
- rotation:[-1.494468618954883, -1.4987317433158989, -3.061254983446741],
- position:[ 19.801820617361624, 2.884673619844108, -0.03362305858221648],
- scale:[3.5741423153151763, 2.8738725275578703, 1],
- },
-
- {
- id: 40,
- /* rotation:[-1.534692822378723, 0.01083403560862361, 3.141535283661569],
- position:[17.2934294239949861, 2.413510747928117, -0.008057029580231356], */
- url: 'https://laser-oss.4dkankan.com/testdata/SS-t-P6zBR73Gke/temp/poi/2022/05/09/7896d6ef-a2d6-4fd7-949c-768782a5b484.mp4',
-
- rotation:[-1.5487684197910518, 0.021848470169552752, -3.1387534893955236],
- position:[17.277316608096, 2.0840432922115846, -0.0931149415437065],
- scale:[2.0821757723834047, 0.6129478480765236, 1],
- visibles: [40]
- },
-
- ];
- let add = (info)=>{
- var video = $(`<video controls="controls" loop autoplay x5-playsinline="" webkit-playsinline="true" playsinline="true" controlslist="nodownload"></video>`)[0];
- video.setAttribute("crossOrigin", 'Anonymous');
- video.src = info.url || Potree.resourcePath+`/video/${Potree.settings.number}/${info.id}.mp4`;
-
- var map = new VideoTexture(video);
- var plane = this.videoPlane = new Mesh(geo, new MeshBasicMaterial({
- color:"#ffffff",
- transparent: !0,
- depthTest:false,
- opacity:0 ,
- //side:2,
- map
- }));
- plane.position.fromArray(info.position);
- plane.rotation.fromArray(info.rotation);
- info.scale && plane.scale.fromArray(info.scale);
- this.scene.scene.add(plane);
- info.plane = plane;
- plane.boundingBox = new Box3(new Vector3(0,-0.5,0),new Vector3(1,-0.4,0.2));
- video.addEventListener('loadeddata', function(e) {
- video.play();
- if(!info.visibles/* ||!viewer.images360.currentPano || info.visibles.includes(viewer.images360.currentPano.id) */){
- plane.material.opacity = 1;
- }
-
- info.scale || plane.scale.set(video.videoWidth/1000,video.videoHeight/1000,1); // 1080 * 1920
- console.log('video loadeddata', info.id);
- });
-
-
-
- if(info.visibles){
- this.images360.addEventListener('flyToPano' ,(e)=>{//飞之前
- if(info.visibles.includes(e.toPano.pano.id)){ //出现
- setTimeout(()=>{
- plane.visible = true;
- video.currentTime = 0;
- video.play();
- if(video.paused){
- var startPlay = ()=>{
- plane.visible && video.play();
- this.removeEventListener('global_mousedown', startPlay);
- };
- this.addEventListener('global_mousedown', startPlay);
- }
- Potree.settings.zoom.enabled = false;
-
- transitions.start(lerp.property(plane.material, "opacity", 1 ) , e.toPano.duration*0.4 , ()=>{
-
- }, 0, easing['easeInOutQuad']);
- }, e.toPano.duration*0.6); //时间上不能和消失的重叠 延迟
-
- }else {
- //消失
- transitions.start(lerp.property(plane.material, "opacity", 0, ) , e.toPano.duration*0.4, ()=>{
- if(!info){
- plane.visible = false;
- video.pause();
- Potree.settings.zoom.enabled = true;
- }
- }, 0, easing['easeInOutQuad']);
- }
-
- });
- }
-
-
-
- var startPlay = ()=>{
- video.play();
- //video.pause()
- //video.currentTime = 0.1;
- this.removeEventListener('global_mousedown', startPlay);
- };
-
- this.addEventListener('global_mousedown', startPlay);
- Potree.settings.isTest && plane.addEventListener('select',(e)=>{console.log(e);});
- };
-
- videoInfo.forEach(info=>{
- add(info);
- });
-
- /* var video = $(`<video controls="controls" loop autoplay x5-playsinline="" webkit-playsinline="true" playsinline="true" controlslist="nodownload"></video>`)[0]
- video.setAttribute("crossOrigin", 'Anonymous')
- video.src = Potree.resourcePath+'/video/SS-t-P6zBR73Gke/40.mp4'
-
- var map = new THREE.VideoTexture(video);
- var plane = this.videoPlane = new THREE.Mesh(geo, new THREE.MeshBasicMaterial({
- color:"#ffffff",
- transparent: !0,
- depthTest:false,
- opacity:0.7,
- side:2,
- map
- }))
- //plane.visible = false
- this.scene.scene.add(plane)
- plane.geometry.computeBoundingBox();
- plane.boundingBox = plane.geometry.boundingBox.clone()//.applyMatrix4()
- plane.boundingBox.max.z = 0.3
- plane.boundingBox.max.y = -0.4
- plane.boundingBox.max.x = 1
-
-
-
- plane.position.copy(this.images360.panos[40].position);
- plane.lookAt(plane.position.clone().setX(0))
-
-
- plane.rotation.set(-1.534692822378723, 0.01083403560862361, 3.141535283661569)
- plane.position.set( 17.2934294239949861, 2.413510747928117, -0.008057029580231356)
-
-
-
- //viewer.transformObject(viewer.videoPlane)
-
- //19:
-
-
-
-
- video.addEventListener('loadeddata', function(e) {
- video.play()
- plane.scale.set(video.videoWidth/1000,video.videoHeight/1000,1) // 1080 * 1920
- console.log('video loadeddata')
- })
-
- //plane.scale.set(1080/1000,1920/1000,1) // 1080 * 1920
-
-
-
- var startPlay = ()=>{
- video.play()
- //video.pause()
- //video.currentTime = 0.1;
- this.removeEventListener('global_mousedown', startPlay)
- }
-
- this.addEventListener('global_mousedown', startPlay)
-
- */
-
-
-
- /* this.images360.addEventListener('flyToPano' ,(e)=>{//飞之前
- if(Potree.settings.displayMode != 'showPanos') return
- let info = videoInfo[e.toPano.pano.id]
- if(info ){ //出现
- setTimeout(()=>{
- plane.visible = true;
- plane.position.fromArray(info.position)
- plane.rotation.fromArray(info.rotation)
-
- video.src = Potree.resourcePath+`/video/${Potree.settings.number}/${e.toPano.pano.id}.mp4`
- video.play();
- video.currentTime = 0
- Potree.settings.zoom.enabled = false
-
- transitions.start(lerp.property(plane.material, "opacity", 1 ) , e.toPano.duration*0.4 , ()=>{
-
- }, 0, easing['easeInOutQuad'])
- }, e.toPano.duration*0.6) //时间上不能和消失的重叠 延迟
-
-
- }
-
- //消失
- transitions.start(lerp.property(plane.material, "opacity", 0, ) , e.toPano.duration*0.4, ()=>{
- if(!info){
- plane.visible = false
- video.pause()
- Potree.settings.zoom.enabled = true
- }
- }, 0, easing['easeInOutQuad'])
-
-
- })
-
-
-
- this.images360.addEventListener('endChangeMode',(e)=>{ //暂时不处理初始加载时就在有视频的点位上的情况
- if(e.mode == 'showPanos'){
- let info = videoInfo[this.images360.currentPano.id]
- if(info ){ //出现
- plane.visible = true;
- plane.position.fromArray(info.position)
- plane.rotation.fromArray(info.rotation)
- plane.material.opacity = 0
-
- video.src = Potree.resourcePath+`/video/${Potree.settings.number}/${this.images360.currentPano.id}.mp4`
- video.play();
- video.currentTime = 0
- Potree.settings.zoom.enabled = false
-
- transitions.start(lerp.property(plane.material, "opacity", 1, (e)=>{console.log('fadeIn',e)}) , 300 , ()=>{
-
- }, 0, easing['easeInOutQuad'])
-
- }
- }else{
- plane.visible = false;
- Potree.settings.zoom.enabled = true
- }
-
- })
-
- */
- }
-
-
-
-
- };
- //------ CLIP 默认clipTask都是clipInside ----------------------
- /*
- 并集相当于加法,交集相当于加法。 所有结果都能展开成多个乘积相加。
- 假设有4个clipBoxes,ABCD, 如果是 A*B + C*D ,那么这是最终结果。 如果是 (A+B)*(C+D) = A*C+A*D+B*C+B*D
- */
-
- let Clips = {
- boxes : [],
- unionGroups : [], //二维数组。最外层要求并集,里层要求交集(如果只有一个元素就是本身)。总结起来就是要求一堆交集的并集
- shaderParams:{},
- needsUpdate : true,
- addClip(box, clipMethod){
- //不允许重复
- if(this.boxes.includes(box)){
- return console.warn('addClip重复添加了box',box)
- }
-
- boxes.push(box);
-
- if(clipMethod == 'any'){//并
- this.unionGroups.push([box]);
- }else if(clipMethod == 'all'){//交
- this.unionGroups.forEach(mixGroup=>mixGroup.push(box));
- }
- this.needsUpdate = true;
- },
-
- removeClip(box){
- if(!this.boxes.includes(box)){
- return console.warn('removeClip没有找到该box',box)
- }
- var newGroups = [];
-
- this.unionGroups.forEach(mixGroup=>{
- if(mixGroup.length == 1 && mixGroup[0] == box)return;//直接删除
- newGroups.push(mixGroup.filter(e=>e!=box));
- });
-
- this.unionGroups = newGroups;
- this.needsUpdate = true;
- }
- ,
-
- clearClip(){
- this.boxes = [];
- this.unionGroups = [];
- this.needsUpdate = true;
- }
-
- ,
-
- updateShaderParams(){//没写完 - - 见 pointcloud clip.vs
- /*
- uniform mat4 clipBoxes[num_clipboxes];
- uniform int clipBoxGroupCount;
- uniform int mixClipIndices[clipboxGroupItemCount]; //把所有的要求都直接放到数组内
- */
-
- //这里需要转为Float32Array..? 参考material.setClipBoxes
- let everyClipGroupCount = this.unionGroups.map(e=>e.length);
-
- let mixClipIndices = [];
- this.unionGroups.forEach(e=>{
- mixClipIndices.push(...e);
- });
-
- this.shaderParams = {
- num_clipboxes : this.boxes.length,
- clipBoxGroupCount : this.unionGroups.length,
- everyClipGroupCount,
- clipBoxIndexCount: mixClipIndices.length,
- mixClipIndices
- };
-
- }
- ,
- getShaderParams(){//每次要传递参数到shader中,执行这个就好
- if(this.needsUpdate){
- this.updateShaderParams();
- }
- return this.shaderParams
- }
-
-
-
-
- };
-
- /* setTimeout(()=>{
- if( Potree.settings.number == 't-YLZ5XAALl7' || 't-e2Kb2iU' ){
-
- let transform = {
- 't-YLZ5XAALl7' : {
- rotation : [0, 0, 0.002326740152215126],
- position : [0.421017820930033, -0.22730084679456727, 0.0068952417582]
- },
- 't-e2Kb2iU' : {
- rotation : [0.06595546058095993, -0.026986798620029413, 0.8429662590573239],
- position : [ -502.6216232179794, 1051.5690392495885, 15.48490744053752]
- },
- }
-
- var path = `${Potree.resourcePath}/models/${Potree.settings.number}/`
-
- viewer.loadObj({
- objurl: path+'pipe.obj',
- mtlurl: path+'pipe.mtl',
- transform : transform[Potree.settings.number]
- })
- }
- },1000)
- //if(!Potree.settings.isOfficial){
- if(number == 't-e2Kb2iU') {
- setTimeout(//暂时延迟,等focus第一个点之后
- ()=>{
- viewer.loadProject(Potree.scriptPath + "/data/"+ number +"/potree.json5", ()=>{
- viewer.scene.cameraAnimations[0].play()
- })
- },
- 3000)
- }
- */
- OrthographicCamera.prototype.zoomTo = function( node, factor = 1){
- if ( !node.geometry && !node.boundingBox) {
- return;
- }
- // TODO
- //let minWS = new THREE.Vector4(node.boundingBox.min.x, node.boundingBox.min.y, node.boundingBox.min.z, 1);
- //let minVS = minWS.applyMatrix4(this.matrixWorldInverse);
- //let right = node.boundingBox.max.x;
- //let bottom = node.boundingBox.min.y;
- //let top = node.boundingBox.max.y;
- this.updateProjectionMatrix();
- };
- PerspectiveCamera.prototype.zoomTo = function (node, factor) {
- if (!node.geometry && !node.boundingSphere && !node.boundingBox) {
- return;
- }
- if (node.geometry && node.geometry.boundingSphere === null) {
- node.geometry.computeBoundingSphere();
- }
- node.updateMatrixWorld();
- let bs;
- if (node.boundingSphere) {
- bs = node.boundingSphere;
- } else if (node.geometry && node.geometry.boundingSphere) {
- bs = node.geometry.boundingSphere;
- } else {
- bs = node.boundingBox.getBoundingSphere(new Sphere());
- }
- let _factor = factor || 1;
- bs = bs.clone().applyMatrix4(node.matrixWorld);
- let radius = bs.radius;
- let fovr = this.fov * Math.PI / 180;
- if (this.aspect < 1) {
- fovr = fovr * this.aspect;
- }
- let distanceFactor = Math.abs(radius / Math.sin(fovr / 2)) * _factor;
- let offset = this.getWorldDirection(new Vector3()).multiplyScalar(-distanceFactor);
- this.position.copy(bs.center.clone().add(offset));
- };
- Ray.prototype.distanceToPlaneWithNegative = function (plane) {
- let denominator = plane.normal.dot(this.direction);
- if (denominator === 0) {
- // line is coplanar, return origin
- if (plane.distanceToPoint(this.origin) === 0) {
- return 0;
- }
- // Null is preferable to undefined since undefined means.... it is undefined
- return null;
- }
- let t = -(this.origin.dot(plane.normal) + plane.constant) / denominator;
- return t;
- };
- const workerPool = new WorkerPool();
- const version = {
- major: 1,
- minor: 8,
- suffix: '.0'
- };
- let lru = new LRU();
-
- //console.log('Potree ' + version.major + '.' + version.minor + version.suffix);
- let pointBudget = 1 * 1000 * 1000;
- let framenumber = 0;
- let numNodesLoading = 0;
- let maxNodesLoading = 4;
- const debug = {};
- exports.scriptPath = "";
- if (document.currentScript && document.currentScript.src) {
- exports.scriptPath = new URL(document.currentScript.src + '/..').href;
- if (exports.scriptPath.slice(-1) === '/') {
- exports.scriptPath = exports.scriptPath.slice(0, -1);
- }
- } else if(({ url: (typeof document === 'undefined' ? new (require('u' + 'rl').URL)('file:' + __filename).href : (document.currentScript && document.currentScript.src || new URL('potree.js', document.baseURI).href)) })){
- exports.scriptPath = new URL((typeof document === 'undefined' ? new (require('u' + 'rl').URL)('file:' + __filename).href : (document.currentScript && document.currentScript.src || new URL('potree.js', document.baseURI).href)) + "/..").href;
- if (exports.scriptPath.slice(-1) === '/') {
- exports.scriptPath = exports.scriptPath.slice(0, -1);
- }
- }else {
- console.error('Potree was unable to find its script path using document.currentScript. Is Potree included with a script tag? Does your browser support this function?');
- }
- let resourcePath = exports.scriptPath + '/resources';
- //add:
- async function loadFile(path, callback){
- if(Potree.fileServer){
- Potree.fileServer.get(path).then(data=>{
- callback && callback(data);
- });
- }else {
- let response = await fetch(path);
- let text = await response.text();
- var data = JSON.parse(text);
- callback && callback(data);
- return data
- }
-
- //查询: http://192.168.0.26:8080/doc.html#/default/filter-%E6%BC%AB%E6%B8%B8%E7%82%B9/filterUsingGET
- }
- async function loadDatasets(callback){//之后直接把path写进来
- var path;
- if(Potree.fileServer){
- path = `/laser/dataset/${Potree.settings.number}/getDataSet`;
- }else {
- //path = `${Potree.settings.urls.prefix2}/indoor/${Potree.settings.number}/api/datasets`
- //现在只能加载得了本地的了
- path = `${Potree.scriptPath}/data/${Potree.settings.number}/getDataSet.json`;
-
- }
- return loadFile(path, callback)
-
- }
- //目前上传平面图后如果不点击保存按钮,数据还是旧的不生效
- async function loadMapEntity(datasetId, force){
- if(!Potree.settings.floorplanEnable && !force && Potree.fileServer )return /* 等待平面图类型定义好会加载 */
-
- let loaded = 0;
-
- let needLoads = datasetId == 'all' ? viewer.scene.pointclouds.map(e=>e.dataset_id) : [datasetId];
-
-
- let callback = (dataset_id, floorplanType, data )=>{
- //要防止旧的比新的先获取到导致覆盖新的,因为两种type随时可能切换
- if(floorplanType != Potree.settings.floorplanType[dataset_id]) return //如果请求的floorplanType不是当前最新的floorplanType就返回
-
- var map = viewer.mapViewer.mapLayer.maps.find(e => e.name == 'floorplan_'+ dataset_id);
- if(map){
- viewer.mapViewer.mapLayer.removeMap(map);
- }
-
- var mapNew = viewer.mapViewer.mapLayer.addMapEntity(data.data || data, dataset_id);
- if(map){
- mapNew.visibleReasons = map.visibleReasons;
- mapNew.unvisibleReasons = map.unvisibleReasons;
- }
- loaded ++;
- };
-
- needLoads.forEach(dataset_id=>{
- let floorplanType = Potree.settings.floorplanType[dataset_id];
- if(!floorplanType)return
- var path;
- if(Potree.fileServer){
- path = `/laser/tiledMap/${Potree.settings.number}/tiledMap/${floorplanType}/${dataset_id}`;
- }else {
- path = `${Potree.settings.urls.prefix2}/indoor/${Potree.settings.number}/api/tiled_maps`;
-
- }
- Potree.settings.floorplanRequests[dataset_id] = true; //开始加载了
- return loadFile(path, callback.bind(this, dataset_id, floorplanType) )
- });
-
-
-
- }
-
- async function loadPanos(datasetId, callback){
- var path;
- let query = `?datasetId=${datasetId}`; //`?lat=${center.lat}&lon=${center.lon}&radius=200000`
- if(Potree.fileServer){
- path = `/laser/filter/${Potree.settings.number}/query` + query;
- }else {
- //path = `${Potree.settings.urls.prefix2}/indoor/${Potree.settings.number}/api/images/filter` + query
- path = `${Potree.scriptPath}/data/${Potree.settings.number}/panos-${datasetId}.json`;
-
- }
- return loadFile(path, callback)
-
- }
- async function loadPanosInfo(callback){
- var path;
- if(Potree.fileServer){
-
- }else {
- path = `${Potree.scriptPath}/data/panoEdit/vision_edit.txt`;
-
- }
- return loadFile(path, callback)
-
- }
- //site_model
- /* {
- "area": 2503.30551910935,
- "attributes": {},
- "center": [
- 113.59568277455075,
- 22.366566635195288,
- 12.78751625
- ],
- "children": [],
- "geometry_hash": 1891071345,
- "id": 10,
- "name": "港湾一号",
- "parentId": null,
- "polygon": {
- "coordinates": [
- [
- [
- 113.59590810534583,
- 22.36679132753878
- ],
- [
- 113.59590810534583,
- 22.366807172528629
- ],
- [
- 113.59545610274934,
- 22.366807172528629
- ],
- [
- 113.59545610274934,
- 22.36679132753878
- ]
- ]
- ],
- "type": "Polygon"
- },
- "type": "BUILDING",
- "volume": null,
- "z_max": null,
- "z_min": null
- }
- */
- function Log(value, color, fontSize){
- color = color || '#13f';
- fontSize = fontSize || 14;
- console.warn(`%c${value}`, `color:${color};font-size:${fontSize}px`);
- }
-
- function loadPointCloud$1(path, name, sceneCode, timeStamp, callback){
- let loaded = function(e){
- e.pointcloud.name = name;
- e.pointcloud.sceneCode = sceneCode; //对应4dkk的场景码
-
- callback(e);
- };
- let promise = new Promise( resolve => {
- // load pointcloud
- if (!path){
- // TODO: callback? comment? Hello? Bueller? Anyone?
- } else if (path.indexOf('ept.json') > 0) {
- EptLoader.load(path, function(geometry) {
- if (!geometry) {
- console.error(new Error(`failed to load point cloud from URL: ${path}`));
- }
- else {
- let pointcloud = new PointCloudOctree(geometry);
- //loaded(pointcloud);
- resolve({type: 'pointcloud_loaded', pointcloud: pointcloud});
- }
- });
- } else if (path.indexOf('cloud.js') > 0) {
- POCLoader.load(path, timeStamp, function (geometry) {
- if (!geometry) {
- //callback({type: 'loading_failed'});
- console.error(new Error(`failed to load point cloud from URL: ${path}`));
- } else {
- let pointcloud = new PointCloudOctree(geometry);
- // loaded(pointcloud);
- resolve({type: 'pointcloud_loaded', pointcloud: pointcloud});
- }
- });
- }/* else if (path.indexOf('metadata.json') > 0) { //部分浏览器(如uc)不支持NodeLoader中的1n的大数据写法
- Potree.OctreeLoader.load(path).then(e => {
- let geometry = e.geometry;
- if(!geometry){
- console.error(new Error(`failed to load point cloud from URL: ${path}`));
- }else{
- let pointcloud = new PointCloudOctree(geometry);
- let aPosition = pointcloud.getAttribute("position");
- let material = pointcloud.material;
- material.elevationRange = [
- aPosition.range[0][2],
- aPosition.range[1][2],
- ];
- // loaded(pointcloud);
- resolve({type: 'pointcloud_loaded', pointcloud: pointcloud});
- }
- });
- OctreeLoader.load(path, function (geometry) {
- if (!geometry) {
- //callback({type: 'loading_failed'});
- console.error(new Error(`failed to load point cloud from URL: ${path}`));
- } else {
- let pointcloud = new PointCloudOctree(geometry);
- // loaded(pointcloud);
- resolve({type: 'pointcloud_loaded', pointcloud: pointcloud});
- }
- });
- } */else if (path.indexOf('.vpc') > 0) {
- PointCloudArena4DGeometry.load(path, function (geometry) {
- if (!geometry) {
- //callback({type: 'loading_failed'});
- console.error(new Error(`failed to load point cloud from URL: ${path}`));
- } else {
- let pointcloud = new PointCloudArena4D(geometry);
- // loaded(pointcloud);
- resolve({type: 'pointcloud_loaded', pointcloud: pointcloud});
- }
- });
- } else {
- //callback({'type': 'loading_failed'});
- console.error(new Error(`failed to load point cloud from URL: ${path}`));
- }
- });
- if(callback){
- promise.then(pointcloud => {
- loaded(pointcloud);
- });
- }else {
- return promise;
- }
- };
- // add selectgroup
- (function($){
- $.fn.extend({
- selectgroup: function(args = {}){
- let elGroup = $(this);
- let rootID = elGroup.prop("id");
- let groupID = `${rootID}`;
- let groupTitle = (args.title !== undefined) ? args.title : "";
- let elButtons = [];
- elGroup.find("option").each((index, value) => {
- let buttonID = $(value).prop("id");
- let label = $(value).html();
- let optionValue = $(value).prop("value");
- let elButton = $(`
- <span style="flex-grow: 1; display: inherit">
- <label for="${buttonID}" class="ui-button" style="width: 100%; padding: .4em .1em">${label}</label>
- <input type="radio" name="${groupID}" id="${buttonID}" value="${optionValue}" style="display: none"/>
- </span>
- `);
- let elLabel = elButton.find("label");
- let elInput = elButton.find("input");
- elInput.change( () => {
- elGroup.find("label").removeClass("ui-state-active");
- elGroup.find("label").addClass("ui-state-default");
- if(elInput.is(":checked")){
- elLabel.addClass("ui-state-active");
- }else {
- //elLabel.addClass("ui-state-default");
- }
- });
- elButtons.push(elButton);
- });
- let elFieldset = $(`
- <fieldset style="border: none; margin: 0px; padding: 0px">
- <legend>${groupTitle}</legend>
- <span style="display: flex">
- </span>
- </fieldset>
- `);
- let elButtonContainer = elFieldset.find("span");
- for(let elButton of elButtons){
- elButtonContainer.append(elButton);
- }
- elButtonContainer.find("label").each( (index, value) => {
- $(value).css("margin", "0px");
- $(value).css("border-radius", "0px");
- $(value).css("border", "1px solid black");
- $(value).css("border-left", "none");
- });
- elButtonContainer.find("label:first").each( (index, value) => {
- $(value).css("border-radius", "4px 0px 0px 4px");
- });
- elButtonContainer.find("label:last").each( (index, value) => {
- $(value).css("border-radius", "0px 4px 4px 0px");
- $(value).css("border-left", "none");
- });
- elGroup.empty();
- elGroup.append(elFieldset);
- }
- });
- })(jQuery);
- exports.Action = Action;
- exports.Alignment = Alignment;
- exports.AnimationPath = AnimationPath;
- exports.Annotation = Annotation;
- exports.Box3Helper = Box3Helper$1;
- exports.BoxVolume = BoxVolume;
- exports.Buttons = Buttons;
- exports.CameraAnimation = CameraAnimation;
- exports.CameraMode = CameraMode;
- exports.ClassificationScheme = ClassificationScheme;
- exports.ClipMethod = ClipMethod;
- exports.ClipTask = ClipTask;
- exports.ClipVolume = ClipVolume;
- exports.ClippingTool = ClippingTool;
- exports.Compass = Compass;
- exports.DeviceOrientationControls = DeviceOrientationControls;
- exports.DownloadStatus = DownloadStatus;
- exports.EarthControls = EarthControls;
- exports.ElevationGradientRepeat = ElevationGradientRepeat;
- exports.Enum = Enum;
- exports.EnumItem = EnumItem;
- exports.EptBinaryLoader = EptBinaryLoader;
- exports.EptKey = EptKey;
- exports.EptLaszipLoader = EptLaszipLoader;
- exports.EptLazBatcher = EptLazBatcher;
- exports.EptLoader = EptLoader;
- exports.EptZstandardLoader = EptZstandardLoader;
- exports.EventDispatcher = EventDispatcher$1;
- exports.EyeDomeLightingMaterial = EyeDomeLightingMaterial;
- exports.Features = Features;
- exports.FirstPersonControls = FirstPersonControls;
- exports.GLCubeFaces = GLCubeFaces$1;
- exports.GeoPackageLoader = GeoPackageLoader;
- exports.Geopackage = Geopackage$1;
- exports.Gradients = Gradients;
- exports.HierarchicalSlider = HierarchicalSlider;
- exports.Images360 = Images360;
- exports.KeyCodes = KeyCodes;
- exports.LRU = LRU;
- exports.LRUItem = LRUItem;
- exports.LengthUnits = LengthUnits;
- exports.Log = Log;
- exports.Measure = Measure;
- exports.MeasuringTool = MeasuringTool;
- exports.Message = Message;
- exports.ModelManagerEvents = ModelManagerEvents;
- exports.NormalizationEDLMaterial = NormalizationEDLMaterial;
- exports.NormalizationMaterial = NormalizationMaterial;
- exports.OrbitControls = OrbitControls;
- exports.OrientedImage = OrientedImage;
- exports.OrientedImageLoader = OrientedImageLoader;
- exports.OrientedImages = OrientedImages;
- exports.POCLoader = POCLoader;
- exports.PanoRendererEvents = PanoRendererEvents;
- exports.PanoSizeClass = PanoSizeClass;
- exports.PanoramaEvents = PanoramaEvents;
- exports.PathAnimation = PathAnimation;
- exports.PointAttribute = PointAttribute;
- exports.PointAttributeTypes = PointAttributeTypes;
- exports.PointAttributes = PointAttributes;
- exports.PointCloudEptGeometry = PointCloudEptGeometry;
- exports.PointCloudEptGeometryNode = PointCloudEptGeometryNode;
- exports.PointCloudMaterial = PointCloudMaterial$1;
- exports.PointCloudOctree = PointCloudOctree;
- exports.PointCloudOctreeGeometry = PointCloudOctreeGeometry;
- exports.PointCloudOctreeGeometryNode = PointCloudOctreeGeometryNode;
- exports.PointCloudOctreeNode = PointCloudOctreeNode;
- exports.PointCloudSM = PointCloudSM;
- exports.PointCloudTree = PointCloudTree;
- exports.PointCloudTreeNode = PointCloudTreeNode;
- exports.PointShape = PointShape;
- exports.PointSizeType = PointSizeType;
- exports.Points = Points$1;
- exports.PolygonClipVolume = PolygonClipVolume;
- exports.Profile = Profile;
- exports.ProfileData = ProfileData;
- exports.ProfileRequest = ProfileRequest;
- exports.ProfileTool = ProfileTool;
- exports.Renderer = Renderer;
- exports.Scene = Scene$1;
- exports.SceneRendererEvents = SceneRendererEvents;
- exports.ScreenBoxSelectTool = ScreenBoxSelectTool;
- exports.ShapefileLoader = ShapefileLoader;
- exports.SphereVolume = SphereVolume;
- exports.SpotLightHelper = SpotLightHelper$1;
- exports.TileDownloaderEvents = TileDownloaderEvents;
- exports.TransformationTool = TransformationTool;
- exports.TreeType = TreeType;
- exports.Utils = Utils;
- exports.VRControls = VRControls;
- exports.Vectors = Vectors;
- exports.Vectors2 = Vectors2;
- exports.Version = Version;
- exports.Viewer = Viewer;
- exports.Volume = Volume;
- exports.VolumeTool = VolumeTool;
- exports.WorkerPool = WorkerPool;
- exports.XHRFactory = XHRFactory;
- exports.config = config$1;
- exports.debug = debug;
- exports.framenumber = framenumber;
- exports.loadDatasets = loadDatasets;
- exports.loadFile = loadFile;
- exports.loadMapEntity = loadMapEntity;
- exports.loadPanos = loadPanos;
- exports.loadPanosInfo = loadPanosInfo;
- exports.loadPointCloud = loadPointCloud$1;
- exports.loadProject = loadProject;
- exports.lru = lru;
- exports.maxNodesLoading = maxNodesLoading;
- exports.mergeEditStart = mergeEditStart;
- exports.numNodesLoading = numNodesLoading;
- exports.panoEditStart = panoEditStart;
- exports.pointBudget = pointBudget;
- exports.resourcePath = resourcePath;
- exports.saveProject = saveProject;
- exports.settings = settings;
- exports.start = start;
- exports.updatePointClouds = updatePointClouds;
- exports.updateVisibility = updateVisibility;
- exports.updateVisibilityStructures = updateVisibilityStructures;
- exports.version = version;
- exports.workerPool = workerPool;
- Object.defineProperty(exports, '__esModule', { value: true });
- })));
- //# sourceMappingURL=potree.js.map
|