]> git.mxchange.org Git - flightgear.git/commitdiff
Work on making 2D panels act like standard scene-graph elements for picking and drawing.
authorJames Turner <zakalawe@mac.com>
Mon, 2 Jan 2012 23:16:18 +0000 (23:16 +0000)
committerJames Turner <zakalawe@mac.com>
Fri, 16 Mar 2012 17:22:18 +0000 (17:22 +0000)
(This includes 2.5D panels in the 3D scene)

12 files changed:
src/Cockpit/panel.cxx
src/Cockpit/panel.hxx
src/Input/FGMouseInput.cxx
src/Main/CameraGroup.cxx
src/Main/CameraGroup.hxx
src/Main/globals.cxx
src/Main/globals.hxx
src/Main/main.cxx
src/Main/renderer.cxx
src/Main/renderer.hxx
src/Model/panelnode.cxx
src/Model/panelnode.hxx

index 3ac15621ec80db5471284feae7c2b0e50896171e..d3f74940b30e957a4c1dda88737573c61b594d93 100644 (file)
@@ -204,7 +204,8 @@ FGPanel::FGPanel ()
     _flipx(fgGetNode("/sim/panel/flip-x", true)),
     _xsize_node(fgGetNode("/sim/startup/xsize", true)),
     _ysize_node(fgGetNode("/sim/startup/ysize", true)),
-    _enable_depth_test(false)
+    _enable_depth_test(false),
+    _drawPanelHotspots("/sim/panel-hotspots")
 {
 }
 
@@ -265,68 +266,24 @@ FGPanel::unbind ()
 
 
 void
-FGPanel::update (double dt)
+FGPanel::update (double /* dt */)
 {
-  std::cout << "OSGFIXME" << std::endl;
+  updateMouseDelay();
 }
 
