#ifndef GLOBJECT_HPP_ #define GLOBJECT_HPP_ #include <array> #include <functional> #include <stdexcept> #include <string> #include <utility> #include <vector> #ifndef GLOBJECT_LOADER #define GLOBJECT_LOADER <GL/glew.h> #endif // cppcheck-suppress preprocessorErrorDirective #include GLOBJECT_LOADER /// Getters and setters #define GLOBJECT_GET_(TYPE, NAME, STATIC, CONST) \ TYPE STATIC const & NAME() CONST \ { \ return NAME##_; \ } #define GLOBJECT_SET_(TYPE, NAME, STATIC, CONST) \ template<typename Type> \ TYPE STATIC NAME(Type && NAME) \ { \ auto NAME##_old = std::move (NAME##_); \ NAME##_ = std::forward<Type>(NAME); \ return NAME##_old; \ } #define GLOBJECT_THREAD(NAME, INIT) \ decltype(NAME) thread_local NAME INIT; #define GLOBJECT_GET( TYPE, NAME) GLOBJECT_GET_(TYPE, NAME,, const) #define GLOBJECT_SET( TYPE, NAME) GLOBJECT_SET_(TYPE, NAME,, const) #define GLOBJECT_GET_THREAD(TYPE, NAME) GLOBJECT_GET_(TYPE, NAME, static,) #define GLOBJECT_SET_THREAD(TYPE, NAME) GLOBJECT_SET_(TYPE, NAME, static,) #define GLOBJECT_ACCESS(TYPE, NAME) \ GLOBJECT_GET(TYPE, NAME) \ GLOBJECT_SET(TYPE, NAME) #define GLOBJECT_ACCESS_THREAD(TYPE, NAME) \ GLOBJECT_GET_THREAD(TYPE, NAME) \ GLOBJECT_SET_THREAD(TYPE, NAME) /// Debug #ifndef GLOBJECT_DEBUG #define GLOBJECT_DEBUG 1 #endif #if GLOBJECT_DEBUG #define GLOBJECT_DEBUG_IF(D) if (GLObject::debug() >= D) #else #define GLOBJECT_DEBUG_IF(D) if (false) #endif /// Class class GLObject { public: /// Core using Version = std::array<GLint, 2>; bool static supported( Version version_min, std::string const & extension = {} ); GLint static get_integer(GLenum name); GLOBJECT_GET(GLuint, object) operator GLuint() const { return object(); } /// Data template<typename Data> struct DataTraits; /// Path using Path = std::string; using Paths = std::vector<Path>; /// Debug using DebugCallback = std::function<void (std::string const & message)>; using DebugObjects = std::vector<GLObject *>; GLOBJECT_ACCESS_THREAD(int, debug) GLOBJECT_ACCESS_THREAD(DebugCallback, debug_callback) GLOBJECT_GET_THREAD (DebugObjects, debug_objects) std::string debug_name() const; std::string virtual debug_info() const; std::string static debug_objects_name(); std::string static debug_objects_info(); /// Exceptions struct Exception : std::runtime_error { using std::runtime_error::runtime_error; }; protected: /// Special member functions using GLGenObjects = void (*)(GLsizei n, GLuint * objects); using GLDeleteObjects = void (*)(GLsizei n, GLuint const * objects); using GLCreateObject = GLuint (*)(); using GLDeleteObject = void (*)(GLuint); template<GLCreateObject gl_create_object> void static gl_create_object_(GLsizei n, GLuint * objects); template<GLDeleteObject gl_delete_object> void static gl_delete_object_(GLsizei n, GLuint * objects); explicit GLObject( GLGenObjects gl_gen_objects, GLDeleteObjects gl_delete_objects, GLenum object_type, std::string object_label ); virtual ~GLObject(); GLObject(GLObject && other) noexcept; GLObject(GLObject const &) = delete; GLObject & operator=(GLObject &&) = delete; GLObject & operator=(GLObject const &) = delete; /// Path Path static path_prefix_( Path const & path, Path const & prefix ); /// TGA class TGA { public: using Size = std::array<GLsizei, 2>; using Data = std::vector<GLubyte>; explicit TGA(Size size, Data data); TGA static read (Path const & path); void write(Path const & path) const; Size size() const; Data const & data(); private: std::string static str_size_(Size size); void check_header_() const; void check_data_() const; private: // NOLINTNEXTLINE struct Header : std::array<GLubyte, 18> { explicit Header(Size size); }; Header header_; Data data_; }; /// Check void static check_path_(Path const & path); void static check_error_(GLenum error); void static check_supported_( Version version_min, std::string const & extension = {} ); void static check_format_( GLenum format, GLenum format_expected ); void static check_type_( GLenum type, GLenum type_expected ); void static check_internal_format_( GLenum internal_format ); /// Fail [[noreturn]] void fail_action_(std::string const & action) const; /// String std::string static str_path_ (Path const & path); std::string static str_paths_ (Paths const & paths); std::string static str_enum_ (GLenum name); std::string static str_error_ (GLenum error); std::string static str_format_ (GLenum format); std::string static str_type_ (GLenum type); std::string static str_internal_format_(GLenum internal_format); private: /// Debug void debug_label_() const; void static debug_objects_push_back_(GLObject * debug_object); void static debug_objects_erase_ (GLObject * debug_object); /// String std::string static str_object_type_(GLenum object_type); private: /// Special member functions GLDeleteObjects const gl_delete_objects_; /// Core auto static constexpr object_pseudo_ = (GLuint)-1; GLenum const object_type_; std::string object_label_; GLuint object_; /// Debug int static thread_local debug_; DebugCallback static thread_local debug_callback_; DebugObjects static thread_local debug_objects_; }; /// Special member functions template<GLObject::GLCreateObject gl_create_object> void GLObject::gl_create_object_(GLsizei n, GLuint * objects) { for (auto i = GLsizei{0}; i < n; ++i) objects[i] = gl_create_object(); } template<GLObject::GLDeleteObject gl_delete_object> void GLObject::gl_delete_object_(GLsizei n, GLuint * objects) { for (auto i = GLsizei{0}; i < n; ++i) gl_delete_object(objects[i]); } /// Data #define GLOBJECT_DATA( \ DATA, \ V1A, \ V2A, \ V1U, \ V2U, \ COLUMNS, \ ROWS, \ ATTRIB, \ UNIFORM, \ SUFFIX, \ FORMAT, \ TYPE, \ INTERNAL_FORMAT, \ VALUE, \ FOR_COLUMNS, \ PLUS_COLUMN, \ PLUS_COLUMN_TIMES_ROWS, \ ... \ ) \ template<> \ struct GLObject::DataTraits<DATA> \ { \ auto static constexpr name = #DATA; \ auto static constexpr columns = GLint {COLUMNS}; \ auto static constexpr rows = GLint {ROWS}; \ auto static constexpr format = GLenum{FORMAT}; \ auto static constexpr type = GLenum{TYPE}; \ auto static constexpr internal_format = GLenum{INTERNAL_FORMAT}; \ void static attrib(GLuint index, DATA const & value) \ { \ GLOBJECT_DEBUG_IF(1) \ check_supported_({V1A, V2A}); \ FOR_COLUMNS \ glVertexAttrib##ATTRIB##SUFFIX( \ index PLUS_COLUMN, \ VALUE PLUS_COLUMN_TIMES_ROWS \ ); \ } \ void static uniform(GLint location, DATA const & value) \ { \ GLOBJECT_DEBUG_IF(1) \ check_supported_({V1U, V2U}); \ glUniform##UNIFORM##SUFFIX( \ location, \ __VA_ARGS__ \ VALUE \ ); \ } \ }; // GLOBJECT_DATA(DATA , V1A, V2A, V1U, V2U, COLUMNS, ROWS, ATTRIB, UNIFORM , SUFFIX , FORMAT, TYPE, INTERNAL_FORMAT , VALUE, FOR_COLUMNS , PLUS_COLUMN, PLUS_COLUMN_TIMES_ROWS, ... ) #define GLOBJECT_DATA_SCALAR( DATA, V1A, V2A, V1U, V2U, ATTRIB, SUFFIX, INTERNAL, TYPE ) GLOBJECT_DATA(DATA , V1A, V2A, V1U, V2U, 1 , 1 , ATTRIB, , 1##SUFFIX , GL_RED, TYPE, GL_R ##INTERNAL, value, , , , ) #define GLOBJECT_DATA_VECTOR_N( DATA, V1A, V2A, V1U, V2U, ATTRIB, SUFFIX, INTERNAL, TYPE, VALUE, FORMAT, N ) GLOBJECT_DATA(DATA##N , V1A, V2A, V1U, V2U, 1 , N , ATTRIB, , N##SUFFIX##v, FORMAT, TYPE, FORMAT##INTERNAL, VALUE, , , , 1, ) #define GLOBJECT_DATA_MATRIX_N( DATA, V1A, V2A, V1U, V2U, ATTRIB, SUFFIX, INTERNAL, TYPE, VALUE, TRANSPOSE, FORMAT, N ) GLOBJECT_DATA(DATA##N , V1A, V2A, V1U, V2U, N , N , ATTRIB, Matrix , N##SUFFIX##v, FORMAT, TYPE, FORMAT##INTERNAL, VALUE, for (auto column = GLuint{0}; column < columns; ++column), + column , + (column * rows) , 1, TRANSPOSE,) #define GLOBJECT_DATA_MATRIX_N_M(DATA, V1A, V2A, V1U, V2U, ATTRIB, SUFFIX, INTERNAL, TYPE, VALUE, TRANSPOSE, FORMAT, N, M) GLOBJECT_DATA(DATA##N##x##M, V1A, V2A, V1U, V2U, N , M , ATTRIB, Matrix##N##x, M##SUFFIX##v, FORMAT, TYPE, FORMAT##INTERNAL, VALUE, for (auto column = GLuint{0}; column < columns; ++column), + column , + (column * rows) , 1, TRANSPOSE,) #define GLOBJECT_DATA_VECTOR( DATA, V1A, V2A, V1U, V2U, ATTRIB, SUFFIX, INTERNAL, TYPE, VALUE ) \ GLOBJECT_DATA_VECTOR_N( DATA, V1A, V2A, V1U, V2U, ATTRIB, SUFFIX, INTERNAL, TYPE, VALUE, GL_RG, 2 ) \ GLOBJECT_DATA_VECTOR_N( DATA, V1A, V2A, V1U, V2U, ATTRIB, SUFFIX, INTERNAL, TYPE, VALUE, GL_RGB, 3 ) \ GLOBJECT_DATA_VECTOR_N( DATA, V1A, V2A, V1U, V2U, ATTRIB, SUFFIX, INTERNAL, TYPE, VALUE, GL_RGBA, 4 ) #define GLOBJECT_DATA_MATRIX( DATA, ATTRIB, SUFFIX, INTERNAL, TYPE, VALUE, TRANSPOSE ) \ GLOBJECT_DATA_MATRIX_N( DATA, 2, 0, 2, 0, ATTRIB, SUFFIX, INTERNAL, TYPE, VALUE, TRANSPOSE, GL_RG, 2 ) \ GLOBJECT_DATA_MATRIX_N_M( DATA, 2, 0, 2, 1, ATTRIB, SUFFIX, INTERNAL, TYPE, VALUE, TRANSPOSE, GL_RGB, 2, 3) \ GLOBJECT_DATA_MATRIX_N_M( DATA, 2, 0, 2, 1, ATTRIB, SUFFIX, INTERNAL, TYPE, VALUE, TRANSPOSE, GL_RGBA, 2, 4) \ GLOBJECT_DATA_MATRIX_N_M( DATA, 2, 0, 2, 1, ATTRIB, SUFFIX, INTERNAL, TYPE, VALUE, TRANSPOSE, GL_RG, 3, 2) \ GLOBJECT_DATA_MATRIX_N( DATA, 2, 0, 2, 0, ATTRIB, SUFFIX, INTERNAL, TYPE, VALUE, TRANSPOSE, GL_RGB, 3 ) \ GLOBJECT_DATA_MATRIX_N_M( DATA, 2, 0, 2, 1, ATTRIB, SUFFIX, INTERNAL, TYPE, VALUE, TRANSPOSE, GL_RGBA, 3, 4) \ GLOBJECT_DATA_MATRIX_N_M( DATA, 2, 0, 2, 1, ATTRIB, SUFFIX, INTERNAL, TYPE, VALUE, TRANSPOSE, GL_RG, 4, 2) \ GLOBJECT_DATA_MATRIX_N_M( DATA, 2, 0, 2, 1, ATTRIB, SUFFIX, INTERNAL, TYPE, VALUE, TRANSPOSE, GL_RGB, 4, 3) \ GLOBJECT_DATA_MATRIX_N( DATA, 2, 0, 2, 0, ATTRIB, SUFFIX, INTERNAL, TYPE, VALUE, TRANSPOSE, GL_RGBA, 4 ) GLOBJECT_DATA_SCALAR( GLfloat, 2, 0, 2, 0, , f, 32F, GL_FLOAT ) GLOBJECT_DATA_SCALAR( GLbyte, 3, 0, 2, 0, I, i, 8I, GL_BYTE ) GLOBJECT_DATA_SCALAR( GLshort, 3, 0, 2, 0, I, i, 16I, GL_SHORT ) GLOBJECT_DATA_SCALAR( GLint, 3, 0, 2, 0, I, i, 32I, GL_INT ) GLOBJECT_DATA_SCALAR( GLubyte, 3, 0, 3, 0, I, ui, 8UI, GL_UNSIGNED_BYTE ) GLOBJECT_DATA_SCALAR( GLushort, 3, 0, 3, 0, I, ui, 16UI, GL_UNSIGNED_SHORT ) GLOBJECT_DATA_SCALAR( GLuint, 3, 0, 3, 0, I, ui, 32UI, GL_UNSIGNED_INT ) #ifdef GLM_VERSION #include <glm/gtc/type_ptr.hpp> GLOBJECT_DATA_VECTOR( glm::vec, 2, 0, 2, 0, , f, 32F, GL_FLOAT, glm::value_ptr(value) ) GLOBJECT_DATA_VECTOR( glm::ivec, 3, 0, 2, 0, I, i, 32I, GL_INT, glm::value_ptr(value) ) GLOBJECT_DATA_VECTOR( glm::uvec, 3, 0, 3, 0, I, ui, 32UI, GL_UNSIGNED_INT, glm::value_ptr(value) ) GLOBJECT_DATA_MATRIX( glm::mat, , f, 32F, GL_FLOAT, glm::value_ptr(value), GL_FALSE ) #endif #endif // GLOBJECT_HPP_