841 lines
25 KiB
C++
841 lines
25 KiB
C++
/* -*-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/LineSegmentIntersector>
|
|
|
|
#include <osg/Geometry>
|
|
#include <osg/Notify>
|
|
#include <osg/io_utils>
|
|
#include <osg/TriangleFunctor>
|
|
#include <osg/KdTree>
|
|
#include <osg/Timer>
|
|
#include <osg/TexMat>
|
|
#include <osg/TemplatePrimitiveFunctor>
|
|
|
|
using namespace osgUtil;
|
|
|
|
namespace LineSegmentIntersectorUtils
|
|
{
|
|
|
|
struct Settings
|
|
{
|
|
Settings() :
|
|
_lineSegIntersector(0),
|
|
_iv(0),
|
|
_drawable(0),
|
|
_limitOneIntersection(false) {}
|
|
|
|
osgUtil::LineSegmentIntersector* _lineSegIntersector;
|
|
osgUtil::IntersectionVisitor* _iv;
|
|
osg::Drawable* _drawable;
|
|
osg::ref_ptr<osg::Vec3Array> _vertices;
|
|
bool _limitOneIntersection;
|
|
};
|
|
|
|
template<typename Vec3, typename value_type>
|
|
struct IntersectFunctor
|
|
{
|
|
Settings* _settings;
|
|
|
|
unsigned int _primitiveIndex;
|
|
Vec3 _start;
|
|
Vec3 _end;
|
|
|
|
typedef std::pair< Vec3, Vec3> StartEnd;
|
|
typedef std::vector< StartEnd > StartEndStack;
|
|
|
|
StartEndStack _startEndStack;
|
|
|
|
Vec3 _d;
|
|
value_type _length;
|
|
value_type _inverse_length;
|
|
|
|
Vec3 _d_invX;
|
|
Vec3 _d_invY;
|
|
Vec3 _d_invZ;
|
|
|
|
bool _hit;
|
|
|
|
IntersectFunctor():
|
|
_primitiveIndex(0),
|
|
_length(0.0),
|
|
_inverse_length(0.0),
|
|
_hit(false)
|
|
{
|
|
}
|
|
|
|
|
|
void set(const osg::Vec3d& s, const osg::Vec3d& e, Settings* settings)
|
|
{
|
|
_settings = settings;
|
|
|
|
_start = s;
|
|
_end = e;
|
|
|
|
_startEndStack.push_back(StartEnd(_start,_end));
|
|
|
|
_d = e - s;
|
|
_length = _d.length();
|
|
_inverse_length = (_length!=0.0) ? 1.0/_length : 0.0;
|
|
_d *= _inverse_length;
|
|
|
|
_d_invX = _d.x()!=0.0 ? _d/_d.x() : Vec3(0.0,0.0,0.0);
|
|
_d_invY = _d.y()!=0.0 ? _d/_d.y() : Vec3(0.0,0.0,0.0);
|
|
_d_invZ = _d.z()!=0.0 ? _d/_d.z() : Vec3(0.0,0.0,0.0);
|
|
}
|
|
|
|
bool enter(const osg::BoundingBox& bb)
|
|
{
|
|
StartEnd startend = _startEndStack.back();
|
|
Vec3& s = startend.first;
|
|
Vec3& e = startend.second;
|
|
|
|
//return true;
|
|
|
|
//if (!bb.valid()) return true;
|
|
|
|
// compare s and e against the xMin to xMax range of bb.
|
|
if (s.x()<=e.x())
|
|
{
|
|
|
|
// trivial reject of segment wholely outside.
|
|
if (e.x()<bb.xMin()) return false;
|
|
if (s.x()>bb.xMax()) return false;
|
|
|
|
if (s.x()<bb.xMin())
|
|
{
|
|
// clip s to xMin.
|
|
s = s+_d_invX*(bb.xMin()-s.x());
|
|
}
|
|
|
|
if (e.x()>bb.xMax())
|
|
{
|
|
// clip e to xMax.
|
|
e = s+_d_invX*(bb.xMax()-s.x());
|
|
}
|
|
}
|
|
else
|
|
{
|
|
if (s.x()<bb.xMin()) return false;
|
|
if (e.x()>bb.xMax()) return false;
|
|
|
|
if (e.x()<bb.xMin())
|
|
{
|
|
// clip s to xMin.
|
|
e = s+_d_invX*(bb.xMin()-s.x());
|
|
}
|
|
|
|
if (s.x()>bb.xMax())
|
|
{
|
|
// clip e to xMax.
|
|
s = s+_d_invX*(bb.xMax()-s.x());
|
|
}
|
|
}
|
|
|
|
// compare s and e against the yMin to yMax range of bb.
|
|
if (s.y()<=e.y())
|
|
{
|
|
|
|
// trivial reject of segment wholely outside.
|
|
if (e.y()<bb.yMin()) return false;
|
|
if (s.y()>bb.yMax()) return false;
|
|
|
|
if (s.y()<bb.yMin())
|
|
{
|
|
// clip s to yMin.
|
|
s = s+_d_invY*(bb.yMin()-s.y());
|
|
}
|
|
|
|
if (e.y()>bb.yMax())
|
|
{
|
|
// clip e to yMax.
|
|
e = s+_d_invY*(bb.yMax()-s.y());
|
|
}
|
|
}
|
|
else
|
|
{
|
|
if (s.y()<bb.yMin()) return false;
|
|
if (e.y()>bb.yMax()) return false;
|
|
|
|
if (e.y()<bb.yMin())
|
|
{
|
|
// clip s to yMin.
|
|
e = s+_d_invY*(bb.yMin()-s.y());
|
|
}
|
|
|
|
if (s.y()>bb.yMax())
|
|
{
|
|
// clip e to yMax.
|
|
s = s+_d_invY*(bb.yMax()-s.y());
|
|
}
|
|
}
|
|
|
|
// compare s and e against the zMin to zMax range of bb.
|
|
if (s.z()<=e.z())
|
|
{
|
|
|
|
// trivial reject of segment wholely outside.
|
|
if (e.z()<bb.zMin()) return false;
|
|
if (s.z()>bb.zMax()) return false;
|
|
|
|
if (s.z()<bb.zMin())
|
|
{
|
|
// clip s to zMin.
|
|
s = s+_d_invZ*(bb.zMin()-s.z());
|
|
}
|
|
|
|
if (e.z()>bb.zMax())
|
|
{
|
|
// clip e to zMax.
|
|
e = s+_d_invZ*(bb.zMax()-s.z());
|
|
}
|
|
}
|
|
else
|
|
{
|
|
if (s.z()<bb.zMin()) return false;
|
|
if (e.z()>bb.zMax()) return false;
|
|
|
|
if (e.z()<bb.zMin())
|
|
{
|
|
// clip s to zMin.
|
|
e = s+_d_invZ*(bb.zMin()-s.z());
|
|
}
|
|
|
|
if (s.z()>bb.zMax())
|
|
{
|
|
// clip e to zMax.
|
|
s = s+_d_invZ*(bb.zMax()-s.z());
|
|
}
|
|
}
|
|
|
|
// OSG_NOTICE<<"clampped segment "<<s<<" "<<e<<std::endl;
|
|
_startEndStack.push_back(startend);
|
|
|
|
return true;
|
|
}
|
|
|
|
void leave()
|
|
{
|
|
// OSG_NOTICE<<"leave() "<<_startEndStack.size()<<std::endl;
|
|
_startEndStack.pop_back();
|
|
}
|
|
|
|
void intersect(const osg::Vec3& v0, const osg::Vec3& v1, const osg::Vec3& v2)
|
|
{
|
|
if (_settings->_limitOneIntersection && _hit) return;
|
|
|
|
// const StartEnd startend = _startEndStack.back();
|
|
// const osg::Vec3& ls = startend.first;
|
|
// const osg::Vec3& le = startend.second;
|
|
|
|
Vec3 T = _start - v0;
|
|
Vec3 E2 = v2 - v0;
|
|
Vec3 E1 = v1 - v0;
|
|
|
|
Vec3 P = _d ^ E2;
|
|
|
|
value_type det = P * E1;
|
|
|
|
value_type r,r0,r1,r2;
|
|
|
|
const value_type epsilon = 1e-10;
|
|
if (det>epsilon)
|
|
{
|
|
value_type u = (P*T);
|
|
if (u<0.0 || u>det) return;
|
|
|
|
osg::Vec3 Q = T ^ E1;
|
|
value_type v = (Q*_d);
|
|
if (v<0.0 || v>det) return;
|
|
|
|
if ((u+v)> det) return;
|
|
|
|
value_type inv_det = 1.0/det;
|
|
value_type t = (Q*E2)*inv_det;
|
|
if (t<0.0 || t>_length) return;
|
|
|
|
u *= inv_det;
|
|
v *= inv_det;
|
|
|
|
r0 = 1.0-u-v;
|
|
r1 = u;
|
|
r2 = v;
|
|
r = t * _inverse_length;
|
|
}
|
|
else if (det<-epsilon)
|
|
{
|
|
value_type u = (P*T);
|
|
if (u>0.0 || u<det) return;
|
|
|
|
Vec3 Q = T ^ E1;
|
|
value_type v = (Q*_d);
|
|
if (v>0.0 || v<det) return;
|
|
|
|
if ((u+v) < det) return;
|
|
|
|
value_type inv_det = 1.0/det;
|
|
value_type t = (Q*E2)*inv_det;
|
|
if (t<0.0 || t>_length) return;
|
|
|
|
u *= inv_det;
|
|
v *= inv_det;
|
|
|
|
r0 = 1.0-u-v;
|
|
r1 = u;
|
|
r2 = v;
|
|
r = t * _inverse_length;
|
|
}
|
|
else
|
|
{
|
|
return;
|
|
}
|
|
|
|
// Remap ratio into the range of LineSegment
|
|
const osg::Vec3d& lsStart = _settings->_lineSegIntersector->getStart();
|
|
const osg::Vec3d& lsEnd = _settings->_lineSegIntersector->getEnd();
|
|
double remap_ratio = ((_start - lsStart).length() + r*_length)/(lsEnd - lsStart).length();
|
|
|
|
Vec3 in = lsStart*(1.0 - remap_ratio) + lsEnd*remap_ratio; // == v0*r0 + v1*r1 + v2*r2;
|
|
Vec3 normal = E1^E2;
|
|
normal.normalize();
|
|
|
|
LineSegmentIntersector::Intersection hit;
|
|
hit.ratio = remap_ratio;
|
|
hit.matrix = _settings->_iv->getModelMatrix();
|
|
hit.nodePath = _settings->_iv->getNodePath();
|
|
hit.drawable = _settings->_drawable;
|
|
hit.primitiveIndex = _primitiveIndex;
|
|
|
|
hit.localIntersectionPoint = in;
|
|
hit.localIntersectionNormal = normal;
|
|
|
|
if (_settings->_vertices.valid())
|
|
{
|
|
const osg::Vec3* first = &(_settings->_vertices->front());
|
|
hit.indexList.reserve(3);
|
|
hit.ratioList.reserve(3);
|
|
|
|
if (r0!=0.0f)
|
|
{
|
|
hit.indexList.push_back(&v0-first);
|
|
hit.ratioList.push_back(r0);
|
|
}
|
|
|
|
if (r1!=0.0f)
|
|
{
|
|
hit.indexList.push_back(&v1-first);
|
|
hit.ratioList.push_back(r1);
|
|
}
|
|
|
|
if (r2!=0.0f)
|
|
{
|
|
hit.indexList.push_back(&v2-first);
|
|
hit.ratioList.push_back(r2);
|
|
}
|
|
}
|
|
|
|
_settings->_lineSegIntersector->insertIntersection(hit);
|
|
_hit = true;
|
|
}
|
|
|
|
// handle lines
|
|
void operator()(const osg::Vec3&, bool /*treatVertexDataAsTemporary*/)
|
|
{
|
|
++_primitiveIndex;
|
|
}
|
|
|
|
void operator()(const osg::Vec3&, const osg::Vec3&, bool /*treatVertexDataAsTemporary*/)
|
|
{
|
|
++_primitiveIndex;
|
|
}
|
|
|
|
// handle triangles
|
|
void operator()(const osg::Vec3& v0, const osg::Vec3& v1, const osg::Vec3& v2, bool /*treatVertexDataAsTemporary*/)
|
|
{
|
|
intersect(v0,v1,v2);
|
|
++_primitiveIndex;
|
|
}
|
|
|
|
void operator()(const osg::Vec3& v0, const osg::Vec3& v1, const osg::Vec3& v2, const osg::Vec3& v3, bool /*treatVertexDataAsTemporary*/)
|
|
{
|
|
intersect(v0,v1,v3);
|
|
intersect(v1,v2,v3);
|
|
++_primitiveIndex;
|
|
}
|
|
|
|
void intersect(const osg::Vec3Array*, int , unsigned int)
|
|
{
|
|
}
|
|
|
|
void intersect(const osg::Vec3Array*, int, unsigned int, unsigned int)
|
|
{
|
|
}
|
|
|
|
void intersect(const osg::Vec3Array* vertices, int primitiveIndex, unsigned int p0, unsigned int p1, unsigned int p2)
|
|
{
|
|
if (_settings->_limitOneIntersection && _hit) return;
|
|
|
|
_primitiveIndex = primitiveIndex;
|
|
|
|
intersect((*vertices)[p0], (*vertices)[p1], (*vertices)[p2]);
|
|
}
|
|
|
|
void intersect(const osg::Vec3Array* vertices, int primitiveIndex, unsigned int p0, unsigned int p1, unsigned int p2, unsigned int p3)
|
|
{
|
|
if (_settings->_limitOneIntersection && _hit) return;
|
|
|
|
_primitiveIndex = primitiveIndex;
|
|
|
|
intersect((*vertices)[p0], (*vertices)[p1], (*vertices)[p3]);
|
|
intersect((*vertices)[p1], (*vertices)[p2], (*vertices)[p3]);
|
|
}
|
|
};
|
|
|
|
} // namespace LineSegmentIntersectorUtils
|
|
|
|
///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
|
//
|
|
// LineSegmentIntersector
|
|
//
|
|
|
|
LineSegmentIntersector::LineSegmentIntersector(const osg::Vec3d& start, const osg::Vec3d& end):
|
|
_parent(0),
|
|
_start(start),
|
|
_end(end)
|
|
{
|
|
}
|
|
|
|
LineSegmentIntersector::LineSegmentIntersector(CoordinateFrame cf, const osg::Vec3d& start, const osg::Vec3d& end,
|
|
LineSegmentIntersector* parent, osgUtil::Intersector::IntersectionLimit intersectionLimit):
|
|
Intersector(cf, intersectionLimit),
|
|
_parent(parent),
|
|
_start(start),
|
|
_end(end)
|
|
{
|
|
}
|
|
|
|
LineSegmentIntersector::LineSegmentIntersector(CoordinateFrame cf, double x, double y):
|
|
Intersector(cf),
|
|
_parent(0)
|
|
{
|
|
switch(cf)
|
|
{
|
|
case WINDOW : _start.set(x,y,0.0); _end.set(x,y,1.0); break;
|
|
case PROJECTION : _start.set(x,y,-1.0); _end.set(x,y,1.0); break;
|
|
case VIEW : _start.set(x,y,0.0); _end.set(x,y,1.0); break;
|
|
case MODEL : _start.set(x,y,0.0); _end.set(x,y,1.0); break;
|
|
}
|
|
}
|
|
|
|
Intersector* LineSegmentIntersector::clone(osgUtil::IntersectionVisitor& iv)
|
|
{
|
|
if (_coordinateFrame==MODEL && iv.getModelMatrix()==0)
|
|
{
|
|
osg::ref_ptr<LineSegmentIntersector> lsi = new LineSegmentIntersector(_start, _end);
|
|
lsi->_parent = this;
|
|
lsi->_intersectionLimit = this->_intersectionLimit;
|
|
lsi->setPrecisionHint(getPrecisionHint());
|
|
return lsi.release();
|
|
}
|
|
|
|
// 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;
|
|
lsi->setPrecisionHint(getPrecisionHint());
|
|
return lsi.release();
|
|
}
|
|
|
|
osg::Matrix LineSegmentIntersector::getTransformation(IntersectionVisitor& iv, CoordinateFrame cf)
|
|
{
|
|
osg::Matrix matrix;
|
|
switch (cf)
|
|
{
|
|
case(WINDOW):
|
|
if (iv.getWindowMatrix()) matrix.preMult( *iv.getWindowMatrix() );
|
|
if (iv.getProjectionMatrix()) matrix.preMult( *iv.getProjectionMatrix() );
|
|
if (iv.getViewMatrix()) matrix.preMult( *iv.getViewMatrix() );
|
|
if (iv.getModelMatrix()) matrix.preMult( *iv.getModelMatrix() );
|
|
break;
|
|
case(PROJECTION):
|
|
if (iv.getProjectionMatrix()) matrix.preMult( *iv.getProjectionMatrix() );
|
|
if (iv.getViewMatrix()) matrix.preMult( *iv.getViewMatrix() );
|
|
if (iv.getModelMatrix()) matrix.preMult( *iv.getModelMatrix() );
|
|
break;
|
|
case(VIEW):
|
|
if (iv.getViewMatrix()) matrix.preMult( *iv.getViewMatrix() );
|
|
if (iv.getModelMatrix()) matrix.preMult( *iv.getModelMatrix() );
|
|
break;
|
|
case(MODEL):
|
|
if (iv.getModelMatrix()) matrix = *iv.getModelMatrix();
|
|
break;
|
|
}
|
|
|
|
osg::Matrix inverse;
|
|
inverse.invert(matrix);
|
|
return inverse;
|
|
}
|
|
|
|
bool LineSegmentIntersector::enter(const osg::Node& node)
|
|
{
|
|
if (reachedLimit()) return false;
|
|
return !node.isCullingActive() || intersects( node.getBound() );
|
|
}
|
|
|
|
void LineSegmentIntersector::leave()
|
|
{
|
|
// do nothing
|
|
}
|
|
|
|
void LineSegmentIntersector::intersect(osgUtil::IntersectionVisitor& iv, osg::Drawable* drawable)
|
|
{
|
|
if (reachedLimit()) return;
|
|
|
|
osg::Vec3d s(_start), e(_end);
|
|
if ( drawable->isCullingActive() && !intersectAndClip( s, e, drawable->getBoundingBox() ) ) return;
|
|
|
|
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)
|
|
{
|
|
if (reachedLimit()) return;
|
|
|
|
LineSegmentIntersectorUtils::Settings settings;
|
|
settings._lineSegIntersector = this;
|
|
settings._iv = &iv;
|
|
settings._drawable = drawable;
|
|
settings._limitOneIntersection = (_intersectionLimit == LIMIT_ONE_PER_DRAWABLE || _intersectionLimit == LIMIT_ONE);
|
|
|
|
osg::Geometry* geometry = drawable->asGeometry();
|
|
if (geometry)
|
|
{
|
|
settings._vertices = dynamic_cast<osg::Vec3Array*>(geometry->getVertexArray());
|
|
}
|
|
|
|
osg::KdTree* kdTree = iv.getUseKdTreeWhenAvailable() ? dynamic_cast<osg::KdTree*>(drawable->getShape()) : 0;
|
|
|
|
if (getPrecisionHint()==USE_DOUBLE_CALCULATIONS)
|
|
{
|
|
osg::TemplatePrimitiveFunctor<LineSegmentIntersectorUtils::IntersectFunctor<osg::Vec3d, double> > intersector;
|
|
intersector.set(s,e, &settings);
|
|
|
|
if (kdTree) kdTree->intersect(intersector, kdTree->getNode(0));
|
|
else drawable->accept(intersector);
|
|
}
|
|
else
|
|
{
|
|
osg::TemplatePrimitiveFunctor<LineSegmentIntersectorUtils::IntersectFunctor<osg::Vec3f, float> > intersector;
|
|
intersector.set(s,e, &settings);
|
|
|
|
if (kdTree) kdTree->intersect(intersector, kdTree->getNode(0));
|
|
else drawable->accept(intersector);
|
|
}
|
|
}
|
|
|
|
void LineSegmentIntersector::reset()
|
|
{
|
|
Intersector::reset();
|
|
|
|
_intersections.clear();
|
|
}
|
|
|
|
bool LineSegmentIntersector::intersects(const osg::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;
|
|
|
|
osg::Vec3d sm = _start - bs._center;
|
|
double c = sm.length2()-bs._radius*bs._radius;
|
|
if (c<0.0) return true;
|
|
|
|
osg::Vec3d se = _end-_start;
|
|
double a = se.length2();
|
|
double b = (sm*se)*2.0;
|
|
double d = b*b-4.0*a*c;
|
|
|
|
if (d<0.0) return false;
|
|
|
|
d = sqrt(d);
|
|
|
|
double div = 1.0/(2.0*a);
|
|
|
|
double r1 = (-b-d)*div;
|
|
double r2 = (-b+d)*div;
|
|
|
|
if (r1<=0.0 && r2<=0.0) return false;
|
|
|
|
if (r1>=1.0 && r2>=1.0) return false;
|
|
|
|
if (_intersectionLimit == LIMIT_NEAREST && !getIntersections().empty())
|
|
{
|
|
double ratio = (sm.length() - bs._radius) / sqrt(a);
|
|
if (ratio >= getIntersections().begin()->ratio) return false;
|
|
}
|
|
|
|
// passed all the rejection tests so line must intersect bounding sphere, return true.
|
|
return true;
|
|
}
|
|
|
|
bool LineSegmentIntersector::intersectAndClip(osg::Vec3d& s, osg::Vec3d& e,const osg::BoundingBox& bbInput)
|
|
{
|
|
osg::Vec3d bb_min(bbInput._min);
|
|
osg::Vec3d bb_max(bbInput._max);
|
|
|
|
double epsilon = 1e-5;
|
|
|
|
// compare s and e against the xMin to xMax range of bb.
|
|
if (s.x()<=e.x())
|
|
{
|
|
// trivial reject of segment wholely outside.
|
|
if (e.x()<bb_min.x()) return false;
|
|
if (s.x()>bb_max.x()) return false;
|
|
|
|
if (s.x()<bb_min.x())
|
|
{
|
|
// clip s to xMin.
|
|
double r = (bb_min.x()-s.x())/(e.x()-s.x()) - epsilon;
|
|
if (r>0.0) s = s + (e-s)*r;
|
|
}
|
|
|
|
if (e.x()>bb_max.x())
|
|
{
|
|
// clip e to xMax.
|
|
double r = (bb_max.x()-s.x())/(e.x()-s.x()) + epsilon;
|
|
if (r<1.0) e = s+(e-s)*r;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
if (s.x()<bb_min.x()) return false;
|
|
if (e.x()>bb_max.x()) return false;
|
|
|
|
if (e.x()<bb_min.x())
|
|
{
|
|
// clip e to xMin.
|
|
double r = (bb_min.x()-e.x())/(s.x()-e.x()) - epsilon;
|
|
if (r>0.0) e = e + (s-e)*r;
|
|
}
|
|
|
|
if (s.x()>bb_max.x())
|
|
{
|
|
// clip s to xMax.
|
|
double r = (bb_max.x()-e.x())/(s.x()-e.x()) + epsilon;
|
|
if (r<1.0) s = e + (s-e)*r;
|
|
}
|
|
}
|
|
|
|
// compare s and e against the yMin to yMax range of bb.
|
|
if (s.y()<=e.y())
|
|
{
|
|
// trivial reject of segment wholely outside.
|
|
if (e.y()<bb_min.y()) return false;
|
|
if (s.y()>bb_max.y()) return false;
|
|
|
|
if (s.y()<bb_min.y())
|
|
{
|
|
// clip s to yMin.
|
|
double r = (bb_min.y()-s.y())/(e.y()-s.y()) - epsilon;
|
|
if (r>0.0) s = s + (e-s)*r;
|
|
}
|
|
|
|
if (e.y()>bb_max.y())
|
|
{
|
|
// clip e to yMax.
|
|
double r = (bb_max.y()-s.y())/(e.y()-s.y()) + epsilon;
|
|
if (r<1.0) e = s+(e-s)*r;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
if (s.y()<bb_min.y()) return false;
|
|
if (e.y()>bb_max.y()) return false;
|
|
|
|
if (e.y()<bb_min.y())
|
|
{
|
|
// clip e to yMin.
|
|
double r = (bb_min.y()-e.y())/(s.y()-e.y()) - epsilon;
|
|
if (r>0.0) e = e + (s-e)*r;
|
|
}
|
|
|
|
if (s.y()>bb_max.y())
|
|
{
|
|
// clip s to yMax.
|
|
double r = (bb_max.y()-e.y())/(s.y()-e.y()) + epsilon;
|
|
if (r<1.0) s = e + (s-e)*r;
|
|
}
|
|
}
|
|
|
|
// compare s and e against the zMin to zMax range of bb.
|
|
if (s.z()<=e.z())
|
|
{
|
|
// trivial reject of segment wholely outside.
|
|
if (e.z()<bb_min.z()) return false;
|
|
if (s.z()>bb_max.z()) return false;
|
|
|
|
if (s.z()<bb_min.z())
|
|
{
|
|
// clip s to zMin.
|
|
double r = (bb_min.z()-s.z())/(e.z()-s.z()) - epsilon;
|
|
if (r>0.0) s = s + (e-s)*r;
|
|
}
|
|
|
|
if (e.z()>bb_max.z())
|
|
{
|
|
// clip e to zMax.
|
|
double r = (bb_max.z()-s.z())/(e.z()-s.z()) + epsilon;
|
|
if (r<1.0) e = s+(e-s)*r;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
if (s.z()<bb_min.z()) return false;
|
|
if (e.z()>bb_max.z()) return false;
|
|
|
|
if (e.z()<bb_min.z())
|
|
{
|
|
// clip e to zMin.
|
|
double r = (bb_min.z()-e.z())/(s.z()-e.z()) - epsilon;
|
|
if (r>0.0) e = e + (s-e)*r;
|
|
}
|
|
|
|
if (s.z()>bb_max.z())
|
|
{
|
|
// clip s to zMax.
|
|
double r = (bb_max.z()-e.z())/(s.z()-e.z()) + epsilon;
|
|
if (r<1.0) s = e + (s-e)*r;
|
|
}
|
|
}
|
|
|
|
// OSG_NOTICE<<"clampped segment "<<s<<" "<<e<<std::endl;
|
|
|
|
// if (s==e) return false;
|
|
|
|
return true;
|
|
}
|
|
|
|
osg::Texture* LineSegmentIntersector::Intersection::getTextureLookUp(osg::Vec3& tc) const
|
|
{
|
|
osg::Geometry* geometry = drawable.valid() ? drawable->asGeometry() : 0;
|
|
osg::Vec3Array* vertices = geometry ? dynamic_cast<osg::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];
|
|
|
|
osg::Array* texcoords = (geometry->getNumTexCoordArrays()>0) ? geometry->getTexCoordArray(0) : 0;
|
|
osg::FloatArray* texcoords_FloatArray = dynamic_cast<osg::FloatArray*>(texcoords);
|
|
osg::Vec2Array* texcoords_Vec2Array = dynamic_cast<osg::Vec2Array*>(texcoords);
|
|
osg::Vec3Array* texcoords_Vec3Array = dynamic_cast<osg::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 osg::Vec2& tc1 = (*texcoords_Vec2Array)[i1];
|
|
const osg::Vec2& tc2 = (*texcoords_Vec2Array)[i2];
|
|
const osg::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 osg::Vec3& tc1 = (*texcoords_Vec3Array)[i1];
|
|
const osg::Vec3& tc2 = (*texcoords_Vec3Array)[i2];
|
|
const osg::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 osg::TexMat* activeTexMat = 0;
|
|
const osg::Texture* activeTexture = 0;
|
|
|
|
if (drawable->getStateSet())
|
|
{
|
|
const osg::TexMat* texMat = dynamic_cast<osg::TexMat*>(drawable->getStateSet()->getTextureAttribute(0,osg::StateAttribute::TEXMAT));
|
|
if (texMat) activeTexMat = texMat;
|
|
|
|
const osg::Texture* texture = dynamic_cast<osg::Texture*>(drawable->getStateSet()->getTextureAttribute(0,osg::StateAttribute::TEXTURE));
|
|
if (texture) activeTexture = texture;
|
|
}
|
|
|
|
for(osg::NodePath::const_reverse_iterator itr = nodePath.rbegin();
|
|
itr != nodePath.rend() && (!activeTexMat || !activeTexture);
|
|
++itr)
|
|
{
|
|
const osg::Node* node = *itr;
|
|
if (node->getStateSet())
|
|
{
|
|
if (!activeTexMat)
|
|
{
|
|
const osg::TexMat* texMat = dynamic_cast<const osg::TexMat*>(node->getStateSet()->getTextureAttribute(0,osg::StateAttribute::TEXMAT));
|
|
if (texMat) activeTexMat = texMat;
|
|
}
|
|
|
|
if (!activeTexture)
|
|
{
|
|
const osg::Texture* texture = dynamic_cast<const osg::Texture*>(node->getStateSet()->getTextureAttribute(0,osg::StateAttribute::TEXTURE));
|
|
if (texture) activeTexture = texture;
|
|
}
|
|
}
|
|
}
|
|
|
|
if (activeTexMat)
|
|
{
|
|
osg::Vec4 tc_transformed = osg::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<osg::Texture*>(activeTexture);
|
|
|
|
}
|
|
return 0;
|
|
}
|