Browse Source

Quick and rough implementation of the OpenGL renderer

rexim 4 years ago
parent
commit
3ba4d91b99
10 changed files with 523 additions and 3 deletions
  1. 4 3
      Makefile
  2. 36 0
      shaders/font.frag
  3. 32 0
      shaders/font.vert
  4. 36 0
      src/file.c
  5. 6 0
      src/file.h
  6. 72 0
      src/gl_extra.c
  7. 13 0
      src/gl_extra.h
  8. 39 0
      src/la.c
  9. 12 0
      src/la.h
  10. 273 0
      src/main.c

+ 4 - 3
Makefile

@@ -1,6 +1,7 @@
-CFLAGS=-Wall -Wextra -std=c11 -pedantic -ggdb `pkg-config --cflags sdl2`
-LIBS=`pkg-config --libs sdl2` -lm
-SRCS=src/main.c src/la.c src/editor.c src/font.c src/sdl_extra.c
+PKGS=sdl2 glew
+CFLAGS=-Wall -Wextra -std=c11 -pedantic -ggdb `pkg-config --cflags $(PKGS)`
+LIBS=`pkg-config --libs $(PKGS)` -lm
+SRCS=src/main.c src/la.c src/editor.c src/font.c src/sdl_extra.c src/file.c src/gl_extra.c
 
 
 te: $(SRCS)
 te: $(SRCS)
 	$(CC) $(CFLAGS) -o te $(SRCS) $(LIBS)
 	$(CC) $(CFLAGS) -o te $(SRCS) $(LIBS)

+ 36 - 0
shaders/font.frag

@@ -0,0 +1,36 @@
+#version 330 core
+
+#define FONT_WIDTH 128
+#define FONT_HEIGHT 64
+#define FONT_COLS 18
+#define FONT_ROWS 7
+#define FONT_CHAR_WIDTH  (FONT_WIDTH  / FONT_COLS)
+#define FONT_CHAR_HEIGHT (FONT_HEIGHT / FONT_ROWS)
+#define FONT_CHAR_WIDTH_UV  (float(FONT_CHAR_WIDTH) / float(FONT_WIDTH))
+#define FONT_CHAR_HEIGHT_UV (float(FONT_CHAR_HEIGHT) / float(FONT_HEIGHT))
+
+#define ASCII_DISPLAY_LOW 32
+#define ASCII_DISPLAY_HIGH 126
+
+uniform sampler2D font;
+uniform float time;
+
+in vec2 uv;
+in float glyph_ch;
+in vec4 glyph_color;
+
+void main() {
+    int ch = int(glyph_ch);
+    if (!(ASCII_DISPLAY_LOW <= ch && ch <= ASCII_DISPLAY_HIGH)) {
+        ch = 63;
+    }
+
+    int index = ch - 32;
+    float x = float(index % FONT_COLS) * FONT_CHAR_WIDTH_UV;
+    float y = float(index / FONT_COLS) * FONT_CHAR_HEIGHT_UV;
+    vec2 pos = vec2(x, y + FONT_CHAR_HEIGHT_UV);
+    vec2 size = vec2(FONT_CHAR_WIDTH_UV, -FONT_CHAR_HEIGHT_UV);
+    vec2 t = pos + size * uv;
+
+    gl_FragColor = texture(font, t)* glyph_color;
+}

+ 32 - 0
shaders/font.vert

