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 = this._particles;
1273             locParticles.length = 0;
1274             var locQuads = this._quads;
1275             locQuads.length = 0;
1276             for (var j = 0; j < tp; j++) {
1277                 locParticles[j] = new cc.Particle();
1278                 locQuads[j] = new cc.V3F_C4B_T2F_Quad(null, null, null, null, locQuadsArrayBuffer, j * quadSize);
1279             }
1280             this._allocatedParticles = tp;
1281             this._totalParticles = tp;
1282 
1283             // Init particles
1284             if (this._batchNode) {
1285                 for (var i = 0; i < tp; i++)
1286                     locParticles[i].atlasIndex = i;
1287             }
1288 
1289             this._quadsArrayBuffer = locQuadsArrayBuffer;
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, locParticles = this._particles;
1701         locParticles.length = 0;
1702         for(i = 0; i< numberOfParticles; i++){
1703             locParticles[i] = new cc.Particle();
1704         }
1705 
1706         if (!locParticles) {
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                 locParticles[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         //for batchNode
1735         this._transformSystemDirty = false;
1736 
1737         // udpate after action in run!
1738         this.scheduleUpdateWithPriority(1);
1739 
1740         if(cc.renderContextType === cc.WEBGL){
1741             // allocating data space
1742             if (!this._allocMemory())
1743                 return false;
1744 
1745             this.initIndices();
1746             //if (cc.TEXTURE_ATLAS_USE_VAO)
1747             //    this._setupVBOandVAO();
1748             //else
1749             this._setupVBO();
1750 
1751             this.setShaderProgram(cc.ShaderCache.getInstance().programForKey(cc.SHADER_POSITION_TEXTURECOLOR));
1752         }
1753 
1754         return true;
1755     },
1756 
1757     destroyParticleSystem:function () {
1758         this.unscheduleUpdate();
1759     },
1760 
1761     /**
1762      * Add a particle to the emitter
1763      * @return {Boolean}
1764      */
1765     addParticle: function () {
1766         if (this.isFull())
1767             return false;
1768         var particle, particles = this._particles;
1769         if (cc.renderContextType === cc.CANVAS) {
1770             if (this._particleCount < particles.length) {
1771                 particle = particles[this._particleCount];
1772             } else {
1773                 particle = new cc.Particle();
1774                 particles.push(particle);
1775             }
1776         } else {
1777             particle = particles[this._particleCount];
1778         }
1779         this.initParticle(particle);
1780         ++this._particleCount;
1781         return true;
1782     },
1783 
1784     /**
1785      * Initializes a particle
1786      * @param {cc.Particle} particle
1787      */
1788     initParticle:function (particle) {
1789         var locRandomMinus11 = cc.RANDOM_MINUS1_1;
1790         // timeToLive
1791         // no negative life. prevent division by 0
1792         particle.timeToLive = this._life + this._lifeVar * locRandomMinus11();
1793         particle.timeToLive = Math.max(0, particle.timeToLive);
1794 
1795         // position
1796         particle.pos.x = this._sourcePosition.x + this._posVar.x * locRandomMinus11();
1797         particle.pos.y = this._sourcePosition.y + this._posVar.y * locRandomMinus11();
1798 
1799         // Color
1800         var start, end;
1801         var locStartColor = this._startColor, locStartColorVar = this._startColorVar;
1802         var locEndColor = this._endColor, locEndColorVar = this._endColorVar;
1803         if (cc.renderContextType === cc.CANVAS) {
1804             start = new cc.Color4F(
1805                 cc.clampf(locStartColor.r + locStartColorVar.r * locRandomMinus11(), 0, 1),
1806                 cc.clampf(locStartColor.g + locStartColorVar.g * locRandomMinus11(), 0, 1),
1807                 cc.clampf(locStartColor.b + locStartColorVar.b * locRandomMinus11(), 0, 1),
1808                 cc.clampf(locStartColor.a + locStartColorVar.a * locRandomMinus11(), 0, 1)
1809             );
1810             end = new cc.Color4F(
1811                 cc.clampf(locEndColor.r + locEndColorVar.r * locRandomMinus11(), 0, 1),
1812                 cc.clampf(locEndColor.g + locEndColorVar.g * locRandomMinus11(), 0, 1),
1813                 cc.clampf(locEndColor.b + locEndColorVar.b * locRandomMinus11(), 0, 1),
1814                 cc.clampf(locEndColor.a + locEndColorVar.a * locRandomMinus11(), 0, 1)
1815             );
1816         } else {
1817             start = {
1818                 r: cc.clampf(locStartColor.r + locStartColorVar.r * locRandomMinus11(), 0, 1),
1819                 g: cc.clampf(locStartColor.g + locStartColorVar.g * locRandomMinus11(), 0, 1),
1820                 b: cc.clampf(locStartColor.b + locStartColorVar.b * locRandomMinus11(), 0, 1),
1821                 a: cc.clampf(locStartColor.a + locStartColorVar.a * locRandomMinus11(), 0, 1)
1822             };
1823             end = {
1824                 r: cc.clampf(locEndColor.r + locEndColorVar.r * locRandomMinus11(), 0, 1),
1825                 g: cc.clampf(locEndColor.g + locEndColorVar.g * locRandomMinus11(), 0, 1),
1826                 b: cc.clampf(locEndColor.b + locEndColorVar.b * locRandomMinus11(), 0, 1),
1827                 a: cc.clampf(locEndColor.a + locEndColorVar.a * locRandomMinus11(), 0, 1)
1828             };
1829         }
1830 
1831         particle.color = start;
1832         var locParticleDeltaColor = particle.deltaColor, locParticleTimeToLive = particle.timeToLive;
1833         locParticleDeltaColor.r = (end.r - start.r) / locParticleTimeToLive;
1834         locParticleDeltaColor.g = (end.g - start.g) / locParticleTimeToLive;
1835         locParticleDeltaColor.b = (end.b - start.b) / locParticleTimeToLive;
1836         locParticleDeltaColor.a = (end.a - start.a) / locParticleTimeToLive;
1837 
1838         // size
1839         var startS = this._startSize + this._startSizeVar * locRandomMinus11();
1840         startS = Math.max(0, startS); // No negative value
1841 
1842         particle.size = startS;
1843         if (this._endSize === cc.PARTICLE_START_SIZE_EQUAL_TO_END_SIZE) {
1844             particle.deltaSize = 0;
1845         } else {
1846             var endS = this._endSize + this._endSizeVar * locRandomMinus11();
1847             endS = Math.max(0, endS); // No negative values
1848             particle.deltaSize = (endS - startS) / locParticleTimeToLive;
1849         }
1850 
1851         // rotation
1852         var startA = this._startSpin + this._startSpinVar * locRandomMinus11();
1853         var endA = this._endSpin + this._endSpinVar * locRandomMinus11();
1854         particle.rotation = startA;
1855         particle.deltaRotation = (endA - startA) / locParticleTimeToLive;
1856 
1857         // position
1858         if (this._positionType == cc.PARTICLE_TYPE_FREE)
1859             particle.startPos = this.convertToWorldSpace(this._pointZeroForParticle);
1860         else if (this._positionType == cc.PARTICLE_TYPE_RELATIVE){
1861             particle.startPos.x = this._position.x;
1862             particle.startPos.y = this._position.y;
1863         }
1864 
1865         // direction
1866         var a = cc.DEGREES_TO_RADIANS(this._angle + this._angleVar * locRandomMinus11());
1867 
1868         // Mode Gravity: A
1869         if (this._emitterMode === cc.PARTICLE_MODE_GRAVITY) {
1870             var locModeA = this.modeA, locParticleModeA = particle.modeA;
1871             var s = locModeA.speed + locModeA.speedVar * locRandomMinus11();
1872 
1873             // direction
1874             locParticleModeA.dir.x = Math.cos(a);
1875             locParticleModeA.dir.y = Math.sin(a);
1876             cc.pMultIn(locParticleModeA.dir, s);
1877 
1878             // radial accel
1879             locParticleModeA.radialAccel = locModeA.radialAccel + locModeA.radialAccelVar * locRandomMinus11();
1880 
1881             // tangential accel
1882             locParticleModeA.tangentialAccel = locModeA.tangentialAccel + locModeA.tangentialAccelVar * locRandomMinus11();
1883 
1884             // rotation is dir
1885             if(locModeA.rotationIsDir)
1886                 particle.rotation = -cc.RADIANS_TO_DEGREES(cc.pToAngle(locParticleModeA.dir));
1887         } else {
1888             // Mode Radius: B
1889             var locModeB = this.modeB, locParitlceModeB = particle.modeB;
1890 
1891             // Set the default diameter of the particle from the source position
1892             var startRadius = locModeB.startRadius + locModeB.startRadiusVar * locRandomMinus11();
1893             var endRadius = locModeB.endRadius + locModeB.endRadiusVar * locRandomMinus11();
1894 
1895             locParitlceModeB.radius = startRadius;
1896             locParitlceModeB.deltaRadius = (locModeB.endRadius === cc.PARTICLE_START_RADIUS_EQUAL_TO_END_RADIUS) ? 0 : (endRadius - startRadius) / locParticleTimeToLive;
1897 
1898             locParitlceModeB.angle = a;
1899             locParitlceModeB.degreesPerSecond = cc.DEGREES_TO_RADIANS(locModeB.rotatePerSecond + locModeB.rotatePerSecondVar * locRandomMinus11());
1900         }
1901     },
1902 
1903     /**
1904      * stop emitting particles. Running particles will continue to run until they die
1905      */
1906     stopSystem:function () {
1907         this._isActive = false;
1908         this._elapsed = this._duration;
1909         this._emitCounter = 0;
1910     },
1911 
1912     /**
1913      * Kill all living particles.
1914      */
1915     resetSystem:function () {
1916         this._isActive = true;
1917         this._elapsed = 0;
1918         var locParticles = this._particles;
1919         for (this._particleIdx = 0; this._particleIdx < this._particleCount; ++this._particleIdx)
1920             locParticles[this._particleIdx].timeToLive = 0 ;
1921     },
1922 
1923     /**
1924      * whether or not the system is full
1925      * @return {Boolean}
1926      */
1927     isFull:function () {
1928         return (this._particleCount >= this._totalParticles);
1929     },
1930 
1931     /**
1932      * should be overridden by subclasses
1933      * @param {cc.Particle} particle
1934      * @param {cc.Point} newPosition
1935      */
1936     updateQuadWithParticle:function (particle, newPosition) {
1937         var quad = null;
1938         if (this._batchNode) {
1939             var batchQuads = this._batchNode.getTextureAtlas().getQuads();
1940             quad = batchQuads[this._atlasIndex + particle.atlasIndex];
1941             this._batchNode.getTextureAtlas()._dirty = true;
1942         } else
1943             quad = this._quads[this._particleIdx];
1944 
1945         var r, g, b, a;
1946         if(this._opacityModifyRGB){
1947             r = 0 | (particle.color.r * particle.color.a * 255);
1948             g = 0 | (particle.color.g * particle.color.a * 255);
1949             b = 0 | (particle.color.b * particle.color.a * 255);
1950             a = 0 | (particle.color.a * 255);
1951         }else{
1952             r = 0 | (particle.color.r * 255);
1953             g = 0 | (particle.color.g * 255);
1954             b = 0 | (particle.color.b * 255);
1955             a = 0 | (particle.color.a * 255);
1956         }
1957 
1958         var locColors = quad.bl.colors;
1959         locColors.r = r;
1960         locColors.g = g;
1961         locColors.b = b;
1962         locColors.a = a;
1963 
1964         locColors = quad.br.colors;
1965         locColors.r = r;
1966         locColors.g = g;
1967         locColors.b = b;
1968         locColors.a = a;
1969 
1970         locColors = quad.tl.colors;
1971         locColors.r = r;
1972         locColors.g = g;
1973         locColors.b = b;
1974         locColors.a = a;
1975 
1976         locColors = quad.tr.colors;
1977         locColors.r = r;
1978         locColors.g = g;
1979         locColors.b = b;
1980         locColors.a = a;
1981 
1982         // vertices
1983         var size_2 = particle.size / 2;
1984         if (particle.rotation) {
1985             var x1 = -size_2;
1986             var y1 = -size_2;
1987 
1988             var x2 = size_2;
1989             var y2 = size_2;
1990             var x = newPosition.x;
1991             var y = newPosition.y;
1992 
1993             var rad = -cc.DEGREES_TO_RADIANS(particle.rotation);
1994             var cr = Math.cos(rad);
1995             var sr = Math.sin(rad);
1996             var ax = x1 * cr - y1 * sr + x;
1997             var ay = x1 * sr + y1 * cr + y;
1998             var bx = x2 * cr - y1 * sr + x;
1999             var by = x2 * sr + y1 * cr + y;
2000             var cx = x2 * cr - y2 * sr + x;
2001             var cy = x2 * sr + y2 * cr + y;
2002             var dx = x1 * cr - y2 * sr + x;
2003             var dy = x1 * sr + y2 * cr + y;
2004 
2005             // bottom-left
2006             quad.bl.vertices.x = ax;
2007             quad.bl.vertices.y = ay;
2008 
2009             // bottom-right vertex:
2010             quad.br.vertices.x = bx;
2011             quad.br.vertices.y = by;
2012 
2013             // top-left vertex:
2014             quad.tl.vertices.x = dx;
2015             quad.tl.vertices.y = dy;
2016 
2017             // top-right vertex:
2018             quad.tr.vertices.x = cx;
2019             quad.tr.vertices.y = cy;
2020         } else {
2021             // bottom-left vertex:
2022             quad.bl.vertices.x = newPosition.x - size_2;
2023             quad.bl.vertices.y = newPosition.y - size_2;
2024 
2025             // bottom-right vertex:
2026             quad.br.vertices.x = newPosition.x + size_2;
2027             quad.br.vertices.y = newPosition.y - size_2;
2028 
2029             // top-left vertex:
2030             quad.tl.vertices.x = newPosition.x - size_2;
2031             quad.tl.vertices.y = newPosition.y + size_2;
2032 
2033             // top-right vertex:
2034             quad.tr.vertices.x = newPosition.x + size_2;
2035             quad.tr.vertices.y = newPosition.y + size_2;
2036         }
2037     },
2038 
2039     /**
2040      * should be overridden by subclasses
2041      */
2042     postStep:function () {
2043         if (cc.renderContextType === cc.WEBGL) {
2044             var gl = cc.renderContext;
2045 
2046             gl.bindBuffer(gl.ARRAY_BUFFER, this._buffersVBO[0]);
2047             gl.bufferData(gl.ARRAY_BUFFER, this._quadsArrayBuffer, gl.DYNAMIC_DRAW);
2048 
2049             // Option 2: Data
2050             //	glBufferData(GL_ARRAY_BUFFER, sizeof(quads_[0]) * particleCount, quads_, GL_DYNAMIC_DRAW);
2051 
2052             // Option 3: Orphaning + glMapBuffer
2053             // glBufferData(GL_ARRAY_BUFFER, sizeof(m_pQuads[0])*m_uTotalParticles, NULL, GL_STREAM_DRAW);
2054             // void *buf = glMapBuffer(GL_ARRAY_BUFFER, GL_WRITE_ONLY);
2055             // memcpy(buf, m_pQuads, sizeof(m_pQuads[0])*m_uTotalParticles);
2056             // glUnmapBuffer(GL_ARRAY_BUFFER);
2057 
2058             //cc.CHECK_GL_ERROR_DEBUG();
2059         }
2060     },
2061 
2062     /**
2063      * update emitter's status
2064      * @override
2065      * @param {Number} dt delta time
2066      */
2067     update:function (dt) {
2068         if (this._isActive && this._emissionRate) {
2069             var rate = 1.0 / this._emissionRate;
2070             //issue #1201, prevent bursts of particles, due to too high emitCounter
2071             if (this._particleCount < this._totalParticles)
2072                 this._emitCounter += dt;
2073 
2074             while ((this._particleCount < this._totalParticles) && (this._emitCounter > rate)) {
2075                 this.addParticle();
2076                 this._emitCounter -= rate;
2077             }
2078 
2079             this._elapsed += dt;
2080             if (this._duration != -1 && this._duration < this._elapsed)
2081                 this.stopSystem();
2082         }
2083         this._particleIdx = 0;
2084 
2085         var currentPosition = cc.Particle.TemporaryPoints[0];
2086         if (this._positionType == cc.PARTICLE_TYPE_FREE) {
2087             cc.pIn(currentPosition, this.convertToWorldSpace(this._pointZeroForParticle));
2088         } else if (this._positionType == cc.PARTICLE_TYPE_RELATIVE) {
2089             currentPosition.x = this._position.x;
2090             currentPosition.y = this._position.y;
2091         }
2092 
2093         if (this._visible) {
2094 
2095             // Used to reduce memory allocation / creation within the loop
2096             var tpa = cc.Particle.TemporaryPoints[1],
2097                 tpb = cc.Particle.TemporaryPoints[2],
2098                 tpc = cc.Particle.TemporaryPoints[3];
2099 
2100             var locParticles = this._particles;
2101             while (this._particleIdx < this._particleCount) {
2102 
2103                 // Reset the working particles
2104                 cc.pZeroIn(tpa);
2105                 cc.pZeroIn(tpb);
2106                 cc.pZeroIn(tpc);
2107 
2108                 var selParticle = locParticles[this._particleIdx];
2109 
2110                 // life
2111                 selParticle.timeToLive -= dt;
2112 
2113                 if (selParticle.timeToLive > 0) {
2114                     // Mode A: gravity, direction, tangential accel & radial accel
2115                     if (this._emitterMode == cc.PARTICLE_MODE_GRAVITY) {
2116 
2117                         var tmp = tpc, radial = tpa, tangential = tpb;
2118 
2119                         // radial acceleration
2120                         if (selParticle.pos.x || selParticle.pos.y) {
2121                             cc.pIn(radial, selParticle.pos);
2122                             cc.pNormalizeIn(radial);
2123                         } else {
2124                             cc.pZeroIn(radial);
2125                         }
2126 
2127                         cc.pIn(tangential, radial);
2128                         cc.pMultIn(radial, selParticle.modeA.radialAccel);
2129 
2130                         // tangential acceleration
2131                         var newy = tangential.x;
2132                         tangential.x = -tangential.y;
2133                         tangential.y = newy;
2134 
2135                         cc.pMultIn(tangential, selParticle.modeA.tangentialAccel);
2136 
2137                         cc.pIn(tmp, radial);
2138                         cc.pAddIn(tmp, tangential);
2139                         cc.pAddIn(tmp, this.modeA.gravity);
2140                         cc.pMultIn(tmp, dt);
2141                         cc.pAddIn(selParticle.modeA.dir, tmp);
2142 
2143 
2144                         cc.pIn(tmp, selParticle.modeA.dir);
2145                         cc.pMultIn(tmp, dt);
2146                         cc.pAddIn(selParticle.pos, tmp);
2147 
2148                     } else {
2149                         // Mode B: radius movement
2150                         var selModeB = selParticle.modeB;
2151                         // Update the angle and radius of the particle.
2152                         selModeB.angle += selModeB.degreesPerSecond * dt;
2153                         selModeB.radius += selModeB.deltaRadius * dt;
2154 
2155                         selParticle.pos.x = -Math.cos(selModeB.angle) * selModeB.radius;
2156                         selParticle.pos.y = -Math.sin(selModeB.angle) * selModeB.radius;
2157                     }
2158 
2159                     // color
2160                     if (!this._dontTint) {
2161                         selParticle.color.r += (selParticle.deltaColor.r * dt);
2162                         selParticle.color.g += (selParticle.deltaColor.g * dt);
2163                         selParticle.color.b += (selParticle.deltaColor.b * dt);
2164                         selParticle.color.a += (selParticle.deltaColor.a * dt);
2165                         selParticle.isChangeColor = true;
2166                     }
2167 
2168                     // size
2169                     selParticle.size += (selParticle.deltaSize * dt);
2170                     selParticle.size = Math.max(0, selParticle.size);
2171 
2172                     // angle
2173                     selParticle.rotation += (selParticle.deltaRotation * dt);
2174 
2175                     //
2176                     // update values in quad
2177                     //
2178                     var newPos = tpa;
2179                     if (this._positionType == cc.PARTICLE_TYPE_FREE || this._positionType == cc.PARTICLE_TYPE_RELATIVE) {
2180 
2181                         var diff = tpb;
2182                         cc.pIn(diff, currentPosition);
2183                         cc.pSubIn(diff, selParticle.startPos);
2184 
2185                         cc.pIn(newPos, selParticle.pos);
2186                         cc.pSubIn(newPos, diff);
2187 
2188                     } else {
2189                         cc.pIn(newPos, selParticle.pos);
2190                     }
2191 
2192                     // translate newPos to correct position, since matrix transform isn't performed in batchnode
2193                     // don't update the particle with the new position information, it will interfere with the radius and tangential calculations
2194                     if (this._batchNode) {
2195                         newPos.x += this._position._x;
2196                         newPos.y += this._position._y;
2197                     }
2198 
2199                     if (cc.renderContextType == cc.WEBGL) {
2200                         // IMPORTANT: newPos may not be used as a reference here! (as it is just the temporary tpa point)
2201                         // the implementation of updateQuadWithParticle must use
2202                         // the x and y values directly
2203                         this.updateQuadWithParticle(selParticle, newPos);
2204                     } else {
2205                         cc.pIn(selParticle.drawPos, newPos);
2206                     }
2207                     //updateParticleImp(self, updateParticleSel, p, newPos);
2208 
2209                     // update particle counter
2210                     ++this._particleIdx;
2211                 } else {
2212                     // life < 0
2213                     var currentIndex = selParticle.atlasIndex;
2214                     if(this._particleIdx !== this._particleCount -1){
2215                          var deadParticle = locParticles[this._particleIdx];
2216                         locParticles[this._particleIdx] = locParticles[this._particleCount -1];
2217                         locParticles[this._particleCount -1] = deadParticle;
2218                     }
2219                     if (this._batchNode) {
2220                         //disable the switched particle
2221                         this._batchNode.disableParticle(this._atlasIndex + currentIndex);
2222 
2223                         //switch indexes
2224                         locParticles[this._particleCount - 1].atlasIndex = currentIndex;
2225                     }
2226 
2227                     --this._particleCount;
2228                     if (this._particleCount == 0 && this._isAutoRemoveOnFinish) {
2229                         this.unscheduleUpdate();
2230                         this._parent.removeChild(this, true);
2231                         return;
2232                     }
2233                 }
2234             }
2235             this._transformSystemDirty = false;
2236         }
2237 
2238         if (!this._batchNode)
2239             this.postStep();
2240     },
2241 
2242     updateWithNoTime:function () {
2243         this.update(0);
2244     },
2245 
2246     /**
2247      * return the string found by key in dict.
2248      * @param {string} key
2249      * @param {object} dict
2250      * @return {String} "" if not found; return the string if found.
2251      * @private
2252      */
2253     _valueForKey:function (key, dict) {
2254         if (dict) {
2255             var pString = dict[key];
2256             return pString != null ? pString : "";
2257         }
2258         return "";
2259     },
2260 
2261     _updateBlendFunc:function () {
2262         if(this._batchNode){
2263             cc.log("Can't change blending functions when the particle is being batched");
2264             return;
2265         }
2266 
2267         var locTexture = this._texture;
2268         if (locTexture && locTexture instanceof cc.Texture2D) {
2269             this._opacityModifyRGB = false;
2270             var locBlendFunc = this._blendFunc;
2271             if (locBlendFunc.src == cc.BLEND_SRC && locBlendFunc.dst == cc.BLEND_DST) {
2272                 if (locTexture.hasPremultipliedAlpha()) {
2273                     this._opacityModifyRGB = true;
2274                 } else {
2275                     locBlendFunc.src = gl.SRC_ALPHA;
2276                     locBlendFunc.dst = gl.ONE_MINUS_SRC_ALPHA;
2277                 }
2278             }
2279         }
2280     },
2281 
2282     clone:function () {
2283         var retParticle = new cc.ParticleSystem();
2284 
2285         // self, not super
2286         if (retParticle.initWithTotalParticles(this._totalParticles)) {
2287             // angle
2288             retParticle._angle = this._angle;
2289             retParticle._angleVar = this._angleVar;
2290 
2291             // duration
2292             retParticle._duration = this._duration;
2293 
2294             // blend function
2295             retParticle._blendFunc.src = this._blendFunc.src;
2296             retParticle._blendFunc.dst = this._blendFunc.dst;
2297 
2298             // color
2299             var particleStartColor = retParticle._startColor, locStartColor = this._startColor;
2300             particleStartColor.r = locStartColor.r;
2301             particleStartColor.g = locStartColor.g;
2302             particleStartColor.b = locStartColor.b;
2303             particleStartColor.a = locStartColor.a;
2304 
2305             var particleStartColorVar =  retParticle._startColorVar, locStartColorVar = this._startColorVar;
2306             particleStartColorVar.r = locStartColorVar.r;
2307             particleStartColorVar.g = locStartColorVar.g;
2308             particleStartColorVar.b = locStartColorVar.b;
2309             particleStartColorVar.a = locStartColorVar.a;
2310 
2311             var particleEndColor = retParticle._endColor, locEndColor = this._endColor;
2312             particleEndColor.r = locEndColor.r;
2313             particleEndColor.g = locEndColor.g;
2314             particleEndColor.b = locEndColor.b;
2315             particleEndColor.a = locEndColor.a;
2316 
2317             var particleEndColorVar = retParticle._endColorVar, locEndColorVar = this._endColorVar;
2318             particleEndColorVar.r = locEndColorVar.r;
2319             particleEndColorVar.g = locEndColorVar.g;
2320             particleEndColorVar.b = locEndColorVar.b;
2321             particleEndColorVar.a = locEndColorVar.a;
2322 
2323             // particle size
2324             retParticle._startSize = this._startSize;
2325             retParticle._startSizeVar = this._startSizeVar;
2326             retParticle._endSize = this._endSize;
2327             retParticle._endSizeVar = this._endSizeVar;
2328 
2329             // position
2330             retParticle.setPosition(this._position._x, this._position._y);
2331             retParticle._posVar.x = this._posVar.x;
2332             retParticle._posVar.y = this._posVar.y;
2333 
2334             // Spinning
2335             retParticle._startSpin = this._startSpin;
2336             retParticle._startSpinVar = this._startSpinVar;
2337             retParticle._endSpin = this._endSpin;
2338             retParticle._endSpinVar = this._endSpinVar;
2339 
2340             retParticle._emitterMode = this._emitterMode;
2341 
2342             // Mode A: Gravity + tangential accel + radial accel
2343             if (this._emitterMode == cc.PARTICLE_MODE_GRAVITY) {
2344                 var particleModeA = retParticle.modeA, locModeA = this.modeA;
2345                 // gravity
2346                 particleModeA.gravity.x = locModeA.gravity.x;
2347                 particleModeA.gravity.y = locModeA.gravity.y;
2348 
2349                 // speed
2350                 particleModeA.speed = locModeA.speed;
2351                 particleModeA.speedVar = locModeA.speedVar;
2352 
2353                 // radial acceleration
2354                 particleModeA.radialAccel = locModeA.radialAccel;
2355 
2356                 particleModeA.radialAccelVar = locModeA.radialAccelVar;
2357 
2358                 // tangential acceleration
2359                 particleModeA.tangentialAccel = locModeA.tangentialAccel;
2360 
2361                 particleModeA.tangentialAccelVar = locModeA.tangentialAccelVar;
2362             } else if (this._emitterMode == cc.PARTICLE_MODE_RADIUS) {
2363                 var particleModeB = retParticle.modeB, locModeB = this.modeB;
2364                 // or Mode B: radius movement
2365                 particleModeB.startRadius = locModeB.startRadius;
2366                 particleModeB.startRadiusVar = locModeB.startRadiusVar;
2367                 particleModeB.endRadius = locModeB.endRadius;
2368                 particleModeB.endRadiusVar = locModeB.endRadiusVar;
2369                 particleModeB.rotatePerSecond = locModeB.rotatePerSecond;
2370                 particleModeB.rotatePerSecondVar = locModeB.rotatePerSecondVar;
2371             }
2372 
2373             // life span
2374             retParticle._life = this._life;
2375             retParticle._lifeVar = this._lifeVar;
2376 
2377             // emission Rate
2378             retParticle._emissionRate = this._emissionRate;
2379 
2380             //don't get the internal texture if a batchNode is used
2381             if (!this._batchNode) {
2382                 // Set a compatible default for the alpha transfer
2383                 retParticle._opacityModifyRGB = this._opacityModifyRGB;
2384 
2385                 // texture
2386                 retParticle._texture = this._texture;
2387             }
2388         }
2389         return retParticle;
2390     },
2391 
2392     /**
2393      * <p> Sets a new CCSpriteFrame as particle.</br>
2394      * WARNING: this method is experimental. Use setTextureWithRect instead.
2395      * </p>
2396      * @param {cc.SpriteFrame} spriteFrame
2397      */
2398     setDisplayFrame:function (spriteFrame) {
2399         var locOffset = spriteFrame.getOffsetInPixels();
2400         if(locOffset.x != 0 || locOffset.y != 0)
2401             cc.log("cc.ParticleSystem.setDisplayFrame(): QuadParticle only supports SpriteFrames with no offsets");
2402 
2403         // update texture before updating texture rect
2404         if (cc.renderContextType === cc.WEBGL)
2405             if (!this._texture || spriteFrame.getTexture()._webTextureObj != this._texture._webTextureObj)
2406                 this.setTexture(spriteFrame.getTexture());
2407     },
2408 
2409     /**
2410      *  Sets a new texture with a rect. The rect is in Points.
2411      * @param {cc.Texture2D} texture
2412      * @param {cc.Rect} rect
2413      */
2414     setTextureWithRect:function (texture, rect) {
2415         var locTexture = this._texture;
2416         if (cc.renderContextType === cc.WEBGL) {
2417             // Only update the texture if is different from the current one
2418             if ((!locTexture || texture._webTextureObj != locTexture._webTextureObj) && (locTexture != texture)) {
2419                 this._texture = texture;
2420                 this._updateBlendFunc();
2421             }
2422         } else {
2423             if ((!locTexture || texture != locTexture) && (locTexture != texture)) {
2424                 this._texture = texture;
2425                 this._updateBlendFunc();
2426             }
2427         }
2428 
2429         this._pointRect = rect;
2430         this.initTexCoordsWithRect(rect);
2431     },
2432 
2433     /**
2434      * draw particle
2435      * @param {CanvasRenderingContext2D} ctx CanvasContext
2436      * @override
2437      */
2438     draw:function (ctx) {
2439         if(!this._textureLoaded || this._batchNode)     // draw should not be called when added to a particleBatchNode
2440             return;
2441 
2442         if (cc.renderContextType === cc.CANVAS)
2443             this._drawForCanvas(ctx);
2444         else
2445             this._drawForWebGL(ctx);
2446 
2447         cc.g_NumberOfDraws++;
2448     },
2449 
2450     _drawForCanvas:function (ctx) {
2451         var context = ctx || cc.renderContext;
2452         context.save();
2453         if (this.isBlendAdditive())
2454             context.globalCompositeOperation = 'lighter';
2455         else
2456             context.globalCompositeOperation = 'source-over';
2457 
2458         for (var i = 0; i < this._particleCount; i++) {
2459             var particle = this._particles[i];
2460             var lpx = (0 | (particle.size * 0.5));
2461 
2462             if (this._drawMode == cc.PARTICLE_TEXTURE_MODE) {
2463 
2464                 var element = this._texture.getHtmlElementObj();
2465 
2466                 // Delay drawing until the texture is fully loaded by the browser
2467                 if (!element.width || !element.height)
2468                     continue;
2469 
2470                 context.save();
2471                 context.globalAlpha = particle.color.a;
2472                 context.translate((0 | particle.drawPos.x), -(0 | particle.drawPos.y));
2473 
2474                 var size = Math.floor(particle.size / 4) * 4;
2475                 var w = this._pointRect.width;
2476                 var h = this._pointRect.height;
2477 
2478                 context.scale(
2479                     Math.max((1 / w) * size, 0.000001),
2480                     Math.max((1 / h) * size, 0.000001)
2481                 );
2482 
2483 
2484                 if (particle.rotation)
2485                     context.rotate(cc.DEGREES_TO_RADIANS(particle.rotation));
2486 
2487                 context.translate(-(0 | (w / 2)), -(0 | (h / 2)));
2488                 if (particle.isChangeColor) {
2489 
2490                     var cacheTextureForColor = cc.TextureCache.getInstance().getTextureColors(element);
2491                     if (cacheTextureForColor) {
2492                         // Create another cache for the tinted version
2493                         // This speeds up things by a fair bit
2494                         if (!cacheTextureForColor.tintCache) {
2495                             cacheTextureForColor.tintCache = document.createElement('canvas');
2496                             cacheTextureForColor.tintCache.width = element.width;
2497                             cacheTextureForColor.tintCache.height = element.height;
2498                         }
2499                         cc.generateTintImage(element, cacheTextureForColor, particle.color, this._pointRect, cacheTextureForColor.tintCache);
2500                         element = cacheTextureForColor.tintCache;
2501                     }
2502                 }
2503 
2504                 context.drawImage(element, 0, 0);
2505                 context.restore();
2506 
2507             } else {
2508                 context.save();
2509                 context.globalAlpha = particle.color.a;
2510 
2511                 context.translate(0 | particle.drawPos.x, -(0 | particle.drawPos.y));
2512 
2513                 if (this._shapeType == cc.PARTICLE_STAR_SHAPE) {
2514                     if (particle.rotation)
2515                         context.rotate(cc.DEGREES_TO_RADIANS(particle.rotation));
2516                     cc.drawingUtil.drawStar(context, lpx, particle.color);
2517                 } else
2518                     cc.drawingUtil.drawColorBall(context, lpx, particle.color);
2519                 context.restore();
2520             }
2521         }
2522         context.restore();
2523     },
2524 
2525     _drawForWebGL:function (ctx) {
2526         if(!this._texture)
2527             return;
2528 
2529         var gl = ctx || cc.renderContext;
2530 
2531         this._shaderProgram.use();
2532         this._shaderProgram.setUniformForModelViewAndProjectionMatrixWithMat4();
2533 
2534         cc.glBindTexture2D(this._texture);
2535         cc.glBlendFuncForParticle(this._blendFunc.src, this._blendFunc.dst);
2536 
2537         //cc.Assert(this._particleIdx == this._particleCount, "Abnormal error in particle quad");
2538 
2539         //
2540         // Using VBO without VAO
2541         //
2542         cc.glEnableVertexAttribs(cc.VERTEX_ATTRIB_FLAG_POSCOLORTEX);
2543 
2544         gl.bindBuffer(gl.ARRAY_BUFFER, this._buffersVBO[0]);
2545         gl.vertexAttribPointer(cc.VERTEX_ATTRIB_POSITION, 3, gl.FLOAT, false, 24, 0);               // vertices
2546         gl.vertexAttribPointer(cc.VERTEX_ATTRIB_COLOR, 4, gl.UNSIGNED_BYTE, true, 24, 12);          // colors
2547         gl.vertexAttribPointer(cc.VERTEX_ATTRIB_TEX_COORDS, 2, gl.FLOAT, false, 24, 16);            // tex coords
2548 
2549         gl.bindBuffer(gl.ELEMENT_ARRAY_BUFFER, this._buffersVBO[1]);
2550         gl.drawElements(gl.TRIANGLES, this._particleIdx * 6, gl.UNSIGNED_SHORT, 0);
2551     },
2552 
2553     /**
2554      * listen the event that coming to foreground on Android
2555      * @param {cc.Class} obj
2556      */
2557     listenBackToForeground:function (obj) {
2558         if (cc.TEXTURE_ATLAS_USE_VAO)
2559             this._setupVBOandVAO();
2560         else
2561             this._setupVBO();
2562     },
2563 
2564     _setupVBOandVAO:function () {
2565         //Not support on WebGL
2566         /*if (cc.renderContextType == cc.CANVAS) {
2567          return;
2568          }*/
2569 
2570         //NOT SUPPORTED
2571         /*glGenVertexArrays(1, this._VAOname);
2572          glBindVertexArray(this._VAOname);
2573 
2574          var kQuadSize = sizeof(m_pQuads[0].bl);
2575 
2576          glGenBuffers(2, this._buffersVBO[0]);
2577 
2578          glBindBuffer(GL_ARRAY_BUFFER, this._buffersVBO[0]);
2579          glBufferData(GL_ARRAY_BUFFER, sizeof(this._quads[0]) * this._totalParticles, this._quads, GL_DYNAMIC_DRAW);
2580 
2581          // vertices
2582          glEnableVertexAttribArray(kCCVertexAttrib_Position);
2583          glVertexAttribPointer(kCCVertexAttrib_Position, 2, GL_FLOAT, GL_FALSE, kQuadSize, offsetof(ccV3F_C4B_T2F, vertices));
2584 
2585          // colors
2586          glEnableVertexAttribArray(kCCVertexAttrib_Color);
2587          glVertexAttribPointer(kCCVertexAttrib_Color, 4, GL_UNSIGNED_BYTE, GL_TRUE, kQuadSize, offsetof(ccV3F_C4B_T2F, colors));
2588 
2589          // tex coords
2590          glEnableVertexAttribArray(kCCVertexAttrib_TexCoords);
2591          glVertexAttribPointer(kCCVertexAttrib_TexCoords, 2, GL_FLOAT, GL_FALSE, kQuadSize, offsetof(ccV3F_C4B_T2F, texCoords));
2592 
2593          glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, this._buffersVBO[1]);
2594          glBufferData(GL_ELEMENT_ARRAY_BUFFER, sizeof(m_pIndices[0]) * m_uTotalParticles * 6, m_pIndices, GL_STATIC_DRAW);
2595 
2596          glBindVertexArray(0);
2597          glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, 0);
2598          glBindBuffer(GL_ARRAY_BUFFER, 0);
2599 
2600          CHECK_GL_ERROR_DEBUG();*/
2601     },
2602 
2603     _setupVBO:function () {
2604         if (cc.renderContextType == cc.CANVAS)
2605             return;
2606 
2607         var gl = cc.renderContext;
2608 
2609         //gl.deleteBuffer(this._buffersVBO[0]);
2610         this._buffersVBO[0] = gl.createBuffer();
2611         gl.bindBuffer(gl.ARRAY_BUFFER, this._buffersVBO[0]);
2612         gl.bufferData(gl.ARRAY_BUFFER, this._quadsArrayBuffer, gl.DYNAMIC_DRAW);
2613 
2614         this._buffersVBO[1] = gl.createBuffer();
2615         gl.bindBuffer(gl.ELEMENT_ARRAY_BUFFER, this._buffersVBO[1]);
2616         gl.bufferData(gl.ELEMENT_ARRAY_BUFFER, this._indices, gl.STATIC_DRAW);
2617 
2618         //cc.CHECK_GL_ERROR_DEBUG();
2619     },
2620 
2621     _allocMemory:function () {
2622         if (cc.renderContextType === cc.CANVAS)
2623             return true;
2624 
2625         //cc.Assert((!this._quads && !this._indices), "Memory already allocated");
2626         if(this._batchNode){
2627             cc.log("cc.ParticleSystem._allocMemory(): Memory should not be allocated when not using batchNode");
2628             return false;
2629         }
2630 
2631         var quadSize = cc.V3F_C4B_T2F_Quad.BYTES_PER_ELEMENT;
2632         var totalParticles = this._totalParticles;
2633         var locQuads = this._quads;
2634         locQuads.length = 0;
2635         this._indices = new Uint16Array(totalParticles * 6);
2636         var locQuadsArrayBuffer = new ArrayBuffer(quadSize * totalParticles);
2637 
2638         for (var i = 0; i < totalParticles; i++)
2639             locQuads[i] = new cc.V3F_C4B_T2F_Quad(null, null, null, null, locQuadsArrayBuffer, i * quadSize);
2640         if (!locQuads || !this._indices) {
2641             cc.log("cocos2d: Particle system: not enough memory");
2642             return false;
2643         }
2644         this._quadsArrayBuffer = locQuadsArrayBuffer;
2645         return true;
2646     }
2647 });
2648 
2649 /**
2650  * <p> return the string found by key in dict. <br/>
2651  *    This plist files can be create manually or with Particle Designer:<br/>
2652  *    http://particledesigner.71squared.com/<br/>
2653  * </p>
2654  * @param {String} plistFile
2655  * @return {cc.ParticleSystem}
2656  */
2657 cc.ParticleSystem.create = function (plistFile) {
2658     var ret = new cc.ParticleSystem();
2659     if (!plistFile || typeof(plistFile) === "number") {
2660         var ton = plistFile || 100;
2661         ret.setDrawMode(cc.PARTICLE_TEXTURE_MODE);
2662         ret.initWithTotalParticles(ton);
2663         return ret;
2664     }
2665 
2666     if (ret && ret.initWithFile(plistFile))
2667         return ret;
2668     return null;
2669 };
2670 
2671 /**
2672  * create a system with a fixed number of particles
2673  * @param {Number} number_of_particles
2674  * @return {cc.ParticleSystem}
2675  */
2676 cc.ParticleSystem.createWithTotalParticles = function (number_of_particles) {
2677     //emitter.initWithTotalParticles(number_of_particles);
2678     var particle = new cc.ParticleSystem();
2679     if (particle && particle.initWithTotalParticles(number_of_particles))
2680         return particle;
2681     return null;
2682 };
2683 
2684 // Different modes
2685 /**
2686  * Mode A:Gravity + Tangential Accel + Radial Accel
2687  * @Class
2688  * @Construct
2689  * @param {cc.Point} [gravity=] Gravity value.
2690  * @param {Number} [speed=0] speed of each particle.
2691  * @param {Number} [speedVar=0] speed variance of each particle.
2692  * @param {Number} [tangentialAccel=0] tangential acceleration of each particle.
2693  * @param {Number} [tangentialAccelVar=0] tangential acceleration variance of each particle.
2694  * @param {Number} [radialAccel=0] radial acceleration of each particle.
2695  * @param {Number} [radialAccelVar=0] radial acceleration variance of each particle.
2696  * @param {boolean} [rotationIsDir=false]
2697  */
2698 cc.ParticleSystem.ModeA = function (gravity, speed, speedVar, tangentialAccel, tangentialAccelVar, radialAccel, radialAccelVar, rotationIsDir) {
2699     /** Gravity value. Only available in 'Gravity' mode. */
2700     this.gravity = gravity ? gravity : cc.PointZero();
2701     /** speed of each particle. Only available in 'Gravity' mode.  */
2702     this.speed = speed || 0;
2703     /** speed variance of each particle. Only available in 'Gravity' mode. */
2704     this.speedVar = speedVar || 0;
2705     /** tangential acceleration of each particle. Only available in 'Gravity' mode. */
2706     this.tangentialAccel = tangentialAccel || 0;
2707     /** tangential acceleration variance of each particle. Only available in 'Gravity' mode. */
2708     this.tangentialAccelVar = tangentialAccelVar || 0;
2709     /** radial acceleration of each particle. Only available in 'Gravity' mode. */
2710     this.radialAccel = radialAccel || 0;
2711     /** radial acceleration variance of each particle. Only available in 'Gravity' mode. */
2712     this.radialAccelVar = radialAccelVar || 0;
2713     /** set the rotation of each particle to its direction Only available in 'Gravity' mode. */
2714     this.rotationIsDir = rotationIsDir || false;
2715 };
2716 
2717 /**
2718  * Mode B: circular movement (gravity, radial accel and tangential accel don't are not used in this mode)
2719  * @Class
2720  * @Construct
2721  * @param {Number} startRadius The starting radius of the particles.
2722  * @param {Number} startRadiusVar The starting radius variance of the particles.
2723  * @param {Number} endRadius The ending radius of the particles.
2724  * @param {Number} endRadiusVar The ending radius variance of the particles.
2725  * @param {Number} rotatePerSecond Number of degress to rotate a particle around the source pos per second.
2726  * @param {Number} rotatePerSecondVar Variance in degrees for rotatePerSecond.
2727  */
2728 cc.ParticleSystem.ModeB = function (startRadius, startRadiusVar, endRadius, endRadiusVar, rotatePerSecond, rotatePerSecondVar) {
2729     /** The starting radius of the particles. Only available in 'Radius' mode. */
2730     this.startRadius = startRadius || 0;
2731     /** The starting radius variance of the particles. Only available in 'Radius' mode. */
2732     this.startRadiusVar = startRadiusVar || 0;
2733     /** The ending radius of the particles. Only available in 'Radius' mode. */
2734     this.endRadius = endRadius || 0;
2735     /** The ending radius variance of the particles. Only available in 'Radius' mode. */
2736     this.endRadiusVar = endRadiusVar || 0;
2737     /** Number of degress to rotate a particle around the source pos per second. Only available in 'Radius' mode. */
2738     this.rotatePerSecond = rotatePerSecond || 0;
2739     /** Variance in degrees for rotatePerSecond. Only available in 'Radius' mode. */
2740     this.rotatePerSecondVar = rotatePerSecondVar || 0;
2741 };
2742