odin
Version:
Node.js Canvas/WebGL Javascript Game Framework
511 lines (357 loc) • 18.6 kB
JavaScript
if (typeof define !== "function") {
var define = require("amdefine")(module);
}
define([
"odin/base/object_pool",
"odin/base/class",
"odin/math/mathf",
"odin/math/vec3",
"odin/math/color",
"odin/core/enums",
"odin/core/assets/assets",
"odin/core/components/particle_system/tween",
"odin/core/components/particle_system/particle"
],
function(ObjectPool, Class, Mathf, Vec3, Color, Enums, Assets, Tween, Particle) {
"use strict";
var EmitterType = Enums.EmitterType,
PI = Math.PI,
TWO_PI = PI * 2,
random = Math.random,
randInt = Mathf.randInt,
randFloat = Mathf.randFloat,
clampTop = Mathf.clampTop,
sqrt = Math.sqrt,
PARTICLE_POOL = Emitter.PARTICLE_POOL = new ObjectPool(Particle);
function Emitter(opts) {
opts || (opts = {});
Class.call(this);
this.sort = opts.sort != undefined ? opts.sort : true;
this.positionType = opts.positionType != undefined ? opts.positionType : EmitterType.Box;
this.velocityType = opts.velocityType != undefined ? opts.velocityType : EmitterType.Box;
this.material = opts.material != undefined ? opts.material : undefined;
this.positionSpread = opts.positionSpread != undefined ? opts.positionSpread : new Vec3(0.5, 0.5, 0.5);
this.positionRadius = opts.positionRadius != undefined ? opts.positionRadius : 0.5;
this.speed = opts.speed != undefined ? opts.speed : 0;
this.speedSpread = opts.speedSpread != undefined ? opts.speedSpread : 0;
this.particleSystem = undefined;
this.transform = undefined;
this.worldSpace = opts.worldSpace != undefined ? opts.worldSpace : true;
this.position = opts.position != undefined ? opts.position : new Vec3;
this.minEmission = opts.minEmission != undefined ? opts.minEmission : 1;
this.maxEmission = opts.maxEmission != undefined ? opts.maxEmission : 2;
this.minLife = opts.minLife != undefined ? opts.minLife : 1;
this.maxLife = opts.maxLife != undefined ? opts.maxLife : 2;
this.minSize = opts.minSize != undefined ? opts.minSize : 0.1;
this.maxSize = opts.maxSize != undefined ? opts.maxSize : 0.5;
this.sizeTween = new Tween(opts.sizeTween);
this.alphaTween = new Tween(opts.alphaTween);
this.colorTween = new Tween(opts.colorTween);
this.velocity = opts.velocity != undefined ? opts.velocity : new Vec3;
this.velocitySpread = opts.velocitySpread != undefined ? opts.velocitySpread : new Vec3;
this.acceleration = opts.acceleration != undefined ? opts.acceleration : new Vec3;
this.accelerationSpread = opts.accelerationSpread != undefined ? opts.accelerationSpread : new Vec3;
this.angularVelocity = opts.angularVelocity != undefined ? opts.angularVelocity : 0;
this.angularVelocitySpread = opts.angularVelocitySpread != undefined ? opts.angularVelocitySpread : 0;
this.angularAcceleration = opts.angularAcceleration != undefined ? opts.angularAcceleration : 0;
this.angularAccelerationSpread = opts.angularAccelerationSpread != undefined ? opts.angularAccelerationSpread : 0;
this.randomAngle = opts.randomAngle != undefined ? opts.randomAngle : true;
this.emissionRate = opts.emissionRate != undefined ? opts.emissionRate : 1 / 60;
this.color = opts.color != undefined ? opts.color : new Color;
this.colorSpread = opts.colorSpread != undefined ? opts.colorSpread : new Color;
this.time = opts.time != undefined ? opts.time : 0;
this._time = 0;
this.duration = opts.duration != undefined ? opts.duration : 0;
this.loop = opts.loop != undefined ? opts.loop : true;
this.playing = opts.playing != undefined ? opts.playing : true;
this.emitting = opts.emitting != undefined ? opts.emitting : true;
this.particles = [];
this._webgl = {};
}
Class.extend(Emitter);
Emitter.prototype.copy = function(other) {
this.sort = other.sort;
this.positionType = other.positionType;
this.velocityType = other.velocityType;
this.material = other.material;
this.position.copy(other.position);
this.positionSpread.copy(other.positionSpread);
this.positionRadius = other.positionRadius;
this.speed = other.speed;
this.speedSpread = other.speedSpread;
this.worldSpace = other.worldSpace;
this.minEmission = other.minEmission;
this.maxEmission = other.maxEmission;
this.minLife = other.minLife;
this.maxLife = other.maxLife;
this.minSize = other.minSize;
this.maxSize = other.maxSize;
this.sizeTween.copy(other.sizeTween);
this.alphaTween.copy(other.alphaTween);
this.colorTween.copy(other.colorTween);
this.velocity.copy(other.velocity);
this.velocitySpread.copy(other.velocitySpread);
this.acceleration.copy(other.acceleration);
this.accelerationSpread.copy(other.accelerationSpread);
this.angularVelocity = other.angularVelocity;
this.angularVelocitySpread = other.angularVelocitySpread;
this.angularAcceleration = other.angularAcceleration;
this.angularAccelerationSpread = other.angularAccelerationSpread;
this.randomAngle = other.randomAngle;
this.emissionRate = other.emissionRate;
this.color.copy(other.color);
this.colorSpread.copy(other.colorSpread);
this.time = other.time;
this._time = other._time;
this.duration = other.duration;
this.loop = other.loop;
this.playing = other.playing;
this.emitting = other.emitting;
return this;
};
Emitter.prototype.play = function() {
this.time = 0;
this.playing = true;
this.emitting = true;
return this;
};
Emitter.prototype.clear = function() {
var particles = this.particles,
i = particles.length;
this.transform = undefined;
this.time = 0;
this._time = 0;
this.playing = false;
this.emitting = false;
while (i--) PARTICLE_POOL.removeObject(particles[i]);
particles.length = 0;
this._webgl = {};
return this;
};
var VEC = new Vec3;
Emitter.prototype.spawn = function(count) {
var transform = this.transform || (this.transform = this.particleSystem.gameObject.transform || this.particleSystem.gameObject.transform2d),
transformPosition = transform.toWorld(VEC.set(0, 0, 0)),
position = this.position,
positionSpread = this.positionSpread,
positionRadius = this.positionRadius,
speed = this.speed,
speedSpread = this.speedSpread,
particles = this.particles,
numParticle2Ds = particles.length,
worldSpace = this.worldSpace,
randomAngle = this.randomAngle,
color = this.color,
colorSpread = this.colorSpread,
useRandColor = colorSpread.lengthSq() > 0,
velocity = this.velocity,
velocitySpread = this.velocitySpread,
acceleration = this.acceleration,
accelerationSpread = this.accelerationSpread,
angularVelocity = this.angularVelocity,
angularVelocitySpread = this.angularVelocitySpread,
angularAcceleration = this.angularAcceleration,
angularAccelerationSpread = this.angularAccelerationSpread,
minLife = this.minLife,
maxLife = this.maxLife,
minSize = this.minSize,
maxSize = this.maxSize,
positionType = this.positionType,
velocityType = this.velocityType,
limit = clampTop(numParticle2Ds + count, Emitter.MAX_PARTICLES) - numParticle2Ds,
posx, posy, posz, vel, acc, pos, col, x, y, z, len, r, dx, dy, dz, spd, particle;
if (positionType !== EmitterType.Box) {
posx = randFloat(-positionSpread.x, positionSpread.x);
posy = randFloat(-positionSpread.y, positionSpread.y);
posz = randFloat(-positionSpread.z, positionSpread.z);
}
for (; limit--;) {
particle = PARTICLE_POOL.create();
pos = particle.position;
vel = particle.velocity;
acc = particle.acceleration;
col = particle.color;
col.r = color.r;
col.g = color.g;
col.b = color.b;
if (useRandColor) {
col.r += colorSpread.r * random();
col.g += colorSpread.g * random();
col.b += colorSpread.b * random();
col.cnormalize();
}
if (worldSpace) {
pos.x = position.x + transformPosition.x;
pos.y = position.y + transformPosition.y;
pos.z = position.z + transformPosition.z;
} else {
pos.x = position.x;
pos.y = position.y;
pos.z = position.z;
}
if (positionType === EmitterType.Box) {
pos.x += randFloat(-positionSpread.x, positionSpread.x);
pos.y += randFloat(-positionSpread.y, positionSpread.y);
pos.z += randFloat(-positionSpread.z, positionSpread.z);
} else { //EmitterType.Sphere
x = randFloat(-1, 1);
y = randFloat(-1, 1);
z = randFloat(-1, 1);
len = x * x + y * y + z * z;
len = len !== 0 ? 1 / sqrt(len) : len;
pos.x += posx + x * len * positionRadius;
pos.y += posy + y * len * positionRadius;
pos.z += posz + z * len * positionRadius;
}
if (velocityType === EmitterType.Box) {
vel.x = velocity.x + randFloat(-velocitySpread.x, velocitySpread.x);
vel.y = velocity.y + randFloat(-velocitySpread.y, velocitySpread.y);
vel.z = velocity.z + randFloat(-velocitySpread.z, velocitySpread.z);
} else { //EmitterType.Sphere
if (worldSpace) {
dx = pos.x - (position.x + transformPosition.x);
dy = pos.y - (position.y + transformPosition.y);
dz = pos.z - (position.z + transformPosition.z);
} else {
dx = pos.x - position.x;
dy = pos.y - position.y;
dz = pos.z - position.z;
}
spd = speed + randFloat(-speedSpread, speedSpread);
r = dx * dx + dy * dy + dz * dz;
r = r !== 0 ? 1 / sqrt(r) : r;
vel.x = dx * r * spd;
vel.y = dy * r * spd;
vel.z = dz * r * spd;
}
acc.x = acceleration.x + randFloat(-accelerationSpread.x, accelerationSpread.x);
acc.y = acceleration.y + randFloat(-accelerationSpread.y, accelerationSpread.y);
acc.z = acceleration.z + randFloat(-accelerationSpread.z, accelerationSpread.z);
particle.angularVelocity = angularVelocity + randFloat(-angularVelocitySpread, angularVelocitySpread);
particle.angularAcceleration = angularAcceleration + randFloat(-angularAccelerationSpread, angularAccelerationSpread);
particle.alpha = 1;
particle.angle = randomAngle ? random() * TWO_PI : 0;
particle.lifeTime = 0;
particle.life = randFloat(minLife, maxLife);
particle.size = randFloat(minSize, maxSize);
particles.push(particle);
}
};
Emitter.prototype.update = function(dt) {
if (!this.playing) return;
var particles = this.particles,
sizeTween = this.sizeTween,
alphaTween = this.alphaTween,
colorTween = this.colorTween,
sizeTweenUpdate = sizeTween.times.length > 0,
alphaTweenUpdate = alphaTween.times.length > 0,
colorTweenUpdate = colorTween.times.length > 0,
particle, life, count,
i;
this.time += dt;
this._time += dt;
count = this._time / this.emissionRate;
if (this.emitting && count >= 1) {
this._time = 0;
this.spawn(randInt(this.minEmission, this.maxEmission) * (count | 0));
if (!this.loop && this.time > this.duration) this.emitting = false;
}
for (i = particles.length; i--;) {
particle = particles[i];
particle.update(dt);
life = particle.lifeTime / particle.life;
if (sizeTweenUpdate) particle.size = sizeTween.update(life);
if (alphaTweenUpdate) particle.alpha = alphaTween.update(life);
if (colorTweenUpdate) colorTween.update(life, particle.color);
if (life > 1) {
PARTICLE_POOL.removeObject(particle);
particles.splice(i, 1);
}
}
if (!this.emitting && particles.length === 0) this.playing = false;
};
Emitter.prototype.toJSON = function(json) {
json = Class.prototype.toJSON.call(this, json);
json.type = 1;
json.sort = this.sort;
json.positionType = this.positionType;
json.velocityType = this.velocityType;
json.material = this.material ? this.material.name : undefined;
json.position = this.position.toJSON(json.position);
json.positionSpread = this.positionSpread.toJSON(json.positionSpread);
json.positionRadius = this.positionRadius;
json.speed = this.speed;
json.speedSpread = this.speedSpread;
json.worldSpace = this.worldSpace;
json.minEmission = this.minEmission;
json.maxEmission = this.maxEmission;
json.minLife = this.minLife;
json.maxLife = this.maxLife;
json.minSize = this.minSize;
json.maxSize = this.maxSize;
json.sizeTween = this.sizeTween.toJSON(json.sizeTween);
json.alphaTween = this.alphaTween.toJSON(json.alphaTween);
json.colorTween = this.colorTween.toJSON(json.colorTween);
json.velocity = this.velocity.toJSON(json.velocity);
json.velocitySpread = this.velocitySpread.toJSON(json.velocitySpread);
json.acceleration = this.acceleration.toJSON(json.acceleration);
json.accelerationSpread = this.accelerationSpread.toJSON(json.accelerationSpread);
json.angularVelocity = this.angularVelocity;
json.angularAcceleration = this.angularAcceleration;
json.angularVelocitySpread = this.angularVelocitySpread;
json.randomAngle = this.randomAngle;
json.emissionRate = this.emissionRate;
json.color = this.color.toJSON(json.color);
json.colorSpread = this.colorSpread.toJSON(json.colorSpread);
json.time = this.time;
json._time = this._time;
json.duration = this.duration;
json.loop = this.loop;
json.playing = this.playing;
json.emitting = this.emitting;
return json;
};
Emitter.prototype.fromJSON = function(json) {
Class.prototype.fromJSON.call(this, json);
this.sort = json.sort;
this.positionType = json.positionType;
this.velocityType = json.velocityType;
this.material = json.material ? Assets.hash[json.material] : undefined;
this.position.fromJSON(json.position);
this.positionSpread.fromJSON(json.positionSpread);
this.positionRadius = json.positionRadius;
this.speed = json.speed;
this.speedSpread = json.speedSpread;
this.worldSpace = json.worldSpace;
this.minEmission = json.minEmission;
this.maxEmission = json.maxEmission;
this.minLife = json.minLife;
this.maxLife = json.maxLife;
this.minSize = json.minSize;
this.maxSize = json.maxSize;
this.sizeTween.fromJSON(json.sizeTween);
this.alphaTween.fromJSON(json.alphaTween);
this.colorTween.fromJSON(json.colorTween);
this.velocity.fromJSON(json.velocity);
this.velocitySpread.fromJSON(json.velocitySpread);
this.acceleration.fromJSON(json.acceleration);
this.accelerationSpread.fromJSON(json.accelerationSpread);
this.angularVelocity = json.angularVelocity;
this.angularAcceleration = json.angularAcceleration;
this.angularVelocitySpread = json.angularVelocitySpread;
this.randomAngle = json.randomAngle;
this.emissionRate = json.emissionRate;
this.color.fromJSON(json.color);
this.colorSpread.fromJSON(json.colorSpread);
this.time = json.time;
this._time = json._time;
this.duration = json.duration;
this.loop = json.loop;
this.playing = json.playing;
this.emitting = json.emitting;
return this;
};
Emitter.MAX_PARTICLES = 1024;
return Emitter;
}
);