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

Revision 13890, 46.2 kB (checked in by robert, 2 days ago)

Added shaders to support experimental shader based displacement mapping technique osgTerrain::ShaderTerrain?.

  • 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.