/* -*-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 #include #include #include #include #include #include #include #include using namespace osgUtil; namespace LineSegmentIntersectorUtils { struct Settings : public osg::Referenced { Settings() : _lineSegIntersector(0), _iv(0), _drawable(0), _limitOneIntersection(false) {} osgUtil::LineSegmentIntersector* _lineSegIntersector; osgUtil::IntersectionVisitor* _iv; osg::Drawable* _drawable; osg::ref_ptr _vertices; bool _limitOneIntersection; }; template struct IntersectFunctor { osg::ref_ptr _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.xMax()) return false; if (s.x()bb.xMax()) { // clip e to xMax. e = s+_d_invX*(bb.xMax()-s.x()); } } else { if (s.x()bb.xMax()) return false; if (e.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.yMax()) return false; if (s.y()bb.yMax()) { // clip e to yMax. e = s+_d_invY*(bb.yMax()-s.y()); } } else { if (s.y()bb.yMax()) return false; if (e.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.zMax()) return false; if (s.z()bb.zMax()) { // clip e to zMax. e = s+_d_invZ*(bb.zMax()-s.z()); } } else { if (s.z()bb.zMax()) return false; if (e.z()bb.zMax()) { // clip e to zMax. s = s+_d_invZ*(bb.zMax()-s.z()); } } // OSG_NOTICE<<"clampped segment "<_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 || u0.0 || v_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 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 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; osg::ref_ptr settings = new LineSegmentIntersectorUtils::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(geometry->getVertexArray()); } osg::KdTree* kdTree = iv.getUseKdTreeWhenAvailable() ? dynamic_cast(drawable->getShape()) : 0; if (getPrecisionHint()==USE_DOUBLE_CALCULATIONS) { osg::TemplatePrimitiveFunctor > intersector; intersector.set(s,e, settings.get()); if (kdTree) kdTree->intersect(intersector, kdTree->getNode(0)); else drawable->accept(intersector); } else { osg::TemplatePrimitiveFunctor > intersector; intersector.set(s,e, settings.get()); 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_max.x()) return false; if (s.x()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_max.x()) return false; if (e.x()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_max.y()) return false; if (s.y()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_max.y()) return false; if (e.y()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_max.z()) return false; if (s.z()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_max.z()) return false; if (e.z()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 "<asGeometry() : 0; osg::Vec3Array* vertices = geometry ? dynamic_cast(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(texcoords); osg::Vec2Array* texcoords_Vec2Array = dynamic_cast(texcoords); osg::Vec3Array* texcoords_Vec3Array = dynamic_cast(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(drawable->getStateSet()->getTextureAttribute(0,osg::StateAttribute::TEXMAT)); if (texMat) activeTexMat = texMat; const osg::Texture* texture = dynamic_cast(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(node->getStateSet()->getTextureAttribute(0,osg::StateAttribute::TEXMAT)); if (texMat) activeTexMat = texMat; } if (!activeTexture) { const osg::Texture* texture = dynamic_cast(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(activeTexture->getTextureWidth()); tc.y() *= static_cast(activeTexture->getTextureHeight()); tc.z() *= static_cast(activeTexture->getTextureDepth()); } } return const_cast(activeTexture); } return 0; }