#pragma once #ifndef _NB_BUFFER #define _NB_BUFFER #include #include #include #include #include #include #include "Shader.h" static unsigned int __NB_GL_DEBUG_ERROR_CODE__; #define __NB_GL_DEBUG_THROW__(cmd, when) while(__NB_GL_DEBUG_ERROR_CODE__=glGetError()){\ std::cout << "[GL ERROR]: " << __NB_GL_DEBUG_ERROR_CODE__;\ std::cout << " " << when << " " << cmd << "\n";\ } #define __NB_GL_DEBUG__(cmd) __NB_GL_DEBUG_THROW__(#cmd, "before"); cmd; __NB_GL_DEBUG_THROW__(#cmd, "after"); namespace NB{ class BufferWarning : public std::runtime_error { public: BufferWarning(const std::string& msg) : std::runtime_error(msg) {} }; static unsigned int getBound(GLenum buffer) { int ret; __NB_GL_DEBUG__(glGetIntegerv(buffer, &ret)); return ret; } template class BufferManager { public: BufferManager( GLenum buffer_type, bool keep_local=true, GLenum usage=GL_STATIC_DRAW ) : _buffer_type(buffer_type), _initialized(false), _keep_local(keep_local), _usage(usage), _size(0), _max_size(0) {} BufferManager( GLenum buffer_type, const std::vector& data, bool keep_local=true, GLenum usage=GL_STATIC_DRAW ) : _buffer_type(buffer_type), _keep_local(keep_local), _usage(usage) { setData(data); } BufferManager( GLenum buffer_type, unsigned int size, bool keep_local=true, GLenum usage=GL_STATIC_DRAW ) : BufferManager(buffer_type, std::vector(size), keep_local, usage) {} GLenum bufferType() const { return _buffer_type; } GLenum usageType() const { return _usage; } unsigned int id() const { return _id; } unsigned int size() const { return _size; } unsigned int capacity() const { return _max_size; } bool isInitialized() const { return _initialized; } bool keepLocal() const { return _keep_local; } std::vector getDataDirect() const { std::vector ret(_size); bind(); glGetBufferSubData(_buffer_type, 0, sizeof(T)*_size, ret.data()); return ret; } std::vector getData() const { if(_keep_local) { return _data; } else { return getDataDirect(); } } unsigned int bind() const { glBindBuffer(_buffer_type, _id); return _id;} void unbind() const { glBindBuffer(_buffer_type, 0); } GLenum usageType(GLenum usage) { _usage=usage; return usageType(); } bool keepLocal(bool keep_local) { if(keep_local && !_keep_local) { _data = getDataDirect(); } _keep_local = keep_local; return _keep_local; } bool initialize(unsigned int n=1) { if (!_initialized) { setData(std::vector(n)); } return _initialized; } void loadData(const std::vector& newData, unsigned int offset=0) { if ( newData.size()+offset > _max_size ) { throw BufferWarning("Attempting to overflow buffer of capacity " + std::to_string(_max_size) + "."); } bind(); glBufferSubData(_buffer_type, offset*sizeof(T), sizeof(T)*newData.size(), newData.data()); if(_keep_local) { memcpy(_data.data()+offset*sizeof(T), newData.data(), sizeof(T)*newData.size()); } } void setData(const std::vector& set_data) { if (_keep_local) {_data = set_data;} if (!_initialized) { glGenBuffers(1, &_id); _initialized = true; } bind(); if (set_data.size() <= _max_size) { glBufferSubData(_buffer_type, 0, sizeof(T)*_size, _data.data()); _size = _data.size(); } else { _size = _data.size(); _max_size = _size; glBufferData(_buffer_type, sizeof(T)*_max_size, _data.data(), _usage); } } unsigned int resize(unsigned int n) { if (_keep_local) { _data.resize(n); } if (n > _max_size) { _max_size = n; if (_initialized) { bind(); glBufferData(_buffer_type, sizeof(T)*_max_size, _data.data(), _usage); } } _size = n; return _size; } unsigned int shrink_to_fit() { if (_size < _max_size) { if (_keep_local) { _data.shrink_to_fit(); _size = _data.size(); bind(); glBufferData(_buffer_type, _size*sizeof(T), _data.data(),_usage); _max_size = _size; } else { _data = getDataDirect(); _size = _data.data(); _max_size = _size; } } return _size; } private: const GLenum _buffer_type; GLenum _usage; std::vector _data; unsigned int _id, _size, _max_size; bool _initialized; bool _keep_local; }; } #endif