]> git.mxchange.org Git - flightgear.git/blobdiff - src/Viewer/CameraGroup.cxx
Replace the NOAA METAR URL with the new, updated one
[flightgear.git] / src / Viewer / CameraGroup.cxx
index 8de78d1aae66e30f5f300be9aabebdf6ff44e9c8..c3d25ac3d8f851e69e0540b67f0fa411eaf0a00e 100644 (file)
 #include "FGEventHandler.hxx"
 #include "WindowBuilder.hxx"
 #include "WindowSystemAdapter.hxx"
+
+#include <simgear/math/SGRect.hxx>
 #include <simgear/props/props.hxx>
+#include <simgear/props/props_io.hxx> // for copyProperties
 #include <simgear/structure/OSGUtils.hxx>
 #include <simgear/structure/OSGVersion.hxx>
 #include <simgear/scene/material/EffectCullVisitor.hxx>
 #include <simgear/scene/util/RenderConstants.hxx>
-#include <simgear/scene/tgdb/userdata.hxx>
 
 #include <algorithm>
 #include <cstring>
 #include <osgViewer/GraphicsWindow>
 #include <osgViewer/Renderer>
 
-namespace flightgear {
-const char* MAIN_CAMERA = "MAIN_CAMERA";
-const char* FAR_CAMERA = "FAR_CAMERA";
-const char* GEOMETRY_CAMERA = "GEOMETRY_CAMERA";
-const char* SHADOW_CAMERA = "SHADOW_CAMERA";
-const char* LIGHTING_CAMERA = "LIGHTING_CAMERA";
-const char* DISPLAY_CAMERA = "DISPLAY_CAMERA";
-}
-
-static osg::Matrix
-invert(const osg::Matrix& matrix)
-{
-    return osg::Matrix::inverse(matrix);
-}
+using namespace osg;
 
-/// Returns the zoom factor of the master camera.
-/// The reference fov is the historic 55 deg
-static double
-zoomFactor()
+namespace
 {
-    double fov = fgGetDouble("/sim/current-view/field-of-view", 55);
-    if (fov < 1)
-        fov = 1;
-    return tan(55*0.5*SG_DEGREES_TO_RADIANS)/tan(fov*0.5*SG_DEGREES_TO_RADIANS);
-}
+    
+    // Given a projection matrix, return a new one with the same frustum
+    // sides and new near / far values.
+    
+    void makeNewProjMat(Matrixd& oldProj, double znear,
+                        double zfar, Matrixd& projection)
+    {
+        projection = oldProj;
+        // Slightly inflate the near & far planes to avoid objects at the
+        // extremes being clipped out.
+        znear *= 0.999;
+        zfar *= 1.001;
+        
+        // Clamp the projection matrix z values to the range (near, far)
+        double epsilon = 1.0e-6;
+        if (fabs(projection(0,3)) < epsilon &&
+            fabs(projection(1,3)) < epsilon &&
+            fabs(projection(2,3)) < epsilon) {
+            // Projection is Orthographic
+            epsilon = -1.0/(zfar - znear); // Used as a temp variable
+            projection(2,2) = 2.0*epsilon;
+            projection(3,2) = (zfar + znear)*epsilon;
+        } else {
+            // Projection is Perspective
+            double trans_near = (-znear*projection(2,2) + projection(3,2)) /
+            (-znear*projection(2,3) + projection(3,3));
+            double trans_far = (-zfar*projection(2,2) + projection(3,2)) /
+            (-zfar*projection(2,3) + projection(3,3));
+            double ratio = fabs(2.0/(trans_near - trans_far));
+            double center = -0.5*(trans_near + trans_far);
+            
+            projection.postMult(osg::Matrixd(1.0, 0.0, 0.0, 0.0,
+                                             0.0, 1.0, 0.0, 0.0,
+                                             0.0, 0.0, ratio, 0.0,
+                                             0.0, 0.0, center*ratio, 1.0));
+        }
+    }
+    
+    osg::Matrix
+    invert(const osg::Matrix& matrix)
+    {
+        return osg::Matrix::inverse(matrix);
+    }
+    
+    /// Returns the zoom factor of the master camera.
+    /// The reference fov is the historic 55 deg
+    double
+    zoomFactor()
+    {
+        double fov = fgGetDouble("/sim/current-view/field-of-view", 55);
+        if (fov < 1)
+            fov = 1;
+        return tan(55*0.5*SG_DEGREES_TO_RADIANS)/tan(fov*0.5*SG_DEGREES_TO_RADIANS);
+    }
+    
+    osg::Vec2d
+    preMult(const osg::Vec2d& v, const osg::Matrix& m)
+    {
+        osg::Vec3d tmp = m.preMult(osg::Vec3(v, 0));
+        return osg::Vec2d(tmp[0], tmp[1]);
+    }
+    
+    osg::Matrix
+    relativeProjection(const osg::Matrix& P0, const osg::Matrix& R, const osg::Vec2d ref[2],
+                       const osg::Matrix& pP, const osg::Matrix& pR, const osg::Vec2d pRef[2])
+    {
+        // Track the way from one projection space to the other:
+        // We want
+        //  P = T*S*P0
+        // where P0 is the projection template sensible for the given window size,
+        // T is a translation matrix and S a scale matrix.
+        // We need to determine T and S so that the reference points in the parents
+        // projection space match the two reference points in this cameras projection space.
+        
+        // Starting from the parents camera projection space, we get into this cameras
+        // projection space by the transform matrix:
+        //  P*R*inv(pP*pR) = T*S*P0*R*inv(pP*pR)
+        // So, at first compute that matrix without T*S and determine S and T from that
+        
+        // Ok, now osg uses the inverse matrix multiplication order, thus:
+        osg::Matrix PtoPwithoutTS = invert(pR*pP)*R*P0;
+        // Compute the parents reference points in the current projection space
+        // without the yet unknown T and S
+        osg::Vec2d pRefInThis[2] = {
+            preMult(pRef[0], PtoPwithoutTS),
+            preMult(pRef[1], PtoPwithoutTS)
+        };
+        
+        // To get the same zoom, rescale to match the parents size
+        double s = (ref[0] - ref[1]).length()/(pRefInThis[0] - pRefInThis[1]).length();
+        osg::Matrix S = osg::Matrix::scale(s, s, 1);
+        
+        // For the translation offset, incorporate the now known scale
+        // and recompute the position ot the first reference point in the
+        // currents projection space without the yet unknown T.
+        pRefInThis[0] = preMult(pRef[0], PtoPwithoutTS*S);
+        // The translation is then the difference of the reference points
+        osg::Matrix T = osg::Matrix::translate(osg::Vec3d(ref[0] - pRefInThis[0], 0));
+        
+        // Compose and return the desired final projection matrix
+        return P0*S*T;
+    }
 
