// ============================================================== // This file is part of MegaGlest (www.glest.org) // // Shader.cpp -- Copyright (c) 2006-2007 David Henry // changed for use with MegaGlest: Copyright (C) 2011- by Mark Vejvoda // // This code is licensed under the MIT license: // http://www.opensource.org/licenses/mit-license.php // // Open Source Initiative OSI - The MIT License (MIT):Licensing // // The MIT License (MIT) // Copyright (c) // // Permission is hereby granted, free of charge, to any person obtaining a // copy of this software and associated documentation files (the "Software"), // to deal in the Software without restriction, including without limitation // the rights to use, copy, modify, merge, publish, distribute, sublicense, // and/or sell copies of the Software, and to permit persons to whom the // Software is furnished to do so, subject to the following conditions: // // The above copyright notice and this permission notice shall be included in // all copies or substantial portions of the Software. // // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL // THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN // THE SOFTWARE.// // // Implementation of GLSL shader related classes. // ///////////////////////////////////////////////////////////////////////////// #include #include #include "ShaderManager.h" #include "GlErrors.h" namespace Shared { namespace Graphics { namespace md5 { using std::cout; using std::cerr; using std::endl; ///////////////////////////////////////////////////////////////////////////// // // Global shader related functions. // ///////////////////////////////////////////////////////////////////////////// static GLboolean GLSLCapable = GL_FALSE; // -------------------------------------------------------------------------- // hasShaderSupport // // Return true if the host has GLSL, so that we can use shaders. // -------------------------------------------------------------------------- GLboolean hasShaderSupport () { return GLSLCapable; } // -------------------------------------------------------------------------- // checkExtensionPresence // // Check if an extension is present on the host OpenGL implementation. // Increment @missing if the extension is missing. // -------------------------------------------------------------------------- static void checkExtensionPresence (const string &name, int &missing) { if (!glewIsSupported (name.c_str ())) { cerr << "* missing " << name << " extension" << endl; missing++; } } // -------------------------------------------------------------------------- // initShaderHandling // // Initialize variables and extensions needed for using GLSL. This // function should be called before any shader usage (at application // initialization for example). // -------------------------------------------------------------------------- void initShaderHandling () { int missing = 0; // Check for extensions needed for GLSL support on host checkExtensionPresence ("GL_ARB_shader_objects", missing); checkExtensionPresence ("GL_ARB_shading_language_100", missing); checkExtensionPresence ("GL_ARB_vertex_shader", missing); checkExtensionPresence ("GL_ARB_fragment_shader", missing); // Disable GLSL if one extension is missing if (missing > 0) GLSLCapable = GL_FALSE; else GLSLCapable = GL_TRUE; } ///////////////////////////////////////////////////////////////////////////// // // class Shader implementation. // ///////////////////////////////////////////////////////////////////////////// // -------------------------------------------------------------------------- // Shader::Shader // // Constructor. // -------------------------------------------------------------------------- Shader::Shader (const string &filename) : _name (filename), _handle (0), _compiled (0) { } // -------------------------------------------------------------------------- // Shader::~Shader // // Destructor. Destroy the shader handle. // -------------------------------------------------------------------------- Shader::~Shader () { if (GLEW_VERSION_2_0) { if (glIsShader (_handle)) glDeleteShader (_handle); } else { GLint type; glGetObjectParameterivARB (_handle, GL_OBJECT_TYPE_ARB, &type); if (GL_SHADER_OBJECT_ARB == type) glDeleteObjectARB (_handle); } } // ------------------------------------------------------------------------- // Shader::printInfoLog // // Print log info about a vertex or a fragment shader. // ------------------------------------------------------------------------- void Shader::printInfoLog () const { GLint infologLength = 0; // First check for previous OpenGL errors... checkOpenGLErrors (__FILE__, __LINE__); // Get log's length if (GLEW_VERSION_2_0) glGetShaderiv (_handle, GL_INFO_LOG_LENGTH, &infologLength); else glGetObjectParameterivARB (_handle, GL_OBJECT_INFO_LOG_LENGTH_ARB, &infologLength); // If log is empty, quit if (infologLength <= 1) return; try { GLchar *infoLog = new GLchar[infologLength]; // Get the log... if (GLEW_VERSION_2_0) glGetShaderInfoLog (_handle, infologLength, NULL, infoLog); else glGetInfoLogARB (_handle, infologLength, NULL, infoLog); // ...and print it to standard output cout << "Shader \"" << _name << "\" InfoLog (" << infologLength << "):" << endl << infoLog << endl; delete [] infoLog; } catch (std::bad_alloc &err) { cerr << "Error: memory allocation failed for shader info log" << endl << " Reason: " << err.what () << endl; } } // ------------------------------------------------------------------------- // Shader::compile // // Create and compile the shader. // ------------------------------------------------------------------------- void Shader::compile () throw (std::runtime_error) { const GLchar *code = _code.c_str (); if (GLEW_VERSION_2_0) { // Create a shader object _handle = glCreateShader (shaderType ()); // Upload shader code to OpenGL glShaderSource (_handle, 1, &code, NULL); // Compile shader glCompileShader (_handle); glGetShaderiv (_handle, GL_COMPILE_STATUS, &_compiled); printInfoLog (); // Check for success if (GL_FALSE == _compiled) throw std::runtime_error ("Compilation failed"); } else { // Create a shader object _handle = glCreateShaderObjectARB (shaderType ()); // Upload shader code to OpenGL glShaderSourceARB (_handle, 1, &code, NULL); // Compile shader glCompileShaderARB (_handle); glGetObjectParameterivARB (_handle, GL_OBJECT_COMPILE_STATUS_ARB, &_compiled); printInfoLog (); // Check for success if (GL_FALSE == _compiled) throw std::runtime_error ("Compilation failed"); } } // ------------------------------------------------------------------------- // Shader::loadShaderFile // // Load shader's GLSL code from file. The code is stored into the // _code string member variable. // ------------------------------------------------------------------------- void Shader::loadShaderFile (const string &filename) throw (std::runtime_error) { // Open the file std::ifstream ifs (filename.c_str (), std::ios::in | std::ios::binary); if (ifs.fail ()) throw std::runtime_error ("Couldn't open shader file: " + filename); // Read whole file into string _code.assign (std::istreambuf_iterator(ifs), std::istreambuf_iterator()); // Close file ifs.close (); } ///////////////////////////////////////////////////////////////////////////// // // class VertexShader implementation. // ///////////////////////////////////////////////////////////////////////////// // -------------------------------------------------------------------------- // VertexShader::VertexShader // // Constructor. Read vertex shader code from file and compile it. // -------------------------------------------------------------------------- VertexShader::VertexShader (const string &filename) : Shader (filename) { try { // Load shader code from file loadShaderFile (filename); // Compile the shader compile (); cout << "* Vertex shader \"" << _name << "\" compiled" << endl; } catch (std::runtime_error &err) { cerr << "Error: Faild to create vertex shader from " << _name << endl; cerr << "Reason: " << err.what () << endl; } } ///////////////////////////////////////////////////////////////////////////// // // class FragmentShader implementation. // ///////////////////////////////////////////////////////////////////////////// // -------------------------------------------------------------------------- // FragmentShader::FragmentShader // // Constructor. Read fragment shader code from file and compile it. // -------------------------------------------------------------------------- FragmentShader::FragmentShader (const string &filename) : Shader (filename) { try { // Load shader code from file loadShaderFile (filename); // Compile the shader compile (); cout << "* Fragment shader \"" << _name << "\" compiled" << endl; } catch (std::runtime_error &err) { cerr << "Error: Faild to create fragment shader from " << _name << endl; cerr << "Reason: " << err.what () << endl; } } ///////////////////////////////////////////////////////////////////////////// // // class ShaderProgram implementation. // ///////////////////////////////////////////////////////////////////////////// // -------------------------------------------------------------------------- // ShaderProgram::ShaderProgram // // Constructor. Link vertex and fragment shader. If the vertex shader // or the fragment shader is invalid (has failed to compile), the // shader program creation is aborted. // -------------------------------------------------------------------------- ShaderProgram::ShaderProgram (const string &filename, const VertexShader &vertexShader, const FragmentShader &fragmentShader) : _name (filename), _handle (0), _linked (0) { try { if (vertexShader.fail ()) throw std::runtime_error ("Invalid vertex shader"); if (fragmentShader.fail ()) throw std::runtime_error ("Invalid fragment shader"); if (GLEW_VERSION_2_0) { // Create program and attach vertex and fragment shaders _handle = glCreateProgram (); glAttachShader (_handle, vertexShader.handle ()); glAttachShader (_handle, fragmentShader.handle ()); // Perform link stage glLinkProgram (_handle); glGetProgramiv (_handle, GL_LINK_STATUS, &_linked); // Validate program glValidateProgram (_handle); printInfoLog (); // Check for success if (GL_FALSE == _linked) throw std::runtime_error ("Link stage failed"); } else { // Create program and attach vertex and fragment shaders _handle = glCreateProgramObjectARB (); glAttachObjectARB (_handle, vertexShader.handle ()); glAttachObjectARB (_handle, fragmentShader.handle ()); // Perform link stage glLinkProgramARB (_handle); glGetObjectParameterivARB (_handle, GL_OBJECT_LINK_STATUS_ARB, &_linked); // Validate program glValidateProgramARB (_handle); printInfoLog (); // Check for success if (GL_FALSE == _linked) throw std::runtime_error ("Link stage failed"); } cout << "* Shader \"" << _name << "\" successfully linked" << endl; } catch (std::runtime_error &err) { cerr << "Error: Faild to create shader " << _name << endl; cerr << "Reason: " << err.what () << endl; } } // -------------------------------------------------------------------------- // ShaderProgram::~ShaderProgram // // Destructor. Destroy the shader program handle. // -------------------------------------------------------------------------- ShaderProgram::~ShaderProgram () { if (GLEW_VERSION_2_0) { if (glIsProgram (_handle)) glDeleteProgram (_handle); } else { GLint type; glGetObjectParameterivARB (_handle, GL_OBJECT_TYPE_ARB, &type); if (GL_PROGRAM_OBJECT_ARB == type) glDeleteObjectARB (_handle); } } // ------------------------------------------------------------------------- // ShaderProgram::use // ShaderProgram::unuse // // Bind/unbind the shader. // ------------------------------------------------------------------------- void ShaderProgram::use () const { if (GLEW_VERSION_2_0) glUseProgram (_handle); else glUseProgramObjectARB (_handle); } void ShaderProgram::unuse () const { if (GLEW_VERSION_2_0) glUseProgram (0); else glUseProgramObjectARB (0); } // ------------------------------------------------------------------------- // ShaderProgram::printInfoLog // // Print log info about a shader program. // ------------------------------------------------------------------------- void ShaderProgram::printInfoLog () const { GLint infologLength = 0; // First check for previous OpenGL errors... checkOpenGLErrors (__FILE__, __LINE__); // Get log's length if (GLEW_VERSION_2_0) glGetProgramiv (_handle, GL_INFO_LOG_LENGTH, &infologLength); else glGetObjectParameterivARB (_handle, GL_OBJECT_INFO_LOG_LENGTH_ARB, &infologLength); // If log is empty, quit if (infologLength <= 1) return; try { GLchar *infoLog = new GLchar[infologLength]; // Get the log... if (GLEW_VERSION_2_0) glGetProgramInfoLog (_handle, infologLength, NULL, infoLog); else glGetInfoLogARB (_handle, infologLength, NULL, infoLog); // ...and print it to standard output cout << "Program \"" << _name << "\" InfoLog (" << infologLength << "):" << endl << infoLog << endl; delete [] infoLog; } catch (std::bad_alloc &err) { cerr << "Error: memory allocation failed for shader program " << "info log" << endl << " Reason: " << err.what () << endl; } } }}} //end namespace