From 27f61cec6ade910ebdaaf161f6e24bae40cbfbff Mon Sep 17 00:00:00 2001
From: till busch <buti@bux.at>
Date: Mon, 12 May 2008 21:23:02 +0200
Subject: [PATCH] fg: add a thumb-util 0.0.1

---
 configure.ac                  |    3 +-
 utils/elevation/elevation.cxx |    2 +
 utils/thumb/Makefile.am       |   24 +++
 utils/thumb/thumb.cxx         |  446 +++++++++++++++++++++++++++++++++++++++++
 4 files changed, 474 insertions(+), 1 deletions(-)
 create mode 100644 utils/thumb/Makefile.am
 create mode 100644 utils/thumb/thumb.cxx

diff --git a/configure.ac b/configure.ac
index 0380707..fb0d884 100644
--- a/configure.ac
+++ b/configure.ac
@@ -692,7 +692,8 @@ AC_CONFIG_FILES([ \
 	tests/Makefile \
 	utils/Makefile \
 	utils/GPSsmooth/Makefile \
-	utils/elevation/Makefile\
+	utils/elevation/Makefile \
+	utils/thumb/Makefile \
 	utils/fgadmin/Makefile \
 	utils/fgadmin/src/Makefile \
 	utils/js_server/Makefile \
diff --git a/utils/elevation/elevation.cxx b/utils/elevation/elevation.cxx
index 2aa7e7f..b60caec 100644
--- a/utils/elevation/elevation.cxx
+++ b/utils/elevation/elevation.cxx
@@ -85,6 +85,8 @@ int main(int argc, char *argv[])
         TileEntry::setModelLoadHelper(&ml);
     }
 
+    arguments.read("--fg_root", fg_root);
+    arguments.read("--fg_scenery", scenery_dirs);
 
     cerr << "fg_root=" << fg_root << endl;
     cerr << "fg_scenery=" << scenery_dirs << endl;
diff --git a/utils/thumb/Makefile.am b/utils/thumb/Makefile.am
new file mode 100644
index 0000000..335e3e0
--- /dev/null
+++ b/utils/thumb/Makefile.am
@@ -0,0 +1,24 @@
+EXTRA_DIST = README
+
+bin_PROGRAMS = thumb
+
+thumb_SOURCES = thumb.cxx
+
+if WITH_THREADS
+THREAD_LIBS = -lsgthreads $(thread_LIBS)
+else
+THREAD_LIBS =
+endif
+
+LIBS=-losg -losgDB -losgParticle -losgUtil -losgViewer
+
+#INCLUDES = -I$(top_srcdir)/src/Scenery/ -I$(top_srcdir)/src/ -I$(top_srcdir)/
+
+thumb_LDADD = \
+	-lsgmaterial -lsgtgdb -lsgmodel \
+	-lsgutil -lsgio -lsgmath -lsgbucket -lsgprops \
+	-lsgdebug -lsgmisc -lsgnasal -lsgxml \
+	-lsgstructure \
+	-lplibsg -lplibul \
+	-lz
+
diff --git a/utils/thumb/thumb.cxx b/utils/thumb/thumb.cxx
new file mode 100644
index 0000000..c1a81c3
--- /dev/null
+++ b/utils/thumb/thumb.cxx
@@ -0,0 +1,446 @@
+// elevation.cxx - calculate terrain elevation
+// GPL, (l) 2008 by till busch <buti@bux.at>
+
+#include <config.h>
+#include <sstream>
+
+#include <simgear/misc/sg_path.hxx>
+#include <simgear/misc/strutils.hxx>
+
+#include <simgear/scene/util/RenderConstants.hxx>
+#include <simgear/scene/util/SGSceneFeatures.hxx>
+#include <simgear/scene/material/matlib.hxx>
+#include <simgear/scene/model/location.hxx>
+#include <simgear/scene/model/modellib.hxx>
+#include <simgear/scene/model/SGReaderWriterXMLOptions.hxx>
+#include <simgear/scene/material/mat.hxx>
+#include <simgear/scene/tgdb/userdata.hxx>
+#include <simgear/scene/tgdb/TileCache.hxx>
+#include <simgear/scene/tgdb/TileEntry.hxx>
+#include <simgear/scene/tgdb/SGReaderWriterBTGOptions.hxx>
+#include <simgear/scene/util/SGNodeMasks.hxx>
+#include <simgear/scene/util/SGUpdateVisitor.hxx>
+
+#include <osg/MatrixTransform>
+#include <osg/Group>
+#include <osg/LightSource>
+#include <osg/Light>
+#include <osg/ArgumentParser>
+#include <osgDB/ReadFile>
+#include <osgDB/FileUtils>
+#include <osgDB/WriteFile>
+#include <osgViewer/Viewer>
+#include <osgUtil/LineSegmentIntersector>
+
+#define FG_ROOT      "/usr/local/share/FlightGear"
+#define SCENERY_DIRS "/usr/local/share/FlightGear/Scenery:/usr/local/share/FlightGear/Scenery-Terrasync"
+
+#define OUTPUT id << " " << lat << " " << lon << " " << file << std::endl
+
+#define visibility 80000.0
+#define SEASON "summer"
+
+#define DESCRIPTION \
+"    take pictures of FlightGear models in scenery.\n" \
+"    reads lines from stdin and writes jpeg-files to cwd.\n\n" \
+"    INPUT FORMAT:\n" \
+"        id model-path lat lon alt-offset heading outfile\n\n" \
+"    SHOT CONFIGURATION:\n" \
+"        you can take an arbitrary number of shots per model. separate\n" \
+"        entries with ':'. <dist-v> and <dist-h> are multiplied with the\n" \
+"        radius of the bounding sphere of the specified model. <hdg> is\n" \
+"        a rotation relative to the object.\n" \
+"        default: 5,6,-10:6,6,-120\n\n" \
+"GPL, (l) 2008 by till busch <buti@bux.at>\n"
+
+using std::cin;
+using std::ws;
+using simgear::TileCache;
+using simgear::TileEntry;
+using simgear::SGReaderWriterXMLOptions;
+
+void add_tile(TileCache &tile_cache, SGBucket &b, double timestamp, osgDB::ReaderWriter::Options *options);
+osg::ref_ptr<osg::LightSource> sceneGraph=new osg::LightSource;
+
+class ModelLoader : public simgear::ModelLoadHelper {
+public:
+    virtual osg::Node *loadTileModel(const string& modelPath, bool cacheModel)
+    {
+//        cerr << modelPath << endl;
+        osg::Node*n = osgDB::readNodeFile(modelPath, _options.get());
+        if(n)
+            n->setNodeMask(n->getNodeMask() & ~SG_NODEMASK_TERRAIN_BIT);
+        return n;
+    }
+    osg::ref_ptr<SGReaderWriterXMLOptions> _options;
+};
+
+struct MyPostDrawCallback : public osg::Camera::DrawCallback
+{
+    MyPostDrawCallback(osg::Image* image):camImage(image){}
+
+    virtual void operator () (const osg::Camera& /*camera*/) const
+    {
+        osgDB::writeImageFile(*camImage, fileName);
+    }
+
+    osg::Image* camImage;
+    string fileName;
+};
+
+struct Shot {
+    double distance_v;
+    double distance_h;
+    double heading;
+};
+
+int main(int argc, char *argv[])
+{
+    string fg_root=FG_ROOT;
+    string scenery_dirs=SCENERY_DIRS;
+
+    string shot_config("5,6,-10:6,6,-120");
+
+    osg::ArgumentParser arguments(&argc,argv);
+    osg::ApplicationUsage *au=arguments.getApplicationUsage();
+    au->setCommandLineUsage(arguments.getApplicationName()+" [option] ...");
+    au->addCommandLineOption("--fg_root <path>", "set flightgear root to <path>", fg_root);
+    au->addCommandLineOption("--fg_scenery <path>", "set scenery dirs to <path> (separated by ':')", scenery_dirs);
+    au->addCommandLineOption("--models", "load other scene models");
+    au->addCommandLineOption("--size <h> <v>", "window and output file dimension");
+    au->addCommandLineOption("--shots <dist-v>,<dist-h>,<hdg>[:...]", "shot configuration");
+    au->setDescription(DESCRIPTION);
+
+    if(arguments.read("--help") || arguments.read("-h")) {
+        arguments.getApplicationUsage()->write(std::cerr);
+        std::cerr << "Description:" << std::endl;
+        std::cerr << arguments.getApplicationUsage()->getDescription() << std::endl;
+        return 1;
+    }
+
+    SGPropertyNode_ptr propRoot=new SGPropertyNode;
+
+    ModelLoader ml;
+    if(arguments.read("--models"))
+    {
+        ml._options = new SGReaderWriterXMLOptions;
+        ml._options->setPropRoot(propRoot);
+        TileEntry::setModelLoadHelper(&ml);
+    }
+
+    arguments.read("--fg_root", fg_root);
+    arguments.read("--fg_scenery", scenery_dirs);
+
+    int width=320,height=200;
+
+    arguments.read("--size", width, height);
+    arguments.read("--shots", shot_config);
+
+    vector<string> shotss = simgear::strutils::split(shot_config, ":");
+    vector<Shot> shots;
+    shots.reserve(shotss.size());
+    for(vector<string>::const_iterator it = shotss.begin(); it != shotss.end(); ++it)
+    {
+        Shot sh;
+        char c;
+        std::istringstream is(*it);
+        is >> sh.distance_v >> c >> sh.distance_h >> c >> sh.heading;
+        shots.push_back(sh);
+    }
+
+    cerr << "fg_root=" << fg_root << endl;
+    cerr << "fg_scenery=" << scenery_dirs << endl;
+
+
+    // setting this higher may cause garbage on stdout
+    osg::setNotifyLevel(osg::FATAL);
+
+    logbuf::set_log_classes(SG_ALL);
+    logbuf::set_log_priority(SG_WARN);
+//    logbuf::set_log_priority(SG_BULK);
+
+    //SGConfigureDirectionalLights( use_point_sprites, enhanced_lighting,
+    //                              distance_attenuation );
+
+    SGSceneFeatures::instance()->setEnablePointSpriteLights(false);
+    SGSceneFeatures::instance()->setEnableDistanceAttenuationLights(false);
+
+    propRoot->setDoubleValue("/environment/visibility-m", visibility);
+    propRoot->setDoubleValue("/sim/time/sun-angle-rad", 1.0);
+    propRoot->setStringValue("/sim/startup/season", SEASON);
+    propRoot->setBoolValue("/sim/rendering/random-objects", false);
+    propRoot->setBoolValue("/sim/rendering/random-vegetation", false);
+
+    sgUserDataInit( propRoot );
+    SGMaterialLib *matlib = new SGMaterialLib;
+    SGPath mpath(fg_root);
+    mpath.append("materials.xml");
+    if(!matlib->load(fg_root, mpath.str(), SEASON, propRoot))
+        SG_LOG( SG_GENERAL, SG_ALERT, "Error loading material lib!" );
+
+    simgear::SGModelLib::init(fg_root);
+
+    osg::ref_ptr<SGReaderWriterBTGOptions> options = new SGReaderWriterBTGOptions;
+    options->setMatlib(matlib);
+    options->setUseRandomObjects(false);
+    options->setUseRandomVegetation(false);
+
+    osg::ref_ptr<SGReaderWriterXMLOptions> obj_options = new SGReaderWriterXMLOptions;
+    obj_options->setPropRoot(propRoot);
+    osgDB::FilePathList &obj_fp = obj_options->getDatabasePathList();
+    obj_fp.clear();
+
+    osgDB::FilePathList &fp = options->getDatabasePathList();
+    fp.clear();
+
+    obj_fp.push_back(fg_root);
+
+    string_list path_list = sgPathSplit(scenery_dirs);
+    std::copy(path_list.begin(), path_list.end(), back_inserter(obj_fp));
+
+
+    for (unsigned i = 0; i < path_list.size(); i++) {
+
+        ulDir *d = ulOpenDir( path_list[i].c_str() );
+        if (d == NULL)
+            continue;
+        ulCloseDir( d );
+
+        SGPath pt( path_list[i] ), po( path_list[i] );
+        pt.append("Terrain");
+        po.append("Objects");
+
+        ulDir *td = ulOpenDir( pt.c_str() );
+        ulDir *od = ulOpenDir( po.c_str() );
+
+        if (td == NULL && od == NULL)
+            fp.push_back( path_list[i] );
+        else {
+            if (td != NULL) {
+                fp.push_back( pt.str() );
+                ulCloseDir( td );
+            }
+            if (od != NULL) {
+                fp.push_back( po.str() );
+                ulCloseDir( od );
+            }
+        }
+        // insert a marker for FGTileEntry::load(), so that
+        // FG_SCENERY=A:B becomes list ["A/Terrain", "A/Objects", "",
+        // "B/Terrain", "B/Objects", ""]
+        fp.push_back("");
+    }
+
+    double lon=-122.0, lat=37.0;
+    int id, tile;
+    string path, file;
+    double timestamp=0.0;
+
+    TileCache tile_cache;
+    tile_cache.set_max_cache_size(9*9);
+
+    SGBucket b,old_bucket;
+    b.make_bad();
+    old_bucket.make_bad();
+
+    osgViewer::Viewer viewer;
+    osg::Matrix projection;
+    projection.makePerspective(25.0, (double)width/(double)height, 1.0, 3000.0);
+
+    viewer.getCamera()->setProjectionMatrix(projection);
+    viewer.getCamera()->setClearColor(osg::Vec4(0.5, 0.6, 1.0, 1.0));
+
+    osg::Node::NodeMask cullmask=viewer.getCamera()->getCullMask();
+    cullmask &= ~simgear::RUNWAYLIGHTS_BIT;
+    cullmask &= ~simgear::GROUNDLIGHTS0_BIT;
+    cullmask &= ~simgear::GROUNDLIGHTS1_BIT;
+    cullmask &= ~simgear::GROUNDLIGHTS2_BIT;
+    viewer.getCamera()->setCullMask(cullmask);
+
+    osg::ref_ptr<osg::Image> image = new osg::Image;
+    image->allocateImage(600,600, 1, GL_RGB, GL_UNSIGNED_BYTE);
+    viewer.getCamera()->attach(osg::Camera::COLOR_BUFFER, image.get());
+    //osg::ref_ptr<MyPostDrawCallback> saveImageCB = new MyPostDrawCallback(image.get());
+    //viewer.getCamera()->setPostDrawCallback(saveImageCB.get());
+
+    viewer.setUpViewInWindow(0, 0, width, height);
+    viewer.setSceneData(sceneGraph.get());
+
+    while (!cin.eof()) {
+        double heading=0.0;
+        double alt_offset=0.0;
+        timestamp = viewer.getFrameStamp()->getReferenceTime();
+        cin >> std::skipws >> id >> path >> lat >> lon >> alt_offset >> heading >> file;
+        b = sgBucketOffset( lon, lat, 0, 0 );
+        SGPath obj_path("Objects");
+        obj_path.append(b.gen_base_path());
+
+        if(b != old_bucket)
+        {
+            old_bucket=b;
+            // add 9 tiles
+            for(int x = -1; x <= 1; ++x) {
+                for(int y = -1; y <= 1; ++y) {
+                    b = sgBucketOffset(lon, lat, x, y);
+                    add_tile(tile_cache, b, timestamp, options.get());
+                }
+            }
+        }
+
+        double radius=200.0;
+        obj_path.append(path);
+        string obj_file = findDataFile(obj_path.c_str(), obj_options.get());
+        if(obj_file.empty())
+        {
+            obj_file = findDataFile(path, obj_options.get());
+        }
+
+        //cerr << "trying " << obj_file << endl;
+        osg::ref_ptr<osg::Node> node = osgDB::readNodeFile(obj_file, obj_options.get());
+        if(node.valid())
+        {
+            radius=node->getBound().radius();
+            if(radius < 0.1 || radius > 12000.0)
+                cerr << "WARNING radius: " << node->getBound().radius() << " at " << obj_file << endl;
+        }
+            SGGeod geod=SGGeod::fromDegM(lon, lat, SG_MAX_ELEVATION_M);
+            SGVec3d start = SGVec3d::fromGeod(geod);
+            SGVec3d contact;
+
+            geod.setElevationM(-1000.0);
+            SGVec3d end = SGVec3d::fromGeod(geod);
+
+            osg::ref_ptr<osgUtil::LineSegmentIntersector> lineSegment;
+            lineSegment = new osgUtil::LineSegmentIntersector(start.osg(), end.osg());
+
+            osgUtil::IntersectionVisitor intersectVisitor(lineSegment.get());
+            intersectVisitor.setTraversalMode(osg::NodeVisitor::TRAVERSE_ALL_CHILDREN);
+            intersectVisitor.setTraversalMask(SG_NODEMASK_TERRAIN_BIT);
+
+            sceneGraph->accept(intersectVisitor);
+
+             osgUtil::LineSegmentIntersector::Intersections& intersections = lineSegment->getIntersections();
+             bool hit=false;
+             double alt=-10000.0;
+             osg::Vec3d normal;
+
+             for(osgUtil::LineSegmentIntersector::Intersections::iterator itr = intersections.begin();
+                 itr != intersections.end(); ++itr)
+             {
+                 const osgUtil::LineSegmentIntersector::Intersection& intersection = *itr;
+
+                 contact.osg() = intersection.getWorldIntersectPoint();
+                 normal = intersection.getWorldIntersectNormal();
+                 SGGeod geod = SGGeod::fromCart(contact);
+                 alt = geod.getElevationM() > alt ? geod.getElevationM() : alt;
+
+                 hit=true;
+             }
+             if(!hit)
+                 cerr << "NO HIT AT " << id << " " << lat << " " << lon << " " << std::endl;
+
+    osg::Vec3d up = start.osg() - end.osg();
+    up.normalize();
+
+    osg::Matrix obj_pos;
+    SGGeod obj_geod = SGGeod::fromDegM(lon, lat, alt + alt_offset);
+    obj_pos = obj_geod.makeZUpFrame();
+    osg::Matrix obj_pos_rot = obj_pos;
+    // hdg is not a compass heading, but a counter-clockwise rotation
+    // around the Z axis
+    obj_pos_rot.preMult(osg::Matrix::rotate(heading * SGD_DEGREES_TO_RADIANS, 0.0, 0.0, 1.0));
+
+    osg::Vec3 center(osg::Vec3(0.0f, 0.0f, radius)*obj_pos);
+
+    osg::Light * l = new osg::Light;
+    l->setLightNum(0);
+    l->setAmbient(osg::Vec4(0.23f,0.23f,0.23f,1.0f));
+    l->setDiffuse(osg::Vec4(0.43f,0.43f,0.43f,1.0f));
+    l->setSpecular(osg::Vec4(0.33f,0.33f,0.33f,1.0f));
+    l->setLinearAttenuation(0.0f);
+    l->setQuadraticAttenuation(0.0f);
+    l->setConstantAttenuation(1.0f);
+    sceneGraph->setLight(l);
+
+    if(node.valid())
+    {
+        osg::ref_ptr<osg::MatrixTransform> mt = new osg::MatrixTransform(obj_pos_rot);
+        mt->addChild(node.get());
+        sceneGraph->addChild(mt.get());
+
+        int cnt=0;
+        for(vector<Shot>::const_iterator it = shots.begin(); it != shots.end(); ++it)
+        {
+            cnt++;
+            std::ostringstream ns;
+            ns << file << "-" << cnt << ".jpg";
+
+            double dist_vertical = radius*(*it).distance_v;
+            double dist_horizontal = radius*(*it).distance_h;
+
+            osg::Matrix sun_pos = obj_pos;
+            sun_pos.preMult(osg::Matrix::rotate((heading-50.0) * SGD_DEGREES_TO_RADIANS, 0.0, 0.0, 1.0));
+            sun_pos.preMult(osg::Matrix::translate(100000.0f, 0.0f, 80000.0f));
+            osg::Vec3 sun;
+            sun = osg::Vec3(0.0f, 0.0f, 0.0f) * sun_pos;
+            l->setPosition(osg::Vec4(sun, 1.0f));
+
+            osg::Matrix eye_pos = obj_pos;
+            eye_pos.preMult(osg::Matrix::rotate((heading+(*it).heading) * SGD_DEGREES_TO_RADIANS, 0.0, 0.0, 1.0));
+            eye_pos.preMult(osg::Matrix::translate(dist_horizontal, 0.0, dist_vertical));
+            osg::Vec3 eye2;
+            eye2 = osg::Vec3f(0,0,0)*eye_pos;
+
+            viewer.getCamera()->setViewMatrixAsLookAt(eye2, center, up);
+            viewer.frame();
+            viewer.frame();
+            osgDB::writeImageFile(*image.get(), ns.str());
+        }
+        sceneGraph->removeChild(mt.get());
+    }
+    else
+    {
+        cerr << "FAILED to load " << path << " (" << obj_file << ")" << endl;
+    }
+    }
+
+    cerr << "tile_cache size= " << tile_cache.get_size() << std::endl;
+
+    return 0;
+}
+
+void add_tile(TileCache &tile_cache, SGBucket &b, double timestamp, osgDB::ReaderWriter::Options *options)
+{
+    //cerr << " ts=" << timestamp <<  endl;
+    TileEntry *t = tile_cache.get_tile(b);
+    if(!t)
+    {
+        /*
+        for(TileCache::tile_map_iterator it=tile_cache.begin(); it != tile_cache.end(); ++it)
+        {
+            cerr << "it ts=" << (*it).second->get_timestamp() << endl;
+        }
+        */
+        // remove old tiles
+        while ( (int)tile_cache.get_size() > tile_cache.get_max_cache_size() ) {
+            long index = tile_cache.get_oldest_tile();
+            if ( index >= 0 ) {
+                TileEntry *old = tile_cache.get_tile( index );
+                tile_cache.clear_entry( index );
+                //cerr << "oldest_tile " << index << " ts=" << old->get_timestamp() <<  endl;
+                old->removeFromSceneGraph();
+                delete old;
+            }
+            else
+                break;
+        }
+        t = new TileEntry(b);
+        tile_cache.insert_tile(t);
+        t->addToSceneGraph(sceneGraph.get());
+        t->getNode()->addChild(osgDB::readNodeFile(t->tileFileName, options));
+        t->prep_ssg_node(visibility);
+        t->getNode()->setUpdateCallback(0);
+    }
+    t->set_timestamp(timestamp);
+}
+
-- 
1.5.2.5


