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