root/OpenSceneGraph/trunk/src/osgViewer/CompositeViewer.cpp @ 13890

Revision 13890, 46.2 kB (checked in by robert, 57 seconds ago)

From Paul Martz, fixed placement of OpenGL header so that it gets generated and placed in the build directory as per the Config file

  • Property svn:eol-style set to native
  • Property svn:keywords set to Author Date Id Revision
Line 
1/* -*-c++-*- OpenSceneGraph - Copyright (C) 1998-2006 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 <osg/GLExtensions>
15#include <osg/TextureRectangle>
16#include <osg/TextureCubeMap>
17
18#include <osgGA/TrackballManipulator>
19#include <osgViewer/CompositeViewer>
20#include <osgViewer/Renderer>
21#include <osgDB/Registry>
22#include <osgDB/ReadFile>
23
24#include <osg/io_utils>
25
26using namespace osgViewer;
27
28CompositeViewer::CompositeViewer()
29{
30    constructorInit();
31}
32
33CompositeViewer::CompositeViewer(const CompositeViewer& cv,const osg::CopyOp& /*copyop*/):
34    osg::Object(true),
35    ViewerBase(cv)
36{
37    constructorInit();
38}
39
40CompositeViewer::CompositeViewer(osg::ArgumentParser& arguments)
41{
42    constructorInit();
43
44    arguments.getApplicationUsage()->addCommandLineOption("--SingleThreaded","Select SingleThreaded threading model for viewer.");
45    arguments.getApplicationUsage()->addCommandLineOption("--CullDrawThreadPerContext","Select CullDrawThreadPerContext threading model for viewer.");
46    arguments.getApplicationUsage()->addCommandLineOption("--DrawThreadPerContext","Select DrawThreadPerContext threading model for viewer.");
47    arguments.getApplicationUsage()->addCommandLineOption("--CullThreadPerCameraDrawThreadPerContext","Select CullThreadPerCameraDrawThreadPerContext threading model for viewer.");
48
49    arguments.getApplicationUsage()->addCommandLineOption("--run-on-demand","Set the run methods frame rate management to only rendering frames when required.");
50    arguments.getApplicationUsage()->addCommandLineOption("--run-continuous","Set the run methods frame rate management to rendering frames continuously.");
51    arguments.getApplicationUsage()->addCommandLineOption("--run-max-frame-rate","Set the run methods maximum permissable frame rate, 0.0 is default and switching off frame rate capping.");
52
53
54    std::string filename;
55    bool readConfig = false;
56    while (arguments.read("-c",filename))
57    {
58        readConfig = readConfiguration(filename) || readConfig;
59    }
60
61    while (arguments.read("--SingleThreaded")) setThreadingModel(SingleThreaded);
62    while (arguments.read("--CullDrawThreadPerContext")) setThreadingModel(CullDrawThreadPerContext);
63    while (arguments.read("--DrawThreadPerContext")) setThreadingModel(DrawThreadPerContext);
64    while (arguments.read("--CullThreadPerCameraDrawThreadPerContext")) setThreadingModel(CullThreadPerCameraDrawThreadPerContext);
65
66
67    while(arguments.read("--run-on-demand")) { setRunFrameScheme(ON_DEMAND); }
68    while(arguments.read("--run-continuous")) { setRunFrameScheme(CONTINUOUS); }
69
70    double runMaxFrameRate;
71    while(arguments.read("--run-max-frame-rate", runMaxFrameRate)) { setRunMaxFrameRate(runMaxFrameRate); }
72
73
74    osg::DisplaySettings::instance()->readCommandLine(arguments);
75    osgDB::readCommandLine(arguments);
76}
77
78void CompositeViewer::constructorInit()
79{
80    _endBarrierPosition = AfterSwapBuffers;
81    _startTick = 0;
82
83    // make sure View is safe to reference multi-threaded.
84    setThreadSafeRefUnref(true);
85
86    _frameStamp = new osg::FrameStamp;
87    _frameStamp->setFrameNumber(0);
88    _frameStamp->setReferenceTime(0);
89    _frameStamp->setSimulationTime(0);
90
91    _eventVisitor = new osgGA::EventVisitor;
92    _eventVisitor->setFrameStamp(_frameStamp.get());
93
94    _updateVisitor = new osgUtil::UpdateVisitor;
95    _updateVisitor->setFrameStamp(_frameStamp.get());
96
97    setViewerStats(new osg::Stats("CompsiteViewer"));
98}
99
100CompositeViewer::~CompositeViewer()
101{
102    OSG_INFO<<"CompositeViewer::~CompositeViewer()"<<std::endl;
103
104    stopThreading();
105
106    Scenes scenes;
107    getScenes(scenes);
108
109    for(Scenes::iterator sitr = scenes.begin();
110        sitr != scenes.end();
111        ++sitr)
112    {
113        Scene* scene = *sitr;
114        if (scene->getDatabasePager())
115        {
116            scene->getDatabasePager()->cancel();
117            scene->setDatabasePager(0);
118        }
119    }
120
121    Contexts contexts;
122    getContexts(contexts);
123
124    // clear out all the previously assigned operations
125    for(Contexts::iterator citr = contexts.begin();
126        citr != contexts.end();
127        ++citr)
128    {
129        (*citr)->close();
130    }
131
132    OSG_INFO<<"finished CompositeViewer::~CompositeViewer()"<<std::endl;
133}
134
135bool CompositeViewer::readConfiguration(const std::string& filename)
136{
137    OSG_NOTICE<<"CompositeViewer::readConfiguration("<<filename<<")"<<std::endl;
138    osg::ref_ptr<osg::Object> obj = osgDB::readObjectFile(filename);
139    osgViewer::View * view = dynamic_cast<osgViewer::View *>(obj.get());
140    if (view)
141    {
142        addView(view);
143        return true;
144    }
145    return false;
146}
147
148
149void CompositeViewer::addView(osgViewer::View* view)
150{
151    if (!view) return;
152
153    bool alreadyRealized = isRealized();
154
155    bool threadsWereRunning = _threadsRunning;
156    if (threadsWereRunning) stopThreading();
157
158    _views.push_back(view);
159
160    view->_viewerBase = this;
161
162    if (view->getSceneData())
163    {
164        // make sure that existing scene graph objects are allocated with thread safe ref/unref
165        if (getThreadingModel()!=ViewerBase::SingleThreaded)
166        {
167            view->getSceneData()->setThreadSafeRefUnref(true);
168        }
169
170        // update the scene graph so that it has enough GL object buffer memory for the graphics contexts that will be using it.
171        view->getSceneData()->resizeGLObjectBuffers(osg::DisplaySettings::instance()->getMaxNumberOfGraphicsContexts());
172    }
173
174    view->setFrameStamp(_frameStamp.get());
175
176    if (alreadyRealized)
177    {
178        Contexts contexts;
179        if (view->getCamera()->getGraphicsContext())
180        {
181            contexts.push_back(view->getCamera()->getGraphicsContext());
182        }
183        for(unsigned int i=0; i<view->getNumSlaves(); ++i)
184        {
185            if (view->getSlave(i)._camera->getGraphicsContext())
186            {
187                contexts.push_back(view->getSlave(i)._camera->getGraphicsContext());
188            }
189        }
190
191        for(Contexts::iterator itr = contexts.begin();
192            itr != contexts.end();
193            ++itr)
194        {
195            if (!((*itr)->isRealized()))
196            {
197                (*itr)->realize();
198            }
199        }
200
201    }
202
203    if (threadsWereRunning) startThreading();
204}
205
206void CompositeViewer::removeView(osgViewer::View* view)
207{
208    for(RefViews::iterator itr = _views.begin();
209        itr != _views.end();
210        ++itr)
211    {
212        if (*itr == view)
213        {
214            bool threadsWereRunning = _threadsRunning;
215            if (threadsWereRunning) stopThreading();
216
217            view->_viewerBase = 0;
218
219            _views.erase(itr);
220
221            if (threadsWereRunning) startThreading();
222
223            return;
224        }
225    }
226}
227
228bool CompositeViewer::isRealized() const
229{
230    Contexts contexts;
231    const_cast<CompositeViewer*>(this)->getContexts(contexts);
232
233    unsigned int numRealizedWindows = 0;
234
235    // clear out all the previously assigned operations
236    for(Contexts::iterator citr = contexts.begin();
237        citr != contexts.end();
238        ++citr)
239    {
240        if ((*citr)->isRealized()) ++numRealizedWindows;
241    }
242
243    return numRealizedWindows > 0;
244}
245
246bool CompositeViewer::checkNeedToDoFrame()
247{
248    if (_requestRedraw) return true;
249    if (_requestContinousUpdate) return true;
250
251    for(RefViews::iterator itr = _views.begin();
252        itr != _views.end();
253        ++itr)
254    {
255        osgViewer::View* view = itr->get();
256        if (view)
257        {
258            // If the database pager is going to update the scene the render flag is
259            // set so that the updates show up
260            if (view->getDatabasePager()->requiresUpdateSceneGraph() ||
261                view->getDatabasePager()->getRequestsInProgress()) return true;
262
263            // if there update callbacks then we need to do frame.
264            if (view->getCamera()->getUpdateCallback()) return true;
265            if (view->getSceneData()!=0 && view->getSceneData()->getNumChildrenRequiringUpdateTraversal()>0) return true;
266        }
267    }
268
269    // check if events are available and need processing
270    if (checkEvents()) return true;
271
272    if (_requestRedraw) return true;
273    if (_requestContinousUpdate) return true;
274
275    return false;
276}
277
278
279bool CompositeViewer::checkEvents()
280{
281    for(RefViews::iterator itr = _views.begin();
282        itr != _views.end();
283        ++itr)
284    {
285        osgViewer::View* view = itr->get();
286        if (view)
287        {
288            // check events from any attached sources
289            for(View::Devices::iterator eitr = view->getDevices().begin();
290                eitr != view->getDevices().end();
291                ++eitr)
292            {
293                osgGA::Device* es = eitr->get();
294                if (es->getCapabilities() & osgGA::Device::RECEIVE_EVENTS)
295                {
296                    if (es->checkEvents()) return true;
297                }
298
299            }
300        }
301    }
302
303    // get events from all windows attached to Viewer.
304    Windows windows;
305    getWindows(windows);
306    for(Windows::iterator witr = windows.begin();
307        witr != windows.end();
308        ++witr)
309    {
310        if ((*witr)->checkEvents()) return true;
311    }
312
313    return false;
314}
315
316int CompositeViewer::run()
317{
318    for(RefViews::iterator itr = _views.begin();
319        itr != _views.end();
320        ++itr)
321    {
322        osgViewer::View* view = itr->get();
323        if ((view->getCameraManipulator()==0) && view->getCamera()->getAllowEventFocus())
324        {
325            view->setCameraManipulator(new osgGA::TrackballManipulator());
326        }
327    }
328
329    setReleaseContextAtEndOfFrameHint(false);
330
331    return ViewerBase::run();
332}
333
334void CompositeViewer::setStartTick(osg::Timer_t tick)
335{
336    _startTick = tick;
337
338    for(RefViews::iterator vitr = _views.begin();
339        vitr != _views.end();
340        ++vitr)
341    {
342        (*vitr)->setStartTick(tick);
343    }
344
345    Contexts contexts;
346    getContexts(contexts,false);
347
348    for(Contexts::iterator citr = contexts.begin();
349        citr != contexts.end();
350        ++citr)
351    {
352        osgViewer::GraphicsWindow* gw = dynamic_cast<osgViewer::GraphicsWindow*>(*citr);
353        if (gw)
354        {
355            gw->getEventQueue()->setStartTick(_startTick);
356        }
357    }
358}
359
360
361void CompositeViewer::setReferenceTime(double time)
362{
363    osg::Timer_t tick = osg::Timer::instance()->tick();
364    double currentTime = osg::Timer::instance()->delta_s(_startTick, tick);
365    double delta_ticks = (time-currentTime)*(osg::Timer::instance()->getSecondsPerTick());
366    if (delta_ticks>=0) tick += osg::Timer_t(delta_ticks);
367    else tick -= osg::Timer_t(-delta_ticks);
368
369    // assign the new start tick
370    setStartTick(tick);
371}
372
373
374
375void CompositeViewer::viewerInit()
376{
377    OSG_INFO<<"CompositeViewer::init()"<<std::endl;
378
379    for(RefViews::iterator itr = _views.begin();
380        itr != _views.end();
381        ++itr)
382    {
383        (*itr)->init();
384    }
385}
386
387void CompositeViewer::getContexts(Contexts& contexts, bool onlyValid)
388{
389    typedef std::set<osg::GraphicsContext*> ContextSet;
390    ContextSet contextSet;
391
392    contexts.clear();
393
394    for(RefViews::iterator vitr = _views.begin();
395        vitr != _views.end();
396        ++vitr)
397    {
398        osgViewer::View* view = vitr->get();
399        osg::GraphicsContext* gc = view->getCamera() ? view->getCamera()->getGraphicsContext() : 0;
400        if (gc && (gc->valid() || !onlyValid))
401        {
402            if (contextSet.count(gc)==0)
403            {
404                contextSet.insert(gc);
405                contexts.push_back(gc);
406            }
407        }
408
409        for(unsigned int i=0; i<view->getNumSlaves(); ++i)
410        {
411            View::Slave& slave = view->getSlave(i);
412            osg::GraphicsContext* sgc = slave._camera.valid() ? slave._camera->getGraphicsContext() : 0;
413            if (sgc && (sgc->valid() || !onlyValid))
414            {
415                if (contextSet.count(sgc)==0)
416                {
417                    contextSet.insert(sgc);
418                    contexts.push_back(sgc);
419                }
420            }
421        }
422    }
423}
424
425void CompositeViewer::getCameras(Cameras& cameras, bool onlyActive)
426{
427    cameras.clear();
428
429    for(RefViews::iterator vitr = _views.begin();
430        vitr != _views.end();
431        ++vitr)
432    {
433        View* view = vitr->get();
434
435        if (view->getCamera() &&
436            (!onlyActive || (view->getCamera()->getGraphicsContext() && view->getCamera()->getGraphicsContext()->valid())) ) cameras.push_back(view->getCamera());
437
438        for(View::Slaves::iterator itr = view->_slaves.begin();
439            itr != view->_slaves.end();
440            ++itr)
441        {
442            if (itr->_camera.valid() &&
443                (!onlyActive || (itr->_camera->getGraphicsContext() && itr->_camera->getGraphicsContext()->valid())) ) cameras.push_back(itr->_camera.get());
444        }
445    }
446}
447
448void CompositeViewer::getScenes(Scenes& scenes, bool onlyValid)
449{
450    scenes.clear();
451
452    typedef std::set<osgViewer::Scene*> SceneSet;
453    SceneSet sceneSet;
454
455    for(RefViews::iterator vitr = _views.begin();
456        vitr != _views.end();
457        ++vitr)
458    {
459        osgViewer::View* view = vitr->get();
460        if (view->getScene() && (!onlyValid || view->getScene()->getSceneData()))
461        {
462            if (sceneSet.count(view->getScene())==0)
463            {
464                sceneSet.insert(view->getScene());
465                scenes.push_back(view->getScene());
466            }
467        }
468    }
469}
470
471void CompositeViewer::getViews(Views& views, bool /*onlyValid*/)
472{
473    views.clear();
474
475    for(RefViews::iterator vitr = _views.begin();
476        vitr != _views.end();
477        ++vitr)
478    {
479        views.push_back(vitr->get());
480    }
481}
482
483void CompositeViewer::getAllThreads(Threads& threads, bool onlyActive)
484{
485    threads.clear();
486
487    OperationThreads operationThreads;
488    getOperationThreads(operationThreads);
489
490    for(OperationThreads::iterator itr = operationThreads.begin();
491        itr != operationThreads.end();
492        ++itr)
493    {
494        threads.push_back(*itr);
495    }
496
497    Scenes scenes;
498    getScenes(scenes);
499
500    for(Scenes::iterator sitr = scenes.begin();
501        sitr != scenes.end();
502        ++sitr)
503    {
504        Scene* scene = *sitr;
505        osgDB::DatabasePager* dp = scene->getDatabasePager();
506        if (dp)
507        {
508            for(unsigned int i=0; i<dp->getNumDatabaseThreads(); ++i)
509            {
510                osgDB::DatabasePager::DatabaseThread* dt = dp->getDatabaseThread(i);
511                if (!onlyActive || dt->isRunning())
512                {
513                    threads.push_back(dt);
514                }
515            }
516        }
517    }
518}
519
520
521void CompositeViewer::getOperationThreads(OperationThreads& threads, bool onlyActive)
522{
523    threads.clear();
524
525    Contexts contexts;
526    getContexts(contexts);
527    for(Contexts::iterator gcitr = contexts.begin();
528        gcitr != contexts.end();
529        ++gcitr)
530    {
531        osg::GraphicsContext* gc = *gcitr;
532        if (gc->getGraphicsThread() &&
533            (!onlyActive || gc->getGraphicsThread()->isRunning()) )
534        {
535            threads.push_back(gc->getGraphicsThread());
536        }
537    }
538
539    Cameras cameras;
540    getCameras(cameras);
541    for(Cameras::iterator citr = cameras.begin();
542        citr != cameras.end();
543        ++citr)
544    {
545        osg::Camera* camera = *citr;
546        if (camera->getCameraThread() &&
547            (!onlyActive || camera->getCameraThread()->isRunning()) )
548        {
549            threads.push_back(camera->getCameraThread());
550        }
551    }
552
553}
554
555void CompositeViewer::realize()
556{
557    //OSG_INFO<<"CompositeViewer::realize()"<<std::endl;
558
559    setCameraWithFocus(0);
560
561    if (_views.empty())
562    {
563        OSG_NOTICE<<"CompositeViewer::realize() - No views to realize."<<std::endl;
564        _done = true;
565        return;
566    }
567
568    Contexts contexts;
569    getContexts(contexts);
570
571    if (contexts.empty())
572    {
573        OSG_INFO<<"CompositeViewer::realize() - No valid contexts found, setting up view across all screens."<<std::endl;
574
575        // no windows are already set up so set up a default view
576        _views[0]->setUpViewAcrossAllScreens();
577
578        getContexts(contexts);
579    }
580
581    if (contexts.empty())
582    {
583        OSG_NOTICE<<"CompositeViewer::realize() - failed to set up any windows"<<std::endl;
584        _done = true;
585        return;
586    }
587
588    // get the display settings that will be active for this viewer
589    osg::DisplaySettings* ds = osg::DisplaySettings::instance().get();
590    osg::GraphicsContext::WindowingSystemInterface* wsi = osg::GraphicsContext::getWindowingSystemInterface();
591
592    // pass on the display settings to the WindowSystemInterface.
593    if (wsi && wsi->getDisplaySettings()==0) wsi->setDisplaySettings(ds);
594
595    unsigned int maxTexturePoolSize = ds->getMaxTexturePoolSize();
596    unsigned int maxBufferObjectPoolSize = ds->getMaxBufferObjectPoolSize();
597
598    for(Contexts::iterator citr = contexts.begin();
599        citr != contexts.end();
600        ++citr)
601    {
602        osg::GraphicsContext* gc = *citr;
603
604        // set the pool sizes, 0 the default will result in no GL object pools.
605        gc->getState()->setMaxTexturePoolSize(maxTexturePoolSize);
606        gc->getState()->setMaxBufferObjectPoolSize(maxBufferObjectPoolSize);
607
608        gc->realize();
609
610        if (_realizeOperation.valid() && gc->valid())
611        {
612            gc->makeCurrent();
613
614            (*_realizeOperation)(gc);
615
616            gc->releaseContext();
617        }
618    }
619
620    // attach contexts to _incrementalCompileOperation if attached.
621    if (_incrementalCompileOperation) _incrementalCompileOperation->assignContexts(contexts);
622
623
624    bool grabFocus = true;
625    if (grabFocus)
626    {
627        for(Contexts::iterator citr = contexts.begin();
628            citr != contexts.end();
629            ++citr)
630        {
631            osgViewer::GraphicsWindow* gw = dynamic_cast<osgViewer::GraphicsWindow*>(*citr);
632            if (gw)
633            {
634                gw->grabFocusIfPointerInWindow();
635            }
636        }
637    }
638
639
640    startThreading();
641
642    // initialize the global timer to be relative to the current time.
643    osg::Timer::instance()->setStartTick();
644
645    // pass on the start tick to all the associated eventqueues
646    setStartTick(osg::Timer::instance()->getStartTick());
647
648    if (osg::DisplaySettings::instance()->getCompileContextsHint())
649    {
650        int numProcessors = OpenThreads::GetNumberOfProcessors();
651        int processNum = 0;
652
653        for(unsigned int i=0; i<= osg::GraphicsContext::getMaxContextID(); ++i)
654        {
655            osg::GraphicsContext* gc = osg::GraphicsContext::getOrCreateCompileContext(i);
656
657            if (gc)
658            {
659                gc->createGraphicsThread();
660                gc->getGraphicsThread()->setProcessorAffinity(processNum % numProcessors);
661                gc->getGraphicsThread()->startThread();
662
663                ++processNum;
664            }
665        }
666    }
667
668}
669
670void CompositeViewer::advance(double simulationTime)
671{
672    if (_done) return;
673
674    double previousReferenceTime = _frameStamp->getReferenceTime();
675    unsigned int previousFrameNumber = _frameStamp->getFrameNumber();
676
677
678    _frameStamp->setFrameNumber(_frameStamp->getFrameNumber()+1);
679
680    _frameStamp->setReferenceTime( osg::Timer::instance()->delta_s(_startTick, osg::Timer::instance()->tick()) );
681
682    if (simulationTime==USE_REFERENCE_TIME)
683    {
684        _frameStamp->setSimulationTime(_frameStamp->getReferenceTime());
685    }
686    else
687    {
688        _frameStamp->setSimulationTime(simulationTime);
689    }
690
691
692    if (getViewerStats() && getViewerStats()->collectStats("frame_rate"))
693    {
694        // update previous frame stats
695        double deltaFrameTime = _frameStamp->getReferenceTime() - previousReferenceTime;
696        getViewerStats()->setAttribute(previousFrameNumber, "Frame duration", deltaFrameTime);
697        getViewerStats()->setAttribute(previousFrameNumber, "Frame rate", 1.0/deltaFrameTime);
698
699        // update current frames stats
700        getViewerStats()->setAttribute(_frameStamp->getFrameNumber(), "Reference time", _frameStamp->getReferenceTime());
701    }
702
703}
704
705void CompositeViewer::setCameraWithFocus(osg::Camera* camera)
706{
707    _cameraWithFocus = camera;
708
709    if (camera)
710    {
711        for(RefViews::iterator vitr = _views.begin();
712            vitr != _views.end();
713            ++vitr)
714        {
715            View* view = vitr->get();
716            if (view->containsCamera(camera))
717            {
718                _viewWithFocus = view;
719                return;
720            }
721        }
722    }
723
724    _viewWithFocus = 0;
725}
726
727
728void CompositeViewer::generateSlavePointerData(osg::Camera* camera, osgGA::GUIEventAdapter& event)
729{
730    osgViewer::GraphicsWindow* gw = dynamic_cast<osgViewer::GraphicsWindow*>(event.getGraphicsContext());
731    if (!gw) return;
732
733    // What type of Camera is it?
734    // 1) Master Camera : do nothin extra
735    // 2) Slave Camera, Relative RF, Same scene graph as master : transform coords into Master Camera and add to PointerData list
736    // 3) Slave Camera, Relative RF, Different scene graph from master : do nothing extra?
737    // 4) Slave Camera, Absolute RF, Same scene graph as master : do nothing extra?
738    // 5) Slave Camera, Absolute RF, Different scene graph : do nothing extra?
739    // 6) Slave Camera, Absolute RF, Different scene graph but a distortion correction subgraph depending upon RTT Camera (slave or master)
740    //                              : project ray into RTT Camera's clip space, and RTT Camera's is Relative RF and sharing same scene graph as master then transform coords.
741
742    // if camera isn't the master it must be a slave and could need reprojecting.
743
744
745    osgViewer::View* view = dynamic_cast<osgViewer::View*>(camera->getView());
746    if (!view) return;
747
748    osg::Camera* view_masterCamera = view->getCamera();
749    if (camera!=view_masterCamera)
750    {
751        float x = event.getX();
752        float y = event.getY();
753
754        bool invert_y = event.getMouseYOrientation()==osgGA::GUIEventAdapter::Y_INCREASING_DOWNWARDS;
755        if (invert_y && gw->getTraits()) y = gw->getTraits()->height - y;
756
757        double master_min_x = -1.0;
758        double master_max_x = 1.0;
759        double master_min_y = -1.0;
760        double master_max_y = 1.0;
761
762        osg::Matrix masterCameraVPW = view_masterCamera->getViewMatrix() * view_masterCamera->getProjectionMatrix();
763        if (view_masterCamera->getViewport())
764        {
765            osg::Viewport* viewport = view_masterCamera->getViewport();
766            master_min_x = viewport->x();
767            master_min_y = viewport->y();
768            master_max_x = viewport->x()+viewport->width();
769            master_max_y = viewport->y()+viewport->height();
770            masterCameraVPW *= viewport->computeWindowMatrix();
771        }
772
773        // slave Camera tahnks to sharing the same View
774        osg::View::Slave* slave = view ? view->findSlaveForCamera(camera) : 0;
775        if (slave)
776        {
777            if (camera->getReferenceFrame()==osg::Camera::RELATIVE_RF && slave->_useMastersSceneData)
778            {
779                osg::Viewport* viewport = camera->getViewport();
780                osg::Matrix localCameraVPW = camera->getViewMatrix() * camera->getProjectionMatrix();
781                if (viewport) localCameraVPW *= viewport->computeWindowMatrix();
782
783                osg::Matrix matrix( osg::Matrix::inverse(localCameraVPW) * masterCameraVPW );
784                osg::Vec3d new_coord = osg::Vec3d(x,y,0.0) * matrix;
785                //OSG_NOTICE<<"    pointer event new_coord.x()="<<new_coord.x()<<" new_coord.y()="<<new_coord.y()<<std::endl;
786                event.addPointerData(new osgGA::PointerData(view_masterCamera, new_coord.x(), master_min_x, master_max_x,
787                                                                               new_coord.y(), master_min_y, master_max_y));
788            }
789            else if (!slave->_useMastersSceneData)
790            {
791                // Are their any RTT Camera's that this Camera depends upon for textures?
792
793                osg::ref_ptr<osgUtil::LineSegmentIntersector> ray = new osgUtil::LineSegmentIntersector(osgUtil::Intersector::WINDOW, x,y);
794                osgUtil::IntersectionVisitor iv(ray.get());
795                camera->accept(iv);
796                if (ray->containsIntersections())
797                {
798                    osg::Vec3 tc;
799                    osg::Texture* texture = ray->getFirstIntersection().getTextureLookUp(tc);
800                    if (texture)
801                    {
802                        // look up Texture in RTT Camera's.
803                        for(unsigned int i=0; i<view->getNumSlaves();++i)
804                        {
805                            osg::Camera* slave_camera = view->getSlave(i)._camera.get();
806                            if (slave_camera)
807                            {
808                                osg::Camera::BufferAttachmentMap::const_iterator ba_itr = slave_camera->getBufferAttachmentMap().find(osg::Camera::COLOR_BUFFER);
809                                if (ba_itr != slave_camera->getBufferAttachmentMap().end())
810                                {
811                                    if (ba_itr->second._texture == texture)
812                                    {
813                                        osg::TextureRectangle* tr = dynamic_cast<osg::TextureRectangle*>(ba_itr->second._texture.get());
814                                        osg::TextureCubeMap* tcm = dynamic_cast<osg::TextureCubeMap*>(ba_itr->second._texture.get());
815                                        if (tr)
816                                        {
817                                            event.addPointerData(new osgGA::PointerData(slave_camera, tc.x(), 0.0f, static_cast<float>(tr->getTextureWidth()),
818                                                                                                           tc.y(), 0.0f, static_cast<float>(tr->getTextureHeight())));
819                                        }
820                                        else if (tcm)
821                                        {
822                                            OSG_NOTICE<<"  Slave has matched texture cubemap"<<ba_itr->second._texture.get()<<", "<<ba_itr->second._face<<std::endl;
823                                        }
824                                        else
825                                        {
826                                            event.addPointerData(new osgGA::PointerData(slave_camera, tc.x(), 0.0f, 1.0f,
827                                                                                                           tc.y(), 0.0f, 1.0f));
828                                        }
829                                    }
830                                }
831                            }
832                        }
833                    }
834                }
835            }
836        }
837    }
838}
839
840
841void CompositeViewer::generatePointerData(osgGA::GUIEventAdapter& event)
842{
843    osgViewer::GraphicsWindow* gw = dynamic_cast<osgViewer::GraphicsWindow*>(event.getGraphicsContext());
844    if (!gw) return;
845
846    float x = event.getX();
847    float y = event.getY();
848
849    bool invert_y = event.getMouseYOrientation()==osgGA::GUIEventAdapter::Y_INCREASING_DOWNWARDS;
850    if (invert_y && gw->getTraits()) y = gw->getTraits()->height - y;
851
852    event.addPointerData(new osgGA::PointerData(gw, x, 0, gw->getTraits()->width,
853                                                    y, 0, gw->getTraits()->height));
854
855    osg::GraphicsContext::Cameras& cameras = gw->getCameras();
856    for(osg::GraphicsContext::Cameras::iterator citr = cameras.begin();
857        citr != cameras.end();
858        ++citr)
859    {
860        osg::Camera* camera = *citr;
861        if (camera->getAllowEventFocus() &&
862            camera->getRenderTargetImplementation()==osg::Camera::FRAME_BUFFER)
863        {
864            osg::Viewport* viewport = camera ? camera->getViewport() : 0;
865            if (viewport &&
866                x >= viewport->x() && y >= viewport->y() &&
867                x <= (viewport->x()+viewport->width()) && y <= (viewport->y()+viewport->height()) )
868            {
869                event.addPointerData(new osgGA::PointerData(camera, (x-viewport->x())/viewport->width()*2.0f-1.0f, -1.0, 1.0,
870                                                                    (y-viewport->y())/viewport->height()*2.0f-1.0f, -1.0, 1.0));
871
872                osgViewer::View* view = dynamic_cast<osgViewer::View*>(camera->getView());
873                osg::Camera* view_masterCamera = view ? view->getCamera() : 0;
874
875                // if camera isn't the master it must be a slave and could need reprojecting.
876                if (view && camera!=view_masterCamera)
877                {
878                    generateSlavePointerData(camera, event);
879                }
880            }
881        }
882    }
883}
884
885void CompositeViewer::reprojectPointerData(osgGA::GUIEventAdapter& source_event, osgGA::GUIEventAdapter& dest_event)
886{
887    osgViewer::GraphicsWindow* gw = dynamic_cast<osgViewer::GraphicsWindow*>(dest_event.getGraphicsContext());
888    if (!gw) return;
889
890    float x = dest_event.getX();
891    float y = dest_event.getY();
892
893    bool invert_y = dest_event.getMouseYOrientation()==osgGA::GUIEventAdapter::Y_INCREASING_DOWNWARDS;
894    if (invert_y && gw->getTraits()) y = gw->getTraits()->height - y;
895
896    dest_event.addPointerData(new osgGA::PointerData(gw, x, 0, gw->getTraits()->width,
897                                                         y, 0, gw->getTraits()->height));
898
899    osg::Camera* camera = (source_event.getNumPointerData()>=2) ? dynamic_cast<osg::Camera*>(source_event.getPointerData(1)->object.get()) : 0;
900    osg::Viewport* viewport = camera ? camera->getViewport() : 0;
901
902    if (!viewport) return;
903
904    dest_event.addPointerData(new osgGA::PointerData(camera, (x-viewport->x())/viewport->width()*2.0f-1.0f, -1.0, 1.0,
905                                                             (y-viewport->y())/viewport->height()*2.0f-1.0f, -1.0, 1.0));
906
907    osgViewer::View* view = dynamic_cast<osgViewer::View*>(camera->getView());
908    osg::Camera* view_masterCamera = view ? view->getCamera() : 0;
909
910    // if camera isn't the master it must be a slave and could need reprojecting.
911    if (view && camera!=view_masterCamera)
912    {
913        generateSlavePointerData(camera, dest_event);
914    }
915}
916
917struct SortEvents
918{
919    bool operator() (const osg::ref_ptr<osgGA::Event>& lhs,const osg::ref_ptr<osgGA::Event>& rhs) const
920    {
921        return lhs->getTime() < rhs->getTime();
922    }
923};
924
925void CompositeViewer::eventTraversal()
926{
927    if (_done) return;
928
929    if (_views.empty()) return;
930
931    double cutOffTime = _frameStamp->getReferenceTime();
932
933    double beginEventTraversal = osg::Timer::instance()->delta_s(_startTick, osg::Timer::instance()->tick());
934
935    // need to copy events from the GraphicsWindow's into local EventQueue for each view;
936    typedef std::map<osgViewer::View*, osgGA::EventQueue::Events> ViewEventsMap;
937    ViewEventsMap viewEventsMap;
938
939    Contexts contexts;
940    getContexts(contexts);
941
942    // set done if there are no windows
943    checkWindowStatus(contexts);
944    if (_done) return;
945
946    osgGA::EventQueue::Events all_events;
947
948    for(Contexts::iterator citr = contexts.begin();
949        citr != contexts.end();
950        ++citr)
951    {
952        osgViewer::GraphicsWindow* gw = dynamic_cast<osgViewer::GraphicsWindow*>(*citr);
953        if (gw)
954        {
955            gw->checkEvents();
956
957            osgGA::EventQueue::Events gw_events;
958            gw->getEventQueue()->takeEvents(gw_events, cutOffTime);
959
960            for(osgGA::EventQueue::Events::iterator itr = gw_events.begin();
961                itr != gw_events.end();
962                ++itr)
963            {
964                osgGA::GUIEventAdapter* ea = (*itr)->asGUIEventAdapter();
965                if (ea) ea->setGraphicsContext(gw);
966            }
967
968            all_events.insert(all_events.end(), gw_events.begin(), gw_events.end());
969        }
970    }
971
972    // sort all the events in time order so we can make sure we pass them all on in the correct order.
973    all_events.sort(SortEvents());
974
975    // pass on pointer data onto non mouse events to keep the position data usable by all recipients of all events.
976    for(osgGA::EventQueue::Events::iterator itr = all_events.begin();
977        itr != all_events.end();
978        ++itr)
979    {
980        osgGA::GUIEventAdapter* event = (*itr)->asGUIEventAdapter();
981        if (!event) continue;
982
983        switch(event->getEventType())
984        {
985            case(osgGA::GUIEventAdapter::PUSH):
986            case(osgGA::GUIEventAdapter::RELEASE):
987            case(osgGA::GUIEventAdapter::DOUBLECLICK):
988            case(osgGA::GUIEventAdapter::MOVE):
989            case(osgGA::GUIEventAdapter::DRAG):
990            {
991                if ((event->getEventType()!=osgGA::GUIEventAdapter::DRAG && event->getEventType()!=osgGA::GUIEventAdapter::RELEASE) ||
992                    !_previousEvent ||
993                    _previousEvent->getGraphicsContext()!=event->getGraphicsContext() ||
994                    _previousEvent->getNumPointerData()<2)
995                {
996                    generatePointerData(*event);
997                }
998                else
999                {
1000                    reprojectPointerData(*_previousEvent, *event);
1001                }
1002
1003#if 0
1004                // assign topmost PointeData settings as the events X,Y and InputRange
1005                osgGA::PointerData* pd = event->getPointerData(event->getNumPointerData()-1);
1006                event->setX(pd->x);
1007                event->setY(pd->y);
1008                event->setInputRange(pd->xMin, pd->yMin, pd->xMax, pd->yMax);
1009                event->setMouseYOrientation(osgGA::GUIEventAdapter::Y_INCREASING_UPWARDS);
1010#else
1011                if (event->getMouseYOrientation()!=osgGA::GUIEventAdapter::Y_INCREASING_UPWARDS)
1012                {
1013                    event->setY((event->getYmax()-event->getY())+event->getYmin());
1014                    event->setMouseYOrientation(osgGA::GUIEventAdapter::Y_INCREASING_UPWARDS);
1015                }
1016#endif
1017
1018                _previousEvent = event;
1019
1020                break;
1021            }
1022            default:
1023                if (_previousEvent.valid()) event->copyPointerDataFrom(*_previousEvent);
1024                break;
1025        }
1026
1027        osgGA::PointerData* pd = event->getNumPointerData()>0 ? event->getPointerData(event->getNumPointerData()-1) : 0;
1028        osg::Camera* camera = pd ? dynamic_cast<osg::Camera*>(pd->object.get()) : 0;
1029        osgViewer::View* view = camera ? dynamic_cast<osgViewer::View*>(camera->getView()) : 0;
1030
1031        if (!view)
1032        {
1033            if (_viewWithFocus.valid())
1034            {
1035                // OSG_NOTICE<<"Falling back to using _viewWithFocus"<<std::endl;
1036                view = _viewWithFocus.get();
1037            }
1038            else if (!_views.empty())
1039            {
1040                // OSG_NOTICE<<"Falling back to using first view as one with focus"<<std::endl;
1041                view = _views[0].get();
1042            }
1043        }
1044
1045        // reassign view with focus
1046        if (_viewWithFocus != view)  _viewWithFocus = view;
1047
1048        if (view)
1049        {
1050            viewEventsMap[view].push_back( event );
1051
1052            osgGA::GUIEventAdapter* eventState = view->getEventQueue()->getCurrentEventState();
1053            eventState->copyPointerDataFrom(*event);
1054        }
1055
1056        _previousEvent = event;
1057    }
1058
1059    // handle any close windows
1060    for(osgGA::EventQueue::Events::iterator itr = all_events.begin();
1061        itr != all_events.end();
1062        ++itr)
1063    {
1064        osgGA::GUIEventAdapter* event = (*itr)->asGUIEventAdapter();
1065        if (!event) continue;
1066
1067        switch(event->getEventType())
1068        {
1069            case(osgGA::GUIEventAdapter::CLOSE_WINDOW):
1070            {
1071                bool wasThreading = areThreadsRunning();
1072                if (wasThreading) stopThreading();
1073
1074                if (event->getGraphicsContext())
1075                {
1076                    event->getGraphicsContext()->close();
1077                }
1078
1079                if (wasThreading) startThreading();
1080
1081                break;
1082            }
1083            default:
1084                break;
1085        }
1086    }
1087
1088
1089    for(RefViews::iterator vitr = _views.begin();
1090        vitr != _views.end();
1091        ++vitr)
1092    {
1093        View* view = vitr->get();
1094
1095        // get events from user Devices attached to Viewer.
1096        for(osgViewer::View::Devices::iterator eitr = view->getDevices().begin();
1097            eitr != view->getDevices().end();
1098            ++eitr)
1099        {
1100            osgGA::Device* es = eitr->get();
1101            if (es->getCapabilities() & osgGA::Device::RECEIVE_EVENTS)
1102                es->checkEvents();
1103
1104            // open question, will we need to reproject mouse coordinates into current view's coordinate frame as is down for GraphicsWindow provided events?
1105            // for now assume now and just get the events directly without any reprojection.
1106            es->getEventQueue()->takeEvents(viewEventsMap[view], cutOffTime);
1107        }
1108
1109        // generate frame event
1110        view->getEventQueue()->frame( getFrameStamp()->getReferenceTime() );
1111
1112        view->getEventQueue()->takeEvents(viewEventsMap[view], cutOffTime);
1113    }
1114
1115    if ((_keyEventSetsDone!=0) || _quitEventSetsDone)
1116    {
1117        for(ViewEventsMap::iterator veitr = viewEventsMap.begin();
1118            veitr != viewEventsMap.end();
1119            ++veitr)
1120        {
1121            for(osgGA::EventQueue::Events::iterator itr = veitr->second.begin();
1122                itr != veitr->second.end();
1123                ++itr)
1124            {
1125                osgGA::GUIEventAdapter* event = (*itr)->asGUIEventAdapter();
1126                if (!event) continue;
1127                switch(event->getEventType())
1128                {
1129                    case(osgGA::GUIEventAdapter::KEYUP):
1130                        if (_keyEventSetsDone && event->getKey()==_keyEventSetsDone) _done = true;
1131                        break;
1132
1133                    case(osgGA::GUIEventAdapter::QUIT_APPLICATION):
1134                        if (_quitEventSetsDone) _done = true;
1135                        break;
1136
1137                    default:
1138                        break;
1139                }
1140            }
1141        }
1142    }
1143
1144    if (_done) return;
1145
1146    if (_eventVisitor.valid())
1147    {
1148        _eventVisitor->setFrameStamp(getFrameStamp());
1149        _eventVisitor->setTraversalNumber(getFrameStamp()->getFrameNumber());
1150
1151        for(ViewEventsMap::iterator veitr = viewEventsMap.begin();
1152            veitr != viewEventsMap.end();
1153            ++veitr)
1154        {
1155            View* view = veitr->first;
1156            _eventVisitor->setActionAdapter(view);
1157
1158            if (view && view->getSceneData())
1159            {
1160                for(osgGA::EventQueue::Events::iterator itr = veitr->second.begin();
1161                    itr != veitr->second.end();
1162                    ++itr)
1163                {
1164                    osgGA::Event* event = itr->get();
1165
1166                    _eventVisitor->reset();
1167                    _eventVisitor->addEvent( event );
1168
1169                    view->getSceneData()->accept(*_eventVisitor);
1170
1171                    // Do EventTraversal for slaves with their own subgraph
1172                    for(unsigned int i=0; i<view->getNumSlaves(); ++i)
1173                    {
1174                        osg::View::Slave& slave = view->getSlave(i);
1175                        osg::Camera* camera = slave._camera.get();
1176                        if(camera && !slave._useMastersSceneData)
1177                        {
1178                            camera->accept(*_eventVisitor);
1179                        }
1180                    }
1181
1182                    // call any camera event callbacks, but only traverse that callback, don't traverse its subgraph
1183                    // leave that to the scene update traversal.
1184                    osg::NodeVisitor::TraversalMode tm = _eventVisitor->getTraversalMode();
1185                    _eventVisitor->setTraversalMode(osg::NodeVisitor::TRAVERSE_NONE);
1186
1187                    if (view->getCamera() && view->getCamera()->getEventCallback()) view->getCamera()->accept(*_eventVisitor);
1188
1189                    for(unsigned int i=0; i<view->getNumSlaves(); ++i)
1190                    {
1191                        osg::View::Slave& slave = view->getSlave(i);
1192                        osg::Camera* camera = view->getSlave(i)._camera.get();
1193                        if (camera && slave._useMastersSceneData && camera->getEventCallback())
1194                        {
1195                            camera->accept(*_eventVisitor);
1196                        }
1197                    }
1198
1199                    _eventVisitor->setTraversalMode(tm);
1200
1201                }
1202            }
1203        }
1204
1205    }
1206
1207    for(ViewEventsMap::iterator veitr = viewEventsMap.begin();
1208        veitr != viewEventsMap.end();
1209        ++veitr)
1210    {
1211        View* view = veitr->first;
1212        _eventVisitor->setActionAdapter(view);
1213
1214        for(osgGA::EventQueue::Events::iterator itr = veitr->second.begin();
1215            itr != veitr->second.end();
1216            ++itr)
1217        {
1218            osgGA::Event* event = itr->get();
1219            for(View::EventHandlers::iterator hitr = view->getEventHandlers().begin();
1220                hitr != view->getEventHandlers().end();
1221                ++hitr)
1222            {
1223                (*hitr)->handle( event, view, _eventVisitor.get());
1224            }
1225        }
1226    }
1227
1228    for(ViewEventsMap::iterator veitr = viewEventsMap.begin();
1229        veitr != viewEventsMap.end();
1230        ++veitr)
1231    {
1232        View* view = veitr->first;
1233        _eventVisitor->setActionAdapter(view);
1234
1235        for(osgGA::EventQueue::Events::iterator itr = veitr->second.begin();
1236            itr != veitr->second.end();
1237            ++itr)
1238        {
1239            osgGA::Event* event = itr->get();
1240            if (view->getCameraManipulator())
1241            {
1242                view->getCameraManipulator()->handle( event, view, _eventVisitor.get());
1243            }
1244        }
1245    }
1246
1247    if (getViewerStats() && getViewerStats()->collectStats("event"))
1248    {
1249        double endEventTraversal = osg::Timer::instance()->delta_s(_startTick, osg::Timer::instance()->tick());
1250
1251        // update current frames stats
1252        getViewerStats()->setAttribute(_frameStamp->getFrameNumber(), "Event traversal begin time", beginEventTraversal);
1253        getViewerStats()->setAttribute(_frameStamp->getFrameNumber(), "Event traversal end time", endEventTraversal);
1254        getViewerStats()->setAttribute(_frameStamp->getFrameNumber(), "Event traversal time taken", endEventTraversal-beginEventTraversal);
1255    }
1256}
1257
1258void CompositeViewer::updateTraversal()
1259{
1260    if (_done) return;
1261
1262    double beginUpdateTraversal = osg::Timer::instance()->delta_s(_startTick, osg::Timer::instance()->tick());
1263
1264    _updateVisitor->reset();
1265    _updateVisitor->setFrameStamp(getFrameStamp());
1266    _updateVisitor->setTraversalNumber(getFrameStamp()->getFrameNumber());
1267
1268    Scenes scenes;
1269    getScenes(scenes);
1270    for(Scenes::iterator sitr = scenes.begin();
1271        sitr != scenes.end();
1272        ++sitr)
1273    {
1274        Scene* scene = *sitr;
1275        scene->updateSceneGraph(*_updateVisitor);
1276    }
1277
1278    // if we have a shared state manager prune any unused entries
1279    if (osgDB::Registry::instance()->getSharedStateManager())
1280        osgDB::Registry::instance()->getSharedStateManager()->prune();
1281
1282    // update the Registry object cache.
1283    osgDB::Registry::instance()->updateTimeStampOfObjectsInCacheWithExternalReferences(*getFrameStamp());
1284    osgDB::Registry::instance()->removeExpiredObjectsInCache(*getFrameStamp());
1285
1286
1287    if (_incrementalCompileOperation.valid())
1288    {
1289        // merge subgraphs that have been compiled by the incremental compiler operation.
1290        _incrementalCompileOperation->mergeCompiledSubgraphs(getFrameStamp());
1291    }
1292
1293    if (_updateOperations.valid())
1294    {
1295        _updateOperations->runOperations(this);
1296    }
1297
1298    for(RefViews::iterator vitr = _views.begin();
1299        vitr != _views.end();
1300        ++vitr)
1301    {
1302        View* view = vitr->get();
1303
1304        {
1305            // Do UpdateTraversal for slaves with their own subgraph
1306            for(unsigned int i=0; i<view->getNumSlaves(); ++i)
1307            {
1308                osg::View::Slave& slave = view->getSlave(i);
1309                osg::Camera* camera = slave._camera.get();
1310                if(camera && !slave._useMastersSceneData)
1311                {
1312                    camera->accept(*_updateVisitor);
1313                }
1314            }
1315
1316            // call any camera update callbacks, but only traverse that callback, don't traverse its subgraph
1317            // leave that to the scene update traversal.
1318            osg::NodeVisitor::TraversalMode tm = _updateVisitor->getTraversalMode();
1319            _updateVisitor->setTraversalMode(osg::NodeVisitor::TRAVERSE_NONE);
1320
1321            if (view->getCamera() && view->getCamera()->getUpdateCallback()) view->getCamera()->accept(*_updateVisitor);
1322
1323            for(unsigned int i=0; i<view->getNumSlaves(); ++i)
1324            {
1325                osg::View::Slave& slave = view->getSlave(i);
1326                osg::Camera* camera = slave._camera.get();
1327                if (camera && slave._useMastersSceneData && camera->getUpdateCallback())
1328                {
1329                    camera->accept(*_updateVisitor);
1330                }
1331            }
1332
1333            _updateVisitor->setTraversalMode(tm);
1334        }
1335
1336
1337        if (view->getCameraManipulator())
1338        {
1339            view->setFusionDistance( view->getCameraManipulator()->getFusionDistanceMode(),
1340                                    view->getCameraManipulator()->getFusionDistanceValue() );
1341
1342            view->getCameraManipulator()->updateCamera(*(view->getCamera()));
1343
1344        }
1345        view->updateSlaves();
1346
1347    }
1348
1349    if (getViewerStats() && getViewerStats()->collectStats("update"))
1350    {
1351        double endUpdateTraversal = osg::Timer::instance()->delta_s(_startTick, osg::Timer::instance()->tick());
1352
1353        // update current frames stats
1354        getViewerStats()->setAttribute(_frameStamp->getFrameNumber(), "Update traversal begin time", beginUpdateTraversal);
1355        getViewerStats()->setAttribute(_frameStamp->getFrameNumber(), "Update traversal end time", endUpdateTraversal);
1356        getViewerStats()->setAttribute(_frameStamp->getFrameNumber(), "Update traversal time taken", endUpdateTraversal-beginUpdateTraversal);
1357    }
1358
1359}
1360
1361double CompositeViewer::elapsedTime()
1362{
1363    return osg::Timer::instance()->delta_s(_startTick, osg::Timer::instance()->tick());
1364}
1365
1366void CompositeViewer::getUsage(osg::ApplicationUsage& usage) const
1367{
1368    for(RefViews::const_iterator vitr = _views.begin();
1369        vitr != _views.end();
1370        ++vitr)
1371    {
1372        const View* view = vitr->get();
1373        if (view->getCameraManipulator())
1374        {
1375            view->getCameraManipulator()->getUsage(usage);
1376        }
1377
1378        for(View::EventHandlers::const_iterator hitr = view->_eventHandlers.begin();
1379            hitr != view->_eventHandlers.end();
1380            ++hitr)
1381        {
1382            (*hitr)->getUsage(usage);
1383        }
1384    }
1385}
Note: See TracBrowser for help on using the browser.