/* * 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/IFontAtlasGenerator.hpp" #include "Base/Logger.hpp" #include namespace OpenVulkano::Scene { using namespace Math; namespace { Shader MakeLabelBgShader(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 LabelDrawable::BACKGROUND_SHADER = MakeLabelBgShader(false); Shader LabelDrawable::BACKGROUND_BILLBOARD_SHADER = MakeLabelBgShader(true); LabelDrawable::LabelDrawable(const std::shared_ptr& atlasData, const LabelDrawableSettings& settings, bool isBillboard) : Drawable(DrawEncoder::GetDrawEncoder(), 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); SetupShaders(); 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); // do not render glyph's background textDrawable.GetConfig().backgroundColor.a = 0; textDrawable.SetShader(&m_textShader); double lineHeight = m_atlasData->meta.lineHeight; textDrawable.GenerateText(text, m_position); m_bbox.Grow(textDrawable.GetBoundingBox()); // update position for next text entry m_position.y = m_bbox.GetMin().y - lineHeight; const auto& min = m_bbox.GetMin(); const auto& max = m_bbox.GetMax(); Vertex v, v2, v3, v4; const float offset = 0.001; const float yOffset = m_settings.hasArrow ? m_settings.arrowLength : 0; v.position = Vector3f(min.x - m_settings.horizontalOffset, min.y - m_settings.verticalOffset - yOffset, min.z - offset); v2.position = Vector3f(max.x + m_settings.horizontalOffset, min.y - m_settings.verticalOffset - yOffset, min.z - offset); v3.position = Vector3f(max.x + m_settings.horizontalOffset, max.y + m_settings.verticalOffset, min.z - offset); v4.position = Vector3f(min.x - m_settings.horizontalOffset, max.y + m_settings.verticalOffset, min.z - offset); m_labelData.textSize.x = v2.position.x - v.position.x; m_labelData.textSize.y = v3.position.y - v.position.y; m_labelData.bboxCenter.x = (v2.position.x + v.position.x) / 2; m_labelData.bboxCenter.y = (v3.position.y + v.position.y) / 2; } void LabelDrawable::SetBillboardSettings(const BillboardControlBlock& settings) { m_billboardSettings = settings; } std::optional LabelDrawable::Intersect(const Ray& ray) const { return ray.IntersectAABB(m_bbox); } void LabelDrawable::SetupShaders() { SetShader(IsBillboard() ? &BACKGROUND_BILLBOARD_SHADER : &BACKGROUND_SHADER); FontAtlasType fontAtlasType(static_cast(m_atlasData->meta.atlasType)); if (!m_isBillboard) { m_textShader.AddShaderProgram(OpenVulkano::ShaderProgramType::VERTEX, "Shader/text"); } else { m_textShader.AddShaderProgram(OpenVulkano::ShaderProgramType::VERTEX, "Shader/billboard"); DescriptorSetLayoutBinding billboardUniformBinding = UniformBuffer::DESCRIPTOR_SET_LAYOUT_BINDING; billboardUniformBinding.stageFlags = ShaderProgramType::Type::VERTEX; m_textShader.AddDescriptorSetLayoutBinding(billboardUniformBinding, 4); } DescriptorSetLayoutBinding textUniformBinding = UniformBuffer::DESCRIPTOR_SET_LAYOUT_BINDING; textUniformBinding.stageFlags = ShaderProgramType::FRAGMENT; m_textShader.AddDescriptorSetLayoutBinding(textUniformBinding, 3); m_textShader.AddShaderProgram(OpenVulkano::ShaderProgramType::FRAGMENT, std::string(fontAtlasType.GetDefaultFragmentShader())); m_textShader.AddVertexInputDescription(OpenVulkano::Vertex::GetVertexInputDescription()); m_textShader.AddDescriptorSetLayoutBinding(Texture::DESCRIPTOR_SET_LAYOUT_BINDING, 2); m_textShader.alphaBlend = true; m_textShader.cullMode = CullMode::NONE; m_textShader.depthWrite = false; m_textShader.depthCompareOp = CompareOp::LESS_OR_EQUAL; } 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; } }