#include "OpenGLShader.h" #include #include #include #include namespace Donut { static uint32_t ShaderTypeFromString(const std::string& type) { if (type == "vertex") return GL_VERTEX_SHADER; if (type == "fragment" || type == "pixel") return GL_FRAGMENT_SHADER; return 0; } OpenGLShader::OpenGLShader(const std::string& filepath) { std::string source = ReadFile(filepath); auto shaderSources = PreProcess(source); Compile(shaderSources); auto lastSlash = filepath.find_last_of("/\\"); lastSlash = lastSlash == std::string::npos ? 0 : lastSlash + 1; auto lastDot = filepath.rfind('.'); auto count = lastDot == std::string::npos ? filepath.size() - lastSlash : lastDot - lastSlash; m_Name = filepath.substr(lastSlash, count); } OpenGLShader::OpenGLShader(const std::string& name, const std::string& vertexSrc, const std::string& fragmentSrc) : m_Name(name) { std::unordered_map sources; sources[GL_VERTEX_SHADER] = vertexSrc; sources[GL_FRAGMENT_SHADER] = fragmentSrc; Compile(sources); } OpenGLShader::~OpenGLShader() { glDeleteProgram(m_RendererID); } std::string OpenGLShader::ReadFile(const std::string& filepath) { std::string result; std::ifstream in(filepath, std::ios::in | std::ios::binary); if (in) { in.seekg(0, std::ios::end); size_t size = in.tellg(); if (size != -1) { result.resize(size); in.seekg(0, std::ios::beg); in.read(&result[0], size); } } return result; } std::unordered_map OpenGLShader::PreProcess(const std::string& source) { std::unordered_map shaderSources; const char* typeToken = "#type"; size_t typeTokenLength = strlen(typeToken); size_t pos = source.find(typeToken, 0); while (pos != std::string::npos) { size_t eol = source.find_first_of("\r\n", pos); size_t begin = pos + typeTokenLength + 1; std::string type = source.substr(begin, eol - begin); size_t nextLinePos = source.find_first_not_of("\r\n", eol); pos = source.find(typeToken, nextLinePos); shaderSources[ShaderTypeFromString(type)] = (pos == std::string::npos) ? source.substr(nextLinePos) : source.substr(nextLinePos, pos - nextLinePos); } return shaderSources; } void OpenGLShader::Compile(const std::unordered_map& shaderSources) { uint32_t program = glCreateProgram(); std::vector glShaderIDs(shaderSources.size()); for (auto& kv : shaderSources) { uint32_t type = kv.first; const std::string& source = kv.second; uint32_t shader = glCreateShader(type); const char* sourceCStr = source.c_str(); glShaderSource(shader, 1, &sourceCStr, 0); glCompileShader(shader); int isCompiled = 0; glGetShaderiv(shader, GL_COMPILE_STATUS, &isCompiled); if (isCompiled == GL_FALSE) { int maxLength = 0; glGetShaderiv(shader, GL_INFO_LOG_LENGTH, &maxLength); std::vector infoLog(maxLength); glGetShaderInfoLog(shader, maxLength, &maxLength, &infoLog[0]); glDeleteShader(shader); std::cout << "Shader compilation failure!" << std::endl << infoLog.data() << std::endl; return; } glAttachShader(program, shader); glShaderIDs.push_back(shader); } m_RendererID = program; glLinkProgram(m_RendererID); int isLinked = 0; glGetProgramiv(m_RendererID, GL_LINK_STATUS, (int*)&isLinked); if (isLinked == GL_FALSE) { int maxLength = 0; glGetProgramiv(m_RendererID, GL_INFO_LOG_LENGTH, &maxLength); std::vector infoLog(maxLength); glGetProgramInfoLog(m_RendererID, maxLength, &maxLength, &infoLog[0]); glDeleteProgram(m_RendererID); for (auto id : glShaderIDs) glDeleteShader(id); std::cout << "Shader link failure!" << std::endl << infoLog.data() << std::endl; return; } for (auto id : glShaderIDs) { glDetachShader(m_RendererID, id); glDeleteShader(id); } } void OpenGLShader::Bind() const { glUseProgram(m_RendererID); } void OpenGLShader::Unbind() const { glUseProgram(0); } void OpenGLShader::SetInt(const std::string& name, int value) { UploadUniformInt(name, value); } void OpenGLShader::SetIntArray(const std::string& name, int* values, uint32_t count) { UploadUniformIntArray(name, values, count); } void OpenGLShader::SetFloat(const std::string& name, float value) { UploadUniformFloat(name, value); } void OpenGLShader::SetFloat3(const std::string& name, const glm::vec3& value) { UploadUniformFloat3(name, value); } void OpenGLShader::SetFloat4(const std::string& name, const glm::vec4& value) { UploadUniformFloat4(name, value); } void OpenGLShader::SetMat4(const std::string& name, const glm::mat4& value) { UploadUniformMat4(name, value); } void OpenGLShader::UploadUniformInt(const std::string& name, int value) { int location = glGetUniformLocation(m_RendererID, name.c_str()); glUniform1i(location, value); } void OpenGLShader::UploadUniformIntArray(const std::string& name, int* values, uint32_t count) { int location = glGetUniformLocation(m_RendererID, name.c_str()); glUniform1iv(location, count, values); } void OpenGLShader::UploadUniformFloat(const std::string& name, float value) { int location = glGetUniformLocation(m_RendererID, name.c_str()); glUniform1f(location, value); } void OpenGLShader::UploadUniformFloat2(const std::string& name, const glm::vec2& value) { int location = glGetUniformLocation(m_RendererID, name.c_str()); glUniform2f(location, value.x, value.y); } void OpenGLShader::UploadUniformFloat3(const std::string& name, const glm::vec3& value) { int location = glGetUniformLocation(m_RendererID, name.c_str()); glUniform3f(location, value.x, value.y, value.z); } void OpenGLShader::UploadUniformFloat4(const std::string& name, const glm::vec4& value) { int location = glGetUniformLocation(m_RendererID, name.c_str()); glUniform4f(location, value.x, value.y, value.z, value.w); } void OpenGLShader::UploadUniformMat3(const std::string& name, const glm::mat3& matrix) { int location = glGetUniformLocation(m_RendererID, name.c_str()); glUniformMatrix3fv(location, 1, GL_FALSE, glm::value_ptr(matrix)); } void OpenGLShader::UploadUniformMat4(const std::string& name, const glm::mat4& matrix) { int location = glGetUniformLocation(m_RendererID, name.c_str()); glUniformMatrix4fv(location, 1, GL_FALSE, glm::value_ptr(matrix)); } };