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