//---------------------------------------------------------------------------
//
// Project: OpenWalnut ( http://www.openwalnut.org )
//
// Copyright 2009 OpenWalnut Community, BSV@Uni-Leipzig and CNCF@MPI-CBS
// For more information see http://www.openwalnut.org/copying
//
// This file is part of OpenWalnut.
//
// OpenWalnut is free software: you can redistribute it and/or modify
// it under the terms of the GNU Lesser General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// OpenWalnut is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU Lesser General Public License for more details.
//
// You should have received a copy of the GNU Lesser General Public License
// along with OpenWalnut. If not, see .
//
//---------------------------------------------------------------------------
#include
#include "WPickHandler.h"
#include "../common/WAssert.h"
const std::string WPickHandler::unpickString = "unpick";
WPickHandler::WPickHandler()
: m_hitResult( WPickInfo() ),
m_startPick( WPickInfo() ),
m_shift( false ),
m_ctrl( false ),
m_viewerName( "" ),
m_paintMode( false ),
m_mouseButton( WPickInfo::NOMOUSE ),
m_inPickMode( false ),
m_scrollWheel( 0 )
{
}
WPickHandler::WPickHandler( std::string viewerName )
: m_hitResult( WPickInfo() ),
m_startPick( WPickInfo() ),
m_shift( false ),
m_ctrl( false ),
m_viewerName( viewerName ),
m_paintMode( false ),
m_mouseButton( WPickInfo::NOMOUSE ),
m_inPickMode( false ),
m_scrollWheel( 0 )
{
}
WPickHandler::~WPickHandler()
{
}
WPickInfo WPickHandler::getHitResult()
{
return m_hitResult;
}
boost::signals2::signal< void( WPickInfo ) >* WPickHandler::getPickSignal()
{
return &m_pickSignal;
}
bool WPickHandler::handle( const osgGA::GUIEventAdapter& ea, osgGA::GUIActionAdapter& aa )
{
switch( ea.getEventType() )
{
case osgGA::GUIEventAdapter::DRAG : // Mouse pushed an dragged
case osgGA::GUIEventAdapter::PUSH : // Mousebutton pushed
{
unsigned int buttonMask = ea.getButtonMask();
if( buttonMask == osgGA::GUIEventAdapter::RIGHT_MOUSE_BUTTON )
{
m_mouseButton = WPickInfo::MOUSE_RIGHT;
osgViewer::View* view = static_cast< osgViewer::View* >( &aa );
if( view )
{
pick( view, ea );
}
}
if( ( buttonMask == osgGA::GUIEventAdapter::LEFT_MOUSE_BUTTON ) && ( m_paintMode ) )
{
m_mouseButton = WPickInfo::MOUSE_LEFT;
osgViewer::View* view = static_cast< osgViewer::View* >( &aa );
if( view )
{
pick( view, ea );
}
}
return false;
}
case osgGA::GUIEventAdapter::RELEASE : // Mousebutton released
{
m_mouseButton = WPickInfo::NOMOUSE;
osgViewer::View* view = static_cast< osgViewer::View* >( &aa );
if( view )
{
unpick();
}
return false;
}
case osgGA::GUIEventAdapter::SCROLL : // Wheel
{
if( m_inPickMode )
{
switch( ea.getScrollingMotion() )
{
case osgGA::GUIEventAdapter::SCROLL_UP:
m_scrollWheel++;
break;
case osgGA::GUIEventAdapter::SCROLL_DOWN:
m_scrollWheel--;
break;
case osgGA::GUIEventAdapter::SCROLL_2D:
// FIXME: the osg doc tells us nothing about this value, but is seems to be always 120 or -120
if( ea.getScrollingDeltaY() > 0 )
{
m_scrollWheel++;
}
else
{
m_scrollWheel--;
}
break;
default:
break;
}
// handle as pick event
osgViewer::View* view = static_cast< osgViewer::View* >( &aa );
if( view )
{
pick( view, ea );
}
ea.setHandled( true );
return true;
}
return false;
}
case osgGA::GUIEventAdapter::KEYUP : // Key on keyboard released.
{
m_shift = false;
m_ctrl = false;
return false;
}
case osgGA::GUIEventAdapter::KEYDOWN : // Key on keyboard pushed.
{
if( ea.getKey() == 'c' )
{
osgViewer::View* view = static_cast< osgViewer::View* >( &aa );
osg::ref_ptr< osgGA::GUIEventAdapter > event = new osgGA::GUIEventAdapter( ea );
event->setX( ( ea.getXmin() + ea.getXmax() ) * 0.5 );
event->setY( ( ea.getYmin() + ea.getYmax() ) * 0.5 );
if( view )
{
pick( view, *event );
}
}
if( ea.getKey() == osgGA::GUIEventAdapter::KEY_Shift_L )
{
m_shift = true;
}
if( ea.getKey() == osgGA::GUIEventAdapter::KEY_Control_L || ea.getKey() == osgGA::GUIEventAdapter::KEY_Control_R )
{
m_ctrl = true;
}
return false;
}
default:
return false;
}
}
void WPickHandler::unpick( )
{
m_inPickMode = false;
if( m_hitResult != WPickInfo() )
{
m_hitResult = WPickInfo( WPickHandler::unpickString, m_viewerName, WPosition(), std::make_pair( 0, 0 ), WPickInfo::NONE );
m_startPick = WPickInfo();
m_scrollWheel = 0;
}
m_pickSignal( getHitResult() );
}
std::string extractSuitableName( osgUtil::LineSegmentIntersector::Intersections::iterator hitr )
{
if( !hitr->nodePath.empty() && !( hitr->nodePath.back()->getName().empty() ) )
{
return hitr->nodePath.back()->getName();
}
else if( hitr->drawable.valid() )
{
return hitr->drawable->className();
}
assert( 0 && "This should not happen. Tell \"wiebel\" if it does." );
return ""; // This line will not be reached.
}
void WPickHandler::updatePickInfoModifierKeys( WPickInfo* pickInfo )
{
if( m_shift )
{
pickInfo->setModifierKey( WPickInfo::SHIFT );
}
if( m_ctrl )
{
pickInfo->setModifierKey( WPickInfo::STRG );
}
}
void WPickHandler::pick( osgViewer::View* view, const osgGA::GUIEventAdapter& ea )
{
osgUtil::LineSegmentIntersector::Intersections intersections;
m_hitResult = WPickInfo();
float x = ea.getX(); // pixel position in x direction
float y = ea.getY(); // pixel position in y direction
WPickInfo pickInfo;
updatePickInfoModifierKeys( &pickInfo );
// if we are in another viewer than the main view we just need the pixel position
if( m_viewerName != "" && m_viewerName != "Main View" )
{
pickInfo = WPickInfo( "", m_viewerName, m_startPick.getPickPosition(), std::make_pair( x, y ),
m_startPick.getModifierKey(), m_mouseButton, m_startPick.getPickNormal(), m_scrollWheel );
m_hitResult = pickInfo;
// if nothing was picked before remember the currently picked.
m_startPick = pickInfo;
m_pickSignal( getHitResult() );
return;
}
bool intersetionsExist = view->computeIntersections( x, y, intersections, 0xFFFFFFF0 );
// if something is picked, get the right thing from the list, because it might be hidden.
bool startPickIsStillInList = false;
osgUtil::LineSegmentIntersector::Intersections::iterator hitr;
if( intersetionsExist )
{
assert( intersections.size() );
hitr = intersections.begin();
bool ignoreFirst = m_ctrl;
while( hitr != intersections.end() )
{
std::string nodeName = extractSuitableName( hitr );
// now we skip everything that starts with an underscore if not in paint mode
if( nodeName[0] == '_' && ( !m_paintMode ) )
{
++hitr;
}
// // now we skip stuff with non-expressive names often used
// else if( nodeName == "Geometry" )
// {
// ++hitr;
// }
// if ctrl is pressed we skip the first thing that gets hit by the pick
else if( ignoreFirst )
{
++hitr;
ignoreFirst = false;
}
else
{
break;
}
}
if( hitr == intersections.end() )
{
// after everything was ignored nothing pickable remained and we have nothing picked before
// we just stop further processing.
if( m_startPick.getName() == "" )
{
return;
}
}
// if we have a previous pick we search for it in the list
if( m_startPick.getName() != "" && m_startPick.getName() != WPickHandler::unpickString )
{
while( ( hitr != intersections.end() ) && !startPickIsStillInList )
{
WPickInfo pickInfoTmp( extractSuitableName( hitr ), m_viewerName, WPosition(), std::make_pair( 0, 0 ), WPickInfo::NONE );
startPickIsStillInList |= ( pickInfoTmp.getName() == m_startPick.getName() );
if( !startPickIsStillInList ) // if iteration not finished yet go on in list
{
++hitr;
}
}
}
} // end of if( intersetionsExist )
else
{
// if we found no intersection and we have noting picked before
// we want to return "nothing" in order to provide the pixel coordinates
// even though we did not hit anything.
if( m_startPick.getName() == "" )
{
pickInfo = WPickInfo( "nothing", m_viewerName, WPosition( 0.0, 0.0, 0.0 ), std::make_pair( x, y ),
m_startPick.getModifierKey(), m_mouseButton, WVector3d( 0.0, 0.0, 0.0 ), m_scrollWheel );
m_hitResult = pickInfo;
m_pickSignal( getHitResult() );
return;
}
}
// Set the new pickInfo if the previously picked is still in list or we have a pick in conjunction with previously no pick
if( startPickIsStillInList || ( intersetionsExist && ( m_startPick.getName() == WPickHandler::unpickString || m_startPick.getName() == "" ) ) )
{
// if nothing was picked before, or the previously picked was found: set new pickInfo
WPosition pickPos;
pickPos[0] = hitr->getWorldIntersectPoint()[0];
pickPos[1] = hitr->getWorldIntersectPoint()[1];
pickPos[2] = hitr->getWorldIntersectPoint()[2];
WVector3d pickNormal;
pickNormal[0] = hitr->getWorldIntersectNormal()[0];
pickNormal[1] = hitr->getWorldIntersectNormal()[1];
pickNormal[2] = hitr->getWorldIntersectNormal()[2];
pickInfo = WPickInfo( extractSuitableName( hitr ), m_viewerName, pickPos, std::make_pair( x, y ),
pickInfo.getModifierKey(), m_mouseButton, pickNormal, m_scrollWheel );
}
// Use the old PickInfo with updated pixel info if we have previously picked something but the old is not in list anymore
if( !startPickIsStillInList && m_startPick.getName() != "" && m_startPick.getName() != WPickHandler::unpickString )
{
pickInfo = WPickInfo( m_startPick.getName(), m_viewerName, m_startPick.getPickPosition(), std::make_pair( x, y ),
m_startPick.getModifierKey(), m_mouseButton, m_startPick.getPickNormal(), m_scrollWheel );
}
m_hitResult = pickInfo;
// if nothing was picked before remember the currently picked.
m_startPick = pickInfo;
m_inPickMode = true;
m_pickSignal( getHitResult() );
}
void WPickHandler::setPaintMode( bool paintMode )
{
m_paintMode = paintMode;
}
void WPickHandler::setPaintMode( int mode )
{
WAssert( mode == 1 || mode == 0, "Unexpected value" );
if( mode == 1 )
{
m_paintMode = true;
}
else
{
m_paintMode = false;
}
}