-
Notifications
You must be signed in to change notification settings - Fork 1.4k
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
From John Vidar Larring, osgautocapture example that captures an imag…
…e from a paged database by frame loop to the paging is complete, then takes a snapshot.
- Loading branch information
1 parent
b2762c4
commit 270fd48
Showing
3 changed files
with
351 additions
and
0 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,4 @@ | ||
SET(TARGET_SRC osgautocapture.cpp ) | ||
|
||
#### end var setup ### | ||
SETUP_EXAMPLE(osgautocapture) |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,346 @@ | ||
/** | ||
* 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> | ||
|
||
#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) | ||
{} | ||
|
||
void apply(osg::Node& node) | ||
{ | ||
T* result = dynamic_cast<T*>(&node); | ||
if (result) | ||
_foundNode = result; | ||
else | ||
traverse(node); | ||
} | ||
|
||
T* _foundNode; | ||
}; | ||
|
||
/** Convenience function*/ | ||
template<class T> | ||
T* findTopMostNodeOfType(osg::Node* node) | ||
{ | ||
if (!node) return 0; | ||
|
||
FindTopMostNodeOfTypeVisitor<T> fnotv; | ||
node->accept(fnotv); | ||
|
||
return fnotv._foundNode; | ||
} | ||
|
||
/** Capture the frame buffer and write image to disk*/ | ||
class WindowCaptureCallback : public osg::Camera::DrawCallback | ||
{ | ||
public: | ||
WindowCaptureCallback(GLenum readBuffer, const std::string& name): | ||
_readBuffer(readBuffer), | ||
_fileName(name) | ||
{ | ||
_image = new osg::Image; | ||
} | ||
|
||
virtual void operator () (osg::RenderInfo& renderInfo) const | ||
{ | ||
glReadBuffer(_readBuffer); | ||
|
||
OpenThreads::ScopedLock<OpenThreads::Mutex> lock(_mutex); | ||
osg::GraphicsContext* gc = renderInfo.getState()->getGraphicsContext(); | ||
if (gc->getTraits()) | ||
{ | ||
GLenum pixelFormat; | ||
|
||
if (gc->getTraits()->alpha) | ||
pixelFormat = GL_RGBA; | ||
else | ||
pixelFormat = GL_RGB; | ||
|
||
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); | ||
} | ||
|
||
if (!_fileName.empty()) | ||
{ | ||
std::cout << "Writing to: " << _fileName << std::endl; | ||
osgDB::writeImageFile(*_image, _fileName); | ||
} | ||
} | ||
|
||
protected: | ||
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: | ||
CustomRenderer(osg::Camera* camera) | ||
: osgViewer::Renderer(camera), | ||
_cullOnly(true) | ||
{ | ||
setTargetFrameRate(1); | ||
setMinimumTimeAvailableForGLCompileAndDeletePerFrame(1); | ||
} | ||
|
||
/** 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; | ||
|
||
updateSceneView(sceneView); | ||
|
||
osgViewer::View* view = dynamic_cast<osgViewer::View*>(_camera->getView()); | ||
if (view) sceneView->setFusionDistance(view->getFusionDistanceMode(), view->getFusionDistanceValue()); | ||
|
||
sceneView->inheritCullSettings(*(sceneView->getCamera())); | ||
sceneView->cull(); | ||
} | ||
|
||
bool _cullOnly; | ||
}; | ||
|
||
|
||
//=============================================================== | ||
// 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"); | ||
|
||
// 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; | ||
} | ||
|
||
// Get user specified number of DatabaseThreads | ||
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; | ||
|
||
// 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; | ||
lat = lat * M_PI / 180.0; // Convert to radians | ||
lon = lon * M_PI / 180.0; // Convert to radians | ||
heading = heading * M_PI / 180.0; // Convert to radians | ||
incline = incline * M_PI / 180.0; // Convert to radians | ||
roll = roll * M_PI / 180.0; // Convert to radians | ||
} | ||
|
||
// load the data | ||
osg::ref_ptr<osg::Node> loadedModel = osgDB::readNodeFiles(arguments); | ||
if (!loadedModel) | ||
{ | ||
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; | ||
|
||
// 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); | ||
|
||
// 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); | ||
|
||
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() ); | ||
} | ||
|
||
|
||
// 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(); | ||
|
||
osg::Timer_t beforeLoadTick = osg::Timer::instance()->tick(); | ||
|
||
// 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; | ||
|
||
osg::Timer_t afterLoadTick = osg::Timer::instance()->tick(); | ||
std::cout<<"Load and Compile time = "<<osg::Timer::instance()->delta_s(beforeLoadTick, afterLoadTick)<<" seconds"<<std::endl; | ||
|
||
|
||
//--- Capture the image!!! --- | ||
if (!activeMode) | ||
{ | ||
// Do cull and draw to render the scene correctly | ||
customRenderer->setCullOnly(false); | ||
|
||
// Add the WindowCaptureCallback now that we have full resolution | ||
viewer.getCamera()->setFinalDrawCallback(new WindowCaptureCallback(GL_BACK, fileName)); | ||
|
||
osg::Timer_t beforeRenderTick = osg::Timer::instance()->tick(); | ||
|
||
// Do rendering with capture callback | ||
viewer.renderingTraversals(); | ||
|
||
osg::Timer_t afterRenderTick = osg::Timer::instance()->tick(); | ||
std::cout<<"Rendring time = "<<osg::Timer::instance()->delta_s(beforeRenderTick, afterRenderTick) <<" seconds"<<std::endl; | ||
|
||
return 0; | ||
} | ||
else | ||
{ | ||
return viewer.run(); | ||
} | ||
} |