#pragma once #ifndef _NB_BUFFER #define _NB_BUFFER #include "GLLoad.hpp" #include #include #include #include #include "OGLObjects.hpp" namespace nb { extern const std::unordered_map BufferTypes; template struct GLSLEnum; template struct GLSLType; #define GLSLTypeTraits(type_, size_, value_) \ template<>\ struct GLSLEnum {\ using type = type_;\ static constexpr size_t size = size_;\ static constexpr GLenum value=value_;\ };\ template<>\ struct GLSLType {\ using type = type_;\ static constexpr size_t size = size_;\ static constexpr GLenum value=value_;\ }; #define GLSLTypeEquivalence(original, newtype) \ template<>\ struct GLSLType : public GLSLType {}; GLSLTypeTraits(uint8_t, 1, GL_UNSIGNED_BYTE); GLSLTypeTraits(uint16_t, 2, GL_UNSIGNED_SHORT); GLSLTypeTraits(uint32_t, 4, GL_UNSIGNED_INT); GLSLTypeTraits(int8_t, 1, GL_BYTE); GLSLTypeTraits(int16_t, 2, GL_SHORT); GLSLTypeTraits(int32_t, 4, GL_INT); GLSLTypeTraits(float, 4, GL_FLOAT); GLSLTypeTraits(double, 8, GL_DOUBLE); class BufferError : public Error { protected: using Base = Error; public: using Base::Base; enum Codes : unsigned int { UNDEFINED, DATA_OVERFLOW, DATA_ERROR }; static const std::string type; static const ErrorCodeMap ErrorMessages; }; class Buffer : public OpenGLObject { protected: using Codes = BufferError::Codes; using OpenGLObject::_id; GLenum _usage = GL_STATIC_DRAW; size_t _size; Buffer(GLenum target) : Target(target) {} virtual GLuint declare() override { if (!_id) { glGenBuffers(1, &_id); } bind(); return _id; } virtual void remove() override { if (_id) { unbind(); glDeleteBuffers(1, &_id); _id = 0; } } public: const GLenum Target; using OpenGLObject::OpenGLObject; Buffer(Buffer&& other); Buffer& operator=(Buffer&& rhs); virtual void bind() const override { if (_id) { glBindBuffer(Target, _id); } else { THROW(OGLError( OGLError::Codes::INVALID_OBJECT, "w/ BufferType " + BufferTypes.at(Target) )); } } virtual void unbind() const override { glBindBuffer(Target, 0); } GLenum usage() const; size_t size() const; ByteVector data() const; void data(const ByteVector& data, GLenum usage=GL_STATIC_DRAW); void data(const void* data, size_t size, GLenum usage=GL_STATIC_DRAW); void subdata(const void* data, size_t size, GLintptr offset=0); void subdata(const ByteVector& data_, GLintptr offset=0); // virtual void clear() const { glClearBufferData(Target, ) } }; /* template class ImmutableBuffer : public virtual Buffer { public: 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: std::shared_ptr _map = nullptr; GLbitfield _mapAccess = 0; }; */ class ArrayBuffer : public Buffer { public: template ArrayBuffer(const std::vector& data, GLenum usage=GL_STATIC_DRAW) : Buffer(GL_ARRAY_BUFFER) { Buffer::data(vectorToBytes(data), usage); } }; class ElementBuffer : protected Buffer { protected: GLenum _glslType; size_t _typeSize; public: using Buffer::Target; using Buffer::bind; using Buffer::usage; using Buffer::unbind; ElementBuffer(); template ElementBuffer(const T* const data_ptr, size_t size, GLenum usage=GL_STATIC_DRAW) : Buffer(GL_ELEMENT_ARRAY_BUFFER) { data(data_ptr, size, usage); } template ElementBuffer(const std::vector& data_, GLenum usage_=GL_STATIC_DRAW) : ElementBuffer(data_.data(), data_.size(), usage_) {} size_t size() const; size_t typeSize() const; ByteVector data() const; GLenum glslType() const; template void data(const T* data_, size_t size_, GLenum usage_=GL_STATIC_DRAW) { declare(); using TypeInfo = GLSLType; _typeSize = TypeInfo::size; Buffer::data(data_, size_*_typeSize, usage_); _glslType = TypeInfo::value; } template void data(const std::vector& data_, GLenum usage_=GL_STATIC_DRAW) { data(data_.data(), data_.size(), usage_); } template void subdata(const T* data, size_t size, size_t offset) { using TypeInfo = GLSLType; if ( _glslType != TypeInfo::value ) { THROW( BufferError( Codes::DATA_ERROR, "Check element buffer data types" ) ); } Buffer::subdata(data, size*_typeSize, offset); } template void subdata(const std::vector& data, size_t offset=0) { subdata(data.data(), data.size(), offset); } }; } // namespace NB #endif // _NB_BUFFER