cocos源码分析--LayerColor的绘制过程

1开始,先创建一个LayerColor

Scene *scene=Scene::create();
    director->runWithScene(scene);
    //目标
    auto layer = LayerColor::create(Color4B(0,255,0,255),100,100);
    //主要的步骤就是设置了node 的 _position
    layer->setPosition(10,10);
    scene->addChild(layer);

2 看一下LayerColor的初始化方法

bool LayerColor::initWithColor(const Color4B& color,GLfloat w,GLfloat h)
{
    if (Layer::init())
    {

        // default blend function
        //指定混合模式
        _blendFunc = BlendFunc::ALPHA_NON_PREMULTIPLIED;

        /*
         realColor和displayedColor 记录元素本身的颜色属性
         displayedColor和displayedOpacity方法用于表示和父亲元素叠加过后的最终绘制颜色
         sprite用于父亲颜色和自己纹理的混合,LayColor默认一致,不叠加颜色
         */
        _displayedColor.r = _realColor.r = color.r;
        _displayedColor.g = _realColor.g = color.g;
        _displayedColor.b = _realColor.b = color.b;
        _displayedOpacity = _realOpacity = color.a;
       
        //四个顶点 初始化
        for (size_t i = 0; i<sizeof(_squareVertices) / sizeof( _squareVertices[0]); i++ )
        {
            _squareVertices[i].x = 0.0f;
            _squareVertices[i].y = 0.0f;
        }
         //四个顶点的颜色归一化,颜色是一样的
        updateColor();
        //w,h 为 设计分辨率,设置顶点的范围
        setContentSize(Size(w,h));
       /*
        每个node拥有一个GLProgramState实例
        查找这种类型的shader GLProgram SHADER_NAME_POSITION_COLOR_NO_MVP
        */
        GLProgramState* state=GLProgramState::getOrCreateWithGLProgramName(GLProgram::SHADER_NAME_POSITION_COLOR_NO_MVP);
        
        setGLProgramState(state);
        return true;
    }
    return false;
}
/// override contentSize
void LayerColor::setContentSize(const Size & size)
{
    //没有赋值的为0,也就是 0(0,0) 1(w,0) 2 (0,h) 3(w,h)
    //绘制顺序为012  213
    _squareVertices[1].x = size.width;
    _squareVertices[2].y = size.height;
    _squareVertices[3].x = size.width;
    _squareVertices[3].y = size.height;
    
    Layer::setContentSize(size);
}
void Node::setContentSize(const Size & size)
{
    if ( ! size.equals(_contentSize))
    {
        _contentSize = size;
        //得到锚点在本地坐标系下的坐标
        _anchorPointInPoints = Vec2(_contentSize.width * _anchorPoint.x,_contentSize.height * _anchorPoint.y );
        //告诉该更新了
        _transformUpdated = _transformDirty = _inverseDirty = _contentSizeDirty = true;
    }
}

3 Director::drawScene方法为正式绘图,如下

void Director::drawScene()
{
     //省略部分代码

    glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);

     
    //兼容cocos2.0,暂时忽略
    pushMatrix(MATRIX_STACK_TYPE::MATRIX_STACK_MODELVIEW);

    // draw the scene
    if (_runningScene)
    {//正式访问
        _runningScene->visit(_renderer,Mat4::IDENTITY,false);//第一次的矩阵是单位矩阵
        
        _eventDispatcher->dispatchEvent(_eventAfterVisit);
    }
     //开始真正的opengl
    _renderer->render();
    
     popMatrix(MATRIX_STACK_TYPE::MATRIX_STACK_MODELVIEW);

    _totalFrames++;

    // swap buffers
    if (_openGLView)
    {
        _openGLView->swapBuffers();//这个之后再看
    }

    if (_displayStats)
    {
        calculateMPF();//不知道啥意思
    }
}

标红不分为开始遍历节点,但不进行opengl绘制,进入代码如下:

