401 lines
9.7 KiB
C++
401 lines
9.7 KiB
C++
#include <NBGraphics/FrameBuffers.hpp>
|
|
|
|
namespace nb {
|
|
|
|
using FBOErrorCodes = FrameBufferError::Codes;
|
|
const std::string FrameBufferError::type = "nb::FrameBufferError";
|
|
const ErrorCodeMap FrameBufferError::ErrorMessages = {
|
|
{FBOErrorCodes::UNDEFINED, "Error"},
|
|
{FBOErrorCodes::INVALID_VALUE, "Invalid value"}
|
|
};
|
|
|
|
GLuint FrameBufferBase::declare() {
|
|
if (!_id) {
|
|
OPENGL_CALL(
|
|
glGenFramebuffers(1, &_id)
|
|
);
|
|
}
|
|
bind();
|
|
return _id;
|
|
}
|
|
|
|
void FrameBufferBase::remove() {
|
|
if (_id) {
|
|
unbind();
|
|
OPENGL_CALL(
|
|
glDeleteFramebuffers(1, &_id)
|
|
);
|
|
_id=0;
|
|
}
|
|
}
|
|
|
|
FrameBufferBase::FrameBufferBase(GLenum target) : Target(target) {}
|
|
|
|
std::shared_ptr<RenderTarget> FrameBufferBase::detach(GLenum attch_) {
|
|
bind();
|
|
auto ret = _attachments.at(attch_);
|
|
_attachments.erase(attch_);
|
|
OPENGL_CALL(
|
|
glFramebufferTexture(Target, attch_, 0, 0)
|
|
);
|
|
return ret;
|
|
}
|
|
|
|
void FrameBufferBase::attach(
|
|
GLenum attachment_,
|
|
std::shared_ptr<Texture> texture_,
|
|
unsigned int level_,
|
|
unsigned int layer_
|
|
) {
|
|
declare();
|
|
if (attachment_ == GL_NONE) {
|
|
THROW(FrameBufferError(FrameBufferError::INVALID_VALUE,
|
|
"Cannot attach object to `GL_NONE`"
|
|
));
|
|
}
|
|
OPENGL_CALL(glFramebufferTextureLayer(
|
|
Target,
|
|
attachment_,
|
|
texture_->id(),
|
|
level_,
|
|
layer_
|
|
));
|
|
_attachments[attachment_] = texture_;
|
|
}
|
|
|
|
void FrameBufferBase::attach(
|
|
GLenum attachment_,
|
|
std::shared_ptr<Texture> texture_,
|
|
unsigned int level_
|
|
) {
|
|
declare();
|
|
if (attachment_ == GL_NONE) {
|
|
THROW(FrameBufferError(FrameBufferError::INVALID_VALUE,
|
|
"Cannot attach object to `GL_NONE`"
|
|
));
|
|
}
|
|
if (texTypeIs2D(texture_->Target)) {
|
|
OPENGL_CALL(glFramebufferTexture2D(
|
|
Target,
|
|
attachment_,
|
|
texture_->Target,
|
|
texture_->id(),
|
|
level_
|
|
));
|
|
} else {
|
|
OPENGL_CALL(glFramebufferTexture1D(
|
|
Target,
|
|
attachment_,
|
|
texture_->Target,
|
|
texture_->id(),
|
|
level_
|
|
));
|
|
}
|
|
_attachments[attachment_] = texture_;
|
|
}
|
|
|
|
void FrameBufferBase::attach(
|
|
GLenum attachment,
|
|
std::shared_ptr<RenderBuffer> renderbuffer
|
|
) {
|
|
declare();
|
|
if (attachment == GL_NONE) {
|
|
THROW(FrameBufferError(FrameBufferError::INVALID_VALUE,
|
|
"Cannot attach RenderBuffer to `GL_NONE`"
|
|
));
|
|
}
|
|
OPENGL_CALL(glFramebufferRenderbuffer(
|
|
Target,
|
|
attachment,
|
|
GL_RENDERBUFFER,
|
|
renderbuffer->id()
|
|
));
|
|
_attachments[attachment] = renderbuffer;
|
|
}
|
|
|
|
void FrameBufferBase::bind() const {
|
|
if (_id) {
|
|
OPENGL_CALL(
|
|
glBindFramebuffer(Target, _id)
|
|
);
|
|
} else {
|
|
THROW(OpenGLObjectError(
|
|
OpenGLObjectError::Codes::INVALID_OBJECT,
|
|
"w/ FrameBuffer"
|
|
));
|
|
}
|
|
}
|
|
|
|
void FrameBufferBase::unbind() const {
|
|
OPENGL_CALL(
|
|
glBindFramebuffer(Target, 0)
|
|
);
|
|
}
|
|
|
|
GLenum FrameBufferBase::status() const {
|
|
bind();
|
|
return OPENGL_CALL(
|
|
glCheckFramebufferStatus(Target)
|
|
);
|
|
}
|
|
|
|
bool FrameBufferBase::complete() const {
|
|
return status() == GL_FRAMEBUFFER_COMPLETE;
|
|
}
|
|
|
|
FrameBufferBase::AttachmentMap FrameBufferBase::getAllBuffers() const {
|
|
return _attachments;
|
|
}
|
|
|
|
std::shared_ptr<RenderTarget> FrameBufferBase::getBuffer(GLenum attch_) const {
|
|
try {
|
|
return _attachments.at(attch_);
|
|
} catch (const std::out_of_range& e) {
|
|
THROW(e);
|
|
}
|
|
}
|
|
|
|
// class ReadFrameBuffer
|
|
|
|
ReadFrameBuffer::ReadFrameBuffer()
|
|
: _location(GL_NONE), FrameBufferBase(GL_READ_FRAMEBUFFER) {}
|
|
|
|
ReadFrameBuffer& ReadFrameBuffer::operator=(WriteFrameBuffer&& mv) {
|
|
if (mv.id()) {
|
|
_attachments = mv.getAllBuffers();
|
|
mv.setWriteBuffer(GL_NONE);
|
|
mv._attachments = {};
|
|
mv._locations = {};
|
|
FrameBufferBase::OpenGLObject::operator=(std::move(mv));
|
|
}
|
|
return *this;
|
|
}
|
|
|
|
ReadFrameBuffer& ReadFrameBuffer::operator=(ReadFrameBuffer&& mv) {
|
|
if (mv.id()) {
|
|
_attachments = mv._attachments;
|
|
_location = mv._location;
|
|
mv._attachments = {};
|
|
mv._location = GL_NONE;
|
|
FrameBufferBase::OpenGLObject::operator=(std::move(mv));
|
|
}
|
|
return *this;
|
|
}
|
|
|
|
std::shared_ptr<RenderTarget> ReadFrameBuffer::getReadBuffer() const {
|
|
if (_location == GL_NONE) { return nullptr; }
|
|
return getBuffer(_location);
|
|
}
|
|
|
|
GLenum ReadFrameBuffer::getReadLocation() const { return _location; }
|
|
|
|
std::shared_ptr<RenderTarget> ReadFrameBuffer::setReadBuffer(GLenum attch_) {
|
|
bind();
|
|
if (attch_!=GL_NONE) {
|
|
try {
|
|
const auto& x = _attachments.at(attch_);
|
|
} catch (const std::out_of_range& e) {
|
|
THROW(FrameBufferError(FrameBufferError::INVALID_VALUE,
|
|
"No buffer set at attachment"
|
|
));
|
|
}
|
|
}
|
|
_location = attch_;
|
|
OPENGL_CALL(glReadBuffer(_location));
|
|
return getReadBuffer();
|
|
}
|
|
|
|
// class WriteFrameBuffer
|
|
|
|
WriteFrameBuffer::WriteFrameBuffer()
|
|
: FrameBufferBase(GL_DRAW_FRAMEBUFFER) {}
|
|
|
|
WriteFrameBuffer& WriteFrameBuffer::operator=(ReadFrameBuffer&& rhs) {
|
|
if (rhs.id()) {
|
|
_attachments = rhs._attachments;
|
|
rhs.setReadBuffer(GL_NONE);
|
|
rhs._attachments = {};
|
|
rhs._location = GL_NONE;
|
|
FrameBufferBase::OpenGLObject::operator=(std::move(rhs));
|
|
}
|
|
return *this;
|
|
}
|
|
|
|
WriteFrameBuffer& WriteFrameBuffer::operator=(WriteFrameBuffer&& rhs) {
|
|
if (rhs.id()) {
|
|
_attachments = rhs._attachments;
|
|
_locations = rhs._locations;
|
|
rhs._attachments = {};
|
|
rhs._locations = {};
|
|
FrameBufferBase::OpenGLObject::operator=(std::move(rhs));
|
|
}
|
|
return *this;
|
|
}
|
|
|
|
using fRGBA = RGBA<float>;
|
|
|
|
std::vector<std::shared_ptr<Texture>> WriteFrameBuffer::getWriteBuffers() const {
|
|
const size_t count = _locations.size();
|
|
std::vector<std::shared_ptr<Texture>> ret(count);
|
|
for (int i = 0; i < count; ++i) {
|
|
ret[i] = std::static_pointer_cast<Texture>(getBuffer(_locations[i]));
|
|
}
|
|
return ret;
|
|
}
|
|
|
|
std::vector<GLenum> WriteFrameBuffer::getWriteLocations() const {
|
|
return _locations;
|
|
}
|
|
|
|
WriteFrameBuffer::DrawTexVec WriteFrameBuffer::setWriteBuffers(const std::vector<GLenum>& attchs_) {
|
|
bind();
|
|
for (GLenum loc : attchs_) {
|
|
if (getBuffer(loc)->Target == GL_RENDERBUFFER) {
|
|
THROW(FrameBufferError(FrameBufferError::INVALID_VALUE,
|
|
"Cannot write to renderbuffer [ @ Attachment = "+std::to_string(loc)+" ]"
|
|
));
|
|
}
|
|
}
|
|
_locations = attchs_;
|
|
OPENGL_CALL(
|
|
glDrawBuffers(_locations.size(), _locations.data())
|
|
);
|
|
return getWriteBuffers();
|
|
}
|
|
|
|
std::shared_ptr<Texture> WriteFrameBuffer::setWriteBuffer(GLenum attch_) {
|
|
bind();
|
|
if (attch_ == GL_NONE) {
|
|
std::vector<GLenum> tmp(_locations.size(), GL_NONE);
|
|
OPENGL_CALL(
|
|
glDrawBuffers(tmp.size(), tmp.data())
|
|
);
|
|
_locations = {};
|
|
return nullptr;
|
|
}
|
|
setWriteBuffers({attch_});
|
|
return std::static_pointer_cast<Texture>(getBuffer(attch_));
|
|
}
|
|
|
|
fRGBA WriteFrameBuffer::clearColorValue() const {
|
|
bind();
|
|
float rgba[4];
|
|
OPENGL_CALL(
|
|
glGetFloatv(GL_COLOR_CLEAR_VALUE, rgba)
|
|
);
|
|
return fRGBA{rgba[0], rgba[1], rgba[2], rgba[3]};
|
|
}
|
|
|
|
fRGBA WriteFrameBuffer::clearColorValue(const fRGBA& color) {
|
|
declare();
|
|
OPENGL_CALL(
|
|
glClearColor(color.r, color.g, color.b, color.a)
|
|
);
|
|
return color;
|
|
}
|
|
|
|
fRGBA WriteFrameBuffer::clearColorValue(float r, float g, float b, float a) {
|
|
return clearColorValue({r, g,b , a});
|
|
}
|
|
|
|
double WriteFrameBuffer::clearDepthValue() const {
|
|
bind();
|
|
double ret;
|
|
OPENGL_CALL(
|
|
glGetDoublev(GL_DEPTH_CLEAR_VALUE, &ret)
|
|
);
|
|
return ret;
|
|
}
|
|
|
|
double WriteFrameBuffer::clearDepthValue(double d_) {
|
|
declare();
|
|
OPENGL_CALL(
|
|
glClearDepth(d_)
|
|
);
|
|
return d_;
|
|
}
|
|
|
|
GLint WriteFrameBuffer::clearStencilValue() const {
|
|
bind();
|
|
GLint ret;
|
|
OPENGL_CALL(
|
|
glGetIntegerv(GL_STENCIL_CLEAR_VALUE, &ret)
|
|
);
|
|
return ret;
|
|
}
|
|
|
|
GLint WriteFrameBuffer::clearStencilValue(GLint s_) {
|
|
declare();
|
|
OPENGL_CALL(
|
|
glClearStencil(s_)
|
|
);
|
|
return s_;
|
|
}
|
|
|
|
void WriteFrameBuffer::clear(GLbitfield mask_) const {
|
|
bind();
|
|
OPENGL_CALL(
|
|
glClear(mask_)
|
|
);
|
|
}
|
|
|
|
void WriteFrameBuffer::clearDepth() const {
|
|
clear(GL_DEPTH_BUFFER_BIT);
|
|
}
|
|
|
|
void WriteFrameBuffer::clearDepth(float val_) const {
|
|
bind();
|
|
OPENGL_CALL(
|
|
glClearBufferfv(GL_DEPTH, 0, &val_)
|
|
);
|
|
}
|
|
|
|
void WriteFrameBuffer::clearStencil() const {
|
|
clear(GL_STENCIL_BUFFER_BIT);
|
|
}
|
|
|
|
void WriteFrameBuffer::clearStencil(GLint val_) const {
|
|
bind();
|
|
OPENGL_CALL(
|
|
glClearBufferiv(GL_STENCIL, 0, &val_)
|
|
);
|
|
}
|
|
|
|
void WriteFrameBuffer::clearDepthStencil() const {
|
|
clear(GL_DEPTH_BUFFER_BIT | GL_STENCIL_BUFFER_BIT);
|
|
}
|
|
|
|
void WriteFrameBuffer::clearDepthStencil(float d_, GLint s_) const {
|
|
bind();
|
|
OPENGL_CALL(
|
|
glClearBufferfi(GL_DEPTH_STENCIL, 0, d_, s_)
|
|
);
|
|
}
|
|
|
|
FrameBuffer::FrameBuffer()
|
|
: FrameBufferBase(GL_FRAMEBUFFER) {}
|
|
|
|
FrameBuffer& FrameBuffer::operator=(FrameBuffer&& rhs) {
|
|
if (rhs.id()) {
|
|
_attachments = rhs._attachments;
|
|
_location = rhs._location;
|
|
_locations = rhs._locations;
|
|
rhs._attachments = {};
|
|
rhs._locations = {};
|
|
rhs._location = GL_NONE;
|
|
OpenGLObject::operator=(std::move(rhs));
|
|
}
|
|
return *this;
|
|
}
|
|
|
|
FrameBuffer& FrameBuffer::operator=(ReadFrameBuffer&& rhs) {
|
|
ReadFrameBuffer::operator=(std::move(rhs));
|
|
return *this;
|
|
}
|
|
|
|
FrameBuffer& FrameBuffer::operator=(WriteFrameBuffer&& rhs) {
|
|
WriteFrameBuffer::operator=(std::move(rhs));
|
|
return *this;
|
|
}
|
|
|
|
} // namespace nb
|