diff --git a/examples/CMakeLists.txt b/examples/CMakeLists.txt index 671ff2d7c..20aedbf23 100644 --- a/examples/CMakeLists.txt +++ b/examples/CMakeLists.txt @@ -40,6 +40,7 @@ IF(DYNAMIC_OPENSCENEGRAPH) ADD_SUBDIRECTORY(osgfadetext) ADD_SUBDIRECTORY(osgfont) ADD_SUBDIRECTORY(osgforest) + ADD_SUBDIRECTORY(osgfpdepth) ADD_SUBDIRECTORY(osgfxbrowser) ADD_SUBDIRECTORY(osgoutline) ADD_SUBDIRECTORY(osggameoflife) diff --git a/examples/osgfpdepth/CMakeLists.txt b/examples/osgfpdepth/CMakeLists.txt new file mode 100644 index 000000000..cf54fa5ce --- /dev/null +++ b/examples/osgfpdepth/CMakeLists.txt @@ -0,0 +1,4 @@ +SET(TARGET_SRC osgfpdepth.cpp ) + +#### end var setup ### +SETUP_EXAMPLE(osgfpdepth) diff --git a/examples/osgfpdepth/osgfpdepth.cpp b/examples/osgfpdepth/osgfpdepth.cpp new file mode 100644 index 000000000..2c32adc39 --- /dev/null +++ b/examples/osgfpdepth/osgfpdepth.cpp @@ -0,0 +1,1007 @@ +/* OpenSceneGraph example, osgfpdepth. +* +* Permission is hereby granted, free of charge, to any person obtaining a copy +* of this software and associated documentation files (the "Software"), to deal +* in the Software without restriction, including without limitation the rights +* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +* copies of the Software, and to permit persons to whom the Software is +* furnished to do so, subject to the following conditions: +* +* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +* THE SOFTWARE. +*/ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include + +#include + +#include +#include +#include + +#include +#include + +/* Demonstration of floating point depth buffers. The most basic way to use + * a floating point depth buffer in OpenGL is to create a frame buffer + * object, attach a color and floating point depth texture, render, + * and then copy the color texture to the screen. When doing + * multisampling we can't use textures directly, so we have to create + * render buffers with the proper format. Then we let OSG handle the + * details of resolving the multisampling. + * + * When using a floating point depth buffer, it's advantageous to + * reverse the depth buffer range (and the depth test, of course) so + * that 0.0 corresponds to the far plane. See + * e.g. http://www.humus.name/index.php?ID=25 for details. + */ +using namespace osg; +using namespace std; + +/* createFBO() and destroyFBO(), and the supporting classes and + * functions below, are only used to test possible valid frame buffer + * configurations at startup. They wouldn't be used in a normal OSG + * program unless we wanted to enumerate all the valid FBO + * combinations and let the user choose between them. + */ + +// Properties of an FBO that we will try to create +struct FboConfig +{ + FboConfig() + : colorFormat(0), depthFormat(0), redbits(0), depthBits(0), + depthSamples(0), coverageSamples(0) + { + } + FboConfig(const string& name_, GLenum colorFormat_, GLenum depthFormat_, + int redbits_, int depthBits_, int depthSamples_ = 0, + int coverageSamples_ = 0) + : name(name_), colorFormat(colorFormat_), depthFormat(depthFormat_), + redbits(redbits_), depthBits(depthBits_), depthSamples(depthSamples_), + coverageSamples(coverageSamples_) + { + } + string name; + GLenum colorFormat; + GLenum depthFormat; + int redbits; + int depthBits; + int depthSamples; + int coverageSamples; +}; + +// Properties of a buffer +struct BufferConfig +{ + BufferConfig() {} + BufferConfig(const string& name_, GLenum format_, int bits_) + : name(name_), format(format_), bits(bits_) + { + } + string name; + GLenum format; + int bits; +}; + +typedef vector BufferConfigList; + +vector validConfigs; +// Ugly global variables for the viewport width and height +int width, height; + +// This is only used when testing possible frame buffer configurations +// to find valid ones. +struct FboData +{ + ref_ptr tex; // color texture + ref_ptr depthTex; // depth texture + ref_ptr fb; // render framebuffer + ref_ptr resolveFB; // multisample resolve target +}; + +Texture2D* makeDepthTexture(int width, int height, GLenum internalFormat); + +// Assemble lists of the valid buffer configurations, along with the +// possibilities for multisample coverage antialiasing, if any. +void getPossibleConfigs(GraphicsContext* gc, BufferConfigList& colorConfigs, + BufferConfigList& depthConfigs, + vector& coverageConfigs) +{ + int maxSamples = 0; + int coverageSampleConfigs = 0; + unsigned contextID = gc->getState()->getContextID(); + colorConfigs.push_back(BufferConfig("RGBA8", GL_RGBA8, 8)); + depthConfigs.push_back(BufferConfig("D24", GL_DEPTH_COMPONENT24, 24)); + FBOExtensions* fboe = FBOExtensions::instance(contextID, true); + if (!fboe->isSupported()) + return; + if (fboe->isMultisampleSupported()) + glGetIntegerv(GL_MAX_SAMPLES_EXT, &maxSamples); + // isMultisampleCoverageSupported + if (isGLExtensionSupported(contextID, + "GL_NV_framebuffer_multisample_coverage")) + { + glGetIntegerv(GL_MAX_MULTISAMPLE_COVERAGE_MODES_NV, + &coverageSampleConfigs); + coverageConfigs.resize(coverageSampleConfigs * 2 + 4); + glGetIntegerv(GL_MULTISAMPLE_COVERAGE_MODES_NV, &coverageConfigs[0]); + } + if (isGLExtensionSupported(contextID, "GL_ARB_depth_buffer_float")) + depthConfigs.push_back(BufferConfig("D32F", GL_DEPTH_COMPONENT32F, 32)); + else if (isGLExtensionSupported(contextID, "GL_NV_depth_buffer_float")) + depthConfigs.push_back(BufferConfig("D32F", GL_DEPTH_COMPONENT32F_NV, + 32)); +} + +bool checkFramebufferStatus(GraphicsContext* gc, bool silent = false) +{ + State& state = *gc->getState(); + unsigned contextID = state.getContextID(); + FBOExtensions* fboe = FBOExtensions::instance(contextID, true); + switch(fboe->glCheckFramebufferStatus(GL_FRAMEBUFFER_EXT)) { + case GL_FRAMEBUFFER_COMPLETE_EXT: + break; + case GL_FRAMEBUFFER_UNSUPPORTED_EXT: + if (!silent) + cout << "Unsupported framebuffer format\n"; + return false; + case GL_FRAMEBUFFER_INCOMPLETE_MISSING_ATTACHMENT_EXT: + if (!silent) + cout << "Framebuffer incomplete, missing attachment\n"; + return false; + case GL_FRAMEBUFFER_INCOMPLETE_ATTACHMENT_EXT: + if (!silent) + cout << "Framebuffer incomplete, duplicate attachment\n"; + return false; + case GL_FRAMEBUFFER_INCOMPLETE_DIMENSIONS_EXT: + if (!silent) + cout << "Framebuffer incomplete, attached images must have same dimensions\n"; + return false; + case GL_FRAMEBUFFER_INCOMPLETE_FORMATS_EXT: + if (!silent) + cout << "Framebuffer incomplete, attached images must have same format\n"; + return false; + case GL_FRAMEBUFFER_INCOMPLETE_DRAW_BUFFER_EXT: + if (!silent) + cout << "Framebuffer incomplete, missing draw buffer\n"; + return false; + case GL_FRAMEBUFFER_INCOMPLETE_READ_BUFFER_EXT: + if (!silent) + cout << "Framebuffer incomplete, missing read buffer\n"; + return false; + default: + return false; + } + return true; +} + +// Attempt to create an FBO with a certain configuration. If the FBO +// is created with fewer bits in any of its parameters, the creation +// is deemed to have failed. Even though the result is a valid FBO, +// we're only interested in discrete, valid configurations. +bool createFBO(GraphicsContext* gc, FboConfig &config, FboData &data) +{ + bool result = true; + bool multisample = config.depthSamples > 0; + bool csaa = config.coverageSamples > config.depthSamples; + data.fb = new FrameBufferObject; + int texWidth = 512, texHeight = 512; + data.tex = new Texture2D; + data.tex->setTextureSize(texWidth, texHeight); + data.tex->setInternalFormat(config.colorFormat); + data.tex->setSourceFormat(GL_RGBA); + data.tex->setSourceType(GL_FLOAT); + data.tex->setFilter(Texture::MIN_FILTER, Texture::LINEAR_MIPMAP_LINEAR); + data.tex->setFilter(Texture::MAG_FILTER, Texture::LINEAR); + data.tex->setWrap(Texture::WRAP_S, Texture::CLAMP_TO_EDGE); + data.tex->setWrap(Texture::WRAP_T, Texture::CLAMP_TO_EDGE); + RenderBuffer* colorRB = 0; + RenderBuffer* depthRB = 0; + if (multisample) + { + data.resolveFB = new FrameBufferObject; + data.resolveFB->setAttachment(Camera::COLOR_BUFFER, + FrameBufferAttachment(data.tex.get())); + colorRB = new RenderBuffer(texWidth, texHeight, config.colorFormat, + config.coverageSamples, config.depthSamples); + data.fb->setAttachment(Camera::COLOR_BUFFER, + FrameBufferAttachment(colorRB)); + depthRB = new RenderBuffer(texWidth, texHeight, config.depthFormat, + config.coverageSamples, config.depthSamples); + data.fb->setAttachment(Camera::DEPTH_BUFFER, + FrameBufferAttachment(depthRB)); + } + else + { + data.depthTex = makeDepthTexture(texWidth, texHeight, + config.depthFormat); + data.fb->setAttachment(Camera::COLOR_BUFFER, + FrameBufferAttachment(data.tex.get())); + data.fb->setAttachment(Camera::DEPTH_BUFFER, + FrameBufferAttachment(data.depthTex.get())); + } + State& state = *gc->getState(); + unsigned int contextID = state.getContextID(); + FBOExtensions* fboe = FBOExtensions::instance(contextID, true); + + data.fb->apply(state); + result = checkFramebufferStatus(gc, true); + if (!result) + { + fboe->glBindFramebuffer(GL_FRAMEBUFFER_EXT, 0); + return false; + } + int query; + if (multisample) + { + GLuint colorRBID = colorRB->getObjectID(contextID, fboe); + fboe->glBindRenderbuffer(GL_RENDERBUFFER_EXT, colorRBID); + if (csaa) + { + fboe->glGetRenderbufferParameteriv(GL_RENDERBUFFER_EXT, + GL_RENDERBUFFER_COVERAGE_SAMPLES_NV, + &query); + if (query < config.coverageSamples) + result = false; + else + config.coverageSamples = query; + fboe->glGetRenderbufferParameteriv(GL_RENDERBUFFER_EXT, + GL_RENDERBUFFER_COLOR_SAMPLES_NV, + &query); + + if ( query < config.depthSamples) + result = false; + else + config.depthSamples = query; // report back the actual number + + } + else + { + fboe->glGetRenderbufferParameteriv(GL_RENDERBUFFER_EXT, + GL_RENDERBUFFER_SAMPLES_EXT, + &query); + if (query < config.depthSamples) + result = false; + else + config.depthSamples = query; + } + } + glGetIntegerv( GL_RED_BITS, &query); + if (query != config.redbits) + result = false; + glGetIntegerv(GL_DEPTH_BITS, &query); + if ( query != config.depthBits) + result = false; + if (result && multisample && data.resolveFB.valid()) + { + data.resolveFB->apply(state); + result = checkFramebufferStatus(gc, true); + if (result) + { + glGetIntegerv( GL_RED_BITS, &query); + if (query != config.redbits) + result = false; + } + } + fboe->glBindFramebuffer(GL_FRAMEBUFFER_EXT, 0); + return result; +} + +void destroyFBO(GraphicsContext* gc, FboData &data) +{ + data.tex = 0; + data.depthTex = 0; + data.fb = 0; + data.resolveFB = 0; + State& state = *gc->getState(); + double availableTime = 100.0; + RenderBuffer::flushDeletedRenderBuffers(state.getContextID(), 0.0, + availableTime); + availableTime = 100.0; + FrameBufferObject::flushDeletedFrameBufferObjects(state.getContextID(), + 0.0, availableTime); +} + +void setAttachmentsFromConfig(Camera* camera, const FboConfig& config); +Switch* makeTexturesAndGeometry(int width, int height, Switch* sw = 0); + +// Application state accessed from event handlers and main function; +// contains state that can be changed by the user and the OSG classes +// used to display / indicate that state. +// +// camera - Camera with fbo, using either fp depth buffer or fixed +// switch child 0 - texture containing rendering of scene +// switch child 1 - fp depth buffer as texture +// switch child 2 - integer depth buffer as texture +// textNotAvailable- "not available" text if texture isn't valid. + +struct AppState : public Referenced +{ + AppState(osgViewer::Viewer* viewer_); + void setStateFromConfig(const FboConfig& config); + void advanceConfig(int increment); + void updateDisplayedTexture(); + void updateNear(); + virtual ~AppState() {} + ref_ptr sw; // switch between displayed texture + bool displayScene; + bool invertRange; + int currentConfig; + osgViewer::Viewer* viewer; + double zNear; + ref_ptr camera; + // text displayed on the screen showing the user's choices + ref_ptr textProjection; + ref_ptr configText; + ref_ptr zNearText; + ref_ptr textNotAvailable; + ref_ptr textInverted; +}; + +AppState::AppState(osgViewer::Viewer* viewer_) + : displayScene(true), invertRange(true), currentConfig(0), + viewer(viewer_), zNear(0.03125) +{ + sw = new Switch; + string fontName("fonts/arial.ttf"); + // Text description of current config + configText = new osgText::Text; + configText->setDataVariance(Object::DYNAMIC); + configText->setFont(fontName); + configText->setPosition(Vec3(50.0f, 50.0f, 0.0f)); + configText->setColor(Vec4(1.0, 1.0, 1.0, 1.0)); + Geode* textGeode = new Geode; + textGeode->addDrawable(configText.get()); + // Text for the near plane distance + zNearText = new osgText::Text; + zNearText->setDataVariance(Object::DYNAMIC); + zNearText->setFont(fontName); + zNearText->setPosition(Vec3(1230.0f, 50.0f, 0.0f)); + zNearText->setColor(Vec4(1.0, 1.0, 1.0, 1.0)); + zNearText->setAlignment(osgText::Text::RIGHT_BASE_LINE); + textGeode->addDrawable(zNearText.get()); + // Projection that lets the text be placed in pixels. + textProjection = new Projection; + textProjection->setMatrix(Matrix::ortho2D(0,1280,0,1024)); + textProjection->addChild(textGeode); + // "texture not available" text displayed when the user trys to + // display the depth texture while multisampling. + osgText::Text* noCanDo = new osgText::Text; + noCanDo->setFont(fontName); + noCanDo->setPosition(Vec3(512.0f, 384.0f, 0.0f)); + noCanDo->setColor(Vec4(1.0, 0.0, 0.0, 1.0)); + noCanDo->setText("not available"); + textNotAvailable = new Geode; + textNotAvailable->addDrawable(noCanDo); + textProjection->addChild(textNotAvailable.get()); + // Is the depth test inverted? + osgText::Text* inverted = new osgText::Text; + inverted->setFont(fontName); + inverted->setPosition(Vec3(512.0f, 50.0f, 0.0f)); + inverted->setColor(Vec4(1.0, 1.0, 1.0, 1.0)); + inverted->setText("inverted depth test"); + textInverted = new Geode; + textInverted->addDrawable(inverted); + textInverted->setNodeMask(~0); + textProjection->addChild(textInverted); + textProjection->getOrCreateStateSet()->setRenderBinDetails(11, "RenderBin"); +} + +void AppState::setStateFromConfig(const FboConfig& config) +{ + Camera* camera = viewer->getSlave(0)._camera.get(); + setAttachmentsFromConfig(camera, config); + osgViewer::Renderer* renderer + = dynamic_cast(camera->getRenderer()); + if (renderer) + renderer->setCameraRequiresSetUp(true); + if (configText.valid()) + { + configText->setText(validConfigs[currentConfig].name); + configText->update(); + } + updateDisplayedTexture(); +} + +void AppState::advanceConfig(int increment) +{ + currentConfig = (currentConfig + increment) % validConfigs.size(); + setStateFromConfig(validConfigs[currentConfig]); +} + +void AppState::updateDisplayedTexture() +{ + if (displayScene) + sw->setSingleChildOn(0); + else if (validConfigs[currentConfig].depthSamples > 0 + || validConfigs[currentConfig].coverageSamples > 0) + sw->setAllChildrenOff(); + else if (validConfigs[currentConfig].depthFormat != GL_DEPTH_COMPONENT24) + sw->setSingleChildOn(2); + else + sw->setSingleChildOn(3); + if (displayScene + || (validConfigs[currentConfig].depthSamples == 0 + && validConfigs[currentConfig].coverageSamples == 0)) + textNotAvailable->setNodeMask(0); + else + textNotAvailable->setNodeMask(~0); +} + +void AppState::updateNear() +{ + // Assume that the viewing frustum is symmetric. + double fovy, aspectRatio, cNear, cFar; + viewer->getCamera()->getProjectionMatrixAsPerspective(fovy, aspectRatio, + cNear, cFar); + viewer->getCamera()->setProjectionMatrixAsPerspective(fovy, aspectRatio, + zNear, cFar); + stringstream nearStream; + nearStream << "near: " << zNear; + zNearText->setText(nearStream.str()); + zNearText->update(); +} + +class ConfigHandler : public osgGA::GUIEventHandler +{ +public: + + ConfigHandler(AppState* appState) + : _appState(appState) + { + } + + virtual bool handle(const osgGA::GUIEventAdapter& ea, + osgGA::GUIActionAdapter& aa, + Object*, NodeVisitor* /*nv*/) + { + if (ea.getHandled()) return false; + osgViewer::Viewer* viewer = dynamic_cast(&aa); + if (!viewer) return false; + switch(ea.getEventType()) + { + case osgGA::GUIEventAdapter::KEYUP: + { + if (ea.getKey()=='d') + { + _appState->displayScene = !_appState->displayScene; + _appState->updateDisplayedTexture(); + return true; + } + else if (ea.getKey()==osgGA::GUIEventAdapter::KEY_Right || + ea.getKey()==osgGA::GUIEventAdapter::KEY_KP_Right) + { + _appState->advanceConfig(1); + return true; + } + else if (ea.getKey()==osgGA::GUIEventAdapter::KEY_Left || + ea.getKey()==osgGA::GUIEventAdapter::KEY_KP_Left) + { + _appState->advanceConfig(-1); + return true; + } + break; + } + default: + break; + } + return false; + } + + void getUsage(ApplicationUsage& usage) const + { + usage.addKeyboardMouseBinding("d", "display depth texture"); + usage.addKeyboardMouseBinding("right arrow", + "next frame buffer configuration"); + usage.addKeyboardMouseBinding("left arrow", + "previous frame buffer configuration"); + } + +protected: + virtual ~ConfigHandler() {} + ref_ptr _appState; +}; + +class DepthHandler : public osgGA::GUIEventHandler +{ +public: + + DepthHandler(AppState *appState, Depth* depth) + : _appState(appState), _depth(depth) + { + depth->setDataVariance(Object::DYNAMIC); + } + + virtual bool handle(const osgGA::GUIEventAdapter& ea, + osgGA::GUIActionAdapter& /*aa*/, + Object*, NodeVisitor* /*nv*/) + { + if (ea.getHandled()) return false; + ref_ptr depth = _depth.lock(); + if (!depth.valid()) + return false; + switch(ea.getEventType()) + { + case(osgGA::GUIEventAdapter::KEYUP): + { + if (ea.getKey() == 'i') + { + _appState->invertRange = !_appState->invertRange; + if (!_appState->invertRange) + { + _appState->camera->setClearDepth(1.0f); + depth->setFunction(Depth::LESS); + depth->setRange(0.0f, 1.0f); + _appState->textInverted->setNodeMask(0); + } + else + { + _appState->camera->setClearDepth(0.0f); + depth->setFunction(Depth::GEQUAL); + depth->setRange(1.0f, 0.0f); + _appState->textInverted->setNodeMask(~0); + } + return true; + } + else if (ea.getKey()==osgGA::GUIEventAdapter::KEY_Up || + ea.getKey()==osgGA::GUIEventAdapter::KEY_KP_Up) + { + _appState->zNear *= 2.0; + _appState->updateNear(); + return true; + } + else if (ea.getKey()==osgGA::GUIEventAdapter::KEY_Down || + ea.getKey()==osgGA::GUIEventAdapter::KEY_KP_Down) + { + _appState->zNear *= .5; + _appState->updateNear(); + return true; + } + break; + } + default: + break; + } + return false; + } + + void getUsage(ApplicationUsage& usage) const + { + usage.addKeyboardMouseBinding("i", "invert depth buffer range"); + usage.addKeyboardMouseBinding("up arrow", + "double near plane distance"); + usage.addKeyboardMouseBinding("down arrow", + "half near plane distance"); + } +protected: + virtual ~DepthHandler() {} + ref_ptr _appState; + observer_ptr _depth; +}; + +Geode* createTextureQuad(Texture2D *texture) +{ + Vec3Array *vertices = new Vec3Array; + vertices->push_back(Vec3(-1.0, -1.0, 0.0)); + vertices->push_back(Vec3(1.0, -1.0, 0.0)); + vertices->push_back(Vec3(1.0, 1.0, 0.0)); + vertices->push_back(Vec3(-1.0, 1.0, 0.0)); + + Vec2Array *texcoord = new Vec2Array; + texcoord->push_back(Vec2(0.0, 0.0)); + texcoord->push_back(Vec2(1.0, 0.0)); + texcoord->push_back(Vec2(1.0, 1.0)); + texcoord->push_back(Vec2(0.0, 1.0)); + + Geometry *geom = new Geometry; + geom->setVertexArray(vertices); + geom->setTexCoordArray(0, texcoord); + geom->addPrimitiveSet(new DrawArrays(GL_QUADS, 0, 4)); + + Geode *geode = new Geode; + geode->addDrawable(geom); + geode->getOrCreateStateSet()->setTextureAttributeAndModes(0, texture, StateAttribute::ON); + + return geode; +} + +struct ResizedCallback : public osg::GraphicsContext::ResizedCallback +{ + ResizedCallback(AppState* appState) + : _appState(appState) + { + } + void resizedImplementation(GraphicsContext* gc, int x, int y, int width, + int height); + ref_ptr _appState; +}; + +void ResizedCallback::resizedImplementation(GraphicsContext* gc, int x, int y, + int width, int height) +{ + gc->resizedImplementation(x, y, width, height); + makeTexturesAndGeometry(width, height, _appState->sw); + _appState->setStateFromConfig(validConfigs[_appState + ->currentConfig]); + osgViewer::Viewer* viewer = _appState->viewer; + Viewport* vp = viewer->getSlave(0)._camera->getViewport(); + if (vp) + { + double oldWidth = vp->width(), oldHeight = vp->height(); + double aspectRatioChange + = (width / oldWidth) / (height / oldHeight); + vp->setViewport(0, 0, width, height); + if (aspectRatioChange != 1.0) + { + Camera* master = viewer->getCamera(); + switch (master->getProjectionResizePolicy()) + { + case Camera::HORIZONTAL: + master->getProjectionMatrix() + *= Matrix::scale(1.0/aspectRatioChange,1.0,1.0); + break; + case Camera::VERTICAL: + master->getProjectionMatrix() + *= Matrix::scale(1.0, aspectRatioChange,1.0); + break; + default: + break; + } + } + } +} + +// Prefer GL_DEPTH_COMPONENT32F, otherwise use +// GL_DEPTH_COMPONENT32F_NV if available +GLenum depthTextureEnum = 0; + +// Standard OSG code for initializing osgViewer::Viewer with explicit +// creation of own graphics context. This is also a good time to test +// for valid frame buffer configurations; we have a valid graphics +// context, but multithreading hasn't started, etc. +GraphicsContext* setupGC(osgViewer::Viewer& viewer, ArgumentParser& arguments) +{ + int x = -1, y = -1, width = -1, height = -1; + while (arguments.read("--window",x,y,width,height)) {} + + GraphicsContext::WindowingSystemInterface* wsi = + GraphicsContext::getWindowingSystemInterface(); + if (!wsi) + { + OSG_NOTIFY(NOTICE)<<"View::setUpViewOnSingleScreen() : Error, no WindowSystemInterface available, cannot create windows."<getScreenResolution(si, w, h); + x = 0; + y = 0; + width = w; + height = h; + decoration = false; + } + + ref_ptr traits + = new GraphicsContext::Traits(ds); + traits->hostName = si.hostName; + traits->displayNum = si.displayNum; + traits->screenNum = si.screenNum; + traits->x = x; + traits->y = y; + traits->width = width; + traits->height = height; + traits->windowDecoration = decoration; + traits->doubleBuffer = true; + traits->sharedContext = 0; + ref_ptr gc + = GraphicsContext::createGraphicsContext(traits.get()); + osgViewer::GraphicsWindow* gw + = dynamic_cast(gc.get()); + if (gw) + { + OSG_NOTIFY(INFO)<<"View::setUpViewOnSingleScreen - GraphicsWindow has been created successfully."<getEventQueue()->getCurrentEventState() + ->setWindowRectangle(0, 0, width, height); + } + else + { + OSG_NOTIFY(NOTICE)<<" GraphicsWindow has not been created successfully."<getProjectionMatrixAsPerspective(fovy, aspectRatio, + zNear, zFar); + double newAspectRatio = double(traits->width) / double(traits->height); + double aspectRatioChange = newAspectRatio / aspectRatio; + if (aspectRatioChange != 1.0) + { + viewer.getCamera()->getProjectionMatrix() + *= Matrix::scale(1.0/aspectRatioChange,1.0,1.0); + } + // Context has to be current to test for extensions + gc->realize(); + gc->makeCurrent(); + unsigned int contextID = gc->getState()->getContextID(); + FBOExtensions* fboe = FBOExtensions::instance(contextID, true); + if (!fboe->isSupported()) + { + OSG_NOTIFY(NOTICE) << "Frame buffer objects are not supported\n"; + gc->releaseContext(); + gc->close(true); + return 0; + } + if (isGLExtensionSupported(contextID, "GL_ARB_depth_buffer_float")) + depthTextureEnum = GL_DEPTH_COMPONENT32F; + else if (isGLExtensionSupported(contextID, "GL_NV_depth_buffer_float")) + depthTextureEnum = GL_DEPTH_COMPONENT32F_NV; + BufferConfigList colorConfigs; + BufferConfigList depthConfigs; + vector coverageConfigs; + getPossibleConfigs(gc, colorConfigs, depthConfigs, coverageConfigs); + int coverageSampleConfigs = (coverageConfigs.size() - 4) / 2; + cout << "color configs\nname\tbits\n"; + for (BufferConfigList::const_iterator colorItr = colorConfigs.begin(), + colorEnd = colorConfigs.end(); + colorItr != colorEnd; + ++colorItr) + { + for (BufferConfigList::const_iterator depthItr = depthConfigs.begin(), + depthEnd = depthConfigs.end(); + depthItr != depthEnd; + ++depthItr) + { + string root = colorItr->name + " " + depthItr->name; + FboConfig config(root, colorItr->format, depthItr->format, + colorItr->bits, depthItr->bits); + FboData data; + if (createFBO(gc, config, data)) + validConfigs.push_back(config); + destroyFBO(gc, data); + if (coverageConfigs.size() > 0) + { + //CSAA provides a list of all supported AA modes for + //quick enumeration + for (int kk = 0; kk < coverageSampleConfigs; kk++) + { + stringstream msText; + msText << root; + config.depthSamples = coverageConfigs[kk*2+1]; + config.coverageSamples = coverageConfigs[kk*2]; + + if ( config.coverageSamples == config.depthSamples ) + { + // Normal antialiasing + msText << " - " << config.depthSamples << " MSAA"; + } + else + { + // coverage antialiasing + msText << " - " << config.coverageSamples << "/" + << config.depthSamples << " CSAA"; + } + config.name = msText.str(); + + if (createFBO(gc, config, data)) { + validConfigs.push_back( config); + } + destroyFBO(gc, data); + } + } + } + } + cout << "valid frame buffer configurations:\n"; + for (vector::iterator itr = validConfigs.begin(), + end = validConfigs.end(); + itr != end; + ++itr) + cout << itr->name << "\n"; + gc->releaseContext(); + + return gc.release(); +} + +ref_ptr colorTexture; +ref_ptr depthTexture; +ref_ptr depthTexture24; + +Texture2D* makeDepthTexture(int width, int height, GLenum internalFormat) +{ + Texture2D *depthTex = new Texture2D; + depthTex->setTextureSize(width, height); + depthTex->setSourceFormat(GL_DEPTH_COMPONENT); + depthTex->setSourceType(GL_FLOAT); + depthTex->setInternalFormat(internalFormat); + depthTex->setFilter(Texture2D::MIN_FILTER, Texture2D::NEAREST); + depthTex->setFilter(Texture2D::MAG_FILTER, Texture2D::NEAREST); + depthTex->setWrap(Texture::WRAP_S, Texture::CLAMP_TO_EDGE); + depthTex->setWrap(Texture::WRAP_T, Texture::CLAMP_TO_EDGE); + return depthTex; +} + +Camera* makeRttCamera(GraphicsContext* gc, int width, int height) +{ + Camera* rttCamera = new Camera; + rttCamera->setGraphicsContext(gc); + rttCamera->setClearMask(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT); + rttCamera->setClearColor(Vec4(0.0, 0.4, 0.5, 0.0)); + // normally the depth test is inverted, although the user can + // change that. + rttCamera->setClearDepth(0.0); + rttCamera->setViewport(0, 0, width, height); + rttCamera->setDrawBuffer(GL_FRONT); + rttCamera->setReadBuffer(GL_FRONT); + rttCamera->setRenderTargetImplementation(Camera::FRAME_BUFFER_OBJECT); + rttCamera->setComputeNearFarMode(CullSettings::DO_NOT_COMPUTE_NEAR_FAR); + return rttCamera; +} + +void setAttachmentsFromConfig(Camera* camera, const FboConfig& config) +{ + // XXX Detaching the old buffers may not be necessary. + if (!camera->getBufferAttachmentMap().empty()) + { + camera->detach(Camera::COLOR_BUFFER); + camera->detach(Camera::DEPTH_BUFFER); + } + camera->attach(Camera::COLOR_BUFFER, colorTexture.get(), 0, 0, false, + config.coverageSamples, config.depthSamples); + if (config.coverageSamples != 0 || config.depthSamples != 0) + camera->attach(Camera::DEPTH_BUFFER, config.depthFormat); + else if (config.depthFormat == GL_DEPTH_COMPONENT24) + camera->attach(Camera::DEPTH_BUFFER, depthTexture24); + else + camera->attach(Camera::DEPTH_BUFFER, depthTexture); +} + +// Create the parts of the local scene graph used to display the final +// results. +Switch* makeTexturesAndGeometry(int width, int height, Switch* sw) +{ + if (!sw) + sw = new Switch; + colorTexture = new Texture2D; + colorTexture->setTextureSize(width, height); + colorTexture->setInternalFormat(GL_RGBA); + colorTexture->setFilter(Texture2D::MIN_FILTER, Texture2D::LINEAR); + colorTexture->setFilter(Texture2D::MAG_FILTER, Texture2D::LINEAR); + colorTexture->setWrap(Texture::WRAP_S, Texture::CLAMP_TO_EDGE); + colorTexture->setWrap(Texture::WRAP_T, Texture::CLAMP_TO_EDGE); + colorTexture->setBorderColor(Vec4(0, 0, 0, 0)); + + depthTexture24 = makeDepthTexture(width, height, GL_DEPTH_COMPONENT24); + if (depthTextureEnum) + depthTexture = makeDepthTexture(width, height, depthTextureEnum); + else + depthTexture = depthTexture24; + sw->removeChildren(0, sw->getNumChildren()); + sw->addChild(createTextureQuad(colorTexture)); + sw->addChild(createTextureQuad(depthTexture)); + sw->addChild(createTextureQuad(depthTexture24)); + sw->setSingleChildOn(0); + return sw; +} + +int main(int argc, char **argv) +{ + // use an ArgumentParser object to manage the program arguments. + ArgumentParser arguments(&argc,argv); + arguments.getApplicationUsage() + ->setDescription(arguments.getApplicationName() + + " demonstrates using a floating point depth buffer.\nThe user can invert the depth buffer range and choose among available multi-sample configurations."); + arguments.getApplicationUsage() + ->addCommandLineOption("--far ", "Set far plane value"); + // if user request help write it out to cout. + if (arguments.read("-h") || arguments.read("--help")) + { + arguments.getApplicationUsage()->write(std::cout); + return 1; + } + float zFar = 500.0f; + while (arguments.read("--far", zFar)) + ; + // construct the viewer. + osgViewer::Viewer viewer; + ref_ptr appState = new AppState(&viewer); + viewer.addEventHandler(new osgViewer::StatsHandler); + viewer.addEventHandler(new osgViewer::WindowSizeHandler); + viewer.addEventHandler(new osgViewer::ScreenCaptureHandler); + // The aspect ratio is set to the correct ratio for the window in + // setupGC(). + viewer.getCamera() + ->setProjectionMatrixAsPerspective(40.0, 1.0, appState->zNear, zFar); + GraphicsContext* gc = setupGC(viewer, arguments); + if (!gc) + return 1; + gc->setResizedCallback(new ResizedCallback(appState.get())); + const GraphicsContext::Traits* traits = gc->getTraits(); + width = traits->width; + height = traits->height; + ref_ptr loadedModel = osgDB::readNodeFiles(arguments); + if (!loadedModel) { + cerr << "couldn't load " << argv[1] << "\n"; + return 1; + } + osgUtil::Optimizer optimizer; + optimizer.optimize(loadedModel.get()); + // creates texture to be rendered + Switch* sw = makeTexturesAndGeometry(width, height, appState->sw); + ref_ptr rttCamera = makeRttCamera(gc, width, height); + rttCamera->setRenderOrder(Camera::PRE_RENDER); + viewer.addSlave(rttCamera); + appState->camera = rttCamera; + // geometry and slave camera to display the result + Group* displayRoot = new Group; + displayRoot->addChild(sw); + displayRoot->addChild(appState->textProjection); + StateSet* displaySS = displayRoot->getOrCreateStateSet(); + displaySS->setMode(GL_LIGHTING, StateAttribute::OFF); + displaySS->setMode(GL_DEPTH_TEST, StateAttribute::OFF); + Camera* texCamera = new Camera; + texCamera->setGraphicsContext(gc); + texCamera->setClearMask(GL_COLOR_BUFFER_BIT); + texCamera->setClearColor(Vec4(0.0, 0.0, 0.0, 0.0)); + texCamera->setReferenceFrame(Camera::ABSOLUTE_RF); + texCamera->setViewport(0, 0, width, height); + texCamera->setDrawBuffer(GL_BACK); + texCamera->setReadBuffer(GL_BACK); + texCamera->addChild(displayRoot); + texCamera->setAllowEventFocus(false); + texCamera->setCullingMode(CullSettings::NO_CULLING); + texCamera->setProjectionResizePolicy(Camera::FIXED); + viewer.addSlave(texCamera, Matrixd(), Matrixd(), false); + viewer.addEventHandler(new ConfigHandler(appState)); + + // add model to the viewer. + Group* sceneRoot = new Group; + StateSet* sceneSS = sceneRoot->getOrCreateStateSet(); + Depth* depth = new Depth(Depth::GEQUAL, 1.0, 0.0); + sceneSS->setAttributeAndModes(depth,(StateAttribute::ON + | StateAttribute::OVERRIDE)); +#if 0 + // Hack to work around Blender osg export bug + sceneSS->setAttributeAndModes(new CullFace(CullFace::BACK)); +#endif + sceneRoot->addChild(loadedModel.get()); + appState->setStateFromConfig(validConfigs[0]); + appState->updateNear(); + viewer.addEventHandler(new DepthHandler(appState.get(), depth)); + // add the help handler + viewer.addEventHandler(new osgViewer + ::HelpHandler(arguments.getApplicationUsage())); + + viewer.setSceneData(sceneRoot); + + return viewer.run(); +} diff --git a/include/osg/FrameBufferObject b/include/osg/FrameBufferObject index 5b731ea9d..cab0112fe 100644 --- a/include/osg/FrameBufferObject +++ b/include/osg/FrameBufferObject @@ -153,6 +153,7 @@ namespace osg typedef void APIENTRY TglFramebufferRenderbuffer(GLenum, GLenum, GLenum, GLuint); typedef void APIENTRY TglGenerateMipmap(GLenum); typedef void APIENTRY TglBlitFramebuffer(GLint, GLint, GLint, GLint, GLint, GLint, GLint, GLint, GLbitfield, GLenum); + typedef void APIENTRY TglGetRenderbufferParameteriv(GLenum, GLenum, GLint*); TglBindRenderbuffer* glBindRenderbuffer; TglGenRenderbuffers* glGenRenderbuffers; @@ -171,6 +172,7 @@ namespace osg TglFramebufferRenderbuffer* glFramebufferRenderbuffer; TglGenerateMipmap* glGenerateMipmap; TglBlitFramebuffer* glBlitFramebuffer; + TglGetRenderbufferParameteriv* glGetRenderbufferParameteriv; static FBOExtensions* instance(unsigned contextID, bool createIfNotInitalized); diff --git a/include/osgViewer/Renderer b/include/osgViewer/Renderer index e624df354..200fb034d 100644 --- a/include/osgViewer/Renderer +++ b/include/osgViewer/Renderer @@ -59,6 +59,7 @@ class OSGVIEWER_EXPORT Renderer : public osg::GraphicsOperation, public OpenGLQu Renderer(osg::Camera* camera); osgUtil::SceneView* getSceneView(unsigned int i) { return _sceneView[i].get(); } + const osgUtil::SceneView* getSceneView(unsigned int i) const { return _sceneView[i].get(); } void setDone(bool done) { _done = done; } bool getDone() { return _done; } @@ -126,6 +127,10 @@ class OSGVIEWER_EXPORT Renderer : public osg::GraphicsOperation, public OpenGLQu void setConservativeTimeRatio(double ratio) { _conservativeTimeRatio = ratio; } double getConservativeTimeRatio() const { return _conservativeTimeRatio; } + /** Force update of state associated with cameras. */ + void setCameraRequiresSetUp(bool flag); + bool getCameraRequiresSetUp() const; + protected: virtual ~Renderer(); diff --git a/src/osg/FrameBufferObject.cpp b/src/osg/FrameBufferObject.cpp index fc7aed226..de8c452b0 100644 --- a/src/osg/FrameBufferObject.cpp +++ b/src/osg/FrameBufferObject.cpp @@ -75,6 +75,7 @@ FBOExtensions::FBOExtensions(unsigned int contextID) LOAD_FBO_EXT(glFramebufferTextureLayer); LOAD_FBO_EXT(glFramebufferRenderbuffer); LOAD_FBO_EXT(glGenerateMipmap); + LOAD_FBO_EXT(glGetRenderbufferParameteriv); _supported = glBindRenderbuffer != 0 && @@ -89,7 +90,8 @@ FBOExtensions::FBOExtensions(unsigned int contextID) glFramebufferTexture2D != 0 && glFramebufferTexture3D != 0 && glFramebufferRenderbuffer != 0 && - glGenerateMipmap != 0; + glGenerateMipmap != 0 && + glGetRenderbufferParameteriv != 0; LOAD_FBO_EXT(glBlitFramebuffer); LOAD_FBO_EXT(glRenderbufferStorageMultisample); diff --git a/src/osgUtil/RenderStage.cpp b/src/osgUtil/RenderStage.cpp index 5a7d2a65d..534d42679 100644 --- a/src/osgUtil/RenderStage.cpp +++ b/src/osgUtil/RenderStage.cpp @@ -331,7 +331,7 @@ void RenderStage::runCameraSetUp(osg::RenderInfo& renderInfo) osg::FBOExtensions* fbo_ext = osg::FBOExtensions::instance(state.getContextID(),true); bool fbo_supported = fbo_ext && fbo_ext->isSupported(); - if (fbo_supported && !_fbo) + if (fbo_supported) { OSG_NOTIFY(osg::INFO)<<"Setting up osg::Camera::FRAME_BUFFER_OBJECT"<getRenderStage() : 0; + if (rs) rs->setCameraRequiresSetUp(flag); + } +} + +bool Renderer::getCameraRequiresSetUp() const +{ + bool result = false; + for (int i = 0; i < 2; ++i) + { + const osgUtil::SceneView* sv = getSceneView(i); + const osgUtil::RenderStage* rs = sv ? sv->getRenderStage() : 0; + if (rs) result = result || rs->getCameraRequiresSetUp(); + } + return result; +}