osgEarth 2.1.1

/home/cube/sources/osgearth/src/osgEarthFeatures/ExtrudeGeometryFilter.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/ExtrudeGeometryFilter>
00020 #include <osgEarthSymbology/MeshSubdivider>
00021 #include <osgEarthSymbology/MeshConsolidator>
00022 #include <osgEarth/ECEF>
00023 #include <osg/ClusterCullingCallback>
00024 #include <osg/Geode>
00025 #include <osg/Geometry>
00026 #include <osg/MatrixTransform>
00027 #include <osgUtil/Tessellator>
00028 #include <osgUtil/Optimizer>
00029 #include <osgUtil/SmoothingVisitor>
00030 #include <osg/Version>
00031 #include <osg/LineWidth>
00032 #include <osg/PolygonOffset>
00033 #include <osgEarth/Version>
00034 
00035 #define LC "[ExtrudeGeometryFilter] "
00036 
00037 using namespace osgEarth;
00038 using namespace osgEarth::Features;
00039 using namespace osgEarth::Symbology;
00040 
00041 namespace
00042 {
00043     // Calculates the rotation angle of a shape. This conanically applies to
00044     // buildings; it finds the longest edge and compares its angle to the
00045     // x-axis to determine a rotation value. This method is used so we can 
00046     // properly rotate textures for rooftop application.
00047     float getApparentRotation( const Geometry* geom )
00048     {
00049         Segment n;
00050         double  maxLen2 = 0.0;
00051         ConstSegmentIterator i( geom, true );
00052         while( i.hasMore() )
00053         {
00054             Segment s = i.next();
00055             double len2 = (s.second - s.first).length2();
00056             if ( len2 > maxLen2 ) 
00057             {
00058                 maxLen2 = len2;
00059                 n = s;
00060             }
00061         }
00062 
00063         const osg::Vec3d& p1 = n.first.x() < n.second.x() ? n.first : n.second;
00064         const osg::Vec3d& p2 = n.first.x() < n.second.x() ? n.second : n.first;
00065 
00066         return atan2( p2.x()-p1.x(), p2.y()-p1.y() );
00067     }
00068 }
00069 
00070 //------------------------------------------------------------------------
00071 
00072 ExtrudeGeometryFilter::ExtrudeGeometryFilter() :
00073 _maxAngle_deg       ( 5.0 ),
00074 _mergeGeometry      ( true ),
00075 _wallAngleThresh_deg( 60.0 ),
00076 _styleDirty         ( true )
00077 {
00078     //NOP
00079 }
00080 
00081 void
00082 ExtrudeGeometryFilter::setStyle( const Style& style )
00083 {
00084     _style      = style;
00085     _styleDirty = true;
00086 }
00087 
00088 void
00089 ExtrudeGeometryFilter::reset( const FilterContext& context )
00090 {
00091     _cosWallAngleThresh = cos( _wallAngleThresh_deg );
00092     _geodes.clear();
00093 
00094     if ( _styleDirty )
00095     {
00096         const StyleSheet* sheet = context.getSession()->styles();
00097 
00098         _wallSkinSymbol    = 0L;
00099         _wallPolygonSymbol = 0L;
00100         _roofSkinSymbol    = 0L;
00101         _roofPolygonSymbol = 0L;
00102         _extrusionSymbol   = 0L;
00103         _outlineSymbol     = 0L;
00104 
00105         _extrusionSymbol = _style.get<ExtrusionSymbol>();
00106         if ( _extrusionSymbol.valid() )
00107         {
00108             // make a copy of the height expression so we can use it:
00109             if ( _extrusionSymbol->heightExpression().isSet() )
00110             {
00111                 _heightExpr = *_extrusionSymbol->heightExpression();
00112             }
00113 
00114             // account for a "height" value that is relative to ZERO (MSL/HAE)
00115             AltitudeSymbol* alt = _style.get<AltitudeSymbol>();
00116             if ( alt && !_extrusionSymbol->heightExpression().isSet() )
00117             {
00118                 if (alt->clamping() == AltitudeSymbol::CLAMP_ABSOLUTE ||
00119                     alt->clamping() == AltitudeSymbol::CLAMP_RELATIVE_TO_TERRAIN )
00120                 {
00121                     _heightExpr = NumericExpression( "0-[__max_hat]" );
00122                 }
00123             }
00124             
00125             // attempt to extract the wall symbols:
00126             if ( _extrusionSymbol->wallStyleName().isSet() && sheet != 0L )
00127             {
00128                 const Style* wallStyle = sheet->getStyle( *_extrusionSymbol->wallStyleName(), false );
00129                 if ( wallStyle )
00130                 {
00131                     _wallSkinSymbol = wallStyle->get<SkinSymbol>();
00132                     _wallPolygonSymbol = wallStyle->get<PolygonSymbol>();
00133                 }
00134             }
00135 
00136             // attempt to extract the rooftop symbols:
00137             if ( _extrusionSymbol->roofStyleName().isSet() && sheet != 0L )
00138             {
00139                 const Style* roofStyle = sheet->getStyle( *_extrusionSymbol->roofStyleName(), false );
00140                 if ( roofStyle )
00141                 {
00142                     _roofSkinSymbol = roofStyle->get<SkinSymbol>();
00143                     _roofPolygonSymbol = roofStyle->get<PolygonSymbol>();
00144                 }
00145             }
00146 
00147             // if there's a line symbol, use it to outline the extruded data.
00148             _outlineSymbol = _style.get<LineSymbol>();
00149         }
00150 
00151         // backup plan for skin symbols:
00152         const SkinSymbol* skin = _style.get<SkinSymbol>();
00153         if ( skin )
00154         {
00155             if ( !_wallSkinSymbol.valid() )
00156                 _wallSkinSymbol = skin;
00157             if ( !_roofSkinSymbol.valid() )
00158                 _roofSkinSymbol = skin;
00159         }
00160 
00161         // backup plan for poly symbols:
00162         const PolygonSymbol* poly = _style.get<PolygonSymbol>();
00163         if ( poly )
00164         {
00165             if ( !_wallPolygonSymbol.valid() )
00166                 _wallPolygonSymbol = poly;
00167             if ( !_roofPolygonSymbol.valid() )
00168                 _roofPolygonSymbol = poly;
00169         }
00170 
00171         _styleDirty = false;
00172     }
00173 }
00174 
00175 bool
00176 ExtrudeGeometryFilter::extrudeGeometry(const Geometry*         input,
00177                                        double                  height,
00178                                        double                  heightOffset,
00179                                        bool                    flatten,
00180                                        osg::Geometry*          walls,
00181                                        osg::Geometry*          roof,
00182                                        osg::Geometry*          base,
00183                                        osg::Geometry*          outline,
00184                                        const osg::Vec4&        wallColor,
00185                                        const osg::Vec4&        roofColor,
00186                                        const osg::Vec4&        outlineColor,
00187                                        const SkinResource*     wallSkin,
00188                                        const SkinResource*     roofSkin,
00189                                        FilterContext&          cx )
00190 {
00191     //todo: establish reference frame for going to geocentric. This will ultimately
00192     // passed in to the function.
00193     const SpatialReference* srs = cx.extent()->getSRS();
00194 
00195     // whether to convert the final geometry to localized ECEF
00196     bool makeECEF = cx.getSession()->getMapInfo().isGeocentric();
00197 
00198     bool made_geom = false;
00199 
00200     double tex_width_m   = wallSkin ? *wallSkin->imageWidth() : 1.0;
00201     double tex_height_m  = wallSkin ? *wallSkin->imageHeight() : 1.0;
00202     bool   tex_repeats_y = wallSkin ? *wallSkin->isTiled() : false;
00203     bool   useColor      = !wallSkin || wallSkin->texEnvMode() != osg::TexEnv::DECAL;
00204 
00205     bool isPolygon = input->getComponentType() == Geometry::TYPE_POLYGON;
00206 
00207     unsigned pointCount = input->getTotalPointCount();
00208     
00209     // If we are extruding a polygon, and applying a wall texture, we need an extra
00210     // point in the geometry in order to close the polygon and generate a unique
00211     // texture coordinate for that final point.
00212     bool isSkinnedPolygon = isPolygon && wallSkin != 0L;
00213 
00214     // Total number of verts. Add 2 to close a polygon (necessary so the first and last
00215     // points can have unique texture coordinates)
00216     unsigned numWallVerts = 2 * pointCount + (isSkinnedPolygon? (2 * input->getNumGeometries()) : 0);
00217 
00218     // create all the OSG geometry components
00219     osg::Vec3Array* verts = new osg::Vec3Array( numWallVerts );
00220     walls->setVertexArray( verts );
00221 
00222     osg::Vec2Array* wallTexcoords = 0L;
00223     if ( wallSkin )
00224     { 
00225         wallTexcoords = new osg::Vec2Array( numWallVerts );
00226         walls->setTexCoordArray( 0, wallTexcoords );
00227     }
00228 
00229     if ( useColor )
00230     {
00231         // per-vertex colors are necessary if we are going to use the MeshConsolidator -gw
00232         osg::Vec4Array* colors = new osg::Vec4Array();
00233         colors->reserve( numWallVerts );
00234         colors->assign( numWallVerts, wallColor );
00235         walls->setColorArray( colors );
00236         walls->setColorBinding( osg::Geometry::BIND_PER_VERTEX );
00237         //osg::Vec4Array* colors = new osg::Vec4Array( 1 );
00238         //(*colors)[0] = wallColor;
00239         //walls->setColorArray( colors );
00240         //walls->setColorBinding( osg::Geometry::BIND_OVERALL );
00241     }
00242 
00243     // set up rooftop tessellation and texturing, if necessary:
00244     osg::Vec3Array* roofVerts     = 0L;
00245     osg::Vec2Array* roofTexcoords = 0L;
00246     float           roofRotation  = 0.0f;
00247     Bounds          roofBounds;
00248     float           sinR, cosR;
00249     double          roofTexSpanX, roofTexSpanY;
00250     osg::ref_ptr<const SpatialReference> roofProjSRS;
00251 
00252     if ( roof )
00253     {
00254         roofVerts = new osg::Vec3Array( pointCount );
00255         roof->setVertexArray( roofVerts );
00256 
00257         // per-vertex colors are necessary if we are going to use the MeshConsolidator -gw
00258         osg::Vec4Array* roofColors = new osg::Vec4Array();
00259         roofColors->reserve( pointCount );
00260         roofColors->assign( pointCount, roofColor );
00261         roof->setColorArray( roofColors );
00262         roof->setColorBinding( osg::Geometry::BIND_PER_VERTEX );
00263         //osg::Vec4Array* roofColors = new osg::Vec4Array( 1 );
00264         //(*roofColors)[0] = roofColor;
00265         //roof->setColorArray( roofColors );
00266         //roof->setColorBinding( osg::Geometry::BIND_OVERALL );
00267 
00268         if ( roofSkin )
00269         {
00270             roofTexcoords = new osg::Vec2Array( pointCount );
00271             roof->setTexCoordArray( 0, roofTexcoords );
00272 
00273             // Get the orientation of the geometry. This is a hueristic that will help 
00274             // us align the roof skin texture properly. TODO: make this optional? It makes
00275             // sense for buildings and such, but perhaps not for all extruded shapes.
00276             roofRotation = getApparentRotation( input );
00277 
00278             roofBounds = input->getBounds();
00279 
00280             // if our data is lat/long, we need to reproject the geometry and the bounds into a projected
00281             // coordinate system in order to properly generate tex coords.
00282             if ( srs->isGeographic() )
00283             {
00284                 osg::Vec2d geogCenter = roofBounds.center2d();
00285                 roofProjSRS = srs->createUTMFromLongitude( Angular(geogCenter.x()) );
00286                 roofBounds.transform( srs, roofProjSRS.get() );
00287                 osg::ref_ptr<Geometry> projectedInput = input->clone();
00288                 srs->transformPoints( roofProjSRS.get(), projectedInput->asVector() );
00289                 roofRotation = getApparentRotation( projectedInput.get() );
00290             }
00291             else
00292             {
00293                 roofRotation = getApparentRotation( input );
00294             }
00295             
00296             sinR = sin(roofRotation);
00297             cosR = cos(roofRotation);
00298 
00299             if ( !roofSkin->isTiled().value() )
00300             {
00301                 //note: doesn't really work
00302                 roofTexSpanX = cosR*roofBounds.width() - sinR*roofBounds.height();
00303                 roofTexSpanY = sinR*roofBounds.width() + cosR*roofBounds.height();
00304             }
00305             else
00306             {
00307                 roofTexSpanX = roofSkin->imageWidth().isSet() ? *roofSkin->imageWidth() : roofSkin->imageHeight().isSet() ? *roofSkin->imageHeight() : 10.0;
00308                 if ( roofTexSpanX <= 0.0 ) roofTexSpanX = 10.0;
00309                 roofTexSpanY = roofSkin->imageHeight().isSet() ? *roofSkin->imageHeight() : roofSkin->imageWidth().isSet() ? *roofSkin->imageWidth() : 10.0;
00310                 if ( roofTexSpanY <= 0.0 ) roofTexSpanY = 10.0;
00311             }
00312         }
00313     }
00314 
00315     osg::Vec3Array* baseVerts = NULL;
00316     if ( base )
00317     {
00318         baseVerts = new osg::Vec3Array( pointCount );
00319         base->setVertexArray( baseVerts );
00320     }
00321 
00322     osg::Vec3Array* outlineVerts = 0L;
00323     osg::Vec3Array* outlineNormals = 0L;
00324     if ( outline )
00325     {
00326         outlineVerts = new osg::Vec3Array( numWallVerts );
00327         outline->setVertexArray( outlineVerts );
00328 
00329         osg::Vec4Array* outlineColors = new osg::Vec4Array();
00330         outlineColors->reserve( numWallVerts );
00331         outlineColors->assign( numWallVerts, outlineColor );
00332         outline->setColorArray( outlineColors );
00333         outline->setColorBinding( osg::Geometry::BIND_PER_VERTEX );
00334 
00335         // cop out, just point all the outline normals up. fix this later.
00336         outlineNormals = new osg::Vec3Array();
00337         outlineNormals->reserve( numWallVerts );
00338         outlineNormals->assign( numWallVerts, osg::Vec3(0,0,1) );
00339         outline->setNormalArray( outlineNormals );
00340     }
00341 
00342     unsigned wallVertPtr    = 0;
00343     unsigned roofVertPtr    = 0;
00344     unsigned baseVertPtr    = 0;
00345 
00346     double     targetLen = -DBL_MAX;
00347     osg::Vec3d minLoc(DBL_MAX, DBL_MAX, DBL_MAX);
00348     double     minLoc_len = DBL_MAX;
00349     osg::Vec3d maxLoc(0,0,0);
00350     double     maxLoc_len = 0;
00351 
00352     // Initial pass over the geometry does two things:
00353     // 1: Calculate the minimum Z across all parts.
00354     // 2: Establish a "target length" for extrusion
00355 
00356     double absHeight = fabs(height);
00357 
00358     ConstGeometryIterator zfinder( input );
00359     while( zfinder.hasMore() )
00360     {
00361         const Geometry* geom = zfinder.next();
00362         for( Geometry::const_iterator m = geom->begin(); m != geom->end(); ++m )
00363         {
00364             osg::Vec3d m_point = *m;
00365 
00366             if ( m_point.z() + absHeight > targetLen )
00367                 targetLen = m_point.z() + absHeight;
00368 
00369             if (m_point.z() < minLoc.z())
00370                 minLoc = m_point;
00371 
00372             if (m_point.z() > maxLoc.z())
00373                 maxLoc = m_point;
00374         }
00375     }
00376 
00377     // apply the height offsets
00378     height    -= heightOffset;
00379     targetLen -= heightOffset;
00380 
00381     // now generate the extruded geometry.
00382     ConstGeometryIterator iter( input );
00383     while( iter.hasMore() )
00384     {
00385         const Geometry* part = iter.next();
00386 
00387         double tex_height_m_adj = tex_height_m;
00388 
00389         unsigned wallPartPtr = wallVertPtr;
00390         unsigned roofPartPtr = roofVertPtr;
00391         unsigned basePartPtr = baseVertPtr;
00392         double   partLen     = 0.0;
00393         double   maxHeight   = 0.0;
00394 
00395         maxHeight = targetLen - minLoc.z();
00396 
00397         // Adjust the texture height so it is a multiple of the maximum height
00398         double div = osg::round(maxHeight / tex_height_m);
00399         if (div == 0) div = 1; //Prevent divide by zero
00400         tex_height_m_adj = maxHeight / div;
00401 
00402         osg::DrawElementsUInt* idx = new osg::DrawElementsUInt( GL_TRIANGLES );
00403 
00404         for( Geometry::const_iterator m = part->begin(); m != part->end(); ++m )
00405         {
00406             osg::Vec3d basePt = *m;
00407             osg::Vec3d roofPt;
00408 
00409             if ( height >= 0 )
00410             {
00411                 if ( flatten )
00412                     roofPt.set( basePt.x(), basePt.y(), targetLen );
00413                 else
00414                     roofPt.set( basePt.x(), basePt.y(), basePt.z() + height );
00415             }
00416             else // height < 0
00417             {
00418                 roofPt = *m;
00419                 basePt.z() += height;
00420             }
00421 
00422             // add to the approprate vertex lists:
00423             int p = wallVertPtr;
00424 
00425             // figure out the rooftop texture coordinates before doing any
00426             // transformations:
00427             if ( roofSkin )
00428             {
00429                 double xr, yr;
00430 
00431                 if ( srs->isGeographic() )
00432                 {
00433                     osg::Vec3d projRoofPt;
00434                     srs->transform( roofPt, roofProjSRS.get(), projRoofPt );
00435                     xr = (projRoofPt.x() - roofBounds.xMin());
00436                     yr = (projRoofPt.y() - roofBounds.yMin());
00437                 }
00438                 else
00439                 {
00440                     xr = (roofPt.x() - roofBounds.xMin());
00441                     yr = (roofPt.y() - roofBounds.yMin());
00442                 }
00443 
00444                 float u = (cosR*xr - sinR*yr) / roofTexSpanX;
00445                 float v = (sinR*xr + cosR*yr) / roofTexSpanY;
00446 
00447                 (*roofTexcoords)[roofVertPtr].set( u, v );
00448             }            
00449 
00450             if ( makeECEF )
00451             {
00452                 ECEF::transformAndLocalize( basePt, basePt, srs, _world2local );
00453                 ECEF::transformAndLocalize( roofPt, roofPt, srs, _world2local );
00454             }
00455 
00456             if ( base )
00457                 (*baseVerts)[baseVertPtr] = basePt;
00458             if ( roof )
00459                 (*roofVerts)[roofVertPtr] = roofPt;
00460 
00461             baseVertPtr++;
00462             roofVertPtr++;
00463 
00464             (*verts)[p] = roofPt;
00465             (*verts)[p+1] = basePt;
00466 
00467             if ( outline )
00468             {
00469                 (*outlineVerts)[p] = roofPt;
00470                 (*outlineVerts)[p+1] = basePt;
00471             }
00472             
00473             partLen += wallVertPtr > wallPartPtr ? ((*verts)[p] - (*verts)[p-2]).length() : 0.0;
00474             double h = tex_repeats_y ? -((*verts)[p] - (*verts)[p+1]).length() : -tex_height_m_adj;
00475 
00476             if ( wallSkin )
00477             {
00478                 (*wallTexcoords)[p].set( partLen/tex_width_m, 0.0f );
00479                 (*wallTexcoords)[p+1].set( partLen/tex_width_m, h/tex_height_m_adj );
00480             }
00481 
00482             // form the 2 triangles
00483             if ( (m+1) == part->end() )
00484             {
00485                 if ( isPolygon )
00486                 {
00487                     // end of the wall; loop around to close it off.
00488                     if ( isSkinnedPolygon )
00489                     {
00490                         // if we requested an extra geometry point, that means we are generating
00491                         // a polygon-closing line so we can have a unique texcoord for it. 
00492                         idx->push_back(wallVertPtr);
00493                         idx->push_back(wallVertPtr+1);
00494                         idx->push_back(wallVertPtr+2);
00495 
00496                         idx->push_back(wallVertPtr+1);
00497                         idx->push_back(wallVertPtr+3);
00498                         idx->push_back(wallVertPtr+2);
00499 
00500                         (*verts)[p+2] = (*verts)[wallPartPtr];
00501                         (*verts)[p+3] = (*verts)[wallPartPtr+1];
00502 
00503                         if ( wallSkin )
00504                         {
00505                             partLen += ((*verts)[p+2] - (*verts)[p]).length();
00506                             double h = tex_repeats_y ? -((*verts)[p+2] - (*verts)[p+3]).length() : -tex_height_m_adj;
00507                             (*wallTexcoords)[p+2].set( partLen/tex_width_m, 0.0f );
00508                             (*wallTexcoords)[p+3].set( partLen/tex_width_m, h/tex_height_m_adj );
00509                         }
00510 
00511                         wallVertPtr += 2;
00512                     }
00513                     else
00514                     {
00515                         // either not a poly, or no wall skin, so we can share the polygon-closing
00516                         // loop point.
00517                         idx->push_back(wallVertPtr); 
00518                         idx->push_back(wallVertPtr+1);
00519                         idx->push_back(wallPartPtr);
00520 
00521                         idx->push_back(wallVertPtr+1);
00522                         idx->push_back(wallPartPtr+1);
00523                         idx->push_back(wallPartPtr);
00524                     }
00525                 }
00526                 else
00527                 {
00528                     //nop - no elements required at the end of a line
00529                 }
00530             }
00531             else
00532             {
00533                 idx->push_back(wallVertPtr); 
00534                 idx->push_back(wallVertPtr+1);
00535                 idx->push_back(wallVertPtr+2); 
00536 
00537                 idx->push_back(wallVertPtr+1);
00538                 idx->push_back(wallVertPtr+3);
00539                 idx->push_back(wallVertPtr+2);
00540             }
00541 
00542             wallVertPtr += 2;
00543             made_geom = true;
00544         }
00545 
00546         walls->addPrimitiveSet( idx );
00547 
00548         if ( roof )
00549         {
00550             roof->addPrimitiveSet( new osg::DrawArrays(
00551                 osg::PrimitiveSet::LINE_LOOP,
00552                 roofPartPtr, roofVertPtr - roofPartPtr ) );
00553         }
00554 
00555         if ( base )
00556         {
00557             // reverse the base verts:
00558             int len = baseVertPtr - basePartPtr;
00559             for( int i=basePartPtr; i<len/2; i++ )
00560                 std::swap( (*baseVerts)[i], (*baseVerts)[basePartPtr+(len-1)-i] );
00561 
00562             base->addPrimitiveSet( new osg::DrawArrays(
00563                 osg::PrimitiveSet::LINE_LOOP,
00564                 basePartPtr, baseVertPtr - basePartPtr ) );
00565         }
00566 
00567         if ( outline )
00568         {
00569             unsigned len = baseVertPtr - basePartPtr;
00570 
00571             GLenum roofLineMode = isPolygon ? GL_LINE_LOOP : GL_LINE_STRIP;
00572             osg::DrawElementsUInt* roofLine = new osg::DrawElementsUInt( roofLineMode );
00573             roofLine->reserveElements( len );
00574             for( unsigned i=0; i<len; ++i )
00575                 roofLine->addElement( basePartPtr + i*2 );
00576             outline->addPrimitiveSet( roofLine );
00577 
00578             osg::DrawElementsUShort* wallLines = new osg::DrawElementsUShort( GL_LINES );
00579             wallLines->reserve( len*2 );
00580             for( unsigned i=0; i<len; ++i )
00581             {
00582                 wallLines->push_back( basePartPtr + i*2 );
00583                 wallLines->push_back( basePartPtr + i*2 + 1 );
00584             }
00585             outline->addPrimitiveSet( wallLines );
00586         }
00587     }
00588 
00589     return made_geom;
00590 }
00591 
00592 void
00593 ExtrudeGeometryFilter::addDrawable( osg::Drawable* drawable, osg::StateSet* stateSet, const std::string& name )
00594 {
00595     // find the geode for the active stateset, creating a new one if necessary. NULL is a 
00596     // valid key as well.
00597     osg::Geode* geode = _geodes[stateSet].get();
00598     if ( !geode )
00599     {
00600         geode = new osg::Geode();
00601         geode->setStateSet( stateSet );
00602         _geodes[stateSet] = geode;
00603     }
00604 
00605     geode->addDrawable( drawable );
00606 
00607     if ( !name.empty() )
00608     {
00609         drawable->setName( name );
00610     }
00611 }
00612 
00613 bool
00614 ExtrudeGeometryFilter::process( FeatureList& features, FilterContext& context )
00615 {
00616     // seed our random number generators
00617     Random wallSkinPRNG( _wallSkinSymbol.valid()? *_wallSkinSymbol->randomSeed() : 0, Random::METHOD_FAST );
00618     Random roofSkinPRNG( _roofSkinSymbol.valid()? *_roofSkinSymbol->randomSeed() : 0, Random::METHOD_FAST );
00619 
00620     for( FeatureList::iterator f = features.begin(); f != features.end(); ++f )
00621     {
00622         Feature* input = f->get();
00623 
00624         GeometryIterator iter( input->getGeometry(), false );
00625         while( iter.hasMore() )
00626         {
00627             Geometry* part = iter.next();
00628 
00629             osg::ref_ptr<osg::Geometry> walls = new osg::Geometry();
00630             //walls->setUseVertexBufferObjects(true);
00631             
00632             osg::ref_ptr<osg::Geometry> rooflines = 0L;
00633             osg::ref_ptr<osg::Geometry> outlines  = 0L;
00634             
00635             if ( part->getType() == Geometry::TYPE_POLYGON )
00636             {
00637                 rooflines = new osg::Geometry();
00638                 //rooflines->setUseVertexBufferObjects(true);
00639 
00640                 // prep the shapes by making sure all polys are open:
00641                 static_cast<Polygon*>(part)->open();
00642             }
00643 
00644             // fire up the outline geometry if we have a line symbol.
00645             if ( _outlineSymbol != 0L )
00646             {
00647                 outlines = new osg::Geometry();
00648             }
00649 
00650             // calculate the extrusion height:
00651             float height;
00652 
00653             if ( _heightCallback.valid() )
00654             {
00655                 height = _heightCallback->operator()(input, context);
00656             }
00657             else if ( _heightExpr.isSet() )
00658             {
00659                 height = input->eval( _heightExpr.mutable_value() );
00660             }
00661             else
00662             {
00663                 height = *_extrusionSymbol->height();
00664             }
00665 
00666             // calculate the height offset from the base:
00667             float offset = 0.0;
00668             if ( _heightOffsetExpr.isSet() )
00669             {
00670                 offset = input->eval( _heightOffsetExpr.mutable_value() );
00671             }
00672 
00673             osg::StateSet* wallStateSet = 0L;
00674             osg::StateSet* roofStateSet = 0L;
00675 
00676             // calculate the wall texturing:
00677             SkinResource* wallSkin = 0L;
00678             if ( _wallSkinSymbol.valid() )
00679             {
00680                 if ( _wallResLib.valid() )
00681                 {
00682                     SkinSymbol querySymbol( *_wallSkinSymbol.get() );
00683                     querySymbol.objectHeight() = fabs(height) - offset;
00684                     wallSkin = _wallResLib->getSkin( &querySymbol, wallSkinPRNG );
00685                 }
00686 
00687                 else
00688                 {
00689                     //TODO: simple single texture?
00690                 }
00691             }
00692 
00693             // calculate the rooftop texture:
00694             SkinResource* roofSkin = 0L;
00695             if ( _roofSkinSymbol.valid() )
00696             {
00697                 if ( _roofResLib.valid() )
00698                 {
00699                     SkinSymbol querySymbol( *_roofSkinSymbol.get() );
00700                     roofSkin = _roofResLib->getSkin( &querySymbol, roofSkinPRNG );
00701                 }
00702 
00703                 else
00704                 {
00705                     //TODO: simple single texture?
00706                 }
00707             }
00708 
00709             // calculate the colors:
00710             osg::Vec4f wallColor(1,1,1,1), roofColor(1,1,1,1), outlineColor(1,1,1,1);
00711 
00712             if ( _wallPolygonSymbol.valid() )
00713             {
00714                 wallColor = _wallPolygonSymbol->fill()->color();
00715             }
00716             if ( _roofPolygonSymbol.valid() )
00717             {
00718                 roofColor = _roofPolygonSymbol->fill()->color();
00719             }
00720             if ( _outlineSymbol.valid() )
00721             {
00722                 outlineColor = _outlineSymbol->stroke()->color();
00723             }
00724 
00725             // Create the extruded geometry!
00726             if (extrudeGeometry( 
00727                     part, height, offset, 
00728                     *_extrusionSymbol->flatten(),
00729                     walls.get(), rooflines.get(), 0L, outlines.get(),
00730                     wallColor, roofColor, outlineColor,
00731                     wallSkin, roofSkin,
00732                     context ) )
00733             {      
00734                 if ( wallSkin )
00735                 {
00736                     wallStateSet = context.resourceCache()->getStateSet( wallSkin );
00737                 }
00738 
00739                 // generate per-vertex normals, altering the geometry as necessary to avoid
00740                 // smoothing around sharp corners
00741     #if OSG_MIN_VERSION_REQUIRED(2,9,9)
00742                 //Crease angle threshold wasn't added until
00743                 osgUtil::SmoothingVisitor::smooth(
00744                     *walls.get(), 
00745                     osg::DegreesToRadians(_wallAngleThresh_deg) );            
00746     #else
00747                 osgUtil::SmoothingVisitor::smooth(*walls.get());            
00748     #endif
00749 
00750                 // tessellate and add the roofs if necessary:
00751                 if ( rooflines.valid() )
00752                 {
00753                     osgUtil::Tessellator tess;
00754                     tess.setTessellationType( osgUtil::Tessellator::TESS_TYPE_GEOMETRY );
00755                     tess.setWindingType( osgUtil::Tessellator::TESS_WINDING_ODD ); //POSITIVE );
00756                     tess.retessellatePolygons( *(rooflines.get()) );
00757 
00758                     // generate default normals (no crease angle necessary; they are all pointing up)
00759                     // TODO do this manually; probably faster
00760                     osgUtil::SmoothingVisitor::smooth( *rooflines.get() );
00761 
00762                     // texture the rooflines if necessary
00763                     //applyOverlayTexturing( rooflines.get(), input, env );
00764 
00765                     // mark this geometry as DYNAMIC because otherwise the OSG optimizer will destroy it.
00766                     // TODO: why??
00767                     rooflines->setDataVariance( osg::Object::DYNAMIC );
00768 
00769                     if ( roofSkin )
00770                     {
00771                         roofStateSet = context.resourceCache()->getStateSet( roofSkin );
00772                     }
00773                 }
00774 
00775                 std::string name;
00776                 if ( !_featureNameExpr.empty() )
00777                     name = input->eval( _featureNameExpr );
00778 
00779                 //MeshConsolidator::run( *walls.get() );
00780                 addDrawable( walls.get(), wallStateSet, name );
00781 
00782                 if ( rooflines.valid() )
00783                 {
00784                     //MeshConsolidator::run( *rooflines.get() );
00785                     addDrawable( rooflines.get(), roofStateSet, name );
00786                 }
00787 
00788                 if ( outlines.valid() )
00789                 {
00790                     addDrawable( outlines.get(), 0L, name );
00791                 }
00792             }   
00793         }
00794     }
00795 
00796     return true;
00797 }
00798 
00799 osg::Node*
00800 ExtrudeGeometryFilter::push( FeatureList& input, FilterContext& context )
00801 {
00802     reset( context );
00803 
00804     // minimally, we require an extrusion symbol.
00805     if ( !_extrusionSymbol.valid() )
00806     {
00807         OE_WARN << LC << "Missing required extrusion symbolology; geometry will be empty" << std::endl;
00808         return new osg::Group();
00809     }
00810 
00811     // establish the active resource library, if applicable.
00812     _wallResLib = 0L;
00813     _roofResLib = 0L;
00814 
00815     const StyleSheet* sheet = context.getSession()->styles();
00816 
00817     if ( sheet != 0L )
00818     {
00819         if ( _wallSkinSymbol.valid() && _wallSkinSymbol->libraryName().isSet() )
00820         {
00821             _wallResLib = sheet->getResourceLibrary( *_wallSkinSymbol->libraryName() );
00822             if ( !_wallResLib.valid() )
00823             {
00824                 OE_WARN << LC << "Unable to load resource library '" << *_wallSkinSymbol->libraryName() << "'"
00825                     << "; wall geometry will not be textured." << std::endl;
00826             }
00827         }
00828 
00829         if ( _roofSkinSymbol.valid() && _roofSkinSymbol->libraryName().isSet() )
00830         {
00831             _roofResLib = sheet->getResourceLibrary( *_roofSkinSymbol->libraryName() );
00832             if ( !_roofResLib.valid() )
00833             {
00834                 OE_WARN << LC << "Unable to load resource library '" << *_roofSkinSymbol->libraryName() << "'"
00835                     << "; roof geometry will not be textured." << std::endl;
00836             }
00837         }
00838     }
00839 
00840     // calculate the localization matrices (_local2world and _world2local)
00841     computeLocalizers( context );
00842 
00843     // push all the features through the extruder.
00844     bool ok = process( input, context );
00845 
00846     // convert everything to triangles and combine drawables.
00847     if ( _mergeGeometry == true && _featureNameExpr.empty() )
00848     {
00849         for( SortedGeodeMap::iterator i = _geodes.begin(); i != _geodes.end(); ++i )
00850         {
00851             MeshConsolidator::run( *i->second.get() );
00852         }
00853     }
00854 
00855     // parent geometry with a delocalizer (if necessary)
00856     osg::Group* group = createDelocalizeGroup();
00857     
00858     // combines geometries where the statesets are the same.
00859     for( SortedGeodeMap::iterator i = _geodes.begin(); i != _geodes.end(); ++i )
00860     {
00861         group->addChild( i->second.get() );
00862     }
00863     _geodes.clear();
00864 
00865     // if we drew outlines, apply a poly offset too.
00866     if ( _outlineSymbol.valid() )
00867     {
00868         osg::StateSet* groupStateSet = group->getOrCreateStateSet();
00869         groupStateSet->setAttributeAndModes( new osg::PolygonOffset(1,1), 1 );
00870         if ( _outlineSymbol->stroke()->width().isSet() )
00871             groupStateSet->setAttributeAndModes( new osg::LineWidth(*_outlineSymbol->stroke()->width()), 1 );
00872     }
00873 
00874     OE_DEBUG << LC << "Sorted geometry into " << group->getNumChildren() << " groups" << std::endl;
00875 
00876     //TODO
00877     // running this after the MC reduces the primitive set count by a huge amount, but I
00878     // have not figured out why yet.
00879     if ( _mergeGeometry == true )
00880     {
00881         osgUtil::Optimizer o;
00882         o.optimize( group, osgUtil::Optimizer::MERGE_GEOMETRY );
00883     }
00884 
00885     return group;
00886 }
 All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator Friends Defines