Cocos Source Code Analysis – LayerColor’s drawing process

1, first create a LayerColor

Scene *scene=Scene::create() ;

director
->runWithScene(scene);
//Goal
auto layer = LayerColor::create(Color4B(0, 255, 0, 255), 100 , 100);
//The main step is to set the _position of node span>
layer->setPosition(10,10);
scene
->addChild(layer);

2 Take a look at the initialization method of LayerColor

< span style="color: #0000ff;">bool LayerColor::initWithColor(const Color4B& color, GLfloat w, GLfloat h)

{
if (Layer::init())
{

// default blend function
//Specify blending mode
_blendFunc = BlendFunc::ALPHA_NON_PREMULTIPLIED;

/*
realColor and displayedColor record the color attributes of the element itself
The displayedColor and displayedOpacity methods are used to represent the final drawing color after superimposing with the parent element
The sprite is used to mix the father's color and its own texture. LayColor is the same by default and does not overlap colors
*/
_displayedColor.r
= _realColor.r = color.r;
_displayedColor.g
= _realColor.g = color.g;
_displayedColor.b
= _realColor.b = color.b;
_displayedOpacity
= _realOpacity = color.a;

//Four vertices initialization
for (size_t i = 0; i<sizeof(_squareVertices) / sizeof( _squareVertices[0< /span>]); i++)
{
_squareVertices[i].x
= 0.0f;
_squareVertices[i].y
= 0.0f;
}
//The colors of the four vertices are normalized, and the colors are the same Of
updateColor();
//w,h is the design resolution, set the range of vertices
setContentSize(Size(w, h));
/*
Each node has a GLProgramState instance
Find this type of 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)
{
//The one without a value is 0, that is, 0(0, 0) 1(w,0) 2 (0,h) 3(w,h)
//The drawing order is 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 &< span style="color: #000000;"> size)
{
if (! size.equals(_contentSize))
{
_contentSize
= size;
//Get the coordinates of the anchor point in the local coordinate system span>
_anchorPointInPoints = Vec2(_contentSize.width * _anchorPoint.x, _contentSize.height * _anchorPoint.y );
//Tell it to be updated
_transformUpdated = _transformDirty = _inverseDirty = _contentSizeDirty = true;
}
}

3 Director::drawScene method is a formal drawing, as follows

void Director::drawScene()

{
//Omit part of the code

glClear(GL_COLOR_BUFFER_BIT
| GL_DEPTH_BUFFER_BIT);


//Compatible with cocos2.0, ignore it for now
pushMatrix(MATRIX_STACK_TYPE::MATRIX_STACK_MODELVIEW);

// draw the scene
if (_runningScene)
{
//Official visit
_runningScene->visit(_renderer, Mat4::IDENTITY, false);//< /span>The first matrix is ​​the identity matrix

_eventDispatcher
->dispatchEvent(_eventAfterVisit);
}
//Start the real opengl
_renderer->render() ;

popMatrix(MATRIX_STACK_TYPE::MATRIX_STACK_MODELVIEW);

_totalFrames
++;

// swap buffers
if (_openGLView)
{
_openGLView
->swapBuffers();//Look at this later< /span>
}

if (_displayStats)
{
calculateMPF();
//I don’t know what it means
}
}

The red mark is not divided into starting traversal nodes, but opengl drawing is not performed. The entry code is as follows:

void Node::visit(Renderer* renderer, const Mat4 &< span style="color: #000000;">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
/*
In order to facilitate the migration to v3.0, we still support the Mat4 stack,
But it has been deprecated and your code should not rely on it
*/
Director
* director = Director::getInstance();
director
->pushMatrix(MATRIX_STACK_TYPE::MATRIX_STACK_MODELVIEW);
//Add the newly produced model view matrix to the top modeview [For the scene, there are already 3 modelviews before executing the following code]
//This code is for 2.0, which is meaningless for 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, _modelViewTransform, flags);

for(auto it=_children.cbegin()+i; it != _children.cend(); ++it)
(
*it)->visit(renderer, _modelViewTransform, flags);
}
else
{
this->draw(renderer, _modelViewTransform, flags);
}

/*
Exit the war after drawing. When it is compatible with 2.0, this is not needed now
The conversion of the matrix is ​​in the shader of the GPU
*/
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;// First pass the father’s change flag come over

//If the transposed matrix changes, mark it as dirty data, To update
flags |= (_transformUpdated? FLAGS_TRANSFORM_DIRTY: 0);
//If the size is changed, mark it as dirty data and update it
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;
}
// One of the transposed matrix and the size has changed
if(flags & FLAGS_DIRTY_MASK)
_modelViewTransform
= this->transform(parentTransform);//The node's own conversion matrix, the local coordinates of the node are multiplied by this matrix to get the world coordinates
//The update is complete, mark it as false
_transformUpdated = false;
_contentSizeDirty
= false;

