/* * 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 "TextDrawable.hpp" #include "Scene/Geometry.hpp" #include "Scene/Material.hpp" #include "Scene/Vertex.hpp" #include "Scene/UniformBuffer.hpp" #include "Scene/FontAtlasGenerator.hpp" #include "Base/Logger.hpp" #include namespace OpenVulkano::Scene { using namespace msdfgen; using namespace msdf_atlas; Shader& TextDrawable::GetDefaultShader() { static bool once = true; static Shader textDefaultShader; if (once) { textDefaultShader.AddShaderProgram(OpenVulkano::ShaderProgramType::VERTEX, "Shader/text"); textDefaultShader.AddShaderProgram(OpenVulkano::ShaderProgramType::FRAGMENT, "Shader/text"); textDefaultShader.AddVertexInputDescription(OpenVulkano::Vertex::GetVertexInputDescription()); textDefaultShader.AddDescriptorSetLayoutBinding(Texture::DESCRIPTOR_SET_LAYOUT_BINDING); textDefaultShader.AddDescriptorSetLayoutBinding(UniformBuffer::DESCRIPTOR_SET_LAYOUT_BINDING); textDefaultShader.alphaBlend = true; textDefaultShader.cullMode = CullMode::NONE; once = false; } return textDefaultShader; } TextDrawable::TextDrawable(FontAtlasGenerator* fontAtlasGenerator, const TextConfig& config) { if (!fontAtlasGenerator) { throw std::runtime_error("FontAtlasGenerator is nullptr"); } if (fontAtlasGenerator->GetAtlasInfo().empty()) { throw std::runtime_error("Glyphs are not loaded"); } m_fontAtlasGenerator = fontAtlasGenerator; m_cfg = config; m_material.texture = const_cast(&m_fontAtlasGenerator->GetAtlas()); m_uniBuffer.Init(sizeof(TextConfig), &m_cfg, 3); } void TextDrawable::GenerateText(const std::string& text, const Math::Vector3f& pos) { if (text.empty()) { return; } std::map& symbols = m_fontAtlasGenerator->GetAtlasInfo(); m_geometry.Close(); m_geometry.Init(text.size() * 4, text.size() * 6); const Texture& atlasTex = *m_material.texture; struct Bbox { double l = 0, r = 0, t = 0, b = 0; }; double cursorX = pos.x; auto begin = text.begin(); auto end = text.end(); for (size_t i = 0; begin != end; i++) { unicode_t c = utf8::next(begin, end); if (symbols.find(c) != symbols.end()) { Bbox glyphBaselineBbox, glyphAtlasBbox; uint32_t vIdx = i * 4; uint32_t indices[] = { 1 + vIdx, 2 + vIdx, 3 + vIdx, 1 + vIdx, 3 + vIdx, 0 + vIdx }; GlyphInfo& info = symbols.at(c); info.geometry.getQuadPlaneBounds(glyphBaselineBbox.l, glyphBaselineBbox.b, glyphBaselineBbox.r, glyphBaselineBbox.t); info.geometry.getQuadAtlasBounds(glyphAtlasBbox.l, glyphAtlasBbox.b, glyphAtlasBbox.r, glyphAtlasBbox.t); double bearingX = info.glyphBox.bounds.l; double bearingY = info.glyphBox.bounds.t; double w = glyphBaselineBbox.r - glyphBaselineBbox.l; double h = glyphBaselineBbox.t - glyphBaselineBbox.b; double l = glyphAtlasBbox.l; double r = glyphAtlasBbox.r; double t = glyphAtlasBbox.t; double b = glyphAtlasBbox.b; double ax = cursorX + bearingX; double ay = pos.y - (h - bearingY); m_geometry.vertices[vIdx].position.x = ax; m_geometry.vertices[vIdx].position.y = ay; m_geometry.vertices[vIdx].position.z = 1; m_geometry.vertices[vIdx].textureCoordinates.x = l / atlasTex.resolution.x; m_geometry.vertices[vIdx].textureCoordinates.y = b / atlasTex.resolution.y; m_geometry.vertices[vIdx + 1].position.x = ax + w; m_geometry.vertices[vIdx + 1].position.y = ay; m_geometry.vertices[vIdx + 1].position.z = 1; m_geometry.vertices[vIdx + 1].textureCoordinates.x = r / atlasTex.resolution.x; m_geometry.vertices[vIdx + 1].textureCoordinates.y = b / atlasTex.resolution.y; m_geometry.vertices[vIdx + 2].position.x = ax + w; m_geometry.vertices[vIdx + 2].position.y = ay + h; m_geometry.vertices[vIdx + 2].position.z = 1; m_geometry.vertices[vIdx + 2].textureCoordinates.x = r / atlasTex.resolution.x; m_geometry.vertices[vIdx + 2].textureCoordinates.y = t / atlasTex.resolution.y; m_geometry.vertices[vIdx + 3].position.x = ax; m_geometry.vertices[vIdx + 3].position.y = ay + h; m_geometry.vertices[vIdx + 3].position.z = 1; m_geometry.vertices[vIdx + 3].textureCoordinates.x = l / atlasTex.resolution.x; m_geometry.vertices[vIdx + 3].textureCoordinates.y = t / atlasTex.resolution.y; m_geometry.SetIndices(indices, 6, 6 * i); // TODO: change to lower value(or ideally remove completely) to avoid overlapping and make less space between symbols // when setting for depth comparison operator will be available( <= ) cursorX += info.glyphBox.advance + 0.08; } else { // throw ? replace with ? character (if available) ? Logger::RENDER->error("Could not find glyph for character {}", c); } } SimpleDrawable::Init(m_shader, &m_geometry, &m_material, &m_uniBuffer); } }