void Node::visit(Renderer* renderer,const Mat4 &parentTransform,uint32_t parentFlags)
{
    // quick return if not visible. children won‘t be drawn.
    if (!_visible)
    {
        return;
    }

    uint32_t flags = processParentFlags(parentTransform,parentFlags);

    // IMPORTANT:
    // To ease the migration to v3.0,we still support the Mat4 stack,// but it is deprecated and your code should not rely on it
    /*
     为了便于迁移到v3.0,我们仍然支持Mat4堆栈,
           但它已被弃用,您的代码不应该依赖它
     */
    Director* director = Director::getInstance();
    director->pushMatrix(MATRIX_STACK_TYPE::MATRIX_STACK_MODELVIEW);
    //把刚生产的模型视图矩阵加入到顶部的modeview【对scene来说,执行下面代码之前,里面已经有3个modelview】
    //这个代码针对2.0的,对3.1没啥意义了
    director->loadMatrix(MATRIX_STACK_TYPE::MATRIX_STACK_MODELVIEW,_modelViewTransform);

    int i = 0;

    if(!_children.empty())
    {
        sortAllChildren();
        // draw children zOrder < 0
        for( ; i < _children.size(); i++ )
        {
            auto node = _children.at(i);

            if ( node && node->_localZOrder < 0 )
                node->visit(renderer,_modelViewTransform,flags);
            else
                break;
        }
        // self draw
        this->draw(renderer,flags);

        for(auto it=_children.cbegin()+i; it != _children.cend(); ++it)
            (*it)->visit(renderer,flags);
    }
    else
    {
        this->draw(renderer,flags);
    }
   
    /*
     画完了就退出战,是兼容2.0的时候,现在不需要这个了
     矩阵的转换都在GPU的shader中了
    */
    director->popMatrix(MATRIX_STACK_TYPE::MATRIX_STACK_MODELVIEW);
    
    // FIX ME: Why need to set _orderOfArrival to 0??
    // Please refer to https://github.com/cocos2d/cocos2d-x/pull/6920
    // reset for next frame
    // _orderOfArrival = 0;
}
uint32_t Node::processParentFlags(const Mat4& parentTransform,uint32_t parentFlags)
{
    uint32_t flags = parentFlags;//先把父亲的改动标志传过来
  
    //如果转置矩阵 变化了, 标记为脏数据,要更新
    flags |= (_transformUpdated ? FLAGS_TRANSFORM_DIRTY : 0);
    //如果大小改变了,标记为脏数据,要更新
    flags |= (_contentSizeDirty ? FLAGS_CONTENT_SIZE_DIRTY : 0);
    //
    if(_usingNormalizedPosition && (flags & FLAGS_CONTENT_SIZE_DIRTY)) {
        CCASSERT(_parent,"setNormalizedPosition() doesn‘t work with orphan nodes");
        auto s = _parent->getContentSize();
        _position.x = _normalizedPosition.x * s.width;
        _position.y = _normalizedPosition.y * s.height;
        _transformUpdated = _transformDirty = _inverseDirty = true;
    }
    //转置矩阵和大小其中一个变了
    if(flags & FLAGS_DIRTY_MASK)
        _modelViewTransform = this->transform(parentTransform);//节点自己的转换矩阵,节点的本地坐标乘以这个矩阵就会得到世界坐标
    //更新完毕,标记为false
    _transformUpdated = false;
    _contentSizeDirty = false;

    return flags;
}

self->draw为绘制自己,但不是真的绘制,而是让自己关联一个绘制命令Command,LayerColor的draw方法重写如下:

//transform为本地坐标转世界坐标的矩阵
void LayerColor::draw(Renderer *renderer,const Mat4 &transform,uint32_t flags)
{
    _customCommand.init(_globalZOrder);
     //回调函数
    _customCommand.func = CC_CALLBACK_0(LayerColor::onDraw,this,transform,flags);
    //把绘制命令的东西放到renderer里面
    renderer->addCommand(&_customCommand);
    
    for(int i = 0; i < 4; ++i)
    {
        Vec4 pos;
        //四个顶点的设计分辨率坐标
        pos.x = _squareVertices[i].x; pos.y = _squareVertices[i].y; pos.z = _positionZ;
        pos.w = 1;//齐次坐标
        //得出来的pos就是世界坐标下的pos了
        _modelViewTransform.transformVector(&pos);
        //pos.w 世界坐标的w始终为1
        //这个世界坐标会在shader内乘以相机矩阵和裁剪矩阵,得出最后的视口需要的坐标
        _noMVPVertices[i] = Vec3(pos.x,pos.y,pos.z)/pos.w;
    }
}

