|
@@ -0,0 +1,235 @@
|
|
|
+#include <stdio.h>
|
|
|
+#include <stdlib.h>
|
|
|
+#include <stdarg.h>
|
|
|
+#include <stdbool.h>
|
|
|
+#include <string.h>
|
|
|
+#include <errno.h>
|
|
|
+
|
|
|
+#define GL_GLEXT_PROTOTYPES
|
|
|
+#include <GLFW/glfw3.h>
|
|
|
+
|
|
|
+#define SCREEN_WIDTH 1024
|
|
|
+#define SCREEN_HEIGHT 768
|
|
|
+
|
|
|
+void panic_errno(const char *fmt, ...)
|
|
|
+{
|
|
|
+ fprintf(stderr, "ERROR: ");
|
|
|
+
|
|
|
+ va_list args;
|
|
|
+ va_start(args, fmt);
|
|
|
+ vfprintf(stderr, fmt, args);
|
|
|
+ va_end(args);
|
|
|
+
|
|
|
+ fprintf(stderr, ": %s\n", strerror(errno));
|
|
|
+
|
|
|
+ exit(1);
|
|
|
+}
|
|
|
+
|
|
|
+char *slurp_file(const char *file_path)
|
|
|
+{
|
|
|
+#define SLURP_FILE_PANIC panic_errno("Could not read file `%s`", file_path)
|
|
|
+ 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
|
|
|
+}
|
|
|
+
|
|
|
+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, "%.*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 err = compile_shader_source(source, shader_type, shader);
|
|
|
+ free(source);
|
|
|
+ return err;
|
|
|
+}
|
|
|
+
|
|
|
+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;
|
|
|
+}
|
|
|
+
|
|
|
+GLuint program = 0;
|
|
|
+GLuint failed_program = 0;
|
|
|
+
|
|
|
+const char *failed_vert_source =
|
|
|
+ "#version 130\n"
|
|
|
+ "\n"
|
|
|
+ "void main(void)\n"
|
|
|
+ "{\n"
|
|
|
+ " int gray = gl_VertexID ^ (gl_VertexID >> 1);\n"
|
|
|
+ "\n"
|
|
|
+ " gl_Position = vec4(\n"
|
|
|
+ " 2 * (gray / 2) - 1,\n"
|
|
|
+ " 2 * (gray % 2) - 1,\n"
|
|
|
+ " 0.0,\n"
|
|
|
+ " 1.0);\n"
|
|
|
+ "};\n";
|
|
|
+
|
|
|
+const char *failed_frag_source =
|
|
|
+ "#version 130\n"
|
|
|
+ "\n"
|
|
|
+ "out vec4 color;\n"
|
|
|
+ "\n"
|
|
|
+ "void main(void) {\n"
|
|
|
+ " color = vec4(1.0, 0.0, 0.0, 1.0);\n"
|
|
|
+ "};\n";
|
|
|
+
|
|
|
+
|
|
|
+void init_failed_program(void)
|
|
|
+{
|
|
|
+ GLuint vert = 0;
|
|
|
+ if (!compile_shader_source(failed_vert_source, GL_VERTEX_SHADER, &vert)) {
|
|
|
+ exit(1);
|
|
|
+ }
|
|
|
+
|
|
|
+ GLuint frag = 0;
|
|
|
+ if (!compile_shader_source(failed_frag_source, GL_FRAGMENT_SHADER, &frag)) {
|
|
|
+ exit(1);
|
|
|
+ }
|
|
|
+
|
|
|
+ if (!link_program(vert, frag, &failed_program)) {
|
|
|
+ exit(1);
|
|
|
+ }
|
|
|
+}
|
|
|
+
|
|
|
+void reload_shaders(void)
|
|
|
+{
|
|
|
+ glDeleteProgram(program);
|
|
|
+
|
|
|
+ GLuint vert = 0;
|
|
|
+ if (!compile_shader_file("./main.vert", GL_VERTEX_SHADER, &vert)) {
|
|
|
+ glUseProgram(failed_program);
|
|
|
+ return;
|
|
|
+ }
|
|
|
+
|
|
|
+ GLuint frag = 0;
|
|
|
+ if (!compile_shader_file("./main.frag", GL_FRAGMENT_SHADER, &frag)) {
|
|
|
+ glUseProgram(failed_program);
|
|
|
+ return;
|
|
|
+ }
|
|
|
+
|
|
|
+ if (!link_program(vert, frag, &program)) {
|
|
|
+ glUseProgram(failed_program);
|
|
|
+ return;
|
|
|
+ }
|
|
|
+
|
|
|
+ glUseProgram(program);
|
|
|
+
|
|
|
+ printf("Successfully Reload the Shaders\n");
|
|
|
+}
|
|
|
+
|
|
|
+void key_callback(GLFWwindow* window, int key, int scancode, int action, int mods)
|
|
|
+{
|
|
|
+ (void) window;
|
|
|
+ (void) scancode;
|
|
|
+ (void) action;
|
|
|
+ (void) mods;
|
|
|
+
|
|
|
+ if (key == GLFW_KEY_F5 && action == GLFW_PRESS) {
|
|
|
+ reload_shaders();
|
|
|
+ }
|
|
|
+}
|
|
|
+
|
|
|
+void window_size_callback(GLFWwindow* window, int width, int height)
|
|
|
+{
|
|
|
+ (void) window;
|
|
|
+ glViewport(
|
|
|
+ width / 2 - SCREEN_WIDTH / 2,
|
|
|
+ height / 2 - SCREEN_HEIGHT / 2,
|
|
|
+ SCREEN_WIDTH,
|
|
|
+ SCREEN_HEIGHT);
|
|
|
+}
|
|
|
+
|
|
|
+int main()
|
|
|
+{
|
|
|
+ if (!glfwInit()) {
|
|
|
+ fprintf(stderr, "ERROR: could not initialize GLFW\n");
|
|
|
+ exit(1);
|
|
|
+ }
|
|
|
+
|
|
|
+ GLFWwindow * const window = glfwCreateWindow(
|
|
|
+ SCREEN_WIDTH,
|
|
|
+ SCREEN_HEIGHT,
|
|
|
+ "atomato",
|
|
|
+ NULL,
|
|
|
+ NULL);
|
|
|
+ if (window == NULL) {
|
|
|
+ fprintf(stderr, "ERROR: could not create a window.\n");
|
|
|
+ glfwTerminate();
|
|
|
+ exit(1);
|
|
|
+ }
|
|
|
+
|
|
|
+ glfwMakeContextCurrent(window);
|
|
|
+
|
|
|
+ init_failed_program();
|
|
|
+ reload_shaders();
|
|
|
+
|
|
|
+ glfwSetKeyCallback(window, key_callback);
|
|
|
+ glfwSetFramebufferSizeCallback(window, window_size_callback);
|
|
|
+
|
|
|
+ while (!glfwWindowShouldClose(window)) {
|
|
|
+ glClear(GL_COLOR_BUFFER_BIT);
|
|
|
+
|
|
|
+ glDrawArrays(GL_QUADS, 0, 4);
|
|
|
+
|
|
|
+ glfwSwapBuffers(window);
|
|
|
+ glfwPollEvents();
|
|
|
+ }
|
|
|
+
|
|
|
+ return 0;
|
|
|
+}
|