]> git.mxchange.org Git - flightgear.git/blobdiff - src/Main/renderer.cxx
properly add librt when clock_gettime is used
[flightgear.git] / src / Main / renderer.cxx
index 0cb00c5ab63d8228368f513d0f60cf3d62011812..25ece71de3820f40cf61ffe0007bfe891809a250 100644 (file)
 
 #include <simgear/compiler.h>
 
+#include <algorithm>
+#include <iostream>
+#include <map>
+#include <vector>
+#include <typeinfo>
+
 #include <osg/ref_ptr>
 #include <osg/AlphaFunc>
 #include <osg/BlendFunc>
 #include <osg/Camera>
 #include <osg/CullFace>
+#include <osg/CullStack>
 #include <osg/Depth>
 #include <osg/Fog>
 #include <osg/Group>
 #include <simgear/screen/jpgfactory.hxx>
 #endif
 
-#include <simgear/environment/visual_enviro.hxx>
-
 #include <Time/light.hxx>
 #include <Time/light.hxx>
 #include <Cockpit/panel.hxx>
-#include <Cockpit/cockpit.hxx>
-#include <Cockpit/hud.hxx>
 #include <Model/panelnode.hxx>
 #include <Model/modelmgr.hxx>
 #include <Model/acmodel.hxx>
 #include <Scenery/scenery.hxx>
 #include <Scenery/redout.hxx>
-#include <Scenery/tilemgr.hxx>
 #include <GUI/new_gui.hxx>
-#include <Instrumentation/instrument_mgr.hxx>
 #include <Instrumentation/HUD/HUD.hxx>
 #include <Environment/precipitation_mgr.hxx>
+#include <Environment/environment_mgr.hxx>
 
 #include "splash.hxx"
 #include "renderer.hxx"
 #include "CameraGroup.hxx"
 #include "FGEventHandler.hxx"
 #include <Main/viewer.hxx>
+#include <Main/viewmgr.hxx>
 
 using namespace osg;
 using namespace simgear;
@@ -126,7 +129,7 @@ public:
       hint->setMode(GL_DONT_CARE);
   }
 private:
-  SGSharedPtr<SGPropertyNode> mConfigNode;
+  SGPropertyNode_ptr mConfigNode;
 };
 
 
@@ -207,10 +210,7 @@ public:
     glPushAttrib(GL_ALL_ATTRIB_BITS);
     glPushClientAttrib(~0u);
 
-    fgCockpitUpdate(&state);
-
-    FGInstrumentMgr *instr = static_cast<FGInstrumentMgr*>(globals->get_subsystem("instrumentation"));
-    HUD *hud = static_cast<HUD*>(instr->get_subsystem("hud"));
+    HUD *hud = static_cast<HUD*>(globals->get_subsystem("hud"));
     hud->draw(state);
 
     // update the panel subsystem
@@ -273,7 +273,7 @@ private:
 class FGWireFrameModeUpdateCallback : public osg::StateAttribute::Callback {
 public:
   FGWireFrameModeUpdateCallback() :
-    mWireframe(fgGetNode("/sim/rendering/wireframe"))
+    mWireframe(fgGetNode("/sim/rendering/wireframe", true))
   { }
   virtual void operator()(osg::StateAttribute* stateAttribute,
                           osg::NodeVisitor*)
@@ -290,13 +290,13 @@ public:
                            osg::PolygonMode::FILL);
   }
 private:
-  SGSharedPtr<SGPropertyNode> mWireframe;
+  SGPropertyNode_ptr mWireframe;
 };
 
 class FGLightModelUpdateCallback : public osg::StateAttribute::Callback {
 public:
   FGLightModelUpdateCallback() :
-    mHighlights(fgGetNode("/sim/rendering/specular-highlight"))
+    mHighlights(fgGetNode("/sim/rendering/specular-highlight", true))
   { }
   virtual void operator()(osg::StateAttribute* stateAttribute,
                           osg::NodeVisitor*)
@@ -321,13 +321,13 @@ public:
     }
   }
 private:
