diff --git a/src/AbstractShaderProgram.cpp b/src/AbstractShaderProgram.cpp new file mode 100644 index 000000000..467f8a1e6 --- /dev/null +++ b/src/AbstractShaderProgram.cpp @@ -0,0 +1,114 @@ +/* + Copyright © 2010 Vladimír Vondruš + + This file is part of Magnum. + + Magnum is free software: you can redistribute it and/or modify + it under the terms of the GNU Lesser General Public License version 3 + only, as published by the Free Software Foundation. + + Magnum is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU Lesser General Public License version 3 for more details. +*/ + +#include "AbstractShaderProgram.h" + +#include +#include + +#define LINKER_MESSAGE_MAX_LENGTH 1024 + +using namespace std; + +namespace Magnum { + +AbstractShaderProgram::LogLevel AbstractShaderProgram::_logLevel = AbstractShaderProgram::Errors; + +AbstractShaderProgram::AbstractShaderProgram(): state(Initialized) { + program = glCreateProgram(); +} + +AbstractShaderProgram::~AbstractShaderProgram() { + if(program) glDeleteProgram(program); +} + +bool AbstractShaderProgram::use() { + if(state != Linked) return false; + + glUseProgram(program); + return true; +} + +bool AbstractShaderProgram::loadShader(Shader* shader) { + if(!shader) return false; + + GLuint _shader = shader->compile(); + if(!_shader) return false; + + glAttachShader(program, _shader); + return true; +} + +bool AbstractShaderProgram::bindAttribute(GLuint location, const string& name) { + /* Check whether given id already exists */ + if(attributes.find(location) != attributes.end()) return false; + + /* Check whether given name already exists */ + for(map::const_iterator it = attributes.begin(); it != attributes.end(); ++it) + if(it->second == name) return false; + + attributes.insert(pair(location, name)); + return true; +} + +void AbstractShaderProgram::link() { + /* Already compiled or failed, exit */ + if(state != Initialized) return; + + /* Set state to failed if anything goes wrong */ + state = Failed; + + /* Bind attributes to specified locations */ + for(map::const_iterator it = attributes.begin(); it != attributes.end(); ++it) + glBindAttribLocation(program, it->first, it->second.c_str()); + + /* Link shader program */ + glLinkProgram(program); + + /* Check link status */ + GLint status; + glGetProgramiv(program, GL_LINK_STATUS, &status); + + /* Display errors or warnings */ + if((status == GL_FALSE && _logLevel != None) || _logLevel == Warnings) { + /* Get message */ + char message[LINKER_MESSAGE_MAX_LENGTH]; + glGetProgramInfoLog(program, LINKER_MESSAGE_MAX_LENGTH, 0, message); + + /* Show error log and delete shader */ + if(status == GL_FALSE) { + cerr << "Shader program failed to link. Error message:" << endl + << message << endl; + + /* Or just warnings, if there are any */ + } else if(message[0] != 0) { + cerr << "Shader program was linked with the following warnings:" << endl + << message << endl; + } + } + + if(status == GL_FALSE) { + glDeleteProgram(program); + program = 0; + state = Failed; + } else state = Linked; +} + +GLint AbstractShaderProgram::uniformLocation(const std::string& name) { + if(state != Linked) return -1; + return glGetUniformLocation(program, name.c_str()); +} + +} diff --git a/src/AbstractShaderProgram.h b/src/AbstractShaderProgram.h new file mode 100644 index 000000000..61c7a96da --- /dev/null +++ b/src/AbstractShaderProgram.h @@ -0,0 +1,190 @@ +#ifndef Magnum_AbstractShaderProgram_h +#define Magnum_AbstractShaderProgram_h +/* + Copyright © 2010 Vladimír Vondruš + + This file is part of Magnum. + + Magnum is free software: you can redistribute it and/or modify + it under the terms of the GNU Lesser General Public License version 3 + only, as published by the Free Software Foundation. + + Magnum is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU Lesser General Public License version 3 for more details. +*/ + +/** @file + * @brief Class Magnum::AbstractShaderProgram + */ + +#include +#include + +#include "Magnum.h" +#include "Shader.h" + +namespace Magnum { + +/** +@brief Base class for shaders + +This class is designed to be used via subclassing. Subclasses define these +functions and properties: + - @b Constructor, which loads particular shaders, links the program, binds + attribute locations and gets uniform locations. + - Attribute location enum with indexes where the particular + attribute is bound, for example: +@code +enum Attribute { + VertexCoordinates = 1, + Color = 2, + TextureCoordinates = 3, + Normal = 4 +}; +@endcode + See also bindAttribute(). + - Uniform binding functions, which set shader uniforms with + setUniform() and setUniformArray() functions. Example: +@code +void setProjectionMatrixUniform(const Matrix4& matrix); +@endcode + +Basic workflow with AbstractShaderProgram subclasses is: instancing the class +(once at the beginning), then in every frame calling use(), binding uniforms +and assigning vertex buffers to given attribute locations. + */ +class AbstractShaderProgram { + DISABLE_COPY(AbstractShaderProgram) + + public: + /** @brief Logging level */ + enum LogLevel { + None, /**< @brief Don't display anything */ + Errors, /**< @brief Display only errors */ + Warnings /**< @brief Display only errors and warnings */ + }; + + /** + * @brief Log level + * + * Log level for displaying compilation messages. Default is Errors. + */ + inline static LogLevel logLevel() { return _logLevel; } + + /** @brief Set log level */ + inline static void setLogLevel(LogLevel level) { _logLevel = level; } + + /** @brief Default constructor */ + AbstractShaderProgram(); + + /** + * @brief Destructor + * + * Deletes the shader program. + */ + ~AbstractShaderProgram(); + + /** + * @brief Use shader + * @return False if the program wasn't successfully linked, true + * otherwise. + */ + bool use(); + + protected: + /** + * @brief Load shader + * @return False if the shader wasn't successfully compiled, true + * otherwise. + * + * Compiles the shader, if it is not already, and prepares it for + * linking. + * @note The shader should be deleted by caller after linking. + */ + bool loadShader(Shader* shader); + + /** + * @brief Bind attribute to given location + * @param location Location + * @param name Attribute name + * @return False if the location or name is already bound, true + * otherwise. + * + * Binds attribute to the location which can be used later when binding + * vertex buffers. + * @note This function should be called between loadShader() calls + * and link(). + */ + bool bindAttribute(GLuint location, const std::string& name); + + /** + * @brief Link the shader + * + * Binds previously specified attributes to given indexes and links the + * shader program together. + */ + void link(); + + /** + * @brief Get uniform location + * @param name Uniform name + * + * @note This function should be called after link(). + */ + GLint uniformLocation(const std::string& name); + + /** + * @brief Set uniform value + * @param location Uniform location (see uniformLocation()) + * @param value Value + * + * @attention This function doesn't check whether this shader is in use! + */ + inline void setUniform(GLint location, GLint value) { + glUniform1i(location, value); + } + + /** @copydoc setUniform(GLint, GLint) */ + void setUniform(GLint location, GLuint value) { + glUniform1ui(location, value); + } + + /** @copydoc setUniform(GLint, GLint) */ + void setUniform(GLint location, GLfloat value) { + glUniform1f(location, value); + } + + /** @copydoc setUniform(GLint, GLint) */ + void setUniform(GLint location, const Vector3& value) { + glUniform3fv(location, 1, value.data()); + } + + /** @copydoc setUniform(GLint, GLint) */ + void setUniform(GLint location, const Vector4& value) { + glUniform4fv(location, 1, value.data()); + } + + /** @copydoc setUniform(GLint, GLint) */ + void setUniform(GLint location, const Matrix4& value) { + glUniformMatrix4fv(location, 1, GL_FALSE, value.data()); + } + + private: + enum State { + Initialized, + Linked, + Failed + }; + + static LogLevel _logLevel; + + GLuint program; + State state; + std::map attributes; +}; + +} + +#endif diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index 531eb4b64..23fd0f169 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -7,8 +7,10 @@ add_subdirectory(Math) set(Magnum_SRCS AbstractObject.cpp + AbstractShaderProgram.cpp Camera.cpp Scene.cpp + Shader.cpp ) add_library(Magnum SHARED ${Magnum_SRCS}) diff --git a/src/Shader.cpp b/src/Shader.cpp new file mode 100644 index 000000000..3cc401334 --- /dev/null +++ b/src/Shader.cpp @@ -0,0 +1,112 @@ +/* + Copyright © 2010 Vladimír Vondruš + + This file is part of Magnum. + + Magnum is free software: you can redistribute it and/or modify + it under the terms of the GNU Lesser General Public License version 3 + only, as published by the Free Software Foundation. + + Magnum is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU Lesser General Public License version 3 for more details. +*/ + +#include "Shader.h" + +#include +#include + +#define COMPILER_MESSAGE_MAX_LENGTH 1024 + +using namespace std; + +namespace Magnum { + +Shader::LogLevel Shader::_logLevel = Shader::Errors; + +bool Shader::addFile(const std::string& filename) { + /* Open file */ + ifstream file(filename.c_str()); + if(!file.good()) { + file.close(); + + cerr << "Shader file " << filename << " cannot be opened." << endl; + + return false; + } + + /* Get size of shader and initialize buffer */ + file.seekg(0, ios::end); + size_t size = file.tellg(); + char* source = new char[size+1]; + source[size] = '\0'; + + /* Read data, close */ + file.seekg(0, ios::beg); + file.read(source, size); + file.close(); + + /* Add to sources and free the buffer */ + addSource(source); + delete source; + + return true; +} + +GLuint Shader::compile() { + /* Already compiled, return */ + if(_state != Initialized) return shader; + + /* Array of sources */ + const GLchar** _sources = new const GLchar*[sources.size()]; + for(size_t i = 0; i != sources.size(); ++i) + _sources[i] = static_cast(sources[i].c_str()); + + /* Create shader and set its source */ + shader = glCreateShader(_type); + glShaderSource(shader, 1, _sources, 0); + + /* Compile shader */ + glCompileShader(shader); + delete _sources; + + /* Check compilation status */ + GLint status; + glGetShaderiv(shader, GL_COMPILE_STATUS, &status); + + /* Display errors or warnings */ + if((status == GL_FALSE && _logLevel != None) || _logLevel == Warnings) { + /* Get message */ + char message[COMPILER_MESSAGE_MAX_LENGTH]; + glGetShaderInfoLog(shader, COMPILER_MESSAGE_MAX_LENGTH, 0, message); + + if(status == GL_FALSE || message[0] != 0) switch(_type) { + case Vertex: cerr << "Vertex"; break; + case Geometry: cerr << "Geometry"; break; + case Fragment: cerr << "Fragment"; break; + } + + /* Show error log and delete shader */ + if(status == GL_FALSE) { + cerr << " shader failed to compile. Error message:" << endl + << message << endl; + + /* Or just warnings, if there are any */ + } else if(message[0] != 0) { + cerr << " shader was compiled with the following warnings:" << endl + << message << endl; + } + } + + if(status == GL_FALSE) { + glDeleteShader(shader); + shader = 0; + _state = Failed; + } else _state = Compiled; + + return shader; +} + +} diff --git a/src/Shader.h b/src/Shader.h new file mode 100644 index 000000000..a8dce4be4 --- /dev/null +++ b/src/Shader.h @@ -0,0 +1,191 @@ +#ifndef Magnum_Shader_h +#define Magnum_Shader_h +/* + Copyright © 2010 Vladimír Vondruš + + This file is part of Magnum. + + Magnum is free software: you can redistribute it and/or modify + it under the terms of the GNU Lesser General Public License version 3 + only, as published by the Free Software Foundation. + + Magnum is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU Lesser General Public License version 3 for more details. +*/ + +/** @file + * @brief Class Magnum::Shader + */ + +#include "Magnum.h" + +#include +#include + +namespace Magnum { + +/** + * @brief Shader + * + * Allows loading and compiling the shader from file or directly from source + * string. Compiled shaders are then passed to AbstractShaderProgram subclasses + * for linking and usage. + */ +class Shader { + DISABLE_COPY(Shader) + + public: + /** @brief Logging level */ + enum LogLevel { + None, /**< @brief Don't display anything */ + Errors, /**< @brief Display only errors */ + Warnings /**< @brief Display only errors and warnings */ + }; + + /** @brief Shader type */ + enum Type { + Vertex = GL_VERTEX_SHADER, /**< @brief Vertex shader */ + Geometry = GL_GEOMETRY_SHADER, /**< @brief Geometry shader */ + Fragment = GL_FRAGMENT_SHADER /**< @brief Fragment shader */ + }; + + /** @brief Shader state */ + enum State { + Initialized, /**< @brief Shader sloaded */ + Compiled, /**< @brief Shader is compiled */ + Failed /**< @brief Compilation failed */ + }; + + /** + * @brief Log level + * + * Log level for displaying compilation messages. + */ + inline static LogLevel logLevel() { return _logLevel; } + + /** @brief Set log level */ + inline static void setLogLevel(LogLevel level) { _logLevel = level; } + + /** + * @brief Load shader from source + * @param type Shader type + * @param source Shader source + * @return Pointer to compiled shader + * + * Loads the shader from one source. Shorthand for + * @code + * Shader* s = new Shader(type); + * s->addData(data); + * @endcode + * Note that it is also possible to compile shader from more than one + * source. + */ + inline static Shader* fromData(Type type, const std::string& source) { + Shader* s = new Shader(type); + s->addSource(source); + return s; + } + + /** + * @brief Load shader from file + * @param type Shader type + * @param filename Source filename + * @return Pointer to compiled shader or zero, if file cannot be opened. + * + * Loads the shader from from one file. Shorthand for + * @code + * Shader* s = new Shader(type); + * if(!s->addFile(filename)) delete s; + * @endcode + * Note that it is also possible to compile shader from more than one + * source. + */ + inline static Shader* fromFile(Type type, const char* filename) { + Shader* s = new Shader(type); + if(!s->addFile(filename)) { + delete s; + return 0; + } + + return s; + } + + /** + * @brief Constructor + * + * Creates empty shader. Sources can be added with addData() or addFile(). + * @see fromData(), fromFile() + */ + inline Shader(Type type): _type(type), _state(Initialized), shader(0) {} + + /** + * @brief Destructor + * + * If the shader is compiled, deletes it. + */ + inline ~Shader() { if(shader) glDeleteShader(shader); } + + /** + * @brief Shader type + * + * Set in constructor. + */ + inline Type type() const { return _type; } + + /** + * @brief Compilation state + * + * Changes after calling compile(). + */ + inline State state() const { return _state; } + + /** + * @brief Add shader source + * @param source String with shader source + * + * If the shader is not compiled already, adds given + * source to source list. Note that it is possible to compile source + * from more than one source. + * @see addFile() + */ + inline void addSource(const std::string& source) { + if(_state == Initialized) sources.push_back(source); + } + + /** + * @brief Add source file + * @param filename Name of source file to read from + * @return False if reading the file fails, true otherwise. + * + * @see addSource() + */ + bool addFile(const std::string& filename); + + /** + * @brief Compile shader + * @return Compiled shader or 0 if compilation failed. + * + * If the shader has any sources present and hasn't been compiled + * before, it tries to compile it. If compilation fails or no sources + * are present, returns 0. If the shader was compiled already, returns + * already existing shader. + * @see logLevel(), state() + */ + GLuint compile(); + + private: + static LogLevel _logLevel; + + Type _type; + State _state; + + std::vector sources; + + GLuint shader; +}; + +} + +#endif