return flags;
}

self->draw is to draw itself, but not really draw, but to associate itself with a drawing command Command, LayerColor’s draw method is overridden As follows:

//transform is local The matrix from coordinates to world coordinates

void LayerColor::draw(Renderer *renderer, const Mat4 &< span style="color: #000000;">transform, uint32_t flags)
{
_customCommand.init(_globalZOrder);
//Callback function
_customCommand.func = CC_CALLBACK_0(LayerColor::onDraw, this, transform, flags);
//Put the drawing commands into the renderer
renderer->addCommand(&_customCommand);

for(int i = 0; i <4; ++i)
{
Vec4 pos;
//The design resolution coordinates of the four vertices
pos.x = _squareVertices[i].x; pos.y = _squareVertices[i].y; pos.z = _positionZ;
pos.w
= 1;// Homogeneous coordinates
//The resulting pos is the pos in world coordinates< /span>
_modelViewTransform.transformVector(&pos);
//The w of pos.w world coordinates is always 1
//This world coordinate will be multiplied by the camera matrix and Clip the matrix to get the coordinates needed for the final viewport
_noMVPVertices[i] = Vec3(pos.x,pos.y,pos.z)/pos.w;
}
}

The code adds the information that needs to be drawn into the customCommand.

4 _renderer->render(); Responsible for executing the opengl drawing commands in the command, the code is as follows:

void Renderer::render()

{

//Process render commands
//1. Sort render commands based on ID
//renderGroups includes several rederqueues, the first one is used by default,
//renderquque includes several rendercommands
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 index)
{
auto command = queue[index];
auto commandType
= command->getType();

if(RenderCommand::Type::CUSTOM_COMMAND == commandType)/ /such as LayerColor
{
flush();
auto cmd
= static_cast(command);
cmd
->execute();//LayerColor:: onDraw, start drawing directly
}


}
}

5 cmd->execute will call the callback function of customCommand, which is onDraw in LayerColor. The code is as follows:

//Call back through a custom method and transform into local coordinates to world coordinates Rotation matrix

void LayerColor::onDraw(const Mat4& transform, uint32_t flags)
{
getGLProgram()
->use();//layercolor’s tansform For the camera matrix * crop matrix
getGLProgram()->setUniformsForBuiltins(transform);//Set the global in the vertex shader The value of the variable, such as the MVP matrix
//Enable vertex coordinates and colors
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 , 0);

setGLBufferData(_squareColors,
4 * sizeof(Color4F) , 1);
glVertexAttribPointer(GLProgram::VERTEX_ATTRIB_COLOR,
4, GL_FLOAT, GL_FALSE, 0 , 0);
#else

//Find the index of the vertex _noMVPVertices is the value of the four vertices in world coordinates , Stored in the cpu but not in the video memory
glVertexAttribPointer(GLProgram::VERTEX_ATTRIB_POSITION, 3, GL_FLOAT, GL_FALSE, 0 , _noMVPVertices);