-  SGSharedPtr<SGPropertyNode> mHighlights;
+  SGPropertyNode_ptr mHighlights;
 };
 
 class FGFogEnableUpdateCallback : public osg::StateSet::Callback {
 public:
   FGFogEnableUpdateCallback() :
-    mFogEnabled(fgGetNode("/sim/rendering/fog"))
+    mFogEnabled(fgGetNode("/sim/rendering/fog", true))
   { }
   virtual void operator()(osg::StateSet* stateSet, osg::NodeVisitor*)
   {
@@ -338,7 +338,7 @@ public:
     }
   }
 private:
-  SGSharedPtr<SGPropertyNode> mFogEnabled;
+  SGPropertyNode_ptr mFogEnabled;
 };
 
 class FGFogUpdateCallback : public osg::StateAttribute::Callback {
@@ -363,17 +363,17 @@ public:
     assert(dynamic_cast<osg::Switch*>(node));
     osg::Switch* sw = static_cast<osg::Switch*>(node);
 
-    double t = globals->get_sim_time_sec();
-    bool enabled = 0 < t;
+    bool enabled = scenery_enabled;
     sw->setValue(0, enabled);
     if (!enabled)
       return;
     traverse(node, nv);
   }
+
+  static bool scenery_enabled;
 };
 
-// Sky structures
-SGSky *thesky;
+bool FGScenerySwitchCallback::scenery_enabled = false;
 
 static osg::ref_ptr<osg::FrameStamp> mFrameStamp = new osg::FrameStamp;
 static osg::ref_ptr<SGUpdateVisitor> mUpdateVisitor= new SGUpdateVisitor;
@@ -382,10 +382,18 @@ static osg::ref_ptr<osg::Group> mRealRoot = new osg::Group;
 
 static osg::ref_ptr<osg::Group> mRoot = new osg::Group;
 
-FGRenderer::FGRenderer()
+#ifdef FG_JPEG_SERVER
+static void updateRenderer()
+{
+    globals->get_renderer()->update();
+}
+#endif
+
+FGRenderer::FGRenderer() :
+    _sky(NULL)
 {
 #ifdef FG_JPEG_SERVER
-   jpgRenderFrame = FGRenderer::update;
+   jpgRenderFrame = updateRenderer;
 #endif
    eventHandler = new FGEventHandler;
 }
@@ -395,6 +403,7 @@ FGRenderer::~FGRenderer()
 #ifdef FG_JPEG_SERVER
    jpgRenderFrame = NULL;
 #endif
+    delete _sky;
 }
 
 // Initialize various GL/view parameters
