#ifndef GLSHADER_SHADER_HPP #define GLSHADER_SHADER_HPP #include <vector> #include <string> #include <unordered_map> #include <GL/glew.h> #include <glm/glm.hpp> #include <glm/gtc/type_ptr.hpp> class Shader { public: Shader(std::vector<std::string> paths, std::string name = ""); Shader(Shader &&); Shader(Shader const &); Shader & operator=(Shader &&); Shader & operator=(Shader const &); ~Shader(); Shader & use(); Shader & validate(); template<typename Value> Shader & uniform(std::string const & name, Value const & value); Shader & uniform(std::string const & name, std::string const & buffer_name); template<typename Value> static void uniform_buffer( std::string const & name, Value const & value, GLenum usage = GL_DYNAMIC_DRAW ); static void uniform_buffer_delete(std::string const & name); private: template<typename Value> using StringCache = std::unordered_map<std::string, Value>; struct UniformBuffer { GLuint buffer; GLsizeiptr size; GLenum usage; GLuint binding; }; void new_(); void delete_(); void ensure_current_( std::string const & operation, std::string const & name = "" ); GLint uniform_location_(std::string const & name); GLuint uniform_block_index_(std::string const & name); static GLuint uniform_buffer_( std::string const & name, GLsizeiptr size, GLenum usage ); GLuint program_; std::vector<std::string> paths_; std::string name_; StringCache<GLint> uniform_location_cache_; StringCache<GLuint> uniform_block_index_cache_; static StringCache<UniformBuffer> uniform_buffer_cache_; static GLuint uniform_buffer_binding_next_; }; #ifdef NDEBUG #define GLSHADER_ENSURE_CURRENT(OPERATION, NAME) #else #define GLSHADER_ENSURE_CURRENT(OPERATION, NAME) \ ensure_current_(OPERATION, NAME) #endif template<typename Value> inline Shader & Shader::uniform( std::string const & name, Value const & value ) { GLSHADER_ENSURE_CURRENT("set uniform block", name); uniform_block_index_(name); uniform_buffer(name, value); uniform(name, name); return *this; } template<typename Value> inline void Shader::uniform_buffer( std::string const & name, Value const & value, GLenum usage ) { glBindBuffer( GL_UNIFORM_BUFFER, uniform_buffer_(name, sizeof(value), usage) ); glBufferSubData(GL_UNIFORM_BUFFER, 0, sizeof(value), &value); } #define GLSHADER_UNIFORM(VALUE_TYPE, CODE) \ template<> \ inline Shader & Shader::uniform( \ std::string const & name, VALUE_TYPE const & value \ ) { \ GLSHADER_ENSURE_CURRENT("set uniform", name); \ CODE; \ return *this; \ } #define GLSHADER_UNIFORM_SCALAR(VALUE_TYPE, GL_TYPE) \ GLSHADER_UNIFORM( \ VALUE_TYPE, \ glUniform1##GL_TYPE( \ uniform_location_(name), value \ ) \ ) #define GLSHADER_UNIFORM_N(N, GLM_VALUE_TYPE, GL_TYPE) \ GLSHADER_UNIFORM( \ glm::GLM_VALUE_TYPE##N, \ glUniform##N##GL_TYPE##v( \ uniform_location_(name), 1, glm::value_ptr(value) \ ) \ ) #define GLSHADER_UNIFORM_N_BOOL(N) \ GLSHADER_UNIFORM( \ glm::bvec##N, \ GLint int_value[N]; \ for (auto i = 0; i < N; ++i) \ int_value[i] = value[i]; \ glUniform##N##iv( \ uniform_location_(name), 1, &int_value[0] \ ) \ ) #define GLSHADER_UNIFORM_MATRIX_N(N) \ GLSHADER_UNIFORM( \ glm::mat##N, \ glUniformMatrix##N##fv( \ uniform_location_(name), 1, GL_FALSE, glm::value_ptr(value) \ ) \ ) #define GLSHADER_UNIFORM_MATRIX_N_M(N, M) \ GLSHADER_UNIFORM( \ glm::mat##N##x##M, \ glUniformMatrix##N##x##M##fv( \ uniform_location_(name), 1, GL_FALSE, glm::value_ptr(value) \ ) \ ) GLSHADER_UNIFORM_SCALAR(bool, i) GLSHADER_UNIFORM_SCALAR(int, i) GLSHADER_UNIFORM_SCALAR(glm::uint, ui) GLSHADER_UNIFORM_SCALAR(float, f) GLSHADER_UNIFORM_SCALAR(double, f) GLSHADER_UNIFORM_N_BOOL(2) GLSHADER_UNIFORM_N_BOOL(3) GLSHADER_UNIFORM_N_BOOL(4) GLSHADER_UNIFORM_N(2, ivec, i) GLSHADER_UNIFORM_N(3, ivec, i) GLSHADER_UNIFORM_N(4, ivec, i) GLSHADER_UNIFORM_N(2, uvec, ui) GLSHADER_UNIFORM_N(3, uvec, ui) GLSHADER_UNIFORM_N(4, uvec, ui) GLSHADER_UNIFORM_N(2, vec, f) GLSHADER_UNIFORM_N(3, vec, f) GLSHADER_UNIFORM_N(4, vec, f) GLSHADER_UNIFORM_MATRIX_N(2) GLSHADER_UNIFORM_MATRIX_N(3) GLSHADER_UNIFORM_MATRIX_N(4) GLSHADER_UNIFORM_MATRIX_N_M(2, 3) GLSHADER_UNIFORM_MATRIX_N_M(2, 4) GLSHADER_UNIFORM_MATRIX_N_M(3, 2) GLSHADER_UNIFORM_MATRIX_N_M(3, 4) GLSHADER_UNIFORM_MATRIX_N_M(4, 2) GLSHADER_UNIFORM_MATRIX_N_M(4, 3) #endif // GLSHADER_SHADER_HPP