From 4caeaa8cbd5200a0e4f77534fb927dfed3232fa7 Mon Sep 17 00:00:00 2001 From: Notiooo Date: Sun, 29 Oct 2023 19:47:57 +0100 Subject: [PATCH] Add OpenGL abstractions --- AimGL/src/Renderer3D/Buffer.cpp | 15 ++++++ AimGL/src/Renderer3D/Buffer.h | 31 +++++++++++ AimGL/src/Renderer3D/BufferElement.cpp | 2 + AimGL/src/Renderer3D/BufferElement.h | 18 +++++++ AimGL/src/Renderer3D/BufferLayout.cpp | 30 +++++++++++ AimGL/src/Renderer3D/BufferLayout.h | 75 ++++++++++++++++++++++++++ AimGL/src/Renderer3D/IndexBuffer.cpp | 47 ++++++++++++++++ AimGL/src/Renderer3D/IndexBuffer.h | 47 ++++++++++++++++ AimGL/src/Renderer3D/Renderer3D.cpp | 52 ++++++++++++++++++ AimGL/src/Renderer3D/Renderer3D.h | 66 +++++++++++++++++++++++ AimGL/src/Renderer3D/VertexArray.cpp | 42 +++++++++++++++ AimGL/src/Renderer3D/VertexArray.h | 43 +++++++++++++++ AimGL/src/Renderer3D/VertexBuffer.cpp | 25 +++++++++ AimGL/src/Renderer3D/VertexBuffer.h | 39 ++++++++++++++ 14 files changed, 532 insertions(+) create mode 100644 AimGL/src/Renderer3D/Buffer.cpp create mode 100644 AimGL/src/Renderer3D/Buffer.h create mode 100644 AimGL/src/Renderer3D/BufferElement.cpp create mode 100644 AimGL/src/Renderer3D/BufferElement.h create mode 100644 AimGL/src/Renderer3D/BufferLayout.cpp create mode 100644 AimGL/src/Renderer3D/BufferLayout.h create mode 100644 AimGL/src/Renderer3D/IndexBuffer.cpp create mode 100644 AimGL/src/Renderer3D/IndexBuffer.h create mode 100644 AimGL/src/Renderer3D/Renderer3D.cpp create mode 100644 AimGL/src/Renderer3D/Renderer3D.h create mode 100644 AimGL/src/Renderer3D/VertexArray.cpp create mode 100644 AimGL/src/Renderer3D/VertexArray.h create mode 100644 AimGL/src/Renderer3D/VertexBuffer.cpp create mode 100644 AimGL/src/Renderer3D/VertexBuffer.h diff --git a/AimGL/src/Renderer3D/Buffer.cpp b/AimGL/src/Renderer3D/Buffer.cpp new file mode 100644 index 0000000..8b5b7f1 --- /dev/null +++ b/AimGL/src/Renderer3D/Buffer.cpp @@ -0,0 +1,15 @@ +#include "Buffer.h" +#include "pch.h" + +Buffer::Buffer(Buffer&& rhs) noexcept +{ + mBufferId = rhs.mBufferId; + rhs.mBufferId = 0; +} + +Buffer& Buffer::operator=(Buffer&& rhs) noexcept +{ + mBufferId = rhs.mBufferId; + rhs.mBufferId = 0; + return *this; +} \ No newline at end of file diff --git a/AimGL/src/Renderer3D/Buffer.h b/AimGL/src/Renderer3D/Buffer.h new file mode 100644 index 0000000..301d597 --- /dev/null +++ b/AimGL/src/Renderer3D/Buffer.h @@ -0,0 +1,31 @@ +#pragma once + +/* + * The base class for all kinds of buffers found in OpenGL. + */ +class Buffer +{ +public: + Buffer() = default; + + Buffer(const Buffer&) = delete; + Buffer(Buffer&&) noexcept; + + Buffer& operator=(const Buffer&) = delete; + Buffer& operator=(Buffer&&) noexcept; + + virtual ~Buffer() = default; + + /** + * Binds a buffer object to the specific (depends on derived class) buffer binding point + */ + virtual void bind() const = 0; + + /** + * Unbinds a buffer object + */ + virtual void unbind() const = 0; + +protected: + unsigned int mBufferId; +}; diff --git a/AimGL/src/Renderer3D/BufferElement.cpp b/AimGL/src/Renderer3D/BufferElement.cpp new file mode 100644 index 0000000..1b9f14c --- /dev/null +++ b/AimGL/src/Renderer3D/BufferElement.cpp @@ -0,0 +1,2 @@ +#include "BufferElement.h" +#include "pch.h" \ No newline at end of file diff --git a/AimGL/src/Renderer3D/BufferElement.h b/AimGL/src/Renderer3D/BufferElement.h new file mode 100644 index 0000000..edf8c09 --- /dev/null +++ b/AimGL/src/Renderer3D/BufferElement.h @@ -0,0 +1,18 @@ +#pragma once + +/** + * Represents the memory layout for the buffer layout part of a given type and number of elements. + */ +struct BufferElement +{ + unsigned int type; //!< OpenGL type of given data + unsigned int count; //!< Number of elements of given type + unsigned char normalized;//!< Is the data normalized + + /** + * Returns the data size of a given OpenGL type. + * @param type OpenGL Type. + * @return Size of given OpenGL type. + */ + static unsigned int sizeOfGLType(unsigned int type); +}; \ No newline at end of file diff --git a/AimGL/src/Renderer3D/BufferLayout.cpp b/AimGL/src/Renderer3D/BufferLayout.cpp new file mode 100644 index 0000000..6614cac --- /dev/null +++ b/AimGL/src/Renderer3D/BufferLayout.cpp @@ -0,0 +1,30 @@ +#include "BufferLayout.h" +#include "Renderer3D/Renderer3D.h" +#include "pch.h" + +unsigned BufferElement::sizeOfGLType(unsigned type) +{ + switch (type) + { + case GL_FLOAT: return 4; + case GL_UNSIGNED_INT: return 4; + case GL_UNSIGNED_BYTE: return 1; + } + ASSERT(false); + return 0; +} + +BufferLayout::BufferLayout() + : mStride(0) +{ +} + +const std::vector BufferLayout::bufferElements() const +{ + return mBufferElements; +} + +unsigned BufferLayout::stride() const +{ + return mStride; +} diff --git a/AimGL/src/Renderer3D/BufferLayout.h b/AimGL/src/Renderer3D/BufferLayout.h new file mode 100644 index 0000000..d712356 --- /dev/null +++ b/AimGL/src/Renderer3D/BufferLayout.h @@ -0,0 +1,75 @@ +#pragma once +#include "Renderer3D/BufferElement.h" +#include + +/** + * Represents the data layout for a given buffer. + */ +class BufferLayout +{ +public: + BufferLayout(); + + /** + * Adds information that the layout consists of the given number of elements of type T. + * @tparam T Parameters other than float, unsigned int and unsigned char are not supported. + * @param count Number of elements of a given type per row. + */ + template + void push(unsigned int count) + { + // static_assert(false); + } + + /** + * Adds information that the layout consists of the given number of elements of type float. + * @param count Number of elements of a float type per row. + */ + template<> + void push(unsigned int count) + { + mBufferElements.push_back({GL_FLOAT, count, GL_FALSE}); + mStride += BufferElement::sizeOfGLType(GL_FLOAT) * count; + } + + /** + * Adds information that the layout consists of the given number of elements of type unsigned + * int. + * @param count Number of elements of a unsigned int type per row. + */ + template<> + void push(unsigned int count) + { + mBufferElements.push_back({GL_UNSIGNED_INT, count, GL_FALSE}); + mStride += BufferElement::sizeOfGLType(GL_UNSIGNED_INT) * count; + } + + /** + * Adds information that the layout consists of the given number of elements of type unsigned + * char. + * @param count Number of elements of a unsigned char type per row. + */ + template<> + void push(unsigned int count) + { + mBufferElements.push_back({GL_UNSIGNED_BYTE, count, GL_TRUE}); + mStride += BufferElement::sizeOfGLType(GL_UNSIGNED_BYTE) * count; + } + + /** + * Contains all buffer elements + * @return A vector of many single buffer elements + */ + [[nodiscard]] const std::vector bufferElements() const; + + /** + * Determines the size that one 'entry', or otherwise 'row' of data contains before the next set + * of data begins. + * @return Size of the stride + */ + [[nodiscard]] unsigned int stride() const; + +private: + std::vector mBufferElements; + unsigned int mStride; +}; \ No newline at end of file diff --git a/AimGL/src/Renderer3D/IndexBuffer.cpp b/AimGL/src/Renderer3D/IndexBuffer.cpp new file mode 100644 index 0000000..777cb85 --- /dev/null +++ b/AimGL/src/Renderer3D/IndexBuffer.cpp @@ -0,0 +1,47 @@ +#include "IndexBuffer.h" +#include "pch.h" + +#include "Renderer3D.h" + +IndexBuffer::IndexBuffer() + : mCount(0) +{ + GLCall(glGenBuffers(1, &mBufferId)); +} + +IndexBuffer::IndexBuffer(const unsigned int* data, unsigned count) + : mCount(count) +{ + ASSERT(sizeof(unsigned int) == sizeof(GLuint)); + + GLCall(glGenBuffers(1, &mBufferId)); + GLCall(glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, mBufferId)); + GLCall(glBufferData(GL_ELEMENT_ARRAY_BUFFER, count * sizeof(GLuint), data, GL_STATIC_DRAW)); +} + +IndexBuffer::~IndexBuffer() +{ + GLCall(glDeleteBuffers(1, &mBufferId)); +} + +void IndexBuffer::bind() const +{ + GLCall(glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, mBufferId)); +} + +void IndexBuffer::unbind() const +{ + GLCall(glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, 0)); +} + +void IndexBuffer::setData(const unsigned int* data, unsigned count) +{ + this->mCount = count; + bind(); + GLCall(glBufferData(GL_ELEMENT_ARRAY_BUFFER, count * sizeof(GLuint), data, GL_STATIC_DRAW)); +} + +unsigned IndexBuffer::size() const +{ + return mCount; +} diff --git a/AimGL/src/Renderer3D/IndexBuffer.h b/AimGL/src/Renderer3D/IndexBuffer.h new file mode 100644 index 0000000..03f17b5 --- /dev/null +++ b/AimGL/src/Renderer3D/IndexBuffer.h @@ -0,0 +1,47 @@ +#pragma once +#include "Buffer.h" + +/** + * A higher level representation of OpenGL IndexBuffer which is an array of pointers into the vertex + * buffer. + */ +class IndexBuffer : public Buffer +{ +public: + IndexBuffer(); + IndexBuffer(const unsigned int* data, unsigned int count); + + IndexBuffer(const IndexBuffer&) = delete; + IndexBuffer(IndexBuffer&&) noexcept = default; + + IndexBuffer& operator=(const IndexBuffer&) = delete; + IndexBuffer& operator=(IndexBuffer&&) noexcept = default; + + ~IndexBuffer() override; + + /** + * Binds a buffer object to the specific (GL_ELEMENT_ARRAY_BUFFER) buffer binding point + */ + void bind() const override; + + /** + * Unbinds a buffer object (GL_ELEMENT_ARRAY_BUFFER) + */ + void unbind() const override; + + /** + * Sets the data with the specified number of ints and binds the buffer + * @param data Array of unsigned ints containing buffer indexes + * @param count Size of the Array + */ + void setData(const unsigned int* data, unsigned count); + + /** + * Returns the number of elements in the index buffer + * @return Size of the index buffer + */ + unsigned int size() const; + +private: + unsigned int mCount; +}; diff --git a/AimGL/src/Renderer3D/Renderer3D.cpp b/AimGL/src/Renderer3D/Renderer3D.cpp new file mode 100644 index 0000000..f33eea2 --- /dev/null +++ b/AimGL/src/Renderer3D/Renderer3D.cpp @@ -0,0 +1,52 @@ +#include "Renderer3D.h" +#include "pch.h" + +#include +#include +#include +#include + +void GLClearError() +{ + while (glGetError() /* != GL_NO_ERROR*/) + { + ; + } +} + +bool GLLogCall(const char* function, const char* file, int line) +{ + if (GLenum error = glGetError()) + { + std::cout << "[OpenGL Error] (0x" << std::hex << error << std::dec << "): " << function + << " " << file << " at line: " << line << std::endl; + return false; + } + return true; +} + +void Renderer3D::draw(const VertexArray& va, const IndexBuffer& ib, const sf::Shader& shader, + const DrawMode& drawMode) const +{ + sf::Shader::bind(&shader); + va.bind(); + ib.bind(); + GLCall(glDrawElements(toOpenGL(drawMode), ib.size(), GL_UNSIGNED_INT, nullptr)); + +#ifdef _DEBUG + sf::Shader::bind(nullptr); + va.unbind(); + ib.unbind(); +#endif +} + +unsigned Renderer3D::toOpenGL(const Renderer3D::DrawMode& drawMode) const +{ + switch (drawMode) + { + case DrawMode::Lines: return GL_LINES; + case DrawMode::Triangles: return GL_TRIANGLES; + case DrawMode::Quads: return GL_QUADS; + default: throw std::runtime_error("This DrawMode is not supported!"); + } +} diff --git a/AimGL/src/Renderer3D/Renderer3D.h b/AimGL/src/Renderer3D/Renderer3D.h new file mode 100644 index 0000000..01231f7 --- /dev/null +++ b/AimGL/src/Renderer3D/Renderer3D.h @@ -0,0 +1,66 @@ +#pragma once + +#include "Renderer3D/IndexBuffer.h" +#include "Renderer3D/VertexArray.h" + +/** + * Activates breakpoint + */ +#ifdef _MSC_VER + #define ASSERT(x) \ + if (!x) \ + __debugbreak(); +#else + #define ASSERT(x) \ + if (!x) \ + raise(SIGTRAP); +#endif + +/** + * Macro to use on OpenGL functions to correctly catch errors and write them out + */ +#define GLCall(x) \ + GLClearError(); \ + x; \ + ASSERT(GLLogCall(#x, __FILE__, __LINE__)); + +/** + * Cleans up OpenGL errors + */ +void GLClearError(); + +/** + * Prints an error if an OpenGL error occurred + * @param function The function in which the error occurred + * @param file The name of the file in which the error occurred + * @param line The line where the error occurred + * @return False if the error occurred, true otherwise + */ +bool GLLogCall(const char* function, const char* file, int line); + +/** + * 3D Renderer allows to draw 3D objects to the screen using appropriate buffers and shaders + */ +class Renderer3D +{ +public: + enum class DrawMode + { + Lines, + Triangles, + Quads + }; + + /** + * Draws the data given in VertexArray, IndexBuffer to the screen using the interpretation given + * in Shader. + * @param va Stores all Vertex Data. + * @param ib Specifies the drawing order of the VertexArray. + * @param shader Shader telling how to draw data. + */ + void draw(const VertexArray& va, const IndexBuffer& ib, const sf::Shader& shader, + const DrawMode& drawMode = DrawMode::Triangles) const; + +private: + unsigned toOpenGL(const DrawMode& drawMode) const; +}; \ No newline at end of file diff --git a/AimGL/src/Renderer3D/VertexArray.cpp b/AimGL/src/Renderer3D/VertexArray.cpp new file mode 100644 index 0000000..3522745 --- /dev/null +++ b/AimGL/src/Renderer3D/VertexArray.cpp @@ -0,0 +1,42 @@ +#include "VertexArray.h" +#include "pch.h" + +#include "Renderer3D/BufferLayout.h" +#include "Renderer3D/Renderer3D.h" + +VertexArray::VertexArray() +{ + GLCall(glGenVertexArrays(1, &mBufferId)); +} + +VertexArray::~VertexArray() +{ + GLCall(glDeleteVertexArrays(1, &mBufferId)); +} + +void VertexArray::bind() const +{ + GLCall(glBindVertexArray(mBufferId)); +} + +void VertexArray::unbind() const +{ + GLCall(glBindVertexArray(0)); +} + +void VertexArray::setBuffer(const VertexBuffer& vb, const BufferLayout& layout) +{ + bind(); + vb.bind(); + + const auto& elements = layout.bufferElements(); + unsigned int offset = 0; + for (unsigned int i = 0; i < elements.size(); ++i) + { + const auto& element = elements[i]; + GLCall(glEnableVertexAttribArray(i)); + GLCall(glVertexAttribPointer(i, element.count, element.type, element.normalized, + layout.stride(), reinterpret_cast(offset))); + offset += element.count * BufferElement::sizeOfGLType(element.type); + } +} diff --git a/AimGL/src/Renderer3D/VertexArray.h b/AimGL/src/Renderer3D/VertexArray.h new file mode 100644 index 0000000..809b48d --- /dev/null +++ b/AimGL/src/Renderer3D/VertexArray.h @@ -0,0 +1,43 @@ +#pragma once + +#include "Buffer.h" +#include "Renderer3D/VertexBuffer.h" + +class BufferLayout; + +class VertexArray : public Buffer +{ +public: + VertexArray(); + VertexArray(const VertexBuffer&) = delete; + VertexArray(VertexArray&&) noexcept = default; + VertexArray& operator=(const VertexArray&) = delete; + VertexArray& operator=(VertexArray&&) noexcept = default; + + ~VertexArray() override; + + /** + * Binds a vertex array object + */ + void bind() const override; + + /** + * Unbinds a vertex array object + */ + void unbind() const override; + + /** + * \brief A function that assigns VertexBuffer it's according BufferLayout. + * + * For example, a VertexBuffer stores position information (3x float) + * and another one stores texture information (2x float). In this case, + * BufferLayout informs the following: + * + * layout.push(3); + * layout.push(2); + * + * \param vb VertexBuffer + * \param layout Layout corresponding to the contents of the vector. + */ + void setBuffer(const VertexBuffer& vb, const BufferLayout& layout); +}; \ No newline at end of file diff --git a/AimGL/src/Renderer3D/VertexBuffer.cpp b/AimGL/src/Renderer3D/VertexBuffer.cpp new file mode 100644 index 0000000..82b0dba --- /dev/null +++ b/AimGL/src/Renderer3D/VertexBuffer.cpp @@ -0,0 +1,25 @@ +#include "VertexBuffer.h" +#include "Renderer3D/Renderer3D.h" +#include "pch.h" + +VertexBuffer::VertexBuffer(const void* data, unsigned size) +{ + GLCall(glGenBuffers(1, &mBufferId)); + GLCall(glBindBuffer(GL_ARRAY_BUFFER, mBufferId)); + GLCall(glBufferData(GL_ARRAY_BUFFER, size, data, GL_STATIC_DRAW)); +} + +VertexBuffer::~VertexBuffer() +{ + GLCall(glDeleteBuffers(1, &mBufferId)); +} + +void VertexBuffer::bind() const +{ + GLCall(glBindBuffer(GL_ARRAY_BUFFER, mBufferId)); +} + +void VertexBuffer::unbind() const +{ + GLCall(glBindBuffer(GL_ARRAY_BUFFER, 0)); +} diff --git a/AimGL/src/Renderer3D/VertexBuffer.h b/AimGL/src/Renderer3D/VertexBuffer.h new file mode 100644 index 0000000..88f550b --- /dev/null +++ b/AimGL/src/Renderer3D/VertexBuffer.h @@ -0,0 +1,39 @@ +#pragma once +#include "Buffer.h" + +/** + * Vertex buffer object (VBO) allows vertex array data to be stored + * in high-performance graphics memory. + */ +class VertexBuffer : public Buffer +{ +public: + VertexBuffer(const void* data, unsigned int size); + + template + VertexBuffer(const std::vector& vector); + + VertexBuffer(const VertexBuffer&) = delete; + VertexBuffer(VertexBuffer&&) noexcept = default; + + VertexBuffer& operator=(const VertexBuffer&) = delete; + VertexBuffer& operator=(VertexBuffer&&) noexcept = default; + + ~VertexBuffer() override; + + /** + * Binds a buffer object to the specific (GL_ARRAY_BUFFER) buffer binding point + */ + void bind() const override; + + /** + * Unbinds a buffer object (GL_ARRAY_BUFFER) + */ + void unbind() const override; +}; + +template +VertexBuffer::VertexBuffer(const std::vector& vector) + : VertexBuffer(vector.data(), vector.size() * sizeof(vector[0])) +{ +}