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.getTextureForKey(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.getTextureForKey(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     /**
1719      * Unschedules the "update" method.
1720      * @function
1721      * @see scheduleUpdate();
1722      */
1723     destroyParticleSystem:function () {
1724         this.unscheduleUpdate();
1725     },
1726 
1727     /**
1728      * Add a particle to the emitter
1729      * @return {Boolean}
1730      */
1731     addParticle: function () {
1732         if (this.isFull())
1733             return false;
1734         var particle, particles = this._particles;
1735         if (cc._renderType === cc._RENDER_TYPE_CANVAS) {
1736             if (this.particleCount < particles.length) {
1737                 particle = particles[this.particleCount];
1738             } else {
1739                 particle = new cc.Particle();
1740                 particles.push(particle);
1741             }
1742         } else {
1743             particle = particles[this.particleCount];
1744         }
1745         this.initParticle(particle);
1746         ++this.particleCount;
1747         return true;
1748     },
1749 
1750     /**
1751      * Initializes a particle
1752      * @param {cc.Particle} particle
1753      */
1754     initParticle:function (particle) {
1755         var locRandomMinus11 = cc.randomMinus1To1;
1756         // timeToLive
1757         // no negative life. prevent division by 0
1758         particle.timeToLive = this.life + this.lifeVar * locRandomMinus11();
1759         particle.timeToLive = Math.max(0, particle.timeToLive);
1760 
1761         // position
1762         particle.pos.x = this._sourcePosition.x + this._posVar.x * locRandomMinus11();
1763         particle.pos.y = this._sourcePosition.y + this._posVar.y * locRandomMinus11();
1764 
1765         // Color
1766         var start, end;
1767         var locStartColor = this._startColor, locStartColorVar = this._startColorVar;
1768         var locEndColor = this._endColor, locEndColorVar = this._endColorVar;
1769         if (cc._renderType === cc._RENDER_TYPE_CANVAS) {
1770             start = cc.color(
1771                 cc.clampf(locStartColor.r + locStartColorVar.r * locRandomMinus11(), 0, 255),
1772                 cc.clampf(locStartColor.g + locStartColorVar.g * locRandomMinus11(), 0, 255),
1773                 cc.clampf(locStartColor.b + locStartColorVar.b * locRandomMinus11(), 0, 255),
1774                 cc.clampf(locStartColor.a + locStartColorVar.a * locRandomMinus11(), 0, 255)
1775             );
1776             end = cc.color(
1777                 cc.clampf(locEndColor.r + locEndColorVar.r * locRandomMinus11(), 0, 255),
1778                 cc.clampf(locEndColor.g + locEndColorVar.g * locRandomMinus11(), 0, 255),
1779                 cc.clampf(locEndColor.b + locEndColorVar.b * locRandomMinus11(), 0, 255),
1780                 cc.clampf(locEndColor.a + locEndColorVar.a * locRandomMinus11(), 0, 255)
1781             );
1782         } else {
1783             start = {
1784                 r: cc.clampf(locStartColor.r + locStartColorVar.r * locRandomMinus11(), 0, 255),
1785                 g: cc.clampf(locStartColor.g + locStartColorVar.g * locRandomMinus11(), 0, 255),
1786                 b: cc.clampf(locStartColor.b + locStartColorVar.b * locRandomMinus11(), 0, 255),
1787                 a: cc.clampf(locStartColor.a + locStartColorVar.a * locRandomMinus11(), 0, 255)
1788             };
1789             end = {
1790                 r: cc.clampf(locEndColor.r + locEndColorVar.r * locRandomMinus11(), 0, 255),
1791                 g: cc.clampf(locEndColor.g + locEndColorVar.g * locRandomMinus11(), 0, 255),
1792                 b: cc.clampf(locEndColor.b + locEndColorVar.b * locRandomMinus11(), 0, 255),
1793                 a: cc.clampf(locEndColor.a + locEndColorVar.a * locRandomMinus11(), 0, 255)
1794             };
1795         }
1796 
1797         particle.color = start;
1798         var locParticleDeltaColor = particle.deltaColor, locParticleTimeToLive = particle.timeToLive;
1799         locParticleDeltaColor.r = (end.r - start.r) / locParticleTimeToLive;
1800         locParticleDeltaColor.g = (end.g - start.g) / locParticleTimeToLive;
1801         locParticleDeltaColor.b = (end.b - start.b) / locParticleTimeToLive;
1802         locParticleDeltaColor.a = (end.a - start.a) / locParticleTimeToLive;
1803 
1804         // size
1805         var startS = this.startSize + this.startSizeVar * locRandomMinus11();
1806         startS = Math.max(0, startS); // No negative value
1807 
1808         particle.size = startS;
1809         if (this.endSize === cc.ParticleSystem.START_SIZE_EQUAL_TO_END_SIZE) {
1810             particle.deltaSize = 0;
1811         } else {
1812             var endS = this.endSize + this.endSizeVar * locRandomMinus11();
1813             endS = Math.max(0, endS); // No negative values
1814             particle.deltaSize = (endS - startS) / locParticleTimeToLive;
1815         }
1816 
1817         // rotation
1818         var startA = this.startSpin + this.startSpinVar * locRandomMinus11();
1819         var endA = this.endSpin + this.endSpinVar * locRandomMinus11();
1820         particle.rotation = startA;
1821         particle.deltaRotation = (endA - startA) / locParticleTimeToLive;
1822 
1823         // position
1824         if (this.positionType == cc.ParticleSystem.TYPE_FREE)
1825             particle.startPos = this.convertToWorldSpace(this._pointZeroForParticle);
1826         else if (this.positionType == cc.ParticleSystem.TYPE_RELATIVE){
1827             particle.startPos.x = this._position.x;
1828             particle.startPos.y = this._position.y;
1829         }
1830 
1831         // direction
1832         var a = cc.degreesToRadians(this.angle + this.angleVar * locRandomMinus11());
1833 
1834         // Mode Gravity: A
1835         if (this.emitterMode === cc.ParticleSystem.MODE_GRAVITY) {
1836             var locModeA = this.modeA, locParticleModeA = particle.modeA;
1837             var s = locModeA.speed + locModeA.speedVar * locRandomMinus11();
1838 
1839             // direction
1840             locParticleModeA.dir.x = Math.cos(a);
1841             locParticleModeA.dir.y = Math.sin(a);
1842             cc.pMultIn(locParticleModeA.dir, s);
1843 
1844             // radial accel
1845             locParticleModeA.radialAccel = locModeA.radialAccel + locModeA.radialAccelVar * locRandomMinus11();
1846 
1847             // tangential accel
1848             locParticleModeA.tangentialAccel = locModeA.tangentialAccel + locModeA.tangentialAccelVar * locRandomMinus11();
1849 
1850             // rotation is dir
1851             if(locModeA.rotationIsDir)
1852                 particle.rotation = -cc.radiansToDegrees(cc.pToAngle(locParticleModeA.dir));
1853         } else {
1854             // Mode Radius: B
1855             var locModeB = this.modeB, locParitlceModeB = particle.modeB;
1856 
1857             // Set the default diameter of the particle from the source position
1858             var startRadius = locModeB.startRadius + locModeB.startRadiusVar * locRandomMinus11();
1859             var endRadius = locModeB.endRadius + locModeB.endRadiusVar * locRandomMinus11();
1860 
1861             locParitlceModeB.radius = startRadius;
1862             locParitlceModeB.deltaRadius = (locModeB.endRadius === cc.ParticleSystem.START_RADIUS_EQUAL_TO_END_RADIUS) ? 0 : (endRadius - startRadius) / locParticleTimeToLive;
1863 
1864             locParitlceModeB.angle = a;
1865             locParitlceModeB.degreesPerSecond = cc.degreesToRadians(locModeB.rotatePerSecond + locModeB.rotatePerSecondVar * locRandomMinus11());
1866         }
1867     },
1868 
1869     /**
1870      * stop emitting particles. Running particles will continue to run until they die
1871      */
1872     stopSystem:function () {
1873         this._isActive = false;
1874         this._elapsed = this.duration;
1875         this._emitCounter = 0;
1876     },
1877 
1878     /**
1879      * Kill all living particles.
1880      */
1881     resetSystem:function () {
1882         this._isActive = true;
1883         this._elapsed = 0;
1884         var locParticles = this._particles;
1885         for (this._particleIdx = 0; this._particleIdx < this.particleCount; ++this._particleIdx)
1886             locParticles[this._particleIdx].timeToLive = 0 ;
1887     },
1888 
1889     /**
1890      * whether or not the system is full
1891      * @return {Boolean}
1892      */
1893     isFull:function () {
1894         return (this.particleCount >= this._totalParticles);
1895     },
1896 
1897     /**
1898      * should be overridden by subclasses
1899      * @param {cc.Particle} particle
1900      * @param {cc.Point} newPosition
1901      */
1902     updateQuadWithParticle:function (particle, newPosition) {
1903         var quad = null;
1904         if (this._batchNode) {
1905             var batchQuads = this._batchNode.textureAtlas.quads;
1906             quad = batchQuads[this.atlasIndex + particle.atlasIndex];
1907             this._batchNode.textureAtlas.dirty = true;
1908         } else
1909             quad = this._quads[this._particleIdx];
1910 
1911         var r, g, b, a;
1912         if (this._opacityModifyRGB) {
1913             r = 0 | (particle.color.r * particle.color.a/255);
1914             g = 0 | (particle.color.g * particle.color.a/255);
1915             b = 0 | (particle.color.b * particle.color.a/255);
1916         } else {
1917             r = 0 | (particle.color.r );
1918             g = 0 | (particle.color.g );
1919             b = 0 | (particle.color.b );
1920         }
1921         a = 0 | (particle.color.a );
1922 
1923         var locColors = quad.bl.colors;
1924         locColors.r = r;
1925         locColors.g = g;
1926         locColors.b = b;
1927         locColors.a = a;
1928 
1929         locColors = quad.br.colors;
1930         locColors.r = r;
1931         locColors.g = g;
1932         locColors.b = b;
1933         locColors.a = a;
1934 
1935         locColors = quad.tl.colors;
1936         locColors.r = r;
1937         locColors.g = g;
1938         locColors.b = b;
1939         locColors.a = a;
1940 
1941         locColors = quad.tr.colors;
1942         locColors.r = r;
1943         locColors.g = g;
1944         locColors.b = b;
1945         locColors.a = a;
1946 
1947         // vertices
1948         var size_2 = particle.size / 2;
1949         if (particle.rotation) {
1950             var x1 = -size_2;
1951             var y1 = -size_2;
1952 
1953             var x2 = size_2;
1954             var y2 = size_2;
1955             var x = newPosition.x;
1956             var y = newPosition.y;
1957 
1958             var rad = -cc.degreesToRadians(particle.rotation);
1959             var cr = Math.cos(rad);
1960             var sr = Math.sin(rad);
1961             var ax = x1 * cr - y1 * sr + x;
1962             var ay = x1 * sr + y1 * cr + y;
1963             var bx = x2 * cr - y1 * sr + x;
1964             var by = x2 * sr + y1 * cr + y;
1965             var cx = x2 * cr - y2 * sr + x;
1966             var cy = x2 * sr + y2 * cr + y;
1967             var dx = x1 * cr - y2 * sr + x;
1968             var dy = x1 * sr + y2 * cr + y;
1969 
1970             // bottom-left
1971             quad.bl.vertices.x = ax;
1972             quad.bl.vertices.y = ay;
1973 
1974             // bottom-right vertex:
1975             quad.br.vertices.x = bx;
1976             quad.br.vertices.y = by;
1977 
1978             // top-left vertex:
1979             quad.tl.vertices.x = dx;
1980             quad.tl.vertices.y = dy;
1981 
1982             // top-right vertex:
1983             quad.tr.vertices.x = cx;
1984             quad.tr.vertices.y = cy;
1985         } else {
1986             // bottom-left vertex:
1987             quad.bl.vertices.x = newPosition.x - size_2;
1988             quad.bl.vertices.y = newPosition.y - size_2;
1989 
1990             // bottom-right vertex:
1991             quad.br.vertices.x = newPosition.x + size_2;
1992             quad.br.vertices.y = newPosition.y - size_2;
1993 
1994             // top-left vertex:
1995             quad.tl.vertices.x = newPosition.x - size_2;
1996             quad.tl.vertices.y = newPosition.y + size_2;
1997 
1998             // top-right vertex:
1999             quad.tr.vertices.x = newPosition.x + size_2;
2000             quad.tr.vertices.y = newPosition.y + size_2;
2001         }
2002     },
2003 
2004     /**
2005      * should be overridden by subclasses
2006      */
2007     postStep:function () {
2008         if (cc._renderType === cc._RENDER_TYPE_WEBGL) {
2009             var gl = cc._renderContext;
2010 
2011             gl.bindBuffer(gl.ARRAY_BUFFER, this._buffersVBO[0]);
2012             gl.bufferData(gl.ARRAY_BUFFER, this._quadsArrayBuffer, gl.DYNAMIC_DRAW);
2013 
2014             // Option 2: Data
2015             //	glBufferData(GL_ARRAY_BUFFER, sizeof(quads_[0]) * particleCount, quads_, GL_DYNAMIC_DRAW);
2016 
2017             // Option 3: Orphaning + glMapBuffer
2018             // glBufferData(GL_ARRAY_BUFFER, sizeof(m_pQuads[0])*m_uTotalParticles, NULL, GL_STREAM_DRAW);
2019             // void *buf = glMapBuffer(GL_ARRAY_BUFFER, GL_WRITE_ONLY);
2020             // memcpy(buf, m_pQuads, sizeof(m_pQuads[0])*m_uTotalParticles);
2021             // glUnmapBuffer(GL_ARRAY_BUFFER);
2022 
2023             //cc.checkGLErrorDebug();
2024         }
2025     },
2026 
2027     /**
2028      * update emitter's status
2029      * @override
2030      * @param {Number} dt delta time
2031      */
2032     update:function (dt) {
2033         if (this._isActive && this.emissionRate) {
2034             var rate = 1.0 / this.emissionRate;
2035             //issue #1201, prevent bursts of particles, due to too high emitCounter
2036             if (this.particleCount < this._totalParticles)
2037                 this._emitCounter += dt;
2038 
2039             while ((this.particleCount < this._totalParticles) && (this._emitCounter > rate)) {
2040                 this.addParticle();
2041                 this._emitCounter -= rate;
2042             }
2043 
2044             this._elapsed += dt;
2045             if (this.duration != -1 && this.duration < this._elapsed)
2046                 this.stopSystem();
2047         }
2048         this._particleIdx = 0;
2049 
2050         var currentPosition = cc.Particle.TemporaryPoints[0];
2051         if (this.positionType == cc.ParticleSystem.TYPE_FREE) {
2052             cc.pIn(currentPosition, this.convertToWorldSpace(this._pointZeroForParticle));
2053         } else if (this.positionType == cc.ParticleSystem.TYPE_RELATIVE) {
2054             currentPosition.x = this._position.x;
2055             currentPosition.y = this._position.y;
2056         }
2057 
2058         if (this._visible) {
2059 
2060             // Used to reduce memory allocation / creation within the loop
2061             var tpa = cc.Particle.TemporaryPoints[1],
2062                 tpb = cc.Particle.TemporaryPoints[2],
2063                 tpc = cc.Particle.TemporaryPoints[3];
2064 
2065             var locParticles = this._particles;
2066             while (this._particleIdx < this.particleCount) {
2067 
2068                 // Reset the working particles
2069                 cc.pZeroIn(tpa);
2070                 cc.pZeroIn(tpb);
2071                 cc.pZeroIn(tpc);
2072 
2073                 var selParticle = locParticles[this._particleIdx];
2074 
2075                 // life
2076                 selParticle.timeToLive -= dt;
2077 
2078                 if (selParticle.timeToLive > 0) {
2079                     // Mode A: gravity, direction, tangential accel & radial accel
2080                     if (this.emitterMode == cc.ParticleSystem.MODE_GRAVITY) {
2081 
2082                         var tmp = tpc, radial = tpa, tangential = tpb;
2083 
2084                         // radial acceleration
2085                         if (selParticle.pos.x || selParticle.pos.y) {
2086                             cc.pIn(radial, selParticle.pos);
2087                             cc.pNormalizeIn(radial);
2088                         } else {
2089                             cc.pZeroIn(radial);
2090                         }
2091 
2092                         cc.pIn(tangential, radial);
2093                         cc.pMultIn(radial, selParticle.modeA.radialAccel);
2094 
2095                         // tangential acceleration
2096                         var newy = tangential.x;
2097                         tangential.x = -tangential.y;
2098                         tangential.y = newy;
2099 
2100                         cc.pMultIn(tangential, selParticle.modeA.tangentialAccel);
2101 
2102                         cc.pIn(tmp, radial);
2103                         cc.pAddIn(tmp, tangential);
2104                         cc.pAddIn(tmp, this.modeA.gravity);
2105                         cc.pMultIn(tmp, dt);
2106                         cc.pAddIn(selParticle.modeA.dir, tmp);
2107 
2108 
2109                         cc.pIn(tmp, selParticle.modeA.dir);
2110                         cc.pMultIn(tmp, dt);
2111                         cc.pAddIn(selParticle.pos, tmp);
2112 
2113                     } else {
2114                         // Mode B: radius movement
2115                         var selModeB = selParticle.modeB;
2116                         // Update the angle and radius of the particle.
2117                         selModeB.angle += selModeB.degreesPerSecond * dt;
2118                         selModeB.radius += selModeB.deltaRadius * dt;
2119 
2120                         selParticle.pos.x = -Math.cos(selModeB.angle) * selModeB.radius;
2121                         selParticle.pos.y = -Math.sin(selModeB.angle) * selModeB.radius;
2122                     }
2123 
2124                     // color
2125                     if (!this._dontTint || cc._renderType === cc._RENDER_TYPE_WEBGL) {
2126                         selParticle.color.r += selParticle.deltaColor.r * dt;
2127                         selParticle.color.g += selParticle.deltaColor.g * dt;
2128                         selParticle.color.b += selParticle.deltaColor.b * dt;
2129                         selParticle.color.a += selParticle.deltaColor.a * dt;
2130                         selParticle.isChangeColor = true;
2131                     }
2132 
2133                     // size
2134                     selParticle.size += (selParticle.deltaSize * dt);
2135                     selParticle.size = Math.max(0, selParticle.size);
2136 
2137                     // angle
2138                     selParticle.rotation += (selParticle.deltaRotation * dt);
2139 
2140                     //
2141                     // update values in quad
2142                     //
2143                     var newPos = tpa;
2144                     if (this.positionType == cc.ParticleSystem.TYPE_FREE || this.positionType == cc.ParticleSystem.TYPE_RELATIVE) {
2145 
2146                         var diff = tpb;
2147                         cc.pIn(diff, currentPosition);
2148                         cc.pSubIn(diff, selParticle.startPos);
2149 
2150                         cc.pIn(newPos, selParticle.pos);
2151                         cc.pSubIn(newPos, diff);
2152 
2153                     } else {
2154                         cc.pIn(newPos, selParticle.pos);
2155                     }
2156 
2157                     // translate newPos to correct position, since matrix transform isn't performed in batchnode
2158                     // don't update the particle with the new position information, it will interfere with the radius and tangential calculations
2159                     if (this._batchNode) {
2160                         newPos.x += this._position.x;
2161                         newPos.y += this._position.y;
2162                     }
2163 
2164                     if (cc._renderType == cc._RENDER_TYPE_WEBGL) {
2165                         // IMPORTANT: newPos may not be used as a reference here! (as it is just the temporary tpa point)
2166                         // the implementation of updateQuadWithParticle must use
2167                         // the x and y values directly
2168                         this.updateQuadWithParticle(selParticle, newPos);
2169                     } else {
2170                         cc.pIn(selParticle.drawPos, newPos);
2171                     }
2172                     //updateParticleImp(self, updateParticleSel, p, newPos);
2173 
2174                     // update particle counter
2175                     ++this._particleIdx;
2176                 } else {
2177                     // life < 0
2178                     var currentIndex = selParticle.atlasIndex;
2179                     if(this._particleIdx !== this.particleCount -1){
2180                          var deadParticle = locParticles[this._particleIdx];
2181                         locParticles[this._particleIdx] = locParticles[this.particleCount -1];
2182                         locParticles[this.particleCount -1] = deadParticle;
2183                     }
2184                     if (this._batchNode) {
2185                         //disable the switched particle
2186                         this._batchNode.disableParticle(this.atlasIndex + currentIndex);
2187 
2188                         //switch indexes
2189                         locParticles[this.particleCount - 1].atlasIndex = currentIndex;
2190                     }
2191 
2192                     --this.particleCount;
2193                     if (this.particleCount == 0 && this.autoRemoveOnFinish) {
2194                         this.unscheduleUpdate();
2195                         this._parent.removeChild(this, true);
2196                         return;
2197                     }
2198                 }
2199             }
2200             this._transformSystemDirty = false;
2201         }
2202 
2203         if (!this._batchNode)
2204             this.postStep();
2205     },
2206 
2207     /**
2208      * update emitter's status (dt = 0)
2209      */
2210     updateWithNoTime:function () {
2211         this.update(0);
2212     },
2213 
2214     //
2215     // return the string found by key in dict.
2216     // @param {string} key
2217     // @param {object} dict
2218     // @return {String} "" if not found; return the string if found.
2219     // @private
2220     //
2221     _valueForKey:function (key, dict) {
2222         if (dict) {
2223             var pString = dict[key];
2224             return pString != null ? pString : "";
2225         }
2226         return "";
2227     },
2228 
2229     _updateBlendFunc:function () {
2230         if(this._batchNode){
2231             cc.log("Can't change blending functions when the particle is being batched");
2232             return;
2233         }
2234 
2235         var locTexture = this._texture;
2236         if (locTexture && locTexture instanceof cc.Texture2D) {
2237             this._opacityModifyRGB = false;
2238             var locBlendFunc = this._blendFunc;
2239             if (locBlendFunc.src == cc.BLEND_SRC && locBlendFunc.dst == cc.BLEND_DST) {
2240                 if (locTexture.hasPremultipliedAlpha()) {
2241                     this._opacityModifyRGB = true;
2242                 } else {
2243                     locBlendFunc.src = cc.SRC_ALPHA;
2244                     locBlendFunc.dst = cc.ONE_MINUS_SRC_ALPHA;
2245                 }
2246             }
2247         }
2248     },
2249 
2250     /**
2251      * to copy object with deep copy.
2252      * returns a clone of action.
2253      *
2254      * @return {cc.ParticleSystem}
2255      */
2256     clone:function () {
2257         var retParticle = new cc.ParticleSystem();
2258 
2259         // self, not super
2260         if (retParticle.initWithTotalParticles(this.getTotalParticles())) {
2261             // angle
2262             retParticle.setAngle(this.getAngle());
2263             retParticle.setAngleVar(this.getAngleVar());
2264 
2265             // duration
2266             retParticle.setDuration(this.getDuration());
2267 
2268             // blend function
2269             var blend = this.getBlendFunc();
2270             retParticle.setBlendFunc(blend.src,blend.dst);
2271 
2272             // color
2273             retParticle.setStartColor(this.getStartColor());
2274 
2275             retParticle.setStartColorVar(this.getStartColorVar());
2276 
2277             retParticle.setEndColor(this.getEndColor());
2278 
2279             retParticle.setEndColorVar(this.getEndColorVar());
2280 
2281             // this size
2282             retParticle.setStartSize(this.getStartSize());
2283             retParticle.setStartSizeVar(this.getStartSizeVar());
2284             retParticle.setEndSize(this.getEndSize());
2285             retParticle.setEndSizeVar(this.getEndSizeVar());
2286 
2287             // position
2288             retParticle.setPosition(cc.p(this.x, this.y));
2289             retParticle.setPosVar(cc.p(this.getPosVar().x,this.getPosVar().y));
2290 
2291             // Spinning
2292             retParticle.setStartSpin(this.getStartSpin()||0);
2293             retParticle.setStartSpinVar(this.getStartSpinVar()||0);
2294             retParticle.setEndSpin(this.getEndSpin()||0);
2295             retParticle.setEndSpinVar(this.getEndSpinVar()||0);
2296 
2297             retParticle.setEmitterMode(this.getEmitterMode());
2298 
2299             // Mode A: Gravity + tangential accel + radial accel
2300             if (this.getEmitterMode() == cc.ParticleSystem.MODE_GRAVITY) {
2301                 // gravity
2302                 var gra = this.getGravity();
2303                 retParticle.setGravity(cc.p(gra.x,gra.y));
2304 
2305                 // speed
2306                 retParticle.setSpeed(this.getSpeed());
2307                 retParticle.setSpeedVar(this.getSpeedVar());
2308 
2309                 // radial acceleration
2310                 retParticle.setRadialAccel(this.getRadialAccel());
2311                 retParticle.setRadialAccelVar(this.getRadialAccelVar());
2312 
2313                 // tangential acceleration
2314                 retParticle.setTangentialAccel(this.getTangentialAccel());
2315                 retParticle.setTangentialAccelVar(this.getTangentialAccelVar());
2316 
2317             } else if (this.getEmitterMode() == cc.ParticleSystem.MODE_RADIUS) {
2318                 // or Mode B: radius movement
2319                 retParticle.setStartRadius(this.getStartRadius());
2320                 retParticle.setStartRadiusVar(this.getStartRadiusVar());
2321                 retParticle.setEndRadius(this.getEndRadius());
2322                 retParticle.setEndRadiusVar(this.getEndRadiusVar());
2323 
2324                 retParticle.setRotatePerSecond(this.getRotatePerSecond());
2325                 retParticle.setRotatePerSecondVar(this.getRotatePerSecondVar());
2326             }
2327 
2328             // life span
2329             retParticle.setLife(this.getLife());
2330             retParticle.setLifeVar(this.getLifeVar());
2331 
2332             // emission Rate
2333             retParticle.setEmissionRate(this.getEmissionRate());
2334 
2335             //don't get the internal texture if a batchNode is used
2336             if (!this.getBatchNode()) {
2337                 // Set a compatible default for the alpha transfer
2338                 retParticle.setOpacityModifyRGB(this.isOpacityModifyRGB());
2339                 // texture
2340                 var texture = this.getTexture();
2341                 if(texture){
2342                     var size = texture.getContentSize();
2343                     retParticle.setTextureWithRect(texture, cc.rect(0, 0, size.width, size.height));
2344                 }
2345             }
2346         }
2347         return retParticle;
2348     },
2349 
2350     /**
2351      * <p> Sets a new CCSpriteFrame as particle.</br>
2352      * WARNING: this method is experimental. Use setTextureWithRect instead.
2353      * </p>
2354      * @param {cc.SpriteFrame} spriteFrame
2355      */
2356     setDisplayFrame:function (spriteFrame) {
2357         var locOffset = spriteFrame.getOffsetInPixels();
2358         if(locOffset.x != 0 || locOffset.y != 0)
2359             cc.log("cc.ParticleSystem.setDisplayFrame(): QuadParticle only supports SpriteFrames with no offsets");
2360 
2361         // update texture before updating texture rect
2362         if (cc._renderType === cc._RENDER_TYPE_WEBGL)
2363             if (!this._texture || spriteFrame.getTexture()._webTextureObj != this._texture._webTextureObj)
2364                 this.setTexture(spriteFrame.getTexture());
2365     },
2366 
2367     /**
2368      *  Sets a new texture with a rect. The rect is in Points.
2369      * @param {cc.Texture2D} texture
2370      * @param {cc.Rect} rect
2371      */
2372     setTextureWithRect:function (texture, rect) {
2373         var locTexture = this._texture;
2374         if (cc._renderType === cc._RENDER_TYPE_WEBGL) {
2375             // Only update the texture if is different from the current one
2376             if ((!locTexture || texture._webTextureObj != locTexture._webTextureObj) && (locTexture != texture)) {
2377                 this._texture = texture;
2378                 this._updateBlendFunc();
2379             }
2380         } else {
2381             if ((!locTexture || texture != locTexture) && (locTexture != texture)) {
2382                 this._texture = texture;
2383                 this._updateBlendFunc();
2384             }
2385         }
2386 
2387         this._pointRect = rect;
2388         this.initTexCoordsWithRect(rect);
2389     },
2390 
2391     /**
2392      * draw particle
2393      * @param {CanvasRenderingContext2D} ctx CanvasContext
2394      * @override
2395      */
2396     draw:function (ctx) {
2397         if(!this._textureLoaded || this._batchNode)     // draw should not be called when added to a particleBatchNode
2398             return;
2399 
2400         if (cc._renderType === cc._RENDER_TYPE_CANVAS)
2401             this._drawForCanvas(ctx);
2402         else
2403             this._drawForWebGL(ctx);
2404 
2405         cc.g_NumberOfDraws++;
2406     },
2407 
2408     _drawForCanvas:function (ctx) {
2409         var context = ctx || cc._renderContext;
2410         context.save();
2411         if (this.isBlendAdditive())
2412             context.globalCompositeOperation = 'lighter';
2413         else
2414             context.globalCompositeOperation = 'source-over';
2415 
2416         var element = this._texture.getHtmlElementObj();
2417         for (var i = 0; i < this.particleCount; i++) {
2418             var particle = this._particles[i];
2419             var lpx = (0 | (particle.size * 0.5));
2420 
2421             if (this.drawMode == cc.ParticleSystem.TEXTURE_MODE) {
2422                 // Delay drawing until the texture is fully loaded by the browser
2423                 if (!element.width || !element.height)
2424                     continue;
2425 
2426                 context.save();
2427                 context.globalAlpha = particle.color.a / 255;
2428                 context.translate((0 | particle.drawPos.x), -(0 | particle.drawPos.y));
2429 
2430                 var size = Math.floor(particle.size / 4) * 4;
2431                 var w = this._pointRect.width;
2432                 var h = this._pointRect.height;
2433 
2434                 context.scale(
2435                     Math.max((1 / w) * size, 0.000001),
2436                     Math.max((1 / h) * size, 0.000001)
2437                 );
2438 
2439                 if (particle.rotation)
2440                     context.rotate(cc.degreesToRadians(particle.rotation));
2441                 context.translate(-(0 | (w / 2)), -(0 | (h / 2)));
2442                 var drawElement = particle.isChangeColor ? this._changeTextureColor(element, particle.color, this._pointRect) : element;
2443                 if(drawElement)
2444                     context.drawImage(drawElement, 0, 0);
2445                 context.restore();
2446             } else {
2447                 context.save();
2448                 context.globalAlpha = particle.color.a / 255;
2449 
2450                 context.translate(0 | particle.drawPos.x, -(0 | particle.drawPos.y));
2451 
2452                 if (this.shapeType == cc.ParticleSystem.STAR_SHAPE) {
2453                     if (particle.rotation)
2454                         context.rotate(cc.degreesToRadians(particle.rotation));
2455                     cc._drawingUtil.drawStar(context, lpx, particle.color);
2456                 } else
2457                     cc._drawingUtil.drawColorBall(context, lpx, particle.color);
2458                 context.restore();
2459             }
2460         }
2461         context.restore();
2462     },
2463 
2464     _changeTextureColor: function(element, color, rect){
2465         if (!element.tintCache) {
2466             element.tintCache = document.createElement('canvas');
2467             element.tintCache.width = element.width;
2468             element.tintCache.height = element.height;
2469         }
2470         return cc.generateTintImageWithMultiply(element, color, rect, element.tintCache);
2471     },
2472 
2473     _drawForWebGL:function (ctx) {
2474         if(!this._texture)
2475             return;
2476 
2477         var gl = ctx || cc._renderContext;
2478 
2479         this._shaderProgram.use();
2480         this._shaderProgram.setUniformForModelViewAndProjectionMatrixWithMat4();
2481 
2482         cc.glBindTexture2D(this._texture);
2483         cc.glBlendFuncForParticle(this._blendFunc.src, this._blendFunc.dst);
2484 
2485         //cc.assert(this._particleIdx == this.particleCount, "Abnormal error in particle quad");
2486 
2487         //
2488         // Using VBO without VAO
2489         //
2490         cc.glEnableVertexAttribs(cc.VERTEX_ATTRIB_FLAG_POS_COLOR_TEX);
2491 
2492         gl.bindBuffer(gl.ARRAY_BUFFER, this._buffersVBO[0]);
2493         gl.vertexAttribPointer(cc.VERTEX_ATTRIB_POSITION, 3, gl.FLOAT, false, 24, 0);               // vertices
2494         gl.vertexAttribPointer(cc.VERTEX_ATTRIB_COLOR, 4, gl.UNSIGNED_BYTE, true, 24, 12);          // colors
2495         gl.vertexAttribPointer(cc.VERTEX_ATTRIB_TEX_COORDS, 2, gl.FLOAT, false, 24, 16);            // tex coords
2496 
2497         gl.bindBuffer(gl.ELEMENT_ARRAY_BUFFER, this._buffersVBO[1]);
2498         gl.drawElements(gl.TRIANGLES, this._particleIdx * 6, gl.UNSIGNED_SHORT, 0);
2499     },
2500 
2501     /**
2502      * listen the event that coming to foreground on Android
2503      * @param {cc.Class} obj
2504      */
2505     listenBackToForeground:function (obj) {
2506         if (cc.TEXTURE_ATLAS_USE_VAO)
2507             this._setupVBOandVAO();
2508         else
2509             this._setupVBO();
2510     },
2511 
2512     _setupVBOandVAO:function () {
2513         //Not support on WebGL
2514         /*if (cc._renderType == cc._RENDER_TYPE_CANVAS) {
2515          return;
2516          }*/
2517 
2518         //NOT SUPPORTED
2519         /*glGenVertexArrays(1, this._VAOname);
2520          glBindVertexArray(this._VAOname);
2521 
2522          var kQuadSize = sizeof(m_pQuads[0].bl);
2523 
2524          glGenBuffers(2, this._buffersVBO[0]);
2525 
2526          glBindBuffer(GL_ARRAY_BUFFER, this._buffersVBO[0]);
2527          glBufferData(GL_ARRAY_BUFFER, sizeof(this._quads[0]) * this._totalParticles, this._quads, GL_DYNAMIC_DRAW);
2528 
2529          // vertices
2530          glEnableVertexAttribArray(kCCVertexAttrib_Position);
2531          glVertexAttribPointer(kCCVertexAttrib_Position, 2, GL_FLOAT, GL_FALSE, kQuadSize, offsetof(ccV3F_C4B_T2F, vertices));
2532 
2533          // colors
2534          glEnableVertexAttribArray(kCCVertexAttrib_Color);
2535          glVertexAttribPointer(kCCVertexAttrib_Color, 4, GL_UNSIGNED_BYTE, GL_TRUE, kQuadSize, offsetof(ccV3F_C4B_T2F, colors));
2536 
2537          // tex coords
2538          glEnableVertexAttribArray(kCCVertexAttrib_TexCoords);
2539          glVertexAttribPointer(kCCVertexAttrib_TexCoords, 2, GL_FLOAT, GL_FALSE, kQuadSize, offsetof(ccV3F_C4B_T2F, texCoords));
2540 
2541          glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, this._buffersVBO[1]);
2542          glBufferData(GL_ELEMENT_ARRAY_BUFFER, sizeof(m_pIndices[0]) * m_uTotalParticles * 6, m_pIndices, GL_STATIC_DRAW);
2543 
2544          glBindVertexArray(0);
2545          glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, 0);
2546          glBindBuffer(GL_ARRAY_BUFFER, 0);
2547 
2548          CHECK_GL_ERROR_DEBUG();*/
2549     },
2550 
2551     _setupVBO:function () {
2552         if (cc._renderType == cc._RENDER_TYPE_CANVAS)
2553             return;
2554 
2555         var gl = cc._renderContext;
2556 
2557         //gl.deleteBuffer(this._buffersVBO[0]);
2558         this._buffersVBO[0] = gl.createBuffer();
2559         gl.bindBuffer(gl.ARRAY_BUFFER, this._buffersVBO[0]);
2560         gl.bufferData(gl.ARRAY_BUFFER, this._quadsArrayBuffer, gl.DYNAMIC_DRAW);
2561 
2562         this._buffersVBO[1] = gl.createBuffer();
2563         gl.bindBuffer(gl.ELEMENT_ARRAY_BUFFER, this._buffersVBO[1]);
2564         gl.bufferData(gl.ELEMENT_ARRAY_BUFFER, this._indices, gl.STATIC_DRAW);
2565 
2566         //cc.checkGLErrorDebug();
2567     },
2568 
2569     _allocMemory:function () {
2570         if (cc._renderType === cc._RENDER_TYPE_CANVAS)
2571             return true;
2572 
2573         //cc.assert((!this._quads && !this._indices), "Memory already allocated");
2574         if(this._batchNode){
2575             cc.log("cc.ParticleSystem._allocMemory(): Memory should not be allocated when not using batchNode");
2576             return false;
2577         }
2578 
2579         var quadSize = cc.V3F_C4B_T2F_Quad.BYTES_PER_ELEMENT;
2580         var totalParticles = this._totalParticles;
2581         var locQuads = this._quads;
2582         locQuads.length = 0;
2583         this._indices = new Uint16Array(totalParticles * 6);
2584         var locQuadsArrayBuffer = new ArrayBuffer(quadSize * totalParticles);
2585 
2586         for (var i = 0; i < totalParticles; i++)
2587             locQuads[i] = new cc.V3F_C4B_T2F_Quad(null, null, null, null, locQuadsArrayBuffer, i * quadSize);
2588         if (!locQuads || !this._indices) {
2589             cc.log("cocos2d: Particle system: not enough memory");
2590             return false;
2591         }
2592         this._quadsArrayBuffer = locQuadsArrayBuffer;
2593         return true;
2594     }
2595 });
2596 
2597 var _p = cc.ParticleSystem.prototype;
2598 
2599 if(cc._renderType === cc._RENDER_TYPE_CANVAS && !cc.sys._supportCanvasNewBlendModes)
2600     _p._changeTextureColor = function (element, color, rect) {
2601         var cacheTextureForColor = cc.textureCache.getTextureColors(element);
2602         if (cacheTextureForColor) {
2603             // Create another cache for the tinted version
2604             // This speeds up things by a fair bit
2605             if (!cacheTextureForColor.tintCache) {
2606                 cacheTextureForColor.tintCache = document.createElement('canvas');
2607                 cacheTextureForColor.tintCache.width = element.width;
2608                 cacheTextureForColor.tintCache.height = element.height;
2609             }
2610             cc.generateTintImage(element, cacheTextureForColor, color, rect, cacheTextureForColor.tintCache);
2611             return cacheTextureForColor.tintCache;
2612         }
2613         return null
2614     };
2615 
2616 // Extended properties
2617 /** @expose */
2618 _p.opacityModifyRGB;
2619 cc.defineGetterSetter(_p, "opacityModifyRGB", _p.isOpacityModifyRGB, _p.setOpacityModifyRGB);
2620 /** @expose */
2621 _p.batchNode;
2622 cc.defineGetterSetter(_p, "batchNode", _p.getBatchNode, _p.setBatchNode);
2623 /** @expose */
2624 _p.active;
2625 cc.defineGetterSetter(_p, "active", _p.isActive);
2626 /** @expose */
2627 _p.sourcePos;
2628 cc.defineGetterSetter(_p, "sourcePos", _p.getSourcePosition, _p.setSourcePosition);
2629 /** @expose */
2630 _p.posVar;
2631 cc.defineGetterSetter(_p, "posVar", _p.getPosVar, _p.setPosVar);
2632 /** @expose */
2633 _p.gravity;
2634 cc.defineGetterSetter(_p, "gravity", _p.getGravity, _p.setGravity);
2635 /** @expose */
2636 _p.speed;
2637 cc.defineGetterSetter(_p, "speed", _p.getSpeed, _p.setSpeed);
2638 /** @expose */
2639 _p.speedVar;
2640 cc.defineGetterSetter(_p, "speedVar", _p.getSpeedVar, _p.setSpeedVar);
2641 /** @expose */
2642 _p.tangentialAccel;
2643 cc.defineGetterSetter(_p, "tangentialAccel", _p.getTangentialAccel, _p.setTangentialAccel);
2644 /** @expose */
2645 _p.tangentialAccelVar;
2646 cc.defineGetterSetter(_p, "tangentialAccelVar", _p.getTangentialAccelVar, _p.setTangentialAccelVar);
2647 /** @expose */
2648 _p.radialAccel;
2649 cc.defineGetterSetter(_p, "radialAccel", _p.getRadialAccel, _p.setRadialAccel);
2650 /** @expose */
2651 _p.radialAccelVar;
2652 cc.defineGetterSetter(_p, "radialAccelVar", _p.getRadialAccelVar, _p.setRadialAccelVar);
2653 /** @expose */
2654 _p.rotationIsDir;
2655 cc.defineGetterSetter(_p, "rotationIsDir", _p.getRotationIsDir, _p.setRotationIsDir);
2656 /** @expose */
2657 _p.startRadius;
2658 cc.defineGetterSetter(_p, "startRadius", _p.getStartRadius, _p.setStartRadius);
2659 /** @expose */
2660 _p.startRadiusVar;
2661 cc.defineGetterSetter(_p, "startRadiusVar", _p.getStartRadiusVar, _p.setStartRadiusVar);
2662 /** @expose */
2663 _p.endRadius;
2664 cc.defineGetterSetter(_p, "endRadius", _p.getEndRadius, _p.setEndRadius);
2665 /** @expose */
2666 _p.endRadiusVar;
2667 cc.defineGetterSetter(_p, "endRadiusVar", _p.getEndRadiusVar, _p.setEndRadiusVar);
2668 /** @expose */
2669 _p.rotatePerS;
2670 cc.defineGetterSetter(_p, "rotatePerS", _p.getRotatePerSecond, _p.setRotatePerSecond);
2671 /** @expose */
2672 _p.rotatePerSVar;
2673 cc.defineGetterSetter(_p, "rotatePerSVar", _p.getRotatePerSecondVar, _p.setRotatePerSecondVar);
2674 /** @expose */
2675 _p.startColor;
2676 cc.defineGetterSetter(_p, "startColor", _p.getStartColor, _p.setStartColor);
2677 /** @expose */
2678 _p.startColorVar;
2679 cc.defineGetterSetter(_p, "startColorVar", _p.getStartColorVar, _p.setStartColorVar);
2680 /** @expose */
2681 _p.endColor;
2682 cc.defineGetterSetter(_p, "endColor", _p.getEndColor, _p.setEndColor);
2683 /** @expose */
2684 _p.endColorVar;
2685 cc.defineGetterSetter(_p, "endColorVar", _p.getEndColorVar, _p.setEndColorVar);
2686 /** @expose */
2687 _p.totalParticles;
2688 cc.defineGetterSetter(_p, "totalParticles", _p.getTotalParticles, _p.setTotalParticles);
2689 /** @expose */
2690 _p.texture;
2691 cc.defineGetterSetter(_p, "texture", _p.getTexture, _p.setTexture);
2692 
2693 
2694 /**
2695  * <p> return the string found by key in dict. <br/>
2696  *    This plist files can be create manually or with Particle Designer:<br/>
2697  *    http://particledesigner.71squared.com/<br/>
2698  * </p>
2699  * @deprecated since v3.0 please use new cc.ParticleSysytem(plistFile) instead.
2700  * @param {String|Number} plistFile
2701  * @return {cc.ParticleSystem}
2702  */
2703 cc.ParticleSystem.create = function (plistFile) {
2704     return new cc.ParticleSystem(plistFile);
2705 };
2706 
2707 /**
2708  * <p> return the string found by key in dict. <br/>
2709  *    This plist files can be create manually or with Particle Designer:<br/>
2710  *    http://particledesigner.71squared.com/<br/>
2711  * </p>
2712  * @deprecated since v3.0 please use new cc.ParticleSysytem(plistFile) instead.
2713  * @function
2714  * @param {String|Number} plistFile
2715  * @return {cc.ParticleSystem}
2716  */
2717 cc.ParticleSystem.createWithTotalParticles = cc.ParticleSystem.create;
2718 
2719 // Different modes
2720 /**
2721  * Mode A:Gravity + Tangential Accel + Radial Accel
2722  * @Class
2723  * @Construct
2724  * @param {cc.Point} [gravity=] Gravity value.
2725  * @param {Number} [speed=0] speed of each particle.
2726  * @param {Number} [speedVar=0] speed variance of each particle.
2727  * @param {Number} [tangentialAccel=0] tangential acceleration of each particle.
2728  * @param {Number} [tangentialAccelVar=0] tangential acceleration variance of each particle.
2729  * @param {Number} [radialAccel=0] radial acceleration of each particle.
2730  * @param {Number} [radialAccelVar=0] radial acceleration variance of each particle.
2731  * @param {boolean} [rotationIsDir=false]
2732  */
2733 cc.ParticleSystem.ModeA = function (gravity, speed, speedVar, tangentialAccel, tangentialAccelVar, radialAccel, radialAccelVar, rotationIsDir) {
2734     /** Gravity value. Only available in 'Gravity' mode. */
2735     this.gravity = gravity ? gravity : cc.p(0,0);
2736     /** speed of each particle. Only available in 'Gravity' mode.  */
2737     this.speed = speed || 0;
2738     /** speed variance of each particle. Only available in 'Gravity' mode. */
2739     this.speedVar = speedVar || 0;
2740     /** tangential acceleration of each particle. Only available in 'Gravity' mode. */
2741     this.tangentialAccel = tangentialAccel || 0;
2742     /** tangential acceleration variance of each particle. Only available in 'Gravity' mode. */
2743     this.tangentialAccelVar = tangentialAccelVar || 0;
2744     /** radial acceleration of each particle. Only available in 'Gravity' mode. */
2745     this.radialAccel = radialAccel || 0;
2746     /** radial acceleration variance of each particle. Only available in 'Gravity' mode. */
2747     this.radialAccelVar = radialAccelVar || 0;
2748     /** set the rotation of each particle to its direction Only available in 'Gravity' mode. */
2749     this.rotationIsDir = rotationIsDir || false;
2750 };
2751 
2752 /**
2753  * Mode B: circular movement (gravity, radial accel and tangential accel don't are not used in this mode)
2754  * @Class
2755  * @Construct
2756  * @param {Number} startRadius The starting radius of the particles.
2757  * @param {Number} startRadiusVar The starting radius variance of the particles.
2758  * @param {Number} endRadius The ending radius of the particles.
2759  * @param {Number} endRadiusVar The ending radius variance of the particles.
2760  * @param {Number} rotatePerSecond Number of degress to rotate a particle around the source pos per second.
2761  * @param {Number} rotatePerSecondVar Variance in degrees for rotatePerSecond.
2762  */
2763 cc.ParticleSystem.ModeB = function (startRadius, startRadiusVar, endRadius, endRadiusVar, rotatePerSecond, rotatePerSecondVar) {
2764     /** The starting radius of the particles. Only available in 'Radius' mode. */
2765     this.startRadius = startRadius || 0;
2766     /** The starting radius variance of the particles. Only available in 'Radius' mode. */
2767     this.startRadiusVar = startRadiusVar || 0;
2768     /** The ending radius of the particles. Only available in 'Radius' mode. */
2769     this.endRadius = endRadius || 0;
2770     /** The ending radius variance of the particles. Only available in 'Radius' mode. */
2771     this.endRadiusVar = endRadiusVar || 0;
2772     /** Number of degress to rotate a particle around the source pos per second. Only available in 'Radius' mode. */
2773     this.rotatePerSecond = rotatePerSecond || 0;
2774     /** Variance in degrees for rotatePerSecond. Only available in 'Radius' mode. */
2775     this.rotatePerSecondVar = rotatePerSecondVar || 0;
2776 };
2777 
2778 /**
2779  * Shape Mode of Particle Draw
2780  * @constant
2781  * @type Number
2782  */
2783 cc.ParticleSystem.SHAPE_MODE = 0;
2784 
2785 /**
2786  * Texture Mode of Particle Draw
2787  * @constant
2788  * @type Number
2789  */
2790 cc.ParticleSystem.TEXTURE_MODE = 1;
2791 
2792 /**
2793  * Star Shape for ShapeMode of Particle
2794  * @constant
2795  * @type Number
2796  */
2797 cc.ParticleSystem.STAR_SHAPE = 0;
2798 
2799 /**
2800  * Ball Shape for ShapeMode of Particle
2801  * @constant
2802  * @type Number
2803  */
2804 cc.ParticleSystem.BALL_SHAPE = 1;
2805 
2806 /**
2807  * The Particle emitter lives forever
2808  * @constant
2809  * @type Number
2810  */
2811 cc.ParticleSystem.DURATION_INFINITY = -1;
2812 
2813 /**
2814  * The starting size of the particle is equal to the ending size
2815  * @constant
2816  * @type Number
2817  */
2818 cc.ParticleSystem.START_SIZE_EQUAL_TO_END_SIZE = -1;
2819 
2820 /**
2821  * The starting radius of the particle is equal to the ending radius
2822  * @constant
2823  * @type Number
2824  */
2825 cc.ParticleSystem.START_RADIUS_EQUAL_TO_END_RADIUS = -1;
2826 
2827 /**
2828  * Gravity mode (A mode)
2829  * @constant
2830  * @type Number
2831  */
2832 cc.ParticleSystem.MODE_GRAVITY = 0;
2833 
2834 /**
2835  * Radius mode (B mode)
2836  * @constant
2837  * @type Number
2838  */
2839 cc.ParticleSystem.MODE_RADIUS = 1;
2840 
2841 /**
2842  * Living particles are attached to the world and are unaffected by emitter repositioning.
2843  * @constant
2844  * @type Number
2845  */
2846 cc.ParticleSystem.TYPE_FREE = 0;
2847 
2848 /**
2849  * Living particles are attached to the world but will follow the emitter repositioning.<br/>
2850  * Use case: Attach an emitter to an sprite, and you want that the emitter follows the sprite.
2851  * @constant
2852  * @type Number
2853  */
2854 cc.ParticleSystem.TYPE_RELATIVE = 1;
2855 
2856 /**
2857  * Living particles are attached to the emitter and are translated along with it.
2858  * @constant
2859  * @type Number
2860  */
2861 cc.ParticleSystem.TYPE_GROUPED = 2;
2862