PageRenderTime 38ms CodeModel.GetById 11ms RepoModel.GetById 0ms app.codeStats 0ms

/Smooth Drawing/libs/cocos2d/CCSpriteBatchNode.m

https://gitlab.com/Mr.Tomato/smooth-drawing
Objective C | 663 lines | 422 code | 141 blank | 100 comment | 71 complexity | 342eeb3bae45d3d857c6d4c080626674 MD5 | raw file
  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. #import "ccConfig.h"
  29. #import "CCSprite.h"
  30. #import "CCSpriteBatchNode.h"
  31. #import "CCGrid.h"
  32. #import "CCDrawingPrimitives.h"
  33. #import "CCTextureCache.h"
  34. #import "CCShaderCache.h"
  35. #import "CCGLProgram.h"
  36. #import "ccGLStateCache.h"
  37. #import "CCDirector.h"
  38. #import "Support/CGPointExtension.h"
  39. #import "Support/TransformUtils.h"
  40. #import "Support/CCProfiling.h"
  41. // external
  42. #import "kazmath/GL/matrix.h"
  43. const NSUInteger defaultCapacity = 29;
  44. #pragma mark -
  45. #pragma mark CCSpriteBatchNode
  46. @interface CCSpriteBatchNode (private)
  47. -(void) updateAtlasIndex:(CCSprite*) sprite currentIndex:(NSInteger*) curIndex;
  48. -(void) swap:(NSInteger) oldIndex withNewIndex:(NSInteger) newIndex;
  49. -(void) updateBlendFunc;
  50. @end
  51. @implementation CCSpriteBatchNode
  52. @synthesize textureAtlas = textureAtlas_;
  53. @synthesize blendFunc = blendFunc_;
  54. @synthesize descendants = descendants_;
  55. /*
  56. * creation with CCTexture2D
  57. */
  58. +(id)batchNodeWithTexture:(CCTexture2D *)tex
  59. {
  60. return [[[self alloc] initWithTexture:tex capacity:defaultCapacity] autorelease];
  61. }
  62. +(id)batchNodeWithTexture:(CCTexture2D *)tex capacity:(NSUInteger)capacity
  63. {
  64. return [[[self alloc] initWithTexture:tex capacity:capacity] autorelease];
  65. }
  66. /*
  67. * creation with File Image
  68. */
  69. +(id)batchNodeWithFile:(NSString*)fileImage capacity:(NSUInteger)capacity
  70. {
  71. return [[[self alloc] initWithFile:fileImage capacity:capacity] autorelease];
  72. }
  73. +(id)batchNodeWithFile:(NSString*) imageFile
  74. {
  75. return [[[self alloc] initWithFile:imageFile capacity:defaultCapacity] autorelease];
  76. }
  77. /*
  78. * init with CCTexture2D
  79. */
  80. -(id)initWithTexture:(CCTexture2D *)tex capacity:(NSUInteger)capacity
  81. {
  82. if( (self=[super init])) {
  83. blendFunc_.src = CC_BLEND_SRC;
  84. blendFunc_.dst = CC_BLEND_DST;
  85. textureAtlas_ = [[CCTextureAtlas alloc] initWithTexture:tex capacity:capacity];
  86. [self updateBlendFunc];
  87. // no lazy alloc in this node
  88. children_ = [[CCArray alloc] initWithCapacity:capacity];
  89. descendants_ = [[CCArray alloc] initWithCapacity:capacity];
  90. self.shaderProgram = [[CCShaderCache sharedShaderCache] programForKey:kCCShader_PositionTextureColor];
  91. }
  92. return self;
  93. }
  94. /*
  95. * init with FileImage
  96. */
  97. -(id)initWithFile:(NSString *)fileImage capacity:(NSUInteger)capacity
  98. {
  99. CCTexture2D *tex = [[CCTextureCache sharedTextureCache] addImage:fileImage];
  100. return [self initWithTexture:tex capacity:capacity];
  101. }
  102. - (NSString*) description
  103. {
  104. return [NSString stringWithFormat:@"<%@ = %08X | Tag = %i>", [self class], self, tag_ ];
  105. }
  106. -(void)dealloc
  107. {
  108. [textureAtlas_ release];
  109. [descendants_ release];
  110. [super dealloc];
  111. }
  112. #pragma mark CCSpriteBatchNode - composition
  113. // override visit.
  114. // Don't call visit on its children
  115. -(void) visit
  116. {
  117. CC_PROFILER_START_CATEGORY(kCCProfilerCategoryBatchSprite, @"CCSpriteBatchNode - visit");
  118. NSAssert(parent_ != nil, @"CCSpriteBatchNode should NOT be root node");
  119. // CAREFUL:
  120. // This visit is almost identical to CCNode#visit
  121. // with the exception that it doesn't call visit on its children
  122. //
  123. // The alternative is to have a void CCSprite#visit, but
  124. // although this is less mantainable, is faster
  125. //
  126. if (!visible_)
  127. return;
  128. kmGLPushMatrix();
  129. if ( grid_ && grid_.active) {
  130. [grid_ beforeDraw];
  131. [self transformAncestors];
  132. }
  133. [self sortAllChildren];
  134. [self transform];
  135. [self draw];
  136. if ( grid_ && grid_.active)
  137. [grid_ afterDraw:self];
  138. kmGLPopMatrix();
  139. orderOfArrival_ = 0;
  140. CC_PROFILER_STOP_CATEGORY(kCCProfilerCategoryBatchSprite, @"CCSpriteBatchNode - visit");
  141. }
  142. // override addChild:
  143. -(void) addChild:(CCSprite*)child z:(NSInteger)z tag:(NSInteger) aTag
  144. {
  145. NSAssert( child != nil, @"Argument must be non-nil");
  146. NSAssert( [child isKindOfClass:[CCSprite class]], @"CCSpriteBatchNode only supports CCSprites as children");
  147. NSAssert( child.texture.name == textureAtlas_.texture.name, @"CCSprite is not using the same texture id");
  148. [super addChild:child z:z tag:aTag];
  149. [self appendChild:child];
  150. }
  151. // override reorderChild
  152. -(void) reorderChild:(CCSprite*)child z:(NSInteger)z
  153. {
  154. NSAssert( child != nil, @"Child must be non-nil");
  155. NSAssert( [children_ containsObject:child], @"Child doesn't belong to Sprite" );
  156. if( z == child.zOrder )
  157. return;
  158. //set the z-order and sort later
  159. [super reorderChild:child z:z];
  160. }
  161. // override removeChild:
  162. -(void)removeChild: (CCSprite *)sprite cleanup:(BOOL)doCleanup
  163. {
  164. // explicit nil handling
  165. if (sprite == nil)
  166. return;
  167. NSAssert([children_ containsObject:sprite], @"CCSpriteBatchNode doesn't contain the sprite. Can't remove it");
  168. // cleanup before removing
  169. [self removeSpriteFromAtlas:sprite];
  170. [super removeChild:sprite cleanup:doCleanup];
  171. }
  172. -(void)removeChildAtIndex:(NSUInteger)index cleanup:(BOOL)doCleanup
  173. {
  174. [self removeChild:(CCSprite *)[children_ objectAtIndex:index] cleanup:doCleanup];
  175. }
  176. -(void)removeAllChildrenWithCleanup:(BOOL)doCleanup
  177. {
  178. // Invalidate atlas index. issue #569
  179. // useSelfRender should be performed on all descendants. issue #1216
  180. [descendants_ makeObjectsPerformSelector:@selector(setBatchNode:) withObject:nil];
  181. [super removeAllChildrenWithCleanup:doCleanup];
  182. [descendants_ removeAllObjects];
  183. [textureAtlas_ removeAllQuads];
  184. }
  185. //override sortAllChildren
  186. - (void) sortAllChildren
  187. {
  188. if (isReorderChildDirty_)
  189. {
  190. NSInteger i,j,length = children_->data->num;
  191. CCNode ** x = children_->data->arr;
  192. CCNode *tempItem;
  193. CCSprite *child;
  194. //insertion sort
  195. for(i=1; i<length; i++)
  196. {
  197. tempItem = x[i];
  198. j = i-1;
  199. //continue moving element downwards while zOrder is smaller or when zOrder is the same but orderOfArrival is smaller
  200. while(j>=0 && ( tempItem.zOrder < x[j].zOrder || ( tempItem.zOrder == x[j].zOrder && tempItem.orderOfArrival < x[j].orderOfArrival ) ) )
  201. {
  202. x[j+1] = x[j];
  203. j--;
  204. }
  205. x[j+1] = tempItem;
  206. }
  207. //sorted now check all children
  208. if ([children_ count] > 0)
  209. {
  210. //first sort all children recursively based on zOrder
  211. [children_ makeObjectsPerformSelector:@selector(sortAllChildren)];
  212. NSInteger index=0;
  213. //fast dispatch, give every child a new atlasIndex based on their relative zOrder (keep parent -> child relations intact)
  214. // and at the same time reorder descedants and the quads to the right index
  215. CCARRAY_FOREACH(children_, child)
  216. [self updateAtlasIndex:child currentIndex:&index];
  217. }
  218. isReorderChildDirty_=NO;
  219. }
  220. }
  221. -(void) updateAtlasIndex:(CCSprite*) sprite currentIndex:(NSInteger*) curIndex
  222. {
  223. CCArray *array = [sprite children];
  224. NSUInteger count = [array count];
  225. NSInteger oldIndex;
  226. if( count == 0 )
  227. {
  228. oldIndex = sprite.atlasIndex;
  229. sprite.atlasIndex = *curIndex;
  230. sprite.orderOfArrival = 0;
  231. if (oldIndex != *curIndex)
  232. [self swap:oldIndex withNewIndex:*curIndex];
  233. (*curIndex)++;
  234. }
  235. else
  236. {
  237. BOOL needNewIndex=YES;
  238. if (((CCSprite*) (array->data->arr[0])).zOrder >= 0)
  239. {
  240. //all children are in front of the parent
  241. oldIndex = sprite.atlasIndex;
  242. sprite.atlasIndex = *curIndex;
  243. sprite.orderOfArrival = 0;
  244. if (oldIndex != *curIndex)
  245. [self swap:oldIndex withNewIndex:*curIndex];
  246. (*curIndex)++;
  247. needNewIndex = NO;
  248. }
  249. CCSprite* child;
  250. CCARRAY_FOREACH(array,child)
  251. {
  252. if (needNewIndex && child.zOrder >= 0)
  253. {
  254. oldIndex = sprite.atlasIndex;
  255. sprite.atlasIndex = *curIndex;
  256. sprite.orderOfArrival = 0;
  257. if (oldIndex != *curIndex)
  258. [self swap:oldIndex withNewIndex:*curIndex];
  259. (*curIndex)++;
  260. needNewIndex = NO;
  261. }
  262. [self updateAtlasIndex:child currentIndex:curIndex];
  263. }
  264. if (needNewIndex)
  265. {//all children have a zOrder < 0)
  266. oldIndex=sprite.atlasIndex;
  267. sprite.atlasIndex=*curIndex;
  268. sprite.orderOfArrival=0;
  269. if (oldIndex!=*curIndex)
  270. [self swap:oldIndex withNewIndex:*curIndex];
  271. (*curIndex)++;
  272. }
  273. }
  274. }
  275. - (void) swap:(NSInteger) oldIndex withNewIndex:(NSInteger) newIndex
  276. {
  277. id* x = descendants_->data->arr;
  278. ccV3F_C4B_T2F_Quad* quads = textureAtlas_.quads;
  279. id tempItem = x[oldIndex];
  280. ccV3F_C4B_T2F_Quad tempItemQuad=quads[oldIndex];
  281. //update the index of other swapped item
  282. ((CCSprite*) x[newIndex]).atlasIndex=oldIndex;
  283. x[oldIndex]=x[newIndex];
  284. quads[oldIndex]=quads[newIndex];
  285. x[newIndex]=tempItem;
  286. quads[newIndex]=tempItemQuad;
  287. }
  288. - (void) reorderBatch:(BOOL) reorder
  289. {
  290. isReorderChildDirty_=reorder;
  291. }
  292. #pragma mark CCSpriteBatchNode - draw
  293. -(void) draw
  294. {
  295. CC_PROFILER_START(@"CCSpriteBatchNode - draw");
  296. // Optimization: Fast Dispatch
  297. if( textureAtlas_.totalQuads == 0 )
  298. return;
  299. CC_NODE_DRAW_SETUP();
  300. [children_ makeObjectsPerformSelector:@selector(updateTransform)];
  301. ccGLBlendFunc( blendFunc_.src, blendFunc_.dst );
  302. [textureAtlas_ drawQuads];
  303. CC_PROFILER_STOP(@"CCSpriteBatchNode - draw");
  304. }
  305. #pragma mark CCSpriteBatchNode - private
  306. -(void) increaseAtlasCapacity
  307. {
  308. // if we're going beyond the current CCTextureAtlas's capacity,
  309. // all the previously initialized sprites will need to redo their texture coords
  310. // this is likely computationally expensive
  311. NSUInteger quantity = (textureAtlas_.capacity + 1) * 4 / 3;
  312. CCLOG(@"cocos2d: CCSpriteBatchNode: resizing TextureAtlas capacity from [%lu] to [%lu].",
  313. (long)textureAtlas_.capacity,
  314. (long)quantity);
  315. if( ! [textureAtlas_ resizeCapacity:quantity] ) {
  316. // serious problems
  317. CCLOG(@"cocos2d: WARNING: Not enough memory to resize the atlas");
  318. NSAssert(NO,@"XXX: CCSpriteBatchNode#increaseAtlasCapacity SHALL handle this assert");
  319. }
  320. }
  321. #pragma mark CCSpriteBatchNode - Atlas Index Stuff
  322. -(NSUInteger) rebuildIndexInOrder:(CCSprite*)node atlasIndex:(NSUInteger)index
  323. {
  324. CCSprite *sprite;
  325. CCARRAY_FOREACH(node.children, sprite){
  326. if( sprite.zOrder < 0 )
  327. index = [self rebuildIndexInOrder:sprite atlasIndex:index];
  328. }
  329. // ignore self (batch node)
  330. if( ! [node isEqual:self]) {
  331. node.atlasIndex = index;
  332. index++;
  333. }
  334. CCARRAY_FOREACH(node.children, sprite){
  335. if( sprite.zOrder >= 0 )
  336. index = [self rebuildIndexInOrder:sprite atlasIndex:index];
  337. }
  338. return index;
  339. }
  340. -(NSUInteger) highestAtlasIndexInChild:(CCSprite*)sprite
  341. {
  342. CCArray *array = [sprite children];
  343. NSUInteger count = [array count];
  344. if( count == 0 )
  345. return sprite.atlasIndex;
  346. else
  347. return [self highestAtlasIndexInChild:[array lastObject]];
  348. }
  349. -(NSUInteger) lowestAtlasIndexInChild:(CCSprite*)sprite
  350. {
  351. CCArray *array = [sprite children];
  352. NSUInteger count = [array count];
  353. if( count == 0 )
  354. return sprite.atlasIndex;
  355. else
  356. return [self lowestAtlasIndexInChild:[array objectAtIndex:0] ];
  357. }
  358. -(NSUInteger)atlasIndexForChild:(CCSprite*)sprite atZ:(NSInteger)z
  359. {
  360. CCArray *brothers = [[sprite parent] children];
  361. NSUInteger childIndex = [brothers indexOfObject:sprite];
  362. // ignore parent Z if parent is batchnode
  363. BOOL ignoreParent = ( sprite.parent == self );
  364. CCSprite *previous = nil;
  365. if( childIndex > 0 )
  366. previous = [brothers objectAtIndex:childIndex-1];
  367. // first child of the sprite sheet
  368. if( ignoreParent ) {
  369. if( childIndex == 0 )
  370. return 0;
  371. // else
  372. return [self highestAtlasIndexInChild: previous] + 1;
  373. }
  374. // parent is a CCSprite, so, it must be taken into account
  375. // first child of an CCSprite ?
  376. if( childIndex == 0 )
  377. {
  378. CCSprite *p = (CCSprite*) sprite.parent;
  379. // less than parent and brothers
  380. if( z < 0 )
  381. return p.atlasIndex;
  382. else
  383. return p.atlasIndex+1;
  384. } else {
  385. // previous & sprite belong to the same branch
  386. if( ( previous.zOrder < 0 && z < 0 )|| (previous.zOrder >= 0 && z >= 0) )
  387. return [self highestAtlasIndexInChild:previous] + 1;
  388. // else (previous < 0 and sprite >= 0 )
  389. CCSprite *p = (CCSprite*) sprite.parent;
  390. return p.atlasIndex + 1;
  391. }
  392. NSAssert( NO, @"Should not happen. Error calculating Z on Batch Node");
  393. return 0;
  394. }
  395. #pragma mark CCSpriteBatchNode - add / remove / reorder helper methods
  396. // add child helper
  397. -(void) insertChild:(CCSprite*)sprite inAtlasAtIndex:(NSUInteger)index
  398. {
  399. [sprite setBatchNode:self];
  400. [sprite setAtlasIndex:index];
  401. [sprite setDirty: YES];
  402. if(textureAtlas_.totalQuads == textureAtlas_.capacity)
  403. [self increaseAtlasCapacity];
  404. ccV3F_C4B_T2F_Quad quad = [sprite quad];
  405. [textureAtlas_ insertQuad:&quad atIndex:index];
  406. ccArray *descendantsData = descendants_->data;
  407. ccArrayInsertObjectAtIndex(descendantsData, sprite, index);
  408. // update indices
  409. NSUInteger i = index+1;
  410. CCSprite *child;
  411. for(; i<descendantsData->num; i++){
  412. child = descendantsData->arr[i];
  413. child.atlasIndex = child.atlasIndex + 1;
  414. }
  415. // add children recursively
  416. CCARRAY_FOREACH(sprite.children, child){
  417. NSUInteger idx = [self atlasIndexForChild:child atZ: child.zOrder];
  418. [self insertChild:child inAtlasAtIndex:idx];
  419. }
  420. }
  421. // addChild helper, faster than insertChild
  422. -(void) appendChild:(CCSprite*)sprite
  423. {
  424. isReorderChildDirty_=YES;
  425. [sprite setBatchNode:self];
  426. [sprite setDirty: YES];
  427. if(textureAtlas_.totalQuads == textureAtlas_.capacity)
  428. [self increaseAtlasCapacity];
  429. ccArray *descendantsData = descendants_->data;
  430. ccArrayAppendObjectWithResize(descendantsData, sprite);
  431. NSUInteger index=descendantsData->num-1;
  432. sprite.atlasIndex=index;
  433. ccV3F_C4B_T2F_Quad quad = [sprite quad];
  434. [textureAtlas_ insertQuad:&quad atIndex:index];
  435. // add children recursively
  436. CCSprite* child;
  437. CCARRAY_FOREACH(sprite.children, child)
  438. [self appendChild:child];
  439. }
  440. // remove child helper
  441. -(void) removeSpriteFromAtlas:(CCSprite*)sprite
  442. {
  443. // remove from TextureAtlas
  444. [textureAtlas_ removeQuadAtIndex:sprite.atlasIndex];
  445. // Cleanup sprite. It might be reused (issue #569)
  446. [sprite setBatchNode:nil];
  447. ccArray *descendantsData = descendants_->data;
  448. NSUInteger index = ccArrayGetIndexOfObject(descendantsData, sprite);
  449. if( index != NSNotFound ) {
  450. ccArrayRemoveObjectAtIndex(descendantsData, index);
  451. // update all sprites beyond this one
  452. NSUInteger count = descendantsData->num;
  453. for(; index < count; index++)
  454. {
  455. CCSprite *s = descendantsData->arr[index];
  456. s.atlasIndex = s.atlasIndex - 1;
  457. }
  458. }
  459. // remove children recursively
  460. CCSprite *child;
  461. CCARRAY_FOREACH(sprite.children, child)
  462. [self removeSpriteFromAtlas:child];
  463. }
  464. #pragma mark CCSpriteBatchNode - CocosNodeTexture protocol
  465. -(void) updateBlendFunc
  466. {
  467. if( ! [textureAtlas_.texture hasPremultipliedAlpha] ) {
  468. blendFunc_.src = GL_SRC_ALPHA;
  469. blendFunc_.dst = GL_ONE_MINUS_SRC_ALPHA;
  470. }
  471. }
  472. -(void) setTexture:(CCTexture2D*)texture
  473. {
  474. textureAtlas_.texture = texture;
  475. [self updateBlendFunc];
  476. }
  477. -(CCTexture2D*) texture
  478. {
  479. return textureAtlas_.texture;
  480. }
  481. @end
  482. #pragma mark - CCSpriteBatchNode Extension
  483. @implementation CCSpriteBatchNode (QuadExtension)
  484. -(void) addQuadFromSprite:(CCSprite*)sprite quadIndex:(NSUInteger)index
  485. {
  486. NSAssert( sprite != nil, @"Argument must be non-nil");
  487. NSAssert( [sprite isKindOfClass:[CCSprite class]], @"CCSpriteBatchNode only supports CCSprites as children");
  488. while(index >= textureAtlas_.capacity || textureAtlas_.capacity == textureAtlas_.totalQuads )
  489. [self increaseAtlasCapacity];
  490. //
  491. // update the quad directly. Don't add the sprite to the scene graph
  492. //
  493. [sprite setBatchNode:self];
  494. [sprite setAtlasIndex:index];
  495. ccV3F_C4B_T2F_Quad quad = [sprite quad];
  496. [textureAtlas_ insertQuad:&quad atIndex:index];
  497. // XXX: updateTransform will update the textureAtlas too using updateQuad.
  498. // XXX: so, it should be AFTER the insertQuad
  499. [sprite setDirty:YES];
  500. [sprite updateTransform];
  501. }
  502. -(id) addSpriteWithoutQuad:(CCSprite*)child z:(NSUInteger)z tag:(NSInteger)aTag
  503. {
  504. NSAssert( child != nil, @"Argument must be non-nil");
  505. NSAssert( [child isKindOfClass:[CCSprite class]], @"CCSpriteBatchNode only supports CCSprites as children");
  506. // quad index is Z
  507. [child setAtlasIndex:z];
  508. // XXX: optimize with a binary search
  509. int i=0;
  510. for( CCSprite *c in descendants_ ) {
  511. if( c.atlasIndex >= z )
  512. break;
  513. i++;
  514. }
  515. [descendants_ insertObject:child atIndex:i];
  516. // IMPORTANT: Call super, and not self. Avoid adding it to the texture atlas array
  517. [super addChild:child z:z tag:aTag];
  518. //#issue 1262 don't use lazy sorting, tiles are added as quads not as sprites, so sprites need to be added in order
  519. [self reorderBatch:NO];
  520. return self;
  521. }
  522. @end