include/globject.hpp
4f14c1c2
 #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_