osgEarth 2.1.1
|
00001 /* -*-c++-*- */ 00002 /* osgEarth - Dynamic map generation toolkit for OpenSceneGraph 00003 * Copyright 2008-2010 Pelican Mapping 00004 * http://osgearth.org 00005 * 00006 * osgEarth is free software; you can redistribute it and/or modify 00007 * it under the terms of the GNU Lesser General Public License as published by 00008 * the Free Software Foundation; either version 2 of the License, or 00009 * (at your option) any later version. 00010 * 00011 * This program is distributed in the hope that it will be useful, 00012 * but WITHOUT ANY WARRANTY; without even the implied warranty of 00013 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 00014 * GNU Lesser General Public License for more details. 00015 * 00016 * You should have received a copy of the GNU Lesser General Public License 00017 * along with this program. If not, see <http://www.gnu.org/licenses/> 00018 */ 00019 #include <osgEarthUtil/ObjectPlacer> 00020 #include <osgEarth/FindNode> 00021 #include <osgEarth/MapNode> 00022 #include <osgEarth/SpatialReference> 00023 #include <osgSim/LineOfSight> 00024 #include <osgUtil/IntersectionVisitor> 00025 #include <osgUtil/LineSegmentIntersector> 00026 #include <osg/MatrixTransform> 00027 #include <osg/CoordinateSystemNode> 00028 00029 using namespace osgEarth::Util; 00030 using namespace osgEarth; 00031 00032 struct CachingReadCallback : public osgSim::DatabaseCacheReadCallback 00033 { 00034 CachingReadCallback(int maxReads) : _reads(0), _maxReads(maxReads) { } 00035 void reset() { _reads = 0; } 00036 virtual osg::Node* readNodeFile(const std::string& filename) { 00037 if ( _reads < _maxReads ) { 00038 _reads++; 00039 return osgSim::DatabaseCacheReadCallback::readNodeFile(filename); 00040 } 00041 else { 00042 return NULL; 00043 } 00044 } 00045 int _reads, _maxReads; 00046 osg::ref_ptr<osg::Node> _lastNodeRead; 00047 }; 00048 00049 ObjectPlacer::ObjectPlacer( osg::Node* terrain, int traversalMask, bool clamp, int maxLevel ) : 00050 _traversalMask( traversalMask ), 00051 _clamp( clamp ) 00052 { 00053 _mapNode = findTopMostNodeOfType<osgEarth::MapNode>( terrain ); 00054 _csn = findTopMostNodeOfType<osg::CoordinateSystemNode>( terrain ); 00055 _readCallback = new CachingReadCallback( maxLevel ); 00056 } 00057 00058 bool 00059 ObjectPlacer::clampGeocentric( osg::CoordinateSystemNode* csn, double lat_rad, double lon_rad, osg::Vec3d& out ) const 00060 { 00061 osg::Vec3d start, end; 00062 00063 csn->getEllipsoidModel()->convertLatLongHeightToXYZ( lat_rad, lon_rad, 50000, start.x(), start.y(), start.z() ); 00064 csn->getEllipsoidModel()->convertLatLongHeightToXYZ( lat_rad, lon_rad, -50000, end.x(), end.y(), end.z() ); 00065 osgUtil::LineSegmentIntersector* i = new osgUtil::LineSegmentIntersector( start, end ); 00066 00067 osgUtil::IntersectionVisitor iv; 00068 iv.setIntersector( i ); 00069 static_cast<CachingReadCallback*>(_readCallback.get())->reset(); 00070 iv.setReadCallback( _readCallback.get() ); 00071 iv.setTraversalMask( _traversalMask ); 00072 00073 _mapNode->accept( iv ); 00074 00075 osgUtil::LineSegmentIntersector::Intersections& results = i->getIntersections(); 00076 if ( !results.empty() ) 00077 { 00078 const osgUtil::LineSegmentIntersector::Intersection& result = *results.begin(); 00079 out = result.matrix.valid() ? 00080 result.localIntersectionPoint * (*result.matrix) : 00081 result.localIntersectionPoint; 00082 return true; 00083 } 00084 return false; 00085 } 00086 00087 bool 00088 ObjectPlacer::clampProjected( osg::CoordinateSystemNode* csn, double x, double y, osg::Vec3d& out ) const 00089 { 00090 osg::Vec3d start( x, y, 50000 ); 00091 osg::Vec3d end(x, y, -50000); 00092 osgUtil::LineSegmentIntersector* i = new osgUtil::LineSegmentIntersector( start, end ); 00093 00094 osgUtil::IntersectionVisitor iv; 00095 iv.setIntersector( i ); 00096 static_cast<CachingReadCallback*>(_readCallback.get())->reset(); 00097 iv.setReadCallback( _readCallback.get() ); 00098 iv.setTraversalMask( _traversalMask ); 00099 00100 _mapNode->accept( iv ); 00101 00102 osgUtil::LineSegmentIntersector::Intersections& results = i->getIntersections(); 00103 if ( !results.empty() ) 00104 { 00105 const osgUtil::LineSegmentIntersector::Intersection& result = *results.begin(); 00106 out = result.matrix.valid() ? 00107 result.localIntersectionPoint * (*result.matrix) : 00108 result.localIntersectionPoint; 00109 return true; 00110 } 00111 return false; 00112 } 00113 00114 bool 00115 ObjectPlacer::createPlacerMatrix( double lat_deg, double lon_deg, double height, osg::Matrixd& out_result ) const 00116 { 00117 if ( !_mapNode.valid() || !_csn.valid() ) 00118 { 00119 OE_WARN << "ObjectPlacer: terrain is missing either a Map or CSN node" << std::endl; 00120 return false; 00121 } 00122 00123 // see whether this is a geocentric model: 00124 bool is_geocentric = _csn.valid() && _csn->getEllipsoidModel() != NULL; 00125 00126 const SpatialReference* srs = _mapNode->getMap()->getProfile()->getSRS(); 00127 00128 // now build a matrix: 00129 if ( is_geocentric ) 00130 { 00131 double lat_rad = osg::DegreesToRadians( lat_deg ); 00132 double lon_rad = osg::DegreesToRadians( lon_deg ); 00133 00134 if ( _clamp ) 00135 { 00136 osg::Vec3d c; 00137 if ( clampGeocentric( _csn.get(), lat_rad, lon_rad, c ) ) 00138 { 00139 srs->getEllipsoid()->computeLocalToWorldTransformFromXYZ( c.x(), c.y(), c.z(), out_result ); 00140 } 00141 } 00142 else 00143 { 00144 srs->getEllipsoid()->computeLocalToWorldTransformFromLatLongHeight( lat_rad, lon_rad, height, out_result ); 00145 } 00146 } 00147 else // projected or "flat geographic" 00148 { 00149 osg::Vec3d local(0, 0, height); 00150 00151 // first convert the input coords to the map srs: 00152 srs->getGeographicSRS()->transform2D( lon_deg, lat_deg, srs, local.x(), local.y()); 00153 00154 if ( _clamp ) 00155 { 00156 clampProjected( _csn.get(), local.x(), local.y(), local ); 00157 local.z() += height; 00158 } 00159 out_result = osg::Matrixd::translate( local ); 00160 } 00161 00162 return true; 00163 } 00164 00165 osg::Node* 00166 ObjectPlacer::placeNode( osg::Node* node, double lat_deg, double lon_deg, double height ) const 00167 { 00168 osg::Node* result = NULL; 00169 00170 osg::Matrixd matrix; 00171 if ( createPlacerMatrix( lat_deg, lon_deg, height, matrix ) ) 00172 { 00173 osg::MatrixTransform* mt = new osg::MatrixTransform( matrix ); 00174 mt->addChild( node ); 00175 result = mt; 00176 } 00177 00178 return result; 00179 }