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 /**
 28  * @constant
 29  * @type Number
 30  */
 31 cc.DEFAULT_SPRITE_BATCH_CAPACITY = 29;
 32 
 33 /**
 34  * <p>
 35  *     In Canvas render mode ,cc.SpriteBatchNodeCanvas is like a normal node: if it contains children.             <br/>
 36  *     If its _useCache is set to true, it can cache the result that all children of SpriteBatchNode to a canvas <br/>
 37  *     (often known as "batch draw").<br/>
 38  *     <br/>
 39  *     A cc.SpriteBatchNode can reference one and only one texture (one image file, one texture atlas).<br/>
 40  *     Only the cc.Sprites that are contained in that texture can be added to the cc.SpriteBatchNode.<br/>
 41  *     All cc.Sprites added to a cc.SpriteBatchNode are drawn in one WebGL draw call. <br/>
 42  *     If the cc.Sprites are not added to a cc.SpriteBatchNode then an WebGL draw call will be needed for each one, which is less efficient. <br/>
 43  *     <br/>
 44  *     Limitations:<br/>
 45  *       - The only object that is accepted as child (or grandchild, grand-grandchild, etc...) is cc.Sprite or any subclass of cc.Sprite. <br/>
 46  *          eg: particles, labels and layer can't be added to a cc.SpriteBatchNode. <br/>
 47  *       - Either all its children are Aliased or Antialiased. It can't be a mix. <br/>
 48  *          This is because "alias" is a property of the texture, and all the sprites share the same texture. </br>
 49  * </p>
 50  * @class
 51  * @extends cc.Node
 52  * @example
 53  * //create a SpriteBatchNode
 54  * var parent2 = cc.SpriteBatchNode.create("res/animations/grossini.png", 50);
 55  */
 56 cc.SpriteBatchNode = cc.Node.extend(/** @lends cc.SpriteBatchNode# */{
 57     _textureAtlas:null,
 58     _blendFunc:null,
 59     // all descendants: chlidren, gran children, etc...
 60     _descendants:null,
 61 
 62     /**
 63      * <p>
 64      *    This is the opposite of "addQuadFromSprite.<br/>
 65      *    It add the sprite to the children and descendants array, but it doesn't update add it to the texture atlas<br/>
 66      * </p>
 67      * @param {cc.Sprite} child
 68      * @param {Number} z zOrder
 69      * @param {Number} aTag
 70      * @return {cc.SpriteBatchNode}
 71      */
 72     addSpriteWithoutQuad:function (child, z, aTag) {
 73         if(!child)
 74             throw "cc.SpriteBatchNode.addQuadFromSprite(): child should be non-null";
 75         if(!(child instanceof cc.Sprite)){
 76             cc.log("cc.SpriteBatchNode.addQuadFromSprite(): SpriteBatchNode only supports cc.Sprites as children");
 77             return null;
 78         }
 79 
 80         // quad index is Z
 81         child.setAtlasIndex(z);
 82 
 83         // XXX: optimize with a binary search
 84         var i = 0, locDescendants = this._descendants;
 85         if (locDescendants && locDescendants.length > 0) {
 86             for (var index = 0; index < locDescendants.length; index++) {
 87                 var obj = locDescendants[index];
 88                 if (obj && (obj.getAtlasIndex() >= z))
 89                     ++i;
 90             }
 91         }
 92         this._descendants = cc.ArrayAppendObjectToIndex(locDescendants, child, i);
 93 
 94         // IMPORTANT: Call super, and not self. Avoid adding it to the texture atlas array
 95         cc.Node.prototype.addChild.call(this, child, z, aTag);
 96 
 97         //#issue 1262 don't use lazy sorting, tiles are added as quads not as sprites, so sprites need to be added in order
 98         this.reorderBatch(false);
 99         return this;
100     },
101 
102     // property
103     /**
104      * Return TextureAtlas of cc.SpriteBatchNode
105      * @return {cc.TextureAtlas}
106      */
107     getTextureAtlas:function () {
108         return this._textureAtlas;
109     },
110 
111     /**
112      * TextureAtlas of cc.SpriteBatchNode setter
113      * @param {cc.TextureAtlas} textureAtlas
114      */
115     setTextureAtlas:function (textureAtlas) {
116         if (textureAtlas != this._textureAtlas) {
117             this._textureAtlas = textureAtlas;
118         }
119     },
120 
121     /**
122      * Return Descendants of cc.SpriteBatchNode
123      * @return {Array}
124      */
125     getDescendants:function () {
126         return  this._descendants;
127     },
128 
129     /**
130      * <p>
131      *    initializes a cc.SpriteBatchNode with a file image (.png, .jpeg, .pvr, etc) and a capacity of children.<br/>
132      *    The capacity will be increased in 33% in runtime if it run out of space.<br/>
133      *    The file will be loaded using the TextureMgr.
134      * </p>
135      * @param {String} fileImage
136      * @param {Number} capacity
137      * @return {Boolean}
138      */
139     initWithFile:function (fileImage, capacity) {
140         var texture2D = cc.TextureCache.getInstance().textureForKey(fileImage);
141         if (!texture2D)
142             texture2D = cc.TextureCache.getInstance().addImage(fileImage);
143         return this.initWithTexture(texture2D, capacity);
144     },
145 
146     _setNodeDirtyForCache:function () {
147         this._cacheDirty = true;
148     },
149 
150     /**
151      * <p>
152      *    initializes a cc.SpriteBatchNode with a file image (.png, .jpeg, .pvr, etc) and a capacity of children.<br/>
153      *    The capacity will be increased in 33% in runtime if it run out of space.<br/>
154      *    The file will be loaded using the TextureMgr.
155      * </p>
156      * @param {String} fileImage
157      * @param {Number} capacity
158      * @return {Boolean}
159      */
160     init:function (fileImage, capacity) {
161         var texture2D = cc.TextureCache.getInstance().textureForKey(fileImage);
162         if (!texture2D)
163             texture2D = cc.TextureCache.getInstance().addImage(fileImage);
164         return this.initWithTexture(texture2D, capacity);
165     },
166 
167     /**
168      * increase Atlas Capacity
169      */
170     increaseAtlasCapacity:function () {
171         // if we're going beyond the current TextureAtlas's capacity,
172         // all the previously initialized sprites will need to redo their texture coords
173         // this is likely computationally expensive
174         var locCapacity = this._textureAtlas.getCapacity();
175         var quantity = Math.floor((locCapacity + 1) * 4 / 3);
176 
177         cc.log("cocos2d: CCSpriteBatchNode: resizing TextureAtlas capacity from " + locCapacity + " to " + quantity + ".");
178 
179         if (!this._textureAtlas.resizeCapacity(quantity)) {
180             // serious problems
181             cc.log("cocos2d: WARNING: Not enough memory to resize the atlas");
182         }
183     },
184 
185     /**
186      * removes a child given a certain index. It will also cleanup the running actions depending on the cleanup parameter.
187      * @warning Removing a child from a cc.SpriteBatchNode is very slow
188      * @param {Number} index
189      * @param {Boolean} doCleanup
190      */
191     removeChildAtIndex:function (index, doCleanup) {
192         this.removeChild(this._children[index], doCleanup);
193     },
194 
195     /**
196      * rebuild index in order for child
197      * @param {cc.Sprite} pobParent
198      * @param {Number} index
199      * @return {Number}
200      */
201     rebuildIndexInOrder:function (pobParent, index) {
202         var children = pobParent.getChildren();
203         if (children && children.length > 0) {
204             for (var i = 0; i < children.length; i++) {
205                 var obj = children[i];
206                 if (obj && (obj.getZOrder() < 0)) {
207                     index = this.rebuildIndexInOrder(obj, index);
208                 }
209             }
210         }
211         // ignore self (batch node)
212         if (!pobParent == this) {
213             pobParent.setAtlasIndex(index);
214             index++;
215         }
216         if (children && children.length > 0) {
217             for (i = 0; i < children.length; i++) {
218                 obj = children[i];
219                 if (obj && (obj.getZOrder() >= 0)) {
220                     index = this.rebuildIndexInOrder(obj, index);
221                 }
222             }
223         }
224         return index;
225     },
226 
227     /**
228      * get highest atlas index in child
229      * @param {cc.Sprite} sprite
230      * @return {Number}
231      */
232     highestAtlasIndexInChild:function (sprite) {
233         var children = sprite.getChildren();
234 
235         if (!children || children.length == 0)
236             return sprite.getAtlasIndex();
237         else
238             return this.highestAtlasIndexInChild(children[children.length - 1]);
239     },
240 
241     /**
242      * get lowest atlas index in child
243      * @param {cc.Sprite} sprite
244      * @return {Number}
245      */
246     lowestAtlasIndexInChild:function (sprite) {
247         var children = sprite.getChildren();
248 
249         if (!children || children.length == 0)
250             return sprite.getAtlasIndex();
251         else
252             return this.lowestAtlasIndexInChild(children[children.length - 1]);
253     },
254 
255     /**
256      * get atlas index for child
257      * @param {cc.Sprite} sprite
258      * @param {Number} nZ
259      * @return {Number}
260      */
261     atlasIndexForChild:function (sprite, nZ) {
262         var brothers = sprite.getParent().getChildren();
263         var childIndex = cc.ArrayGetIndexOfObject(brothers, sprite);
264 
265         // ignore parent Z if parent is spriteSheet
266         var ignoreParent = sprite.getParent() == this;
267         var previous = null;
268         if (childIndex > 0 && childIndex < cc.UINT_MAX)
269             previous = brothers[childIndex - 1];
270 
271         // first child of the sprite sheet
272         if (ignoreParent) {
273             if (childIndex == 0)
274                 return 0;
275             return this.highestAtlasIndexInChild(previous) + 1;
276         }
277 
278         // parent is a cc.Sprite, so, it must be taken into account
279         // first child of an cc.Sprite ?
280         var selParent;
281         if (childIndex == 0) {
282             selParent = sprite.getParent();
283 
284             // less than parent and brothers
285             if (nZ < 0)
286                 return selParent.getAtlasIndex();
287             else
288                 return selParent.getAtlasIndex() + 1;
289         } else {
290             // previous & sprite belong to the same branch
291             if ((previous.getZOrder() < 0 && nZ < 0) || (previous.getZOrder() >= 0 && nZ >= 0))
292                 return this.highestAtlasIndexInChild(previous) + 1;
293 
294             // else (previous < 0 and sprite >= 0 )
295             selParent = sprite.getParent();
296             return selParent.getAtlasIndex() + 1;
297         }
298     },
299 
300     /**
301      * Sprites use this to start sortChildren, don't call this manually
302      * @param {Boolean} reorder
303      */
304     reorderBatch:function (reorder) {
305         this._reorderChildDirty = reorder;
306     },
307 
308     /**
309      * set the source blending function for the texture
310      * @param {Number | cc.BlendFunc} src
311      * @param {Number} dst
312      */
313     setBlendFunc:function (src, dst) {
314         if (arguments.length == 1)
315             this._blendFunc = src;
316         else
317             this._blendFunc = {src:src, dst:dst};
318     },
319 
320     /**
321      * returns the blending function used for the texture
322      * @return {cc.BlendFunc}
323      */
324     getBlendFunc:function () {
325         return this._blendFunc;
326     },
327 
328     /**
329      *  (override reorderChild of cc.Node)
330      * @override
331      * @param {cc.Sprite} child
332      * @param {Number} zOrder
333      */
334     reorderChild:function (child, zOrder) {
335         if(!child)
336             throw "cc.SpriteBatchNode.addChild():child should be non-null";
337         if(this._children.indexOf(child) === -1) {
338             cc.log("cc.SpriteBatchNode.addChild(): Child doesn't belong to Sprite");
339             return;
340         }
341 
342         if (zOrder === child.getZOrder())
343             return;
344 
345         //set the z-order and sort later
346         cc.Node.prototype.reorderChild.call(this, child, zOrder);
347         this.setNodeDirty();
348     },
349 
350     /**
351      * remove child from cc.SpriteBatchNode (override removeChild of cc.Node)
352      * @param {cc.Sprite} child
353      * @param cleanup
354      */
355     removeChild:function (child, cleanup) {
356         // explicit null handling
357         if (child == null)
358             return;
359         if(this._children.indexOf(child) === -1){
360             cc.log("cc.SpriteBatchNode.addChild(): sprite batch node should contain the child");
361             return;
362         }
363 
364         // cleanup before removing
365         this.removeSpriteFromAtlas(child);
366         cc.Node.prototype.removeChild.call(this, child, cleanup);
367     },
368 
369     _mvpMatrix:null,
370     _textureForCanvas:null,
371     _useCache:false,
372     _originalTexture:null,
373 
374     /**
375      * Constructor
376      * @param {String} fileImage
377      */
378     ctor: null,
379 
380     _ctorForCanvas: function (fileImage) {
381         cc.Node.prototype.ctor.call(this);
382         if (fileImage)
383             this.init(fileImage, cc.DEFAULT_SPRITE_BATCH_CAPACITY);
384     },
385 
386     _ctorForWebGL: function (fileImage) {
387         cc.Node.prototype.ctor.call(this);
388         this._mvpMatrix = new cc.kmMat4();
389         if (fileImage)
390             this.init(fileImage, cc.DEFAULT_SPRITE_BATCH_CAPACITY);
391     },
392 
393 
394     /**
395      * <p>
396      *   Updates a quad at a certain index into the texture atlas. The CCSprite won't be added into the children array.                 <br/>
397      *   This method should be called only when you are dealing with very big AtlasSrite and when most of the cc.Sprite won't be updated.<br/>
398      *   For example: a tile map (cc.TMXMap) or a label with lots of characters (BitmapFontAtlas)<br/>
399      * </p>
400      * @param {cc.Sprite} sprite
401      * @param {Number} index
402      */
403     updateQuadFromSprite:null,
404 
405     _updateQuadFromSpriteForCanvas:function (sprite, index) {
406         if(!sprite)
407             throw "cc.SpriteBatchNode.updateQuadFromSprite(): sprite should be non-null";
408         if(!(sprite instanceof cc.Sprite)){
409             cc.log("cc.SpriteBatchNode.updateQuadFromSprite(): cc.SpriteBatchNode only supports cc.Sprites as children");
410             return;
411         }
412 
413         //
414         // update the quad directly. Don't add the sprite to the scene graph
415         //
416         sprite.setBatchNode(this);
417         sprite.setAtlasIndex(index);
418 
419         sprite.setDirty(true);
420         // UpdateTransform updates the textureAtlas quad
421         sprite.updateTransform();
422     },
423 
424     _updateQuadFromSpriteForWebGL:function (sprite, index) {
425         if(!sprite)
426             throw "cc.SpriteBatchNode.updateQuadFromSprite(): sprite should be non-null";
427         if(!(sprite instanceof cc.Sprite)){
428             cc.log("cc.SpriteBatchNode.updateQuadFromSprite(): cc.SpriteBatchNode only supports cc.Sprites as children");
429             return;
430         }
431 
432         // make needed room
433         var locCapacity = this._textureAtlas.getCapacity();
434         while (index >= locCapacity || locCapacity == this._textureAtlas.getTotalQuads()) {
435             this.increaseAtlasCapacity();
436         }
437 
438         //
439         // update the quad directly. Don't add the sprite to the scene graph
440         //
441         sprite.setBatchNode(this);
442         sprite.setAtlasIndex(index);
443 
444         sprite.setDirty(true);
445         // UpdateTransform updates the textureAtlas quad
446         sprite.updateTransform();
447     },
448 
449     _swap:function (oldIndex, newIndex) {
450         var locDescendants = this._descendants;
451         var locTextureAtlas = this._textureAtlas;
452         var quads = locTextureAtlas.getQuads();
453         var tempItem = locDescendants[oldIndex];
454         var tempIteQuad = cc.V3F_C4B_T2F_QuadCopy(quads[oldIndex]);
455 
456         //update the index of other swapped item
457         locDescendants[newIndex].setAtlasIndex(oldIndex);
458         locDescendants[oldIndex] = locDescendants[newIndex];
459 
460         locTextureAtlas.updateQuad(quads[newIndex], oldIndex);
461         locDescendants[newIndex] = tempItem;
462         locTextureAtlas.updateQuad(tempIteQuad, newIndex);
463     },
464 
465     /**
466      * <p>
467      *    Inserts a quad at a certain index into the texture atlas. The cc.Sprite won't be added into the children array.                    <br/>
468      *    This method should be called only when you are dealing with very big AtlasSprite and when most of the cc.Sprite won't be updated.  <br/>
469      *    For example: a tile map (cc.TMXMap) or a label with lots of characters (cc.LabelBMFont)
470      * </p>
471      * @param {cc.Sprite} sprite
472      * @param {Number} index
473      */
474     insertQuadFromSprite:null,
475 
476     _insertQuadFromSpriteForCanvas:function (sprite, index) {
477         if(!sprite)
478             throw "cc.SpriteBatchNode.insertQuadFromSprite(): sprite should be non-null";
479         if(!(sprite instanceof cc.Sprite)){
480             cc.log("cc.SpriteBatchNode.insertQuadFromSprite(): cc.SpriteBatchNode only supports cc.Sprites as children");
481             return;
482         }
483 
484         //
485         // update the quad directly. Don't add the sprite to the scene graph
486         //
487         sprite.setBatchNode(this);
488         sprite.setAtlasIndex(index);
489 
490         // XXX: updateTransform will update the textureAtlas too, using updateQuad.
491         // XXX: so, it should be AFTER the insertQuad
492         sprite.setDirty(true);
493         sprite.updateTransform();
494         this._children = cc.ArrayAppendObjectToIndex(this._children, sprite, index);
495     },
496 
497     _insertQuadFromSpriteForWebGL:function (sprite, index) {
498         if(!sprite)
499             throw "cc.SpriteBatchNode.insertQuadFromSprite(): sprite should be non-null";
500         if(!(sprite instanceof cc.Sprite)){
501             cc.log("cc.SpriteBatchNode.insertQuadFromSprite(): cc.SpriteBatchNode only supports cc.Sprites as children");
502             return;
503         }
504 
505         // make needed room
506         var locTextureAtlas = this._textureAtlas;
507         while (index >= locTextureAtlas.getCapacity() || locTextureAtlas.getCapacity() === locTextureAtlas.getTotalQuads())
508             this.increaseAtlasCapacity();
509 
510         //
511         // update the quad directly. Don't add the sprite to the scene graph
512         //
513         sprite.setBatchNode(this);
514         sprite.setAtlasIndex(index);
515         locTextureAtlas.insertQuad(sprite.getQuad(), index);
516 
517         // XXX: updateTransform will update the textureAtlas too, using updateQuad.
518         // XXX: so, it should be AFTER the insertQuad
519         sprite.setDirty(true);
520         sprite.updateTransform();
521     },
522 
523     _updateAtlasIndex:function (sprite, curIndex) {
524         var count = 0;
525         var pArray = sprite.getChildren();
526         if (pArray)
527             count = pArray.length;
528 
529         var oldIndex = 0;
530         if (count === 0) {
531             oldIndex = sprite.getAtlasIndex();
532             sprite.setAtlasIndex(curIndex);
533             sprite.setOrderOfArrival(0);
534             if (oldIndex != curIndex)
535                 this._swap(oldIndex, curIndex);
536             curIndex++;
537         } else {
538             var needNewIndex = true;
539             if (pArray[0].getZOrder() >= 0) {
540                 //all children are in front of the parent
541                 oldIndex = sprite.getAtlasIndex();
542                 sprite.setAtlasIndex(curIndex);
543                 sprite.setOrderOfArrival(0);
544                 if (oldIndex != curIndex)
545                     this._swap(oldIndex, curIndex);
546                 curIndex++;
547                 needNewIndex = false;
548             }
549             for (var i = 0; i < pArray.length; i++) {
550                 var child = pArray[i];
551                 if (needNewIndex && child.getZOrder() >= 0) {
552                     oldIndex = sprite.getAtlasIndex();
553                     sprite.setAtlasIndex(curIndex);
554                     sprite.setOrderOfArrival(0);
555                     if (oldIndex != curIndex) {
556                         this._swap(oldIndex, curIndex);
557                     }
558                     curIndex++;
559                     needNewIndex = false;
560                 }
561                 curIndex = this._updateAtlasIndex(child, curIndex);
562             }
563 
564             if (needNewIndex) {
565                 //all children have a zOrder < 0)
566                 oldIndex = sprite.getAtlasIndex();
567                 sprite.setAtlasIndex(curIndex);
568                 sprite.setOrderOfArrival(0);
569                 if (oldIndex != curIndex) {
570                     this._swap(oldIndex, curIndex);
571                 }
572                 curIndex++;
573             }
574         }
575 
576         return curIndex;
577     },
578 
579     _updateBlendFunc:function () {
580         if (!this._textureAtlas.getTexture().hasPremultipliedAlpha()) {
581             this._blendFunc.src = gl.SRC_ALPHA;
582             this._blendFunc.dst = gl.ONE_MINUS_SRC_ALPHA;
583         }
584     },
585 
586     /**
587      * <p>
588      *    initializes a CCSpriteBatchNode with a texture2d and capacity of children.<br/>
589      *    The capacity will be increased in 33% in runtime if it run out of space.
590      * </p>
591      * @param {cc.Texture2D} tex
592      * @param {Number} [capacity]
593      * @return {Boolean}
594      */
595     initWithTexture:null,
596 
597     _initWithTextureForCanvas:function (tex, capacity) {
598         this._children = [];
599         this._descendants = [];
600 
601         this._blendFunc = new cc.BlendFunc(cc.BLEND_SRC, cc.BLEND_DST);
602 
603         this._originalTexture = tex;
604         this._textureForCanvas = tex;
605         return true;
606     },
607 
608     _initWithTextureForWebGL:function (tex, capacity) {
609         this._children = [];
610         this._descendants = [];
611 
612         this._blendFunc = new cc.BlendFunc(cc.BLEND_SRC, cc.BLEND_DST);
613         capacity = capacity || cc.DEFAULT_SPRITE_BATCH_CAPACITY;
614         this._textureAtlas = new cc.TextureAtlas();
615         this._textureAtlas.initWithTexture(tex, capacity);
616         this._updateBlendFunc();
617         this.setShaderProgram(cc.ShaderCache.getInstance().programForKey(cc.SHADER_POSITION_TEXTURECOLOR));
618         return true;
619     },
620 
621     /**
622      * add child helper
623      * @param {cc.Sprite} sprite
624      * @param {Number} index
625      */
626     insertChild:function (sprite, index) {
627         sprite.setBatchNode(this);
628         sprite.setAtlasIndex(index);
629         sprite.setDirty(true);
630 
631         var locTextureAtlas = this._textureAtlas;
632         if (locTextureAtlas.getTotalQuads() >= locTextureAtlas.getCapacity())
633             this.increaseAtlasCapacity();
634 
635         locTextureAtlas.insertQuad(sprite.getQuad(), index);
636         this._descendants = cc.ArrayAppendObjectToIndex(this._descendants, sprite, index);
637 
638         // update indices
639         var i = index + 1, locDescendant = this._descendants;
640         if (locDescendant && locDescendant.length > 0) {
641             for (; i < locDescendant.length; i++)
642                 locDescendant[i].setAtlasIndex(locDescendant[i].getAtlasIndex() + 1);
643         }
644 
645         // add children recursively
646         var locChildren = sprite.getChildren();
647         if (locChildren && locChildren.length > 0) {
648             for (i = 0; i < locChildren.length; i++) {
649                 if (locChildren[i]) {
650                     var getIndex = this.atlasIndexForChild(locChildren[i], locChildren[i].getZOrder());
651                     this.insertChild(locChildren[i], getIndex);
652                 }
653             }
654         }
655     },
656 
657     /**
658      * addChild helper, faster than insertChild
659      * @param {cc.Sprite} sprite
660      */
661     appendChild:null,
662 
663     _appendChildForCanvas:function (sprite) {
664         this._reorderChildDirty = true;
665         sprite.setBatchNode(this);
666         sprite.setDirty(true);
667 
668         this._descendants.push(sprite);
669         var index = this._descendants.length - 1;
670         sprite.setAtlasIndex(index);
671 
672         // add children recursively
673         var children = sprite.getChildren();
674         for (var i = 0; i < children.length; i++)
675             this.appendChild(children[i]);
676     },
677 
678     _appendChildForWebGL:function (sprite) {
679         this._reorderChildDirty = true;
680         sprite.setBatchNode(this);
681         sprite.setDirty(true);
682 
683         this._descendants.push(sprite);
684         var index = this._descendants.length - 1;
685         sprite.setAtlasIndex(index);
686 
687         var locTextureAtlas = this._textureAtlas;
688         if (locTextureAtlas.getTotalQuads() == locTextureAtlas.getCapacity())
689             this.increaseAtlasCapacity();
690         locTextureAtlas.insertQuad(sprite.getQuad(), index);
691 
692         // add children recursively
693         var children = sprite.getChildren();
694         for (var i = 0; i < children.length; i++)
695             this.appendChild(children[i]);
696     },
697 
698     /**
699      * remove sprite from TextureAtlas
700      * @param {cc.Sprite} sprite
701      */
702     removeSpriteFromAtlas:null,
703 
704     _removeSpriteFromAtlasForCanvas:function (sprite) {
705         // Cleanup sprite. It might be reused (issue #569)
706         sprite.setBatchNode(null);
707         var locDescendants = this._descendants;
708         var index = cc.ArrayGetIndexOfObject(locDescendants, sprite);
709         if (index != -1) {
710             cc.ArrayRemoveObjectAtIndex(locDescendants, index);
711 
712             // update all sprites beyond this one
713             var len = locDescendants.length;
714             for (; index < len; ++index) {
715                 var s = locDescendants[index];
716                 s.setAtlasIndex(s.getAtlasIndex() - 1);
717             }
718         }
719 
720         // remove children recursively
721         var children = sprite.getChildren();
722         if (children && children.length > 0) {
723             for (var i = 0; i < children.length; i++)
724                 if (children[i])
725                     this.removeSpriteFromAtlas(children[i]);
726         }
727     },
728 
729     _removeSpriteFromAtlasForWebGL:function (sprite) {
730         this._textureAtlas.removeQuadAtIndex(sprite.getAtlasIndex());   // remove from TextureAtlas
731 
732         // Cleanup sprite. It might be reused (issue #569)
733         sprite.setBatchNode(null);
734 
735         var locDescendants = this._descendants;
736         var index = cc.ArrayGetIndexOfObject(locDescendants, sprite);
737         if (index != -1) {
738             cc.ArrayRemoveObjectAtIndex(locDescendants, index);
739 
740             // update all sprites beyond this one
741 
742             var len = locDescendants.length;
743             for (; index < len; ++index) {
744                 var s = locDescendants[index];
745                 s.setAtlasIndex(s.getAtlasIndex() - 1);
746             }
747         }
748 
749         // remove children recursively
750         var children = sprite.getChildren();
751         if (children && children.length > 0) {
752             for (var i = 0; i < children.length; i++)
753                 if (children[i])
754                     this.removeSpriteFromAtlas(children[i]);
755         }
756     },
757     // CCTextureProtocol
758     /**
759      * Return texture of cc.SpriteBatchNode
760      * @return {cc.Texture2D|HTMLImageElement|HTMLCanvasElement}
761      */
762     getTexture:null,
763 
764     _getTextureForCanvas:function () {
765         return this._textureForCanvas;
766     },
767 
768     _getTextureForWebGL:function () {
769         return this._textureAtlas.getTexture();
770     },
771 
772     /**
773      * texture of cc.SpriteBatchNode setter
774      * @param {cc.Texture2D} texture
775      */
776     setTexture:null,
777 
778     _setTextureForCanvas:function (texture) {
779         this._textureForCanvas = texture;
780         var locChildren = this._children;
781         for (var i = 0; i < locChildren.length; i++)
782             locChildren[i].setTexture(texture);
783     },
784 
785     _setTextureForWebGL:function (texture) {
786         this._textureAtlas.setTexture(texture);
787         this._updateBlendFunc();
788     },
789 
790     /**
791      * don't call visit on it's children ( override visit of cc.Node )
792      * @override
793      * @param {CanvasRenderingContext2D} ctx
794      */
795     visit:null,
796 
797     _visitForCanvas:function (ctx) {
798         var context = ctx || cc.renderContext;
799         // quick return if not visible
800         if (!this._visible)
801             return;
802 
803         context.save();
804         this.transform(ctx);
805         var i, locChildren = this._children;
806 
807         if (locChildren) {
808             this.sortAllChildren();
809             for (i = 0; i < locChildren.length; i++) {
810                 if (locChildren[i])
811                     locChildren[i].visit(context);
812             }
813         }
814 
815         context.restore();
816     },
817 
818     _visitForWebGL:function (ctx) {
819         var gl = ctx || cc.renderContext;
820 
821         // CAREFUL:
822         // This visit is almost identical to CocosNode#visit
823         // with the exception that it doesn't call visit on it's children
824         //
825         // The alternative is to have a void CCSprite#visit, but
826         // although this is less mantainable, is faster
827         //
828         if (!this._visible)
829             return;
830         cc.kmGLPushMatrix();
831         var locGrid = this._grid;
832         if (locGrid && locGrid.isActive()) {
833             locGrid.beforeDraw();
834             this.transformAncestors();
835         }
836         this.sortAllChildren();
837         this.transform(gl);
838         this.draw(gl);
839         if (locGrid && locGrid.isActive())
840             locGrid.afterDraw(this);
841         cc.kmGLPopMatrix();
842         this.setOrderOfArrival(0);
843     },
844 
845     /**
846      * add child to cc.SpriteBatchNode (override addChild of cc.Node)
847      * @override
848      * @param {cc.Sprite} child
849      * @param {Number} [zOrder]
850      * @param {Number} [tag]
851      */
852     addChild: null,
853 
854     _addChildForCanvas: function (child, zOrder, tag) {
855         if (child == null)
856             throw "cc.SpriteBatchNode.addChild(): child should be non-null";
857         if(!(child instanceof cc.Sprite)){
858            cc.log( "cc.SpriteBatchNode.addChild(): cc.SpriteBatchNode only supports cc.Sprites as children");
859             return;
860         }
861 
862         zOrder = (zOrder == null) ? child.getZOrder() : zOrder;
863         tag = (tag == null) ? child.getTag() : tag;
864 
865         cc.Node.prototype.addChild.call(this, child, zOrder, tag);
866         this.appendChild(child);
867         this.setNodeDirty();
868     },
869 
870     _addChildForWebGL: function (child, zOrder, tag) {
871         if (child == null)
872             throw "cc.SpriteBatchNode.addChild(): child should be non-null";
873         if(!(child instanceof cc.Sprite)){
874             cc.log( "cc.SpriteBatchNode.addChild(): cc.SpriteBatchNode only supports cc.Sprites as children");
875             return;
876         }
877         if(child.getTexture() != this._textureAtlas.getTexture()){                    // check cc.Sprite is using the same texture id
878             cc.log( "cc.SpriteBatchNode.addChild(): cc.Sprite is not using the same texture");
879             return;
880         }
881 
882         zOrder = (zOrder == null) ? child.getZOrder() : zOrder;
883         tag = (tag == null) ? child.getTag() : tag;
884 
885         cc.Node.prototype.addChild.call(this, child, zOrder, tag);
886         this.appendChild(child);
887         this.setNodeDirty();
888     },
889 
890     /**
891      * <p>Removes all children from the container and do a cleanup all running actions depending on the cleanup parameter. <br/>
892      * (override removeAllChildren of cc.Node)</p>
893      * @param {Boolean} cleanup
894      */
895     removeAllChildren:null,
896 
897     _removeAllChildrenForCanvas:function (cleanup) {
898         // Invalidate atlas index. issue #569
899         // useSelfRender should be performed on all descendants. issue #1216
900         var locDescendants = this._descendants;
901         if (locDescendants && locDescendants.length > 0) {
902             for (var i = 0, len = locDescendants.length; i < len; i++) {
903                 if (locDescendants[i])
904                     locDescendants[i].setBatchNode(null);
905             }
906         }
907 
908         cc.Node.prototype.removeAllChildren.call(this, cleanup);
909         this._descendants.length = 0;
910     },
911 
912     _removeAllChildrenForWebGL:function (cleanup) {
913         // Invalidate atlas index. issue #569
914         // useSelfRender should be performed on all descendants. issue #1216
915         var locDescendants = this._descendants;
916         if (locDescendants && locDescendants.length > 0) {
917             for (var i = 0, len = locDescendants.length; i < len; i++) {
918                 if (locDescendants[i])
919                     locDescendants[i].setBatchNode(null);
920             }
921         }
922         cc.Node.prototype.removeAllChildren.call(this, cleanup);
923         this._descendants.length = 0;
924         this._textureAtlas.removeAllQuads();
925     },
926 
927     sortAllChildren:null,
928 
929     _sortAllChildrenForCanvas:function () {
930         if (this._reorderChildDirty) {
931             var i, j = 0, locChildren = this._children;
932             var length = locChildren.length, tempChild;
933             //insertion sort
934             for (i = 1; i < length; i++) {
935                 var tempItem = locChildren[i];
936                 j = i - 1;
937                 tempChild =  locChildren[j];
938 
939                 //continue moving element downwards while zOrder is smaller or when zOrder is the same but mutatedIndex is smaller
940                 while (j >= 0 && ( tempItem._zOrder < tempChild._zOrder ||
941                     ( tempItem._zOrder == tempChild._zOrder && tempItem._orderOfArrival < tempChild._orderOfArrival ))) {
942                     locChildren[j + 1] = tempChild;
943                     j = j - 1;
944                     tempChild =  locChildren[j];
945                 }
946                 locChildren[j + 1] = tempItem;
947             }
948 
949             //sorted now check all children
950             if (locChildren.length > 0) {
951                 //first sort all children recursively based on zOrder
952                 this._arrayMakeObjectsPerformSelector(locChildren, cc.Node.StateCallbackType.sortAllChildren);
953             }
954             this._reorderChildDirty = false;
955         }
956     },
957 
958     _sortAllChildrenForWebGL:function () {
959         if (this._reorderChildDirty) {
960             var childrenArr = this._children;
961             var i, j = 0, length = childrenArr.length, tempChild;
962             //insertion sort
963             for (i = 1; i < length; i++) {
964                 var tempItem = childrenArr[i];
965                 j = i - 1;
966                 tempChild =  childrenArr[j];
967 
968                 //continue moving element downwards while zOrder is smaller or when zOrder is the same but mutatedIndex is smaller
969                 while (j >= 0 && ( tempItem._zOrder < tempChild._zOrder ||
970                     ( tempItem._zOrder == tempChild._zOrder && tempItem._orderOfArrival < tempChild._orderOfArrival ))) {
971                     childrenArr[j + 1] = tempChild;
972                     j = j - 1;
973                     tempChild =  childrenArr[j];
974                 }
975                 childrenArr[j + 1] = tempItem;
976             }
977 
978             //sorted now check all children
979             if (childrenArr.length > 0) {
980                 //first sort all children recursively based on zOrder
981                 this._arrayMakeObjectsPerformSelector(childrenArr, cc.Node.StateCallbackType.sortAllChildren);
982 
983                 var index = 0;
984                 //fast dispatch, give every child a new atlasIndex based on their relative zOrder (keep parent -> child relations intact)
985                 // and at the same time reorder descedants and the quads to the right index
986                 for (i = 0; i < childrenArr.length; i++)
987                     index = this._updateAtlasIndex(childrenArr[i], index);
988             }
989             this._reorderChildDirty = false;
990         }
991     },
992     /**
993      * draw cc.SpriteBatchNode (override draw of cc.Node)
994      */
995     draw:null,
996 
997     _drawForWebGL:function () {
998         // Optimization: Fast Dispatch
999         if (this._textureAtlas.getTotalQuads() === 0)
1000             return;
1001 
1002         //cc.NODE_DRAW_SETUP(this);
1003         this._shaderProgram.use();
1004         this._shaderProgram.setUniformForModelViewAndProjectionMatrixWithMat4();
1005         this._arrayMakeObjectsPerformSelector(this._children, cc.Node.StateCallbackType.updateTransform);
1006         cc.glBlendFunc(this._blendFunc.src, this._blendFunc.dst);
1007 
1008         this._textureAtlas.drawQuads();
1009     }
1010 });
1011 
1012 if(cc.Browser.supportWebGL){
1013     cc.SpriteBatchNode.prototype.ctor = cc.SpriteBatchNode.prototype._ctorForWebGL;
1014     cc.SpriteBatchNode.prototype.updateQuadFromSprite = cc.SpriteBatchNode.prototype._updateQuadFromSpriteForWebGL;
1015     cc.SpriteBatchNode.prototype.insertQuadFromSprite = cc.SpriteBatchNode.prototype._insertQuadFromSpriteForWebGL;
1016     cc.SpriteBatchNode.prototype.initWithTexture = cc.SpriteBatchNode.prototype._initWithTextureForWebGL;
1017     cc.SpriteBatchNode.prototype.appendChild = cc.SpriteBatchNode.prototype._appendChildForWebGL;
1018     cc.SpriteBatchNode.prototype.removeSpriteFromAtlas = cc.SpriteBatchNode.prototype._removeSpriteFromAtlasForWebGL;
1019     cc.SpriteBatchNode.prototype.getTexture = cc.SpriteBatchNode.prototype._getTextureForWebGL;
1020     cc.SpriteBatchNode.prototype.setTexture = cc.SpriteBatchNode.prototype._setTextureForWebGL;
1021     cc.SpriteBatchNode.prototype.visit = cc.SpriteBatchNode.prototype._visitForWebGL;
1022     cc.SpriteBatchNode.prototype.addChild = cc.SpriteBatchNode.prototype._addChildForWebGL;
1023     cc.SpriteBatchNode.prototype.removeAllChildren = cc.SpriteBatchNode.prototype._removeAllChildrenForWebGL;
1024     cc.SpriteBatchNode.prototype.sortAllChildren = cc.SpriteBatchNode.prototype._sortAllChildrenForWebGL;
1025     cc.SpriteBatchNode.prototype.draw = cc.SpriteBatchNode.prototype._drawForWebGL;
1026 } else {
1027     cc.SpriteBatchNode.prototype.ctor = cc.SpriteBatchNode.prototype._ctorForCanvas;
1028     cc.SpriteBatchNode.prototype.updateQuadFromSprite = cc.SpriteBatchNode.prototype._updateQuadFromSpriteForCanvas;
1029     cc.SpriteBatchNode.prototype.insertQuadFromSprite = cc.SpriteBatchNode.prototype._insertQuadFromSpriteForCanvas;
1030     cc.SpriteBatchNode.prototype.initWithTexture = cc.SpriteBatchNode.prototype._initWithTextureForCanvas;
1031     cc.SpriteBatchNode.prototype.appendChild = cc.SpriteBatchNode.prototype._appendChildForCanvas;
1032     cc.SpriteBatchNode.prototype.removeSpriteFromAtlas = cc.SpriteBatchNode.prototype._removeSpriteFromAtlasForCanvas;
1033     cc.SpriteBatchNode.prototype.getTexture = cc.SpriteBatchNode.prototype._getTextureForCanvas;
1034     cc.SpriteBatchNode.prototype.setTexture = cc.SpriteBatchNode.prototype._setTextureForCanvas;
1035     cc.SpriteBatchNode.prototype.visit = cc.SpriteBatchNode.prototype._visitForCanvas;
1036     cc.SpriteBatchNode.prototype.removeAllChildren = cc.SpriteBatchNode.prototype._removeAllChildrenForCanvas;
1037     cc.SpriteBatchNode.prototype.addChild = cc.SpriteBatchNode.prototype._addChildForCanvas;
1038     cc.SpriteBatchNode.prototype.sortAllChildren = cc.SpriteBatchNode.prototype._sortAllChildrenForCanvas;
1039     cc.SpriteBatchNode.prototype.draw = cc.Node.prototype.draw;
1040 }
1041 
1042 /**
1043  * <p>
1044  *    creates a cc.SpriteBatchNodeCanvas with a file image (.png, .jpg etc) with a default capacity of 29 children.<br/>
1045  *    The capacity will be increased in 33% in runtime if it run out of space.<br/>
1046  *    The file will be loaded using the TextureMgr.<br/>
1047  * </p>
1048  * @param {String} fileImage
1049  * @param {Number} capacity
1050  * @return {cc.SpriteBatchNode}
1051  * @example
1052  * //create a SpriteBatchNode
1053  * var parent2 = cc.SpriteBatchNode.create("res/animations/grossini.png", 50);
1054  */
1055 cc.SpriteBatchNode.create = function (fileImage, capacity) {
1056     capacity = capacity || cc.DEFAULT_SPRITE_BATCH_CAPACITY;
1057     var batchNode = new cc.SpriteBatchNode();
1058     batchNode.init(fileImage, capacity);
1059     return batchNode;
1060 };
1061 
1062 /**
1063  * <p>
1064  *   creates a cc.SpriteBatchNodeCanvas with a texture2d and a default capacity of 29 children.   <br/>
1065  *   The capacity will be increased in 33% in runtime if it run out of space.               <br/>
1066  * </p>
1067  * @param {cc.Texture2D} texture
1068  * @param {Number} [capacity]
1069  * @return {cc.SpriteBatchNode}
1070  */
1071 cc.SpriteBatchNode.createWithTexture = function (texture, capacity) {
1072     capacity = capacity || cc.DEFAULT_SPRITE_BATCH_CAPACITY;
1073     var batchNode = new cc.SpriteBatchNode();
1074     batchNode.initWithTexture(texture, capacity);
1075     return batchNode;
1076 };