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

Revision 13429, 45.6 kB (checked in by robert, 26 minutes ago)

Moved osgTerrain::GeometryPool? from osgterrain example into osgTerrain NodeKit?

  • 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    unsigned int maxTexturePoolSize = osg::DisplaySettings::instance()->getMaxTexturePoolSize();
589    unsigned int maxBufferObjectPoolSize = osg::DisplaySettings::instance()->getMaxBufferObjectPoolSize();
590
591    for(Contexts::iterator citr = contexts.begin();
592        citr != contexts.end();
593        ++citr)
594    {
595        osg::GraphicsContext* gc = *citr;
596
597        // set the pool sizes, 0 the default will result in no GL object pools.
598        gc->getState()->setMaxTexturePoolSize(maxTexturePoolSize);
599        gc->getState()->setMaxBufferObjectPoolSize(maxBufferObjectPoolSize);
600
601        gc->realize();
602
603        if (_realizeOperation.valid() && gc->valid())
604        {
605            gc->makeCurrent();
606
607            (*_realizeOperation)(gc);
608
609            gc->releaseContext();
610        }
611    }
612
613    // attach contexts to _incrementalCompileOperation if attached.
614    if (_incrementalCompileOperation) _incrementalCompileOperation->assignContexts(contexts);
615
616
617    bool grabFocus = true;
618    if (grabFocus)
619    {
620        for(Contexts::iterator citr = contexts.begin();
621            citr != contexts.end();
622            ++citr)
623        {
624            osgViewer::GraphicsWindow* gw = dynamic_cast<osgViewer::GraphicsWindow*>(*citr);
625            if (gw)
626            {
627                gw->grabFocusIfPointerInWindow();
628            }
629        }
630    }
631
632
633    startThreading();
634
635    // initialize the global timer to be relative to the current time.
636    osg::Timer::instance()->setStartTick();
637
638    // pass on the start tick to all the associated eventqueues
639    setStartTick(osg::Timer::instance()->getStartTick());
640
641    if (osg::DisplaySettings::instance()->getCompileContextsHint())
642    {
643        int numProcessors = OpenThreads::GetNumberOfProcessors();
644        int processNum = 0;
645
646        for(unsigned int i=0; i<= osg::GraphicsContext::getMaxContextID(); ++i)
647        {
648            osg::GraphicsContext* gc = osg::GraphicsContext::getOrCreateCompileContext(i);
649
650            if (gc)
651            {
652                gc->createGraphicsThread();
653                gc->getGraphicsThread()->setProcessorAffinity(processNum % numProcessors);
654                gc->getGraphicsThread()->startThread();
655
656                ++processNum;
657            }
658        }
659    }
660
661}
662
663void CompositeViewer::advance(double simulationTime)
664{
665    if (_done) return;
666
667    double previousReferenceTime = _frameStamp->getReferenceTime();
668    unsigned int previousFrameNumber = _frameStamp->getFrameNumber();
669
670
671    _frameStamp->setFrameNumber(_frameStamp->getFrameNumber()+1);
672
673    _frameStamp->setReferenceTime( osg::Timer::instance()->delta_s(_startTick, osg::Timer::instance()->tick()) );
674
675    if (simulationTime==USE_REFERENCE_TIME)
676    {
677        _frameStamp->setSimulationTime(_frameStamp->getReferenceTime());
678    }
679    else
680    {
681        _frameStamp->setSimulationTime(simulationTime);
682    }
683
684
685    if (getViewerStats() && getViewerStats()->collectStats("frame_rate"))
686    {
687        // update previous frame stats
688        double deltaFrameTime = _frameStamp->getReferenceTime() - previousReferenceTime;
689        getViewerStats()->setAttribute(previousFrameNumber, "Frame duration", deltaFrameTime);
690        getViewerStats()->setAttribute(previousFrameNumber, "Frame rate", 1.0/deltaFrameTime);
691
692        // update current frames stats
693        getViewerStats()->setAttribute(_frameStamp->getFrameNumber(), "Reference time", _frameStamp->getReferenceTime());
694    }
695
696}
697
698void CompositeViewer::setCameraWithFocus(osg::Camera* camera)
699{
700    _cameraWithFocus = camera;
701
702    if (camera)
703    {
704        for(RefViews::iterator vitr = _views.begin();
705            vitr != _views.end();
706            ++vitr)
707        {
708            View* view = vitr->get();
709            if (view->containsCamera(camera))
710            {
711                _viewWithFocus = view;
712                return;
713            }
714        }
715    }
716
717    _viewWithFocus = 0;
718}
719
720
721void CompositeViewer::generateSlavePointerData(osg::Camera* camera, osgGA::GUIEventAdapter& event)
722{
723    osgViewer::GraphicsWindow* gw = dynamic_cast<osgViewer::GraphicsWindow*>(event.getGraphicsContext());
724    if (!gw) return;
725
726    // What type of Camera is it?
727    // 1) Master Camera : do nothin extra
728    // 2) Slave Camera, Relative RF, Same scene graph as master : transform coords into Master Camera and add to PointerData list
729    // 3) Slave Camera, Relative RF, Different scene graph from master : do nothing extra?
730    // 4) Slave Camera, Absolute RF, Same scene graph as master : do nothing extra?
731    // 5) Slave Camera, Absolute RF, Different scene graph : do nothing extra?
732    // 6) Slave Camera, Absolute RF, Different scene graph but a distortion correction subgraph depending upon RTT Camera (slave or master)
733    //                              : 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.
734
735    // if camera isn't the master it must be a slave and could need reprojecting.
736   
737   
738    osgViewer::View* view = dynamic_cast<osgViewer::View*>(camera->getView());
739    if (!view) return;
740   
741    osg::Camera* view_masterCamera = view->getCamera();
742    if (camera!=view_masterCamera)
743    {
744        float x = event.getX();
745        float y = event.getY();
746
747        bool invert_y = event.getMouseYOrientation()==osgGA::GUIEventAdapter::Y_INCREASING_DOWNWARDS;
748        if (invert_y && gw->getTraits()) y = gw->getTraits()->height - y;
749
750        osg::Matrix masterCameraVPW = view_masterCamera->getViewMatrix() * view_masterCamera->getProjectionMatrix();
751        if (view_masterCamera->getViewport())
752        {
753            osg::Viewport* viewport = view_masterCamera->getViewport();
754            masterCameraVPW *= viewport->computeWindowMatrix();
755        }
756
757        // slave Camera tahnks to sharing the same View
758        osg::View::Slave* slave = view ? view->findSlaveForCamera(camera) : 0;
759        if (slave)
760        {
761            if (camera->getReferenceFrame()==osg::Camera::RELATIVE_RF && slave->_useMastersSceneData)
762            {
763                osg::Viewport* viewport = camera->getViewport();
764                osg::Matrix localCameraVPW = camera->getViewMatrix() * camera->getProjectionMatrix();
765                if (viewport) localCameraVPW *= viewport->computeWindowMatrix();
766
767                osg::Matrix matrix( osg::Matrix::inverse(localCameraVPW) * masterCameraVPW );
768                osg::Vec3d new_coord = osg::Vec3d(x,y,0.0) * matrix;
769                //OSG_NOTICE<<"    pointer event new_coord.x()="<<new_coord.x()<<" new_coord.y()="<<new_coord.y()<<std::endl;
770                event.addPointerData(new osgGA::PointerData(view_masterCamera, new_coord.x(), -1.0, 1.0,
771                                                                               new_coord.y(), -1.0, 1.0));
772            }
773            else if (!slave->_useMastersSceneData)
774            {
775                // Are their any RTT Camera's that this Camera depends upon for textures?
776
777                osg::ref_ptr<osgUtil::LineSegmentIntersector> ray = new osgUtil::LineSegmentIntersector(osgUtil::Intersector::WINDOW, x,y);
778                osgUtil::IntersectionVisitor iv(ray.get());
779                camera->accept(iv);
780                if (ray->containsIntersections())
781                {
782                    osg::Vec3 tc;
783                    osg::Texture* texture = ray->getFirstIntersection().getTextureLookUp(tc);
784                    if (texture)
785                    {
786                        // look up Texture in RTT Camera's.
787                        for(unsigned int i=0; i<view->getNumSlaves();++i)
788                        {
789                            osg::Camera* slave_camera = view->getSlave(i)._camera.get();
790                            if (slave_camera)
791                            {
792                                osg::Camera::BufferAttachmentMap::const_iterator ba_itr = slave_camera->getBufferAttachmentMap().find(osg::Camera::COLOR_BUFFER);
793                                if (ba_itr != slave_camera->getBufferAttachmentMap().end())
794                                {
795                                    if (ba_itr->second._texture == texture)
796                                    {
797                                        osg::TextureRectangle* tr = dynamic_cast<osg::TextureRectangle*>(ba_itr->second._texture.get());
798                                        osg::TextureCubeMap* tcm = dynamic_cast<osg::TextureCubeMap*>(ba_itr->second._texture.get());
799                                        if (tr)
800                                        {
801                                            event.addPointerData(new osgGA::PointerData(slave_camera, tc.x(), 0.0f, static_cast<float>(tr->getTextureWidth()),
802                                                                                                           tc.y(), 0.0f, static_cast<float>(tr->getTextureHeight())));
803                                        }
804                                        else if (tcm)
805                                        {
806                                            OSG_NOTICE<<"  Slave has matched texture cubemap"<<ba_itr->second._texture.get()<<", "<<ba_itr->second._face<<std::endl;
807                                        }
808                                        else
809                                        {
810                                            event.addPointerData(new osgGA::PointerData(slave_camera, tc.x(), 0.0f, 1.0f,
811                                                                                                           tc.y(), 0.0f, 1.0f));
812                                        }
813                                    }
814                                }
815                            }
816                        }
817                    }
818                }
819            }
820        }
821    }
822}
823   
824   
825void CompositeViewer::generatePointerData(osgGA::GUIEventAdapter& event)
826{
827    osgViewer::GraphicsWindow* gw = dynamic_cast<osgViewer::GraphicsWindow*>(event.getGraphicsContext());
828    if (!gw) return;
829
830    float x = event.getX();
831    float y = event.getY();
832
833    bool invert_y = event.getMouseYOrientation()==osgGA::GUIEventAdapter::Y_INCREASING_DOWNWARDS;
834    if (invert_y && gw->getTraits()) y = gw->getTraits()->height - y;
835   
836    event.addPointerData(new osgGA::PointerData(gw, x, 0, gw->getTraits()->width,
837                                                    y, 0, gw->getTraits()->height));
838
839    osg::GraphicsContext::Cameras& cameras = gw->getCameras();
840    for(osg::GraphicsContext::Cameras::iterator citr = cameras.begin();
841        citr != cameras.end();
842        ++citr)
843    {
844        osg::Camera* camera = *citr;
845        if (camera->getAllowEventFocus() &&
846            camera->getRenderTargetImplementation()==osg::Camera::FRAME_BUFFER)
847        {
848            osg::Viewport* viewport = camera ? camera->getViewport() : 0;
849            if (viewport &&
850                x >= viewport->x() && y >= viewport->y() &&
851                x <= (viewport->x()+viewport->width()) && y <= (viewport->y()+viewport->height()) )
852            {
853                event.addPointerData(new osgGA::PointerData(camera, (x-viewport->x())/viewport->width()*2.0f-1.0f, -1.0, 1.0,
854                                                                    (y-viewport->y())/viewport->height()*2.0f-1.0f, -1.0, 1.0));
855
856                osgViewer::View* view = dynamic_cast<osgViewer::View*>(camera->getView());
857                osg::Camera* view_masterCamera = view ? view->getCamera() : 0;
858
859                // if camera isn't the master it must be a slave and could need reprojecting.
860                if (view && camera!=view_masterCamera)
861                {
862                    generateSlavePointerData(camera, event);
863                }
864            }
865        }
866    }
867}
868
869void CompositeViewer::reprojectPointerData(osgGA::GUIEventAdapter& source_event, osgGA::GUIEventAdapter& dest_event)
870{
871    osgViewer::GraphicsWindow* gw = dynamic_cast<osgViewer::GraphicsWindow*>(dest_event.getGraphicsContext());
872    if (!gw) return;
873
874    float x = dest_event.getX();
875    float y = dest_event.getY();
876
877    bool invert_y = dest_event.getMouseYOrientation()==osgGA::GUIEventAdapter::Y_INCREASING_DOWNWARDS;
878    if (invert_y && gw->getTraits()) y = gw->getTraits()->height - y;
879
880    dest_event.addPointerData(new osgGA::PointerData(gw, x, 0, gw->getTraits()->width,
881                                                         y, 0, gw->getTraits()->height));
882
883    osg::Camera* camera = (source_event.getNumPointerData()>=2) ? dynamic_cast<osg::Camera*>(source_event.getPointerData(1)->object.get()) : 0;
884    osg::Viewport* viewport = camera ? camera->getViewport() : 0;
885
886    if (!viewport) return;
887   
888    dest_event.addPointerData(new osgGA::PointerData(camera, (x-viewport->x())/viewport->width()*2.0f-1.0f, -1.0, 1.0,
889                                                             (y-viewport->y())/viewport->height()*2.0f-1.0f, -1.0, 1.0));
890
891    osgViewer::View* view = dynamic_cast<osgViewer::View*>(camera->getView());
892    osg::Camera* view_masterCamera = view ? view->getCamera() : 0;
893
894    // if camera isn't the master it must be a slave and could need reprojecting.
895    if (view && camera!=view_masterCamera)
896    {
897        generateSlavePointerData(camera, dest_event);
898    }
899}
900
901struct SortEvents
902{
903    bool operator() (const osg::ref_ptr<osgGA::GUIEventAdapter>& lhs,const osg::ref_ptr<osgGA::GUIEventAdapter>& rhs) const
904    {
905        return lhs->getTime() < rhs->getTime();
906    }
907};
908
909void CompositeViewer::eventTraversal()
910{
911    if (_done) return;
912
913    if (_views.empty()) return;
914
915#if 1
916    double cutOffTime = _frameStamp->getReferenceTime();
917#else   
918    double cutOffTime = (_runFrameScheme==ON_DEMAND) ? DBL_MAX : _frameStamp->getReferenceTime();
919#endif
920
921    double beginEventTraversal = osg::Timer::instance()->delta_s(_startTick, osg::Timer::instance()->tick());
922
923    // need to copy events from the GraphicsWindow's into local EventQueue for each view;
924    typedef std::map<osgViewer::View*, osgGA::EventQueue::Events> ViewEventsMap;
925    ViewEventsMap viewEventsMap;
926
927    Contexts contexts;
928    getContexts(contexts);
929
930    // set done if there are no windows
931    checkWindowStatus(contexts);
932    if (_done) return;
933   
934    osgGA::EventQueue::Events all_events;
935   
936    for(Contexts::iterator citr = contexts.begin();
937        citr != contexts.end();
938        ++citr)
939    {
940        osgViewer::GraphicsWindow* gw = dynamic_cast<osgViewer::GraphicsWindow*>(*citr);
941        if (gw)
942        {
943            gw->checkEvents();
944
945            osgGA::EventQueue::Events gw_events;
946            gw->getEventQueue()->takeEvents(gw_events, cutOffTime);
947
948            for(osgGA::EventQueue::Events::iterator itr = gw_events.begin();
949                itr != gw_events.end();
950                ++itr)
951            {
952                (*itr)->setGraphicsContext(gw);
953            }
954
955            all_events.insert(all_events.end(), gw_events.begin(), gw_events.end());
956        }
957    }
958
959    // sort all the events in time order so we can make sure we pass them all on in the correct order.
960    all_events.sort(SortEvents());
961   
962    // pass on pointer data onto non mouse events to keep the position data usable by all recipients of all events.
963    for(osgGA::EventQueue::Events::iterator itr = all_events.begin();
964        itr != all_events.end();
965        ++itr)
966    {
967        osgGA::GUIEventAdapter* event = itr->get();
968
969        switch(event->getEventType())
970        {
971            case(osgGA::GUIEventAdapter::PUSH):
972            case(osgGA::GUIEventAdapter::RELEASE):
973            case(osgGA::GUIEventAdapter::DOUBLECLICK):
974            case(osgGA::GUIEventAdapter::MOVE):
975            case(osgGA::GUIEventAdapter::DRAG):
976            {
977                if ((event->getEventType()!=osgGA::GUIEventAdapter::DRAG && event->getEventType()!=osgGA::GUIEventAdapter::RELEASE) ||
978                    !_previousEvent ||
979                    _previousEvent->getGraphicsContext()!=event->getGraphicsContext() ||
980                    _previousEvent->getNumPointerData()<2)
981                {
982                    generatePointerData(*event);
983                }
984                else
985                {
986                    reprojectPointerData(*_previousEvent, *event);
987                }
988
989#if 0               
990                // assign topmost PointeData settings as the events X,Y and InputRange
991                osgGA::PointerData* pd = event->getPointerData(event->getNumPointerData()-1);
992                event->setX(pd->x);
993                event->setY(pd->y);
994                event->setInputRange(pd->xMin, pd->yMin, pd->xMax, pd->yMax);
995                event->setMouseYOrientation(osgGA::GUIEventAdapter::Y_INCREASING_UPWARDS);
996#else               
997                if (event->getMouseYOrientation()!=osgGA::GUIEventAdapter::Y_INCREASING_UPWARDS)
998                {
999                    event->setY((event->getYmax()-event->getY())+event->getYmin());
1000                    event->setMouseYOrientation(osgGA::GUIEventAdapter::Y_INCREASING_UPWARDS);
1001                }
1002#endif               
1003
1004                _previousEvent = event;
1005               
1006                break;
1007            }
1008            default:
1009                if (_previousEvent.valid()) event->copyPointerDataFrom(*_previousEvent);
1010                break;
1011        }
1012
1013        osgGA::PointerData* pd = event->getNumPointerData()>0 ? event->getPointerData(event->getNumPointerData()-1) : 0;
1014        osg::Camera* camera = pd ? dynamic_cast<osg::Camera*>(pd->object.get()) : 0;
1015        osgViewer::View* view = camera ? dynamic_cast<osgViewer::View*>(camera->getView()) : 0;
1016       
1017        if (!view)
1018        {
1019            if (_viewWithFocus.valid())
1020            {
1021                // OSG_NOTICE<<"Falling back to using _viewWithFocus"<<std::endl;
1022                view = _viewWithFocus.get();
1023            }
1024            else if (!_views.empty())
1025            {
1026                // OSG_NOTICE<<"Falling back to using first view as one with focus"<<std::endl;
1027                view = _views[0].get();
1028            }
1029        }
1030       
1031        // reassign view with focus
1032        if (_viewWithFocus != view)  _viewWithFocus = view;
1033
1034        if (view)
1035        {
1036            viewEventsMap[view].push_back( event );
1037           
1038            osgGA::GUIEventAdapter* eventState = view->getEventQueue()->getCurrentEventState();
1039            eventState->copyPointerDataFrom(*event);
1040        }
1041
1042        _previousEvent = event;
1043    }
1044
1045    // handle any close windows
1046    for(osgGA::EventQueue::Events::iterator itr = all_events.begin();
1047        itr != all_events.end();
1048        ++itr)
1049    {
1050        osgGA::GUIEventAdapter* event = itr->get();
1051        switch(event->getEventType())
1052        {
1053            case(osgGA::GUIEventAdapter::CLOSE_WINDOW):
1054            {
1055                bool wasThreading = areThreadsRunning();
1056                if (wasThreading) stopThreading();
1057
1058                if (event->getGraphicsContext())
1059                {
1060                    event->getGraphicsContext()->close();
1061                }
1062
1063                if (wasThreading) startThreading();
1064
1065                break;
1066            }
1067            default:
1068                break;
1069        }
1070    }
1071       
1072
1073    for(RefViews::iterator vitr = _views.begin();
1074        vitr != _views.end();
1075        ++vitr)
1076    {
1077        View* view = vitr->get();
1078
1079        // get events from user Devices attached to Viewer.
1080        for(osgViewer::View::Devices::iterator eitr = view->getDevices().begin();
1081            eitr != view->getDevices().end();
1082            ++eitr)
1083        {
1084            osgGA::Device* es = eitr->get();
1085            if (es->getCapabilities() & osgGA::Device::RECEIVE_EVENTS)
1086                es->checkEvents();
1087
1088            // open question, will we need to reproject mouse coordinates into current view's coordinate frame as is down for GraphicsWindow provided events?
1089            // for now assume now and just get the events directly without any reprojection.
1090            es->getEventQueue()->takeEvents(viewEventsMap[view], cutOffTime);
1091        }
1092
1093        // generate frame event
1094        view->getEventQueue()->frame( getFrameStamp()->getReferenceTime() );
1095
1096        view->getEventQueue()->takeEvents(viewEventsMap[view], cutOffTime);
1097    }
1098
1099    if ((_keyEventSetsDone!=0) || _quitEventSetsDone)
1100    {
1101        for(ViewEventsMap::iterator veitr = viewEventsMap.begin();
1102            veitr != viewEventsMap.end();
1103            ++veitr)
1104        {
1105            for(osgGA::EventQueue::Events::iterator itr = veitr->second.begin();
1106                itr != veitr->second.end();
1107                ++itr)
1108            {
1109                osgGA::GUIEventAdapter* event = itr->get();
1110                switch(event->getEventType())
1111                {
1112                    case(osgGA::GUIEventAdapter::KEYUP):
1113                        if (_keyEventSetsDone && event->getKey()==_keyEventSetsDone) _done = true;
1114                        break;
1115
1116                    case(osgGA::GUIEventAdapter::QUIT_APPLICATION):
1117                        if (_quitEventSetsDone) _done = true;
1118                        break;
1119
1120                    default:
1121                        break;
1122                }
1123            }
1124        }
1125    }
1126
1127    if (_done) return;
1128
1129    if (_eventVisitor.valid())
1130    {
1131        _eventVisitor->setFrameStamp(getFrameStamp());
1132        _eventVisitor->setTraversalNumber(getFrameStamp()->getFrameNumber());
1133
1134        for(ViewEventsMap::iterator veitr = viewEventsMap.begin();
1135            veitr != viewEventsMap.end();
1136            ++veitr)
1137        {
1138            View* view = veitr->first;
1139
1140            if (view && view->getSceneData())
1141            {
1142                _eventVisitor->setActionAdapter(view);
1143
1144                for(osgGA::EventQueue::Events::iterator itr = veitr->second.begin();
1145                    itr != veitr->second.end();
1146                    ++itr)
1147                {
1148                    osgGA::GUIEventAdapter* event = itr->get();
1149
1150                    _eventVisitor->reset();
1151                    _eventVisitor->addEvent( event );
1152
1153                    view->getSceneData()->accept(*_eventVisitor);
1154
1155                    // Do EventTraversal for slaves with their own subgraph
1156                    for(unsigned int i=0; i<view->getNumSlaves(); ++i)
1157                    {
1158                        osg::View::Slave& slave = view->getSlave(i);
1159                        osg::Camera* camera = slave._camera.get();
1160                        if(camera && !slave._useMastersSceneData)
1161                        {
1162                            camera->accept(*_eventVisitor);
1163                        }
1164                    }
1165
1166                    // call any camera event callbacks, but only traverse that callback, don't traverse its subgraph
1167                    // leave that to the scene update traversal.
1168                    osg::NodeVisitor::TraversalMode tm = _eventVisitor->getTraversalMode();
1169                    _eventVisitor->setTraversalMode(osg::NodeVisitor::TRAVERSE_NONE);
1170
1171                    if (view->getCamera() && view->getCamera()->getEventCallback()) view->getCamera()->accept(*_eventVisitor);
1172
1173                    for(unsigned int i=0; i<view->getNumSlaves(); ++i)
1174                    {
1175                        osg::View::Slave& slave = view->getSlave(i);
1176                        osg::Camera* camera = view->getSlave(i)._camera.get();
1177                        if (camera && slave._useMastersSceneData && camera->getEventCallback())
1178                        {
1179                            camera->accept(*_eventVisitor);
1180                        }
1181                    }
1182
1183                    _eventVisitor->setTraversalMode(tm);
1184
1185                }
1186            }
1187        }
1188
1189    }
1190
1191    for(ViewEventsMap::iterator veitr = viewEventsMap.begin();
1192        veitr != viewEventsMap.end();
1193        ++veitr)
1194    {
1195        View* view = veitr->first;
1196
1197        for(osgGA::EventQueue::Events::iterator itr = veitr->second.begin();
1198            itr != veitr->second.end();
1199            ++itr)
1200        {
1201            osgGA::GUIEventAdapter* event = itr->get();
1202
1203            for(View::EventHandlers::iterator hitr = view->getEventHandlers().begin();
1204                hitr != view->getEventHandlers().end();
1205                ++hitr)
1206            {
1207                (*hitr)->handleWithCheckAgainstIgnoreHandledEventsMask( *event, *view, 0, _eventVisitor.get());
1208            }
1209        }
1210    }
1211
1212    for(ViewEventsMap::iterator veitr = viewEventsMap.begin();
1213        veitr != viewEventsMap.end();
1214        ++veitr)
1215    {
1216        View* view = veitr->first;
1217
1218        for(osgGA::EventQueue::Events::iterator itr = veitr->second.begin();
1219            itr != veitr->second.end();
1220            ++itr)
1221        {
1222            osgGA::GUIEventAdapter* event = itr->get();
1223
1224            if (view->getCameraManipulator())
1225            {
1226                view->getCameraManipulator()->handleWithCheckAgainstIgnoreHandledEventsMask( *event, *view);
1227            }
1228        }
1229    }
1230
1231    if (getViewerStats() && getViewerStats()->collectStats("event"))
1232    {
1233        double endEventTraversal = osg::Timer::instance()->delta_s(_startTick, osg::Timer::instance()->tick());
1234
1235        // update current frames stats
1236        getViewerStats()->setAttribute(_frameStamp->getFrameNumber(), "Event traversal begin time", beginEventTraversal);
1237        getViewerStats()->setAttribute(_frameStamp->getFrameNumber(), "Event traversal end time", endEventTraversal);
1238        getViewerStats()->setAttribute(_frameStamp->getFrameNumber(), "Event traversal time taken", endEventTraversal-beginEventTraversal);
1239    }
1240}
1241
1242void CompositeViewer::updateTraversal()
1243{
1244    if (_done) return;
1245
1246    double beginUpdateTraversal = osg::Timer::instance()->delta_s(_startTick, osg::Timer::instance()->tick());
1247
1248    _updateVisitor->reset();
1249    _updateVisitor->setFrameStamp(getFrameStamp());
1250    _updateVisitor->setTraversalNumber(getFrameStamp()->getFrameNumber());
1251
1252    Scenes scenes;
1253    getScenes(scenes);
1254    for(Scenes::iterator sitr = scenes.begin();
1255        sitr != scenes.end();
1256        ++sitr)
1257    {
1258        Scene* scene = *sitr;
1259        scene->updateSceneGraph(*_updateVisitor);
1260    }
1261
1262    // if we have a shared state manager prune any unused entries
1263    if (osgDB::Registry::instance()->getSharedStateManager())
1264        osgDB::Registry::instance()->getSharedStateManager()->prune();
1265
1266    // update the Registry object cache.
1267    osgDB::Registry::instance()->updateTimeStampOfObjectsInCacheWithExternalReferences(*getFrameStamp());
1268    osgDB::Registry::instance()->removeExpiredObjectsInCache(*getFrameStamp());
1269
1270
1271    if (_incrementalCompileOperation.valid())
1272    {
1273        // merge subgraphs that have been compiled by the incremental compiler operation.
1274        _incrementalCompileOperation->mergeCompiledSubgraphs(getFrameStamp());
1275    }
1276
1277    if (_updateOperations.valid())
1278    {
1279        _updateOperations->runOperations(this);
1280    }
1281
1282    for(RefViews::iterator vitr = _views.begin();
1283        vitr != _views.end();
1284        ++vitr)
1285    {
1286        View* view = vitr->get();
1287
1288        {
1289            // Do UpdateTraversal for slaves with their own subgraph
1290            for(unsigned int i=0; i<view->getNumSlaves(); ++i)
1291            {
1292                osg::View::Slave& slave = view->getSlave(i);
1293                osg::Camera* camera = slave._camera.get();
1294                if(camera && !slave._useMastersSceneData)
1295                {
1296                    camera->accept(*_updateVisitor);
1297                }
1298            }
1299
1300            // call any camera update callbacks, but only traverse that callback, don't traverse its subgraph
1301            // leave that to the scene update traversal.
1302            osg::NodeVisitor::TraversalMode tm = _updateVisitor->getTraversalMode();
1303            _updateVisitor->setTraversalMode(osg::NodeVisitor::TRAVERSE_NONE);
1304
1305            if (view->getCamera() && view->getCamera()->getUpdateCallback()) view->getCamera()->accept(*_updateVisitor);
1306
1307            for(unsigned int i=0; i<view->getNumSlaves(); ++i)
1308            {
1309                osg::View::Slave& slave = view->getSlave(i);
1310                osg::Camera* camera = slave._camera.get();
1311                if (camera && slave._useMastersSceneData && camera->getUpdateCallback())
1312                {
1313                    camera->accept(*_updateVisitor);
1314                }
1315            }
1316
1317            _updateVisitor->setTraversalMode(tm);
1318        }
1319
1320
1321        if (view->getCameraManipulator())
1322        {
1323            view->setFusionDistance( view->getCameraManipulator()->getFusionDistanceMode(),
1324                                    view->getCameraManipulator()->getFusionDistanceValue() );
1325           
1326            view->getCameraManipulator()->updateCamera(*(view->getCamera()));
1327           
1328        }
1329        view->updateSlaves();
1330
1331    }
1332
1333    if (getViewerStats() && getViewerStats()->collectStats("update"))
1334    {
1335        double endUpdateTraversal = osg::Timer::instance()->delta_s(_startTick, osg::Timer::instance()->tick());
1336
1337        // update current frames stats
1338        getViewerStats()->setAttribute(_frameStamp->getFrameNumber(), "Update traversal begin time", beginUpdateTraversal);
1339        getViewerStats()->setAttribute(_frameStamp->getFrameNumber(), "Update traversal end time", endUpdateTraversal);
1340        getViewerStats()->setAttribute(_frameStamp->getFrameNumber(), "Update traversal time taken", endUpdateTraversal-beginUpdateTraversal);
1341    }
1342
1343}
1344
1345double CompositeViewer::elapsedTime()
1346{
1347    return osg::Timer::instance()->delta_s(_startTick, osg::Timer::instance()->tick());
1348}
1349
1350void CompositeViewer::getUsage(osg::ApplicationUsage& usage) const
1351{
1352    for(RefViews::const_iterator vitr = _views.begin();
1353        vitr != _views.end();
1354        ++vitr)
1355    {
1356        const View* view = vitr->get();
1357        if (view->getCameraManipulator())
1358        {
1359            view->getCameraManipulator()->getUsage(usage);
1360        }
1361
1362        for(View::EventHandlers::const_iterator hitr = view->_eventHandlers.begin();
1363            hitr != view->_eventHandlers.end();
1364            ++hitr)
1365        {
1366            (*hitr)->getUsage(usage);
1367        }
1368    }
1369}
Note: See TracBrowser for help on using the browser.