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/ReaderWriter>
39 #include <osgDB/ReadFile>
40 #include <osgDB/Registry>
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>
55 #include "ReaderWriterSPT.hxx"
56 #include "ReaderWriterSTG.hxx"
57 #include "SGOceanTile.hxx"
58 #include "TileEntry.hxx"
61 using namespace simgear;
63 static ModelLoadHelper *_modelLoader=0;
66 osgDB::RegisterReaderWriterProxy<ReaderWriterSTG> g_readerWriterSTGProxy;
67 ModelRegistryCallbackProxy<LoadOnlyCallback> g_stgCallbackProxy("stg");
69 osgDB::RegisterReaderWriterProxy<ReaderWriterSPT> g_readerWriterSPTProxy;
70 ModelRegistryCallbackProxy<LoadOnlyCallback> g_sptCallbackProxy("spt");
74 static SGBucket getBucketFromFileName(const std::string& fileName)
76 std::istringstream ss(osgDB::getNameLessExtension(fileName));
81 return SGBucket(index);
85 loadStgFile(const std::string& absoluteFileName, osg::Group& group, const osgDB::Options* options)
87 if (absoluteFileName.empty())
90 sg_gzifstream in( absoluteFileName );
94 SG_LOG(SG_TERRAIN, SG_INFO, "Loading stg file " << absoluteFileName);
96 std::string filePath = osgDB::getFilePath(absoluteFileName);
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);
104 osg::ref_ptr<SGReaderWriterOptions> sharedOptions;
105 sharedOptions = SGReaderWriterOptions::copyOrCreate(options);
106 sharedOptions->getDatabasePathList().clear();
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);
114 bool has_base = false;
115 while ( ! in.eof() ) {
120 if ( token.empty() || token[0] == '#' ) {
125 // Then there is always a name
129 SGPath path = filePath;
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 );
138 node = osgDB::readRefNodeFile(path.str(),
139 staticOptions.get());
142 SG_LOG( SG_TERRAIN, SG_ALERT, absoluteFileName
143 << ": Failed to load OBJECT_BASE '"
147 } else if ( token == "OBJECT" ) {
148 node = osgDB::readRefNodeFile(path.str(),
149 staticOptions.get());
152 SG_LOG( SG_TERRAIN, SG_ALERT, absoluteFileName
153 << ": Failed to load OBJECT '"
158 double lon, lat, elev, hdg;
159 in >> lon >> lat >> elev >> hdg;
162 if ( token == "OBJECT_STATIC" ) {
163 /// Hmm, the findDataFile should happen downstream
164 std::string absName = osgDB::findDataFile(name,
165 staticOptions.get());
167 node = _modelLoader->loadTileModel(absName, false);
169 osg::ref_ptr<SGReaderWriterOptions> opt;
170 opt = new SGReaderWriterOptions(*staticOptions);
171 if (SGPath(absName).lower_extension() == "ac")
172 opt->setInstantiateEffects(true);
174 opt->setInstantiateEffects(false);
175 node = osgDB::readRefNodeFile(absName, opt.get());
179 SG_LOG( SG_TERRAIN, SG_ALERT, absoluteFileName
180 << ": Failed to load OBJECT_STATIC '"
184 } else if ( token == "OBJECT_SHARED" ) {
186 node = _modelLoader->loadTileModel(name, true);
188 osg::ref_ptr<SGReaderWriterOptions> opt;
189 opt = new SGReaderWriterOptions(*sharedOptions);
191 /// Hmm, the findDataFile should happen in the downstream readers
192 std::string absName = osgDB::findDataFile(name, opt.get());
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);
200 opt->setInstantiateEffects(false);
201 proxyNode->setDatabaseOptions(opt.get());
206 SG_LOG( SG_TERRAIN, SG_ALERT, absoluteFileName
207 << ": Failed to load OBJECT_SHARED '"
211 } else if ( token == "OBJECT_SIGN" ) {
212 node = SGMakeSign(staticOptions->getMaterialLib(), name);
214 } else if ( token == "OBJECT_RUNWAY_SIGN" ) {
215 node = SGMakeRunwaySign(staticOptions->getMaterialLib(), name);
218 SG_LOG( SG_TERRAIN, SG_ALERT, absoluteFileName
219 << ": Unknown token '" << token << "'" );
222 if (node.valid() && token != "OBJECT") {
224 matrix = makeZUpFrame(SGGeod::fromDegM(lon, lat, elev));
225 matrix.preMultRotate(osg::Quat(SGMiscd::deg2rad(hdg),
226 osg::Vec3(0, 0, 1)));
228 osg::MatrixTransform* matrixTransform;
229 matrixTransform = new osg::MatrixTransform(matrix);
230 matrixTransform->setDataVariance(osg::Object::STATIC);
231 matrixTransform->addChild(node.get());
232 node = matrixTransform;
237 group.addChild(node.get());
246 TileEntry::setModelLoadHelper(ModelLoadHelper *m)
252 TileEntry::TileEntry ( const SGBucket& b )
254 tileFileName(b.gen_index_str()),
255 _node( new osg::LOD ),
257 _current_view(false),
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);
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)
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);
284 TileEntry::~TileEntry ()
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) {
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 );
300 TileEntry::loadTileByFileName(const string& fileName,
301 const osgDB::Options* options)
303 SG_LOG(SG_TERRAIN, SG_INFO, "Loading tile " << fileName);
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();
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) {
328 objects.append("Objects");
329 objects.append(basePath);
330 objects.append(simpleFileName);
331 if (loadStgFile(objects.str(), *group, options))
335 terrain.append("Terrain");
336 terrain.append(basePath);
337 terrain.append(simpleFileName);
338 if (loadStgFile(terrain.str(), *group, options))
342 // ... or generate an ocean tile on the fly
344 SG_LOG(SG_TERRAIN, SG_INFO, " Generating ocean tile");
346 osg::ref_ptr<SGReaderWriterOptions> opt;
347 opt = SGReaderWriterOptions::copyOrCreate(options);
348 osg::Node* node = SGOceanTile(bucket, opt->getMaterialLib());
350 group->addChild(node);
352 SG_LOG( SG_TERRAIN, SG_ALERT,
353 "Warning: failed to generate ocean tile!" );
356 return group.release();
360 TileEntry::addToSceneGraph(osg::Group *terrain_branch)
362 terrain_branch->addChild( _node.get() );
364 SG_LOG( SG_TERRAIN, SG_DEBUG,
365 "connected a tile into scene graph. _node = "
367 SG_LOG( SG_TERRAIN, SG_DEBUG, "num parents now = "
368 << _node->getNumParents() );
373 TileEntry::removeFromSceneGraph()
375 SG_LOG( SG_TERRAIN, SG_DEBUG, "disconnecting TileEntry nodes" );
378 SG_LOG( SG_TERRAIN, SG_DEBUG, "removing a not-fully loaded tile!" );
380 SG_LOG( SG_TERRAIN, SG_DEBUG, "removing a fully loaded tile! _node = " << _node.get() );
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 ) ;
388 parent->removeChild( _node.get() );
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 ) ;
402 parent->removeChild( _node.get() );
405 _node = new osg::LOD;
407 parent->addChild(_node.get());