-static osg::Vec2d
-preMult(const osg::Vec2d& v, const osg::Matrix& m)
-{
-  osg::Vec3d tmp = m.preMult(osg::Vec3(v, 0));
-  return osg::Vec2d(tmp[0], tmp[1]);
-}
+} // of anonymous namespace
 
-static osg::Matrix
-relativeProjection(const osg::Matrix& P0, const osg::Matrix& R, const osg::Vec2d ref[2],
-                   const osg::Matrix& pP, const osg::Matrix& pR, const osg::Vec2d pRef[2])
-{
-  // Track the way from one projection space to the other:
-  // We want
-  //  P = T*S*P0
-  // where P0 is the projection template sensible for the given window size,
-  // T is a translation matrix and S a scale matrix.
-  // We need to determine T and S so that the reference points in the parents
-  // projection space match the two reference points in this cameras projection space.
-
-  // Starting from the parents camera projection space, we get into this cameras
-  // projection space by the transform matrix:
-  //  P*R*inv(pP*pR) = T*S*P0*R*inv(pP*pR)
-  // So, at first compute that matrix without T*S and determine S and T from that
-
-  // Ok, now osg uses the inverse matrix multiplication order, thus:
-  osg::Matrix PtoPwithoutTS = invert(pR*pP)*R*P0;
-  // Compute the parents reference points in the current projection space
-  // without the yet unknown T and S
-  osg::Vec2d pRefInThis[2] = {
-    preMult(pRef[0], PtoPwithoutTS),
-    preMult(pRef[1], PtoPwithoutTS)
-  };
-
-  // To get the same zoom, rescale to match the parents size
-  double s = (ref[0] - ref[1]).length()/(pRefInThis[0] - pRefInThis[1]).length();
-  osg::Matrix S = osg::Matrix::scale(s, s, 1);
-
-  // For the translation offset, incorporate the now known scale
-  // and recompute the position ot the first reference point in the
-  // currents projection space without the yet unknown T.
-  pRefInThis[0] = preMult(pRef[0], PtoPwithoutTS*S);
-  // The translation is then the difference of the reference points
-  osg::Matrix T = osg::Matrix::translate(osg::Vec3d(ref[0] - pRefInThis[0], 0));
-
-  // Compose and return the desired final projection matrix
-  return P0*S*T;
-}
+typedef std::vector<SGPropertyNode_ptr> SGPropertyNodeVec;
 
 namespace flightgear
 {
@@ -137,59 +176,121 @@ using namespace osg;
 using std::strcmp;
 using std::string;
 
-ref_ptr<CameraGroup> CameraGroup::_defaultGroup;
-
-CameraGroup::CameraGroup(osgViewer::Viewer* viewer) :
-    _viewer(viewer)
-{
-}
-
-}
-
-namespace
-{
-using namespace osg;
-
-// Given a projection matrix, return a new one with the same frustum
-// sides and new near / far values.
-
-void makeNewProjMat(Matrixd& oldProj, double znear,
-                                       double zfar, Matrixd& projection)
-{
-    projection = oldProj;
-    // Slightly inflate the near & far planes to avoid objects at the
-    // extremes being clipped out.
-    znear *= 0.999;
-    zfar *= 1.001;
-
-    // Clamp the projection matrix z values to the range (near, far)
-    double epsilon = 1.0e-6;
-    if (fabs(projection(0,3)) < epsilon &&
-        fabs(projection(1,3)) < epsilon &&
-        fabs(projection(2,3)) < epsilon) {
-        // Projection is Orthographic
-        epsilon = -1.0/(zfar - znear); // Used as a temp variable
-        projection(2,2) = 2.0*epsilon;
-        projection(3,2) = (zfar + znear)*epsilon;
-    } else {
-        // Projection is Perspective
-        double trans_near = (-znear*projection(2,2) + projection(3,2)) /
-            (-znear*projection(2,3) + projection(3,3));
-        double trans_far = (-zfar*projection(2,2) + projection(3,2)) /
-            (-zfar*projection(2,3) + projection(3,3));
-        double ratio = fabs(2.0/(trans_near - trans_far));
-        double center = -0.5*(trans_near + trans_far);
-
-        projection.postMult(osg::Matrixd(1.0, 0.0, 0.0, 0.0,
-                                         0.0, 1.0, 0.0, 0.0,
-                                         0.0, 0.0, ratio, 0.0,
-                                         0.0, 0.0, center*ratio, 1.0));
-    }
-}
-}
+    class CameraGroupListener : public SGPropertyChangeListener
+    {
+    public:
+        CameraGroupListener(CameraGroup* cg, SGPropertyNode* gnode) :
+        _groupNode(gnode),
+        _cameraGroup(cg)
+        {
+            listenToNode("znear", 0.1f);
+            listenToNode("zfar", 120000.0f);
+            listenToNode("near-field", 100.0f);
+        }
+        
+        virtual ~CameraGroupListener()
+        {
+            unlisten("znear");
+            unlisten("zfar");
+            unlisten("near-field");
+        }
+        
+        virtual void valueChanged(SGPropertyNode* prop)
+        {
+            if (!strcmp(prop->getName(), "znear")) {
+                _cameraGroup->setZNear(prop->getFloatValue());
+            } else if (!strcmp(prop->getName(), "zfar")) {
+                _cameraGroup->setZFar(prop->getFloatValue());
+            } else if (!strcmp(prop->getName(), "near-field")) {
+                _cameraGroup->setNearField(prop->getFloatValue());
+            }
+        }
+    private:
+        void listenToNode(const std::string& name, double val)
+        {
+            SGPropertyNode* n = _groupNode->getChild(name);
+            if (!n) {
+                n = _groupNode->getChild(name, 0 /* index */, true);
+                n->setDoubleValue(val);
+            }
+            n->addChangeListener(this);
+            valueChanged(n); // propogate initial state through
+        }
+        
+        void unlisten(const std::string& name)
+        {
+            _groupNode->getChild(name)->removeChangeListener(this);
+        }
+        
+        SGPropertyNode_ptr _groupNode;
+        CameraGroup* _cameraGroup; // non-owning reference
+        
+    };
+    
+    class CameraViewportListener : public SGPropertyChangeListener
+    {
+    public:
+        CameraViewportListener(CameraInfo* info,
+                               SGPropertyNode* vnode,
+                               const osg::GraphicsContext::Traits *traits) :
+        _viewportNode(vnode),
+        _camera(info)
+        {
+            listenToNode("x", 0.0f);
+            listenToNode("y", 0.0f);
+            listenToNode("width", traits->width);
+            listenToNode("height", traits->height);
+        }
+        
+        virtual ~CameraViewportListener()
+        {
+            unlisten("x");
+            unlisten("y");
+            unlisten("width");
+            unlisten("height");
+        }
+        
+        virtual void valueChanged(SGPropertyNode* prop)
+        {
+            if (!strcmp(prop->getName(), "x")) {
+                _camera->x = prop->getDoubleValue();
+            } else if (!strcmp(prop->getName(), "y")) {
+                _camera->y = prop->getDoubleValue();
+            } else if (!strcmp(prop->getName(), "width")) {
+                _camera->width = prop->getDoubleValue();
+            } else if (!strcmp(prop->getName(), "height")) {
+                _camera->height = prop->getDoubleValue();
+            }
+        }
+    private:
+        void listenToNode(const std::string& name, double val)
+        {
+            SGPropertyNode* n = _viewportNode->getChild(name);
+            if (!n) {
+                n = _viewportNode->getChild(name, 0 /* index */, true);
+                n->setDoubleValue(val);
+            }
+            n->addChangeListener(this);
+            valueChanged(n); // propogate initial state through
+        }
+        
+        void unlisten(const std::string& name)
+        {
+            _viewportNode->getChild(name)->removeChangeListener(this);
+        }
+        
+        SGPropertyNode_ptr _viewportNode;
+        CameraInfo* _camera;
+        
+    };
+    
+    const char* MAIN_CAMERA = "main";
+    const char* FAR_CAMERA = "far";
+    const char* GEOMETRY_CAMERA = "geometry";
+    const char* SHADOW_CAMERA = "shadow";
+    const char* LIGHTING_CAMERA = "lighting";
+    const char* DISPLAY_CAMERA = "display";
 
