root/OpenSceneGraph/trunk/applications/osgfilecache/osgfilecache.cpp @ 12697

Revision 12697, 16.1 kB (checked in by robert, 3 years ago)

From Alberto Luacas, typo fixes

  • Property svn:eol-style set to native
Line 
1/* -*-c++-*- OpenSceneGraph - Copyright (C) 1998-2006 Robert Osfield
2 *
3 * This application is open source and may be redistributed and/or modified   
4 * freely and without restriction, both in commercial and non commercial applications,
5 * as long as this copyright notice is maintained.
6 *
7 * This application is distributed in the hope that it will be useful,
8 * but WITHOUT ANY WARRANTY; without even the implied warranty of
9 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
10*/
11
12#include <osg/Timer>
13#include <osg/ArgumentParser>
14#include <osg/ApplicationUsage>
15#include <osg/CoordinateSystemNode>
16#include <osg/Geometry>
17#include <osg/PagedLOD>
18#include <osg/io_utils>
19
20#include <osgTerrain/TerrainTile>
21
22#include <osgDB/Archive>
23#include <osgDB/ReadFile>
24#include <osgDB/WriteFile>
25#include <osgDB/FileUtils>
26#include <osgDB/FileCache>
27#include <osgDB/FileNameUtils>
28
29#include <iostream>
30#include <algorithm>
31
32#include <signal.h>
33#include <stdlib.h>
34
35static bool s_ExitApplication = false;
36
37struct Extents
38{
39
40    Extents():
41        _maxLevel(0),
42        _min(DBL_MAX,DBL_MAX),
43        _max(-DBL_MAX,-DBL_MAX) {}
44
45    Extents(unsigned int maxLevel, double minX, double minY, double maxX, double maxY):
46        _maxLevel(maxLevel),
47        _min(minX, minY),
48        _max(maxX, maxY) {}
49   
50    Extents(const Extents& extents):
51        _maxLevel(extents._maxLevel),
52        _min(extents._min),
53        _max(extents._max) {}
54       
55    Extents& operator = (const Extents& rhs)
56    {
57        if (&rhs == this) return *this;
58       
59        _maxLevel = rhs._maxLevel;
60        _min = rhs._min;
61        _max = rhs._max;
62               
63        return *this;
64    }
65
66    bool intersects(unsigned level, osg::Vec2d& in_min, osg::Vec2d& in_max)
67    {
68        osg::notify(osg::INFO)<<"intersects("<<level<<", min="<<in_min<<" max="<<in_max<<")"<<std::endl;
69        osg::notify(osg::INFO)<<"  _maxLevel="<<_maxLevel<<", _min="<<_min<<" _max="<<_max<<std::endl;
70
71        if (level>_maxLevel) return false;
72       
73        osg::Vec2d union_min, union_max;
74
75        // handle mins       
76        if (_min.x()!=DBL_MAX && in_min.x()!=DBL_MAX)
77        {
78            // both _min.x() and in_min.x() are defined so use max of two
79            union_min.x() = _min.x()>in_min.x() ? _min.x() : in_min.x();
80        }
81        else
82        {
83            // one or both _min.x() and in_min.x() aren't defined so use min of two
84            union_min.x() = _min.x()<in_min.x() ? _min.x() : in_min.x();
85        }
86
87        if (_min.y()!=DBL_MAX && in_min.y()!=DBL_MAX)
88        {
89            // both _min.y() and in_min.y() are defined so use max of two
90            union_min.y() = _min.y()>in_min.y() ? _min.y() : in_min.y();
91        }
92        else
93        {
94            // one or both _min.y() and in_min.y() aren't defined so use min of two
95            union_min.y() = _min.y()<in_min.y() ? _min.y() : in_min.y();
96        }
97
98        // handle maxs       
99        if (_max.x()!=-DBL_MAX && in_max.x()!=-DBL_MAX)
100        {
101            // both _max.x() and in_max.x() are defined so use max of two
102            union_max.x() = _max.x()<in_max.x() ? _max.x() : in_max.x();
103        }
104        else
105        {
106            // one or both _max.x() and in_max.x() aren't defined so use max of two
107            union_max.x() = _max.x()>in_max.x() ? _max.x() : in_max.x();
108        }
109
110        if (_max.y()!=-DBL_MAX && in_max.y()!=-DBL_MAX)
111        {
112            // both _max.y() and in_max.y() are defined so use max of two
113            union_max.y() = _max.y()<in_max.y() ? _max.y() : in_max.y();
114        }
115        else
116        {
117            // one or both _max.y() and in_max.y() aren't defined so use max of two
118            union_max.y() = _max.y()>in_max.y() ? _max.y() : in_max.y();
119        }
120
121        bool result = union_min.x()<union_max.x() && union_min.y()<union_max.y() ;
122
123        osg::notify(osg::INFO)<<"  union_min="<<union_min<<" union_max="<<union_max<<" result = "<<result<<std::endl;
124       
125        return result;
126    }
127
128    unsigned int    _maxLevel;
129    osg::Vec2d      _min;
130    osg::Vec2d      _max;
131};
132
133class CheckValidVisitor : public osg::NodeVisitor
134{
135public:
136
137    CheckValidVisitor():
138        osg::NodeVisitor(osg::NodeVisitor::TRAVERSE_ALL_CHILDREN),
139        _numInvalidGeometries(0) {}
140
141    void apply(osg::Geode& geode)
142    {
143        unsigned int local_numInvalidGeometries = 0;
144        for(unsigned int i=0; i<geode.getNumDrawables(); ++i)
145        {
146            osg::Geometry* geometry = dynamic_cast<osg::Geometry*>(geode.getDrawable(i));
147            if (geometry)
148            {
149                if (!geometry->verifyArrays(_errorReports)) ++local_numInvalidGeometries;
150            }
151        }
152        if (local_numInvalidGeometries)
153        {
154            _errorReports<<"Geode "<<geode.getName()<<" contains problem geometries"<<std::endl;
155            _numInvalidGeometries += local_numInvalidGeometries;
156        }
157    }
158
159    bool valid() const { return _numInvalidGeometries==0; }
160
161    unsigned int _numInvalidGeometries;
162    std::stringstream _errorReports;
163   
164};
165
166
167class LoadDataVisitor : public osg::NodeVisitor
168{
169public:
170
171
172    LoadDataVisitor():
173        osg::NodeVisitor(osg::NodeVisitor::TRAVERSE_ALL_CHILDREN),
174        _currentLevel(0) {}
175
176    void setFileCache(osgDB::FileCache* fileCache) { _fileCache = fileCache; }
177       
178    void addExtents(unsigned int maxLevel, double minX, double minY, double maxX, double maxY)
179    {
180        _extentsList.push_back(Extents(maxLevel, minX, minY, maxX, maxY));
181    }
182
183    void addExtents(unsigned int maxLevel)
184    {
185        _extentsList.push_back(Extents(maxLevel, DBL_MAX, DBL_MAX, -DBL_MAX, -DBL_MAX));
186    }
187
188    void apply(osg::CoordinateSystemNode& cs)
189    {
190        _csnStack.push_back(&cs);
191       
192        if (!s_ExitApplication) traverse(cs);
193
194        _csnStack.pop_back();
195    }
196   
197    void apply(osg::Group& group)
198    {
199        if (s_ExitApplication) return;
200   
201        osgTerrain::TerrainTile* terrainTile = dynamic_cast<osgTerrain::TerrainTile*>(&group);
202        osgTerrain::Locator* locator = terrainTile ? terrainTile->getLocator() : 0;
203        if (locator)
204        {
205            osg::Vec3d l00(0.0,0.0,0.0);
206            osg::Vec3d l10(1.0,0.0,0.0);
207            osg::Vec3d l11(1.0,1.0,0.0);
208            osg::Vec3d l01(0.0,1.0,0.0);
209
210            osg::Vec3d w00, w10, w11, w01;
211
212            locator->convertLocalToModel(l00, w00);
213            locator->convertLocalToModel(l10, w10);
214            locator->convertLocalToModel(l11, w11);
215            locator->convertLocalToModel(l01, w01);
216
217            if (locator->getEllipsoidModel() &&
218                locator->getCoordinateSystemType()==osgTerrain::Locator::GEOCENTRIC)
219            {
220                convertXYZToLatLongHeight(locator->getEllipsoidModel(), w00);
221                convertXYZToLatLongHeight(locator->getEllipsoidModel(), w10);
222                convertXYZToLatLongHeight(locator->getEllipsoidModel(), w11);
223                convertXYZToLatLongHeight(locator->getEllipsoidModel(), w01);
224            }
225
226            updateBound(w00);
227            updateBound(w10);
228            updateBound(w11);
229            updateBound(w01);
230
231            return;
232        }
233       
234        traverse(group);
235    }
236
237    void apply(osg::Transform& transform)
238    {
239        osg::Matrix matrix;
240        if (!_matrixStack.empty()) matrix = _matrixStack.back();
241
242        transform.computeLocalToWorldMatrix(matrix,this);
243
244        pushMatrix(matrix);
245
246        traverse(transform);
247
248        popMatrix();
249    }
250
251    void apply(osg::PagedLOD& plod)
252    {
253        if (s_ExitApplication) return;
254   
255        ++_currentLevel;
256       
257        initBound();
258       
259        // first compute the bounds of this subgraph
260        for(unsigned int i=0; i<plod.getNumFileNames(); ++i)
261        {
262            if (plod.getFileName(i).empty())
263            {
264                traverse(plod);
265            }
266        }
267       
268        if (intersects())
269        {
270            for(unsigned int i=0; i<plod.getNumFileNames(); ++i)
271            {
272                osg::notify(osg::INFO)<<"   filename["<<i<<"] "<<plod.getFileName(i)<<std::endl;
273                if (!plod.getFileName(i).empty())
274                {
275                    std::string filename;
276                    if (!plod.getDatabasePath().empty())
277                    {
278                        filename = plod.getDatabasePath() + plod.getFileName(i);
279                    }
280                    else
281                    {
282                        filename = plod.getFileName(i);
283                    }
284
285
286                    osg::ref_ptr<osg::Node> node = readNodeFileAndWriteToCache(filename);
287
288                    if (!s_ExitApplication && node.valid()) node->accept(*this);
289                }
290            }
291        }
292       
293        --_currentLevel;
294    }
295   
296    void apply(osg::Geode& geode)
297    {
298        for(unsigned int i=0; i<geode.getNumDrawables(); ++i)
299        {
300            osg::Geometry* geom = geode.getDrawable(i)->asGeometry();
301            if (geom)
302            {
303                osg::Vec3Array* vertices = dynamic_cast<osg::Vec3Array*>(geom->getVertexArray());
304                if (vertices) updateBound(*vertices);
305            }
306        }
307    }
308   
309    osg::Node* readNodeFileAndWriteToCache(const std::string& filename)
310    {
311        osg::Node* node = 0;
312        if (_fileCache.valid() && osgDB::containsServerAddress(filename))
313        {
314            if (_fileCache->existsInCache(filename))
315            {
316                osg::notify(osg::NOTICE)<<"reading from FileCache: "<<filename<<std::endl;
317                node = _fileCache->readNode(filename, osgDB::Registry::instance()->getOptions()).takeNode();
318            }
319            else
320            {
321                osg::notify(osg::NOTICE)<<"reading : "<<filename<<std::endl;
322
323                node = osgDB::readNodeFile(filename);
324                if (node)
325                {
326                    osg::notify(osg::NOTICE)<<"write to FileCache : "<<filename<<std::endl;
327
328                    _fileCache->writeNode(*node, filename, osgDB::Registry::instance()->getOptions());
329                }
330            }
331        }
332        else
333        {
334            osg::notify(osg::NOTICE)<<"reading : "<<filename<<std::endl;
335            node = osgDB::readNodeFile(filename);
336        }
337
338        if (node)
339        {
340            CheckValidVisitor cvv;
341            node->accept(cvv);
342            if (!cvv.valid())
343            {
344                OSG_NOTICE<<"Warning, errors in geometry found in file "<<filename<<std::endl;
345                OSG_NOTICE<<cvv._errorReports.str()<<std::endl;
346            }
347        }
348        return node;
349    }
350
351
352protected:
353
354    inline void pushMatrix(osg::Matrix& matrix) { _matrixStack.push_back(matrix); }
355   
356    inline void popMatrix() { _matrixStack.pop_back(); }
357
358    void convertXYZToLatLongHeight(osg::EllipsoidModel* em, osg::Vec3d& v)
359    {
360        em->convertXYZToLatLongHeight(v.x(), v.y(), v.z(),
361                                      v.y(), v.x(), v.z());
362                                     
363        v.x() = osg::RadiansToDegrees(v.x());
364        v.y() = osg::RadiansToDegrees(v.y());
365    }
366   
367    void initBound()
368    {
369        _min.set(DBL_MAX, DBL_MAX);
370        _max.set(-DBL_MAX, -DBL_MAX);
371    }
372
373    void updateBound(osg::Vec3d& v)
374    {
375        if (v.x() < _min.x()) _min.x() = v.x();
376        if (v.y() < _min.y()) _min.y() = v.y();
377        if (v.x() > _max.x()) _max.x() = v.x();
378        if (v.y() > _max.y()) _max.y() = v.y();
379    }
380   
381    void updateBound(osg::Vec3Array& vertices)
382    {
383        // set up matrix
384        osg::Matrix matrix;
385        if (!_matrixStack.empty()) matrix = _matrixStack.back();
386       
387        // set up ellipsoid model
388        osg::EllipsoidModel* em = !_csnStack.empty() ?  _csnStack.back()->getEllipsoidModel() : 0;
389       
390        for(osg::Vec3Array::iterator itr = vertices.begin();
391            itr != vertices.end();
392            ++itr)
393        {
394            osg::Vec3d v = osg::Vec3d(*itr) * matrix;
395            if (em) convertXYZToLatLongHeight(em, v);
396
397            updateBound(v);
398        }
399    }
400   
401    bool intersects()
402    {
403        osg::notify(osg::INFO)<<"intersects() _min = "<<_min<<" _max = "<<_max<<std::endl;
404        for(ExtentsList::iterator itr = _extentsList.begin();
405            itr != _extentsList.end();
406            ++itr)
407        {
408            if (itr->intersects(_currentLevel, _min, _max)) return true;
409        }
410       
411        return false;
412    }
413
414    typedef std::vector<Extents>                    ExtentsList;
415    typedef std::vector<osg::Matrix>                MatrixStack;
416    typedef std::vector<osg::CoordinateSystemNode*> CSNStack;
417
418    osg::ref_ptr<osgDB::FileCache>  _fileCache;
419
420    ExtentsList     _extentsList;
421    unsigned int    _currentLevel;
422    MatrixStack     _matrixStack;
423    CSNStack        _csnStack;
424   
425    osg::Vec2d      _min;
426    osg::Vec2d      _max;
427};
428
429static void signalHandler(int sig)
430{
431    printf("\nCaught signal %d, requesting exit...\n\n",sig);
432    s_ExitApplication = true;
433}
434
435int main( int argc, char **argv )
436{
437#ifndef _WIN32
438    signal(SIGHUP, signalHandler);
439    signal(SIGQUIT, signalHandler);
440    signal(SIGKILL, signalHandler);
441    signal(SIGUSR1, signalHandler);
442    signal(SIGUSR2, signalHandler);
443#endif
444    signal(SIGABRT, signalHandler);
445    signal(SIGINT, signalHandler);
446    signal(SIGTERM, signalHandler);
447
448
449    // use an ArgumentParser object to manage the program arguments.
450    osg::ArgumentParser arguments(&argc,argv);
451   
452    // set up the usage document, in case we need to print out how to use this program.
453    arguments.getApplicationUsage()->setApplicationName(arguments.getApplicationName());
454    arguments.getApplicationUsage()->setDescription(arguments.getApplicationName()+" is an application for collecting a set of separate files into a single archive file that can be later read in OSG applications..");
455    arguments.getApplicationUsage()->setCommandLineUsage(arguments.getApplicationName()+" [options] filename ...");
456    arguments.getApplicationUsage()->addCommandLineOption("-l level","Read down to level across the whole database.");
457    arguments.getApplicationUsage()->addCommandLineOption("-e level minX minY maxX maxY","Read down to <level> across the extents minX, minY to maxY, maxY.  Note, for geocentric datase X and Y are longitude and latitude respectively.");
458    arguments.getApplicationUsage()->addCommandLineOption("-c directory","Shorthand for --file-cache directory.");
459    arguments.getApplicationUsage()->addCommandLineOption("--file-cache directory","Set directory as to place cache download files.");
460       
461    // if user request help write it out to cout.
462    if (arguments.read("-h") || arguments.read("--help"))
463    {
464        arguments.getApplicationUsage()->write(std::cout);
465        return 1;
466    }
467   
468    LoadDataVisitor ldv;
469
470    std::string fileCachePath;
471    while(arguments.read("--file-cache",fileCachePath) || arguments.read("-c",fileCachePath)) {}
472   
473    if (fileCachePath.empty())
474    {   
475        const char* env_fileCachePath = getenv("OSG_FILE_CACHE");
476        if (env_fileCachePath)
477        {
478            fileCachePath = env_fileCachePath;
479        }
480    }
481
482    if (fileCachePath.empty())
483    {
484        std::cout<<"No path to the file cache defined, please set OSG_FILE_PATH env var, or use --file-cache <directory> to set a suitable directory for the file cache."<<std::endl;
485        return 1;
486    }
487   
488    ldv.setFileCache(new osgDB::FileCache(fileCachePath));
489
490    unsigned int maxLevels = 0;
491    while(arguments.read("-l",maxLevels))
492    {
493        ldv.addExtents(maxLevels);
494    }
495   
496    double minX, maxX, minY, maxY;
497    while(arguments.read("-e",maxLevels, minX, minY, maxX, maxY))
498    {
499        ldv.addExtents(maxLevels, minX, minY, maxX, maxY);
500    }
501
502   
503    std::string filename;
504    for(int i=1; i<arguments.argc(); ++i)
505    {
506        if (!arguments.isOption(i))
507        {
508            filename = arguments[i];
509            break;
510        }
511    }
512   
513    if (filename.empty())
514    {
515        std::cout<<"No file to load specified."<<std::endl;
516        return 1;
517    }
518   
519    osg::ref_ptr<osg::Node> loadedModel = ldv.readNodeFileAndWriteToCache(filename);
520    if (!loadedModel)
521    {
522        std::cout<<"No data loaded, please specify a database to load"<<std::endl;
523        return 1;
524    }
525   
526   
527    loadedModel->accept(ldv);
528
529    if (s_ExitApplication)
530    {
531        std::cout<<"osgfilecache cleanly exited in response to signal."<<std::endl;
532    }
533
534    return 0;
535}
Note: See TracBrowser for help on using the browser.