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 };