代码把需要绘制的信息加入到了customCommand里面。

4 _renderer->render(); 负责执行command内的opengl绘制命令,代码如下:

void Renderer::render()
{
    
        //Process render commands
        //1. Sort render commands based on ID
        //renderGroups包括若干rederqueue,默认使用第一个,
        //renderquque包括若干个rendercommand
        for (auto &renderqueue : _renderGroups)
        {
            renderqueue.sort();
        }
        
        visitRenderQueue(_renderGroups[0]);
        
        flush();
    
        clean();
    
}
void Renderer::visitRenderQueue(const RenderQueue& queue)
{
    ssize_t size = queue.size();
    
    for (ssize_t index = 0; index < size; ++index)
    {
        auto command = queue[index];
        auto commandType = command->getType();
        
         if(RenderCommand::Type::CUSTOM_COMMAND == commandType)//比如 LayerColor
        {
            flush();
            auto cmd = static_cast<CustomCommand*>(command);
            cmd->execute();//会调用LayerColor::onDraw,直接开始绘图
        }
       
       
    }
}

5 cmd->execute会调用customCommand的回调函数,在LayerColor中为onDraw,代码如下:

//通过自定义方法进行回调 transform为 本地坐标转世界坐标的旋转矩阵
void LayerColor::onDraw(const Mat4& transform,uint32_t flags)
{
    getGLProgram()->use();//layercolor 的tansform为相机矩阵*裁剪矩阵
    getGLProgram()->setUniformsForBuiltins(transform);//设置顶点着色器中全局变量的值,如MVP矩阵
     //启用 顶点坐标和颜色
    GL::enableVertexAttribs( GL::VERTEX_ATTRIB_FLAG_POSITION | GL::VERTEX_ATTRIB_FLAG_COLOR );
    //
    // Attributes
    //
#ifdef EMSCRIPTEN
    setGLBufferData(_noMVPVertices,4 * sizeof(Vec3),0);
    glVertexAttribPointer(GLProgram::VERTEX_ATTRIB_POSITION,3,GL_FLOAT,GL_FALSE,0);

    setGLBufferData(_squareColors,4 * sizeof(Color4F),1);
    glVertexAttribPointer(GLProgram::VERTEX_ATTRIB_COLOR,4,0);
#else
    
    //找到顶点的索引 _noMVPVertices为世界坐标中的四个顶点的值,存在了cpu中,没有存到显存
    glVertexAttribPointer(GLProgram::VERTEX_ATTRIB_POSITION,3,0,_noMVPVertices);

    glVertexAttribPointer(GLProgram::VERTEX_ATTRIB_COLOR,_squareColors);
#endif // EMSCRIPTEN
   
    //混合,源和目标 颜色的混合
    GL::blendFunc( _blendFunc.src,_blendFunc.dst );
    
    //画这四个点
    glDrawArrays(GL_TRIANGLE_STRIP,4);

    //这是记录图元和顶点吗
   // CC_INCREMENT_GL_DRAWN_BATCHES_AND_VERTICES(1,4);
    auto __renderer__ = Director::getInstance()->getRenderer();
    __renderer__->addDrawnBatches(1);
    __renderer__->addDrawnVertices(4);
    
}

LayerColor绘制过程比较简单,没有纹理设置,只有顶点和颜色,通过glDrawArrays绘制完成

相关文章

    本文实践自 RayWenderlich、Ali Hafizji 的文章《...
Cocos-code-ide使用入门学习地点:杭州滨江邮箱:appdevzw@1...
第一次開始用手游引擎挺激动!!!进入正题。下载资源1:从C...
    Cocos2d-x是一款强大的基于OpenGLES的跨平台游戏开发...
1.  来源 QuickV3sample项目中的2048样例游戏,以及最近《...
   Cocos2d-x3.x已经支持使用CMake来进行构建了,这里尝试...