/* * 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; LabelDrawable::LabelDrawable(const std::shared_ptr& atlasData, const LabelDrawableSettings& settings, bool isBillboard) : Drawable(DrawEncoder::GetDrawEncoder(), DrawPhase::MAIN) { if (atlasData->glyphs.empty() || !atlasData->texture.size) { throw std::runtime_error("Can't create label drawable. Either glyphs or texture is empty"); } m_atlasData = atlasData; m_isBillboard = isBillboard; SetLabelSettings(settings); SetupShaders(); SetupBuffers(); m_backgroundGeometry.SetFreeAfterUpload(false); } void LabelDrawable::SetLabelSettings(const LabelDrawableSettings& settings) { m_settings = settings; m_labelData.hasRoundedCorners = settings.hasRoundedCorners; m_labelData.hasArrow = settings.hasArrow; m_labelData.cornerRadius = settings.cornerRadius; m_labelData.arrowLength = settings.arrowLength; } void LabelDrawable::AddText(const std::string& text, const TextConfig& config) { if (text.empty()) { return; } // do not render glyph's background TextDrawable& textDrawable = m_texts.emplace_back(m_atlasData, config); textDrawable.GetConfig().backgroundColor.a = 0; textDrawable.SetShader(&m_textShader); double lineHeight = m_atlasData->meta.lineHeight; textDrawable.GenerateText(text, m_position); RecalculateBbox(textDrawable.GetBoundingBox()); // update position for next text entry m_position.y = m_bbox.GetMin().y - lineHeight; if (!m_settings.hasArrow) { if (m_backgroundGeometry.vertexCount != 4) { m_backgroundGeometry.Init(4, 6); uint32_t indices[6] = { 0, 1, 2, 0, 2, 3 }; m_backgroundGeometry.SetIndices(indices, 6); } } else { if (m_backgroundGeometry.vertexCount != 7) { m_backgroundGeometry.Init(7, 9); uint32_t indices[9] = { 0, 1, 2, 0, 2, 3, 4, 5, 6 }; m_backgroundGeometry.SetIndices(indices, 9); } } const auto& min = m_bbox.GetMin(); const auto& max = m_bbox.GetMax(); Vertex v, v2, v3, v4; v.color = v2.color = v3.color = v4.color = m_settings.backgroundColor; const float offset = 0.001; v.position = Vector3f(min.x - m_settings.horizontalOffset, min.y - m_settings.verticalOffset, min.z - offset); v2.position = Vector3f(max.x + m_settings.horizontalOffset, min.y - m_settings.verticalOffset, 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); if (!m_settings.hasArrow) { v.textureCoordinates = Vector3f(0, 0, 0); v2.textureCoordinates = Vector3f(1, 0, 0); v3.textureCoordinates = Vector3f(1, 1, 0); v4.textureCoordinates = Vector3f(0, 1, 0); } else { const float w = v2.position.x - v.position.x; const float h = v3.position.y - v2.position.y + m_settings.arrowLength; v.textureCoordinates = Vector3f(0, v.position.y / h, 0); v2.textureCoordinates = Vector3f(1, v2.position.y / h, 0); v3.textureCoordinates = Vector3f(1, 1, 0); v4.textureCoordinates = Vector3f(0, 1, 0); // arrow vertices Vertex a, b, c; a.position = Vector3f(v.position.x + w / 3, v.position.y, v.position.z); b.position = Vector3f(v.position.x + w / 2, v.position.y - m_settings.arrowLength, v.position.z); c.position = Vector3f(v2.position.x - w / 3, v.position.y, v.position.z); a.color = b.color = c.color = m_settings.backgroundColor; a.textureCoordinates = Vector3f(a.position.x / w, a.position.y / h, 0); b.textureCoordinates = Vector3f(b.position.x / w, 0, 0); c.textureCoordinates = Vector3f(c.position.x / w, c.position.y / h, 0); m_backgroundGeometry.vertices[4] = a; m_backgroundGeometry.vertices[5] = b; m_backgroundGeometry.vertices[6] = c; } m_backgroundGeometry.vertices[0] = v; m_backgroundGeometry.vertices[1] = v2; m_backgroundGeometry.vertices[2] = v3; m_backgroundGeometry.vertices[3] = v4; if (m_settings.hasRoundedCorners) { m_labelData.textSize.x = v2.position.x - v.position.x; m_labelData.textSize.y = v3.position.y - v.position.y; } } void LabelDrawable::SetBillboardSettings(const BillboardControlBlock& settings) { m_billboardSettings = settings; } void LabelDrawable::RecalculateBbox(const Math::AABB& other) { if (m_bbox.IsEmpty()) { m_bbox = other; } else { auto& currentMin = m_bbox.GetMin(); auto& currentMax = m_bbox.GetMax(); currentMin.x = std::min(currentMin.x, other.min.x); currentMin.y = std::min(currentMin.y, other.min.y); currentMax.x = std::max(currentMax.x, other.max.x); currentMax.y = std::max(currentMax.y, other.max.y); } } void LabelDrawable::SetupShaders() { DescriptorSetLayoutBinding binding = UniformBuffer::DESCRIPTOR_SET_LAYOUT_BINDING; if (!m_isBillboard) { m_backgroundShader.AddShaderProgram(OpenVulkano::ShaderProgramType::VERTEX, "Shader/basic"); } else { m_backgroundShader.AddShaderProgram(OpenVulkano::ShaderProgramType::VERTEX, "Shader/billboard"); // binding for billboard's buffer binding.stageFlags = ShaderProgramType::Type::VERTEX; m_backgroundShader.AddDescriptorSetLayoutBinding(binding, 4); } m_backgroundShader.AddShaderProgram(OpenVulkano::ShaderProgramType::FRAGMENT, "Shader/label"); m_backgroundShader.AddVertexInputDescription(OpenVulkano::Vertex::GetVertexInputDescription()); m_backgroundShader.AddDescriptorSetLayoutBinding(Texture::DESCRIPTOR_SET_LAYOUT_BINDING, 2); binding.stageFlags = ShaderProgramType::Type::FRAGMENT; m_backgroundShader.AddDescriptorSetLayoutBinding(binding, 5); m_backgroundShader.cullMode = CullMode::NONE; SetShader(&m_backgroundShader); 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() { DescriptorSetLayoutBinding binding = UniformBuffer::DESCRIPTOR_SET_LAYOUT_BINDING; binding.stageFlags = ShaderProgramType::Type::VERTEX; m_billboardBuffer.size = sizeof(BillboardControlBlock); m_billboardBuffer.data = &m_billboardSettings; m_billboardBuffer.setId = 4; m_billboardBuffer.binding = binding; binding.stageFlags = ShaderProgramType::Type::FRAGMENT; m_labelBuffer.size = sizeof(LabelUniformData); m_labelBuffer.data = &m_labelData; m_labelBuffer.setId = 5; m_labelBuffer.binding = binding; } }