From 81bffb7511f0c08d878e159b28cb47b4a2c5d2f3 Mon Sep 17 00:00:00 2001 From: NaifBanana <30419422+NaifBanana@users.noreply.github.com> Date: Mon, 8 Jun 2026 03:20:51 -0500 Subject: [PATCH] Large OpenGL Object handling overhaul --- engine/NBGraphics/Buffers.hpp | 463 ++++++++++--------------- engine/NBGraphics/CMakeLists.txt | 20 +- engine/NBGraphics/Camera.hpp | 5 +- engine/NBGraphics/GLLoad.hpp | 1 - engine/NBGraphics/OGLObjects.hpp | 45 +++ engine/NBGraphics/Textures.hpp | 61 ++++ engine/NBGraphics/VAOManager.hpp | 75 ---- engine/NBGraphics/VertexArray.hpp | 176 ++++++++++ engine/NBGraphics/Window.hpp | 3 +- engine/NBGraphics/src/Buffers.cpp | 16 + engine/NBGraphics/src/Camera.cpp | 5 +- engine/NBGraphics/src/OGLObjects.cpp | 11 + engine/NBGraphics/src/Textures.cpp | 10 + engine/NBGraphics/src/VAOManager.cpp | 225 ------------ engine/NBGraphics/src/VertexArray.cpp | 222 ++++++++++++ engine/NBGraphics/tests/CMakeLists.txt | 16 + engine/NBGraphics/tests/TestWindow.cpp | 6 + 17 files changed, 772 insertions(+), 588 deletions(-) create mode 100644 engine/NBGraphics/OGLObjects.hpp create mode 100644 engine/NBGraphics/Textures.hpp delete mode 100644 engine/NBGraphics/VAOManager.hpp create mode 100644 engine/NBGraphics/VertexArray.hpp create mode 100644 engine/NBGraphics/src/OGLObjects.cpp create mode 100644 engine/NBGraphics/src/Textures.cpp delete mode 100644 engine/NBGraphics/src/VAOManager.cpp create mode 100644 engine/NBGraphics/src/VertexArray.cpp create mode 100644 engine/NBGraphics/tests/CMakeLists.txt create mode 100644 engine/NBGraphics/tests/TestWindow.cpp diff --git a/engine/NBGraphics/Buffers.hpp b/engine/NBGraphics/Buffers.hpp index 58e8c42..4df38f3 100644 --- a/engine/NBGraphics/Buffers.hpp +++ b/engine/NBGraphics/Buffers.hpp @@ -4,15 +4,17 @@ #include "GLLoad.hpp" +#include #include #include #include -#include +#include +#include + +#include "OGLObjects.hpp" namespace nb { - -typedef std::vector RawVec; static uint8_t GLSLTypeSize(GLenum type) { switch(type) { @@ -35,51 +37,30 @@ static uint8_t GLSLTypeSize(GLenum type) { } } -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) { +ByteVector vectorToRaw(const std::vector& vec) { unsigned int num_bytes = vec.size() * sizeof(T); - RawVec ret(num_bytes); + ByteVector 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); +ByteVector concatVectorsToRaw(const std::vector& vec1, const std::vector& vec2) { + ByteVector vec1_raw = vectorToRaw(vec1); + ByteVector 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); + ByteVector 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) { +std::vector rawToVector(const ByteVector& vec) { if (vec.size() % sizeof(T) != 0) { - throw std::runtime_error("Data size does not align to std::vector<" + std::string(typeid(T).name()) + ">."); + 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); @@ -88,278 +69,206 @@ std::vector rawToVector(const RawVec& vec) { 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(); } +class BufferError : public Error { + using Base = Error; - 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; + public: using Base::Base; + + enum Codes : unsigned int { + UNDEFINED, DATA_OVERFLOW, INVALID_BUFFER, HANDLE_OVERWRITE + }; - VAO() { glGenVertexArrays(1, &_id); } - - void bind() const { glBindVertexArray(_id); } - void unbind() const { glBindVertexArray(0); } - - -protected: - using Base::_id; - const - - void remove() { glDeleteVertexArrays(1, &_id); } - + static const std::string type; + static const ErrorCodeMap ErrorMessages; }; template -class Buffer : public virtual OpenGLObject> { -public: - using Base = OpenGLObject; - using Base::Base; +class Buffer : public OpenGLObject { + using Codes = BufferError::Codes; - Buffer(GLenum usage_ = GL_STATIC_DRAW) : usage(usage_) { glGenBuffers(1, &_id); } + public: + using OpenGLObject::OpenGLObject; + + Buffer() = default; + Buffer(Buffer&& other) { + *this = std::move(other); + } + Buffer& operator=(Buffer&& rhs) { + if (_id) { THROW(BufferError(Codes::HANDLE_OVERWRITE)); } + _id = rhs._id; + _usage = rhs._usage; + _size = rhs._size; - 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); } + rhs._id = 0; + rhs._usage = GL_STATIC_DRAW; + rhs._size = 0; + return *this; + } + virtual void bind() const override { + if (_id) { + glBindBuffer(Target, _id); + } + THROW(BufferError(Codes::INVALID_BUFFER)); + } + virtual void unbind() const override { glBindBuffer(Target, 0); } + virtual GLenum usage() const { return _usage; } + virtual GLenum usage(GLenum usage_) { + _usage = usage_; + data(data(), _usage); + return _usage; + } + virtual size_t size() const { return _size; } + virtual ByteVector data() const { + bind(); + ByteVector ret(_size); + glGetBufferSubData(Target, 0, _size, ret.data()); + return ret; + } + virtual void data( + const void* data_, + GLsizei size_, + GLenum usage_=GL_STATIC_DRAW + ) { + declare(); + bind(); + glBufferData(Target, size_, data_, usage_); + _size = size_; + _usage = usage_; + } + virtual void data(const ByteVector& data_, GLenum usage_=GL_STATIC_DRAW) { + data(Target, data_.size(), data_.data(), usage_); - const GLenum GLTarget = BufferType::GLTarget; - GLenum usage; + } + virtual void subdata(void* data_, GLsizeiptr size_, GLintptr offset_=0) { + bind(); + if (offset_+size_ <= _size) { + THROW(BufferError(BufferError::Codes::DATA_OVERFLOW)); + } + glBufferSubData(Target, offset_, size_, data_); + } + virtual void subdata(ByteVector& data_, GLintptr offset_=0) { + bind(); + size_t size_ = data_.size(); + if (offset_+size_ <= _size) { + THROW(BufferError(BufferError::Codes::DATA_OVERFLOW)); + } + glBufferSubData(Target, offset_, data_.size(), data_.data()); + } + // virtual void clear() const { glClearBufferData(Target, ) } + static const GLenum Target; + + protected: + using OpenGLObject::_id; + GLenum _usage = GL_STATIC_DRAW; + size_t _size; + virtual GLuint declare() override { + if (!_id) { + glGenBuffers(1, &_id); + } + return _id; + } + virtual void remove() override { + if (_id) { + glDeleteBuffers(1, &_id); + _id = 0; + } + } + +}; + +template +const GLenum Buffer::Target = BufferType::Target; + +template +class ImmutableBuffer : public virtual Buffer { + public: + using Base = Buffer; + using Base::Target; + using Base::bind; + + 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: using Base::_id; - void remove() { glDeleteBuffers(1, &_id); } + std::shared_ptr _map = nullptr; + GLbitfield _mapAccess = 0; }; -class VertexBuffer : public virtual Buffer { -public: +template +class ArrayBuffer : public Buffer> { + using Base = Buffer>; + using BufferType = Base; - static const GLenum GLTarget = GL_ARRAY_BUFFER; -protected: + public: + using Base::Base; + using Base::data; + ArrayBuffer(const ByteVector&, GLenum usage_=GL_STATIC_DRAW); + + static const GLenum Target = GL_ARRAY_BUFFER; + + protected: + using Base::_usage; +}; + +template <> +class ArrayBuffer +: public virtual ArrayBuffer, public virtual ImmutableBuffer> { + using Base = ArrayBuffer; + using BufferType = ImmutableBuffer>; + using BufferType::BufferType; + + public: + using Base::Target; }; -class ElementBuffer : public virtual Buffer { -public: +template +class ElementBuffer : public virtual Buffer> { + using Base = Buffer>; + using BufferType = Base; + + public: + using Base::Base; + static const GLenum Target = GL_ELEMENT_ARRAY_BUFFER; - static const GLenum GLTarget = GL_ELEMENT_ARRAY_BUFFER; - -protected: + 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) {} +template <> +class ElementBuffer +: public virtual ElementBuffer, public virtual ImmutableBuffer> { + using Base = ElementBuffer; + using BufferType = ImmutableBuffer>; - 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}; + public: + using BufferType::BufferType; + using Base::Target; }; -*/ - } // namespace NB #endif // _NB_BUFFER diff --git a/engine/NBGraphics/CMakeLists.txt b/engine/NBGraphics/CMakeLists.txt index 6a22f25..f799880 100644 --- a/engine/NBGraphics/CMakeLists.txt +++ b/engine/NBGraphics/CMakeLists.txt @@ -1,5 +1,3 @@ -include_directories(./.) - find_package(OpenGL) add_subdirectory(${GLFW_PATH} ${GLFW_PATH}/build) @@ -10,8 +8,11 @@ set(GLFW_BUILD_TESTS OFF CACHE BOOL "" FORCE) set(GLFW_BUILD_EXAMPLES OFF CACHE BOOL "" FORCE) toAbsolutePath(NB_GRAPHICS_SOURCE - ./src/Window.cpp ./src/Buffers.cpp + ./src/OGLObjects.cpp + ./src/Textures.cpp + ./src/VertexArray.cpp + ./src/Window.cpp ) toAbsolutePath(NB_GRAPHICS_INCLUDE @@ -19,8 +20,10 @@ toAbsolutePath(NB_GRAPHICS_INCLUDE ./Camera.hpp ./Draw.hpp ./GLLoad.hpp + ./OGLObjects.hpp ./shader.hpp - ./VAOManager.hpp + ./Textures.hpp + ./VertexArray.hpp ./Window.hpp ) @@ -31,4 +34,11 @@ add_library(NBGraphics ${NB_GRAPHICS_SOURCE} ${GLAD_PATH}/src/glad.c ) -target_link_libraries(NBGraphics glfw3) +target_link_libraries(NBGraphics glfw) + +target_include_directories(NBGraphics PUBLIC ./.) + +if (NB_BUILD_TESTS) + add_subdirectory(./tests) +endif() + diff --git a/engine/NBGraphics/Camera.hpp b/engine/NBGraphics/Camera.hpp index 35e109d..8e31c8c 100644 --- a/engine/NBGraphics/Camera.hpp +++ b/engine/NBGraphics/Camera.hpp @@ -2,13 +2,14 @@ #ifndef _NB_CAMERA #define _NB_CAMERA -#include +#include "GLLoad.hpp" + #include #include #include #include -namespace NB { +namespace nb { class Camera { public: diff --git a/engine/NBGraphics/GLLoad.hpp b/engine/NBGraphics/GLLoad.hpp index 5936ed9..7989fb3 100644 --- a/engine/NBGraphics/GLLoad.hpp +++ b/engine/NBGraphics/GLLoad.hpp @@ -5,7 +5,6 @@ #include #include -#include #include namespace NB { diff --git a/engine/NBGraphics/OGLObjects.hpp b/engine/NBGraphics/OGLObjects.hpp new file mode 100644 index 0000000..64cd302 --- /dev/null +++ b/engine/NBGraphics/OGLObjects.hpp @@ -0,0 +1,45 @@ +#pragma once +#ifndef _NB_OGL_OBJECTS +#define _NB_OGL_OBJECTS + +#include "GLLoad.hpp" + +#include + +namespace nb { + +class OGLError : public Error { + using Base = Error; + + public: + using Base::Base; + + enum Codes : unsigned int { + UNDEFINED, HANGING_OBJECT + }; + + static const std::string type; + static const ErrorCodeMap ErrorMessages; +}; + +class OpenGLObject { + public: + OpenGLObject(const OpenGLObject&) = delete; + OpenGLObject& operator=(const OpenGLObject&) = delete; + ~OpenGLObject() { remove(); } + + virtual void bind() const = 0; + virtual void unbind() const = 0; + GLuint id() const { return _id; } + + protected: + OpenGLObject() = default; + + virtual GLuint declare() = 0; + virtual void remove() { this->remove(); }; + + GLuint _id; +}; + +} // namespace nb +#endif // _NB_OGL_OBJECTS \ No newline at end of file diff --git a/engine/NBGraphics/Textures.hpp b/engine/NBGraphics/Textures.hpp new file mode 100644 index 0000000..8984e72 --- /dev/null +++ b/engine/NBGraphics/Textures.hpp @@ -0,0 +1,61 @@ +#pragma once +#ifndef _NB_TEXTURES +#define _NB_TEXTURES + +#include "Buffers.hpp" +#include "OGLObjects.hpp" + +namespace nb { + +template +class TextureBuffer : public virtual Buffer> { + using Base = Buffer>; + using BufferType = Base; + + public: + using Base::Base; + static const GLenum Target = GL_TEXTURE_BUFFER; + + protected: + +}; + +template <> +class TextureBuffer +: public virtual TextureBuffer, public virtual ImmutableBuffer> { + using Base = TextureBuffer; + using BufferType = ImmutableBuffer>; + + public: + using BufferType::BufferType; + using Base::Target; +}; + +class Texture : public OpenGLObject { + using Base = OpenGLObject; +public: + using Base::Base; + using Base::id; + + Texture(GLenum); + + virtual void bind() const override { glBindTexture(target, _id); } + virtual void unbind() const override { glBindTexture(target, 0); } + + const GLenum target; + + friend Base; + + protected: + using Base::_id; + + virtual void remove() override { + if (_id) { + glDeleteTextures(0, &_id); + } + } + +}; + +} // namespace nb +#endif // _NB_TEXTURES \ No newline at end of file diff --git a/engine/NBGraphics/VAOManager.hpp b/engine/NBGraphics/VAOManager.hpp deleted file mode 100644 index 142882c..0000000 --- a/engine/NBGraphics/VAOManager.hpp +++ /dev/null @@ -1,75 +0,0 @@ -#pragma once -#ifndef _NB_VAO_MANAGER -#define _NB_VAO_MANAGER - -#include - -#include -#include -#include - -#include "Buffers.h" - -#define THROW_VAO_ERROR(msg) throw VAOError(msg, __FILE__, __LINE__); - -namespace NB { - -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) {} -}; - -class VAOManager : public OpenGLObject { -public: - typedef std::shared_ptr BufferManagerPointer; - //typedef BufferManager BufferManagerPointer; - - VAOManager(); - VAOManager( - std::vector, - BufferManagerPointer elmt_buf = nullptr, - const VertexAttributeList& vert_attrs = {} - ); - VAOManager( - BufferManagerPointer vert_bufs, - BufferManagerPointer elmt_buf = nullptr, - const VertexAttributeList& vert_attrs = {} - ) : VAOManager(std::vector(1, vert_bufs), elmt_buf, vert_attrs) {} - - VAOManager(VAOManager&& rhs); - VAOManager& operator=(VAOManager&& rhs); - - VertexAttributeList getLayout() const; - GLuint id() const; - unsigned int vertSize(GLuint) const; - std::vector getVertexBuffers() const; - bool isInitialized() const override; - RawVec attributeData(unsigned int); - - RawVec attributeData(unsigned int, const RawVec&); - void bind() const; - void unbind() const; - void addVBO(BufferManagerPointer); - void changeEBO(BufferManagerPointer); - VertexAttributeList addVertexAttributes(const VertexAttributeList&); - VertexAttributeList addVertexAttributes(VertexAttribute); - VertexAttributeList generate(); - VertexAttributeList generate(VertexAttributeList); - VertexAttributeList changeLayout(unsigned int, VertexAttributePointer); - - VAOError checkValid(const VertexAttributeList&); - -private: - void remove() const override; - - using OpenGLObject::_id; - std::map _vert_buffers; - BufferManagerPointer _elmt_buffer = nullptr; - VertexAttributeList _vert_attrs; -}; - -} // namespace NB -#endif // _NB_VAO_MANAGER \ No newline at end of file diff --git a/engine/NBGraphics/VertexArray.hpp b/engine/NBGraphics/VertexArray.hpp new file mode 100644 index 0000000..30afe3a --- /dev/null +++ b/engine/NBGraphics/VertexArray.hpp @@ -0,0 +1,176 @@ +#pragma once +#ifndef _NB_VERTEX_ARRAY +#define _NB_VERTEX_ARRAY + +#include "GLLoad.hpp" + +#include + +#include "Buffers.hpp" +#include "OGLObjects.hpp" + + +namespace nb { + +struct VertexAttributeLayout { + int32_t offset = 0; + GLsizei stride = 0; + GLuint divisor = 0; +}; + +struct VertexAttribute { + GLint GLSLSize; + GLenum GLSLType; + GLboolean GLSLNormalization; + VertexAttributeLayout layout; +}; + +struct VertexAttributePointer; + +static bool isIndexInVertexAttribute(int i, const VertexAttribute& va) { + if ((i -= va.layout.offset) < 0) { return false; } + return (i%va.layout.stride) < GLSLTypeSize(va.GLSLType) * va.GLSLSize; +} + +typedef std::vector VertexAttributeList; +typedef std::vector VertexAttributePointerList; + +class VAOError : public Error { + using Base = Error; + + public: + using Base::Base; + + enum Codes : unsigned int { + UNDEFINED, INVALID_ATTRIBUTE, INVALID_VAO + }; + + static const std::string type; + static const ErrorCodeMap ErrorMessages; +}; + +template +class VertexBuffer : public ArrayBuffer{ + using Base = ArrayBuffer; + + public: + template + VertexBuffer(const VertexAttributeList& vas, Args... args) + : attributes(vas) { + if (sizeof...(Args)) { + data(args...); + } + } + + using Base::Target; + const VertexAttributeList attributes; + + protected: + using Base::_usage; + +}; + +template <> +class VertexBuffer +: public virtual VertexBuffer, public virtual ImmutableBuffer> { + using Base = ArrayBuffer; + using BufferType = ImmutableBuffer>; + using BufferType::BufferType; + + public: + using Base::Target; + +}; + +struct VertexAttributePointer { + VertexAttribute attribute; + VertexBuffer* buffer; + GLuint index; +}; + +class VAO : public OpenGLObject { + using Base = OpenGLObject; + using Codes = VAOError::Codes; + + public: + using Base::Base; + VAO() = default; + VAO(const VertexAttributePointerList&); + VAO(VAO&&); + + VAO& operator=(VAO&&); + + virtual void bind() const override { + if(_id) { + glBindVertexArray(_id); + } else { + THROW(Codes::INVALID_VAO); + } + } + virtual void unbind() const override { glBindVertexArray(0); } + + VertexAttributePointerList attributes() const; + VertexAttributePointerList attributes(const VertexAttributePointerList&); + VertexAttributePointer attr(GLuint) const; + void enable() const; + void enable(GLuint) const; + void disable() const; + void disable(GLuint) const; + + protected: + using Base::_id; + VertexAttributePointerList _attrs; + + virtual void remove() override { + if(_id) { + disable(); + bind(); + glDeleteVertexArrays(1, &_id); + _id = 0; + } + } + virtual GLuint declare() override { + if (!_id) { + glGenVertexArrays(1, &_id); + } + return _id; + } + +}; + +class VertexGroup { + public: + VertexGroup(VertexGroup&&); + VertexGroup& operator=(VertexGroup&&); + + void bind() const { + _elmt_buf.bind(); + _vao.bind(); + } + void unbind() const { + _vao.bind(); + _elmt_buf.unbind(); + } + + VertexBuffer& buffer(size_t); + size_t buffer(VertexBuffer&&); + std::vector>> buffers(); + size_t buffers(std::vector>&&); + VertexBuffer&& dropBuffer(size_t); + + VertexAttributePointer attribute(size_t) const; + void enableAttr(GLuint); + void disableAttr(GLuint); + void enableBuffer(size_t); + void disableBuffer(size_t); + void enable(); + void disable(); + + protected: + std::vector>> _vertex_data; + ElementBuffer _elmt_buf; + VAO _vao; +}; + +} // namespace nb +#endif // _NB_VERTEX_ARRAY \ No newline at end of file diff --git a/engine/NBGraphics/Window.hpp b/engine/NBGraphics/Window.hpp index 22a82f8..88e84df 100644 --- a/engine/NBGraphics/Window.hpp +++ b/engine/NBGraphics/Window.hpp @@ -4,12 +4,13 @@ #include "GLLoad.hpp" -#include #include #include #include #include +#include + namespace nb { class GLError : public std::runtime_error { diff --git a/engine/NBGraphics/src/Buffers.cpp b/engine/NBGraphics/src/Buffers.cpp index 24f9115..3f80a45 100644 --- a/engine/NBGraphics/src/Buffers.cpp +++ b/engine/NBGraphics/src/Buffers.cpp @@ -2,4 +2,20 @@ namespace nb { +using BufferErrorCodes = BufferError::Codes; +const std::string BufferError::type = "nb::BufferError"; +const ErrorCodeMap BufferError::ErrorMessages = { + { BufferErrorCodes::UNDEFINED, "Error" }, + { BufferErrorCodes::DATA_OVERFLOW, "Attempting buffer overflow" }, + { BufferErrorCodes::INVALID_BUFFER, "Attempting operation on invalid buffer" }, + {BufferErrorCodes::HANDLE_OVERWRITE, "Attempting to overwrite active buffer"} + +}; + +template<> +ArrayBuffer::ArrayBuffer(const ByteVector& data_, GLenum usage_) +: BufferType() { + data(data_, usage_); +} + } \ No newline at end of file diff --git a/engine/NBGraphics/src/Camera.cpp b/engine/NBGraphics/src/Camera.cpp index 8431597..e7aa711 100644 --- a/engine/NBGraphics/src/Camera.cpp +++ b/engine/NBGraphics/src/Camera.cpp @@ -1,5 +1,6 @@ -#include "Camera.h" -namespace NB { +#include "Camera.hpp" + +namespace nb { // Camera class Camera::Camera(const Vec3& pos, const Vec3& tar, const Vec3& up) { diff --git a/engine/NBGraphics/src/OGLObjects.cpp b/engine/NBGraphics/src/OGLObjects.cpp new file mode 100644 index 0000000..d66a2dc --- /dev/null +++ b/engine/NBGraphics/src/OGLObjects.cpp @@ -0,0 +1,11 @@ +#include "OGLObjects.hpp" + +namespace nb { + +const std::string OGLError::type = "nb::OGLError"; +const ErrorCodeMap OGLError::ErrorMessages = { + { OGLError::Codes::UNDEFINED, "Error" }, + {OGLError::Codes::HANGING_OBJECT, "Attempting to leave a hanging OpenGL object"} +}; + +} // namespace nb \ No newline at end of file diff --git a/engine/NBGraphics/src/Textures.cpp b/engine/NBGraphics/src/Textures.cpp new file mode 100644 index 0000000..9b448c7 --- /dev/null +++ b/engine/NBGraphics/src/Textures.cpp @@ -0,0 +1,10 @@ +#include "Textures.hpp" + +namespace nb{ + +Texture::Texture(GLenum target_) : target(target_) { + glGenTextures(1, &_id); +} + + +} // namespace nb \ No newline at end of file diff --git a/engine/NBGraphics/src/VAOManager.cpp b/engine/NBGraphics/src/VAOManager.cpp deleted file mode 100644 index c9ea7d5..0000000 --- a/engine/NBGraphics/src/VAOManager.cpp +++ /dev/null @@ -1,225 +0,0 @@ -#include "VAOManager.hpp" - -namespace NB { - -using BufferManagerPointer = std::shared_ptr; - -VAOManager::VAOManager() { glGenVertexArrays(1, &_id); } - -VAOManager::VAOManager( - std::vector vert_bufs, - BufferManagerPointer elmt_buf, - const VertexAttributeList& vert_attrs -) : VAOManager() { - _elmt_buffer = elmt_buf; - for (BufferManagerPointer vb : vert_bufs) { - _vert_buffers[vb->id()] = vb; - } - if (vert_attrs.size()!=0) { - generate(vert_attrs); - } -} - -VAOManager::VAOManager(VAOManager&& rhs) { *this = std::move(rhs); } - -VAOManager& VAOManager::operator=(VAOManager&& rhs) { - remove(); - _vert_buffers = rhs._vert_buffers; - _elmt_buffer = rhs._elmt_buffer; - _vert_attrs = rhs._vert_attrs; - return *this; -} - -VertexAttributeList VAOManager::getLayout() const { return _vert_attrs; } - -GLuint VAOManager::id() const { return _id; } - -bool VAOManager::isInitialized() const { - return _id && glIsVertexArray(_id); -} - -void VAOManager::remove() const { - bind(); - for (int i{0}; i < _vert_attrs.size(); ++i) { - glDisableVertexAttribArray(i); - } - unbind(); - glDisableVertexAttribArray(_id); -} - -unsigned int VAOManager::vertSize(GLuint id) const { - unsigned int size = 0; - for (const VertexAttribute& va : _vert_attrs) { - if (va.ptr.buffer == id) { - size += va.GLSLSize * GLSLTypeSize(va.GLSLType); - } - } - return size; -} - -std::vector VAOManager::getVertexBuffers() const { - std::vector ret; - for (const auto& i : _vert_buffers) { - ret.emplace_back(i.second); - } - return ret; -} - -RawVec VAOManager::attributeData(unsigned int i) { - if (i <= _vert_attrs.size()) { - throw std::out_of_range("No vertex attribute exists for specified index"); - } - VertexAttribute attr = _vert_attrs[i]; - BufferManagerPointer buffer = _vert_buffers[attr.ptr.buffer]; - RawVec data = buffer->data(); - unsigned int attr_size = attr.GLSLSize*GLSLTypeSize(attr.GLSLType); - RawVec ret((buffer->size()-attr.ptr.offset-attr_size)*attr_size/attr.ptr.stride); - int data_size = data.size(); - for (int i{0}; i < data_size; ++i) { - if (isIndexInVertexAttribute(i, attr)) { - ret[i] = data[i]; - } - } - return ret; -} - -RawVec VAOManager::attributeData(unsigned int i, const RawVec& new_data) { - if (i >= _vert_attrs.size()) { - throw std::out_of_range("No vertex attribute exists for specified index."); - } - VertexAttribute attr = _vert_attrs[i]; - BufferManagerPointer buffer = _vert_buffers[attr.ptr.buffer]; - unsigned int attr_size = attr.GLSLSize*GLSLTypeSize(attr.GLSLType); - unsigned int offset = attr.ptr.offset; - unsigned int num_verts = buffer->size() / vertSize(buffer->id()); - if (num_verts*attr_size != new_data.size()) { - // WHY HERE???? - THROW_VAO_ERROR( - "Input data size of " + std::to_string(new_data.size()) - + " does not match data size for requested vertex attribute of total size " + std::to_string(num_verts*attr_size) - + "." - ); - } - uint64_t pos = uint64_t(new_data.data()); - for (int i{0}; idata((void*)(pos+i*attr_size), offset+i*attr.ptr.stride, attr_size); - } - return RawVec(new_data); -} - -void VAOManager::bind() const { - glBindVertexArray(_id); - if (_elmt_buffer) { _elmt_buffer->bind(); } -} - -void VAOManager::unbind() const { - glBindVertexArray(0); - if (_elmt_buffer) { _elmt_buffer->unbind(); } -} - -void VAOManager::addVBO(BufferManagerPointer 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 VAOManager::changeEBO(BufferManagerPointer ebo) { - _elmt_buffer = ebo; -} - -VertexAttributeList VAOManager::addVertexAttributes(const VertexAttributeList& vert_attrs) { - VertexAttributeList curr_vas = _vert_attrs; - curr_vas.insert(curr_vas.end(), vert_attrs.begin(), vert_attrs.end()); - try { - throw checkValid(curr_vas); - } catch (VAOError vaoe) { - if (vaoe.error) { - THROW_VAO_ERROR(vaoe.what()); - } - } - _vert_attrs = curr_vas; - generate(); - return _vert_attrs; -} - -VertexAttributeList VAOManager::addVertexAttributes(VertexAttribute vert_attr) { - return addVertexAttributes({vert_attr}); -} - -VertexAttributeList VAOManager::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 VAOManager::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 VAOManager::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( - _id, - 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 VAOManager::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); -} - -} // namespace NB diff --git a/engine/NBGraphics/src/VertexArray.cpp b/engine/NBGraphics/src/VertexArray.cpp new file mode 100644 index 0000000..01d3c63 --- /dev/null +++ b/engine/NBGraphics/src/VertexArray.cpp @@ -0,0 +1,222 @@ +#include "VertexArray.hpp" + +namespace nb { + +using VAOErrorCodes = VAOError::Codes; +const std::string VAOError::type = "nb::VAOError"; +const ErrorCodeMap VAOError::ErrorMessages = { + { VAOErrorCodes::UNDEFINED, "Error" }, + { VAOErrorCodes::INVALID_ATTRIBUTE, "Targeting invalid attribute"}, + { VAOErrorCodes::INVALID_VAO, "Targeting invalid VAO" } +}; + +VAO::VAO(const VertexAttributePointerList& attr_ptrs) { + attributes(attr_ptrs); +} + +VAO::VAO(VAO&& other) { + *this = std::move(other); +} + +VAO& VAO::operator=(VAO&& rhs) { + _id = rhs._id; + _attrs = rhs._attrs; + + rhs._id = 0; + rhs._attrs = {}; + + return *this; +} + +VertexAttributePointerList VAO::attributes() const { + return _attrs; +} + +VertexAttributePointerList VAO::attributes(const VertexAttributePointerList& attrs_) { + declare(); + disable(); + _attrs = attrs_; + bind(); + for (auto attr_ptr : attrs_) { + attr_ptr.buffer->bind(); + GLuint idx = attr_ptr.index; + glVertexAttribPointer( + idx, + attr_ptr.attribute.GLSLSize, + attr_ptr.attribute.GLSLType, + attr_ptr.attribute.GLSLNormalization, + attr_ptr.attribute.layout.stride, + (void*)attr_ptr.attribute.layout.offset + ); + glEnableVertexAttribArray(idx); + } + unbind(); + return _attrs; +} + +VertexAttributePointer VAO::attr(GLuint idx) const { + for (auto attr_ptr : _attrs) { + if (idx == attr_ptr.index) { + return attr_ptr; + } + } + THROW(BufferError(Codes::INVALID_ATTRIBUTE)); +} + +void VAO::enable() const { + if (_id) { + bind(); + for(auto attr_ptr : _attrs) { + glEnableVertexAttribArray(attr_ptr.index); + } + unbind(); + } +} + +void VAO::enable(GLuint idx) const { + if(_id) { + bind(); + glEnableVertexAttribArray(attr(idx).index); + unbind(); + } +} + +void VAO::disable() const { + if(_id) { + bind(); + for(auto attr_ptr : _attrs) { + glDisableVertexAttribArray(attr_ptr.index); + } + unbind(); + } +} + +void VAO::disable(GLuint idx) const { + if(_id) { + bind(); + glDisableVertexAttribArray(attr(idx).index); + unbind(); + } +} + +VertexGroup::VertexGroup(VertexGroup&& other) { + *this = std::move(other); +} + +VertexGroup& VertexGroup::operator=(VertexGroup&& rhs) { + _vertex_data = rhs._vertex_data; + _elmt_buf = std::move(rhs._elmt_buf); + _vao = std::move(rhs._vao); + + rhs._vertex_data = {}; + rhs._elmt_buf = ElementBuffer(); + rhs._vao = VAO(); + + return *this; +} + +size_t VertexGroup::buffer(VertexBuffer&& buf_) { + _vertex_data.emplace_back( + std::make_shared>(std::move(buf_)) + ); + VertexBuffer* buf_ptr = _vertex_data.back().get(); + VertexAttributePointerList attrs = _vao.attributes(); + GLuint idx = attrs.size(); + for (auto attr : buf_ptr->attributes) { + attrs.emplace_back( + VertexAttributePointer{attr, buf_ptr,idx} + ); + idx++; + } + _vao.attributes(attrs); + return _vertex_data.size(); +} + +VertexBuffer& VertexGroup::buffer(size_t idx) { + return *_vertex_data[idx]; +} + +std::vector>> VertexGroup::buffers() { + return _vertex_data; +} + +size_t VertexGroup::buffers(std::vector>&& bufs_) { + _vertex_data = {}; + for (auto& buf : bufs_) { + _vertex_data.emplace_back( + std::make_shared>(std::move(buf)) + ); + } + VertexAttributePointerList attr_ptrs; + GLuint idx = 0; + for (auto buf : _vertex_data) { + for (auto attr : buf->attributes) { + attr_ptrs.emplace_back( + VertexAttributePointer{attr, buf.get(), idx} + ); + idx++; + } + } + _vao.attributes(attr_ptrs); + return _vertex_data.size(); +} + +VertexBuffer&& VertexGroup::dropBuffer(size_t idx) { + if (idx >= _vertex_data.size()) { + THROW(BufferError(BufferError::Codes::INVALID_BUFFER)); + } + std::vector> tmp; + for (auto buf : _vertex_data) { + tmp.emplace_back(std::move(*buf)); + } + _vertex_data.clear(); + std::shared_ptr> ret_ptr = + std::make_shared>(std::move(tmp[idx])); + tmp.erase(tmp.begin()+idx); + buffers(std::move(tmp)); + return std::move(*ret_ptr); +} + +VertexAttributePointer VertexGroup::attribute(size_t idx_) const { + return _vao.attr(idx_); +} + +void VertexGroup::enableAttr(GLuint idx) { + bind(); + _vao.enable(idx); + unbind(); +} + +void VertexGroup::disableAttr(GLuint idx) { + bind(); + _vao.disable(idx); + unbind(); +} + +void VertexGroup::disableBuffer(size_t idx) { + auto buf_ptr = _vertex_data[idx].get(); + bind(); + for (auto attr : _vao.attributes()) { + if (attr.buffer == buf_ptr) { + _vao.disable(attr.index); + } + } + unbind(); +} + +void VertexGroup::enableBuffer(size_t idx) { + auto buf_ptr = _vertex_data[idx].get(); + bind(); + for (auto attr : _vao.attributes()) { + if (attr.buffer == buf_ptr) { + _vao.enable(attr.index); + } + } + unbind(); +} + +void VertexGroup::enable() { _vao.enable(); } + +void VertexGroup::disable() { _vao.disable(); } + +} // namespace nb diff --git a/engine/NBGraphics/tests/CMakeLists.txt b/engine/NBGraphics/tests/CMakeLists.txt new file mode 100644 index 0000000..e352a10 --- /dev/null +++ b/engine/NBGraphics/tests/CMakeLists.txt @@ -0,0 +1,16 @@ +cmake_minimum_required(VERSION 3.26.0) + +if (NB_BUILD_TESTS) + enable_testing() + include(GoogleTest) + add_executable(TestWindow + ./TestWindow.cpp + ) + target_link_libraries(TestWindow + NBCore + NBGraphics + GTest::gtest_main + ) + gtest_discover_tests(TestWindow) + +endif() \ No newline at end of file diff --git a/engine/NBGraphics/tests/TestWindow.cpp b/engine/NBGraphics/tests/TestWindow.cpp new file mode 100644 index 0000000..d7df069 --- /dev/null +++ b/engine/NBGraphics/tests/TestWindow.cpp @@ -0,0 +1,6 @@ +#include + +int main() { + + return 0; +} \ No newline at end of file