1 /* -*-c++-*- Present3D - Copyright (C) 1999-2006 Robert Osfield
2  *
3  * This software is open source and may be redistributed and/or modified under
4  * the terms of the GNU General Public License (GPL) version 2.0.
5  * The full license is in LICENSE.txt file included with this distribution,.
6  *
7  * This software is distributed in the hope that it will be useful,
8  * but WITHOUT ANY WARRANTY; without even the implied warranty of
9  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
10  * include LICENSE.txt for more details.
11 */
12 
13 #include <osg/Geometry>
14 #include <osg/Texture2D>
15 #include <osg/AutoTransform>
16 #include <osg/Notify>
17 #include <osg/io_utils>
18 
19 
20 #include <osgDB/ReadFile>
21 #include <osgDB/WriteFile>
22 #include <osgDB/FileNameUtils>
23 #include <osgUtil/Optimizer>
24 
25 #include <osgViewer/Viewer>
26 #include <osgViewer/ViewerEventHandlers>
27 
28 #include <OpenThreads/Thread>
29 
30 #include <osgGA/GUIEventHandler>
31 #include <osgGA/TrackballManipulator>
32 #include <osgGA/FlightManipulator>
33 #include <osgGA/DriveManipulator>
34 #include <osgGA/KeySwitchMatrixManipulator>
35 #include <osgGA/AnimationPathManipulator>
36 #include <osgGA/TerrainManipulator>
37 #include <osgGA/AnimationPathManipulator>
38 #include <osgGA/StateSetManipulator>
39 #include <osgGA/MultiTouchTrackballManipulator>
40 
41 #include <osgPresentation/SlideEventHandler>
42 #include <osgPresentation/Cursor>
43 
44 #include "ReadShowFile.h"
45 #include "PointsEventHandler.h"
46 #include "Cluster.h"
47 #include "ExportHTML.h"
48 #include "SpellChecker.h"
49 
50 #include <sstream>
51 #include <fstream>
52 #include <iostream>
53 
54 
55 #include <string.h>
56 
57 #ifdef USE_SDL
58     #include "SDLIntegration.h"
59 #endif
60 
61 #ifdef OSG_LIBRARY_STATIC
62 
63     // include the plugins we need
64     USE_OSGPLUGIN(ive)
65     USE_OSGPLUGIN(osg)
66     USE_OSGPLUGIN(osg2)
67     USE_OSGPLUGIN(p3d)
68     USE_OSGPLUGIN(paths)
69     USE_OSGPLUGIN(rgb)
70     USE_OSGPLUGIN(OpenFlight)
71     USE_OSGPLUGIN(obj)
72 
73 #ifdef USE_FREETYPE
74     USE_OSGPLUGIN(freetype)
75 #endif
76 
77 #ifdef USE_PNG
78     USE_OSGPLUGIN(png)
79 #endif
80 
81 #ifdef USE_JPEG
82     USE_OSGPLUGIN(jpeg)
83 #endif
84 
85 #ifdef USE_FFMPEG
86     USE_OSGPLUGIN(ffmpeg)
87 #endif
88 
89 #ifdef USE_POPPLER_CAIRO
90     USE_OSGPLUGIN(pdf)
91 #endif
92 
93 #ifdef USE_CURL
94     USE_OSGPLUGIN(curl)
95 #endif
96 
97     USE_DOTOSGWRAPPER_LIBRARY(osg)
98     USE_DOTOSGWRAPPER_LIBRARY(osgFX)
99     USE_DOTOSGWRAPPER_LIBRARY(osgParticle)
100     USE_DOTOSGWRAPPER_LIBRARY(osgShadow)
101     USE_DOTOSGWRAPPER_LIBRARY(osgSim)
102     USE_DOTOSGWRAPPER_LIBRARY(osgTerrain)
103     USE_DOTOSGWRAPPER_LIBRARY(osgText)
104     USE_DOTOSGWRAPPER_LIBRARY(osgViewer)
105     USE_DOTOSGWRAPPER_LIBRARY(osgVolume)
106     USE_DOTOSGWRAPPER_LIBRARY(osgWidget)
107 
108     USE_SERIALIZER_WRAPPER_LIBRARY(osg)
109     USE_SERIALIZER_WRAPPER_LIBRARY(osgAnimation)
110     USE_SERIALIZER_WRAPPER_LIBRARY(osgFX)
111     USE_SERIALIZER_WRAPPER_LIBRARY(osgManipulator)
112     USE_SERIALIZER_WRAPPER_LIBRARY(osgParticle)
113     USE_SERIALIZER_WRAPPER_LIBRARY(osgShadow)
114     USE_SERIALIZER_WRAPPER_LIBRARY(osgSim)
115     USE_SERIALIZER_WRAPPER_LIBRARY(osgTerrain)
116     USE_SERIALIZER_WRAPPER_LIBRARY(osgText)
117     USE_SERIALIZER_WRAPPER_LIBRARY(osgVolume)
118 
119     // include the platform specific GraphicsWindow implementation.
120     USE_GRAPHICSWINDOW()
121 
122 #endif
123 
124 static const char* s_version = "1.4 beta";
125 
setViewer(osgViewer::Viewer & viewer,float width,float height,float distance)126 void setViewer(osgViewer::Viewer& viewer, float width, float height, float distance)
127 {
128     double vfov = osg::RadiansToDegrees(atan2(height/2.0f,distance)*2.0);
129     // double hfov = osg::RadiansToDegrees(atan2(width/2.0f,distance)*2.0);
130 
131     viewer.getCamera()->setProjectionMatrixAsPerspective( vfov, width/height, 0.1, 1000.0);
132 
133     OSG_INFO<<"setProjectionMatrixAsPerspective( "<<vfov<<", "<<width/height<<", "<<0.1<<", "<<1000.0<<");"<<std::endl;
134 }
135 
136 class ForwardToDeviceEventHandler : public osgGA::GUIEventHandler {
137 public:
ForwardToDeviceEventHandler(osgGA::Device * device,bool format_mouse_events)138     ForwardToDeviceEventHandler(osgGA::Device* device, bool format_mouse_events) : osgGA::GUIEventHandler(), _device(device), _forwardMouseEvents(format_mouse_events) {}
139 
handle(const osgGA::GUIEventAdapter & ea,osgGA::GUIActionAdapter & aa,osg::Object *,osg::NodeVisitor *)140     virtual bool handle (const osgGA::GUIEventAdapter &ea, osgGA::GUIActionAdapter &aa, osg::Object *, osg::NodeVisitor *)
141     {
142         switch (ea.getEventType())
143         {
144             case osgGA::GUIEventAdapter::PUSH:
145             case osgGA::GUIEventAdapter::RELEASE:
146             case osgGA::GUIEventAdapter::MOVE:
147             case osgGA::GUIEventAdapter::DRAG:
148             case osgGA::GUIEventAdapter::SCROLL:
149                 if (_forwardMouseEvents)
150                     _device->sendEvent(ea);
151                 break;
152 
153             default:
154                 _device->sendEvent(ea);
155                 break;
156         }
157         return false;
158     }
159 
160 
handle(osgGA::Event * event,osg::Object * object,osg::NodeVisitor * nv)161     bool handle(osgGA::Event* event, osg::Object* object, osg::NodeVisitor* nv)
162     {
163         if (event->asGUIEventAdapter())
164             return osgGA::GUIEventHandler::handle(event, object, nv);
165         else
166         {
167             _device->sendEvent(*event);
168             return false;
169         }
170     }
171 
172 
173 private:
174     osg::ref_ptr<osgGA::Device> _device;
175     bool _forwardMouseEvents;
176 };
177 
178 
179 class DumpEventHandler : public osgGA::GUIEventHandler {
180 public:
DumpEventHandler()181     DumpEventHandler() : osgGA::GUIEventHandler() {}
182 
handle(const osgGA::GUIEventAdapter & ea,osgGA::GUIActionAdapter & aa,osg::Object *,osg::NodeVisitor *)183     virtual bool handle (const osgGA::GUIEventAdapter &ea, osgGA::GUIActionAdapter &aa, osg::Object *, osg::NodeVisitor *)
184     {
185         switch (ea.getEventType())
186         {
187             case osgGA::GUIEventAdapter::FRAME:
188                 return false;
189                 break;
190             case osgGA::GUIEventAdapter::PUSH:
191                 std::cout << "PUSH: ";
192                 break;
193             case osgGA::GUIEventAdapter::RELEASE:
194                 std::cout << "RELEASE: ";
195                 break;
196             case osgGA::GUIEventAdapter::MOVE:
197                 std::cout << "MOVE: ";
198                 break;
199             case osgGA::GUIEventAdapter::DRAG:
200                 std::cout << "DRAG: ";
201                 break;
202             case osgGA::GUIEventAdapter::SCROLL:
203                 std::cout << "SCROLL: ";
204                 break;
205                 break;
206 
207             default:
208                 std::cout << ea.getEventType() << " ";
209                 break;
210         }
211         std::cout << ea.getX() << "/" << ea.getY() << " " << ea.isMultiTouchEvent() << std::endl;
212         return false;
213     }
214 
215 
handle(osgGA::Event * event,osg::Object * object,osg::NodeVisitor * nv)216     bool handle(osgGA::Event* event, osg::Object* object, osg::NodeVisitor* nv)
217     {
218         if (event->asGUIEventAdapter())
219             return osgGA::GUIEventHandler::handle(event, object, nv);
220         else
221         {
222             return false;
223         }
224     }
225 
226 
227 private:
228 };
229 
230 
231 
232 enum P3DApplicationType
233 {
234     VIEWER,
235     MASTER,
236     SLAVE
237 };
238 
239 
processLoadedModel(osg::ref_ptr<osg::Node> & loadedModel,int optimizer_options,const std::string & cursorFileName)240 void processLoadedModel(osg::ref_ptr<osg::Node>& loadedModel, int optimizer_options, const std::string& cursorFileName)
241 {
242     if (!loadedModel) return;
243 
244 #if !defined(OSG_GLES2_AVAILABLE) && !defined(OSG_GL3_AVAILABLE)
245 
246     // add back in enabling of the GL_ALPHA_TEST to get around the core OSG no longer setting it by default for opaque bins.
247     // the alpha test is required for the volume rendering alpha clipping to work.
248     loadedModel->getOrCreateStateSet()->setMode(GL_ALPHA_TEST, osg::StateAttribute::ON);
249 #endif
250 
251     // optimize the scene graph, remove redundant nodes and state etc.
252     osgUtil::Optimizer optimizer;
253     optimizer.optimize(loadedModel.get(), optimizer_options);
254 
255     if (!cursorFileName.empty())
256     {
257         osg::ref_ptr<osg::Group> group = new osg::Group;
258         group->addChild(loadedModel.get());
259 
260         OSG_NOTICE<<"Creating Cursor"<<std::endl;
261         group->addChild(new osgPresentation::Cursor(cursorFileName, 20.0f));
262 
263         loadedModel = group;
264     }
265 }
266 
addDeviceTo(osgViewer::Viewer & viewer,const std::string & device_name,bool forward_mouse_events)267 void addDeviceTo(osgViewer::Viewer& viewer, const std::string& device_name, bool forward_mouse_events)
268 {
269     osg::ref_ptr<osgGA::Device> dev = osgDB::readRefFile<osgGA::Device>(device_name);
270     if (dev.valid())
271     {
272         OSG_INFO << "Adding Device : " << device_name << std::endl;
273         viewer.addDevice(dev.get());
274 
275         if ((dev->getCapabilities() & osgGA::Device::SEND_EVENTS))
276             viewer.getEventHandlers().push_front(new ForwardToDeviceEventHandler(dev.get(), forward_mouse_events));
277     }
278     else
279     {
280         OSG_WARN << "could not open device: " << device_name << std::endl;
281     }
282 }
283 
284 
main(int argc,char ** argv)285 int main( int argc, char **argv )
286 {
287     // use an ArgumentParser object to manage the program arguments.
288     osg::ArgumentParser arguments(&argc,argv);
289 
290     // set up the usage document, in case we need to print out how to use this program.
291     arguments.getApplicationUsage()->setApplicationName(arguments.getApplicationName());
292     arguments.getApplicationUsage()->setDescription(arguments.getApplicationName()+" is the application for presenting 3D interactive slide shows.");
293     arguments.getApplicationUsage()->setCommandLineUsage(arguments.getApplicationName()+" [options] filename ...");
294     arguments.getApplicationUsage()->addCommandLineOption("-h or --help","Display this information");
295     arguments.getApplicationUsage()->addCommandLineOption("-a","Turn auto stepping on by default");
296     arguments.getApplicationUsage()->addCommandLineOption("-d <float>","Time duration in seconds between layers/slides");
297     arguments.getApplicationUsage()->addCommandLineOption("-s <float> <float> <float>","width, height, distance and of the screen away from the viewer");
298     arguments.getApplicationUsage()->addCommandLineOption("--viewer","Start Present3D as the viewer version.");
299     arguments.getApplicationUsage()->addCommandLineOption("--authoring","Start Present3D as the authoring version, license required.");
300     arguments.getApplicationUsage()->addCommandLineOption("--master","Start Present3D as the master version, license required.");
301     arguments.getApplicationUsage()->addCommandLineOption("--slave","Start Present3D as the slave version, license required.");
302     arguments.getApplicationUsage()->addCommandLineOption("--publishing","Start Present3D as the publishing version, license required.");
303     arguments.getApplicationUsage()->addCommandLineOption("--timeDelayOnNewSlideWithMovies","Set the time delay on new slide with movies, done to allow movie threads to get in sync with rendering thread.");
304     arguments.getApplicationUsage()->addCommandLineOption("--targetFrameRate","Set the target frame rate, defaults to 80Hz.");
305     arguments.getApplicationUsage()->addCommandLineOption("--version","Report the Present3D version.");
306     arguments.getApplicationUsage()->addCommandLineOption("--print <filename>","Print out slides to a series of image files.");
307     arguments.getApplicationUsage()->addCommandLineOption("--html <filename>","Print out slides to a series of html & image files.");
308     arguments.getApplicationUsage()->addCommandLineOption("--loop","Switch on looping of presentation.");
309     arguments.getApplicationUsage()->addCommandLineOption("--devices","Print the Video input capability via QuickTime and exit.");
310     arguments.getApplicationUsage()->addCommandLineOption("--forwardMouseEvents","forward also mouse/touch-events to the devices");
311     arguments.getApplicationUsage()->addCommandLineOption("--suppressEnvTags", "suppresses all found ENV-tags in the presentation");
312 
313     // add alias from xml to p3d to provide backwards compatibility for old p3d files.
314     osgDB::Registry::instance()->addFileExtensionAlias("xml","p3d");
315 
316     // if user requests devices video capability.
317     if (arguments.read("-devices") || arguments.read("--devices"))
318     {
319         // Force load QuickTime plugin, probe video capability, exit
320         osgDB::readRefImageFile("devices.live");
321         return 1;
322     }
323 
324     bool suppress_env_tags = false;
325     if (arguments.read("--suppressEnvTags"))
326         suppress_env_tags = true;
327 
328     // read any env vars from presentations before we create viewer to make sure the viewer
329     // utilises these env vars
330     if (!suppress_env_tags && p3d::readEnvVars(arguments))
331     {
332         osg::DisplaySettings::instance()->readEnvironmentalVariables();
333     }
334 
335     // set up any logins required for http access
336     std::string url, username, password;
337     while(arguments.read("--login",url, username, password))
338     {
339         if (!osgDB::Registry::instance()->getAuthenticationMap())
340         {
341             osgDB::Registry::instance()->setAuthenticationMap(new osgDB::AuthenticationMap);
342             osgDB::Registry::instance()->getAuthenticationMap()->addAuthenticationDetails(
343                 url,
344                 new osgDB::AuthenticationDetails(username, password)
345             );
346         }
347     }
348 
349 
350 
351 #ifdef USE_SDL
352     SDLIntegration sdlIntegration;
353 
354     osg::notify(osg::INFO)<<"USE_SDL"<<std::endl;
355 #endif
356 
357     bool doSetViewer = true;
358     std::string configurationFile;
359 
360     // check env vars for configuration file
361     const char* str = getenv("PRESENT3D_CONFIG_FILE");
362     if (!str) str = getenv("OSG_CONFIG_FILE");
363     if (str) configurationFile = str;
364 
365     // check command line parameters for configuration file.
366     while (arguments.read("-c",configurationFile)) {}
367 
368     osg::Vec4 clearColor(0.0f,0.0f,0.0f,0.0f);
369 
370     while (arguments.read("--clear-color",clearColor[0],clearColor[1],clearColor[2],clearColor[3])) {}
371 
372     std::string filename;
373     if (arguments.read("--spell-check",filename))
374     {
375         p3d::SpellChecker spellChecker;
376         spellChecker.checkP3dXml(filename);
377         return 1;
378     }
379 
380     if (arguments.read("--strip-text",filename))
381     {
382         p3d::XmlPatcher patcher;
383         // patcher.stripP3dXml(filename, osg::notify(osg::NOTICE));
384 
385         osg::ref_ptr<osgDB::XmlNode> newNode = patcher.simplifyP3dXml(filename);
386         if (newNode.valid())
387         {
388             newNode->write(std::cout);
389         }
390         return 1;
391     }
392 
393     std::string lhs_filename, rhs_filename;
394     if (arguments.read("--merge",lhs_filename, rhs_filename))
395     {
396         p3d::XmlPatcher patcher;
397         osg::ref_ptr<osgDB::XmlNode> newNode = patcher.mergeP3dXml(lhs_filename, rhs_filename);
398         if (newNode.valid())
399         {
400             newNode->write(std::cout);
401         }
402         return 1;
403     }
404 
405 
406     // construct the viewer.
407     osgViewer::Viewer viewer(arguments);
408 
409     // set clear colour to black by default.
410     viewer.getCamera()->setClearColor(clearColor);
411 
412     if (!configurationFile.empty())
413     {
414         viewer.readConfiguration(configurationFile);
415         doSetViewer = false;
416     }
417 
418     bool forwardMouseEvents = false;
419     if (arguments.read("--forwardMouseEvents"))
420         forwardMouseEvents = true;
421 
422     const char* p3dDevice = getenv("P3D_DEVICE");
423     if (p3dDevice)
424     {
425         osgDB::StringList devices;
426         osgDB::split(p3dDevice, devices);
427         for(osgDB::StringList::iterator i = devices.begin(); i != devices.end(); ++i)
428         {
429             addDeviceTo(viewer, *i, forwardMouseEvents);
430         }
431     }
432 
433 
434     std::string device;
435     while (arguments.read("--device", device))
436     {
437         addDeviceTo(viewer, device, forwardMouseEvents);
438 
439     }
440 
441     if (arguments.read("--http-control"))
442     {
443 
444         std::string server_address = "localhost";
445         std::string server_port = "8080";
446         std::string document_root = "htdocs";
447 
448         while (arguments.read("--http-server-address", server_address)) {}
449         while (arguments.read("--http-server-port", server_port)) {}
450         while (arguments.read("--http-document-root", document_root)) {}
451 
452         osg::ref_ptr<osgDB::Options> device_options = new osgDB::Options("documentRegisteredHandlers");
453 
454         osg::ref_ptr<osgGA::Device> rest_http_device = osgDB::readRefFile<osgGA::Device>(server_address+":"+server_port+"/"+document_root+".resthttp", device_options.get());
455         if (rest_http_device.valid())
456         {
457             viewer.addDevice(rest_http_device.get());
458         }
459     }
460 
461     // set up stereo masks
462 
463     viewer.getCamera()->setCullMaskLeft(0x00000001);
464     viewer.getCamera()->setCullMaskRight(0x00000002);
465 
466     bool assignLeftCullMaskForMono = true;
467     if (assignLeftCullMaskForMono)
468     {
469         viewer.getCamera()->setCullMask(viewer.getCamera()->getCullMaskLeft());
470     }
471     else
472     {
473         viewer.getCamera()->setCullMask(0xffffffff);
474     }
475 
476     // set up the camera manipulators.
477     {
478         osg::ref_ptr<osgGA::KeySwitchMatrixManipulator> keyswitchManipulator = new osgGA::KeySwitchMatrixManipulator;
479 
480         keyswitchManipulator->addMatrixManipulator( '1', "Trackball", new osgGA::MultiTouchTrackballManipulator() );
481         keyswitchManipulator->addMatrixManipulator( '2', "Flight", new osgGA::FlightManipulator() );
482         keyswitchManipulator->addMatrixManipulator( '3', "Drive", new osgGA::DriveManipulator() );
483         keyswitchManipulator->addMatrixManipulator( '4', "Terrain", new osgGA::TerrainManipulator() );
484 
485         std::string pathfile;
486         char keyForAnimationPath = '5';
487         while (arguments.read("-p",pathfile))
488         {
489             osgGA::AnimationPathManipulator* apm = new osgGA::AnimationPathManipulator(pathfile);
490             if (apm || !apm->valid())
491             {
492                 unsigned int num = keyswitchManipulator->getNumMatrixManipulators();
493                 keyswitchManipulator->addMatrixManipulator( keyForAnimationPath, "Path", apm );
494                 keyswitchManipulator->selectMatrixManipulator(num);
495                 ++keyForAnimationPath;
496             }
497         }
498 
499         viewer.setCameraManipulator( keyswitchManipulator.get() );
500     }
501 
502     //viewer.getEventHandlers().push_front(new DumpEventHandler());
503 
504     // add the state manipulator
505     osg::ref_ptr<osgGA::StateSetManipulator> ssManipulator = new osgGA::StateSetManipulator(viewer.getCamera()->getOrCreateStateSet());
506     ssManipulator->setKeyEventToggleTexturing('e');
507     viewer.addEventHandler( ssManipulator.get() );
508 
509     // add the state manipulator
510     viewer.addEventHandler( new osgViewer::StatsHandler() );
511 
512     viewer.addEventHandler( new osgViewer::WindowSizeHandler() );
513 
514     // neeed to address.
515     // viewer.getScene()->getUpdateVisitor()->setTraversalMode(osg::NodeVisitor::TRAVERSE_ACTIVE_CHILDREN);
516 
517 
518     const char* p3dCursor = getenv("P3D_CURSOR");
519     std::string cursorFileName( p3dCursor ? p3dCursor : "");
520     while (arguments.read("--cursor",cursorFileName)) {}
521 
522     const char* p3dShowCursor = getenv("P3D_SHOW_CURSOR");
523     std::string showCursor( p3dShowCursor ? p3dShowCursor : "YES");
524     while (arguments.read("--show-cursor")) { showCursor="YES"; }
525     while (arguments.read("--hide-cursor")) { showCursor="NO"; }
526 
527     bool hideCursor = (showCursor=="No" || showCursor=="NO" || showCursor=="no");
528 
529     while (arguments.read("--set-viewer")) { doSetViewer = true; }
530 
531     while (arguments.read("--no-set-viewer")) { doSetViewer = false; }
532 
533     // if we want to hide the cursor override the custom cursor.
534     if (hideCursor) cursorFileName.clear();
535 
536 
537     // cluster related entries.
538     int socketNumber=8100;
539     while (arguments.read("-n",socketNumber)) {}
540 
541     float camera_fov=-1.0f;
542     while (arguments.read("-f",camera_fov)) {}
543 
544     float camera_offset=45.0f;
545     while (arguments.read("-o",camera_offset)) {}
546 
547 
548     std::string exportName;
549     while (arguments.read("--print",exportName)) {}
550 
551     while (arguments.read("--html",exportName)) {}
552 
553     // read any time delay argument.
554     float timeDelayBetweenSlides = 1.0f;
555     while (arguments.read("-d",timeDelayBetweenSlides)) {}
556 
557     bool autoSteppingActive = false;
558     while (arguments.read("-a")) autoSteppingActive = true;
559 
560     bool loopPresentation = false;
561     while (arguments.read("--loop")) loopPresentation = true;
562 
563     {
564         // set update hte default traversal mode settings for update visitor
565         // default to osg::NodeVisitor::TRAVERSE_ACTIVE_CHILDREN.
566         osg::NodeVisitor::TraversalMode updateTraversalMode = osg::NodeVisitor::TRAVERSE_ACTIVE_CHILDREN; // viewer.getUpdateVisitor()->getTraversalMode();
567 
568         const char* p3dUpdateStr = getenv("P3D_UPDATE");
569         if (p3dUpdateStr)
570         {
571             std::string updateStr(p3dUpdateStr);
572             if (updateStr=="active" || updateStr=="Active" || updateStr=="ACTIVE") updateTraversalMode = osg::NodeVisitor::TRAVERSE_ACTIVE_CHILDREN;
573             else if (updateStr=="all" || updateStr=="All" || updateStr=="ALL") updateTraversalMode = osg::NodeVisitor::TRAVERSE_ALL_CHILDREN;
574         }
575 
576         while(arguments.read("--update-active")) updateTraversalMode = osg::NodeVisitor::TRAVERSE_ACTIVE_CHILDREN;
577         while(arguments.read("--update-all")) updateTraversalMode = osg::NodeVisitor::TRAVERSE_ALL_CHILDREN;
578 
579         viewer.getUpdateVisitor()->setTraversalMode(updateTraversalMode);
580     }
581 
582 
583     // register the slide event handler - which moves the presentation from slide to slide, layer to layer.
584     osg::ref_ptr<osgPresentation::SlideEventHandler> seh = new osgPresentation::SlideEventHandler(&viewer);
585     viewer.addEventHandler(seh.get());
586 
587     seh->setAutoSteppingActive(autoSteppingActive);
588     seh->setTimeDelayBetweenSlides(timeDelayBetweenSlides);
589     seh->setLoopPresentation(loopPresentation);
590 
591     double targetFrameRate = 80.0;
592     while (arguments.read("--targetFrameRate",targetFrameRate)) {}
593 
594 
595     // set the time delay
596     float timeDelayOnNewSlideWithMovies = 0.4f;
597     while (arguments.read("--timeDelayOnNewSlideWithMovies",timeDelayOnNewSlideWithMovies)) {}
598     seh->setTimeDelayOnNewSlideWithMovies(timeDelayOnNewSlideWithMovies);
599 
600     // set up optimizer options
601     unsigned int optimizer_options = osgUtil::Optimizer::DEFAULT_OPTIMIZATIONS;
602     bool release_and_compile = false;
603     while (arguments.read("--release-and-compile"))
604     {
605         release_and_compile = true;
606     }
607     seh->setReleaseAndCompileOnEachNewSlide(release_and_compile);
608     if (release_and_compile)
609     {
610         // make sure that imagery stays around after being applied to textures.
611         viewer.getDatabasePager()->setUnrefImageDataAfterApplyPolicy(true,false);
612         optimizer_options &= ~osgUtil::Optimizer::OPTIMIZE_TEXTURE_SETTINGS;
613     }
614 //
615 //     osgDB::Registry::instance()->getOrCreateDatabasePager()->setUnrefImageDataAfterApplyPolicy(true,false);
616 //     optimizer_options &= ~osgUtil::Optimizer::OPTIMIZE_TEXTURE_SETTINGS;
617 //     osg::Texture::getTextureObjectManager()->setExpiryDelay(0.0f);
618 //     osgDB::Registry::instance()->getOrCreateDatabasePager()->setExpiryDelay(1.0f);
619 
620     // register the handler for modifying the point size
621     osg::ref_ptr<PointsEventHandler> peh = new PointsEventHandler;
622     viewer.addEventHandler(peh.get());
623 
624     // add the screen capture handler
625     std::string screenCaptureFilename = "screen_shot.jpg";
626     while(arguments.read("--screenshot", screenCaptureFilename)) {}
627     osg::ref_ptr<osgViewer::ScreenCaptureHandler::WriteToFile> writeFile = new osgViewer::ScreenCaptureHandler::WriteToFile(
628         osgDB::getNameLessExtension(screenCaptureFilename),
629         osgDB::getFileExtension(screenCaptureFilename) );
630     osg::ref_ptr<osgViewer::ScreenCaptureHandler> screenCaptureHandler = new osgViewer::ScreenCaptureHandler(writeFile.get());
631     screenCaptureHandler->setKeyEventTakeScreenShot('m');//osgGA::GUIEventAdapter::KEY_Print);
632     screenCaptureHandler->setKeyEventToggleContinuousCapture('M');
633     viewer.addEventHandler(screenCaptureHandler.get());
634 
635     // osg::DisplaySettings::instance()->setSplitStereoAutoAjustAspectRatio(false);
636 
637     float width = osg::DisplaySettings::instance()->getScreenWidth();
638     float height = osg::DisplaySettings::instance()->getScreenHeight();
639     float distance = osg::DisplaySettings::instance()->getScreenDistance();
640     while (arguments.read("-s", width, height, distance))
641     {
642         osg::DisplaySettings::instance()->setScreenDistance(distance);
643         osg::DisplaySettings::instance()->setScreenHeight(height);
644         osg::DisplaySettings::instance()->setScreenWidth(width);
645     }
646 
647     std::string outputFileName;
648     while(arguments.read("--output",outputFileName)) {}
649 
650 
651     // get details on keyboard and mouse bindings used by the viewer.
652     viewer.getUsage(*arguments.getApplicationUsage());
653 
654     // if user request help write it out to cout.
655     unsigned int helpType = 0;
656     if ((helpType = arguments.readHelpType()))
657     {
658         arguments.getApplicationUsage()->write(std::cout, helpType);
659         return 1;
660     }
661 
662     P3DApplicationType P3DApplicationType = VIEWER;
663 
664     str = getenv("PRESENT3D_TYPE");
665     if (str)
666     {
667         if (strcmp(str,"viewer")==0) P3DApplicationType = VIEWER;
668         else if (strcmp(str,"master")==0) P3DApplicationType = MASTER;
669         else if (strcmp(str,"slave")==0) P3DApplicationType = SLAVE;
670     }
671 
672     while (arguments.read("--viewer")) { P3DApplicationType = VIEWER; }
673     while (arguments.read("--master")) { P3DApplicationType = MASTER; }
674     while (arguments.read("--slave")) { P3DApplicationType = SLAVE; }
675 
676     while (arguments.read("--version"))
677     {
678         std::string appTypeName = "invalid";
679         switch(P3DApplicationType)
680         {
681             case(VIEWER): appTypeName = "viewer"; break;
682             case(MASTER): appTypeName = "master"; break;
683             case(SLAVE): appTypeName = "slave"; break;
684         }
685 
686         osg::notify(osg::NOTICE)<<std::endl;
687         osg::notify(osg::NOTICE)<<"Present3D "<<appTypeName<<" version : "<<s_version<<std::endl;
688         osg::notify(osg::NOTICE)<<std::endl;
689 
690         return 0;
691     }
692 
693     // any option left unread are converted into errors to write out later.
694     //arguments.reportRemainingOptionsAsUnrecognized();
695 
696     // report any errors if they have ocured when parsing the program aguments.
697     if (arguments.errors())
698     {
699         arguments.writeErrorMessages(osg::notify(osg::INFO));
700         return 1;
701     }
702 
703 
704     // read files name from arguments.
705     p3d::FileNameList xmlFiles, normalFiles;
706     if (!p3d::getFileNames(arguments, xmlFiles, normalFiles))
707     {
708         osg::notify(osg::NOTICE)<<std::endl;
709         osg::notify(osg::NOTICE)<<"No file specified, please specify and file to load."<<std::endl;
710         osg::notify(osg::NOTICE)<<std::endl;
711         return 1;
712     }
713 
714 
715 
716     bool viewerInitialized = false;
717     if (!xmlFiles.empty())
718     {
719         osg::ref_ptr<osg::Node> holdingModel = p3d::readHoldingSlide(xmlFiles.front());
720 
721         if (holdingModel.valid())
722         {
723             viewer.setSceneData(holdingModel.get());
724 
725             seh->selectSlide(0);
726 
727             if (!viewerInitialized)
728             {
729                 // pass the global stateset to the point event handler so that it can
730                 // alter the point size of all points in the scene.
731                 peh->setStateSet(viewer.getCamera()->getOrCreateStateSet());
732 
733                 // create the windows and run the threads.
734                 viewer.realize();
735 
736                 if (doSetViewer) setViewer(viewer, width, height, distance);
737 
738                 viewerInitialized = true;
739             }
740 
741             seh->home();
742 
743             // render a frame
744             viewer.frame();
745         }
746     }
747 
748     osg::Timer timer;
749     osg::Timer_t start_tick = timer.tick();
750 
751 
752     osg::ref_ptr<osgDB::ReaderWriter::Options> cacheAllOption = new osgDB::ReaderWriter::Options;
753     if(suppress_env_tags)
754         cacheAllOption->setPluginStringData("suppressEnvTags", "true");
755 
756     cacheAllOption->setObjectCacheHint(osgDB::ReaderWriter::Options::CACHE_ALL);
757     osgDB::Registry::instance()->setOptions(cacheAllOption.get());
758 
759     // read the scene from the list of file specified commandline args.
760     osg::ref_ptr<osg::Node> loadedModel = p3d::readShowFiles(arguments,cacheAllOption.get()); // osgDB::readNodeFiles(arguments, cacheAllOption.get());
761 
762 
763     osgDB::Registry::instance()->setOptions( 0 );
764 
765 
766     // if no model has been successfully loaded report failure.
767     if (!loadedModel)
768     {
769         osg::notify(osg::INFO) << arguments.getApplicationName() <<": No data loaded" << std::endl;
770         return 1;
771     }
772 
773     osg::Timer_t end_tick = timer.tick();
774 
775     osg::notify(osg::INFO) << "Time to load = "<<timer.delta_s(start_tick,end_tick)<<std::endl;
776 
777 
778     if (loadedModel->getNumDescriptions()>0)
779     {
780         for(unsigned int i=0; i<loadedModel->getNumDescriptions(); ++i)
781         {
782             const std::string& desc = loadedModel->getDescription(i);
783             if (desc=="loop")
784             {
785                 osg::notify(osg::NOTICE)<<"Enabling looping"<<std::endl;
786                 seh->setLoopPresentation(true);
787             }
788             else if (desc=="auto")
789             {
790                 osg::notify(osg::NOTICE)<<"Enabling auto run"<<std::endl;
791                 seh->setAutoSteppingActive(true);
792             }
793         }
794     }
795 
796 
797     processLoadedModel(loadedModel, optimizer_options, cursorFileName);
798 
799     // set the scene to render
800     viewer.setSceneData(loadedModel.get());
801 
802     if (!viewerInitialized)
803     {
804         // pass the global stateset to the point event handler so that it can
805         // alter the point size of all points in the scene.
806         peh->setStateSet(viewer.getCamera()->getOrCreateStateSet());
807 
808         // create the windows and run the threads.
809         viewer.realize();
810 
811         if (doSetViewer) setViewer(viewer, width, height, distance);
812 
813         viewerInitialized = true;
814     }
815 
816 
817 
818 
819     // pass the model to the slide event handler so it knows which to manipulate.
820     seh->set(loadedModel.get());
821     seh->selectSlide(0);
822 
823     seh->home();
824 
825     if (!outputFileName.empty())
826     {
827         osgDB::writeNodeFile(*loadedModel,outputFileName);
828         return 0;
829     }
830 
831 
832     if (!cursorFileName.empty() || hideCursor)
833     {
834         // have to add a frame in here to avoid problems with X11 threading issue on switching off the cursor
835         // not yet sure why it makes a difference, but it at least fixes the crash that would otherwise occur
836         // under X11.
837         viewer.frame();
838 
839         // switch off the cursor
840         osgViewer::Viewer::Windows windows;
841         viewer.getWindows(windows);
842         for(osgViewer::Viewer::Windows::iterator itr = windows.begin();
843             itr != windows.end();
844             ++itr)
845         {
846             (*itr)->useCursor(false);
847         }
848     }
849 
850     osg::Timer_t startOfFrameTick = osg::Timer::instance()->tick();
851     double targetFrameTime = 1.0/targetFrameRate;
852 
853     if (exportName.empty())
854     {
855         // objects for managing the broadcasting and recieving of camera packets.
856         CameraPacket cp;
857         Broadcaster  bc;
858         Receiver     rc;
859         bc.setPort(static_cast<short int>(socketNumber));
860         rc.setPort(static_cast<short int>(socketNumber));
861 
862         bool masterKilled = false;
863         DataConverter scratchPad(1024);
864 
865         while( !viewer.done() && !masterKilled)
866         {
867             // wait for all cull and draw threads to complete.
868             viewer.advance();
869 
870             osg::Timer_t currentTick = osg::Timer::instance()->tick();
871             double deltaTime = osg::Timer::instance()->delta_s(startOfFrameTick, currentTick);
872 
873 
874             if (deltaTime<targetFrameTime)
875             {
876                 OpenThreads::Thread::microSleep(static_cast<unsigned int>((targetFrameTime-deltaTime)*1000000.0));
877             }
878 
879             startOfFrameTick =  osg::Timer::instance()->tick();
880 
881 #if 0
882             if (kmcb)
883             {
884                 double time = kmcb->getTime();
885                 viewer.getFrameStamp()->setReferenceTime(time);
886             }
887 #endif
888 
889 #ifdef USE_SDL
890             sdlIntegration.update(viewer);
891 #endif
892 
893             if (P3DApplicationType==MASTER)
894             {
895                 // take camera zero as the guide.
896                 osg::Matrix modelview(viewer.getCamera()->getViewMatrix());
897 
898                 cp.setPacket(modelview,viewer.getFrameStamp());
899 
900                 // cp.readEventQueue(viewer);
901 
902                 scratchPad.reset();
903                 scratchPad.write(cp);
904 
905                 scratchPad.reset();
906                 scratchPad.read(cp);
907 
908                 bc.setBuffer(scratchPad.startPtr(), scratchPad.numBytes());
909 
910                 std::cout << "bc.sync()"<<scratchPad.numBytes()<<std::endl;
911 
912                 bc.sync();
913             }
914             else if (P3DApplicationType==SLAVE)
915             {
916                 rc.setBuffer(scratchPad.startPtr(), scratchPad.numBytes());
917 
918                 rc.sync();
919 
920                 scratchPad.reset();
921                 scratchPad.read(cp);
922 
923                 // cp.writeEventQueue(viewer);
924 
925                 if (cp.getMasterKilled())
926                 {
927                     std::cout << "Received master killed."<<std::endl;
928                     // break out of while (!done) loop since we've now want to shut down.
929                     masterKilled = true;
930                 }
931             }
932 
933             // update the scene by traversing it with the update visitor which will
934             // call all node update callbacks and animations.
935             viewer.eventTraversal();
936 
937             if (seh->getRequestReload())
938             {
939                 OSG_INFO<<"Reload requested"<<std::endl;
940                 seh->setRequestReload(false);
941                 int previous_ActiveSlide = seh->getActiveSlide();
942                 int previous_ActiveLayer = seh->getActiveLayer();
943 
944                 // reset time so any event key generate
945 
946                 loadedModel = p3d::readShowFiles(arguments,cacheAllOption.get());
947                 processLoadedModel(loadedModel, optimizer_options, cursorFileName);
948 
949                 if (!loadedModel)
950                 {
951                     return 0;
952                 }
953 
954                 viewer.setSceneData(loadedModel.get());
955                 seh->set(loadedModel.get());
956                 seh->selectSlide(previous_ActiveSlide, previous_ActiveLayer);
957 
958                 continue;
959 
960             }
961 
962             // update the scene by traversing it with the update visitor which will
963             // call all node update callbacks and animations.
964             viewer.updateTraversal();
965 
966             if (P3DApplicationType==SLAVE)
967             {
968                 osg::Matrix modelview;
969                 cp.getModelView(modelview,camera_offset);
970 
971                 viewer.getCamera()->setViewMatrix(modelview);
972             }
973 
974             // fire off the cull and draw traversals of the scene.
975             if(!masterKilled)
976                 viewer.renderingTraversals();
977         }
978     }
979     else
980     {
981         ExportHTML::write(seh.get(), viewer, exportName);
982     }
983 
984     return 0;
985 }
986 
987