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