osgEarth 2.1.1

/home/cube/sources/osgearth/src/osgEarthFeatures/SubstituteModelFilter.cpp

Go to the documentation of this file.
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 <osgEarthFeatures/SubstituteModelFilter>
00020 #include <osgEarthSymbology/MeshConsolidator>
00021 #include <osgEarth/HTTPClient>
00022 #include <osgEarth/ECEF>
00023 #include <osg/AutoTransform>
00024 #include <osg/Drawable>
00025 #include <osg/Geode>
00026 #include <osg/MatrixTransform>
00027 #include <osg/NodeVisitor>
00028 #include <osgUtil/Optimizer>
00029 #include <list>
00030 #include <deque>
00031 
00032 #define LC "[SubstituteModelFilter] "
00033 
00034 using namespace osgEarth;
00035 using namespace osgEarth::Features;
00036 using namespace osgEarth::Symbology;
00037 
00038 //------------------------------------------------------------------------
00039 
00040 namespace
00041 {
00042     static osg::Node* s_defaultModel =0L;
00043 }
00044 
00045 //------------------------------------------------------------------------
00046 
00047 SubstituteModelFilter::SubstituteModelFilter( const Style& style ) :
00048 _style   ( style ),
00049 _cluster ( false ),
00050 _merge   ( true )
00051 {
00052     //NOP
00053 }
00054 
00055 bool
00056 SubstituteModelFilter::process(const FeatureList&           features,                               
00057                                const MarkerSymbol*          symbol,
00058                                Session*                     session,
00059                                osg::Group*                  attachPoint,
00060                                FilterContext&               context )
00061 {
00062     bool makeECEF = context.getSession()->getMapInfo().isGeocentric();
00063 
00064     // first, go through the features and build the model cache. Apply the model matrix' scale
00065     // factor to any AutoTransforms directly (cloning them as necessary)
00066     std::map< std::pair<URI, float>, osg::ref_ptr<osg::Node> > uniqueModels;
00067     //std::map< Feature*, osg::ref_ptr<osg::Node> > featureModels;
00068 
00069     StringExpression  uriEx   = *symbol->url();
00070     NumericExpression scaleEx = *symbol->scale();
00071 
00072     for( FeatureList::const_iterator f = features.begin(); f != features.end(); ++f )
00073     {
00074         Feature* input = f->get();
00075 
00076         // evaluate the marker URI expression:
00077         StringExpression uriEx = *symbol->url();
00078         URI markerURI( input->eval(uriEx), uriEx.uriContext() );
00079 
00080         // find the corresponding marker in the cache
00081         MarkerResource* marker = 0L;
00082         MarkerCache::Record rec = _markerCache.get( markerURI );
00083         if ( rec.valid() ) {
00084             marker = rec.value();
00085         }
00086         else {
00087             marker = new MarkerResource();
00088             marker->uri() = markerURI;
00089             _markerCache.insert( markerURI, marker );
00090         }
00091 
00092         // evalute the scale expression (if there is one)
00093         float scale = 1.0f;
00094         osg::Matrixd scaleMatrix;
00095 
00096         if ( symbol->scale().isSet() )
00097         {
00098             scale = input->eval( scaleEx );
00099             if ( scale == 0.0 )
00100                 scale = 1.0;
00101             scaleMatrix = osg::Matrix::scale( scale, scale, scale );
00102         }
00103 
00104         // how that we have a marker source, create a node for it
00105         std::pair<URI,float> key( markerURI, scale );
00106         osg::ref_ptr<osg::Node>& model = uniqueModels[key];
00107         if ( !model.valid() )
00108         {
00109             model = context.resourceCache()->getMarkerNode( marker );
00110 
00111             if ( scale != 1.0f && dynamic_cast<osg::AutoTransform*>( model.get() ) )
00112             {
00113                 // clone the old AutoTransform, set the new scale, and copy over its children.
00114                 osg::AutoTransform* oldAT = dynamic_cast<osg::AutoTransform*>(model.get());
00115                 osg::AutoTransform* newAT = osg::clone( oldAT );
00116 
00117                 // make a scaler and put it between the new AutoTransform and its kids
00118                 osg::MatrixTransform* scaler = new osg::MatrixTransform(osg::Matrix::scale(scale,scale,scale));
00119                 for( unsigned i=0; i<newAT->getNumChildren(); ++i )
00120                     scaler->addChild( newAT->getChild(0) );
00121                 newAT->removeChildren(0, newAT->getNumChildren());
00122                 newAT->addChild( scaler );
00123                 model = newAT;
00124             }
00125         }
00126 
00127         if ( model.valid() )
00128         {
00129             GeometryIterator gi( input->getGeometry(), false );
00130             while( gi.hasMore() )
00131             {
00132                 Geometry* geom = gi.next();
00133 
00134                 for( unsigned i=0; i<geom->size(); ++i )
00135                 {
00136                     osg::Matrixd mat;
00137 
00138                     osg::Vec3d point = (*geom)[i];
00139                     if ( makeECEF )
00140                     {
00141                         // the "rotation" element lets us re-orient the instance to ensure it's pointing up. We
00142                         // could take a shortcut and just use the current extent's local2world matrix for this,
00143                         // but if the tile is big enough the up vectors won't be quite right.
00144                         osg::Matrixd rotation;
00145                         ECEF::transformAndGetRotationMatrix( context.profile()->getSRS(), point, point, rotation );
00146                         mat = rotation * scaleMatrix * osg::Matrixd::translate( point ) * _world2local;
00147                     }
00148                     else
00149                     {
00150                         mat = scaleMatrix * osg::Matrixd::translate( point ) * _world2local;
00151                     }
00152 
00153                     osg::MatrixTransform* xform = new osg::MatrixTransform();
00154                     xform->setMatrix( mat );
00155 
00156                     xform->addChild( model.get() );
00157 
00158                     attachPoint->addChild( xform );
00159 
00160                     // name the feature if necessary
00161                     if ( !_featureNameExpr.empty() )
00162                     {
00163                         const std::string& name = input->eval( _featureNameExpr );
00164                         if ( !name.empty() )
00165                             xform->setName( name );
00166                     }
00167                 }
00168             }
00169         }
00170     }
00171 
00172     return true;
00173 }
00174 
00175 
00176 
00177 
00178 struct ClusterVisitor : public osg::NodeVisitor
00179 {
00180         ClusterVisitor( const FeatureList& features, const MarkerSymbol* symbol, FeaturesToNodeFilter* f2n, FilterContext& cx )
00181             : _features   ( features ),
00182               _symbol     ( symbol ),
00183               //_modelMatrix( modelMatrix ),
00184               _f2n        ( f2n ),
00185               _cx         ( cx ),
00186               osg::NodeVisitor( osg::NodeVisitor::TRAVERSE_ALL_CHILDREN )
00187         {
00188             //nop
00189         }
00190 
00191         void apply( osg::Geode& geode )
00192         {
00193             bool makeECEF = _cx.getSession()->getMapInfo().isGeocentric();
00194             const SpatialReference* srs = _cx.profile()->getSRS();
00195 
00196             NumericExpression scaleEx = *_symbol->scale();
00197             osg::Matrixd scaleMatrix;
00198 
00199             // save the geode's drawables..
00200             osg::Geode::DrawableList old_drawables = geode.getDrawableList();
00201 
00202             // ..and clear out the drawables list.
00203             geode.removeDrawables( 0, geode.getNumDrawables() );
00204 
00205             // foreach each drawable that was originally in the geode...
00206             for( osg::Geode::DrawableList::iterator i = old_drawables.begin(); i != old_drawables.end(); i++ )
00207             {
00208                 osg::Geometry* originalDrawable = dynamic_cast<osg::Geometry*>( i->get() );
00209                 if ( !originalDrawable )
00210                     continue;
00211 
00212                 // go through the list of input features...
00213                 for( FeatureList::const_iterator j = _features.begin(); j != _features.end(); j++ )
00214                 {
00215                     const Feature* feature = j->get();
00216 
00217                     if ( _symbol->scale().isSet() )
00218                     {
00219                         double scale = feature->eval( scaleEx );
00220                         scaleMatrix.makeScale( scale, scale, scale );
00221                     }
00222 
00223                     ConstGeometryIterator gi( feature->getGeometry(), false );
00224                     while( gi.hasMore() )
00225                     {
00226                         const Geometry* geom = gi.next();
00227 
00228                         for( Geometry::const_iterator k = geom->begin(); k != geom->end(); ++k )
00229                         {
00230                             osg::Vec3d   point = *k;
00231                             osg::Matrixd mat;
00232 
00233                             if ( makeECEF )
00234                             {
00235                                 osg::Matrixd rotation;
00236                                 ECEF::transformAndGetRotationMatrix( srs, point, point, rotation );
00237                                 mat = rotation * scaleMatrix * osg::Matrixd::translate(point) * _f2n->world2local();
00238                             }
00239                             else
00240                             {
00241                                 mat = scaleMatrix * osg::Matrixd::translate(point) * _f2n->world2local();
00242                             }
00243 
00244                             // clone the source drawable once for each input feature.
00245                             osg::ref_ptr<osg::Geometry> newDrawable = osg::clone( 
00246                                 originalDrawable, 
00247                                 osg::CopyOp::DEEP_COPY_ARRAYS | osg::CopyOp::DEEP_COPY_PRIMITIVES );
00248 
00249                             osg::Vec3Array* verts = dynamic_cast<osg::Vec3Array*>( newDrawable->getVertexArray() );
00250                             if ( verts )
00251                             {
00252                                 for( osg::Vec3Array::iterator v = verts->begin(); v != verts->end(); ++v )
00253                                 {
00254                                     (*v).set( (*v) * mat );
00255                                 }
00256                                 
00257                                 // add the new cloned, translated drawable back to the geode.
00258                                 geode.addDrawable( newDrawable.get() );
00259                             }
00260                         }
00261 
00262                     }
00263                 }
00264             }
00265 
00266             geode.dirtyBound();
00267 
00268             MeshConsolidator::run( geode );
00269 
00270             // merge the geometry. Not sure this is necessary
00271             osgUtil::Optimizer opt;
00272             opt.optimize( &geode, osgUtil::Optimizer::MERGE_GEOMETRY | osgUtil::Optimizer::SHARE_DUPLICATE_STATE );
00273             
00274             osg::NodeVisitor::apply( geode );
00275         }
00276 
00277     private:
00278         const FeatureList&    _features;
00279         FilterContext         _cx;
00280         const MarkerSymbol*   _symbol;
00281         //osg::Matrixd          _modelMatrix;
00282         FeaturesToNodeFilter* _f2n;
00283     };
00284 
00285 
00286 typedef std::map< osg::Node*, FeatureList > MarkerToFeatures;
00287 
00288 //clustering:
00289 //  troll the external model for geodes. for each geode, create a geode in the target
00290 //  model. then, for each geometry in that geode, replicate it once for each instance of
00291 //  the model in the feature batch and transform the actual verts to a local offset
00292 //  relative to the tile centroid. Finally, reassemble all the geodes and optimize. 
00293 //  hopefully stateset sharing etc will work out. we may need to strip out LODs too.
00294 bool
00295 SubstituteModelFilter::cluster(const FeatureList&           features,
00296                                const MarkerSymbol*          symbol, 
00297                                Session*                     session,
00298                                osg::Group*                  attachPoint,
00299                                FilterContext&               context )
00300 {    
00301     MarkerToFeatures markerToFeatures;
00302 
00303     // first, sort the features into buckets, each bucket corresponding to a
00304     // unique marker.
00305     for (FeatureList::const_iterator i = features.begin(); i != features.end(); ++i)
00306     {
00307         Feature* f = i->get();
00308 
00309         // resolve the URI for the marker:
00310         StringExpression uriEx( *symbol->url() );
00311         URI markerURI( f->eval( uriEx ), uriEx.uriContext() );
00312 
00313         // find and load the corresponding marker model. We're using the session-level
00314         // object store to cache models. This is thread-safe sine we are always going
00315         // to CLONE the model before using it.
00316         osg::Node* model = context.getSession()->getObject<osg::Node>( markerURI.full() );
00317         if ( !model )
00318         {
00319             osg::ref_ptr<MarkerResource> mres = new MarkerResource();
00320             mres->uri() = markerURI;
00321             model = mres->createNode();
00322             if ( model )
00323             {
00324                 context.getSession()->putObject( markerURI.full(), model );
00325             }
00326         }
00327 
00328         if ( model )
00329         {
00330             MarkerToFeatures::iterator itr = markerToFeatures.find( model );
00331             if (itr == markerToFeatures.end())
00332                 markerToFeatures[ model ].push_back( f );
00333             else
00334                 itr->second.push_back( f );
00335         }
00336     }
00337 
00338     //For each model, cluster the features that use that marker
00339     for (MarkerToFeatures::iterator i = markerToFeatures.begin(); i != markerToFeatures.end(); ++i)
00340     {
00341         osg::Node* prototype = i->first;
00342 
00343         // we're using the Session cache since we know we'll be cloning.
00344         if ( prototype )
00345         {
00346             osg::Node* clone = osg::clone( prototype, osg::CopyOp::DEEP_COPY_ALL );
00347 
00348             // ..and apply the clustering to the copy.
00349             ClusterVisitor cv( i->second, symbol, this, context );
00350             clone->accept( cv );
00351 
00352             attachPoint->addChild( clone );
00353         }
00354     }
00355 
00356     return true;
00357 }
00358 
00359 osg::Node*
00360 SubstituteModelFilter::push(FeatureList& features, FilterContext& context)
00361 {
00362     if ( !isSupported() ) {
00363         OE_WARN << "SubstituteModelFilter support not enabled" << std::endl;
00364         return 0L;
00365     }
00366 
00367     if ( _style.empty() ) {
00368         OE_WARN << LC << "Empty style; cannot process features" << std::endl;
00369         return 0L;
00370     }
00371 
00372     const MarkerSymbol* symbol = _style.getSymbol<MarkerSymbol>();
00373     if ( !symbol ) {
00374         OE_WARN << LC << "No MarkerSymbol found in style; cannot process feautres" << std::endl;
00375         return 0L;
00376     }
00377 
00378     FilterContext newContext( context );
00379 
00380     computeLocalizers( context );
00381 
00382     osg::Group* group = createDelocalizeGroup();
00383 
00384     bool ok = true;
00385 
00386     if ( _cluster )
00387     {
00388         ok = cluster( features, symbol, context.getSession(), group, newContext );
00389     }
00390 
00391     else
00392     {
00393         process( features, symbol, context.getSession(), group, newContext );
00394 
00395 #if 0
00396         // speeds things up a bit, at the expense of creating tons of geometry..
00397 
00398         // this optimizer pass will remove all the MatrixTransform nodes that we
00399         // used to offset each instance
00400         osgUtil::Optimizer optimizer;
00401         optimizer.optimize( group, osgUtil::Optimizer::FLATTEN_STATIC_TRANSFORMS_DUPLICATING_SHARED_SUBGRAPHS );
00402 #endif
00403 
00404     }
00405 
00406     return group;
00407 }
 All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator Friends Defines