commit a68d1ad8b606109a61f34f354dbf8c0e4a707479
parent 6f5335c418506eb7bdc1432536c334389f436143
Author: rexim <reximkut@gmail.com>
Date: Sun, 11 Jul 2021 22:36:10 +0700
(#27) Add cursor rendering to Free_Glyph
Diffstat:
8 files changed, 223 insertions(+), 24 deletions(-)
diff --git a/build.sh b/build.sh
@@ -6,7 +6,7 @@ CC="${CXX:-cc}"
PKGS="sdl2 glew freetype2"
CFLAGS="-Wall -Wextra -std=c11 -pedantic -ggdb"
LIBS=-lm
-SRC="src/main.c src/la.c src/editor.c src/sdl_extra.c src/file.c src/gl_extra.c src/tile_glyph.c src/free_glyph.c"
+SRC="src/main.c src/la.c src/editor.c src/sdl_extra.c src/file.c src/gl_extra.c src/tile_glyph.c src/free_glyph.c src/cursor_renderer.c"
if [ `uname` = "Darwin" ]; then
CFLAGS+=" -framework OpenGL"
diff --git a/shaders/cursor.frag b/shaders/cursor.frag
@@ -0,0 +1,14 @@
+#version 330 core
+
+#define PERIOD 1.0
+#define BLINK_THRESHOLD 0.5
+
+uniform float time;
+uniform float last_stroke;
+
+void main() {
+ float t = time - last_stroke;
+ float threshold = float(t < BLINK_THRESHOLD);
+ float blink = mod(floor(t / PERIOD), 2);
+ gl_FragColor = vec4(1.0) * min(threshold + blink, 1.0);
+}
diff --git a/shaders/cursor.vert b/shaders/cursor.vert
@@ -0,0 +1,23 @@
+#version 330 core
+
+uniform vec2 resolution;
+uniform vec2 camera;
+uniform vec2 pos;
+uniform float height;
+
+#define WIDTH 5.0
+
+out vec2 uv;
+
+vec2 project_point(vec2 point)
+{
+ return 2.0 * (point - camera) / resolution;
+}
+
+void main() {
+ uv = vec2(float(gl_VertexID & 1), float((gl_VertexID >> 1) & 1));
+ gl_Position = vec4(
+ project_point(uv * vec2(WIDTH, height) + pos),
+ 0.0,
+ 1.0);
+}
diff --git a/src/cursor_renderer.c b/src/cursor_renderer.c
@@ -0,0 +1,48 @@
+#include <stdlib.h>
+#include "./gl_extra.h"
+#include "./cursor_renderer.h"
+
+void cursor_renderer_init(Cursor_Renderer *cr,
+ const char *vert_file_path,
+ const char *frag_file_path)
+{
+ // Init Shaders
+ {
+ GLuint vert_shader = 0;
+ if (!compile_shader_file(vert_file_path, GL_VERTEX_SHADER, &vert_shader)) {
+ exit(1);
+ }
+ GLuint frag_shader = 0;
+ if (!compile_shader_file(frag_file_path, GL_FRAGMENT_SHADER, &frag_shader)) {
+ exit(1);
+ }
+
+ if (!link_program(vert_shader, frag_shader, &cr->program)) {
+ exit(1);
+ }
+
+ glUseProgram(cr->program);
+
+ cr->time_uniform = glGetUniformLocation(cr->program, "time");
+ cr->resolution_uniform = glGetUniformLocation(cr->program, "resolution");
+ cr->camera_uniform = glGetUniformLocation(cr->program, "camera");
+ cr->pos_uniform = glGetUniformLocation(cr->program, "pos");
+ cr->height_uniform = glGetUniformLocation(cr->program, "height");
+ cr->last_stroke_uniform = glGetUniformLocation(cr->program, "last_stroke");
+ }
+}
+
+void cursor_renderer_use(const Cursor_Renderer *cr)
+{
+ glUseProgram(cr->program);
+}
+
+void cursor_renderer_move_to(const Cursor_Renderer *cr, Vec2f pos)
+{
+ glUniform2f(cr->pos_uniform, pos.x, pos.y);
+}
+
+void cursor_renderer_draw()
+{
+ glDrawArrays(GL_TRIANGLE_STRIP, 0, 4);
+}
diff --git a/src/cursor_renderer.h b/src/cursor_renderer.h
@@ -0,0 +1,31 @@
+#ifndef CURSOR_RENDERER_H_
+#define CURSOR_RENDERER_H_
+
+#define GLEW_STATIC
+#include <GL/glew.h>
+
+#define GL_GLEXT_PROTOTYPES
+#include <SDL2/SDL_opengl.h>
+
+#include "./la.h"
+
+typedef struct {
+ GLuint program;
+
+ GLint time_uniform;
+ GLint resolution_uniform;
+ GLint camera_uniform;
+ GLint pos_uniform;
+ GLint height_uniform;
+ GLint last_stroke_uniform;
+} Cursor_Renderer;
+
+void cursor_renderer_init(Cursor_Renderer *cr,
+ const char *vert_file_path,
+ const char *frag_file_path);
+
+void cursor_renderer_use(const Cursor_Renderer *cr);
+void cursor_renderer_move_to(const Cursor_Renderer *cr, Vec2f pos);
+void cursor_renderer_draw(void);
+
+#endif // CURSOR_RENDERER_H_
diff --git a/src/free_glyph.c b/src/free_glyph.c
@@ -102,16 +102,15 @@ void free_glyph_buffer_init(Free_Glyph_Buffer *fgb,
exit(1);
}
- GLuint program = 0;
- if (!link_program(vert_shader, frag_shader, &program)) {
+ if (!link_program(vert_shader, frag_shader, &fgb->program)) {
exit(1);
}
- glUseProgram(program);
+ glUseProgram(fgb->program);
- fgb->time_uniform = glGetUniformLocation(program, "time");
- fgb->resolution_uniform = glGetUniformLocation(program, "resolution");
- fgb->camera_uniform = glGetUniformLocation(program, "camera");
+ fgb->time_uniform = glGetUniformLocation(fgb->program, "time");
+ fgb->resolution_uniform = glGetUniformLocation(fgb->program, "resolution");
+ fgb->camera_uniform = glGetUniformLocation(fgb->program, "camera");
}
// Glyph Texture Atlas
@@ -185,6 +184,13 @@ void free_glyph_buffer_init(Free_Glyph_Buffer *fgb,
}
}
+void free_glyph_buffer_use(const Free_Glyph_Buffer *fgb)
+{
+ glBindVertexArray(fgb->vao);
+ glBindBuffer(GL_ARRAY_BUFFER, fgb->vbo);
+ glUseProgram(fgb->program);
+}
+
void free_glyph_buffer_clear(Free_Glyph_Buffer *fgb)
{
fgb->glyphs_count = 0;
@@ -209,6 +215,21 @@ void free_glyph_buffer_draw(Free_Glyph_Buffer *fgb)
glDrawArraysInstanced(GL_TRIANGLE_STRIP, 0, 4, (GLsizei) fgb->glyphs_count);
}
+float free_glyph_buffer_cursor_pos(const Free_Glyph_Buffer *fgb, const char *text, size_t text_size, Vec2f pos, size_t col)
+{
+ for (size_t i = 0; i < text_size; ++i) {
+ if (i == col) {
+ return pos.x;
+ }
+
+ Glyph_Metric metric = fgb->metrics[(int) text[i]];
+ pos.x += metric.ax;
+ pos.y += metric.ay;
+ }
+
+ return pos.x;
+}
+
void free_glyph_buffer_render_line_sized(Free_Glyph_Buffer *fgb, const char *text, size_t text_size, Vec2f pos, Vec4f fg_color, Vec4f bg_color)
{
for (size_t i = 0; i < text_size; ++i) {
diff --git a/src/free_glyph.h b/src/free_glyph.h
@@ -52,6 +52,7 @@ typedef struct {
typedef struct {
GLuint vao;
GLuint vbo;
+ GLuint program;
FT_UInt atlas_width;
FT_UInt atlas_height;
@@ -72,11 +73,14 @@ void free_glyph_buffer_init(Free_Glyph_Buffer *fgb,
FT_Face face,
const char *vert_file_path,
const char *frag_file_path);
+void free_glyph_buffer_use(const Free_Glyph_Buffer *fgb);
void free_glyph_buffer_clear(Free_Glyph_Buffer *fgb);
void free_glyph_buffer_push(Free_Glyph_Buffer *fgb, Free_Glyph glyph);
void free_glyph_buffer_sync(Free_Glyph_Buffer *fgb);
void free_glyph_buffer_draw(Free_Glyph_Buffer *fgb);
+float free_glyph_buffer_cursor_pos(const Free_Glyph_Buffer *fgb, const char *text, size_t text_size, Vec2f pos, size_t col);
+
void free_glyph_buffer_render_line_sized(Free_Glyph_Buffer *fgb, const char *text, size_t text_size, Vec2f pos, Vec4f fg_color, Vec4f bg_color);
void free_glyph_buffer_render_line(Free_Glyph_Buffer *fgb, const char *text, Vec2f pos, Vec4f fg_color, Vec4f bg_color);
diff --git a/src/main.c b/src/main.c
@@ -26,6 +26,7 @@
#include "./gl_extra.h"
#include "./tile_glyph.h"
#include "./free_glyph.h"
+#include "./cursor_renderer.h"
#define SCREEN_WIDTH 800
#define SCREEN_HEIGHT 600
@@ -80,6 +81,7 @@ void gl_render_cursor(Tile_Glyph_Buffer *tgb)
static Tile_Glyph_Buffer tgb = {0};
#else
static Free_Glyph_Buffer fgb = {0};
+static Cursor_Renderer cr = {0};
#endif
void render_editor_into_tgb(SDL_Window *window, Tile_Glyph_Buffer *tgb, Editor *editor)
@@ -119,31 +121,51 @@ void render_editor_into_tgb(SDL_Window *window, Tile_Glyph_Buffer *tgb, Editor *
// TODO(#27): Free_Glyph renderer does not support cursor
// TODO(#28): Camera location is broken in Free_Glyph Renderer
-void render_editor_into_fgb(SDL_Window *window, Free_Glyph_Buffer *fgb, Editor *editor)
+void render_editor_into_fgb(SDL_Window *window, Free_Glyph_Buffer *fgb, Cursor_Renderer *cr, Editor *editor)
{
+ int w, h;
+ SDL_GetWindowSize(window, &w, &h);
+
+ free_glyph_buffer_use(fgb);
{
- int w, h;
- SDL_GetWindowSize(window, &w, &h);
glUniform2f(fgb->resolution_uniform, (float) w, (float) h);
- }
+ glUniform1f(fgb->time_uniform, (float) SDL_GetTicks() / 1000.0f);
+ glUniform2f(fgb->camera_uniform, camera_pos.x, camera_pos.y);
+
+ free_glyph_buffer_clear(fgb);
- glUniform1f(fgb->time_uniform, (float) SDL_GetTicks() / 1000.0f);
- glUniform2f(fgb->camera_uniform, camera_pos.x, camera_pos.y);
+ {
+ for (size_t row = 0; row < editor->size; ++row) {
+ const Line *line = editor->lines + row;
+ free_glyph_buffer_render_line_sized(
+ fgb, line->chars, line->size,
+ vec2f(0, -(float)row * FREE_GLYPH_FONT_SIZE),
+ vec4fs(1.0f), vec4fs(0.0f));
+ }
+ }
- free_glyph_buffer_clear(fgb);
+ free_glyph_buffer_sync(fgb);
+ free_glyph_buffer_draw(fgb);
+ }
+ cursor_renderer_use(cr);
{
- for (size_t row = 0; row < editor->size; ++row) {
- const Line *line = editor->lines + row;
- free_glyph_buffer_render_line_sized(
- fgb, line->chars, line->size,
- vec2f(0, -(float)row * FREE_GLYPH_FONT_SIZE),
- vec4fs(1.0f), vec4fs(0.0f));
+ glUniform2f(cr->resolution_uniform, (float) w, (float) h);
+ glUniform1f(cr->time_uniform, (float) SDL_GetTicks() / 1000.0f);
+ glUniform2f(cr->camera_uniform, camera_pos.x, camera_pos.y);
+ glUniform1f(cr->height_uniform, FREE_GLYPH_FONT_SIZE);
+
+ float y = -(float) editor->cursor_row * FREE_GLYPH_FONT_SIZE;
+
+ float x = 0.0f;
+ if (editor->cursor_row < editor->size) {
+ Line *line = &editor->lines[editor->cursor_row];
+ x = free_glyph_buffer_cursor_pos(fgb, line->chars, line->size, vec2f(0.0, y), editor->cursor_col);
}
- }
- free_glyph_buffer_sync(fgb);
- free_glyph_buffer_draw(fgb);
+ cursor_renderer_move_to(cr, vec2f(x, y));
+ cursor_renderer_draw();
+ }
}
int main(int argc, char **argv)
@@ -246,6 +268,9 @@ int main(int argc, char **argv)
face,
"./shaders/free_glyph.vert",
"./shaders/free_glyph.frag");
+ cursor_renderer_init(&cr,
+ "./shaders/cursor.vert",
+ "./shaders/cursor.frag");
#endif
bool quit = false;
@@ -263,6 +288,10 @@ int main(int argc, char **argv)
switch (event.key.keysym.sym) {
case SDLK_BACKSPACE: {
editor_backspace(&editor);
+#ifndef TILE_GLYPH_RENDER
+ cursor_renderer_use(&cr);
+ glUniform1f(cr.last_stroke_uniform, (float) SDL_GetTicks() / 1000.0f);
+#endif
}
break;
@@ -275,11 +304,19 @@ int main(int argc, char **argv)
case SDLK_RETURN: {
editor_insert_new_line(&editor);
+#ifndef TILE_GLYPH_RENDER
+ cursor_renderer_use(&cr);
+ glUniform1f(cr.last_stroke_uniform, (float) SDL_GetTicks() / 1000.0f);
+#endif
}
break;
case SDLK_DELETE: {
editor_delete(&editor);
+#ifndef TILE_GLYPH_RENDER
+ cursor_renderer_use(&cr);
+ glUniform1f(cr.last_stroke_uniform, (float) SDL_GetTicks() / 1000.0f);
+#endif
}
break;
@@ -287,23 +324,40 @@ int main(int argc, char **argv)
if (editor.cursor_row > 0) {
editor.cursor_row -= 1;
}
+#ifndef TILE_GLYPH_RENDER
+ cursor_renderer_use(&cr);
+ glUniform1f(cr.last_stroke_uniform, (float) SDL_GetTicks() / 1000.0f);
+#endif
}
break;
case SDLK_DOWN: {
editor.cursor_row += 1;
+#ifndef TILE_GLYPH_RENDER
+ cursor_renderer_use(&cr);
+ glUniform1f(cr.last_stroke_uniform, (float) SDL_GetTicks() / 1000.0f);
+#endif
}
break;
case SDLK_LEFT: {
if (editor.cursor_col > 0) {
editor.cursor_col -= 1;
+#ifndef TILE_GLYPH_RENDER
+ cursor_renderer_use(&cr);
+ glUniform1f(cr.last_stroke_uniform, (float) SDL_GetTicks() / 1000.0f);
+#endif
}
}
break;
case SDLK_RIGHT: {
editor.cursor_col += 1;
+#ifndef TILE_GLYPH_RENDER
+ cursor_renderer_use(&cr);
+ glUniform1f(cr.last_stroke_uniform, (float) SDL_GetTicks() / 1000.0f);
+#endif
+
}
break;
}
@@ -312,6 +366,10 @@ int main(int argc, char **argv)
case SDL_TEXTINPUT: {
editor_insert_text_before_cursor(&editor, event.text.text);
+#ifndef TILE_GLYPH_RENDER
+ cursor_renderer_use(&cr);
+ glUniform1f(cr.last_stroke_uniform, (float) SDL_GetTicks() / 1000.0f);
+#endif
}
break;
@@ -360,7 +418,7 @@ int main(int argc, char **argv)
#ifdef TILE_GLYPH_RENDER
render_editor_into_tgb(window, &tgb, &editor);
#else
- render_editor_into_fgb(window, &fgb, &editor);
+ render_editor_into_fgb(window, &fgb, &cr, &editor);
#endif // TILE_GLYPH_RENDER
SDL_GL_SwapWindow(window);