cocos2d理解--SpriteBatchNode和TextureAtlas

前端之家收集整理的这篇文章主要介绍了cocos2d理解--SpriteBatchNode和TextureAtlas前端之家小编觉得挺不错的,现在分享给大家,也给大家做个参考。

1:TextureAtlas

文件

  1. GLushort* _indices; //顶点index
  2. GLuint _VAOname;
  3. GLuint _buffersVBO[2]; //0: vertex 1: indices
  4. bool _dirty; //indicates whether or not the array buffer of the VBO needs to be updated
  5. /** quantity of quads that are going to be drawn */
  6. ssize_t _totalQuads; //所有的顶点数量
  7. /** quantity of quads that can be stored with the current texture atlas size */
  8. ssize_t _capacity;
  9. /** Texture of the texture atlas */
  10. Texture2D* _texture;
  11. /** Quads that are going to be rendered */
  12. V3F_C4B_T2F_Quad* _quads; //顶点数据
  13.  
  14. 文件代码分析:
  15. ``bool TextureAtlas::initWithTexture(Texture2D *texture,ssize_t capacity)
  16. {
  17. CCASSERT(capacity>=0,"Capacity must be >= 0");
  18.  
  19. // CCASSERT(texture != nullptr,"texture should not be null");
  20. _capacity = capacity; //预留的顶点数量
  21. _totalQuads = 0;
  22.  
  23. // retained in property
  24. this->_texture = texture;
  25. CC_SAFE_RETAIN(_texture);
  26.  
  27. // Re-initialization is not allowed
  28. CCASSERT(_quads == nullptr && _indices == nullptr,"");
  29.  
  30. //给预留的顶点数量分配内存。每个顶点数据包括(位置---V3F,颜色---C4B,uv值---T2F)
  31. _quads = (V3F_C4B_T2F_Quad*)malloc( _capacity * sizeof(V3F_C4B_T2F_Quad) );
  32. //所有构成三角形的顶点index,4个顶点有6个index(两个三角形)
  33. _indices = (GLushort *)malloc( _capacity * 6 * sizeof(GLushort) );
  34.  
  35. if( ! ( _quads && _indices) && _capacity > 0)
  36. {
  37. //CCLOG("cocos2d: TextureAtlas: not enough memory");
  38. CC_SAFE_FREE(_quads);
  39. CC_SAFE_FREE(_indices);
  40.  
  41. // release texture,should set it to null,because the destruction will
  42. // release it too. see cocos2d-x issue #484
  43. CC_SAFE_RELEASE_NULL(_texture);
  44. return false;
  45. }
  46.  
  47. memset( _quads,0,_capacity * sizeof(V3F_C4B_T2F_Quad) );
  48. memset( _indices,_capacity * 6 * sizeof(GLushort) );
  49.  
  50. //.. ...省略部分代码
  51.  
  52. return true;
  53. }

