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