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>
35 #include <osg/ProxyNode>
37 #include <osgDB/FileNameUtils>
38 #include <osgDB/FileUtils>
39 #include <osgDB/ReaderWriter>
40 #include <osgDB/ReadFile>
41 #include <osgDB/Registry>
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>
55 #include "SGOceanTile.hxx"
56 #include "TileEntry.hxx"
59 using namespace simgear;
61 static ModelLoadHelper *_modelLoader=0;
63 static SGBucket getBucketFromFileName(const std::string& fileName)
65 std::istringstream ss(osgDB::getNameLessExtension(fileName));
70 return SGBucket(index);
74 loadStgFile(const std::string& absoluteFileName, osg::Group& group, const osgDB::Options* options)
76 if (absoluteFileName.empty())
79 sg_gzifstream in( absoluteFileName );
83 SG_LOG(SG_TERRAIN, SG_INFO, "Loading stg file " << absoluteFileName);
85 std::string filePath = osgDB::getFilePath(absoluteFileName);
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);
93 osg::ref_ptr<SGReaderWriterOptions> sharedOptions;
94 sharedOptions = SGReaderWriterOptions::copyOrCreate(options);
95 sharedOptions->getDatabasePathList().clear();
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);
103 bool has_base = false;
104 while ( ! in.eof() ) {
109 if ( token.empty() || token[0] == '#' ) {
114 // Then there is always a name
118 SGPath path = filePath;
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 );
127 node = osgDB::readRefNodeFile(path.str(),
128 staticOptions.get());
131 SG_LOG( SG_TERRAIN, SG_ALERT, absoluteFileName
132 << ": Failed to load OBJECT_BASE '"
136 } else if ( token == "OBJECT" ) {
137 node = osgDB::readRefNodeFile(path.str(),
138 staticOptions.get());
141 SG_LOG( SG_TERRAIN, SG_ALERT, absoluteFileName
142 << ": Failed to load OBJECT '"
147 double lon, lat, elev, hdg;
148 in >> lon >> lat >> elev >> hdg;
151 if ( token == "OBJECT_STATIC" ) {
152 /// Hmm, the findDataFile should happen downstream
153 std::string absName = osgDB::findDataFile(name,
154 staticOptions.get());
156 node = _modelLoader->loadTileModel(absName, false);
158 osg::ref_ptr<SGReaderWriterOptions> opt;
159 opt = new SGReaderWriterOptions(*staticOptions);
160 if (SGPath(absName).lower_extension() == "ac")
161 opt->setInstantiateEffects(true);
163 opt->setInstantiateEffects(false);
164 node = osgDB::readRefNodeFile(absName, opt.get());
168 SG_LOG( SG_TERRAIN, SG_ALERT, absoluteFileName
169 << ": Failed to load OBJECT_STATIC '"
173 } else if ( token == "OBJECT_SHARED" ) {
175 node = _modelLoader->loadTileModel(name, true);
177 osg::ref_ptr<SGReaderWriterOptions> opt;
178 opt = new SGReaderWriterOptions(*sharedOptions);
180 /// Hmm, the findDataFile should happen in the downstream readers
181 std::string absName = osgDB::findDataFile(name, opt.get());
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);
189 opt->setInstantiateEffects(false);
190 proxyNode->setDatabaseOptions(opt.get());
195 SG_LOG( SG_TERRAIN, SG_ALERT, absoluteFileName
196 << ": Failed to load OBJECT_SHARED '"
200 } else if ( token == "OBJECT_SIGN" ) {
201 node = SGMakeSign(staticOptions->getMaterialLib(), name);
203 } else if ( token == "OBJECT_RUNWAY_SIGN" ) {
204 node = SGMakeRunwaySign(staticOptions->getMaterialLib(), name);
207 SG_LOG( SG_TERRAIN, SG_ALERT, absoluteFileName
208 << ": Unknown token '" << token << "'" );
211 if (node.valid() && token != "OBJECT") {
213 matrix = makeZUpFrame(SGGeod::fromDegM(lon, lat, elev));
214 matrix.preMultRotate(osg::Quat(SGMiscd::deg2rad(hdg),
215 osg::Vec3(0, 0, 1)));
217 osg::MatrixTransform* matrixTransform;
218 matrixTransform = new osg::MatrixTransform(matrix);
219 matrixTransform->setDataVariance(osg::Object::STATIC);
220 matrixTransform->addChild(node.get());
221 node = matrixTransform;
226 group.addChild(node.get());
235 TileEntry::setModelLoadHelper(ModelLoadHelper *m)
241 TileEntry::TileEntry ( const SGBucket& b )
243 tileFileName(b.gen_index_str()),
244 _node( new osg::LOD ),
246 _current_view(false),
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);
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)
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);
273 TileEntry::~TileEntry ()
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) {
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 );
289 TileEntry::loadTileByFileName(const string& fileName,
290 const osgDB::Options* options)
292 SG_LOG(SG_TERRAIN, SG_INFO, "Loading tile " << fileName);
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();
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) {
317 objects.append("Objects");
318 objects.append(basePath);
319 objects.append(simpleFileName);
320 if (loadStgFile(objects.str(), *group, options))
324 terrain.append("Terrain");
325 terrain.append(basePath);
326 terrain.append(simpleFileName);
327 if (loadStgFile(terrain.str(), *group, options))
331 // ... or generate an ocean tile on the fly
333 SG_LOG(SG_TERRAIN, SG_INFO, " Generating ocean tile");
335 osg::ref_ptr<SGReaderWriterOptions> opt;
336 opt = SGReaderWriterOptions::copyOrCreate(options);
337 osg::Node* node = SGOceanTile(bucket, opt->getMaterialLib());
339 group->addChild(node);
341 SG_LOG( SG_TERRAIN, SG_ALERT,
342 "Warning: failed to generate ocean tile!" );
345 return group.release();
349 TileEntry::addToSceneGraph(osg::Group *terrain_branch)
351 terrain_branch->addChild( _node.get() );
353 SG_LOG( SG_TERRAIN, SG_DEBUG,
354 "connected a tile into scene graph. _node = "
356 SG_LOG( SG_TERRAIN, SG_DEBUG, "num parents now = "
357 << _node->getNumParents() );
362 TileEntry::removeFromSceneGraph()
364 SG_LOG( SG_TERRAIN, SG_DEBUG, "disconnecting TileEntry nodes" );
367 SG_LOG( SG_TERRAIN, SG_DEBUG, "removing a not-fully loaded tile!" );
369 SG_LOG( SG_TERRAIN, SG_DEBUG, "removing a fully loaded tile! _node = " << _node.get() );
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 ) ;
377 parent->removeChild( _node.get() );
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 ) ;
391 parent->removeChild( _node.get() );
394 _node = new osg::LOD;
396 parent->addChild(_node.get());