opengl-triangle

Basic application that draws triangle on a window using opengl
Index Commits Files Refs
main.c (6403B)
   1 #include <stdio.h>
   2 #include <stdlib.h>
   3 #include <errno.h>
   4 #include <string.h>
   5 #include <stdbool.h>
   6 
   7 #include "glad.h"
   8 #include <GLFW/glfw3.h>
   9 
  10 #define WINDOW_TITLE "OpenGL Example"
  11 
  12 #define LOG_INFO(str) do {    \
  13     printf("[LOG]: %s\n", str);    \
  14 } while (0)
  15 
  16 #define LOG_ERROR(str) do {   \
  17     fprintf(stderr, "[ERROR]: ");   \
  18     fprintf(stderr, "%s\n", str);  \
  19 } while (0)
  20 
  21 float min(float a, float b) {
  22     return (a < b) ? a : b;
  23 }
  24 
  25 void framebuffer_size_callback(GLFWwindow* window, int width, int height) {
  26     float min_side = min((float)width, (float)height);
  27 
  28     float x = ((float)width - min_side) / 2;
  29     float y = ((float)height - min_side) / 2;
  30 
  31     glViewport(x, y, min_side, min_side);
  32 }
  33 
  34 char *load_file(const char *file_path) {
  35     FILE *fp;
  36     char *new_str;
  37     size_t new_str_len;
  38 
  39     if((fp = fopen(file_path, "rb")) == NULL) {
  40         fprintf(stderr, "`%s`: %s\n", file_path, strerror(errno));
  41         return NULL;
  42     }
  43     
  44     fseek (fp, 0, SEEK_END);
  45     new_str_len = ftell(fp);
  46     fseek (fp, 0, SEEK_SET);
  47 
  48     new_str = malloc(new_str_len + 1);
  49 
  50     if(new_str == NULL) {
  51         fprintf(stderr, "`%s`: %s\n", file_path, strerror(errno));
  52         return NULL;
  53     }
  54 
  55     fread (new_str, sizeof(char), new_str_len, fp);
  56     new_str[new_str_len] = '\0';
  57     fclose(fp);
  58 
  59     return new_str;
  60 }
  61 
  62 bool new_shader_from_file_path(GLenum type, GLuint *shader, const char *file_path) {
  63     const char *shader_source = load_file(file_path);
  64 
  65     if(shader_source == NULL) {
  66         return false;
  67     }
  68 
  69     *shader = glCreateShader(type);
  70 
  71     int return_value;
  72     char compile_log[512];
  73 
  74     glShaderSource(*shader, 1, &shader_source, NULL);
  75     LOG_INFO("Compiling shader");
  76     glCompileShader(*shader);
  77     glGetShaderiv(*shader, GL_COMPILE_STATUS, &return_value);
  78     if(return_value == GL_FALSE) {
  79         glGetShaderInfoLog(*shader, 512, NULL, compile_log);
  80         *shader = 0;
  81         LOG_ERROR("Compilation of shader failed");
  82         // LOG_ERROR(strstr(compile_log, "error:") + 7);
  83         fprintf(stderr, "%s", compile_log);
  84         return false;
  85     }
  86 
  87     return true;
  88 }
  89 
  90 
  91 bool new_program_from_shaders(GLuint *shader_program,
  92         GLuint *vertex_shader, GLuint *fragment_shader) {
  93 
  94     int return_value;
  95     char link_log[512];
  96 
  97     // link both vertex and fragment shaders
  98     *shader_program = glCreateProgram();
  99 
 100     glAttachShader(*shader_program, *vertex_shader);
 101     glAttachShader(*shader_program, *fragment_shader);
 102     LOG_INFO("Linking shaders");
 103     glLinkProgram(*shader_program);
 104     glGetProgramiv(*shader_program, GL_LINK_STATUS, &return_value);
 105     if(return_value == GL_FALSE) {
 106         glGetProgramInfoLog(*shader_program, 512, NULL, link_log);
 107         LOG_ERROR("Linking shaders failed");
 108         LOG_ERROR(link_log + 7); // remove the `error: ` prefix of message
 109         return false;
 110     }
 111 
 112     return true;
 113 }
 114 
 115 bool new_program_from_shaders_source(GLuint *shader_program,
 116         const char *vert_shader_file_path, const char *frag_shader_file_path) {
 117 
 118     GLuint vertex_shader, fragment_shader;
 119 
 120     if(!new_shader_from_file_path(GL_VERTEX_SHADER,
 121                 &vertex_shader, vert_shader_file_path)) {
 122 
 123         return false;
 124     }
 125 
 126     if(!new_shader_from_file_path(GL_FRAGMENT_SHADER,
 127                 &fragment_shader, frag_shader_file_path)) {
 128 
 129         return false;
 130     }
 131 
 132     if(!new_program_from_shaders(shader_program, &vertex_shader, &fragment_shader)) {
 133         return false;
 134     }
 135 
 136     // program with shaders compiled and linked properly
 137     return true;
 138 }
 139 
 140 GLuint current_program;
 141 
 142 bool reload_shaders(const char *vert_shader_file_path, const char *frag_shader_file_path) {
 143     LOG_INFO("Recompiling shaders");
 144 
 145     GLuint new_program;
 146     if(!new_program_from_shaders_source(&new_program,
 147             vert_shader_file_path, frag_shader_file_path)) {
 148 
 149         LOG_ERROR("Could not recompile shaders. Keeping previous version");
 150         return false;
 151     }
 152     glDeleteProgram(current_program);
 153     current_program = new_program;
 154     LOG_INFO("Done reloading shaders");
 155     return true;
 156 }
 157 
 158 // https://www.glfw.org/docs/3.3/group__keys.html
 159 static void key_handler(GLFWwindow *window,
 160         int key, int scancode, int action, int mods) {
 161 
 162     if(action == GLFW_PRESS) {
 163         switch(key) {
 164             case GLFW_KEY_ESCAPE:
 165             case GLFW_KEY_Q:
 166                 glfwSetWindowShouldClose(window, GLFW_TRUE);
 167                 break;
 168             case GLFW_KEY_F5:
 169                 reload_shaders("main.vert", "main.frag");
 170                 break;
 171             default:
 172                 break;
 173         }
 174     }
 175 }
 176 
 177 void glfw_error_callback(int error_code, const char* description) {
 178     fprintf(stderr, "%s\n", description);
 179 }
 180 
 181 int setup_glfw(GLFWwindow **window) {
 182     glfwInit();
 183     glfwWindowHint(GLFW_CONTEXT_VERSION_MAJOR, 3);
 184     glfwWindowHint(GLFW_CONTEXT_VERSION_MINOR, 3);
 185     glfwWindowHint(GLFW_OPENGL_PROFILE, GLFW_OPENGL_CORE_PROFILE);
 186     glfwSetErrorCallback(glfw_error_callback);
 187 
 188     *window = glfwCreateWindow(800, 800, WINDOW_TITLE, NULL, NULL);
 189     if (*window == NULL) {
 190         LOG_ERROR("Failed to create GLFW window");
 191         glfwTerminate();
 192         return glfwGetError(NULL);
 193     }
 194 
 195     glfwMakeContextCurrent(*window);
 196     if (!gladLoadGLLoader((GLADloadproc)glfwGetProcAddress)) {
 197         LOG_ERROR("Failed to initialize GLAD");
 198         glfwTerminate();
 199         return glfwGetError(NULL);
 200     }
 201 
 202     // bind key_handler
 203     glfwSetKeyCallback(*window, key_handler);
 204 
 205     // bind the callback which sets the rendering context size on window resize
 206     glfwSetFramebufferSizeCallback(*window, framebuffer_size_callback);
 207     return 0;
 208 }
 209 
 210 int main (void) {
 211     GLuint VBO, VAO; // Vertex Buffer Objects, Vertex Array Object
 212     GLFWwindow *window;
 213 
 214     float vertices[] = {
 215         -0.50f, -0.400f, 0.00f,
 216          0.50f, -0.400f, 0.00f,
 217          0.00f,  0.466f, 0.00f
 218     };
 219 
 220     int error_code;
 221     if((error_code = setup_glfw(&window)) != 0) {
 222         return error_code;
 223     }
 224 
 225     glGenBuffers(1, &VBO);
 226     glBindBuffer(GL_ARRAY_BUFFER, VBO);
 227     glBufferData(GL_ARRAY_BUFFER, sizeof(vertices), vertices, GL_STATIC_DRAW);
 228 
 229     glGenVertexArrays(1, &VAO);
 230     glBindVertexArray(VAO);
 231 
 232     glVertexAttribPointer(0, 3, GL_FLOAT, GL_FALSE, 3*sizeof(float), (void*)0);
 233     glEnableVertexAttribArray(0);
 234 
 235     if(!new_program_from_shaders_source(&current_program, "main.vert", "main.frag")) {
 236         glfwDestroyWindow(window);
 237         glfwTerminate();
 238         return -1;
 239     }
 240 
 241     float time;
 242     int time_uniform = glGetUniformLocation(current_program, "time");
 243     while(!glfwWindowShouldClose(window)) {
 244         // glClearColor(0.2f, 0.3f, 0.3f, 1.0f);
 245         glClearColor(0.0f, 0.0f, 0.0f, 1.0f);
 246         glClear(GL_COLOR_BUFFER_BIT);
 247 
 248         time = glfwGetTime();
 249         glUseProgram(current_program);
 250         glUniform1f(time_uniform, time);
 251         
 252         glBindVertexArray(VAO);
 253         glDrawArrays(GL_TRIANGLES, 0, 3);
 254         glfwSwapBuffers(window);
 255         glfwPollEvents();
 256     }
 257 
 258     LOG_INFO("Freeing resources");
 259     glDeleteProgram(current_program);
 260     glfwDestroyWindow(window);
 261     glfwTerminate();
 262     return 0;
 263 }