cocos2d 3.0 Label创建到渲染(TTF)
Cocos2d 中的ttf文字渲染本质上是根据ttfconfig配置生成对应属性的图片,最终对图片进行渲染。
Label* Label::createWithTTF(const std::string& text, const std::string& fontFile, float fontSize, const Size& dimensions /* = Size::ZERO */, TextHAlignment hAlignment /* = TextHAlignment::LEFT */, TextVAlignment vAlignment /* = TextVAlignment::TOP */)
{
auto ret = new (std::nothrow) Label(hAlignment,vAlignment);
if (ret && ret->initWithTTF(text, fontFile, fontSize, dimensions, hAlignment, vAlignment)) // 生成label
{
ret->autorelease();
return ret;
}
CC_SAFE_DELETE(ret);
return nullptr;
}
bool Label::initWithTTF(const std::string& text,
const std::string& fontFilePath, float fontSize,
const Size& dimensions,
TextHAlignment /*hAlignment*/, TextVAlignment /*vAlignment*/)
{
if (FileUtils::getInstance()->isFileExist(fontFilePath))
{
TTFConfig ttfConfig(fontFilePath, fontSize, GlyphCollection::DYNAMIC);
if (setTTFConfig(ttfConfig))
{
setDimensions(dimensions.width, dimensions.height);
setString(text); // 设置字符串
}
return true;
}
return false;
}
bool Label::setTTFConfig(const TTFConfig& ttfConfig)
{
_originalFontSize = ttfConfig.fontSize;
return setTTFConfigInternal(ttfConfig);
}
bool Label::setTTFConfigInternal(const TTFConfig& ttfConfig)
{
FontAtlas *newAtlas = FontAtlasCache::getFontAtlasTTF(&ttfConfig); // 根据ttfconfig生成新的图集
if (!newAtlas)
{
reset();
return false;
}
_currentLabelType = LabelType::TTF;
setFontAtlas(newAtlas,ttfConfig.distanceFieldEnabled,true); // 设置字体
_fontConfig = ttfConfig;
…
}
Cocos2d 3.0使用render进行渲染,每帧执行director的visit函数,递归调用子节点的visit,同时向render中添加command, 在每帧结束之前集中渲染。
void Label::draw(Renderer *renderer, const Mat4 &transform, uint32_t flags)
{
if (_batchNodes.empty() || _lengthOfString <= 0)
{
return;
}
// Don't do calculate the culling if the transform was not updated
bool transformUpdated = flags & FLAGS_TRANSFORM_DIRTY;
#if CC_USE_CULLING
auto visitingCamera = Camera::getVisitingCamera();
auto defaultCamera = Camera::getDefaultCamera();
if (visitingCamera == defaultCamera) {
_insideBounds = (transformUpdated || visitingCamera->isViewProjectionUpdated()) ? renderer->checkVisibility(transform, _contentSize) : _insideBounds;
}
else
{
_insideBounds = renderer->checkVisibility(transform, _contentSize);
}
if (_insideBounds)
#endif
{
if (!_shadowEnabled && (_currentLabelType == LabelType::BMFONT || _currentLabelType == LabelType::CHARMAP))
{
for (auto&& it : _letters)
{
it.second->updateTransform();
}
// ETC1 ALPHA supports for BMFONT & CHARMAP
auto textureAtlas = _batchNodes.at(0)->getTextureAtlas();
auto texture = textureAtlas->getTexture();
_quadCommand.init(_globalZOrder, texture, getGLProgramState(),
_blendFunc, textureAtlas->getQuads(), textureAtlas->getTotalQuads(), transform, flags);
renderer->addCommand(&_quadCommand);
}
else
{
_customCommand.init(_globalZOrder, transform, flags);
_customCommand.func = CC_CALLBACK_0(Label::onDraw, this, transform, transformUpdated);
renderer->addCommand(&_customCommand);
}
}
}
void Label::visit(Renderer *renderer, const Mat4 &parentTransform, uint32_t parentFlags)
{
if (! _visible || (_utf8Text.empty() && _children.empty()) )
{
return;
}
if (_systemFontDirty || _contentDirty)
{
updateContent();
}
uint32_t flags = processParentFlags(parentTransform, parentFlags);
if (!_utf8Text.empty() && _shadowEnabled && (_shadowDirty || (flags & FLAGS_DIRTY_MASK)))
{
_position.x += _shadowOffset.width;
_position.y += _shadowOffset.height;
_transformDirty = _inverseDirty = true;
_shadowTransform = transform(parentTransform); // shadow的transform
_position.x -= _shadowOffset.width;
_position.y -= _shadowOffset.height;
_transformDirty = _inverseDirty = true;
_shadowDirty = false;
}
bool visibleByCamera = isVisitableByVisitingCamera();
if (_children.empty() && !_textSprite && !visibleByCamera)
{
return;
}
// 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
_director->pushMatrix(MATRIX_STACK_TYPE::MATRIX_STACK_MODELVIEW);
_director->loadMatrix(MATRIX_STACK_TYPE::MATRIX_STACK_MODELVIEW, _modelViewTransform);
if (!_children.empty()) // 子节点不为空
{
sortAllChildren();
int i = 0;
// draw children zOrder < 0
for (auto size = _children.size(); i < size; ++i)
{
auto node = _children.at(i);
if (node && node->getLocalZOrder() < 0)
node->visit(renderer, _modelViewTransform, flags); // 遍历子节点,访问visit
else
break;
}
this->drawSelf(visibleByCamera, renderer, flags);
for (auto it = _children.cbegin() + i, itCend = _children.cend(); it != itCend; ++it)
{
(*it)->visit(renderer, _modelViewTransform, flags);
}
}
else
{
this->drawSelf(visibleByCamera, renderer, flags); // 渲染该节点
}
_director->popMatrix(MATRIX_STACK_TYPE::MATRIX_STACK_MODELVIEW);
}
void Label::drawSelf(bool visibleByCamera, Renderer* renderer, uint32_t flags)
{
if (_textSprite)
{
if (_shadowNode)
{
_shadowNode->visit(renderer, _modelViewTransform, flags);
}
_textSprite->visit(renderer, _modelViewTransform, flags);
}
else if (visibleByCamera && !_utf8Text.empty()) // 摄像机下可见并且渲染目标是string
{
draw(renderer, _modelViewTransform, flags);
}
}
void Label::draw(Renderer *renderer, const Mat4 &transform, uint32_t flags)
{
if (_batchNodes.empty() || _lengthOfString <= 0)
{
return;
}
// Don't do calculate the culling if the transform was not updated
bool transformUpdated = flags & FLAGS_TRANSFORM_DIRTY;
#if CC_USE_CULLING
auto visitingCamera = Camera::getVisitingCamera();
auto defaultCamera = Camera::getDefaultCamera();
if (visitingCamera == defaultCamera) {
_insideBounds = (transformUpdated || visitingCamera->isViewProjectionUpdated()) ? renderer->checkVisibility(transform, _contentSize) : _insideBounds;
}
else
{
_insideBounds = renderer->checkVisibility(transform, _contentSize);
}
if (_insideBounds)
#endif
{
if (!_shadowEnabled && (_currentLabelType == LabelType::BMFONT || _currentLabelType == LabelType::CHARMAP))
{
for (auto&& it : _letters)
{
it.second->updateTransform();
}
// ETC1 ALPHA supports for BMFONT & CHARMAP
auto textureAtlas = _batchNodes.at(0)->getTextureAtlas();
auto texture = textureAtlas->getTexture();
_quadCommand.init(_globalZOrder, texture, getGLProgramState(),
_blendFunc, textureAtlas->getQuads(), textureAtlas->getTotalQuads(), transform, flags);
renderer->addCommand(&_quadCommand);
}
else
{
_customCommand.init(_globalZOrder, transform, flags);
_customCommand.func = CC_CALLBACK_0(Label::onDraw, this, transform, transformUpdated); // 使用onDraw创建回调函数
renderer->addCommand(&_customCommand); // 将回调函数加入到render中等待帧结束前同一执行渲染。
}
}
}
void Label::onDraw(const Mat4& transform, bool /*transformUpdated*/)
{
auto glprogram = getGLProgram();
glprogram->use();
GL::blendFunc(_blendFunc.src, _blendFunc.dst);
if (_shadowEnabled)
{
if (_boldEnabled)
onDrawShadow(glprogram, _textColorF);
else
onDrawShadow(glprogram, _shadowColor4F); // 阴影渲染
}
glprogram->setUniformsForBuiltins(transform); // 如果对shadow进行渲染,需要还原渲染位置偏移
for (auto&& it : _letters)
{
it.second->updateTransform();
}
if (_currentLabelType == LabelType::TTF)
{
switch (_currLabelEffect) {
case LabelEffect::OUTLINE:
// draw outline of text
glprogram->setUniformLocationWith1i(_uniformEffectType, 1); // 1: outline 将uniform设置问outline渲染
glprogram->setUniformLocationWith4f(_uniformEffectColor,
_effectColorF.r, _effectColorF.g, _effectColorF.b, _effectColorF.a);
for (auto&& batchNode : _batchNodes)
{
batchNode->getTextureAtlas()->drawQuads(); // 渲染outline
}
// draw text without outline
glprogram->setUniformLocationWith1i(_uniformEffectType, 0); // 0: text 将uniform设置问文字渲染
glprogram->setUniformLocationWith4f(_uniformTextColor, _textColorF.r, _textColorF.g, _textColorF.b, _textColorF.a);
break;
case LabelEffect::GLOW:
glprogram->setUniformLocationWith4f(_uniformEffectColor,
_effectColorF.r, _effectColorF.g, _effectColorF.b, _effectColorF.a);
case LabelEffect::NORMAL:
glprogram->setUniformLocationWith4f(_uniformTextColor,
_textColorF.r, _textColorF.g, _textColorF.b, _textColorF.a);
break;
default:
break;
}
}
for (auto&& batchNode : _batchNodes)
{
batchNode->getTextureAtlas()->drawQuads(); // 渲染outline
}
}
// 阴影渲染
void Label::onDrawShadow(GLProgram* glProgram, const Color4F& shadowColor)
{
if (_currentLabelType == LabelType::TTF)
{
if (_currLabelEffect == LabelEffect::OUTLINE) // 如果使用outline, 使用outline对应的fragmentShader
{
glProgram->setUniformLocationWith1i(_uniformEffectType, 2); // 2: shadow 设置fragmentShader 中的uniform 为shadow渲染
glProgram->setUniformLocationWith4f(_uniformEffectColor, shadowColor.r, shadowColor.g, shadowColor.b, shadowColor.a); // 通过片段着色器中uniform设置shadow颜色
}
else
{
glProgram->setUniformLocationWith4f(_uniformTextColor, shadowColor.r, shadowColor.g, shadowColor.b, shadowColor.a);
if (_currLabelEffect == LabelEffect::GLOW)
{
glProgram->setUniformLocationWith4f(_uniformEffectColor, shadowColor.r, shadowColor.g, shadowColor.b, shadowColor.a);
}
}
glProgram->setUniformsForBuiltins(_shadowTransform); // 设置渲染位置为shadow的位置
for (auto&& it : _letters)
{
it.second->updateTransform(); // 父节点位置改变,letter位置需要刷新
}
for (auto&& batchNode : _batchNodes)
{
batchNode->getTextureAtlas()->drawQuads(); // 渲染文字图片
}
}
else
{
Color3B oldColor = _realColor;
GLubyte oldOPacity = _displayedOpacity;
_displayedOpacity = shadowColor.a * (oldOPacity / 255.0f) * 255;
setColor(Color3B(shadowColor));
glProgram->setUniformsForBuiltins(_shadowTransform);
for (auto&& it : _letters)
{
it.second->updateTransform();
}
for (auto&& batchNode : _batchNodes)
{
batchNode->getTextureAtlas()->drawQuads();
}
_displayedOpacity = oldOPacity;
setColor(oldColor);
}
}
理解不深,有错误的地方欢迎大神们留言指点!