-void
-FGPanel::update (osg::State& state, GLfloat winx, GLfloat winw, GLfloat winy, GLfloat winh)
-{
-  using namespace osg;
-                               // Calculate accelerations
-                               // and jiggle the panel accordingly
-                               // The factors and bounds are just
-                               // initial guesses; using sqrt smooths
-                               // out the spikes.
-  double x_offset = _x_offset->getIntValue();
-  double y_offset = _y_offset->getIntValue();
-
-
-  glMatrixMode(GL_PROJECTION);
-  glPushMatrix();
-  Matrixf proj;
-  if ( _flipx->getBoolValue() ) {
-      proj = Matrixf::ortho2D(winx + winw, winx, winy + winh, winy); /* up side down */
-  } else {
-      proj = Matrixf::ortho2D(winx, winx + winw, winy, winy + winh); /* right side up */
-  }
-  glLoadMatrix(proj.ptr());
-  
-  glMatrixMode(GL_MODELVIEW);
-  glPushMatrix();
-  glLoadIdentity();
+double
+FGPanel::getAspectScale() const
+{
+  // set corner-coordinates correctly
   
-  glTranslated(x_offset, y_offset, 0);
+  int xsize = _xsize_node->getIntValue();
+  int ysize = _ysize_node->getIntValue();
+  float aspect_adjust = get_aspect_adjust(xsize, ysize);
   
-  draw(state);
-
-  glMatrixMode(GL_PROJECTION);
-  glPopMatrix();
-  glMatrixMode(GL_MODELVIEW);
-  glPopMatrix();
-}
-
-/**
- * Update the panel.
- */
-void
-FGPanel::update (osg::State& state)
-{
-                               // Do nothing if the panel isn't visible.
-    if ( !fgPanelVisible() ) {
-        return;
-    }
-
-    updateMouseDelay();
-
-                               // Now, draw the panel
-    float aspect_adjust = get_aspect_adjust(_xsize_node->getIntValue(),
-                                            _ysize_node->getIntValue());
-    if (aspect_adjust <1.0)
-        update(state, WIN_X, int(WIN_W * aspect_adjust), WIN_Y, WIN_H);
-    else
-        update(state, WIN_X, WIN_W, WIN_Y, int(WIN_H / aspect_adjust));
+  if (aspect_adjust < 1.0)
+    return ysize / (double) WIN_H;
+  else
+    return xsize /(double) WIN_W;  
 }
 
 /**
@@ -345,10 +302,10 @@ void FGPanel::updateMouseDelay()
     }
 }
 
-
 void
 FGPanel::draw(osg::State& state)
 {
+    
   // In 3D mode, it's possible that we are being drawn exactly on top
   // of an existing polygon.  Use an offset to prevent z-fighting.  In
   // 2D mode, this is a no-op.
@@ -363,6 +320,7 @@ FGPanel::draw(osg::State& state)
     panelStateSet->setMode(GL_LIGHTING, osg::StateAttribute::OFF);
     panelStateSet->setMode(GL_BLEND, osg::StateAttribute::ON);
     panelStateSet->setMode(GL_ALPHA_TEST, osg::StateAttribute::ON);
+  
     osg::Material* material = new osg::Material;
     material->setColorMode(osg::Material::AMBIENT_AND_DIFFUSE);
     material->setDiffuse(osg::Material::FRONT_AND_BACK, osg::Vec4(1, 1, 1, 1));
@@ -370,6 +328,7 @@ FGPanel::draw(osg::State& state)
     material->setSpecular(osg::Material::FRONT_AND_BACK, osg::Vec4(0, 0, 0, 1));
     material->setEmission(osg::Material::FRONT_AND_BACK, osg::Vec4(0, 0, 0, 1));
     panelStateSet->setAttribute(material);
+    
     panelStateSet->setMode(GL_CULL_FACE, osg::StateAttribute::ON);
     panelStateSet->setAttributeAndModes(new osg::CullFace(osg::CullFace::BACK));
     panelStateSet->setAttributeAndModes(new osg::Depth(osg::Depth::LEQUAL));
@@ -495,7 +454,7 @@ FGPanel::draw(osg::State& state)
 
   // Draw yellow "hotspots" if directed to.  This is a panel authoring
   // feature; not intended to be high performance or to look good.
-  if ( fgGetBool("/sim/panel-hotspots") ) {
+  if ( _drawPanelHotspots ) {
     static osg::ref_ptr<osg::StateSet> hotspotStateSet;
     if (!hotspotStateSet.valid()) {
       hotspotStateSet = new osg::StateSet;
index ab4f5264fabee673544d1a7ba01370b312607c45..424800e65717b4c68bc5605dd729ed2a2da81021 100644 (file)
@@ -27,6 +27,8 @@
 
 #include <simgear/compiler.h>
 #include <simgear/props/props.hxx>
+#include <simgear/props/PropertyObject.hxx>
+
 #include <simgear/structure/subsystem_mgr.hxx>
 #include <simgear/structure/SGBinding.hxx>
 #include <simgear/math/interpolater.hxx>
@@ -128,8 +130,9 @@ public:
   virtual void unbind ();
   virtual void draw (osg::State& state);
   virtual void update (double);
-  void update (osg::State& state);
-  virtual void update (osg::State& state, GLfloat winx, GLfloat winw, GLfloat winy, GLfloat winh);
+  //void update (osg::State& state);
+  //virtual void update (osg::State& state, GLfloat winx, GLfloat winw, GLfloat winy, GLfloat winh);
 
   virtual void updateMouseDelay();
 
@@ -174,6 +177,8 @@ public:
 
   bool getAutohide(void) const { return _autohide; };
   void setAutohide(bool enable) { _autohide = enable; };
+  
+  double getAspectScale() const;
 
 private:
   void setupVirtualCockpit();
@@ -203,6 +208,8 @@ private:
   instrument_list_type _instruments;
   bool _enable_depth_test;
   bool _autohide;
+  
+  SGPropObjBool _drawPanelHotspots;
 };
 
 
index 927106f47c490543a4fa6425af1ca8318b931c0a..8ebdb6becffdd8419e97a137fe794e3cc595ac9a 100644 (file)
@@ -276,14 +276,9 @@ void FGMouseInput::doMouseClick (int b, int updown, int x, int y, bool mainWindo
   }
 
   if (mode.pass_through) {
+    // remove once PUI uses standard picking mechanism
     if (0 <= x && 0 <= y && puMouse(b, updown, x, y))
       return;
-    else if (0 <= x && 0 <= y && (globals->get_current_panel() != 0) &&
-             globals->get_current_panel()->getVisibility() &&
-             globals->get_current_panel()->doMouseAction(b, updown, x, y))
-      return;
-    else if (0 <= x && 0 <= y && fgHandle3DPanelMouseEvent(b, updown, x, y))
-      return;
     else {
       // pui didn't want the click event so compute a
       // scenegraph intersection point corresponding to the mouse click
index 135c67604229516763e68e0681fabb9a0e7ae355..cac2bfa5e2183dfce4f3bc16e222f44b7cf9de92 100644 (file)
@@ -281,13 +281,19 @@ void CameraGroup::update(const osg::Vec3d& position,
 #endif
         Camera* camera = info->camera.get();
         Matrix viewMatrix;
-        if ((info->flags & VIEW_ABSOLUTE) != 0)
+      
+        if (info->flags & GUI) {
+          viewMatrix = osg::Matrix(); // identifty transform on the GUI camera
+        } else if ((info->flags & VIEW_ABSOLUTE) != 0)
             viewMatrix = slave._viewOffset;
         else
             viewMatrix = masterView * slave._viewOffset;
         camera->setViewMatrix(viewMatrix);
         Matrix projectionMatrix;
-        if ((info->flags & PROJECTION_ABSOLUTE) != 0) {
+        
+        if (info->flags & GUI) {
+          projectionMatrix = osg::Matrix::ortho2D(0, info->width, 0, info->height);
+        } else if ((info->flags & PROJECTION_ABSOLUTE) != 0) {
             if (info->flags & ENABLE_MASTER_ZOOM) {
                 if (info->relativeCameraParent < _cameras.size()) {
                     // template projection matrix and view matrix of the current camera
@@ -920,7 +926,7 @@ CameraInfo* CameraGroup::buildGUICamera(SGPropertyNode* cameraNode,
     camera->setCullingMode(osg::CullSettings::NO_CULLING);
     camera->setProjectionResizePolicy(Camera::FIXED);
     camera->setReferenceFrame(Transform::ABSOLUTE_RF);
-    const int cameraFlags = GUI;
+    const int cameraFlags = GUI | DO_INTERSECTION_TEST;
     CameraInfo* result = addCamera(cameraFlags, camera, Matrixd::identity(),
                                    Matrixd::identity(), false);
     SGPropertyNode* viewportNode = cameraNode->getNode("viewport", true);
@@ -987,72 +993,103 @@ void CameraGroup::resized()
     }
 }
 
+const CameraInfo* CameraGroup::getGUICamera() const
+{
+    ConstCameraIterator result
+        = std::find_if(camerasBegin(), camerasEnd(),
+                   FlagTester<CameraInfo>(GUI));
+    if (result == camerasEnd()) {
+        return NULL;
+    }
+
+    return *result;
+}
+  
 Camera* getGUICamera(CameraGroup* cgroup)
 {
-    CameraGroup::CameraIterator end = cgroup->camerasEnd();
-    CameraGroup::CameraIterator result
-        = std::find_if(cgroup->camerasBegin(), end,
-                       FlagTester<CameraInfo>(CameraGroup::GUI));
-    if (result != end)
-        return (*result)->camera.get();
-    else
-        return 0;
+    const CameraInfo* info = cgroup->getGUICamera();
+    if (!info) {
+        return NULL;
+    }
+    
+    return info->camera.get();
 }
 
+static bool computeCameraIntersection(const CameraInfo* cinfo,
+                                      const osgGA::GUIEventAdapter* ea,
+                                      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->camera.get();
+  if (camera->getGraphicsContext() != ea->getGraphicsContext())
+    return false;
+  
+  const Viewport* viewport = camera->getViewport();
+  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))
+    return false;
+  
+  Vec4d start(x, y, 0.0, 1.0);
+  Vec4d end(x, y, 1.0, 1.0);
+  Matrix windowMat = viewport->computeWindowMatrix();
+  Matrix startPtMat = Matrix::inverse(camera->getProjectionMatrix()
+                                      * windowMat);
+  Matrix endPtMat;
+  if (!cinfo->farCamera.valid() || cinfo->farCamera->getNodeMask() == 0)
+    endPtMat = startPtMat;
+  else
+    endPtMat = Matrix::inverse(cinfo->farCamera->getProjectionMatrix()
+                               * windowMat);
+  start = start * startPtMat;
+  start /= start.w();
+  end = end * endPtMat;
+  end /= end.w();
+  ref_ptr<LineSegmentIntersector> picker
+  = new LineSegmentIntersector(Intersector::VIEW,
+                               Vec3d(start.x(), start.y(), start.z()),
+                               Vec3d(end.x(), end.y(), end.z()));
+  osgUtil::IntersectionVisitor iv(picker.get());
+  const_cast<Camera*>(camera)->accept(iv);
+  if (picker->containsIntersections()) {
+    intersections = picker->getIntersections();
+    return true;
+  }
+  
+  return false;
+}
+  
 bool computeIntersections(const CameraGroup* cgroup,
                           const osgGA::GUIEventAdapter* ea,
                           osgUtil::LineSegmentIntersector::Intersections& intersections)
 {
-    using osgUtil::Intersector;
-    using osgUtil::LineSegmentIntersector;
-    double x, y;
-    eventToWindowCoords(ea, x, y);
+    // test the GUI first
+    const CameraInfo* guiCamera = cgroup->getGUICamera();
+    if (guiCamera && computeCameraIntersection(guiCamera, ea, intersections))
+        return true;
+    
     // Find camera that contains event
     for (CameraGroup::ConstCameraIterator iter = cgroup->camerasBegin(),
              e = cgroup->camerasEnd();
          iter != e;
          ++iter) {
         const CameraInfo* cinfo = iter->get();
-        if ((cinfo->flags & CameraGroup::DO_INTERSECTION_TEST) == 0)
-            continue;
-        const Camera* camera = cinfo->camera.get();
-        if (camera->getGraphicsContext() != ea->getGraphicsContext())
+        if (cinfo == guiCamera)
             continue;
-        const Viewport* viewport = camera->getViewport();
-        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))
-            continue;
-        Vec4d start(x, y, 0.0, 1.0);
-        Vec4d end(x, y, 1.0, 1.0);
-        Matrix windowMat = viewport->computeWindowMatrix();
-        Matrix startPtMat = Matrix::inverse(camera->getProjectionMatrix()
-                                            * windowMat);
-        Matrix endPtMat;
-        if (!cinfo->farCamera.valid() || cinfo->farCamera->getNodeMask() == 0)
-            endPtMat = startPtMat;
-        else
-            endPtMat = Matrix::inverse(cinfo->farCamera->getProjectionMatrix()
-                                       * windowMat);
-        start = start * startPtMat;
-        start /= start.w();
-        end = end * endPtMat;
-        end /= end.w();
-        ref_ptr<LineSegmentIntersector> picker
-            = new LineSegmentIntersector(Intersector::VIEW,
-                                         Vec3d(start.x(), start.y(), start.z()),
-                                         Vec3d(end.x(), end.y(), end.z()));
-        osgUtil::IntersectionVisitor iv(picker.get());
-        const_cast<Camera*>(camera)->accept(iv);
-        if (picker->containsIntersections()) {
-            intersections = picker->getIntersections();
+        
+        if (computeCameraIntersection(cinfo, ea, intersections))
             return true;
-        } else {
-            break;
-        }
     }
+  
     intersections.clear();
     return false;
 }
index a74895e467f7946e155d1199815c559f30b8a950..177782d9f840aef17e5aea94aab8d14d3ff3f3b6 100644 (file)
@@ -214,6 +214,11 @@ public:
      * get aspect ratio of master camera's viewport
      */
     double getMasterAspectRatio() const;
+  
+    /**
+     * find the GUI camera if one is defined 
+     */
+    const CameraInfo* getGUICamera() const;
 protected:
     CameraList _cameras;
     osg::ref_ptr<osgViewer::Viewer> _viewer;
index bbe5dc9fdcfb7e253a947438c3c2ba37626cc9a0..3f624f6bbfff9b4a7b057eba8c8166fa9b76dcf7 100644 (file)
@@ -514,5 +514,12 @@ void FGGlobals::set_warp_delta( long int d )
 {
   fgSetInt("/sim/time/warp-delta", d);
 }
+
+void FGGlobals::set_current_panel( FGPanel *cp )
+{
+  current_panel = cp;
+// poke the renderer to rebuild the scene node as necessary
+  get_renderer()->panelChanged();
+}
     
 // end of globals.cxx
index 297d9c576cb27609a14042d72a32b0c4c9a72f35..01f7281070dd925be8370500c4a5ff9e7b7c61d6 100644 (file)
@@ -243,7 +243,7 @@ public:
     inline void set_ATC_mgr( FGATCMgr *a ) {ATC_mgr = a; }
 
     inline FGPanel *get_current_panel() const { return current_panel; }
-    inline void set_current_panel( FGPanel *cp ) { current_panel = cp; }
+    void set_current_panel( FGPanel *cp );
 
     inline FGControls *get_controls() const { return controls; }
     inline void set_controls( FGControls *c ) { controls = c; }
index 6e70ff89428033a5b4a3dbd1cbddd437c885ec3e..5535060d557180964e19f2df109d27441868cc9c 100644 (file)
@@ -259,19 +259,6 @@ struct GeneralInitOperation : public GraphicsContextOperation
     }
 };
 