-namespace flightgear
-{
 void CameraInfo::updateCameras()
 {
     bufferSize->set( osg::Vec2f( width, height ) );
@@ -197,7 +298,11 @@ void CameraInfo::updateCameras()
     for (CameraMap::iterator ii = cameras.begin(); ii != cameras.end(); ++ii ) {
         float f = ii->second.scaleFactor;
         if ( f == 0.0f ) continue;
-        ii->second.camera->getViewport()->setViewport(x*f, y*f, width*f, height*f);
+
+        if (ii->second.camera->getRenderTargetImplementation() == osg::Camera::FRAME_BUFFER_OBJECT)
+            ii->second.camera->getViewport()->setViewport(0, 0, width*f, height*f);
+        else
+            ii->second.camera->getViewport()->setViewport(x*f, y*f, width*f, height*f);
     }
 
     for (RenderBufferMap::iterator ii = buffers.begin(); ii != buffers.end(); ++ii ) {
@@ -213,6 +318,9 @@ void CameraInfo::updateCameras()
 
 void CameraInfo::resized(double w, double h)
 {
+    if (w == 1.0 && h == 1.0)
+        return;
+
     bufferSize->set( osg::Vec2f( w, h ) );
 
     for (RenderBufferMap::iterator ii = buffers.begin(); ii != buffers.end(); ++ii) {
@@ -251,6 +359,11 @@ void CameraInfo::resized(double w, double h)
     }
 }
 
+CameraInfo::~CameraInfo()
+{
+    delete viewportListener;
+}
+    
 osg::Camera* CameraInfo::getCamera(const std::string& k) const
 {
     CameraMap::const_iterator ii = cameras.find( k );
@@ -275,10 +388,30 @@ int CameraInfo::getMainSlaveIndex() const
 void CameraInfo::setMatrices(osg::Camera* c)
 {
     view->set( c->getViewMatrix() );
-    viewInverse->set( osg::Matrix::inverse( c->getViewMatrix() ) );
+    osg::Matrixd vi = c->getInverseViewMatrix();
+    viewInverse->set( vi );
     projInverse->set( osg::Matrix::inverse( c->getProjectionMatrix() ) );
+    osg::Vec4d pos = osg::Vec4d(0., 0., 0., 1.) * vi;
+    worldPosCart->set( osg::Vec3f( pos.x(), pos.y(), pos.z() ) );
+    SGGeod pos2 = SGGeod::fromCart( SGVec3d( pos.x(), pos.y(), pos.z() ) );
+    worldPosGeod->set( osg::Vec3f( pos2.getLongitudeRad(), pos2.getLatitudeRad(), pos2.getElevationM() ) );
 }
 
+CameraGroup::~CameraGroup()
+{
+    
+    for (CameraList::iterator i = _cameras.begin(); i != _cameras.end(); ++i) {
+        CameraInfo* info = *i;
+        for (CameraMap::iterator ii = info->cameras.begin(); ii != info->cameras.end(); ++ii) {
+            RenderStageInfo& rsi = ii->second;
+            unsigned int slaveIndex = _viewer->findSlaveIndexForCamera(rsi.camera);
+            _viewer->removeSlave(slaveIndex);
+        }
+    }
+    
+    _cameras.clear();
+}
+    
 void CameraGroup::update(const osg::Vec3d& position,
                          const osg::Quat& orientation)
 {
@@ -292,11 +425,8 @@ void CameraGroup::update(const osg::Vec3d& position,
 
         Camera* camera = info->getCamera(MAIN_CAMERA);
         if ( camera ) {
-            const View::Slave& slave = _viewer->getSlave(info->getMainSlaveIndex());
-#if SG_OSG_VERSION_LESS_THAN(3,0,0)
-            // refreshes camera viewports (for now)
-            info->updateCameras();
-#endif
+            const osg::View::Slave& slave = _viewer->getSlave(info->getMainSlaveIndex());
+
             Matrix viewMatrix;
             if (info->flags & GUI) {
                 viewMatrix = osg::Matrix(); // identifty transform on the GUI camera
@@ -385,7 +515,7 @@ void CameraGroup::update(const osg::Vec3d& position,
 
                 Camera* camera = ii->second.camera.get();
                 int slaveIndex = ii->second.slaveIndex;
-                const View::Slave& slave = _viewer->getSlave(slaveIndex);
+                const osg::View::Slave& slave = _viewer->getSlave(slaveIndex);
 
                 if ( !viewDone ) {
                     if ((info->flags & VIEW_ABSOLUTE) != 0)
@@ -437,6 +567,13 @@ void CameraGroup::update(const osg::Vec3d& position,
     globals->get_renderer()->setPlanes( _zNear, _zFar );
 }
 
+ref_ptr<CameraGroup> CameraGroup::_defaultGroup;
+
+CameraGroup::CameraGroup(osgViewer::Viewer* viewer) :
+_viewer(viewer)
+{
+}
+    
 void CameraGroup::setCameraParameters(float vfov, float aspectRatio)
 {
     if (vfov != 0.0f && aspectRatio != 0.0f)
@@ -463,74 +600,6 @@ double CameraGroup::getMasterAspectRatio() const
     
     return static_cast<double>(viewport->height()) / viewport->width();
 }
-    
-}
-
-namespace
-{
-// A raw value for property nodes that references a class member via
-// an osg::ref_ptr.
-template<class C, class T>
-class RefMember : public SGRawValue<T>
-{
-public:
-    RefMember (C *obj, T C::*ptr)
-        : _obj(obj), _ptr(ptr) {}
-    virtual ~RefMember () {}
-    virtual T getValue () const
-    {
-        return _obj.get()->*_ptr;
-    }
-    virtual bool setValue (T value)
-    {
-        _obj.get()->*_ptr = value;
-        return true;
-    }
-    virtual SGRawValue<T> * clone () const
-    {
-        return new RefMember(_obj.get(), _ptr);
-    }
-private:
-    ref_ptr<C> _obj;
-    T C::* const _ptr;
-};
-
-template<typename C, typename T>
-RefMember<C, T> makeRefMember(C *obj, T C::*ptr)
-{
-    return RefMember<C, T>(obj, ptr);
-}
-
-template<typename C, typename T>
-void bindMemberToNode(SGPropertyNode* parent, const char* childName,
-                      C* obj, T C::*ptr, T value)
-{
-    SGPropertyNode* valNode = parent->getNode(childName);
-    RefMember<C, T> refMember = makeRefMember(obj, ptr);
-    if (!valNode) {
-        valNode = parent->getNode(childName, true);
-        valNode->tie(refMember, false);
-        setValue(valNode, value);
-    } else {
-        valNode->tie(refMember, true);
-    }
-}
-
-void buildViewport(flightgear::CameraInfo* info, SGPropertyNode* viewportNode,
-                   const osg::GraphicsContext::Traits *traits)
-{
-    using namespace flightgear;
-    bindMemberToNode(viewportNode, "x", info, &CameraInfo::x, 0.0);
-    bindMemberToNode(viewportNode, "y", info, &CameraInfo::y, 0.0);
-    bindMemberToNode(viewportNode, "width", info, &CameraInfo::width,
-                     static_cast<double>(traits->width));
-    bindMemberToNode(viewportNode, "height", info, &CameraInfo::height,
-                     static_cast<double>(traits->height));
-}
-}
-
-namespace flightgear
-{
 
 // Mostly copied from osg's osgViewer/View.cpp
 
@@ -743,6 +812,7 @@ CameraInfo* CameraGroup::buildCamera(SGPropertyNode* cameraNode)
         return 0;
     }
     Camera* camera = new Camera;
+    camera->setName("windowCamera");
     camera->setAllowEventFocus(false);
     camera->setGraphicsContext(window->gc.get());
     camera->setViewport(new Viewport);
@@ -751,9 +821,7 @@ CameraInfo* CameraGroup::buildCamera(SGPropertyNode* cameraNode)
     camera->setInheritanceMask(CullSettings::ALL_VARIABLES
                                & ~(CullSettings::CULL_MASK
                                    | CullSettings::CULLING_MODE
-#if defined(HAVE_CULLSETTINGS_CLEAR_MASK)
                                    | CullSettings::CLEAR_MASK
-#endif
                                    ));
 
     osg::Matrix vOff;
@@ -949,6 +1017,7 @@ CameraInfo* CameraGroup::buildCamera(SGPropertyNode* cameraNode)
     bool useMasterSceneGraph = !psNode;
     CameraInfo* info = globals->get_renderer()->buildRenderingPipeline(this, cameraFlags, camera, vOff, pOff,
                                                                         window->gc.get(), useMasterSceneGraph);
+    
     info->name = cameraNode->getStringValue("name");
     info->physicalWidth = physicalWidth;
     info->physicalHeight = physicalHeight;
@@ -961,10 +1030,13 @@ CameraInfo* CameraGroup::buildCamera(SGPropertyNode* cameraNode)
     info->parentReference[1] = parentReference[1];
     info->thisReference[0] = thisReference[0];
     info->thisReference[1] = thisReference[1];
+    
     // If a viewport isn't set on the camera, then it's hard to dig it
     // out of the SceneView objects in the viewer, and the coordinates
     // of mouse events are somewhat bizzare.
-    buildViewport(info, viewportNode, window->gc->getTraits());
+    
+    info->viewportListener = new CameraViewportListener(info, viewportNode, window->gc->getTraits());
+    
     info->updateCameras();
     // Distortion camera needs the viewport which is created by addCamera().
     if (psNode) {
@@ -1000,9 +1072,7 @@ CameraInfo* CameraGroup::buildGUICamera(SGPropertyNode* cameraNode,
     camera->setInheritanceMask(CullSettings::ALL_VARIABLES
                                & ~(CullSettings::COMPUTE_NEAR_FAR_MODE
                                    | CullSettings::CULLING_MODE
-#if defined(HAVE_CULLSETTINGS_CLEAR_MASK)
                                    | CullSettings::CLEAR_MASK
-#endif
                                    ));
     camera->setComputeNearFarMode(osg::CullSettings::DO_NOT_COMPUTE_NEAR_FAR);
     camera->setCullingMode(osg::CullSettings::NO_CULLING);
@@ -1011,9 +1081,16 @@ CameraInfo* CameraGroup::buildGUICamera(SGPropertyNode* cameraNode,
     const int cameraFlags = GUI | DO_INTERSECTION_TEST;
 
     CameraInfo* result = new CameraInfo(cameraFlags);
+    result->name = "GUI camera";
     // The camera group will always update the camera
     camera->setReferenceFrame(Transform::ABSOLUTE_RF);
 
+    // Draw all nodes in the order they are added to the GUI camera
+    camera->getOrCreateStateSet()
+          ->setRenderBinDetails( 0,
+                                 "PreOrderBin",
+                                 osg::StateSet::OVERRIDE_RENDERBIN_DETAILS );
+
     getViewer()->addSlave(camera, Matrixd::identity(), Matrixd::identity(), false);
     //installCullVisitor(camera);
     int slaveIndex = getViewer()->getNumSlaves() - 1;
@@ -1025,7 +1102,9 @@ CameraInfo* CameraGroup::buildGUICamera(SGPropertyNode* cameraNode,
     // should be assigned by a camera manager.
     camera->setRenderOrder(osg::Camera::POST_RENDER, 10000);
     SGPropertyNode* viewportNode = cameraNode->getNode("viewport", true);
-    buildViewport(result, viewportNode, window->gc->getTraits());
+
+    result->viewportListener = new CameraViewportListener(result, viewportNode,
+                                                              window->gc->getTraits());
 
     // Disable statistics for the GUI camera.
     camera->setStats(0);
@@ -1036,9 +1115,9 @@ CameraInfo* CameraGroup::buildGUICamera(SGPropertyNode* cameraNode,
 CameraGroup* CameraGroup::buildCameraGroup(osgViewer::Viewer* viewer,
                                            SGPropertyNode* gnode)
 {
-    sgUserDataInit( globals->get_props() );
-
     CameraGroup* cgroup = new CameraGroup(viewer);
+    cgroup->_listener.reset(new CameraGroupListener(cgroup, gnode));
+    
     for (int i = 0; i < gnode->nChildren(); ++i) {
         SGPropertyNode* pNode = gnode->getChild(i);
         const char* name = pNode->getName();
@@ -1050,10 +1129,7 @@ CameraGroup* CameraGroup::buildCameraGroup(osgViewer::Viewer* viewer,
             cgroup->buildGUICamera(pNode);
         }
     }
-    bindMemberToNode(gnode, "znear", cgroup, &CameraGroup::_zNear, .1f);
-    bindMemberToNode(gnode, "zfar", cgroup, &CameraGroup::_zFar, 120000.0f);
-    bindMemberToNode(gnode, "near-field", cgroup, &CameraGroup::_nearField,
-                     100.0f);
+
     return cgroup;
 }
 
@@ -1082,6 +1158,18 @@ void CameraGroup::setCameraCullMasks(Node::NodeMask nm)
             camera = info->getCamera( GEOMETRY_CAMERA );
             if (camera == 0) continue;
             camera->setCullMask( nm & ~simgear::MODELLIGHT_BIT );
+
+            camera = info->getCamera( LIGHTING_CAMERA );
+            if (camera == 0) continue;
+            osg::Switch* sw = camera->getChild(0)->asSwitch();
+            for (unsigned int i = 0; i < sw->getNumChildren(); ++i) {
+                osg::Camera* lc = dynamic_cast<osg::Camera*>(sw->getChild(i));
+                if (lc == 0) continue;
+                string name = lc->getName();
+                if (name == "LightCamera") {
+                    lc->setCullMask( (nm & simgear::LIGHTS_BITS) | (lc->getCullMask() & ~simgear::LIGHTS_BITS) );
+                }
+            }
         }
     }
 }
@@ -1125,34 +1213,34 @@ Camera* getGUICamera(CameraGroup* cgroup)
     return info->getCamera(MAIN_CAMERA);
 }
 
-static bool computeCameraIntersection(const CameraInfo* cinfo,
-                                      const osgGA::GUIEventAdapter* ea,
+
+static bool computeCameraIntersection(const CameraInfo* cinfo, const osg::Vec2d& windowPos,
                                       osgUtil::LineSegmentIntersector::Intersections& intersections)
 {
   using osgUtil::Intersector;
   using osgUtil::LineSegmentIntersector;
-  double x, y;
-  eventToWindowCoords(ea, x, y);
-  
+
   if (!(cinfo->flags & CameraGroup::DO_INTERSECTION_TEST))
     return false;
   
   const Camera* camera = cinfo->getCamera(MAIN_CAMERA);
   if ( !camera )
     camera = cinfo->getCamera( GEOMETRY_CAMERA );
-  if (camera->getGraphicsContext() != ea->getGraphicsContext())
-    return false;
+  // if (camera->getGraphicsContext() != ea->getGraphicsContext())
+ //   return false;
   
   const Viewport* viewport = camera->getViewport();
+  SGRect<double> viewportRect(viewport->x(), viewport->y(),
+                              viewport->x() + viewport->width() - 1.0,
+                              viewport->y() + viewport->height()- 1.0);
+    
   double epsilon = 0.5;
-  if (!(x >= viewport->x() - epsilon
-        && x < viewport->x() + viewport->width() -1.0 + epsilon
-        && y >= viewport->y() - epsilon
-        && y < viewport->y() + viewport->height() -1.0 + epsilon))
+  if (!viewportRect.contains(windowPos.x(), windowPos.y(), epsilon))
     return false;
   
-  Vec4d start(x, y, 0.0, 1.0);
-  Vec4d end(x, y, 1.0, 1.0);
+  Vec4d start(windowPos.x(), windowPos.y(), 0.0, 1.0);
+  Vec4d end(windowPos.x(), windowPos.y(), 1.0, 1.0);
   Matrix windowMat = viewport->computeWindowMatrix();
   Matrix startPtMat = Matrix::inverse(camera->getProjectionMatrix()
                                       * windowMat);
@@ -1172,7 +1260,8 @@ static bool computeCameraIntersection(const CameraInfo* cinfo,
                                Vec3d(start.x(), start.y(), start.z()),
                                Vec3d(end.x(), end.y(), end.z()));
   osgUtil::IntersectionVisitor iv(picker.get());
-  iv.setTraversalMask( ~simgear::MODELLIGHT_BIT );
+  iv.setTraversalMask( simgear::PICK_BIT );
+    
   const_cast<Camera*>(camera)->accept(iv);
   if (picker->containsIntersections()) {
     intersections = picker->getIntersections();
@@ -1183,12 +1272,12 @@ static bool computeCameraIntersection(const CameraInfo* cinfo,
 }
   
 bool computeIntersections(const CameraGroup* cgroup,
-                          const osgGA::GUIEventAdapter* ea,
+                          const osg::Vec2d& windowPos,
                           osgUtil::LineSegmentIntersector::Intersections& intersections)
 {
     // test the GUI first
     const CameraInfo* guiCamera = cgroup->getGUICamera();
-    if (guiCamera && computeCameraIntersection(guiCamera, ea, intersections))
+    if (guiCamera && computeCameraIntersection(guiCamera, windowPos, intersections))
         return true;
     
     // Find camera that contains event
@@ -1200,7 +1289,7 @@ bool computeIntersections(const CameraGroup* cgroup,
         if (cinfo == guiCamera)
             continue;
         
-        if (computeCameraIntersection(cinfo, ea, intersections))
+        if (computeCameraIntersection(cinfo, windowPos, intersections))
             return true;
     }
   
@@ -1247,4 +1336,50 @@ void warpGUIPointer(CameraGroup* cgroup, int x, int y)
               * (eventState->getYmax() - eventState->getYmin())));
     cgroup->getViewer()->getEventQueue()->mouseWarped(viewerX, viewerY);
 }
+
+void CameraGroup::buildDefaultGroup(osgViewer::Viewer* viewer)
+{
+    // Look for windows, camera groups, and the old syntax of
+    // top-level cameras
+    SGPropertyNode* renderingNode = fgGetNode("/sim/rendering");
+    SGPropertyNode* cgroupNode = renderingNode->getNode("camera-group", true);
+    bool oldSyntax = !cgroupNode->hasChild("camera");
+    if (oldSyntax) {
+        for (int i = 0; i < renderingNode->nChildren(); ++i) {
+            SGPropertyNode* propNode = renderingNode->getChild(i);
+            const char* propName = propNode->getName();
+            if (!strcmp(propName, "window") || !strcmp(propName, "camera")) {
+                SGPropertyNode* copiedNode
+                    = cgroupNode->getNode(propName, propNode->getIndex(), true);
+                copyProperties(propNode, copiedNode);
+            }
+        }
+        
+        SGPropertyNodeVec cameras(cgroupNode->getChildren("camera"));
+        SGPropertyNode* masterCamera = 0;
+        SGPropertyNodeVec::const_iterator it;
+        for (it = cameras.begin(); it != cameras.end(); ++it) {
+            if ((*it)->getDoubleValue("shear-x", 0.0) == 0.0
+                && (*it)->getDoubleValue("shear-y", 0.0) == 0.0) {
+                masterCamera = it->ptr();
+                break;
+            }
+        }
+        if (!masterCamera) {
+            WindowBuilder *windowBuilder = WindowBuilder::getWindowBuilder();
+            masterCamera = cgroupNode->getChild("camera", cameras.size(), true);
+            setValue(masterCamera->getNode("window/name", true),
+                     windowBuilder->getDefaultWindowName());
+        }
+        SGPropertyNode* nameNode = masterCamera->getNode("window/name");
+        if (nameNode)
+            setValue(cgroupNode->getNode("gui/window/name", true),
+                     nameNode->getStringValue());
+    }
+    
+    CameraGroup* cgroup = buildCameraGroup(viewer, cgroupNode);
+    setDefault(cgroup);
 }
+    
+} // of namespace flightgear
+