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

Revision 13876, 46.0 kB (checked in by robert, 3 hours ago)

Added write support

  • 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::GUIEventAdapter>& lhs,const osg::ref_ptr<osgGA::GUIEventAdapter>& 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                (*itr)->setGraphicsContext(gw);
965            }
966
967            all_events.insert(all_events.end(), gw_events.begin(), gw_events.end());
968        }
969    }
970
971    // sort all the events in time order so we can make sure we pass them all on in the correct order.
972    all_events.sort(SortEvents());
973
974    // pass on pointer data onto non mouse events to keep the position data usable by all recipients of all events.
975    for(osgGA::EventQueue::Events::iterator itr = all_events.begin();
976        itr != all_events.end();
977        ++itr)
978    {
979        osgGA::GUIEventAdapter* event = itr->get();
980
981        switch(event->getEventType())
982        {
983            case(osgGA::GUIEventAdapter::PUSH):
984            case(osgGA::GUIEventAdapter::RELEASE):
985            case(osgGA::GUIEventAdapter::DOUBLECLICK):
986            case(osgGA::GUIEventAdapter::MOVE):
987            case(osgGA::GUIEventAdapter::DRAG):
988            {
989                if ((event->getEventType()!=osgGA::GUIEventAdapter::DRAG && event->getEventType()!=osgGA::GUIEventAdapter::RELEASE) ||
990                    !_previousEvent ||
991                    _previousEvent->getGraphicsContext()!=event->getGraphicsContext() ||
992                    _previousEvent->getNumPointerData()<2)
993                {
994                    generatePointerData(*event);
995                }
996                else
997                {
998                    reprojectPointerData(*_previousEvent, *event);
999                }
1000
1001#if 0
1002                // assign topmost PointeData settings as the events X,Y and InputRange
1003                osgGA::PointerData* pd = event->getPointerData(event->getNumPointerData()-1);
1004                event->setX(pd->x);
1005                event->setY(pd->y);
1006                event->setInputRange(pd->xMin, pd->yMin, pd->xMax, pd->yMax);
1007                event->setMouseYOrientation(osgGA::GUIEventAdapter::Y_INCREASING_UPWARDS);
1008#else
1009                if (event->getMouseYOrientation()!=osgGA::GUIEventAdapter::Y_INCREASING_UPWARDS)
1010                {
1011                    event->setY((event->getYmax()-event->getY())+event->getYmin());
1012                    event->setMouseYOrientation(osgGA::GUIEventAdapter::Y_INCREASING_UPWARDS);
1013                }
1014#endif
1015
1016                _previousEvent = event;
1017
1018                break;
1019            }
1020            default:
1021                if (_previousEvent.valid()) event->copyPointerDataFrom(*_previousEvent);
1022                break;
1023        }
1024
1025        osgGA::PointerData* pd = event->getNumPointerData()>0 ? event->getPointerData(event->getNumPointerData()-1) : 0;
1026        osg::Camera* camera = pd ? dynamic_cast<osg::Camera*>(pd->object.get()) : 0;
1027        osgViewer::View* view = camera ? dynamic_cast<osgViewer::View*>(camera->getView()) : 0;
1028
1029        if (!view)
1030        {
1031            if (_viewWithFocus.valid())
1032            {
1033                // OSG_NOTICE<<"Falling back to using _viewWithFocus"<<std::endl;
1034                view = _viewWithFocus.get();
1035            }
1036            else if (!_views.empty())
1037            {
1038                // OSG_NOTICE<<"Falling back to using first view as one with focus"<<std::endl;
1039                view = _views[0].get();
1040            }
1041        }
1042
1043        // reassign view with focus
1044        if (_viewWithFocus != view)  _viewWithFocus = view;
1045
1046        if (view)
1047        {
1048            viewEventsMap[view].push_back( event );
1049
1050            osgGA::GUIEventAdapter* eventState = view->getEventQueue()->getCurrentEventState();
1051            eventState->copyPointerDataFrom(*event);
1052        }
1053
1054        _previousEvent = event;
1055    }
1056
1057    // handle any close windows
1058    for(osgGA::EventQueue::Events::iterator itr = all_events.begin();
1059        itr != all_events.end();
1060        ++itr)
1061    {
1062        osgGA::GUIEventAdapter* event = itr->get();
1063        switch(event->getEventType())
1064        {
1065            case(osgGA::GUIEventAdapter::CLOSE_WINDOW):
1066            {
1067                bool wasThreading = areThreadsRunning();
1068                if (wasThreading) stopThreading();
1069
1070                if (event->getGraphicsContext())
1071                {
1072                    event->getGraphicsContext()->close();
1073                }
1074
1075                if (wasThreading) startThreading();
1076
1077                break;
1078            }
1079            default:
1080                break;
1081        }
1082    }
1083
1084
1085    for(RefViews::iterator vitr = _views.begin();
1086        vitr != _views.end();
1087        ++vitr)
1088    {
1089        View* view = vitr->get();
1090
1091        // get events from user Devices attached to Viewer.
1092        for(osgViewer::View::Devices::iterator eitr = view->getDevices().begin();
1093            eitr != view->getDevices().end();
1094            ++eitr)
1095        {
1096            osgGA::Device* es = eitr->get();
1097            if (es->getCapabilities() & osgGA::Device::RECEIVE_EVENTS)
1098                es->checkEvents();
1099
1100            // open question, will we need to reproject mouse coordinates into current view's coordinate frame as is down for GraphicsWindow provided events?
1101            // for now assume now and just get the events directly without any reprojection.
1102            es->getEventQueue()->takeEvents(viewEventsMap[view], cutOffTime);
1103        }
1104
1105        // generate frame event
1106        view->getEventQueue()->frame( getFrameStamp()->getReferenceTime() );
1107
1108        view->getEventQueue()->takeEvents(viewEventsMap[view], cutOffTime);
1109    }
1110
1111    if ((_keyEventSetsDone!=0) || _quitEventSetsDone)
1112    {
1113        for(ViewEventsMap::iterator veitr = viewEventsMap.begin();
1114            veitr != viewEventsMap.end();
1115            ++veitr)
1116        {
1117            for(osgGA::EventQueue::Events::iterator itr = veitr->second.begin();
1118                itr != veitr->second.end();
1119                ++itr)
1120            {
1121                osgGA::GUIEventAdapter* event = itr->get();
1122                switch(event->getEventType())
1123                {
1124                    case(osgGA::GUIEventAdapter::KEYUP):
1125                        if (_keyEventSetsDone && event->getKey()==_keyEventSetsDone) _done = true;
1126                        break;
1127
1128                    case(osgGA::GUIEventAdapter::QUIT_APPLICATION):
1129                        if (_quitEventSetsDone) _done = true;
1130                        break;
1131
1132                    default:
1133                        break;
1134                }
1135            }
1136        }
1137    }
1138
1139    if (_done) return;
1140
1141    if (_eventVisitor.valid())
1142    {
1143        _eventVisitor->setFrameStamp(getFrameStamp());
1144        _eventVisitor->setTraversalNumber(getFrameStamp()->getFrameNumber());
1145
1146        for(ViewEventsMap::iterator veitr = viewEventsMap.begin();
1147            veitr != viewEventsMap.end();
1148            ++veitr)
1149        {
1150            View* view = veitr->first;
1151
1152            if (view && view->getSceneData())
1153            {
1154                _eventVisitor->setActionAdapter(view);
1155
1156                for(osgGA::EventQueue::Events::iterator itr = veitr->second.begin();
1157                    itr != veitr->second.end();
1158                    ++itr)
1159                {
1160                    osgGA::GUIEventAdapter* event = itr->get();
1161
1162                    _eventVisitor->reset();
1163                    _eventVisitor->addEvent( event );
1164
1165                    view->getSceneData()->accept(*_eventVisitor);
1166
1167                    // Do EventTraversal for slaves with their own subgraph
1168                    for(unsigned int i=0; i<view->getNumSlaves(); ++i)
1169                    {
1170                        osg::View::Slave& slave = view->getSlave(i);
1171                        osg::Camera* camera = slave._camera.get();
1172                        if(camera && !slave._useMastersSceneData)
1173                        {
1174                            camera->accept(*_eventVisitor);
1175                        }
1176                    }
1177
1178                    // call any camera event callbacks, but only traverse that callback, don't traverse its subgraph
1179                    // leave that to the scene update traversal.
1180                    osg::NodeVisitor::TraversalMode tm = _eventVisitor->getTraversalMode();
1181                    _eventVisitor->setTraversalMode(osg::NodeVisitor::TRAVERSE_NONE);
1182
1183                    if (view->getCamera() && view->getCamera()->getEventCallback()) view->getCamera()->accept(*_eventVisitor);
1184
1185                    for(unsigned int i=0; i<view->getNumSlaves(); ++i)
1186                    {
1187                        osg::View::Slave& slave = view->getSlave(i);
1188                        osg::Camera* camera = view->getSlave(i)._camera.get();
1189                        if (camera && slave._useMastersSceneData && camera->getEventCallback())
1190                        {
1191                            camera->accept(*_eventVisitor);
1192                        }
1193                    }
1194
1195                    _eventVisitor->setTraversalMode(tm);
1196
1197                }
1198            }
1199        }
1200
1201    }
1202
1203    for(ViewEventsMap::iterator veitr = viewEventsMap.begin();
1204        veitr != viewEventsMap.end();
1205        ++veitr)
1206    {
1207        View* view = veitr->first;
1208
1209        for(osgGA::EventQueue::Events::iterator itr = veitr->second.begin();
1210            itr != veitr->second.end();
1211            ++itr)
1212        {
1213            osgGA::GUIEventAdapter* event = itr->get();
1214
1215            for(View::EventHandlers::iterator hitr = view->getEventHandlers().begin();
1216                hitr != view->getEventHandlers().end();
1217                ++hitr)
1218            {
1219                (*hitr)->handleWithCheckAgainstIgnoreHandledEventsMask( *event, *view, 0, _eventVisitor.get());
1220            }
1221        }
1222    }
1223
1224    for(ViewEventsMap::iterator veitr = viewEventsMap.begin();
1225        veitr != viewEventsMap.end();
1226        ++veitr)
1227    {
1228        View* view = veitr->first;
1229
1230        for(osgGA::EventQueue::Events::iterator itr = veitr->second.begin();
1231            itr != veitr->second.end();
1232            ++itr)
1233        {
1234            osgGA::GUIEventAdapter* event = itr->get();
1235
1236            if (view->getCameraManipulator())
1237            {
1238                view->getCameraManipulator()->handleWithCheckAgainstIgnoreHandledEventsMask( *event, *view);
1239            }
1240        }
1241    }
1242
1243    if (getViewerStats() && getViewerStats()->collectStats("event"))
1244    {
1245        double endEventTraversal = osg::Timer::instance()->delta_s(_startTick, osg::Timer::instance()->tick());
1246
1247        // update current frames stats
1248        getViewerStats()->setAttribute(_frameStamp->getFrameNumber(), "Event traversal begin time", beginEventTraversal);
1249        getViewerStats()->setAttribute(_frameStamp->getFrameNumber(), "Event traversal end time", endEventTraversal);
1250        getViewerStats()->setAttribute(_frameStamp->getFrameNumber(), "Event traversal time taken", endEventTraversal-beginEventTraversal);
1251    }
1252}
1253
1254void CompositeViewer::updateTraversal()
1255{
1256    if (_done) return;
1257
1258    double beginUpdateTraversal = osg::Timer::instance()->delta_s(_startTick, osg::Timer::instance()->tick());
1259
1260    _updateVisitor->reset();
1261    _updateVisitor->setFrameStamp(getFrameStamp());
1262    _updateVisitor->setTraversalNumber(getFrameStamp()->getFrameNumber());
1263
1264    Scenes scenes;
1265    getScenes(scenes);
1266    for(Scenes::iterator sitr = scenes.begin();
1267        sitr != scenes.end();
1268        ++sitr)
1269    {
1270        Scene* scene = *sitr;
1271        scene->updateSceneGraph(*_updateVisitor);
1272    }
1273
1274    // if we have a shared state manager prune any unused entries
1275    if (osgDB::Registry::instance()->getSharedStateManager())
1276        osgDB::Registry::instance()->getSharedStateManager()->prune();
1277
1278    // update the Registry object cache.
1279    osgDB::Registry::instance()->updateTimeStampOfObjectsInCacheWithExternalReferences(*getFrameStamp());
1280    osgDB::Registry::instance()->removeExpiredObjectsInCache(*getFrameStamp());
1281
1282
1283    if (_incrementalCompileOperation.valid())
1284    {
1285        // merge subgraphs that have been compiled by the incremental compiler operation.
1286        _incrementalCompileOperation->mergeCompiledSubgraphs(getFrameStamp());
1287    }
1288
1289    if (_updateOperations.valid())
1290    {
1291        _updateOperations->runOperations(this);
1292    }
1293
1294    for(RefViews::iterator vitr = _views.begin();
1295        vitr != _views.end();
1296        ++vitr)
1297    {
1298        View* view = vitr->get();
1299
1300        {
1301            // Do UpdateTraversal for slaves with their own subgraph
1302            for(unsigned int i=0; i<view->getNumSlaves(); ++i)
1303            {
1304                osg::View::Slave& slave = view->getSlave(i);
1305                osg::Camera* camera = slave._camera.get();
1306                if(camera && !slave._useMastersSceneData)
1307                {
1308                    camera->accept(*_updateVisitor);
1309                }
1310            }
1311
1312            // call any camera update callbacks, but only traverse that callback, don't traverse its subgraph
1313            // leave that to the scene update traversal.
1314            osg::NodeVisitor::TraversalMode tm = _updateVisitor->getTraversalMode();
1315            _updateVisitor->setTraversalMode(osg::NodeVisitor::TRAVERSE_NONE);
1316
1317            if (view->getCamera() && view->getCamera()->getUpdateCallback()) view->getCamera()->accept(*_updateVisitor);
1318
1319            for(unsigned int i=0; i<view->getNumSlaves(); ++i)
1320            {
1321                osg::View::Slave& slave = view->getSlave(i);
1322                osg::Camera* camera = slave._camera.get();
1323                if (camera && slave._useMastersSceneData && camera->getUpdateCallback())
1324                {
1325                    camera->accept(*_updateVisitor);
1326                }
1327            }
1328
1329            _updateVisitor->setTraversalMode(tm);
1330        }
1331
1332
1333        if (view->getCameraManipulator())
1334        {
1335            view->setFusionDistance( view->getCameraManipulator()->getFusionDistanceMode(),
1336                                    view->getCameraManipulator()->getFusionDistanceValue() );
1337
1338            view->getCameraManipulator()->updateCamera(*(view->getCamera()));
1339
1340        }
1341        view->updateSlaves();
1342
1343    }
1344
1345    if (getViewerStats() && getViewerStats()->collectStats("update"))
1346    {
1347        double endUpdateTraversal = osg::Timer::instance()->delta_s(_startTick, osg::Timer::instance()->tick());
1348
1349        // update current frames stats
1350        getViewerStats()->setAttribute(_frameStamp->getFrameNumber(), "Update traversal begin time", beginUpdateTraversal);
1351        getViewerStats()->setAttribute(_frameStamp->getFrameNumber(), "Update traversal end time", endUpdateTraversal);
1352        getViewerStats()->setAttribute(_frameStamp->getFrameNumber(), "Update traversal time taken", endUpdateTraversal-beginUpdateTraversal);
1353    }
1354
1355}
1356
1357double CompositeViewer::elapsedTime()
1358{
1359    return osg::Timer::instance()->delta_s(_startTick, osg::Timer::instance()->tick());
1360}
1361
1362void CompositeViewer::getUsage(osg::ApplicationUsage& usage) const
1363{
1364    for(RefViews::const_iterator vitr = _views.begin();
1365        vitr != _views.end();
1366        ++vitr)
1367    {
1368        const View* view = vitr->get();
1369        if (view->getCameraManipulator())
1370        {
1371            view->getCameraManipulator()->getUsage(usage);
1372        }
1373
1374        for(View::EventHandlers::const_iterator hitr = view->_eventHandlers.begin();
1375            hitr != view->_eventHandlers.end();
1376            ++hitr)
1377        {
1378            (*hitr)->getUsage(usage);
1379        }
1380    }
1381}
Note: See TracBrowser for help on using the browser.