Feature* FeatureListSource::getFeature( FeatureID fid ) { for (FeatureList::iterator itr = _features.begin(); itr != _features.end(); ++itr) { if (itr->get()->getFID() == fid) { return itr->get(); } } return NULL; }
FilterContext TransformFilter::push( FeatureList& input, FilterContext& incx ) { _bbox = osg::BoundingBoxd(); // first transform all the points into the output SRS, collecting a bounding box as we go: bool ok = true; for( FeatureList::iterator i = input.begin(); i != input.end(); i++ ) if ( !push( i->get(), incx ) ) ok = false; FilterContext outcx( incx ); outcx.isGeocentric() = _makeGeocentric; if ( _outputSRS.valid() ) { if ( incx.extent()->isValid() ) outcx.profile() = new FeatureProfile( incx.extent()->transform( _outputSRS.get() ) ); else outcx.profile() = new FeatureProfile( incx.profile()->getExtent().transform( _outputSRS.get() ) ); } // set the reference frame to shift data to the centroid. This will // prevent floating point precision errors in the openGL pipeline for // properly gridded data. if ( _bbox.valid() && _localize ) { // create a suitable reference frame: osg::Matrixd localizer; if ( _makeGeocentric ) { localizer = createGeocentricInvRefFrame( _bbox.center(), _outputSRS.get() ); localizer.invert( localizer ); } else { localizer = osg::Matrixd::translate( -_bbox.center() ); } // localize the geometry relative to the reference frame. for( FeatureList::iterator i = input.begin(); i != input.end(); i++ ) { localizeGeometry( i->get(), localizer ); } outcx.setReferenceFrame( localizer ); } return outcx; }
FilterContext ScaleFilter::push( FeatureList& input, FilterContext& cx ) { for( FeatureList::iterator i = input.begin(); i != input.end(); ++i ) { Feature* input = i->get(); if ( input && input->getGeometry() ) { Bounds envelope = input->getGeometry()->getBounds(); // now scale and shift everything GeometryIterator scale_iter( input->getGeometry() ); while( scale_iter.hasMore() ) { Geometry* geom = scale_iter.next(); for( osg::Vec3dArray::iterator v = geom->begin(); v != geom->end(); v++ ) { double xr = (v->x() - envelope.xMin()) / envelope.width(); v->x() += (xr - 0.5) * _scale; double yr = (v->y() - envelope.yMin()) / envelope.height(); v->y() += (yr - 0.5) * _scale; } } } } return cx; }
const FeatureProfile* FeatureListSource::createFeatureProfile() { const SpatialReference* srs = 0L; osgEarth::Bounds bounds; if ( !_features.empty() ) { // Get the SRS of the first feature srs = _features.front()->getSRS(); // Compute the extent of the features for (FeatureList::iterator itr = _features.begin(); itr != _features.end(); ++itr) { Feature* feature = itr->get(); if (feature->getGeometry()) { bounds.expandBy( feature->getGeometry()->getBounds() ); } } } // return the new profile, or a default extent if the profile could not be computed. if ( srs && bounds.isValid() ) return new FeatureProfile( GeoExtent(srs, bounds) ); else return new FeatureProfile( _defaultExtent ); }
FilterContext ScriptFilter::push( FeatureList& input, FilterContext& context ) { if ( !isSupported() ) { OE_WARN << "ScriptFilter support not enabled" << std::endl; return context; } if (!_engine.valid()) { OE_WARN << "No scripting engine\n"; return context; } bool ok = true; for( FeatureList::iterator i = input.begin(); i != input.end(); ) { if ( push( i->get(), context ) ) { ++i; } else { i = input.erase(i); } } return context; }
void ImageOverlay::init() { OpenThreads::ScopedLock< OpenThreads::Mutex > lock(_mutex); if (_root->getNumChildren() > 0) { _root->removeChildren(0, _root->getNumChildren()); } if ( !_clampCallback.valid() ) { _clampCallback = new TerrainCallbackAdapter<ImageOverlay>(this); } if ( getMapNode() ) { const SpatialReference* mapSRS = getMapNode()->getMapSRS(); // calculate a bounding polytope in world space (for mesh clamping): osg::ref_ptr<Feature> f = new Feature( new Polygon(), mapSRS->getGeodeticSRS() ); Geometry* g = f->getGeometry(); g->push_back( osg::Vec3d(_lowerLeft.x(), _lowerLeft.y(), 0) ); g->push_back( osg::Vec3d(_lowerRight.x(), _lowerRight.y(), 0) ); g->push_back( osg::Vec3d(_upperRight.x(), _upperRight.y(), 0) ); g->push_back( osg::Vec3d(_upperLeft.x(), _upperLeft.y(), 0) ); f->getWorldBoundingPolytope( getMapNode()->getMapSRS(), _boundingPolytope ); FeatureList features; if (!mapSRS->isGeographic()) { f->splitAcrossDateLine(features); } else { features.push_back( f ); } for (FeatureList::iterator itr = features.begin(); itr != features.end(); ++itr) { _root->addChild(createNode(itr->get(), features.size() > 1)); } _dirty = false; // Set the annotation up for auto-clamping. We always need to auto-clamp a draped image // so that the mesh roughly conforms with the surface, otherwise the draping routine // might clip it. Style style; style.getOrCreate<AltitudeSymbol>()->clamping() = AltitudeSymbol::CLAMP_RELATIVE_TO_TERRAIN; applyStyle( style ); setLightingIfNotSet( false ); getMapNode()->getTerrain()->addTerrainCallback( _clampCallback.get() ); clamp( getMapNode()->getTerrain()->getGraph(), getMapNode()->getTerrain() ); } }
void AltitudeFilter::pushAndDontClamp( FeatureList& features, FilterContext& cx ) { NumericExpression scaleExpr; if ( _altitude.valid() && _altitude->verticalScale().isSet() ) scaleExpr = *_altitude->verticalScale(); NumericExpression offsetExpr; if ( _altitude.valid() && _altitude->verticalOffset().isSet() ) offsetExpr = *_altitude->verticalOffset(); for( FeatureList::iterator i = features.begin(); i != features.end(); ++i ) { Feature* feature = i->get(); // run a symbol script if present. if ( _altitude.valid() && _altitude->script().isSet() ) { StringExpression temp( _altitude->script().get() ); feature->eval( temp, &cx ); } double minHAT = DBL_MAX; double maxHAT = -DBL_MAX; double scaleZ = 1.0; if ( _altitude.valid() && _altitude->verticalScale().isSet() ) scaleZ = feature->eval( scaleExpr, &cx ); double offsetZ = 0.0; if ( _altitude.valid() && _altitude->verticalOffset().isSet() ) offsetZ = feature->eval( offsetExpr, &cx ); GeometryIterator gi( feature->getGeometry() ); while( gi.hasMore() ) { Geometry* geom = gi.next(); for( Geometry::iterator g = geom->begin(); g != geom->end(); ++g ) { g->z() *= scaleZ; g->z() += offsetZ; if ( g->z() < minHAT ) minHAT = g->z(); if ( g->z() > maxHAT ) maxHAT = g->z(); } } if ( minHAT != DBL_MAX ) { feature->set( "__min_hat", minHAT ); feature->set( "__max_hat", maxHAT ); } } }
virtual FilterContext push( FeatureList& input, FilterContext& context ) { for (FeatureList::iterator itr = input.begin(); itr != input.end(); itr++) { //Change the value of the attribute if (_key.isSet() && _value.isSet()) { itr->get()->set(*_key, std::string(*_value)); } } return context; }
FilterContext ClampFilter::push( FeatureList& features, const FilterContext& cx ) { const Session* session = cx.getSession(); if ( !session ) { OE_WARN << LC << "No session - session is required for elevation clamping" << std::endl; return cx; } // the map against which we'll be doing elevation clamping MapFrame mapf = session->createMapFrame( Map::ELEVATION_LAYERS ); const SpatialReference* mapSRS = mapf.getProfile()->getSRS(); const SpatialReference* featureSRS = cx.profile()->getSRS(); bool isGeocentric = session->getMapInfo().isGeocentric(); // establish an elevation query interface based on the features' SRS. ElevationQuery eq( mapf ); for( FeatureList::iterator i = features.begin(); i != features.end(); ++i ) { Feature* feature = i->get(); GeometryIterator gi( feature->getGeometry() ); while( gi.hasMore() ) { Geometry* geom = gi.next(); if ( isGeocentric ) { // convert to map coords: cx.toWorld( geom ); mapSRS->transformFromECEF( geom ); // populate the elevations: eq.getElevations( geom, mapSRS ); // convert back to geocentric: mapSRS->transformToECEF( geom ); cx.toLocal( geom ); } else { // clamps the entire array to the highest available resolution. eq.getElevations( geom, featureSRS ); } } } return cx; }
FeatureCursor* FeatureListSource::createFeatureCursor( const Symbology::Query& query ) { //Create a copy of all of the features before returning the cursor. //The processing filters in osgEarth can modify the features as they are operating and we don't want our original data destroyed. FeatureList cursorFeatures; for (FeatureList::iterator itr = _features.begin(); itr != _features.end(); ++itr) { Feature* feature = new osgEarth::Features::Feature(*(itr->get()), osg::CopyOp::DEEP_COPY_ALL); cursorFeatures.push_back( feature ); } return new FeatureListCursor( cursorFeatures ); }
bool FeatureListSource::deleteFeature(FeatureID fid) { for (FeatureList::iterator itr = _features.begin(); itr != _features.end(); ++itr) { if (itr->get()->getFID() == fid) { _features.erase( itr ); dirty(); return true; } } return false; }
FilterContext ResampleFilter::push( FeatureList& input, FilterContext& context ) { if ( !isSupported() ) { OE_WARN << "ResampleFilter support not enabled" << std::endl; return context; } bool ok = true; for( FeatureList::iterator i = input.begin(); i != input.end(); ++i ) if ( !push( i->get(), context ) ) ok = false; return context; }
FilterContext BufferFilter::push( FeatureList& input, FilterContext& context ) { if ( !isSupported() ) { OE_WARN << "BufferFilter support not enabled - please compile osgEarth with GEOS" << std::endl; return context; } //OE_NOTICE << "Buffer: input = " << input.size() << " features" << std::endl; bool ok = true; for( FeatureList::iterator i = input.begin(); i != input.end(); ++i ) if ( !push( i->get(), context ) ) ok = false; return context; }
FilterContext ScatterFilter::push(FeatureList& features, FilterContext& context ) { if ( !isSupported() ) { OE_WARN << LC << "support for this filter is not enabled" << std::endl; return context; } // seed the random number generator so the randomness is the same each time _prng = Random( _randomSeed, Random::METHOD_FAST ); for( FeatureList::iterator i = features.begin(); i != features.end(); ++i ) { Feature* f = i->get(); Geometry* geom = f->getGeometry(); if ( !geom ) continue; const SpatialReference* geomSRS = context.profile()->getSRS(); osg::ref_ptr< PointSet > points = new PointSet(); if ( geom->getComponentType() == Geometry::TYPE_POLYGON ) { polyScatter( geom, geomSRS, context, points.get() ); } else if ( geom->getComponentType() == Geometry::TYPE_LINESTRING || geom->getComponentType() == Geometry::TYPE_RING ) { lineScatter( geom, geomSRS, context, points.get() ); } else { points = static_cast< PointSet*>(geom->cloneAs(Geometry::TYPE_POINTSET)); } // replace the source geometry with the scattered points. f->setGeometry( points.get() ); } return context; }
FilterContext CentroidFilter::push(FeatureList& features, FilterContext& context ) { for( FeatureList::iterator i = features.begin(); i != features.end(); ++i ) { Feature* f = i->get(); Geometry* geom = f->getGeometry(); if ( !geom ) continue; PointSet* newGeom = new PointSet(); newGeom->push_back( geom->getBounds().center() ); f->setGeometry( newGeom ); } return context; }
FilterContext BufferFilter::push( FeatureList& input, FilterContext& context ) { if ( !isSupported() ) { OE_WARN << "BufferFilter support not enabled - please compile osgEarth with GEOS" << std::endl; return context; } //OE_NOTICE << "Buffer: input = " << input.size() << " features" << std::endl; for( FeatureList::iterator i = input.begin(); i != input.end(); ) { Feature* feature = i->get(); if ( !feature || !feature->getGeometry() ) continue; osg::ref_ptr<Symbology::Geometry> output; Symbology::BufferParameters params; params._capStyle = _capStyle == Stroke::LINECAP_ROUND ? Symbology::BufferParameters::CAP_ROUND : _capStyle == Stroke::LINECAP_SQUARE ? Symbology::BufferParameters::CAP_SQUARE : _capStyle == Stroke::LINECAP_FLAT ? Symbology::BufferParameters::CAP_FLAT : Symbology::BufferParameters::CAP_SQUARE; params._cornerSegs = _numQuadSegs; if ( feature->getGeometry()->buffer( _distance.value(), output, params ) ) { feature->setGeometry( output.get() ); ++i; } else { i = input.erase( i ); OE_DEBUG << LC << "feature " << feature->getFID() << " yielded no geometry" << std::endl; } } return context; }
FilterContext ConvertTypeFilter::push( FeatureList& input, FilterContext& context ) { if ( !isSupported() ) { OE_WARN << "ConvertTypeFilter support not enabled" << std::endl; return context; } bool ok = true; for( FeatureList::iterator i = input.begin(); i != input.end(); ++i ) { Feature* input = i->get(); if ( input && input->getGeometry() && input->getGeometry()->getComponentType() != _toType ) { input->setGeometry( input->getGeometry()->cloneAs(_toType) ); } } return context; }
std::string Feature::featuresToGeoJSON( FeatureList& features) { std::stringstream buf; buf << "{\"type\": \"FeatureCollection\", \"features\": ["; FeatureList::iterator last = features.end(); last--; for (FeatureList::iterator i = features.begin(); i != features.end(); i++) { buf << i->get()->getGeoJSON(); if (i != last) { buf << ","; } } buf << "]}"; return buf.str(); }
bool ExtrudeGeometryFilter::process( FeatureList& features, FilterContext& context ) { // seed our random number generators Random wallSkinPRNG( _wallSkinSymbol.valid()? *_wallSkinSymbol->randomSeed() : 0, Random::METHOD_FAST ); Random roofSkinPRNG( _roofSkinSymbol.valid()? *_roofSkinSymbol->randomSeed() : 0, Random::METHOD_FAST ); for( FeatureList::iterator f = features.begin(); f != features.end(); ++f ) { Feature* input = f->get(); GeometryIterator iter( input->getGeometry(), false ); while( iter.hasMore() ) { Geometry* part = iter.next(); osg::ref_ptr<osg::Geometry> walls = new osg::Geometry(); walls->setUseVertexBufferObjects( _useVertexBufferObjects.get() ); osg::ref_ptr<osg::Geometry> rooflines = 0L; osg::ref_ptr<osg::Geometry> baselines = 0L; osg::ref_ptr<osg::Geometry> outlines = 0L; if ( part->getType() == Geometry::TYPE_POLYGON ) { rooflines = new osg::Geometry(); rooflines->setUseVertexBufferObjects( _useVertexBufferObjects.get() ); // prep the shapes by making sure all polys are open: static_cast<Polygon*>(part)->open(); } // fire up the outline geometry if we have a line symbol. if ( _outlineSymbol != 0L ) { outlines = new osg::Geometry(); outlines->setUseVertexBufferObjects( _useVertexBufferObjects.get() ); } // make a base cap if we're doing stencil volumes. if ( _makeStencilVolume ) { baselines = new osg::Geometry(); baselines->setUseVertexBufferObjects( _useVertexBufferObjects.get() ); } // calculate the extrusion height: float height; if ( _heightCallback.valid() ) { height = _heightCallback->operator()(input, context); } else if ( _heightExpr.isSet() ) { height = input->eval( _heightExpr.mutable_value(), &context ); } else { height = *_extrusionSymbol->height(); } // calculate the height offset from the base: float offset = 0.0; if ( _heightOffsetExpr.isSet() ) { offset = input->eval( _heightOffsetExpr.mutable_value(), &context ); } osg::ref_ptr<osg::StateSet> wallStateSet; osg::ref_ptr<osg::StateSet> roofStateSet; // calculate the wall texturing: SkinResource* wallSkin = 0L; if ( _wallSkinSymbol.valid() ) { if ( _wallResLib.valid() ) { SkinSymbol querySymbol( *_wallSkinSymbol.get() ); querySymbol.objectHeight() = fabs(height) - offset; wallSkin = _wallResLib->getSkin( &querySymbol, wallSkinPRNG, context.getDBOptions() ); } else { //TODO: simple single texture? } } // calculate the rooftop texture: SkinResource* roofSkin = 0L; if ( _roofSkinSymbol.valid() ) { if ( _roofResLib.valid() ) { SkinSymbol querySymbol( *_roofSkinSymbol.get() ); roofSkin = _roofResLib->getSkin( &querySymbol, roofSkinPRNG, context.getDBOptions() ); } else { //TODO: simple single texture? } } // Build the data model for the structure. Structure structure; buildStructure( part, height, offset, _extrusionSymbol->flatten().get(), wallSkin, roofSkin, structure, context); // Create the walls. if ( walls.valid() ) { osg::Vec4f wallColor(1,1,1,1), wallBaseColor(1,1,1,1); if ( _wallPolygonSymbol.valid() ) { wallColor = _wallPolygonSymbol->fill()->color(); } if ( _extrusionSymbol->wallGradientPercentage().isSet() ) { wallBaseColor = Color(wallColor).brightness( 1.0 - *_extrusionSymbol->wallGradientPercentage() ); } else { wallBaseColor = wallColor; } buildWallGeometry(structure, walls.get(), wallColor, wallBaseColor, wallSkin); if ( wallSkin ) { // Get a stateset for the individual wall stateset context.resourceCache()->getOrCreateStateSet( wallSkin, wallStateSet ); } } // tessellate and add the roofs if necessary: if ( rooflines.valid() ) { osg::Vec4f roofColor(1,1,1,1); if ( _roofPolygonSymbol.valid() ) { roofColor = _roofPolygonSymbol->fill()->color(); } buildRoofGeometry(structure, rooflines.get(), roofColor, roofSkin); if ( roofSkin ) { // Get a stateset for the individual roof skin context.resourceCache()->getOrCreateStateSet( roofSkin, roofStateSet ); } } if ( outlines.valid() ) { osg::Vec4f outlineColor(1,1,1,1); if ( _outlineSymbol.valid() ) { outlineColor = _outlineSymbol->stroke()->color(); } float minCreaseAngle = _outlineSymbol->creaseAngle().value(); buildOutlineGeometry(structure, outlines.get(), outlineColor, minCreaseAngle); } if ( baselines.valid() ) { //TODO. osgUtil::Tessellator tess; tess.setTessellationType( osgUtil::Tessellator::TESS_TYPE_GEOMETRY ); tess.setWindingType( osgUtil::Tessellator::TESS_WINDING_ODD ); tess.retessellatePolygons( *(baselines.get()) ); } // Set up for feature naming and feature indexing: std::string name; if ( !_featureNameExpr.empty() ) name = input->eval( _featureNameExpr, &context ); FeatureSourceIndex* index = context.featureIndex(); if ( walls.valid() ) { addDrawable( walls.get(), wallStateSet.get(), name, input, index ); } if ( rooflines.valid() ) { addDrawable( rooflines.get(), roofStateSet.get(), name, input, index ); } if ( baselines.valid() ) { addDrawable( baselines.get(), 0L, name, input, index ); } if ( outlines.valid() ) { addDrawable( outlines.get(), 0L, name, input, index ); } } } return true; }
bool BuildGeometryFilter::process( FeatureList& features, const FilterContext& context ) { bool makeECEF = false; const SpatialReference* featureSRS = 0L; const SpatialReference* mapSRS = 0L; if ( context.isGeoreferenced() ) { makeECEF = context.getSession()->getMapInfo().isGeocentric(); featureSRS = context.extent()->getSRS(); mapSRS = context.getSession()->getMapInfo().getProfile()->getSRS(); } for( FeatureList::iterator f = features.begin(); f != features.end(); ++f ) { Feature* input = f->get(); GeometryIterator parts( input->getGeometry(), false ); while( parts.hasMore() ) { Geometry* part = parts.next(); // skip empty geometry if ( part->size() == 0 ) continue; const Style& myStyle = input->style().isSet() ? *input->style() : _style; bool setLinePropsHere = input->style().isSet(); // otherwise it will be set globally, we assume float width = 1.0f; bool hasPolyOutline = false; const PointSymbol* pointSymbol = myStyle.get<PointSymbol>(); const LineSymbol* lineSymbol = myStyle.get<LineSymbol>(); const PolygonSymbol* polySymbol = myStyle.get<PolygonSymbol>(); // resolve the geometry type from the component type and the symbology: Geometry::Type renderType = Geometry::TYPE_UNKNOWN; // First priority is a matching part type and symbol: if ( polySymbol != 0L && part->getType() == Geometry::TYPE_POLYGON ) { renderType = Geometry::TYPE_POLYGON; } else if ( lineSymbol != 0L && part->isLinear() ) { renderType = part->getType(); } else if ( pointSymbol != 0L && part->getType() == Geometry::TYPE_POINTSET ) { renderType = Geometry::TYPE_POINTSET; } // Second priority is the symbol: else if ( polySymbol != 0L ) { renderType = Geometry::TYPE_POLYGON; } else if ( lineSymbol != 0L ) { if ( part->getType() == Geometry::TYPE_POLYGON ) renderType = Geometry::TYPE_RING; else renderType = Geometry::TYPE_LINESTRING; } else if ( pointSymbol != 0L ) { renderType = Geometry::TYPE_POINTSET; } // No symbol? just use the geometry type. else { renderType = part->getType(); } // validate the geometry: if ( renderType == Geometry::TYPE_POLYGON && part->size() < 3 ) continue; else if ( (renderType == Geometry::TYPE_LINESTRING || renderType == Geometry::TYPE_RING) && part->size() < 2 ) continue; // resolve the color: osg::Vec4f primaryColor = polySymbol ? osg::Vec4f(polySymbol->fill()->color()) : lineSymbol ? osg::Vec4f(lineSymbol->stroke()->color()) : pointSymbol ? osg::Vec4f(pointSymbol->fill()->color()) : osg::Vec4f(1,1,1,1); osg::Geometry* osgGeom = new osg::Geometry(); osgGeom->setUseVertexBufferObjects( _useVertexBufferObjects.value() ); if ( _featureNameExpr.isSet() ) { const std::string& name = input->eval( _featureNameExpr.mutable_value(), &context ); osgGeom->setName( name ); } // build the geometry: osg::Vec3Array* allPoints = 0L; if ( renderType == Geometry::TYPE_POLYGON ) { buildPolygon(part, featureSRS, mapSRS, makeECEF, true, osgGeom); allPoints = static_cast<osg::Vec3Array*>( osgGeom->getVertexArray() ); } else { // line or point geometry GLenum primMode = renderType == Geometry::TYPE_LINESTRING ? GL_LINE_STRIP : renderType == Geometry::TYPE_RING ? GL_LINE_LOOP : GL_POINTS; allPoints = new osg::Vec3Array(); transformAndLocalize( part->asVector(), featureSRS, allPoints, mapSRS, _world2local, makeECEF ); osgGeom->addPrimitiveSet( new osg::DrawArrays( primMode, 0, part->size() ) ); osgGeom->setVertexArray( allPoints ); applyLineAndPointSymbology( osgGeom->getOrCreateStateSet(), lineSymbol, pointSymbol ); if ( primMode == GL_POINTS && allPoints->size() == 1 ) { const osg::Vec3d& center = (*allPoints)[0]; osgGeom->setInitialBound( osg::BoundingBox(center-osg::Vec3(.5,.5,.5), center+osg::Vec3(.5,.5,.5)) ); } } if (allPoints->getVertexBufferObject()) allPoints->getVertexBufferObject()->setUsage(GL_STATIC_DRAW_ARB); // subdivide the mesh if necessary to conform to an ECEF globe: if ( makeECEF && renderType != Geometry::TYPE_POINTSET ) { // check for explicit tessellation disable: const LineSymbol* line = _style.get<LineSymbol>(); bool disableTess = line && line->tessellation().isSetTo(0); if ( makeECEF && !disableTess ) { double threshold = osg::DegreesToRadians( *_maxAngle_deg ); OE_DEBUG << "Running mesh subdivider with threshold " << *_maxAngle_deg << std::endl; MeshSubdivider ms( _world2local, _local2world ); //ms.setMaxElementsPerEBO( INT_MAX ); if ( input->geoInterp().isSet() ) ms.run( *osgGeom, threshold, *input->geoInterp() ); else ms.run( *osgGeom, threshold, *_geoInterp ); } } // assign the primary color: #if USE_SINGLE_COLOR osg::Vec4Array* colors = new osg::Vec4Array( 1 ); (*colors)[0] = primaryColor; osgGeom->setColorBinding( osg::Geometry::BIND_OVERALL ); #else osg::Vec4Array* colors = new osg::Vec4Array( osgGeom->getVertexArray()->getNumElements() ); //allPoints->size() ); for(unsigned c=0; c<colors->size(); ++c) (*colors)[c] = primaryColor; osgGeom->setColorBinding( osg::Geometry::BIND_PER_VERTEX ); #endif osgGeom->setColorArray( colors ); _geode->addDrawable( osgGeom ); // record the geometry's primitive set(s) in the index: if ( context.featureIndex() ) context.featureIndex()->tagPrimitiveSets( osgGeom, input ); // build secondary geometry, if necessary (polygon outlines) if ( renderType == Geometry::TYPE_POLYGON && lineSymbol ) { // polygon offset on the poly so the outline doesn't z-fight osgGeom->getOrCreateStateSet()->setAttributeAndModes( new osg::PolygonOffset(1,1), 1 ); osg::Geometry* outline = new osg::Geometry(); outline->setUseVertexBufferObjects( _useVertexBufferObjects.value() ); buildPolygon(part, featureSRS, mapSRS, makeECEF, false, outline); if ( outline->getVertexArray()->getVertexBufferObject() ) outline->getVertexArray()->getVertexBufferObject()->setUsage(GL_STATIC_DRAW_ARB); osg::Vec4f outlineColor = lineSymbol->stroke()->color(); osg::Vec4Array* outlineColors = new osg::Vec4Array(); #if USE_SINGLE_COLOR outlineColors->reserve(1); outlineColors->push_back( outlineColor ); outline->setColorBinding( osg::Geometry::BIND_OVERALL ); #else unsigned pcount = part->getTotalPointCount(); outlineColors->reserve( pcount ); for( unsigned c=0; c < pcount; ++c ) outlineColors->push_back( outlineColor ); outline->setColorBinding( osg::Geometry::BIND_PER_VERTEX ); #endif outline->setColorArray(outlineColors); // check for explicit tessellation disable: bool disableTess = lineSymbol && lineSymbol->tessellation().isSetTo(0); // subdivide if necessary. if ( makeECEF && !disableTess ) { double threshold = osg::DegreesToRadians( *_maxAngle_deg ); OE_DEBUG << "Running mesh subdivider for outlines with threshold " << *_maxAngle_deg << std::endl; MeshSubdivider ms( _world2local, _local2world ); if ( input->geoInterp().isSet() ) ms.run( *outline, threshold, *input->geoInterp() ); else ms.run( *outline, threshold, *_geoInterp ); } applyLineAndPointSymbology( outline->getOrCreateStateSet(), lineSymbol, 0L ); // make normals before adding an outline osgUtil::SmoothingVisitor sv; _geode->accept( sv ); _geode->addDrawable( outline ); //_featureNode->addDrawable( outline, input->getFID() ); // Mark each primitive set with its feature ID. if ( context.featureIndex() ) context.featureIndex()->tagPrimitiveSets( outline, input ); } } } return true; }
void FeatureNode::build() { if ( !_clampCallback.valid() ) _clampCallback = new ClampCallback(this); _attachPoint = 0L; // if there is existing geometry, kill it this->removeChildren( 0, this->getNumChildren() ); if ( !getMapNode() ) return; if ( _features.empty() ) return; const Style &style = getStyle(); // compilation options. GeometryCompilerOptions options = _options; // figure out what kind of altitude manipulation we need to perform. AnnotationUtils::AltitudePolicy ap; AnnotationUtils::getAltitudePolicy( style, ap ); // If we're doing auto-clamping on the CPU, shut off compiler map clamping // clamping since it would be redundant. if ( ap.sceneClamping ) { options.ignoreAltitudeSymbol() = true; } _clamperData.clear(); osg::Node* node = _compiled.get(); if (_needsRebuild || !_compiled.valid() ) { // Clone the Features before rendering as the GeometryCompiler and it's filters can change the coordinates // of the geometry when performing localization or converting to geocentric. _extent = GeoExtent::INVALID; FeatureList clone; for(FeatureList::iterator itr = _features.begin(); itr != _features.end(); ++itr) { Feature* feature = new Feature( *itr->get(), osg::CopyOp::DEEP_COPY_ALL); GeoExtent featureExtent(feature->getSRS(), feature->getGeometry()->getBounds()); if (_extent.isInvalid()) { _extent = featureExtent; } else { _extent.expandToInclude( featureExtent ); } clone.push_back( feature ); } // prep the compiler: GeometryCompiler compiler( options ); Session* session = new Session( getMapNode()->getMap(), _styleSheet.get() ); FilterContext context( session, new FeatureProfile( _extent ), _extent, _index); _compiled = compiler.compile( clone, style, context ); node = _compiled.get(); _needsRebuild = false; // Compute the world bounds osg::BoundingSphered bounds; for( FeatureList::iterator itr = _features.begin(); itr != _features.end(); ++itr) { osg::BoundingSphered bs; itr->get()->getWorldBound(getMapNode()->getMapSRS(), bs); bounds.expandBy(bs); } // The polytope will ensure we only clamp to intersecting tiles: Feature::getWorldBoundingPolytope(bounds, getMapNode()->getMapSRS(), _featurePolytope); } if ( node ) { if ( AnnotationUtils::styleRequiresAlphaBlending( style ) && getStyle().get<ExtrusionSymbol>() ) { node = AnnotationUtils::installTwoPassAlpha( node ); } _attachPoint = new osg::Group(); _attachPoint->addChild( node ); // Draped (projected) geometry if ( ap.draping ) { DrapeableNode* d = new DrapeableNode(); d->addChild( _attachPoint ); this->addChild( d ); } // GPU-clamped geometry else if ( ap.gpuClamping ) { ClampableNode* clampable = new ClampableNode(); clampable->addChild( _attachPoint ); this->addChild( clampable ); } else { this->addChild( _attachPoint ); // set default lighting based on whether we are extruding: setDefaultLighting( style.has<ExtrusionSymbol>() ); } applyRenderSymbology(style); if ( getMapNode()->getTerrain() ) { if ( ap.sceneClamping ) { // Need dynamic data variance since scene clamping will change the verts SetDataVarianceVisitor sdv(osg::Object::DYNAMIC); this->accept(sdv); getMapNode()->getTerrain()->addTerrainCallback(_clampCallback.get()); clamp(getMapNode()->getTerrain()->getGraph(), getMapNode()->getTerrain()); } else { getMapNode()->getTerrain()->removeTerrainCallback( _clampCallback.get() ); } } } }
void AltitudeFilter::pushAndClamp( FeatureList& features, FilterContext& cx ) { const Session* session = cx.getSession(); // the map against which we'll be doing elevation clamping //MapFrame mapf = session->createMapFrame( Map::ELEVATION_LAYERS ); MapFrame mapf = session->createMapFrame( (Map::ModelParts)(Map::TERRAIN_LAYERS | Map::MODEL_LAYERS) ); const SpatialReference* mapSRS = mapf.getProfile()->getSRS(); osg::ref_ptr<const SpatialReference> featureSRS = cx.profile()->getSRS(); // establish an elevation query interface based on the features' SRS. ElevationQuery eq( mapf ); // want a result even if it's low res eq.setFallBackOnNoData( true ); NumericExpression scaleExpr; if ( _altitude->verticalScale().isSet() ) scaleExpr = *_altitude->verticalScale(); NumericExpression offsetExpr; if ( _altitude->verticalOffset().isSet() ) offsetExpr = *_altitude->verticalOffset(); // whether to record the min/max height-above-terrain values. bool collectHATs = _altitude->clamping() == AltitudeSymbol::CLAMP_RELATIVE_TO_TERRAIN || _altitude->clamping() == AltitudeSymbol::CLAMP_ABSOLUTE; // whether to clamp every vertex (or just the centroid) bool perVertex = _altitude->binding() == AltitudeSymbol::BINDING_VERTEX; // whether the SRS's have a compatible vertical datum. bool vertEquiv = featureSRS->isVertEquivalentTo( mapSRS ); for( FeatureList::iterator i = features.begin(); i != features.end(); ++i ) { Feature* feature = i->get(); // run a symbol script if present. if ( _altitude.valid() && _altitude->script().isSet() ) { StringExpression temp( _altitude->script().get() ); feature->eval( temp, &cx ); } double maxTerrainZ = -DBL_MAX; double minTerrainZ = DBL_MAX; double minHAT = DBL_MAX; double maxHAT = -DBL_MAX; double scaleZ = 1.0; if ( _altitude.valid() && _altitude->verticalScale().isSet() ) scaleZ = feature->eval( scaleExpr, &cx ); double offsetZ = 0.0; if ( _altitude.valid() && _altitude->verticalOffset().isSet() ) offsetZ = feature->eval( offsetExpr, &cx ); GeometryIterator gi( feature->getGeometry() ); while( gi.hasMore() ) { Geometry* geom = gi.next(); // Absolute heights in Z. Only need to collect the HATs; the geometry // remains unchanged. if ( _altitude->clamping() == AltitudeSymbol::CLAMP_ABSOLUTE ) { if ( perVertex ) { std::vector<double> elevations; elevations.reserve( geom->size() ); if ( eq.getElevations( geom->asVector(), featureSRS, elevations, _maxRes ) ) { for( unsigned i=0; i<geom->size(); ++i ) { osg::Vec3d& p = (*geom)[i]; p.z() *= scaleZ; p.z() += offsetZ; double z = p.z(); if ( !vertEquiv ) { osg::Vec3d tempgeo; if ( !featureSRS->transform(p, mapSRS->getGeographicSRS(), tempgeo) ) z = tempgeo.z(); } double hat = z - elevations[i]; if ( hat > maxHAT ) maxHAT = hat; if ( hat < minHAT ) minHAT = hat; if ( elevations[i] > maxTerrainZ ) maxTerrainZ = elevations[i]; if ( elevations[i] < minTerrainZ ) minTerrainZ = elevations[i]; } } } else // per centroid { osgEarth::Bounds bounds = geom->getBounds(); const osg::Vec2d& center = bounds.center2d(); GeoPoint centroid(featureSRS, center.x(), center.y()); double centroidElevation; if ( eq.getElevation( centroid, centroidElevation, _maxRes ) ) { for( unsigned i=0; i<geom->size(); ++i ) { osg::Vec3d& p = (*geom)[i]; p.z() *= scaleZ; p.z() += offsetZ; double z = p.z(); if ( !vertEquiv ) { osg::Vec3d tempgeo; if ( !featureSRS->transform(p, mapSRS->getGeographicSRS(), tempgeo) ) z = tempgeo.z(); } double hat = z - centroidElevation; if ( hat > maxHAT ) maxHAT = hat; if ( hat < minHAT ) minHAT = hat; } if ( centroidElevation > maxTerrainZ ) maxTerrainZ = centroidElevation; if ( centroidElevation < minTerrainZ ) minTerrainZ = centroidElevation; } } } // Heights-above-ground in Z. Need to resolve this to an absolute number // and record HATs along the way. else if ( _altitude->clamping() == AltitudeSymbol::CLAMP_RELATIVE_TO_TERRAIN ) { osg::ref_ptr<const SpatialReference> featureSRSwithMapVertDatum = !vertEquiv ? SpatialReference::create(featureSRS->getHorizInitString(), mapSRS->getVertInitString()) : 0L; if ( perVertex ) { std::vector<double> elevations; elevations.reserve( geom->size() ); if ( eq.getElevations( geom->asVector(), featureSRS, elevations, _maxRes ) ) { for( unsigned i=0; i<geom->size(); ++i ) { osg::Vec3d& p = (*geom)[i]; p.z() *= scaleZ; p.z() += offsetZ; double hat = p.z(); p.z() = elevations[i] + p.z(); // if necessary, convert the Z value (which is now in the map's SRS) back to // the feature's SRS. if ( !vertEquiv ) { featureSRSwithMapVertDatum->transform(p, featureSRS, p); } if ( hat > maxHAT ) maxHAT = hat; if ( hat < minHAT ) minHAT = hat; if ( elevations[i] > maxTerrainZ ) maxTerrainZ = elevations[i]; if ( elevations[i] < minTerrainZ ) minTerrainZ = elevations[i]; } } } else // per-centroid { osgEarth::Bounds bounds = geom->getBounds(); const osg::Vec2d& center = bounds.center2d(); GeoPoint centroid(featureSRS, center.x(), center.y()); double centroidElevation; if ( eq.getElevation( centroid, centroidElevation, _maxRes ) ) { for( unsigned i=0; i<geom->size(); ++i ) { osg::Vec3d& p = (*geom)[i]; p.z() *= scaleZ; p.z() += offsetZ; double hat = p.z(); p.z() = centroidElevation + p.z(); // if necessary, convert the Z value (which is now in the map's SRS) back to // the feature's SRS. if ( !vertEquiv ) { featureSRSwithMapVertDatum->transform(p, featureSRS, p); } if ( hat > maxHAT ) maxHAT = hat; if ( hat < minHAT ) minHAT = hat; } if ( centroidElevation > maxTerrainZ ) maxTerrainZ = centroidElevation; if ( centroidElevation < minTerrainZ ) minTerrainZ = centroidElevation; } } } // Clamp - replace the geometry's Z with the terrain height. else // CLAMP_TO_TERRAIN { if ( perVertex ) { eq.getElevations( geom->asVector(), featureSRS, true, _maxRes ); // if necessary, transform the Z values (which are now in the map SRS) back // into the feature's SRS. if ( !vertEquiv ) { osg::ref_ptr<const SpatialReference> featureSRSwithMapVertDatum = SpatialReference::create(featureSRS->getHorizInitString(), mapSRS->getVertInitString()); osg::Vec3d tempgeo; for( unsigned i=0; i<geom->size(); ++i ) { osg::Vec3d& p = (*geom)[i]; featureSRSwithMapVertDatum->transform(p, featureSRS, p); } } } else // per-centroid { osgEarth::Bounds bounds = geom->getBounds(); const osg::Vec2d& center = bounds.center2d(); GeoPoint centroid(featureSRS, center.x(), center.y()); double centroidElevation; osg::ref_ptr<const SpatialReference> featureSRSWithMapVertDatum; if ( !vertEquiv ) featureSRSWithMapVertDatum = SpatialReference::create(featureSRS->getHorizInitString(), mapSRS->getVertInitString()); if ( eq.getElevation( centroid, centroidElevation, _maxRes ) ) { for( unsigned i=0; i<geom->size(); ++i ) { osg::Vec3d& p = (*geom)[i]; p.z() = centroidElevation; if ( !vertEquiv ) { featureSRSWithMapVertDatum->transform(p, featureSRS, p); } } } } } if ( !collectHATs ) { for( Geometry::iterator i = geom->begin(); i != geom->end(); ++i ) { i->z() *= scaleZ; i->z() += offsetZ; } } } if ( minHAT != DBL_MAX ) { feature->set( "__min_hat", minHAT ); feature->set( "__max_hat", maxHAT ); } if ( minTerrainZ != DBL_MAX ) { feature->set( "__min_terrain_z", minTerrainZ ); feature->set( "__max_terrain_z", maxTerrainZ ); } } }
bool ExtrudeGeometryFilter::process( FeatureList& features, FilterContext& context ) { // seed our random number generators Random wallSkinPRNG( _wallSkinSymbol.valid()? *_wallSkinSymbol->randomSeed() : 0, Random::METHOD_FAST ); Random roofSkinPRNG( _roofSkinSymbol.valid()? *_roofSkinSymbol->randomSeed() : 0, Random::METHOD_FAST ); for( FeatureList::iterator f = features.begin(); f != features.end(); ++f ) { Feature* input = f->get(); GeometryIterator iter( input->getGeometry(), false ); while( iter.hasMore() ) { Geometry* part = iter.next(); osg::ref_ptr<osg::Geometry> walls = new osg::Geometry(); walls->setUseVertexBufferObjects( _useVertexBufferObjects.get() ); osg::ref_ptr<osg::Geometry> rooflines = 0L; osg::ref_ptr<osg::Geometry> baselines = 0L; osg::ref_ptr<osg::Geometry> outlines = 0L; if ( part->getType() == Geometry::TYPE_POLYGON ) { rooflines = new osg::Geometry(); rooflines->setUseVertexBufferObjects( _useVertexBufferObjects.get() ); // prep the shapes by making sure all polys are open: static_cast<Polygon*>(part)->open(); } // fire up the outline geometry if we have a line symbol. if ( _outlineSymbol != 0L ) { outlines = new osg::Geometry(); outlines->setUseVertexBufferObjects( _useVertexBufferObjects.get() ); } // make a base cap if we're doing stencil volumes. if ( _makeStencilVolume ) { baselines = new osg::Geometry(); baselines->setUseVertexBufferObjects( _useVertexBufferObjects.get() ); } // calculate the extrusion height: float height; if ( _heightCallback.valid() ) { height = _heightCallback->operator()(input, context); } else if ( _heightExpr.isSet() ) { height = input->eval( _heightExpr.mutable_value(), &context ); } else { height = *_extrusionSymbol->height(); } // calculate the height offset from the base: float offset = 0.0; if ( _heightOffsetExpr.isSet() ) { offset = input->eval( _heightOffsetExpr.mutable_value(), &context ); } osg::ref_ptr<osg::StateSet> wallStateSet; osg::ref_ptr<osg::StateSet> roofStateSet; // calculate the wall texturing: SkinResource* wallSkin = 0L; if ( _wallSkinSymbol.valid() ) { if ( _wallResLib.valid() ) { SkinSymbol querySymbol( *_wallSkinSymbol.get() ); querySymbol.objectHeight() = fabs(height) - offset; wallSkin = _wallResLib->getSkin( &querySymbol, wallSkinPRNG, context.getDBOptions() ); } else { //TODO: simple single texture? } } // calculate the rooftop texture: SkinResource* roofSkin = 0L; if ( _roofSkinSymbol.valid() ) { if ( _roofResLib.valid() ) { SkinSymbol querySymbol( *_roofSkinSymbol.get() ); roofSkin = _roofResLib->getSkin( &querySymbol, roofSkinPRNG, context.getDBOptions() ); } else { //TODO: simple single texture? } } // calculate the colors: osg::Vec4f wallColor(1,1,1,0), wallBaseColor(1,1,1,0), roofColor(1,1,1,0), outlineColor(1,1,1,1); if ( _wallPolygonSymbol.valid() ) { wallColor = _wallPolygonSymbol->fill()->color(); if ( _extrusionSymbol->wallGradientPercentage().isSet() ) { wallBaseColor = Color(wallColor).brightness( 1.0 - *_extrusionSymbol->wallGradientPercentage() ); } else { wallBaseColor = wallColor; } } if ( _roofPolygonSymbol.valid() ) { roofColor = _roofPolygonSymbol->fill()->color(); } if ( _outlineSymbol.valid() ) { outlineColor = _outlineSymbol->stroke()->color(); } // Create the extruded geometry! if (extrudeGeometry( part, height, offset, *_extrusionSymbol->flatten(), walls.get(), rooflines.get(), baselines.get(), outlines.get(), wallColor, wallBaseColor, roofColor, outlineColor, wallSkin, roofSkin, context ) ) { if ( wallSkin ) { context.resourceCache()->getStateSet( wallSkin, wallStateSet ); } // generate per-vertex normals, altering the geometry as necessary to avoid // smoothing around sharp corners osgUtil::SmoothingVisitor::smooth( *walls.get(), osg::DegreesToRadians(_wallAngleThresh_deg) ); // tessellate and add the roofs if necessary: if ( rooflines.valid() ) { osgUtil::Tessellator tess; tess.setTessellationType( osgUtil::Tessellator::TESS_TYPE_GEOMETRY ); tess.setWindingType( osgUtil::Tessellator::TESS_WINDING_ODD ); tess.retessellatePolygons( *(rooflines.get()) ); // generate default normals (no crease angle necessary; they are all pointing up) // TODO do this manually; probably faster if ( !_makeStencilVolume ) osgUtil::SmoothingVisitor::smooth( *rooflines.get() ); if ( roofSkin ) { context.resourceCache()->getStateSet( roofSkin, roofStateSet ); } } if ( baselines.valid() ) { osgUtil::Tessellator tess; tess.setTessellationType( osgUtil::Tessellator::TESS_TYPE_GEOMETRY ); tess.setWindingType( osgUtil::Tessellator::TESS_WINDING_ODD ); tess.retessellatePolygons( *(baselines.get()) ); } std::string name; if ( !_featureNameExpr.empty() ) name = input->eval( _featureNameExpr, &context ); FeatureSourceIndex* index = context.featureIndex(); addDrawable( walls.get(), wallStateSet.get(), name, input, index ); if ( rooflines.valid() ) { addDrawable( rooflines.get(), roofStateSet.get(), name, input, index ); } if ( baselines.valid() ) { addDrawable( baselines.get(), 0L, name, input, index ); } if ( outlines.valid() ) { addDrawable( outlines.get(), 0L, name, input, index ); } } } } return true; }
osg::Geode* BuildGeometryFilter::processPolygonizedLines(FeatureList& features, bool twosided, FilterContext& context) { osg::Geode* geode = new osg::Geode(); // establish some referencing bool makeECEF = false; const SpatialReference* featureSRS = 0L; const SpatialReference* mapSRS = 0L; if ( context.isGeoreferenced() ) { makeECEF = context.getSession()->getMapInfo().isGeocentric(); featureSRS = context.extent()->getSRS(); mapSRS = context.getSession()->getMapInfo().getProfile()->getSRS(); } // iterate over all features. for( FeatureList::iterator i = features.begin(); i != features.end(); ++i ) { Feature* input = i->get(); // extract the required line symbol; bail out if not found. const LineSymbol* line = input->style().isSet() && input->style()->has<LineSymbol>() ? input->style()->get<LineSymbol>() : _style.get<LineSymbol>(); if ( !line ) continue; // run a symbol script if present. if ( line->script().isSet() ) { StringExpression temp( line->script().get() ); input->eval( temp, &context ); } // The operator we'll use to make lines into polygons. PolygonizeLinesOperator polygonizer( *line->stroke() ); // iterate over all the feature's geometry parts. We will treat // them as lines strings. GeometryIterator parts( input->getGeometry(), true ); while( parts.hasMore() ) { Geometry* part = parts.next(); // if the underlying geometry is a ring (or a polygon), close it so the // polygonizer will generate a closed loop. Ring* ring = dynamic_cast<Ring*>(part); if ( ring ) ring->close(); // skip invalid geometry if ( part->size() < 2 ) continue; // transform the geometry into the target SRS and localize it about // a local reference point. osg::ref_ptr<osg::Vec3Array> verts = new osg::Vec3Array(); osg::ref_ptr<osg::Vec3Array> normals = new osg::Vec3Array(); transformAndLocalize( part->asVector(), featureSRS, verts.get(), normals.get(), mapSRS, _world2local, makeECEF ); // turn the lines into polygons. osg::Geometry* geom = polygonizer( verts.get(), normals.get(), twosided ); if ( geom ) { geode->addDrawable( geom ); } // record the geometry's primitive set(s) in the index: if ( context.featureIndex() ) context.featureIndex()->tagDrawable( geom, input ); // install clamping attributes if necessary if (_style.has<AltitudeSymbol>() && _style.get<AltitudeSymbol>()->technique() == AltitudeSymbol::TECHNIQUE_GPU) { Clamping::applyDefaultClampingAttrs( geom, input->getDouble("__oe_verticalOffset", 0.0) ); } } polygonizer.installShaders( geode ); } return geode; }
osg::Geode* BuildGeometryFilter::processLines(FeatureList& features, FilterContext& context) { osg::Geode* geode = new osg::Geode(); bool makeECEF = false; const SpatialReference* featureSRS = 0L; const SpatialReference* mapSRS = 0L; // set up referencing information: if ( context.isGeoreferenced() ) { makeECEF = context.getSession()->getMapInfo().isGeocentric(); featureSRS = context.extent()->getSRS(); mapSRS = context.getSession()->getMapInfo().getProfile()->getSRS(); } for( FeatureList::iterator f = features.begin(); f != features.end(); ++f ) { Feature* input = f->get(); // extract the required line symbol; bail out if not found. const LineSymbol* line = input->style().isSet() && input->style()->has<LineSymbol>() ? input->style()->get<LineSymbol>() : _style.get<LineSymbol>(); if ( !line ) continue; // run a symbol script if present. if ( line->script().isSet() ) { StringExpression temp( line->script().get() ); input->eval( temp, &context ); } GeometryIterator parts( input->getGeometry(), true ); while( parts.hasMore() ) { Geometry* part = parts.next(); // skip invalid geometry for lines. if ( part->size() < 2 ) continue; // if the underlying geometry is a ring (or a polygon), use a line loop; otherwise // use a line strip. GLenum primMode = dynamic_cast<Ring*>(part) ? GL_LINE_LOOP : GL_LINE_STRIP; // resolve the color: osg::Vec4f primaryColor = line->stroke()->color(); osg::ref_ptr<osg::Geometry> osgGeom = new osg::Geometry(); //osgGeom->setUseVertexBufferObjects( true ); //osgGeom->setUseDisplayList( false ); // embed the feature name if requested. Warning: blocks geometry merge optimization! if ( _featureNameExpr.isSet() ) { const std::string& name = input->eval( _featureNameExpr.mutable_value(), &context ); osgGeom->setName( name ); } // build the geometry: osg::Vec3Array* allPoints = new osg::Vec3Array(); transformAndLocalize( part->asVector(), featureSRS, allPoints, mapSRS, _world2local, makeECEF ); osgGeom->addPrimitiveSet( new osg::DrawArrays(primMode, 0, allPoints->getNumElements()) ); osgGeom->setVertexArray( allPoints ); if ( input->style().isSet() ) { //TODO: re-evaluate this. does it hinder geometry merging? applyLineSymbology( osgGeom->getOrCreateStateSet(), line ); } // subdivide the mesh if necessary to conform to an ECEF globe; // but if the tessellation is set to zero, or if the style specifies a // tessellation size, skip this step. if ( makeECEF && !line->tessellation().isSetTo(0) && !line->tessellationSize().isSet() ) { double threshold = osg::DegreesToRadians( *_maxAngle_deg ); OE_DEBUG << "Running mesh subdivider with threshold " << *_maxAngle_deg << std::endl; MeshSubdivider ms( _world2local, _local2world ); //ms.setMaxElementsPerEBO( INT_MAX ); if ( input->geoInterp().isSet() ) ms.run( *osgGeom, threshold, *input->geoInterp() ); else ms.run( *osgGeom, threshold, *_geoInterp ); } // assign the primary color (PER_VERTEX required for later optimization) osg::Vec4Array* colors = new osg::Vec4Array; colors->assign( osgGeom->getVertexArray()->getNumElements(), primaryColor ); osgGeom->setColorArray( colors ); osgGeom->setColorBinding( osg::Geometry::BIND_PER_VERTEX ); geode->addDrawable( osgGeom ); // record the geometry's primitive set(s) in the index: if ( context.featureIndex() ) context.featureIndex()->tagDrawable( osgGeom, input ); // install clamping attributes if necessary if (_style.has<AltitudeSymbol>() && _style.get<AltitudeSymbol>()->technique() == AltitudeSymbol::TECHNIQUE_GPU) { Clamping::applyDefaultClampingAttrs( osgGeom, input->getDouble("__oe_verticalOffset", 0.0) ); } } } return geode; }
void FeatureNode::build() { // if there's a decoration, clear it out first. this->clearDecoration(); _attachPoint = 0L; // if there is existing geometry, kill it this->removeChildren( 0, this->getNumChildren() ); if ( !getMapNode() ) return; if ( _features.empty() ) return; const Style &style = getStyle(); // compilation options. GeometryCompilerOptions options = _options; // figure out what kind of altitude manipulation we need to perform. AnnotationUtils::AltitudePolicy ap; AnnotationUtils::getAltitudePolicy( style, ap ); // If we're doing auto-clamping on the CPU, shut off compiler map clamping // clamping since it would be redundant. // TODO: I think this is OBE now that we have "scene" clamping technique.. if ( ap.sceneClamping ) { options.ignoreAltitudeSymbol() = true; } osg::Node* node = _compiled.get(); if (_needsRebuild || !_compiled.valid() ) { // Clone the Features before rendering as the GeometryCompiler and it's filters can change the coordinates // of the geometry when performing localization or converting to geocentric. _extent = GeoExtent::INVALID; FeatureList clone; for(FeatureList::iterator itr = _features.begin(); itr != _features.end(); ++itr) { Feature* feature = new Feature( *itr->get(), osg::CopyOp::DEEP_COPY_ALL); GeoExtent featureExtent(feature->getSRS(), feature->getGeometry()->getBounds()); if (_extent.isInvalid()) { _extent = featureExtent; } else { _extent.expandToInclude( featureExtent ); } clone.push_back( feature ); } // prep the compiler: GeometryCompiler compiler( options ); Session* session = new Session( getMapNode()->getMap(), _styleSheet.get() ); FilterContext context( session, new FeatureProfile( _extent ), _extent ); _compiled = compiler.compile( clone, style, context ); node = _compiled.get(); _needsRebuild = false; // Compute the world bounds osg::BoundingSphered bounds; for( FeatureList::iterator itr = _features.begin(); itr != _features.end(); ++itr) { osg::BoundingSphered bs; itr->get()->getWorldBound(getMapNode()->getMapSRS(), bs); bounds.expandBy(bs); } // The polytope will ensure we only clamp to intersecting tiles: Feature::getWorldBoundingPolytope(bounds, getMapNode()->getMapSRS(), _featurePolytope); } if ( node ) { if ( AnnotationUtils::styleRequiresAlphaBlending( style ) && getStyle().get<ExtrusionSymbol>() ) { node = AnnotationUtils::installTwoPassAlpha( node ); } //OE_NOTICE << GeometryUtils::geometryToGeoJSON( _feature->getGeometry() ) << std::endl; _attachPoint = new osg::Group(); _attachPoint->addChild( node ); // Draped (projected) geometry if ( ap.draping ) { DrapeableNode* d = new DrapeableNode(); // getMapNode() ); d->addChild( _attachPoint ); this->addChild( d ); } // GPU-clamped geometry else if ( ap.gpuClamping ) { ClampableNode* clampable = new ClampableNode( getMapNode() ); clampable->addChild( _attachPoint ); this->addChild( clampable ); const RenderSymbol* render = style.get<RenderSymbol>(); if ( render && render->depthOffset().isSet() ) { clampable->setDepthOffsetOptions( *render->depthOffset() ); } } else { this->addChild( _attachPoint ); // CPU-clamped geometry? if ( ap.sceneClamping ) { // save for later when we need to reclamp the mesh on the CPU _altitude = style.get<AltitudeSymbol>(); // activate the terrain callback: setCPUAutoClamping( true ); // set default lighting based on whether we are extruding: setLightingIfNotSet( style.has<ExtrusionSymbol>() ); // do an initial clamp to get started. clampMesh( getMapNode()->getTerrain()->getGraph() ); } applyRenderSymbology( style ); } } updateClusterCulling(); }
void AltitudeFilter::pushAndDontClamp( FeatureList& features, FilterContext& cx ) { NumericExpression scaleExpr; if ( _altitude.valid() && _altitude->verticalScale().isSet() ) scaleExpr = *_altitude->verticalScale(); NumericExpression offsetExpr; if ( _altitude.valid() && _altitude->verticalOffset().isSet() ) offsetExpr = *_altitude->verticalOffset(); bool gpuClamping = _altitude.valid() && _altitude->technique() == _altitude->TECHNIQUE_GPU; bool ignoreZ = gpuClamping && _altitude->clamping() == _altitude->CLAMP_TO_TERRAIN; for( FeatureList::iterator i = features.begin(); i != features.end(); ++i ) { Feature* feature = i->get(); // run a symbol script if present. if ( _altitude.valid() && _altitude->script().isSet() ) { StringExpression temp( _altitude->script().get() ); feature->eval( temp, &cx ); } double minHAT = DBL_MAX; double maxHAT = -DBL_MAX; double scaleZ = 1.0; if ( _altitude.valid() && _altitude->verticalScale().isSet() ) scaleZ = feature->eval( scaleExpr, &cx ); optional<double> offsetZ( 0.0 ); if ( _altitude.valid() && _altitude->verticalOffset().isSet() ) offsetZ = feature->eval( offsetExpr, &cx ); GeometryIterator gi( feature->getGeometry() ); while( gi.hasMore() ) { Geometry* geom = gi.next(); for( Geometry::iterator g = geom->begin(); g != geom->end(); ++g ) { if ( ignoreZ ) { g->z() = 0.0; } if ( !gpuClamping ) { g->z() *= scaleZ; g->z() += offsetZ.get(); } if ( g->z() < minHAT ) minHAT = g->z(); if ( g->z() > maxHAT ) maxHAT = g->z(); } } if ( minHAT != DBL_MAX ) { feature->set( "__min_hat", minHAT ); feature->set( "__max_hat", maxHAT ); } // encode the Z offset if if ( gpuClamping ) { feature->set("__oe_verticalScale", scaleZ); feature->set("__oe_verticalOffset", offsetZ.get()); } } }
osg::Node* PolygonizeLinesFilter::push(FeatureList& input, FilterContext& cx) { // compute the coordinate localization matrices. computeLocalizers( cx ); // establish some things bool makeECEF = false; const SpatialReference* featureSRS = 0L; const SpatialReference* mapSRS = 0L; if ( cx.isGeoreferenced() ) { makeECEF = cx.getSession()->getMapInfo().isGeocentric(); featureSRS = cx.extent()->getSRS(); mapSRS = cx.getSession()->getMapInfo().getProfile()->getSRS(); } // The operator we'll use to make lines into polygons. const LineSymbol* line = _style.get<LineSymbol>(); PolygonizeLinesOperator polygonize( line ? (*line->stroke()) : Stroke() ); // Geode to hold all the geometries. osg::Geode* geode = new osg::Geode(); // iterate over all features. for( FeatureList::iterator i = input.begin(); i != input.end(); ++i ) { Feature* f = i->get(); // iterate over all the feature's geometry parts. We will treat // them as lines strings. GeometryIterator parts( f->getGeometry(), false ); while( parts.hasMore() ) { Geometry* part = parts.next(); // skip empty geometry if ( part->size() == 0 ) continue; // transform the geometry into the target SRS and localize it about // a local reference point. osg::Vec3Array* verts = new osg::Vec3Array(); osg::Vec3Array* normals = new osg::Vec3Array(); transformAndLocalize( part->asVector(), featureSRS, verts, normals, mapSRS, _world2local, makeECEF ); // turn the lines into polygons. osg::Geometry* geom = polygonize( verts, normals ); geode->addDrawable( geom ); } } // attempt to combine geometries for better performance MeshConsolidator::run( *geode ); // GPU performance optimization: #if 0 // issue: ignores vertex attributes osgUtil::Optimizer optimizer; optimizer.optimize( result, osgUtil::Optimizer::VERTEX_PRETRANSFORM | osgUtil::Optimizer::VERTEX_POSTTRANSFORM ); #endif return delocalize( geode ); }
osg::Geode* BuildGeometryFilter::processPoints(FeatureList& features, FilterContext& context) { osg::Geode* geode = new osg::Geode(); bool makeECEF = false; const SpatialReference* featureSRS = 0L; const SpatialReference* mapSRS = 0L; // set up referencing information: if ( context.isGeoreferenced() ) { makeECEF = context.getSession()->getMapInfo().isGeocentric(); featureSRS = context.extent()->getSRS(); mapSRS = context.getSession()->getMapInfo().getProfile()->getSRS(); } for( FeatureList::iterator f = features.begin(); f != features.end(); ++f ) { Feature* input = f->get(); GeometryIterator parts( input->getGeometry(), true ); while( parts.hasMore() ) { Geometry* part = parts.next(); // extract the required point symbol; bail out if not found. const PointSymbol* point = input->style().isSet() && input->style()->has<PointSymbol>() ? input->style()->get<PointSymbol>() : _style.get<PointSymbol>(); if ( !point ) continue; // resolve the color: osg::Vec4f primaryColor = point->fill()->color(); osg::ref_ptr<osg::Geometry> osgGeom = new osg::Geometry(); //osgGeom->setUseVertexBufferObjects( true ); //osgGeom->setUseDisplayList( false ); // embed the feature name if requested. Warning: blocks geometry merge optimization! if ( _featureNameExpr.isSet() ) { const std::string& name = input->eval( _featureNameExpr.mutable_value(), &context ); osgGeom->setName( name ); } // build the geometry: osg::Vec3Array* allPoints = new osg::Vec3Array(); transformAndLocalize( part->asVector(), featureSRS, allPoints, mapSRS, _world2local, makeECEF ); osgGeom->addPrimitiveSet( new osg::DrawArrays(GL_POINTS, 0, allPoints->getNumElements()) ); osgGeom->setVertexArray( allPoints ); if ( input->style().isSet() ) { //TODO: re-evaluate this. does it hinder geometry merging? applyPointSymbology( osgGeom->getOrCreateStateSet(), point ); } // assign the primary color (PER_VERTEX required for later optimization) osg::Vec4Array* colors = new osg::Vec4Array; colors->assign( osgGeom->getVertexArray()->getNumElements(), primaryColor ); osgGeom->setColorArray( colors ); osgGeom->setColorBinding( osg::Geometry::BIND_PER_VERTEX ); geode->addDrawable( osgGeom ); // record the geometry's primitive set(s) in the index: if ( context.featureIndex() ) context.featureIndex()->tagDrawable( osgGeom, input ); // install clamping attributes if necessary if (_style.has<AltitudeSymbol>() && _style.get<AltitudeSymbol>()->technique() == AltitudeSymbol::TECHNIQUE_GPU) { Clamping::applyDefaultClampingAttrs( osgGeom, input->getDouble("__oe_verticalOffset", 0.0) ); } } } return geode; }
osg::Node* PolygonizeLinesFilter::push(FeatureList& input, FilterContext& cx) { // compute the coordinate localization matrices. computeLocalizers( cx ); // establish some things bool makeECEF = false; const SpatialReference* featureSRS = 0L; const SpatialReference* mapSRS = 0L; if ( cx.isGeoreferenced() ) { makeECEF = cx.getSession()->getMapInfo().isGeocentric(); featureSRS = cx.extent()->getSRS(); mapSRS = cx.getSession()->getMapInfo().getProfile()->getSRS(); } // The operator we'll use to make lines into polygons. const LineSymbol* line = _style.get<LineSymbol>(); PolygonizeLinesOperator polygonize( line ? (*line->stroke()) : Stroke() ); // Geode to hold all the geometries. osg::Geode* geode = new osg::Geode(); // iterate over all features. for( FeatureList::iterator i = input.begin(); i != input.end(); ++i ) { Feature* f = i->get(); // iterate over all the feature's geometry parts. We will treat // them as lines strings. GeometryIterator parts( f->getGeometry(), false ); while( parts.hasMore() ) { Geometry* part = parts.next(); // skip empty geometry if ( part->size() == 0 ) continue; // transform the geometry into the target SRS and localize it about // a local reference point. osg::Vec3Array* verts = new osg::Vec3Array(); osg::Vec3Array* normals = new osg::Vec3Array(); transformAndLocalize( part->asVector(), featureSRS, verts, normals, mapSRS, _world2local, makeECEF ); // turn the lines into polygons. osg::Geometry* geom = polygonize( verts, normals ); geode->addDrawable( geom ); // record the geometry's primitive set(s) in the index: if ( cx.featureIndex() ) cx.featureIndex()->tagPrimitiveSets( geom, f ); } } // attempt to combine geometries for better performance MeshConsolidator::run( *geode ); // GPU performance optimization: VertexCacheOptimizer vco; geode->accept( vco ); // If we're auto-scaling, we need a shader float minPixels = line ? line->stroke()->minPixels().getOrUse( 0.0f ) : 0.0f; if ( minPixels > 0.0f ) { osg::StateSet* stateSet = geode->getOrCreateStateSet(); VirtualProgram* vp = VirtualProgram::getOrCreate(stateSet); vp->setName( "osgEarth::PolygonizeLines" ); const char* vs = "#version " GLSL_VERSION_STR "\n" GLSL_DEFAULT_PRECISION_FLOAT "\n" "attribute vec3 oe_polyline_center; \n" "uniform float oe_polyline_scale; \n" "uniform float oe_polyline_min_pixels; \n" "uniform mat3 oe_WindowScaleMatrix; \n" "void oe_polyline_scalelines(inout vec4 VertexMODEL) \n" "{ \n" " if ( oe_polyline_scale != 1.0 || oe_polyline_min_pixels > 0.0 ) \n" " { \n" " vec4 center_model = vec4(oe_polyline_center*VertexMODEL.w, VertexMODEL.w); \n" " vec4 vector_model = VertexMODEL - center_model; \n" " if ( length(vector_model.xyz) > 0.0 ) \n" " { \n" " float scale = oe_polyline_scale; \n" " vec4 vertex_clip = gl_ModelViewProjectionMatrix * VertexMODEL; \n" " vec4 center_clip = gl_ModelViewProjectionMatrix * center_model; \n" " vec4 vector_clip = vertex_clip - center_clip; \n" " if ( oe_polyline_min_pixels > 0.0 ) \n" " { \n" " vec3 vector_win = oe_WindowScaleMatrix * (vertex_clip.xyz/vertex_clip.w - center_clip.xyz/center_clip.w); \n" " float min_scale = max( (0.5*oe_polyline_min_pixels)/length(vector_win.xy), 1.0 ); \n" " scale = max( scale, min_scale ); \n" " } \n" " VertexMODEL = center_model + vector_model*scale; \n" " } \n" " } \n" "} \n"; vp->setFunction( "oe_polyline_scalelines", vs, ShaderComp::LOCATION_VERTEX_MODEL ); vp->addBindAttribLocation( "oe_polyline_center", osg::Drawable::ATTRIBUTE_6 ); // add the default scaling uniform. // good way to test: // osgearth_viewer earthfile --uniform oe_polyline_scale 1.0 10.0 osg::Uniform* scaleU = new osg::Uniform(osg::Uniform::FLOAT, "oe_polyline_scale"); scaleU->set( 1.0f ); stateSet->addUniform( scaleU, 1 ); // the default "min pixels" uniform. osg::Uniform* minPixelsU = new osg::Uniform(osg::Uniform::FLOAT, "oe_polyline_min_pixels"); minPixelsU->set( minPixels ); stateSet->addUniform( minPixelsU, 1 ); } return delocalize( geode ); }