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(¤t_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 }