#include 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 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_, 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_, 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 ) { 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 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 ReadFrameBuffer::getReadBuffer() const { if (_location == GL_NONE) { return nullptr; } return getBuffer(_location); } GLenum ReadFrameBuffer::getReadLocation() const { return _location; } std::shared_ptr 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; std::vector> WriteFrameBuffer::getWriteBuffers() const { const size_t count = _locations.size(); std::vector> ret(count); for (int i = 0; i < count; ++i) { ret[i] = std::static_pointer_cast(getBuffer(_locations[i])); } return ret; } std::vector WriteFrameBuffer::getWriteLocations() const { return _locations; } WriteFrameBuffer::DrawTexVec WriteFrameBuffer::setWriteBuffers(const std::vector& 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 WriteFrameBuffer::setWriteBuffer(GLenum attch_) { bind(); if (attch_ == GL_NONE) { std::vector tmp(_locations.size(), GL_NONE); OPENGL_CALL( glDrawBuffers(tmp.size(), tmp.data()) ); _locations = {}; return nullptr; } setWriteBuffers({attch_}); return std::static_pointer_cast(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