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 <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 }