Skip to content

Instantly share code, notes, and snippets.

@jasonbeverage
Created March 8, 2017 15:18
Show Gist options
  • Save jasonbeverage/21de8805b3806043b5bd3a7347a02efc to your computer and use it in GitHub Desktop.
Save jasonbeverage/21de8805b3806043b5bd3a7347a02efc to your computer and use it in GitHub Desktop.
Test of using Futures/Promise to do async things with osg.
/* -*-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