2003-12-16 00:46:06 +08:00
// C++ source file - (C) 2003 Robert Osfield, released under the OSGPL.
//
2006-11-02 20:27:15 +08:00
// Simple example of use of Producer::RenderSurface + KeyboardMouseCallback + SimpleViewer
2003-12-16 00:46:06 +08:00
// example that provides the user with control over view position with basic picking.
# include <Producer/RenderSurface>
# include <Producer/KeyboardMouse>
# include <Producer/Trackball>
# include <osg/Timer>
2005-04-08 17:45:06 +08:00
# include <osg/io_utils>
2006-06-12 22:04:40 +08:00
# include <osg/observer_ptr>
2003-12-16 00:46:06 +08:00
2006-11-01 22:41:32 +08:00
# include <osgUtil/IntersectionVisitor>
2006-11-29 00:30:38 +08:00
# include <osgUtil/PolytopeIntersector>
# include <osgUtil/LineSegmentIntersector>
2003-12-16 00:46:06 +08:00
# include <osgDB/ReadFile>
2006-01-04 00:52:06 +08:00
# include <osgDB/WriteFile>
2003-12-16 00:46:06 +08:00
2006-06-12 19:32:11 +08:00
# include <osgGA/TrackballManipulator>
# include <osgGA/StateSetManipulator>
2003-12-16 00:46:06 +08:00
2006-11-02 20:27:15 +08:00
# include <osgViewer/SimpleViewer>
2006-06-12 19:32:11 +08:00
# include <osgFX/Scribe>
2003-12-16 00:46:06 +08:00
2006-06-12 22:04:40 +08:00
// ----------- Begining of glue classes to adapter Producer's keyboard mouse events to osgGA's abstraction events.
2006-06-12 19:32:11 +08:00
class MyKeyboardMouseCallback : public Producer : : KeyboardMouseCallback
2006-01-04 00:52:06 +08:00
{
public :
2006-06-12 19:32:11 +08:00
MyKeyboardMouseCallback ( osgGA : : EventQueue * eventQueue ) :
_done ( false ) ,
_eventQueue ( eventQueue )
2006-01-04 00:52:06 +08:00
{
}
2006-06-12 19:32:11 +08:00
virtual void shutdown ( )
2003-12-16 00:46:06 +08:00
{
2006-06-12 19:32:11 +08:00
_done = true ;
2003-12-16 00:46:06 +08:00
}
virtual void specialKeyPress ( Producer : : KeyCharacter key )
{
if ( key = = Producer : : KeyChar_Escape )
shutdown ( ) ;
2006-06-12 19:32:11 +08:00
_eventQueue - > keyPress ( ( osgGA : : GUIEventAdapter : : KeySymbol ) key ) ;
2003-12-16 00:46:06 +08:00
}
2006-06-12 19:32:11 +08:00
virtual void specialKeyRelease ( Producer : : KeyCharacter key )
2003-12-16 00:46:06 +08:00
{
2006-06-12 19:32:11 +08:00
_eventQueue - > keyRelease ( ( osgGA : : GUIEventAdapter : : KeySymbol ) key ) ;
2003-12-16 00:46:06 +08:00
}
virtual void keyPress ( Producer : : KeyCharacter key )
{
2006-06-12 19:32:11 +08:00
_eventQueue - > keyPress ( ( osgGA : : GUIEventAdapter : : KeySymbol ) key ) ;
2003-12-16 00:46:06 +08:00
}
2006-06-12 19:32:11 +08:00
virtual void keyRelease ( Producer : : KeyCharacter key )
2003-12-16 00:46:06 +08:00
{
2006-06-12 19:32:11 +08:00
_eventQueue - > keyRelease ( ( osgGA : : GUIEventAdapter : : KeySymbol ) key ) ;
2003-12-16 00:46:06 +08:00
}
2006-06-12 19:32:11 +08:00
virtual void mouseMotion ( float mx , float my )
2003-12-16 00:46:06 +08:00
{
2006-06-12 19:32:11 +08:00
_eventQueue - > mouseMotion ( mx , my ) ;
2003-12-16 00:46:06 +08:00
}
2006-06-12 19:32:11 +08:00
virtual void buttonPress ( float mx , float my , unsigned int mbutton )
2003-12-16 00:46:06 +08:00
{
2006-06-12 19:32:11 +08:00
_eventQueue - > mouseButtonPress ( mx , my , mbutton ) ;
2003-12-16 00:46:06 +08:00
}
2006-06-12 19:32:11 +08:00
virtual void buttonRelease ( float mx , float my , unsigned int mbutton )
2003-12-16 00:46:06 +08:00
{
2006-06-12 19:32:11 +08:00
_eventQueue - > mouseButtonRelease ( mx , my , mbutton ) ;
2003-12-16 00:46:06 +08:00
}
2006-06-12 19:32:11 +08:00
bool done ( ) { return _done ; }
2006-01-04 00:52:06 +08:00
2003-12-16 00:46:06 +08:00
private :
bool _done ;
2006-06-12 19:32:11 +08:00
osg : : ref_ptr < osgGA : : EventQueue > _eventQueue ;
} ;
class MyActionAdapter : public osgGA : : GUIActionAdapter , public osg : : Referenced
{
public :
// Override from GUIActionAdapter
virtual void requestRedraw ( ) { }
// Override from GUIActionAdapter
virtual void requestContinuousUpdate ( bool = true ) { }
// Override from GUIActionAdapter
virtual void requestWarpPointer ( float , float ) { }
2003-12-16 00:46:06 +08:00
} ;
2006-06-12 22:04:40 +08:00
// ----------- End of glue classes to adapter Producer's keyboard mouse events to osgGA's abstraction events.
2006-06-12 19:32:11 +08:00
2006-06-12 22:04:40 +08:00
class CreateModelToSaveVisitor : public osg : : NodeVisitor
{
public :
CreateModelToSaveVisitor ( ) :
osg : : NodeVisitor ( osg : : NodeVisitor : : TRAVERSE_ALL_CHILDREN )
{
_group = new osg : : Group ;
_addToModel = false ;
}
virtual void apply ( osg : : Node & node )
{
osgFX : : Scribe * scribe = dynamic_cast < osgFX : : Scribe * > ( & node ) ;
if ( scribe )
{
for ( unsigned int i = 0 ; i < scribe - > getNumChildren ( ) ; + + i )
{
_group - > addChild ( scribe - > getChild ( i ) ) ;
}
}
else
{
traverse ( node ) ;
}
}
osg : : ref_ptr < osg : : Group > _group ;
bool _addToModel ;
} ;
// class to handle events with a pick
class PickHandler : public osgGA : : GUIEventHandler {
public :
2006-11-02 20:27:15 +08:00
PickHandler ( ) :
2006-06-12 22:04:40 +08:00
_mx ( 0.0 ) , _my ( 0.0 ) { }
~ PickHandler ( ) { }
2006-11-02 20:27:15 +08:00
bool handle ( const osgGA : : GUIEventAdapter & ea , osgGA : : GUIActionAdapter & aa )
2006-06-12 22:04:40 +08:00
{
2006-11-02 20:27:15 +08:00
osgViewer : : SimpleViewer * viewer = dynamic_cast < osgViewer : : SimpleViewer * > ( & aa ) ;
2006-06-12 22:04:40 +08:00
switch ( ea . getEventType ( ) )
{
case ( osgGA : : GUIEventAdapter : : KEYUP ) :
{
2006-11-02 20:27:15 +08:00
if ( ea . getKey ( ) = = ' s ' & & viewer )
2006-06-12 22:04:40 +08:00
{
2006-11-02 20:27:15 +08:00
saveSelectedModel ( viewer - > getSceneData ( ) ) ;
2006-06-12 22:04:40 +08:00
}
return false ;
}
case ( osgGA : : GUIEventAdapter : : PUSH ) :
case ( osgGA : : GUIEventAdapter : : MOVE ) :
{
_mx = ea . getX ( ) ;
_my = ea . getY ( ) ;
return false ;
}
case ( osgGA : : GUIEventAdapter : : RELEASE ) :
{
if ( _mx = = ea . getX ( ) & & _my = = ea . getY ( ) )
{
// only do a pick if the mouse hasn't moved
2006-11-02 20:27:15 +08:00
pick ( ea , viewer ) ;
2006-06-12 22:04:40 +08:00
}
return true ;
}
default :
return false ;
}
}
2006-11-02 20:27:15 +08:00
void pick ( const osgGA : : GUIEventAdapter & ea , osgViewer : : SimpleViewer * viewer )
2006-06-12 22:04:40 +08:00
{
2006-11-02 20:27:15 +08:00
osg : : Node * scene = viewer - > getSceneData ( ) ;
2006-06-12 22:04:40 +08:00
if ( ! scene ) return ;
2006-11-02 23:50:04 +08:00
osg : : notify ( osg : : NOTICE ) < < std : : endl ;
2006-11-01 22:41:32 +08:00
2006-11-02 23:50:04 +08:00
osg : : Node * node = 0 ;
osg : : Group * parent = 0 ;
2006-11-01 22:41:32 +08:00
2006-11-02 23:50:04 +08:00
bool usePolytopePicking = true ;
if ( usePolytopePicking )
{
2006-11-03 01:05:46 +08:00
#if 0
2006-11-02 23:50:04 +08:00
// use window coordinates
// remap the mouse x,y into viewport coordinates.
osg : : Viewport * viewport = viewer - > getCamera ( ) - > getViewport ( ) ;
2006-11-03 01:05:46 +08:00
double mx = viewport - > x ( ) + ( int ) ( ( double ) viewport - > width ( ) * ( ea . getXnormalized ( ) * 0.5 + 0.5 ) ) ;
double my = viewport - > y ( ) + ( int ) ( ( double ) viewport - > height ( ) * ( ea . getYnormalized ( ) * 0.5 + 0.5 ) ) ;
// half width, height.
double w = 5.0f ;
double h = 5.0f ;
osgUtil : : PolytopeIntersector * picker = new osgUtil : : PolytopeIntersector ( osgUtil : : Intersector : : WINDOW , mx - w , my - h , mx + w , my + h ) ;
# else
double mx = ea . getXnormalized ( ) ;
double my = ea . getYnormalized ( ) ;
double w = 0.05 ;
double h = 0.05 ;
osgUtil : : PolytopeIntersector * picker = new osgUtil : : PolytopeIntersector ( osgUtil : : Intersector : : PROJECTION , mx - w , my - h , mx + w , my + h ) ;
# endif
2006-11-02 23:50:04 +08:00
osgUtil : : IntersectionVisitor iv ( picker ) ;
viewer - > getCamera ( ) - > accept ( iv ) ;
2006-11-02 20:27:15 +08:00
2006-11-02 23:50:04 +08:00
if ( picker - > containsIntersections ( ) )
{
osgUtil : : PolytopeIntersector : : Intersection intersection = picker - > getFirstIntersection ( ) ;
osg : : NodePath & nodePath = intersection . nodePath ;
node = ( nodePath . size ( ) > = 1 ) ? nodePath [ nodePath . size ( ) - 1 ] : 0 ;
parent = ( nodePath . size ( ) > = 2 ) ? dynamic_cast < osg : : Group * > ( nodePath [ nodePath . size ( ) - 2 ] ) : 0 ;
if ( node ) std : : cout < < " Hits " < < node - > className ( ) < < " nodePath size " < < nodePath . size ( ) < < std : : endl ;
}
}
else
2006-11-01 22:41:32 +08:00
{
2006-11-02 23:50:04 +08:00
#if 0
2006-11-03 01:05:46 +08:00
// use non dimensional coordinates - in projection/clip space
osgUtil : : LineSegmentIntersector * picker = new osgUtil : : LineSegmentIntersector ( osgUtil : : Intersector : : PROJECTION , ea . getXnormalized ( ) , ea . getYnormalized ( ) ) ;
2006-11-02 23:50:04 +08:00
# else
// use window coordinates
// remap the mouse x,y into viewport coordinates.
osg : : Viewport * viewport = viewer - > getCamera ( ) - > getViewport ( ) ;
float mx = viewport - > x ( ) + ( int ) ( ( float ) viewport - > width ( ) * ( ea . getXnormalized ( ) * 0.5f + 0.5f ) ) ;
float my = viewport - > y ( ) + ( int ) ( ( float ) viewport - > height ( ) * ( ea . getYnormalized ( ) * 0.5f + 0.5f ) ) ;
2006-11-03 01:05:46 +08:00
osgUtil : : LineSegmentIntersector * picker = new osgUtil : : LineSegmentIntersector ( osgUtil : : Intersector : : WINDOW , mx , my ) ;
2006-11-02 23:50:04 +08:00
# endif
osgUtil : : IntersectionVisitor iv ( picker ) ;
2006-11-01 22:41:32 +08:00
2006-11-02 23:50:04 +08:00
viewer - > getCamera ( ) - > accept ( iv ) ;
2006-11-01 22:41:32 +08:00
2006-11-02 23:50:04 +08:00
if ( picker - > containsIntersections ( ) )
2006-11-01 22:41:32 +08:00
{
2006-11-02 23:50:04 +08:00
osgUtil : : LineSegmentIntersector : : Intersection intersection = picker - > getFirstIntersection ( ) ;
osg : : notify ( osg : : NOTICE ) < < " Picked " < < intersection . localIntersectionPoint < < std : : endl ;
2006-11-01 22:41:32 +08:00
2006-11-02 23:50:04 +08:00
osg : : NodePath & nodePath = intersection . nodePath ;
node = ( nodePath . size ( ) > = 1 ) ? nodePath [ nodePath . size ( ) - 1 ] : 0 ;
parent = ( nodePath . size ( ) > = 2 ) ? dynamic_cast < osg : : Group * > ( nodePath [ nodePath . size ( ) - 2 ] ) : 0 ;
2006-11-01 22:41:32 +08:00
2006-11-02 23:50:04 +08:00
if ( node ) std : : cout < < " Hits " < < node - > className ( ) < < " nodePath size " < < nodePath . size ( ) < < std : : endl ;
}
}
// now we try to decorate the hit node by the osgFX::Scribe to show that its been "picked"
if ( parent & & node )
{
std : : cout < < " parent " < < parent - > className ( ) < < std : : endl ;
osgFX : : Scribe * parentAsScribe = dynamic_cast < osgFX : : Scribe * > ( parent ) ;
if ( ! parentAsScribe )
{
// node not already picked, so highlight it with an osgFX::Scribe
osgFX : : Scribe * scribe = new osgFX : : Scribe ( ) ;
scribe - > addChild ( node ) ;
parent - > replaceChild ( node , scribe ) ;
}
else
{
// node already picked so we want to remove scribe to unpick it.
osg : : Node : : ParentList parentList = parentAsScribe - > getParents ( ) ;
for ( osg : : Node : : ParentList : : iterator itr = parentList . begin ( ) ;
itr ! = parentList . end ( ) ;
+ + itr )
2006-11-01 22:41:32 +08:00
{
2006-11-02 23:50:04 +08:00
( * itr ) - > replaceChild ( parentAsScribe , node ) ;
2006-11-01 22:41:32 +08:00
}
}
}
2006-06-12 22:04:40 +08:00
}
2006-11-02 20:27:15 +08:00
void saveSelectedModel ( osg : : Node * scene )
2006-06-12 22:04:40 +08:00
{
2006-11-02 20:27:15 +08:00
if ( ! scene ) return ;
2006-06-12 22:04:40 +08:00
CreateModelToSaveVisitor cmtsv ;
2006-11-02 20:27:15 +08:00
scene - > accept ( cmtsv ) ;
2006-06-12 22:04:40 +08:00
if ( cmtsv . _group - > getNumChildren ( ) > 0 )
{
std : : cout < < " Writing selected compoents to 'selected_model.osg' " < < std : : endl ;
osgDB : : writeNodeFile ( * cmtsv . _group , " selected_model.osg " ) ;
}
}
protected :
float _mx , _my ;
} ;
2003-12-16 00:46:06 +08:00
int main ( int argc , char * * argv )
{
if ( argc < 2 )
{
std : : cout < < argv [ 0 ] < < " : requires filename argument. " < < std : : endl ;
return 1 ;
}
// load the scene.
osg : : ref_ptr < osg : : Node > loadedModel = osgDB : : readNodeFile ( argv [ 1 ] ) ;
if ( ! loadedModel )
{
std : : cout < < argv [ 0 ] < < " : No data loaded. " < < std : : endl ;
return 1 ;
}
// create the window to draw to.
osg : : ref_ptr < Producer : : RenderSurface > renderSurface = new Producer : : RenderSurface ;
2003-12-17 07:43:37 +08:00
renderSurface - > setWindowName ( " osgkeyboardmouse " ) ;
2003-12-16 00:46:06 +08:00
renderSurface - > setWindowRectangle ( 100 , 100 , 800 , 600 ) ;
renderSurface - > useBorder ( true ) ;
renderSurface - > realize ( ) ;
// create the view of the scene.
2006-11-02 20:27:15 +08:00
osgViewer : : SimpleViewer viewer ;
2006-09-26 00:25:53 +08:00
viewer . setSceneData ( loadedModel . get ( ) ) ;
2003-12-16 00:46:06 +08:00
// set up a KeyboardMouse to manage the events comming in from the RenderSurface
osg : : ref_ptr < Producer : : KeyboardMouse > kbm = new Producer : : KeyboardMouse ( renderSurface . get ( ) ) ;
// create a KeyboardMouseCallback to handle the mouse events within this applications
2006-09-26 00:25:53 +08:00
osg : : ref_ptr < MyKeyboardMouseCallback > kbmcb = new MyKeyboardMouseCallback ( viewer . getEventQueue ( ) ) ;
2006-06-12 19:32:11 +08:00
// create a tracball manipulator to move the camera around in response to keyboard/mouse events
2006-09-26 00:25:53 +08:00
viewer . setCameraManipulator ( new osgGA : : TrackballManipulator ) ;
2006-06-12 19:32:11 +08:00
osg : : ref_ptr < osgGA : : StateSetManipulator > statesetManipulator = new osgGA : : StateSetManipulator ;
2006-09-26 00:25:53 +08:00
statesetManipulator - > setStateSet ( viewer . getSceneView ( ) - > getGlobalStateSet ( ) ) ;
viewer . addEventHandler ( statesetManipulator . get ( ) ) ;
2003-12-16 00:46:06 +08:00
2006-09-26 00:25:53 +08:00
// add the pick handler
2006-11-02 20:27:15 +08:00
viewer . addEventHandler ( new PickHandler ( ) ) ;
2003-12-16 00:46:06 +08:00
2006-09-26 00:25:53 +08:00
// set the window dimensions
viewer . getEventQueue ( ) - > getCurrentEventState ( ) - > setWindowRectangle ( 100 , 100 , 800 , 600 ) ;
2006-06-12 19:32:11 +08:00
2006-09-26 00:25:53 +08:00
// set the mouse input range.
2006-06-12 19:32:11 +08:00
// Producer defaults to using non-dimensional units, so we pass this onto osgGA, most windowing toolkits use pixel coords so use the window size instead.
2006-09-26 00:25:53 +08:00
viewer . getEventQueue ( ) - > getCurrentEventState ( ) - > setInputRange ( - 1.0 , - 1.0 , 1.0 , 1.0 ) ;
2006-06-12 19:32:11 +08:00
2006-09-26 00:25:53 +08:00
// Producer has the y axis increase upwards, like OpenGL, and contary to most Windowing toolkits.
// we need to construct the event queue so that it knows about this convention.
viewer . getEventQueue ( ) - > getCurrentEventState ( ) - > setMouseYOrientation ( osgGA : : GUIEventAdapter : : Y_INCREASING_UPWARDS ) ;
2006-06-12 19:32:11 +08:00
2006-09-26 00:25:53 +08:00
viewer . init ( ) ;
2006-06-12 19:32:11 +08:00
2003-12-16 00:46:06 +08:00
// main loop (note, window toolkits which take control over the main loop will require a window redraw callback containing the code below.)
while ( renderSurface - > isRealized ( ) & & ! kbmcb - > done ( ) )
{
2006-09-26 00:25:53 +08:00
// update the window dimensions, in case the window has been resized.
viewer . getEventQueue ( ) - > windowResize ( 0 , 0 , renderSurface - > getWindowWidth ( ) , renderSurface - > getWindowHeight ( ) , false ) ;
2003-12-16 00:46:06 +08:00
2006-06-12 19:32:11 +08:00
// pass any keyboard mouse events onto the local keyboard mouse callback.
2003-12-16 00:46:06 +08:00
kbm - > update ( * kbmcb ) ;
2006-09-26 00:25:53 +08:00
viewer . frame ( ) ;
2003-12-16 00:46:06 +08:00
// Swap Buffers
renderSurface - > swapBuffers ( ) ;
}
return 0 ;
}