diff --git a/examples/CMakeLists.txt b/examples/CMakeLists.txt index 5a4146120..64f241968 100644 --- a/examples/CMakeLists.txt +++ b/examples/CMakeLists.txt @@ -21,6 +21,7 @@ IF(DYNAMIC_OPENSCENEGRAPH) ADD_SUBDIRECTORY(osgautotransform) ADD_SUBDIRECTORY(osgbillboard) ADD_SUBDIRECTORY(osgblendequation) + ADD_SUBDIRECTORY(osgautocapture) ADD_SUBDIRECTORY(osgcallback) ADD_SUBDIRECTORY(osgcamera) ADD_SUBDIRECTORY(osgcatch) diff --git a/examples/osgautocapture/CMakeLists.txt b/examples/osgautocapture/CMakeLists.txt new file mode 100644 index 000000000..4c2302d75 --- /dev/null +++ b/examples/osgautocapture/CMakeLists.txt @@ -0,0 +1,4 @@ +SET(TARGET_SRC osgautocapture.cpp ) + +#### end var setup ### +SETUP_EXAMPLE(osgautocapture) diff --git a/examples/osgautocapture/osgautocapture.cpp b/examples/osgautocapture/osgautocapture.cpp new file mode 100644 index 000000000..b067dff51 --- /dev/null +++ b/examples/osgautocapture/osgautocapture.cpp @@ -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 +#include +#include +#include + +#include +#include + +#include +#include + +#include +#include +#include +#include +#include + +#include +#include + +#include +#include +#include + +#include +#include + +/** Helper class*/ +template +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(&node); + if (result) + _foundNode = result; + else + traverse(node); + } + + T* _foundNode; +}; + +/** Convenience function*/ +template +T* findTopMostNodeOfType(osg::Node* node) +{ + if (!node) return 0; + + FindTopMostNodeOfTypeVisitor 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 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="<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 _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(_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 ", "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 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(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 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 = 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 <getRequestsInProgress()<<" "; + viewer.updateTraversal(); + viewer.renderingTraversals(); + } +// std::cout<tick(); + std::cout<<"Load and Compile time = "<delta_s(beforeLoadTick, afterLoadTick)<<" seconds"<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 = "<delta_s(beforeRenderTick, afterRenderTick) <<" seconds"<