diff options
author | pryazha <pryadeiniv@mail.ru> | 2025-08-04 01:19:01 +0500 |
---|---|---|
committer | pryazha <pryadeiniv@mail.ru> | 2025-08-04 01:19:01 +0500 |
commit | 99337878eca2807436bcf11d36946b90db44a2d3 (patch) | |
tree | cc1acfb8ec79abcc8d5323680dfd574fc5436f03 /text_rendering | |
parent | 2e64d3c5d6eb1eb04168d39d5eb5f2d89af1a8b0 (diff) |
Diffstat (limited to 'text_rendering')
-rw-r--r-- | text_rendering/bounds.frag | 8 | ||||
-rw-r--r-- | text_rendering/bounds.vert | 12 | ||||
-rwxr-xr-x | text_rendering/build.sh | 7 | ||||
-rw-r--r-- | text_rendering/glyph.frag | 16 | ||||
-rw-r--r-- | text_rendering/glyph.vert | 18 | ||||
-rw-r--r-- | text_rendering/ibmvga8x16.ttf | bin | 0 -> 69760 bytes | |||
-rw-r--r-- | text_rendering/text_rendering.c | 199 |
7 files changed, 260 insertions, 0 deletions
diff --git a/text_rendering/bounds.frag b/text_rendering/bounds.frag new file mode 100644 index 0000000..34646c6 --- /dev/null +++ b/text_rendering/bounds.frag @@ -0,0 +1,8 @@ +#version 330 core + +out vec4 frag_color; + +void main() +{ + frag_color = vec4(1.0); +} diff --git a/text_rendering/bounds.vert b/text_rendering/bounds.vert new file mode 100644 index 0000000..8c115a5 --- /dev/null +++ b/text_rendering/bounds.vert @@ -0,0 +1,12 @@ +#version 330 core + +layout(location = 0) in vec4 vertex; + +uniform mat4 projection; + +void main() +{ + gl_Position = projection * vec4(vertex.xy, 0.0, 1.0); +} + + diff --git a/text_rendering/build.sh b/text_rendering/build.sh new file mode 100755 index 0000000..bfca9cc --- /dev/null +++ b/text_rendering/build.sh @@ -0,0 +1,7 @@ +#!/bin/sh +include="-I../libs -I../libs/pwyazh -I/usr/include/freetype2" +cflags="-g -Wall -Wextra $include" +libs='-lm -lGL -lGLEW -lglfw -lfreetype' +target='text_rendering' +set -x +gcc -o $target $cflags $target.c $libs diff --git a/text_rendering/glyph.frag b/text_rendering/glyph.frag new file mode 100644 index 0000000..a6fae4b --- /dev/null +++ b/text_rendering/glyph.frag @@ -0,0 +1,16 @@ +#version 330 core + +in vert_t { + vec2 tex_coords; +} vert; + +out vec4 frag_color; + +uniform sampler2D glyph; +uniform vec3 text_color; + +void main() +{ + vec4 glyph_color = vec4(1.0, 1.0, 1.0, texture(glyph, vert.tex_coords).r); + frag_color = vec4(text_color, 1.0) * glyph_color; +} diff --git a/text_rendering/glyph.vert b/text_rendering/glyph.vert new file mode 100644 index 0000000..ebee1b5 --- /dev/null +++ b/text_rendering/glyph.vert @@ -0,0 +1,18 @@ +#version 330 core + +/* combine position and texture coordinates into vec4 */ +layout(location = 0) in vec4 vertex; + +out vert_t { + vec2 tex_coords; +} vert; + +uniform mat4 projection; + +void main() +{ + gl_Position = projection * vec4(vertex.xy, 0.0, 1.0); + vert.tex_coords = vertex.zw; +} + + diff --git a/text_rendering/ibmvga8x16.ttf b/text_rendering/ibmvga8x16.ttf Binary files differnew file mode 100644 index 0000000..72231a6 --- /dev/null +++ b/text_rendering/ibmvga8x16.ttf 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; +} |