]> git.mxchange.org Git - flightgear.git/commitdiff
Allow Canvas placed on 3D objects receiving mouse events.
authorThomas Geymayer <tomgey@gmail.com>
Thu, 9 May 2013 19:34:58 +0000 (21:34 +0200)
committerThomas Geymayer <tomgey@gmail.com>
Thu, 9 May 2013 19:38:40 +0000 (21:38 +0200)
 - Add option 'capture-events' to canvas aircraft and scenery
   placements to allow events being forwarded to the respective
   canvas.
 - Clean up and restructure parts of the mouse event/picking
   handling to support forwarding events to canvasses.

src/Canvas/canvas_mgr.cxx
src/Canvas/gui_mgr.cxx
src/Cockpit/od_gauge.cxx
src/Cockpit/od_gauge.hxx
src/Input/FGMouseInput.cxx
src/Viewer/renderer.cxx

index b57dcaeb91952137159e1672ad1c8baa55f3dc0d..29a1228bcbbaaf152792e36330e16985c95790a9 100644 (file)
@@ -50,7 +50,8 @@ static sc::Placements addSceneObjectPlacement( SGPropertyNode* placement,
     model_data->getNode(),
     placement,
     canvas->getTexture(),
-    canvas->getCullCallback()
+    canvas->getCullCallback(),
+    canvas
   );
 }
 
@@ -76,7 +77,8 @@ CanvasMgr::CanvasMgr():
       &FGODGauge::set_aircraft_texture,
       _1,
       boost::bind(&sc::Canvas::getTexture, _2),
-      boost::bind(&sc::Canvas::getCullCallback, _2)
+      boost::bind(&sc::Canvas::getCullCallback, _2),
+      _2
     )
   );
 
index 91424d9b2da5a5e9b6b4f65f299304c5947fe18e..e76571e3adbf1b7e4f429d038b46ca01a293a4e8 100644 (file)
@@ -294,8 +294,7 @@ bool GUIMgr::handleMouse(const osgGA::GUIEventAdapter& ea)
     return false;
 
   namespace sc = simgear::canvas;
-  sc::MouseEventPtr event(new sc::MouseEvent);
-  event->time = ea.getTime();
+  sc::MouseEventPtr event(new sc::MouseEvent(ea));
 
   event->screen_pos.x() = 0.5 * (ea.getXnormalized() + 1) * _width + 0.5;
   event->screen_pos.y() = 0.5 * (ea.getYnormalized() + 1) * _height + 0.5;
@@ -310,9 +309,6 @@ bool GUIMgr::handleMouse(const osgGA::GUIEventAdapter& ea)
   _last_y = event->getScreenY();
 
   event->client_pos = event->screen_pos;
-  event->button = ea.getButton();
-  event->state = ea.getButtonMask();
-  event->mod = ea.getModKeyMask();
 
   if( !_resize_window.expired() )
   {
index ff26b8f0c09cfea1dbf08750e4c3470d16ef7e70..f2343a0aa5a8d90e363fd592b6e311694ba29bb6 100644 (file)
@@ -36,7 +36,6 @@
 #include <osg/Camera>
 #include <osg/Geode>
 #include <osg/NodeVisitor>
-#include <osg/Material>
 #include <osg/Matrix>
 #include <osg/PolygonMode>
 #include <osg/ShadeModel>
@@ -45,6 +44,7 @@
 
 #include <osgDB/FileNameUtils>
 
+#include <simgear/canvas/CanvasObjectPlacement.hxx>
 #include <simgear/scene/material/EffectGeode.hxx>
 #include <simgear/scene/util/RenderConstants.hxx>
 
@@ -86,12 +86,15 @@ class ReplaceStaticTextureVisitor:
                                  osg::Texture2D* new_texture ):
         osg::NodeVisitor(osg::NodeVisitor::TRAVERSE_ALL_CHILDREN),
         _tex_name( osgDB::getSimpleFileName(name) ),
