From Jan Peciva, RayIntersector implementation

This commit is contained in:
Robert Osfield 2013-10-03 08:45:57 +00:00
parent 69f2fb56b3
commit 83c396b13b
6 changed files with 525 additions and 15 deletions

View File

@ -50,9 +50,9 @@ class Intersector : public osg::Referenced
LIMIT_NEAREST
};
Intersector(CoordinateFrame cf=MODEL):
Intersector(CoordinateFrame cf=MODEL, IntersectionLimit il=NO_LIMIT):
_coordinateFrame(cf),
_intersectionLimit(NO_LIMIT),
_intersectionLimit(il),
_disabledCount(0) {}

View File

@ -25,11 +25,12 @@ class OSGUTIL_EXPORT LineSegmentIntersector : public Intersector
{
public:
/** Construct a LineSegmentIntersector the runs between the specified start and end points in MODEL coordinates. */
/** Construct a LineSegmentIntersector that runs between the specified start and end points in MODEL coordinates. */
LineSegmentIntersector(const osg::Vec3d& start, const osg::Vec3d& end);
/** Construct a LineSegmentIntersector the runs between the specified start and end points in the specified coordinate frame. */
LineSegmentIntersector(CoordinateFrame cf, const osg::Vec3d& start, const osg::Vec3d& end);
/** Construct a LineSegmentIntersector that runs between the specified start and end points in the specified coordinate frame. */
LineSegmentIntersector(CoordinateFrame cf, const osg::Vec3d& start, const osg::Vec3d& end, LineSegmentIntersector* parent = NULL,
osgUtil::Intersector::IntersectionLimit intersectionLimit = osgUtil::Intersector::NO_LIMIT);
/** Convenience constructor for supporting picking in WINDOW, or PROJECTION coordinates
* In WINDOW coordinates creates a start value of (x,y,0) and end value of (x,y,1).
@ -94,11 +95,19 @@ class OSGUTIL_EXPORT LineSegmentIntersector : public Intersector
virtual void intersect(osgUtil::IntersectionVisitor& iv, osg::Drawable* drawable);
virtual void intersect(osgUtil::IntersectionVisitor& iv, osg::Drawable* drawable,
const osg::Vec3d& s, const osg::Vec3d& e);
virtual void reset();
virtual bool containsIntersections() { return !getIntersections().empty(); }
protected:
/** Compute the matrix that transforms the local coordinate system of parent Intersector (usually
the current intersector) into the child coordinate system of the child Intersector.
cf parameter indicates the coordinate frame of parent Intersector. */
static osg::Matrix getTransformation(osgUtil::IntersectionVisitor& iv, CoordinateFrame cf);
protected:
bool intersects(const osg::BoundingSphere& bs);
bool intersectAndClip(osg::Vec3d& s, osg::Vec3d& e,const osg::BoundingBox& bb);

View File

@ -0,0 +1,128 @@
/* -*-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 OSGUTIL_RAYINTERSECTOR
#define OSGUTIL_RAYINTERSECTOR 1
#include <osgUtil/IntersectionVisitor>
namespace osgUtil
{
/** RayIntersector implements possibly-infinite line intersections with the scene graph.
*
* Compared with LineSegmentIntersector, RayIntersector supports infinite intersection
* lines, start and end point can be given in homogeneous coordinates and projection
* matrix is allowed to have z-far plane at infinity (often used in shadow volume
* technique).
*
* Currently, picking of objects at infinity is not supported. Please, contribute.
*
* The class is be used in conjunction with IntersectionVisitor. */
class OSGUTIL_EXPORT RayIntersector : public Intersector
{
public:
/** Construct a RayIntersector. You will need to provide start and end point,
* or start point and direction. See setStart() and setDirecton(). */
RayIntersector(CoordinateFrame cf = MODEL, RayIntersector* parent = NULL,
osgUtil::Intersector::IntersectionLimit intersectionLimit = osgUtil::Intersector::NO_LIMIT);
/** Construct a RayIntersector that runs from start point in specified direction to the infinity.
* Start and direction are provided in MODEL coordinates. */
RayIntersector(const osg::Vec3d& start, const osg::Vec3d& direction);
/** Construct a RayIntersector the runs from start point in specified direction to the infinity in the specified coordinate frame. */
RayIntersector(CoordinateFrame cf, const osg::Vec3d& start, const osg::Vec3d& direction, RayIntersector* parent = NULL,
osgUtil::Intersector::IntersectionLimit intersectionLimit = osgUtil::Intersector::NO_LIMIT);
/** Convenience constructor for supporting picking in WINDOW and PROJECTION coordinates.
* In WINDOW coordinates, it creates a start value of (x,y,0) and end value of (x,y,1).
* In PROJECTION coordinates (clip space cube), it creates a start value of (x,y,-1) and end value of (x,y,1).
* In VIEW and MODEL coordinates, it creates a start value of (x,y,0) and end value of (x,y,1).*/
RayIntersector(CoordinateFrame cf, double x, double y);
struct OSGUTIL_EXPORT Intersection
{
Intersection() : distance(-1.0), primitiveIndex(0) {}
bool operator < (const Intersection& rhs) const { return distance < rhs.distance; }
typedef std::vector<unsigned int> IndexList;
typedef std::vector<double> RatioList;
double distance;
osg::NodePath nodePath;
osg::ref_ptr<osg::Drawable> drawable;
osg::ref_ptr<osg::RefMatrix> matrix;
osg::Vec3d localIntersectionPoint;
osg::Vec3 localIntersectionNormal;
IndexList indexList;
RatioList ratioList;
unsigned int primitiveIndex;
const osg::Vec3d& getLocalIntersectPoint() const { return localIntersectionPoint; }
osg::Vec3d getWorldIntersectPoint() const { return matrix.valid() ? localIntersectionPoint * (*matrix) : localIntersectionPoint; }
const osg::Vec3& getLocalIntersectNormal() const { return localIntersectionNormal; }
osg::Vec3 getWorldIntersectNormal() const { return matrix.valid() ? osg::Matrix::transform3x3(osg::Matrix::inverse(*matrix),localIntersectionNormal) : localIntersectionNormal; }
/** convinience function for mapping the intersection point to any textures assigned to the objects intersected.
* Returns the Texture pointer and texture coords of object hit when a texture is available on the object, returns NULL otherwise.*/
osg::Texture* getTextureLookUp(osg::Vec3& tc) const;
};
typedef std::multiset<Intersection> Intersections;
inline void insertIntersection(const Intersection& intersection) { getIntersections().insert(intersection); }
inline Intersections& getIntersections() { return _parent ? _parent->_intersections : _intersections; }
inline Intersection getFirstIntersection() { Intersections& intersections = getIntersections(); return intersections.empty() ? Intersection() : *(intersections.begin()); }
virtual void setStart(const osg::Vec3d& start) { _start = start; }
inline const osg::Vec3d& getStart() const { return _start; }
virtual void setDirection(const osg::Vec3d& dir) { _direction = dir; }
inline const osg::Vec3d& getDirection() const { return _direction; }
public:
virtual Intersector* clone(osgUtil::IntersectionVisitor& iv);
virtual bool enter(const osg::Node& node);
virtual void leave();
virtual void intersect(osgUtil::IntersectionVisitor& iv, osg::Drawable* drawable);
virtual void reset();
virtual bool containsIntersections() { return !getIntersections().empty(); }
protected:
virtual bool intersects(const osg::BoundingSphere& bs);
bool intersectAndClip(osg::Vec3d& s, const osg::Vec3d& d, osg::Vec3d& e, const osg::BoundingBox& bb);
RayIntersector* _parent;
osg::Vec3d _start;
osg::Vec3d _direction;
Intersections _intersections;
};
}
#endif

