#include #include #include #include #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); }