diff --git a/src/osgEarth/EarthManipulator b/src/osgEarth/EarthManipulator index fc8d0fee5b..ee42efbd36 100644 --- a/src/osgEarth/EarthManipulator +++ b/src/osgEarth/EarthManipulator @@ -514,6 +514,10 @@ namespace osgEarth { namespace Util void setZoomToMouse(bool value) { _zoomToMouse = value; } bool getZoomToMouse() const { return _zoomToMouse; } + /** Allow multitouch drag events to be combined with pinch/twist in the same gesture */ + void setAllowTouchDragCombos(bool value) { _allowTouchDragCombos = value; } + bool getAllowTouchDragCombos() const { return _allowTouchDragCombos; } + private: friend class EarthManipulator; @@ -561,6 +565,7 @@ namespace osgEarth { namespace Util double _throwDecayRate; bool _zoomToMouse; + bool _allowTouchDragCombos; }; public: diff --git a/src/osgEarth/EarthManipulator.cpp b/src/osgEarth/EarthManipulator.cpp index ccf0405d8d..982da7e9e9 100644 --- a/src/osgEarth/EarthManipulator.cpp +++ b/src/osgEarth/EarthManipulator.cpp @@ -297,7 +297,8 @@ _terrainAvoidanceEnabled ( true ), _terrainAvoidanceMinDistance ( 1.0 ), _throwingEnabled ( false ), _throwDecayRate ( 0.05 ), -_zoomToMouse ( false ) +_zoomToMouse ( false ), +_allowTouchDragCombos ( false ) { //NOP } @@ -329,7 +330,8 @@ _terrainAvoidanceEnabled( rhs._terrainAvoidanceEnabled ), _terrainAvoidanceMinDistance( rhs._terrainAvoidanceMinDistance ), _throwingEnabled( rhs._throwingEnabled ), _throwDecayRate( rhs._throwDecayRate ), -_zoomToMouse( rhs._zoomToMouse ) +_zoomToMouse( rhs._zoomToMouse ), +_allowTouchDragCombos( rhs._allowTouchDragCombos ) { //NOP } @@ -2188,10 +2190,13 @@ EarthManipulator::parseTouchEvents( TouchEvents& output ) // Threshold in pixels for determining if a two finger drag happened. float dragThres = 1.0f; + bool doMultiDrag = ( osg::equivalent(vec0.x(), vec1.x(), dragThres) && + osg::equivalent(vec0.y(), vec1.y(), dragThres) ) || + _settings->getAllowTouchDragCombos(); - // now see if that corresponds to any touch events: - if (osg::equivalent(vec0.x(), vec1.x(), dragThres) && - osg::equivalent(vec0.y(), vec1.y(), dragThres)) + // First check if we should be doing the multidrag event, either because + // it was a pure drag, or because we allow it in combination with pinch/twise + if( doMultiDrag ) { // two-finger drag. output.push_back(TouchEvent()); @@ -2200,7 +2205,9 @@ EarthManipulator::parseTouchEvents( TouchEvents& output ) ev._dx = 0.5 * (dx[0] + dx[1]) * sens; ev._dy = 0.5 * (dy[0] + dy[1]) * sens; } - else + + // If it wasn't a drag event, or if we allow drag combinations, do the pinch/twist + if( !doMultiDrag || _settings->getAllowTouchDragCombos() ) { // otherwise it's a pinch and/or a zoom. You can do them together. if (fabs(deltaDistance) > (1.0 * 0.0005 / sens)) @@ -2689,6 +2696,9 @@ EarthManipulator::zoom( double dx, double dy, osg::View* in_view ) if ( !camera ) return; + if ( !recalculateCenterFromLookVector() ) + return; + // reset the "remembered start location" if we're just starting a continuous zoom static osg::Vec3d zero(0,0,0); if (_last_action._type != ACTION_ZOOM) @@ -3371,6 +3381,9 @@ EarthManipulator::drag(double dx, double dy, osg::View* theView) if ( !camera ) return; + if ( !recalculateCenterFromLookVector() ) + return; + osg::Matrix viewMat = camera->getViewMatrix(); osg::Matrix viewMatInv = camera->getInverseViewMatrix(); if (!_ga_t1.valid()) diff --git a/src/osgEarth/FeatureDisplayLayout b/src/osgEarth/FeatureDisplayLayout index 9fbf278747..18f522b5af 100644 --- a/src/osgEarth/FeatureDisplayLayout +++ b/src/osgEarth/FeatureDisplayLayout @@ -116,6 +116,27 @@ namespace osgEarth optional& minRange() { return _minRange; } const optional& minRange() const { return _minRange; } + /** + * Whether features are loaded based on range. If not, pixels on screen + * is used. Default = range + */ + optional& useRange() { return _useRange; } + const optional& useRange() const { return _useRange; } + + /** + * When using pixel size on screen instead of range, this specifies the maximum + * number of pixels at which to display the feature at a given LOD + */ + optional& maxPixels() { return _maxPixels;} + const optional& maxPixels() const { return _maxPixels;} + + /** + * When using pixel size on screen instead of range, this specifies the minimum + * number of pixels at which to display the feature at a given LOD + */ + optional& minPixels() { return _minPixels; } + const optional& minPixels() const { return _minPixels; } + /** * Whether to crop geometry to fit within the cell extents when chopping * a feature level up into grid cells. By default, this is false, meaning @@ -184,6 +205,9 @@ namespace osgEarth optional _tileSizeFactor; optional _minRange; optional _maxRange; + optional _minPixels; + optional _maxPixels; + optional _useRange; optional _cropFeatures; optional _priorityOffset; optional _priorityScale; diff --git a/src/osgEarth/FeatureDisplayLayout.cpp b/src/osgEarth/FeatureDisplayLayout.cpp index ff1fc288ec..47c51af3eb 100644 --- a/src/osgEarth/FeatureDisplayLayout.cpp +++ b/src/osgEarth/FeatureDisplayLayout.cpp @@ -71,6 +71,9 @@ FeatureDisplayLayout::FeatureDisplayLayout( const Config& conf ) : _tileSizeFactor( 3.5f ), _minRange ( 0.0f ), _maxRange ( 0.0f ), +_minPixels ( 0.0f ), +_maxPixels ( 0.0f ), +_useRange ( true ), _cropFeatures ( false ), _priorityOffset( 0.0f ), _priorityScale ( 1.0f ), @@ -91,6 +94,9 @@ FeatureDisplayLayout::fromConfig( const Config& conf ) conf.get( "min_expiry_time", _minExpiryTime ); conf.get( "min_range", _minRange ); conf.get( "max_range", _maxRange ); + conf.get( "min_pixels", _minPixels); + conf.get( "max_pixels", _maxPixels); + conf.get( "use_range", _useRange); conf.get("paged", _paged); ConfigSet children = conf.children( "level" ); for( ConfigSet::const_iterator i = children.begin(); i != children.end(); ++i ) @@ -109,6 +115,9 @@ FeatureDisplayLayout::getConfig() const conf.set( "min_expiry_time", _minExpiryTime ); conf.set( "min_range", _minRange ); conf.set( "max_range", _maxRange ); + conf.set( "min_pixels", _minPixels ); + conf.set( "max_pixels", _maxPixels ); + conf.set( "use_range", _useRange ); conf.set("paged", _paged); for( Levels::const_iterator i = _levels.begin(); i != _levels.end(); ++i ) conf.add( i->second.getConfig() ); diff --git a/src/osgEarth/FeatureModelGraph.cpp b/src/osgEarth/FeatureModelGraph.cpp index e10df801be..c3a8a500e1 100644 --- a/src/osgEarth/FeatureModelGraph.cpp +++ b/src/osgEarth/FeatureModelGraph.cpp @@ -269,8 +269,20 @@ namespace p->setLoadFunction(func); p->setCenter(bs.center()); p->setRadius(bs.radius()); - p->setMinRange(minRange); - p->setMaxRange(maxRange); + if( layout.useRange().get() ) + { + p->setMinRange(minRange); + p->setMaxRange(maxRange); + } + else + { + if( layout.minPixels().isSet() ) + p->setMinPixels(layout.minPixels().get()); + + if( layout.maxPixels().isSet() ) + p->setMaxPixels(layout.maxPixels().get()); + } + p->setPriorityScale(layout.priorityScale().get()); p->setSceneGraphCallbacks(sgCallbacks); return p; diff --git a/src/osgEarth/Filter b/src/osgEarth/Filter index 0e1e737095..73f4608915 100644 --- a/src/osgEarth/Filter +++ b/src/osgEarth/Filter @@ -1,239 +1,308 @@ -/* -*-c++-*- */ -/* osgEarth - Geospatial SDK for OpenSceneGraph - * Copyright 2020 Pelican Mapping - * http://osgearth.org - * - * osgEarth is free software; you can redistribute it and/or modify - * it under the terms of the GNU Lesser General Public License as published by - * the Free Software Foundation; either version 2 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public License - * along with this program. If not, see - */ -#ifndef OSGEARTHFEATURES_FILTER_H -#define OSGEARTHFEATURES_FILTER_H 1 - -#include -#include -#include -#include -#include -#include - - -namespace osgEarth { namespace Util -{ - using namespace osgEarth; - - /** - * Base class for a filter. - */ - class OSGEARTH_EXPORT Filter : public osg::Object - { - public: - META_Object(osgEarth, Filter); - - protected: - virtual ~Filter(); - Filter() : osg::Object() { } - Filter(const Filter& rhs, const osg::CopyOp& copyop) : osg::Object(rhs, copyop) { } - }; - - /** - * Base class for feature filters. - */ - class OSGEARTH_EXPORT FeatureFilter : public Filter - { - public: - /** - * Push a list of features through the filter. - */ - virtual FilterContext push( FeatureList& input, FilterContext& context ) =0; - - /** - * Optionally initialize the filter. - */ - virtual Status initialize(const osgDB::Options* readOptions) { return Status::OK(); } - - /** - * Serialize this FeatureFilter - */ - virtual Config getConfig() const { return Config(); } - - /** - * Called when the FeatureFilter is added to the map. - */ - virtual void addedToMap(const class Map*); - - protected: - FeatureFilter() { } - FeatureFilter(const FeatureFilter& rhs, const osg::CopyOp& c) : Filter(rhs, c) { } - virtual ~FeatureFilter(); - }; - - //! Vector of feature filters (ref counted) - class OSGEARTH_EXPORT FeatureFilterChain : - public osg::Referenced, - public osg::MixinVector > - { - public: - static FeatureFilterChain* create( - const std::vector& filters, - const osgDB::Options* readOptions); - - const Status& getStatus() const { return _status; } - - private: - Status _status; - }; - - /** - * A Factory that can create a FeatureFilter from a Config - */ - class OSGEARTH_EXPORT FeatureFilterFactory : public osg::Referenced - { - public: - virtual FeatureFilter* create( const Config& conf ) = 0; - }; - - typedef std::list< osg::ref_ptr< FeatureFilterFactory > > FeatureFilterFactoryList; - - /** - * A registry of FeatureFilter plugins - */ - class OSGEARTH_EXPORT FeatureFilterRegistry : public osg::Referenced - { - public: - /** - * The singleton instance of the factory - */ - static FeatureFilterRegistry* instance(); - - /* - * Adds a new FeatureFilterFactory to the list - */ - void add( FeatureFilterFactory* factory ); - - /** - * Creates a FeatureFilter with the registered plugins from the given Config - */ - FeatureFilter* create(const Config& conf, const osgDB::Options* dbo); - - protected: - FeatureFilterRegistry(); - FeatureFilterFactoryList _factories; - }; - - template - struct SimpleFeatureFilterFactory : public FeatureFilterFactory - { - SimpleFeatureFilterFactory(const std::string& key):_key(key){} - - virtual FeatureFilter* create(const Config& conf) - { - if (conf.key() == _key) return new T(conf); - return 0; - } - - std::string _key; - }; - - template - struct RegisterFeatureFilterProxy - { - RegisterFeatureFilterProxy( T* factory) { FeatureFilterRegistry::instance()->add( factory ); } - RegisterFeatureFilterProxy() { FeatureFilterRegistry::instance()->add( new T ); } - }; - -#define OSGEARTH_REGISTER_FEATUREFILTER( CLASSNAME )\ - extern "C" void osgearth_featurefilter_##KEY(void) {} \ - static osgEarth::RegisterFeatureFilterProxy s_osgEarthRegisterFeatureFilterProxy_##CLASSNAME; - -#define USE_OSGEARTH_FEATUREFILTER( KEY ) \ - extern "C" void osgearth_featurefilter_##KEY(void); \ - static osgDB::PluginFunctionProxy proxy_osgearth_featurefilter_##KEY(osgearth_featurefilter_##KEY); - -#define OSGEARTH_REGISTER_SIMPLE_FEATUREFILTER( KEY, CLASSNAME)\ - extern "C" void osgearth_simple_featurefilter_##KEY(void) {} \ - static osgEarth::RegisterFeatureFilterProxy< osgEarth::SimpleFeatureFilterFactory > s_osgEarthRegisterFeatureFilterProxy_##CLASSNAME##KEY(new osgEarth::SimpleFeatureFilterFactory(#KEY)); - -#define USE_OSGEARTH_SIMPLE_FEATUREFILTER( KEY ) \ - extern "C" void osgearth_simple_featurefilter_##KEY(void); \ - static osgDB::PluginFunctionProxy proxy_osgearth_simple_featurefilter_##KEY(osgearth_simple_featurefilter_##KEY); - - - //-------------------------------------------------------------------- - - class OSGEARTH_EXPORT FeatureFilterDriver : public osgDB::ReaderWriter - { - protected: - const ConfigOptions& getConfigOptions(const osgDB::Options* options) const; - }; - - /** - * Base class for a filter that converts features into an osg Node. - */ - class OSGEARTH_EXPORT FeaturesToNodeFilter : public Filter - { - public: - virtual osg::Node* push( FeatureList& input, FilterContext& context ) =0; - - public: - const osg::Matrixd& local2world() const { return _local2world; } - const osg::Matrixd& world2local() const { return _world2local; } - - protected: - - virtual ~FeaturesToNodeFilter(); - - // computes the matricies required to localizer/delocalize double-precision coords - void computeLocalizers( const FilterContext& context ); - void computeLocalizers( const FilterContext& context, const osgEarth::GeoExtent &extent, osg::Matrixd &out_w2l, osg::Matrixd &out_l2w ); - - /** Parents the node with a localizer group if necessary */ - osg::Node* delocalize( osg::Node* node ) const; - osg::Node* delocalize( osg::Node* node, const osg::Matrixd &local2World ) const; - osg::Group* delocalizeAsGroup( osg::Node* node ) const; - osg::Group* delocalizeAsGroup( osg::Node* node, const osg::Matrixd &local2World ) const; - osg::Group* createDelocalizeGroup() const; - osg::Group* createDelocalizeGroup( const osg::Matrixd &local2World) const; - - void transformAndLocalize( - const std::vector& input, - const SpatialReference* inputSRS, - osg::Vec3Array* output, - const SpatialReference* outputSRS, - const osg::Matrixd& world2local, - bool toECEF ); - - void transformAndLocalize( - const std::vector& input, - const SpatialReference* inputSRS, - osg::Vec3Array* out_verts, - osg::Vec3Array* out_normals, - const SpatialReference* outputSRS, - const osg::Matrixd& world2local, - bool toECEF ); - - void transformAndLocalize( - const osg::Vec3d& input, - const SpatialReference* inputSRS, - osg::Vec3d& output, - const SpatialReference* outputSRS, - const osg::Matrixd& world2local, - bool toECEF ); - - void applyPointSymbology(osg::StateSet*, const class PointSymbol*); - - osg::Matrixd _world2local, _local2world; // for coordinate localization - }; - -} } - -#endif // OSGEARTHFEATURES_FILTER_H +/* -*-c++-*- */ +/* osgEarth - Geospatial SDK for OpenSceneGraph + * Copyright 2020 Pelican Mapping + * http://osgearth.org + * + * osgEarth is free software; you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this program. If not, see + */ +#ifndef OSGEARTHFEATURES_FILTER_H +#define OSGEARTHFEATURES_FILTER_H 1 + +#include +#include +#include +#include +#include +#include + + +namespace osgEarth { namespace Util +{ + using namespace osgEarth; + + /** + * Base class for a filter. + */ + class OSGEARTH_EXPORT Filter : public osg::Object + { + public: + META_Object(osgEarth, Filter); + + protected: + virtual ~Filter(); + Filter() : osg::Object() { } + Filter(const Filter& rhs, const osg::CopyOp& copyop) : osg::Object(rhs, copyop) { } + }; + + /** + * Base class for feature filters. + */ + class OSGEARTH_EXPORT FeatureFilter : public Filter + { + public: + /** + * Push a list of features through the filter. + */ + virtual FilterContext push( FeatureList& input, FilterContext& context ) =0; + + /** + * Optionally initialize the filter. + */ + virtual Status initialize(const osgDB::Options* readOptions) { return Status::OK(); } + + /** + * Serialize this FeatureFilter + */ + virtual Config getConfig() const { return Config(); } + + protected: + FeatureFilter() { } + FeatureFilter(const FeatureFilter& rhs, const osg::CopyOp& c) : Filter(rhs, c) { } + virtual ~FeatureFilter(); + }; + + //! Vector of feature filters (ref counted) + class OSGEARTH_EXPORT FeatureFilterChain : + public osg::Referenced, + public osg::MixinVector > + { + public: + static FeatureFilterChain* create( + const std::vector& filters, + const osgDB::Options* readOptions); + + const Status& getStatus() const { return _status; } + + private: + Status _status; + }; + + /** + * A Factory that can create a FeatureFilter from a Config + */ + class OSGEARTH_EXPORT FeatureFilterFactory : public osg::Referenced + { + public: + virtual FeatureFilter* create( const Config& conf ) = 0; + }; + + typedef std::list< osg::ref_ptr< FeatureFilterFactory > > FeatureFilterFactoryList; + + /** + * A registry of FeatureFilter plugins + */ + class OSGEARTH_EXPORT FeatureFilterRegistry : public osg::Referenced + { + public: + /** + * The singleton instance of the factory + */ + static FeatureFilterRegistry* instance(); + + /* + * Adds a new FeatureFilterFactory to the list + */ + void add( FeatureFilterFactory* factory ); + + /** + * Creates a FeatureFilter with the registered plugins from the given Config + */ + FeatureFilter* create(const Config& conf, const osgDB::Options* dbo); + + protected: + FeatureFilterRegistry(); + FeatureFilterFactoryList _factories; + }; + + template + struct SimpleFeatureFilterFactory : public FeatureFilterFactory + { + SimpleFeatureFilterFactory(const std::string& key):_key(key){} + + virtual FeatureFilter* create(const Config& conf) + { + if (conf.key() == _key) return new T(conf); + return 0; + } + + std::string _key; + }; + + template + struct RegisterFeatureFilterProxy + { + RegisterFeatureFilterProxy( T* factory) { FeatureFilterRegistry::instance()->add( factory ); } + RegisterFeatureFilterProxy() { FeatureFilterRegistry::instance()->add( new T ); } + }; + +#define OSGEARTH_REGISTER_FEATUREFILTER( CLASSNAME )\ + extern "C" void osgearth_featurefilter_##KEY(void) {} \ + static osgEarth::RegisterFeatureFilterProxy s_osgEarthRegisterFeatureFilterProxy_##CLASSNAME; + +#define USE_OSGEARTH_FEATUREFILTER( KEY ) \ + extern "C" void osgearth_featurefilter_##KEY(void); \ + static osgDB::PluginFunctionProxy proxy_osgearth_featurefilter_##KEY(osgearth_featurefilter_##KEY); + +#define OSGEARTH_REGISTER_SIMPLE_FEATUREFILTER( KEY, CLASSNAME)\ + extern "C" void osgearth_simple_featurefilter_##KEY(void) {} \ + static osgEarth::RegisterFeatureFilterProxy< osgEarth::SimpleFeatureFilterFactory > s_osgEarthRegisterFeatureFilterProxy_##CLASSNAME##KEY(new osgEarth::SimpleFeatureFilterFactory(#KEY)); + +#define USE_OSGEARTH_SIMPLE_FEATUREFILTER( KEY ) \ + extern "C" void osgearth_simple_featurefilter_##KEY(void); \ + static osgDB::PluginFunctionProxy proxy_osgearth_simple_featurefilter_##KEY(osgearth_simple_featurefilter_##KEY); + + + //-------------------------------------------------------------------- + + class OSGEARTH_EXPORT FeatureFilterDriver : public osgDB::ReaderWriter + { + protected: + const ConfigOptions& getConfigOptions(const osgDB::Options* options) const; + }; + + /** + * Base class for a filter that converts features into an osg Node. + */ + class OSGEARTH_EXPORT FeaturesToNodeFilter : public Filter + { + public: + virtual osg::Node* push( FeatureList& input, FilterContext& context ) =0; + + public: + const osg::Matrixd& local2world() const { return _local2world; } + const osg::Matrixd& world2local() const { return _world2local; } + + protected: + + virtual ~FeaturesToNodeFilter(); + + // computes the matricies required to localizer/delocalize double-precision coords + void computeLocalizers( const FilterContext& context ); + void computeLocalizers( const FilterContext& context, const osgEarth::GeoExtent &extent, osg::Matrixd &out_w2l, osg::Matrixd &out_l2w ); + + /** Parents the node with a localizer group if necessary */ + osg::Node* delocalize( osg::Node* node ) const; + osg::Node* delocalize( osg::Node* node, const osg::Matrixd &local2World ) const; + osg::Group* delocalizeAsGroup( osg::Node* node ) const; + osg::Group* delocalizeAsGroup( osg::Node* node, const osg::Matrixd &local2World ) const; + osg::Group* createDelocalizeGroup() const; + osg::Group* createDelocalizeGroup( const osg::Matrixd &local2World) const; + + void transformAndLocalize( + const std::vector& input, + const SpatialReference* inputSRS, + osg::Vec3Array* output, + const SpatialReference* outputSRS, + const osg::Matrixd& world2local, + bool toECEF ); + + void transformAndLocalize( + const std::vector& input, + const SpatialReference* inputSRS, + osg::Vec3Array* out_verts, + osg::Vec3Array* out_normals, + const SpatialReference* outputSRS, + const osg::Matrixd& world2local, + bool toECEF ); + + void transformAndLocalize( + const osg::Vec3d& input, + const SpatialReference* inputSRS, + osg::Vec3d& output, + const SpatialReference* outputSRS, + const osg::Matrixd& world2local, + bool toECEF ); + + void applyPointSymbology(osg::StateSet*, const class PointSymbol*); + + osg::Matrixd _world2local, _local2world; // for coordinate localization + }; + + class OSGEARTH_EXPORT FeaturesToNodeFilterFactory : public osg::Referenced + { + public: + virtual FeaturesToNodeFilter* create(const Config& conf, osgEarth::Style style) = 0; + }; + + typedef std::list> FeaturesToNodeFilterFactoryList; + + class OSGEARTH_EXPORT FeaturesToNodeFilterRegistry : public osg::Referenced + { + public: + static FeaturesToNodeFilterRegistry* instance(); + + void add( FeaturesToNodeFilterFactory* factory ); + FeaturesToNodeFilter* create( + const Config& conf, + const osgDB::Options* dbo, + osgEarth::Style style); + + protected: + FeaturesToNodeFilterRegistry(); + FeaturesToNodeFilterFactoryList _factories; + }; + + template + struct SimpleFeaturesToNodeFilterFactory : public FeaturesToNodeFilterFactory + { + SimpleFeaturesToNodeFilterFactory(const std::string& key) : _key(key) {} + + virtual FeaturesToNodeFilter* create(const Config& conf, osgEarth::Style style) + { + if (conf.key() == _key) return new T(style); + return 0; + } + + std::string _key; + }; + + template + struct RegisterFeaturesToNodeFilterProxy + { + RegisterFeaturesToNodeFilterProxy( T* factory) + { + FeaturesToNodeFilterRegistry::instance()->add( factory ); + } + + RegisterFeaturesToNodeFilterProxy() + { + FeaturesToNodeFilterRegistry::instance()->add( new T ); + } + }; + +#define OSGEARTH_REGISTER_FEATURESTONODEFILTER( CLASSNAME )\ + extern "C" void osgearth_featurestonodefilter_##KEY(void) {} \ + static osgEarth::RegisterFeaturesToNodeFilterProxy s_osgEarthRegisterFeaturesToNodeFilterProxy_##CLASSNAME; + +#define USE_OSGEARTH_FEATURESTONODEFILTER( KEY ) \ + extern "C" void osgearth_featurestonodefilter_##KEY(void); \ + static osgDB::PluginFunctionProxy proxy_osgearth_featurestonodefilter_##KEY(osgearth_featurestonodefilter_##KEY); + +#define OSGEARTH_REGISTER_SIMPLE_FEATURESTONODEFILTER( KEY, CLASSNAME)\ + extern "C" void osgearth_simple_featurestonodefilter_##KEY(void) {} \ + static osgEarth::RegisterFeaturesToNodeFilterProxy< osgEarth::SimpleFeaturesToNodeFilterFactory > s_osgEarthRegisterFeaturesToNodeFilterProxy_##CLASSNAME##KEY(new osgEarth::SimpleFeaturesToNodeFilterFactory(#KEY)); + +#define USE_OSGEARTH_SIMPLE_FEATURESTONODEFILTER( KEY ) \ + extern "C" void osgearth_simple_featurestonodefilter_##KEY(void); \ + static osgDB::PluginFunctionProxy proxy_osgearth_simple_featurestonodefilter_##KEY(osgearth_simple_featurestonodefilter_##KEY); + + class OSGEARTH_EXPORT FeaturesToNodeFilterDriver : public osgDB::ReaderWriter + { + protected: + const ConfigOptions& getConfigOptions(const osgDB::Options* options) const; + }; + +} } + +#endif // OSGEARTHFEATURES_FILTER_H diff --git a/src/osgEarth/Filter.cpp b/src/osgEarth/Filter.cpp index 36ae8318c2..b8c33044ad 100644 --- a/src/osgEarth/Filter.cpp +++ b/src/osgEarth/Filter.cpp @@ -1,380 +1,462 @@ -/* -*-c++-*- */ -/* osgEarth - Geospatial SDK for OpenSceneGraph - * Copyright 2020 Pelican Mapping - * http://osgearth.org - * - * osgEarth is free software; you can redistribute it and/or modify - * it under the terms of the GNU Lesser General Public License as published by - * the Free Software Foundation; either version 2 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public License - * along with this program. If not, see - */ -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -using namespace osgEarth; -using namespace osgEarth::Util; - -/********************************************************************************/ -Filter::~Filter() -{ -} - -/********************************************************************************/ -FeatureFilter::~FeatureFilter() -{ -} - -void FeatureFilter::addedToMap(const class Map*) -{ -} - -/********************************************************************************/ - -#undef LC -#define LC "[FeatureFilterChain] " - -FeatureFilterChain* -FeatureFilterChain::create(const std::vector& filters, const osgDB::Options* readOptions) -{ - // Create and initialize the filters. - FeatureFilterChain* chain = NULL; - - for(unsigned i=0; icreate( conf.getConfig(), 0L ); - if ( filter ) - { - if (chain == NULL) - chain = new FeatureFilterChain(); - - chain->push_back( filter ); - Status s = filter->initialize(readOptions); - if (s.isError()) - { - chain->_status = s; - OE_WARN << LC << "Filter problem: " << filter->getName() << " : " << s.message() << std::endl; - break; - } - } - } - - return chain; -} - -/********************************************************************************/ - -#undef LC -#define LC "[FeatureFilterRegistry] " - -FeatureFilterRegistry::FeatureFilterRegistry() -{ -} - -FeatureFilterRegistry* -FeatureFilterRegistry::instance() -{ - // OK to be in the local scope since this gets called at static init time - // by the OSGEARTH_REGISTER_ANNOTATION macro - static FeatureFilterRegistry* s_singleton =0L; - static Threading::Mutex s_singletonMutex(OE_MUTEX_NAME); - - if ( !s_singleton ) - { - Threading::ScopedMutexLock lock(s_singletonMutex); - if ( !s_singleton ) - { - s_singleton = new FeatureFilterRegistry(); - } - } - return s_singleton; -} - -void -FeatureFilterRegistry::add( FeatureFilterFactory* factory ) -{ - _factories.push_back( factory ); -} - -#define FEATURE_FILTER_OPTIONS_TAG "__osgEarth::FeatureFilterOptions" - -FeatureFilter* -FeatureFilterRegistry::create(const Config& conf, const osgDB::Options* dbo) -{ - const std::string& driver = conf.key(); - - osg::ref_ptr result; - - for (FeatureFilterFactoryList::iterator itr = _factories.begin(); result == 0L && itr != _factories.end(); itr++) - { - result = itr->get()->create( conf ); - } - - if ( !result.valid() ) - { - // not found; try to load from plugin. - if ( driver.empty() ) - { - OE_WARN << LC << "ILLEGAL- no driver set for feature filter" << std::endl; - return 0L; - } - - ConfigOptions options(conf); - - osg::ref_ptr dbopt = Registry::instance()->cloneOrCreateOptions(dbo); - dbopt->setPluginData( FEATURE_FILTER_OPTIONS_TAG, (void*)&options ); - - std::string driverExt = std::string( ".osgearth_featurefilter_" ) + driver; - osg::ref_ptr object = osgDB::readRefObjectFile( driverExt, dbopt.get() ); - result = dynamic_cast( object.release() ); - } - - if ( !result.valid() ) - { - OE_WARN << LC << "Failed to load FeatureFilter driver \"" << driver << "\"" << std::endl; - } - - return result.release(); -} - -const ConfigOptions& -FeatureFilterDriver::getConfigOptions(const osgDB::Options* options) const -{ - static ConfigOptions s_default; - const void* data = options->getPluginData(FEATURE_FILTER_OPTIONS_TAG); - return data ? *static_cast(data) : s_default; -} - -/********************************************************************************/ - -#undef LC -#define LC "[FeaturesToNodeFilter] " - -FeaturesToNodeFilter::~FeaturesToNodeFilter() -{ - //nop -} - -void -FeaturesToNodeFilter::computeLocalizers( const FilterContext& context ) -{ - computeLocalizers(context, context.extent().get(), _world2local, _local2world); -} - -void -FeaturesToNodeFilter::computeLocalizers( const FilterContext& context, const osgEarth::GeoExtent &extent, osg::Matrixd &out_w2l, osg::Matrixd &out_l2w ) -{ - if ( context.isGeoreferenced() ) - { - bool ecef = context.getOutputSRS()->isGeographic(); - - if (ecef) - { - - const SpatialReference* geogSRS = context.getOutputSRS()->getGeographicSRS(); - GeoExtent geodExtent = extent.transform( geogSRS ); - if ( geodExtent.width() < 180.0 ) - { - osg::Vec3d centroid, centroidECEF; - geodExtent.getCentroid( centroid.x(), centroid.y() ); - geogSRS->transform( centroid, geogSRS->getGeocentricSRS(), centroidECEF ); - geogSRS->getGeocentricSRS()->createLocalToWorld( centroidECEF, out_l2w ); - out_w2l.invert( out_l2w ); - } - } - - else // projected - { - if ( extent.isValid() ) - { - osg::Vec3d centroid; - extent.getCentroid(centroid.x(), centroid.y()); - - extent.getSRS()->transform( - centroid, - context.getSession()->getMapSRS(), - centroid ); - - out_w2l.makeTranslate( -centroid ); - out_l2w.invert( out_w2l ); - } - } - } -} - -void -FeaturesToNodeFilter::transformAndLocalize(const std::vector& input, - const SpatialReference* inputSRS, - osg::Vec3Array* output, - const SpatialReference* outputSRS, - const osg::Matrixd& world2local, - bool toECEF ) -{ - output->reserve( output->size() + input.size() ); - - if ( toECEF ) - { - ECEF::transformAndLocalize( input, inputSRS, output, outputSRS, world2local ); - } - else if ( inputSRS ) - { - std::vector temp( input ); - inputSRS->transform( temp, outputSRS ); - - for( std::vector::const_iterator i = temp.begin(); i != temp.end(); ++i ) - { - output->push_back( (*i) * world2local ); - } - } - else - { - for( std::vector::const_iterator i = input.begin(); i != input.end(); ++i ) - { - output->push_back( (*i) * world2local ); - } - } -} - - - -void -FeaturesToNodeFilter::transformAndLocalize(const std::vector& input, - const SpatialReference* inputSRS, - osg::Vec3Array* output_verts, - osg::Vec3Array* output_normals, - const SpatialReference* outputSRS, - const osg::Matrixd& world2local, - bool toECEF ) -{ - // pre-allocate enough space (performance) - output_verts->reserve( output_verts->size() + input.size() ); - - if ( output_normals ) - output_normals->reserve( output_verts->size() ); - - if ( toECEF ) - { - ECEF::transformAndLocalize( input, inputSRS, output_verts, output_normals, outputSRS, world2local ); - } - else if ( inputSRS ) - { - std::vector temp( input ); - inputSRS->transform( temp, outputSRS ); - - for( std::vector::const_iterator i = temp.begin(); i != temp.end(); ++i ) - { - output_verts->push_back( (*i) * world2local ); - if ( output_normals ) - output_normals->push_back( osg::Vec3(0,0,1) ); - } - } - else - { - for( std::vector::const_iterator i = input.begin(); i != input.end(); ++i ) - { - output_verts->push_back( (*i) * world2local ); - if ( output_normals ) - output_normals->push_back( osg::Vec3(0,0,1) ); - } - } -} - - - - -void -FeaturesToNodeFilter::transformAndLocalize(const osg::Vec3d& input, - const SpatialReference* inputSRS, - osg::Vec3d& output, - const SpatialReference* outputSRS, - const osg::Matrixd& world2local, - bool toECEF ) -{ - if ( toECEF && inputSRS && outputSRS ) - { - ECEF::transformAndLocalize( input, inputSRS, output, outputSRS, world2local ); - } - else if ( inputSRS ) - { - inputSRS->transform( input, outputSRS, output ); - output = output * world2local; - } - else - { - output = input * world2local; - } -} - - -osg::Node* -FeaturesToNodeFilter::delocalize( osg::Node* node ) const -{ - return delocalize(node, _local2world); -} - -osg::Node* -FeaturesToNodeFilter::delocalize( osg::Node* node, const osg::Matrixd &local2world) const -{ - if ( !local2world.isIdentity() ) - return delocalizeAsGroup( node, local2world ); - else - return node; -} - -osg::Group* -FeaturesToNodeFilter::delocalizeAsGroup( osg::Node* node ) const -{ - return delocalizeAsGroup( node, _local2world ); -} - -osg::Group* -FeaturesToNodeFilter::delocalizeAsGroup( osg::Node* node, const osg::Matrixd &local2world ) const -{ - osg::Group* group = createDelocalizeGroup(local2world); - if ( node ) - group->addChild( node ); - return group; -} - -osg::Group* -FeaturesToNodeFilter::createDelocalizeGroup() const -{ - return createDelocalizeGroup( _local2world ); -} - -osg::Group* -FeaturesToNodeFilter::createDelocalizeGroup( const osg::Matrixd &local2world ) const -{ - osg::Group* group = local2world.isIdentity() ? - new osg::Group() : - new osg::MatrixTransform( local2world ); - - return group; -} - -void -FeaturesToNodeFilter::applyPointSymbology(osg::StateSet* stateset, - const PointSymbol* point) -{ - if ( point ) - { - float size = osg::maximum( 0.1f, *point->size() ); - GLUtils::setPointSize(stateset, size, 1); - } -} +/* -*-c++-*- */ +/* osgEarth - Geospatial SDK for OpenSceneGraph + * Copyright 2020 Pelican Mapping + * http://osgearth.org + * + * osgEarth is free software; you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this program. If not, see + */ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +using namespace osgEarth; +using namespace osgEarth::Util; + +/********************************************************************************/ +Filter::~Filter() +{ +} + +/********************************************************************************/ +FeatureFilter::~FeatureFilter() +{ +} + +/********************************************************************************/ + +#undef LC +#define LC "[FeatureFilterChain] " + +FeatureFilterChain* +FeatureFilterChain::create(const std::vector& filters, const osgDB::Options* readOptions) +{ + // Create and initialize the filters. + FeatureFilterChain* chain = NULL; + + for(unsigned i=0; icreate( conf.getConfig(), 0L ); + if ( filter ) + { + if (chain == NULL) + chain = new FeatureFilterChain(); + + chain->push_back( filter ); + Status s = filter->initialize(readOptions); + if (s.isError()) + { + chain->_status = s; + OE_WARN << LC << "Filter problem: " << filter->getName() << " : " << s.message() << std::endl; + break; + } + } + } + + return chain; +} + +/********************************************************************************/ + +#undef LC +#define LC "[FeatureFilterRegistry] " + +FeatureFilterRegistry::FeatureFilterRegistry() +{ +} + +FeatureFilterRegistry* +FeatureFilterRegistry::instance() +{ + // OK to be in the local scope since this gets called at static init time + // by the OSGEARTH_REGISTER_ANNOTATION macro + static FeatureFilterRegistry* s_singleton =0L; + static Threading::Mutex s_singletonMutex(OE_MUTEX_NAME); + + if ( !s_singleton ) + { + Threading::ScopedMutexLock lock(s_singletonMutex); + if ( !s_singleton ) + { + s_singleton = new FeatureFilterRegistry(); + } + } + return s_singleton; +} + +void +FeatureFilterRegistry::add( FeatureFilterFactory* factory ) +{ + _factories.push_back( factory ); +} + +#define FEATURE_FILTER_OPTIONS_TAG "__osgEarth::FeatureFilterOptions" + +FeatureFilter* +FeatureFilterRegistry::create(const Config& conf, const osgDB::Options* dbo) +{ + const std::string& driver = conf.key(); + + osg::ref_ptr result; + + for (FeatureFilterFactoryList::iterator itr = _factories.begin(); result == 0L && itr != _factories.end(); itr++) + { + result = itr->get()->create( conf ); + } + + if ( !result.valid() ) + { + // not found; try to load from plugin. + if ( driver.empty() ) + { + OE_WARN << LC << "ILLEGAL- no driver set for feature filter" << std::endl; + return 0L; + } + + ConfigOptions options(conf); + + osg::ref_ptr dbopt = Registry::instance()->cloneOrCreateOptions(dbo); + dbopt->setPluginData( FEATURE_FILTER_OPTIONS_TAG, (void*)&options ); + + std::string driverExt = std::string( ".osgearth_featurefilter_" ) + driver; + osg::ref_ptr object = osgDB::readRefObjectFile( driverExt, dbopt.get() ); + result = dynamic_cast( object.release() ); + } + + if ( !result.valid() ) + { + OE_WARN << LC << "Failed to load FeatureFilter driver \"" << driver << "\"" << std::endl; + } + + return result.release(); +} + +const ConfigOptions& +FeatureFilterDriver::getConfigOptions(const osgDB::Options* options) const +{ + static ConfigOptions s_default; + const void* data = options->getPluginData(FEATURE_FILTER_OPTIONS_TAG); + return data ? *static_cast(data) : s_default; +} + +/********************************************************************************/ + +#undef LC +#define LC "[FeaturesToNodeFilter] " + +FeaturesToNodeFilter::~FeaturesToNodeFilter() +{ + //nop +} + +void +FeaturesToNodeFilter::computeLocalizers( const FilterContext& context ) +{ + computeLocalizers(context, context.extent().get(), _world2local, _local2world); +} + +void +FeaturesToNodeFilter::computeLocalizers( const FilterContext& context, const osgEarth::GeoExtent &extent, osg::Matrixd &out_w2l, osg::Matrixd &out_l2w ) +{ + if ( context.isGeoreferenced() ) + { + bool ecef = context.getOutputSRS()->isGeographic(); + + if (ecef) + { + + const SpatialReference* geogSRS = context.getOutputSRS()->getGeographicSRS(); + GeoExtent geodExtent = extent.transform( geogSRS ); + if ( geodExtent.width() < 180.0 ) + { + osg::Vec3d centroid, centroidECEF; + geodExtent.getCentroid( centroid.x(), centroid.y() ); + geogSRS->transform( centroid, geogSRS->getGeocentricSRS(), centroidECEF ); + geogSRS->getGeocentricSRS()->createLocalToWorld( centroidECEF, out_l2w ); + out_w2l.invert( out_l2w ); + } + } + + else // projected + { + if ( extent.isValid() ) + { + osg::Vec3d centroid; + extent.getCentroid(centroid.x(), centroid.y()); + + extent.getSRS()->transform( + centroid, + context.getSession()->getMapSRS(), + centroid ); + + out_w2l.makeTranslate( -centroid ); + out_l2w.invert( out_w2l ); + } + } + } +} + +void +FeaturesToNodeFilter::transformAndLocalize(const std::vector& input, + const SpatialReference* inputSRS, + osg::Vec3Array* output, + const SpatialReference* outputSRS, + const osg::Matrixd& world2local, + bool toECEF ) +{ + output->reserve( output->size() + input.size() ); + + if ( toECEF ) + { + ECEF::transformAndLocalize( input, inputSRS, output, outputSRS, world2local ); + } + else if ( inputSRS ) + { + std::vector temp( input ); + inputSRS->transform( temp, outputSRS ); + + for( std::vector::const_iterator i = temp.begin(); i != temp.end(); ++i ) + { + output->push_back( (*i) * world2local ); + } + } + else + { + for( std::vector::const_iterator i = input.begin(); i != input.end(); ++i ) + { + output->push_back( (*i) * world2local ); + } + } +} + + + +void +FeaturesToNodeFilter::transformAndLocalize(const std::vector& input, + const SpatialReference* inputSRS, + osg::Vec3Array* output_verts, + osg::Vec3Array* output_normals, + const SpatialReference* outputSRS, + const osg::Matrixd& world2local, + bool toECEF ) +{ + // pre-allocate enough space (performance) + output_verts->reserve( output_verts->size() + input.size() ); + + if ( output_normals ) + output_normals->reserve( output_verts->size() ); + + if ( toECEF ) + { + ECEF::transformAndLocalize( input, inputSRS, output_verts, output_normals, outputSRS, world2local ); + } + else if ( inputSRS ) + { + std::vector temp( input ); + inputSRS->transform( temp, outputSRS ); + + for( std::vector::const_iterator i = temp.begin(); i != temp.end(); ++i ) + { + output_verts->push_back( (*i) * world2local ); + if ( output_normals ) + output_normals->push_back( osg::Vec3(0,0,1) ); + } + } + else + { + for( std::vector::const_iterator i = input.begin(); i != input.end(); ++i ) + { + output_verts->push_back( (*i) * world2local ); + if ( output_normals ) + output_normals->push_back( osg::Vec3(0,0,1) ); + } + } +} + + + + +void +FeaturesToNodeFilter::transformAndLocalize(const osg::Vec3d& input, + const SpatialReference* inputSRS, + osg::Vec3d& output, + const SpatialReference* outputSRS, + const osg::Matrixd& world2local, + bool toECEF ) +{ + if ( toECEF && inputSRS && outputSRS ) + { + ECEF::transformAndLocalize( input, inputSRS, output, outputSRS, world2local ); + } + else if ( inputSRS ) + { + inputSRS->transform( input, outputSRS, output ); + output = output * world2local; + } + else + { + output = input * world2local; + } +} + + +osg::Node* +FeaturesToNodeFilter::delocalize( osg::Node* node ) const +{ + return delocalize(node, _local2world); +} + +osg::Node* +FeaturesToNodeFilter::delocalize( osg::Node* node, const osg::Matrixd &local2world) const +{ + if ( !local2world.isIdentity() ) + return delocalizeAsGroup( node, local2world ); + else + return node; +} + +osg::Group* +FeaturesToNodeFilter::delocalizeAsGroup( osg::Node* node ) const +{ + return delocalizeAsGroup( node, _local2world ); +} + +osg::Group* +FeaturesToNodeFilter::delocalizeAsGroup( osg::Node* node, const osg::Matrixd &local2world ) const +{ + osg::Group* group = createDelocalizeGroup(local2world); + if ( node ) + group->addChild( node ); + return group; +} + +osg::Group* +FeaturesToNodeFilter::createDelocalizeGroup() const +{ + return createDelocalizeGroup( _local2world ); +} + +osg::Group* +FeaturesToNodeFilter::createDelocalizeGroup( const osg::Matrixd &local2world ) const +{ + osg::Group* group = local2world.isIdentity() ? + new osg::Group() : + new osg::MatrixTransform( local2world ); + + return group; +} + +void +FeaturesToNodeFilter::applyPointSymbology(osg::StateSet* stateset, + const PointSymbol* point) +{ + if ( point ) + { + float size = osg::maximum( 0.1f, *point->size() ); + GLUtils::setPointSize(stateset, size, 1); + } +} + +/********************************************************************************/ + +#undef LC +#define LC "[FeaturesToNodeFilterRegistry] " + +FeaturesToNodeFilterRegistry::FeaturesToNodeFilterRegistry() : _factories() +{ +} + +FeaturesToNodeFilterRegistry* +FeaturesToNodeFilterRegistry::instance() +{ + // OK to be in the local scope since this gets called at static init time + // by the OSGEARTH_REGISTER_ANNOTATION macro + static FeaturesToNodeFilterRegistry* s_singleton =0L; + static Threading::Mutex s_singletonMutex(OE_MUTEX_NAME); + + if ( !s_singleton ) + { + Threading::ScopedMutexLock lock(s_singletonMutex); + if ( !s_singleton ) + { + s_singleton = new FeaturesToNodeFilterRegistry(); + } + } + return s_singleton; +} + +void +FeaturesToNodeFilterRegistry::add( FeaturesToNodeFilterFactory* factory ) +{ + _factories.push_back( factory ); +} + +#define FEATURES_TO_NODE_FILTER_OPTIONS_TAG "__osgEarth::FeaturesToNodeFilterOptions" + +FeaturesToNodeFilter* +FeaturesToNodeFilterRegistry::create( + const Config& conf, + const osgDB::Options* dbo, + osgEarth::Style style) +{ + const std::string& driver = conf.key(); + + osg::ref_ptr result; + + for (FeaturesToNodeFilterFactoryList::iterator itr = _factories.begin(); result == 0L && itr != _factories.end(); itr++) + { + result = itr->get()->create(conf, style); + } + + if ( !result.valid() ) + { + // not found; try to load from plugin. + if ( driver.empty() ) + { + OE_WARN << LC << "ILLEGAL- no driver set for features to node filter" << std::endl; + return 0L; + } + + ConfigOptions options(conf); + + osg::ref_ptr dbopt = Registry::instance()->cloneOrCreateOptions(dbo); + dbopt->setPluginData( FEATURES_TO_NODE_FILTER_OPTIONS_TAG, (void*)&options ); + + std::string driverExt = std::string( ".osgearth_featurestonodefilter_" ) + driver; + osg::ref_ptr object = osgDB::readRefObjectFile( driverExt, dbopt.get() ); + result = dynamic_cast( object.release() ); + } + + if ( !result.valid() ) + { + OE_WARN << LC << "Failed to load FeaturesToNodeFilter driver \"" << driver << "\"" << std::endl; + } + + return result.release(); +} + +const ConfigOptions& +FeaturesToNodeFilterDriver::getConfigOptions(const osgDB::Options* options) const +{ + static ConfigOptions s_default; + const void* data = options->getPluginData(FEATURES_TO_NODE_FILTER_OPTIONS_TAG); + return data ? *static_cast(data) : s_default; +} diff --git a/src/osgEarth/GeometryCompiler.cpp b/src/osgEarth/GeometryCompiler.cpp index f7400ed5e8..c3ac7ded5a 100644 --- a/src/osgEarth/GeometryCompiler.cpp +++ b/src/osgEarth/GeometryCompiler.cpp @@ -1,606 +1,615 @@ -/* -*-c++-*- */ -/* osgEarth - Dynamic map generation toolkit for OpenSceneGraph - * Copyright 2020 Pelican Mapping - * http://osgearth.org - * - * osgEarth is free software; you can redistribute it and/or modify - * it under the terms of the GNU Lesser General Public License as published by - * the Free Software Foundation; either version 2 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public License - * along with this program. If not, see - */ -#include "GeometryCompiler" - -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#include -#include -#include -#include -#include -#include -#include -#include - -#include -#include -#include -#include -#include - -#include - -#define LC "[GeometryCompiler] " - -using namespace osgEarth; -using namespace osgEarth::Util; -using namespace osgEarth::Util; - -//#define PROFILING 1 - -//----------------------------------------------------------------------- - -GeometryCompilerOptions GeometryCompilerOptions::s_defaults(true); - -void -GeometryCompilerOptions::setDefaults(const GeometryCompilerOptions& defaults) -{ - s_defaults = defaults; -} - -// defaults. -GeometryCompilerOptions::GeometryCompilerOptions(bool stockDefaults) : -_maxGranularity_deg ( 10.0 ), -_mergeGeometry ( true ), -_clustering ( false ), -_instancing ( true ), -_ignoreAlt ( false ), -_shaderPolicy ( SHADERPOLICY_GENERATE ), -_geoInterp ( GEOINTERP_GREAT_CIRCLE ), -_optimizeStateSharing ( true ), -_optimize ( false ), -_optimizeVertexOrdering( true ), -_validate ( false ), -_maxPolyTilingAngle ( 45.0f ), -_useOSGTessellator ( false ) -{ - //nop -} - -//----------------------------------------------------------------------- - -GeometryCompilerOptions::GeometryCompilerOptions(const ConfigOptions& conf) : -_maxGranularity_deg ( s_defaults.maxGranularity().value() ), -_mergeGeometry ( s_defaults.mergeGeometry().value() ), -_clustering ( s_defaults.clustering().value() ), -_instancing ( s_defaults.instancing().value() ), -_ignoreAlt ( s_defaults.ignoreAltitudeSymbol().value() ), -_shaderPolicy ( s_defaults.shaderPolicy().value() ), -_geoInterp ( s_defaults.geoInterp().value() ), -_optimizeStateSharing ( s_defaults.optimizeStateSharing().value() ), -_optimize ( s_defaults.optimize().value() ), -_optimizeVertexOrdering( s_defaults.optimizeVertexOrdering().value() ), -_validate ( s_defaults.validate().value() ), -_maxPolyTilingAngle ( s_defaults.maxPolygonTilingAngle().value() ), -_useOSGTessellator (s_defaults.useOSGTessellator().value()) -{ - fromConfig(conf.getConfig()); -} - -void -GeometryCompilerOptions::fromConfig( const Config& conf ) -{ - conf.get( "max_granularity", _maxGranularity_deg ); - conf.get( "merge_geometry", _mergeGeometry ); - conf.get( "clustering", _clustering ); - conf.get( "instancing", _instancing ); - conf.get( "feature_name", _featureNameExpr ); - conf.get( "ignore_altitude", _ignoreAlt ); - conf.get( "geo_interpolation", "great_circle", _geoInterp, GEOINTERP_GREAT_CIRCLE ); - conf.get( "geo_interpolation", "rhumb_line", _geoInterp, GEOINTERP_RHUMB_LINE ); - conf.get( "optimize_state_sharing", _optimizeStateSharing ); - conf.get( "optimize", _optimize ); - conf.get( "optimize_vertex_ordering", _optimizeVertexOrdering); - conf.get( "validate", _validate ); - conf.get( "max_polygon_tiling_angle", _maxPolyTilingAngle ); - conf.get( "use_osg_tessellator", _useOSGTessellator); - - conf.get( "shader_policy", "disable", _shaderPolicy, SHADERPOLICY_DISABLE ); - conf.get( "shader_policy", "inherit", _shaderPolicy, SHADERPOLICY_INHERIT ); - conf.get( "shader_policy", "generate", _shaderPolicy, SHADERPOLICY_GENERATE ); -} - -Config -GeometryCompilerOptions::getConfig() const -{ - Config conf; - conf.set( "max_granularity", _maxGranularity_deg ); - conf.set( "merge_geometry", _mergeGeometry ); - conf.set( "clustering", _clustering ); - conf.set( "instancing", _instancing ); - conf.set( "feature_name", _featureNameExpr ); - conf.set( "ignore_altitude", _ignoreAlt ); - conf.set( "geo_interpolation", "great_circle", _geoInterp, GEOINTERP_GREAT_CIRCLE ); - conf.set( "geo_interpolation", "rhumb_line", _geoInterp, GEOINTERP_RHUMB_LINE ); - conf.set( "optimize_state_sharing", _optimizeStateSharing ); - conf.set( "optimize", _optimize ); - conf.set( "optimize_vertex_ordering", _optimizeVertexOrdering); - conf.set( "validate", _validate ); - conf.set( "max_polygon_tiling_angle", _maxPolyTilingAngle ); - conf.set( "use_osg_tessellator", _useOSGTessellator); - - conf.set( "shader_policy", "disable", _shaderPolicy, SHADERPOLICY_DISABLE ); - conf.set( "shader_policy", "inherit", _shaderPolicy, SHADERPOLICY_INHERIT ); - conf.set( "shader_policy", "generate", _shaderPolicy, SHADERPOLICY_GENERATE ); - return conf; -} - - -//----------------------------------------------------------------------- - -GeometryCompiler::GeometryCompiler() -{ - //nop -} - -GeometryCompiler::GeometryCompiler( const GeometryCompilerOptions& options ) : -_options( options ) -{ - //nop -} - -osg::Node* -GeometryCompiler::compile(Geometry* geometry, - const Style& style, - const FilterContext& context) -{ - osg::ref_ptr f = new Feature(geometry, 0L); // no SRS! - return compile(f.get(), style, context); -} - -osg::Node* -GeometryCompiler::compile(Geometry* geometry, - const Style& style) -{ - osg::ref_ptr f = new Feature(geometry, 0L); // no SRS! - return compile(f.get(), style, FilterContext(0L) ); -} - -osg::Node* -GeometryCompiler::compile(Geometry* geometry, - const FilterContext& context) -{ - return compile( geometry, Style(), context ); -} - -osg::Node* -GeometryCompiler::compile(Feature* feature, - const Style& style, - const FilterContext& context) -{ - FeatureList workingSet; - workingSet.push_back(feature); - return compile(workingSet, style, context); -} - -osg::Node* -GeometryCompiler::compile(Feature* feature, - const FilterContext& context) -{ - return compile(feature, *feature->style(), context); -} - -osg::Node* -GeometryCompiler::compile(FeatureCursor* cursor, - const Style& style, - const FilterContext& context) - -{ - // start by making a working copy of the feature set - FeatureList workingSet; - cursor->fill( workingSet ); - - return compile(workingSet, style, context); -} - -osg::Node* -GeometryCompiler::compile(FeatureList& workingSet, - const Style& style, - const FilterContext& context) -{ - OE_PROFILING_ZONE; - -#ifdef PROFILING - osg::Timer_t p_start = osg::Timer::instance()->tick(); - unsigned p_features = workingSet.size(); -#endif - - // for debugging/validation. - std::vector history; - bool trackHistory = (_options.validate() == true); - - osg::ref_ptr 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 defaultPoint; - osg::ref_ptr defaultLine; - osg::ref_ptr defaultPolygon; - - // go through the Style and figure out which filters to use. - const PointSymbol* point = style.get(); - const LineSymbol* line = style.get(); - const PolygonSymbol* polygon = style.get(); - const ExtrusionSymbol* extrusion = style.get(); - const AltitudeSymbol* altitude = style.get(); - const TextSymbol* text = style.get(); - const IconSymbol* icon = style.get(); - const ModelSymbol* model = style.get(); - const RenderSymbol* render = style.get(); - - // Perform tessellation first. - if ( line ) - { - if ( line->tessellation().isSet() ) - { - 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() ) - { - 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 && !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_POINT: - 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() ); - - // instance substitution (replaces marker) - if ( model ) - { - const InstanceSymbol* instance = (const InstanceSymbol*)model; - - // 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 ); - } - } - - // 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()); - //else if (_options.optimize() == true) - // extrude.setMergeGeometry(false); - - - 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(); - filter.useOSGTessellator() = *_options.useOSGTessellator(); - - if (_options.maxPolygonTilingAngle().isSet()) - filter.maxPolygonTilingAngle() = *_options.maxPolygonTilingAngle(); - - if ( _options.featureName().isSet() ) - filter.featureName() = *_options.featureName(); - - if (_options.optimizeVertexOrdering().isSet()) - filter.optimizeVertexOrdering() = *_options.optimizeVertexOrdering(); - - if (render && render->maxCreaseAngle().isSet()) - filter.maxCreaseAngle() = render->maxCreaseAngle().get(); - - osg::Node* node = filter.push( workingSet, sharedCX ); - if ( node ) - { - if ( trackHistory ) history.push_back( "geometry" ); - resultGroup->addChild( node ); - } - } - - if ( text || icon ) - { - // Only clamp annotation types when the technique is - // explicity set to MAP. Otherwise, the annotation subsystem - // will automatically use SCENE clamping. - bool altRequiredForAnnotations = - altRequired && - altitude->technique().isSetTo(altitude->TECHNIQUE_MAP); - - if ( altRequiredForAnnotations ) - { - 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()) - { - ShaderPolicy shaderPolicy = _options.shaderPolicy().get(); - - if (shaderPolicy == SHADERPOLICY_GENERATE) - { - // no ss cache because we will optimize later. - Registry::shaderGenerator().run( - resultGroup.get(), - "GeometryCompiler shadergen" ); - } - else if (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 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 0 // never do this, let the filters do it. - 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); - - osgUtil::Optimizer::MergeGeometryVisitor mg; - mg.setTargetMaximumNumberOfVertices(Registry::instance()->getMaxNumberOfVertsPerDrawable()); - resultGroup->accept(mg); - - OE_DEBUG << LC << "optimize complete" << std::endl; - - if ( trackHistory ) history.push_back( "optimize" ); - } -#endif - - //test: dump the tile to disk - //OE_WARN << "Writing GC node file to out.osgt..." << std::endl; - //osgDB::writeNodeFile( *(resultGroup.get()), "out.osgt" ); - -#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::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"; - } - - // Build kdtrees to increase intersection speed. - if (osgDB::Registry::instance()->getKdTreeBuilder()) - { - osg::ref_ptr< osg::KdTreeBuilder > kdTreeBuilder = osgDB::Registry::instance()->getKdTreeBuilder()->clone(); - resultGroup->accept(*kdTreeBuilder.get()); - } - - return resultGroup.release(); -} +/* -*-c++-*- */ +/* osgEarth - Dynamic map generation toolkit for OpenSceneGraph + * Copyright 2020 Pelican Mapping + * http://osgearth.org + * + * osgEarth is free software; you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this program. If not, see + */ +#include "GeometryCompiler" + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include + +#include + +#define LC "[GeometryCompiler] " + +using namespace osgEarth; +using namespace osgEarth::Util; +using namespace osgEarth::Util; + +//#define PROFILING 1 + +//----------------------------------------------------------------------- + +GeometryCompilerOptions GeometryCompilerOptions::s_defaults(true); + +void +GeometryCompilerOptions::setDefaults(const GeometryCompilerOptions& defaults) +{ + s_defaults = defaults; +} + +// defaults. +GeometryCompilerOptions::GeometryCompilerOptions(bool stockDefaults) : +_maxGranularity_deg ( 10.0 ), +_mergeGeometry ( true ), +_clustering ( false ), +_instancing ( true ), +_ignoreAlt ( false ), +_shaderPolicy ( SHADERPOLICY_GENERATE ), +_geoInterp ( GEOINTERP_GREAT_CIRCLE ), +_optimizeStateSharing ( true ), +_optimize ( false ), +_optimizeVertexOrdering( true ), +_validate ( false ), +_maxPolyTilingAngle ( 45.0f ), +_useOSGTessellator ( false ) +{ + //nop +} + +//----------------------------------------------------------------------- + +GeometryCompilerOptions::GeometryCompilerOptions(const ConfigOptions& conf) : +_maxGranularity_deg ( s_defaults.maxGranularity().value() ), +_mergeGeometry ( s_defaults.mergeGeometry().value() ), +_clustering ( s_defaults.clustering().value() ), +_instancing ( s_defaults.instancing().value() ), +_ignoreAlt ( s_defaults.ignoreAltitudeSymbol().value() ), +_shaderPolicy ( s_defaults.shaderPolicy().value() ), +_geoInterp ( s_defaults.geoInterp().value() ), +_optimizeStateSharing ( s_defaults.optimizeStateSharing().value() ), +_optimize ( s_defaults.optimize().value() ), +_optimizeVertexOrdering( s_defaults.optimizeVertexOrdering().value() ), +_validate ( s_defaults.validate().value() ), +_maxPolyTilingAngle ( s_defaults.maxPolygonTilingAngle().value() ), +_useOSGTessellator (s_defaults.useOSGTessellator().value()) +{ + fromConfig(conf.getConfig()); +} + +void +GeometryCompilerOptions::fromConfig( const Config& conf ) +{ + conf.get( "max_granularity", _maxGranularity_deg ); + conf.get( "merge_geometry", _mergeGeometry ); + conf.get( "clustering", _clustering ); + conf.get( "instancing", _instancing ); + conf.get( "feature_name", _featureNameExpr ); + conf.get( "ignore_altitude", _ignoreAlt ); + conf.get( "geo_interpolation", "great_circle", _geoInterp, GEOINTERP_GREAT_CIRCLE ); + conf.get( "geo_interpolation", "rhumb_line", _geoInterp, GEOINTERP_RHUMB_LINE ); + conf.get( "optimize_state_sharing", _optimizeStateSharing ); + conf.get( "optimize", _optimize ); + conf.get( "optimize_vertex_ordering", _optimizeVertexOrdering); + conf.get( "validate", _validate ); + conf.get( "max_polygon_tiling_angle", _maxPolyTilingAngle ); + conf.get( "use_osg_tessellator", _useOSGTessellator); + + conf.get( "shader_policy", "disable", _shaderPolicy, SHADERPOLICY_DISABLE ); + conf.get( "shader_policy", "inherit", _shaderPolicy, SHADERPOLICY_INHERIT ); + conf.get( "shader_policy", "generate", _shaderPolicy, SHADERPOLICY_GENERATE ); +} + +Config +GeometryCompilerOptions::getConfig() const +{ + Config conf; + conf.set( "max_granularity", _maxGranularity_deg ); + conf.set( "merge_geometry", _mergeGeometry ); + conf.set( "clustering", _clustering ); + conf.set( "instancing", _instancing ); + conf.set( "feature_name", _featureNameExpr ); + conf.set( "ignore_altitude", _ignoreAlt ); + conf.set( "geo_interpolation", "great_circle", _geoInterp, GEOINTERP_GREAT_CIRCLE ); + conf.set( "geo_interpolation", "rhumb_line", _geoInterp, GEOINTERP_RHUMB_LINE ); + conf.set( "optimize_state_sharing", _optimizeStateSharing ); + conf.set( "optimize", _optimize ); + conf.set( "optimize_vertex_ordering", _optimizeVertexOrdering); + conf.set( "validate", _validate ); + conf.set( "max_polygon_tiling_angle", _maxPolyTilingAngle ); + conf.set( "use_osg_tessellator", _useOSGTessellator); + + conf.set( "shader_policy", "disable", _shaderPolicy, SHADERPOLICY_DISABLE ); + conf.set( "shader_policy", "inherit", _shaderPolicy, SHADERPOLICY_INHERIT ); + conf.set( "shader_policy", "generate", _shaderPolicy, SHADERPOLICY_GENERATE ); + return conf; +} + + +//----------------------------------------------------------------------- + +GeometryCompiler::GeometryCompiler() +{ + //nop +} + +GeometryCompiler::GeometryCompiler( const GeometryCompilerOptions& options ) : +_options( options ) +{ + //nop +} + +osg::Node* +GeometryCompiler::compile(Geometry* geometry, + const Style& style, + const FilterContext& context) +{ + osg::ref_ptr f = new Feature(geometry, 0L); // no SRS! + return compile(f.get(), style, context); +} + +osg::Node* +GeometryCompiler::compile(Geometry* geometry, + const Style& style) +{ + osg::ref_ptr f = new Feature(geometry, 0L); // no SRS! + return compile(f.get(), style, FilterContext(0L) ); +} + +osg::Node* +GeometryCompiler::compile(Geometry* geometry, + const FilterContext& context) +{ + return compile( geometry, Style(), context ); +} + +osg::Node* +GeometryCompiler::compile(Feature* feature, + const Style& style, + const FilterContext& context) +{ + FeatureList workingSet; + workingSet.push_back(feature); + return compile(workingSet, style, context); +} + +osg::Node* +GeometryCompiler::compile(Feature* feature, + const FilterContext& context) +{ + return compile(feature, *feature->style(), context); +} + +osg::Node* +GeometryCompiler::compile(FeatureCursor* cursor, + const Style& style, + const FilterContext& context) + +{ + // start by making a working copy of the feature set + FeatureList workingSet; + cursor->fill( workingSet ); + + return compile(workingSet, style, context); +} + +osg::Node* +GeometryCompiler::compile(FeatureList& workingSet, + const Style& style, + const FilterContext& context) +{ + OE_PROFILING_ZONE; + +#ifdef PROFILING + osg::Timer_t p_start = osg::Timer::instance()->tick(); + unsigned p_features = workingSet.size(); +#endif + + // for debugging/validation. + std::vector history; + bool trackHistory = (_options.validate() == true); + + osg::ref_ptr 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 defaultPoint; + osg::ref_ptr defaultLine; + osg::ref_ptr defaultPolygon; + + // go through the Style and figure out which filters to use. + const PointSymbol* point = style.get(); + const LineSymbol* line = style.get(); + const PolygonSymbol* polygon = style.get(); + const ExtrusionSymbol* extrusion = style.get(); + const AltitudeSymbol* altitude = style.get(); + const TextSymbol* text = style.get(); + const IconSymbol* icon = style.get(); + const ModelSymbol* model = style.get(); + const RenderSymbol* render = style.get(); + + // Perform tessellation first. + if ( line ) + { + if ( line->tessellation().isSet() ) + { + 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() ) + { + 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 && !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_POINT: + 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() ); + + // instance substitution (replaces marker) + if ( model ) + { + const InstanceSymbol* instance = (const InstanceSymbol*)model; + + // 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 ); + } + } + + // 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()); + //else if (_options.optimize() == true) + // extrude.setMergeGeometry(false); + + + 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(); + filter.useOSGTessellator() = *_options.useOSGTessellator(); + + if (_options.maxPolygonTilingAngle().isSet()) + filter.maxPolygonTilingAngle() = *_options.maxPolygonTilingAngle(); + + if ( _options.featureName().isSet() ) + filter.featureName() = *_options.featureName(); + + if (_options.optimizeVertexOrdering().isSet()) + filter.optimizeVertexOrdering() = *_options.optimizeVertexOrdering(); + + if (render && render->maxCreaseAngle().isSet()) + filter.maxCreaseAngle() = render->maxCreaseAngle().get(); + + osg::Node* node = filter.push( workingSet, sharedCX ); + if ( node ) + { + if ( trackHistory ) history.push_back( "geometry" ); + resultGroup->addChild( node ); + } + } + + if ( text || icon ) + { + // Only clamp annotation types when the technique is + // explicity set to MAP. Otherwise, the annotation subsystem + // will automatically use SCENE clamping. + bool altRequiredForAnnotations = + altRequired && + altitude->technique().isSetTo(altitude->TECHNIQUE_MAP); + + if ( altRequiredForAnnotations ) + { + AltitudeFilter clamp; + clamp.setPropertiesFromStyle( style ); + sharedCX = clamp.push( workingSet, sharedCX ); + if ( trackHistory ) history.push_back( "altitude" ); + altRequired = false; + } + + std::string ftnf_driver = style.getSymbol()->provider().get(); + Config ftnf_cfg(ftnf_driver); + osg::ref_ptr filter = FeaturesToNodeFilterRegistry::instance()->create( + ftnf_cfg, + 0L, + style); + + if (!filter) + filter = new BuildTextFilter( style ); + + osg::Node* node = filter->push( workingSet, sharedCX ); + if ( node ) + { + if ( trackHistory ) history.push_back( "text" ); + resultGroup->addChild( node ); + } + } + + if (Registry::capabilities().supportsGLSL()) + { + ShaderPolicy shaderPolicy = _options.shaderPolicy().get(); + + if (shaderPolicy == SHADERPOLICY_GENERATE) + { + // no ss cache because we will optimize later. + Registry::shaderGenerator().run( + resultGroup.get(), + "GeometryCompiler shadergen" ); + } + else if (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 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 0 // never do this, let the filters do it. + 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); + + osgUtil::Optimizer::MergeGeometryVisitor mg; + mg.setTargetMaximumNumberOfVertices(Registry::instance()->getMaxNumberOfVertsPerDrawable()); + resultGroup->accept(mg); + + OE_DEBUG << LC << "optimize complete" << std::endl; + + if ( trackHistory ) history.push_back( "optimize" ); + } +#endif + + //test: dump the tile to disk + //OE_WARN << "Writing GC node file to out.osgt..." << std::endl; + //osgDB::writeNodeFile( *(resultGroup.get()), "out.osgt" ); + +#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::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"; + } + + // Build kdtrees to increase intersection speed. + if (osgDB::Registry::instance()->getKdTreeBuilder()) + { + osg::ref_ptr< osg::KdTreeBuilder > kdTreeBuilder = osgDB::Registry::instance()->getKdTreeBuilder()->clone(); + resultGroup->accept(*kdTreeBuilder.get()); + } + + return resultGroup.release(); +} diff --git a/src/osgEarth/SimplePager b/src/osgEarth/SimplePager index 830049dc0e..d796964959 100644 --- a/src/osgEarth/SimplePager +++ b/src/osgEarth/SimplePager @@ -65,6 +65,8 @@ namespace osgEarth { namespace Util float getPriorityOffset() const { return _priorityOffset; } void setPriorityOffset(float value) { _priorityOffset = value; } + void setMinPixel(float value) { _minPixel = value; _useRange = false; } + void setEnableCancelation(bool value); bool getEnableCancelation() const; @@ -120,6 +122,8 @@ namespace osgEarth { namespace Util bool _additive; double _rangeFactor; + bool _useRange; + float _minPixel; unsigned int _minLevel; unsigned int _maxLevel; osg::ref_ptr _profile; diff --git a/src/osgEarth/SimplePager.cpp b/src/osgEarth/SimplePager.cpp index de8ebe8ba0..e59bc7eff0 100644 --- a/src/osgEarth/SimplePager.cpp +++ b/src/osgEarth/SimplePager.cpp @@ -23,6 +23,8 @@ SimplePager::SimplePager(const osgEarth::Map* map, const osgEarth::Profile* prof _map(map), _profile( profile ), _rangeFactor( 6.0 ), +_useRange(true), +_minPixel(0.0), _additive(false), _minLevel(0), _maxLevel(30), @@ -235,8 +237,11 @@ SimplePager::createPagedNode(const TileKey& key, ProgressCallback* progress) loadRange = (float)(tileRadius * _rangeFactor); pagedNode->setRefinePolicy(_additive ? REFINE_ADD : REFINE_REPLACE); } - - pagedNode->setMaxRange(loadRange); + + if (_useRange) + pagedNode->setMaxRange(loadRange); + else + pagedNode->setMinPixels(_minPixel); //OE_INFO << "PagedNode2: key="<setOwnerName(getName()); fmg->setFilterChain(chain.get()); fmg->setAdditive(*_options->additive()); + if (_options->minPixel().isSet()) + { + fmg->setMinPixel(_options->minPixel().get()); + } + fmg->build(); _root->removeChildren(0, _root->getNumChildren());