@@ -0,0 +1,32 @@
+#version 330 core
+
+#define FONT_WIDTH 128
+#define FONT_HEIGHT 64
+#define FONT_COLS 18
+#define FONT_ROWS 7
+#define FONT_CHAR_WIDTH  (FONT_WIDTH  / FONT_COLS)
+#define FONT_CHAR_HEIGHT (FONT_HEIGHT / FONT_ROWS)
+
+uniform vec2 resolution;
+
+layout(location = 0) in vec2 pos;
+layout(location = 1) in float scale;
+layout(location = 2) in float ch;
+layout(location = 3) in vec4 color;
+
+out vec2 uv;
+out float glyph_ch;
+out vec4 glyph_color;
+
+vec2 project_point(vec2 point)
+{
+    return 2.0 * point / resolution;
+}
+
+void main() {
+    uv = vec2(float(gl_VertexID & 1), float((gl_VertexID >> 1) & 1));
+    vec2 char_size = vec2(float(FONT_CHAR_WIDTH), float(FONT_CHAR_HEIGHT));
+    gl_Position = vec4(project_point(uv * char_size * scale + pos), 0.0, 1.0);
+    glyph_ch = ch;
+    glyph_color = color;
+}

+ 36 - 0
src/file.c

@@ -0,0 +1,36 @@
+#include <stdio.h>
+#include <stdlib.h>
+#include <errno.h>
+#include <string.h>
+#include "./file.h"
+
+char *slurp_file(const char *file_path)
+{
+#define SLURP_FILE_PANIC \
+    do { \
+        fprintf(stderr, "Could not read file `%s`: %s\n", file_path, strerror(errno)); \
+        exit(1); \
+    } while (0)
+
+    FILE *f = fopen(file_path, "r");
+    if (f == NULL) SLURP_FILE_PANIC;
+    if (fseek(f, 0, SEEK_END) < 0) SLURP_FILE_PANIC;
+
+    long size = ftell(f);
+    if (size < 0) SLURP_FILE_PANIC;
+
+    char *buffer = malloc(size + 1);
+    if (buffer == NULL) SLURP_FILE_PANIC;
+
+    if (fseek(f, 0, SEEK_SET) < 0) SLURP_FILE_PANIC;
+
+    fread(buffer, 1, size, f);
+    if (ferror(f) < 0) SLURP_FILE_PANIC;
+
+    buffer[size] = '\0';
+
+    if (fclose(f) < 0) SLURP_FILE_PANIC;
+
+    return buffer;
+#undef SLURP_FILE_PANIC
+}

+ 6 - 0
src/file.h

@@ -0,0 +1,6 @@
+#ifndef FILE_H_
+#define FILE_H_
+
+char *slurp_file(const char *file_path);
+
+#endif // FILE_H_

+ 72 - 0
src/gl_extra.c

@@ -0,0 +1,72 @@
+#include <stdio.h>
+#include <stdlib.h>
+#include "file.h"
+#include "gl_extra.h"
+
+const char *shader_type_as_cstr(GLuint shader)
+{
+    switch (shader) {
+    case GL_VERTEX_SHADER:
+        return "GL_VERTEX_SHADER";
+    case GL_FRAGMENT_SHADER:
+        return "GL_FRAGMENT_SHADER";
+    default:
+        return "(Unknown)";
+    }
+}
+
+bool compile_shader_source(const GLchar *source, GLenum shader_type, GLuint *shader)
+{
+    *shader = glCreateShader(shader_type);
+    glShaderSource(*shader, 1, &source, NULL);
+    glCompileShader(*shader);
+
+    GLint compiled = 0;
+    glGetShaderiv(*shader, GL_COMPILE_STATUS, &compiled);
+
+    if (!compiled) {
+        GLchar message[1024];
+        GLsizei message_size = 0;
+        glGetShaderInfoLog(*shader, sizeof(message), &message_size, message);
+        fprintf(stderr, "ERROR: could not compile %s\n", shader_type_as_cstr(shader_type));
+        fprintf(stderr, "%.*s\n", message_size, message);
+        return false;
+    }
+
+    return true;
+}
+
+bool compile_shader_file(const char *file_path, GLenum shader_type, GLuint *shader)
+{
+    char *source = slurp_file(file_path);
+    bool ok = compile_shader_source(source, shader_type, shader);
+    if (!ok) {
+        fprintf(stderr, "ERROR: failed to compile `%s` shader file\n", file_path);
+    }
+    free(source);
+    return ok;
+}
+
+bool link_program(GLuint vert_shader, GLuint frag_shader, GLuint *program)
+{
+    *program = glCreateProgram();
+
+    glAttachShader(*program, vert_shader);
+    glAttachShader(*program, frag_shader);
+    glLinkProgram(*program);
+
+    GLint linked = 0;
+    glGetProgramiv(*program, GL_LINK_STATUS, &linked);
+    if (!linked) {
+        GLsizei message_size = 0;
+        GLchar message[1024];
+
+        glGetProgramInfoLog(*program, sizeof(message), &message_size, message);
+        fprintf(stderr, "Program Linking: %.*s\n", message_size, message);
+    }
+
+    glDeleteShader(vert_shader);
+    glDeleteShader(frag_shader);
+
+    return program;
+}

