#pragma once #ifndef _NB_BUFFER #define _NB_BUFFER #include #include #include #include #include #include "Shader.h" namespace NB{ class BufferWarning : public std::runtime_error { public: BufferWarning(const std::string& msg) : std::runtime_error(msg) {} }; 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); glBindBuffer(_buffer_type, _id); glGetBufferSubData(_buffer_type, 0, sizeof(T)*_size, ret.data()); glBindBuffer(_buffer_type, 0); return ret; } std::vector getData() const { if(_keep_local) { return _data; } else { return getDataDirect(); } } unsigned int bind() const { glBindBuffer(_buffer_type, _id); return _id; } bool isBound() const { return (getBound(_buffer_type) == _id); } 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) + "."); } glBindBuffer(_buffer_type, _id); 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()); } glBindBuffer(_buffer_type, 0); } void setData(const std::vector& set_data) { if (_keep_local) {_data = set_data;} if (!_initialized) { glGenBuffers(1, &_id); _initialized = true; } glBindBuffer(_buffer_type, _id); 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); } glBindBuffer(_buffer_type, 0); } unsigned int resize(unsigned int n) { if (_keep_local) { _data.resize(n); } if (n > _max_size) { _max_size = n; if (_initialized) { glBindBuffer(_buffer_type, _id); glBufferData(_buffer_type, sizeof(T)*_max_size, _data.data(), _usage); glBindBuffer(_buffer_type, 0); } } _size = n; return _size; } unsigned int shrink_to_fit() { if (_size < _max_size) { if (_keep_local) { _data.shrink_to_fit(); _size = _data.size(); glBindBuffer(_buffer_type, _id); glBufferData(_buffer_type, _size*sizeof(T), _data.data(),_usage); glBindBuffer(_buffer_type, 0); _max_size = _size; } else { _data = getDataDirect(); _size = _data.data(); _max_size = _size; } } return _size; } static unsigned int getBound(GLenum buffer) { unsigned int ret; glGetIntegerv(buffer, &ret); return ret; } private: const GLenum _buffer_type; GLenum _usage; std::vector _data; unsigned int _id, _size, _max_size; bool _initialized; bool _keep_local; }; } #endif