[WIP] Add text rendering

This commit is contained in:
Piotr Krygier committed 2025-06-20 13:44:33 +02:00
1 parent 3bd14b20c2
commit b2508ce4ff
14 files changed
+246 -25

No files matched your search

+11
View File
@@ -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/")
+4
View File
@@ -19,3 +19,7 @@ custom_commands = cd Criterion && meson build && cd build && ninja
[stb] [stb]
git_url = https://github.com/nothings/stb.git git_url = https://github.com/nothings/stb.git
tag = master 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
+4
View File
@@ -2,6 +2,7 @@ find_package(Vulkan REQUIRED)
find_package(vma REQUIRED) find_package(vma REQUIRED)
find_package(glfw3 REQUIRED) find_package(glfw3 REQUIRED)
find_package(stb REQUIRED) find_package(stb REQUIRED)
find_package(FreeType REQUIRED)
find_package(Vulkan COMPONENTS glslc) find_package(Vulkan COMPONENTS glslc)
find_program(glslc_executable NAMES glslc HINTS ${Vulkan_GLSLC_EXECUTABLE}/..) find_program(glslc_executable NAMES glslc HINTS ${Vulkan_GLSLC_EXECUTABLE}/..)
@@ -42,6 +43,7 @@ ${CMAKE_SOURCE_DIR}
${vma_INCLUDE_DIR} ${vma_INCLUDE_DIR}
${glfw3_INCLUDE_DIR} ${glfw3_INCLUDE_DIR}
${stb_INCLUDE_DIR} ${stb_INCLUDE_DIR}
${FreeType_INCLUDE_DIR}
) )
target_sources(rse_graphics PRIVATE target_sources(rse_graphics PRIVATE
@@ -57,10 +59,12 @@ src/rse_math.c
src/mesh_controller.c src/mesh_controller.c
src/vulkan_commons.c src/vulkan_commons.c
src/rse_vulkan_image.c src/rse_vulkan_image.c
src/rse_text_renderer.c
) )
target_link_libraries(rse_graphics PRIVATE target_link_libraries(rse_graphics PRIVATE
rse_utilities rse_utilities
${Vulkan_LIBRARIES} ${Vulkan_LIBRARIES}
${glfw} ${glfw}
${freetype}
) )
Binary file not shown.
Binary file not shown.
+46
View File
@@ -0,0 +1,46 @@
/**
* @file locale_fonts.h
* @author Piotr Krygier (everyonecancode@gmail.com)
* @brief Error messages for font handlers
* @version 0.1
* @date 2022-05-13
*
* @copyright Copyright (c) 2022
*
*/
#ifndef RSE_LOCALE_FONTS_H
#define RSE_LOCALE_FONTS_H
#include "utilities/localization.h"
#define font_messages font_messages_nolocale[SELECTED_LANGUAGE]
enum font_messages_t {
FONT_INITIALIZATION_FAILED,
FONT_LOAD_FONT_FAILED,
FONT_CHANGE_SIZE_FAILED,
FONT_LOAD_GLYPH_FAILED,
FONT_RENDER_GLYPH_FAILED,
FONT_LAST_MESSAGE
};
static const char font_messages_nolocale[LAST_LANGUAGE][FONT_LAST_MESSAGE][MAX_MESSAGE_LENGHT] = {
{/* EN_US */
/* FONT_INITIALIZATION_FAILED */
"Font initialization failed\n",
/* FONT_LOAD_FONT_FAILED */
"Failed to load new font\n",
/* FONT_CHANGE_SIZE_FAILED */
"Failed to change font size\n",
/* FONT_LOAD_GLYPH_FAILED */
"Failed to load glyph into memeory\n",
/* FONT_RENDER_GLYPH_FAILED */
"Failed to render glypth into bitmap\n"
}
};
#endif // RSE_LOCALE_FONTS_H
+1 -1
View File
@@ -1,5 +1,5 @@
/** /**
* @file localeVulkan.h * @file locale_vulkan.h
* @author Piotr Krygier (everyonecancode@gmail.com) * @author Piotr Krygier (everyonecancode@gmail.com)
* @brief Error messages for Vulkan initializers * @brief Error messages for Vulkan initializers
* @version 0.1 * @version 0.1
+26
View File
@@ -0,0 +1,26 @@
/**
* @file rse_font_errors.h
* @author Piotr Krygier (everyonecancode@gmail.com)
* @brief
* @version 0.1
* @date 2023-10-06
*
* @copyright Copyright (c) 2023
*
*/
#ifndef RSE_FONT_ERRORS_H
#define RSE_FONT_ERRORS_H
#define RSE_FONT_MODULE_ID 0x0400U
/* Error codes */
enum rse_window_error_t {
FONT_ERROR_INIT_FAILED = RSE_FONT_MODULE_ID,
FONT_ERROR_LOAD_FAILED,
FONT_ERROR_CHANGE_SIZE_FAILED,
FONT_ERROR_LOAD_GLYPH_FAILED,
FONT_ERROR_RENDER_GLYPH_FAILED
};
#endif /* RSE_WINDOW_ERRORS_H */
+16 -11
View File
@@ -1,4 +1,5 @@
#include "rse_graphics.h" #include "rse_graphics.h"
#include "src/rse_text_renderer.h"
#include "window.h" #include "window.h"
#include "vulkan_base.h" #include "vulkan_base.h"
@@ -8,11 +9,13 @@
#include "utilities/logger.h" #include "utilities/logger.h"
#include "utilities/rse_commons.h" #include "utilities/rse_commons.h"
#include "utilities/rse_errors_common.h" #include "utilities/rse_errors_common.h"
#include "rse_font_errors.h"
#include "rse_math.h" #include "rse_math.h"
extern struct rse_vulkan_state_t g_vulkan_state; extern struct rse_vulkan_state_t g_vulkan_state;
rse_err_t rse_graphics_init(void) rse_err_t rse_graphics_init(void)
{ {
rse_err_t status = RSE_ERROR_NO_ERROR; rse_err_t status = RSE_ERROR_NO_ERROR;
@@ -36,21 +39,23 @@ void rse_graphics_test_function(void)
rse_err_t status = RSE_ERROR_NO_ERROR; rse_err_t status = RSE_ERROR_NO_ERROR;
uint16_t tex_id_2 = 0; uint16_t tex_id_2 = 0;
status = rse_load_texture("../../test_image.jpg", &tex_id_2); status = rse_load_texture_from_file("../../test_image.jpg", &tex_id_2);
if (status != RSE_ERROR_NO_ERROR) { if (status != RSE_ERROR_NO_ERROR) {
LOGE("Failed to do something. Check error messages"); LOGE("Failed to do something. Check error messages. Error: %d", status);
return; return;
} }
(rse_fonts_initialize());
(rse_fonts_load_from_file("/usr/share/fonts/JetBrainsMonoNerdFont/JetBrainsMonoNerdFont-Medium.ttf"));
// FIXME: Temporary array of vertices, for testing purposes // FIXME: Temporary array of vertices, for testing purposes
struct rse_vertex_t vertices[] = {{{-0.5f, -0.5f, 0.0f}, {1.0f, 0.0f, 0.0f}, {0.0f, 0.0f}}, struct rse_vertex_t vertices[] = {{{-0.5f, -0.5f, 0.0f}, {1.0f, 1.0f, 1.0f}, {0.0f, 0.0f}},
{{0.5f, -0.5f, 0.0f}, {0.0f, 1.0f, 0.0f}, {1.0f, 0.0f}}, {{0.5f, -0.5f, 0.0f}, {1.0f, 1.0f, 1.0f}, {1.0f, 0.0f}},
{{0.5f, 0.5f, 0.0f}, {0.0f, 0.0f, 1.0f}, {1.0f, 1.0f}}, {{0.5f, 0.5f, 0.0f}, {1.0f, 1.0f, 1.0f}, {1.0f, 1.0f}},
{{-0.5f, 0.5f, 0.0f}, {1.0f, 1.0f, 1.0f}, {0.0f, 1.0f}}}; {{-0.5f, 0.5f, 0.0f}, {1.0f, 1.0f, 1.0f}, {0.0f, 1.0f}}};
struct rse_vertex_t vertices2[] = {{{-0.5f, -0.5f, -0.5f}, {1.0f, 0.0f, 0.0f}, {0.0f, 0.0f}}, struct rse_vertex_t vertices2[] = {{{-0.5f, -0.5f, -0.5f}, {1.0f, 1.0f, 1.0f}, {0.0f, 0.0f}},
{{0.5f, -0.5f, -0.5f}, {0.0f, 1.0f, 0.0f}, {1.0f, 0.0f}}, {{0.5f, -0.5f, -0.5f}, {1.0f, 1.0f, 1.0f}, {1.0f, 0.0f}},
{{0.5f, 0.5f, -0.5f}, {0.0f, 0.0f, 1.0f}, {1.0f, 1.0f}}, {{0.5f, 0.5f, -0.5f}, {1.0f, 1.0f, 1.0f}, {1.0f, 1.0f}},
{{-0.5f, 0.5f, -0.5f}, {1.0f, 1.0f, 1.0f}, {0.0f, 1.0f}}}; {{-0.5f, 0.5f, -0.5f}, {1.0f, 1.0f, 1.0f}, {0.0f, 1.0f}}};
// FIXME: Temporary array of vertices, for testing purposes // FIXME: Temporary array of vertices, for testing purposes
@@ -61,14 +66,14 @@ void rse_graphics_test_function(void)
{0.0f, 0.0f, 0.0f}, {0.0f, 0.0f, 0.0f},
{0.0f, 0.0f, rse_math_deg_to_radians(45.0f)}, {0.0f, 0.0f, rse_math_deg_to_radians(45.0f)},
1.0f, 1.0f,
tex_id_2 4
}); });
create_mesh_instance(mesh_id, (struct rse_instance_data_t){ create_mesh_instance(mesh_id, (struct rse_instance_data_t){
{-1.0f, 0.0f, 0.3f}, {-1.0f, 0.0f, 0.3f},
{0.0f, 0.0f, 0.0f}, {0.0f, 0.0f, 0.0f},
1.0f, 1.0f,
tex_id_2 49
}); });
mesh_id = create_mesh(vertices2, indices, 8, 6); mesh_id = create_mesh(vertices2, indices, 8, 6);
@@ -76,7 +81,7 @@ void rse_graphics_test_function(void)
{0.0f, 1.0f, 0.0f}, {0.0f, 1.0f, 0.0f},
{0.0f, 0.0f, rse_math_deg_to_radians(0.0f)}, {0.0f, 0.0f, rse_math_deg_to_radians(0.0f)},
1.0f, 1.0f,
tex_id_2 33
}); });
} }
+82
View File
@@ -0,0 +1,82 @@
#include "rse_text_renderer.h"
#include <stdint.h>
#include <strings.h>
#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;
}
+22
View File
@@ -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_
+20 -8
View File
@@ -242,6 +242,10 @@ static rse_err_t format_to_pixel_size(VkFormat format,
{ {
switch (format) switch (format)
{ {
case VK_FORMAT_R8_SRGB:
case VK_FORMAT_R8_UNORM:
*format_size = 1;
break;
case VK_FORMAT_R8G8B8A8_SRGB: case VK_FORMAT_R8G8B8A8_SRGB:
*format_size = 4; *format_size = 4;
break; 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, static rse_err_t create_textured_image(uint32_t width,
uint32_t height, uint32_t height,
VkFormat format, VkFormat format,
unsigned char* pixels, const unsigned char* pixels,
uint16_t* texture_id) uint16_t* texture_id)
{ {
rse_err_t status = RSE_ERROR_NO_ERROR; rse_err_t status = RSE_ERROR_NO_ERROR;
@@ -403,7 +407,20 @@ rse_err_t init_vulkan_images()
return status; 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; rse_err_t status = RSE_ERROR_NO_ERROR;
int width = 0; int width = 0;
@@ -418,16 +435,11 @@ rse_err_t rse_load_texture(const char* file_path, uint16_t* texture_id)
return -1; return -1;
} }
/* Create actual Vulkan Image */ STATUS_CHECK(rse_load_texture_from_bitmat(pixel_buffer, width, height, texture_id, VK_FORMAT_R8G8B8A8_SRGB));
status = create_textured_image(width,
height,
VK_FORMAT_R8G8B8A8_SRGB,
pixel_buffer, texture_id);
stbi_image_free(pixel_buffer); stbi_image_free(pixel_buffer);
return status; return status;
} }
void rse_destroy_textures() void rse_destroy_textures()
+11 -2
View File
@@ -34,13 +34,22 @@
rse_err_t init_vulkan_images(void); 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 file_path Path to texture file
* @param texture_id Texture ID will be set up here * @param texture_id Texture ID will be set up here
* @return rse_err_t RSE_ERROR_NO_ERROR on success * @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. * @brief Destroy all loaded textures. Usefull for closing application or just clearing.
+3 -3
View File
@@ -16,9 +16,9 @@ void rse_read_file(const char* file_name, long* bytes_count, char* buffer)
fclose(file); fclose(file);
return; return;
} }
fread(buffer, *bytes_count, 1, file);
if (fread(buffer, *bytes_count, 1, file)) { if (ferror(file) != 0) {
perror("Empty file read"); perror("Error reading file");
} }
fclose(file); fclose(file);