diff --git a/examples/osgstereoimage/osgstereoimage.cpp b/examples/osgstereoimage/osgstereoimage.cpp index f2207d7b3..28610b752 100644 --- a/examples/osgstereoimage/osgstereoimage.cpp +++ b/examples/osgstereoimage/osgstereoimage.cpp @@ -13,13 +13,325 @@ #include #include +#include #include #include #include #include +#include +#include +#include + +typedef std::vector FileList; + +class SlideEventHandler : public osgGA::GUIEventHandler, public osg::NodeCallback +{ +public: + + SlideEventHandler(); + + META_Object(osgStereImageApp,SlideEventHandler); + + + void set(osg::Switch* sw, osg::TexMat* texmat,float timePerSlide, bool autoSteppingActive); + + virtual void accept(osgGA::GUIEventHandlerVisitor& v) { v.visit(*this); } + + virtual bool handle(const osgGA::GUIEventAdapter& ea,osgGA::GUIActionAdapter&); + + virtual void getUsage(osg::ApplicationUsage& usage) const; + + virtual void operator()(osg::Node* node, osg::NodeVisitor* nv); + + void nextSlide(); + + void previousSlide(); + + void scaleImage(float s); + + void rotateImage(float rx,float ry); + + +protected: + + ~SlideEventHandler() {} + SlideEventHandler(const SlideEventHandler&,const osg::CopyOp&) {} + + osg::ref_ptr _switch; + osg::ref_ptr _texmat; + bool _firstTraversal; + unsigned int _activeSlide; + double _previousTime; + double _timePerSlide; + bool _autoSteppingActive; + +}; + +SlideEventHandler::SlideEventHandler(): + _switch(0), + _texmat(0), + _firstTraversal(true), + _activeSlide(0), + _previousTime(-1.0f), + _timePerSlide(5.0), + _autoSteppingActive(false) +{ +} + +void SlideEventHandler::set(osg::Switch* sw, osg::TexMat* texmat,float timePerSlide, bool autoSteppingActive) +{ + _switch = sw; + _switch->setUpdateCallback(this); + + _texmat = texmat; + + _timePerSlide = timePerSlide; + _autoSteppingActive = autoSteppingActive; +} + +bool SlideEventHandler::handle(const osgGA::GUIEventAdapter& ea,osgGA::GUIActionAdapter&) +{ + switch(ea.getEventType()) + { + case(osgGA::GUIEventAdapter::KEYDOWN): + { + if (ea.getKey()=='a') + { + _autoSteppingActive = !_autoSteppingActive; + _previousTime = ea.time(); + return true; + } + else if (ea.getKey()=='n') + { + nextSlide(); + return true; + } + else if (ea.getKey()=='p') + { + previousSlide(); + return true; + } + else if (ea.getKey()=='z') + { + scaleImage(0.99f); + return true; + } + else if (ea.getKey()=='x') + { + scaleImage(1.01f); + return true; + } + else if (ea.getKey()==' ') + { + if (_texmat.valid()) _texmat->setMatrix(osg::Matrix::identity()); + return true; + } + return false; + } + case(osgGA::GUIEventAdapter::DRAG): + case(osgGA::GUIEventAdapter::MOVE): + { + static int px = ea.getX(); + static int py = ea.getY(); + + int dx = ea.getX()-px; + int dy = ea.getY()-py; + + px = ea.getX(); + py = ea.getY(); + + rotateImage((float)dx/(float)(ea.getXmax()-ea.getXmin()),(float)dy/(float)(ea.getYmax()-ea.getYmin())); + + return true; + } + + default: + return false; + } +} + +void SlideEventHandler::getUsage(osg::ApplicationUsage& usage) const +{ + usage.addKeyboardMouseBinding("Space","Reset the image position to center"); + usage.addKeyboardMouseBinding("a","Toggle on/off the automatic advancement for image to image"); + usage.addKeyboardMouseBinding("n","Advance to next image"); + usage.addKeyboardMouseBinding("p","Move to previous image"); + usage.addKeyboardMouseBinding("z","Zoom into the image"); + usage.addKeyboardMouseBinding("x","Zoom out of the image"); +} + +void SlideEventHandler::operator()(osg::Node* node, osg::NodeVisitor* nv) +{ + if (_autoSteppingActive && nv->getFrameStamp()) + { + double time = nv->getFrameStamp()->getReferenceTime(); + + if (_firstTraversal) + { + _firstTraversal = false; + _previousTime = time; + } + else if (time-_previousTime>_timePerSlide) + { + _previousTime = time; + + nextSlide(); + } + + } + + traverse(node,nv); +} + +void SlideEventHandler::nextSlide() +{ + if (_switch->getNumChildren()==0) return; + + ++_activeSlide; + if (_activeSlide>=_switch->getNumChildren()) _activeSlide = 0; + + _switch->setSingleChildOn(_activeSlide); +} + +void SlideEventHandler::previousSlide() +{ + if (_switch->getNumChildren()==0) return; + + if (_activeSlide==0) _activeSlide = _switch->getNumChildren()-1; + else --_activeSlide; + + _switch->setSingleChildOn(_activeSlide); +} + +void SlideEventHandler::scaleImage(float s) +{ + if (_texmat.valid()) + { + _texmat->setMatrix(_texmat->getMatrix()*osg::Matrix::translate(-0.5f,-0.5f,0.0f)*osg::Matrix::scale(s,s,1.0f)*osg::Matrix::translate(0.5f,0.5f,0.0f)); + } +} + +void SlideEventHandler::rotateImage(float rx,float ry) +{ + if (_texmat.valid()) + { + const float scale = 0.5f; + _texmat->setMatrix(_texmat->getMatrix()*osg::Matrix::translate(-rx*scale,ry*scale,0.0f)); + } +} + +osg::Geode* createSectorForImage(osg::Image* image,float s,float t, float radius, float height, float length) +{ + + int numSegments = 20; + float Theta = length/radius; + float dTheta = Theta/(float)(numSegments-1); + + float ThetaZero = height*s/(t*radius); + + // set up the texture. + osg::Texture2D* texture = new osg::Texture2D; + texture->setFilter(osg::Texture2D::MIN_FILTER,osg::Texture2D::LINEAR); + texture->setFilter(osg::Texture2D::MAG_FILTER,osg::Texture2D::LINEAR); + texture->setImage(image); + + // set up the drawstate. + osg::StateSet* dstate = new osg::StateSet; + dstate->setMode(GL_CULL_FACE,osg::StateAttribute::OFF); + dstate->setMode(GL_LIGHTING,osg::StateAttribute::OFF); + dstate->setTextureAttributeAndModes(0, texture,osg::StateAttribute::ON); + + // set up the geoset. + osg::Geometry* geom = new osg::Geometry; + geom->setStateSet(dstate); + + osg::Vec3Array* coords = new osg::Vec3Array(); + osg::Vec2Array* tcoords = new osg::Vec2Array(); + + int i; + float angle = -Theta/2.0f; + for(i=0; + ipush_back(osg::Vec3(sinf(angle)*radius,cosf(angle)*radius,height*0.5f)); // top + coords->push_back(osg::Vec3(sinf(angle)*radius,cosf(angle)*radius,-height*0.5f)); // bottom. + + tcoords->push_back(osg::Vec2(angle/ThetaZero+0.5f,1.0f)); // top + tcoords->push_back(osg::Vec2(angle/ThetaZero+0.5f,0.0f)); // bottom. + + } + + osg::Vec4Array* colors = new osg::Vec4Array(); + colors->push_back(osg::Vec4(1.0f,1.0f,1.0f,1.0f)); + + osg::DrawArrays* elements = new osg::DrawArrays(osg::PrimitiveSet::QUAD_STRIP,0,coords->size()); + + + + geom->setVertexArray(coords); + geom->setTexCoordArray(0,tcoords); + geom->setColorArray(colors); + geom->setColorBinding(osg::Geometry::BIND_OVERALL); + + geom->addPrimitiveSet(elements); + + // set up the geode. + osg::Geode* geode = new osg::Geode; + geode->addDrawable(geom); + + return geode; + +} + +// create a switch containing a set of child each containing a +// stereo image pair. +osg::Switch* createScene(const FileList& fileList, float radius, float height, float length) +{ + osg::Switch* sw = new osg::Switch; + + // load the images. + for(unsigned int i=0;i+1 imageLeft = osgDB::readImageFile(fileList[i]); + osg::ref_ptr imageRight = osgDB::readImageFile(fileList[i+1]); + if (imageLeft.valid() && imageRight.valid()) + { + float average_s = (imageLeft->s()+imageRight->s())*0.5f; + float average_t = (imageLeft->t()+imageRight->t())*0.5f; + + osg::Geode* geodeLeft = createSectorForImage(imageLeft.get(),average_s,average_t, radius, height, length); + geodeLeft->setNodeMask(0x01); + + osg::Geode* geodeRight = createSectorForImage(imageRight.get(),average_s,average_t, radius, height, length); + geodeRight->setNodeMask(0x02); + + osg::ref_ptr imageGroup = new osg::Group; + + imageGroup->addChild(geodeLeft); + imageGroup->addChild(geodeRight); + + sw->addChild(imageGroup.get()); + } + else + { + std::cout << "Warning: Unable to load both image files, '"<getNumChildren()>0) + { + // select first child. + sw->setSingleChildOn(0); + } + + return sw; +} + int main( int argc, char **argv ) { @@ -28,6 +340,8 @@ int main( int argc, char **argv ) // set up the usage document, in case we need to print out how to use this program. arguments.getApplicationUsage()->setCommandLineUsage(arguments.getProgramName()+" [options] image_file_left_eye image_file_right_eye"); + arguments.getApplicationUsage()->addCommandLineOption("-d ","Time delay in sceonds between the display of successive image pairs when in auto advance mode."); + arguments.getApplicationUsage()->addCommandLineOption("-a","Enter auto advance of image pairs on start up."); arguments.getApplicationUsage()->addCommandLineOption("-h or --help","Display this information"); @@ -35,11 +349,24 @@ int main( int argc, char **argv ) osgProducer::Viewer viewer(arguments); // set up the value with sensible default event handlers. - viewer.setUpViewer(osgProducer::Viewer::STANDARD_SETTINGS); + viewer.setUpViewer(osgProducer::Viewer::ESCAPE_SETS_DONE); + + // register the handler to add keyboard and mosue handling. + SlideEventHandler* seh = new SlideEventHandler(); + viewer.getEventHandlerList().push_front(seh); + // get details on keyboard and mouse bindings used by the viewer. viewer.getUsage(*arguments.getApplicationUsage()); + // read any time delay argument. + float timeDelayBetweenSlides = 5.0f; + while (arguments.read("-d",timeDelayBetweenSlides)) {} + + bool autoSteppingActive = false; + while (arguments.read("-a")) autoSteppingActive = true; + + // if user request help write it out to cout. if (arguments.read("-h") || arguments.read("--help")) { @@ -58,48 +385,13 @@ int main( int argc, char **argv ) } // extract the filenames from the arguments list. - std::string fileLeft,fileRight; + FileList fileList; for(int pos=1;poss()+imageRight->s())*0.5f; - float average_t = (imageLeft->t()+imageRight->t())*0.5f; - - osg::Geode* geodeLeft = osg::createGeodeForImage(imageLeft,average_s,average_t); - geodeLeft->setNodeMask(0x01); - - osg::Geode* geodeRight = osg::createGeodeForImage(imageRight,average_s,average_t); - geodeRight->setNodeMask(0x02); - - osg::ref_ptr rootNode = new osg::Group; - rootNode->addChild(geodeLeft); - rootNode->addChild(geodeRight); - - // set the scene to render - viewer.setSceneData(rootNode.get()); - // set up the use of stereo by default. osg::DisplaySettings* ds = viewer.getDisplaySettings(); if (!ds) ds = osg::DisplaySettings::instance(); @@ -108,6 +400,36 @@ int main( int argc, char **argv ) // create the windows and run the threads. viewer.realize(Producer::CameraGroup::ThreadPerCamera); + // now the windows have been realized we switch off the cursor to prevent it + // distracting the people seeing the stereo images. + float fovy = 1.0f; + for( unsigned int i = 0; i < viewer.getCameraConfig()->getNumberOfCameras(); i++ ) + { + Producer::Camera* cam = viewer.getCameraConfig()->getCamera(i); + Producer::RenderSurface* rs = cam->getRenderSurface(); + rs->useCursor(false); + fovy = cam->getLensVerticalFov(); + } + + float radius = 1.0f; + float height = 2*radius*tanf(fovy*0.5f); + float length = osg::PI*radius; // half a cylinder. + + // creat the scene from the file list. + osg::ref_ptr rootNode = createScene(fileList,radius,height,length); + + // use a texure matrix to control the placement of the image. + osg::StateSet* stateset = rootNode->getOrCreateStateSet(); + osg::TexMat* texmat = new osg::TexMat; + stateset->setTextureAttribute(0,texmat); + + + osgDB::writeNodeFile(*rootNode,"test.osg"); + + // set the scene to render + viewer.setSceneData(rootNode.get()); + + // set all the sceneview's up so that their left and right add cull masks are set up. for(osgProducer::OsgCameraGroup::SceneHandlerList::iterator itr=viewer.getSceneHandlerList().begin(); itr!=viewer.getSceneHandlerList().end(); @@ -117,9 +439,18 @@ int main( int argc, char **argv ) sceneview->setCullMask(0xffffffff); sceneview->setCullMaskLeft(0x00000001); sceneview->setCullMaskRight(0x00000002); + sceneview->setFusionDistance(osgUtil::SceneView::USE_FUSION_DISTANCE_VALUE,radius); + sceneview->setCamera(0); } + // set up the SlideEventHandler. + seh->set(rootNode.get(),texmat,timeDelayBetweenSlides,autoSteppingActive); + + + osg::Matrix homePosition; + homePosition.makeLookAt(osg::Vec3(0.0f,0.0f,0.0f),osg::Vec3(0.0f,1.0f,0.0f),osg::Vec3(0.0f,0.0f,1.0f)); + while( !viewer.done() ) { // wait for all cull and draw threads to complete. @@ -129,6 +460,8 @@ int main( int argc, char **argv ) // call all node update callbacks and animations. viewer.update(); + viewer.setView(homePosition.ptr()); + // fire off the cull and draw traversals of the scene. viewer.frame();