/* -*-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 using namespace osgUtil; namespace PlaneIntersectorUtils { struct RefPolyline : public osg::Referenced { typedef std::vector Polyline; Polyline _polyline; void reverse() { unsigned int s=0; unsigned int e=_polyline.size()-1; for(; s > PolylineMap; typedef std::vector< osg::ref_ptr > PolylineList; PolylineList _polylines; PolylineMap _startPolylineMap; PolylineMap _endPolylineMap; osg::ref_ptr _em; void add(const osg::Vec3d& v1, const osg::Vec3d& v2) { add(osg::Vec4d(v1,0.0), osg::Vec4d(v2,0.0)); } void add(const osg::Vec4d& v1, const osg::Vec4d& v2) { if (v1==v2) return; PolylineMap::iterator v1_start_itr = _startPolylineMap.find(v1); PolylineMap::iterator v1_end_itr = _endPolylineMap.find(v1); PolylineMap::iterator v2_start_itr = _startPolylineMap.find(v2); PolylineMap::iterator v2_end_itr = _endPolylineMap.find(v2); unsigned int v1_connections = 0; if (v1_start_itr != _startPolylineMap.end()) ++v1_connections; if (v1_end_itr != _endPolylineMap.end()) ++v1_connections; unsigned int v2_connections = 0; if (v2_start_itr != _startPolylineMap.end()) ++v2_connections; if (v2_end_itr != _endPolylineMap.end()) ++v2_connections; if (v1_connections==0) // v1 is no connected to anything. { if (v2_connections==0) { // new polyline newline(v1,v2); } else if (v2_connections==1) { // v2 must connect to either a start or an end. if (v2_start_itr != _startPolylineMap.end()) { insertAtStart(v1, v2_start_itr); } else if (v2_end_itr != _endPolylineMap.end()) { insertAtEnd(v1, v2_end_itr); } else { osg::notify(osg::NOTICE)<<"Error: should not get here!"<_polyline.push_back(v1); polyline->_polyline.push_back(v2); _startPolylineMap[v1] = polyline; _endPolylineMap[v2] = polyline; } void insertAtStart(const osg::Vec4d& v, PolylineMap::iterator v_start_itr) { // put v1 at the start of its poyline RefPolyline* polyline = v_start_itr->second.get(); polyline->_polyline.insert(polyline->_polyline.begin(),v); // reinsert the polyine at the new v1 end _startPolylineMap[v] = polyline; // remove the original entry _startPolylineMap.erase(v_start_itr); } void insertAtEnd(const osg::Vec4d& v, PolylineMap::iterator v_end_itr) { // put v1 at the end of its poyline RefPolyline* polyline = v_end_itr->second.get(); polyline->_polyline.push_back(v); // reinsert the polyine at the new v1 end _endPolylineMap[v] = polyline; // remove the original entry _endPolylineMap.erase(v_end_itr); } void fuse_start_to_start(PolylineMap::iterator start1_itr, PolylineMap::iterator start2_itr) { osg::ref_ptr poly1 = start1_itr->second; osg::ref_ptr poly2 = start2_itr->second; PolylineMap::iterator end1_itr = _endPolylineMap.find(poly1->_polyline.back()); PolylineMap::iterator end2_itr = _endPolylineMap.find(poly2->_polyline.back()); // clean up the iterators associated with the original polylines _startPolylineMap.erase(start1_itr); _startPolylineMap.erase(start2_itr); _endPolylineMap.erase(end1_itr); _endPolylineMap.erase(end2_itr); // reverse the first polyline poly1->reverse(); // add the second polyline to the first poly1->_polyline.insert( poly1->_polyline.end(), poly2->_polyline.begin(), poly2->_polyline.end() ); _startPolylineMap[poly1->_polyline.front()] = poly1; _endPolylineMap[poly1->_polyline.back()] = poly1; } void fuse_start_to_end(PolylineMap::iterator start_itr, PolylineMap::iterator end_itr) { osg::ref_ptr end_poly = end_itr->second; osg::ref_ptr start_poly = start_itr->second; PolylineMap::iterator end_start_poly_itr = _endPolylineMap.find(start_poly->_polyline.back()); // add start_poly to end of end_poly end_poly->_polyline.insert( end_poly->_polyline.end(), start_poly->_polyline.begin(), start_poly->_polyline.end() ); // reassign the end of the start poly so that it now points to the merged end_poly end_start_poly_itr->second = end_poly; // remove entries for the end of the end_poly and the start of the start_poly _endPolylineMap.erase(end_itr); _startPolylineMap.erase(start_itr); if (end_poly==start_poly) { _polylines.push_back(end_poly); } } void fuse_end_to_end(PolylineMap::iterator end1_itr, PolylineMap::iterator end2_itr) { // return; osg::ref_ptr poly1 = end1_itr->second; osg::ref_ptr poly2 = end2_itr->second; PolylineMap::iterator start1_itr = _startPolylineMap.find(poly1->_polyline.front()); PolylineMap::iterator start2_itr = _startPolylineMap.find(poly2->_polyline.front()); // clean up the iterators associated with the original polylines _startPolylineMap.erase(start1_itr); _startPolylineMap.erase(start2_itr); _endPolylineMap.erase(end1_itr); _endPolylineMap.erase(end2_itr); // reverse the first polyline poly2->reverse(); // add the second polyline to the first poly1->_polyline.insert( poly1->_polyline.end(), poly2->_polyline.begin(), poly2->_polyline.end() ); _startPolylineMap[poly1->_polyline.front()] = poly1; _endPolylineMap[poly1->_polyline.back()] = poly1; } void consolidatePolylineLists() { // move the remaining open ended line segments into the polyline list for(PolylineMap::iterator sitr = _startPolylineMap.begin(); sitr != _startPolylineMap.end(); ++sitr) { _polylines.push_back(sitr->second); } } void report() { osg::notify(osg::NOTICE)<<"report()"<_polyline; for(RefPolyline::Polyline::iterator vitr = polyline.begin(); vitr != polyline.end(); ++vitr) { osg::notify(osg::NOTICE)<<" "<<*vitr< _matrix; bool _recordHeightsAsAttributes; osg::ref_ptr _em; PolylineConnector _polylineConnector; TriangleIntersector() { _hit = false; } void set(const osg::Plane& plane, const osg::Polytope& polytope, osg::RefMatrix* matrix, bool recordHeightsAsAttributes, osg::EllipsoidModel* em) { _plane = plane; _polytope = polytope; _hit = false; _matrix = matrix; _recordHeightsAsAttributes = recordHeightsAsAttributes; _em = em; } inline double distance(const osg::Plane& plane, const osg::Vec4d& v) const { return plane[0]*v.x()+ plane[1]*v.y()+ plane[2]*v.z()+ plane[3]; } inline void add(osg::Vec4d& vs, osg::Vec4d& ve) { if (_polytope.getPlaneList().empty()) { _polylineConnector.add(vs,ve); } else { for(osg::Polytope::PlaneList::iterator itr = _polytope.getPlaneList().begin(); itr != _polytope.getPlaneList().end(); ++itr) { osg::Plane& plane = *itr; double ds = distance(plane,vs); double de = distance(plane,ve); if (ds<0.0) { if (de<0.0) { // osg::notify(osg::NOTICE)<<"Disgard segment "<0) ++numAbove; else ++numOnPlane; if (d2<0) ++numBelow; else if (d2>0) ++numAbove; else ++numOnPlane; if (d3<0) ++numBelow; else if (d3>0) ++numAbove; else ++numOnPlane; // trivially discard triangles that are completely one side of the plane if (numAbove==3 || numBelow==3) return; _hit = true; if (numOnPlane==3) { // triangle lives wholy in the plane osg::notify(osg::NOTICE)<<"3"<convertXYZToLatLongHeight(tp.x(), tp.y(), tp.z(), latitude, longitude, height); p1[3] = height; tp = v2 * (*_matrix); _em->convertXYZToLatLongHeight(tp.x(), tp.y(), tp.z(), latitude, longitude, height); p2[3] = height; tp = v3 * (*_matrix); _em->convertXYZToLatLongHeight(tp.x(), tp.y(), tp.z(), latitude, longitude, height); p3[3] = height; } else { _em->convertXYZToLatLongHeight(v1.x(), v1.y(), v1.z(), latitude, longitude, height); p1[3] = height; _em->convertXYZToLatLongHeight(v2.x(), v2.y(), v2.z(), latitude, longitude, height); p2[3] = height; _em->convertXYZToLatLongHeight(v3.x(), v3.y(), v3.z(), latitude, longitude, height); p3[3] = height; } } if (d1*d2 < 0.0) { // edge 12 itersects double div = 1.0 / (d2-d1); v[numIntersects++] = p1* (d2*div) - p2 * (d1*div); } if (d2*d3 < 0.0) { // edge 23 itersects double div = 1.0 / (d3-d2); v[numIntersects++] = p2* (d3*div) - p3 * (d2*div); } if (d1*d3 < 0.0) { if (numIntersects<2) { // edge 13 itersects double div = 1.0 / (d3-d1); v[numIntersects++] = p1* (d3*div) - p3 * (d1*div); } else { osg::notify(osg::NOTICE)<<"!!! too many intersecting edges found !!!"< pi = new PlaneIntersector(_plane, _polytope); pi->_parent = this; pi->_recordHeightsAsAttributes = _recordHeightsAsAttributes; pi->_em = _em; return pi.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; switch (_coordinateFrame) { 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::Plane plane = _plane; plane.transformProvidingInverse(matrix); osg::Polytope transformedPolytope; transformedPolytope.setAndTransformProvidingInverse(_polytope, matrix); osg::ref_ptr pi = new PlaneIntersector(plane, transformedPolytope); pi->_parent = this; pi->_recordHeightsAsAttributes = _recordHeightsAsAttributes; pi->_em = _em; return pi.release(); } bool PlaneIntersector::enter(const osg::Node& node) { return !node.isCullingActive() || ( _plane.intersect(node.getBound())==0 && _polytope.contains(node.getBound()) ); } void PlaneIntersector::leave() { // do nothing. } void PlaneIntersector::intersect(osgUtil::IntersectionVisitor& iv, osg::Drawable* drawable) { // osg::notify(osg::NOTICE)<<"PlaneIntersector::intersect(osgUtil::IntersectionVisitor& iv, osg::Drawable* drawable)"<getBound() )!=0 ) return; if ( !_polytope.contains( drawable->getBound() ) ) return; // osg::notify(osg::NOTICE)<<"Succed PlaneIntersector::intersect(osgUtil::IntersectionVisitor& iv, osg::Drawable* drawable)"< ti; ti.set(_plane, _polytope, iv.getModelMatrix(), _recordHeightsAsAttributes, _em.get()); drawable->accept(ti); ti._polylineConnector.consolidatePolylineLists(); if (ti._hit) { Intersections& intersections = getIntersections(); for(PlaneIntersectorUtils::PolylineConnector::PolylineList::iterator pitr = ti._polylineConnector._polylines.begin(); pitr != ti._polylineConnector._polylines.end(); ++pitr) { unsigned int pos = intersections.size(); intersections.push_back(Intersection()); Intersection& new_intersection = intersections[pos]; new_intersection.matrix = iv.getModelMatrix(); new_intersection.polyline.reserve((*pitr)->_polyline.size()); if (_recordHeightsAsAttributes) new_intersection.attributes.reserve((*pitr)->_polyline.size()); for(PlaneIntersectorUtils::RefPolyline::Polyline::iterator vitr = (*pitr)->_polyline.begin(); vitr != (*pitr)->_polyline.end(); ++vitr) { const osg::Vec4d& v = *vitr; new_intersection.polyline.push_back( osg::Vec3d(v.x(), v.y(), v.z()) ); if (_recordHeightsAsAttributes) new_intersection.attributes.push_back( v.w() ); } new_intersection.nodePath = iv.getNodePath(); new_intersection.drawable = drawable; } } } void PlaneIntersector::reset() { Intersector::reset(); _intersections.clear(); }