/********************************************************************** * * * cgt - CAMP Graphics Toolbox, Copyright (C) 2012-2014 * * Chair for Computer Aided Medical Procedures * * Technische Universitaet Muenchen, Germany. * * * * * * forked from tgt - Tiny Graphics Toolbox, Copyright (C) 2006-2011 * * Visualization and Computer Graphics Group, Department of * * Computer Science, University of Muenster, Germany. * * * * * * This file is part of the cgt library. This library is free * * software; you can redistribute it and/or modify it under the terms * * of the GNU Lesser General Public License version 2.1 as published * * by the Free Software Foundation. * * * * This library 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 for more details. * * * * You should have received a copy of the GNU Lesser General Public * * License in the file "LICENSE.txt" along with this library. * * If not, see . * * * **********************************************************************/ #ifndef CGT_SHADERMANAGER_H #define CGT_SHADERMANAGER_H #include #include #include #include "cgt/exception.h" #include "cgt/manager.h" #include "cgt/matrix.h" #include "cgt/cgt_gl.h" #include "cgt/types.h" #include "cgt/singleton.h" #include "cgt/vector.h" namespace cgt { /** * Type of a shader object, can be vertex, fragment or geometry shader * * #include statements are allowed. * * Geometry shaders can be controled using directives in shader source. * Accepted directives: * GL_GEOMETRY_INPUT_TYPE_EXT(GL_POINTS | GL_LINES | GL_LINES_ADJACENCY_EXT | GL_TRIANGLES | GL_TRIANGLES_ADJACENCY_EXT) * GL_GEOMETRY_OUTPUT_TYPE_EXT(GL_POINTS | GL_LINE_STRIP | GL_TRIANGLE_STRIP) * GL_GEOMETRY_VERTICES_OUT_EXT() * No newline or space allowed between each pair of brackets. * * Example geometry shader header: * #version 120 * #extension GL_EXT_geometry_shader4 : enable * //GL_GEOMETRY_INPUT_TYPE_EXT(GL_LINES) * //GL_GEOMETRY_OUTPUT_TYPE_EXT(GL_LINE_STRIP) * //GL_GEOMETRY_VERTICES_OUT_EXT(42) * [...] */ class CGT_API ShaderObject { public: friend class Shader; friend class ShaderPreprocessor; enum ShaderType { VERTEX_SHADER = GL_VERTEX_SHADER, FRAGMENT_SHADER = GL_FRAGMENT_SHADER, GEOMETRY_SHADER = GL_GEOMETRY_SHADER_EXT }; // Helper for resolving line number when includes are used in shader files struct LineInfo { LineInfo(int n, std::string s, int sn) : lineNumber_(n), filename_(s), sourceLineNumber_(sn) {} int lineNumber_; //< line number in preprocessed file std::string filename_; //< filename of included file int sourceLineNumber_; //< line number in included file (needed when it itself //< includes another file) }; /** * Creates a shader object of the specified type */ ShaderObject(const std::string& filename, ShaderType type = VERTEX_SHADER); /** * Deletes the shader and source */ ~ShaderObject(); /** * Loads the shader source from the specified file. * * @throw Exception if loading failed. */ void loadSourceFromFile(const std::string& filename) throw (Exception); bool compileShader(); bool isCompiled() const { return isCompiled_; } std::string getCompilerLog() const; bool rebuildFromFile(); /** * Use h as header for shadersource (copies h) */ void setHeader(const std::string& h); void setCustomGlslVersion(const std::string& version) { customGlslVersion_ = version; } ShaderType getType() { return shaderType_; } void setSource(std::string source) { source_ = source; unparsedSource_ = source; } const std::string getSource() { return unparsedSource_; } /** * Set geometry shader input type. For this change to take effect call setDirectives() and * re-link already linked shaders. Currently only GL_POINTS, GL_LINES, * GL_LINES_ADJACENCY_EXT, GL_TRIANGLES or GL_TRIANGLES_ADJACENCY_EXT can be used. */ void setInputType(GLint inputType) { inputType_ = inputType; } GLint getInputType() const { return inputType_; } /** * Set geometry shader output type. For this change to take effect call setDirectives() and * re-link already linked shaders. Currently only GL_POINTS, GL_LINE_STRIP or * GL_TRIANGLE_STRIP can be used. */ void setOutputType(GLint outputType) { outputType_ = outputType; } GLint getOuputType() const { return outputType_; } /** * Set maximum number of primitives a geometry shader can create. * For this change to take effect call setDirectives() * re-link already linked shaders. Limited by GL_MAX_GEOMETRY_OUTPUT_VERTICES_EXT. */ void setVerticesOut(GLint verticesOut) { verticesOut_ = verticesOut; } GLint getVerticesOut() const { return verticesOut_; } protected: void uploadSource(); std::string filename_; ShaderType shaderType_; GLuint id_; std::string source_; std::string unparsedSource_; std::string header_; std::string customGlslVersion_; ///< Optional custom GLSL version, will override global GLSL version if set bool isCompiled_; GLint inputType_; GLint outputType_; GLint verticesOut_; std::vector lineTracker_; ///< keeps track of line numbers when includes are used static const std::string loggerCat_; }; //------------------------------------------------------------------------------ /** * Represents an OpenGL shader program, consisting of linked ShaderObjects. * * @note Convenient loading of shaders from file is provided by ShaderManager. */ class CGT_API Shader { friend class ShaderManager; public: /** * Guard to enable ignoring uniform location errors and ensuring to disable it again upon destruction. */ struct IgnoreUniformLocationErrorGuard { public: /** * Creates a new IgnoreUniformLocationErrorGuard for the given shader. * Sets the shader's ignoreError_ to true. * \param shader Shader to enable ignoring uniform location errors. */ IgnoreUniformLocationErrorGuard(cgt::Shader* shader) : _shader(shader) , _stateToRestore(shader->getIgnoreUniformLocationError()) { _shader->setIgnoreUniformLocationError(true); }; /** * Destructor, restores the shader's original ignoreError_ state. */ ~IgnoreUniformLocationErrorGuard() { _shader->setIgnoreUniformLocationError(_stateToRestore); } private: cgt::Shader* _shader; ///< Shader to modify bool _stateToRestore; ///< Original state }; Shader(); /** * Detaches all shader objects, deletes them an disposes all textures */ ~Shader(); /** * Attach shader object to Shader */ void attachObject(ShaderObject* obj); void detachObject(ShaderObject* obj); void detachObjectsByType(ShaderObject::ShaderType type); /** * Link all shader objects to one shader. * Will re-link already linked shaders. * @return true for success */ bool linkProgram(); bool rebuild(); bool rebuildFromFile(); void setHeaders(const std::string& customHeader); void bindFragDataLocation(GLuint colorNumber, std::string name); GLint getID() {return id_;}; /** * Activates the shader */ void activate(); static void activate(GLint id); static void deactivate(); static GLint getCurrentProgram(); /** * Returns whether the Shader is currently activated */ bool isActivated(); bool isLinked() { return isLinked_; } std::string getLinkerLog() const; // Subroutines bool selectSubroutine(ShaderObject::ShaderType type, const std::string& subroutineName); // // Uniform stuff // /** * Returns uniform location, or -1 on failure */ GLint getUniformLocation(const std::string& name); void setIgnoreUniformLocationError(bool ignoreError); bool getIgnoreUniformLocationError(); // Floats bool setUniform(const std::string& name, GLfloat value); bool setUniform(const std::string& name, GLfloat v1, GLfloat v2); bool setUniform(const std::string& name, GLfloat v1, GLfloat v2, GLfloat v3); bool setUniform(const std::string& name, GLfloat v1, GLfloat v2, GLfloat v3, GLfloat v4); bool setUniform(const std::string& name, GLfloat* v, int count); // Integers bool setUniform(const std::string& name, GLint value); bool setUniform(const std::string& name, GLint v1, GLint v2); bool setUniform(const std::string& name, GLint v1, GLint v2, GLint v3); bool setUniform(const std::string& name, GLint v1, GLint v2, GLint v3, GLint v4); bool setUniform(const std::string& name, GLint* v, int count); // Unsigned Integers bool setUniform(const std::string& name, GLuint value); bool setUniform(const std::string& name, GLuint v1, GLuint v2); bool setUniform(const std::string& name, GLuint v1, GLuint v2, GLuint v3); bool setUniform(const std::string& name, GLuint v1, GLuint v2, GLuint v3, GLuint v4); bool setUniform(const std::string& name, GLuint* v, int count); // Booleans bool setUniform(const std::string& name, bool value); bool setUniform(const std::string& name, bool v1, bool v2); bool setUniform(const std::string& name, bool v1, bool v2, bool v3); bool setUniform(const std::string& name, bool v1, bool v2, bool v3, bool v4); bool setUniform(const std::string& name, GLboolean* v, int count); // Vectors bool setUniform(const std::string& name, const Vector2f& value); bool setUniform(const std::string& name, Vector2f* vectors, GLsizei count = 1); bool setUniform(const std::string& name, const Vector3f& value); bool setUniform(const std::string& name, Vector3f* vectors, GLsizei count = 1); bool setUniform(const std::string& name, const Vector4f& value); bool setUniform(const std::string& name, Vector4f* vectors, GLsizei count = 1); bool setUniform(const std::string& name, const ivec2& value); bool setUniform(const std::string& name, ivec2* vectors, GLsizei count = 1); bool setUniform(const std::string& name, const ivec3& value); bool setUniform(const std::string& name, ivec3* vectors, GLsizei count = 1); bool setUniform(const std::string& name, const ivec4& value); bool setUniform(const std::string& name, ivec4* vectors, GLsizei count = 1); // Note: Matrix is transposed by OpenGL bool setUniform(const std::string& name, const Matrix2f& value, bool transpose = false); bool setUniform(const std::string& name, const Matrix3f& value, bool transpose = false); bool setUniform(const std::string& name, const Matrix4f& value, bool transpose = false); // No location lookup // // Floats static void setUniform(GLint l, GLfloat value); static void setUniform(GLint l, GLfloat v1, GLfloat v2); static void setUniform(GLint l, GLfloat v1, GLfloat v2, GLfloat v3); static void setUniform(GLint l, GLfloat v1, GLfloat v2, GLfloat v3, GLfloat v4); // Integers static void setUniform(GLint l, GLint value); static void setUniform(GLint l, GLint v1, GLint v2); static void setUniform(GLint l, GLint v1, GLint v2, GLint v3); static void setUniform(GLint l, GLint v1, GLint v2, GLint v3, GLint v4); // Vectors static void setUniform(GLint l, const Vector2f& value); static void setUniform(GLint l, const Vector3f& value); static void setUniform(GLint l, const Vector4f& value); static void setUniform(GLint l, const ivec2& value); static void setUniform(GLint l, const ivec3& value); static void setUniform(GLint l, const ivec4& value); static void setUniform(GLint l, const Matrix2f& value, bool transpose = false); static void setUniform(GLint l, const Matrix3f& value, bool transpose = false); static void setUniform(GLint l, const Matrix4f& value, bool transpose = false); // Attributes // // 1 component static void setAttribute(GLint index, GLfloat v1); static void setAttribute(GLint index, GLshort v1); static void setAttribute(GLint index, GLdouble v1); // 2 components static void setAttribute(GLint index, GLfloat v1, GLfloat v2); static void setAttribute(GLint index, GLshort v1, GLshort v2); static void setAttribute(GLint index, GLdouble v1, GLdouble v2); // 3 components static void setAttribute(GLint index, GLfloat v1, GLfloat v2, GLfloat v3); static void setAttribute(GLint index, GLshort v1, GLshort v2, GLshort v3); static void setAttribute(GLint index, GLdouble v1, GLdouble v2, GLdouble v3); // 4 components static void setAttribute(GLint index, GLfloat v1, GLfloat v2, GLfloat v3, GLfloat v4); static void setAttribute(GLint index, GLshort v1, GLshort v2, GLshort v3, GLshort v4); static void setAttribute(GLint index, GLdouble v1, GLdouble v2, GLdouble v3, GLdouble v4); // For vectors static void setAttribute(GLint index, const Vector2f& v); static void setAttribute(GLint index, const Vector3f& v); static void setAttribute(GLint index, const Vector4f& v); static void setAttribute(GLint index, const Vector2d& v); static void setAttribute(GLint index, const Vector3d& v); static void setAttribute(GLint index, const Vector4d& v); static void setAttribute(GLint index, const Vector2& v); static void setAttribute(GLint index, const Vector3& v); static void setAttribute(GLint index, const Vector4& v); static void setAttribute(GLint index, const Vector4& v); static void setAttribute(GLint index, const Vector4& v); static void setAttribute(GLint index, const Vector4& v); static void setAttribute(GLint index, const Vector4& v); static void setAttribute(GLint index, const Vector4& v); // Attribute locations // ATTENTION: This method was deliberately deactivated, since it was not working as expected // with AMD GPUs. // void setAttributeLocation(GLuint index, const std::string& name); GLint getAttributeLocation(const std::string& name); // Normalized attributes static void setNormalizedAttribute(GLint index, GLubyte v1, GLubyte v2, GLubyte v3, GLubyte v4); static void setNormalizedAttribute(GLint index, const Vector4& v); static void setNormalizedAttribute(GLint index, const Vector4& v); static void setNormalizedAttribute(GLint index, const Vector4& v); // Unsigned version static void setNormalizedAttribute(GLint index, const Vector4& v); static void setNormalizedAttribute(GLint index, const Vector4& v); static void setNormalizedAttribute(GLint index, const Vector4& v); protected: /** * Load filename.vert and filename.frag (vertex and fragment shader) and link shader. * * @param customHeader Header to be put in front of the shader source. * * @throw Exception if loading failed */ void load(const std::string& filename, const std::string& customHeader = "") throw (Exception); /** * Load vertex shader \p vertFilename, geometry shader \p geomFilename, * fragment shader \p fragFilename. * * @param customHeader header to be put in front of the shader source * * @throw Exception if loading failed */ void loadSeparate(const std::string& vertFilename, const std::string& geomFilename, const std::string& fragFilename, const std::string& customHeader = "", const std::string& customGlslVersion = "") throw (Exception); typedef std::list ShaderObjects; ShaderObjects objects_; GLuint id_; bool isLinked_; bool ignoreError_; static const std::string loggerCat_; }; //------------------------------------------------------------------------------ class ShaderManager; #ifdef DLL_TEMPLATE_INST template class CGT_API Singleton; #endif #ifdef DLL_TEMPLATE_INST template class CGT_API ResourceManager; #endif /** * Loads shaders from the file system, managing a shader search path. * * @see ResourceManager */ class CGT_API ShaderManager : public ResourceManager, public Singleton { public: ShaderManager(); /** * Load given shaders from file and link them. * Empty file names will be ignored (not loaded and linked). You have to pass the complete * file names, inclusive file extensions (".vert", ".geom", frag"). * * \param vertFilename Vertex shader file name * \param fragFilename Fragment shader file name * \param customHeader Custom header to add to all shaders (may be empty) * \throw Exception if loading failed. * \return The newly created shader. */ Shader* load(const std::string& vertFilename, const std::string& fragFilename, const std::string& customHeader) throw (Exception); /** * Load given shaders from file and link them. * Empty file names will be ignored (not loaded and linked). You have to pass the complete * file names, inclusive file extensions (".vert", ".geom", frag"). * * \param vertFilename Vertex shader file name * \param geomFilename Geometry shader file name (leave empty to disable geometry shader) * \param fragFilename Fragment shader file name * \param customHeader Custom header to add to all shaders (may be empty) * \throw Exception if loading failed. * \return The newly created shader. */ Shader* load(const std::string& vertFilename, const std::string& geomFilename, const std::string& fragFilename, const std::string& customHeader) throw(Exception); /** * Load given shaders from file and link them. * Empty file names will be ignored (not loaded and linked). You have to pass the complete * file names, inclusive file extensions (".vert", ".geom", frag"). * * \param vertFilename Vertex shader file name * \param geomFilename Geometry shader file name (leave empty to disable geometry shader) * \param fragFilename Fragment shader file name * \param customHeader Custom header to add to all shaders (may be empty) * \param customGlslVersion Custom GLSL version for all shaders (leave empty to use default GLSL version from ShaderManager). * \throw Exception if loading failed. * \return The newly created shader. */ Shader* loadWithCustomGlslVersion(const std::string& vertFilename, const std::string& geomFilename, const std::string& fragFilename, const std::string& customHeader, const std::string& customGlslVersion) throw(Exception); bool rebuildAllShadersFromFile(); /** * Sets the global header that will be added to all shaders to \a header. * \param header The new global header that will be added to all shaders. */ void setGlobalHeader(const std::string& header) { globalHeader_ = header; } /** * Gets the global header that will be added to all shaders. * \return globalHeader_ */ const std::string& getGlobalHeader() const { return globalHeader_; } /** * Sets the default GLSL version string, will be added to the '#version' pragma * at the beginning of each shader. * \param version Default GLSL version string. */ void setDefaultGlslVersion(const std::string& version) { defaultGlslVersion_ = version; } /** * Gets the default GLSL version string, will be added to the '#version' pragma * at the beginning of each shader. */ const std::string& getDefaultGlslVersion() const { return defaultGlslVersion_; } protected: std::string defaultGlslVersion_; ///< Default GLSL version string, will be added to the '#version' pragma at the beginning of each shader std::string globalHeader_; ///< Global header that will be added to all shaders. static const std::string loggerCat_; }; /** * Parses #include statements and geometry shader settings */ class ShaderPreprocessor { public: enum Mode { MODE_NONE = 0, MODE_INCLUDE = 1, MODE_GEOMETRY = 2 }; ShaderPreprocessor(ShaderObject* obj, Mode mode = Mode(MODE_INCLUDE | MODE_GEOMETRY)); // Returns the parsed result std::string getResult() const; GLint getGeomShaderInputType() const; GLint getGeomShaderOutputType() const; GLint getGeomShaderVerticesOut() const; protected: void parse(); void parsePart(const std::string& input, const std::string& name = ""); void outputComment(const std::string& comment, const std::string& type = "INFO"); /** * Scan for geometry shader directives in shader source. * * Accepted directives: * GL_GEOMETRY_INPUT_TYPE_EXT(GL_POINTS | GL_LINES | GL_LINES_ADJACENCY_EXT | GL_TRIANGLES | GL_TRIANGLES_ADJACENCY_EXT) * GL_GEOMETRY_OUTPUT_TYPE_EXT(GL_POINTS | GL_LINE_STRIP | GL_TRIANGLE_STRIP) * GL_GEOMETRY_VERTICES_OUT_EXT() * No newline or space allowed between each pair of brackets. * * Example geometry shader header: * #version 120 * #extension GL_EXT_geometry_shader4 : enable * * //GL_GEOMETRY_INPUT_TYPE_EXT(GL_LINES) * //GL_GEOMETRY_OUTPUT_TYPE_EXT(GL_LINE_STRIP) * //GL_GEOMETRY_VERTICES_OUT_EXT(42) * [...] */ bool scanGeomShaderDirectives(); std::string getGeomShaderDirective(const std::string& d); ShaderObject* shd_; std::vector& lineTracker_; ///< keeps track of line numbers when includes are used std::set includedFilenames_; ///< list of all filenames that have already been included (to avoid double inclusion) int activeLine_; std::ostringstream result_; GLint inputType_; GLint outputType_; GLint verticesOut_; static const std::string loggerCat_; }; } // namespace cgt #define ShdrMgr cgt::Singleton::getRef() #endif //CGT_SHADERMANAGER_H