#include "shader.h" #include "util/Log.h" #include #include #include Shader::Shader(std::string_view name, std::filesystem::path const &directory) : program(glCreateProgram()) { std::filesystem::path vertex_shader_path = directory / name; vertex_shader_path.concat(".vert"); std::filesystem::path frag_shader_path = directory / name; frag_shader_path.concat(".frag"); std::string vertex_shader_source = parse(vertex_shader_path); std::string fragment_shader_source = parse(frag_shader_path); GLuint vertex_shader = compile(vertex_shader_source, GL_VERTEX_SHADER); GLuint fragment_shader = compile(fragment_shader_source, GL_FRAGMENT_SHADER); glAttachShader(program, vertex_shader); glAttachShader(program, fragment_shader); glLinkProgram(program); GLint linked = 0; glGetProgramiv(program, GL_LINK_STATUS, &linked); if (linked == 0) { Log::logger().warn(R"(Failed to link Shader "{}")", name); } #ifdef NDEBUG glDetachShader(program, vertex_shader); glDetachShader(program, fragment_shader); glDeleteShader(vertex_shader); glDeleteShader(fragment_shader); #endif Log::logger().trace(R"(Loaded Shader "{}")", name); } Shader::~Shader() { glDeleteProgram(program); } void Shader::bind() const { glUseProgram(program); } void Shader::unbind() { glUseProgram(0); } auto Shader::parse(const std::filesystem::path &path) -> std::string { std::fstream file; file.open(path, std::ios::in); if (!file.is_open()) { Log::logger().critical(R"(Shader "{}" not found!)", path.string()); std::terminate(); } return {(std::istreambuf_iterator(file)), (std::istreambuf_iterator())}; } auto Shader::compile(std::string_view source, GLenum type) -> GLuint { GLuint program = glCreateShader(type); auto const *src = source.data(); glShaderSource(program, 1, &src, nullptr); glCompileShader(program); int result{}; glGetShaderiv(program, GL_COMPILE_STATUS, &result); if (result != GL_TRUE) { int length{}; glGetShaderiv(program, GL_INFO_LOG_LENGTH, &length); std::string message; message.reserve(static_cast(length)); glGetShaderInfoLog(program, length, &length, message.data()); Log::logger().error("Shader compilation failed: {}", message); return 0; } return program; } auto Shader::retrieveUniformLocation(std::string_view uniform_name) const -> GLint { if (uniform_location_cache.find(uniform_name.data()) != uniform_location_cache.end()) { return uniform_location_cache[uniform_name.data()]; } GLint location = glGetUniformLocation(program, uniform_name.data()); if (location != -1) { uniform_location_cache[uniform_name.data()] = location; } else { Log::logger().warn(R"(Uniform "{}" not found.)", uniform_name); } return location; } template void Shader::set_uniform(std::string_view name, T value) const { GLint location = retrieveUniformLocation(name); if constexpr (std::is_same_v || std::is_same_v) { glUniform1i(location, (int)value); } else if constexpr (std::is_same_v) { glUniform1ui(location, value); } else if constexpr (std::is_same_v) { glUniform1f(location, value); } else if constexpr (std::is_same_v) { glUniform2f(location, value.x, value.y); //NOLINT(cppcoreguidelines-pro-type-union-access) } else if constexpr (std::is_same_v) { glUniform3f(location, value.x, value.y, value.z); //NOLINT(cppcoreguidelines-pro-type-union-access) } else if constexpr (std::is_same_v) { glUniformMatrix3fv(location, 1, GL_FALSE, glm::value_ptr(value)); } else if constexpr (std::is_same_v) { glUniformMatrix4fv(location, 1, GL_FALSE, glm::value_ptr(value)); } } template void Shader::set_uniform(std::string_view, bool) const; template void Shader::set_uniform(std::string_view, int) const; template void Shader::set_uniform(std::string_view, unsigned) const; template void Shader::set_uniform(std::string_view, float) const; template void Shader::set_uniform(std::string_view, glm::vec2) const; template void Shader::set_uniform(std::string_view, glm::vec3) const; template void Shader::set_uniform(std::string_view, glm::mat3) const; template void Shader::set_uniform(std::string_view, glm::mat4) const;