From 367f1813dea1e24d6f7e7eb3ecbcfa87cb98a5d1 Mon Sep 17 00:00:00 2001 From: timoore Date: Wed, 14 May 2008 22:07:50 +0000 Subject: [PATCH] sg: move most scenery-related code to simgear From Till Busch --- projects/VC7.1/SimGear.vcproj | 15 + simgear/scene/model/ModelRegistry.cxx | 6 +- simgear/scene/model/SGReaderWriterXML.cxx | 1 + simgear/scene/tgdb/Makefile.am | 8 +- simgear/scene/tgdb/ReaderWriterSTG.cxx | 77 +++ simgear/scene/tgdb/ReaderWriterSTG.hxx | 41 ++ .../scene/tgdb/SGReaderWriterBTGOptions.hxx | 4 +- simgear/scene/tgdb/TileCache.cxx | 171 ++++++ simgear/scene/tgdb/TileCache.hxx | 129 +++++ simgear/scene/tgdb/TileEntry.cxx | 503 ++++++++++++++++++ simgear/scene/tgdb/TileEntry.hxx | 182 +++++++ simgear/scene/tgdb/userdata.cxx | 2 +- 12 files changed, 1132 insertions(+), 7 deletions(-) create mode 100644 simgear/scene/tgdb/ReaderWriterSTG.cxx create mode 100644 simgear/scene/tgdb/ReaderWriterSTG.hxx create mode 100644 simgear/scene/tgdb/TileCache.cxx create mode 100644 simgear/scene/tgdb/TileCache.hxx create mode 100644 simgear/scene/tgdb/TileEntry.cxx create mode 100644 simgear/scene/tgdb/TileEntry.hxx diff --git a/projects/VC7.1/SimGear.vcproj b/projects/VC7.1/SimGear.vcproj index 5a817b0f..77477a11 100755 --- a/projects/VC7.1/SimGear.vcproj +++ b/projects/VC7.1/SimGear.vcproj @@ -893,6 +893,21 @@ + + + + + + + + + + (refAttr.first.get()); if (!texture) return; - + texture->setDataVariance(Object::STATIC); } @@ -406,7 +406,7 @@ osg::Node* OptimizeModelPolicy::optimize(osg::Node* node, // STATIC so that textures will be globally shared. SGTexDataVarianceVisitor dataVarianceVisitor; node->accept(dataVarianceVisitor); - + SGTexCompressionVisitor texComp; node->accept(texComp); return node; @@ -585,4 +585,4 @@ typedef ModelRegistryCallback g_acRegister("ac"); -} +} diff --git a/simgear/scene/model/SGReaderWriterXML.cxx b/simgear/scene/model/SGReaderWriterXML.cxx index 88f9e645..b952248a 100644 --- a/simgear/scene/model/SGReaderWriterXML.cxx +++ b/simgear/scene/model/SGReaderWriterXML.cxx @@ -181,6 +181,7 @@ sgLoad3DModel_internal(const string &path, throw sg_io_exception("Failed to load 3D model", sg_location(modelpath.str())); } + model->setName(modelpath.str()); bool needTransform=false; // Set up the alignment node if needed diff --git a/simgear/scene/tgdb/Makefile.am b/simgear/scene/tgdb/Makefile.am index 7c0a7fa3..0600f3d1 100644 --- a/simgear/scene/tgdb/Makefile.am +++ b/simgear/scene/tgdb/Makefile.am @@ -9,6 +9,7 @@ include_HEADERS = \ obj.hxx \ pt_lights.hxx \ userdata.hxx \ + ReaderWriterSTG.hxx \ SGOceanTile.hxx \ SGVasiDrawable.hxx \ SGDirectionalLightBin.hxx \ @@ -19,18 +20,23 @@ include_HEADERS = \ SGTriangleBin.hxx \ SGVertexArrayBin.hxx \ GroundLightManager.hxx \ - ShaderGeometry.hxx + ShaderGeometry.hxx \ + TileCache.hxx \ + TileEntry.hxx libsgtgdb_a_SOURCES = \ apt_signs.cxx \ obj.cxx \ pt_lights.cxx \ userdata.cxx \ + ReaderWriterSTG.cxx \ SGOceanTile.cxx \ SGReaderWriterBTG.cxx \ SGVasiDrawable.cxx \ GroundLightManager.cxx \ ShaderGeometry.cxx \ + TileCache.cxx \ + TileEntry.cxx \ TreeBin.cxx INCLUDES = -I$(top_srcdir) diff --git a/simgear/scene/tgdb/ReaderWriterSTG.cxx b/simgear/scene/tgdb/ReaderWriterSTG.cxx new file mode 100644 index 00000000..8344245e --- /dev/null +++ b/simgear/scene/tgdb/ReaderWriterSTG.cxx @@ -0,0 +1,77 @@ +// tileentry.cxx -- routines to handle a scenery tile +// +// Written by Curtis Olson, started May 1998. +// +// Copyright (C) 1998 - 2001 Curtis L. Olson - http://www.flightgear.org/~curt +// +// This program is free software; you can redistribute it and/or +// modify it under the terms of the GNU General Public License as +// published by the Free Software Foundation; either version 2 of the +// License, or (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, but +// WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +// General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program; if not, write to the Free Software +// Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. +// + +#include + +#include +#include + +#include "TileEntry.hxx" +#include "ReaderWriterSTG.hxx" + +using namespace simgear; + +const char* ReaderWriterSTG::className() const +{ + return "STG Database reader"; +} + +bool ReaderWriterSTG::acceptsExtension(const std::string& extension) const +{ + return (osgDB::equalCaseInsensitive(extension, "gz") + || osgDB::equalCaseInsensitive(extension, "stg")); +} + +//#define SLOW_PAGER 1 +#ifdef SLOW_PAGER +#include +#endif + +osgDB::ReaderWriter::ReadResult +ReaderWriterSTG::readNode(const std::string& fileName, + const osgDB::ReaderWriter::Options* options) const +{ + std::string ext = osgDB::getLowerCaseFileExtension(fileName); + if(!acceptsExtension(ext)) + return ReadResult::FILE_NOT_HANDLED; + std::string stgFileName; + if (osgDB::equalCaseInsensitive(ext, "gz")) { + stgFileName = osgDB::getNameLessExtension(fileName); + if (!acceptsExtension( + osgDB::getLowerCaseFileExtension(stgFileName))) { + return ReadResult::FILE_NOT_HANDLED; + } + } else { + stgFileName = fileName; + } + osg::Node* result + = TileEntry::loadTileByName(osgDB::getNameLessExtension(stgFileName), + options); + // For debugging race conditions +#ifdef SLOW_PAGER + sleep(5); +#endif + if (result) + return result; + else + return ReadResult::FILE_NOT_HANDLED; +} + diff --git a/simgear/scene/tgdb/ReaderWriterSTG.hxx b/simgear/scene/tgdb/ReaderWriterSTG.hxx new file mode 100644 index 00000000..7f53f65a --- /dev/null +++ b/simgear/scene/tgdb/ReaderWriterSTG.hxx @@ -0,0 +1,41 @@ +// tileentry.hxx -- routines to handle an individual scenery tile +// +// Written by Curtis Olson, started May 1998. +// +// Copyright (C) 1998 - 2001 Curtis L. Olson - http://www.flightgear.org/~curt +// +// This program is free software; you can redistribute it and/or +// modify it under the terms of the GNU General Public License as +// published by the Free Software Foundation; either version 2 of the +// License, or (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, but +// WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +// General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program; if not, write to the Free Software +// Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + + +#ifndef _READERWRITERSTG_HXX +#define _READERWRITERSTG_HXX + +#include + +namespace simgear { + +class ReaderWriterSTG : public osgDB::ReaderWriter { +public: + virtual const char* className() const; + + virtual bool acceptsExtension(const std::string& extension) const; + + virtual ReadResult readNode(const std::string& fileName, + const osgDB::ReaderWriter::Options* options) + const; +}; + +} +#endif // _READERWRITERSTG_HXX diff --git a/simgear/scene/tgdb/SGReaderWriterBTGOptions.hxx b/simgear/scene/tgdb/SGReaderWriterBTGOptions.hxx index 86174bf2..ed612325 100644 --- a/simgear/scene/tgdb/SGReaderWriterBTGOptions.hxx +++ b/simgear/scene/tgdb/SGReaderWriterBTGOptions.hxx @@ -15,7 +15,7 @@ // Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. // #ifndef SGREADERWRITERBTGOPTIONS_HXX -#define SGREADERWRITERBTGOPTIONS_HXX 1 +#define SGREADERWRITERBTGOPTIONS_HXX #include #include @@ -28,7 +28,7 @@ public: _useRandomObjects(false), _useRandomVegetation(false) {} - + SGReaderWriterBTGOptions(const SGReaderWriterBTGOptions& options, const osg::CopyOp& copyop=osg::CopyOp::SHALLOW_COPY): osgDB::ReaderWriter::Options(options, copyop), diff --git a/simgear/scene/tgdb/TileCache.cxx b/simgear/scene/tgdb/TileCache.cxx new file mode 100644 index 00000000..9004f706 --- /dev/null +++ b/simgear/scene/tgdb/TileCache.cxx @@ -0,0 +1,171 @@ +// newcache.cxx -- routines to handle scenery tile caching +// +// Written by Curtis Olson, started December 2000. +// +// Copyright (C) 2000 Curtis L. Olson - http://www.flightgear.org/~curt +// +// This program is free software; you can redistribute it and/or +// modify it under the terms of the GNU General Public License as +// published by the Free Software Foundation; either version 2 of the +// License, or (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, but +// WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +// General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program; if not, write to the Free Software +// Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. +// +// $Id$ + +#include +#include +#include + +#include "TileEntry.hxx" +#include "TileCache.hxx" + +SG_USING_NAMESPACE(std); + +using simgear::TileEntry; +using simgear::TileCache; + +TileCache::TileCache( void ) : + max_cache_size(100) +{ + tile_cache.clear(); +} + + +TileCache::~TileCache( void ) { + clear_cache(); +} + + +// Free a tile cache entry +void TileCache::entry_free( long cache_index ) { + SG_LOG( SG_TERRAIN, SG_DEBUG, "FREEING CACHE ENTRY = " << cache_index ); + TileEntry *tile = tile_cache[cache_index]; + tile->removeFromSceneGraph(); + + tile->free_tile(); + delete tile; + + tile_cache.erase( cache_index ); +} + + +// Initialize the tile cache subsystem +void TileCache::init( void ) { + SG_LOG( SG_TERRAIN, SG_INFO, "Initializing the tile cache." ); + + SG_LOG( SG_TERRAIN, SG_INFO, " max cache size = " + << max_cache_size ); + SG_LOG( SG_TERRAIN, SG_INFO, " current cache size = " + << tile_cache.size() ); + +#if 0 // don't clear the cache + clear_cache(); +#endif + + SG_LOG( SG_TERRAIN, SG_INFO, " done with init()" ); +} + + +// Search for the specified "bucket" in the cache +bool TileCache::exists( const SGBucket& b ) const { + long tile_index = b.gen_index(); + const_tile_map_iterator it = tile_cache.find( tile_index ); + + return ( it != tile_cache.end() ); +} + + +// Return the index of the oldest tile in the cache, return -1 if +// nothing available to be removed. +long TileCache::get_oldest_tile() { + // we need to free the furthest entry + long min_index = -1; + double timestamp = 0.0; + double min_time = DBL_MAX; + + tile_map_iterator current = tile_cache.begin(); + tile_map_iterator end = tile_cache.end(); + + for ( ; current != end; ++current ) { + long index = current->first; + TileEntry *e = current->second; + if ( e->is_loaded() ) { + timestamp = e->get_timestamp(); + if ( timestamp < min_time ) { + min_time = timestamp; + min_index = index; + } + } else { + SG_LOG( SG_TERRAIN, SG_DEBUG, "loaded = " << e->is_loaded() + << " time stamp = " << e->get_timestamp() ); + } + } + + SG_LOG( SG_TERRAIN, SG_DEBUG, " index = " << min_index ); + SG_LOG( SG_TERRAIN, SG_DEBUG, " min_time = " << min_time ); + + return min_index; +} + + +// Clear the inner ring flag for all tiles in the cache so that the +// external tile scheduler can flag the inner ring correctly. +void TileCache::clear_inner_ring_flags() { + tile_map_iterator current = tile_cache.begin(); + tile_map_iterator end = tile_cache.end(); + + for ( ; current != end; ++current ) { + TileEntry *e = current->second; + if ( e->is_loaded() ) { + e->set_inner_ring( false ); + } + } +} + +// Clear a cache entry, note that the cache only holds pointers +// and this does not free the object which is pointed to. +void TileCache::clear_entry( long cache_index ) { + tile_cache.erase( cache_index ); +} + + +// Clear all completely loaded tiles (ignores partially loaded tiles) +void TileCache::clear_cache() { + std::vector indexList; + tile_map_iterator current = tile_cache.begin(); + tile_map_iterator end = tile_cache.end(); + + for ( ; current != end; ++current ) { + long index = current->first; + SG_LOG( SG_TERRAIN, SG_DEBUG, "clearing " << index ); + TileEntry *e = current->second; + if ( e->is_loaded() ) { + e->tile_bucket.make_bad(); + // entry_free modifies tile_cache, so store index and call entry_free() later; + indexList.push_back( index); + } + } + for (unsigned int it = 0; it < indexList.size(); it++) { + entry_free( indexList[ it]); + } +} + +/** + * Create a new tile and schedule it for loading. + */ +bool TileCache::insert_tile( TileEntry *e ) { + // register tile in the cache + long tile_index = e->get_tile_bucket().gen_index(); + tile_cache[tile_index] = e; + + return true; +} + diff --git a/simgear/scene/tgdb/TileCache.hxx b/simgear/scene/tgdb/TileCache.hxx new file mode 100644 index 00000000..6e137d0a --- /dev/null +++ b/simgear/scene/tgdb/TileCache.hxx @@ -0,0 +1,129 @@ +// newcache.hxx -- routines to handle scenery tile caching +// +// Written by Curtis Olson, started December 2000. +// +// Copyright (C) 2000 Curtis L. Olson - http://www.flightgear.org/~curt +// +// This program is free software; you can redistribute it and/or +// modify it under the terms of the GNU General Public License as +// published by the Free Software Foundation; either version 2 of the +// License, or (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, but +// WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +// General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program; if not, write to the Free Software +// Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. +// +// $Id$ + + +#ifndef _TILECACHE_HXX +#define _TILECACHE_HXX + +#include + +#include +#include +#include + +SG_USING_STD(map); + +namespace simgear { + +// A class to store and manage a pile of tiles +class TileCache { +public: + typedef map < long, simgear::TileEntry * > tile_map; + typedef tile_map::iterator tile_map_iterator; + typedef tile_map::const_iterator const_tile_map_iterator; +private: + // cache storage space + tile_map tile_cache; + + // maximum cache size + int max_cache_size; + + // pointers to allow an external linear traversal of cache entries + tile_map_iterator current; + + // Free a tile cache entry + void entry_free( long cache_index ); + +public: + tile_map_iterator begin() { return tile_cache.begin(); } + tile_map_iterator end() { return tile_cache.end(); } + const_tile_map_iterator begin() const { return tile_cache.begin(); } + const_tile_map_iterator end() const { return tile_cache.end(); } + + // Constructor + TileCache(); + + // Destructor + ~TileCache(); + + // Initialize the tile cache subsystem + void init( void ); + + // Check if the specified "bucket" exists in the cache + bool exists( const SGBucket& b ) const; + + // Return the index of the oldest tile in the cache, return -1 if + // nothing available to be removed. + long get_oldest_tile(); + + // Clear the inner ring flag for all tiles in the cache so that + // the external tile scheduler can flag the inner ring correctly. + void clear_inner_ring_flags(); + + // Clear a cache entry, note that the cache only holds pointers + // and this does not free the object which is pointed to. + void clear_entry( long cache_entry ); + + // Clear all completely loaded tiles (ignores partially loaded tiles) + void clear_cache(); + + // Return a pointer to the specified tile cache entry + inline simgear::TileEntry *get_tile( const long tile_index ) const { + const_tile_map_iterator it = tile_cache.find( tile_index ); + if ( it != tile_cache.end() ) { + return it->second; + } else { + return NULL; + } + } + + // Return a pointer to the specified tile cache entry + inline simgear::TileEntry *get_tile( const SGBucket& b ) const { + return get_tile( b.gen_index() ); + } + + // Return the cache size + inline size_t get_size() const { return tile_cache.size(); } + + // External linear traversal of cache + inline void reset_traversal() { current = tile_cache.begin(); } + inline bool at_end() { return current == tile_cache.end(); } + inline simgear::TileEntry *get_current() const { + // cout << "index = " << current->first << endl; + return current->second; + } + inline void next() { ++current; } + + inline int get_max_cache_size() const { return max_cache_size; } + inline void set_max_cache_size( int m ) { max_cache_size = m; } + + /** + * Create a new tile and enqueue it for loading. + * @param b + * @return success/failure + */ + bool insert_tile( simgear::TileEntry* e ); +}; + +} + +#endif // _TILECACHE_HXX diff --git a/simgear/scene/tgdb/TileEntry.cxx b/simgear/scene/tgdb/TileEntry.cxx new file mode 100644 index 00000000..0b4ebfad --- /dev/null +++ b/simgear/scene/tgdb/TileEntry.cxx @@ -0,0 +1,503 @@ +// tileentry.cxx -- routines to handle a scenery tile +// +// Written by Curtis Olson, started May 1998. +// +// Copyright (C) 1998 - 2001 Curtis L. Olson - http://www.flightgear.org/~curt +// +// This program is free software; you can redistribute it and/or +// modify it under the terms of the GNU General Public License as +// published by the Free Software Foundation; either version 2 of the +// License, or (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, but +// WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +// General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program; if not, write to the Free Software +// Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + +#include +#include + +#include STL_STRING +#include + +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "ReaderWriterSTG.hxx" +#include "TileEntry.hxx" + +SG_USING_STD(string); +using namespace simgear; + +ModelLoadHelper *TileEntry::_modelLoader=0; + +namespace { +osgDB::RegisterReaderWriterProxy g_readerWriterSTGProxy; +ModelRegistryCallbackProxy g_stgCallbackProxy("stg"); +} + +// FIXME: investigate what huge update flood is clamped away here ... +class FGTileUpdateCallback : public osg::NodeCallback { +public: + virtual void operator()(osg::Node* node, osg::NodeVisitor* nv) + { + assert(dynamic_cast(nv)); + SGUpdateVisitor* updateVisitor = static_cast(nv); + + osg::Vec3 center = node->getBound().center(); + double distance = dist(updateVisitor->getGlobalEyePos(), + SGVec3d(center[0], center[1], center[2])); + if (updateVisitor->getVisibility() + node->getBound().radius() < distance) + return; + + traverse(node, nv); + } +}; + +namespace +{ +// Update the timestamp on a tile whenever it is in view. + +class TileCullCallback : public osg::NodeCallback +{ +public: + TileCullCallback() : _timeStamp(0) {} + TileCullCallback(const TileCullCallback& tc, const osg::CopyOp& copyOp) : + NodeCallback(tc, copyOp), _timeStamp(tc._timeStamp) + { + } + + virtual void operator()(osg::Node* node, osg::NodeVisitor* nv); + double getTimeStamp() const { return _timeStamp; } + void setTimeStamp(double timeStamp) { _timeStamp = timeStamp; } +protected: + double _timeStamp; +}; +} + +void TileCullCallback::operator()(osg::Node* node, osg::NodeVisitor* nv) +{ + if (nv->getFrameStamp()) + _timeStamp = nv->getFrameStamp()->getReferenceTime(); + traverse(node, nv); +} + +double TileEntry::get_timestamp() const +{ + if (_node.valid()) { + return (dynamic_cast(_node->getCullCallback())) + ->getTimeStamp(); + } else + return DBL_MAX; +} + +void TileEntry::set_timestamp(double time_ms) +{ + if (_node.valid()) { + TileCullCallback* cb + = dynamic_cast(_node->getCullCallback()); + if (cb) + cb->setTimeStamp(time_ms); + } +} + +// Constructor +TileEntry::TileEntry ( const SGBucket& b ) + : tile_bucket( b ), + _node( new osg::LOD ), + is_inner_ring(false), + free_tracker(0), + tileFileName(b.gen_index_str()) +{ + _node->setUpdateCallback(new FGTileUpdateCallback); + _node->setCullCallback(new TileCullCallback); + tileFileName += ".stg"; + _node->setName(tileFileName); + // Give a default LOD range so that traversals that traverse + // active children (like the groundcache lookup) will work before + // tile manager has had a chance to update this node. + _node->setRange(0, 0.0, 10000.0); +} + + +// Destructor +TileEntry::~TileEntry () +{ +} + +static void WorldCoordinate(osg::Matrix& obj_pos, double lat, + double lon, double elev, double hdg) +{ + SGGeod geod = SGGeod::fromDegM(lon, lat, elev); + obj_pos = geod.makeZUpFrame(); + // hdg is not a compass heading, but a counter-clockwise rotation + // around the Z axis + obj_pos.preMult(osg::Matrix::rotate(hdg * SGD_DEGREES_TO_RADIANS, + 0.0, 0.0, 1.0)); +} + + +// Free "n" leaf elements of an ssg tree. returns the number of +// elements freed. An empty branch node is considered a leaf. This +// is intended to spread the load of freeing a complex tile out over +// several frames. +static int fgPartialFreeSSGtree( osg::Group *b, int n ) { + int num_deletes = b->getNumChildren(); + + b->removeChildren(0, b->getNumChildren()); + + return num_deletes; +} + + +// Clean up the memory used by this tile and delete the arrays used by +// ssg as well as the whole ssg branch +bool TileEntry::free_tile() { + int delete_size = 100; + SG_LOG( SG_TERRAIN, SG_DEBUG, + "FREEING TILE = (" << tile_bucket << ")" ); + + SG_LOG( SG_TERRAIN, SG_DEBUG, "(start) free_tracker = " << free_tracker ); + + if ( !(free_tracker & NODES) ) { + free_tracker |= NODES; + } else if ( !(free_tracker & VEC_PTRS) ) { + free_tracker |= VEC_PTRS; + } else if ( !(free_tracker & TERRA_NODE) ) { + // delete the terrain branch (this should already have been + // disconnected from the scene graph) + SG_LOG( SG_TERRAIN, SG_DEBUG, "FREEING terra_transform" ); + if ( fgPartialFreeSSGtree( _node.get(), delete_size ) == 0 ) { + _node = 0; + free_tracker |= TERRA_NODE; + } + } else if ( !(free_tracker & LIGHTMAPS) ) { + free_tracker |= LIGHTMAPS; + } else { + return true; + } + + SG_LOG( SG_TERRAIN, SG_DEBUG, "(end) free_tracker = " << free_tracker ); + + // if we fall down to here, we still have work todo, return false + return false; +} + + +// Update the ssg transform node for this tile so it can be +// properly drawn relative to our (0,0,0) point +void TileEntry::prep_ssg_node(float vis) { + if (!is_loaded()) + return; + // visibility can change from frame to frame so we update the + // range selector cutoff's each time. + float bounding_radius = _node->getChild(0)->getBound().radius(); + _node->setRange( 0, 0, vis + bounding_radius ); +} + +bool TileEntry::obj_load( const string& path, + osg::Group *geometry, bool is_base, const osgDB::ReaderWriter::Options*options) +{ + osg::Node* node = osgDB::readNodeFile(path, options); + if (node) + geometry->addChild(node); + + return node; +} + + +typedef enum { + OBJECT, + OBJECT_SHARED, + OBJECT_STATIC, + OBJECT_SIGN, + OBJECT_RUNWAY_SIGN +} object_type; + + +// storage class for deferred object processing in TileEntry::load() +struct Object { + Object(object_type t, const string& token, const SGPath& p, istream& in) + : type(t), path(p) + { + in >> name; + if (type != OBJECT) + in >> lon >> lat >> elev >> hdg; + in >> ::skipeol; + + if (type == OBJECT) + SG_LOG(SG_TERRAIN, SG_INFO, " " << token << " " << name); + else + SG_LOG(SG_TERRAIN, SG_INFO, " " << token << " " << name << " lon=" << + lon << " lat=" << lat << " elev=" << elev << " hdg=" << hdg); + } + object_type type; + string name; + SGPath path; + double lon, lat, elev, hdg; +}; + +// Work in progress... load the tile based entirely by name cuz that's +// what we'll want to do with the database pager. + +osg::Node* +TileEntry::loadTileByName(const string& index_str, + const osgDB::ReaderWriter::Options* options) +{ + long tileIndex; + { + std::istringstream idxStream(index_str); + idxStream >> tileIndex; + } + SGBucket tile_bucket(tileIndex); + const string basePath = tile_bucket.gen_base_path(); + + bool found_tile_base = false; + + SGPath object_base; + vector objects; + + SG_LOG( SG_TERRAIN, SG_INFO, "Loading tile " << index_str ); + + osgDB::FilePathList path_list=options->getDatabasePathList(); + + // scan and parse all files and store information + for (unsigned int i = 0; i < path_list.size(); i++) { + // If we found a terrain tile in Terrain/, we have to process the + // Objects/ dir in the same group, too, before we can stop scanning. + // FGGlobals::set_fg_scenery() inserts an empty string to path_list + // as marker. + + if (path_list[i].empty()) { + if (found_tile_base) + break; + else + continue; + } + + bool has_base = false; + + SGPath tile_path = path_list[i]; + tile_path.append(basePath); + + SGPath basename = tile_path; + basename.append( index_str ); + + SG_LOG( SG_TERRAIN, SG_INFO, " Trying " << basename.str() ); + + + // Check for master .stg (scene terra gear) file + SGPath stg_name = basename; + stg_name.concat( ".stg" ); + + sg_gzifstream in( stg_name.str() ); + if ( !in.is_open() ) + continue; + + while ( ! in.eof() ) { + string token; + in >> token; + + if ( token.empty() || token[0] == '#' ) { + in >> ::skipeol; + continue; + } + // Load only once (first found) + if ( token == "OBJECT_BASE" ) { + string name; + in >> name >> ::skipws; + SG_LOG( SG_TERRAIN, SG_INFO, " " << token << " " << name ); + + if (!found_tile_base) { + found_tile_base = true; + has_base = true; + + object_base = tile_path; + object_base.append(name); + + } else + SG_LOG(SG_TERRAIN, SG_INFO, " (skipped)"); + + // Load only if base is not in another file + } else if ( token == "OBJECT" ) { + if (!found_tile_base || has_base) + objects.push_back(new Object(OBJECT, token, tile_path, in)); + else { + string name; + in >> name >> ::skipeol; + SG_LOG(SG_TERRAIN, SG_INFO, " " << token << " " + << name << " (skipped)"); + } + + // Always OK to load + } else if ( token == "OBJECT_STATIC" ) { + objects.push_back(new Object(OBJECT_STATIC, token, tile_path, in)); + + } else if ( token == "OBJECT_SHARED" ) { + objects.push_back(new Object(OBJECT_SHARED, token, tile_path, in)); + + } else if ( token == "OBJECT_SIGN" ) { + objects.push_back(new Object(OBJECT_SIGN, token, tile_path, in)); + + } else if ( token == "OBJECT_RUNWAY_SIGN" ) { + objects.push_back(new Object(OBJECT_RUNWAY_SIGN, token, tile_path, in)); + + } else { + SG_LOG( SG_TERRAIN, SG_DEBUG, + "Unknown token '" << token << "' in " << stg_name.str() ); + in >> ::skipws; + } + } + } + + SGReaderWriterBTGOptions *opt = new SGReaderWriterBTGOptions(*dynamic_cast(options)); + + // obj_load() will generate ground lighting for us ... + osg::Group* new_tile = new osg::Group; + + if (found_tile_base) { + // load tile if found ... + opt->setCalcLights(true); + obj_load( object_base.str(), new_tile, true, options); + + } else { + // ... or generate an ocean tile on the fly + SG_LOG(SG_TERRAIN, SG_INFO, " Generating ocean tile"); + if ( !SGGenTile( path_list[0], tile_bucket, + opt->getMatlib(), new_tile ) ) { + SG_LOG( SG_TERRAIN, SG_ALERT, + "Warning: failed to generate ocean tile!" ); + } + } + + + // now that we have a valid center, process all the objects + for (unsigned int j = 0; j < objects.size(); j++) { + const Object *obj = objects[j]; + + if (obj->type == OBJECT) { + SGPath custom_path = obj->path; + custom_path.append( obj->name ); + opt->setCalcLights(true); + obj_load( custom_path.str(), new_tile, false, options); + + } else if (obj->type == OBJECT_SHARED || obj->type == OBJECT_STATIC) { + // object loading is deferred to main render thread, + // but lets figure out the paths right now. + SGPath custom_path; + if ( obj->type == OBJECT_STATIC ) { + custom_path = obj->path; + } else { + // custom_path = globals->get_fg_root(); + } + custom_path.append( obj->name ); + + osg::Matrix obj_pos; + WorldCoordinate( obj_pos, obj->lat, obj->lon, obj->elev, obj->hdg ); + + osg::MatrixTransform *obj_trans = new osg::MatrixTransform; + obj_trans->setMatrix( obj_pos ); + + // wire as much of the scene graph together as we can + new_tile->addChild( obj_trans ); + + osg::Node* model = 0; + if(_modelLoader) + model = _modelLoader->loadTileModel(custom_path.str(), + obj->type == OBJECT_SHARED); + if (model) + obj_trans->addChild(model); + } else if (obj->type == OBJECT_SIGN || obj->type == OBJECT_RUNWAY_SIGN) { + // load the object itself + SGPath custom_path = obj->path; + custom_path.append( obj->name ); + + osg::Matrix obj_pos; + WorldCoordinate( obj_pos, obj->lat, obj->lon, obj->elev, obj->hdg ); + + osg::MatrixTransform *obj_trans = new osg::MatrixTransform; + obj_trans->setMatrix( obj_pos ); + + osg::Node *custom_obj = 0; + if (obj->type == OBJECT_SIGN) + custom_obj = SGMakeSign(opt->getMatlib(), custom_path.str(), obj->name); + else + custom_obj = SGMakeRunwaySign(opt->getMatlib(), custom_path.str(), obj->name); + + // wire the pieces together + if ( custom_obj != NULL ) { + obj_trans -> addChild( custom_obj ); + } + new_tile->addChild( obj_trans ); + + } + delete obj; + } + return new_tile; +} + +void +TileEntry::addToSceneGraph(osg::Group *terrain_branch) +{ + terrain_branch->addChild( _node.get() ); + + SG_LOG( SG_TERRAIN, SG_DEBUG, + "connected a tile into scene graph. _node = " + << _node.get() ); + SG_LOG( SG_TERRAIN, SG_DEBUG, "num parents now = " + << _node->getNumParents() ); +} + + +void +TileEntry::removeFromSceneGraph() +{ + SG_LOG( SG_TERRAIN, SG_DEBUG, "disconnecting TileEntry nodes" ); + + if (! is_loaded()) { + SG_LOG( SG_TERRAIN, SG_DEBUG, "removing a not-fully loaded tile!" ); + } else { + SG_LOG( SG_TERRAIN, SG_DEBUG, "removing a fully loaded tile! _node = " << _node.get() ); + } + + // find the nodes branch parent + if ( _node->getNumParents() > 0 ) { + // find the first parent (should only be one) + osg::Group *parent = _node->getParent( 0 ) ; + if( parent ) { + parent->removeChild( _node.get() ); + } + } +} + diff --git a/simgear/scene/tgdb/TileEntry.hxx b/simgear/scene/tgdb/TileEntry.hxx new file mode 100644 index 00000000..4ebc993f --- /dev/null +++ b/simgear/scene/tgdb/TileEntry.hxx @@ -0,0 +1,182 @@ +// tileentry.hxx -- routines to handle an individual scenery tile +// +// Written by Curtis Olson, started May 1998. +// +// Copyright (C) 1998 - 2001 Curtis L. Olson - http://www.flightgear.org/~curt +// +// This program is free software; you can redistribute it and/or +// modify it under the terms of the GNU General Public License as +// published by the Free Software Foundation; either version 2 of the +// License, or (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, but +// WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +// General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program; if not, write to the Free Software +// Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. +// +// $Id$ + + +#ifndef _TILEENTRY_HXX +#define _TILEENTRY_HXX + + +#ifndef __cplusplus +# error This library requires C++ +#endif + +#include + +#include +#include STL_STRING + +#include +#include +#include +#include + +#include +#include +#include +#include + +#if defined( sgi ) +#include +#endif + +SG_USING_STD(string); +SG_USING_STD(vector); + +namespace simgear { + +class ModelLoadHelper; + +/** + * A class to encapsulate everything we need to know about a scenery tile. + */ +class TileEntry { + +public: + // this tile's official location in the world + SGBucket tile_bucket; + std::string tileFileName; + + typedef vector < Point3D > point_list; + typedef point_list::iterator point_list_iterator; + typedef point_list::const_iterator const_point_list_iterator; + +private: + + // pointer to ssg range selector for this tile + osg::ref_ptr _node; + + static bool obj_load( const std::string& path, + osg::Group* geometry, + bool is_base, + const osgDB::ReaderWriter::Options* options); + + /** + * this value is used by the tile scheduler/loader to mark which + * tiles are in the primary ring (i.e. the current tile or the + * surrounding eight.) Other routines then can use this as an + * optimization and not do some operation to tiles outside of this + * inner ring. (For instance vasi color updating) + */ + bool is_inner_ring; + + /** + * this variable tracks the status of the incremental memory + * freeing. + */ + enum { + NODES = 0x01, + VEC_PTRS = 0x02, + TERRA_NODE = 0x04, + GROUND_LIGHTS = 0x08, + VASI_LIGHTS = 0x10, + RWY_LIGHTS = 0x20, + TAXI_LIGHTS = 0x40, + LIGHTMAPS = 0x80 + }; + int free_tracker; + + static ModelLoadHelper *_modelLoader; + +public: + + // Constructor + TileEntry( const SGBucket& b ); + + // Destructor + ~TileEntry(); + + static void setModelLoadHelper(ModelLoadHelper *m) { _modelLoader=m; } + + // Clean up the memory used by this tile and delete the arrays + // used by ssg as well as the whole ssg branch. This does a + // partial clean up and exits so we can spread the load across + // multiple frames. Returns false if work remaining to be done, + // true if dynamically allocated memory used by this tile is + // completely freed. + bool free_tile(); + + // Update the ssg transform node for this tile so it can be + // properly drawn relative to our (0,0,0) point + void prep_ssg_node(float vis); + + /** + * Transition to OSG database pager + */ + static osg::Node* loadTileByName(const std::string& index_str, + const osgDB::ReaderWriter::Options*); + /** + * Return true if the tile entry is loaded, otherwise return false + * indicating that the loading thread is still working on this. + */ + inline bool is_loaded() const + { + return _node->getNumChildren() > 0; + } + + /** + * Return the "bucket" for this tile + */ + inline const SGBucket& get_tile_bucket() const { return tile_bucket; } + + /** + * Add terrain mesh and ground lighting to scene graph. + */ + void addToSceneGraph( osg::Group *terrain_branch); + + /** + * disconnect terrain mesh and ground lighting nodes from scene + * graph for this tile. + */ + void removeFromSceneGraph(); + + + /** + * return the scenegraph node for the terrain + */ + osg::LOD *getNode() const { return _node.get(); } + + double get_timestamp() const; + void set_timestamp( double time_ms ); + + inline bool get_inner_ring() const { return is_inner_ring; } + inline void set_inner_ring( bool val ) { is_inner_ring = val; } +}; + +class ModelLoadHelper { +public: + virtual osg::Node *loadTileModel(const string& modelPath, bool cacheModel)=0; + +}; + +} + +#endif // _TILEENTRY_HXX diff --git a/simgear/scene/tgdb/userdata.cxx b/simgear/scene/tgdb/userdata.cxx index 5413c8df..6a4065dc 100644 --- a/simgear/scene/tgdb/userdata.cxx +++ b/simgear/scene/tgdb/userdata.cxx @@ -60,7 +60,7 @@ void sgUserDataInit( SGPropertyNode *p ) { root_props = p; } - osg::Node* sgGetRandomModel(SGMatModel *obj) { +osg::Node* sgGetRandomModel(SGMatModel *obj) { return obj->get_random_model( root_props ); } -- 2.39.5