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/fstream>
21 #include <osgDB/ReadFile>
22 #include <osgDB/WriteFile>
23 #include <osgUtil/Optimizer>
24 
25 #include <osg/ImageStream>
26 #include <osg/Geode>
27 #include <osg/Notify>
28 #include <osg/MatrixTransform>
29 #include <osg/Switch>
30 #include <osg/TexMat>
31 #include <osg/Texture2D>
32 
33 #include <iostream>
34 
35 typedef std::vector<std::string> FileList;
36 
37 #include <osg/Program>
38 #include <osg/Shader>
39 
createColorToGreyscaleStateSet()40 osg::StateSet* createColorToGreyscaleStateSet()
41 {
42     osg::StateSet* stateset = new osg::StateSet;
43 
44     osg::Program* program = new osg::Program;
45     stateset->setAttribute(program);
46 
47     const char* fragSource =
48     {
49         "uniform sampler2D baseTexture;\n"
50         "uniform mat4 colorMatrix;\n"
51         "void main(void)\n"
52         "{\n"
53         "    vec4 color = texture2D( baseTexture, gl_TexCoord[0].st );\n"
54         "    gl_FragColor = colorMatrix * color;\n"
55         "}\n"
56     };
57     program->addShader(new osg::Shader(osg::Shader::FRAGMENT, fragSource));
58 
59     stateset->addUniform(new osg::Uniform("baseTexture",0));
60 
61     osg::Matrixf colorMatrix(
62         0.3f, 0.3f, 0.3f, 0.0f,
63         0.59f, 0.59f, 0.59f, 0.0f,
64         0.11f, 0.11f, 0.11f, 0.0f,
65         0.0f, 0.0f, 0.0f, 1.0f
66     );
67 
68     stateset->addUniform(new osg::Uniform("colorMatrix",colorMatrix));
69 
70     return stateset;
71 }
72 
73 
createSectorForImage(osg::Image * image,osg::TexMat * texmat,float s,float t,float radius,float height,float length)74 osg::Geode* createSectorForImage(osg::Image* image, osg::TexMat* texmat, float s,float t, float radius, float height, float length)
75 {
76     bool flip = image->getOrigin()==osg::Image::TOP_LEFT;
77 
78     int numSegments = 20;
79     float Theta = length/radius;
80     float dTheta = Theta/(float)(numSegments-1);
81 
82     float ThetaZero = height*s/(t*radius);
83 
84     // set up the texture.
85     osg::Texture2D* texture = new osg::Texture2D;
86     texture->setFilter(osg::Texture2D::MIN_FILTER,osg::Texture2D::LINEAR);
87     texture->setFilter(osg::Texture2D::MAG_FILTER,osg::Texture2D::LINEAR);
88     texture->setWrap(osg::Texture2D::WRAP_S,osg::Texture2D::CLAMP_TO_BORDER);
89     texture->setWrap(osg::Texture2D::WRAP_T,osg::Texture2D::CLAMP_TO_BORDER);
90     texture->setResizeNonPowerOfTwoHint(false);
91     texture->setImage(image);
92 
93     // set up the drawstate.
94     osg::StateSet* dstate = new osg::StateSet;
95     dstate->setMode(GL_CULL_FACE,osg::StateAttribute::OFF);
96     dstate->setMode(GL_LIGHTING,osg::StateAttribute::OFF);
97     dstate->setTextureAttributeAndModes(0, texture,osg::StateAttribute::ON);
98     dstate->setTextureAttribute(0, texmat);
99 
100     // set up the geoset.
101     osg::Geometry* geom = new osg::Geometry;
102     geom->setStateSet(dstate);
103 
104     osg::Vec3Array* coords = new osg::Vec3Array();
105     osg::Vec2Array* tcoords = new osg::Vec2Array();
106 
107     int i;
108     float angle = -Theta/2.0f;
109     for(i=0;
110         i<numSegments;
111         ++i, angle+=dTheta)
112     {
113         coords->push_back(osg::Vec3(sinf(angle)*radius,cosf(angle)*radius,height*0.5f)); // top
114         coords->push_back(osg::Vec3(sinf(angle)*radius,cosf(angle)*radius,-height*0.5f)); // bottom.
115 
116         tcoords->push_back(osg::Vec2(angle/ThetaZero+0.5f, flip ? 0.0f : 1.0f)); // top
117         tcoords->push_back(osg::Vec2(angle/ThetaZero+0.5f, flip ? 1.0f : 0.0f)); // bottom.
118 
119     }
120 
121     osg::Vec4Array* colors = new osg::Vec4Array();
122     colors->push_back(osg::Vec4(1.0f,1.0f,1.0f,1.0f));
123 
124     osg::DrawArrays* elements = new osg::DrawArrays(osg::PrimitiveSet::QUAD_STRIP,0,coords->size());
125 
126 
127 
128     geom->setVertexArray(coords);
129     geom->setTexCoordArray(0,tcoords);
130     geom->setColorArray(colors, osg::Array::BIND_OVERALL);
131 
132     geom->addPrimitiveSet(elements);
133 
134     // set up the geode.
135     osg::Geode* geode = new osg::Geode;
136     geode->addDrawable(geom);
137 
138     return geode;
139 
140 }
141 
loadImages(std::string image1,std::string image2,osg::TexMat * texmatLeft,osg::TexMat * texmatRight,float radius,float height,float length)142 osg::Group * loadImages(std::string image1, std::string image2, osg::TexMat* texmatLeft, osg::TexMat* texmatRight, float radius, float height, float length)
143 {
144     osg::ref_ptr<osg::Image> imageLeft = osgDB::readRefImageFile(image1);
145     osg::ref_ptr<osg::Image> imageRight = osgDB::readRefImageFile(image2);
146     if (imageLeft.valid() && imageRight.valid())
147     {
148 	osg::ImageStream* streamLeft = dynamic_cast<osg::ImageStream*>(imageLeft.get());
149 	if (streamLeft) streamLeft->play();
150 
151 	osg::ImageStream* streamRight = dynamic_cast<osg::ImageStream*>(imageRight.get());
152 	if (streamRight) streamRight->play();
153 
154 
155 	float average_s = (imageLeft->s()+imageRight->s())*0.5f;
156 	float average_t = (imageLeft->t()+imageRight->t())*0.5f;
157 	osg::Geode* geodeLeft = createSectorForImage(imageLeft.get(),texmatLeft,average_s,average_t, radius, height, length);
158 	geodeLeft->setNodeMask(0x01);
159 
160 	osg::Geode* geodeRight = createSectorForImage(imageRight.get(),texmatRight,average_s,average_t, radius, height, length);
161 	geodeRight->setNodeMask(0x02);
162 
163 	osg::Group * imageGroup = new osg::Group;
164 
165 	imageGroup->addChild(geodeLeft);
166 	imageGroup->addChild(geodeRight);
167 	return imageGroup;
168     }
169     else
170     {
171 	std::cout << "Warning: Unable to load both image files, '"<<image1<<"' & '"<<image2<<"', required for stereo imaging."<<std::endl;
172 	return 0;
173     }
174 }
175 
176 // create a switch containing a set of child each containing a
177 // stereo image pair.
createScene(FileList fileList,osg::TexMat * texmatLeft,osg::TexMat * texmatRight,float radius,float height,float length)178 osg::Switch* createScene(FileList fileList, osg::TexMat* texmatLeft, osg::TexMat* texmatRight, float radius, float height, float length)
179 {
180     osg::Switch* sw = new osg::Switch;
181 
182     // load the images.
183     for(unsigned int i=0;i+1<fileList.size();i+=2)
184     {
185         osg::Group * imageGroup = loadImages(fileList[i],fileList[i+1],texmatLeft,texmatRight, radius,  height, length);
186         if (imageGroup) sw->addChild(imageGroup);
187     }
188 
189 
190     if (sw->getNumChildren()>0)
191     {
192         // select first child.
193         sw->setSingleChildOn(0);
194     }
195 
196     return sw;
197 }
198 
199 class SlideEventHandler : public osgGA::GUIEventHandler
200 {
201 public:
202 
203     SlideEventHandler();
204 
205     META_Object(osgStereImageApp,SlideEventHandler);
206 
207 
208     void set(osg::Switch* sw, float offsetX, float offsetY, osg::TexMat* texmatLeft, osg::TexMat* texmatRight, float timePerSlide, bool autoSteppingActive);
209 
210     void set(FileList fileList, osg::Switch* sw, float offsetX, float offsetY, osg::TexMat* texmatLeft, osg::TexMat* texmatRight, float radius, float height, float length, float timePerSlide, bool autoSteppingActive);
211 
212 
213     virtual bool handle(const osgGA::GUIEventAdapter& ea,osgGA::GUIActionAdapter&);
214 
215     virtual void getUsage(osg::ApplicationUsage& usage) const;
216 
217     virtual void operator()(osg::Node* node, osg::NodeVisitor* nv);
218 
219     void nextSlide();
220 
221     void previousSlide();
222 
223     void scaleImage(float s);
224 
225     void offsetImage(float ds,float dt);
226 
227     void rotateImage(float rx,float ry);
228 
229     void initTexMatrices();
230 
231 protected:
232 
~SlideEventHandler()233     ~SlideEventHandler() {}
SlideEventHandler(const SlideEventHandler &,const osg::CopyOp &)234     SlideEventHandler(const SlideEventHandler&,const osg::CopyOp&) {}
235 
236     osg::ref_ptr<osg::Switch>   _switch;
237     osg::ref_ptr<osg::TexMat>   _texmatLeft;
238     osg::ref_ptr<osg::TexMat>   _texmatRight;
239     float                        _radius;
240     float                        _height;
241     float                        _length;
242     bool                        _firstTraversal;
243     unsigned int                _activeSlide;
244     double                      _previousTime;
245     double                      _timePerSlide;
246     bool                        _autoSteppingActive;
247     float                       _initSeperationX;
248     float                       _currentSeperationX;
249     float                       _initSeperationY;
250     float                       _currentSeperationY;
251     FileList                     _fileList;
252 
253 };
254 
SlideEventHandler()255 SlideEventHandler::SlideEventHandler():
256     _switch(0),
257     _texmatLeft(0),
258     _texmatRight(0),
259     _firstTraversal(true),
260     _activeSlide(0),
261     _previousTime(-1.0f),
262     _timePerSlide(5.0),
263     _autoSteppingActive(false)
264 {
265 }
266 
set(osg::Switch * sw,float offsetX,float offsetY,osg::TexMat * texmatLeft,osg::TexMat * texmatRight,float timePerSlide,bool autoSteppingActive)267 void SlideEventHandler::set(osg::Switch* sw, float offsetX, float offsetY, osg::TexMat* texmatLeft, osg::TexMat* texmatRight, float timePerSlide, bool autoSteppingActive)
268 {
269     _switch = sw;
270     _switch->setUpdateCallback(this);
271 
272     _texmatLeft = texmatLeft;
273     _texmatRight = texmatRight;
274 
275     _timePerSlide = timePerSlide;
276     _autoSteppingActive = autoSteppingActive;
277 
278     _initSeperationX = offsetX;
279     _currentSeperationX = _initSeperationX;
280 
281     _initSeperationY = offsetY;
282     _currentSeperationY = _initSeperationY;
283 
284     initTexMatrices();
285 
286 }
287 
set(FileList fileList,osg::Switch * sw,float offsetX,float offsetY,osg::TexMat * texmatLeft,osg::TexMat * texmatRight,float radius,float height,float length,float timePerSlide,bool autoSteppingActive)288 void SlideEventHandler::set(FileList fileList, osg::Switch* sw, float offsetX, float offsetY, osg::TexMat* texmatLeft, osg::TexMat* texmatRight, float radius, float height, float length, float timePerSlide, bool autoSteppingActive)
289 {
290     _switch = sw;
291     _switch->setUpdateCallback(this);
292     _fileList=FileList(fileList);
293 
294     osg::ref_ptr<osg::Group> imageGroup = loadImages(fileList[0],fileList[1],texmatLeft,texmatRight, radius,  height, length);
295     if (imageGroup.get())_switch->addChild(imageGroup.get());
296 
297     _texmatLeft = texmatLeft;
298     _texmatRight = texmatRight;
299 
300     _radius=radius;
301     _height=height;
302     _length=length;
303 
304     _timePerSlide = timePerSlide;
305     _autoSteppingActive = autoSteppingActive;
306 
307     _initSeperationX = offsetX;
308     _currentSeperationX = _initSeperationX;
309 
310     _initSeperationY = offsetY;
311     _currentSeperationY = _initSeperationY;
312 
313     initTexMatrices();
314 }
315 
316 
handle(const osgGA::GUIEventAdapter & ea,osgGA::GUIActionAdapter &)317 bool SlideEventHandler::handle(const osgGA::GUIEventAdapter& ea,osgGA::GUIActionAdapter&)
318 {
319     switch(ea.getEventType())
320     {
321         case(osgGA::GUIEventAdapter::KEYDOWN):
322         {
323             if (ea.getKey()=='a')
324             {
325                 _autoSteppingActive = !_autoSteppingActive;
326                 _previousTime = ea.getTime();
327                 return true;
328             }
329             else if ((ea.getKey()=='n') || (ea.getKey()==osgGA::GUIEventAdapter::KEY_Right))
330             {
331                 nextSlide();
332                 return true;
333             }
334             else if ((ea.getKey()=='p') || (ea.getKey()==osgGA::GUIEventAdapter::KEY_Left))
335             {
336                 previousSlide();
337                 return true;
338             }
339             else if ((ea.getKey()=='w') || (ea.getKey()==osgGA::GUIEventAdapter::KEY_KP_Add))
340             {
341                 scaleImage(0.99f);
342                 return true;
343             }
344             else if ((ea.getKey()=='s') || (ea.getKey()==osgGA::GUIEventAdapter::KEY_KP_Subtract))
345             {
346                 scaleImage(1.01f);
347                 return true;
348             }
349             else if (ea.getKey()=='j')
350             {
351                 offsetImage(-0.001f,0.0f);
352                 return true;
353             }
354             else if (ea.getKey()=='k')
355             {
356                 offsetImage(0.001f,0.0f);
357                 return true;
358             }
359             else if (ea.getKey()=='i')
360             {
361                 offsetImage(0.0f,-0.001f);
362                 return true;
363             }
364             else if (ea.getKey()=='m')
365             {
366                 offsetImage(0.0f,0.001f);
367                 return true;
368             }
369             else if (ea.getKey()==' ')
370             {
371                 initTexMatrices();
372                 return true;
373             }
374             return false;
375         }
376         case(osgGA::GUIEventAdapter::DRAG):
377         case(osgGA::GUIEventAdapter::MOVE):
378         {
379             static float px = ea.getXnormalized();
380             static float py = ea.getYnormalized();
381 
382             float dx = ea.getXnormalized()-px;
383             float dy = ea.getYnormalized()-py;
384 
385             px = ea.getXnormalized();
386             py = ea.getYnormalized();
387 
388             rotateImage(dx,dy);
389 
390             return true;
391         }
392 
393         default:
394             return false;
395     }
396 }
397 
getUsage(osg::ApplicationUsage & usage) const398 void SlideEventHandler::getUsage(osg::ApplicationUsage& usage) const
399 {
400     usage.addKeyboardMouseBinding("Space","Reset the image position to center");
401     usage.addKeyboardMouseBinding("a","Toggle on/off the automatic advancement for image to image");
402     usage.addKeyboardMouseBinding("n","Advance to next image");
403     usage.addKeyboardMouseBinding("p","Move to previous image");
404     usage.addKeyboardMouseBinding("q","Zoom into the image");
405     usage.addKeyboardMouseBinding("a","Zoom out of the image");
406     usage.addKeyboardMouseBinding("j","Reduce horizontal offset");
407     usage.addKeyboardMouseBinding("k","Increase horizontal offset");
408     usage.addKeyboardMouseBinding("m","Reduce vertical offset");
409     usage.addKeyboardMouseBinding("i","Increase vertical offset");
410 }
411 
operator ()(osg::Node * node,osg::NodeVisitor * nv)412 void SlideEventHandler::operator()(osg::Node* node, osg::NodeVisitor* nv)
413 {
414     if (_autoSteppingActive && nv->getFrameStamp())
415     {
416         double time = nv->getFrameStamp()->getSimulationTime();
417 
418         if (_firstTraversal)
419         {
420             _firstTraversal = false;
421             _previousTime = time;
422         }
423         else if (time-_previousTime>_timePerSlide)
424         {
425             _previousTime = time;
426 
427             nextSlide();
428         }
429 
430     }
431 
432     traverse(node,nv);
433 }
434 
nextSlide()435 void SlideEventHandler::nextSlide()
436 {
437 
438     if (_switch->getNumChildren()==0) return;
439 
440     ++_activeSlide;
441 
442     if (_fileList.size()>0) {
443         if (_activeSlide>= _fileList.size()/2 ) _activeSlide = 0;
444         osg::ref_ptr<osg::Group> images = loadImages(_fileList[2*_activeSlide],_fileList[2*_activeSlide+1],_texmatLeft.get(),_texmatRight.get(),_radius,_height,_length);
445         if (images.valid()) _switch->replaceChild(_switch->getChild(0),images.get());
446 
447     } else {
448         if (_activeSlide>=_switch->getNumChildren()) _activeSlide = 0;
449 
450         _switch->setSingleChildOn(_activeSlide);
451     }
452 }
453 
previousSlide()454 void SlideEventHandler::previousSlide()
455 {
456     if (_switch->getNumChildren()==0) return;
457 
458     if (_fileList.size()>0) {
459         if (_activeSlide==0) _activeSlide = _fileList.size()/2-1;
460         else --_activeSlide;
461         osg::ref_ptr<osg::Group> images = loadImages(_fileList[2*_activeSlide],_fileList[2*_activeSlide+1],_texmatLeft.get(),_texmatRight.get(),_radius,_height,_length);
462         if (images.valid()) _switch->replaceChild(_switch->getChild(0),images.get());
463     } else {
464         if (_activeSlide==0) _activeSlide = _switch->getNumChildren()-1;
465         else --_activeSlide;
466 
467         _switch->setSingleChildOn(_activeSlide);
468     }
469 }
470 
scaleImage(float s)471 void SlideEventHandler::scaleImage(float s)
472 {
473     _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));
474     _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));
475 }
476 
offsetImage(float ds,float dt)477 void SlideEventHandler::offsetImage(float ds,float dt)
478 {
479     _currentSeperationX+=ds;
480     _currentSeperationY+=dt;
481     osg::notify(osg::NOTICE)<<"image offset x = "<<_currentSeperationX<<"  y ="<<_currentSeperationY<<std::endl;
482     _texmatLeft->setMatrix(_texmatLeft->getMatrix()*osg::Matrix::translate(ds,dt,0.0f));
483     _texmatRight->setMatrix(_texmatRight->getMatrix()*osg::Matrix::translate(-ds,-dt,0.0f));
484 }
485 
rotateImage(float rx,float ry)486 void SlideEventHandler::rotateImage(float rx,float ry)
487 {
488     const float scale = 0.5f;
489     _texmatLeft->setMatrix(_texmatLeft->getMatrix()*osg::Matrix::translate(-rx*scale,-ry*scale,0.0f));
490     _texmatRight->setMatrix(_texmatRight->getMatrix()*osg::Matrix::translate(-rx*scale,-ry*scale,0.0f));
491 }
492 
initTexMatrices()493 void SlideEventHandler::initTexMatrices()
494 {
495     _texmatLeft->setMatrix(osg::Matrix::translate(_initSeperationX,_initSeperationY,0.0f));
496     _texmatRight->setMatrix(osg::Matrix::translate(-_initSeperationX,-_initSeperationY,0.0f));
497 }
498 
499 
500 
main(int argc,char ** argv)501 int main( int argc, char **argv )
502 {
503     // use an ArgumentParser object to manage the program arguments.
504     osg::ArgumentParser arguments(&argc,argv);
505 
506     // set up the usage document, in case we need to print out how to use this program.
507     arguments.getApplicationUsage()->setDescription(arguments.getApplicationName()+" is the example which demonstrates use node masks to create stereo images.");
508     arguments.getApplicationUsage()->setCommandLineUsage(arguments.getApplicationName()+" [options] image_file_left_eye image_file_right_eye");
509     arguments.getApplicationUsage()->addCommandLineOption("-d <float>","Time delay in seconds between the display of successive image pairs when in auto advance mode.");
510     arguments.getApplicationUsage()->addCommandLineOption("-a","Enter auto advance of image pairs on start up.");
511     arguments.getApplicationUsage()->addCommandLineOption("-x <float>","Horizontal offset of left and right images.");
512     arguments.getApplicationUsage()->addCommandLineOption("-y <float>","Vertical offset of left and right images.");
513     arguments.getApplicationUsage()->addCommandLineOption("--disk","Keep images on disk");
514     arguments.getApplicationUsage()->addCommandLineOption("-files <filename>","Load filenames from a file");
515     arguments.getApplicationUsage()->addCommandLineOption("-h or --help","Display this information");
516     arguments.getApplicationUsage()->addCommandLineOption("--SingleThreaded","Select SingleThreaded threading model for viewer.");
517     arguments.getApplicationUsage()->addCommandLineOption("--CullDrawThreadPerContext","Select CullDrawThreadPerContext threading model for viewer.");
518     arguments.getApplicationUsage()->addCommandLineOption("--DrawThreadPerContext","Select DrawThreadPerContext threading model for viewer.");
519     arguments.getApplicationUsage()->addCommandLineOption("--CullThreadPerCameraDrawThreadPerContext","Select CullThreadPerCameraDrawThreadPerContext threading model for viewer.");
520 
521 
522     // construct the viewer.
523     osgViewer::Viewer viewer(arguments);
524 
525     // register the handler to add keyboard and mouse handling.
526     SlideEventHandler* seh = new SlideEventHandler();
527     viewer.addEventHandler(seh);
528 
529     // read any time delay argument.
530     float timeDelayBetweenSlides = 5.0f;
531     while (arguments.read("-d",timeDelayBetweenSlides)) {}
532 
533     bool autoSteppingActive = false;
534     while (arguments.read("-a")) autoSteppingActive = true;
535 
536     float offsetX=0.0f;
537     while (arguments.read("-x",offsetX)) {}
538 
539     float offsetY=0.0f;
540     while (arguments.read("-y",offsetY)) {}
541 
542     bool onDisk=false;
543     while (arguments.read("--disk")) { onDisk=true;}
544 
545     std::string filename="";
546     FileList fileList;
547     // extract the filenames from the a file, one filename per line.
548     while (arguments.read("-files",filename)) {
549         osgDB::ifstream is(filename.c_str());
550         if (is) {
551                 std::string line;
552                 while (std::getline(is,line,'\n')) fileList.push_back(line);
553                 is.close();
554             }
555 
556     }
557 
558     // if user request help write it out to cout.
559     if (arguments.read("-h") || arguments.read("--help"))
560     {
561         arguments.getApplicationUsage()->write(std::cout);
562         return 1;
563     }
564 
565     osgViewer::Viewer::ThreadingModel threading = osgViewer::Viewer::SingleThreaded;
566     while (arguments.read("--SingleThreaded")) threading = osgViewer::Viewer::SingleThreaded;
567     while (arguments.read("--CullDrawThreadPerContext")) threading = osgViewer::Viewer::CullDrawThreadPerContext;
568     while (arguments.read("--DrawThreadPerContext")) threading = osgViewer::Viewer::DrawThreadPerContext;
569     while (arguments.read("--CullThreadPerCameraDrawThreadPerContext")) threading = osgViewer::Viewer::CullThreadPerCameraDrawThreadPerContext;
570 
571     viewer.setThreadingModel(threading);
572 
573     // any option left unread are converted into errors to write out later.
574     arguments.reportRemainingOptionsAsUnrecognized();
575 
576     // report any errors if they have occurred when parsing the program arguments.
577     if (arguments.errors())
578     {
579         arguments.writeErrorMessages(std::cout);
580         return 1;
581     }
582 
583     // extract the filenames from the arguments list.
584     for(int pos=1;pos<arguments.argc();++pos)
585     {
586         if (arguments.isString(pos)) fileList.push_back(arguments[pos]);
587     }
588 
589     if (fileList.empty())
590     {
591         fileList.push_back("Images/dog_left_eye.jpg");
592          fileList.push_back("Images/dog_right_eye.jpg");
593     }
594     else if (fileList.size()<2)
595     {
596         arguments.getApplicationUsage()->write(std::cout,osg::ApplicationUsage::COMMAND_LINE_OPTION);
597         return 1;
598     }
599 
600     // now the windows have been realized we switch off the cursor to prevent it
601     // distracting the people seeing the stereo images.
602     double fovy, aspectRatio, zNear, zFar;
603     viewer.getCamera()->getProjectionMatrixAsPerspective(fovy, aspectRatio, zNear, zFar);
604 
605     float radius = 1.0f;
606     float height = 2*radius*tan(osg::DegreesToRadians(fovy)*0.5f);
607     float length = osg::PI*radius;  // half a cylinder.
608 
609     // use a texture matrix to control the placement of the image.
610     osg::TexMat* texmatLeft = new osg::TexMat;
611     osg::TexMat* texmatRight = new osg::TexMat;
612 
613     // creat the scene from the file list.
614     osg::ref_ptr<osg::Switch> rootNode;
615     if (!onDisk)  rootNode = createScene(fileList,texmatLeft,texmatRight,radius,height,length);
616     else rootNode=new osg::Switch();
617 
618     //osgDB::writeNodeFile(*rootNode,"test.osgt");
619 
620 
621 
622     viewer.getCamera()->setCullMask(0xffffffff);
623     viewer.getCamera()->setCullMaskLeft(0x00000001);
624     viewer.getCamera()->setCullMaskRight(0x00000002);
625 
626     // set up the use of stereo by default.
627     osg::DisplaySettings::instance()->setStereo(true);
628 
629     if (osg::DisplaySettings::instance()->getStereoMode()==osg::DisplaySettings::ANAGLYPHIC)
630     {
631         rootNode->setStateSet(createColorToGreyscaleStateSet());
632     }
633 
634 
635     // set the scene to render
636     viewer.setSceneData(rootNode.get());
637 
638 
639     // create the windows and run the threads.
640     viewer.realize();
641 
642 
643     // switch off the cursor
644     osgViewer::Viewer::Windows windows;
645     viewer.getWindows(windows);
646     for(osgViewer::Viewer::Windows::iterator itr = windows.begin();
647         itr != windows.end();
648         ++itr)
649     {
650         (*itr)->useCursor(false);
651     }
652 
653     viewer.setFusionDistance(osgUtil::SceneView::USE_FUSION_DISTANCE_VALUE,radius);
654 
655     // set up the SlideEventHandler.
656     if (onDisk) seh->set(fileList,rootNode.get(),offsetX,offsetY,texmatLeft,texmatRight,radius,height,length,timeDelayBetweenSlides,autoSteppingActive);
657     else seh->set(rootNode.get(),offsetX,offsetY,texmatLeft,texmatRight,timeDelayBetweenSlides,autoSteppingActive);
658 
659     osg::Matrix homePosition;
660     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));
661 
662     while( !viewer.done() )
663     {
664         viewer.getCamera()->setViewMatrix(homePosition);
665 
666         // fire off the cull and draw traversals of the scene.
667         viewer.frame();
668 
669     }
670 
671     return 0;
672 }
673 
674