diff options
Diffstat (limited to 'src/shader.c')
| -rw-r--r-- | src/shader.c | 186 |
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); +} |