366 lines
9.6 KiB
C++
366 lines
9.6 KiB
C++
#pragma once
|
|
#ifndef _NB_BUFFER
|
|
#define _NB_BUFFER
|
|
|
|
#include "GLLoad.hpp"
|
|
|
|
#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
|