/* * 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 "utf8.h" #include "fmt/core.h" namespace OpenVulkano::Scene { using namespace msdfgen; using namespace msdf_atlas; TextDrawable::~TextDrawable() { delete m_mesh; delete m_material; } void TextDrawable::GenerateText(const std::string& text, const Math::Vector3f& pos) { if (!m_fontAtlasGenerator) { Logger::RENDER->error("Can't draw text. FontAtlasGenerator is nullptr"); return; } if (m_mesh) { delete m_mesh; m_mesh = nullptr; } if (m_material) { delete m_material; m_material = nullptr; } if (text.empty()) { return; } std::map& symbols = m_fontAtlasGenerator->GetAtlasInfo(); if (symbols.empty()) { throw std::runtime_error("Glyphs are not loaded"); } m_mesh = new Geometry(); m_material = new Material(); m_mesh->freeAfterUpload = false; m_mesh->Init(text.size() * 4, text.size() * 6); 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); const Texture& atlasTex = m_fontAtlasGenerator->GetAtlas(); m_material->texture = const_cast(&atlasTex); m_mesh->vertices[vIdx].position.x = ax; m_mesh->vertices[vIdx].position.y = ay; m_mesh->vertices[vIdx].position.z = 1; m_mesh->vertices[vIdx].textureCoordinates.x = l / atlasTex.resolution.x; m_mesh->vertices[vIdx].textureCoordinates.y = b / atlasTex.resolution.y; m_mesh->vertices[vIdx + 1].position.x = ax + w; m_mesh->vertices[vIdx + 1].position.y = ay; m_mesh->vertices[vIdx + 1].position.z = 1; m_mesh->vertices[vIdx + 1].textureCoordinates.x = r / atlasTex.resolution.x; m_mesh->vertices[vIdx + 1].textureCoordinates.y = b / atlasTex.resolution.y; m_mesh->vertices[vIdx + 2].position.x = ax + w; m_mesh->vertices[vIdx + 2].position.y = ay + h; m_mesh->vertices[vIdx + 2].position.z = 1; m_mesh->vertices[vIdx + 2].textureCoordinates.x = r / atlasTex.resolution.x; m_mesh->vertices[vIdx + 2].textureCoordinates.y = t / atlasTex.resolution.y; m_mesh->vertices[vIdx + 3].position.x = ax; m_mesh->vertices[vIdx + 3].position.y = ay + h; m_mesh->vertices[vIdx + 3].position.z = 1; m_mesh->vertices[vIdx + 3].textureCoordinates.x = l / atlasTex.resolution.x; m_mesh->vertices[vIdx + 3].textureCoordinates.y = t / atlasTex.resolution.y; m_mesh->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(fmt::format("Could not find glyph for character {}", c)); } } } }