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