Hi Robert,
I've made some modifications on LOD and PagedLOD while working on the
high-res screen autocapturing functionality (osgautocapture +
osgposter example, but not finished). I just added a USER_SPECIFIED
item to the range mode enumeration and a setActiveChild() method, to
force load specified level of details instead of computing the
distance from eyepoint, if needed be.
To make it work, developers should change the range mode first, and
then decide which child of the LOD node will become active:
lod->setRangeMode( osg::LOD::USER_SPECIFIED );
lod->setActiveChild( 1 );
Child node at index 1 will be accepted (or automatically) in update
and cull traversals, regardless of min/max range settings.
And it's easy to reset:
lod->setRangeMode( osg::LOD::DISTANCE_FROM_EYE_POINT );
This mechanism should be useful if we want to load specified level of
a LOD node at some time. For instance, high-res capturing requires
multiple projection matrices for sub-rendering cameras, but leaves the
modelview matrix alone. In that case, we could either set the LOD
scale to a very low value, or keep the camera close to the ground to
obtain highest levels of a terrain with quadtree. But both are not
good enough, I think.
If it's possible to set the active child of a LOD or PagedLOD
directly, a developer may force loading the highest levels of a
limited area of terrain tiles and capture a sub-image, and then
another area, until he collects all the sub-images and constructs the
entire poster. This would be easier and much more efficient than
loading the whole detailed terrain. What's your opinion?
Cheers,
Wang Rui
/* -*-c++-*- OpenSceneGraph - Copyright (C) 1998-2006 Robert Osfield
*
* This library is open source and may be redistributed and/or modified under
* the terms of the OpenSceneGraph Public License (OSGPL) version 0.0 or
* (at your option) any later version. The full license is in LICENSE file
* included with this distribution, and on the openscenegraph.org website.
*
* This library 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
* OpenSceneGraph Public License for more details.
*/
#ifndef OSG_LOD
#define OSG_LOD 1
#include <osg/Group>
namespace osg {
/** LOD - Level Of Detail group node which allows switching between children
depending on distance from eye point.
Typical uses are for load balancing - objects further away from
the eye point are rendered at a lower level of detail, and at times
of high stress on the graphics pipeline lower levels of detail can
also be chosen by adjusting the viewers's Camera/CullSettings LODScale
value.
Each child has a corresponding valid range consisting of a minimum
and maximum distance. Given a distance to the viewer (d), LOD displays
a child if min <= d < max. LOD may display multiple children simultaneously
if their corresponding ranges overlap. Children can be in any order,
and don't need to be sorted by range or amount of detail. If the number of
ranges (m) is less than the number of children (n), then children m+1
through
n are ignored.
*/
class OSG_EXPORT LOD : public Group
{
public :
LOD();
/** Copy constructor using CopyOp to manage deep vs shallow copy.*/
LOD(const LOD&,const CopyOp& copyop=CopyOp::SHALLOW_COPY);
META_Node(osg, LOD);
typedef osg::BoundingSphere::vec_type vec_type;
typedef osg::BoundingSphere::value_type value_type;
virtual void traverse(NodeVisitor& nv);
virtual bool addChild(Node *child);
virtual bool addChild(Node *child, float min, float max);
virtual bool removeChildren(unsigned int pos,unsigned int
numChildrenToRemove=1);
typedef std::pair<float,float> MinMaxPair;
typedef std::vector<MinMaxPair> RangeList;
/** Modes which control how the center of object should be determined
when computing which child is active.*/
enum CenterMode
{
USE_BOUNDING_SPHERE_CENTER,
USER_DEFINED_CENTER
};
/** Set how the center of object should be determined when computing
which child is active.*/
void setCenterMode(CenterMode mode) { _centerMode=mode; }
/** Get how the center of object should be determined when computing
which child is active.*/
CenterMode getCenterMode() const { return _centerMode; }
/** Sets the object-space point which defines the center of the
osg::LOD.
center is affected by any transforms in the hierarchy above the
osg::LOD.*/
inline void setCenter(const vec_type& center) {
_centerMode=USER_DEFINED_CENTER; _userDefinedCenter = center; }
/** return the LOD center point. */
inline const vec_type& getCenter() const { if
(_centerMode==USER_DEFINED_CENTER) return _userDefinedCenter; else return
getBound().center(); }
/** Set the object-space reference radius of the volume enclosed by the
LOD.
* Used to determine the bounding sphere of the LOD in the absence of
any children.*/
inline void setRadius(value_type radius) { _radius = radius; }
/** Get the object-space radius of the volume enclosed by the LOD.*/
inline value_type getRadius() const { return _radius; }
/** Modes that control how the range values should be interpreted when
computing which child is active.*/
enum RangeMode
{
DISTANCE_FROM_EYE_POINT,
PIXEL_SIZE_ON_SCREEN,
USER_SPECIFIED
};
/** Set how the range values should be interpreted when computing which
child is active.*/
void setRangeMode(RangeMode mode) { _rangeMode = mode; }
/** Get how the range values should be interpreted when computing which
child is active.*/
RangeMode getRangeMode() const { return _rangeMode; }
/** Set an active child directly in user specified range mode.*/
void setActiveChild(unsigned int childNo) { _activeChild = childNo; }
/** Get the active child in user specified range mode.*/
unsigned int getActiveChild() { return _activeChild; }
/** Sets the min and max visible ranges of range of specific child.
Values are floating point distance specified in local objects
coordinates.*/
void setRange(unsigned int childNo, float min,float max);
/** returns the min visible range for specified child.*/
inline float getMinRange(unsigned int childNo) const { return
_rangeList[childNo].first; }
/** returns the max visible range for specified child.*/
inline float getMaxRange(unsigned int childNo) const { return
_rangeList[childNo].second; }
/** returns the number of ranges currently set.
* An LOD which has been fully set up will have
getNumChildren()==getNumRanges(). */
inline unsigned int getNumRanges() const { return _rangeList.size(); }
/** set the list of MinMax ranges for each child.*/
inline void setRangeList(const RangeList& rangeList) {
_rangeList=rangeList; }
/** return the list of MinMax ranges for each child.*/
inline const RangeList& getRangeList() const { return _rangeList; }
virtual BoundingSphere computeBound() const;
protected :
virtual ~LOD() {}
CenterMode _centerMode;
vec_type _userDefinedCenter;
value_type _radius;
RangeMode _rangeMode;
RangeList _rangeList;
unsigned int _activeChild;
};
}
#endif
/* -*-c++-*- OpenSceneGraph - Copyright (C) 1998-2006 Robert Osfield
*
* This library is open source and may be redistributed and/or modified under
* the terms of the OpenSceneGraph Public License (OSGPL) version 0.0 or
* (at your option) any later version. The full license is in LICENSE file
* included with this distribution, and on the openscenegraph.org website.
*
* This library 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
* OpenSceneGraph Public License for more details.
*/
#include <osg/LOD>
#include <osg/CullStack>
#include <algorithm>
using namespace osg;
LOD::LOD():
_centerMode(USE_BOUNDING_SPHERE_CENTER),
_radius(-1.0f),
_rangeMode(DISTANCE_FROM_EYE_POINT),
_activeChild(0)
{
}
LOD::LOD(const LOD& lod,const CopyOp& copyop):
Group(lod,copyop),
_centerMode(lod._centerMode),
_userDefinedCenter(lod._userDefinedCenter),
_radius(lod._radius),
_rangeMode(lod._rangeMode),
_rangeList(lod._rangeList),
_activeChild(lod._activeChild)
{
}
void LOD::traverse(NodeVisitor& nv)
{
switch(nv.getTraversalMode())
{
case(NodeVisitor::TRAVERSE_ALL_CHILDREN):
std::for_each(_children.begin(),_children.end(),NodeAcceptOp(nv));
break;
case(NodeVisitor::TRAVERSE_ACTIVE_CHILDREN):
{
if (_rangeMode==USER_SPECIFIED)
{
unsigned int numChildren = _children.size();
if (_activeChild<numChildren)
{
_children[_activeChild]->accept(nv);
}
else
{
_children[numChildren-1]->accept(nv);
}
break;
}
float required_range = 0;
if (_rangeMode==DISTANCE_FROM_EYE_POINT)
{
required_range = nv.getDistanceToViewPoint(getCenter(),true);
}
else
{
osg::CullStack* cullStack = dynamic_cast<osg::CullStack*>(&nv);
if (cullStack && cullStack->getLODScale())
{
required_range = cullStack->clampedPixelSize(getBound()) /
cullStack->getLODScale();
}
else
{
// fallback to selecting the highest res tile by
// finding out the max range
for(unsigned int i=0;i<_rangeList.size();++i)
{
required_range =
osg::maximum(required_range,_rangeList[i].first);
}
}
}
unsigned int numChildren = _children.size();
if (_rangeList.size()<numChildren) numChildren=_rangeList.size();
for(unsigned int i=0;i<numChildren;++i)
{
if (_rangeList[i].first<=required_range &&
required_range<_rangeList[i].second)
{
_children[i]->accept(nv);
}
}
break;
}
default:
break;
}
}
BoundingSphere LOD::computeBound() const
{
if (_centerMode==USER_DEFINED_CENTER && _radius>=0.0f)
{
return BoundingSphere(_userDefinedCenter,_radius);
}
else
{
return Group::computeBound();
}
}
bool LOD::addChild( Node *child )
{
if (Group::addChild(child))
{
if (_children.size()>_rangeList.size())
{
float maxRange = !_rangeList.empty() ? _rangeList.back().second :
0.0f;
_rangeList.resize(_children.size(),MinMaxPair(maxRange,maxRange));
}
return true;
}
return false;
}
bool LOD::addChild(Node *child, float min, float max)
{
if (Group::addChild(child))
{
if (_children.size()>_rangeList.size())
_rangeList.resize(_children.size(),MinMaxPair(min,min));
_rangeList[_children.size()-1].first = min;
_rangeList[_children.size()-1].second = max;
return true;
}
return false;
}
bool LOD::removeChildren( unsigned int pos,unsigned int numChildrenToRemove)
{
if (pos<_rangeList.size()) _rangeList.erase(_rangeList.begin()+pos,
osg::minimum(_rangeList.begin()+(pos+numChildrenToRemove), _rangeList.end()) );
return Group::removeChildren(pos,numChildrenToRemove);
}
void LOD::setRange(unsigned int childNo, float min,float max)
{
if (childNo>=_rangeList.size())
_rangeList.resize(childNo+1,MinMaxPair(min,min));
_rangeList[childNo].first=min;
_rangeList[childNo].second=max;
}
/* -*-c++-*- OpenSceneGraph - Copyright (C) 1998-2006 Robert Osfield
*
* This library is open source and may be redistributed and/or modified under
* the terms of the OpenSceneGraph Public License (OSGPL) version 0.0 or
* (at your option) any later version. The full license is in LICENSE file
* included with this distribution, and on the openscenegraph.org website.
*
* This library 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
* OpenSceneGraph Public License for more details.
*/
#include <osg/PagedLOD>
#include <osg/CullStack>
#include <osg/Notify>
#include <algorithm>
using namespace osg;
PagedLOD::PerRangeData::PerRangeData():
_priorityOffset(0.0f),
_priorityScale(1.0f),
_timeStamp(0.0f),
_frameNumber(0),
_frameNumberOfLastReleaseGLObjects(0) {}
PagedLOD::PerRangeData::PerRangeData(const PerRangeData& prd):
_filename(prd._filename),
_priorityOffset(prd._priorityOffset),
_priorityScale(prd._priorityScale),
_timeStamp(prd._timeStamp),
_frameNumber(prd._frameNumber),
_frameNumberOfLastReleaseGLObjects(prd._frameNumberOfLastReleaseGLObjects),
_databaseRequest(prd._databaseRequest) {}
PagedLOD::PerRangeData& PagedLOD::PerRangeData::operator = (const PerRangeData&
prd)
{
if (this==&prd) return *this;
_filename = prd._filename;
_priorityOffset = prd._priorityOffset;
_priorityScale = prd._priorityScale;
_timeStamp = prd._timeStamp;
_frameNumber = prd._frameNumber;
_frameNumberOfLastReleaseGLObjects = prd._frameNumberOfLastReleaseGLObjects;
_databaseRequest = prd._databaseRequest;
return *this;
}
PagedLOD::PagedLOD()
{
_frameNumberOfLastTraversal = 0;
_centerMode = USER_DEFINED_CENTER;
_radius = -1;
_numChildrenThatCannotBeExpired = 0;
_disableExternalChildrenPaging = false;
}
PagedLOD::PagedLOD(const PagedLOD& plod,const CopyOp& copyop):
LOD(plod,copyop),
_databaseOptions(plod._databaseOptions),
_databasePath(plod._databasePath),
_frameNumberOfLastTraversal(plod._frameNumberOfLastTraversal),
_numChildrenThatCannotBeExpired(plod._numChildrenThatCannotBeExpired),
_disableExternalChildrenPaging(plod._disableExternalChildrenPaging),
_perRangeDataList(plod._perRangeDataList)
{
}
PagedLOD::~PagedLOD()
{
}
void PagedLOD::setDatabasePath(const std::string& path)
{
_databasePath = path;
if (!_databasePath.empty())
{
char& lastCharacter = _databasePath[_databasePath.size()-1];
const char unixSlash = '/';
const char winSlash = '\\';
if (lastCharacter==winSlash)
{
lastCharacter = unixSlash;
}
else if (lastCharacter!=unixSlash)
{
_databasePath += unixSlash;
}
/*
// make sure the last character is the appropriate slash
#ifdef WIN32
if (lastCharacter==unixSlash)
{
lastCharacter = winSlash;
}
else if (lastCharacter!=winSlash)
{
_databasePath += winSlash;
}
#else
if (lastCharacter==winSlash)
{
lastCharacter = unixSlash;
}
else if (lastCharacter!=unixSlash)
{
_databasePath += unixSlash;
}
#endif
*/
}
}
void PagedLOD::traverse(NodeVisitor& nv)
{
// set the frame number of the traversal so that external nodes can find
out how active this
// node is.
if (nv.getFrameStamp() &&
nv.getVisitorType()==osg::NodeVisitor::CULL_VISITOR)
{
setFrameNumberOfLastTraversal(nv.getFrameStamp()->getFrameNumber());
}
double timeStamp =
nv.getFrameStamp()?nv.getFrameStamp()->getReferenceTime():0.0;
int frameNumber = nv.getFrameStamp()?nv.getFrameStamp()->getFrameNumber():0;
bool updateTimeStamp = nv.getVisitorType()==osg::NodeVisitor::CULL_VISITOR;
switch(nv.getTraversalMode())
{
case(NodeVisitor::TRAVERSE_ALL_CHILDREN):
std::for_each(_children.begin(),_children.end(),NodeAcceptOp(nv));
break;
case(NodeVisitor::TRAVERSE_ACTIVE_CHILDREN):
{
float required_range = 0;
if (_rangeMode==DISTANCE_FROM_EYE_POINT)
{
required_range = nv.getDistanceToViewPoint(getCenter(),true);
}
else if (_rangeMode==PIXEL_SIZE_ON_SCREEN)
{
osg::CullStack* cullStack = dynamic_cast<osg::CullStack*>(&nv);
if (cullStack && cullStack->getLODScale()>0.0f)
{
required_range = cullStack->clampedPixelSize(getBound()) /
cullStack->getLODScale();
}
else
{
// fallback to selecting the highest res tile by
// finding out the max range
for(unsigned int i=0;i<_rangeList.size();++i)
{
required_range =
osg::maximum(required_range,_rangeList[i].first);
}
}
}
int lastChildTraversed = -1;
bool needToLoadChild = false;
if (_rangeMode==USER_SPECIFIED)
{
if (_activeChild<_children.size())
{
_children[_activeChild]->accept(nv);
lastChildTraversed = (int)_activeChild;
}
else
needToLoadChild = true;
}
else
{
for(unsigned int i=0;i<_rangeList.size();++i)
{
if (_rangeList[i].first<=required_range &&
required_range<_rangeList[i].second)
{
if (i<_children.size())
{
if (updateTimeStamp)
{
_perRangeDataList[i]._timeStamp=timeStamp;
_perRangeDataList[i]._frameNumber=frameNumber;
}
_children[i]->accept(nv);
lastChildTraversed = (int)i;
}
else
needToLoadChild = true;
}
}
}
if (needToLoadChild)
{
unsigned int numChildren = _children.size();
// select the last valid child.
if (numChildren>0 && ((int)numChildren-1)!=lastChildTraversed)
{
if (updateTimeStamp)
{
_perRangeDataList[numChildren-1]._timeStamp=timeStamp;
_perRangeDataList[numChildren-1]._frameNumber=frameNumber;
}
_children[numChildren-1]->accept(nv);
}
// now request the loading of the next unloaded child.
if (!_disableExternalChildrenPaging &&
nv.getDatabaseRequestHandler() &&
numChildren<_perRangeDataList.size())
{
// compute priority from where abouts in the required range
the distance falls.
float priority =
(_rangeList[numChildren].second-required_range)/(_rangeList[numChildren].second-_rangeList[numChildren].first);
// invert priority for PIXEL_SIZE_ON_SCREEN mode
if(_rangeMode==PIXEL_SIZE_ON_SCREEN)
{
priority = -priority;
}
// modify the priority according to the child's priority
offset and scale.
priority = _perRangeDataList[numChildren]._priorityOffset +
priority * _perRangeDataList[numChildren]._priorityScale;
if (_databasePath.empty())
{
nv.getDatabaseRequestHandler()->requestNodeFile(_perRangeDataList[numChildren]._filename,this,priority,nv.getFrameStamp(),
_perRangeDataList[numChildren]._databaseRequest, _databaseOptions.get());
}
else
{
// prepend the databasePath to the child's filename.
nv.getDatabaseRequestHandler()->requestNodeFile(_databasePath+_perRangeDataList[numChildren]._filename,this,priority,nv.getFrameStamp(),
_perRangeDataList[numChildren]._databaseRequest, _databaseOptions.get());
}
}
}
break;
}
default:
break;
}
}
void PagedLOD::expandPerRangeDataTo(unsigned int pos)
{
if (pos>=_perRangeDataList.size()) _perRangeDataList.resize(pos+1);
}
bool PagedLOD::addChild( Node *child )
{
if (LOD::addChild(child))
{
expandPerRangeDataTo(_children.size()-1);
return true;
}
return false;
}
bool PagedLOD::addChild(Node *child, float min, float max)
{
if (LOD::addChild(child,min,max))
{
expandPerRangeDataTo(_children.size()-1);
return true;
}
return false;
}
bool PagedLOD::addChild(Node *child, float min, float max,const std::string&
filename, float priorityOffset, float priorityScale)
{
if (LOD::addChild(child,min,max))
{
setFileName(_children.size()-1,filename);
setPriorityOffset(_children.size()-1,priorityOffset);
setPriorityScale(_children.size()-1,priorityScale);
return true;
}
return false;
}
bool PagedLOD::removeChildren( unsigned int pos,unsigned int
numChildrenToRemove)
{
if (pos<_rangeList.size()) _rangeList.erase(_rangeList.begin()+pos,
osg::minimum(_rangeList.begin()+(pos+numChildrenToRemove), _rangeList.end()) );
if (pos<_perRangeDataList.size())
_perRangeDataList.erase(_perRangeDataList.begin()+pos,
osg::minimum(_perRangeDataList.begin()+ (pos+numChildrenToRemove),
_perRangeDataList.end()) );
return Group::removeChildren(pos,numChildrenToRemove);
}
bool PagedLOD::removeExpiredChildren(double expiryTime, int expiryFrame,
NodeList& removedChildren)
{
if (_children.size()>_numChildrenThatCannotBeExpired)
{
if (!_perRangeDataList[_children.size()-1]._filename.empty() &&
_perRangeDataList[_children.size()-1]._timeStamp<expiryTime &&
_perRangeDataList[_children.size()-1]._frameNumber<expiryFrame)
{
osg::Node* nodeToRemove = _children[_children.size()-1].get();
removedChildren.push_back(nodeToRemove);
return Group::removeChildren(_children.size()-1,1);
}
}
return false;
}
bool PagedLOD::releaseGLObjectsOnExpiredChildren(double releaseTime, int
releaseFrame)
{
unsigned int numChildrenReleased = 0;
unsigned int numChildren = osg::minimum(_perRangeDataList.size(),
_children.size());
for(unsigned int i=_numChildrenThatCannotBeExpired; i<numChildren; ++i)
{
if (_perRangeDataList[i]._frameNumberOfLastReleaseGLObjects !=
_perRangeDataList[i]._frameNumber &&
_perRangeDataList[i]._timeStamp<releaseTime &&
_perRangeDataList[i]._frameNumber<releaseFrame)
{
_perRangeDataList[i]._frameNumberOfLastReleaseGLObjects =
_perRangeDataList[i]._frameNumber;
_children[i]->releaseGLObjects();
++numChildrenReleased;
}
}
return numChildrenReleased>0;
}
_______________________________________________
osg-submissions mailing list
[email protected]
http://lists.openscenegraph.org/listinfo.cgi/osg-submissions-openscenegraph.org