2008-11-26 19:23:06 +08:00
/**
* TODO :
* 1 ) Change example to use offscreen rendering ( pbuffer ) so that it becomes a true commandline tool with now windows
* 2 ) Make example work with other threading models than SingleThreaded
* 3 ) Add support for autocapture to movies
*
*/
# include <osg/ArgumentParser>
# include <osg/CoordinateSystemNode>
# include <osg/Matrix>
# include <osg/NodeVisitor>
# include <osgUtil/IntersectionVisitor>
# include <osgUtil/GLObjectsVisitor>
# include <osgDB/ReadFile>
# include <osgDB/WriteFile>
# include <osgGA/DriveManipulator>
# include <osgGA/FlightManipulator>
# include <osgGA/KeySwitchMatrixManipulator>
# include <osgGA/TerrainManipulator>
# include <osgGA/TrackballManipulator>
# include <osgTerrain/Terrain>
# include <osgTerrain/GeometryTechnique>
# include <osgViewer/Viewer>
# include <osgViewer/ViewerEventHandlers>
# include <osgViewer/Renderer>
2015-10-22 21:42:19 +08:00
2008-11-26 19:23:06 +08:00
# include <iostream>
# include <sstream>
/** Helper class*/
template < class T >
class FindTopMostNodeOfTypeVisitor : public osg : : NodeVisitor
{
public :
FindTopMostNodeOfTypeVisitor ( ) :
osg : : NodeVisitor ( osg : : NodeVisitor : : TRAVERSE_ALL_CHILDREN ) ,
_foundNode ( 0 )
{ }
2015-10-22 21:42:19 +08:00
2008-11-26 19:23:06 +08:00
void apply ( osg : : Node & node )
{
T * result = dynamic_cast < T * > ( & node ) ;
if ( result )
_foundNode = result ;
else
traverse ( node ) ;
}
2015-10-22 21:42:19 +08:00
2008-11-26 19:23:06 +08:00
T * _foundNode ;
} ;
/** Convenience function*/
template < class T >
T * findTopMostNodeOfType ( osg : : Node * node )
{
if ( ! node ) return 0 ;
FindTopMostNodeOfTypeVisitor < T > fnotv ;
node - > accept ( fnotv ) ;
2015-10-22 21:42:19 +08:00
2008-11-26 19:23:06 +08:00
return fnotv . _foundNode ;
}
/** Capture the frame buffer and write image to disk*/
class WindowCaptureCallback : public osg : : Camera : : DrawCallback
{
2015-10-22 21:42:19 +08:00
public :
2008-11-26 19:23:06 +08:00
WindowCaptureCallback ( GLenum readBuffer , const std : : string & name ) :
_readBuffer ( readBuffer ) ,
_fileName ( name )
{
_image = new osg : : Image ;
}
2015-10-22 21:42:19 +08:00
2008-11-26 19:23:06 +08:00
virtual void operator ( ) ( osg : : RenderInfo & renderInfo ) const
{
2010-01-26 19:37:24 +08:00
# if !defined(OSG_GLES1_AVAILABLE) && !defined(OSG_GLES2_AVAILABLE)
2008-11-26 19:23:06 +08:00
glReadBuffer ( _readBuffer ) ;
2009-11-01 17:04:41 +08:00
# else
osg : : notify ( osg : : NOTICE ) < < " Error: GLES unable to do glReadBuffer " < < std : : endl ;
2009-10-30 23:16:44 +08:00
# endif
2008-11-26 19:23:06 +08:00
OpenThreads : : ScopedLock < OpenThreads : : Mutex > lock ( _mutex ) ;
osg : : GraphicsContext * gc = renderInfo . getState ( ) - > getGraphicsContext ( ) ;
if ( gc - > getTraits ( ) )
{
GLenum pixelFormat ;
if ( gc - > getTraits ( ) - > alpha )
pixelFormat = GL_RGBA ;
2015-10-22 21:42:19 +08:00
else
2008-11-26 19:23:06 +08:00
pixelFormat = GL_RGB ;
2015-10-22 21:42:19 +08:00
2010-01-26 19:37:24 +08:00
# if defined(OSG_GLES1_AVAILABLE) || defined(OSG_GLES2_AVAILABLE)
2010-02-20 04:58:46 +08:00
if ( pixelFormat = = GL_RGB )
{
2010-11-03 17:28:28 +08:00
GLint value = 0 ;
2010-02-20 04:58:46 +08:00
# ifndef GL_IMPLEMENTATION_COLOR_READ_FORMAT
# define GL_IMPLEMENTATION_COLOR_READ_FORMAT 0x8B9B
# endif
glGetIntegerv ( GL_IMPLEMENTATION_COLOR_READ_FORMAT , & value ) ;
if ( value ! = GL_RGB | |
value ! = GL_UNSIGNED_BYTE )
{
2010-01-26 19:37:24 +08:00
pixelFormat = GL_RGBA ; //always supported
2010-02-20 04:58:46 +08:00
}
}
2010-01-26 19:37:24 +08:00
# endif
2008-11-26 19:23:06 +08:00
int width = gc - > getTraits ( ) - > width ;
int height = gc - > getTraits ( ) - > height ;
std : : cout < < " Capture: size= " < < width < < " x " < < height < < " , format= " < < ( pixelFormat = = GL_RGBA ? " GL_RGBA " : " GL_RGB " ) < < std : : endl ;
_image - > readPixels ( 0 , 0 , width , height , pixelFormat , GL_UNSIGNED_BYTE ) ;
}
2015-10-22 21:42:19 +08:00
2008-11-26 19:23:06 +08:00
if ( ! _fileName . empty ( ) )
{
std : : cout < < " Writing to: " < < _fileName < < std : : endl ;
osgDB : : writeImageFile ( * _image , _fileName ) ;
}
}
2015-10-22 21:42:19 +08:00
protected :
2008-11-26 19:23:06 +08:00
GLenum _readBuffer ;
std : : string _fileName ;
osg : : ref_ptr < osg : : Image > _image ;
mutable OpenThreads : : Mutex _mutex ;
} ;
/** Do Culling only while loading PagedLODs*/
class CustomRenderer : public osgViewer : : Renderer
{
public :
2015-10-22 21:42:19 +08:00
CustomRenderer ( osg : : Camera * camera )
2008-11-26 19:23:06 +08:00
: osgViewer : : Renderer ( camera ) ,
_cullOnly ( true )
{
}
/** Set flag to omit drawing in renderingTraversals */
void setCullOnly ( bool on ) { _cullOnly = on ; }
virtual void operator ( ) ( osg : : GraphicsContext * /*context*/ )
{
if ( _graphicsThreadDoesCull )
{
if ( _cullOnly )
cull ( ) ;
else
cull_draw ( ) ;
}
}
virtual void cull ( )
{
osgUtil : : SceneView * sceneView = _sceneView [ 0 ] . get ( ) ;
if ( ! sceneView | | _done ) return ;
2015-10-22 21:42:19 +08:00
2008-11-26 19:23:06 +08:00
updateSceneView ( sceneView ) ;
2015-10-22 21:42:19 +08:00
2008-11-26 19:23:06 +08:00
osgViewer : : View * view = dynamic_cast < osgViewer : : View * > ( _camera - > getView ( ) ) ;
if ( view ) sceneView - > setFusionDistance ( view - > getFusionDistanceMode ( ) , view - > getFusionDistanceValue ( ) ) ;
sceneView - > inheritCullSettings ( * ( sceneView - > getCamera ( ) ) ) ;
sceneView - > cull ( ) ;
}
2015-10-22 21:42:19 +08:00
2008-11-26 19:23:06 +08:00
bool _cullOnly ;
} ;
2015-10-22 21:42:19 +08:00
2008-11-26 19:23:06 +08:00
//===============================================================
// MAIN
//
int main ( int argc , char * * argv )
{
osg : : ArgumentParser arguments ( & argc , argv ) ;
osg : : ApplicationUsage * usage = arguments . getApplicationUsage ( ) ;
usage - > setApplicationName ( arguments . getApplicationName ( ) ) ;
usage - > setDescription ( arguments . getApplicationName ( ) + " loads a model, sets a camera position and automatically captures screenshot to disk " ) ;
usage - > setCommandLineUsage ( arguments . getApplicationName ( ) + " [options] filename ... " ) ;
usage - > addCommandLineOption ( " --camera <lat> <lon> <alt> <heading> <incline> <roll> " , " Specify camera position for image capture. Angles are specified in degrees and altitude in meters above sealevel (e.g. --camera 55 10 300000 0 30 0) " ) ;
usage - > addCommandLineOption ( " --filename " , " Filename for the captured image " , " autocapture.jpg " ) ;
usage - > addCommandLineOption ( " --db-threads " , " Number of DatabasePager threads to use " , " 2 " ) ;
usage - > addCommandLineOption ( " --active " , " Use active rendering instead of passive / lazy rendering " ) ;
2010-01-26 19:37:24 +08:00
usage - > addCommandLineOption ( " --pbuffer " , " Render into a pbuffer, not into a window " ) ;
2008-11-26 19:23:06 +08:00
// Construct the viewer and register options arguments.
osgViewer : : Viewer viewer ( arguments ) ;
if ( arguments . argc ( ) < = 1 )
{
arguments . getApplicationUsage ( ) - > write ( std : : cout , osg : : ApplicationUsage : : COMMAND_LINE_OPTION ) ;
return 1 ;
}
2010-01-26 19:37:24 +08:00
// Get user specified number of DatabaseThreads
2008-11-26 19:23:06 +08:00
int dbThreads = 2 ;
arguments . read ( " --db-threads " , dbThreads ) ;
if ( dbThreads < 1 ) dbThreads = 1 ;
osg : : DisplaySettings : : instance ( ) - > setNumOfDatabaseThreadsHint ( dbThreads ) ;
// Get user specified file name
std : : string fileName ( " autocapture.jpg " ) ;
arguments . read ( " --filename " , fileName ) ;
// Rendering mode is passive by default
bool activeMode = false ;
if ( arguments . read ( " --active " ) )
activeMode = true ;
2010-01-26 19:37:24 +08:00
bool use_pbuffer = false ;
if ( arguments . read ( " --pbuffer " ) ) {
if ( ! activeMode ) {
use_pbuffer = true ;
} else {
osg : : notify ( osg : : NOTICE ) < < " ignoring --pbuffer because --active specified on commandline " < < std : : endl ;
}
}
if ( use_pbuffer ) {
2010-05-12 19:37:27 +08:00
osg : : DisplaySettings * ds = osg : : DisplaySettings : : instance ( ) . get ( ) ;
2010-01-26 19:37:24 +08:00
osg : : ref_ptr < osg : : GraphicsContext : : Traits > traits = new osg : : GraphicsContext : : Traits ( ds ) ;
if ( viewer . getCamera ( ) - > getGraphicsContext ( ) & & viewer . getCamera ( ) - > getGraphicsContext ( ) - > getTraits ( ) ) {
//use viewer settings for window size
osg : : ref_ptr < const osg : : GraphicsContext : : Traits > src_traits = viewer . getCamera ( ) - > getGraphicsContext ( ) - > getTraits ( ) ;
traits - > screenNum = src_traits - > screenNum ;
traits - > displayNum = src_traits - > displayNum ;
traits - > hostName = src_traits - > hostName ;
traits - > width = src_traits - > width ;
traits - > height = src_traits - > height ;
traits - > red = src_traits - > red ;
traits - > green = src_traits - > green ;
traits - > blue = src_traits - > blue ;
traits - > alpha = src_traits - > alpha ;
traits - > depth = src_traits - > depth ;
traits - > pbuffer = true ;
} else {
2018-04-21 00:18:22 +08:00
//viewer would use fullscreen size (unknown here) pbuffer will use 4096 x4096 (or best available)
2010-01-26 19:37:24 +08:00
traits - > width = 1 < < 12 ;
traits - > height = 1 < < 12 ;
traits - > pbuffer = true ;
}
osg : : ref_ptr < osg : : GraphicsContext > pbuffer = osg : : GraphicsContext : : createGraphicsContext ( traits . get ( ) ) ;
if ( pbuffer . valid ( ) )
{
osg : : notify ( osg : : NOTICE ) < < " Pixel buffer has been created successfully. " < < std : : endl ;
osg : : ref_ptr < osg : : Camera > camera = new osg : : Camera ( * viewer . getCamera ( ) ) ;
camera - > setGraphicsContext ( pbuffer . get ( ) ) ;
camera - > setViewport ( new osg : : Viewport ( 0 , 0 , traits - > width , traits - > height ) ) ;
GLenum buffer = pbuffer - > getTraits ( ) - > doubleBuffer ? GL_BACK : GL_FRONT ;
camera - > setDrawBuffer ( buffer ) ;
camera - > setReadBuffer ( buffer ) ;
2010-01-27 00:48:55 +08:00
viewer . setCamera ( camera . get ( ) ) ;
2010-01-26 19:37:24 +08:00
}
else
{
osg : : notify ( osg : : NOTICE ) < < " Pixel buffer has not been created successfully. " < < std : : endl ;
}
}
2008-11-26 19:23:06 +08:00
// Read camera settings for screenshot
double lat = 50 ;
double lon = 10 ;
double alt = 2000 ;
double heading = 0 ;
double incline = 45 ;
double roll = 0 ;
bool camera_specified = false ;
if ( arguments . read ( " --camera " , lat , lon , alt , heading , incline , roll ) )
{
camera_specified = true ;
2008-11-27 17:45:41 +08:00
lat = osg : : DegreesToRadians ( lat ) ;
lon = osg : : DegreesToRadians ( lon ) ;
heading = osg : : DegreesToRadians ( heading ) ;
incline = osg : : DegreesToRadians ( incline ) ;
roll = osg : : DegreesToRadians ( roll ) ;
2008-11-26 19:23:06 +08:00
}
// load the data
2015-10-22 21:42:19 +08:00
osg : : ref_ptr < osg : : Node > loadedModel = osgDB : : readRefNodeFiles ( arguments ) ;
if ( ! loadedModel )
2008-11-26 19:23:06 +08:00
{
std : : cout < < arguments . getApplicationName ( ) < < " : No data loaded " < < std : : endl ;
return 1 ;
}
// any option left unread are converted into errors to write out later.
arguments . reportRemainingOptionsAsUnrecognized ( ) ;
// report any errors if they have occurred when parsing the program arguments.
if ( arguments . errors ( ) )
{
arguments . writeErrorMessages ( std : : cout ) ;
return 1 ;
}
// Setup specified camera
if ( camera_specified )
{
osg : : CoordinateSystemNode * csn = findTopMostNodeOfType < osg : : CoordinateSystemNode > ( loadedModel . get ( ) ) ;
if ( ! csn ) return 1 ;
2015-10-22 21:42:19 +08:00
2008-11-26 19:23:06 +08:00
// Compute eye point in world coordiantes
osg : : Vec3d eye ;
csn - > getEllipsoidModel ( ) - > convertLatLongHeightToXYZ ( lat , lon , alt , eye . x ( ) , eye . y ( ) , eye . z ( ) ) ;
// Build matrix for computing target vector
osg : : Matrixd target_matrix = osg : : Matrixd : : rotate ( - heading , osg : : Vec3d ( 1 , 0 , 0 ) ,
- lat , osg : : Vec3d ( 0 , 1 , 0 ) ,
lon , osg : : Vec3d ( 0 , 0 , 1 ) ) ;
// Compute tangent vector ...
osg : : Vec3d tangent = target_matrix . preMult ( osg : : Vec3d ( 0 , 0 , 1 ) ) ;
// Compute non-inclined, non-rolled up vector ...
osg : : Vec3d up ( eye ) ;
up . normalize ( ) ;
// Incline by rotating the target- and up vector around the tangent/up-vector
// cross-product ...
osg : : Vec3d up_cross_tangent = up ^ tangent ;
osg : : Matrixd incline_matrix = osg : : Matrixd : : rotate ( incline , up_cross_tangent ) ;
osg : : Vec3d target = incline_matrix . preMult ( tangent ) ;
2015-10-22 21:42:19 +08:00
2008-11-26 19:23:06 +08:00
// Roll by rotating the up vector around the target vector ...
osg : : Matrixd roll_matrix = incline_matrix * osg : : Matrixd : : rotate ( roll , target ) ;
up = roll_matrix . preMult ( up ) ;
2015-10-22 21:42:19 +08:00
2008-11-26 19:23:06 +08:00
viewer . getCamera ( ) - > setViewMatrixAsLookAt ( eye , eye + target , up ) ;
}
else
{
// Only add camera manipulators if camera is not specified
camera_specified = false ;
osg : : ref_ptr < osgGA : : KeySwitchMatrixManipulator > keyswitchManipulator = new osgGA : : KeySwitchMatrixManipulator ;
keyswitchManipulator - > addMatrixManipulator ( ' 1 ' , " Trackball " , new osgGA : : TrackballManipulator ( ) ) ;
keyswitchManipulator - > addMatrixManipulator ( ' 2 ' , " Flight " , new osgGA : : FlightManipulator ( ) ) ;
keyswitchManipulator - > addMatrixManipulator ( ' 3 ' , " Drive " , new osgGA : : DriveManipulator ( ) ) ;
keyswitchManipulator - > addMatrixManipulator ( ' 4 ' , " Terrain " , new osgGA : : TerrainManipulator ( ) ) ;
2012-04-26 18:07:36 +08:00
2015-10-22 21:42:19 +08:00
viewer . setCameraManipulator ( keyswitchManipulator . get ( ) ) ;
2008-11-26 19:23:06 +08:00
}
2015-10-22 21:42:19 +08:00
2008-11-26 19:23:06 +08:00
// Optimize DatabasePager for auto-capture
osgDB : : DatabasePager * pager = viewer . getDatabasePager ( ) ;
pager - > setDoPreCompile ( false ) ;
// Install custom renderer
osg : : ref_ptr < CustomRenderer > customRenderer = new CustomRenderer ( viewer . getCamera ( ) ) ;
viewer . getCamera ( ) - > setRenderer ( customRenderer . get ( ) ) ;
// Override threading model
viewer . setThreadingModel ( osgViewer : : Viewer : : SingleThreaded ) ;
// Set the final SceneData to show
viewer . setSceneData ( loadedModel . get ( ) ) ;
// Realize GUI
viewer . realize ( ) ;
//--- Load PageLOD tiles ---
// Initiate the first PagedLOD request
viewer . frame ( ) ;
2015-10-22 21:42:19 +08:00
2008-11-26 19:23:06 +08:00
osg : : Timer_t beforeLoadTick = osg : : Timer : : instance ( ) - > tick ( ) ;
2015-10-22 21:42:19 +08:00
2008-11-26 19:23:06 +08:00
// Keep updating and culling until full level of detail is reached
while ( ! viewer . done ( ) & & pager - > getRequestsInProgress ( ) )
{
// std::cout <<pager->getRequestsInProgress()<<" ";
viewer . updateTraversal ( ) ;
viewer . renderingTraversals ( ) ;
}
// std::cout<<std::endl;
2015-10-22 21:42:19 +08:00
2008-11-26 19:23:06 +08:00
osg : : Timer_t afterLoadTick = osg : : Timer : : instance ( ) - > tick ( ) ;
std : : cout < < " Load and Compile time = " < < osg : : Timer : : instance ( ) - > delta_s ( beforeLoadTick , afterLoadTick ) < < " seconds " < < std : : endl ;
2010-01-26 19:37:24 +08:00
// Do cull and draw to render the scene correctly
customRenderer - > setCullOnly ( false ) ;
2015-10-22 21:42:19 +08:00
2008-11-26 19:23:06 +08:00
//--- Capture the image!!! ---
if ( ! activeMode )
{
// Add the WindowCaptureCallback now that we have full resolution
2010-01-26 19:37:24 +08:00
GLenum buffer = viewer . getCamera ( ) - > getGraphicsContext ( ) - > getTraits ( ) - > doubleBuffer ? GL_BACK : GL_FRONT ;
viewer . getCamera ( ) - > setFinalDrawCallback ( new WindowCaptureCallback ( buffer , fileName ) ) ;
2008-11-26 19:23:06 +08:00
osg : : Timer_t beforeRenderTick = osg : : Timer : : instance ( ) - > tick ( ) ;
// Do rendering with capture callback
viewer . renderingTraversals ( ) ;
osg : : Timer_t afterRenderTick = osg : : Timer : : instance ( ) - > tick ( ) ;
2015-04-13 18:43:56 +08:00
std : : cout < < " Rendering time = " < < osg : : Timer : : instance ( ) - > delta_s ( beforeRenderTick , afterRenderTick ) < < " seconds " < < std : : endl ;
2008-11-26 19:23:06 +08:00
return 0 ;
}
else
{
return viewer . run ( ) ;
}
}