summaryrefslogtreecommitdiff
path: root/text_rendering/text_rendering.c
diff options
context:
space:
mode:
authorpryazha <pryadeiniv@mail.ru>2025-08-04 01:19:01 +0500
committerpryazha <pryadeiniv@mail.ru>2025-08-04 01:19:01 +0500
commit99337878eca2807436bcf11d36946b90db44a2d3 (patch)
treecc1acfb8ec79abcc8d5323680dfd574fc5436f03 /text_rendering/text_rendering.c
parent2e64d3c5d6eb1eb04168d39d5eb5f2d89af1a8b0 (diff)
text rendering chapterHEADmaster
Diffstat (limited to 'text_rendering/text_rendering.c')
-rw-r--r--text_rendering/text_rendering.c199
1 files changed, 199 insertions, 0 deletions
diff --git a/text_rendering/text_rendering.c b/text_rendering/text_rendering.c
new file mode 100644
index 0000000..398b202
--- /dev/null
+++ b/text_rendering/text_rendering.c
@@ -0,0 +1,199 @@
+#include <GL/glew.h>
+#include <GLFW/glfw3.h>
+
+#include <ft2build.h>
+#include FT_FREETYPE_H
+
+#include "common.h"
+
+typedef struct {
+ U32 texture_id;
+ V2F size;
+ V2F bearing;
+ U32 advance;
+} char_t;
+
+char_t chars[128];
+static U32 vao, vbo;
+
+V2F get_text_size(const char *text, F32 scale)
+{
+ V2F size = {0};
+ for (const char *c = text; *c; c++) {
+ char_t ch = chars[(U8)*c];
+ F32 h = ch.size.y * scale;
+ if (h > size.y)
+ size.y = h;
+ size.x += (ch.advance >> 6) * scale;
+ }
+ return size;
+}
+
+void render_text(const char *text, U32 shader, V2F pos, F32 scale, V3F color)
+{
+ glUseProgram(shader);
+ shader_set_3fv(shader, "text_color", color);
+ glActiveTexture(GL_TEXTURE0);
+ glBindVertexArray(vao);
+ for (const char *c = text; *c; c++) {
+ char_t ch = chars[(U8)*c];
+ F32 x = pos.x + ch.bearing.x * scale;
+ F32 y = pos.y - (ch.size.y - ch.bearing.y) * scale;
+ F32 w = ch.size.x * scale;
+ F32 h = ch.size.y * scale;
+ F32 vertices[] = {
+ x, y + h, 0.0f, 0.0f,
+ x, y, 0.0f, 1.0f,
+ x + w, y, 1.0f, 1.0f,
+
+ x, y + h, 0.0f, 0.0f,
+ x + w, y, 1.0f, 1.0f,
+ x + w, y + h, 1.0f, 0.0f,
+ };
+ glBindTexture(GL_TEXTURE_2D, ch.texture_id);
+ glBindBuffer(GL_ARRAY_BUFFER, vbo);
+ glBufferSubData(GL_ARRAY_BUFFER, 0, sizeof(vertices), vertices);
+ glBindBuffer(GL_ARRAY_BUFFER, 0);
+ glDrawArrays(GL_TRIANGLES, 0, 6);
+ glBindTexture(GL_TEXTURE_2D, 0);
+ pos.x += (ch.advance >> 6) * scale;
+ }
+ glBindVertexArray(0);
+ glUseProgram(0);
+}
+
+void render_bounds(const char *text, U32 shader, V2F pos, F32 scale)
+{
+ glUseProgram(shader);
+ glBindVertexArray(vao);
+ V2F size = get_text_size(text, scale);
+ F32 x = pos.x;
+ F32 y = pos.y;
+ F32 w = size.x;
+ F32 h = size.y;
+ F32 vertices[] = {
+ x, y + h, 0.0f, 0.0f,
+ x, y, 0.0f, 1.0f,
+ x + w, y, 1.0f, 1.0f,
+
+ x, y + h, 0.0f, 0.0f,
+ x + w, y, 1.0f, 1.0f,
+ x + w, y + h, 1.0f, 0.0f,
+ };
+ glBindBuffer(GL_ARRAY_BUFFER, vbo);
+ glBufferSubData(GL_ARRAY_BUFFER, 0, sizeof(vertices), vertices);
+ glBindBuffer(GL_ARRAY_BUFFER, 0);
+ glPolygonMode(GL_FRONT_AND_BACK, GL_LINE);
+ glDrawArrays(GL_TRIANGLES, 0, 6);
+ glPolygonMode(GL_FRONT_AND_BACK, GL_FILL);
+ glBindVertexArray(0);
+ glUseProgram(0);
+}
+
+int main(void)
+{
+ state_t state = init_state(1600, 800, 1);
+ init_glfw(&state);
+ init_gl();
+ glEnable(GL_BLEND);
+ glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
+
+ FT_Library ft;
+ if (FT_Init_FreeType(&ft))
+ die("failed to initialize freetype");
+ FT_Face face;
+ const char *font_name = "ibmvga8x16.ttf";
+ if (FT_New_Face(ft, font_name, 0, &face))
+ die("failed to load %s", font_name);
+ FT_Set_Pixel_Sizes(face, 0, 48);
+ glPixelStorei(GL_UNPACK_ALIGNMENT, 1);
+ for (U32 c = 0; c < 128; c++) {
+ if (FT_Load_Char(face, c, FT_LOAD_RENDER))
+ die("failed to load X character");
+ U32 texture;
+ glGenTextures(1, &texture);
+ glBindTexture(GL_TEXTURE_2D, texture);
+ glTexImage2D(GL_TEXTURE_2D, 0, GL_RED,
+ face->glyph->bitmap.width,
+ face->glyph->bitmap.rows,
+ 0, GL_RED, GL_UNSIGNED_BYTE,
+ face->glyph->bitmap.buffer);
+ glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
+ glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
+ glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
+ glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
+ chars[c] = (char_t){
+ texture,
+ {face->glyph->bitmap.width, face->glyph->bitmap.rows},
+ {face->glyph->bitmap_left, face->glyph->bitmap_top},
+ face->glyph->advance.x
+ };
+
+ }
+ FT_Done_Face(face);
+ FT_Done_FreeType(ft);
+
+ U32 shader = add_shader("glyph.vert", "glyph.frag", 0);
+ MAT4 projection = ortho(0.0f, state.width, 0.0f, state.height, 0.0f, 1.0f);
+ glUseProgram(shader);
+ shader_set_mat4fv(shader, "projection", projection);
+ glUseProgram(0);
+
+ U32 bounds_shader = add_shader("bounds.vert", "bounds.frag", 0);
+ glUseProgram(bounds_shader);
+ shader_set_mat4fv(bounds_shader, "projection", projection);
+ glUseProgram(0);
+
+ glGenVertexArrays(1, &vao);
+ glGenBuffers(1, &vbo);
+ glBindVertexArray(vao);
+ glBindBuffer(GL_ARRAY_BUFFER, vbo);
+ glBufferData(GL_ARRAY_BUFFER, sizeof(F32) * 6 * 4, 0, GL_DYNAMIC_DRAW);
+ glEnableVertexAttribArray(0);
+ glVertexAttribPointer(0, 4, GL_FLOAT, GL_FALSE, 4 * sizeof(F32), 0);
+ glBindBuffer(GL_ARRAY_BUFFER, 0);
+ glBindVertexArray(0);
+
+ glClearColor(0.16f, 0.16f, 0.16f, 1.0f);
+ F32 time = 0.0f;
+
+ V2F pos = {state.width / 2.0f, state.height / 2.0f};
+ V2F vel = {0.5f, 0.5f};
+
+ S32 show_bounds = 0;
+ const char *text = "foo, bar, nope";
+ F32 scale = 1.0f;
+ V2F size = get_text_size(text, scale);
+
+ while (!glfwWindowShouldClose(state.window)) {
+ handle_glfw_events(state.window, &state.input);
+ F32 dt = lock_framerate(60);
+
+ /* update */
+ F32 sin_value = 0.25f * sinf(time) + 0.75f;
+ F32 cos_value = 0.25f * cosf(time) + 0.75f;
+ V3F color = v3f(sin_value, cos_value, sin_value);
+ F32 speed = 256.0f;
+ V2F new_pos = v2f_add(pos, v2f_scalef(vel, speed * dt));
+ if (new_pos.x < 0.0f || new_pos.x + size.x > state.width)
+ vel.x = -vel.x;
+ if (new_pos.y < 0.0f || new_pos.y + size.y > state.height)
+ vel.y = -vel.y;
+ pos = new_pos;
+
+ if (key_first_press(state.input.jump))
+ show_bounds = !show_bounds;
+
+ time += dt;
+
+ /* render */
+ glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
+ render_text(text, shader, pos, scale, color);
+ if (show_bounds)
+ render_bounds(text, bounds_shader, pos, scale);
+
+ glfwSwapBuffers(state.window);
+ }
+
+ return 0;
+}