A 3D game engine from scratch.
1// (c) 2020 Vlad-Stefan Harbuz <vlad@vladh.net>
2
3#include "../src_external/glad/glad.h"
4#include "../src_external/pstr.h"
5#include "logs.hpp"
6#include "array.hpp"
7#include "mats.hpp"
8#include "fonts.hpp"
9#include "intrinsics.hpp"
10
11
12f32
13fonts::frac_px_to_px(u32 n)
14{
15 return (f32)(n >> 6);
16}
17
18
19f32
20fonts::font_unit_to_px(u32 n)
21{
22 // NOTE: We should be dividing by units_per_em here...probably?
23 // This is because we expect height etc. to be in "font units".
24 // But treating these metrics as "fractional pixels" seems to work,
25 // whereas division by units_per_em doesn't.
26 // Check this in more detail.
27 return (f32)(n >> 6);
28}
29
30
31fonts::FontAsset *
32fonts::get_by_name(Array<FontAsset> *assets, const char *name)
33{
34 each (asset, *assets) {
35 if (pstr_eq(asset->name, name)) {
36 return asset;
37 }
38 }
39 logs::warning("Could not find FontAsset with name %s", name);
40 return nullptr;
41}
42
43
44fonts::FontAsset *
45fonts::init_font_asset(
46 FontAsset *font_asset,
47 memory::Pool *memory_pool,
48 mats::TextureAtlas *texture_atlas,
49 FT_Library *ft_library,
50 const char *name,
51 const char *filename,
52 u16 font_size
53) {
54 font_asset->name = name;
55 font_asset->font_size = font_size;
56
57 char path[MAX_PATH];
58 strcpy(path, FONTS_DIR);
59 strcat(path, filename);
60
61 font_asset->characters = Array<Character>(memory_pool, CHAR_MAX_CODEPOINT_TO_LOAD + 1, "characters");
62
63 FT_Face face;
64 if (FT_New_Face(*ft_library, path, 0, &face)) {
65 logs::error("Could not load font at %s", path);
66 }
67 FT_Set_Pixel_Sizes(face, 0, font_asset->font_size);
68 if (!FT_IS_SCALABLE(face)) {
69 logs::fatal("Font face not scalable, don't know what to do.");
70 }
71 font_asset->units_per_em = face->units_per_EM;
72 font_asset->ascender = face->ascender;
73 font_asset->descender = face->descender;
74 font_asset->height = face->height;
75
76 load_glyphs(font_asset, face, texture_atlas);
77
78 FT_Done_Face(face);
79
80 return font_asset;
81}
82
83
84void
85fonts::load_glyphs(
86 FontAsset *font_asset,
87 FT_Face face,
88 mats::TextureAtlas *texture_atlas
89) {
90 FT_GlyphSlot glyph = face->glyph;
91
92 // TODO: Can we avoid loading all characters twice here?
93 for (u32 c = 0; c < CHAR_MAX_CODEPOINT_TO_LOAD; c++) {
94 Character *character = font_asset->characters.push();
95
96 if (FT_Load_Char(face, c, FT_LOAD_RENDER)) {
97 logs::error("Failed to load glyph %s", c);
98 continue;
99 }
100
101 character->size = iv2(glyph->bitmap.width, glyph->bitmap.rows);
102 character->bearing = iv2(glyph->bitmap_left, glyph->bitmap_top);
103 character->advance = iv2(glyph->advance.x, glyph->advance.y);
104 }
105
106 mats::activate_font_texture(texture_atlas->texture_name);
107
108 for (u32 c = 0; c < CHAR_MAX_CODEPOINT_TO_LOAD; c++) {
109 if (
110 // Unicode C0 controls
111 (c <= 0x1F) ||
112 // DEL
113 (c == 0x7F) ||
114 // Unicode C1 controls
115 (c >= 0x80 && c <= 0x9F)
116 ) {
117 continue;
118 }
119
120 Character *character = font_asset->characters[c];
121
122 iv2 tex_coords = mats::push_space_to_texture_atlas(texture_atlas, character->size);
123
124 if (FT_Load_Char(face, c, FT_LOAD_RENDER)) {
125 logs::error("Failed to load glyph %s", c);
126 continue;
127 }
128
129 mats::push_font_texture(tex_coords, character->size, glyph->bitmap.buffer);
130
131 character->tex_coords = tex_coords;
132 }
133}