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 00020 #include <osgEarthUtil/ClampCallback> 00021 #include <osgEarth/Notify> 00022 #include <osgUtil/IntersectionVisitor> 00023 #include <osgUtil/LineSegmentIntersector> 00024 #include <osgEarth/FindNode> 00025 #include <OpenThreads/ScopedLock> 00026 #include <osg/MatrixTransform> 00027 00028 #include <osg/io_utils> 00029 00030 using namespace osg; 00031 using namespace osgEarth; 00032 using namespace osgEarth::Util; 00033 using namespace OpenThreads; 00034 00035 ClampCallback::ClampCallback(osg::Node* terrainNode) 00036 { 00037 _offset = 0; 00038 _lastCulledFrame = 0; 00039 setTerrainNode( terrainNode ); 00040 _intersectionMask = 0xffffffff; 00041 } 00042 00043 bool ClampCallback::clamp(const osg::Vec3d& pos, osg::Vec3d& out) const 00044 { 00045 //Return if there is no terrain node assigned 00046 if (!_terrainNode.valid()) return false; 00047 00048 //Compute the up vector 00049 osg::Vec3d up = _csn.valid() ? _csn->computeLocalUpVector( pos ) : osg::Vec3d(0,0,1); 00050 up.normalize(); 00051 00052 double segOffset = 50000; 00053 00054 osg::Vec3d start = pos + (up * segOffset); 00055 osg::Vec3d end = pos - (up * segOffset); 00056 00057 osgUtil::LineSegmentIntersector* i = new osgUtil::LineSegmentIntersector( start, end ); 00058 00059 osgUtil::IntersectionVisitor iv; 00060 iv.setTraversalMask(_intersectionMask); 00061 iv.setIntersector( i ); 00062 _terrainNode->accept( iv ); 00063 00064 osgUtil::LineSegmentIntersector::Intersections& results = i->getIntersections(); 00065 if ( !results.empty() ) 00066 { 00067 const osgUtil::LineSegmentIntersector::Intersection& result = *results.begin(); 00068 out = result.getWorldIntersectPoint(); 00069 out += (up * _offset); 00070 return true; 00071 } 00072 return false; 00073 } 00074 00075 bool ClampCallback::clampGeometry(osg::Geometry* geom, const osg::Matrixd& localToWorld, const osg::Matrixd& worldToLocal) const 00076 { 00077 osg::Vec3Array* verts = static_cast<osg::Vec3Array*>(geom->getVertexArray()); 00078 for (unsigned int i = 0; i < verts->size(); ++i) 00079 { 00080 osg::Vec3d pos = (*verts)[i]; 00081 pos = pos * localToWorld; 00082 00083 osg::Vec3d out; 00084 if (clamp(pos, out)) 00085 { 00086 out = out * worldToLocal; 00087 (*verts)[i].set(out.x(), out.y(), out.z() ); 00088 } 00089 } 00090 geom->dirtyBound(); 00091 geom->dirtyDisplayList(); 00092 00093 return true; 00094 } 00095 00096 void ClampCallback::setTerrainNode(osg::Node* terrainNode) 00097 { 00098 _terrainNode = terrainNode; 00099 if (_terrainNode.valid()) 00100 { 00101 _csn = findTopMostNodeOfType<osg::CoordinateSystemNode>(_terrainNode.get()); 00102 } 00103 } 00104 00105 void ClampCallback::operator()(osg::Node* node, osg::NodeVisitor* nv) 00106 { 00107 if (nv->getVisitorType() == NodeVisitor::UPDATE_VISITOR) 00108 { 00109 //Only clamp if this node has been culled in the last few frames 00110 if (nv->getFrameStamp()->getFrameNumber() - _lastCulledFrame < 2) 00111 { 00112 osg::MatrixTransform* mt = dynamic_cast<osg::MatrixTransform*>(node); 00113 if (mt) 00114 { 00115 osg::Matrixd matrix = mt->getMatrix(); 00116 osg::Vec3d pos = matrix.getTrans(); 00117 osg::Vec3d clamped; 00118 if (clamp(pos, clamped)) 00119 { 00120 //We need to translate the current matrix by an amount that would move the current position to the clamped position 00121 osg::Vec3d trans = (clamped - pos ); 00122 matrix *= osg::Matrixd::translate( trans ); 00123 mt->setMatrix( matrix ); 00124 } 00125 } 00126 else 00127 { 00128 osg::Geode* geode = dynamic_cast<osg::Geode*>(node); 00129 if (geode) 00130 { 00131 osg::Matrixd localToWorld = osg::computeLocalToWorld( nv->getNodePath() ); 00132 osg::Matrixd worldToLocal = osg::computeWorldToLocal( nv->getNodePath() ); 00133 00134 for (unsigned int i = 0; i < geode->getNumDrawables(); ++i) 00135 { 00136 osg::Geometry* geom = geode->getDrawable(i)->asGeometry(); 00137 if (geom) 00138 { 00139 clampGeometry( geom, localToWorld, worldToLocal); 00140 } 00141 } 00142 } 00143 } 00144 } 00145 } 00146 else if (nv->getVisitorType() == NodeVisitor::CULL_VISITOR) 00147 { 00148 _lastCulledFrame = nv->getFrameStamp()->getFrameNumber(); 00149 } 00150 //Continue traversal 00151 traverse(node, nv); 00152 } 00153 00154 void 00155 ClampCallback::setOffset(double offset) 00156 { 00157 _offset = offset; 00158 }