topical media & game development
mobile-query-three-plugins-flamethrower-vendor-fireworks-bundle.js / js
var Fireworks = {};////////////////////////////////////////////////////////////////////////////
// //
////////////////////////////////////////////////////////////////////////////
Fireworks.EffectsStackBuilder = function(emitter){
this._emitter = emitter;
};
Getter for the emitter
Fireworks.EffectsStackBuilder.prototype.emitter = function(){
return this._emitter;
};
Fireworks.EffectsStackBuilder.prototype.back = function(){
return this._emitter;
}
Fireworks.EffectsStackBuilder.prototype.createEffect = function(name, opts){
var creator = Fireworks.createEffect(name, opts).pushTo(this._emitter).back(this);
creator.effect().emitter(this._emitter);
return creator;
}
////////////////////////////////////////////////////////////////////////////
// //
////////////////////////////////////////////////////////////////////////////
Basic Fireworks.Effect builder
Fireworks.createEffect = function(name, opts){
// handle polymophism
if( typeof(name) === 'object' ){
opts = name;
name = undefined;
}
var effect = new Fireworks.Effect();
effect.opts = opts;
effect.name = name;
effect.back = null;
var methods = {
onCreate: function(val){
effect.onCreate = val;
return methods;
},
onBirth: function(val){
effect.onBirth = val;
return methods;
},
onUpdate: function(val){
effect.onUpdate = val;
return methods;
},
onDeath: function(val){
effect.onDeath = val;
return methods;
},
onPreUpdate: function(val){
effect.onPreUpdate = val;
return methods;
},
onPreRender: function(val){
effect.onPreRender = val;
return methods;
},
onRender: function(val){
effect.onRender = val;
return methods;
},
onPostRender: function(val){
effect.onPostRender = val;
return methods;
},
onIntensityChange: function(val){
effect.onIntensityChange= val;
return methods;
},
pushTo : function(emitter){
emitter.effects().push(effect);
return methods;
},
back : function(value){
if( value === undefined ) return effect.back;
effect.back = value;
return methods;
},
effect : function(){
return effect;
}
}
return methods;
}
An effect to apply on particles
Fireworks.Effect = function(){
this._emitter = null;
}
Fireworks.Effect.prototype.destroy = function(){
}
Getter/Setter for the emitter
Fireworks.Effect.prototype.emitter = function(value){
if( value === undefined ) return this._emitter;
this._emitter = value;
return this;
};
Callback called on particle creation
//Fireworks.Effect.prototype.onCreate = function(){
//}
//
Callback called when a particle is spawning
TODO to rename onSpawn
//Fireworks.Effect.prototype.onBirth = function(){
//}
//
//Fireworks.Effect.prototype.onDeath = function(){
//}
//
//Fireworks.Effect.prototype.onUpdate = function(){
//}
Fireworks.createEmitter = function(opts){
return new Fireworks.Emitter(opts);
}
The emitter of particles
Fireworks.Emitter = function(opts){
this._nParticles = opts.nParticles !== undefined ? opts.nParticles : 100;
this._particles = [];
this._effects = [];
this._started = false;
this._onUpdated = null;
this._intensity = 0;
this._maxDeltaTime = 1/3;
this._effectsStackBuilder = new Fireworks.EffectsStackBuilder(this)
}
Fireworks.Emitter.prototype.destroy = function()
{
this._effects.forEach(function(effect){
effect.destroy();
});
this._particles.forEach(function(particle){
particle.destroy();
});
}
////////////////////////////////////////////////////////////////////////////
// Getters //
////////////////////////////////////////////////////////////////////////////
Fireworks.Emitter.prototype.effects = function(){
return this._effects;
}
Fireworks.Emitter.prototype.effect = function(name){
for(var i = 0; i < this._effects.length; i++){
var effect = this._effects[i];
if( effect.name === name ) return effect;
}
return null;
}
Fireworks.Emitter.prototype.particles = function(){
return this._particles;
}
Fireworks.Emitter.prototype.liveParticles = function(){
return this._liveParticles;
}
Fireworks.Emitter.prototype.deadParticles = function(){
return this._deadParticles;
}
Fireworks.Emitter.prototype.nParticles = function(){
return this._nParticles;
}
Fireworks.Emitter.prototype.effectsStackBuilder = function(){
return this._effectsStackBuilder;
}
Getter/setter for intensity
Fireworks.Emitter.prototype.intensity = function(value){
// if it is a getter, return value
if( value === undefined ) return this._intensity;
// if the value didnt change, return for chained api
if( value === this._intensity ) return this;
// sanity check
console.assert( value >= 0, 'Fireworks.Emitter.intensity: invalid value.', value);
console.assert( value <= 1, 'Fireworks.Emitter.intensity: invalid value.', value);
// backup the old value
var oldValue = this._intensity;
// update the value
this._intensity = value;
// notify all effects
this._effects.forEach(function(effect){
if( !effect.onIntensityChange ) return;
effect.onIntensityChange(this._intensity, oldValue);
}.bind(this));
return this; // for chained API
}
Getter/setter for intensity
Fireworks.Emitter.prototype.maxDeltaTime = function(value){
if( value === undefined ) return this._maxDeltaTime;
this._maxDeltaTime = value;
return this;
}
////////////////////////////////////////////////////////////////////////////
// backward compatibility //
////////////////////////////////////////////////////////////////////////////
Fireworks.Emitter.prototype.setParticleData = function(particle, namespace, value){
particle.set(namespace, value);
}
Fireworks.Emitter.prototype.getParticleData = function(particle, namespace){
return particle.get(namespace);
}
////////////////////////////////////////////////////////////////////////////
// Start function //
////////////////////////////////////////////////////////////////////////////
Fireworks.Emitter.prototype.start = function()
{
console.assert( this._effects.length > 0, "At least one effect MUST be set")
console.assert( this._started === false );
this._particles = new Array(this._nParticles);
for(var i = 0; i < this._nParticles; i++){
this._particles[i] = new Fireworks.Particle();
}
this._liveParticles = [];
this._deadParticles = this._particles.slice(0);
this._started = true;
// onCreate on all particles
this._effects.forEach(function(effect){
if( !effect.onCreate ) return;
this._particles.forEach(function(particle, particleIdx){
effect.onCreate(particle, particleIdx);
})
}.bind(this));
// set the intensity to 1
this.intensity(1)
return this; // for chained API
}
Fireworks.Emitter.prototype.update = function(deltaTime){
// bound the deltaTime to this._maxDeltaTime
deltaTime = Math.min(this._maxDeltaTime, deltaTime)
// honor effect.onPreUpdate
this._effects.forEach(function(effect){
if( !effect.onPreUpdate ) return;
effect.onPreUpdate(deltaTime);
}.bind(this));
// update each particles
this._effects.forEach(function(effect){
if( !effect.onUpdate ) return;
this._liveParticles.forEach(function(particle){
effect.onUpdate(particle, deltaTime);
})
}.bind(this));
return this; // for chained API
}
Fireworks.Emitter.prototype.render = function(){
this._effects.forEach(function(effect){
if( !effect.onPreRender ) return;
effect.onPreRender();
}.bind(this));
this._effects.forEach(function(effect){
if( !effect.onRender ) return;
this._liveParticles.forEach(function(particle){
effect.onRender(particle);
})
}.bind(this));
this._effects.forEach(function(effect){
if( !effect.onPostRender ) return;
effect.onPostRender();
}.bind(this));
return this; // for chained API
}
////////////////////////////////////////////////////////////////////////////
// //
////////////////////////////////////////////////////////////////////////////
kill this particle
Fireworks.Emitter.prototype.killParticle = function(particle)
{
var idx = this._liveParticles.indexOf(particle);
console.assert( idx !== -1 )
this._liveParticles.splice(idx, 1)
this._deadParticles.push(particle);
// do the death on all effects
this.effects().forEach(function(effect){
effect.onDeath && effect.onDeath(particle);
}.bind(this));
}
Spawn a particle
Fireworks.Emitter.prototype.spawnParticle = function(){
console.assert(this._deadParticles.length >= 1, 'no more particle available' );
// change the particles
var particle = this.deadParticles().pop();
this.liveParticles().push(particle);
// do the birth on all effects
this.effects().forEach(function(effect){
effect.onBirth && effect.onBirth(particle);
}.bind(this));
}
Fireworks.createLinearGradient = function(opts){
var LinearGradient = new Fireworks.LinearGradient(opts);
return LinearGradient;
}
The emitter of particles
Fireworks.LinearGradient = function(opts){
this._keyPoints = [];
}
Fireworks.LinearGradient.prototype.push = function(x, y){
this._keyPoints.push({
x : x,
y : y
});
return this;
}
Compute a value for this LinearGradient
Fireworks.LinearGradient.prototype.get = function(x){
for( var i = 0; i < this._keyPoints.length; i++ ){
var keyPoint = this._keyPoints[i];
if( x <= keyPoint.x ) break;
}
if( i === 0 ) return this._keyPoints[0].y;
console.assert(i < this._keyPoints.length )
var prev = this._keyPoints[i-1];
var next = this._keyPoints[i];
var ratio = (x - prev.x) / (next.x - prev.x)
var y = prev.y + ratio * (next.y - prev.y)
return y;
};
The emitter of particles
Fireworks.Particle = function(){
}
Fireworks.Particle.prototype.set = function(key, value){
console.assert( this[key] === undefined, "key already defined: "+key );
this[key] = value;
return this[key];
}
Fireworks.Particle.prototype.get = function(key){
console.assert( this[key] !== undefined, "key undefined: "+key );
return this[key];
}
Fireworks.Particle.prototype.has = function(key){
return this[key] !== undefined ? true : false;
}
Fireworks.Shape = function(){
}
**
// *
parameter: {Fireworks.Vector} point the point coordinate to test
// *
returns: {Boolean} true if point is inside the shape, false otherwise
//*/
//Fireworks.Shape.prototype.contains = function(point){
//}
//
**
// * generate a random point contained in this shape
// *
returns: {Fireworks.Vector} the just generated random point
//*/
//Firefly.Shape.prototype.randomPoint = function(vector){
//}
Fireworks.createVector = function(x, y, z){
return new Fireworks.Vector(x,y,z);
};
jme- copy of THREE.Vector3 github.com/mrdoob/three.js/blob/master/src/core/Vector3.js
author: mr.doob / mrdoob.com/
author: kile / kile.stravaganza.org/
author: philogb / blog.thejit.org/
author: mikael emtinger / gomo.se/
author: egraether / egraether.com/
Fireworks.Vector = function ( x, y, z ) {
this.x = x || 0;
this.y = y || 0;
this.z = z || 0;
};
Fireworks.Vector.prototype = {
constructor: Fireworks.Vector,
set: function ( x, y, z ) {
this.x = x;
this.y = y;
this.z = z;
return this;
},
setX: function ( x ) {
this.x = x;
return this;
},
setY: function ( y ) {
this.y = y;
return this;
},
setZ: function ( z ) {
this.z = z;
return this;
},
random : function( ) {
this.x = Math.random() - 0.5;
this.y = Math.random() - 0.5;
this.z = Math.random() - 0.5;
return this;
}, // jme - added
toString : function(){
return JSON.stringify(this);
}, // jme - added
copy: function ( v ) {
this.x = v.x;
this.y = v.y;
this.z = v.z;
return this;
},
add: function ( a, b ) {
this.x = a.x + b.x;
this.y = a.y + b.y;
this.z = a.z + b.z;
return this;
},
addSelf: function ( v ) {
this.x += v.x;
this.y += v.y;
this.z += v.z;
return this;
},
addScalar: function ( s ) {
this.x += s;
this.y += s;
this.z += s;
return this;
},
sub: function ( a, b ) {
this.x = a.x - b.x;
this.y = a.y - b.y;
this.z = a.z - b.z;
return this;
},
subSelf: function ( v ) {
this.x -= v.x;
this.y -= v.y;
this.z -= v.z;
return this;
},
multiply: function ( a, b ) {
this.x = a.x * b.x;
this.y = a.y * b.y;
this.z = a.z * b.z;
return this;
},
multiplySelf: function ( v ) {
this.x *= v.x;
this.y *= v.y;
this.z *= v.z;
return this;
},
multiplyScalar: function ( s ) {
this.x *= s;
this.y *= s;
this.z *= s;
return this;
},
divideSelf: function ( v ) {
this.x /= v.x;
this.y /= v.y;
this.z /= v.z;
return this;
},
divideScalar: function ( s ) {
if ( s ) {
this.x /= s;
this.y /= s;
this.z /= s;
} else {
this.x = 0;
this.y = 0;
this.z = 0;
}
return this;
},
negate: function() {
return this.multiplyScalar( - 1 );
},
dot: function ( v ) {
return this.x * v.x + this.y * v.y + this.z * v.z;
},
lengthSq: function () {
return this.x * this.x + this.y * this.y + this.z * this.z;
},
length: function () {
return Math.sqrt( this.lengthSq() );
},
normalize: function () {
return this.divideScalar( this.length() );
},
setLength: function ( l ) {
return this.normalize().multiplyScalar( l );
},
cross: function ( a, b ) {
this.x = a.y * b.z - a.z * b.y;
this.y = a.z * b.x - a.x * b.z;
this.z = a.x * b.y - a.y * b.x;
return this;
},
crossSelf: function ( v ) {
var x = this.x, y = this.y, z = this.z;
this.x = y * v.z - z * v.y;
this.y = z * v.x - x * v.z;
this.z = x * v.y - y * v.x;
return this;
},
distanceTo: function ( v ) {
return Math.sqrt( this.distanceToSquared( v ) );
},
distanceToSquared: function ( v ) {
return new Fireworks.Vector().sub( this, v ).lengthSq();
},
equals: function ( v ) {
return ( ( v.x === this.x ) && ( v.y === this.y ) && ( v.z === this.z ) );
},
isZero: function () {
return ( this.lengthSq() < 0.0001 /* almostZero */ );
},
clone: function () {
return new Fireworks.Vector( this.x, this.y, this.z );
}
};
Shortcut to create Fireworks.EffectRandomDriftVelocity
Fireworks.EffectsStackBuilder.prototype.acceleration = function(opts)
{
opts = opts || {};
var effectId = opts.effectId || 'acceleration';
console.assert( opts.shape instanceof Fireworks.Shape );
// create the effect itself
Fireworks.createEffect(effectId, {
shape : opts.shape
}).onCreate(function(particle){
particle.set('acceleration', {
vector : new Fireworks.Vector()
});
}).onBirth(function(particle){
var acceleration= particle.get('acceleration').vector;
this.opts.shape.randomPoint(acceleration)
}).onUpdate(function(particle, deltaTime){
var velocity = particle.get('velocity').vector;
var acceleration= particle.get('acceleration').vector;
velocity.x += acceleration.x * deltaTime;
velocity.y += acceleration.y * deltaTime;
velocity.z += acceleration.z * deltaTime;
}).pushTo(this._emitter);
return this; // for chained API
};
Handle the friction - aka a value between 0 and 1 which multiply the velocity
parameter: {Number} value the friction number between 0 and 1
Fireworks.EffectsStackBuilder.prototype.friction = function(value)
{
// handle parameter polymorphism
value = value !== undefined ? value : 1;
// sanity check
console.assert( value >= 0 && value <= 1.0 );
// create the effect itself
Fireworks.createEffect('friction')
.onCreate(function(particle, particleIdx){
particle.set('friction', {
value : value
});
})
.onBirth(function(particle){
var data = particle.get('friction');
data.value = value
})
.onUpdate(function(particle){
var friction = particle.get('friction').value;
var velocity = particle.get('velocity').vector;
velocity.multiplyScalar(friction);
})
.pushTo(this._emitter);
// return this for chained API
return this;
};
Shortcut to create Fireworks.EffectRandomDriftVelocity
Fireworks.EffectsStackBuilder.prototype.lifeTime = function(minAge, maxAge)
{
// sanity check
console.assert( minAge !== undefined )
// if maxAge isnt
if( maxAge === undefined ) maxAge = minAge;
console.assert( maxAge !== undefined )
// create the effect itself
var emitter = this._emitter;
Fireworks.createEffect('lifeTime', {
minAge : minAge,
maxAge : maxAge
}).onCreate(function(particle){
var data = particle.set('lifeTime', {
curAge : 0,
minAge : 0,
maxAge : 0,
normalizedAge : function(){
return (data.curAge - data.minAge) / (data.maxAge - data.minAge);
}
});
}).onBirth(function(particle){
var lifeTime = particle.get('lifeTime');
lifeTime.curAge = 0;
lifeTime.maxAge = this.opts.minAge + Math.random()*(this.opts.maxAge-this.opts.minAge);
}).onUpdate(function(particle, deltaTime){
var lifeTime = particle.get('lifeTime');
lifeTime.curAge += deltaTime;
if( lifeTime.curAge > lifeTime.maxAge ) emitter.killParticle(particle);
}).pushTo(this._emitter);
// return this for chained API
return this;
};
Shortcut to create Fireworks.EffectRandomDriftVelocity
Fireworks.EffectsStackBuilder.prototype.position = function(shape)
{
console.assert( shape instanceof Fireworks.Shape );
Fireworks.createEffect('position', {
shape : shape
}).onCreate(function(particle){
particle.set('position', {
vector : new Fireworks.Vector()
});
}).onBirth(function(particle){
var position = particle.get('position').vector;
this.opts.shape.randomPoint(position)
}).pushTo(this._emitter);
return this; // for chained API
};
Shortcut to create Fireworks.EffectRandomDriftVelocity
Fireworks.EffectsStackBuilder.prototype.radialVelocity = function(minSpeed, maxSpeed)
{
Fireworks.createEffect('radialVelocity', {
minSpeed : minSpeed,
maxSpeed : maxSpeed
}).onCreate(function(particle){
particle.set('velocity', {
vector : new Fireworks.Vector()
});
}).onBirth(function(particle, deltaTime){
var position = particle.get('position').vector;
var velocity = particle.get('velocity').vector;
var length = this.opts.minSpeed + (this.opts.maxSpeed - this.opts.minSpeed)*Math.random();
velocity.copy(position).setLength(length);
}).onUpdate(function(particle, deltaTime){
var position = particle.get('position').vector;
var velocity = particle.get('velocity').vector;
position.x += velocity.x * deltaTime;
position.y += velocity.y * deltaTime;
position.z += velocity.z * deltaTime;
}).pushTo(this._emitter);
return this; // for chained API
};
Shortcut to create Fireworks.EffectRandomDriftVelocity
Fireworks.EffectsStackBuilder.prototype.randomVelocityDrift = function(drift)
{
// create the effect itself
Fireworks.createEffect('randomVelocityDrift', {
drift : drift
}).onUpdate(function(particle, deltaTime){
var velocity = particle.get('velocity').vector;
velocity.x += (Math.random()*2 - 1) * this.opts.drift.x * deltaTime;
velocity.y += (Math.random()*2 - 1) * this.opts.drift.y * deltaTime;
velocity.z += (Math.random()*2 - 1) * this.opts.drift.z * deltaTime;
}).pushTo(this._emitter);
// return for chained API
return this; // for chained API
};
Create a velocity effect
parameter: {Fireworks.Shape} shape set the direction of the velocity by a randompoint in this shape
parameter: {Number?} speed set the speed itself. if undefined, keep randompoint length for speed
Fireworks.EffectsStackBuilder.prototype.velocity = function(shape, speed)
{
Fireworks.createEffect('velocity', {
shape : shape,
speed : speed !== undefined ? speed : -1
}).onCreate(function(particle){
particle.set('velocity', {
vector : new Fireworks.Vector()
});
}).onBirth(function(particle){
var velocity = particle.get('velocity').vector;
this.opts.shape.randomPoint(velocity)
if( this.opts.speed !== -1 ) velocity.setLength(this.opts.speed);
}).onUpdate(function(particle, deltaTime){
var position = particle.get('position').vector;
var velocity = particle.get('velocity').vector;
position.x += velocity.x * deltaTime;
position.y += velocity.y * deltaTime;
position.z += velocity.z * deltaTime;
}).pushTo(this._emitter);
return this; // for chained API
};
Shortcut to create Fireworks.Shape.Box
Fireworks.createShapeBox = function(centerX, centerY, centerZ, sizeX, sizeY, sizeZ){
var center = new Fireworks.Vector(centerX, centerY, centerZ);
var size = new Fireworks.Vector(sizeX, sizeY, sizeZ);
return new Fireworks.Shape.Box(center, size);
};
Handle a Firework.Shape forming a sphere
parameter: {Fireworks.Vector} center the center of the sphape
parameter: {Fireworks.Vector} shape the size of the shape
Fireworks.Shape.Box = function(center, size)
{
this.center = center;
this.size = size;
this._vector = new Fireworks.Vector();
}
// inherit from Fireworks.Effect
Fireworks.Shape.Box.prototype = new Fireworks.Shape();
Fireworks.Shape.Box.prototype.constructor = Fireworks.Shape.Box;
Fireworks.Shape.Box.prototype.contains = function(point){
// compute delta between the point and the center
var delta = this._vector.sub(point, this.center);
// test the delta is too far
if( Math.abs(delta.x) > this.size.x/2 ) return false;
if( Math.abs(delta.y) > this.size.y/2 ) return false;
if( Math.abs(delta.z) > this.size.z/2 ) return false;
// if all tests, passed true
return true;
}
Fireworks.Shape.Box.prototype.randomPoint = function(vector){
var point = vector || this._vector;
// get a random point
point.x = Math.random() * this.size.x - this.size.x/2;
point.y = Math.random() * this.size.y - this.size.y/2;
point.z = Math.random() * this.size.z - this.size.z/2;
// add this.center
point.addSelf(this.center);
// return the point
return point;
}
Shortcut to create Fireworks.Shape.Point
Fireworks.createShapePoint = function(positionX, positionY, positionZ){
var position = new Fireworks.Vector(positionX, positionY, positionZ);
return new Fireworks.Shape.Point(position);
};
Handle a Firework.Shape forming a point
parameter: {Fireworks.Vector} position the position of the point
Fireworks.Shape.Point = function(position)
{
this.position = position;
this._vector = new Fireworks.Vector();
}
// inherit from Fireworks.Effect
Fireworks.Shape.Point.prototype = new Fireworks.Shape();
Fireworks.Shape.Point.prototype.constructor = Fireworks.Shape.Point;
Fireworks.Shape.Point.prototype.contains = function(point){
if( point.x !== this.position.x ) return false;
if( point.y !== this.position.y ) return false;
if( point.z !== this.position.z ) return false;
// if all tests, passed true
return true;
}
Fireworks.Shape.Point.prototype.randomPoint = function(vector){
var point = vector || this._vector;
// get a random point
point.copy(this.position);
// return the point
return point;
}
Shortcut to create Fireworks.Shape.Box
Fireworks.createShapeSphere = function(centerX, centerY, centerZ, radius, boundingbox){
var center = new Fireworks.Vector(centerX, centerY, centerZ);
return new Fireworks.ShapeSphere(center, radius);
};
Handle a Firework.Shape forming a sphere
parameter: {Fireworks.Vector} center the center of the sphere
parameter: {Number} radius the radius of the sphere
Fireworks.ShapeSphere = function(center, radius)
{
this.center = center;
this.radius = radius;
this._vector = new Fireworks.Vector();
}
// inherit from Fireworks.Effect
Fireworks.ShapeSphere.prototype = new Fireworks.Shape();
Fireworks.ShapeSphere.prototype.constructor = Fireworks.ShapeSphere;
Fireworks.ShapeSphere.prototype.contains = function(point){
// compute distance between the point and the center
var distance = this._vector.sub(point, this.center).length();
// return true if this distance is <= than sphere radius
return distance <= this.radius;
}
Fireworks.ShapeSphere.prototype.randomPoint = function(vector){
var point = vector || this._vector;
// get a random point
point.x = Math.random()-0.5;
point.y = Math.random()-0.5;
point.z = Math.random()-0.5;
// compute the length between the point
var length = Math.random()*this.radius;
// set the point at the proper distance;
point.setLength( length );
// add the center
point.addSelf(this.center);
// return the point
return point;
}
Spawner deliverying paricles in one shot
parameter: {Number?} the number of particle to emit
Fireworks.EffectsStackBuilder.prototype.spawnerOneShot = function(nParticles)
{
// handle parameter polymorphism
nParticles = nParticles || this.emitter().nParticles();
// define local variables
var emitter = this.emitter();
var nSent = 0;
var spawning = true;
// create the effect itself
Fireworks.createEffect('spawner', {
start : function(){ spawning = true; },
stop : function(){ spawning = false; }
}).onPreUpdate(function(deltaTime){
// if spawning is false, do nothing
if( spawning === false ) return;
// if already completed, do nothing
if( nParticles === nSent ) return;
// spawn each particle
var amount = nParticles - nSent;
amount = Math.min(amount, emitter.deadParticles().length);
for(var i = 0; i < amount; i++){
emitter.spawnParticle();
}
// update the amount of sent particles
nSent += amount;
}).pushTo(this._emitter);
// return this for chained API
return this;
};
Spawner deliverying paricles at a steady rate
parameter: {Number?} rate the rate at which it gonna emit
Fireworks.EffectsStackBuilder.prototype.spawnerSteadyRate = function(rate)
{
// handle parameter polymorphism
rate = rate !== undefined ? rate : 1;
// define local variables
var emitter = this.emitter();
var nToCreate = 1;
var spawning = true;
// create the effect itself
Fireworks.createEffect('spawner', {
rate : rate,
start : function(){ spawning = true; },
stop : function(){ spawning = false; }
}).onPreUpdate(function(deltaTime){
var rate = this.opts.rate;
// if spawning is false, do nothing
if( spawning === false ) return;
// update nToCreate
nToCreate += rate * deltaTime;
// nParticles is the interger part of nToCreate as you spawn them one by one
var nParticles = Math.floor(nToCreate);
// dont spawn more particles than available
// TODO here estimate how much more is needed to never lack of it
nParticles = Math.min(nParticles, emitter.deadParticles().length);
// update nToCreate
nToCreate -= nParticles;
// spawn each particle
for(var i = 0; i < nParticles; i++){
emitter.spawnParticle();
}
}).pushTo(this._emitter);
// return this for chained API
return this;
};
render to canvas
Fireworks.EffectsStackBuilder.prototype.renderToCanvas = function(opts)
{
opts = opts || {};
var ctx = opts.ctx || buildDefaultContext();
// create the effect itself
var effect = Fireworks.createEffect('renderToCanvas', {
ctx : ctx
}).pushTo(this._emitter);
if( opts.type === 'arc' ) ctorTypeArc(effect);
else if( opts.type === 'drawImage' ) ctorTypeDrawImage(effect);
else{
console.assert(false, 'renderToCanvas opts.type is invalid: ');
}
return this; // for chained API
function buildDefaultContext(){
// build canvas element
var canvas = document.createElement('canvas');
canvas.width = window.innerWidth;
canvas.height = window.innerHeight;
document.body.appendChild(canvas);
// canvas.style
canvas.style.position = "absolute";
canvas.style.left = 0;
canvas.style.top = 0;
// setup ctx
var ctx = canvas.getContext('2d');
// return ctx
return ctx;
}
function ctorTypeArc(){
return effect.onCreate(function(particle, particleIdx){
particle.set('renderToCanvas', {
size : 3
});
}).onRender(function(particle){
var position = particle.get('position').vector;
var size = particle.get('renderToCanvas').size;
ctx.beginPath();
ctx.arc(position.x + canvas.width /2,
position.y + canvas.height/2, size, 0, Math.PI*2, true);
ctx.fill();
});
};
function ctorTypeDrawImage(){
// handle parameter polymorphism
if( typeof(opts.image) === 'string' ){
var images = [new Image];
images[0].src = opts.image;
}else if( opts.image instanceof Image ){
var images = [opts.image];
}else if( opts.image instanceof Array ){
var images = opts.image;
}else console.assert(false, 'invalid .renderToCanvas() options')
return effect.onCreate(function(particle, particleIdx){
particle.set('renderToCanvas', {
scale : 1, // should that be there ? or in its own effect ?
opacity : 1, // should that be there ? or in its own effect ?
rotation : 0*Math.PI
});
}).onRender(function(particle){
var position = particle.get('position').vector;
var data = particle.get('renderToCanvas');
var canonAge = particle.get('lifeTime').normalizedAge();
var imageIdx = Math.floor(canonAge * images.length);
var image = images[imageIdx];
// save the context
ctx.save();
// translate in canvas's center, and the particle position
ctx.translate(position.x + canvas.width/2, position.y + canvas.height/2);
// set the scale of this particles
ctx.scale(data.scale, data.scale);
// set the rotation
ctx.rotate(data.rotation);
// set ctx.globalAlpha
ctx.globalAlpha = data.opacity;
// draw the image itself
if( image instanceof Image ){
ctx.drawImage(image, -image.width/2, -image.height/2);
}else if( typeof(image) === 'object' ){
ctx.drawImage(image.image
, image.offsetX, image.offsetY , image.width, image.height
, -image.width/2, -image.height/2, image.width, image.height);
}else console.assert(false);
// restore the context
ctx.restore();
});
};
};
render to three.js THREE.Object3D
If i play with object3D.visible true/false instead of Object3D.add/remove
i got a lot of artefacts
Fireworks.EffectsStackBuilder.prototype.renderToThreejsObject3D = function(opts)
{
var effectId = opts.effectId || 'renderToThreeParticleSystem';
var container = opts.container;
// create the effect itself
Fireworks.createEffect(effectId)
.onCreate(function(particle, particleIdx){
particle.set('threejsObject3D', {
object3d : opts.create()
});
console.assert(particle.get('threejsObject3D').object3d instanceof THREE.Object3D);
var object3d = particle.get('threejsObject3D').object3d;
// object3d.visible= false;
}).onBirth(function(particle){
var object3d = particle.get('threejsObject3D').object3d;
// object3d.visible= true;
container.add(object3d);
}).onDeath(function(particle){
var object3d = particle.get('threejsObject3D').object3d;
// object3d.visible= false;
container.remove(object3d);
}).onRender(function(particle){
var object3d = particle.get('threejsObject3D').object3d;
var position = particle.get('position').vector;
object3d.position.set(position.x, position.y, position.z);
}).pushTo(this._emitter);
return this; // for chained API
};
render to three.js to THREE.ParticleSystem
Fireworks.EffectsStackBuilder.prototype.renderToThreejsParticleSystem = function(opts)
{
opts = opts || {};
var effectId = opts.effectId || 'renderToThreejsParticleSystem';
var particleSystem = opts.particleSystem || defaultParticleSystem;
// if opts.particleSystem is a function, call it to create the particleSystem
if( typeof(particleSystem) === 'function' ) particleSystem = particleSystem(this._emitter);
// sanity check
console.assert(particleSystem instanceof THREE.ParticleSystem, "particleSystem MUST be THREE.ParticleSystem");
// some aliases
var geometry = particleSystem.geometry;
console.assert(geometry.vertices.length >= this._emitter.nParticles())
// create the effect itself
Fireworks.createEffect(effectId, {
particleSystem : particleSystem
}).onCreate(function(particle, particleIdx){
particle.set('threejsParticle', {
particleIdx : particleIdx
});
var vertex = geometry.vertices[particleIdx];
vertex.set(Infinity, Infinity, Infinity);
}).onDeath(function(particle){
var particleIdx = particle.get('threejsParticle').particleIdx;
var vertex = geometry.vertices[particleIdx];
vertex.set(Infinity, Infinity, Infinity);
}).onRender(function(particle){
var particleIdx = particle.get('threejsParticle').particleIdx;
var vertex = geometry.vertices[particleIdx];
var position = particle.get('position').vector;
vertex.set(position.x, position.y, position.z);
}).pushTo(this._emitter);
return this; // for chained API
/////////////////////////////////////////////////////////////////
// Internal Functions //
/////////////////////////////////////////////////////////////////
function defaultParticleSystem(emitter){
var geometry = new THREE.Geometry();
for( var i = 0; i < emitter.nParticles(); i++ ){
geometry.vertices.push( new THREE.Vector3() );
}
var material = new THREE.ParticleBasicMaterial({
size : 5,
sizeAttenuation : true,
color : 0xE01B6A,
map : generateTexture(),
blending : THREE.AdditiveBlending,
depthWrite : false,
transparent : true
});
var particleSystem = new THREE.ParticleSystem(geometry, material);
particleSystem.dynamic = true;
particleSystem.sortParticles = true;
return particleSystem;
}
function generateTexture(size){
size = size || 128;
var canvas = document.createElement( 'canvas' );
var context = canvas.getContext( '2d' );
canvas.width = canvas.height = size;
var gradient = context.createRadialGradient( canvas.width/2, canvas.height /2, 0, canvas.width /2, canvas.height /2, canvas.width /2 );
gradient.addColorStop( 0 , 'rgba(255,255,255,1)' );
gradient.addColorStop( 0.5, 'rgba(255,255,255,1)' );
gradient.addColorStop( 0.7, 'rgba(128,128,128,1)' );
gradient.addColorStop( 1 , 'rgba(0,0,0,1)' );
context.beginPath();
context.arc(size/2, size/2, size/2, 0, Math.PI*2, false);
context.closePath();
context.fillStyle = gradient;
//context.fillStyle = 'rgba(128,128,128,1)';
context.fill();
var texture = new THREE.Texture( canvas );
texture.needsUpdate = true;
return texture;
}
};
Fireworks.Emitter.prototype.bindTriggerDomEvents = function(domElement){
var tmp = new Fireworks.BindTriggerDomEvents(this, domElement);
return this; // for chained API
}
Fireworks.BindTriggerDomEvents = function(emitter, domElement){
this._domElement= domElement || document.body;
// bind mouse event
this._onMouseDown = function(){ emitter.effect('spawner').opts.start(); };
this._onMouseUp = function(){ emitter.effect('spawner').opts.stop(); };
this._domElement.addEventListener('mousedown' , this._onMouseDown );
this._domElement.addEventListener('mouseup' , this._onMouseUp );
// change emitter intensity on mousewheel
this._onMouseWheel = function(event){
var intensity = emitter.intensity();
intensity += event.wheelDelta < 0 ? -0.05 : 0.05;
intensity = Math.max(intensity, 0);
intensity = Math.min(intensity, 1);
emitter.intensity(intensity);
};
this._domElement.addEventListener('mousewheel' , this._onMouseWheel );
}
Fireworks.BindTriggerDomEvents.prototype.destroy = function(){
this._domElement.removeEventListener('mousedown' , this._onMouseDown );
this._domElement.removeEventListener('mouseup' , this._onMouseUp );
this._domElement.removeEventListener('mousewheel' , this._onMouseWheel );
};
Fireworks.DatGui4Emitter = function(emitter){
var gui = new dat.GUI();
var effects = emitter.effects();
effects.forEach(function(effect, idx){
var effectName = effect.name || "effect-"+idx;
var opts = effect.opts || {};
var keys = Object.keys(opts).filter(function(key){
if( opts[key] instanceof Fireworks.Vector ) return true;
if( typeof(opts[key]) === 'object' ) return false;
return true;
});
if( keys.length ){
var folder = gui.addFolder('Effect: '+effectName);
keys.forEach(function(key){
if( opts[key] instanceof Fireworks.Vector ){
folder.add(opts[key], 'x').name(key+"X");
folder.add(opts[key], 'y').name(key+"Y");
folder.add(opts[key], 'z').name(key+"Z");
}else{
folder.add(opts, key);
}
});
}
});
// return the built gui
return gui;
};Fireworks.ProceduralTextures = {};
Fireworks.ProceduralTextures.buildTexture = function(size)
{
size = size || 150;
var canvas = document.createElement( 'canvas' );
var context = canvas.getContext( '2d' );
canvas.width = canvas.height = size;
var gradient = context.createRadialGradient( canvas.width/2, canvas.height /2, 0, canvas.width /2, canvas.height /2, canvas.width /2 );
gradient.addColorStop( 0 , 'rgba(255,255,255,1)' );
gradient.addColorStop( 0.5, 'rgba(255,255,255,1)' );
gradient.addColorStop( 0.7, 'rgba(128,128,128,1)' );
gradient.addColorStop( 1 , 'rgba(0,0,0,1)' );
context.beginPath();
context.arc(size/2, size/2, size/2, 0, Math.PI*2, false);
context.closePath();
context.fillStyle = gradient;
//context.fillStyle = 'rgba(128,128,128,1)';
context.fill();
var texture = new THREE.Texture( canvas );
texture.needsUpdate = true;
return texture;
}
(C) Æliens
04/09/2009
You may not copy or print any of this material without explicit permission of the author or the publisher.
In case of other copyright issues, contact the author.