diff --git a/examples/ExampleApps/TextExampleApp.cpp b/examples/ExampleApps/TextExampleApp.cpp index 86a01b0..a295540 100644 --- a/examples/ExampleApps/TextExampleApp.cpp +++ b/examples/ExampleApps/TextExampleApp.cpp @@ -56,6 +56,7 @@ namespace OpenVulkano texts.push_back(std::make_pair("ABab?.^{}_cdFGETG123)(", TextConfig())); texts.push_back(std::make_pair("Hello, World!", TextConfig())); texts.push_back(std::make_pair("\u0410\u0411\u0412\u041F", TextConfig())); + texts.push_back(std::make_pair("This is first line\nSecond gg line\nThird G line", TextConfig())); texts[0].second.applyBorder = true; texts[1].second.backgroundColor.a = 1; @@ -73,7 +74,7 @@ namespace OpenVulkano } #ifdef MSDFGEN_AVAILABLE - m_atlasGenerator.GenerateAtlas(fontPath, charset); + m_atlasGenerator.GenerateAtlas(fontPath, charset, "roboto-regular-atlas.png"); #endif for (int i = 0; i < texts.size(); i++) @@ -81,9 +82,8 @@ namespace OpenVulkano #ifdef MSDFGEN_AVAILABLE TextDrawable* t = new TextDrawable(&m_atlasGenerator, texts[i].second); #else - auto metadataInfoPath = resourceLoader.GetResourcePath("atlas_metadata"); + auto metadataInfo = resourceLoader.GetResource("atlas_metadata"); auto data = resourceLoader.GetResource("roboto-regular-atlas.png"); - auto metadataInfo = TextDrawable::ReadAtlasMetadataFile(metadataInfoPath); Image::ImageLoaderPng loader; static auto image = loader.loadData(reinterpret_cast(data.Data()), data.Size()); static Texture tex; diff --git a/examples/ExampleSources/atlas_metadata b/examples/ExampleSources/atlas_metadata index c130a42..8f6ea29 100644 Binary files a/examples/ExampleSources/atlas_metadata and b/examples/ExampleSources/atlas_metadata differ diff --git a/openVulkanoCpp/Scene/AtlasMetadata.hpp b/openVulkanoCpp/Scene/AtlasMetadata.hpp index 9a94f5c..99f6e7f 100644 --- a/openVulkanoCpp/Scene/AtlasMetadata.hpp +++ b/openVulkanoCpp/Scene/AtlasMetadata.hpp @@ -18,4 +18,11 @@ namespace OpenVulkano::Scene Math::Vector2f_SIMD uv[4] = {}; double advance = 0; }; + + struct AtlasMetadata + { + // vertical difference between baselines + double lineHeight = 0; + }; + } \ No newline at end of file diff --git a/openVulkanoCpp/Scene/FontAtlasGenerator.cpp b/openVulkanoCpp/Scene/FontAtlasGenerator.cpp index b693367..659ef88 100644 --- a/openVulkanoCpp/Scene/FontAtlasGenerator.cpp +++ b/openVulkanoCpp/Scene/FontAtlasGenerator.cpp @@ -55,7 +55,8 @@ namespace OpenVulkano::Scene return; } std::fstream fs(outputFile.c_str(), std::ios_base::out | std::ios_base::binary | std::ios_base::trunc); - for (const auto& [key, val] : m_symbols) + fs.write(reinterpret_cast(&m_meta), sizeof(AtlasMetadata)); + for (const auto& [key, val] : m_symbols) { fs.write(reinterpret_cast(&key), sizeof(uint32_t)); fs.write(reinterpret_cast(&val), sizeof(GlyphInfo)); @@ -92,7 +93,9 @@ namespace OpenVulkano::Scene m_atlasTex.textureBuffer = (msdfgen::byte*) storage.pixels; m_atlasTex.format = OpenVulkano::DataFormat::R8_UNORM; m_atlasTex.size = storage.width * storage.height * 1; // 1 channel - + + m_meta.lineHeight = fontGeometry.getMetrics().lineHeight; + struct Bbox { double l = 0, r = 0, t = 0, b = 0; diff --git a/openVulkanoCpp/Scene/FontAtlasGenerator.hpp b/openVulkanoCpp/Scene/FontAtlasGenerator.hpp index 2cc0173..484b1c9 100644 --- a/openVulkanoCpp/Scene/FontAtlasGenerator.hpp +++ b/openVulkanoCpp/Scene/FontAtlasGenerator.hpp @@ -30,13 +30,15 @@ namespace OpenVulkano::Scene const std::optional& pngOutput = std::nullopt); void SaveAtlasMetadataInfo(const std::string& outputFile) const; const Texture& GetAtlas() const { return m_atlasTex; } - std::map& GetAtlasInfo() { return m_symbols; } + std::map& GetGlyphsInfo() { return m_symbols; } + AtlasMetadata& GetAtlasMetadata() { return m_meta; } private: void Generate(FreetypeHandle* ft, FontHandle* font, const Charset& chset, const std::optional& pngOutput); private: ImmediateAtlasGenerator> m_generator; Texture m_atlasTex; + AtlasMetadata m_meta; std::map m_symbols; }; } diff --git a/openVulkanoCpp/Scene/TextDrawable.cpp b/openVulkanoCpp/Scene/TextDrawable.cpp index 89c653e..d2debdb 100644 --- a/openVulkanoCpp/Scene/TextDrawable.cpp +++ b/openVulkanoCpp/Scene/TextDrawable.cpp @@ -40,32 +40,33 @@ namespace OpenVulkano::Scene return textDefaultShader; } - std::map TextDrawable::ReadAtlasMetadataFile(const std::string& atlasMetadataFile) + TextDrawable::TextDrawable(const std::string& atlasMetadataFile, Texture* atlasTex, const TextConfig& config) + : TextDrawable(OpenVulkano::Utils::ReadFile(atlasMetadataFile), atlasTex, config) { - OpenVulkano::Array data = OpenVulkano::Utils::ReadFile(atlasMetadataFile); - return ReadAtlasMetadata(data); } - std::map TextDrawable::ReadAtlasMetadata(const Array& atlasMetadata) + TextDrawable::TextDrawable(const Array& atlasMetadata, Texture* atlasTex, const TextConfig& config) { size_t len = atlasMetadata.Size(); size_t read_bytes = 0; uint32_t unicode = 0; - GlyphInfo info; - std::map glyphs; + std::memcpy(&m_meta, atlasMetadata.Data(), sizeof(AtlasMetadata)); + read_bytes += sizeof(AtlasMetadata); while (read_bytes < len) { std::memcpy(&unicode, atlasMetadata.Data() + read_bytes, sizeof(uint32_t)); read_bytes += sizeof(uint32_t); + GlyphInfo& info = m_glyphs[unicode]; std::memcpy(&info, atlasMetadata.Data() + read_bytes, sizeof(GlyphInfo)); read_bytes += sizeof(GlyphInfo); - glyphs[unicode] = std::move(info); } - return glyphs; + m_material.texture = atlasTex; + m_cfg = config; + m_uniBuffer.Init(sizeof(TextConfig), &m_cfg, 3); } TextDrawable::TextDrawable(const std::map& glyphData, Texture* atlasTex, - const TextConfig& config) + const TextConfig& config) { if (!atlasTex) { throw std::runtime_error("Atlas texture is nullptr"); } if (glyphData.empty()) { throw std::runtime_error("Glyphs are not loaded"); } @@ -79,7 +80,7 @@ namespace OpenVulkano::Scene 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"); } + if (fontAtlasGenerator->GetGlyphsInfo().empty()) { throw std::runtime_error("Glyphs are not loaded"); } m_fontAtlasGenerator = fontAtlasGenerator; m_cfg = config; m_material.texture = const_cast(&m_fontAtlasGenerator->GetAtlas()); @@ -101,7 +102,7 @@ namespace OpenVulkano::Scene // TODO: better implementation to decide what to use: data from atlas generator or data read from file // we have msdf but loaded glyphs metadata from file before std::optional>> glyphData; - if (m_fontAtlasGenerator->GetAtlasInfo().empty() && !m_glyphs.empty()) + if (m_fontAtlasGenerator->GetGlyphsInfo().empty() && !m_glyphs.empty()) { // texture is set in ctor glyphData = m_glyphs; @@ -110,19 +111,29 @@ namespace OpenVulkano::Scene { // just in case if FontAtlasGenerator changed it's atlas m_material.texture = const_cast(&m_fontAtlasGenerator->GetAtlas()); - glyphData = m_fontAtlasGenerator->GetAtlasInfo(); + glyphData = m_fontAtlasGenerator->GetGlyphsInfo(); } std::map& symbols = glyphData.value(); + AtlasMetadata& meta = m_fontAtlasGenerator->GetAtlasMetadata(); #else std::map& symbols = m_glyphs; + AtlasMetadata& meta = m_meta; #endif const Texture& atlasTex = *m_material.texture; double cursorX = pos.x; auto begin = text.begin(); auto end = text.end(); + const double lineHeight = meta.lineHeight; + double posY = pos.y; for (size_t i = 0; begin != end; i++) { uint32_t c = utf8::next(begin, end); + if (c == '\n') + { + posY -= lineHeight; + cursorX = pos.x; + continue; + } if (symbols.find(c) != symbols.end()) { uint32_t vIdx = i * 4; @@ -131,25 +142,25 @@ namespace OpenVulkano::Scene // left bottom m_geometry.vertices[vIdx].position.x = info.xyz[0].x + cursorX; - m_geometry.vertices[vIdx].position.y = pos.y - info.xyz[0].y; + m_geometry.vertices[vIdx].position.y = posY - info.xyz[0].y; m_geometry.vertices[vIdx].position.z = info.xyz[0].z; m_geometry.vertices[vIdx].textureCoordinates = Math::Vector3f(info.uv[0], 0); // right bottom m_geometry.vertices[vIdx + 1].position.x = info.xyz[1].x + cursorX; - m_geometry.vertices[vIdx + 1].position.y = pos.y - info.xyz[1].y; + m_geometry.vertices[vIdx + 1].position.y = posY - info.xyz[1].y; m_geometry.vertices[vIdx + 1].position.z = info.xyz[1].z; m_geometry.vertices[vIdx + 1].textureCoordinates = Math::Vector3f(info.uv[1], 0); // top right m_geometry.vertices[vIdx + 2].position.x = info.xyz[2].x + cursorX; - m_geometry.vertices[vIdx + 2].position.y = pos.y + info.xyz[2].y; + m_geometry.vertices[vIdx + 2].position.y = posY + info.xyz[2].y; m_geometry.vertices[vIdx + 2].position.z = info.xyz[2].z; m_geometry.vertices[vIdx + 2].textureCoordinates = Math::Vector3f(info.uv[2], 0); // top left m_geometry.vertices[vIdx + 3].position.x = info.xyz[3].x + cursorX; - m_geometry.vertices[vIdx + 3].position.y = pos.y + info.xyz[3].y; + m_geometry.vertices[vIdx + 3].position.y = posY + info.xyz[3].y; m_geometry.vertices[vIdx + 3].position.z = info.xyz[3].z; m_geometry.vertices[vIdx + 3].textureCoordinates = Math::Vector3f(info.uv[3], 0); m_geometry.SetIndices(indices, 6, 6 * i); diff --git a/openVulkanoCpp/Scene/TextDrawable.hpp b/openVulkanoCpp/Scene/TextDrawable.hpp index 18634cb..1d30d70 100644 --- a/openVulkanoCpp/Scene/TextDrawable.hpp +++ b/openVulkanoCpp/Scene/TextDrawable.hpp @@ -48,8 +48,8 @@ namespace OpenVulkano::Scene { public: static Shader& GetDefaultShader(); - static std::map ReadAtlasMetadataFile(const std::string& atlasMetadataFile); - static std::map ReadAtlasMetadata(const Array& atlasMetadata); + TextDrawable(const std::string& atlasMetadataFile, Texture* atlasTex, const TextConfig& config = TextConfig()); + TextDrawable(const Array& atlasMetadata, Texture* atlasTex, const TextConfig& config = TextConfig()); TextDrawable(const std::map& glyphData, Texture* atlasTex, const TextConfig& config = TextConfig()); #ifdef MSDFGEN_AVAILABLE TextDrawable(FontAtlasGenerator* fontAtlasGenerator, const TextConfig& config = TextConfig()); @@ -62,7 +62,7 @@ namespace OpenVulkano::Scene #ifdef MSDFGEN_AVAILABLE void SetFontAtlasGenerator(FontAtlasGenerator* fontAtlasGenerator) { - if (!fontAtlasGenerator || fontAtlasGenerator->GetAtlasInfo().empty()) + if (!fontAtlasGenerator || fontAtlasGenerator->GetGlyphsInfo().empty()) { Logger::RENDER->error("FontAtlasGenerator is either nullptr or doesn't contain glyphs info"); return; @@ -76,6 +76,7 @@ namespace OpenVulkano::Scene Material m_material; UniformBuffer m_uniBuffer; std::map m_glyphs; + AtlasMetadata m_meta; #ifdef MSDFGEN_AVAILABLE FontAtlasGenerator* m_fontAtlasGenerator = nullptr; #endif