cocos2d理解--SpriteBatchNode和TextureAtlas

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

1:TextureAtlas

文件

GLushort*           _indices;  //顶点index
    GLuint              _VAOname;
    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;
    /** Quads that are going to be rendered */
    V3F_C4B_T2F_Quad* _quads;  //顶点数据文件代码分析:
``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,"");

//给预留的顶点数量分配内存。每个顶点数据包括(位置---V3F,颜色---C4B,uv值---T2F)
    _quads = (V3F_C4B_T2F_Quad*)malloc( _capacity * sizeof(V3F_C4B_T2F_Quad) );
//所有构成三角形的顶点index,4个顶点有6个index(两个三角形)
    _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,_capacity * 6 * sizeof(GLushort) );

//.. ...省略部分代码

    return true;
}

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

for( int i=0; i < _capacity; i++)
{
    _indices[i*6+0] = i*4+0;  //第i*4+0个顶点构成2*i个三角形的第一个顶点
    _indices[i*6+1] = i*4+1;  //第i*4+1个顶点构成2*i个三角形的第二个顶点
    _indices[i*6+2] = i*4+2;  //第i*4+2个顶点构成2*i个三角形的第三个顶点

    // inverted index. issue #179
    _indices[i*6+3] = i*4+3;  //第i*4+3个顶点构成2*i+1个三角形的第一个顶点
    _indices[i*6+4] = i*4+2;  //第i*4+2个顶点构成2*i+1个三角形的第二个顶点
    _indices[i*6+5] = i*4+1;  //第i*4+1个顶点构成2*i+1个三角形的第三个顶点      
}

}

