#include "shader.h" #include "../debug/logger.h" #define STB_INCLUDE_IMPLEMENTATION #define STB_INCLUDE_LINE_GLSL #include "stb_include.h" // Internal functions void r_shader_set_uniform_locations(r_shader *shader); bool r_shader_from_files(r_shader *shader, const char *vertex, const char *fragment, const char *geometry, const char *tesseletion_control, const char *tesseletion_evaluation, const char *compute) { const int count = 6; char *source[count] = {NULL, NULL, NULL, NULL, NULL, NULL}; const char *filename[count] = {vertex, fragment, geometry, tesseletion_control, tesseletion_evaluation, compute}; const char *type[count] = {"vertex", "fragment", "geometry", "tesseletion_control", "tesseletion_evaluation", "compute"}; // Load sources bool has_error = false; char error[256]; for(int i = 0; i < count; i++) { if(filename[i]) { char path[4096]; u32 len = strlen(filename[i]); memcpy(path, filename[i], len+1); int last_slash = len; while(last_slash > 0) { if(filename[i][last_slash] == '/') break; last_slash--; } path[last_slash] = '\0'; source[i] = stb_include_file((char*)filename[i], NULL, path, error); if(!source[i]) { has_error = true; LOG(LOG_ERROR, "Error loading %s shader: %s", type[i], error); break; } } } // Compile shader if(!has_error) { has_error = !r_shader_from_text(shader, source[0], source[1], source[2], source[3], source[5], source[5]); } // Cleanup for(int i = 0; i < count; i++) { if(source[i]) free(source[i]); } return !has_error; } bool r_shader_from_text(r_shader *shader, const char *vertex, const char *fragment, const char *geometry, const char *tesseletion_control, const char *tesseletion_evaluation, const char *compute) { int count = 6; GLuint type[count] = {GL_VERTEX_SHADER, GL_FRAGMENT_SHADER, GL_GEOMETRY_SHADER, GL_TESS_CONTROL_SHADER, GL_TESS_EVALUATION_SHADER, GL_COMPUTE_SHADER}; const char *source[count] = {vertex, fragment, geometry, tesseletion_control, tesseletion_evaluation, compute}; // Create shader program shader->id = glCreateProgram(); bool has_error = false; for(int i = 0; i < count; i++) { if(!source[i]) continue; // Compile current shader source GLuint source_id = glCreateShader(type[i]); GLint size = strlen(source[i]); glShaderSource(source_id, 1, &source[i], &size); glCompileShader(source_id); // Check for success/errors char build_info[2048]; GLint build_success; glGetShaderiv(source_id, GL_COMPILE_STATUS, &build_success); if (!build_success) { glGetShaderInfoLog(source_id, 2048, NULL, build_info); LOG(LOG_ERROR, "Cannot compile shader %.*s: %s", (int)size, source, build_info); glDeleteShader(source_id); has_error = true; break; } // Attach compiler shader to program glAttachShader(shader->id, source_id); } // @Correctness: Clean up shader sources after fail if(has_error) return false; // Link program glLinkProgram(shader->id); // Check for success/error GLint build_success; char build_info[512]; glGetProgramiv(shader->id, GL_LINK_STATUS, &build_success); if(!build_success) { glGetProgramInfoLog(shader->id, 512, NULL, build_info); LOG(LOG_ERROR, "Cannot link shader program: %s", build_info); glDeleteShader(shader->id); has_error = true; } // Load uniform locations if(!has_error) r_shader_set_uniform_locations(shader); return !has_error; } void r_shader_delete(r_shader *shader) { glDeleteProgram(shader->id); } // Internal functions void r_shader_set_uniform_locations(r_shader *shader) { shader->time = glGetUniformLocation(shader->id, "time"); shader->width = glGetUniformLocation(shader->id, "width"); shader->height = glGetUniformLocation(shader->id, "height"); shader->view_matrix = glGetUniformLocation(shader->id, "view_matrix"); shader->view_matrix_inverse = glGetUniformLocation(shader->id, "view_matrix_inverse"); shader->model_matrix = glGetUniformLocation(shader->id, "model_matrix"); shader->camera_position = glGetUniformLocation(shader->id, "camera_position"); for(int i = 0; i < R_SHADER_COLOR_MAX; i++) { char uniform_name[32]; sprintf(uniform_name, "color%d", i); shader->color[i] = glGetUniformLocation(shader->id, uniform_name); } for(int i = 0; i < R_SHADER_TEXTURES_MAX; i++) { char uniform_name[32]; sprintf(uniform_name, "has_texture%d", i); shader->has_texture [i] = glGetUniformLocation(shader->id, uniform_name); sprintf(uniform_name, "texture%d", i); shader->texture [i] = glGetUniformLocation(shader->id, uniform_name); sprintf(uniform_name, "texture_channels%d", i); shader->texture_channels[i] = glGetUniformLocation(shader->id, uniform_name); } shader->lights = glGetUniformBlockIndex(shader->id, "lights"); if(shader->lights != GL_INVALID_INDEX) glUniformBlockBinding(shader->id, shader->lights, 0); shader->has_shadow_map = glGetUniformLocation(shader->id, "has_shadow_map"); shader->shadow_map = glGetUniformLocation(shader->id, "shadow_map"); shader->shadow_matrix = glGetUniformLocation(shader->id, "shadow_matrix"); shader->has_environment_map = glGetUniformLocation(shader->id, "has_environment_map"); shader->environment_map = glGetUniformLocation(shader->id, "environment_map"); shader->has_albedo_texture = glGetUniformLocation(shader->id, "has_albedo_texture"); shader->albedo_texture = glGetUniformLocation(shader->id, "albedo_texture"); shader->albedo_factor = glGetUniformLocation(shader->id, "albedo_factor"); shader->has_metallic_texture = glGetUniformLocation(shader->id, "has_metallic_texture"); shader->metallic_texture = glGetUniformLocation(shader->id, "metallic_texture"); shader->metallic_factor = glGetUniformLocation(shader->id, "metallic_factor"); shader->has_roughness_texture = glGetUniformLocation(shader->id, "has_roughness_texture"); shader->roughness_texture = glGetUniformLocation(shader->id, "roughness_texture"); shader->roughness_factor = glGetUniformLocation(shader->id, "roughness_factor"); shader->has_normal_texture = glGetUniformLocation(shader->id, "has_normal_texture"); shader->normal_texture = glGetUniformLocation(shader->id, "normal_texture"); shader->has_emissive_texture = glGetUniformLocation(shader->id, "has_emissive_texture"); shader->emissive_texture = glGetUniformLocation(shader->id, "emissive_texture"); shader->emissive_factor = glGetUniformLocation(shader->id, "emissive_factor"); }