summary refs log tree commit diff stats
path: root/libs/cocos2d/CCSpriteBatchNode.m
diff options
context:
space:
mode:
Diffstat (limited to 'libs/cocos2d/CCSpriteBatchNode.m')
-rwxr-xr-xlibs/cocos2d/CCSpriteBatchNode.m503
1 files changed, 503 insertions, 0 deletions
diff --git a/libs/cocos2d/CCSpriteBatchNode.m b/libs/cocos2d/CCSpriteBatchNode.m new file mode 100755 index 0000000..7c8b05b --- /dev/null +++ b/libs/cocos2d/CCSpriteBatchNode.m
@@ -0,0 +1,503 @@
1/*
2 * cocos2d for iPhone: http://www.cocos2d-iphone.org
3 *
4 * Copyright (C) 2009 Matt Oswald
5 *
6 * Copyright (c) 2009-2010 Ricardo Quesada
7 * Copyright (c) 2011 Zynga Inc.
8 *
9 * Permission is hereby granted, free of charge, to any person obtaining a copy
10 * of this software and associated documentation files (the "Software"), to deal
11 * in the Software without restriction, including without limitation the rights
12 * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
13 * copies of the Software, and to permit persons to whom the Software is
14 * furnished to do so, subject to the following conditions:
15 *
16 * The above copyright notice and this permission notice shall be included in
17 * all copies or substantial portions of the Software.
18 *
19 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
20 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
21 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
22 * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
23 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
24 * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
25 * THE SOFTWARE.
26 *
27 */
28
29
30#import "ccConfig.h"
31#import "CCSprite.h"
32#import "CCSpriteBatchNode.h"
33#import "CCGrid.h"
34#import "CCDrawingPrimitives.h"
35#import "CCTextureCache.h"
36#import "Support/CGPointExtension.h"
37
38const NSUInteger defaultCapacity = 29;
39
40#pragma mark -
41#pragma mark CCSpriteBatchNode
42
43static SEL selUpdate = NULL;
44
45@interface CCSpriteBatchNode (private)
46-(void) updateBlendFunc;
47@end
48
49@implementation CCSpriteBatchNode
50
51@synthesize textureAtlas = textureAtlas_;
52@synthesize blendFunc = blendFunc_;
53@synthesize descendants = descendants_;
54
55
56+(void) initialize
57{
58 if ( self == [CCSpriteBatchNode class] ) {
59 selUpdate = @selector(updateTransform);
60 }
61}
62/*
63 * creation with CCTexture2D
64 */
65+(id)batchNodeWithTexture:(CCTexture2D *)tex
66{
67 return [[[self alloc] initWithTexture:tex capacity:defaultCapacity] autorelease];
68}
69+(id)spriteSheetWithTexture:(CCTexture2D *)tex // XXX DEPRECATED
70{
71 return [self batchNodeWithTexture:tex];
72}
73
74+(id)batchNodeWithTexture:(CCTexture2D *)tex capacity:(NSUInteger)capacity
75{
76 return [[[self alloc] initWithTexture:tex capacity:capacity] autorelease];
77}
78+(id)spriteSheetWithTexture:(CCTexture2D *)tex capacity:(NSUInteger)capacity // XXX DEPRECATED
79{
80 return [self batchNodeWithTexture:tex capacity:capacity];
81}
82
83/*
84 * creation with File Image
85 */
86+(id)batchNodeWithFile:(NSString*)fileImage capacity:(NSUInteger)capacity
87{
88 return [[[self alloc] initWithFile:fileImage capacity:capacity] autorelease];
89}
90+(id)spriteSheetWithFile:(NSString*)fileImage capacity:(NSUInteger)capacity // XXX DEPRECATED
91{
92 return [self batchNodeWithFile:fileImage capacity:capacity];
93}
94
95+(id)batchNodeWithFile:(NSString*) imageFile
96{
97 return [[[self alloc] initWithFile:imageFile capacity:defaultCapacity] autorelease];
98}
99+(id)spriteSheetWithFile:(NSString*) imageFile // XXX DEPRECATED
100{
101 return [self batchNodeWithFile:imageFile];
102}
103
104
105/*
106 * init with CCTexture2D
107 */
108-(id)initWithTexture:(CCTexture2D *)tex capacity:(NSUInteger)capacity
109{
110 if( (self=[super init])) {
111
112 blendFunc_.src = CC_BLEND_SRC;
113 blendFunc_.dst = CC_BLEND_DST;
114 textureAtlas_ = [[CCTextureAtlas alloc] initWithTexture:tex capacity:capacity];
115
116 [self updateBlendFunc];
117
118 // no lazy alloc in this node
119 children_ = [[CCArray alloc] initWithCapacity:capacity];
120 descendants_ = [[CCArray alloc] initWithCapacity:capacity];
121 }
122
123 return self;
124}
125
126/*
127 * init with FileImage
128 */
129-(id)initWithFile:(NSString *)fileImage capacity:(NSUInteger)capacity
130{
131 CCTexture2D *tex = [[CCTextureCache sharedTextureCache] addImage:fileImage];
132 return [self initWithTexture:tex capacity:capacity];
133}
134
135- (NSString*) description
136{
137 return [NSString stringWithFormat:@"<%@ = %08X | Tag = %i>", [self class], self, tag_ ];
138}
139
140-(void)dealloc
141{
142 [textureAtlas_ release];
143 [descendants_ release];
144
145 [super dealloc];
146}
147
148#pragma mark CCSpriteBatchNode - composition
149
150// override visit.
151// Don't call visit on it's children
152-(void) visit
153{
154
155 // CAREFUL:
156 // This visit is almost identical to CocosNode#visit
157 // with the exception that it doesn't call visit on it's children
158 //
159 // The alternative is to have a void CCSprite#visit, but
160 // although this is less mantainable, is faster
161 //
162 if (!visible_)
163 return;
164
165 glPushMatrix();
166
167 if ( grid_ && grid_.active) {
168 [grid_ beforeDraw];
169 [self transformAncestors];
170 }
171
172 [self transform];
173
174 [self draw];
175
176 if ( grid_ && grid_.active)
177 [grid_ afterDraw:self];
178
179 glPopMatrix();
180}
181
182// XXX deprecated
183-(CCSprite*) createSpriteWithRect:(CGRect)rect
184{
185 CCSprite *sprite = [CCSprite spriteWithTexture:textureAtlas_.texture rect:rect];
186 [sprite useBatchNode:self];
187
188 return sprite;
189}
190
191// XXX deprecated
192-(void) initSprite:(CCSprite*)sprite rect:(CGRect)rect
193{
194 [sprite initWithTexture:textureAtlas_.texture rect:rect];
195 [sprite useBatchNode:self];
196}
197
198// override addChild:
199-(void) addChild:(CCSprite*)child z:(NSInteger)z tag:(NSInteger) aTag
200{
201 NSAssert( child != nil, @"Argument must be non-nil");
202 NSAssert( [child isKindOfClass:[CCSprite class]], @"CCSpriteBatchNode only supports CCSprites as children");
203 NSAssert( child.texture.name == textureAtlas_.texture.name, @"CCSprite is not using the same texture id");
204
205 [super addChild:child z:z tag:aTag];
206
207 NSUInteger index = [self atlasIndexForChild:child atZ:z];
208 [self insertChild:child inAtlasAtIndex:index];
209}
210
211// override reorderChild
212-(void) reorderChild:(CCSprite*)child z:(NSInteger)z
213{
214 NSAssert( child != nil, @"Child must be non-nil");
215 NSAssert( [children_ containsObject:child], @"Child doesn't belong to Sprite" );
216
217 if( z == child.zOrder )
218 return;
219
220 // XXX: Instead of removing/adding, it is more efficient to reorder manually
221 [child retain];
222 [self removeChild:child cleanup:NO];
223 [self addChild:child z:z];
224 [child release];
225}
226
227// override removeChild:
228-(void)removeChild: (CCSprite *)sprite cleanup:(BOOL)doCleanup
229{
230 // explicit nil handling
231 if (sprite == nil)
232 return;
233
234 NSAssert([children_ containsObject:sprite], @"CCSpriteBatchNode doesn't contain the sprite. Can't remove it");
235
236 // cleanup before removing
237 [self removeSpriteFromAtlas:sprite];
238
239 [super removeChild:sprite cleanup:doCleanup];
240}
241
242-(void)removeChildAtIndex:(NSUInteger)index cleanup:(BOOL)doCleanup
243{
244 [self removeChild:(CCSprite *)[children_ objectAtIndex:index] cleanup:doCleanup];
245}
246
247-(void)removeAllChildrenWithCleanup:(BOOL)doCleanup
248{
249 // Invalidate atlas index. issue #569
250 [children_ makeObjectsPerformSelector:@selector(useSelfRender)];
251
252 [super removeAllChildrenWithCleanup:doCleanup];
253
254 [descendants_ removeAllObjects];
255 [textureAtlas_ removeAllQuads];
256}
257
258#pragma mark CCSpriteBatchNode - draw
259-(void) draw
260{
261 [super draw];
262
263 // Optimization: Fast Dispatch
264 if( textureAtlas_.totalQuads == 0 )
265 return;
266
267 CCSprite *child;
268 ccArray *array = descendants_->data;
269
270 NSUInteger i = array->num;
271 id *arr = array->arr;
272
273 if( i > 0 ) {
274
275 while (i-- > 0) {
276 child = *arr++;
277
278 // fast dispatch
279 child->updateMethod(child, selUpdate);
280
281#if CC_SPRITEBATCHNODE_DEBUG_DRAW
282 //Issue #528
283 CGRect rect = [child boundingBox];
284 CGPoint vertices[4]={
285 ccp(rect.origin.x,rect.origin.y),
286 ccp(rect.origin.x+rect.size.width,rect.origin.y),
287 ccp(rect.origin.x+rect.size.width,rect.origin.y+rect.size.height),
288 ccp(rect.origin.x,rect.origin.y+rect.size.height),
289 };
290 ccDrawPoly(vertices, 4, YES);
291#endif // CC_SPRITEBATCHNODE_DEBUG_DRAW
292 }
293 }
294
295 // Default GL states: GL_TEXTURE_2D, GL_VERTEX_ARRAY, GL_COLOR_ARRAY, GL_TEXTURE_COORD_ARRAY
296 // Needed states: GL_TEXTURE_2D, GL_VERTEX_ARRAY, GL_COLOR_ARRAY, GL_TEXTURE_COORD_ARRAY
297 // Unneeded states: -
298
299 BOOL newBlend = blendFunc_.src != CC_BLEND_SRC || blendFunc_.dst != CC_BLEND_DST;
300 if( newBlend )
301 glBlendFunc( blendFunc_.src, blendFunc_.dst );
302
303 [textureAtlas_ drawQuads];
304 if( newBlend )
305 glBlendFunc(CC_BLEND_SRC, CC_BLEND_DST);
306}
307
308#pragma mark CCSpriteBatchNode - private
309-(void) increaseAtlasCapacity
310{
311 // if we're going beyond the current TextureAtlas's capacity,
312 // all the previously initialized sprites will need to redo their texture coords
313 // this is likely computationally expensive
314 NSUInteger quantity = (textureAtlas_.capacity + 1) * 4 / 3;
315
316 CCLOG(@"cocos2d: CCSpriteBatchNode: resizing TextureAtlas capacity from [%lu] to [%lu].",
317 (long)textureAtlas_.capacity,
318 (long)quantity);
319
320
321 if( ! [textureAtlas_ resizeCapacity:quantity] ) {
322 // serious problems
323 CCLOG(@"cocos2d: WARNING: Not enough memory to resize the atlas");
324 NSAssert(NO,@"XXX: SpriteSheet#increaseAtlasCapacity SHALL handle this assert");
325 }
326}
327
328
329#pragma mark CCSpriteBatchNode - Atlas Index Stuff
330
331-(NSUInteger) rebuildIndexInOrder:(CCSprite*)node atlasIndex:(NSUInteger)index
332{
333 CCSprite *sprite;
334 CCARRAY_FOREACH(node.children, sprite){
335 if( sprite.zOrder < 0 )
336 index = [self rebuildIndexInOrder:sprite atlasIndex:index];
337 }
338
339 // ignore self (batch node)
340 if( ! [node isEqual:self]) {
341 node.atlasIndex = index;
342 index++;
343 }
344
345 CCARRAY_FOREACH(node.children, sprite){
346 if( sprite.zOrder >= 0 )
347 index = [self rebuildIndexInOrder:sprite atlasIndex:index];
348 }
349
350 return index;
351}
352
353-(NSUInteger) highestAtlasIndexInChild:(CCSprite*)sprite
354{
355 CCArray *array = [sprite children];
356 NSUInteger count = [array count];
357 if( count == 0 )
358 return sprite.atlasIndex;
359 else
360 return [self highestAtlasIndexInChild:[array lastObject]];
361}
362
363-(NSUInteger) lowestAtlasIndexInChild:(CCSprite*)sprite
364{
365 CCArray *array = [sprite children];
366 NSUInteger count = [array count];
367 if( count == 0 )
368 return sprite.atlasIndex;
369 else
370 return [self lowestAtlasIndexInChild:[array objectAtIndex:0] ];
371}
372
373
374-(NSUInteger)atlasIndexForChild:(CCSprite*)sprite atZ:(NSInteger)z
375{
376 CCArray *brothers = [[sprite parent] children];
377 NSUInteger childIndex = [brothers indexOfObject:sprite];
378
379 // ignore parent Z if parent is batchnode
380 BOOL ignoreParent = ( sprite.parent == self );
381 CCSprite *previous = nil;
382 if( childIndex > 0 )
383 previous = [brothers objectAtIndex:childIndex-1];
384
385 // first child of the sprite sheet
386 if( ignoreParent ) {
387 if( childIndex == 0 )
388 return 0;
389 // else
390 return [self highestAtlasIndexInChild: previous] + 1;
391 }
392
393 // parent is a CCSprite, so, it must be taken into account
394
395 // first child of an CCSprite ?
396 if( childIndex == 0 )
397 {
398 CCSprite *p = (CCSprite*) sprite.parent;
399
400 // less than parent and brothers
401 if( z < 0 )
402 return p.atlasIndex;
403 else
404 return p.atlasIndex+1;
405
406 } else {
407 // previous & sprite belong to the same branch
408 if( ( previous.zOrder < 0 && z < 0 )|| (previous.zOrder >= 0 && z >= 0) )
409 return [self highestAtlasIndexInChild:previous] + 1;
410
411 // else (previous < 0 and sprite >= 0 )
412 CCSprite *p = (CCSprite*) sprite.parent;
413 return p.atlasIndex + 1;
414 }
415
416 NSAssert( NO, @"Should not happen. Error calculating Z on Batch Node");
417 return 0;
418}
419
420#pragma mark CCSpriteBatchNode - add / remove / reorder helper methods
421// add child helper
422-(void) insertChild:(CCSprite*)sprite inAtlasAtIndex:(NSUInteger)index
423{
424 [sprite useBatchNode:self];
425 [sprite setAtlasIndex:index];
426 [sprite setDirty: YES];
427
428 if(textureAtlas_.totalQuads == textureAtlas_.capacity)
429 [self increaseAtlasCapacity];
430
431 ccV3F_C4B_T2F_Quad quad = [sprite quad];
432 [textureAtlas_ insertQuad:&quad atIndex:index];
433
434 ccArray *descendantsData = descendants_->data;
435
436 ccArrayInsertObjectAtIndex(descendantsData, sprite, index);
437
438 // update indices
439 NSUInteger i = index+1;
440 CCSprite *child;
441 for(; i<descendantsData->num; i++){
442 child = descendantsData->arr[i];
443 child.atlasIndex = child.atlasIndex + 1;
444 }
445
446 // add children recursively
447 CCARRAY_FOREACH(sprite.children, child){
448 NSUInteger idx = [self atlasIndexForChild:child atZ: child.zOrder];
449 [self insertChild:child inAtlasAtIndex:idx];
450 }
451}
452
453// remove child helper
454-(void) removeSpriteFromAtlas:(CCSprite*)sprite
455{
456 // remove from TextureAtlas
457 [textureAtlas_ removeQuadAtIndex:sprite.atlasIndex];
458
459 // Cleanup sprite. It might be reused (issue #569)
460 [sprite useSelfRender];
461
462 ccArray *descendantsData = descendants_->data;
463 NSUInteger index = ccArrayGetIndexOfObject(descendantsData, sprite);
464 if( index != NSNotFound ) {
465 ccArrayRemoveObjectAtIndex(descendantsData, index);
466
467 // update all sprites beyond this one
468 NSUInteger count = descendantsData->num;
469
470 for(; index < count; index++)
471 {
472 CCSprite *s = descendantsData->arr[index];
473 s.atlasIndex = s.atlasIndex - 1;
474 }
475 }
476
477 // remove children recursively
478 CCSprite *child;
479 CCARRAY_FOREACH(sprite.children, child)
480 [self removeSpriteFromAtlas:child];
481}
482
483#pragma mark CCSpriteBatchNode - CocosNodeTexture protocol
484
485-(void) updateBlendFunc
486{
487 if( ! [textureAtlas_.texture hasPremultipliedAlpha] ) {
488 blendFunc_.src = GL_SRC_ALPHA;
489 blendFunc_.dst = GL_ONE_MINUS_SRC_ALPHA;
490 }
491}
492
493-(void) setTexture:(CCTexture2D*)texture
494{
495 textureAtlas_.texture = texture;
496 [self updateBlendFunc];
497}
498
499-(CCTexture2D*) texture
500{
501 return textureAtlas_.texture;
502}
503@end