``
//将quad插入到所有数据的index位置中
void TextureAtlas::insertQuad(V3F_C4B_T2F_Quad *quad,ssize_t index)
{
    CCASSERT( index>=0 && index<_capacity,"insertQuadWithTexture: Invalid index");

    _totalQuads++;  //顶点数量+1
    CCASSERT( _totalQuads <= _capacity,"invalid totalQuads");

    // issue #575. index can be > totalQuads
    auto remaining = (_totalQuads-1) - index; //将index后面的remaining个顶点往后移。

    // last object doesn't need to be moved
    if( remaining > 0) 
    {
        // texture coordinates
        //移动remaining个数据往后移动。
        memmove( &_quads[index+1],&_quads[index],sizeof(_quads[0]) * remaining );        
    }

    _quads[index] = *quad;


    _dirty = true;

}
//将quads的amount个数据插入到所有数据的index位置上。
void TextureAtlas::insertQuads(V3F_C4B_T2F_Quad* quads,ssize_t index,ssize_t amount)
{
    CCASSERT(index>=0 && amount>=0 && index+amount<=_capacity,"insertQuadWithTexture: Invalid index + amount");

    _totalQuads += amount;  //顶点数据+amount

    CCASSERT( _totalQuads <= _capacity,"invalid totalQuads");

    // issue #575. index can be > totalQuads
    auto remaining = (_totalQuads-1) - index - amount;

    // last object doesn't need to be moved
    if( remaining > 0)
    {
        // tex coordinates
        //将index后面的所有数据向后移动amount个位置
        memmove( &_quads[index+amount],sizeof(_quads[0]) * remaining );
    }

    //设置index后面的数据
    auto max = index + amount;
    int j = 0;
    for (ssize_t i = index; i < max ; i++)
    {
        _quads[index] = quads[j];
        index++;
        j++;
    }

    _dirty = true;
}
//将oldIndex位置的数据插入到newIndex的位置。并且中间的数据一并移动。
//oldIndex,A,A..,newIndex---->A,newIndex,oldIndex。
//newIndex,oldIndex---->oldIndex,A..。
void TextureAtlas::insertQuadFromIndex(ssize_t oldIndex,ssize_t newIndex)
{
    CCASSERT( newIndex >= 0 && newIndex < _totalQuads,"insertQuadFromIndex:atIndex: Invalid index");
    CCASSERT( oldIndex >= 0 && oldIndex < _totalQuads,"insertQuadFromIndex:atIndex: Invalid index");

    if( oldIndex == newIndex )
    {
        return;
    }
    // because it is ambiguous in iphone,so we implement abs ourselves
    // unsigned int howMany = abs( oldIndex - newIndex);
 auto howMany = (oldIndex - newIndex) > 0 ? (oldIndex - newIndex) :  (newIndex - oldIndex);
    auto dst = oldIndex;
    auto src = oldIndex + 1;
    if( oldIndex > newIndex)
    {
        dst = newIndex+1;
        src = newIndex;
    }

    // texture coordinates
    V3F_C4B_T2F_Quad quadsBackup = _quads[oldIndex];
    memmove( &_quads[dst],&_quads[src],sizeof(_quads[0]) * howMany );
    _quads[newIndex] = quadsBackup;


    _dirty = true;
}
//删除index的数据,并且将index后面的 数据往前移动一个。
void TextureAtlas::removeQuadAtIndex(ssize_t index)
{
    CCASSERT( index>=0 && index<_totalQuads,"removeQuadAtIndex: Invalid index");

    auto remaining = (_totalQuads-1) - index;

    // last object doesn't need to be moved
    if( remaining ) 
    {
        // texture coordinates
        memmove( &_quads[index],&_quads[index+1],sizeof(_quads[0]) * remaining );
    }

    _totalQuads--;


    _dirty = true;
}
//删除index开始的amount个数据,后面的数据往前移动
void TextureAtlas::removeQuadsAtIndex(ssize_t index,ssize_t amount)
{
    CCASSERT(index>=0 && amount>=0 && index+amount<=_totalQuads,"removeQuadAtIndex: index + amount out of bounds");

    auto remaining = (_totalQuads) - (index + amount);

    _totalQuads -= amount;

    if ( remaining )
    {
        memmove( &_quads[index],&_quads[index+amount],sizeof(_quads[0]) * remaining );
    }

    _dirty = true;
}
//移动oldIndex开始的amount到newIndex位置。
void TextureAtlas::moveQuadsFromIndex(ssize_t oldIndex,ssize_t amount,ssize_t newIndex)
{
    CCASSERT(oldIndex>=0 && amount>=0 && newIndex>=0,"values must be >= 0");
    CCASSERT(newIndex + amount <= _totalQuads,"insertQuadFromIndex:atIndex: Invalid index");
    CCASSERT(oldIndex < _totalQuads,"insertQuadFromIndex:atIndex: Invalid index");

    if( oldIndex == newIndex )
    {
        return;
    }
    //create buffer
    size_t quadSize = sizeof(V3F_C4B_T2F_Quad);
    V3F_C4B_T2F_Quad* tempQuads = (V3F_C4B_T2F_Quad*)malloc( quadSize * amount);
    memcpy( tempQuads,&_quads[oldIndex],quadSize * amount );

    if (newIndex < oldIndex)
    {
        // move quads from newIndex to newIndex + amount to make room for buffer
        memmove( &_quads[newIndex],&_quads[newIndex+amount],(oldIndex-newIndex)*quadSize);
    }
    else
    {
        // move quads above back
        memmove( &_quads[oldIndex],&_quads[oldIndex+amount],(newIndex-oldIndex)*quadSize);
    }
    memcpy( &_quads[newIndex],tempQuads,amount*quadSize);

    free(tempQuads);

    _dirty = true;
}
//渲染start位置的numberOfQuads的数据
void TextureAtlas::drawNumberOfQuads(ssize_t numberOfQuads,ssize_t start)
{
    CCASSERT(numberOfQuads>=0 && start>=0,"numberOfQuads and start must be >= 0");

    if(!numberOfQuads)
        return;

    GL::bindTexture2D(_texture->getName());

    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

// addChild helper,faster than insertChild
void SpriteBatchNode::appendChild(Sprite* sprite)
{
    _reorderChildDirty=true;
    sprite->setBatchNode(this);
    sprite->setDirty(true);

    if(_textureAtlas->getTotalQuads() == _textureAtlas->getCapacity()) {
        increaseAtlasCapacity();
    }

    _descendants.push_back(sprite);//所有的子节点。
    int index = static_cast<int>(_descendants.size()-1);

    sprite->setAtlasIndex(index); 设置sprite的渲染节点为TextureAtlas的第index个数据

    V3F_C4B_T2F_Quad quad = sprite->getQuad();  
    _textureAtlas->insertQuad(&quad,index);//将sprite的顶点数据传入到TextureAtlas的index位置。

    // add children recursively
    auto& children = sprite->getChildren();
    for(const auto &child: children) {
        appendChild(static_cast<Sprite*>(child)); //将sprite的所有数据加入到descendants。
    }
}
//将sprite的数据插入到TextureAtlas的index位置。但是现在还不append到spriteBatchNode
void SpriteBatchNode::insertQuadFromSprite(Sprite *sprite,ssize_t index)
{
    CCASSERT( sprite != nullptr,"Argument must be non-nullptr");
    CCASSERT( dynamic_cast<Sprite*>(sprite),"CCSpriteBatchNode only supports Sprites as children");

    // make needed room
    while(index >= _textureAtlas->getCapacity() || _textureAtlas->getCapacity() == _textureAtlas->getTotalQuads())
    {
        this->increaseAtlasCapacity();
    }
    //
    // update the quad directly. Don't add the sprite to the scene graph
    //
    sprite->setBatchNode(this);
    sprite->setAtlasIndex(index);

    V3F_C4B_T2F_Quad quad = sprite->getQuad();
    _textureAtlas->insertQuad(&quad,index);

    // FIXME:: updateTransform will update the textureAtlas too,using updateQuad.
    // FIXME:: so,it should be AFTER the insertQuad
    sprite->setDirty(true);
    sprite->updateTransform();
}
//获取sprite的第一个atlas 数据的index
ssize_t SpriteBatchNode::atlasIndexForChild(Sprite *sprite,int nZ)
{
    auto& siblings = sprite->getParent()->getChildren();
    auto childIndex = siblings.getIndex(sprite);

    // ignore parent Z if parent is spriteSheet
    bool ignoreParent = (SpriteBatchNode*)(sprite->getParent()) == this;
    Sprite *prev = nullptr;
    if (childIndex > 0 && childIndex != -1)
    {
        prev = static_cast<Sprite*>(siblings.at(childIndex - 1));
    }

    // first child of the sprite sheet
    if (ignoreParent)
    {
        if (childIndex == 0)
        {
            return 0;
        }

        return highestAtlasIndexInChild(prev) + 1; //之前兄弟节点的最后一个子节点的index +1
    }

    // parent is a Sprite,so,it must be taken into account

    // first child of an Sprite ?
    if (childIndex == 0)//第一个节点
    {
        Sprite *p = static_cast<Sprite*>(sprite->getParent());

        // less than parent and brothers
        if (nZ < 0)
        {
            return p->getAtlasIndex();//
        }
        else
        {
            return p->getAtlasIndex() + 1;//如果nz>=0,那么index是父节点的index +1。
        }
    }
    else
    {
        // prevIoUs & sprite belong to the same branch
        if ((prev->getLocalZOrder() < 0 && nZ < 0) || (prev->getLocalZOrder() >= 0 && nZ >= 0))
        {
            //prev和本 节点在同一批次,那么index是prev的最大index+1。
            return highestAtlasIndexInChild(prev) + 1;  
        }

        // else (prevIoUs < 0 and sprite >= 0 )
        Sprite *p = static_cast<Sprite*>(sprite->getParent());//这种情况下,是父节点的index+1
        return p->getAtlasIndex() + 1;
    }

    // Should not happen. Error calculating Z on SpriteSheet
    CCASSERT(0,"should not run here");
    return 0;
}
//sprite和sprite所有子节点的最大index
ssize_t SpriteBatchNode::highestAtlasIndexInChild(Sprite *sprite)
{
    auto& children = sprite->getChildren();

    if (children.size() == 0)
    {
        return sprite->getAtlasIndex();
    }
    else
    {
        return highestAtlasIndexInChild( static_cast<Sprite*>(children.back()));
    }
}

//sprite和sprite所有子节点的最小index
ssize_t SpriteBatchNode::lowestAtlasIndexInChild(Sprite *sprite)
{
    auto& children = sprite->getChildren();

    if (children.size() == 0)
    {
        return sprite->getAtlasIndex();
    }
    else
    {
        return lowestAtlasIndexInChild(static_cast<Sprite*>(children.at(0)));
    }
}
//更新sprite和sprite子节点的atlas数据的index。
void SpriteBatchNode::updateAtlasIndex(Sprite* sprite,ssize_t* curIndex)
{
    auto& array = sprite->getChildren();
    auto count = array.size();

    ssize_t oldIndex = 0;

    if( count == 0 )//如果没有子节点,那么sprite就是curIndex位置了
    {
        oldIndex = sprite->getAtlasIndex();
        sprite->setAtlasIndex(*curIndex);
        sprite->setOrderOfArrival(0);
        if (oldIndex != *curIndex){
            swap(oldIndex,*curIndex);
        }
        (*curIndex)++;
    }
    else
    {
        bool needNewIndex=true;

        //先localZ<0的,然后sprite,最后localZ>0

        //因为先对所有的子节点排序过。先localZ<0,然后localZ>0
        if (array.at(0)->getLocalZOrder() >= 0)//如果第一个localZ>0那么先设置sprite的index
        {
            //all children are in front of the parent
            oldIndex = sprite->getAtlasIndex();
            sprite->setAtlasIndex(*curIndex);
            sprite->setOrderOfArrival(0);
            if (oldIndex != *curIndex)
            {
                swap(oldIndex,*curIndex);
            }
            (*curIndex)++;

            needNewIndex = false;
        }

        for(const auto &child: array) {
            Sprite* sp = static_cast<Sprite*>(child);
            //找到了第一个localZorder>0的,设置sprite。
            if (needNewIndex && sp->getLocalZOrder() >= 0)
            {
                oldIndex = sprite->getAtlasIndex();
                sprite->setAtlasIndex(*curIndex);
                sprite->setOrderOfArrival(0);
                if (oldIndex != *curIndex) {
                    this->swap(oldIndex,*curIndex);
                }
                (*curIndex)++;
                needNewIndex = false;
            }

            updateAtlasIndex(sp,curIndex);
        }


        //这种情况是:所有的子节点都是localZ<0。
        if (needNewIndex)
        {//all children have a zOrder < 0)
            oldIndex = sprite->getAtlasIndex();
            sprite->setAtlasIndex(*curIndex);
            sprite->setOrderOfArrival(0);
            if (oldIndex != *curIndex) {
                swap(oldIndex,*curIndex);
            }
            (*curIndex)++;
        }
    }
}

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