OpenWalnut  1.4.0
WPickHandler.cpp
00001 //---------------------------------------------------------------------------
00002 //
00003 // Project: OpenWalnut ( http://www.openwalnut.org )
00004 //
00005 // Copyright 2009 OpenWalnut Community, BSV@Uni-Leipzig and CNCF@MPI-CBS
00006 // For more information see http://www.openwalnut.org/copying
00007 //
00008 // This file is part of OpenWalnut.
00009 //
00010 // OpenWalnut is free software: you can redistribute it and/or modify
00011 // it under the terms of the GNU Lesser General Public License as published by
00012 // the Free Software Foundation, either version 3 of the License, or
00013 // (at your option) any later version.
00014 //
00015 // OpenWalnut is distributed in the hope that it will be useful,
00016 // but WITHOUT ANY WARRANTY; without even the implied warranty of
00017 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
00018 // GNU Lesser General Public License for more details.
00019 //
00020 // You should have received a copy of the GNU Lesser General Public License
00021 // along with OpenWalnut. If not, see <http://www.gnu.org/licenses/>.
00022 //
00023 //---------------------------------------------------------------------------
00024 
00025 #include <string>
00026 
00027 #include "../common/WLogger.h"
00028 
00029 #include "WPickHandler.h"
00030 
00031 WPickHandler::WPickHandler()
00032     : m_hitResult( WPickInfo() ),
00033       m_startPick( WPickInfo() ),
00034       m_shift( false ),
00035       m_ctrl( false ),
00036       m_viewerName( "" ),
00037       m_paintMode( 0 ),
00038       m_mouseButton( WPickInfo::NOMOUSE ),
00039       m_inPickMode( false ),
00040       m_scrollWheel( 0 )
00041 {
00042 }
00043 
00044 WPickHandler::WPickHandler( std::string viewerName )
00045     : m_hitResult( WPickInfo() ),
00046       m_startPick( WPickInfo() ),
00047       m_shift( false ),
00048       m_ctrl( false ),
00049       m_viewerName( viewerName ),
00050       m_paintMode( 0 ),
00051       m_mouseButton( WPickInfo::NOMOUSE ),
00052       m_inPickMode( false ),
00053       m_scrollWheel( 0 )
00054 {
00055 }
00056 
00057 WPickHandler::~WPickHandler()
00058 {
00059 }
00060 
00061 WPickInfo WPickHandler::getHitResult()
00062 {
00063     return m_hitResult;
00064 }
00065 
00066 boost::signals2::signal1< void, WPickInfo >* WPickHandler::getPickSignal()
00067 {
00068     return &m_pickSignal;
00069 }
00070 
00071 bool WPickHandler::handle( const osgGA::GUIEventAdapter& ea, osgGA::GUIActionAdapter& aa )
00072 {
00073     switch( ea.getEventType() )
00074     {
00075         case osgGA::GUIEventAdapter::DRAG : // Mouse pushed an dragged
00076         case osgGA::GUIEventAdapter::PUSH : // Mousebutton pushed
00077         {
00078             unsigned int buttonMask = ea.getButtonMask();
00079             if( buttonMask == osgGA::GUIEventAdapter::RIGHT_MOUSE_BUTTON )
00080             {
00081                 m_mouseButton = WPickInfo::MOUSE_RIGHT;
00082                 osgViewer::View* view = static_cast< osgViewer::View* >( &aa );
00083                 if( view )
00084                 {
00085                     pick( view, ea );
00086                 }
00087             }
00088             if( ( buttonMask == osgGA::GUIEventAdapter::LEFT_MOUSE_BUTTON ) && ( m_paintMode == 1 ) )
00089             {
00090                 m_mouseButton = WPickInfo::MOUSE_LEFT;
00091                 osgViewer::View* view = static_cast< osgViewer::View* >( &aa );
00092                 if( view )
00093                 {
00094                     pick( view, ea );
00095                 }
00096             }
00097             return false;
00098         }
00099         case osgGA::GUIEventAdapter::RELEASE : // Mousebutton released
00100         {
00101             m_mouseButton = WPickInfo::NOMOUSE;
00102             osgViewer::View* view = static_cast< osgViewer::View* >( &aa );
00103             if( view )
00104             {
00105                 unpick();
00106             }
00107             return false;
00108         }
00109         case osgGA::GUIEventAdapter::SCROLL : // Wheel
00110         {
00111             if( m_inPickMode )
00112             {
00113                 switch( ea.getScrollingMotion() )
00114                 {
00115                     case osgGA::GUIEventAdapter::SCROLL_UP:
00116                         m_scrollWheel++;
00117                         break;
00118                     case osgGA::GUIEventAdapter::SCROLL_DOWN:
00119                         m_scrollWheel--;
00120                     case osgGA::GUIEventAdapter::SCROLL_2D:
00121                         // FIXME: the osg doc tells us nothing about this value, but is seems to be always 120 or -120
00122                         if( ea.getScrollingDeltaY() > 0 )
00123                         {
00124                             m_scrollWheel++;
00125                         }
00126                         else
00127                         {
00128                             m_scrollWheel--;
00129                         }
00130                         break;
00131                     default:
00132                         break;
00133                 }
00134 
00135                 // handle as pick event
00136                 osgViewer::View* view = static_cast< osgViewer::View* >( &aa );
00137                 if( view )
00138                 {
00139                     pick( view, ea );
00140                 }
00141                 ea.setHandled( true );
00142                 return true;
00143             }
00144             return false;
00145         }
00146         case osgGA::GUIEventAdapter::KEYUP : // Key on keyboard released.
00147         {
00148             m_shift = false;
00149             m_ctrl = false;
00150             return false;
00151         }
00152         case osgGA::GUIEventAdapter::KEYDOWN : // Key on keyboard pushed.
00153         {
00154             if( ea.getKey() == 'c' )
00155             {
00156                 osgViewer::View* view = static_cast< osgViewer::View* >( &aa );
00157                 osg::ref_ptr< osgGA::GUIEventAdapter > event = new osgGA::GUIEventAdapter( ea );
00158                 event->setX( ( ea.getXmin() + ea.getXmax() ) * 0.5 );
00159                 event->setY( ( ea.getYmin() + ea.getYmax() ) * 0.5 );
00160                 if( view )
00161                 {
00162                     pick( view, *event );
00163                 }
00164             }
00165             if( ea.getKey() == osgGA::GUIEventAdapter::KEY_Shift_L )
00166             {
00167                 m_shift = true;
00168             }
00169             if( ea.getKey() == osgGA::GUIEventAdapter::KEY_Control_L ||  ea.getKey() == osgGA::GUIEventAdapter::KEY_Control_R )
00170             {
00171                 m_ctrl = true;
00172             }
00173             return false;
00174         }
00175         default:
00176             return false;
00177     }
00178 }
00179 
00180 void WPickHandler::unpick( )
00181 {
00182     m_inPickMode = false;
00183     if( m_hitResult != WPickInfo() )
00184     {
00185         m_hitResult = WPickInfo( "unpick", m_viewerName, WPosition(), std::make_pair( 0, 0 ), WPickInfo::NONE );
00186         m_startPick = WPickInfo();
00187         m_scrollWheel = 0;
00188     }
00189     m_pickSignal( getHitResult() );
00190 }
00191 
00192 std::string extractSuitableName( osgUtil::LineSegmentIntersector::Intersections::iterator hitr )
00193 {
00194     if( !hitr->nodePath.empty() && !( hitr->nodePath.back()->getName().empty() ) )
00195     {
00196         return hitr->nodePath.back()->getName();
00197     }
00198     else if( hitr->drawable.valid() )
00199     {
00200         return  hitr->drawable->className();
00201     }
00202     assert( 0 && "This should not happen. Tell \"wiebel\" if it does." );
00203     return ""; // This line will not be reached.
00204 }
00205 
00206 void WPickHandler::updatePickInfoModifierKeys( WPickInfo* pickInfo )
00207 {
00208     if( m_shift )
00209     {
00210         pickInfo->setModifierKey( WPickInfo::SHIFT );
00211     }
00212 
00213     if( m_ctrl )
00214     {
00215         pickInfo->setModifierKey( WPickInfo::STRG );
00216     }
00217 }
00218 
00219 void WPickHandler::pick( osgViewer::View* view, const osgGA::GUIEventAdapter& ea )
00220 {
00221     osgUtil::LineSegmentIntersector::Intersections intersections;
00222     m_hitResult = WPickInfo();
00223     float x = ea.getX(); // pixel position in x direction
00224     float y = ea.getY(); // pixel position in x direction
00225 
00226     WPickInfo pickInfo;
00227 
00228     updatePickInfoModifierKeys( &pickInfo );
00229 
00230     // if we are in another viewer than the main view we just need the pixel position
00231     if( m_viewerName != "" && m_viewerName != "Main View" )
00232     {
00233         pickInfo = WPickInfo( "", m_viewerName, m_startPick.getPickPosition(), std::make_pair( x, y ),
00234                               m_startPick.getModifierKey(), m_mouseButton, m_startPick.getPickNormal(), m_scrollWheel );
00235         m_hitResult = pickInfo;
00236 
00237         // if nothing was picked before remember the currently picked.
00238         m_startPick = pickInfo;
00239 
00240         m_pickSignal( getHitResult() );
00241 
00242         return;
00243     }
00244 
00245     bool intersetionsExist = view->computeIntersections( x, y, intersections, 0xFFFFFFF0 );
00246 
00247     // if something is picked, get the right thing from the list, because it might be hidden.
00248     bool startPickIsStillInList = false;
00249     osgUtil::LineSegmentIntersector::Intersections::iterator hitr;
00250     if( intersetionsExist )
00251     {
00252         assert( intersections.size() );
00253         hitr = intersections.begin();
00254 
00255         bool ignoreFirst = m_ctrl;
00256 
00257         while( hitr != intersections.end() )
00258         {
00259             std::string nodeName = extractSuitableName( hitr );
00260             // now we skip everything that starts with an underscore if not in paint mode
00261             if(  nodeName[0] == '_' && ( m_paintMode == 0  ) )
00262             {
00263                 ++hitr;
00264             }
00265             // if ctrl is pressed we skip the first thing that gets hit by the pick
00266             else if( ignoreFirst )
00267             {
00268                 ++hitr;
00269                 ignoreFirst = false;
00270             }
00271             else
00272             {
00273                 break;
00274             }
00275         }
00276 
00277         if( hitr == intersections.end() )
00278         {
00279             // after everything was ignored nothing pickable remained and we have noting picked before
00280             // we just stop further processing.
00281             if(  m_startPick.getName() == "" )
00282             {
00283                 return;
00284             }
00285         }
00286 
00287         // if we have a previous pick we search for it in the list
00288         if( m_startPick.getName() != ""  && m_startPick.getName() != "unpick" )
00289         {
00290             while( ( hitr != intersections.end() ) && !startPickIsStillInList )
00291             {
00292                 WPickInfo pickInfoTmp( extractSuitableName( hitr ), m_viewerName, WPosition(), std::make_pair( 0, 0 ), WPickInfo::NONE );
00293                 startPickIsStillInList |= ( pickInfoTmp.getName() == m_startPick.getName() );
00294 
00295                 if( !startPickIsStillInList ) // if iteration not finished yet go on in list
00296                 {
00297                     ++hitr;
00298                 }
00299             }
00300         }
00301     } // end of if( intersetionsExist )
00302     else
00303     {
00304         // if we found no intersection and we have noting picked before
00305         // we want to return "nothing" in order to provide the pixel coordinates
00306         // even though we did not hit anything.
00307         if(  m_startPick.getName() == "" )
00308         {
00309             pickInfo = WPickInfo( "nothing", m_viewerName, WPosition( 0.0, 0.0, 0.0 ), std::make_pair( x, y ),
00310                                   m_startPick.getModifierKey(), m_mouseButton, WVector3d( 0.0, 0.0, 0.0 ), m_scrollWheel );
00311 
00312             m_hitResult = pickInfo;
00313             m_pickSignal( getHitResult() );
00314             return;
00315         }
00316     }
00317 
00318     // Set the new pickInfo if the previously picked is still in list or we have a pick in conjunction with previously no pick
00319     if( startPickIsStillInList || ( intersetionsExist && ( m_startPick.getName() == "unpick" || m_startPick.getName() == "" ) ) )
00320     {
00321         // if nothing was picked before, or the previously picked was found: set new pickInfo
00322         WPosition pickPos;
00323         pickPos[0] = hitr->getWorldIntersectPoint()[0];
00324         pickPos[1] = hitr->getWorldIntersectPoint()[1];
00325         pickPos[2] = hitr->getWorldIntersectPoint()[2];
00326 
00327         WVector3d pickNormal;
00328         pickNormal[0] = hitr->getWorldIntersectNormal()[0];
00329         pickNormal[1] = hitr->getWorldIntersectNormal()[1];
00330         pickNormal[2] = hitr->getWorldIntersectNormal()[2];
00331         pickInfo = WPickInfo( extractSuitableName( hitr ), m_viewerName, pickPos, std::make_pair( x, y ),
00332                               pickInfo.getModifierKey(), m_mouseButton, pickNormal, m_scrollWheel );
00333     }
00334 
00335     // Use the old PickInfo with updated pixel info if we have previously picked something but the old is not in list anymore
00336     if( !startPickIsStillInList && m_startPick.getName() != ""  && m_startPick.getName() != "unpick" )
00337     {
00338         pickInfo = WPickInfo( m_startPick.getName(), m_viewerName, m_startPick.getPickPosition(), std::make_pair( x, y ),
00339                               m_startPick.getModifierKey(), m_mouseButton, m_startPick.getPickNormal(), m_scrollWheel );
00340     }
00341 
00342     m_hitResult = pickInfo;
00343 
00344     // if nothing was picked before remember the currently picked.
00345     m_startPick = pickInfo;
00346     m_inPickMode = true;
00347 
00348     m_pickSignal( getHitResult() );
00349 }
00350 
00351 void WPickHandler::setPaintMode( int mode )
00352 {
00353     m_paintMode = mode;
00354 }