cocos2dx的渲染树
来源:互联网 发布:在线讲课软件 编辑:程序博客网 时间:2024/06/12 18:10
cocos2dx的渲染顺序是由渲染树来决定的。渲染树是由各种游戏元素按照层次关系构成的树结构,表示Cocos2d-x游戏的绘制层次。
由导演类Director控制渲染树的根节点场景Scene,场景中包含层Layer,层Layer中又包含精灵等。
层次关系如图:
按照官网开发者指南中的说法,所有节点都是存储在一个场景图_scene graph_中,_scene graph_是一个用来存储场景图形的数据结构,其实是一个树形结构。
可以通过API中的addChild()方法来创建_scene graph_场景。
在对节点之间添加调用addchild(node,Zorder,tag)的时候,就把该子节点添加到该父节点的子树中,ZOrder决定了子节点是在父节点左边的子树还是右边的子树。没有设置ZOrder的会按照添加的前后顺序决定渲染次序。
对_scene graph_进行遍历的时候,cocos2dx采用了_in-order walk_算法(按顺序)。类似于二叉树的中根遍历,先遍历左子树,然后访问根结点,最后遍历右子树。所以ZOrder值小的先渲染,ZOrder值大的后渲染,最右边的子树会最后显示到场景中,在所有节点的最上面。
接着从代码中看一下cocos2dx的渲染结构。
游戏程序从win32目录下的main.cpp开始运行
int APIENTRY _tWinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, LPTSTR lpCmdLine, int nCmdShow){ UNREFERENCED_PARAMETER(hPrevInstance); UNREFERENCED_PARAMETER(lpCmdLine); // create the application instance AppDelegate app; return Application::getInstance()->run();}
运行Application单例的run方法。继续追踪run方法
int Application::run(){ ...... while(!glview->windowShouldClose()) { QueryPerformanceCounter(&nNow); if (nNow.QuadPart - nLast.QuadPart > _animationInterval.QuadPart) { nLast.QuadPart = nNow.QuadPart; director->mainLoop(); glview->pollEvents(); } else { Sleep(0); } }......}
通过导演类来运行mainLoop方法。
void DisplayLinkDirector::mainLoop(){ if (_purgeDirectorInNextLoop) { _purgeDirectorInNextLoop = false; purgeDirector(); } else if (! _invalid) { drawScene(); // release the objects PoolManager::getInstance()->getCurrentPool()->clear(); }}
drawScene方法
void Director::drawScene(){ ...... // draw the scene if (_runningScene) { _runningScene->visit(_renderer, Mat4::IDENTITY, false); _eventDispatcher->dispatchEvent(_eventAfterVisit); } // draw the notifications node if (_notificationNode) { _notificationNode->visit(_renderer, Mat4::IDENTITY, false); } if (_displayStats) { showStats(); } _renderer->render(); _eventDispatcher->dispatchEvent(_eventAfterDraw); ......}
场景的visit方法
void Node::visit(Renderer* renderer, const Mat4 &parentTransform, uint32_t parentFlags){ ...... 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); } ......}
先获取子节点,然后递归调用子节点的visit()函数,到了没有子节点的节点,开始调用draw()函数。
在节点Node的visit方法可以看到,要完成渲染树的绘制,就要遍历根节点,然后绘制自己的左根节点,再绘制根节点,最后绘制右根节点。继续追踪节点的draw方法
void Node::draw(Renderer* renderer, const Mat4 &transform, uint32_t flags){}
居然什么都没做?原来draw是个虚函数,上面this指针调用的是其子类的draw方法,那就看Sprite的draw方法。
void Sprite::draw(Renderer *renderer, const Mat4 &transform, uint32_t flags){ // Don't do calculate the culling if the transform was not updated _insideBounds = (flags & FLAGS_TRANSFORM_DIRTY) ? renderer->checkVisibility(transform, _contentSize) : _insideBounds; if(_insideBounds) { _quadCommand.init(_globalZOrder, _texture->getName(), getGLProgramState(), _blendFunc, &_quad, 1, transform); renderer->addCommand(&_quadCommand);#if CC_SPRITE_DEBUG_DRAW _customDebugDrawCommand.init(_globalZOrder); _customDebugDrawCommand.func = CC_CALLBACK_0(Sprite::drawDebugData, this); renderer->addCommand(&_customDebugDrawCommand);#endif //CC_SPRITE_DEBUG_DRAW }}
看到draw函数在向renderer添加了两个绘制命令,其中quadCommand初始化后就可以加入到绘制命令中,customDebugDrawCommand传入了一个回调函数。再来看addCommand函数。
void Renderer::addCommand(RenderCommand* command){ int renderQueue =_commandGroupStack.top(); addCommand(command, renderQueue);}void Renderer::addCommand(RenderCommand* command, int renderQueue){ CCASSERT(!_isRendering, "Cannot add command while rendering"); CCASSERT(renderQueue >=0, "Invalid render queue"); CCASSERT(command->getType() != RenderCommand::Type::UNKNOWN_COMMAND, "Invalid Command Type"); _renderGroups[renderQueue].push_back(command);}
最终把绘制命令添加到_renderGroups数组中,然后在Renderer::render()中实现。
void Renderer::render(){ //Uncomment this once everything is rendered by new renderer //glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT); //TODO setup camera or MVP _isRendering = true; if (_glViewAssigned) { // cleanup _drawnBatches = _drawnVertices = 0; //Process render commands //1. Sort render commands based on ID for (auto &renderqueue : _renderGroups) { renderqueue.sort(); } visitRenderQueue(_renderGroups[0]); flush(); } clean(); _isRendering = false;}
Renderer::render()中实现了真正的绘制,首先清除,然后排序,最后通过visitRenderQueue方法执行_renderGroups数组中的绘制命令
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::QUAD_COMMAND == commandType) { flush3D(); auto cmd = static_cast<QuadCommand*>(command); //Batch quads if(_numQuads + cmd->getQuadCount() > VBO_SIZE) { CCASSERT(cmd->getQuadCount()>= 0 && cmd->getQuadCount() < VBO_SIZE, "VBO is not big enough for quad data, please break the quad data down or use customized render command"); //Draw batched quads if VBO is full drawBatchedQuads(); } _batchedQuadCommands.push_back(cmd); memcpy(_quads + _numQuads, cmd->getQuads(), sizeof(V3F_C4B_T2F_Quad) * cmd->getQuadCount()); convertToWorldCoordinates(_quads + _numQuads, cmd->getQuadCount(), cmd->getModelView()); _numQuads += cmd->getQuadCount(); } else if(RenderCommand::Type::GROUP_COMMAND == commandType) { flush(); int renderQueueID = ((GroupCommand*) command)->getRenderQueueID(); visitRenderQueue(_renderGroups[renderQueueID]); } else if(RenderCommand::Type::CUSTOM_COMMAND == commandType) { flush(); auto cmd = static_cast<CustomCommand*>(command); cmd->execute(); } else if(RenderCommand::Type::BATCH_COMMAND == commandType) { flush(); auto cmd = static_cast<BatchCommand*>(command); cmd->execute(); } else if (RenderCommand::Type::MESH_COMMAND == commandType) { flush2D(); auto cmd = static_cast<MeshCommand*>(command); if (_lastBatchedMeshCommand == nullptr || _lastBatchedMeshCommand->getMaterialID() != cmd->getMaterialID()) { flush3D(); cmd->preBatchDraw(); cmd->batchDraw(); _lastBatchedMeshCommand = cmd; } else { cmd->batchDraw(); } } else { CCLOGERROR("Unknown commands in renderQueue"); } }}
drawBatchedQuads()中调用OpenGL的API来进行渲染.
流程图如下:
- cocos2dx的渲染树
- cocos2dx之渲染树的绘制
- cocos2dx之渲染树的绘制
- cocos2dx之渲染树的绘制
- cocos2dX 文字的渲染
- cocos2dx的渲染机制
- cocos2dx的渲染机制
- cocos2dx下离屏渲染遇到的问题
- cocos2dx-ui的渲染机制
- cocos2dx源码分析:TrianglesCommand的合并渲染
- cocos2dx渲染指令CustomCommand的使用
- cocos2dx的渲染流程(源码走读)
- 【cocos2dx】打印渲染树,一种用于检测内存泄漏及调试的思路
- cocos2dx渲染流程
- cocos2dx 文字渲染
- Cocos2Dx之渲染流程
- cocos2dx渲染流程
- cocos2dx 离屏渲染
- java封装
- MYSQL 使用大全
- 网站压力测试工具Webbench介绍
- 【Ajax技术】JQuery的应用与高级调试技巧
- AMH使用介绍
- cocos2dx的渲染树
- <汇编语言程序设计> 课堂笔记
- OpenCV 3 CUDA小测试
- ecstore2.0的虚拟开发环境配置
- 黑马程序员---iOS基础---C语言中的位运算,文件操作等问题
- NY 448 寻找最大数【贪心】
- 实习小结七:html布局问题--关于float和clear
- Android之常用类型转换
- Qt获取组合键