diff --git a/Make/makedirdefs b/Make/makedirdefs index c501c2558..d9863231d 100644 --- a/Make/makedirdefs +++ b/Make/makedirdefs @@ -187,6 +187,7 @@ EXAMPLE_DIRS = \ osgcubemap\ osgdepthshadow\ osgdistortion\ + osgfbo\ osgforest\ osgfxbrowser\ osggeodemo\ diff --git a/examples/osgfbo/FBOExtensions.cpp b/examples/osgfbo/FBOExtensions.cpp new file mode 100644 index 000000000..982788421 --- /dev/null +++ b/examples/osgfbo/FBOExtensions.cpp @@ -0,0 +1,38 @@ +#include "FBOExtensions.h" +#include + +using namespace osg; + +#define LOAD_FBO_EXT(name) name = (T##name *)getGLExtensionFuncPtr(#name); + +FBOExtensions::FBOExtensions(unsigned int contextID) +: _supported(false) +{ + if (!isGLExtensionSupported(contextID, "GL_EXT_framebuffer_object")) + return; + + LOAD_FBO_EXT(glBindRenderbufferEXT); + LOAD_FBO_EXT(glGenRenderbuffersEXT); + LOAD_FBO_EXT(glRenderbufferStorageEXT); + LOAD_FBO_EXT(glBindFramebufferEXT); + LOAD_FBO_EXT(glGenFramebuffersEXT); + LOAD_FBO_EXT(glCheckFramebufferStatusEXT); + LOAD_FBO_EXT(glFramebufferTexture1DEXT); + LOAD_FBO_EXT(glFramebufferTexture2DEXT); + LOAD_FBO_EXT(glFramebufferTexture3DEXT); + LOAD_FBO_EXT(glFramebufferRenderbufferEXT); + LOAD_FBO_EXT(glGenerateMipmapEXT); + + _supported = + glBindRenderbufferEXT != 0 && + glGenRenderbuffersEXT != 0 && + glRenderbufferStorageEXT != 0 && + glBindFramebufferEXT != 0 && + glGenFramebuffersEXT != 0 && + glCheckFramebufferStatusEXT != 0 && + glFramebufferTexture1DEXT != 0 && + glFramebufferTexture2DEXT != 0 && + glFramebufferTexture3DEXT != 0 && + glFramebufferRenderbufferEXT != 0 && + glGenerateMipmapEXT != 0; +} diff --git a/examples/osgfbo/FBOExtensions.h b/examples/osgfbo/FBOExtensions.h new file mode 100644 index 000000000..37da7697e --- /dev/null +++ b/examples/osgfbo/FBOExtensions.h @@ -0,0 +1,118 @@ +#ifndef OSG_FBOEXTENSIONS +#define OSG_FBOEXTENSIONS 1 + +#include +#include + +#ifndef GL_EXT_framebuffer_object +#define GL_EXT_framebuffer_object 1 +#define GL_FRAMEBUFFER_EXT 0x8D40 +#define GL_RENDERBUFFER_EXT 0x8D41 +#define GL_STENCIL_INDEX_EXT 0x8D45 +#define GL_STENCIL_INDEX1_EXT 0x8D46 +#define GL_STENCIL_INDEX4_EXT 0x8D47 +#define GL_STENCIL_INDEX8_EXT 0x8D48 +#define GL_STENCIL_INDEX16_EXT 0x8D49 +#define GL_RENDERBUFFER_WIDTH_EXT 0x8D42 +#define GL_RENDERBUFFER_HEIGHT_EXT 0x8D43 +#define GL_RENDERBUFFER_INTERNAL_FORMAT_EXT 0x8D44 +#define GL_RENDERBUFFER_RED_SIZE_EXT 0x8D50 +#define GL_RENDERBUFFER_GREEN_SIZE_EXT 0x8D51 +#define GL_RENDERBUFFER_BLUE_SIZE_EXT 0x8D52 +#define GL_RENDERBUFFER_ALPHA_SIZE_EXT 0x8D53 +#define GL_RENDERBUFFER_DEPTH_SIZE_EXT 0x8D54 +#define GL_RENDERBUFFER_STENCIL_SIZE_EXT 0x8D55 +#define GL_FRAMEBUFFER_ATTACHMENT_OBJECT_TYPE_EXT 0x8CD0 +#define GL_FRAMEBUFFER_ATTACHMENT_OBJECT_NAME_EXT 0x8CD1 +#define GL_FRAMEBUFFER_ATTACHMENT_TEXTURE_LEVEL_EXT 0x8CD2 +#define GL_FRAMEBUFFER_ATTACHMENT_TEXTURE_CUBE_MAP_FACE_EXT 0x8CD3 +#define GL_FRAMEBUFFER_ATTACHMENT_TEXTURE_3D_ZOFFSET_EXT 0x8CD4 +#define GL_COLOR_ATTACHMENT0_EXT 0x8CE0 +#define GL_COLOR_ATTACHMENT1_EXT 0x8CE1 +#define GL_COLOR_ATTACHMENT2_EXT 0x8CE2 +#define GL_COLOR_ATTACHMENT3_EXT 0x8CE3 +#define GL_COLOR_ATTACHMENT4_EXT 0x8CE4 +#define GL_COLOR_ATTACHMENT5_EXT 0x8CE5 +#define GL_COLOR_ATTACHMENT6_EXT 0x8CE6 +#define GL_COLOR_ATTACHMENT7_EXT 0x8CE7 +#define GL_COLOR_ATTACHMENT8_EXT 0x8CE8 +#define GL_COLOR_ATTACHMENT9_EXT 0x8CE9 +#define GL_COLOR_ATTACHMENT10_EXT 0x8CEA +#define GL_COLOR_ATTACHMENT11_EXT 0x8CEB +#define GL_COLOR_ATTACHMENT12_EXT 0x8CEC +#define GL_COLOR_ATTACHMENT13_EXT 0x8CED +#define GL_COLOR_ATTACHMENT14_EXT 0x8CEE +#define GL_COLOR_ATTACHMENT15_EXT 0x8CEF +#define GL_DEPTH_ATTACHMENT_EXT 0x8D00 +#define GL_STENCIL_ATTACHMENT_EXT 0x8D20 +#define GL_FRAMEBUFFER_COMPLETE_EXT 0x8CD5 +#define GL_FRAMEBUFFER_INCOMPLETE_ATTACHMENT_EXT 0x8CD6 +#define GL_FRAMEBUFFER_INCOMPLETE_MISSING_ATTACHMENT_EXT 0x8CD7 +#define GL_FRAMEBUFFER_INCOMPLETE_DUPLICATE_ATTACHMENT_EXT 0x8CD8 +#define GL_FRAMEBUFFER_INCOMPLETE_DIMENSIONS_EXT 0x8CD9 +#define GL_FRAMEBUFFER_INCOMPLETE_FORMATS_EXT 0x8CDA +#define GL_FRAMEBUFFER_INCOMPLETE_DRAW_BUFFER_EXT 0x8CDB +#define GL_FRAMEBUFFER_INCOMPLETE_READ_BUFFER_EXT 0x8CDC +#define GL_FRAMEBUFFER_UNSUPPORTED_EXT 0x8CDD +#define GL_FRAMEBUFFER_BINDING_EXT 0x8CA6 +#define GL_RENDERBUFFER_BINDING_EXT 0x8CA7 +#define GL_MAX_COLOR_ATTACHMENTS_EXT 0x8CDF +#define GL_MAX_RENDERBUFFER_SIZE_EXT 0x84E8 +#define GL_INVALID_FRAMEBUFFER_OPERATION_EXT 0x0506 +#endif + +namespace osg +{ + + class FBOExtensions + { + public: + typedef void APIENTRY TglBindRenderbufferEXT(GLenum, GLuint); + typedef void APIENTRY TglGenRenderbuffersEXT(GLsizei, GLuint *); + typedef void APIENTRY TglRenderbufferStorageEXT(GLenum, GLenum, GLsizei, GLsizei); + typedef void APIENTRY TglBindFramebufferEXT(GLenum, GLuint); + typedef void APIENTRY TglGenFramebuffersEXT(GLsizei, GLuint *); + typedef GLenum APIENTRY TglCheckFramebufferStatusEXT(GLenum); + typedef void APIENTRY TglFramebufferTexture1DEXT(GLenum, GLenum, GLenum, GLuint, GLint); + typedef void APIENTRY TglFramebufferTexture2DEXT(GLenum, GLenum, GLenum, GLuint, GLint); + typedef void APIENTRY TglFramebufferTexture3DEXT(GLenum, GLenum, GLenum, GLuint, GLint, GLint); + typedef void APIENTRY TglFramebufferRenderbufferEXT(GLenum, GLenum, GLenum, GLuint); + typedef void APIENTRY TglGenerateMipmapEXT(GLenum); + + TglBindRenderbufferEXT *glBindRenderbufferEXT; + TglGenRenderbuffersEXT *glGenRenderbuffersEXT; + TglRenderbufferStorageEXT *glRenderbufferStorageEXT; + TglBindFramebufferEXT *glBindFramebufferEXT; + TglGenFramebuffersEXT *glGenFramebuffersEXT; + TglCheckFramebufferStatusEXT *glCheckFramebufferStatusEXT; + TglFramebufferTexture1DEXT *glFramebufferTexture1DEXT; + TglFramebufferTexture2DEXT *glFramebufferTexture2DEXT; + TglFramebufferTexture3DEXT *glFramebufferTexture3DEXT; + TglFramebufferRenderbufferEXT *glFramebufferRenderbufferEXT; + TglGenerateMipmapEXT *glGenerateMipmapEXT; + + static FBOExtensions *instance(unsigned contextID) + { + static buffered_object _instances; + FBOExtensions *ext = _instances[contextID]; + if (!ext) + { + ext = new FBOExtensions(contextID); + _instances[contextID] = ext; + } + return ext; + } + + bool isSupported() const { return _supported; } + + protected: + FBOExtensions(unsigned int contextID); + + private: + bool _supported; + }; + +} + +#endif + diff --git a/examples/osgfbo/FramebufferAttachment.cpp b/examples/osgfbo/FramebufferAttachment.cpp new file mode 100644 index 000000000..d58c3d931 --- /dev/null +++ b/examples/osgfbo/FramebufferAttachment.cpp @@ -0,0 +1,173 @@ +#include "FramebufferAttachment.h" +#include "Renderbuffer.h" +#include "FBOExtensions.h" +#include +#include +#include +#include +#include +#include + +using namespace osg; + +#ifndef GL_TEXTURE_CUBE_MAP_POSITIVE_X +#define GL_TEXTURE_CUBE_MAP_POSITIVE_X 0x8515 +#endif + +struct FramebufferAttachment::Pimpl +{ + enum TargetType + { + RENDERBUFFER, + TEXTURE1D, + TEXTURE2D, + TEXTURE3D, + TEXTURECUBE, + TEXTURERECT + }; + + TargetType targetType; + ref_ptr renderbufferTarget; + ref_ptr textureTarget; + int cubeMapFace; + int level; + int zoffset; + + explicit Pimpl(TargetType ttype = RENDERBUFFER, int lev = 0) + : targetType(ttype), + cubeMapFace(0), + level(lev), + zoffset(0) + { + } + + Pimpl(const Pimpl ©) + : targetType(copy.targetType), + renderbufferTarget(copy.renderbufferTarget), + textureTarget(copy.textureTarget), + cubeMapFace(copy.cubeMapFace), + level(copy.level), + zoffset(copy.zoffset) + { + } +}; + +FramebufferAttachment::FramebufferAttachment() +{ + _ximpl = new Pimpl; +} + +FramebufferAttachment::FramebufferAttachment(const FramebufferAttachment ©) +{ + _ximpl = new Pimpl(*copy._ximpl); +} + +FramebufferAttachment::FramebufferAttachment(Renderbuffer* target) +{ + _ximpl = new Pimpl(Pimpl::RENDERBUFFER); + _ximpl->renderbufferTarget = target; +} + +FramebufferAttachment::FramebufferAttachment(Texture1D* target, int level) +{ + _ximpl = new Pimpl(Pimpl::TEXTURE1D, level); + _ximpl->textureTarget = target; +} + +FramebufferAttachment::FramebufferAttachment(Texture2D* target, int level) +{ + _ximpl = new Pimpl(Pimpl::TEXTURE2D, level); + _ximpl->textureTarget = target; +} + +FramebufferAttachment::FramebufferAttachment(Texture3D* target, int level, int zoffset) +{ + _ximpl = new Pimpl(Pimpl::TEXTURE3D, level); + _ximpl->textureTarget = target; + _ximpl->zoffset = zoffset; +} + +FramebufferAttachment::FramebufferAttachment(TextureCubeMap* target, int face, int level) +{ + _ximpl = new Pimpl(Pimpl::TEXTURECUBE, level); + _ximpl->textureTarget = target; + _ximpl->cubeMapFace = face; +} + +FramebufferAttachment::FramebufferAttachment(TextureRectangle* target) +{ + _ximpl = new Pimpl(Pimpl::TEXTURERECT); + _ximpl->textureTarget = target; +} + +FramebufferAttachment::~FramebufferAttachment() +{ + delete _ximpl; +} + +FramebufferAttachment &FramebufferAttachment::operator = (const FramebufferAttachment ©) +{ + delete _ximpl; + _ximpl = new Pimpl(*copy._ximpl); + return *this; +} + +void FramebufferAttachment::attach(State &state, GLenum attachment_point, const FBOExtensions* ext) const +{ + unsigned int contextID = state.getContextID(); + + // force compile texture if necessary + Texture::TextureObject *tobj = 0; + if (_ximpl->textureTarget.valid()) + { + tobj = _ximpl->textureTarget->getTextureObject(contextID); + if (!tobj || tobj->_id == 0) + { + _ximpl->textureTarget->compileGLObjects(state); + tobj = _ximpl->textureTarget->getTextureObject(contextID); + } + if (!tobj || tobj->_id == 0) + return; + } + + switch (_ximpl->targetType) + { + default: + case Pimpl::RENDERBUFFER: + ext->glFramebufferRenderbufferEXT(GL_FRAMEBUFFER_EXT, attachment_point, GL_RENDERBUFFER_EXT, _ximpl->renderbufferTarget->getObjectID(contextID, ext)); + break; + case Pimpl::TEXTURE1D: + ext->glFramebufferTexture1DEXT(GL_FRAMEBUFFER_EXT, attachment_point, GL_TEXTURE_1D, tobj->_id, _ximpl->level); + break; + case Pimpl::TEXTURE2D: + ext->glFramebufferTexture2DEXT(GL_FRAMEBUFFER_EXT, attachment_point, GL_TEXTURE_2D, tobj->_id, _ximpl->level); + break; + case Pimpl::TEXTURE3D: + ext->glFramebufferTexture3DEXT(GL_FRAMEBUFFER_EXT, attachment_point, GL_TEXTURE_3D, tobj->_id, _ximpl->level, _ximpl->zoffset); + break; + case Pimpl::TEXTURERECT: + ext->glFramebufferTexture2DEXT(GL_FRAMEBUFFER_EXT, attachment_point, GL_TEXTURE_RECTANGLE, tobj->_id, 0); + break; + case Pimpl::TEXTURECUBE: + ext->glFramebufferTexture2DEXT(GL_FRAMEBUFFER_EXT, attachment_point, GL_TEXTURE_CUBE_MAP_POSITIVE_X + _ximpl->cubeMapFace, tobj->_id, _ximpl->level); + break; + } +} + +int FramebufferAttachment::compare(const FramebufferAttachment &fa) const +{ + if (&fa == this) return 0; + if (_ximpl->targetType < fa._ximpl->targetType) return -1; + if (_ximpl->targetType > fa._ximpl->targetType) return 1; + if (_ximpl->renderbufferTarget.get() < fa._ximpl->renderbufferTarget.get()) return -1; + if (_ximpl->renderbufferTarget.get() > fa._ximpl->renderbufferTarget.get()) return 1; + if (_ximpl->textureTarget.get() < fa._ximpl->textureTarget.get()) return -1; + if (_ximpl->textureTarget.get() > fa._ximpl->textureTarget.get()) return 1; + if (_ximpl->cubeMapFace < fa._ximpl->cubeMapFace) return -1; + if (_ximpl->cubeMapFace > fa._ximpl->cubeMapFace) return 1; + if (_ximpl->level < fa._ximpl->level) return -1; + if (_ximpl->level > fa._ximpl->level) return 1; + if (_ximpl->zoffset < fa._ximpl->zoffset) return -1; + if (_ximpl->zoffset > fa._ximpl->zoffset) return 1; + return 0; +} diff --git a/examples/osgfbo/FramebufferAttachment.h b/examples/osgfbo/FramebufferAttachment.h new file mode 100644 index 000000000..2c24b37ad --- /dev/null +++ b/examples/osgfbo/FramebufferAttachment.h @@ -0,0 +1,48 @@ +#ifndef OSG_FRAMEBUFFERATTACHMENT +#define OSG_FRAMEBUFFERATTACHMENT 1 + +#include +#include + +namespace osg +{ + + class FBOExtensions; + + class Renderbuffer; + class Texture1D; + class Texture2D; + class Texture3D; + class TextureCubeMap; + class TextureRectangle; + + class FramebufferAttachment + { + public: + FramebufferAttachment(); + FramebufferAttachment(const FramebufferAttachment ©); + + explicit FramebufferAttachment(Renderbuffer* target); + explicit FramebufferAttachment(Texture1D* target, int level = 0); + explicit FramebufferAttachment(Texture2D* target, int level = 0); + explicit FramebufferAttachment(Texture3D* target, int zoffset, int level = 0); + explicit FramebufferAttachment(TextureCubeMap* target, int face, int level = 0); + explicit FramebufferAttachment(TextureRectangle* target); + + ~FramebufferAttachment(); + + FramebufferAttachment &operator = (const FramebufferAttachment ©); + + void attach(State &state, GLenum attachment_point, const FBOExtensions* ext) const; + int compare(const FramebufferAttachment &fa) const; + + private: + // use the Pimpl idiom to avoid dependency from + // all Texture* headers + struct Pimpl; + Pimpl* _ximpl; + }; + +} + +#endif diff --git a/examples/osgfbo/FramebufferObject.cpp b/examples/osgfbo/FramebufferObject.cpp new file mode 100644 index 000000000..a40b2061b --- /dev/null +++ b/examples/osgfbo/FramebufferObject.cpp @@ -0,0 +1,79 @@ +#include "FramebufferObject.h" +#include "FBOExtensions.h" +#include +#include + +using namespace osg; + +FramebufferObject::FramebufferObject() +: StateAttribute() +{ +} + +FramebufferObject::FramebufferObject(const FramebufferObject ©, const CopyOp ©op) +: StateAttribute(copy, copyop), + _attachments(copy._attachments) +{ +} + +void FramebufferObject::apply(State &state) const +{ + unsigned int contextID = state.getContextID(); + + if (_unsupported[contextID]) + return; + + FBOExtensions* ext = FBOExtensions::instance(contextID); + if (!ext->isSupported()) + { + _unsupported[contextID] = 1; + notify(WARN) << "Warning: EXT_framebuffer_object is not supported" << std::endl; + return; + } + + if (_attachments.empty()) + { + ext->glBindFramebufferEXT(GL_FRAMEBUFFER_EXT, 0); + return; + } + + int &dirtyAttachmentList = _dirtyAttachmentList[contextID]; + + GLuint &fboID = _fboID[contextID]; + if (fboID == 0) + { + ext->glGenFramebuffersEXT(1, &fboID); + if (fboID == 0) + { + notify(WARN) << "Warning: FramebufferObject: could not create the FBO" << std::endl; + return; + } + dirtyAttachmentList = 1; + } + + ext->glBindFramebufferEXT(GL_FRAMEBUFFER_EXT, fboID); + + if (dirtyAttachmentList) + { + for (AttachmentMap::const_iterator i=_attachments.begin(); i!=_attachments.end(); ++i) + { + const FramebufferAttachment &fa = i->second; + fa.attach(state, i->first, ext); + } + dirtyAttachmentList = 0; + } +} + +int FramebufferObject::compare(const StateAttribute &sa) const +{ + COMPARE_StateAttribute_Types(FramebufferObject, sa); + COMPARE_StateAttribute_Parameter(_attachments.size()); + AttachmentMap::const_iterator i = _attachments.begin(); + AttachmentMap::const_iterator j = rhs._attachments.begin(); + for (; i!=_attachments.end(); ++i, ++j) + { + int cmp = i->second.compare(j->second); + if (cmp != 0) return cmp; + } + return 0; +} diff --git a/examples/osgfbo/FramebufferObject.h b/examples/osgfbo/FramebufferObject.h new file mode 100644 index 000000000..bba60e2be --- /dev/null +++ b/examples/osgfbo/FramebufferObject.h @@ -0,0 +1,73 @@ +#ifndef OSG_FRAMEBUFFEROBJECT +#define OSG_FRAMEBUFFEROBJECT 1 + +#include +#include + +#include "FramebufferAttachment.h" + +namespace osg +{ + + class FramebufferObject: public StateAttribute + { + public: + typedef std::map AttachmentMap; + + FramebufferObject(); + FramebufferObject(const FramebufferObject ©, const CopyOp ©op = CopyOp::SHALLOW_COPY); + + META_StateAttribute(osg, FramebufferObject, (StateAttribute::Type)0x101010/*FRAMEBUFFEROBJECT*/); + + inline const AttachmentMap &getAttachmentMap() const; + inline bool hasAttachment(GLenum attachment_point) const; + inline const FramebufferAttachment& getAttachment(GLenum attachment_point) const; + inline void setAttachment(GLenum attachment_point, const FramebufferAttachment &attachment); + + int compare(const StateAttribute &sa) const; + void apply(State &state) const; + + protected: + virtual ~FramebufferObject() {} + FramebufferObject &operator=(const FramebufferObject &) { return *this; } + + inline void dirtyAll(); + + private: + AttachmentMap _attachments; + mutable buffered_value _dirtyAttachmentList; + mutable buffered_value _unsupported; + mutable buffered_value _fboID; + }; + + // INLINE METHODS + + inline const FramebufferObject::AttachmentMap &FramebufferObject::getAttachmentMap() const + { + return _attachments; + } + + inline bool FramebufferObject::hasAttachment(GLenum attachment_point) const + { + return _attachments.find(attachment_point) != _attachments.end(); + } + + inline const FramebufferAttachment &FramebufferObject::getAttachment(GLenum attachment_point) const + { + return _attachments.find(attachment_point)->second; + } + + inline void FramebufferObject::setAttachment(GLenum attachment_point, const FramebufferAttachment &attachment) + { + _attachments[attachment_point] = attachment; + dirtyAll(); + } + + inline void FramebufferObject::dirtyAll() + { + _dirtyAttachmentList.setAllElementsTo(1); + } + +} + +#endif diff --git a/examples/osgfbo/GNUmakefile b/examples/osgfbo/GNUmakefile new file mode 100644 index 000000000..abdaa848b --- /dev/null +++ b/examples/osgfbo/GNUmakefile @@ -0,0 +1,22 @@ +TOPDIR = ../.. +include $(TOPDIR)/Make/makedefs + +CXXFILES =\ + FBOExtensions.cpp\ + FramebufferObject.cpp\ + Renderbuffer.cpp\ + FramebufferAttachment.cpp\ + fbo.cpp\ + +LIBS += -losgProducer -lProducer -losgText -losgGA -losgDB -losgUtil -losg $(GL_LIBS) $(X_LIBS) $(OTHER_LIBS) + +INSTFILES = \ + $(CXXFILES)\ + GNUmakefile.inst=GNUmakefile + +EXEC = osgfbo + +INC += $(X_INC) + +include $(TOPDIR)/Make/makerules + diff --git a/examples/osgfbo/GNUmakefile.inst b/examples/osgfbo/GNUmakefile.inst new file mode 100644 index 000000000..59d1b0f8d --- /dev/null +++ b/examples/osgfbo/GNUmakefile.inst @@ -0,0 +1,17 @@ +TOPDIR = ../.. +include $(TOPDIR)/Make/makedefs + +CXXFILES =\ + FBOExtensions.cpp\ + FramebufferObject.cpp\ + Renderbuffer.cpp\ + FramebufferAttachment.cpp\ + fbo.cpp\ + +LIBS += -losgProducer -lProducer -losgDB -losgText -losgUtil -losg $(GL_LIBS) $(X_LIBS) $(OTHER_LIBS) + +EXEC = osgfbo + +INC += $(X_INC) + +include $(TOPDIR)/Make/makerules diff --git a/examples/osgfbo/Renderbuffer.cpp b/examples/osgfbo/Renderbuffer.cpp new file mode 100644 index 000000000..8061ea191 --- /dev/null +++ b/examples/osgfbo/Renderbuffer.cpp @@ -0,0 +1,53 @@ +#include "Renderbuffer.h" +#include "FBOExtensions.h" + +using namespace osg; + +Renderbuffer::Renderbuffer() +: Object(), + _width(512), + _height(512), + _internalFormat(GL_DEPTH_COMPONENT24) +{ +} + +Renderbuffer::Renderbuffer(int width, int height, GLenum internalFormat) +: Object(), + _width(width), + _height(height), + _internalFormat(internalFormat) +{ +} + +Renderbuffer::Renderbuffer(const Renderbuffer ©, const CopyOp ©op) +: Object(copy, copyop), + _width(copy._width), + _height(copy._height), + _internalFormat(copy._internalFormat) +{ +} + +GLuint Renderbuffer::getObjectID(unsigned int contextID, const FBOExtensions *ext) const +{ + GLuint &objectID = _objectID[contextID]; + + int &dirty = _dirty[contextID]; + + if (objectID == 0) + { + ext->glGenRenderbuffersEXT(1, &objectID); + if (objectID == 0) + return 0; + dirty = 1; + } + + if (dirty) + { + // bind and configure + ext->glBindRenderbufferEXT(GL_RENDERBUFFER_EXT, objectID); + ext->glRenderbufferStorageEXT(GL_RENDERBUFFER_EXT, _internalFormat, _width, _height); + dirty = 0; + } + + return objectID; +} diff --git a/examples/osgfbo/Renderbuffer.h b/examples/osgfbo/Renderbuffer.h new file mode 100644 index 000000000..25fd4d5ef --- /dev/null +++ b/examples/osgfbo/Renderbuffer.h @@ -0,0 +1,114 @@ +#ifndef OSG_RENDERBUFFER +#define OSG_RENDERBUFFER 1 + +#include +#include +#include + +#ifndef GL_VERSION_1_4 +#define GL_DEPTH_COMPONENT16 0x81A5 +#define GL_DEPTH_COMPONENT24 0x81A6 +#define GL_DEPTH_COMPONENT32 0x81A7 +#endif + +namespace osg +{ + + class FBOExtensions; + + class Renderbuffer: public Object + { + public: + Renderbuffer(); + Renderbuffer(int width, int height, GLenum internalFormat); + Renderbuffer(const Renderbuffer ©, const CopyOp ©op = CopyOp::SHALLOW_COPY); + + META_Object(osg, Renderbuffer); + + inline int getWidth() const; + inline int getHeight() const; + inline void setWidth(int w); + inline void setHeight(int h); + inline void setSize(int w, int h); + inline GLenum getInternalFormat() const; + inline void setInternalFormat(GLenum format); + + GLuint getObjectID(unsigned int contextID, const FBOExtensions *ext) const; + inline int compare(const Renderbuffer &rb) const; + + protected: + virtual ~Renderbuffer() {} + Renderbuffer &operator=(const Renderbuffer &) { return *this; } + + inline void dirtyAll() const; + + private: + mutable buffered_value _objectID; + mutable buffered_value _dirty; + GLenum _internalFormat; + int _width; + int _height; + }; + + // INLINE METHODS + + inline int Renderbuffer::getWidth() const + { + return _width; + } + + inline int Renderbuffer::getHeight() const + { + return _height; + } + + inline void Renderbuffer::setWidth(int w) + { + _width = w; + dirtyAll(); + } + + inline void Renderbuffer::setHeight(int h) + { + _height = h; + dirtyAll(); + } + + inline void Renderbuffer::setSize(int w, int h) + { + _width = w; + _height = h; + dirtyAll(); + } + + inline GLenum Renderbuffer::getInternalFormat() const + { + return _internalFormat; + } + + inline void Renderbuffer::setInternalFormat(GLenum format) + { + _internalFormat = format; + dirtyAll(); + } + + inline void Renderbuffer::dirtyAll() const + { + _dirty.setAllElementsTo(1); + } + + inline int Renderbuffer::compare(const Renderbuffer &rb) const + { + if (&rb == this) return 0; + if (_internalFormat < rb._internalFormat) return -1; + if (_internalFormat > rb._internalFormat) return 1; + if (_width < rb._width) return -1; + if (_width > rb._width) return 1; + if (_height < rb._height) return -1; + if (_height > rb._height) return 1; + return 0; + } + +} + +#endif diff --git a/examples/osgfbo/osgfbo.cpp b/examples/osgfbo/osgfbo.cpp new file mode 100644 index 000000000..b2f06b68f --- /dev/null +++ b/examples/osgfbo/osgfbo.cpp @@ -0,0 +1,158 @@ +#include +#include +#include +#include +#include +#include +#include + +#include + +#include + +#include "FramebufferObject.h" +#include "FramebufferAttachment.h" +#include "Renderbuffer.h" +#include "FBOExtensions.h" + +// This drawable class provides the means for clearing +// the color and depth buffer and it is used in this +// example to clear the offscreen buffers. Of course +// this is just a trick, but I couldn't think of any +// cleaner way to do that (ClearNode always affects +// the main buffers). +class ClearBuffer: public osg::Drawable +{ +public: + ClearBuffer() {} + ClearBuffer(const ClearBuffer ©, const osg::CopyOp ©op = osg::CopyOp::SHALLOW_COPY): osg::Drawable(copy, copyop) {} + META_Object(example, ClearBuffer); + void drawImplementation(osg::State &state) const + { + glPushAttrib(GL_COLOR_BUFFER_BIT); + glClearColor(0.5f, 0.1f, 0.1f, 1.0f); + glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT); + glPopAttrib(); + } +}; + +// This function builds a textured quad +osg::Node *build_quad(osg::Texture2D *tex) +{ + osg::Geometry *geo = new osg::Geometry; + osg::Vec3Array *vx = new osg::Vec3Array; + vx->push_back(osg::Vec3(-10, 0, -10)); + vx->push_back(osg::Vec3(10, 0, -10)); + vx->push_back(osg::Vec3(10, 0, 10)); + vx->push_back(osg::Vec3(-10, 0, 10)); + geo->setVertexArray(vx); + osg::Vec3Array *nx = new osg::Vec3Array; + nx->push_back(osg::Vec3(0, -1, 0)); + geo->setNormalArray(nx); + geo->setNormalBinding(osg::Geometry::BIND_OVERALL); + osg::Vec2Array *tx = new osg::Vec2Array; + tx->push_back(osg::Vec2(0, 0)); + tx->push_back(osg::Vec2(1, 0)); + tx->push_back(osg::Vec2(1, 1)); + tx->push_back(osg::Vec2(0, 1)); + geo->setTexCoordArray(0, tx); + geo->addPrimitiveSet(new osg::DrawArrays(GL_QUADS, 0, 4)); + geo->getOrCreateStateSet()->setTextureAttributeAndModes(0, tex); + + osg::Geode *geode = new osg::Geode; + geode->addDrawable(geo); + return geode; +} + +// This class is provided because osg::Texture classes +// don't apply texture parameters when the image pointer +// is null. When performing render-to-texture, texture +// data must be applied as usual but a null pointer must +// be specified as pixel data. +// This is another trick: it would be better to allow +// null image pointers in osg::Texture classes. +struct MySubload: public osg::Texture2D::SubloadCallback +{ + void load(const osg::Texture2D &texture, osg::State &state) const + { + glTexImage2D(GL_TEXTURE_2D, 0, texture.getInternalFormat(), texture.getTextureWidth(), texture.getTextureHeight(), texture.getBorderWidth(), GL_RGBA, GL_UNSIGNED_BYTE, 0); + } + + void subload(const osg::Texture2D &texture, osg::State &state) const + { + } +}; + +void build_world(osg::Group *root) +{ + int width = 512; + int height = 512; + + // create and configure the texture that we're going + // to use as target for render-to-texture + osg::ref_ptr tex = new osg::Texture2D; + tex->setTextureSize(width, height); + tex->setInternalFormat(GL_RGBA); + tex->setFilter(osg::Texture::MIN_FILTER, osg::Texture::LINEAR); + tex->setFilter(osg::Texture::MAG_FILTER, osg::Texture::LINEAR); + tex->setWrap(osg::Texture::WRAP_S, osg::Texture::CLAMP_TO_EDGE); + tex->setWrap(osg::Texture::WRAP_T, osg::Texture::CLAMP_TO_EDGE); + tex->setSubloadCallback(new MySubload); + + // create and configure a framebuffer object. + // We attach the texture to the first color buffer, + // and we attach a simple offscreen image (Renderbuffer) + // to the depth buffer in order to allow depth operations + osg::ref_ptr fbo = new osg::FramebufferObject(); + fbo->setAttachment(GL_COLOR_ATTACHMENT0_EXT, osg::FramebufferAttachment(tex.get())); + fbo->setAttachment(GL_DEPTH_ATTACHMENT_EXT, osg::FramebufferAttachment(new osg::Renderbuffer(width, height, GL_DEPTH_COMPONENT24))); + + // create a subgraph that will be rendered to texture. + // We apply the previously created FBO and a Viewport + // attribute to this subgraph. + osg::ref_ptr offscreen = new osg::MatrixTransform; + root->addChild(offscreen.get()); + offscreen->getOrCreateStateSet()->setRenderBinDetails(-1, "RenderBin"); + offscreen->getOrCreateStateSet()->setAttribute(fbo.get()); + offscreen->getOrCreateStateSet()->setAttribute(new osg::Viewport(0, 0, width, height)); + offscreen->setMatrix(osg::Matrix::lookAt(osg::Vec3(0, -20, 0), osg::Vec3(0, 0, 0), osg::Vec3(0, 0, 1))); + offscreen->setReferenceFrame(osg::Transform::ABSOLUTE_RF); + + // add a ClearBuffer drawable to the offscreen subgraph + // in order to clear the color and depth buffers + osg::ref_ptr cbuf = new osg::Geode; + cbuf->addDrawable(new ClearBuffer); + cbuf->getOrCreateStateSet()->setRenderBinDetails(-2, "RenderBin"); + offscreen->addChild(cbuf.get()); + + // add our beloved cow the offscreen subgraph + offscreen->addChild(osgDB::readNodeFile("cow.osg")); + + // now create a simple quad that will be rendered + // in the main framebuffers. The quad's texture + // will be the content of the FBO's color buffer + root->addChild(build_quad(tex.get())); +} + +int main() +{ + osg::ref_ptr root = new osg::Group; + build_world(root.get()); + + osgProducer::Viewer viewer; + viewer.setUpViewer(); + viewer.setSceneData(root.get()); + viewer.realize(); + + while (!viewer.done()) + { + viewer.sync(); + viewer.update(); + viewer.frame(); + } + + viewer.sync(); + viewer.cleanup_frame(); + viewer.sync(); + return 0; +}