aboutsummaryrefslogtreecommitdiff
path: root/src/graphics/shader.c
diff options
context:
space:
mode:
Diffstat (limited to 'src/graphics/shader.c')
-rw-r--r--src/graphics/shader.c195
1 files changed, 195 insertions, 0 deletions
diff --git a/src/graphics/shader.c b/src/graphics/shader.c
new file mode 100644
index 0000000..3d8a204
--- /dev/null
+++ b/src/graphics/shader.c
@@ -0,0 +1,195 @@
+#include "../../include/mgl/graphics/shader.h"
+#include "../../include/mgl/graphics/texture.h"
+#include "../../include/mgl/system/fileutils.h"
+#include "../../include/mgl/mgl.h"
+#include <stdlib.h>
+#include <stdio.h>
+
+typedef struct {
+ unsigned int id;
+ mgl_shader_type shader_type;
+} mgl_shader;
+
+static unsigned int mgl_shader_type_to_gl_shader_type(mgl_shader_type shader_type) {
+ switch(shader_type) {
+ case MGL_SHADER_VERTEX: return GL_VERTEX_SHADER;
+ case MGL_SHADER_FRAGMENT: return GL_FRAGMENT_SHADER;
+ case MGL_SHADER_GEOMETRY: return GL_GEOMETRY_SHADER;
+ }
+ return 0;
+}
+
+static void print_compile_log(mgl_context *context, unsigned int shader_id, const char *prefix) {
+ int log_length = 0;
+ context->gl.glGetShaderiv(shader_id, GL_INFO_LOG_LENGTH, &log_length);
+ if(log_length > 0) {
+ char *log_str = malloc(log_length + 1);
+ if(!log_str) {
+ fprintf(stderr, "Error: failed to allocate memory for shader compile log\n");
+ return;
+ }
+
+ log_str[0] = '\0';
+ log_str[log_length] = '\0';
+ context->gl.glGetShaderInfoLog(shader_id, log_length, NULL, log_str);
+
+ fprintf(stderr, "%s: %s\n", prefix, log_str);
+ free(log_str);
+ }
+}
+
+static void mgl_shader_unload(mgl_shader *self);
+
+static int mgl_shader_load_from_memory(mgl_shader *self, unsigned char *shader_data, int shader_size, mgl_shader_type shader_type) {
+ self->id = 0;
+ self->shader_type = shader_type;
+
+ mgl_context *context = mgl_get_context();
+ self->id = context->gl.glCreateShader(mgl_shader_type_to_gl_shader_type(shader_type));
+ if(self->id == 0) {
+ fprintf(stderr, "Error: failed to load shader, error: glCreateShader failed\n");
+ return -1;
+ }
+
+ context->gl.glShaderSource(self->id, 1, (const char* const*)&shader_data, &shader_size);
+ context->gl.glCompileShader(self->id);
+
+ int compiled_successfully = GL_FALSE;
+ context->gl.glGetShaderiv(self->id, GL_COMPILE_STATUS, &compiled_successfully);
+ if(compiled_successfully == GL_FALSE) {
+ print_compile_log(context, self->id, "Error");
+ mgl_shader_unload(self);
+ return -1;
+ }
+
+ print_compile_log(context, self->id, "Warning");
+ return 0;
+}
+
+static int mgl_shader_load_from_file(mgl_shader *self, const char *filepath, mgl_shader_type shader_type) {
+ self->id = 0;
+ self->shader_type = shader_type;
+
+ mgl_filedata filedata;
+ if(mgl_load_file(filepath, &filedata) != 0) {
+ fprintf(stderr, "Error: failed to load shader %s, error: mgl_load_file failed\n", filepath);
+ return -1;
+ }
+
+ if(filedata.size > INT32_MAX) {
+ fprintf(stderr, "Error: failed to load shader %s, error: shader size is too large\n", filepath);
+ return -1;
+ }
+
+ int res = mgl_shader_load_from_memory(self, filedata.data, filedata.size, shader_type);
+ mgl_filedata_free(&filedata);
+ return res;
+}
+
+void mgl_shader_unload(mgl_shader *self) {
+ mgl_context *context = mgl_get_context();
+ if(self->id) {
+ context->gl.glDeleteShader(self->id);
+ self->id = 0;
+ }
+}
+
+int mgl_shader_program_init(mgl_shader_program *self) {
+ self->id = 0;
+
+ mgl_context *context = mgl_get_context();
+ self->id = context->gl.glCreateProgram();
+ if(self->id == 0) {
+ fprintf(stderr, "Error: failed to create shader program: error glCreateProgram failed\n");
+ return -1;
+ }
+
+ return 0;
+}
+
+void mgl_shader_program_deinit(mgl_shader_program *self){
+ mgl_context *context = mgl_get_context();
+ if(self->id) {
+ context->gl.glDeleteProgram(self->id);
+ self->id = 0;
+ }
+}
+
+/* TODO: Check for attach shader error */
+
+int mgl_shader_program_add_shader_from_file(mgl_shader_program *self, const char *filepath, mgl_shader_type shader_type){
+ mgl_shader shader;
+ if(mgl_shader_load_from_file(&shader, filepath, shader_type) != 0)
+ return -1;
+
+ mgl_get_context()->gl.glAttachShader(self->id, shader.id);
+ mgl_shader_unload(&shader); /* TODO: Verify if deleting the shader here is always ok now that we have attached it */
+ return 0;
+}
+
+int mgl_shader_program_add_shader_from_memory(mgl_shader_program *self, unsigned char *shader_data, int shader_size, mgl_shader_type shader_type){
+ mgl_shader shader;
+ if(mgl_shader_load_from_memory(&shader, shader_data, shader_size, shader_type) != 0)
+ return -1;
+
+ mgl_get_context()->gl.glAttachShader(self->id, shader.id);
+ mgl_shader_unload(&shader); /* TODO: Verify if deleting the shader here is always ok now that we have attached it */
+ return 0;
+}
+
+static void print_link_log(mgl_context *context, unsigned int shader_id, const char *prefix) {
+ int log_length = 0;
+ context->gl.glGetProgramiv(shader_id, GL_INFO_LOG_LENGTH, &log_length);
+ if(log_length > 0) {
+ char *log_str = malloc(log_length + 1);
+ if(!log_str) {
+ fprintf(stderr, "Error: failed to allocate memory for shader link log\n");
+ return;
+ }
+
+ log_str[0] = '\0';
+ log_str[log_length] = '\0';
+ context->gl.glGetProgramInfoLog(shader_id, log_length, NULL, log_str);
+
+ fprintf(stderr, "%s: %s\n", prefix, log_str);
+ free(log_str);
+ }
+}
+
+int mgl_shader_program_finalize(mgl_shader_program *self) {
+ mgl_context *context = mgl_get_context();
+
+ context->gl.glLinkProgram(self->id);
+ int is_linked = GL_TRUE;
+ context->gl.glGetProgramiv(self->id, GL_LINK_STATUS, &is_linked);
+ if(is_linked == GL_FALSE) {
+ print_link_log(context, self->id, "Error");
+ return -1;
+ }
+
+ print_link_log(context, self->id, "Warning");
+ return 0;
+}
+
+/* TODO: Optimize glUseProgram */
+/* TODO: Optimize glGetUniformLocation */
+
+int mgl_shader_program_set_uniform_vec2f(mgl_shader_program *self, const char *uniform_name, mgl_vec2f value) {
+ mgl_context *context = mgl_get_context();
+ int uniform_location = context->gl.glGetUniformLocation(self->id, uniform_name);
+ if(uniform_location == -1) {
+ fprintf(stderr, "Error: no uniform by the name %s was found in the shader\n", uniform_name);
+ return -1;
+ }
+
+ context->gl.glUseProgram(self->id);
+ context->gl.glUniform2fv(uniform_location, 1, &value.x);
+ context->gl.glUseProgram(0);
+ return 0;
+}
+
+/* TODO: Optimize glUseProgram */
+void mgl_shader_program_use(mgl_shader_program *shader_program) {
+ mgl_context *context = mgl_get_context();
+ context->gl.glUseProgram(shader_program ? shader_program->id : 0);
+}