#ifndef GLSHADER_SHADER_HPP_ #define GLSHADER_SHADER_HPP_ #include <map> #include <set> #include <string> #include <unordered_map> #include <GL/glew.h> class Shader { public: using Paths = std::set<std::string>; explicit Shader(Paths const & paths); virtual ~Shader(); Shader(Shader &&) noexcept; Shader(Shader const &) = delete; Shader & operator=(Shader &&) = delete; Shader & operator=(Shader const &) = delete; using Defines = std::map<std::string, std::string>; using Locations = std::map<std::string, GLuint>; static void root(std::string const & root); static void defines(Defines const & defines); static void verts(Locations const & verts); static void frags(Locations const & frags); GLuint program() const; Shader & validate(); Shader & use(); template<typename Value> Shader & uniform( std::string const & name, Value const & value, bool required = true ) = delete; protected: struct Uniform { GLint location; bool set; }; void validate_() const; void current_( std::string const & error ) const; static void error_( std::string const & error, std::string const & error_hint = {} ); Uniform * uniform_( std::string const & error, std::string const & name, bool required ); template<typename Value> using ByName = std::unordered_map<std::string, Value>; GLuint program_; std::string program_name_; std::string static root_; Defines static defines_; Locations static verts_; Locations static frags_; ByName<Uniform> uniforms_; }; // Debug macros. #ifndef NDEBUG #define GLSHADER_DEBUG_(...) __VA_ARGS__ #define GLSHADER_DEBUG_ERROR_(...) \ auto error = std::string{} + __VA_ARGS__; #else #define GLSHADER_DEBUG_(...) #define GLSHADER_DEBUG_ERROR_(...) \ auto error = std::string{}; #endif // NDEBUG // Inline definitions. #define GLSHADER_SET_(TYPE, NAME) \ inline void Shader::NAME(TYPE const & NAME) \ { \ NAME##_ = NAME; \ } GLSHADER_SET_(std::string, root) GLSHADER_SET_(Defines, defines) GLSHADER_SET_(Locations, verts) GLSHADER_SET_(Locations, frags) inline GLuint Shader::program() const { return program_; } inline Shader & Shader::validate() { GLSHADER_DEBUG_(validate_();) return *this; } inline Shader & Shader::use() { glUseProgram(program_); return *this; } // Uniform template specializations. #define GLSHADER_UNIFORM_SIGNATURE_(TYPE) \ template<> \ inline Shader & Shader::uniform( \ std::string const & name, \ TYPE const & value, \ bool required \ ) #define GLSHADER_UNIFORM_DELETE(TYPE) \ GLSHADER_UNIFORM_SIGNATURE_(TYPE) = delete; #define GLSHADER_UNIFORM(TYPE, CODE) \ GLSHADER_UNIFORM_SIGNATURE_(TYPE) \ { \ GLSHADER_DEBUG_ERROR_( \ "Failed to set uniform '" + name + "' of " + program_name_ \ ) \ GLSHADER_DEBUG_(current_(error);) \ if (auto * uniform = uniform_(error, name, required)) \ { \ GLint const & location = uniform->location; \ GLSHADER_DEBUG_(error_(error, "unprocessed previous error");) \ CODE \ GLSHADER_DEBUG_(error_(error, "wrong type?");) \ GLSHADER_DEBUG_(uniform->set = true;) \ } \ return *this; \ } // Uniform scalar template specializations. #define GLSHADER_UNIFORM_SCALAR_(TYPE, SUFFIX) \ GLSHADER_UNIFORM( \ TYPE, \ glUniform1##SUFFIX( \ location, value \ ); \ ) GLSHADER_UNIFORM_SCALAR_(bool, i) GLSHADER_UNIFORM_SCALAR_(GLboolean, i) GLSHADER_UNIFORM_SCALAR_(GLint, i) GLSHADER_UNIFORM_SCALAR_(GLuint, ui) GLSHADER_UNIFORM_SCALAR_(GLfloat, f) GLSHADER_UNIFORM_DELETE(GLdouble) #endif // GLSHADER_SHADER_HPP_