void TextureAtlas::setupIndices()
{
if (_capacity == 0)
return;

  1. for( int i=0; i < _capacity; i++)
  2. {
  3. _indices[i*6+0] = i*4+0; //第i*4+0个顶点构成2*i个三角形的第一个顶点
  4. _indices[i*6+1] = i*4+1; //第i*4+1个顶点构成2*i个三角形的第二个顶点
  5. _indices[i*6+2] = i*4+2; //第i*4+2个顶点构成2*i个三角形的第三个顶点
  6.  
  7. // inverted index. issue #179
  8. _indices[i*6+3] = i*4+3; //第i*4+3个顶点构成2*i+1个三角形的第一个顶点
  9. _indices[i*6+4] = i*4+2; //第i*4+2个顶点构成2*i+1个三角形的第二个顶点
  10. _indices[i*6+5] = i*4+1; //第i*4+1个顶点构成2*i+1个三角形的第三个顶点
  11. }

}

  1. ``
  2. //将quad插入到所有数据的index位置中
  3. void TextureAtlas::insertQuad(V3F_C4B_T2F_Quad *quad,ssize_t index)
  4. {
  5. CCASSERT( index>=0 && index<_capacity,"insertQuadWithTexture: Invalid index");
  6.  
  7. _totalQuads++; //顶点数量+1
  8. CCASSERT( _totalQuads <= _capacity,"invalid totalQuads");
  9.  
  10. // issue #575. index can be > totalQuads
  11. auto remaining = (_totalQuads-1) - index; //将index后面的remaining个顶点往后移。
  12.  
  13. // last object doesn't need to be moved
  14. if( remaining > 0)
  15. {
  16. // texture coordinates
  17. //移动remaining个数据往后移动。
  18. memmove( &_quads[index+1],&_quads[index],sizeof(_quads[0]) * remaining );
  19. }
  20.  
  21. _quads[index] = *quad;
  22.  
  23.  
  24. _dirty = true;
  25.  
  26. }
  1. //将quads的amount个数据插入到所有数据的index位置上。
  2. void TextureAtlas::insertQuads(V3F_C4B_T2F_Quad* quads,ssize_t index,ssize_t amount)
  3. {
  4. CCASSERT(index>=0 && amount>=0 && index+amount<=_capacity,"insertQuadWithTexture: Invalid index + amount");
  5.  
  6. _totalQuads += amount; //顶点数据+amount
  7.  
  8. CCASSERT( _totalQuads <= _capacity,"invalid totalQuads");
  9.  
  10. // issue #575. index can be > totalQuads
  11. auto remaining = (_totalQuads-1) - index - amount;
  12.  
  13. // last object doesn't need to be moved
  14. if( remaining > 0)
  15. {
  16. // tex coordinates
  17. //将index后面的所有数据向后移动amount个位置
  18. memmove( &_quads[index+amount],sizeof(_quads[0]) * remaining );
  19. }
  20.  
  21. //设置index后面的数据
  22. auto max = index + amount;
  23. int j = 0;
  24. for (ssize_t i = index; i < max ; i++)
  25. {
  26. _quads[index] = quads[j];
  27. index++;
  28. j++;
  29. }
  30.  
  31. _dirty = true;
  32. }
  1. //将oldIndex位置的数据插入到newIndex的位置。并且中间的数据一并移动。
  2. //oldIndex,A,A..,newIndex---->A,newIndex,oldIndex。
  3. //newIndex,oldIndex---->oldIndex,A..。
  4. void TextureAtlas::insertQuadFromIndex(ssize_t oldIndex,ssize_t newIndex)
  5. {
  6. CCASSERT( newIndex >= 0 && newIndex < _totalQuads,"insertQuadFromIndex:atIndex: Invalid index");
  7. CCASSERT( oldIndex >= 0 && oldIndex < _totalQuads,"insertQuadFromIndex:atIndex: Invalid index");
  8.  
  9. if( oldIndex == newIndex )
  10. {
  11. return;
  12. }
  13. // because it is ambiguous in iphone,so we implement abs ourselves
  14. // unsigned int howMany = abs( oldIndex - newIndex);
  15. auto howMany = (oldIndex - newIndex) > 0 ? (oldIndex - newIndex) : (newIndex - oldIndex);
  16. auto dst = oldIndex;
  17. auto src = oldIndex + 1;
  18. if( oldIndex > newIndex)
  19. {
  20. dst = newIndex+1;
  21. src = newIndex;
  22. }
  23.  
  24. // texture coordinates
  25. V3F_C4B_T2F_Quad quadsBackup = _quads[oldIndex];
  26. memmove( &_quads[dst],&_quads[src],sizeof(_quads[0]) * howMany );
  27. _quads[newIndex] = quadsBackup;
  28.  
  29.  
  30. _dirty = true;
  31. }
  1. //删除index的数据,并且将index后面的 数据往前移动一个。
  2. void TextureAtlas::removeQuadAtIndex(ssize_t index)
  3. {
  4. CCASSERT( index>=0 && index<_totalQuads,"removeQuadAtIndex: Invalid index");
  5.  
  6. auto remaining = (_totalQuads-1) - index;
  7.  
  8. // last object doesn't need to be moved
  9. if( remaining )
  10. {
  11. // texture coordinates
  12. memmove( &_quads[index],&_quads[index+1],sizeof(_quads[0]) * remaining );
  13. }
  14.  
  15. _totalQuads--;
  16.  
  17.  
  18. _dirty = true;
  19. }
  1. //删除index开始的amount个数据,后面的数据往前移动
  2. void TextureAtlas::removeQuadsAtIndex(ssize_t index,ssize_t amount)
  3. {
  4. CCASSERT(index>=0 && amount>=0 && index+amount<=_totalQuads,"removeQuadAtIndex: index + amount out of bounds");
  5.  
  6. auto remaining = (_totalQuads) - (index + amount);
  7.  
  8. _totalQuads -= amount;
  9.  
  10. if ( remaining )
  11. {
  12. memmove( &_quads[index],&_quads[index+amount],sizeof(_quads[0]) * remaining );
  13. }
  14.  
  15. _dirty = true;
  16. }
  1. //移动oldIndex开始的amount到newIndex位置。
  2. void TextureAtlas::moveQuadsFromIndex(ssize_t oldIndex,ssize_t amount,ssize_t newIndex)
  3. {
  4. CCASSERT(oldIndex>=0 && amount>=0 && newIndex>=0,"values must be >= 0");
  5. CCASSERT(newIndex + amount <= _totalQuads,"insertQuadFromIndex:atIndex: Invalid index");
  6. CCASSERT(oldIndex < _totalQuads,"insertQuadFromIndex:atIndex: Invalid index");
  7.  
  8. if( oldIndex == newIndex )
  9. {
  10. return;
  11. }
  12. //create buffer
  13. size_t quadSize = sizeof(V3F_C4B_T2F_Quad);
  14. V3F_C4B_T2F_Quad* tempQuads = (V3F_C4B_T2F_Quad*)malloc( quadSize * amount);
  15. memcpy( tempQuads,&_quads[oldIndex],quadSize * amount );
  16.  
  17. if (newIndex < oldIndex)
  18. {
  19. // move quads from newIndex to newIndex + amount to make room for buffer
  20. memmove( &_quads[newIndex],&_quads[newIndex+amount],(oldIndex-newIndex)*quadSize);
  21. }
  22. else
  23. {
  24. // move quads above back
  25. memmove( &_quads[oldIndex],&_quads[oldIndex+amount],(newIndex-oldIndex)*quadSize);
  26. }
  27. memcpy( &_quads[newIndex],tempQuads,amount*quadSize);
  28.  
  29. free(tempQuads);
  30.  
  31. _dirty = true;
  32. }
  1. //渲染start位置的numberOfQuads的数据
  2. void TextureAtlas::drawNumberOfQuads(ssize_t numberOfQuads,ssize_t start)
  3. {
  4. CCASSERT(numberOfQuads>=0 && start>=0,"numberOfQuads and start must be >= 0");
  5.  
  6. if(!numberOfQuads)
  7. return;
  8.  
  9. GL::bindTexture2D(_texture->getName());
  10.  
  11. if (Configuration::getInstance()->supportsShareableVAO()) { // // Using VBO and VAO // // FIXME:: update is done in draw... perhaps it should be done in a timer if (_dirty) { glBindBuffer(GL_ARRAY_BUFFER,_buffersVBO[0]); // option 1: subdata // glBufferSubData(GL_ARRAY_BUFFER,sizeof(_quads[0])*start,sizeof(_quads[0]) * n,&_quads[start] ); // option 2: data // glBufferData(GL_ARRAY_BUFFER,sizeof(quads_[0]) * (n-start),&quads_[start],GL_DYNAMIC_DRAW); // option 3: orphaning + glMapBuffer glBufferData(GL_ARRAY_BUFFER,sizeof(_quads[0]) * _capacity,nullptr,GL_DYNAMIC_DRAW); void *buf = glMapBuffer(GL_ARRAY_BUFFER,GL_WRITE_ONLY); memcpy(buf,_quads,sizeof(_quads[0])* _totalQuads); glUnmapBuffer(GL_ARRAY_BUFFER); glBindBuffer(GL_ARRAY_BUFFER,0); _dirty = false; } GL::bindVAO(_VAOname); #if CC_REBIND_INDICES_BUFFER glBindBuffer(GL_ELEMENT_ARRAY_BUFFER,_buffersVBO[1]); #endif glDrawElements(GL_TRIANGLES,(GLsizei) numberOfQuads*6,GL_UNSIGNED_SHORT,(GLvoid*) (start*6*sizeof(_indices[0])) ); GL::bindVAO(0); #if CC_REBIND_INDICES_BUFFER glBindBuffer(GL_ELEMENT_ARRAY_BUFFER,0); #endif // glBindVertexArray(0); } else { // // Using VBO without VAO // #define kQuadSize sizeof(_quads[0].bl) glBindBuffer(GL_ARRAY_BUFFER,_buffersVBO[0]); // FIXME:: update is done in draw... perhaps it should be done in a timer if (_dirty) { glBufferSubData(GL_ARRAY_BUFFER,sizeof(_quads[0]) * _totalQuads,&_quads[0] ); _dirty = false; } GL::enableVertexAttribs(GL::VERTEX_ATTRIB_FLAG_POS_COLOR_TEX); // vertices glVertexAttribPointer(GLProgram::VERTEX_ATTRIB_POSITION,3,GL_FLOAT,GL_FALSE,kQuadSize,(GLvoid*) offsetof(V3F_C4B_T2F,vertices)); // colors glVertexAttribPointer(GLProgram::VERTEX_ATTRIB_COLOR,4,GL_UNSIGNED_BYTE,GL_TRUE,colors)); // tex coords glVertexAttribPointer(GLProgram::VERTEX_ATTRIB_TEX_COORD,2,texCoords)); glBindBuffer(GL_ELEMENT_ARRAY_BUFFER,_buffersVBO[1]); glDrawElements(GL_TRIANGLES,(GLsizei)numberOfQuads*6,(GLvoid*) (start*6*sizeof(_indices[0]))); glBindBuffer(GL_ARRAY_BUFFER,0); glBindBuffer(GL_ELEMENT_ARRAY_BUFFER,0); } CC_INCREMENT_GL_DRAWN_BATCHES_AND_VERTICES(1,numberOfQuads*6); CHECK_GL_ERROR_DEBUG(); }

2:SpriteBatchNode

  1. // addChild helper,faster than insertChild
  2. void SpriteBatchNode::appendChild(Sprite* sprite)
  3. {
  4. _reorderChildDirty=true;
  5. sprite->setBatchNode(this);
  6. sprite->setDirty(true);
  7.  
  8. if(_textureAtlas->getTotalQuads() == _textureAtlas->getCapacity()) {
  9. increaseAtlasCapacity();
  10. }
  11.  
  12. _descendants.push_back(sprite);//所有的子节点。
  13. int index = static_cast<int>(_descendants.size()-1);
  14.  
  15. sprite->setAtlasIndex(index); 设置sprite的渲染节点为TextureAtlas的第index个数据
  16.  
  17. V3F_C4B_T2F_Quad quad = sprite->getQuad();
  18. _textureAtlas->insertQuad(&quad,index);//将sprite的顶点数据传入到TextureAtlas的index位置。
  19.  
  20. // add children recursively
  21. auto& children = sprite->getChildren();
  22. for(const auto &child: children) {
  23. appendChild(static_cast<Sprite*>(child)); //将sprite的所有数据加入到descendants。
  24. }
  25. }
  1. //将sprite的数据插入到TextureAtlas的index位置。但是现在还不append到spriteBatchNode
  2. void SpriteBatchNode::insertQuadFromSprite(Sprite *sprite,ssize_t index)
  3. {
  4. CCASSERT( sprite != nullptr,"Argument must be non-nullptr");
  5. CCASSERT( dynamic_cast<Sprite*>(sprite),"CCSpriteBatchNode only supports Sprites as children");
  6.  
  7. // make needed room
  8. while(index >= _textureAtlas->getCapacity() || _textureAtlas->getCapacity() == _textureAtlas->getTotalQuads())
  9. {
  10. this->increaseAtlasCapacity();
  11. }
  12. //
  13. // update the quad directly. Don't add the sprite to the scene graph
  14. //
  15. sprite->setBatchNode(this);
  16. sprite->setAtlasIndex(index);
  17.  
  18. V3F_C4B_T2F_Quad quad = sprite->getQuad();
  19. _textureAtlas->insertQuad(&quad,index);
  20.  
  21. // FIXME:: updateTransform will update the textureAtlas too,using updateQuad.
  22. // FIXME:: so,it should be AFTER the insertQuad
  23. sprite->setDirty(true);
  24. sprite->updateTransform();
  25. }
  1. //获取sprite的第一个atlas 数据的index
  2. ssize_t SpriteBatchNode::atlasIndexForChild(Sprite *sprite,int nZ)
  3. {
  4. auto& siblings = sprite->getParent()->getChildren();
  5. auto childIndex = siblings.getIndex(sprite);
  6.  
  7. // ignore parent Z if parent is spriteSheet
  8. bool ignoreParent = (SpriteBatchNode*)(sprite->getParent()) == this;
  9. Sprite *prev = nullptr;
  10. if (childIndex > 0 && childIndex != -1)
  11. {
  12. prev = static_cast<Sprite*>(siblings.at(childIndex - 1));
  13. }
  14.  
  15. // first child of the sprite sheet
  16. if (ignoreParent)
  17. {
  18. if (childIndex == 0)
  19. {
  20. return 0;
  21. }
  22.  
  23. return highestAtlasIndexInChild(prev) + 1; //之前兄弟节点的最后一个子节点的index +1
  24. }
  25.  
  26. // parent is a Sprite,so,it must be taken into account
  27.  
  28. // first child of an Sprite ?
  29. if (childIndex == 0)//第一个节点
  30. {
  31. Sprite *p = static_cast<Sprite*>(sprite->getParent());
  32.  
  33. // less than parent and brothers
  34. if (nZ < 0)
  35. {
  36. return p->getAtlasIndex();//
  37. }
  38. else
  39. {
  40. return p->getAtlasIndex() + 1;//如果nz>=0,那么index是父节点的index +1。
  41. }
  42. }
  43. else
  44. {
  45. // prevIoUs & sprite belong to the same branch
  46. if ((prev->getLocalZOrder() < 0 && nZ < 0) || (prev->getLocalZOrder() >= 0 && nZ >= 0))
  47. {
  48. //prev和本 节点在同一批次,那么index是prev的最大index+1。
  49. return highestAtlasIndexInChild(prev) + 1;
  50. }
  51.  
  52. // else (prevIoUs < 0 and sprite >= 0 )
  53. Sprite *p = static_cast<Sprite*>(sprite->getParent());//这种情况下,是父节点的index+1
  54. return p->getAtlasIndex() + 1;
  55. }
  56.  
  57. // Should not happen. Error calculating Z on SpriteSheet
  58. CCASSERT(0,"should not run here");
  59. return 0;
  60. }
  1. //sprite和sprite所有子节点的最大index
  2. ssize_t SpriteBatchNode::highestAtlasIndexInChild(Sprite *sprite)
  3. {
  4. auto& children = sprite->getChildren();
  5.  
  6. if (children.size() == 0)
  7. {
  8. return sprite->getAtlasIndex();
  9. }
  10. else
  11. {
  12. return highestAtlasIndexInChild( static_cast<Sprite*>(children.back()));
  13. }
  14. }
  15.  
  16. //sprite和sprite所有子节点的最小index
  17. ssize_t SpriteBatchNode::lowestAtlasIndexInChild(Sprite *sprite)
  18. {
  19. auto& children = sprite->getChildren();
  20.  
  21. if (children.size() == 0)
  22. {
  23. return sprite->getAtlasIndex();
  24. }
  25. else
  26. {
  27. return lowestAtlasIndexInChild(static_cast<Sprite*>(children.at(0)));
  28. }
  29. }
  1. //更新sprite和sprite子节点的atlas数据的index。
  2. void SpriteBatchNode::updateAtlasIndex(Sprite* sprite,ssize_t* curIndex)
  3. {
  4. auto& array = sprite->getChildren();
  5. auto count = array.size();
  6.  
  7. ssize_t oldIndex = 0;
  8.  
  9. if( count == 0 )//如果没有子节点,那么sprite就是curIndex位置了
  10. {
  11. oldIndex = sprite->getAtlasIndex();
  12. sprite->setAtlasIndex(*curIndex);
  13. sprite->setOrderOfArrival(0);
  14. if (oldIndex != *curIndex){
  15. swap(oldIndex,*curIndex);
  16. }
  17. (*curIndex)++;
  18. }
  19. else
  20. {
  21. bool needNewIndex=true;
  22.  
  23. //先localZ<0的,然后sprite,最后localZ>0
  24.  
  25. //因为先对所有的子节点排序过。先localZ<0,然后localZ>0
  26. if (array.at(0)->getLocalZOrder() >= 0)//如果第一个localZ>0那么先设置sprite的index
  27. {
  28. //all children are in front of the parent
  29. oldIndex = sprite->getAtlasIndex();
  30. sprite->setAtlasIndex(*curIndex);
  31. sprite->setOrderOfArrival(0);
  32. if (oldIndex != *curIndex)
  33. {
  34. swap(oldIndex,*curIndex);
  35. }
  36. (*curIndex)++;
  37.  
  38. needNewIndex = false;
  39. }
  40.  
  41. for(const auto &child: array) {
  42. Sprite* sp = static_cast<Sprite*>(child);
  43. //找到了第一个localZorder>0的,设置sprite。
  44. if (needNewIndex && sp->getLocalZOrder() >= 0)
  45. {
  46. oldIndex = sprite->getAtlasIndex();
  47. sprite->setAtlasIndex(*curIndex);
  48. sprite->setOrderOfArrival(0);
  49. if (oldIndex != *curIndex) {
  50. this->swap(oldIndex,*curIndex);
  51. }
  52. (*curIndex)++;
  53. needNewIndex = false;
  54. }
  55.  
  56. updateAtlasIndex(sp,curIndex);
  57. }
  58.  
  59.  
  60. //这种情况是:所有的子节点都是localZ<0。
  61. if (needNewIndex)
  62. {//all children have a zOrder < 0)
  63. oldIndex = sprite->getAtlasIndex();
  64. sprite->setAtlasIndex(*curIndex);
  65. sprite->setOrderOfArrival(0);
  66. if (oldIndex != *curIndex) {
  67. swap(oldIndex,*curIndex);
  68. }
  69. (*curIndex)++;
  70. }
  71. }
  72. }

猜你在找的Cocos2d-x相关文章