Files
OpenVulkano/openVulkanoCpp/Scene/Prefabs/LabelDrawable.cpp
2025-01-26 18:16:18 +01:00

118 lines
4.3 KiB
C++

/*
* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at https://mozilla.org/MPL/2.0/.
*/
#include "LabelDrawable.hpp"
#include "Scene/TextDrawable.hpp"
#include "Scene/DrawEncoder.hpp"
#include "Scene/Vertex.hpp"
#include "Scene/Shader/Shader.hpp"
#include "Scene/Text/IFontAtlasGenerator.hpp"
#include "Scene/Text/FontAtlasType.hpp"
#include <optional>
namespace OpenVulkano::Scene
{
namespace
{
Shader MakeLabelBgShader(const bool billboard)
{
Shader backgroundShader;
backgroundShader.AddShaderProgram(ShaderProgramType::VERTEX, billboard ? "Shader/labelBillboard" : "Shader/label");
backgroundShader.AddShaderProgram(ShaderProgramType::FRAGMENT, "Shader/label");
backgroundShader.AddDescriptorSetLayoutBinding(Texture::DESCRIPTOR_SET_LAYOUT_BINDING, 2);
backgroundShader.AddDescriptorSetLayoutBinding(UniformBuffer::DESCRIPTOR_SET_LAYOUT_BINDING, 4);
backgroundShader.topology = Topology::TRIANGLE_STRIP;
backgroundShader.cullMode = CullMode::NONE;
return backgroundShader;
}
Shader MakeLabelTextShader(const FontAtlasType type, const bool billboard)
{
Shader shader = TextDrawable::MakeDefaultShader(type);
shader.depthCompareOp = CompareOp::LESS_OR_EQUAL;
shader.EnableDepthBias();
if (billboard)
{
for (auto& program : shader.shaderPrograms)
{
if (program.type == ShaderProgramType::VERTEX)
{
program.name = "Shader/textBillboard";
break;
}
}
DescriptorSetLayoutBinding billboardUniformBinding = UniformBuffer::DESCRIPTOR_SET_LAYOUT_BINDING;
shader.AddDescriptorSetLayoutBinding(billboardUniformBinding, 4);
shader.depthBiasConstant = 0.01f;
}
return shader;
}
Shader BACKGROUND_SHADER = MakeLabelBgShader(false);
Shader BACKGROUND_BILLBOARD_SHADER = MakeLabelBgShader(true);
std::array TEXT_SHADERS = {
MakeLabelTextShader(FontAtlasType::SDF, false),
MakeLabelTextShader(FontAtlasType::SDF, true),
MakeLabelTextShader(FontAtlasType::MSDF, false),
MakeLabelTextShader(FontAtlasType::MSDF, true),
MakeLabelTextShader(FontAtlasType::BITMAP, false),
MakeLabelTextShader(FontAtlasType::BITMAP, true),
};
Shader* GetTextShader(const FontAtlasType type, const bool billboard)
{
return &TEXT_SHADERS[static_cast<int>(type) << 1 | billboard];
}
}
LabelDrawable::LabelDrawable(const std::shared_ptr<FontAtlas>& atlasData, const LabelDrawableSettings& settings)
: Drawable(DrawEncoder::GetDrawEncoder<LabelDrawable>(), DrawPhase::MAIN), m_atlasData(atlasData)
, m_labelBuffer(sizeof(LabelUniformData), &m_labelData, 4)
{
if (!atlasData || !*atlasData) throw std::runtime_error("Can't create label drawable. Either glyphs or texture is empty");
SetLabelSettings(settings);
SetShader(IsBillboard() ? &BACKGROUND_BILLBOARD_SHADER : &BACKGROUND_SHADER);
}
void LabelDrawable::SetLabelSettings(const LabelDrawableSettings& settings)
{
m_settings = settings;
m_labelData.color = settings.backgroundColor;
m_labelData.hasRoundedCorners = settings.hasRoundedCorners;
m_labelData.hasArrow = settings.hasArrow;
m_labelData.cornerRadius = settings.cornerRadius * settings.cornerRadius;
m_labelData.arrowLength = settings.arrowLength;
m_labelData.arrowWidth = settings.arrowWidth;
}
void LabelDrawable::AddText(const std::string& text, const TextConfig& config)
{
if (text.empty()) return;
TextDrawable& textDrawable = m_texts.emplace_back(m_atlasData, config);
textDrawable.GetConfig().backgroundColor.a = 0; // do not render glyph's background
double lineHeight = m_atlasData->GetLineHeight();
textDrawable.GenerateText(text, m_position);
textDrawable.SetShader(GetTextShader(m_atlasData->GetAtlasType(), IsBillboard()));
m_bbox.Grow(textDrawable.GetBoundingBox());
// update position for next text entry
m_position.y = m_bbox.GetMin().y - lineHeight;
Math::Vector2f padding = m_settings.padding * 2;
if (m_settings.hasArrow) padding.y += m_settings.arrowLength;
m_labelData.textSize = m_bbox.GetSize() + padding;
m_labelData.bboxCenter = m_bbox.GetCenter();
if (m_settings.hasArrow) m_labelData.bboxCenter.y -= m_settings.arrowLength * 0.5f;
}
std::optional<RayHit> LabelDrawable::Intersect(const Ray& ray) const
{
return ray.IntersectAABB(m_bbox);
}
}