glVertexAttribPointer(GLProgram::VERTEX_ATTRIB_COLOR,
4, GL_FLOAT, GL_FALSE, 0 , _squareColors);
#endif // EMSCRIPTEN

//mixing, mixing of source and target colors
GL::blendFunc( _blendFunc.src, _blendFunc.dst );

//Draw these four points
glDrawArrays(GL_TRIANGLE_STRIP, 0, 4);

// Is this recording primitives and vertices?
// CC_INCREMENT_GL_DRAWN_BATCHES_AND_VERTICES(1,4);
auto __renderer__ = Director::getInstance()->getRenderer();
__renderer__
->addDrawnBatches(1);
__renderer__
->addDrawnVertices(4);

}

LayerColor drawing process is relatively simple, there is no texture setting, only vertices and colors, which are drawn through glDrawArrays

Scene *scene=Scene::create();

director
->runWithScene(scene);
//Goal
auto layer = LayerColor::create(Color4B(0, 255, 0, 255), 100 , 100);
//The main step is to set the _position of node span>
layer->setPosition(10,10);
scene
->addChild(layer);

bool LayerColor:: initWithColor(const Color4B& color, GLfloat w, GLfloat h)

{
if (Layer::init())
{

// default blend function
//Specify blending mode
_blendFunc = BlendFunc::ALPHA_NON_PREMULTIPLIED;

/*
realColor and displayedColor record the color attributes of the element itself
The displayedColor and displayedOpacity methods are used to represent the final drawing color after superimposing with the parent element
The sprite is used to mix the father's color and its own texture. LayColor is the same by default and does not overlap colors
*/
_displayedColor.r
= _realColor.r = color.r;
_displayedColor.g
= _realColor.g = color.g;
_displayedColor.b
= _realColor.b = color.b;
_displayedOpacity
= _realOpacity = color.a;

//Four vertices initialization
for (size_t i = 0; i<sizeof(_squareVertices) / sizeof( _squareVertices[0< /span>]); i++)
{
_squareVertices[i].x
= 0.0f;
_squareVertices[i].y
= 0.0f;
}
//The colors of the four vertices are normalized, and the colors are the same Of
updateColor();
//w,h is the design resolution, set the range of vertices
setContentSize(Size(w, h));
/*
Each node has a GLProgramState instance
Find this type of 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)
{
//The one without a value is 0, which is 0(0, 0) 1(w,0) 2 (0,h) 3(w,h)
//The drawing order is 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;
}
}

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();
//不知道啥意思
}
}

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, _modelViewTransform, flags);

for(auto it=_children.cbegin()+i; it != _children.cend(); ++it)
(
*it)->visit(renderer, _modelViewTransform, flags);
}
else
{
this->draw(renderer, _modelViewTransform, 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;
}

//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;
}
}

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(command);
cmd
->execute();//会调用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, 0);

setGLBufferData(_squareColors,
4 * sizeof(Color4F), 1);
glVertexAttribPointer(GLProgram::VERTEX_ATTRIB_COLOR,
4, GL_FLOAT, GL_FALSE, 0, 0);
#else

//找到顶点的索引 _noMVPVertices为世界坐标中的四个顶点的值,存在了cpu中,没有存到显存
glVertexAttribPointer(GLProgram::VERTEX_ATTRIB_POSITION, 3, GL_FLOAT, GL_FALSE, 0, _noMVPVertices);

glVertexAttribPointer(GLProgram::VERTEX_ATTRIB_COLOR,
4, GL_FLOAT, GL_FALSE, 0, _squareColors);
#endif // EMSCRIPTEN

//混合,源和目标 颜色的混合
GL::blendFunc( _blendFunc.src, _blendFunc.dst );

//画这四个点
glDrawArrays(GL_TRIANGLE_STRIP, 0, 4);

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

}

Leave a Comment

Your email address will not be published.