/* ** BuilderNative Renderer ** Copyright (c) 2019 Magnus Norddahl ** ** This software is provided 'as-is', without any express or implied ** warranty. In no event will the authors be held liable for any damages ** arising from the use of this software. ** ** Permission is granted to anyone to use this software for any purpose, ** including commercial applications, and to alter it and redistribute it ** freely, subject to the following restrictions: ** ** 1. The origin of this software must not be misrepresented; you must not ** claim that you wrote the original software. If you use this software ** in a product, an acknowledgment in the product documentation would be ** appreciated but is not required. ** 2. Altered source versions must be plainly marked as such, and must not be ** misrepresented as being the original software. ** 3. This notice may not be removed or altered from any source distribution. */ #include "Precomp.h" #include "GLShader.h" #include "GLRenderDevice.h" #include void GLShader::Setup(const std::string& identifier, const std::string& vertexShader, const std::string& fragmentShader, bool alphatest) { mIdentifier = identifier; mVertexText = vertexShader; mFragmentText = fragmentShader; mAlphatest = alphatest; } bool GLShader::CheckCompile(GLRenderDevice* device) { bool firstCall = !mProgramBuilt; if (firstCall) { mProgramBuilt = true; CreateProgram(device); glUseProgram(mProgram); glUniform1i(glGetUniformLocation(mProgram, "texture1"), 0); glUseProgram(0); } return !mErrors.size(); } std::string GLShader::GetCompileError() { std::string lines = "Error compiling "; if (!mVertexShader) lines += "vertex "; else if (!mFragmentShader) lines += "fragment "; lines += "shader \"" + mIdentifier + "\":\r\n"; for (auto c : mErrors) { if (c == '\r') continue; if (c == '\n') lines += "\r\n"; else lines += c; } return lines; } void GLShader::Bind() { if (!mProgram || !mProgramBuilt || mErrors.size()) return; glUseProgram(mProgram); } void GLShader::CreateProgram(GLRenderDevice* device) { const char* prefixNAT = R"( #version 150 #line 1 )"; const char* prefixAT = R"( #version 150 #define ALPHA_TEST #line 1 )"; const char* prefix = mAlphatest ? prefixAT : prefixNAT; mVertexShader = CompileShader(prefix + mVertexText, GL_VERTEX_SHADER); if (!mVertexShader) return; mFragmentShader = CompileShader(prefix + mFragmentText, GL_FRAGMENT_SHADER); if (!mFragmentShader) return; mProgram = glCreateProgram(); glAttachShader(mProgram, mVertexShader); glAttachShader(mProgram, mFragmentShader); glBindAttribLocation(mProgram, (GLuint)DeclarationUsage::Position, "AttrPosition"); glBindAttribLocation(mProgram, (GLuint)DeclarationUsage::Color, "AttrColor"); glBindAttribLocation(mProgram, (GLuint)DeclarationUsage::TextureCoordinate, "AttrUV"); glBindAttribLocation(mProgram, (GLuint)DeclarationUsage::Normal, "AttrNormal"); glLinkProgram(mProgram); GLint status = 0; glGetProgramiv(mProgram, GL_LINK_STATUS, &status); if (status != GL_TRUE) { GLsizei length = 0; glGetProgramiv(mProgram, GL_INFO_LOG_LENGTH, &length); std::vector errors(length + (size_t)1); glGetProgramInfoLog(mProgram, (GLsizei)errors.size(), &length, errors.data()); mErrors = { errors.begin(), errors.begin() + length }; glDeleteProgram(mProgram); glDeleteShader(mVertexShader); glDeleteShader(mFragmentShader); mProgram = 0; mVertexShader = 0; mFragmentShader = 0; return; } UniformLastUpdates.resize(device->mUniformInfo.size()); UniformLocations.resize(device->mUniformInfo.size(), (GLuint)-1); int count = (int)UniformLocations.size(); for (int i = 0; i < count; i++) { const auto& name = device->mUniformInfo[i].Name; if (!name.empty()) UniformLocations[i] = glGetUniformLocation(mProgram, name.c_str()); } } GLuint GLShader::CompileShader(const std::string& code, GLenum type) { GLuint shader = glCreateShader(type); const GLchar* sources[] = { (GLchar*)code.data() }; const GLint lengths[] = { (GLint)code.size() }; glShaderSource(shader, 1, sources, lengths); glCompileShader(shader); GLint status = 0; glGetShaderiv(shader, GL_COMPILE_STATUS, &status); if (status != GL_TRUE) { GLsizei length = 0; glGetShaderiv(shader, GL_INFO_LOG_LENGTH, &length); std::vector errors(length + (size_t)1); glGetShaderInfoLog(shader, (GLsizei)errors.size(), &length, errors.data()); mErrors = { errors.begin(), errors.begin() + length }; glDeleteShader(shader); return 0; } return shader; } void GLShader::ReleaseResources() { if (mProgram) glDeleteProgram(mProgram); if (mVertexShader) glDeleteShader(mVertexShader); if (mFragmentShader) glDeleteShader(mFragmentShader); mProgram = 0; mVertexShader = 0; mFragmentShader = 0; }