summaryrefslogtreecommitdiff
path: root/text_rendering
diff options
context:
space:
mode:
Diffstat (limited to 'text_rendering')
-rw-r--r--text_rendering/bounds.frag8
-rw-r--r--text_rendering/bounds.vert12
-rwxr-xr-xtext_rendering/build.sh7
-rw-r--r--text_rendering/glyph.frag16
-rw-r--r--text_rendering/glyph.vert18
-rw-r--r--text_rendering/ibmvga8x16.ttfbin0 -> 69760 bytes
-rw-r--r--text_rendering/text_rendering.c199
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
new file mode 100644
index 0000000..72231a6
--- /dev/null
+++ b/text_rendering/ibmvga8x16.ttf
Binary files differ
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;
+}