476 lines
15 KiB
Plaintext
476 lines
15 KiB
Plaintext
|
/* -*-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 <http://www.gnu.org/licenses/>
|
||
|
*/
|
||
|
#pragma once
|
||
|
|
||
|
#include <osgEarth/Common>
|
||
|
#include <osg/View>
|
||
|
#include <osg/PagedLOD>
|
||
|
#include <osgEarth/LoadableNode>
|
||
|
#include <osgEarth/PagedNode>
|
||
|
#include <osgGA/GUIActionAdapter>
|
||
|
#include <osgUtil/LineSegmentIntersector>
|
||
|
#include <osgEarth/Threading>
|
||
|
#include <set>
|
||
|
#include <vector>
|
||
|
|
||
|
namespace osgEarth { namespace Util
|
||
|
{
|
||
|
using namespace osgEarth;
|
||
|
|
||
|
/**
|
||
|
* Visitor that finds all the parental Camera Views, and calls an operator
|
||
|
* on each one.
|
||
|
*/
|
||
|
template<typename T>
|
||
|
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<typename T>
|
||
|
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<T*>(&node);
|
||
|
if (result)
|
||
|
{
|
||
|
_foundNode = result;
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
traverse(node);
|
||
|
}
|
||
|
}
|
||
|
|
||
|
T* _foundNode;
|
||
|
};
|
||
|
|
||
|
/**
|
||
|
* Collects all the nodes of type "T"
|
||
|
*/
|
||
|
template<typename T>
|
||
|
struct FindNodesVisitor : public osg::NodeVisitor
|
||
|
{
|
||
|
FindNodesVisitor() : osg::NodeVisitor(osg::NodeVisitor::TRAVERSE_ALL_CHILDREN) { }
|
||
|
|
||
|
void apply(osg::Node& node)
|
||
|
{
|
||
|
T* result = dynamic_cast<T*>( &node );
|
||
|
if ( result )
|
||
|
_results.push_back( result );
|
||
|
traverse(node);
|
||
|
}
|
||
|
|
||
|
std::vector<T*> _results;
|
||
|
};
|
||
|
|
||
|
template<typename T, typename FUNC>
|
||
|
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<T*>( &node );
|
||
|
if ( result )
|
||
|
_func( result );
|
||
|
traverse(node);
|
||
|
}
|
||
|
|
||
|
FUNC _func;
|
||
|
};
|
||
|
|
||
|
template<typename T, typename FUNC>
|
||
|
inline void forEachNodeOfType(osg::Node* root, const FUNC& functor)
|
||
|
{
|
||
|
NodeFunctorVisitor<T, FUNC> 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<typename T>
|
||
|
T* findTopMostNodeOfType(osg::Node* node, unsigned traversalMask =~0)
|
||
|
{
|
||
|
if (!node) return 0;
|
||
|
|
||
|
FindTopMostNodeOfTypeVisitor<T> fnotv;
|
||
|
fnotv.setTraversalMode(osg::NodeVisitor::TRAVERSE_ALL_CHILDREN);
|
||
|
fnotv.setTraversalMask(traversalMask);
|
||
|
node->accept(fnotv);
|
||
|
|
||
|
return fnotv._foundNode;
|
||
|
}
|
||
|
|
||
|
//! Synonym for findTopMostNodeOfType.
|
||
|
template<typename T>
|
||
|
T* findNode(osg::Node* node, unsigned traversalMask = ~0) {
|
||
|
return findTopMostNodeOfType<T>(node, traversalMask);
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* Searchs the scene graph upward starting at [node] and returns the first node found
|
||
|
* that matches the template parameter type.
|
||
|
*/
|
||
|
template<typename T>
|
||
|
T* findFirstParentOfType(osg::Node* node, unsigned traversalMask =~0)
|
||
|
{
|
||
|
if (!node) return 0;
|
||
|
|
||
|
FindTopMostNodeOfTypeVisitor<T> 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<typename T>
|
||
|
T* findRelativeNodeOfType(osg::Node* node, unsigned traversalMask =~0)
|
||
|
{
|
||
|
if ( !node ) return 0;
|
||
|
T* result = findFirstParentOfType<T>( node, traversalMask );
|
||
|
if ( !result )
|
||
|
result = findTopMostNodeOfType<T>( 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<typename T>
|
||
|
T* findInNodePath(osg::NodeVisitor& nv) {
|
||
|
for (osg::NodePath::iterator i = nv.getNodePath().begin(); i != nv.getNodePath().end(); ++i) {
|
||
|
T* node = dynamic_cast<T*>(*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; i<parent->getNumChildren(); ++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<osg::Node> > 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; i<oldGroup->getNumChildren(); ++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; i<parent->getNumChildren(); ++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<osg::Group> 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<osg::Node*> _orphans;
|
||
|
};
|
||
|
|
||
|
|
||
|
/**
|
||
|
* Group that acts like a normal group but also notifies another
|
||
|
* object when a change occurs.
|
||
|
*/
|
||
|
template<typename T>
|
||
|
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<T> _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<osg::BoundingSphered>& 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<osg::Matrix> 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<osg::BoundingSphered>& areasToLoad);
|
||
|
|
||
|
} }
|