| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008100910101011101210131014101510161017101810191020102110221023102410251026102710281029103010311032103310341035103610371038103910401041104210431044104510461047104810491050105110521053105410551056105710581059106010611062106310641065106610671068106910701071107210731074107510761077107810791080108110821083108410851086108710881089109010911092109310941095109610971098109911001101110211031104110511061107110811091110111111121113111411151116111711181119112011211122112311241125112611271128112911301131113211331134113511361137113811391140114111421143114411451146114711481149115011511152115311541155115611571158115911601161116211631164116511661167116811691170117111721173117411751176117711781179118011811182118311841185118611871188118911901191119211931194119511961197119811991200120112021203120412051206120712081209121012111212121312141215121612171218121912201221122212231224122512261227122812291230123112321233123412351236123712381239124012411242124312441245124612471248124912501251125212531254125512561257125812591260126112621263126412651266126712681269127012711272127312741275127612771278127912801281128212831284128512861287128812891290129112921293129412951296129712981299130013011302130313041305130613071308130913101311131213131314131513161317131813191320132113221323132413251326132713281329133013311332133313341335133613371338133913401341134213431344134513461347134813491350135113521353135413551356135713581359136013611362136313641365136613671368136913701371137213731374137513761377137813791380138113821383138413851386138713881389139013911392139313941395139613971398139914001401140214031404140514061407140814091410141114121413141414151416141714181419142014211422142314241425142614271428142914301431143214331434143514361437143814391440144114421443144414451446144714481449145014511452145314541455145614571458145914601461146214631464146514661467146814691470147114721473147414751476147714781479148014811482148314841485148614871488148914901491149214931494149514961497149814991500150115021503150415051506150715081509151015111512151315141515151615171518151915201521152215231524152515261527152815291530153115321533153415351536153715381539154015411542154315441545154615471548154915501551155215531554155515561557155815591560156115621563156415651566156715681569157015711572157315741575157615771578157915801581158215831584158515861587158815891590159115921593159415951596159715981599160016011602160316041605160616071608160916101611161216131614161516161617161816191620162116221623162416251626162716281629163016311632163316341635163616371638163916401641164216431644164516461647164816491650165116521653165416551656165716581659166016611662166316641665166616671668166916701671167216731674167516761677167816791680168116821683168416851686168716881689169016911692169316941695169616971698169917001701170217031704170517061707170817091710171117121713171417151716171717181719172017211722172317241725172617271728172917301731173217331734173517361737173817391740174117421743174417451746174717481749175017511752175317541755175617571758175917601761176217631764176517661767176817691770177117721773177417751776177717781779178017811782178317841785178617871788178917901791179217931794179517961797179817991800180118021803180418051806180718081809181018111812181318141815181618171818181918201821182218231824182518261827182818291830183118321833183418351836183718381839184018411842184318441845184618471848184918501851185218531854185518561857185818591860186118621863186418651866186718681869187018711872187318741875187618771878187918801881188218831884188518861887188818891890189118921893189418951896189718981899190019011902190319041905190619071908190919101911191219131914191519161917191819191920192119221923192419251926192719281929193019311932193319341935193619371938193919401941194219431944194519461947194819491950195119521953195419551956195719581959196019611962196319641965196619671968196919701971197219731974197519761977197819791980198119821983198419851986198719881989199019911992199319941995199619971998199920002001200220032004200520062007200820092010201120122013201420152016201720182019202020212022202320242025202620272028202920302031203220332034203520362037203820392040204120422043204420452046204720482049205020512052205320542055205620572058205920602061206220632064206520662067206820692070207120722073207420752076207720782079208020812082208320842085208620872088208920902091209220932094209520962097209820992100210121022103210421052106210721082109211021112112211321142115211621172118211921202121212221232124212521262127212821292130213121322133213421352136213721382139214021412142214321442145214621472148214921502151215221532154215521562157215821592160216121622163216421652166216721682169217021712172217321742175217621772178217921802181218221832184218521862187218821892190219121922193219421952196219721982199220022012202220322042205220622072208220922102211221222132214221522162217221822192220222122222223222422252226222722282229223022312232223322342235223622372238223922402241224222432244224522462247224822492250225122522253225422552256225722582259226022612262226322642265226622672268226922702271227222732274227522762277227822792280228122822283228422852286228722882289229022912292229322942295229622972298229923002301230223032304230523062307230823092310231123122313231423152316231723182319232023212322232323242325232623272328232923302331233223332334233523362337233823392340234123422343234423452346234723482349235023512352235323542355235623572358235923602361236223632364236523662367236823692370237123722373237423752376237723782379238023812382238323842385238623872388238923902391239223932394239523962397239823992400240124022403240424052406240724082409241024112412241324142415241624172418241924202421242224232424242524262427242824292430243124322433243424352436243724382439244024412442244324442445244624472448244924502451245224532454245524562457245824592460246124622463246424652466246724682469247024712472247324742475247624772478247924802481248224832484248524862487248824892490249124922493249424952496249724982499250025012502250325042505250625072508250925102511251225132514251525162517251825192520252125222523252425252526252725282529253025312532253325342535253625372538253925402541254225432544254525462547254825492550255125522553255425552556255725582559256025612562256325642565256625672568256925702571257225732574257525762577257825792580258125822583258425852586258725882589259025912592259325942595259625972598259926002601260226032604260526062607260826092610261126122613261426152616261726182619262026212622262326242625262626272628262926302631263226332634263526362637263826392640264126422643264426452646264726482649265026512652265326542655265626572658265926602661266226632664266526662667266826692670267126722673267426752676267726782679268026812682268326842685268626872688268926902691269226932694269526962697269826992700270127022703270427052706270727082709271027112712271327142715271627172718271927202721272227232724272527262727272827292730273127322733273427352736273727382739274027412742274327442745274627472748274927502751275227532754275527562757275827592760276127622763276427652766276727682769277027712772277327742775277627772778277927802781278227832784278527862787278827892790279127922793279427952796279727982799280028012802280328042805280628072808280928102811281228132814281528162817281828192820282128222823282428252826282728282829283028312832283328342835283628372838283928402841284228432844284528462847284828492850285128522853285428552856285728582859286028612862286328642865286628672868286928702871287228732874287528762877287828792880288128822883288428852886288728882889289028912892289328942895289628972898289929002901290229032904290529062907290829092910291129122913291429152916291729182919292029212922292329242925292629272928292929302931293229332934293529362937293829392940294129422943294429452946294729482949295029512952295329542955295629572958295929602961296229632964296529662967296829692970297129722973297429752976297729782979298029812982298329842985298629872988298929902991299229932994299529962997299829993000300130023003300430053006300730083009301030113012301330143015301630173018301930203021302230233024302530263027302830293030303130323033303430353036303730383039304030413042304330443045304630473048304930503051305230533054305530563057305830593060306130623063306430653066306730683069307030713072307330743075307630773078307930803081308230833084308530863087308830893090309130923093309430953096309730983099310031013102310331043105310631073108310931103111311231133114311531163117311831193120312131223123312431253126312731283129313031313132313331343135313631373138313931403141314231433144314531463147314831493150315131523153315431553156315731583159316031613162316331643165316631673168316931703171317231733174317531763177317831793180318131823183318431853186318731883189319031913192319331943195319631973198319932003201320232033204320532063207320832093210321132123213321432153216321732183219322032213222322332243225322632273228322932303231323232333234323532363237323832393240324132423243324432453246324732483249325032513252325332543255325632573258325932603261326232633264326532663267326832693270327132723273327432753276327732783279328032813282328332843285328632873288328932903291329232933294329532963297329832993300330133023303330433053306330733083309331033113312331333143315331633173318331933203321332233233324332533263327332833293330333133323333333433353336333733383339334033413342334333443345334633473348334933503351335233533354335533563357335833593360336133623363336433653366336733683369337033713372337333743375337633773378337933803381338233833384338533863387338833893390339133923393339433953396339733983399340034013402340334043405340634073408340934103411341234133414341534163417341834193420342134223423342434253426342734283429343034313432343334343435343634373438343934403441344234433444344534463447344834493450345134523453345434553456345734583459346034613462346334643465346634673468346934703471347234733474347534763477347834793480348134823483348434853486348734883489349034913492349334943495349634973498349935003501350235033504350535063507350835093510351135123513351435153516351735183519352035213522352335243525352635273528352935303531353235333534353535363537353835393540354135423543354435453546354735483549355035513552355335543555355635573558355935603561356235633564356535663567356835693570357135723573357435753576357735783579358035813582358335843585358635873588358935903591359235933594359535963597359835993600360136023603360436053606360736083609361036113612361336143615361636173618361936203621362236233624362536263627362836293630363136323633363436353636363736383639364036413642364336443645364636473648364936503651365236533654365536563657365836593660366136623663366436653666366736683669367036713672367336743675367636773678367936803681368236833684368536863687368836893690369136923693369436953696369736983699370037013702370337043705370637073708370937103711371237133714371537163717371837193720372137223723372437253726372737283729373037313732373337343735373637373738373937403741374237433744374537463747374837493750375137523753375437553756375737583759376037613762376337643765376637673768376937703771377237733774377537763777377837793780378137823783378437853786378737883789379037913792379337943795379637973798379938003801380238033804380538063807380838093810381138123813381438153816381738183819382038213822382338243825382638273828382938303831383238333834383538363837383838393840384138423843384438453846384738483849385038513852385338543855385638573858385938603861386238633864386538663867386838693870387138723873387438753876387738783879388038813882388338843885388638873888388938903891389238933894389538963897389838993900390139023903390439053906390739083909391039113912391339143915391639173918391939203921392239233924392539263927392839293930393139323933393439353936393739383939394039413942394339443945394639473948394939503951395239533954395539563957395839593960396139623963396439653966396739683969397039713972397339743975397639773978397939803981398239833984398539863987398839893990399139923993399439953996399739983999400040014002400340044005400640074008400940104011401240134014401540164017401840194020402140224023402440254026402740284029403040314032403340344035403640374038403940404041404240434044404540464047404840494050405140524053405440554056405740584059406040614062406340644065406640674068406940704071407240734074407540764077407840794080408140824083408440854086408740884089409040914092409340944095409640974098409941004101410241034104410541064107410841094110411141124113411441154116411741184119412041214122412341244125412641274128412941304131413241334134413541364137413841394140414141424143414441454146414741484149415041514152415341544155415641574158415941604161416241634164416541664167416841694170417141724173417441754176417741784179418041814182418341844185418641874188418941904191419241934194419541964197419841994200420142024203420442054206420742084209421042114212421342144215421642174218421942204221422242234224422542264227422842294230423142324233423442354236423742384239424042414242424342444245424642474248424942504251425242534254425542564257425842594260426142624263426442654266426742684269427042714272427342744275427642774278427942804281428242834284428542864287428842894290429142924293429442954296429742984299430043014302430343044305430643074308430943104311431243134314431543164317431843194320432143224323432443254326432743284329433043314332433343344335433643374338433943404341434243434344434543464347434843494350435143524353435443554356435743584359436043614362436343644365436643674368436943704371437243734374437543764377437843794380438143824383438443854386438743884389439043914392439343944395439643974398439944004401440244034404440544064407440844094410441144124413441444154416441744184419442044214422442344244425442644274428442944304431443244334434443544364437443844394440444144424443444444454446444744484449445044514452445344544455445644574458445944604461446244634464446544664467446844694470447144724473447444754476447744784479448044814482448344844485448644874488448944904491449244934494449544964497449844994500450145024503450445054506450745084509451045114512451345144515451645174518451945204521452245234524452545264527452845294530453145324533453445354536453745384539454045414542454345444545454645474548454945504551455245534554455545564557455845594560456145624563456445654566456745684569457045714572457345744575457645774578457945804581458245834584458545864587458845894590459145924593459445954596459745984599460046014602460346044605460646074608460946104611461246134614461546164617461846194620462146224623462446254626462746284629463046314632463346344635463646374638463946404641464246434644464546464647464846494650465146524653465446554656465746584659466046614662466346644665466646674668466946704671467246734674467546764677467846794680468146824683468446854686468746884689469046914692469346944695469646974698469947004701470247034704470547064707470847094710471147124713471447154716471747184719472047214722472347244725472647274728472947304731473247334734473547364737473847394740474147424743474447454746474747484749475047514752475347544755475647574758475947604761476247634764476547664767476847694770477147724773477447754776477747784779478047814782478347844785478647874788478947904791479247934794479547964797479847994800480148024803480448054806480748084809481048114812481348144815481648174818481948204821482248234824482548264827482848294830483148324833483448354836483748384839484048414842484348444845484648474848484948504851485248534854485548564857485848594860486148624863486448654866486748684869487048714872487348744875487648774878487948804881488248834884488548864887488848894890489148924893489448954896489748984899490049014902490349044905490649074908490949104911491249134914491549164917491849194920492149224923492449254926492749284929493049314932493349344935493649374938493949404941494249434944494549464947494849494950495149524953495449554956495749584959496049614962496349644965496649674968496949704971497249734974497549764977497849794980498149824983498449854986498749884989499049914992499349944995499649974998499950005001500250035004500550065007500850095010501150125013501450155016501750185019502050215022502350245025502650275028502950305031503250335034503550365037503850395040504150425043504450455046504750485049505050515052505350545055505650575058505950605061506250635064506550665067506850695070507150725073507450755076507750785079508050815082508350845085508650875088508950905091509250935094509550965097509850995100510151025103510451055106510751085109511051115112511351145115511651175118511951205121512251235124512551265127512851295130513151325133513451355136513751385139514051415142514351445145514651475148514951505151515251535154515551565157515851595160516151625163516451655166516751685169517051715172517351745175517651775178517951805181518251835184518551865187518851895190519151925193519451955196519751985199520052015202520352045205520652075208520952105211521252135214521552165217521852195220522152225223522452255226522752285229523052315232523352345235523652375238523952405241524252435244524552465247524852495250525152525253525452555256525752585259526052615262526352645265526652675268526952705271527252735274527552765277527852795280528152825283528452855286528752885289529052915292529352945295529652975298529953005301530253035304530553065307530853095310531153125313531453155316531753185319532053215322532353245325532653275328532953305331533253335334533553365337533853395340534153425343534453455346534753485349535053515352535353545355535653575358535953605361536253635364536553665367536853695370537153725373537453755376537753785379538053815382538353845385538653875388538953905391539253935394539553965397539853995400540154025403540454055406540754085409541054115412541354145415541654175418541954205421542254235424542554265427542854295430543154325433543454355436543754385439544054415442544354445445544654475448544954505451545254535454545554565457545854595460546154625463546454655466546754685469547054715472547354745475547654775478547954805481548254835484548554865487548854895490549154925493549454955496549754985499550055015502550355045505550655075508550955105511551255135514551555165517551855195520552155225523552455255526552755285529553055315532553355345535553655375538553955405541554255435544554555465547554855495550555155525553555455555556555755585559556055615562556355645565556655675568556955705571557255735574557555765577557855795580558155825583558455855586558755885589559055915592559355945595559655975598559956005601560256035604560556065607560856095610561156125613561456155616561756185619562056215622562356245625562656275628562956305631563256335634563556365637563856395640564156425643564456455646564756485649565056515652565356545655565656575658565956605661566256635664566556665667566856695670567156725673567456755676567756785679568056815682568356845685568656875688568956905691569256935694569556965697569856995700570157025703570457055706570757085709571057115712571357145715571657175718571957205721572257235724572557265727572857295730573157325733573457355736573757385739574057415742574357445745574657475748574957505751575257535754575557565757575857595760576157625763576457655766576757685769577057715772577357745775577657775778577957805781578257835784578557865787578857895790579157925793579457955796579757985799580058015802580358045805580658075808580958105811581258135814581558165817581858195820582158225823582458255826582758285829583058315832583358345835583658375838583958405841584258435844584558465847584858495850585158525853585458555856585758585859586058615862586358645865586658675868586958705871587258735874587558765877587858795880588158825883588458855886588758885889589058915892589358945895589658975898589959005901590259035904590559065907590859095910591159125913591459155916591759185919592059215922592359245925592659275928592959305931593259335934593559365937593859395940594159425943594459455946594759485949595059515952595359545955595659575958595959605961596259635964596559665967596859695970597159725973597459755976597759785979598059815982598359845985598659875988598959905991599259935994599559965997599859996000600160026003600460056006600760086009601060116012601360146015601660176018601960206021602260236024602560266027602860296030603160326033603460356036603760386039604060416042604360446045604660476048604960506051605260536054605560566057605860596060606160626063606460656066606760686069607060716072607360746075607660776078607960806081608260836084608560866087608860896090609160926093609460956096609760986099610061016102610361046105610661076108610961106111611261136114611561166117611861196120612161226123612461256126612761286129613061316132613361346135613661376138613961406141614261436144614561466147614861496150615161526153615461556156615761586159616061616162616361646165616661676168616961706171617261736174617561766177617861796180618161826183618461856186618761886189619061916192619361946195619661976198619962006201620262036204620562066207620862096210621162126213621462156216621762186219622062216222622362246225622662276228622962306231623262336234623562366237623862396240624162426243624462456246624762486249625062516252625362546255625662576258625962606261626262636264626562666267626862696270627162726273627462756276627762786279628062816282628362846285628662876288628962906291629262936294629562966297629862996300630163026303630463056306630763086309631063116312631363146315631663176318631963206321632263236324632563266327632863296330633163326333633463356336633763386339634063416342634363446345634663476348634963506351635263536354635563566357635863596360636163626363636463656366636763686369637063716372637363746375637663776378637963806381638263836384638563866387638863896390639163926393639463956396639763986399640064016402640364046405640664076408640964106411641264136414641564166417641864196420642164226423642464256426642764286429643064316432643364346435643664376438643964406441644264436444644564466447644864496450645164526453645464556456645764586459646064616462646364646465646664676468646964706471647264736474647564766477647864796480648164826483648464856486648764886489649064916492649364946495649664976498649965006501650265036504650565066507650865096510651165126513651465156516651765186519652065216522652365246525652665276528652965306531653265336534653565366537653865396540654165426543654465456546654765486549655065516552655365546555655665576558655965606561656265636564656565666567656865696570657165726573657465756576657765786579658065816582658365846585658665876588658965906591659265936594659565966597659865996600660166026603660466056606660766086609661066116612661366146615661666176618661966206621662266236624662566266627662866296630663166326633663466356636663766386639664066416642664366446645664666476648664966506651665266536654665566566657665866596660666166626663666466656666666766686669667066716672667366746675667666776678667966806681668266836684668566866687668866896690669166926693669466956696669766986699670067016702670367046705670667076708670967106711671267136714671567166717671867196720672167226723672467256726672767286729673067316732673367346735673667376738673967406741674267436744674567466747674867496750675167526753675467556756675767586759676067616762676367646765676667676768676967706771677267736774677567766777677867796780678167826783678467856786678767886789679067916792679367946795679667976798679968006801680268036804680568066807680868096810681168126813681468156816681768186819682068216822682368246825682668276828682968306831683268336834683568366837683868396840684168426843684468456846684768486849685068516852685368546855685668576858685968606861686268636864686568666867686868696870687168726873687468756876687768786879688068816882688368846885688668876888688968906891689268936894689568966897689868996900690169026903690469056906690769086909691069116912691369146915691669176918691969206921692269236924692569266927692869296930693169326933693469356936693769386939694069416942694369446945694669476948694969506951695269536954695569566957695869596960696169626963696469656966696769686969697069716972697369746975697669776978697969806981698269836984698569866987698869896990699169926993699469956996699769986999700070017002700370047005700670077008700970107011701270137014701570167017701870197020702170227023702470257026702770287029703070317032703370347035703670377038703970407041704270437044704570467047704870497050705170527053705470557056705770587059706070617062706370647065706670677068706970707071707270737074707570767077707870797080708170827083708470857086708770887089709070917092709370947095709670977098709971007101710271037104710571067107710871097110711171127113711471157116711771187119712071217122712371247125712671277128712971307131713271337134713571367137713871397140714171427143714471457146714771487149715071517152715371547155715671577158715971607161716271637164716571667167716871697170717171727173717471757176717771787179718071817182718371847185718671877188718971907191719271937194719571967197719871997200720172027203720472057206720772087209721072117212721372147215721672177218721972207221722272237224722572267227722872297230723172327233723472357236723772387239724072417242724372447245724672477248724972507251725272537254725572567257725872597260726172627263726472657266726772687269727072717272727372747275727672777278727972807281728272837284728572867287728872897290729172927293729472957296729772987299730073017302730373047305730673077308730973107311731273137314731573167317731873197320732173227323732473257326732773287329733073317332733373347335733673377338733973407341734273437344734573467347734873497350735173527353735473557356735773587359736073617362736373647365736673677368736973707371737273737374737573767377737873797380738173827383738473857386738773887389739073917392739373947395739673977398739974007401740274037404740574067407740874097410741174127413741474157416741774187419742074217422742374247425742674277428742974307431743274337434743574367437743874397440744174427443744474457446744774487449745074517452745374547455745674577458745974607461746274637464746574667467746874697470747174727473747474757476747774787479748074817482748374847485748674877488748974907491749274937494749574967497749874997500750175027503750475057506750775087509751075117512751375147515751675177518751975207521752275237524752575267527752875297530753175327533753475357536753775387539754075417542754375447545754675477548754975507551755275537554755575567557755875597560756175627563756475657566756775687569757075717572757375747575757675777578757975807581758275837584758575867587758875897590759175927593759475957596759775987599760076017602760376047605760676077608760976107611761276137614761576167617761876197620762176227623762476257626762776287629763076317632763376347635763676377638763976407641764276437644764576467647764876497650765176527653765476557656765776587659766076617662766376647665766676677668766976707671767276737674767576767677767876797680768176827683768476857686768776887689769076917692769376947695769676977698769977007701770277037704770577067707770877097710771177127713771477157716771777187719772077217722772377247725772677277728772977307731773277337734773577367737773877397740774177427743774477457746774777487749775077517752775377547755775677577758775977607761776277637764776577667767776877697770777177727773777477757776777777787779778077817782778377847785778677877788778977907791779277937794779577967797779877997800780178027803780478057806780778087809781078117812781378147815781678177818781978207821782278237824782578267827782878297830783178327833783478357836783778387839784078417842784378447845784678477848784978507851785278537854785578567857785878597860786178627863786478657866786778687869787078717872787378747875787678777878787978807881788278837884788578867887788878897890789178927893789478957896789778987899790079017902790379047905790679077908790979107911791279137914791579167917791879197920792179227923792479257926792779287929793079317932793379347935793679377938793979407941794279437944794579467947794879497950795179527953795479557956795779587959796079617962796379647965796679677968796979707971797279737974797579767977797879797980798179827983798479857986798779887989799079917992799379947995799679977998799980008001800280038004800580068007800880098010801180128013801480158016801780188019802080218022802380248025802680278028802980308031803280338034803580368037803880398040804180428043804480458046804780488049805080518052805380548055805680578058805980608061806280638064806580668067806880698070807180728073807480758076807780788079808080818082808380848085808680878088808980908091809280938094809580968097809880998100810181028103810481058106810781088109811081118112811381148115811681178118811981208121812281238124812581268127812881298130813181328133813481358136813781388139814081418142814381448145814681478148814981508151815281538154815581568157815881598160816181628163816481658166816781688169817081718172817381748175817681778178817981808181818281838184818581868187818881898190819181928193819481958196819781988199820082018202820382048205820682078208820982108211821282138214821582168217821882198220822182228223822482258226822782288229823082318232823382348235823682378238823982408241824282438244824582468247824882498250825182528253825482558256825782588259826082618262826382648265826682678268826982708271827282738274827582768277827882798280828182828283828482858286828782888289829082918292829382948295829682978298829983008301830283038304830583068307830883098310831183128313831483158316831783188319832083218322832383248325832683278328832983308331833283338334833583368337833883398340834183428343834483458346834783488349835083518352835383548355835683578358835983608361836283638364836583668367836883698370837183728373837483758376837783788379838083818382838383848385838683878388838983908391839283938394839583968397839883998400840184028403840484058406840784088409841084118412841384148415841684178418841984208421842284238424842584268427842884298430843184328433843484358436843784388439844084418442844384448445844684478448844984508451845284538454845584568457845884598460846184628463846484658466846784688469847084718472847384748475847684778478847984808481848284838484848584868487848884898490849184928493849484958496849784988499850085018502850385048505850685078508850985108511851285138514851585168517851885198520852185228523852485258526852785288529853085318532853385348535853685378538853985408541854285438544854585468547854885498550855185528553855485558556855785588559856085618562856385648565856685678568856985708571857285738574857585768577857885798580858185828583858485858586858785888589859085918592859385948595859685978598859986008601860286038604860586068607860886098610861186128613861486158616861786188619862086218622862386248625862686278628862986308631863286338634863586368637863886398640864186428643864486458646864786488649865086518652865386548655865686578658865986608661866286638664866586668667866886698670867186728673867486758676867786788679868086818682868386848685868686878688868986908691869286938694869586968697869886998700870187028703870487058706870787088709871087118712871387148715871687178718871987208721872287238724872587268727872887298730873187328733873487358736873787388739874087418742874387448745874687478748874987508751875287538754875587568757875887598760876187628763876487658766876787688769877087718772877387748775877687778778877987808781878287838784878587868787878887898790879187928793879487958796879787988799880088018802880388048805880688078808880988108811881288138814881588168817881888198820882188228823882488258826882788288829883088318832883388348835883688378838883988408841884288438844884588468847884888498850885188528853885488558856885788588859886088618862886388648865886688678868886988708871887288738874887588768877887888798880888188828883888488858886888788888889889088918892889388948895889688978898889989008901890289038904890589068907890889098910891189128913891489158916891789188919892089218922892389248925892689278928892989308931893289338934893589368937893889398940894189428943894489458946894789488949895089518952895389548955895689578958895989608961896289638964896589668967896889698970897189728973897489758976897789788979898089818982898389848985898689878988898989908991899289938994899589968997899889999000900190029003900490059006900790089009901090119012901390149015901690179018901990209021902290239024902590269027902890299030903190329033903490359036903790389039904090419042904390449045904690479048904990509051905290539054905590569057905890599060906190629063906490659066906790689069907090719072907390749075907690779078907990809081908290839084908590869087908890899090909190929093909490959096909790989099910091019102910391049105910691079108910991109111911291139114911591169117911891199120912191229123912491259126912791289129913091319132913391349135913691379138913991409141914291439144914591469147914891499150915191529153915491559156915791589159916091619162916391649165916691679168916991709171917291739174917591769177917891799180918191829183918491859186918791889189919091919192919391949195919691979198919992009201920292039204920592069207920892099210921192129213921492159216921792189219922092219222922392249225922692279228922992309231923292339234923592369237923892399240924192429243924492459246924792489249925092519252925392549255925692579258925992609261926292639264926592669267926892699270927192729273927492759276927792789279928092819282928392849285928692879288928992909291929292939294929592969297929892999300930193029303930493059306930793089309931093119312931393149315931693179318931993209321932293239324932593269327932893299330933193329333933493359336933793389339934093419342934393449345934693479348934993509351935293539354935593569357935893599360936193629363936493659366936793689369937093719372937393749375937693779378937993809381938293839384938593869387938893899390939193929393939493959396939793989399940094019402940394049405940694079408940994109411941294139414941594169417941894199420942194229423942494259426942794289429943094319432943394349435943694379438943994409441944294439444944594469447944894499450945194529453945494559456945794589459946094619462946394649465946694679468946994709471947294739474947594769477947894799480948194829483948494859486948794889489949094919492949394949495949694979498949995009501950295039504950595069507950895099510951195129513951495159516951795189519952095219522952395249525952695279528952995309531953295339534953595369537953895399540954195429543954495459546954795489549955095519552955395549555955695579558955995609561956295639564956595669567956895699570957195729573957495759576957795789579958095819582958395849585958695879588958995909591959295939594959595969597959895999600960196029603960496059606960796089609961096119612961396149615961696179618961996209621962296239624962596269627962896299630963196329633963496359636963796389639964096419642964396449645964696479648964996509651965296539654965596569657965896599660966196629663966496659666966796689669967096719672967396749675967696779678967996809681968296839684968596869687968896899690969196929693969496959696969796989699970097019702970397049705970697079708970997109711971297139714971597169717971897199720972197229723972497259726972797289729973097319732973397349735973697379738973997409741974297439744974597469747974897499750975197529753975497559756975797589759976097619762976397649765976697679768976997709771977297739774977597769777977897799780978197829783978497859786978797889789979097919792979397949795979697979798979998009801980298039804980598069807980898099810981198129813981498159816981798189819982098219822982398249825982698279828982998309831983298339834983598369837983898399840984198429843984498459846984798489849985098519852985398549855985698579858985998609861986298639864986598669867986898699870987198729873987498759876987798789879988098819882988398849885988698879888988998909891989298939894989598969897989898999900990199029903990499059906990799089909991099119912991399149915991699179918991999209921992299239924992599269927992899299930993199329933993499359936993799389939994099419942994399449945994699479948994999509951995299539954995599569957995899599960996199629963996499659966996799689969997099719972997399749975997699779978997999809981998299839984998599869987998899899990999199929993999499959996999799989999100001000110002100031000410005100061000710008100091001010011100121001310014100151001610017100181001910020100211002210023100241002510026100271002810029100301003110032100331003410035100361003710038100391004010041100421004310044100451004610047100481004910050100511005210053100541005510056100571005810059100601006110062100631006410065100661006710068100691007010071100721007310074100751007610077100781007910080100811008210083100841008510086100871008810089100901009110092100931009410095100961009710098100991010010101101021010310104101051010610107101081010910110101111011210113101141011510116101171011810119101201012110122101231012410125101261012710128101291013010131101321013310134101351013610137101381013910140101411014210143101441014510146101471014810149101501015110152101531015410155101561015710158101591016010161101621016310164101651016610167101681016910170101711017210173101741017510176101771017810179101801018110182101831018410185101861018710188101891019010191101921019310194101951019610197101981019910200102011020210203102041020510206102071020810209102101021110212102131021410215102161021710218102191022010221102221022310224102251022610227102281022910230102311023210233102341023510236102371023810239102401024110242102431024410245102461024710248102491025010251102521025310254102551025610257102581025910260102611026210263102641026510266102671026810269102701027110272102731027410275102761027710278102791028010281102821028310284102851028610287102881028910290102911029210293102941029510296102971029810299103001030110302103031030410305103061030710308103091031010311103121031310314103151031610317103181031910320103211032210323103241032510326103271032810329103301033110332103331033410335103361033710338103391034010341103421034310344103451034610347103481034910350103511035210353103541035510356103571035810359103601036110362103631036410365103661036710368103691037010371103721037310374103751037610377103781037910380103811038210383103841038510386103871038810389103901039110392103931039410395103961039710398103991040010401104021040310404104051040610407104081040910410104111041210413104141041510416104171041810419104201042110422104231042410425104261042710428104291043010431104321043310434104351043610437104381043910440104411044210443104441044510446104471044810449104501045110452104531045410455104561045710458104591046010461104621046310464104651046610467104681046910470104711047210473104741047510476104771047810479104801048110482104831048410485104861048710488104891049010491104921049310494104951049610497104981049910500105011050210503105041050510506105071050810509105101051110512105131051410515105161051710518105191052010521105221052310524105251052610527105281052910530105311053210533105341053510536105371053810539105401054110542105431054410545105461054710548105491055010551105521055310554105551055610557105581055910560105611056210563105641056510566105671056810569105701057110572105731057410575105761057710578105791058010581105821058310584105851058610587105881058910590105911059210593105941059510596105971059810599106001060110602106031060410605106061060710608106091061010611106121061310614106151061610617106181061910620106211062210623106241062510626106271062810629106301063110632106331063410635106361063710638106391064010641106421064310644106451064610647106481064910650106511065210653106541065510656106571065810659106601066110662106631066410665106661066710668106691067010671106721067310674106751067610677106781067910680106811068210683106841068510686106871068810689106901069110692106931069410695106961069710698106991070010701107021070310704107051070610707107081070910710107111071210713107141071510716107171071810719107201072110722107231072410725107261072710728107291073010731107321073310734107351073610737107381073910740107411074210743107441074510746107471074810749107501075110752107531075410755107561075710758107591076010761107621076310764107651076610767107681076910770107711077210773107741077510776107771077810779107801078110782107831078410785107861078710788107891079010791107921079310794107951079610797107981079910800108011080210803108041080510806108071080810809108101081110812108131081410815108161081710818108191082010821108221082310824108251082610827108281082910830108311083210833108341083510836108371083810839108401084110842108431084410845108461084710848108491085010851108521085310854108551085610857108581085910860108611086210863108641086510866108671086810869108701087110872108731087410875108761087710878108791088010881108821088310884108851088610887108881088910890108911089210893108941089510896108971089810899109001090110902109031090410905109061090710908109091091010911109121091310914109151091610917109181091910920109211092210923109241092510926109271092810929109301093110932109331093410935109361093710938109391094010941109421094310944109451094610947109481094910950109511095210953109541095510956109571095810959109601096110962109631096410965109661096710968109691097010971109721097310974109751097610977109781097910980109811098210983109841098510986109871098810989109901099110992109931099410995109961099710998109991100011001110021100311004110051100611007110081100911010110111101211013110141101511016110171101811019110201102111022110231102411025110261102711028110291103011031110321103311034110351103611037110381103911040110411104211043110441104511046110471104811049110501105111052110531105411055110561105711058110591106011061110621106311064110651106611067110681106911070110711107211073110741107511076110771107811079110801108111082110831108411085110861108711088110891109011091110921109311094110951109611097110981109911100111011110211103111041110511106111071110811109111101111111112111131111411115111161111711118111191112011121111221112311124111251112611127111281112911130111311113211133111341113511136111371113811139111401114111142111431114411145111461114711148111491115011151111521115311154111551115611157111581115911160111611116211163111641116511166111671116811169111701117111172111731117411175111761117711178111791118011181111821118311184111851118611187111881118911190111911119211193111941119511196111971119811199112001120111202112031120411205112061120711208112091121011211112121121311214112151121611217112181121911220112211122211223112241122511226112271122811229112301123111232112331123411235112361123711238112391124011241112421124311244112451124611247112481124911250112511125211253112541125511256112571125811259112601126111262112631126411265112661126711268112691127011271112721127311274112751127611277112781127911280112811128211283112841128511286112871128811289112901129111292112931129411295112961129711298112991130011301113021130311304113051130611307113081130911310113111131211313113141131511316113171131811319113201132111322113231132411325113261132711328113291133011331113321133311334113351133611337113381133911340113411134211343113441134511346113471134811349113501135111352113531135411355113561135711358113591136011361113621136311364113651136611367113681136911370113711137211373113741137511376113771137811379113801138111382113831138411385113861138711388113891139011391113921139311394113951139611397113981139911400114011140211403114041140511406114071140811409114101141111412114131141411415114161141711418114191142011421114221142311424114251142611427114281142911430114311143211433114341143511436114371143811439114401144111442114431144411445114461144711448114491145011451114521145311454114551145611457114581145911460114611146211463114641146511466114671146811469114701147111472114731147411475114761147711478114791148011481114821148311484114851148611487114881148911490114911149211493114941149511496114971149811499115001150111502115031150411505115061150711508115091151011511115121151311514115151151611517115181151911520115211152211523115241152511526115271152811529115301153111532115331153411535115361153711538115391154011541115421154311544115451154611547115481154911550115511155211553115541155511556115571155811559115601156111562115631156411565115661156711568115691157011571115721157311574115751157611577115781157911580115811158211583115841158511586115871158811589115901159111592115931159411595115961159711598115991160011601116021160311604116051160611607116081160911610116111161211613116141161511616116171161811619116201162111622116231162411625116261162711628116291163011631116321163311634116351163611637116381163911640116411164211643116441164511646116471164811649116501165111652116531165411655116561165711658116591166011661116621166311664116651166611667116681166911670116711167211673116741167511676116771167811679116801168111682116831168411685116861168711688116891169011691116921169311694116951169611697116981169911700117011170211703117041170511706117071170811709117101171111712117131171411715117161171711718117191172011721117221172311724117251172611727117281172911730117311173211733117341173511736117371173811739117401174111742117431174411745117461174711748117491175011751117521175311754117551175611757117581175911760117611176211763117641176511766117671176811769117701177111772117731177411775117761177711778117791178011781117821178311784117851178611787117881178911790117911179211793117941179511796117971179811799118001180111802118031180411805118061180711808118091181011811118121181311814118151181611817118181181911820118211182211823118241182511826118271182811829118301183111832118331183411835118361183711838118391184011841118421184311844118451184611847118481184911850118511185211853118541185511856118571185811859118601186111862118631186411865118661186711868118691187011871118721187311874118751187611877118781187911880118811188211883118841188511886118871188811889118901189111892118931189411895118961189711898118991190011901119021190311904119051190611907119081190911910119111191211913119141191511916119171191811919119201192111922119231192411925119261192711928119291193011931119321193311934119351193611937119381193911940119411194211943119441194511946119471194811949119501195111952119531195411955119561195711958119591196011961119621196311964119651196611967119681196911970119711197211973119741197511976119771197811979119801198111982119831198411985119861198711988119891199011991119921199311994119951199611997119981199912000120011200212003120041200512006120071200812009120101201112012120131201412015120161201712018120191202012021120221202312024120251202612027120281202912030120311203212033120341203512036120371203812039120401204112042120431204412045120461204712048120491205012051120521205312054120551205612057120581205912060120611206212063120641206512066120671206812069120701207112072120731207412075120761207712078120791208012081120821208312084120851208612087120881208912090120911209212093120941209512096120971209812099121001210112102121031210412105121061210712108121091211012111121121211312114121151211612117121181211912120121211212212123121241212512126121271212812129121301213112132121331213412135121361213712138121391214012141121421214312144121451214612147121481214912150121511215212153121541215512156121571215812159121601216112162121631216412165121661216712168121691217012171121721217312174121751217612177121781217912180121811218212183121841218512186121871218812189121901219112192121931219412195121961219712198121991220012201122021220312204122051220612207122081220912210122111221212213122141221512216122171221812219122201222112222122231222412225122261222712228122291223012231122321223312234122351223612237122381223912240122411224212243122441224512246122471224812249122501225112252122531225412255122561225712258122591226012261122621226312264122651226612267122681226912270122711227212273122741227512276122771227812279122801228112282122831228412285122861228712288122891229012291122921229312294122951229612297122981229912300123011230212303123041230512306123071230812309123101231112312123131231412315123161231712318123191232012321123221232312324123251232612327123281232912330123311233212333123341233512336123371233812339123401234112342123431234412345123461234712348123491235012351123521235312354123551235612357123581235912360123611236212363123641236512366123671236812369123701237112372123731237412375123761237712378123791238012381123821238312384123851238612387123881238912390123911239212393123941239512396123971239812399124001240112402124031240412405124061240712408124091241012411124121241312414124151241612417124181241912420124211242212423124241242512426124271242812429124301243112432124331243412435124361243712438124391244012441124421244312444124451244612447124481244912450124511245212453124541245512456124571245812459124601246112462124631246412465124661246712468124691247012471124721247312474124751247612477124781247912480124811248212483124841248512486124871248812489124901249112492124931249412495124961249712498124991250012501125021250312504125051250612507125081250912510125111251212513125141251512516125171251812519125201252112522125231252412525125261252712528125291253012531125321253312534125351253612537125381253912540125411254212543125441254512546125471254812549125501255112552125531255412555125561255712558125591256012561125621256312564125651256612567125681256912570125711257212573125741257512576125771257812579125801258112582125831258412585125861258712588125891259012591125921259312594125951259612597125981259912600126011260212603126041260512606126071260812609126101261112612126131261412615126161261712618126191262012621126221262312624126251262612627126281262912630126311263212633126341263512636126371263812639126401264112642126431264412645126461264712648126491265012651126521265312654126551265612657126581265912660126611266212663126641266512666126671266812669126701267112672126731267412675126761267712678126791268012681126821268312684126851268612687126881268912690126911269212693126941269512696126971269812699127001270112702127031270412705127061270712708127091271012711127121271312714127151271612717127181271912720127211272212723127241272512726127271272812729127301273112732127331273412735127361273712738127391274012741127421274312744127451274612747127481274912750127511275212753127541275512756127571275812759127601276112762127631276412765127661276712768127691277012771127721277312774127751277612777127781277912780127811278212783127841278512786127871278812789127901279112792127931279412795127961279712798127991280012801128021280312804128051280612807128081280912810128111281212813128141281512816128171281812819128201282112822128231282412825128261282712828128291283012831128321283312834128351283612837128381283912840128411284212843128441284512846128471284812849128501285112852128531285412855128561285712858128591286012861128621286312864128651286612867128681286912870128711287212873128741287512876128771287812879128801288112882128831288412885128861288712888128891289012891128921289312894128951289612897128981289912900129011290212903129041290512906129071290812909129101291112912129131291412915129161291712918129191292012921129221292312924129251292612927129281292912930129311293212933129341293512936129371293812939129401294112942129431294412945129461294712948129491295012951129521295312954129551295612957129581295912960129611296212963129641296512966129671296812969129701297112972129731297412975129761297712978129791298012981129821298312984129851298612987129881298912990129911299212993129941299512996129971299812999130001300113002130031300413005130061300713008130091301013011130121301313014130151301613017130181301913020130211302213023130241302513026130271302813029130301303113032130331303413035130361303713038130391304013041130421304313044130451304613047130481304913050130511305213053130541305513056130571305813059130601306113062130631306413065130661306713068130691307013071130721307313074130751307613077130781307913080130811308213083130841308513086130871308813089130901309113092130931309413095130961309713098130991310013101131021310313104131051310613107131081310913110131111311213113131141311513116131171311813119131201312113122131231312413125131261312713128131291313013131131321313313134131351313613137131381313913140131411314213143131441314513146131471314813149131501315113152131531315413155131561315713158131591316013161131621316313164131651316613167131681316913170131711317213173131741317513176131771317813179131801318113182131831318413185131861318713188131891319013191131921319313194131951319613197131981319913200132011320213203132041320513206132071320813209132101321113212132131321413215132161321713218132191322013221132221322313224132251322613227132281322913230132311323213233132341323513236132371323813239132401324113242132431324413245132461324713248132491325013251132521325313254132551325613257132581325913260132611326213263132641326513266132671326813269132701327113272132731327413275132761327713278132791328013281132821328313284132851328613287132881328913290132911329213293132941329513296132971329813299133001330113302133031330413305133061330713308133091331013311133121331313314133151331613317133181331913320133211332213323133241332513326133271332813329133301333113332133331333413335133361333713338133391334013341133421334313344133451334613347133481334913350133511335213353133541335513356133571335813359133601336113362133631336413365133661336713368133691337013371133721337313374133751337613377133781337913380133811338213383133841338513386133871338813389133901339113392133931339413395133961339713398133991340013401134021340313404134051340613407134081340913410134111341213413134141341513416134171341813419134201342113422134231342413425134261342713428134291343013431134321343313434134351343613437134381343913440134411344213443134441344513446134471344813449134501345113452134531345413455134561345713458134591346013461134621346313464134651346613467134681346913470134711347213473134741347513476134771347813479134801348113482134831348413485134861348713488134891349013491134921349313494134951349613497134981349913500135011350213503135041350513506135071350813509135101351113512135131351413515135161351713518135191352013521135221352313524135251352613527135281352913530135311353213533135341353513536135371353813539135401354113542135431354413545135461354713548135491355013551135521355313554135551355613557135581355913560135611356213563135641356513566135671356813569135701357113572135731357413575135761357713578135791358013581135821358313584135851358613587135881358913590135911359213593135941359513596135971359813599136001360113602136031360413605136061360713608136091361013611136121361313614136151361613617136181361913620136211362213623136241362513626136271362813629136301363113632136331363413635136361363713638136391364013641136421364313644136451364613647136481364913650136511365213653136541365513656136571365813659136601366113662136631366413665136661366713668136691367013671136721367313674136751367613677136781367913680136811368213683136841368513686136871368813689136901369113692136931369413695136961369713698136991370013701137021370313704137051370613707137081370913710137111371213713137141371513716137171371813719137201372113722137231372413725137261372713728137291373013731137321373313734137351373613737137381373913740137411374213743 |
- // ThreeExtras.js - http://github.com/mrdoob/three.js
- /**
- * @author alteredq / http://alteredqualia.com/
- */
- THREE.ColorUtils = {
- adjustHSV : function ( color, h, s, v ) {
- var hsv = THREE.ColorUtils.__hsv;
- THREE.ColorUtils.rgbToHsv( color, hsv );
- hsv.h = THREE.Math.clamp( hsv.h + h, 0, 1 );
- hsv.s = THREE.Math.clamp( hsv.s + s, 0, 1 );
- hsv.v = THREE.Math.clamp( hsv.v + v, 0, 1 );
- color.setHSV( hsv.h, hsv.s, hsv.v );
- },
- // based on MochiKit implementation by Bob Ippolito
- rgbToHsv : function ( color, hsv ) {
- var r = color.r;
- var g = color.g;
- var b = color.b;
- var max = Math.max( Math.max( r, g ), b );
- var min = Math.min( Math.min( r, g ), b );
- var hue;
- var saturation;
- var value = max;
- if ( min === max ) {
- hue = 0;
- saturation = 0;
- } else {
- var delta = ( max - min );
- saturation = delta / max;
- if ( r === max ) {
- hue = ( g - b ) / delta;
- } else if ( g === max ) {
- hue = 2 + ( ( b - r ) / delta );
- } else {
- hue = 4 + ( ( r - g ) / delta );
- }
- hue /= 6;
- if ( hue < 0 ) {
- hue += 1;
- }
- if ( hue > 1 ) {
- hue -= 1;
- }
- }
- if ( hsv === undefined ) {
- hsv = { h: 0, s: 0, v: 0 };
- }
- hsv.h = hue;
- hsv.s = saturation;
- hsv.v = value;
- return hsv;
- }
- };
- THREE.ColorUtils.__hsv = { h: 0, s: 0, v: 0 };/**
- * @author mrdoob / http://mrdoob.com/
- * @author alteredq / http://alteredqualia.com/
- */
- THREE.GeometryUtils = {
- // Merge two geometries or geometry and geometry from object (using object's transform)
- merge: function ( geometry1, object2 /* mesh | geometry */ ) {
- var matrix, matrixRotation,
- vertexOffset = geometry1.vertices.length,
- uvPosition = geometry1.faceVertexUvs[ 0 ].length,
- geometry2 = object2 instanceof THREE.Mesh ? object2.geometry : object2,
- vertices1 = geometry1.vertices,
- vertices2 = geometry2.vertices,
- faces1 = geometry1.faces,
- faces2 = geometry2.faces,
- uvs1 = geometry1.faceVertexUvs[ 0 ],
- uvs2 = geometry2.faceVertexUvs[ 0 ];
- var geo1MaterialsMap = {};
- for ( var i = 0; i < geometry1.materials.length; i ++ ) {
- var id = geometry1.materials[ i ].id;
- geo1MaterialsMap[ id ] = i;
- }
- if ( object2 instanceof THREE.Mesh ) {
- object2.matrixAutoUpdate && object2.updateMatrix();
- matrix = object2.matrix;
- matrixRotation = new THREE.Matrix4();
- matrixRotation.extractRotation( matrix, object2.scale );
- }
- // vertices
- for ( var i = 0, il = vertices2.length; i < il; i ++ ) {
- var vertex = vertices2[ i ];
- var vertexCopy = vertex.clone();
- if ( matrix ) matrix.multiplyVector3( vertexCopy );
- vertices1.push( vertexCopy );
- }
- // faces
- for ( i = 0, il = faces2.length; i < il; i ++ ) {
- var face = faces2[ i ], faceCopy, normal, color,
- faceVertexNormals = face.vertexNormals,
- faceVertexColors = face.vertexColors;
- if ( face instanceof THREE.Face3 ) {
- faceCopy = new THREE.Face3( face.a + vertexOffset, face.b + vertexOffset, face.c + vertexOffset );
- } else if ( face instanceof THREE.Face4 ) {
- faceCopy = new THREE.Face4( face.a + vertexOffset, face.b + vertexOffset, face.c + vertexOffset, face.d + vertexOffset );
- }
- faceCopy.normal.copy( face.normal );
- if ( matrixRotation ) matrixRotation.multiplyVector3( faceCopy.normal );
- for ( var j = 0, jl = faceVertexNormals.length; j < jl; j ++ ) {
- normal = faceVertexNormals[ j ].clone();
- if ( matrixRotation ) matrixRotation.multiplyVector3( normal );
- faceCopy.vertexNormals.push( normal );
- }
- faceCopy.color.copy( face.color );
- for ( var j = 0, jl = faceVertexColors.length; j < jl; j ++ ) {
- color = faceVertexColors[ j ];
- faceCopy.vertexColors.push( color.clone() );
- }
- if ( face.materialIndex !== undefined ) {
- var material2 = geometry2.materials[ face.materialIndex ];
- var materialId2 = material2.id;
- var materialIndex = geo1MaterialsMap[ materialId2 ];
- if ( materialIndex === undefined ) {
- materialIndex = geometry1.materials.length;
- geo1MaterialsMap[ materialId2 ] = materialIndex;
- geometry1.materials.push( material2 );
- }
- faceCopy.materialIndex = materialIndex;
- }
- faceCopy.centroid.copy( face.centroid );
- if ( matrix ) matrix.multiplyVector3( faceCopy.centroid );
- faces1.push( faceCopy );
- }
- // uvs
- for ( i = 0, il = uvs2.length; i < il; i ++ ) {
- var uv = uvs2[ i ], uvCopy = [];
- for ( var j = 0, jl = uv.length; j < jl; j ++ ) {
- uvCopy.push( new THREE.UV( uv[ j ].u, uv[ j ].v ) );
- }
- uvs1.push( uvCopy );
- }
- },
- clone: function ( geometry ) {
- var cloneGeo = new THREE.Geometry();
- var i, il;
- var vertices = geometry.vertices,
- faces = geometry.faces,
- uvs = geometry.faceVertexUvs[ 0 ];
- // materials
- if ( geometry.materials ) {
- cloneGeo.materials = geometry.materials.slice();
- }
- // vertices
- for ( i = 0, il = vertices.length; i < il; i ++ ) {
- var vertex = vertices[ i ];
- cloneGeo.vertices.push( vertex.clone() );
- }
- // faces
- for ( i = 0, il = faces.length; i < il; i ++ ) {
- var face = faces[ i ];
- cloneGeo.faces.push( face.clone() );
- }
- // uvs
- for ( i = 0, il = uvs.length; i < il; i ++ ) {
- var uv = uvs[ i ], uvCopy = [];
- for ( var j = 0, jl = uv.length; j < jl; j ++ ) {
- uvCopy.push( new THREE.UV( uv[ j ].u, uv[ j ].v ) );
- }
- cloneGeo.faceVertexUvs[ 0 ].push( uvCopy );
- }
- return cloneGeo;
- },
- // Get random point in triangle (via barycentric coordinates)
- // (uniform distribution)
- // http://www.cgafaq.info/wiki/Random_Point_In_Triangle
- randomPointInTriangle: function ( vectorA, vectorB, vectorC ) {
- var a, b, c,
- point = new THREE.Vector3(),
- tmp = THREE.GeometryUtils.__v1;
- a = THREE.GeometryUtils.random();
- b = THREE.GeometryUtils.random();
- if ( ( a + b ) > 1 ) {
- a = 1 - a;
- b = 1 - b;
- }
- c = 1 - a - b;
- point.copy( vectorA );
- point.multiplyScalar( a );
- tmp.copy( vectorB );
- tmp.multiplyScalar( b );
- point.addSelf( tmp );
- tmp.copy( vectorC );
- tmp.multiplyScalar( c );
- point.addSelf( tmp );
- return point;
- },
- // Get random point in face (triangle / quad)
- // (uniform distribution)
- randomPointInFace: function ( face, geometry, useCachedAreas ) {
- var vA, vB, vC, vD;
- if ( face instanceof THREE.Face3 ) {
- vA = geometry.vertices[ face.a ];
- vB = geometry.vertices[ face.b ];
- vC = geometry.vertices[ face.c ];
- return THREE.GeometryUtils.randomPointInTriangle( vA, vB, vC );
- } else if ( face instanceof THREE.Face4 ) {
- vA = geometry.vertices[ face.a ];
- vB = geometry.vertices[ face.b ];
- vC = geometry.vertices[ face.c ];
- vD = geometry.vertices[ face.d ];
- var area1, area2;
- if ( useCachedAreas ) {
- if ( face._area1 && face._area2 ) {
- area1 = face._area1;
- area2 = face._area2;
- } else {
- area1 = THREE.GeometryUtils.triangleArea( vA, vB, vD );
- area2 = THREE.GeometryUtils.triangleArea( vB, vC, vD );
- face._area1 = area1;
- face._area2 = area2;
- }
- } else {
- area1 = THREE.GeometryUtils.triangleArea( vA, vB, vD ),
- area2 = THREE.GeometryUtils.triangleArea( vB, vC, vD );
- }
- var r = THREE.GeometryUtils.random() * ( area1 + area2 );
- if ( r < area1 ) {
- return THREE.GeometryUtils.randomPointInTriangle( vA, vB, vD );
- } else {
- return THREE.GeometryUtils.randomPointInTriangle( vB, vC, vD );
- }
- }
- },
- // Get uniformly distributed random points in mesh
- // - create array with cumulative sums of face areas
- // - pick random number from 0 to total area
- // - find corresponding place in area array by binary search
- // - get random point in face
- randomPointsInGeometry: function ( geometry, n ) {
- var face, i,
- faces = geometry.faces,
- vertices = geometry.vertices,
- il = faces.length,
- totalArea = 0,
- cumulativeAreas = [],
- vA, vB, vC, vD;
- // precompute face areas
- for ( i = 0; i < il; i ++ ) {
- face = faces[ i ];
- if ( face instanceof THREE.Face3 ) {
- vA = vertices[ face.a ];
- vB = vertices[ face.b ];
- vC = vertices[ face.c ];
- face._area = THREE.GeometryUtils.triangleArea( vA, vB, vC );
- } else if ( face instanceof THREE.Face4 ) {
- vA = vertices[ face.a ];
- vB = vertices[ face.b ];
- vC = vertices[ face.c ];
- vD = vertices[ face.d ];
- face._area1 = THREE.GeometryUtils.triangleArea( vA, vB, vD );
- face._area2 = THREE.GeometryUtils.triangleArea( vB, vC, vD );
- face._area = face._area1 + face._area2;
- }
- totalArea += face._area;
- cumulativeAreas[ i ] = totalArea;
- }
- // binary search cumulative areas array
- function binarySearchIndices( value ) {
- function binarySearch( start, end ) {
- // return closest larger index
- // if exact number is not found
- if ( end < start )
- return start;
- var mid = start + Math.floor( ( end - start ) / 2 );
- if ( cumulativeAreas[ mid ] > value ) {
- return binarySearch( start, mid - 1 );
- } else if ( cumulativeAreas[ mid ] < value ) {
- return binarySearch( mid + 1, end );
- } else {
- return mid;
- }
- }
- var result = binarySearch( 0, cumulativeAreas.length - 1 )
- return result;
- }
- // pick random face weighted by face area
- var r, index,
- result = [];
- var stats = {};
- for ( i = 0; i < n; i ++ ) {
- r = THREE.GeometryUtils.random() * totalArea;
- index = binarySearchIndices( r );
- result[ i ] = THREE.GeometryUtils.randomPointInFace( faces[ index ], geometry, true );
- if ( ! stats[ index ] ) {
- stats[ index ] = 1;
- } else {
- stats[ index ] += 1;
- }
- }
- return result;
- },
- // Get triangle area (by Heron's formula)
- // http://en.wikipedia.org/wiki/Heron%27s_formula
- triangleArea: function ( vectorA, vectorB, vectorC ) {
- var s, a, b, c,
- tmp = THREE.GeometryUtils.__v1;
- tmp.sub( vectorA, vectorB );
- a = tmp.length();
- tmp.sub( vectorA, vectorC );
- b = tmp.length();
- tmp.sub( vectorB, vectorC );
- c = tmp.length();
- s = 0.5 * ( a + b + c );
- return Math.sqrt( s * ( s - a ) * ( s - b ) * ( s - c ) );
- },
- // Center geometry so that 0,0,0 is in center of bounding box
- center: function ( geometry ) {
- geometry.computeBoundingBox();
- var bb = geometry.boundingBox;
- var offset = new THREE.Vector3();
- offset.add( bb.min, bb.max );
- offset.multiplyScalar( -0.5 );
- geometry.applyMatrix( new THREE.Matrix4().makeTranslation( offset.x, offset.y, offset.z ) );
- geometry.computeBoundingBox();
- return offset;
- },
- // Normalize UVs to be from <0,1>
- // (for now just the first set of UVs)
- normalizeUVs: function ( geometry ) {
- var uvSet = geometry.faceVertexUvs[ 0 ];
- for ( var i = 0, il = uvSet.length; i < il; i ++ ) {
- var uvs = uvSet[ i ];
- for ( var j = 0, jl = uvs.length; j < jl; j ++ ) {
- // texture repeat
- if( uvs[ j ].u !== 1.0 ) uvs[ j ].u = uvs[ j ].u - Math.floor( uvs[ j ].u );
- if( uvs[ j ].v !== 1.0 ) uvs[ j ].v = uvs[ j ].v - Math.floor( uvs[ j ].v );
- }
- }
- },
- triangulateQuads: function ( geometry ) {
- var i, il, j, jl;
- var faces = [];
- var faceUvs = [];
- var faceVertexUvs = [];
- for ( i = 0, il = geometry.faceUvs.length; i < il; i ++ ) {
- faceUvs[ i ] = [];
- }
- for ( i = 0, il = geometry.faceVertexUvs.length; i < il; i ++ ) {
- faceVertexUvs[ i ] = [];
- }
- for ( i = 0, il = geometry.faces.length; i < il; i ++ ) {
- var face = geometry.faces[ i ];
- if ( face instanceof THREE.Face4 ) {
- var a = face.a;
- var b = face.b;
- var c = face.c;
- var d = face.d;
- var triA = new THREE.Face3();
- var triB = new THREE.Face3();
- triA.color.copy( face.color );
- triB.color.copy( face.color );
- triA.materialIndex = face.materialIndex;
- triB.materialIndex = face.materialIndex;
- triA.a = a;
- triA.b = b;
- triA.c = d;
- triB.a = b;
- triB.b = c;
- triB.c = d;
- if ( face.vertexColors.length === 4 ) {
- triA.vertexColors[ 0 ] = face.vertexColors[ 0 ].clone();
- triA.vertexColors[ 1 ] = face.vertexColors[ 1 ].clone();
- triA.vertexColors[ 2 ] = face.vertexColors[ 3 ].clone();
- triB.vertexColors[ 0 ] = face.vertexColors[ 1 ].clone();
- triB.vertexColors[ 1 ] = face.vertexColors[ 2 ].clone();
- triB.vertexColors[ 2 ] = face.vertexColors[ 3 ].clone();
- }
- faces.push( triA, triB );
- for ( j = 0, jl = geometry.faceVertexUvs.length; j < jl; j ++ ) {
- if ( geometry.faceVertexUvs[ j ].length ) {
- var uvs = geometry.faceVertexUvs[ j ][ i ];
- var uvA = uvs[ 0 ];
- var uvB = uvs[ 1 ];
- var uvC = uvs[ 2 ];
- var uvD = uvs[ 3 ];
- var uvsTriA = [ uvA.clone(), uvB.clone(), uvD.clone() ];
- var uvsTriB = [ uvB.clone(), uvC.clone(), uvD.clone() ];
- faceVertexUvs[ j ].push( uvsTriA, uvsTriB );
- }
- }
- for ( j = 0, jl = geometry.faceUvs.length; j < jl; j ++ ) {
- if ( geometry.faceUvs[ j ].length ) {
- var faceUv = geometry.faceUvs[ j ][ i ];
- faceUvs[ j ].push( faceUv, faceUv );
- }
- }
- } else {
- faces.push( face );
- for ( j = 0, jl = geometry.faceUvs.length; j < jl; j ++ ) {
- faceUvs[ j ].push( geometry.faceUvs[ j ] );
- }
- for ( j = 0, jl = geometry.faceVertexUvs.length; j < jl; j ++ ) {
- faceVertexUvs[ j ].push( geometry.faceVertexUvs[ j ] );
- }
- }
- }
- geometry.faces = faces;
- geometry.faceUvs = faceUvs;
- geometry.faceVertexUvs = faceVertexUvs;
- geometry.computeCentroids();
- geometry.computeFaceNormals();
- geometry.computeVertexNormals();
- if ( geometry.hasTangents ) geometry.computeTangents();
- },
- // Make all faces use unique vertices
- // so that each face can be separated from others
- explode: function( geometry ) {
- var vertices = [];
- for ( var i = 0, il = geometry.faces.length; i < il; i ++ ) {
- var n = vertices.length;
- var face = geometry.faces[ i ];
- if ( face instanceof THREE.Face4 ) {
- var a = face.a;
- var b = face.b;
- var c = face.c;
- var d = face.d;
- var va = geometry.vertices[ a ];
- var vb = geometry.vertices[ b ];
- var vc = geometry.vertices[ c ];
- var vd = geometry.vertices[ d ];
- vertices.push( va.clone() );
- vertices.push( vb.clone() );
- vertices.push( vc.clone() );
- vertices.push( vd.clone() );
- face.a = n;
- face.b = n + 1;
- face.c = n + 2;
- face.d = n + 3;
- } else {
- var a = face.a;
- var b = face.b;
- var c = face.c;
- var va = geometry.vertices[ a ];
- var vb = geometry.vertices[ b ];
- var vc = geometry.vertices[ c ];
- vertices.push( va.clone() );
- vertices.push( vb.clone() );
- vertices.push( vc.clone() );
- face.a = n;
- face.b = n + 1;
- face.c = n + 2;
- }
- }
- geometry.vertices = vertices;
- delete geometry.__tmpVertices;
- },
- // Break faces with edges longer than maxEdgeLength
- // - not recursive
- tessellate: function ( geometry, maxEdgeLength ) {
- var i, il, face,
- a, b, c, d,
- va, vb, vc, vd,
- dab, dbc, dac, dcd, dad,
- m, m1, m2,
- vm, vm1, vm2,
- vnm, vnm1, vnm2,
- vcm, vcm1, vcm2,
- triA, triB,
- quadA, quadB,
- edge;
- var faces = [];
- var faceVertexUvs = [];
- for ( i = 0, il = geometry.faceVertexUvs.length; i < il; i ++ ) {
- faceVertexUvs[ i ] = [];
- }
- for ( i = 0, il = geometry.faces.length; i < il; i ++ ) {
- face = geometry.faces[ i ];
- if ( face instanceof THREE.Face3 ) {
- a = face.a;
- b = face.b;
- c = face.c;
- va = geometry.vertices[ a ];
- vb = geometry.vertices[ b ];
- vc = geometry.vertices[ c ];
- dab = va.distanceTo( vb );
- dbc = vb.distanceTo( vc );
- dac = va.distanceTo( vc );
- if ( dab > maxEdgeLength || dbc > maxEdgeLength || dac > maxEdgeLength ) {
- m = geometry.vertices.length;
- triA = face.clone();
- triB = face.clone();
- if ( dab >= dbc && dab >= dac ) {
- vm = va.clone();
- vm.lerpSelf( vb, 0.5 );
- triA.a = a;
- triA.b = m;
- triA.c = c;
- triB.a = m;
- triB.b = b;
- triB.c = c;
- if ( face.vertexNormals.length === 3 ) {
- vnm = face.vertexNormals[ 0 ].clone();
- vnm.lerpSelf( face.vertexNormals[ 1 ], 0.5 );
- triA.vertexNormals[ 1 ].copy( vnm );
- triB.vertexNormals[ 0 ].copy( vnm );
- }
- if ( face.vertexColors.length === 3 ) {
- vcm = face.vertexColors[ 0 ].clone();
- vcm.lerpSelf( face.vertexColors[ 1 ], 0.5 );
- triA.vertexColors[ 1 ].copy( vcm );
- triB.vertexColors[ 0 ].copy( vcm );
- }
- edge = 0;
- } else if ( dbc >= dab && dbc >= dac ) {
- vm = vb.clone();
- vm.lerpSelf( vc, 0.5 );
- triA.a = a;
- triA.b = b;
- triA.c = m;
- triB.a = m;
- triB.b = c;
- triB.c = a;
- if ( face.vertexNormals.length === 3 ) {
- vnm = face.vertexNormals[ 1 ].clone();
- vnm.lerpSelf( face.vertexNormals[ 2 ], 0.5 );
- triA.vertexNormals[ 2 ].copy( vnm );
- triB.vertexNormals[ 0 ].copy( vnm );
- triB.vertexNormals[ 1 ].copy( face.vertexNormals[ 2 ] );
- triB.vertexNormals[ 2 ].copy( face.vertexNormals[ 0 ] );
- }
- if ( face.vertexColors.length === 3 ) {
- vcm = face.vertexColors[ 1 ].clone();
- vcm.lerpSelf( face.vertexColors[ 2 ], 0.5 );
- triA.vertexColors[ 2 ].copy( vcm );
- triB.vertexColors[ 0 ].copy( vcm );
- triB.vertexColors[ 1 ].copy( face.vertexColors[ 2 ] );
- triB.vertexColors[ 2 ].copy( face.vertexColors[ 0 ] );
- }
- edge = 1;
- } else {
- vm = va.clone();
- vm.lerpSelf( vc, 0.5 );
- triA.a = a;
- triA.b = b;
- triA.c = m;
- triB.a = m;
- triB.b = b;
- triB.c = c;
- if ( face.vertexNormals.length === 3 ) {
- vnm = face.vertexNormals[ 0 ].clone();
- vnm.lerpSelf( face.vertexNormals[ 2 ], 0.5 );
- triA.vertexNormals[ 2 ].copy( vnm );
- triB.vertexNormals[ 0 ].copy( vnm );
- }
- if ( face.vertexColors.length === 3 ) {
- vcm = face.vertexColors[ 0 ].clone();
- vcm.lerpSelf( face.vertexColors[ 2 ], 0.5 );
- triA.vertexColors[ 2 ].copy( vcm );
- triB.vertexColors[ 0 ].copy( vcm );
- }
- edge = 2;
- }
- faces.push( triA, triB );
- geometry.vertices.push( vm );
- var j, jl, uvs, uvA, uvB, uvC, uvM, uvsTriA, uvsTriB;
- for ( j = 0, jl = geometry.faceVertexUvs.length; j < jl; j ++ ) {
- if ( geometry.faceVertexUvs[ j ].length ) {
- uvs = geometry.faceVertexUvs[ j ][ i ];
- uvA = uvs[ 0 ];
- uvB = uvs[ 1 ];
- uvC = uvs[ 2 ];
- // AB
- if ( edge === 0 ) {
- uvM = uvA.clone();
- uvM.lerpSelf( uvB, 0.5 );
- uvsTriA = [ uvA.clone(), uvM.clone(), uvC.clone() ];
- uvsTriB = [ uvM.clone(), uvB.clone(), uvC.clone() ];
- // BC
- } else if ( edge === 1 ) {
- uvM = uvB.clone();
- uvM.lerpSelf( uvC, 0.5 );
- uvsTriA = [ uvA.clone(), uvB.clone(), uvM.clone() ];
- uvsTriB = [ uvM.clone(), uvC.clone(), uvA.clone() ];
- // AC
- } else {
- uvM = uvA.clone();
- uvM.lerpSelf( uvC, 0.5 );
- uvsTriA = [ uvA.clone(), uvB.clone(), uvM.clone() ];
- uvsTriB = [ uvM.clone(), uvB.clone(), uvC.clone() ];
- }
- faceVertexUvs[ j ].push( uvsTriA, uvsTriB );
- }
- }
- } else {
- faces.push( face );
- for ( j = 0, jl = geometry.faceVertexUvs.length; j < jl; j ++ ) {
- faceVertexUvs[ j ].push( geometry.faceVertexUvs[ j ][ i ] );
- }
- }
- } else {
- a = face.a;
- b = face.b;
- c = face.c;
- d = face.d;
- va = geometry.vertices[ a ];
- vb = geometry.vertices[ b ];
- vc = geometry.vertices[ c ];
- vd = geometry.vertices[ d ];
- dab = va.distanceTo( vb );
- dbc = vb.distanceTo( vc );
- dcd = vc.distanceTo( vd );
- dad = va.distanceTo( vd );
- if ( dab > maxEdgeLength || dbc > maxEdgeLength || dcd > maxEdgeLength || dad > maxEdgeLength ) {
- m1 = geometry.vertices.length;
- m2 = geometry.vertices.length + 1;
- quadA = face.clone();
- quadB = face.clone();
- if ( ( dab >= dbc && dab >= dcd && dab >= dad ) || ( dcd >= dbc && dcd >= dab && dcd >= dad ) ) {
- vm1 = va.clone();
- vm1.lerpSelf( vb, 0.5 );
- vm2 = vc.clone();
- vm2.lerpSelf( vd, 0.5 );
- quadA.a = a;
- quadA.b = m1;
- quadA.c = m2;
- quadA.d = d;
- quadB.a = m1;
- quadB.b = b;
- quadB.c = c;
- quadB.d = m2;
- if ( face.vertexNormals.length === 4 ) {
- vnm1 = face.vertexNormals[ 0 ].clone();
- vnm1.lerpSelf( face.vertexNormals[ 1 ], 0.5 );
- vnm2 = face.vertexNormals[ 2 ].clone();
- vnm2.lerpSelf( face.vertexNormals[ 3 ], 0.5 );
- quadA.vertexNormals[ 1 ].copy( vnm1 );
- quadA.vertexNormals[ 2 ].copy( vnm2 );
- quadB.vertexNormals[ 0 ].copy( vnm1 );
- quadB.vertexNormals[ 3 ].copy( vnm2 );
- }
- if ( face.vertexColors.length === 4 ) {
- vcm1 = face.vertexColors[ 0 ].clone();
- vcm1.lerpSelf( face.vertexColors[ 1 ], 0.5 );
- vcm2 = face.vertexColors[ 2 ].clone();
- vcm2.lerpSelf( face.vertexColors[ 3 ], 0.5 );
- quadA.vertexColors[ 1 ].copy( vcm1 );
- quadA.vertexColors[ 2 ].copy( vcm2 );
- quadB.vertexColors[ 0 ].copy( vcm1 );
- quadB.vertexColors[ 3 ].copy( vcm2 );
- }
- edge = 0;
- } else {
- vm1 = vb.clone();
- vm1.lerpSelf( vc, 0.5 );
- vm2 = vd.clone();
- vm2.lerpSelf( va, 0.5 );
- quadA.a = a;
- quadA.b = b;
- quadA.c = m1;
- quadA.d = m2;
- quadB.a = m2;
- quadB.b = m1;
- quadB.c = c;
- quadB.d = d;
- if ( face.vertexNormals.length === 4 ) {
- vnm1 = face.vertexNormals[ 1 ].clone();
- vnm1.lerpSelf( face.vertexNormals[ 2 ], 0.5 );
- vnm2 = face.vertexNormals[ 3 ].clone();
- vnm2.lerpSelf( face.vertexNormals[ 0 ], 0.5 );
- quadA.vertexNormals[ 2 ].copy( vnm1 );
- quadA.vertexNormals[ 3 ].copy( vnm2 );
- quadB.vertexNormals[ 0 ].copy( vnm2 );
- quadB.vertexNormals[ 1 ].copy( vnm1 );
- }
- if ( face.vertexColors.length === 4 ) {
- vcm1 = face.vertexColors[ 1 ].clone();
- vcm1.lerpSelf( face.vertexColors[ 2 ], 0.5 );
- vcm2 = face.vertexColors[ 3 ].clone();
- vcm2.lerpSelf( face.vertexColors[ 0 ], 0.5 );
- quadA.vertexColors[ 2 ].copy( vcm1 );
- quadA.vertexColors[ 3 ].copy( vcm2 );
- quadB.vertexColors[ 0 ].copy( vcm2 );
- quadB.vertexColors[ 1 ].copy( vcm1 );
- }
- edge = 1;
- }
- faces.push( quadA, quadB );
- geometry.vertices.push( vm1, vm2 );
- var j, jl, uvs, uvA, uvB, uvC, uvD, uvM1, uvM2, uvsQuadA, uvsQuadB;
- for ( j = 0, jl = geometry.faceVertexUvs.length; j < jl; j ++ ) {
- if ( geometry.faceVertexUvs[ j ].length ) {
- uvs = geometry.faceVertexUvs[ j ][ i ];
- uvA = uvs[ 0 ];
- uvB = uvs[ 1 ];
- uvC = uvs[ 2 ];
- uvD = uvs[ 3 ];
- // AB + CD
- if ( edge === 0 ) {
- uvM1 = uvA.clone();
- uvM1.lerpSelf( uvB, 0.5 );
- uvM2 = uvC.clone();
- uvM2.lerpSelf( uvD, 0.5 );
- uvsQuadA = [ uvA.clone(), uvM1.clone(), uvM2.clone(), uvD.clone() ];
- uvsQuadB = [ uvM1.clone(), uvB.clone(), uvC.clone(), uvM2.clone() ];
- // BC + AD
- } else {
- uvM1 = uvB.clone();
- uvM1.lerpSelf( uvC, 0.5 );
- uvM2 = uvD.clone();
- uvM2.lerpSelf( uvA, 0.5 );
- uvsQuadA = [ uvA.clone(), uvB.clone(), uvM1.clone(), uvM2.clone() ];
- uvsQuadB = [ uvM2.clone(), uvM1.clone(), uvC.clone(), uvD.clone() ];
- }
- faceVertexUvs[ j ].push( uvsQuadA, uvsQuadB );
- }
- }
- } else {
- faces.push( face );
- for ( j = 0, jl = geometry.faceVertexUvs.length; j < jl; j ++ ) {
- faceVertexUvs[ j ].push( geometry.faceVertexUvs[ j ][ i ] );
- }
- }
- }
- }
- geometry.faces = faces;
- geometry.faceVertexUvs = faceVertexUvs;
- }
- };
- THREE.GeometryUtils.random = THREE.Math.random16;
- THREE.GeometryUtils.__v1 = new THREE.Vector3();
- /**
- * @author alteredq / http://alteredqualia.com/
- * @author mrdoob / http://mrdoob.com/
- */
- THREE.ImageUtils = {
- crossOrigin: 'anonymous',
- loadTexture: function ( url, mapping, onLoad, onError ) {
- var texture = new THREE.Texture( undefined, mapping );
- var loader = new THREE.ImageLoader();
- loader.addEventListener( 'load', function ( event ) {
- texture.image = event.content;
- texture.needsUpdate = true;
- if ( onLoad ) onLoad( texture );
- } );
- loader.addEventListener( 'error', function ( event ) {
- if ( onError ) onError( event.message );
- } );
- loader.crossOrigin = this.crossOrigin;
- loader.load( url );
- return texture;
- },
- loadTextureCube: function ( array, mapping, onLoad ) {
- var i, l, images = [];
- var texture = new THREE.Texture( images, mapping );
- texture.flipY = false;
- images.loadCount = 0;
- for ( i = 0, l = array.length; i < l; ++ i ) {
- images[ i ] = new Image();
- images[ i ].onload = function () {
- images.loadCount += 1;
- if ( images.loadCount === 6 ) {
- texture.needsUpdate = true;
- if ( onLoad ) onLoad();
- }
- };
- images[ i ].crossOrigin = this.crossOrigin;
- images[ i ].src = array[ i ];
- }
- return texture;
- },
- getNormalMap: function ( image, depth ) {
- // Adapted from http://www.paulbrunt.co.uk/lab/heightnormal/
- var cross = function ( a, b ) {
- return [ a[ 1 ] * b[ 2 ] - a[ 2 ] * b[ 1 ], a[ 2 ] * b[ 0 ] - a[ 0 ] * b[ 2 ], a[ 0 ] * b[ 1 ] - a[ 1 ] * b[ 0 ] ];
- }
- var subtract = function ( a, b ) {
- return [ a[ 0 ] - b[ 0 ], a[ 1 ] - b[ 1 ], a[ 2 ] - b[ 2 ] ];
- }
- var normalize = function ( a ) {
- var l = Math.sqrt( a[ 0 ] * a[ 0 ] + a[ 1 ] * a[ 1 ] + a[ 2 ] * a[ 2 ] );
- return [ a[ 0 ] / l, a[ 1 ] / l, a[ 2 ] / l ];
- }
- depth = depth | 1;
- var width = image.width;
- var height = image.height;
- var canvas = document.createElement( 'canvas' );
- canvas.width = width;
- canvas.height = height;
- var context = canvas.getContext( '2d' );
- context.drawImage( image, 0, 0 );
- var data = context.getImageData( 0, 0, width, height ).data;
- var imageData = context.createImageData( width, height );
- var output = imageData.data;
- for ( var x = 0; x < width; x ++ ) {
- for ( var y = 0; y < height; y ++ ) {
- var ly = y - 1 < 0 ? 0 : y - 1;
- var uy = y + 1 > height - 1 ? height - 1 : y + 1;
- var lx = x - 1 < 0 ? 0 : x - 1;
- var ux = x + 1 > width - 1 ? width - 1 : x + 1;
- var points = [];
- var origin = [ 0, 0, data[ ( y * width + x ) * 4 ] / 255 * depth ];
- points.push( [ - 1, 0, data[ ( y * width + lx ) * 4 ] / 255 * depth ] );
- points.push( [ - 1, - 1, data[ ( ly * width + lx ) * 4 ] / 255 * depth ] );
- points.push( [ 0, - 1, data[ ( ly * width + x ) * 4 ] / 255 * depth ] );
- points.push( [ 1, - 1, data[ ( ly * width + ux ) * 4 ] / 255 * depth ] );
- points.push( [ 1, 0, data[ ( y * width + ux ) * 4 ] / 255 * depth ] );
- points.push( [ 1, 1, data[ ( uy * width + ux ) * 4 ] / 255 * depth ] );
- points.push( [ 0, 1, data[ ( uy * width + x ) * 4 ] / 255 * depth ] );
- points.push( [ - 1, 1, data[ ( uy * width + lx ) * 4 ] / 255 * depth ] );
- var normals = [];
- var num_points = points.length;
- for ( var i = 0; i < num_points; i ++ ) {
- var v1 = points[ i ];
- var v2 = points[ ( i + 1 ) % num_points ];
- v1 = subtract( v1, origin );
- v2 = subtract( v2, origin );
- normals.push( normalize( cross( v1, v2 ) ) );
- }
- var normal = [ 0, 0, 0 ];
- for ( var i = 0; i < normals.length; i ++ ) {
- normal[ 0 ] += normals[ i ][ 0 ];
- normal[ 1 ] += normals[ i ][ 1 ];
- normal[ 2 ] += normals[ i ][ 2 ];
- }
- normal[ 0 ] /= normals.length;
- normal[ 1 ] /= normals.length;
- normal[ 2 ] /= normals.length;
- var idx = ( y * width + x ) * 4;
- output[ idx ] = ( ( normal[ 0 ] + 1.0 ) / 2.0 * 255 ) | 0;
- output[ idx + 1 ] = ( ( normal[ 1 ] + 1.0 ) / 2.0 * 255 ) | 0;
- output[ idx + 2 ] = ( normal[ 2 ] * 255 ) | 0;
- output[ idx + 3 ] = 255;
- }
- }
- context.putImageData( imageData, 0, 0 );
- return canvas;
- },
- generateDataTexture: function ( width, height, color ) {
- var size = width * height;
- var data = new Uint8Array( 3 * size );
- var r = Math.floor( color.r * 255 );
- var g = Math.floor( color.g * 255 );
- var b = Math.floor( color.b * 255 );
- for ( var i = 0; i < size; i ++ ) {
- data[ i * 3 ] = r;
- data[ i * 3 + 1 ] = g;
- data[ i * 3 + 2 ] = b;
- }
- var texture = new THREE.DataTexture( data, width, height, THREE.RGBFormat );
- texture.needsUpdate = true;
- return texture;
- }
- };
- /**
- * @author alteredq / http://alteredqualia.com/
- */
- THREE.SceneUtils = {
- showHierarchy : function ( root, visible ) {
- THREE.SceneUtils.traverseHierarchy( root, function( node ) { node.visible = visible; } );
- },
- traverseHierarchy : function ( root, callback ) {
- var n, i, l = root.children.length;
- for ( i = 0; i < l; i ++ ) {
- n = root.children[ i ];
- callback( n );
- THREE.SceneUtils.traverseHierarchy( n, callback );
- }
- },
- createMultiMaterialObject : function ( geometry, materials ) {
- var i, il = materials.length,
- group = new THREE.Object3D();
- for ( i = 0; i < il; i ++ ) {
- var object = new THREE.Mesh( geometry, materials[ i ] );
- group.add( object );
- }
- return group;
- },
- cloneObject: function ( source ) {
- var object;
- // subclass specific properties
- // (must process in order from more specific subclasses to more abstract classes)
- if ( source instanceof THREE.MorphAnimMesh ) {
- object = new THREE.MorphAnimMesh( source.geometry, source.material );
- object.duration = source.duration;
- object.mirroredLoop = source.mirroredLoop;
- object.time = source.time;
- object.lastKeyframe = source.lastKeyframe;
- object.currentKeyframe = source.currentKeyframe;
- object.direction = source.direction;
- object.directionBackwards = source.directionBackwards;
- } else if ( source instanceof THREE.SkinnedMesh ) {
- object = new THREE.SkinnedMesh( source.geometry, source.material );
- } else if ( source instanceof THREE.Mesh ) {
- object = new THREE.Mesh( source.geometry, source.material );
- } else if ( source instanceof THREE.Line ) {
- object = new THREE.Line( source.geometry, source.material, source.type );
- } else if ( source instanceof THREE.Ribbon ) {
- object = new THREE.Ribbon( source.geometry, source.material );
- } else if ( source instanceof THREE.ParticleSystem ) {
- object = new THREE.ParticleSystem( source.geometry, source.material );
- object.sortParticles = source.sortParticles;
- } else if ( source instanceof THREE.Particle ) {
- object = new THREE.Particle( source.material );
- } else if ( source instanceof THREE.Sprite ) {
- object = new THREE.Sprite( {} );
- object.color.copy( source.color );
- object.map = source.map;
- object.blending = source.blending;
- object.useScreenCoordinates = source.useScreenCoordinates;
- object.mergeWith3D = source.mergeWith3D;
- object.affectedByDistance = source.affectedByDistance;
- object.scaleByViewport = source.scaleByViewport;
- object.alignment = source.alignment;
- object.rotation3d.copy( source.rotation3d );
- object.rotation = source.rotation;
- object.opacity = source.opacity;
- object.uvOffset.copy( source.uvOffset );
- object.uvScale.copy( source.uvScale);
- } else if ( source instanceof THREE.LOD ) {
- object = new THREE.LOD();
- /*
- } else if ( source instanceof THREE.MarchingCubes ) {
- object = new THREE.MarchingCubes( source.resolution, source.material );
- object.field.set( source.field );
- object.isolation = source.isolation;
- */
- } else if ( source instanceof THREE.Object3D ) {
- object = new THREE.Object3D();
- }
- // base class properties
- object.name = source.name;
- object.parent = source.parent;
- object.up.copy( source.up );
- object.position.copy( source.position );
- // because of Sprite madness
- if ( object.rotation instanceof THREE.Vector3 )
- object.rotation.copy( source.rotation );
- object.eulerOrder = source.eulerOrder;
- object.scale.copy( source.scale );
- object.dynamic = source.dynamic;
- object.doubleSided = source.doubleSided;
- object.flipSided = source.flipSided;
- object.renderDepth = source.renderDepth;
- object.rotationAutoUpdate = source.rotationAutoUpdate;
- object.matrix.copy( source.matrix );
- object.matrixWorld.copy( source.matrixWorld );
- object.matrixRotationWorld.copy( source.matrixRotationWorld );
- object.matrixAutoUpdate = source.matrixAutoUpdate;
- object.matrixWorldNeedsUpdate = source.matrixWorldNeedsUpdate;
- object.quaternion.copy( source.quaternion );
- object.useQuaternion = source.useQuaternion;
- object.boundRadius = source.boundRadius;
- object.boundRadiusScale = source.boundRadiusScale;
- object.visible = source.visible;
- object.castShadow = source.castShadow;
- object.receiveShadow = source.receiveShadow;
- object.frustumCulled = source.frustumCulled;
- // children
- for ( var i = 0; i < source.children.length; i ++ ) {
- var child = THREE.SceneUtils.cloneObject( source.children[ i ] );
- object.children[ i ] = child;
- child.parent = object;
- }
- // LODs need to be patched separately to use cloned children
- if ( source instanceof THREE.LOD ) {
- for ( var i = 0; i < source.LODs.length; i ++ ) {
- var lod = source.LODs[ i ];
- object.LODs[ i ] = { visibleAtDistance: lod.visibleAtDistance, object3D: object.children[ i ] };
- }
- }
- return object;
- },
- detach : function ( child, parent, scene ) {
- child.applyMatrix( parent.matrixWorld );
- parent.remove( child );
- scene.add( child );
- },
- attach: function ( child, scene, parent ) {
- var matrixWorldInverse = new THREE.Matrix4();
- matrixWorldInverse.getInverse( parent.matrixWorld );
- child.applyMatrix( matrixWorldInverse );
- scene.remove( child );
- parent.add( child );
- }
- };
- /**
- * @author alteredq / http://alteredqualia.com/
- * @author mr.doob / http://mrdoob.com/
- *
- * ShaderUtils currently contains:
- *
- * fresnel
- * normal
- * cube
- *
- */
- if ( THREE.WebGLRenderer ) {
- THREE.ShaderUtils = {
- lib: {
- /* -------------------------------------------------------------------------
- // Fresnel shader
- // - based on Nvidia Cg tutorial
- ------------------------------------------------------------------------- */
- 'fresnel': {
- uniforms: {
- "mRefractionRatio": { type: "f", value: 1.02 },
- "mFresnelBias": { type: "f", value: 0.1 },
- "mFresnelPower": { type: "f", value: 2.0 },
- "mFresnelScale": { type: "f", value: 1.0 },
- "tCube": { type: "t", value: 1, texture: null }
- },
- fragmentShader: [
- "uniform samplerCube tCube;",
- "varying vec3 vReflect;",
- "varying vec3 vRefract[3];",
- "varying float vReflectionFactor;",
- "void main() {",
- "vec4 reflectedColor = textureCube( tCube, vec3( -vReflect.x, vReflect.yz ) );",
- "vec4 refractedColor = vec4( 1.0, 1.0, 1.0, 1.0 );",
- "refractedColor.r = textureCube( tCube, vec3( -vRefract[0].x, vRefract[0].yz ) ).r;",
- "refractedColor.g = textureCube( tCube, vec3( -vRefract[1].x, vRefract[1].yz ) ).g;",
- "refractedColor.b = textureCube( tCube, vec3( -vRefract[2].x, vRefract[2].yz ) ).b;",
- "refractedColor.a = 1.0;",
- "gl_FragColor = mix( refractedColor, reflectedColor, clamp( vReflectionFactor, 0.0, 1.0 ) );",
- "}"
- ].join("\n"),
- vertexShader: [
- "uniform float mRefractionRatio;",
- "uniform float mFresnelBias;",
- "uniform float mFresnelScale;",
- "uniform float mFresnelPower;",
- "varying vec3 vReflect;",
- "varying vec3 vRefract[3];",
- "varying float vReflectionFactor;",
- "void main() {",
- "vec4 mvPosition = modelViewMatrix * vec4( position, 1.0 );",
- "vec4 mPosition = objectMatrix * vec4( position, 1.0 );",
- "vec3 nWorld = normalize ( mat3( objectMatrix[0].xyz, objectMatrix[1].xyz, objectMatrix[2].xyz ) * normal );",
- "vec3 I = mPosition.xyz - cameraPosition;",
- "vReflect = reflect( I, nWorld );",
- "vRefract[0] = refract( normalize( I ), nWorld, mRefractionRatio );",
- "vRefract[1] = refract( normalize( I ), nWorld, mRefractionRatio * 0.99 );",
- "vRefract[2] = refract( normalize( I ), nWorld, mRefractionRatio * 0.98 );",
- "vReflectionFactor = mFresnelBias + mFresnelScale * pow( 1.0 + dot( normalize( I ), nWorld ), mFresnelPower );",
- "gl_Position = projectionMatrix * mvPosition;",
- "}"
- ].join("\n")
- },
- /* -------------------------------------------------------------------------
- // Normal map shader
- // - Blinn-Phong
- // - normal + diffuse + specular + AO + displacement + reflection + shadow maps
- // - point and directional lights (use with "lights: true" material option)
- ------------------------------------------------------------------------- */
- 'normal' : {
- uniforms: THREE.UniformsUtils.merge( [
- THREE.UniformsLib[ "fog" ],
- THREE.UniformsLib[ "lights" ],
- THREE.UniformsLib[ "shadowmap" ],
- {
- "enableAO" : { type: "i", value: 0 },
- "enableDiffuse" : { type: "i", value: 0 },
- "enableSpecular" : { type: "i", value: 0 },
- "enableReflection": { type: "i", value: 0 },
- "tDiffuse" : { type: "t", value: 0, texture: null },
- "tCube" : { type: "t", value: 1, texture: null },
- "tNormal" : { type: "t", value: 2, texture: null },
- "tSpecular" : { type: "t", value: 3, texture: null },
- "tAO" : { type: "t", value: 4, texture: null },
- "tDisplacement": { type: "t", value: 5, texture: null },
- "uNormalScale": { type: "f", value: 1.0 },
- "uDisplacementBias": { type: "f", value: 0.0 },
- "uDisplacementScale": { type: "f", value: 1.0 },
- "uDiffuseColor": { type: "c", value: new THREE.Color( 0xffffff ) },
- "uSpecularColor": { type: "c", value: new THREE.Color( 0x111111 ) },
- "uAmbientColor": { type: "c", value: new THREE.Color( 0xffffff ) },
- "uShininess": { type: "f", value: 30 },
- "uOpacity": { type: "f", value: 1 },
- "uReflectivity": { type: "f", value: 0.5 },
- "uOffset" : { type: "v2", value: new THREE.Vector2( 0, 0 ) },
- "uRepeat" : { type: "v2", value: new THREE.Vector2( 1, 1 ) },
- "wrapRGB" : { type: "v3", value: new THREE.Vector3( 1, 1, 1 ) }
- }
- ] ),
- fragmentShader: [
- "uniform vec3 uAmbientColor;",
- "uniform vec3 uDiffuseColor;",
- "uniform vec3 uSpecularColor;",
- "uniform float uShininess;",
- "uniform float uOpacity;",
- "uniform bool enableDiffuse;",
- "uniform bool enableSpecular;",
- "uniform bool enableAO;",
- "uniform bool enableReflection;",
- "uniform sampler2D tDiffuse;",
- "uniform sampler2D tNormal;",
- "uniform sampler2D tSpecular;",
- "uniform sampler2D tAO;",
- "uniform samplerCube tCube;",
- "uniform float uNormalScale;",
- "uniform float uReflectivity;",
- "varying vec3 vTangent;",
- "varying vec3 vBinormal;",
- "varying vec3 vNormal;",
- "varying vec2 vUv;",
- "uniform vec3 ambientLightColor;",
- "#if MAX_DIR_LIGHTS > 0",
- "uniform vec3 directionalLightColor[ MAX_DIR_LIGHTS ];",
- "uniform vec3 directionalLightDirection[ MAX_DIR_LIGHTS ];",
- "#endif",
- "#if MAX_POINT_LIGHTS > 0",
- "uniform vec3 pointLightColor[ MAX_POINT_LIGHTS ];",
- "varying vec4 vPointLight[ MAX_POINT_LIGHTS ];",
- "#endif",
- "#if MAX_SPOT_LIGHTS > 0",
- "uniform vec3 spotLightColor[ MAX_SPOT_LIGHTS ];",
- "uniform vec3 spotLightPosition[ MAX_SPOT_LIGHTS ];",
- "uniform vec3 spotLightDirection[ MAX_SPOT_LIGHTS ];",
- "uniform float spotLightAngle[ MAX_SPOT_LIGHTS ];",
- "uniform float spotLightExponent[ MAX_SPOT_LIGHTS ];",
- "varying vec4 vSpotLight[ MAX_SPOT_LIGHTS ];",
- "varying vec3 vWorldPosition;",
- "#endif",
- "#ifdef WRAP_AROUND",
- "uniform vec3 wrapRGB;",
- "#endif",
- "varying vec3 vViewPosition;",
- THREE.ShaderChunk[ "shadowmap_pars_fragment" ],
- THREE.ShaderChunk[ "fog_pars_fragment" ],
- "void main() {",
- "gl_FragColor = vec4( vec3( 1.0 ), uOpacity );",
- "vec3 specularTex = vec3( 1.0 );",
- "vec3 normalTex = texture2D( tNormal, vUv ).xyz * 2.0 - 1.0;",
- "normalTex.xy *= uNormalScale;",
- "normalTex = normalize( normalTex );",
- "if( enableDiffuse ) {",
- "#ifdef GAMMA_INPUT",
- "vec4 texelColor = texture2D( tDiffuse, vUv );",
- "texelColor.xyz *= texelColor.xyz;",
- "gl_FragColor = gl_FragColor * texelColor;",
- "#else",
- "gl_FragColor = gl_FragColor * texture2D( tDiffuse, vUv );",
- "#endif",
- "}",
- "if( enableAO ) {",
- "#ifdef GAMMA_INPUT",
- "vec4 aoColor = texture2D( tAO, vUv );",
- "aoColor.xyz *= aoColor.xyz;",
- "gl_FragColor.xyz = gl_FragColor.xyz * aoColor.xyz;",
- "#else",
- "gl_FragColor.xyz = gl_FragColor.xyz * texture2D( tAO, vUv ).xyz;",
- "#endif",
- "}",
- "if( enableSpecular )",
- "specularTex = texture2D( tSpecular, vUv ).xyz;",
- "mat3 tsb = mat3( normalize( vTangent ), normalize( vBinormal ), normalize( vNormal ) );",
- "vec3 finalNormal = tsb * normalTex;",
- "vec3 normal = normalize( finalNormal );",
- "vec3 viewPosition = normalize( vViewPosition );",
- // point lights
- "#if MAX_POINT_LIGHTS > 0",
- "vec3 pointDiffuse = vec3( 0.0 );",
- "vec3 pointSpecular = vec3( 0.0 );",
- "for ( int i = 0; i < MAX_POINT_LIGHTS; i ++ ) {",
- "vec3 pointVector = normalize( vPointLight[ i ].xyz );",
- "float pointDistance = vPointLight[ i ].w;",
- // diffuse
- "#ifdef WRAP_AROUND",
- "float pointDiffuseWeightFull = max( dot( normal, pointVector ), 0.0 );",
- "float pointDiffuseWeightHalf = max( 0.5 * dot( normal, pointVector ) + 0.5, 0.0 );",
- "vec3 pointDiffuseWeight = mix( vec3 ( pointDiffuseWeightFull ), vec3( pointDiffuseWeightHalf ), wrapRGB );",
- "#else",
- "float pointDiffuseWeight = max( dot( normal, pointVector ), 0.0 );",
- "#endif",
- "pointDiffuse += pointDistance * pointLightColor[ i ] * uDiffuseColor * pointDiffuseWeight;",
- // specular
- "vec3 pointHalfVector = normalize( pointVector + viewPosition );",
- "float pointDotNormalHalf = max( dot( normal, pointHalfVector ), 0.0 );",
- "float pointSpecularWeight = specularTex.r * max( pow( pointDotNormalHalf, uShininess ), 0.0 );",
- "#ifdef PHYSICALLY_BASED_SHADING",
- // 2.0 => 2.0001 is hack to work around ANGLE bug
- "float specularNormalization = ( uShininess + 2.0001 ) / 8.0;",
- "vec3 schlick = uSpecularColor + vec3( 1.0 - uSpecularColor ) * pow( 1.0 - dot( pointVector, pointHalfVector ), 5.0 );",
- "pointSpecular += schlick * pointLightColor[ i ] * pointSpecularWeight * pointDiffuseWeight * pointDistance * specularNormalization;",
- "#else",
- "pointSpecular += pointDistance * pointLightColor[ i ] * uSpecularColor * pointSpecularWeight * pointDiffuseWeight;",
- "#endif",
- "}",
- "#endif",
- // spot lights
- "#if MAX_SPOT_LIGHTS > 0",
- "vec3 spotDiffuse = vec3( 0.0 );",
- "vec3 spotSpecular = vec3( 0.0 );",
- "for ( int i = 0; i < MAX_SPOT_LIGHTS; i ++ ) {",
- "vec3 spotVector = normalize( vSpotLight[ i ].xyz );",
- "float spotDistance = vSpotLight[ i ].w;",
- "float spotEffect = dot( spotLightDirection[ i ], normalize( spotLightPosition[ i ] - vWorldPosition ) );",
- "if ( spotEffect > spotLightAngle[ i ] ) {",
- "spotEffect = pow( spotEffect, spotLightExponent[ i ] );",
- // diffuse
- "#ifdef WRAP_AROUND",
- "float spotDiffuseWeightFull = max( dot( normal, spotVector ), 0.0 );",
- "float spotDiffuseWeightHalf = max( 0.5 * dot( normal, spotVector ) + 0.5, 0.0 );",
- "vec3 spotDiffuseWeight = mix( vec3 ( spotDiffuseWeightFull ), vec3( spotDiffuseWeightHalf ), wrapRGB );",
- "#else",
- "float spotDiffuseWeight = max( dot( normal, spotVector ), 0.0 );",
- "#endif",
- "spotDiffuse += spotDistance * spotLightColor[ i ] * uDiffuseColor * spotDiffuseWeight * spotEffect;",
- // specular
- "vec3 spotHalfVector = normalize( spotVector + viewPosition );",
- "float spotDotNormalHalf = max( dot( normal, spotHalfVector ), 0.0 );",
- "float spotSpecularWeight = specularTex.r * max( pow( spotDotNormalHalf, uShininess ), 0.0 );",
- "#ifdef PHYSICALLY_BASED_SHADING",
- // 2.0 => 2.0001 is hack to work around ANGLE bug
- "float specularNormalization = ( uShininess + 2.0001 ) / 8.0;",
- "vec3 schlick = uSpecularColor + vec3( 1.0 - uSpecularColor ) * pow( 1.0 - dot( spotVector, spotHalfVector ), 5.0 );",
- "spotSpecular += schlick * spotLightColor[ i ] * spotSpecularWeight * spotDiffuseWeight * spotDistance * specularNormalization * spotEffect;",
- "#else",
- "spotSpecular += spotDistance * spotLightColor[ i ] * uSpecularColor * spotSpecularWeight * spotDiffuseWeight * spotEffect;",
- "#endif",
- "}",
- "}",
- "#endif",
- // directional lights
- "#if MAX_DIR_LIGHTS > 0",
- "vec3 dirDiffuse = vec3( 0.0 );",
- "vec3 dirSpecular = vec3( 0.0 );",
- "for( int i = 0; i < MAX_DIR_LIGHTS; i++ ) {",
- "vec4 lDirection = viewMatrix * vec4( directionalLightDirection[ i ], 0.0 );",
- "vec3 dirVector = normalize( lDirection.xyz );",
- // diffuse
- "#ifdef WRAP_AROUND",
- "float directionalLightWeightingFull = max( dot( normal, dirVector ), 0.0 );",
- "float directionalLightWeightingHalf = max( 0.5 * dot( normal, dirVector ) + 0.5, 0.0 );",
- "vec3 dirDiffuseWeight = mix( vec3( directionalLightWeightingFull ), vec3( directionalLightWeightingHalf ), wrapRGB );",
- "#else",
- "float dirDiffuseWeight = max( dot( normal, dirVector ), 0.0 );",
- "#endif",
- "dirDiffuse += directionalLightColor[ i ] * uDiffuseColor * dirDiffuseWeight;",
- // specular
- "vec3 dirHalfVector = normalize( dirVector + viewPosition );",
- "float dirDotNormalHalf = max( dot( normal, dirHalfVector ), 0.0 );",
- "float dirSpecularWeight = specularTex.r * max( pow( dirDotNormalHalf, uShininess ), 0.0 );",
- "#ifdef PHYSICALLY_BASED_SHADING",
- // 2.0 => 2.0001 is hack to work around ANGLE bug
- "float specularNormalization = ( uShininess + 2.0001 ) / 8.0;",
- "vec3 schlick = uSpecularColor + vec3( 1.0 - uSpecularColor ) * pow( 1.0 - dot( dirVector, dirHalfVector ), 5.0 );",
- "dirSpecular += schlick * directionalLightColor[ i ] * dirSpecularWeight * dirDiffuseWeight * specularNormalization;",
- "#else",
- "dirSpecular += directionalLightColor[ i ] * uSpecularColor * dirSpecularWeight * dirDiffuseWeight;",
- "#endif",
- "}",
- "#endif",
- // all lights contribution summation
- "vec3 totalDiffuse = vec3( 0.0 );",
- "vec3 totalSpecular = vec3( 0.0 );",
- "#if MAX_DIR_LIGHTS > 0",
- "totalDiffuse += dirDiffuse;",
- "totalSpecular += dirSpecular;",
- "#endif",
- "#if MAX_POINT_LIGHTS > 0",
- "totalDiffuse += pointDiffuse;",
- "totalSpecular += pointSpecular;",
- "#endif",
- "#if MAX_SPOT_LIGHTS > 0",
- "totalDiffuse += spotDiffuse;",
- "totalSpecular += spotSpecular;",
- "#endif",
- "gl_FragColor.xyz = gl_FragColor.xyz * ( totalDiffuse + ambientLightColor * uAmbientColor) + totalSpecular;",
- "if ( enableReflection ) {",
- "vec3 wPos = cameraPosition - vViewPosition;",
- "vec3 vReflect = reflect( normalize( wPos ), normal );",
- "vec4 cubeColor = textureCube( tCube, vec3( -vReflect.x, vReflect.yz ) );",
- "#ifdef GAMMA_INPUT",
- "cubeColor.xyz *= cubeColor.xyz;",
- "#endif",
- "gl_FragColor.xyz = mix( gl_FragColor.xyz, cubeColor.xyz, specularTex.r * uReflectivity );",
- "}",
- THREE.ShaderChunk[ "shadowmap_fragment" ],
- THREE.ShaderChunk[ "linear_to_gamma_fragment" ],
- THREE.ShaderChunk[ "fog_fragment" ],
- "}"
- ].join("\n"),
- vertexShader: [
- "attribute vec4 tangent;",
- "uniform vec2 uOffset;",
- "uniform vec2 uRepeat;",
- "#ifdef VERTEX_TEXTURES",
- "uniform sampler2D tDisplacement;",
- "uniform float uDisplacementScale;",
- "uniform float uDisplacementBias;",
- "#endif",
- "varying vec3 vTangent;",
- "varying vec3 vBinormal;",
- "varying vec3 vNormal;",
- "varying vec2 vUv;",
- "#if MAX_POINT_LIGHTS > 0",
- "uniform vec3 pointLightPosition[ MAX_POINT_LIGHTS ];",
- "uniform float pointLightDistance[ MAX_POINT_LIGHTS ];",
- "varying vec4 vPointLight[ MAX_POINT_LIGHTS ];",
- "#endif",
- "#if MAX_SPOT_LIGHTS > 0",
- "uniform vec3 spotLightPosition[ MAX_SPOT_LIGHTS ];",
- "uniform float spotLightDistance[ MAX_SPOT_LIGHTS ];",
- "varying vec4 vSpotLight[ MAX_SPOT_LIGHTS ];",
- "varying vec3 vWorldPosition;",
- "#endif",
- "varying vec3 vViewPosition;",
- THREE.ShaderChunk[ "shadowmap_pars_vertex" ],
- "void main() {",
- "vec4 mvPosition = modelViewMatrix * vec4( position, 1.0 );",
- "vViewPosition = -mvPosition.xyz;",
- // normal, tangent and binormal vectors
- "vNormal = normalMatrix * normal;",
- "vTangent = normalMatrix * tangent.xyz;",
- "vBinormal = cross( vNormal, vTangent ) * tangent.w;",
- "vUv = uv * uRepeat + uOffset;",
- // point lights
- "#if MAX_POINT_LIGHTS > 0",
- "for( int i = 0; i < MAX_POINT_LIGHTS; i++ ) {",
- "vec4 lPosition = viewMatrix * vec4( pointLightPosition[ i ], 1.0 );",
- "vec3 lVector = lPosition.xyz - mvPosition.xyz;",
- "float lDistance = 1.0;",
- "if ( pointLightDistance[ i ] > 0.0 )",
- "lDistance = 1.0 - min( ( length( lVector ) / pointLightDistance[ i ] ), 1.0 );",
- "lVector = normalize( lVector );",
- "vPointLight[ i ] = vec4( lVector, lDistance );",
- "}",
- "#endif",
- // spot lights
- "#if MAX_SPOT_LIGHTS > 0",
- "for( int i = 0; i < MAX_SPOT_LIGHTS; i ++ ) {",
- "vec4 lPosition = viewMatrix * vec4( spotLightPosition[ i ], 1.0 );",
- "vec3 lVector = lPosition.xyz - mvPosition.xyz;",
- "float lDistance = 1.0;",
- "if ( spotLightDistance[ i ] > 0.0 )",
- "lDistance = 1.0 - min( ( length( lVector ) / spotLightDistance[ i ] ), 1.0 );",
- "vSpotLight[ i ] = vec4( lVector, lDistance );",
- "}",
- "vec4 mPosition = objectMatrix * vec4( position, 1.0 );",
- "vWorldPosition = mPosition.xyz;",
- "#endif",
- // displacement mapping
- "#ifdef VERTEX_TEXTURES",
- "vec3 dv = texture2D( tDisplacement, uv ).xyz;",
- "float df = uDisplacementScale * dv.x + uDisplacementBias;",
- "vec4 displacedPosition = vec4( normalize( vNormal.xyz ) * df, 0.0 ) + mvPosition;",
- "gl_Position = projectionMatrix * displacedPosition;",
- "#else",
- "gl_Position = projectionMatrix * mvPosition;",
- "#endif",
- THREE.ShaderChunk[ "shadowmap_vertex" ],
- "}"
- ].join("\n")
- },
- /* -------------------------------------------------------------------------
- // Cube map shader
- ------------------------------------------------------------------------- */
- 'cube': {
- uniforms: { "tCube": { type: "t", value: 1, texture: null },
- "tFlip": { type: "f", value: -1 } },
- vertexShader: [
- "varying vec3 vViewPosition;",
- "void main() {",
- "vec4 mPosition = objectMatrix * vec4( position, 1.0 );",
- "vViewPosition = cameraPosition - mPosition.xyz;",
- "gl_Position = projectionMatrix * modelViewMatrix * vec4( position, 1.0 );",
- "}"
- ].join("\n"),
- fragmentShader: [
- "uniform samplerCube tCube;",
- "uniform float tFlip;",
- "varying vec3 vViewPosition;",
- "void main() {",
- "vec3 wPos = cameraPosition - vViewPosition;",
- "gl_FragColor = textureCube( tCube, vec3( tFlip * wPos.x, wPos.yz ) );",
- "}"
- ].join("\n")
- }
- }
- };
- };/**
- * @author zz85 / http://www.lab4games.net/zz85/blog
- * @author alteredq / http://alteredqualia.com/
- *
- * For Text operations in three.js (See TextGeometry)
- *
- * It uses techniques used in:
- *
- * typeface.js and canvastext
- * For converting fonts and rendering with javascript
- * http://typeface.neocracy.org
- *
- * Triangulation ported from AS3
- * Simple Polygon Triangulation
- * http://actionsnippet.com/?p=1462
- *
- * A Method to triangulate shapes with holes
- * http://www.sakri.net/blog/2009/06/12/an-approach-to-triangulating-polygons-with-holes/
- *
- */
- THREE.FontUtils = {
- faces : {},
- // Just for now. face[weight][style]
- face : "helvetiker",
- weight: "normal",
- style : "normal",
- size : 150,
- divisions : 10,
- getFace : function() {
- return this.faces[ this.face ][ this.weight ][ this.style ];
- },
- loadFace : function( data ) {
- var family = data.familyName.toLowerCase();
- var ThreeFont = this;
- ThreeFont.faces[ family ] = ThreeFont.faces[ family ] || {};
- ThreeFont.faces[ family ][ data.cssFontWeight ] = ThreeFont.faces[ family ][ data.cssFontWeight ] || {};
- ThreeFont.faces[ family ][ data.cssFontWeight ][ data.cssFontStyle ] = data;
- var face = ThreeFont.faces[ family ][ data.cssFontWeight ][ data.cssFontStyle ] = data;
- return data;
- },
- drawText : function( text ) {
- var characterPts = [], allPts = [];
- // RenderText
- var i, p,
- face = this.getFace(),
- scale = this.size / face.resolution,
- offset = 0,
- chars = String( text ).split( '' ),
- length = chars.length;
- var fontPaths = [];
- for ( i = 0; i < length; i ++ ) {
- var path = new THREE.Path();
- var ret = this.extractGlyphPoints( chars[ i ], face, scale, offset, path );
- offset += ret.offset;
- fontPaths.push( ret.path );
- }
- // get the width
- var width = offset / 2;
- //
- // for ( p = 0; p < allPts.length; p++ ) {
- //
- // allPts[ p ].x -= width;
- //
- // }
- //var extract = this.extractPoints( allPts, characterPts );
- //extract.contour = allPts;
- //extract.paths = fontPaths;
- //extract.offset = width;
- return { paths : fontPaths, offset : width };
- },
- extractGlyphPoints : function( c, face, scale, offset, path ) {
- var pts = [];
- var i, i2, divisions,
- outline, action, length,
- scaleX, scaleY,
- x, y, cpx, cpy, cpx0, cpy0, cpx1, cpy1, cpx2, cpy2,
- laste,
- glyph = face.glyphs[ c ] || face.glyphs[ '?' ];
- if ( !glyph ) return;
- if ( glyph.o ) {
- outline = glyph._cachedOutline || ( glyph._cachedOutline = glyph.o.split( ' ' ) );
- length = outline.length;
- scaleX = scale;
- scaleY = scale;
- for ( i = 0; i < length; ) {
- action = outline[ i ++ ];
- //console.log( action );
- switch( action ) {
- case 'm':
- // Move To
- x = outline[ i++ ] * scaleX + offset;
- y = outline[ i++ ] * scaleY;
- path.moveTo( x, y );
- break;
- case 'l':
- // Line To
- x = outline[ i++ ] * scaleX + offset;
- y = outline[ i++ ] * scaleY;
- path.lineTo(x,y);
- break;
- case 'q':
- // QuadraticCurveTo
- cpx = outline[ i++ ] * scaleX + offset;
- cpy = outline[ i++ ] * scaleY;
- cpx1 = outline[ i++ ] * scaleX + offset;
- cpy1 = outline[ i++ ] * scaleY;
- path.quadraticCurveTo(cpx1, cpy1, cpx, cpy);
- laste = pts[ pts.length - 1 ];
- if ( laste ) {
- cpx0 = laste.x;
- cpy0 = laste.y;
- for ( i2 = 1, divisions = this.divisions; i2 <= divisions; i2 ++ ) {
- var t = i2 / divisions;
- var tx = THREE.Shape.Utils.b2( t, cpx0, cpx1, cpx );
- var ty = THREE.Shape.Utils.b2( t, cpy0, cpy1, cpy );
- }
- }
- break;
- case 'b':
- // Cubic Bezier Curve
- cpx = outline[ i++ ] * scaleX + offset;
- cpy = outline[ i++ ] * scaleY;
- cpx1 = outline[ i++ ] * scaleX + offset;
- cpy1 = outline[ i++ ] * -scaleY;
- cpx2 = outline[ i++ ] * scaleX + offset;
- cpy2 = outline[ i++ ] * -scaleY;
- path.bezierCurveTo( cpx, cpy, cpx1, cpy1, cpx2, cpy2 );
- laste = pts[ pts.length - 1 ];
- if ( laste ) {
- cpx0 = laste.x;
- cpy0 = laste.y;
- for ( i2 = 1, divisions = this.divisions; i2 <= divisions; i2 ++ ) {
- var t = i2 / divisions;
- var tx = THREE.Shape.Utils.b3( t, cpx0, cpx1, cpx2, cpx );
- var ty = THREE.Shape.Utils.b3( t, cpy0, cpy1, cpy2, cpy );
- }
- }
- break;
- }
- }
- }
- return { offset: glyph.ha*scale, path:path};
- }
- };
- THREE.FontUtils.generateShapes = function( text, parameters ) {
- // Parameters
- parameters = parameters || {};
- var size = parameters.size !== undefined ? parameters.size : 100;
- var curveSegments = parameters.curveSegments !== undefined ? parameters.curveSegments: 4;
- var font = parameters.font !== undefined ? parameters.font : "helvetiker";
- var weight = parameters.weight !== undefined ? parameters.weight : "normal";
- var style = parameters.style !== undefined ? parameters.style : "normal";
- THREE.FontUtils.size = size;
- THREE.FontUtils.divisions = curveSegments;
- THREE.FontUtils.face = font;
- THREE.FontUtils.weight = weight;
- THREE.FontUtils.style = style;
- // Get a Font data json object
- var data = THREE.FontUtils.drawText( text );
- var paths = data.paths;
- var shapes = [];
- for ( var p = 0, pl = paths.length; p < pl; p ++ ) {
- Array.prototype.push.apply( shapes, paths[ p ].toShapes() );
- }
- return shapes;
- };
- /**
- * This code is a quick port of code written in C++ which was submitted to
- * flipcode.com by John W. Ratcliff // July 22, 2000
- * See original code and more information here:
- * http://www.flipcode.com/archives/Efficient_Polygon_Triangulation.shtml
- *
- * ported to actionscript by Zevan Rosser
- * www.actionsnippet.com
- *
- * ported to javascript by Joshua Koo
- * http://www.lab4games.net/zz85/blog
- *
- */
- ( function( namespace ) {
- var EPSILON = 0.0000000001;
- // takes in an contour array and returns
- var process = function( contour, indices ) {
- var n = contour.length;
- if ( n < 3 ) return null;
- var result = [],
- verts = [],
- vertIndices = [];
- /* we want a counter-clockwise polygon in verts */
- var u, v, w;
- if ( area( contour ) > 0.0 ) {
- for ( v = 0; v < n; v++ ) verts[ v ] = v;
- } else {
- for ( v = 0; v < n; v++ ) verts[ v ] = ( n - 1 ) - v;
- }
- var nv = n;
- /* remove nv - 2 vertices, creating 1 triangle every time */
- var count = 2 * nv; /* error detection */
- for( v = nv - 1; nv > 2; ) {
- /* if we loop, it is probably a non-simple polygon */
- if ( ( count-- ) <= 0 ) {
- //** Triangulate: ERROR - probable bad polygon!
- //throw ( "Warning, unable to triangulate polygon!" );
- //return null;
- // Sometimes warning is fine, especially polygons are triangulated in reverse.
- console.log( "Warning, unable to triangulate polygon!" );
- if ( indices ) return vertIndices;
- return result;
- }
- /* three consecutive vertices in current polygon, <u,v,w> */
- u = v; if ( nv <= u ) u = 0; /* previous */
- v = u + 1; if ( nv <= v ) v = 0; /* new v */
- w = v + 1; if ( nv <= w ) w = 0; /* next */
- if ( snip( contour, u, v, w, nv, verts ) ) {
- var a, b, c, s, t;
- /* true names of the vertices */
- a = verts[ u ];
- b = verts[ v ];
- c = verts[ w ];
- /* output Triangle */
- /*
- result.push( contour[ a ] );
- result.push( contour[ b ] );
- result.push( contour[ c ] );
- */
- result.push( [ contour[ a ],
- contour[ b ],
- contour[ c ] ] );
- vertIndices.push( [ verts[ u ], verts[ v ], verts[ w ] ] );
- /* remove v from the remaining polygon */
- for( s = v, t = v + 1; t < nv; s++, t++ ) {
- verts[ s ] = verts[ t ];
- }
- nv--;
- /* reset error detection counter */
- count = 2 * nv;
- }
- }
- if ( indices ) return vertIndices;
- return result;
- };
- // calculate area of the contour polygon
- var area = function ( contour ) {
- var n = contour.length;
- var a = 0.0;
- for( var 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;
- };
- // see if p is inside triangle abc
- var insideTriangle = function( ax, ay,
- bx, by,
- cx, cy,
- px, py ) {
- var aX, aY, bX, bY;
- var cX, cY, apx, apy;
- var bpx, bpy, cpx, cpy;
- var cCROSSap, bCROSScp, aCROSSbp;
- aX = cx - bx; aY = cy - by;
- bX = ax - cx; bY = ay - cy;
- cX = bx - ax; cY = by - ay;
- apx= px -ax; apy= py - ay;
- bpx= px - bx; bpy= py - by;
- cpx= px - cx; cpy= py - cy;
- aCROSSbp = aX*bpy - aY*bpx;
- cCROSSap = cX*apy - cY*apx;
- bCROSScp = bX*cpy - bY*cpx;
- return ( (aCROSSbp >= 0.0) && (bCROSScp >= 0.0) && (cCROSSap >= 0.0) );
- };
- var snip = function ( contour, u, v, w, n, verts ) {
- var p;
- var ax, ay, bx, by;
- var cx, cy, px, py;
- ax = contour[ verts[ u ] ].x;
- ay = contour[ verts[ u ] ].y;
- bx = contour[ verts[ v ] ].x;
- by = contour[ verts[ v ] ].y;
- cx = contour[ verts[ w ] ].x;
- cy = contour[ verts[ w ] ].y;
- if ( EPSILON > (((bx-ax)*(cy-ay)) - ((by-ay)*(cx-ax))) ) return false;
- for ( p = 0; p < n; p++ ) {
- if( (p == u) || (p == v) || (p == w) ) continue;
- px = contour[ verts[ p ] ].x
- py = contour[ verts[ p ] ].y
- if ( insideTriangle( ax, ay, bx, by, cx, cy, px, py ) ) return false;
- }
- return true;
- };
- namespace.Triangulate = process;
- namespace.Triangulate.area = area;
- return namespace;
- })(THREE.FontUtils);
- // To use the typeface.js face files, hook up the API
- self._typeface_js = { faces: THREE.FontUtils.faces, loadFace: THREE.FontUtils.loadFace };/**
- * @author alteredq / http://alteredqualia.com/
- */
- THREE.BufferGeometry = function () {
- this.id = THREE.GeometryCount ++;
- // attributes
- this.attributes = {};
- // attributes typed arrays are kept only if dynamic flag is set
- this.dynamic = false;
- // boundings
- this.boundingBox = null;
- this.boundingSphere = null;
- this.hasTangents = false;
- // for compatibility
- this.morphTargets = [];
- };
- THREE.BufferGeometry.prototype = {
- constructor : THREE.BufferGeometry,
- applyMatrix: function ( matrix ) {
- var positionArray;
- var normalArray;
- if ( this.attributes[ "position" ] ) positionArray = this.attributes[ "position" ].array;
- if ( this.attributes[ "normal" ] ) normalArray = this.attributes[ "normal" ].array;
- if ( positionArray !== undefined ) {
- matrix.multiplyVector3Array( positionArray );
- this.verticesNeedUpdate = true;
- }
- if ( normalArray !== undefined ) {
- var matrixRotation = new THREE.Matrix4();
- matrixRotation.extractRotation( matrix );
- matrixRotation.multiplyVector3Array( normalArray );
- this.normalsNeedUpdate = true;
- }
- },
- computeBoundingBox: function () {
- if ( ! this.boundingBox ) {
- this.boundingBox = {
- min: new THREE.Vector3( Infinity, Infinity, Infinity ),
- max: new THREE.Vector3( -Infinity, -Infinity, -Infinity )
- };
- }
- var positions = this.attributes[ "position" ].array;
- if ( positions ) {
- var bb = this.boundingBox;
- var x, y, z;
- for ( var i = 0, il = positions.length; i < il; i += 3 ) {
- x = positions[ i ];
- y = positions[ i + 1 ];
- z = positions[ i + 2 ];
- // bounding box
- if ( x < bb.min.x ) {
- bb.min.x = x;
- } else if ( x > bb.max.x ) {
- bb.max.x = x;
- }
- if ( y < bb.min.y ) {
- bb.min.y = y;
- } else if ( y > bb.max.y ) {
- bb.max.y = y;
- }
- if ( z < bb.min.z ) {
- bb.min.z = z;
- } else if ( z > bb.max.z ) {
- bb.max.z = z;
- }
- }
- }
- if ( positions === undefined || positions.length === 0 ) {
- this.boundingBox.min.set( 0, 0, 0 );
- this.boundingBox.max.set( 0, 0, 0 );
- }
- },
- computeBoundingSphere: function () {
- if ( ! this.boundingSphere ) this.boundingSphere = { radius: 0 };
- var positions = this.attributes[ "position" ].array;
- if ( positions ) {
- var radius, maxRadius = 0;
- var x, y, z;
- for ( var i = 0, il = positions.length; i < il; i += 3 ) {
- x = positions[ i ];
- y = positions[ i + 1 ];
- z = positions[ i + 2 ];
- radius = Math.sqrt( x * x + y * y + z * z );
- if ( radius > maxRadius ) maxRadius = radius;
- }
- this.boundingSphere.radius = maxRadius;
- }
- },
- computeVertexNormals: function () {
- if ( this.attributes[ "position" ] && this.attributes[ "index" ] ) {
- var i, il;
- var j, jl;
- var nVertexElements = this.attributes[ "position" ].array.length;
- if ( this.attributes[ "normal" ] === undefined ) {
- this.attributes[ "normal" ] = {
- itemSize: 3,
- array: new Float32Array( nVertexElements ),
- numItems: nVertexElements
- };
- } else {
- // reset existing normals to zero
- for ( i = 0, il = this.attributes[ "normal" ].array.length; i < il; i ++ ) {
- this.attributes[ "normal" ].array[ i ] = 0;
- }
- }
- var offsets = this.offsets;
- var indices = this.attributes[ "index" ].array;
- var positions = this.attributes[ "position" ].array;
- var normals = this.attributes[ "normal" ].array;
- var vA, vB, vC, x, y, z,
- pA = new THREE.Vector3(),
- pB = new THREE.Vector3(),
- pC = new THREE.Vector3(),
- cb = new THREE.Vector3(),
- ab = new THREE.Vector3();
- for ( j = 0, jl = offsets.length; j < jl; ++ j ) {
- var start = offsets[ j ].start;
- var count = offsets[ j ].count;
- var index = offsets[ j ].index;
- for ( i = start, il = start + count; i < il; i += 3 ) {
- vA = index + indices[ i ];
- vB = index + indices[ i + 1 ];
- vC = index + indices[ i + 2 ];
- x = positions[ vA * 3 ];
- y = positions[ vA * 3 + 1 ];
- z = positions[ vA * 3 + 2 ];
- pA.set( x, y, z );
- x = positions[ vB * 3 ];
- y = positions[ vB * 3 + 1 ];
- z = positions[ vB * 3 + 2 ];
- pB.set( x, y, z );
- x = positions[ vC * 3 ];
- y = positions[ vC * 3 + 1 ];
- z = positions[ vC * 3 + 2 ];
- pC.set( x, y, z );
- cb.sub( pC, pB );
- ab.sub( pA, pB );
- cb.crossSelf( ab );
- normals[ vA * 3 ] += cb.x;
- normals[ vA * 3 + 1 ] += cb.y;
- normals[ vA * 3 + 2 ] += cb.z;
- normals[ vB * 3 ] += cb.x;
- normals[ vB * 3 + 1 ] += cb.y;
- normals[ vB * 3 + 2 ] += cb.z;
- normals[ vC * 3 ] += cb.x;
- normals[ vC * 3 + 1 ] += cb.y;
- normals[ vC * 3 + 2 ] += cb.z;
- }
- }
- // normalize normals
- for ( i = 0, il = normals.length; i < il; i += 3 ) {
- x = normals[ i ];
- y = normals[ i + 1 ];
- z = normals[ i + 2 ];
- var n = 1.0 / Math.sqrt( x * x + y * y + z * z );
- normals[ i ] *= n;
- normals[ i + 1 ] *= n;
- normals[ i + 2 ] *= n;
- }
- this.normalsNeedUpdate = true;
- }
- },
- computeTangents: function () {
- // based on http://www.terathon.com/code/tangent.html
- // (per vertex tangents)
- if ( this.attributes[ "index" ] === undefined ||
- this.attributes[ "position" ] === undefined ||
- this.attributes[ "normal" ] === undefined ||
- this.attributes[ "uv" ] === undefined ) {
- console.warn( "Missing required attributes (index, position, normal or uv) in BufferGeometry.computeTangents()" );
- return;
- }
- var indices = this.attributes[ "index" ].array;
- var positions = this.attributes[ "position" ].array;
- var normals = this.attributes[ "normal" ].array;
- var uvs = this.attributes[ "uv" ].array;
- var nVertices = positions.length / 3;
- if ( this.attributes[ "tangent" ] === undefined ) {
- var nTangentElements = 4 * nVertices;
- this.attributes[ "tangent" ] = {
- itemSize: 4,
- array: new Float32Array( nTangentElements ),
- numItems: nTangentElements
- };
- }
- var tangents = this.attributes[ "tangent" ].array;
- var tan1 = [], tan2 = [];
- for ( var k = 0; k < nVertices; k ++ ) {
- tan1[ k ] = new THREE.Vector3();
- tan2[ k ] = new THREE.Vector3();
- }
- var xA, yA, zA,
- xB, yB, zB,
- xC, yC, zC,
- uA, vA,
- uB, vB,
- uC, vC,
- x1, x2, y1, y2, z1, z2,
- s1, s2, t1, t2, r;
- var sdir = new THREE.Vector3(), tdir = new THREE.Vector3();
- function handleTriangle( a, b, c ) {
- xA = positions[ a * 3 ];
- yA = positions[ a * 3 + 1 ];
- zA = positions[ a * 3 + 2 ];
- xB = positions[ b * 3 ];
- yB = positions[ b * 3 + 1 ];
- zB = positions[ b * 3 + 2 ];
- xC = positions[ c * 3 ];
- yC = positions[ c * 3 + 1 ];
- zC = positions[ c * 3 + 2 ];
- uA = uvs[ a * 2 ];
- vA = uvs[ a * 2 + 1 ];
- uB = uvs[ b * 2 ];
- vB = uvs[ b * 2 + 1 ];
- uC = uvs[ c * 2 ];
- vC = uvs[ c * 2 + 1 ];
- x1 = xB - xA;
- x2 = xC - xA;
- y1 = yB - yA;
- y2 = yC - yA;
- z1 = zB - zA;
- z2 = zC - zA;
- s1 = uB - uA;
- s2 = uC - uA;
- t1 = vB - vA;
- t2 = vC - vA;
- r = 1.0 / ( s1 * t2 - s2 * t1 );
- sdir.set( ( t2 * x1 - t1 * x2 ) * r,
- ( t2 * y1 - t1 * y2 ) * r,
- ( t2 * z1 - t1 * z2 ) * r );
- tdir.set( ( s1 * x2 - s2 * x1 ) * r,
- ( s1 * y2 - s2 * y1 ) * r,
- ( s1 * z2 - s2 * z1 ) * r );
- tan1[ a ].addSelf( sdir );
- tan1[ b ].addSelf( sdir );
- tan1[ c ].addSelf( sdir );
- tan2[ a ].addSelf( tdir );
- tan2[ b ].addSelf( tdir );
- tan2[ c ].addSelf( tdir );
- }
- var i, il;
- var j, jl;
- var iA, iB, iC;
- var offsets = this.offsets;
- for ( j = 0, jl = offsets.length; j < jl; ++ j ) {
- var start = offsets[ j ].start;
- var count = offsets[ j ].count;
- var index = offsets[ j ].index;
- for ( i = start, il = start + count; i < il; i += 3 ) {
- iA = index + indices[ i ];
- iB = index + indices[ i + 1 ];
- iC = index + indices[ i + 2 ];
- handleTriangle( iA, iB, iC );
- }
- }
- var tmp = new THREE.Vector3(), tmp2 = new THREE.Vector3();
- var n = new THREE.Vector3(), n2 = new THREE.Vector3();
- var w, t, test;
- var nx, ny, nz;
- function handleVertex( v ) {
- n.x = normals[ v * 3 ];
- n.y = normals[ v * 3 + 1 ];
- n.z = normals[ v * 3 + 2 ];
- n2.copy( n );
- t = tan1[ v ];
- // Gram-Schmidt orthogonalize
- tmp.copy( t );
- tmp.subSelf( n.multiplyScalar( n.dot( t ) ) ).normalize();
- // Calculate handedness
- tmp2.cross( n2, t );
- test = tmp2.dot( tan2[ v ] );
- w = ( test < 0.0 ) ? -1.0 : 1.0;
- tangents[ v * 4 ] = tmp.x;
- tangents[ v * 4 + 1 ] = tmp.y;
- tangents[ v * 4 + 2 ] = tmp.z;
- tangents[ v * 4 + 3 ] = w;
- }
- for ( j = 0, jl = offsets.length; j < jl; ++ j ) {
- var start = offsets[ j ].start;
- var count = offsets[ j ].count;
- var index = offsets[ j ].index;
- for ( i = start, il = start + count; i < il; i += 3 ) {
- iA = index + indices[ i ];
- iB = index + indices[ i + 1 ];
- iC = index + indices[ i + 2 ];
- handleVertex( iA );
- handleVertex( iB );
- handleVertex( iC );
- }
- }
- this.hasTangents = true;
- this.tangentsNeedUpdate = true;
- }
- };
- /**
- * @author zz85 / http://www.lab4games.net/zz85/blog
- * Extensible curve object
- *
- * Some common of Curve methods
- * .getPoint(t), getTangent(t)
- * .getPointAt(u), getTagentAt(u)
- * .getPoints(), .getSpacedPoints()
- * .getLength()
- * .updateArcLengths()
- *
- * This file contains following classes:
- *
- * -- 2d classes --
- * THREE.Curve
- * THREE.LineCurve
- * THREE.QuadraticBezierCurve
- * THREE.CubicBezierCurve
- * THREE.SplineCurve
- * THREE.ArcCurve
- *
- * -- 3d classes --
- * THREE.LineCurve3
- * THREE.QuadraticBezierCurve3
- * THREE.CubicBezierCurve3
- * THREE.SplineCurve3
- * THREE.ClosedSplineCurve3
- *
- * A series of curves can be represented as a THREE.CurvePath
- *
- **/
- /**************************************************************
- * Abstract Curve base class
- **************************************************************/
- THREE.Curve = function () {
- };
- // Virtual base class method to overwrite and implement in subclasses
- // - t [0 .. 1]
- THREE.Curve.prototype.getPoint = function ( t ) {
- console.log( "Warning, getPoint() not implemented!" );
- return null;
- };
- // Get point at relative position in curve according to arc length
- // - u [0 .. 1]
- THREE.Curve.prototype.getPointAt = function ( u ) {
- var t = this.getUtoTmapping( u );
- return this.getPoint( t );
- };
- // Get sequence of points using getPoint( t )
- THREE.Curve.prototype.getPoints = function ( divisions ) {
- if ( !divisions ) divisions = 5;
- var d, pts = [];
- for ( d = 0; d <= divisions; d ++ ) {
- pts.push( this.getPoint( d / divisions ) );
- }
- return pts;
- };
- // Get sequence of points using getPointAt( u )
- THREE.Curve.prototype.getSpacedPoints = function ( divisions ) {
- if ( !divisions ) divisions = 5;
- var d, pts = [];
- for ( d = 0; d <= divisions; d ++ ) {
- pts.push( this.getPointAt( d / divisions ) );
- }
- return pts;
- };
- // Get total curve arc length
- THREE.Curve.prototype.getLength = function () {
- var lengths = this.getLengths();
- return lengths[ lengths.length - 1 ];
- };
- // Get list of cumulative segment lengths
- THREE.Curve.prototype.getLengths = function ( divisions ) {
- if ( !divisions ) divisions = (this.__arcLengthDivisions) ? (this.__arcLengthDivisions): 200;
- if ( this.cacheArcLengths
- && ( this.cacheArcLengths.length == divisions + 1 )
- && !this.needsUpdate) {
- //console.log( "cached", this.cacheArcLengths );
- return this.cacheArcLengths;
- }
- this.needsUpdate = false;
- var cache = [];
- var current, last = this.getPoint( 0 );
- var p, sum = 0;
- cache.push( 0 );
- for ( 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.
- };
- THREE.Curve.prototype.updateArcLengths = function() {
- this.needsUpdate = true;
- this.getLengths();
- };
- // Given u ( 0 .. 1 ), get a t to find p. This gives you points which are equi distance
- THREE.Curve.prototype.getUtoTmapping = function ( u, distance ) {
- var arcLengths = this.getLengths();
- var i = 0, il = arcLengths.length;
- var targetArcLength; // The targeted u distance value to get
- if ( distance ) {
- targetArcLength = distance;
- } else {
- targetArcLength = u * arcLengths[ il - 1 ];
- }
- //var time = Date.now();
- // binary search for the index with largest value smaller than target u distance
- var 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;
- continue;
- } else if ( comparison > 0 ) {
- high = i - 1;
- continue;
- } else {
- high = i;
- break;
- // DONE
- }
- }
- i = high;
- //console.log('b' , i, low, high, Date.now()- time);
- if ( arcLengths[ i ] == targetArcLength ) {
- var t = i / ( il - 1 );
- return t;
- }
- // we could get finer grain at lengths, or use simple interpolatation between two points
- var lengthBefore = arcLengths[ i ];
- var lengthAfter = arcLengths[ i + 1 ];
- var segmentLength = lengthAfter - lengthBefore;
- // determine where we are between the 'before' and 'after' points
- var segmentFraction = ( targetArcLength - lengthBefore ) / segmentLength;
- // add that fractional amount to t
- var t = ( i + segmentFraction ) / ( il -1 );
- return t;
- };
- // In 2D space, there are actually 2 normal vectors,
- // and in 3D space, infinte
- // TODO this should be depreciated.
- THREE.Curve.prototype.getNormalVector = function( t ) {
- var vec = this.getTangent( t );
- return new THREE.Vector2( -vec.y , vec.x );
- };
- // Returns a unit vector tangent at t
- // In case any sub curve does not implement its tangent / normal finding,
- // we get 2 points with a small delta and find a gradient of the 2 points
- // which seems to make a reasonable approximation
- THREE.Curve.prototype.getTangent = function( t ) {
- var delta = 0.0001;
- var t1 = t - delta;
- var t2 = t + delta;
- // Capping in case of danger
- if ( t1 < 0 ) t1 = 0;
- if ( t2 > 1 ) t2 = 1;
- var pt1 = this.getPoint( t1 );
- var pt2 = this.getPoint( t2 );
-
- var vec = pt2.clone().subSelf(pt1);
- return vec.normalize();
- };
- THREE.Curve.prototype.getTangentAt = function ( u ) {
- var t = this.getUtoTmapping( u );
- return this.getTangent( t );
- };
- /**************************************************************
- * Line
- **************************************************************/
- THREE.LineCurve = function ( v1, v2 ) {
- this.v1 = v1;
- this.v2 = v2;
- };
- THREE.LineCurve.prototype = Object.create( THREE.Curve.prototype );
- THREE.LineCurve.prototype.getPoint = function ( t ) {
- var point = this.v2.clone().subSelf(this.v1);
- point.multiplyScalar( t ).addSelf( this.v1 );
- return point;
- };
- // Line curve is linear, so we can overwrite default getPointAt
- THREE.LineCurve.prototype.getPointAt = function ( u ) {
- return this.getPoint( u );
- };
- THREE.LineCurve.prototype.getTangent = function( t ) {
- var tangent = this.v2.clone().subSelf(this.v1);
- return tangent.normalize();
- };
- /**************************************************************
- * Quadratic Bezier curve
- **************************************************************/
- THREE.QuadraticBezierCurve = function ( v0, v1, v2 ) {
- this.v0 = v0;
- this.v1 = v1;
- this.v2 = v2;
- };
- THREE.QuadraticBezierCurve.prototype = Object.create( THREE.Curve.prototype );
- THREE.QuadraticBezierCurve.prototype.getPoint = function ( t ) {
- var tx, ty;
- tx = THREE.Shape.Utils.b2( t, this.v0.x, this.v1.x, this.v2.x );
- ty = THREE.Shape.Utils.b2( t, this.v0.y, this.v1.y, this.v2.y );
- return new THREE.Vector2( tx, ty );
- };
- THREE.QuadraticBezierCurve.prototype.getTangent = function( t ) {
- var tx, ty;
- tx = THREE.Curve.Utils.tangentQuadraticBezier( t, this.v0.x, this.v1.x, this.v2.x );
- ty = THREE.Curve.Utils.tangentQuadraticBezier( t, this.v0.y, this.v1.y, this.v2.y );
- // returns unit vector
- var tangent = new THREE.Vector2( tx, ty );
- tangent.normalize();
- return tangent;
- };
- /**************************************************************
- * Cubic Bezier curve
- **************************************************************/
- THREE.CubicBezierCurve = function ( v0, v1, v2, v3 ) {
- this.v0 = v0;
- this.v1 = v1;
- this.v2 = v2;
- this.v3 = v3;
- };
- THREE.CubicBezierCurve.prototype = Object.create( THREE.Curve.prototype );
- THREE.CubicBezierCurve.prototype.getPoint = function ( t ) {
- var tx, ty;
- tx = THREE.Shape.Utils.b3( t, this.v0.x, this.v1.x, this.v2.x, this.v3.x );
- ty = THREE.Shape.Utils.b3( t, this.v0.y, this.v1.y, this.v2.y, this.v3.y );
- return new THREE.Vector2( tx, ty );
- };
- THREE.CubicBezierCurve.prototype.getTangent = function( t ) {
- var tx, ty;
- tx = THREE.Curve.Utils.tangentCubicBezier( t, this.v0.x, this.v1.x, this.v2.x, this.v3.x );
- ty = THREE.Curve.Utils.tangentCubicBezier( t, this.v0.y, this.v1.y, this.v2.y, this.v3.y );
- var tangent = new THREE.Vector2( tx, ty );
- tangent.normalize();
- return tangent;
- };
- /**************************************************************
- * Spline curve
- **************************************************************/
- THREE.SplineCurve = function ( points /* array of Vector2 */ ) {
- this.points = (points == undefined) ? [] : points;
- };
- THREE.SplineCurve.prototype = Object.create( THREE.Curve.prototype );
- THREE.SplineCurve.prototype.getPoint = function ( t ) {
- var v = new THREE.Vector2();
- var c = [];
- var points = this.points, point, intPoint, weight;
- point = ( points.length - 1 ) * t;
- intPoint = Math.floor( point );
- weight = point - intPoint;
- c[ 0 ] = intPoint == 0 ? intPoint : intPoint - 1;
- c[ 1 ] = intPoint;
- c[ 2 ] = intPoint > points.length - 2 ? points.length -1 : intPoint + 1;
- c[ 3 ] = intPoint > points.length - 3 ? points.length -1 : intPoint + 2;
- v.x = THREE.Curve.Utils.interpolate( points[ c[ 0 ] ].x, points[ c[ 1 ] ].x, points[ c[ 2 ] ].x, points[ c[ 3 ] ].x, weight );
- v.y = THREE.Curve.Utils.interpolate( points[ c[ 0 ] ].y, points[ c[ 1 ] ].y, points[ c[ 2 ] ].y, points[ c[ 3 ] ].y, weight );
- return v;
- };
- /**************************************************************
- * Ellipse curve
- **************************************************************/
- THREE.EllipseCurve = function ( aX, aY, xRadius, yRadius,
- aStartAngle, aEndAngle,
- aClockwise ) {
- this.aX = aX;
- this.aY = aY;
- this.xRadius = xRadius;
- this.yRadius = yRadius;
- this.aStartAngle = aStartAngle;
- this.aEndAngle = aEndAngle;
- this.aClockwise = aClockwise;
- };
- THREE.EllipseCurve.prototype = Object.create( THREE.Curve.prototype );
- THREE.EllipseCurve.prototype.getPoint = function ( t ) {
- var deltaAngle = this.aEndAngle - this.aStartAngle;
- if ( !this.aClockwise ) {
- t = 1 - t;
- }
- var angle = this.aStartAngle + t * deltaAngle;
- var tx = this.aX + this.xRadius * Math.cos( angle );
- var ty = this.aY + this.yRadius * Math.sin( angle );
- return new THREE.Vector2( tx, ty );
- };
- /**************************************************************
- * Arc curve
- **************************************************************/
- THREE.ArcCurve = function ( aX, aY, aRadius, aStartAngle, aEndAngle, aClockwise ) {
- THREE.EllipseCurve.call( this, aX, aY, aRadius, aRadius, aStartAngle, aEndAngle, aClockwise );
- };
- THREE.ArcCurve.prototype = Object.create( THREE.EllipseCurve.prototype );
- /**************************************************************
- * Utils
- **************************************************************/
- THREE.Curve.Utils = {
- tangentQuadraticBezier: function ( t, p0, p1, p2 ) {
- return 2 * ( 1 - t ) * ( p1 - p0 ) + 2 * t * ( p2 - p1 );
- },
- // Puay Bing, thanks for helping with this derivative!
- tangentCubicBezier: function (t, p0, p1, p2, p3 ) {
- return -3 * p0 * (1 - t) * (1 - t) +
- 3 * p1 * (1 - t) * (1-t) - 6 *t *p1 * (1-t) +
- 6 * t * p2 * (1-t) - 3 * t * t * p2 +
- 3 * t * t * p3;
- },
- tangentSpline: function ( t, p0, p1, p2, p3 ) {
- // To check if my formulas are correct
- var h00 = 6 * t * t - 6 * t; // derived from 2t^3 − 3t^2 + 1
- var h10 = 3 * t * t - 4 * t + 1; // t^3 − 2t^2 + t
- var h01 = -6 * t * t + 6 * t; // − 2t3 + 3t2
- var h11 = 3 * t * t - 2 * t; // t3 − t2
- return h00 + h10 + h01 + h11;
- },
- // Catmull-Rom
- interpolate: function( p0, p1, p2, p3, t ) {
- var v0 = ( p2 - p0 ) * 0.5;
- var v1 = ( p3 - p1 ) * 0.5;
- var t2 = t * t;
- var t3 = t * t2;
- return ( 2 * p1 - 2 * p2 + v0 + v1 ) * t3 + ( - 3 * p1 + 3 * p2 - 2 * v0 - v1 ) * t2 + v0 * t + p1;
- }
- };
- // TODO: Transformation for Curves?
- /**************************************************************
- * 3D Curves
- **************************************************************/
- // A Factory method for creating new curve subclasses
- THREE.Curve.create = function ( constructor, getPointFunc ) {
- constructor.prototype = Object.create( THREE.Curve.prototype );
- constructor.prototype.getPoint = getPointFunc;
- return constructor;
- };
- /**************************************************************
- * Line3D
- **************************************************************/
- THREE.LineCurve3 = THREE.Curve.create(
- function ( v1, v2 ) {
- this.v1 = v1;
- this.v2 = v2;
- },
- function ( t ) {
- var r = new THREE.Vector3();
- r.sub( this.v2, this.v1 ); // diff
- r.multiplyScalar( t );
- r.addSelf( this.v1 );
- return r;
- }
- );
- /**************************************************************
- * Quadratic Bezier 3D curve
- **************************************************************/
- THREE.QuadraticBezierCurve3 = THREE.Curve.create(
- function ( v0, v1, v2 ) {
- this.v0 = v0;
- this.v1 = v1;
- this.v2 = v2;
- },
- function ( t ) {
- var tx, ty, tz;
- tx = THREE.Shape.Utils.b2( t, this.v0.x, this.v1.x, this.v2.x );
- ty = THREE.Shape.Utils.b2( t, this.v0.y, this.v1.y, this.v2.y );
- tz = THREE.Shape.Utils.b2( t, this.v0.z, this.v1.z, this.v2.z );
- return new THREE.Vector3( tx, ty, tz );
- }
- );
- /**************************************************************
- * Cubic Bezier 3D curve
- **************************************************************/
- THREE.CubicBezierCurve3 = THREE.Curve.create(
- function ( v0, v1, v2, v3 ) {
- this.v0 = v0;
- this.v1 = v1;
- this.v2 = v2;
- this.v3 = v3;
- },
- function ( t ) {
- var tx, ty, tz;
- tx = THREE.Shape.Utils.b3( t, this.v0.x, this.v1.x, this.v2.x, this.v3.x );
- ty = THREE.Shape.Utils.b3( t, this.v0.y, this.v1.y, this.v2.y, this.v3.y );
- tz = THREE.Shape.Utils.b3( t, this.v0.z, this.v1.z, this.v2.z, this.v3.z );
- return new THREE.Vector3( tx, ty, tz );
- }
- );
- /**************************************************************
- * Spline 3D curve
- **************************************************************/
- THREE.SplineCurve3 = THREE.Curve.create(
- function ( points /* array of Vector3 */) {
- this.points = (points == undefined) ? [] : points;
- },
- function ( t ) {
- var v = new THREE.Vector3();
- var c = [];
- var points = this.points, point, intPoint, weight;
- point = ( points.length - 1 ) * t;
- intPoint = Math.floor( point );
- weight = point - intPoint;
- c[ 0 ] = intPoint == 0 ? intPoint : intPoint - 1;
- c[ 1 ] = intPoint;
- c[ 2 ] = intPoint > points.length - 2 ? points.length - 1 : intPoint + 1;
- c[ 3 ] = intPoint > points.length - 3 ? points.length - 1 : intPoint + 2;
- var pt0 = points[ c[0] ],
- pt1 = points[ c[1] ],
- pt2 = points[ c[2] ],
- pt3 = points[ c[3] ];
- v.x = THREE.Curve.Utils.interpolate(pt0.x, pt1.x, pt2.x, pt3.x, weight);
- v.y = THREE.Curve.Utils.interpolate(pt0.y, pt1.y, pt2.y, pt3.y, weight);
- v.z = THREE.Curve.Utils.interpolate(pt0.z, pt1.z, pt2.z, pt3.z, weight);
- return v;
- }
- );
- // THREE.SplineCurve3.prototype.getTangent = function(t) {
- // var v = new THREE.Vector3();
- // var c = [];
- // var points = this.points, point, intPoint, weight;
- // point = ( points.length - 1 ) * t;
- // intPoint = Math.floor( point );
- // weight = point - intPoint;
- // c[ 0 ] = intPoint == 0 ? intPoint : intPoint - 1;
- // c[ 1 ] = intPoint;
- // c[ 2 ] = intPoint > points.length - 2 ? points.length - 1 : intPoint + 1;
- // c[ 3 ] = intPoint > points.length - 3 ? points.length - 1 : intPoint + 2;
- // var pt0 = points[ c[0] ],
- // pt1 = points[ c[1] ],
- // pt2 = points[ c[2] ],
- // pt3 = points[ c[3] ];
- // // t = weight;
- // v.x = THREE.Curve.Utils.tangentSpline( t, pt0.x, pt1.x, pt2.x, pt3.x );
- // v.y = THREE.Curve.Utils.tangentSpline( t, pt0.y, pt1.y, pt2.y, pt3.y );
- // v.z = THREE.Curve.Utils.tangentSpline( t, pt0.z, pt1.z, pt2.z, pt3.z );
- // return v;
-
- // }
- /**************************************************************
- * Closed Spline 3D curve
- **************************************************************/
- THREE.ClosedSplineCurve3 = THREE.Curve.create(
- function ( points /* array of Vector3 */) {
- this.points = (points == undefined) ? [] : points;
- },
- function ( t ) {
- var v = new THREE.Vector3();
- var c = [];
- var points = this.points, point, intPoint, weight;
- point = ( points.length - 0 ) * t;
- // This needs to be from 0-length +1
- intPoint = Math.floor( point );
- weight = point - intPoint;
-
- intPoint += intPoint > 0 ? 0 : ( Math.floor( Math.abs( intPoint ) / points.length ) + 1 ) * points.length;
- c[ 0 ] = ( intPoint - 1 ) % points.length;
- c[ 1 ] = ( intPoint ) % points.length;
- c[ 2 ] = ( intPoint + 1 ) % points.length;
- c[ 3 ] = ( intPoint + 2 ) % points.length;
- v.x = THREE.Curve.Utils.interpolate( points[ c[ 0 ] ].x, points[ c[ 1 ] ].x, points[ c[ 2 ] ].x, points[ c[ 3 ] ].x, weight );
- v.y = THREE.Curve.Utils.interpolate( points[ c[ 0 ] ].y, points[ c[ 1 ] ].y, points[ c[ 2 ] ].y, points[ c[ 3 ] ].y, weight );
- v.z = THREE.Curve.Utils.interpolate( points[ c[ 0 ] ].z, points[ c[ 1 ] ].z, points[ c[ 2 ] ].z, points[ c[ 3 ] ].z, weight );
-
- return v;
- }
- );
- /**
- * @author zz85 / http://www.lab4games.net/zz85/blog
- *
- **/
- /**************************************************************
- * Curved Path - a curve path is simply a array of connected
- * curves, but retains the api of a curve
- **************************************************************/
- THREE.CurvePath = function () {
- this.curves = [];
- this.bends = [];
-
- this.autoClose = false; // Automatically closes the path
- };
- THREE.CurvePath.prototype = Object.create( THREE.Curve.prototype );
- THREE.CurvePath.prototype.add = function ( curve ) {
- this.curves.push( curve );
- };
- THREE.CurvePath.prototype.checkConnection = function() {
- // TODO
- // If the ending of curve is not connected to the starting
- // or the next curve, then, this is not a real path
- };
- THREE.CurvePath.prototype.closePath = function() {
- // TODO Test
- // and verify for vector3 (needs to implement equals)
- // Add a line curve if start and end of lines are not connected
- var startPoint = this.curves[0].getPoint(0);
- var endPoint = this.curves[this.curves.length-1].getPoint(1);
-
- if (!startPoint.equals(endPoint)) {
- this.curves.push( new THREE.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')
- THREE.CurvePath.prototype.getPoint = function( t ) {
- var d = t * this.getLength();
- var curveLengths = this.getCurveLengths();
- var i = 0, diff, curve;
- // To think about boundaries points.
- while ( i < curveLengths.length ) {
- if ( curveLengths[ i ] >= d ) {
- diff = curveLengths[ i ] - d;
- curve = this.curves[ i ];
- var u = 1 - diff / curve.getLength();
- return curve.getPointAt( u );
- break;
- }
- i ++;
- }
- return null;
- // loop where sum != 0, sum > d , sum+1 <d
- };
- /*
- THREE.CurvePath.prototype.getTangent = function( t ) {
- };*/
- // 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
- THREE.CurvePath.prototype.getLength = function() {
- var lens = this.getCurveLengths();
- return lens[ lens.length - 1 ];
- };
- // Compute lengths and cache them
- // We cannot overwrite getLengths() because UtoT mapping uses it.
- THREE.CurvePath.prototype.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 subsurve
- // Push sums into cached array
- var lengths = [], sums = 0;
- var i, il = this.curves.length;
- for ( i = 0; i < il; i ++ ) {
- sums += this.curves[ i ].getLength();
- lengths.push( sums );
- }
- this.cacheLengths = lengths;
- return lengths;
- };
- // Returns min and max coordinates, as well as centroid
- THREE.CurvePath.prototype.getBoundingBox = function () {
- var points = this.getPoints();
- var maxX, maxY;
- var minX, minY;
- maxX = maxY = Number.NEGATIVE_INFINITY;
- minX = minY = Number.POSITIVE_INFINITY;
- var p, i, il, sum;
- sum = new THREE.Vector2();
- for ( i = 0, il = points.length; i < il; i ++ ) {
- p = points[ i ];
- if ( p.x > maxX ) maxX = p.x;
- else if ( p.x < minX ) minX = p.x;
- if ( p.y > maxY ) maxY = p.y;
- else if ( p.y < minY ) minY = p.y;
- sum.addSelf( p.x, p.y );
- }
- return {
- minX: minX,
- minY: minY,
- maxX: maxX,
- maxY: maxY,
- centroid: sum.divideScalar( il )
- };
- };
- /**************************************************************
- * Create Geometries Helpers
- **************************************************************/
- /// Generate geometry from path points (for Line or ParticleSystem objects)
- THREE.CurvePath.prototype.createPointsGeometry = function( divisions ) {
- var pts = this.getPoints( divisions, true );
- return this.createGeometry( pts );
- };
- // Generate geometry from equidistance sampling along the path
- THREE.CurvePath.prototype.createSpacedPointsGeometry = function( divisions ) {
- var pts = this.getSpacedPoints( divisions, true );
- return this.createGeometry( pts );
- };
- THREE.CurvePath.prototype.createGeometry = function( points ) {
- var geometry = new THREE.Geometry();
- for ( var i = 0; i < points.length; i ++ ) {
- geometry.vertices.push( new THREE.Vector3( points[ i ].x, points[ i ].y, 0 ) );
- }
- return geometry;
- };
- /**************************************************************
- * Bend / Wrap Helper Methods
- **************************************************************/
- // Wrap path / Bend modifiers?
- THREE.CurvePath.prototype.addWrapPath = function ( bendpath ) {
- this.bends.push( bendpath );
- };
- THREE.CurvePath.prototype.getTransformedPoints = function( segments, bends ) {
- var oldPts = this.getPoints( segments ); // getPoints getSpacedPoints
- var i, il;
- if ( !bends ) {
- bends = this.bends;
- }
- for ( i = 0, il = bends.length; i < il; i ++ ) {
- oldPts = this.getWrapPoints( oldPts, bends[ i ] );
- }
- return oldPts;
- };
- THREE.CurvePath.prototype.getTransformedSpacedPoints = function( segments, bends ) {
- var oldPts = this.getSpacedPoints( segments );
- var i, il;
- if ( !bends ) {
- bends = this.bends;
- }
- for ( i = 0, il = bends.length; i < il; i ++ ) {
- oldPts = this.getWrapPoints( oldPts, bends[ i ] );
- }
- return oldPts;
- };
- // This returns getPoints() bend/wrapped around the contour of a path.
- // Read http://www.planetclegg.com/projects/WarpingTextToSplines.html
- THREE.CurvePath.prototype.getWrapPoints = function ( oldPts, path ) {
- var bounds = this.getBoundingBox();
- var i, il, p, oldX, oldY, xNorm;
- for ( i = 0, il = oldPts.length; i < il; i ++ ) {
- p = oldPts[ i ];
- oldX = p.x;
- oldY = p.y;
- xNorm = oldX / bounds.maxX;
- // If using actual distance, for length > path, requires line extrusions
- //xNorm = path.getUtoTmapping(xNorm, oldX); // 3 styles. 1) wrap stretched. 2) wrap stretch by arc length 3) warp by actual distance
- xNorm = path.getUtoTmapping( xNorm, oldX );
- // check for out of bounds?
- var pathPt = path.getPoint( xNorm );
- var normal = path.getNormalVector( xNorm ).multiplyScalar( oldY );
- p.x = pathPt.x + normal.x;
- p.y = pathPt.y + normal.y;
- }
- return oldPts;
- };
- /**
- * @author alteredq / http://alteredqualia.com/
- */
- THREE.Gyroscope = function () {
- THREE.Object3D.call( this );
- };
- THREE.Gyroscope.prototype = Object.create( THREE.Object3D.prototype );
- THREE.Gyroscope.prototype.updateMatrixWorld = function ( force ) {
- this.matrixAutoUpdate && this.updateMatrix();
- // update matrixWorld
- if ( this.matrixWorldNeedsUpdate || force ) {
- if ( this.parent ) {
- this.matrixWorld.multiply( this.parent.matrixWorld, this.matrix );
- this.matrixWorld.decompose( this.translationWorld, this.rotationWorld, this.scaleWorld );
- this.matrix.decompose( this.translationObject, this.rotationObject, this.scaleObject );
- this.matrixWorld.compose( this.translationWorld, this.rotationObject, this.scaleWorld );
- } else {
- this.matrixWorld.copy( this.matrix );
- }
- this.matrixWorldNeedsUpdate = false;
- force = true;
- }
- // update children
- for ( var i = 0, l = this.children.length; i < l; i ++ ) {
- this.children[ i ].updateMatrixWorld( force );
- }
- };
- THREE.Gyroscope.prototype.translationWorld = new THREE.Vector3();
- THREE.Gyroscope.prototype.translationObject = new THREE.Vector3();
- THREE.Gyroscope.prototype.rotationWorld = new THREE.Quaternion();
- THREE.Gyroscope.prototype.rotationObject = new THREE.Quaternion();
- THREE.Gyroscope.prototype.scaleWorld = new THREE.Vector3();
- THREE.Gyroscope.prototype.scaleObject = new THREE.Vector3();
- /**
- * @author zz85 / http://www.lab4games.net/zz85/blog
- * Creates free form 2d path using series of points, lines or curves.
- *
- **/
- THREE.Path = function ( points ) {
- THREE.CurvePath.call(this);
- this.actions = [];
- if ( points ) {
- this.fromPoints( points );
- }
- };
- THREE.Path.prototype = Object.create( THREE.CurvePath.prototype );
- THREE.PathActions = {
- MOVE_TO: 'moveTo',
- LINE_TO: 'lineTo',
- QUADRATIC_CURVE_TO: 'quadraticCurveTo', // Bezier quadratic curve
- BEZIER_CURVE_TO: 'bezierCurveTo', // Bezier cubic curve
- CSPLINE_THRU: 'splineThru', // Catmull-rom spline
- ARC: 'arc', // Circle
- ELLIPSE: 'ellipse'
- };
- // TODO Clean up PATH API
- // Create path using straight lines to connect all points
- // - vectors: array of Vector2
- THREE.Path.prototype.fromPoints = function ( vectors ) {
- this.moveTo( vectors[ 0 ].x, vectors[ 0 ].y );
- for ( var v = 1, vlen = vectors.length; v < vlen; v ++ ) {
- this.lineTo( vectors[ v ].x, vectors[ v ].y );
- };
- };
- // startPath() endPath()?
- THREE.Path.prototype.moveTo = function ( x, y ) {
- var args = Array.prototype.slice.call( arguments );
- this.actions.push( { action: THREE.PathActions.MOVE_TO, args: args } );
- };
- THREE.Path.prototype.lineTo = function ( x, y ) {
- var args = Array.prototype.slice.call( arguments );
- var lastargs = this.actions[ this.actions.length - 1 ].args;
- var x0 = lastargs[ lastargs.length - 2 ];
- var y0 = lastargs[ lastargs.length - 1 ];
- var curve = new THREE.LineCurve( new THREE.Vector2( x0, y0 ), new THREE.Vector2( x, y ) );
- this.curves.push( curve );
- this.actions.push( { action: THREE.PathActions.LINE_TO, args: args } );
- };
- THREE.Path.prototype.quadraticCurveTo = function( aCPx, aCPy, aX, aY ) {
- var args = Array.prototype.slice.call( arguments );
- var lastargs = this.actions[ this.actions.length - 1 ].args;
- var x0 = lastargs[ lastargs.length - 2 ];
- var y0 = lastargs[ lastargs.length - 1 ];
- var curve = new THREE.QuadraticBezierCurve( new THREE.Vector2( x0, y0 ),
- new THREE.Vector2( aCPx, aCPy ),
- new THREE.Vector2( aX, aY ) );
- this.curves.push( curve );
- this.actions.push( { action: THREE.PathActions.QUADRATIC_CURVE_TO, args: args } );
- };
- THREE.Path.prototype.bezierCurveTo = function( aCP1x, aCP1y,
- aCP2x, aCP2y,
- aX, aY ) {
- var args = Array.prototype.slice.call( arguments );
- var lastargs = this.actions[ this.actions.length - 1 ].args;
- var x0 = lastargs[ lastargs.length - 2 ];
- var y0 = lastargs[ lastargs.length - 1 ];
- var curve = new THREE.CubicBezierCurve( new THREE.Vector2( x0, y0 ),
- new THREE.Vector2( aCP1x, aCP1y ),
- new THREE.Vector2( aCP2x, aCP2y ),
- new THREE.Vector2( aX, aY ) );
- this.curves.push( curve );
- this.actions.push( { action: THREE.PathActions.BEZIER_CURVE_TO, args: args } );
- };
- THREE.Path.prototype.splineThru = function( pts /*Array of Vector*/ ) {
- var args = Array.prototype.slice.call( arguments );
- var lastargs = this.actions[ this.actions.length - 1 ].args;
- var x0 = lastargs[ lastargs.length - 2 ];
- var y0 = lastargs[ lastargs.length - 1 ];
- //---
- var npts = [ new THREE.Vector2( x0, y0 ) ];
- Array.prototype.push.apply( npts, pts );
- var curve = new THREE.SplineCurve( npts );
- this.curves.push( curve );
- this.actions.push( { action: THREE.PathActions.CSPLINE_THRU, args: args } );
- };
- // FUTURE: Change the API or follow canvas API?
- THREE.Path.prototype.ellipse = function ( aX, aY, xRadius, yRadius,
- aStartAngle, aEndAngle, aClockwise ) {
- var laste = this.actions[ this.actions.length - 1];
- this.absellipse(laste.x + aX, laste.y + aY, xRadius, yRadius,
- aStartAngle, aEndAngle, aClockwise );
- };
-
- THREE.Path.prototype.arc = function ( aX, aY, aRadius,
- aStartAngle, aEndAngle, aClockwise ) {
- var laste = this.actions[ this.actions.length - 1];
- this.absarc(laste.x + aX, laste.y + aY, aRadius,
- aStartAngle, aEndAngle, aClockwise );
- };
-
- THREE.Path.prototype.absellipse = function ( aX, aY, xRadius, yRadius,
- aStartAngle, aEndAngle, aClockwise ) {
- var args = Array.prototype.slice.call( arguments );
- var curve = new THREE.EllipseCurve( aX, aY, xRadius, yRadius,
- aStartAngle, aEndAngle, aClockwise );
- this.curves.push( curve );
- // All of the other actions look to the last two elements in the list to
- // find the ending point, so we need to append them.
- var lastPoint = curve.getPoint(aClockwise ? 1 : 0);
- args.push(lastPoint.x);
- args.push(lastPoint.y);
- this.actions.push( { action: THREE.PathActions.ELLIPSE, args: args } );
- };
- THREE.Path.prototype.absarc = function ( aX, aY, aRadius,
- aStartAngle, aEndAngle, aClockwise ) {
- this.absellipse(aX, aY, aRadius, aRadius,
- aStartAngle, aEndAngle, aClockwise);
- };
- THREE.Path.prototype.getSpacedPoints = function ( divisions, closedPath ) {
- if ( ! divisions ) divisions = 40;
- var points = [];
- for ( var i = 0; i < divisions; i ++ ) {
- points.push( this.getPoint( i / divisions ) );
- //if( !this.getPoint( i / divisions ) ) throw "DIE";
- }
- // if ( closedPath ) {
- //
- // points.push( points[ 0 ] );
- //
- // }
- return points;
- };
- /* Return an array of vectors based on contour of the path */
- THREE.Path.prototype.getPoints = function( divisions, closedPath ) {
- if (this.useSpacedPoints) {
- console.log('tata');
- return this.getSpacedPoints( divisions, closedPath );
- }
- divisions = divisions || 12;
- var points = [];
- var i, il, item, action, args;
- var cpx, cpy, cpx2, cpy2, cpx1, cpy1, cpx0, cpy0,
- laste, j,
- t, tx, ty;
- for ( i = 0, il = this.actions.length; i < il; i ++ ) {
- item = this.actions[ i ];
- action = item.action;
- args = item.args;
- switch( action ) {
- case THREE.PathActions.MOVE_TO:
- points.push( new THREE.Vector2( args[ 0 ], args[ 1 ] ) );
- break;
- case THREE.PathActions.LINE_TO:
- points.push( new THREE.Vector2( args[ 0 ], args[ 1 ] ) );
- break;
- case THREE.PathActions.QUADRATIC_CURVE_TO:
- cpx = args[ 2 ];
- cpy = args[ 3 ];
- cpx1 = args[ 0 ];
- cpy1 = args[ 1 ];
- if ( points.length > 0 ) {
- laste = points[ points.length - 1 ];
- cpx0 = laste.x;
- cpy0 = laste.y;
- } else {
- laste = this.actions[ i - 1 ].args;
- cpx0 = laste[ laste.length - 2 ];
- cpy0 = laste[ laste.length - 1 ];
- }
- for ( j = 1; j <= divisions; j ++ ) {
- t = j / divisions;
- tx = THREE.Shape.Utils.b2( t, cpx0, cpx1, cpx );
- ty = THREE.Shape.Utils.b2( t, cpy0, cpy1, cpy );
- points.push( new THREE.Vector2( tx, ty ) );
- }
- break;
- case THREE.PathActions.BEZIER_CURVE_TO:
- cpx = args[ 4 ];
- cpy = args[ 5 ];
- cpx1 = args[ 0 ];
- cpy1 = args[ 1 ];
- cpx2 = args[ 2 ];
- cpy2 = args[ 3 ];
- if ( points.length > 0 ) {
- laste = points[ points.length - 1 ];
- cpx0 = laste.x;
- cpy0 = laste.y;
- } else {
- laste = this.actions[ i - 1 ].args;
- cpx0 = laste[ laste.length - 2 ];
- cpy0 = laste[ laste.length - 1 ];
- }
- for ( j = 1; j <= divisions; j ++ ) {
- t = j / divisions;
- tx = THREE.Shape.Utils.b3( t, cpx0, cpx1, cpx2, cpx );
- ty = THREE.Shape.Utils.b3( t, cpy0, cpy1, cpy2, cpy );
- points.push( new THREE.Vector2( tx, ty ) );
- }
- break;
- case THREE.PathActions.CSPLINE_THRU:
- laste = this.actions[ i - 1 ].args;
- var last = new THREE.Vector2( laste[ laste.length - 2 ], laste[ laste.length - 1 ] );
- var spts = [ last ];
- var n = divisions * args[ 0 ].length;
- spts = spts.concat( args[ 0 ] );
- var spline = new THREE.SplineCurve( spts );
- for ( j = 1; j <= n; j ++ ) {
- points.push( spline.getPointAt( j / n ) ) ;
- }
- break;
- case THREE.PathActions.ARC:
- var aX = args[ 0 ], aY = args[ 1 ],
- aRadius = args[ 2 ],
- aStartAngle = args[ 3 ], aEndAngle = args[ 4 ],
- aClockwise = !!args[ 5 ];
- var deltaAngle = aEndAngle - aStartAngle;
- var angle;
- var tdivisions = divisions * 2;
- for ( j = 1; j <= tdivisions; j ++ ) {
- t = j / tdivisions;
- if ( ! aClockwise ) {
- t = 1 - t;
- }
- angle = aStartAngle + t * deltaAngle;
- tx = aX + aRadius * Math.cos( angle );
- ty = aY + aRadius * Math.sin( angle );
- //console.log('t', t, 'angle', angle, 'tx', tx, 'ty', ty);
- points.push( new THREE.Vector2( tx, ty ) );
- }
- //console.log(points);
- break;
-
- case THREE.PathActions.ELLIPSE:
- var aX = args[ 0 ], aY = args[ 1 ],
- xRadius = args[ 2 ],
- yRadius = args[3]
- aStartAngle = args[ 4 ], aEndAngle = args[ 5 ],
- aClockwise = !!args[ 6 ];
- var deltaAngle = aEndAngle - aStartAngle;
- var angle;
- var tdivisions = divisions * 2;
- for ( j = 1; j <= tdivisions; j ++ ) {
- t = j / tdivisions;
- if ( ! aClockwise ) {
- t = 1 - t;
- }
- angle = aStartAngle + t * deltaAngle;
- tx = aX + xRadius * Math.cos( angle );
- ty = aY + yRadius * Math.sin( angle );
- //console.log('t', t, 'angle', angle, 'tx', tx, 'ty', ty);
- points.push( new THREE.Vector2( tx, ty ) );
- }
- //console.log(points);
- break;
- } // end switch
- }
- // Normalize to remove the closing point by default.
- var lastPoint = points[ points.length - 1];
- var EPSILON = 0.0000000001;
- if ( Math.abs(lastPoint.x - points[ 0 ].x) < EPSILON &&
- Math.abs(lastPoint.y - points[ 0 ].y) < EPSILON)
- points.splice( points.length - 1, 1);
- if ( closedPath ) {
- points.push( points[ 0 ] );
- }
- return points;
- };
- // Breaks path into shapes
- THREE.Path.prototype.toShapes = function() {
- var i, il, item, action, args;
- var subPaths = [], lastPath = new THREE.Path();
- for ( i = 0, il = this.actions.length; i < il; i ++ ) {
- item = this.actions[ i ];
- args = item.args;
- action = item.action;
- if ( action == THREE.PathActions.MOVE_TO ) {
- if ( lastPath.actions.length != 0 ) {
- subPaths.push( lastPath );
- lastPath = new THREE.Path();
- }
- }
- lastPath[ action ].apply( lastPath, args );
- }
- if ( lastPath.actions.length != 0 ) {
- subPaths.push( lastPath );
- }
- // console.log(subPaths);
- if ( subPaths.length == 0 ) return [];
- var tmpPath, tmpShape, shapes = [];
- var holesFirst = !THREE.Shape.Utils.isClockWise( subPaths[ 0 ].getPoints() );
- // console.log("Holes first", holesFirst);
- if ( subPaths.length == 1) {
- tmpPath = subPaths[0];
- tmpShape = new THREE.Shape();
- tmpShape.actions = tmpPath.actions;
- tmpShape.curves = tmpPath.curves;
- shapes.push( tmpShape );
- return shapes;
- };
- if ( holesFirst ) {
- tmpShape = new THREE.Shape();
- for ( i = 0, il = subPaths.length; i < il; i ++ ) {
- tmpPath = subPaths[ i ];
- if ( THREE.Shape.Utils.isClockWise( tmpPath.getPoints() ) ) {
- tmpShape.actions = tmpPath.actions;
- tmpShape.curves = tmpPath.curves;
- shapes.push( tmpShape );
- tmpShape = new THREE.Shape();
- //console.log('cw', i);
- } else {
- tmpShape.holes.push( tmpPath );
- //console.log('ccw', i);
- }
- }
- } else {
- // Shapes first
- for ( i = 0, il = subPaths.length; i < il; i ++ ) {
- tmpPath = subPaths[ i ];
- if ( THREE.Shape.Utils.isClockWise( tmpPath.getPoints() ) ) {
- if ( tmpShape ) shapes.push( tmpShape );
- tmpShape = new THREE.Shape();
- tmpShape.actions = tmpPath.actions;
- tmpShape.curves = tmpPath.curves;
- } else {
- tmpShape.holes.push( tmpPath );
- }
- }
- shapes.push( tmpShape );
- }
- //console.log("shape", shapes);
- return shapes;
- };
- /**
- * @author zz85 / http://www.lab4games.net/zz85/blog
- * Defines a 2d shape plane using paths.
- **/
- // STEP 1 Create a path.
- // STEP 2 Turn path into shape.
- // STEP 3 ExtrudeGeometry takes in Shape/Shapes
- // STEP 3a - Extract points from each shape, turn to vertices
- // STEP 3b - Triangulate each shape, add faces.
- THREE.Shape = function ( ) {
- THREE.Path.apply( this, arguments );
- this.holes = [];
- };
- THREE.Shape.prototype = Object.create( THREE.Path.prototype );
- // Convenience method to return ExtrudeGeometry
- THREE.Shape.prototype.extrude = function ( options ) {
- var extruded = new THREE.ExtrudeGeometry( this, options );
- return extruded;
- };
- // Get points of holes
- THREE.Shape.prototype.getPointsHoles = function ( divisions ) {
- var i, il = this.holes.length, holesPts = [];
- for ( i = 0; i < il; i ++ ) {
- holesPts[ i ] = this.holes[ i ].getTransformedPoints( divisions, this.bends );
- }
- return holesPts;
- };
- // Get points of holes (spaced by regular distance)
- THREE.Shape.prototype.getSpacedPointsHoles = function ( divisions ) {
- var i, il = this.holes.length, holesPts = [];
- for ( i = 0; i < il; i ++ ) {
- holesPts[ i ] = this.holes[ i ].getTransformedSpacedPoints( divisions, this.bends );
- }
- return holesPts;
- };
- // Get points of shape and holes (keypoints based on segments parameter)
- THREE.Shape.prototype.extractAllPoints = function ( divisions ) {
- return {
- shape: this.getTransformedPoints( divisions ),
- holes: this.getPointsHoles( divisions )
- };
- };
- THREE.Shape.prototype.extractPoints = function ( divisions ) {
- if (this.useSpacedPoints) {
- return this.extractAllSpacedPoints(divisions);
- }
- return this.extractAllPoints(divisions);
- };
- //
- // THREE.Shape.prototype.extractAllPointsWithBend = function ( divisions, bend ) {
- //
- // return {
- //
- // shape: this.transform( bend, divisions ),
- // holes: this.getPointsHoles( divisions, bend )
- //
- // };
- //
- // };
- // Get points of shape and holes (spaced by regular distance)
- THREE.Shape.prototype.extractAllSpacedPoints = function ( divisions ) {
- return {
- shape: this.getTransformedSpacedPoints( divisions ),
- holes: this.getSpacedPointsHoles( divisions )
- };
- };
- /**************************************************************
- * Utils
- **************************************************************/
- THREE.Shape.Utils = {
- /*
- contour - array of vector2 for contour
- holes - array of array of vector2
- */
- removeHoles: function ( contour, holes ) {
- var shape = contour.concat(); // work on this shape
- var allpoints = shape.concat();
- /* For each isolated shape, find the closest points and break to the hole to allow triangulation */
- var prevShapeVert, nextShapeVert,
- prevHoleVert, nextHoleVert,
- holeIndex, shapeIndex,
- shapeId, shapeGroup,
- h, h2,
- hole, shortest, d,
- p, pts1, pts2,
- tmpShape1, tmpShape2,
- tmpHole1, tmpHole2,
- verts = [];
- for ( h = 0; h < holes.length; h ++ ) {
- hole = holes[ h ];
- /*
- shapeholes[ h ].concat(); // preserves original
- holes.push( hole );
- */
- Array.prototype.push.apply( allpoints, hole );
- shortest = Number.POSITIVE_INFINITY;
- // Find the shortest pair of pts between shape and hole
- // Note: Actually, I'm not sure now if we could optimize this to be faster than O(m*n)
- // Using distanceToSquared() intead of distanceTo() should speed a little
- // since running square roots operations are reduced.
- for ( h2 = 0; h2 < hole.length; h2 ++ ) {
- pts1 = hole[ h2 ];
- var dist = [];
- for ( p = 0; p < shape.length; p++ ) {
- pts2 = shape[ p ];
- d = pts1.distanceToSquared( pts2 );
- dist.push( d );
- if ( d < shortest ) {
- shortest = d;
- holeIndex = h2;
- shapeIndex = p;
- }
- }
- }
- //console.log("shortest", shortest, dist);
- prevShapeVert = ( shapeIndex - 1 ) >= 0 ? shapeIndex - 1 : shape.length - 1;
- prevHoleVert = ( holeIndex - 1 ) >= 0 ? holeIndex - 1 : hole.length - 1;
- var areaapts = [
- hole[ holeIndex ],
- shape[ shapeIndex ],
- shape[ prevShapeVert ]
- ];
- var areaa = THREE.FontUtils.Triangulate.area( areaapts );
- var areabpts = [
- hole[ holeIndex ],
- hole[ prevHoleVert ],
- shape[ shapeIndex ]
- ];
- var areab = THREE.FontUtils.Triangulate.area( areabpts );
- var shapeOffset = 1;
- var holeOffset = -1;
- var oldShapeIndex = shapeIndex, oldHoleIndex = holeIndex;
- shapeIndex += shapeOffset;
- holeIndex += holeOffset;
- if ( shapeIndex < 0 ) { shapeIndex += shape.length; }
- shapeIndex %= shape.length;
- if ( holeIndex < 0 ) { holeIndex += hole.length; }
- holeIndex %= hole.length;
- prevShapeVert = ( shapeIndex - 1 ) >= 0 ? shapeIndex - 1 : shape.length - 1;
- prevHoleVert = ( holeIndex - 1 ) >= 0 ? holeIndex - 1 : hole.length - 1;
- areaapts = [
- hole[ holeIndex ],
- shape[ shapeIndex ],
- shape[ prevShapeVert ]
- ];
- var areaa2 = THREE.FontUtils.Triangulate.area( areaapts );
- areabpts = [
- hole[ holeIndex ],
- hole[ prevHoleVert ],
- shape[ shapeIndex ]
- ];
- var areab2 = THREE.FontUtils.Triangulate.area( areabpts );
- //console.log(areaa,areab ,areaa2,areab2, ( areaa + areab ), ( areaa2 + areab2 ));
- if ( ( areaa + areab ) > ( areaa2 + areab2 ) ) {
- // In case areas are not correct.
- //console.log("USE THIS");
- shapeIndex = oldShapeIndex;
- holeIndex = oldHoleIndex ;
- if ( shapeIndex < 0 ) { shapeIndex += shape.length; }
- shapeIndex %= shape.length;
- if ( holeIndex < 0 ) { holeIndex += hole.length; }
- holeIndex %= hole.length;
- prevShapeVert = ( shapeIndex - 1 ) >= 0 ? shapeIndex - 1 : shape.length - 1;
- prevHoleVert = ( holeIndex - 1 ) >= 0 ? holeIndex - 1 : hole.length - 1;
- } else {
- //console.log("USE THAT ")
- }
- tmpShape1 = shape.slice( 0, shapeIndex );
- tmpShape2 = shape.slice( shapeIndex );
- tmpHole1 = hole.slice( holeIndex );
- tmpHole2 = hole.slice( 0, holeIndex );
- // Should check orders here again?
- var trianglea = [
- hole[ holeIndex ],
- shape[ shapeIndex ],
- shape[ prevShapeVert ]
- ];
- var triangleb = [
- hole[ holeIndex ] ,
- hole[ prevHoleVert ],
- shape[ shapeIndex ]
- ];
- verts.push( trianglea );
- verts.push( triangleb );
- shape = tmpShape1.concat( tmpHole1 ).concat( tmpHole2 ).concat( tmpShape2 );
- }
- return {
- shape:shape, /* shape with no holes */
- isolatedPts: verts, /* isolated faces */
- allpoints: allpoints
- }
- },
- triangulateShape: function ( contour, holes ) {
- var shapeWithoutHoles = THREE.Shape.Utils.removeHoles( contour, holes );
- var shape = shapeWithoutHoles.shape,
- allpoints = shapeWithoutHoles.allpoints,
- isolatedPts = shapeWithoutHoles.isolatedPts;
- var triangles = THREE.FontUtils.Triangulate( shape, false ); // True returns indices for points of spooled shape
- // To maintain reference to old shape, one must match coordinates, or offset the indices from original arrays. It's probably easier to do the first.
- //console.log( "triangles",triangles, triangles.length );
- //console.log( "allpoints",allpoints, allpoints.length );
- var i, il, f, face,
- key, index,
- allPointsMap = {},
- isolatedPointsMap = {};
- // prepare all points map
- for ( i = 0, il = allpoints.length; i < il; i ++ ) {
- key = allpoints[ i ].x + ":" + allpoints[ i ].y;
- if ( allPointsMap[ key ] !== undefined ) {
- console.log( "Duplicate point", key );
- }
- allPointsMap[ key ] = i;
- }
- // check all face vertices against all points map
- for ( i = 0, il = triangles.length; i < il; i ++ ) {
- face = triangles[ i ];
- for ( f = 0; f < 3; f ++ ) {
- key = face[ f ].x + ":" + face[ f ].y;
- index = allPointsMap[ key ];
- if ( index !== undefined ) {
- face[ f ] = index;
- }
- }
- }
- // check isolated points vertices against all points map
- for ( i = 0, il = isolatedPts.length; i < il; i ++ ) {
- face = isolatedPts[ i ];
- for ( f = 0; f < 3; f ++ ) {
- key = face[ f ].x + ":" + face[ f ].y;
- index = allPointsMap[ key ];
- if ( index !== undefined ) {
- face[ f ] = index;
- }
- }
- }
- return triangles.concat( isolatedPts );
- }, // end triangulate shapes
- /*
- triangulate2 : function( pts, holes ) {
- // For use with Poly2Tri.js
- var allpts = pts.concat();
- var shape = [];
- for (var p in pts) {
- shape.push(new js.poly2tri.Point(pts[p].x, pts[p].y));
- }
- var swctx = new js.poly2tri.SweepContext(shape);
- for (var h in holes) {
- var aHole = holes[h];
- var newHole = []
- for (i in aHole) {
- newHole.push(new js.poly2tri.Point(aHole[i].x, aHole[i].y));
- allpts.push(aHole[i]);
- }
- swctx.AddHole(newHole);
- }
- var find;
- var findIndexForPt = function (pt) {
- find = new THREE.Vector2(pt.x, pt.y);
- var p;
- for (p=0, pl = allpts.length; p<pl; p++) {
- if (allpts[p].equals(find)) return p;
- }
- return -1;
- };
- // triangulate
- js.poly2tri.sweep.Triangulate(swctx);
- var triangles = swctx.GetTriangles();
- var tr ;
- var facesPts = [];
- for (var t in triangles) {
- tr = triangles[t];
- facesPts.push([
- findIndexForPt(tr.GetPoint(0)),
- findIndexForPt(tr.GetPoint(1)),
- findIndexForPt(tr.GetPoint(2))
- ]);
- }
- // console.log(facesPts);
- // console.log("triangles", triangles.length, triangles);
- // Returns array of faces with 3 element each
- return facesPts;
- },
- */
- isClockWise: function ( pts ) {
- return THREE.FontUtils.Triangulate.area( pts ) < 0;
- },
- // Bezier Curves formulas obtained from
- // http://en.wikipedia.org/wiki/B%C3%A9zier_curve
- // Quad Bezier Functions
- b2p0: function ( t, p ) {
- var k = 1 - t;
- return k * k * p;
- },
- b2p1: function ( t, p ) {
- return 2 * ( 1 - t ) * t * p;
- },
- b2p2: function ( t, p ) {
- return t * t * p;
- },
- b2: function ( t, p0, p1, p2 ) {
- return this.b2p0( t, p0 ) + this.b2p1( t, p1 ) + this.b2p2( t, p2 );
- },
- // Cubic Bezier Functions
- b3p0: function ( t, p ) {
- var k = 1 - t;
- return k * k * k * p;
- },
- b3p1: function ( t, p ) {
- var k = 1 - t;
- return 3 * k * k * t * p;
- },
- b3p2: function ( t, p ) {
- var k = 1 - t;
- return 3 * k * t * t * p;
- },
- b3p3: function ( t, p ) {
- return t * t * t * p;
- },
- b3: function ( t, p0, p1, p2, p3 ) {
- return this.b3p0( t, p0 ) + this.b3p1( t, p1 ) + this.b3p2( t, p2 ) + this.b3p3( t, p3 );
- }
- };
- /**
- * @author mikael emtinger / http://gomo.se/
- */
- THREE.AnimationHandler = (function() {
- var playing = [];
- var library = {};
- var that = {};
- //--- update ---
- that.update = function( deltaTimeMS ) {
- for( var i = 0; i < playing.length; i ++ )
- playing[ i ].update( deltaTimeMS );
- };
- //--- add ---
- that.addToUpdate = function( animation ) {
- if ( playing.indexOf( animation ) === -1 )
- playing.push( animation );
- };
- //--- remove ---
- that.removeFromUpdate = function( animation ) {
- var index = playing.indexOf( animation );
- if( index !== -1 )
- playing.splice( index, 1 );
- };
- //--- add ---
- that.add = function( data ) {
- if ( library[ data.name ] !== undefined )
- console.log( "THREE.AnimationHandler.add: Warning! " + data.name + " already exists in library. Overwriting." );
- library[ data.name ] = data;
- initData( data );
- };
- //--- get ---
- that.get = function( name ) {
- if ( typeof name === "string" ) {
- if ( library[ name ] ) {
- return library[ name ];
- } else {
- console.log( "THREE.AnimationHandler.get: Couldn't find animation " + name );
- return null;
- }
- } else {
- // todo: add simple tween library
- }
- };
- //--- parse ---
- that.parse = function( root ) {
- // setup hierarchy
- var hierarchy = [];
- if ( root instanceof THREE.SkinnedMesh ) {
- for( var b = 0; b < root.bones.length; b++ ) {
- hierarchy.push( root.bones[ b ] );
- }
- } else {
- parseRecurseHierarchy( root, hierarchy );
- }
- return hierarchy;
- };
- var parseRecurseHierarchy = function( root, hierarchy ) {
- hierarchy.push( root );
- for( var c = 0; c < root.children.length; c++ )
- parseRecurseHierarchy( root.children[ c ], hierarchy );
- }
- //--- init data ---
- var initData = function( data ) {
- if( data.initialized === true )
- return;
- // loop through all keys
- for( var h = 0; h < data.hierarchy.length; h ++ ) {
- for( var k = 0; k < data.hierarchy[ h ].keys.length; k ++ ) {
- // remove minus times
- if( data.hierarchy[ h ].keys[ k ].time < 0 )
- data.hierarchy[ h ].keys[ k ].time = 0;
- // create quaternions
- if( data.hierarchy[ h ].keys[ k ].rot !== undefined &&
- !( data.hierarchy[ h ].keys[ k ].rot instanceof THREE.Quaternion ) ) {
- var quat = data.hierarchy[ h ].keys[ k ].rot;
- data.hierarchy[ h ].keys[ k ].rot = new THREE.Quaternion( quat[0], quat[1], quat[2], quat[3] );
- }
- }
- // prepare morph target keys
- if( data.hierarchy[ h ].keys.length && data.hierarchy[ h ].keys[ 0 ].morphTargets !== undefined ) {
- // get all used
- var usedMorphTargets = {};
- for ( var k = 0; k < data.hierarchy[ h ].keys.length; k ++ ) {
- for ( var m = 0; m < data.hierarchy[ h ].keys[ k ].morphTargets.length; m ++ ) {
- var morphTargetName = data.hierarchy[ h ].keys[ k ].morphTargets[ m ];
- usedMorphTargets[ morphTargetName ] = -1;
- }
- }
- data.hierarchy[ h ].usedMorphTargets = usedMorphTargets;
- // set all used on all frames
- for ( var k = 0; k < data.hierarchy[ h ].keys.length; k ++ ) {
- var influences = {};
- for ( var morphTargetName in usedMorphTargets ) {
- for ( var m = 0; m < data.hierarchy[ h ].keys[ k ].morphTargets.length; m ++ ) {
- if ( data.hierarchy[ h ].keys[ k ].morphTargets[ m ] === morphTargetName ) {
- influences[ morphTargetName ] = data.hierarchy[ h ].keys[ k ].morphTargetsInfluences[ m ];
- break;
- }
- }
- if ( m === data.hierarchy[ h ].keys[ k ].morphTargets.length ) {
- influences[ morphTargetName ] = 0;
- }
- }
- data.hierarchy[ h ].keys[ k ].morphTargetsInfluences = influences;
- }
- }
- // remove all keys that are on the same time
- for ( var k = 1; k < data.hierarchy[ h ].keys.length; k ++ ) {
- if ( data.hierarchy[ h ].keys[ k ].time === data.hierarchy[ h ].keys[ k - 1 ].time ) {
- data.hierarchy[ h ].keys.splice( k, 1 );
- k --;
- }
- }
- // set index
- for ( var k = 0; k < data.hierarchy[ h ].keys.length; k ++ ) {
- data.hierarchy[ h ].keys[ k ].index = k;
- }
- }
- // JIT
- var lengthInFrames = parseInt( data.length * data.fps, 10 );
- data.JIT = {};
- data.JIT.hierarchy = [];
- for( var h = 0; h < data.hierarchy.length; h ++ )
- data.JIT.hierarchy.push( new Array( lengthInFrames ) );
- // done
- data.initialized = true;
- };
- // interpolation types
- that.LINEAR = 0;
- that.CATMULLROM = 1;
- that.CATMULLROM_FORWARD = 2;
- return that;
- }());
- /**
- * @author mikael emtinger / http://gomo.se/
- * @author mrdoob / http://mrdoob.com/
- * @author alteredq / http://alteredqualia.com/
- */
- THREE.Animation = function ( root, name, interpolationType ) {
- this.root = root;
- this.data = THREE.AnimationHandler.get( name );
- this.hierarchy = THREE.AnimationHandler.parse( root );
- this.currentTime = 0;
- this.timeScale = 1;
- this.isPlaying = false;
- this.isPaused = true;
- this.loop = true;
- this.interpolationType = interpolationType !== undefined ? interpolationType : THREE.AnimationHandler.LINEAR;
- this.points = [];
- this.target = new THREE.Vector3();
- };
- THREE.Animation.prototype.play = function ( loop, startTimeMS ) {
- if ( this.isPlaying === false ) {
- this.isPlaying = true;
- this.loop = loop !== undefined ? loop : true;
- this.currentTime = startTimeMS !== undefined ? startTimeMS : 0;
- // reset key cache
- var h, hl = this.hierarchy.length,
- object;
- for ( h = 0; h < hl; h ++ ) {
- object = this.hierarchy[ h ];
- if ( this.interpolationType !== THREE.AnimationHandler.CATMULLROM_FORWARD ) {
- object.useQuaternion = true;
- }
- object.matrixAutoUpdate = true;
- if ( object.animationCache === undefined ) {
- object.animationCache = {};
- object.animationCache.prevKey = { pos: 0, rot: 0, scl: 0 };
- object.animationCache.nextKey = { pos: 0, rot: 0, scl: 0 };
- object.animationCache.originalMatrix = object instanceof THREE.Bone ? object.skinMatrix : object.matrix;
- }
- var prevKey = object.animationCache.prevKey;
- var nextKey = object.animationCache.nextKey;
- prevKey.pos = this.data.hierarchy[ h ].keys[ 0 ];
- prevKey.rot = this.data.hierarchy[ h ].keys[ 0 ];
- prevKey.scl = this.data.hierarchy[ h ].keys[ 0 ];
- nextKey.pos = this.getNextKeyWith( "pos", h, 1 );
- nextKey.rot = this.getNextKeyWith( "rot", h, 1 );
- nextKey.scl = this.getNextKeyWith( "scl", h, 1 );
- }
- this.update( 0 );
- }
- this.isPaused = false;
- THREE.AnimationHandler.addToUpdate( this );
- };
- THREE.Animation.prototype.pause = function() {
- if ( this.isPaused === true ) {
- THREE.AnimationHandler.addToUpdate( this );
- } else {
- THREE.AnimationHandler.removeFromUpdate( this );
- }
- this.isPaused = !this.isPaused;
- };
- THREE.Animation.prototype.stop = function() {
- this.isPlaying = false;
- this.isPaused = false;
- THREE.AnimationHandler.removeFromUpdate( this );
- };
- THREE.Animation.prototype.update = function ( deltaTimeMS ) {
- // early out
- if ( this.isPlaying === false ) return;
- // vars
- var types = [ "pos", "rot", "scl" ];
- var type;
- var scale;
- var vector;
- var prevXYZ, nextXYZ;
- var prevKey, nextKey;
- var object;
- var animationCache;
- var frame;
- var JIThierarchy = this.data.JIT.hierarchy;
- var currentTime, unloopedCurrentTime;
- var currentPoint, forwardPoint, angle;
- this.currentTime += deltaTimeMS * this.timeScale;
- unloopedCurrentTime = this.currentTime;
- currentTime = this.currentTime = this.currentTime % this.data.length;
- frame = parseInt( Math.min( currentTime * this.data.fps, this.data.length * this.data.fps ), 10 );
- for ( var h = 0, hl = this.hierarchy.length; h < hl; h ++ ) {
- object = this.hierarchy[ h ];
- animationCache = object.animationCache;
- // loop through pos/rot/scl
- for ( var t = 0; t < 3; t ++ ) {
- // get keys
- type = types[ t ];
- prevKey = animationCache.prevKey[ type ];
- nextKey = animationCache.nextKey[ type ];
- // switch keys?
- if ( nextKey.time <= unloopedCurrentTime ) {
- // did we loop?
- if ( currentTime < unloopedCurrentTime ) {
- if ( this.loop ) {
- prevKey = this.data.hierarchy[ h ].keys[ 0 ];
- nextKey = this.getNextKeyWith( type, h, 1 );
- while( nextKey.time < currentTime ) {
- prevKey = nextKey;
- nextKey = this.getNextKeyWith( type, h, nextKey.index + 1 );
- }
- } else {
- this.stop();
- return;
- }
- } else {
- do {
- prevKey = nextKey;
- nextKey = this.getNextKeyWith( type, h, nextKey.index + 1 );
- } while( nextKey.time < currentTime )
- }
- animationCache.prevKey[ type ] = prevKey;
- animationCache.nextKey[ type ] = nextKey;
- }
- object.matrixAutoUpdate = true;
- object.matrixWorldNeedsUpdate = true;
- scale = ( currentTime - prevKey.time ) / ( nextKey.time - prevKey.time );
- prevXYZ = prevKey[ type ];
- nextXYZ = nextKey[ type ];
- // check scale error
- if ( scale < 0 || scale > 1 ) {
- console.log( "THREE.Animation.update: Warning! Scale out of bounds:" + scale + " on bone " + h );
- scale = scale < 0 ? 0 : 1;
- }
- // interpolate
- if ( type === "pos" ) {
- vector = object.position;
- if ( this.interpolationType === THREE.AnimationHandler.LINEAR ) {
- vector.x = prevXYZ[ 0 ] + ( nextXYZ[ 0 ] - prevXYZ[ 0 ] ) * scale;
- vector.y = prevXYZ[ 1 ] + ( nextXYZ[ 1 ] - prevXYZ[ 1 ] ) * scale;
- vector.z = prevXYZ[ 2 ] + ( nextXYZ[ 2 ] - prevXYZ[ 2 ] ) * scale;
- } else if ( this.interpolationType === THREE.AnimationHandler.CATMULLROM ||
- this.interpolationType === THREE.AnimationHandler.CATMULLROM_FORWARD ) {
- this.points[ 0 ] = this.getPrevKeyWith( "pos", h, prevKey.index - 1 )[ "pos" ];
- this.points[ 1 ] = prevXYZ;
- this.points[ 2 ] = nextXYZ;
- this.points[ 3 ] = this.getNextKeyWith( "pos", h, nextKey.index + 1 )[ "pos" ];
- scale = scale * 0.33 + 0.33;
- currentPoint = this.interpolateCatmullRom( this.points, scale );
- vector.x = currentPoint[ 0 ];
- vector.y = currentPoint[ 1 ];
- vector.z = currentPoint[ 2 ];
- if ( this.interpolationType === THREE.AnimationHandler.CATMULLROM_FORWARD ) {
- forwardPoint = this.interpolateCatmullRom( this.points, scale * 1.01 );
- this.target.set( forwardPoint[ 0 ], forwardPoint[ 1 ], forwardPoint[ 2 ] );
- this.target.subSelf( vector );
- this.target.y = 0;
- this.target.normalize();
- angle = Math.atan2( this.target.x, this.target.z );
- object.rotation.set( 0, angle, 0 );
- }
- }
- } else if ( type === "rot" ) {
- THREE.Quaternion.slerp( prevXYZ, nextXYZ, object.quaternion, scale );
- } else if ( type === "scl" ) {
- vector = object.scale;
- vector.x = prevXYZ[ 0 ] + ( nextXYZ[ 0 ] - prevXYZ[ 0 ] ) * scale;
- vector.y = prevXYZ[ 1 ] + ( nextXYZ[ 1 ] - prevXYZ[ 1 ] ) * scale;
- vector.z = prevXYZ[ 2 ] + ( nextXYZ[ 2 ] - prevXYZ[ 2 ] ) * scale;
- }
- }
- }
- };
- // Catmull-Rom spline
- THREE.Animation.prototype.interpolateCatmullRom = function ( points, scale ) {
- var c = [], v3 = [],
- point, intPoint, weight, w2, w3,
- pa, pb, pc, pd;
- point = ( points.length - 1 ) * scale;
- intPoint = Math.floor( point );
- weight = point - intPoint;
- c[ 0 ] = intPoint === 0 ? intPoint : intPoint - 1;
- c[ 1 ] = intPoint;
- c[ 2 ] = intPoint > points.length - 2 ? intPoint : intPoint + 1;
- c[ 3 ] = intPoint > points.length - 3 ? intPoint : intPoint + 2;
- pa = points[ c[ 0 ] ];
- pb = points[ c[ 1 ] ];
- pc = points[ c[ 2 ] ];
- pd = points[ c[ 3 ] ];
- w2 = weight * weight;
- w3 = weight * w2;
- v3[ 0 ] = this.interpolate( pa[ 0 ], pb[ 0 ], pc[ 0 ], pd[ 0 ], weight, w2, w3 );
- v3[ 1 ] = this.interpolate( pa[ 1 ], pb[ 1 ], pc[ 1 ], pd[ 1 ], weight, w2, w3 );
- v3[ 2 ] = this.interpolate( pa[ 2 ], pb[ 2 ], pc[ 2 ], pd[ 2 ], weight, w2, w3 );
- return v3;
- };
- THREE.Animation.prototype.interpolate = function ( p0, p1, p2, p3, t, t2, t3 ) {
- var v0 = ( p2 - p0 ) * 0.5,
- v1 = ( p3 - p1 ) * 0.5;
- return ( 2 * ( p1 - p2 ) + v0 + v1 ) * t3 + ( - 3 * ( p1 - p2 ) - 2 * v0 - v1 ) * t2 + v0 * t + p1;
- };
- // Get next key with
- THREE.Animation.prototype.getNextKeyWith = function ( type, h, key ) {
- var keys = this.data.hierarchy[ h ].keys;
- if ( this.interpolationType === THREE.AnimationHandler.CATMULLROM ||
- this.interpolationType === THREE.AnimationHandler.CATMULLROM_FORWARD ) {
- key = key < keys.length - 1 ? key : keys.length - 1;
- } else {
- key = key % keys.length;
- }
- for ( ; key < keys.length; key++ ) {
- if ( keys[ key ][ type ] !== undefined ) {
- return keys[ key ];
- }
- }
- return this.data.hierarchy[ h ].keys[ 0 ];
- };
- // Get previous key with
- THREE.Animation.prototype.getPrevKeyWith = function ( type, h, key ) {
- var keys = this.data.hierarchy[ h ].keys;
- if ( this.interpolationType === THREE.AnimationHandler.CATMULLROM ||
- this.interpolationType === THREE.AnimationHandler.CATMULLROM_FORWARD ) {
- key = key > 0 ? key : 0;
- } else {
- key = key >= 0 ? key : key + keys.length;
- }
- for ( ; key >= 0; key -- ) {
- if ( keys[ key ][ type ] !== undefined ) {
- return keys[ key ];
- }
- }
- return this.data.hierarchy[ h ].keys[ keys.length - 1 ];
- };
- /**
- * @author mikael emtinger / http://gomo.se/
- * @author mrdoob / http://mrdoob.com/
- * @author alteredq / http://alteredqualia.com/
- * @author khang duong
- * @author erik kitson
- */
- THREE.KeyFrameAnimation = function( root, data, JITCompile ) {
- this.root = root;
- this.data = THREE.AnimationHandler.get( data );
- this.hierarchy = THREE.AnimationHandler.parse( root );
- this.currentTime = 0;
- this.timeScale = 0.001;
- this.isPlaying = false;
- this.isPaused = true;
- this.loop = true;
- this.JITCompile = JITCompile !== undefined ? JITCompile : true;
- // initialize to first keyframes
- for ( var h = 0, hl = this.hierarchy.length; h < hl; h++ ) {
- var keys = this.data.hierarchy[h].keys,
- sids = this.data.hierarchy[h].sids,
- obj = this.hierarchy[h];
- if ( keys.length && sids ) {
- for ( var s = 0; s < sids.length; s++ ) {
- var sid = sids[ s ],
- next = this.getNextKeyWith( sid, h, 0 );
- if ( next ) {
- next.apply( sid );
- }
- }
- obj.matrixAutoUpdate = false;
- this.data.hierarchy[h].node.updateMatrix();
- obj.matrixWorldNeedsUpdate = true;
- }
- }
- };
- // Play
- THREE.KeyFrameAnimation.prototype.play = function( loop, startTimeMS ) {
- if( !this.isPlaying ) {
- this.isPlaying = true;
- this.loop = loop !== undefined ? loop : true;
- this.currentTime = startTimeMS !== undefined ? startTimeMS : 0;
- this.startTimeMs = startTimeMS;
- this.startTime = 10000000;
- this.endTime = -this.startTime;
- // reset key cache
- var h, hl = this.hierarchy.length,
- object,
- node;
- for ( h = 0; h < hl; h++ ) {
- object = this.hierarchy[ h ];
- node = this.data.hierarchy[ h ];
- object.useQuaternion = true;
- if ( node.animationCache === undefined ) {
- node.animationCache = {};
- node.animationCache.prevKey = null;
- node.animationCache.nextKey = null;
- node.animationCache.originalMatrix = object instanceof THREE.Bone ? object.skinMatrix : object.matrix;
- }
- var keys = this.data.hierarchy[h].keys;
- if (keys.length) {
- node.animationCache.prevKey = keys[ 0 ];
- node.animationCache.nextKey = keys[ 1 ];
- this.startTime = Math.min( keys[0].time, this.startTime );
- this.endTime = Math.max( keys[keys.length - 1].time, this.endTime );
- }
- }
- this.update( 0 );
- }
- this.isPaused = false;
- THREE.AnimationHandler.addToUpdate( this );
- };
- // Pause
- THREE.KeyFrameAnimation.prototype.pause = function() {
- if( this.isPaused ) {
- THREE.AnimationHandler.addToUpdate( this );
- } else {
- THREE.AnimationHandler.removeFromUpdate( this );
- }
- this.isPaused = !this.isPaused;
- };
- // Stop
- THREE.KeyFrameAnimation.prototype.stop = function() {
- this.isPlaying = false;
- this.isPaused = false;
- THREE.AnimationHandler.removeFromUpdate( this );
- // reset JIT matrix and remove cache
- for ( var h = 0; h < this.data.hierarchy.length; h++ ) {
-
- var obj = this.hierarchy[ h ];
- var node = this.data.hierarchy[ h ];
- if ( node.animationCache !== undefined ) {
- var original = node.animationCache.originalMatrix;
- if( obj instanceof THREE.Bone ) {
- original.copy( obj.skinMatrix );
- obj.skinMatrix = original;
- } else {
- original.copy( obj.matrix );
- obj.matrix = original;
- }
- delete node.animationCache;
- }
- }
- };
- // Update
- THREE.KeyFrameAnimation.prototype.update = function( deltaTimeMS ) {
- // early out
- if( !this.isPlaying ) return;
- // vars
- var prevKey, nextKey;
- var object;
- var node;
- var frame;
- var JIThierarchy = this.data.JIT.hierarchy;
- var currentTime, unloopedCurrentTime;
- var looped;
- // update
- this.currentTime += deltaTimeMS * this.timeScale;
- unloopedCurrentTime = this.currentTime;
- currentTime = this.currentTime = this.currentTime % this.data.length;
- // if looped around, the current time should be based on the startTime
- if ( currentTime < this.startTimeMs ) {
- currentTime = this.currentTime = this.startTimeMs + currentTime;
- }
- frame = parseInt( Math.min( currentTime * this.data.fps, this.data.length * this.data.fps ), 10 );
- looped = currentTime < unloopedCurrentTime;
- if ( looped && !this.loop ) {
- // Set the animation to the last keyframes and stop
- for ( var h = 0, hl = this.hierarchy.length; h < hl; h++ ) {
- var keys = this.data.hierarchy[h].keys,
- sids = this.data.hierarchy[h].sids,
- end = keys.length-1,
- obj = this.hierarchy[h];
- if ( keys.length ) {
- for ( var s = 0; s < sids.length; s++ ) {
- var sid = sids[ s ],
- prev = this.getPrevKeyWith( sid, h, end );
- if ( prev ) {
- prev.apply( sid );
- }
- }
- this.data.hierarchy[h].node.updateMatrix();
- obj.matrixWorldNeedsUpdate = true;
- }
- }
- this.stop();
- return;
- }
- // check pre-infinity
- if ( currentTime < this.startTime ) {
- return;
- }
- // update
- for ( var h = 0, hl = this.hierarchy.length; h < hl; h++ ) {
- object = this.hierarchy[ h ];
- node = this.data.hierarchy[ h ];
- var keys = node.keys,
- animationCache = node.animationCache;
- // use JIT?
- if ( this.JITCompile && JIThierarchy[ h ][ frame ] !== undefined ) {
- if( object instanceof THREE.Bone ) {
- object.skinMatrix = JIThierarchy[ h ][ frame ];
- object.matrixWorldNeedsUpdate = false;
- } else {
- object.matrix = JIThierarchy[ h ][ frame ];
- object.matrixWorldNeedsUpdate = true;
- }
- // use interpolation
- } else if ( keys.length ) {
- // make sure so original matrix and not JIT matrix is set
- if ( this.JITCompile && animationCache ) {
- if( object instanceof THREE.Bone ) {
- object.skinMatrix = animationCache.originalMatrix;
- } else {
- object.matrix = animationCache.originalMatrix;
- }
- }
- prevKey = animationCache.prevKey;
- nextKey = animationCache.nextKey;
- if ( prevKey && nextKey ) {
- // switch keys?
- if ( nextKey.time <= unloopedCurrentTime ) {
- // did we loop?
- if ( looped && this.loop ) {
- prevKey = keys[ 0 ];
- nextKey = keys[ 1 ];
- while ( nextKey.time < currentTime ) {
- prevKey = nextKey;
- nextKey = keys[ prevKey.index + 1 ];
- }
- } else if ( !looped ) {
- var lastIndex = keys.length - 1;
- while ( nextKey.time < currentTime && nextKey.index !== lastIndex ) {
- prevKey = nextKey;
- nextKey = keys[ prevKey.index + 1 ];
- }
- }
- animationCache.prevKey = prevKey;
- animationCache.nextKey = nextKey;
- }
- if(nextKey.time >= currentTime)
- prevKey.interpolate( nextKey, currentTime );
- else
- prevKey.interpolate( nextKey, nextKey.time);
- }
- this.data.hierarchy[h].node.updateMatrix();
- object.matrixWorldNeedsUpdate = true;
- }
- }
- // update JIT?
- if ( this.JITCompile ) {
- if ( JIThierarchy[ 0 ][ frame ] === undefined ) {
- this.hierarchy[ 0 ].updateMatrixWorld( true );
- for ( var h = 0; h < this.hierarchy.length; h++ ) {
- if( this.hierarchy[ h ] instanceof THREE.Bone ) {
- JIThierarchy[ h ][ frame ] = this.hierarchy[ h ].skinMatrix.clone();
- } else {
- JIThierarchy[ h ][ frame ] = this.hierarchy[ h ].matrix.clone();
- }
- }
- }
- }
- };
- // Get next key with
- THREE.KeyFrameAnimation.prototype.getNextKeyWith = function( sid, h, key ) {
- var keys = this.data.hierarchy[ h ].keys;
- key = key % keys.length;
- for ( ; key < keys.length; key++ ) {
- if ( keys[ key ].hasTarget( sid ) ) {
- return keys[ key ];
- }
- }
- return keys[ 0 ];
- };
- // Get previous key with
- THREE.KeyFrameAnimation.prototype.getPrevKeyWith = function( sid, h, key ) {
- var keys = this.data.hierarchy[ h ].keys;
- key = key >= 0 ? key : key + keys.length;
- for ( ; key >= 0; key-- ) {
- if ( keys[ key ].hasTarget( sid ) ) {
- return keys[ key ];
- }
- }
- return keys[ keys.length - 1 ];
- };
- /**
- * Camera for rendering cube maps
- * - renders scene into axis-aligned cube
- *
- * @author alteredq / http://alteredqualia.com/
- */
- THREE.CubeCamera = function ( near, far, cubeResolution ) {
- THREE.Object3D.call( this );
- var fov = 90, aspect = 1;
- var cameraPX = new THREE.PerspectiveCamera( fov, aspect, near, far );
- cameraPX.up.set( 0, -1, 0 );
- cameraPX.lookAt( new THREE.Vector3( 1, 0, 0 ) );
- this.add( cameraPX );
- var cameraNX = new THREE.PerspectiveCamera( fov, aspect, near, far );
- cameraNX.up.set( 0, -1, 0 );
- cameraNX.lookAt( new THREE.Vector3( -1, 0, 0 ) );
- this.add( cameraNX );
- var cameraPY = new THREE.PerspectiveCamera( fov, aspect, near, far );
- cameraPY.up.set( 0, 0, 1 );
- cameraPY.lookAt( new THREE.Vector3( 0, 1, 0 ) );
- this.add( cameraPY );
- var cameraNY = new THREE.PerspectiveCamera( fov, aspect, near, far );
- cameraNY.up.set( 0, 0, -1 );
- cameraNY.lookAt( new THREE.Vector3( 0, -1, 0 ) );
- this.add( cameraNY );
- var cameraPZ = new THREE.PerspectiveCamera( fov, aspect, near, far );
- cameraPZ.up.set( 0, -1, 0 );
- cameraPZ.lookAt( new THREE.Vector3( 0, 0, 1 ) );
- this.add( cameraPZ );
- var cameraNZ = new THREE.PerspectiveCamera( fov, aspect, near, far );
- cameraNZ.up.set( 0, -1, 0 );
- cameraNZ.lookAt( new THREE.Vector3( 0, 0, -1 ) );
- this.add( cameraNZ );
- this.renderTarget = new THREE.WebGLRenderTargetCube( cubeResolution, cubeResolution, { format: THREE.RGBFormat, magFilter: THREE.LinearFilter, minFilter: THREE.LinearFilter } );
- this.updateCubeMap = function ( renderer, scene ) {
- var renderTarget = this.renderTarget;
- var generateMipmaps = renderTarget.generateMipmaps;
- renderTarget.generateMipmaps = false;
- renderTarget.activeCubeFace = 0;
- renderer.render( scene, cameraPX, renderTarget );
- renderTarget.activeCubeFace = 1;
- renderer.render( scene, cameraNX, renderTarget );
- renderTarget.activeCubeFace = 2;
- renderer.render( scene, cameraPY, renderTarget );
- renderTarget.activeCubeFace = 3;
- renderer.render( scene, cameraNY, renderTarget );
- renderTarget.activeCubeFace = 4;
- renderer.render( scene, cameraPZ, renderTarget );
- renderTarget.generateMipmaps = generateMipmaps;
- renderTarget.activeCubeFace = 5;
- renderer.render( scene, cameraNZ, renderTarget );
- };
- };
- THREE.CubeCamera.prototype = Object.create( THREE.Object3D.prototype );
- /*
- * @author zz85 / http://twitter.com/blurspline / http://www.lab4games.net/zz85/blog
- *
- * A general perpose camera, for setting FOV, Lens Focal Length,
- * and switching between perspective and orthographic views easily.
- * Use this only if you do not wish to manage
- * both a Orthographic and Perspective Camera
- *
- */
- THREE.CombinedCamera = function ( width, height, fov, near, far, orthoNear, orthoFar ) {
- THREE.Camera.call( this );
- this.fov = fov;
- this.left = -width / 2;
- this.right = width / 2
- this.top = height / 2;
- this.bottom = -height / 2;
- // We could also handle the projectionMatrix internally, but just wanted to test nested camera objects
- this.cameraO = new THREE.OrthographicCamera( width / - 2, width / 2, height / 2, height / - 2, orthoNear, orthoFar );
- this.cameraP = new THREE.PerspectiveCamera( fov, width / height, near, far );
- this.zoom = 1;
- this.toPerspective();
- var aspect = width/height;
- };
- THREE.CombinedCamera.prototype = Object.create( THREE.Camera.prototype );
- THREE.CombinedCamera.prototype.toPerspective = function () {
- // Switches to the Perspective Camera
- this.near = this.cameraP.near;
- this.far = this.cameraP.far;
- this.cameraP.fov = this.fov / this.zoom ;
- this.cameraP.updateProjectionMatrix();
- this.projectionMatrix = this.cameraP.projectionMatrix;
- this.inPerspectiveMode = true;
- this.inOrthographicMode = false;
- };
- THREE.CombinedCamera.prototype.toOrthographic = function () {
- // Switches to the Orthographic camera estimating viewport from Perspective
- var fov = this.fov;
- var aspect = this.cameraP.aspect;
- var near = this.cameraP.near;
- var far = this.cameraP.far;
- // The size that we set is the mid plane of the viewing frustum
- var hyperfocus = ( near + far ) / 2;
- var halfHeight = Math.tan( fov / 2 ) * hyperfocus;
- var planeHeight = 2 * halfHeight;
- var planeWidth = planeHeight * aspect;
- var halfWidth = planeWidth / 2;
- halfHeight /= this.zoom;
- halfWidth /= this.zoom;
- this.cameraO.left = -halfWidth;
- this.cameraO.right = halfWidth;
- this.cameraO.top = halfHeight;
- this.cameraO.bottom = -halfHeight;
- // this.cameraO.left = -farHalfWidth;
- // this.cameraO.right = farHalfWidth;
- // this.cameraO.top = farHalfHeight;
- // this.cameraO.bottom = -farHalfHeight;
- // this.cameraO.left = this.left / this.zoom;
- // this.cameraO.right = this.right / this.zoom;
- // this.cameraO.top = this.top / this.zoom;
- // this.cameraO.bottom = this.bottom / this.zoom;
- this.cameraO.updateProjectionMatrix();
- this.near = this.cameraO.near;
- this.far = this.cameraO.far;
- this.projectionMatrix = this.cameraO.projectionMatrix;
- this.inPerspectiveMode = false;
- this.inOrthographicMode = true;
- };
- THREE.CombinedCamera.prototype.setSize = function( width, height ) {
- this.cameraP.aspect = width / height;
- this.left = -width / 2;
- this.right = width / 2
- this.top = height / 2;
- this.bottom = -height / 2;
- };
- THREE.CombinedCamera.prototype.setFov = function( fov ) {
- this.fov = fov;
- if ( this.inPerspectiveMode ) {
- this.toPerspective();
- } else {
- this.toOrthographic();
- }
- };
- // For mantaining similar API with PerspectiveCamera
- THREE.CombinedCamera.prototype.updateProjectionMatrix = function() {
- if ( this.inPerspectiveMode ) {
- this.toPerspective();
- } else {
- this.toPerspective();
- this.toOrthographic();
- }
- };
- /*
- * Uses Focal Length (in mm) to estimate and set FOV
- * 35mm (fullframe) camera is used if frame size is not specified;
- * Formula based on http://www.bobatkins.com/photography/technical/field_of_view.html
- */
- THREE.CombinedCamera.prototype.setLens = function ( focalLength, frameHeight ) {
- frameHeight = frameHeight !== undefined ? frameHeight : 24;
- var fov = 2 * Math.atan( frameHeight / ( focalLength * 2 ) ) * ( 180 / Math.PI );
- this.setFov( fov );
- return fov;
- };
- THREE.CombinedCamera.prototype.setZoom = function( zoom ) {
- this.zoom = zoom;
- if ( this.inPerspectiveMode ) {
- this.toPerspective();
- } else {
- this.toOrthographic();
- }
- };
- THREE.CombinedCamera.prototype.toFrontView = function() {
- this.rotation.x = 0;
- this.rotation.y = 0;
- this.rotation.z = 0;
- // should we be modifing the matrix instead?
- this.rotationAutoUpdate = false;
- };
- THREE.CombinedCamera.prototype.toBackView = function() {
- this.rotation.x = 0;
- this.rotation.y = Math.PI;
- this.rotation.z = 0;
- this.rotationAutoUpdate = false;
- };
- THREE.CombinedCamera.prototype.toLeftView = function() {
- this.rotation.x = 0;
- this.rotation.y = - Math.PI / 2;
- this.rotation.z = 0;
- this.rotationAutoUpdate = false;
- };
- THREE.CombinedCamera.prototype.toRightView = function() {
- this.rotation.x = 0;
- this.rotation.y = Math.PI / 2;
- this.rotation.z = 0;
- this.rotationAutoUpdate = false;
- };
- THREE.CombinedCamera.prototype.toTopView = function() {
- this.rotation.x = - Math.PI / 2;
- this.rotation.y = 0;
- this.rotation.z = 0;
- this.rotationAutoUpdate = false;
- };
- THREE.CombinedCamera.prototype.toBottomView = function() {
- this.rotation.x = Math.PI / 2;
- this.rotation.y = 0;
- this.rotation.z = 0;
- this.rotationAutoUpdate = false;
- };
- /**
- * @author mrdoob / http://mrdoob.com/
- * @author alteredq / http://alteredqualia.com/
- * @author paulirish / http://paulirish.com/
- */
- THREE.FirstPersonControls = function ( object, domElement ) {
- this.object = object;
- this.target = new THREE.Vector3( 0, 0, 0 );
- this.domElement = ( domElement !== undefined ) ? domElement : document;
- this.movementSpeed = 1.0;
- this.lookSpeed = 0.005;
- this.lookVertical = true;
- this.autoForward = false;
- // this.invertVertical = false;
- this.activeLook = true;
- this.heightSpeed = false;
- this.heightCoef = 1.0;
- this.heightMin = 0.0;
- this.heightMax = 1.0;
- this.constrainVertical = false;
- this.verticalMin = 0;
- this.verticalMax = Math.PI;
- this.autoSpeedFactor = 0.0;
- this.mouseX = 0;
- this.mouseY = 0;
- this.lat = 0;
- this.lon = 0;
- this.phi = 0;
- this.theta = 0;
- this.moveForward = false;
- this.moveBackward = false;
- this.moveLeft = false;
- this.moveRight = false;
- this.freeze = false;
- this.mouseDragOn = false;
- this.viewHalfX = 0;
- this.viewHalfY = 0;
- if ( this.domElement !== document ) {
- this.domElement.setAttribute( 'tabindex', -1 );
- }
- //
- this.handleResize = function () {
- if ( this.domElement === document ) {
- this.viewHalfX = window.innerWidth / 2;
- this.viewHalfY = window.innerHeight / 2;
- } else {
- this.viewHalfX = this.domElement.offsetWidth / 2;
- this.viewHalfY = this.domElement.offsetHeight / 2;
- }
- };
- this.onMouseDown = function ( event ) {
- if ( this.domElement !== document ) {
- this.domElement.focus();
- }
- event.preventDefault();
- event.stopPropagation();
- if ( this.activeLook ) {
- switch ( event.button ) {
- case 0: this.moveForward = true; break;
- case 2: this.moveBackward = true; break;
- }
- }
- this.mouseDragOn = true;
- };
- this.onMouseUp = function ( event ) {
- event.preventDefault();
- event.stopPropagation();
- if ( this.activeLook ) {
- switch ( event.button ) {
- case 0: this.moveForward = false; break;
- case 2: this.moveBackward = false; break;
- }
- }
- this.mouseDragOn = false;
- };
- this.onMouseMove = function ( event ) {
- if ( this.domElement === document ) {
- this.mouseX = event.pageX - this.viewHalfX;
- this.mouseY = event.pageY - this.viewHalfY;
- } else {
- this.mouseX = event.pageX - this.domElement.offsetLeft - this.viewHalfX;
- this.mouseY = event.pageY - this.domElement.offsetTop - this.viewHalfY;
- }
- };
- this.onKeyDown = function ( event ) {
- //event.preventDefault();
- switch ( event.keyCode ) {
- case 38: /*up*/
- case 87: /*W*/ this.moveForward = true; break;
- case 37: /*left*/
- case 65: /*A*/ this.moveLeft = true; break;
- case 40: /*down*/
- case 83: /*S*/ this.moveBackward = true; break;
- case 39: /*right*/
- case 68: /*D*/ this.moveRight = true; break;
- case 82: /*R*/ this.moveUp = true; break;
- case 70: /*F*/ this.moveDown = true; break;
- case 81: /*Q*/ this.freeze = !this.freeze; break;
- }
- };
- this.onKeyUp = function ( event ) {
- switch( event.keyCode ) {
- case 38: /*up*/
- case 87: /*W*/ this.moveForward = false; break;
- case 37: /*left*/
- case 65: /*A*/ this.moveLeft = false; break;
- case 40: /*down*/
- case 83: /*S*/ this.moveBackward = false; break;
- case 39: /*right*/
- case 68: /*D*/ this.moveRight = false; break;
- case 82: /*R*/ this.moveUp = false; break;
- case 70: /*F*/ this.moveDown = false; break;
- }
- };
- this.update = function( delta ) {
- var actualMoveSpeed = 0;
- if ( this.freeze ) {
- return;
- } else {
- if ( this.heightSpeed ) {
- var y = THREE.Math.clamp( this.object.position.y, this.heightMin, this.heightMax );
- var heightDelta = y - this.heightMin;
- this.autoSpeedFactor = delta * ( heightDelta * this.heightCoef );
- } else {
- this.autoSpeedFactor = 0.0;
- }
- actualMoveSpeed = delta * this.movementSpeed;
- if ( this.moveForward || ( this.autoForward && !this.moveBackward ) ) this.object.translateZ( - ( actualMoveSpeed + this.autoSpeedFactor ) );
- if ( this.moveBackward ) this.object.translateZ( actualMoveSpeed );
- if ( this.moveLeft ) this.object.translateX( - actualMoveSpeed );
- if ( this.moveRight ) this.object.translateX( actualMoveSpeed );
- if ( this.moveUp ) this.object.translateY( actualMoveSpeed );
- if ( this.moveDown ) this.object.translateY( - actualMoveSpeed );
- var actualLookSpeed = delta * this.lookSpeed;
- if ( !this.activeLook ) {
- actualLookSpeed = 0;
- }
- this.lon += this.mouseX * actualLookSpeed;
- if( this.lookVertical ) this.lat -= this.mouseY * actualLookSpeed; // * this.invertVertical?-1:1;
- this.lat = Math.max( - 85, Math.min( 85, this.lat ) );
- this.phi = ( 90 - this.lat ) * Math.PI / 180;
- this.theta = this.lon * Math.PI / 180;
- var targetPosition = this.target,
- position = this.object.position;
- targetPosition.x = position.x + 100 * Math.sin( this.phi ) * Math.cos( this.theta );
- targetPosition.y = position.y + 100 * Math.cos( this.phi );
- targetPosition.z = position.z + 100 * Math.sin( this.phi ) * Math.sin( this.theta );
- }
- var verticalLookRatio = 1;
- if ( this.constrainVertical ) {
- verticalLookRatio = Math.PI / ( this.verticalMax - this.verticalMin );
- }
- this.lon += this.mouseX * actualLookSpeed;
- if( this.lookVertical ) this.lat -= this.mouseY * actualLookSpeed * verticalLookRatio;
- this.lat = Math.max( - 85, Math.min( 85, this.lat ) );
- this.phi = ( 90 - this.lat ) * Math.PI / 180;
- this.theta = this.lon * Math.PI / 180;
- if ( this.constrainVertical ) {
- this.phi = THREE.Math.mapLinear( this.phi, 0, Math.PI, this.verticalMin, this.verticalMax );
- }
- var targetPosition = this.target,
- position = this.object.position;
- targetPosition.x = position.x + 100 * Math.sin( this.phi ) * Math.cos( this.theta );
- targetPosition.y = position.y + 100 * Math.cos( this.phi );
- targetPosition.z = position.z + 100 * Math.sin( this.phi ) * Math.sin( this.theta );
- this.object.lookAt( targetPosition );
- };
- this.domElement.addEventListener( 'contextmenu', function ( event ) { event.preventDefault(); }, false );
- this.domElement.addEventListener( 'mousemove', bind( this, this.onMouseMove ), false );
- this.domElement.addEventListener( 'mousedown', bind( this, this.onMouseDown ), false );
- this.domElement.addEventListener( 'mouseup', bind( this, this.onMouseUp ), false );
- this.domElement.addEventListener( 'keydown', bind( this, this.onKeyDown ), false );
- this.domElement.addEventListener( 'keyup', bind( this, this.onKeyUp ), false );
- function bind( scope, fn ) {
- return function () {
- fn.apply( scope, arguments );
- };
- };
- this.handleResize();
- };
- /**
- * @author alteredq / http://alteredqualia.com/
- */
- THREE.PathControls = function ( object, domElement ) {
- this.object = object;
- this.domElement = ( domElement !== undefined ) ? domElement : document;
- this.id = "PathControls" + THREE.PathControlsIdCounter ++;
- // API
- this.duration = 10 * 1000; // milliseconds
- this.waypoints = [];
- this.useConstantSpeed = true;
- this.resamplingCoef = 50;
- this.debugPath = new THREE.Object3D();
- this.debugDummy = new THREE.Object3D();
- this.animationParent = new THREE.Object3D();
- this.lookSpeed = 0.005;
- this.lookVertical = true;
- this.lookHorizontal = true;
- this.verticalAngleMap = { srcRange: [ 0, 2 * Math.PI ], dstRange: [ 0, 2 * Math.PI ] };
- this.horizontalAngleMap = { srcRange: [ 0, 2 * Math.PI ], dstRange: [ 0, 2 * Math.PI ] };
- // internals
- this.target = new THREE.Object3D();
- this.mouseX = 0;
- this.mouseY = 0;
- this.lat = 0;
- this.lon = 0;
- this.phi = 0;
- this.theta = 0;
- var PI2 = Math.PI * 2,
- PI180 = Math.PI / 180;
- this.viewHalfX = 0;
- this.viewHalfY = 0;
- if ( this.domElement !== document ) {
- this.domElement.setAttribute( 'tabindex', -1 );
- }
- // methods
- this.handleResize = function () {
- if ( this.domElement === document ) {
- this.viewHalfX = window.innerWidth / 2;
- this.viewHalfY = window.innerHeight / 2;
- } else {
- this.viewHalfX = this.domElement.offsetWidth / 2;
- this.viewHalfY = this.domElement.offsetHeight / 2;
- }
- };
- this.update = function ( delta ) {
- var srcRange, dstRange;
- if( this.lookHorizontal ) this.lon += this.mouseX * this.lookSpeed * delta;
- if( this.lookVertical ) this.lat -= this.mouseY * this.lookSpeed * delta;
- this.lon = Math.max( 0, Math.min( 360, this.lon ) );
- this.lat = Math.max( - 85, Math.min( 85, this.lat ) );
- this.phi = ( 90 - this.lat ) * PI180;
- this.theta = this.lon * PI180;
- this.phi = normalize_angle_rad( this.phi );
- // constrain vertical look angle
- srcRange = this.verticalAngleMap.srcRange;
- dstRange = this.verticalAngleMap.dstRange;
- var tmpPhi = THREE.Math.mapLinear( this.phi, srcRange[ 0 ], srcRange[ 1 ], dstRange[ 0 ], dstRange[ 1 ] );
- var tmpPhiFullRange = dstRange[ 1 ] - dstRange[ 0 ];
- var tmpPhiNormalized = ( tmpPhi - dstRange[ 0 ] ) / tmpPhiFullRange;
- this.phi = QuadraticEaseInOut( tmpPhiNormalized ) * tmpPhiFullRange + dstRange[ 0 ];
- // constrain horizontal look angle
- srcRange = this.horizontalAngleMap.srcRange;
- dstRange = this.horizontalAngleMap.dstRange;
- var tmpTheta = THREE.Math.mapLinear( this.theta, srcRange[ 0 ], srcRange[ 1 ], dstRange[ 0 ], dstRange[ 1 ] );
- var tmpThetaFullRange = dstRange[ 1 ] - dstRange[ 0 ];
- var tmpThetaNormalized = ( tmpTheta - dstRange[ 0 ] ) / tmpThetaFullRange;
- this.theta = QuadraticEaseInOut( tmpThetaNormalized ) * tmpThetaFullRange + dstRange[ 0 ];
- var targetPosition = this.target.position,
- position = this.object.position;
- targetPosition.x = 100 * Math.sin( this.phi ) * Math.cos( this.theta );
- targetPosition.y = 100 * Math.cos( this.phi );
- targetPosition.z = 100 * Math.sin( this.phi ) * Math.sin( this.theta );
- this.object.lookAt( this.target.position );
- };
- this.onMouseMove = function ( event ) {
- if ( this.domElement === document ) {
- this.mouseX = event.pageX - this.viewHalfX;
- this.mouseY = event.pageY - this.viewHalfY;
- } else {
- this.mouseX = event.pageX - this.domElement.offsetLeft - this.viewHalfX;
- this.mouseY = event.pageY - this.domElement.offsetTop - this.viewHalfY;
- }
- };
- // utils
- function normalize_angle_rad( a ) {
- var b = a % PI2;
- return b >= 0 ? b : b + PI2;
- };
- function distance( a, b ) {
- var dx = a[ 0 ] - b[ 0 ],
- dy = a[ 1 ] - b[ 1 ],
- dz = a[ 2 ] - b[ 2 ];
- return Math.sqrt( dx * dx + dy * dy + dz * dz );
- };
- function QuadraticEaseInOut ( k ) {
- if ( ( k *= 2 ) < 1 ) return 0.5 * k * k;
- return - 0.5 * ( --k * ( k - 2 ) - 1 );
- };
- function bind( scope, fn ) {
- return function () {
- fn.apply( scope, arguments );
- };
- };
- function initAnimationPath( parent, spline, name, duration ) {
- var animationData = {
- name: name,
- fps: 0.6,
- length: duration,
- hierarchy: []
- };
- var i,
- parentAnimation, childAnimation,
- path = spline.getControlPointsArray(),
- sl = spline.getLength(),
- pl = path.length,
- t = 0,
- first = 0,
- last = pl - 1;
- parentAnimation = { parent: -1, keys: [] };
- parentAnimation.keys[ first ] = { time: 0, pos: path[ first ], rot: [ 0, 0, 0, 1 ], scl: [ 1, 1, 1 ] };
- parentAnimation.keys[ last ] = { time: duration, pos: path[ last ], rot: [ 0, 0, 0, 1 ], scl: [ 1, 1, 1 ] };
- for ( i = 1; i < pl - 1; i++ ) {
- // real distance (approximation via linear segments)
- t = duration * sl.chunks[ i ] / sl.total;
- // equal distance
- //t = duration * ( i / pl );
- // linear distance
- //t += duration * distance( path[ i ], path[ i - 1 ] ) / sl.total;
- parentAnimation.keys[ i ] = { time: t, pos: path[ i ] };
- }
- animationData.hierarchy[ 0 ] = parentAnimation;
- THREE.AnimationHandler.add( animationData );
- return new THREE.Animation( parent, name, THREE.AnimationHandler.CATMULLROM_FORWARD, false );
- };
- function createSplineGeometry( spline, n_sub ) {
- var i, index, position,
- geometry = new THREE.Geometry();
- for ( i = 0; i < spline.points.length * n_sub; i ++ ) {
- index = i / ( spline.points.length * n_sub );
- position = spline.getPoint( index );
- geometry.vertices[ i ] = new THREE.Vector3( position.x, position.y, position.z );
- }
- return geometry;
- };
- function createPath( parent, spline ) {
- var lineGeo = createSplineGeometry( spline, 10 ),
- particleGeo = createSplineGeometry( spline, 10 ),
- lineMat = new THREE.LineBasicMaterial( { color: 0xff0000, linewidth: 3 } ),
- lineObj = new THREE.Line( lineGeo, lineMat ),
- particleObj = new THREE.ParticleSystem( particleGeo, new THREE.ParticleBasicMaterial( { color: 0xffaa00, size: 3 } ) );
- lineObj.scale.set( 1, 1, 1 );
- parent.add( lineObj );
- particleObj.scale.set( 1, 1, 1 );
- parent.add( particleObj );
- var waypoint,
- geo = new THREE.SphereGeometry( 1, 16, 8 ),
- mat = new THREE.MeshBasicMaterial( { color: 0x00ff00 } );
- for ( var i = 0; i < spline.points.length; i ++ ) {
- waypoint = new THREE.Mesh( geo, mat );
- waypoint.position.copy( spline.points[ i ] );
- parent.add( waypoint );
- }
- };
- this.init = function ( ) {
- // constructor
- this.spline = new THREE.Spline();
- this.spline.initFromArray( this.waypoints );
- if ( this.useConstantSpeed ) {
- this.spline.reparametrizeByArcLength( this.resamplingCoef );
- }
- if ( this.createDebugDummy ) {
- var dummyParentMaterial = new THREE.MeshLambertMaterial( { color: 0x0077ff } ),
- dummyChildMaterial = new THREE.MeshLambertMaterial( { color: 0x00ff00 } ),
- dummyParentGeo = new THREE.CubeGeometry( 10, 10, 20 ),
- dummyChildGeo = new THREE.CubeGeometry( 2, 2, 10 );
- this.animationParent = new THREE.Mesh( dummyParentGeo, dummyParentMaterial );
- var dummyChild = new THREE.Mesh( dummyChildGeo, dummyChildMaterial );
- dummyChild.position.set( 0, 10, 0 );
- this.animation = initAnimationPath( this.animationParent, this.spline, this.id, this.duration );
- this.animationParent.add( this.object );
- this.animationParent.add( this.target );
- this.animationParent.add( dummyChild );
- } else {
- this.animation = initAnimationPath( this.animationParent, this.spline, this.id, this.duration );
- this.animationParent.add( this.target );
- this.animationParent.add( this.object );
- }
- if ( this.createDebugPath ) {
- createPath( this.debugPath, this.spline );
- }
- this.domElement.addEventListener( 'mousemove', bind( this, this.onMouseMove ), false );
- };
- this.handleResize();
- };
- THREE.PathControlsIdCounter = 0;
- /**
- * @author James Baicoianu / http://www.baicoianu.com/
- */
- THREE.FlyControls = function ( object, domElement ) {
- this.object = object;
- this.domElement = ( domElement !== undefined ) ? domElement : document;
- if ( domElement ) this.domElement.setAttribute( 'tabindex', -1 );
- // API
- this.movementSpeed = 1.0;
- this.rollSpeed = 0.005;
- this.dragToLook = false;
- this.autoForward = false;
- // disable default target object behavior
- this.object.useQuaternion = true;
- // internals
- this.tmpQuaternion = new THREE.Quaternion();
- this.mouseStatus = 0;
- this.moveState = { up: 0, down: 0, left: 0, right: 0, forward: 0, back: 0, pitchUp: 0, pitchDown: 0, yawLeft: 0, yawRight: 0, rollLeft: 0, rollRight: 0 };
- this.moveVector = new THREE.Vector3( 0, 0, 0 );
- this.rotationVector = new THREE.Vector3( 0, 0, 0 );
- this.handleEvent = function ( event ) {
- if ( typeof this[ event.type ] == 'function' ) {
- this[ event.type ]( event );
- }
- };
- this.keydown = function( event ) {
- if ( event.altKey ) {
- return;
- }
- //event.preventDefault();
- switch ( event.keyCode ) {
- case 16: /* shift */ this.movementSpeedMultiplier = .1; break;
- case 87: /*W*/ this.moveState.forward = 1; break;
- case 83: /*S*/ this.moveState.back = 1; break;
- case 65: /*A*/ this.moveState.left = 1; break;
- case 68: /*D*/ this.moveState.right = 1; break;
- case 82: /*R*/ this.moveState.up = 1; break;
- case 70: /*F*/ this.moveState.down = 1; break;
- case 38: /*up*/ this.moveState.pitchUp = 1; break;
- case 40: /*down*/ this.moveState.pitchDown = 1; break;
- case 37: /*left*/ this.moveState.yawLeft = 1; break;
- case 39: /*right*/ this.moveState.yawRight = 1; break;
- case 81: /*Q*/ this.moveState.rollLeft = 1; break;
- case 69: /*E*/ this.moveState.rollRight = 1; break;
- }
- this.updateMovementVector();
- this.updateRotationVector();
- };
- this.keyup = function( event ) {
- switch( event.keyCode ) {
- case 16: /* shift */ this.movementSpeedMultiplier = 1; break;
- case 87: /*W*/ this.moveState.forward = 0; break;
- case 83: /*S*/ this.moveState.back = 0; break;
- case 65: /*A*/ this.moveState.left = 0; break;
- case 68: /*D*/ this.moveState.right = 0; break;
- case 82: /*R*/ this.moveState.up = 0; break;
- case 70: /*F*/ this.moveState.down = 0; break;
- case 38: /*up*/ this.moveState.pitchUp = 0; break;
- case 40: /*down*/ this.moveState.pitchDown = 0; break;
- case 37: /*left*/ this.moveState.yawLeft = 0; break;
- case 39: /*right*/ this.moveState.yawRight = 0; break;
- case 81: /*Q*/ this.moveState.rollLeft = 0; break;
- case 69: /*E*/ this.moveState.rollRight = 0; break;
- }
- this.updateMovementVector();
- this.updateRotationVector();
- };
- this.mousedown = function( event ) {
- if ( this.domElement !== document ) {
- this.domElement.focus();
- }
- event.preventDefault();
- event.stopPropagation();
- if ( this.dragToLook ) {
- this.mouseStatus ++;
- } else {
- switch ( event.button ) {
- case 0: this.object.moveForward = true; break;
- case 2: this.object.moveBackward = true; break;
- }
- }
- };
- this.mousemove = function( event ) {
- if ( !this.dragToLook || this.mouseStatus > 0 ) {
- var container = this.getContainerDimensions();
- var halfWidth = container.size[ 0 ] / 2;
- var halfHeight = container.size[ 1 ] / 2;
- this.moveState.yawLeft = - ( ( event.pageX - container.offset[ 0 ] ) - halfWidth ) / halfWidth;
- this.moveState.pitchDown = ( ( event.pageY - container.offset[ 1 ] ) - halfHeight ) / halfHeight;
- this.updateRotationVector();
- }
- };
- this.mouseup = function( event ) {
- event.preventDefault();
- event.stopPropagation();
- if ( this.dragToLook ) {
- this.mouseStatus --;
- this.moveState.yawLeft = this.moveState.pitchDown = 0;
- } else {
- switch ( event.button ) {
- case 0: this.moveForward = false; break;
- case 2: this.moveBackward = false; break;
- }
- }
- this.updateRotationVector();
- };
- this.update = function( delta ) {
- var moveMult = delta * this.movementSpeed;
- var rotMult = delta * this.rollSpeed;
- this.object.translateX( this.moveVector.x * moveMult );
- this.object.translateY( this.moveVector.y * moveMult );
- this.object.translateZ( this.moveVector.z * moveMult );
- this.tmpQuaternion.set( this.rotationVector.x * rotMult, this.rotationVector.y * rotMult, this.rotationVector.z * rotMult, 1 ).normalize();
- this.object.quaternion.multiplySelf( this.tmpQuaternion );
- this.object.matrix.setPosition( this.object.position );
- this.object.matrix.setRotationFromQuaternion( this.object.quaternion );
- this.object.matrixWorldNeedsUpdate = true;
- };
- this.updateMovementVector = function() {
- var forward = ( this.moveState.forward || ( this.autoForward && !this.moveState.back ) ) ? 1 : 0;
- this.moveVector.x = ( -this.moveState.left + this.moveState.right );
- this.moveVector.y = ( -this.moveState.down + this.moveState.up );
- this.moveVector.z = ( -forward + this.moveState.back );
- //console.log( 'move:', [ this.moveVector.x, this.moveVector.y, this.moveVector.z ] );
- };
- this.updateRotationVector = function() {
- this.rotationVector.x = ( -this.moveState.pitchDown + this.moveState.pitchUp );
- this.rotationVector.y = ( -this.moveState.yawRight + this.moveState.yawLeft );
- this.rotationVector.z = ( -this.moveState.rollRight + this.moveState.rollLeft );
- //console.log( 'rotate:', [ this.rotationVector.x, this.rotationVector.y, this.rotationVector.z ] );
- };
- this.getContainerDimensions = function() {
- if ( this.domElement != document ) {
- return {
- size : [ this.domElement.offsetWidth, this.domElement.offsetHeight ],
- offset : [ this.domElement.offsetLeft, this.domElement.offsetTop ]
- };
- } else {
- return {
- size : [ window.innerWidth, window.innerHeight ],
- offset : [ 0, 0 ]
- };
- }
- };
- function bind( scope, fn ) {
- return function () {
- fn.apply( scope, arguments );
- };
- };
- this.domElement.addEventListener( 'mousemove', bind( this, this.mousemove ), false );
- this.domElement.addEventListener( 'mousedown', bind( this, this.mousedown ), false );
- this.domElement.addEventListener( 'mouseup', bind( this, this.mouseup ), false );
- this.domElement.addEventListener( 'keydown', bind( this, this.keydown ), false );
- this.domElement.addEventListener( 'keyup', bind( this, this.keyup ), false );
- this.updateMovementVector();
- this.updateRotationVector();
- };
- /**
- * @author mikael emtinger / http://gomo.se/
- * @author alteredq / http://alteredqualia.com/
- */
- THREE.RollControls = function ( object, domElement ) {
- this.object = object;
- this.domElement = ( domElement !== undefined ) ? domElement : document;
- // API
- this.mouseLook = true;
- this.autoForward = false;
- this.lookSpeed = 1;
- this.movementSpeed = 1;
- this.rollSpeed = 1;
- this.constrainVertical = [ -0.9, 0.9 ];
- // disable default target object behavior
- this.object.matrixAutoUpdate = false;
- // internals
- this.forward = new THREE.Vector3( 0, 0, 1 );
- this.roll = 0;
- var xTemp = new THREE.Vector3();
- var yTemp = new THREE.Vector3();
- var zTemp = new THREE.Vector3();
- var rollMatrix = new THREE.Matrix4();
- var doRoll = false, rollDirection = 1, forwardSpeed = 0, sideSpeed = 0, upSpeed = 0;
- var mouseX = 0, mouseY = 0;
- var windowHalfX = 0;
- var windowHalfY = 0;
- //
- this.handleResize = function () {
- windowHalfX = window.innerWidth / 2;
- windowHalfY = window.innerHeight / 2;
- };
- // custom update
- this.update = function ( delta ) {
- if ( this.mouseLook ) {
- var actualLookSpeed = delta * this.lookSpeed;
- this.rotateHorizontally( actualLookSpeed * mouseX );
- this.rotateVertically( actualLookSpeed * mouseY );
- }
- var actualSpeed = delta * this.movementSpeed;
- var forwardOrAuto = ( forwardSpeed > 0 || ( this.autoForward && ! ( forwardSpeed < 0 ) ) ) ? 1 : forwardSpeed;
- this.object.translateZ( -actualSpeed * forwardOrAuto );
- this.object.translateX( actualSpeed * sideSpeed );
- this.object.translateY( actualSpeed * upSpeed );
- if( doRoll ) {
- this.roll += this.rollSpeed * delta * rollDirection;
- }
- // cap forward up / down
- if( this.forward.y > this.constrainVertical[ 1 ] ) {
- this.forward.y = this.constrainVertical[ 1 ];
- this.forward.normalize();
- } else if( this.forward.y < this.constrainVertical[ 0 ] ) {
- this.forward.y = this.constrainVertical[ 0 ];
- this.forward.normalize();
- }
- // construct unrolled camera matrix
- zTemp.copy( this.forward );
- yTemp.set( 0, 1, 0 );
- xTemp.cross( yTemp, zTemp ).normalize();
- yTemp.cross( zTemp, xTemp ).normalize();
- this.object.matrix.elements[0] = xTemp.x; this.object.matrix.elements[4] = yTemp.x; this.object.matrix.elements[8] = zTemp.x;
- this.object.matrix.elements[1] = xTemp.y; this.object.matrix.elements[5] = yTemp.y; this.object.matrix.elements[9] = zTemp.y;
- this.object.matrix.elements[2] = xTemp.z; this.object.matrix.elements[6] = yTemp.z; this.object.matrix.elements[10] = zTemp.z;
- // calculate roll matrix
- rollMatrix.identity();
- rollMatrix.elements[0] = Math.cos( this.roll ); rollMatrix.elements[4] = -Math.sin( this.roll );
- rollMatrix.elements[1] = Math.sin( this.roll ); rollMatrix.elements[5] = Math.cos( this.roll );
- // multiply camera with roll
- this.object.matrix.multiplySelf( rollMatrix );
- this.object.matrixWorldNeedsUpdate = true;
- // set position
- this.object.matrix.elements[12] = this.object.position.x;
- this.object.matrix.elements[13] = this.object.position.y;
- this.object.matrix.elements[14] = this.object.position.z;
- };
- this.translateX = function ( distance ) {
- this.object.position.x += this.object.matrix.elements[0] * distance;
- this.object.position.y += this.object.matrix.elements[1] * distance;
- this.object.position.z += this.object.matrix.elements[2] * distance;
- };
- this.translateY = function ( distance ) {
- this.object.position.x += this.object.matrix.elements[4] * distance;
- this.object.position.y += this.object.matrix.elements[5] * distance;
- this.object.position.z += this.object.matrix.elements[6] * distance;
- };
- this.translateZ = function ( distance ) {
- this.object.position.x -= this.object.matrix.elements[8] * distance;
- this.object.position.y -= this.object.matrix.elements[9] * distance;
- this.object.position.z -= this.object.matrix.elements[10] * distance;
- };
- this.rotateHorizontally = function ( amount ) {
- // please note that the amount is NOT degrees, but a scale value
- xTemp.set( this.object.matrix.elements[0], this.object.matrix.elements[1], this.object.matrix.elements[2] );
- xTemp.multiplyScalar( amount );
- this.forward.subSelf( xTemp );
- this.forward.normalize();
- };
- this.rotateVertically = function ( amount ) {
- // please note that the amount is NOT degrees, but a scale value
- yTemp.set( this.object.matrix.elements[4], this.object.matrix.elements[5], this.object.matrix.elements[6] );
- yTemp.multiplyScalar( amount );
- this.forward.addSelf( yTemp );
- this.forward.normalize();
- };
- function onKeyDown( event ) {
- //event.preventDefault();
- switch ( event.keyCode ) {
- case 38: /*up*/
- case 87: /*W*/ forwardSpeed = 1; break;
- case 37: /*left*/
- case 65: /*A*/ sideSpeed = -1; break;
- case 40: /*down*/
- case 83: /*S*/ forwardSpeed = -1; break;
- case 39: /*right*/
- case 68: /*D*/ sideSpeed = 1; break;
- case 81: /*Q*/ doRoll = true; rollDirection = 1; break;
- case 69: /*E*/ doRoll = true; rollDirection = -1; break;
- case 82: /*R*/ upSpeed = 1; break;
- case 70: /*F*/ upSpeed = -1; break;
- }
- };
- function onKeyUp( event ) {
- switch( event.keyCode ) {
- case 38: /*up*/
- case 87: /*W*/ forwardSpeed = 0; break;
- case 37: /*left*/
- case 65: /*A*/ sideSpeed = 0; break;
- case 40: /*down*/
- case 83: /*S*/ forwardSpeed = 0; break;
- case 39: /*right*/
- case 68: /*D*/ sideSpeed = 0; break;
- case 81: /*Q*/ doRoll = false; break;
- case 69: /*E*/ doRoll = false; break;
- case 82: /*R*/ upSpeed = 0; break;
- case 70: /*F*/ upSpeed = 0; break;
- }
- };
- function onMouseMove( event ) {
- mouseX = ( event.clientX - windowHalfX ) / window.innerWidth;
- mouseY = ( event.clientY - windowHalfY ) / window.innerHeight;
- };
- function onMouseDown ( event ) {
- event.preventDefault();
- event.stopPropagation();
- switch ( event.button ) {
- case 0: forwardSpeed = 1; break;
- case 2: forwardSpeed = -1; break;
- }
- };
- function onMouseUp ( event ) {
- event.preventDefault();
- event.stopPropagation();
- switch ( event.button ) {
- case 0: forwardSpeed = 0; break;
- case 2: forwardSpeed = 0; break;
- }
- };
- this.domElement.addEventListener( 'contextmenu', function ( event ) { event.preventDefault(); }, false );
- this.domElement.addEventListener( 'mousemove', onMouseMove, false );
- this.domElement.addEventListener( 'mousedown', onMouseDown, false );
- this.domElement.addEventListener( 'mouseup', onMouseUp, false );
- this.domElement.addEventListener( 'keydown', onKeyDown, false );
- this.domElement.addEventListener( 'keyup', onKeyUp, false );
- this.handleResize();
- };
- /**
- * @author Eberhard Graether / http://egraether.com/
- */
- THREE.TrackballControls = function ( object, domElement ) {
- THREE.EventTarget.call( this );
- var _this = this,
- STATE = { NONE : -1, ROTATE : 0, ZOOM : 1, PAN : 2 };
- this.object = object;
- this.domElement = ( domElement !== undefined ) ? domElement : document;
- // API
- this.enabled = true;
- this.screen = { width: 0, height: 0, offsetLeft: 0, offsetTop: 0 };
- this.radius = ( this.screen.width + this.screen.height ) / 4;
- this.rotateSpeed = 1.0;
- this.zoomSpeed = 1.2;
- this.panSpeed = 0.3;
- this.noRotate = false;
- this.noZoom = false;
- this.noPan = false;
- this.staticMoving = false;
- this.dynamicDampingFactor = 0.2;
- this.minDistance = 0;
- this.maxDistance = Infinity;
- this.keys = [ 65 /*A*/, 83 /*S*/, 68 /*D*/ ];
- // internals
- this.target = new THREE.Vector3();
- var lastPosition = new THREE.Vector3();
- var _keyPressed = false,
- _state = STATE.NONE,
- _eye = new THREE.Vector3(),
- _rotateStart = new THREE.Vector3(),
- _rotateEnd = new THREE.Vector3(),
- _zoomStart = new THREE.Vector2(),
- _zoomEnd = new THREE.Vector2(),
- _panStart = new THREE.Vector2(),
- _panEnd = new THREE.Vector2();
- // events
- var changeEvent = { type: 'change' };
- // methods
- this.handleResize = function () {
- this.screen.width = window.innerWidth;
- this.screen.height = window.innerHeight;
- this.screen.offsetLeft = 0;
- this.screen.offsetTop = 0;
- this.radius = ( this.screen.width + this.screen.height ) / 4;
- };
- this.handleEvent = function ( event ) {
- if ( typeof this[ event.type ] == 'function' ) {
- this[ event.type ]( event );
- }
- };
- this.getMouseOnScreen = function ( clientX, clientY ) {
- return new THREE.Vector2(
- ( clientX - _this.screen.offsetLeft ) / _this.radius * 0.5,
- ( clientY - _this.screen.offsetTop ) / _this.radius * 0.5
- );
- };
- this.getMouseProjectionOnBall = function ( clientX, clientY ) {
- var mouseOnBall = new THREE.Vector3(
- ( clientX - _this.screen.width * 0.5 - _this.screen.offsetLeft ) / _this.radius,
- ( _this.screen.height * 0.5 + _this.screen.offsetTop - clientY ) / _this.radius,
- 0.0
- );
- var length = mouseOnBall.length();
- if ( length > 1.0 ) {
- mouseOnBall.normalize();
- } else {
- mouseOnBall.z = Math.sqrt( 1.0 - length * length );
- }
- _eye.copy( _this.object.position ).subSelf( _this.target );
- var projection = _this.object.up.clone().setLength( mouseOnBall.y );
- projection.addSelf( _this.object.up.clone().crossSelf( _eye ).setLength( mouseOnBall.x ) );
- projection.addSelf( _eye.setLength( mouseOnBall.z ) );
- return projection;
- };
- this.rotateCamera = function () {
- var angle = Math.acos( _rotateStart.dot( _rotateEnd ) / _rotateStart.length() / _rotateEnd.length() );
- if ( angle ) {
- var axis = ( new THREE.Vector3() ).cross( _rotateStart, _rotateEnd ).normalize(),
- quaternion = new THREE.Quaternion();
- angle *= _this.rotateSpeed;
- quaternion.setFromAxisAngle( axis, -angle );
- quaternion.multiplyVector3( _eye );
- quaternion.multiplyVector3( _this.object.up );
- quaternion.multiplyVector3( _rotateEnd );
- if ( _this.staticMoving ) {
- _rotateStart = _rotateEnd;
- } else {
- quaternion.setFromAxisAngle( axis, angle * ( _this.dynamicDampingFactor - 1.0 ) );
- quaternion.multiplyVector3( _rotateStart );
- }
- }
- };
- this.zoomCamera = function () {
- var factor = 1.0 + ( _zoomEnd.y - _zoomStart.y ) * _this.zoomSpeed;
- if ( factor !== 1.0 && factor > 0.0 ) {
- _eye.multiplyScalar( factor );
- if ( _this.staticMoving ) {
- _zoomStart = _zoomEnd;
- } else {
- _zoomStart.y += ( _zoomEnd.y - _zoomStart.y ) * this.dynamicDampingFactor;
- }
- }
- };
- this.panCamera = function () {
- var mouseChange = _panEnd.clone().subSelf( _panStart );
- if ( mouseChange.lengthSq() ) {
- mouseChange.multiplyScalar( _eye.length() * _this.panSpeed );
- var pan = _eye.clone().crossSelf( _this.object.up ).setLength( mouseChange.x );
- pan.addSelf( _this.object.up.clone().setLength( mouseChange.y ) );
- _this.object.position.addSelf( pan );
- _this.target.addSelf( pan );
- if ( _this.staticMoving ) {
- _panStart = _panEnd;
- } else {
- _panStart.addSelf( mouseChange.sub( _panEnd, _panStart ).multiplyScalar( _this.dynamicDampingFactor ) );
- }
- }
- };
- this.checkDistances = function () {
- if ( !_this.noZoom || !_this.noPan ) {
- if ( _this.object.position.lengthSq() > _this.maxDistance * _this.maxDistance ) {
- _this.object.position.setLength( _this.maxDistance );
- }
- if ( _eye.lengthSq() < _this.minDistance * _this.minDistance ) {
- _this.object.position.add( _this.target, _eye.setLength( _this.minDistance ) );
- }
- }
- };
- this.update = function () {
- _eye.copy( _this.object.position ).subSelf( _this.target );
- if ( !_this.noRotate ) {
- _this.rotateCamera();
- }
- if ( !_this.noZoom ) {
- _this.zoomCamera();
- }
- if ( !_this.noPan ) {
- _this.panCamera();
- }
- _this.object.position.add( _this.target, _eye );
- _this.checkDistances();
- _this.object.lookAt( _this.target );
- if ( lastPosition.distanceToSquared( _this.object.position ) > 0 ) {
- _this.dispatchEvent( changeEvent );
- lastPosition.copy( _this.object.position );
- }
- };
- // listeners
- function keydown( event ) {
- if ( ! _this.enabled ) return;
- //event.preventDefault();
- if ( _state !== STATE.NONE ) {
- return;
- } else if ( event.keyCode === _this.keys[ STATE.ROTATE ] && !_this.noRotate ) {
- _state = STATE.ROTATE;
- } else if ( event.keyCode === _this.keys[ STATE.ZOOM ] && !_this.noZoom ) {
- _state = STATE.ZOOM;
- } else if ( event.keyCode === _this.keys[ STATE.PAN ] && !_this.noPan ) {
- _state = STATE.PAN;
- }
- if ( _state !== STATE.NONE ) {
- _keyPressed = true;
- }
- }
- function keyup( event ) {
- if ( ! _this.enabled ) return;
- if ( _state !== STATE.NONE ) {
- _state = STATE.NONE;
- }
- }
- function mousedown( event ) {
- if ( ! _this.enabled ) return;
- event.preventDefault();
- event.stopPropagation();
- if ( _state === STATE.NONE ) {
- _state = event.button;
- if ( _state === STATE.ROTATE && !_this.noRotate ) {
- _rotateStart = _rotateEnd = _this.getMouseProjectionOnBall( event.clientX, event.clientY );
- } else if ( _state === STATE.ZOOM && !_this.noZoom ) {
- _zoomStart = _zoomEnd = _this.getMouseOnScreen( event.clientX, event.clientY );
- } else if ( !this.noPan ) {
- _panStart = _panEnd = _this.getMouseOnScreen( event.clientX, event.clientY );
- }
- }
- }
- function mousemove( event ) {
- if ( ! _this.enabled ) return;
- if ( _keyPressed ) {
- _rotateStart = _rotateEnd = _this.getMouseProjectionOnBall( event.clientX, event.clientY );
- _zoomStart = _zoomEnd = _this.getMouseOnScreen( event.clientX, event.clientY );
- _panStart = _panEnd = _this.getMouseOnScreen( event.clientX, event.clientY );
- _keyPressed = false;
- }
- if ( _state === STATE.NONE ) {
- return;
- } else if ( _state === STATE.ROTATE && !_this.noRotate ) {
- _rotateEnd = _this.getMouseProjectionOnBall( event.clientX, event.clientY );
- } else if ( _state === STATE.ZOOM && !_this.noZoom ) {
- _zoomEnd = _this.getMouseOnScreen( event.clientX, event.clientY );
- } else if ( _state === STATE.PAN && !_this.noPan ) {
- _panEnd = _this.getMouseOnScreen( event.clientX, event.clientY );
- }
- }
- function mouseup( event ) {
- if ( ! _this.enabled ) return;
- event.preventDefault();
- event.stopPropagation();
- _state = STATE.NONE;
- }
- this.domElement.addEventListener( 'contextmenu', function ( event ) { event.preventDefault(); }, false );
- this.domElement.addEventListener( 'mousemove', mousemove, false );
- this.domElement.addEventListener( 'mousedown', mousedown, false );
- this.domElement.addEventListener( 'mouseup', mouseup, false );
- // this.domElement.addEventListener( 'DOMMouseScroll', mousewheel, false );
- // this.domElement.addEventListener( 'mousewheel', mousewheel, false );
- window.addEventListener( 'keydown', keydown, false );
- window.addEventListener( 'keyup', keyup, false );
- this.handleResize();
- };
- /**
- * @author qiao / https://github.com/qiao
- * @author mrdoob / http://mrdoob.com
- * @author alteredq / http://alteredqualia.com/
- */
- THREE.OrbitControls = function ( object, domElement ) {
- THREE.EventTarget.call( this );
- this.object = object;
- this.domElement = ( domElement !== undefined ) ? domElement : document;
- // API
- this.center = new THREE.Vector3();
- this.userZoom = true;
- this.userZoomSpeed = 1.0;
- this.userRotate = true;
- this.userRotateSpeed = 1.0;
- this.autoRotate = false;
- this.autoRotateSpeed = 2.0; // 30 seconds per round when fps is 60
- // internals
- var scope = this;
- var EPS = 0.000001;
- var PIXELS_PER_ROUND = 1800;
- var rotateStart = new THREE.Vector2();
- var rotateEnd = new THREE.Vector2();
- var rotateDelta = new THREE.Vector2();
- var zoomStart = new THREE.Vector2();
- var zoomEnd = new THREE.Vector2();
- var zoomDelta = new THREE.Vector2();
- var phiDelta = 0;
- var thetaDelta = 0;
- var scale = 1;
- var lastPosition = new THREE.Vector3();
- var STATE = { NONE : -1, ROTATE : 0, ZOOM : 1 };
- var state = STATE.NONE;
- // events
- var changeEvent = { type: 'change' };
- this.rotateLeft = function ( angle ) {
- if ( angle === undefined ) {
- angle = getAutoRotationAngle();
- }
- thetaDelta -= angle;
- };
- this.rotateRight = function ( angle ) {
- if ( angle === undefined ) {
- angle = getAutoRotationAngle();
- }
- thetaDelta += angle;
- };
- this.rotateUp = function ( angle ) {
- if ( angle === undefined ) {
- angle = getAutoRotationAngle();
- }
- phiDelta -= angle;
- };
- this.rotateDown = function ( angle ) {
- if ( angle === undefined ) {
- angle = getAutoRotationAngle();
- }
- phiDelta += angle;
- };
- this.zoomIn = function ( zoomScale ) {
- if ( zoomScale === undefined ) {
- zoomScale = getZoomScale();
- }
- scale /= zoomScale;
- };
- this.zoomOut = function ( zoomScale ) {
- if ( zoomScale === undefined ) {
- zoomScale = getZoomScale();
- }
- scale *= zoomScale;
- };
- this.update = function () {
- var position = this.object.position;
- var offset = position.clone().subSelf( this.center )
- // angle from z-axis around y-axis
- var theta = Math.atan2( offset.x, offset.z );
- // angle from y-axis
- var phi = Math.atan2( Math.sqrt( offset.x * offset.x + offset.z * offset.z ), offset.y );
- if ( this.autoRotate ) {
- this.rotateLeft( getAutoRotationAngle() );
- }
- theta += thetaDelta;
- phi += phiDelta;
- // restrict phi to be betwee EPS and PI-EPS
- phi = Math.max( EPS, Math.min( Math.PI - EPS, phi ) );
- var radius = offset.length();
- offset.x = radius * Math.sin( phi ) * Math.sin( theta );
- offset.y = radius * Math.cos( phi );
- offset.z = radius * Math.sin( phi ) * Math.cos( theta );
- offset.multiplyScalar( scale );
- position.copy( this.center ).addSelf( offset );
- this.object.lookAt( this.center );
- thetaDelta = 0;
- phiDelta = 0;
- scale = 1;
- if ( lastPosition.distanceTo( this.object.position ) > 0 ) {
- this.dispatchEvent( changeEvent );
- lastPosition.copy( this.object.position );
- }
- };
- function getAutoRotationAngle() {
- return 2 * Math.PI / 60 / 60 * scope.autoRotateSpeed;
- }
- function getZoomScale() {
- return Math.pow( 0.95, scope.userZoomSpeed );
- }
- function onMouseDown( event ) {
- if ( !scope.userRotate ) return;
- event.preventDefault();
- if ( event.button === 0 || event.button === 2 ) {
- state = STATE.ROTATE;
- rotateStart.set( event.clientX, event.clientY );
- } else if ( event.button === 1 ) {
- state = STATE.ZOOM;
- zoomStart.set( event.clientX, event.clientY );
- }
- document.addEventListener( 'mousemove', onMouseMove, false );
- document.addEventListener( 'mouseup', onMouseUp, false );
- }
- function onMouseMove( event ) {
- event.preventDefault();
- if ( state === STATE.ROTATE ) {
- rotateEnd.set( event.clientX, event.clientY );
- rotateDelta.sub( rotateEnd, rotateStart );
- scope.rotateLeft( 2 * Math.PI * rotateDelta.x / PIXELS_PER_ROUND * scope.userRotateSpeed );
- scope.rotateUp( 2 * Math.PI * rotateDelta.y / PIXELS_PER_ROUND * scope.userRotateSpeed );
- rotateStart.copy( rotateEnd );
- } else if ( state === STATE.ZOOM ) {
- zoomEnd.set( event.clientX, event.clientY );
- zoomDelta.sub( zoomEnd, zoomStart );
- if ( zoomDelta.y > 0 ) {
- scope.zoomIn();
- } else {
- scope.zoomOut();
- }
- zoomStart.copy( zoomEnd );
- }
- }
- function onMouseUp( event ) {
- if ( ! scope.userRotate ) return;
- document.removeEventListener( 'mousemove', onMouseMove, false );
- document.removeEventListener( 'mouseup', onMouseUp, false );
- state = STATE.NONE;
- }
- function onMouseWheel( event ) {
- if ( ! scope.userZoom ) return;
- if ( event.wheelDelta > 0 ) {
- scope.zoomOut();
- } else {
- scope.zoomIn();
- }
- }
- this.domElement.addEventListener( 'contextmenu', function ( event ) { event.preventDefault(); }, false );
- this.domElement.addEventListener( 'mousedown', onMouseDown, false );
- this.domElement.addEventListener( 'mousewheel', onMouseWheel, false );
- };
- /**
- * @author mr.doob / http://mrdoob.com/
- * based on http://papervision3d.googlecode.com/svn/trunk/as3/trunk/src/org/papervision3d/objects/primitives/Cube.as
- */
- THREE.CubeGeometry = function ( width, height, depth, segmentsWidth, segmentsHeight, segmentsDepth, materials, sides ) {
- THREE.Geometry.call( this );
- var scope = this,
- width_half = width / 2,
- height_half = height / 2,
- depth_half = depth / 2;
- var mpx, mpy, mpz, mnx, mny, mnz;
- if ( materials !== undefined ) {
- if ( materials instanceof Array ) {
- this.materials = materials;
- } else {
- this.materials = [];
- for ( var i = 0; i < 6; i ++ ) {
- this.materials.push( materials );
- }
- }
- mpx = 0; mnx = 1; mpy = 2; mny = 3; mpz = 4; mnz = 5;
- } else {
- this.materials = [];
- }
- this.sides = { px: true, nx: true, py: true, ny: true, pz: true, nz: true };
- if ( sides != undefined ) {
- for ( var s in sides ) {
- if ( this.sides[ s ] !== undefined ) {
- this.sides[ s ] = sides[ s ];
- }
- }
- }
- this.sides.px && buildPlane( 'z', 'y', - 1, - 1, depth, height, width_half, mpx ); // px
- this.sides.nx && buildPlane( 'z', 'y', 1, - 1, depth, height, - width_half, mnx ); // nx
- this.sides.py && buildPlane( 'x', 'z', 1, 1, width, depth, height_half, mpy ); // py
- this.sides.ny && buildPlane( 'x', 'z', 1, - 1, width, depth, - height_half, mny ); // ny
- this.sides.pz && buildPlane( 'x', 'y', 1, - 1, width, height, depth_half, mpz ); // pz
- this.sides.nz && buildPlane( 'x', 'y', - 1, - 1, width, height, - depth_half, mnz ); // nz
- function buildPlane( u, v, udir, vdir, width, height, depth, material ) {
- var w, ix, iy,
- gridX = segmentsWidth || 1,
- gridY = segmentsHeight || 1,
- width_half = width / 2,
- height_half = height / 2,
- offset = scope.vertices.length;
- if ( ( u === 'x' && v === 'y' ) || ( u === 'y' && v === 'x' ) ) {
- w = 'z';
- } else if ( ( u === 'x' && v === 'z' ) || ( u === 'z' && v === 'x' ) ) {
- w = 'y';
- gridY = segmentsDepth || 1;
- } else if ( ( u === 'z' && v === 'y' ) || ( u === 'y' && v === 'z' ) ) {
- w = 'x';
- gridX = segmentsDepth || 1;
- }
- var gridX1 = gridX + 1,
- gridY1 = gridY + 1,
- segment_width = width / gridX,
- segment_height = height / gridY,
- normal = new THREE.Vector3();
- normal[ w ] = depth > 0 ? 1 : - 1;
- for ( iy = 0; iy < gridY1; iy ++ ) {
- for ( ix = 0; ix < gridX1; ix ++ ) {
- var vector = new THREE.Vector3();
- vector[ u ] = ( ix * segment_width - width_half ) * udir;
- vector[ v ] = ( iy * segment_height - height_half ) * vdir;
- vector[ w ] = depth;
- scope.vertices.push( vector );
- }
- }
- for ( iy = 0; iy < gridY; iy++ ) {
- for ( ix = 0; ix < gridX; ix++ ) {
- var a = ix + gridX1 * iy;
- var b = ix + gridX1 * ( iy + 1 );
- var c = ( ix + 1 ) + gridX1 * ( iy + 1 );
- var d = ( ix + 1 ) + gridX1 * iy;
- var face = new THREE.Face4( a + offset, b + offset, c + offset, d + offset );
- face.normal.copy( normal );
- face.vertexNormals.push( normal.clone(), normal.clone(), normal.clone(), normal.clone() );
- face.materialIndex = material;
- scope.faces.push( face );
- scope.faceVertexUvs[ 0 ].push( [
- new THREE.UV( ix / gridX, 1 - iy / gridY ),
- new THREE.UV( ix / gridX, 1 - ( iy + 1 ) / gridY ),
- new THREE.UV( ( ix + 1 ) / gridX, 1- ( iy + 1 ) / gridY ),
- new THREE.UV( ( ix + 1 ) / gridX, 1 - iy / gridY )
- ] );
- }
- }
- }
- this.computeCentroids();
- this.mergeVertices();
- };
- THREE.CubeGeometry.prototype = Object.create( THREE.Geometry.prototype );
- /**
- * @author mr.doob / http://mrdoob.com/
- */
- THREE.CylinderGeometry = function ( radiusTop, radiusBottom, height, segmentsRadius, segmentsHeight, openEnded ) {
- THREE.Geometry.call( this );
- radiusTop = radiusTop !== undefined ? radiusTop : 20;
- radiusBottom = radiusBottom !== undefined ? radiusBottom : 20;
- height = height !== undefined ? height : 100;
- var heightHalf = height / 2;
- var segmentsX = segmentsRadius || 8;
- var segmentsY = segmentsHeight || 1;
- var x, y, vertices = [], uvs = [];
- for ( y = 0; y <= segmentsY; y ++ ) {
- var verticesRow = [];
- var uvsRow = [];
- var v = y / segmentsY;
- var radius = v * ( radiusBottom - radiusTop ) + radiusTop;
- for ( x = 0; x <= segmentsX; x ++ ) {
- var u = x / segmentsX;
- var vertex = new THREE.Vector3();
- vertex.x = radius * Math.sin( u * Math.PI * 2 );
- vertex.y = - v * height + heightHalf;
- vertex.z = radius * Math.cos( u * Math.PI * 2 );
- this.vertices.push( vertex );
- verticesRow.push( this.vertices.length - 1 );
- uvsRow.push( new THREE.UV( u, v ) );
- }
- vertices.push( verticesRow );
- uvs.push( uvsRow );
- }
- var tanTheta = ( radiusBottom - radiusTop ) / height;
- var na, nb;
- for ( x = 0; x < segmentsX; x ++ ) {
- if ( radiusTop !== 0 ) {
- na = this.vertices[ vertices[ 0 ][ x ] ].clone();
- nb = this.vertices[ vertices[ 0 ][ x + 1 ] ].clone();
- } else {
- na = this.vertices[ vertices[ 1 ][ x ] ].clone();
- nb = this.vertices[ vertices[ 1 ][ x + 1 ] ].clone();
- }
-
- na.setY( Math.sqrt( na.x * na.x + na.z * na.z ) * tanTheta ).normalize();
- nb.setY( Math.sqrt( nb.x * nb.x + nb.z * nb.z ) * tanTheta ).normalize();
- for ( y = 0; y < segmentsY; y ++ ) {
- var v1 = vertices[ y ][ x ];
- var v2 = vertices[ y + 1 ][ x ];
- var v3 = vertices[ y + 1 ][ x + 1 ];
- var v4 = vertices[ y ][ x + 1 ];
- var n1 = na.clone();
- var n2 = na.clone();
- var n3 = nb.clone();
- var n4 = nb.clone();
- var uv1 = uvs[ y ][ x ].clone();
- var uv2 = uvs[ y + 1 ][ x ].clone();
- var uv3 = uvs[ y + 1 ][ x + 1 ].clone();
- var uv4 = uvs[ y ][ x + 1 ].clone();
- this.faces.push( new THREE.Face4( v1, v2, v3, v4, [ n1, n2, n3, n4 ] ) );
- this.faceVertexUvs[ 0 ].push( [ uv1, uv2, uv3, uv4 ] );
- }
- }
- // top cap
- if ( !openEnded && radiusTop > 0 ) {
- this.vertices.push( new THREE.Vector3( 0, heightHalf, 0 ) );
- for ( x = 0; x < segmentsX; x ++ ) {
- var v1 = vertices[ 0 ][ x ];
- var v2 = vertices[ 0 ][ x + 1 ];
- var v3 = this.vertices.length - 1;
- var n1 = new THREE.Vector3( 0, 1, 0 );
- var n2 = new THREE.Vector3( 0, 1, 0 );
- var n3 = new THREE.Vector3( 0, 1, 0 );
- var uv1 = uvs[ 0 ][ x ].clone();
- var uv2 = uvs[ 0 ][ x + 1 ].clone();
- var uv3 = new THREE.UV( uv2.u, 0 );
- this.faces.push( new THREE.Face3( v1, v2, v3, [ n1, n2, n3 ] ) );
- this.faceVertexUvs[ 0 ].push( [ uv1, uv2, uv3 ] );
- }
- }
- // bottom cap
- if ( !openEnded && radiusBottom > 0 ) {
- this.vertices.push( new THREE.Vector3( 0, - heightHalf, 0 ) );
- for ( x = 0; x < segmentsX; x ++ ) {
- var v1 = vertices[ y ][ x + 1 ];
- var v2 = vertices[ y ][ x ];
- var v3 = this.vertices.length - 1;
- var n1 = new THREE.Vector3( 0, - 1, 0 );
- var n2 = new THREE.Vector3( 0, - 1, 0 );
- var n3 = new THREE.Vector3( 0, - 1, 0 );
- var uv1 = uvs[ y ][ x + 1 ].clone();
- var uv2 = uvs[ y ][ x ].clone();
- var uv3 = new THREE.UV( uv2.u, 1 );
- this.faces.push( new THREE.Face3( v1, v2, v3, [ n1, n2, n3 ] ) );
- this.faceVertexUvs[ 0 ].push( [ uv1, uv2, uv3 ] );
- }
- }
- this.computeCentroids();
- this.computeFaceNormals();
- }
- THREE.CylinderGeometry.prototype = Object.create( THREE.Geometry.prototype );
- /**
- * @author zz85 / http://www.lab4games.net/zz85/blog
- *
- * Creates extruded geometry from a path shape.
- *
- * parameters = {
- *
- * size: <float>, // size of the text
- * height: <float>, // thickness to extrude text
- * curveSegments: <int>, // number of points on the curves
- * steps: <int>, // number of points for z-side extrusions / used for subdividing segements of extrude spline too
- amount: <int>, // Amount
- *
- * bevelEnabled: <bool>, // turn on bevel
- * bevelThickness: <float>, // how deep into text bevel goes
- * bevelSize: <float>, // how far from text outline is bevel
- * bevelSegments: <int>, // number of bevel layers
- *
- * extrudePath: <THREE.CurvePath> // 3d spline path to extrude shape along. (creates Frames if .frames aren't defined)
- * frames: <THREE.TubeGeometry.FrenetFrames> // containing arrays of tangents, normals, binormals
- * bendPath: <THREE.CurvePath> // 2d path for bend the shape around x/y plane
- *
- * material: <int> // material index for front and back faces
- * extrudeMaterial: <int> // material index for extrusion and beveled faces
- * uvGenerator: <Object> // object that provides UV generator functions
- *
- * }
- **/
- THREE.ExtrudeGeometry = function( shapes, options ) {
- if ( typeof( shapes ) === "undefined" ) {
- shapes = [];
- return;
- }
- THREE.Geometry.call( this );
- shapes = shapes instanceof Array ? shapes : [ shapes ];
- this.shapebb = shapes[ shapes.length - 1 ].getBoundingBox();
- this.addShapeList( shapes, options );
- this.computeCentroids();
- this.computeFaceNormals();
- // can't really use automatic vertex normals
- // as then front and back sides get smoothed too
- // should do separate smoothing just for sides
- //this.computeVertexNormals();
- //console.log( "took", ( Date.now() - startTime ) );
- };
- THREE.ExtrudeGeometry.prototype = Object.create( THREE.Geometry.prototype );
- THREE.ExtrudeGeometry.prototype.addShapeList = function(shapes, options) {
- var sl = shapes.length;
-
- for ( var s = 0; s < sl; s ++ ) {
- var shape = shapes[ s ];
- this.addShape( shape, options );
- }
- };
- THREE.ExtrudeGeometry.prototype.addShape = function( shape, options ) {
- var amount = options.amount !== undefined ? options.amount : 100;
- var bevelThickness = options.bevelThickness !== undefined ? options.bevelThickness : 6; // 10
- var bevelSize = options.bevelSize !== undefined ? options.bevelSize : bevelThickness - 2; // 8
- var bevelSegments = options.bevelSegments !== undefined ? options.bevelSegments : 3;
- var bevelEnabled = options.bevelEnabled !== undefined ? options.bevelEnabled : true; // false
- var curveSegments = options.curveSegments !== undefined ? options.curveSegments : 12;
- var steps = options.steps !== undefined ? options.steps : 1;
- var bendPath = options.bendPath;
- var extrudePath = options.extrudePath;
- var extrudePts, extrudeByPath = false;
- var material = options.material;
- var extrudeMaterial = options.extrudeMaterial;
- // Use default WorldUVGenerator if no UV generators are specified.
- var uvgen = options.UVGenerator !== undefined ? options.UVGenerator : THREE.ExtrudeGeometry.WorldUVGenerator;
- var shapebb = this.shapebb;
- //shapebb = shape.getBoundingBox();
- var splineTube, binormal, normal, position2;
- if ( extrudePath ) {
- extrudePts = extrudePath.getSpacedPoints( steps );
- extrudeByPath = true;
- bevelEnabled = false; // bevels not supported for path extrusion
- // SETUP TNB variables
- // Reuse TNB from TubeGeomtry for now.
- // TODO1 - have a .isClosed in spline?
- splineTube = options.frames !== undefined ?
- options.frames :
- new THREE.TubeGeometry.FrenetFrames(extrudePath, steps, false);
- // console.log(splineTube, 'splineTube', splineTube.normals.length, 'steps', steps, 'extrudePts', extrudePts.length);
- binormal = new THREE.Vector3();
- normal = new THREE.Vector3();
- position2 = new THREE.Vector3();
- }
- // Safeguards if bevels are not enabled
- if ( ! bevelEnabled ) {
- bevelSegments = 0;
- bevelThickness = 0;
- bevelSize = 0;
- }
- // Variables initalization
- var ahole, h, hl; // looping of holes
- var scope = this;
- var bevelPoints = [];
- var shapesOffset = this.vertices.length;
- if ( bendPath ) {
- shape.addWrapPath( bendPath );
- }
- var shapePoints = shape.extractPoints();
- var vertices = shapePoints.shape;
- var holes = shapePoints.holes;
- var reverse = !THREE.Shape.Utils.isClockWise( vertices ) ;
- if ( reverse ) {
- vertices = vertices.reverse();
- // Maybe we should also check if holes are in the opposite direction, just to be safe ...
- for ( h = 0, hl = holes.length; h < hl; h ++ ) {
- ahole = holes[ h ];
- if ( THREE.Shape.Utils.isClockWise( ahole ) ) {
- holes[ h ] = ahole.reverse();
- }
- }
- reverse = false; // If vertices are in order now, we shouldn't need to worry about them again (hopefully)!
- }
- var faces = THREE.Shape.Utils.triangulateShape ( vertices, holes );
- //var faces = THREE.Shape.Utils.triangulate2( vertices, holes );
- // Would it be better to move points after triangulation?
- // shapePoints = shape.extractAllPointsWithBend( curveSegments, bendPath );
- // vertices = shapePoints.shape;
- // holes = shapePoints.holes;
- //console.log(faces);
- ////
- /// Handle Vertices
- ////
- var contour = vertices; // vertices has all points but contour has only points of circumference
- for ( h = 0, hl = holes.length; h < hl; h ++ ) {
- ahole = holes[ h ];
- vertices = vertices.concat( ahole );
- }
- function scalePt2 ( pt, vec, size ) {
- if ( !vec ) console.log( "die" );
- return vec.clone().multiplyScalar( size ).addSelf( pt );
- }
- var b, bs, t, z,
- vert, vlen = vertices.length,
- face, flen = faces.length,
- cont, clen = contour.length;
- //------
- // Find directions for point movement
- //
- var RAD_TO_DEGREES = 180 / Math.PI;
- function getBevelVec( pt_i, pt_j, pt_k ) {
- // Algorithm 2
- return getBevelVec2( pt_i, pt_j, pt_k );
- }
- function getBevelVec1( pt_i, pt_j, pt_k ) {
- var anglea = Math.atan2( pt_j.y - pt_i.y, pt_j.x - pt_i.x );
- var angleb = Math.atan2( pt_k.y - pt_i.y, pt_k.x - pt_i.x );
- if ( anglea > angleb ) {
- angleb += Math.PI * 2;
- }
- var anglec = ( anglea + angleb ) / 2;
- //console.log('angle1', anglea * RAD_TO_DEGREES,'angle2', angleb * RAD_TO_DEGREES, 'anglec', anglec *RAD_TO_DEGREES);
- var x = - Math.cos( anglec );
- var y = - Math.sin( anglec );
- var vec = new THREE.Vector2( x, y ); //.normalize();
- return vec;
- }
- function getBevelVec2( pt_i, pt_j, pt_k ) {
- var a = THREE.ExtrudeGeometry.__v1,
- b = THREE.ExtrudeGeometry.__v2,
- v_hat = THREE.ExtrudeGeometry.__v3,
- w_hat = THREE.ExtrudeGeometry.__v4,
- p = THREE.ExtrudeGeometry.__v5,
- q = THREE.ExtrudeGeometry.__v6,
- v, w,
- v_dot_w_hat, q_sub_p_dot_w_hat,
- s, intersection;
- // good reading for line-line intersection
- // http://sputsoft.com/blog/2010/03/line-line-intersection.html
- // define a as vector j->i
- // define b as vectot k->i
- a.set( pt_i.x - pt_j.x, pt_i.y - pt_j.y );
- b.set( pt_i.x - pt_k.x, pt_i.y - pt_k.y );
- // get unit vectors
- v = a.normalize();
- w = b.normalize();
- // normals from pt i
- v_hat.set( -v.y, v.x );
- w_hat.set( w.y, -w.x );
- // pts from i
- p.copy( pt_i ).addSelf( v_hat );
- q.copy( pt_i ).addSelf( w_hat );
- if ( p.equals( q ) ) {
- //console.log("Warning: lines are straight");
- return w_hat.clone();
- }
- // Points from j, k. helps prevents points cross overover most of the time
- p.copy( pt_j ).addSelf( v_hat );
- q.copy( pt_k ).addSelf( w_hat );
- v_dot_w_hat = v.dot( w_hat );
- q_sub_p_dot_w_hat = q.subSelf( p ).dot( w_hat );
- // We should not reach these conditions
- if ( v_dot_w_hat === 0 ) {
- console.log( "Either infinite or no solutions!" );
- if ( q_sub_p_dot_w_hat === 0 ) {
- console.log( "Its finite solutions." );
- } else {
- console.log( "Too bad, no solutions." );
- }
- }
- s = q_sub_p_dot_w_hat / v_dot_w_hat;
- if ( s < 0 ) {
- // in case of emergecy, revert to algorithm 1.
- return getBevelVec1( pt_i, pt_j, pt_k );
- }
- intersection = v.multiplyScalar( s ).addSelf( p );
- return intersection.subSelf( pt_i ).clone(); // Don't normalize!, otherwise sharp corners become ugly
- }
- var contourMovements = [];
- for ( var 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)
- var pt_i = contour[ i ];
- var pt_j = contour[ j ];
- var pt_k = contour[ k ];
- contourMovements[ i ]= getBevelVec( contour[ i ], contour[ j ], contour[ k ] );
- }
- var holesMovements = [], oneHoleMovements, verticesMovements = contourMovements.concat();
- for ( h = 0, hl = holes.length; h < hl; h ++ ) {
- ahole = holes[ h ];
- oneHoleMovements = [];
- for ( 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 ( b = 0; b < bevelSegments; b ++ ) {
- //for ( b = bevelSegments; b > 0; b -- ) {
- t = b / bevelSegments;
- z = bevelThickness * ( 1 - t );
- //z = bevelThickness * t;
- bs = bevelSize * ( Math.sin ( t * Math.PI/2 ) ) ; // curved
- //bs = bevelSize * t ; // linear
- // contract shape
- for ( i = 0, il = contour.length; i < il; i ++ ) {
- vert = scalePt2( contour[ i ], contourMovements[ i ], bs );
- //vert = scalePt( contour[ i ], contourCentroid, bs, false );
- v( vert.x, vert.y, - z );
- }
- // expand holes
- for ( h = 0, hl = holes.length; h < hl; h++ ) {
- ahole = holes[ h ];
- oneHoleMovements = holesMovements[ h ];
- for ( i = 0, il = ahole.length; i < il; i++ ) {
- vert = scalePt2( ahole[ i ], oneHoleMovements[ i ], bs );
- //vert = scalePt( ahole[ i ], holesCentroids[ h ], bs, true );
- v( vert.x, vert.y, -z );
- }
- }
- }
- bs = bevelSize;
- // Back facing vertices
- for ( i = 0; i < vlen; i ++ ) {
- 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]).addSelf(normal).addSelf(binormal);
-
- v(position2.x, position2.y, position2.z);
- }
- }
- // Add stepped vertices...
- // Including front facing vertices
- var s;
- for ( s = 1; s <= steps; s ++ ) {
- for ( i = 0; i < vlen; i ++ ) {
- vert = bevelEnabled ? scalePt2( vertices[ i ], verticesMovements[ i ], bs ) : vertices[ i ];
- if ( !extrudeByPath ) {
- v( vert.x, vert.y, amount / 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]).addSelf(normal).addSelf(binormal);
- v(position2.x, position2.y, position2.z );
- }
- }
- }
- // Add bevel segments planes
- //for ( b = 1; b <= bevelSegments; b ++ ) {
- for ( b = bevelSegments - 1; b >= 0; b -- ) {
- t = b / bevelSegments;
- z = bevelThickness * ( 1 - t );
- //bs = bevelSize * ( 1-Math.sin ( ( 1 - t ) * Math.PI/2 ) );
- bs = bevelSize * Math.sin ( t * Math.PI/2 ) ;
- // contract shape
- for ( i = 0, il = contour.length; i < il; i ++ ) {
- vert = scalePt2( contour[ i ], contourMovements[ i ], bs );
- v( vert.x, vert.y, amount + z );
- }
- // expand holes
- for ( h = 0, hl = holes.length; h < hl; h ++ ) {
- ahole = holes[ h ];
- oneHoleMovements = holesMovements[ h ];
- for ( i = 0, il = ahole.length; i < il; i++ ) {
- vert = scalePt2( ahole[ i ], oneHoleMovements[ i ], bs );
- if ( !extrudeByPath ) {
- v( vert.x, vert.y, amount + z );
- } else {
- v( vert.x, vert.y + extrudePts[ steps - 1 ].y, extrudePts[ steps - 1 ].x + z );
- }
- }
- }
- }
- ////
- /// Handle Faces
- ////
- // Top and bottom faces
- buildLidFaces();
- // Sides faces
- buildSideFaces();
- ///// Internal functions
- function buildLidFaces() {
- if ( bevelEnabled ) {
- var layer = 0 ; // steps + 1
- var offset = vlen * layer;
- // Bottom faces
-
- for ( i = 0; i < flen; i ++ ) {
- face = faces[ i ];
- f3( face[ 2 ]+ offset, face[ 1 ]+ offset, face[ 0 ] + offset, true );
- }
- layer = steps + bevelSegments * 2;
- offset = vlen * layer;
- // Top faces
- for ( i = 0; i < flen; i ++ ) {
- face = faces[ i ];
- f3( face[ 0 ] + offset, face[ 1 ] + offset, face[ 2 ] + offset, false );
- }
- } else {
-
- // Bottom faces
- for ( i = 0; i < flen; i++ ) {
- face = faces[ i ];
- f3( face[ 2 ], face[ 1 ], face[ 0 ], true );
- }
- // Top faces
- for ( i = 0; i < flen; i ++ ) {
- face = faces[ i ];
- f3( face[ 0 ] + vlen * steps, face[ 1 ] + vlen * steps, face[ 2 ] + vlen * steps, false );
- }
- }
- }
- // Create faces for the z-sides of the shape
- function buildSideFaces() {
- var layeroffset = 0;
- sidewalls( contour, layeroffset );
- layeroffset += contour.length;
- for ( h = 0, hl = holes.length; h < hl; h ++ ) {
- ahole = holes[ h ];
- sidewalls( ahole, layeroffset );
- //, true
- layeroffset += ahole.length;
- }
- }
- function sidewalls( contour, layeroffset ) {
- var j, k;
- i = contour.length;
- while ( --i >= 0 ) {
- j = i;
- k = i - 1;
- if ( k < 0 ) k = contour.length - 1;
- //console.log('b', i,j, i-1, k,vertices.length);
- var s = 0, sl = steps + bevelSegments * 2;
- for ( s = 0; s < sl; s ++ ) {
- var slen1 = vlen * s;
- var slen2 = vlen * ( s + 1 );
- var a = layeroffset + j + slen1,
- b = layeroffset + k + slen1,
- c = layeroffset + k + slen2,
- d = layeroffset + j + slen2;
- f4( a, b, c, d, contour, s, sl, j, k );
- }
- }
- }
- function v( x, y, z ) {
- scope.vertices.push( new THREE.Vector3( x, y, z ) );
- }
- function f3( a, b, c, isBottom ) {
- a += shapesOffset;
- b += shapesOffset;
- c += shapesOffset;
- // normal, color, material
- scope.faces.push( new THREE.Face3( a, b, c, null, null, material ) );
- var uvs = isBottom ? uvgen.generateBottomUV( scope, shape, options, a, b, c)
- : uvgen.generateTopUV( scope, shape, options, a, b, c);
- scope.faceVertexUvs[ 0 ].push(uvs);
- }
- function f4( a, b, c, d, wallContour, stepIndex, stepsLength, contourIndex1, contourIndex2 ) {
- a += shapesOffset;
- b += shapesOffset;
- c += shapesOffset;
- d += shapesOffset;
- scope.faces.push( new THREE.Face4( a, b, c, d, null, null, extrudeMaterial ) );
-
- var uvs = uvgen.generateSideWallUV( scope, shape, wallContour, options, a, b, c, d,
- stepIndex, stepsLength, contourIndex1, contourIndex2 );
- scope.faceVertexUvs[ 0 ].push(uvs);
- }
- };
- THREE.ExtrudeGeometry.WorldUVGenerator = {
- generateTopUV: function( geometry, extrudedShape, extrudeOptions, indexA, indexB, indexC) {
- var ax = geometry.vertices[ indexA ].x,
- ay = geometry.vertices[ indexA ].y,
- bx = geometry.vertices[ indexB ].x,
- by = geometry.vertices[ indexB ].y,
- cx = geometry.vertices[ indexC ].x,
- cy = geometry.vertices[ indexC ].y;
-
- return [
- new THREE.UV( ax, 1 - ay ),
- new THREE.UV( bx, 1 - by ),
- new THREE.UV( cx, 1 - cy )
- ];
- },
- generateBottomUV: function( geometry, extrudedShape, extrudeOptions, indexA, indexB, indexC) {
- return this.generateTopUV( geometry, extrudedShape, extrudeOptions, indexA, indexB, indexC );
- },
- generateSideWallUV: function( geometry, extrudedShape, wallContour, extrudeOptions,
- indexA, indexB, indexC, indexD, stepIndex, stepsLength,
- contourIndex1, contourIndex2 ) {
- var ax = geometry.vertices[ indexA ].x,
- ay = geometry.vertices[ indexA ].y,
- az = geometry.vertices[ indexA ].z,
- bx = geometry.vertices[ indexB ].x,
- by = geometry.vertices[ indexB ].y,
- bz = geometry.vertices[ indexB ].z,
- cx = geometry.vertices[ indexC ].x,
- cy = geometry.vertices[ indexC ].y,
- cz = geometry.vertices[ indexC ].z,
- dx = geometry.vertices[ indexD ].x,
- dy = geometry.vertices[ indexD ].y,
- dz = geometry.vertices[ indexD ].z;
-
- if ( Math.abs( ay - by ) < 0.01 ) {
- return [
- new THREE.UV( ax, az ),
- new THREE.UV( bx, bz ),
- new THREE.UV( cx, cz ),
- new THREE.UV( dx, dz )
- ];
- } else {
- return [
- new THREE.UV( ay, az ),
- new THREE.UV( by, bz ),
- new THREE.UV( cy, cz ),
- new THREE.UV( dy, dz )
- ];
- }
- }
- };
- THREE.ExtrudeGeometry.__v1 = new THREE.Vector2();
- THREE.ExtrudeGeometry.__v2 = new THREE.Vector2();
- THREE.ExtrudeGeometry.__v3 = new THREE.Vector2();
- THREE.ExtrudeGeometry.__v4 = new THREE.Vector2();
- THREE.ExtrudeGeometry.__v5 = new THREE.Vector2();
- THREE.ExtrudeGeometry.__v6 = new THREE.Vector2();
- /**
- * @author astrodud / http://astrodud.isgreat.org/
- * @author zz85 / https://github.com/zz85
- */
- THREE.LatheGeometry = function ( points, steps, angle ) {
- THREE.Geometry.call( this );
- var _steps = steps || 12;
- var _angle = angle || 2 * Math.PI;
- var _newV = [];
- var _matrix = new THREE.Matrix4().makeRotationZ( _angle / _steps );
- for ( var j = 0; j < points.length; j ++ ) {
- _newV[ j ] = points[ j ].clone();
- this.vertices.push( _newV[ j ] );
- }
- var i, il = _steps + 1;
- for ( i = 0; i < il; i ++ ) {
- for ( var j = 0; j < _newV.length; j ++ ) {
- _newV[ j ] = _matrix.multiplyVector3( _newV[ j ].clone() );
- this.vertices.push( _newV[ j ] );
- }
- }
- for ( i = 0; i < _steps; i ++ ) {
- for ( var k = 0, kl = points.length; k < kl - 1; k ++ ) {
- var a = i * kl + k;
- var b = ( ( i + 1 ) % il ) * kl + k;
- var c = ( ( i + 1 ) % il ) * kl + ( k + 1 ) % kl;
- var d = i * kl + ( k + 1 ) % kl;
- this.faces.push( new THREE.Face4( a, b, c, d ) );
- this.faceVertexUvs[ 0 ].push( [
- new THREE.UV( 1 - i / _steps, k / kl ),
- new THREE.UV( 1 - ( i + 1 ) / _steps, k / kl ),
- new THREE.UV( 1 - ( i + 1 ) / _steps, ( k + 1 ) / kl ),
- new THREE.UV( 1 - i / _steps, ( k + 1 ) / kl )
-
- ] );
- }
- }
- this.computeCentroids();
- this.computeFaceNormals();
- this.computeVertexNormals();
- };
- THREE.LatheGeometry.prototype = Object.create( THREE.Geometry.prototype );
- /**
- * @author mr.doob / http://mrdoob.com/
- * based on http://papervision3d.googlecode.com/svn/trunk/as3/trunk/src/org/papervision3d/objects/primitives/Plane.as
- */
- THREE.PlaneGeometry = function ( width, depth, segmentsWidth, segmentsDepth ) {
- THREE.Geometry.call( this );
- var ix, iz,
- width_half = width / 2,
- depth_half = depth / 2,
- gridX = segmentsWidth || 1,
- gridZ = segmentsDepth || 1,
- gridX1 = gridX + 1,
- gridZ1 = gridZ + 1,
- segment_width = width / gridX,
- segment_depth = depth / gridZ,
- normal = new THREE.Vector3( 0, 1, 0 );
- for ( iz = 0; iz < gridZ1; iz ++ ) {
- for ( ix = 0; ix < gridX1; ix ++ ) {
- var x = ix * segment_width - width_half;
- var z = iz * segment_depth - depth_half;
- this.vertices.push( new THREE.Vector3( x, 0, z ) );
- }
- }
- for ( iz = 0; iz < gridZ; iz ++ ) {
- for ( ix = 0; ix < gridX; ix ++ ) {
- var a = ix + gridX1 * iz;
- var b = ix + gridX1 * ( iz + 1 );
- var c = ( ix + 1 ) + gridX1 * ( iz + 1 );
- var d = ( ix + 1 ) + gridX1 * iz;
- var face = new THREE.Face4( a, b, c, d );
- face.normal.copy( normal );
- face.vertexNormals.push( normal.clone(), normal.clone(), normal.clone(), normal.clone() );
- this.faces.push( face );
- this.faceVertexUvs[ 0 ].push( [
- new THREE.UV( ix / gridX, 1 - iz / gridZ ),
- new THREE.UV( ix / gridX, 1 - ( iz + 1 ) / gridZ ),
- new THREE.UV( ( ix + 1 ) / gridX, 1 - ( iz + 1 ) / gridZ ),
- new THREE.UV( ( ix + 1 ) / gridX, 1 - iz / gridZ )
- ] );
- }
- }
- this.computeCentroids();
- };
- THREE.PlaneGeometry.prototype = Object.create( THREE.Geometry.prototype );
- /**
- * @author mr.doob / http://mrdoob.com/
- */
- THREE.SphereGeometry = function ( radius, segmentsWidth, segmentsHeight, phiStart, phiLength, thetaStart, thetaLength ) {
- THREE.Geometry.call( this );
- radius = radius || 50;
- phiStart = phiStart !== undefined ? phiStart : 0;
- phiLength = phiLength !== undefined ? phiLength : Math.PI * 2;
- thetaStart = thetaStart !== undefined ? thetaStart : 0;
- thetaLength = thetaLength !== undefined ? thetaLength : Math.PI;
- var segmentsX = Math.max( 3, Math.floor( segmentsWidth ) || 8 );
- var segmentsY = Math.max( 2, Math.floor( segmentsHeight ) || 6 );
- var x, y, vertices = [], uvs = [];
- for ( y = 0; y <= segmentsY; y ++ ) {
- var verticesRow = [];
- var uvsRow = [];
- for ( x = 0; x <= segmentsX; x ++ ) {
- var u = x / segmentsX;
- var v = y / segmentsY;
- var vertex = new THREE.Vector3();
- 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 );
- this.vertices.push( vertex );
- verticesRow.push( this.vertices.length - 1 );
- uvsRow.push( new THREE.UV( u, 1 - v ) );
- }
- vertices.push( verticesRow );
- uvs.push( uvsRow );
- }
- for ( y = 0; y < segmentsY; y ++ ) {
- for ( x = 0; x < segmentsX; x ++ ) {
- var v1 = vertices[ y ][ x + 1 ];
- var v2 = vertices[ y ][ x ];
- var v3 = vertices[ y + 1 ][ x ];
- var v4 = vertices[ y + 1 ][ x + 1 ];
- var n1 = this.vertices[ v1 ].clone().normalize();
- var n2 = this.vertices[ v2 ].clone().normalize();
- var n3 = this.vertices[ v3 ].clone().normalize();
- var n4 = this.vertices[ v4 ].clone().normalize();
- var uv1 = uvs[ y ][ x + 1 ].clone();
- var uv2 = uvs[ y ][ x ].clone();
- var uv3 = uvs[ y + 1 ][ x ].clone();
- var uv4 = uvs[ y + 1 ][ x + 1 ].clone();
- if ( Math.abs( this.vertices[ v1 ].y ) == radius ) {
- this.faces.push( new THREE.Face3( v1, v3, v4, [ n1, n3, n4 ] ) );
- this.faceVertexUvs[ 0 ].push( [ uv1, uv3, uv4 ] );
- } else if ( Math.abs( this.vertices[ v3 ].y ) == radius ) {
- this.faces.push( new THREE.Face3( v1, v2, v3, [ n1, n2, n3 ] ) );
- this.faceVertexUvs[ 0 ].push( [ uv1, uv2, uv3 ] );
- } else {
- this.faces.push( new THREE.Face4( v1, v2, v3, v4, [ n1, n2, n3, n4 ] ) );
- this.faceVertexUvs[ 0 ].push( [ uv1, uv2, uv3, uv4 ] );
- }
- }
- }
- this.computeCentroids();
- this.computeFaceNormals();
- this.boundingSphere = { radius: radius };
- };
- THREE.SphereGeometry.prototype = Object.create( THREE.Geometry.prototype );
- /**
- * @author zz85 / http://www.lab4games.net/zz85/blog
- * @author alteredq / http://alteredqualia.com/
- *
- * For creating 3D text geometry in three.js
- *
- * Text = 3D Text
- *
- * parameters = {
- * size: <float>, // size of the text
- * height: <float>, // thickness to extrude text
- * curveSegments: <int>, // number of points on the curves
- *
- * font: <string>, // font name
- * weight: <string>, // font weight (normal, bold)
- * style: <string>, // font style (normal, italics)
- *
- * bevelEnabled: <bool>, // turn on bevel
- * bevelThickness: <float>, // how deep into text bevel goes
- * bevelSize: <float>, // how far from text outline is bevel
- *
- * bend: <bool> // bend according to hardcoded curve (generates bendPath)
- * bendPath: <curve> // wraps text according to bend Path
- * }
- *
- */
- /* Usage Examples
-
- // TextGeometry wrapper
- var text3d = new TextGeometry( text, options );
- // Complete manner
- var textShapes = THREE.FontUtils.generateShapes( text, options );
- var text3d = new ExtrudeGeometry( textShapes, options );
-
- */
- THREE.TextGeometry = function ( text, parameters ) {
- var textShapes = THREE.FontUtils.generateShapes( text, parameters );
- // translate parameters to ExtrudeGeometry API
- parameters.amount = 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;
- if ( parameters.bend ) {
- var b = textShapes[ textShapes.length - 1 ].getBoundingBox();
- var max = b.maxX;
- parameters.bendPath = new THREE.QuadraticBezierCurve(
- new THREE.Vector2( 0, 0 ),
- new THREE.Vector2( max / 2, 120 ),
- new THREE.Vector2( max, 0 )
- );
- }
- THREE.ExtrudeGeometry.call( this, textShapes, parameters );
- };
- THREE.TextGeometry.prototype = Object.create( THREE.ExtrudeGeometry.prototype );
- /**
- * @author oosmoxiecode
- * @author mr.doob / http://mrdoob.com/
- * based on http://code.google.com/p/away3d/source/browse/trunk/fp10/Away3DLite/src/away3dlite/primitives/Torus.as?r=2888
- */
- THREE.TorusGeometry = function ( radius, tube, segmentsR, segmentsT, arc ) {
- THREE.Geometry.call( this );
- var scope = this;
- this.radius = radius || 100;
- this.tube = tube || 40;
- this.segmentsR = segmentsR || 8;
- this.segmentsT = segmentsT || 6;
- this.arc = arc || Math.PI * 2;
- var center = new THREE.Vector3(), uvs = [], normals = [];
- for ( var j = 0; j <= this.segmentsR; j ++ ) {
- for ( var i = 0; i <= this.segmentsT; i ++ ) {
- var u = i / this.segmentsT * this.arc;
- var v = j / this.segmentsR * Math.PI * 2;
- center.x = this.radius * Math.cos( u );
- center.y = this.radius * Math.sin( u );
- var vertex = new THREE.Vector3();
- vertex.x = ( this.radius + this.tube * Math.cos( v ) ) * Math.cos( u );
- vertex.y = ( this.radius + this.tube * Math.cos( v ) ) * Math.sin( u );
- vertex.z = this.tube * Math.sin( v );
- this.vertices.push( vertex );
- uvs.push( new THREE.UV( i / this.segmentsT, 1 - j / this.segmentsR ) );
- normals.push( vertex.clone().subSelf( center ).normalize() );
- }
- }
- for ( var j = 1; j <= this.segmentsR; j ++ ) {
- for ( var i = 1; i <= this.segmentsT; i ++ ) {
- var a = ( this.segmentsT + 1 ) * j + i - 1;
- var b = ( this.segmentsT + 1 ) * ( j - 1 ) + i - 1;
- var c = ( this.segmentsT + 1 ) * ( j - 1 ) + i;
- var d = ( this.segmentsT + 1 ) * j + i;
- var face = new THREE.Face4( a, b, c, d, [ normals[ a ], normals[ b ], normals[ c ], normals[ d ] ] );
- face.normal.addSelf( normals[ a ] );
- face.normal.addSelf( normals[ b ] );
- face.normal.addSelf( normals[ c ] );
- face.normal.addSelf( normals[ d ] );
- face.normal.normalize();
- this.faces.push( face );
- this.faceVertexUvs[ 0 ].push( [ uvs[ a ].clone(), uvs[ b ].clone(), uvs[ c ].clone(), uvs[ d ].clone() ] );
- }
- }
- this.computeCentroids();
- };
- THREE.TorusGeometry.prototype = Object.create( THREE.Geometry.prototype );
- /**
- * @author oosmoxiecode
- * based on http://code.google.com/p/away3d/source/browse/trunk/fp10/Away3D/src/away3d/primitives/TorusKnot.as?spec=svn2473&r=2473
- */
- THREE.TorusKnotGeometry = function ( radius, tube, segmentsR, segmentsT, p, q, heightScale ) {
- THREE.Geometry.call( this );
- var scope = this;
- this.radius = radius || 200;
- this.tube = tube || 40;
- this.segmentsR = segmentsR || 64;
- this.segmentsT = segmentsT || 8;
- this.p = p || 2;
- this.q = q || 3;
- this.heightScale = heightScale || 1;
- this.grid = new Array(this.segmentsR);
- var tang = new THREE.Vector3();
- var n = new THREE.Vector3();
- var bitan = new THREE.Vector3();
- for ( var i = 0; i < this.segmentsR; ++ i ) {
- this.grid[ i ] = new Array( this.segmentsT );
- for ( var j = 0; j < this.segmentsT; ++ j ) {
- var u = i / this.segmentsR * 2 * this.p * Math.PI;
- var v = j / this.segmentsT * 2 * Math.PI;
- var p1 = getPos( u, v, this.q, this.p, this.radius, this.heightScale );
- var p2 = getPos( u + 0.01, v, this.q, this.p, this.radius, this.heightScale );
- var cx, cy;
- tang.sub( p2, p1 );
- n.add( p2, p1 );
- bitan.cross( tang, n );
- n.cross( bitan, tang );
- bitan.normalize();
- n.normalize();
- cx = - this.tube * Math.cos( v ); // TODO: Hack: Negating it so it faces outside.
- cy = this.tube * Math.sin( v );
- p1.x += cx * n.x + cy * bitan.x;
- p1.y += cx * n.y + cy * bitan.y;
- p1.z += cx * n.z + cy * bitan.z;
- this.grid[ i ][ j ] = vert( p1.x, p1.y, p1.z );
- }
- }
- for ( var i = 0; i < this.segmentsR; ++ i ) {
- for ( var j = 0; j < this.segmentsT; ++ j ) {
- var ip = ( i + 1 ) % this.segmentsR;
- var jp = ( j + 1 ) % this.segmentsT;
- var a = this.grid[ i ][ j ];
- var b = this.grid[ ip ][ j ];
- var c = this.grid[ ip ][ jp ];
- var d = this.grid[ i ][ jp ];
- var uva = new THREE.UV( i / this.segmentsR, j / this.segmentsT );
- var uvb = new THREE.UV( ( i + 1 ) / this.segmentsR, j / this.segmentsT );
- var uvc = new THREE.UV( ( i + 1 ) / this.segmentsR, ( j + 1 ) / this.segmentsT );
- var uvd = new THREE.UV( i / this.segmentsR, ( j + 1 ) / this.segmentsT );
- this.faces.push( new THREE.Face4( a, b, c, d ) );
- this.faceVertexUvs[ 0 ].push( [ uva,uvb,uvc, uvd ] );
- }
- }
- this.computeCentroids();
- this.computeFaceNormals();
- this.computeVertexNormals();
- function vert( x, y, z ) {
- return scope.vertices.push( new THREE.Vector3( x, y, z ) ) - 1;
- }
- function getPos( u, v, in_q, in_p, radius, heightScale ) {
- var cu = Math.cos( u );
- var cv = Math.cos( v );
- var su = Math.sin( u );
- var quOverP = in_q / in_p * u;
- var cs = Math.cos( quOverP );
- var tx = radius * ( 2 + cs ) * 0.5 * cu;
- var ty = radius * ( 2 + cs ) * su * 0.5;
- var tz = heightScale * radius * Math.sin( quOverP ) * 0.5;
- return new THREE.Vector3( tx, ty, tz );
- }
- };
- THREE.TorusKnotGeometry.prototype = Object.create( THREE.Geometry.prototype );
- /**
- * @author WestLangley / https://github.com/WestLangley
- * @author zz85 / https://github.com/zz85
- * @author miningold / https://github.com/miningold
- *
- * Modified from the TorusKnotGeometry by @oosmoxiecode
- *
- * Creates a tube which extrudes along a 3d spline
- *
- * Uses parallel transport frames as described in
- * http://www.cs.indiana.edu/pub/techreports/TR425.pdf
- */
- THREE.TubeGeometry = function( path, segments, radius, segmentsRadius, closed, debug ) {
- THREE.Geometry.call( this );
- this.path = path;
- this.segments = segments || 64;
- this.radius = radius || 1;
- this.segmentsRadius = segmentsRadius || 8;
- this.closed = closed || false;
- if ( debug ) this.debug = new THREE.Object3D();
- this.grid = [];
- var scope = this,
- tangent,
- normal,
- binormal,
- numpoints = this.segments + 1,
-
- x, y, z,
- tx, ty, tz,
- u, v,
- cx, cy,
- pos, pos2 = new THREE.Vector3(),
- i, j,
- ip, jp,
- a, b, c, d,
- uva, uvb, uvc, uvd;
- var frames = new THREE.TubeGeometry.FrenetFrames(path, segments, closed),
- tangents = frames.tangents,
- normals = frames.normals,
- binormals = frames.binormals;
- // proxy internals
- this.tangents = tangents;
- this.normals = normals;
- this.binormals = binormals;
-
- function vert( x, y, z ) {
- return scope.vertices.push( new THREE.Vector3( x, y, z ) ) - 1;
- }
- // consruct the grid
- for ( i = 0; i < numpoints; i++ ) {
- this.grid[ i ] = [];
- u = i / ( numpoints - 1 );
- pos = path.getPointAt( u );
- tangent = tangents[ i ];
- normal = normals[ i ];
- binormal = binormals[ i ];
- if ( this.debug ) {
- this.debug.add(new THREE.ArrowHelper(tangent, pos, radius, 0x0000ff));
- this.debug.add(new THREE.ArrowHelper(normal, pos, radius, 0xff0000));
- this.debug.add(new THREE.ArrowHelper(binormal, pos, radius, 0x00ff00));
- }
- for ( j = 0; j < this.segmentsRadius; j++ ) {
- v = j / this.segmentsRadius * 2 * Math.PI;
- cx = -this.radius * Math.cos( v ); // TODO: Hack: Negating it so it faces outside.
- cy = this.radius * Math.sin( v );
- pos2.copy( pos );
- pos2.x += cx * normal.x + cy * binormal.x;
- pos2.y += cx * normal.y + cy * binormal.y;
- pos2.z += cx * normal.z + cy * binormal.z;
- this.grid[ i ][ j ] = vert( pos2.x, pos2.y, pos2.z );
- }
- }
- // construct the mesh
- for ( i = 0; i < this.segments; i++ ) {
- for ( j = 0; j < this.segmentsRadius; j++ ) {
- ip = ( closed ) ? (i + 1) % this.segments : i + 1;
- jp = (j + 1) % this.segmentsRadius;
- a = this.grid[ i ][ j ]; // *** NOT NECESSARILY PLANAR ! ***
- b = this.grid[ ip ][ j ];
- c = this.grid[ ip ][ jp ];
- d = this.grid[ i ][ jp ];
- uva = new THREE.UV( i / this.segments, j / this.segmentsRadius );
- uvb = new THREE.UV( ( i + 1 ) / this.segments, j / this.segmentsRadius );
- uvc = new THREE.UV( ( i + 1 ) / this.segments, ( j + 1 ) / this.segmentsRadius );
- uvd = new THREE.UV( i / this.segments, ( j + 1 ) / this.segmentsRadius );
- this.faces.push( new THREE.Face4( a, b, c, d ) );
- this.faceVertexUvs[ 0 ].push( [ uva, uvb, uvc, uvd ] );
- }
- }
- this.computeCentroids();
- this.computeFaceNormals();
- this.computeVertexNormals();
- };
- THREE.TubeGeometry.prototype = Object.create( THREE.Geometry.prototype );
- // For computing of Frenet frames, exposing the tangents, normals and binormals the spline
- THREE.TubeGeometry.FrenetFrames = function(path, segments, closed) {
- var
- tangent = new THREE.Vector3(),
- normal = new THREE.Vector3(),
- binormal = new THREE.Vector3(),
- tangents = [],
- normals = [],
- binormals = [],
- vec = new THREE.Vector3(),
- mat = new THREE.Matrix4(),
- numpoints = segments + 1,
- theta,
- epsilon = 0.0001,
- smallest,
- tx, ty, tz,
- i, u, v;
- // expose internals
- this.tangents = tangents;
- this.normals = normals;
- this.binormals = binormals;
- // compute the tangent vectors for each segment on the path
- for ( i = 0; i < numpoints; i++ ) {
- u = i / ( numpoints - 1 );
- tangents[ i ] = path.getTangentAt( u );
- tangents[ i ].normalize();
- }
- initialNormal3();
- function initialNormal1(lastBinormal) {
- // fixed start binormal. Has dangers of 0 vectors
- normals[ 0 ] = new THREE.Vector3();
- binormals[ 0 ] = new THREE.Vector3();
- if (lastBinormal===undefined) lastBinormal = new THREE.Vector3( 0, 0, 1 );
- normals[ 0 ].cross( lastBinormal, tangents[ 0 ] ).normalize();
- binormals[ 0 ].cross( tangents[ 0 ], normals[ 0 ] ).normalize();
- }
- function initialNormal2() {
- // This uses the Frenet-Serret formula for deriving binormal
- var t2 = path.getTangentAt( epsilon );
- normals[ 0 ] = new THREE.Vector3().sub( t2, tangents[ 0 ] ).normalize();
- binormals[ 0 ] = new THREE.Vector3().cross( tangents[ 0 ], normals[ 0 ] );
- normals[ 0 ].cross( binormals[ 0 ], tangents[ 0 ] ).normalize(); // last binormal x tangent
- binormals[ 0 ].cross( tangents[ 0 ], normals[ 0 ] ).normalize();
- }
- function initialNormal3() {
- // select an initial normal vector perpenicular to the first tangent vector,
- // and in the direction of the smallest tangent xyz component
- normals[ 0 ] = new THREE.Vector3();
- binormals[ 0 ] = new THREE.Vector3();
- smallest = Number.MAX_VALUE;
- tx = Math.abs( tangents[ 0 ].x );
- ty = Math.abs( tangents[ 0 ].y );
- tz = Math.abs( tangents[ 0 ].z );
- if ( tx <= smallest ) {
- smallest = tx;
- normal.set( 1, 0, 0 );
- }
- if ( ty <= smallest ) {
- smallest = ty;
- normal.set( 0, 1, 0 );
- }
- if ( tz <= smallest ) {
- normal.set( 0, 0, 1 );
- }
- vec.cross( tangents[ 0 ], normal ).normalize();
- normals[ 0 ].cross( tangents[ 0 ], vec );
- binormals[ 0 ].cross( tangents[ 0 ], normals[ 0 ] );
- }
- // compute the slowly-varying normal and binormal vectors for each segment on the path
- for ( i = 1; i < numpoints; i++ ) {
- normals[ i ] = normals[ i-1 ].clone();
- binormals[ i ] = binormals[ i-1 ].clone();
- vec.cross( tangents[ i-1 ], tangents[ i ] );
- if ( vec.length() > epsilon ) {
- vec.normalize();
- theta = Math.acos( tangents[ i-1 ].dot( tangents[ i ] ) );
- mat.makeRotationAxis( vec, theta ).multiplyVector3( normals[ i ] );
- }
- binormals[ i ].cross( tangents[ i ], normals[ i ] );
- }
- // if the curve is closed, postprocess the vectors so the first and last normal vectors are the same
- if ( closed ) {
- theta = Math.acos( normals[ 0 ].dot( normals[ numpoints-1 ] ) );
- theta /= ( numpoints - 1 );
- if ( tangents[ 0 ].dot( vec.cross( normals[ 0 ], normals[ numpoints-1 ] ) ) > 0 ) {
- theta = -theta;
- }
- for ( i = 1; i < numpoints; i++ ) {
- // twist a little...
- mat.makeRotationAxis( tangents[ i ], theta * i ).multiplyVector3( normals[ i ] );
- binormals[ i ].cross( tangents[ i ], normals[ i ] );
- }
- }
- };
- /**
- * @author clockworkgeek / https://github.com/clockworkgeek
- * @author timothypratley / https://github.com/timothypratley
- */
- THREE.PolyhedronGeometry = function ( vertices, faces, radius, detail ) {
- THREE.Geometry.call( this );
- radius = radius || 1;
- detail = detail || 0;
- var that = this;
- for ( var i = 0, l = vertices.length; i < l; i ++ ) {
- prepare( new THREE.Vector3( vertices[ i ][ 0 ], vertices[ i ][ 1 ], vertices[ i ][ 2 ] ) );
- }
- var midpoints = [], p = this.vertices;
- for ( var i = 0, l = faces.length; i < l; i ++ ) {
- make( p[ faces[ i ][ 0 ] ], p[ faces[ i ][ 1 ] ], p[ faces[ i ][ 2 ] ], detail );
- }
- this.mergeVertices();
- // Apply radius
- for ( var i = 0, l = this.vertices.length; i < l; i ++ ) {
- this.vertices[ i ].multiplyScalar( radius );
- }
- /**
- * Project vector onto sphere's surface
- */
- function prepare( vector ) {
- var vertex = vector.normalize().clone();
- vertex.index = that.vertices.push( vertex ) - 1;
- // Texture coords are equivalent to map coords, calculate angle and convert to fraction of a circle.
- var u = azimuth( vector ) / 2 / Math.PI + 0.5;
- var v = inclination( vector ) / Math.PI + 0.5;
- vertex.uv = new THREE.UV( u, v );
- return vertex;
- }
- /**
- * Approximate a curved face with recursively sub-divided triangles.
- */
- function make( v1, v2, v3, detail ) {
- if ( detail < 1 ) {
- var face = new THREE.Face3( v1.index, v2.index, v3.index, [ v1.clone(), v2.clone(), v3.clone() ] );
- face.centroid.addSelf( v1 ).addSelf( v2 ).addSelf( v3 ).divideScalar( 3 );
- face.normal = face.centroid.clone().normalize();
- that.faces.push( face );
- var azi = azimuth( face.centroid );
- that.faceVertexUvs[ 0 ].push( [
- correctUV( v1.uv, v1, azi ),
- correctUV( v2.uv, v2, azi ),
- correctUV( v3.uv, v3, azi )
- ] );
- }
- else {
- detail -= 1;
- // split triangle into 4 smaller triangles
- make( v1, midpoint( v1, v2 ), midpoint( v1, v3 ), detail ); // top quadrant
- make( midpoint( v1, v2 ), v2, midpoint( v2, v3 ), detail ); // left quadrant
- make( midpoint( v1, v3 ), midpoint( v2, v3 ), v3, detail ); // right quadrant
- make( midpoint( v1, v2 ), midpoint( v2, v3 ), midpoint( v1, v3 ), detail ); // center quadrant
- }
- }
- function midpoint( v1, v2 ) {
- if ( !midpoints[ v1.index ] ) midpoints[ v1.index ] = [];
- if ( !midpoints[ v2.index ] ) midpoints[ v2.index ] = [];
- var mid = midpoints[ v1.index ][ v2.index ];
- if ( mid === undefined ) {
- // generate mean point and project to surface with prepare()
- midpoints[ v1.index ][ v2.index ] = midpoints[ v2.index ][ v1.index ] = mid = prepare(
- new THREE.Vector3().add( v1, v2 ).divideScalar( 2 )
- );
- }
- return mid;
- }
- /**
- * 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 ) ) );
- }
- /**
- * Texture fixing helper. Spheres have some odd behaviours.
- */
- function correctUV( uv, vector, azimuth ) {
- if ( (azimuth < 0) && (uv.u === 1) ) uv = new THREE.UV( uv.u - 1, uv.v );
- if ( (vector.x === 0) && (vector.z === 0) ) uv = new THREE.UV( azimuth / 2 / Math.PI + 0.5, uv.v );
- return uv;
- }
- this.computeCentroids();
- this.boundingSphere = { radius: radius };
- };
- THREE.PolyhedronGeometry.prototype = Object.create( THREE.Geometry.prototype );
- /**
- * @author timothypratley / https://github.com/timothypratley
- */
- THREE.IcosahedronGeometry = function ( radius, detail ) {
- var t = ( 1 + Math.sqrt( 5 ) ) / 2;
- var 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 ]
- ];
- var faces = [
- [ 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 ]
- ];
- THREE.PolyhedronGeometry.call( this, vertices, faces, radius, detail );
- };
- THREE.IcosahedronGeometry.prototype = Object.create( THREE.Geometry.prototype );
- /**
- * @author timothypratley / https://github.com/timothypratley
- */
- THREE.OctahedronGeometry = function ( radius, detail ) {
- var vertices = [
- [ 1, 0, 0 ], [ -1, 0, 0 ], [ 0, 1, 0 ], [ 0, -1, 0 ], [ 0, 0, 1 ], [ 0, 0, -1 ]
- ];
- var faces = [
- [ 0, 2, 4 ], [ 0, 4, 3 ], [ 0, 3, 5 ], [ 0, 5, 2 ], [ 1, 2, 5 ], [ 1, 5, 3 ], [ 1, 3, 4 ], [ 1, 4, 2 ]
- ];
- THREE.PolyhedronGeometry.call( this, vertices, faces, radius, detail );
- };
- THREE.OctahedronGeometry.prototype = Object.create( THREE.Geometry.prototype );
- /**
- * @author timothypratley / https://github.com/timothypratley
- */
- THREE.TetrahedronGeometry = function ( radius, detail ) {
- var vertices = [
- [ 1, 1, 1 ], [ -1, -1, 1 ], [ -1, 1, -1 ], [ 1, -1, -1 ]
- ];
- var faces = [
- [ 2, 1, 0 ], [ 0, 3, 2 ], [ 1, 3, 0 ], [ 2, 3, 1 ]
- ];
- THREE.PolyhedronGeometry.call( this, vertices, faces, radius, detail );
- };
- THREE.TetrahedronGeometry.prototype = Object.create( THREE.Geometry.prototype );
- /**
- * @author zz85 / https://github.com/zz85
- * Parametric Surfaces Geometry
- * based on the brilliant article by @prideout http://prideout.net/blog/?p=44
- *
- * new THREE.ParametricGeometry( parametricFunction, uSements, ySegements, useTris );
- *
- */
- THREE.ParametricGeometry = function ( func, slices, stacks, useTris ) {
- THREE.Geometry.call( this );
- var verts = this.vertices;
- var faces = this.faces;
- var uvs = this.faceVertexUvs[ 0 ];
- useTris = (useTris === undefined) ? false : useTris;
- var i, il, j, p;
- var u, v;
- var stackCount = stacks + 1;
- var sliceCount = slices + 1;
-
- for ( i = 0; i <= stacks; i ++ ) {
- v = i / stacks;
- for ( j = 0; j <= slices; j ++ ) {
- u = j / slices;
- p = func( u, v );
- verts.push( p );
- }
- }
- var a, b, c, d;
- var uva, uvb, uvc, uvd;
- for ( i = 0; i < stacks; i ++ ) {
- for ( j = 0; j < slices; j ++ ) {
- a = i * sliceCount + j;
- b = i * sliceCount + j + 1;
- c = (i + 1) * sliceCount + j;
- d = (i + 1) * sliceCount + j + 1;
- uva = new THREE.UV( j / slices, i / stacks );
- uvb = new THREE.UV( ( j + 1 ) / slices, i / stacks );
- uvc = new THREE.UV( j / slices, ( i + 1 ) / stacks );
- uvd = new THREE.UV( ( j + 1 ) / slices, ( i + 1 ) / stacks );
- if ( useTris ) {
- faces.push( new THREE.Face3( a, b, c ) );
- faces.push( new THREE.Face3( b, d, c ) );
- uvs.push( [ uva, uvb, uvc ] );
- uvs.push( [ uvb, uvd, uvc ] );
- } else {
- faces.push( new THREE.Face4( a, b, d, c ) );
- uvs.push( [ uva, uvb, uvd, uvc ] );
- }
- }
-
- }
- // console.log(this);
- // magic bullet
- // var diff = this.mergeVertices();
- // console.log('removed ', diff, ' vertices by merging');
-
- this.computeCentroids();
- this.computeFaceNormals();
- this.computeVertexNormals();
- };
- THREE.ParametricGeometry.prototype = Object.create( THREE.Geometry.prototype );
- /**
- * @author qiao / https://github.com/qiao
- * @fileoverview This is a convex hull generator using the incremental method.
- * The complexity is O(n^2) where n is the number of vertices.
- * O(nlogn) algorithms do exist, but they are much more complicated.
- *
- * Benchmark:
- *
- * Platform: CPU: P7350 @2.00GHz Engine: V8
- *
- * Num Vertices Time(ms)
- *
- * 10 1
- * 20 3
- * 30 19
- * 40 48
- * 50 107
- */
- THREE.ConvexGeometry = function( vertices ) {
- THREE.Geometry.call( this );
- var faces = [ [ 0, 1, 2 ], [ 0, 2, 1 ] ];
- for ( var i = 3; i < vertices.length; i++ ) {
- addPoint( i );
- }
- function addPoint( vertexId ) {
- var vertex = vertices[ vertexId ].clone();
- var mag = vertex.length();
- vertex.x += mag * randomOffset();
- vertex.y += mag * randomOffset();
- vertex.z += mag * randomOffset();
- var hole = [];
- for ( var f = 0; f < faces.length; ) {
- var face = faces[ f ];
-
- // for each face, if the vertex can see it,
- // then we try to add the face's edges into the hole.
- if ( visible( face, vertex ) ) {
- for ( var e = 0; e < 3; e++ ) {
-
- var edge = [ face[ e ], face[ ( e + 1 ) % 3 ] ];
- var boundary = true;
- // remove duplicated edges.
- for ( var h = 0; h < hole.length; h++ ) {
-
- if ( equalEdge( hole[ h ], edge ) ) {
-
- hole[ h ] = hole[ hole.length - 1 ];
- hole.pop();
- boundary = false;
- break;
- }
- }
- if ( boundary ) {
- hole.push( edge );
- }
- }
- // remove faces[ f ]
- faces[ f ] = faces[ faces.length - 1 ];
- faces.pop();
-
- } else { // not visible
-
- f++;
- }
- }
- // construct the new faces formed by the edges of the hole and the vertex
- for ( var h = 0; h < hole.length; h++ ) {
- faces.push( [
- hole[ h ][ 0 ],
- hole[ h ][ 1 ],
- vertexId
- ] );
- }
- }
- /**
- * Whether the face is visible from the vertex
- */
- function visible( face, vertex ) {
- var va = vertices[ face[ 0 ] ];
- var vb = vertices[ face[ 1 ] ];
- var vc = vertices[ face[ 2 ] ];
- var n = normal( va, vb, vc );
- // distance from face to origin
- var dist = n.dot( va );
- return n.dot( vertex ) >= dist;
- }
- /**
- * Face normal
- */
- function normal( va, vb, vc ) {
-
- var cb = new THREE.Vector3();
- var ab = new THREE.Vector3();
- cb.sub( vc, vb );
- ab.sub( va, vb );
- cb.crossSelf( ab );
- if ( !cb.isZero() ) {
-
- cb.normalize();
- }
- return cb;
- }
- /**
- * Detect whether two edges are equal.
- * Note that when constructing the convex hull, two same edges can only
- * be of the negative direction.
- */
- function equalEdge( ea, eb ) {
-
- return ea[ 0 ] === eb[ 1 ] && ea[ 1 ] === eb[ 0 ];
- }
- /**
- * Create a random offset between -1e-6 and 1e-6.
- */
- function randomOffset() {
- return ( Math.random() - 0.5 ) * 2 * 1e-6;
- }
- /**
- * XXX: Not sure if this is the correct approach. Need someone to review.
- */
- function vertexUv( vertex ) {
- var mag = vertex.length();
- return new THREE.UV( vertex.x / mag, vertex.y / mag );
- }
- // Push vertices into `this.vertices`, skipping those inside the hull
- var id = 0;
- var newId = new Array( vertices.length ); // map from old vertex id to new id
- for ( var i = 0; i < faces.length; i++ ) {
- var face = faces[ i ];
-
- for ( var j = 0; j < 3; j++ ) {
-
- if ( newId[ face[ j ] ] === undefined ) {
-
- newId[ face[ j ] ] = id++;
- this.vertices.push( vertices[ face[ j ] ] );
-
- }
- face[ j ] = newId[ face[ j ] ];
- }
-
- }
- // Convert faces into instances of THREE.Face3
- for ( var i = 0; i < faces.length; i++ ) {
- this.faces.push( new THREE.Face3(
- faces[ i ][ 0 ],
- faces[ i ][ 1 ],
- faces[ i ][ 2 ]
- ) );
- }
- // Compute UVs
- for ( var i = 0; i < this.faces.length; i++ ) {
-
- var face = this.faces[ i ];
- this.faceVertexUvs[ 0 ].push( [
- vertexUv( this.vertices[ face.a ] ),
- vertexUv( this.vertices[ face.b ] ),
- vertexUv( this.vertices[ face.c ])
- ] );
- }
-
- this.computeCentroids();
- this.computeFaceNormals();
- this.computeVertexNormals();
- };
- THREE.ConvexGeometry.prototype = Object.create( THREE.Geometry.prototype );
- /**
- * @author sroucheray / http://sroucheray.org/
- * @author mr.doob / http://mrdoob.com/
- */
- THREE.AxisHelper = function () {
- THREE.Object3D.call( this );
- var lineGeometry = new THREE.Geometry();
- lineGeometry.vertices.push( new THREE.Vector3() );
- lineGeometry.vertices.push( new THREE.Vector3( 0, 100, 0 ) );
- var coneGeometry = new THREE.CylinderGeometry( 0, 5, 25, 5, 1 );
- var line, cone;
- // x
- line = new THREE.Line( lineGeometry, new THREE.LineBasicMaterial( { color : 0xff0000 } ) );
- line.rotation.z = - Math.PI / 2;
- this.add( line );
- cone = new THREE.Mesh( coneGeometry, new THREE.MeshBasicMaterial( { color : 0xff0000 } ) );
- cone.position.x = 100;
- cone.rotation.z = - Math.PI / 2;
- this.add( cone );
- // y
- line = new THREE.Line( lineGeometry, new THREE.LineBasicMaterial( { color : 0x00ff00 } ) );
- this.add( line );
- cone = new THREE.Mesh( coneGeometry, new THREE.MeshBasicMaterial( { color : 0x00ff00 } ) );
- cone.position.y = 100;
- this.add( cone );
- // z
- line = new THREE.Line( lineGeometry, new THREE.LineBasicMaterial( { color : 0x0000ff } ) );
- line.rotation.x = Math.PI / 2;
- this.add( line );
- cone = new THREE.Mesh( coneGeometry, new THREE.MeshBasicMaterial( { color : 0x0000ff } ) );
- cone.position.z = 100;
- cone.rotation.x = Math.PI / 2;
- this.add( cone );
- };
- THREE.AxisHelper.prototype = Object.create( THREE.Object3D.prototype );
- /**
- * @author WestLangley / http://github.com/WestLangley
- * @author zz85 / https://github.com/zz85
- *
- * Creates an arrow for visualizing directions
- *
- * Parameters:
- * dir - Vector3
- * origin - Vector3
- * length - Number
- * hex - color in hex value
- */
- THREE.ArrowHelper = function ( dir, origin, length, hex ) {
- THREE.Object3D.call( this );
- if ( hex === undefined ) hex = 0xffff00;
- if ( length === undefined ) length = 20;
- var lineGeometry = new THREE.Geometry();
- lineGeometry.vertices.push( new THREE.Vector3( 0, 0, 0 ) );
- lineGeometry.vertices.push( new THREE.Vector3( 0, 1, 0 ) );
- this.line = new THREE.Line( lineGeometry, new THREE.LineBasicMaterial( { color: hex } ) );
- this.add( this.line );
- var coneGeometry = new THREE.CylinderGeometry( 0, 0.05, 0.25, 5, 1 );
- this.cone = new THREE.Mesh( coneGeometry, new THREE.MeshBasicMaterial( { color: hex } ) );
- this.cone.position.set( 0, 1, 0 );
- this.add( this.cone );
- if ( origin instanceof THREE.Vector3 ) this.position = origin;
- this.setDirection( dir );
- this.setLength( length );
- };
- THREE.ArrowHelper.prototype = Object.create( THREE.Object3D.prototype );
- THREE.ArrowHelper.prototype.setDirection = function ( dir ) {
- var axis = new THREE.Vector3( 0, 1, 0 ).crossSelf( dir );
- var radians = Math.acos( new THREE.Vector3( 0, 1, 0 ).dot( dir.clone().normalize() ) );
- this.matrix = new THREE.Matrix4().makeRotationAxis( axis.normalize(), radians );
- this.rotation.setEulerFromRotationMatrix( this.matrix, this.eulerOrder );
- };
- THREE.ArrowHelper.prototype.setLength = function ( length ) {
- this.scale.set( length, length, length );
- };
- THREE.ArrowHelper.prototype.setColor = function ( hex ) {
- this.line.material.color.setHex( hex );
- this.cone.material.color.setHex( hex );
- };
- /**
- * @author alteredq / http://alteredqualia.com/
- *
- * - 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
- */
- THREE.CameraHelper = function ( camera ) {
- THREE.Object3D.call( this );
- var _this = this;
- this.lineGeometry = new THREE.Geometry();
- this.lineMaterial = new THREE.LineBasicMaterial( { color: 0xffffff, vertexColors: THREE.FaceColors } );
- this.pointMap = {};
- // colors
- var hexFrustum = 0xffaa00,
- hexCone = 0xff0000,
- hexUp = 0x00aaff,
- hexTarget = 0xffffff,
- hexCross = 0x333333;
- // near
- addLine( "n1", "n2", hexFrustum );
- addLine( "n2", "n4", hexFrustum );
- addLine( "n4", "n3", hexFrustum );
- addLine( "n3", "n1", hexFrustum );
- // far
- addLine( "f1", "f2", hexFrustum );
- addLine( "f2", "f4", hexFrustum );
- addLine( "f4", "f3", hexFrustum );
- addLine( "f3", "f1", hexFrustum );
- // sides
- addLine( "n1", "f1", hexFrustum );
- addLine( "n2", "f2", hexFrustum );
- addLine( "n3", "f3", hexFrustum );
- addLine( "n4", "f4", hexFrustum );
- // cone
- addLine( "p", "n1", hexCone );
- addLine( "p", "n2", hexCone );
- addLine( "p", "n3", hexCone );
- addLine( "p", "n4", hexCone );
- // up
- addLine( "u1", "u2", hexUp );
- addLine( "u2", "u3", hexUp );
- addLine( "u3", "u1", hexUp );
- // target
- addLine( "c", "t", hexTarget );
- addLine( "p", "c", hexCross );
- // cross
- addLine( "cn1", "cn2", hexCross );
- addLine( "cn3", "cn4", hexCross );
- addLine( "cf1", "cf2", hexCross );
- addLine( "cf3", "cf4", hexCross );
- this.camera = camera;
- function addLine( a, b, hex ) {
- addPoint( a, hex );
- addPoint( b, hex );
- }
- function addPoint( id, hex ) {
- _this.lineGeometry.vertices.push( new THREE.Vector3() );
- _this.lineGeometry.colors.push( new THREE.Color( hex ) );
- if ( _this.pointMap[ id ] === undefined ) _this.pointMap[ id ] = [];
- _this.pointMap[ id ].push( _this.lineGeometry.vertices.length - 1 );
- }
- this.update( camera );
- this.lines = new THREE.Line( this.lineGeometry, this.lineMaterial, THREE.LinePieces );
- this.add( this.lines );
- };
- THREE.CameraHelper.prototype = Object.create( THREE.Object3D.prototype );
- THREE.CameraHelper.prototype.update = function () {
- var camera = this.camera;
- var w = 1;
- var h = 1;
- var _this = this;
- // we need just camera projection matrix
- // world matrix must be identity
- THREE.CameraHelper.__c.projectionMatrix.copy( camera.projectionMatrix );
- // center / target
- setPoint( "c", 0, 0, -1 );
- setPoint( "t", 0, 0, 1 );
- // near
- setPoint( "n1", -w, -h, -1 );
- setPoint( "n2", w, -h, -1 );
- setPoint( "n3", -w, h, -1 );
- setPoint( "n4", w, h, -1 );
- // far
- setPoint( "f1", -w, -h, 1 );
- setPoint( "f2", w, -h, 1 );
- setPoint( "f3", -w, h, 1 );
- setPoint( "f4", w, h, 1 );
- // up
- setPoint( "u1", w * 0.7, h * 1.1, -1 );
- setPoint( "u2", -w * 0.7, h * 1.1, -1 );
- setPoint( "u3", 0, h * 2, -1 );
- // cross
- setPoint( "cf1", -w, 0, 1 );
- setPoint( "cf2", w, 0, 1 );
- setPoint( "cf3", 0, -h, 1 );
- setPoint( "cf4", 0, h, 1 );
- setPoint( "cn1", -w, 0, -1 );
- setPoint( "cn2", w, 0, -1 );
- setPoint( "cn3", 0, -h, -1 );
- setPoint( "cn4", 0, h, -1 );
- function setPoint( point, x, y, z ) {
- THREE.CameraHelper.__v.set( x, y, z );
- THREE.CameraHelper.__projector.unprojectVector( THREE.CameraHelper.__v, THREE.CameraHelper.__c );
- var points = _this.pointMap[ point ];
- if ( points !== undefined ) {
- for ( var i = 0, il = points.length; i < il; i ++ ) {
- var j = points[ i ];
- _this.lineGeometry.vertices[ j ].copy( THREE.CameraHelper.__v );
- }
- }
- }
- this.lineGeometry.verticesNeedUpdate = true;
- };
- THREE.CameraHelper.__projector = new THREE.Projector();
- THREE.CameraHelper.__v = new THREE.Vector3();
- THREE.CameraHelper.__c = new THREE.Camera();
- /*
- * @author zz85 / http://twitter.com/blurspline / http://www.lab4games.net/zz85/blog
- *
- * Subdivision Geometry Modifier
- * using Catmull-Clark Subdivision Surfaces
- * for creating smooth geometry meshes
- *
- * Note: a modifier modifies vertices and faces of geometry,
- * so use THREE.GeometryUtils.clone() if orignal geoemtry needs to be retained
- *
- * Readings:
- * http://en.wikipedia.org/wiki/Catmull%E2%80%93Clark_subdivision_surface
- * http://www.rorydriscoll.com/2008/08/01/catmull-clark-subdivision-the-basics/
- * http://xrt.wikidot.com/blog:31
- * "Subdivision Surfaces in Character Animation"
- *
- * Supports:
- * Closed and Open geometries.
- *
- * TODO:
- * crease vertex and "semi-sharp" features
- * selective subdivision
- */
- THREE.SubdivisionModifier = function( subdivisions ) {
-
- this.subdivisions = (subdivisions === undefined ) ? 1 : subdivisions;
-
- // Settings
- this.useOldVertexColors = false;
- this.supportUVs = true;
- this.debug = false;
-
- };
- // Applies the "modify" pattern
- THREE.SubdivisionModifier.prototype.modify = function ( geometry ) {
-
- var repeats = this.subdivisions;
-
- while ( repeats-- > 0 ) {
- this.smooth( geometry );
- }
-
- };
- // Performs an iteration of Catmull-Clark Subdivision
- THREE.SubdivisionModifier.prototype.smooth = function ( oldGeometry ) {
-
- //debug( 'running smooth' );
-
- // New set of vertices, faces and uvs
- var newVertices = [], newFaces = [], newUVs = [];
-
- function v( x, y, z ) {
- newVertices.push( new THREE.Vector3( x, y, z ) );
- }
-
- var scope = this;
- function debug() {
- if (scope.debug) console.log.apply(console, arguments);
- }
- function warn() {
- if (console)
- console.log.apply(console, arguments);
- }
- function f4( a, b, c, d, oldFace, orders, facei ) {
-
- // TODO move vertex selection over here!
-
- var newFace = new THREE.Face4( a, b, c, d, null, oldFace.color, oldFace.materialIndex );
-
- if (scope.useOldVertexColors) {
-
- newFace.vertexColors = [];
-
- var color, tmpColor, order;
- for (var i=0;i<4;i++) {
- order = orders[i];
-
- color = new THREE.Color(),
- color.setRGB(0,0,0);
-
- for (var j=0, jl=0; j<order.length;j++) {
- tmpColor = oldFace.vertexColors[order[j]-1];
- color.r += tmpColor.r;
- color.g += tmpColor.g;
- color.b += tmpColor.b;
- }
-
- color.r /= order.length;
- color.g /= order.length;
- color.b /= order.length;
-
- newFace.vertexColors[i] = color;
-
- }
-
- }
-
- newFaces.push( newFace );
- if (scope.supportUVs) {
- var aUv = [
- getUV(a, ''),
- getUV(b, facei),
- getUV(c, facei),
- getUV(d, facei)
- ];
-
- if (!aUv[0]) debug('a :( ', a+':'+facei);
- else if (!aUv[1]) debug('b :( ', b+':'+facei);
- else if (!aUv[2]) debug('c :( ', c+':'+facei);
- else if (!aUv[3]) debug('d :( ', d+':'+facei);
- else
- newUVs.push( aUv );
- }
- }
-
- function edge_hash( a, b ) {
- return Math.min( a, b ) + "_" + Math.max( a, b );
- }
-
- function computeEdgeFaces( geometry ) {
- var i, il, v1, v2, j, k,
- face, faceIndices, faceIndex,
- edge,
- hash,
- edgeFaceMap = {};
- function mapEdgeHash( hash, i ) {
-
- if ( edgeFaceMap[ hash ] === undefined ) {
- edgeFaceMap[ hash ] = [];
-
- }
-
- edgeFaceMap[ hash ].push( i );
- }
- // construct vertex -> face map
- for( i = 0, il = geometry.faces.length; i < il; i ++ ) {
- face = geometry.faces[ i ];
- if ( face instanceof THREE.Face3 ) {
- hash = edge_hash( face.a, face.b );
- mapEdgeHash( hash, i );
- hash = edge_hash( face.b, face.c );
- mapEdgeHash( hash, i );
- hash = edge_hash( face.c, face.a );
- mapEdgeHash( hash, i );
- } else if ( face instanceof THREE.Face4 ) {
- hash = edge_hash( face.a, face.b );
- mapEdgeHash( hash, i );
- hash = edge_hash( face.b, face.c );
- mapEdgeHash( hash, i );
- hash = edge_hash( face.c, face.d );
- mapEdgeHash( hash, i );
-
- hash = edge_hash( face.d, face.a );
- mapEdgeHash( hash, i );
- }
- }
- // extract faces
-
- // var edges = [];
- //
- // var numOfEdges = 0;
- // for (i in edgeFaceMap) {
- // numOfEdges++;
- //
- // edge = edgeFaceMap[i];
- // edges.push(edge);
- //
- // }
-
- //debug('edgeFaceMap', edgeFaceMap, 'geometry.edges',geometry.edges, 'numOfEdges', numOfEdges);
- return edgeFaceMap;
- }
-
- var originalPoints = oldGeometry.vertices;
- var originalFaces = oldGeometry.faces;
-
- var newPoints = originalPoints.concat(); // Vertices
-
- var facePoints = [], edgePoints = {};
-
- var sharpEdges = {}, sharpVertices = [], sharpFaces = [];
-
- var uvForVertices = {}; // Stored in {vertex}:{old face} format
- var originalVerticesLength = originalPoints.length;
- function getUV(vertexNo, oldFaceNo) {
- var j,jl;
- var key = vertexNo+':'+oldFaceNo;
- var theUV = uvForVertices[key];
- if (!theUV) {
- if (vertexNo>=originalVerticesLength && vertexNo < (originalVerticesLength + originalFaces.length)) {
- debug('face pt');
- } else {
- debug('edge pt');
- }
- warn('warning, UV not found for', key);
- return null;
- }
- return theUV;
-
- // Original faces -> Vertex Nos.
- // new Facepoint -> Vertex Nos.
- // edge Points
- }
- function addUV(vertexNo, oldFaceNo, value) {
- var key = vertexNo+':'+oldFaceNo;
- if (!(key in uvForVertices)) {
- uvForVertices[key] = value;
- } else {
- warn('dup vertexNo', vertexNo, 'oldFaceNo', oldFaceNo, 'value', value, 'key', key, uvForVertices[key]);
- }
- }
-
- // Step 1
- // For each face, add a face point
- // Set each face point to be the centroid of all original points for the respective face.
- // debug(oldGeometry);
- var i, il, j, jl, face;
-
- // For Uvs
- var uvs = oldGeometry.faceVertexUvs[0];
- var abcd = 'abcd', vertice;
- debug('originalFaces, uvs, originalVerticesLength', originalFaces.length, uvs.length, originalVerticesLength);
- if (scope.supportUVs)
- for (i=0, il = uvs.length; i<il; i++ ) {
- for (j=0,jl=uvs[i].length;j<jl;j++) {
- vertice = originalFaces[i][abcd.charAt(j)];
-
- addUV(vertice, i, uvs[i][j]);
-
- }
- }
- if (uvs.length == 0) scope.supportUVs = false;
- // Additional UVs check, if we index original
- var uvCount = 0;
- for (var u in uvForVertices) {
- uvCount++;
- }
- if (!uvCount) {
- scope.supportUVs = false;
- debug('no uvs');
- }
- debug('-- Original Faces + Vertices UVs completed', uvForVertices, 'vs', uvs.length);
-
- var avgUv ;
- for (i=0, il = originalFaces.length; i<il ;i++) {
- face = originalFaces[ i ];
- facePoints.push( face.centroid );
- newPoints.push( face.centroid );
-
-
- if (!scope.supportUVs) continue;
-
- // Prepare subdivided uv
-
- avgUv = new THREE.UV();
-
- if ( face instanceof THREE.Face3 ) {
- avgUv.u = getUV( face.a, i ).u + getUV( face.b, i ).u + getUV( face.c, i ).u;
- avgUv.v = getUV( face.a, i ).v + getUV( face.b, i ).v + getUV( face.c, i ).v;
- avgUv.u /= 3;
- avgUv.v /= 3;
-
- } else if ( face instanceof THREE.Face4 ) {
- avgUv.u = getUV( face.a, i ).u + getUV( face.b, i ).u + getUV( face.c, i ).u + getUV( face.d, i ).u;
- avgUv.v = getUV( face.a, i ).v + getUV( face.b, i ).v + getUV( face.c, i ).v + getUV( face.d, i ).v;
- avgUv.u /= 4;
- avgUv.v /= 4;
- }
- addUV(originalVerticesLength + i, '', avgUv);
- }
- debug('-- added UVs for new Faces', uvForVertices);
- // Step 2
- // For each edge, add an edge point.
- // Set each edge point to be the average of the two neighbouring face points and its two original endpoints.
-
- var edgeFaceMap = computeEdgeFaces ( oldGeometry ); // Edge Hash -> Faces Index
- var edge, faceIndexA, faceIndexB, avg;
-
- // debug('edgeFaceMap', edgeFaceMap);
- var edgeCount = 0;
- var edgeVertex, edgeVertexA, edgeVertexB;
-
- ////
-
- var vertexEdgeMap = {}; // Gives edges connecting from each vertex
- var vertexFaceMap = {}; // Gives faces connecting from each vertex
-
- function addVertexEdgeMap(vertex, edge) {
- if (vertexEdgeMap[vertex]===undefined) {
- vertexEdgeMap[vertex] = [];
- }
-
- vertexEdgeMap[vertex].push(edge);
- }
-
- function addVertexFaceMap(vertex, face, edge) {
- if (vertexFaceMap[vertex]===undefined) {
- vertexFaceMap[vertex] = {};
- }
-
- vertexFaceMap[vertex][face] = edge;
- // vertexFaceMap[vertex][face] = null;
- }
-
- // Prepares vertexEdgeMap and vertexFaceMap
- for (i in edgeFaceMap) { // This is for every edge
- edge = edgeFaceMap[i];
-
- edgeVertex = i.split('_');
- edgeVertexA = edgeVertex[0];
- edgeVertexB = edgeVertex[1];
-
- // Maps an edgeVertex to connecting edges
- addVertexEdgeMap(edgeVertexA, [edgeVertexA, edgeVertexB] );
- addVertexEdgeMap(edgeVertexB, [edgeVertexA, edgeVertexB] );
-
-
- for (j=0,jl=edge.length;j<jl;j++) {
- face = edge[j];
-
- addVertexFaceMap(edgeVertexA, face, i);
- addVertexFaceMap(edgeVertexB, face, i);
- }
-
- if (edge.length < 2) {
- // edge is "sharp";
- sharpEdges[i] = true;
- sharpVertices[edgeVertexA] = true;
- sharpVertices[edgeVertexB] = true;
-
- }
-
- }
-
- debug('vertexEdgeMap',vertexEdgeMap, 'vertexFaceMap', vertexFaceMap);
-
-
- for (i in edgeFaceMap) {
- edge = edgeFaceMap[i];
-
- faceIndexA = edge[0]; // face index a
- faceIndexB = edge[1]; // face index b
-
- edgeVertex = i.split('_');
- edgeVertexA = edgeVertex[0];
- edgeVertexB = edgeVertex[1];
-
-
- avg = new THREE.Vector3();
-
- //debug(i, faceIndexB,facePoints[faceIndexB]);
-
- if (sharpEdges[i]) {
- //debug('warning, ', i, 'edge has only 1 connecting face', edge);
-
- // For a sharp edge, average the edge end points.
- avg.addSelf(originalPoints[edgeVertexA]);
- avg.addSelf(originalPoints[edgeVertexB]);
-
- avg.multiplyScalar(0.5);
-
- sharpVertices[newPoints.length] = true;
-
- } else {
-
- avg.addSelf(facePoints[faceIndexA]);
- avg.addSelf(facePoints[faceIndexB]);
-
- avg.addSelf(originalPoints[edgeVertexA]);
- avg.addSelf(originalPoints[edgeVertexB]);
-
- avg.multiplyScalar(0.25);
-
- }
-
- edgePoints[i] = originalVerticesLength + originalFaces.length + edgeCount;
-
- newPoints.push( avg );
-
- edgeCount ++;
-
- if (!scope.supportUVs) {
- continue;
- }
- // debug('faceIndexAB', faceIndexA, faceIndexB, sharpEdges[i]);
- // Prepare subdivided uv
-
- avgUv = new THREE.UV();
-
- avgUv.u = getUV(edgeVertexA, faceIndexA).u + getUV(edgeVertexB, faceIndexA).u;
- avgUv.v = getUV(edgeVertexA, faceIndexA).v + getUV(edgeVertexB, faceIndexA).v;
- avgUv.u /= 2;
- avgUv.v /= 2;
- addUV(edgePoints[i], faceIndexA, avgUv);
- if (!sharpEdges[i]) {
- avgUv = new THREE.UV();
-
- avgUv.u = getUV(edgeVertexA, faceIndexB).u + getUV(edgeVertexB, faceIndexB).u;
- avgUv.v = getUV(edgeVertexA, faceIndexB).v + getUV(edgeVertexB, faceIndexB).v;
- avgUv.u /= 2;
- avgUv.v /= 2;
-
- addUV(edgePoints[i], faceIndexB, avgUv);
- }
-
- }
- debug('-- Step 2 done');
- // Step 3
- // For each face point, add an edge for every edge of the face,
- // connecting the face point to each edge point for the face.
-
-
- var facePt, currentVerticeIndex;
-
- var hashAB, hashBC, hashCD, hashDA, hashCA;
-
- var abc123 = ['123', '12', '2', '23'];
- var bca123 = ['123', '23', '3', '31'];
- var cab123 = ['123', '31', '1', '12'];
- var abc1234 = ['1234', '12', '2', '23'];
- var bcd1234 = ['1234', '23', '3', '34'];
- var cda1234 = ['1234', '34', '4', '41'];
- var dab1234 = ['1234', '41', '1', '12'];
-
-
- for (i=0, il = facePoints.length; i<il ;i++) { // for every face
- facePt = facePoints[i];
- face = originalFaces[i];
- currentVerticeIndex = originalVerticesLength+ i;
-
- if ( face instanceof THREE.Face3 ) {
-
- // create 3 face4s
-
- hashAB = edge_hash( face.a, face.b );
- hashBC = edge_hash( face.b, face.c );
- hashCA = edge_hash( face.c, face.a );
-
- f4( currentVerticeIndex, edgePoints[hashAB], face.b, edgePoints[hashBC], face, abc123, i );
- f4( currentVerticeIndex, edgePoints[hashBC], face.c, edgePoints[hashCA], face, bca123, i );
- f4( currentVerticeIndex, edgePoints[hashCA], face.a, edgePoints[hashAB], face, cab123, i );
-
- } else if ( face instanceof THREE.Face4 ) {
- // create 4 face4s
-
- hashAB = edge_hash( face.a, face.b );
- hashBC = edge_hash( face.b, face.c );
- hashCD = edge_hash( face.c, face.d );
- hashDA = edge_hash( face.d, face.a );
-
- f4( currentVerticeIndex, edgePoints[hashAB], face.b, edgePoints[hashBC], face, abc1234, i );
- f4( currentVerticeIndex, edgePoints[hashBC], face.c, edgePoints[hashCD], face, bcd1234, i );
- f4( currentVerticeIndex, edgePoints[hashCD], face.d, edgePoints[hashDA], face, cda1234, i );
- f4( currentVerticeIndex, edgePoints[hashDA], face.a, edgePoints[hashAB], face, dab1234, i );
-
- } else {
- debug('face should be a face!', face);
- }
- }
-
- newVertices = newPoints;
-
- // debug('original ', oldGeometry.vertices.length, oldGeometry.faces.length );
- // debug('new points', newPoints.length, 'faces', newFaces.length );
-
- // Step 4
-
- // For each original point P,
- // take the average F of all n face points for faces touching P,
- // and take the average R of all n edge midpoints for edges touching P,
- // where each edge midpoint is the average of its two endpoint vertices.
- // Move each original point to the point
-
- var F = new THREE.Vector3();
- var R = new THREE.Vector3();
- var n;
- for (i=0, il = originalPoints.length; i<il; i++) {
- // (F + 2R + (n-3)P) / n
-
- if (vertexEdgeMap[i]===undefined) continue;
-
- F.set(0,0,0);
- R.set(0,0,0);
- var newPos = new THREE.Vector3(0,0,0);
-
- var f =0;
- for (j in vertexFaceMap[i]) {
- F.addSelf(facePoints[j]);
- f++;
- }
-
- var sharpEdgeCount = 0;
-
- n = vertexEdgeMap[i].length;
-
- for (j=0;j<n;j++) {
- if (
- sharpEdges[
- edge_hash(vertexEdgeMap[i][j][0],vertexEdgeMap[i][j][1])
- ]) {
- sharpEdgeCount++;
- }
- }
-
- if ( sharpEdgeCount==2 ) {
- continue;
- // Do not move vertex if there's 2 connecting sharp edges.
- }
- /*
- if (sharpEdgeCount>2) {
- // TODO
- }
- */
-
- F.divideScalar(f);
-
-
-
- for (j=0; j<n;j++) {
- edge = vertexEdgeMap[i][j];
- var midPt = originalPoints[edge[0]].clone().addSelf(originalPoints[edge[1]]).divideScalar(2);
- R.addSelf(midPt);
- // R.addSelf(originalPoints[edge[0]]);
- // R.addSelf(originalPoints[edge[1]]);
- }
-
- R.divideScalar(n);
-
- newPos.addSelf(originalPoints[i]);
- newPos.multiplyScalar(n - 3);
-
- newPos.addSelf(F);
- newPos.addSelf(R.multiplyScalar(2));
- newPos.divideScalar(n);
-
- newVertices[i] = newPos;
-
-
- }
-
- var newGeometry = oldGeometry; // Let's pretend the old geometry is now new :P
-
- newGeometry.vertices = newVertices;
- newGeometry.faces = newFaces;
- newGeometry.faceVertexUvs[ 0 ] = newUVs;
-
- delete newGeometry.__tmpVertices; // makes __tmpVertices undefined :P
-
- newGeometry.computeCentroids();
- newGeometry.computeFaceNormals();
- newGeometry.computeVertexNormals();
-
- };
- /**
- * @author alteredq / http://alteredqualia.com/
- */
- THREE.ImmediateRenderObject = function ( ) {
- THREE.Object3D.call( this );
- this.render = function ( renderCallback ) { };
- };
- THREE.ImmediateRenderObject.prototype = Object.create( THREE.Object3D.prototype );
- /**
- * @author mikael emtinger / http://gomo.se/
- * @author alteredq / http://alteredqualia.com/
- */
- THREE.LensFlare = function ( texture, size, distance, blending, color ) {
- THREE.Object3D.call( this );
- this.lensFlares = [];
- this.positionScreen = new THREE.Vector3();
- this.customUpdateCallback = undefined;
- if( texture !== undefined ) {
- this.add( texture, size, distance, blending, color );
- }
- };
- THREE.LensFlare.prototype = Object.create( THREE.Object3D.prototype );
- /*
- * Add: adds another flare
- */
- THREE.LensFlare.prototype.add = function ( texture, size, distance, blending, color, opacity ) {
- if( size === undefined ) size = -1;
- if( distance === undefined ) distance = 0;
- if( opacity === undefined ) opacity = 1;
- if( color === undefined ) color = new THREE.Color( 0xffffff );
- if( blending === undefined ) blending = THREE.NormalBlending;
- distance = Math.min( distance, Math.max( 0, distance ) );
- this.lensFlares.push( { texture: texture, // THREE.Texture
- size: size, // size in pixels (-1 = use texture.width)
- distance: distance, // distance (0-1) from light source (0=at light source)
- x: 0, y: 0, z: 0, // screen position (-1 => 1) z = 0 is ontop z = 1 is back
- scale: 1, // scale
- rotation: 1, // rotation
- opacity: opacity, // opacity
- color: color, // color
- blending: blending } ); // blending
- };
- /*
- * Update lens flares update positions on all flares based on the screen position
- * Set myLensFlare.customUpdateCallback to alter the flares in your project specific way.
- */
- THREE.LensFlare.prototype.updateLensFlares = function () {
- var f, fl = this.lensFlares.length;
- var flare;
- var vecX = -this.positionScreen.x * 2;
- var vecY = -this.positionScreen.y * 2;
- for( f = 0; f < fl; f ++ ) {
- flare = this.lensFlares[ f ];
- flare.x = this.positionScreen.x + vecX * flare.distance;
- flare.y = this.positionScreen.y + vecY * flare.distance;
- flare.wantedRotation = flare.x * Math.PI * 0.25;
- flare.rotation += ( flare.wantedRotation - flare.rotation ) * 0.25;
- }
- };
- /**
- * @author alteredq / http://alteredqualia.com/
- */
- THREE.MorphBlendMesh = function( geometry, material ) {
- THREE.Mesh.call( this, geometry, material );
- this.animationsMap = {};
- this.animationsList = [];
- // prepare default animation
- // (all frames played together in 1 second)
- var numFrames = this.geometry.morphTargets.length;
- var name = "__default";
- var startFrame = 0;
- var endFrame = numFrames - 1;
- var fps = numFrames / 1;
- this.createAnimation( name, startFrame, endFrame, fps );
- this.setAnimationWeight( name, 1 );
- };
- THREE.MorphBlendMesh.prototype = Object.create( THREE.Mesh.prototype );
- THREE.MorphBlendMesh.prototype.createAnimation = function ( name, start, end, fps ) {
- var animation = {
- startFrame: start,
- endFrame: end,
- length: end - start + 1,
- fps: fps,
- duration: ( end - start ) / fps,
- lastFrame: 0,
- currentFrame: 0,
- active: false,
- time: 0,
- direction: 1,
- weight: 1,
- directionBackwards: false,
- mirroredLoop: false
- };
- this.animationsMap[ name ] = animation;
- this.animationsList.push( animation );
- };
- THREE.MorphBlendMesh.prototype.autoCreateAnimations = function ( fps ) {
- var pattern = /([a-z]+)(\d+)/;
- var firstAnimation, frameRanges = {};
- var geometry = this.geometry;
- for ( var i = 0, il = geometry.morphTargets.length; i < il; i ++ ) {
- var morph = geometry.morphTargets[ i ];
- var chunks = morph.name.match( pattern );
- if ( chunks && chunks.length > 1 ) {
- var name = chunks[ 1 ];
- var num = chunks[ 2 ];
- if ( ! frameRanges[ name ] ) frameRanges[ name ] = { start: Infinity, end: -Infinity };
- var range = frameRanges[ name ];
- if ( i < range.start ) range.start = i;
- if ( i > range.end ) range.end = i;
- if ( ! firstAnimation ) firstAnimation = name;
- }
- }
- for ( var name in frameRanges ) {
- var range = frameRanges[ name ];
- this.createAnimation( name, range.start, range.end, fps );
- }
- this.firstAnimation = firstAnimation;
- };
- THREE.MorphBlendMesh.prototype.setAnimationDirectionForward = function ( name ) {
- var animation = this.animationsMap[ name ];
- if ( animation ) {
- animation.direction = 1;
- animation.directionBackwards = false;
- }
- };
- THREE.MorphBlendMesh.prototype.setAnimationDirectionBackward = function ( name ) {
- var animation = this.animationsMap[ name ];
- if ( animation ) {
- animation.direction = -1;
- animation.directionBackwards = true;
- }
- };
- THREE.MorphBlendMesh.prototype.setAnimationFPS = function ( name, fps ) {
- var animation = this.animationsMap[ name ];
- if ( animation ) {
- animation.fps = fps;
- animation.duration = ( animation.end - animation.start ) / animation.fps;
- }
- };
- THREE.MorphBlendMesh.prototype.setAnimationDuration = function ( name, duration ) {
- var animation = this.animationsMap[ name ];
- if ( animation ) {
- animation.duration = duration;
- animation.fps = ( animation.end - animation.start ) / animation.duration;
- }
- };
- THREE.MorphBlendMesh.prototype.setAnimationWeight = function ( name, weight ) {
- var animation = this.animationsMap[ name ];
- if ( animation ) {
- animation.weight = weight;
- }
- };
- THREE.MorphBlendMesh.prototype.setAnimationTime = function ( name, time ) {
- var animation = this.animationsMap[ name ];
- if ( animation ) {
- animation.time = time;
- }
- };
- THREE.MorphBlendMesh.prototype.getAnimationTime = function ( name ) {
- var time = 0;
- var animation = this.animationsMap[ name ];
- if ( animation ) {
- time = animation.time;
- }
- return time;
- };
- THREE.MorphBlendMesh.prototype.getAnimationDuration = function ( name ) {
- var duration = -1;
- var animation = this.animationsMap[ name ];
- if ( animation ) {
- duration = animation.duration;
- }
- return duration;
- };
- THREE.MorphBlendMesh.prototype.playAnimation = function ( name ) {
- var animation = this.animationsMap[ name ];
- if ( animation ) {
- animation.time = 0;
- animation.active = true;
- } else {
- console.warn( "animation[" + name + "] undefined" );
- }
- };
- THREE.MorphBlendMesh.prototype.stopAnimation = function ( name ) {
- var animation = this.animationsMap[ name ];
- if ( animation ) {
- animation.active = false;
- }
- };
- THREE.MorphBlendMesh.prototype.update = function ( delta ) {
- for ( var i = 0, il = this.animationsList.length; i < il; i ++ ) {
- var animation = this.animationsList[ i ];
- if ( ! animation.active ) continue;
- var frameTime = animation.duration / animation.length;
- animation.time += animation.direction * delta;
- if ( animation.mirroredLoop ) {
- if ( animation.time > animation.duration || animation.time < 0 ) {
- animation.direction *= -1;
- if ( animation.time > animation.duration ) {
- animation.time = animation.duration;
- animation.directionBackwards = true;
- }
- if ( animation.time < 0 ) {
- animation.time = 0;
- animation.directionBackwards = false;
- }
- }
- } else {
- animation.time = animation.time % animation.duration;
- if ( animation.time < 0 ) animation.time += animation.duration;
- }
- var keyframe = animation.startFrame + THREE.Math.clamp( Math.floor( animation.time / frameTime ), 0, animation.length - 1 );
- var weight = animation.weight;
- if ( keyframe !== animation.currentFrame ) {
- this.morphTargetInfluences[ animation.lastFrame ] = 0;
- this.morphTargetInfluences[ animation.currentFrame ] = 1 * weight;
- this.morphTargetInfluences[ keyframe ] = 0;
- animation.lastFrame = animation.currentFrame;
- animation.currentFrame = keyframe;
- }
- var mix = ( animation.time % frameTime ) / frameTime;
- if ( animation.directionBackwards ) mix = 1 - mix;
- this.morphTargetInfluences[ animation.currentFrame ] = mix * weight;
- this.morphTargetInfluences[ animation.lastFrame ] = ( 1 - mix ) * weight;
- }
- };
- /**
- * @author mikael emtinger / http://gomo.se/
- * @author alteredq / http://alteredqualia.com/
- */
- THREE.LensFlarePlugin = function ( ) {
- var _gl, _renderer, _lensFlare = {};
- this.init = function ( renderer ) {
- _gl = renderer.context;
- _renderer = renderer;
- _lensFlare.vertices = new Float32Array( 8 + 8 );
- _lensFlare.faces = new Uint16Array( 6 );
- var i = 0;
- _lensFlare.vertices[ i++ ] = -1; _lensFlare.vertices[ i++ ] = -1; // vertex
- _lensFlare.vertices[ i++ ] = 0; _lensFlare.vertices[ i++ ] = 0; // uv... etc.
- _lensFlare.vertices[ i++ ] = 1; _lensFlare.vertices[ i++ ] = -1;
- _lensFlare.vertices[ i++ ] = 1; _lensFlare.vertices[ i++ ] = 0;
- _lensFlare.vertices[ i++ ] = 1; _lensFlare.vertices[ i++ ] = 1;
- _lensFlare.vertices[ i++ ] = 1; _lensFlare.vertices[ i++ ] = 1;
- _lensFlare.vertices[ i++ ] = -1; _lensFlare.vertices[ i++ ] = 1;
- _lensFlare.vertices[ i++ ] = 0; _lensFlare.vertices[ i++ ] = 1;
- i = 0;
- _lensFlare.faces[ i++ ] = 0; _lensFlare.faces[ i++ ] = 1; _lensFlare.faces[ i++ ] = 2;
- _lensFlare.faces[ i++ ] = 0; _lensFlare.faces[ i++ ] = 2; _lensFlare.faces[ i++ ] = 3;
- // buffers
- _lensFlare.vertexBuffer = _gl.createBuffer();
- _lensFlare.elementBuffer = _gl.createBuffer();
- _gl.bindBuffer( _gl.ARRAY_BUFFER, _lensFlare.vertexBuffer );
- _gl.bufferData( _gl.ARRAY_BUFFER, _lensFlare.vertices, _gl.STATIC_DRAW );
- _gl.bindBuffer( _gl.ELEMENT_ARRAY_BUFFER, _lensFlare.elementBuffer );
- _gl.bufferData( _gl.ELEMENT_ARRAY_BUFFER, _lensFlare.faces, _gl.STATIC_DRAW );
- // textures
- _lensFlare.tempTexture = _gl.createTexture();
- _lensFlare.occlusionTexture = _gl.createTexture();
- _gl.bindTexture( _gl.TEXTURE_2D, _lensFlare.tempTexture );
- _gl.texImage2D( _gl.TEXTURE_2D, 0, _gl.RGB, 16, 16, 0, _gl.RGB, _gl.UNSIGNED_BYTE, null );
- _gl.texParameteri( _gl.TEXTURE_2D, _gl.TEXTURE_WRAP_S, _gl.CLAMP_TO_EDGE );
- _gl.texParameteri( _gl.TEXTURE_2D, _gl.TEXTURE_WRAP_T, _gl.CLAMP_TO_EDGE );
- _gl.texParameteri( _gl.TEXTURE_2D, _gl.TEXTURE_MAG_FILTER, _gl.NEAREST );
- _gl.texParameteri( _gl.TEXTURE_2D, _gl.TEXTURE_MIN_FILTER, _gl.NEAREST );
- _gl.bindTexture( _gl.TEXTURE_2D, _lensFlare.occlusionTexture );
- _gl.texImage2D( _gl.TEXTURE_2D, 0, _gl.RGBA, 16, 16, 0, _gl.RGBA, _gl.UNSIGNED_BYTE, null );
- _gl.texParameteri( _gl.TEXTURE_2D, _gl.TEXTURE_WRAP_S, _gl.CLAMP_TO_EDGE );
- _gl.texParameteri( _gl.TEXTURE_2D, _gl.TEXTURE_WRAP_T, _gl.CLAMP_TO_EDGE );
- _gl.texParameteri( _gl.TEXTURE_2D, _gl.TEXTURE_MAG_FILTER, _gl.NEAREST );
- _gl.texParameteri( _gl.TEXTURE_2D, _gl.TEXTURE_MIN_FILTER, _gl.NEAREST );
- if ( _gl.getParameter( _gl.MAX_VERTEX_TEXTURE_IMAGE_UNITS ) <= 0 ) {
- _lensFlare.hasVertexTexture = false;
- _lensFlare.program = createProgram( THREE.ShaderFlares[ "lensFlare" ] );
- } else {
- _lensFlare.hasVertexTexture = true;
- _lensFlare.program = createProgram( THREE.ShaderFlares[ "lensFlareVertexTexture" ] );
- }
- _lensFlare.attributes = {};
- _lensFlare.uniforms = {};
- _lensFlare.attributes.vertex = _gl.getAttribLocation ( _lensFlare.program, "position" );
- _lensFlare.attributes.uv = _gl.getAttribLocation ( _lensFlare.program, "uv" );
- _lensFlare.uniforms.renderType = _gl.getUniformLocation( _lensFlare.program, "renderType" );
- _lensFlare.uniforms.map = _gl.getUniformLocation( _lensFlare.program, "map" );
- _lensFlare.uniforms.occlusionMap = _gl.getUniformLocation( _lensFlare.program, "occlusionMap" );
- _lensFlare.uniforms.opacity = _gl.getUniformLocation( _lensFlare.program, "opacity" );
- _lensFlare.uniforms.color = _gl.getUniformLocation( _lensFlare.program, "color" );
- _lensFlare.uniforms.scale = _gl.getUniformLocation( _lensFlare.program, "scale" );
- _lensFlare.uniforms.rotation = _gl.getUniformLocation( _lensFlare.program, "rotation" );
- _lensFlare.uniforms.screenPosition = _gl.getUniformLocation( _lensFlare.program, "screenPosition" );
- _lensFlare.attributesEnabled = false;
- };
- /*
- * Render lens flares
- * Method: renders 16x16 0xff00ff-colored points scattered over the light source area,
- * reads these back and calculates occlusion.
- * Then _lensFlare.update_lensFlares() is called to re-position and
- * update transparency of flares. Then they are rendered.
- *
- */
- this.render = function ( scene, camera, viewportWidth, viewportHeight ) {
- var flares = scene.__webglFlares,
- nFlares = flares.length;
- if ( ! nFlares ) return;
- var tempPosition = new THREE.Vector3();
- var invAspect = viewportHeight / viewportWidth,
- halfViewportWidth = viewportWidth * 0.5,
- halfViewportHeight = viewportHeight * 0.5;
- var size = 16 / viewportHeight,
- scale = new THREE.Vector2( size * invAspect, size );
- var screenPosition = new THREE.Vector3( 1, 1, 0 ),
- screenPositionPixels = new THREE.Vector2( 1, 1 );
- var uniforms = _lensFlare.uniforms,
- attributes = _lensFlare.attributes;
- // set _lensFlare program and reset blending
- _gl.useProgram( _lensFlare.program );
- if ( ! _lensFlare.attributesEnabled ) {
- _gl.enableVertexAttribArray( _lensFlare.attributes.vertex );
- _gl.enableVertexAttribArray( _lensFlare.attributes.uv );
- _lensFlare.attributesEnabled = true;
- }
- // loop through all lens flares to update their occlusion and positions
- // setup gl and common used attribs/unforms
- _gl.uniform1i( uniforms.occlusionMap, 0 );
- _gl.uniform1i( uniforms.map, 1 );
- _gl.bindBuffer( _gl.ARRAY_BUFFER, _lensFlare.vertexBuffer );
- _gl.vertexAttribPointer( attributes.vertex, 2, _gl.FLOAT, false, 2 * 8, 0 );
- _gl.vertexAttribPointer( attributes.uv, 2, _gl.FLOAT, false, 2 * 8, 8 );
- _gl.bindBuffer( _gl.ELEMENT_ARRAY_BUFFER, _lensFlare.elementBuffer );
- _gl.disable( _gl.CULL_FACE );
- _gl.depthMask( false );
- var i, j, jl, flare, sprite;
- for ( i = 0; i < nFlares; i ++ ) {
- size = 16 / viewportHeight;
- scale.set( size * invAspect, size );
- // calc object screen position
- flare = flares[ i ];
- tempPosition.set( flare.matrixWorld.elements[12], flare.matrixWorld.elements[13], flare.matrixWorld.elements[14] );
- camera.matrixWorldInverse.multiplyVector3( tempPosition );
- camera.projectionMatrix.multiplyVector3( tempPosition );
- // setup arrays for gl programs
- screenPosition.copy( tempPosition )
- screenPositionPixels.x = screenPosition.x * halfViewportWidth + halfViewportWidth;
- screenPositionPixels.y = screenPosition.y * halfViewportHeight + halfViewportHeight;
- // screen cull
- if ( _lensFlare.hasVertexTexture || (
- screenPositionPixels.x > 0 &&
- screenPositionPixels.x < viewportWidth &&
- screenPositionPixels.y > 0 &&
- screenPositionPixels.y < viewportHeight ) ) {
- // save current RGB to temp texture
- _gl.activeTexture( _gl.TEXTURE1 );
- _gl.bindTexture( _gl.TEXTURE_2D, _lensFlare.tempTexture );
- _gl.copyTexImage2D( _gl.TEXTURE_2D, 0, _gl.RGB, screenPositionPixels.x - 8, screenPositionPixels.y - 8, 16, 16, 0 );
- // render pink quad
- _gl.uniform1i( uniforms.renderType, 0 );
- _gl.uniform2f( uniforms.scale, scale.x, scale.y );
- _gl.uniform3f( uniforms.screenPosition, screenPosition.x, screenPosition.y, screenPosition.z );
- _gl.disable( _gl.BLEND );
- _gl.enable( _gl.DEPTH_TEST );
- _gl.drawElements( _gl.TRIANGLES, 6, _gl.UNSIGNED_SHORT, 0 );
- // copy result to occlusionMap
- _gl.activeTexture( _gl.TEXTURE0 );
- _gl.bindTexture( _gl.TEXTURE_2D, _lensFlare.occlusionTexture );
- _gl.copyTexImage2D( _gl.TEXTURE_2D, 0, _gl.RGBA, screenPositionPixels.x - 8, screenPositionPixels.y - 8, 16, 16, 0 );
- // restore graphics
- _gl.uniform1i( uniforms.renderType, 1 );
- _gl.disable( _gl.DEPTH_TEST );
- _gl.activeTexture( _gl.TEXTURE1 );
- _gl.bindTexture( _gl.TEXTURE_2D, _lensFlare.tempTexture );
- _gl.drawElements( _gl.TRIANGLES, 6, _gl.UNSIGNED_SHORT, 0 );
- // update object positions
- flare.positionScreen.copy( screenPosition )
- if ( flare.customUpdateCallback ) {
- flare.customUpdateCallback( flare );
- } else {
- flare.updateLensFlares();
- }
- // render flares
- _gl.uniform1i( uniforms.renderType, 2 );
- _gl.enable( _gl.BLEND );
- for ( j = 0, jl = flare.lensFlares.length; j < jl; j ++ ) {
- sprite = flare.lensFlares[ j ];
- if ( sprite.opacity > 0.001 && sprite.scale > 0.001 ) {
- screenPosition.x = sprite.x;
- screenPosition.y = sprite.y;
- screenPosition.z = sprite.z;
- size = sprite.size * sprite.scale / viewportHeight;
- scale.x = size * invAspect;
- scale.y = size;
- _gl.uniform3f( uniforms.screenPosition, screenPosition.x, screenPosition.y, screenPosition.z );
- _gl.uniform2f( uniforms.scale, scale.x, scale.y );
- _gl.uniform1f( uniforms.rotation, sprite.rotation );
- _gl.uniform1f( uniforms.opacity, sprite.opacity );
- _gl.uniform3f( uniforms.color, sprite.color.r, sprite.color.g, sprite.color.b );
- _renderer.setBlending( sprite.blending, sprite.blendEquation, sprite.blendSrc, sprite.blendDst );
- _renderer.setTexture( sprite.texture, 1 );
- _gl.drawElements( _gl.TRIANGLES, 6, _gl.UNSIGNED_SHORT, 0 );
- }
- }
- }
- }
- // restore gl
- _gl.enable( _gl.CULL_FACE );
- _gl.enable( _gl.DEPTH_TEST );
- _gl.depthMask( true );
- };
- function createProgram ( shader ) {
- var program = _gl.createProgram();
- var fragmentShader = _gl.createShader( _gl.FRAGMENT_SHADER );
- var vertexShader = _gl.createShader( _gl.VERTEX_SHADER );
- _gl.shaderSource( fragmentShader, shader.fragmentShader );
- _gl.shaderSource( vertexShader, shader.vertexShader );
- _gl.compileShader( fragmentShader );
- _gl.compileShader( vertexShader );
- _gl.attachShader( program, fragmentShader );
- _gl.attachShader( program, vertexShader );
- _gl.linkProgram( program );
- return program;
- };
- };/**
- * @author alteredq / http://alteredqualia.com/
- */
- THREE.ShadowMapPlugin = function ( ) {
- var _gl,
- _renderer,
- _depthMaterial, _depthMaterialMorph, _depthMaterialSkin,
- _frustum = new THREE.Frustum(),
- _projScreenMatrix = new THREE.Matrix4(),
- _min = new THREE.Vector3(),
- _max = new THREE.Vector3();
- this.init = function ( renderer ) {
- _gl = renderer.context;
- _renderer = renderer;
- var depthShader = THREE.ShaderLib[ "depthRGBA" ];
- var depthUniforms = THREE.UniformsUtils.clone( depthShader.uniforms );
- _depthMaterial = new THREE.ShaderMaterial( { fragmentShader: depthShader.fragmentShader, vertexShader: depthShader.vertexShader, uniforms: depthUniforms } );
- _depthMaterialMorph = new THREE.ShaderMaterial( { fragmentShader: depthShader.fragmentShader, vertexShader: depthShader.vertexShader, uniforms: depthUniforms, morphTargets: true } );
- _depthMaterialSkin = new THREE.ShaderMaterial( { fragmentShader: depthShader.fragmentShader, vertexShader: depthShader.vertexShader, uniforms: depthUniforms, skinning: true } );
- _depthMaterial._shadowPass = true;
- _depthMaterialMorph._shadowPass = true;
- _depthMaterialSkin._shadowPass = true;
- };
- this.render = function ( scene, camera ) {
- if ( ! ( _renderer.shadowMapEnabled && _renderer.shadowMapAutoUpdate ) ) return;
- this.update( scene, camera );
- };
- this.update = function ( scene, camera ) {
- var i, il, j, jl, n,
- shadowMap, shadowMatrix, shadowCamera,
- program, buffer, material,
- webglObject, object, light,
- renderList,
- lights = [],
- k = 0,
- fog = null;
- // set GL state for depth map
- _gl.clearColor( 1, 1, 1, 1 );
- _gl.disable( _gl.BLEND );
- _gl.enable( _gl.CULL_FACE );
- if ( _renderer.shadowMapCullFrontFaces ) {
- _gl.cullFace( _gl.FRONT );
- } else {
- _gl.cullFace( _gl.BACK );
- }
- _renderer.setDepthTest( true );
- // preprocess lights
- // - skip lights that are not casting shadows
- // - create virtual lights for cascaded shadow maps
- for ( i = 0, il = scene.__lights.length; i < il; i ++ ) {
- light = scene.__lights[ i ];
- if ( ! light.castShadow ) continue;
- if ( ( light instanceof THREE.DirectionalLight ) && light.shadowCascade ) {
- for ( n = 0; n < light.shadowCascadeCount; n ++ ) {
- var virtualLight;
- if ( ! light.shadowCascadeArray[ n ] ) {
- virtualLight = createVirtualLight( light, n );
- virtualLight.originalCamera = camera;
- var gyro = new THREE.Gyroscope();
- gyro.position = light.shadowCascadeOffset;
- gyro.add( virtualLight );
- gyro.add( virtualLight.target );
- camera.add( gyro );
- light.shadowCascadeArray[ n ] = virtualLight;
- console.log( "Created virtualLight", virtualLight );
- } else {
- virtualLight = light.shadowCascadeArray[ n ];
- }
- updateVirtualLight( light, n );
- lights[ k ] = virtualLight;
- k ++;
- }
- } else {
- lights[ k ] = light;
- k ++;
- }
- }
- // render depth map
- for ( i = 0, il = lights.length; i < il; i ++ ) {
- light = lights[ i ];
- if ( ! light.shadowMap ) {
- var pars = { minFilter: THREE.LinearFilter, magFilter: THREE.LinearFilter, format: THREE.RGBAFormat };
- light.shadowMap = new THREE.WebGLRenderTarget( light.shadowMapWidth, light.shadowMapHeight, pars );
- light.shadowMapSize = new THREE.Vector2( light.shadowMapWidth, light.shadowMapHeight );
- light.shadowMatrix = new THREE.Matrix4();
- }
- if ( ! light.shadowCamera ) {
- if ( light instanceof THREE.SpotLight ) {
- light.shadowCamera = new THREE.PerspectiveCamera( light.shadowCameraFov, light.shadowMapWidth / light.shadowMapHeight, light.shadowCameraNear, light.shadowCameraFar );
- } else if ( light instanceof THREE.DirectionalLight ) {
- light.shadowCamera = new THREE.OrthographicCamera( light.shadowCameraLeft, light.shadowCameraRight, light.shadowCameraTop, light.shadowCameraBottom, light.shadowCameraNear, light.shadowCameraFar );
- } else {
- console.error( "Unsupported light type for shadow" );
- continue;
- }
- scene.add( light.shadowCamera );
- if ( _renderer.autoUpdateScene ) scene.updateMatrixWorld();
- }
- if ( light.shadowCameraVisible && ! light.cameraHelper ) {
- light.cameraHelper = new THREE.CameraHelper( light.shadowCamera );
- light.shadowCamera.add( light.cameraHelper );
- }
- if ( light.isVirtual && virtualLight.originalCamera == camera ) {
- updateShadowCamera( camera, light );
- }
- shadowMap = light.shadowMap;
- shadowMatrix = light.shadowMatrix;
- shadowCamera = light.shadowCamera;
- shadowCamera.position.copy( light.matrixWorld.getPosition() );
- shadowCamera.lookAt( light.target.matrixWorld.getPosition() );
- shadowCamera.updateMatrixWorld();
- shadowCamera.matrixWorldInverse.getInverse( shadowCamera.matrixWorld );
- if ( light.cameraHelper ) light.cameraHelper.lines.visible = light.shadowCameraVisible;
- if ( light.shadowCameraVisible ) light.cameraHelper.update();
- // compute shadow matrix
- 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.multiplySelf( shadowCamera.projectionMatrix );
- shadowMatrix.multiplySelf( shadowCamera.matrixWorldInverse );
- // update camera matrices and frustum
- if ( ! shadowCamera._viewMatrixArray ) shadowCamera._viewMatrixArray = new Float32Array( 16 );
- if ( ! shadowCamera._projectionMatrixArray ) shadowCamera._projectionMatrixArray = new Float32Array( 16 );
- shadowCamera.matrixWorldInverse.flattenToArray( shadowCamera._viewMatrixArray );
- shadowCamera.projectionMatrix.flattenToArray( shadowCamera._projectionMatrixArray );
- _projScreenMatrix.multiply( shadowCamera.projectionMatrix, shadowCamera.matrixWorldInverse );
- _frustum.setFromMatrix( _projScreenMatrix );
- // render shadow map
- _renderer.setRenderTarget( shadowMap );
- _renderer.clear();
- // set object matrices & frustum culling
- renderList = scene.__webglObjects;
- for ( j = 0, jl = renderList.length; j < jl; j ++ ) {
- webglObject = renderList[ j ];
- object = webglObject.object;
- webglObject.render = false;
- if ( object.visible && object.castShadow ) {
- if ( ! ( object instanceof THREE.Mesh ) || ! ( object.frustumCulled ) || _frustum.contains( object ) ) {
- object._modelViewMatrix.multiply( shadowCamera.matrixWorldInverse, object.matrixWorld );
- webglObject.render = true;
- }
- }
- }
- // render regular objects
- for ( j = 0, jl = renderList.length; j < jl; j ++ ) {
- webglObject = renderList[ j ];
- if ( webglObject.render ) {
- object = webglObject.object;
- buffer = webglObject.buffer;
- // culling is overriden globally for all objects
- // while rendering depth map
- //_renderer.setObjectFaces( object );
- if ( object.customDepthMaterial ) {
- material = object.customDepthMaterial;
- } else if ( object.geometry.morphTargets.length ) {
- material = _depthMaterialMorph;
- } else if ( object instanceof THREE.SkinnedMesh ) {
- material = _depthMaterialSkin;
- } else {
- material = _depthMaterial;
- }
- if ( buffer instanceof THREE.BufferGeometry ) {
- _renderer.renderBufferDirect( shadowCamera, scene.__lights, fog, material, buffer, object );
- } else {
- _renderer.renderBuffer( shadowCamera, scene.__lights, fog, material, buffer, object );
- }
- }
- }
- // set matrices and render immediate objects
- renderList = scene.__webglObjectsImmediate;
- for ( j = 0, jl = renderList.length; j < jl; j ++ ) {
- webglObject = renderList[ j ];
- object = webglObject.object;
- if ( object.visible && object.castShadow ) {
- object._modelViewMatrix.multiply( shadowCamera.matrixWorldInverse, object.matrixWorld );
- _renderer.renderImmediateObject( shadowCamera, scene.__lights, fog, _depthMaterial, object );
- }
- }
- }
- // restore GL state
- var clearColor = _renderer.getClearColor(),
- clearAlpha = _renderer.getClearAlpha();
- _gl.clearColor( clearColor.r, clearColor.g, clearColor.b, clearAlpha );
- _gl.enable( _gl.BLEND );
- if ( _renderer.shadowMapCullFrontFaces ) {
- _gl.cullFace( _gl.BACK );
- }
- };
- function createVirtualLight( light, cascade ) {
- var virtualLight = new THREE.DirectionalLight();
- virtualLight.isVirtual = true;
- virtualLight.onlyShadow = true;
- virtualLight.castShadow = true;
- virtualLight.shadowCameraNear = light.shadowCameraNear;
- virtualLight.shadowCameraFar = light.shadowCameraFar;
- virtualLight.shadowCameraLeft = light.shadowCameraLeft;
- virtualLight.shadowCameraRight = light.shadowCameraRight;
- virtualLight.shadowCameraBottom = light.shadowCameraBottom;
- virtualLight.shadowCameraTop = light.shadowCameraTop;
- virtualLight.shadowCameraVisible = light.shadowCameraVisible;
- virtualLight.shadowDarkness = light.shadowDarkness;
- virtualLight.shadowBias = light.shadowCascadeBias[ cascade ];
- virtualLight.shadowMapWidth = light.shadowCascadeWidth[ cascade ];
- virtualLight.shadowMapHeight = light.shadowCascadeHeight[ cascade ];
- virtualLight.pointsWorld = [];
- virtualLight.pointsFrustum = [];
- var pointsWorld = virtualLight.pointsWorld,
- pointsFrustum = virtualLight.pointsFrustum;
- for ( var i = 0; i < 8; i ++ ) {
- pointsWorld[ i ] = new THREE.Vector3();
- pointsFrustum[ i ] = new THREE.Vector3();
- }
- var nearZ = light.shadowCascadeNearZ[ cascade ];
- var farZ = light.shadowCascadeFarZ[ cascade ];
- pointsFrustum[ 0 ].set( -1, -1, nearZ );
- pointsFrustum[ 1 ].set( 1, -1, nearZ );
- pointsFrustum[ 2 ].set( -1, 1, nearZ );
- pointsFrustum[ 3 ].set( 1, 1, nearZ );
- pointsFrustum[ 4 ].set( -1, -1, farZ );
- pointsFrustum[ 5 ].set( 1, -1, farZ );
- pointsFrustum[ 6 ].set( -1, 1, farZ );
- pointsFrustum[ 7 ].set( 1, 1, farZ );
- return virtualLight;
- }
- // Synchronize virtual light with the original light
- function updateVirtualLight( light, cascade ) {
- var virtualLight = light.shadowCascadeArray[ cascade ];
- virtualLight.position.copy( light.position );
- virtualLight.target.position.copy( light.target.position );
- virtualLight.lookAt( virtualLight.target );
- virtualLight.shadowCameraVisible = light.shadowCameraVisible;
- virtualLight.shadowDarkness = light.shadowDarkness;
- virtualLight.shadowBias = light.shadowCascadeBias[ cascade ];
- var nearZ = light.shadowCascadeNearZ[ cascade ];
- var farZ = light.shadowCascadeFarZ[ cascade ];
- var pointsFrustum = virtualLight.pointsFrustum;
- pointsFrustum[ 0 ].z = nearZ;
- pointsFrustum[ 1 ].z = nearZ;
- pointsFrustum[ 2 ].z = nearZ;
- pointsFrustum[ 3 ].z = nearZ;
- pointsFrustum[ 4 ].z = farZ;
- pointsFrustum[ 5 ].z = farZ;
- pointsFrustum[ 6 ].z = farZ;
- pointsFrustum[ 7 ].z = farZ;
- }
- // Fit shadow camera's ortho frustum to camera frustum
- function updateShadowCamera( camera, light ) {
- var shadowCamera = light.shadowCamera,
- pointsFrustum = light.pointsFrustum,
- pointsWorld = light.pointsWorld;
- _min.set( Infinity, Infinity, Infinity );
- _max.set( -Infinity, -Infinity, -Infinity );
- for ( var i = 0; i < 8; i ++ ) {
- var p = pointsWorld[ i ];
- p.copy( pointsFrustum[ i ] );
- THREE.ShadowMapPlugin.__projector.unprojectVector( p, camera );
- shadowCamera.matrixWorldInverse.multiplyVector3( p );
- if ( p.x < _min.x ) _min.x = p.x;
- if ( p.x > _max.x ) _max.x = p.x;
- if ( p.y < _min.y ) _min.y = p.y;
- if ( p.y > _max.y ) _max.y = p.y;
- if ( p.z < _min.z ) _min.z = p.z;
- if ( p.z > _max.z ) _max.z = p.z;
- }
- shadowCamera.left = _min.x;
- shadowCamera.right = _max.x;
- shadowCamera.top = _max.y;
- shadowCamera.bottom = _min.y;
- // can't really fit near/far
- //shadowCamera.near = _min.z;
- //shadowCamera.far = _max.z;
- shadowCamera.updateProjectionMatrix();
- }
- };
- THREE.ShadowMapPlugin.__projector = new THREE.Projector();
- /**
- * @author mikael emtinger / http://gomo.se/
- * @author alteredq / http://alteredqualia.com/
- */
- THREE.SpritePlugin = function ( ) {
- var _gl, _renderer, _sprite = {};
- this.init = function ( renderer ) {
- _gl = renderer.context;
- _renderer = renderer;
- _sprite.vertices = new Float32Array( 8 + 8 );
- _sprite.faces = new Uint16Array( 6 );
- var i = 0;
- _sprite.vertices[ i++ ] = -1; _sprite.vertices[ i++ ] = -1; // vertex 0
- _sprite.vertices[ i++ ] = 0; _sprite.vertices[ i++ ] = 0; // uv 0
- _sprite.vertices[ i++ ] = 1; _sprite.vertices[ i++ ] = -1; // vertex 1
- _sprite.vertices[ i++ ] = 1; _sprite.vertices[ i++ ] = 0; // uv 1
- _sprite.vertices[ i++ ] = 1; _sprite.vertices[ i++ ] = 1; // vertex 2
- _sprite.vertices[ i++ ] = 1; _sprite.vertices[ i++ ] = 1; // uv 2
- _sprite.vertices[ i++ ] = -1; _sprite.vertices[ i++ ] = 1; // vertex 3
- _sprite.vertices[ i++ ] = 0; _sprite.vertices[ i++ ] = 1; // uv 3
- i = 0;
- _sprite.faces[ i++ ] = 0; _sprite.faces[ i++ ] = 1; _sprite.faces[ i++ ] = 2;
- _sprite.faces[ i++ ] = 0; _sprite.faces[ i++ ] = 2; _sprite.faces[ i++ ] = 3;
- _sprite.vertexBuffer = _gl.createBuffer();
- _sprite.elementBuffer = _gl.createBuffer();
- _gl.bindBuffer( _gl.ARRAY_BUFFER, _sprite.vertexBuffer );
- _gl.bufferData( _gl.ARRAY_BUFFER, _sprite.vertices, _gl.STATIC_DRAW );
- _gl.bindBuffer( _gl.ELEMENT_ARRAY_BUFFER, _sprite.elementBuffer );
- _gl.bufferData( _gl.ELEMENT_ARRAY_BUFFER, _sprite.faces, _gl.STATIC_DRAW );
- _sprite.program = createProgram( THREE.ShaderSprite[ "sprite" ] );
- _sprite.attributes = {};
- _sprite.uniforms = {};
- _sprite.attributes.position = _gl.getAttribLocation ( _sprite.program, "position" );
- _sprite.attributes.uv = _gl.getAttribLocation ( _sprite.program, "uv" );
- _sprite.uniforms.uvOffset = _gl.getUniformLocation( _sprite.program, "uvOffset" );
- _sprite.uniforms.uvScale = _gl.getUniformLocation( _sprite.program, "uvScale" );
- _sprite.uniforms.rotation = _gl.getUniformLocation( _sprite.program, "rotation" );
- _sprite.uniforms.scale = _gl.getUniformLocation( _sprite.program, "scale" );
- _sprite.uniforms.alignment = _gl.getUniformLocation( _sprite.program, "alignment" );
- _sprite.uniforms.color = _gl.getUniformLocation( _sprite.program, "color" );
- _sprite.uniforms.map = _gl.getUniformLocation( _sprite.program, "map" );
- _sprite.uniforms.opacity = _gl.getUniformLocation( _sprite.program, "opacity" );
- _sprite.uniforms.useScreenCoordinates = _gl.getUniformLocation( _sprite.program, "useScreenCoordinates" );
- _sprite.uniforms.affectedByDistance = _gl.getUniformLocation( _sprite.program, "affectedByDistance" );
- _sprite.uniforms.screenPosition = _gl.getUniformLocation( _sprite.program, "screenPosition" );
- _sprite.uniforms.modelViewMatrix = _gl.getUniformLocation( _sprite.program, "modelViewMatrix" );
- _sprite.uniforms.projectionMatrix = _gl.getUniformLocation( _sprite.program, "projectionMatrix" );
- _sprite.attributesEnabled = false;
- };
- this.render = function ( scene, camera, viewportWidth, viewportHeight ) {
- var sprites = scene.__webglSprites,
- nSprites = sprites.length;
- if ( ! nSprites ) return;
- var attributes = _sprite.attributes,
- uniforms = _sprite.uniforms;
- var invAspect = viewportHeight / viewportWidth;
- var halfViewportWidth = viewportWidth * 0.5,
- halfViewportHeight = viewportHeight * 0.5;
- var mergeWith3D = true;
- // setup gl
- _gl.useProgram( _sprite.program );
- if ( ! _sprite.attributesEnabled ) {
- _gl.enableVertexAttribArray( attributes.position );
- _gl.enableVertexAttribArray( attributes.uv );
- _sprite.attributesEnabled = true;
- }
- _gl.disable( _gl.CULL_FACE );
- _gl.enable( _gl.BLEND );
- _gl.depthMask( true );
- _gl.bindBuffer( _gl.ARRAY_BUFFER, _sprite.vertexBuffer );
- _gl.vertexAttribPointer( attributes.position, 2, _gl.FLOAT, false, 2 * 8, 0 );
- _gl.vertexAttribPointer( attributes.uv, 2, _gl.FLOAT, false, 2 * 8, 8 );
- _gl.bindBuffer( _gl.ELEMENT_ARRAY_BUFFER, _sprite.elementBuffer );
- _gl.uniformMatrix4fv( uniforms.projectionMatrix, false, camera._projectionMatrixArray );
- _gl.activeTexture( _gl.TEXTURE0 );
- _gl.uniform1i( uniforms.map, 0 );
- // update positions and sort
- var i, sprite, screenPosition, size, scale = [];
- for( i = 0; i < nSprites; i ++ ) {
- sprite = sprites[ i ];
- if ( ! sprite.visible || sprite.opacity === 0 ) continue;
- if( ! sprite.useScreenCoordinates ) {
- sprite._modelViewMatrix.multiply( camera.matrixWorldInverse, sprite.matrixWorld );
- sprite.z = - sprite._modelViewMatrix.elements[14];
- } else {
- sprite.z = - sprite.position.z;
- }
- }
- sprites.sort( painterSort );
- // render all sprites
- for( i = 0; i < nSprites; i ++ ) {
- sprite = sprites[ i ];
- if ( ! sprite.visible || sprite.opacity === 0 ) continue;
- if ( sprite.map && sprite.map.image && sprite.map.image.width ) {
- if ( sprite.useScreenCoordinates ) {
- _gl.uniform1i( uniforms.useScreenCoordinates, 1 );
- _gl.uniform3f( uniforms.screenPosition, ( sprite.position.x - halfViewportWidth ) / halfViewportWidth,
- ( halfViewportHeight - sprite.position.y ) / halfViewportHeight,
- Math.max( 0, Math.min( 1, sprite.position.z ) ) );
- } else {
- _gl.uniform1i( uniforms.useScreenCoordinates, 0 );
- _gl.uniform1i( uniforms.affectedByDistance, sprite.affectedByDistance ? 1 : 0 );
- _gl.uniformMatrix4fv( uniforms.modelViewMatrix, false, sprite._modelViewMatrix.elements );
- }
- size = sprite.map.image.width / ( sprite.scaleByViewport ? viewportHeight : 1 );
- scale[ 0 ] = size * invAspect * sprite.scale.x;
- scale[ 1 ] = size * sprite.scale.y;
- _gl.uniform2f( uniforms.uvScale, sprite.uvScale.x, sprite.uvScale.y );
- _gl.uniform2f( uniforms.uvOffset, sprite.uvOffset.x, sprite.uvOffset.y );
- _gl.uniform2f( uniforms.alignment, sprite.alignment.x, sprite.alignment.y );
- _gl.uniform1f( uniforms.opacity, sprite.opacity );
- _gl.uniform3f( uniforms.color, sprite.color.r, sprite.color.g, sprite.color.b );
- _gl.uniform1f( uniforms.rotation, sprite.rotation );
- _gl.uniform2fv( uniforms.scale, scale );
- if ( sprite.mergeWith3D && !mergeWith3D ) {
- _gl.enable( _gl.DEPTH_TEST );
- mergeWith3D = true;
- } else if ( ! sprite.mergeWith3D && mergeWith3D ) {
- _gl.disable( _gl.DEPTH_TEST );
- mergeWith3D = false;
- }
- _renderer.setBlending( sprite.blending, sprite.blendEquation, sprite.blendSrc, sprite.blendDst );
- _renderer.setTexture( sprite.map, 0 );
- _gl.drawElements( _gl.TRIANGLES, 6, _gl.UNSIGNED_SHORT, 0 );
- }
- }
- // restore gl
- _gl.enable( _gl.CULL_FACE );
- _gl.enable( _gl.DEPTH_TEST );
- _gl.depthMask( true );
- };
- function createProgram ( shader ) {
- var program = _gl.createProgram();
- var fragmentShader = _gl.createShader( _gl.FRAGMENT_SHADER );
- var vertexShader = _gl.createShader( _gl.VERTEX_SHADER );
- _gl.shaderSource( fragmentShader, shader.fragmentShader );
- _gl.shaderSource( vertexShader, shader.vertexShader );
- _gl.compileShader( fragmentShader );
- _gl.compileShader( vertexShader );
- _gl.attachShader( program, fragmentShader );
- _gl.attachShader( program, vertexShader );
- _gl.linkProgram( program );
- return program;
- };
- function painterSort ( a, b ) {
- return b.z - a.z;
- };
- };/**
- * @author alteredq / http://alteredqualia.com/
- */
- THREE.DepthPassPlugin = function ( ) {
- this.enabled = false;
- this.renderTarget = null;
- var _gl,
- _renderer,
- _depthMaterial, _depthMaterialMorph,
- _frustum = new THREE.Frustum(),
- _projScreenMatrix = new THREE.Matrix4();
- this.init = function ( renderer ) {
- _gl = renderer.context;
- _renderer = renderer;
- var depthShader = THREE.ShaderLib[ "depthRGBA" ];
- var depthUniforms = THREE.UniformsUtils.clone( depthShader.uniforms );
- _depthMaterial = new THREE.ShaderMaterial( { fragmentShader: depthShader.fragmentShader, vertexShader: depthShader.vertexShader, uniforms: depthUniforms } );
- _depthMaterialMorph = new THREE.ShaderMaterial( { fragmentShader: depthShader.fragmentShader, vertexShader: depthShader.vertexShader, uniforms: depthUniforms, morphTargets: true } );
- _depthMaterial._shadowPass = true;
- _depthMaterialMorph._shadowPass = true;
- };
- this.render = function ( scene, camera ) {
- if ( ! this.enabled ) return;
- this.update( scene, camera );
- };
- this.update = function ( scene, camera ) {
- var i, il, j, jl, n,
- program, buffer, material,
- webglObject, object, light,
- renderList,
- fog = null;
- // set GL state for depth map
- _gl.clearColor( 1, 1, 1, 1 );
- _gl.disable( _gl.BLEND );
- _renderer.setDepthTest( true );
- // update scene
- if ( _renderer.autoUpdateScene ) scene.updateMatrixWorld();
- // update camera matrices and frustum
- if ( ! camera._viewMatrixArray ) camera._viewMatrixArray = new Float32Array( 16 );
- if ( ! camera._projectionMatrixArray ) camera._projectionMatrixArray = new Float32Array( 16 );
- camera.matrixWorldInverse.getInverse( camera.matrixWorld );
- camera.matrixWorldInverse.flattenToArray( camera._viewMatrixArray );
- camera.projectionMatrix.flattenToArray( camera._projectionMatrixArray );
- _projScreenMatrix.multiply( camera.projectionMatrix, camera.matrixWorldInverse );
- _frustum.setFromMatrix( _projScreenMatrix );
- // render depth map
- _renderer.setRenderTarget( this.renderTarget );
- _renderer.clear();
- // set object matrices & frustum culling
- renderList = scene.__webglObjects;
- for ( j = 0, jl = renderList.length; j < jl; j ++ ) {
- webglObject = renderList[ j ];
- object = webglObject.object;
- webglObject.render = false;
- if ( object.visible ) {
- if ( ! ( object instanceof THREE.Mesh ) || ! ( object.frustumCulled ) || _frustum.contains( object ) ) {
- //object.matrixWorld.flattenToArray( object._objectMatrixArray );
- object._modelViewMatrix.multiply( camera.matrixWorldInverse, object.matrixWorld);
- webglObject.render = true;
- }
- }
- }
- // render regular objects
- for ( j = 0, jl = renderList.length; j < jl; j ++ ) {
- webglObject = renderList[ j ];
- if ( webglObject.render ) {
- object = webglObject.object;
- buffer = webglObject.buffer;
- _renderer.setObjectFaces( object );
- if ( object.customDepthMaterial ) {
- material = object.customDepthMaterial;
- } else if ( object.geometry.morphTargets.length ) {
- material = _depthMaterialMorph;
- } else {
- material = _depthMaterial;
- }
- if ( buffer instanceof THREE.BufferGeometry ) {
- _renderer.renderBufferDirect( camera, scene.__lights, fog, material, buffer, object );
- } else {
- _renderer.renderBuffer( camera, scene.__lights, fog, material, buffer, object );
- }
- }
- }
- // set matrices and render immediate objects
- renderList = scene.__webglObjectsImmediate;
- for ( j = 0, jl = renderList.length; j < jl; j ++ ) {
- webglObject = renderList[ j ];
- object = webglObject.object;
- if ( object.visible && object.castShadow ) {
- /*
- if ( object.matrixAutoUpdate ) {
- object.matrixWorld.flattenToArray( object._objectMatrixArray );
- }
- */
- object._modelViewMatrix.multiply( camera.matrixWorldInverse, object.matrixWorld);
- _renderer.renderImmediateObject( camera, scene.__lights, fog, _depthMaterial, object );
- }
- }
- // restore GL state
- var clearColor = _renderer.getClearColor(),
- clearAlpha = _renderer.getClearAlpha();
- _gl.clearColor( clearColor.r, clearColor.g, clearColor.b, clearAlpha );
- _gl.enable( _gl.BLEND );
- };
- };
- /**
- * @author mikael emtinger / http://gomo.se/
- *
- */
- THREE.ShaderFlares = {
- 'lensFlareVertexTexture': {
- vertexShader: [
- "uniform vec3 screenPosition;",
- "uniform vec2 scale;",
- "uniform float rotation;",
- "uniform int renderType;",
- "uniform sampler2D occlusionMap;",
- "attribute vec2 position;",
- "attribute vec2 uv;",
- "varying vec2 vUV;",
- "varying float vVisibility;",
- "void main() {",
- "vUV = uv;",
- "vec2 pos = position;",
- "if( renderType == 2 ) {",
- "vec4 visibility = texture2D( occlusionMap, vec2( 0.1, 0.1 ) ) +",
- "texture2D( occlusionMap, vec2( 0.5, 0.1 ) ) +",
- "texture2D( occlusionMap, vec2( 0.9, 0.1 ) ) +",
- "texture2D( occlusionMap, vec2( 0.9, 0.5 ) ) +",
- "texture2D( occlusionMap, vec2( 0.9, 0.9 ) ) +",
- "texture2D( occlusionMap, vec2( 0.5, 0.9 ) ) +",
- "texture2D( occlusionMap, vec2( 0.1, 0.9 ) ) +",
- "texture2D( occlusionMap, vec2( 0.1, 0.5 ) ) +",
- "texture2D( occlusionMap, vec2( 0.5, 0.5 ) );",
- "vVisibility = ( visibility.r / 9.0 ) *",
- "( 1.0 - visibility.g / 9.0 ) *",
- "( visibility.b / 9.0 ) *",
- "( 1.0 - visibility.a / 9.0 );",
- "pos.x = cos( rotation ) * position.x - sin( rotation ) * position.y;",
- "pos.y = sin( rotation ) * position.x + cos( rotation ) * position.y;",
- "}",
- "gl_Position = vec4( ( pos * scale + screenPosition.xy ).xy, screenPosition.z, 1.0 );",
- "}"
- ].join( "\n" ),
- fragmentShader: [
- "precision mediump float;",
- "uniform sampler2D map;",
- "uniform float opacity;",
- "uniform int renderType;",
- "uniform vec3 color;",
- "varying vec2 vUV;",
- "varying float vVisibility;",
- "void main() {",
- // pink square
- "if( renderType == 0 ) {",
- "gl_FragColor = vec4( 1.0, 0.0, 1.0, 0.0 );",
- // restore
- "} else if( renderType == 1 ) {",
- "gl_FragColor = texture2D( map, vUV );",
- // flare
- "} else {",
- "vec4 texture = texture2D( map, vUV );",
- "texture.a *= opacity * vVisibility;",
- "gl_FragColor = texture;",
- "gl_FragColor.rgb *= color;",
- "}",
- "}"
- ].join( "\n" )
- },
- 'lensFlare': {
- vertexShader: [
- "uniform vec3 screenPosition;",
- "uniform vec2 scale;",
- "uniform float rotation;",
- "uniform int renderType;",
- "attribute vec2 position;",
- "attribute vec2 uv;",
- "varying vec2 vUV;",
- "void main() {",
- "vUV = uv;",
- "vec2 pos = position;",
- "if( renderType == 2 ) {",
- "pos.x = cos( rotation ) * position.x - sin( rotation ) * position.y;",
- "pos.y = sin( rotation ) * position.x + cos( rotation ) * position.y;",
- "}",
- "gl_Position = vec4( ( pos * scale + screenPosition.xy ).xy, screenPosition.z, 1.0 );",
- "}"
- ].join( "\n" ),
- fragmentShader: [
- "precision mediump float;",
- "uniform sampler2D map;",
- "uniform sampler2D occlusionMap;",
- "uniform float opacity;",
- "uniform int renderType;",
- "uniform vec3 color;",
- "varying vec2 vUV;",
- "void main() {",
- // pink square
- "if( renderType == 0 ) {",
- "gl_FragColor = vec4( texture2D( map, vUV ).rgb, 0.0 );",
- // restore
- "} else if( renderType == 1 ) {",
- "gl_FragColor = texture2D( map, vUV );",
- // flare
- "} else {",
- "float visibility = texture2D( occlusionMap, vec2( 0.5, 0.1 ) ).a +",
- "texture2D( occlusionMap, vec2( 0.9, 0.5 ) ).a +",
- "texture2D( occlusionMap, vec2( 0.5, 0.9 ) ).a +",
- "texture2D( occlusionMap, vec2( 0.1, 0.5 ) ).a;",
- "visibility = ( 1.0 - visibility / 4.0 );",
- "vec4 texture = texture2D( map, vUV );",
- "texture.a *= opacity * visibility;",
- "gl_FragColor = texture;",
- "gl_FragColor.rgb *= color;",
- "}",
- "}"
- ].join( "\n" )
- }
- };
- /**
- * @author mikael emtinger / http://gomo.se/
- *
- */
- THREE.ShaderSprite = {
- 'sprite': {
- vertexShader: [
- "uniform int useScreenCoordinates;",
- "uniform int affectedByDistance;",
- "uniform vec3 screenPosition;",
- "uniform mat4 modelViewMatrix;",
- "uniform mat4 projectionMatrix;",
- "uniform float rotation;",
- "uniform vec2 scale;",
- "uniform vec2 alignment;",
- "uniform vec2 uvOffset;",
- "uniform vec2 uvScale;",
- "attribute vec2 position;",
- "attribute vec2 uv;",
- "varying vec2 vUV;",
- "void main() {",
- "vUV = uvOffset + uv * uvScale;",
- "vec2 alignedPosition = position + alignment;",
- "vec2 rotatedPosition;",
- "rotatedPosition.x = ( cos( rotation ) * alignedPosition.x - sin( rotation ) * alignedPosition.y ) * scale.x;",
- "rotatedPosition.y = ( sin( rotation ) * alignedPosition.x + cos( rotation ) * alignedPosition.y ) * scale.y;",
- "vec4 finalPosition;",
- "if( useScreenCoordinates != 0 ) {",
- "finalPosition = vec4( screenPosition.xy + rotatedPosition, screenPosition.z, 1.0 );",
- "} else {",
- "finalPosition = projectionMatrix * modelViewMatrix * vec4( 0.0, 0.0, 0.0, 1.0 );",
- "finalPosition.xy += rotatedPosition * ( affectedByDistance == 1 ? 1.0 : finalPosition.z );",
- "}",
- "gl_Position = finalPosition;",
- "}"
- ].join( "\n" ),
- fragmentShader: [
- "precision mediump float;",
- "uniform vec3 color;",
- "uniform sampler2D map;",
- "uniform float opacity;",
- "varying vec2 vUV;",
- "void main() {",
- "vec4 texture = texture2D( map, vUV );",
- "gl_FragColor = vec4( color * texture.xyz, texture.a * opacity );",
- "}"
- ].join( "\n" )
- }
- };
|