这个类不包含具体的绘制命令,主要是cocos2dx提供用来完成分组绘制的功能。比如你有一批绘制命令他们可能类型不同,有的是BatchCommand,有的是QuadCommand类,但这些命令必须依次被执行,中间不能插入其他来源的命令。就比如RenderTexture动态创建纹理。举个例子
RenderTexture* tex = RenderTexture::create(s.width, s.height, Texture2D::PixelFormat::RGBA8888);
tex->retain();
tex->begin();
Sprite* pTempSprite1 = Sprite::createWithSpriteFrame(pOldSprite1->getDisplayFrame());
pTempSprite1->visit();
Sprite* pTempSprite2 = Sprite::createWithSpriteFrame(pOldSprite2->getDisplayFrame());
pTempSprite2->visit();
tex->end();
上面这段代码调用了两个精灵的Visit,应该对应着两个绘制命令,执行这两个命令的时候中间不能执行其他任何命令,顺序也不能变,否则得到的结果就可能不是我们想要的了。
而Render中绘制命令的执行顺序跟加入的顺序没有必然关系,有可能排个序就把这两个命令分散了。但是有了GroupCommand就能完成这个功能。
那么GroupCommand是怎么实现的呢。拿RenderTexture上没那段举例,在跟代码看看。。
void GroupCommand::init(float globalOrder)
{
_globalOrder = globalOrder;
auto manager = Director::getInstance()->getRenderer()->getGroupCommandManager();
manager->releaseGroupID(_renderQueueID);
_renderQueueID = manager->getGroupID();
}
int GroupCommandManager::getGroupID()
{
//Reuse old id
for(auto it = _groupMapping.begin(); it != _groupMapping.end(); ++it)
{
if(!it->second)
{
_groupMapping[it->first] = true;
return it->first;
}
}
//Create new ID
// int newID = _groupMapping.size();
int newID = Director::getInstance()->getRenderer()->createRenderQueue();
_groupMapping[newID] = true;
return newID;
}
int Renderer::createRenderQueue()
{
RenderQueue newRenderQueue;
_renderGroups.push_back(newRenderQueue);
return (int)_renderGroups.size() - 1;
}
GroupCommand在初始化的时候会在Render里_renderGroups栈顶创建一个RenderQueue,renderGroups是一个RenderQueue容器。
在RenderTexture的begin里就会调用GroupCommand的初始化函数
void RenderTexture::begin()
{
Director* director = Director::getInstance();
CCASSERT(nullptr != director, "Director is null when seting matrix stack");
director->pushMatrix(MATRIX_STACK_TYPE::MATRIX_STACK_PROJECTION);
_projectionMatrix = director->getMatrix(MATRIX_STACK_TYPE::MATRIX_STACK_PROJECTION);
director->pushMatrix(MATRIX_STACK_TYPE::MATRIX_STACK_MODELVIEW);
_transformMatrix = director->getMatrix(MATRIX_STACK_TYPE::MATRIX_STACK_MODELVIEW);
if(!_keepMatrix)
{
director->setProjection(director->getProjection());
const Size& texSize = _texture->getContentSizeInPixels();
// Calculate the adjustment ratios based on the old and new projections
Size size = director->getWinSizeInPixels();
float widthRatio = size.width / texSize.width;
float heightRatio = size.height / texSize.height;
Mat4 orthoMatrix;
Mat4::createOrthographicOffCenter((float)-1.0 / widthRatio, (float)1.0 / widthRatio, (float)-1.0 / heightRatio, (float)1.0 / heightRatio, -1, 1, &orthoMatrix);
director->multiplyMatrix(MATRIX_STACK_TYPE::MATRIX_STACK_PROJECTION, orthoMatrix);
}
_groupCommand.init(_globalZOrder);
Renderer *renderer = Director::getInstance()->getRenderer();
renderer->addCommand(&_groupCommand);
renderer->pushGroup(_groupCommand.getRenderQueueID());
_beginCommand.init(_globalZOrder);
_beginCommand.func = CC_CALLBACK_0(RenderTexture::onBegin, this);
Director::getInstance()->getRenderer()->addCommand(&_beginCommand);
}
于是我们就在renderGroups栈顶创建了一个新的RenderQueue,而之后的在RenderTexture调用end之前的命令全部会按顺序加入到这个新的RenderQueue,如果这些命令的
_globalOrder没有进行特殊赋值,那他们的绘制顺序也不会改变。最后调用RenderTexture的end,弹出栈顶。
void RenderTexture::end()
{
_endCommand.init(_globalZOrder);
_endCommand.func = CC_CALLBACK_0(RenderTexture::onEnd, this);
Director* director = Director::getInstance();
CCASSERT(nullptr != director, "Director is null when seting matrix stack");
Renderer *renderer = director->getRenderer();
renderer->addCommand(&_endCommand);
renderer->popGroup();
director->popMatrix(MATRIX_STACK_TYPE::MATRIX_STACK_PROJECTION);
director->popMatrix(MATRIX_STACK_TYPE::MATRIX_STACK_MODELVIEW);
}
之后非这个RenderTexture的绘制命令将不会进入这个RenderQueue,也就不会影响他的渲染。
如果有新的RenderTexture开始创建动态纹理会怎样呢?
Render里面有一个stack<int> _commandGroupStack,每次GroupCommand初始化之后创建完RenderQueue,压入renderGroups栈顶,当加入这个GroupCommand命令的时候renderGroups栈顶应该是原先的值,所以GroupCommand不会被加入到新建的RenderQueue中,等加入完之后,RenderTexture就将新的RenderQueue的索引加入到
commandGroupStack中
_groupCommand.init(_globalZOrder);
Renderer *renderer = Director::getInstance()->getRenderer();
renderer->addCommand(&_groupCommand);
renderer->pushGroup(_groupCommand.getRenderQueueID());
接下来的非GroupCommand就都会放在心的RenderQueue中,RenderTexture的end,将索引弹出栈顶,而GroupCommand中保存着新RenderQueue的索引,当执行到
GroupCommand时,它会找到对应的RenderQueue,然后执行这个RenderQueue里面的命令
以下是添加了20个Sprite后有用同一个RenderTexture进行了两次纹理创建后_renderGroups的结构图