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 /**
 29  * <p>
 30  *     A cc.SpriteBatchNode can reference one and only one texture (one image file, one texture atlas).<br/>
 31  *     Only the cc.Sprites that are contained in that texture can be added to the cc.SpriteBatchNode.<br/>
 32  *     All cc.Sprites added to a cc.SpriteBatchNode are drawn in one WebGL draw call. <br/>
 33  *     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/>
 34  *     <br/>
 35  *     Limitations:<br/>
 36  *       - The only object that is accepted as child (or grandchild, grand-grandchild, etc...) is cc.Sprite or any subclass of cc.Sprite. <br/>
 37  *          eg: particles, labels and layer can't be added to a cc.SpriteBatchNode. <br/>
 38  *       - Either all its children are Aliased or Antialiased. It can't be a mix. <br/>
 39  *          This is because "alias" is a property of the texture, and all the sprites share the same texture. </br>
 40  * </p>
 41  * @class
 42  * @extends cc.Node
 43  *
 44  * @param {String|cc.Texture2D} fileImage
 45  * @example
 46  *
 47  * // 1. create a SpriteBatchNode with image path
 48  * var spriteBatchNode = new cc.SpriteBatchNode("res/animations/grossini.png");
 49  *
 50  * // 2. create a SpriteBatchNode with texture
 51  * var texture = cc.textureCache.addImage("res/animations/grossini.png");
 52  * var spriteBatchNode = new cc.SpriteBatchNode(texture);
 53  *
 54  * @property {cc.TextureAtlas}  textureAtlas    - The texture atlas
 55  * @property {Array}            descendants     - <@readonly> Descendants of sprite batch node
 56  */
 57 cc.SpriteBatchNode = cc.Node.extend(/** @lends cc.SpriteBatchNode# */{
 58     _blendFunc: null,
 59     // all descendants: chlidren, gran children, etc...
 60     _texture: null,
 61     _className: "SpriteBatchNode",
 62 
 63     ctor: function (fileImage) {
 64         cc.Node.prototype.ctor.call(this);
 65         this._blendFunc = new cc.BlendFunc(cc.BLEND_SRC, cc.BLEND_DST);
 66 
 67         var texture2D;
 68         if (cc.isString(fileImage)) {
 69             texture2D = cc.textureCache.getTextureForKey(fileImage);
 70             if (!texture2D)
 71                 texture2D = cc.textureCache.addImage(fileImage);
 72         }else if (fileImage instanceof cc.Texture2D)
 73             texture2D = fileImage;
 74 
 75         texture2D && this.initWithTexture(texture2D);
 76     },
 77 
 78     /**
 79      * <p>
 80      *    Same as addChild
 81      * </p>
 82      * @param {cc.Sprite} child
 83      * @param {Number} z zOrder
 84      * @param {Number} aTag
 85      * @return {cc.SpriteBatchNode}
 86      * @deprecated since v3.12
 87      */
 88     addSpriteWithoutQuad: function (child, z, aTag) {
 89         this.addChild(child, z, aTag);
 90         return this;
 91     },
 92 
 93     // property
 94     /**
 95      * Return null, no texture atlas is used any more
 96      * @return {cc.TextureAtlas}
 97      * @deprecated since v3.12
 98      */
 99     getTextureAtlas: function () {
100         return null;
101     },
102 
103     /**
104      * TextureAtlas of cc.SpriteBatchNode setter
105      * @param {cc.TextureAtlas} textureAtlas
106      * @deprecated since v3.12
107      */
108     setTextureAtlas: function (textureAtlas) {},
109 
110     /**
111      * Return Descendants of cc.SpriteBatchNode
112      * @return {Array}
113      * @deprecated since v3.12
114      */
115     getDescendants: function () {
116         return this._children;
117     },
118 
119     /**
120      * <p>
121      *    Initializes a cc.SpriteBatchNode with a file image (.png, .jpeg, .pvr, etc) and a capacity of children.<br/>
122      *    The capacity will be increased in 33% in runtime if it run out of space.<br/>
123      *    The file will be loaded using the TextureMgr.<br/>
124      *    Please pass parameters to constructor to initialize the sprite batch node, do not call this function yourself.
125      * </p>
126      * @param {String} fileImage
127      * @param {Number} capacity
128      * @return {Boolean}
129      */
130     initWithFile: function (fileImage, capacity) {
131         var texture2D = cc.textureCache.getTextureForKey(fileImage);
132         if (!texture2D)
133             texture2D = cc.textureCache.addImage(fileImage);
134         return this.initWithTexture(texture2D, capacity);
135     },
136 
137     /**
138      * <p>
139      *    initializes a cc.SpriteBatchNode with a file image (.png, .jpeg, .pvr, etc) and a capacity of children.<br/>
140      *    The capacity will be increased in 33% in runtime if it run out of space.<br/>
141      *    The file will be loaded using the TextureMgr.<br/>
142      *    Please pass parameters to constructor to initialize the sprite batch node, do not call this function yourself.
143      * </p>
144      * @param {String} fileImage
145      * @param {Number} capacity
146      * @return {Boolean}
147      */
148     init: function (fileImage, capacity) {
149         var texture2D = cc.textureCache.getTextureForKey(fileImage);
150         if (!texture2D)
151             texture2D = cc.textureCache.addImage(fileImage);
152         return this.initWithTexture(texture2D, capacity);
153     },
154 
155     /**
156      * Do nothing
157      * @deprecated since v3.12
158      */
159     increaseAtlasCapacity: function () {},
160 
161     /**
162      * Removes a child given a certain index. It will also cleanup the running actions depending on the cleanup parameter.
163      * @warning Removing a child from a cc.SpriteBatchNode is very slow
164      * @param {Number} index
165      * @param {Boolean} doCleanup
166      */
167     removeChildAtIndex: function (index, doCleanup) {
168         this.removeChild(this._children[index], doCleanup);
169     },
170 
171     /**
172      * Do nothing
173      * @param {cc.Sprite} pobParent
174      * @param {Number} index
175      * @return {Number}
176      * @deprecated since v3.12
177      */
178     rebuildIndexInOrder: function (pobParent, index) {
179         return index;
180     },
181 
182     /**
183      * Returns highest atlas index in child
184      * @param {cc.Sprite} sprite
185      * @return {Number}
186      * @deprecated since v3.12
187      */
188     highestAtlasIndexInChild: function (sprite) {
189         var children = sprite.children;
190         if (!children || children.length === 0)
191             return sprite.zIndex;
192         else
193             return this.highestAtlasIndexInChild(children[children.length - 1]);
194     },
195 
196     /**
197      * Returns lowest atlas index in child
198      * @param {cc.Sprite} sprite
199      * @return {Number}
200      * @deprecated since v3.12
201      */
202     lowestAtlasIndexInChild: function (sprite) {
203         var children = sprite.children;
204         if (!children || children.length === 0)
205             return sprite.zIndex;
206         else
207             return this.lowestAtlasIndexInChild(children[children.length - 1]);
208     },
209 
210     /**
211      * Returns index for child
212      * @param {cc.Sprite} sprite
213      * @return {Number}
214      * @deprecated since v3.12
215      */
216     atlasIndexForChild: function (sprite) {
217         return sprite.zIndex;
218     },
219 
220     /**
221      * Sprites use this to start sortChildren, don't call this manually
222      * @param {Boolean} reorder
223      * @deprecated since v3.12
224      */
225     reorderBatch: function (reorder) {
226         this._reorderChildDirty = reorder;
227     },
228 
229     /**
230      * Sets the source and destination blending function for the texture
231      * @param {Number | cc.BlendFunc} src
232      * @param {Number} dst
233      */
234     setBlendFunc: function (src, dst) {
235         if (dst === undefined)
236             this._blendFunc = src;
237         else
238             this._blendFunc = {src: src, dst: dst};
239     },
240 
241     /**
242      * Returns the blending function used for the texture
243      * @return {cc.BlendFunc}
244      */
245     getBlendFunc: function () {
246         return new cc.BlendFunc(this._blendFunc.src,this._blendFunc.dst);
247     },
248 
249     /**
250      * <p>
251      *   Updates a quad at a certain index into the texture atlas. The CCSprite won't be added into the children array.                 <br/>
252      *   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/>
253      *   For example: a tile map (cc.TMXMap) or a label with lots of characters (BitmapFontAtlas)<br/>
254      * </p>
255      * @function
256      * @param {cc.Sprite} sprite
257      * @param {Number} index
258      */
259     updateQuadFromSprite: function (sprite, index) {
260         cc.assert(sprite, cc._LogInfos.CCSpriteBatchNode_updateQuadFromSprite_2);
261         if (!(sprite instanceof cc.Sprite)) {
262             cc.log(cc._LogInfos.CCSpriteBatchNode_updateQuadFromSprite);
263             return;
264         }
265 
266         //
267         // update the quad directly. Don't add the sprite to the scene graph
268         //
269         sprite.dirty = true;
270         // UpdateTransform updates the textureAtlas quad
271         sprite._renderCmd.transform(this._renderCmd, true);
272     },
273 
274     /**
275      * <p>
276      *    Same as addChild(sprite, index)
277      * </p>
278      * @function
279      * @param {cc.Sprite} sprite
280      * @param {Number} index
281      * @deprecated since v3.12
282      */
283     insertQuadFromSprite: function (sprite, index) {
284         this.addChild(sprite, index);
285     },
286 
287     /**
288      * Same as addChild(sprite, index)
289      * @param {cc.Sprite} sprite The child sprite
290      * @param {Number} index The insert index
291      * @deprecated since v3.12
292      */
293     insertChild: function (sprite, index) {
294         this.addChild(sprite, index);
295     },
296 
297     /**
298      * Add child at the end
299      * @function
300      * @param {cc.Sprite} sprite
301      */
302     appendChild: function (sprite) {
303         this.sortAllChildren();
304         var lastLocalZOrder = this._children[this._children.length-1]._localZOrder;
305         this.addChild(sprite. lastLocalZOrder + 1);
306     },
307 
308     /**
309      * Same as removeChild
310      * @function
311      * @param {cc.Sprite} sprite
312      * @param {Boolean} [cleanup=true]  true if all running actions and callbacks on the child node will be cleanup, false otherwise.
313      * @deprecated since v3.12
314      */
315     removeSpriteFromAtlas: function (sprite, cleanup) {
316         this.removeChild(sprite, cleanup);
317     },
318 
319     /**
320      * Set the texture property
321      * @function
322      * @param {cc.Texture2D} tex
323      * @return {Boolean}
324      */
325     initWithTexture: function (tex) {
326         this.setTexture(tex);
327         return true;
328     },
329 
330     // CCTextureProtocol
331     /**
332      * Returns texture of the sprite batch node
333      * @function
334      * @return {cc.Texture2D}
335      */
336     getTexture: function () {
337         return this._texture;
338     },
339 
340     /**
341      * Sets the texture of the sprite batch node.
342      * @function
343      * @param {cc.Texture2D} texture
344      */
345     setTexture: function(texture){
346         this._texture = texture;
347 
348         if (texture._textureLoaded) {
349             var children = this._children, i, len = children.length;
350             for (i = 0; i < len; ++i) {
351                 children[i].setTexture(texture);
352             }
353         }
354         else {
355             texture.addEventListener("load", function(){
356                 var children = this._children, i, len = children.length;
357                 for (i = 0; i < len; ++i) {
358                     children[i].setTexture(texture);
359                 }
360             }, this);
361         }
362     },
363 
364     setShaderProgram: function (newShaderProgram) {
365         this._renderCmd.setShaderProgram(newShaderProgram);
366         var children = this._children, i, len = children.length;
367         for (i = 0; i < len; ++i) {
368             children[i].setShaderProgram(newShaderProgram);
369         }
370     },
371 
372     /**
373      * Add child to the sprite batch node (override addChild of cc.Node)
374      * @function
375      * @override
376      * @param {cc.Sprite} child
377      * @param {Number} [zOrder]
378      * @param {Number} [tag]
379      */
380     addChild: function (child, zOrder, tag) {
381         cc.assert(child !== undefined, cc._LogInfos.CCSpriteBatchNode_addChild_3);
382 
383         if(!this._isValidChild(child))
384             return;
385 
386         zOrder = (zOrder === undefined) ? child.zIndex : zOrder;
387         tag = (tag === undefined) ? child.tag : tag;
388         cc.Node.prototype.addChild.call(this, child, zOrder, tag);
389         
390         // Apply shader
391         if (this._renderCmd._shaderProgram) {
392             child.shaderProgram = this._renderCmd._shaderProgram;
393         }
394     },
395 
396     _isValidChild: function (child) {
397         if (!(child instanceof cc.Sprite)) {
398             cc.log(cc._LogInfos.Sprite_addChild_4);
399             return false;
400         }
401         if (child.texture !== this._texture) {
402             cc.log(cc._LogInfos.Sprite_addChild_5);
403             return false;
404         }
405         return true;
406     }
407 });
408 
409 var _p = cc.SpriteBatchNode.prototype;
410 
411 // Override properties
412 cc.defineGetterSetter(_p, "texture", _p.getTexture, _p.setTexture);
413 cc.defineGetterSetter(_p, "shaderProgram", _p.getShaderProgram, _p.setShaderProgram);
414 
415 
416 /**
417  * <p>
418  *    creates a cc.SpriteBatchNodeCanvas with a file image (.png, .jpg etc) with a default capacity of 29 children.<br/>
419  *    The capacity will be increased in 33% in runtime if it run out of space.<br/>
420  *    The file will be loaded using the TextureMgr.<br/>
421  * </p>
422  * @deprecated since v3.0, please use new construction instead
423  * @see cc.SpriteBatchNode
424  * @param {String|cc.Texture2D} fileImage
425  * @return {cc.SpriteBatchNode}
426  */
427 cc.SpriteBatchNode.create = function (fileImage) {
428     return new cc.SpriteBatchNode(fileImage);
429 };
430 
431 /**
432  * @deprecated since v3.0, please use new construction instead
433  * @see cc.SpriteBatchNode
434  * @function
435  */
436 cc.SpriteBatchNode.createWithTexture = cc.SpriteBatchNode.create;
437