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 <osgEarthUtil/SpatialData> 00020 #include <osgUtil/CullVisitor> 00021 #include <osg/PolygonOffset> 00022 #include <osg/Polytope> 00023 #include <osg/Geometry> 00024 #include <osg/Depth> 00025 #include <osgText/Text> 00026 #include <sstream> 00027 00028 #define LC "[GeoGraph] " 00029 00030 using namespace osgEarth; 00031 using namespace osgEarth::Util; 00032 00033 //------------------------------------------------------------------------ 00034 00035 namespace 00036 { 00037 unsigned getIndex( const GeoExtent& cellExtent, const osg::Vec3d& point, unsigned xdim, unsigned ydim ) 00038 { 00039 double cw = cellExtent.width() / (double)xdim; 00040 double ch = cellExtent.height() / (double)ydim; 00041 00042 unsigned col = osg::clampBelow( (unsigned)((point.x()-cellExtent.xMin())/cw), xdim-1 ); 00043 unsigned row = osg::clampBelow( (unsigned)((point.y()-cellExtent.yMin())/ch), ydim-1 ); 00044 00045 return row*xdim + col; 00046 } 00047 00048 static osgText::Font* s_font = 0L; 00049 00050 osg::Geode* makeClusterGeode( const GeoExtent& cellExtent, unsigned num ) 00051 { 00052 osgText::Text* t = new osgText::Text(); 00053 00054 double clat, clon; 00055 cellExtent.getCentroid( clon, clat ); 00056 osg::Vec3d xyz; 00057 cellExtent.getSRS()->getEllipsoid()->convertLatLongHeightToXYZ( 00058 osg::DegreesToRadians( clat ), osg::DegreesToRadians( clon ), 0, xyz.x(), xyz.y(), xyz.z() ); 00059 t->setPosition( xyz ); 00060 00061 std::stringstream buf; 00062 buf << num; 00063 t->setText( buf.str() ); 00064 t->setCharacterSizeMode( osgText::TextBase::SCREEN_COORDS ); 00065 t->setCharacterSize( 22.0f ); 00066 t->setAutoRotateToScreen( true ); 00067 00068 if ( !s_font ) 00069 s_font = osgText::readFontFile( "arialbd.ttf" ); 00070 t->setFont( s_font ); 00071 00072 t->setBackdropType( osgText::Text::OUTLINE ); 00073 t->setColor( osg::Vec4(1,1,1,1) ); 00074 t->setBackdropColor( osg::Vec4(0,0,0,1) ); 00075 00076 osg::Geode* geode = new osg::Geode(); 00077 geode->addDrawable( t ); 00078 00079 osg::StateSet* s = geode->getOrCreateStateSet(); 00080 s->setAttributeAndModes( new osg::Depth(osg::Depth::ALWAYS) ); 00081 00082 t->setDataVariance( osg::Object::DYNAMIC ); 00083 00084 return geode; 00085 } 00086 00087 GeoObjectCollection::iterator 00088 findObject( GeoObjectCollection& objects, GeoObject* object ) 00089 { 00090 float key = object->getPriority(); 00091 GeoObjectCollection::iterator first = objects.find(key); 00092 if ( first == objects.end() ) 00093 return objects.end(); 00094 00095 GeoObjectCollection::iterator last = objects.upper_bound(key); 00096 for( ; first != last; ++first ) 00097 if ( first->second.get() == object ) 00098 return first; 00099 00100 return objects.end(); 00101 } 00102 } 00103 00104 //------------------------------------------------------------------------ 00105 00106 GeoObject::GeoObject() 00107 { 00108 //NOP 00109 } 00110 00111 //------------------------------------------------------------------------ 00112 00113 GeoGraph::GeoGraph(const GeoExtent& extent, float maxRange, unsigned maxObjects, 00114 unsigned splitDim, float splitRangeFactor, 00115 unsigned rootWidth, unsigned rootHeight ) : 00116 GeoCell( extent, maxRange, maxObjects, splitDim, splitRangeFactor, 0 ) 00117 { 00118 _rootWidth = osg::maximum( rootWidth, (unsigned)2 ); 00119 _rootHeight = osg::maximum( rootHeight, (unsigned)2 ); 00120 00121 if ( _depth == 0 ) 00122 { 00123 double xinterval = extent.width() / (double)_rootWidth; 00124 double yinterval = extent.height() / (double)_rootHeight; 00125 00126 for( unsigned y=0; y<_rootHeight; ++y ) 00127 { 00128 for( unsigned x=0; x<_rootWidth; ++x ) 00129 { 00130 GeoExtent cellExtent( 00131 _extent.getSRS(), 00132 _extent.xMin() + xinterval*(double)x, 00133 _extent.yMin() + yinterval*(double)y, 00134 _extent.xMin() + xinterval*(double)(x+1), 00135 _extent.yMin() + yinterval*(double)(y+1) ); 00136 00137 GeoCell * child = new GeoCell( 00138 cellExtent, 00139 _maxRange, 00140 _maxObjects, 00141 _splitDim, 00142 _splitRangeFactor, 00143 1 ); 00144 00145 this->addChild( child, 0, maxRange ); //FLT_MAX ); 00146 } 00147 } 00148 } 00149 } 00150 00151 bool 00152 GeoGraph::insertObject( GeoObject* object ) 00153 { 00154 osg::Vec3d loc; 00155 if ( object->getLocation(loc) ) 00156 { 00157 unsigned index = getIndex( _extent, loc, _rootWidth, _rootHeight ); 00158 return static_cast<GeoCell*>(getChild(index))->insertObject( object ); 00159 } 00160 else 00161 { 00162 return false; 00163 } 00164 } 00165 00166 //------------------------------------------------------------------------ 00167 00168 GeoCell::GeoCell(const GeoExtent& extent, float maxRange, unsigned maxObjects, 00169 unsigned splitDim, float splitRangeFactor, unsigned depth ) : 00170 _extent( extent ), 00171 _maxRange( maxRange ), 00172 _maxObjects( maxObjects ), 00173 _splitDim( splitDim ), 00174 _splitRangeFactor( splitRangeFactor ), 00175 _depth( depth ), 00176 _minObjects( (maxObjects/10)*8 ), // 80% 00177 _count( 0 ), 00178 _boundaryPoints( 10 ), 00179 _frameStamp( 0 ) 00180 { 00181 generateBoundaries(); 00182 //generateBoundaryGeometry(); 00183 00184 // Disable OSG's culling so we can do our own custom culling. 00185 this->setCullingActive( false ); 00186 } 00187 00188 void 00189 GeoCell::generateBoundaries() 00190 { 00191 const osg::EllipsoidModel* em = _extent.getSRS()->getEllipsoid(); 00192 static const double hae = 1e6; 00193 static const double hbe = -1e5; 00194 00195 // find the geodetic center: 00196 osg::Vec3d gcenter; 00197 _extent.getCentroid( gcenter.x(), gcenter.y() ); 00198 00199 // convert to a geocentric vector: 00200 osg::Vec3d center; 00201 em->convertLatLongHeightToXYZ( 00202 osg::DegreesToRadians(gcenter.y()), osg::DegreesToRadians(gcenter.x()), 0.0, center.x(), center.y(), center.z()); 00203 00204 osg::Vec3d centerVec = center; 00205 centerVec.normalize(); 00206 00207 // find the 4 geodetic corners: 00208 osg::Vec3d gcorner[4]; 00209 gcorner[0].set( _extent.xMin(), _extent.yMin(), 0.0 ); 00210 gcorner[1].set( _extent.xMin(), _extent.yMax(), 0.0 ); 00211 gcorner[2].set( _extent.xMax(), _extent.yMax(), 0.0 ); 00212 gcorner[3].set( _extent.xMax(), _extent.yMin(), 0.0 ); 00213 00214 // and convert those to geocentric vectors: 00215 osg::Vec3d corner[4]; 00216 osg::Vec3d cornerVec[4]; 00217 for(unsigned i=0; i<4; ++i ) 00218 { 00219 em->convertLatLongHeightToXYZ( 00220 osg::DegreesToRadians(gcorner[i].y()), osg::DegreesToRadians(gcorner[i].x()), 0.0, 00221 corner[i].x(), corner[i].y(), corner[i].z() ); 00222 cornerVec[i] = corner[i]; 00223 cornerVec[i].normalize(); 00224 } 00225 00226 // now extrude the center and corners up and down to get the boundary points: 00227 unsigned p = 0; 00228 _boundaryPoints[p++] = center + centerVec*hae; 00229 _boundaryPoints[p++] = center +centerVec*hbe; 00230 for( unsigned i=0; i<4; ++i ) 00231 { 00232 _boundaryPoints[p++] = corner[i] + cornerVec[i]*hae; 00233 _boundaryPoints[p++] = corner[i] + cornerVec[i]*hbe; 00234 } 00235 } 00236 00237 osg::BoundingSphere 00238 GeoCell::computeBound() const 00239 { 00240 osg::BoundingSphere bs; 00241 for( unsigned i=0; i<10; ++i ) 00242 bs.expandBy( _boundaryPoints[i] ); 00243 return bs; 00244 } 00245 00246 void 00247 GeoCell::generateBoundaryGeometry() 00248 { 00249 osg::Geometry* g = new osg::Geometry(); 00250 00251 osg::Vec3Array* v = new osg::Vec3Array(10); 00252 for( unsigned i=0; i<10; ++i ) 00253 (*v)[i] = _boundaryPoints[i]; 00254 g->setVertexArray( v ); 00255 00256 osg::DrawElementsUByte* el = new osg::DrawElementsUByte( GL_QUADS ); 00257 el->push_back( 7 ); el->push_back( 5 ); el->push_back( 4 ); el->push_back( 6 ); 00258 el->push_back( 9 ); el->push_back( 7 ); el->push_back( 6 ); el->push_back( 8 ); 00259 el->push_back( 3 ); el->push_back( 9 ); el->push_back( 8 ); el->push_back( 2 ); 00260 el->push_back( 5 ); el->push_back( 3 ); el->push_back( 2 ); el->push_back( 4 ); 00261 //el->push_back( 2 ); el->push_back( 8 ); el->push_back( 6 ); el->push_back( 4 ); //top 00262 //el->push_back( 9 ); el->push_back( 3 ); el->push_back( 5 ); el->push_back( 7 ); // bottom 00263 g->addPrimitiveSet( el ); 00264 00265 osg::Vec4Array* c = new osg::Vec4Array(1); 00266 (*c)[0].set( 1, 1, 1, 0.25 ); 00267 g->setColorArray( c ); 00268 g->setColorBinding( osg::Geometry::BIND_OVERALL ); 00269 00270 _boundaryColor = c; 00271 00272 g->setDataVariance( osg::Object::DYNAMIC ); 00273 g->setUseDisplayList( false ); 00274 g->setUseVertexBufferObjects( true ); 00275 00276 osg::StateSet* set = g->getOrCreateStateSet(); 00277 set->setMode( GL_BLEND, 1 ); 00278 set->setRenderingHint( osg::StateSet::TRANSPARENT_BIN ); 00279 set->setAttribute( new osg::PolygonOffset(1,1), 1 ); 00280 00281 _boundaryGeode = new osg::Geode(); 00282 _boundaryGeode->addDrawable( g ); 00283 } 00284 00285 bool 00286 GeoCell::intersects( const osg::Polytope& tope ) const 00287 { 00288 const osg::Polytope::PlaneList& planes = tope.getPlaneList(); 00289 for( osg::Polytope::PlaneList::const_iterator i = planes.begin(); i != planes.end(); ++i ) 00290 { 00291 if ( i->intersect( _boundaryPoints ) < 0 ) 00292 return false; 00293 } 00294 return true; 00295 } 00296 00297 void 00298 GeoCell::traverse( osg::NodeVisitor& nv ) 00299 { 00300 bool isCull = nv.getVisitorType() == osg::NodeVisitor::CULL_VISITOR; 00301 00302 if ( _depth > 0 ) 00303 { 00304 if ( isCull ) 00305 { 00306 // process boundary geometry, if present. 00307 if ( _boundaryGeode.valid() ) 00308 { 00309 if ( _count > 0 ) 00310 (*_boundaryColor)[0].set(1,0,0,0.35); 00311 else 00312 (*_boundaryColor)[0].set(1,1,1,0.25); 00313 _boundaryColor->dirty(); 00314 00315 _boundaryGeode->accept( nv ); 00316 } 00317 00318 // custom BSP culling function. this checks that the set of boundary points 00319 // for this cell intersects the viewing frustum. 00320 osgUtil::CullVisitor* cv = dynamic_cast<osgUtil::CullVisitor*>( &nv ); 00321 if ( cv && !intersects( cv->getCurrentCullingSet().getFrustum() ) ) 00322 { 00323 return; 00324 } 00325 00326 // passed cull, so record the framestamp. 00327 _frameStamp = cv->getFrameStamp()->getFrameNumber(); 00328 } 00329 00330 if ( _objects.size() > 0 ) 00331 { 00332 for( GeoObjectCollection::iterator i = _objects.begin(); i != _objects.end(); ++i ) 00333 { 00334 osg::Node* node = i->second->getNode(); 00335 if ( node ) 00336 node->accept( nv ); 00337 } 00338 } 00339 00340 if ( _clusterGeode.valid() ) 00341 _clusterGeode->accept( nv ); 00342 } 00343 00344 else 00345 { 00346 if ( isCull ) 00347 _frameStamp = nv.getFrameStamp()->getFrameNumber(); 00348 } 00349 00350 osg::LOD::traverse( nv ); 00351 } 00352 00353 void 00354 GeoCell::adjustCount( int delta ) 00355 { 00356 _count += delta; 00357 00358 if ( _depth > 0 && getNumParents() > 0 ) 00359 { 00360 static_cast<GeoCell*>(getParent(0))->adjustCount( delta ); 00361 00362 #if 0 00363 if ( !_clusterGeode.valid() ) 00364 { 00365 _clusterGeode = makeClusterGeode( _extent, _count ); 00366 } 00367 else 00368 { 00369 osgText::Text* t = static_cast<osgText::Text*>( _clusterGeode->getDrawable(0) ); 00370 std::stringstream buf; 00371 buf << _count; 00372 t->setText( buf.str() ); 00373 } 00374 #endif 00375 } 00376 } 00377 00378 bool 00379 GeoCell::insertObject( GeoObject* object ) 00380 { 00381 osg::Vec3d location; 00382 if ( object->getLocation(location) && _extent.contains(location.x(), location.y()) ) 00383 { 00384 object->_cell = this; 00385 _objects.insert( std::make_pair(object->getPriority(), object) ); 00386 00387 if ( _objects.size() > _maxObjects ) 00388 { 00389 GeoObjectCollection::iterator low = _objects.begin(); 00390 GeoObject* lowPriObject = low->second.get(); 00391 00392 if ( getNumChildren() == 0 ) 00393 split(); 00394 00395 lowPriObject->getLocation(location); 00396 unsigned index = getIndex(_extent, location, _splitDim, _splitDim); 00397 bool insertedOK = static_cast<GeoCell*>(getChild(index))->insertObject( lowPriObject ); 00398 if ( insertedOK ) 00399 { 00400 // remove it from this cell. 00401 _objects.erase( low ); 00402 } 00403 else 00404 { 00405 // should never ever happen.. 00406 OE_WARN << LC << "Object insertion failed" << std::endl; 00407 return false; 00408 } 00409 } 00410 return true; 00411 } 00412 else 00413 { 00414 return false; 00415 } 00416 } 00417 00418 void 00419 GeoCell::split() 00420 { 00421 // the max LOD range for children of this cell: 00422 float newRange = _maxRange * _splitRangeFactor; 00423 00424 // first create all the child cells: 00425 double xInterval = _extent.width() / (double)_splitDim; 00426 double yInterval = _extent.height() / (double)_splitDim; 00427 00428 for( unsigned row=0; row<_splitDim; ++row ) 00429 { 00430 for( unsigned col=0; col<_splitDim; ++col ) 00431 { 00432 GeoExtent cellExtent( 00433 _extent.getSRS(), 00434 _extent.xMin() + xInterval*(double)col, 00435 _extent.yMin() + yInterval*(double)row, 00436 _extent.xMin() + xInterval*(double)(col+1), 00437 _extent.yMin() + yInterval*(double)(row+1) ); 00438 00439 this->addChild( 00440 new GeoCell(cellExtent, newRange, _maxObjects, _splitDim, _splitRangeFactor, _depth+1), 00441 0.0f, 00442 newRange ); 00443 } 00444 } 00445 } 00446 00447 bool 00448 GeoCell::removeObject( GeoObject* object ) 00449 { 00450 if ( object->_cell.get() == this ) 00451 { 00452 object->_cell = 0L; 00453 _objects.erase( findObject(_objects, object) ); 00454 adjustCount( -1 ); 00455 00456 // if we just fell beneath the threshold, pull one up from below. 00457 if ( _objects.size() == _minObjects-1 ) 00458 { 00459 //TODO. 00460 } 00461 00462 // TODO: rebalance, merge the tree, etc. 00463 return true; 00464 } 00465 else 00466 { 00467 for( unsigned i=0; i<getNumChildren(); ++i ) 00468 { 00469 if ( static_cast<GeoCell*>(getChild(i))->removeObject( object ) ) 00470 return true; 00471 } 00472 } 00473 return false; 00474 } 00475 00476 void 00477 GeoCell::merge() 00478 { 00479 //NYI //TODO 00480 } 00481 00482 bool 00483 GeoCell::reindexObject( GeoObject* object ) 00484 { 00485 GeoCell* owner = object->getGeoCell(); 00486 if ( owner ) 00487 { 00488 osg::Vec3d location; 00489 if ( object->getLocation(location) && !owner->_extent.contains(location.x(), location.y()) ) 00490 { 00491 // first remove from its current cell 00492 owner->removeObject( object ); 00493 00494 GeoCell* cell = dynamic_cast<GeoCell*>( owner->getParent(0) ); 00495 while( cell ) 00496 { 00497 if ( cell->getExtent().contains(location.x(), location.y()) ) 00498 { 00499 if ( cell->insertObject( object ) ) 00500 return true; 00501 } 00502 cell = dynamic_cast<GeoCell*>( cell->getParent(0) ); 00503 } 00504 } 00505 00506 // no change 00507 return true; 00508 } 00509 else 00510 { 00511 return insertObject( object ); 00512 } 00513 } 00514 00515 #if 0 00516 bool 00517 GeoCell::reindex( GeoObject* object ) 00518 { 00519 osg::Vec3d location; 00520 if ( object->getLocation(location) && !_extent.contains(location.x(), location.y()) ) 00521 { 00522 // first remove from its current cell 00523 osg::ref_ptr<GeoCell> safeCell = object->_cell.get(); 00524 if ( safeCell.valid() ) 00525 { 00526 object->_cell = 0L; 00527 safeCell->_objects.erase( findObject(safeCell->_objects, object) ); 00528 //safeCell->_objects.erase( std::find( _objects.begin(), _objects.end(), std::make_pair(object->getPriority(),object) ) ); 00529 //safeCell->_objects.erase( std::find( safeCell->_objects.begin(), safeCell->_objects.end(), object ) ); 00530 safeCell->adjustCount( -1 ); 00531 //safeCell->removeObject( object ); 00532 } 00533 00534 GeoCell* cell = dynamic_cast<GeoCell*>( this->getParent(0) ); 00535 while( cell ) 00536 { 00537 if ( cell->getExtent().contains(location.x(), location.y()) ) 00538 { 00539 if ( cell->insertObject( object ) ) 00540 return true; 00541 } 00542 cell = dynamic_cast<GeoCell*>( cell->getParent(0) ); 00543 } 00544 } 00545 00546 return true; 00547 } 00548 #endif