@@ -402,7 +411,7 @@ FGRenderer::~FGRenderer()
 // critical parts of the scene graph in addition to the splash screen.
 void
 FGRenderer::splashinit( void ) {
-    osgViewer::Viewer* viewer = globals->get_renderer()->getViewer();
+    osgViewer::Viewer* viewer = getViewer();
     mRealRoot = dynamic_cast<osg::Group*>(viewer->getSceneData());
     mRealRoot->addChild(fgCreateSplashNode());
     mFrameStamp = viewer->getFrameStamp();
@@ -410,10 +419,59 @@ FGRenderer::splashinit( void ) {
     // visitor automatically.
     mUpdateVisitor->setFrameStamp(mFrameStamp.get());
     viewer->setUpdateVisitor(mUpdateVisitor.get());
+    fgSetDouble("/sim/startup/splash-alpha", 1.0);
 }
 
 void
 FGRenderer::init( void )
+{
+    _scenery_loaded   = fgGetNode("/sim/sceneryloaded", true);
+    _scenery_override = fgGetNode("/sim/sceneryloaded-override", true);
+    _panel_hotspots   = fgGetNode("/sim/panel-hotspots", true);
+    _virtual_cockpit  = fgGetNode("/sim/virtual-cockpit", true);
+
+    _sim_delta_sec = fgGetNode("/sim/time/delta-sec", true);
+
+    _xsize         = fgGetNode("/sim/startup/xsize", true);
+    _ysize         = fgGetNode("/sim/startup/ysize", true);
+    _splash_alpha  = fgGetNode("/sim/startup/splash-alpha", true);
+
+    _skyblend             = fgGetNode("/sim/rendering/skyblend", true);
+    _point_sprites        = fgGetNode("/sim/rendering/point-sprites", true);
+    _enhanced_lighting    = fgGetNode("/sim/rendering/enhanced-lighting", true);
+    _distance_attenuation = fgGetNode("/sim/rendering/distance-attenuation", true);
+    _horizon_effect       = fgGetNode("/sim/rendering/horizon-effect", true);
+    _textures             = fgGetNode("/sim/rendering/textures", true);
+
+    _altitude_ft = fgGetNode("/position/altitude-ft", true);
+
+    _cloud_status = fgGetNode("/environment/clouds/status", true);
+    _visibility_m = fgGetNode("/environment/visibility-m", true);
+    
+    bool use_point_sprites = _point_sprites->getBoolValue();
+    bool enhanced_lighting = _enhanced_lighting->getBoolValue();
+    bool distance_attenuation = _distance_attenuation->getBoolValue();
+
+    SGConfigureDirectionalLights( use_point_sprites, enhanced_lighting,
+                                  distance_attenuation );
+    
+// create sky, but can't build until setupView, since we depend
+// on other subsystems to be inited, eg Ephemeris    
+    _sky = new SGSky;
+    
+    SGPath texture_path(globals->get_fg_root());
+    texture_path.append("Textures");
+    texture_path.append("Sky");
+    for (int i = 0; i < FGEnvironmentMgr::MAX_CLOUD_LAYERS; i++) {
+        SGCloudLayer * layer = new SGCloudLayer(texture_path.str());
+        _sky->add_cloud_layer(layer);
+    }
+    
+    _sky->texture_path( texture_path.str() );
+}
+
+void
+FGRenderer::setupView( void )
 {
     osgViewer::Viewer* viewer = globals->get_renderer()->getViewer();
     osg::initNotifyLevel();
@@ -428,6 +486,17 @@ FGRenderer::init( void )
     if ( fgGetBool("/sim/startup/fullscreen") )
         fgOSFullScreen();
 
+// build the sky    
+    // The sun and moon diameters are scaled down numbers of the
+    // actual diameters. This was needed to fit both the sun and the
+    // moon within the distance to the far clip plane.
+    // Moon diameter:    3,476 kilometers
+    // Sun diameter: 1,390,000 kilometers
+    _sky->build( 80000.0, 80000.0,
+                  463.3, 361.8,
+                  *globals->get_ephem(),
+                  fgGetNode("/environment", true));
+    
     viewer->getCamera()
         ->setComputeNearFarMode(osg::CullSettings::DO_NOT_COMPUTE_NEAR_FAR);
     
@@ -503,15 +572,17 @@ FGRenderer::init( void )
     sunLight->setUpdateCallback(new FGLightSourceUpdateCallback(true));
     sunLight->setReferenceFrame(osg::LightSource::RELATIVE_RF);
     sunLight->setLocalStateSetModes(osg::StateAttribute::ON);
+    
     // Hang a StateSet above the sky subgraph in order to turn off
     // light 0
     Group* skyGroup = new Group;
     StateSet* skySS = skyGroup->getOrCreateStateSet();
     skySS->setMode(GL_LIGHT0, StateAttribute::OFF);
-    skyGroup->addChild(thesky->getPreRoot());
+    skyGroup->addChild(_sky->getPreRoot());
     sunLight->addChild(skyGroup);
     mRoot->addChild(sceneGroup);
     mRoot->addChild(sunLight);
+    
     // Clouds are added to the scene graph later
     stateSet = globals->get_scenery()->get_scene_graph()->getOrCreateStateSet();
     stateSet->setMode(GL_ALPHA_TEST, osg::StateAttribute::ON);
@@ -550,7 +621,7 @@ FGRenderer::init( void )
     // The clouds are attached directly to the scene graph root
     // because, in theory, they don't want the same default state set
     // as the rest of the scene. This may not be true in practice.
-    mRealRoot->addChild(thesky->getCloudRoot());
+    mRealRoot->addChild(_sky->getCloudRoot());
     mRealRoot->addChild(FGCreateRedoutNode());
     // Attach empty program to the scene root so that shader programs
     // don't leak into state sets (effects) that shouldn't have one.
@@ -558,39 +629,51 @@ FGRenderer::init( void )
     stateSet->setAttributeAndModes(new osg::Program, osg::StateAttribute::ON);
 }
 
-
 // Update all Visuals (redraws anything graphics related)
 void
-FGRenderer::update( bool refresh_camera_settings ) {
-    bool scenery_loaded = fgGetBool("sim/sceneryloaded", false)
-                          || fgGetBool("sim/sceneryloaded-override");
-    osgViewer::Viewer* viewer = globals->get_renderer()->getViewer();
-    if (!scenery_loaded) {
-      fgSetDouble("/sim/startup/splash-alpha", 1.0);
-      return;
+FGRenderer::update( ) {
+    if (!(_scenery_loaded->getBoolValue() || 
+           _scenery_override->getBoolValue()))
+    {
+        _splash_alpha->setDoubleValue(1.0);
+        return;
     }
+    osgViewer::Viewer* viewer = globals->get_renderer()->getViewer();
 
-    // Fade out the splash screen over the first three seconds.
-    double sAlpha = SGMiscd::max(0, (2.5 - globals->get_sim_time_sec()) / 2.5);
-    fgSetDouble("/sim/startup/splash-alpha", sAlpha);
+    if (_splash_alpha->getDoubleValue()>0.0)
+    {
+        // Fade out the splash screen
+        const double fade_time = 0.8;
+        const double fade_steps_per_sec = 20;
+        double delay_time = SGMiscd::min(fade_time/fade_steps_per_sec,
+                                         (SGTimeStamp::now() - _splash_time).toSecs());
+        _splash_time = SGTimeStamp::now();
+        double sAlpha = _splash_alpha->getDoubleValue();
+        sAlpha -= SGMiscd::max(0.0,delay_time/fade_time);
+        FGScenerySwitchCallback::scenery_enabled = (sAlpha<1.0);
+        _splash_alpha->setDoubleValue(sAlpha);
+    }
 
-    bool skyblend = fgGetBool("/sim/rendering/skyblend");
-    bool use_point_sprites = fgGetBool("/sim/rendering/point-sprites");
-    bool enhanced_lighting = fgGetBool("/sim/rendering/enhanced-lighting");
-    bool distance_attenuation
-        = fgGetBool("/sim/rendering/distance-attenuation");
-    // OSGFIXME
-    SGConfigureDirectionalLights( use_point_sprites, enhanced_lighting,
-                                  distance_attenuation );
+#if 0 // OSGFIXME
+    // OSGFIXME: features no longer available or no longer run-time configurable
+    {
+        bool use_point_sprites = _point_sprites->getBoolValue();
+        bool enhanced_lighting = _enhanced_lighting->getBoolValue();
+        bool distance_attenuation = _distance_attenuation->getBoolValue();
+    
+        SGConfigureDirectionalLights( use_point_sprites, enhanced_lighting,
+                                      distance_attenuation );
+    }
+#endif
 
     FGLight *l = static_cast<FGLight*>(globals->get_subsystem("lighting"));
 
     // update fog params
     double actual_visibility;
-    if (fgGetBool("/environment/clouds/status")) {
-        actual_visibility = thesky->get_visibility();
+    if (_cloud_status->getBoolValue()) {
+        actual_visibility = _sky->get_visibility();
     } else {
-        actual_visibility = fgGetDouble("/environment/visibility-m");
+        actual_visibility = _visibility_m->getDoubleValue();
     }
 
     // idle_state is now 1000 meaning we've finished all our
@@ -600,17 +683,13 @@ FGRenderer::update( bool refresh_camera_settings ) {
     FGViewer *current__view = globals->get_current_view();
     // Force update of center dependent values ...
     current__view->set_dirty();
-
-    if ( refresh_camera_settings ) {
-        // update view port
-        resize( fgGetInt("/sim/startup/xsize"),
-                fgGetInt("/sim/startup/ysize") );
-    }
+  
     osg::Camera *camera = viewer->getCamera();
 
+    bool skyblend = _skyblend->getBoolValue();
     if ( skyblend ) {
        
-        if ( fgGetBool("/sim/rendering/textures") ) {
+        if ( _textures->getBoolValue() ) {
             SGVec4f clearColor(l->adj_fog_color());
             camera->setClearColor(toOsg(clearColor));
         }
@@ -620,11 +699,11 @@ FGRenderer::update( bool refresh_camera_settings ) {
     }
 
     // update fog params if visibility has changed
-    double visibility_meters = fgGetDouble("/environment/visibility-m");
-    thesky->set_visibility(visibility_meters);
+    double visibility_meters = _visibility_m->getDoubleValue();
+    _sky->set_visibility(visibility_meters);
 
-    double altitude_m = fgGetDouble("/position/altitude-ft") * SG_FEET_TO_METER;
-    thesky->modify_vis( altitude_m, 0.0 /* time factor, now unused */);
+    double altitude_m = _altitude_ft->getDoubleValue() * SG_FEET_TO_METER;
+    _sky->modify_vis( altitude_m, 0.0 /* time factor, now unused */);
 
     // update the sky dome
     if ( skyblend ) {
@@ -636,7 +715,7 @@ FGRenderer::update( bool refresh_camera_settings ) {
         // Sun distance: 150,000,000 kilometers
 
         double sun_horiz_eff, moon_horiz_eff;
-        if (fgGetBool("/sim/rendering/horizon-effect")) {
+        if (_horizon_effect->getBoolValue()) {
             sun_horiz_eff
                 = 0.67 + pow(osg::clampAbove(0.5 + cos(l->get_sun_angle()),
                                              0.0),
@@ -667,9 +746,9 @@ FGRenderer::update( bool refresh_camera_settings ) {
         scolor.sun_angle   = l->get_sun_angle();
         scolor.moon_angle  = l->get_moon_angle();
   
-        double delta_time_sec = fgGetDouble("/sim/time/delta-sec");
-        thesky->reposition( sstate, *globals->get_ephem(), delta_time_sec );
-        thesky->repaint( scolor, *globals->get_ephem() );
+        double delta_time_sec = _sim_delta_sec->getDoubleValue();
+        _sky->reposition( sstate, *globals->get_ephem(), delta_time_sec );
+        _sky->repaint( scolor, *globals->get_ephem() );
 
             //OSGFIXME
 //         shadows->setupShadows(
@@ -713,45 +792,24 @@ FGRenderer::update( bool refresh_camera_settings ) {
                              l->get_sun_angle()*SGD_RADIANS_TO_DEGREES);
     mUpdateVisitor->setVisibility(actual_visibility);
     simgear::GroundLightManager::instance()->update(mUpdateVisitor.get());
-    bool hotspots = fgGetBool("/sim/panel-hotspots");
     osg::Node::NodeMask cullMask = ~simgear::LIGHTS_BITS & ~simgear::PICK_BIT;
     cullMask |= simgear::GroundLightManager::instance()
         ->getLightNodeMask(mUpdateVisitor.get());
-    if (hotspots)
+    if (_panel_hotspots->getBoolValue())
         cullMask |= simgear::PICK_BIT;
     CameraGroup::getDefault()->setCameraCullMasks(cullMask);
 }
 
-
-
-// options.cxx needs to see this for toggle_panel()
-// Handle new window size or exposure
 void
-FGRenderer::resize( int width, int height ) {
-    int view_h;
-
-    if ( (!fgGetBool("/sim/virtual-cockpit"))
-         && fgPanelVisible() && idle_state == 1000 ) {
-        view_h = (int)(height * (globals->get_current_panel()->getViewHeight() -
-                             globals->get_current_panel()->getYOffset()) / 768.0);
-    } else {
-        view_h = height;
-    }
-
-    static int lastwidth = 0;
-    static int lastheight = 0;
-    if (width != lastwidth)
-        fgSetInt("/sim/startup/xsize", lastwidth = width);
-    if (height != lastheight)
-        fgSetInt("/sim/startup/ysize", lastheight = height);
-
-    // for all views
-    FGViewMgr *viewmgr = globals->get_viewmgr();
-    if (viewmgr) {
-      for ( int i = 0; i < viewmgr->size(); ++i ) {
-        viewmgr->get_view(i)->
-          set_aspect_ratio((float)view_h / (float)width);
-      }
+FGRenderer::resize( int width, int height )
+{
+    int curWidth = _xsize->getIntValue(),
+        curHeight = _ysize->getIntValue();
+    SG_LOG(SG_VIEW, SG_DEBUG, "FGRenderer::resize: new size " << width << " x " << height);
+    if ((curHeight != height) || (curWidth != width)) {
+    // must guard setting these, or PLIB-PUI fails with too many live interfaces
+        _xsize->setIntValue(width);
+        _ysize->setIntValue(height);
     }
 }
 
@@ -828,5 +886,207 @@ fgDumpNodeToFile(osg::Node* node, const char* filename)
 {
     return osgDB::writeNodeFile(*node, filename);
 }
+
+namespace flightgear
+{
+using namespace osg;
+
+class VisibleSceneInfoVistor : public NodeVisitor, CullStack
+{
+public:
+    VisibleSceneInfoVistor()
+        : NodeVisitor(CULL_VISITOR, TRAVERSE_ACTIVE_CHILDREN)
+    {
+        setCullingMode(CullSettings::SMALL_FEATURE_CULLING
+                       | CullSettings::VIEW_FRUSTUM_CULLING);
+        setComputeNearFarMode(CullSettings::DO_NOT_COMPUTE_NEAR_FAR);
+    }
+
+    VisibleSceneInfoVistor(const VisibleSceneInfoVistor& rhs)
+    {
+    }
+
+    META_NodeVisitor("flightgear","VisibleSceneInfoVistor")
+
+    typedef std::map<const std::string,int> InfoMap;
+
+    void getNodeInfo(Node* node)
+    {
+        const char* typeName = typeid(*node).name();
+        classInfo[typeName]++;
+        const std::string& nodeName = node->getName();
+        if (!nodeName.empty())
+            nodeInfo[nodeName]++;
+    }
+
+    void dumpInfo()
+    {
+        using namespace std;
+        typedef vector<InfoMap::iterator> FreqVector;
+        cout << "class info:\n";
+        FreqVector classes;
+        for (InfoMap::iterator itr = classInfo.begin(), end = classInfo.end();
+             itr != end;
+             ++itr)
+            classes.push_back(itr);
+        sort(classes.begin(), classes.end(), freqComp);
+        for (FreqVector::iterator itr = classes.begin(), end = classes.end();
+             itr != end;
+             ++itr) {
+            cout << (*itr)->first << " " << (*itr)->second << "\n";
+        }
+        cout << "\nnode info:\n";
+        FreqVector nodes;
+        for (InfoMap::iterator itr = nodeInfo.begin(), end = nodeInfo.end();
+             itr != end;
+             ++itr)
+            nodes.push_back(itr);
+
+        sort (nodes.begin(), nodes.end(), freqComp);
+        for (FreqVector::iterator itr = nodes.begin(), end = nodes.end();
+             itr != end;
+             ++itr) {
+            cout << (*itr)->first << " " << (*itr)->second << "\n";
+        }
+        cout << endl;
+    }
+    
+    void doTraversal(Camera* camera, Node* root, Viewport* viewport)
+    {
+        ref_ptr<RefMatrix> projection
+            = createOrReuseMatrix(camera->getProjectionMatrix());
+        ref_ptr<RefMatrix> mv = createOrReuseMatrix(camera->getViewMatrix());
+        if (!viewport)
+            viewport = camera->getViewport();
+        if (viewport)
+            pushViewport(viewport);
+        pushProjectionMatrix(projection.get());
+        pushModelViewMatrix(mv.get(), Transform::ABSOLUTE_RF);
+        root->accept(*this);
+        popModelViewMatrix();
+        popProjectionMatrix();
+        if (viewport)
+            popViewport();
+        dumpInfo();
+    }
+
+    void apply(Node& node)
+    {
+        if (isCulled(node))
+            return;
+        pushCurrentMask();
+        getNodeInfo(&node);
+        traverse(node);
+        popCurrentMask();
+    }
+    void apply(Group& node)
+    {
+        if (isCulled(node))
+            return;
+        pushCurrentMask();
+        getNodeInfo(&node);
+        traverse(node);
+        popCurrentMask();
+    }
+
+    void apply(Transform& node)
+    {
+        if (isCulled(node))
+            return;
+        pushCurrentMask();
+        ref_ptr<RefMatrix> matrix = createOrReuseMatrix(*getModelViewMatrix());
+        node.computeLocalToWorldMatrix(*matrix,this);
+        pushModelViewMatrix(matrix.get(), node.getReferenceFrame());
+        getNodeInfo(&node);
+        traverse(node);
+        popModelViewMatrix();
+        popCurrentMask();
+    }
+
+    void apply(Camera& camera)
+    {
+        // Save current cull settings
+        CullSettings saved_cull_settings(*this);
+
+        // set cull settings from this Camera
+        setCullSettings(camera);
+        // inherit the settings from above
+        inheritCullSettings(saved_cull_settings, camera.getInheritanceMask());
+
+        // set the cull mask.
+        unsigned int savedTraversalMask = getTraversalMask();
+        bool mustSetCullMask = (camera.getInheritanceMask()
+                                & osg::CullSettings::CULL_MASK) == 0;
+        if (mustSetCullMask)
+            setTraversalMask(camera.getCullMask());
+
+        osg::RefMatrix* projection = 0;
+        osg::RefMatrix* modelview = 0;
+
+        if (camera.getReferenceFrame()==osg::Transform::RELATIVE_RF) {
+            if (camera.getTransformOrder()==osg::Camera::POST_MULTIPLY) {
+                projection = createOrReuseMatrix(*getProjectionMatrix()
+                                                 *camera.getProjectionMatrix());
+                modelview = createOrReuseMatrix(*getModelViewMatrix()
+                                                * camera.getViewMatrix());
+            }
+            else {              // pre multiply 
+                projection = createOrReuseMatrix(camera.getProjectionMatrix()
+                                                 * (*getProjectionMatrix()));
+                modelview = createOrReuseMatrix(camera.getViewMatrix()
+                                                * (*getModelViewMatrix()));
+            }
+        } else {
+            // an absolute reference frame
+            projection = createOrReuseMatrix(camera.getProjectionMatrix());
+            modelview = createOrReuseMatrix(camera.getViewMatrix());
+        }
+        if (camera.getViewport())
+            pushViewport(camera.getViewport());
+
+        pushProjectionMatrix(projection);
+        pushModelViewMatrix(modelview, camera.getReferenceFrame());    
+
+        traverse(camera);
+    
+        // restore the previous model view matrix.
+        popModelViewMatrix();
+
+        // restore the previous model view matrix.
+        popProjectionMatrix();
+
+        if (camera.getViewport()) popViewport();
+
+        // restore the previous traversal mask settings
+        if (mustSetCullMask)
+            setTraversalMask(savedTraversalMask);
+
+        // restore the previous cull settings
+        setCullSettings(saved_cull_settings);
+    }
+
+protected:
+    // sort in reverse
+    static bool freqComp(const InfoMap::iterator& lhs, const InfoMap::iterator& rhs)
+    {
+        return lhs->second > rhs->second;
+    }
+    InfoMap classInfo;
+    InfoMap nodeInfo;
+};
+
+bool printVisibleSceneInfo(FGRenderer* renderer)
+{
+    osgViewer::Viewer* viewer = renderer->getViewer();
+    VisibleSceneInfoVistor vsv;
+    Viewport* vp = 0;
+    if (!viewer->getCamera()->getViewport() && viewer->getNumSlaves() > 0) {
+        const View::Slave& slave = viewer->getSlave(0);
+        vp = slave._camera->getViewport();
+    }
+    vsv.doTraversal(viewer->getCamera(), viewer->getSceneData(), vp);
+    return true;
+}
+}
 // end of renderer.cxx