]> git.mxchange.org Git - simgear.git/blob - simgear/scene/tgdb/TileEntry.cxx
Consolidate the different ReaderWriterOptions classes.
[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/math/SGMath.hxx>
46 #include <simgear/misc/sgstream.hxx>
47 #include <simgear/scene/material/mat.hxx>
48 #include <simgear/scene/material/matlib.hxx>
49 #include <simgear/scene/model/ModelRegistry.hxx>
50 #include <simgear/scene/tgdb/apt_signs.hxx>
51 #include <simgear/scene/tgdb/obj.hxx>
52 #include <simgear/scene/util/SGReaderWriterOptions.hxx>
53
54 #include "ReaderWriterSTG.hxx"
55 #include "TileEntry.hxx"
56
57 using std::string;
58 using namespace simgear;
59
60 ModelLoadHelper *TileEntry::_modelLoader=0;
61
62 namespace {
63 osgDB::RegisterReaderWriterProxy<ReaderWriterSTG> g_readerWriterSTGProxy;
64 ModelRegistryCallbackProxy<LoadOnlyCallback> g_stgCallbackProxy("stg");
65 }
66
67
68 // Constructor
69 TileEntry::TileEntry ( const SGBucket& b )
70     : tile_bucket( b ),
71       tileFileName(b.gen_index_str()),
72       _node( new osg::LOD ),
73       _priority(-FLT_MAX),
74       _current_view(false),
75       _time_expired(-1.0)
76 {
77     tileFileName += ".stg";
78     _node->setName(tileFileName);
79     // Give a default LOD range so that traversals that traverse
80     // active children (like the groundcache lookup) will work before
81     // tile manager has had a chance to update this node.
82     _node->setRange(0, 0.0, 10000.0);
83 }
84
85 TileEntry::TileEntry( const TileEntry& t )
86 : tile_bucket( t.tile_bucket ),
87   tileFileName(t.tileFileName),
88   _node( new osg::LOD ),
89   _priority(t._priority),
90   _current_view(t._current_view),
91   _time_expired(t._time_expired)
92 {
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 // Destructor
101 TileEntry::~TileEntry ()
102 {
103 }
104
105 static void WorldCoordinate(osg::Matrix& obj_pos, double lat,
106                             double lon, double elev, double hdg)
107 {
108     SGGeod geod = SGGeod::fromDegM(lon, lat, elev);
109     obj_pos = geod.makeZUpFrame();
110     // hdg is not a compass heading, but a counter-clockwise rotation
111     // around the Z axis
112     obj_pos.preMult(osg::Matrix::rotate(hdg * SGD_DEGREES_TO_RADIANS,
113                                         0.0, 0.0, 1.0));
114 }
115
116
117 // Update the ssg transform node for this tile so it can be
118 // properly drawn relative to our (0,0,0) point
119 void TileEntry::prep_ssg_node(float vis) {
120     if (!is_loaded())
121         return;
122     // visibility can change from frame to frame so we update the
123     // range selector cutoff's each time.
124     float bounding_radius = _node->getChild(0)->getBound().radius();
125     _node->setRange( 0, 0, vis + bounding_radius );
126 }
127
128 bool TileEntry::obj_load(const string& path, osg::Group *geometry, bool is_base,
129                          const osgDB::ReaderWriter::Options* options)
130 {
131     osg::Node* node = osgDB::readNodeFile(path, options);
132     if (node)
133       geometry->addChild(node);
134
135     return node != 0;
136 }
137
138
139 typedef enum {
140     OBJECT,
141     OBJECT_SHARED,
142     OBJECT_STATIC,
143     OBJECT_SIGN,
144     OBJECT_RUNWAY_SIGN
145 } object_type;
146
147
148 // storage class for deferred object processing in TileEntry::load()
149 struct Object {
150     Object(object_type t, const string& token, const SGPath& p,
151            std::istream& in)
152         : type(t), path(p)
153     {
154         in >> name;
155         if (type != OBJECT)
156             in >> lon >> lat >> elev >> hdg;
157         in >> ::skipeol;
158
159         if (type == OBJECT)
160             SG_LOG(SG_TERRAIN, SG_BULK, "    " << token << "  " << name);
161         else
162             SG_LOG(SG_TERRAIN, SG_BULK, "    " << token << "  " << name << "  lon=" <<
163                     lon << "  lat=" << lat << "  elev=" << elev << "  hdg=" << hdg);
164     }
165     object_type type;
166     string name;
167     SGPath path;
168     double lon, lat, elev, hdg;
169 };
170
171 // Work in progress... load the tile based entirely by name cuz that's
172 // what we'll want to do with the database pager.
173
174 osg::Node*
175 TileEntry::loadTileByFileName(const string& fileName,
176                               const osgDB::ReaderWriter::Options* options)
177 {
178     std::string index_str = osgDB::getNameLessExtension(fileName);
179     index_str = osgDB::getSimpleFileName(index_str);
180
181     long tileIndex;
182     {
183         std::istringstream idxStream(index_str);
184         idxStream >> tileIndex;
185     }
186     SGBucket tile_bucket(tileIndex);
187     const string basePath = tile_bucket.gen_base_path();
188
189     bool found_tile_base = false;
190
191     SGPath object_base;
192     vector<const Object*> objects;
193
194     SG_LOG( SG_TERRAIN, SG_INFO, "Loading tile " << index_str );
195
196     osgDB::FilePathList path_list=options->getDatabasePathList();
197     // Make sure we find the original filename here...
198     std::string filePath = osgDB::getFilePath(fileName);
199     if (!filePath.empty())
200         path_list.push_front(filePath);
201
202     // scan and parse all files and store information
203     for (unsigned int i = 0; i < path_list.size(); i++) {
204         // If we found a terrain tile in Terrain/, we have to process the
205         // Objects/ dir in the same group, too, before we can stop scanning.
206         // FGGlobals::set_fg_scenery() inserts an empty string to path_list
207         // as marker.
208
209         if (path_list[i].empty()) {
210             if (found_tile_base)
211                 break;
212             else
213                 continue;
214         }
215
216         bool has_base = false;
217
218         SGPath tile_path = path_list[i];
219         tile_path.append(basePath);
220
221         SGPath basename = tile_path;
222         basename.append( index_str );
223
224         SG_LOG( SG_TERRAIN, SG_DEBUG, "  Trying " << basename.str() );
225
226
227         // Check for master .stg (scene terra gear) file
228         SGPath stg_name = basename;
229         stg_name.concat( ".stg" );
230
231         sg_gzifstream in( stg_name.str() );
232         if ( !in.is_open() )
233             continue;
234
235         while ( ! in.eof() ) {
236             string token;
237             in >> token;
238
239             if ( token.empty() || token[0] == '#' ) {
240                in >> ::skipeol;
241                continue;
242             }
243                             // Load only once (first found)
244             if ( token == "OBJECT_BASE" ) {
245                 string name;
246                 in >> name >> ::skipws;
247                 SG_LOG( SG_TERRAIN, SG_BULK, "    " << token << " " << name );
248
249                 if (!found_tile_base) {
250                     found_tile_base = true;
251                     has_base = true;
252
253                     object_base = tile_path;
254                     object_base.append(name);
255
256                 } else
257                     SG_LOG(SG_TERRAIN, SG_BULK, "    (skipped)");
258
259                             // Load only if base is not in another file
260             } else if ( token == "OBJECT" ) {
261                 if (!found_tile_base || has_base)
262                     objects.push_back(new Object(OBJECT, token, tile_path, in));
263                 else {
264                     string name;
265                     in >> name >> ::skipeol;
266                     SG_LOG(SG_TERRAIN, SG_BULK, "    " << token << "  "
267                             << name << "  (skipped)");
268                 }
269
270                             // Always OK to load
271             } else if ( token == "OBJECT_STATIC" ) {
272                 objects.push_back(new Object(OBJECT_STATIC, token, tile_path, in));
273
274             } else if ( token == "OBJECT_SHARED" ) {
275                 objects.push_back(new Object(OBJECT_SHARED, token, tile_path, in));
276
277             } else if ( token == "OBJECT_SIGN" ) {
278                 objects.push_back(new Object(OBJECT_SIGN, token, tile_path, in));
279
280             } else if ( token == "OBJECT_RUNWAY_SIGN" ) {
281                 objects.push_back(new Object(OBJECT_RUNWAY_SIGN, token, tile_path, in));
282
283             } else {
284                 SG_LOG( SG_TERRAIN, SG_DEBUG,
285                         "Unknown token '" << token << "' in " << stg_name.str() );
286                 in >> ::skipws;
287             }
288         }
289     }
290
291     const SGReaderWriterOptions* btgOpt;
292     btgOpt = dynamic_cast<const SGReaderWriterOptions*>(options);
293     osg::ref_ptr<SGReaderWriterOptions> opt;
294     if (btgOpt)
295         opt = new SGReaderWriterOptions(*btgOpt);
296     else
297         opt = new SGReaderWriterOptions;
298
299     // obj_load() will generate ground lighting for us ...
300     osg::Group* new_tile = new osg::Group;
301
302     if (found_tile_base) {
303         // load tile if found ...
304         obj_load( object_base.str(), new_tile, true, opt.get());
305
306     } else {
307         // ... or generate an ocean tile on the fly
308         SG_LOG(SG_TERRAIN, SG_INFO, "  Generating ocean tile");
309         if ( !SGGenTile( path_list[0], tile_bucket,
310                         opt->getMaterialLib(), new_tile ) ) {
311             SG_LOG( SG_TERRAIN, SG_ALERT,
312                     "Warning: failed to generate ocean tile!" );
313         }
314     }
315
316
317     // now that we have a valid center, process all the objects
318     for (unsigned int j = 0; j < objects.size(); j++) {
319         const Object *obj = objects[j];
320
321         if (obj->type == OBJECT) {
322             SGPath custom_path = obj->path;
323             custom_path.append( obj->name );
324             obj_load( custom_path.str(), new_tile, false, opt.get());
325
326         } else if (obj->type == OBJECT_SHARED || obj->type == OBJECT_STATIC) {
327             // object loading is deferred to main render thread,
328             // but lets figure out the paths right now.
329             SGPath custom_path;
330             if ( obj->type == OBJECT_STATIC ) {
331                 custom_path = obj->path;
332             } else {
333                 // custom_path = globals->get_fg_root();
334             }
335             custom_path.append( obj->name );
336
337             osg::Matrix obj_pos;
338             WorldCoordinate( obj_pos, obj->lat, obj->lon, obj->elev, obj->hdg );
339
340             osg::MatrixTransform *obj_trans = new osg::MatrixTransform;
341             obj_trans->setDataVariance(osg::Object::STATIC);
342             obj_trans->setMatrix( obj_pos );
343
344             // wire as much of the scene graph together as we can
345             new_tile->addChild( obj_trans );
346
347             osg::Node* model = 0;
348             if(_modelLoader)
349                 model = _modelLoader->loadTileModel(custom_path.str(),
350                                                     obj->type == OBJECT_SHARED);
351             if (model)
352                 obj_trans->addChild(model);
353         } else if (obj->type == OBJECT_SIGN || obj->type == OBJECT_RUNWAY_SIGN) {
354             // load the object itself
355             SGPath custom_path = obj->path;
356             custom_path.append( obj->name );
357
358             osg::Matrix obj_pos;
359             WorldCoordinate( obj_pos, obj->lat, obj->lon, obj->elev, obj->hdg );
360
361             osg::MatrixTransform *obj_trans = new osg::MatrixTransform;
362             obj_trans->setDataVariance(osg::Object::STATIC);
363             obj_trans->setMatrix( obj_pos );
364
365             osg::Node *custom_obj = 0;
366             if (obj->type == OBJECT_SIGN)
367                 custom_obj = SGMakeSign(opt->getMaterialLib(), custom_path.str(), obj->name);
368             else
369                 custom_obj = SGMakeRunwaySign(opt->getMaterialLib(), custom_path.str(), obj->name);
370
371             // wire the pieces together
372             if ( custom_obj != NULL ) {
373                 obj_trans -> addChild( custom_obj );
374             }
375             new_tile->addChild( obj_trans );
376
377         }
378         delete obj;
379     }
380     return new_tile;
381 }
382
383 void
384 TileEntry::addToSceneGraph(osg::Group *terrain_branch)
385 {
386     terrain_branch->addChild( _node.get() );
387
388     SG_LOG( SG_TERRAIN, SG_DEBUG,
389             "connected a tile into scene graph.  _node = "
390             << _node.get() );
391     SG_LOG( SG_TERRAIN, SG_DEBUG, "num parents now = "
392             << _node->getNumParents() );
393 }
394
395
396 void
397 TileEntry::removeFromSceneGraph()
398 {
399     SG_LOG( SG_TERRAIN, SG_DEBUG, "disconnecting TileEntry nodes" );
400
401     if (! is_loaded()) {
402         SG_LOG( SG_TERRAIN, SG_DEBUG, "removing a not-fully loaded tile!" );
403     } else {
404         SG_LOG( SG_TERRAIN, SG_DEBUG, "removing a fully loaded tile!  _node = " << _node.get() );
405     }
406
407     // find the nodes branch parent
408     if ( _node->getNumParents() > 0 ) {
409         // find the first parent (should only be one)
410         osg::Group *parent = _node->getParent( 0 ) ;
411         if( parent ) {
412             parent->removeChild( _node.get() );
413         }
414     }
415 }
416
417 void
418 TileEntry::refresh()
419 {
420     osg::Group *parent = NULL;
421     // find the nodes branch parent
422     if ( _node->getNumParents() > 0 ) {
423         // find the first parent (should only be one)
424         parent = _node->getParent( 0 ) ;
425         if( parent ) {
426             parent->removeChild( _node.get() );
427         }
428     }
429     _node = new osg::LOD;
430     if (parent)
431         parent->addChild(_node.get());
432 }