+ 13 - 0
src/gl_extra.h

@@ -0,0 +1,13 @@
+#ifndef GL_EXTRA_H_
+#define GL_EXTRA_H_
+
+#define GL_GLEXT_PROTOTYPES
+#include <SDL2/SDL_opengl.h>
+
+#include <stdbool.h>
+
+bool compile_shader_source(const GLchar *source, GLenum shader_type, GLuint *shader);
+bool compile_shader_file(const char *file_path, GLenum shader_type, GLuint *shader);
+bool link_program(GLuint vert_shader, GLuint frag_shader, GLuint *program);
+
+#endif // GL_EXTRA_H_

+ 39 - 0
src/la.c

@@ -28,8 +28,47 @@ Vec2f vec2f_mul(Vec2f a, Vec2f b)
     return vec2f(a.x * b.x, a.y * b.y);
     return vec2f(a.x * b.x, a.y * b.y);
 }
 }
 
 
+Vec2f vec2f_mul3(Vec2f a, Vec2f b, Vec2f c)
+{
+    return vec2f_mul(vec2f_mul(a, b), c);
+}
+
 Vec2f vec2f_div(Vec2f a, Vec2f b)
 Vec2f vec2f_div(Vec2f a, Vec2f b)
 {
 {
     return vec2f(a.x / b.x, a.y / b.y);
     return vec2f(a.x / b.x, a.y / b.y);
 }
 }
 
 
+Vec4f vec4f(float x, float y, float z, float w)
+{
+    return (Vec4f) {
+        .x = x,
+        .y = y,
+        .z = z,
+        .w = w,
+    };
+}
+
+Vec4f vec4fs(float x)
+{
+    return vec4f(x, x, x, x);
+}
+
+Vec4f vec4f_add(Vec4f a, Vec4f b)
+{
+    return vec4f(a.x + b.x, a.y + b.y, a.z + b.z, a.w + b.w);
+}
+
+Vec4f vec4f_sub(Vec4f a, Vec4f b)
+{
+    return vec4f(a.x - b.x, a.y - b.y, a.z - b.z, a.w - b.w);
+}
+
+Vec4f vec4f_mul(Vec4f a, Vec4f b)
+{
+    return vec4f(a.x * b.x, a.y * b.y, a.z * b.z, a.w * b.w);
+}
+
+Vec4f vec4f_div(Vec4f a, Vec4f b)
+{
+    return vec4f(a.x / b.x, a.y / b.y, a.z / b.z, a.w / b.w);
+}

+ 12 - 0
src/la.h

@@ -10,6 +10,18 @@ Vec2f vec2fs(float x);
 Vec2f vec2f_add(Vec2f a, Vec2f b);
 Vec2f vec2f_add(Vec2f a, Vec2f b);
 Vec2f vec2f_sub(Vec2f a, Vec2f b);
 Vec2f vec2f_sub(Vec2f a, Vec2f b);
 Vec2f vec2f_mul(Vec2f a, Vec2f b);
 Vec2f vec2f_mul(Vec2f a, Vec2f b);
