/* -*-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. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS * IN THE SOFTWARE. * * You should have received a copy of the GNU Lesser General Public License * along with this program. If not, see */ #pragma once #include #include #include #include #include #include #include #include #include #include namespace osgEarth { namespace Util { using namespace osgEarth; /** * Visitor that finds all the parental Camera Views, and calls an operator * on each one. */ template class ViewVisitor : public osg::NodeVisitor, public T { public: ViewVisitor() : osg::NodeVisitor(osg::NodeVisitor::TRAVERSE_PARENTS) { } virtual ~ViewVisitor() { } void apply(osg::Camera& cam) { osg::View* view = cam.getView(); if ( view ) this->operator()( view ); traverse(cam); } }; /** * Visitor that locates a node by its type */ template class FindTopMostNodeOfTypeVisitor : public osg::NodeVisitor { public: FindTopMostNodeOfTypeVisitor(): osg::NodeVisitor(osg::NodeVisitor::TRAVERSE_ALL_CHILDREN), _foundNode(0) {} void apply(osg::Node& node) { T* result = dynamic_cast(&node); if (result) { _foundNode = result; } else { traverse(node); } } T* _foundNode; }; /** * Collects all the nodes of type "T" */ template struct FindNodesVisitor : public osg::NodeVisitor { FindNodesVisitor() : osg::NodeVisitor(osg::NodeVisitor::TRAVERSE_ALL_CHILDREN) { } void apply(osg::Node& node) { T* result = dynamic_cast( &node ); if ( result ) _results.push_back( result ); traverse(node); } std::vector _results; }; template struct NodeFunctorVisitor : public osg::NodeVisitor { NodeFunctorVisitor(const FUNC& func) : osg::NodeVisitor(osg::NodeVisitor::TRAVERSE_ALL_CHILDREN), _func(func) { } void apply(osg::Node& node) override { T* result = dynamic_cast( &node ); if ( result ) _func( result ); traverse(node); } FUNC _func; }; template inline void forEachNodeOfType(osg::Node* root, const FUNC& functor) { NodeFunctorVisitor visitor(functor); if (root) root->accept( visitor ); } /** * Searchs the scene graph downward starting at [node] and returns the first node found * that matches the template parameter type. */ template T* findTopMostNodeOfType(osg::Node* node, unsigned traversalMask =~0) { if (!node) return 0; FindTopMostNodeOfTypeVisitor fnotv; fnotv.setTraversalMode(osg::NodeVisitor::TRAVERSE_ALL_CHILDREN); fnotv.setTraversalMask(traversalMask); node->accept(fnotv); return fnotv._foundNode; } //! Synonym for findTopMostNodeOfType. template T* findNode(osg::Node* node, unsigned traversalMask = ~0) { return findTopMostNodeOfType(node, traversalMask); } /** * Searchs the scene graph upward starting at [node] and returns the first node found * that matches the template parameter type. */ template T* findFirstParentOfType(osg::Node* node, unsigned traversalMask =~0) { if (!node) return 0; FindTopMostNodeOfTypeVisitor fnotv; fnotv.setTraversalMode(osg::NodeVisitor::TRAVERSE_PARENTS); fnotv.setTraversalMask(traversalMask); node->accept(fnotv); return fnotv._foundNode; } /** * Searchs the scene graph starting at [node] and returns the first node found * that matches the template parameter type. First searched upward, then downward. */ template T* findRelativeNodeOfType(osg::Node* node, unsigned traversalMask =~0) { if ( !node ) return 0; T* result = findFirstParentOfType( node, traversalMask ); if ( !result ) result = findTopMostNodeOfType( node, traversalMask ); return result; } /** Find the top of the scene graph through parent 0 */ inline osg::Node* findTopOfGraph(osg::Node* node) { return node && node->getNumParents() > 0 ? findTopOfGraph(node->getParent(0)) : node; } /** Finds a typed node in a node visitor's node path */ template T* findInNodePath(osg::NodeVisitor& nv) { for (osg::NodePath::iterator i = nv.getNodePath().begin(); i != nv.getNodePath().end(); ++i) { T* node = dynamic_cast(*i); if (node) return node; } return 0L; } /** Finds all the siblings of a node */ inline osg::NodeList findSiblings(osg::Node* node) { osg::NodeList output; if (node && node->getNumParents() > 0) { auto parent = node->getParent(0); for(unsigned i=0; igetNumChildren(); ++i) if (parent->getChild(i) != node) output.push_back(parent->getChild(i)); } return output; } class FindNamedNodeVisitor : public osg::NodeVisitor { public: FindNamedNodeVisitor(const std::string& name) : osg::NodeVisitor(osg::NodeVisitor::TRAVERSE_ALL_CHILDREN), _name(name) { setTraversalMask(~0); } virtual void apply(osg::Node& node) { if (node.getName() == _name) { _foundNodes.push_back(&node); } traverse(node); } typedef std::vector< osg::ref_ptr > NodeList; std::string _name; NodeList _foundNodes; }; inline osg::Node* findNamedNode(osg::Node* node, const std::string& name) { FindNamedNodeVisitor v(name); node->accept(v); if (!v._foundNodes.empty()) { return v._foundNodes[0].get(); } return 0; } /** * Replace one group with another */ inline void replaceGroup( osg::Group* oldGroup, osg::Group* newGroup ) { if ( oldGroup && newGroup && oldGroup->getNumParents() > 0 ) { for(unsigned i=0; igetNumChildren(); ++i) { newGroup->addChild( oldGroup->getChild(i) ); } osg::Node::ParentList parents = oldGroup->getParents(); for(osg::Node::ParentList::iterator i = parents.begin(); i != parents.end(); ++i ) { (*i)->replaceChild( oldGroup, newGroup ); } } } /** Insert a group between a parent and its children. */ inline void insertGroup(osg::Group* newGroup, osg::Group* parent) { if (parent && newGroup) { for(unsigned i=0; igetNumChildren(); ++i) { newGroup->addChild( parent->getChild(i) ); } parent->removeChildren(0, parent->getNumChildren()); parent->addChild( newGroup ); } } /** Insert a group above a node. */ inline void insertParent(osg::Group* newParent, osg::Node* existingChild) { if ( newParent && existingChild ) { newParent->addChild(existingChild); for (unsigned i = 0; i < existingChild->getNumParents(); ++i) { osg::Group* parent = existingChild->getParent(i); if ( parent != newParent ) { parent->removeChild( existingChild ); parent->addChild( newParent ); } } } } /** Remove a group from the middle of a scene graph */ inline void removeGroup(osg::Group* group) { if (group) { osg::ref_ptr g = group; while (g->getNumParents() > 0) { osg::Group* parent = group->getParent(group->getNumParents()-1); for (unsigned c = 0; c < group->getNumChildren(); ++c) { parent->addChild(group->getChild(c)); } parent->removeChild(group); } } } /** * Remove all a group's children. */ inline void clearChildren( osg::Group* group ) { if ( group ) group->removeChildren( 0, group->getNumChildren() ); } /** * OSG Group that keeps its children as observer_ptrs instead of ref_ptrs, and * removes them when they deref. */ class OSGEARTH_EXPORT ObserverGroup : public osg::Group { public: ObserverGroup(); virtual void traverse( osg::NodeVisitor& nv ); std::set _orphans; }; /** * Group that acts like a normal group but also notifies another * object when a change occurs. */ template class NotifierGroup : public osg::Group { public: NotifierGroup(T* listener) : _listener(listener) { } virtual bool addChild( osg::Node* child ) { bool ok = osg::Group::addChild(child); if ( ok && _listener.valid() ) _listener->onGroupChanged(this); return ok; } virtual bool insertChild( unsigned index, osg::Node* child ) { bool ok = osg::Group::insertChild(index, child); if ( ok && _listener.valid() ) _listener->onGroupChanged(this); return ok; } virtual bool removeChild( osg::Node* child ) { bool ok = osg::Group::removeChild( child ); if ( ok && _listener.valid() ) _listener->onGroupChanged(this); return ok; } virtual bool replaceChild( osg::Node* origChild, osg::Node* newChild ) { bool ok = osg::Group::replaceChild(origChild, newChild); if ( ok && _listener.valid() ) _listener->onGroupChanged(this); return ok; } protected: virtual ~NotifierGroup() { } osg::observer_ptr _listener; }; /** * Adjusts a node's update traversal count by a delta. * Only safe to call from the UPDATE thread */ #define ADJUST_UPDATE_TRAV_COUNT( NODE, DELTA ) \ { \ unsigned oldCount = NODE ->getNumChildrenRequiringUpdateTraversal(); \ unsigned newCount = (unsigned)(((int)oldCount)+(DELTA)); \ if ( ((DELTA) < 0 && newCount < oldCount) || ((DELTA) >= 0 && newCount >= oldCount) ) { \ NODE ->setNumChildrenRequiringUpdateTraversal( newCount ); \ } else { \ OE_WARN << "**INTERNAL: ADJUST_UPDATE_TRAV_COUNT wrapped around" << std::endl; \ } \ } /** * Adjusts a node's event traversal count by a delta. * Only safe to call from the main/event/update threads */ #define ADJUST_EVENT_TRAV_COUNT( NODE, DELTA ) \ { \ unsigned oldCount = NODE ->getNumChildrenRequiringEventTraversal(); \ unsigned newCount = (unsigned)(((int)oldCount)+(DELTA)); \ if ( ((DELTA) < 0 && newCount < oldCount) || ((DELTA) >= 0 && newCount >= oldCount) ) { \ NODE ->setNumChildrenRequiringEventTraversal( newCount ); \ } else { \ OE_WARN << "**INTERNAL: ADJUST_EVENT_TRAV_COUNT wrapped around" << std::endl; \ } \ } /** * Enables auto unloading on any PagedNode2 in the scene graph * This is useful if you've used the LoadDataVisitor to * preload an area and you no longer care about it. */ struct OSGEARTH_EXPORT EnableAutoUnloadVisitor : public osg::NodeVisitor { EnableAutoUnloadVisitor(); void apply(osg::Node& node); }; /** * Preloads data within a list of bounding spheres so that the data is available without * actually viewing it first. * Once the visitor has been sent down a scene graph, you can check the isFullyLoaded * property to determine if all of the data in that area has been loaded. If isFullyLoaded is false * then send the visitor down again until isFullyLoaded returns true. */ struct OSGEARTH_EXPORT LoadDataVisitor : public osg::NodeVisitor { LoadDataVisitor(); bool getLoadHighestResolutionOnly() const; void setLoadHighestResolutionOnly(bool value); //! Whether all potential data in the areas are loaded bool isFullyLoaded() const; //! Resets isFullyLoaded so you can reuse the same visitor multiple times. void reset(); bool intersects(osg::Node& node); //! A list of bounding spheres in world coordinates to intersect the scene graph against. std::vector& getAreasToLoad(); void apply(osg::Node& node); void apply(LoadableNode& loadableNode); void apply(osg::Transform& transform); /** * Most async nodes will require an update traversal to be called. If you calling this visitor outside * of a normal frame loop you will need to call manualUpdate after you run the visitor in order to update the scene graph properly */ void manualUpdate(); inline void pushMatrix(osg::Matrix& matrix) { _matrixStack.push_back(matrix); } inline void popMatrix() { _matrixStack.pop_back(); } typedef std::vector MatrixStack; MatrixStack _matrixStack; bool _fullyLoaded = true; std::set < osg::ref_ptr < PagingManager> > _pagingManagers; std::vector< osg::BoundingSphered > _areasToLoad; bool _loadHighestResolutionOnly = false; }; /** * Loads async data within the given areas and sets the nodes to not expire. */ extern OSGEARTH_EXPORT void loadData(osg::Node* node, std::vector& areasToLoad); } }