Files
OpenVulkano/openVulkanoCpp/Scene/Prefabs/LabelDrawable.cpp
2025-01-05 17:36:19 +01:00

151 lines
5.4 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/IFontAtlasGenerator.hpp"
#include "Base/Logger.hpp"
#include <optional>
namespace OpenVulkano::Scene
{
namespace
{
Shader MakeLabelBgShader(const bool billboard)
{
Shader backgroundShader;
if (!billboard)
{
backgroundShader.AddShaderProgram(ShaderProgramType::VERTEX, "Shader/label");
}
else
{
backgroundShader.AddShaderProgram(ShaderProgramType::VERTEX, "Shader/labelBillboard");
// binding for billboard's buffer
DescriptorSetLayoutBinding binding = UniformBuffer::DESCRIPTOR_SET_LAYOUT_BINDING;
binding.stageFlags = ShaderProgramType::Type::VERTEX;
backgroundShader.AddDescriptorSetLayoutBinding(binding, 4);
}
backgroundShader.AddShaderProgram(ShaderProgramType::FRAGMENT, "Shader/label");
backgroundShader.AddDescriptorSetLayoutBinding(Texture::DESCRIPTOR_SET_LAYOUT_BINDING, 2);
backgroundShader.AddDescriptorSetLayoutBinding(UniformBuffer::DESCRIPTOR_SET_LAYOUT_BINDING, 5);
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;
if (billboard)
{
for (auto& program : shader.shaderPrograms)
{
if (program.type == ShaderProgramType::VERTEX)
{
program.name = "Shader/textBillboard";
break;
}
}
DescriptorSetLayoutBinding billboardUniformBinding = UniformBuffer::DESCRIPTOR_SET_LAYOUT_BINDING;
billboardUniformBinding.stageFlags = ShaderProgramType::Type::VERTEX;
shader.AddDescriptorSetLayoutBinding(billboardUniformBinding, 4);
}
shader.EnableDepthBias();
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<AtlasData>& atlasData, const LabelDrawableSettings& settings, const bool isBillboard)
: Drawable(DrawEncoder::GetDrawEncoder<LabelDrawable>(), DrawPhase::MAIN), m_atlasData(atlasData), m_isBillboard(isBillboard)
{
if (atlasData->glyphs.empty() || !atlasData->texture.size)
{
throw std::runtime_error("Can't create label drawable. Either glyphs or texture is empty");
}
SetLabelSettings(settings);
SetShader(IsBillboard() ? &BACKGROUND_BILLBOARD_SHADER : &BACKGROUND_SHADER);
SetupBuffers();
}
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->meta.lineHeight;
textDrawable.GenerateText(text, m_position);
textDrawable.SetShader(GetTextShader(m_atlasData->meta.atlasType, m_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 = Math::Vector2f(m_bbox.GetSize()) + padding * 2;
m_labelData.bboxCenter = { m_bbox.GetCenter() };
if (m_settings.hasArrow) m_labelData.bboxCenter.y -= m_settings.arrowLength;
}
void LabelDrawable::SetBillboardSettings(const BillboardControlBlock& settings)
{
m_billboardSettings = settings;
}
std::optional<RayHit> LabelDrawable::Intersect(const Ray& ray) const
{
return ray.IntersectAABB(m_bbox);
}
void LabelDrawable::SetupBuffers()
{
m_billboardBuffer.size = sizeof(BillboardControlBlock);
m_billboardBuffer.data = &m_billboardSettings;
m_billboardBuffer.setId = 4;
DescriptorSetLayoutBinding binding = UniformBuffer::DESCRIPTOR_SET_LAYOUT_BINDING;
binding.stageFlags = ShaderProgramType::Type::VERTEX;
m_billboardBuffer.binding = binding;
m_labelBuffer.size = sizeof(LabelUniformData);
m_labelBuffer.data = &m_labelData;
m_labelBuffer.setId = 5;
m_labelBuffer.binding = UniformBuffer::DESCRIPTOR_SET_LAYOUT_BINDING;
}
}