View File

@ -32,6 +32,7 @@ SET(TARGET_H
${HEADER_PATH}/PolytopeIntersector
${HEADER_PATH}/PositionalStateContainer
${HEADER_PATH}/PrintVisitor
${HEADER_PATH}/RayIntersector
${HEADER_PATH}/ReflectionMapGenerator
${HEADER_PATH}/RenderBin
${HEADER_PATH}/RenderLeaf
@ -74,6 +75,7 @@ SET(TARGET_SRC
PolytopeIntersector.cpp
PositionalStateContainer.cpp
PrintVisitor.cpp
RayIntersector.cpp
RenderBin.cpp
RenderLeaf.cpp
RenderStage.cpp

View File

@ -77,7 +77,7 @@ namespace LineSegmentIntersectorUtils
_limitOneIntersection = false;
}
void set(const osg::Vec3d& start, osg::Vec3d& end, float ratio=FLT_MAX)
void set(const osg::Vec3d& start, const osg::Vec3d& end, float ratio=FLT_MAX)
{
_hit=false;
_index = 0;
@ -216,8 +216,9 @@ LineSegmentIntersector::LineSegmentIntersector(const osg::Vec3d& start, const os
{
}
LineSegmentIntersector::LineSegmentIntersector(CoordinateFrame cf, const osg::Vec3d& start, const osg::Vec3d& end):
Intersector(cf),
LineSegmentIntersector::LineSegmentIntersector(CoordinateFrame cf, const osg::Vec3d& start, const osg::Vec3d& end,
LineSegmentIntersector* parent, osgUtil::Intersector::IntersectionLimit intersectionLimit):
Intersector(cf, intersectionLimit),
_parent(0),
_start(start),
_end(end)
@ -249,8 +250,18 @@ Intersector* LineSegmentIntersector::clone(osgUtil::IntersectionVisitor& iv)
// compute the matrix that takes this Intersector from its CoordinateFrame into the local MODEL coordinate frame
// that geometry in the scene graph will always be in.
osg::Matrix matrix(getTransformation(iv, _coordinateFrame));
osg::ref_ptr<LineSegmentIntersector> lsi = new LineSegmentIntersector(_start * matrix, _end * matrix);
lsi->_parent = this;
lsi->_intersectionLimit = this->_intersectionLimit;
return lsi.release();
}
osg::Matrix LineSegmentIntersector::getTransformation(IntersectionVisitor& iv, CoordinateFrame cf)
{
osg::Matrix matrix;
switch (_coordinateFrame)
switch (cf)
{
case(WINDOW):
if (iv.getWindowMatrix()) matrix.preMult( *iv.getWindowMatrix() );
@ -274,11 +285,7 @@ Intersector* LineSegmentIntersector::clone(osgUtil::IntersectionVisitor& iv)
osg::Matrix inverse;
inverse.invert(matrix);
osg::ref_ptr<LineSegmentIntersector> lsi = new LineSegmentIntersector(_start * inverse, _end * inverse);
lsi->_parent = this;
lsi->_intersectionLimit = this->_intersectionLimit;
return lsi.release();
return inverse;
}
bool LineSegmentIntersector::enter(const osg::Node& node)
@ -301,6 +308,12 @@ void LineSegmentIntersector::intersect(osgUtil::IntersectionVisitor& iv, osg::Dr
if (iv.getDoDummyTraversal()) return;
intersect(iv, drawable, s, e);
}
void LineSegmentIntersector::intersect(osgUtil::IntersectionVisitor& iv, osg::Drawable* drawable,
const osg::Vec3d& s, const osg::Vec3d& e)
{
osg::KdTree* kdTree = iv.getUseKdTreeWhenAvailable() ? dynamic_cast<osg::KdTree*>(drawable->getShape()) : 0;
if (kdTree)
{

View File

@ -0,0 +1,358 @@
/* -*-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 <osgUtil/RayIntersector>
#include <osgUtil/LineSegmentIntersector>
#include <osg/KdTree>
#include <osg/Notify>
#include <osg/TexMat>
#include <limits>
#include <cmath>
using namespace osg;
using namespace osgUtil;
///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
//
// RayIntersector
//
RayIntersector::RayIntersector(CoordinateFrame cf, RayIntersector* parent,
Intersector::IntersectionLimit intersectionLimit) :
Intersector(cf, intersectionLimit),
_parent(parent)
{
}
RayIntersector::RayIntersector(const Vec3d& start, const Vec3d& direction) :
Intersector(),
_start(start),
_direction(direction)
{
}
RayIntersector::RayIntersector(CoordinateFrame cf, const Vec3d& start, const Vec3d& direction,
RayIntersector* parent, Intersector::IntersectionLimit intersectionLimit) :
Intersector(cf, intersectionLimit),
_parent(parent),
_start(start),
_direction(direction)
{
}
RayIntersector::RayIntersector(CoordinateFrame cf, double x, double y) :
Intersector(cf),
_parent(0)
{
switch(cf)
{
case WINDOW: setStart(Vec3d(x,y,0.)); setDirection(Vec3d(0.,0.,1.)); break;
case PROJECTION: setStart(Vec3d(x,y,-1.)); setDirection(Vec3d(0.,0.,1.)); break;
case VIEW: setStart(Vec3d(x,y,0.)); setDirection(Vec3d(0.,0.,1.)); break;
case MODEL: setStart(Vec3d(x,y,0.)); setDirection(Vec3d(0.,0.,1.)); break;
}
}
Intersector* RayIntersector::clone(IntersectionVisitor& iv)
{
if (_coordinateFrame==MODEL && iv.getModelMatrix()==0)
{
return new RayIntersector(MODEL, _start, _direction, this, _intersectionLimit);
}
Matrix matrix(LineSegmentIntersector::getTransformation(iv, _coordinateFrame));
Vec3d newStart = _start * matrix;
Vec4d tmp = Vec4d(_start + _direction, 1.) * matrix;
Vec3d newEnd = Vec3d(tmp.x(), tmp.y(), tmp.z()) - (newStart * tmp.w());
return new RayIntersector(MODEL, newStart, newEnd, this, _intersectionLimit);
}
bool RayIntersector::enter(const Node& node)
{
if (reachedLimit()) return false;
return !node.isCullingActive() || intersects( node.getBound() );
}
void RayIntersector::leave()
{
// do nothing
}
void RayIntersector::reset()
{
Intersector::reset();
_intersections.clear();
}
void RayIntersector::intersect(IntersectionVisitor& iv, Drawable* drawable)
{
// did we reached what we wanted as specified by setIntersectionLimit()?
if (reachedLimit()) return;
// clip ray to finite line segment
Vec3d s(_start), e;
if (!intersectAndClip(s, _direction, e, drawable->getBound())) return;
// dummy traversal
if (iv.getDoDummyTraversal()) return;
// get intersections using LineSegmentIntersector
LineSegmentIntersector lsi(MODEL, s, e, NULL, _intersectionLimit);
lsi.intersect(iv, drawable, s, e);
// copy intersections from LineSegmentIntersector
LineSegmentIntersector::Intersections intersections = lsi.getIntersections();
if (!intersections.empty())
{
double preLength = (s - _start).length();
double esLength = (e - s).length();
for(LineSegmentIntersector::Intersections::iterator it = intersections.begin();
it != intersections.end(); it++)
{
Intersection hit;
hit.distance = preLength + it->ratio * esLength;
hit.matrix = it->matrix;
hit.nodePath = it->nodePath;
hit.drawable = it->drawable;
hit.primitiveIndex = it->primitiveIndex;
hit.localIntersectionPoint = it->localIntersectionPoint;
hit.localIntersectionNormal = it->localIntersectionNormal;
hit.indexList = it->indexList;
hit.ratioList = it->ratioList;
insertIntersection(hit);
}
}
}
bool RayIntersector::intersects(const BoundingSphere& bs)
{
// if bs not valid then return true based on the assumption that an invalid sphere is yet to be defined.
if (!bs.valid()) return true;
// test for _start inside the bounding sphere
Vec3d sm = _start - bs._center;
double c = sm.length2() - bs._radius * bs._radius;
if (c<0.0) return true;
// solve quadratic equation
double a = _direction.length2();
double b = (sm * _direction) * 2.0;
double d = b * b - 4.0 * a * c;
// no intersections if d<0
if (d<0.0) return false;
// compute two solutions of quadratic equation
d = sqrt(d);
double div = 1.0/(2.0*a);
double r1 = (-b-d)*div;
double r2 = (-b+d)*div;
// return false if both intersections are before the ray start
if (r1<=0.0 && r2<=0.0) return false;
// if LIMIT_NEAREST and closest point of bounding sphere is further than already found intersection, return false
if (_intersectionLimit == LIMIT_NEAREST && !getIntersections().empty())
{
double minDistance = sm.length() - bs._radius;
if (minDistance >= getIntersections().begin()->distance) return false;
}
// passed all the rejection tests so line must intersect bounding sphere, return true.
return true;
}
bool RayIntersector::intersectAndClip(Vec3d& s, const Vec3d& d, Vec3d& e, const BoundingBox& bbInput)
{
// bounding box min and max
Vec3d bb_min(bbInput._min);
Vec3d bb_max(bbInput._max);
// Expand the extents of the bounding box by the epsilon to prevent numerical errors resulting in misses.
const double epsilon = 1e-6;
// clip s against all three components of the Min to Max range of bb
for (int i=0; i<3; i++)
{
// test direction
if (d[i] >= 0.)
{
// trivial reject of segment wholly outside
if (s[i] > bb_max[i]) return false;
if (s[i] < bb_min[i])
{
// clip s to xMin
double t = (bb_min[i]-s[i])/d[i] - epsilon;
if (t>0.0) s = s + d*t;
}
}
else
{
// trivial reject of segment wholly outside
if (s[i] < bb_min[i]) return false;
if (s[i] > bb_max[i])
{
// clip s to xMax
double t = (bb_max[i]-s[i])/d[i] - epsilon;
if (t>0.0) s = s + d*t;
}
}
}
// t for ending point of clipped ray
double end_t = std::numeric_limits<double>::infinity();
// get end point by clipping the ray by bb
// note: this can not be done in previous loop as start point s is moving
for (int i=0; i<3; i++)
{
// test direction
if (d[i] >= 0.)
{
// compute end_t based on xMax
double t = (bb_max[i]-s[i])/d[i] + epsilon;
if (t < end_t)
end_t = t;
}
else
{
// compute end_t based on xMin
double t = (bb_min[i]-s[i])/d[i] + epsilon;
if (t < end_t)
end_t = t;
}
}
// compute e
e = s + d*end_t;
return true;
}
Texture* RayIntersector::Intersection::getTextureLookUp(Vec3& tc) const
{
Geometry* geometry = drawable.valid() ? drawable->asGeometry() : 0;
Vec3Array* vertices = geometry ? dynamic_cast<Vec3Array*>(geometry->getVertexArray()) : 0;
if (vertices)
{
if (indexList.size()==3 && ratioList.size()==3)
{
unsigned int i1 = indexList[0];
unsigned int i2 = indexList[1];
unsigned int i3 = indexList[2];
float r1 = ratioList[0];
float r2 = ratioList[1];
float r3 = ratioList[2];
Array* texcoords = (geometry->getNumTexCoordArrays()>0) ? geometry->getTexCoordArray(0) : 0;
FloatArray* texcoords_FloatArray = dynamic_cast<FloatArray*>(texcoords);
Vec2Array* texcoords_Vec2Array = dynamic_cast<Vec2Array*>(texcoords);
Vec3Array* texcoords_Vec3Array = dynamic_cast<Vec3Array*>(texcoords);
if (texcoords_FloatArray)
{
// we have tex coord array so now we can compute the final tex coord at the point of intersection.
float tc1 = (*texcoords_FloatArray)[i1];
float tc2 = (*texcoords_FloatArray)[i2];
float tc3 = (*texcoords_FloatArray)[i3];
tc.x() = tc1*r1 + tc2*r2 + tc3*r3;
}
else if (texcoords_Vec2Array)
{
// we have tex coord array so now we can compute the final tex coord at the point of intersection.
const Vec2& tc1 = (*texcoords_Vec2Array)[i1];
const Vec2& tc2 = (*texcoords_Vec2Array)[i2];
const Vec2& tc3 = (*texcoords_Vec2Array)[i3];
tc.x() = tc1.x()*r1 + tc2.x()*r2 + tc3.x()*r3;
tc.y() = tc1.y()*r1 + tc2.y()*r2 + tc3.y()*r3;
}
else if (texcoords_Vec3Array)
{
// we have tex coord array so now we can compute the final tex coord at the point of intersection.
const Vec3& tc1 = (*texcoords_Vec3Array)[i1];
const Vec3& tc2 = (*texcoords_Vec3Array)[i2];
const Vec3& tc3 = (*texcoords_Vec3Array)[i3];
tc.x() = tc1.x()*r1 + tc2.x()*r2 + tc3.x()*r3;
tc.y() = tc1.y()*r1 + tc2.y()*r2 + tc3.y()*r3;
tc.z() = tc1.z()*r1 + tc2.z()*r2 + tc3.z()*r3;
}
else
{
return 0;
}
}
const TexMat* activeTexMat = 0;
const Texture* activeTexture = 0;
if (drawable->getStateSet())
{
const TexMat* texMat = dynamic_cast<TexMat*>(drawable->getStateSet()->getTextureAttribute(0,StateAttribute::TEXMAT));
if (texMat) activeTexMat = texMat;
const Texture* texture = dynamic_cast<Texture*>(drawable->getStateSet()->getTextureAttribute(0,StateAttribute::TEXTURE));
if (texture) activeTexture = texture;
}
for(NodePath::const_reverse_iterator itr = nodePath.rbegin();
itr != nodePath.rend() && (!activeTexMat || !activeTexture);
++itr)
{
const Node* node = *itr;
if (node->getStateSet())
{
if (!activeTexMat)
{
const TexMat* texMat = dynamic_cast<const TexMat*>(node->getStateSet()->getTextureAttribute(0,StateAttribute::TEXMAT));
if (texMat) activeTexMat = texMat;
}
if (!activeTexture)
{
const Texture* texture = dynamic_cast<const Texture*>(node->getStateSet()->getTextureAttribute(0,StateAttribute::TEXTURE));
if (texture) activeTexture = texture;
}
}
}
if (activeTexMat)
{
Vec4 tc_transformed = Vec4(tc.x(), tc.y(), tc.z() ,0.0f) * activeTexMat->getMatrix();
tc.x() = tc_transformed.x();
tc.y() = tc_transformed.y();
tc.z() = tc_transformed.z();
if (activeTexture && activeTexMat->getScaleByTextureRectangleSize())
{
tc.x() *= static_cast<float>(activeTexture->getTextureWidth());
tc.y() *= static_cast<float>(activeTexture->getTextureHeight());
tc.z() *= static_cast<float>(activeTexture->getTextureDepth());
}
}
return const_cast<Texture*>(activeTexture);
}
return 0;
}