-
-osg::Node* load_panel(SGPropertyNode *n)
-{
-    osg::Geode* geode = new osg::Geode;
-    geode->addDrawable(new FGPanelNode(n));
-    return geode;
-}
-
-SGPath resolve_path(const std::string& s)
-{
-  return globals->resolve_maybe_aircraft_path(s);
-}
-
 }
 
 // This is the top level master main function that is registered as
@@ -349,7 +336,7 @@ static void fgIdleFunction ( void ) {
         ////////////////////////////////////////////////////////////////////
         globals->set_matlib( new SGMaterialLib );
         simgear::SGModelLib::init(globals->get_fg_root(), globals->get_props());
-        simgear::SGModelLib::setPanelFunc(load_panel);
+        simgear::SGModelLib::setPanelFunc(FGPanelNode::load);
 
         ////////////////////////////////////////////////////////////////////
         // Initialize the TG scenery subsystem.
index 6e7e81d601332a7aaf4e6e3838479e1ef3289e8b..864f0ababa517d4eb51f9df9139676fe4204492a 100644 (file)
@@ -85,6 +85,7 @@
 #include <Time/light.hxx>
 #include <Time/light.hxx>
 #include <Cockpit/panel.hxx>
+
 #include <Model/panelnode.hxx>
 #include <Model/modelmgr.hxx>
 #include <Model/acmodel.hxx>
@@ -179,9 +180,9 @@ public:
 private:
 };
 
