osgEarth 2.1.1

/home/cube/sources/osgearth/src/osgEarthFeatures/BuildGeometryFilter.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/BuildGeometryFilter>
00020 #include <osgEarthSymbology/TextSymbol>
00021 #include <osgEarthSymbology/PointSymbol>
00022 #include <osgEarthSymbology/LineSymbol>
00023 #include <osgEarthSymbology/PolygonSymbol>
00024 #include <osgEarthSymbology/MeshSubdivider>
00025 #include <osgEarthSymbology/MeshConsolidator>
00026 #include <osgEarth/ECEF>
00027 #include <osg/Geode>
00028 #include <osg/Geometry>
00029 #include <osg/LineWidth>
00030 #include <osg/LineStipple>
00031 #include <osg/Point>
00032 #include <osg/Depth>
00033 #include <osg/PolygonOffset>
00034 #include <osg/MatrixTransform>
00035 #include <osg/ClusterCullingCallback>
00036 #include <osgText/Text>
00037 #include <osgUtil/Tessellator>
00038 #include <osgUtil/Optimizer>
00039 #include <osgDB/WriteFile>
00040 #include <osg/Version>
00041 
00042 #define LC "[BuildGeometryFilter] "
00043 
00044 using namespace osgEarth;
00045 using namespace osgEarth::Features;
00046 using namespace osgEarth::Symbology;
00047 
00048 BuildGeometryFilter::BuildGeometryFilter( const Style& style ) :
00049 _style        ( style ),
00050 _maxAngle_deg ( 5.0 ),
00051 _geoInterp    ( GEOINTERP_RHUMB_LINE ),
00052 _mergeGeometry( false )
00053 {
00054     reset();
00055 }
00056 
00057 void
00058 BuildGeometryFilter::reset()
00059 {
00060     _result = 0L;
00061     _geode = new osg::Geode();
00062     _hasLines = false;
00063     _hasPoints = false;
00064     _hasPolygons = false;
00065 }
00066 
00067 bool
00068 BuildGeometryFilter::process( FeatureList& features, const FilterContext& context )
00069 {
00070     bool makeECEF = context.getSession()->getMapInfo().isGeocentric();
00071     const SpatialReference* srs = context.extent()->getSRS();
00072 
00073     for( FeatureList::iterator f = features.begin(); f != features.end(); ++f )
00074     {
00075         Feature* input = f->get();
00076 
00077         GeometryIterator parts( input->getGeometry(), false );
00078         while( parts.hasMore() )
00079         {
00080             Geometry* part = parts.next();
00081             
00082             osg::PrimitiveSet::Mode primMode = osg::PrimitiveSet::POINTS;
00083 
00084             const Style& myStyle = input->style().isSet() ? *input->style() : _style;
00085 
00086             osg::Vec4f color = osg::Vec4(1,1,1,1);
00087             bool tessellatePolys = true;
00088 
00089             bool setWidth = input->style().isSet(); // otherwise it will be set globally, we assume
00090             float width = 1.0f;
00091 
00092             switch( part->getType() )
00093             {
00094             case Geometry::TYPE_POINTSET:
00095                 {
00096                     _hasPoints = true;
00097                     primMode = osg::PrimitiveSet::POINTS;
00098                     const PointSymbol* point = myStyle.getSymbol<PointSymbol>();
00099                     if (point)
00100                     {
00101                         color = point->fill()->color();
00102                     }
00103                 }
00104                 break;
00105 
00106             case Geometry::TYPE_LINESTRING:
00107                 {
00108                     _hasLines = true;
00109                     primMode = osg::PrimitiveSet::LINE_STRIP;
00110                     const LineSymbol* lineSymbol = myStyle.getSymbol<LineSymbol>();
00111                     if (lineSymbol)
00112                     {
00113                         color = lineSymbol->stroke()->color();
00114                         width = lineSymbol->stroke()->width().isSet() ? *lineSymbol->stroke()->width() : 1.0f;
00115                     }
00116                 }
00117                 break;
00118 
00119             case Geometry::TYPE_RING:
00120                 {
00121                     _hasLines = true;
00122                     primMode = osg::PrimitiveSet::LINE_LOOP;
00123                     const LineSymbol* lineSymbol = myStyle.getSymbol<LineSymbol>();
00124                     if (lineSymbol)
00125                     {
00126                         color = lineSymbol->stroke()->color();
00127                         width = lineSymbol->stroke()->width().isSet() ? *lineSymbol->stroke()->width() : 1.0f;
00128                     }
00129                 }
00130                 break;
00131 
00132             case Geometry::TYPE_POLYGON:
00133                 {
00134                     primMode = osg::PrimitiveSet::LINE_LOOP; // loop will tessellate into polys
00135                     const PolygonSymbol* poly = myStyle.getSymbol<PolygonSymbol>();
00136                     if (poly)
00137                     {
00138                         _hasPolygons = true;
00139                         color = poly->fill()->color();
00140                     }
00141                     else
00142                     {
00143                         // if we have a line symbol and no polygon symbol, draw as an outline.
00144                         _hasLines = true;
00145                         const LineSymbol* line = myStyle.getSymbol<LineSymbol>();
00146                         if ( line )
00147                         {
00148                             color = line->stroke()->color();
00149                             width = line->stroke()->width().isSet() ? *line->stroke()->width() : 1.0f;
00150                             tessellatePolys = false;
00151                         }
00152                     }
00153                 }
00154                 break;
00155             }
00156             
00157             osg::Geometry* osgGeom = new osg::Geometry();
00158 
00159             if ( _featureNameExpr.isSet() )
00160             {
00161                 const std::string& name = input->eval( _featureNameExpr.mutable_value() );
00162                 osgGeom->setName( name );
00163             }
00164 
00165             // NOTE: benchmarking reveals VBOs to be much slower (for static data, at least)
00166             //osgGeom->setUseVertexBufferObjects( true );
00167             //osgGeom->setUseDisplayList( false );
00168 
00169             if ( setWidth && width != 1.0f )
00170             {
00171                 osgGeom->getOrCreateStateSet()->setAttributeAndModes(
00172                     new osg::LineWidth( width ), osg::StateAttribute::ON );
00173             }
00174 
00175             if (_hasLines)
00176             {
00177                 const LineSymbol* line = myStyle.getSymbol<LineSymbol>();
00178                 if (line && line->stroke().isSet() && line->stroke()->stipple().isSet())
00179                 {
00180                     osg::LineStipple* lineStipple = new osg::LineStipple;
00181                     lineStipple->setPattern( *line->stroke()->stipple() );            
00182                     osgGeom->getOrCreateStateSet()->setAttributeAndModes( lineStipple, osg::StateAttribute::ON );
00183                 }
00184             }
00185             
00186             if (part->getType() == Geometry::TYPE_POLYGON && static_cast<Polygon*>(part)->getHoles().size() > 0 )
00187             {
00188                 Polygon* poly = static_cast<Polygon*>(part);
00189                 int totalPoints = poly->getTotalPointCount();
00190                 osg::Vec3Array* allPoints; // = new osg::Vec3Array( totalPoints );
00191 
00192                 if ( makeECEF )
00193                 {
00194                     allPoints = new osg::Vec3Array();
00195                     ECEF::transformAndLocalize( part->asVector(), allPoints, srs, _world2local );
00196                 }
00197                 else
00198                 {
00199                     allPoints = new osg::Vec3Array( totalPoints );
00200                     std::copy( part->begin(), part->end(), allPoints->begin() );
00201                 }
00202                 osgGeom->addPrimitiveSet( new osg::DrawArrays( primMode, 0, part->size() ) );
00203 
00204                 int offset = part->size();
00205 
00206                 for( RingCollection::const_iterator h = poly->getHoles().begin(); h != poly->getHoles().end(); ++h )
00207                 {
00208                     Geometry* hole = h->get();
00209                     if ( hole->isValid() )
00210                     {
00211                         if ( makeECEF )
00212                             ECEF::transformAndLocalize( hole->asVector(), allPoints, srs, _world2local );
00213                         else
00214                             std::copy( hole->begin(), hole->end(), allPoints->begin() + offset );
00215 
00216                         osgGeom->addPrimitiveSet( new osg::DrawArrays( primMode, offset, hole->size() ) );
00217                         offset += hole->size();
00218                     }
00219                 }
00220                 osgGeom->setVertexArray( allPoints );
00221             }
00222             else
00223             {
00224                 if ( makeECEF )
00225                 {
00226                     osg::Vec3Array* newPart = new osg::Vec3Array();
00227                     ECEF::transformAndLocalize( part->asVector(), newPart, srs, _world2local );
00228                     osgGeom->setVertexArray( newPart );
00229                 }
00230                 else
00231                 {
00232                     osgGeom->setVertexArray( part->toVec3Array() );
00233                 }
00234                 osgGeom->addPrimitiveSet( new osg::DrawArrays( primMode, 0, part->size() ) );
00235             }
00236 
00237             // tessellate all polygon geometries. Tessellating each geometry separately
00238             // with TESS_TYPE_GEOMETRY is much faster than doing the whole bunch together
00239             // using TESS_TYPE_DRAWABLE.
00240 
00241             if ( part->getType() == Geometry::TYPE_POLYGON && tessellatePolys )
00242             {
00243                 osgUtil::Tessellator tess;
00244                 //tess.setTessellationType( osgUtil::Tessellator::TESS_TYPE_DRAWABLE );
00245                 //tess.setWindingType( osgUtil::Tessellator::TESS_WINDING_ODD );
00246                 tess.setTessellationType( osgUtil::Tessellator::TESS_TYPE_GEOMETRY );
00247                 tess.setWindingType( osgUtil::Tessellator::TESS_WINDING_POSITIVE );
00248 
00249                 tess.retessellatePolygons( *osgGeom );
00250 
00251                 // the tessellator results in a collection of trifans, strips, etc. This step will
00252                 // consolidate those into one (or more if necessary) GL_TRIANGLES primitive.
00253                 //NOTE: this now happens elsewhere 
00254                 //MeshConsolidator::run( *osgGeom );
00255 
00256                 // mark this geometry as DYNAMIC because otherwise the OSG optimizer will destroy it.
00257                 //osgGeom->setDataVariance( osg::Object::DYNAMIC );
00258             }
00259 
00260             if ( context.getSession()->getMapInfo().isGeocentric() && part->getType() != Geometry::TYPE_POINTSET )
00261 //            if ( context.isGeocentric() && part->getType() != Geometry::TYPE_POINTSET )
00262             {
00263                 double threshold = osg::DegreesToRadians( *_maxAngle_deg );
00264 
00265                 MeshSubdivider ms( _world2local, _local2world ); //context.referenceFrame(), context.inverseReferenceFrame() );
00266                 //ms.setMaxElementsPerEBO( INT_MAX );
00267                 if ( input->geoInterp().isSet() )
00268                     ms.run( *osgGeom, threshold, *input->geoInterp() );
00269                 else
00270                     ms.run( *osgGeom, threshold, *_geoInterp );
00271             }
00272 
00273             // NOTE! per-vertex colors makes the optimizer destroy the geometry....
00274             osg::Vec4Array* colors = new osg::Vec4Array(1);
00275             (*colors)[0] = color;
00276             osgGeom->setColorArray( colors );
00277             osgGeom->setColorBinding( osg::Geometry::BIND_OVERALL );
00278 
00279             // add the part to the geode.
00280             _geode->addDrawable( osgGeom );
00281         }
00282     }
00283     
00284     return true;
00285 }
00286 
00287 osg::Node*
00288 BuildGeometryFilter::push( FeatureList& input, FilterContext& context )
00289 {
00290     reset();
00291 
00292     computeLocalizers( context );
00293 
00294     bool ok = process( input, context );
00295 
00296     // convert all geom to triangles and consolidate into minimal set of Geometries
00297     if ( !_featureNameExpr.isSet() )
00298     {
00299         MeshConsolidator::run( *_geode.get() );
00300     }
00301 
00302     osg::Node* result = 0L;
00303 
00304     if ( ok )
00305     {
00306         if ( !_style.empty() && _geode.valid() )
00307         {
00308             // could optimize this to only happen is lines or points were created ..
00309             const LineSymbol* lineSymbol = _style.getSymbol<LineSymbol>();
00310             float size = 1.0;
00311             if (lineSymbol)
00312                 size = lineSymbol->stroke()->width().value();
00313 
00314             _geode->getOrCreateStateSet()->setAttribute( new osg::Point(size), osg::StateAttribute::ON );
00315             _geode->getOrCreateStateSet()->setAttribute( new osg::LineWidth(size), osg::StateAttribute::ON );
00316 
00317             const PointSymbol* pointSymbol = _style.getSymbol<PointSymbol>();
00318             if ( pointSymbol && pointSymbol->size().isSet() )
00319                 _geode->getOrCreateStateSet()->setAttribute( 
00320                     new osg::Point( *pointSymbol->size() ), osg::StateAttribute::ON );
00321         }
00322 
00323         result = delocalize( _geode.release() );
00324     }
00325     else
00326     {
00327         result = 0L;
00328     }
00329 
00330     return result;
00331 }
 All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator Friends Defines