root/OpenSceneGraph/trunk/examples/osgstereoimage/osgstereoimage.cpp @ 2663

Revision 2663, 17.5 kB (checked in by robert, 10 years ago)

Fixed the handling of the Producer Fovy so that it accounts for Producer
now using degrees in set and get methods

  • Property svn:eol-style set to native
  • Property svn:keywords set to Author Date Id Revision
Line 
1/* -*-c++-*- OpenSceneGraph - Copyright (C) 1998-2003 Robert Osfield
2 *
3 * This library is open source and may be redistributed and/or modified under 
4 * the terms of the OpenSceneGraph Public License (OSGPL) version 0.0 or
5 * (at your option) any later version.  The full license is in LICENSE file
6 * included with this distribution, and on the openscenegraph.org website.
7 *
8 * This library is distributed in the hope that it will be useful,
9 * but WITHOUT ANY WARRANTY; without even the implied warranty of
10 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
11 * OpenSceneGraph Public License for more details.
12*/
13
14#include <osgProducer/Viewer>
15#include <osgDB/ReadFile>
16#include <osgDB/WriteFile>
17#include <osgUtil/Optimizer>
18
19#include <osg/Geode>
20#include <osg/Notify>
21#include <osg/MatrixTransform>
22#include <osg/Switch>
23#include <osg/TexMat>
24#include <osg/Texture2D>
25
26typedef std::vector<std::string> FileList;
27
28class SlideEventHandler : public osgGA::GUIEventHandler, public osg::NodeCallback
29{
30public:
31
32    SlideEventHandler();
33   
34    META_Object(osgStereImageApp,SlideEventHandler);
35
36
37    void set(osg::Switch* sw, float offsetX, float offsetY, osg::TexMat* texmatLeft, osg::TexMat* texmatRight, float timePerSlide, bool autoSteppingActive);
38
39    virtual void accept(osgGA::GUIEventHandlerVisitor& v) { v.visit(*this); }
40
41    virtual bool handle(const osgGA::GUIEventAdapter& ea,osgGA::GUIActionAdapter&);
42   
43    virtual void getUsage(osg::ApplicationUsage& usage) const;
44
45    virtual void operator()(osg::Node* node, osg::NodeVisitor* nv);
46   
47    void nextSlide();
48   
49    void previousSlide();
50   
51    void scaleImage(float s);
52   
53    void offsetImage(float ds,float dt);
54
55    void rotateImage(float rx,float ry);
56
57    void initTexMatrices();
58
59protected:
60
61    ~SlideEventHandler() {}
62    SlideEventHandler(const SlideEventHandler&,const osg::CopyOp&) {}
63
64    osg::ref_ptr<osg::Switch>   _switch;
65    osg::ref_ptr<osg::TexMat>   _texmatLeft;
66    osg::ref_ptr<osg::TexMat>   _texmatRight;
67    bool                        _firstTraversal;
68    unsigned int                _activeSlide;
69    double                      _previousTime;
70    double                      _timePerSlide;
71    bool                        _autoSteppingActive;
72    float                       _initSeperationX;
73    float                       _currentSeperationX;
74    float                       _initSeperationY;
75    float                       _currentSeperationY;
76       
77};
78
79SlideEventHandler::SlideEventHandler():
80    _switch(0),
81    _texmatLeft(0),
82    _texmatRight(0),
83    _firstTraversal(true),
84    _activeSlide(0),
85    _previousTime(-1.0f),
86    _timePerSlide(5.0),
87    _autoSteppingActive(false)
88{
89}
90
91void SlideEventHandler::set(osg::Switch* sw, float offsetX, float offsetY, osg::TexMat* texmatLeft, osg::TexMat* texmatRight, float timePerSlide, bool autoSteppingActive)
92{
93    _switch = sw;
94    _switch->setUpdateCallback(this);
95
96    _texmatLeft = texmatLeft;
97    _texmatRight = texmatRight;
98
99    _timePerSlide = timePerSlide;
100    _autoSteppingActive = autoSteppingActive;   
101   
102    _initSeperationX = offsetX;
103    _currentSeperationX = _initSeperationX;
104
105    _initSeperationY = offsetY;
106    _currentSeperationY = _initSeperationY;
107
108    initTexMatrices();
109
110}
111
112bool SlideEventHandler::handle(const osgGA::GUIEventAdapter& ea,osgGA::GUIActionAdapter&)
113{
114    switch(ea.getEventType())
115    {
116        case(osgGA::GUIEventAdapter::KEYDOWN):
117        {
118            if (ea.getKey()=='a')
119            {
120                _autoSteppingActive = !_autoSteppingActive;
121                _previousTime = ea.time();
122                return true;
123            }
124            else if (ea.getKey()=='n')
125            {
126                nextSlide();
127                return true;
128            }
129            else if (ea.getKey()=='p')
130            {
131                previousSlide();
132                return true;
133            }
134            else if (ea.getKey()=='w')
135            {
136                scaleImage(0.99f);
137                return true;
138            }
139            else if (ea.getKey()=='s')
140            {
141                scaleImage(1.01f);
142                return true;
143            }
144            else if (ea.getKey()=='j')
145            {
146                offsetImage(-0.001f,0.0f);
147                return true;
148            }
149            else if (ea.getKey()=='k')
150            {
151                offsetImage(0.001f,0.0f);
152                return true;
153            }
154            else if (ea.getKey()=='i')
155            {
156                offsetImage(0.0f,-0.001f);
157                return true;
158            }
159            else if (ea.getKey()=='m')
160            {
161                offsetImage(0.0f,0.001f);
162                return true;
163            }
164            else if (ea.getKey()==' ')
165            {
166                initTexMatrices();
167                return true;
168            }
169            return false;
170        }
171        case(osgGA::GUIEventAdapter::DRAG):
172        case(osgGA::GUIEventAdapter::MOVE):
173        {
174            static float px = ea.getXnormalized();
175            static float py = ea.getYnormalized();
176           
177            float dx = ea.getXnormalized()-px;
178            float dy = ea.getYnormalized()-py;
179           
180            px = ea.getXnormalized();
181            py = ea.getYnormalized();
182           
183            rotateImage(dx,dy);
184           
185            return true;
186        }
187
188        default:
189            return false;
190    }
191}
192
193void SlideEventHandler::getUsage(osg::ApplicationUsage& usage) const
194{
195    usage.addKeyboardMouseBinding("Space","Reset the image position to center");
196    usage.addKeyboardMouseBinding("a","Toggle on/off the automatic advancement for image to image");
197    usage.addKeyboardMouseBinding("n","Advance to next image");
198    usage.addKeyboardMouseBinding("p","Move to previous image");
199    usage.addKeyboardMouseBinding("q","Zoom into the image");
200    usage.addKeyboardMouseBinding("a","Zoom out of the image");
201    usage.addKeyboardMouseBinding("j","Reduce horizontal offset");
202    usage.addKeyboardMouseBinding("k","Increase horizontal offset");
203    usage.addKeyboardMouseBinding("m","Reduce vertical offset");
204    usage.addKeyboardMouseBinding("i","Increase vertical offset");
205}
206
207void SlideEventHandler::operator()(osg::Node* node, osg::NodeVisitor* nv)
208{
209    if (_autoSteppingActive && nv->getFrameStamp())
210    {
211        double time = nv->getFrameStamp()->getReferenceTime();
212       
213        if (_firstTraversal)
214        {
215            _firstTraversal = false;
216            _previousTime = time;
217        }
218        else if (time-_previousTime>_timePerSlide)
219        {
220            _previousTime = time;
221           
222            nextSlide();
223        }
224       
225    }
226
227    traverse(node,nv);
228}
229
230void SlideEventHandler::nextSlide()
231{
232    if (_switch->getNumChildren()==0) return;
233
234    ++_activeSlide;
235    if (_activeSlide>=_switch->getNumChildren()) _activeSlide = 0;
236
237    _switch->setSingleChildOn(_activeSlide);
238}
239
240void SlideEventHandler::previousSlide()
241{
242    if (_switch->getNumChildren()==0) return;
243
244    if (_activeSlide==0) _activeSlide = _switch->getNumChildren()-1;
245    else --_activeSlide;
246
247    _switch->setSingleChildOn(_activeSlide);
248}
249
250void SlideEventHandler::scaleImage(float s)
251{
252    _texmatLeft->setMatrix(_texmatLeft->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));
253    _texmatRight->setMatrix(_texmatRight->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));
254}
255
256void SlideEventHandler::offsetImage(float ds,float dt)
257{
258    _currentSeperationX+=ds;
259    _currentSeperationY+=dt;
260    osg::notify(osg::NOTICE)<<"image offset x = "<<_currentSeperationX<<"  y ="<<_currentSeperationY<<std::endl;
261    _texmatLeft->setMatrix(_texmatLeft->getMatrix()*osg::Matrix::translate(ds,dt,0.0f));
262    _texmatRight->setMatrix(_texmatRight->getMatrix()*osg::Matrix::translate(-ds,-dt,0.0f));
263}
264
265void SlideEventHandler::rotateImage(float rx,float ry)
266{
267    const float scale = 0.5f;
268    _texmatLeft->setMatrix(_texmatLeft->getMatrix()*osg::Matrix::translate(-rx*scale,-ry*scale,0.0f));
269    _texmatRight->setMatrix(_texmatRight->getMatrix()*osg::Matrix::translate(-rx*scale,-ry*scale,0.0f));
270}
271
272void SlideEventHandler::initTexMatrices()
273{
274    _texmatLeft->setMatrix(osg::Matrix::translate(_initSeperationX,_initSeperationY,0.0f));
275    _texmatRight->setMatrix(osg::Matrix::translate(-_initSeperationX,-_initSeperationY,0.0f));
276}
277
278
279osg::Geode* createSectorForImage(osg::Image* image, osg::TexMat* texmat, float s,float t, float radius, float height, float length)
280{
281
282    int numSegments = 20;
283    float Theta = length/radius;
284    float dTheta = Theta/(float)(numSegments-1);
285   
286    float ThetaZero = height*s/(t*radius);
287   
288    // set up the texture.
289    osg::Texture2D* texture = new osg::Texture2D;
290    texture->setFilter(osg::Texture2D::MIN_FILTER,osg::Texture2D::LINEAR);
291    texture->setFilter(osg::Texture2D::MAG_FILTER,osg::Texture2D::LINEAR);
292    texture->setImage(image);
293
294    // set up the drawstate.
295    osg::StateSet* dstate = new osg::StateSet;
296    dstate->setMode(GL_CULL_FACE,osg::StateAttribute::OFF);
297    dstate->setMode(GL_LIGHTING,osg::StateAttribute::OFF);
298    dstate->setTextureAttributeAndModes(0, texture,osg::StateAttribute::ON);
299    dstate->setTextureAttribute(0, texmat);
300
301    // set up the geoset.
302    osg::Geometry* geom = new osg::Geometry;
303    geom->setStateSet(dstate);
304
305    osg::Vec3Array* coords = new osg::Vec3Array();
306    osg::Vec2Array* tcoords = new osg::Vec2Array();
307   
308    int i;
309    float angle = -Theta/2.0f;
310    for(i=0;
311        i<numSegments;
312        ++i, angle+=dTheta)
313    {
314        coords->push_back(osg::Vec3(sinf(angle)*radius,cosf(angle)*radius,height*0.5f)); // top
315        coords->push_back(osg::Vec3(sinf(angle)*radius,cosf(angle)*radius,-height*0.5f)); // bottom.
316       
317        tcoords->push_back(osg::Vec2(angle/ThetaZero+0.5f,1.0f)); // top
318        tcoords->push_back(osg::Vec2(angle/ThetaZero+0.5f,0.0f)); // bottom.
319
320    }
321
322    osg::Vec4Array* colors = new osg::Vec4Array();
323    colors->push_back(osg::Vec4(1.0f,1.0f,1.0f,1.0f));
324
325    osg::DrawArrays* elements = new osg::DrawArrays(osg::PrimitiveSet::QUAD_STRIP,0,coords->size());
326
327   
328
329    geom->setVertexArray(coords);
330    geom->setTexCoordArray(0,tcoords);
331    geom->setColorArray(colors);
332    geom->setColorBinding(osg::Geometry::BIND_OVERALL);
333
334    geom->addPrimitiveSet(elements);
335
336    // set up the geode.
337    osg::Geode* geode = new osg::Geode;
338    geode->addDrawable(geom);
339
340    return geode;
341
342}
343
344// create a switch containing a set of child each containing a
345// stereo image pair.
346osg::Switch* createScene(const FileList& fileList, osg::TexMat* texmatLeft, osg::TexMat* texmatRight, float radius, float height, float length)
347{
348    osg::Switch* sw = new osg::Switch;
349
350    // load the images.
351    for(unsigned int i=0;i+1<fileList.size();i+=2)
352    {
353        osg::ref_ptr<osg::Image> imageLeft = osgDB::readImageFile(fileList[i]);
354        osg::ref_ptr<osg::Image> imageRight = osgDB::readImageFile(fileList[i+1]);
355        if (imageLeft.valid() && imageRight.valid())
356        {
357            float average_s = (imageLeft->s()+imageRight->s())*0.5f;
358            float average_t = (imageLeft->t()+imageRight->t())*0.5f;
359
360            osg::Geode* geodeLeft = createSectorForImage(imageLeft.get(),texmatLeft,average_s,average_t, radius, height, length);
361            geodeLeft->setNodeMask(0x01);
362
363            osg::Geode* geodeRight = createSectorForImage(imageRight.get(),texmatRight,average_s,average_t, radius, height, length);
364            geodeRight->setNodeMask(0x02);
365
366            osg::ref_ptr<osg::Group> imageGroup = new osg::Group;
367           
368            imageGroup->addChild(geodeLeft);
369            imageGroup->addChild(geodeRight);
370           
371            sw->addChild(imageGroup.get());
372        }
373        else
374        {
375            std::cout << "Warning: Unable to load both image files, '"<<fileList[i]<<"' & '"<<fileList[i+1]<<"', required for stereo imaging."<<std::endl;
376        }
377    }
378
379
380    if (sw->getNumChildren()>0)
381    {
382        // select first child.
383        sw->setSingleChildOn(0);
384    }
385
386    return sw;
387}
388
389int main( int argc, char **argv )
390{
391
392    // use an ArgumentParser object to manage the program arguments.
393    osg::ArgumentParser arguments(&argc,argv);
394   
395    // set up the usage document, in case we need to print out how to use this program.
396    arguments.getApplicationUsage()->setDescription(arguments.getApplicationName()+" is the example which demonstrates use node masks to create stereo images.");
397    arguments.getApplicationUsage()->setCommandLineUsage(arguments.getApplicationName()+" [options] image_file_left_eye image_file_right_eye");
398    arguments.getApplicationUsage()->addCommandLineOption("-d <float>","Time delay in sceonds between the display of successive image pairs when in auto advance mode.");
399    arguments.getApplicationUsage()->addCommandLineOption("-a","Enter auto advance of image pairs on start up.");
400    arguments.getApplicationUsage()->addCommandLineOption("-x <float>","Horizontal offset of left and right images.");
401    arguments.getApplicationUsage()->addCommandLineOption("-y <float>","Vertical offset of left and right images.");
402    arguments.getApplicationUsage()->addCommandLineOption("-h or --help","Display this information");
403   
404
405    // construct the viewer.
406    osgProducer::Viewer viewer(arguments);
407
408    // set up the value with sensible default event handlers.
409    viewer.setUpViewer(osgProducer::Viewer::ESCAPE_SETS_DONE);
410
411    // register the handler to add keyboard and mosue handling.
412    SlideEventHandler* seh = new SlideEventHandler();
413    viewer.getEventHandlerList().push_front(seh);
414
415
416    // get details on keyboard and mouse bindings used by the viewer.
417    viewer.getUsage(*arguments.getApplicationUsage());
418
419    // read any time delay argument.
420    float timeDelayBetweenSlides = 5.0f;
421    while (arguments.read("-d",timeDelayBetweenSlides)) {}
422
423    bool autoSteppingActive = false;
424    while (arguments.read("-a")) autoSteppingActive = true;
425
426    float offsetX=0.0f;
427    while (arguments.read("-x",offsetX)) {}
428
429    float offsetY=0.0f;
430    while (arguments.read("-y",offsetY)) {}
431
432    // if user request help write it out to cout.
433    if (arguments.read("-h") || arguments.read("--help"))
434    {
435        arguments.getApplicationUsage()->write(std::cout);
436        return 1;
437    }
438
439    // any option left unread are converted into errors to write out later.
440    arguments.reportRemainingOptionsAsUnrecognized();
441
442    // report any errors if they have occured when parsing the program aguments.
443    if (arguments.errors())
444    {
445        arguments.writeErrorMessages(std::cout);
446        return 1;
447    }
448   
449    if (arguments.argc()<=1)
450    {
451        arguments.getApplicationUsage()->write(std::cout,osg::ApplicationUsage::COMMAND_LINE_OPTION);
452        return 1;
453    }
454
455    // extract the filenames from the arguments list.
456    FileList fileList;
457    for(int pos=1;pos<arguments.argc();++pos)
458    {
459        if (arguments.isString(pos)) fileList.push_back(arguments[pos]);
460    }
461
462    if (fileList.size()<2)
463    {
464        return 1;
465    }
466
467    // set up the use of stereo by default.
468    osg::DisplaySettings* ds = viewer.getDisplaySettings();
469    if (!ds) ds = osg::DisplaySettings::instance();
470    if (ds) ds->setStereo(true);
471
472    // create the windows and run the threads.
473    viewer.realize();
474
475    // now the windows have been realized we switch off the cursor to prevent it
476    // distracting the people seeing the stereo images.
477    float fovy = 1.0f;
478    for( unsigned int i = 0; i < viewer.getCameraConfig()->getNumberOfCameras(); i++ )
479    {
480        Producer::Camera* cam = viewer.getCameraConfig()->getCamera(i);
481        Producer::RenderSurface* rs = cam->getRenderSurface();
482        rs->useCursor(false);
483        fovy = osg::DegreesToRadians(cam->getLensVerticalFov());
484    }
485
486    float radius = 1.0f;
487    float height = 2*radius*tan(fovy*0.5f);
488    float length = osg::PI*radius;  // half a cylinder.
489
490    // use a texure matrix to control the placement of the image.
491    osg::TexMat* texmatLeft = new osg::TexMat;
492    osg::TexMat* texmatRight = new osg::TexMat;
493
494    // creat the scene from the file list.
495    osg::ref_ptr<osg::Switch> rootNode = createScene(fileList,texmatLeft,texmatRight,radius,height,length);
496
497
498    //osgDB::writeNodeFile(*rootNode,"test.osg");
499
500    // set the scene to render
501    viewer.setSceneData(rootNode.get());
502
503
504    // set all the sceneview's up so that their left and right add cull masks are set up.
505    for(osgProducer::OsgCameraGroup::SceneHandlerList::iterator itr=viewer.getSceneHandlerList().begin();
506        itr!=viewer.getSceneHandlerList().end();
507        ++itr)
508    {
509        osgUtil::SceneView* sceneview = (*itr)->getSceneView();
510        sceneview->setCullMask(0xffffffff);
511        sceneview->setCullMaskLeft(0x00000001);
512        sceneview->setCullMaskRight(0x00000002);
513        sceneview->setFusionDistance(osgUtil::SceneView::USE_FUSION_DISTANCE_VALUE,radius);
514    }
515
516
517    // set up the SlideEventHandler.
518    seh->set(rootNode.get(),offsetX,offsetY,texmatLeft,texmatRight,timeDelayBetweenSlides,autoSteppingActive);
519   
520   
521    osg::Matrix homePosition;
522    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));
523       
524    while( !viewer.done() )
525    {
526        // wait for all cull and draw threads to complete.
527        viewer.sync();
528
529        // update the scene by traversing it with the the update visitor which will
530        // call all node update callbacks and animations.
531        viewer.update();
532         
533        viewer.setView(homePosition);
534
535        // fire off the cull and draw traversals of the scene.
536        viewer.frame();
537       
538    }
539   
540    // wait for all cull and draw threads to complete before exit.
541    viewer.sync();
542   
543    return 0;
544}
Note: See TracBrowser for help on using the browser.