+Vec2f vec2f_mul3(Vec2f a, Vec2f b, Vec2f c);
 Vec2f vec2f_div(Vec2f a, Vec2f b);
 Vec2f vec2f_div(Vec2f a, Vec2f b);
 
 
+typedef struct {
+    float x, y, z, w;
+} Vec4f;
+
+Vec4f vec4f(float x, float y, float z, float w);
+Vec4f vec4fs(float x);
+Vec4f vec4f_add(Vec4f a, Vec4f b);
+Vec4f vec4f_sub(Vec4f a, Vec4f b);
+Vec4f vec4f_mul(Vec4f a, Vec4f b);
+Vec4f vec4f_div(Vec4f a, Vec4f b);
+
 #endif // LA_H_
 #endif // LA_H_

+ 273 - 0
src/main.c

@@ -4,6 +4,12 @@
 
 
 #include <SDL.h>
 #include <SDL.h>
 
 
+#include <SDL2/SDL.h>
+#define GLEW_STATIC
+#include <GL/glew.h>
+#define GL_GLEXT_PROTOTYPES
+#include <SDL2/SDL_opengl.h>
+
 #define STB_IMAGE_IMPLEMENTATION
 #define STB_IMAGE_IMPLEMENTATION
 #include "./stb_image.h"
 #include "./stb_image.h"
 
 
@@ -13,6 +19,7 @@
 #include "./editor.h"
 #include "./editor.h"
 #include "./la.h"
 #include "./la.h"
 #include "./sdl_extra.h"
 #include "./sdl_extra.h"
+#include "./gl_extra.h"
 #include "./font.h"
 #include "./font.h"
 
 
 #define SCREEN_WIDTH 800
 #define SCREEN_WIDTH 800
@@ -70,6 +77,271 @@ void usage(FILE *stream)
 // TODO: Delete line
 // TODO: Delete line
 // TODO: Split the line on Enter
 // TODO: Split the line on Enter
 
 
