From b2508ce4ffd2b03ca9c9cd43d0f1e0e8c8d4f3c8 Mon Sep 17 00:00:00 2001 From: Piotr Krygier Date: Fri, 2 Feb 2024 15:06:47 +0100 Subject: [PATCH] [WIP] Add text rendering --- cmake/FindFreeType.cmake | 11 +++++ dependencies.dep | 4 ++ graphics/CMakeLists.txt | 4 ++ graphics/shaders/shader.frag | 2 +- graphics/shaders/shader.frag.num | Bin 1144 -> 0 bytes graphics/shaders/shader.vert.num | Bin 4844 -> 0 bytes graphics/src/locale_fonts.h | 46 +++++++++++++++++ graphics/src/locale_vulkan.h | 4 +- graphics/src/rse_font_errors.h | 26 ++++++++++ graphics/src/rse_graphics.c | 27 +++++----- graphics/src/rse_text_renderer.c | 82 +++++++++++++++++++++++++++++++ graphics/src/rse_text_renderer.h | 22 +++++++++ graphics/src/rse_vulkan_image.c | 28 ++++++++--- graphics/src/rse_vulkan_image.h | 13 ++++- utilities/src/file_utils.c | 6 +-- 15 files changed, 248 insertions(+), 27 deletions(-) create mode 100644 cmake/FindFreeType.cmake delete mode 100644 graphics/shaders/shader.frag.num delete mode 100644 graphics/shaders/shader.vert.num create mode 100644 graphics/src/locale_fonts.h create mode 100644 graphics/src/rse_font_errors.h create mode 100644 graphics/src/rse_text_renderer.c create mode 100644 graphics/src/rse_text_renderer.h diff --git a/cmake/FindFreeType.cmake b/cmake/FindFreeType.cmake new file mode 100644 index 00000000..5cf6ff55 --- /dev/null +++ b/cmake/FindFreeType.cmake @@ -0,0 +1,11 @@ + +set(FREE_TYPE_DIR "${PROJECT_SOURCE_DIR}/third_party/freetype/") + +find_library( + freetype + NAMES freetype + HINTS ${FREE_TYPE_DIR}/build/ +) + + +set(FreeType_INCLUDE_DIR "${FREE_TYPE_DIR}/include/") diff --git a/dependencies.dep b/dependencies.dep index c3569080..d2daa44d 100644 --- a/dependencies.dep +++ b/dependencies.dep @@ -19,3 +19,7 @@ custom_commands = cd Criterion && meson build && cd build && ninja [stb] git_url = https://github.com/nothings/stb.git tag = master + +[FreeType] +dl_url = https://download.savannah.gnu.org/releases/freetype/freetype-2.10.0.tar.gz +custom_commands = tar -xf freetype-2.10.0.tar.gz mv freetype-2.10.0 freetype && rm freetype-2.10.0.tar.gz && cmake -E make_directory build && cmake -E chdir build cmake -D BUILD_SHARED_LIBS:BOOL=true .. && cmake --build build diff --git a/graphics/CMakeLists.txt b/graphics/CMakeLists.txt index 5ce10914..a367403f 100644 --- a/graphics/CMakeLists.txt +++ b/graphics/CMakeLists.txt @@ -2,6 +2,7 @@ find_package(Vulkan REQUIRED) find_package(vma REQUIRED) find_package(glfw3 REQUIRED) find_package(stb REQUIRED) +find_package(FreeType REQUIRED) find_package(Vulkan COMPONENTS glslc) find_program(glslc_executable NAMES glslc HINTS ${Vulkan_GLSLC_EXECUTABLE}/..) @@ -42,6 +43,7 @@ ${CMAKE_SOURCE_DIR} ${vma_INCLUDE_DIR} ${glfw3_INCLUDE_DIR} ${stb_INCLUDE_DIR} + ${FreeType_INCLUDE_DIR} ) target_sources(rse_graphics PRIVATE @@ -57,10 +59,12 @@ src/rse_math.c src/mesh_controller.c src/vulkan_commons.c src/rse_vulkan_image.c +src/rse_text_renderer.c ) target_link_libraries(rse_graphics PRIVATE rse_utilities ${Vulkan_LIBRARIES} ${glfw} + ${freetype} ) diff --git a/graphics/shaders/shader.frag b/graphics/shaders/shader.frag index 64b5b629..19ed2efd 100644 --- a/graphics/shaders/shader.frag +++ b/graphics/shaders/shader.frag @@ -14,4 +14,4 @@ layout(location = 0) out vec4 outColor; void main() { outColor = vec4(fragColor * texture(texSampler[instanceTextureId], fragTexCoord).rgb, 1.0); -} \ No newline at end of file +} diff --git a/graphics/shaders/shader.frag.num b/graphics/shaders/shader.frag.num deleted file mode 100644 index 3a47c217f89d5a8629ab493676aa65aa41ecb544..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 1144 zcmYk4UuzRl5XHx)N!!%^v$nO?>ZVm+gyMsWASzNy5CW;BsBcTi-joZQ-MHCEeDlSx z0>DsX+F=qJRcg^ayYMsl^@x|Ax zIB}z1;fG~j#6C;h#AnyqTMPPTtJ69gmC4zQm(R(xX_b(WTwAtg%ktQu>oWptvfHvf z*#p_O^u*PW{wk_&_D7qSRlnH^<+D5+XTF~ogZSIHoBFZ-hxtHT(LniHl?h(sK`BW)kVUD$k9=znNkB zg+pJLM-N87E>A6Z^kB{dcc_!nCwlPWn&Ts7+EM`BvHrK^!Q|eNXTgyh%q&=Y!n^Vj zKnuN|ai{~29?X2GMf^aXMSjk5U!LBW5%D8=wD9Z)Gf%i@!e|>Zv!Cx@>Gs*j+*r)x zub8U*Td~YzL&hw?^t3OdpAS;e%RL$Afk#7Mhcb9DeH_c^;Y)aJ)4N3j-_~tW=aGy% zpT8mcrMF+&Lp+kv3%XC@ILC=BlCkF8ss1Aw`{&%_8ArZLy$asN6B+x_6K}}gX#%_x KV*ixkwd_9{yJBkq diff --git a/graphics/shaders/shader.vert.num b/graphics/shaders/shader.vert.num deleted file mode 100644 index ca65223c4be9e21bb37e2e549378e5a9d9a30e12..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 4844 zcmZ9OX?Ijb5QeWL6F?C~aaRoBf{M7WxFDzji6-psF(i|4aWWGp69il!isG&)xbL7M zis)bPSNX+re4aa9aLx4;UGG~JoRytj=$-sKHkj81XCS+feaL>~0CE^Pf*eJTA;*yuNC*GT zVEvn9o!RMGPwzlaUtdq}LjyzO;{y||UA5{!Ew5JxhVy21sFm-i>Nl(8m)D1Clfz|) z3LfQpD(o?fJtmM!HnXTNuTQiF>qFJ`jn-f*ZxnUwHy@v&!mlH{gmrAEF&)|M>Ksaa zt@>{D&TI>Mv(b8Vu+_%fz@sFU&`x+(YYg=_*u27j_9d?a^Y*Uhn}Qq1p+djyS=6>+ zXt2g{;b#_o4E|KKwyx|$d`4>n{nh5?YO_^!OV~EAt}Qm~BA5QouHf@}YeT-w zoCmg5wS4@+yxpdgHYcxbvQ|53^9;_|&sslb-Biy<8qKkllOrS5X5ZHB%#IwD!lvI? zW4Kyl4bFOY?>jCy+7egv44PW-S%Pun(sT#qD`^hk67JbyLrV261O0|RnHb;%qL*`=6f+l z?0b>ZekkeDlkdUq;r`lxOgQeQ_;BLltiC7D>I{Bn?Ls2|FX-lz*I(>=lQaLXtj302 z?E90`{yVF_-WB7;zQ>6F16|*+i+!JRx6>2raQ=SA`~y>AHJ7uWg}4TuU&6i<|E7ms z?BDe=UONr@_dV>R=-W!ThVCERu#2Y>et@dxqMwEM_I=;?Qlq`}|Cr>b@DZPrIPcvY z7`by1&upCb`#7yKvCq}$?z0GSAF;lX_ZoEb2EG>EeIxI6 z=;qZ=TjX7eZr&w`dByr__uR`6d*>UC+&3hCD$V6auw1!!H-YV)bJ1>Y>ywY%H=|F_ zeG8m?IySY89eAIXcx?JEp(cM4#aTmHi=GA6R-K=*b-u0NLSl_VU zlk71E@p2^gxfk7iV!rnkwo~@|!PXUZtU%YtI<&<;51?B|%tgEc(eAldA@*)15_>-aQPqcg{t7%tbzO_n=SDy&6tFa<4&`E9ZU$Y;Nn*9&?e88hg>pb9oe8 z?$={rx#&+Hx-s(ZWsTje{m5D*<|NiP_F0E+-oWe8%YEMf)zM4z5}E1Z1f9zvHZ=N<-|+u3W6*~>?bIeK~a z+riGLpT7)M*5?s@%&RTtQbQj@wjnWZvHsSry@6^+X|UhytpK$i=96TN*u z+OV6a582LEsTj=`{?~Xa&M)Z@9n%_x0%SrCt#FfwXUgFAUdmqkt{k6y0 zrqH!HlS8bZA_oy?W^E&^pCR($^EtXci;(d70$tv>Y0Q_1aq +#include + +#include "freetype/fttypes.h" +#include "locale_fonts.h" +#include "rse_font_errors.h" +#include "utilities/logger.h" +#include "utilities/rse_commons.h" +#include "utilities/rse_errors_common.h" +#include "rse_vulkan_image.h" + +#include "ft2build.h" +#include "vulkan/vulkan_core.h" +#include FT_FREETYPE_H + +#define FONT_CHARACTERS_COUNT 126 + +uint16_t g_font_textures[FONT_CHARACTERS_COUNT]; + +FT_Library g_library; +rse_err_t rse_fonts_initialize() +{ + FT_Error error; + + error = FT_Init_FreeType(&g_library); + + if (error) { + LOGF(font_messages[FONT_INITIALIZATION_FAILED]); + return FONT_ERROR_INIT_FAILED; + } + return RSE_ERROR_NO_ERROR; +} + + +rse_err_t rse_fonts_load_from_file(const char* file_path) +{ + rse_err_t status = RSE_ERROR_NO_ERROR; + FT_Error error; + FT_Face face; + size_t iter = 0U; + + error = FT_New_Face(g_library, file_path, 0, &face); + + if (error) { + LOGF(font_messages[FONT_LOAD_FONT_FAILED]); + return FONT_ERROR_LOAD_FAILED; + } + + /* By default we set font size to 16px. TODO: change later? */ + error = FT_Set_Pixel_Sizes(face, 0, 48); + + if (error) { + LOGF(font_messages[FONT_CHANGE_SIZE_FAILED]); + return FONT_ERROR_CHANGE_SIZE_FAILED; + } + + /* Convert font face to textured image */ + /* Loading only needed ascii characters, from 40 (space) to 126 (~) */ + for (iter = 33U; iter < face->num_glyphs && iter < 127U; ++iter) { + error = FT_Load_Char(face, iter, FT_LOAD_RENDER); + + if (error) { + LOGF(font_messages[FONT_LOAD_GLYPH_FAILED]); + return FONT_ERROR_LOAD_GLYPH_FAILED; + } + + STATUS_CHECK(rse_load_texture_from_bitmat(face->glyph->bitmap.buffer, + face->glyph->bitmap.width, + face->glyph->bitmap.rows, + &g_font_textures[iter], VK_FORMAT_R8_UNORM)); + } + return status; +} + +rse_err_t rse_print_text(const char* text) +{ + rse_err_t status = RSE_ERROR_NO_ERROR; + + + return status; +} diff --git a/graphics/src/rse_text_renderer.h b/graphics/src/rse_text_renderer.h new file mode 100644 index 00000000..1c14f4a2 --- /dev/null +++ b/graphics/src/rse_text_renderer.h @@ -0,0 +1,22 @@ + +/** + * @file rse_text_renderer.h + * @author Piotr Krygier (piotr.krygier@meshsystems.com) + * @brief Load and render text on screen or in space + * @version 0.1 + * @date 2024-02-02 + * + * @copyright Copyright (c) 2023 + * + */ + + +#ifndef RSE_TEXT_RENDERER_H +#define RSE_TEXT_RENDERER_H + +#include "utilities/rse_commons.h" + +rse_err_t rse_fonts_initialize(void); +rse_err_t rse_fonts_load_from_file(const char* file_path); + +#endif // !RSE_TEXT_RENDERER_H_ diff --git a/graphics/src/rse_vulkan_image.c b/graphics/src/rse_vulkan_image.c index 2d40718c..f127c7fe 100644 --- a/graphics/src/rse_vulkan_image.c +++ b/graphics/src/rse_vulkan_image.c @@ -242,6 +242,10 @@ static rse_err_t format_to_pixel_size(VkFormat format, { switch (format) { + case VK_FORMAT_R8_SRGB: + case VK_FORMAT_R8_UNORM: + *format_size = 1; + break; case VK_FORMAT_R8G8B8A8_SRGB: *format_size = 4; break; @@ -258,7 +262,7 @@ static rse_err_t format_to_pixel_size(VkFormat format, static rse_err_t create_textured_image(uint32_t width, uint32_t height, VkFormat format, - unsigned char* pixels, + const unsigned char* pixels, uint16_t* texture_id) { rse_err_t status = RSE_ERROR_NO_ERROR; @@ -403,7 +407,20 @@ rse_err_t init_vulkan_images() return status; } -rse_err_t rse_load_texture(const char* file_path, uint16_t* texture_id) +rse_err_t rse_load_texture_from_bitmat(const unsigned char* pixel_buffer, int width, int height, uint16_t* texture_id, VkFormat color_format) +{ + rse_err_t status = RSE_ERROR_NO_ERROR; + + /* Create actual Vulkan Image */ + STATUS_CHECK(create_textured_image(width, + height, + color_format, + pixel_buffer, texture_id)); + + return status; +} + +rse_err_t rse_load_texture_from_file(const char* file_path, uint16_t* texture_id) { rse_err_t status = RSE_ERROR_NO_ERROR; int width = 0; @@ -418,16 +435,11 @@ rse_err_t rse_load_texture(const char* file_path, uint16_t* texture_id) return -1; } - /* Create actual Vulkan Image */ - status = create_textured_image(width, - height, - VK_FORMAT_R8G8B8A8_SRGB, - pixel_buffer, texture_id); + STATUS_CHECK(rse_load_texture_from_bitmat(pixel_buffer, width, height, texture_id, VK_FORMAT_R8G8B8A8_SRGB)); stbi_image_free(pixel_buffer); return status; - } void rse_destroy_textures() diff --git a/graphics/src/rse_vulkan_image.h b/graphics/src/rse_vulkan_image.h index 037cd977..5d551073 100644 --- a/graphics/src/rse_vulkan_image.h +++ b/graphics/src/rse_vulkan_image.h @@ -34,13 +34,22 @@ rse_err_t init_vulkan_images(void); /** - * @brief Loads image from file, for later to be uysed as a texture + * @brief Loads image from file, for later to be used as a texture * * @param file_path Path to texture file * @param texture_id Texture ID will be set up here * @return rse_err_t RSE_ERROR_NO_ERROR on success */ -rse_err_t rse_load_texture(const char* file_path, uint16_t* texture_id); +rse_err_t rse_load_texture_from_bitmat(const unsigned char* pixel_buffer, int width, int height, uint16_t* texture_id, VkFormat color_format); + +/** + * @brief Loads image from file, for later to be used as a texture + * + * @param file_path Path to texture file + * @param texture_id Texture ID will be set up here + * @return rse_err_t RSE_ERROR_NO_ERROR on success + */ +rse_err_t rse_load_texture_from_file(const char* file_path, uint16_t* texture_id); /** * @brief Destroy all loaded textures. Usefull for closing application or just clearing. diff --git a/utilities/src/file_utils.c b/utilities/src/file_utils.c index 2f52e6c4..a8703f27 100644 --- a/utilities/src/file_utils.c +++ b/utilities/src/file_utils.c @@ -16,9 +16,9 @@ void rse_read_file(const char* file_name, long* bytes_count, char* buffer) fclose(file); return; } - - if (fread(buffer, *bytes_count, 1, file)) { - perror("Empty file read"); + fread(buffer, *bytes_count, 1, file); + if (ferror(file) != 0) { + perror("Error reading file"); } fclose(file);