NBEngine/engine/NBGraphics/Buffers.hpp
2026-04-04 15:01:39 -05:00

368 lines
9.7 KiB
C++

#pragma once
#ifndef _NB_BUFFER
#define _NB_BUFFER
#include "GLLoad.hpp"
#include <exception>
#include <map>
#include <string>
#include <vector>
#include <NBCore/Errors.hpp>
#include <NBCore/Types.hpp>
namespace nb {
typedef std::vector<unsigned char> RawVec;
static uint8_t GLSLTypeSize(GLenum type) {
switch(type) {
case GL_SHORT:
case GL_UNSIGNED_SHORT:
case GL_HALF_FLOAT:
return 2;
break;
case GL_INT:
case GL_UNSIGNED_INT:
case GL_FLOAT:
return 4;
break;
case GL_DOUBLE:
return 8;
break;
default:
return 1;
break;
}
}
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<VertexAttribute> VertexAttributeList;
template<typename T>
RawVec vectorToRaw(const std::vector<T>& vec) {
unsigned int num_bytes = vec.size() * sizeof(T);
RawVec ret(num_bytes);
memcpy(ret.data(), vec.data(), num_bytes);
return ret;
}
template<typename T, typename S>
RawVec concatVectorsToRaw(const std::vector<T>& vec1, const std::vector<S>& vec2) {
RawVec vec1_raw = vectorToRaw<T>(vec1);
RawVec vec2_raw = vectorToRaw<S>(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);
memcpy(ret.data(), vec1_raw.data(), vec1_raw_size);
memcpy(ret.data() + vec1_raw_size, vec2_raw.data(), vec2_raw_size);
return ret;
}
template<typename T>
std::vector<T> rawToVector(const RawVec& vec) {
if (vec.size() % sizeof(T) != 0) {
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<T> ret(num_elmts);
memcpy(ret.data(), vec.data(), vec.size());
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 <typename ObjectType>
class OpenGLObject {
public:
OpenGLObject(OpenGLObject&&) = default;
OpenGLObject& operator=(OpenGLObject&&) = default;
~OpenGLObject() { remove(); }
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<VAO> {
public:
using Base = OpenGLObject<VAO>;
using Base::Base;
VAO() { glGenVertexArrays(1, &_id); }
void bind() const { glBindVertexArray(_id); }
void unbind() const { glBindVertexArray(0); }
protected:
using Base::_id;
const
void remove() { glDeleteVertexArrays(1, &_id); }
};
template <typename BufferType>
class Buffer : public virtual OpenGLObject<Buffer<BufferType>> {
public:
using Base = OpenGLObject<Buffer>;
using Base::Base;
Buffer(GLenum usage_ = GL_STATIC_DRAW) : usage(usage_) { glGenBuffers(1, &_id); }
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); }
const GLenum GLTarget = BufferType::GLTarget;
GLenum usage;
protected:
using Base::_id;
void remove() { glDeleteBuffers(1, &_id); }
};
class VertexBuffer : public virtual Buffer<VertexBuffer> {
public:
static const GLenum GLTarget = GL_ARRAY_BUFFER;
protected:
};
class ElementBuffer : public virtual Buffer<ElementBuffer> {
public:
static const GLenum GLTarget = GL_ELEMENT_ARRAY_BUFFER;
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) {}
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};
};
*/
} // namespace NB
#endif // _NB_BUFFER