当前位置: 首页 > 工具软件 > Glow Label > 使用案例 >

cocos2d-x 3.0 Label创建到渲染(TTF)2021-06-21

朱翔
2023-12-01

cocos2d 3.0 Label创建到渲染(TTF)

Cocos2d 中的ttf文字渲染本质上是根据ttfconfig配置生成对应属性的图片,最终对图片进行渲染。

一.创建Label

1.创建label通过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;
}

2. 设置ttfConfig中的各属性

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

3.根据ttfConfig生成对应的Atlas

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

二.渲染Label

Cocos2d 3.0使用render进行渲染,每帧执行director的visit函数,递归调用子节点的visit,同时向render中添加command, 在每帧结束之前集中渲染。

1.label的visit执行

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

2.渲染该节点,判断是使用系统font,还是其他格式

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

3.创建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); // 使用onDraw创建回调函数

            renderer->addCommand(&_customCommand); // 将回调函数加入到render中等待帧结束前同一执行渲染。
        }
    }
}

4.对label的shadow、outline以及文字本体进行渲染

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

理解不深,有错误的地方欢迎大神们留言指点!

 类似资料: