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