diff --git a/examples/CMakeLists.txt b/examples/CMakeLists.txt index 3e89b301a..20ddec7a5 100644 --- a/examples/CMakeLists.txt +++ b/examples/CMakeLists.txt @@ -69,6 +69,7 @@ IF(DYNAMIC_OPENSCENEGRAPH) ADD_SUBDIRECTORY(osgmultiviewpaging) ADD_SUBDIRECTORY(osgoccluder) ADD_SUBDIRECTORY(osgocclusionquery) + ADD_SUBDIRECTORY(osgoit) ADD_SUBDIRECTORY(osgpackeddepthstencil) ADD_SUBDIRECTORY(osgpagedlod) ADD_SUBDIRECTORY(osgparametric) diff --git a/examples/osgoit/CMakeLists.txt b/examples/osgoit/CMakeLists.txt new file mode 100644 index 000000000..40e84f52a --- /dev/null +++ b/examples/osgoit/CMakeLists.txt @@ -0,0 +1,2 @@ +SET(TARGET_SRC osgoit.cpp) +SETUP_EXAMPLE(osgoit) diff --git a/examples/osgoit/osgoit.cpp b/examples/osgoit/osgoit.cpp new file mode 100644 index 000000000..d69c2aeda --- /dev/null +++ b/examples/osgoit/osgoit.cpp @@ -0,0 +1,503 @@ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include + +#include +#include + +#include + +#include +#include + +// Some choices for the kind of textures we can use ... +#define USE_TEXTURE_RECTANGLE +// #define USE_NON_POWER_OF_TWO_TEXTURE +#define USE_PACKED_DEPTH_STENCIL + +template +inline T +nextPowerOfTwo(T k) +{ + if (k == T(0)) + return 1; + k--; + for (int i = 1; i < std::numeric_limits::digits; i <<= 1) + k = k | k >> i; + return k + 1; +} + +class DepthPeeling : public osg::Referenced { +public: + osg::Node* + createQuad(unsigned layerNumber, unsigned numTiles) + { + float tileSpan = 1; + float tileOffsetX = 0; + float tileOffsetY = 0; + if (_showAllLayers) { + tileSpan /= numTiles; + tileOffsetX = tileSpan * (layerNumber%numTiles); + tileOffsetY = 1 - tileSpan * (1 + layerNumber/numTiles); + } + + osg::Vec3Array* vertices = new osg::Vec3Array; + + vertices->push_back(osg::Vec3f(tileOffsetX , tileOffsetY , 0)); + vertices->push_back(osg::Vec3f(tileOffsetX , tileOffsetY + tileSpan, 0)); + vertices->push_back(osg::Vec3f(tileOffsetX + tileSpan, tileOffsetY + tileSpan, 0)); + vertices->push_back(osg::Vec3f(tileOffsetX + tileSpan, tileOffsetY , 0)); + + osg::Vec3Array* colors = new osg::Vec3Array; + colors->push_back(osg::Vec3(1, 1, 1)); + + osg::Vec2Array* texcoords = new osg::Vec2Array; + texcoords->push_back(osg::Vec2f(0, 0)); + texcoords->push_back(osg::Vec2f(0, 1)); + texcoords->push_back(osg::Vec2f(1, 1)); + texcoords->push_back(osg::Vec2f(1, 0)); + + osg::Geometry* geometry = new osg::Geometry; + geometry->setVertexArray(vertices); + geometry->setTexCoordArray(0, texcoords); + + geometry->setColorArray(colors); + geometry->setColorBinding(osg::Geometry::BIND_OVERALL); + + geometry->addPrimitiveSet(new osg::DrawArrays(GL_QUADS, 0, 4)); + + osg::Geode* geode = new osg::Geode; + geode->addDrawable(geometry); + + return geode; + } + + class CullCallback : public osg::NodeCallback { + public: + CullCallback(unsigned texUnit, unsigned texWidth, unsigned texHeight, unsigned offsetValue) : + _texUnit(texUnit), + _texWidth(texWidth), + _texHeight(texHeight), + _offsetValue(offsetValue) + { + } + + virtual void operator()(osg::Node* node, osg::NodeVisitor* nv) + { + osgUtil::CullVisitor* cullVisitor = static_cast(nv); + osgUtil::RenderStage* renderStage = cullVisitor->getCurrentRenderStage(); + const osg::Viewport* viewport = renderStage->getViewport(); + + osg::Matrixd m(*cullVisitor->getProjectionMatrix()); + m.postMultTranslate(osg::Vec3d(1, 1, 1)); + m.postMultScale(osg::Vec3d(0.5, 0.5, 0.5)); + + // scale the texture coordinates to the viewport +#ifdef USE_TEXTURE_RECTANGLE + m.postMultScale(osg::Vec3d(viewport->width(), viewport->height(), 1)); +#else +#ifndef USE_NON_POWER_OF_TWO_TEXTURE + m.postMultScale(osg::Vec3d(viewport->width()/double(_texWidth), viewport->height()/double(_texHeight), 1)); +#endif +#endif + + // Kind of polygon offset: note this way, we can also offset lines and points. + // Whereas with the polygon offset we could only handle surface primitives. + m.postMultTranslate(osg::Vec3d(0, 0, -ldexp(double(_offsetValue), -24))); + + osg::TexMat* texMat = new osg::TexMat(m); + osg::StateSet* stateSet = new osg::StateSet; + stateSet->setTextureAttribute(_texUnit, texMat); + cullVisitor->pushStateSet(stateSet); + traverse(node, nv); + cullVisitor->popStateSet(); + } + + private: + unsigned _texUnit; + unsigned _texWidth; + unsigned _texHeight; + unsigned _offsetValue; + }; + + void + createPeeling() + { + int numTiles = ceil(sqrt(double(_numPasses))); + + _root->removeChildren(0, _root->getNumChildren()); + _colorTextures.clear(); + + // If not enabled, just use the top level camera + if (!_depthPeelingEnabled) { + _root->addChild(_scene.get()); + return; + } + + _compositeCamera = new osg::Camera; + _compositeCamera->setDataVariance(osg::Object::DYNAMIC); + _compositeCamera->setInheritanceMask(osg::Camera::READ_BUFFER | osg::Camera::DRAW_BUFFER); + _compositeCamera->setRenderOrder(osg::Camera::POST_RENDER); + _compositeCamera->setComputeNearFarMode(osg::Camera::COMPUTE_NEAR_FAR_USING_PRIMITIVES); + _compositeCamera->setClearMask(0); + + _compositeCamera->setReferenceFrame(osg::Transform::ABSOLUTE_RF); + _compositeCamera->setViewMatrix(osg::Matrix()); + _compositeCamera->setProjectionMatrix(osg::Matrix::ortho2D(0, 1, 0, 1)); + + _compositeCamera->setCullCallback(new CullCallback(0, _texWidth, _texHeight, 0)); + + osg::StateSet* stateSet = _compositeCamera->getOrCreateStateSet(); + stateSet->setBinName("TraversalOrderBin"); + stateSet->setRenderBinMode(osg::StateSet::USE_RENDERBIN_DETAILS); + + _root->addChild(_compositeCamera.get()); + + for (unsigned i = 0; i < 2; ++i) { +#ifdef USE_TEXTURE_RECTANGLE + _depthTextures[i] = new osg::TextureRectangle; +#else + _depthTextures[i] = new osg::Texture2D; +#endif + _depthTextures[i]->setTextureSize(_texWidth, _texHeight); + + _depthTextures[i]->setFilter(osg::Texture::MIN_FILTER, osg::Texture::NEAREST); + _depthTextures[i]->setFilter(osg::Texture::MAG_FILTER, osg::Texture::NEAREST); + _depthTextures[i]->setWrap(osg::Texture::WRAP_S, osg::Texture::CLAMP_TO_BORDER); + _depthTextures[i]->setWrap(osg::Texture::WRAP_T, osg::Texture::CLAMP_TO_BORDER); + +#ifdef USE_PACKED_DEPTH_STENCIL + _depthTextures[i]->setInternalFormat(GL_DEPTH24_STENCIL8_EXT); + _depthTextures[i]->setSourceFormat(GL_DEPTH_STENCIL_EXT); + _depthTextures[i]->setSourceType(GL_UNSIGNED_INT_24_8_EXT); +#else + _depthTextures[i]->setInternalFormat(GL_DEPTH_COMPONENT); + _depthTextures[i]->setInternalFormat(GL_DEPTH_COMPONENT24); +#endif + + _depthTextures[i]->setShadowComparison(true); + _depthTextures[i]->setShadowAmbient(0); // The r value if the test fails + _depthTextures[i]->setShadowCompareFunc(osg::Texture::GREATER); + _depthTextures[i]->setShadowTextureMode(osg::Texture::INTENSITY); + } + + // Then, the other ones + for (unsigned i = 0; i < _numPasses; ++i) { + osg::Camera* camera = new osg::Camera; + camera->setDataVariance(osg::Object::DYNAMIC); + + camera->setInheritanceMask(osg::Camera::ALL_VARIABLES); + camera->setRenderTargetImplementation(osg::Camera::FRAME_BUFFER_OBJECT); + camera->setRenderOrder(osg::Camera::PRE_RENDER, i); + camera->setClearMask(GL_DEPTH_BUFFER_BIT | GL_COLOR_BUFFER_BIT); + camera->setClearColor(osg::Vec4f(0, 0, 0, 0)); + + camera->setComputeNearFarMode(osg::Camera::DO_NOT_COMPUTE_NEAR_FAR); + + osg::ref_ptr depthTexture = _depthTextures[i%2]; + osg::ref_ptr prevDepthTexture = _depthTextures[(i+1)%2]; + +#ifdef USE_PACKED_DEPTH_STENCIL + camera->attach(osg::Camera::PACKED_DEPTH_STENCIL_BUFFER, depthTexture.get()); +#else + camera->attach(osg::Camera::DEPTH_BUFFER, depthTexture.get()); +#endif + +#ifdef USE_TEXTURE_RECTANGLE + osg::ref_ptr colorTexture = new osg::TextureRectangle; +#else + osg::ref_ptr colorTexture = new osg::Texture2D; +#endif + _colorTextures.push_back(colorTexture); + + colorTexture->setTextureSize(_texWidth, _texHeight); + colorTexture->setFilter(osg::Texture::MIN_FILTER, osg::Texture::NEAREST); + colorTexture->setFilter(osg::Texture::MAG_FILTER, osg::Texture::NEAREST); + colorTexture->setWrap(osg::Texture::WRAP_S, osg::Texture::CLAMP_TO_BORDER); + colorTexture->setWrap(osg::Texture::WRAP_T, osg::Texture::CLAMP_TO_BORDER); + colorTexture->setInternalFormat(GL_RGBA); + camera->attach(osg::Camera::COLOR_BUFFER, colorTexture.get()); + + camera->getOrCreateStateSet()->setMode(GL_BLEND, osg::StateAttribute::OFF | osg::StateAttribute::OVERRIDE); + if (0 == i) { + camera->addChild(_scene.get()); + } else { + osg::StateSet* stateSet = camera->getOrCreateStateSet(); + + stateSet->setAttributeAndModes(new osg::AlphaFunc(osg::AlphaFunc::GREATER, 0.01), + osg::StateAttribute::ON | osg::StateAttribute::OVERRIDE); + + stateSet->setTextureAttributeAndModes(_texUnit, prevDepthTexture.get()); + + // Is the default ... + // stateSet->setTextureAttributeAndModes(_texUnit, new osg::TexEnv(osg::TexEnv::MODULATE)); + stateSet->setTextureMode(_texUnit, GL_TEXTURE_GEN_S, osg::StateAttribute::ON); + stateSet->setTextureMode(_texUnit, GL_TEXTURE_GEN_T, osg::StateAttribute::ON); + stateSet->setTextureMode(_texUnit, GL_TEXTURE_GEN_R, osg::StateAttribute::ON); + stateSet->setTextureMode(_texUnit, GL_TEXTURE_GEN_Q, osg::StateAttribute::ON); + + osg::TexGenNode* texGenNode = new osg::TexGenNode; + texGenNode->setReferenceFrame(osg::TexGenNode::ABSOLUTE_RF); + texGenNode->setTextureUnit(_texUnit); + texGenNode->getTexGen()->setMode(osg::TexGen::EYE_LINEAR); + camera->addChild(texGenNode); + camera->addCullCallback(new CullCallback(_texUnit, _texWidth, _texHeight, _offsetValue)); + + texGenNode->addChild(_scene.get()); + } + + _root->addChild(camera); + + osg::Node* geode = createQuad(i, numTiles); + osg::StateSet* stateSet = geode->getOrCreateStateSet(); + stateSet->setTextureAttributeAndModes(0, colorTexture.get(), osg::StateAttribute::ON); + stateSet->setAttribute(new osg::BlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA), osg::StateAttribute::ON); + stateSet->setMode(GL_BLEND, osg::StateAttribute::ON); + stateSet->setMode(GL_LIGHTING, osg::StateAttribute::OFF); + stateSet->setMode(GL_DEPTH_TEST, osg::StateAttribute::OFF); + _compositeCamera->insertChild(0, geode); + } + } + + DepthPeeling(unsigned width, unsigned height) : + _numPasses(8), + _texUnit(1), + _texWidth(width), + _texHeight(height), + _showAllLayers(false), + _depthPeelingEnabled(true), + _offsetValue(8), + _root(new osg::Group), + _scene(new osg::Group) + { + createPeeling(); + } + + void setScene(osg::Node* scene) + { + _scene->removeChildren(0, _scene->getNumChildren()); + _scene->addChild(scene); + } + + osg::Node* getRoot() + { + return _root.get(); + } + + void resize(int width, int height) + { +#ifdef USE_TEXTURE_RECTANGLE + _depthTextures[0]->setTextureSize(width, height); + _depthTextures[1]->setTextureSize(width, height); + for (unsigned i = 0; i < _colorTextures.size(); ++i) + _colorTextures[i]->setTextureSize(width, height); + _texWidth = width; + _texHeight = height; +#else +#ifndef USE_NON_POWER_OF_TWO_TEXTURE + width = nextPowerOfTwo(width); + height = nextPowerOfTwo(height); +#endif + _depthTextures[0]->setTextureSize(width, height); + _depthTextures[1]->setTextureSize(width, height); + for (unsigned i = 0; i < _colorTextures.size(); ++i) + _colorTextures[i]->setTextureSize(width, height); + _texWidth = width; + _texHeight = height; +#endif + createPeeling(); + } + + void setNumPasses(unsigned numPasses) + { + if (numPasses == _numPasses) + return; + if (numPasses == unsigned(-1)) + return; + _numPasses = numPasses; + createPeeling(); + } + unsigned getNumPasses() const + { + return _numPasses; + } + + void setTexUnit(unsigned texUnit) + { + if (texUnit == _texUnit) + return; + _texUnit = texUnit; + createPeeling(); + } + + void setShowAllLayers(bool showAllLayers) + { + if (showAllLayers == _showAllLayers) + return; + _showAllLayers = showAllLayers; + createPeeling(); + } + bool getShowAllLayers() const + { + return _showAllLayers; + } + + void setDepthPeelingEnabled(bool depthPeelingEnabled) + { + if (depthPeelingEnabled == _depthPeelingEnabled) + return; + _depthPeelingEnabled = depthPeelingEnabled; + createPeeling(); + } + bool getDepthPeelingEnabled() const + { + return _depthPeelingEnabled; + } + + void setOffsetValue(unsigned offsetValue) + { + if (offsetValue == _offsetValue) + return; + _offsetValue = offsetValue; + createPeeling(); + } + unsigned getOffsetValue() const + { + return _offsetValue; + } + + unsigned _numPasses; + unsigned _texUnit; + unsigned _texWidth; + unsigned _texHeight; + bool _showAllLayers; + bool _depthPeelingEnabled; + unsigned _offsetValue; + + // The root node that is handed over to the viewer + osg::ref_ptr _root; + + // The scene that is displayed + osg::ref_ptr _scene; + + // The final camera that composites the pre rendered textures to the final picture + osg::ref_ptr _compositeCamera; + +#ifdef USE_TEXTURE_RECTANGLE + osg::ref_ptr _depthTextures[2]; + std::vector > _colorTextures; +#else + osg::ref_ptr _depthTextures[2]; + std::vector > _colorTextures; +#endif +}; + +class EventHandler : public osgGA::GUIEventHandler { +public: + EventHandler(DepthPeeling* depthPeeling) : + _depthPeeling(depthPeeling) + { } + + /** Handle events, return true if handled, false otherwise. */ + virtual bool handle(const osgGA::GUIEventAdapter& ea, osgGA::GUIActionAdapter&, osg::Object*, osg::NodeVisitor*) + { + if (ea.getEventType() == osgGA::GUIEventAdapter::RESIZE) { + _depthPeeling->resize(ea.getWindowWidth(), ea.getWindowHeight()); + return true; + } + + if (ea.getEventType() == osgGA::GUIEventAdapter::KEYDOWN) { + switch (ea.getKey()) { + case 'd': + _depthPeeling->setDepthPeelingEnabled(!_depthPeeling->getDepthPeelingEnabled()); + return true; + case 'm': + _depthPeeling->setNumPasses(_depthPeeling->getNumPasses() + 1); + return true; + case 'n': + _depthPeeling->setNumPasses(_depthPeeling->getNumPasses() - 1); + return true; + case 'p': + _depthPeeling->setOffsetValue(_depthPeeling->getOffsetValue() + 1); + return true; + case 'o': + _depthPeeling->setOffsetValue(_depthPeeling->getOffsetValue() - 1); + return true; + case 'l': + _depthPeeling->setShowAllLayers(!_depthPeeling->getShowAllLayers()); + return true; + default: + return false; + }; + } + + return false; + } + + osg::ref_ptr _depthPeeling; +}; + +int main(int argc, char** argv) +{ + // use an ArgumentParser object to manage the program arguments. + osg::ArgumentParser arguments(&argc, argv); + arguments.getApplicationUsage()->addKeyboardMouseBinding("d", "Toggle depth peeling enabled"); + arguments.getApplicationUsage()->addKeyboardMouseBinding("m", "Increase the number of depth peeling layers"); + arguments.getApplicationUsage()->addKeyboardMouseBinding("n", "Decrease the number of depth peeling layers"); + arguments.getApplicationUsage()->addKeyboardMouseBinding("l", "Toggle display of the individual or composed layer textures"); + arguments.getApplicationUsage()->addKeyboardMouseBinding("p", "Increase the layer offset"); + arguments.getApplicationUsage()->addKeyboardMouseBinding("o", "Decrease the layer offset"); + + // Have the usual viewer + osgViewer::Viewer viewer(arguments); + + osg::DisplaySettings* displaySettings = new osg::DisplaySettings; + viewer.setDisplaySettings(displaySettings); + + // Add the stats handler + viewer.addEventHandler(new osgViewer::StatsHandler); + + // add the help handler + viewer.addEventHandler(new osgViewer::HelpHandler(arguments.getApplicationUsage())); + + // load the data + osg::ref_ptr loadedModel = osgDB::readNodeFiles(arguments); + if (!loadedModel) + { + std::cout << arguments.getApplicationName() <<": No data loaded" << std::endl; + return 1; + } + + // any option left unread are converted into errors to write out later. + arguments.reportRemainingOptionsAsUnrecognized(); + + // report any errors if they have occurred when parsing the program arguments. + if (arguments.errors()) + { + arguments.writeErrorMessages(std::cout); + return 1; + } + + // The initial size sez to 0, 0. We get a resize event for the right size ... + DepthPeeling* depthPeeling = new DepthPeeling(0, 0); + depthPeeling->setScene(loadedModel.get()); + viewer.setSceneData(depthPeeling->getRoot()); + + // Add the event handler for the depth peeling stuff + viewer.addEventHandler(new EventHandler(depthPeeling)); + + return viewer.run(); +}