vbo, vao, and buffer classes

This commit is contained in:
frank 2021-10-18 17:33:33 -04:00
parent 3e5e0fcbb8
commit 3212dc15cf
6 changed files with 325 additions and 48 deletions

View File

@ -11,6 +11,7 @@
*/
#include "GLObject.hpp"
using namespace sb;
/* The deleter function is used for freeing the memory allocated to the object (for example, glDeleteTextures,
* or a custom function that does something in addition to calling the appropriate GL deleter function). */
@ -48,3 +49,75 @@ bool GLObject::generated() const
{
return static_cast<bool>(object_id);
};
VAO::VAO() : GLObject(vao_deleter) {}
/* Bind this VAO to the current GL context */
void VAO::bind() const
{
glBindVertexArray(id());
}
/* Forward the GL VAO generate function to the base class */
void VAO::generate()
{
GLObject::generate(glGenVertexArrays);
}
/* This function gets passed to the abstract base class for deleting the VAO data when the ID
* pointer goes out of scope (when all instances of this VAO and its copies are out of scope) */
void sb::vao_deleter(GLuint* id)
{
/* not sure why SDL_Log works here on program exit but SDL_LogDebug and SDL_LogInfo don't */
SDL_Log("destroying VAO ID %i", *id);
glDeleteVertexArrays(1, id);
}
Buffer::Buffer() : Buffer(GL_INVALID_ENUM) {}
Buffer::Buffer(GLenum target) : GLObject(buffer_deleter), buffer_target(target) {}
/* Forward the GL generate function to the base class. */
void Buffer::generate()
{
GLObject::generate(glGenBuffers);
}
/* Set the target for this buffer. */
void Buffer::target(GLenum target)
{
buffer_target = target;
}
/* Return the GL enum for target */
GLenum Buffer::target() const
{
return buffer_target;
}
/* Bind this Buffer to the current GL context. An exception will be thrown if the target has not
* been set. */
void Buffer::bind() const
{
if (target() == GL_INVALID_ENUM)
{
throw std::invalid_argument("target must be set before binding buffer");
}
glBindBuffer(target(), id());
}
/* Set the target and bind the buffer. */
void Buffer::bind(GLenum target)
{
this->target(target);
bind();
}
/* This function gets passed to the abstract base class for deleting the Buffer data when the ID
* pointer goes out of scope (when all instances of this Buffer and its copies are out of scope) */
void sb::buffer_deleter(GLuint* id)
{
/* not sure why SDL_Log works here on program exit but SDL_LogDebug and SDL_LogInfo don't */
SDL_Log("destroying buffer ID %i", *id);
glDeleteBuffers(1, id);
}

View File

@ -17,11 +17,17 @@
An appropriate deleter function must be passed in to the constructor from the derived
class. It can be a custom function, or it can be one of the GL deleter functions like
glDeleteTextures.
A VAO class and a general Buffer class are also defined here. The VAO class just fills
in the abstract methods of the GLObject class and doesn't have any further Space Box
specific implementation. The buffer class is probably most useful being inherited by a
derived class that implements a more specific type of buffer that passes its target to
the base class, like the VBO class.
*/
#ifndef GLObject_h_
#define GLObject_h_
#ifndef SB_GLOBJECT_H_
#define SB_GLOBJECT_H_
/* include Open GL */
#if defined(__EMSCRIPTEN__)
@ -36,30 +42,72 @@
#include <functional>
#include "Log.hpp"
class GLObject
namespace sb
{
private:
class GLObject
{
typedef std::function<void(GLsizei, GLuint*)> generator_function;
typedef std::function<void(GLuint*)> deleter_function;
private:
std::shared_ptr<GLuint> object_id = nullptr;
deleter_function deleter = nullptr;
typedef std::function<void(GLsizei, GLuint*)> generator_function;
typedef std::function<void(GLuint*)> deleter_function;
protected:
std::shared_ptr<GLuint> object_id = nullptr;
deleter_function deleter = nullptr;
GLObject(deleter_function);
virtual void bind() const = 0;
void generate(generator_function);
protected:
public:
GLObject(deleter_function);
virtual void bind() const = 0;
void generate(generator_function);
virtual void id(GLuint);
virtual GLuint id() const;
virtual bool generated() const;
virtual ~GLObject() = default;
public:
};
virtual void id(GLuint);
virtual GLuint id() const;
virtual bool generated() const;
virtual ~GLObject() = default;
};
class VAO : public GLObject
{
public:
VAO();
void generate();
void bind() const;
};
void vao_deleter(GLuint*);
class Buffer : public GLObject
{
private:
GLenum buffer_target = GL_INVALID_ENUM;
protected:
GLenum target() const;
public:
Buffer();
Buffer(GLenum);
void target(GLenum);
void generate();
void bind() const;
void bind(GLenum);
};
void buffer_deleter(GLuint*);
}
#endif

View File

@ -1,4 +1,17 @@
/* /\ +--------------------------------------------------------------+
____/ \____ /| - zlib/MIT/Unlicenced game framework licensed to freely use, |
\ / / | copy, modify and sell without restriction |
+--\ ^__^ /--+ | |
| ~/ \~ | | - originally created at [http://nugget.fun] |
| ~~~~~~~~~~~~ | +--------------------------------------------------------------+
| SPACE ~~~~~ | /
| ~~~~~~~ BOX |/
+--------------+
*/
#include "Texture.hpp"
using namespace sb;
/* Have to pass our deleter to abstract base class at instantiation */
Texture::Texture() : GLObject(texture_deleter) {}
@ -125,7 +138,7 @@ bool Texture::operator==(const Texture& texture) const
/* This function gets passed to the abstract base class for deleting the texture data when the ID
* pointer goes out of scope (when all instances of this texture and its copies are out of scope) */
void texture_deleter(GLuint* id)
void sb::texture_deleter(GLuint* id)
{
/* not sure why SDL_Log works here at program end but SDL_LogDebug and SDL_LogInfo don't */
SDL_Log("destroying texture ID %i", *id);

View File

@ -19,9 +19,17 @@
*/
#ifndef Texture_h_
#define Texture_h_
#ifndef SB_TEXTURE_H_
#define SB_TEXTURE_H_
/* include Open GL */
#if defined(__EMSCRIPTEN__)
#include <GL/glew.h>
#else
#include "glew/glew.h"
#endif
#include <sstream>
#include <stdexcept>
#include "glm/vec2.hpp"
#include "SDL.h"
@ -31,39 +39,37 @@
#include "GLObject.hpp"
#include "Log.hpp"
/* include Open GL */
#if defined(__EMSCRIPTEN__)
#include <GL/glew.h>
#else
#include "glew/glew.h"
#endif
class Texture : public GLObject
namespace sb
{
class Texture : public GLObject
{
private:
private:
fs::path path = "";
fs::path path = "";
public:
public:
Texture();
Texture(fs::path);
void associate(fs::path);
void generate();
void generate(glm::vec2);
void load();
void load(fs::path);
void load(SDL_RWops*);
void load(SDL_Surface*);
void load(void*, GLenum = GL_RGBA, GLenum = GL_UNSIGNED_BYTE);
void load(void*, glm::vec2, GLenum = GL_RGBA, GLenum = GL_UNSIGNED_BYTE);
void bind() const override;
glm::vec2 size() const;
bool operator==(const Texture&) const;
Texture();
Texture(fs::path);
void associate(fs::path);
void generate();
void generate(glm::vec2);
void load();
void load(fs::path);
void load(SDL_RWops*);
void load(SDL_Surface*);
void load(void*, GLenum = GL_RGBA, GLenum = GL_UNSIGNED_BYTE);
void load(void*, glm::vec2, GLenum = GL_RGBA, GLenum = GL_UNSIGNED_BYTE);
void bind() const override;
glm::vec2 size() const;
bool operator==(const Texture&) const;
};
};
void texture_deleter(GLuint*);
void texture_deleter(GLuint*);
}
#endif

69
src/VBO.cpp Normal file
View File

@ -0,0 +1,69 @@
/* /\ +--------------------------------------------------------------+
____/ \____ /| - zlib/MIT/Unlicenced game framework licensed to freely use, |
\ / / | copy, modify and sell without restriction |
+--\ ^__^ /--+ | |
| ~/ \~ | | - originally created at [http://nugget.fun] |
| ~~~~~~~~~~~~ | +--------------------------------------------------------------+
| SPACE ~~~~~ | /
| ~~~~~~~ BOX |/
+--------------+
*/
#include "VBO.hpp"
using namespace sb;
/* Initialize a Vertex Buffer Object. The base class will be initalized with a target of
* GL_ARRAY_BUFFER. */
VBO::VBO() : Buffer(GL_ARRAY_BUFFER) {}
/* Allocate size bytes of vertex attribute memory on the GPU. Usage should be one of GL_STREAM_DRAW,
* GL_STREAM_READ, GL_STREAM_COPY, GL_STATIC_DRAW, GL_STATIC_READ, GL_STATIC_COPY, GL_DYNAMIC_DRAW,
* GL_DYNAMIC_READ, or GL_DYNAMIC_COPY. The memory will be uninitialized. */
void VBO::allocate(GLsizeiptr size, GLenum usage)
{
glBufferData(target(), size, nullptr, usage);
/* Debug */
std::ostringstream message;
message << "After allocating memory for " << *this;
sb::Log::gl_errors(message.str());
}
/* Set memory in the GPU buffer to the values of the attribute data using glBufferSubData. The memory
* set is a contiguous area from the object's current byte offset value to the offset plus the size in
* bytes of the attributes. After the memory is set, the offset is updated to point to the end of the
* area.
*
* Define an array of vertex attribute data on the GPU by passing the attributes's index, dimensions,
* normalization state, and type to glVertexAttribPointer, along with the buffer object's current
* offset.
*
* The updated offset is returned and can be used to independently keep track of where the next
* attributes will be stored in the buffer and apply to direct usage of glBufferSubData. */
GLintptr VBO::add(sb::Attributes& attributes)
{
/* Set memory */
glBufferSubData(target(), offset, attributes.size(), attributes);
/* Debug */
std::ostringstream initialization_message;
initialization_message << "After setting " << attributes.size() << "bytes in " << *this;
sb::Log::gl_errors(initialization_message.str());
/* Define array */
glVertexAttribPointer(
attributes.index(), attributes.dimensions(), attributes.type(), attributes.normalized(), 0, reinterpret_cast<GLvoid*>(offset));
/* Debug */
std::ostringstream pointer_message;
pointer_message << "After defining attribute pointer for <Attributes (" << attributes.dimensions() << "D, id: " <<
attributes.index() << ", size:" << attributes.size() << ")>";
sb::Log::gl_errors(pointer_message.str());
/* Increase offset */
offset += attributes.size();
return offset;
}
/* Overload the stream operator to support the VBO string representation. */
std::ostream& sb::operator<<(std::ostream& out, const VBO& vbo)
{
out << "<Vertex Buffer Object (id: " << vbo.id() << ")>";
return out;
}

68
src/VBO.hpp Normal file
View File

@ -0,0 +1,68 @@
/* /\ +--------------------------------------------------------------+
____/ \____ /| - zlib/MIT/Unlicenced game framework licensed to freely use, |
\ / / | copy, modify and sell without restriction |
+--\ ^__^ /--+ | |
| ~/ \~ | | - originally created at [http://nugget.fun] |
| ~~~~~~~~~~~~ | +--------------------------------------------------------------+
| SPACE ~~~~~ | /
| ~~~~~~~ BOX |/
+--------------+
[VBO.hpp]
This class can be used to allocate space for vertex attributes on the GPU and transfer their
values into the allocated space.
First allocate space by passing a size in bytes to the allocate member function. This size
can be calculated by adding together the sizes of every Attributes object that is intended to
be added to the VBO. After allocating, pass each Attributes object to the add function one at
a time to fill the buffer.
This class doesn't support updating the attribute values once they've been added. To do that,
either reallocate the entire buffer and readd the attribute values or keep track of the offset
returned by the add function and use glBufferSubData independently.
*/
#ifndef SB_VBO_H_
#define SB_VBO_H_
/* include Open GL */
#if defined(__EMSCRIPTEN__)
#include <GL/glew.h>
#else
#include "glew/glew.h"
#endif
#include <iostream>
#include <sstream>
#include "GLObject.hpp"
#include "Attributes.hpp"
#include "Log.hpp"
namespace sb
{
class VBO;
std::ostream& operator<<(std::ostream&, const VBO&);
class VBO : public Buffer
{
private:
GLintptr offset = 0;
public:
VBO();
void allocate(GLsizeiptr, GLenum);
GLintptr add(sb::Attributes&);
friend std::ostream& operator<<(std::ostream&, const VBO&);
};
}
#endif