#include #include #include #include "voxel.h" #include "utils.h" #include "list.h" #include "gl.h" static void print_shader_log(const char *filename, u32 id) { i32 log_len; char *log; glGetShaderiv(id, GL_INFO_LOG_LENGTH, &log_len); log = xalloc(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 GL_RESULT compile_shader(u32 *out, const char *filename, const char *code, u32 type) { u32 id; i32 status, 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 GL_ERROR; } *out = id; return GL_OK; } static void locate_attributes(Shader *shader) { i32 count, max_len; List names, locations; list_init_string(&names); list_init_i32(&locations); glGetProgramiv(shader->program_id, GL_ACTIVE_ATTRIBUTES, &count); glGetProgramiv(shader->program_id, GL_ACTIVE_ATTRIBUTE_MAX_LENGTH, &max_len); DEBUG("shader %d has %d attributes", shader->program_id, count); for (i32 i = 0; i < count; i++) { i32 size, location; u32 type; char *name; name = xalloc(max_len + 1); glGetActiveAttrib(shader->program_id, i, max_len, NULL, &size, &type, name); location = glGetAttribLocation(shader->program_id, name); DEBUG("shader %d located attribute %s at location %d", shader->program_id, name, location); list_push_string(&names, name); list_push_i32(&locations, location); } shader->attribute_names = names; shader->attribute_locations = locations; } static void locate_uniforms(Shader *shader) { i32 count, max_len; List names, locations; list_init_string(&names); list_init_i32(&locations); glGetProgramiv(shader->program_id, GL_ACTIVE_UNIFORMS, &count); glGetProgramiv(shader->program_id, GL_ACTIVE_UNIFORM_MAX_LENGTH, &max_len); DEBUG("shader %d has %d uniforms", shader->program_id, count); for (i32 i = 0; i < count; i++) { i32 size, location; u32 type; char *name; name = xalloc(max_len + 1); glGetActiveUniform(shader->program_id, i, max_len, NULL, &size, &type, name); location = glGetUniformLocation(shader->program_id, name); DEBUG("shader %d located uniform %s at location %d", shader->program_id, name, location); list_push_string(&names, name); list_push_i32(&locations, location); } shader->uniform_names = names; shader->uniform_locations = locations; } static void locate_uniform_blocks(Shader *shader) { i32 count; List names, indicies; list_init_string(&names); list_init_i32(&indicies); glGetProgramiv(shader->program_id, GL_ACTIVE_UNIFORM_BLOCKS, &count); DEBUG("shader %d has %d uniform blocks", shader->program_id, count); for (i32 i = 0; i < count; i++) { i32 index, name_len; char *name; glGetActiveUniformBlockiv(shader->program_id, i, GL_UNIFORM_BLOCK_NAME_LENGTH, &name_len); name = xalloc(name_len + 1); glGetActiveUniformBlockName(shader->program_id, i, name_len, NULL, name); index = glGetUniformBlockIndex(shader->program_id, name); DEBUG("shader %d located uniform block %s at index %d", shader->program_id, name, index); list_push_string(&names, name); list_push_i32(&indicies, index); } shader->uniform_block_names = names; shader->uniform_block_indicies = indicies; } GL_RESULT shader_init(Shader *shader, const char *vertex_file, const char *fragment_file) { char *vertex, *fragment; // 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); glLinkProgram(shader->program_id); glValidateProgram(shader->program_id); locate_attributes(shader); locate_uniforms(shader); locate_uniform_blocks(shader); if (vertex) free(vertex); if (fragment) free(fragment); return GL_OK; failure: if (vertex) free(vertex); if (fragment) free(fragment); free(shader); return GL_ERROR; } 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); for (u32 i = 0; i < shader->attribute_names.len; i++) free(shader->attribute_names.strings[i]); list_free(&shader->attribute_names); list_free(&shader->attribute_locations); for (u32 i = 0; i < shader->uniform_names.len; i++) free(shader->uniform_names.strings[i]); list_free(&shader->uniform_names); list_free(&shader->uniform_locations); for (u32 i = 0; i < shader->uniform_block_names.len; i++) free(shader->uniform_block_names.strings[i]); list_free(&shader->uniform_block_names); list_free(&shader->uniform_block_indicies); } int shader_attribute_location(Shader *shader, const char *name) { for (u32 i = 0; i < shader->attribute_names.len; i++) { const char *attribute = shader->attribute_names.strings[i]; if (strcmp(attribute, name) == 0) return shader->attribute_locations.ints[i]; } WARN("unknown attribute '%s'", name); return -1; } int shader_uniform_location(Shader *shader, const char *name) { for (u32 i = 0; i < shader->uniform_names.len; i++) { const char *uniform = shader->uniform_names.strings[i]; if (strcmp(uniform, name) == 0) return shader->uniform_locations.ints[i]; } WARN("unknown uniform '%s'", name); return -1; } int shader_uniform_block_index(Shader *shader, const char *name) { for (u32 i = 0; i < shader->uniform_block_names.len; i++) { const char *uniform_block = shader->uniform_block_names.strings[i]; if (strcmp(uniform_block, name) == 0) return shader->uniform_block_indicies.ints[i]; } WARN("unknown uniform block '%s'", name); return -1; } void shader_load_float(Shader *shader, const char *name, float value) { int location = shader_uniform_location(shader, name); glUniform1f(location, value); } void shader_load_int(Shader *shader, const char *name, int value) { int location = shader_uniform_location(shader, name); glUniform1i(location, value); } void shader_load_vec3(Shader *shader, const char *name, vec3 value) { int location = shader_uniform_location(shader, name); glUniform3f(location, value[0], value[1], value[2]); } void shader_load_ivec3(Shader *shader, const char *name, ivec3 value) { int location = shader_uniform_location(shader, name); glUniform3i(location, value[0], value[1], value[2]); } void shader_load_mat4(Shader *shader, const char *name, mat4 value) { int location = shader_uniform_location(shader, name); glUniformMatrix4fv(location, 1, GL_FALSE, (float *)value); } void shader_load_ubo(Shader *shader, const char *name, u32 binding) { int index = shader_uniform_block_index(shader, name); glUniformBlockBinding(shader->program_id, index, binding); }