#pragma once #include "collections.h" #include "gl_traits.h" #include "special_members.h" #include class VertexArrayObject { public: template [[nodiscard]] VertexArrayObject(const T & arrayObject) { glBindVertexArray(arrayObject); } ~VertexArrayObject() { glBindVertexArray(0); } NO_MOVE(VertexArrayObject); NO_COPY(VertexArrayObject); template struct MP { constexpr MP(m T::*p) : P {p} { } operator void *() const { return &(static_cast(nullptr)->*P); } m T::*P; using value_type = m; }; template MP(m T::*) -> MP; template VertexArrayObject & addAttribs(const GLuint arrayBuffer, const SequentialCollection auto & vertices, const GLuint divisor = 0) { addAttribs(arrayBuffer, divisor); data(vertices, arrayBuffer, GL_ARRAY_BUFFER); return *this; } template VertexArrayObject & addAttribs(const GLuint arrayBuffer, const GLuint divisor = 0) { configure_attribs(arrayBuffer, divisor); return *this; } // Customisation point template VertexArrayObject & addAttribsFor(const GLuint arrayBuffer, const GLuint divisor = 0); template VertexArrayObject & addIndices(const GLuint arrayBuffer, const Indices & indices) { data(indices, arrayBuffer, GL_ELEMENT_ARRAY_BUFFER); return *this; } VertexArrayObject & addIndices(const GLuint arrayBuffer) { glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, arrayBuffer); return *this; } template static void data(const Data & data, const GLuint arrayBuffer, GLenum target) { using Value = typename Data::value_type; glBindBuffer(target, arrayBuffer); glBufferData(target, static_cast(sizeof(Value) * data.size()), data.data(), GL_STATIC_DRAW); } private: template static auto set_pointer(const GLuint vertexArrayId, const void * ptr, const GLuint divisor) { using traits = gl_traits; const auto usedAttribs = traits::vertexAttribFunc(vertexArrayId, traits::size, traits::type, sizeof(VertexT), ptr); for (GLuint i {}; i < usedAttribs; i++) { glEnableVertexAttribArray(vertexArrayId + i); glVertexAttribDivisor(vertexArrayId + i, divisor); } return usedAttribs; } template static auto set_pointer(const GLuint vertexArrayId, const GLuint divisor) { return set_pointer(vertexArrayId, attrib, divisor); } template void configure_attribs(const GLuint arrayBuffer, const GLuint divisor) { glBindBuffer(GL_ARRAY_BUFFER, arrayBuffer); if constexpr (sizeof...(attribs) == 0) { vertexArrayId += set_pointer(vertexArrayId, nullptr, divisor); } else { ((vertexArrayId += set_pointer(vertexArrayId, divisor)), ...); } } GLuint vertexArrayId {}; };