osgEarth 2.1.1
|
00001 /* -*-c++-*- */ 00002 /* osgEarth - Dynamic map generation toolkit for OpenSceneGraph 00003 * Copyright 2008-2010 Pelican Mapping 00004 * http://osgearth.org 00005 * 00006 * osgEarth is free software; you can redistribute it and/or modify 00007 * it under the terms of the GNU Lesser General Public License as published by 00008 * the Free Software Foundation; either version 2 of the License, or 00009 * (at your option) any later version. 00010 * 00011 * This program is distributed in the hope that it will be useful, 00012 * but WITHOUT ANY WARRANTY; without even the implied warranty of 00013 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 00014 * GNU Lesser General Public License for more details. 00015 * 00016 * You should have received a copy of the GNU Lesser General Public License 00017 * along with this program. If not, see <http://www.gnu.org/licenses/> 00018 */ 00019 #include "MeshManager" 00020 #include <osg/CullFace> 00021 #include <osg/Texture2D> 00022 00023 // -------------------------------------------------------------------------- 00024 00025 struct ImageRequest : public osgEarth::TaskRequest 00026 { 00027 ImageRequest( ImageLayer* layer, const TileKey& key ) : _layer(layer), _key(key) { } 00028 00029 void operator()( ProgressCallback* progress ) 00030 { 00031 _result = _layer->createImage( _key ); 00032 } 00033 00034 osg::ref_ptr<ImageLayer> _layer; 00035 TileKey _key; 00036 GeoImage _result; 00037 }; 00038 00039 // -------------------------------------------------------------------------- 00040 00041 MeshManager::MeshManager( Manifold* manifold, Map* map ) : 00042 _manifold( manifold ), 00043 _map( map ), 00044 _minGeomLevel( 1 ), 00045 _minActiveLevel( 0 ), 00046 _maxActiveLevel( MAX_ACTIVE_LEVEL ), 00047 _maxJobsPerFrame( MAX_JOBS_PER_FRAME ) 00048 { 00049 // fire up a task service to load textures. 00050 _imageService = new TaskService( "Image Service", 16 ); 00051 00052 _amrGeom = new AMRGeometry(); 00053 _amrGeom->setDataVariance( osg::Object::DYNAMIC ); 00054 00055 _amrGeode = new osg::Geode(); 00056 _amrGeode->addDrawable( _amrGeom.get() ); 00057 _amrGeode->getOrCreateStateSet()->setAttributeAndModes( new osg::CullFace( osg::CullFace::BACK ), 1 ); 00058 00059 // set up the manifold framework. 00060 manifold->initialize( this ); 00061 } 00062 00063 NodeIndex 00064 MeshManager::addNode( const MeshNode& node ) 00065 { 00066 NodeIndex result; 00067 00068 if ( _freeList.empty() ) 00069 { 00070 _nodes.push_back( node ); 00071 result = _nodes.size()-1; 00072 } 00073 else 00074 { 00075 NodeIndex ni = _freeList.front(); 00076 _freeList.pop(); 00077 _nodes[ni] = node; 00078 result = ni; 00079 } 00080 00081 return result; 00082 } 00083 00084 NodeIndex 00085 MeshManager::addNode( const osg::Vec3d& manifoldCoord ) 00086 { 00087 return addNode( _manifold->createNode( manifoldCoord ) ); 00088 } 00089 00090 void 00091 MeshManager::removeNode( NodeIndex ni ) 00092 { 00093 _freeList.push( ni ); 00094 } 00095 00096 void 00097 MeshManager::queueForRefresh( Diamond* d ) 00098 { 00099 //OE_NOTICE << d->_name << ": queued for refresh." << std::endl; 00100 _dirtyQueue.push( d ); 00101 } 00102 00103 void 00104 MeshManager::queueForSplit( Diamond* d, float priority ) 00105 { 00106 if ( !d->_queuedForSplit ) 00107 { 00108 //OE_NOTICE << "q split: " << d->_name << std::endl; 00109 _splitQueue.push( DiamondJob( d, priority ) ); 00110 d->_queuedForSplit = true; 00111 } 00112 } 00113 00114 void 00115 MeshManager::queueForMerge( Diamond* d, float priority ) 00116 { 00117 if ( !d->_queuedForMerge ) 00118 { 00119 //OE_NOTICE << "q merge: " << d->_name << std::endl; 00120 _mergeQueue.push( DiamondJob( d, priority ) ); 00121 d->_queuedForMerge = true; 00122 } 00123 } 00124 00125 void 00126 MeshManager::queueForImage( Diamond* d, float priority ) 00127 { 00128 if ( !d->_queuedForImage && !d->_imageRequest.valid() ) 00129 { 00130 //OE_NOTICE << "REQ: " << d->_key->str() << " queueing request..." << std::endl; 00131 d->_imageRequest = new ImageRequest( _map->getImageLayerAt(0), d->_key ); 00132 _imageService->add( d->_imageRequest.get() ); 00133 _imageQueue.push_back( DiamondJob( d, priority ) ); //.insert( DiamondJob( d, priority ) ); 00134 d->_queuedForImage = true; 00135 } 00136 } 00137 00138 static void 00139 outlineTexture( osg::Image* image ) 00140 { 00141 for( int s=1; s<image->s()-1; ++s ) 00142 { 00143 *((unsigned int*)image->data( s, 1 )) = 0x00ff00ff; 00144 *((unsigned int*)image->data( s, image->t()-2 )) = 0x00ff00ff; 00145 } 00146 00147 for( int t=1; t<image->t()-1; ++t ) 00148 { 00149 *((unsigned int*)image->data( 1, t )) = 0x00ff00ff; 00150 *((unsigned int*)image->data( image->s()-2, t )) = 0x00ff00ff; 00151 } 00152 } 00153 00154 void 00155 MeshManager::update() 00156 { 00157 int j; 00158 00159 // process the split queue. these are diamonds that have requested to be split into 00160 // all four children. 00161 for( j=0; j<_maxJobsPerFrame && !_splitQueue.empty(); ++j ) 00162 //if( !_splitQueue.empty() ) 00163 { 00164 Diamond* d = _splitQueue.top()._d.get(); 00165 if ( d->_status == ACTIVE && d->referenceCount() > 1 ) 00166 { 00167 if ( d->_queuedForSplit ) 00168 { 00169 //OE_NOTICE << "split: " << d->_name << "\n"; 00170 d->split(); 00171 d->_queuedForSplit = false; 00172 d->_queuedForMerge = false; 00173 } 00174 else 00175 { 00176 //OE_WARN << d->_name << " was in the split Q, but NOT marked for splitting!" << std::endl; 00177 } 00178 } 00179 else 00180 { 00181 // the diamond was removed while in the queue. ignore it. 00182 } 00183 _splitQueue.pop(); 00184 } 00185 00186 // process the merge queue. these are diamonds that have requested that all their 00187 // children be removed. 00188 // FUTURE: process jobs until we reach some sort of time quota? 00189 00190 for( j=0; j<_maxJobsPerFrame && !_mergeQueue.empty(); ++j ) 00191 { 00192 Diamond* d = _mergeQueue.top()._d.get(); 00193 if ( d->_status == ACTIVE && d->referenceCount() > 1 ) 00194 { 00195 if ( d->_queuedForMerge ) 00196 { 00197 //OE_NOTICE << "merge: " << d->_name << "\n"; 00198 00199 //TODO: when you merge, children are recursively merged..thus the merge 00200 //may take some time. rather it might be better to traverse and schedule 00201 //child merges first.? 00202 d->merge(); 00203 d->_queuedForMerge = false; 00204 d->_queuedForSplit = false; 00205 } 00206 else 00207 { 00208 //this means that the diamond was once queued for a merge, but then 00209 // passed cull again. 00210 //OE_WARN << d->_name << " was in the merge Q, but NOT marked for merging!" << std::endl; 00211 } 00212 } 00213 _mergeQueue.pop(); 00214 } 00215 00216 // process the texture image request queue. 00217 j=0; 00218 for( DiamondJobList::iterator i = _imageQueue.begin(); i != _imageQueue.end() && j < _maxJobsPerFrame; ++j ) 00219 //if( _imageQueue.size() > 0 ) 00220 { 00221 bool increment = true; 00222 bool remove = true; 00223 00224 Diamond* d = i->_d.get(); 00225 00226 if ( d->_status == ACTIVE && d->referenceCount() > 1 && d->_imageRequest.valid() ) 00227 { 00228 if ( d->_imageRequest->isCompleted() ) 00229 { 00230 //OE_NOTICE << "REQ: " << d->_key->str() << " completed" << std::endl; 00231 osg::Texture2D* tex = 0L; 00232 00233 #ifdef USE_DEBUG_TEXTURES 00234 00235 tex = new osg::Texture2D(); 00236 tex->setImage( createDebugImage() ); 00237 00238 #else 00239 00240 GeoImage* geoImage = dynamic_cast<GeoImage*>( d->_imageRequest->getResult() ); 00241 if ( geoImage ) 00242 { 00243 tex = new osg::Texture2D(); 00244 tex->setImage( geoImage->getImage() ); 00245 } 00246 00247 #endif // USE_DEBUG_TEXTURES 00248 00249 if ( tex ) 00250 { 00251 tex->setWrap( osg::Texture::WRAP_S, osg::Texture::CLAMP_TO_EDGE ); 00252 tex->setWrap( osg::Texture::WRAP_T, osg::Texture::CLAMP_TO_EDGE ); 00253 tex->setFilter( osg::Texture::MIN_FILTER, osg::Texture::LINEAR_MIPMAP_LINEAR ); 00254 tex->setFilter( osg::Texture::MAG_FILTER, osg::Texture::LINEAR ); 00255 d->_stateSet->setTextureAttributeAndModes( 0, tex, osg::StateAttribute::ON ); 00256 d->_stateSet->dirty(); // bump revision number so that users of this stateset can detect the change 00257 d->_hasFinalImage = true; 00258 00259 #ifdef OUTLINE_TEXTURES 00260 00261 outlineTexture( tex->getImage() ); 00262 #endif 00263 } 00264 00265 remove = true; 00266 } 00267 00268 else 00269 { 00270 remove = false; 00271 } 00272 } 00273 00274 00275 if ( remove ) 00276 { 00277 d->_imageRequest = 0L; 00278 d->_queuedForImage = false; 00279 i = _imageQueue.erase( i ); 00280 } 00281 else 00282 { 00283 ++i; 00284 } 00285 } 00286 00287 #ifdef USE_DIRTY_QUEUE 00288 00289 // process the dirty diamond queue. these are diamonds that have been changed and 00290 // need a new primitive set. 00291 // NOTE: we need to process the entire dirty queue each frame. 00292 while( _dirtyQueue.size() > 0 ) 00293 { 00294 Diamond* d = _dirtyQueue.front().get(); 00295 00296 if ( d->_status == ACTIVE && d->referenceCount() > 1 ) 00297 { 00298 // first, check to see whether the diamond's target stateset is ready. if so, 00299 // install it and mark it up to date. 00300 if ( d->_targetStateSetOwner->_stateSet->outOfSyncWith( d->_targetStateSetRevision ) ) 00301 { 00302 d->_amrDrawable->_stateSet = d->_targetStateSetOwner->_stateSet.get(); 00303 00304 d->_currentStateSetOwner = d->_targetStateSetOwner; 00305 d->_targetStateSetOwner->_stateSet->sync( d->_targetStateSetRevision ); 00306 } 00307 00308 // rebuild the primitives now. 00309 d->refreshDrawable(); 00310 } 00311 _dirtyQueue.pop(); 00312 } 00313 00314 #endif 00315 00316 //OE_NOTICE << "dq size = " << _dirtyQueue.size() << "; splits = " << _splitQueue.size() << "; merges = " << _mergeQueue.size() << std::endl; 00317 }