-        _new_texture(new_texture)
+        _new_texture(new_texture),
+        _cull_callback(0)
     {}
 
     ReplaceStaticTextureVisitor( SGPropertyNode* placement,
                                  osg::Texture2D* new_texture,
-                                 osg::NodeCallback* cull_callback = 0 ):
+                                 osg::NodeCallback* cull_callback = 0,
+                                 const simgear::canvas::CanvasWeakPtr& canvas =
+                                   simgear::canvas::CanvasWeakPtr() ):
         osg::NodeVisitor(osg::NodeVisitor::TRAVERSE_ALL_CHILDREN),
         _tex_name( osgDB::getSimpleFileName(
           placement->getStringValue("texture"))
@@ -100,7 +103,8 @@ class ReplaceStaticTextureVisitor:
         _parent_name( placement->getStringValue("parent") ),
         _node(placement),
         _new_texture(new_texture),
-        _cull_callback(cull_callback)
+        _cull_callback(cull_callback),
+        _canvas(canvas)
     {
       if(    _tex_name.empty()
           && _node_name.empty()
@@ -200,7 +204,7 @@ class ReplaceStaticTextureVisitor:
                                         osg::StateAttribute::ON );
 
         _placements.push_back( simgear::canvas::PlacementPtr(
-          new ObjectPlacement(_node, group)
+          new simgear::canvas::ObjectPlacement(_node, group, _canvas)
         ));
 
         SG_LOG
@@ -218,77 +222,6 @@ class ReplaceStaticTextureVisitor:
 
   protected:
 
-    class ObjectPlacement:
-      public simgear::canvas::Placement
-    {
-      public:
-
-        ObjectPlacement( SGPropertyNode* node,
-                         GroupPtr group ):
-          Placement(node),
-          _group(group)
-        {
-          // TODO make more generic and extendable for more properties
-          if( node->hasValue("emission") )
-            setEmission( node->getFloatValue("emission") );
-        }
-
-        virtual bool childChanged(SGPropertyNode* node)
-        {
-          if( node->getParent() != _node )
-            return false;
-
-          if( node->getNameString() == "emission" )
-            setEmission( node->getFloatValue() );
-          else
-            return false;
-
-          return true;
-        }
-
-        void setEmission(float emit)
-        {
-          emit = SGMiscf::clip(emit, 0, 1);
-
-          if( !_material )
-          {
-            _material = new osg::Material;
-            _material->setColorMode(osg::Material::OFF);
-            _material->setDataVariance(osg::Object::DYNAMIC);
-            _group->getOrCreateStateSet()
-                  ->setAttribute(_material, ( osg::StateAttribute::ON
-                                            | osg::StateAttribute::OVERRIDE ) );
-          }
-
-          _material->setEmission(
-            osg::Material::FRONT_AND_BACK,
-            osg::Vec4(emit, emit, emit, emit)
-          );
-        }
-
-        /**
-         * Remove placement from the scene
-         */
-        virtual ~ObjectPlacement()
-        {
-          assert( _group->getNumChildren() == 1 );
-          osg::Node *child = _group->getChild(0);
-
-          if( _group->getNumParents() )
-          {
-            osg::Group *parent = _group->getParent(0);
-            parent->addChild(child);
-            parent->removeChild(_group);
-          }
-
-          _group->removeChild(child);
-        }
-
-      private:
-        GroupPtr            _group;
-        MaterialPtr         _material;
-    };
-
     std::string _tex_name,      ///<! Name of texture to be replaced
                 _node_name,     ///<! Only replace if node name matches
                 _parent_name;   ///<! Only replace if any parent node matches
@@ -298,7 +231,8 @@ class ReplaceStaticTextureVisitor:
     osg::Texture2D     *_new_texture;
     osg::NodeCallback  *_cull_callback;
 
-    simgear::canvas::Placements _placements;
+    simgear::canvas::CanvasWeakPtr  _canvas;
+    simgear::canvas::Placements     _placements;
 };
 
 //------------------------------------------------------------------------------
@@ -330,9 +264,13 @@ simgear::canvas::Placements
 FGODGauge::set_texture( osg::Node* branch,
                         SGPropertyNode* placement,
                         osg::Texture2D* new_texture,
-                        osg::NodeCallback* cull_callback )
+                        osg::NodeCallback* cull_callback,
+                        const simgear::canvas::CanvasWeakPtr& canvas )
 {
-  ReplaceStaticTextureVisitor visitor(placement, new_texture, cull_callback);
+  ReplaceStaticTextureVisitor visitor( placement,
+                                       new_texture,
+                                       cull_callback,
+                                       canvas );
   branch->accept(visitor);
   return visitor.getPlacements();
 }
@@ -341,13 +279,15 @@ FGODGauge::set_texture( osg::Node* branch,
 simgear::canvas::Placements
 FGODGauge::set_aircraft_texture( SGPropertyNode* placement,
                                  osg::Texture2D* new_texture,
-                                 osg::NodeCallback* cull_callback )
+                                 osg::NodeCallback* cull_callback,
+                                 const simgear::canvas::CanvasWeakPtr& canvas )
 {
   return set_texture
   (
     globals->get_scenery()->get_aircraft_branch(),
     placement,
     new_texture,
-    cull_callback
+    cull_callback,
+    canvas
   );
 }
