[3998] | 1 | /* -*- mode: c++; c-basic-offset: 4; indent-tabs-mode: nil -*- */ |
---|
| 2 | /* |
---|
| 3 | * Copyright (C) 2004-2013 HUBzero Foundation, LLC |
---|
| 4 | * |
---|
| 5 | * Author: Leif Delgass <ldelgass@purdue.edu> |
---|
| 6 | */ |
---|
| 7 | |
---|
| 8 | #include <cfloat> |
---|
| 9 | #include <cstring> |
---|
| 10 | #include <cassert> |
---|
| 11 | #include <cmath> |
---|
[4305] | 12 | #include <cstdlib> |
---|
[3998] | 13 | |
---|
[4636] | 14 | #include <limits> |
---|
[4632] | 15 | #include <set> |
---|
| 16 | |
---|
[4382] | 17 | #include <sys/types.h> |
---|
| 18 | #include <unistd.h> // For getpid() |
---|
| 19 | |
---|
[3998] | 20 | #include <GL/gl.h> |
---|
| 21 | |
---|
| 22 | #ifdef WANT_TRACE |
---|
| 23 | #include <sys/time.h> |
---|
| 24 | #endif |
---|
| 25 | |
---|
[4645] | 26 | #include <osgDB/WriteFile> |
---|
[4382] | 27 | #include <osgDB/FileUtils> |
---|
| 28 | #include <osgDB/FileNameUtils> |
---|
[4028] | 29 | #include <osgGA/StateSetManipulator> |
---|
[4305] | 30 | #include <osgGA/GUIEventAdapter> |
---|
[4320] | 31 | #include <osgViewer/ViewerEventHandlers> |
---|
[4028] | 32 | |
---|
[4009] | 33 | #include <osgEarth/Version> |
---|
[4382] | 34 | #include <osgEarth/FileUtils> |
---|
| 35 | #include <osgEarth/Cache> |
---|
| 36 | #include <osgEarthDrivers/cache_filesystem/FileSystemCache> |
---|
[4009] | 37 | #include <osgEarth/MapNode> |
---|
[4246] | 38 | #include <osgEarth/Bounds> |
---|
| 39 | #include <osgEarth/Profile> |
---|
[4273] | 40 | #include <osgEarth/Viewpoint> |
---|
[4574] | 41 | #include <osgEarth/GeoMath> |
---|
[4009] | 42 | #include <osgEarth/TerrainLayer> |
---|
| 43 | #include <osgEarth/ImageLayer> |
---|
[4013] | 44 | #include <osgEarth/ElevationLayer> |
---|
| 45 | #include <osgEarth/ModelLayer> |
---|
[4382] | 46 | #include <osgEarth/DateTime> |
---|
[4632] | 47 | #include <osgEarth/Pickers> |
---|
[4645] | 48 | #include <osgEarthFeatures/Feature> |
---|
| 49 | #include <osgEarthSymbology/Color> |
---|
| 50 | #include <osgEarthSymbology/Geometry> |
---|
| 51 | #include <osgEarthSymbology/Style> |
---|
| 52 | #include <osgEarthSymbology/StyleSheet> |
---|
| 53 | #include <osgEarthSymbology/IconSymbol> |
---|
| 54 | #include <osgEarthSymbology/LineSymbol> |
---|
| 55 | |
---|
[4632] | 56 | #include <osgEarthAnnotation/AnnotationNode> |
---|
[4645] | 57 | #include <osgEarthAnnotation/FeatureNode> |
---|
[4632] | 58 | #include <osgEarthAnnotation/PlaceNode> |
---|
| 59 | #include <osgEarthAnnotation/HighlightDecoration> |
---|
| 60 | #include <osgEarthAnnotation/ScaleDecoration> |
---|
[4009] | 61 | #include <osgEarthUtil/EarthManipulator> |
---|
[4643] | 62 | #if OSGEARTH_MIN_VERSION_REQUIRED(2, 5, 1) |
---|
[4282] | 63 | #include <osgEarthUtil/Sky> |
---|
[4643] | 64 | #include <osgEarthDrivers/sky_simple/SimpleSkyOptions> |
---|
[4282] | 65 | #else |
---|
| 66 | #include <osgEarthUtil/SkyNode> |
---|
| 67 | #endif |
---|
[4036] | 68 | #include <osgEarthUtil/AutoClipPlaneHandler> |
---|
[4014] | 69 | #include <osgEarthUtil/MouseCoordsTool> |
---|
[4310] | 70 | #include <osgEarthUtil/UTMGraticule> |
---|
| 71 | #include <osgEarthUtil/MGRSGraticule> |
---|
| 72 | #include <osgEarthUtil/GeodeticGraticule> |
---|
[4307] | 73 | #include <osgEarthUtil/LatLongFormatter> |
---|
[4349] | 74 | #include <osgEarthUtil/MGRSFormatter> |
---|
[4246] | 75 | #include <osgEarthUtil/GLSLColorFilter> |
---|
[4285] | 76 | #include <osgEarthUtil/VerticalScale> |
---|
[4085] | 77 | #include <osgEarthDrivers/gdal/GDALOptions> |
---|
[4246] | 78 | #include <osgEarthDrivers/engine_mp/MPTerrainEngineOptions> |
---|
[4009] | 79 | |
---|
[3998] | 80 | #include "Renderer.h" |
---|
[4382] | 81 | #if 0 |
---|
| 82 | #include "SingleWindow.h" |
---|
| 83 | #endif |
---|
[4349] | 84 | #include "ScaleBar.h" |
---|
[4382] | 85 | #include "FileUtil.h" |
---|
[3998] | 86 | #include "Trace.h" |
---|
| 87 | |
---|
| 88 | #define MSECS_ELAPSED(t1, t2) \ |
---|
| 89 | ((t1).tv_sec == (t2).tv_sec ? (((t2).tv_usec - (t1).tv_usec)/1.0e+3) : \ |
---|
| 90 | (((t2).tv_sec - (t1).tv_sec))*1.0e+3 + (double)((t2).tv_usec - (t1).tv_usec)/1.0e+3) |
---|
| 91 | |
---|
[4635] | 92 | #define BASE_IMAGE "world.tif" |
---|
| 93 | #define PIN_ICON "placemark32.png" |
---|
[4246] | 94 | |
---|
[3998] | 95 | using namespace GeoVis; |
---|
| 96 | |
---|
| 97 | Renderer::Renderer() : |
---|
[4293] | 98 | _needsRedraw(false), |
---|
[3998] | 99 | _windowWidth(500), |
---|
[4349] | 100 | _windowHeight(500), |
---|
| 101 | _scaleBarUnits(UNITS_METERS) |
---|
[3998] | 102 | { |
---|
[4354] | 103 | TRACE("Enter"); |
---|
| 104 | |
---|
[3998] | 105 | _bgColor[0] = 0; |
---|
| 106 | _bgColor[1] = 0; |
---|
| 107 | _bgColor[2] = 0; |
---|
[4628] | 108 | //setMaximumFrameRateInHertz(15.0); |
---|
[4632] | 109 | // 100 Mbps |
---|
[4628] | 110 | setMaximumBitrate(1.0e8); |
---|
[4025] | 111 | _lastFrameTime = _minFrameTime; |
---|
[4629] | 112 | TRACE("Bandwidth target: %.2f Mbps", (float)(getMaximumBitrate()/1.0e6)); |
---|
[4628] | 113 | TRACE("Frame rate target: %.2f Hz", (float)getMaximumFrameRateInHertz()); |
---|
[4632] | 114 | TRACE("Frame time target: %.2f msec", _minFrameTime * 1000.0f); |
---|
[4293] | 115 | |
---|
[4305] | 116 | char *base = getenv("MAP_BASE_URI"); |
---|
| 117 | if (base != NULL) { |
---|
| 118 | _baseURI = base; |
---|
| 119 | TRACE("Setting base URI: %s", _baseURI.c_str()); |
---|
| 120 | } |
---|
[3998] | 121 | } |
---|
| 122 | |
---|
[4028] | 123 | osgGA::EventQueue *Renderer::getEventQueue() |
---|
| 124 | { |
---|
[4293] | 125 | if (_viewer.valid()) { |
---|
| 126 | osgViewer::ViewerBase::Windows windows; |
---|
| 127 | _viewer->getWindows(windows); |
---|
[4320] | 128 | if (windows.size() > 0) { |
---|
| 129 | return windows[0]->getEventQueue(); |
---|
| 130 | } |
---|
[4293] | 131 | } |
---|
[4320] | 132 | return NULL; |
---|
[4028] | 133 | } |
---|
| 134 | |
---|
[3998] | 135 | Renderer::~Renderer() |
---|
| 136 | { |
---|
| 137 | TRACE("Enter"); |
---|
| 138 | |
---|
[4382] | 139 | removeDirectory(_cacheDir.c_str()); |
---|
| 140 | |
---|
[3998] | 141 | TRACE("Leave"); |
---|
| 142 | } |
---|
| 143 | |
---|
[4635] | 144 | std::string Renderer::getBaseImage() |
---|
| 145 | { |
---|
| 146 | std::ostringstream oss; |
---|
| 147 | oss << _resourcePath << "/" << BASE_IMAGE; |
---|
| 148 | return oss.str(); |
---|
| 149 | } |
---|
| 150 | |
---|
| 151 | std::string Renderer::getPinIcon() |
---|
| 152 | { |
---|
| 153 | std::ostringstream oss; |
---|
| 154 | oss << _resourcePath << "/" << PIN_ICON; |
---|
| 155 | return oss.str(); |
---|
| 156 | } |
---|
| 157 | |
---|
[4382] | 158 | void Renderer::setupCache() |
---|
| 159 | { |
---|
| 160 | std::ostringstream dir; |
---|
| 161 | dir << "/tmp/geovis_cache" << getpid(); |
---|
| 162 | _cacheDir = dir.str(); |
---|
| 163 | const char *path = _cacheDir.c_str(); |
---|
| 164 | TRACE("Cache dir: %s", path); |
---|
| 165 | removeDirectory(path); |
---|
| 166 | if (!osgDB::makeDirectory(_cacheDir)) { |
---|
| 167 | ERROR("Failed to create directory '%s'", path); |
---|
| 168 | } |
---|
| 169 | } |
---|
| 170 | |
---|
[4246] | 171 | void Renderer::initColorMaps() |
---|
| 172 | { |
---|
[4293] | 173 | if (!_colorMaps.empty()) |
---|
| 174 | return; |
---|
| 175 | |
---|
[4246] | 176 | osg::TransferFunction1D *defaultColorMap = new osg::TransferFunction1D; |
---|
| 177 | defaultColorMap->allocate(256); |
---|
| 178 | defaultColorMap->setColor(0.00, osg::Vec4f(0,0,1,1), false); |
---|
| 179 | defaultColorMap->setColor(0.25, osg::Vec4f(0,1,1,1), false); |
---|
| 180 | defaultColorMap->setColor(0.50, osg::Vec4f(0,1,0,1), false); |
---|
| 181 | defaultColorMap->setColor(0.75, osg::Vec4f(1,1,0,1), false); |
---|
| 182 | defaultColorMap->setColor(1.00, osg::Vec4f(1,0,0,1), false); |
---|
| 183 | defaultColorMap->updateImage(); |
---|
| 184 | addColorMap("default", defaultColorMap); |
---|
| 185 | osg::TransferFunction1D *defaultGrayColorMap = new osg::TransferFunction1D; |
---|
| 186 | defaultGrayColorMap->allocate(256); |
---|
| 187 | defaultGrayColorMap->setColor(0, osg::Vec4f(0,0,0,1), false); |
---|
| 188 | defaultGrayColorMap->setColor(1, osg::Vec4f(1,1,1,1), false); |
---|
| 189 | defaultGrayColorMap->updateImage(); |
---|
| 190 | addColorMap("grayDefault", defaultGrayColorMap); |
---|
| 191 | } |
---|
| 192 | |
---|
| 193 | void Renderer::addColorMap(const ColorMapId& id, osg::TransferFunction1D *xfer) |
---|
| 194 | { |
---|
| 195 | _colorMaps[id] = xfer; |
---|
| 196 | } |
---|
| 197 | |
---|
| 198 | void Renderer::deleteColorMap(const ColorMapId& id) |
---|
| 199 | { |
---|
| 200 | ColorMapHashmap::iterator itr; |
---|
| 201 | bool doAll = false; |
---|
| 202 | |
---|
| 203 | if (id.compare("all") == 0) { |
---|
| 204 | itr = _colorMaps.begin(); |
---|
| 205 | doAll = true; |
---|
| 206 | } else { |
---|
| 207 | itr = _colorMaps.find(id); |
---|
| 208 | } |
---|
| 209 | |
---|
| 210 | if (itr == _colorMaps.end()) { |
---|
| 211 | ERROR("Unknown ColorMap %s", id.c_str()); |
---|
| 212 | return; |
---|
| 213 | } |
---|
| 214 | |
---|
| 215 | do { |
---|
| 216 | if (itr->first.compare("default") == 0 || |
---|
| 217 | itr->first.compare("grayDefault") == 0) { |
---|
| 218 | if (id.compare("all") != 0) { |
---|
| 219 | WARN("Cannot delete a default color map"); |
---|
| 220 | } |
---|
| 221 | continue; |
---|
| 222 | } |
---|
| 223 | |
---|
| 224 | TRACE("Deleting ColorMap %s", itr->first.c_str()); |
---|
| 225 | itr = _colorMaps.erase(itr); |
---|
| 226 | } while (doAll && itr != _colorMaps.end()); |
---|
| 227 | } |
---|
| 228 | |
---|
| 229 | void Renderer::setColorMapNumberOfTableEntries(const ColorMapId& id, |
---|
| 230 | int numEntries) |
---|
| 231 | { |
---|
| 232 | ColorMapHashmap::iterator itr; |
---|
| 233 | bool doAll = false; |
---|
| 234 | |
---|
| 235 | if (id.compare("all") == 0) { |
---|
| 236 | itr = _colorMaps.begin(); |
---|
| 237 | doAll = true; |
---|
| 238 | } else { |
---|
| 239 | itr = _colorMaps.find(id); |
---|
| 240 | } |
---|
| 241 | |
---|
| 242 | if (itr == _colorMaps.end()) { |
---|
| 243 | ERROR("Unknown ColorMap %s", id.c_str()); |
---|
| 244 | return; |
---|
| 245 | } |
---|
| 246 | |
---|
| 247 | do { |
---|
| 248 | itr->second->allocate(numEntries); |
---|
| 249 | itr->second->updateImage(); |
---|
| 250 | } while (doAll && ++itr != _colorMaps.end()); |
---|
| 251 | } |
---|
| 252 | |
---|
[4293] | 253 | void Renderer::initViewer() { |
---|
| 254 | if (_viewer.valid()) |
---|
| 255 | return; |
---|
| 256 | _viewer = new osgViewer::Viewer(); |
---|
[4382] | 257 | #if 1 |
---|
| 258 | osg::DisplaySettings *ds = _viewer->getDisplaySettings(); |
---|
| 259 | if (ds == NULL) { |
---|
| 260 | ds = osg::DisplaySettings::instance().get(); |
---|
| 261 | } |
---|
| 262 | ds->setDoubleBuffer(false); |
---|
| 263 | ds->setMinimumNumAlphaBits(8); |
---|
| 264 | ds->setMinimumNumStencilBits(8); |
---|
| 265 | ds->setNumMultiSamples(0); |
---|
| 266 | #endif |
---|
[4293] | 267 | _viewer->setThreadingModel(osgViewer::ViewerBase::SingleThreaded); |
---|
| 268 | _viewer->getDatabasePager()->setUnrefImageDataAfterApplyPolicy(false, false); |
---|
| 269 | _viewer->setReleaseContextAtEndOfFrameHint(false); |
---|
[4307] | 270 | //_viewer->setLightingMode(osg::View::SKY_LIGHT); |
---|
[4293] | 271 | _viewer->getCamera()->setClearColor(osg::Vec4(_bgColor[0], _bgColor[1], _bgColor[2], 1)); |
---|
[4320] | 272 | _viewer->getCamera()->setClearMask(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT); |
---|
[4293] | 273 | _viewer->getCamera()->setNearFarRatio(0.00002); |
---|
| 274 | _viewer->getCamera()->setSmallFeatureCullingPixelSize(-1.0f); |
---|
| 275 | _stateManip = new osgGA::StateSetManipulator(_viewer->getCamera()->getOrCreateStateSet()); |
---|
| 276 | _viewer->addEventHandler(_stateManip); |
---|
[4320] | 277 | //_viewer->addEventHandler(new osgViewer::StatsHandler()); |
---|
[4635] | 278 | |
---|
| 279 | #ifdef DEBUG |
---|
| 280 | if (_viewer->getViewerStats() != NULL) { |
---|
| 281 | TRACE("Enabling stats"); |
---|
| 282 | _viewer->getViewerStats()->collectStats("scene", true); |
---|
| 283 | } |
---|
| 284 | #endif |
---|
| 285 | #if 0 |
---|
| 286 | osgViewer::ViewerBase::Windows windows; |
---|
| 287 | _viewer->getWindows(windows); |
---|
| 288 | if (windows.size() == 1) { |
---|
| 289 | windows[0]->setSyncToVBlank(false); |
---|
| 290 | } else { |
---|
| 291 | ERROR("Num windows: %lu", windows.size()); |
---|
| 292 | } |
---|
| 293 | #endif |
---|
[4293] | 294 | } |
---|
| 295 | |
---|
| 296 | void Renderer::finalizeViewer() { |
---|
| 297 | initViewer(); |
---|
[4382] | 298 | TRACE("Before _viewer->isRealized()"); |
---|
| 299 | if (!_viewer->isRealized()) { |
---|
| 300 | int screen = 0; |
---|
| 301 | const char *displayEnv = getenv("DISPLAY"); |
---|
| 302 | if (displayEnv != NULL) { |
---|
| 303 | TRACE("DISPLAY: %s", displayEnv); |
---|
| 304 | // 3 parts: host, display, screen |
---|
| 305 | int part = 0; |
---|
| 306 | for (size_t c = 0; c < strlen(displayEnv); c++) { |
---|
| 307 | if (displayEnv[c] == ':') { |
---|
| 308 | part = 1; |
---|
| 309 | } else if (part == 1 && displayEnv[c] == '.') { |
---|
| 310 | part = 2; |
---|
| 311 | } else if (part == 2) { |
---|
| 312 | screen = atoi(&displayEnv[c]); |
---|
| 313 | break; |
---|
| 314 | } |
---|
[4354] | 315 | } |
---|
| 316 | } |
---|
[4382] | 317 | TRACE("Using screen: %d", screen); |
---|
[4320] | 318 | #ifdef USE_OFFSCREEN_RENDERING |
---|
| 319 | #ifdef USE_PBUFFER |
---|
| 320 | osg::ref_ptr<osg::GraphicsContext> pbuffer; |
---|
| 321 | osg::ref_ptr<osg::GraphicsContext::Traits> traits = new osg::GraphicsContext::Traits; |
---|
| 322 | traits->x = 0; |
---|
| 323 | traits->y = 0; |
---|
| 324 | traits->width = _windowWidth; |
---|
| 325 | traits->height = _windowHeight; |
---|
| 326 | traits->red = 8; |
---|
| 327 | traits->green = 8; |
---|
| 328 | traits->blue = 8; |
---|
| 329 | traits->alpha = 8; |
---|
| 330 | traits->windowDecoration = false; |
---|
| 331 | traits->pbuffer = true; |
---|
| 332 | traits->doubleBuffer = true; |
---|
| 333 | traits->sharedContext = 0; |
---|
| 334 | |
---|
| 335 | pbuffer = osg::GraphicsContext::createGraphicsContext(traits.get()); |
---|
| 336 | if (pbuffer.valid()) { |
---|
| 337 | TRACE("Pixel buffer has been created successfully."); |
---|
| 338 | } else { |
---|
| 339 | ERROR("Pixel buffer has not been created successfully."); |
---|
| 340 | } |
---|
| 341 | osg::Camera *camera = new osg::Camera; |
---|
| 342 | camera->setGraphicsContext(pbuffer.get()); |
---|
| 343 | camera->setViewport(new osg::Viewport(0, 0, _windowWidth, _windowHeight)); |
---|
| 344 | GLenum buffer = pbuffer->getTraits()->doubleBuffer ? GL_BACK : GL_FRONT; |
---|
| 345 | camera->setDrawBuffer(buffer); |
---|
| 346 | camera->setReadBuffer(buffer); |
---|
[4307] | 347 | _captureCallback = new ScreenCaptureCallback(); |
---|
[4320] | 348 | camera->setFinalDrawCallback(_captureCallback.get()); |
---|
| 349 | _viewer->addSlave(camera, osg::Matrixd(), osg::Matrixd()); |
---|
| 350 | #else |
---|
| 351 | _viewer->getCamera()->setRenderTargetImplementation(osg::Camera::FRAME_BUFFER_OBJECT); |
---|
| 352 | osg::Texture2D* texture2D = new osg::Texture2D; |
---|
| 353 | texture2D->setTextureSize(_windowWidth, _windowHeight); |
---|
| 354 | texture2D->setInternalFormat(GL_RGBA); |
---|
| 355 | texture2D->setFilter(osg::Texture2D::MIN_FILTER, osg::Texture2D::NEAREST); |
---|
| 356 | texture2D->setFilter(osg::Texture2D::MAG_FILTER, osg::Texture2D::NEAREST); |
---|
| 357 | |
---|
| 358 | _viewer->getCamera()->setImplicitBufferAttachmentMask(0, 0); |
---|
| 359 | _viewer->getCamera()->attach(osg::Camera::COLOR_BUFFER0, texture2D); |
---|
| 360 | //_viewer->getCamera()->attach(osg::Camera::DEPTH_BUFFER, GL_DEPTH_COMPONENT24); |
---|
| 361 | _viewer->getCamera()->attach(osg::Camera::PACKED_DEPTH_STENCIL_BUFFER, GL_DEPTH24_STENCIL8_EXT); |
---|
| 362 | _captureCallback = new ScreenCaptureCallback(texture2D); |
---|
[4307] | 363 | _viewer->getCamera()->setFinalDrawCallback(_captureCallback.get()); |
---|
[4354] | 364 | _viewer->setUpViewInWindow(0, 0, _windowWidth, _windowHeight, screen); |
---|
[4320] | 365 | #endif |
---|
| 366 | #else |
---|
| 367 | _captureCallback = new ScreenCaptureCallback(); |
---|
| 368 | _viewer->getCamera()->setFinalDrawCallback(_captureCallback.get()); |
---|
[4382] | 369 | #if 1 |
---|
[4354] | 370 | _viewer->setUpViewInWindow(0, 0, _windowWidth, _windowHeight, screen); |
---|
[4382] | 371 | #else |
---|
| 372 | SingleWindow *windowConfig = new SingleWindow(0, 0, _windowWidth, _windowHeight, screen); |
---|
| 373 | osg::DisplaySettings *ds = windowConfig->getActiveDisplaySetting(*_viewer.get()); |
---|
| 374 | ds->setDoubleBuffer(false); |
---|
| 375 | ds->setMinimumNumAlphaBits(8); |
---|
| 376 | ds->setMinimumNumStencilBits(8); |
---|
| 377 | ds->setNumMultiSamples(0); |
---|
| 378 | windowConfig->setWindowDecoration(false); |
---|
| 379 | windowConfig->setOverrideRedirect(false); |
---|
| 380 | _viewer->apply(windowConfig); |
---|
[4320] | 381 | #endif |
---|
[4382] | 382 | #endif |
---|
[4293] | 383 | _viewer->realize(); |
---|
| 384 | initColorMaps(); |
---|
[4349] | 385 | // HACK: This seems to initialize something required for properly |
---|
| 386 | // mapping mouse coords |
---|
| 387 | assert(getEventQueue() != NULL); |
---|
| 388 | getEventQueue()->mouseMotion(_windowWidth/2, _windowHeight/2); |
---|
[4293] | 389 | } |
---|
| 390 | } |
---|
| 391 | |
---|
[4345] | 392 | void Renderer::initControls() |
---|
| 393 | { |
---|
| 394 | if (_hbox.valid()) |
---|
| 395 | return; |
---|
| 396 | _hbox = |
---|
| 397 | new osgEarth::Util::Controls::HBox(osgEarth::Util::Controls::Control::ALIGN_RIGHT, |
---|
| 398 | osgEarth::Util::Controls::Control::ALIGN_BOTTOM, |
---|
| 399 | osgEarth::Util::Controls::Gutter(0, 2, 2, 0), 2.0f); |
---|
| 400 | _copyrightLabel = |
---|
| 401 | new osgEarth::Util::Controls::LabelControl("Map data © 2014 ACME Corp.", 12.0f); |
---|
| 402 | _copyrightLabel->setForeColor(osg::Vec4f(1, 1, 1, 1)); |
---|
| 403 | _copyrightLabel->setHaloColor(osg::Vec4f(0, 0, 0, 1)); |
---|
| 404 | _copyrightLabel->setEncoding(osgText::String::ENCODING_UTF8); |
---|
| 405 | _scaleLabel = |
---|
| 406 | new osgEarth::Util::Controls::LabelControl("- km", 12.0f); |
---|
| 407 | _scaleLabel->setForeColor(osg::Vec4f(1, 1, 1, 1)); |
---|
| 408 | _scaleLabel->setHaloColor(osg::Vec4f(0, 0, 0, 1)); |
---|
| 409 | _scaleBar = |
---|
| 410 | new osgEarth::Util::Controls::Frame(); |
---|
| 411 | _scaleBar->setVertFill(true); |
---|
| 412 | _scaleBar->setForeColor(osg::Vec4f(0, 0, 0, 1)); |
---|
| 413 | _scaleBar->setBackColor(osg::Vec4f(1, 1, 1, 0.6)); |
---|
| 414 | _scaleBar->setBorderColor(osg::Vec4f(0, 0, 0 ,1)); |
---|
| 415 | _scaleBar->setBorderWidth(1.0); |
---|
| 416 | _hbox->addControl(_copyrightLabel.get()); |
---|
| 417 | _hbox->addControl(_scaleLabel.get()); |
---|
| 418 | _hbox->addControl(_scaleBar.get()); |
---|
[4643] | 419 | #if OSGEARTH_MIN_VERSION_REQUIRED(2, 5, 1) |
---|
[4575] | 420 | osgEarth::Util::Controls::ControlCanvas::getOrCreate(_viewer.get())->addControl(_hbox.get()); |
---|
| 421 | #else |
---|
[4345] | 422 | osgEarth::Util::Controls::ControlCanvas::get(_viewer.get(), true)->addControl(_hbox.get()); |
---|
[4575] | 423 | #endif |
---|
[4349] | 424 | // Install an event callback to handle scale bar updates |
---|
| 425 | // Can't use an update callback since that will trigger |
---|
| 426 | // constant rendering |
---|
| 427 | _mapNode->setEventCallback(new MapNodeCallback(this)); |
---|
[4345] | 428 | } |
---|
| 429 | |
---|
[4310] | 430 | void Renderer::setGraticule(bool enable, GraticuleType type) |
---|
| 431 | { |
---|
| 432 | if (!_mapNode.valid() || !_sceneRoot.valid()) |
---|
| 433 | return; |
---|
| 434 | if (enable) { |
---|
| 435 | if (_graticule.valid()) { |
---|
| 436 | _sceneRoot->removeChild(_graticule.get()); |
---|
| 437 | _graticule = NULL; |
---|
| 438 | } |
---|
| 439 | switch (type) { |
---|
| 440 | case GRATICULE_UTM: { |
---|
| 441 | osgEarth::Util::UTMGraticule *gr = new osgEarth::Util::UTMGraticule(_mapNode.get()); |
---|
| 442 | _sceneRoot->addChild(gr); |
---|
| 443 | _graticule = gr; |
---|
| 444 | } |
---|
| 445 | break; |
---|
| 446 | case GRATICULE_MGRS: { |
---|
| 447 | osgEarth::Util::MGRSGraticule *gr = new osgEarth::Util::MGRSGraticule(_mapNode.get()); |
---|
| 448 | _sceneRoot->addChild(gr); |
---|
| 449 | _graticule = gr; |
---|
| 450 | } |
---|
| 451 | break; |
---|
| 452 | case GRATICULE_GEODETIC: |
---|
| 453 | default: |
---|
| 454 | osgEarth::Util::GeodeticGraticule *gr = new osgEarth::Util::GeodeticGraticule(_mapNode.get()); |
---|
| 455 | osgEarth::Util::GeodeticGraticuleOptions opt = gr->getOptions(); |
---|
| 456 | opt.lineStyle()->getOrCreate<osgEarth::Symbology::LineSymbol>()->stroke()->color().set(1,0,0,1); |
---|
| 457 | gr->setOptions(opt); |
---|
| 458 | _sceneRoot->addChild(gr); |
---|
| 459 | _graticule = gr; |
---|
| 460 | } |
---|
| 461 | } else if (_graticule.valid()) { |
---|
| 462 | _sceneRoot->removeChild(_graticule.get()); |
---|
| 463 | _graticule = NULL; |
---|
| 464 | } |
---|
| 465 | _needsRedraw = true; |
---|
| 466 | } |
---|
| 467 | |
---|
[4349] | 468 | void Renderer::setReadout(int x, int y) |
---|
[4307] | 469 | { |
---|
[4349] | 470 | if (!_coordsCallback.valid() || !_mapNode.valid() || !_viewer.valid()) |
---|
| 471 | return; |
---|
| 472 | |
---|
| 473 | osgEarth::GeoPoint mapCoord; |
---|
| 474 | if (mapMouseCoords(x, y, mapCoord)) { |
---|
| 475 | _coordsCallback->set(mapCoord, _viewer->asView(), _mapNode); |
---|
| 476 | } else { |
---|
| 477 | _coordsCallback->reset(_viewer->asView(), _mapNode); |
---|
| 478 | } |
---|
| 479 | _needsRedraw = true; |
---|
| 480 | } |
---|
| 481 | |
---|
| 482 | void Renderer::clearReadout() |
---|
| 483 | { |
---|
| 484 | if (_coordsCallback.valid()) { |
---|
| 485 | _coordsCallback->reset(_viewer->asView(), _mapNode); |
---|
| 486 | } |
---|
| 487 | _needsRedraw = true; |
---|
| 488 | } |
---|
| 489 | |
---|
| 490 | void Renderer::setCoordinateReadout(bool state, CoordinateDisplayType type, |
---|
| 491 | int precision) |
---|
| 492 | { |
---|
| 493 | if (!state) { |
---|
| 494 | if (_mouseCoordsTool.valid() && _viewer.valid()) { |
---|
| 495 | _viewer->removeEventHandler(_mouseCoordsTool.get()); |
---|
| 496 | _mouseCoordsTool = NULL; |
---|
| 497 | } |
---|
| 498 | if (_coordsCallback.valid() && _viewer.valid()) { |
---|
| 499 | osgEarth::Util::Controls::LabelControl *readout = |
---|
| 500 | _coordsCallback->getLabel(); |
---|
[4643] | 501 | #if OSGEARTH_MIN_VERSION_REQUIRED(2, 5, 1) |
---|
[4575] | 502 | osgEarth::Util::Controls::ControlCanvas::getOrCreate(_viewer.get())->removeControl(readout); |
---|
| 503 | #else |
---|
[4349] | 504 | osgEarth::Util::Controls::ControlCanvas::get(_viewer.get(), true)->removeControl(readout); |
---|
[4575] | 505 | #endif |
---|
[4349] | 506 | _coordsCallback = NULL; |
---|
| 507 | } |
---|
| 508 | } else { |
---|
| 509 | initMouseCoordsTool(type, precision); |
---|
| 510 | } |
---|
| 511 | _needsRedraw = true; |
---|
| 512 | } |
---|
| 513 | |
---|
| 514 | osgEarth::Util::MGRSFormatter::Precision |
---|
| 515 | Renderer::getMGRSPrecision(int precisionInMeters) |
---|
| 516 | { |
---|
| 517 | switch (precisionInMeters) { |
---|
| 518 | case 1: |
---|
| 519 | return osgEarth::Util::MGRSFormatter::PRECISION_1M; |
---|
| 520 | case 10: |
---|
| 521 | return osgEarth::Util::MGRSFormatter::PRECISION_10M; |
---|
| 522 | case 100: |
---|
| 523 | return osgEarth::Util::MGRSFormatter::PRECISION_100M; |
---|
| 524 | case 1000: |
---|
| 525 | return osgEarth::Util::MGRSFormatter::PRECISION_1000M; |
---|
| 526 | case 10000: |
---|
| 527 | return osgEarth::Util::MGRSFormatter::PRECISION_10000M; |
---|
| 528 | case 100000: |
---|
| 529 | return osgEarth::Util::MGRSFormatter::PRECISION_100000M; |
---|
| 530 | default: |
---|
| 531 | ERROR("Invalid precision: %d", precisionInMeters); |
---|
| 532 | return osgEarth::Util::MGRSFormatter::PRECISION_1M; |
---|
| 533 | } |
---|
| 534 | } |
---|
| 535 | |
---|
| 536 | void Renderer::initMouseCoordsTool(CoordinateDisplayType type, int precision) |
---|
| 537 | { |
---|
| 538 | if (!_viewer.valid()) |
---|
| 539 | return; |
---|
[4307] | 540 | if (_mouseCoordsTool.valid()) { |
---|
| 541 | _viewer->removeEventHandler(_mouseCoordsTool.get()); |
---|
| 542 | } |
---|
[4349] | 543 | _mouseCoordsTool = new MouseCoordsTool(_mapNode.get()); |
---|
| 544 | osgEarth::Util::Controls::LabelControl *readout; |
---|
| 545 | if (_coordsCallback.valid()) { |
---|
| 546 | readout = _coordsCallback->getLabel(); |
---|
| 547 | _coordsCallback = NULL; |
---|
| 548 | } else { |
---|
| 549 | readout = new osgEarth::Util::Controls::LabelControl("", 12.0f); |
---|
[4643] | 550 | #if OSGEARTH_MIN_VERSION_REQUIRED(2, 5, 1) |
---|
[4575] | 551 | osgEarth::Util::Controls::ControlCanvas::getOrCreate(_viewer.get())->addControl(readout); |
---|
| 552 | #else |
---|
[4349] | 553 | osgEarth::Util::Controls::ControlCanvas::get(_viewer.get(), true)->addControl(readout); |
---|
[4575] | 554 | #endif |
---|
[4345] | 555 | readout->setForeColor(osg::Vec4f(1, 1, 1, 1)); |
---|
| 556 | readout->setHaloColor(osg::Vec4f(0, 0, 0, 1)); |
---|
[4307] | 557 | } |
---|
[4349] | 558 | |
---|
| 559 | osgEarth::Util::Formatter *formatter = NULL; |
---|
| 560 | if (type == COORDS_MGRS) { |
---|
| 561 | osgEarth::Util::MGRSFormatter::Precision prec = |
---|
| 562 | osgEarth::Util::MGRSFormatter::PRECISION_1M; |
---|
| 563 | if (precision > 0) { |
---|
| 564 | prec = getMGRSPrecision(precision); |
---|
| 565 | } |
---|
| 566 | unsigned int opts = 0u; |
---|
| 567 | formatter = new osgEarth::Util::MGRSFormatter(prec, NULL, opts); |
---|
| 568 | } else { |
---|
| 569 | osgEarth::Util::LatLongFormatter::AngularFormat af; |
---|
| 570 | unsigned int opts = osgEarth::Util::LatLongFormatter::USE_SYMBOLS; |
---|
| 571 | switch (type) { |
---|
| 572 | case COORDS_LATLONG_DEGREES_DECIMAL_MINUTES: |
---|
| 573 | af = osgEarth::Util::LatLongFormatter::FORMAT_DEGREES_DECIMAL_MINUTES; |
---|
| 574 | break; |
---|
| 575 | case COORDS_LATLONG_DEGREES_MINUTES_SECONDS: |
---|
| 576 | af = osgEarth::Util::LatLongFormatter::FORMAT_DEGREES_MINUTES_SECONDS; |
---|
| 577 | break; |
---|
| 578 | default: |
---|
| 579 | af = osgEarth::Util::LatLongFormatter::FORMAT_DECIMAL_DEGREES; |
---|
| 580 | } |
---|
| 581 | osgEarth::Util::LatLongFormatter *latlong = new osgEarth::Util::LatLongFormatter(af, opts); |
---|
| 582 | if (precision > 0) { |
---|
| 583 | latlong->setPrecision(precision); |
---|
| 584 | } |
---|
| 585 | formatter = latlong; |
---|
| 586 | } |
---|
| 587 | _coordsCallback = new MouseCoordsCallback(readout, formatter); |
---|
| 588 | |
---|
[4307] | 589 | _mouseCoordsTool->addCallback(_coordsCallback.get()); |
---|
| 590 | _viewer->addEventHandler(_mouseCoordsTool.get()); |
---|
| 591 | } |
---|
| 592 | |
---|
[4305] | 593 | void Renderer::initEarthManipulator() |
---|
| 594 | { |
---|
| 595 | _manipulator = new osgEarth::Util::EarthManipulator; |
---|
[4307] | 596 | #if 1 |
---|
[4305] | 597 | osgEarth::Util::EarthManipulator::Settings *settings = _manipulator->getSettings(); |
---|
| 598 | settings->bindMouse(osgEarth::Util::EarthManipulator::ACTION_ROTATE, |
---|
| 599 | osgGA::GUIEventAdapter::MIDDLE_MOUSE_BUTTON, |
---|
| 600 | osgGA::GUIEventAdapter::MODKEY_ALT); |
---|
| 601 | osgEarth::Util::EarthManipulator::ActionOptions options; |
---|
| 602 | options.clear(); |
---|
| 603 | options.add(osgEarth::Util::EarthManipulator::OPTION_CONTINUOUS, true); |
---|
| 604 | settings->bindMouse(osgEarth::Util::EarthManipulator::ACTION_ZOOM, |
---|
| 605 | osgGA::GUIEventAdapter::RIGHT_MOUSE_BUTTON, |
---|
| 606 | osgGA::GUIEventAdapter::MODKEY_ALT, options); |
---|
| 607 | _manipulator->applySettings(settings); |
---|
[4307] | 608 | #endif |
---|
[4305] | 609 | _viewer->setCameraManipulator(_manipulator.get()); |
---|
| 610 | _manipulator->setNode(NULL); |
---|
| 611 | _manipulator->setNode(_sceneRoot.get()); |
---|
[4382] | 612 | //_manipulator->computeHomePosition(); |
---|
[4305] | 613 | } |
---|
| 614 | |
---|
[4009] | 615 | void Renderer::loadEarthFile(const char *path) |
---|
| 616 | { |
---|
| 617 | TRACE("Loading %s", path); |
---|
[4028] | 618 | osg::Node *node = osgDB::readNodeFile(path); |
---|
| 619 | if (node == NULL) { |
---|
| 620 | ERROR("Couldn't load %s", path); |
---|
| 621 | return; |
---|
| 622 | } |
---|
| 623 | osgEarth::MapNode *mapNode = osgEarth::MapNode::findMapNode(node); |
---|
[4009] | 624 | if (mapNode == NULL) { |
---|
| 625 | ERROR("Couldn't find MapNode"); |
---|
[4028] | 626 | return; |
---|
[4009] | 627 | } else { |
---|
[4293] | 628 | initViewer(); |
---|
[4310] | 629 | _sceneRoot = new osg::Group; |
---|
| 630 | _sceneRoot->addChild(node); |
---|
[4009] | 631 | _map = mapNode->getMap(); |
---|
| 632 | } |
---|
[4028] | 633 | _mapNode = mapNode; |
---|
[4282] | 634 | |
---|
[4036] | 635 | if (_clipPlaneCullCallback.valid()) { |
---|
| 636 | _viewer->getCamera()->removeCullCallback(_clipPlaneCullCallback.get()); |
---|
| 637 | _clipPlaneCullCallback = NULL; |
---|
| 638 | } |
---|
| 639 | if (_map->isGeocentric()) { |
---|
| 640 | _clipPlaneCullCallback = new osgEarth::Util::AutoClipPlaneCullCallback(mapNode); |
---|
| 641 | _viewer->getCamera()->addCullCallback(_clipPlaneCullCallback.get()); |
---|
| 642 | } |
---|
[4009] | 643 | _viewer->setSceneData(_sceneRoot.get()); |
---|
[4307] | 644 | |
---|
[4349] | 645 | if (_mouseCoordsTool.valid()) { |
---|
| 646 | initMouseCoordsTool(); |
---|
| 647 | } |
---|
[4345] | 648 | initControls(); |
---|
[4305] | 649 | initEarthManipulator(); |
---|
[4307] | 650 | |
---|
[4009] | 651 | _viewer->home(); |
---|
[4293] | 652 | finalizeViewer(); |
---|
[4009] | 653 | _needsRedraw = true; |
---|
| 654 | } |
---|
| 655 | |
---|
[4246] | 656 | void Renderer::resetMap(osgEarth::MapOptions::CoordinateSystemType type, |
---|
| 657 | const char *profile, |
---|
| 658 | double bounds[4]) |
---|
[4028] | 659 | { |
---|
| 660 | TRACE("Restting map with type %d, profile %s", type, profile); |
---|
| 661 | |
---|
| 662 | osgEarth::MapOptions mapOpts; |
---|
| 663 | mapOpts.coordSysType() = type; |
---|
| 664 | if (profile != NULL) { |
---|
[4246] | 665 | if (bounds != NULL) { |
---|
| 666 | mapOpts.profile() = osgEarth::ProfileOptions(); |
---|
| 667 | if (strcmp(profile, "geodetic") == 0) { |
---|
| 668 | mapOpts.profile()->srsString() = "epsg:4326"; |
---|
| 669 | } else if (strcmp(profile, "spherical-mercator") == 0) { |
---|
| 670 | // Projection used by Google/Bing/OSM |
---|
| 671 | // aka epsg:900913 meters in x/y |
---|
| 672 | // aka WGS84 Web Mercator (Auxiliary Sphere) |
---|
| 673 | // X/Y: -20037508.34m to 20037508.34m |
---|
| 674 | mapOpts.profile()->srsString() = "epsg:3857"; |
---|
| 675 | } else { |
---|
| 676 | mapOpts.profile()->srsString() = profile; |
---|
| 677 | } |
---|
[4247] | 678 | TRACE("Setting profile bounds: %g %g %g %g", |
---|
| 679 | bounds[0], bounds[1], bounds[2], bounds[3]); |
---|
[4246] | 680 | mapOpts.profile()->bounds() = |
---|
| 681 | osgEarth::Bounds(bounds[0], bounds[1], bounds[2], bounds[3]); |
---|
| 682 | } else { |
---|
| 683 | mapOpts.profile() = osgEarth::ProfileOptions(profile); |
---|
| 684 | } |
---|
[4028] | 685 | } else if (type == osgEarth::MapOptions::CSTYPE_PROJECTED) { |
---|
[4246] | 686 | mapOpts.profile() = osgEarth::ProfileOptions("global-mercator"); |
---|
[4028] | 687 | } |
---|
[4293] | 688 | |
---|
[4575] | 689 | #ifdef USE_CACHE |
---|
[4382] | 690 | setupCache(); |
---|
| 691 | osgEarth::Drivers::FileSystemCacheOptions cacheOpts; |
---|
| 692 | cacheOpts.rootPath() = _cacheDir; |
---|
| 693 | mapOpts.cache() = cacheOpts; |
---|
[4575] | 694 | #endif |
---|
[4382] | 695 | |
---|
[4293] | 696 | initViewer(); |
---|
| 697 | |
---|
[4305] | 698 | //mapOpts.referenceURI() = _baseURI; |
---|
[4028] | 699 | osgEarth::Map *map = new osgEarth::Map(mapOpts); |
---|
| 700 | _map = map; |
---|
[4246] | 701 | osgEarth::Drivers::GDALOptions bopts; |
---|
[4635] | 702 | bopts.url() = getBaseImage(); |
---|
[4246] | 703 | addImageLayer("base", bopts); |
---|
[4643] | 704 | #if OSGEARTH_MIN_VERSION_REQUIRED(2, 5, 1) |
---|
[4575] | 705 | osgEarth::Drivers::MPTerrainEngine::MPTerrainEngineOptions mpOpt; |
---|
| 706 | #else |
---|
[4246] | 707 | osgEarth::Drivers::MPTerrainEngineOptions mpOpt; |
---|
[4575] | 708 | #endif |
---|
[4246] | 709 | // Set background layer color |
---|
| 710 | mpOpt.color() = osg::Vec4(1, 1, 1, 1); |
---|
| 711 | //mpOpt.minLOD() = 1; |
---|
[4273] | 712 | // Sets shader uniform for terrain renderer (config var defaults to false) |
---|
| 713 | mpOpt.enableLighting() = false; |
---|
[4246] | 714 | osgEarth::MapNodeOptions mapNodeOpts(mpOpt); |
---|
[4273] | 715 | // Sets GL_LIGHTING state in MapNode's StateSet (config var defaults to true) |
---|
| 716 | mapNodeOpts.enableLighting() = true; |
---|
[4643] | 717 | //mapNodeOpts.getTerrainOptions().loadingPolicy().mapLoadingThreadsPerCore() = 1; |
---|
| 718 | //mapNodeOpts.getTerrainOptions().loadingPolicy().numLoadingThreads() = 1; |
---|
| 719 | //mapNodeOpts.getTerrainOptions().loadingPolicy().numCompileThreadsPerCore() = 1; |
---|
| 720 | //mapNodeOpts.getTerrainOptions().loadingPolicy().numCompileThreads() = 1; |
---|
[4028] | 721 | osgEarth::MapNode *mapNode = new osgEarth::MapNode(map, mapNodeOpts); |
---|
| 722 | _mapNode = mapNode; |
---|
[4282] | 723 | if (_map->isGeocentric()) { |
---|
[4382] | 724 | osgEarth::DateTime now; |
---|
[4643] | 725 | #if OSGEARTH_MIN_VERSION_REQUIRED(2, 5, 1) |
---|
[4382] | 726 | TRACE("Creating SkyNode"); |
---|
[4643] | 727 | osgEarth::Drivers::SimpleSky::SimpleSkyOptions skyOpts; |
---|
[4382] | 728 | skyOpts.hours() = now.hours(); |
---|
| 729 | skyOpts.ambient() = 0.2f; |
---|
[4643] | 730 | skyOpts.atmosphericLighting() = true; |
---|
| 731 | skyOpts.exposure() = 3.0; |
---|
[4573] | 732 | osgEarth::Util::SkyNode *sky = osgEarth::Util::SkyNode::create(skyOpts, mapNode); |
---|
[4382] | 733 | sky->addChild(mapNode); |
---|
| 734 | sky->attach(_viewer.get(), 0); |
---|
[4282] | 735 | _sceneRoot = sky; |
---|
| 736 | #else |
---|
[4310] | 737 | _sceneRoot = new osg::Group(); |
---|
| 738 | _sceneRoot->addChild(_mapNode.get()); |
---|
[4382] | 739 | |
---|
| 740 | TRACE("Creating SkyNode"); |
---|
| 741 | osgEarth::Util::SkyNode *sky = new osgEarth::Util::SkyNode(map); |
---|
| 742 | _sceneRoot->addChild(sky); |
---|
| 743 | sky->setAmbientBrightness(0.2f); |
---|
| 744 | sky->setDateTime(now); |
---|
| 745 | sky->attach(_viewer.get(), 0); |
---|
[4282] | 746 | #endif |
---|
| 747 | } else { |
---|
[4310] | 748 | _sceneRoot = new osg::Group(); |
---|
| 749 | _sceneRoot->addChild(_mapNode.get()); |
---|
[4282] | 750 | } |
---|
[4036] | 751 | |
---|
| 752 | if (_clipPlaneCullCallback.valid()) { |
---|
| 753 | _viewer->getCamera()->removeCullCallback(_clipPlaneCullCallback.get()); |
---|
| 754 | _clipPlaneCullCallback = NULL; |
---|
| 755 | } |
---|
| 756 | if (_map->isGeocentric()) { |
---|
| 757 | _clipPlaneCullCallback = new osgEarth::Util::AutoClipPlaneCullCallback(mapNode); |
---|
| 758 | _viewer->getCamera()->addCullCallback(_clipPlaneCullCallback.get()); |
---|
| 759 | } |
---|
[4028] | 760 | _viewer->setSceneData(_sceneRoot.get()); |
---|
[4349] | 761 | if (_mouseCoordsTool.valid()) { |
---|
| 762 | initMouseCoordsTool(); |
---|
| 763 | } |
---|
[4345] | 764 | initControls(); |
---|
[4307] | 765 | //_viewer->setSceneData(_sceneRoot.get()); |
---|
[4305] | 766 | initEarthManipulator(); |
---|
[4028] | 767 | _viewer->home(); |
---|
[4293] | 768 | |
---|
| 769 | finalizeViewer(); |
---|
[4028] | 770 | _needsRedraw = true; |
---|
| 771 | } |
---|
| 772 | |
---|
[4036] | 773 | void Renderer::clearMap() |
---|
| 774 | { |
---|
| 775 | if (_map.valid()) { |
---|
| 776 | _map->clear(); |
---|
[4293] | 777 | _needsRedraw = true; |
---|
[4036] | 778 | } |
---|
| 779 | } |
---|
| 780 | |
---|
[4273] | 781 | void Renderer::setLighting(bool state) |
---|
| 782 | { |
---|
| 783 | if (_mapNode.valid()) { |
---|
| 784 | TRACE("Setting lighting: %d", state ? 1 : 0); |
---|
| 785 | _mapNode->getOrCreateStateSet() |
---|
| 786 | ->setMode(GL_LIGHTING, state ? 1 : 0); |
---|
[4293] | 787 | _needsRedraw = true; |
---|
[4273] | 788 | } |
---|
| 789 | } |
---|
| 790 | |
---|
[4293] | 791 | void Renderer::setViewerLightType(osg::View::LightingMode mode) |
---|
| 792 | { |
---|
| 793 | if (_viewer.valid()) { |
---|
| 794 | _viewer->setLightingMode(mode); |
---|
| 795 | _needsRedraw = true; |
---|
| 796 | } |
---|
| 797 | } |
---|
| 798 | |
---|
[4273] | 799 | void Renderer::setTerrainVerticalScale(double scale) |
---|
| 800 | { |
---|
| 801 | if (_mapNode.valid()) { |
---|
[4285] | 802 | if (!_verticalScale.valid()) { |
---|
| 803 | _verticalScale = new osgEarth::Util::VerticalScale; |
---|
| 804 | _mapNode->getTerrainEngine()->addEffect(_verticalScale); |
---|
| 805 | } |
---|
| 806 | _verticalScale->setScale(scale); |
---|
[4293] | 807 | _needsRedraw = true; |
---|
[4273] | 808 | } |
---|
| 809 | } |
---|
| 810 | |
---|
| 811 | void Renderer::setTerrainLighting(bool state) |
---|
| 812 | { |
---|
| 813 | #if 1 |
---|
[4293] | 814 | if (!_mapNode.valid()) { |
---|
| 815 | ERROR("No map node"); |
---|
| 816 | return; |
---|
| 817 | } |
---|
| 818 | // XXX: HACK alert |
---|
| 819 | // Find the terrain engine container (might be above one or more decorators) |
---|
| 820 | osg::Group *group = _mapNode->getTerrainEngine(); |
---|
| 821 | while (group->getParent(0) != NULL && group->getParent(0) != _mapNode.get()) { |
---|
| 822 | group = group->getParent(0); |
---|
| 823 | } |
---|
| 824 | if (group != NULL && group->getParent(0) == _mapNode.get()) { |
---|
| 825 | TRACE("Setting terrain lighting: %d", state ? 1 : 0); |
---|
| 826 | if (group->getOrCreateStateSet()->getUniform("oe_mode_GL_LIGHTING") != NULL) { |
---|
| 827 | group->getStateSet()->getUniform("oe_mode_GL_LIGHTING")->set(state); |
---|
[4273] | 828 | } else { |
---|
| 829 | ERROR("Can't get terrain lighting uniform"); |
---|
| 830 | } |
---|
[4293] | 831 | } else { |
---|
[4305] | 832 | ERROR("Can't find terrain engine container"); |
---|
[4273] | 833 | } |
---|
| 834 | #else |
---|
| 835 | if (_stateManip.valid()) { |
---|
| 836 | _stateManip->setLightingEnabled(state); |
---|
| 837 | } |
---|
| 838 | #endif |
---|
| 839 | _needsRedraw = true; |
---|
| 840 | } |
---|
| 841 | |
---|
| 842 | void Renderer::setTerrainWireframe(bool state) |
---|
| 843 | { |
---|
[4293] | 844 | if (!_map.valid()) { |
---|
| 845 | ERROR("No map"); |
---|
| 846 | return; |
---|
| 847 | } |
---|
[4273] | 848 | #if 0 |
---|
[4293] | 849 | if (!_mapNode.valid()) { |
---|
| 850 | ERROR("No map node"); |
---|
| 851 | return; |
---|
[4273] | 852 | } |
---|
[4293] | 853 | TRACE("Setting terrain wireframe: %d", state ? 1 : 0); |
---|
| 854 | osg::StateSet *state = _mapNode->getOrCreateStateSet(); |
---|
| 855 | osg::PolygonMode *pmode = dynamic_cast< osg::PolygonMode* >(state->getAttribute(osg::StateAttribute::POLYGONMODE)); |
---|
| 856 | if (pmode == NULL) { |
---|
| 857 | pmode = new osg::PolygonMode; |
---|
| 858 | state->setAttribute(pmode); |
---|
| 859 | } |
---|
| 860 | if (state) { |
---|
| 861 | pmode->setMode(osg::PolygonMode::FRONT_AND_BACK, osg::PolygonMode::LINE); |
---|
| 862 | } else { |
---|
| 863 | pmode->setMode(osg::PolygonMode::FRONT_AND_BACK, osg::PolygonMode::FILL); |
---|
| 864 | } |
---|
| 865 | _needsRedraw = true; |
---|
[4273] | 866 | #else |
---|
| 867 | if (_stateManip.valid()) { |
---|
| 868 | _stateManip->setPolygonMode(state ? osg::PolygonMode::LINE : osg::PolygonMode::FILL); |
---|
[4293] | 869 | _needsRedraw = true; |
---|
[4273] | 870 | } |
---|
| 871 | #endif |
---|
| 872 | } |
---|
| 873 | |
---|
[4312] | 874 | void Renderer::saveNamedViewpoint(const char *name) |
---|
| 875 | { |
---|
| 876 | _viewpoints[name] = getViewpoint(); |
---|
| 877 | } |
---|
| 878 | |
---|
[4319] | 879 | bool Renderer::removeNamedViewpoint(const char *name) |
---|
| 880 | { |
---|
| 881 | ViewpointHashmap::iterator itr = _viewpoints.find(name); |
---|
| 882 | if (itr != _viewpoints.end()) { |
---|
| 883 | _viewpoints.erase(name); |
---|
| 884 | return true; |
---|
| 885 | } else { |
---|
| 886 | ERROR("Unknown viewpoint: '%s'", name); |
---|
| 887 | return false; |
---|
| 888 | } |
---|
| 889 | } |
---|
| 890 | |
---|
[4312] | 891 | bool Renderer::restoreNamedViewpoint(const char *name, double durationSecs) |
---|
| 892 | { |
---|
| 893 | ViewpointHashmap::iterator itr = _viewpoints.find(name); |
---|
| 894 | if (itr != _viewpoints.end()) { |
---|
| 895 | setViewpoint(itr->second, durationSecs); |
---|
| 896 | return true; |
---|
| 897 | } else { |
---|
| 898 | ERROR("Unknown viewpoint: '%s'", name); |
---|
| 899 | return false; |
---|
| 900 | } |
---|
| 901 | } |
---|
| 902 | |
---|
[4273] | 903 | void Renderer::setViewpoint(const osgEarth::Viewpoint& v, double durationSecs) |
---|
| 904 | { |
---|
| 905 | if (_manipulator.valid()) { |
---|
[4328] | 906 | TRACE("Setting viewpoint: %g %g %g %g %g %g", |
---|
| 907 | v.x(), v.y(), v.z(), v.getHeading(), v.getPitch(), v.getRange()); |
---|
[4273] | 908 | _manipulator->setViewpoint(v, durationSecs); |
---|
[4293] | 909 | _needsRedraw = true; |
---|
[4328] | 910 | } else { |
---|
| 911 | ERROR("No manipulator"); |
---|
[4273] | 912 | } |
---|
| 913 | } |
---|
| 914 | |
---|
| 915 | osgEarth::Viewpoint Renderer::getViewpoint() |
---|
| 916 | { |
---|
| 917 | if (_manipulator.valid()) { |
---|
| 918 | return _manipulator->getViewpoint(); |
---|
| 919 | } else { |
---|
| 920 | // Uninitialized, invalid viewpoint |
---|
| 921 | return osgEarth::Viewpoint(); |
---|
| 922 | } |
---|
| 923 | } |
---|
| 924 | |
---|
[4575] | 925 | static void srsInfo(const osgEarth::SpatialReference *srs) |
---|
| 926 | { |
---|
| 927 | TRACE("SRS: %s", srs->getName().c_str()); |
---|
| 928 | TRACE("horiz: \"%s\" vert: \"%s\"", srs->getHorizInitString().c_str(), srs->getVertInitString().c_str()); |
---|
| 929 | TRACE("geographic: %d geodetic: %d projected: %d ecef: %d mercator: %d spherical mercator: %d northpolar: %d southpolar: %d userdefined: %d contiguous: %d cube: %d ltp: %d plate_carre: %d", |
---|
| 930 | srs->isGeographic() ? 1 : 0, |
---|
| 931 | srs->isGeodetic() ? 1 : 0, |
---|
| 932 | srs->isProjected() ? 1 : 0, |
---|
| 933 | srs->isECEF() ? 1 : 0, |
---|
| 934 | srs->isMercator() ? 1 : 0, |
---|
| 935 | srs->isSphericalMercator() ? 1 : 0, |
---|
| 936 | srs->isNorthPolar() ? 1 : 0, |
---|
| 937 | srs->isSouthPolar() ? 1 : 0, |
---|
| 938 | srs->isUserDefined() ? 1 : 0, |
---|
| 939 | srs->isContiguous() ? 1 : 0, |
---|
| 940 | srs->isCube() ? 1 : 0, |
---|
| 941 | srs->isLTP() ? 1 : 0, |
---|
| 942 | srs->isPlateCarre() ? 1 : 0); |
---|
| 943 | } |
---|
| 944 | |
---|
| 945 | bool Renderer::getWorldCoords(const osgEarth::GeoPoint& mapPt, osg::Vec3d *world) |
---|
| 946 | { |
---|
| 947 | if (!_mapNode.valid() || _mapNode->getTerrain() == NULL) { |
---|
| 948 | ERROR("No map"); |
---|
| 949 | return false; |
---|
| 950 | } |
---|
| 951 | TRACE("Input SRS:"); |
---|
| 952 | srsInfo(mapPt.getSRS()); |
---|
| 953 | TRACE("Map SRS:"); |
---|
| 954 | srsInfo(_mapNode->getMapSRS()); |
---|
| 955 | bool ret = mapPt.toWorld(*world, _mapNode->getTerrain()); |
---|
| 956 | TRACE("In: %g,%g,%g Out: %g,%g,%g", |
---|
| 957 | mapPt.x(), mapPt.y(), mapPt.z(), |
---|
| 958 | world->x(), world->y(), world->z()); |
---|
| 959 | return ret; |
---|
| 960 | } |
---|
| 961 | |
---|
| 962 | bool Renderer::worldToScreen(const osg::Vec3d& world, osg::Vec3d *screen, bool invertY) |
---|
| 963 | { |
---|
| 964 | if (!_viewer.valid()) { |
---|
| 965 | ERROR("No viewer"); |
---|
| 966 | return false; |
---|
| 967 | } |
---|
| 968 | osg::Camera *cam = _viewer->getCamera(); |
---|
| 969 | osg::Matrixd MVP = cam->getViewMatrix() * cam->getProjectionMatrix(); |
---|
| 970 | // Get clip coords |
---|
| 971 | osg::Vec4d pt; |
---|
| 972 | pt = osg::Vec4d(world, 1.0) * MVP; |
---|
| 973 | // Clip |
---|
| 974 | if (pt.x() < -pt.w() || |
---|
| 975 | pt.x() > pt.w() || |
---|
| 976 | pt.y() < -pt.w() || |
---|
| 977 | pt.y() > pt.w() || |
---|
| 978 | pt.z() < -pt.w() || |
---|
| 979 | pt.z() > pt.w()) { |
---|
| 980 | // Outside frustum |
---|
| 981 | TRACE("invalid pt: %g,%g,%g,%g", pt.x(), pt.y(), pt.z(), pt.w()); |
---|
| 982 | return false; |
---|
| 983 | } |
---|
| 984 | TRACE("clip pt: %g,%g,%g,%g", pt.x(), pt.y(), pt.z(), pt.w()); |
---|
| 985 | // Perspective divide: now NDC |
---|
| 986 | pt /= pt.w(); |
---|
| 987 | const osg::Viewport *viewport = cam->getViewport(); |
---|
| 988 | #if 1 |
---|
| 989 | screen->x() = viewport->x() + viewport->width() * 0.5 + pt.x() * viewport->width() * 0.5; |
---|
| 990 | screen->y() = viewport->y() + viewport->height() * 0.5 + pt.y() * viewport->height() * 0.5; |
---|
| 991 | //double near = 0; |
---|
| 992 | //double far = 1; |
---|
| 993 | //screen->z() = (far + near) * 0.5 + (far - near) * 0.5 * pt.z(); |
---|
| 994 | screen->z() = 0.5 + 0.5 * pt.z(); |
---|
| 995 | #else |
---|
| 996 | *screen = osg::Vec3d(pt.x(), pt.y(), pt.z()) * cam->getViewport()->computeWindowMatrix(); |
---|
| 997 | #endif |
---|
| 998 | if (invertY) { |
---|
| 999 | screen->y() = viewport->height() - screen->y(); |
---|
| 1000 | } |
---|
| 1001 | TRACE("screen: %g,%g,%g", screen->x(), screen->y(), screen->z()); |
---|
| 1002 | return true; |
---|
| 1003 | } |
---|
| 1004 | |
---|
[4636] | 1005 | bool Renderer::worldToScreen(std::vector<osg::Vec3d>& coords, bool invertY) |
---|
| 1006 | { |
---|
| 1007 | if (!_viewer.valid()) { |
---|
| 1008 | ERROR("No viewer"); |
---|
| 1009 | return false; |
---|
| 1010 | } |
---|
[4645] | 1011 | bool ret = true; |
---|
[4636] | 1012 | osg::Camera *cam = _viewer->getCamera(); |
---|
| 1013 | osg::Matrixd MVP = cam->getViewMatrix() * cam->getProjectionMatrix(); |
---|
| 1014 | const osg::Viewport *viewport = cam->getViewport(); |
---|
| 1015 | for (std::vector<osg::Vec3d>::iterator itr = coords.begin(); |
---|
| 1016 | itr != coords.end(); ++itr) { |
---|
| 1017 | // Get clip coords |
---|
| 1018 | osg::Vec4d pt; |
---|
| 1019 | pt = osg::Vec4d(*itr, 1.0) * MVP; |
---|
| 1020 | // Clip |
---|
| 1021 | if (pt.x() < -pt.w() || |
---|
| 1022 | pt.x() > pt.w() || |
---|
| 1023 | pt.y() < -pt.w() || |
---|
| 1024 | pt.y() > pt.w() || |
---|
| 1025 | pt.z() < -pt.w() || |
---|
| 1026 | pt.z() > pt.w()) { |
---|
| 1027 | // Outside frustum |
---|
| 1028 | TRACE("invalid pt: %g,%g,%g,%g", pt.x(), pt.y(), pt.z(), pt.w()); |
---|
| 1029 | itr->set(std::numeric_limits<double>::quiet_NaN(), |
---|
| 1030 | std::numeric_limits<double>::quiet_NaN(), |
---|
| 1031 | std::numeric_limits<double>::quiet_NaN()); |
---|
[4645] | 1032 | ret = false; |
---|
[4636] | 1033 | continue; |
---|
| 1034 | } |
---|
| 1035 | TRACE("clip pt: %g,%g,%g,%g", pt.x(), pt.y(), pt.z(), pt.w()); |
---|
| 1036 | // Perspective divide: now NDC |
---|
| 1037 | pt /= pt.w(); |
---|
| 1038 | #if 1 |
---|
| 1039 | itr->x() = viewport->x() + viewport->width() * 0.5 + pt.x() * viewport->width() * 0.5; |
---|
| 1040 | itr->y() = viewport->y() + viewport->height() * 0.5 + pt.y() * viewport->height() * 0.5; |
---|
| 1041 | //double near = 0; |
---|
| 1042 | //double far = 1; |
---|
| 1043 | //itr->z() = (far + near) * 0.5 + (far - near) * 0.5 * pt.z(); |
---|
| 1044 | itr->z() = 0.5 + 0.5 * pt.z(); |
---|
| 1045 | #else |
---|
| 1046 | *itr = osg::Vec3d(pt.x(), pt.y(), pt.z()) * viewport->computeWindowMatrix(); |
---|
| 1047 | #endif |
---|
| 1048 | if (invertY) { |
---|
| 1049 | itr->y() = viewport->height() - itr->y(); |
---|
| 1050 | } |
---|
| 1051 | TRACE("screen: %g,%g,%g", itr->x(), itr->y(), itr->z()); |
---|
| 1052 | } |
---|
[4645] | 1053 | return ret; |
---|
[4636] | 1054 | } |
---|
| 1055 | |
---|
[4645] | 1056 | bool Renderer::mouseToLatLong(int mouseX, int mouseY, |
---|
| 1057 | double *latitude, double *longitude) |
---|
| 1058 | { |
---|
| 1059 | if (getMapSRS() == NULL) |
---|
| 1060 | return false; |
---|
| 1061 | const osgEarth::SpatialReference *outSRS = |
---|
| 1062 | getMapSRS()->getGeographicSRS(); |
---|
| 1063 | if (outSRS == NULL) |
---|
| 1064 | return false; |
---|
| 1065 | osgEarth::GeoPoint mapPoint; |
---|
| 1066 | if (mapMouseCoords(mouseX, mouseY, mapPoint)) { |
---|
| 1067 | mapPoint = mapPoint.transform(outSRS); |
---|
| 1068 | *longitude = mapPoint.x(); |
---|
| 1069 | *latitude = mapPoint.y(); |
---|
| 1070 | return true; |
---|
| 1071 | } else { |
---|
| 1072 | return false; |
---|
| 1073 | } |
---|
| 1074 | } |
---|
| 1075 | |
---|
[4322] | 1076 | /** |
---|
| 1077 | * \brief Map screen mouse coordinates to map coordinates |
---|
| 1078 | * |
---|
| 1079 | * This method assumes that mouse Y coordinates are 0 at the top |
---|
| 1080 | * of the screen and increase going down if invertY is set, and |
---|
| 1081 | * 0 at the bottom and increase going up otherwise. |
---|
| 1082 | */ |
---|
| 1083 | bool Renderer::mapMouseCoords(float mouseX, float mouseY, |
---|
| 1084 | osgEarth::GeoPoint& map, bool invertY) |
---|
[4028] | 1085 | { |
---|
[4320] | 1086 | if (!_mapNode.valid() || _mapNode->getTerrain() == NULL) { |
---|
[4293] | 1087 | ERROR("No map"); |
---|
| 1088 | return false; |
---|
| 1089 | } |
---|
[4320] | 1090 | if (!_viewer.valid()) { |
---|
[4345] | 1091 | ERROR("No viewer"); |
---|
[4320] | 1092 | return false; |
---|
| 1093 | } |
---|
[4322] | 1094 | if (invertY) { |
---|
| 1095 | mouseY = ((float)_windowHeight - mouseY); |
---|
| 1096 | } |
---|
[4028] | 1097 | osg::Vec3d world; |
---|
[4033] | 1098 | if (_mapNode->getTerrain()->getWorldCoordsUnderMouse(_viewer->asView(), mouseX, mouseY, world)) { |
---|
[4636] | 1099 | map.fromWorld(getMapSRS(), world); |
---|
[4028] | 1100 | return true; |
---|
| 1101 | } |
---|
| 1102 | return false; |
---|
| 1103 | } |
---|
| 1104 | |
---|
[4628] | 1105 | bool Renderer::addImageLayer(const char *name, |
---|
[4284] | 1106 | osgEarth::TileSourceOptions& opts, |
---|
[4246] | 1107 | bool makeShared, |
---|
| 1108 | bool visible) |
---|
[4009] | 1109 | { |
---|
[4293] | 1110 | if (!_map.valid()) { |
---|
| 1111 | ERROR("No map"); |
---|
[4628] | 1112 | return false; |
---|
[4293] | 1113 | } |
---|
[4308] | 1114 | TRACE("layer: %s", name); |
---|
[4285] | 1115 | if (!opts.tileSize().isSet()) { |
---|
| 1116 | opts.tileSize() = 256; |
---|
| 1117 | } |
---|
[4009] | 1118 | osgEarth::ImageLayerOptions layerOpts(name, opts); |
---|
[4643] | 1119 | #if OSGEARTH_MIN_VERSION_REQUIRED(2, 5, 1) |
---|
[4629] | 1120 | layerOpts.textureCompression() = osg::Texture::USE_IMAGE_DATA_FORMAT; |
---|
| 1121 | #endif |
---|
[4246] | 1122 | if (makeShared) { |
---|
| 1123 | layerOpts.shared() = true; |
---|
| 1124 | } |
---|
| 1125 | if (!visible) { |
---|
| 1126 | layerOpts.visible() = false; |
---|
| 1127 | } |
---|
[4628] | 1128 | osg::ref_ptr<osgEarth::ImageLayer> layer = new osgEarth::ImageLayer(layerOpts); |
---|
| 1129 | _map->addImageLayer(layer.get()); |
---|
| 1130 | if (layer->getTileSource() == NULL || !layer->getTileSource()->isOK()) { |
---|
| 1131 | ERROR("Failed to add image layer: %s", name); |
---|
| 1132 | _map->removeImageLayer(layer.get()); |
---|
| 1133 | return false; |
---|
| 1134 | } |
---|
[4086] | 1135 | _needsRedraw = true; |
---|
[4628] | 1136 | return true; |
---|
[4009] | 1137 | } |
---|
| 1138 | |
---|
[4246] | 1139 | void Renderer::addColorFilter(const char *name, |
---|
| 1140 | const char *shader) |
---|
| 1141 | { |
---|
[4293] | 1142 | if (!_map.valid()) { |
---|
| 1143 | ERROR("No map"); |
---|
| 1144 | return; |
---|
| 1145 | } |
---|
| 1146 | osgEarth::ImageLayer *layer = _map->getImageLayerByName(name); |
---|
| 1147 | if (layer == NULL) { |
---|
| 1148 | TRACE("Image layer not found: %s", name); |
---|
| 1149 | return; |
---|
| 1150 | } |
---|
| 1151 | osgEarth::Util::GLSLColorFilter *filter = new osgEarth::Util::GLSLColorFilter; |
---|
| 1152 | filter->setCode(shader); |
---|
| 1153 | //filter->setCode("color.rgb = color.r > 0.5 ? vec3(1.0) : vec3(0.0);"); |
---|
| 1154 | layer->addColorFilter(filter); |
---|
| 1155 | _needsRedraw = true; |
---|
[4246] | 1156 | } |
---|
| 1157 | |
---|
| 1158 | void Renderer::removeColorFilter(const char *name, int idx) |
---|
| 1159 | { |
---|
[4293] | 1160 | if (!_map.valid()) { |
---|
| 1161 | ERROR("No map"); |
---|
| 1162 | return; |
---|
| 1163 | } |
---|
[4246] | 1164 | osgEarth::ImageLayer *layer = _map->getImageLayerByName(name); |
---|
| 1165 | if (layer == NULL) { |
---|
| 1166 | TRACE("Image layer not found: %s", name); |
---|
| 1167 | return; |
---|
| 1168 | } |
---|
| 1169 | if (idx < 0) { |
---|
| 1170 | while (!layer->getColorFilters().empty()) { |
---|
| 1171 | layer->removeColorFilter(layer->getColorFilters()[0]); |
---|
| 1172 | } |
---|
| 1173 | } else { |
---|
| 1174 | layer->removeColorFilter(layer->getColorFilters().at(idx)); |
---|
| 1175 | } |
---|
| 1176 | _needsRedraw = true; |
---|
| 1177 | } |
---|
| 1178 | |
---|
[4009] | 1179 | void Renderer::removeImageLayer(const char *name) |
---|
| 1180 | { |
---|
[4293] | 1181 | if (!_map.valid()) { |
---|
| 1182 | ERROR("No map"); |
---|
| 1183 | return; |
---|
| 1184 | } |
---|
[4009] | 1185 | osgEarth::ImageLayer *layer = _map->getImageLayerByName(name); |
---|
[4036] | 1186 | if (layer != NULL) { |
---|
| 1187 | _map->removeImageLayer(layer); |
---|
[4086] | 1188 | _needsRedraw = true; |
---|
| 1189 | } else { |
---|
| 1190 | TRACE("Image layer not found: %s", name); |
---|
[4036] | 1191 | } |
---|
[4009] | 1192 | } |
---|
| 1193 | |
---|
[4013] | 1194 | void Renderer::moveImageLayer(const char *name, unsigned int pos) |
---|
[4009] | 1195 | { |
---|
[4293] | 1196 | if (!_map.valid()) { |
---|
| 1197 | ERROR("No map"); |
---|
| 1198 | return; |
---|
| 1199 | } |
---|
[4009] | 1200 | osgEarth::ImageLayer *layer = _map->getImageLayerByName(name); |
---|
[4036] | 1201 | if (layer != NULL) { |
---|
| 1202 | _map->moveImageLayer(layer, pos); |
---|
[4086] | 1203 | _needsRedraw = true; |
---|
| 1204 | } else { |
---|
| 1205 | TRACE("Image layer not found: %s", name); |
---|
[4036] | 1206 | } |
---|
[4009] | 1207 | } |
---|
| 1208 | |
---|
| 1209 | void Renderer::setImageLayerOpacity(const char *name, double opacity) |
---|
| 1210 | { |
---|
[4293] | 1211 | if (!_map.valid()) { |
---|
| 1212 | ERROR("No map"); |
---|
| 1213 | return; |
---|
| 1214 | } |
---|
[4009] | 1215 | osgEarth::ImageLayer *layer = _map->getImageLayerByName(name); |
---|
[4036] | 1216 | if (layer != NULL) { |
---|
| 1217 | layer->setOpacity(opacity); |
---|
[4086] | 1218 | _needsRedraw = true; |
---|
| 1219 | } else { |
---|
| 1220 | TRACE("Image layer not found: %s", name); |
---|
[4036] | 1221 | } |
---|
[4009] | 1222 | } |
---|
| 1223 | |
---|
| 1224 | void Renderer::setImageLayerVisibility(const char *name, bool state) |
---|
| 1225 | { |
---|
| 1226 | #if OSGEARTH_MIN_VERSION_REQUIRED(2, 4, 0) |
---|
[4293] | 1227 | if (!_map.valid()) { |
---|
| 1228 | ERROR("No map"); |
---|
| 1229 | return; |
---|
| 1230 | } |
---|
[4009] | 1231 | osgEarth::ImageLayer *layer = _map->getImageLayerByName(name); |
---|
[4036] | 1232 | if (layer != NULL) { |
---|
| 1233 | layer->setVisible(state); |
---|
[4086] | 1234 | _needsRedraw = true; |
---|
| 1235 | } else { |
---|
| 1236 | TRACE("Image layer not found: %s", name); |
---|
[4036] | 1237 | } |
---|
[4009] | 1238 | #endif |
---|
| 1239 | } |
---|
| 1240 | |
---|
[4246] | 1241 | void Renderer::addElevationLayer(const char *name, |
---|
[4284] | 1242 | osgEarth::TileSourceOptions& opts) |
---|
[4013] | 1243 | { |
---|
[4293] | 1244 | if (!_map.valid()) { |
---|
| 1245 | ERROR("No map"); |
---|
| 1246 | return; |
---|
| 1247 | } |
---|
[4308] | 1248 | TRACE("layer: %s", name); |
---|
[4284] | 1249 | if (!opts.tileSize().isSet()) { |
---|
| 1250 | opts.tileSize() = 15; |
---|
| 1251 | } |
---|
| 1252 | osgEarth::ElevationLayerOptions layerOpts(name, opts); |
---|
[4246] | 1253 | // XXX: GDAL does not report vertical datum, it should be specified here |
---|
[4282] | 1254 | // Common options: geodetic (default), egm96, egm84, egm2008 |
---|
| 1255 | //layerOpts.verticalDatum() = "egm96"; |
---|
[4013] | 1256 | _map->addElevationLayer(new osgEarth::ElevationLayer(layerOpts)); |
---|
[4086] | 1257 | _needsRedraw = true; |
---|
[4013] | 1258 | } |
---|
| 1259 | |
---|
| 1260 | void Renderer::removeElevationLayer(const char *name) |
---|
| 1261 | { |
---|
[4293] | 1262 | if (!_map.valid()) { |
---|
| 1263 | ERROR("No map"); |
---|
| 1264 | return; |
---|
| 1265 | } |
---|
[4013] | 1266 | osgEarth::ElevationLayer *layer = _map->getElevationLayerByName(name); |
---|
[4036] | 1267 | if (layer != NULL) { |
---|
| 1268 | _map->removeElevationLayer(layer); |
---|
[4086] | 1269 | _needsRedraw = true; |
---|
| 1270 | } else { |
---|
| 1271 | TRACE("Elevation layer not found: %s", name); |
---|
[4036] | 1272 | } |
---|
[4013] | 1273 | } |
---|
| 1274 | |
---|
| 1275 | void Renderer::moveElevationLayer(const char *name, unsigned int pos) |
---|
| 1276 | { |
---|
[4293] | 1277 | if (!_map.valid()) { |
---|
| 1278 | ERROR("No map"); |
---|
| 1279 | return; |
---|
| 1280 | } |
---|
[4013] | 1281 | osgEarth::ElevationLayer *layer = _map->getElevationLayerByName(name); |
---|
[4036] | 1282 | if (layer != NULL) { |
---|
| 1283 | _map->moveElevationLayer(layer, pos); |
---|
[4086] | 1284 | _needsRedraw = true; |
---|
| 1285 | } else { |
---|
| 1286 | TRACE("Elevation layer not found: %s", name); |
---|
[4036] | 1287 | } |
---|
[4013] | 1288 | } |
---|
| 1289 | |
---|
[4036] | 1290 | void Renderer::setElevationLayerVisibility(const char *name, bool state) |
---|
| 1291 | { |
---|
| 1292 | #if OSGEARTH_MIN_VERSION_REQUIRED(2, 4, 0) |
---|
[4293] | 1293 | if (!_map.valid()) { |
---|
| 1294 | ERROR("No map"); |
---|
| 1295 | return; |
---|
| 1296 | } |
---|
[4036] | 1297 | osgEarth::ElevationLayer *layer = _map->getElevationLayerByName(name); |
---|
| 1298 | if (layer != NULL) { |
---|
| 1299 | layer->setVisible(state); |
---|
[4086] | 1300 | _needsRedraw = true; |
---|
| 1301 | } else { |
---|
| 1302 | TRACE("Elevation layer not found: %s", name); |
---|
[4036] | 1303 | } |
---|
| 1304 | #endif |
---|
| 1305 | } |
---|
| 1306 | |
---|
[4645] | 1307 | void Renderer::initAnnotations() |
---|
[4632] | 1308 | { |
---|
| 1309 | if (!_annotations.valid()) { |
---|
| 1310 | _annotations = new osg::Group(); |
---|
[4645] | 1311 | _annotations->setName("Annotations"); |
---|
[4632] | 1312 | _sceneRoot->addChild(_annotations.get()); |
---|
| 1313 | _placeNodes = new osg::Group(); |
---|
[4645] | 1314 | _placeNodes->setName("Place Nodes"); |
---|
[4632] | 1315 | osgEarth::Decluttering::setEnabled(_placeNodes->getOrCreateStateSet(), true); |
---|
| 1316 | _annotations->addChild(_placeNodes.get()); |
---|
| 1317 | } |
---|
[4645] | 1318 | } |
---|
[4632] | 1319 | |
---|
[4645] | 1320 | void Renderer::initBoxSelection(int x, int y) |
---|
| 1321 | { |
---|
| 1322 | double latitude, longitude; |
---|
| 1323 | if (!mouseToLatLong(x, y, &latitude, &longitude)) { |
---|
| 1324 | return; |
---|
| 1325 | } |
---|
| 1326 | _anchorLat = latitude; |
---|
| 1327 | _anchorLong = longitude; |
---|
| 1328 | addRhumbBox(latitude, latitude, longitude, longitude); |
---|
| 1329 | } |
---|
| 1330 | |
---|
| 1331 | void Renderer::updateBoxSelection(int x, int y) |
---|
| 1332 | { |
---|
| 1333 | osgEarth::Annotation::FeatureNode *node = _selectionBox.get(); |
---|
| 1334 | double nlat, nlong; |
---|
| 1335 | if (!mouseToLatLong(x, y, &nlat, &nlong)) { |
---|
| 1336 | return; |
---|
| 1337 | } |
---|
| 1338 | double latMin, latMax, longMin, longMax; |
---|
| 1339 | if (nlong >= _anchorLong && nlat >= _anchorLat) { |
---|
| 1340 | // +x +y |
---|
| 1341 | longMin = _anchorLong; |
---|
| 1342 | latMin = _anchorLat; |
---|
| 1343 | longMax = nlong; |
---|
| 1344 | latMax = nlat; |
---|
| 1345 | } else if (nlong < _anchorLong && nlat >= _anchorLat) { |
---|
| 1346 | // -x +y |
---|
| 1347 | longMin = nlong; |
---|
| 1348 | latMin = _anchorLat; |
---|
| 1349 | longMax = _anchorLong; |
---|
| 1350 | latMax = nlat; |
---|
| 1351 | } else if (nlong < _anchorLong && nlat < _anchorLat) { |
---|
| 1352 | // -x -y |
---|
| 1353 | longMin = nlong; |
---|
| 1354 | latMin = nlat; |
---|
| 1355 | longMax = _anchorLong; |
---|
| 1356 | latMax = _anchorLat; |
---|
| 1357 | } else { |
---|
| 1358 | // +x -y |
---|
| 1359 | longMin = _anchorLong; |
---|
| 1360 | latMin = nlat; |
---|
| 1361 | longMax = nlong; |
---|
| 1362 | latMax = _anchorLat; |
---|
| 1363 | } |
---|
| 1364 | osgEarth::Symbology::Geometry *geom = node->getFeature()->getGeometry(); |
---|
| 1365 | (*geom)[0] = osg::Vec3d(longMax, latMin, 0); |
---|
| 1366 | (*geom)[1] = osg::Vec3d(longMin, latMin, 0); |
---|
| 1367 | (*geom)[2] = osg::Vec3d(longMin, latMax, 0); |
---|
| 1368 | (*geom)[3] = osg::Vec3d(longMax, latMax, 0); |
---|
| 1369 | node->init(); |
---|
| 1370 | _needsRedraw = true; |
---|
| 1371 | } |
---|
| 1372 | |
---|
| 1373 | void Renderer::clearBoxSelection() |
---|
| 1374 | { |
---|
| 1375 | if (_annotations.valid() && _selectionBox.valid()) { |
---|
| 1376 | _annotations->removeChild(_selectionBox.get()); |
---|
| 1377 | _selectionBox = NULL; |
---|
| 1378 | } |
---|
| 1379 | _needsRedraw = true; |
---|
| 1380 | } |
---|
| 1381 | |
---|
| 1382 | void Renderer::addRhumbBox(double latMin, double latMax, |
---|
| 1383 | double longMin, double longMax) |
---|
| 1384 | { |
---|
| 1385 | if (!_mapNode.valid()) { |
---|
| 1386 | ERROR("No map node"); |
---|
| 1387 | return; |
---|
| 1388 | } |
---|
| 1389 | initAnnotations(); |
---|
| 1390 | |
---|
| 1391 | if (_selectionBox.valid()) { |
---|
| 1392 | osgEarth::Symbology::Geometry *geom = _selectionBox->getFeature()->getGeometry(); |
---|
| 1393 | (*geom)[0] = osg::Vec3d(longMax, latMin, 0); |
---|
| 1394 | (*geom)[1] = osg::Vec3d(longMin, latMin, 0); |
---|
| 1395 | (*geom)[2] = osg::Vec3d(longMin, latMax, 0); |
---|
| 1396 | (*geom)[3] = osg::Vec3d(longMax, latMax, 0); |
---|
| 1397 | _selectionBox->init(); |
---|
| 1398 | } else { |
---|
| 1399 | const osgEarth::SpatialReference* geoSRS = _mapNode->getMapSRS()->getGeographicSRS(); |
---|
| 1400 | osgEarth::Symbology::Geometry *geom = new osgEarth::Symbology::Polygon(); |
---|
| 1401 | geom->push_back(osg::Vec3d(longMax, latMin, 0)); |
---|
| 1402 | geom->push_back(osg::Vec3d(longMin, latMin, 0)); |
---|
| 1403 | geom->push_back(osg::Vec3d(longMin, latMax, 0)); |
---|
| 1404 | geom->push_back(osg::Vec3d(longMax, latMax, 0)); |
---|
| 1405 | osgEarth::Symbology::Style boxStyle; |
---|
| 1406 | #if 1 |
---|
| 1407 | osgEarth::Symbology::PolygonSymbol *poly = boxStyle.getOrCreate<osgEarth::Symbology::PolygonSymbol>(); |
---|
| 1408 | poly->fill()->color() = osgEarth::Symbology::Color::Cyan; |
---|
| 1409 | poly->fill()->color().a() = 0.5; |
---|
[4648] | 1410 | #else |
---|
[4645] | 1411 | osgEarth::Symbology::LineSymbol *line = boxStyle.getOrCreate<osgEarth::Symbology::LineSymbol>(); |
---|
| 1412 | line->stroke()->color() = osgEarth::Symbology::Color::Yellow; |
---|
| 1413 | line->stroke()->width() = 2.0f; |
---|
| 1414 | //line->creaseAngle() = 45.0f; |
---|
| 1415 | line->tessellation() = 10; |
---|
| 1416 | #endif |
---|
| 1417 | osgEarth::Symbology::AltitudeSymbol *alt = boxStyle.getOrCreate<osgEarth::Symbology::AltitudeSymbol>(); |
---|
| 1418 | alt->clamping() = osgEarth::Symbology::AltitudeSymbol::CLAMP_TO_TERRAIN; |
---|
| 1419 | //alt->technique() = osgEarth::Symbology::AltitudeSymbol::TECHNIQUE_GPU; |
---|
| 1420 | alt->technique() = osgEarth::Symbology::AltitudeSymbol::TECHNIQUE_DRAPE; |
---|
| 1421 | //alt->technique() = osgEarth::Symbology::AltitudeSymbol::TECHNIQUE_SCENE; |
---|
| 1422 | #if 0 |
---|
| 1423 | osgEarth::Symbology::RenderSymbol* rs = boxStyle.getOrCreateSymbol<osgEarth::Symbology::RenderSymbol>(); |
---|
| 1424 | rs->depthOffset()->enabled() = true; |
---|
| 1425 | rs->depthOffset()->minBias() = 1000; |
---|
| 1426 | #endif |
---|
| 1427 | osgEarth::Features::Feature *feature = new osgEarth::Features::Feature(geom, geoSRS, boxStyle); |
---|
| 1428 | //feature->geoInterp() = osgEarth::GEOINTERP_GREAT_CIRCLE; |
---|
| 1429 | feature->geoInterp() = osgEarth::GEOINTERP_RHUMB_LINE; |
---|
| 1430 | _selectionBox = |
---|
| 1431 | new osgEarth::Annotation::FeatureNode(_mapNode, feature); |
---|
| 1432 | _annotations->addChild(_selectionBox.get()); |
---|
| 1433 | } |
---|
| 1434 | |
---|
| 1435 | _needsRedraw = true; |
---|
| 1436 | } |
---|
| 1437 | |
---|
| 1438 | void Renderer::addPlaceNode(double latitude, double longitude, char *labelText) |
---|
| 1439 | { |
---|
| 1440 | if (!_mapNode.valid()) { |
---|
| 1441 | ERROR("No map node"); |
---|
| 1442 | return; |
---|
| 1443 | } |
---|
| 1444 | initAnnotations(); |
---|
| 1445 | |
---|
[4632] | 1446 | const osgEarth::SpatialReference* geoSRS = _mapNode->getMapSRS()->getGeographicSRS(); |
---|
| 1447 | |
---|
| 1448 | osgEarth::Symbology::Style pin; |
---|
[4635] | 1449 | pin.getOrCreate<osgEarth::Symbology::IconSymbol>()->url()->setLiteral(getPinIcon()); |
---|
[4645] | 1450 | osgEarth::Annotation::AnnotationNode *anno = |
---|
| 1451 | new osgEarth::Annotation::PlaceNode(_mapNode, osgEarth::GeoPoint(geoSRS, longitude, latitude), labelText, pin); |
---|
[4632] | 1452 | _placeNodes->addChild(anno); |
---|
| 1453 | osgEarth::Annotation::DecorationInstaller |
---|
| 1454 | highlightInstaller("hover", new osgEarth::Annotation::HighlightDecoration()); |
---|
| 1455 | _annotations->accept(highlightInstaller); |
---|
| 1456 | // scale labels when hovering: |
---|
| 1457 | osgEarth::Annotation::DecorationInstaller |
---|
| 1458 | scaleInstaller("hover", new osgEarth::Annotation::ScaleDecoration(1.1f)); |
---|
| 1459 | _placeNodes->accept(scaleInstaller); |
---|
[4645] | 1460 | #if 0 |
---|
| 1461 | writeScene("/tmp/test.osg"); |
---|
| 1462 | #endif |
---|
[4632] | 1463 | _needsRedraw = true; |
---|
| 1464 | } |
---|
| 1465 | |
---|
| 1466 | void Renderer::hoverPlaceNode(int x, int y, bool invertY) |
---|
| 1467 | { |
---|
| 1468 | osgEarth::Picker picker(_viewer.get(), _placeNodes.get()); |
---|
| 1469 | osgEarth::Picker::Hits hits; |
---|
| 1470 | float mouseX = (float)x; |
---|
| 1471 | float mouseY = (float)y; |
---|
| 1472 | if (invertY) { |
---|
| 1473 | mouseY = ((float)_windowHeight - mouseY); |
---|
| 1474 | } |
---|
| 1475 | std::set<osgEarth::Annotation::AnnotationNode*> toUnHover; |
---|
| 1476 | for (std::set<osgEarth::Annotation::AnnotationNode*>::iterator itr = _hovered.begin(); |
---|
| 1477 | itr != _hovered.end(); ++itr) { |
---|
| 1478 | toUnHover.insert(*itr); |
---|
| 1479 | } |
---|
| 1480 | if (picker.pick(mouseX, mouseY, hits)) { |
---|
| 1481 | TRACE("Picker hit!"); |
---|
| 1482 | for (osgEarth::Picker::Hits::const_iterator hitr = hits.begin(); |
---|
| 1483 | hitr != hits.end(); ++hitr) { |
---|
| 1484 | osgEarth::Annotation::AnnotationNode *anno = |
---|
| 1485 | picker.getNode<osgEarth::Annotation::AnnotationNode>(*hitr); |
---|
| 1486 | if (anno != NULL) { |
---|
| 1487 | if (_hovered.find(anno) == _hovered.end()) { |
---|
| 1488 | _hovered.insert(anno); |
---|
| 1489 | anno->setDecoration("hover"); |
---|
| 1490 | _needsRedraw = true; |
---|
| 1491 | } |
---|
| 1492 | toUnHover.erase(anno); |
---|
| 1493 | } |
---|
| 1494 | } |
---|
[4645] | 1495 | #if 0 |
---|
| 1496 | writeScene("/tmp/test.osgt"); |
---|
| 1497 | #endif |
---|
[4632] | 1498 | } |
---|
| 1499 | for (std::set<osgEarth::Annotation::AnnotationNode *>::iterator itr = toUnHover.begin(); |
---|
| 1500 | itr != toUnHover.end(); ++itr) { |
---|
| 1501 | _hovered.erase(*itr); |
---|
| 1502 | (*itr)->clearDecoration(); |
---|
| 1503 | _needsRedraw = true; |
---|
| 1504 | } |
---|
| 1505 | } |
---|
| 1506 | |
---|
| 1507 | void Renderer::deletePlaceNode(int x, int y, bool invertY) |
---|
| 1508 | { |
---|
| 1509 | osgEarth::Picker picker(_viewer.get(), _placeNodes.get()); |
---|
| 1510 | osgEarth::Picker::Hits hits; |
---|
| 1511 | float mouseX = (float)x; |
---|
| 1512 | float mouseY = (float)y; |
---|
| 1513 | if (invertY) { |
---|
| 1514 | mouseY = ((float)_windowHeight - mouseY); |
---|
| 1515 | } |
---|
| 1516 | if (picker.pick(mouseX, mouseY, hits)) { |
---|
| 1517 | TRACE("Picker hit!"); |
---|
| 1518 | // prevent multiple hits on the same instance |
---|
| 1519 | std::set<osgEarth::Annotation::AnnotationNode *> fired; |
---|
| 1520 | for (osgEarth::Picker::Hits::const_iterator hitr = hits.begin(); |
---|
| 1521 | hitr != hits.end(); ++hitr) { |
---|
| 1522 | osgEarth::Annotation::AnnotationNode *anno = |
---|
| 1523 | picker.getNode<osgEarth::Annotation::AnnotationNode>(*hitr); |
---|
| 1524 | if (anno != NULL && fired.find(anno) == fired.end()) { |
---|
| 1525 | fired.insert(anno); |
---|
| 1526 | _needsRedraw = true; |
---|
| 1527 | } |
---|
| 1528 | } |
---|
| 1529 | for (std::set<osgEarth::Annotation::AnnotationNode *>::iterator itr = fired.begin(); |
---|
| 1530 | itr != fired.end(); ++itr) { |
---|
| 1531 | (*itr)->clearDecoration(); |
---|
| 1532 | _placeNodes->removeChild(*itr); |
---|
| 1533 | if (_hovered.find(*itr) != _hovered.end()) { |
---|
| 1534 | _hovered.erase(*itr); |
---|
| 1535 | } |
---|
| 1536 | } |
---|
| 1537 | } else { |
---|
| 1538 | TRACE("NO Picker hits"); |
---|
| 1539 | } |
---|
[4645] | 1540 | #if 0 |
---|
| 1541 | writeScene("/tmp/test.osg"); |
---|
| 1542 | #endif |
---|
[4632] | 1543 | } |
---|
| 1544 | |
---|
[4284] | 1545 | void Renderer::addModelLayer(const char *name, osgEarth::ModelSourceOptions& opts) |
---|
[4013] | 1546 | { |
---|
[4293] | 1547 | if (!_map.valid()) { |
---|
| 1548 | ERROR("No map"); |
---|
| 1549 | return; |
---|
| 1550 | } |
---|
[4308] | 1551 | TRACE("layer: %s", name); |
---|
[4013] | 1552 | osgEarth::ModelLayerOptions layerOpts(name, opts); |
---|
| 1553 | _map->addModelLayer(new osgEarth::ModelLayer(layerOpts)); |
---|
[4086] | 1554 | _needsRedraw = true; |
---|
[4013] | 1555 | } |
---|
| 1556 | |
---|
| 1557 | void Renderer::removeModelLayer(const char *name) |
---|
| 1558 | { |
---|
[4293] | 1559 | if (!_map.valid()) { |
---|
| 1560 | ERROR("No map"); |
---|
| 1561 | return; |
---|
| 1562 | } |
---|
[4013] | 1563 | osgEarth::ModelLayer *layer = _map->getModelLayerByName(name); |
---|
[4036] | 1564 | if (layer != NULL) { |
---|
| 1565 | _map->removeModelLayer(layer); |
---|
[4086] | 1566 | _needsRedraw = true; |
---|
| 1567 | } else { |
---|
| 1568 | TRACE("Model layer not found: %s", name); |
---|
[4036] | 1569 | } |
---|
[4013] | 1570 | } |
---|
| 1571 | |
---|
| 1572 | void Renderer::moveModelLayer(const char *name, unsigned int pos) |
---|
| 1573 | { |
---|
[4293] | 1574 | if (!_map.valid()) { |
---|
| 1575 | ERROR("No map"); |
---|
| 1576 | return; |
---|
| 1577 | } |
---|
[4013] | 1578 | osgEarth::ModelLayer *layer = _map->getModelLayerByName(name); |
---|
[4036] | 1579 | if (layer != NULL) { |
---|
| 1580 | _map->moveModelLayer(layer, pos); |
---|
[4086] | 1581 | _needsRedraw = true; |
---|
| 1582 | } else { |
---|
| 1583 | TRACE("Model layer not found: %s", name); |
---|
[4036] | 1584 | } |
---|
[4013] | 1585 | } |
---|
| 1586 | |
---|
[4054] | 1587 | void Renderer::setModelLayerOpacity(const char *name, double opacity) |
---|
| 1588 | { |
---|
| 1589 | #if OSGEARTH_MIN_VERSION_REQUIRED(2, 5, 0) |
---|
[4293] | 1590 | if (!_map.valid()) { |
---|
| 1591 | ERROR("No map"); |
---|
| 1592 | return; |
---|
| 1593 | } |
---|
[4087] | 1594 | osgEarth::ModelLayer *layer = _map->getModelLayerByName(name); |
---|
[4054] | 1595 | if (layer != NULL) { |
---|
| 1596 | layer->setOpacity(opacity); |
---|
[4086] | 1597 | _needsRedraw = true; |
---|
| 1598 | } else { |
---|
| 1599 | TRACE("Model layer not found: %s", name); |
---|
[4054] | 1600 | } |
---|
| 1601 | #endif |
---|
| 1602 | } |
---|
| 1603 | |
---|
[4036] | 1604 | void Renderer::setModelLayerVisibility(const char *name, bool state) |
---|
| 1605 | { |
---|
| 1606 | #if OSGEARTH_MIN_VERSION_REQUIRED(2, 4, 0) |
---|
[4293] | 1607 | if (!_map.valid()) { |
---|
| 1608 | ERROR("No map"); |
---|
| 1609 | return; |
---|
| 1610 | } |
---|
[4036] | 1611 | osgEarth::ModelLayer *layer = _map->getModelLayerByName(name); |
---|
| 1612 | if (layer != NULL) { |
---|
| 1613 | layer->setVisible(state); |
---|
[4086] | 1614 | _needsRedraw = true; |
---|
| 1615 | } else { |
---|
| 1616 | TRACE("Model layer not found: %s", name); |
---|
[4036] | 1617 | } |
---|
| 1618 | #endif |
---|
| 1619 | } |
---|
| 1620 | |
---|
[3998] | 1621 | /** |
---|
| 1622 | * \brief Resize the render window (image size for renderings) |
---|
| 1623 | */ |
---|
| 1624 | void Renderer::setWindowSize(int width, int height) |
---|
| 1625 | { |
---|
| 1626 | if (_windowWidth == width && |
---|
| 1627 | _windowHeight == height) { |
---|
| 1628 | TRACE("No change"); |
---|
| 1629 | return; |
---|
| 1630 | } |
---|
| 1631 | |
---|
| 1632 | TRACE("Setting window size to %dx%d", width, height); |
---|
| 1633 | |
---|
[4628] | 1634 | double origBitrate = getMaximumBitrate(); |
---|
| 1635 | |
---|
[3998] | 1636 | _windowWidth = width; |
---|
| 1637 | _windowHeight = height; |
---|
[4628] | 1638 | |
---|
| 1639 | setMaximumBitrate(origBitrate); |
---|
[4629] | 1640 | TRACE("Bandwidth target: %.2f Mbps", (float)(getMaximumBitrate()/1.0e6)); |
---|
[4628] | 1641 | TRACE("Frame rate target: %.2f Hz", (float)getMaximumFrameRateInHertz()); |
---|
| 1642 | |
---|
[4293] | 1643 | if (_viewer.valid()) { |
---|
[4320] | 1644 | #ifdef USE_OFFSCREEN_RENDERING |
---|
| 1645 | #ifdef USE_PBUFFER |
---|
| 1646 | osg::ref_ptr<osg::GraphicsContext> pbuffer; |
---|
| 1647 | osg::ref_ptr<osg::GraphicsContext::Traits> traits = new osg::GraphicsContext::Traits; |
---|
| 1648 | traits->x = 0; |
---|
| 1649 | traits->y = 0; |
---|
| 1650 | traits->width = _windowWidth; |
---|
| 1651 | traits->height = _windowHeight; |
---|
| 1652 | traits->red = 8; |
---|
| 1653 | traits->green = 8; |
---|
| 1654 | traits->blue = 8; |
---|
| 1655 | traits->alpha = 8; |
---|
| 1656 | traits->windowDecoration = false; |
---|
| 1657 | traits->pbuffer = true; |
---|
| 1658 | traits->doubleBuffer = true; |
---|
| 1659 | traits->sharedContext = 0; |
---|
| 1660 | |
---|
| 1661 | pbuffer = osg::GraphicsContext::createGraphicsContext(traits.get()); |
---|
| 1662 | if (pbuffer.valid()) { |
---|
| 1663 | TRACE("Pixel buffer has been created successfully."); |
---|
| 1664 | } else { |
---|
| 1665 | ERROR("Pixel buffer has not been created successfully."); |
---|
| 1666 | } |
---|
| 1667 | osg::Camera *camera = new osg::Camera; |
---|
| 1668 | camera->setGraphicsContext(pbuffer.get()); |
---|
| 1669 | camera->setViewport(new osg::Viewport(0, 0, _windowWidth, _windowHeight)); |
---|
| 1670 | GLenum buffer = pbuffer->getTraits()->doubleBuffer ? GL_BACK : GL_FRONT; |
---|
| 1671 | camera->setDrawBuffer(buffer); |
---|
| 1672 | camera->setReadBuffer(buffer); |
---|
| 1673 | camera->setFinalDrawCallback(_captureCallback.get()); |
---|
| 1674 | _viewer->addSlave(camera, osg::Matrixd(), osg::Matrixd()); |
---|
| 1675 | _viewer->realize(); |
---|
| 1676 | #else |
---|
| 1677 | if (_captureCallback.valid()) { |
---|
| 1678 | _captureCallback->getTexture()->setTextureSize(_windowWidth, _windowHeight); |
---|
| 1679 | } |
---|
[4293] | 1680 | osgViewer::ViewerBase::Windows windows; |
---|
| 1681 | _viewer->getWindows(windows); |
---|
| 1682 | if (windows.size() == 1) { |
---|
| 1683 | windows[0]->setWindowRectangle(0, 0, _windowWidth, _windowHeight); |
---|
| 1684 | } else { |
---|
| 1685 | ERROR("Num windows: %lu", windows.size()); |
---|
| 1686 | } |
---|
[4320] | 1687 | #endif |
---|
| 1688 | #else |
---|
| 1689 | osgViewer::ViewerBase::Windows windows; |
---|
| 1690 | _viewer->getWindows(windows); |
---|
| 1691 | if (windows.size() == 1) { |
---|
| 1692 | windows[0]->setWindowRectangle(0, 0, _windowWidth, _windowHeight); |
---|
| 1693 | } else { |
---|
| 1694 | ERROR("Num windows: %lu", windows.size()); |
---|
| 1695 | } |
---|
| 1696 | #endif |
---|
[4349] | 1697 | // HACK: Without this, the mouse coordinate mapping uses the old size |
---|
| 1698 | // for 1 frame. |
---|
| 1699 | assert(_viewer->getEventQueue() != NULL); |
---|
| 1700 | //TRACE("Window EventQueue: %p", getEventQueue()); |
---|
| 1701 | //TRACE("Viewer EventQueue: %p", _viewer->getEventQueue()); |
---|
| 1702 | _viewer->getEventQueue()->windowResize(0, 0, _windowWidth, _windowHeight); |
---|
[4293] | 1703 | _needsRedraw = true; |
---|
[4009] | 1704 | } |
---|
[3998] | 1705 | } |
---|
| 1706 | |
---|
| 1707 | /** |
---|
| 1708 | * \brief Set the orientation of the camera from a quaternion |
---|
| 1709 | * rotation |
---|
| 1710 | * |
---|
| 1711 | * \param[in] quat A quaternion with scalar part first: w,x,y,z |
---|
| 1712 | * \param[in] absolute Is rotation absolute or relative? |
---|
| 1713 | */ |
---|
| 1714 | void Renderer::setCameraOrientation(const double quat[4], bool absolute) |
---|
| 1715 | { |
---|
[4293] | 1716 | if (_manipulator.valid()) { |
---|
| 1717 | _manipulator->setRotation(osg::Quat(quat[1], quat[2], quat[3], quat[0])); |
---|
| 1718 | _needsRedraw = true; |
---|
| 1719 | } |
---|
[3998] | 1720 | } |
---|
| 1721 | |
---|
| 1722 | /** |
---|
| 1723 | * \brief Reset pan, zoom, clipping planes and optionally rotation |
---|
| 1724 | * |
---|
| 1725 | * \param[in] resetOrientation Reset the camera rotation/orientation also |
---|
| 1726 | */ |
---|
| 1727 | void Renderer::resetCamera(bool resetOrientation) |
---|
| 1728 | { |
---|
| 1729 | TRACE("Enter: resetOrientation=%d", resetOrientation ? 1 : 0); |
---|
[4293] | 1730 | if (_viewer.valid()) { |
---|
| 1731 | _viewer->home(); |
---|
| 1732 | _needsRedraw = true; |
---|
| 1733 | } |
---|
[3998] | 1734 | } |
---|
| 1735 | |
---|
| 1736 | /** |
---|
| 1737 | * \brief Perform a 2D translation of the camera |
---|
| 1738 | * |
---|
| 1739 | * x,y pan amount are specified as signed absolute pan amount in viewport |
---|
| 1740 | * units -- i.e. 0 is no pan, .5 is half the viewport, 2 is twice the viewport, |
---|
| 1741 | * etc. |
---|
| 1742 | * |
---|
| 1743 | * \param[in] x Viewport coordinate horizontal panning (positive number pans |
---|
| 1744 | * camera left, object right) |
---|
| 1745 | * \param[in] y Viewport coordinate vertical panning (positive number pans |
---|
| 1746 | * camera up, object down) |
---|
| 1747 | */ |
---|
[4313] | 1748 | void Renderer::panCamera(double x, double y) |
---|
[3998] | 1749 | { |
---|
[4313] | 1750 | TRACE("Enter: %g %g", x, y); |
---|
[3998] | 1751 | |
---|
[4293] | 1752 | if (_manipulator.valid()) { |
---|
| 1753 | // Wants mouse delta x,y in normalized screen coords |
---|
| 1754 | _manipulator->pan(x, y); |
---|
| 1755 | _needsRedraw = true; |
---|
| 1756 | } |
---|
[3998] | 1757 | } |
---|
| 1758 | |
---|
[4313] | 1759 | void Renderer::rotateCamera(double x, double y) |
---|
[4009] | 1760 | { |
---|
[4313] | 1761 | TRACE("Enter: %g %g", x, y); |
---|
[4009] | 1762 | |
---|
[4293] | 1763 | if (_manipulator.valid()) { |
---|
| 1764 | _manipulator->rotate(x, y); |
---|
| 1765 | _needsRedraw = true; |
---|
| 1766 | } |
---|
[4009] | 1767 | } |
---|
| 1768 | |
---|
[3998] | 1769 | /** |
---|
| 1770 | * \brief Dolly camera or set orthographic scaling based on camera type |
---|
| 1771 | * |
---|
[4316] | 1772 | * \param[in] y Mouse y coordinate in normalized screen coords |
---|
[3998] | 1773 | */ |
---|
[4316] | 1774 | void Renderer::zoomCamera(double y) |
---|
[3998] | 1775 | { |
---|
[4316] | 1776 | TRACE("Enter: y: %g", y); |
---|
[3998] | 1777 | |
---|
[4293] | 1778 | if (_manipulator.valid()) { |
---|
[4645] | 1779 | // +y = zoom out, -y = zoom in |
---|
[4316] | 1780 | TRACE("camDist: %g", _manipulator->getDistance()); |
---|
[4645] | 1781 | if ((_mapScale < 0.0 && y > 0.0) || |
---|
| 1782 | (_mapScale < 0.1 && y < 0.0) || |
---|
| 1783 | (_mapScale > 40000.0 && y > 0.0)) |
---|
| 1784 | return; |
---|
[4328] | 1785 | #if 1 |
---|
| 1786 | _manipulator->zoom(0, y); |
---|
| 1787 | #else |
---|
[4293] | 1788 | double dist = _manipulator->getDistance(); |
---|
[4316] | 1789 | dist *= (1.0 + y); |
---|
[4293] | 1790 | _manipulator->setDistance(dist); |
---|
[4328] | 1791 | #endif |
---|
[4293] | 1792 | _needsRedraw = true; |
---|
| 1793 | } |
---|
[3998] | 1794 | } |
---|
| 1795 | |
---|
[4315] | 1796 | /** |
---|
| 1797 | * \brief Dolly camera to set distance from focal point |
---|
| 1798 | * |
---|
| 1799 | * \param[in] dist distance in map? coordinates |
---|
| 1800 | */ |
---|
| 1801 | void Renderer::setCameraDistance(double dist) |
---|
| 1802 | { |
---|
[4316] | 1803 | TRACE("Enter: dist: %g", dist); |
---|
[4315] | 1804 | |
---|
| 1805 | if (_manipulator.valid()) { |
---|
[4316] | 1806 | TRACE("camDist: %g", _manipulator->getDistance()); |
---|
| 1807 | |
---|
[4315] | 1808 | _manipulator->setDistance(dist); |
---|
| 1809 | |
---|
| 1810 | _needsRedraw = true; |
---|
| 1811 | } |
---|
| 1812 | } |
---|
| 1813 | |
---|
[4033] | 1814 | void Renderer::keyPress(int key) |
---|
| 1815 | { |
---|
[4293] | 1816 | osgGA::EventQueue *queue = getEventQueue(); |
---|
| 1817 | if (queue != NULL) { |
---|
| 1818 | queue->keyPress(key); |
---|
| 1819 | _needsRedraw = true; |
---|
| 1820 | } |
---|
[4033] | 1821 | } |
---|
| 1822 | |
---|
| 1823 | void Renderer::keyRelease(int key) |
---|
| 1824 | { |
---|
[4293] | 1825 | osgGA::EventQueue *queue = getEventQueue(); |
---|
| 1826 | if (queue != NULL) { |
---|
| 1827 | queue->keyRelease(key); |
---|
| 1828 | _needsRedraw = true; |
---|
| 1829 | } |
---|
[4033] | 1830 | } |
---|
| 1831 | |
---|
[4028] | 1832 | void Renderer::setThrowingEnabled(bool state) |
---|
| 1833 | { |
---|
[4282] | 1834 | if (_manipulator.valid()) { |
---|
| 1835 | _manipulator->getSettings()->setThrowingEnabled(state); |
---|
| 1836 | } |
---|
[4028] | 1837 | } |
---|
| 1838 | |
---|
[4014] | 1839 | void Renderer::mouseDoubleClick(int button, double x, double y) |
---|
| 1840 | { |
---|
[4293] | 1841 | osgGA::EventQueue *queue = getEventQueue(); |
---|
| 1842 | if (queue != NULL) { |
---|
| 1843 | queue->mouseDoubleButtonPress((float)x, (float)y, button); |
---|
| 1844 | _needsRedraw = true; |
---|
| 1845 | } |
---|
[4014] | 1846 | } |
---|
| 1847 | |
---|
| 1848 | void Renderer::mouseClick(int button, double x, double y) |
---|
| 1849 | { |
---|
[4293] | 1850 | osgGA::EventQueue *queue = getEventQueue(); |
---|
| 1851 | if (queue != NULL) { |
---|
| 1852 | queue->mouseButtonPress((float)x, (float)y, button); |
---|
| 1853 | _needsRedraw = true; |
---|
| 1854 | } |
---|
[4014] | 1855 | } |
---|
| 1856 | |
---|
| 1857 | void Renderer::mouseDrag(int button, double x, double y) |
---|
| 1858 | { |
---|
[4293] | 1859 | osgGA::EventQueue *queue = getEventQueue(); |
---|
| 1860 | if (queue != NULL) { |
---|
| 1861 | queue->mouseMotion((float)x, (float)y); |
---|
| 1862 | _needsRedraw = true; |
---|
| 1863 | } |
---|
[4014] | 1864 | } |
---|
| 1865 | |
---|
| 1866 | void Renderer::mouseRelease(int button, double x, double y) |
---|
| 1867 | { |
---|
[4293] | 1868 | osgGA::EventQueue *queue = getEventQueue(); |
---|
| 1869 | if (queue != NULL) { |
---|
| 1870 | queue->mouseButtonRelease((float)x, (float)y, button); |
---|
| 1871 | _needsRedraw = true; |
---|
| 1872 | } |
---|
[4014] | 1873 | } |
---|
| 1874 | |
---|
| 1875 | void Renderer::mouseMotion(double x, double y) |
---|
| 1876 | { |
---|
[4307] | 1877 | if (_mouseCoordsTool.valid()) { |
---|
| 1878 | #if 1 |
---|
[4293] | 1879 | osgGA::EventQueue *queue = getEventQueue(); |
---|
| 1880 | if (queue != NULL) { |
---|
| 1881 | queue->mouseMotion((float)x, (float)y); |
---|
[4308] | 1882 | _needsRedraw = true; |
---|
[4293] | 1883 | } |
---|
[4307] | 1884 | #else |
---|
[4322] | 1885 | if (_viewer.valid() && _coordsCallback.valid()) { |
---|
| 1886 | osgEarth::GeoPoint mapPt; |
---|
| 1887 | if (mapMouseCoords((float)x, (float)y, mapPt)) { |
---|
| 1888 | _coordsCallback->set(mapPt, _viewer->asView(), _mapNode); |
---|
[4307] | 1889 | } else { |
---|
[4322] | 1890 | _coordsCallback->reset(_viewer->asView(), _mapNode); |
---|
[4307] | 1891 | } |
---|
[4322] | 1892 | _needsRedraw = true; |
---|
[4307] | 1893 | } |
---|
[4293] | 1894 | #endif |
---|
[4028] | 1895 | } |
---|
[4014] | 1896 | } |
---|
| 1897 | |
---|
| 1898 | void Renderer::mouseScroll(int direction) |
---|
| 1899 | { |
---|
[4293] | 1900 | osgGA::EventQueue *queue = getEventQueue(); |
---|
| 1901 | if (queue != NULL) { |
---|
| 1902 | queue->mouseScroll((direction > 0 ? osgGA::GUIEventAdapter::SCROLL_UP : osgGA::GUIEventAdapter::SCROLL_DOWN)); |
---|
| 1903 | _needsRedraw = true; |
---|
| 1904 | } |
---|
[4014] | 1905 | } |
---|
| 1906 | |
---|
[3998] | 1907 | /** |
---|
| 1908 | * \brief Set the RGB background color to render into the image |
---|
| 1909 | */ |
---|
| 1910 | void Renderer::setBackgroundColor(float color[3]) |
---|
| 1911 | { |
---|
| 1912 | _bgColor[0] = color[0]; |
---|
| 1913 | _bgColor[1] = color[1]; |
---|
| 1914 | _bgColor[2] = color[2]; |
---|
| 1915 | |
---|
[4293] | 1916 | if (_viewer.valid()) { |
---|
| 1917 | _viewer->getCamera()->setClearColor(osg::Vec4(color[0], color[1], color[2], 1)); |
---|
[3998] | 1918 | |
---|
[4293] | 1919 | _needsRedraw = true; |
---|
| 1920 | } |
---|
[3998] | 1921 | } |
---|
| 1922 | |
---|
| 1923 | /** |
---|
| 1924 | * \brief Sets flag to trigger rendering next time render() is called |
---|
| 1925 | */ |
---|
| 1926 | void Renderer::eventuallyRender() |
---|
| 1927 | { |
---|
| 1928 | _needsRedraw = true; |
---|
| 1929 | } |
---|
| 1930 | |
---|
[4033] | 1931 | /** |
---|
| 1932 | * \brief Get a timeout in usecs for select() |
---|
| 1933 | * |
---|
| 1934 | * If the paging thread is idle, returns <0 indicating that the |
---|
| 1935 | * select call can block until data is available. Otherwise, |
---|
| 1936 | * if the frame render time was faster than the target frame |
---|
| 1937 | * rate, return the remaining frame time. |
---|
| 1938 | */ |
---|
[4025] | 1939 | long Renderer::getTimeout() |
---|
| 1940 | { |
---|
[4033] | 1941 | if (!checkNeedToDoFrame()) |
---|
| 1942 | // <0 means no timeout, block until socket has data |
---|
| 1943 | return -1L; |
---|
[4025] | 1944 | if (_lastFrameTime < _minFrameTime) { |
---|
[4632] | 1945 | return (long)1.0e6*(_minFrameTime - _lastFrameTime); |
---|
[4025] | 1946 | } else { |
---|
[4033] | 1947 | // No timeout (poll) |
---|
[4025] | 1948 | return 0L; |
---|
| 1949 | } |
---|
| 1950 | } |
---|
| 1951 | |
---|
[4033] | 1952 | /** |
---|
| 1953 | * \brief Check if paging thread is quiescent |
---|
| 1954 | */ |
---|
[4027] | 1955 | bool Renderer::isPagerIdle() |
---|
[4025] | 1956 | { |
---|
[4293] | 1957 | if (!_viewer.valid()) |
---|
| 1958 | return true; |
---|
| 1959 | else |
---|
| 1960 | return (!_viewer->getDatabasePager()->requiresUpdateSceneGraph() && |
---|
| 1961 | !_viewer->getDatabasePager()->getRequestsInProgress()); |
---|
[4025] | 1962 | } |
---|
| 1963 | |
---|
[3998] | 1964 | /** |
---|
[4033] | 1965 | * \brief Check is frame call is necessary to render and/or update |
---|
| 1966 | * in response to events or timed actions |
---|
| 1967 | */ |
---|
| 1968 | bool Renderer::checkNeedToDoFrame() |
---|
| 1969 | { |
---|
| 1970 | return (_needsRedraw || |
---|
[4293] | 1971 | (_viewer.valid() && _viewer->checkNeedToDoFrame())); |
---|
[4033] | 1972 | } |
---|
| 1973 | |
---|
| 1974 | /** |
---|
[4349] | 1975 | * \brief MapNode event phase |
---|
| 1976 | * |
---|
| 1977 | * This is called by the MapNode's event callback during the event |
---|
| 1978 | * traversal of the viewer |
---|
| 1979 | */ |
---|
| 1980 | void Renderer::mapNodeUpdate() |
---|
| 1981 | { |
---|
| 1982 | computeMapScale(); |
---|
| 1983 | } |
---|
| 1984 | |
---|
[4628] | 1985 | void Renderer::markFrameStart() |
---|
| 1986 | { |
---|
| 1987 | _startFrameTime = osg::Timer::instance()->tick(); |
---|
| 1988 | } |
---|
| 1989 | |
---|
| 1990 | void Renderer::markFrameEnd() |
---|
| 1991 | { |
---|
| 1992 | osg::Timer_t endFrameTick = osg::Timer::instance()->tick(); |
---|
| 1993 | _lastFrameTime = osg::Timer::instance()->delta_s(_startFrameTime, endFrameTick); |
---|
[4632] | 1994 | if (_lastFrameTime > _minFrameTime) { |
---|
| 1995 | ERROR("BROKE FRAME by %.2f msec", (_lastFrameTime - _minFrameTime)*1000.0f); |
---|
| 1996 | } else { |
---|
| 1997 | TRACE("Frame time: %.2f msec", _lastFrameTime*1000.0f); |
---|
| 1998 | } |
---|
[4628] | 1999 | #ifdef USE_THROTTLING_SLEEP |
---|
| 2000 | if (_lastFrameTime < _minFrameTime) { |
---|
[4632] | 2001 | TRACE("Sleeping for %.2f msec", (_minFrameTime - _lastFrameTime)*1000.0f); |
---|
[4628] | 2002 | OpenThreads::Thread::microSleep(static_cast<unsigned int>(1000000.0*(_minFrameTime - _lastFrameTime))); |
---|
| 2003 | } |
---|
| 2004 | #endif |
---|
| 2005 | } |
---|
| 2006 | |
---|
[4349] | 2007 | /** |
---|
[3998] | 2008 | * \brief Cause the rendering to render a new image if needed |
---|
| 2009 | * |
---|
| 2010 | * The _needsRedraw flag indicates if a state change has occured since |
---|
| 2011 | * the last rendered frame |
---|
| 2012 | */ |
---|
| 2013 | bool Renderer::render() |
---|
| 2014 | { |
---|
[4293] | 2015 | if (_viewer.valid() && checkNeedToDoFrame()) { |
---|
[4028] | 2016 | TRACE("Enter needsRedraw=%d", _needsRedraw ? 1 : 0); |
---|
[4632] | 2017 | _renderStartTime = osg::Timer::instance()->tick(); |
---|
[4009] | 2018 | TRACE("Before frame()"); |
---|
[3998] | 2019 | _viewer->frame(); |
---|
[4009] | 2020 | TRACE("After frame()"); |
---|
[4632] | 2021 | _renderStopTime = osg::Timer::instance()->tick(); |
---|
| 2022 | _renderTime = osg::Timer::instance()->delta_s(_renderStartTime, _renderStopTime); |
---|
| 2023 | TRACE("Render time: %g msec", _renderTime * 1000.0); |
---|
[4628] | 2024 | #ifndef SLEEP_AFTER_QUEUE_FRAME |
---|
[4632] | 2025 | _lastFrameTime = _renderTime; |
---|
[4628] | 2026 | #ifdef USE_THROTTLING_SLEEP |
---|
| 2027 | if (_lastFrameTime < _minFrameTime) { |
---|
[4632] | 2028 | TRACE("Sleeping for %.2f msec", (_minFrameTime - _lastFrameTime)*1000.0f); |
---|
| 2029 | OpenThreads::Thread::microSleep(static_cast<unsigned int>(1.0e6*(_minFrameTime - _lastFrameTime))); |
---|
[4009] | 2030 | } |
---|
[4025] | 2031 | #endif |
---|
[4628] | 2032 | #endif |
---|
[4028] | 2033 | #ifdef WANT_TRACE |
---|
[4009] | 2034 | if (_viewer->getViewerStats() != NULL) { |
---|
| 2035 | _viewer->getViewerStats()->report(std::cerr, _viewer->getViewerStats()->getLatestFrameNumber()); |
---|
| 2036 | } |
---|
[4028] | 2037 | #endif |
---|
[3998] | 2038 | _needsRedraw = false; |
---|
| 2039 | return true; |
---|
[4632] | 2040 | } else { |
---|
| 2041 | _renderStartTime = _renderStopTime = osg::Timer::instance()->tick(); |
---|
| 2042 | _renderTime = 0; |
---|
[3998] | 2043 | return false; |
---|
[4632] | 2044 | } |
---|
[3998] | 2045 | } |
---|
| 2046 | |
---|
| 2047 | /** |
---|
| 2048 | * \brief Read back the rendered framebuffer image |
---|
| 2049 | */ |
---|
[4009] | 2050 | osg::Image *Renderer::getRenderedFrame() |
---|
[3998] | 2051 | { |
---|
[4293] | 2052 | if (_captureCallback.valid()) |
---|
| 2053 | return _captureCallback->getImage(); |
---|
| 2054 | else |
---|
| 2055 | return NULL; |
---|
[3998] | 2056 | } |
---|
[4345] | 2057 | |
---|
[4349] | 2058 | void Renderer::setScaleBar(bool state) |
---|
| 2059 | { |
---|
| 2060 | if (_scaleLabel.valid()) { |
---|
| 2061 | _scaleLabel->setVisible(state); |
---|
| 2062 | } |
---|
| 2063 | if (_scaleBar.valid()) { |
---|
| 2064 | _scaleBar->setVisible(state); |
---|
| 2065 | } |
---|
| 2066 | _needsRedraw = true; |
---|
| 2067 | } |
---|
| 2068 | |
---|
| 2069 | void Renderer::setScaleBarUnits(ScaleBarUnits units) |
---|
| 2070 | { |
---|
| 2071 | _scaleBarUnits = units; |
---|
| 2072 | _needsRedraw = true; |
---|
| 2073 | } |
---|
| 2074 | |
---|
[4345] | 2075 | /** |
---|
| 2076 | * \brief Compute the scale ratio of the map based on a horizontal center line |
---|
| 2077 | * |
---|
| 2078 | * The idea here is to take 2 screen points on a horizontal line in the center |
---|
| 2079 | * of the screen and convert to lat/long. The lat/long coordinates are then |
---|
| 2080 | * used to compute the great circle distance (assuming spherical earth) between |
---|
| 2081 | * the points. |
---|
| 2082 | * |
---|
| 2083 | * We could use local projected map coordinates for the distance computation, |
---|
| 2084 | * which would be faster; however, this would not show e.g. the change in |
---|
| 2085 | * scale at different latitudes |
---|
| 2086 | */ |
---|
| 2087 | double Renderer::computeMapScale() |
---|
| 2088 | { |
---|
[4349] | 2089 | if (!_scaleLabel.valid() || !_scaleLabel->visible()) { |
---|
| 2090 | return -1.0; |
---|
| 2091 | } |
---|
[4345] | 2092 | if (!_mapNode.valid() || _mapNode->getTerrain() == NULL) { |
---|
| 2093 | ERROR("No map"); |
---|
| 2094 | return -1.0; |
---|
| 2095 | } |
---|
| 2096 | if (!_viewer.valid()) { |
---|
| 2097 | ERROR("No viewer"); |
---|
| 2098 | return -1.0; |
---|
| 2099 | } |
---|
| 2100 | |
---|
| 2101 | double x, y; |
---|
| 2102 | double pixelWidth = _windowWidth * 0.1 * 2.0; |
---|
| 2103 | if (pixelWidth < 10) |
---|
| 2104 | pixelWidth = 10; |
---|
| 2105 | if (pixelWidth > 150) |
---|
| 2106 | pixelWidth = 150; |
---|
| 2107 | x = (double)(_windowWidth -1)/2.0 - pixelWidth / 2.0; |
---|
| 2108 | y = (double)(_windowHeight-1)/2.0; |
---|
| 2109 | |
---|
| 2110 | osg::Vec3d world1, world2; |
---|
| 2111 | if (!_mapNode->getTerrain()->getWorldCoordsUnderMouse(_viewer->asView(), x, y, world1)) { |
---|
| 2112 | // off map |
---|
| 2113 | TRACE("Off map coords: %g %g", x, y); |
---|
| 2114 | _scaleLabel->setText(""); |
---|
| 2115 | _scaleBar->setWidth(0); |
---|
| 2116 | return -1.0; |
---|
| 2117 | } |
---|
| 2118 | x += pixelWidth; |
---|
| 2119 | if (!_mapNode->getTerrain()->getWorldCoordsUnderMouse(_viewer->asView(), x, y, world2)) { |
---|
| 2120 | // off map |
---|
| 2121 | TRACE("Off map coords: %g %g", x, y); |
---|
| 2122 | _scaleLabel->setText(""); |
---|
| 2123 | _scaleBar->setWidth(0); |
---|
| 2124 | return -1.0; |
---|
| 2125 | } |
---|
| 2126 | |
---|
| 2127 | TRACE("w1: %g %g %g w2: %g %g %g", |
---|
| 2128 | world1.x(), world1.y(), world1.z(), |
---|
| 2129 | world2.x(), world2.y(), world2.z()); |
---|
| 2130 | |
---|
| 2131 | double meters; |
---|
| 2132 | double radius = 6378137.0; |
---|
| 2133 | if (_mapNode->getMapSRS() && |
---|
| 2134 | _mapNode->getMapSRS()->getEllipsoid()) { |
---|
| 2135 | radius = _mapNode->getMapSRS()->getEllipsoid()->getRadiusEquator(); |
---|
| 2136 | } |
---|
| 2137 | if (!_map->isGeocentric() && |
---|
| 2138 | _mapNode->getMapSRS() && |
---|
| 2139 | _mapNode->getMapSRS()->isGeographic()) { |
---|
| 2140 | TRACE("Map is geographic"); |
---|
| 2141 | // World cords are already lat/long |
---|
| 2142 | // Compute great circle distance |
---|
| 2143 | meters = |
---|
| 2144 | osgEarth::GeoMath::distance(world1, world2, _mapNode->getMapSRS()); |
---|
| 2145 | } else if (_mapNode->getMapSRS()) { |
---|
| 2146 | // Get map coords in lat/long |
---|
| 2147 | osgEarth::GeoPoint mapPoint1, mapPoint2; |
---|
| 2148 | mapPoint1.fromWorld(_mapNode->getMapSRS(), world1); |
---|
| 2149 | mapPoint1.makeGeographic(); |
---|
| 2150 | mapPoint2.fromWorld(_mapNode->getMapSRS(), world2); |
---|
| 2151 | mapPoint2.makeGeographic(); |
---|
| 2152 | // Compute great circle distance |
---|
| 2153 | meters = |
---|
| 2154 | osgEarth::GeoMath::distance(osg::DegreesToRadians(mapPoint1.y()), |
---|
| 2155 | osg::DegreesToRadians(mapPoint1.x()), |
---|
| 2156 | osg::DegreesToRadians(mapPoint2.y()), |
---|
| 2157 | osg::DegreesToRadians(mapPoint2.x()), |
---|
| 2158 | radius); |
---|
| 2159 | } else { |
---|
| 2160 | // Assume geocentric? |
---|
| 2161 | ERROR("No map SRS"); |
---|
| 2162 | _scaleLabel->setText(""); |
---|
| 2163 | _scaleBar->setWidth(0); |
---|
| 2164 | return -1.0; |
---|
| 2165 | } |
---|
| 2166 | |
---|
| 2167 | double scale = meters / pixelWidth; |
---|
[4349] | 2168 | // 1mi = 5280 feet |
---|
| 2169 | //double scaleMiles = scale / 1609.344; // International mile = 1609.344m |
---|
| 2170 | //double scaleNauticalMiles = scale / 1852.0; // nautical mile = 1852m |
---|
| 2171 | //double scaleUSSurveyMiles = scale / 1609.347218694; // US survey mile = 5280 US survey feet |
---|
| 2172 | //double scaleUSSurveyFeet = scale * 3937.0/1200.0; // US survey foot = 1200/3937 m |
---|
[4345] | 2173 | TRACE("m: %g px: %g m/px: %g", meters, pixelWidth, scale); |
---|
[4645] | 2174 | _mapScale = scale; |
---|
[4349] | 2175 | switch (_scaleBarUnits) { |
---|
| 2176 | case UNITS_NAUTICAL_MILES: { |
---|
| 2177 | double nmi = meters / 1852.0; |
---|
| 2178 | scale = nmi / pixelWidth; |
---|
| 2179 | nmi = normalizeScaleNauticalMiles(nmi); |
---|
| 2180 | pixelWidth = nmi / scale; |
---|
| 2181 | if (_scaleLabel.valid()) { |
---|
| 2182 | _scaleLabel->setText(osgEarth::Stringify() |
---|
| 2183 | << nmi |
---|
| 2184 | << " nmi"); |
---|
| 2185 | } |
---|
[4345] | 2186 | } |
---|
[4349] | 2187 | break; |
---|
[4354] | 2188 | case UNITS_US_SURVEY_FEET: { |
---|
| 2189 | double feet = meters * 3937.0/1200.0; |
---|
| 2190 | scale = feet / pixelWidth; |
---|
| 2191 | feet = normalizeScaleFeet(feet); |
---|
| 2192 | pixelWidth = feet / scale; |
---|
| 2193 | if (_scaleLabel.valid()) { |
---|
| 2194 | if (feet >= 5280) { |
---|
| 2195 | _scaleLabel->setText(osgEarth::Stringify() |
---|
| 2196 | << feet / 5280.0 |
---|
| 2197 | << " miUS"); |
---|
| 2198 | } else { |
---|
| 2199 | _scaleLabel->setText(osgEarth::Stringify() |
---|
| 2200 | << feet |
---|
| 2201 | << " ftUS"); |
---|
| 2202 | } |
---|
| 2203 | } |
---|
| 2204 | } |
---|
| 2205 | break; |
---|
[4349] | 2206 | case UNITS_INTL_FEET: { |
---|
[4354] | 2207 | double feet = 5280.0 * meters / 1609.344; |
---|
[4349] | 2208 | scale = feet / pixelWidth; |
---|
| 2209 | feet = normalizeScaleFeet(feet); |
---|
| 2210 | pixelWidth = feet / scale; |
---|
| 2211 | if (_scaleLabel.valid()) { |
---|
| 2212 | if (feet >= 5280) { |
---|
| 2213 | _scaleLabel->setText(osgEarth::Stringify() |
---|
| 2214 | << feet / 5280.0 |
---|
| 2215 | << " mi"); |
---|
| 2216 | } else { |
---|
| 2217 | _scaleLabel->setText(osgEarth::Stringify() |
---|
| 2218 | << feet |
---|
| 2219 | << " ft"); |
---|
| 2220 | } |
---|
| 2221 | } |
---|
[4345] | 2222 | } |
---|
[4349] | 2223 | break; |
---|
| 2224 | case UNITS_METERS: |
---|
| 2225 | default: { |
---|
| 2226 | meters = normalizeScaleMeters(meters); |
---|
| 2227 | pixelWidth = meters / scale; |
---|
| 2228 | if (_scaleLabel.valid()) { |
---|
| 2229 | if (meters >= 1000) { |
---|
| 2230 | _scaleLabel->setText(osgEarth::Stringify() |
---|
| 2231 | << meters / 1000.0 |
---|
| 2232 | << " km"); |
---|
| 2233 | } else { |
---|
| 2234 | _scaleLabel->setText(osgEarth::Stringify() |
---|
| 2235 | << meters |
---|
| 2236 | << " m"); |
---|
| 2237 | } |
---|
| 2238 | } |
---|
| 2239 | } |
---|
| 2240 | break; |
---|
| 2241 | } |
---|
[4345] | 2242 | if (_scaleBar.valid()) { |
---|
| 2243 | _scaleBar->setWidth(pixelWidth); |
---|
| 2244 | } |
---|
| 2245 | return scale; |
---|
| 2246 | } |
---|
[4382] | 2247 | |
---|
| 2248 | std::string Renderer::getCanonicalPath(const std::string& url) const |
---|
| 2249 | { |
---|
| 2250 | std::string retStr; |
---|
| 2251 | std::string proto = osgDB::getServerProtocol(url); |
---|
| 2252 | if (proto.empty()) { |
---|
| 2253 | retStr = osgDB::getRealPath(url); |
---|
| 2254 | if (!osgDB::fileExists(retStr)) { |
---|
| 2255 | retStr = ""; |
---|
| 2256 | } |
---|
[4466] | 2257 | } else { |
---|
| 2258 | retStr = url; |
---|
[4382] | 2259 | } |
---|
| 2260 | return retStr; |
---|
| 2261 | } |
---|
[4645] | 2262 | |
---|
| 2263 | void Renderer::writeScene(const std::string& file) |
---|
| 2264 | { |
---|
| 2265 | if (_sceneRoot.valid()) { |
---|
| 2266 | osgDB::writeNodeFile(*_sceneRoot.get(), file); |
---|
| 2267 | } |
---|
| 2268 | } |
---|