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