From 5a081c6c5afad372db556fc3b4bb7ce2e78746c3 Mon Sep 17 00:00:00 2001 From: Glenn Waldron Date: Wed, 5 Jun 2024 10:42:26 -0400 Subject: [PATCH] Revert ModelLayer changes - to much dependent code in vrv --- .../osgearth_map/osgearth_map.cpp | 2 +- src/osgEarth/ModelLayer | 92 ++- src/osgEarth/ModelLayer.cpp | 535 +++++++++++++----- 3 files changed, 470 insertions(+), 159 deletions(-) diff --git a/src/applications/osgearth_map/osgearth_map.cpp b/src/applications/osgearth_map/osgearth_map.cpp index 3246932f87..e0318cf92f 100644 --- a/src/applications/osgearth_map/osgearth_map.cpp +++ b/src/applications/osgearth_map/osgearth_map.cpp @@ -204,7 +204,7 @@ main(int argc, char** argv) // put a model on the map atop Pike's Peak, Colorado, USA auto modelLayer = new ModelLayer(); modelLayer->setURL("../data/red_flag.osg.2000.scale"); - modelLayer->setPosition(GeoPoint(SpatialReference::get("wgs84"), -105.042292, 38.840829)); + modelLayer->setLocation(GeoPoint(SpatialReference::get("wgs84"), -105.042292, 38.840829)); map->addLayer(modelLayer); // make the map scene graph: diff --git a/src/osgEarth/ModelLayer b/src/osgEarth/ModelLayer index 0705515428..b2f9e0ce33 100644 --- a/src/osgEarth/ModelLayer +++ b/src/osgEarth/ModelLayer @@ -29,6 +29,8 @@ namespace osgEarth { + class Map; + /** * Layer that contains an OSG scene graph */ @@ -36,56 +38,110 @@ namespace osgEarth { public: // serialization class OSGEARTH_EXPORT Options : public VisibleLayer::Options { - public: + public: META_LayerOptions(osgEarth, Options, VisibleLayer::Options); OE_OPTION(URI, url); - OE_OPTION(GeoPoint, position); + OE_OPTION(float, lodScale); + OE_OPTION(GeoPoint, location); OE_OPTION(osg::Vec3, orientation); - OE_OPTION(bool, lightingEnabled, true); - OE_OPTION(float, minPixels, 0.0f); - OE_OPTION(ShaderPolicy, shaderPolicy, SHADERPOLICY_GENERATE); + OE_OPTION(ShaderPolicy, shaderPolicy); + OE_OPTION(float, loadingPriorityScale); + OE_OPTION(float, loadingPriorityOffset); + OE_OPTION(bool, paged); + OE_OPTION(bool, lightingEnabled); + OE_OPTION(unsigned, maskMinLevel); + OE_OPTION(ModelSourceOptions, driver); virtual Config getConfig() const; private: - void fromConfig( const Config& conf ); + void fromConfig(const Config& conf); }; public: META_Layer(osgEarth, ModelLayer, Options, VisibleLayer, Model); public: + //! Sets the node to add to the map's scene graph. Set this OR setURL + //! Call this OR setURL, but not both. + void setNode(osg::Node* node); + //! URL from which to load the model to use in this layer. //! Call this OR setNode, but not both. Call before opening the layer. void setURL(const URI& url); const URI& getURL() const; + //! LOD scale for the scene graph in this model layer. + //! Call before opening the layer. + void setLODScale(const float& value); + const float& getLODScale() const; + + //! Whether the model loaded by setURL() should be paged in versus + //! loaded immediately. Only applicable when using setURL. + //! Call before opening the layer. + void setPaged(const bool& value); + const bool& getPaged() const; + //! Sets the location at which to position the model. //! Call before opening the layer. - void setPosition(const GeoPoint& value); - const GeoPoint& getPosition() const; + void setLocation(const GeoPoint& value); + const GeoPoint& getLocation() const; //! Sets the orientation (HPR) of the model in degrees //! Call before opening the layer. - void setOrientationHPR(const osg::Vec3& value); - const osg::Vec3& getOrientationHPR() const; + void setOrientation(const osg::Vec3& value); + const osg::Vec3& getOrientation() const; + + //! Minimum terrain LOD at which to apply the mask. + //! Call before opening the layer. + void setMaskMinLevel(const unsigned& value); + const unsigned& getMaskMinLevel() const; + + //! Whether lighting should affect the model graph + void setLightingEnabled(bool value); + bool isLightingEnabled() const; //! Whether to generate shaders or use the default shaders void setShaderPolicy(const ShaderPolicy& value); const ShaderPolicy& getShaderPolicy() const; - //! minimum visibility size in pixels - void setMinPixels(float value); - const float& getMinPixels() const; + public: // deprecated + //! @deprecated - subclass ModelLayer instead of using ModelSource plugins + //! Access the underlying model source. + ModelSource* getModelSource() const { return _modelSource.get(); } public: // Layer - Status openImplementation() override; - osg::Node* getNode() const override; - void init() override; + //! Open the layer and return its status + virtual Status openImplementation(); - private: - osg::ref_ptr _root; + //! Called when this layer is added to a Map + virtual void addedToMap(const Map*); + + //! Called when this layer is removed from a Map + virtual void removedFromMap(const Map*); + + //! Node created by this model layer + virtual osg::Node* getNode() const; + + //! Generate a cache ID for this layer + virtual std::string getCacheID() const; + + protected: // Layer + + //! post-ctor initialization + virtual void init(); + + protected: + + virtual ~ModelLayer(); + + osg::ref_ptr _modelSource; + Revision _modelSourceRev; + osg::ref_ptr _cacheSettings; + osg::ref_ptr _root; }; + + using ModelLayerVector = std::vector< osg::ref_ptr >; } OSGEARTH_SPECIALIZE_CONFIG(osgEarth::ModelLayer::Options); diff --git a/src/osgEarth/ModelLayer.cpp b/src/osgEarth/ModelLayer.cpp index a75dbbecd9..a33accfebe 100644 --- a/src/osgEarth/ModelLayer.cpp +++ b/src/osgEarth/ModelLayer.cpp @@ -22,9 +22,11 @@ #include #include +#include +#include +#include #include #include -#include #define LC "[ModelLayer] " << getName() << " : " @@ -37,38 +39,126 @@ ModelLayer::Options::getConfig() const { Config conf = VisibleLayer::Options::getConfig(); - conf.set("url", url()); - conf.set("position", position()); - conf.set("orientation", orientation()); - conf.set("min_pixels", minPixels()); + conf.set("url", _url); + conf.set("lod_scale", _lodScale); + conf.set("location", _location); + conf.set("orientation", _orientation); + conf.set("loading_priority_scale", _loadingPriorityScale); + conf.set("loading_priority_offset", _loadingPriorityOffset); + conf.set("paged", _paged); + + conf.set("shader_policy", "disable", _shaderPolicy, SHADERPOLICY_DISABLE); + conf.set("shader_policy", "inherit", _shaderPolicy, SHADERPOLICY_INHERIT); + conf.set("shader_policy", "generate", _shaderPolicy, SHADERPOLICY_GENERATE); - conf.set("shader_policy", "disable", shaderPolicy(), SHADERPOLICY_DISABLE); - conf.set("shader_policy", "inherit", shaderPolicy(), SHADERPOLICY_INHERIT); - conf.set("shader_policy", "generate", shaderPolicy(), SHADERPOLICY_GENERATE); + conf.set("lighting", _lightingEnabled); + conf.set("mask_min_level", _maskMinLevel); - conf.set( "lighting", lightingEnabled() ); + if (driver().isSet()) + conf.merge(driver()->getConfig()); return conf; } void -ModelLayer::Options::fromConfig( const Config& conf ) +ModelLayer::Options::fromConfig(const Config& conf) { - conf.get("url", url()); - conf.get("position", position()); - conf.get("location", position()); - conf.get("orientation", orientation()); - conf.get("min_pixels", minPixels()); - - conf.get("shader_policy", "disable", shaderPolicy(), SHADERPOLICY_DISABLE); - conf.get("shader_policy", "inherit", shaderPolicy(), SHADERPOLICY_INHERIT); - conf.get("shader_policy", "generate", shaderPolicy(), SHADERPOLICY_GENERATE); - - conf.get( "lighting", lightingEnabled()); + _lightingEnabled.init(true); + _maskMinLevel.init(0); + _lodScale.init(1.0f); + _shaderPolicy.init(SHADERPOLICY_GENERATE); + _loadingPriorityScale.init(1.0f); + _loadingPriorityOffset.init(0.0f); + _paged.init(false); + + conf.get("url", _url); + conf.get("lod_scale", _lodScale); + conf.get("location", _location); + conf.get("position", _location); + conf.get("orientation", _orientation); + conf.get("loading_priority_scale", _loadingPriorityScale); + conf.get("loading_priority_offset", _loadingPriorityOffset); + conf.get("paged", _paged); + + conf.get("shader_policy", "disable", _shaderPolicy, SHADERPOLICY_DISABLE); + conf.get("shader_policy", "inherit", _shaderPolicy, SHADERPOLICY_INHERIT); + conf.get("shader_policy", "generate", _shaderPolicy, SHADERPOLICY_GENERATE); + + conf.get("lighting", _lightingEnabled); + conf.get("mask_min_level", _maskMinLevel); + + if (conf.hasValue("driver")) + driver() = ModelSourceOptions(conf); } +//------------------------------------------------------------------------ + namespace { + class LODScaleOverrideNode : public osg::Group + { + public: + LODScaleOverrideNode() : m_lodScale(1.0f) {} + virtual ~LODScaleOverrideNode() {} + public: + void setLODScale(float scale) { m_lodScale = scale; } + float getLODScale() const { return m_lodScale; } + + virtual void traverse(osg::NodeVisitor& nv) + { + if (nv.getVisitorType() == osg::NodeVisitor::CULL_VISITOR) + { + osg::CullStack* cullStack = dynamic_cast(&nv); + if (cullStack) + { + float oldLODScale = cullStack->getLODScale(); + cullStack->setLODScale(oldLODScale * m_lodScale); + osg::Group::traverse(nv); + cullStack->setLODScale(oldLODScale); + } + else + osg::Group::traverse(nv); + } + else + osg::Group::traverse(nv); + } + + private: + float m_lodScale; + }; + + class SetLoadPriorityVisitor : public osg::NodeVisitor + { + public: + SetLoadPriorityVisitor(float scale = 1.0f, float offset = 0.0f) + : osg::NodeVisitor(osg::NodeVisitor::TRAVERSE_ALL_CHILDREN) + , m_scale(scale) + , m_offset(offset) + { + setNodeMaskOverride(~0); + } + + virtual void apply(osg::PagedLOD& node) + { + for (unsigned n = 0; n < node.getNumFileNames(); n++) + { + float old; + old = node.getPriorityScale(n); + node.setPriorityScale(n, old * m_scale); + old = node.getPriorityOffset(n); + node.setPriorityOffset(n, old + m_offset); + } + traverse(node); + } + + private: + float m_scale; + float m_offset; + }; + + /** + * Visitor that sets the DBOptions on deferred-loading nodes. + */ class SetDBOptionsVisitor : public osg::NodeVisitor { private: @@ -102,77 +192,23 @@ namespace REGISTER_OSGEARTH_LAYER(model, ModelLayer); -void -ModelLayer::setURL(const URI& value) -{ - options().url() = value; -} +OE_LAYER_PROPERTY_IMPL(ModelLayer, URI, URL, url); +OE_LAYER_PROPERTY_IMPL(ModelLayer, float, LODScale, lodScale); +OE_LAYER_PROPERTY_IMPL(ModelLayer, bool, Paged, paged); +OE_LAYER_PROPERTY_IMPL(ModelLayer, GeoPoint, Location, location); +OE_LAYER_PROPERTY_IMPL(ModelLayer, osg::Vec3, Orientation, orientation); +OE_LAYER_PROPERTY_IMPL(ModelLayer, unsigned, MaskMinLevel, maskMinLevel); +OE_LAYER_PROPERTY_IMPL(ModelLayer, ShaderPolicy, ShaderPolicy, shaderPolicy); -const URI& -ModelLayer::getURL() const +ModelLayer::~ModelLayer() { - return options().url().get(); + //nop } -void -ModelLayer::setShaderPolicy(const ShaderPolicy& value) -{ - options().shaderPolicy() = value; -} - -const -ShaderPolicy& ModelLayer::getShaderPolicy() const -{ - return options().shaderPolicy().get(); -} - -void -ModelLayer::setPosition(const GeoPoint& value) -{ - options().position() = value; - - auto xform = findNode(getNode()); - if (xform) - { - xform->setPosition(value); - } -} - -const GeoPoint& -ModelLayer::getPosition() const -{ - return options().position().value(); -} - -void -ModelLayer::setOrientationHPR(const osg::Vec3& hpr) -{ - options().orientation() = hpr; - - auto pat = findNode(getNode()); - if (pat) - { - osg::Matrix rot_mat; - rot_mat.makeRotate( - osg::DegreesToRadians(options().orientation()->y()), osg::Vec3(1, 0, 0), - osg::DegreesToRadians(options().orientation()->x()), osg::Vec3(0, 0, 1), - osg::DegreesToRadians(options().orientation()->z()), osg::Vec3(0, 1, 0)); - pat->setAttitude(rot_mat.getRotate()); - } -} - -const osg::Vec3& -ModelLayer::getOrientationHPR() const -{ - return options().orientation().value(); -} - - - void ModelLayer::init() { - super::init(); + VisibleLayer::init(); _root = new osg::Group(); _root->setName(getName()); } @@ -180,12 +216,35 @@ ModelLayer::init() Status ModelLayer::openImplementation() { - Status parentStatus = super::openImplementation(); + Status parentStatus = VisibleLayer::openImplementation(); if (parentStatus.isError()) return parentStatus; + // Do we need to load a model source? + if (!_modelSource.valid() && options().driver().isSet()) + { + std::string driverName = options().driver()->getDriver(); + + OE_INFO << LC << "Opening; driver=\"" << driverName << "\"" << std::endl; + + Status status; + + // Try to create the model source: + _modelSource = ModelSourceFactory::create(options().driver().get()); + if (_modelSource.valid()) + { + _modelSource->setName(this->getName()); + const Status& modelStatus = _modelSource->open(getReadOptions()); + return modelStatus; + } + else + { + return Status(Status::ServiceUnavailable, Stringify() << "Failed to create driver \"" << driverName << "\""); + } + } + // Do we have a model URL to load? - if (options().url().isSet()) + else if (!_modelSource.valid() && options().url().isSet()) { osg::ref_ptr localReadOptions = Registry::instance()->cloneOrCreateOptions(getReadOptions()); @@ -194,11 +253,30 @@ ModelLayer::openImplementation() localReadOptions->getDatabasePathList().push_back( osgDB::getFilePath(options().url()->full())); - osg::ref_ptr modelParent; + // Only support paging if user has enabled it and provided a min/max range + bool paged = + (options().paged() == true) && + (options().minVisibleRange().isSet() || options().maxVisibleRange().isSet()); + + osg::ref_ptr result; + osg::ref_ptr modelNode; + osg::ref_ptr modelNodeParent; + + // If we're not paging, just load the node now: + if (!paged) + { + ReadResult rr = options().url()->readNode(localReadOptions.get()); + if (rr.failed()) + { + return Status(Status::ResourceUnavailable, + Stringify() << "Failed to load model from URL (" << rr.errorDetail() << ")"); + } + modelNode = rr.getNode(); + } // Apply the location and orientation, if available: - GeoTransform* geo = nullptr; - osg::PositionAttitudeTransform* pat = nullptr; + GeoTransform* geo = 0L; + osg::PositionAttitudeTransform* pat = 0L; if (options().orientation().isSet()) { @@ -209,89 +287,266 @@ ModelLayer::openImplementation() osg::DegreesToRadians(options().orientation()->x()), osg::Vec3(0, 0, 1), osg::DegreesToRadians(options().orientation()->z()), osg::Vec3(0, 1, 0)); pat->setAttitude(rot_mat.getRotate()); - modelParent = pat; + modelNodeParent = pat; } - if (options().position().isSet()) + if (options().location().isSet()) { geo = new GeoTransform(); - geo->setPosition(options().position().get()); + geo->setPosition(options().location().get()); if (pat) geo->addChild(pat); - else - modelParent = geo; + + modelNodeParent = geo; } - float minRange = options().minVisibleRange().getOrUse(0.0f); - float maxRange = options().maxVisibleRange().getOrUse(FLT_MAX); + result = modelNodeParent.get(); - PagedNode2* plod = new PagedNode2(); + if (options().minVisibleRange().isSet() || options().maxVisibleRange().isSet()) + { + float minRange = options().minVisibleRange().getOrUse(0.0f); + float maxRange = options().maxVisibleRange().getOrUse(FLT_MAX); - URI uri = options().url().get(); - auto localOptions = options(); + osg::Group* group = nullptr; - auto loader = [localOptions, localReadOptions](Cancelable*) + if (!paged) { - osg::ref_ptr node = localOptions.url()->getNode(localReadOptions.get()); - if (node.valid()) + // Just use a regular LOD + osg::LOD* lod = new osg::LOD(); + lod->addChild(modelNode.release()); + lod->setRange(0, minRange, maxRange); + group = lod; + } + else + { + PagedNode2* plod = new PagedNode2(); + + URI uri = options().url().get(); + + plod->setLoadFunction([uri, localReadOptions](Cancelable*) { + osg::ref_ptr node = uri.getNode(localReadOptions.get()); + ShaderGenerator gen; + node->accept(gen); + return node; + }); + + plod->setMinRange(minRange); + plod->setMaxRange(maxRange); + + // FIXME: Tries to set the PagedNode center based on a GeoPoint; + // if said GeoPoint has an ALTMODE_RELATIVE it will fail to resolve and + // will print the ILLEGAL toWorld message. + // Options: (a) Use a terrain callback to set the Z value (b) use ElevationRanges + // (c) use a ModelNode instead + osg::Vec3d center; + if (options().location().isSet()) + { + options().location()->toWorld(center); + } + else { - if (localOptions.shaderPolicy() == SHADERPOLICY_GENERATE) - { - osg::ref_ptr cache = new StateSetCache(); - Registry::shaderGenerator().run(node, localOptions.url()->base(), cache.get()); - } - else if (localOptions.shaderPolicy() == SHADERPOLICY_DISABLE) - { - node->getOrCreateStateSet()->setAttributeAndModes( - new osg::Program(), - osg::StateAttribute::OFF | osg::StateAttribute::OVERRIDE); - } - - // apply the DB options if there are any, so that deferred nodes like PagedLOD et al - // will inherit the loading options. - if (localReadOptions.valid()) - { - SetDBOptionsVisitor setDBO(localReadOptions.get()); - node->accept(setDBO); - } + center = result->getBound().center(); } + plod->setCenter(center); - return node; - }; + if (result.valid()) + { + plod->setRadius(std::max(result->getBound().radius(), maxRange)); + } + else + { + plod->setRadius(maxRange); + } - plod->setLoadFunction(loader); + group = plod; + } - if (options().minPixels().isSet()) - { - plod->setMinPixels(options().minPixels().value()); + modelNodeParent->addChild(group); } else { - plod->setMinRange(minRange); - plod->setMaxRange(maxRange); + // Simply add the node to the matrix transform + if (modelNode.valid() && modelNodeParent.valid()) + { + modelNodeParent->addChild(modelNode.get()); + } } - // Only supports GeoPoint ALTMODE_ABSOLUTE. - osg::Vec3d center; - if (options().position().isSet()) - options().position()->toWorld(center); + if (!result.valid()) + { + result = modelNode.get(); + } - plod->setCenter(center); - plod->setRadius(maxRange); + if (result.valid()) + { + if (options().loadingPriorityScale().isSet() || options().loadingPriorityOffset().isSet()) + { + SetLoadPriorityVisitor slpv(options().loadingPriorityScale().value(), options().loadingPriorityOffset().value()); + result->accept(slpv); + } - if (modelParent.valid()) - modelParent->addChild(plod); + if (options().lodScale().isSet()) + { + LODScaleOverrideNode* node = new LODScaleOverrideNode; + node->setLODScale(options().lodScale().value()); + node->addChild(result.release()); + result = node; + } - auto result = modelParent.valid() ? modelParent : plod; + if (options().shaderPolicy() == SHADERPOLICY_GENERATE) + { + osg::ref_ptr cache = new StateSetCache(); + + Registry::shaderGenerator().run( + result.get(), + options().url()->base(), + cache.get()); + } + else if (options().shaderPolicy() == SHADERPOLICY_DISABLE) + { + result->getOrCreateStateSet()->setAttributeAndModes( + new osg::Program(), + osg::StateAttribute::OFF | osg::StateAttribute::OVERRIDE); + } + + // apply the DB options if there are any, so that deferred nodes like PagedLOD et al + // will inherit the loading options. + if (localReadOptions.valid()) + { + SetDBOptionsVisitor setDBO(localReadOptions.get()); + result->accept(setDBO); + } + } - _root->addChild(result); + if (result.valid()) + { + setNode(result.release()); + } } return Status::NoError; } +std::string +ModelLayer::getCacheID() const +{ + // Customized from the base class to use the driver() config instead of full config: + std::string binID; + if (options().cacheId().isSet() && !options().cacheId()->empty()) + { + binID = options().cacheId().get(); + } + else + { + Config conf = options().driver()->getConfig(); + binID = hashToString(conf.toJSON(false)); + } + return binID; +} + +void +ModelLayer::addedToMap(const Map* map) +{ + VisibleLayer::addedToMap(map); + + if (getStatus().isError()) + return; + + // Using a model source? + if (_modelSource.valid()) + { + // reset the scene graph. + while (_root->getNumChildren() > 0) + { + getSceneGraphCallbacks()->fireRemoveNode(_root->getChild(0)); + _root->removeChildren(0, 1); + } + + // Share the scene graph callbacks with the model source: + _modelSource->setSceneGraphCallbacks(getSceneGraphCallbacks()); + + // Create the scene graph from the mode source: + osg::ref_ptr node = _modelSource->createNode(map, 0L); + if (node.valid()) + { + if (options().lightingEnabled().isSet()) + { + setLightingEnabled(options().lightingEnabled().get()); + } + + _modelSource->sync(_modelSourceRev); + + // Handle disabling depth testing + if (_modelSource->getOptions().depthTestEnabled() == false) + { + osg::StateSet* ss = node->getOrCreateStateSet(); + ss->setAttributeAndModes(new osg::Depth(osg::Depth::ALWAYS)); + ss->setRenderBinDetails(99999, "RenderBin"); //TODO: configure this bin ... + } + + // enfore a rendering bin if necessary: + if (_modelSource->getOptions().renderOrder().isSet()) + { + osg::StateSet* ss = node->getOrCreateStateSet(); + ss->setRenderBinDetails( + _modelSource->getOptions().renderOrder().value(), + ss->getBinName().empty() ? "DepthSortedBin" : ss->getBinName()); + } + + if (_modelSource->getOptions().renderBin().isSet()) + { + osg::StateSet* ss = node->getOrCreateStateSet(); + ss->setRenderBinDetails( + ss->getBinNumber(), + _modelSource->getOptions().renderBin().get()); + } + + _root->addChild(node.get()); + } + } +} + +void +ModelLayer::removedFromMap(const Map* map) +{ + // dispose of the scene graph. + while (_root->getNumChildren() > 0) + { + getSceneGraphCallbacks()->fireRemoveNode(_root->getChild(0)); + _root->removeChildren(0, 1); + } +} + +void +ModelLayer::setNode(osg::Node* node) +{ + _root->removeChildren(0, _root->getNumChildren()); + if (node) + { + _root->addChild(node); + setStatus(Status::OK()); + } +} + osg::Node* ModelLayer::getNode() const { return _root.get(); } + +void +ModelLayer::setLightingEnabled(bool value) +{ + options().lightingEnabled() = value; + + GLUtils::setLighting( + _root->getOrCreateStateSet(), + value ? osg::StateAttribute::ON : + (osg::StateAttribute::OFF | osg::StateAttribute::PROTECTED)); +} + +bool +ModelLayer::isLightingEnabled() const +{ + return options().lightingEnabled().get(); +}