diff --git a/main.c b/main.c index b8f46bc..27b3dbe 100644 --- a/main.c +++ b/main.c @@ -1,13 +1,10 @@ #include #include -#include -#include #include #include "log.h" -#include "main.h" - -#define UNUSED(a) (void)a +#include "shaders.h" +#include "renderer.h" #define WINDOW_WIDTH 800 #define WINDOW_HEIGHT 600 @@ -15,66 +12,9 @@ #define FRAGMENT_SHADER_FILE "main.frag" int main() { - /* Initialize GLFW */ - if (!glfwInit()) { - log_error("[GLFW] Failed to init"); - return EXIT_FAILURE; - } - glfwWindowHint(GLFW_CONTEXT_VERSION_MAJOR, 3); - glfwWindowHint(GLFW_CONTEXT_VERSION_MINOR, 3); - glfwWindowHint(GLFW_OPENGL_PROFILE, GLFW_OPENGL_CORE_PROFILE); + GLFWwindow *window = initialize_window(WINDOW_WIDTH, WINDOW_HEIGHT); - /* Create window */ - GLFWwindow *window = - glfwCreateWindow(WINDOW_WIDTH, WINDOW_HEIGHT, "OpenGL Test", NULL, NULL); - if (window == NULL) { - log_error("[GLFW] Failed to create window"); - glfwTerminate(); - return EXIT_FAILURE; - } - glfwMakeContextCurrent(window); - log_debug("[GLFW] Created window of size %d, %d", WINDOW_WIDTH, - WINDOW_HEIGHT); - - glViewport(0, 0, WINDOW_WIDTH, WINDOW_HEIGHT); - glfwSetFramebufferSizeCallback(window, framebuffer_size_callback); - - /* Initialize OpenGL */ - if (glewInit() != GLEW_OK) { - log_error("[GLEW] Failed to initialize"); - return EXIT_FAILURE; - } - log_debug("[GLEW] Initialized successfully"); - - /* Define vertices */ - float vertices[] = { - -1.0, -1.0, 0.0, // bottom left - -1.0, 1.0, 0.0, // top left - 1.0, -1.0, 0.0, // top right - 1.0, 1.0, 0.0 // bottom right - }; - unsigned int indices[] = { - 0, 1, 2, // first triangle - 1, 2, 3 // second triangle - }; - - unsigned int VBO = 0; - glGenBuffers(1, &VBO); - unsigned int EBO = 0; - glGenBuffers(1, &EBO); - unsigned int VAO = 0; - glGenVertexArrays(1, &VAO); - - glBindVertexArray(VAO); - glBindBuffer(GL_ARRAY_BUFFER, VBO); - glBufferData(GL_ARRAY_BUFFER, sizeof(vertices), vertices, GL_STATIC_DRAW); - glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, EBO); - glBufferData(GL_ELEMENT_ARRAY_BUFFER, sizeof(indices), indices, - GL_STATIC_DRAW); - - glVertexAttribPointer(0, 3, GL_FLOAT, GL_FALSE, 3 * sizeof(float), (void *)0); - glEnableVertexAttribArray(0); - log_debug("Vertex data initialized successfully"); + unsigned int VAO = initialize_vertices(); unsigned int shader_program = glCreateProgram(); int result = compile_shaders(&shader_program, FRAGMENT_SHADER_FILE); @@ -86,7 +26,7 @@ int main() { /* Drawing loop */ size_t frame = 0; while (!glfwWindowShouldClose(window)) { - process_input(window, &shader_program); + process_input(window, &shader_program, FRAGMENT_SHADER_FILE); /* Background */ glClearColor(0, 0, 0, 1.0f); @@ -94,6 +34,7 @@ int main() { glUseProgram(shader_program); + /* Setup uniforms */ float time = glfwGetTime(); int frag_time_location = glGetUniformLocation(shader_program, "u_time"); glUniform1f(frag_time_location, time); @@ -113,6 +54,7 @@ int main() { glGetUniformLocation(shader_program, "u_mouse"); glUniform2f(frag_mouse_resolution_location, mouse_x, mouse_y); + /* Draw the vertices */ // glPolygonMode(GL_FRONT_AND_BACK, GL_LINE); /* For wireframe mode */ glBindVertexArray(VAO); glDrawElements(GL_TRIANGLES, 6, GL_UNSIGNED_INT, 0); @@ -127,151 +69,3 @@ int main() { return EXIT_SUCCESS; } -/** - * @brief Compile shaders from source files. - * - * This function reads the source files of the vertex and fragment - * shaders, compiles them, and links them together in a shader - * program. - * - * @param shader_program Pointer to the ID of the shader program - * where the shaders will be stored. - * @param fragment_shader_file File name of the fragment shader. - * @return 0 on success, 1 on error. - */ -int compile_shaders(unsigned int *shader_program, - const char *const fragment_shader_file) { - /* Compile vertex shader */ - const char *const vertex_shader_source = - "#version 330 core\n" - "layout (location = 0) in vec3 aPos;\n" - "void main()\n" - "{\n" - " gl_Position = vec4(aPos, 1.0);\n" - "}\n"; - - unsigned int vertex_shader = glCreateShader(GL_VERTEX_SHADER); - glShaderSource(vertex_shader, 1, &vertex_shader_source, NULL); - glCompileShader(vertex_shader); - int success = 0; - char info_log[512]; - glGetShaderiv(vertex_shader, GL_COMPILE_STATUS, &success); - if (!success) { - glGetShaderInfoLog(vertex_shader, 512, NULL, info_log); - log_error("Vertex shader compilation failed: %s", info_log); - } - - /* Compile fragment shader */ - const char *const fragment_shader_source = read_file(fragment_shader_file); - if (fragment_shader_source == NULL) { - log_error("Could not load fragment shader from file %s", - fragment_shader_file); - return 1; - } - unsigned int fragment_shader = glCreateShader(GL_FRAGMENT_SHADER); - glShaderSource(fragment_shader, 1, &fragment_shader_source, NULL); - glCompileShader(fragment_shader); - glGetShaderiv(fragment_shader, GL_COMPILE_STATUS, &success); - if (!success) { - glGetShaderInfoLog(fragment_shader, 512, NULL, info_log); - log_error("Fragment shader compilation failed: %s", info_log); - return 1; - } - - /* Link shaders */ - unsigned int new_shader_program = glCreateProgram(); - glAttachShader(new_shader_program, vertex_shader); - glAttachShader(new_shader_program, fragment_shader); - glLinkProgram(new_shader_program); - glGetShaderiv(new_shader_program, GL_LINK_STATUS, &success); - if (!success) { - glGetShaderInfoLog(new_shader_program, 512, NULL, info_log); - log_error("Shader program linking failed: %s", info_log); - return 1; - } - - glDeleteProgram(*shader_program); - *shader_program = new_shader_program; - - free((void *)fragment_shader_source); - glDeleteShader(vertex_shader); - glDeleteShader(fragment_shader); - - log_debug("Shaders compiled successfully"); - - return 0; -} - -/** - * @brief Callback to adjust the size of the viewport when the window - * is resized. - * - * @param window The current window. - * @param width The new width. - * @param height The new height. - */ -void framebuffer_size_callback(GLFWwindow *window, int width, int height) { - UNUSED(window); - glViewport(0, 0, width, height); -} - -/** - * @brief Ensure the window is closed when the user presses the escape - * key. - * - * @param window The current window. - * @param shader_program Pointer to the shader program to update if needed. - */ -void process_input(GLFWwindow *window, unsigned int *shader_program) { - if (glfwGetKey(window, GLFW_KEY_ESCAPE) == GLFW_PRESS) { - log_debug("Quitting"); - glfwSetWindowShouldClose(window, true); - } else if (glfwGetKey(window, GLFW_KEY_R) == GLFW_PRESS) { - compile_shaders(shader_program, FRAGMENT_SHADER_FILE); - } -} - -/** - * @brief Reads a file in a heap-allocated buffer. - * - * This function computes the length of the file, allocate a buffer of - * the correct size, and reads the file in the buffer. Returns `NULL` - * on error. - * - * @param filename The file to read. - * @return A buffer containing the contents of the file, or `NULL` if - * there was an error. - */ -char *read_file(const char *const filename) { - FILE *fd = fopen(filename, "r"); - if (fd == NULL) { - log_error("Could not open file %s", filename); - return NULL; - } - - if (fseek(fd, 0, SEEK_END) == -1) { - perror("fseek"); - return NULL; - } - long size = ftell(fd); - if (size == -1) { - perror("ftell"); - return NULL; - } - rewind(fd); - char *buf = calloc(size + 1, 1); - if (buf == NULL) { - log_error("Failed to allocate memory to read file %s", filename); - return NULL; - } - - long bytes_read = fread(buf, 1, size, fd); - if (bytes_read != size) { - log_error("Failed to read file %s (%ld bytes read out of %ld total)", - filename, bytes_read, size); - return NULL; - } - - fclose(fd); - return buf; -} diff --git a/main.h b/main.h deleted file mode 100644 index 966f0ed..0000000 --- a/main.h +++ /dev/null @@ -1,12 +0,0 @@ -#ifndef MAIN_H -#define MAIN_H - -#include - -char *read_file(const char *const filename); -int compile_shaders(unsigned int *shader_program, - const char *const fragment_shader_file); -void process_input(GLFWwindow *window, unsigned int *shader_program); -void framebuffer_size_callback(GLFWwindow *window, int width, int height); - -#endif /* MAIN_H */ diff --git a/meson.build b/meson.build index c4ea4d4..0670100 100644 --- a/meson.build +++ b/meson.build @@ -14,7 +14,7 @@ glew_dep = dependency('glew') executable( 'shadertool', - sources: ['main.c', 'log.c'], + sources: ['main.c', 'renderer.c', 'shaders.c', 'log.c'], dependencies: [glfw_dep, glew_dep], c_args: '-DLOG_USE_COLOR', ) diff --git a/renderer.c b/renderer.c new file mode 100644 index 0000000..ce1185a --- /dev/null +++ b/renderer.c @@ -0,0 +1,122 @@ +#include +#include + +#include "log.h" +#include "renderer.h" +#include "shaders.h" + +#define UNUSED(a) (void)a + +/** + * @brief Initialize GLFW and OpenGL, and create a window. + * + * @param width The width of the window to create. + * @param height The height of the window to create. + * @return A pointer to the newly created GLFW window. + */ +GLFWwindow *initialize_window(int width, int height) { + /* Initialize GLFW */ + if (!glfwInit()) { + log_error("[GLFW] Failed to init"); + return NULL; + } + glfwWindowHint(GLFW_CONTEXT_VERSION_MAJOR, 3); + glfwWindowHint(GLFW_CONTEXT_VERSION_MINOR, 3); + glfwWindowHint(GLFW_OPENGL_PROFILE, GLFW_OPENGL_CORE_PROFILE); + + /* Create window */ + GLFWwindow *window = + glfwCreateWindow(width, height, "ShaderTool", NULL, NULL); + if (window == NULL) { + log_error("[GLFW] Failed to create window"); + glfwTerminate(); + return NULL; + } + glfwMakeContextCurrent(window); + log_debug("[GLFW] Created window of size %d, %d", width, height); + + glViewport(0, 0, width, height); + glfwSetFramebufferSizeCallback(window, framebuffer_size_callback); + + /* Initialize OpenGL */ + if (glewInit() != GLEW_OK) { + log_error("[GLEW] Failed to initialize"); + return NULL; + } + log_debug("[GLEW] Initialized successfully"); + + return window; +} + +/** + * @brief Initialize the vertex array. + * + * This functions defines a simple rectangle containing the whole + * viewport. + * + * @return The vertex array object ID. + */ +unsigned int initialize_vertices() { + /* Define vertices */ + float vertices[] = { + -1.0, -1.0, 0.0, // bottom left + -1.0, 1.0, 0.0, // top left + 1.0, -1.0, 0.0, // top right + 1.0, 1.0, 0.0 // bottom right + }; + unsigned int indices[] = { + 0, 1, 2, // first triangle + 1, 2, 3 // second triangle + }; + + unsigned int VBO = 0; + glGenBuffers(1, &VBO); + unsigned int EBO = 0; + glGenBuffers(1, &EBO); + unsigned int VAO = 0; + glGenVertexArrays(1, &VAO); + + glBindVertexArray(VAO); + glBindBuffer(GL_ARRAY_BUFFER, VBO); + glBufferData(GL_ARRAY_BUFFER, sizeof(vertices), vertices, GL_STATIC_DRAW); + glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, EBO); + glBufferData(GL_ELEMENT_ARRAY_BUFFER, sizeof(indices), indices, + GL_STATIC_DRAW); + + glVertexAttribPointer(0, 3, GL_FLOAT, GL_FALSE, 3 * sizeof(float), (void *)0); + glEnableVertexAttribArray(0); + log_debug("Vertex data initialized successfully"); + + return VAO; +} + +/** + * @brief Callback to adjust the size of the viewport when the window + * is resized. + * + * @param window The current window. + * @param width The new width. + * @param height The new height. + */ +void framebuffer_size_callback(GLFWwindow *window, int width, int height) { + UNUSED(window); + glViewport(0, 0, width, height); +} + +/** + * @brief Ensure the window is closed when the user presses the escape + * key. + * + * @param window The current window. + * @param shader_program Pointer to the shader program to update if needed. + * @param fragment_shader_file The shader file to reload if needed. + */ +void process_input(GLFWwindow *window, unsigned int *shader_program, + const char *const fragment_shader_file) { + if (glfwGetKey(window, GLFW_KEY_ESCAPE) == GLFW_PRESS) { + log_debug("Quitting"); + glfwSetWindowShouldClose(window, true); + } else if (glfwGetKey(window, GLFW_KEY_R) == GLFW_PRESS) { + compile_shaders(shader_program, fragment_shader_file); + } +} diff --git a/renderer.h b/renderer.h new file mode 100644 index 0000000..fb51a8d --- /dev/null +++ b/renderer.h @@ -0,0 +1,12 @@ +#ifndef RENDERER_H +#define RENDERER_H + +#include + +GLFWwindow *initialize_window(int width, int height); +unsigned int initialize_vertices(); +void process_input(GLFWwindow *window, unsigned int *shader_program, + const char *const fragment_shader_file); +void framebuffer_size_callback(GLFWwindow *window, int width, int height); + +#endif /* RENDERER_H */ diff --git a/shaders.c b/shaders.c new file mode 100644 index 0000000..c90fdb8 --- /dev/null +++ b/shaders.c @@ -0,0 +1,125 @@ +#include +#include +#include + +#include "log.h" + +/** + * @brief Reads a file in a heap-allocated buffer. + * + * This function computes the length of the file, allocate a buffer of + * the correct size, and reads the file in the buffer. Returns `NULL` + * on error. + * + * @param filename The file to read. + * @return A buffer containing the contents of the file, or `NULL` if + * there was an error. + */ +char *read_file(const char *const filename) { + FILE *fd = fopen(filename, "r"); + if (fd == NULL) { + log_error("Could not open file %s", filename); + return NULL; + } + + if (fseek(fd, 0, SEEK_END) == -1) { + perror("fseek"); + return NULL; + } + long size = ftell(fd); + if (size == -1) { + perror("ftell"); + return NULL; + } + rewind(fd); + char *buf = calloc(size + 1, 1); + if (buf == NULL) { + log_error("Failed to allocate memory to read file %s", filename); + return NULL; + } + + long bytes_read = fread(buf, 1, size, fd); + if (bytes_read != size) { + log_error("Failed to read file %s (%ld bytes read out of %ld total)", + filename, bytes_read, size); + return NULL; + } + + fclose(fd); + return buf; +} + +/** + * @brief Compile shaders from source files. + * + * This function reads the source files of the vertex and fragment + * shaders, compiles them, and links them together in a shader + * program. + * + * @param shader_program Pointer to the ID of the shader program + * where the shaders will be stored. + * @param fragment_shader_file File name of the fragment shader. + * @return 0 on success, 1 on error. + */ +int compile_shaders(unsigned int *shader_program, + const char *const fragment_shader_file) { + /* Compile vertex shader */ + const char *const vertex_shader_source = + "#version 330 core\n" + "layout (location = 0) in vec3 aPos;\n" + "void main()\n" + "{\n" + " gl_Position = vec4(aPos, 1.0);\n" + "}\n"; + + unsigned int vertex_shader = glCreateShader(GL_VERTEX_SHADER); + glShaderSource(vertex_shader, 1, &vertex_shader_source, NULL); + glCompileShader(vertex_shader); + int success = 0; + char info_log[512]; + glGetShaderiv(vertex_shader, GL_COMPILE_STATUS, &success); + if (!success) { + glGetShaderInfoLog(vertex_shader, 512, NULL, info_log); + log_error("Vertex shader compilation failed: %s", info_log); + } + + /* Compile fragment shader */ + const char *const fragment_shader_source = read_file(fragment_shader_file); + if (fragment_shader_source == NULL) { + log_error("Could not load fragment shader from file %s", + fragment_shader_file); + return 1; + } + unsigned int fragment_shader = glCreateShader(GL_FRAGMENT_SHADER); + glShaderSource(fragment_shader, 1, &fragment_shader_source, NULL); + glCompileShader(fragment_shader); + glGetShaderiv(fragment_shader, GL_COMPILE_STATUS, &success); + if (!success) { + glGetShaderInfoLog(fragment_shader, 512, NULL, info_log); + log_error("Fragment shader compilation failed: %s", info_log); + return 1; + } + + /* Link shaders */ + unsigned int new_shader_program = glCreateProgram(); + glAttachShader(new_shader_program, vertex_shader); + glAttachShader(new_shader_program, fragment_shader); + glLinkProgram(new_shader_program); + glGetShaderiv(new_shader_program, GL_LINK_STATUS, &success); + if (!success) { + glGetShaderInfoLog(new_shader_program, 512, NULL, info_log); + log_error("Shader program linking failed: %s", info_log); + return 1; + } + + glDeleteProgram(*shader_program); + *shader_program = new_shader_program; + + free((void *)fragment_shader_source); + glDeleteShader(vertex_shader); + glDeleteShader(fragment_shader); + + log_debug("Shaders compiled successfully"); + + return 0; +} diff --git a/shaders.h b/shaders.h new file mode 100644 index 0000000..8692570 --- /dev/null +++ b/shaders.h @@ -0,0 +1,8 @@ +#ifndef SHADERS_H +#define SHADERS_H + +char *read_file(const char *const filename); +int compile_shaders(unsigned int *shader_program, + const char *const fragment_shader_file); + +#endif /* SHADERS_H */