Improved the PickVisitor to make it more flexible and robust.

This commit is contained in:
Robert Osfield
2005-12-06 21:59:43 +00:00
parent a865ea7773
commit 4d658ae1ea
3 changed files with 96 additions and 152 deletions

View File

@@ -115,6 +115,8 @@ class OSGUTIL_EXPORT IntersectVisitor : public osg::NodeVisitor
HitList& getHitList(const osg::LineSegment* seg) { return _segHitList[seg]; }
int getNumHits(const osg::LineSegment* seg) { return _segHitList[seg].size(); }
LineSegmentHitListMap& getSegHitList() { return _segHitList; }
bool hits();

View File

@@ -34,143 +34,84 @@ using namespace osg;
//
// Picking intersection visitor.
//
class PickIntersectVisitor : public osgUtil::IntersectVisitor
class PickVisitor : public osgUtil::IntersectVisitor
{
public:
PickIntersectVisitor()
{
PickVisitor(const osg::Viewport* viewport, const osg::Matrixd& proj, const osg::Matrixd& view, float mx, float my):
_mx(mx),
_my(my),
_lastViewport(viewport),
_lastProjectionMatrix(proj),
_lastViewMatrix(view)
{
if (viewport &&
mx >= static_cast<float>(viewport->x()) &&
my >= static_cast<float>(viewport->y()) &&
mx < static_cast<float>(viewport->x()+viewport->width()) &&
my < static_cast<float>(viewport->y()+viewport->height()))
{
// mouse pointer intersect viewport so we can proceed to set up a line segment
osg::Matrix MVPW = view * proj * viewport->computeWindowMatrix();
osg::Matrixd inverseMVPW;
inverseMVPW.invert(MVPW);
osg::Vec3 nearPoint = osg::Vec3(mx,my,0.0f) * inverseMVPW;
osg::Vec3 farPoint = osg::Vec3(mx,my,1.0f) * inverseMVPW;
osg::LineSegment* lineSegment = new osg::LineSegment;
lineSegment->set(nearPoint, farPoint);
addLineSegment(lineSegment);
}
}
virtual ~PickIntersectVisitor() {}
HitList& getIntersections(osg::Node *scene, osg::Vec3 nr, osg::Vec3 fr)
{
// option for non-sceneView users: you need to get the screen perp line and call getIntersections
// if you are using Projection nodes you should also call setxy to define the xp,yp positions for use with
// the ray transformed by Projection
_lineSegment = new osg::LineSegment;
_lineSegment->set(nr,fr); // make a line segment
//std::cout<<"near "<<nr<<std::endl;
//std::cout<<"far "<<fr<<std::endl;
addLineSegment(_lineSegment.get());
scene->accept(*this);
return getHitList(_lineSegment.get());
}
private:
osg::ref_ptr<osg::LineSegment> _lineSegment;
friend class osgUtil::IntersectVisitor;
};
// PickVisitor traverses whole scene and checks below all Projection nodes
class PickVisitor : public osg::NodeVisitor
{
public:
PickVisitor()
{
xp=yp=0;
setTraversalMode(osg::NodeVisitor::TRAVERSE_ACTIVE_CHILDREN);
}
~PickVisitor()
void runNestedPickVisitor(osg::Node& node, const osg::Viewport* viewport, const osg::Matrix& proj, const osg::Matrix& view, float mx, float my)
{
}
void setTraversalMask(osg::Node::NodeMask traversalMask)
{
NodeVisitor::setTraversalMask(traversalMask);
_piv.setTraversalMask(traversalMask);
}
// Aug 2003 added to pass the nodemaskOverride to the PickIntersectVisitor
// may be used make the visitor override the nodemask to visit invisible actions
inline void setNodeMaskOverride(osg::Node::NodeMask mask)
{
_piv.setNodeMaskOverride(mask);
_nodeMaskOverride = mask;
}
virtual void apply(osg::Projection& pr)
{
osg::Matrixd mt;
mt.invert(pr.getMatrix());
osg::Vec3 npt=osg::Vec3(xp,yp,-1.0f) * mt, farpt=osg::Vec3(xp,yp,1.0f) * mt;
// traversing the nodes children, using the projection direction
for (unsigned int i=0; i<pr.getNumChildren(); i++)
{
osg::Node *nodech=pr.getChild(i);
osgUtil::IntersectVisitor::HitList &hli=_piv.getIntersections(nodech,npt, farpt);
for(osgUtil::IntersectVisitor::HitList::iterator hitr=hli.begin();
hitr!=hli.end();
++hitr)
{ // add the projection hits to the scene hits.
// This is why _lineSegment is retained as a member of PickIntersectVisitor
_PIVsegHitList.push_back(*hitr);
}
traverse(*nodech);
}
}
virtual void apply(osg::CameraNode& camera)
{
// partial fix for CameraNode... current code
// assumes CameraNode has an absolute projection matrix, rather than an acculated one,
// will need to think about how to handle relative project matrices. RO November 2005.
osg::Matrixd mt;
mt.invert(camera.getProjectionMatrix());
osg::Vec3 npt=osg::Vec3(xp,yp,-1.0f) * mt, farpt=osg::Vec3(xp,yp,1.0f) * mt;
osgUtil::IntersectVisitor::HitList &hli=_piv.getIntersections(&camera,npt, farpt);
for(osgUtil::IntersectVisitor::HitList::iterator hitr=hli.begin();
hitr!=hli.end();
++hitr)
{
// add the projection hits to the scene hits.
// This is why _lineSegment is retained as a member of PickIntersectVisitor
_PIVsegHitList.push_back(*hitr);
}
}
osgUtil::IntersectVisitor::HitList& getHits(osg::Node *node, const osg::Matrixd &projm, const float x, const float y)
{
setxy(x,y);
_PIVsegHitList.clear();
// utility for non=sceneview viewers
// x,y are values returned by
osg::Matrixd inverseMVPW;
inverseMVPW.invert(projm);
osg::Vec3 near_point = osg::Vec3(x,y,-1.0f)*inverseMVPW;
osg::Vec3 far_point = osg::Vec3(x,y,1.0f)*inverseMVPW;
if (near_point.valid() && far_point.valid())
{
_PIVsegHitList = _piv.getIntersections(node,near_point,far_point); // fill hitlist
}
// traverse for any projection/camera nodes.
node->accept(*this);
return _PIVsegHitList;
}
inline void setxy(float xpt, float ypt) { xp=xpt; yp=ypt; }
inline bool hits() const { return _PIVsegHitList.size()>0;}
private:
PickVisitor newPickVisitor( viewport, proj, view, _mx, _my );
newPickVisitor.setTraversalMask(getTraversalMask());
// the new pickvisitor over the nodes children.
node.traverse( newPickVisitor );
for(LineSegmentHitListMap::iterator itr = newPickVisitor._segHitList.begin();
itr != newPickVisitor._segHitList.end();
++itr)
{
_segHitList.insert(*itr);
}
}
void apply(osg::Projection& projection)
{
runNestedPickVisitor( projection,
_lastViewport.get(),
projection.getMatrix(),
_lastViewMatrix,
_mx, _my );
}
void apply(osg::CameraNode& camera)
{
runNestedPickVisitor( camera,
camera.getViewport() ? camera.getViewport() : _lastViewport.get(),
camera.getProjectionMatrix(),
camera.getViewMatrix(),
_mx, _my );
}
float _mx;
float _my;
PickIntersectVisitor _piv;
float xp, yp; // start point in viewport fraction coordiantes
osgUtil::IntersectVisitor::HitList _PIVsegHitList;
osg::ref_ptr<const osg::Viewport> _lastViewport;
osg::Matrixd _lastProjectionMatrix;
osg::Matrixd _lastViewMatrix;
};
class CollectedCoordinateSystemNodesVisitor : public osg::NodeVisitor
{
public:
@@ -878,7 +819,7 @@ bool Viewer::computeNearFarPoints(float x,float y,unsigned int cameraNum,osg::Ve
}
bool Viewer::computeIntersections(float x,float y,unsigned int cameraNum,osg::Node *node,osgUtil::IntersectVisitor::HitList& hits,osg::Node::NodeMask traversalMask)
bool Viewer::computeIntersections(float x,float y,unsigned int cameraNum,osg::Node* node,osgUtil::IntersectVisitor::HitList& hits,osg::Node::NodeMask traversalMask)
{
float pixel_x,pixel_y;
if (computePixelCoords(x,y,cameraNum,pixel_x,pixel_y))
@@ -886,39 +827,38 @@ bool Viewer::computeIntersections(float x,float y,unsigned int cameraNum,osg::No
Producer::Camera* camera=getCamera(cameraNum);
int pr_wx, pr_wy;
unsigned int pr_width, pr_height;
camera->getProjectionRectangle( pr_wx, pr_wy, pr_width, pr_height );
// convert into clip coords.
float rx = 2.0f*(pixel_x - (float)pr_wx)/(float)pr_width-1.0f;
float ry = 2.0f*(pixel_y - (float)pr_wy)/(float)pr_height-1.0f;
//std::cout << " rx "<<rx<<" "<<ry<<std::endl;
osgProducer::OsgSceneHandler* sh = dynamic_cast<osgProducer::OsgSceneHandler*>(camera->getSceneHandler());
osgUtil::SceneView* sv = sh?sh->getSceneView():0;
osg::Matrixd vum;
osgUtil::SceneView* sv = sh ? sh->getSceneView() : 0;
osg::Matrixd proj;
osg::Matrixd view;
const osg::Viewport* viewport = 0;
if (sv!=0)
{
vum.set(sv->getViewMatrix() *
sv->getProjectionMatrix());
viewport = sv->getViewport();
proj = sv->getProjectionMatrix();
view = sv->getViewMatrix();
}
else
{
vum.set(osg::Matrixd(camera->getViewMatrix()) *
osg::Matrixd(camera->getProjectionMatrix()));
viewport = 0;
proj = osg::Matrixd(camera->getProjectionMatrix());
view = osg::Matrixd(camera->getViewMatrix());
}
PickVisitor iv;
iv.setTraversalMask(traversalMask);
node = node->getParent(0);
PickVisitor pick(viewport, proj, view, pixel_x, pixel_y);
pick.setTraversalMask(traversalMask);
node->accept(pick);
osgUtil::IntersectVisitor::HitList localHits;
localHits = iv.getHits(node, vum, rx,ry);
if (localHits.empty()) return false;
hits.insert(hits.begin(),localHits.begin(),localHits.end());
// copy all the hits across to the external hits list
for(osgUtil::IntersectVisitor::LineSegmentHitListMap::iterator itr = pick.getSegHitList().begin();
itr != pick.getSegHitList().end();
++itr)
{
hits.insert(hits.end(),itr->second.begin(), itr->second.end());
}
return true;
}

View File

@@ -84,6 +84,7 @@ BEGIN_OBJECT_REFLECTOR(osgUtil::IntersectVisitor)
I_Method1(void, addLineSegment, IN, osg::LineSegment *, seg);
I_Method1(osgUtil::IntersectVisitor::HitList &, getHitList, IN, const osg::LineSegment *, seg);
I_Method1(int, getNumHits, IN, const osg::LineSegment *, seg);
I_Method0(osgUtil::IntersectVisitor::LineSegmentHitListMap &, getSegHitList);
I_Method0(bool, hits);
I_Method1(void, setLODSelectionMode, IN, osgUtil::IntersectVisitor::LODSelectionMode, mode);
I_Method0(osgUtil::IntersectVisitor::LODSelectionMode, getLODSelectionMode);
@@ -99,6 +100,7 @@ BEGIN_OBJECT_REFLECTOR(osgUtil::IntersectVisitor)
I_Method1(void, apply, IN, osg::LOD &, node);
I_ReadOnlyProperty(osg::Vec3, EyePoint);
I_Property(osgUtil::IntersectVisitor::LODSelectionMode, LODSelectionMode);
I_ReadOnlyProperty(osgUtil::IntersectVisitor::LineSegmentHitListMap &, SegHitList);
END_REFLECTOR
STD_MAP_REFLECTOR(std::map< const osg::LineSegment * COMMA osgUtil::IntersectVisitor::HitList >);