/****************************************************************************** * File - shader.cpp * Author - Joey Pollack * Date - 2022/08/04 (y/m/d) * Mod Date - 2022/08/04 (y/m/d) * Description - opengl shader system ******************************************************************************/ #include "shader.h" #include #include #include namespace lunarium { Shader::Shader(const char* vert_source, const char* geo_source, const char* frag_source) : mGLID(0) { // Validate parameters if (!vert_source) { Logger::Error(LogCategory::GRAPHICS, "Failed to create Shader - vert_source can not be nullptr!"); return; } if (!frag_source) { Logger::Error(LogCategory::GRAPHICS, "Failed to create Shader - vert_source can not be nullptr!"); return; } // Compile source code u32 vert_id = CompileSource(vert_source, GL_VERTEX_SHADER); u32 geo_id = (geo_source ? CompileSource(geo_source, GL_GEOMETRY_SHADER) : 0 ); u32 frag_id = CompileSource(frag_source, GL_FRAGMENT_SHADER); // Validate compilation if (vert_id < 1 || frag_id < 1) { // Free any succesfully compiled shader sources if (vert_id > 0) glDeleteShader(vert_id); if (geo_id > 0) glDeleteShader(geo_id); if (frag_id > 0) glDeleteShader(frag_id); Logger::Error(LogCategory::GRAPHICS, "Failed to create shader program"); return; } // Link shader program mGLID = glCreateProgram(); glAttachShader(mGLID, vert_id); if (geo_id > 0) glAttachShader(mGLID, geo_id); glAttachShader(mGLID, frag_id); glLinkProgram(mGLID); // check for linking errors int success; const int buffer_size = 512; char info_log[buffer_size]; glGetProgramiv(mGLID, GL_LINK_STATUS, &success); if (!success) { glGetProgramInfoLog(mGLID, 512, NULL, info_log); Logger::Error(LogCategory::GRAPHICS, "Failed to link shader program: %s", info_log); glDeleteProgram(mGLID); mGLID = 0; } // Clean up if (vert_id > 0) glDeleteShader(vert_id); if (geo_id > 0) glDeleteShader(geo_id); if (frag_id > 0) glDeleteShader(frag_id); } Shader::~Shader() { if (IsValid()) { glDeleteProgram(mGLID); mGLID = 0; } } bool Shader::IsValid() const { return mGLID > 0; } void Shader::Use() const { glUseProgram(mGLID); } OpRes Shader::GetAllUniforms(std::vector& uniforms) { uniforms.clear(); GLint size; // size of the variable GLenum type; // type of the variable (float, vec3 or mat4, etc) const GLsizei buffer_size = 16; // maximum name length GLchar name[buffer_size]; // variable name in GLSL GLsizei length; // name length int count = -1; glGetProgramiv(mGLID, GL_ACTIVE_UNIFORMS, &count); Logger::Debug(LogCategory::GRAPHICS, "Active Uniforms for shader %n: %n", mGLID, count); for (int i = 0; i < count; i++) { glGetActiveUniform(mGLID, (GLuint)i, buffer_size, &length, &size, &type, name); u32 location = glGetUniformLocation(mGLID, name); Logger::Debug(LogCategory::GRAPHICS, "Uniform #%n Type: %u Name: %s Location: %n", i, type, name, location); Uniform u; u.Location = location; u.Type = GLToUniformType(type); u.Name = std::string(name); uniforms.push_back(u); } return OpRes::OK(); } void Shader::GetUniformLocation(Uniform& uniform) { uniform.Location = glGetUniformLocation(mGLID, uniform.Name.c_str()); } OpRes Shader::SetUniform(Uniform& uniform, void* values) { if (uniform.Location == -1) { GetUniformLocation(uniform); if (uniform.Location == -1) { return OpRes::Fail("Could not find location for uniform: %s", uniform.Name.c_str()); } } switch (uniform.Type) { case UniformType::F1: glUniform1fv(uniform.Location, 1, (GLfloat*)values); break; case UniformType::F2: glUniform2fv(uniform.Location, 1, (GLfloat*)values); break; case UniformType::F3: glUniform3fv(uniform.Location, 1, (GLfloat*)values); break; case UniformType::F4: glUniform4fv(uniform.Location, 1, (GLfloat*)values); break; case UniformType::I1: glUniform1iv(uniform.Location, 1, (GLint*)values); break; case UniformType::I2: glUniform2iv(uniform.Location, 1, (GLint*)values); break; case UniformType::I3: glUniform3iv(uniform.Location, 1, (GLint*)values); break; case UniformType::I4: glUniform4iv(uniform.Location, 1, (GLint*)values); break; case UniformType::FMAT3: glUniformMatrix3fv(uniform.Location, 1, GL_FALSE, (GLfloat*)values); break; case UniformType::FMAT4: glUniformMatrix4fv(uniform.Location, 1, GL_FALSE, (GLfloat*)values); break; default: return OpRes::Fail("Can not set uniform value - Unknown type"); } int error = glGetError(); if (error > 0) { Logger::Error(LogCategory::GRAPHICS, "Error setting uniform - Name: %s error code: %d", uniform.Name.c_str(), error); } return OpRes::OK(); } u32 Shader::CompileSource(const char* source, u32 source_type) { u32 id = glCreateShader(source_type); glShaderSource(id, 1, &source, NULL); glCompileShader(id); int success; const int buffer_size = 512; char info_log[buffer_size]; glGetShaderiv(id, GL_COMPILE_STATUS, &success); if (!success) { glGetShaderInfoLog(id, buffer_size, NULL, info_log); std::string type = ""; switch (source_type) { case GL_VERTEX_SHADER: type = "Vertex"; break; case GL_GEOMETRY_SHADER: type = "Geometry"; break; case GL_FRAGMENT_SHADER: type = "Fragment"; break; } Logger::Error(LogCategory::GRAPHICS, "Failed to compile %s shader source: %s", type.c_str(), info_log); return 0; } return id; } UniformType Shader::GLToUniformType(u32 type) { switch (type) { case GL_FLOAT: return UniformType::F1; case GL_FLOAT_VEC2: return UniformType::F2; case GL_FLOAT_VEC3: return UniformType::F3; case GL_FLOAT_VEC4: return UniformType::F4; case GL_INT: return UniformType::I1; case GL_INT_VEC2: return UniformType::I2; case GL_INT_VEC3: return UniformType::I3; case GL_INT_VEC4: return UniformType::I4; case GL_FLOAT_MAT3: return UniformType::FMAT3; case GL_FLOAT_MAT4: return UniformType::FMAT4; } Logger::Warn(LogCategory::GRAPHICS, "Can not convert GL uniform type - Unknown type: %u", type); return UniformType::UNKNOWN; } }