index 2578651945508992f7da59a32e33fc7619f08b80..e819a8e3b321b97d07559c1066b8470310f775f7 100644 (file)
@@ -86,7 +86,9 @@ class FGODGauge:
     set_texture( osg::Node* branch,
                  SGPropertyNode* placement,
                  osg::Texture2D* new_texture,
-                 osg::NodeCallback* cull_callback = 0 );
+                 osg::NodeCallback* cull_callback = 0,
+                 const simgear::canvas::CanvasWeakPtr& canvas =
+                   simgear::canvas::CanvasWeakPtr() );
 
     /**
      * Replace an opengl texture name inside the aircraft scene graph.
@@ -101,7 +103,9 @@ class FGODGauge:
     simgear::canvas::Placements
     set_aircraft_texture( SGPropertyNode* placement,
                           osg::Texture2D* new_texture,
-                          osg::NodeCallback* cull_callback = 0 );
+                          osg::NodeCallback* cull_callback = 0,
+                          const simgear::canvas::CanvasWeakPtr& canvas =
+                            simgear::canvas::CanvasWeakPtr() );
 
 };
 
index ffd9c9e154c496a79ba71f595b190929f5358399..218b3342b727b1e95b3486b24c7f37434c14e681 100644 (file)
@@ -49,13 +49,19 @@ using std::ios_base;
 const int MAX_MICE = 1;
 const int MAX_MOUSE_BUTTONS = 8;
 
+typedef std::vector<SGSceneryPick> SGSceneryPicks;
+typedef SGSharedPtr<SGPickCallback> SGPickCallbackPtr;
+typedef std::list<SGPickCallbackPtr> SGPickCallbackList;
+
 ////////////////////////////////////////////////////////////////////////
 
 /**
  * List of currently pressed mouse button events
  */
