/* * * Copyright (C) 2023 Brett Terpstra * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ #ifndef BLT_WITH_GRAPHICS_MODEL_H #define BLT_WITH_GRAPHICS_MODEL_H #include #include #include #include #include #include namespace blt::gfx { struct vbo_t_owner; class static_dynamic_array; class vertex_array_t; // vbo struct vertex_buffer_t { friend vbo_t_owner; friend vertex_array_t; friend static_dynamic_array; private: GLuint bufferID_ = 0; GLsizeiptr size_ = 0; GLint buffer_type = 0; GLint memory_type = 0; public: void create(GLint type = GL_ARRAY_BUFFER); void bind() const; void unbind() const; void allocate(GLsizeiptr size, GLint mem_type = GL_STATIC_DRAW, const void* data = nullptr); template void allocate(GLsizeiptr size, const T* data, GLint mem_type = GL_STATIC_DRAW) { allocate(size, mem_type, static_cast(data)); } void sub_update(GLsizeiptr offset, GLsizeiptr size, void* data) const; void update(GLsizeiptr size, void* data); void destroy(); }; // ssbo struct shader_buffer_t : public vertex_buffer_t { public: inline void create() { vertex_buffer_t::create(GL_SHADER_STORAGE_BUFFER); } }; // ebo struct element_buffer_t : public vertex_buffer_t { public: inline void create() { vertex_buffer_t::create(GL_ELEMENT_ARRAY_BUFFER); } }; struct vbo_t_owner { vertex_buffer_t vbo; vbo_t_owner() = default; explicit vbo_t_owner(vertex_buffer_t vbo): vbo(vbo) {} vertex_buffer_t* operator->() { return &vbo; } ~vbo_t_owner() { if (!vbo.bufferID_) return; vbo.destroy(); vbo.unbind(); } }; /** * Since most VAOs will not use more than 8 VBOs it makes no sense to heap allocate memory to store them * This class is used to make that easier to handle */ class static_dynamic_array { public: using vbo_type = std::shared_ptr; private: static constexpr size_t DATA_SIZE = 8; using array_t = std::array; std::variant data_; size_t size_ = DATA_SIZE; size_t max = 0; void swap(); public: static_dynamic_array(); static_dynamic_array(const static_dynamic_array& copy) = delete; static_dynamic_array(static_dynamic_array&& move) noexcept = default; static_dynamic_array& operator=(const static_dynamic_array& copy) = delete; static_dynamic_array& operator=(static_dynamic_array&& move) noexcept = default; vbo_type& operator[](size_t index); [[nodiscard]] inline size_t used() const noexcept { return max; } ~static_dynamic_array() { if (std::holds_alternative(data_)) delete[] std::get(data_); } }; /** * basic VAO class. */ class vertex_array_t { private: GLuint vaoID; static_dynamic_array VBOs; blt::hashset_t used_attributes; vertex_buffer_t element; void handle_vbo(const vertex_buffer_t& vbo, int attribute_number, int coordinate_size, GLenum type, int stride, long offset); public: vertex_array_t(); vertex_array_t(const vertex_array_t&) = delete; vertex_array_t(vertex_array_t&&) = delete; vertex_array_t& operator=(const vertex_array_t&) = delete; vertex_array_t& operator=(vertex_array_t&&) = delete; /** * This function takes ownership of the underlying VBO (GPU side). It will be freed when the basic vertex array is deleted * @param vbo vbo to bind to this attribute * @param attribute_number attribute number to bind to * @param coordinate_size size of the data (number of * @param type type of data * @param stride how many bytes this data takes (for the entire per-vertex data structure) 0 will assume packed data * This is in effect how many bytes until the next block of data * @param offset offset into the data structure to where the data is stored * @return a shared pointer to the stored vbo. used for chaining VAOs with multiple shared VBOs */ static_dynamic_array::vbo_type bindVBO(const vertex_buffer_t& vbo, int attribute_number, int coordinate_size, GLenum type, int stride, long offset); // same as the other bind method except you provide the shared reference. void bindVBO(const static_dynamic_array::vbo_type& vbo, int attribute_number, int coordinate_size, GLenum type, int stride, long offset); inline void bindElement(const vertex_buffer_t& vbo) { bind(); element = vbo; vbo.bind(); unbind(); } inline vertex_buffer_t& getElement() { return element; } /** * Returns a non-owning reference to a vbo allowing for updating the VBO * The VBO is considered invalid if its ID is 0 */ inline vertex_buffer_t& operator[](size_t index) { return getBuffer(index); } inline vertex_buffer_t& getBuffer(size_t index) { return VBOs[index]->vbo; } inline void bind() const { glBindVertexArray(vaoID); } static inline void unbind() { glBindVertexArray(0); } static inline static_dynamic_array::vbo_type createSharedVBO(const vertex_buffer_t& vbo) { return std::make_shared(vbo); } ~vertex_array_t(); }; } #endif //BLT_WITH_GRAPHICS_MODEL_H