00001
00002
00003
00004
00005
00006
00007
00008
00009
00010
00011
00012
00013
00014
00015
00016
00017
00018
00019
00020
00021 #ifdef HAVE_CONFIG_H
00022 # include <simgear_config.h>
00023 #endif
00024
00025 #include <simgear/compiler.h>
00026
00027 #include <string>
00028 #include <sstream>
00029 #include <istream>
00030
00031 #include <osg/LOD>
00032 #include <osg/MatrixTransform>
00033 #include <osg/Math>
00034 #include <osg/NodeCallback>
00035
00036 #include <osgDB/FileNameUtils>
00037 #include <osgDB/ReaderWriter>
00038 #include <osgDB/ReadFile>
00039 #include <osgDB/Registry>
00040
00041 #include <simgear/bucket/newbucket.hxx>
00042 #include <simgear/debug/logstream.hxx>
00043 #include <simgear/math/sg_geodesy.hxx>
00044 #include <simgear/math/sg_random.h>
00045 #include <simgear/math/SGMath.hxx>
00046 #include <simgear/misc/sgstream.hxx>
00047 #include <simgear/scene/material/mat.hxx>
00048 #include <simgear/scene/material/matlib.hxx>
00049 #include <simgear/scene/model/ModelRegistry.hxx>
00050 #include <simgear/scene/tgdb/apt_signs.hxx>
00051 #include <simgear/scene/tgdb/obj.hxx>
00052 #include <simgear/scene/tgdb/SGReaderWriterBTGOptions.hxx>
00053
00054 #include "ReaderWriterSTG.hxx"
00055 #include "TileEntry.hxx"
00056
00057 using std::string;
00058 using namespace simgear;
00059
00060 ModelLoadHelper *TileEntry::_modelLoader=0;
00061
00062 namespace {
00063 osgDB::RegisterReaderWriterProxy<ReaderWriterSTG> g_readerWriterSTGProxy;
00064 ModelRegistryCallbackProxy<LoadOnlyCallback> g_stgCallbackProxy("stg");
00065 }
00066
00067 namespace
00068 {
00069
00070
00071 class TileCullCallback : public osg::NodeCallback
00072 {
00073 public:
00074 TileCullCallback() : _timeStamp(0) {}
00075 TileCullCallback(const TileCullCallback& tc, const osg::CopyOp& copyOp) :
00076 NodeCallback(tc, copyOp), _timeStamp(tc._timeStamp)
00077 {
00078 }
00079
00080 virtual void operator()(osg::Node* node, osg::NodeVisitor* nv);
00081 double getTimeStamp() const { return _timeStamp; }
00082 void setTimeStamp(double timeStamp) { _timeStamp = timeStamp; }
00083 protected:
00084 double _timeStamp;
00085 };
00086 }
00087
00088 void TileCullCallback::operator()(osg::Node* node, osg::NodeVisitor* nv)
00089 {
00090 if (nv->getFrameStamp())
00091 _timeStamp = nv->getFrameStamp()->getReferenceTime();
00092 traverse(node, nv);
00093 }
00094
00095 double TileEntry::get_timestamp() const
00096 {
00097 if (_node.valid()) {
00098 return (dynamic_cast<TileCullCallback*>(_node->getCullCallback()))
00099 ->getTimeStamp();
00100 } else
00101 return DBL_MAX;
00102 }
00103
00104 void TileEntry::set_timestamp(double time_ms)
00105 {
00106 if (_node.valid()) {
00107 TileCullCallback* cb
00108 = dynamic_cast<TileCullCallback*>(_node->getCullCallback());
00109 if (cb)
00110 cb->setTimeStamp(time_ms);
00111 }
00112 }
00113
00114
00115 TileEntry::TileEntry ( const SGBucket& b )
00116 : tile_bucket( b ),
00117 tileFileName(b.gen_index_str()),
00118 _node( new osg::LOD ),
00119 is_inner_ring(false)
00120 {
00121 _node->setCullCallback(new TileCullCallback);
00122 tileFileName += ".stg";
00123 _node->setName(tileFileName);
00124
00125
00126
00127 _node->setRange(0, 0.0, 10000.0);
00128 }
00129
00130
00131
00132 TileEntry::~TileEntry ()
00133 {
00134 }
00135
00136 static void WorldCoordinate(osg::Matrix& obj_pos, double lat,
00137 double lon, double elev, double hdg)
00138 {
00139 SGGeod geod = SGGeod::fromDegM(lon, lat, elev);
00140 obj_pos = geod.makeZUpFrame();
00141
00142
00143 obj_pos.preMult(osg::Matrix::rotate(hdg * SGD_DEGREES_TO_RADIANS,
00144 0.0, 0.0, 1.0));
00145 }
00146
00147
00148
00149
00150 void TileEntry::prep_ssg_node(float vis) {
00151 if (!is_loaded())
00152 return;
00153
00154
00155 float bounding_radius = _node->getChild(0)->getBound().radius();
00156 _node->setRange( 0, 0, vis + bounding_radius );
00157 }
00158
00159 bool TileEntry::obj_load(const string& path, osg::Group *geometry, bool is_base,
00160 const osgDB::ReaderWriter::Options* options)
00161 {
00162 osg::Node* node = osgDB::readNodeFile(path, options);
00163 if (node)
00164 geometry->addChild(node);
00165
00166 return node != 0;
00167 }
00168
00169
00170 typedef enum {
00171 OBJECT,
00172 OBJECT_SHARED,
00173 OBJECT_STATIC,
00174 OBJECT_SIGN,
00175 OBJECT_RUNWAY_SIGN
00176 } object_type;
00177
00178
00179
00180 struct Object {
00181 Object(object_type t, const string& token, const SGPath& p,
00182 std::istream& in)
00183 : type(t), path(p)
00184 {
00185 in >> name;
00186 if (type != OBJECT)
00187 in >> lon >> lat >> elev >> hdg;
00188 in >> ::skipeol;
00189
00190 if (type == OBJECT)
00191 SG_LOG(SG_TERRAIN, SG_BULK, " " << token << " " << name);
00192 else
00193 SG_LOG(SG_TERRAIN, SG_BULK, " " << token << " " << name << " lon=" <<
00194 lon << " lat=" << lat << " elev=" << elev << " hdg=" << hdg);
00195 }
00196 object_type type;
00197 string name;
00198 SGPath path;
00199 double lon, lat, elev, hdg;
00200 };
00201
00202
00203
00204
00205 osg::Node*
00206 TileEntry::loadTileByFileName(const string& fileName,
00207 const osgDB::ReaderWriter::Options* options)
00208 {
00209 std::string index_str = osgDB::getNameLessExtension(fileName);
00210 index_str = osgDB::getSimpleFileName(index_str);
00211
00212 long tileIndex;
00213 {
00214 std::istringstream idxStream(index_str);
00215 idxStream >> tileIndex;
00216 }
00217 SGBucket tile_bucket(tileIndex);
00218 const string basePath = tile_bucket.gen_base_path();
00219
00220 bool found_tile_base = false;
00221
00222 SGPath object_base;
00223 vector<const Object*> objects;
00224
00225 SG_LOG( SG_TERRAIN, SG_INFO, "Loading tile " << index_str );
00226
00227 osgDB::FilePathList path_list=options->getDatabasePathList();
00228
00229 std::string filePath = osgDB::getFilePath(fileName);
00230 if (!filePath.empty())
00231 path_list.push_front(filePath);
00232
00233
00234 for (unsigned int i = 0; i < path_list.size(); i++) {
00235
00236
00237
00238
00239
00240 if (path_list[i].empty()) {
00241 if (found_tile_base)
00242 break;
00243 else
00244 continue;
00245 }
00246
00247 bool has_base = false;
00248
00249 SGPath tile_path = path_list[i];
00250 tile_path.append(basePath);
00251
00252 SGPath basename = tile_path;
00253 basename.append( index_str );
00254
00255 SG_LOG( SG_TERRAIN, SG_DEBUG, " Trying " << basename.str() );
00256
00257
00258
00259 SGPath stg_name = basename;
00260 stg_name.concat( ".stg" );
00261
00262 sg_gzifstream in( stg_name.str() );
00263 if ( !in.is_open() )
00264 continue;
00265
00266 while ( ! in.eof() ) {
00267 string token;
00268 in >> token;
00269
00270 if ( token.empty() || token[0] == '#' ) {
00271 in >> ::skipeol;
00272 continue;
00273 }
00274
00275 if ( token == "OBJECT_BASE" ) {
00276 string name;
00277 in >> name >> ::skipws;
00278 SG_LOG( SG_TERRAIN, SG_BULK, " " << token << " " << name );
00279
00280 if (!found_tile_base) {
00281 found_tile_base = true;
00282 has_base = true;
00283
00284 object_base = tile_path;
00285 object_base.append(name);
00286
00287 } else
00288 SG_LOG(SG_TERRAIN, SG_BULK, " (skipped)");
00289
00290
00291 } else if ( token == "OBJECT" ) {
00292 if (!found_tile_base || has_base)
00293 objects.push_back(new Object(OBJECT, token, tile_path, in));
00294 else {
00295 string name;
00296 in >> name >> ::skipeol;
00297 SG_LOG(SG_TERRAIN, SG_BULK, " " << token << " "
00298 << name << " (skipped)");
00299 }
00300
00301
00302 } else if ( token == "OBJECT_STATIC" ) {
00303 objects.push_back(new Object(OBJECT_STATIC, token, tile_path, in));
00304
00305 } else if ( token == "OBJECT_SHARED" ) {
00306 objects.push_back(new Object(OBJECT_SHARED, token, tile_path, in));
00307
00308 } else if ( token == "OBJECT_SIGN" ) {
00309 objects.push_back(new Object(OBJECT_SIGN, token, tile_path, in));
00310
00311 } else if ( token == "OBJECT_RUNWAY_SIGN" ) {
00312 objects.push_back(new Object(OBJECT_RUNWAY_SIGN, token, tile_path, in));
00313
00314 } else {
00315 SG_LOG( SG_TERRAIN, SG_DEBUG,
00316 "Unknown token '" << token << "' in " << stg_name.str() );
00317 in >> ::skipws;
00318 }
00319 }
00320 }
00321
00322 const SGReaderWriterBTGOptions* btgOpt;
00323 btgOpt = dynamic_cast<const SGReaderWriterBTGOptions *>(options);
00324 osg::ref_ptr<SGReaderWriterBTGOptions> opt;
00325 if (btgOpt)
00326 opt = new SGReaderWriterBTGOptions(*btgOpt);
00327 else
00328 opt = new SGReaderWriterBTGOptions;
00329
00330
00331 osg::Group* new_tile = new osg::Group;
00332
00333 if (found_tile_base) {
00334
00335 opt->setCalcLights(true);
00336 obj_load( object_base.str(), new_tile, true, opt);
00337
00338 } else {
00339
00340 SG_LOG(SG_TERRAIN, SG_INFO, " Generating ocean tile");
00341 if ( !SGGenTile( path_list[0], tile_bucket,
00342 opt->getMatlib(), new_tile ) ) {
00343 SG_LOG( SG_TERRAIN, SG_ALERT,
00344 "Warning: failed to generate ocean tile!" );
00345 }
00346 }
00347
00348
00349
00350 for (unsigned int j = 0; j < objects.size(); j++) {
00351 const Object *obj = objects[j];
00352
00353 if (obj->type == OBJECT) {
00354 SGPath custom_path = obj->path;
00355 custom_path.append( obj->name );
00356 opt->setCalcLights(true);
00357 obj_load( custom_path.str(), new_tile, false, opt);
00358
00359 } else if (obj->type == OBJECT_SHARED || obj->type == OBJECT_STATIC) {
00360
00361
00362 SGPath custom_path;
00363 if ( obj->type == OBJECT_STATIC ) {
00364 custom_path = obj->path;
00365 } else {
00366
00367 }
00368 custom_path.append( obj->name );
00369
00370 osg::Matrix obj_pos;
00371 WorldCoordinate( obj_pos, obj->lat, obj->lon, obj->elev, obj->hdg );
00372
00373 osg::MatrixTransform *obj_trans = new osg::MatrixTransform;
00374 obj_trans->setDataVariance(osg::Object::STATIC);
00375 obj_trans->setMatrix( obj_pos );
00376
00377
00378 new_tile->addChild( obj_trans );
00379
00380 osg::Node* model = 0;
00381 if(_modelLoader)
00382 model = _modelLoader->loadTileModel(custom_path.str(),
00383 obj->type == OBJECT_SHARED);
00384 if (model)
00385 obj_trans->addChild(model);
00386 } else if (obj->type == OBJECT_SIGN || obj->type == OBJECT_RUNWAY_SIGN) {
00387
00388 SGPath custom_path = obj->path;
00389 custom_path.append( obj->name );
00390
00391 osg::Matrix obj_pos;
00392 WorldCoordinate( obj_pos, obj->lat, obj->lon, obj->elev, obj->hdg );
00393
00394 osg::MatrixTransform *obj_trans = new osg::MatrixTransform;
00395 obj_trans->setDataVariance(osg::Object::STATIC);
00396 obj_trans->setMatrix( obj_pos );
00397
00398 osg::Node *custom_obj = 0;
00399 if (obj->type == OBJECT_SIGN)
00400 custom_obj = SGMakeSign(opt->getMatlib(), custom_path.str(), obj->name);
00401 else
00402 custom_obj = SGMakeRunwaySign(opt->getMatlib(), custom_path.str(), obj->name);
00403
00404
00405 if ( custom_obj != NULL ) {
00406 obj_trans -> addChild( custom_obj );
00407 }
00408 new_tile->addChild( obj_trans );
00409
00410 }
00411 delete obj;
00412 }
00413 return new_tile;
00414 }
00415
00416 void
00417 TileEntry::addToSceneGraph(osg::Group *terrain_branch)
00418 {
00419 terrain_branch->addChild( _node.get() );
00420
00421 SG_LOG( SG_TERRAIN, SG_DEBUG,
00422 "connected a tile into scene graph. _node = "
00423 << _node.get() );
00424 SG_LOG( SG_TERRAIN, SG_DEBUG, "num parents now = "
00425 << _node->getNumParents() );
00426 }
00427
00428
00429 void
00430 TileEntry::removeFromSceneGraph()
00431 {
00432 SG_LOG( SG_TERRAIN, SG_DEBUG, "disconnecting TileEntry nodes" );
00433
00434 if (! is_loaded()) {
00435 SG_LOG( SG_TERRAIN, SG_DEBUG, "removing a not-fully loaded tile!" );
00436 } else {
00437 SG_LOG( SG_TERRAIN, SG_DEBUG, "removing a fully loaded tile! _node = " << _node.get() );
00438 }
00439
00440
00441 if ( _node->getNumParents() > 0 ) {
00442
00443 osg::Group *parent = _node->getParent( 0 ) ;
00444 if( parent ) {
00445 parent->removeChild( _node.get() );
00446 }
00447 }
00448 }
00449