NBEngine/engine/NBGraphics/FrameBuffers.hpp
2026-08-20 08:12:55 -05:00

236 lines
6.4 KiB
C++

#pragma once
#ifndef _NB_FRAMEBUFFER
#define _NB_FRAMEBUFFER
#include "GLLoad.hpp"
#include <NBCore/Errors.hpp>
#include "Image.hpp"
#include "OGLObjects.hpp"
#include "Textures.hpp"
namespace nb {
class FrameBufferError : public Error<FrameBufferError> {
protected:
using Base = Error<FrameBufferError>;
public:
using Base::Base;
enum Codes : unsigned int {
UNDEFINED, INVALID_VALUE
};
static const std::string type;
static const ErrorCodeMap ErrorMessages;
};
class RenderBuffer : public RenderTarget {
protected:
GLuint declare() override {
if (!_id) {
glRenderbufferStorage(
GL_RENDERBUFFER,
format,
width,
height
);
}
return _id;
}
void remove() override {
if (_id) {
unbind();
glDeleteRenderbuffers(1, &_id);
_id = 0;
}
}
public:
const unsigned int width;
const unsigned int height;
const GLenum format;
template<typename PixelType>
RenderBuffer(unsigned int width_, unsigned int height_) :
width(width_),
height(height_),
format(OGLPixelFormat<PixelType>::glFormat),
RenderTarget(GL_RENDERBUFFER)
{ declare(); }
RenderBuffer(RenderBuffer&& rhs) :
width(rhs.width),
height(rhs.height),
format(rhs.format),
RenderTarget(std::move(rhs))
{}
RenderBuffer& operator=(RenderBuffer&&) = delete;
void bind() const override {
if (_id) {
glBindRenderbuffer(GL_RENDERBUFFER, _id);
} else {
THROW(OGLError(
OGLError::Codes::INVALID_OBJECT,
"w/ RenderBuffer"
));
}
}
void unbind() const override {
glBindRenderbuffer(GL_RENDERBUFFER, 0);
}
};
class FrameBufferBase : public OpenGLObject {
private:
static bool texTypeIs2D(GLenum texType) {
switch(texType) {
case GL_TEXTURE_2D:
default:
return true;
break;
}
}
protected:
using AttachmentMap = std::unordered_map<GLenum, std::shared_ptr<RenderTarget>>;
AttachmentMap _attachments;
FrameBufferBase(GLenum target);
GLuint declare() override;
void remove() override;
std::shared_ptr<RenderTarget> detach(GLenum);
void attach(
GLenum attachment,
std::shared_ptr<Texture> texture,
unsigned int level=0
);
void attach(
GLenum attachment,
std::shared_ptr<Texture> texture,
unsigned int level,
unsigned int layer
);
void attach(
GLenum attachment,
std::shared_ptr<RenderBuffer> renderBuffer
) {
declare();
if (attachment == GL_NONE) {
THROW(FrameBufferError(FrameBufferError::INVALID_VALUE,
"Cannot attach RenderBuffer to `GL_NONE`"
));
}
glFramebufferRenderbuffer(
Target,
attachment,
GL_RENDERBUFFER,
renderBuffer->id()
);
_attachments[attachment] = renderBuffer;
}
public:
const GLenum Target;
void bind() const override;
void unbind() const override;
GLenum status() const;
AttachmentMap getAllBuffers() const;
std::shared_ptr<RenderTarget> getBuffer(GLenum) const;
};
class ReadFrameBuffer;
class WriteFrameBuffer;
class ReadFrameBuffer : public virtual FrameBufferBase {
friend WriteFrameBuffer;
protected:
using FrameBufferBase::_attachments;
GLenum _location;
public:
using FrameBufferBase::attach;
ReadFrameBuffer();
ReadFrameBuffer& operator=(WriteFrameBuffer&&);
ReadFrameBuffer& operator=(ReadFrameBuffer&&);
std::shared_ptr<RenderTarget> getReadBuffer() const;
GLenum getReadLocation() const;
std::shared_ptr<RenderTarget> setReadBuffer(GLenum attachment);
template<typename T, typename... Args>
std::shared_ptr<RenderTarget> setReadBuffer(
GLenum attachment,
std::shared_ptr<T> target,
Args... args
) {
declare();
FrameBufferBase::attach(attachment, target, args...);
setReadBuffer(attachment);
return getReadBuffer();
}
};
class WriteFrameBuffer : public virtual FrameBufferBase {
friend ReadFrameBuffer;
protected:
using DrawTexVec = std::vector<std::shared_ptr<Texture>>;
using FrameBufferBase::_attachments;
std::vector<GLenum> _locations;
public:
WriteFrameBuffer();
WriteFrameBuffer& operator=(ReadFrameBuffer&&);
WriteFrameBuffer& operator=(WriteFrameBuffer&&);
template<typename... T>
void attach(
GLenum attachment,
std::shared_ptr<Texture> texture,
T... args
) { FrameBufferBase::attach(attachment, texture, args...); }
DrawTexVec getWriteBuffers() const;
std::vector<GLenum> getWriteLocations() const;
DrawTexVec setWriteBuffers(const std::vector<GLenum>& attachments);
std::shared_ptr<Texture> setWriteBuffer(GLenum attachment);
template<typename... T>
std::shared_ptr<Texture> setWriteBuffer(
GLenum attachment,
std::shared_ptr<Texture> texture,
T... args
) {
attach(attachment, texture, args...);
setWriteBuffer(attachment);
return std::static_pointer_cast<Texture>(_attachments.at(attachment));
}
RGBA<float> clearColorValue() const;
RGBA<float> clearColorValue(const RGBA<float>&);
RGBA<float> clearColorValue(float r, float g, float b, float a);
double clearDepthValue() const;
double clearDepthValue(double);
GLint clearStencilValue() const;
GLint clearStencilValue(GLint);
void clear(GLbitfield mask=(
GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT | GL_STENCIL_BUFFER_BIT
)) const;
void clearColor() const;
void clearDepth() const;
void clearDepth(float) const;
void clearStencil() const;
void clearStencil(GLint) const;
void clearDepthStencil() const;
void clearDepthStencil(float, GLint) const;
};
class FrameBuffer : public ReadFrameBuffer, public WriteFrameBuffer {
protected:
using FrameBufferBase::_attachments;
using ReadFrameBuffer::_location;
using WriteFrameBuffer::_locations;
public:
FrameBuffer();
FrameBuffer& operator=(FrameBuffer&&);
FrameBuffer& operator=(ReadFrameBuffer&&);
FrameBuffer& operator=(WriteFrameBuffer&&);
};
} // namespace nb
#endif // _NB_FRAMEBUFFER