diff options
Diffstat (limited to 'libs/cocos2d/CCSprite.m')
-rwxr-xr-x | libs/cocos2d/CCSprite.m | 1029 |
1 files changed, 1029 insertions, 0 deletions
diff --git a/libs/cocos2d/CCSprite.m b/libs/cocos2d/CCSprite.m new file mode 100755 index 0000000..37f06d2 --- /dev/null +++ b/libs/cocos2d/CCSprite.m | |||
@@ -0,0 +1,1029 @@ | |||
1 | /* | ||
2 | * cocos2d for iPhone: http://www.cocos2d-iphone.org | ||
3 | * | ||
4 | * Copyright (c) 2008-2010 Ricardo Quesada | ||
5 | * Copyright (c) 2011 Zynga Inc. | ||
6 | * | ||
7 | * Permission is hereby granted, free of charge, to any person obtaining a copy | ||
8 | * of this software and associated documentation files (the "Software"), to deal | ||
9 | * in the Software without restriction, including without limitation the rights | ||
10 | * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell | ||
11 | * copies of the Software, and to permit persons to whom the Software is | ||
12 | * furnished to do so, subject to the following conditions: | ||
13 | * | ||
14 | * The above copyright notice and this permission notice shall be included in | ||
15 | * all copies or substantial portions of the Software. | ||
16 | * | ||
17 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR | ||
18 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, | ||
19 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE | ||
20 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER | ||
21 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, | ||
22 | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN | ||
23 | * THE SOFTWARE. | ||
24 | * | ||
25 | */ | ||
26 | |||
27 | #import <Availability.h> | ||
28 | |||
29 | #import "ccConfig.h" | ||
30 | #import "CCSpriteBatchNode.h" | ||
31 | #import "CCSprite.h" | ||
32 | #import "CCSpriteFrame.h" | ||
33 | #import "CCSpriteFrameCache.h" | ||
34 | #import "CCAnimation.h" | ||
35 | #import "CCAnimationCache.h" | ||
36 | #import "CCTextureCache.h" | ||
37 | #import "Support/CGPointExtension.h" | ||
38 | #import "CCDrawingPrimitives.h" | ||
39 | |||
40 | #pragma mark - | ||
41 | #pragma mark CCSprite | ||
42 | |||
43 | #if CC_SPRITEBATCHNODE_RENDER_SUBPIXEL | ||
44 | #define RENDER_IN_SUBPIXEL | ||
45 | #else | ||
46 | #define RENDER_IN_SUBPIXEL(__A__) ( (int)(__A__)) | ||
47 | #endif | ||
48 | |||
49 | // XXX: Optmization | ||
50 | struct transformValues_ { | ||
51 | CGPoint pos; // position x and y | ||
52 | CGPoint scale; // scale x and y | ||
53 | float rotation; | ||
54 | CGPoint skew; // skew x and y | ||
55 | CGPoint ap; // anchor point in pixels | ||
56 | BOOL visible; | ||
57 | }; | ||
58 | |||
59 | @interface CCSprite (Private) | ||
60 | -(void)updateTextureCoords:(CGRect)rect; | ||
61 | -(void)updateBlendFunc; | ||
62 | -(void) initAnimationDictionary; | ||
63 | -(void) getTransformValues:(struct transformValues_*)tv; // optimization | ||
64 | @end | ||
65 | |||
66 | @implementation CCSprite | ||
67 | |||
68 | @synthesize dirty = dirty_; | ||
69 | @synthesize quad = quad_; | ||
70 | @synthesize atlasIndex = atlasIndex_; | ||
71 | @synthesize textureRect = rect_; | ||
72 | @synthesize textureRectRotated = rectRotated_; | ||
73 | @synthesize blendFunc = blendFunc_; | ||
74 | @synthesize usesBatchNode = usesBatchNode_; | ||
75 | @synthesize textureAtlas = textureAtlas_; | ||
76 | @synthesize batchNode = batchNode_; | ||
77 | @synthesize honorParentTransform = honorParentTransform_; | ||
78 | @synthesize offsetPositionInPixels = offsetPositionInPixels_; | ||
79 | |||
80 | |||
81 | +(id)spriteWithTexture:(CCTexture2D*)texture | ||
82 | { | ||
83 | return [[[self alloc] initWithTexture:texture] autorelease]; | ||
84 | } | ||
85 | |||
86 | +(id)spriteWithTexture:(CCTexture2D*)texture rect:(CGRect)rect | ||
87 | { | ||
88 | return [[[self alloc] initWithTexture:texture rect:rect] autorelease]; | ||
89 | } | ||
90 | |||
91 | +(id)spriteWithFile:(NSString*)filename | ||
92 | { | ||
93 | return [[[self alloc] initWithFile:filename] autorelease]; | ||
94 | } | ||
95 | |||
96 | +(id)spriteWithFile:(NSString*)filename rect:(CGRect)rect | ||
97 | { | ||
98 | return [[[self alloc] initWithFile:filename rect:rect] autorelease]; | ||
99 | } | ||
100 | |||
101 | +(id)spriteWithSpriteFrame:(CCSpriteFrame*)spriteFrame | ||
102 | { | ||
103 | return [[[self alloc] initWithSpriteFrame:spriteFrame] autorelease]; | ||
104 | } | ||
105 | |||
106 | +(id)spriteWithSpriteFrameName:(NSString*)spriteFrameName | ||
107 | { | ||
108 | CCSpriteFrame *frame = [[CCSpriteFrameCache sharedSpriteFrameCache] spriteFrameByName:spriteFrameName]; | ||
109 | |||
110 | NSAssert1(frame!=nil, @"Invalid spriteFrameName: %@", spriteFrameName); | ||
111 | return [self spriteWithSpriteFrame:frame]; | ||
112 | } | ||
113 | |||
114 | +(id)spriteWithCGImage:(CGImageRef)image key:(NSString*)key | ||
115 | { | ||
116 | return [[[self alloc] initWithCGImage:image key:key] autorelease]; | ||
117 | } | ||
118 | |||
119 | +(id) spriteWithBatchNode:(CCSpriteBatchNode*)batchNode rect:(CGRect)rect | ||
120 | { | ||
121 | return [[[self alloc] initWithBatchNode:batchNode rect:rect] autorelease]; | ||
122 | } | ||
123 | |||
124 | -(id) init | ||
125 | { | ||
126 | if( (self=[super init]) ) { | ||
127 | dirty_ = recursiveDirty_ = NO; | ||
128 | |||
129 | // by default use "Self Render". | ||
130 | // if the sprite is added to a batchnode, then it will automatically switch to "batchnode Render" | ||
131 | [self useSelfRender]; | ||
132 | |||
133 | opacityModifyRGB_ = YES; | ||
134 | opacity_ = 255; | ||
135 | color_ = colorUnmodified_ = ccWHITE; | ||
136 | |||
137 | blendFunc_.src = CC_BLEND_SRC; | ||
138 | blendFunc_.dst = CC_BLEND_DST; | ||
139 | |||
140 | // update texture (calls updateBlendFunc) | ||
141 | [self setTexture:nil]; | ||
142 | |||
143 | // clean the Quad | ||
144 | bzero(&quad_, sizeof(quad_)); | ||
145 | |||
146 | flipY_ = flipX_ = NO; | ||
147 | |||
148 | // lazy alloc | ||
149 | animations_ = nil; | ||
150 | |||
151 | // default transform anchor: center | ||
152 | anchorPoint_ = ccp(0.5f, 0.5f); | ||
153 | |||
154 | // zwoptex default values | ||
155 | offsetPositionInPixels_ = CGPointZero; | ||
156 | |||
157 | honorParentTransform_ = CC_HONOR_PARENT_TRANSFORM_ALL; | ||
158 | hasChildren_ = NO; | ||
159 | |||
160 | // Atlas: Color | ||
161 | ccColor4B tmpColor = {255,255,255,255}; | ||
162 | quad_.bl.colors = tmpColor; | ||
163 | quad_.br.colors = tmpColor; | ||
164 | quad_.tl.colors = tmpColor; | ||
165 | quad_.tr.colors = tmpColor; | ||
166 | |||
167 | // Atlas: Vertex | ||
168 | |||
169 | // updated in "useSelfRender" | ||
170 | |||
171 | // Atlas: TexCoords | ||
172 | [self setTextureRectInPixels:CGRectZero rotated:NO untrimmedSize:CGSizeZero]; | ||
173 | |||
174 | // updateMethod selector | ||
175 | updateMethod = (__typeof__(updateMethod))[self methodForSelector:@selector(updateTransform)]; | ||
176 | } | ||
177 | |||
178 | return self; | ||
179 | } | ||
180 | |||
181 | -(id) initWithTexture:(CCTexture2D*)texture rect:(CGRect)rect | ||
182 | { | ||
183 | NSAssert(texture!=nil, @"Invalid texture for sprite"); | ||
184 | // IMPORTANT: [self init] and not [super init]; | ||
185 | if( (self = [self init]) ) | ||
186 | { | ||
187 | [self setTexture:texture]; | ||
188 | [self setTextureRect:rect]; | ||
189 | } | ||
190 | return self; | ||
191 | } | ||
192 | |||
193 | -(id) initWithTexture:(CCTexture2D*)texture | ||
194 | { | ||
195 | NSAssert(texture!=nil, @"Invalid texture for sprite"); | ||
196 | |||
197 | CGRect rect = CGRectZero; | ||
198 | rect.size = texture.contentSize; | ||
199 | return [self initWithTexture:texture rect:rect]; | ||
200 | } | ||
201 | |||
202 | -(id) initWithFile:(NSString*)filename | ||
203 | { | ||
204 | NSAssert(filename!=nil, @"Invalid filename for sprite"); | ||
205 | |||
206 | CCTexture2D *texture = [[CCTextureCache sharedTextureCache] addImage: filename]; | ||
207 | if( texture ) { | ||
208 | CGRect rect = CGRectZero; | ||
209 | rect.size = texture.contentSize; | ||
210 | return [self initWithTexture:texture rect:rect]; | ||
211 | } | ||
212 | |||
213 | [self release]; | ||
214 | return nil; | ||
215 | } | ||
216 | |||
217 | -(id) initWithFile:(NSString*)filename rect:(CGRect)rect | ||
218 | { | ||
219 | NSAssert(filename!=nil, @"Invalid filename for sprite"); | ||
220 | |||
221 | CCTexture2D *texture = [[CCTextureCache sharedTextureCache] addImage: filename]; | ||
222 | if( texture ) | ||
223 | return [self initWithTexture:texture rect:rect]; | ||
224 | |||
225 | [self release]; | ||
226 | return nil; | ||
227 | } | ||
228 | |||
229 | - (id) initWithSpriteFrame:(CCSpriteFrame*)spriteFrame | ||
230 | { | ||
231 | NSAssert(spriteFrame!=nil, @"Invalid spriteFrame for sprite"); | ||
232 | |||
233 | id ret = [self initWithTexture:spriteFrame.texture rect:spriteFrame.rect]; | ||
234 | [self setDisplayFrame:spriteFrame]; | ||
235 | return ret; | ||
236 | } | ||
237 | |||
238 | -(id)initWithSpriteFrameName:(NSString*)spriteFrameName | ||
239 | { | ||
240 | NSAssert(spriteFrameName!=nil, @"Invalid spriteFrameName for sprite"); | ||
241 | |||
242 | CCSpriteFrame *frame = [[CCSpriteFrameCache sharedSpriteFrameCache] spriteFrameByName:spriteFrameName]; | ||
243 | return [self initWithSpriteFrame:frame]; | ||
244 | } | ||
245 | |||
246 | // XXX: deprecated | ||
247 | - (id) initWithCGImage: (CGImageRef)image | ||
248 | { | ||
249 | NSAssert(image!=nil, @"Invalid CGImageRef for sprite"); | ||
250 | |||
251 | // XXX: possible bug. See issue #349. New API should be added | ||
252 | NSString *key = [NSString stringWithFormat:@"%08X",(unsigned long)image]; | ||
253 | CCTexture2D *texture = [[CCTextureCache sharedTextureCache] addCGImage:image forKey:key]; | ||
254 | |||
255 | CGRect rect = CGRectZero; | ||
256 | rect.size = texture.contentSize; | ||
257 | |||
258 | return [self initWithTexture:texture rect:rect]; | ||
259 | } | ||
260 | |||
261 | - (id) initWithCGImage:(CGImageRef)image key:(NSString*)key | ||
262 | { | ||
263 | NSAssert(image!=nil, @"Invalid CGImageRef for sprite"); | ||
264 | |||
265 | // XXX: possible bug. See issue #349. New API should be added | ||
266 | CCTexture2D *texture = [[CCTextureCache sharedTextureCache] addCGImage:image forKey:key]; | ||
267 | |||
268 | CGRect rect = CGRectZero; | ||
269 | rect.size = texture.contentSize; | ||
270 | |||
271 | return [self initWithTexture:texture rect:rect]; | ||
272 | } | ||
273 | |||
274 | -(id) initWithBatchNode:(CCSpriteBatchNode*)batchNode rect:(CGRect)rect | ||
275 | { | ||
276 | id ret = [self initWithTexture:batchNode.texture rect:rect]; | ||
277 | [self useBatchNode:batchNode]; | ||
278 | |||
279 | return ret; | ||
280 | } | ||
281 | |||
282 | -(id) initWithBatchNode:(CCSpriteBatchNode*)batchNode rectInPixels:(CGRect)rect | ||
283 | { | ||
284 | id ret = [self initWithTexture:batchNode.texture]; | ||
285 | [self setTextureRectInPixels:rect rotated:NO untrimmedSize:rect.size]; | ||
286 | [self useBatchNode:batchNode]; | ||
287 | |||
288 | return ret; | ||
289 | } | ||
290 | |||
291 | - (NSString*) description | ||
292 | { | ||
293 | return [NSString stringWithFormat:@"<%@ = %08X | Rect = (%.2f,%.2f,%.2f,%.2f) | tag = %i | atlasIndex = %i>", [self class], self, | ||
294 | rect_.origin.x, rect_.origin.y, rect_.size.width, rect_.size.height, | ||
295 | tag_, | ||
296 | atlasIndex_ | ||
297 | ]; | ||
298 | } | ||
299 | |||
300 | - (void) dealloc | ||
301 | { | ||
302 | [texture_ release]; | ||
303 | [animations_ release]; | ||
304 | [super dealloc]; | ||
305 | } | ||
306 | |||
307 | -(void) useSelfRender | ||
308 | { | ||
309 | atlasIndex_ = CCSpriteIndexNotInitialized; | ||
310 | usesBatchNode_ = NO; | ||
311 | textureAtlas_ = nil; | ||
312 | batchNode_ = nil; | ||
313 | dirty_ = recursiveDirty_ = NO; | ||
314 | |||
315 | float x1 = 0 + offsetPositionInPixels_.x; | ||
316 | float y1 = 0 + offsetPositionInPixels_.y; | ||
317 | float x2 = x1 + rectInPixels_.size.width; | ||
318 | float y2 = y1 + rectInPixels_.size.height; | ||
319 | quad_.bl.vertices = (ccVertex3F) { x1, y1, 0 }; | ||
320 | quad_.br.vertices = (ccVertex3F) { x2, y1, 0 }; | ||
321 | quad_.tl.vertices = (ccVertex3F) { x1, y2, 0 }; | ||
322 | quad_.tr.vertices = (ccVertex3F) { x2, y2, 0 }; | ||
323 | } | ||
324 | |||
325 | -(void) useBatchNode:(CCSpriteBatchNode*)batchNode | ||
326 | { | ||
327 | usesBatchNode_ = YES; | ||
328 | textureAtlas_ = [batchNode textureAtlas]; // weak ref | ||
329 | batchNode_ = batchNode; // weak ref | ||
330 | } | ||
331 | |||
332 | -(void) initAnimationDictionary | ||
333 | { | ||
334 | animations_ = [[NSMutableDictionary alloc] initWithCapacity:2]; | ||
335 | } | ||
336 | |||
337 | -(void)setTextureRect:(CGRect)rect | ||
338 | { | ||
339 | CGRect rectInPixels = CC_RECT_POINTS_TO_PIXELS( rect ); | ||
340 | [self setTextureRectInPixels:rectInPixels rotated:NO untrimmedSize:rectInPixels.size]; | ||
341 | } | ||
342 | |||
343 | -(void)setTextureRectInPixels:(CGRect)rect rotated:(BOOL)rotated untrimmedSize:(CGSize)untrimmedSize | ||
344 | { | ||
345 | rectInPixels_ = rect; | ||
346 | rect_ = CC_RECT_PIXELS_TO_POINTS( rect ); | ||
347 | rectRotated_ = rotated; | ||
348 | |||
349 | [self setContentSizeInPixels:untrimmedSize]; | ||
350 | [self updateTextureCoords:rectInPixels_]; | ||
351 | |||
352 | CGPoint relativeOffsetInPixels = unflippedOffsetPositionFromCenter_; | ||
353 | |||
354 | // issue #732 | ||
355 | if( flipX_ ) | ||
356 | relativeOffsetInPixels.x = -relativeOffsetInPixels.x; | ||
357 | if( flipY_ ) | ||
358 | relativeOffsetInPixels.y = -relativeOffsetInPixels.y; | ||
359 | |||
360 | offsetPositionInPixels_.x = relativeOffsetInPixels.x + (contentSizeInPixels_.width - rectInPixels_.size.width) / 2; | ||
361 | offsetPositionInPixels_.y = relativeOffsetInPixels.y + (contentSizeInPixels_.height - rectInPixels_.size.height) / 2; | ||
362 | |||
363 | |||
364 | // rendering using batch node | ||
365 | if( usesBatchNode_ ) { | ||
366 | // update dirty_, don't update recursiveDirty_ | ||
367 | dirty_ = YES; | ||
368 | } | ||
369 | |||
370 | // self rendering | ||
371 | else | ||
372 | { | ||
373 | // Atlas: Vertex | ||
374 | float x1 = 0 + offsetPositionInPixels_.x; | ||
375 | float y1 = 0 + offsetPositionInPixels_.y; | ||
376 | float x2 = x1 + rectInPixels_.size.width; | ||
377 | float y2 = y1 + rectInPixels_.size.height; | ||
378 | |||
379 | // Don't update Z. | ||
380 | quad_.bl.vertices = (ccVertex3F) { x1, y1, 0 }; | ||
381 | quad_.br.vertices = (ccVertex3F) { x2, y1, 0 }; | ||
382 | quad_.tl.vertices = (ccVertex3F) { x1, y2, 0 }; | ||
383 | quad_.tr.vertices = (ccVertex3F) { x2, y2, 0 }; | ||
384 | } | ||
385 | } | ||
386 | |||
387 | -(void)updateTextureCoords:(CGRect)rect | ||
388 | { | ||
389 | CCTexture2D *tex = (usesBatchNode_)?[textureAtlas_ texture]:texture_; | ||
390 | if(!tex) | ||
391 | return; | ||
392 | |||
393 | float atlasWidth = (float)tex.pixelsWide; | ||
394 | float atlasHeight = (float)tex.pixelsHigh; | ||
395 | |||
396 | float left,right,top,bottom; | ||
397 | |||
398 | if(rectRotated_){ | ||
399 | #if CC_FIX_ARTIFACTS_BY_STRECHING_TEXEL | ||
400 | left = (2*rect.origin.x+1)/(2*atlasWidth); | ||
401 | right = left+(rect.size.height*2-2)/(2*atlasWidth); | ||
402 | top = (2*rect.origin.y+1)/(2*atlasHeight); | ||
403 | bottom = top+(rect.size.width*2-2)/(2*atlasHeight); | ||
404 | #else | ||
405 | left = rect.origin.x/atlasWidth; | ||
406 | right = left+(rect.size.height/atlasWidth); | ||
407 | top = rect.origin.y/atlasHeight; | ||
408 | bottom = top+(rect.size.width/atlasHeight); | ||
409 | #endif // ! CC_FIX_ARTIFACTS_BY_STRECHING_TEXEL | ||
410 | |||
411 | if( flipX_) | ||
412 | CC_SWAP(top,bottom); | ||
413 | if( flipY_) | ||
414 | CC_SWAP(left,right); | ||
415 | |||
416 | quad_.bl.texCoords.u = left; | ||
417 | quad_.bl.texCoords.v = top; | ||
418 | quad_.br.texCoords.u = left; | ||
419 | quad_.br.texCoords.v = bottom; | ||
420 | quad_.tl.texCoords.u = right; | ||
421 | quad_.tl.texCoords.v = top; | ||
422 | quad_.tr.texCoords.u = right; | ||
423 | quad_.tr.texCoords.v = bottom; | ||
424 | } else { | ||
425 | #if CC_FIX_ARTIFACTS_BY_STRECHING_TEXEL | ||
426 | left = (2*rect.origin.x+1)/(2*atlasWidth); | ||
427 | right = left + (rect.size.width*2-2)/(2*atlasWidth); | ||
428 | top = (2*rect.origin.y+1)/(2*atlasHeight); | ||
429 | bottom = top + (rect.size.height*2-2)/(2*atlasHeight); | ||
430 | #else | ||
431 | left = rect.origin.x/atlasWidth; | ||
432 | right = left + rect.size.width/atlasWidth; | ||
433 | top = rect.origin.y/atlasHeight; | ||
434 | bottom = top + rect.size.height/atlasHeight; | ||
435 | #endif // ! CC_FIX_ARTIFACTS_BY_STRECHING_TEXEL | ||
436 | |||
437 | if( flipX_) | ||
438 | CC_SWAP(left,right); | ||
439 | if( flipY_) | ||
440 | CC_SWAP(top,bottom); | ||
441 | |||
442 | quad_.bl.texCoords.u = left; | ||
443 | quad_.bl.texCoords.v = bottom; | ||
444 | quad_.br.texCoords.u = right; | ||
445 | quad_.br.texCoords.v = bottom; | ||
446 | quad_.tl.texCoords.u = left; | ||
447 | quad_.tl.texCoords.v = top; | ||
448 | quad_.tr.texCoords.u = right; | ||
449 | quad_.tr.texCoords.v = top; | ||
450 | } | ||
451 | } | ||
452 | |||
453 | -(void)updateTransform | ||
454 | { | ||
455 | NSAssert( usesBatchNode_, @"updateTransform is only valid when CCSprite is being renderd using an CCSpriteBatchNode"); | ||
456 | |||
457 | // optimization. Quick return if not dirty | ||
458 | if( ! dirty_ ) | ||
459 | return; | ||
460 | |||
461 | CGAffineTransform matrix; | ||
462 | |||
463 | // Optimization: if it is not visible, then do nothing | ||
464 | if( ! visible_ ) { | ||
465 | quad_.br.vertices = quad_.tl.vertices = quad_.tr.vertices = quad_.bl.vertices = (ccVertex3F){0,0,0}; | ||
466 | [textureAtlas_ updateQuad:&quad_ atIndex:atlasIndex_]; | ||
467 | dirty_ = recursiveDirty_ = NO; | ||
468 | return ; | ||
469 | } | ||
470 | |||
471 | |||
472 | // Optimization: If parent is batchnode, or parent is nil | ||
473 | // build Affine transform manually | ||
474 | if( ! parent_ || parent_ == batchNode_ ) { | ||
475 | |||
476 | float radians = -CC_DEGREES_TO_RADIANS(rotation_); | ||
477 | float c = cosf(radians); | ||
478 | float s = sinf(radians); | ||
479 | |||
480 | matrix = CGAffineTransformMake( c * scaleX_, s * scaleX_, | ||
481 | -s * scaleY_, c * scaleY_, | ||
482 | positionInPixels_.x, positionInPixels_.y); | ||
483 | if( skewX_ || skewY_ ) { | ||
484 | CGAffineTransform skewMatrix = CGAffineTransformMake(1.0f, tanf(CC_DEGREES_TO_RADIANS(skewY_)), | ||
485 | tanf(CC_DEGREES_TO_RADIANS(skewX_)), 1.0f, | ||
486 | 0.0f, 0.0f); | ||
487 | matrix = CGAffineTransformConcat(skewMatrix, matrix); | ||
488 | } | ||
489 | matrix = CGAffineTransformTranslate(matrix, -anchorPointInPixels_.x, -anchorPointInPixels_.y); | ||
490 | |||
491 | |||
492 | } else { // parent_ != batchNode_ | ||
493 | |||
494 | // else do affine transformation according to the HonorParentTransform | ||
495 | |||
496 | matrix = CGAffineTransformIdentity; | ||
497 | ccHonorParentTransform prevHonor = CC_HONOR_PARENT_TRANSFORM_ALL; | ||
498 | |||
499 | for (CCNode *p = self ; p && p != batchNode_ ; p = p.parent) { | ||
500 | |||
501 | // Might happen. Issue #1053 | ||
502 | NSAssert( [p isKindOfClass:[CCSprite class]], @"CCSprite should be a CCSprite subclass. Probably you initialized an sprite with a batchnode, but you didn't add it to the batch node." ); | ||
503 | |||
504 | struct transformValues_ tv; | ||
505 | [(CCSprite*)p getTransformValues: &tv]; | ||
506 | |||
507 | // If any of the parents are not visible, then don't draw this node | ||
508 | if( ! tv.visible ) { | ||
509 | quad_.br.vertices = quad_.tl.vertices = quad_.tr.vertices = quad_.bl.vertices = (ccVertex3F){0,0,0}; | ||
510 | [textureAtlas_ updateQuad:&quad_ atIndex:atlasIndex_]; | ||
511 | dirty_ = recursiveDirty_ = NO; | ||
512 | return; | ||
513 | } | ||
514 | CGAffineTransform newMatrix = CGAffineTransformIdentity; | ||
515 | |||
516 | // 2nd: Translate, Skew, Rotate, Scale | ||
517 | if( prevHonor & CC_HONOR_PARENT_TRANSFORM_TRANSLATE ) | ||
518 | newMatrix = CGAffineTransformTranslate(newMatrix, tv.pos.x, tv.pos.y); | ||
519 | if( prevHonor & CC_HONOR_PARENT_TRANSFORM_ROTATE ) | ||
520 | newMatrix = CGAffineTransformRotate(newMatrix, -CC_DEGREES_TO_RADIANS(tv.rotation)); | ||
521 | if ( prevHonor & CC_HONOR_PARENT_TRANSFORM_SKEW ) { | ||
522 | CGAffineTransform skew = CGAffineTransformMake(1.0f, tanf(CC_DEGREES_TO_RADIANS(tv.skew.y)), tanf(CC_DEGREES_TO_RADIANS(tv.skew.x)), 1.0f, 0.0f, 0.0f); | ||
523 | // apply the skew to the transform | ||
524 | newMatrix = CGAffineTransformConcat(skew, newMatrix); | ||
525 | } | ||
526 | if( prevHonor & CC_HONOR_PARENT_TRANSFORM_SCALE ) { | ||
527 | newMatrix = CGAffineTransformScale(newMatrix, tv.scale.x, tv.scale.y); | ||
528 | } | ||
529 | |||
530 | // 3rd: Translate anchor point | ||
531 | newMatrix = CGAffineTransformTranslate(newMatrix, -tv.ap.x, -tv.ap.y); | ||
532 | |||
533 | // 4th: Matrix multiplication | ||
534 | matrix = CGAffineTransformConcat( matrix, newMatrix); | ||
535 | |||
536 | prevHonor = [(CCSprite*)p honorParentTransform]; | ||
537 | } | ||
538 | } | ||
539 | |||
540 | |||
541 | // | ||
542 | // calculate the Quad based on the Affine Matrix | ||
543 | // | ||
544 | |||
545 | CGSize size = rectInPixels_.size; | ||
546 | |||
547 | float x1 = offsetPositionInPixels_.x; | ||
548 | float y1 = offsetPositionInPixels_.y; | ||
549 | |||
550 | float x2 = x1 + size.width; | ||
551 | float y2 = y1 + size.height; | ||
552 | float x = matrix.tx; | ||
553 | float y = matrix.ty; | ||
554 | |||
555 | float cr = matrix.a; | ||
556 | float sr = matrix.b; | ||
557 | float cr2 = matrix.d; | ||
558 | float sr2 = -matrix.c; | ||
559 | float ax = x1 * cr - y1 * sr2 + x; | ||
560 | float ay = x1 * sr + y1 * cr2 + y; | ||
561 | |||
562 | float bx = x2 * cr - y1 * sr2 + x; | ||
563 | float by = x2 * sr + y1 * cr2 + y; | ||
564 | |||
565 | float cx = x2 * cr - y2 * sr2 + x; | ||
566 | float cy = x2 * sr + y2 * cr2 + y; | ||
567 | |||
568 | float dx = x1 * cr - y2 * sr2 + x; | ||
569 | float dy = x1 * sr + y2 * cr2 + y; | ||
570 | |||
571 | quad_.bl.vertices = (ccVertex3F) { RENDER_IN_SUBPIXEL(ax), RENDER_IN_SUBPIXEL(ay), vertexZ_ }; | ||
572 | quad_.br.vertices = (ccVertex3F) { RENDER_IN_SUBPIXEL(bx), RENDER_IN_SUBPIXEL(by), vertexZ_ }; | ||
573 | quad_.tl.vertices = (ccVertex3F) { RENDER_IN_SUBPIXEL(dx), RENDER_IN_SUBPIXEL(dy), vertexZ_ }; | ||
574 | quad_.tr.vertices = (ccVertex3F) { RENDER_IN_SUBPIXEL(cx), RENDER_IN_SUBPIXEL(cy), vertexZ_ }; | ||
575 | |||
576 | [textureAtlas_ updateQuad:&quad_ atIndex:atlasIndex_]; | ||
577 | dirty_ = recursiveDirty_ = NO; | ||
578 | } | ||
579 | |||
580 | // XXX: Optimization: instead of calling 5 times the parent sprite to obtain: position, scale.x, scale.y, anchorpoint and rotation, | ||
581 | // this fuction return the 5 values in 1 single call | ||
582 | -(void) getTransformValues:(struct transformValues_*) tv | ||
583 | { | ||
584 | tv->pos = positionInPixels_; | ||
585 | tv->scale.x = scaleX_; | ||
586 | tv->scale.y = scaleY_; | ||
587 | tv->rotation = rotation_; | ||
588 | tv->skew.x = skewX_; | ||
589 | tv->skew.y = skewY_; | ||
590 | tv->ap = anchorPointInPixels_; | ||
591 | tv->visible = visible_; | ||
592 | } | ||
593 | |||
594 | #pragma mark CCSprite - draw | ||
595 | |||
596 | -(void) draw | ||
597 | { | ||
598 | [super draw]; | ||
599 | |||
600 | NSAssert(!usesBatchNode_, @"If CCSprite is being rendered by CCSpriteBatchNode, CCSprite#draw SHOULD NOT be called"); | ||
601 | |||
602 | // Default GL states: GL_TEXTURE_2D, GL_VERTEX_ARRAY, GL_COLOR_ARRAY, GL_TEXTURE_COORD_ARRAY | ||
603 | // Needed states: GL_TEXTURE_2D, GL_VERTEX_ARRAY, GL_COLOR_ARRAY, GL_TEXTURE_COORD_ARRAY | ||
604 | // Unneeded states: - | ||
605 | |||
606 | BOOL newBlend = blendFunc_.src != CC_BLEND_SRC || blendFunc_.dst != CC_BLEND_DST; | ||
607 | if( newBlend ) | ||
608 | glBlendFunc( blendFunc_.src, blendFunc_.dst ); | ||
609 | |||
610 | #define kQuadSize sizeof(quad_.bl) | ||
611 | glBindTexture(GL_TEXTURE_2D, [texture_ name]); | ||
612 | |||
613 | long offset = (long)&quad_; | ||
614 | |||
615 | // vertex | ||
616 | NSInteger diff = offsetof( ccV3F_C4B_T2F, vertices); | ||
617 | glVertexPointer(3, GL_FLOAT, kQuadSize, (void*) (offset + diff) ); | ||
618 | |||
619 | // color | ||
620 | diff = offsetof( ccV3F_C4B_T2F, colors); | ||
621 | glColorPointer(4, GL_UNSIGNED_BYTE, kQuadSize, (void*)(offset + diff)); | ||
622 | |||
623 | // tex coords | ||
624 | diff = offsetof( ccV3F_C4B_T2F, texCoords); | ||
625 | glTexCoordPointer(2, GL_FLOAT, kQuadSize, (void*)(offset + diff)); | ||
626 | |||
627 | glDrawArrays(GL_TRIANGLE_STRIP, 0, 4); | ||
628 | |||
629 | if( newBlend ) | ||
630 | glBlendFunc(CC_BLEND_SRC, CC_BLEND_DST); | ||
631 | |||
632 | #if CC_SPRITE_DEBUG_DRAW == 1 | ||
633 | // draw bounding box | ||
634 | CGSize s = self.contentSize; | ||
635 | CGPoint vertices[4] = { | ||
636 | ccp(0,0), ccp(s.width,0), | ||
637 | ccp(s.width,s.height), ccp(0,s.height) | ||
638 | }; | ||
639 | ccDrawPoly(vertices, 4, YES); | ||
640 | #elif CC_SPRITE_DEBUG_DRAW == 2 | ||
641 | // draw texture box | ||
642 | CGSize s = self.textureRect.size; | ||
643 | CGPoint offsetPix = self.offsetPositionInPixels; | ||
644 | CGPoint vertices[4] = { | ||
645 | ccp(offsetPix.x,offsetPix.y), ccp(offsetPix.x+s.width,offsetPix.y), | ||
646 | ccp(offsetPix.x+s.width,offsetPix.y+s.height), ccp(offsetPix.x,offsetPix.y+s.height) | ||
647 | }; | ||
648 | ccDrawPoly(vertices, 4, YES); | ||
649 | #endif // CC_SPRITE_DEBUG_DRAW | ||
650 | |||
651 | } | ||
652 | |||
653 | #pragma mark CCSprite - CCNode overrides | ||
654 | |||
655 | -(void) addChild:(CCSprite*)child z:(NSInteger)z tag:(NSInteger) aTag | ||
656 | { | ||
657 | NSAssert( child != nil, @"Argument must be non-nil"); | ||
658 | |||
659 | [super addChild:child z:z tag:aTag]; | ||
660 | |||
661 | if( usesBatchNode_ ) { | ||
662 | NSAssert( [child isKindOfClass:[CCSprite class]], @"CCSprite only supports CCSprites as children when using CCSpriteBatchNode"); | ||
663 | NSAssert( child.texture.name == textureAtlas_.texture.name, @"CCSprite is not using the same texture id"); | ||
664 | |||
665 | NSUInteger index = [batchNode_ atlasIndexForChild:child atZ:z]; | ||
666 | [batchNode_ insertChild:child inAtlasAtIndex:index]; | ||
667 | } | ||
668 | |||
669 | hasChildren_ = YES; | ||
670 | } | ||
671 | |||
672 | -(void) reorderChild:(CCSprite*)child z:(NSInteger)z | ||
673 | { | ||
674 | NSAssert( child != nil, @"Child must be non-nil"); | ||
675 | NSAssert( [children_ containsObject:child], @"Child doesn't belong to Sprite" ); | ||
676 | |||
677 | if( z == child.zOrder ) | ||
678 | return; | ||
679 | |||
680 | if( usesBatchNode_ ) { | ||
681 | // XXX: Instead of removing/adding, it is more efficient to reorder manually | ||
682 | [child retain]; | ||
683 | [self removeChild:child cleanup:NO]; | ||
684 | [self addChild:child z:z]; | ||
685 | [child release]; | ||
686 | } | ||
687 | |||
688 | else | ||
689 | [super reorderChild:child z:z]; | ||
690 | } | ||
691 | |||
692 | -(void)removeChild: (CCSprite *)sprite cleanup:(BOOL)doCleanup | ||
693 | { | ||
694 | if( usesBatchNode_ ) | ||
695 | [batchNode_ removeSpriteFromAtlas:sprite]; | ||
696 | |||
697 | [super removeChild:sprite cleanup:doCleanup]; | ||
698 | |||
699 | hasChildren_ = ( [children_ count] > 0 ); | ||
700 | } | ||
701 | |||
702 | -(void)removeAllChildrenWithCleanup:(BOOL)doCleanup | ||
703 | { | ||
704 | if( usesBatchNode_ ) { | ||
705 | CCSprite *child; | ||
706 | CCARRAY_FOREACH(children_, child) | ||
707 | [batchNode_ removeSpriteFromAtlas:child]; | ||
708 | } | ||
709 | |||
710 | [super removeAllChildrenWithCleanup:doCleanup]; | ||
711 | |||
712 | hasChildren_ = NO; | ||
713 | } | ||
714 | |||
715 | // | ||
716 | // CCNode property overloads | ||
717 | // used only when parent is CCSpriteBatchNode | ||
718 | // | ||
719 | #pragma mark CCSprite - property overloads | ||
720 | |||
721 | |||
722 | -(void) setDirtyRecursively:(BOOL)b | ||
723 | { | ||
724 | dirty_ = recursiveDirty_ = b; | ||
725 | // recursively set dirty | ||
726 | if( hasChildren_ ) { | ||
727 | CCSprite *child; | ||
728 | CCARRAY_FOREACH(children_, child) | ||
729 | [child setDirtyRecursively:YES]; | ||
730 | } | ||
731 | } | ||
732 | |||
733 | // XXX HACK: optimization | ||
734 | #define SET_DIRTY_RECURSIVELY() { \ | ||
735 | if( usesBatchNode_ && ! recursiveDirty_ ) { \ | ||
736 | dirty_ = recursiveDirty_ = YES; \ | ||
737 | if( hasChildren_) \ | ||
738 | [self setDirtyRecursively:YES]; \ | ||
739 | } \ | ||
740 | } | ||
741 | |||
742 | -(void)setPosition:(CGPoint)pos | ||
743 | { | ||
744 | [super setPosition:pos]; | ||
745 | SET_DIRTY_RECURSIVELY(); | ||
746 | } | ||
747 | |||
748 | -(void)setPositionInPixels:(CGPoint)pos | ||
749 | { | ||
750 | [super setPositionInPixels:pos]; | ||
751 | SET_DIRTY_RECURSIVELY(); | ||
752 | } | ||
753 | |||
754 | -(void)setRotation:(float)rot | ||
755 | { | ||
756 | [super setRotation:rot]; | ||
757 | SET_DIRTY_RECURSIVELY(); | ||
758 | } | ||
759 | |||
760 | -(void)setSkewX:(float)sx | ||
761 | { | ||
762 | [super setSkewX:sx]; | ||
763 | SET_DIRTY_RECURSIVELY(); | ||
764 | } | ||
765 | |||
766 | -(void)setSkewY:(float)sy | ||
767 | { | ||
768 | [super setSkewY:sy]; | ||
769 | SET_DIRTY_RECURSIVELY(); | ||
770 | } | ||
771 | |||
772 | -(void)setScaleX:(float) sx | ||
773 | { | ||
774 | [super setScaleX:sx]; | ||
775 | SET_DIRTY_RECURSIVELY(); | ||
776 | } | ||
777 | |||
778 | -(void)setScaleY:(float) sy | ||
779 | { | ||
780 | [super setScaleY:sy]; | ||
781 | SET_DIRTY_RECURSIVELY(); | ||
782 | } | ||
783 | |||
784 | -(void)setScale:(float) s | ||
785 | { | ||
786 | [super setScale:s]; | ||
787 | SET_DIRTY_RECURSIVELY(); | ||
788 | } | ||
789 | |||
790 | -(void) setVertexZ:(float)z | ||
791 | { | ||
792 | [super setVertexZ:z]; | ||
793 | SET_DIRTY_RECURSIVELY(); | ||
794 | } | ||
795 | |||
796 | -(void)setAnchorPoint:(CGPoint)anchor | ||
797 | { | ||
798 | [super setAnchorPoint:anchor]; | ||
799 | SET_DIRTY_RECURSIVELY(); | ||
800 | } | ||
801 | |||
802 | -(void)setIsRelativeAnchorPoint:(BOOL)relative | ||
803 | { | ||
804 | NSAssert( ! usesBatchNode_, @"relativeTransformAnchor is invalid in CCSprite"); | ||
805 | [super setIsRelativeAnchorPoint:relative]; | ||
806 | } | ||
807 | |||
808 | -(void)setVisible:(BOOL)v | ||
809 | { | ||
810 | [super setVisible:v]; | ||
811 | SET_DIRTY_RECURSIVELY(); | ||
812 | } | ||
813 | |||
814 | -(void)setFlipX:(BOOL)b | ||
815 | { | ||
816 | if( flipX_ != b ) { | ||
817 | flipX_ = b; | ||
818 | [self setTextureRectInPixels:rectInPixels_ rotated:rectRotated_ untrimmedSize:contentSizeInPixels_]; | ||
819 | } | ||
820 | } | ||
821 | -(BOOL) flipX | ||
822 | { | ||
823 | return flipX_; | ||
824 | } | ||
825 | |||
826 | -(void) setFlipY:(BOOL)b | ||
827 | { | ||
828 | if( flipY_ != b ) { | ||
829 | flipY_ = b; | ||
830 | [self setTextureRectInPixels:rectInPixels_ rotated:rectRotated_ untrimmedSize:contentSizeInPixels_]; | ||
831 | } | ||
832 | } | ||
833 | -(BOOL) flipY | ||
834 | { | ||
835 | return flipY_; | ||
836 | } | ||
837 | |||
838 | // | ||
839 | // RGBA protocol | ||
840 | // | ||
841 | #pragma mark CCSprite - RGBA protocol | ||
842 | -(void) updateColor | ||
843 | { | ||
844 | ccColor4B color4 = {color_.r, color_.g, color_.b, opacity_ }; | ||
845 | |||
846 | quad_.bl.colors = color4; | ||
847 | quad_.br.colors = color4; | ||
848 | quad_.tl.colors = color4; | ||
849 | quad_.tr.colors = color4; | ||
850 | |||
851 | // renders using Sprite Manager | ||
852 | if( usesBatchNode_ ) { | ||
853 | if( atlasIndex_ != CCSpriteIndexNotInitialized) | ||
854 | [textureAtlas_ updateQuad:&quad_ atIndex:atlasIndex_]; | ||
855 | else | ||
856 | // no need to set it recursively | ||
857 | // update dirty_, don't update recursiveDirty_ | ||
858 | dirty_ = YES; | ||
859 | } | ||
860 | // self render | ||
861 | // do nothing | ||
862 | } | ||
863 | |||
864 | -(GLubyte) opacity | ||
865 | { | ||
866 | return opacity_; | ||
867 | } | ||
868 | |||
869 | -(void) setOpacity:(GLubyte) anOpacity | ||
870 | { | ||
871 | opacity_ = anOpacity; | ||
872 | |||
873 | // special opacity for premultiplied textures | ||
874 | if( opacityModifyRGB_ ) | ||
875 | [self setColor: colorUnmodified_]; | ||
876 | |||
877 | [self updateColor]; | ||
878 | } | ||
879 | |||
880 | - (ccColor3B) color | ||
881 | { | ||
882 | if(opacityModifyRGB_) | ||
883 | return colorUnmodified_; | ||
884 | |||
885 | return color_; | ||
886 | } | ||
887 | |||
888 | -(void) setColor:(ccColor3B)color3 | ||
889 | { | ||
890 | color_ = colorUnmodified_ = color3; | ||
891 | |||
892 | if( opacityModifyRGB_ ){ | ||
893 | color_.r = color3.r * opacity_/255; | ||
894 | color_.g = color3.g * opacity_/255; | ||
895 | color_.b = color3.b * opacity_/255; | ||
896 | } | ||
897 | |||
898 | [self updateColor]; | ||
899 | } | ||
900 | |||
901 | -(void) setOpacityModifyRGB:(BOOL)modify | ||
902 | { | ||
903 | ccColor3B oldColor = self.color; | ||
904 | opacityModifyRGB_ = modify; | ||
905 | self.color = oldColor; | ||
906 | } | ||
907 | |||
908 | -(BOOL) doesOpacityModifyRGB | ||
909 | { | ||
910 | return opacityModifyRGB_; | ||
911 | } | ||
912 | |||
913 | // | ||
914 | // Frames | ||
915 | // | ||
916 | #pragma mark CCSprite - Frames | ||
917 | |||
918 | -(void) setDisplayFrame:(CCSpriteFrame*)frame | ||
919 | { | ||
920 | unflippedOffsetPositionFromCenter_ = frame.offsetInPixels; | ||
921 | |||
922 | CCTexture2D *newTexture = [frame texture]; | ||
923 | // update texture before updating texture rect | ||
924 | if ( newTexture.name != texture_.name ) | ||
925 | [self setTexture: newTexture]; | ||
926 | |||
927 | // update rect | ||
928 | rectRotated_ = frame.rotated; | ||
929 | [self setTextureRectInPixels:frame.rectInPixels rotated:frame.rotated untrimmedSize:frame.originalSizeInPixels]; | ||
930 | } | ||
931 | |||
932 | // XXX deprecated | ||
933 | -(void) setDisplayFrame: (NSString*) animationName index:(int) frameIndex | ||
934 | { | ||
935 | if( ! animations_ ) | ||
936 | [self initAnimationDictionary]; | ||
937 | |||
938 | CCAnimation *a = [animations_ objectForKey: animationName]; | ||
939 | CCSpriteFrame *frame = [[a frames] objectAtIndex:frameIndex]; | ||
940 | |||
941 | NSAssert( frame, @"CCSprite#setDisplayFrame. Invalid frame"); | ||
942 | |||
943 | [self setDisplayFrame:frame]; | ||
944 | } | ||
945 | |||
946 | -(void) setDisplayFrameWithAnimationName: (NSString*) animationName index:(int) frameIndex | ||
947 | { | ||
948 | NSAssert( animationName, @"CCSprite#setDisplayFrameWithAnimationName. animationName must not be nil"); | ||
949 | |||
950 | CCAnimation *a = [[CCAnimationCache sharedAnimationCache] animationByName:animationName]; | ||
951 | |||
952 | NSAssert( a, @"CCSprite#setDisplayFrameWithAnimationName: Frame not found"); | ||
953 | |||
954 | CCSpriteFrame *frame = [[a frames] objectAtIndex:frameIndex]; | ||
955 | |||
956 | NSAssert( frame, @"CCSprite#setDisplayFrame. Invalid frame"); | ||
957 | |||
958 | [self setDisplayFrame:frame]; | ||
959 | } | ||
960 | |||
961 | |||
962 | -(BOOL) isFrameDisplayed:(CCSpriteFrame*)frame | ||
963 | { | ||
964 | CGRect r = [frame rect]; | ||
965 | return ( CGRectEqualToRect(r, rect_) && | ||
966 | frame.texture.name == self.texture.name ); | ||
967 | } | ||
968 | |||
969 | -(CCSpriteFrame*) displayedFrame | ||
970 | { | ||
971 | return [CCSpriteFrame frameWithTexture:texture_ | ||
972 | rectInPixels:rectInPixels_ | ||
973 | rotated:rectRotated_ | ||
974 | offset:unflippedOffsetPositionFromCenter_ | ||
975 | originalSize:contentSizeInPixels_]; | ||
976 | } | ||
977 | |||
978 | -(void) addAnimation: (CCAnimation*) anim | ||
979 | { | ||
980 | // lazy alloc | ||
981 | if( ! animations_ ) | ||
982 | [self initAnimationDictionary]; | ||
983 | |||
984 | [animations_ setObject:anim forKey:[anim name]]; | ||
985 | } | ||
986 | |||
987 | -(CCAnimation*)animationByName: (NSString*) animationName | ||
988 | { | ||
989 | NSAssert( animationName != nil, @"animationName parameter must be non nil"); | ||
990 | return [animations_ objectForKey:animationName]; | ||
991 | } | ||
992 | |||
993 | #pragma mark CCSprite - CocosNodeTexture protocol | ||
994 | |||
995 | -(void) updateBlendFunc | ||
996 | { | ||
997 | NSAssert( ! usesBatchNode_, @"CCSprite: updateBlendFunc doesn't work when the sprite is rendered using a CCSpriteBatchNode"); | ||
998 | |||
999 | // it's possible to have an untextured sprite | ||
1000 | if( !texture_ || ! [texture_ hasPremultipliedAlpha] ) { | ||
1001 | blendFunc_.src = GL_SRC_ALPHA; | ||
1002 | blendFunc_.dst = GL_ONE_MINUS_SRC_ALPHA; | ||
1003 | [self setOpacityModifyRGB:NO]; | ||
1004 | } else { | ||
1005 | blendFunc_.src = CC_BLEND_SRC; | ||
1006 | blendFunc_.dst = CC_BLEND_DST; | ||
1007 | [self setOpacityModifyRGB:YES]; | ||
1008 | } | ||
1009 | } | ||
1010 | |||
1011 | -(void) setTexture:(CCTexture2D*)texture | ||
1012 | { | ||
1013 | NSAssert( ! usesBatchNode_, @"CCSprite: setTexture doesn't work when the sprite is rendered using a CCSpriteBatchNode"); | ||
1014 | |||
1015 | // accept texture==nil as argument | ||
1016 | NSAssert( !texture || [texture isKindOfClass:[CCTexture2D class]], @"setTexture expects a CCTexture2D. Invalid argument"); | ||
1017 | |||
1018 | [texture_ release]; | ||
1019 | texture_ = [texture retain]; | ||
1020 | |||
1021 | [self updateBlendFunc]; | ||
1022 | } | ||
1023 | |||
1024 | -(CCTexture2D*) texture | ||
1025 | { | ||
1026 | return texture_; | ||
1027 | } | ||
1028 | |||
1029 | @end | ||