+// #define OPENGL_RENDERER
+
+#ifdef OPENGL_RENDERER
+void MessageCallback(GLenum source,
+                     GLenum type,
+                     GLuint id,
+                     GLenum severity,
+                     GLsizei length,
+                     const GLchar* message,
+                     const void* userParam)
+{
+    (void) source;
+    (void) id;
+    (void) length;
+    (void) userParam;
+    fprintf(stderr, "GL CALLBACK: %s type = 0x%x, severity = 0x%x, message = %s\n",
+            (type == GL_DEBUG_TYPE_ERROR ? "** GL ERROR **" : ""),
+            type, severity, message);
+}
+
+typedef struct {
+    Vec2f pos;
+    float scale;
+    float ch;
+    Vec4f color;
+} Glyph;
+
+typedef enum {
+    GLYPH_ATTR_POS = 0,
+    GLYPH_ATTR_SCALE,
+    GLYPH_ATTR_CH,
+    GLYPH_ATTR_COLOR,
+    COUNT_GLYPH_ATTRS,
+} Glyph_Attr;
+
+typedef struct {
+    size_t offset;
+    size_t comps;
+} Glyph_Attr_Def;
+
+static const Glyph_Attr_Def glyph_attr_defs[COUNT_GLYPH_ATTRS] = {
+    [GLYPH_ATTR_POS]   = {
+        .offset = offsetof(Glyph, pos),
+        .comps = 2,
+    },
+    [GLYPH_ATTR_SCALE] = {
+        .offset = offsetof(Glyph, scale),
+        .comps = 1,
+    },
+    [GLYPH_ATTR_CH]    = {
+        .offset = offsetof(Glyph, ch),
+        .comps = 1,
+    },
+    [GLYPH_ATTR_COLOR] = {
+        .offset = offsetof(Glyph, color),
+        .comps = 4,
+    },
+};
+static_assert(COUNT_GLYPH_ATTRS == 4, "The amount of glyph vertex attributes have changed");
+
+#define GLYPH_BUFFER_CAP 1024
+
+Glyph glyph_buffer[GLYPH_BUFFER_CAP];
+size_t glyph_buffer_count = 0;
+
+void glyph_buffer_push(Glyph glyph)
+{
+    assert(glyph_buffer_count < GLYPH_BUFFER_CAP);
+    glyph_buffer[glyph_buffer_count++] = glyph;
+}
+
+void glyph_buffer_sync(void)
+{
+    glBufferSubData(GL_ARRAY_BUFFER,
+                    0,
+                    glyph_buffer_count * sizeof(Glyph),
+                    glyph_buffer);
+}
+
+void gl_render_text(const char *text, size_t text_size,
+                    Vec2f pos, float scale, Vec4f color)
+{
+    for (size_t i = 0; i < text_size; ++i) {
+        const Vec2f char_size = vec2f(FONT_CHAR_WIDTH, FONT_CHAR_HEIGHT);
+        const Glyph glyph = {
+            .pos = vec2f_add(pos, vec2f_mul3(char_size,
+                                             vec2f((float) i, 0.0f),
+                                             vec2fs(scale))),
+            .scale = scale,
+            .ch = (float) text[i],
+            .color = color
+        };
+        glyph_buffer_push(glyph);
+    }
+}
+
+int main(int argc, char **argv)
+{
+    (void) argc;
+    (void) argv;
+
+    scc(SDL_Init(SDL_INIT_VIDEO));
+
+    SDL_Window *window =
+        scp(SDL_CreateWindow("Text Editor",
+                             0, 0,
+                             SCREEN_WIDTH, SCREEN_HEIGHT,
+                             SDL_WINDOW_RESIZABLE | SDL_WINDOW_OPENGL));
+
+    {
+        SDL_GL_SetAttribute(SDL_GL_CONTEXT_MAJOR_VERSION, 3);
+        SDL_GL_SetAttribute(SDL_GL_CONTEXT_MINOR_VERSION, 3);
+        SDL_GL_SetAttribute(SDL_GL_CONTEXT_PROFILE_MASK, SDL_GL_CONTEXT_PROFILE_CORE);
+
+        int major;
+        int minor;
+        SDL_GL_GetAttribute(SDL_GL_CONTEXT_MAJOR_VERSION, &major);
+        SDL_GL_GetAttribute(SDL_GL_CONTEXT_MINOR_VERSION, &minor);
+        printf("GL version %d.%d\n", major, minor);
+    }
+
+    scp(SDL_GL_CreateContext(window));
+
+    if (GLEW_OK != glewInit()) {
+        fprintf(stderr, "Could not initialize GLEW!");
+        exit(1);
+    }
+
+    if (!GLEW_ARB_draw_instanced) {
+        fprintf(stderr, "ARB_draw_instanced is not supported; game may not work properly!!\n");
+        exit(1);
+    }
+
+    if (!GLEW_ARB_instanced_arrays) {
+        fprintf(stderr, "ARB_instanced_arrays is not supported; game may not work properly!!\n");
+        exit(1);
+    }
+
+    glEnable(GL_BLEND);
+    glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
+
+    if (GLEW_ARB_debug_output) {
+        glEnable(GL_DEBUG_OUTPUT);
+        glDebugMessageCallback(MessageCallback, 0);
+    } else {
+        fprintf(stderr, "WARNING! GLEW_ARB_debug_output is not available");
+    }
+
+    GLint time_uniform;
+    GLint resolution_uniform;
+
+    // Initialize Shaders
+    {
+        GLuint vert_shader = 0;
+        if (!compile_shader_file("./shaders/font.vert", GL_VERTEX_SHADER, &vert_shader)) {
+            exit(1);
+        }
+        GLuint frag_shader = 0;
+        if (!compile_shader_file("./shaders/font.frag", GL_FRAGMENT_SHADER, &frag_shader)) {
+            exit(1);
+        }
+
+        GLuint program = 0;
+        if (!link_program(vert_shader, frag_shader, &program)) {
+            exit(1);
+        }
+
+        glUseProgram(program);
+
+        time_uniform = glGetUniformLocation(program, "time");
+        resolution_uniform = glGetUniformLocation(program, "resolution");
+
+        glUniform2f(resolution_uniform, SCREEN_WIDTH, SCREEN_HEIGHT);
+    }
+
+    // Init Font Texture
+    {
+        const char *file_path = "charmap-oldschool_white.png";
+        int width, height, n;
+        unsigned char *pixels = stbi_load(file_path, &width, &height, &n, STBI_rgb_alpha);
+        if (pixels == NULL) {
+            fprintf(stderr, "ERROR: could not load file %s: %s\n",
+                    file_path, stbi_failure_reason());
+            exit(1);
+        }
+
+        glActiveTexture(GL_TEXTURE0);
+
+        GLuint font_texture = 0;
+        glGenTextures(1, &font_texture);
+        glBindTexture(GL_TEXTURE_2D, font_texture);
+
+        glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
+        glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
+        glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
+        glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
+
+        glTexImage2D(GL_TEXTURE_2D,
+                     0,
+                     GL_RGBA,
+                     width,
+                     height,
+                     0,
+                     GL_RGBA,
+                     GL_UNSIGNED_BYTE,
+                     pixels);
+    }
+
+    // Init Buffers
+    {
+        GLuint vao = 0;
+        glGenVertexArrays(1, &vao);
+        glBindVertexArray(vao);
+
+        GLuint vbo = 0;
+        glGenBuffers(1, &vbo);
+        glBindBuffer(GL_ARRAY_BUFFER, vbo);
+        glBufferData(GL_ARRAY_BUFFER,
+                     sizeof(glyph_buffer),
+                     glyph_buffer,
+                     GL_DYNAMIC_DRAW);
+
+        for (Glyph_Attr attr = 0; attr < COUNT_GLYPH_ATTRS; ++attr) {
+            glEnableVertexAttribArray(attr);
+            glVertexAttribPointer(
+                attr,
+                glyph_attr_defs[attr].comps,
+                GL_FLOAT,
+                GL_FALSE,
+                sizeof(Glyph),
+                (void*) glyph_attr_defs[attr].offset);
+            glVertexAttribDivisor(attr, 1);
+        }
+    }
+
+    const char *text = "Hello, World";
+    Vec4f color = vec4f(1.0f, 0.0f, 0.0f, 1.0f);
+    gl_render_text(text, strlen(text), vec2fs(0.0f), 5.0f, color);
+    glyph_buffer_sync();
+
+    bool quit = false;
+    while (!quit) {
+        SDL_Event event = {0};
+        while (SDL_PollEvent(&event)) {
+            switch (event.type) {
+            case SDL_QUIT: {
+                quit = true;
+            }
+            break;
+            }
+        }
+
+        glClearColor(0.0f, 0.0f, 0.0f, 1.0f);
+        glClear(GL_COLOR_BUFFER_BIT);
+
+        glUniform1f(time_uniform, (float) SDL_GetTicks() / 1000.0f);
+
+        glDrawArraysInstanced(GL_TRIANGLE_STRIP, 0, 4, glyph_buffer_count);
+
+        SDL_GL_SwapWindow(window);
+    }
+
+    return 0;
+}
+#else
 int main(int argc, char **argv)
 int main(int argc, char **argv)
 {
 {
     const char *file_path = NULL;
     const char *file_path = NULL;
@@ -205,3 +477,4 @@ int main(int argc, char **argv)
 
 
     return 0;
     return 0;
 }
 }
+#endif // OPENGL_RENDERER