#pragma once #ifndef _NB_BUFFER #define _NB_BUFFER #include "GLLoad.hpp" #include #include #include #include #include #include "OGLObjects.hpp" namespace nb { extern const std::unordered_map BufferTypes; class BufferError : public Error { protected: using Base = Error; using Base::Base; public: enum Codes : unsigned int { UNDEFINED, DATA_OVERFLOW, INVALID_BUFFER, HANDLE_OVERWRITE, }; static const std::string type; static const ErrorCodeMap ErrorMessages; }; template class Buffer : public OpenGLObject { using Codes = BufferError::Codes; public: using OpenGLObject::OpenGLObject; Buffer() = default; Buffer(Buffer&& other) { *this = std::move(other); } Buffer& operator=(Buffer&& rhs) { auto targ_name = BufferTypes.at(Target); if (Target != rhs.Target) { THROW(BufferError( Codes::INVALID_BUFFER, "w/ ("+targ_name+") := ("+BufferTypes.at(rhs.Target)+")" )); } //nb::logger.log(std::to_string(_id) + ":=" + std::to_string(rhs._id)); if (_id) { THROW(BufferError( Codes::HANDLE_OVERWRITE, "w/ BufferType = " + targ_name )); } _id = rhs._id; _usage = rhs._usage; _size = rhs._size; rhs._id = 0; rhs._usage = GL_STATIC_DRAW; rhs._size = 0; return *this; } virtual void bind() const override { if (_id) { glBindBuffer(Target, _id); } else { WARN(BufferError( Codes::INVALID_BUFFER, "w/ BufferType " + BufferTypes.at(Target) ), 0xFE); } } virtual void unbind() const override { glBindBuffer(Target, 0); } virtual GLenum usage() const { return _usage; } virtual GLenum usage(GLenum usage_) { _usage = usage_; data(data(), _usage); return _usage; } virtual size_t size() const { return _size; } virtual ByteVector data() const { bind(); ByteVector ret(_size); glGetBufferSubData(Target, 0, _size, ret.data()); return ret; } virtual void data( const void* data_, GLsizei size_, GLenum usage_=GL_STATIC_DRAW ) { declare(); bind(); glBufferData(Target, size_, data_, usage_); _size = size_; _usage = usage_; } virtual void data(const ByteVector& data_, GLenum usage_=GL_STATIC_DRAW) { data(data_.data(), data_.size(), usage_); } virtual void subdata(void* data_, GLsizeiptr size_, GLintptr offset_=0) { bind(); if (offset_+size_ <= _size) { THROW(BufferError(BufferError::Codes::DATA_OVERFLOW)); } glBufferSubData(Target, offset_, size_, data_); } virtual void subdata(ByteVector& data_, GLintptr offset_=0) { bind(); size_t size_ = data_.size(); if (offset_+size_ <= _size) { THROW(BufferError(BufferError::Codes::DATA_OVERFLOW)); } glBufferSubData(Target, offset_, data_.size(), data_.data()); } // virtual void clear() const { glClearBufferData(Target, ) } static const GLenum Target; protected: using OpenGLObject::_id; GLenum _usage = GL_STATIC_DRAW; size_t _size; virtual GLuint declare() override { if (!_id) { glGenBuffers(1, &_id); } return _id; } virtual void remove() override { if (_id) { glDeleteBuffers(1, &_id); _id = 0; } } }; template const GLenum Buffer::Target = BufferType::Target; template class ImmutableBuffer : public virtual Buffer { public: using Base = Buffer; using Base::Target; using Base::bind; ImmutableBuffer() = default; ImmutableBuffer(size_t size_, GLenum usage_ = GL_STATIC_DRAW, GLbitfield flags_=0x0) : size(size_), Base::usage(usage_) { glBufferStorage(Target, size, nullptr, flags_); } std::weak_ptr map(GLbitfield access_) { if (_map && _mapAccess != access_) { unmap(); } _mapAccess = access_; if (!_map) { bind(); glMapBuffer(Target, _mapAccess); } return _map; } void unmap() { bind(); glUnmapBuffer(Target); _map.reset(); } const size_t size; protected: using Base::_id; std::shared_ptr _map = nullptr; GLbitfield _mapAccess = 0; }; template class ArrayBuffer : public Buffer> { using Base = Buffer>; using BufferType = Base; public: using Base::Base; using Base::data; ArrayBuffer(const ByteVector&, GLenum usage_=GL_STATIC_DRAW); static const GLenum Target = GL_ARRAY_BUFFER; protected: using Base::_usage; }; template <> class ArrayBuffer : public virtual ArrayBuffer, public virtual ImmutableBuffer> { using Base = ArrayBuffer; using BufferType = ImmutableBuffer>; using BufferType::BufferType; public: using Base::Target; }; template class ElementBuffer : public Buffer> { using Base = Buffer>; using BufferType = Base; public: using Base::Base; static const GLenum Target = GL_ELEMENT_ARRAY_BUFFER; protected: }; template <> class ElementBuffer : public virtual ElementBuffer, public virtual ImmutableBuffer> { using Base = ElementBuffer; using BufferType = ImmutableBuffer>; public: using BufferType::BufferType; using Base::Target; }; } // namespace NB #endif // _NB_BUFFER