#pragma once #ifndef _NB_BUFFER #define _NB_BUFFER #include #include #include #include #include #include #include "Shader.h" #define THROW_BUFFER_ERROR(msg) throw BufferError(msg, __FILE__, __LINE__); #define THROW_VAO_ERROR(msg) throw VAOError(msg, __FILE__, __LINE__); namespace NB{ typedef std::vector RawVec; 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; }; 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; } 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; } } 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) {} }; class VAOError : public std::runtime_error { public: const bool error; VAOError(const std::string& msg, const std::string& file="", int line=-1) : std::runtime_error(NB::formatDebugString(msg, file, line)), error(true) {} VAOError(bool isError=true) : std::runtime_error(""), error(isError) {} }; template class BufferManager { public: const GLenum BufferType; BufferManager( bool keep_local=true, GLenum usage=GL_STATIC_DRAW ) : BufferType(BT), _keep_local(keep_local), _usage(usage), _size(0) { static_assert( !(BT==GL_ARRAY_BUFFER) || std::is_same::value, "'GL_ARRAY_BUFFER' buffer manager must have type of 'unsigned char'." ); static_assert( !(BT==GL_ELEMENT_ARRAY_BUFFER) || std::is_integral::value, "'GL_ELEMENT_ARRAY_BUFFER' buffer manager must be integral type." ); glGenBuffers(1, &_id); } BufferManager( const std::vector& data, bool keep_local=true, GLenum usage=GL_STATIC_DRAW ) : BufferManager(keep_local, usage) { setData(data); } BufferManager( unsigned int size, bool keep_local=true, GLenum usage=GL_STATIC_DRAW ) : BufferManager(std::vector(size), keep_local, usage) {} ~BufferManager() { glDeleteBuffers(1, &_id); } GLenum usageType() const { return _usage; } unsigned int id() const { return _id; } GLsizei capacity() const { return _size; } bool isInitialized() const { return glIsBuffer(_id); } bool keepLocal() const { return _keep_local; } std::vector getDataDirect() const { std::vector ret(_size); bind(); glGetBufferSubData(BT, 0, sizeof(T)*_size, ret.data()); return ret; } std::vector getData() const { if(_keep_local) { return _data; } else { return getDataDirect(); } } GLuint bind() const { glBindBuffer(BT, _id); return _id; } void unbind() const { glBindBuffer(BT, 0); } GLenum usageType(GLenum usage) { _usage=usage; return usageType(); } bool keepLocal(bool keep_local) { if(keep_local && !_keep_local) { _data = getDataDirect(); } if (!keep_local && _keep_local && _data.size() != 0) { _data.erase(_data.begin(), _data.end()); } _keep_local = keep_local; return _keep_local; } void setData(const std::vector& set_data) { if (!glfwGetCurrentContext()) { THROW_BUFFER_ERROR("No OpenGL context."); } _size = set_data.size(); bind(); glBufferData(BT, _size*sizeof(T), set_data.data(), _usage); if (_keep_local) { _data = set_data; } } unsigned int setSubData(const std::vector& newData, unsigned int offset=0) { if (newData.size()+offset > _size) { THROW_BUFFER_ERROR( "Attempting to overflow buffer of capacity " + std::to_string(_size) + " with data type size " + std::to_string(sizeof(T)) + "."); } bind(); glBufferSubData(BT, offset*sizeof(T), sizeof(T)*newData.size(), newData.data()); if(_keep_local) { memcpy(_data.data()+offset*sizeof(T), newData.data(), sizeof(T)*newData.size()); } return _size; } unsigned int resize(unsigned int n) { std::vector new_vector = getData(); new_vector.resize(n); setData(new_vector); return n; } private: GLenum _usage; GLuint _id = 0; std::vector _data; unsigned int _size; bool _keep_local; }; template class VAOManager { public: VAOManager() : _elmt_buffer(nullptr){ glGenVertexArrays(1, &_id); } VAOManager( std::vector*> vert_bufs, BufferManager* elmt_buf = nullptr, const VertexAttributeList& vert_attrs = {} ) : VAOManager() { _elmt_buffer = elmt_buf; for (BufferManager* vb : vert_bufs) { _vert_buffers[vb->id()] = vb; } if (vert_attrs.size()!=0) { generate(vert_attrs); } } VAOManager( BufferManager* vert_bufs, BufferManager* elmt_buf = nullptr, const VertexAttributeList& vert_attrs = {} ) : VAOManager(std::vector*>(1, vert_bufs), elmt_buf, vert_attrs) {} VertexAttributeList getLayout() const { return _vert_attrs; } GLuint id() const { return _id; } const VertexAttribute& operator[](unsigned int i) { return _vert_attrs[i]; } unsigned int vertSize(unsigned int i) const { // PROBLEM IS ALL VAPs POINT TO IBO rn unsigned int vert_size = 0; for (const VertexAttribute& va : _vert_attrs) { if (va.ptr.buffer == i) { vert_size += va.GLSLSize * GLSLTypeSize(va.GLSLType); } } return vert_size; } std::vector getVBOs() { std::vector ret; for(std::map*>::const_iterator it = _vert_buffers.begin(); it != _vert_buffers.end(); ++it) { ret.push_back(it->first); } return ret; } void bind() const { glBindVertexArray(_id); if (_elmt_buffer) { _elmt_buffer->bind(); } } void unbind() const { glBindVertexArray(0); if (_elmt_buffer) { _elmt_buffer->bind(); } } void addVBO(BufferManager* vert_buf) { GLuint vert_id = vert_buf->id(); if (_vert_buffers.find(vert_id) == _vert_buffers.end()) { _vert_buffers[vert_buf->id()] = vert_buf; } else { THROW_VAO_ERROR("Attempting to add identical VBO id of " + std::to_string(vert_id) + "."); } } void changeEBO(BufferManager* ebo) { _elmt_buffer = ebo; } VertexAttributeList addVertexAttributes(const VertexAttributeList& vert_attrs) { try { throw checkValid(vert_attrs); } catch (VAOError vaoe) { if (vaoe.error) { THROW_VAO_ERROR(vaoe.what()); } } unsigned int num_attrs_old = _vert_attrs.size(); unsigned int num_attrs_new = num_attrs_old+vert_attrs.size(); const VertexAttribute* va; bind(); for (int i = num_attrs_old; i < num_attrs_new; ++i) { va = &(vert_attrs.data()[i-num_attrs_old]); _vert_attrs.emplace_back(*va); _vert_buffers[va->ptr.buffer]->bind(); glVertexAttribPointer( i, va->GLSLSize, va->GLSLType, va->GLSLNormalization, va->ptr.stride, (void*)va->ptr.offset ); glVertexAttribDivisor(i, va->ptr.divisor); glEnableVertexAttribArray(i); glVertexAttribDivisor(i, va->ptr.divisor); } unbind(); return _vert_attrs; } VertexAttributeList addVertexAttributes(VertexAttribute vert_attr) { return addVertexAttributes({vert_attr}); } VertexAttributeList generate() { bind(); unsigned int num_attrs = _vert_attrs.size(); VertexAttribute* va; for (int i = 0; i < num_attrs; ++i) { va = &(_vert_attrs[i]); _vert_buffers[va->ptr.buffer]->bind(); glVertexAttribPointer( i, va->GLSLSize, va->GLSLType, va->GLSLNormalization, va->ptr.stride, (void*)va->ptr.offset ); glVertexAttribDivisor(i, va->ptr.divisor); glEnableVertexAttribArray(i); } if (_elmt_buffer != nullptr) { _elmt_buffer->bind(); } unbind(); return _vert_attrs; } VertexAttributeList generate(VertexAttributeList vert_attrs) { try { throw checkValid(vert_attrs); } catch (VAOError vaoe) { if (vaoe.error) { THROW_VAO_ERROR(vaoe.what()); } } _vert_attrs.swap(vert_attrs); return generate(); } VertexAttributeList changeLayout(unsigned int i, VertexAttributePointer vap) { _vert_attrs[i].ptr = vap; bind(); const VertexAttribute* va = &(_vert_attrs[i]); glDisableVertexAttribArray(i); _vert_buffers[va->ptr.buffer]->bind(); glVertexAttribPointer( i, va->GLSLSize, va->GLSLType, va->GLSLNormalization, va->ptr.stride, (void*)va->ptr.offset ); glVertexAttribDivisor(i, va->ptr.divisor); glEnableVertexAttribArray(i); unbind(); return _vert_attrs; } VAOError checkValid(const VertexAttributeList& vert_attrs) { GLuint va_id; unsigned int num_attrs = vert_attrs.size(); for (int i = 0; i < num_attrs; ++i) { va_id = vert_attrs[i].ptr.buffer; if (_vert_buffers.find(va_id) == _vert_buffers.cend()) { return VAOError("Attempting to point to unknown VBO of id " + std::to_string(va_id) + " at Vertex Attribute " + std::to_string(i) + "."); } if (vert_attrs[i].ptr.stride<0) { return VAOError("Invalid stride value at Vertex Attribute " + std::to_string(i) + "."); } } return VAOError(false); } private: GLuint _id; std::map*> _vert_buffers; BufferManager* _elmt_buffer; VertexAttributeList _vert_attrs; }; } #endif