]> git.mxchange.org Git - simgear.git/blob - simgear/scene/tgdb/TileEntry.cxx
scenery: move static ReaderWriter proxies around.
[simgear.git] / simgear / scene / tgdb / TileEntry.cxx
1 // tileentry.cxx -- routines to handle a scenery tile
2 //
3 // Written by Curtis Olson, started May 1998.
4 //
5 // Copyright (C) 1998 - 2001  Curtis L. Olson  - http://www.flightgear.org/~curt
6 //
7 // This program is free software; you can redistribute it and/or
8 // modify it under the terms of the GNU General Public License as
9 // published by the Free Software Foundation; either version 2 of the
10 // License, or (at your option) any later version.
11 //
12 // This program is distributed in the hope that it will be useful, but
13 // WITHOUT ANY WARRANTY; without even the implied warranty of
14 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
15 // General Public License for more details.
16 //
17 // You should have received a copy of the GNU General Public License
18 // along with this program; if not, write to the Free Software
19 // Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
20
21 #ifdef HAVE_CONFIG_H
22 #  include <simgear_config.h>
23 #endif
24
25 #include <simgear/compiler.h>
26
27 #include <string>
28 #include <sstream>
29 #include <istream>
30
31 #include <osg/LOD>
32 #include <osg/MatrixTransform>
33 #include <osg/Math>
34 #include <osg/NodeCallback>
35 #include <osg/ProxyNode>
36
37 #include <osgDB/FileNameUtils>
38 #include <osgDB/FileUtils>
39 #include <osgDB/ReaderWriter>
40 #include <osgDB/ReadFile>
41 #include <osgDB/Registry>
42
43 #include <simgear/bucket/newbucket.hxx>
44 #include <simgear/debug/logstream.hxx>
45 #include <simgear/math/sg_geodesy.hxx>
46 #include <simgear/math/sg_random.h>
47 #include <simgear/misc/sgstream.hxx>
48 #include <simgear/scene/material/mat.hxx>
49 #include <simgear/scene/material/matlib.hxx>
50 #include <simgear/scene/tgdb/apt_signs.hxx>
51 #include <simgear/scene/tgdb/obj.hxx>
52 #include <simgear/scene/util/OsgMath.hxx>
53 #include <simgear/scene/util/SGReaderWriterOptions.hxx>
54
55 #include "SGOceanTile.hxx"
56 #include "TileEntry.hxx"
57
58 using std::string;
59 using namespace simgear;
60
61 static ModelLoadHelper *_modelLoader=0;
62
63 static SGBucket getBucketFromFileName(const std::string& fileName)
64 {
65     std::istringstream ss(osgDB::getNameLessExtension(fileName));
66     long index;
67     ss >> index;
68     if (ss.fail())
69         return SGBucket();
70     return SGBucket(index);
71 }
72
73 static bool
74 loadStgFile(const std::string& absoluteFileName, osg::Group& group, const osgDB::Options* options)
75 {
76     if (absoluteFileName.empty())
77         return false;
78
79     sg_gzifstream in( absoluteFileName );
80     if ( !in.is_open() )
81         return false;
82
83     SG_LOG(SG_TERRAIN, SG_INFO, "Loading stg file " << absoluteFileName);
84     
85     std::string filePath = osgDB::getFilePath(absoluteFileName);
86
87     osg::ref_ptr<SGReaderWriterOptions> staticOptions;
88     staticOptions = SGReaderWriterOptions::copyOrCreate(options);
89     staticOptions->getDatabasePathList().clear();
90     staticOptions->getDatabasePathList().push_back(filePath);
91     staticOptions->setObjectCacheHint(osgDB::Options::CACHE_NONE);
92     
93     osg::ref_ptr<SGReaderWriterOptions> sharedOptions;
94     sharedOptions = SGReaderWriterOptions::copyOrCreate(options);
95     sharedOptions->getDatabasePathList().clear();
96     
97     SGPath path = filePath;
98     path.append(".."); path.append(".."); path.append("..");
99     sharedOptions->getDatabasePathList().push_back(path.str());
100     std::string fg_root = options->getPluginStringData("SimGear::FG_ROOT");
101     sharedOptions->getDatabasePathList().push_back(fg_root);
102     
103     bool has_base = false;
104     while ( ! in.eof() ) {
105         std::string token;
106         in >> token;
107         
108         // No comment
109         if ( token.empty() || token[0] == '#' ) {
110             in >> ::skipeol;
111             continue;
112         }
113         
114         // Then there is always a name
115         std::string name;
116         in >> name;
117         
118         SGPath path = filePath;
119         path.append(name);
120         
121         osg::ref_ptr<osg::Node> node;
122         if ( token == "OBJECT_BASE" ) {
123             // Load only once (first found)
124             SG_LOG( SG_TERRAIN, SG_BULK, "    " << token << " " << name );
125             
126             has_base = true;
127             node = osgDB::readRefNodeFile(path.str(),
128                                           staticOptions.get());
129                 
130             if (!node.valid()) {
131                 SG_LOG( SG_TERRAIN, SG_ALERT, absoluteFileName
132                         << ": Failed to load OBJECT_BASE '"
133                         << name << "'" );
134             }
135             
136         } else if ( token == "OBJECT" ) {
137             node = osgDB::readRefNodeFile(path.str(),
138                                           staticOptions.get());
139                 
140             if (!node.valid()) {
141                 SG_LOG( SG_TERRAIN, SG_ALERT, absoluteFileName
142                         << ": Failed to load OBJECT '"
143                         << name << "'" );
144             }
145             
146         } else {
147             double lon, lat, elev, hdg;
148             in >> lon >> lat >> elev >> hdg;
149             
150             // Always OK to load
151             if ( token == "OBJECT_STATIC" ) {
152                 /// Hmm, the findDataFile should happen downstream
153                 std::string absName = osgDB::findDataFile(name,
154                                                           staticOptions.get());
155                 if(_modelLoader) {
156                     node = _modelLoader->loadTileModel(absName, false);
157                 } else {
158                     osg::ref_ptr<SGReaderWriterOptions> opt;
159                     opt = new SGReaderWriterOptions(*staticOptions);
160                     if (SGPath(absName).lower_extension() == "ac")
161                         opt->setInstantiateEffects(true);
162                     else
163                         opt->setInstantiateEffects(false);
164                     node = osgDB::readRefNodeFile(absName, opt.get());
165                 }
166                 
167                 if (!node.valid()) {
168                     SG_LOG( SG_TERRAIN, SG_ALERT, absoluteFileName
169                             << ": Failed to load OBJECT_STATIC '"
170                             << name << "'" );
171                 }
172                 
173             } else if ( token == "OBJECT_SHARED" ) {
174                 if(_modelLoader) {
175                     node = _modelLoader->loadTileModel(name, true);
176                 } else {
177                     osg::ref_ptr<SGReaderWriterOptions> opt;
178                     opt = new SGReaderWriterOptions(*sharedOptions);
179
180                     /// Hmm, the findDataFile should happen in the downstream readers
181                     std::string absName = osgDB::findDataFile(name, opt.get());
182
183                     osg::ProxyNode* proxyNode = new osg::ProxyNode;
184                     proxyNode->setLoadingExternalReferenceMode(osg::ProxyNode::DEFER_LOADING_TO_DATABASE_PAGER);
185                     proxyNode->setFileName(0, absName);
186                     if (SGPath(absName).lower_extension() == "ac")
187                         opt->setInstantiateEffects(true);
188                     else
189                         opt->setInstantiateEffects(false);
190                     proxyNode->setDatabaseOptions(opt.get());
191                     node = proxyNode;
192                 }
193                 
194                 if (!node.valid()) {
195                     SG_LOG( SG_TERRAIN, SG_ALERT, absoluteFileName
196                             << ": Failed to load OBJECT_SHARED '"
197                             << name << "'" );
198                 }
199                 
200             } else if ( token == "OBJECT_SIGN" ) {
201                 node = SGMakeSign(staticOptions->getMaterialLib(), name);
202                 
203             } else if ( token == "OBJECT_RUNWAY_SIGN" ) {
204                 node = SGMakeRunwaySign(staticOptions->getMaterialLib(), name);
205                 
206             } else {
207                 SG_LOG( SG_TERRAIN, SG_ALERT, absoluteFileName
208                         << ": Unknown token '" << token << "'" );
209             }
210             
211             if (node.valid() && token != "OBJECT") {
212                 osg::Matrix matrix;
213                 matrix = makeZUpFrame(SGGeod::fromDegM(lon, lat, elev));
214                 matrix.preMultRotate(osg::Quat(SGMiscd::deg2rad(hdg),
215                                                osg::Vec3(0, 0, 1)));
216                 
217                 osg::MatrixTransform* matrixTransform;
218                 matrixTransform = new osg::MatrixTransform(matrix);
219                 matrixTransform->setDataVariance(osg::Object::STATIC);
220                 matrixTransform->addChild(node.get());
221                 node = matrixTransform;
222             }
223         }
224         
225         if (node.valid())
226             group.addChild(node.get());
227         
228         in >> ::skipeol;
229     }
230
231     return has_base;
232 }
233     
234 void
235 TileEntry::setModelLoadHelper(ModelLoadHelper *m)
236 {
237     _modelLoader=m;
238 }
239
240 // Constructor
241 TileEntry::TileEntry ( const SGBucket& b )
242     : tile_bucket( b ),
243       tileFileName(b.gen_index_str()),
244       _node( new osg::LOD ),
245       _priority(-FLT_MAX),
246       _current_view(false),
247       _time_expired(-1.0)
248 {
249     tileFileName += ".stg";
250     _node->setName(tileFileName);
251     // Give a default LOD range so that traversals that traverse
252     // active children (like the groundcache lookup) will work before
253     // tile manager has had a chance to update this node.
254     _node->setRange(0, 0.0, 10000.0);
255 }
256
257 TileEntry::TileEntry( const TileEntry& t )
258 : tile_bucket( t.tile_bucket ),
259   tileFileName(t.tileFileName),
260   _node( new osg::LOD ),
261   _priority(t._priority),
262   _current_view(t._current_view),
263   _time_expired(t._time_expired)
264 {
265     _node->setName(tileFileName);
266     // Give a default LOD range so that traversals that traverse
267     // active children (like the groundcache lookup) will work before
268     // tile manager has had a chance to update this node.
269     _node->setRange(0, 0.0, 10000.0);
270 }
271
272 // Destructor
273 TileEntry::~TileEntry ()
274 {
275 }
276
277 // Update the ssg transform node for this tile so it can be
278 // properly drawn relative to our (0,0,0) point
279 void TileEntry::prep_ssg_node(float vis) {
280     if (!is_loaded())
281         return;
282     // visibility can change from frame to frame so we update the
283     // range selector cutoff's each time.
284     float bounding_radius = _node->getChild(0)->getBound().radius();
285     _node->setRange( 0, 0, vis + bounding_radius );
286 }
287
288 osg::Node*
289 TileEntry::loadTileByFileName(const string& fileName,
290                               const osgDB::Options* options)
291 {
292     SG_LOG(SG_TERRAIN, SG_INFO, "Loading tile " << fileName);
293
294     // We treat 123.stg different than ./123.stg.
295     // The difference is that ./123.stg as well as any absolute path
296     // really loads the given stg file and only this.
297     // In contrast 123.stg uses the search paths to load a set of stg
298     // files spread across the scenery directories.
299     std::string simpleFileName = osgDB::getSimpleFileName(fileName);
300     SGBucket bucket = getBucketFromFileName(simpleFileName);
301     osg::ref_ptr<osg::Group> group = new osg::Group;
302     if (simpleFileName != fileName || !options) {
303         // This is considered a real existing file.
304         // We still apply the search path algorithms for relative files.
305         loadStgFile(osgDB::findDataFile(fileName, options), *group, options);
306         return group.release();
307     }
308
309     // This is considered a meta file, so apply the scenery path search
310     const osgDB::FilePathList& filePathList = options->getDatabasePathList();
311     std::string basePath = bucket.gen_base_path();
312     // Stop scanning once an object base is found
313     bool foundBase = false;
314     for (osgDB::FilePathList::const_iterator i = filePathList.begin();
315          i != filePathList.end() && !foundBase; ++i) {
316         SGPath objects(*i);
317         objects.append("Objects");
318         objects.append(basePath);
319         objects.append(simpleFileName);
320         if (loadStgFile(objects.str(), *group, options))
321             foundBase = true;
322         
323         SGPath terrain(*i);
324         terrain.append("Terrain");
325         terrain.append(basePath);
326         terrain.append(simpleFileName);
327         if (loadStgFile(terrain.str(), *group, options))
328             foundBase = true;
329     }
330     
331     // ... or generate an ocean tile on the fly
332     if (!foundBase) {
333         SG_LOG(SG_TERRAIN, SG_INFO, "  Generating ocean tile");
334         
335         osg::ref_ptr<SGReaderWriterOptions> opt;
336         opt = SGReaderWriterOptions::copyOrCreate(options);
337         osg::Node* node = SGOceanTile(bucket, opt->getMaterialLib());
338         if ( node ) {
339             group->addChild(node);
340         } else {
341             SG_LOG( SG_TERRAIN, SG_ALERT,
342                     "Warning: failed to generate ocean tile!" );
343         }
344     }
345     return group.release();
346 }
347
348 void
349 TileEntry::addToSceneGraph(osg::Group *terrain_branch)
350 {
351     terrain_branch->addChild( _node.get() );
352
353     SG_LOG( SG_TERRAIN, SG_DEBUG,
354             "connected a tile into scene graph.  _node = "
355             << _node.get() );
356     SG_LOG( SG_TERRAIN, SG_DEBUG, "num parents now = "
357             << _node->getNumParents() );
358 }
359
360
361 void
362 TileEntry::removeFromSceneGraph()
363 {
364     SG_LOG( SG_TERRAIN, SG_DEBUG, "disconnecting TileEntry nodes" );
365
366     if (! is_loaded()) {
367         SG_LOG( SG_TERRAIN, SG_DEBUG, "removing a not-fully loaded tile!" );
368     } else {
369         SG_LOG( SG_TERRAIN, SG_DEBUG, "removing a fully loaded tile!  _node = " << _node.get() );
370     }
371
372     // find the nodes branch parent
373     if ( _node->getNumParents() > 0 ) {
374         // find the first parent (should only be one)
375         osg::Group *parent = _node->getParent( 0 ) ;
376         if( parent ) {
377             parent->removeChild( _node.get() );
378         }
379     }
380 }
381
382 void
383 TileEntry::refresh()
384 {
385     osg::Group *parent = NULL;
386     // find the nodes branch parent
387     if ( _node->getNumParents() > 0 ) {
388         // find the first parent (should only be one)
389         parent = _node->getParent( 0 ) ;
390         if( parent ) {
391             parent->removeChild( _node.get() );
392         }
393     }
394     _node = new osg::LOD;
395     if (parent)
396         parent->addChild(_node.get());
397 }