From 3aebed206e2da00623d9d81fa0eb2b2abef72c23 Mon Sep 17 00:00:00 2001 From: Robert Osfield Date: Tue, 14 Oct 2008 14:58:10 +0000 Subject: [PATCH] From J.P Delport, game of life example that demonstrates ping pong render to texture rendering --- examples/CMakeLists.txt | 1 + examples/osggameoflife/CMakeLists.txt | 12 ++ examples/osggameoflife/GameOfLifePass.cpp | 204 ++++++++++++++++++++++ examples/osggameoflife/GameOfLifePass.h | 83 +++++++++ examples/osggameoflife/osggameoflife.cpp | 154 ++++++++++++++++ 5 files changed, 454 insertions(+) create mode 100644 examples/osggameoflife/CMakeLists.txt create mode 100644 examples/osggameoflife/GameOfLifePass.cpp create mode 100644 examples/osggameoflife/GameOfLifePass.h create mode 100644 examples/osggameoflife/osggameoflife.cpp diff --git a/examples/CMakeLists.txt b/examples/CMakeLists.txt index 0244109a0..9eea433a9 100644 --- a/examples/CMakeLists.txt +++ b/examples/CMakeLists.txt @@ -36,6 +36,7 @@ IF(DYNAMIC_OPENSCENEGRAPH) ADD_SUBDIRECTORY(osgfont) ADD_SUBDIRECTORY(osgforest) ADD_SUBDIRECTORY(osgfxbrowser) + ADD_SUBDIRECTORY(osggameoflife) ADD_SUBDIRECTORY(osggeodemo) ADD_SUBDIRECTORY(osggeometry) ADD_SUBDIRECTORY(osggeometryshaders) diff --git a/examples/osggameoflife/CMakeLists.txt b/examples/osggameoflife/CMakeLists.txt new file mode 100644 index 000000000..78c314793 --- /dev/null +++ b/examples/osggameoflife/CMakeLists.txt @@ -0,0 +1,12 @@ +SET(TARGET_SRC + GameOfLifePass.cpp + osggameoflife.cpp + gameoflife_frag.cpp +) + +SET(TARGET_H + GameOfLifePass.h +) + +#### end var setup ### +SETUP_EXAMPLE(osggameoflife) diff --git a/examples/osggameoflife/GameOfLifePass.cpp b/examples/osggameoflife/GameOfLifePass.cpp new file mode 100644 index 000000000..78456ec8d --- /dev/null +++ b/examples/osggameoflife/GameOfLifePass.cpp @@ -0,0 +1,204 @@ +/* OpenSceneGraph example, osggameoflife. +* +* 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 "GameOfLifePass.h" +#include +#include + +ProcessPass::ProcessPass(osg::TextureRectangle *in_tex, + osg::TextureRectangle *out_tex, + int width, int height): + _TextureWidth(width), + _TextureHeight(height) +{ + _RootGroup = new osg::Group; + + _InTexture = in_tex; + _OutTexture = out_tex; + + _Camera = new osg::Camera; + setupCamera(); + _Camera->addChild(createTexturedQuad().get()); + + _RootGroup->addChild(_Camera.get()); + + setShader("shaders/gameoflife.frag"); +} + +ProcessPass::~ProcessPass() +{ +} + +osg::ref_ptr ProcessPass::createTexturedQuad() +{ + osg::ref_ptr top_group = new osg::Group; + + osg::ref_ptr quad_geode = new osg::Geode; + + osg::ref_ptr quad_coords = new osg::Vec3Array; // vertex coords + // counter-clockwise + quad_coords->push_back(osg::Vec3d(0, 0, -1)); + quad_coords->push_back(osg::Vec3d(1, 0, -1)); + quad_coords->push_back(osg::Vec3d(1, 1, -1)); + quad_coords->push_back(osg::Vec3d(0, 1, -1)); + + osg::ref_ptr quad_tcoords = new osg::Vec2Array; // texture coords + quad_tcoords->push_back(osg::Vec2(0, 0)); + quad_tcoords->push_back(osg::Vec2(_TextureWidth, 0)); + quad_tcoords->push_back(osg::Vec2(_TextureWidth, _TextureHeight)); + quad_tcoords->push_back(osg::Vec2(0, _TextureHeight)); + + osg::ref_ptr quad_geom = new osg::Geometry; + osg::ref_ptr quad_da = new osg::DrawArrays(osg::PrimitiveSet::QUADS,0,4); + + osg::ref_ptr quad_colors = new osg::Vec4Array; + quad_colors->push_back(osg::Vec4(1.0f,1.0f,1.0f,1.0f)); + + quad_geom->setVertexArray(quad_coords.get()); + quad_geom->setTexCoordArray(0, quad_tcoords.get()); + quad_geom->addPrimitiveSet(quad_da.get()); + quad_geom->setColorArray(quad_colors.get()); + quad_geom->setColorBinding(osg::Geometry::BIND_OVERALL); + + _StateSet = quad_geom->getOrCreateStateSet(); + _StateSet->setMode(GL_LIGHTING,osg::StateAttribute::OFF); + _StateSet->setMode(GL_DEPTH_TEST,osg::StateAttribute::OFF); + + _StateSet->setTextureAttributeAndModes(0, _InTexture.get(), osg::StateAttribute::ON); + + _StateSet->addUniform(new osg::Uniform("textureIn", 0)); + + quad_geode->addDrawable(quad_geom.get()); + + top_group->addChild(quad_geode.get()); + + return top_group; +} + +void ProcessPass::setupCamera() +{ + // clearing + _Camera->setClearMask(GL_DEPTH_BUFFER_BIT); + + // projection and view + _Camera->setProjectionMatrix(osg::Matrix::ortho2D(0,1,0,1)); + _Camera->setReferenceFrame(osg::Transform::ABSOLUTE_RF); + _Camera->setViewMatrix(osg::Matrix::identity()); + + // viewport + _Camera->setViewport(0, 0, _TextureWidth, _TextureHeight); + + _Camera->setRenderOrder(osg::Camera::PRE_RENDER); + _Camera->setRenderTargetImplementation(osg::Camera::FRAME_BUFFER_OBJECT); + + _Camera->attach(osg::Camera::BufferComponent(osg::Camera::COLOR_BUFFER), _OutTexture.get()); +} + +void ProcessPass::setShader(std::string filename) +{ + std::string foundFile = osgDB::findDataFile(filename); + if (foundFile.empty()) + { + osg::notify(osg::NOTICE)<<"Could not file shader file: "< fshader = new osg::Shader( osg::Shader::FRAGMENT ); + fshader->loadShaderSourceFromFile(foundFile); + + _FragmentProgram = 0; + _FragmentProgram = new osg::Program; + + _FragmentProgram->addShader(fshader.get()); + + _StateSet->setAttributeAndModes(_FragmentProgram.get(), osg::StateAttribute::ON | osg::StateAttribute::OVERRIDE ); +} + +//////////////////////////////////////////////////////////////////////// + +GameOfLifePass::GameOfLifePass(osg::Image *in_image) +{ + _TextureWidth = in_image->s(); + _TextureHeight = in_image->t(); + + _RootGroup = new osg::Group; + + _BranchSwith[0] = new osg::Switch; + _BranchSwith[1] = new osg::Switch; + + _RootGroup->addChild(_BranchSwith[0].get()); + _RootGroup->addChild(_BranchSwith[1].get()); + + _ActiveBranch = 0; + activateBranch(); + + createOutputTextures(); + _InOutTextureLife[0]->setImage(in_image); + + _ProcessPass[0] = new ProcessPass(_InOutTextureLife[0].get(), + _InOutTextureLife[1].get(), + _TextureWidth, _TextureHeight); + + // For the other pass, the input/output textures are flipped + _ProcessPass[1] = new ProcessPass(_InOutTextureLife[1].get(), + _InOutTextureLife[0].get(), + _TextureWidth, _TextureHeight); + + _BranchSwith[0]->addChild(_ProcessPass[0]->getRoot().get()); + _BranchSwith[1]->addChild(_ProcessPass[1]->getRoot().get()); +} + +GameOfLifePass::~GameOfLifePass() +{ + delete _ProcessPass[0]; + delete _ProcessPass[1]; +} + +osg::ref_ptr GameOfLifePass::getOutputTexture() +{ + int out_tex = (_ActiveBranch == 0) ? 1 : 0; + return _ProcessPass[out_tex]->getOutputTexture(); +} + +void GameOfLifePass::activateBranch() +{ + int onb = _ActiveBranch; + int offb = (onb == 1) ? 0 : 1; + + _BranchSwith[onb]->setAllChildrenOn(); + _BranchSwith[offb]->setAllChildrenOff(); +} + +void GameOfLifePass::flip() +{ + _ActiveBranch = (_ActiveBranch == 1) ? 0 : 1; + activateBranch(); +} + +void GameOfLifePass::createOutputTextures() +{ + for (int i=0; i<2; i++) { + _InOutTextureLife[i] = new osg::TextureRectangle; + + _InOutTextureLife[i]->setTextureSize(_TextureWidth, _TextureHeight); + _InOutTextureLife[i]->setInternalFormat(GL_RGBA); + _InOutTextureLife[i]->setFilter(osg::Texture2D::MIN_FILTER,osg::Texture2D::NEAREST); + _InOutTextureLife[i]->setFilter(osg::Texture2D::MAG_FILTER,osg::Texture2D::NEAREST); + } +} + diff --git a/examples/osggameoflife/GameOfLifePass.h b/examples/osggameoflife/GameOfLifePass.h new file mode 100644 index 000000000..7ac8d43c1 --- /dev/null +++ b/examples/osggameoflife/GameOfLifePass.h @@ -0,0 +1,83 @@ +/* OpenSceneGraph example, osggameoflife. +* +* 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. +*/ + +#ifndef GAMEOFLIFEPASS_H +#define GAMEOFLIFEPASS_H 1 + +#include +#include +#include +#include +#include +#include +#include +#include + +class ProcessPass { + public: + ProcessPass(osg::TextureRectangle *in_tex, + osg::TextureRectangle *out_tex, + int width, int height); + ~ProcessPass(); + osg::ref_ptr getRoot() { return _RootGroup; } + osg::ref_ptr getOutputTexture() { return _OutTexture; } + void setShader(std::string filename); + + private: + osg::ref_ptr createTexturedQuad(); + void setupCamera(); + + osg::ref_ptr _RootGroup; + osg::ref_ptr _Camera; + osg::ref_ptr _InTexture; + osg::ref_ptr _OutTexture; + int _TextureWidth; + int _TextureHeight; + osg::ref_ptr _FragmentProgram; + osg::ref_ptr _StateSet; +}; + +class GameOfLifePass { + public: + GameOfLifePass(osg::Image *in_image); + ~GameOfLifePass(); + osg::ref_ptr getRoot() { return _RootGroup; } + osg::ref_ptr getOutputTexture(); + void setShader(std::string filename); + // Switch branches so we flip textures + void flip(); + + private: + osg::ref_ptr createTexturedQuad(); + void setupCamera(); + void createOutputTextures(); + void activateBranch(); + + osg::ref_ptr _RootGroup; + osg::ref_ptr _Camera; + osg::ref_ptr _InOutTextureLife[2]; + int _TextureWidth; + int _TextureHeight; + int _ActiveBranch; + osg::ref_ptr _FragmentProgram; + osg::ref_ptr _StateSet; + osg::ref_ptr _BranchSwith[2]; + ProcessPass *_ProcessPass[2]; +}; + +#endif //GAMEOFLIFEPASS_H diff --git a/examples/osggameoflife/osggameoflife.cpp b/examples/osggameoflife/osggameoflife.cpp new file mode 100644 index 000000000..83a1e8cfa --- /dev/null +++ b/examples/osggameoflife/osggameoflife.cpp @@ -0,0 +1,154 @@ +/* OpenSceneGraph example, osggameoflife. +* +* 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 "GameOfLifePass.h" + +GameOfLifePass *golpass; +osg::ref_ptr geomss; // stateset where we can attach textures + +osg::Node* createScene(osg::Image *start_im) +{ + int width = start_im->s(); + int height = start_im->t(); + + osg::Group* topnode = new osg::Group; + + // create quad to display image on + osg::ref_ptr geode = new osg::Geode(); + + // each geom will contain a quad + osg::ref_ptr da = new osg::DrawArrays(osg::PrimitiveSet::QUADS,0,4); + + osg::ref_ptr colors = new osg::Vec4Array; + colors->push_back(osg::Vec4(1.0f,1.0f,1.0f,1.0f)); + + osg::ref_ptr tcoords = new osg::Vec2Array; // texture coords + tcoords->push_back(osg::Vec2(0, 0)); + tcoords->push_back(osg::Vec2(width, 0)); + tcoords->push_back(osg::Vec2(width, height)); + tcoords->push_back(osg::Vec2(0, height)); + + osg::ref_ptr vcoords = new osg::Vec3Array; // vertex coords + osg::ref_ptr geom = new osg::Geometry; + + // initial viewer camera looks along y + vcoords->push_back(osg::Vec3d(0, 0, 0)); + vcoords->push_back(osg::Vec3d(width, 0, 0)); + vcoords->push_back(osg::Vec3d(width, 0, height)); + vcoords->push_back(osg::Vec3d(0, 0, height)); + + geom->setVertexArray(vcoords.get()); + geom->setTexCoordArray(0,tcoords.get()); + geom->addPrimitiveSet(da.get()); + geom->setColorArray(colors.get()); + geom->setColorBinding(osg::Geometry::BIND_OVERALL); + geomss = geom->getOrCreateStateSet(); + geomss->setMode(GL_LIGHTING, osg::StateAttribute::OFF); + + geode->addDrawable(geom.get()); + + topnode->addChild(geode.get()); + + // create the ping pong processing passes + golpass = new GameOfLifePass(start_im); + topnode->addChild(golpass->getRoot().get()); + + // attach the output of the processing to the geom + geomss->setTextureAttributeAndModes(0, + golpass->getOutputTexture().get(), + osg::StateAttribute::ON); + return topnode; +} + +int main(int argc, char *argv[]) +{ + // use an ArgumentParser object to manage the program arguments. + osg::ArgumentParser arguments(&argc,argv); + + // set up the usage document, in case we need to print out how to use this program. + arguments.getApplicationUsage()->setDescription(arguments.getApplicationName()+" is the example which demonstrates ping pong rendering with FBOs and mutliple rendering branches. It uses Conway's Game of Life to illustrate the concept."); + arguments.getApplicationUsage()->setCommandLineUsage(arguments.getApplicationName()+" [options] --startim start_image"); + arguments.getApplicationUsage()->addCommandLineOption("-h or --help","Display this information"); + arguments.getApplicationUsage()->addCommandLineOption("--startim","The initial image to seed the game of life with."); + + // if user request help write it out to cout. + if (arguments.read("-h") || arguments.read("--help")) + { + arguments.getApplicationUsage()->write(std::cout); + return 1; + } + + std::string startName(""); + while(arguments.read("--startim", startName)) {} + + if (startName == "") { + arguments.getApplicationUsage()->write(std::cout); + return 1; + } + + // load the image + osg::ref_ptr startIm = osgDB::readImageFile(startName); + + if (!startIm) { + std::cout << "Could not load start image.\n"; + return(1); + } + + osg::Node* scene = createScene(startIm.get()); + + // construct the viewer. + osgViewer::Viewer viewer; + viewer.setThreadingModel(osgViewer::Viewer::SingleThreaded); + + // add the stats handler + viewer.addEventHandler(new osgViewer::StatsHandler); + + viewer.setSceneData(scene); + + viewer.realize(); + viewer.setCameraManipulator( new osgGA::TrackballManipulator ); + + while(!viewer.done()) + { + viewer.frame(); + // flip the textures after we've completed a frame + golpass->flip(); + // attach the proper output to view + geomss->setTextureAttributeAndModes(0, + golpass->getOutputTexture().get(), + osg::StateAttribute::ON); + } + + return 0; +}