diff --git a/3rdParty/msdf/CMakeLists.txt b/3rdParty/msdf/CMakeLists.txt index 7a2317f..767c2bf 100644 --- a/3rdParty/msdf/CMakeLists.txt +++ b/3rdParty/msdf/CMakeLists.txt @@ -36,7 +36,7 @@ if (WIN32) set(TRIPLET x64-windows-static-md-release CACHE INTERNAL "triplet") elseif(UNIX AND NOT APPLE) set(TRIPLET x64-linux CACHE INTERNAL "triplet") -elseif(APPLE) +elseif(APPLE AND NOT IOS) set(TRIPLET arm64-osx CACHE INTERNAL "triplet") elseif(IOS) set(TRIPLET arm64-ios CACHE INTERNAL "triplet") diff --git a/examples/ExampleApps/TextExampleApp.cpp b/examples/ExampleApps/TextExampleApp.cpp index 82ac220..bd64add 100644 --- a/examples/ExampleApps/TextExampleApp.cpp +++ b/examples/ExampleApps/TextExampleApp.cpp @@ -63,39 +63,20 @@ namespace OpenVulkano auto& resourceLoader = ResourceLoader::GetInstance(); const std::string fontPath = resourceLoader.GetResourcePath("Roboto-Regular.ttf"); const std::string atlasPath = (fs::path(fontPath).parent_path() / "roboto-regular-atlas.png").string(); - m_nodesPool.resize(N); m_drawablesPool.resize(N); - m_uniBuffers.resize(N); - - for (int i = 0; i < N; i++) - { - m_uniBuffers[i].Init(sizeof(TextConfig), &texts[i].second, 3); - } - - m_shader.AddShaderProgram(OpenVulkano::ShaderProgramType::VERTEX, "Shader/text"); - m_shader.AddShaderProgram(OpenVulkano::ShaderProgramType::FRAGMENT, "Shader/text"); - m_shader.AddVertexInputDescription(OpenVulkano::Vertex::GetVertexInputDescription()); - m_shader.AddDescriptorSetLayoutBinding(Texture::DESCRIPTOR_SET_LAYOUT_BINDING); - m_shader.AddDescriptorSetLayoutBinding(UniformBuffer::DESCRIPTOR_SET_LAYOUT_BINDING); - m_shader.alphaBlend = true; - m_shader.cullMode = CullMode::NONE; - + Charset charset = Charset::ASCII; for (unicode_t c = 0x0410; c <= 0x041F; c++) { // some unicode values charset.add(c); } - m_atlasGenerator.GenerateAtlas(fontPath, atlasPath, charset); + m_atlasGenerator.GenerateAtlas(fontPath, charset); for (int i = 0; i < texts.size(); i++) { - TextDrawable* t = new TextDrawable(); - t->SetFontAtlasGenerator(&m_atlasGenerator); - t->SetConfig(texts[i].second); - t->SetUniformBuffer(&m_uniBuffers[i]); - t->SetShader(&m_shader); + TextDrawable* t = new TextDrawable(&m_atlasGenerator, texts[i].second); t->GenerateText(texts[i].first); m_drawablesPool[i] = t; m_nodesPool[i].Init(); @@ -131,9 +112,7 @@ namespace OpenVulkano private: OpenVulkano::Scene::Scene m_scene; PerspectiveCamera m_cam; - std::vector m_uniBuffers; OpenVulkano::FreeCamCameraController m_camController; - Shader m_shader; FontAtlasGenerator m_atlasGenerator; std::vector m_drawablesPool; std::vector m_nodesPool; diff --git a/openVulkanoCpp/Image/ImageLoader.cpp b/openVulkanoCpp/Image/ImageLoader.cpp index e6d847a..d05d4ec 100644 --- a/openVulkanoCpp/Image/ImageLoader.cpp +++ b/openVulkanoCpp/Image/ImageLoader.cpp @@ -4,8 +4,6 @@ * file, You can obtain one at https://mozilla.org/MPL/2.0/. */ -#pragma once - #include "ImageLoader.hpp" #include "Base/Logger.hpp" #include diff --git a/openVulkanoCpp/Scene/FontAtlasGenerator.cpp b/openVulkanoCpp/Scene/FontAtlasGenerator.cpp index eb6d662..7a8c4c2 100644 --- a/openVulkanoCpp/Scene/FontAtlasGenerator.cpp +++ b/openVulkanoCpp/Scene/FontAtlasGenerator.cpp @@ -12,36 +12,47 @@ namespace OpenVulkano::Scene using namespace msdfgen; using namespace msdf_atlas; - void FontAtlasGenerator::GenerateAtlas(const std::string& fontFile, const std::string& outputFile, const Charset& chset) + void FontAtlasGenerator::GenerateAtlas(const std::string& fontFile, const Charset& charset, const std::optional& pngOutput) { - if (chset.empty()) + if (charset.empty()) { + Logger::RENDER->info("Provided charset is empty. Atlas will not be generated"); return; } // TODO: dynamic atlas and add only those symbols which are not present yet in current atlas - Charset absentSymbols; - for (auto c : chset) + FreetypeHandle* ft = initializeFreetype(); + if (!ft) { throw std::runtime_error("Failed to initialize freetype"); } + FontHandle* font = loadFont(ft, fontFile.data()); + if (!font) { - if (!m_symbols.contains(c)) - { - absentSymbols.add(c); - } + deinitializeFreetype(ft); + throw std::runtime_error(fmt::format("Failed to load font from file {0}", fontFile.data())); } - if (m_loadedFont == fontFile && absentSymbols.empty()) + Generate(ft, font, charset, pngOutput); + } + + void FontAtlasGenerator::GenerateAtlas(const msdfgen::byte* fontData, int length, const Charset& charset, + const std::optional& pngOutput) + { + FreetypeHandle* ft = initializeFreetype(); + if (!ft) { throw std::runtime_error("Failed to initialize freetype"); } + FontHandle* font = loadFontData(ft, fontData, length); + if (!font) { - return; + deinitializeFreetype(ft); + throw std::runtime_error("Failed to load font data from given buffer"); } + Generate(ft, font, charset, pngOutput); + } + + void FontAtlasGenerator::Generate(FreetypeHandle* ft, FontHandle* font, const Charset& chset, + const std::optional& pngOutput) + { m_symbols.clear(); - m_loadedFont = fontFile; - std::vector glyphsGeometry; - std::pair handlers = GetHandlers(fontFile); - FreetypeHandle* ft = handlers.first; - FontHandle* font = handlers.second; - // FontGeometry is a helper class that loads a set of glyphs from a single font. FontGeometry fontGeometry(&glyphsGeometry); - fontGeometry.loadCharset(font, 1, absentSymbols); + fontGeometry.loadCharset(font, 1, chset); TightAtlasPacker packer; packer.setDimensionsConstraint(DimensionsConstraint::SQUARE); @@ -66,29 +77,15 @@ namespace OpenVulkano::Scene m_atlasTex.size = storage.width * storage.height * 1; // 1 channel for (const auto& glyph: glyphsGeometry) { - GlyphInfo info; + GlyphInfo& info = m_symbols[glyph.getCodepoint()]; info.geometry = glyph; info.glyphBox = m_generator.getLayout()[idx++]; - m_symbols[glyph.getCodepoint()] = std::move(info); } - savePng(m_generator.atlasStorage(), outputFile.c_str()); + if (pngOutput && !pngOutput->empty()) + { + savePng(m_generator.atlasStorage(), pngOutput->c_str()); + } destroyFont(font); deinitializeFreetype(ft); } - - std::pair FontAtlasGenerator::GetHandlers(const std::string& fontFile) - { - FreetypeHandle* ft = initializeFreetype(); - if (!ft) - { - throw std::runtime_error("Failed to initialize freetype"); - } - FontHandle* font = loadFont(ft, fontFile.data()); - if (!font) - { - deinitializeFreetype(ft); - throw std::runtime_error(fmt::format("Failed to load font from file {0}", fontFile.data())); - } - return { ft, font }; - } } diff --git a/openVulkanoCpp/Scene/FontAtlasGenerator.hpp b/openVulkanoCpp/Scene/FontAtlasGenerator.hpp index 6c14640..d7912e4 100644 --- a/openVulkanoCpp/Scene/FontAtlasGenerator.hpp +++ b/openVulkanoCpp/Scene/FontAtlasGenerator.hpp @@ -7,6 +7,7 @@ #pragma once #include +#include #include #include "Scene/Texture.hpp" #include "msdfgen.h" @@ -28,15 +29,18 @@ namespace OpenVulkano::Scene class FontAtlasGenerator { public: - void GenerateAtlas(const std::string& fontFile, const std::string& outputFile, const Charset& = Charset::ASCII); + void GenerateAtlas(const std::string& fontFile, const Charset& charset = Charset::ASCII, + const std::optional& pngOutput = std::nullopt); + void GenerateAtlas(const msdfgen::byte* fontData, int length, const Charset& charset = Charset::ASCII, + const std::optional& pngOutput = std::nullopt); const Texture& GetAtlas() const { return m_atlasTex; } std::map& GetAtlasInfo() { return m_symbols; } private: - std::pair GetHandlers(const std::string& fontFile); + void Generate(FreetypeHandle* ft, FontHandle* font, const Charset& chset, + const std::optional& pngOutput); private: ImmediateAtlasGenerator> m_generator; Texture m_atlasTex; std::map m_symbols; - std::string m_loadedFont; }; } \ No newline at end of file diff --git a/openVulkanoCpp/Scene/SimpleDrawable.hpp b/openVulkanoCpp/Scene/SimpleDrawable.hpp index fe14d00..231a87b 100644 --- a/openVulkanoCpp/Scene/SimpleDrawable.hpp +++ b/openVulkanoCpp/Scene/SimpleDrawable.hpp @@ -16,7 +16,6 @@ namespace OpenVulkano::Scene class SimpleDrawable : public Drawable { - protected: Geometry* m_mesh = nullptr; Material* m_material = nullptr; UniformBuffer* m_uniBuffer = nullptr; diff --git a/openVulkanoCpp/Scene/TextDrawable.cpp b/openVulkanoCpp/Scene/TextDrawable.cpp index ff3cfc3..613efc2 100644 --- a/openVulkanoCpp/Scene/TextDrawable.cpp +++ b/openVulkanoCpp/Scene/TextDrawable.cpp @@ -11,51 +11,51 @@ #include "Scene/UniformBuffer.hpp" #include "Scene/FontAtlasGenerator.hpp" #include "Base/Logger.hpp" -#include "utf8.h" -#include "fmt/core.h" +#include namespace OpenVulkano::Scene { using namespace msdfgen; using namespace msdf_atlas; - TextDrawable::~TextDrawable() - { - delete m_mesh; - delete m_material; + 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 (!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); + m_geometry.Close(); + m_geometry.Init(text.size() * 4, text.size() * 6); + const Texture& atlasTex = *m_material.texture; struct Bbox { @@ -89,34 +89,31 @@ namespace OpenVulkano::Scene 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); + 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; @@ -127,5 +124,6 @@ namespace OpenVulkano::Scene Logger::RENDER->error("Could not find glyph for character {}", c); } } + SimpleDrawable::Init(m_shader, &m_geometry, &m_material, &m_uniBuffer); } } \ No newline at end of file diff --git a/openVulkanoCpp/Scene/TextDrawable.hpp b/openVulkanoCpp/Scene/TextDrawable.hpp index 4b09a72..20453b0 100644 --- a/openVulkanoCpp/Scene/TextDrawable.hpp +++ b/openVulkanoCpp/Scene/TextDrawable.hpp @@ -13,6 +13,10 @@ #include "SimpleDrawable.hpp" #include "FontAtlasGenerator.hpp" #include "Texture.hpp" +#include "Material.hpp" +#include "Geometry.hpp" +#include "UniformBuffer.hpp" +#include "Base/Logger.hpp" #include "msdfgen.h" #include "msdfgen-ext.h" #include "msdf-atlas-gen/msdf-atlas-gen.h" @@ -37,16 +41,30 @@ namespace OpenVulkano::Scene class TextDrawable : public SimpleDrawable { public: - TextDrawable() = default; - ~TextDrawable(); - void SetUniformBuffer(UniformBuffer* buffer) { m_uniBuffer = buffer; } + static Shader& GetDefaultShader(); + TextDrawable(FontAtlasGenerator* fontAtlasGenerator, const TextConfig& config = TextConfig()); void GenerateText(const std::string& text, const Math::Vector3f& pos = Math::Vector3f(0.f)); void SetConfig(const TextConfig& cfg) { m_cfg = cfg; } + void SetShader(Shader* shader) { m_shader = shader; } TextConfig& GetConfig() { return m_cfg; } - void SetFontAtlasGenerator(FontAtlasGenerator* fontAtlasGenerator) { m_fontAtlasGenerator = fontAtlasGenerator; } + Shader* GetShader() { return m_shader; } + void SetFontAtlasGenerator(FontAtlasGenerator* fontAtlasGenerator) + { + if (!fontAtlasGenerator || fontAtlasGenerator->GetAtlasInfo().empty()) + { + Logger::RENDER->error("FontAtlasGenerator is either nullptr or doesn't contain glyphs info"); + return; + } + m_fontAtlasGenerator = fontAtlasGenerator; + m_material.texture = const_cast(&m_fontAtlasGenerator->GetAtlas()); + } FontAtlasGenerator* GetFontAtlasGenerator() { return m_fontAtlasGenerator; } private: + Geometry m_geometry; + Material m_material; + UniformBuffer m_uniBuffer; FontAtlasGenerator* m_fontAtlasGenerator = nullptr; + Shader* m_shader = &GetDefaultShader(); TextConfig m_cfg; }; }