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