/* ************************************************************************** */ /* */ /* ::: :::::::: */ /* text_pipeline.cpp :+: :+: :+: */ /* +:+ +:+ +:+ */ /* By: maldavid +#+ +:+ +#+ */ /* +#+#+#+#+#+ +#+ */ /* Created: 2023/04/06 16:41:13 by maldavid #+# #+# */ /* Updated: 2023/11/23 14:26:48 by maldavid ### ########.fr */ /* */ /* ************************************************************************** */ #include #include #include #include #include #define STB_TRUETYPE_IMPLEMENTATION #include constexpr const int RANGE = 1024; namespace mlx { TextDrawData::TextDrawData(std::string _text, int _color, int _x, int _y) : x(_x), y(_y), color(_color), text(std::move(_text)) {} void TextDrawData::init(TextLibrary& library, std::array& cdata) noexcept { std::vector vertexData; std::vector indexData; float stb_x = 0.0f; float stb_y = 0.0f; for(char c : text) { if(c < 32 || c > 127) continue; stbtt_aligned_quad q; stbtt_GetBakedQuad(cdata.data(), RANGE, RANGE, c - 32, &stb_x, &stb_y, &q, 1); std::size_t index = vertexData.size(); vertexData.emplace_back(glm::vec2{q.x0, q.y0}, glm::vec4{color & 0x00FF0000, color & 0x0000FF00, color & 0x000000FF, 0xFFFFFFFF}, glm::vec2{q.s0, q.t0}); vertexData.emplace_back(glm::vec2{q.x1, q.y0}, glm::vec4{color & 0x00FF0000, color & 0x0000FF00, color & 0x000000FF, 0xFFFFFFFF}, glm::vec2{q.s1, q.t0}); vertexData.emplace_back(glm::vec2{q.x1, q.y1}, glm::vec4{color & 0x00FF0000, color & 0x0000FF00, color & 0x000000FF, 0xFFFFFFFF}, glm::vec2{q.s1, q.t1}); vertexData.emplace_back(glm::vec2{q.x0, q.y1}, glm::vec4{color & 0x00FF0000, color & 0x0000FF00, color & 0x000000FF, 0xFFFFFFFF}, glm::vec2{q.s0, q.t1}); indexData.emplace_back(index + 0); indexData.emplace_back(index + 1); indexData.emplace_back(index + 2); indexData.emplace_back(index + 2); indexData.emplace_back(index + 3); indexData.emplace_back(index + 0); } std::shared_ptr text_data = std::make_shared(); text_data->init(text, std::move(vertexData), std::move(indexData)); id = library.addTextToLibrary(text_data); #ifdef DEBUG core::error::report(e_kind::message, "Text put : registered new text to render"); #endif } void TextPutPipeline::init(Renderer* renderer) noexcept { _renderer = renderer; uint8_t tmp_bitmap[RANGE * RANGE]; uint8_t vulkan_bitmap[RANGE * RANGE * 4]; stbtt_BakeFontBitmap(dogica_ttf, 0, 6.0f, tmp_bitmap, RANGE, RANGE, 32, 96, _cdata.data()); for(int i = 0, j = 0; i < RANGE * RANGE; i++, j += 4) { vulkan_bitmap[j + 0] = tmp_bitmap[i]; vulkan_bitmap[j + 1] = tmp_bitmap[i]; vulkan_bitmap[j + 2] = tmp_bitmap[i]; vulkan_bitmap[j + 3] = tmp_bitmap[i]; } _atlas.create(vulkan_bitmap, RANGE, RANGE, VK_FORMAT_R8G8B8A8_UNORM, "__mlx_texts_pipeline_texture_atlas", true); _atlas.setDescriptor(renderer->getFragDescriptorSet().duplicate()); } void TextPutPipeline::loadFont(const std::filesystem::path& filepath, float scale) { uint8_t tmp_bitmap[RANGE * RANGE]; uint8_t vulkan_bitmap[RANGE * RANGE * 4]; std::ifstream file(filepath, std::ios::binary); if(!file.is_open()) { core::error::report(e_kind::error, "Font load : cannot open font file, %s", filepath.string().c_str()); return; } std::ifstream::pos_type fileSize = std::filesystem::file_size(filepath); file.seekg(0, std::ios::beg); std::vector bytes(fileSize); file.read(reinterpret_cast(bytes.data()), fileSize); file.close(); stbtt_BakeFontBitmap(bytes.data(), 0, scale, tmp_bitmap, RANGE, RANGE, 32, 96, _cdata.data()); for(int i = 0, j = 0; i < RANGE * RANGE; i++, j += 4) { vulkan_bitmap[j + 0] = tmp_bitmap[i]; vulkan_bitmap[j + 1] = tmp_bitmap[i]; vulkan_bitmap[j + 2] = tmp_bitmap[i]; vulkan_bitmap[j + 3] = tmp_bitmap[i]; } destroy(); _atlas.create(vulkan_bitmap, RANGE, RANGE, VK_FORMAT_R8G8B8A8_UNORM, "__mlx_texts_pipeline_texture_atlas", true); _atlas.setDescriptor(_renderer->getFragDescriptorSet().duplicate()); } void TextPutPipeline::put(int x, int y, int color, std::string str) { auto res = _drawlist.emplace(std::move(str), color, x, y); if(res.second) const_cast(*res.first).init(_library, _cdata); } void TextPutPipeline::render() { _atlas.updateSet(0); for(auto& draw : _drawlist) { std::shared_ptr draw_data = _library.getTextData(draw.id); draw_data->bind(*_renderer); _atlas.render(*_renderer, draw.x, draw.y, draw_data->getIBOsize()); } } void TextPutPipeline::destroy() noexcept { _library.clearLibrary(); _drawlist.clear(); _atlas.destroy(); } }