1 /**
  2  * The Render Engine
  3  * AccumulatorParticleEngine
  4  *
  5  * @fileoverview An extension to the standard particle engine which accumulates and
  6  *    fades out particles for a more dramatic effect.
  7  *
  8  * @author: Brett Fattori (brettf@renderengine.com)
  9  *
 10  * @author: $Author: bfattori@gmail.com $
 11  * @version: $Revision: 1571 $
 12  *
 13  * Copyright (c) 2011 Brett Fattori (brettf@renderengine.com)
 14  *
 15  * Permission is hereby granted, free of charge, to any person obtaining a copy
 16  * of this software and associated documentation files (the "Software"), to deal
 17  * in the Software without restriction, including without limitation the rights
 18  * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
 19  * copies of the Software, and to permit persons to whom the Software is
 20  * furnished to do so, subject to the following conditions:
 21  *
 22  * The above copyright notice and this permission notice shall be included in
 23  * all copies or substantial portions of the Software.
 24  *
 25  * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
 26  * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
 27  * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
 28  * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
 29  * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
 30  * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
 31  * THE SOFTWARE.
 32  *
 33  */
 34 
 35 // The class this file defines and its required classes
 36 R.Engine.define({
 37     "class":"R.particles.AccumulatorParticleEngine",
 38     "requires":[
 39         "R.particles.ParticleEngine",
 40         "R.struct.Container",
 41         "R.rendercontexts.CanvasContext",
 42         "R.util.RenderUtil"
 43     ]
 44 });
 45 
 46 /**
 47  * @class An updated particle engine with an accumulator buffer so that
 48  *        effects such as bloom, glow, and tail can be achieved.  A note
 49  *        of caution, using the accumulator particle engine <em>will be
 50  *        slower</em> compared with the basic particle engine.
 51  *        <p/>
 52  *        Because of the effect used by the accumulator particle engine,
 53  *        background imagery will be darkened slightly.
 54  *
 55  * @extends R.particles.ParticleEngine
 56  * @constructor
 57  * @description Create a particle engine
 58  */
 59 R.particles.AccumulatorParticleEngine = function () {
 60     return R.particles.ParticleEngine.extend(/** @scope R.particles.AccumulatorParticleEngine.prototype */{
 61 
 62         accumulator:null, // The accumulated frame
 63         fadeRate:0, // The rate at which particles fade out
 64         blur:false,
 65         radius:1,
 66         hasBackground:false,
 67 
 68         /** @private */
 69         constructor:function (fadeRate) {
 70             this.base("AccumulatorParticleEngine");
 71             this.accumulator = null;
 72             this.fadeRate = fadeRate || 0.5;
 73             this.blur = false;
 74             this.radius = 1;
 75             this.hasBackground = false;
 76         },
 77 
 78         /**
 79          * Destroy the particle engine
 80          */
 81         destroy:function () {
 82             this.accumulator.destroy();
 83             this.base();
 84         },
 85 
 86         /**
 87          * Releases the particle engine back into the pool.
 88          */
 89         release:function () {
 90             this.base();
 91             this.accumulator = null;
 92             this.fadeRate = 0;
 93             this.blur = false;
 94             this.radius = 1;
 95             this.hasBackground = false;
 96         },
 97 
 98         /**
 99          * Set the rate at which the particles fade out
100          * @param fadeRate {Number} A value between 0 and 1
101          */
102         setFadeRate:function (fadeRate) {
103             this.fadeRate = fadeRate;
104             this.accumulator = null;
105         },
106 
107         /**
108          * Enable blurring of the particles in the accumulator
109          * @param state {Boolean} <code>true</code> to enable (default: false)
110          */
111         setBlur:function (state) {
112             this.blur = state;
113         },
114 
115         /**
116          * Set the blurring radius around the pixel.  Higher numbers result in lower frame rates.
117          * @param radius {Number} The radius of the blur (default: 1)
118          */
119         setBlurRadius:function (radius) {
120             this.radius = radius;
121         },
122 
123         /**
124          * Set this value to <code>true</code> if the particle engine is atop a background image.
125          * This will have the effect of slightly darkening the background image.  If the background
126          * is solid black, you can set this to <code>false</code>.
127          * @param state {Boolean} The background state
128          */
129         setBackgroundState:function (state) {
130             this.hasBackground = state;
131         },
132 
133         /**
134          * Clear the accumulator
135          */
136         reset:function () {
137             if (this.accumulator) {
138                 this.accumulator.reset();
139             }
140             this.base();
141         },
142 
143         /**
144          * Update the particles within the render context.
145          *
146          * @param renderContext {R.rendercontexts.AbstractRenderContext} The context the particles will be rendered within.
147          * @param time {Number} The global time within the engine.
148          * @param dt {Number} The delta between the world time and the last time the world was updated
149          *          in milliseconds.
150          */
151         update:function (renderContext, time, dt) {
152             if (R.Engine.options.disableParticleEngine) {
153                 return;
154             }
155 
156             // Is there an accumulator already?
157             if (!this.accumulator) {
158                 // Create the accumulator buffer, at the size of the renderContext
159                 this.accumulator = R.rendercontexts.CanvasContext.create("APEContext",
160                     renderContext.getViewport().w, renderContext.getViewport().h);
161             }
162 
163             if (!this.blur) {
164                 // Fade the accumulator at a set rate
165                 this.accumulator.get2DContext().globalAlpha = this.fadeRate;
166                 this.accumulator.get2DContext().globalCompositeOperation = this.hasBackground ? "xor" : "source-atop";
167                 this.accumulator.setFillStyle("rgb(0,0,0)");
168                 this.accumulator.drawFilledRectangle(renderContext.getViewport());
169                 this.accumulator.get2DContext().globalCompositeOperation = "source-over";
170             } else {
171                 var vp = R.math.Rectangle2D.create(renderContext.getViewport()),
172                     ox = vp.x, oy = vp.y;
173                 this.accumulator.get2DContext().globalAlpha = 0.5;
174                 for (var y = -this.radius; y <= this.radius; y++) {
175                     for (var x = -this.radius; x <= this.radius; x++) {
176                         vp.y = oy + y;
177                         vp.x = ox + x;
178                         this.accumulator.drawImage(vp, this.accumulator.getSurface());
179                     }
180                 }
181                 vp.destroy();
182             }
183 
184             this.accumulator.get2DContext().globalAlpha = 1.0;
185 
186             // Render particles to the accumulator
187             this.base(this.accumulator, time, dt);
188 
189             // Render the contents of the accumulator to the render context
190             renderContext.drawImage(renderContext.getViewport(), this.accumulator.getSurface());
191         },
192 
193         /**
194          * Get the properties object for the particle engine
195          * @return {Object}
196          */
197         getProperties:function () {
198             var self = this;
199             var prop = this.base(self);
200             return $.extend(prop, {
201                 "FadeRate":[function () {
202                     return self.fadeRate;
203                 },
204                     null, false]
205             });
206         }
207 
208 
209     }, /** @scope R.particles.AccumulatorParticleEngine.prototype */{
210         /**
211          * Get the class name of this object
212          *
213          * @return {String} "R.particles.AccumulatorParticleEngine"
214          */
215         getClassName:function () {
216             return "R.particles.AccumulatorParticleEngine";
217         }
218     });
219 };