-class ActivePickCallbacks : public std::map<int, std::list<SGSharedPtr<SGPickCallback> > > {
-public:
+class ActivePickCallbacks:
+  public std::map<int, SGPickCallbackList>
+{
+  public:
     void update( double dt, unsigned int keyModState );
     void init( int button, const osgGA::GUIEventAdapter* ea );
 };
@@ -71,14 +77,14 @@ void ActivePickCallbacks::init( int button, const osgGA::GUIEventAdapter* ea )
   // That is they get sorted by distance and by scenegraph depth.
   // The nearest one is the first one and the deepest
   // (the most specialized one in the scenegraph) is the first.
-  std::vector<SGSceneryPick> pickList;
+  SGSceneryPicks pickList;
   if (!globals->get_renderer()->pick(pickList, windowPos)) {
     return;
   }
 
-  std::vector<SGSceneryPick>::const_iterator i;
+  SGSceneryPicks::const_iterator i;
   for (i = pickList.begin(); i != pickList.end(); ++i) {
-    if (i->callback->buttonPressed(button, ea, i->info)) {
+    if (i->callback->buttonPressed(button, *ea, i->info)) {
         (*this)[button].push_back(i->callback);
         return;
     }
@@ -89,7 +95,7 @@ void ActivePickCallbacks::update( double dt, unsigned int keyModState )
 {
   // handle repeatable mouse press events
   for( iterator mi = begin(); mi != end(); ++mi ) {
-    std::list<SGSharedPtr<SGPickCallback> >::iterator li;
+    SGPickCallbackList::iterator li;
     for (li = mi->second.begin(); li != mi->second.end(); ++li) {
       (*li)->update(dt, keyModState);
     }
@@ -130,6 +136,18 @@ struct mouse {
     mouse_mode * modes;
 };
 
+static
+const SGSceneryPick*
+getPick( const SGSceneryPicks& pick_list,
+         const SGPickCallback* cb )
+{
+  for(size_t i = 0; i < pick_list.size(); ++i)
+    if( pick_list[i].callback == cb )
+      return &pick_list[i];
+
+  return 0;
+}
+
 ////////////////////////////////////////////////////////////////////////
 
 class FGMouseInput::FGMouseInputPrivate : public SGPropertyChangeListener
@@ -197,37 +215,44 @@ public:
   
     void doHoverPick(const osg::Vec2d& windowPos)
     {
-        std::vector<SGSceneryPick> pickList;
-        SGPickCallback::Priority priority = SGPickCallback::PriorityScenery;
-      
         FGMouseCursor::Cursor cur = FGMouseCursor::CURSOR_ARROW;
         bool explicitCursor = false;
         bool didPick = false;
-      
-        if (globals->get_renderer()->pick(pickList, windowPos)) {
+
+        SGPickCallback::Priority priority = SGPickCallback::PriorityScenery;
+        SGSceneryPicks pickList;
+        globals->get_renderer()->pick(pickList, windowPos);
+
+        SGSceneryPicks::const_iterator i;
+        for( i = pickList.begin(); i != pickList.end(); ++i )
+        {
+            bool done = i->callback->hover(windowPos, i->info);
+            std::string curName(i->callback->getCursor());
+            if (!curName.empty()) {
+                explicitCursor = true;
+                cur = FGMouseCursor::cursorFromString(curName.c_str());
+            }
             
-            std::vector<SGSceneryPick>::const_iterator i;
-            for (i = pickList.begin(); i != pickList.end(); ++i) {
-                bool done = i->callback->hover(windowPos, i->info);
-                std::string curName(i->callback->getCursor());
-                if (!curName.empty()) {
-                    explicitCursor = true;
-                    cur = FGMouseCursor::cursorFromString(curName.c_str());
-                }
-                
             // if the callback is of higher prioirty (lower enum index),
             // record that.
-                if (i->callback->getPriority() < priority) {
-                    priority = i->callback->getPriority();
-                }
-                
-                if (done) {
-                    didPick = true;
-                    break;
-                }
-            } // of picks iteration
-        } else { // of have valid pick
+            if (i->callback->getPriority() < priority) {
+                priority = i->callback->getPriority();
+            }
+
+            if (done) {
+                didPick = true;
+                break;
+            }
+        } // of picks iteration
+
+        // Check if any pick from the previous iteration has disappeared. If so
+        // notify the callback that the mouse has left its element.
+        for( i = _previous_picks.begin(); i != _previous_picks.end(); ++i )
+        {
+          if( !getPick(pickList, i->callback) )
+            i->callback->mouseLeave(windowPos);
         }
+        _previous_picks = pickList;
       
         if (!explicitCursor && (priority == SGPickCallback::PriorityPanel)) {
             cur = FGMouseCursor::CURSOR_HAND;
@@ -245,12 +270,27 @@ public:
     {
         FGMouseCursor::Cursor cur = FGMouseCursor::CURSOR_CLOSED_HAND;
         
-        BOOST_FOREACH(SGPickCallback* cb, activePickCallbacks[0]) {
-            cb->mouseMoved(ea);
-            std::string curName(cb->getCursor());
-            if (!curName.empty()) {
-                cur = FGMouseCursor::cursorFromString(curName.c_str());
-            }
+        osg::Vec2d windowPos;
+        flightgear::eventToWindowCoords(ea, windowPos.x(), windowPos.y());
+
+        SGSceneryPicks pickList;
+        if( !globals->get_renderer()->pick(pickList, windowPos) )
+          return;
+
+        for( ActivePickCallbacks::iterator mi = activePickCallbacks.begin();
+                                           mi != activePickCallbacks.end();
+                                         ++mi )
+        {
+          SGPickCallbackList::iterator li;
+          for( li = mi->second.begin(); li != mi->second.end(); ++li )
+          {
+            const SGSceneryPick* pick = getPick(pickList, *li);
+            (*li)->mouseMoved(*ea, pick ? &pick->info : 0);
+
+            std::string curName((*li)->getCursor());
+            if( !curName.empty() )
+              cur = FGMouseCursor::cursorFromString(curName.c_str());
+          }
         }
 
         FGMouseCursor::instance()->setCursor(cur);
@@ -278,6 +318,7 @@ public:
     }
     
     ActivePickCallbacks activePickCallbacks;
+    SGSceneryPicks _previous_picks;
 
     mouse mice[MAX_MICE];
     
@@ -513,12 +554,26 @@ void FGMouseInput::doMouseClick (int b, int updown, int x, int y, bool mainWindo
   // requested, and return if one of
   // them consumes the event.
 
-  if (updown != MOUSE_BUTTON_DOWN) {
+  osg::Vec2d windowPos;
+  flightgear::eventToWindowCoords(ea, windowPos.x(), windowPos.y());
+
+  SGSceneryPicks pickList;
+  globals->get_renderer()->pick(pickList, windowPos);
+
+  if( updown != MOUSE_BUTTON_DOWN )
+  {
     // Execute the mouse up event in any case, may be we should
     // stop processing here?
-    while (!d->activePickCallbacks[b].empty()) {
-      d->activePickCallbacks[b].front()->buttonReleased(ea->getModKeyMask());
-      d->activePickCallbacks[b].pop_front();
+
+    SGPickCallbackList& callbacks = d->activePickCallbacks[b];
+
+    while( !callbacks.empty() )
+    {
+      SGPickCallbackPtr& cb = callbacks.front();
+      const SGSceneryPick* pick = getPick(pickList, cb);
+      cb->buttonReleased(ea->getModKeyMask(), *ea, pick ? &pick->info : 0);
+
+      callbacks.pop_front();
     }
   }
 
@@ -540,8 +595,6 @@ void FGMouseInput::doMouseClick (int b, int updown, int x, int y, bool mainWindo
       }
     } else {
       // do a hover pick now, to fix up cursor
-      osg::Vec2d windowPos;
-      flightgear::eventToWindowCoords(ea, windowPos.x(), windowPos.y());
       d->doHoverPick(windowPos);
     } // mouse button was released
   } // of pass-through mode
index 2584df1414747d2bed96b966fb44185ca7b055ab..6dcfd315999b35b1b90c830ba2ecddbbd61eb359 100644 (file)
@@ -1723,6 +1723,62 @@ FGRenderer::resize( int width, int height )
     }
 }
 
+typedef osgUtil::LineSegmentIntersector::Intersection Intersection;
+SGVec2d uvFromIntersection(const Intersection& hit)
+{
+  // Taken from http://trac.openscenegraph.org/projects/osg/browser/OpenSceneGraph/trunk/examples/osgmovie/osgmovie.cpp
+
+  osg::Drawable* drawable = hit.drawable.get();
+  osg::Geometry* geometry = drawable ? drawable->asGeometry() : 0;
+  osg::Vec3Array* vertices =
+    geometry ? dynamic_cast<osg::Vec3Array*>(geometry->getVertexArray()) : 0;
+
+  if( !vertices )
+  {
+    SG_LOG(SG_INPUT, SG_WARN, "Unable to get vertices for intersection.");
+    return SGVec2d(-9999,-9999);
+  }
+
+  // get the vertex indices.
+  const Intersection::IndexList& indices = hit.indexList;
+  const Intersection::RatioList& ratios = hit.ratioList;
+
+  if( indices.size() != 3 || ratios.size() != 3 )
+  {
+    SG_LOG( SG_INPUT,
+            SG_WARN,
+            "Intersection has insufficient indices to work with." );
+    return SGVec2d(-9999,-9999);
+  }
+
+  unsigned int i1 = indices[0];
+  unsigned int i2 = indices[1];
+  unsigned int i3 = indices[2];
+
+  float r1 = ratios[0];
+  float r2 = ratios[1];
+  float r3 = ratios[2];
+
+  osg::Array* texcoords =
+    (geometry->getNumTexCoordArrays() > 0) ? geometry->getTexCoordArray(0) : 0;
+  osg::Vec2Array* texcoords_Vec2Array =
+    dynamic_cast<osg::Vec2Array*>(texcoords);
+
+  if( !texcoords_Vec2Array )
+  {
+    SG_LOG(SG_INPUT, SG_WARN, "Unable to get texcoords for intersection.");
+    return SGVec2d(-9999,-9999);
+  }
+
+  // we have tex coord array so now we can compute the final tex coord at the
+  // point of intersection.
+  osg::Vec2 tc1 = (*texcoords_Vec2Array)[i1];
+  osg::Vec2 tc2 = (*texcoords_Vec2Array)[i2];
+  osg::Vec2 tc3 = (*texcoords_Vec2Array)[i3];
+
+  return toSG( osg::Vec2d(tc1 * r1 + tc2 * r2 + tc3 * r3) );
+}
+
 bool
 FGRenderer::pick(std::vector<SGSceneryPick>& pickList, const osg::Vec2& windowPos)
 {
@@ -1750,6 +1806,10 @@ FGRenderer::pick(std::vector<SGSceneryPick>& pickList, const osg::Vec2& windowPo
                 SGSceneryPick sceneryPick;
                 sceneryPick.info.local = toSG(hit->getLocalIntersectPoint());
                 sceneryPick.info.wgs84 = toSG(hit->getWorldIntersectPoint());
+
+                if( pickCallback->needsUV() )
+                  sceneryPick.info.uv = uvFromIntersection(*hit);
+
                 sceneryPick.callback = pickCallback;
                 pickList.push_back(sceneryPick);
             }