12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788899091929394959697989910010110210310410510610710810911011111211311411511611711811912012112212312412512612712812913013113213313413513613713813914014114214314414514614714814915015115215315415515615715815916016116216316416516616716816917017117217317417517617717817918018118218318418518618718818919019119219319419519619719819920020120220320420520620720820921021121221321421521621721821922022122222322422522622722822923023123223323423523623723823924024124224324424524624724824925025125225325425525625725825926026126226326426526626726826927027127227327427527627727827928028128228328428528628728828929029129229329429529629729829930030130230330430530630730830931031131231331431531631731831932032132232332432532632732832933033133233333433533633733833934034134234334434534634734834935035135235335435535635735835936036136236336436536636736836937037137237337437537637737837938038138238338438538638738838939039139239339439539639739839940040140240340440540640740840941041141241341441541641741841942042142242342442542642742842943043143243343443543643743843944044144244344444544644744844945045145245345445545645745845946046146246346446546646746846947047147247347447547647747847948048148248348448548648748848949049149249349449549649749849950050150250350450550650750850951051151251351451551651751851952052152252352452552652752852953053153253353453553653753853954054154254354454554654754854955055155255355455555655755855956056156256356456556656756856957057157257357457557657757857958058158258358458558658758858959059159259359459559659759859960060160260360460560660760860961061161261361461561661761861962062162262362462562662762862963063163263363463563663763863964064164264364464564664764864965065165265365465565665765865966066166266366466566666766866967067167267367467567667767867968068168268368468568668768868969069169269369469569669769869970070170270370470570670770870971071171271371471571671771871972072172272372472572672772872973073173273373473573673773873974074174274374474574674774874975075175275375475575675775875976076176276376476576676776876977077177277377477577677777877978078178278378478578678778878979079179279379479579679779879980080180280380480580680780880981081181281381481581681781881982082182282382482582682782882983083183283383483583683783883984084184284384484584684784884985085185285385485585685785885986086186286386486586686786886987087187287387487587687787887988088188288388488588688788888989089189289389489589689789889990090190290390490590690790890991091191291391491591691791891992092192292392492592692792892993093193293393493593693793893994094194294394494594694794894995095195295395495595695795895996096196296396496596696796896997097197297397497597697797897998098198298398498598698798898999099199299399499599699799899910001001100210031004100510061007100810091010101110121013101410151016101710181019102010211022102310241025102610271028102910301031103210331034103510361037103810391040104110421043104410451046104710481049105010511052105310541055105610571058105910601061106210631064106510661067106810691070107110721073107410751076107710781079108010811082108310841085108610871088108910901091109210931094109510961097109810991100110111021103110411051106110711081109111011111112111311141115111611171118111911201121112211231124112511261127112811291130113111321133113411351136113711381139114011411142114311441145114611471148114911501151115211531154115511561157115811591160116111621163116411651166116711681169117011711172117311741175117611771178117911801181118211831184118511861187118811891190119111921193119411951196119711981199120012011202120312041205120612071208120912101211121212131214121512161217121812191220122112221223122412251226122712281229123012311232123312341235123612371238123912401241124212431244124512461247124812491250125112521253125412551256125712581259126012611262126312641265126612671268126912701271127212731274127512761277127812791280128112821283128412851286128712881289129012911292129312941295129612971298129913001301130213031304130513061307130813091310131113121313131413151316131713181319132013211322132313241325132613271328132913301331133213331334133513361337133813391340134113421343134413451346134713481349135013511352135313541355135613571358135913601361136213631364136513661367136813691370137113721373137413751376137713781379138013811382138313841385138613871388138913901391139213931394139513961397139813991400140114021403140414051406140714081409141014111412141314141415141614171418141914201421142214231424142514261427142814291430143114321433143414351436143714381439144014411442144314441445144614471448144914501451145214531454145514561457145814591460146114621463146414651466146714681469147014711472147314741475147614771478147914801481148214831484148514861487148814891490149114921493149414951496149714981499150015011502150315041505150615071508150915101511151215131514151515161517151815191520152115221523152415251526152715281529153015311532153315341535153615371538153915401541154215431544154515461547154815491550155115521553155415551556155715581559156015611562156315641565156615671568156915701571157215731574157515761577157815791580158115821583158415851586158715881589159015911592159315941595159615971598159916001601160216031604160516061607160816091610161116121613161416151616161716181619162016211622162316241625162616271628162916301631163216331634163516361637163816391640164116421643164416451646164716481649165016511652165316541655165616571658165916601661166216631664166516661667166816691670167116721673167416751676167716781679168016811682168316841685168616871688168916901691169216931694169516961697169816991700170117021703170417051706170717081709171017111712171317141715171617171718171917201721172217231724172517261727172817291730173117321733173417351736173717381739174017411742174317441745174617471748174917501751175217531754175517561757175817591760176117621763176417651766176717681769177017711772177317741775177617771778177917801781178217831784178517861787178817891790179117921793179417951796179717981799180018011802180318041805180618071808180918101811181218131814181518161817181818191820182118221823182418251826182718281829183018311832183318341835183618371838183918401841184218431844184518461847184818491850185118521853185418551856185718581859186018611862186318641865186618671868186918701871187218731874187518761877187818791880188118821883188418851886188718881889189018911892189318941895189618971898189919001901190219031904190519061907190819091910191119121913191419151916191719181919192019211922192319241925192619271928192919301931193219331934193519361937193819391940194119421943194419451946194719481949195019511952195319541955195619571958195919601961196219631964196519661967196819691970197119721973197419751976197719781979198019811982198319841985198619871988198919901991199219931994199519961997199819992000200120022003200420052006200720082009201020112012201320142015201620172018201920202021202220232024202520262027202820292030203120322033203420352036203720382039204020412042204320442045204620472048204920502051205220532054205520562057205820592060206120622063206420652066206720682069207020712072207320742075207620772078207920802081208220832084208520862087208820892090209120922093209420952096209720982099210021012102210321042105210621072108210921102111211221132114211521162117211821192120212121222123212421252126212721282129213021312132213321342135213621372138213921402141214221432144214521462147214821492150215121522153215421552156215721582159216021612162216321642165216621672168216921702171217221732174217521762177217821792180218121822183218421852186218721882189219021912192219321942195219621972198219922002201220222032204220522062207220822092210221122122213221422152216221722182219222022212222222322242225222622272228222922302231223222332234223522362237223822392240224122422243224422452246224722482249225022512252225322542255225622572258225922602261226222632264226522662267226822692270227122722273227422752276227722782279228022812282228322842285228622872288228922902291229222932294229522962297229822992300230123022303230423052306230723082309231023112312231323142315231623172318231923202321232223232324232523262327232823292330233123322333233423352336233723382339234023412342234323442345234623472348234923502351235223532354235523562357235823592360236123622363236423652366236723682369237023712372237323742375237623772378237923802381238223832384238523862387238823892390239123922393239423952396239723982399240024012402240324042405240624072408240924102411241224132414241524162417241824192420242124222423242424252426242724282429243024312432243324342435243624372438243924402441244224432444244524462447244824492450245124522453245424552456245724582459246024612462246324642465246624672468246924702471247224732474247524762477247824792480248124822483248424852486248724882489249024912492249324942495249624972498249925002501250225032504250525062507250825092510251125122513251425152516251725182519252025212522252325242525252625272528252925302531253225332534253525362537253825392540254125422543254425452546254725482549255025512552255325542555255625572558255925602561256225632564256525662567256825692570257125722573257425752576257725782579258025812582258325842585258625872588258925902591259225932594259525962597259825992600260126022603260426052606260726082609261026112612261326142615261626172618261926202621262226232624262526262627262826292630263126322633263426352636263726382639264026412642264326442645264626472648264926502651265226532654265526562657265826592660266126622663266426652666266726682669267026712672267326742675267626772678267926802681268226832684268526862687268826892690269126922693269426952696269726982699270027012702270327042705270627072708270927102711271227132714271527162717271827192720272127222723272427252726272727282729273027312732273327342735273627372738273927402741274227432744274527462747274827492750275127522753275427552756275727582759276027612762276327642765276627672768276927702771277227732774277527762777277827792780278127822783278427852786278727882789279027912792279327942795279627972798279928002801280228032804280528062807280828092810281128122813281428152816281728182819282028212822282328242825282628272828282928302831283228332834283528362837283828392840284128422843284428452846284728482849285028512852285328542855285628572858285928602861286228632864286528662867286828692870287128722873287428752876287728782879288028812882288328842885288628872888288928902891289228932894289528962897289828992900290129022903290429052906290729082909291029112912291329142915291629172918291929202921292229232924292529262927292829292930293129322933293429352936293729382939294029412942294329442945294629472948294929502951295229532954295529562957295829592960296129622963296429652966296729682969297029712972297329742975297629772978297929802981298229832984298529862987298829892990299129922993299429952996299729982999300030013002300330043005300630073008300930103011301230133014301530163017301830193020302130223023302430253026302730283029303030313032303330343035303630373038303930403041304230433044304530463047304830493050305130523053305430553056305730583059306030613062306330643065306630673068306930703071307230733074307530763077307830793080308130823083308430853086308730883089309030913092309330943095309630973098309931003101310231033104310531063107310831093110311131123113311431153116311731183119312031213122312331243125312631273128312931303131313231333134313531363137313831393140314131423143314431453146314731483149315031513152315331543155315631573158315931603161316231633164316531663167316831693170317131723173317431753176317731783179318031813182318331843185318631873188318931903191319231933194319531963197319831993200320132023203320432053206320732083209321032113212321332143215321632173218321932203221322232233224322532263227322832293230323132323233323432353236323732383239324032413242324332443245324632473248324932503251325232533254325532563257325832593260326132623263326432653266326732683269327032713272327332743275327632773278327932803281328232833284328532863287328832893290329132923293329432953296329732983299330033013302330333043305330633073308330933103311331233133314331533163317331833193320332133223323332433253326332733283329333033313332333333343335333633373338333933403341334233433344334533463347334833493350335133523353335433553356335733583359336033613362336333643365336633673368336933703371337233733374337533763377337833793380338133823383338433853386338733883389339033913392339333943395339633973398339934003401340234033404340534063407340834093410341134123413341434153416341734183419342034213422342334243425342634273428342934303431343234333434343534363437343834393440344134423443344434453446344734483449345034513452345334543455345634573458345934603461346234633464346534663467346834693470347134723473347434753476347734783479348034813482348334843485348634873488348934903491349234933494349534963497349834993500350135023503350435053506350735083509351035113512351335143515351635173518351935203521352235233524352535263527352835293530353135323533353435353536353735383539354035413542354335443545354635473548354935503551355235533554355535563557355835593560356135623563356435653566356735683569357035713572357335743575357635773578357935803581358235833584358535863587358835893590359135923593359435953596359735983599360036013602360336043605360636073608360936103611361236133614361536163617361836193620362136223623362436253626362736283629363036313632363336343635363636373638363936403641364236433644364536463647364836493650365136523653365436553656365736583659366036613662366336643665366636673668366936703671367236733674367536763677367836793680368136823683368436853686368736883689369036913692369336943695369636973698369937003701370237033704370537063707370837093710371137123713371437153716371737183719372037213722372337243725372637273728372937303731373237333734373537363737373837393740374137423743374437453746374737483749375037513752375337543755375637573758375937603761376237633764376537663767376837693770377137723773377437753776377737783779378037813782378337843785378637873788378937903791379237933794379537963797379837993800380138023803380438053806380738083809381038113812381338143815381638173818381938203821382238233824382538263827382838293830383138323833383438353836383738383839384038413842384338443845384638473848384938503851385238533854385538563857385838593860386138623863386438653866386738683869387038713872387338743875387638773878387938803881388238833884388538863887388838893890389138923893389438953896389738983899390039013902390339043905390639073908390939103911391239133914391539163917391839193920392139223923392439253926392739283929393039313932393339343935393639373938393939403941394239433944394539463947394839493950395139523953395439553956395739583959396039613962396339643965396639673968396939703971 |
- /******/ (function(modules) { // webpackBootstrap
- /******/ // The module cache
- /******/ var installedModules = {};
-
- /******/ // The require function
- /******/ function __webpack_require__(moduleId) {
-
- /******/ // Check if module is in cache
- /******/ if(installedModules[moduleId])
- /******/ return installedModules[moduleId].exports;
-
- /******/ // Create a new module (and put it into the cache)
- /******/ var module = installedModules[moduleId] = {
- /******/ exports: {},
- /******/ id: moduleId,
- /******/ loaded: false
- /******/ };
-
- /******/ // Execute the module function
- /******/ modules[moduleId].call(module.exports, module, module.exports, __webpack_require__);
-
- /******/ // Flag the module as loaded
- /******/ module.loaded = true;
-
- /******/ // Return the exports of the module
- /******/ return module.exports;
- /******/ }
-
-
- /******/ // expose the modules object (__webpack_modules__)
- /******/ __webpack_require__.m = modules;
-
- /******/ // expose the module cache
- /******/ __webpack_require__.c = installedModules;
-
- /******/ // __webpack_public_path__
- /******/ __webpack_require__.p = "";
-
- /******/ // Load entry module and return exports
- /******/ return __webpack_require__(0);
- /******/ })
- /************************************************************************/
- /******/ ([
- /* 0 */
- /***/ (function(module, exports, __webpack_require__) {
-
- /**
- * Particles component for A-Frame.
- *
- * ShaderParticleEngine by Squarefeet (https://github.com/squarefeet).
- */
-
- var SPE = __webpack_require__(1);
-
- if (typeof AFRAME === 'undefined') {
- throw new Error('Component attempted to register before AFRAME was available.');
- }
-
- AFRAME.registerComponent('particle-system', {
-
- schema: {
- preset: {
- type: 'string',
- default: '',
- oneOf: ['default', 'dust', 'snow', 'rain']
- },
- maxAge: {
- type: 'number',
- default: 6
- },
- positionSpread: {
- type: 'vec3',
- default: { x: 0, y: 0, z: 0 }
- },
- type: {
- type: 'number',
- default: SPE.distributions.BOX
- },
- rotationAxis: {
- type: 'string',
- default: 'x'
- },
- rotationAngle: {
- type: 'number',
- default: 0
- },
- rotationAngleSpread: {
- type: 'number',
- default: 0
- },
- accelerationValue: {
- type: 'vec3',
- default: { x: 0, y: -10, z: 0 }
- },
- accelerationSpread: {
- type: 'vec3',
- default: { x: 10, y: 0, z: 10 }
- },
- velocityValue: {
- type: 'vec3',
- default: { x: 0, y: 25, z: 0 }
- },
- velocitySpread: {
- type: 'vec3',
- default: { x: 10, y: 7.5, z: 10 }
- },
- dragValue: {
- type: 'number',
- default: 0
- },
- dragSpread: {
- type: 'number',
- default: 0
- },
- dragRandomise: {
- type: 'boolean',
- default: false
- },
- color: {
- type: 'array',
- default: [ '#0000FF', '#FF0000' ]
- },
- size: {
- type: 'array',
- default: [ '1' ]
- },
- sizeSpread: {
- type: 'array',
- default: [ '0' ]
- },
- direction: {
- type: 'number',
- default: 1
- },
- duration: {
- type: 'number',
- default: Infinity
- },
- particleCount: {
- type: 'number',
- default: 1000
- },
- texture: {
- type: 'asset',
- default: 'https://cdn.rawgit.com/IdeaSpaceVR/aframe-particle-system-component/master/dist/images/star2.png'
- },
- randomise: {
- type: 'boolean',
- default: false
- },
- opacity: {
- type: 'array',
- default: [ '1' ]
- },
- opacitySpread: {
- type: 'array',
- default: [ '0' ]
- },
- maxParticleCount: {
- type: 'number',
- default: 250000
- },
- blending: {
- type: 'number',
- default: THREE.AdditiveBlending,
- oneOf: [THREE.NoBlending,THREE.NormalBlending,THREE.AdditiveBlending,THREE.SubtractiveBlending,THREE.MultiplyBlending]
- },
- enabled: {
- type:'boolean',
- default:true
- }
- },
-
-
- init: function() {
-
- this.presets = {};
-
- /* preset settings can be overwritten */
-
- this.presets['dust'] = {
- maxAge: 20,
- positionSpread: {x:100,y:100,z:100},
- rotationAngle: 3.14,
- accelerationValue: {x: 0, y: 0, z: 0},
- accelerationSpread: {x: 0, y: 0, z: 0},
- velocityValue: {x: 1, y: 0.3, z: 1},
- velocitySpread: {x: 0.5, y: 1, z: 0.5},
- color: ['#FFFFFF'],
- particleCount: 100,
- texture: 'https://cdn.rawgit.com/IdeaSpaceVR/aframe-particle-system-component/master/dist/images/smokeparticle.png'
- };
-
-
- this.presets['snow'] = {
- maxAge: 20,
- positionSpread: {x:100,y:100,z:100},
- rotationAngle: 3.14,
- accelerationValue: {x: 0, y: 0, z: 0},
- accelerationSpread: {x: 0.2, y: 0, z: 0.2},
- velocityValue: {x: 0, y: 8, z: 0},
- velocitySpread: {x: 2, y: 0, z: 2},
- color: ['#FFFFFF'],
- particleCount: 200,
- texture: 'https://cdn.rawgit.com/IdeaSpaceVR/aframe-particle-system-component/master/dist/images/smokeparticle.png'
- };
-
-
- this.presets['rain'] = {
- maxAge: 1,
- positionSpread: {x:100,y:100,z:100},
- rotationAngle: 3.14,
- accelerationValue: {x: 0, y: 3, z: 0},
- accelerationSpread: {x: 2, y: 1, z: 2},
- velocityValue: {x: 0, y: 75, z: 0},
- velocitySpread: {x: 10, y: 50, z: 10},
- color: ['#FFFFFF'],
- size: 0.4,
- texture: 'https://cdn.rawgit.com/IdeaSpaceVR/aframe-particle-system-component/master/dist/images/raindrop.png'
- };
-
-
- },
-
-
- update: function (oldData) {
-
- // Remove old particle group.
- if (this.particleGroup) {
- this.el.removeObject3D('particle-system');
- }
-
- // Set the selected preset, if any, or use an empty object to keep schema defaults
- this.preset = this.presets[this.data.preset] || {};
-
- // Get custom, preset, or default data for each property defined in the schema
- for (var key in this.data) {
- this.data[key] = this.applyPreset(key);
- }
-
- this.initParticleSystem(this.data);
-
- if(this.data.enabled === true) {
- this.startParticles()
- } else {
- this.stopParticles()
- }
- },
-
-
- applyPreset: function (key) {
- // !this.attrValue[key] = the user did not set a custom value
- // this.preset[key] = there exists a value for this key in the selected preset
- if (!this.attrValue[key] && this.preset[key]) {
- return this.preset[key];
- } else {
- // Otherwise stick to the user or schema default value
- return this.data[key];
- }
- },
-
-
- tick: function(time, dt) {
-
- this.particleGroup.tick(dt / 1000);
- },
-
-
- remove: function() {
-
- // Remove particle system.
- if (!this.particleGroup) { return; }
- this.el.removeObject3D('particle-system');
- },
-
- startParticles: function() {
- this.particleGroup.emitters.forEach(function(em) { em.enable() });
- },
-
- stopParticles: function() {
- this.particleGroup.emitters.forEach(function(em) { em.disable() });
- },
-
-
- initParticleSystem: function(settings) {
-
- var loader = new THREE.TextureLoader();
- var particle_texture = loader.load(
- settings.texture,
- function (texture) {
- return texture;
- },
- function (xhr) {
- console.log((xhr.loaded / xhr.total * 100) + '% loaded');
- },
- function (xhr) {
- console.log('An error occurred');
- }
- );
-
- this.particleGroup = new SPE.Group({
- texture: {
- value: particle_texture
- },
- maxParticleCount: settings.maxParticleCount,
- blending: settings.blending
- });
-
- var emitter = new SPE.Emitter({
- maxAge: {
- value: settings.maxAge
- },
- type: {
- value: settings.type
- },
- position: {
- spread: new THREE.Vector3(settings.positionSpread.x, settings.positionSpread.y, settings.positionSpread.z),
- randomise: settings.randomise
- //spreadClamp: new THREE.Vector3( 2, 2, 2 ),
- //radius: 4
- },
- rotation: {
- axis: (settings.rotationAxis=='x'?new THREE.Vector3(1, 0, 0):(settings.rotationAxis=='y'?new THREE.Vector3(0, 1, 0):(settings.rotationAxis=='z'?new THREE.Vector3(0, 0, 1):new THREE.Vector3(0, 1, 0)))),
- angle: settings.rotationAngle,
- angleSpread: settings.rotationAngleSpread,
- static: true
- },
- acceleration: {
- value: new THREE.Vector3(settings.accelerationValue.x, settings.accelerationValue.y, settings.accelerationValue.z),
- spread: new THREE.Vector3(settings.accelerationSpread.x, settings.accelerationSpread.y, settings.accelerationSpread.z)
- },
- velocity: {
- value: new THREE.Vector3(settings.velocityValue.x, settings.velocityValue.y, settings.velocityValue.z),
- spread: new THREE.Vector3(settings.velocitySpread.x, settings.velocitySpread.y, settings.velocitySpread.z)
- },
- drag: {
- value: new THREE.Vector3(settings.dragValue.x, settings.dragValue.y, settings.dragValue.z),
- spread: new THREE.Vector3(settings.dragSpread.x, settings.dragSpread.y, settings.dragSpread.z),
- randomise: settings.dragRandomise
- },
- color: {
- value: settings.color.map(function(c) { return new THREE.Color(c); })
- },
- size: { value: settings.size.map(function (s) { return parseFloat(s); }),
- spread: settings.sizeSpread.map(function (s) { return parseFloat(s); }) },
-
- /*wiggle: { value: 4, spread: 2 }, //settings.wiggle,*/
- /*drag: {
- value: settings.drag
- },*/
- direction: {
- value: settings.direction
- },
- duration: settings.duration,
- opacity: { value: settings.opacity.map(function (o) { return parseFloat(o); }),
- spread: settings.opacitySpread.map(function (o) { return parseFloat(o); }) },
- particleCount: settings.particleCount
- });
-
- this.particleGroup.addEmitter(emitter);
- this.particleGroup.mesh.frustumCulled = false;
- this.el.setObject3D('particle-system', this.particleGroup.mesh);
- }
- });
-
-
- /***/ }),
- /* 1 */
- /***/ (function(module, exports, __webpack_require__) {
-
- var __WEBPACK_AMD_DEFINE_FACTORY__, __WEBPACK_AMD_DEFINE_RESULT__;/* shader-particle-engine 1.0.6
- *
- * (c) 2015 Luke Moody (http://www.github.com/squarefeet)
- * Originally based on Lee Stemkoski's original work (https://github.com/stemkoski/stemkoski.github.com/blob/master/Three.js/js/ParticleEngine.js).
- *
- * shader-particle-engine may be freely distributed under the MIT license (See LICENSE at root of this repository.)
- */
- /**
- * @typedef {Number} distribution
- * @property {Number} SPE.distributions.BOX Values will be distributed within a box.
- * @property {Number} SPE.distributions.SPHERE Values will be distributed within a sphere.
- * @property {Number} SPE.distributions.DISC Values will be distributed within a 2D disc.
- */
-
- /**
- * Namespace for Shader Particle Engine.
- *
- * All SPE-related code sits under this namespace.
- *
- * @type {Object}
- * @namespace
- */
- var SPE = {
-
- /**
- * A map of supported distribution types used
- * by SPE.Emitter instances.
- *
- * These distribution types can be applied to
- * an emitter globally, which will affect the
- * `position`, `velocity`, and `acceleration`
- * value calculations for an emitter, or they
- * can be applied on a per-property basis.
- *
- * @enum {Number}
- */
- distributions: {
- /**
- * Values will be distributed within a box.
- * @type {Number}
- */
- BOX: 1,
-
- /**
- * Values will be distributed on a sphere.
- * @type {Number}
- */
- SPHERE: 2,
-
- /**
- * Values will be distributed on a 2d-disc shape.
- * @type {Number}
- */
- DISC: 3,
-
- /**
- * Values will be distributed along a line.
- * @type {Number}
- */
- LINE: 4
- },
-
-
- /**
- * Set this value to however many 'steps' you
- * want value-over-lifetime properties to have.
- *
- * It's adjustable to fix an interpolation problem:
- *
- * Assuming you specify an opacity value as [0, 1, 0]
- * and the `valueOverLifetimeLength` is 4, then the
- * opacity value array will be reinterpolated to
- * be [0, 0.66, 0.66, 0].
- * This isn't ideal, as particles would never reach
- * full opacity.
- *
- * NOTE:
- * This property affects the length of ALL
- * value-over-lifetime properties for ALL
- * emitters and ALL groups.
- *
- * Only values >= 3 && <= 4 are allowed.
- *
- * @type {Number}
- */
- valueOverLifetimeLength: 4
- };
-
- // Module loader support:
- if ( true ) {
- !(__WEBPACK_AMD_DEFINE_FACTORY__ = (SPE), __WEBPACK_AMD_DEFINE_RESULT__ = (typeof __WEBPACK_AMD_DEFINE_FACTORY__ === 'function' ? (__WEBPACK_AMD_DEFINE_FACTORY__.call(exports, __webpack_require__, exports, module)) : __WEBPACK_AMD_DEFINE_FACTORY__), __WEBPACK_AMD_DEFINE_RESULT__ !== undefined && (module.exports = __WEBPACK_AMD_DEFINE_RESULT__));
- }
- else if ( typeof exports !== 'undefined' && typeof module !== 'undefined' ) {
- module.exports = SPE;
- }
-
-
- /**
- * A helper class for TypedArrays.
- *
- * Allows for easy resizing, assignment of various component-based
- * types (Vector2s, Vector3s, Vector4s, Mat3s, Mat4s),
- * as well as Colors (where components are `r`, `g`, `b`),
- * Numbers, and setting from other TypedArrays.
- *
- * @author Luke Moody
- * @constructor
- * @param {Function} TypedArrayConstructor The constructor to use (Float32Array, Uint8Array, etc.)
- * @param {Number} size The size of the array to create
- * @param {Number} componentSize The number of components per-value (ie. 3 for a vec3, 9 for a Mat3, etc.)
- * @param {Number} indexOffset The index in the array from which to start assigning values. Default `0` if none provided
- */
- SPE.TypedArrayHelper = function( TypedArrayConstructor, size, componentSize, indexOffset ) {
- 'use strict';
-
- this.componentSize = componentSize || 1;
- this.size = ( size || 1 );
- this.TypedArrayConstructor = TypedArrayConstructor || Float32Array;
- this.array = new TypedArrayConstructor( size * this.componentSize );
- this.indexOffset = indexOffset || 0;
- };
-
- SPE.TypedArrayHelper.constructor = SPE.TypedArrayHelper;
-
- /**
- * Sets the size of the internal array.
- *
- * Delegates to `this.shrink` or `this.grow` depending on size
- * argument's relation to the current size of the internal array.
- *
- * Note that if the array is to be shrunk, data will be lost.
- *
- * @param {Number} size The new size of the array.
- */
- SPE.TypedArrayHelper.prototype.setSize = function( size, noComponentMultiply ) {
- 'use strict';
-
- var currentArraySize = this.array.length;
-
- if ( !noComponentMultiply ) {
- size = size * this.componentSize;
- }
-
- if ( size < currentArraySize ) {
- return this.shrink( size );
- }
- else if ( size > currentArraySize ) {
- return this.grow( size );
- }
- else {
- console.info( 'TypedArray is already of size:', size + '.', 'Will not resize.' );
- }
- };
-
- /**
- * Shrinks the internal array.
- *
- * @param {Number} size The new size of the typed array. Must be smaller than `this.array.length`.
- * @return {SPE.TypedArrayHelper} Instance of this class.
- */
- SPE.TypedArrayHelper.prototype.shrink = function( size ) {
- 'use strict';
-
- this.array = this.array.subarray( 0, size );
- this.size = size;
- return this;
- };
-
- /**
- * Grows the internal array.
- * @param {Number} size The new size of the typed array. Must be larger than `this.array.length`.
- * @return {SPE.TypedArrayHelper} Instance of this class.
- */
- SPE.TypedArrayHelper.prototype.grow = function( size ) {
- 'use strict';
-
- var existingArray = this.array,
- newArray = new this.TypedArrayConstructor( size );
-
- newArray.set( existingArray );
- this.array = newArray;
- this.size = size;
-
- return this;
- };
-
-
- /**
- * Perform a splice operation on this array's buffer.
- * @param {Number} start The start index of the splice. Will be multiplied by the number of components for this attribute.
- * @param {Number} end The end index of the splice. Will be multiplied by the number of components for this attribute.
- * @returns {Object} The SPE.TypedArrayHelper instance.
- */
- SPE.TypedArrayHelper.prototype.splice = function( start, end ) {
- 'use strict';
- start *= this.componentSize;
- end *= this.componentSize;
-
- var data = [],
- array = this.array,
- size = array.length;
-
- for ( var i = 0; i < size; ++i ) {
- if ( i < start || i >= end ) {
- data.push( array[ i ] );
- }
- // array[ i ] = 0;
- }
-
- this.setFromArray( 0, data );
-
- return this;
- };
-
-
- /**
- * Copies from the given TypedArray into this one, using the index argument
- * as the start position. Alias for `TypedArray.set`. Will automatically resize
- * if the given source array is of a larger size than the internal array.
- *
- * @param {Number} index The start position from which to copy into this array.
- * @param {TypedArray} array The array from which to copy; the source array.
- * @return {SPE.TypedArrayHelper} Instance of this class.
- */
- SPE.TypedArrayHelper.prototype.setFromArray = function( index, array ) {
- 'use strict';
-
- var sourceArraySize = array.length,
- newSize = index + sourceArraySize;
-
- if ( newSize > this.array.length ) {
- this.grow( newSize );
- }
- else if ( newSize < this.array.length ) {
- this.shrink( newSize );
- }
-
- this.array.set( array, this.indexOffset + index );
-
- return this;
- };
-
- /**
- * Set a Vector2 value at `index`.
- *
- * @param {Number} index The index at which to set the vec2 values from.
- * @param {Vector2} vec2 Any object that has `x` and `y` properties.
- * @return {SPE.TypedArrayHelper} Instance of this class.
- */
- SPE.TypedArrayHelper.prototype.setVec2 = function( index, vec2 ) {
- 'use strict';
-
- return this.setVec2Components( index, vec2.x, vec2.y );
- };
-
- /**
- * Set a Vector2 value using raw components.
- *
- * @param {Number} index The index at which to set the vec2 values from.
- * @param {Number} x The Vec2's `x` component.
- * @param {Number} y The Vec2's `y` component.
- * @return {SPE.TypedArrayHelper} Instance of this class.
- */
- SPE.TypedArrayHelper.prototype.setVec2Components = function( index, x, y ) {
- 'use strict';
-
- var array = this.array,
- i = this.indexOffset + ( index * this.componentSize );
-
- array[ i ] = x;
- array[ i + 1 ] = y;
- return this;
- };
-
- /**
- * Set a Vector3 value at `index`.
- *
- * @param {Number} index The index at which to set the vec3 values from.
- * @param {Vector3} vec2 Any object that has `x`, `y`, and `z` properties.
- * @return {SPE.TypedArrayHelper} Instance of this class.
- */
- SPE.TypedArrayHelper.prototype.setVec3 = function( index, vec3 ) {
- 'use strict';
-
- return this.setVec3Components( index, vec3.x, vec3.y, vec3.z );
- };
-
- /**
- * Set a Vector3 value using raw components.
- *
- * @param {Number} index The index at which to set the vec3 values from.
- * @param {Number} x The Vec3's `x` component.
- * @param {Number} y The Vec3's `y` component.
- * @param {Number} z The Vec3's `z` component.
- * @return {SPE.TypedArrayHelper} Instance of this class.
- */
- SPE.TypedArrayHelper.prototype.setVec3Components = function( index, x, y, z ) {
- 'use strict';
-
- var array = this.array,
- i = this.indexOffset + ( index * this.componentSize );
-
- array[ i ] = x;
- array[ i + 1 ] = y;
- array[ i + 2 ] = z;
- return this;
- };
-
- /**
- * Set a Vector4 value at `index`.
- *
- * @param {Number} index The index at which to set the vec4 values from.
- * @param {Vector4} vec2 Any object that has `x`, `y`, `z`, and `w` properties.
- * @return {SPE.TypedArrayHelper} Instance of this class.
- */
- SPE.TypedArrayHelper.prototype.setVec4 = function( index, vec4 ) {
- 'use strict';
-
- return this.setVec4Components( index, vec4.x, vec4.y, vec4.z, vec4.w );
- };
-
- /**
- * Set a Vector4 value using raw components.
- *
- * @param {Number} index The index at which to set the vec4 values from.
- * @param {Number} x The Vec4's `x` component.
- * @param {Number} y The Vec4's `y` component.
- * @param {Number} z The Vec4's `z` component.
- * @param {Number} w The Vec4's `w` component.
- * @return {SPE.TypedArrayHelper} Instance of this class.
- */
- SPE.TypedArrayHelper.prototype.setVec4Components = function( index, x, y, z, w ) {
- 'use strict';
-
- var array = this.array,
- i = this.indexOffset + ( index * this.componentSize );
-
- array[ i ] = x;
- array[ i + 1 ] = y;
- array[ i + 2 ] = z;
- array[ i + 3 ] = w;
- return this;
- };
-
- /**
- * Set a Matrix3 value at `index`.
- *
- * @param {Number} index The index at which to set the matrix values from.
- * @param {Matrix3} mat3 The 3x3 matrix to set from. Must have a TypedArray property named `elements` to copy from.
- * @return {SPE.TypedArrayHelper} Instance of this class.
- */
- SPE.TypedArrayHelper.prototype.setMat3 = function( index, mat3 ) {
- 'use strict';
-
- return this.setFromArray( this.indexOffset + ( index * this.componentSize ), mat3.elements );
- };
-
- /**
- * Set a Matrix4 value at `index`.
- *
- * @param {Number} index The index at which to set the matrix values from.
- * @param {Matrix4} mat3 The 4x4 matrix to set from. Must have a TypedArray property named `elements` to copy from.
- * @return {SPE.TypedArrayHelper} Instance of this class.
- */
- SPE.TypedArrayHelper.prototype.setMat4 = function( index, mat4 ) {
- 'use strict';
-
- return this.setFromArray( this.indexOffset + ( index * this.componentSize ), mat4.elements );
- };
-
- /**
- * Set a Color value at `index`.
- *
- * @param {Number} index The index at which to set the vec3 values from.
- * @param {Color} color Any object that has `r`, `g`, and `b` properties.
- * @return {SPE.TypedArrayHelper} Instance of this class.
- */
- SPE.TypedArrayHelper.prototype.setColor = function( index, color ) {
- 'use strict';
-
- return this.setVec3Components( index, color.r, color.g, color.b );
- };
-
- /**
- * Set a Number value at `index`.
- *
- * @param {Number} index The index at which to set the vec3 values from.
- * @param {Number} numericValue The number to assign to this index in the array.
- * @return {SPE.TypedArrayHelper} Instance of this class.
- */
- SPE.TypedArrayHelper.prototype.setNumber = function( index, numericValue ) {
- 'use strict';
-
- this.array[ this.indexOffset + ( index * this.componentSize ) ] = numericValue;
- return this;
- };
-
- /**
- * Returns the value of the array at the given index, taking into account
- * the `indexOffset` property of this class.
- *
- * Note that this function ignores the component size and will just return a
- * single value.
- *
- * @param {Number} index The index in the array to fetch.
- * @return {Number} The value at the given index.
- */
- SPE.TypedArrayHelper.prototype.getValueAtIndex = function( index ) {
- 'use strict';
-
- return this.array[ this.indexOffset + index ];
- };
-
- /**
- * Returns the component value of the array at the given index, taking into account
- * the `indexOffset` property of this class.
- *
- * If the componentSize is set to 3, then it will return a new TypedArray
- * of length 3.
- *
- * @param {Number} index The index in the array to fetch.
- * @return {TypedArray} The component value at the given index.
- */
- SPE.TypedArrayHelper.prototype.getComponentValueAtIndex = function( index ) {
- 'use strict';
-
- return this.array.subarray( this.indexOffset + ( index * this.componentSize ) );
- };
-
- /**
- * A helper to handle creating and updating a THREE.BufferAttribute instance.
- *
- * @author Luke Moody
- * @constructor
- * @param {String} type The buffer attribute type. See SPE.ShaderAttribute.typeSizeMap for valid values.
- * @param {Boolean=} dynamicBuffer Whether this buffer attribute should be marked as dynamic or not.
- * @param {Function=} arrayType A reference to a TypedArray constructor. Defaults to Float32Array if none provided.
- */
- SPE.ShaderAttribute = function( type, dynamicBuffer, arrayType ) {
- 'use strict';
-
- var typeMap = SPE.ShaderAttribute.typeSizeMap;
-
- this.type = typeof type === 'string' && typeMap.hasOwnProperty( type ) ? type : 'f';
- this.componentSize = typeMap[ this.type ];
- this.arrayType = arrayType || Float32Array;
- this.typedArray = null;
- this.bufferAttribute = null;
- this.dynamicBuffer = !!dynamicBuffer;
-
- this.updateMin = 0;
- this.updateMax = 0;
- };
-
- SPE.ShaderAttribute.constructor = SPE.ShaderAttribute;
-
- /**
- * A map of uniform types to their component size.
- * @enum {Number}
- */
- SPE.ShaderAttribute.typeSizeMap = {
- /**
- * Float
- * @type {Number}
- */
- f: 1,
-
- /**
- * Vec2
- * @type {Number}
- */
- v2: 2,
-
- /**
- * Vec3
- * @type {Number}
- */
- v3: 3,
-
- /**
- * Vec4
- * @type {Number}
- */
- v4: 4,
-
- /**
- * Color
- * @type {Number}
- */
- c: 3,
-
- /**
- * Mat3
- * @type {Number}
- */
- m3: 9,
-
- /**
- * Mat4
- * @type {Number}
- */
- m4: 16
- };
-
- /**
- * Calculate the minimum and maximum update range for this buffer attribute using
- * component size independant min and max values.
- *
- * @param {Number} min The start of the range to mark as needing an update.
- * @param {Number} max The end of the range to mark as needing an update.
- */
- SPE.ShaderAttribute.prototype.setUpdateRange = function( min, max ) {
- 'use strict';
-
- this.updateMin = Math.min( min * this.componentSize, this.updateMin * this.componentSize );
- this.updateMax = Math.max( max * this.componentSize, this.updateMax * this.componentSize );
- };
-
- /**
- * Calculate the number of indices that this attribute should mark as needing
- * updating. Also marks the attribute as needing an update.
- */
- SPE.ShaderAttribute.prototype.flagUpdate = function() {
- 'use strict';
-
- var attr = this.bufferAttribute,
- range = attr.updateRange;
-
- range.offset = this.updateMin;
- range.count = Math.min( ( this.updateMax - this.updateMin ) + this.componentSize, this.typedArray.array.length );
- // console.log( range.offset, range.count, this.typedArray.array.length );
- // console.log( 'flagUpdate:', range.offset, range.count );
- attr.needsUpdate = true;
- };
-
-
-
- /**
- * Reset the index update counts for this attribute
- */
- SPE.ShaderAttribute.prototype.resetUpdateRange = function() {
- 'use strict';
-
- this.updateMin = 0;
- this.updateMax = 0;
- };
-
- SPE.ShaderAttribute.prototype.resetDynamic = function() {
- 'use strict';
- this.bufferAttribute.usage = this.dynamicBuffer ?
- THREE.DynamicDrawUsage :
- THREE.StaticDrawUsage;
- };
-
- /**
- * Perform a splice operation on this attribute's buffer.
- * @param {Number} start The start index of the splice. Will be multiplied by the number of components for this attribute.
- * @param {Number} end The end index of the splice. Will be multiplied by the number of components for this attribute.
- */
- SPE.ShaderAttribute.prototype.splice = function( start, end ) {
- 'use strict';
-
- this.typedArray.splice( start, end );
-
- // Reset the reference to the attribute's typed array
- // since it has probably changed.
- this.forceUpdateAll();
- };
-
- SPE.ShaderAttribute.prototype.forceUpdateAll = function() {
- 'use strict';
-
- this.bufferAttribute.array = this.typedArray.array;
- this.bufferAttribute.updateRange.offset = 0;
- this.bufferAttribute.updateRange.count = -1;
- // this.bufferAttribute.dynamic = false;
- // this.bufferAttribute.usage = this.dynamicBuffer ?
- // THREE.DynamicDrawUsage :
- // THREE.StaticDrawUsage;
-
- this.bufferAttribute.usage = THREE.StaticDrawUsage;
- this.bufferAttribute.needsUpdate = true;
- };
-
- /**
- * Make sure this attribute has a typed array associated with it.
- *
- * If it does, then it will ensure the typed array is of the correct size.
- *
- * If not, a new SPE.TypedArrayHelper instance will be created.
- *
- * @param {Number} size The size of the typed array to create or update to.
- */
- SPE.ShaderAttribute.prototype._ensureTypedArray = function( size ) {
- 'use strict';
-
- // Condition that's most likely to be true at the top: no change.
- if ( this.typedArray !== null && this.typedArray.size === size * this.componentSize ) {
- return;
- }
-
- // Resize the array if we need to, telling the TypedArrayHelper to
- // ignore it's component size when evaluating size.
- else if ( this.typedArray !== null && this.typedArray.size !== size ) {
- this.typedArray.setSize( size );
- }
-
- // This condition should only occur once in an attribute's lifecycle.
- else if ( this.typedArray === null ) {
- this.typedArray = new SPE.TypedArrayHelper( this.arrayType, size, this.componentSize );
- }
- };
-
-
- /**
- * Creates a THREE.BufferAttribute instance if one doesn't exist already.
- *
- * Ensures a typed array is present by calling _ensureTypedArray() first.
- *
- * If a buffer attribute exists already, then it will be marked as needing an update.
- *
- * @param {Number} size The size of the typed array to create if one doesn't exist, or resize existing array to.
- */
- SPE.ShaderAttribute.prototype._createBufferAttribute = function( size ) {
- 'use strict';
-
- // Make sure the typedArray is present and correct.
- this._ensureTypedArray( size );
-
- // Don't create it if it already exists, but do
- // flag that it needs updating on the next render
- // cycle.
- if ( this.bufferAttribute !== null ) {
- this.bufferAttribute.array = this.typedArray.array;
-
- // Since THREE.js version 81, dynamic count calculation was removed
- // so I need to do it manually here.
- //
- // In the next minor release, I may well remove this check and force
- // dependency on THREE r81+.
- if ( parseFloat( THREE.REVISION ) >= 81 ) {
- this.bufferAttribute.count = this.bufferAttribute.array.length / this.bufferAttribute.itemSize;
- }
-
- this.bufferAttribute.needsUpdate = true;
- return;
- }
-
- this.bufferAttribute = new THREE.BufferAttribute( this.typedArray.array, this.componentSize );
- // this.bufferAttribute.dynamic = this.dynamicBuffer;
- this.bufferAttribute.usage = this.dynamicBuffer ?
- THREE.DynamicDrawUsage :
- THREE.StaticDrawUsage;
- };
-
- /**
- * Returns the length of the typed array associated with this attribute.
- * @return {Number} The length of the typed array. Will be 0 if no typed array has been created yet.
- */
- SPE.ShaderAttribute.prototype.getLength = function() {
- 'use strict';
-
- if ( this.typedArray === null ) {
- return 0;
- }
-
- return this.typedArray.array.length;
- };
-
-
- SPE.shaderChunks = {
- // Register color-packing define statements.
- defines: [
- '#define PACKED_COLOR_SIZE 256.0',
- '#define PACKED_COLOR_DIVISOR 255.0'
- ].join( '\n' ),
-
- // All uniforms used by vertex / fragment shaders
- uniforms: [
- 'uniform float deltaTime;',
- 'uniform float runTime;',
- 'uniform sampler2D tex;',
- 'uniform vec4 textureAnimation;',
- 'uniform float scale;',
- ].join( '\n' ),
-
- // All attributes used by the vertex shader.
- //
- // Note that some attributes are squashed into other ones:
- //
- // * Drag is acceleration.w
- attributes: [
- 'attribute vec4 acceleration;',
- 'attribute vec3 velocity;',
- 'attribute vec4 rotation;',
- 'attribute vec3 rotationCenter;',
- 'attribute vec4 params;',
- 'attribute vec4 size;',
- 'attribute vec4 angle;',
- 'attribute vec4 color;',
- 'attribute vec4 opacity;'
- ].join( '\n' ),
-
- //
- varyings: [
- 'varying vec4 vColor;',
- '#ifdef SHOULD_ROTATE_TEXTURE',
- ' varying float vAngle;',
- '#endif',
-
- '#ifdef SHOULD_CALCULATE_SPRITE',
- ' varying vec4 vSpriteSheet;',
- '#endif'
- ].join( '\n' ),
-
-
- // Branch-avoiding comparison fns
- // - http://theorangeduck.com/page/avoiding-shader-conditionals
- branchAvoidanceFunctions: [
- 'float when_gt(float x, float y) {',
- ' return max(sign(x - y), 0.0);',
- '}',
-
- 'float when_lt(float x, float y) {',
- ' return min( max(1.0 - sign(x - y), 0.0), 1.0 );',
- '}',
-
- 'float when_eq( float x, float y ) {',
- ' return 1.0 - abs( sign( x - y ) );',
- '}',
-
- 'float when_ge(float x, float y) {',
- ' return 1.0 - when_lt(x, y);',
- '}',
-
- 'float when_le(float x, float y) {',
- ' return 1.0 - when_gt(x, y);',
- '}',
-
- // Branch-avoiding logical operators
- // (to be used with above comparison fns)
- 'float and(float a, float b) {',
- ' return a * b;',
- '}',
-
- 'float or(float a, float b) {',
- ' return min(a + b, 1.0);',
- '}',
- ].join( '\n' ),
-
-
- // From:
- // - http://stackoverflow.com/a/12553149
- // - https://stackoverflow.com/questions/22895237/hexadecimal-to-rgb-values-in-webgl-shader
- unpackColor: [
- 'vec3 unpackColor( in float hex ) {',
- ' vec3 c = vec3( 0.0 );',
-
- ' float r = mod( (hex / PACKED_COLOR_SIZE / PACKED_COLOR_SIZE), PACKED_COLOR_SIZE );',
- ' float g = mod( (hex / PACKED_COLOR_SIZE), PACKED_COLOR_SIZE );',
- ' float b = mod( hex, PACKED_COLOR_SIZE );',
-
- ' c.r = r / PACKED_COLOR_DIVISOR;',
- ' c.g = g / PACKED_COLOR_DIVISOR;',
- ' c.b = b / PACKED_COLOR_DIVISOR;',
-
- ' return c;',
- '}',
- ].join( '\n' ),
-
- unpackRotationAxis: [
- 'vec3 unpackRotationAxis( in float hex ) {',
- ' vec3 c = vec3( 0.0 );',
-
- ' float r = mod( (hex / PACKED_COLOR_SIZE / PACKED_COLOR_SIZE), PACKED_COLOR_SIZE );',
- ' float g = mod( (hex / PACKED_COLOR_SIZE), PACKED_COLOR_SIZE );',
- ' float b = mod( hex, PACKED_COLOR_SIZE );',
-
- ' c.r = r / PACKED_COLOR_DIVISOR;',
- ' c.g = g / PACKED_COLOR_DIVISOR;',
- ' c.b = b / PACKED_COLOR_DIVISOR;',
-
- ' c *= vec3( 2.0 );',
- ' c -= vec3( 1.0 );',
-
- ' return c;',
- '}',
- ].join( '\n' ),
-
- floatOverLifetime: [
- 'float getFloatOverLifetime( in float positionInTime, in vec4 attr ) {',
- ' highp float value = 0.0;',
- ' float deltaAge = positionInTime * float( VALUE_OVER_LIFETIME_LENGTH - 1 );',
- ' float fIndex = 0.0;',
- ' float shouldApplyValue = 0.0;',
-
- // This might look a little odd, but it's faster in the testing I've done than using branches.
- // Uses basic maths to avoid branching.
- //
- // Take a look at the branch-avoidance functions defined above,
- // and be sure to check out The Orange Duck site where I got this
- // from (link above).
-
- // Fix for static emitters (age is always zero).
- ' value += attr[ 0 ] * when_eq( deltaAge, 0.0 );',
- '',
- ' for( int i = 0; i < VALUE_OVER_LIFETIME_LENGTH - 1; ++i ) {',
- ' fIndex = float( i );',
- ' shouldApplyValue = and( when_gt( deltaAge, fIndex ), when_le( deltaAge, fIndex + 1.0 ) );',
- ' value += shouldApplyValue * mix( attr[ i ], attr[ i + 1 ], deltaAge - fIndex );',
- ' }',
- '',
- ' return value;',
- '}',
- ].join( '\n' ),
-
- colorOverLifetime: [
- 'vec3 getColorOverLifetime( in float positionInTime, in vec3 color1, in vec3 color2, in vec3 color3, in vec3 color4 ) {',
- ' vec3 value = vec3( 0.0 );',
- ' value.x = getFloatOverLifetime( positionInTime, vec4( color1.x, color2.x, color3.x, color4.x ) );',
- ' value.y = getFloatOverLifetime( positionInTime, vec4( color1.y, color2.y, color3.y, color4.y ) );',
- ' value.z = getFloatOverLifetime( positionInTime, vec4( color1.z, color2.z, color3.z, color4.z ) );',
- ' return value;',
- '}',
- ].join( '\n' ),
-
- paramFetchingFunctions: [
- 'float getAlive() {',
- ' return params.x;',
- '}',
-
- 'float getAge() {',
- ' return params.y;',
- '}',
-
- 'float getMaxAge() {',
- ' return params.z;',
- '}',
-
- 'float getWiggle() {',
- ' return params.w;',
- '}',
- ].join( '\n' ),
-
- forceFetchingFunctions: [
- 'vec4 getPosition( in float age ) {',
- ' return modelViewMatrix * vec4( position, 1.0 );',
- '}',
-
- 'vec3 getVelocity( in float age ) {',
- ' return velocity * age;',
- '}',
-
- 'vec3 getAcceleration( in float age ) {',
- ' return acceleration.xyz * age;',
- '}',
- ].join( '\n' ),
-
-
- rotationFunctions: [
- // Huge thanks to:
- // - http://www.neilmendoza.com/glsl-rotation-about-an-arbitrary-axis/
- '#ifdef SHOULD_ROTATE_PARTICLES',
- ' mat4 getRotationMatrix( in vec3 axis, in float angle) {',
- ' axis = normalize(axis);',
- ' float s = sin(angle);',
- ' float c = cos(angle);',
- ' float oc = 1.0 - c;',
- '',
- ' return mat4(oc * axis.x * axis.x + c, oc * axis.x * axis.y - axis.z * s, oc * axis.z * axis.x + axis.y * s, 0.0,',
- ' oc * axis.x * axis.y + axis.z * s, oc * axis.y * axis.y + c, oc * axis.y * axis.z - axis.x * s, 0.0,',
- ' oc * axis.z * axis.x - axis.y * s, oc * axis.y * axis.z + axis.x * s, oc * axis.z * axis.z + c, 0.0,',
- ' 0.0, 0.0, 0.0, 1.0);',
- ' }',
- '',
- ' vec3 getRotation( in vec3 pos, in float positionInTime ) {',
- ' if( rotation.y == 0.0 ) {',
- ' return pos;',
- ' }',
- '',
- ' vec3 axis = unpackRotationAxis( rotation.x );',
- ' vec3 center = rotationCenter;',
- ' vec3 translated;',
- ' mat4 rotationMatrix;',
-
- ' float angle = 0.0;',
- ' angle += when_eq( rotation.z, 0.0 ) * rotation.y;',
- ' angle += when_gt( rotation.z, 0.0 ) * mix( 0.0, rotation.y, positionInTime );',
- ' translated = rotationCenter - pos;',
- ' rotationMatrix = getRotationMatrix( axis, angle );',
- ' return center - vec3( rotationMatrix * vec4( translated, 0.0 ) );',
- ' }',
- '#endif'
- ].join( '\n' ),
-
-
- // Fragment chunks
- rotateTexture: [
- ' vec2 vUv = vec2( gl_PointCoord.x, 1.0 - gl_PointCoord.y );',
- '',
- ' #ifdef SHOULD_ROTATE_TEXTURE',
- ' float x = gl_PointCoord.x - 0.5;',
- ' float y = 1.0 - gl_PointCoord.y - 0.5;',
- ' float c = cos( -vAngle );',
- ' float s = sin( -vAngle );',
-
- ' vUv = vec2( c * x + s * y + 0.5, c * y - s * x + 0.5 );',
- ' #endif',
- '',
-
- // Spritesheets overwrite angle calculations.
- ' #ifdef SHOULD_CALCULATE_SPRITE',
- ' float framesX = vSpriteSheet.x;',
- ' float framesY = vSpriteSheet.y;',
- ' float columnNorm = vSpriteSheet.z;',
- ' float rowNorm = vSpriteSheet.w;',
-
- ' vUv.x = gl_PointCoord.x * framesX + columnNorm;',
- ' vUv.y = 1.0 - (gl_PointCoord.y * framesY + rowNorm);',
- ' #endif',
-
- '',
- ' vec4 rotatedTexture = texture2D( tex, vUv );',
- ].join( '\n' )
- };
-
- SPE.shaders = {
- vertex: [
- SPE.shaderChunks.defines,
- SPE.shaderChunks.uniforms,
- SPE.shaderChunks.attributes,
- SPE.shaderChunks.varyings,
-
- THREE.ShaderChunk.common,
- THREE.ShaderChunk.logdepthbuf_pars_vertex,
- THREE.ShaderChunk.fog_pars_vertex,
-
- SPE.shaderChunks.branchAvoidanceFunctions,
- SPE.shaderChunks.unpackColor,
- SPE.shaderChunks.unpackRotationAxis,
- SPE.shaderChunks.floatOverLifetime,
- SPE.shaderChunks.colorOverLifetime,
- SPE.shaderChunks.paramFetchingFunctions,
- SPE.shaderChunks.forceFetchingFunctions,
- SPE.shaderChunks.rotationFunctions,
-
-
- 'void main() {',
-
-
- //
- // Setup...
- //
- ' highp float age = getAge();',
- ' highp float alive = getAlive();',
- ' highp float maxAge = getMaxAge();',
- ' highp float positionInTime = (age / maxAge);',
- ' highp float isAlive = when_gt( alive, 0.0 );',
-
- ' #ifdef SHOULD_WIGGLE_PARTICLES',
- ' float wiggleAmount = positionInTime * getWiggle();',
- ' float wiggleSin = isAlive * sin( wiggleAmount );',
- ' float wiggleCos = isAlive * cos( wiggleAmount );',
- ' #endif',
-
- //
- // Forces
- //
-
- // Get forces & position
- ' vec3 vel = getVelocity( age );',
- ' vec3 accel = getAcceleration( age );',
- ' vec3 force = vec3( 0.0 );',
- ' vec3 pos = vec3( position );',
-
- // Calculate the required drag to apply to the forces.
- ' float drag = 1.0 - (positionInTime * 0.5) * acceleration.w;',
-
- // Integrate forces...
- ' force += vel;',
- ' force *= drag;',
- ' force += accel * age;',
- ' pos += force;',
-
-
- // Wiggly wiggly wiggle!
- ' #ifdef SHOULD_WIGGLE_PARTICLES',
- ' pos.x += wiggleSin;',
- ' pos.y += wiggleCos;',
- ' pos.z += wiggleSin;',
- ' #endif',
-
-
- // Rotate the emitter around it's central point
- ' #ifdef SHOULD_ROTATE_PARTICLES',
- ' pos = getRotation( pos, positionInTime );',
- ' #endif',
-
- // Convert pos to a world-space value
- ' vec4 mvPosition = modelViewMatrix * vec4( pos, 1.0 );',
-
- // Determine point size.
- ' highp float pointSize = getFloatOverLifetime( positionInTime, size ) * isAlive;',
-
- // Determine perspective
- ' #ifdef HAS_PERSPECTIVE',
- ' float perspective = scale / length( mvPosition.xyz );',
- ' #else',
- ' float perspective = 1.0;',
- ' #endif',
-
- // Apply perpective to pointSize value
- ' float pointSizePerspective = pointSize * perspective;',
-
-
- //
- // Appearance
- //
-
- // Determine color and opacity for this particle
- ' #ifdef COLORIZE',
- ' vec3 c = isAlive * getColorOverLifetime(',
- ' positionInTime,',
- ' unpackColor( color.x ),',
- ' unpackColor( color.y ),',
- ' unpackColor( color.z ),',
- ' unpackColor( color.w )',
- ' );',
- ' #else',
- ' vec3 c = vec3(1.0);',
- ' #endif',
-
- ' float o = isAlive * getFloatOverLifetime( positionInTime, opacity );',
-
- // Assign color to vColor varying.
- ' vColor = vec4( c, o );',
-
- // Determine angle
- ' #ifdef SHOULD_ROTATE_TEXTURE',
- ' vAngle = isAlive * getFloatOverLifetime( positionInTime, angle );',
- ' #endif',
-
- // If this particle is using a sprite-sheet as a texture, we'll have to figure out
- // what frame of the texture the particle is using at it's current position in time.
- ' #ifdef SHOULD_CALCULATE_SPRITE',
- ' float framesX = textureAnimation.x;',
- ' float framesY = textureAnimation.y;',
- ' float loopCount = textureAnimation.w;',
- ' float totalFrames = textureAnimation.z;',
- ' float frameNumber = mod( (positionInTime * loopCount) * totalFrames, totalFrames );',
-
- ' float column = floor(mod( frameNumber, framesX ));',
- ' float row = floor( (frameNumber - column) / framesX );',
-
- ' float columnNorm = column / framesX;',
- ' float rowNorm = row / framesY;',
-
- ' vSpriteSheet.x = 1.0 / framesX;',
- ' vSpriteSheet.y = 1.0 / framesY;',
- ' vSpriteSheet.z = columnNorm;',
- ' vSpriteSheet.w = rowNorm;',
- ' #endif',
-
- //
- // Write values
- //
-
- // Set PointSize according to size at current point in time.
- ' gl_PointSize = pointSizePerspective;',
- ' gl_Position = projectionMatrix * mvPosition;',
-
- THREE.ShaderChunk.logdepthbuf_vertex,
- THREE.ShaderChunk.fog_vertex,
-
- '}'
- ].join( '\n' ),
-
- fragment: [
- SPE.shaderChunks.uniforms,
-
- THREE.ShaderChunk.common,
- THREE.ShaderChunk.fog_pars_fragment,
- THREE.ShaderChunk.logdepthbuf_pars_fragment,
-
- SPE.shaderChunks.varyings,
-
- SPE.shaderChunks.branchAvoidanceFunctions,
-
- 'void main() {',
- ' vec3 outgoingLight = vColor.xyz;',
- ' ',
- ' #ifdef ALPHATEST',
- ' if ( vColor.w < float(ALPHATEST) ) discard;',
- ' #endif',
-
- SPE.shaderChunks.rotateTexture,
-
- THREE.ShaderChunk.logdepthbuf_fragment,
-
- ' outgoingLight = vColor.xyz * rotatedTexture.xyz;',
- ' gl_FragColor = vec4( outgoingLight.xyz, rotatedTexture.w * vColor.w );',
-
- THREE.ShaderChunk.fog_fragment,
-
- '}'
- ].join( '\n' )
- };
-
-
- /**
- * A bunch of utility functions used throughout the library.
- * @namespace
- * @type {Object}
- */
- SPE.utils = {
- /**
- * A map of types used by `SPE.utils.ensureTypedArg` and
- * `SPE.utils.ensureArrayTypedArg` to compare types against.
- *
- * @enum {String}
- */
- types: {
- /**
- * Boolean type.
- * @type {String}
- */
- BOOLEAN: 'boolean',
-
- /**
- * String type.
- * @type {String}
- */
- STRING: 'string',
-
- /**
- * Number type.
- * @type {String}
- */
- NUMBER: 'number',
-
- /**
- * Object type.
- * @type {String}
- */
- OBJECT: 'object'
- },
-
- /**
- * Given a value, a type, and a default value to fallback to,
- * ensure the given argument adheres to the type requesting,
- * returning the default value if type check is false.
- *
- * @param {(boolean|string|number|object)} arg The value to perform a type-check on.
- * @param {String} type The type the `arg` argument should adhere to.
- * @param {(boolean|string|number|object)} defaultValue A default value to fallback on if the type check fails.
- * @return {(boolean|string|number|object)} The given value if type check passes, or the default value if it fails.
- */
- ensureTypedArg: function( arg, type, defaultValue ) {
- 'use strict';
-
- if ( typeof arg === type ) {
- return arg;
- }
- else {
- return defaultValue;
- }
- },
-
- /**
- * Given an array of values, a type, and a default value,
- * ensure the given array's contents ALL adhere to the provided type,
- * returning the default value if type check fails.
- *
- * If the given value to check isn't an Array, delegates to SPE.utils.ensureTypedArg.
- *
- * @param {Array|boolean|string|number|object} arg The array of values to check type of.
- * @param {String} type The type that should be adhered to.
- * @param {(boolean|string|number|object)} defaultValue A default fallback value.
- * @return {(boolean|string|number|object)} The given value if type check passes, or the default value if it fails.
- */
- ensureArrayTypedArg: function( arg, type, defaultValue ) {
- 'use strict';
-
- // If the argument being checked is an array, loop through
- // it and ensure all the values are of the correct type,
- // falling back to the defaultValue if any aren't.
- if ( Array.isArray( arg ) ) {
- for ( var i = arg.length - 1; i >= 0; --i ) {
- if ( typeof arg[ i ] !== type ) {
- return defaultValue;
- }
- }
-
- return arg;
- }
-
- // If the arg isn't an array then just fallback to
- // checking the type.
- return this.ensureTypedArg( arg, type, defaultValue );
- },
-
- /**
- * Ensures the given value is an instance of a constructor function.
- *
- * @param {Object} arg The value to check instance of.
- * @param {Function} instance The constructor of the instance to check against.
- * @param {Object} defaultValue A default fallback value if instance check fails
- * @return {Object} The given value if type check passes, or the default value if it fails.
- */
- ensureInstanceOf: function( arg, instance, defaultValue ) {
- 'use strict';
-
- if ( instance !== undefined && arg instanceof instance ) {
- return arg;
- }
- else {
- return defaultValue;
- }
- },
-
- /**
- * Given an array of values, ensure the instances of all items in the array
- * matches the given instance constructor falling back to a default value if
- * the check fails.
- *
- * If given value isn't an Array, delegates to `SPE.utils.ensureInstanceOf`.
- *
- * @param {Array|Object} arg The value to perform the instanceof check on.
- * @param {Function} instance The constructor of the instance to check against.
- * @param {Object} defaultValue A default fallback value if instance check fails
- * @return {Object} The given value if type check passes, or the default value if it fails.
- */
- ensureArrayInstanceOf: function( arg, instance, defaultValue ) {
- 'use strict';
-
- // If the argument being checked is an array, loop through
- // it and ensure all the values are of the correct type,
- // falling back to the defaultValue if any aren't.
- if ( Array.isArray( arg ) ) {
- for ( var i = arg.length - 1; i >= 0; --i ) {
- if ( instance !== undefined && arg[ i ] instanceof instance === false ) {
- return defaultValue;
- }
- }
-
- return arg;
- }
-
- // If the arg isn't an array then just fallback to
- // checking the type.
- return this.ensureInstanceOf( arg, instance, defaultValue );
- },
-
- /**
- * Ensures that any "value-over-lifetime" properties of an emitter are
- * of the correct length (as dictated by `SPE.valueOverLifetimeLength`).
- *
- * Delegates to `SPE.utils.interpolateArray` for array resizing.
- *
- * If properties aren't arrays, then property values are put into one.
- *
- * @param {Object} property The property of an SPE.Emitter instance to check compliance of.
- * @param {Number} minLength The minimum length of the array to create.
- * @param {Number} maxLength The maximum length of the array to create.
- */
- ensureValueOverLifetimeCompliance: function( property, minLength, maxLength ) {
- 'use strict';
-
- minLength = minLength || 3;
- maxLength = maxLength || 3;
-
- // First, ensure both properties are arrays.
- if ( Array.isArray( property._value ) === false ) {
- property._value = [ property._value ];
- }
-
- if ( Array.isArray( property._spread ) === false ) {
- property._spread = [ property._spread ];
- }
-
- var valueLength = this.clamp( property._value.length, minLength, maxLength ),
- spreadLength = this.clamp( property._spread.length, minLength, maxLength ),
- desiredLength = Math.max( valueLength, spreadLength );
-
- if ( property._value.length !== desiredLength ) {
- property._value = this.interpolateArray( property._value, desiredLength );
- }
-
- if ( property._spread.length !== desiredLength ) {
- property._spread = this.interpolateArray( property._spread, desiredLength );
- }
- },
-
- /**
- * Performs linear interpolation (lerp) on an array.
- *
- * For example, lerping [1, 10], with a `newLength` of 10 will produce [1, 2, 3, 4, 5, 6, 7, 8, 9, 10].
- *
- * Delegates to `SPE.utils.lerpTypeAgnostic` to perform the actual
- * interpolation.
- *
- * @param {Array} srcArray The array to lerp.
- * @param {Number} newLength The length the array should be interpolated to.
- * @return {Array} The interpolated array.
- */
- interpolateArray: function( srcArray, newLength ) {
- 'use strict';
-
- var sourceLength = srcArray.length,
- newArray = [ typeof srcArray[ 0 ].clone === 'function' ? srcArray[ 0 ].clone() : srcArray[ 0 ] ],
- factor = ( sourceLength - 1 ) / ( newLength - 1 );
-
-
- for ( var i = 1; i < newLength - 1; ++i ) {
- var f = i * factor,
- before = Math.floor( f ),
- after = Math.ceil( f ),
- delta = f - before;
-
- newArray[ i ] = this.lerpTypeAgnostic( srcArray[ before ], srcArray[ after ], delta );
- }
-
- newArray.push(
- typeof srcArray[ sourceLength - 1 ].clone === 'function' ?
- srcArray[ sourceLength - 1 ].clone() :
- srcArray[ sourceLength - 1 ]
- );
-
- return newArray;
- },
-
- /**
- * Clamp a number to between the given min and max values.
- * @param {Number} value The number to clamp.
- * @param {Number} min The minimum value.
- * @param {Number} max The maximum value.
- * @return {Number} The clamped number.
- */
- clamp: function( value, min, max ) {
- 'use strict';
-
- return Math.max( min, Math.min( value, max ) );
- },
-
- /**
- * If the given value is less than the epsilon value, then return
- * a randomised epsilon value if specified, or just the epsilon value if not.
- * Works for negative numbers as well as positive.
- *
- * @param {Number} value The value to perform the operation on.
- * @param {Boolean} randomise Whether the value should be randomised.
- * @return {Number} The result of the operation.
- */
- zeroToEpsilon: function( value, randomise ) {
- 'use strict';
-
- var epsilon = 0.00001,
- result = value;
-
- result = randomise ? Math.random() * epsilon * 10 : epsilon;
-
- if ( value < 0 && value > -epsilon ) {
- result = -result;
- }
-
- // if ( value === 0 ) {
- // result = randomise ? Math.random() * epsilon * 10 : epsilon;
- // }
- // else if ( value > 0 && value < epsilon ) {
- // result = randomise ? Math.random() * epsilon * 10 : epsilon;
- // }
- // else if ( value < 0 && value > -epsilon ) {
- // result = -( randomise ? Math.random() * epsilon * 10 : epsilon );
- // }
-
- return result;
- },
-
- /**
- * Linearly interpolates two values of various types. The given values
- * must be of the same type for the interpolation to work.
- * @param {(number|Object)} start The start value of the lerp.
- * @param {(number|object)} end The end value of the lerp.
- * @param {Number} delta The delta posiiton of the lerp operation. Ideally between 0 and 1 (inclusive).
- * @return {(number|object|undefined)} The result of the operation. Result will be undefined if
- * the start and end arguments aren't a supported type, or
- * if their types do not match.
- */
- lerpTypeAgnostic: function( start, end, delta ) {
- 'use strict';
-
- var types = this.types,
- out;
-
- if ( typeof start === types.NUMBER && typeof end === types.NUMBER ) {
- return start + ( ( end - start ) * delta );
- }
- else if ( start instanceof THREE.Vector2 && end instanceof THREE.Vector2 ) {
- out = start.clone();
- out.x = this.lerp( start.x, end.x, delta );
- out.y = this.lerp( start.y, end.y, delta );
- return out;
- }
- else if ( start instanceof THREE.Vector3 && end instanceof THREE.Vector3 ) {
- out = start.clone();
- out.x = this.lerp( start.x, end.x, delta );
- out.y = this.lerp( start.y, end.y, delta );
- out.z = this.lerp( start.z, end.z, delta );
- return out;
- }
- else if ( start instanceof THREE.Vector4 && end instanceof THREE.Vector4 ) {
- out = start.clone();
- out.x = this.lerp( start.x, end.x, delta );
- out.y = this.lerp( start.y, end.y, delta );
- out.z = this.lerp( start.z, end.z, delta );
- out.w = this.lerp( start.w, end.w, delta );
- return out;
- }
- else if ( start instanceof THREE.Color && end instanceof THREE.Color ) {
- out = start.clone();
- out.r = this.lerp( start.r, end.r, delta );
- out.g = this.lerp( start.g, end.g, delta );
- out.b = this.lerp( start.b, end.b, delta );
- return out;
- }
- else {
- console.warn( 'Invalid argument types, or argument types do not match:', start, end );
- }
- },
-
- /**
- * Perform a linear interpolation operation on two numbers.
- * @param {Number} start The start value.
- * @param {Number} end The end value.
- * @param {Number} delta The position to interpolate to.
- * @return {Number} The result of the lerp operation.
- */
- lerp: function( start, end, delta ) {
- 'use strict';
- return start + ( ( end - start ) * delta );
- },
-
- /**
- * Rounds a number to a nearest multiple.
- *
- * @param {Number} n The number to round.
- * @param {Number} multiple The multiple to round to.
- * @return {Number} The result of the round operation.
- */
- roundToNearestMultiple: function( n, multiple ) {
- 'use strict';
-
- var remainder = 0;
-
- if ( multiple === 0 ) {
- return n;
- }
-
- remainder = Math.abs( n ) % multiple;
-
- if ( remainder === 0 ) {
- return n;
- }
-
- if ( n < 0 ) {
- return -( Math.abs( n ) - remainder );
- }
-
- return n + multiple - remainder;
- },
-
- /**
- * Check if all items in an array are equal. Uses strict equality.
- *
- * @param {Array} array The array of values to check equality of.
- * @return {Boolean} Whether the array's values are all equal or not.
- */
- arrayValuesAreEqual: function( array ) {
- 'use strict';
-
- for ( var i = 0; i < array.length - 1; ++i ) {
- if ( array[ i ] !== array[ i + 1 ] ) {
- return false;
- }
- }
-
- return true;
- },
-
- // colorsAreEqual: function() {
- // var colors = Array.prototype.slice.call( arguments ),
- // numColors = colors.length;
-
- // for ( var i = 0, color1, color2; i < numColors - 1; ++i ) {
- // color1 = colors[ i ];
- // color2 = colors[ i + 1 ];
-
- // if (
- // color1.r !== color2.r ||
- // color1.g !== color2.g ||
- // color1.b !== color2.b
- // ) {
- // return false
- // }
- // }
-
- // return true;
- // },
-
-
- /**
- * Given a start value and a spread value, create and return a random
- * number.
- * @param {Number} base The start value.
- * @param {Number} spread The size of the random variance to apply.
- * @return {Number} A randomised number.
- */
- randomFloat: function( base, spread ) {
- 'use strict';
- return base + spread * ( Math.random() - 0.5 );
- },
-
-
-
- /**
- * Given an SPE.ShaderAttribute instance, and various other settings,
- * assign values to the attribute's array in a `vec3` format.
- *
- * @param {Object} attribute The instance of SPE.ShaderAttribute to save the result to.
- * @param {Number} index The offset in the attribute's TypedArray to save the result from.
- * @param {Object} base THREE.Vector3 instance describing the start value.
- * @param {Object} spread THREE.Vector3 instance describing the random variance to apply to the start value.
- * @param {Object} spreadClamp THREE.Vector3 instance describing the multiples to clamp the randomness to.
- */
- randomVector3: function( attribute, index, base, spread, spreadClamp ) {
- 'use strict';
-
- var x = base.x + ( Math.random() * spread.x - ( spread.x * 0.5 ) ),
- y = base.y + ( Math.random() * spread.y - ( spread.y * 0.5 ) ),
- z = base.z + ( Math.random() * spread.z - ( spread.z * 0.5 ) );
-
- // var x = this.randomFloat( base.x, spread.x ),
- // y = this.randomFloat( base.y, spread.y ),
- // z = this.randomFloat( base.z, spread.z );
-
- if ( spreadClamp ) {
- x = -spreadClamp.x * 0.5 + this.roundToNearestMultiple( x, spreadClamp.x );
- y = -spreadClamp.y * 0.5 + this.roundToNearestMultiple( y, spreadClamp.y );
- z = -spreadClamp.z * 0.5 + this.roundToNearestMultiple( z, spreadClamp.z );
- }
-
- attribute.typedArray.setVec3Components( index, x, y, z );
- },
-
- /**
- * Given an SPE.Shader attribute instance, and various other settings,
- * assign Color values to the attribute.
- * @param {Object} attribute The instance of SPE.ShaderAttribute to save the result to.
- * @param {Number} index The offset in the attribute's TypedArray to save the result from.
- * @param {Object} base THREE.Color instance describing the start color.
- * @param {Object} spread THREE.Vector3 instance describing the random variance to apply to the start color.
- */
- randomColor: function( attribute, index, base, spread ) {
- 'use strict';
-
- var r = base.r + ( Math.random() * spread.x ),
- g = base.g + ( Math.random() * spread.y ),
- b = base.b + ( Math.random() * spread.z );
-
- r = this.clamp( r, 0, 1 );
- g = this.clamp( g, 0, 1 );
- b = this.clamp( b, 0, 1 );
-
-
- attribute.typedArray.setVec3Components( index, r, g, b );
- },
-
-
- randomColorAsHex: ( function() {
- 'use strict';
-
- var workingColor = new THREE.Color();
-
- /**
- * Assigns a random color value, encoded as a hex value in decimal
- * format, to a SPE.ShaderAttribute instance.
- * @param {Object} attribute The instance of SPE.ShaderAttribute to save the result to.
- * @param {Number} index The offset in the attribute's TypedArray to save the result from.
- * @param {Object} base THREE.Color instance describing the start color.
- * @param {Object} spread THREE.Vector3 instance describing the random variance to apply to the start color.
- */
- return function( attribute, index, base, spread ) {
- var numItems = base.length,
- colors = [];
-
- for ( var i = 0; i < numItems; ++i ) {
- var spreadVector = spread[ i ];
-
- workingColor.copy( base[ i ] );
-
- workingColor.r += ( Math.random() * spreadVector.x ) - ( spreadVector.x * 0.5 );
- workingColor.g += ( Math.random() * spreadVector.y ) - ( spreadVector.y * 0.5 );
- workingColor.b += ( Math.random() * spreadVector.z ) - ( spreadVector.z * 0.5 );
-
- workingColor.r = this.clamp( workingColor.r, 0, 1 );
- workingColor.g = this.clamp( workingColor.g, 0, 1 );
- workingColor.b = this.clamp( workingColor.b, 0, 1 );
-
- colors.push( workingColor.getHex() );
- }
-
- attribute.typedArray.setVec4Components( index, colors[ 0 ], colors[ 1 ], colors[ 2 ], colors[ 3 ] );
- };
- }() ),
-
- /**
- * Given an SPE.ShaderAttribute instance, and various other settings,
- * assign values to the attribute's array in a `vec3` format.
- *
- * @param {Object} attribute The instance of SPE.ShaderAttribute to save the result to.
- * @param {Number} index The offset in the attribute's TypedArray to save the result from.
- * @param {Object} start THREE.Vector3 instance describing the start line position.
- * @param {Object} end THREE.Vector3 instance describing the end line position.
- */
- randomVector3OnLine: function( attribute, index, start, end ) {
- 'use strict';
- var pos = start.clone();
-
- pos.lerp( end, Math.random() );
-
- attribute.typedArray.setVec3Components( index, pos.x, pos.y, pos.z );
- },
-
- /**
- * Given an SPE.Shader attribute instance, and various other settings,
- * assign Color values to the attribute.
- * @param {Object} attribute The instance of SPE.ShaderAttribute to save the result to.
- * @param {Number} index The offset in the attribute's TypedArray to save the result from.
- * @param {Object} base THREE.Color instance describing the start color.
- * @param {Object} spread THREE.Vector3 instance describing the random variance to apply to the start color.
- */
-
- /**
- * Assigns a random vector 3 value to an SPE.ShaderAttribute instance, projecting the
- * given values onto a sphere.
- *
- * @param {Object} attribute The instance of SPE.ShaderAttribute to save the result to.
- * @param {Number} index The offset in the attribute's TypedArray to save the result from.
- * @param {Object} base THREE.Vector3 instance describing the origin of the transform.
- * @param {Number} radius The radius of the sphere to project onto.
- * @param {Number} radiusSpread The amount of randomness to apply to the projection result
- * @param {Object} radiusScale THREE.Vector3 instance describing the scale of each axis of the sphere.
- * @param {Number} radiusSpreadClamp What numeric multiple the projected value should be clamped to.
- */
- randomVector3OnSphere: function(
- attribute, index, base, radius, radiusSpread, radiusScale, radiusSpreadClamp, distributionClamp
- ) {
- 'use strict';
-
- var depth = 2 * Math.random() - 1,
- t = 6.2832 * Math.random(),
- r = Math.sqrt( 1 - depth * depth ),
- rand = this.randomFloat( radius, radiusSpread ),
- x = 0,
- y = 0,
- z = 0;
-
-
- if ( radiusSpreadClamp ) {
- rand = Math.round( rand / radiusSpreadClamp ) * radiusSpreadClamp;
- }
-
-
-
- // Set position on sphere
- x = r * Math.cos( t ) * rand;
- y = r * Math.sin( t ) * rand;
- z = depth * rand;
-
- // Apply radius scale to this position
- x *= radiusScale.x;
- y *= radiusScale.y;
- z *= radiusScale.z;
-
- // Translate to the base position.
- x += base.x;
- y += base.y;
- z += base.z;
-
- // Set the values in the typed array.
- attribute.typedArray.setVec3Components( index, x, y, z );
- },
-
- seededRandom: function( seed ) {
- var x = Math.sin( seed ) * 10000;
- return x - ( x | 0 );
- },
-
-
-
- /**
- * Assigns a random vector 3 value to an SPE.ShaderAttribute instance, projecting the
- * given values onto a 2d-disc.
- *
- * @param {Object} attribute The instance of SPE.ShaderAttribute to save the result to.
- * @param {Number} index The offset in the attribute's TypedArray to save the result from.
- * @param {Object} base THREE.Vector3 instance describing the origin of the transform.
- * @param {Number} radius The radius of the sphere to project onto.
- * @param {Number} radiusSpread The amount of randomness to apply to the projection result
- * @param {Object} radiusScale THREE.Vector3 instance describing the scale of each axis of the disc. The z-component is ignored.
- * @param {Number} radiusSpreadClamp What numeric multiple the projected value should be clamped to.
- */
- randomVector3OnDisc: function( attribute, index, base, radius, radiusSpread, radiusScale, radiusSpreadClamp ) {
- 'use strict';
-
- var t = 6.2832 * Math.random(),
- rand = Math.abs( this.randomFloat( radius, radiusSpread ) ),
- x = 0,
- y = 0,
- z = 0;
-
- if ( radiusSpreadClamp ) {
- rand = Math.round( rand / radiusSpreadClamp ) * radiusSpreadClamp;
- }
-
- // Set position on sphere
- x = Math.cos( t ) * rand;
- y = Math.sin( t ) * rand;
-
- // Apply radius scale to this position
- x *= radiusScale.x;
- y *= radiusScale.y;
-
- // Translate to the base position.
- x += base.x;
- y += base.y;
- z += base.z;
-
- // Set the values in the typed array.
- attribute.typedArray.setVec3Components( index, x, y, z );
- },
-
- randomDirectionVector3OnSphere: ( function() {
- 'use strict';
-
- var v = new THREE.Vector3();
-
- /**
- * Given an SPE.ShaderAttribute instance, create a direction vector from the given
- * position, using `speed` as the magnitude. Values are saved to the attribute.
- *
- * @param {Object} attribute The instance of SPE.ShaderAttribute to save the result to.
- * @param {Number} index The offset in the attribute's TypedArray to save the result from.
- * @param {Number} posX The particle's x coordinate.
- * @param {Number} posY The particle's y coordinate.
- * @param {Number} posZ The particle's z coordinate.
- * @param {Object} emitterPosition THREE.Vector3 instance describing the emitter's base position.
- * @param {Number} speed The magnitude to apply to the vector.
- * @param {Number} speedSpread The amount of randomness to apply to the magnitude.
- */
- return function( attribute, index, posX, posY, posZ, emitterPosition, speed, speedSpread ) {
- v.copy( emitterPosition );
-
- v.x -= posX;
- v.y -= posY;
- v.z -= posZ;
-
- v.normalize().multiplyScalar( -this.randomFloat( speed, speedSpread ) );
-
- attribute.typedArray.setVec3Components( index, v.x, v.y, v.z );
- };
- }() ),
-
-
- randomDirectionVector3OnDisc: ( function() {
- 'use strict';
-
- var v = new THREE.Vector3();
-
- /**
- * Given an SPE.ShaderAttribute instance, create a direction vector from the given
- * position, using `speed` as the magnitude. Values are saved to the attribute.
- *
- * @param {Object} attribute The instance of SPE.ShaderAttribute to save the result to.
- * @param {Number} index The offset in the attribute's TypedArray to save the result from.
- * @param {Number} posX The particle's x coordinate.
- * @param {Number} posY The particle's y coordinate.
- * @param {Number} posZ The particle's z coordinate.
- * @param {Object} emitterPosition THREE.Vector3 instance describing the emitter's base position.
- * @param {Number} speed The magnitude to apply to the vector.
- * @param {Number} speedSpread The amount of randomness to apply to the magnitude.
- */
- return function( attribute, index, posX, posY, posZ, emitterPosition, speed, speedSpread ) {
- v.copy( emitterPosition );
-
- v.x -= posX;
- v.y -= posY;
- v.z -= posZ;
-
- v.normalize().multiplyScalar( -this.randomFloat( speed, speedSpread ) );
-
- attribute.typedArray.setVec3Components( index, v.x, v.y, 0 );
- };
- }() ),
-
- getPackedRotationAxis: ( function() {
- 'use strict';
-
- var v = new THREE.Vector3(),
- vSpread = new THREE.Vector3(),
- c = new THREE.Color(),
- addOne = new THREE.Vector3( 1, 1, 1 );
-
- /**
- * Given a rotation axis, and a rotation axis spread vector,
- * calculate a randomised rotation axis, and pack it into
- * a hexadecimal value represented in decimal form.
- * @param {Object} axis THREE.Vector3 instance describing the rotation axis.
- * @param {Object} axisSpread THREE.Vector3 instance describing the amount of randomness to apply to the rotation axis.
- * @return {Number} The packed rotation axis, with randomness.
- */
- return function( axis, axisSpread ) {
- v.copy( axis ).normalize();
- vSpread.copy( axisSpread ).normalize();
-
- v.x += ( -axisSpread.x * 0.5 ) + ( Math.random() * axisSpread.x );
- v.y += ( -axisSpread.y * 0.5 ) + ( Math.random() * axisSpread.y );
- v.z += ( -axisSpread.z * 0.5 ) + ( Math.random() * axisSpread.z );
-
- // v.x = Math.abs( v.x );
- // v.y = Math.abs( v.y );
- // v.z = Math.abs( v.z );
-
- v.normalize().add( addOne ).multiplyScalar( 0.5 );
-
- c.setRGB( v.x, v.y, v.z );
-
- return c.getHex();
- };
- }() )
- };
-
-
- /**
- * An SPE.Group instance.
- * @typedef {Object} Group
- * @see SPE.Group
- */
-
- /**
- * A map of options to configure an SPE.Group instance.
- * @typedef {Object} GroupOptions
- *
- * @property {Object} texture An object describing the texture used by the group.
- *
- * @property {Object} texture.value An instance of THREE.Texture.
- *
- * @property {Object=} texture.frames A THREE.Vector2 instance describing the number
- * of frames on the x- and y-axis of the given texture.
- * If not provided, the texture will NOT be treated as
- * a sprite-sheet and as such will NOT be animated.
- *
- * @property {Number} [texture.frameCount=texture.frames.x * texture.frames.y] The total number of frames in the sprite-sheet.
- * Allows for sprite-sheets that don't fill the entire
- * texture.
- *
- * @property {Number} texture.loop The number of loops through the sprite-sheet that should
- * be performed over the course of a single particle's lifetime.
- *
- * @property {Number} fixedTimeStep If no `dt` (or `deltaTime`) value is passed to this group's
- * `tick()` function, this number will be used to move the particle
- * simulation forward. Value in SECONDS.
- *
- * @property {Boolean} hasPerspective Whether the distance a particle is from the camera should affect
- * the particle's size.
- *
- * @property {Boolean} colorize Whether the particles in this group should be rendered with color, or
- * whether the only color of particles will come from the provided texture.
- *
- * @property {Number} blending One of Three.js's blending modes to apply to this group's `ShaderMaterial`.
- *
- * @property {Boolean} transparent Whether these particle's should be rendered with transparency.
- *
- * @property {Number} alphaTest Sets the alpha value to be used when running an alpha test on the `texture.value` property. Value between 0 and 1.
- *
- * @property {Boolean} depthWrite Whether rendering the group has any effect on the depth buffer.
- *
- * @property {Boolean} depthTest Whether to have depth test enabled when rendering this group.
- *
- * @property {Boolean} fog Whether this group's particles should be affected by their scene's fog.
- *
- * @property {Number} scale The scale factor to apply to this group's particle sizes. Useful for
- * setting particle sizes to be relative to renderer size.
- */
-
-
- /**
- * The SPE.Group class. Creates a new group, containing a material, geometry, and mesh.
- *
- * @constructor
- * @param {GroupOptions} options A map of options to configure the group instance.
- */
- SPE.Group = function( options ) {
- 'use strict';
-
- var utils = SPE.utils,
- types = utils.types;
-
- // Ensure we have a map of options to play with
- options = utils.ensureTypedArg( options, types.OBJECT, {} );
- options.texture = utils.ensureTypedArg( options.texture, types.OBJECT, {} );
-
- // Assign a UUID to this instance
- this.uuid = THREE.Math.generateUUID();
-
- // If no `deltaTime` value is passed to the `SPE.Group.tick` function,
- // the value of this property will be used to advance the simulation.
- this.fixedTimeStep = utils.ensureTypedArg( options.fixedTimeStep, types.NUMBER, 0.016 );
-
- // Set properties used in the uniforms map, starting with the
- // texture stuff.
- this.texture = utils.ensureInstanceOf( options.texture.value, THREE.Texture, null );
- this.textureFrames = utils.ensureInstanceOf( options.texture.frames, THREE.Vector2, new THREE.Vector2( 1, 1 ) );
- this.textureFrameCount = utils.ensureTypedArg( options.texture.frameCount, types.NUMBER, this.textureFrames.x * this.textureFrames.y );
- this.textureLoop = utils.ensureTypedArg( options.texture.loop, types.NUMBER, 1 );
- this.textureFrames.max( new THREE.Vector2( 1, 1 ) );
-
- this.hasPerspective = utils.ensureTypedArg( options.hasPerspective, types.BOOLEAN, true );
- this.colorize = utils.ensureTypedArg( options.colorize, types.BOOLEAN, true );
-
- this.maxParticleCount = utils.ensureTypedArg( options.maxParticleCount, types.NUMBER, null );
-
-
- // Set properties used to define the ShaderMaterial's appearance.
- this.blending = utils.ensureTypedArg( options.blending, types.NUMBER, THREE.AdditiveBlending );
- this.transparent = utils.ensureTypedArg( options.transparent, types.BOOLEAN, true );
- this.alphaTest = parseFloat( utils.ensureTypedArg( options.alphaTest, types.NUMBER, 0.0 ) );
- this.depthWrite = utils.ensureTypedArg( options.depthWrite, types.BOOLEAN, false );
- this.depthTest = utils.ensureTypedArg( options.depthTest, types.BOOLEAN, true );
- this.fog = utils.ensureTypedArg( options.fog, types.BOOLEAN, true );
- this.scale = utils.ensureTypedArg( options.scale, types.NUMBER, 300 );
-
- // Where emitter's go to curl up in a warm blanket and live
- // out their days.
- this.emitters = [];
- this.emitterIDs = [];
-
- // Create properties for use by the emitter pooling functions.
- this._pool = [];
- this._poolCreationSettings = null;
- this._createNewWhenPoolEmpty = 0;
-
- // Whether all attributes should be forced to updated
- // their entire buffer contents on the next tick.
- //
- // Used when an emitter is removed.
- this._attributesNeedRefresh = false;
- this._attributesNeedDynamicReset = false;
-
- this.particleCount = 0;
-
-
- // Map of uniforms to be applied to the ShaderMaterial instance.
- this.uniforms = {
- tex: {
- type: 't',
- value: this.texture
- },
- textureAnimation: {
- type: 'v4',
- value: new THREE.Vector4(
- this.textureFrames.x,
- this.textureFrames.y,
- this.textureFrameCount,
- Math.max( Math.abs( this.textureLoop ), 1.0 )
- )
- },
- fogColor: {
- type: 'c',
- value: this.fog ? new THREE.Color() : null
- },
- fogNear: {
- type: 'f',
- value: 10
- },
- fogFar: {
- type: 'f',
- value: 200
- },
- fogDensity: {
- type: 'f',
- value: 0.5
- },
- deltaTime: {
- type: 'f',
- value: 0
- },
- runTime: {
- type: 'f',
- value: 0
- },
- scale: {
- type: 'f',
- value: this.scale
- }
- };
-
- // Add some defines into the mix...
- this.defines = {
- HAS_PERSPECTIVE: this.hasPerspective,
- COLORIZE: this.colorize,
- VALUE_OVER_LIFETIME_LENGTH: SPE.valueOverLifetimeLength,
-
- SHOULD_ROTATE_TEXTURE: false,
- SHOULD_ROTATE_PARTICLES: false,
- SHOULD_WIGGLE_PARTICLES: false,
-
- SHOULD_CALCULATE_SPRITE: this.textureFrames.x > 1 || this.textureFrames.y > 1
- };
-
- // Map of all attributes to be applied to the particles.
- //
- // See SPE.ShaderAttribute for a bit more info on this bit.
- this.attributes = {
- position: new SPE.ShaderAttribute( 'v3', true ),
- acceleration: new SPE.ShaderAttribute( 'v4', true ), // w component is drag
- velocity: new SPE.ShaderAttribute( 'v3', true ),
- rotation: new SPE.ShaderAttribute( 'v4', true ),
- rotationCenter: new SPE.ShaderAttribute( 'v3', true ),
- params: new SPE.ShaderAttribute( 'v4', true ), // Holds (alive, age, delay, wiggle)
- size: new SPE.ShaderAttribute( 'v4', true ),
- angle: new SPE.ShaderAttribute( 'v4', true ),
- color: new SPE.ShaderAttribute( 'v4', true ),
- opacity: new SPE.ShaderAttribute( 'v4', true )
- };
-
- this.attributeKeys = Object.keys( this.attributes );
- this.attributeCount = this.attributeKeys.length;
-
- // Create the ShaderMaterial instance that'll help render the
- // particles.
- this.material = new THREE.ShaderMaterial( {
- uniforms: this.uniforms,
- vertexShader: SPE.shaders.vertex,
- fragmentShader: SPE.shaders.fragment,
- blending: this.blending,
- transparent: this.transparent,
- alphaTest: this.alphaTest,
- depthWrite: this.depthWrite,
- depthTest: this.depthTest,
- defines: this.defines,
- fog: this.fog
- } );
-
- // Create the BufferGeometry and Points instances, ensuring
- // the geometry and material are given to the latter.
- this.geometry = new THREE.BufferGeometry();
- this.mesh = new THREE.Points( this.geometry, this.material );
-
- if ( this.maxParticleCount === null ) {
- console.warn( 'SPE.Group: No maxParticleCount specified. Adding emitters after rendering will probably cause errors.' );
- }
- };
-
- SPE.Group.constructor = SPE.Group;
-
-
- SPE.Group.prototype._updateDefines = function() {
- 'use strict';
-
- var emitters = this.emitters,
- i = emitters.length - 1,
- emitter,
- defines = this.defines;
-
- for ( i; i >= 0; --i ) {
- emitter = emitters[ i ];
-
- // Only do angle calculation if there's no spritesheet defined.
- //
- // Saves calculations being done and then overwritten in the shaders.
- if ( !defines.SHOULD_CALCULATE_SPRITE ) {
- defines.SHOULD_ROTATE_TEXTURE = defines.SHOULD_ROTATE_TEXTURE || !!Math.max(
- Math.max.apply( null, emitter.angle.value ),
- Math.max.apply( null, emitter.angle.spread )
- );
- }
-
- defines.SHOULD_ROTATE_PARTICLES = defines.SHOULD_ROTATE_PARTICLES || !!Math.max(
- emitter.rotation.angle,
- emitter.rotation.angleSpread
- );
-
- defines.SHOULD_WIGGLE_PARTICLES = defines.SHOULD_WIGGLE_PARTICLES || !!Math.max(
- emitter.wiggle.value,
- emitter.wiggle.spread
- );
- }
-
- this.material.needsUpdate = true;
- };
-
- SPE.Group.prototype._applyAttributesToGeometry = function() {
- 'use strict';
-
- var attributes = this.attributes,
- geometry = this.geometry,
- geometryAttributes = geometry.attributes,
- attribute,
- geometryAttribute;
-
- // Loop through all the shader attributes and assign (or re-assign)
- // typed array buffers to each one.
- for ( var attr in attributes ) {
- if ( attributes.hasOwnProperty( attr ) ) {
- attribute = attributes[ attr ];
- geometryAttribute = geometryAttributes[ attr ];
-
- // Update the array if this attribute exists on the geometry.
- //
- // This needs to be done because the attribute's typed array might have
- // been resized and reinstantiated, and might now be looking at a
- // different ArrayBuffer, so reference needs updating.
- if ( geometryAttribute ) {
- geometryAttribute.array = attribute.typedArray.array;
- }
-
- // // Add the attribute to the geometry if it doesn't already exist.
- else {
- geometry.setAttribute( attr, attribute.bufferAttribute );
- }
-
- // Mark the attribute as needing an update the next time a frame is rendered.
- attribute.bufferAttribute.needsUpdate = true;
- }
- }
-
- // Mark the draw range on the geometry. This will ensure
- // only the values in the attribute buffers that are
- // associated with a particle will be used in THREE's
- // render cycle.
- this.geometry.setDrawRange( 0, this.particleCount );
- };
-
- /**
- * Adds an SPE.Emitter instance to this group, creating particle values and
- * assigning them to this group's shader attributes.
- *
- * @param {Emitter} emitter The emitter to add to this group.
- */
- SPE.Group.prototype.addEmitter = function( emitter ) {
- 'use strict';
-
- // Ensure an actual emitter instance is passed here.
- //
- // Decided not to throw here, just in case a scene's
- // rendering would be paused. Logging an error instead
- // of stopping execution if exceptions aren't caught.
- if ( emitter instanceof SPE.Emitter === false ) {
- console.error( '`emitter` argument must be instance of SPE.Emitter. Was provided with:', emitter );
- return;
- }
-
- // If the emitter already exists as a member of this group, then
- // stop here, we don't want to add it again.
- else if ( this.emitterIDs.indexOf( emitter.uuid ) > -1 ) {
- console.error( 'Emitter already exists in this group. Will not add again.' );
- return;
- }
-
- // And finally, if the emitter is a member of another group,
- // don't add it to this group.
- else if ( emitter.group !== null ) {
- console.error( 'Emitter already belongs to another group. Will not add to requested group.' );
- return;
- }
-
- var attributes = this.attributes,
- start = this.particleCount,
- end = start + emitter.particleCount;
-
- // Update this group's particle count.
- this.particleCount = end;
-
- // Emit a warning if the emitter being added will exceed the buffer sizes specified.
- if ( this.maxParticleCount !== null && this.particleCount > this.maxParticleCount ) {
- console.warn( 'SPE.Group: maxParticleCount exceeded. Requesting', this.particleCount, 'particles, can support only', this.maxParticleCount );
- }
-
-
- // Set the `particlesPerSecond` value (PPS) on the emitter.
- // It's used to determine how many particles to release
- // on a per-frame basis.
- emitter._calculatePPSValue( emitter.maxAge._value + emitter.maxAge._spread );
- emitter._setBufferUpdateRanges( this.attributeKeys );
-
- // Store the offset value in the TypedArray attributes for this emitter.
- emitter._setAttributeOffset( start );
-
- // Save a reference to this group on the emitter so it knows
- // where it belongs.
- emitter.group = this;
-
- // Store reference to the attributes on the emitter for
- // easier access during the emitter's tick function.
- emitter.attributes = this.attributes;
-
-
-
- // Ensure the attributes and their BufferAttributes exist, and their
- // TypedArrays are of the correct size.
- for ( var attr in attributes ) {
- if ( attributes.hasOwnProperty( attr ) ) {
- // When creating a buffer, pass through the maxParticle count
- // if one is specified.
- attributes[ attr ]._createBufferAttribute(
- this.maxParticleCount !== null ?
- this.maxParticleCount :
- this.particleCount
- );
- }
- }
-
- // Loop through each particle this emitter wants to have, and create the attributes values,
- // storing them in the TypedArrays that each attribute holds.
- for ( var i = start; i < end; ++i ) {
- emitter._assignPositionValue( i );
- emitter._assignForceValue( i, 'velocity' );
- emitter._assignForceValue( i, 'acceleration' );
- emitter._assignAbsLifetimeValue( i, 'opacity' );
- emitter._assignAbsLifetimeValue( i, 'size' );
- emitter._assignAngleValue( i );
- emitter._assignRotationValue( i );
- emitter._assignParamsValue( i );
- emitter._assignColorValue( i );
- }
-
- // Update the geometry and make sure the attributes are referencing
- // the typed arrays properly.
- this._applyAttributesToGeometry();
-
- // Store this emitter in this group's emitter's store.
- this.emitters.push( emitter );
- this.emitterIDs.push( emitter.uuid );
-
- // Update certain flags to enable shader calculations only if they're necessary.
- this._updateDefines( emitter );
-
- // Update the material since defines might have changed
- this.material.needsUpdate = true;
- this.geometry.needsUpdate = true;
- this._attributesNeedRefresh = true;
-
- // Return the group to enable chaining.
- return this;
- };
-
- /**
- * Removes an SPE.Emitter instance from this group. When called,
- * all particle's belonging to the given emitter will be instantly
- * removed from the scene.
- *
- * @param {Emitter} emitter The emitter to add to this group.
- */
- SPE.Group.prototype.removeEmitter = function( emitter ) {
- 'use strict';
-
- var emitterIndex = this.emitterIDs.indexOf( emitter.uuid );
-
- // Ensure an actual emitter instance is passed here.
- //
- // Decided not to throw here, just in case a scene's
- // rendering would be paused. Logging an error instead
- // of stopping execution if exceptions aren't caught.
- if ( emitter instanceof SPE.Emitter === false ) {
- console.error( '`emitter` argument must be instance of SPE.Emitter. Was provided with:', emitter );
- return;
- }
-
- // Issue an error if the emitter isn't a member of this group.
- else if ( emitterIndex === -1 ) {
- console.error( 'Emitter does not exist in this group. Will not remove.' );
- return;
- }
-
- // Kill all particles by marking them as dead
- // and their age as 0.
- var start = emitter.attributeOffset,
- end = start + emitter.particleCount,
- params = this.attributes.params.typedArray;
-
- // Set alive and age to zero.
- for ( var i = start; i < end; ++i ) {
- params.array[ i * 4 ] = 0.0;
- params.array[ i * 4 + 1 ] = 0.0;
- }
-
- // Remove the emitter from this group's "store".
- this.emitters.splice( emitterIndex, 1 );
- this.emitterIDs.splice( emitterIndex, 1 );
-
- // Remove this emitter's attribute values from all shader attributes.
- // The `.splice()` call here also marks each attribute's buffer
- // as needing to update it's entire contents.
- for ( var attr in this.attributes ) {
- if ( this.attributes.hasOwnProperty( attr ) ) {
- this.attributes[ attr ].splice( start, end );
- }
- }
-
- // Ensure this group's particle count is correct.
- this.particleCount -= emitter.particleCount;
-
- // Call the emitter's remove method.
- emitter._onRemove();
-
- // Set a flag to indicate that the attribute buffers should
- // be updated in their entirety on the next frame.
- this._attributesNeedRefresh = true;
- };
-
-
- /**
- * Fetch a single emitter instance from the pool.
- * If there are no objects in the pool, a new emitter will be
- * created if specified.
- *
- * @return {Emitter|null}
- */
- SPE.Group.prototype.getFromPool = function() {
- 'use strict';
-
- var pool = this._pool,
- createNew = this._createNewWhenPoolEmpty;
-
- if ( pool.length ) {
- return pool.pop();
- }
- else if ( createNew ) {
- var emitter = new SPE.Emitter( this._poolCreationSettings );
-
- this.addEmitter( emitter );
-
- return emitter;
- }
-
- return null;
- };
-
-
- /**
- * Release an emitter into the pool.
- *
- * @param {ShaderParticleEmitter} emitter
- * @return {Group} This group instance.
- */
- SPE.Group.prototype.releaseIntoPool = function( emitter ) {
- 'use strict';
-
- if ( emitter instanceof SPE.Emitter === false ) {
- console.error( 'Argument is not instanceof SPE.Emitter:', emitter );
- return;
- }
-
- emitter.reset();
- this._pool.unshift( emitter );
-
- return this;
- };
-
-
- /**
- * Get the pool array
- *
- * @return {Array}
- */
- SPE.Group.prototype.getPool = function() {
- 'use strict';
- return this._pool;
- };
-
-
- /**
- * Add a pool of emitters to this particle group
- *
- * @param {Number} numEmitters The number of emitters to add to the pool.
- * @param {EmitterOptions|Array} emitterOptions An object, or array of objects, describing the options to pass to each emitter.
- * @param {Boolean} createNew Should a new emitter be created if the pool runs out?
- * @return {Group} This group instance.
- */
- SPE.Group.prototype.addPool = function( numEmitters, emitterOptions, createNew ) {
- 'use strict';
-
- var emitter;
-
- // Save relevant settings and flags.
- this._poolCreationSettings = emitterOptions;
- this._createNewWhenPoolEmpty = !!createNew;
-
- // Create the emitters, add them to this group and the pool.
- for ( var i = 0; i < numEmitters; ++i ) {
- if ( Array.isArray( emitterOptions ) ) {
- emitter = new SPE.Emitter( emitterOptions[ i ] );
- }
- else {
- emitter = new SPE.Emitter( emitterOptions );
- }
- this.addEmitter( emitter );
- this.releaseIntoPool( emitter );
- }
-
- return this;
- };
-
-
-
- SPE.Group.prototype._triggerSingleEmitter = function( pos ) {
- 'use strict';
-
- var emitter = this.getFromPool(),
- self = this;
-
- if ( emitter === null ) {
- console.log( 'SPE.Group pool ran out.' );
- return;
- }
-
- // TODO:
- // - Make sure buffers are update with thus new position.
- if ( pos instanceof THREE.Vector3 ) {
- emitter.position.value.copy( pos );
-
- // Trigger the setter for this property to force an
- // update to the emitter's position attribute.
- emitter.position.value = emitter.position.value;
- }
-
- emitter.enable();
-
- setTimeout( function() {
- emitter.disable();
- self.releaseIntoPool( emitter );
- }, ( Math.max( emitter.duration, ( emitter.maxAge.value + emitter.maxAge.spread ) ) ) * 1000 );
-
- return this;
- };
-
-
- /**
- * Set a given number of emitters as alive, with an optional position
- * vector3 to move them to.
- *
- * @param {Number} numEmitters The number of emitters to activate
- * @param {Object} [position=undefined] A THREE.Vector3 instance describing the position to activate the emitter(s) at.
- * @return {Group} This group instance.
- */
- SPE.Group.prototype.triggerPoolEmitter = function( numEmitters, position ) {
- 'use strict';
-
- if ( typeof numEmitters === 'number' && numEmitters > 1 ) {
- for ( var i = 0; i < numEmitters; ++i ) {
- this._triggerSingleEmitter( position );
- }
- }
- else {
- this._triggerSingleEmitter( position );
- }
-
- return this;
- };
-
-
-
- SPE.Group.prototype._updateUniforms = function( dt ) {
- 'use strict';
-
- this.uniforms.runTime.value += dt;
- this.uniforms.deltaTime.value = dt;
- };
-
- SPE.Group.prototype._resetBufferRanges = function() {
- 'use strict';
-
- var keys = this.attributeKeys,
- i = this.attributeCount - 1,
- attrs = this.attributes;
-
- for ( i; i >= 0; --i ) {
- attrs[ keys[ i ] ].resetUpdateRange();
- }
- };
-
-
- SPE.Group.prototype._updateBuffers = function( emitter ) {
- 'use strict';
-
- var keys = this.attributeKeys,
- i = this.attributeCount - 1,
- attrs = this.attributes,
- emitterRanges = emitter.bufferUpdateRanges,
- key,
- emitterAttr,
- attr;
-
- for ( i; i >= 0; --i ) {
- key = keys[ i ];
- emitterAttr = emitterRanges[ key ];
- attr = attrs[ key ];
- attr.setUpdateRange( emitterAttr.min, emitterAttr.max );
- attr.flagUpdate();
- }
- };
-
-
- /**
- * Simulate all the emitter's belonging to this group, updating
- * attribute values along the way.
- * @param {Number} [dt=Group's `fixedTimeStep` value] The number of seconds to simulate the group's emitters for (deltaTime)
- */
- SPE.Group.prototype.tick = function( dt ) {
- 'use strict';
-
- var emitters = this.emitters,
- numEmitters = emitters.length,
- deltaTime = dt || this.fixedTimeStep,
- keys = this.attributeKeys,
- i,
- attrs = this.attributes;
-
- // Update uniform values.
- this._updateUniforms( deltaTime );
-
- // Reset buffer update ranges on the shader attributes.
- this._resetBufferRanges();
-
-
- // If nothing needs updating, then stop here.
- if (
- numEmitters === 0 &&
- this._attributesNeedRefresh === false &&
- this._attributesNeedDynamicReset === false
- ) {
- return;
- }
-
- // Loop through each emitter in this group and
- // simulate it, then update the shader attribute
- // buffers.
- for ( var i = 0, emitter; i < numEmitters; ++i ) {
- emitter = emitters[ i ];
- emitter.tick( deltaTime );
- this._updateBuffers( emitter );
- }
-
- // If the shader attributes have been refreshed,
- // then the dynamic properties of each buffer
- // attribute will need to be reset back to
- // what they should be.
- if ( this._attributesNeedDynamicReset === true ) {
- i = this.attributeCount - 1;
-
- for ( i; i >= 0; --i ) {
- attrs[ keys[ i ] ].resetDynamic();
- }
-
- this._attributesNeedDynamicReset = false;
- }
-
- // If this group's shader attributes need a full refresh
- // then mark each attribute's buffer attribute as
- // needing so.
- if ( this._attributesNeedRefresh === true ) {
- i = this.attributeCount - 1;
-
- for ( i; i >= 0; --i ) {
- attrs[ keys[ i ] ].forceUpdateAll();
- }
-
- this._attributesNeedRefresh = false;
- this._attributesNeedDynamicReset = true;
- }
- };
-
-
- /**
- * Dipose the geometry and material for the group.
- *
- * @return {Group} Group instance.
- */
- SPE.Group.prototype.dispose = function() {
- 'use strict';
- this.geometry.dispose();
- this.material.dispose();
- return this;
- };
-
-
- /**
- * An SPE.Emitter instance.
- * @typedef {Object} Emitter
- * @see SPE.Emitter
- */
-
- /**
- * A map of options to configure an SPE.Emitter instance.
- *
- * @typedef {Object} EmitterOptions
- *
- * @property {distribution} [type=BOX] The default distribution this emitter should use to control
- * its particle's spawn position and force behaviour.
- * Must be an SPE.distributions.* value.
- *
- *
- * @property {Number} [particleCount=100] The total number of particles this emitter will hold. NOTE: this is not the number
- * of particles emitted in a second, or anything like that. The number of particles
- * emitted per-second is calculated by particleCount / maxAge (approximately!)
- *
- * @property {Number|null} [duration=null] The duration in seconds that this emitter should live for. If not specified, the emitter
- * will emit particles indefinitely.
- * NOTE: When an emitter is older than a specified duration, the emitter is NOT removed from
- * it's group, but rather is just marked as dead, allowing it to be reanimated at a later time
- * using `SPE.Emitter.prototype.enable()`.
- *
- * @property {Boolean} [isStatic=false] Whether this emitter should be not be simulated (true).
- * @property {Boolean} [activeMultiplier=1] A value between 0 and 1 describing what percentage of this emitter's particlesPerSecond should be
- * emitted, where 0 is 0%, and 1 is 100%.
- * For example, having an emitter with 100 particles, a maxAge of 2, yields a particlesPerSecond
- * value of 50. Setting `activeMultiplier` to 0.5, then, will only emit 25 particles per second (0.5 = 50%).
- * Values greater than 1 will emulate a burst of particles, causing the emitter to run out of particles
- * before it's next activation cycle.
- *
- * @property {Boolean} [direction=1] The direction of the emitter. If value is `1`, emitter will start at beginning of particle's lifecycle.
- * If value is `-1`, emitter will start at end of particle's lifecycle and work it's way backwards.
- *
- * @property {Object} [maxAge={}] An object describing the particle's maximum age in seconds.
- * @property {Number} [maxAge.value=2] A number between 0 and 1 describing the amount of maxAge to apply to all particles.
- * @property {Number} [maxAge.spread=0] A number describing the maxAge variance on a per-particle basis.
- *
- *
- * @property {Object} [position={}] An object describing this emitter's position.
- * @property {Object} [position.value=new THREE.Vector3()] A THREE.Vector3 instance describing this emitter's base position.
- * @property {Object} [position.spread=new THREE.Vector3()] A THREE.Vector3 instance describing this emitter's position variance on a per-particle basis.
- * Note that when using a SPHERE or DISC distribution, only the x-component
- * of this vector is used.
- * When using a LINE distribution, this value is the endpoint of the LINE.
- * @property {Object} [position.spreadClamp=new THREE.Vector3()] A THREE.Vector3 instance describing the numeric multiples the particle's should
- * be spread out over.
- * Note that when using a SPHERE or DISC distribution, only the x-component
- * of this vector is used.
- * When using a LINE distribution, this property is ignored.
- * @property {Number} [position.radius=10] This emitter's base radius.
- * @property {Object} [position.radiusScale=new THREE.Vector3()] A THREE.Vector3 instance describing the radius's scale in all three axes. Allows a SPHERE or DISC to be squashed or stretched.
- * @property {distribution} [position.distribution=value of the `type` option.] A specific distribution to use when radiusing particles. Overrides the `type` option.
- * @property {Boolean} [position.randomise=false] When a particle is re-spawned, whether it's position should be re-randomised or not. Can incur a performance hit.
- *
- *
- * @property {Object} [velocity={}] An object describing this particle velocity.
- * @property {Object} [velocity.value=new THREE.Vector3()] A THREE.Vector3 instance describing this emitter's base velocity.
- * @property {Object} [velocity.spread=new THREE.Vector3()] A THREE.Vector3 instance describing this emitter's velocity variance on a per-particle basis.
- * Note that when using a SPHERE or DISC distribution, only the x-component
- * of this vector is used.
- * @property {distribution} [velocity.distribution=value of the `type` option.] A specific distribution to use when calculating a particle's velocity. Overrides the `type` option.
- * @property {Boolean} [velocity.randomise=false] When a particle is re-spawned, whether it's velocity should be re-randomised or not. Can incur a performance hit.
- *
- *
- * @property {Object} [acceleration={}] An object describing this particle's acceleration.
- * @property {Object} [acceleration.value=new THREE.Vector3()] A THREE.Vector3 instance describing this emitter's base acceleration.
- * @property {Object} [acceleration.spread=new THREE.Vector3()] A THREE.Vector3 instance describing this emitter's acceleration variance on a per-particle basis.
- * Note that when using a SPHERE or DISC distribution, only the x-component
- * of this vector is used.
- * @property {distribution} [acceleration.distribution=value of the `type` option.] A specific distribution to use when calculating a particle's acceleration. Overrides the `type` option.
- * @property {Boolean} [acceleration.randomise=false] When a particle is re-spawned, whether it's acceleration should be re-randomised or not. Can incur a performance hit.
- *
- *
- * @property {Object} [drag={}] An object describing this particle drag. Drag is applied to both velocity and acceleration values.
- * @property {Number} [drag.value=0] A number between 0 and 1 describing the amount of drag to apply to all particles.
- * @property {Number} [drag.spread=0] A number describing the drag variance on a per-particle basis.
- * @property {Boolean} [drag.randomise=false] When a particle is re-spawned, whether it's drag should be re-randomised or not. Can incur a performance hit.
- *
- *
- * @property {Object} [wiggle={}] This is quite a fun one! The values of this object will determine whether a particle will wiggle, or jiggle, or wave,
- * or shimmy, or waggle, or... Well you get the idea. The wiggle is calculated over-time, meaning that a particle will
- * start off with no wiggle, and end up wiggling about with the distance of the `value` specified by the time it dies.
- * It's quite handy to simulate fire embers, or similar effects where the particle's position should slightly change over
- * time, and such change isn't easily controlled by rotation, velocity, or acceleration. The wiggle is a combination of sin and cos calculations, so is circular in nature.
- * @property {Number} [wiggle.value=0] A number describing the amount of wiggle to apply to all particles. It's measured in distance.
- * @property {Number} [wiggle.spread=0] A number describing the wiggle variance on a per-particle basis.
- *
- *
- * @property {Object} [rotation={}] An object describing this emitter's rotation. It can either be static, or set to rotate from 0radians to the value of `rotation.value`
- * over a particle's lifetime. Rotation values affect both a particle's position and the forces applied to it.
- * @property {Object} [rotation.axis=new THREE.Vector3(0, 1, 0)] A THREE.Vector3 instance describing this emitter's axis of rotation.
- * @property {Object} [rotation.axisSpread=new THREE.Vector3()] A THREE.Vector3 instance describing the amount of variance to apply to the axis of rotation on
- * a per-particle basis.
- * @property {Number} [rotation.angle=0] The angle of rotation, given in radians. If `rotation.static` is true, the emitter will start off rotated at this angle, and stay as such.
- * Otherwise, the particles will rotate from 0radians to this value over their lifetimes.
- * @property {Number} [rotation.angleSpread=0] The amount of variance in each particle's rotation angle.
- * @property {Boolean} [rotation.static=false] Whether the rotation should be static or not.
- * @property {Object} [rotation.center=The value of `position.value`] A THREE.Vector3 instance describing the center point of rotation.
- * @property {Boolean} [rotation.randomise=false] When a particle is re-spawned, whether it's rotation should be re-randomised or not. Can incur a performance hit.
- *
- *
- * @property {Object} [color={}] An object describing a particle's color. This property is a "value-over-lifetime" property, meaning an array of values and spreads can be
- * given to describe specific value changes over a particle's lifetime.
- * Depending on the value of SPE.valueOverLifetimeLength, if arrays of THREE.Color instances are given, then the array will be interpolated to
- * have a length matching the value of SPE.valueOverLifetimeLength.
- * @property {Object} [color.value=new THREE.Color()] Either a single THREE.Color instance, or an array of THREE.Color instances to describe the color of a particle over it's lifetime.
- * @property {Object} [color.spread=new THREE.Vector3()] Either a single THREE.Vector3 instance, or an array of THREE.Vector3 instances to describe the color variance of a particle over it's lifetime.
- * @property {Boolean} [color.randomise=false] When a particle is re-spawned, whether it's color should be re-randomised or not. Can incur a performance hit.
- *
- *
- * @property {Object} [opacity={}] An object describing a particle's opacity. This property is a "value-over-lifetime" property, meaning an array of values and spreads can be
- * given to describe specific value changes over a particle's lifetime.
- * Depending on the value of SPE.valueOverLifetimeLength, if arrays of numbers are given, then the array will be interpolated to
- * have a length matching the value of SPE.valueOverLifetimeLength.
- * @property {Number} [opacity.value=1] Either a single number, or an array of numbers to describe the opacity of a particle over it's lifetime.
- * @property {Number} [opacity.spread=0] Either a single number, or an array of numbers to describe the opacity variance of a particle over it's lifetime.
- * @property {Boolean} [opacity.randomise=false] When a particle is re-spawned, whether it's opacity should be re-randomised or not. Can incur a performance hit.
- *
- *
- * @property {Object} [size={}] An object describing a particle's size. This property is a "value-over-lifetime" property, meaning an array of values and spreads can be
- * given to describe specific value changes over a particle's lifetime.
- * Depending on the value of SPE.valueOverLifetimeLength, if arrays of numbers are given, then the array will be interpolated to
- * have a length matching the value of SPE.valueOverLifetimeLength.
- * @property {Number} [size.value=1] Either a single number, or an array of numbers to describe the size of a particle over it's lifetime.
- * @property {Number} [size.spread=0] Either a single number, or an array of numbers to describe the size variance of a particle over it's lifetime.
- * @property {Boolean} [size.randomise=false] When a particle is re-spawned, whether it's size should be re-randomised or not. Can incur a performance hit.
- *
- *
- * @property {Object} [angle={}] An object describing a particle's angle. The angle is a 2d-rotation, measured in radians, applied to the particle's texture.
- * NOTE: if a particle's texture is a sprite-sheet, this value IS IGNORED.
- * This property is a "value-over-lifetime" property, meaning an array of values and spreads can be
- * given to describe specific value changes over a particle's lifetime.
- * Depending on the value of SPE.valueOverLifetimeLength, if arrays of numbers are given, then the array will be interpolated to
- * have a length matching the value of SPE.valueOverLifetimeLength.
- * @property {Number} [angle.value=0] Either a single number, or an array of numbers to describe the angle of a particle over it's lifetime.
- * @property {Number} [angle.spread=0] Either a single number, or an array of numbers to describe the angle variance of a particle over it's lifetime.
- * @property {Boolean} [angle.randomise=false] When a particle is re-spawned, whether it's angle should be re-randomised or not. Can incur a performance hit.
- *
- */
-
- /**
- * The SPE.Emitter class.
- *
- * @constructor
- *
- * @param {EmitterOptions} options A map of options to configure the emitter.
- */
- SPE.Emitter = function( options ) {
- 'use strict';
-
- var utils = SPE.utils,
- types = utils.types,
- lifetimeLength = SPE.valueOverLifetimeLength;
-
- // Ensure we have a map of options to play with,
- // and that each option is in the correct format.
- options = utils.ensureTypedArg( options, types.OBJECT, {} );
- options.position = utils.ensureTypedArg( options.position, types.OBJECT, {} );
- options.velocity = utils.ensureTypedArg( options.velocity, types.OBJECT, {} );
- options.acceleration = utils.ensureTypedArg( options.acceleration, types.OBJECT, {} );
- options.radius = utils.ensureTypedArg( options.radius, types.OBJECT, {} );
- options.drag = utils.ensureTypedArg( options.drag, types.OBJECT, {} );
- options.rotation = utils.ensureTypedArg( options.rotation, types.OBJECT, {} );
- options.color = utils.ensureTypedArg( options.color, types.OBJECT, {} );
- options.opacity = utils.ensureTypedArg( options.opacity, types.OBJECT, {} );
- options.size = utils.ensureTypedArg( options.size, types.OBJECT, {} );
- options.angle = utils.ensureTypedArg( options.angle, types.OBJECT, {} );
- options.wiggle = utils.ensureTypedArg( options.wiggle, types.OBJECT, {} );
- options.maxAge = utils.ensureTypedArg( options.maxAge, types.OBJECT, {} );
-
- if ( options.onParticleSpawn ) {
- console.warn( 'onParticleSpawn has been removed. Please set properties directly to alter values at runtime.' );
- }
-
- this.uuid = THREE.Math.generateUUID();
-
- this.type = utils.ensureTypedArg( options.type, types.NUMBER, SPE.distributions.BOX );
-
- // Start assigning properties...kicking it off with props that DON'T support values over
- // lifetimes.
- //
- // Btw, values over lifetimes are just the new way of referring to *Start, *Middle, and *End.
- this.position = {
- _value: utils.ensureInstanceOf( options.position.value, THREE.Vector3, new THREE.Vector3() ),
- _spread: utils.ensureInstanceOf( options.position.spread, THREE.Vector3, new THREE.Vector3() ),
- _spreadClamp: utils.ensureInstanceOf( options.position.spreadClamp, THREE.Vector3, new THREE.Vector3() ),
- _distribution: utils.ensureTypedArg( options.position.distribution, types.NUMBER, this.type ),
- _randomise: utils.ensureTypedArg( options.position.randomise, types.BOOLEAN, false ),
- _radius: utils.ensureTypedArg( options.position.radius, types.NUMBER, 10 ),
- _radiusScale: utils.ensureInstanceOf( options.position.radiusScale, THREE.Vector3, new THREE.Vector3( 1, 1, 1 ) ),
- _distributionClamp: utils.ensureTypedArg( options.position.distributionClamp, types.NUMBER, 0 ),
- };
-
- this.velocity = {
- _value: utils.ensureInstanceOf( options.velocity.value, THREE.Vector3, new THREE.Vector3() ),
- _spread: utils.ensureInstanceOf( options.velocity.spread, THREE.Vector3, new THREE.Vector3() ),
- _distribution: utils.ensureTypedArg( options.velocity.distribution, types.NUMBER, this.type ),
- _randomise: utils.ensureTypedArg( options.position.randomise, types.BOOLEAN, false )
- };
-
- this.acceleration = {
- _value: utils.ensureInstanceOf( options.acceleration.value, THREE.Vector3, new THREE.Vector3() ),
- _spread: utils.ensureInstanceOf( options.acceleration.spread, THREE.Vector3, new THREE.Vector3() ),
- _distribution: utils.ensureTypedArg( options.acceleration.distribution, types.NUMBER, this.type ),
- _randomise: utils.ensureTypedArg( options.position.randomise, types.BOOLEAN, false )
- };
-
- this.drag = {
- _value: utils.ensureTypedArg( options.drag.value, types.NUMBER, 0 ),
- _spread: utils.ensureTypedArg( options.drag.spread, types.NUMBER, 0 ),
- _randomise: utils.ensureTypedArg( options.position.randomise, types.BOOLEAN, false )
- };
-
- this.wiggle = {
- _value: utils.ensureTypedArg( options.wiggle.value, types.NUMBER, 0 ),
- _spread: utils.ensureTypedArg( options.wiggle.spread, types.NUMBER, 0 )
- };
-
- this.rotation = {
- _axis: utils.ensureInstanceOf( options.rotation.axis, THREE.Vector3, new THREE.Vector3( 0.0, 1.0, 0.0 ) ),
- _axisSpread: utils.ensureInstanceOf( options.rotation.axisSpread, THREE.Vector3, new THREE.Vector3() ),
- _angle: utils.ensureTypedArg( options.rotation.angle, types.NUMBER, 0 ),
- _angleSpread: utils.ensureTypedArg( options.rotation.angleSpread, types.NUMBER, 0 ),
- _static: utils.ensureTypedArg( options.rotation.static, types.BOOLEAN, false ),
- _center: utils.ensureInstanceOf( options.rotation.center, THREE.Vector3, this.position._value.clone() ),
- _randomise: utils.ensureTypedArg( options.position.randomise, types.BOOLEAN, false )
- };
-
-
- this.maxAge = {
- _value: utils.ensureTypedArg( options.maxAge.value, types.NUMBER, 2 ),
- _spread: utils.ensureTypedArg( options.maxAge.spread, types.NUMBER, 0 )
- };
-
-
-
- // The following properties can support either single values, or an array of values that change
- // the property over a particle's lifetime (value over lifetime).
- this.color = {
- _value: utils.ensureArrayInstanceOf( options.color.value, THREE.Color, new THREE.Color() ),
- _spread: utils.ensureArrayInstanceOf( options.color.spread, THREE.Vector3, new THREE.Vector3() ),
- _randomise: utils.ensureTypedArg( options.position.randomise, types.BOOLEAN, false )
- };
-
- this.opacity = {
- _value: utils.ensureArrayTypedArg( options.opacity.value, types.NUMBER, 1 ),
- _spread: utils.ensureArrayTypedArg( options.opacity.spread, types.NUMBER, 0 ),
- _randomise: utils.ensureTypedArg( options.position.randomise, types.BOOLEAN, false )
- };
-
- this.size = {
- _value: utils.ensureArrayTypedArg( options.size.value, types.NUMBER, 1 ),
- _spread: utils.ensureArrayTypedArg( options.size.spread, types.NUMBER, 0 ),
- _randomise: utils.ensureTypedArg( options.position.randomise, types.BOOLEAN, false )
- };
-
- this.angle = {
- _value: utils.ensureArrayTypedArg( options.angle.value, types.NUMBER, 0 ),
- _spread: utils.ensureArrayTypedArg( options.angle.spread, types.NUMBER, 0 ),
- _randomise: utils.ensureTypedArg( options.position.randomise, types.BOOLEAN, false )
- };
-
-
- // Assign renaining option values.
- this.particleCount = utils.ensureTypedArg( options.particleCount, types.NUMBER, 100 );
- this.duration = utils.ensureTypedArg( options.duration, types.NUMBER, null );
- this.isStatic = utils.ensureTypedArg( options.isStatic, types.BOOLEAN, false );
- this.activeMultiplier = utils.ensureTypedArg( options.activeMultiplier, types.NUMBER, 1 );
- this.direction = utils.ensureTypedArg( options.direction, types.NUMBER, 1 );
-
- // Whether this emitter is alive or not.
- this.alive = utils.ensureTypedArg( options.alive, types.BOOLEAN, true );
-
-
- // The following properties are set internally and are not
- // user-controllable.
- this.particlesPerSecond = 0;
-
- // The current particle index for which particles should
- // be marked as active on the next update cycle.
- this.activationIndex = 0;
-
- // The offset in the typed arrays this emitter's
- // particle's values will start at
- this.attributeOffset = 0;
-
- // The end of the range in the attribute buffers
- this.attributeEnd = 0;
-
-
-
- // Holds the time the emitter has been alive for.
- this.age = 0.0;
-
- // Holds the number of currently-alive particles
- this.activeParticleCount = 0.0;
-
- // Holds a reference to this emitter's group once
- // it's added to one.
- this.group = null;
-
- // Holds a reference to this emitter's group's attributes object
- // for easier access.
- this.attributes = null;
-
- // Holds a reference to the params attribute's typed array
- // for quicker access.
- this.paramsArray = null;
-
- // A set of flags to determine whether particular properties
- // should be re-randomised when a particle is reset.
- //
- // If a `randomise` property is given, this is preferred.
- // Otherwise, it looks at whether a spread value has been
- // given.
- //
- // It allows randomization to be turned off as desired. If
- // all randomization is turned off, then I'd expect a performance
- // boost as no attribute buffers (excluding the `params`)
- // would have to be re-passed to the GPU each frame (since nothing
- // except the `params` attribute would have changed).
- this.resetFlags = {
- // params: utils.ensureTypedArg( options.maxAge.randomise, types.BOOLEAN, !!options.maxAge.spread ) ||
- // utils.ensureTypedArg( options.wiggle.randomise, types.BOOLEAN, !!options.wiggle.spread ),
- position: utils.ensureTypedArg( options.position.randomise, types.BOOLEAN, false ) ||
- utils.ensureTypedArg( options.radius.randomise, types.BOOLEAN, false ),
- velocity: utils.ensureTypedArg( options.velocity.randomise, types.BOOLEAN, false ),
- acceleration: utils.ensureTypedArg( options.acceleration.randomise, types.BOOLEAN, false ) ||
- utils.ensureTypedArg( options.drag.randomise, types.BOOLEAN, false ),
- rotation: utils.ensureTypedArg( options.rotation.randomise, types.BOOLEAN, false ),
- rotationCenter: utils.ensureTypedArg( options.rotation.randomise, types.BOOLEAN, false ),
- size: utils.ensureTypedArg( options.size.randomise, types.BOOLEAN, false ),
- color: utils.ensureTypedArg( options.color.randomise, types.BOOLEAN, false ),
- opacity: utils.ensureTypedArg( options.opacity.randomise, types.BOOLEAN, false ),
- angle: utils.ensureTypedArg( options.angle.randomise, types.BOOLEAN, false )
- };
-
- this.updateFlags = {};
- this.updateCounts = {};
-
- // A map to indicate which emitter parameters should update
- // which attribute.
- this.updateMap = {
- maxAge: 'params',
- position: 'position',
- velocity: 'velocity',
- acceleration: 'acceleration',
- drag: 'acceleration',
- wiggle: 'params',
- rotation: 'rotation',
- size: 'size',
- color: 'color',
- opacity: 'opacity',
- angle: 'angle'
- };
-
- for ( var i in this.updateMap ) {
- if ( this.updateMap.hasOwnProperty( i ) ) {
- this.updateCounts[ this.updateMap[ i ] ] = 0.0;
- this.updateFlags[ this.updateMap[ i ] ] = false;
- this._createGetterSetters( this[ i ], i );
- }
- }
-
- this.bufferUpdateRanges = {};
- this.attributeKeys = null;
- this.attributeCount = 0;
-
-
- // Ensure that the value-over-lifetime property objects above
- // have value and spread properties that are of the same length.
- //
- // Also, for now, make sure they have a length of 3 (min/max arguments here).
- utils.ensureValueOverLifetimeCompliance( this.color, lifetimeLength, lifetimeLength );
- utils.ensureValueOverLifetimeCompliance( this.opacity, lifetimeLength, lifetimeLength );
- utils.ensureValueOverLifetimeCompliance( this.size, lifetimeLength, lifetimeLength );
- utils.ensureValueOverLifetimeCompliance( this.angle, lifetimeLength, lifetimeLength );
- };
-
- SPE.Emitter.constructor = SPE.Emitter;
-
- SPE.Emitter.prototype._createGetterSetters = function( propObj, propName ) {
- 'use strict';
-
- var self = this;
-
- for ( var i in propObj ) {
- if ( propObj.hasOwnProperty( i ) ) {
-
- var name = i.replace( '_', '' );
-
- Object.defineProperty( propObj, name, {
- get: ( function( prop ) {
- return function() {
- return this[ prop ];
- };
- }( i ) ),
-
- set: ( function( prop ) {
- return function( value ) {
- var mapName = self.updateMap[ propName ],
- prevValue = this[ prop ],
- length = SPE.valueOverLifetimeLength;
-
- if ( prop === '_rotationCenter' ) {
- self.updateFlags.rotationCenter = true;
- self.updateCounts.rotationCenter = 0.0;
- }
- else if ( prop === '_randomise' ) {
- self.resetFlags[ mapName ] = value;
- }
- else {
- self.updateFlags[ mapName ] = true;
- self.updateCounts[ mapName ] = 0.0;
- }
-
- self.group._updateDefines();
-
- this[ prop ] = value;
-
- // If the previous value was an array, then make
- // sure the provided value is interpolated correctly.
- if ( Array.isArray( prevValue ) ) {
- SPE.utils.ensureValueOverLifetimeCompliance( self[ propName ], length, length );
- }
- };
- }( i ) )
- } );
- }
- }
- };
-
- SPE.Emitter.prototype._setBufferUpdateRanges = function( keys ) {
- 'use strict';
-
- this.attributeKeys = keys;
- this.attributeCount = keys.length;
-
- for ( var i = this.attributeCount - 1; i >= 0; --i ) {
- this.bufferUpdateRanges[ keys[ i ] ] = {
- min: Number.POSITIVE_INFINITY,
- max: Number.NEGATIVE_INFINITY
- };
- }
- };
-
- SPE.Emitter.prototype._calculatePPSValue = function( groupMaxAge ) {
- 'use strict';
-
- var particleCount = this.particleCount;
-
-
- // Calculate the `particlesPerSecond` value for this emitter. It's used
- // when determining which particles should die and which should live to
- // see another day. Or be born, for that matter. The "God" property.
- if ( this.duration ) {
- this.particlesPerSecond = particleCount / ( groupMaxAge < this.duration ? groupMaxAge : this.duration );
- }
- else {
- this.particlesPerSecond = particleCount / groupMaxAge;
- }
- };
-
- SPE.Emitter.prototype._setAttributeOffset = function( startIndex ) {
- this.attributeOffset = startIndex;
- this.activationIndex = startIndex;
- this.activationEnd = startIndex + this.particleCount;
- };
-
-
- SPE.Emitter.prototype._assignValue = function( prop, index ) {
- 'use strict';
-
- switch ( prop ) {
- case 'position':
- this._assignPositionValue( index );
- break;
-
- case 'velocity':
- case 'acceleration':
- this._assignForceValue( index, prop );
- break;
-
- case 'size':
- case 'opacity':
- this._assignAbsLifetimeValue( index, prop );
- break;
-
- case 'angle':
- this._assignAngleValue( index );
- break;
-
- case 'params':
- this._assignParamsValue( index );
- break;
-
- case 'rotation':
- this._assignRotationValue( index );
- break;
-
- case 'color':
- this._assignColorValue( index );
- break;
- }
- };
-
- SPE.Emitter.prototype._assignPositionValue = function( index ) {
- 'use strict';
-
- var distributions = SPE.distributions,
- utils = SPE.utils,
- prop = this.position,
- attr = this.attributes.position,
- value = prop._value,
- spread = prop._spread,
- distribution = prop._distribution;
-
- switch ( distribution ) {
- case distributions.BOX:
- utils.randomVector3( attr, index, value, spread, prop._spreadClamp );
- break;
-
- case distributions.SPHERE:
- utils.randomVector3OnSphere( attr, index, value, prop._radius, prop._spread.x, prop._radiusScale, prop._spreadClamp.x, prop._distributionClamp || this.particleCount );
- break;
-
- case distributions.DISC:
- utils.randomVector3OnDisc( attr, index, value, prop._radius, prop._spread.x, prop._radiusScale, prop._spreadClamp.x );
- break;
-
- case distributions.LINE:
- utils.randomVector3OnLine( attr, index, value, spread );
- break;
- }
- };
-
- SPE.Emitter.prototype._assignForceValue = function( index, attrName ) {
- 'use strict';
-
- var distributions = SPE.distributions,
- utils = SPE.utils,
- prop = this[ attrName ],
- value = prop._value,
- spread = prop._spread,
- distribution = prop._distribution,
- pos,
- positionX,
- positionY,
- positionZ,
- i;
-
- switch ( distribution ) {
- case distributions.BOX:
- utils.randomVector3( this.attributes[ attrName ], index, value, spread );
- break;
-
- case distributions.SPHERE:
- pos = this.attributes.position.typedArray.array;
- i = index * 3;
-
- // Ensure position values aren't zero, otherwise no force will be
- // applied.
- // positionX = utils.zeroToEpsilon( pos[ i ], true );
- // positionY = utils.zeroToEpsilon( pos[ i + 1 ], true );
- // positionZ = utils.zeroToEpsilon( pos[ i + 2 ], true );
- positionX = pos[ i ];
- positionY = pos[ i + 1 ];
- positionZ = pos[ i + 2 ];
-
- utils.randomDirectionVector3OnSphere(
- this.attributes[ attrName ], index,
- positionX, positionY, positionZ,
- this.position._value,
- prop._value.x,
- prop._spread.x
- );
- break;
-
- case distributions.DISC:
- pos = this.attributes.position.typedArray.array;
- i = index * 3;
-
- // Ensure position values aren't zero, otherwise no force will be
- // applied.
- // positionX = utils.zeroToEpsilon( pos[ i ], true );
- // positionY = utils.zeroToEpsilon( pos[ i + 1 ], true );
- // positionZ = utils.zeroToEpsilon( pos[ i + 2 ], true );
- positionX = pos[ i ];
- positionY = pos[ i + 1 ];
- positionZ = pos[ i + 2 ];
-
- utils.randomDirectionVector3OnDisc(
- this.attributes[ attrName ], index,
- positionX, positionY, positionZ,
- this.position._value,
- prop._value.x,
- prop._spread.x
- );
- break;
-
- case distributions.LINE:
- utils.randomVector3OnLine( this.attributes[ attrName ], index, value, spread );
- break;
- }
-
- if ( attrName === 'acceleration' ) {
- var drag = utils.clamp( utils.randomFloat( this.drag._value, this.drag._spread ), 0, 1 );
- this.attributes.acceleration.typedArray.array[ index * 4 + 3 ] = drag;
- }
- };
-
- SPE.Emitter.prototype._assignAbsLifetimeValue = function( index, propName ) {
- 'use strict';
-
- var array = this.attributes[ propName ].typedArray,
- prop = this[ propName ],
- utils = SPE.utils,
- value;
-
- if ( utils.arrayValuesAreEqual( prop._value ) && utils.arrayValuesAreEqual( prop._spread ) ) {
- value = Math.abs( utils.randomFloat( prop._value[ 0 ], prop._spread[ 0 ] ) );
- array.setVec4Components( index, value, value, value, value );
- }
- else {
- array.setVec4Components( index,
- Math.abs( utils.randomFloat( prop._value[ 0 ], prop._spread[ 0 ] ) ),
- Math.abs( utils.randomFloat( prop._value[ 1 ], prop._spread[ 1 ] ) ),
- Math.abs( utils.randomFloat( prop._value[ 2 ], prop._spread[ 2 ] ) ),
- Math.abs( utils.randomFloat( prop._value[ 3 ], prop._spread[ 3 ] ) )
- );
- }
- };
-
- SPE.Emitter.prototype._assignAngleValue = function( index ) {
- 'use strict';
-
- var array = this.attributes.angle.typedArray,
- prop = this.angle,
- utils = SPE.utils,
- value;
-
- if ( utils.arrayValuesAreEqual( prop._value ) && utils.arrayValuesAreEqual( prop._spread ) ) {
- value = utils.randomFloat( prop._value[ 0 ], prop._spread[ 0 ] );
- array.setVec4Components( index, value, value, value, value );
- }
- else {
- array.setVec4Components( index,
- utils.randomFloat( prop._value[ 0 ], prop._spread[ 0 ] ),
- utils.randomFloat( prop._value[ 1 ], prop._spread[ 1 ] ),
- utils.randomFloat( prop._value[ 2 ], prop._spread[ 2 ] ),
- utils.randomFloat( prop._value[ 3 ], prop._spread[ 3 ] )
- );
- }
- };
-
- SPE.Emitter.prototype._assignParamsValue = function( index ) {
- 'use strict';
-
- this.attributes.params.typedArray.setVec4Components( index,
- this.isStatic ? 1 : 0,
- 0.0,
- Math.abs( SPE.utils.randomFloat( this.maxAge._value, this.maxAge._spread ) ),
- SPE.utils.randomFloat( this.wiggle._value, this.wiggle._spread )
- );
- };
-
- SPE.Emitter.prototype._assignRotationValue = function( index ) {
- 'use strict';
-
- this.attributes.rotation.typedArray.setVec3Components( index,
- SPE.utils.getPackedRotationAxis( this.rotation._axis, this.rotation._axisSpread ),
- SPE.utils.randomFloat( this.rotation._angle, this.rotation._angleSpread ),
- this.rotation._static ? 0 : 1
- );
-
- this.attributes.rotationCenter.typedArray.setVec3( index, this.rotation._center );
- };
-
- SPE.Emitter.prototype._assignColorValue = function( index ) {
- 'use strict';
- SPE.utils.randomColorAsHex( this.attributes.color, index, this.color._value, this.color._spread );
- };
-
- SPE.Emitter.prototype._resetParticle = function( index ) {
- 'use strict';
-
- var resetFlags = this.resetFlags,
- updateFlags = this.updateFlags,
- updateCounts = this.updateCounts,
- keys = this.attributeKeys,
- key,
- updateFlag;
-
- for ( var i = this.attributeCount - 1; i >= 0; --i ) {
- key = keys[ i ];
- updateFlag = updateFlags[ key ];
-
- if ( resetFlags[ key ] === true || updateFlag === true ) {
- this._assignValue( key, index );
- this._updateAttributeUpdateRange( key, index );
-
- if ( updateFlag === true && updateCounts[ key ] === this.particleCount ) {
- updateFlags[ key ] = false;
- updateCounts[ key ] = 0.0;
- }
- else if ( updateFlag == true ) {
- ++updateCounts[ key ];
- }
- }
- }
- };
-
- SPE.Emitter.prototype._updateAttributeUpdateRange = function( attr, i ) {
- 'use strict';
-
- var ranges = this.bufferUpdateRanges[ attr ];
-
- ranges.min = Math.min( i, ranges.min );
- ranges.max = Math.max( i, ranges.max );
- };
-
- SPE.Emitter.prototype._resetBufferRanges = function() {
- 'use strict';
-
- var ranges = this.bufferUpdateRanges,
- keys = this.bufferUpdateKeys,
- i = this.bufferUpdateCount - 1,
- key;
-
- for ( i; i >= 0; --i ) {
- key = keys[ i ];
- ranges[ key ].min = Number.POSITIVE_INFINITY;
- ranges[ key ].max = Number.NEGATIVE_INFINITY;
- }
- };
-
- SPE.Emitter.prototype._onRemove = function() {
- 'use strict';
- // Reset any properties of the emitter that were set by
- // a group when it was added.
- this.particlesPerSecond = 0;
- this.attributeOffset = 0;
- this.activationIndex = 0;
- this.activeParticleCount = 0;
- this.group = null;
- this.attributes = null;
- this.paramsArray = null;
- this.age = 0.0;
- };
-
- SPE.Emitter.prototype._decrementParticleCount = function() {
- 'use strict';
- --this.activeParticleCount;
-
- // TODO:
- // - Trigger event if count === 0.
- };
-
- SPE.Emitter.prototype._incrementParticleCount = function() {
- 'use strict';
- ++this.activeParticleCount;
-
- // TODO:
- // - Trigger event if count === this.particleCount.
- };
-
- SPE.Emitter.prototype._checkParticleAges = function( start, end, params, dt ) {
- 'use strict';
- for ( var i = end - 1, index, maxAge, age, alive; i >= start; --i ) {
- index = i * 4;
-
- alive = params[ index ];
-
- if ( alive === 0.0 ) {
- continue;
- }
-
- // Increment age
- age = params[ index + 1 ];
- maxAge = params[ index + 2 ];
-
- if ( this.direction === 1 ) {
- age += dt;
-
- if ( age >= maxAge ) {
- age = 0.0;
- alive = 0.0;
- this._decrementParticleCount();
- }
- }
- else {
- age -= dt;
-
- if ( age <= 0.0 ) {
- age = maxAge;
- alive = 0.0;
- this._decrementParticleCount();
- }
- }
-
- params[ index ] = alive;
- params[ index + 1 ] = age;
-
- this._updateAttributeUpdateRange( 'params', i );
- }
- };
-
- SPE.Emitter.prototype._activateParticles = function( activationStart, activationEnd, params, dtPerParticle ) {
- 'use strict';
- var direction = this.direction;
-
- for ( var i = activationStart, index, dtValue; i < activationEnd; ++i ) {
- index = i * 4;
-
- // Don't re-activate particles that aren't dead yet.
- // if ( params[ index ] !== 0.0 && ( this.particleCount !== 1 || this.activeMultiplier !== 1 ) ) {
- // continue;
- // }
-
- if ( params[ index ] != 0.0 && this.particleCount !== 1 ) {
- continue;
- }
-
- // Increment the active particle count.
- this._incrementParticleCount();
-
- // Mark the particle as alive.
- params[ index ] = 1.0;
-
- // Reset the particle
- this._resetParticle( i );
-
- // Move each particle being activated to
- // it's actual position in time.
- //
- // This stops particles being 'clumped' together
- // when frame rates are on the lower side of 60fps
- // or not constant (a very real possibility!)
- dtValue = dtPerParticle * ( i - activationStart )
- params[ index + 1 ] = direction === -1 ? params[ index + 2 ] - dtValue : dtValue;
-
- this._updateAttributeUpdateRange( 'params', i );
- }
- };
-
- /**
- * Simulates one frame's worth of particles, updating particles
- * that are already alive, and marking ones that are currently dead
- * but should be alive as alive.
- *
- * If the emitter is marked as static, then this function will do nothing.
- *
- * @param {Number} dt The number of seconds to simulate (deltaTime)
- */
- SPE.Emitter.prototype.tick = function( dt ) {
- 'use strict';
-
- if ( this.isStatic ) {
- return;
- }
-
- if ( this.paramsArray === null ) {
- this.paramsArray = this.attributes.params.typedArray.array;
- }
-
- var start = this.attributeOffset,
- end = start + this.particleCount,
- params = this.paramsArray, // vec3( alive, age, maxAge, wiggle )
- ppsDt = this.particlesPerSecond * this.activeMultiplier * dt,
- activationIndex = this.activationIndex;
-
- // Reset the buffer update indices.
- this._resetBufferRanges();
-
- // Increment age for those particles that are alive,
- // and kill off any particles whose age is over the limit.
- this._checkParticleAges( start, end, params, dt );
-
- // If the emitter is dead, reset the age of the emitter to zero,
- // ready to go again if required
- if ( this.alive === false ) {
- this.age = 0.0;
- return;
- }
-
- // If the emitter has a specified lifetime and we've exceeded it,
- // mark the emitter as dead.
- if ( this.duration !== null && this.age > this.duration ) {
- this.alive = false;
- this.age = 0.0;
- return;
- }
-
-
- var activationStart = this.particleCount === 1 ? activationIndex : ( activationIndex | 0 ),
- activationEnd = Math.min( activationStart + ppsDt, this.activationEnd ),
- activationCount = activationEnd - this.activationIndex | 0,
- dtPerParticle = activationCount > 0 ? dt / activationCount : 0;
-
- this._activateParticles( activationStart, activationEnd, params, dtPerParticle );
-
- // Move the activation window forward, soldier.
- this.activationIndex += ppsDt;
-
- if ( this.activationIndex > end ) {
- this.activationIndex = start;
- }
-
-
- // Increment the age of the emitter.
- this.age += dt;
- };
-
- /**
- * Resets all the emitter's particles to their start positions
- * and marks the particles as dead if the `force` argument is
- * true.
- *
- * @param {Boolean} [force=undefined] If true, all particles will be marked as dead instantly.
- * @return {Emitter} This emitter instance.
- */
- SPE.Emitter.prototype.reset = function( force ) {
- 'use strict';
-
- this.age = 0.0;
- this.alive = false;
-
- if ( force === true ) {
- var start = this.attributeOffset,
- end = start + this.particleCount,
- array = this.paramsArray,
- attr = this.attributes.params.bufferAttribute;
-
- for ( var i = end - 1, index; i >= start; --i ) {
- index = i * 4;
-
- array[ index ] = 0.0;
- array[ index + 1 ] = 0.0;
- }
-
- attr.updateRange.offset = 0;
- attr.updateRange.count = -1;
- attr.needsUpdate = true;
- }
-
- return this;
- };
-
- /**
- * Enables the emitter. If not already enabled, the emitter
- * will start emitting particles.
- *
- * @return {Emitter} This emitter instance.
- */
- SPE.Emitter.prototype.enable = function() {
- 'use strict';
- this.alive = true;
- return this;
- };
-
- /**
- * Disables th emitter, but does not instantly remove it's
- * particles fromt the scene. When called, the emitter will be
- * 'switched off' and just stop emitting. Any particle's alive will
- * be allowed to finish their lifecycle.
- *
- * @return {Emitter} This emitter instance.
- */
- SPE.Emitter.prototype.disable = function() {
- 'use strict';
-
- this.alive = false;
- return this;
- };
-
- /**
- * Remove this emitter from it's parent group (if it has been added to one).
- * Delgates to SPE.group.prototype.removeEmitter().
- *
- * When called, all particle's belonging to this emitter will be instantly
- * removed from the scene.
- *
- * @return {Emitter} This emitter instance.
- *
- * @see SPE.Group.prototype.removeEmitter
- */
- SPE.Emitter.prototype.remove = function() {
- 'use strict';
- if ( this.group !== null ) {
- this.group.removeEmitter( this );
- }
- else {
- console.error( 'Emitter does not belong to a group, cannot remove.' );
- }
-
- return this;
- };
-
- /***/ })
- /******/ ]);
|