1 /****************************************************************************
  2  Copyright (c) 2010-2012 cocos2d-x.org
  3  Copyright (c) 2008-2010 Ricardo Quesada
  4  Copyright (c) 2011      Zynga Inc.
  5 
  6  http://www.cocos2d-x.org
  7 
  8  Permission is hereby granted, free of charge, to any person obtaining a copy
  9  of this software and associated documentation files (the "Software"), to deal
 10  in the Software without restriction, including without limitation the rights
 11  to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
 12  copies of the Software, and to permit persons to whom the Software is
 13  furnished to do so, subject to the following conditions:
 14 
 15  The above copyright notice and this permission notice shall be included in
 16  all copies or substantial portions of the Software.
 17 
 18  THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
 19  IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
 20  FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
 21  AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
 22  LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
 23  OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
 24  THE SOFTWARE.
 25  ****************************************************************************/
 26 
 27 // ideas taken from:
 28 //   . The ocean spray in your face [Jeff Lander]
 29 //      http://www.double.co.nz/dust/col0798.pdf
 30 //   . Building an Advanced Particle System [John van der Burg]
 31 //      http://www.gamasutra.com/features/20000623/vanderburg_01.htm
 32 //   . LOVE game engine
 33 //      http://love2d.org/
 34 //
 35 //
 36 // Radius mode support, from 71 squared
 37 //      http://particledesigner.71squared.com/
 38 //
 39 // IMPORTANT: Particle Designer is supported by cocos2d, but
 40 // 'Radius Mode' in Particle Designer uses a fixed emit rate of 30 hz. Since that can't be guarateed in cocos2d,
 41 //  cocos2d uses a another approach, but the results are almost identical.
 42 //
 43 
 44 /**
 45  * Shape Mode of Particle Draw
 46  * @constant
 47  * @type Number
 48  */
 49 cc.PARTICLE_SHAPE_MODE = 0;
 50 /**
 51  * Texture Mode of Particle Draw
 52  * @constant
 53  * @type Number
 54  */
 55 cc.PARTICLE_TEXTURE_MODE = 1;
 56 
 57 /**
 58  * Star Shape for ShapeMode of Particle
 59  * @constant
 60  * @type Number
 61  */
 62 cc.PARTICLE_STAR_SHAPE = 0;
 63 /**
 64  * Ball Shape for ShapeMode of Particle
 65  * @constant
 66  * @type Number
 67  */
 68 cc.PARTICLE_BALL_SHAPE = 1;
 69 
 70 /**
 71  * The Particle emitter lives forever
 72  * @constant
 73  * @type Number
 74  */
 75 cc.PARTICLE_DURATION_INFINITY = -1;
 76 
 77 /**
 78  * The starting size of the particle is equal to the ending size
 79  * @constant
 80  * @type Number
 81  */
 82 cc.PARTICLE_START_SIZE_EQUAL_TO_END_SIZE = -1;
 83 
 84 /**
 85  * The starting radius of the particle is equal to the ending radius
 86  * @constant
 87  * @type Number
 88  */
 89 cc.PARTICLE_START_RADIUS_EQUAL_TO_END_RADIUS = -1;
 90 
 91 /**
 92  * Gravity mode (A mode)
 93  * @constant
 94  * @type Number
 95  */
 96 cc.PARTICLE_MODE_GRAVITY = 0;
 97 
 98 /**
 99  * Radius mode (B mode)
100  * @constant
101  * @type Number
102  */
103 cc.PARTICLE_MODE_RADIUS = 1;
104 
105 // tCCPositionType
106 // possible types of particle positions
107 
108 /**
109  * Living particles are attached to the world and are unaffected by emitter repositioning.
110  * @constant
111  * @type Number
112  */
113 cc.PARTICLE_TYPE_FREE = 0;
114 
115 /**
116  * Living particles are attached to the world but will follow the emitter repositioning.<br/>
117  * Use case: Attach an emitter to an sprite, and you want that the emitter follows the sprite.
118  * @constant
119  * @type Number
120  */
121 cc.PARTICLE_TYPE_RELATIVE = 1;
122 
123 /**
124  * Living particles are attached to the emitter and are translated along with it.
125  * @constant
126  * @type Number
127  */
128 cc.PARTICLE_TYPE_GROUPED = 2;
129 
130 /**
131  * Structure that contains the values of each particle
132  * @Class
133  * @Construct
134  * @param {cc.Point} [pos=cc.p(0,0)] Position of particle
135  * @param {cc.Point} [startPos=cc.p(0,0)]
136  * @param {cc.Color} [color= cc.color(0, 0, 0, 255)]
137  * @param {cc.Color} [deltaColor=cc.color(0, 0, 0, 255)]
138  * @param {cc.Size} [size=0]
139  * @param {cc.Size} [deltaSize=0]
140  * @param {Number} [rotation=0]
141  * @param {Number} [deltaRotation=0]
142  * @param {Number} [timeToLive=0]
143  * @param {Number} [atlasIndex=0]
144  * @param {cc.Particle.ModeA} [modeA=]
145  * @param {cc.Particle.ModeA} [modeB=]
146  */
147 cc.Particle = function (pos, startPos, color, deltaColor, size, deltaSize, rotation, deltaRotation, timeToLive, atlasIndex, modeA, modeB) {
148     this.pos = pos ? pos : cc.p(0,0);
149     this.startPos = startPos ? startPos : cc.p(0,0);
150     this.color = color ? color : {r:0, g: 0, b:0, a:255};
151     this.deltaColor = deltaColor ? deltaColor : {r:0, g: 0, b:0, a:255} ;
152     this.size = size || 0;
153     this.deltaSize = deltaSize || 0;
154     this.rotation = rotation || 0;
155     this.deltaRotation = deltaRotation || 0;
156     this.timeToLive = timeToLive || 0;
157     this.atlasIndex = atlasIndex || 0;
158     this.modeA = modeA ? modeA : new cc.Particle.ModeA();
159     this.modeB = modeB ? modeB : new cc.Particle.ModeB();
160     this.isChangeColor = false;
161     this.drawPos = cc.p(0, 0);
162 };
163 
164 /**
165  * Mode A: gravity, direction, radial accel, tangential accel
166  * @Class
167  * @Construct
168  * @param {cc.Point} dir direction of particle
169  * @param {Number} radialAccel
170  * @param {Number} tangentialAccel
171  */
172 cc.Particle.ModeA = function (dir, radialAccel, tangentialAccel) {
173     this.dir = dir ? dir : cc.p(0,0);
174     this.radialAccel = radialAccel || 0;
175     this.tangentialAccel = tangentialAccel || 0;
176 };
177 
178 /**
179  * Mode B: radius mode
180  * @Class
181  * @Construct
182  * @param {Number} angle
183  * @param {Number} degreesPerSecond
184  * @param {Number} radius
185  * @param {Number} deltaRadius
186  */
187 cc.Particle.ModeB = function (angle, degreesPerSecond, radius, deltaRadius) {
188     this.angle = angle || 0;
189     this.degreesPerSecond = degreesPerSecond || 0;
190     this.radius = radius || 0;
191     this.deltaRadius = deltaRadius || 0;
192 };
193 
194 /**
195   * Array of Point instances used to optimize particle updates
196   */
197 cc.Particle.TemporaryPoints = [
198     cc.p(),
199     cc.p(),
200     cc.p(),
201     cc.p()
202 ];
203 
204 /**
205  * <p>
206  *     Particle System base class. <br/>
207  *     Attributes of a Particle System:<br/>
208  *     - emmision rate of the particles<br/>
209  *     - Gravity Mode (Mode A): <br/>
210  *     - gravity <br/>
211  *     - direction <br/>
212  *     - speed +-  variance <br/>
213  *     - tangential acceleration +- variance<br/>
214  *     - radial acceleration +- variance<br/>
215  *     - Radius Mode (Mode B):      <br/>
216  *     - startRadius +- variance    <br/>
217  *     - endRadius +- variance      <br/>
218  *     - rotate +- variance         <br/>
219  *     - Properties common to all modes: <br/>
220  *     - life +- life variance      <br/>
221  *     - start spin +- variance     <br/>
222  *     - end spin +- variance       <br/>
223  *     - start size +- variance     <br/>
224  *     - end size +- variance       <br/>
225  *     - start color +- variance    <br/>
226  *     - end color +- variance      <br/>
227  *     - life +- variance           <br/>
228  *     - blending function          <br/>
229  *     - texture                    <br/>
230  *                                  <br/>
231  *     cocos2d also supports particles generated by Particle Designer (http://particledesigner.71squared.com/).<br/>
232  *     'Radius Mode' in Particle Designer uses a fixed emit rate of 30 hz. Since that can't be guarateed in cocos2d,  <br/>
233  *     cocos2d uses a another approach, but the results are almost identical.<br/>
234  *     cocos2d supports all the variables used by Particle Designer plus a bit more:  <br/>
235  *     - spinning particles (supported when using ParticleSystem)       <br/>
236  *     - tangential acceleration (Gravity mode)                               <br/>
237  *     - radial acceleration (Gravity mode)                                   <br/>
238  *     - radius direction (Radius mode) (Particle Designer supports outwards to inwards direction only) <br/>
239  *     It is possible to customize any of the above mentioned properties in runtime. Example:   <br/>
240  * </p>
241  * @class
242  * @extends cc.Node
243  *
244  * @property {Boolean}              opacityModifyRGB    - Indicate whether the alpha value modify color.
245  * @property {cc.SpriteBatchNode}   batchNode           - Weak reference to the sprite batch node.
246  * @property {Boolean}              active              - <@readonly> Indicate whether the particle system is activated.
247  * @property {Number}               shapeType           - ShapeType of ParticleSystem : cc.PARTICLE_BALL_SHAPE | cc.PARTICLE_STAR_SHAPE.
248  * @property {Number}               atlasIndex          - Index of system in batch node array.
249  * @property {Number}               particleCount       - Current quantity of particles that are being simulated.
250  * @property {Number}               duration            - How many seconds the emitter wil run. -1 means 'forever'
251  * @property {cc.Point}             sourcePos           - Source position of the emitter.
252  * @property {cc.Point}             posVar              - Variation of source position.
253  * @property {Number}               life                - Life of each particle setter.
254  * @property {Number}               lifeVar             - Variation of life.
255  * @property {Number}               angle               - Angle of each particle setter.
256  * @property {Number}               angleVar            - Variation of angle of each particle setter.
257  * @property {Number}               startSize           - Start size in pixels of each particle.
258  * @property {Number}               startSizeVar        - Variation of start size in pixels.
259  * @property {Number}               endSize             - End size in pixels of each particle.
260  * @property {Number}               endSizeVar          - Variation of end size in pixels.
261  * @property {Number}               startSpin           - Start angle of each particle.
262  * @property {Number}               startSpinVar        - Variation of start angle.
263  * @property {Number}               endSpin             - End angle of each particle.
264  * @property {Number}               endSpinVar          - Variation of end angle.
265  * @property {cc.Point}             gravity             - Gravity of the emitter.
266  * @property {cc.Point}             speed               - Speed of the emitter.
267  * @property {cc.Point}             speedVar            - Variation of the speed.
268  * @property {Number}               tangentialAccel     - Tangential acceleration of each particle. Only available in 'Gravity' mode.
269  * @property {Number}               tangentialAccelVar  - Variation of the tangential acceleration.
270  * @property {Number}               tangentialAccel     - Radial acceleration of each particle. Only available in 'Gravity' mode.
271  * @property {Number}               tangentialAccelVar  - Variation of the radial acceleration.
272  * @property {Boolean}              rotationIsDir       - Indicate whether the rotation of each particle equals to its direction. Only available in 'Gravity' mode.
273  * @property {Number}               startRadius         - Starting radius of the particles. Only available in 'Radius' mode.
274  * @property {Number}               startRadiusVar      - Variation of the starting radius.
275  * @property {Number}               endRadius           - Ending radius of the particles. Only available in 'Radius' mode.
276  * @property {Number}               endRadiusVar        - Variation of the ending radius.
277  * @property {Number}               rotatePerS          - Number of degress to rotate a particle around the source pos per second. Only available in 'Radius' mode.
278  * @property {Number}               rotatePerSVar       - Variation of the degress to rotate a particle around the source pos per second.
279  * @property {cc.Color}             startColor          - Start color of each particle.
280  * @property {cc.Color}             startColorVar       - Variation of the start color.
281  * @property {cc.Color}             endColor            - Ending color of each particle.
282  * @property {cc.Color}             endColorVar         - Variation of the end color.
283  * @property {Number}               emissionRate        - Emission rate of the particles.
284  * @property {Number}               emitterMode         - Emitter modes: CCPARTICLE_MODE_GRAVITY: uses gravity, speed, radial and tangential acceleration; CCPARTICLE_MODE_RADIUS: uses radius movement + rotation.
285  * @property {Number}               positionType        - Particles movement type: cc.PARTICLE_TYPE_FREE | cc.PARTICLE_TYPE_GROUPED.
286  * @property {Number}               totalParticles      - Maximum particles of the system.
287  * @property {Boolean}              autoRemoveOnFinish  - Indicate whether the node will be auto-removed when it has no particles left.
288  * @property {cc.Texture2D}         texture             - Texture of Particle System.
289  *
290  * @example
291  *  emitter.radialAccel = 15;
292  *  emitter.startSpin = 0;
293  */
294 cc.ParticleSystem = cc.Node.extend(/** @lends cc.ParticleSystem# */{
295     //***********variables*************
296     _plistFile: "",
297     //! time elapsed since the start of the system (in seconds)
298     _elapsed: 0,
299 
300     _dontTint: false,
301 
302     // Different modes
303     //! Mode A:Gravity + Tangential Accel + Radial Accel
304     modeA: null,
305     //! Mode B: circular movement (gravity, radial accel and tangential accel don't are not used in this mode)
306     modeB: null,
307     _className:"ParticleSystem",
308 
309     //private POINTZERO for ParticleSystem
310     _pointZeroForParticle: cc.p(0, 0),
311 
312     //! Array of particles
313     _particles: null,
314 
315     // color modulate
316     //  BOOL colorModulate;
317 
318     //! How many particles can be emitted per second
319     _emitCounter: 0,
320     //!  particle idx
321     _particleIdx: 0,
322 
323     _batchNode: null,
324     atlasIndex: 0,
325 
326     //true if scaled or rotated
327     _transformSystemDirty: false,
328     _allocatedParticles: 0,
329 
330     //drawMode
331     drawMode: cc.PARTICLE_SHAPE_MODE,
332 
333     //shape type
334     shapeType: cc.PARTICLE_BALL_SHAPE,
335     _isActive: false,
336     particleCount: 0,
337     duration: 0,
338     _sourcePosition: null,
339     _posVar: null,
340     life: 0,
341     lifeVar: 0,
342     angle: 0,
343     angleVar: 0,
344     startSize: 0,
345     startSizeVar: 0,
346     endSize: 0,
347     endSizeVar: 0,
348     _startColor: null,
349     _startColorVar: null,
350     _endColor: null,
351     _endColorVar: null,
352     startSpin: 0,
353     startSpinVar: 0,
354     endSpin: 0,
355     endSpinVar: 0,
356     emissionRate: 0,
357     _totalParticles: 0,
358     _texture: null,
359     _blendFunc: null,
360     _opacityModifyRGB: false,
361     positionType: cc.PARTICLE_TYPE_FREE,
362     autoRemoveOnFinish: false,
363     emitterMode: 0,
364 
365     // quads to be rendered
366     _quads:null,
367     // indices
368     _indices:null,
369 
370     //_VAOname:0,
371     //0: vertex  1: indices
372     _buffersVBO:null,
373     _pointRect:null,
374 
375     _textureLoaded: null,
376     _quadsArrayBuffer:null,
377 
378     /**
379      * <p> return the string found by key in dict. <br/>
380      *    This plist files can be create manually or with Particle Designer:<br/>
381      *    http://particledesigner.71squared.com/<br/>
382      * </p>
383      * @constructor
384      * @param {String|Number} plistFile
385      */
386     ctor:function (plistFile) {
387         cc.Node.prototype.ctor.call(this);
388         this.emitterMode = cc.PARTICLE_MODE_GRAVITY;
389         this.modeA = new cc.ParticleSystem.ModeA();
390         this.modeB = new cc.ParticleSystem.ModeB();
391         this._blendFunc = {src:cc.BLEND_SRC, dst:cc.BLEND_DST};
392 
393         this._particles = [];
394         this._sourcePosition = cc.p(0, 0);
395         this._posVar = cc.p(0, 0);
396 
397         this._startColor = cc.color(255, 255, 255, 255);
398         this._startColorVar = cc.color(255, 255, 255, 255);
399         this._endColor = cc.color(255, 255, 255, 255);
400         this._endColorVar = cc.color(255, 255, 255, 255);
401 
402         this._plistFile = "";
403         this._elapsed = 0;
404         this._dontTint = false;
405         this._pointZeroForParticle = cc.p(0, 0);
406         this._emitCounter = 0;
407         this._particleIdx = 0;
408         this._batchNode = null;
409         this.atlasIndex = 0;
410 
411         this._transformSystemDirty = false;
412         this._allocatedParticles = 0;
413         this.drawMode = cc.PARTICLE_SHAPE_MODE;
414         this.shapeType = cc.PARTICLE_BALL_SHAPE;
415         this._isActive = false;
416         this.particleCount = 0;
417         this.duration = 0;
418         this.life = 0;
419         this.lifeVar = 0;
420         this.angle = 0;
421         this.angleVar = 0;
422         this.startSize = 0;
423         this.startSizeVar = 0;
424         this.endSize = 0;
425         this.endSizeVar = 0;
426 
427         this.startSpin = 0;
428         this.startSpinVar = 0;
429         this.endSpin = 0;
430         this.endSpinVar = 0;
431         this.emissionRate = 0;
432         this._totalParticles = 0;
433         this._texture = null;
434         this._opacityModifyRGB = false;
435         this.positionType = cc.PARTICLE_TYPE_FREE;
436         this.autoRemoveOnFinish = false;
437 
438         this._buffersVBO = [0, 0];
439         this._quads = [];
440         this._indices = [];
441         this._pointRect = cc.rect(0, 0, 0, 0);
442         this._textureLoaded = true;
443 
444         if (cc._renderType === cc._RENDER_TYPE_WEBGL) {
445             this._quadsArrayBuffer = null;
446         }
447 
448         if (!plistFile || typeof(plistFile) === "number") {
449             var ton = plistFile || 100;
450             this.setDrawMode(cc.PARTICLE_TEXTURE_MODE);
451             this.initWithTotalParticles(ton);
452         }else{
453             this.initWithFile(plistFile);
454         }
455     },
456 
457     /**
458      * initializes the indices for the vertices
459      */
460     initIndices:function () {
461         var locIndices = this._indices;
462         for (var i = 0, len = this._totalParticles; i < len; ++i) {
463             var i6 = i * 6;
464             var i4 = i * 4;
465             locIndices[i6 + 0] = i4 + 0;
466             locIndices[i6 + 1] = i4 + 1;
467             locIndices[i6 + 2] = i4 + 2;
468 
469             locIndices[i6 + 5] = i4 + 1;
470             locIndices[i6 + 4] = i4 + 2;
471             locIndices[i6 + 3] = i4 + 3;
472         }
473     },
474 
475     /**
476      * <p> initializes the texture with a rectangle measured Points<br/>
477      * pointRect should be in Texture coordinates, not pixel coordinates
478      * </p>
479      * @param {cc.Rect} pointRect
480      */
481     initTexCoordsWithRect:function (pointRect) {
482         var scaleFactor = cc.contentScaleFactor();
483         // convert to pixels coords
484         var rect = cc.rect(
485             pointRect.x * scaleFactor,
486             pointRect.y * scaleFactor,
487             pointRect.width * scaleFactor,
488             pointRect.height * scaleFactor);
489 
490         var wide = pointRect.width;
491         var high = pointRect.height;
492 
493         if (this._texture) {
494             wide = this._texture.pixelsWidth;
495             high = this._texture.pixelsHeight;
496         }
497 
498         if(cc._renderType === cc._RENDER_TYPE_CANVAS)
499             return;
500 
501         var left, bottom, right, top;
502         if (cc.FIX_ARTIFACTS_BY_STRECHING_TEXEL) {
503             left = (rect.x * 2 + 1) / (wide * 2);
504             bottom = (rect.y * 2 + 1) / (high * 2);
505             right = left + (rect.width * 2 - 2) / (wide * 2);
506             top = bottom + (rect.height * 2 - 2) / (high * 2);
507         } else {
508             left = rect.x / wide;
509             bottom = rect.y / high;
510             right = left + rect.width / wide;
511             top = bottom + rect.height / high;
512         }
513 
514         // Important. Texture in cocos2d are inverted, so the Y component should be inverted
515         var temp = top;
516         top = bottom;
517         bottom = temp;
518 
519         var quads;
520         var start = 0, end = 0;
521         if (this._batchNode) {
522             quads = this._batchNode.textureAtlas.quads;
523             start = this.atlasIndex;
524             end = this.atlasIndex + this._totalParticles;
525         } else {
526             quads = this._quads;
527             start = 0;
528             end = this._totalParticles;
529         }
530 
531         for (var i = start; i < end; i++) {
532             if (!quads[i])
533                 quads[i] = cc.V3F_C4B_T2F_QuadZero();
534 
535             // bottom-left vertex:
536             var selQuad = quads[i];
537             selQuad.bl.texCoords.u = left;
538             selQuad.bl.texCoords.v = bottom;
539             // bottom-right vertex:
540             selQuad.br.texCoords.u = right;
541             selQuad.br.texCoords.v = bottom;
542             // top-left vertex:
543             selQuad.tl.texCoords.u = left;
544             selQuad.tl.texCoords.v = top;
545             // top-right vertex:
546             selQuad.tr.texCoords.u = right;
547             selQuad.tr.texCoords.v = top;
548         }
549     },
550 
551     /**
552      * return weak reference to the cc.SpriteBatchNode that renders the cc.Sprite
553      * @return {cc.ParticleBatchNode}
554      */
555     getBatchNode:function () {
556         return this._batchNode;
557     },
558 
559     /**
560      *  set weak reference to the cc.SpriteBatchNode that renders the cc.Sprite
561      * @param {cc.ParticleBatchNode} batchNode
562      */
563     setBatchNode:function (batchNode) {
564         if (this._batchNode != batchNode) {
565             var oldBatch = this._batchNode;
566 
567             this._batchNode = batchNode; //weak reference
568 
569             if (batchNode) {
570                 var locParticles = this._particles;
571                 for (var i = 0; i < this._totalParticles; i++)
572                     locParticles[i].atlasIndex = i;
573             }
574 
575             // NEW: is self render ?
576             if (!batchNode) {
577                 this._allocMemory();
578                 this.initIndices();
579                 this.setTexture(oldBatch.getTexture());
580                 //if (cc.TEXTURE_ATLAS_USE_VAO)
581                 //    this._setupVBOandVAO();
582                 //else
583                 this._setupVBO();
584             } else if (!oldBatch) {
585                 // OLD: was it self render cleanup  ?
586                 // copy current state to batch
587                 this._batchNode.textureAtlas._copyQuadsToTextureAtlas(this._quads, this.atlasIndex);
588 
589                 //delete buffer
590                 cc._renderContext.deleteBuffer(this._buffersVBO[1]);     //where is re-bindBuffer code?
591 
592                 //if (cc.TEXTURE_ATLAS_USE_VAO)
593                 //    glDeleteVertexArrays(1, this._VAOname);
594             }
595         }
596     },
597 
598     /**
599      * return index of system in batch node array
600      * @return {Number}
601      */
602     getAtlasIndex:function () {
603         return this.atlasIndex;
604     },
605 
606     /**
607      * set index of system in batch node array
608      * @param {Number} atlasIndex
609      */
610     setAtlasIndex:function (atlasIndex) {
611         this.atlasIndex = atlasIndex;
612     },
613 
614     /**
615      * Return DrawMode of ParticleSystem
616      * @return {Number}
617      */
618     getDrawMode:function () {
619         return this.drawMode;
620     },
621 
622     /**
623      * DrawMode of ParticleSystem setter
624      * @param {Number} drawMode
625      */
626     setDrawMode:function (drawMode) {
627         this.drawMode = drawMode;
628     },
629 
630     /**
631      * Return ShapeType of ParticleSystem
632      * @return {Number}
633      */
634     getShapeType:function () {
635         return this.shapeType;
636     },
637 
638     /**
639      * ShapeType of ParticleSystem setter
640      * @param {Number} shapeType
641      */
642     setShapeType:function (shapeType) {
643         this.shapeType = shapeType;
644     },
645 
646     /**
647      * Return ParticleSystem is active
648      * @return {Boolean}
649      */
650     isActive:function () {
651         return this._isActive;
652     },
653 
654     /**
655      * Quantity of particles that are being simulated at the moment
656      * @return {Number}
657      */
658     getParticleCount:function () {
659         return this.particleCount;
660     },
661 
662     /**
663      * Quantity of particles setter
664      * @param {Number} particleCount
665      */
666     setParticleCount:function (particleCount) {
667         this.particleCount = particleCount;
668     },
669 
670     /**
671      * How many seconds the emitter wil run. -1 means 'forever'
672      * @return {Number}
673      */
674     getDuration:function () {
675         return this.duration;
676     },
677 
678     /**
679      * set run seconds of the emitter
680      * @param {Number} duration
681      */
682     setDuration:function (duration) {
683         this.duration = duration;
684     },
685 
686     /**
687      * Return sourcePosition of the emitter
688      * @return {cc.Point | Object}
689      */
690     getSourcePosition:function () {
691         return {x:this._sourcePosition.x, y:this._sourcePosition.y};
692     },
693 
694     /**
695      * sourcePosition of the emitter setter
696      * @param sourcePosition
697      */
698     setSourcePosition:function (sourcePosition) {
699         this._sourcePosition = sourcePosition;
700     },
701 
702     /**
703      * Return Position variance of the emitter
704      * @return {cc.Point | Object}
705      */
706     getPosVar:function () {
707         return {x: this._posVar.x, y: this._posVar.y};
708     },
709 
710     /**
711      * Position variance of the emitter setter
712      * @param {cc.Point} posVar
713      */
714     setPosVar:function (posVar) {
715         this._posVar = posVar;
716     },
717 
718     /**
719      * Return life of each particle
720      * @return {Number}
721      */
722     getLife:function () {
723         return this.life;
724     },
725 
726     /**
727      * life of each particle setter
728      * @param {Number} life
729      */
730     setLife:function (life) {
731         this.life = life;
732     },
733 
734     /**
735      * Return life variance of each particle
736      * @return {Number}
737      */
738     getLifeVar:function () {
739         return this.lifeVar;
740     },
741 
742     /**
743      * life variance of each particle setter
744      * @param {Number} lifeVar
745      */
746     setLifeVar:function (lifeVar) {
747         this.lifeVar = lifeVar;
748     },
749 
750     /**
751      * Return angle of each particle
752      * @return {Number}
753      */
754     getAngle:function () {
755         return this.angle;
756     },
757 
758     /**
759      * angle of each particle setter
760      * @param {Number} angle
761      */
762     setAngle:function (angle) {
763         this.angle = angle;
764     },
765 
766     /**
767      * Return angle variance of each particle
768      * @return {Number}
769      */
770     getAngleVar:function () {
771         return this.angleVar;
772     },
773 
774     /**
775      * angle variance of each particle setter
776      * @param angleVar
777      */
778     setAngleVar:function (angleVar) {
779         this.angleVar = angleVar;
780     },
781 
782     // mode A
783     /**
784      * Return Gravity of emitter
785      * @return {cc.Point}
786      */
787     getGravity:function () {
788         if(this.emitterMode !== cc.PARTICLE_MODE_GRAVITY)
789             cc.log("cc.ParticleBatchNode.getGravity() : Particle Mode should be Gravity");
790         var locGravity = this.modeA.gravity;
791         return cc.p(locGravity.x, locGravity.y);
792     },
793 
794     /**
795      * Gravity of emitter setter
796      * @param {cc.Point} gravity
797      */
798     setGravity:function (gravity) {
799         if(this.emitterMode !== cc.PARTICLE_MODE_GRAVITY)
800             cc.log("cc.ParticleBatchNode.setGravity() : Particle Mode should be Gravity");
801         this.modeA.gravity = gravity;
802     },
803 
804     /**
805      * Return Speed of each particle
806      * @return {Number}
807      */
808     getSpeed:function () {
809         if(this.emitterMode !== cc.PARTICLE_MODE_GRAVITY)
810             cc.log("cc.ParticleBatchNode.getSpeed() : Particle Mode should be Gravity");
811         return this.modeA.speed;
812     },
813 
814     /**
815      * Speed of each particle setter
816      * @param {Number} speed
817      */
818     setSpeed:function (speed) {
819         if(this.emitterMode !== cc.PARTICLE_MODE_GRAVITY)
820             cc.log("cc.ParticleBatchNode.setSpeed() : Particle Mode should be Gravity");
821         this.modeA.speed = speed;
822     },
823 
824     /**
825      * return speed variance of each particle. Only available in 'Gravity' mode.
826      * @return {Number}
827      */
828     getSpeedVar:function () {
829         if(this.emitterMode !== cc.PARTICLE_MODE_GRAVITY)
830             cc.log("cc.ParticleBatchNode.getSpeedVar() : Particle Mode should be Gravity");
831         return this.modeA.speedVar;
832     },
833 
834     /**
835      * speed variance of each particle setter. Only available in 'Gravity' mode.
836      * @param {Number} speedVar
837      */
838     setSpeedVar:function (speedVar) {
839         if(this.emitterMode !== cc.PARTICLE_MODE_GRAVITY)
840             cc.log("cc.ParticleBatchNode.setSpeedVar() : Particle Mode should be Gravity");
841         this.modeA.speedVar = speedVar;
842     },
843 
844     /**
845      * Return tangential acceleration of each particle. Only available in 'Gravity' mode.
846      * @return {Number}
847      */
848     getTangentialAccel:function () {
849         if(this.emitterMode !== cc.PARTICLE_MODE_GRAVITY)
850             cc.log("cc.ParticleBatchNode.getTangentialAccel() : Particle Mode should be Gravity");
851         return this.modeA.tangentialAccel;
852     },
853 
854     /**
855      * Tangential acceleration of each particle setter. Only available in 'Gravity' mode.
856      * @param {Number} tangentialAccel
857      */
858     setTangentialAccel:function (tangentialAccel) {
859         if(this.emitterMode !== cc.PARTICLE_MODE_GRAVITY)
860             cc.log("cc.ParticleBatchNode.setTangentialAccel() : Particle Mode should be Gravity");
861         this.modeA.tangentialAccel = tangentialAccel;
862     },
863 
864     /**
865      * Return tangential acceleration variance of each particle. Only available in 'Gravity' mode.
866      * @return {Number}
867      */
868     getTangentialAccelVar:function () {
869         if(this.emitterMode !== cc.PARTICLE_MODE_GRAVITY)
870             cc.log("cc.ParticleBatchNode.getTangentialAccelVar() : Particle Mode should be Gravity");
871         return this.modeA.tangentialAccelVar;
872     },
873 
874     /**
875      * tangential acceleration variance of each particle setter. Only available in 'Gravity' mode.
876      * @param {Number} tangentialAccelVar
877      */
878     setTangentialAccelVar:function (tangentialAccelVar) {
879         if(this.emitterMode !== cc.PARTICLE_MODE_GRAVITY)
880             cc.log("cc.ParticleBatchNode.setTangentialAccelVar() : Particle Mode should be Gravity");
881         this.modeA.tangentialAccelVar = tangentialAccelVar;
882     },
883 
884     /**
885      * Return radial acceleration of each particle. Only available in 'Gravity' mode.
886      * @return {Number}
887      */
888     getRadialAccel:function () {
889         if(this.emitterMode !== cc.PARTICLE_MODE_GRAVITY)
890             cc.log("cc.ParticleBatchNode.getRadialAccel() : Particle Mode should be Gravity");
891         return this.modeA.radialAccel;
892     },
893 
894     /**
895      * radial acceleration of each particle setter. Only available in 'Gravity' mode.
896      * @param {Number} radialAccel
897      */
898     setRadialAccel:function (radialAccel) {
899         if(this.emitterMode !== cc.PARTICLE_MODE_GRAVITY)
900             cc.log("cc.ParticleBatchNode.setRadialAccel() : Particle Mode should be Gravity");
901         this.modeA.radialAccel = radialAccel;
902     },
903 
904     /**
905      * Return radial acceleration variance of each particle. Only available in 'Gravity' mode.
906      * @return {Number}
907      */
908     getRadialAccelVar:function () {
909         if(this.emitterMode !== cc.PARTICLE_MODE_GRAVITY)
910             cc.log("cc.ParticleBatchNode.getRadialAccelVar() : Particle Mode should be Gravity");
911         return this.modeA.radialAccelVar;
912     },
913 
914     /**
915      * radial acceleration variance of each particle setter. Only available in 'Gravity' mode.
916      * @param {Number} radialAccelVar
917      */
918     setRadialAccelVar:function (radialAccelVar) {
919         if(this.emitterMode !== cc.PARTICLE_MODE_GRAVITY)
920             cc.log("cc.ParticleBatchNode.setRadialAccelVar() : Particle Mode should be Gravity");
921         this.modeA.radialAccelVar = radialAccelVar;
922     },
923 
924     /**
925      * get the rotation of each particle to its direction Only available in 'Gravity' mode.
926      * @returns {boolean}
927      */
928     getRotationIsDir: function(){
929         if(this.emitterMode !== cc.PARTICLE_MODE_GRAVITY)
930             cc.log("cc.ParticleBatchNode.getRotationIsDir() : Particle Mode should be Gravity");
931         return this.modeA.rotationIsDir;
932     },
933 
934     /**
935      * set the rotation of each particle to its direction Only available in 'Gravity' mode.
936      * @param {boolean} t
937      */
938     setRotationIsDir: function(t){
939         if(this.emitterMode !== cc.PARTICLE_MODE_GRAVITY)
940             cc.log("cc.ParticleBatchNode.setRotationIsDir() : Particle Mode should be Gravity");
941         this.modeA.rotationIsDir = t;
942     },
943 
944     // mode B
945     /**
946      * Return starting radius of the particles. Only available in 'Radius' mode.
947      * @return {Number}
948      */
949     getStartRadius:function () {
950         if(this.emitterMode !== cc.PARTICLE_MODE_RADIUS)
951             cc.log("cc.ParticleBatchNode.getStartRadius() : Particle Mode should be Radius");
952         return this.modeB.startRadius;
953     },
954 
955     /**
956      * starting radius of the particles setter. Only available in 'Radius' mode.
957      * @param {Number} startRadius
958      */
959     setStartRadius:function (startRadius) {
960         if(this.emitterMode !== cc.PARTICLE_MODE_RADIUS)
961             cc.log("cc.ParticleBatchNode.setStartRadius() : Particle Mode should be Radius");
962         this.modeB.startRadius = startRadius;
963     },
964 
965     /**
966      * Return starting radius variance of the particles. Only available in 'Radius' mode.
967      * @return {Number}
968      */
969     getStartRadiusVar:function () {
970         if(this.emitterMode !== cc.PARTICLE_MODE_RADIUS)
971             cc.log("cc.ParticleBatchNode.getStartRadiusVar() : Particle Mode should be Radius");
972         return this.modeB.startRadiusVar;
973     },
974 
975     /**
976      * starting radius variance of the particles setter. Only available in 'Radius' mode.
977      * @param {Number} startRadiusVar
978      */
979     setStartRadiusVar:function (startRadiusVar) {
980         if(this.emitterMode !== cc.PARTICLE_MODE_RADIUS)
981             cc.log("cc.ParticleBatchNode.setStartRadiusVar() : Particle Mode should be Radius");
982         this.modeB.startRadiusVar = startRadiusVar;
983     },
984 
985     /**
986      * Return ending radius of the particles. Only available in 'Radius' mode.
987      * @return {Number}
988      */
989     getEndRadius:function () {
990         if(this.emitterMode !== cc.PARTICLE_MODE_RADIUS)
991             cc.log("cc.ParticleBatchNode.getEndRadius() : Particle Mode should be Radius");
992         return this.modeB.endRadius;
993     },
994 
995     /**
996      * ending radius of the particles setter. Only available in 'Radius' mode.
997      * @param {Number} endRadius
998      */
999     setEndRadius:function (endRadius) {
1000         if(this.emitterMode !== cc.PARTICLE_MODE_RADIUS)
1001             cc.log("cc.ParticleBatchNode.setEndRadius() : Particle Mode should be Radius");
1002         this.modeB.endRadius = endRadius;
1003     },
1004 
1005     /**
1006      * Return ending radius variance of the particles. Only available in 'Radius' mode.
1007      * @return {Number}
1008      */
1009     getEndRadiusVar:function () {
1010         if(this.emitterMode !== cc.PARTICLE_MODE_RADIUS)
1011             cc.log("cc.ParticleBatchNode.getEndRadiusVar() : Particle Mode should be Radius");
1012         return this.modeB.endRadiusVar;
1013     },
1014 
1015     /**
1016      * ending radius variance of the particles setter. Only available in 'Radius' mode.
1017      * @param endRadiusVar
1018      */
1019     setEndRadiusVar:function (endRadiusVar) {
1020         if(this.emitterMode !== cc.PARTICLE_MODE_RADIUS)
1021             cc.log("cc.ParticleBatchNode.setEndRadiusVar() : Particle Mode should be Radius");
1022         this.modeB.endRadiusVar = endRadiusVar;
1023     },
1024 
1025     /**
1026      * get Number of degress to rotate a particle around the source pos per second. Only available in 'Radius' mode.
1027      * @return {Number}
1028      */
1029     getRotatePerSecond:function () {
1030         if(this.emitterMode !== cc.PARTICLE_MODE_RADIUS)
1031             cc.log("cc.ParticleBatchNode.getRotatePerSecond() : Particle Mode should be Radius");
1032         return this.modeB.rotatePerSecond;
1033     },
1034 
1035     /**
1036      * set Number of degress to rotate a particle around the source pos per second. Only available in 'Radius' mode.
1037      * @param {Number} degrees
1038      */
1039     setRotatePerSecond:function (degrees) {
1040         if(this.emitterMode !== cc.PARTICLE_MODE_RADIUS)
1041             cc.log("cc.ParticleBatchNode.setRotatePerSecond() : Particle Mode should be Radius");
1042         this.modeB.rotatePerSecond = degrees;
1043     },
1044 
1045     /**
1046      * Return Variance in degrees for rotatePerSecond. Only available in 'Radius' mode.
1047      * @return {Number}
1048      */
1049     getRotatePerSecondVar:function () {
1050         if(this.emitterMode !== cc.PARTICLE_MODE_RADIUS)
1051             cc.log("cc.ParticleBatchNode.getRotatePerSecondVar() : Particle Mode should be Radius");
1052         return this.modeB.rotatePerSecondVar;
1053     },
1054 
1055     /**
1056      * Variance in degrees for rotatePerSecond setter. Only available in 'Radius' mode.
1057      * @param degrees
1058      */
1059     setRotatePerSecondVar:function (degrees) {
1060         if(this.emitterMode !== cc.PARTICLE_MODE_RADIUS)
1061             cc.log("cc.ParticleBatchNode.setRotatePerSecondVar() : Particle Mode should be Radius");
1062         this.modeB.rotatePerSecondVar = degrees;
1063     },
1064     //////////////////////////////////////////////////////////////////////////
1065 
1066     //don't use a transform matrix, this is faster
1067     setScale:function (scale, scaleY) {
1068         this._transformSystemDirty = true;
1069         cc.Node.prototype.setScale.call(this, scale, scaleY);
1070     },
1071 
1072     setRotation:function (newRotation) {
1073         this._transformSystemDirty = true;
1074         cc.Node.prototype.setRotation.call(this, newRotation);
1075     },
1076 
1077     setScaleX:function (newScaleX) {
1078         this._transformSystemDirty = true;
1079         cc.Node.prototype.setScaleX.call(this, newScaleX);
1080     },
1081 
1082     setScaleY:function (newScaleY) {
1083         this._transformSystemDirty = true;
1084         cc.Node.prototype.setScaleY.call(this, newScaleY);
1085     },
1086 
1087     /**
1088      * get start size in pixels of each particle
1089      * @return {Number}
1090      */
1091     getStartSize:function () {
1092         return this.startSize;
1093     },
1094 
1095     /**
1096      * set start size in pixels of each particle
1097      * @param {Number} startSize
1098      */
1099     setStartSize:function (startSize) {
1100         this.startSize = startSize;
1101     },
1102 
1103     /**
1104      * get size variance in pixels of each particle
1105      * @return {Number}
1106      */
1107     getStartSizeVar:function () {
1108         return this.startSizeVar;
1109     },
1110 
1111     /**
1112      * set size variance in pixels of each particle
1113      * @param {Number} startSizeVar
1114      */
1115     setStartSizeVar:function (startSizeVar) {
1116         this.startSizeVar = startSizeVar;
1117     },
1118 
1119     /**
1120      * get end size in pixels of each particle
1121      * @return {Number}
1122      */
1123     getEndSize:function () {
1124         return this.endSize;
1125     },
1126 
1127     /**
1128      * set end size in pixels of each particle
1129      * @param endSize
1130      */
1131     setEndSize:function (endSize) {
1132         this.endSize = endSize;
1133     },
1134 
1135     /**
1136      * get end size variance in pixels of each particle
1137      * @return {Number}
1138      */
1139     getEndSizeVar:function () {
1140         return this.endSizeVar;
1141     },
1142 
1143     /**
1144      * set end size variance in pixels of each particle
1145      * @param {Number} endSizeVar
1146      */
1147     setEndSizeVar:function (endSizeVar) {
1148         this.endSizeVar = endSizeVar;
1149     },
1150 
1151     /**
1152      * set start color of each particle
1153      * @return {cc.Color}
1154      */
1155     getStartColor:function () {
1156         return this._startColor;
1157     },
1158 
1159     /**
1160      * get start color of each particle
1161      * @param {cc.Color} startColor
1162      */
1163     setStartColor:function (startColor) {
1164         this._startColor = cc.color(startColor);
1165     },
1166 
1167     /**
1168      * get start color variance of each particle
1169      * @return {cc.Color}
1170      */
1171     getStartColorVar:function () {
1172         return this._startColorVar;
1173     },
1174 
1175     /**
1176      * set start color variance of each particle
1177      * @param {cc.Color} startColorVar
1178      */
1179     setStartColorVar:function (startColorVar) {
1180         this._startColorVar = cc.color(startColorVar);
1181     },
1182 
1183     /**
1184      * get end color and end color variation of each particle
1185      * @return {cc.Color}
1186      */
1187     getEndColor:function () {
1188         return this._endColor;
1189     },
1190 
1191     /**
1192      * set end color and end color variation of each particle
1193      * @param {cc.Color} endColor
1194      */
1195     setEndColor:function (endColor) {
1196         this._endColor = cc.color(endColor);
1197     },
1198 
1199     /**
1200      * get end color variance of each particle
1201      * @return {cc.Color}
1202      */
1203     getEndColorVar:function () {
1204         return this._endColorVar;
1205     },
1206 
1207     /**
1208      * set end color variance of each particle
1209      * @param {cc.Color} endColorVar
1210      */
1211     setEndColorVar:function (endColorVar) {
1212         this._endColorVar = cc.color(endColorVar);
1213     },
1214 
1215     /**
1216      * get initial angle of each particle
1217      * @return {Number}
1218      */
1219     getStartSpin:function () {
1220         return this.startSpin;
1221     },
1222 
1223     /**
1224      * set initial angle of each particle
1225      * @param {Number} startSpin
1226      */
1227     setStartSpin:function (startSpin) {
1228         this.startSpin = startSpin;
1229     },
1230 
1231     /**
1232      * get initial angle variance of each particle
1233      * @return {Number}
1234      */
1235     getStartSpinVar:function () {
1236         return this.startSpinVar;
1237     },
1238 
1239     /**
1240      * set initial angle variance of each particle
1241      * @param {Number} startSpinVar
1242      */
1243     setStartSpinVar:function (startSpinVar) {
1244         this.startSpinVar = startSpinVar;
1245     },
1246 
1247     /**
1248      * get end angle of each particle
1249      * @return {Number}
1250      */
1251     getEndSpin:function () {
1252         return this.endSpin;
1253     },
1254 
1255     /**
1256      * set end angle of each particle
1257      * @param {Number} endSpin
1258      */
1259     setEndSpin:function (endSpin) {
1260         this.endSpin = endSpin;
1261     },
1262 
1263     /**
1264      * get end angle variance of each particle
1265      * @return {Number}
1266      */
1267     getEndSpinVar:function () {
1268         return this.endSpinVar;
1269     },
1270 
1271     /**
1272      * set end angle variance of each particle
1273      * @param {Number} endSpinVar
1274      */
1275     setEndSpinVar:function (endSpinVar) {
1276         this.endSpinVar = endSpinVar;
1277     },
1278 
1279     /**
1280      * get emission rate of the particles
1281      * @return {Number}
1282      */
1283     getEmissionRate:function () {
1284         return this.emissionRate;
1285     },
1286 
1287     /**
1288      * set emission rate of the particles
1289      * @param {Number} emissionRate
1290      */
1291     setEmissionRate:function (emissionRate) {
1292         this.emissionRate = emissionRate;
1293     },
1294 
1295     /**
1296      * get maximum particles of the system
1297      * @return {Number}
1298      */
1299     getTotalParticles:function () {
1300         return this._totalParticles;
1301     },
1302 
1303     /**
1304      * set maximum particles of the system
1305      * @param {Number} tp totalParticles
1306      */
1307     setTotalParticles:function (tp) {
1308         //cc.assert(tp <= this._allocatedParticles, "Particle: resizing particle array only supported for quads");
1309         if (cc._renderType === cc._RENDER_TYPE_CANVAS){
1310             this._totalParticles = (tp < 200) ? tp : 200;
1311             return;
1312         }
1313 
1314         // If we are setting the total numer of particles to a number higher
1315         // than what is allocated, we need to allocate new arrays
1316         if (tp > this._allocatedParticles) {
1317             var quadSize = cc.V3F_C4B_T2F_Quad.BYTES_PER_ELEMENT;
1318             // Allocate new memory
1319             this._indices = new Uint16Array(tp * 6);
1320             var locQuadsArrayBuffer = new ArrayBuffer(tp * quadSize);
1321             //TODO need fix
1322             // Assign pointers
1323             var locParticles = this._particles;
1324             locParticles.length = 0;
1325             var locQuads = this._quads;
1326             locQuads.length = 0;
1327             for (var j = 0; j < tp; j++) {
1328                 locParticles[j] = new cc.Particle();
1329                 locQuads[j] = new cc.V3F_C4B_T2F_Quad(null, null, null, null, locQuadsArrayBuffer, j * quadSize);
1330             }
1331             this._allocatedParticles = tp;
1332             this._totalParticles = tp;
1333 
1334             // Init particles
1335             if (this._batchNode) {
1336                 for (var i = 0; i < tp; i++)
1337                     locParticles[i].atlasIndex = i;
1338             }
1339 
1340             this._quadsArrayBuffer = locQuadsArrayBuffer;
1341 
1342             this.initIndices();
1343             //if (cc.TEXTURE_ATLAS_USE_VAO)
1344             //    this._setupVBOandVAO();
1345             //else
1346             this._setupVBO();
1347 
1348             //set the texture coord
1349             if(this._texture){
1350                 this.initTexCoordsWithRect(cc.rect(0, 0, this._texture.width, this._texture.height));
1351             }
1352         } else
1353             this._totalParticles = tp;
1354         this.resetSystem();
1355     },
1356 
1357     /**
1358      * get Texture of Particle System
1359      * @return {cc.Texture2D}
1360      */
1361     getTexture:function () {
1362         return this._texture;
1363     },
1364 
1365     /**
1366      * set Texture of Particle System
1367      * @param {cc.Texture2D } texture
1368      */
1369     setTexture:function (texture) {
1370         if(texture.isLoaded()){
1371             this.setTextureWithRect(texture, cc.rect(0, 0, texture.width, texture.height));
1372         } else {
1373             this._textureLoaded = false;
1374             texture.addLoadedEventListener(function(sender){
1375                 this._textureLoaded = true;
1376                 this.setTextureWithRect(sender, cc.rect(0, 0, sender.width, sender.height));
1377             }, this);
1378         }
1379     },
1380 
1381     /** conforms to CocosNodeTexture protocol */
1382     /**
1383      * get BlendFunc of Particle System
1384      * @return {cc.BlendFunc}
1385      */
1386     getBlendFunc:function () {
1387         return this._blendFunc;
1388     },
1389 
1390     /**
1391      * set BlendFunc of Particle System
1392      * @param {Number} src
1393      * @param {Number} dst
1394      */
1395     setBlendFunc:function (src, dst) {
1396         if (dst === undefined) {
1397             if (this._blendFunc != src) {
1398                 this._blendFunc = src;
1399                 this._updateBlendFunc();
1400             }
1401         } else {
1402             if (this._blendFunc.src != src || this._blendFunc.dst != dst) {
1403                 this._blendFunc = {src:src, dst:dst};
1404                 this._updateBlendFunc();
1405             }
1406         }
1407     },
1408 
1409     /**
1410      * does the alpha value modify color getter
1411      * @return {Boolean}
1412      */
1413     isOpacityModifyRGB:function () {
1414         return this._opacityModifyRGB;
1415     },
1416 
1417     /**
1418      * does the alpha value modify color setter
1419      * @param newValue
1420      */
1421     setOpacityModifyRGB:function (newValue) {
1422         this._opacityModifyRGB = newValue;
1423     },
1424 
1425     /**
1426      * <p>whether or not the particles are using blend additive.<br/>
1427      *     If enabled, the following blending function will be used.<br/>
1428      * </p>
1429      * @return {Boolean}
1430      * @example
1431      *    source blend function = GL_SRC_ALPHA;
1432      *    dest blend function = GL_ONE;
1433      */
1434     isBlendAdditive:function () {
1435         return (( this._blendFunc.src == cc.SRC_ALPHA && this._blendFunc.dst == cc.ONE) || (this._blendFunc.src == cc.ONE && this._blendFunc.dst == cc.ONE));
1436     },
1437 
1438     /**
1439      * <p>whether or not the particles are using blend additive.<br/>
1440      *     If enabled, the following blending function will be used.<br/>
1441      * </p>
1442      * @param {Boolean} isBlendAdditive
1443      */
1444     setBlendAdditive:function (isBlendAdditive) {
1445         var locBlendFunc = this._blendFunc;
1446         if (isBlendAdditive) {
1447             locBlendFunc.src = cc.SRC_ALPHA;
1448             locBlendFunc.dst = cc.ONE;
1449         } else {
1450             if (cc._renderType === cc._RENDER_TYPE_WEBGL) {
1451                 if (this._texture && !this._texture.hasPremultipliedAlpha()) {
1452                     locBlendFunc.src = cc.SRC_ALPHA;
1453                     locBlendFunc.dst = cc.ONE_MINUS_SRC_ALPHA;
1454                 } else {
1455                     locBlendFunc.src = cc.BLEND_SRC;
1456                     locBlendFunc.dst = cc.BLEND_DST;
1457                 }
1458             } else {
1459                 locBlendFunc.src = cc.BLEND_SRC;
1460                 locBlendFunc.dst = cc.BLEND_DST;
1461             }
1462         }
1463     },
1464 
1465     /**
1466      * get particles movement type: Free or Grouped
1467      * @return {Number}
1468      */
1469     getPositionType:function () {
1470         return this.positionType;
1471     },
1472 
1473     /**
1474      * set particles movement type: Free or Grouped
1475      * @param {Number} positionType
1476      */
1477     setPositionType:function (positionType) {
1478         this.positionType = positionType;
1479     },
1480 
1481     /**
1482      *  <p> return whether or not the node will be auto-removed when it has no particles left.<br/>
1483      *      By default it is false.<br/>
1484      *  </p>
1485      * @return {Boolean}
1486      */
1487     isAutoRemoveOnFinish:function () {
1488         return this.autoRemoveOnFinish;
1489     },
1490 
1491     /**
1492      *  <p> set whether or not the node will be auto-removed when it has no particles left.<br/>
1493      *      By default it is false.<br/>
1494      *  </p>
1495      * @param {Boolean} isAutoRemoveOnFinish
1496      */
1497     setAutoRemoveOnFinish:function (isAutoRemoveOnFinish) {
1498         this.autoRemoveOnFinish = isAutoRemoveOnFinish;
1499     },
1500 
1501     /**
1502      * return kind of emitter modes
1503      * @return {Number}
1504      */
1505     getEmitterMode:function () {
1506         return this.emitterMode;
1507     },
1508 
1509     /**
1510      * <p>Switch between different kind of emitter modes:<br/>
1511      *  - CCPARTICLE_MODE_GRAVITY: uses gravity, speed, radial and tangential acceleration<br/>
1512      *  - CCPARTICLE_MODE_RADIUS: uses radius movement + rotation <br/>
1513      *  </p>
1514      * @param {Number} emitterMode
1515      */
1516     setEmitterMode:function (emitterMode) {
1517         this.emitterMode = emitterMode;
1518     },
1519 
1520     /**
1521      * initializes a cc.ParticleSystem
1522      */
1523     init:function () {
1524         return this.initWithTotalParticles(150);
1525     },
1526 
1527     /**
1528      * <p>
1529      *     initializes a CCParticleSystem from a plist file. <br/>
1530      *      This plist files can be creted manually or with Particle Designer:<br/>
1531      *      http://particledesigner.71squared.com/
1532      * </p>
1533      * @param {String} plistFile
1534      * @return {boolean}
1535      */
1536     initWithFile:function (plistFile) {
1537         this._plistFile = plistFile;
1538         var dict = cc.loader.getRes(plistFile);
1539         if(!dict){
1540             cc.log("cc.ParticleSystem.initWithFile(): Particles: file not found");
1541             return false;
1542         }
1543 
1544         // XXX compute path from a path, should define a function somewhere to do it
1545         return this.initWithDictionary(dict, "");
1546     },
1547 
1548     /**
1549      * return bounding box of particle system in world space
1550      * @return {cc.Rect}
1551      */
1552     getBoundingBoxToWorld:function () {
1553         return cc.rect(0, 0, cc._canvas.width, cc._canvas.height);
1554     },
1555 
1556     /**
1557      * initializes a particle system from a NSDictionary and the path from where to load the png
1558      * @param {object} dictionary
1559      * @param {String} dirname
1560      * @return {Boolean}
1561      */
1562     initWithDictionary:function (dictionary, dirname) {
1563         var ret = false;
1564         var buffer = null;
1565         var image = null;
1566         var locValueForKey = this._valueForKey;
1567 
1568         var maxParticles = parseInt(locValueForKey("maxParticles", dictionary));
1569         // self, not super
1570         if (this.initWithTotalParticles(maxParticles)) {
1571             // angle
1572             this.angle = parseFloat(locValueForKey("angle", dictionary));
1573             this.angleVar = parseFloat(locValueForKey("angleVariance", dictionary));
1574 
1575             // duration
1576             this.duration = parseFloat(locValueForKey("duration", dictionary));
1577 
1578             // blend function
1579             this._blendFunc.src = parseInt(locValueForKey("blendFuncSource", dictionary));
1580             this._blendFunc.dst = parseInt(locValueForKey("blendFuncDestination", dictionary));
1581 
1582             // color
1583             var locStartColor = this._startColor;
1584             locStartColor.r = parseFloat(locValueForKey("startColorRed", dictionary)) * 255;
1585             locStartColor.g = parseFloat(locValueForKey("startColorGreen", dictionary)) * 255;
1586             locStartColor.b = parseFloat(locValueForKey("startColorBlue", dictionary)) * 255;
1587             locStartColor.a = parseFloat(locValueForKey("startColorAlpha", dictionary)) * 255;
1588 
1589             var locStartColorVar = this._startColorVar;
1590             locStartColorVar.r = parseFloat(locValueForKey("startColorVarianceRed", dictionary)) * 255;
1591             locStartColorVar.g = parseFloat(locValueForKey("startColorVarianceGreen", dictionary)) * 255;
1592             locStartColorVar.b = parseFloat(locValueForKey("startColorVarianceBlue", dictionary)) * 255;
1593             locStartColorVar.a = parseFloat(locValueForKey("startColorVarianceAlpha", dictionary)) * 255;
1594 
1595             var locEndColor = this._endColor;
1596             locEndColor.r = parseFloat(locValueForKey("finishColorRed", dictionary)) * 255;
1597             locEndColor.g = parseFloat(locValueForKey("finishColorGreen", dictionary)) * 255;
1598             locEndColor.b = parseFloat(locValueForKey("finishColorBlue", dictionary)) * 255;
1599             locEndColor.a = parseFloat(locValueForKey("finishColorAlpha", dictionary)) * 255;
1600 
1601             var locEndColorVar = this._endColorVar;
1602             locEndColorVar.r = parseFloat(locValueForKey("finishColorVarianceRed", dictionary)) * 255;
1603             locEndColorVar.g = parseFloat(locValueForKey("finishColorVarianceGreen", dictionary)) * 255;
1604             locEndColorVar.b = parseFloat(locValueForKey("finishColorVarianceBlue", dictionary)) * 255;
1605             locEndColorVar.a = parseFloat(locValueForKey("finishColorVarianceAlpha", dictionary)) * 255;
1606 
1607             // particle size
1608             this.startSize = parseFloat(locValueForKey("startParticleSize", dictionary));
1609             this.startSizeVar = parseFloat(locValueForKey("startParticleSizeVariance", dictionary));
1610             this.endSize = parseFloat(locValueForKey("finishParticleSize", dictionary));
1611             this.endSizeVar = parseFloat(locValueForKey("finishParticleSizeVariance", dictionary));
1612 
1613             // position
1614             this.setPosition(parseFloat(locValueForKey("sourcePositionx", dictionary)),
1615                               parseFloat(locValueForKey("sourcePositiony", dictionary)));
1616             this._posVar.x = parseFloat(locValueForKey("sourcePositionVariancex", dictionary));
1617             this._posVar.y = parseFloat(locValueForKey("sourcePositionVariancey", dictionary));
1618 
1619             // Spinning
1620             this.startSpin = parseFloat(locValueForKey("rotationStart", dictionary));
1621             this.startSpinVar = parseFloat(locValueForKey("rotationStartVariance", dictionary));
1622             this.endSpin = parseFloat(locValueForKey("rotationEnd", dictionary));
1623             this.endSpinVar = parseFloat(locValueForKey("rotationEndVariance", dictionary));
1624 
1625             this.emitterMode = parseInt(locValueForKey("emitterType", dictionary));
1626 
1627             // Mode A: Gravity + tangential accel + radial accel
1628             if (this.emitterMode == cc.PARTICLE_MODE_GRAVITY) {
1629                 var locModeA = this.modeA;
1630                 // gravity
1631                 locModeA.gravity.x = parseFloat(locValueForKey("gravityx", dictionary));
1632                 locModeA.gravity.y = parseFloat(locValueForKey("gravityy", dictionary));
1633 
1634                 // speed
1635                 locModeA.speed = parseFloat(locValueForKey("speed", dictionary));
1636                 locModeA.speedVar = parseFloat(locValueForKey("speedVariance", dictionary));
1637 
1638                 // radial acceleration
1639                 var pszTmp = locValueForKey("radialAcceleration", dictionary);
1640                 locModeA.radialAccel = (pszTmp) ? parseFloat(pszTmp) : 0;
1641 
1642                 pszTmp = locValueForKey("radialAccelVariance", dictionary);
1643                 locModeA.radialAccelVar = (pszTmp) ? parseFloat(pszTmp) : 0;
1644 
1645                 // tangential acceleration
1646                 pszTmp = locValueForKey("tangentialAcceleration", dictionary);
1647                 locModeA.tangentialAccel = (pszTmp) ? parseFloat(pszTmp) : 0;
1648 
1649                 pszTmp = locValueForKey("tangentialAccelVariance", dictionary);
1650                 locModeA.tangentialAccelVar = (pszTmp) ? parseFloat(pszTmp) : 0;
1651 
1652                 // rotation is dir
1653                 var locRotationIsDir = locValueForKey("rotationIsDir", dictionary).toLowerCase();
1654                 locModeA.rotationIsDir = (locRotationIsDir != null && (locRotationIsDir === "true" || locRotationIsDir === "1"));
1655             } else if (this.emitterMode == cc.PARTICLE_MODE_RADIUS) {
1656                 // or Mode B: radius movement
1657                 var locModeB = this.modeB;
1658                 locModeB.startRadius = parseFloat(locValueForKey("maxRadius", dictionary));
1659                 locModeB.startRadiusVar = parseFloat(locValueForKey("maxRadiusVariance", dictionary));
1660                 locModeB.endRadius = parseFloat(locValueForKey("minRadius", dictionary));
1661                 locModeB.endRadiusVar = 0;
1662                 locModeB.rotatePerSecond = parseFloat(locValueForKey("rotatePerSecond", dictionary));
1663                 locModeB.rotatePerSecondVar = parseFloat(locValueForKey("rotatePerSecondVariance", dictionary));
1664             } else {
1665                 cc.log("cc.ParticleSystem.initWithDictionary(): Invalid emitterType in config file");
1666                 return false;
1667             }
1668 
1669             // life span
1670             this.life = parseFloat(locValueForKey("particleLifespan", dictionary));
1671             this.lifeVar = parseFloat(locValueForKey("particleLifespanVariance", dictionary));
1672 
1673             // emission Rate
1674             this.emissionRate = this._totalParticles / this.life;
1675 
1676             //don't get the internal texture if a batchNode is used
1677             if (!this._batchNode) {
1678                 // Set a compatible default for the alpha transfer
1679                 this._opacityModifyRGB = false;
1680 
1681                 // texture
1682                 // Try to get the texture from the cache
1683                 var textureName = locValueForKey("textureFileName", dictionary);
1684                 var imgPath = cc.path.changeBasename(this._plistFile, textureName);
1685                 var tex = cc.textureCache.textureForKey(imgPath);
1686 
1687                 if (tex) {
1688                     this.setTexture(tex);
1689                 } else {
1690                     var textureData = locValueForKey("textureImageData", dictionary);
1691 
1692                     if (textureData && textureData.length == 0) {
1693                         tex = cc.textureCache.addImage(imgPath);
1694                         if (!tex)
1695                             return false;
1696                         this.setTexture(tex);
1697                     } else {
1698                         buffer = cc.unzipBase64AsArray(textureData, 1);
1699                         if (!buffer) {
1700                             cc.log("cc.ParticleSystem: error decoding or ungzipping textureImageData");
1701                             return false;
1702                         }
1703 
1704                         var imageFormat = cc.getImageFormatByData(buffer);
1705 
1706                         if(imageFormat !== cc.FMT_TIFF && imageFormat !== cc.FMT_PNG){
1707                             cc.log("cc.ParticleSystem: unknown image format with Data");
1708                             return false;
1709                         }
1710 
1711                         var canvasObj = cc.newElement("canvas");
1712                         if(imageFormat === cc.FMT_PNG){
1713                             var myPngObj = new cc.PNGReader(buffer);
1714                             myPngObj.render(canvasObj);
1715                         } else {
1716                             var myTIFFObj = cc.tiffReader;
1717                             myTIFFObj.parseTIFF(buffer,canvasObj);
1718                         }
1719 
1720                         cc.textureCache.cacheImage(imgPath, canvasObj);
1721 
1722                         var addTexture = cc.textureCache.textureForKey(imgPath);
1723                         if(!addTexture)
1724                             cc.log("cc.ParticleSystem.initWithDictionary() : error loading the texture");
1725                         this.setTexture(addTexture);
1726                     }
1727                 }
1728             }
1729             ret = true;
1730         }
1731         return ret;
1732     },
1733 
1734     /**
1735      * Initializes a system with a fixed number of particles
1736      * @param {Number} numberOfParticles
1737      * @return {Boolean}
1738      */
1739     initWithTotalParticles:function (numberOfParticles) {
1740         this._totalParticles = numberOfParticles;
1741 
1742         var i, locParticles = this._particles;
1743         locParticles.length = 0;
1744         for(i = 0; i< numberOfParticles; i++){
1745             locParticles[i] = new cc.Particle();
1746         }
1747 
1748         if (!locParticles) {
1749             cc.log("Particle system: not enough memory");
1750             return false;
1751         }
1752         this._allocatedParticles = numberOfParticles;
1753 
1754         if (this._batchNode)
1755             for (i = 0; i < this._totalParticles; i++)
1756                 locParticles[i].atlasIndex = i;
1757 
1758         // default, active
1759         this._isActive = true;
1760 
1761         // default blend function
1762         this._blendFunc.src = cc.BLEND_SRC;
1763         this._blendFunc.dst = cc.BLEND_DST;
1764 
1765         // default movement type;
1766         this.positionType = cc.PARTICLE_TYPE_FREE;
1767 
1768         // by default be in mode A:
1769         this.emitterMode = cc.PARTICLE_MODE_GRAVITY;
1770 
1771         // default: modulate
1772         // XXX: not used
1773         //  colorModulate = YES;
1774         this.autoRemoveOnFinish = false;
1775 
1776         //for batchNode
1777         this._transformSystemDirty = false;
1778 
1779         // udpate after action in run!
1780         this.scheduleUpdateWithPriority(1);
1781 
1782         if(cc._renderType === cc._RENDER_TYPE_WEBGL){
1783             // allocating data space
1784             if (!this._allocMemory())
1785                 return false;
1786 
1787             this.initIndices();
1788             //if (cc.TEXTURE_ATLAS_USE_VAO)
1789             //    this._setupVBOandVAO();
1790             //else
1791             this._setupVBO();
1792 
1793             this.shaderProgram = cc.shaderCache.programForKey(cc.SHADER_POSITION_TEXTURECOLOR);
1794         }
1795 
1796         return true;
1797     },
1798 
1799     destroyParticleSystem:function () {
1800         this.unscheduleUpdate();
1801     },
1802 
1803     /**
1804      * Add a particle to the emitter
1805      * @return {Boolean}
1806      */
1807     addParticle: function () {
1808         if (this.isFull())
1809             return false;
1810         var particle, particles = this._particles;
1811         if (cc._renderType === cc._RENDER_TYPE_CANVAS) {
1812             if (this.particleCount < particles.length) {
1813                 particle = particles[this.particleCount];
1814             } else {
1815                 particle = new cc.Particle();
1816                 particles.push(particle);
1817             }
1818         } else {
1819             particle = particles[this.particleCount];
1820         }
1821         this.initParticle(particle);
1822         ++this.particleCount;
1823         return true;
1824     },
1825 
1826     /**
1827      * Initializes a particle
1828      * @param {cc.Particle} particle
1829      */
1830     initParticle:function (particle) {
1831         var locRandomMinus11 = cc.randomMinus1To1;
1832         // timeToLive
1833         // no negative life. prevent division by 0
1834         particle.timeToLive = this.life + this.lifeVar * locRandomMinus11();
1835         particle.timeToLive = Math.max(0, particle.timeToLive);
1836 
1837         // position
1838         particle.pos.x = this._sourcePosition.x + this._posVar.x * locRandomMinus11();
1839         particle.pos.y = this._sourcePosition.y + this._posVar.y * locRandomMinus11();
1840 
1841         // Color
1842         var start, end;
1843         var locStartColor = this._startColor, locStartColorVar = this._startColorVar;
1844         var locEndColor = this._endColor, locEndColorVar = this._endColorVar;
1845         if (cc._renderType === cc._RENDER_TYPE_CANVAS) {
1846             start = cc.color(
1847                 cc.clampf(locStartColor.r + locStartColorVar.r * locRandomMinus11(), 0, 255),
1848                 cc.clampf(locStartColor.g + locStartColorVar.g * locRandomMinus11(), 0, 255),
1849                 cc.clampf(locStartColor.b + locStartColorVar.b * locRandomMinus11(), 0, 255),
1850                 cc.clampf(locStartColor.a + locStartColorVar.a * locRandomMinus11(), 0, 255)
1851             );
1852             end = cc.color(
1853                 cc.clampf(locEndColor.r + locEndColorVar.r * locRandomMinus11(), 0, 255),
1854                 cc.clampf(locEndColor.g + locEndColorVar.g * locRandomMinus11(), 0, 255),
1855                 cc.clampf(locEndColor.b + locEndColorVar.b * locRandomMinus11(), 0, 255),
1856                 cc.clampf(locEndColor.a + locEndColorVar.a * locRandomMinus11(), 0, 255)
1857             );
1858         } else {
1859             start = {
1860                 r: cc.clampf(locStartColor.r + locStartColorVar.r * locRandomMinus11(), 0, 255),
1861                 g: cc.clampf(locStartColor.g + locStartColorVar.g * locRandomMinus11(), 0, 255),
1862                 b: cc.clampf(locStartColor.b + locStartColorVar.b * locRandomMinus11(), 0, 255),
1863                 a: cc.clampf(locStartColor.a + locStartColorVar.a * locRandomMinus11(), 0, 255)
1864             };
1865             end = {
1866                 r: cc.clampf(locEndColor.r + locEndColorVar.r * locRandomMinus11(), 0, 255),
1867                 g: cc.clampf(locEndColor.g + locEndColorVar.g * locRandomMinus11(), 0, 255),
1868                 b: cc.clampf(locEndColor.b + locEndColorVar.b * locRandomMinus11(), 0, 255),
1869                 a: cc.clampf(locEndColor.a + locEndColorVar.a * locRandomMinus11(), 0, 255)
1870             };
1871         }
1872 
1873         particle.color = start;
1874         var locParticleDeltaColor = particle.deltaColor, locParticleTimeToLive = particle.timeToLive;
1875         locParticleDeltaColor.r = (end.r - start.r) / locParticleTimeToLive;
1876         locParticleDeltaColor.g = (end.g - start.g) / locParticleTimeToLive;
1877         locParticleDeltaColor.b = (end.b - start.b) / locParticleTimeToLive;
1878         locParticleDeltaColor.a = (end.a - start.a) / locParticleTimeToLive;
1879 
1880         // size
1881         var startS = this.startSize + this.startSizeVar * locRandomMinus11();
1882         startS = Math.max(0, startS); // No negative value
1883 
1884         particle.size = startS;
1885         if (this.endSize === cc.PARTICLE_START_SIZE_EQUAL_TO_END_SIZE) {
1886             particle.deltaSize = 0;
1887         } else {
1888             var endS = this.endSize + this.endSizeVar * locRandomMinus11();
1889             endS = Math.max(0, endS); // No negative values
1890             particle.deltaSize = (endS - startS) / locParticleTimeToLive;
1891         }
1892 
1893         // rotation
1894         var startA = this.startSpin + this.startSpinVar * locRandomMinus11();
1895         var endA = this.endSpin + this.endSpinVar * locRandomMinus11();
1896         particle.rotation = startA;
1897         particle.deltaRotation = (endA - startA) / locParticleTimeToLive;
1898 
1899         // position
1900         if (this.positionType == cc.PARTICLE_TYPE_FREE)
1901             particle.startPos = this.convertToWorldSpace(this._pointZeroForParticle);
1902         else if (this.positionType == cc.PARTICLE_TYPE_RELATIVE){
1903             particle.startPos.x = this._position.x;
1904             particle.startPos.y = this._position.y;
1905         }
1906 
1907         // direction
1908         var a = cc.degreesToRadians(this.angle + this.angleVar * locRandomMinus11());
1909 
1910         // Mode Gravity: A
1911         if (this.emitterMode === cc.PARTICLE_MODE_GRAVITY) {
1912             var locModeA = this.modeA, locParticleModeA = particle.modeA;
1913             var s = locModeA.speed + locModeA.speedVar * locRandomMinus11();
1914 
1915             // direction
1916             locParticleModeA.dir.x = Math.cos(a);
1917             locParticleModeA.dir.y = Math.sin(a);
1918             cc.pMultIn(locParticleModeA.dir, s);
1919 
1920             // radial accel
1921             locParticleModeA.radialAccel = locModeA.radialAccel + locModeA.radialAccelVar * locRandomMinus11();
1922 
1923             // tangential accel
1924             locParticleModeA.tangentialAccel = locModeA.tangentialAccel + locModeA.tangentialAccelVar * locRandomMinus11();
1925 
1926             // rotation is dir
1927             if(locModeA.rotationIsDir)
1928                 particle.rotation = -cc.radiansToDegress(cc.pToAngle(locParticleModeA.dir));
1929         } else {
1930             // Mode Radius: B
1931             var locModeB = this.modeB, locParitlceModeB = particle.modeB;
1932 
1933             // Set the default diameter of the particle from the source position
1934             var startRadius = locModeB.startRadius + locModeB.startRadiusVar * locRandomMinus11();
1935             var endRadius = locModeB.endRadius + locModeB.endRadiusVar * locRandomMinus11();
1936 
1937             locParitlceModeB.radius = startRadius;
1938             locParitlceModeB.deltaRadius = (locModeB.endRadius === cc.PARTICLE_START_RADIUS_EQUAL_TO_END_RADIUS) ? 0 : (endRadius - startRadius) / locParticleTimeToLive;
1939 
1940             locParitlceModeB.angle = a;
1941             locParitlceModeB.degreesPerSecond = cc.degreesToRadians(locModeB.rotatePerSecond + locModeB.rotatePerSecondVar * locRandomMinus11());
1942         }
1943     },
1944 
1945     /**
1946      * stop emitting particles. Running particles will continue to run until they die
1947      */
1948     stopSystem:function () {
1949         this._isActive = false;
1950         this._elapsed = this.duration;
1951         this._emitCounter = 0;
1952     },
1953 
1954     /**
1955      * Kill all living particles.
1956      */
1957     resetSystem:function () {
1958         this._isActive = true;
1959         this._elapsed = 0;
1960         var locParticles = this._particles;
1961         for (this._particleIdx = 0; this._particleIdx < this.particleCount; ++this._particleIdx)
1962             locParticles[this._particleIdx].timeToLive = 0 ;
1963     },
1964 
1965     /**
1966      * whether or not the system is full
1967      * @return {Boolean}
1968      */
1969     isFull:function () {
1970         return (this.particleCount >= this._totalParticles);
1971     },
1972 
1973     /**
1974      * should be overridden by subclasses
1975      * @param {cc.Particle} particle
1976      * @param {cc.Point} newPosition
1977      */
1978     updateQuadWithParticle:function (particle, newPosition) {
1979         var quad = null;
1980         if (this._batchNode) {
1981             var batchQuads = this._batchNode.textureAtlas.quads;
1982             quad = batchQuads[this.atlasIndex + particle.atlasIndex];
1983             this._batchNode.textureAtlas.dirty = true;
1984         } else
1985             quad = this._quads[this._particleIdx];
1986 
1987         var r, g, b, a;
1988         if (this._opacityModifyRGB) {
1989             r = 0 | (particle.color.r * particle.color.a/255);
1990             g = 0 | (particle.color.g * particle.color.a/255);
1991             b = 0 | (particle.color.b * particle.color.a/255);
1992         } else {
1993             r = 0 | (particle.color.r );
1994             g = 0 | (particle.color.g );
1995             b = 0 | (particle.color.b );
1996         }
1997         a = 0 | (particle.color.a );
1998 
1999         var locColors = quad.bl.colors;
2000         locColors.r = r;
2001         locColors.g = g;
2002         locColors.b = b;
2003         locColors.a = a;
2004 
2005         locColors = quad.br.colors;
2006         locColors.r = r;
2007         locColors.g = g;
2008         locColors.b = b;
2009         locColors.a = a;
2010 
2011         locColors = quad.tl.colors;
2012         locColors.r = r;
2013         locColors.g = g;
2014         locColors.b = b;
2015         locColors.a = a;
2016 
2017         locColors = quad.tr.colors;
2018         locColors.r = r;
2019         locColors.g = g;
2020         locColors.b = b;
2021         locColors.a = a;
2022 
2023         // vertices
2024         var size_2 = particle.size / 2;
2025         if (particle.rotation) {
2026             var x1 = -size_2;
2027             var y1 = -size_2;
2028 
2029             var x2 = size_2;
2030             var y2 = size_2;
2031             var x = newPosition.x;
2032             var y = newPosition.y;
2033 
2034             var rad = -cc.degreesToRadians(particle.rotation);
2035             var cr = Math.cos(rad);
2036             var sr = Math.sin(rad);
2037             var ax = x1 * cr - y1 * sr + x;
2038             var ay = x1 * sr + y1 * cr + y;
2039             var bx = x2 * cr - y1 * sr + x;
2040             var by = x2 * sr + y1 * cr + y;
2041             var cx = x2 * cr - y2 * sr + x;
2042             var cy = x2 * sr + y2 * cr + y;
2043             var dx = x1 * cr - y2 * sr + x;
2044             var dy = x1 * sr + y2 * cr + y;
2045 
2046             // bottom-left
2047             quad.bl.vertices.x = ax;
2048             quad.bl.vertices.y = ay;
2049 
2050             // bottom-right vertex:
2051             quad.br.vertices.x = bx;
2052             quad.br.vertices.y = by;
2053 
2054             // top-left vertex:
2055             quad.tl.vertices.x = dx;
2056             quad.tl.vertices.y = dy;
2057 
2058             // top-right vertex:
2059             quad.tr.vertices.x = cx;
2060             quad.tr.vertices.y = cy;
2061         } else {
2062             // bottom-left vertex:
2063             quad.bl.vertices.x = newPosition.x - size_2;
2064             quad.bl.vertices.y = newPosition.y - size_2;
2065 
2066             // bottom-right vertex:
2067             quad.br.vertices.x = newPosition.x + size_2;
2068             quad.br.vertices.y = newPosition.y - size_2;
2069 
2070             // top-left vertex:
2071             quad.tl.vertices.x = newPosition.x - size_2;
2072             quad.tl.vertices.y = newPosition.y + size_2;
2073 
2074             // top-right vertex:
2075             quad.tr.vertices.x = newPosition.x + size_2;
2076             quad.tr.vertices.y = newPosition.y + size_2;
2077         }
2078     },
2079 
2080     /**
2081      * should be overridden by subclasses
2082      */
2083     postStep:function () {
2084         if (cc._renderType === cc._RENDER_TYPE_WEBGL) {
2085             var gl = cc._renderContext;
2086 
2087             gl.bindBuffer(gl.ARRAY_BUFFER, this._buffersVBO[0]);
2088             gl.bufferData(gl.ARRAY_BUFFER, this._quadsArrayBuffer, gl.DYNAMIC_DRAW);
2089 
2090             // Option 2: Data
2091             //	glBufferData(GL_ARRAY_BUFFER, sizeof(quads_[0]) * particleCount, quads_, GL_DYNAMIC_DRAW);
2092 
2093             // Option 3: Orphaning + glMapBuffer
2094             // glBufferData(GL_ARRAY_BUFFER, sizeof(m_pQuads[0])*m_uTotalParticles, NULL, GL_STREAM_DRAW);
2095             // void *buf = glMapBuffer(GL_ARRAY_BUFFER, GL_WRITE_ONLY);
2096             // memcpy(buf, m_pQuads, sizeof(m_pQuads[0])*m_uTotalParticles);
2097             // glUnmapBuffer(GL_ARRAY_BUFFER);
2098 
2099             //cc.checkGLErrorDebug();
2100         }
2101     },
2102 
2103     /**
2104      * update emitter's status
2105      * @override
2106      * @param {Number} dt delta time
2107      */
2108     update:function (dt) {
2109         if (this._isActive && this.emissionRate) {
2110             var rate = 1.0 / this.emissionRate;
2111             //issue #1201, prevent bursts of particles, due to too high emitCounter
2112             if (this.particleCount < this._totalParticles)
2113                 this._emitCounter += dt;
2114 
2115             while ((this.particleCount < this._totalParticles) && (this._emitCounter > rate)) {
2116                 this.addParticle();
2117                 this._emitCounter -= rate;
2118             }
2119 
2120             this._elapsed += dt;
2121             if (this.duration != -1 && this.duration < this._elapsed)
2122                 this.stopSystem();
2123         }
2124         this._particleIdx = 0;
2125 
2126         var currentPosition = cc.Particle.TemporaryPoints[0];
2127         if (this.positionType == cc.PARTICLE_TYPE_FREE) {
2128             cc.pIn(currentPosition, this.convertToWorldSpace(this._pointZeroForParticle));
2129         } else if (this.positionType == cc.PARTICLE_TYPE_RELATIVE) {
2130             currentPosition.x = this._position.x;
2131             currentPosition.y = this._position.y;
2132         }
2133 
2134         if (this._visible) {
2135 
2136             // Used to reduce memory allocation / creation within the loop
2137             var tpa = cc.Particle.TemporaryPoints[1],
2138                 tpb = cc.Particle.TemporaryPoints[2],
2139                 tpc = cc.Particle.TemporaryPoints[3];
2140 
2141             var locParticles = this._particles;
2142             while (this._particleIdx < this.particleCount) {
2143 
2144                 // Reset the working particles
2145                 cc.pZeroIn(tpa);
2146                 cc.pZeroIn(tpb);
2147                 cc.pZeroIn(tpc);
2148 
2149                 var selParticle = locParticles[this._particleIdx];
2150 
2151                 // life
2152                 selParticle.timeToLive -= dt;
2153 
2154                 if (selParticle.timeToLive > 0) {
2155                     // Mode A: gravity, direction, tangential accel & radial accel
2156                     if (this.emitterMode == cc.PARTICLE_MODE_GRAVITY) {
2157 
2158                         var tmp = tpc, radial = tpa, tangential = tpb;
2159 
2160                         // radial acceleration
2161                         if (selParticle.pos.x || selParticle.pos.y) {
2162                             cc.pIn(radial, selParticle.pos);
2163                             cc.pNormalizeIn(radial);
2164                         } else {
2165                             cc.pZeroIn(radial);
2166                         }
2167 
2168                         cc.pIn(tangential, radial);
2169                         cc.pMultIn(radial, selParticle.modeA.radialAccel);
2170 
2171                         // tangential acceleration
2172                         var newy = tangential.x;
2173                         tangential.x = -tangential.y;
2174                         tangential.y = newy;
2175 
2176                         cc.pMultIn(tangential, selParticle.modeA.tangentialAccel);
2177 
2178                         cc.pIn(tmp, radial);
2179                         cc.pAddIn(tmp, tangential);
2180                         cc.pAddIn(tmp, this.modeA.gravity);
2181                         cc.pMultIn(tmp, dt);
2182                         cc.pAddIn(selParticle.modeA.dir, tmp);
2183 
2184 
2185                         cc.pIn(tmp, selParticle.modeA.dir);
2186                         cc.pMultIn(tmp, dt);
2187                         cc.pAddIn(selParticle.pos, tmp);
2188 
2189                     } else {
2190                         // Mode B: radius movement
2191                         var selModeB = selParticle.modeB;
2192                         // Update the angle and radius of the particle.
2193                         selModeB.angle += selModeB.degreesPerSecond * dt;
2194                         selModeB.radius += selModeB.deltaRadius * dt;
2195 
2196                         selParticle.pos.x = -Math.cos(selModeB.angle) * selModeB.radius;
2197                         selParticle.pos.y = -Math.sin(selModeB.angle) * selModeB.radius;
2198                     }
2199 
2200                     // color
2201                     if (!this._dontTint) {
2202                         selParticle.color.r += (selParticle.deltaColor.r * dt);
2203                         selParticle.color.g += (selParticle.deltaColor.g * dt);
2204                         selParticle.color.b += (selParticle.deltaColor.b * dt);
2205                         selParticle.color.a += (selParticle.deltaColor.a * dt);
2206                         selParticle.isChangeColor = true;
2207                     }
2208 
2209                     // size
2210                     selParticle.size += (selParticle.deltaSize * dt);
2211                     selParticle.size = Math.max(0, selParticle.size);
2212 
2213                     // angle
2214                     selParticle.rotation += (selParticle.deltaRotation * dt);
2215 
2216                     //
2217                     // update values in quad
2218                     //
2219                     var newPos = tpa;
2220                     if (this.positionType == cc.PARTICLE_TYPE_FREE || this.positionType == cc.PARTICLE_TYPE_RELATIVE) {
2221 
2222                         var diff = tpb;
2223                         cc.pIn(diff, currentPosition);
2224                         cc.pSubIn(diff, selParticle.startPos);
2225 
2226                         cc.pIn(newPos, selParticle.pos);
2227                         cc.pSubIn(newPos, diff);
2228 
2229                     } else {
2230                         cc.pIn(newPos, selParticle.pos);
2231                     }
2232 
2233                     // translate newPos to correct position, since matrix transform isn't performed in batchnode
2234                     // don't update the particle with the new position information, it will interfere with the radius and tangential calculations
2235                     if (this._batchNode) {
2236                         newPos.x += this._position.x;
2237                         newPos.y += this._position.y;
2238                     }
2239 
2240                     if (cc._renderType == cc._RENDER_TYPE_WEBGL) {
2241                         // IMPORTANT: newPos may not be used as a reference here! (as it is just the temporary tpa point)
2242                         // the implementation of updateQuadWithParticle must use
2243                         // the x and y values directly
2244                         this.updateQuadWithParticle(selParticle, newPos);
2245                     } else {
2246                         cc.pIn(selParticle.drawPos, newPos);
2247                     }
2248                     //updateParticleImp(self, updateParticleSel, p, newPos);
2249 
2250                     // update particle counter
2251                     ++this._particleIdx;
2252                 } else {
2253                     // life < 0
2254                     var currentIndex = selParticle.atlasIndex;
2255                     if(this._particleIdx !== this.particleCount -1){
2256                          var deadParticle = locParticles[this._particleIdx];
2257                         locParticles[this._particleIdx] = locParticles[this.particleCount -1];
2258                         locParticles[this.particleCount -1] = deadParticle;
2259                     }
2260                     if (this._batchNode) {
2261                         //disable the switched particle
2262                         this._batchNode.disableParticle(this.atlasIndex + currentIndex);
2263 
2264                         //switch indexes
2265                         locParticles[this.particleCount - 1].atlasIndex = currentIndex;
2266                     }
2267 
2268                     --this.particleCount;
2269                     if (this.particleCount == 0 && this.autoRemoveOnFinish) {
2270                         this.unscheduleUpdate();
2271                         this._parent.removeChild(this, true);
2272                         return;
2273                     }
2274                 }
2275             }
2276             this._transformSystemDirty = false;
2277         }
2278 
2279         if (!this._batchNode)
2280             this.postStep();
2281     },
2282 
2283     updateWithNoTime:function () {
2284         this.update(0);
2285     },
2286 
2287     /**
2288      * return the string found by key in dict.
2289      * @param {string} key
2290      * @param {object} dict
2291      * @return {String} "" if not found; return the string if found.
2292      * @private
2293      */
2294     _valueForKey:function (key, dict) {
2295         if (dict) {
2296             var pString = dict[key];
2297             return pString != null ? pString : "";
2298         }
2299         return "";
2300     },
2301 
2302     _updateBlendFunc:function () {
2303         if(this._batchNode){
2304             cc.log("Can't change blending functions when the particle is being batched");
2305             return;
2306         }
2307 
2308         var locTexture = this._texture;
2309         if (locTexture && locTexture instanceof cc.Texture2D) {
2310             this._opacityModifyRGB = false;
2311             var locBlendFunc = this._blendFunc;
2312             if (locBlendFunc.src == cc.BLEND_SRC && locBlendFunc.dst == cc.BLEND_DST) {
2313                 if (locTexture.hasPremultipliedAlpha()) {
2314                     this._opacityModifyRGB = true;
2315                 } else {
2316                     locBlendFunc.src = cc.SRC_ALPHA;
2317                     locBlendFunc.dst = cc.ONE_MINUS_SRC_ALPHA;
2318                 }
2319             }
2320         }
2321     },
2322 
2323     clone:function () {
2324         var retParticle = new cc.ParticleSystem();
2325 
2326         // self, not super
2327         if (retParticle.initWithTotalParticles(this._totalParticles)) {
2328             // angle
2329             retParticle.angle = this.angle;
2330             retParticle.angleVar = this.angleVar;
2331 
2332             // duration
2333             retParticle.duration = this.duration;
2334 
2335             // blend function
2336             retParticle._blendFunc.src = this._blendFunc.src;
2337             retParticle._blendFunc.dst = this._blendFunc.dst;
2338 
2339             // color
2340             var particleStartColor = retParticle._startColor, locStartColor = this._startColor;
2341             particleStartColor.r = locStartColor.r;
2342             particleStartColor.g = locStartColor.g;
2343             particleStartColor.b = locStartColor.b;
2344             particleStartColor.a = locStartColor.a;
2345 
2346             var particleStartColorVar =  retParticle._startColorVar, locStartColorVar = this._startColorVar;
2347             particleStartColorVar.r = locStartColorVar.r;
2348             particleStartColorVar.g = locStartColorVar.g;
2349             particleStartColorVar.b = locStartColorVar.b;
2350             particleStartColorVar.a = locStartColorVar.a;
2351 
2352             var particleEndColor = retParticle._endColor, locEndColor = this._endColor;
2353             particleEndColor.r = locEndColor.r;
2354             particleEndColor.g = locEndColor.g;
2355             particleEndColor.b = locEndColor.b;
2356             particleEndColor.a = locEndColor.a;
2357 
2358             var particleEndColorVar = retParticle._endColorVar, locEndColorVar = this._endColorVar;
2359             particleEndColorVar.r = locEndColorVar.r;
2360             particleEndColorVar.g = locEndColorVar.g;
2361             particleEndColorVar.b = locEndColorVar.b;
2362             particleEndColorVar.a = locEndColorVar.a;
2363 
2364             // particle size
2365             retParticle.startSize = this.startSize;
2366             retParticle.startSizeVar = this.startSizeVar;
2367             retParticle.endSize = this.endSize;
2368             retParticle.endSizeVar = this.endSizeVar;
2369 
2370             // position
2371             retParticle.x = this._position.x;
2372 	        retParticle.y = this._position.y;
2373             retParticle._posVar.x = this._posVar.x;
2374             retParticle._posVar.y = this._posVar.y;
2375 
2376             // Spinning
2377             retParticle.startSpin = this.startSpin;
2378             retParticle.startSpinVar = this.startSpinVar;
2379             retParticle.endSpin = this.endSpin;
2380             retParticle.endSpinVar = this.endSpinVar;
2381 
2382             retParticle.emitterMode = this.emitterMode;
2383 
2384             // Mode A: Gravity + tangential accel + radial accel
2385             if (this.emitterMode == cc.PARTICLE_MODE_GRAVITY) {
2386                 var particleModeA = retParticle.modeA, locModeA = this.modeA;
2387                 // gravity
2388                 particleModeA.gravity.x = locModeA.gravity.x;
2389                 particleModeA.gravity.y = locModeA.gravity.y;
2390 
2391                 // speed
2392                 particleModeA.speed = locModeA.speed;
2393                 particleModeA.speedVar = locModeA.speedVar;
2394 
2395                 // radial acceleration
2396                 particleModeA.radialAccel = locModeA.radialAccel;
2397 
2398                 particleModeA.radialAccelVar = locModeA.radialAccelVar;
2399 
2400                 // tangential acceleration
2401                 particleModeA.tangentialAccel = locModeA.tangentialAccel;
2402 
2403                 particleModeA.tangentialAccelVar = locModeA.tangentialAccelVar;
2404             } else if (this.emitterMode == cc.PARTICLE_MODE_RADIUS) {
2405                 var particleModeB = retParticle.modeB, locModeB = this.modeB;
2406                 // or Mode B: radius movement
2407                 particleModeB.startRadius = locModeB.startRadius;
2408                 particleModeB.startRadiusVar = locModeB.startRadiusVar;
2409                 particleModeB.endRadius = locModeB.endRadius;
2410                 particleModeB.endRadiusVar = locModeB.endRadiusVar;
2411                 particleModeB.rotatePerSecond = locModeB.rotatePerSecond;
2412                 particleModeB.rotatePerSecondVar = locModeB.rotatePerSecondVar;
2413             }
2414 
2415             // life span
2416             retParticle.life = this.life;
2417             retParticle.lifeVar = this.lifeVar;
2418 
2419             // emission Rate
2420             retParticle.emissionRate = this.emissionRate;
2421 
2422             //don't get the internal texture if a batchNode is used
2423             if (!this._batchNode) {
2424                 // Set a compatible default for the alpha transfer
2425                 retParticle._opacityModifyRGB = this._opacityModifyRGB;
2426 
2427                 // texture
2428                 retParticle._texture = this._texture;
2429             }
2430         }
2431         return retParticle;
2432     },
2433 
2434     /**
2435      * <p> Sets a new CCSpriteFrame as particle.</br>
2436      * WARNING: this method is experimental. Use setTextureWithRect instead.
2437      * </p>
2438      * @param {cc.SpriteFrame} spriteFrame
2439      */
2440     setDisplayFrame:function (spriteFrame) {
2441         var locOffset = spriteFrame.getOffsetInPixels();
2442         if(locOffset.x != 0 || locOffset.y != 0)
2443             cc.log("cc.ParticleSystem.setDisplayFrame(): QuadParticle only supports SpriteFrames with no offsets");
2444 
2445         // update texture before updating texture rect
2446         if (cc._renderType === cc._RENDER_TYPE_WEBGL)
2447             if (!this._texture || spriteFrame.getTexture()._webTextureObj != this._texture._webTextureObj)
2448                 this.setTexture(spriteFrame.getTexture());
2449     },
2450 
2451     /**
2452      *  Sets a new texture with a rect. The rect is in Points.
2453      * @param {cc.Texture2D} texture
2454      * @param {cc.Rect} rect
2455      */
2456     setTextureWithRect:function (texture, rect) {
2457         var locTexture = this._texture;
2458         if (cc._renderType === cc._RENDER_TYPE_WEBGL) {
2459             // Only update the texture if is different from the current one
2460             if ((!locTexture || texture._webTextureObj != locTexture._webTextureObj) && (locTexture != texture)) {
2461                 this._texture = texture;
2462                 this._updateBlendFunc();
2463             }
2464         } else {
2465             if ((!locTexture || texture != locTexture) && (locTexture != texture)) {
2466                 this._texture = texture;
2467                 this._updateBlendFunc();
2468             }
2469         }
2470 
2471         this._pointRect = rect;
2472         this.initTexCoordsWithRect(rect);
2473     },
2474 
2475     /**
2476      * draw particle
2477      * @param {CanvasRenderingContext2D} ctx CanvasContext
2478      * @override
2479      */
2480     draw:function (ctx) {
2481         if(!this._textureLoaded || this._batchNode)     // draw should not be called when added to a particleBatchNode
2482             return;
2483 
2484         if (cc._renderType === cc._RENDER_TYPE_CANVAS)
2485             this._drawForCanvas(ctx);
2486         else
2487             this._drawForWebGL(ctx);
2488 
2489         cc.g_NumberOfDraws++;
2490     },
2491 
2492     _drawForCanvas:function (ctx) {
2493         var context = ctx || cc._renderContext;
2494         context.save();
2495         if (this.isBlendAdditive())
2496             context.globalCompositeOperation = 'lighter';
2497         else
2498             context.globalCompositeOperation = 'source-over';
2499 
2500         for (var i = 0; i < this.particleCount; i++) {
2501             var particle = this._particles[i];
2502             var lpx = (0 | (particle.size * 0.5));
2503 
2504             if (this.drawMode == cc.PARTICLE_TEXTURE_MODE) {
2505 
2506                 var element = this._texture.getHtmlElementObj();
2507 
2508                 // Delay drawing until the texture is fully loaded by the browser
2509                 if (!element.width || !element.height)
2510                     continue;
2511 
2512                 context.save();
2513                 context.globalAlpha = particle.color.a / 255;
2514                 context.translate((0 | particle.drawPos.x), -(0 | particle.drawPos.y));
2515 
2516                 var size = Math.floor(particle.size / 4) * 4;
2517                 var w = this._pointRect.width;
2518                 var h = this._pointRect.height;
2519 
2520                 context.scale(
2521                     Math.max((1 / w) * size, 0.000001),
2522                     Math.max((1 / h) * size, 0.000001)
2523                 );
2524 
2525 
2526                 if (particle.rotation)
2527                     context.rotate(cc.degreesToRadians(particle.rotation));
2528 
2529                 context.translate(-(0 | (w / 2)), -(0 | (h / 2)));
2530                 if (particle.isChangeColor) {
2531 
2532                     var cacheTextureForColor = cc.textureCache.getTextureColors(element);
2533                     if (cacheTextureForColor) {
2534                         // Create another cache for the tinted version
2535                         // This speeds up things by a fair bit
2536                         if (!cacheTextureForColor.tintCache) {
2537                             cacheTextureForColor.tintCache = cc.newElement('canvas');
2538                             cacheTextureForColor.tintCache.width = element.width;
2539                             cacheTextureForColor.tintCache.height = element.height;
2540                         }
2541                         cc.generateTintImage(element, cacheTextureForColor, particle.color, this._pointRect, cacheTextureForColor.tintCache);
2542                         element = cacheTextureForColor.tintCache;
2543                     }
2544                 }
2545 
2546                 context.drawImage(element, 0, 0);
2547                 context.restore();
2548 
2549             } else {
2550                 context.save();
2551                 context.globalAlpha = particle.color.a / 255;
2552 
2553                 context.translate(0 | particle.drawPos.x, -(0 | particle.drawPos.y));
2554 
2555                 if (this.shapeType == cc.PARTICLE_STAR_SHAPE) {
2556                     if (particle.rotation)
2557                         context.rotate(cc.degreesToRadians(particle.rotation));
2558                     cc._drawingUtil.drawStar(context, lpx, particle.color);
2559                 } else
2560                     cc._drawingUtil.drawColorBall(context, lpx, particle.color);
2561                 context.restore();
2562             }
2563         }
2564         context.restore();
2565     },
2566 
2567     _drawForWebGL:function (ctx) {
2568         if(!this._texture)
2569             return;
2570 
2571         var gl = ctx || cc._renderContext;
2572 
2573         this._shaderProgram.use();
2574         this._shaderProgram.setUniformForModelViewAndProjectionMatrixWithMat4();
2575 
2576         cc.glBindTexture2D(this._texture);
2577         cc.glBlendFuncForParticle(this._blendFunc.src, this._blendFunc.dst);
2578 
2579         //cc.assert(this._particleIdx == this.particleCount, "Abnormal error in particle quad");
2580 
2581         //
2582         // Using VBO without VAO
2583         //
2584         cc.glEnableVertexAttribs(cc.VERTEX_ATTRIB_FLAG_POS_COLOR_TEX);
2585 
2586         gl.bindBuffer(gl.ARRAY_BUFFER, this._buffersVBO[0]);
2587         gl.vertexAttribPointer(cc.VERTEX_ATTRIB_POSITION, 3, gl.FLOAT, false, 24, 0);               // vertices
2588         gl.vertexAttribPointer(cc.VERTEX_ATTRIB_COLOR, 4, gl.UNSIGNED_BYTE, true, 24, 12);          // colors
2589         gl.vertexAttribPointer(cc.VERTEX_ATTRIB_TEX_COORDS, 2, gl.FLOAT, false, 24, 16);            // tex coords
2590 
2591         gl.bindBuffer(gl.ELEMENT_ARRAY_BUFFER, this._buffersVBO[1]);
2592         gl.drawElements(gl.TRIANGLES, this._particleIdx * 6, gl.UNSIGNED_SHORT, 0);
2593     },
2594 
2595     /**
2596      * listen the event that coming to foreground on Android
2597      * @param {cc.Class} obj
2598      */
2599     listenBackToForeground:function (obj) {
2600         if (cc.TEXTURE_ATLAS_USE_VAO)
2601             this._setupVBOandVAO();
2602         else
2603             this._setupVBO();
2604     },
2605 
2606     _setupVBOandVAO:function () {
2607         //Not support on WebGL
2608         /*if (cc._renderType == cc._RENDER_TYPE_CANVAS) {
2609          return;
2610          }*/
2611 
2612         //NOT SUPPORTED
2613         /*glGenVertexArrays(1, this._VAOname);
2614          glBindVertexArray(this._VAOname);
2615 
2616          var kQuadSize = sizeof(m_pQuads[0].bl);
2617 
2618          glGenBuffers(2, this._buffersVBO[0]);
2619 
2620          glBindBuffer(GL_ARRAY_BUFFER, this._buffersVBO[0]);
2621          glBufferData(GL_ARRAY_BUFFER, sizeof(this._quads[0]) * this._totalParticles, this._quads, GL_DYNAMIC_DRAW);
2622 
2623          // vertices
2624          glEnableVertexAttribArray(kCCVertexAttrib_Position);
2625          glVertexAttribPointer(kCCVertexAttrib_Position, 2, GL_FLOAT, GL_FALSE, kQuadSize, offsetof(ccV3F_C4B_T2F, vertices));
2626 
2627          // colors
2628          glEnableVertexAttribArray(kCCVertexAttrib_Color);
2629          glVertexAttribPointer(kCCVertexAttrib_Color, 4, GL_UNSIGNED_BYTE, GL_TRUE, kQuadSize, offsetof(ccV3F_C4B_T2F, colors));
2630 
2631          // tex coords
2632          glEnableVertexAttribArray(kCCVertexAttrib_TexCoords);
2633          glVertexAttribPointer(kCCVertexAttrib_TexCoords, 2, GL_FLOAT, GL_FALSE, kQuadSize, offsetof(ccV3F_C4B_T2F, texCoords));
2634 
2635          glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, this._buffersVBO[1]);
2636          glBufferData(GL_ELEMENT_ARRAY_BUFFER, sizeof(m_pIndices[0]) * m_uTotalParticles * 6, m_pIndices, GL_STATIC_DRAW);
2637 
2638          glBindVertexArray(0);
2639          glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, 0);
2640          glBindBuffer(GL_ARRAY_BUFFER, 0);
2641 
2642          CHECK_GL_ERROR_DEBUG();*/
2643     },
2644 
2645     _setupVBO:function () {
2646         if (cc._renderType == cc._RENDER_TYPE_CANVAS)
2647             return;
2648 
2649         var gl = cc._renderContext;
2650 
2651         //gl.deleteBuffer(this._buffersVBO[0]);
2652         this._buffersVBO[0] = gl.createBuffer();
2653         gl.bindBuffer(gl.ARRAY_BUFFER, this._buffersVBO[0]);
2654         gl.bufferData(gl.ARRAY_BUFFER, this._quadsArrayBuffer, gl.DYNAMIC_DRAW);
2655 
2656         this._buffersVBO[1] = gl.createBuffer();
2657         gl.bindBuffer(gl.ELEMENT_ARRAY_BUFFER, this._buffersVBO[1]);
2658         gl.bufferData(gl.ELEMENT_ARRAY_BUFFER, this._indices, gl.STATIC_DRAW);
2659 
2660         //cc.checkGLErrorDebug();
2661     },
2662 
2663     _allocMemory:function () {
2664         if (cc._renderType === cc._RENDER_TYPE_CANVAS)
2665             return true;
2666 
2667         //cc.assert((!this._quads && !this._indices), "Memory already allocated");
2668         if(this._batchNode){
2669             cc.log("cc.ParticleSystem._allocMemory(): Memory should not be allocated when not using batchNode");
2670             return false;
2671         }
2672 
2673         var quadSize = cc.V3F_C4B_T2F_Quad.BYTES_PER_ELEMENT;
2674         var totalParticles = this._totalParticles;
2675         var locQuads = this._quads;
2676         locQuads.length = 0;
2677         this._indices = new Uint16Array(totalParticles * 6);
2678         var locQuadsArrayBuffer = new ArrayBuffer(quadSize * totalParticles);
2679 
2680         for (var i = 0; i < totalParticles; i++)
2681             locQuads[i] = new cc.V3F_C4B_T2F_Quad(null, null, null, null, locQuadsArrayBuffer, i * quadSize);
2682         if (!locQuads || !this._indices) {
2683             cc.log("cocos2d: Particle system: not enough memory");
2684             return false;
2685         }
2686         this._quadsArrayBuffer = locQuadsArrayBuffer;
2687         return true;
2688     }
2689 });
2690 
2691 var _p = cc.ParticleSystem.prototype;
2692 
2693 // Extended properties
2694 /** @expose */
2695 _p.opacityModifyRGB;
2696 cc.defineGetterSetter(_p, "opacityModifyRGB", _p.isOpacityModifyRGB, _p.setOpacityModifyRGB);
2697 /** @expose */
2698 _p.batchNode;
2699 cc.defineGetterSetter(_p, "batchNode", _p.getBatchNode, _p.setBatchNode);
2700 /** @expose */
2701 _p.active;
2702 cc.defineGetterSetter(_p, "active", _p.isActive);
2703 /** @expose */
2704 _p.sourcePos;
2705 cc.defineGetterSetter(_p, "sourcePos", _p.getSourcePosition, _p.setSourcePosition);
2706 /** @expose */
2707 _p.posVar;
2708 cc.defineGetterSetter(_p, "posVar", _p.getPosVar, _p.setPosVar);
2709 /** @expose */
2710 _p.gravity;
2711 cc.defineGetterSetter(_p, "gravity", _p.getGravity, _p.setGravity);
2712 /** @expose */
2713 _p.speed;
2714 cc.defineGetterSetter(_p, "speed", _p.getSpeed, _p.setSpeed);
2715 /** @expose */
2716 _p.speedVar;
2717 cc.defineGetterSetter(_p, "speedVar", _p.getSpeedVar, _p.setSpeedVar);
2718 /** @expose */
2719 _p.tangentialAccel;
2720 cc.defineGetterSetter(_p, "tangentialAccel", _p.getTangentialAccel, _p.setTangentialAccel);
2721 /** @expose */
2722 _p.tangentialAccelVar;
2723 cc.defineGetterSetter(_p, "tangentialAccelVar", _p.getTangentialAccelVar, _p.setTangentialAccelVar);
2724 /** @expose */
2725 _p.radialAccel;
2726 cc.defineGetterSetter(_p, "radialAccel", _p.getRadialAccel, _p.setRadialAccel);
2727 /** @expose */
2728 _p.radialAccelVar;
2729 cc.defineGetterSetter(_p, "radialAccelVar", _p.getRadialAccelVar, _p.setRadialAccelVar);
2730 /** @expose */
2731 _p.rotationIsDir;
2732 cc.defineGetterSetter(_p, "rotationIsDir", _p.getRotationIsDir, _p.setRotationIsDir);
2733 /** @expose */
2734 _p.startRadius;
2735 cc.defineGetterSetter(_p, "startRadius", _p.getStartRadius, _p.setStartRadius);
2736 /** @expose */
2737 _p.startRadiusVar;
2738 cc.defineGetterSetter(_p, "startRadiusVar", _p.getStartRadiusVar, _p.setStartRadiusVar);
2739 /** @expose */
2740 _p.endRadius;
2741 cc.defineGetterSetter(_p, "endRadius", _p.getEndRadius, _p.setEndRadius);
2742 /** @expose */
2743 _p.endRadiusVar;
2744 cc.defineGetterSetter(_p, "endRadiusVar", _p.getEndRadiusVar, _p.setEndRadiusVar);
2745 /** @expose */
2746 _p.rotatePerS;
2747 cc.defineGetterSetter(_p, "rotatePerS", _p.getRotatePerSecond, _p.setRotatePerSecond);
2748 /** @expose */
2749 _p.rotatePerSVar;
2750 cc.defineGetterSetter(_p, "rotatePerSVar", _p.getRotatePerSecondVar, _p.setRotatePerSecondVar);
2751 /** @expose */
2752 _p.startColor;
2753 cc.defineGetterSetter(_p, "startColor", _p.getStartColor, _p.setStartColor);
2754 /** @expose */
2755 _p.startColorVar;
2756 cc.defineGetterSetter(_p, "startColorVar", _p.getStartColorVar, _p.setStartColorVar);
2757 /** @expose */
2758 _p.endColor;
2759 cc.defineGetterSetter(_p, "endColor", _p.getEndColor, _p.setEndColor);
2760 /** @expose */
2761 _p.endColorVar;
2762 cc.defineGetterSetter(_p, "endColorVar", _p.getEndColorVar, _p.setEndColorVar);
2763 /** @expose */
2764 _p.totalParticles;
2765 cc.defineGetterSetter(_p, "totalParticles", _p.getTotalParticles, _p.setTotalParticles);
2766 /** @expose */
2767 _p.texture;
2768 cc.defineGetterSetter(_p, "texture", _p.getTexture, _p.setTexture);
2769 
2770 
2771 /**
2772  * <p> return the string found by key in dict. <br/>
2773  *    This plist files can be create manually or with Particle Designer:<br/>
2774  *    http://particledesigner.71squared.com/<br/>
2775  * </p>
2776  * @param {String|Number} plistFile
2777  * @return {cc.ParticleSystem}
2778  */
2779 cc.ParticleSystem.create = function (plistFile) {
2780     return new cc.ParticleSystem(plistFile);
2781 };
2782 
2783 // Different modes
2784 /**
2785  * Mode A:Gravity + Tangential Accel + Radial Accel
2786  * @Class
2787  * @Construct
2788  * @param {cc.Point} [gravity=] Gravity value.
2789  * @param {Number} [speed=0] speed of each particle.
2790  * @param {Number} [speedVar=0] speed variance of each particle.
2791  * @param {Number} [tangentialAccel=0] tangential acceleration of each particle.
2792  * @param {Number} [tangentialAccelVar=0] tangential acceleration variance of each particle.
2793  * @param {Number} [radialAccel=0] radial acceleration of each particle.
2794  * @param {Number} [radialAccelVar=0] radial acceleration variance of each particle.
2795  * @param {boolean} [rotationIsDir=false]
2796  */
2797 cc.ParticleSystem.ModeA = function (gravity, speed, speedVar, tangentialAccel, tangentialAccelVar, radialAccel, radialAccelVar, rotationIsDir) {
2798     /** Gravity value. Only available in 'Gravity' mode. */
2799     this.gravity = gravity ? gravity : cc.p(0,0);
2800     /** speed of each particle. Only available in 'Gravity' mode.  */
2801     this.speed = speed || 0;
2802     /** speed variance of each particle. Only available in 'Gravity' mode. */
2803     this.speedVar = speedVar || 0;
2804     /** tangential acceleration of each particle. Only available in 'Gravity' mode. */
2805     this.tangentialAccel = tangentialAccel || 0;
2806     /** tangential acceleration variance of each particle. Only available in 'Gravity' mode. */
2807     this.tangentialAccelVar = tangentialAccelVar || 0;
2808     /** radial acceleration of each particle. Only available in 'Gravity' mode. */
2809     this.radialAccel = radialAccel || 0;
2810     /** radial acceleration variance of each particle. Only available in 'Gravity' mode. */
2811     this.radialAccelVar = radialAccelVar || 0;
2812     /** set the rotation of each particle to its direction Only available in 'Gravity' mode. */
2813     this.rotationIsDir = rotationIsDir || false;
2814 };
2815 
2816 /**
2817  * Mode B: circular movement (gravity, radial accel and tangential accel don't are not used in this mode)
2818  * @Class
2819  * @Construct
2820  * @param {Number} startRadius The starting radius of the particles.
2821  * @param {Number} startRadiusVar The starting radius variance of the particles.
2822  * @param {Number} endRadius The ending radius of the particles.
2823  * @param {Number} endRadiusVar The ending radius variance of the particles.
2824  * @param {Number} rotatePerSecond Number of degress to rotate a particle around the source pos per second.
2825  * @param {Number} rotatePerSecondVar Variance in degrees for rotatePerSecond.
2826  */
2827 cc.ParticleSystem.ModeB = function (startRadius, startRadiusVar, endRadius, endRadiusVar, rotatePerSecond, rotatePerSecondVar) {
2828     /** The starting radius of the particles. Only available in 'Radius' mode. */
2829     this.startRadius = startRadius || 0;
2830     /** The starting radius variance of the particles. Only available in 'Radius' mode. */
2831     this.startRadiusVar = startRadiusVar || 0;
2832     /** The ending radius of the particles. Only available in 'Radius' mode. */
2833     this.endRadius = endRadius || 0;
2834     /** The ending radius variance of the particles. Only available in 'Radius' mode. */
2835     this.endRadiusVar = endRadiusVar || 0;
2836     /** Number of degress to rotate a particle around the source pos per second. Only available in 'Radius' mode. */
2837     this.rotatePerSecond = rotatePerSecond || 0;
2838     /** Variance in degrees for rotatePerSecond. Only available in 'Radius' mode. */
2839     this.rotatePerSecondVar = rotatePerSecondVar || 0;
2840 };
2841