diff --git a/lib/SPE.js b/lib/SPE.js index 49c8239..89301ef 100644 --- a/lib/SPE.js +++ b/lib/SPE.js @@ -1,5 +1,5 @@ -/* shader-particle-engine 1.0.5 - * +/* 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). * @@ -20,7 +20,7 @@ * @type {Object} * @namespace */ -var SPE = { + var SPE = { /** * A map of supported distribution types used @@ -52,6 +52,12 @@ var SPE = { * @type {Number} */ DISC: 3, + + /** + * Values will be distributed along a line. + * @type {Number} + */ + LINE: 4 }, @@ -88,6 +94,7 @@ else if ( typeof exports !== 'undefined' && typeof module !== 'undefined' ) { module.exports = SPE; } + /** * A helper class for TypedArrays. * @@ -430,19 +437,19 @@ SPE.TypedArrayHelper.prototype.getComponentValueAtIndex = function( index ) { * @param {Function=} arrayType A reference to a TypedArray constructor. Defaults to Float32Array if none provided. */ SPE.ShaderAttribute = function( type, dynamicBuffer, arrayType ) { - 'use strict'; + 'use strict'; - var typeMap = SPE.ShaderAttribute.typeSizeMap; + 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.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; + this.updateMin = 0; + this.updateMax = 0; }; SPE.ShaderAttribute.constructor = SPE.ShaderAttribute; @@ -452,47 +459,47 @@ SPE.ShaderAttribute.constructor = SPE.ShaderAttribute; * @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 + /** + * 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 }; /** @@ -503,10 +510,10 @@ SPE.ShaderAttribute.typeSizeMap = { * @param {Number} max The end of the range to mark as needing an update. */ SPE.ShaderAttribute.prototype.setUpdateRange = function( min, max ) { - 'use strict'; + 'use strict'; - this.updateMin = Math.min( min * this.componentSize, this.updateMin * this.componentSize ); - this.updateMax = Math.max( max * this.componentSize, this.updateMax * this.componentSize ); + this.updateMin = Math.min( min * this.componentSize, this.updateMin * this.componentSize ); + this.updateMax = Math.max( max * this.componentSize, this.updateMax * this.componentSize ); }; /** @@ -514,16 +521,16 @@ SPE.ShaderAttribute.prototype.setUpdateRange = function( min, max ) { * updating. Also marks the attribute as needing an update. */ SPE.ShaderAttribute.prototype.flagUpdate = function() { - 'use strict'; + 'use strict'; - var attr = this.bufferAttribute, - range = attr.updateRange; + 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; + 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; }; @@ -532,15 +539,17 @@ SPE.ShaderAttribute.prototype.flagUpdate = function() { * Reset the index update counts for this attribute */ SPE.ShaderAttribute.prototype.resetUpdateRange = function() { - 'use strict'; + 'use strict'; - this.updateMin = 0; - this.updateMax = 0; + this.updateMin = 0; + this.updateMax = 0; }; SPE.ShaderAttribute.prototype.resetDynamic = function() { - 'use strict'; - this.bufferAttribute.dynamic = this.dynamicBuffer; + 'use strict'; + this.bufferAttribute.usage = this.dynamicBuffer ? + THREE.DynamicDrawUsage : + THREE.StaticDrawUsage; }; /** @@ -549,23 +558,28 @@ SPE.ShaderAttribute.prototype.resetDynamic = function() { * @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'; + 'use strict'; - this.typedArray.splice( start, end ); + this.typedArray.splice( start, end ); - // Reset the reference to the attribute's typed array - // since it has probably changed. - this.forceUpdateAll(); + // Reset the reference to the attribute's typed array + // since it has probably changed. + this.forceUpdateAll(); }; SPE.ShaderAttribute.prototype.forceUpdateAll = function() { - 'use strict'; + '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.array = this.typedArray.array; - this.bufferAttribute.updateRange.offset = 0; - this.bufferAttribute.updateRange.count = -1; - this.bufferAttribute.dynamic = false; - this.bufferAttribute.needsUpdate = true; + this.bufferAttribute.usage = THREE.StaticDrawUsage; + this.bufferAttribute.needsUpdate = true; }; /** @@ -578,23 +592,23 @@ SPE.ShaderAttribute.prototype.forceUpdateAll = function() { * @param {Number} size The size of the typed array to create or update to. */ SPE.ShaderAttribute.prototype._ensureTypedArray = function( size ) { - 'use strict'; + '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; - } + // 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 ); - } + // 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 ); - } + // 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 ); + } }; @@ -608,22 +622,35 @@ SPE.ShaderAttribute.prototype._ensureTypedArray = function( size ) { * @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'; + 'use strict'; - // Make sure the typedArray is present and correct. - this._ensureTypedArray( size ); + // 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; - this.bufferAttribute.needsUpdate = true; - return; - } + // 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; - this.bufferAttribute = new THREE.BufferAttribute( this.typedArray.array, this.componentSize ); - this.bufferAttribute.dynamic = this.dynamicBuffer; + // 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; }; /** @@ -631,15 +658,16 @@ SPE.ShaderAttribute.prototype._createBufferAttribute = function( size ) { * @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'; + 'use strict'; - if ( this.typedArray === null ) { - return 0; - } + if ( this.typedArray === null ) { + return 0; + } - return this.typedArray.array.length; + return this.typedArray.array.length; }; + SPE.shaderChunks = { // Register color-packing define statements. defines: [ @@ -651,7 +679,7 @@ SPE.shaderChunks = { uniforms: [ 'uniform float deltaTime;', 'uniform float runTime;', - 'uniform sampler2D texture;', + 'uniform sampler2D tex;', 'uniform vec4 textureAnimation;', 'uniform float scale;', ].join( '\n' ), @@ -892,193 +920,193 @@ SPE.shaderChunks = { ' #endif', '', - ' vec4 rotatedTexture = texture2D( texture, vUv );', + ' 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, - - - 'vec4 mvPosition;', - '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 - // + 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', - // Get forces & position - ' vec3 vel = getVelocity( age );', - ' vec3 accel = getAcceleration( age );', - ' vec3 force = vec3( 0.0 );', - ' vec3 pos = vec3( position );', + // 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', - // Calculate the required drag to apply to the forces. - ' float drag = 1.0 - (positionInTime * 0.5) * acceleration.w;', + // + // Write values + // - // Integrate forces... - ' force += vel;', - ' force *= drag;', - ' force += accel * age;', - ' pos += force;', + // 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, - // Wiggly wiggly wiggle! - ' #ifdef SHOULD_WIGGLE_PARTICLES', - ' pos.x += wiggleSin;', - ' pos.y += wiggleCos;', - ' pos.z += wiggleSin;', - ' #endif', + '}' + ].join( '\n' ), + fragment: [ + SPE.shaderChunks.uniforms, - // Rotate the emitter around it's central point - ' #ifdef SHOULD_ROTATE_PARTICLES', - ' pos = getRotation( pos, positionInTime );', - ' #endif', + THREE.ShaderChunk.common, + THREE.ShaderChunk.fog_pars_fragment, + THREE.ShaderChunk.logdepthbuf_pars_fragment, - // Convert pos to a world-space value - ' vec4 mvPos = mvPosition = modelViewMatrix * vec4( pos, 1.0 );', + SPE.shaderChunks.varyings, - // Determine point size. - ' highp float pointSize = getFloatOverLifetime( positionInTime, size ) * isAlive;', - - // Determine perspective - ' #ifdef HAS_PERSPECTIVE', - ' float perspective = scale / length( mvPos.xyz );', - ' #else', - ' float perspective = 1.0;', - ' #endif', + SPE.shaderChunks.branchAvoidanceFunctions, - // Apply perpective to pointSize value - ' float pointSizePerspective = pointSize * perspective;', + 'void main() {', + ' vec3 outgoingLight = vColor.xyz;', + ' ', + ' #ifdef ALPHATEST', + ' if ( vColor.w < float(ALPHATEST) ) discard;', + ' #endif', + SPE.shaderChunks.rotateTexture, - // - // 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', + THREE.ShaderChunk.logdepthbuf_fragment, - // - // Write values - // - - // Set PointSize according to size at current point in time. - ' gl_PointSize = pointSizePerspective;', - ' gl_Position = projectionMatrix * mvPos;', - - 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;', + ' outgoingLight = vColor.xyz * rotatedTexture.xyz;', ' gl_FragColor = vec4( outgoingLight.xyz, rotatedTexture.w * vColor.w );', - THREE.ShaderChunk.fog_fragment, + THREE.ShaderChunk.fog_fragment, - '}' - ].join( '\n' ) + '}' + ].join( '\n' ) }; + /** * A bunch of utility functions used throughout the library. * @namespace @@ -1585,6 +1613,33 @@ SPE.utils = { }; }() ), + /** + * 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. @@ -1785,6 +1840,7 @@ SPE.utils = { }() ) }; + /** * An SPE.Group instance. * @typedef {Object} Group @@ -1906,7 +1962,7 @@ SPE.Group = function( options ) { // Map of uniforms to be applied to the ShaderMaterial instance. this.uniforms = { - texture: { + tex: { type: 't', value: this.texture }, @@ -1921,7 +1977,7 @@ SPE.Group = function( options ) { }, fogColor: { type: 'c', - value: new THREE.Color() + value: this.fog ? new THREE.Color() : null }, fogNear: { type: 'f', @@ -2281,7 +2337,11 @@ SPE.Group.prototype.getFromPool = function() { return pool.pop(); } else if ( createNew ) { - return new SPE.Emitter( this._poolCreationSettings ); + var emitter = new SPE.Emitter( this._poolCreationSettings ); + + this.addEmitter( emitter ); + + return emitter; } return null; @@ -2534,6 +2594,7 @@ SPE.Group.prototype.dispose = function() { return this; }; + /** * An SPE.Emitter instance. * @typedef {Object} Emitter @@ -2581,10 +2642,12 @@ SPE.Group.prototype.dispose = function() { * @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. @@ -2712,7 +2775,7 @@ SPE.Emitter = function( options ) { this.uuid = THREE.Math.generateUUID(); - this.type = utils.ensureTypedArg( options.type.value, types.NUMBER, SPE.distributions.BOX ); + 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. @@ -3065,6 +3128,10 @@ SPE.Emitter.prototype._assignPositionValue = function( index ) { 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; } }; @@ -3131,6 +3198,10 @@ SPE.Emitter.prototype._assignForceValue = function( index, attrName ) { prop._spread.x ); break; + + case distributions.LINE: + utils.randomVector3OnLine( this.attributes[ attrName ], index, value, spread ); + break; } if ( attrName === 'acceleration' ) { @@ -3524,4 +3595,4 @@ SPE.Emitter.prototype.remove = function() { } return this; -}; +}; \ No newline at end of file diff --git a/lib/SPE.min.js b/lib/SPE.min.js index 44e9e57..6cfdcc8 100644 --- a/lib/SPE.min.js +++ b/lib/SPE.min.js @@ -1,98 +1,52 @@ -/* shader-particle-engine 1.0.5 +/* 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.) */ -var SPE={distributions:{BOX:1,SPHERE:2,DISC:3,},valueOverLifetimeLength:4};if(typeof define==='function'&&define.amd){define('spe',SPE)} -else if(typeof exports!=='undefined'&&typeof module!=='undefined'){module.exports=SPE} -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;SPE.TypedArrayHelper.prototype.setSize=function(size,noComponentMultiply){'use strict';var currentArraySize=this.array.length;if(!noComponentMultiply){size=size*this.componentSize} -if(sizecurrentArraySize){return this.grow(size)} -else{console.info('TypedArray is already of size:',size+'.','Will not resize.')}};SPE.TypedArrayHelper.prototype.shrink=function(size){'use strict';this.array=this.array.subarray(0,size);this.size=size;return this};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};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=end){data.push(array[i])}} -this.setFromArray(0,data);return this};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=0;--i){if(typeof arg[i]!==type){return defaultValue}} -return arg} -return this.ensureTypedArg(arg,type,defaultValue)},ensureInstanceOf:function(arg,instance,defaultValue){'use strict';if(instance!==undefined&&arg instanceof instance){return arg} -else{return defaultValue}},ensureArrayInstanceOf:function(arg,instance,defaultValue){'use strict';if(Array.isArray(arg)){for(var i=arg.length-1;i>=0;--i){if(instance!==undefined&&arg[i]instanceof instance===!1){return defaultValue}} -return arg} -return this.ensureInstanceOf(arg,instance,defaultValue)},ensureValueOverLifetimeCompliance:function(property,minLength,maxLength){'use strict';minLength=minLength||3;maxLength=maxLength||3;if(Array.isArray(property._value)===!1){property._value=[property._value]} -if(Array.isArray(property._spread)===!1){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)}},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-epsilon){result=-result} -return result},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)}},lerp:function(start,end,delta){'use strict';return start+((end-start)*delta)},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},arrayValuesAreEqual:function(array){'use strict';for(var i=0;i1||this.textureFrames.y>1};this.attributes={position:new SPE.ShaderAttribute('v3',!0),acceleration:new SPE.ShaderAttribute('v4',!0),velocity:new SPE.ShaderAttribute('v3',!0),rotation:new SPE.ShaderAttribute('v4',!0),rotationCenter:new SPE.ShaderAttribute('v3',!0),params:new SPE.ShaderAttribute('v4',!0),size:new SPE.ShaderAttribute('v4',!0),angle:new SPE.ShaderAttribute('v4',!0),color:new SPE.ShaderAttribute('v4',!0),opacity:new SPE.ShaderAttribute('v4',!0)};this.attributeKeys=Object.keys(this.attributes);this.attributeCount=this.attributeKeys.length;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});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];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=!0};SPE.Group.prototype._applyAttributesToGeometry=function(){'use strict';var attributes=this.attributes,geometry=this.geometry,geometryAttributes=geometry.attributes,attribute,geometryAttribute;for(var attr in attributes){if(attributes.hasOwnProperty(attr)){attribute=attributes[attr];geometryAttribute=geometryAttributes[attr];if(geometryAttribute){geometryAttribute.array=attribute.typedArray.array} -else{geometry.addAttribute(attr,attribute.bufferAttribute)} -attribute.bufferAttribute.needsUpdate=!0}} -this.geometry.setDrawRange(0,this.particleCount)};SPE.Group.prototype.addEmitter=function(emitter){'use strict';if(emitter instanceof SPE.Emitter===!1){console.error('`emitter` argument must be instance of SPE.Emitter. Was provided with:',emitter);return} -else if(this.emitterIDs.indexOf(emitter.uuid)>-1){console.error('Emitter already exists in this group. Will not add again.');return} -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;this.particleCount=end;if(this.maxParticleCount!==null&&this.particleCount>this.maxParticleCount){console.warn('SPE.Group: maxParticleCount exceeded. Requesting',this.particleCount,'particles, can support only',this.maxParticleCount)} -emitter._calculatePPSValue(emitter.maxAge._value+emitter.maxAge._spread);emitter._setBufferUpdateRanges(this.attributeKeys);emitter._setAttributeOffset(start);emitter.group=this;emitter.attributes=this.attributes;for(var attr in attributes){if(attributes.hasOwnProperty(attr)){attributes[attr]._createBufferAttribute(this.maxParticleCount!==null?this.maxParticleCount:this.particleCount)}} -for(var i=start;i1){for(var i=0;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()}};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;this._updateUniforms(deltaTime);this._resetBufferRanges();if(numEmitters===0&&this._attributesNeedRefresh===!1&&this._attributesNeedDynamicReset===!1){return} -for(var i=0,emitter;i=0;--i){attrs[keys[i]].resetDynamic()} -this._attributesNeedDynamicReset=!1} -if(this._attributesNeedRefresh===!0){i=this.attributeCount-1;for(i;i>=0;--i){attrs[keys[i]].forceUpdateAll()} -this._attributesNeedRefresh=!1;this._attributesNeedDynamicReset=!0}};SPE.Group.prototype.dispose=function(){'use strict';this.geometry.dispose();this.material.dispose();return this};SPE.Emitter=function(options){'use strict';var utils=SPE.utils,types=utils.types,lifetimeLength=SPE.valueOverLifetimeLength;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);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,!1),_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,!1)};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,!1)};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,!1)};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,!1),_center:utils.ensureInstanceOf(options.rotation.center,THREE.Vector3,this.position._value.clone()),_randomise:utils.ensureTypedArg(options.position.randomise,types.BOOLEAN,!1)};this.maxAge={_value:utils.ensureTypedArg(options.maxAge.value,types.NUMBER,2),_spread:utils.ensureTypedArg(options.maxAge.spread,types.NUMBER,0)};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,!1)};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,!1)};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,!1)};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,!1)};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,!1);this.activeMultiplier=utils.ensureTypedArg(options.activeMultiplier,types.NUMBER,1);this.direction=utils.ensureTypedArg(options.direction,types.NUMBER,1);this.alive=utils.ensureTypedArg(options.alive,types.BOOLEAN,!0);this.particlesPerSecond=0;this.activationIndex=0;this.attributeOffset=0;this.attributeEnd=0;this.age=0.0;this.activeParticleCount=0.0;this.group=null;this.attributes=null;this.paramsArray=null;this.resetFlags={position:utils.ensureTypedArg(options.position.randomise,types.BOOLEAN,!1)||utils.ensureTypedArg(options.radius.randomise,types.BOOLEAN,!1),velocity:utils.ensureTypedArg(options.velocity.randomise,types.BOOLEAN,!1),acceleration:utils.ensureTypedArg(options.acceleration.randomise,types.BOOLEAN,!1)||utils.ensureTypedArg(options.drag.randomise,types.BOOLEAN,!1),rotation:utils.ensureTypedArg(options.rotation.randomise,types.BOOLEAN,!1),rotationCenter:utils.ensureTypedArg(options.rotation.randomise,types.BOOLEAN,!1),size:utils.ensureTypedArg(options.size.randomise,types.BOOLEAN,!1),color:utils.ensureTypedArg(options.color.randomise,types.BOOLEAN,!1),opacity:utils.ensureTypedArg(options.opacity.randomise,types.BOOLEAN,!1),angle:utils.ensureTypedArg(options.angle.randomise,types.BOOLEAN,!1)};this.updateFlags={};this.updateCounts={};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]]=!1;this._createGetterSetters(this[i],i)}} -this.bufferUpdateRanges={};this.attributeKeys=null;this.attributeCount=0;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=!0;self.updateCounts.rotationCenter=0.0} -else if(prop==='_randomise'){self.resetFlags[mapName]=value} -else{self.updateFlags[mapName]=!0;self.updateCounts[mapName]=0.0} -self.group._updateDefines();this[prop]=value;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;if(this.duration){this.particlesPerSecond=particleCount/(groupMaxAge=0;--i){key=keys[i];updateFlag=updateFlags[key];if(resetFlags[key]===!0||updateFlag===!0){this._assignValue(key,index);this._updateAttributeUpdateRange(key,index);if(updateFlag===!0&&updateCounts[key]===this.particleCount){updateFlags[key]=!1;updateCounts[key]=0.0} -else if(updateFlag==!0){++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';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};SPE.Emitter.prototype._incrementParticleCount=function(){'use strict';++this.activeParticleCount};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} -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;ithis.duration){this.alive=!1;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);this.activationIndex+=ppsDt;if(this.activationIndex>end){this.activationIndex=start} -this.age+=dt};SPE.Emitter.prototype.reset=function(force){'use strict';this.age=0.0;this.alive=!1;if(force===!0){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=!0} -return this};SPE.Emitter.prototype.enable=function(){'use strict';this.alive=!0;return this};SPE.Emitter.prototype.disable=function(){'use strict';this.alive=!1;return this};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} +var SPE={distributions:{BOX:1,SPHERE:2,DISC:3,LINE:4},valueOverLifetimeLength:4};"function"==typeof define&&define.amd?define("spe",SPE):"undefined"!=typeof exports&&"undefined"!=typeof module&&(module.exports=SPE),SPE.TypedArrayHelper=function(a,b,c,d){"use strict";this.componentSize=c||1,this.size=b||1,this.TypedArrayConstructor=a||Float32Array,this.array=new a(b*this.componentSize),this.indexOffset=d||0},SPE.TypedArrayHelper.constructor=SPE.TypedArrayHelper,SPE.TypedArrayHelper.prototype.setSize=function(a,b){"use strict";var c=this.array.length;return b||(a*=this.componentSize),ac?this.grow(a):void console.info("TypedArray is already of size:",a+".","Will not resize.")},SPE.TypedArrayHelper.prototype.shrink=function(a){"use strict";return this.array=this.array.subarray(0,a),this.size=a,this},SPE.TypedArrayHelper.prototype.grow=function(a){"use strict";var b=this.array,c=new this.TypedArrayConstructor(a);return c.set(b),this.array=c,this.size=a,this}, +SPE.TypedArrayHelper.prototype.splice=function(a,b){"use strict";a*=this.componentSize,b*=this.componentSize;for(var c=[],d=this.array,e=d.length,f=0;f=b)&&c.push(d[f]);return this.setFromArray(0,c),this},SPE.TypedArrayHelper.prototype.setFromArray=function(a,b){"use strict";var c=b.length,d=a+c;return d>this.array.length?this.grow(d):d=81&&(this.bufferAttribute.count=this.bufferAttribute.array.length/this.bufferAttribute.itemSize),void(this.bufferAttribute.needsUpdate=!0);this.bufferAttribute=new THREE.BufferAttribute(this.typedArray.array,this.componentSize),this.bufferAttribute.usage=this.dynamicBuffer?THREE.DynamicDrawUsage:THREE.StaticDrawUsage},SPE.ShaderAttribute.prototype.getLength=function(){"use strict";return null===this.typedArray?0:this.typedArray.array.length},SPE.shaderChunks={ +defines:["#define PACKED_COLOR_SIZE 256.0","#define PACKED_COLOR_DIVISOR 255.0"].join("\n"),uniforms:["uniform float deltaTime;","uniform float runTime;","uniform sampler2D tex;","uniform vec4 textureAnimation;","uniform float scale;"].join("\n"),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"), +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);","}","float and(float a, float b) {"," return a * b;","}","float or(float a, float b) {"," return min(a + b, 1.0);","}"].join("\n"),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;"," 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:["#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"), +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",""," #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() {"," 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"," vec3 vel = getVelocity( age );"," vec3 accel = getAcceleration( age );"," vec3 force = vec3( 0.0 );"," vec3 pos = vec3( position );"," float drag = 1.0 - (positionInTime * 0.5) * acceleration.w;"," force += vel;"," force *= drag;"," force += accel * age;"," pos += force;"," #ifdef SHOULD_WIGGLE_PARTICLES"," pos.x += wiggleSin;"," pos.y += wiggleCos;"," pos.z += wiggleSin;"," #endif"," #ifdef SHOULD_ROTATE_PARTICLES"," pos = getRotation( pos, positionInTime );"," #endif"," vec4 mvPosition = modelViewMatrix * vec4( pos, 1.0 );"," highp float pointSize = getFloatOverLifetime( positionInTime, size ) * isAlive;"," #ifdef HAS_PERSPECTIVE"," float perspective = scale / length( mvPosition.xyz );"," #else"," float perspective = 1.0;"," #endif"," float pointSizePerspective = pointSize * perspective;"," #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 );"," vColor = vec4( c, o );"," #ifdef SHOULD_ROTATE_TEXTURE"," vAngle = isAlive * getFloatOverLifetime( positionInTime, angle );"," #endif"," #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"," 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")},SPE.utils={types:{BOOLEAN:"boolean",STRING:"string",NUMBER:"number",OBJECT:"object"},ensureTypedArg:function(a,b,c){"use strict";return typeof a===b?a:c},ensureArrayTypedArg:function(a,b,c){"use strict";if(Array.isArray(a)){for(var d=a.length-1;d>=0;--d)if(typeof a[d]!==b)return c;return a}return this.ensureTypedArg(a,b,c)},ensureInstanceOf:function(a,b,c){"use strict" +;return void 0!==b&&a instanceof b?a:c},ensureArrayInstanceOf:function(a,b,c){"use strict";if(Array.isArray(a)){for(var d=a.length-1;d>=0;--d)if(void 0!==b&&a[d]instanceof b==!1)return c;return a}return this.ensureInstanceOf(a,b,c)},ensureValueOverLifetimeCompliance:function(a,b,c){"use strict";b=b||3,c=c||3,!1===Array.isArray(a._value)&&(a._value=[a._value]),!1===Array.isArray(a._spread)&&(a._spread=[a._spread]);var d=this.clamp(a._value.length,b,c),e=this.clamp(a._spread.length,b,c),f=Math.max(d,e);a._value.length!==f&&(a._value=this.interpolateArray(a._value,f)),a._spread.length!==f&&(a._spread=this.interpolateArray(a._spread,f))},interpolateArray:function(a,b){"use strict";for(var c=a.length,d=["function"==typeof a[0].clone?a[0].clone():a[0]],e=(c-1)/(b-1),f=1;f-c&&(d=-d),d},lerpTypeAgnostic:function(a,b,c){"use strict";var d,e=this.types;return typeof a===e.NUMBER&&typeof b===e.NUMBER?a+(b-a)*c:a instanceof THREE.Vector2&&b instanceof THREE.Vector2?(d=a.clone(),d.x=this.lerp(a.x,b.x,c),d.y=this.lerp(a.y,b.y,c),d):a instanceof THREE.Vector3&&b instanceof THREE.Vector3?(d=a.clone(),d.x=this.lerp(a.x,b.x,c),d.y=this.lerp(a.y,b.y,c),d.z=this.lerp(a.z,b.z,c),d):a instanceof THREE.Vector4&&b instanceof THREE.Vector4?(d=a.clone(),d.x=this.lerp(a.x,b.x,c),d.y=this.lerp(a.y,b.y,c),d.z=this.lerp(a.z,b.z,c),d.w=this.lerp(a.w,b.w,c),d):a instanceof THREE.Color&&b instanceof THREE.Color?(d=a.clone(),d.r=this.lerp(a.r,b.r,c),d.g=this.lerp(a.g,b.g,c),d.b=this.lerp(a.b,b.b,c),d):void console.warn("Invalid argument types, or argument types do not match:",a,b)},lerp:function(a,b,c){"use strict";return a+(b-a)*c}, +roundToNearestMultiple:function(a,b){"use strict";var c=0;return 0===b?a:(c=Math.abs(a)%b,0===c?a:a<0?-(Math.abs(a)-c):a+b-c)},arrayValuesAreEqual:function(a){"use strict";for(var b=0;b1||this.textureFrames.y>1},this.attributes={position:new SPE.ShaderAttribute("v3",!0),acceleration:new SPE.ShaderAttribute("v4",!0),velocity:new SPE.ShaderAttribute("v3",!0),rotation:new SPE.ShaderAttribute("v4",!0),rotationCenter:new SPE.ShaderAttribute("v3",!0),params:new SPE.ShaderAttribute("v4",!0),size:new SPE.ShaderAttribute("v4",!0),angle:new SPE.ShaderAttribute("v4",!0),color:new SPE.ShaderAttribute("v4",!0),opacity:new SPE.ShaderAttribute("v4",!0)},this.attributeKeys=Object.keys(this.attributes),this.attributeCount=this.attributeKeys.length,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}), +this.geometry=new THREE.BufferGeometry,this.mesh=new THREE.Points(this.geometry,this.material),null===this.maxParticleCount&&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 a,b=this.emitters,c=b.length-1,d=this.defines;for(c;c>=0;--c)a=b[c],d.SHOULD_CALCULATE_SPRITE||(d.SHOULD_ROTATE_TEXTURE=d.SHOULD_ROTATE_TEXTURE||!!Math.max(Math.max.apply(null,a.angle.value),Math.max.apply(null,a.angle.spread))),d.SHOULD_ROTATE_PARTICLES=d.SHOULD_ROTATE_PARTICLES||!!Math.max(a.rotation.angle,a.rotation.angleSpread),d.SHOULD_WIGGLE_PARTICLES=d.SHOULD_WIGGLE_PARTICLES||!!Math.max(a.wiggle.value,a.wiggle.spread);this.material.needsUpdate=!0},SPE.Group.prototype._applyAttributesToGeometry=function(){"use strict";var a,b,c=this.attributes,d=this.geometry,e=d.attributes;for(var f in c)c.hasOwnProperty(f)&&(a=c[f],b=e[f], +b?b.array=a.typedArray.array:d.addAttribute(f,a.bufferAttribute),a.bufferAttribute.needsUpdate=!0);this.geometry.setDrawRange(0,this.particleCount)},SPE.Group.prototype.addEmitter=function(a){"use strict";if(a instanceof SPE.Emitter==!1)return void console.error("`emitter` argument must be instance of SPE.Emitter. Was provided with:",a);if(this.emitterIDs.indexOf(a.uuid)>-1)return void console.error("Emitter already exists in this group. Will not add again.");if(null!==a.group)return void console.error("Emitter already belongs to another group. Will not add to requested group.");var b=this.attributes,c=this.particleCount,d=c+a.particleCount;this.particleCount=d,null!==this.maxParticleCount&&this.particleCount>this.maxParticleCount&&console.warn("SPE.Group: maxParticleCount exceeded. Requesting",this.particleCount,"particles, can support only",this.maxParticleCount),a._calculatePPSValue(a.maxAge._value+a.maxAge._spread),a._setBufferUpdateRanges(this.attributeKeys), +a._setAttributeOffset(c),a.group=this,a.attributes=this.attributes;for(var e in b)b.hasOwnProperty(e)&&b[e]._createBufferAttribute(null!==this.maxParticleCount?this.maxParticleCount:this.particleCount);for(var f=c;f1)for(var c=0;c=0;--b)c[a[b]].resetUpdateRange()},SPE.Group.prototype._updateBuffers=function(a){"use strict";var b,c,d,e=this.attributeKeys,f=this.attributeCount-1,g=this.attributes,h=a.bufferUpdateRanges;for(f;f>=0;--f)b=e[f],c=h[b],d=g[b],d.setUpdateRange(c.min,c.max),d.flagUpdate()},SPE.Group.prototype.tick=function(a){"use strict";var b,c=this.emitters,d=c.length,e=a||this.fixedTimeStep,f=this.attributeKeys,g=this.attributes;if(this._updateUniforms(e),this._resetBufferRanges(),0!==d||!1!==this._attributesNeedRefresh||!1!==this._attributesNeedDynamicReset){for(var h,b=0;b=0;--b)g[f[b]].resetDynamic();this._attributesNeedDynamicReset=!1}if(!0===this._attributesNeedRefresh){for(b=this.attributeCount-1;b>=0;--b)g[f[b]].forceUpdateAll();this._attributesNeedRefresh=!1,this._attributesNeedDynamicReset=!0}}}, +SPE.Group.prototype.dispose=function(){"use strict";return this.geometry.dispose(),this.material.dispose(),this},SPE.Emitter=function(a){"use strict";var b=SPE.utils,c=b.types,d=SPE.valueOverLifetimeLength;a=b.ensureTypedArg(a,c.OBJECT,{}),a.position=b.ensureTypedArg(a.position,c.OBJECT,{}),a.velocity=b.ensureTypedArg(a.velocity,c.OBJECT,{}),a.acceleration=b.ensureTypedArg(a.acceleration,c.OBJECT,{}),a.radius=b.ensureTypedArg(a.radius,c.OBJECT,{}),a.drag=b.ensureTypedArg(a.drag,c.OBJECT,{}),a.rotation=b.ensureTypedArg(a.rotation,c.OBJECT,{}),a.color=b.ensureTypedArg(a.color,c.OBJECT,{}),a.opacity=b.ensureTypedArg(a.opacity,c.OBJECT,{}),a.size=b.ensureTypedArg(a.size,c.OBJECT,{}),a.angle=b.ensureTypedArg(a.angle,c.OBJECT,{}),a.wiggle=b.ensureTypedArg(a.wiggle,c.OBJECT,{}),a.maxAge=b.ensureTypedArg(a.maxAge,c.OBJECT,{}),a.onParticleSpawn&&console.warn("onParticleSpawn has been removed. Please set properties directly to alter values at runtime."),this.uuid=THREE.Math.generateUUID(), +this.type=b.ensureTypedArg(a.type,c.NUMBER,SPE.distributions.BOX),this.position={_value:b.ensureInstanceOf(a.position.value,THREE.Vector3,new THREE.Vector3),_spread:b.ensureInstanceOf(a.position.spread,THREE.Vector3,new THREE.Vector3),_spreadClamp:b.ensureInstanceOf(a.position.spreadClamp,THREE.Vector3,new THREE.Vector3),_distribution:b.ensureTypedArg(a.position.distribution,c.NUMBER,this.type),_randomise:b.ensureTypedArg(a.position.randomise,c.BOOLEAN,!1),_radius:b.ensureTypedArg(a.position.radius,c.NUMBER,10),_radiusScale:b.ensureInstanceOf(a.position.radiusScale,THREE.Vector3,new THREE.Vector3(1,1,1)),_distributionClamp:b.ensureTypedArg(a.position.distributionClamp,c.NUMBER,0)},this.velocity={_value:b.ensureInstanceOf(a.velocity.value,THREE.Vector3,new THREE.Vector3),_spread:b.ensureInstanceOf(a.velocity.spread,THREE.Vector3,new THREE.Vector3),_distribution:b.ensureTypedArg(a.velocity.distribution,c.NUMBER,this.type),_randomise:b.ensureTypedArg(a.position.randomise,c.BOOLEAN,!1)}, +this.acceleration={_value:b.ensureInstanceOf(a.acceleration.value,THREE.Vector3,new THREE.Vector3),_spread:b.ensureInstanceOf(a.acceleration.spread,THREE.Vector3,new THREE.Vector3),_distribution:b.ensureTypedArg(a.acceleration.distribution,c.NUMBER,this.type),_randomise:b.ensureTypedArg(a.position.randomise,c.BOOLEAN,!1)},this.drag={_value:b.ensureTypedArg(a.drag.value,c.NUMBER,0),_spread:b.ensureTypedArg(a.drag.spread,c.NUMBER,0),_randomise:b.ensureTypedArg(a.position.randomise,c.BOOLEAN,!1)},this.wiggle={_value:b.ensureTypedArg(a.wiggle.value,c.NUMBER,0),_spread:b.ensureTypedArg(a.wiggle.spread,c.NUMBER,0)},this.rotation={_axis:b.ensureInstanceOf(a.rotation.axis,THREE.Vector3,new THREE.Vector3(0,1,0)),_axisSpread:b.ensureInstanceOf(a.rotation.axisSpread,THREE.Vector3,new THREE.Vector3),_angle:b.ensureTypedArg(a.rotation.angle,c.NUMBER,0),_angleSpread:b.ensureTypedArg(a.rotation.angleSpread,c.NUMBER,0),_static:b.ensureTypedArg(a.rotation.static,c.BOOLEAN,!1), +_center:b.ensureInstanceOf(a.rotation.center,THREE.Vector3,this.position._value.clone()),_randomise:b.ensureTypedArg(a.position.randomise,c.BOOLEAN,!1)},this.maxAge={_value:b.ensureTypedArg(a.maxAge.value,c.NUMBER,2),_spread:b.ensureTypedArg(a.maxAge.spread,c.NUMBER,0)},this.color={_value:b.ensureArrayInstanceOf(a.color.value,THREE.Color,new THREE.Color),_spread:b.ensureArrayInstanceOf(a.color.spread,THREE.Vector3,new THREE.Vector3),_randomise:b.ensureTypedArg(a.position.randomise,c.BOOLEAN,!1)},this.opacity={_value:b.ensureArrayTypedArg(a.opacity.value,c.NUMBER,1),_spread:b.ensureArrayTypedArg(a.opacity.spread,c.NUMBER,0),_randomise:b.ensureTypedArg(a.position.randomise,c.BOOLEAN,!1)},this.size={_value:b.ensureArrayTypedArg(a.size.value,c.NUMBER,1),_spread:b.ensureArrayTypedArg(a.size.spread,c.NUMBER,0),_randomise:b.ensureTypedArg(a.position.randomise,c.BOOLEAN,!1)},this.angle={_value:b.ensureArrayTypedArg(a.angle.value,c.NUMBER,0), +_spread:b.ensureArrayTypedArg(a.angle.spread,c.NUMBER,0),_randomise:b.ensureTypedArg(a.position.randomise,c.BOOLEAN,!1)},this.particleCount=b.ensureTypedArg(a.particleCount,c.NUMBER,100),this.duration=b.ensureTypedArg(a.duration,c.NUMBER,null),this.isStatic=b.ensureTypedArg(a.isStatic,c.BOOLEAN,!1),this.activeMultiplier=b.ensureTypedArg(a.activeMultiplier,c.NUMBER,1),this.direction=b.ensureTypedArg(a.direction,c.NUMBER,1),this.alive=b.ensureTypedArg(a.alive,c.BOOLEAN,!0),this.particlesPerSecond=0,this.activationIndex=0,this.attributeOffset=0,this.attributeEnd=0,this.age=0,this.activeParticleCount=0,this.group=null,this.attributes=null,this.paramsArray=null,this.resetFlags={position:b.ensureTypedArg(a.position.randomise,c.BOOLEAN,!1)||b.ensureTypedArg(a.radius.randomise,c.BOOLEAN,!1),velocity:b.ensureTypedArg(a.velocity.randomise,c.BOOLEAN,!1),acceleration:b.ensureTypedArg(a.acceleration.randomise,c.BOOLEAN,!1)||b.ensureTypedArg(a.drag.randomise,c.BOOLEAN,!1), +rotation:b.ensureTypedArg(a.rotation.randomise,c.BOOLEAN,!1),rotationCenter:b.ensureTypedArg(a.rotation.randomise,c.BOOLEAN,!1),size:b.ensureTypedArg(a.size.randomise,c.BOOLEAN,!1),color:b.ensureTypedArg(a.color.randomise,c.BOOLEAN,!1),opacity:b.ensureTypedArg(a.opacity.randomise,c.BOOLEAN,!1),angle:b.ensureTypedArg(a.angle.randomise,c.BOOLEAN,!1)},this.updateFlags={},this.updateCounts={},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 e in this.updateMap)this.updateMap.hasOwnProperty(e)&&(this.updateCounts[this.updateMap[e]]=0,this.updateFlags[this.updateMap[e]]=!1,this._createGetterSetters(this[e],e));this.bufferUpdateRanges={},this.attributeKeys=null,this.attributeCount=0,b.ensureValueOverLifetimeCompliance(this.color,d,d),b.ensureValueOverLifetimeCompliance(this.opacity,d,d), +b.ensureValueOverLifetimeCompliance(this.size,d,d),b.ensureValueOverLifetimeCompliance(this.angle,d,d)},SPE.Emitter.constructor=SPE.Emitter,SPE.Emitter.prototype._createGetterSetters=function(a,b){"use strict";var c=this;for(var d in a)if(a.hasOwnProperty(d)){var e=d.replace("_","");Object.defineProperty(a,e,{get:function(a){return function(){return this[a]}}(d),set:function(a){return function(d){var e=c.updateMap[b],f=this[a],g=SPE.valueOverLifetimeLength;"_rotationCenter"===a?(c.updateFlags.rotationCenter=!0,c.updateCounts.rotationCenter=0):"_randomise"===a?c.resetFlags[e]=d:(c.updateFlags[e]=!0,c.updateCounts[e]=0),c.group._updateDefines(),this[a]=d,Array.isArray(f)&&SPE.utils.ensureValueOverLifetimeCompliance(c[b],g,g)}}(d)})}},SPE.Emitter.prototype._setBufferUpdateRanges=function(a){"use strict";this.attributeKeys=a,this.attributeCount=a.length;for(var b=this.attributeCount-1;b>=0;--b)this.bufferUpdateRanges[a[b]]={min:Number.POSITIVE_INFINITY,max:Number.NEGATIVE_INFINITY}}, +SPE.Emitter.prototype._calculatePPSValue=function(a){"use strict";var b=this.particleCount;this.duration?this.particlesPerSecond=b/(a=0;--h)b=g[h],c=e[b],!0!==d[b]&&!0!==c||(this._assignValue(b,a),this._updateAttributeUpdateRange(b,a),!0===c&&f[b]===this.particleCount?(e[b]=!1,f[b]=0):1==c&&++f[b])},SPE.Emitter.prototype._updateAttributeUpdateRange=function(a,b){"use strict";var c=this.bufferUpdateRanges[a];c.min=Math.min(b,c.min),c.max=Math.max(b,c.max)},SPE.Emitter.prototype._resetBufferRanges=function(){"use strict";var a,b=this.bufferUpdateRanges,c=this.bufferUpdateKeys,d=this.bufferUpdateCount-1;for(d;d>=0;--d)a=c[d],b[a].min=Number.POSITIVE_INFINITY,b[a].max=Number.NEGATIVE_INFINITY},SPE.Emitter.prototype._onRemove=function(){"use strict";this.particlesPerSecond=0,this.attributeOffset=0,this.activationIndex=0,this.activeParticleCount=0, +this.group=null,this.attributes=null,this.paramsArray=null,this.age=0},SPE.Emitter.prototype._decrementParticleCount=function(){"use strict";--this.activeParticleCount},SPE.Emitter.prototype._incrementParticleCount=function(){"use strict";++this.activeParticleCount},SPE.Emitter.prototype._checkParticleAges=function(a,b,c,d){"use strict";for(var e,f,g,h,i=b-1;i>=a;--i)e=4*i,0!==(h=c[e])&&(g=c[e+1],f=c[e+2],1===this.direction?(g+=d)>=f&&(g=0,h=0,this._decrementParticleCount()):(g-=d)<=0&&(g=f,h=0,this._decrementParticleCount()),c[e]=h,c[e+1]=g,this._updateAttributeUpdateRange("params",i))},SPE.Emitter.prototype._activateParticles=function(a,b,c,d){"use strict";for(var e,f,g=this.direction,h=a;hthis.duration)return this.alive=!1,void(this.age=0);var g=1===this.particleCount?f:0|f,h=Math.min(g+e,this.activationEnd),i=h-this.activationIndex|0,j=i>0?a/i:0;this._activateParticles(g,h,d,j),this.activationIndex+=e,this.activationIndex>c&&(this.activationIndex=b),this.age+=a}},SPE.Emitter.prototype.reset=function(a){"use strict";if(this.age=0,this.alive=!1,!0===a){for(var b,c=this.attributeOffset,d=c+this.particleCount,e=this.paramsArray,f=this.attributes.params.bufferAttribute,g=d-1;g>=c;--g)b=4*g,e[b]=0,e[b+1]=0;f.updateRange.offset=0,f.updateRange.count=-1,f.needsUpdate=!0}return this}, +SPE.Emitter.prototype.enable=function(){"use strict";return this.alive=!0,this},SPE.Emitter.prototype.disable=function(){"use strict";return this.alive=!1,this},SPE.Emitter.prototype.remove=function(){"use strict";return null!==this.group?this.group.removeEmitter(this):console.error("Emitter does not belong to a group, cannot remove."),this}; \ No newline at end of file