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

Revision 6941, 18.1 kB (checked in by robert, 8 years ago)

From Martin Lavery and Robert Osfield, Updated examples to use a variation of the MIT License

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