summaryrefslogtreecommitdiff
path: root/src/shader.c
diff options
context:
space:
mode:
Diffstat (limited to 'src/shader.c')
-rw-r--r--src/shader.c186
1 files changed, 186 insertions, 0 deletions
diff --git a/src/shader.c b/src/shader.c
new file mode 100644
index 0000000..eefcfc2
--- /dev/null
+++ b/src/shader.c
@@ -0,0 +1,186 @@
+#include <GL/glew.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+
+#include "voxel.h"
+#include "shader.h"
+
+static char *read_file(const char *filename)
+{
+ FILE *file;
+ long length, read;
+ char *buffer;
+
+ file = fopen(filename, "r");
+ if (file == NULL) {
+ ERROR("could not read file: %s", filename);
+ return NULL;
+ }
+
+ fseek(file, 0, SEEK_END);
+ length = ftell(file);
+ fseek(file, 0, SEEK_SET);
+
+ buffer = malloc(length + 1);
+ read = fread(buffer, 1, length, file);
+ buffer[length] = 0;
+
+ if (read < length) {
+ ERROR("could not read file: %s", filename);
+ free(buffer);
+ return NULL;
+ }
+
+ fclose(file);
+ return buffer;
+}
+
+static void print_shader_log(const char *filename, GLuint id)
+{
+ GLint log_len;
+ char *log;
+ glGetShaderiv(id, GL_INFO_LOG_LENGTH, &log_len);
+ log = malloc(log_len + 1);
+ glGetShaderInfoLog(id, log_len, &log_len, log);
+ log[log_len] = 0;
+ ERROR("failed to compile shader: '%s'\n%s", filename, log);
+ free(log);
+}
+
+static int compile_shader(GLuint *out, const char *filename, const char *code, GLenum type)
+{
+ GLuint id;
+ GLint status;
+ int code_len;
+
+ id = glCreateShader(type);
+ code_len = strlen(code);
+ glShaderSource(id, 1, &code, &code_len);
+ glCompileShader(id);
+ glGetShaderiv(id, GL_COMPILE_STATUS, &status);
+ if (status == GL_FALSE) {
+ print_shader_log(filename, id);
+ glDeleteShader(id);
+ return 1;
+ }
+
+ *out = id;
+ return 0;
+}
+
+static void parse_bind_attributes(Shader *shader, char *code)
+{
+ char *line, *last_line;
+ char *token, *last_token;
+ int attribute = 0;
+
+ line = strtok_r(code, "\n", &last_line);
+ for (; line != NULL; line = strtok_r(NULL, "\n", &last_line)) {
+ token = strtok_r(line, " \t", &last_token);
+ if (strcmp(token, "in") != 0)
+ continue;
+
+ token = strtok_r(NULL, " \t", &last_token);
+ token = strtok_r(NULL, " \t", &last_token);
+ *strchr(token, ';') = 0;
+
+ glBindAttribLocation(shader->program_id, attribute, token);
+ attribute++;
+ }
+}
+
+Shader *shader_init(const char *vertex_file, const char *fragment_file)
+{
+ Shader *shader;
+ char *vertex, *fragment;
+
+ shader = malloc(sizeof(Shader));
+ memset(shader, 0, sizeof(Shader));
+
+ // read shader code from file
+ vertex = read_file(vertex_file);
+ fragment = read_file(fragment_file);
+ if (!vertex || !fragment)
+ goto failure;
+
+ // compile shaders
+ if (compile_shader(&shader->vertex_id, vertex_file, vertex, GL_VERTEX_SHADER))
+ goto failure;
+ if (compile_shader(&shader->fragment_id, fragment_file, fragment,
+ GL_FRAGMENT_SHADER))
+ goto failure;
+
+ shader->program_id = glCreateProgram();
+ glAttachShader(shader->program_id, shader->vertex_id);
+ glAttachShader(shader->program_id, shader->fragment_id);
+ parse_bind_attributes(shader, vertex);
+ glLinkProgram(shader->program_id);
+ glValidateProgram(shader->program_id);
+
+ if (vertex)
+ free(vertex);
+ if (fragment)
+ free(fragment);
+
+ return shader;
+
+failure:
+ if (vertex)
+ free(vertex);
+ if (fragment)
+ free(fragment);
+ free(shader);
+ return NULL;
+}
+
+void shader_bind(Shader *shader)
+{
+ glEnable(GL_DEPTH_TEST);
+ glEnable(GL_CULL_FACE);
+ glCullFace(GL_BACK);
+ glFrontFace(GL_CW);
+ glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
+ glUseProgram(shader->program_id);
+}
+
+void shader_unbind(void)
+{
+ glDisable(GL_CULL_FACE);
+ glUseProgram(0);
+}
+
+void shader_free(Shader *shader)
+{
+ glDetachShader(shader->program_id, shader->vertex_id);
+ glDetachShader(shader->program_id, shader->fragment_id);
+ glDeleteShader(shader->vertex_id);
+ glDeleteShader(shader->fragment_id);
+ glDeleteProgram(shader->program_id);
+ free(shader);
+}
+
+GLint shader_uniform_location(Shader *shader, const char *name)
+{
+ return glGetUniformLocation(shader->program_id, name);
+}
+
+void shader_loadf(GLint location, float value)
+{
+ glUniform1f(location, value);
+}
+
+void shader_loadi(GLint location, int value)
+{
+ glUniform1i(location, value);
+}
+
+void shader_loadv3f(GLint location, vec3 value)
+{
+ glUniform3f(location, value[0], value[1], value[2]);
+}
+
+void shader_loadm4f(GLint location, mat4 value)
+{
+ glUniformMatrix4fv(location, 1, GL_FALSE, (float *)value);
+}