/* <copyright> This file contains proprietary software owned by Motorola Mobility, Inc.<br/> No rights, expressed or implied, whatsoever to this software are provided by Motorola Mobility, Inc. hereunder.<br/> (c) Copyright 2011 Motorola Mobility, Inc. All Rights Reserved. </copyright> */ var RDGE = RDGE || {}; RDGE.particle = function (def, id) { this.id = id; this.def = def; if (this.def.numFrames == undefined) { if (this.def.textureSize && this.def.frameSize) { this.def.numFrames = (this.def.textureSize[0] / this.def.frameSize[0]) * (this.def.textureSize[1] / this.def.frameSize[1]); } else { this.def.numFrames = 0; } } this.pos = RDGE.vec3.zero(); this.delta = RDGE.vec3.zero(); this.rotate = 0.0; this.age = 0.0; this.lifespan = 0.0; this.velocity = RDGE.vec3.zero(); this.gravity = RDGE.vec3.zero(); this.frame = 0; this.frameCount = 0; this.lastPos = RDGE.vec3.zero(); this.state = 0; this.hide = false; this.color = RDGE.vec4.zero(); this.randomize = function (min, max) { return min + (max - min) * Math.random(); }; this.rate = this.randomize(-1.0, 1.0); this.randomize3 = function (min, max) { return [this.randomize(min[0], max[0]), this.randomize(min[1], max[1]), this.randomize(min[2], max[2])]; }; this.spawn = function (spawnMatrix) { if (this.def.initialframe == undefined) { this.frame = this.id % this.def.numFrames; } else { this.frame = this.randomize(this.def.initialframe[0], this.def.initialframe[1]); } this.pos = this.randomize3(this.def.initialpos[0], this.def.initialpos[1]); if (this.def.worldSpace) { // calculate the initial position in world space. this.pos = RDGE.mat4.transformPoint(spawnMatrix, this.pos); } // all other values are assumed to be defined in local or world space depending on // the particles worldSpace designation. var toRadians = Math.PI / 180.0; if (this.def.initialsize) { this.size = this.randomize(this.def.initialsize[0], this.def.initialsize[1]); } else { this.size = 1.0; } this.velocity = this.randomize3(this.def.initialvel[0], this.def.initialvel[1]); this.gravity = [this.def.gravity[0], this.def.gravity[1], this.def.gravity[2]]; this.rotate = this.randomize(this.def.initialrot[0] * toRadians, this.def.initialrot[1] * toRadians); this.lifespan = this.randomize(this.def.lifespan[0], this.def.lifespan[1]); this.age = 0.0; this.delta = [0.0, 0.0, 0.0]; this.lastPos = RDGE.vec3.add(this.pos, RDGE.vec3.scale(this.velocity, -1.0 / 30.0)); this.color = [1, 1, 1, 1]; //RDGE.vec4.random( [0.0, 0.0, 0.0, 1.0], [1.0, 1.0, 1.0, 1.0] ); }; }; // double buffered array utility class RDGE.DoubleBuffer = function (arrType, size) { this.buffer = {}; this.buffer[0] = new arrType(size); this.buffer[1] = new arrType(size); this.bufferIndex = 0; this.flip = function () { this.bufferIndex = 1 - this.bufferIndex; } this.front = function () { return this.buffer[this.bufferIndex]; } this.back = function () { return this.buffer[1 - this.bufferIndex]; } }; // cycling buffer RDGE.particleBuffer = function (pdef, emitter, size) { var renderer = RDGE.globals.engine.getContext().renderer; var ctx = renderer.ctx; s_particleShader = new RDGE.jshader(); s_particleShader.def = { 'shaders': { 'defaultVShader': "assets/shaders/particle_vshader.glsl", 'defaultFShader': "assets/shaders/particle_fshader.glsl" }, 'techniques': { 'defaultTechnique': [{ 'vshader': 'defaultVShader', 'fshader': 'defaultFShader', 'attributes': { 'a_pos': { 'type': 'vec4' }, 'a_posId': { 'type': 'float' }, 'a_rotation': { 'type': 'float' }, 'a_size': { 'type': 'float' }, 'a_color': { 'type': 'vec4' } }, 'params': { 'u_projMatrix': { 'type': 'mat4' }, 'u_viewMatrix': { 'type': 'mat4' }, 'u_worldMatrix': { 'type': 'mat4' }, 'u_particleSizeX': { 'type': 'vec4' }, 'u_particleSizeY': { 'type': 'vec4' }, 'u_particleRot': { 'type': 'vec4' }, 'u_particleColors': { 'type': 'mat4' }, 'u_textureSize': { 'type': 'vec2' }, 'u_frameSize': { 'type': 'vec2' }, 's_texture0': { 'type': 'tex2d' } // 's_texture1' : { 'type' : 'tex2d' } } }] } } s_particleShader.init(); s_particleTextures = {}; this.shader = s_particleShader; this.owner = emitter; if (pdef.texture && s_particleTextures[pdef.texture] == undefined) { s_particleTextures[pdef.texture] = renderer.createTexture(pdef.texture); if (!pdef.textureSize || !pdef.frameSize) { pdef.textureSize = []; pdef.textureSize[0] = s_particleTextures[pdef.texture].image.width; pdef.textureSize[1] = s_particleTextures[pdef.texture].image.height; } if (!pdef.frameSize) { pdef.frameSize = []; pdef.frameSize[0] = s_particleTextures[pdef.texture].image.width; pdef.frameSize[1] = s_particleTextures[pdef.texture].image.height; } } if (pdef.texture2 && s_particleTextures[pdef.texture2] == undefined) { s_particleTextures[pdef.texture2] = renderer.createTexture(pdef.texture2); } this.texture = s_particleTextures[pdef.texture]; this.texture2 = s_particleTextures[pdef.texture2]; this.bounds = {}; this.bounds.min = RDGE.vec3.zero(); this.bounds.max = RDGE.vec3.zero(); this.srcBlend = pdef.srcBlend; this.dstBlend = pdef.dstBlend; this.particles = new Array(); this.posBuffer = new RDGE.DoubleBuffer(Float32Array, 16 * size); // 4 positions per particle, 4 components per position (particle age in w) this.posIdBuffer = new Float32Array(4 * size); this.sizeBuffer = new RDGE.DoubleBuffer(Float32Array, 4 * size); this.rotBuffer = new RDGE.DoubleBuffer(Float32Array, 4 * size); this.colorBuffer = new RDGE.DoubleBuffer(Float32Array, 16 * size); this.indexBuffer = new RDGE.DoubleBuffer(Uint16Array, 6 * size); this.indexBuffer.front().numIndices = 0; this.indexBuffer.back().numIndices = 0; for (i = 0; i < size; ++i) { this.particles.push(new RDGE.particle(pdef, i)); // initialize double buffers. // the first pass will init the front buffers. // the second pass will init the back buffers. for (j = 0; j < 2; ++j) { // init position buffer var pfb = this.posBuffer.front(); var i16 = i * 16; for (j = 0; j < 4; ++j) { var cmpBaseIndex = i16 + j * 4; pfb[cmpBaseIndex + 0] = 0; pfb[cmpBaseIndex + 1] = 0; pfb[cmpBaseIndex + 2] = 0; pfb[cmpBaseIndex + 3] = 1; } // init rotation buffer var i4 = i * 4; var rfb = this.rotBuffer.front(); rfb[i4 + 0] = 0; rfb[i4 + 1] = 0; rfb[i4 + 2] = 0; rfb[i4 + 3] = 0; // init rotation buffer var i4 = i * 4; var sfb = this.sizeBuffer.front(); rfb[i4 + 0] = 1; rfb[i4 + 1] = 1; rfb[i4 + 2] = 1; rfb[i4 + 3] = 1; // init color buffer var i4 = i * 4; var cfb = this.colorBuffer.front(); cfb[i4] = 0xFFFFFF; // init index buffer var ifb = this.indexBuffer.front(); var i6 = i * 6; ifb[i6 + 0] = i4 + 1; ifb[i6 + 1] = i4 + 0; ifb[i6 + 2] = i4 + 3; ifb[i6 + 3] = i4 + 1; ifb[i6 + 4] = i4 + 3; ifb[i6 + 5] = i4 + 2; // flip buffers this.posBuffer.flip(); this.rotBuffer.flip(); this.indexBuffer.flip(); } var i4 = i * 4; this.posIdBuffer[i4 + 0] = 0; this.posIdBuffer[i4 + 1] = 1; this.posIdBuffer[i4 + 2] = 2; this.posIdBuffer[i4 + 3] = 3; } this.posBufferObject = ctx.createBuffer(); ctx.bindBuffer(ctx.ARRAY_BUFFER, this.posBufferObject); ctx.bufferData(ctx.ARRAY_BUFFER, this.posBuffer.front(), ctx.DYNAMIC_DRAW); this.posIdBufferObject = ctx.createBuffer(); ctx.bindBuffer(ctx.ARRAY_BUFFER, this.posIdBufferObject); ctx.bufferData(ctx.ARRAY_BUFFER, this.posIdBuffer, ctx.DYNAMIC_DRAW); this.rotBufferObject = ctx.createBuffer(); ctx.bindBuffer(ctx.ARRAY_BUFFER, this.rotBufferObject); ctx.bufferData(ctx.ARRAY_BUFFER, this.rotBuffer.front(), ctx.DYNAMIC_DRAW); this.sizeBufferObject = ctx.createBuffer(); ctx.bindBuffer(ctx.ARRAY_BUFFER, this.sizeBufferObject); ctx.bufferData(ctx.ARRAY_BUFFER, this.sizeBuffer.front(), ctx.DYNAMIC_DRAW); this.colorBufferObject = ctx.createBuffer(); ctx.bindBuffer(ctx.ARRAY_BUFFER, this.colorBufferObject); ctx.bufferData(ctx.ARRAY_BUFFER, this.colorBuffer.front(), ctx.DYNAMIC_DRAW); this.indexBufferObject = ctx.createBuffer(); ctx.bindBuffer(ctx.ELEMENT_ARRAY_BUFFER, this.indexBufferObject); ctx.bufferData(ctx.ELEMENT_ARRAY_BUFFER, this.indexBuffer.front(), ctx.DYNAMIC_DRAW); this.start = 0; this.end = 0; this.cap = size; this.kill = function () { this.start++; if (this.start > (this.cap - 1)) this.start = 0; }; this.emit = function (parent) { this.end++; if (this.end >= this.cap) { this.end = 0; } if (this.end == this.start) { this.kill(); } this.particles[this.end].spawn(this.owner.world); }; this.sync = function () { this.posBuffer.flip(); this.rotBuffer.flip(); this.sizeBuffer.flip(); this.colorBuffer.flip(); this.indexBuffer.flip(); // update the back buffer. var dstPosBuffer = this.posBuffer.back(); var dstRotBuffer = this.rotBuffer.back(); var dstSizeBuffer = this.sizeBuffer.back(); var dstColorBuffer = this.colorBuffer.back(); var dstIdxBuffer = this.indexBuffer.back(); var bmin = this.bounds.min; var bmax = this.bounds.max; bmin[0] = 1e10; bmin[1] = 1e10; bmin[2] = 1e10; bmax[0] = -1e10; bmax[1] = -1e10; bmax[2] = -1e10; var numIndices = 0; var i = this.start; while (1) { var x, y, z, w; var p = this.particles[i]; var age = (p.age / p.lifespan); // normalized age // combine frame number and age and store in w. // to decode : // frame = floor( w ); // age = fract( w ); var pw = Math.min(age, 0.999) + Math.floor(p.frame); if (age < 1.0) { // || (pdef.persist != undefined && pdef.persist == true)) { var px = p.pos[0]; var py = p.pos[1]; var pz = p.pos[2]; if (px > bmax[0]) { bmax[0] = px; } if (px > bmax[1]) { bmax[1] = py; } if (px > bmax[2]) { bmax[2] = pz; } if (px < bmin[0]) { bmin[0] = px; } if (px < bmin[1]) { bmin[1] = py; } if (px < bmin[2]) { bmin[2] = pz; } var i16 = i * 16; for (j = 0; j < 4; ++j) { var cmpBaseIndex = i16 + j * 4; dstPosBuffer[cmpBaseIndex + 0] = px; dstPosBuffer[cmpBaseIndex + 1] = py; dstPosBuffer[cmpBaseIndex + 2] = pz; dstPosBuffer[cmpBaseIndex + 3] = pw; } var i4 = i * 4; var r = 0.0; if (pdef.velocityAligned) { r = Math.atan2(p.delta[0], p.delta[1]); } dstRotBuffer[i4 + 0] = p.rotate + r; dstRotBuffer[i4 + 1] = p.rotate + r; dstRotBuffer[i4 + 2] = p.rotate + r; dstRotBuffer[i4 + 3] = p.rotate + r; dstSizeBuffer[i4 + 0] = p.size; dstSizeBuffer[i4 + 1] = p.size; dstSizeBuffer[i4 + 2] = p.size; dstSizeBuffer[i4 + 3] = p.size; var i16 = i * 16; for (j = 0; j < 4; ++j) { var cmpBaseIndex = i16 + j * 4; dstColorBuffer[cmpBaseIndex + 0] = p.color[0]; dstColorBuffer[cmpBaseIndex + 1] = p.color[1]; dstColorBuffer[cmpBaseIndex + 2] = p.color[2]; dstColorBuffer[cmpBaseIndex + 3] = p.color[3]; } dstIdxBuffer[numIndices + 0] = i4 + 1; dstIdxBuffer[numIndices + 1] = i4 + 0; dstIdxBuffer[numIndices + 2] = i4 + 3; dstIdxBuffer[numIndices + 3] = i4 + 1; dstIdxBuffer[numIndices + 4] = i4 + 2; dstIdxBuffer[numIndices + 5] = i4 + 3; numIndices += 6; } if (i == this.end) { break; } i++; if (i == this.cap && this.end != this.cap) { i = 0; } } dstIdxBuffer.numIndices = numIndices; if (!pdef.worldSpace) { // calculate the world space bounds from local space. // first transform our local space min-max coordinates to world space. var bminW = RDGE.mat4.mul(bmin, this.owner.world); var bmaxW = RDGE.mat4.mul(bmax, this.owner.world); // now calculate axis aligned bounds based on the world space coordinates. if (bmaxW[0] > bmax[0]) { bmax[0] = bmaxW[0]; } if (bmaxW[1] > bmax[1]) { bmax[1] = bmaxW[1]; } if (bmaxW[2] > bmax[2]) { bmax[2] = bmaxW[2]; } if (bminW[0] < bmin[0]) { bmin[0] = bminW[0]; } if (bminW[1] < bmin[1]) { bmin[1] = bminW[1]; } if (bminW[2] < bmin[2]) { bmin[2] = bminW[2]; } } }; this.update = function (dt, movers) { var i = this.start; if (pdef.persist != undefined && pdef.persist == false) { while (1) { var p = this.particles[i]; if (p.age < p.lifespan) break; this.kill(); if (i == this.end) { break; } i++; if (i == this.cap && this.end != this.cap) { i = 0; } } } i = this.start; while (1) { var p = this.particles[i]; var j = movers.length; while (j) { movers[--j].move(p, dt); } if (i == this.end) { break; } i++; if (i == this.cap && this.end != this.cap) { i = 0; } } this.sync(); }; this.render = function () { var renderer = RDGE.globals.engine.getContext().renderer; var ctx = RDGE.globals.engine.getContext().renderer.ctx; // draw using the front buffer. var srcPosBuffer = this.posBuffer.front(); var srcRotBuffer = this.rotBuffer.front(); var srcSizeBuffer = this.sizeBuffer.front(); var srcColBuffer = this.colorBuffer.front(); var srcIdxBuffer = this.indexBuffer.front(); if (srcIdxBuffer.numIndices > 0) { // set modelview matrix var shaderparms = this.shader.defaultTechnique; var activeCam = renderer.cameraManager().getActiveCamera(); if (pdef.worldSpace) { shaderparms.u_viewMatrix.set(activeCam.view); shaderparms.u_worldMatrix.set(RDGE.mat4.identity()); } else { shaderparms.u_viewMatrix.set(activeCam.view); shaderparms.u_worldMatrix.set(this.owner.world); // shaderparms.u_mvMatrix.set(RDGE.mat4.mul(this.owner.world, activeCam.view)); } shaderparms.u_projMatrix.set(activeCam.proj); shaderparms.u_particleSizeX.set(pdef.sizeX); shaderparms.u_particleSizeY.set(pdef.sizeY); shaderparms.u_particleRot.set(RDGE.vec4.scale(pdef.rotation, Math.PI / 180.0)); shaderparms.u_particleColors.set(RDGE.mat4.transpose(pdef.colors)); shaderparms.u_textureSize.set(pdef.textureSize); shaderparms.u_frameSize.set(pdef.frameSize); shaderparms.s_texture0.set(this.texture); // shaderparms.s_texture1.set(null); var passCount = this.shader.begin(); for (passIdx = 0; passIdx < passCount; ++passIdx) { this.shader.beginPass(passIdx); // setup render states if (!pdef.depthTest) { ctx.disable(ctx.DEPTH_TEST); } ctx.enable(ctx.BLEND); ctx.blendFunc(ctx[pdef.srcBlend], ctx[pdef.dstBlend]); ctx.enableVertexAttribArray(0); ctx.enableVertexAttribArray(1); ctx.enableVertexAttribArray(2); ctx.enableVertexAttribArray(3); ctx.enableVertexAttribArray(4); // update position buffer ctx.bindBuffer(ctx.ARRAY_BUFFER, this.posBufferObject); ctx.bufferSubData(ctx.ARRAY_BUFFER, 0, srcPosBuffer); ctx.bindBuffer(ctx.ARRAY_BUFFER, this.posBufferObject); ctx.vertexAttribPointer(0, 4, ctx.FLOAT, false, 0, 0); // position id buffer ctx.bindBuffer(ctx.ARRAY_BUFFER, this.posIdBufferObject); ctx.vertexAttribPointer(1, 1, ctx.FLOAT, false, 0, 0); // update rotation buffer ctx.bindBuffer(ctx.ARRAY_BUFFER, this.rotBufferObject); ctx.bufferSubData(ctx.ARRAY_BUFFER, 0, srcRotBuffer); ctx.bindBuffer(ctx.ARRAY_BUFFER, this.rotBufferObject); ctx.vertexAttribPointer(2, 1, ctx.FLOAT, false, 0, 0); // update size buffer ctx.bindBuffer(ctx.ARRAY_BUFFER, this.sizeBufferObject); ctx.bufferSubData(ctx.ARRAY_BUFFER, 0, srcSizeBuffer); ctx.bindBuffer(ctx.ARRAY_BUFFER, this.sizeBufferObject); ctx.vertexAttribPointer(3, 1, ctx.FLOAT, false, 0, 0); // update color buffer ctx.bindBuffer(ctx.ARRAY_BUFFER, this.colorBufferObject); ctx.bufferSubData(ctx.ARRAY_BUFFER, 0, srcColBuffer); ctx.bindBuffer(ctx.ARRAY_BUFFER, this.colorBufferObject); ctx.vertexAttribPointer(4, 4, ctx.FLOAT, false, 0, 0); // update index buffer ctx.bindBuffer(ctx.ELEMENT_ARRAY_BUFFER, this.indexBufferObject); ctx.bufferSubData(ctx.ELEMENT_ARRAY_BUFFER, 0, srcIdxBuffer); // draw ctx.drawElements(ctx.TRIANGLES, srcIdxBuffer.numIndices, ctx.UNSIGNED_SHORT, 0); // restore render states ctx.disable(ctx.BLEND); if (!pdef.depthTest) { ctx.enable(ctx.DEPTH_TEST); } ctx.blendFunc(ctx.ONE, ctx.ZERO); this.shader.endPass(); } this.shader.end(); } }; }; particleMoverDefault = function () { this.move = function (particle, dt) { particle.age += dt; if (particle.def.persist) { if (particle.def.cycle && particle.age > particle.lifespan * 0.99) { particle.age -= particle.lifespan * 0.99; } else { particle.age = Math.min(particle.age, particle.lifespan * 0.99); } } else { particle.age = Math.min(particle.age, particle.lifespan); } if (particle.def.frameRate > 0.0) { particle.frame += particle.def.frameRate * dt; //Math.min( particle.def.numFrames - 1, particle.frame + particle.def.frameRate * dt ); if (particle.def.frameLoop) { particle.frame = particle.frame % particle.def.numFrames; } else { particle.frame = Math.min(particle.def.numFrames - 1, particle.frame) } } var oldPos = particle.pos; var pg = particle.gravity; var pv = particle.velocity; var pp = particle.pos; var pd = particle.delta; pv[0] += pg[0] * dt; pv[1] += pg[1] * dt; pv[2] += pg[2] * dt; pp[0] = pp[0] + pv[0] * dt; pp[1] = pp[1] + pv[1] * dt; pp[2] = pp[2] + pv[2] * dt; pd[0] = pp[0] - oldPos[0]; pd[1] = pp[1] - oldPos[1]; pd[2] = pp[2] - oldPos[2]; // particle turbulence if (particle.def.turbulence) { var tMin = particle.def.turbulence[0]; var tMax = particle.def.turbulence[1]; var tx = Math.random() * (tMax[0] - tMin[0]) + tMin[0]; var ty = Math.random() * (tMax[1] - tMin[1]) + tMin[1]; var tz = Math.random() * (tMax[2] - tMin[2]) + tMin[2]; pv[0] += tx; pv[1] += ty; pv[2] += tz; } // particle jitter if (particle.def.jitter) { var jMin = particle.def.jitter[0]; var jMax = particle.def.jitter[1]; var jx = Math.random() * (jMax[0] - jMin[0]) + jMin[0]; var jy = Math.random() * (jMax[1] - jMin[1]) + jMin[1]; var jz = Math.random() * (jMax[2] - jMin[2]) + jMin[2]; pp[0] += jx; pp[1] += jy; pp[2] += jz; } }; }; RDGE.particleEmitter = function (def) { this.def = def; this.world = RDGE.mat4.identity(); this.pbuffer = new RDGE.particleBuffer(this.def.particle, this, this.def.maxParticles); this.movers = new Array(); var creatorFunc = particleMoverDefault; if (def.particle.moverFunc) { creatorFunc = eval(def.particle.moverFunc); } this.movers.push(new creatorFunc()); this.localTime = 0.0; this.lastEmitTime = 0.0; this.emitCounter = 0; this.firstUpdate = true; this.attachToNode = function (node) { this.controller = node; }; this.update = function (dt) { this.localTime += dt; if (this.firstUpdate) { this.lastEmitTime = this.localTime; this.firstUpdate = false; } if (this.def.emit != undefined && this.def.emit == false) { this.lastEmitTime = this.localTime - 1.0 / this.def.emitRate; } // this needs to be handled differently.. var maxParticles = this.def.maxParticles; var emitOnce = this.def.emitOnce; var emitCount = (this.def.emit == undefined || this.def.emit == true) ? Math.floor((this.localTime - this.lastEmitTime) * this.def.emitRate) : 0; while (emitCount--) { if (emitOnce && this.emitCounter++ >= maxParticles) { break; } this.pbuffer.emit(this.world); this.lastEmitTime = this.localTime; } this.pbuffer.update(dt, this.movers); }; }; RDGE.particleSys = function (addr) { loaded = (typeof loaded == 'undefined') ? {} : loaded; this.def = null; this.node = null; this.world = RDGE.mat4.identity(); this.emitters = {}; // load particle system definition at addr this.init = function () { if (this.def == null) { return; } for (e in this.def.emitters) { this.emitters[e] = new RDGE.particleEmitter(this.def.emitters[e]); } this.bounds = {}; this.bounds.min = RDGE.vec3.zero(); this.bounds.max = RDGE.vec3.zero(); }; if (!loaded[addr]) { var request = new XMLHttpRequest(); request.sender = this; request.onreadystatechange = function () { if (request.readyState == 4) { if (request.status == 200 || window.location.href.indexOf("http") == -1) { request.sender.def = eval("(" + request.responseText + ")"); //retrieve result as an JavaScript object loaded[addr] = request.sender.def; request.sender.init(); } else { alert("An error has occured loading particle system."); } } } request.open("GET", addr, true); request.send(null); } else { this.def = loaded[addr]; this.init(); } this.update = function (dt) { if (this.def == null) { return; } var bmin = this.bounds.min; var bmax = this.bounds.max; bmin[0] = 1e10; bmin[1] = 1e10; bmin[2] = 1e10; bmax[0] = -1e10; bmax[1] = -1e10; bmax[2] = -1e10; var parent = this.node ? this.node.world : this.world; for (em in this.emitters) { var emitter = this.emitters[em]; emitter.world = parent; emitter.update(dt); var emin = emitter.pbuffer.bounds.min; var emax = emitter.pbuffer.bounds.max; // calculate a bounds that fits all particles. if (emin[0] < bmin[0]) { bmin[0] = emin[0]; } if (emin[1] < bmin[1]) { bmin[1] = emin[1]; } if (emin[2] < bmin[2]) { bmin[2] = emin[2]; } if (emax[0] > bmax[0]) { bmax[0] = emax[0]; } if (emax[1] > bmax[1]) { bmax[1] = emax[1]; } if (emax[2] > bmax[2]) { bmax[2] = emax[2]; } } }; this.render = function () { if (this.def == null) { return; } for (em in this.emitters) { var pbuffer = this.emitters[em].pbuffer; pbuffer.render(); } }; this.attachToNode = function (node) { this.node = node; }; }; RDGE.g_particleSystemManager = new RDGE.objectManager(); RDGE.g_particleSystemManager.update = function (dt) { var i = this.objects.length - 1; while (i >= 0) { var psys = this.objects[i]; if (psys) { psys.update(dt); } i--; } }; RDGE.g_particleSystemManager.render = function (dt) { var i = 0; while (i < this.objects.length) { var psys = this.objects[i]; if (psys) { psys.render(); } i++; } };