155 lines
4.9 KiB
C
155 lines
4.9 KiB
C
#include "text_renderer.h"
|
|
#include <stdint.h>
|
|
#include <strings.h>
|
|
|
|
#include "freetype/fttypes.h"
|
|
#include "font_errors.h"
|
|
#include "src/mesh_controller.h"
|
|
#include "src/math.h"
|
|
#include "src/vulkan_commons.h"
|
|
#include "utilities/logger.h"
|
|
#include "utilities/commons.h"
|
|
#include "utilities/errors_common.h"
|
|
#include "utilities/localization.h"
|
|
#include "vulkan_image.h"
|
|
|
|
#include "vulkan/vulkan_core.h"
|
|
#include FT_FREETYPE_H
|
|
|
|
/* Load only ASCII characters that are needed. TODO: Add unicode characters */
|
|
#define FONT_CHARACTERS_COUNT 94
|
|
#define FONT_START_CHARACTER 32U
|
|
|
|
#define FONT_SIZE 200
|
|
/* Use this to scale font to 0.0 <-> 1.0 range */
|
|
#define FONT_SCALING 1.0f/FONT_SIZE
|
|
|
|
struct font_data_t {
|
|
uint16_t mesh_id;
|
|
uint16_t texture_id;
|
|
float width;
|
|
float height;
|
|
float bearing_x;
|
|
float bearing_y;
|
|
float advance;
|
|
};
|
|
|
|
struct font_data_t g_fonts_data[FONT_CHARACTERS_COUNT];
|
|
|
|
FT_Library g_library;
|
|
rse_err_t fonts_initialize()
|
|
{
|
|
FT_Error error;
|
|
|
|
memset(g_fonts_data, 0, sizeof(g_fonts_data));
|
|
error = FT_Init_FreeType(&g_library);
|
|
|
|
if (error) {
|
|
LOGF(_("Font initialization failed\n"));
|
|
return FONT_ERROR_INIT_FAILED;
|
|
}
|
|
return RSE_ERROR_NO_ERROR;
|
|
}
|
|
|
|
|
|
rse_err_t fonts_load_from_file(struct graphics_context_t* context, const char* file_path)
|
|
{
|
|
rse_err_t status = RSE_ERROR_NO_ERROR;
|
|
FT_Error error;
|
|
FT_Face face;
|
|
size_t iter = 0U;
|
|
struct vertex_t font_vertices[4];
|
|
uint16_t font_indices[] = {0, 1, 2, 2, 3, 0};
|
|
|
|
error = FT_New_Face(g_library, file_path, 0, &face);
|
|
|
|
if (error) {
|
|
LOGF(_("Failed to load new font\n"));
|
|
return FONT_ERROR_LOAD_FAILED;
|
|
}
|
|
|
|
/* By default we set font size to FONT_SIZE.*/
|
|
error = FT_Set_Pixel_Sizes(face, 0, FONT_SIZE);
|
|
|
|
if (error) {
|
|
LOGF(_("Failed to change font size\n"));
|
|
return FONT_ERROR_CHANGE_SIZE_FAILED;
|
|
}
|
|
|
|
/* Convert font face to textured image */
|
|
/* Loading only needed ascii characters, from 40 (space) to 126 (~) */
|
|
for (iter = 0U; iter < face->num_glyphs && iter < FONT_CHARACTERS_COUNT; ++iter) {
|
|
error = FT_Load_Char(face, iter + FONT_START_CHARACTER, FT_LOAD_RENDER);
|
|
|
|
if (error) {
|
|
LOGF(_("Failed to load glyph into memeory\n"));
|
|
return FONT_ERROR_LOAD_GLYPH_FAILED;
|
|
}
|
|
|
|
g_fonts_data[iter].width = face->glyph->bitmap.width;
|
|
g_fonts_data[iter].height = face->glyph->bitmap.rows;
|
|
g_fonts_data[iter].bearing_x = face->glyph->bitmap_left;
|
|
g_fonts_data[iter].bearing_y = face->glyph->bitmap_top;
|
|
g_fonts_data[iter].advance = (face->glyph->advance.x >> 6);
|
|
|
|
/* Skip generating mesh if character is space */
|
|
if ((char)(iter + FONT_START_CHARACTER) == ' ') {
|
|
continue;
|
|
}
|
|
|
|
font_vertices[0] = (struct vertex_t) {
|
|
{g_fonts_data[iter].bearing_x, 0.0f, 0.0f}, {1.0f, 1.0f, 1.0f}, {0.0f, 0.0f}
|
|
};
|
|
font_vertices[1] = (struct vertex_t) {
|
|
{g_fonts_data[iter].bearing_x + g_fonts_data[iter].width, 0.0f, 0.0f}, {1.0f, 1.0f, 1.0f}, {1.0f, 0.0f}
|
|
};
|
|
font_vertices[2] = (struct vertex_t) {
|
|
{g_fonts_data[iter].bearing_x + g_fonts_data[iter].width, g_fonts_data[iter].height, 0.0f}, {1.0f, 1.0f, 1.0f}, {1.0f, 1.0f}
|
|
};
|
|
font_vertices[3] = (struct vertex_t) {
|
|
{g_fonts_data[iter].bearing_x, g_fonts_data[iter].height, 0.0f}, {1.0f, 1.0f, 1.0f}, {0.0f, 1.0f}
|
|
};
|
|
|
|
g_fonts_data[iter].mesh_id = create_mesh(context, font_vertices, font_indices, 8, 6);
|
|
|
|
STATUS_CHECK(load_texture_from_bitmat(context, face->glyph->bitmap.buffer,
|
|
face->glyph->bitmap.width,
|
|
face->glyph->bitmap.rows,
|
|
&g_fonts_data[iter].texture_id, VK_FORMAT_R8_SRGB));
|
|
|
|
}
|
|
return status;
|
|
}
|
|
|
|
rse_err_t print_text(struct graphics_context_t* context, const char* text, struct vec3_t position, float font_size)
|
|
{
|
|
rse_err_t status = RSE_ERROR_NO_ERROR;
|
|
size_t iter = 0U;
|
|
size_t charactet_code = 0U;
|
|
float advance = 0.0f;
|
|
float x_pos = 0.0f;
|
|
float y_pos = 0.0f;
|
|
float scale = FONT_SCALING * font_size;
|
|
|
|
for (iter = 0U; iter < strlen(text); ++iter) {
|
|
if (text[iter] == ' ') {
|
|
advance += g_fonts_data[charactet_code].advance * scale;
|
|
continue;
|
|
}
|
|
charactet_code = (size_t)text[iter] - FONT_START_CHARACTER;
|
|
|
|
x_pos = position.x + advance - (g_fonts_data[charactet_code].bearing_x * scale);
|
|
y_pos = position.y - (g_fonts_data[charactet_code].bearing_y * scale);
|
|
create_mesh_instance(context, g_fonts_data[charactet_code].mesh_id, (struct instance_data_t){
|
|
{x_pos, y_pos, 0.0f},
|
|
{0.0f, 0.0f, math_deg_to_radians(0.0f)},
|
|
FONT_SCALING * font_size,
|
|
g_fonts_data[charactet_code].texture_id
|
|
});
|
|
|
|
advance += g_fonts_data[charactet_code].advance * scale;
|
|
}
|
|
|
|
return status;
|
|
}
|