-class SGHUDAndPanelDrawable : public osg::Drawable {
+class SGHUDDrawable : public osg::Drawable {
 public:
-  SGHUDAndPanelDrawable()
+  SGHUDDrawable()
   {
     // Dynamic stuff, do not store geometry
     setUseDisplayList(false);
@@ -209,24 +210,16 @@ public:
 
     glPushAttrib(GL_ALL_ATTRIB_BITS);
     glPushClientAttrib(~0u);
-
+      
     HUD *hud = static_cast<HUD*>(globals->get_subsystem("hud"));
     hud->draw(state);
 
-    // update the panel subsystem
-    if ( globals->get_current_panel() != NULL )
-        globals->get_current_panel()->update(state);
-    // We don't need a state here - can be safely removed when we can pick
-    // correctly
-    fgUpdate3DPanels();
-
     glPopClientAttrib();
     glPopAttrib();
-
   }
 
-  virtual osg::Object* cloneType() const { return new SGHUDAndPanelDrawable; }
-  virtual osg::Object* clone(const osg::CopyOp&) const { return new SGHUDAndPanelDrawable; }
+  virtual osg::Object* cloneType() const { return new SGHUDDrawable; }
+  virtual osg::Object* clone(const osg::CopyOp&) const { return new SGHUDDrawable; }
   
 private:
 };
@@ -382,6 +375,25 @@ static osg::ref_ptr<osg::Group> mRealRoot = new osg::Group;
 
 static osg::ref_ptr<osg::Group> mRoot = new osg::Group;
 
+static osg::ref_ptr<osg::Switch> panelSwitch;
+                                    
+                                    
+// update callback for the switch node controlling the 2D panel
+class FGPanelSwitchCallback : public osg::NodeCallback {
+public:
+    virtual void operator()(osg::Node* node, osg::NodeVisitor* nv)
+    {
+        assert(dynamic_cast<osg::Switch*>(node));
+        osg::Switch* sw = static_cast<osg::Switch*>(node);
+        
+        bool enabled = fgPanelVisible();
+        sw->setValue(0, enabled);
+        if (!enabled)
+            return;
+        traverse(node, nv);
+    }
+};
+
 #ifdef FG_JPEG_SERVER
 static void updateRenderer()
 {
@@ -627,11 +639,30 @@ FGRenderer::setupView( void )
     // plug in the GUI
     osg::Camera* guiCamera = getGUICamera(CameraGroup::getDefault());
     if (guiCamera) {
+        
         osg::Geode* geode = new osg::Geode;
         geode->addDrawable(new SGPuDrawable);
-        geode->addDrawable(new SGHUDAndPanelDrawable);
+        geode->addDrawable(new SGHUDDrawable);
         guiCamera->addChild(geode);
+      
+        panelSwitch = new osg::Switch;
+        osg::StateSet* stateSet = panelSwitch->getOrCreateStateSet();
+        stateSet->setRenderBinDetails(1000, "RenderBin");
+        
+        // speed optimization?
+        stateSet->setMode(GL_CULL_FACE, osg::StateAttribute::OFF);
+        stateSet->setAttribute(new osg::BlendFunc(osg::BlendFunc::SRC_ALPHA, osg::BlendFunc::ONE_MINUS_SRC_ALPHA));
+        stateSet->setMode(GL_BLEND, osg::StateAttribute::ON);
+        stateSet->setMode(GL_FOG, osg::StateAttribute::OFF);
+        stateSet->setMode(GL_DEPTH_TEST, osg::StateAttribute::OFF);
+        
+        
+        panelSwitch->setUpdateCallback(new FGPanelSwitchCallback);
+        panelChanged();
+        
+        guiCamera->addChild(panelSwitch.get());
     }
+    
     osg::Switch* sw = new osg::Switch;
     sw->setUpdateCallback(new FGScenerySwitchCallback);
     sw->addChild(mRoot.get());
@@ -647,6 +678,20 @@ FGRenderer::setupView( void )
     stateSet->setAttributeAndModes(new osg::Program, osg::StateAttribute::ON);
 }
 
+void FGRenderer::panelChanged()
+{
+    if (!panelSwitch) {
+        return;
+    }
+    
+    osg::Node* n = FGPanelNode::createNode(globals->get_current_panel());
+    if (panelSwitch->getNumChildren()) {
+        panelSwitch->setChild(0, n);
+    } else {
+        panelSwitch->addChild(n);
+    }
+}
+                                    
 // Update all Visuals (redraws anything graphics related)
 void
 FGRenderer::update( ) {
index d3b96bbd93038e2d44b0430395f60998470e5488..29d9a699642706dd9d5d13c776212e8e62ebda59 100644 (file)
@@ -74,6 +74,10 @@ public:
 
     SGSky* getSky() const { return _sky; }
     
+    /**
+     * inform the renderer when the global (2D) panel is changed
+     */
+    void panelChanged();
 protected:
     osg::ref_ptr<osgViewer::Viewer> viewer;
     osg::ref_ptr<flightgear::FGEventHandler> eventHandler;
index 7161daa2c57029eb44a1eeacc02fb9274079bdbf..46988d8e61597c87c5fa40984164d13ef79c5daf 100644 (file)
@@ -2,44 +2,68 @@
 #  include <config.h>
 #endif
 
-#include <simgear/compiler.h>
-#include <simgear/structure/exception.hxx>
+#include "panelnode.hxx"
 
 #include <vector>
 #include <algorithm>
 
-#include <Cockpit/panel.hxx>
-#include <Cockpit/panel_io.hxx>
-#include "panelnode.hxx"
+#include <osg/Geode>
 
-using std::vector;
+#include <simgear/compiler.h>
+#include <simgear/structure/exception.hxx>
+#include <simgear/debug/logstream.hxx>
 
+#include <simgear/scene/util/OSGMath.hxx>
+#include <simgear/scene/util/SGPickCallback.hxx>
+#include <simgear/scene/util/SGSceneUserData.hxx>
+#include <simgear/scene/util/SGNodeMasks.hxx>
 
-// Static (!) handling for all 3D panels in the program.
-// OSGFIXME: Put the panel as different elements in the scenegraph.
-// Then just pick in that scenegraph.
-vector<FGPanelNode*> all_3d_panels;
-bool fgHandle3DPanelMouseEvent( int button, int updown, int x, int y )
-{
-    for ( unsigned int i = 0; i < all_3d_panels.size(); i++ ) {
-        if ( all_3d_panels[i]->doMouseAction(button, updown, x, y) ) {
-            return true;
-        }
-    }
-    return false;
-}
+#include <Main/fg_os.hxx>
+#include <Cockpit/panel.hxx>
+#include <Cockpit/panel_io.hxx>
 
-void fgUpdate3DPanels()
-{
-    for ( unsigned int i = 0; i < all_3d_panels.size(); i++ ) {
-        all_3d_panels[i]->getPanel()->updateMouseDelay();
-    }
-}
 
-FGPanelNode::FGPanelNode(SGPropertyNode* props)
-{
-    int i;
+using std::vector;
 
+class FGPanelPickCallback : public SGPickCallback {
+public:
+  FGPanelPickCallback(FGPanelNode* p) :
+    panel(p)
+  {}
+  
+  virtual bool buttonPressed(int b, const Info& info)
+  {    
+    button = b;
+  // convert to panel coordinates
+    osg::Matrixd m = osg::Matrixd::inverse(panel->transformMatrix());
+    picked = toOsg(info.local) * m;
+    SG_LOG( SG_INSTR, SG_DEBUG, "panel pick: " << toSG(picked) );
+  
+  // send to the panel
+    return panel->getPanel()->doLocalMouseAction(button, MOUSE_BUTTON_DOWN, 
+                                          picked.x(), picked.y());
+  }
+  
+  virtual void update(double /* dt */)
+  {
+    panel->getPanel()->updateMouseDelay();
+  }
+  
+  virtual void buttonReleased(void)
+  {
+    panel->getPanel()->doLocalMouseAction(button, MOUSE_BUTTON_UP, 
+                                          picked.x(), picked.y());
+  }
+  
+private:
+  FGPanelNode* panel;
+  int button;
+  osg::Vec3 picked;
+};
+
+FGPanelNode::FGPanelNode(SGPropertyNode* props) :
+  _resizeToViewport(false)
+{  
     // Make an FGPanel object.  But *don't* call init() or bind() on
     // it -- those methods touch static state.
     const char *path = props->getStringValue("path");
@@ -47,33 +71,48 @@ FGPanelNode::FGPanelNode(SGPropertyNode* props)
     if (!_panel)
         throw sg_io_exception(string("Failed to load panel ") + path);
 
-    // Never mind.  We *have* to call init to make sure the static
-    // state is initialized (it's not, if there aren't any 2D
-    // panels).  This is a memory leak and should be fixed!`
-    // FIXME
-    _panel->init();
-
-    _panel->setDepthTest( props->getBoolValue("depth-test") );
-
-    // Read out the pixel-space info
-    _xmax = _panel->getWidth();
-    _ymax = _panel->getHeight();
-
     // And the corner points
     SGPropertyNode* pt = props->getChild("bottom-left");
     _bottomLeft[0] = pt->getFloatValue("x-m");
     _bottomLeft[1] = pt->getFloatValue("y-m");
     _bottomLeft[2] = pt->getFloatValue("z-m");
-
+    
     pt = props->getChild("top-left");
     _topLeft[0] = pt->getFloatValue("x-m");
     _topLeft[1] = pt->getFloatValue("y-m");
     _topLeft[2] = pt->getFloatValue("z-m");
-
+    
     pt = props->getChild("bottom-right");
     _bottomRight[0] = pt->getFloatValue("x-m");
     _bottomRight[1] = pt->getFloatValue("y-m");
     _bottomRight[2] = pt->getFloatValue("z-m");
+  
+    _panel->setDepthTest( props->getBoolValue("depth-test") );
+  
+    initWithPanel();
+}
+
+FGPanelNode::FGPanelNode(FGPanel* p) :
+  _panel(p),
+  _resizeToViewport(true)
+{
+    initWithPanel();
+}
+
+void FGPanelNode::initWithPanel()
+{
+    int i;
+
+    // Never mind.  We *have* to call init to make sure the static
+    // state is initialized (it's not, if there aren't any 2D
+    // panels).  This is a memory leak and should be fixed!`
+    // FIXME
+    _panel->init();
+
+    // Read out the pixel-space info
+    _xmax = _panel->getWidth();
+    _ymax = _panel->getHeight();
+
 
     // Now generate our transformation matrix.  For shorthand, use
     // "a", "b", and "c" as our corners and "m" as the matrix. The
@@ -103,102 +142,93 @@ FGPanelNode::FGPanelNode(SGPropertyNode* props)
         m(1,i) *= 1.0/_ymax;
     }
 
-    _lastViewport[0] = 0;
-    _lastViewport[1] = 0;
-    _lastViewport[2] = 0;
-    _lastViewport[3] = 0;
-
     dirtyBound();
-
-    // All done.  Add us to the list
-    all_3d_panels.push_back(this);
-
     setUseDisplayList(false);
+    setDataVariance(Object::DYNAMIC);
+
     getOrCreateStateSet()->setRenderingHint(osg::StateSet::TRANSPARENT_BIN);
     getOrCreateStateSet()->setMode(GL_BLEND, osg::StateAttribute::ON);
 }
 
 FGPanelNode::~FGPanelNode()
 {
-    vector<FGPanelNode*>::iterator i =
-      find(all_3d_panels.begin(), all_3d_panels.end(), this);
-    if (i != all_3d_panels.end()) {
-        all_3d_panels.erase(i);
-    }
     delete _panel;
 }
 
+osg::Matrix FGPanelNode::transformMatrix() const
+{
+  if (!_resizeToViewport) {
+    return _xform;
+  }
+  
+  double s = _panel->getAspectScale();
+  osg::Matrix m = osg::Matrix::scale(s, s, 1.0);
+  m *= osg::Matrix::translate(_panel->getXOffset(), _panel->getYOffset(), 0.0);
+
+  return m;
+}
+
 void
 FGPanelNode::drawImplementation(osg::State& state) const
-{
+{  
   osg::ref_ptr<osg::RefMatrix> mv = new osg::RefMatrix;
-  mv->set(_xform*state.getModelViewMatrix());
+  mv->set(transformMatrix() * state.getModelViewMatrix());
   state.applyModelViewMatrix(mv.get());
   
-  // Grab the matrix state, so that we can get back from screen
-  // coordinates to panel coordinates when the user clicks the
-  // mouse.
-  // OSGFIXME: we don't need that when we can really pick
-  _lastModelview = state.getModelViewMatrix();
-  _lastProjection = state.getProjectionMatrix();
-
-  const osg::Viewport* vp = state.getCurrentViewport();
-  _lastViewport[0] = vp->x();
-  _lastViewport[1] = vp->y();
-  _lastViewport[2] = vp->width();
-  _lastViewport[3] = vp->height();
-  
   _panel->draw(state);
 }
 
 osg::BoundingBox
 FGPanelNode::computeBound() const
 {
+    osg::Vec3 coords[4];
+    osg::Matrix m(transformMatrix());
+    coords[0] = m.preMult(osg::Vec3(0,0,0));
+    coords[1] = m.preMult(osg::Vec3(_xmax,0,0));
+    coords[2] = m.preMult(osg::Vec3(0,_ymax,0));
+  
     osg::BoundingBox bb;
-    bb.expandBy(_bottomLeft);
-    bb.expandBy(_bottomRight);
-    bb.expandBy(_topLeft);
+    bb.expandBy(coords[0]);
+    bb.expandBy(coords[1]);
+    bb.expandBy(coords[2]);
     return bb;
 }
 
-bool FGPanelNode::doMouseAction(int button, int updown, int x, int y)
+void FGPanelNode::accept(osg::PrimitiveFunctor& functor) const
 {
-    if (_lastViewport[2] == 0 || _lastViewport[3] == 0) {
-        // we haven't been drawn yet, presumably
-        return false;
-    }
+  osg::Vec3 coords[4];
+  osg::Matrix m(transformMatrix());
+
+  coords[0] = m.preMult(osg::Vec3(0,0,0));
+  coords[1] = m.preMult(osg::Vec3(_xmax,0,0));
+  coords[2] = m.preMult(osg::Vec3(_xmax, _ymax, 0));
+  coords[3] = m.preMult(osg::Vec3(0,_ymax,0));
+
+  functor.setVertexArray(4, coords);
+  functor.drawArrays( GL_QUADS, 0, 4);
+}
+
+static osg::Node* createGeode(FGPanelNode* panel)
+{
+    osg::Geode* geode = new osg::Geode;
+    geode->addDrawable(panel);
+    
+    geode->setNodeMask(SG_NODEMASK_PICK_BIT | SG_NODEMASK_2DPANEL_BIT);
+    
+    SGSceneUserData* userData;
+    userData = SGSceneUserData::getOrCreateSceneUserData(geode);
+    userData->setPickCallback(new FGPanelPickCallback(panel));
+    return geode;
+}
 
-    // Covert the screen coordinates to viewport coordinates in the
-    // range [0:1], then transform to OpenGL "post projection" coords
-    // in [-1:1].  Remember the difference in Y direction!
-    float vx = (x + 0.5 - _lastViewport[0]) / _lastViewport[2];
-    float vy = (y + 0.5 - _lastViewport[1]) / _lastViewport[3];
-    vx = 2*vx - 1;
-    vy = 1 - 2*vy;
-
-    // Make two vectors in post-projection coordinates at the given
-    // screen, one in the near field and one in the far field.
-    osg::Vec3 a, b;
-    a[0] = b[0] = vx;
-    a[1] = b[1] = vy;
-    a[2] =  0.75; // "Near" Z value
-    b[2] = -0.75; // "Far" Z value
-
-    // Run both vectors "backwards" through the OpenGL matrix
-    // transformation.  Remember to w-normalize the vectors!
-    osg::Matrix m = _lastModelview*_lastProjection;
-    m = osg::Matrix::inverse(m);
-
-    a = m.preMult(a);
-    b = m.preMult(b);
-
-    // And find their intersection on the z=0 plane.  The resulting X
-    // and Y coordinates are the hit location in panel coordinates.
-    float dxdz = (b[0] - a[0]) / (b[2] - a[2]);
-    float dydz = (b[1] - a[1]) / (b[2] - a[2]);
-    int panelX = (int)(a[0] - a[2]*dxdz + 0.5);
-    int panelY = (int)(a[1] - a[2]*dydz + 0.5);
-
-    return _panel->doLocalMouseAction(button, updown, panelX, panelY);
+osg::Node* FGPanelNode::createNode(FGPanel* p)
+{
+    FGPanelNode* drawable = new FGPanelNode(p);
+    return createGeode(drawable);
 }
 
+osg::Node* FGPanelNode::load(SGPropertyNode *n)
+{
+  FGPanelNode* drawable = new FGPanelNode(n);
+  return createGeode(drawable);
+}
index 9a5bf938a61bc2c41435592d18e431f74ce19025..c8db4ba29ce17cd9ba37647239678bc38050e02a 100644 (file)
@@ -8,20 +8,13 @@
 class FGPanel;
 class SGPropertyNode;
 
-// PanelNode defines an SSG leaf object that draws a FGPanel object
-// into the scene graph.  Note that this is an incomplete SSG object,
-// many methods, mostly involved with modelling and runtime
-// inspection, are unimplemented.
+// PanelNode defines an OSG drawable wrapping the 2D panel drawing code
 
-// Static mouse handler for all FGPanelNodes.  Very clumsy; this
-// should really be done through our container (an aircraft model,
-// typically).
-bool fgHandle3DPanelMouseEvent(int button, int updown, int x, int y);
-void fgUpdate3DPanels();
-
-class FGPanelNode : public osg::Drawable // OSGFIXME 
+class FGPanelNode : public osg::Drawable
 {
 public:
+    FGPanelNode(FGPanel* panel);
+  
     FGPanelNode(SGPropertyNode* props);
     virtual ~FGPanelNode();
 
@@ -29,17 +22,30 @@ public:
     virtual osg::Object* cloneType() const { return 0; }
     virtual osg::Object* clone(const osg::CopyOp& copyop) const { return 0; }        
 
-    bool doMouseAction(int button, int updown, int x, int y);
-
     FGPanel* getPanel() { return _panel; }
 
     virtual void drawImplementation(osg::RenderInfo& renderInfo) const
     { drawImplementation(*renderInfo.getState()); }
+  
     void drawImplementation(osg::State& state) const;
     virtual osg::BoundingBox computeBound() const;
 
+    /** Return true, FGPanelNode does support accept(PrimitiveFunctor&). */
+    virtual bool supports(const osg::PrimitiveFunctor&) const { return true; }
+  
+    virtual void accept(osg::PrimitiveFunctor& functor) const;
+  
+    static osg::Node* load(SGPropertyNode *n);
+    static osg::Node* createNode(FGPanel* panel);
+  
+    osg::Matrix transformMatrix() const;
+  
 private:
+    void initWithPanel();
+    
     FGPanel* _panel;
+  
+    bool _resizeToViewport;
 
     // Panel corner coordinates
     osg::Vec3 _bottomLeft, _topLeft, _bottomRight;
@@ -51,13 +57,6 @@ private:
     // The matrix that results, which transforms 2D x/y panel
     // coordinates into 3D coordinates of the panel quadrilateral.
     osg::Matrix _xform;
-
-    // The matrix transformation state that was active the last time
-    // we were rendered.  Used by the mouse code to compute
-    // intersections.
-    mutable osg::Matrix _lastModelview;
-    mutable osg::Matrix _lastProjection;
-    mutable double _lastViewport[4];
 };