cocos2dx--TextureAtlas

在Cocos2d-x中,精灵CCSprite对象可以直接通过加载图片的形式创建,这是最简单的形式,
这样做有两个弊端,一个是浪费内存,另一个是降低效率。
所谓浪费内存,其原因是在为图片分配内存空间时,需要分配超过图片尺寸的,且是最小的2的幂次的尺寸。如果图片长和宽分别是129px和65px,则分配的内存空间需要256*128*每个像素占用的内存字节数。可以发现实际的图片信息大约只占用了内存空间的1/4,也就是说将近3/4的内存空间被浪费了。每通过图片创建一个精灵,就会发生内存浪费的现象。精灵数量越多,内存的浪费越严重。

所谓降低效率,是指在生成精灵CCSprite对象的时候,Cocos2d-x会进行渲染操作。渲染操作包括图形处理硬件的准备渲染、执行渲染以及最后的清理工作。精灵越多,游戏的执行速度越慢。

那么如何解决呢?可以使用精灵批处理节点CCSpriteBatchNode与纹理贴图集解决,前者主要用于减少相同图片的渲染操作,后者可以有效节约内存。

如果游戏中需要创建很多基于相同图片的精灵,例如屏幕中的大量子弹,如果每次都单独创建一个精灵的话,每次都需要渲染。但是如果将他们加入到精灵批处理节点CCSpriteBatchNode中,则只需执行一次渲染即可。CCSpriteBatchNode类似于图层,它是一个精灵的容器,并且要求这些精灵都基于相同的图片(如果不是相同图片则报错)。

注意,由于创建的精灵都添加到了同一个批处理节点中,所以他们具有相同的Z序。
既然每次通过图片创建精灵都会发生内存浪费的现象,所以何不将所有图片整合成一张大图片呢?这张大图就称作纹理贴图集(Texture Atlas)。通过纹理贴图集和批处理节点,就可以达到既节省内存,又提升渲染速度的目的。纹理贴图集的长和宽都满足2的n次方,应该把尽可能多的图片放入一个纹理贴图集中。

纹理贴图集中的每一个小图片需要对应一个它在贴图集中的尺寸和偏移量信息,这个信息就称作精灵框架(Sprite Frame)。有了精灵框架信息,就能够从大图中取出对应位置的小图。如果你有很多单独的图片,可以使用现有的工具创建贴图集。


TextureAtlas

TextureAtlas维持了多个Quad,它们都使用相同的纹理,使用相同的渲染参数,这样通过一次性绘制多个Quad来提高效率。 主要的成员变量:

GLushort*           _indices; //索引
GLuint              _VAOname; //VAO
GLuint              _buffersVBO[2]; //0: vertex  1: indices
bool                _dirty; //indicates whether or not the array buffer of the VBO needs to be updated
/** quantity of quads that are going to be drawn */
ssize_t _totalQuads;
/** quantity of quads that can be stored with the current texture atlas size */
ssize_t _capacity;
/** Texture of the texture atlas */
Texture2D* _texture;//说明所有quad共享一个纹理
/** Quads that are going to be rendered */
V3F_C4B_T2F_Quad* _quads;

来看一下init函数:

bool TextureAtlas::initWithTexture(Texture2D *texture, ssize_t capacity)
{
    CCASSERT(capacity>=0, "Capacity must be >= 0");

//    CCASSERT(texture != nullptr, "texture should not be null");
    _capacity = capacity;
    _totalQuads = 0;

    // retained in property
    this->_texture = texture;
    CC_SAFE_RETAIN(_texture);

    // Re-initialization is not allowed
    CCASSERT(_quads == nullptr && _indices == nullptr, "");
    //按照_capacity的大小先把内存分配好
    _quads = (V3F_C4B_T2F_Quad*)malloc( _capacity * sizeof(V3F_C4B_T2F_Quad) );
    _indices = (GLushort *)malloc( _capacity * 6 * sizeof(GLushort) );

    if( ! ( _quads && _indices) && _capacity > 0)
    {
        //CCLOG("cocos2d: TextureAtlas: not enough memory");
        CC_SAFE_FREE(_quads);
        CC_SAFE_FREE(_indices);

        // release texture, should set it to null, because the destruction will
        // release it too. see cocos2d-x issue #484
        CC_SAFE_RELEASE_NULL(_texture);
        return false;
    }

    memset( _quads, 0, _capacity * sizeof(V3F_C4B_T2F_Quad) );
    memset( _indices, 0, _capacity * 6 * sizeof(GLushort) );

#if CC_ENABLE_CACHE_TEXTURE_DATA
    /** listen the event that renderer was recreated on Android/WP8 */
    _rendererRecreatedListener = EventListenerCustom::create(EVENT_RENDERER_RECREATED, CC_CALLBACK_1(TextureAtlas::listenRendererRecreated, this));
    Director::getInstance()->getEventDispatcher()->addEventListenerWithFixedPriority(_rendererRecreatedListener, -1);
#endif
  //给索引数组赋值
    this->setupIndices();
    //和Render类类似
    if (Configuration::getInstance()->supportsShareableVAO())
    {
        setupVBOandVAO();
    }
    else
    {
        setupVBO();
    }

    _dirty = true;

    return true;
}

TextureAtlas提供了对Quad的操作:

void updateQuad(V3F_C4B_T2F_Quad* quad, ssize_t index);
//下面需要调用memmove移动内存
void insertQuad(V3F_C4B_T2F_Quad* quad, ssize_t index);
void insertQuads(V3F_C4B_T2F_Quad* quads, ssize_t index, ssize_t amount);
void removeQuadAtIndex(ssize_t index);
void removeQuadsAtIndex(ssize_t index, ssize_t amount);
//将_totalQuads置0
void removeAllQuads();
void increaseTotalQuadsWith(ssize_t amount);
void moveQuadsFromIndex(ssize_t oldIndex, ssize_t amount, ssize_t newIndex);
void moveQuadsFromIndex(ssize_t index, ssize_t newIndex);
void fillWithEmptyQuadsFromIndex(ssize_t index, ssize_t amount);
TextureAtlas有自己的绘制函数,在BatchCommand中维持一个TextureAtlas*,在其execute函数中

void BatchCommand::execute()
{
    // Set material
    _shader->use();
    _shader->setUniformsForBuiltins(_mv);
    GL::bindTexture2D(_textureID);
    GL::blendFunc(_blendType.src, _blendType.dst);

    // Draw
    _textureAtlas->drawQuads();
}










你可能感兴趣的:(TextureAtlas)