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