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