1 // tileentry.cxx -- routines to handle a scenery tile
3 // Written by Curtis Olson, started May 1998.
5 // Copyright (C) 1998 - 2001 Curtis L. Olson - http://www.flightgear.org/~curt
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.
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.
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.
22 # include <simgear_config.h>
25 #include <simgear/compiler.h>
32 #include <osg/MatrixTransform>
34 #include <osg/NodeCallback>
36 #include <osgDB/FileNameUtils>
37 #include <osgDB/ReaderWriter>
38 #include <osgDB/ReadFile>
39 #include <osgDB/Registry>
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>
54 #include "ReaderWriterSPT.hxx"
55 #include "ReaderWriterSTG.hxx"
56 #include "SGOceanTile.hxx"
57 #include "TileEntry.hxx"
60 using namespace simgear;
62 ModelLoadHelper *TileEntry::_modelLoader=0;
65 osgDB::RegisterReaderWriterProxy<ReaderWriterSTG> g_readerWriterSTGProxy;
66 ModelRegistryCallbackProxy<LoadOnlyCallback> g_stgCallbackProxy("stg");
68 osgDB::RegisterReaderWriterProxy<ReaderWriterSPT> g_readerWriterSPTProxy;
69 ModelRegistryCallbackProxy<LoadOnlyCallback> g_sptCallbackProxy("spt");
73 static SGBucket getBucketFromFileName(const std::string& fileName)
75 std::istringstream ss(osgDB::getNameLessExtension(fileName));
80 return SGBucket(index);
84 TileEntry::TileEntry ( const SGBucket& b )
86 tileFileName(b.gen_index_str()),
87 _node( new osg::LOD ),
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);
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)
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);
116 TileEntry::~TileEntry ()
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) {
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 );
132 TileEntry::loadTileByFileName(const string& fileName,
133 const osgDB::Options* options)
135 SG_LOG(SG_TERRAIN, SG_INFO, "Loading tile " << fileName);
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];
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) {
156 terrain.append("Terrain");
157 terrain.append(basePath);
158 terrain.append(simpleFileName);
161 objects.append("Objects");
162 objects.append(basePath);
163 objects.append(simpleFileName);
165 if (terrain.isFile() || objects.isFile()) {
166 absoluteFileName[0] = terrain.str();
167 absoluteFileName[1] = objects.str();
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
179 std::string fg_root = options->getPluginStringData("SimGear::FG_ROOT");
180 osg::ref_ptr<SGReaderWriterOptions> opt;
181 opt = SGReaderWriterOptions::copyOrCreate(options);
183 bool found_tile_base = false;
184 osg::ref_ptr<osg::Group> group = new osg::Group;
185 for (unsigned i = 0; i < 2; ++i) {
187 if (absoluteFileName[i].empty())
190 sg_gzifstream in( absoluteFileName[i] );
194 SG_LOG(SG_TERRAIN, SG_INFO, "Loading stg file " << absoluteFileName[i]);
196 std::string filePath = osgDB::getFilePath(absoluteFileName[i]);
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);
204 osg::ref_ptr<SGReaderWriterOptions> sharedOptions;
205 sharedOptions = SGReaderWriterOptions::copyOrCreate(options);
206 sharedOptions->getDatabasePathList().clear();
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);
213 bool has_base = false;
214 while ( ! in.eof() ) {
219 if ( token.empty() || token[0] == '#' ) {
224 // Then there is always a name
228 SGPath path = filePath;
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 );
236 if (!found_tile_base) {
237 found_tile_base = true;
240 node = osgDB::readRefNodeFile(path.str(),
241 staticOptions.get());
244 SG_LOG( SG_TERRAIN, SG_ALERT, absoluteFileName[i]
245 << ": Failed to load OBJECT_BASE '"
249 SG_LOG(SG_TERRAIN, SG_BULK, " (skipped)");
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());
258 SG_LOG( SG_TERRAIN, SG_ALERT, absoluteFileName[i]
259 << ": Failed to load OBJECT '"
263 SG_LOG(SG_TERRAIN, SG_BULK, " " << token << " "
264 << name << " (skipped)");
268 double lon, lat, elev, hdg;
269 in >> lon >> lat >> elev >> hdg;
272 if ( token == "OBJECT_STATIC" ) {
273 /// Hmm, the findDataFile should happen downstream
274 std::string absName = osgDB::findDataFile(name,
275 staticOptions.get());
277 node = _modelLoader->loadTileModel(absName, false);
279 node = osgDB::readRefNodeFile(absName,
280 staticOptions.get());
284 SG_LOG( SG_TERRAIN, SG_ALERT, absoluteFileName[i]
285 << ": Failed to load OBJECT_STATIC '"
289 } else if ( token == "OBJECT_SHARED" ) {
291 node = _modelLoader->loadTileModel(name, true);
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());
301 SG_LOG( SG_TERRAIN, SG_ALERT, absoluteFileName[i]
302 << ": Failed to load OBJECT_SHARED '"
306 } else if ( token == "OBJECT_SIGN" ) {
307 node = SGMakeSign(opt->getMaterialLib(), name);
309 } else if ( token == "OBJECT_RUNWAY_SIGN" ) {
310 node = SGMakeRunwaySign(opt->getMaterialLib(), name);
313 SG_LOG( SG_TERRAIN, SG_ALERT, absoluteFileName[i]
314 << ": Unknown token '" << token << "'" );
317 if (node.valid() && token != "OBJECT") {
319 matrix = makeZUpFrame(SGGeod::fromDegM(lon, lat, elev));
320 matrix.preMultRotate(osg::Quat(SGMiscd::deg2rad(hdg),
321 osg::Vec3(0, 0, 1)));
323 osg::MatrixTransform* matrixTransform;
324 matrixTransform = new osg::MatrixTransform(matrix);
325 matrixTransform->setDataVariance(osg::Object::STATIC);
326 matrixTransform->addChild(node.get());
327 node = matrixTransform;
332 group->addChild(node.get());
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");
342 osg::Node* node = SGOceanTile(bucket, opt->getMaterialLib());
344 group->addChild(node);
346 SG_LOG( SG_TERRAIN, SG_ALERT,
347 "Warning: failed to generate ocean tile!" );
351 return group.release();
355 TileEntry::addToSceneGraph(osg::Group *terrain_branch)
357 terrain_branch->addChild( _node.get() );
359 SG_LOG( SG_TERRAIN, SG_DEBUG,
360 "connected a tile into scene graph. _node = "
362 SG_LOG( SG_TERRAIN, SG_DEBUG, "num parents now = "
363 << _node->getNumParents() );
368 TileEntry::removeFromSceneGraph()
370 SG_LOG( SG_TERRAIN, SG_DEBUG, "disconnecting TileEntry nodes" );
373 SG_LOG( SG_TERRAIN, SG_DEBUG, "removing a not-fully loaded tile!" );
375 SG_LOG( SG_TERRAIN, SG_DEBUG, "removing a fully loaded tile! _node = " << _node.get() );
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 ) ;
383 parent->removeChild( _node.get() );
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 ) ;
397 parent->removeChild( _node.get() );
400 _node = new osg::LOD;
402 parent->addChild(_node.get());