bool BillboardExtension::connect(MapNode* mapNode) { if ( !mapNode ) { OE_WARN << LC << "Illegal: MapNode cannot be null." << std::endl; return false; } OE_INFO << LC << "Connecting to MapNode.\n"; if ( !_options.imageURI().isSet() ) { OE_WARN << LC << "Illegal: image URI is required" << std::endl; return false; } if ( !_options.featureOptions().isSet() ) { OE_WARN << LC << "Illegal: feature source is required" << std::endl; return false; } _features = FeatureSourceFactory::create( _options.featureOptions().value() ); if ( !_features.valid() ) { OE_WARN << LC << "Illegal: no valid feature source provided" << std::endl; return false; } //if ( _features->getGeometryType() != osgEarth::Symbology::Geometry::TYPE_POINTSET ) //{ // OE_WARN << LC << "Illegal: only points currently supported" << std::endl; // return false; //} _features->initialize( _dbOptions ); osg::Vec3dArray* verts; if ( _features->getFeatureProfile() ) { verts = new osg::Vec3dArray(); OE_NOTICE << "Reading features...\n"; osg::ref_ptr<FeatureCursor> cursor = _features->createFeatureCursor(); while ( cursor.valid() && cursor->hasMore() ) { Feature* f = cursor->nextFeature(); if ( f && f->getGeometry() ) { if ( f->getGeometry()->getComponentType() == Geometry::TYPE_POLYGON ) { FilterContext cx; cx.setProfile( new FeatureProfile(_features->getFeatureProfile()->getExtent()) ); ScatterFilter scatter; scatter.setDensity( _options.density().get() ); scatter.setRandom( true ); FeatureList featureList; featureList.push_back(f); scatter.push( featureList, cx ); } // Init a filter to tranform feature in desired SRS if (!mapNode->getMapSRS()->isEquivalentTo(_features->getFeatureProfile()->getSRS())) { FilterContext cx; cx.setProfile( new FeatureProfile(_features->getFeatureProfile()->getExtent()) ); TransformFilter xform( mapNode->getMapSRS() ); FeatureList featureList; featureList.push_back(f); cx = xform.push(featureList, cx); } GeometryIterator iter(f->getGeometry()); while(iter.hasMore()) { const Geometry* geom = iter.next(); osg::ref_ptr<osg::Vec3dArray> fVerts = geom->createVec3dArray(); verts->insert(verts->end(), fVerts->begin(), fVerts->end()); } } } } else { OE_WARN << LC << "Illegal: feature source has no SRS" << std::endl; return false; } if ( verts && verts->size() > 0 ) { OE_NOTICE << LC << "Read " << verts->size() << " points.\n"; //localize all the verts GeoPoint centroid; _features->getFeatureProfile()->getExtent().getCentroid(centroid); centroid = centroid.transform(mapNode->getMapSRS()); OE_NOTICE << "Centroid = " << centroid.x() << ", " << centroid.y() << "\n"; osg::Matrixd l2w, w2l; centroid.createLocalToWorld(l2w); w2l.invert(l2w); osg::MatrixTransform* mt = new osg::MatrixTransform; mt->setMatrix(l2w); OE_NOTICE << "Clamping elevations...\n"; osgEarth::ElevationQuery eq(mapNode->getMap()); eq.setFallBackOnNoData( true ); eq.getElevations(verts->asVector(), mapNode->getMapSRS(), true, 0.005); OE_NOTICE << "Building geometry...\n"; osg::Vec3Array* normals = new osg::Vec3Array(verts->size()); osg::Vec4Array* colors = new osg::Vec4Array(verts->size()); Random rng; for (int i=0; i < verts->size(); i++) { GeoPoint vert(mapNode->getMapSRS(), (*verts)[i], osgEarth::ALTMODE_ABSOLUTE); osg::Vec3d world; vert.toWorld(world); (*verts)[i] = world * w2l; osg::Vec3 normal = world; normal.normalize(); (*normals)[i] = osg::Matrix::transform3x3(normal, w2l); double n = rng.next(); (*colors)[i].set( n, n, n, 1 ); } //create geom and primitive sets osg::Geometry* geometry = new osg::Geometry(); geometry->setVertexArray( verts ); geometry->setNormalArray( normals ); geometry->setNormalBinding( osg::Geometry::BIND_PER_VERTEX ); geometry->setColorArray(colors); geometry->setColorBinding( osg::Geometry::BIND_PER_VERTEX ); geometry->addPrimitiveSet( new osg::DrawArrays( GL_POINTS, 0, verts->size() ) ); //create image and texture to render to osg::Texture2D* tex = new osg::Texture2D(_options.imageURI()->getImage(_dbOptions)); tex->setResizeNonPowerOfTwoHint(false); tex->setFilter( osg::Texture::MIN_FILTER, osg::Texture::LINEAR_MIPMAP_LINEAR ); tex->setFilter( osg::Texture::MAG_FILTER, osg::Texture::LINEAR ); tex->setWrap(osg::Texture::WRAP_S, osg::Texture::CLAMP_TO_EDGE); tex->setWrap(osg::Texture::WRAP_T, osg::Texture::CLAMP_TO_EDGE); geometry->setName("BillboardPoints"); osg::Geode* geode = new osg::Geode; geode->addDrawable(geometry); //osg::ref_ptr<StateSetCache> cache = new StateSetCache(); //Registry::shaderGenerator().run(geode, cache.get()); //set the texture related uniforms osg::StateSet* geode_ss = geode->getOrCreateStateSet(); geode_ss->setTextureAttributeAndModes( 2, tex, 1 ); geode_ss->getOrCreateUniform("billboard_tex", osg::Uniform::SAMPLER_2D)->set( 2 ); float bbWidth = (float)tex->getImage()->s() / 2.0f; float bbHeight = (float)tex->getImage()->t(); float aspect = (float)tex->getImage()->s() / (float)tex->getImage()->t(); if (_options.height().isSet()) { bbHeight = _options.height().get(); if (!_options.width().isSet()) { bbWidth = bbHeight * aspect / 2.0f; } } if (_options.width().isSet()) { bbWidth = _options.width().get() / 2.0f; if (!_options.height().isSet()) { bbHeight = _options.width().get() / aspect; } } geode_ss->getOrCreateUniform("billboard_width", osg::Uniform::FLOAT)->set( bbWidth ); geode_ss->getOrCreateUniform("billboard_height", osg::Uniform::FLOAT)->set( bbHeight ); geode_ss->setMode(GL_BLEND, osg::StateAttribute::ON); //for now just using an osg::Program //TODO: need to add GeometryShader support to the shader comp setup VirtualProgram* vp = VirtualProgram::getOrCreate(geode_ss); vp->setName( "osgEarth Billboard Extension" ); ShaderPackage shaders; shaders.add( "Billboard geometry shader", billboardGeomShader ); shaders.add( "Billboard fragment shader", billboardFragShader ); shaders.loadAll( vp ); geode_ss->setMode( GL_CULL_FACE, osg::StateAttribute::OFF ); geode->setCullingActive(false); mt->addChild(geode); mapNode->getModelLayerGroup()->addChild(mt); return true; } return false; }
osg::Node* GeomCompiler::compile(FeatureCursor* cursor, const Style& style, const FilterContext& context) { if ( !context.profile() ) { OE_WARN << LC << "Valid feature profile required" << std::endl; return 0L; } if ( style.empty() ) { OE_WARN << LC << "Non-empty style required" << std::endl; return 0L; } osg::ref_ptr<osg::Group> resultGroup = new osg::Group(); // start by making a working copy of the feature set FeatureList workingSet; cursor->fill( workingSet ); // create a filter context that will track feature data through the process FilterContext cx = context; if ( !cx.extent().isSet() ) cx.extent() = cx.profile()->getExtent(); // only localize coordinates if the map if geocentric AND the extent is // less than 180 degrees. const MapInfo& mi = cx.getSession()->getMapInfo(); GeoExtent workingExtent = cx.extent()->transform( cx.profile()->getSRS()->getGeographicSRS() ); bool localize = mi.isGeocentric() && workingExtent.width() < 180.0; // go through the Style and figure out which filters to use. const MarkerSymbol* marker = style.get<MarkerSymbol>(); const PointSymbol* point = style.get<PointSymbol>(); const LineSymbol* line = style.get<LineSymbol>(); const PolygonSymbol* polygon = style.get<PolygonSymbol>(); const ExtrusionSymbol* extrusion = style.get<ExtrusionSymbol>(); const AltitudeSymbol* altitude = style.get<AltitudeSymbol>(); const TextSymbol* text = style.get<TextSymbol>(); // transform the features into the map profile TransformFilter xform( mi.getProfile()->getSRS(), mi.isGeocentric() ); xform.setLocalizeCoordinates( localize ); if ( altitude && altitude->verticalOffset().isSet() ) xform.setMatrix( osg::Matrixd::translate(0, 0, *altitude->verticalOffset()) ); cx = xform.push( workingSet, cx ); bool clampRequired = altitude && altitude->clamping() != AltitudeSymbol::CLAMP_NONE; // model substitution if ( marker ) { if ( marker->placement() == MarkerSymbol::PLACEMENT_RANDOM || marker->placement() == MarkerSymbol::PLACEMENT_INTERVAL ) { ScatterFilter scatter; scatter.setDensity( *marker->density() ); scatter.setRandom( marker->placement() == MarkerSymbol::PLACEMENT_RANDOM ); scatter.setRandomSeed( *marker->randomSeed() ); cx = scatter.push( workingSet, cx ); } if ( clampRequired ) { ClampFilter clamp; clamp.setIgnoreZ( altitude->clamping() == AltitudeSymbol::CLAMP_TO_TERRAIN ); cx = clamp.push( workingSet, cx ); clampRequired = false; } SubstituteModelFilter sub( style ); sub.setClustering( *_options.clustering() ); if ( marker->scale().isSet() ) sub.setModelMatrix( osg::Matrixd::scale( *marker->scale() ) ); cx = sub.push( workingSet, cx ); osg::Node* node = sub.getNode(); if ( node ) resultGroup->addChild( node ); } // extruded geometry if ( extrusion && ( line || polygon ) ) { if ( clampRequired ) { ClampFilter clamp; clamp.setIgnoreZ( altitude->clamping() == AltitudeSymbol::CLAMP_TO_TERRAIN ); if ( extrusion->heightReference() == ExtrusionSymbol::HEIGHT_REFERENCE_MSL ) clamp.setMaxZAttributeName( "__max_z"); cx = clamp.push( workingSet, cx ); clampRequired = false; } ExtrudeGeometryFilter extrude; if ( extrusion ) { if ( extrusion->height().isSet() ) extrude.setExtrusionHeight( *extrusion->height() ); if ( extrusion->heightExpression().isSet() ) extrude.setExtrusionExpr( *extrusion->heightExpression() ); //extrude.setHeightReferenceFrame( *extrusion->heightReference() ); if ( extrusion->heightReference() == ExtrusionSymbol::HEIGHT_REFERENCE_MSL ) extrude.setHeightOffsetExpression( NumericExpression("[__max_z]") ); extrude.setFlatten( *extrusion->flatten() ); } if ( polygon ) { extrude.setColor( polygon->fill()->color() ); } osg::Node* node = extrude.push( workingSet, cx ); if ( node ) resultGroup->addChild( node ); } // simple geometry else if ( point || line || polygon ) { if ( clampRequired ) { ClampFilter clamp; clamp.setIgnoreZ( altitude->clamping() == AltitudeSymbol::CLAMP_TO_TERRAIN ); cx = clamp.push( workingSet, cx ); clampRequired = false; } BuildGeometryFilter filter( style ); if ( _options.maxGranularity().isSet() ) filter.maxGranularity() = *_options.maxGranularity(); if ( _options.mergeGeometry().isSet() ) filter.mergeGeometry() = *_options.mergeGeometry(); if ( _options.featureName().isSet() ) filter.featureName() = *_options.featureName(); cx = filter.push( workingSet, cx ); osg::Node* node = filter.getNode(); if ( node ) resultGroup->addChild( node ); } if ( text ) { if ( clampRequired ) { ClampFilter clamp; clamp.setIgnoreZ( altitude->clamping() == AltitudeSymbol::CLAMP_TO_TERRAIN ); cx = clamp.push( workingSet, cx ); clampRequired = false; } BuildTextFilter filter( style ); cx = filter.push( workingSet, cx ); osg::Node* node = filter.takeNode(); if ( node ) resultGroup->addChild( node ); } //else // insufficient symbology //{ // OE_WARN << LC << "Insufficient symbology; no geometry created" << std::endl; //} // install the localization transform if necessary. if ( cx.hasReferenceFrame() ) { osg::MatrixTransform* delocalizer = new osg::MatrixTransform( cx.inverseReferenceFrame() ); delocalizer->addChild( resultGroup.get() ); resultGroup = delocalizer; } resultGroup->getOrCreateStateSet()->setMode( GL_BLEND, 1 ); //osgDB::writeNodeFile( *(resultGroup.get()), "out.osg" ); return resultGroup.release(); }
osg::Node* GeometryCompiler::compile(FeatureList& workingSet, const Style& style, const FilterContext& context) { #ifdef PROFILING osg::Timer_t p_start = osg::Timer::instance()->tick(); unsigned p_features = workingSet.size(); #endif // for debugging/validation. std::vector<std::string> history; bool trackHistory = (_options.validate() == true); osg::ref_ptr<osg::Group> resultGroup = new osg::Group(); // create a filter context that will track feature data through the process FilterContext sharedCX = context; if ( !sharedCX.extent().isSet() && sharedCX.profile() ) { sharedCX.extent() = sharedCX.profile()->getExtent(); } // ref_ptr's to hold defaults in case we need them. osg::ref_ptr<PointSymbol> defaultPoint; osg::ref_ptr<LineSymbol> defaultLine; osg::ref_ptr<PolygonSymbol> defaultPolygon; // go through the Style and figure out which filters to use. const PointSymbol* point = style.get<PointSymbol>(); const LineSymbol* line = style.get<LineSymbol>(); const PolygonSymbol* polygon = style.get<PolygonSymbol>(); const ExtrusionSymbol* extrusion = style.get<ExtrusionSymbol>(); const AltitudeSymbol* altitude = style.get<AltitudeSymbol>(); const TextSymbol* text = style.get<TextSymbol>(); const MarkerSymbol* marker = style.get<MarkerSymbol>(); // to be deprecated const IconSymbol* icon = style.get<IconSymbol>(); const ModelSymbol* model = style.get<ModelSymbol>(); // Perform tessellation first. if ( line ) { if ( line->tessellation().isSet() ) { TemplateFeatureFilter<TessellateOperator> filter; filter.setNumPartitions( *line->tessellation() ); filter.setDefaultGeoInterp( _options.geoInterp().get() ); sharedCX = filter.push( workingSet, sharedCX ); if ( trackHistory ) history.push_back( "tessellation" ); } else if ( line->tessellationSize().isSet() ) { TemplateFeatureFilter<TessellateOperator> filter; filter.setMaxPartitionSize( *line->tessellationSize() ); filter.setDefaultGeoInterp( _options.geoInterp().get() ); sharedCX = filter.push( workingSet, sharedCX ); if ( trackHistory ) history.push_back( "tessellationSize" ); } } // if the style was empty, use some defaults based on the geometry type of the // first feature. if ( !point && !line && !polygon && !marker && !extrusion && !text && !model && !icon && workingSet.size() > 0 ) { Feature* first = workingSet.begin()->get(); Geometry* geom = first->getGeometry(); if ( geom ) { switch( geom->getComponentType() ) { case Geometry::TYPE_LINESTRING: case Geometry::TYPE_RING: defaultLine = new LineSymbol(); line = defaultLine.get(); break; case Geometry::TYPE_POINTSET: defaultPoint = new PointSymbol(); point = defaultPoint.get(); break; case Geometry::TYPE_POLYGON: defaultPolygon = new PolygonSymbol(); polygon = defaultPolygon.get(); break; case Geometry::TYPE_MULTI: case Geometry::TYPE_UNKNOWN: break; } } } // resample the geometry if necessary: if (_options.resampleMode().isSet()) { ResampleFilter resample; resample.resampleMode() = *_options.resampleMode(); if (_options.resampleMaxLength().isSet()) { resample.maxLength() = *_options.resampleMaxLength(); } sharedCX = resample.push( workingSet, sharedCX ); if ( trackHistory ) history.push_back( "resample" ); } // check whether we need to do elevation clamping: bool altRequired = _options.ignoreAltitudeSymbol() != true && altitude && ( altitude->clamping() != AltitudeSymbol::CLAMP_NONE || altitude->verticalOffset().isSet() || altitude->verticalScale().isSet() || altitude->script().isSet() ); // marker substitution -- to be deprecated in favor of model/icon if ( marker ) { if ( trackHistory ) history.push_back( "marker" ); // use a separate filter context since we'll be munging the data FilterContext markerCX = sharedCX; if ( marker->placement() == MarkerSymbol::PLACEMENT_RANDOM || marker->placement() == MarkerSymbol::PLACEMENT_INTERVAL ) { ScatterFilter scatter; scatter.setDensity( *marker->density() ); scatter.setRandom( marker->placement() == MarkerSymbol::PLACEMENT_RANDOM ); scatter.setRandomSeed( *marker->randomSeed() ); markerCX = scatter.push( workingSet, markerCX ); if ( trackHistory ) history.push_back( "scatter" ); } else if ( marker->placement() == MarkerSymbol::PLACEMENT_CENTROID ) { CentroidFilter centroid; markerCX = centroid.push( workingSet, markerCX ); if ( trackHistory ) history.push_back( "centroid" ); } if ( altRequired ) { AltitudeFilter clamp; clamp.setPropertiesFromStyle( style ); markerCX = clamp.push( workingSet, markerCX ); if ( trackHistory ) history.push_back( "altitude" ); // don't set this; we changed the input data. //altRequired = false; } SubstituteModelFilter sub( style ); sub.setClustering( *_options.clustering() ); sub.setUseDrawInstanced( *_options.instancing() ); if ( _options.featureName().isSet() ) sub.setFeatureNameExpr( *_options.featureName() ); osg::Node* node = sub.push( workingSet, markerCX ); if ( node ) { if ( trackHistory ) history.push_back( "substitute" ); resultGroup->addChild( node ); } } // instance substitution (replaces marker) else if ( model ) { const InstanceSymbol* instance = model ? (const InstanceSymbol*)model : (const InstanceSymbol*)icon; // use a separate filter context since we'll be munging the data FilterContext localCX = sharedCX; if ( trackHistory ) history.push_back( "model"); if ( instance->placement() == InstanceSymbol::PLACEMENT_RANDOM || instance->placement() == InstanceSymbol::PLACEMENT_INTERVAL ) { ScatterFilter scatter; scatter.setDensity( *instance->density() ); scatter.setRandom( instance->placement() == InstanceSymbol::PLACEMENT_RANDOM ); scatter.setRandomSeed( *instance->randomSeed() ); localCX = scatter.push( workingSet, localCX ); if ( trackHistory ) history.push_back( "scatter" ); } else if ( instance->placement() == InstanceSymbol::PLACEMENT_CENTROID ) { CentroidFilter centroid; localCX = centroid.push( workingSet, localCX ); if ( trackHistory ) history.push_back( "centroid" ); } if ( altRequired ) { AltitudeFilter clamp; clamp.setPropertiesFromStyle( style ); localCX = clamp.push( workingSet, localCX ); if ( trackHistory ) history.push_back( "altitude" ); } SubstituteModelFilter sub( style ); // activate clustering sub.setClustering( *_options.clustering() ); // activate draw-instancing sub.setUseDrawInstanced( *_options.instancing() ); // activate feature naming if ( _options.featureName().isSet() ) sub.setFeatureNameExpr( *_options.featureName() ); osg::Node* node = sub.push( workingSet, localCX ); if ( node ) { if ( trackHistory ) history.push_back( "substitute" ); resultGroup->addChild( node ); // enable auto scaling on the group? if ( model && model->autoScale() == true ) { resultGroup->getOrCreateStateSet()->setRenderBinDetails(0, osgEarth::AUTO_SCALE_BIN ); } } } // extruded geometry if ( extrusion ) { if ( altRequired ) { AltitudeFilter clamp; clamp.setPropertiesFromStyle( style ); sharedCX = clamp.push( workingSet, sharedCX ); if ( trackHistory ) history.push_back( "altitude" ); altRequired = false; } ExtrudeGeometryFilter extrude; extrude.setStyle( style ); // apply per-feature naming if requested. if ( _options.featureName().isSet() ) extrude.setFeatureNameExpr( *_options.featureName() ); if ( _options.mergeGeometry().isSet() ) extrude.setMergeGeometry( *_options.mergeGeometry() ); osg::Node* node = extrude.push( workingSet, sharedCX ); if ( node ) { if ( trackHistory ) history.push_back( "extrude" ); resultGroup->addChild( node ); } } // simple geometry else if ( point || line || polygon ) { if ( altRequired ) { AltitudeFilter clamp; clamp.setPropertiesFromStyle( style ); sharedCX = clamp.push( workingSet, sharedCX ); if ( trackHistory ) history.push_back( "altitude" ); altRequired = false; } BuildGeometryFilter filter( style ); filter.maxGranularity() = *_options.maxGranularity(); filter.geoInterp() = *_options.geoInterp(); if ( _options.featureName().isSet() ) filter.featureName() = *_options.featureName(); osg::Node* node = filter.push( workingSet, sharedCX ); if ( node ) { if ( trackHistory ) history.push_back( "geometry" ); resultGroup->addChild( node ); } } if ( text || icon ) { if ( altRequired ) { AltitudeFilter clamp; clamp.setPropertiesFromStyle( style ); sharedCX = clamp.push( workingSet, sharedCX ); if ( trackHistory ) history.push_back( "altitude" ); altRequired = false; } BuildTextFilter filter( style ); osg::Node* node = filter.push( workingSet, sharedCX ); if ( node ) { if ( trackHistory ) history.push_back( "text" ); resultGroup->addChild( node ); } } if (Registry::capabilities().supportsGLSL()) { if ( _options.shaderPolicy() == SHADERPOLICY_GENERATE ) { // no ss cache because we will optimize later. Registry::shaderGenerator().run( resultGroup.get(), "osgEarth.GeomCompiler" ); } else if ( _options.shaderPolicy() == SHADERPOLICY_DISABLE ) { resultGroup->getOrCreateStateSet()->setAttributeAndModes( new osg::Program(), osg::StateAttribute::OFF | osg::StateAttribute::OVERRIDE ); if ( trackHistory ) history.push_back( "no shaders" ); } } // Optimize stateset sharing. if ( _options.optimizeStateSharing() == true ) { // Common state set cache? osg::ref_ptr<StateSetCache> sscache; if ( sharedCX.getSession() ) { // with a shared cache, don't combine statesets. They may be // in the live graph sscache = sharedCX.getSession()->getStateSetCache(); sscache->consolidateStateAttributes( resultGroup.get() ); } else { // isolated: perform full optimization sscache = new StateSetCache(); sscache->optimize( resultGroup.get() ); } if ( trackHistory ) history.push_back( "share state" ); } if ( _options.optimize() == true ) { OE_DEBUG << LC << "optimize begin" << std::endl; // Run the optimizer on the resulting graph int optimizations = osgUtil::Optimizer::FLATTEN_STATIC_TRANSFORMS | osgUtil::Optimizer::REMOVE_REDUNDANT_NODES | osgUtil::Optimizer::COMBINE_ADJACENT_LODS | osgUtil::Optimizer::SHARE_DUPLICATE_STATE | osgUtil::Optimizer::MERGE_GEOMETRY | osgUtil::Optimizer::CHECK_GEOMETRY | osgUtil::Optimizer::MERGE_GEODES | osgUtil::Optimizer::STATIC_OBJECT_DETECTION; osgUtil::Optimizer opt; opt.optimize(resultGroup.get(), optimizations); OE_DEBUG << LC << "optimize complete" << std::endl; if ( trackHistory ) history.push_back( "optimize" ); } //test: dump the tile to disk //osgDB::writeNodeFile( *(resultGroup.get()), "out.osg" ); #ifdef PROFILING static double totalTime = 0.0; static Threading::Mutex totalTimeMutex; osg::Timer_t p_end = osg::Timer::instance()->tick(); double t = osg::Timer::instance()->delta_s(p_start, p_end); totalTimeMutex.lock(); totalTime += t; totalTimeMutex.unlock(); OE_INFO << LC << "features = " << p_features << ", time = " << t << " s. cummulative = " << totalTime << " s." << std::endl; #endif if ( _options.validate() == true ) { OE_NOTICE << LC << "-- Start Debugging --\n"; std::stringstream buf; buf << "HISTORY "; for(std::vector<std::string>::iterator h = history.begin(); h != history.end(); ++h) buf << ".. " << *h; OE_NOTICE << LC << buf.str() << "\n"; osgEarth::GeometryValidator validator; resultGroup->accept(validator); OE_NOTICE << LC << "-- End Debugging --\n"; } return resultGroup.release(); }
osg::Node* GeometryCompiler::compile(FeatureList& workingSet, const Style& style, const FilterContext& context) { #ifdef PROFILING osg::Timer_t p_start = osg::Timer::instance()->tick(); unsigned p_features = workingSet.size(); #endif osg::ref_ptr<osg::Group> resultGroup = new osg::Group(); // create a filter context that will track feature data through the process FilterContext sharedCX = context; if ( !sharedCX.extent().isSet() && sharedCX.profile() ) { sharedCX.extent() = sharedCX.profile()->getExtent(); } // ref_ptr's to hold defaults in case we need them. osg::ref_ptr<PointSymbol> defaultPoint; osg::ref_ptr<LineSymbol> defaultLine; osg::ref_ptr<PolygonSymbol> defaultPolygon; // go through the Style and figure out which filters to use. const PointSymbol* point = style.get<PointSymbol>(); const LineSymbol* line = style.get<LineSymbol>(); const PolygonSymbol* polygon = style.get<PolygonSymbol>(); const ExtrusionSymbol* extrusion = style.get<ExtrusionSymbol>(); const AltitudeSymbol* altitude = style.get<AltitudeSymbol>(); const TextSymbol* text = style.get<TextSymbol>(); const MarkerSymbol* marker = style.get<MarkerSymbol>(); // to be deprecated const IconSymbol* icon = style.get<IconSymbol>(); const ModelSymbol* model = style.get<ModelSymbol>(); // check whether we need tessellation: if ( line && line->tessellation().isSet() ) { TemplateFeatureFilter<TessellateOperator> filter; filter.setNumPartitions( *line->tessellation() ); sharedCX = filter.push( workingSet, sharedCX ); } // if the style was empty, use some defaults based on the geometry type of the // first feature. if ( !point && !line && !polygon && !marker && !extrusion && !text && !model && !icon && workingSet.size() > 0 ) { Feature* first = workingSet.begin()->get(); Geometry* geom = first->getGeometry(); if ( geom ) { switch( geom->getComponentType() ) { case Geometry::TYPE_LINESTRING: case Geometry::TYPE_RING: defaultLine = new LineSymbol(); line = defaultLine.get(); break; case Geometry::TYPE_POINTSET: defaultPoint = new PointSymbol(); point = defaultPoint.get(); break; case Geometry::TYPE_POLYGON: defaultPolygon = new PolygonSymbol(); polygon = defaultPolygon.get(); break; case Geometry::TYPE_MULTI: case Geometry::TYPE_UNKNOWN: break; } } } // resample the geometry if necessary: if (_options.resampleMode().isSet()) { ResampleFilter resample; resample.resampleMode() = *_options.resampleMode(); if (_options.resampleMaxLength().isSet()) { resample.maxLength() = *_options.resampleMaxLength(); } sharedCX = resample.push( workingSet, sharedCX ); } // check whether we need to do elevation clamping: bool altRequired = _options.ignoreAltitudeSymbol() != true && altitude && ( altitude->clamping() != AltitudeSymbol::CLAMP_NONE || altitude->verticalOffset().isSet() || altitude->verticalScale().isSet() || altitude->script().isSet() ); // marker substitution -- to be deprecated in favor of model/icon if ( marker ) { // use a separate filter context since we'll be munging the data FilterContext markerCX = sharedCX; if ( marker->placement() == MarkerSymbol::PLACEMENT_RANDOM || marker->placement() == MarkerSymbol::PLACEMENT_INTERVAL ) { ScatterFilter scatter; scatter.setDensity( *marker->density() ); scatter.setRandom( marker->placement() == MarkerSymbol::PLACEMENT_RANDOM ); scatter.setRandomSeed( *marker->randomSeed() ); markerCX = scatter.push( workingSet, markerCX ); } else if ( marker->placement() == MarkerSymbol::PLACEMENT_CENTROID ) { CentroidFilter centroid; centroid.push( workingSet, markerCX ); } if ( altRequired ) { AltitudeFilter clamp; clamp.setPropertiesFromStyle( style ); markerCX = clamp.push( workingSet, markerCX ); // don't set this; we changed the input data. //altRequired = false; } SubstituteModelFilter sub( style ); sub.setClustering( *_options.clustering() ); sub.setUseDrawInstanced( *_options.instancing() ); if ( _options.featureName().isSet() ) sub.setFeatureNameExpr( *_options.featureName() ); osg::Node* node = sub.push( workingSet, markerCX ); if ( node ) { resultGroup->addChild( node ); } } // instance substitution (replaces marker) else if ( model ) { const InstanceSymbol* instance = model ? (const InstanceSymbol*)model : (const InstanceSymbol*)icon; // use a separate filter context since we'll be munging the data FilterContext localCX = sharedCX; if ( instance->placement() == InstanceSymbol::PLACEMENT_RANDOM || instance->placement() == InstanceSymbol::PLACEMENT_INTERVAL ) { ScatterFilter scatter; scatter.setDensity( *instance->density() ); scatter.setRandom( instance->placement() == InstanceSymbol::PLACEMENT_RANDOM ); scatter.setRandomSeed( *instance->randomSeed() ); localCX = scatter.push( workingSet, localCX ); } else if ( instance->placement() == InstanceSymbol::PLACEMENT_CENTROID ) { CentroidFilter centroid; centroid.push( workingSet, localCX ); } if ( altRequired ) { AltitudeFilter clamp; clamp.setPropertiesFromStyle( style ); localCX = clamp.push( workingSet, localCX ); } SubstituteModelFilter sub( style ); // activate clustering sub.setClustering( *_options.clustering() ); // activate draw-instancing sub.setUseDrawInstanced( *_options.instancing() ); // activate feature naming if ( _options.featureName().isSet() ) sub.setFeatureNameExpr( *_options.featureName() ); osg::Node* node = sub.push( workingSet, localCX ); if ( node ) { resultGroup->addChild( node ); // enable auto scaling on the group? if ( model && model->autoScale() == true ) { resultGroup->getOrCreateStateSet()->setRenderBinDetails(0, osgEarth::AUTO_SCALE_BIN ); } } } // extruded geometry if ( extrusion ) { if ( altRequired ) { AltitudeFilter clamp; clamp.setPropertiesFromStyle( style ); sharedCX = clamp.push( workingSet, sharedCX ); altRequired = false; } ExtrudeGeometryFilter extrude; extrude.setStyle( style ); // Activate texture arrays if the GPU supports them and if the user // hasn't disabled them. extrude.useTextureArrays() = Registry::capabilities().supportsTextureArrays() && _options.useTextureArrays() == true; // apply per-feature naming if requested. if ( _options.featureName().isSet() ) extrude.setFeatureNameExpr( *_options.featureName() ); if ( _options.useVertexBufferObjects().isSet()) extrude.useVertexBufferObjects() = *_options.useVertexBufferObjects(); osg::Node* node = extrude.push( workingSet, sharedCX ); if ( node ) { resultGroup->addChild( node ); } } // simple geometry else if ( point || line || polygon ) { if ( altRequired ) { AltitudeFilter clamp; clamp.setPropertiesFromStyle( style ); sharedCX = clamp.push( workingSet, sharedCX ); altRequired = false; } BuildGeometryFilter filter( style ); if ( _options.maxGranularity().isSet() ) filter.maxGranularity() = *_options.maxGranularity(); if ( _options.geoInterp().isSet() ) filter.geoInterp() = *_options.geoInterp(); if ( _options.featureName().isSet() ) filter.featureName() = *_options.featureName(); osg::Node* node = filter.push( workingSet, sharedCX ); if ( node ) { resultGroup->addChild( node ); } } if ( text || icon ) { if ( altRequired ) { AltitudeFilter clamp; clamp.setPropertiesFromStyle( style ); sharedCX = clamp.push( workingSet, sharedCX ); altRequired = false; } BuildTextFilter filter( style ); osg::Node* node = filter.push( workingSet, sharedCX ); if ( node ) { resultGroup->addChild( node ); } } if (Registry::capabilities().supportsGLSL()) { if ( _options.shaderPolicy() == SHADERPOLICY_GENERATE ) { // no ss cache because we will optimize later. Registry::shaderGenerator().run( resultGroup.get(), "osgEarth.GeomCompiler" ); } else if ( _options.shaderPolicy() == SHADERPOLICY_DISABLE ) { resultGroup->getOrCreateStateSet()->setAttributeAndModes( new osg::Program(), osg::StateAttribute::OFF | osg::StateAttribute::OVERRIDE ); } } // Optimize stateset sharing. if ( _options.optimizeStateSharing() == true ) { // Common state set cache? osg::ref_ptr<StateSetCache> sscache; if ( sharedCX.getSession() ) { // with a shared cache, don't combine statesets. They may be // in the live graph sscache = sharedCX.getSession()->getStateSetCache(); sscache->consolidateStateAttributes( resultGroup.get() ); } else { // isolated: perform full optimization sscache = new StateSetCache(); sscache->optimize( resultGroup.get() ); } } //test: dump the tile to disk //osgDB::writeNodeFile( *(resultGroup.get()), "out.osg" ); #ifdef PROFILING osg::Timer_t p_end = osg::Timer::instance()->tick(); OE_INFO << LC << "features = " << p_features << ", time = " << osg::Timer::instance()->delta_s(p_start, p_end) << " s." << std::endl; #endif return resultGroup.release(); }