#pragma once #ifndef _NB_BUFFER #define _NB_BUFFER #include "GLLoad.hpp" #include #include #include #include #include "NBCore/Errors.hpp" #include "NBCore/Types.hpp" namespace nb { typedef std::vector RawVec; static uint8_t GLSLTypeSize(GLenum type) { switch(type) { case GL_SHORT: case GL_UNSIGNED_SHORT: case GL_HALF_FLOAT: return 2; break; case GL_INT: case GL_UNSIGNED_INT: case GL_FLOAT: return 4; break; case GL_DOUBLE: return 8; break; default: return 1; break; } } struct VertexAttributePointer { GLuint buffer = 0; int32_t offset = 0; GLsizei stride = -1; GLuint divisor = 0; }; struct VertexAttribute { GLint GLSLSize; GLenum GLSLType; GLboolean GLSLNormalization; VertexAttributePointer ptr; }; static bool isIndexInVertexAttribute(int i, const VertexAttribute& va) { if ((i -= va.ptr.offset) < 0) { return false; } return (i%va.ptr.stride) < GLSLTypeSize(va.GLSLType) * va.GLSLSize; } typedef std::vector VertexAttributeList; template RawVec vectorToRaw(const std::vector& vec) { unsigned int num_bytes = vec.size() * sizeof(T); RawVec ret(num_bytes); memcpy(ret.data(), vec.data(), num_bytes); return ret; } template RawVec concatVectorsToRaw(const std::vector& vec1, const std::vector& vec2) { RawVec vec1_raw = vectorToRaw(vec1); RawVec vec2_raw = vectorToRaw(vec2); unsigned int vec1_raw_size = vec1_raw.size(); unsigned int vec2_raw_size = vec2_raw.size(); RawVec ret(vec1_raw_size + vec2_raw_size); memcpy(ret.data(), vec1_raw.data(), vec1_raw_size); memcpy(ret.data() + vec1_raw_size, vec2_raw.data(), vec2_raw_size); return ret; } template std::vector rawToVector(const RawVec& vec) { if (vec.size() % sizeof(T) != 0) { throw std::runtime_error("Data size does not align to std::vector<" + std::string(typeid(T).name()) + ">."); } unsigned int num_elmts = vec.size() / sizeof(T); std::vector ret(num_elmts); memcpy(ret.data(), vec.data(), vec.size()); return ret; } class BufferError : public std::runtime_error { public: const bool error; BufferError(const std::string& msg, const std::string& file="", int line=-1) : std::runtime_error(NB::formatDebugString(msg, file, line)), error(true) {} BufferError(bool isError=true) : std::runtime_error(""), error(isError) {} }; template class OpenGLObject { public: OpenGLObject(OpenGLObject&&) = default; OpenGLObject& operator=(OpenGLObject&&) = default; ~OpenGLObject() { remove(); } virtual void bind() const = 0; virtual void unbind() const = 0; virtual bool isInitialized() const = 0; GLuint id() const { return _id; } protected: OpenGLObject() = default; OpenGLObject(const OpenGLObject&) = delete; OpenGLObject& operator=(const OpenGLObject&) = delete; virtual void remove() const = 0; GLuint _id; }; class VAO : public virtual OpenGLObject { public: using Base = OpenGLObject; using Base::Base; VAO() { glGenVertexArrays(1, &_id); } void bind() const { glBindVertexArray(_id); } void unbind() const { glBindVertexArray(0); } protected: using Base::_id; const void remove() { glDeleteVertexArrays(1, &_id); } }; template class Buffer : public virtual OpenGLObject> { public: using Base = OpenGLObject; using Base::Base; Buffer(GLenum usage_ = GL_STATIC_DRAW) : usage(usage_) { glGenBuffers(1, &_id); } void bind() const { glBindBuffer(GLTarget, _id); } void unbind() const { glBindBuffer(GLTarget, 0); } void data(void* data_, size_t size) const { glBufferData(GLTarget, size, data_, usage); } void data(nb::ByteVector& data_) const { glBufferData(GLTarget, data_.size(), data_.data(), usage); } void invalidate() const { glInvalidateBufferData(_id); } const GLenum GLTarget = BufferType::GLTarget; GLenum usage; protected: using Base::_id; void remove() { glDeleteBuffers(1, &_id); } }; class VertexBuffer : public virtual Buffer { public: static const GLenum GLTarget = GL_ARRAY_BUFFER; protected: }; class ElementBuffer : public virtual Buffer { public: static const GLenum GLTarget = GL_ELEMENT_ARRAY_BUFFER; protected: }; /* class Buffer : public OpenGLObject { public: Buffer( GLenum buffer_type, GLenum usage=GL_STATIC_DRAW ) : _type(buffer_type), _usage(usage) { glGenBuffers(1, &_id); } Buffer( const RawVec& init_data, GLenum buffer_type, GLenum usage=GL_STATIC_DRAW ) : _type{buffer_type}, _usage{usage} { data(init_data); } Buffer( unsigned int size, GLenum buffer_type, GLenum usage=GL_STATIC_DRAW ) : Buffer(RawVec(size), buffer_type, usage) {} Buffer(Buffer&& rhs) { *this = std::move(rhs); } Buffer& operator=(Buffer&& rhs) { remove(); _usage = rhs._usage; _id = rhs._id; rhs._id = 0; return *this; } GLenum usage() const { return _usage; } unsigned int id() const { return _id; } GLuint size() const { return _size; } bool isInitialized() const override { return _id && glIsBuffer(_id) && bool(_size); } RawVec data() const { RawVec ret(_size); bind(); glGetBufferSubData(_type, 0, _size, ret.data()); return ret; } void bind() const override { glBindBuffer(_type, _id); } void unbind() const override { glBindBuffer(_type, 0); } GLenum usage(GLenum usage) { _usage=usage; data(data()); return _usage; } void data(const RawVec& set_data) { if (!glfwGetCurrentContext()) { THROW_BUFFER_ERROR("No OpenGL context."); } bind(); glBufferData(_type, set_data.size(), set_data.data(), _usage); _size = set_data.size(); } void data(const RawVec& newData, unsigned int offset) { if (newData.size()+offset > _size) { THROW_BUFFER_ERROR("Attempting to overflow buffer of capacity " + std::to_string(_size) + "."); } bind(); glBufferSubData(_type, offset, newData.size(), newData.data()); } void data(void* src, unsigned int offset, unsigned int size) { if (size+offset > _size) { THROW_BUFFER_ERROR("Attempting to overflow buffer of capacity " + std::to_string(_size) + "."); } bind(); glBufferSubData(_type, offset, size, src); } protected: virtual void remove() const override { unbind(); if (_id) { glDeleteBuffers(1, &_id); } } using OpenGLObject::_id; GLenum _type; GLenum _usage; unsigned int _size = 0; }; class Texture : public OpenGLObject { public: Texture(GLenum tar=GL_TEXTURE_2D) : _target(tar) { glGenTextures(1, &_id); } Texture(Texture&& rhs) { *this=std::move(rhs); } Texture& operator=(Texture&& rhs) { _target = rhs._target; _id = rhs._id; rhs._id = 0; return *this; } void bind() const override { glBindTexture(_target, _id); } void unbind() const override { glBindTexture(_target, 0); } bool isInitialized() const override { return _id && glIsTexture(_id); } protected: using OpenGLObject::_id; GLenum _target; }; class Framebuffer : public OpenGLObject { public: Framebuffer(GLenum tar=GL_FRAMEBUFFER) : _target(tar) { glGenFramebuffers(1, &_id); } Framebuffer(Framebuffer&& rhs) { *this=std::move(rhs); } Framebuffer& operator=(Framebuffer&& rhs) { remove(); _target = rhs._target; _id = rhs._id; rhs._id = 0; return *this; } void bind() const override { glBindFramebuffer(_target, _id); } void unbind() const override { glBindFramebuffer(_target, 0); } bool isInitialized() const override { return _id && glIsFramebuffer(_id); } GLenum status() const { bind(); return glCheckFramebufferStatus(_target); } protected: virtual void remove() const override { unbind(); if (_id) { glDeleteFramebuffers(1, &_id); } } using OpenGLObject::_id; GLenum _target; }; class Renderbuffer : public OpenGLObject { public: Renderbuffer(unsigned int x, unsigned int y, GLenum format=GL_RGBA32F, unsigned int multi=0) : Renderbuffer() { storage(x, y, format, multi); } Renderbuffer() { glGenRenderbuffers(1, &_id); } Renderbuffer(Renderbuffer&& rhs) { *this = std::move(rhs); } Renderbuffer& operator=(Renderbuffer&& rhs) { _format = rhs._format; _id = rhs._id; rhs._id = 0; return *this; } virtual void bind() const override { glBindRenderbuffer(GL_RENDERBUFFER, _id); } virtual void unbind() const override { glBindRenderbuffer(GL_RENDERBUFFER, _id); } virtual bool isInitialized() const override { return _id && glIsRenderbuffer(_id); } void storage(unsigned int x, unsigned int y, GLenum format=GL_RGBA32F, unsigned int multi=0) { _sizex = x; _sizey = y; _format = format; _multisample = multi; bind(); glRenderbufferStorageMultisample(GL_RENDERBUFFER, _multisample, _format, _sizex, _sizey); } protected: virtual void remove() const override { unbind(); if (_id) { glDeleteRenderbuffers(1, &_id); } } using OpenGLObject::_id; GLenum _format; unsigned int _sizex{0}; unsigned int _sizey{0}; unsigned int _multisample{0}; }; */ } // namespace NB #endif // _NB_BUFFER