osg::NodeCallback* ClusterCullingFactory::create(const GeoExtent& extent) { GeoPoint centerPoint; extent.getCentroid( centerPoint ); // select the farthest corner: GeoPoint edgePoint; if ( centerPoint.y() >= 0.0 ) edgePoint = GeoPoint(extent.getSRS(), extent.xMin(), extent.yMin(), 0.0, ALTMODE_ABSOLUTE); else edgePoint = GeoPoint(extent.getSRS(), extent.xMin(), extent.yMax(), 0.0, ALTMODE_ABSOLUTE); // convert both to ECEF and make unit vectors: osg::Vec3d center, centerNormal, edge, edgeNormal; centerPoint.toWorld( center ); edgePoint.toWorld( edge ); edgeNormal = edge; edgeNormal.normalize(); centerNormal = center; centerNormal.normalize(); // determine the height above the center point necessary to see the edge point: double dp = centerNormal * edgeNormal; double centerLen = center.length(); double height = (edge.length() / dp) - centerLen; // set the control point at that height: osg::Vec3d controlPoint = center + centerNormal*height; // cluster culling only occurs beyond the maximum radius: double maxRadius = (controlPoint-edge).length(); // determine the maximum visibility angle (i.e. minimum dot product // of the center up vector and the vector from the control point to // the edge point) osg::Vec3d cpToEdge = edge - controlPoint; cpToEdge.normalize(); double minDeviation = centerNormal * cpToEdge; // create and return the callback. return create( controlPoint, centerNormal, minDeviation, maxRadius); }
SurfaceNode::SurfaceNode(const TileKey& tilekey, const MapInfo& mapinfo, const RenderBindings& bindings, TileDrawable* drawable) : _debugNodeVisible(false) { _tileKey = tilekey; _drawable = drawable; _surfaceGeode = new osg::Geode(); _surfaceGeode->addDrawable( drawable ); // Create the final node. addChild( _surfaceGeode.get() ); // Establish a local reference frame for the tile: osg::Vec3d centerWorld; GeoPoint centroid; tilekey.getExtent().getCentroid(centroid); centroid.toWorld(centerWorld); osg::Matrix local2world; centroid.createLocalToWorld( local2world ); setMatrix( local2world ); _worldCorners.resize(8); _childrenCorners.resize(4); for(size_t i = 0; i < _childrenCorners.size(); ++i) { _childrenCorners[i].resize(8); } // Initialize the cached bounding box. setElevationExtrema(osg::Vec2f(0, 0)); }
void TerrainProfileCalculator::computeTerrainProfile( osgEarth::MapNode* mapNode, const GeoPoint& start, const GeoPoint& end, TerrainProfile& profile) { osg::Vec3d startvec, endvec; start.toWorld( startvec, mapNode->getTerrain() ); end.toWorld( endvec, mapNode->getTerrain() ); osgSim::ElevationSlice slice; slice.setStartPoint( startvec ); slice.setEndPoint( endvec ); slice.setDatabaseCacheReadCallback( 0 ); slice.computeIntersections( mapNode->getTerrainEngine()); profile.clear(); for (unsigned int i = 0; i < slice.getDistanceHeightIntersections().size(); i++) { profile.addElevation( slice.getDistanceHeightIntersections()[i].first, slice.getDistanceHeightIntersections()[i].second); } }
bool MapInfo::toWorldPoint( const GeoPoint& input, osg::Vec3d& output ) const { if (!input.isValid()) return false; //Transform the incoming point to the map's SRS GeoPoint mapPoint; toMapPoint(input, mapPoint ); return mapPoint.toWorld( output ); }
void ClipSpace::clampToLeft(GeoPoint& p) { p.transformInPlace(p.getSRS()->getGeographicSRS()); osg::Vec3d world; p.toWorld(world); osg::Vec3d clip = world * _worldToClip; clip.x() = -1.0; world = clip * _clipToWorld; p.fromWorld(p.getSRS(), world); }
osg::AnimationPath* createAnimationPath(const GeoPoint& pos, const SpatialReference* mapSRS, float radius, double looptime) { // set up the animation path osg::AnimationPath* animationPath = new osg::AnimationPath; animationPath->setLoopMode(osg::AnimationPath::LOOP); int numSamples = 40; double delta = osg::PI * 2.0 / (double)numSamples; //Get the center point in geocentric GeoPoint mapPos = pos.transform(mapSRS); osg::Vec3d centerWorld; mapPos.toWorld( centerWorld ); bool isProjected = mapSRS->isProjected(); osg::Vec3d up = isProjected ? osg::Vec3d(0,0,1) : centerWorld; up.normalize(); //Get the "side" vector osg::Vec3d side = isProjected ? osg::Vec3d(1,0,0) : up ^ osg::Vec3d(0,0,1); double time=0.0f; double time_delta = looptime/(double)numSamples; osg::Vec3d firstPosition; osg::Quat firstRotation; for (unsigned int i = 0; i < (unsigned int)numSamples; i++) { double angle = delta * (double)i; osg::Quat quat(angle, up ); osg::Vec3d spoke = quat * (side * radius); osg::Vec3d end = centerWorld + spoke; osg::Quat makeUp; makeUp.makeRotate(osg::Vec3d(0,0,1), up); osg::Quat rot = makeUp; animationPath->insert(time,osg::AnimationPath::ControlPoint(end,rot)); if (i == 0) { firstPosition = end; firstRotation = rot; } time += time_delta; } animationPath->insert(time, osg::AnimationPath::ControlPoint(firstPosition, firstRotation)); return animationPath; }
osg::Geometry* GeometryPool::createGeometry(const TileKey& tileKey, const MapInfo& mapInfo, MaskGenerator* maskSet) const { // Establish a local reference frame for the tile: osg::Vec3d centerWorld; GeoPoint centroid; tileKey.getExtent().getCentroid( centroid ); centroid.toWorld( centerWorld ); osg::Matrix world2local, local2world; centroid.createWorldToLocal( world2local ); local2world.invert( world2local ); // Attempt to calculate the number of verts in the surface geometry. bool createSkirt = _options.heightFieldSkirtRatio() > 0.0f; unsigned numVertsInSurface = (_tileSize*_tileSize); unsigned numVertsInSkirt = createSkirt ? _tileSize*4u - 4u : 0; unsigned numVerts = numVertsInSurface + numVertsInSkirt; unsigned numIndiciesInSurface = (_tileSize-1) * (_tileSize-1) * 6; unsigned numIncidesInSkirt = getNumSkirtElements(); GLenum mode = (_options.gpuTessellation() == true) ? GL_PATCHES : GL_TRIANGLES; // Pre-allocate enough space for all triangles. osg::DrawElements* primSet = new osg::DrawElementsUShort(mode); primSet->reserveElements(numIndiciesInSurface + numIncidesInSkirt); osg::BoundingSphere tileBound; // the geometry: osg::Geometry* geom = new osg::Geometry(); geom->setUseVertexBufferObjects(true); geom->setUseDisplayList(false); geom->addPrimitiveSet( primSet ); // the vertex locations: osg::Vec3Array* verts = new osg::Vec3Array(); verts->reserve( numVerts ); geom->setVertexArray( verts ); // the surface normals (i.e. extrusion vectors) osg::Vec3Array* normals = new osg::Vec3Array(); normals->reserve( numVerts ); geom->setNormalArray( normals ); geom->setNormalBinding( geom->BIND_PER_VERTEX ); osg::Vec3Array* neighbors = 0L; if ( _options.morphTerrain() == true ) { // neighbor positions (for morphing) neighbors = new osg::Vec3Array(); neighbors->reserve( numVerts ); geom->setTexCoordArray( 1, neighbors ); } // tex coord is [0..1] across the tile. The 3rd dimension tracks whether the // vert is masked: 0=yes, 1=no #ifdef SHARE_TEX_COORDS bool populateTexCoords = false; if ( !_sharedTexCoords.valid() ) { _sharedTexCoords = new osg::Vec3Array(); _sharedTexCoords->reserve( numVerts ); populateTexCoords = true; } osg::Vec3Array* texCoords = _sharedTexCoords.get(); #else bool populateTexCoords = true; osg::Vec3Array* texCoords = new osg::Vec3Array(); texCoords->reserve( numVerts ); #endif geom->setTexCoordArray( 0, texCoords ); float delta = 1.0/(_tileSize-1); osg::Vec3d tdelta(delta,0,0); tdelta.normalize(); osg::Vec3d vZero(0,0,0); osg::ref_ptr<GeoLocator> locator = GeoLocator::createForKey( tileKey, mapInfo ); for(unsigned row=0; row<_tileSize; ++row) { float ny = (float)row/(float)(_tileSize-1); for(unsigned col=0; col<_tileSize; ++col) { float nx = (float)col/(float)(_tileSize-1); osg::Vec3d model; locator->unitToModel(osg::Vec3d(nx, ny, 0.0f), model); osg::Vec3d modelLTP = model*world2local; verts->push_back( modelLTP ); tileBound.expandBy( verts->back() ); if ( populateTexCoords ) { // if masked then set textCoord z-value to 0.0 float marker = maskSet ? maskSet->getMarker(nx, ny) : MASK_MARKER_NORMAL; texCoords->push_back( osg::Vec3f(nx, ny, marker) ); } osg::Vec3d modelPlusOne; locator->unitToModel(osg::Vec3d(nx, ny, 1.0f), modelPlusOne); osg::Vec3d normal = (modelPlusOne*world2local)-modelLTP; normal.normalize(); normals->push_back( normal ); // neighbor: if ( neighbors ) { osg::Vec3d modelNeighborLTP = (*verts)[verts->size() - getMorphNeighborIndexOffset(col, row, _tileSize)]; neighbors->push_back(modelNeighborLTP); } } } // Now tessellate the surface. // TODO: do we really need this?? bool swapOrientation = !locator->orientationOpenGL(); for(unsigned j=0; j<_tileSize-1; ++j) { for(unsigned i=0; i<_tileSize-1; ++i) { int i00; int i01; if (swapOrientation) { i01 = j*_tileSize + i; i00 = i01+_tileSize; } else { i00 = j*_tileSize + i; i01 = i00+_tileSize; } int i10 = i00+1; int i11 = i01+1; // skip any triangles that have a discarded vertex: bool discard = maskSet && ( maskSet->isMasked( (*texCoords)[i00] ) || maskSet->isMasked( (*texCoords)[i11] ) ); if ( !discard ) { discard = maskSet && maskSet->isMasked( (*texCoords)[i01] ); if ( !discard ) { primSet->addElement(i01); primSet->addElement(i00); primSet->addElement(i11); } discard = maskSet && maskSet->isMasked( (*texCoords)[i10] ); if ( !discard ) { primSet->addElement(i00); primSet->addElement(i10); primSet->addElement(i11); } } } } if ( createSkirt ) { // SKIRTS: // calculate the skirt extrusion height double height = tileBound.radius() * _options.heightFieldSkirtRatio().get(); unsigned skirtIndex = verts->size(); // first, create all the skirt verts, normals, and texcoords. for(int c=0; c<(int)_tileSize-1; ++c) addSkirtDataForIndex( c, height ); //top for(int r=0; r<(int)_tileSize-1; ++r) addSkirtDataForIndex( r*_tileSize+(_tileSize-1), height ); //right for(int c=_tileSize-1; c>=0; --c) addSkirtDataForIndex( (_tileSize-1)*_tileSize+c, height ); //bottom for(int r=_tileSize-1; r>=0; --r) addSkirtDataForIndex( r*_tileSize, height ); //left // then create the elements indices: int i; for(i=skirtIndex; i<(int)verts->size()-2; i+=2) addSkirtTriangles( i, i+2 ); addSkirtTriangles( i, skirtIndex ); } // create mask geometry if (maskSet) { osg::ref_ptr<osg::DrawElementsUInt> maskPrim = maskSet->createMaskPrimitives(mapInfo, verts, texCoords, normals, neighbors); if (maskPrim) geom->addPrimitiveSet( maskPrim ); } return geom; }
void RadialLineOfSightNode::compute_fill(osg::Node* node) { if ( !getMapNode() ) return; GeoPoint centerMap; _center.transform( getMapNode()->getMapSRS(), centerMap ); centerMap.toWorld( _centerWorld, getMapNode()->getTerrain() ); bool isProjected = getMapNode()->getMapSRS()->isProjected(); osg::Vec3d up = isProjected ? osg::Vec3d(0,0,1) : osg::Vec3d(_centerWorld); up.normalize(); //Get the "side" vector osg::Vec3d side = isProjected ? osg::Vec3d(1,0,0) : up ^ osg::Vec3d(0,0,1); //Get the number of spokes double delta = osg::PI * 2.0 / (double)_numSpokes; osg::Geometry* geometry = new osg::Geometry; geometry->setUseVertexBufferObjects(true); osg::Vec3Array* verts = new osg::Vec3Array(); verts->reserve(_numSpokes * 2); geometry->setVertexArray( verts ); osg::Vec4Array* colors = new osg::Vec4Array(); colors->reserve( _numSpokes * 2 ); geometry->setColorArray( colors ); geometry->setColorBinding(osg::Geometry::BIND_PER_VERTEX); osg::ref_ptr<osgUtil::IntersectorGroup> ivGroup = new osgUtil::IntersectorGroup(); for (unsigned int i = 0; i < (unsigned int)_numSpokes; i++) { double angle = delta * (double)i; osg::Quat quat(angle, up ); osg::Vec3d spoke = quat * (side * _radius); osg::Vec3d end = _centerWorld + spoke; osg::ref_ptr<DPLineSegmentIntersector> dplsi = new DPLineSegmentIntersector( _centerWorld, end ); ivGroup->addIntersector( dplsi.get() ); } osgUtil::IntersectionVisitor iv; iv.setIntersector( ivGroup.get() ); node->accept( iv ); for (unsigned int i = 0; i < (unsigned int)_numSpokes; i++) { //Get the current hit DPLineSegmentIntersector* los = dynamic_cast<DPLineSegmentIntersector*>(ivGroup->getIntersectors()[i].get()); DPLineSegmentIntersector::Intersections& hits = los->getIntersections(); osg::Vec3d currEnd = los->getEnd(); bool currHasLOS = hits.empty(); osg::Vec3d currHit = currHasLOS ? osg::Vec3d() : hits.begin()->getWorldIntersectPoint(); //Get the next hit unsigned int nextIndex = i + 1; if (nextIndex == _numSpokes) nextIndex = 0; DPLineSegmentIntersector* losNext = static_cast<DPLineSegmentIntersector*>(ivGroup->getIntersectors()[nextIndex].get()); DPLineSegmentIntersector::Intersections& hitsNext = losNext->getIntersections(); osg::Vec3d nextEnd = losNext->getEnd(); bool nextHasLOS = hitsNext.empty(); osg::Vec3d nextHit = nextHasLOS ? osg::Vec3d() : hitsNext.begin()->getWorldIntersectPoint(); if (currHasLOS && nextHasLOS) { //Both rays have LOS verts->push_back( _centerWorld - _centerWorld ); colors->push_back( _goodColor ); verts->push_back( nextEnd - _centerWorld ); colors->push_back( _goodColor ); verts->push_back( currEnd - _centerWorld ); colors->push_back( _goodColor ); } else if (!currHasLOS && !nextHasLOS) { //Both rays do NOT have LOS //Draw the "good triangle" verts->push_back( _centerWorld - _centerWorld ); colors->push_back( _goodColor ); verts->push_back( nextHit - _centerWorld ); colors->push_back( _goodColor ); verts->push_back( currHit - _centerWorld ); colors->push_back( _goodColor ); //Draw the two bad triangles verts->push_back( currHit - _centerWorld ); colors->push_back( _badColor ); verts->push_back( nextHit - _centerWorld ); colors->push_back( _badColor ); verts->push_back( nextEnd - _centerWorld ); colors->push_back( _badColor ); verts->push_back( currHit - _centerWorld ); colors->push_back( _badColor ); verts->push_back( nextEnd - _centerWorld ); colors->push_back( _badColor ); verts->push_back( currEnd - _centerWorld ); colors->push_back( _badColor ); } else if (!currHasLOS && nextHasLOS) { //Current does not have LOS but next does //Draw the good portion verts->push_back( _centerWorld - _centerWorld ); colors->push_back( _goodColor ); verts->push_back( nextEnd - _centerWorld ); colors->push_back( _goodColor ); verts->push_back( currHit - _centerWorld ); colors->push_back( _goodColor ); //Draw the bad portion verts->push_back( currHit - _centerWorld ); colors->push_back( _badColor ); verts->push_back( nextEnd - _centerWorld ); colors->push_back( _badColor ); verts->push_back( currEnd - _centerWorld ); colors->push_back( _badColor ); } else if (currHasLOS && !nextHasLOS) { //Current does not have LOS but next does //Draw the good portion verts->push_back( _centerWorld - _centerWorld ); colors->push_back( _goodColor ); verts->push_back( nextHit - _centerWorld ); colors->push_back( _goodColor ); verts->push_back( currEnd - _centerWorld ); colors->push_back( _goodColor ); //Draw the bad portion verts->push_back( nextHit - _centerWorld ); colors->push_back( _badColor ); verts->push_back( nextEnd - _centerWorld ); colors->push_back( _badColor ); verts->push_back( currEnd - _centerWorld ); colors->push_back( _badColor ); } } geometry->addPrimitiveSet(new osg::DrawArrays(GL_TRIANGLES, 0, verts->size())); osg::Geode* geode = new osg::Geode(); geode->addDrawable( geometry ); getOrCreateStateSet()->setMode(GL_LIGHTING, osg::StateAttribute::OFF); getOrCreateStateSet()->setMode(GL_BLEND, osg::StateAttribute::ON); osg::MatrixTransform* mt = new osg::MatrixTransform; mt->setMatrix(osg::Matrixd::translate(_centerWorld)); mt->addChild(geode); //Remove all the children removeChildren(0, getNumChildren()); addChild( mt ); for( LOSChangedCallbackList::iterator i = _changedCallbacks.begin(); i != _changedCallbacks.end(); i++ ) { i->get()->onChanged(); } }
void RadialLineOfSightNode::compute_line(osg::Node* node) { if ( !getMapNode() ) return; GeoPoint centerMap; _center.transform( getMapNode()->getMapSRS(), centerMap ); centerMap.toWorld( _centerWorld, getMapNode()->getTerrain() ); bool isProjected = getMapNode()->getMapSRS()->isProjected(); osg::Vec3d up = isProjected ? osg::Vec3d(0,0,1) : osg::Vec3d(_centerWorld); up.normalize(); //Get the "side" vector osg::Vec3d side = isProjected ? osg::Vec3d(1,0,0) : up ^ osg::Vec3d(0,0,1); //Get the number of spokes double delta = osg::PI * 2.0 / (double)_numSpokes; osg::Geometry* geometry = new osg::Geometry; geometry->setUseVertexBufferObjects(true); osg::Vec3Array* verts = new osg::Vec3Array(); verts->reserve(_numSpokes * 5); geometry->setVertexArray( verts ); osg::Vec4Array* colors = new osg::Vec4Array(); colors->reserve( _numSpokes * 5 ); geometry->setColorArray( colors ); geometry->setColorBinding(osg::Geometry::BIND_PER_VERTEX); osg::Vec3d previousEnd; osg::Vec3d firstEnd; osg::ref_ptr<osgUtil::IntersectorGroup> ivGroup = new osgUtil::IntersectorGroup(); for (unsigned int i = 0; i < (unsigned int)_numSpokes; i++) { double angle = delta * (double)i; osg::Quat quat(angle, up ); osg::Vec3d spoke = quat * (side * _radius); osg::Vec3d end = _centerWorld + spoke; osg::ref_ptr<DPLineSegmentIntersector> dplsi = new DPLineSegmentIntersector( _centerWorld, end ); ivGroup->addIntersector( dplsi.get() ); } osgUtil::IntersectionVisitor iv; iv.setIntersector( ivGroup.get() ); node->accept( iv ); for (unsigned int i = 0; i < (unsigned int)_numSpokes; i++) { DPLineSegmentIntersector* los = dynamic_cast<DPLineSegmentIntersector*>(ivGroup->getIntersectors()[i].get()); DPLineSegmentIntersector::Intersections& hits = los->getIntersections(); osg::Vec3d start = los->getStart(); osg::Vec3d end = los->getEnd(); osg::Vec3d hit; bool hasLOS = hits.empty(); if (!hasLOS) { hit = hits.begin()->getWorldIntersectPoint(); } if (hasLOS) { verts->push_back( start - _centerWorld ); verts->push_back( end - _centerWorld ); colors->push_back( _goodColor ); colors->push_back( _goodColor ); } else { if (_displayMode == LineOfSight::MODE_SPLIT) { verts->push_back( start - _centerWorld ); verts->push_back( hit - _centerWorld ); colors->push_back( _goodColor ); colors->push_back( _goodColor ); verts->push_back( hit - _centerWorld ); verts->push_back( end - _centerWorld ); colors->push_back( _badColor ); colors->push_back( _badColor ); } else if (_displayMode == LineOfSight::MODE_SINGLE) { verts->push_back( start - _centerWorld ); verts->push_back( end - _centerWorld ); colors->push_back( _badColor ); colors->push_back( _badColor ); } } if (i > 0) { verts->push_back( end - _centerWorld ); verts->push_back( previousEnd - _centerWorld ); colors->push_back( _outlineColor ); colors->push_back( _outlineColor ); } else { firstEnd = end; } previousEnd = end; } //Add the last outside of circle verts->push_back( firstEnd - _centerWorld ); verts->push_back( previousEnd - _centerWorld ); colors->push_back( osg::Vec4(1,1,1,1)); colors->push_back( osg::Vec4(1,1,1,1)); geometry->addPrimitiveSet(new osg::DrawArrays(GL_LINES, 0, verts->size())); osg::Geode* geode = new osg::Geode(); geode->addDrawable( geometry ); getOrCreateStateSet()->setMode(GL_LIGHTING, osg::StateAttribute::OFF); osg::MatrixTransform* mt = new osg::MatrixTransform; mt->setMatrix(osg::Matrixd::translate(_centerWorld)); mt->addChild(geode); //Remove all the children removeChildren(0, getNumChildren()); addChild( mt ); for( LOSChangedCallbackList::iterator i = _changedCallbacks.begin(); i != _changedCallbacks.end(); i++ ) { i->get()->onChanged(); } }
bool ElevationQuery::getElevationImpl(const GeoPoint& point, float& out_elevation, double desiredResolution, double* out_actualResolution) { // assertion. if ( !point.isAbsolute() ) { OE_WARN << LC << "Assertion failure; input must be absolute" << std::endl; return false; } osg::Timer_t begin = osg::Timer::instance()->tick(); // first try the terrain patches. if ( _terrainModelLayers.size() > 0 ) { osgUtil::IntersectionVisitor iv; if ( _ivrc.valid() ) iv.setReadCallback(_ivrc.get()); for(LayerVector::iterator i = _terrainModelLayers.begin(); i != _terrainModelLayers.end(); ++i) { // find the scene graph for this layer: Layer* layer = i->get(); osg::Node* node = layer->getNode(); if ( node ) { // configure for intersection: osg::Vec3d surface; point.toWorld( surface ); // trivial bounds check: if ( node->getBound().contains(surface) ) { osg::Vec3d nvector; point.createWorldUpVector(nvector); osg::Vec3d start( surface + nvector*5e5 ); osg::Vec3d end ( surface - nvector*5e5 ); // first time through, set up the intersector on demand if ( !_lsi.valid() ) { _lsi = new osgUtil::LineSegmentIntersector(start, end); _lsi->setIntersectionLimit( _lsi->LIMIT_NEAREST ); } else { _lsi->reset(); _lsi->setStart( start ); _lsi->setEnd ( end ); } // try it. iv.setIntersector( _lsi.get() ); node->accept( iv ); // check for a result!! if ( _lsi->containsIntersections() ) { osg::Vec3d isect = _lsi->getIntersections().begin()->getWorldIntersectPoint(); // transform back to input SRS: GeoPoint output; output.fromWorld( point.getSRS(), isect ); out_elevation = (float)output.z(); if ( out_actualResolution ) *out_actualResolution = 0.0; return true; } } else { //OE_INFO << LC << "Trivial rejection (bounds check)" << std::endl; } } } } if (_elevationLayers.empty()) { // this means there are no heightfields. out_elevation = NO_DATA_VALUE; return true; } // secure map pointer: osg::ref_ptr<const Map> map; if (!_map.lock(map)) { return false; } // tile size (resolution of elevation tiles) unsigned tileSize = 257; // yes? // default LOD: unsigned lod = 23u; // attempt to map the requested resolution to an LOD: if (desiredResolution > 0.0) { int level = map->getProfile()->getLevelOfDetailForHorizResolution(desiredResolution, tileSize); if ( level > 0 ) lod = level; } // do we need a new ElevationEnvelope? if (!_envelope.valid() || !point.getSRS()->isHorizEquivalentTo(_envelope->getSRS()) || lod != _envelope->getLOD()) { _envelope = map->getElevationPool()->createEnvelope(point.getSRS(), lod); } // sample the elevation, and if requested, the resolution as well: if (out_actualResolution) { std::pair<float, float> result = _envelope->getElevationAndResolution(point.x(), point.y()); out_elevation = result.first; *out_actualResolution = result.second; } else { out_elevation = _envelope->getElevation(point.x(), point.y()); } return out_elevation != NO_DATA_VALUE; }
void PolyhedralLineOfSightNode::updateSamples() { if ( _geode->getNumDrawables() == 0 ) rebuildGeometry(); osg::Geometry* geom = _geode->getDrawable(0)->asGeometry(); osg::Vec3Array* verts = dynamic_cast<osg::Vec3Array*>( geom->getVertexArray() ); double distance = _distance.as(Units::METERS); // get the world coords and a l2w transform for the origin: osg::Vec3d originWorld; osg::Matrix local2world, world2local; Terrain* t = getMapNode()->getTerrain(); GeoPoint origin = getPosition(); origin.makeAbsolute( t ); origin.toWorld( originWorld, t ); origin.createLocalToWorld( local2world ); world2local.invert( local2world ); // set up an intersector: osgUtil::LineSegmentIntersector* lsi = new osgUtil::LineSegmentIntersector( originWorld, originWorld ); osgUtil::IntersectionVisitor iv( lsi ); // intersect the verts (skip the origin point) with the map node. for( osg::Vec3Array::iterator v = verts->begin()+1; v != verts->end(); ++v ) { osg::Vec3d unit = *v; unit.normalize(); unit *= distance; osg::Vec3d world = unit * local2world; if ( osg::equivalent(unit.length(), 0.0) ) { OE_WARN << "problem." << std::endl; } lsi->reset(); lsi->setStart( originWorld ); lsi->setEnd( world ); OE_DEBUG << LC << "Ray: " << originWorld.x() << "," << originWorld.y() << "," << originWorld.z() << " => " << world.x() << "," << world.y() << "," << world.z() << std::endl; getMapNode()->getTerrain()->accept( iv ); osgUtil::LineSegmentIntersector::Intersections& hits = lsi->getIntersections(); if ( !hits.empty() ) { const osgUtil::LineSegmentIntersector::Intersection& hit = *hits.begin(); osg::Vec3d newV = hit.getWorldIntersectPoint() * world2local; if ( newV.length() > 1.0 ) *v = newV; else *v = unit; } else { *v = unit; } //OE_NOTICE << "Radial point = " << v->x() << ", " << v->y() << ", " << v->z() << std::endl; } verts->dirty(); geom->dirtyBound(); }
osg::Geometry* GeometryPool::createGeometry(const TileKey& tileKey, const MapInfo& mapInfo, MaskGenerator* maskSet) const { // Establish a local reference frame for the tile: osg::Vec3d centerWorld; GeoPoint centroid; tileKey.getExtent().getCentroid( centroid ); centroid.toWorld( centerWorld ); osg::Matrix world2local, local2world; centroid.createWorldToLocal( world2local ); local2world.invert( world2local ); // Attempt to calculate the number of verts in the surface geometry. bool createSkirt = _options.heightFieldSkirtRatio() > 0.0f; unsigned numVertsInSurface = (_tileSize*_tileSize); unsigned numVertsInSkirt = createSkirt ? _tileSize*4u - 4u : 0; unsigned numVerts = numVertsInSurface + numVertsInSkirt; unsigned numIndiciesInSurface = (_tileSize-1) * (_tileSize-1) * 6; unsigned numIncidesInSkirt = createSkirt ? (_tileSize-1) * 4 * 6 : 0; GLenum mode = (_options.gpuTessellation() == true) ? GL_PATCHES : GL_TRIANGLES; // Pre-allocate enough space for all triangles. osg::DrawElements* primSet = new osg::DrawElementsUShort(mode); primSet->reserveElements(numIndiciesInSurface + numIncidesInSkirt); osg::BoundingSphere tileBound; // the geometry: osg::Geometry* geom = new osg::Geometry(); geom->setUseVertexBufferObjects(true); geom->setUseDisplayList(false); geom->addPrimitiveSet( primSet ); // the vertex locations: osg::Vec3Array* verts = new osg::Vec3Array(); verts->reserve( numVerts ); geom->setVertexArray( verts ); // the surface normals (i.e. extrusion vectors) osg::Vec3Array* normals = new osg::Vec3Array(); normals->reserve( numVerts ); geom->setNormalArray( normals ); geom->setNormalBinding( geom->BIND_PER_VERTEX ); // neighbor positions (for morphing) osg::Vec3Array* neighbors = new osg::Vec3Array(); neighbors->reserve( numVerts ); geom->setTexCoordArray( 1, neighbors ); // tex coord is [0..1] across the tile. The 3rd dimension tracks whether the // vert is masked: 0=yes, 1=no #ifdef SHARE_TEX_COORDS bool populateTexCoords = false; if ( !_sharedTexCoords.valid() ) { _sharedTexCoords = new osg::Vec3Array(); _sharedTexCoords->reserve( numVerts ); populateTexCoords = true; } osg::Vec3Array* texCoords = _sharedTexCoords.get(); #else bool populateTexCoords = true; osg::Vec3Array* texCoords = new osg::Vec3Array(); texCoords->reserve( numVerts ); #endif geom->setTexCoordArray( 0, texCoords ); float delta = 1.0/(_tileSize-1); osg::Vec3d tdelta(delta,0,0); tdelta.normalize(); osg::Vec3d vZero(0,0,0); osg::ref_ptr<GeoLocator> locator = GeoLocator::createForKey( tileKey, mapInfo ); for(unsigned row=0; row<_tileSize; ++row) { float ny = (float)row/(float)(_tileSize-1); for(unsigned col=0; col<_tileSize; ++col) { float nx = (float)col/(float)(_tileSize-1); osg::Vec3d model; locator->unitToModel(osg::Vec3d(nx, ny, 0.0f), model); osg::Vec3d modelLTP = model*world2local; verts->push_back( modelLTP ); tileBound.expandBy( verts->back() ); // no masking in the geometry pool, so always write a z=1.0 -gw //bool masked = _maskSet.contains(nx, ny); if ( populateTexCoords ) { texCoords->push_back( osg::Vec3f(nx, ny, 1.0f) ); } osg::Vec3d modelPlusOne; locator->unitToModel(osg::Vec3d(nx, ny, 1.0f), modelPlusOne); osg::Vec3d normal = (modelPlusOne*world2local)-modelLTP; normal.normalize(); normals->push_back( normal ); // neighbor: osg::Vec3d modelNeighborLTP = (*verts)[verts->size() - getMorphNeighborIndexOffset(col, row, _tileSize)]; neighbors->push_back(modelNeighborLTP); } } // Now tessellate the surface. // TODO: do we really need this?? bool swapOrientation = !locator->orientationOpenGL(); for(unsigned j=0; j<_tileSize-1; ++j) { for(unsigned i=0; i<_tileSize-1; ++i) { int i00; int i01; if (swapOrientation) { i01 = j*_tileSize + i; i00 = i01+_tileSize; } else { i00 = j*_tileSize + i; i01 = i00+_tileSize; } int i10 = i00+1; int i11 = i01+1; // If the quad does not intersect a mask, tessellate it; otherwise skip it // since the mask generator will take care of it. bool addTris = true; if ( maskSet ) { addTris = !maskSet->isMasked( (*texCoords)[i00] ) && !maskSet->isMasked( (*texCoords)[i01] ) && !maskSet->isMasked( (*texCoords)[i10] ) && !maskSet->isMasked( (*texCoords)[i11] ) && !maskSet->containedByQuadAtColRow( i, j, _tileSize ); } if ( addTris ) { primSet->addElement(i01); primSet->addElement(i00); primSet->addElement(i11); primSet->addElement(i00); primSet->addElement(i10); primSet->addElement(i11); } } } if ( createSkirt ) { // SKIRTS: // calculate the skirt extrusion height double height = tileBound.radius() * _options.heightFieldSkirtRatio().get(); unsigned skirtIndex = verts->size(); // first, create all the skirt verts, normals, and texcoords. for(int c=0; c<(int)_tileSize-1; ++c) addSkirtDataForIndex( c, height ); //top for(int r=0; r<(int)_tileSize-1; ++r) addSkirtDataForIndex( r*_tileSize+(_tileSize-1), height ); //right for(int c=_tileSize-1; c>=0; --c) addSkirtDataForIndex( (_tileSize-1)*_tileSize+c, height ); //bottom for(int r=_tileSize-1; r>=0; --r) addSkirtDataForIndex( r*_tileSize, height ); //left // then create the elements indices: int i; for(i=skirtIndex; i<(int)verts->size()-2; i+=2) addSkirtTriangles( i, i+2 ); addSkirtTriangles( i, skirtIndex ); } #if 0 // if we're using patches, we must create a "proxy" primitive set that supports // PrimitiveFunctor et al (for intersections, bounds testing, etc.) if ( mode == GL_PATCHES ) { osg::PrimitiveSet* patchesAsTriangles = osg::clone( primSet, osg::CopyOp::SHALLOW_COPY ); patchesAsTriangles->setMode( GL_TRIANGLES ); geom->setPatchTriangles( patchesAsTriangles ); } #endif return geom; }
bool ElevationQuery::getElevationImpl(const GeoPoint& point, /* abs */ double& out_elevation, double desiredResolution, double* out_actualResolution) { // assertion. if ( !point.isAbsolute() ) { OE_WARN << LC << "Assertion failure; input must be absolute" << std::endl; return false; } osg::Timer_t begin = osg::Timer::instance()->tick(); // first try the terrain patches. if ( _patchLayers.size() > 0 ) { osgUtil::IntersectionVisitor iv; for(std::vector<ModelLayer*>::iterator i = _patchLayers.begin(); i != _patchLayers.end(); ++i) { // find the scene graph for this layer: osg::Node* node = (*i)->getSceneGraph( _mapf.getUID() ); if ( node ) { // configure for intersection: osg::Vec3d surface; point.toWorld( surface ); // trivial bounds check: if ( node->getBound().contains(surface) ) { osg::Vec3d nvector; point.createWorldUpVector(nvector); osg::Vec3d start( surface + nvector*5e5 ); osg::Vec3d end ( surface - nvector*5e5 ); // first time through, set up the intersector on demand if ( !_patchLayersLSI.valid() ) { _patchLayersLSI = new DPLineSegmentIntersector(start, end); _patchLayersLSI->setIntersectionLimit( _patchLayersLSI->LIMIT_NEAREST ); } else { _patchLayersLSI->reset(); _patchLayersLSI->setStart( start ); _patchLayersLSI->setEnd ( end ); } // try it. iv.setIntersector( _patchLayersLSI.get() ); node->accept( iv ); // check for a result!! if ( _patchLayersLSI->containsIntersections() ) { osg::Vec3d isect = _patchLayersLSI->getIntersections().begin()->getWorldIntersectPoint(); // transform back to input SRS: GeoPoint output; output.fromWorld( point.getSRS(), isect ); out_elevation = output.z(); if ( out_actualResolution ) *out_actualResolution = 0.0; return true; } } else { //OE_INFO << LC << "Trivial rejection (bounds check)" << std::endl; } } } } if ( _mapf.elevationLayers().empty() ) { // this means there are no heightfields. out_elevation = 0.0; return true; } // tile size (resolution of elevation tiles) unsigned tileSize = std::max(_mapf.getMapOptions().elevationTileSize().get(), 2u); //This is the max resolution that we actually have data at this point unsigned int bestAvailLevel = getMaxLevel( point.x(), point.y(), point.getSRS(), _mapf.getProfile()); if (desiredResolution > 0.0) { unsigned int desiredLevel = _mapf.getProfile()->getLevelOfDetailForHorizResolution( desiredResolution, tileSize ); if (desiredLevel < bestAvailLevel) bestAvailLevel = desiredLevel; } OE_DEBUG << LC << "Best available data level " << point.x() << ", " << point.y() << " = " << bestAvailLevel << std::endl; // transform the input coords to map coords: GeoPoint mapPoint = point; if ( point.isValid() && !point.getSRS()->isHorizEquivalentTo( _mapf.getProfile()->getSRS() ) ) { mapPoint = point.transform(_mapf.getProfile()->getSRS()); if ( !mapPoint.isValid() ) { OE_WARN << LC << "Fail: coord transform failed" << std::endl; return false; } } // get the tilekey corresponding to the tile we need: TileKey key = _mapf.getProfile()->createTileKey( mapPoint.x(), mapPoint.y(), bestAvailLevel ); if ( !key.valid() ) { OE_WARN << LC << "Fail: coords fall outside map" << std::endl; return false; } bool result = false; while (!result) { GeoHeightField geoHF; TileCache::Record record; // Try to get the hf from the cache if ( _cache.get( key, record ) ) { geoHF = record.value(); } else { // Create it osg::ref_ptr<osg::HeightField> hf = new osg::HeightField(); hf->allocate( tileSize, tileSize ); // Initialize the heightfield to nodata for (unsigned int i = 0; i < hf->getFloatArray()->size(); i++) { hf->getFloatArray()->at( i ) = NO_DATA_VALUE; } if (_mapf.populateHeightField(hf, key, false)) { geoHF = GeoHeightField( hf.get(), key.getExtent() ); _cache.insert( key, geoHF ); } } if (geoHF.valid()) { float elevation = 0.0f; result = geoHF.getElevation( mapPoint.getSRS(), mapPoint.x(), mapPoint.y(), _mapf.getMapInfo().getElevationInterpolation(), mapPoint.getSRS(), elevation); if (result && elevation != NO_DATA_VALUE) { // see what the actual resolution of the heightfield is. if ( out_actualResolution ) *out_actualResolution = geoHF.getXInterval(); out_elevation = (double)elevation; break; } else { result = false; } } if (!result) { key = key.createParentKey(); if (!key.valid()) { break; } } } osg::Timer_t end = osg::Timer::instance()->tick(); _queries++; _totalTime += osg::Timer::instance()->delta_s( begin, end ); return result; }