Created
March 8, 2017 15:18
-
-
Save jasonbeverage/21de8805b3806043b5bd3a7347a02efc to your computer and use it in GitHub Desktop.
Test of using Futures/Promise to do async things with osg.
This file contains hidden or 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
| /* -*-c++-*- OpenSceneGraph - Copyright (C) 1998-2010 Robert Osfield | |
| * | |
| * This application is open source and may be redistributed and/or modified | |
| * freely and without restriction, both in commercial and non commercial applications, | |
| * as long as this copyright notice is maintained. | |
| * | |
| * This application 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. | |
| */ | |
| #include <osgDB/ReadFile> | |
| #include <osgUtil/Optimizer> | |
| #include <osg/CoordinateSystemNode> | |
| #include <osg/ImageStream> | |
| #include <osg/Switch> | |
| #include <osg/Types> | |
| #include <osgText/Text> | |
| #include <osgViewer/Viewer> | |
| #include <osgViewer/ViewerEventHandlers> | |
| #include <osgGA/TrackballManipulator> | |
| #include <osgGA/FlightManipulator> | |
| #include <osgGA/DriveManipulator> | |
| #include <osgGA/KeySwitchMatrixManipulator> | |
| #include <osgGA/StateSetManipulator> | |
| #include <osgGA/AnimationPathManipulator> | |
| #include <osgGA/TerrainManipulator> | |
| #include <osgGA/SphericalManipulator> | |
| #include <osgGA/Device> | |
| #include <iostream> | |
| #include <osgEarth/ThreadingUtils> | |
| #include <osgEarth/TileKey> | |
| #include <osgEarth/Registry> | |
| using namespace std::placeholders; | |
| using namespace osgEarth; | |
| osg::ref_ptr< osg::OperationQueue > queue = new osg::OperationQueue; | |
| class LoadNodeOperation : public osg::Operation | |
| { | |
| public: | |
| LoadNodeOperation(const std::string& url, osgEarth::Threading::Promise<osg::Node> promise): | |
| _url(url), | |
| _promise(promise) | |
| { | |
| } | |
| void operator()(osg::Object*) | |
| { | |
| if (!_promise.isCancelled()) | |
| { | |
| _promise.resolve(osgDB::readNodeFile(_url)); | |
| } | |
| } | |
| osgEarth::Threading::Promise<osg::Node> _promise; | |
| std::string _url; | |
| }; | |
| class LoadTextureOperation : public osg::Operation | |
| { | |
| public: | |
| LoadTextureOperation(const std::string& url, osgEarth::Threading::Promise<osg::Texture2D> promise): | |
| _url(url), | |
| _promise(promise) | |
| { | |
| } | |
| void operator()(osg::Object*) | |
| { | |
| if (!_promise.isCancelled()) | |
| { | |
| osg::ref_ptr< osg::Image > image = osgDB::readImageFile(_url); | |
| if (image.valid()) | |
| { | |
| osg::Texture2D *tex = new osg::Texture2D; | |
| tex->setResizeNonPowerOfTwoHint(false); | |
| tex->setFilter( osg::Texture::MIN_FILTER, osg::Texture::NEAREST ); | |
| tex->setFilter( osg::Texture::MAG_FILTER, osg::Texture::LINEAR ); | |
| tex->setWrap( osg::Texture::WRAP_S, osg::Texture::CLAMP_TO_EDGE ); | |
| tex->setWrap( osg::Texture::WRAP_T, osg::Texture::CLAMP_TO_EDGE ); | |
| tex->setImage(image.get()); | |
| _promise.resolve(tex); | |
| } | |
| } | |
| } | |
| osgEarth::Threading::Promise<osg::Texture2D> _promise; | |
| std::string _url; | |
| }; | |
| Threading::Future<osg::Node> readNodeAsync(const std::string& url, std::function< void(osg::Node*)> then) | |
| { | |
| Threading::Promise<osg::Node> promise(then); | |
| queue->add(new LoadNodeOperation(url, promise)); | |
| return promise.getFuture(); | |
| } | |
| Threading::Future<osg::Texture2D> readTextureAsync(const std::string& url, std::function< void(osg::Texture2D*)> then) | |
| { | |
| Threading::Promise<osg::Texture2D> promise(then); | |
| queue->add(new LoadTextureOperation(url, promise)); | |
| return promise.getFuture(); | |
| } | |
| class FutureNode : public osg::Node | |
| { | |
| public: | |
| FutureNode(Threading::Future<osg::Node> future): | |
| _future(future) | |
| { | |
| } | |
| osg::BoundingSphere | |
| computeBound() const | |
| { | |
| if (_node.valid()) | |
| { | |
| return _node->computeBound(); | |
| } | |
| return osg::Node::computeBound(); | |
| } | |
| virtual void traverse(osg::NodeVisitor& nv ) | |
| { | |
| if (!_node && _future.isFulfilled()) | |
| { | |
| _node = _future.get(); | |
| dirtyBound(); | |
| } | |
| if (_node.valid()) | |
| { | |
| _node->traverse(nv); | |
| } | |
| else | |
| { | |
| osg::Node::traverse(nv); | |
| } | |
| } | |
| Threading::Future<osg::Node> _future; | |
| osg::ref_ptr< osg::Node > _node; | |
| }; | |
| osg::Texture2D* getPlaceHolder() | |
| { | |
| static osg::ref_ptr< osg::Texture2D > placeholder; | |
| if (!placeholder.valid()) | |
| { | |
| placeholder = new osg::Texture2D; | |
| placeholder->setResizeNonPowerOfTwoHint(false); | |
| placeholder->setFilter( osg::Texture::MIN_FILTER, osg::Texture::NEAREST ); | |
| placeholder->setFilter( osg::Texture::MAG_FILTER, osg::Texture::LINEAR ); | |
| placeholder->setWrap( osg::Texture::WRAP_S, osg::Texture::CLAMP_TO_EDGE ); | |
| placeholder->setWrap( osg::Texture::WRAP_T, osg::Texture::CLAMP_TO_EDGE ); | |
| osg::Image* img = osgDB::readImageFile("http://a.deviantart.net/avatars/l/o/loading-plz.gif"); | |
| osg::ImageStream* is = dynamic_cast<osg::ImageStream*>(img); | |
| if (is) | |
| { | |
| is->play(); | |
| } | |
| placeholder->setImage(img); | |
| } | |
| return placeholder.get(); | |
| } | |
| osg::Node* makeTexturedQuad(const osgEarth::TileKey& key) | |
| { | |
| osg::Geometry* geometry = new osg::Geometry; | |
| osg::Vec3Array* verts = new osg::Vec3Array; | |
| verts->push_back(osg::Vec3(key.getTileX(), 0, key.getTileY())); | |
| verts->push_back(osg::Vec3(key.getTileX() + 1.0, 0, key.getTileY())); | |
| verts->push_back(osg::Vec3(key.getTileX() + 1.0, 0, key.getTileY() + 1.0)); | |
| verts->push_back(osg::Vec3(key.getTileX(), 0, key.getTileY() + 1.0)); | |
| geometry->setVertexArray(verts); | |
| osg::Vec2Array* texCoords = new osg::Vec2Array; | |
| texCoords->push_back(osg::Vec2f(0.0f, 0.0f)); | |
| texCoords->push_back(osg::Vec2f(1.0f, 0.0f)); | |
| texCoords->push_back(osg::Vec2f(1.0f, 1.0f)); | |
| texCoords->push_back(osg::Vec2f(0.0f, 1.0f)); | |
| geometry->setTexCoordArray(0, texCoords); | |
| geometry->setTexCoordArray(1, texCoords); | |
| std::stringstream buf; | |
| buf << "http://readymap.org/readymap/tiles/1.0.0/22/" << key.getLevelOfDetail() << "/" << key.getTileX() << "/" << key.getTileY() << ".jpg"; | |
| std::string readymapURL = buf.str(); | |
| /* | |
| osg::Texture2D *readymapTexture = new osg::Texture2D; | |
| readymapTexture->setResizeNonPowerOfTwoHint(false); | |
| readymapTexture->setFilter( osg::Texture::MIN_FILTER, osg::Texture::NEAREST ); | |
| readymapTexture->setFilter( osg::Texture::MAG_FILTER, osg::Texture::LINEAR ); | |
| readymapTexture->setWrap( osg::Texture::WRAP_S, osg::Texture::CLAMP_TO_EDGE ); | |
| readymapTexture->setWrap( osg::Texture::WRAP_T, osg::Texture::CLAMP_TO_EDGE ); | |
| readymapTexture->setImage(osgDB::readImageFile(readymapURL)); | |
| */ | |
| geometry->getOrCreateStateSet()->setTextureAttributeAndModes(0, getPlaceHolder(), osg::StateAttribute::ON); | |
| Threading::Future<osg::Texture2D> futureTex = readTextureAsync(readymapURL, [geometry](osg::Texture2D* texture) { | |
| geometry->getOrCreateStateSet()->setTextureAttributeAndModes(0, texture, osg::StateAttribute::ON); | |
| }); | |
| geometry->setUseVertexBufferObjects(true); | |
| geometry->setUseDisplayList(false); | |
| geometry->addPrimitiveSet(new osg::DrawArrays(GL_QUADS, 0, verts->size())); | |
| osg::Geode* geode =new osg::Geode; | |
| geode->addDrawable( geometry ); | |
| return geode; | |
| } | |
| int main(int argc, char** argv) | |
| { | |
| // use an ArgumentParser object to manage the program arguments. | |
| osg::ArgumentParser arguments(&argc,argv); | |
| osgViewer::Viewer viewer(arguments); | |
| // add the state manipulator | |
| viewer.addEventHandler( new osgGA::StateSetManipulator(viewer.getCamera()->getOrCreateStateSet()) ); | |
| // add the thread model handler | |
| viewer.addEventHandler(new osgViewer::ThreadingHandler); | |
| // add the window size toggle handler | |
| viewer.addEventHandler(new osgViewer::WindowSizeHandler); | |
| // add the stats handler | |
| viewer.addEventHandler(new osgViewer::StatsHandler); | |
| // add the help handler | |
| viewer.addEventHandler(new osgViewer::HelpHandler(arguments.getApplicationUsage())); | |
| // add the record camera path handler | |
| viewer.addEventHandler(new osgViewer::RecordCameraPathHandler); | |
| // add the LOD Scale handler | |
| viewer.addEventHandler(new osgViewer::LODScaleHandler); | |
| // add the screen capture handler | |
| viewer.addEventHandler(new osgViewer::ScreenCaptureHandler); | |
| // 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; | |
| } | |
| osg::Group* root = new osg::Group; | |
| root->getOrCreateStateSet()->setMode(GL_LIGHTING, osg::StateAttribute::OFF); | |
| // Make a threadpool. | |
| unsigned int numThreads = 16; | |
| std::vector< osg::ref_ptr< osg::OperationsThread > > threads; | |
| for (unsigned int i = 0; i < numThreads; ++i) | |
| { | |
| osg::OperationsThread* thread = new osg::OperationsThread(); | |
| thread->setOperationQueue(queue.get()); | |
| thread->start(); | |
| threads.push_back( thread ); | |
| } | |
| /* | |
| osg::ref_ptr< LoadNodeOperation > operation = new LoadNodeOperation("cow.osg"); | |
| queue->add( operation ); | |
| while (!operation->_result.valid()) | |
| { | |
| OSG_NOTICE << "Waiting..." << std::endl; | |
| } | |
| */ | |
| /* | |
| Threading::Future<osg::Node> node = readNodeAsync("cow.osg", nullptr); | |
| while (!node.isFulfilled()) | |
| { | |
| OSG_NOTICE << "Waiting..." << std::endl; | |
| } | |
| root->addChild( node.get() ); | |
| */ | |
| // Just print out a message saying the node was loaded. | |
| /* | |
| OSG_NOTICE << "Start load..." << std::endl; | |
| Threading::Future<osg::Node> node = readNodeAsync("cow.osg", [](osg::Node*) { | |
| OSG_NOTICE << "Loaded cow.osg" << std::endl; | |
| }); | |
| OSG_NOTICE << "End load..." << std::endl; | |
| */ | |
| // Load the node into the group. This isn't threadsafe b/c "then" is called immediately. | |
| /* | |
| Threading::Future<osg::Node> result = readNodeAsync("cow.osg", [root](osg::Node* node) { | |
| OE_NOTICE << "Adding node...." << std::endl; | |
| root->addChild(node); | |
| }); | |
| */ | |
| //Load the node into the group with one line of code. | |
| // This would work if addChild was a void function. | |
| //readNodeAsync("cow.osg", std::bind(&osg::Group::addChild, root, _1)); | |
| //root->addChild(new FutureNode(readNodeAsync("cow.osg", nullptr))); | |
| unsigned int lod = 6; | |
| const Profile* profile = osgEarth::Registry::instance()->getGlobalGeodeticProfile(); | |
| unsigned int wide, high; | |
| profile->getNumTiles(lod, wide, high); | |
| for (unsigned int c = 0; c < wide; c++) | |
| { | |
| for (unsigned int r = 0; r < high; r++) | |
| { | |
| TileKey key(lod, c, r, profile); | |
| //root->addChild(makeURLTile(key)); | |
| //root->addChild(makeLayerTile(layer, key)); | |
| root->addChild(makeTexturedQuad(key)); | |
| /* | |
| root->addChild(makeLambdaTile([layer](const TileKey& key) -> osg::Image* { | |
| GeoImage img = layer->createImage(key); | |
| return img.getImage(); | |
| } , key)); | |
| */ | |
| } | |
| } | |
| viewer.setSceneData( root); | |
| viewer.realize(); | |
| return viewer.run(); | |
| } |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment