osgEarth 2.1.1

/home/cube/sources/osgearth/src/osgEarthDrivers/model_feature_stencil/FeatureStencilModelSource.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 
00020 #include <osgEarth/Registry>
00021 #include <osgEarth/Map>
00022 #include <osgEarthFeatures/FeatureModelSource>
00023 #include <osgEarthFeatures/FeatureSource>
00024 #include <osgEarthFeatures/BufferFilter>
00025 #include <osgEarthFeatures/TransformFilter>
00026 #include <osgEarthFeatures/ResampleFilter>
00027 #include <osgEarthFeatures/ConvertTypeFilter>
00028 #include <osgEarthFeatures/FeatureGridder>
00029 #include <osgEarthSymbology/StencilVolumeNode>
00030 #include <osgEarthSymbology/Style>
00031 #include <osgEarthSymbology/StencilVolumeNode>
00032 #include <osg/Notify>
00033 #include <osg/MatrixTransform>
00034 #include <osg/ClusterCullingCallback>
00035 #include <osg/Geode>
00036 #include <osg/Projection>
00037 #include <osgUtil/Tessellator>
00038 #include <osg/MatrixTransform>
00039 #include <osgDB/FileNameUtils>
00040 #include <OpenThreads/Mutex>
00041 #include <OpenThreads/ScopedLock>
00042 
00043 #include "FeatureStencilModelOptions"
00044 
00045 using namespace osgEarth;
00046 using namespace osgEarth::Features;
00047 using namespace osgEarth::Symbology;
00048 using namespace osgEarth::Drivers;
00049 using namespace OpenThreads;
00050 
00051 #define LC "[FeatureStencilModelSource] "
00052 
00053 #define RENDER_BIN_START 100
00054 #define MAX_NUM_STYLES   100
00055 #define OFF_PROTECTED osg::StateAttribute::OFF | osg::StateAttribute::PROTECTED
00056 
00057 namespace
00058 {
00060     osg::Node* createColorNode( const osg::Vec4f& color )
00061     {
00062         // make a full screen quad:
00063         osg::Geometry* quad = new osg::Geometry();
00064         osg::Vec3Array* verts = new osg::Vec3Array(4);
00065         (*verts)[0].set( 0, 1, 0 );
00066         (*verts)[1].set( 0, 0, 0 );
00067         (*verts)[2].set( 1, 0, 0 );
00068         (*verts)[3].set( 1, 1, 0 );
00069         quad->setVertexArray( verts );
00070         quad->addPrimitiveSet( new osg::DrawArrays( osg::PrimitiveSet::QUADS, 0, 4 ) );
00071         osg::Vec4Array* colors = new osg::Vec4Array(1);
00072         (*colors)[0] = color;
00073         quad->setColorArray( colors );
00074         quad->setColorBinding( osg::Geometry::BIND_OVERALL );
00075         osg::Geode* quad_geode = new osg::Geode();
00076         quad_geode->addDrawable( quad );
00077 
00078         osg::StateSet* quad_ss = quad->getOrCreateStateSet();
00079         quad_ss->setMode( GL_CULL_FACE, OFF_PROTECTED );
00080         quad_ss->setMode( GL_DEPTH_TEST, OFF_PROTECTED );
00081         quad_ss->setMode( GL_LIGHTING, OFF_PROTECTED );
00082         osg::MatrixTransform* abs = new osg::MatrixTransform();
00083         abs->setReferenceFrame( osg::Transform::ABSOLUTE_RF );
00084         abs->setMatrix( osg::Matrix::identity() );
00085         abs->addChild( quad_geode );
00086 
00087         osg::Projection* proj = new osg::Projection();
00088         proj->setMatrix( osg::Matrix::ortho(0, 1, 0, 1, 0, -1) );
00089         proj->addChild( abs );
00090 
00091         proj->getOrCreateStateSet()->setMode( GL_BLEND, 1 );    
00092 
00093         return proj;
00094     }
00095 
00096     void tessellate( osg::Geometry* geom )
00097     {
00098         osgUtil::Tessellator tess;
00099         tess.setTessellationType( osgUtil::Tessellator::TESS_TYPE_GEOMETRY );
00100         tess.setWindingType( osgUtil::Tessellator::TESS_WINDING_ODD );
00101     //    tess.setWindingType( osgUtil::Tessellator::TESS_WINDING_POSITIVE );
00102         tess.retessellatePolygons( *geom );
00103     }
00104 
00105     osg::Geode*
00106     createVolume(osgEarth::Symbology::Geometry* geom,
00107                  double               offset,
00108                  double               height,
00109                  const FilterContext& context )
00110     {
00111         if ( !geom ) return 0L;
00112 
00113         int numRings = 0;
00114 
00115         // start by offsetting the input data and counting the number of rings
00116         {
00117             osgEarth::Symbology::GeometryIterator i( geom );
00118             while( i.hasMore() )
00119             {
00120                 osgEarth::Symbology::Geometry* part = i.next();
00121 
00122                 if (offset != 0.0)
00123                 {
00124                     for( osg::Vec3dArray::iterator j = part->begin(); j != part->end(); j++ )
00125                     {
00126                         if ( context.isGeocentric() )
00127                         {
00128                             osg::Vec3d world = context.toWorld( *j );
00129                             // TODO: get the proper up vector; this is spherical.. or does it really matter for
00130                             // stencil volumes?
00131                             osg::Vec3d offset_vec = world;
00132                             offset_vec.normalize();
00133                             *j = context.toLocal( world + offset_vec * offset ); //(*j) += offset_vec * offset;
00134                         }
00135                         else
00136                         {
00137                             (*j).z() += offset;
00138                         }
00139                     }
00140                 }
00141 
00142                 // in the meantime, count the # of closed geoms. We will need to know this in 
00143                 // order to pre-allocate the proper # of verts.
00144                 if ( part->getType() == osgEarth::Symbology::Geometry::TYPE_POLYGON || part->getType() == osgEarth::Symbology::Geometry::TYPE_RING )
00145                 {
00146                     numRings++;
00147                 }
00148             }
00149         }
00150 
00151         // now, go thru and remove any coplanar segments from the geometry. The tesselator will
00152         // not work include a vert connecting two colinear segments in the tesselation, and this
00153         // will break the stenciling logic.
00154     #define PARALLEL_EPSILON 0.01
00155         osgEarth::Symbology::GeometryIterator i( geom );
00156         while( i.hasMore() )
00157         {
00158             osgEarth::Symbology::Geometry* part = i.next();
00159             if ( part->size() >= 3 )
00160             {
00161                 osg::Vec3d prevVec = part->front() - part->back();
00162                 prevVec.normalize();
00163 
00164                 for( osg::Vec3dArray::iterator j = part->begin(); part->size() >= 3 && j != part->end(); )
00165                 {
00166                     osg::Vec3d& p0 = *j;
00167                     osg::Vec3d& p1 = j+1 != part->end() ? *(j+1) : part->front();
00168                     osg::Vec3d vec = p1-p0; vec.normalize();
00169 
00170                     // if the vectors are essentially parallel, remove the extraneous vertex.
00171                     if ( (prevVec ^ vec).length() < PARALLEL_EPSILON )
00172                     {
00173                         j = part->erase( j );
00174                         //OE_NOTICE << "removed colinear segment" << std::endl;
00175                     }
00176                     else
00177                     {
00178                         ++j;
00179                         prevVec = vec;
00180                     }
00181                 }
00182             }
00183         }
00184 
00185 
00186         bool made_geom = true;
00187         const SpatialReference* srs = context.profile()->getSRS();
00188 
00189         // total up all the points so we can pre-allocate the vertex arrays.
00190         int num_cap_verts = geom->getTotalPointCount();
00191         int num_wall_verts = 2 * (num_cap_verts + numRings); // add in numRings b/c we need to close each wall
00192 
00193         osg::Geometry* walls = new osg::Geometry();
00194         osg::Vec3Array* verts = new osg::Vec3Array( num_wall_verts );
00195         walls->setVertexArray( verts );
00196 
00197         osg::Geometry* top_cap = new osg::Geometry();
00198         osg::Vec3Array* top_verts = new osg::Vec3Array( num_cap_verts );
00199         top_cap->setVertexArray( top_verts );
00200 
00201         osg::Geometry* bottom_cap = new osg::Geometry();
00202         osg::Vec3Array* bottom_verts = new osg::Vec3Array( num_cap_verts );
00203         bottom_cap->setVertexArray( bottom_verts );
00204 
00205         int wall_vert_ptr = 0;
00206         int top_vert_ptr = 0;
00207         int bottom_vert_ptr = 0;
00208 
00209         //double target_len = height;
00210 
00211         // now generate the extruded geometry.
00212         osgEarth::Symbology::GeometryIterator k( geom );
00213         while( k.hasMore() )
00214         {
00215             osgEarth::Symbology::Geometry* part = k.next();
00216 
00217             unsigned int wall_part_ptr = wall_vert_ptr;
00218             unsigned int top_part_ptr = top_vert_ptr;
00219             unsigned int bottom_part_ptr = bottom_vert_ptr;
00220             double part_len = 0.0;
00221 
00222             GLenum prim_type = part->getType() == osgEarth::Symbology::Geometry::TYPE_POINTSET ? GL_LINES : GL_TRIANGLE_STRIP;
00223 
00224             for( osg::Vec3dArray::const_iterator m = part->begin(); m != part->end(); ++m )
00225             {
00226                 osg::Vec3d extrude_vec;
00227 
00228                 if ( srs )
00229                 {
00230                     osg::Vec3d m_world = context.toWorld( *m ); //*m * context.inverseReferenceFrame();
00231                     if ( context.isGeocentric() )
00232                     {
00233                         osg::Vec3d p_vec = m_world; // todo: not exactly right; spherical
00234 
00235                         osg::Vec3d unit_vec = p_vec; 
00236                         unit_vec.normalize();
00237                         p_vec = p_vec + unit_vec*height;
00238 
00239                         extrude_vec = context.toLocal( p_vec ); //p_vec * context.referenceFrame();
00240                     }
00241                     else
00242                     {
00243                         extrude_vec.set( m_world.x(), m_world.y(), height );
00244                         extrude_vec = context.toLocal( extrude_vec ); //extrude_vec * context.referenceFrame();
00245                     }
00246                 }
00247                 else
00248                 {
00249                     extrude_vec.set( m->x(), m->y(), height );
00250                 }
00251 
00252                 (*top_verts)[top_vert_ptr++] = extrude_vec;
00253                 (*bottom_verts)[bottom_vert_ptr++] = *m;
00254                  
00255                 part_len += wall_vert_ptr > (int)wall_part_ptr?
00256                     (extrude_vec - (*verts)[wall_vert_ptr-2]).length() :
00257                     0.0;
00258 
00259                 int p;
00260 
00261                 p = wall_vert_ptr++;
00262                 (*verts)[p] = extrude_vec;
00263 
00264                 p = wall_vert_ptr++;
00265                 (*verts)[p] = *m;
00266             }
00267 
00268             // close the wall if it's a ring/poly:
00269             if ( part->getType() == osgEarth::Symbology::Geometry::TYPE_RING || part->getType() == osgEarth::Symbology::Geometry::TYPE_POLYGON )
00270             {
00271                 part_len += wall_vert_ptr > (int)wall_part_ptr?
00272                     ((*verts)[wall_part_ptr] - (*verts)[wall_vert_ptr-2]).length() :
00273                     0.0;
00274 
00275                 int p;
00276 
00277                 p = wall_vert_ptr++;
00278                 (*verts)[p] = (*verts)[wall_part_ptr];
00279 
00280                 p = wall_vert_ptr++;
00281                 (*verts)[p] = (*verts)[wall_part_ptr+1];
00282             }
00283 
00284             walls->addPrimitiveSet( new osg::DrawArrays(
00285                 prim_type,
00286                 wall_part_ptr, wall_vert_ptr - wall_part_ptr ) );
00287 
00288             top_cap->addPrimitiveSet( new osg::DrawArrays(
00289                 osg::PrimitiveSet::LINE_LOOP,
00290                 top_part_ptr, top_vert_ptr - top_part_ptr ) );
00291 
00292             // reverse the bottom verts so the front face is down:
00293             std::reverse( bottom_verts->begin()+bottom_part_ptr, bottom_verts->begin()+bottom_vert_ptr );
00294 
00295             bottom_cap->addPrimitiveSet( new osg::DrawArrays(
00296                 osg::PrimitiveSet::LINE_LOOP,
00297                 bottom_part_ptr, bottom_vert_ptr - bottom_part_ptr ) );
00298         }
00299 
00300         // build solid surfaces for the caps:
00301         tessellate( top_cap );
00302         tessellate( bottom_cap );
00303 
00304         osg::Geode* geode = new osg::Geode();
00305         geode->addDrawable( walls );
00306         geode->addDrawable( top_cap );
00307         geode->addDrawable( bottom_cap );
00308 
00309         return geode;
00310     }
00311 
00312     struct BuildData // : public osg::Referenced
00313     {
00314         //BuildData() { }
00315         BuildData( int renderBinStart ) : _renderBin( renderBinStart ) { }
00316 
00317         typedef std::pair<std::string, osg::ref_ptr<StencilVolumeNode> > StyleGroup;
00318         int                       _renderBin;
00319         Threading::ReadWriteMutex _mutex;
00320         std::vector<StyleGroup>   _styleGroups;  // NOTE: DO NOT ACCESS without a mutex!
00321 
00322 
00323         bool getStyleNode( const std::string& styleName, StencilVolumeNode*& out_svn, bool useLock )
00324         {
00325             if ( useLock )
00326             {
00327                 Threading::ScopedReadLock lock( _mutex );
00328                 return getStyleNodeWithoutLocking( styleName, out_svn );
00329             }
00330             else
00331             {
00332                 return getStyleNodeWithoutLocking( styleName, out_svn );
00333             }
00334         }
00335 
00336     private:
00337         bool getStyleNodeWithoutLocking( const std::string& styleName, StencilVolumeNode*& out_svn )
00338         {
00339             for(std::vector<StyleGroup>::iterator i = _styleGroups.begin(); i != _styleGroups.end(); ++i )
00340             {
00341                 if( i->first == styleName )
00342                 {
00343                     out_svn = i->second.get();
00344                     return true;
00345                 }
00346             }
00347             return false;
00348         }
00349     };
00350 
00351     class StencilVolumeNodeFactory : public FeatureNodeFactory
00352     {
00353     protected:
00354         const FeatureStencilModelOptions _options;
00355         int                              _renderBinStart;
00356         BuildData                        _buildData;
00357 
00358     public:
00359         StencilVolumeNodeFactory( const FeatureStencilModelOptions& options, int renderBinStart )
00360             : _options(options),
00361               _buildData( renderBinStart )
00362         { }
00363 
00364         //override
00365         bool createOrUpdateNode(
00366             FeatureCursor*            cursor,
00367             const Style&              style,
00368             const FilterContext&      context,
00369             osg::ref_ptr<osg::Node>&  node )
00370         {
00371             const MapInfo& mi = context.getSession()->getMapInfo();
00372             
00373             // A processing context to use locally
00374             FilterContext cx = context;
00375 
00376             // make a working copy of the feature data.
00377             FeatureList featureList;
00378             cursor->fill( featureList );
00379 
00380             //for (FeatureList::const_iterator it = features.begin(); it != features.end(); ++it)
00381             //    featureList.push_back(osg::clone((*it).get(),osg::CopyOp::DEEP_COPY_ALL));
00382 
00383             // establish the extrusion distance for the stencil volumes
00384             double extrusionDistance = 1;
00385             double densificationThreshold = 1.0;
00386             if ( _options.extrusionDistance().isSet() )
00387             {
00388                 extrusionDistance = *_options.extrusionDistance();
00389             }
00390             else
00391             {
00392                 if ( mi.isGeocentric() )
00393                     extrusionDistance = 300000.0; // meters geocentric
00394                 else if ( mi.getProfile()->getSRS()->isGeographic() )
00395                     extrusionDistance = 5.0; // degrees-as-meters
00396                 else
00397                     extrusionDistance = 12000.0; // meters
00398             }
00399 
00400             densificationThreshold = *_options.densificationThreshold();
00401 
00402             // Scan the geometry to see if it includes line data, since that will require buffering:
00403             bool hasLines = false;
00404             for( FeatureList::const_iterator i = featureList.begin(); i != featureList.end(); ++i )
00405             {
00406                 Feature* feature = (*i).get();
00407                 Geometry* geom = feature->getGeometry();
00408                 if ( geom && 
00409                      ( geom->getComponentType() == Geometry::TYPE_LINESTRING ||
00410                        geom->getComponentType() == Geometry::TYPE_RING ) )
00411                 {
00412                     hasLines = true;
00413                     break;
00414                 }
00415             }
00416 
00417             // If the geometry is lines, we need to buffer them before they will work with stenciling
00418             if ( hasLines )
00419             {
00420                 const LineSymbol* line = style.getSymbol<LineSymbol>();
00421                 if (line)
00422                 {
00423                     BufferFilter buffer;
00424                     buffer.distance() = 0.5 * line->stroke()->width().value();
00425                     buffer.capStyle() = line->stroke()->lineCap().value();
00426                     cx = buffer.push( featureList, cx );
00427                 }
00428             }
00429 
00430             // Transform them into the map's SRS, localizing the verts along the way:
00431             TransformFilter xform( mi.getProfile()->getSRS() );
00432             xform.setMakeGeocentric( mi.isGeocentric() );
00433             xform.setLocalizeCoordinates( !mi.isGeocentric() );
00434             cx = xform.push( featureList, cx );
00435 
00436             if ( mi.isGeocentric() )
00437             {
00438                 // We need to make sure that on a round globe, the points are sampled such that
00439                 // long segments follow the curvature of the earth. By the way, if a Buffer was
00440                 // applied, that will also remove colinear segment points. Resample the points to 
00441                 // achieve a usable tesselation.
00442                 ResampleFilter resample;
00443                 resample.maxLength() = densificationThreshold;
00444                 resample.minLength() = 0.0;
00445                 resample.perturbationThreshold() = 0.1;
00446                 cx = resample.push( featureList, cx );
00447             }
00448 
00449             // Extrude and cap the geometry in both directions to build a stencil volume:
00450             osg::Group* volumes = 0L;
00451 
00452             for( FeatureList::iterator i = featureList.begin(); i != featureList.end(); ++i )
00453             {
00454                 Feature* feature = (*i).get();
00455                 Geometry* geom = feature->getGeometry();
00456                 osg::Node* volume = createVolume( geom, -extrusionDistance, extrusionDistance * 2.0, cx );
00457 
00458                 if ( volume )
00459                 {
00460                     if ( !volumes )
00461                         volumes = new osg::Group();
00462                     volumes->addChild( volume );
00463                 }
00464             }
00465 
00466             if ( volumes )
00467             {
00468                 // Resolve the localizing reference frame if necessary:
00469                 if ( cx.hasReferenceFrame() )
00470                 {
00471                     osg::MatrixTransform* xform = new osg::MatrixTransform( cx.inverseReferenceFrame() );
00472                     xform->addChild( volumes );
00473                     volumes = xform;
00474                 }
00475 
00476                 // Apply an LOD if required:
00477                 if ( _options.minRange().isSet() || _options.maxRange().isSet() )
00478                 {
00479                     osg::LOD* lod = new osg::LOD();
00480                     lod->addChild( volumes, _options.minRange().value(), _options.maxRange().value() );
00481                     volumes = lod;
00482                 }
00483 
00484                 // Add the volumes to the appropriate style group.
00485                 StencilVolumeNode* styleNode = dynamic_cast<StencilVolumeNode*>( getOrCreateStyleGroup( style, cx.getSession() ) );
00486                 styleNode->addVolumes( volumes );
00487             }
00488 
00489             node = 0L; // always return null, since we added our geom to the style group.
00490             return volumes != 0L;
00491         }
00492 
00493         //override
00494         osg::Group* getOrCreateStyleGroup( const Style& style, Session* session )
00495         {
00496             if ( _options.showVolumes() == true )
00497             {
00498                 return new osg::Group();
00499             }
00500             else
00501             {
00502                 StencilVolumeNode* styleNode = 0L;
00503                 if ( !_buildData.getStyleNode(style.getName(), styleNode, true) )
00504                 {
00505                     // did not find; write-lock it and try again (double-check pattern)
00506                     Threading::ScopedWriteLock exclusiveLock( _buildData._mutex );
00507 
00508                     if ( !_buildData.getStyleNode(style.getName(), styleNode, false) )
00509                     {
00510                         OE_INFO << LC << "Create style group \"" << style.getName() << "\"" << std::endl;
00511 
00512                         styleNode = new StencilVolumeNode( *_options.mask(), *_options.inverted() );
00513 
00514                         if ( _options.mask() == false )
00515                         {
00516                             osg::Vec4f maskColor = osg::Vec4(1,1,0,1);
00517 
00518                             if (/*hasLines &&*/ style.getSymbol<LineSymbol>())
00519                             {
00520                                 const LineSymbol* line = style.getSymbol<LineSymbol>();
00521                                 maskColor = line->stroke()->color();
00522                             } 
00523                             else
00524                             {
00525                                 const PolygonSymbol* poly = style.getSymbol<PolygonSymbol>();
00526                                 if (poly)
00527                                     maskColor = poly->fill()->color();
00528                             }
00529                             styleNode->addChild( createColorNode(maskColor) );
00530                             
00531                             osg::StateSet* ss = styleNode->getOrCreateStateSet();
00532 
00533                             ss->setMode( GL_LIGHTING, _options.enableLighting() == true?
00534                                  osg::StateAttribute::ON | osg::StateAttribute::PROTECTED :
00535                                  osg::StateAttribute::OFF | osg::StateAttribute::PROTECTED );
00536                         }
00537 
00538                         _buildData._renderBin = styleNode->setBaseRenderBin( _buildData._renderBin );
00539                         _buildData._styleGroups.push_back( BuildData::StyleGroup(style.getName(), styleNode) );
00540                     }
00541                 }
00542 
00543                 return styleNode;
00544             }
00545         }
00546     };
00547 
00548 
00549     class FeatureStencilModelSource : public FeatureModelSource
00550     {
00551     public:
00552         FeatureStencilModelSource( const ModelSourceOptions& options, int renderBinStart ) :
00553             FeatureModelSource( options ),
00554             _options( options ),
00555             _renderBinStart( renderBinStart )
00556         {
00557             // make sure we have stencil bits. Note, this only works before
00558             // a viewer gets created. You may need to allocate stencil bits
00559             // yourself if you make this object after realizing a viewer.
00560             if ( osg::DisplaySettings::instance()->getMinimumNumStencilBits() < 8 )
00561             {
00562                 osg::DisplaySettings::instance()->setMinimumNumStencilBits( 8 );
00563             }
00564         }
00565         
00566         //override
00567         virtual const FeatureModelSourceOptions& getFeatureModelOptions() const
00568         {
00569             return _options;
00570         }
00571 
00572         //override
00573         void initialize( const std::string& referenceURI, const Map* map )
00574         {
00575             FeatureModelSource::initialize( referenceURI, map );
00576         }
00577 
00578         //override
00579         FeatureNodeFactory* createFeatureNodeFactory()
00580         {
00581             return new StencilVolumeNodeFactory( _options, _renderBinStart );
00582         }
00583 
00584     protected:
00585         int _renderBinStart;
00586         const FeatureStencilModelOptions _options;
00587     };
00588 }
00589 
00590 
00591 class FeatureStencilModelSourceDriver : public ModelSourceDriver
00592 {
00593 public:
00594     FeatureStencilModelSourceDriver() :
00595         _renderBinStart( RENDER_BIN_START )
00596     {
00597         supportsExtension( "osgearth_model_feature_stencil", "osgEarth feature stencil plugin" );
00598     }
00599 
00600     virtual const char* className()
00601     {
00602         return "osgEarth Feature Stencil Model Plugin";
00603     }
00604 
00605     FeatureStencilModelSource* create( const Options* options )
00606     {
00607         ScopedLock<Mutex> lock( _createMutex );
00608 
00609         FeatureStencilModelSource* obj = new FeatureStencilModelSource( 
00610             getModelSourceOptions(options), 
00611             _renderBinStart );
00612 
00613         _renderBinStart += MAX_NUM_STYLES*4;
00614 
00615         return obj;
00616     }
00617 
00618     virtual ReadResult readObject(const std::string& file_name, const Options* options) const
00619     {
00620         if ( !acceptsExtension(osgDB::getLowerCaseFileExtension( file_name )))
00621             return ReadResult::FILE_NOT_HANDLED;
00622 
00623         FeatureStencilModelSourceDriver* nonConstThis = const_cast<FeatureStencilModelSourceDriver*>(this);
00624         return nonConstThis->create( options );
00625     }
00626 
00627 protected:
00628     Mutex _createMutex;
00629     int _renderBinStart;
00630 };
00631 
00632 REGISTER_OSGPLUGIN(osgearth_model_feature_stencil, FeatureStencilModelSourceDriver)
 All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator Friends Defines