source: trunk/packages/vizservers/geovis/Renderer.cpp @ 4369

Last change on this file since 4369 was 4354, checked in by ldelgass, 10 years ago

Set screen number from DISPLAY in geovis

File size: 51.8 KB
Line 
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>
12#include <cstdlib>
13
14#include <GL/gl.h>
15
16#ifdef WANT_TRACE
17#include <sys/time.h>
18#endif
19
20#include <osgGA/StateSetManipulator>
21#include <osgGA/GUIEventAdapter>
22#include <osgViewer/ViewerEventHandlers>
23
24#include <osgEarth/Version>
25#include <osgEarth/MapNode>
26#include <osgEarth/Bounds>
27#include <osgEarth/Profile>
28#include <osgEarth/Viewpoint>
29#include <osgEarth/TerrainLayer>
30#include <osgEarth/ImageLayer>
31#include <osgEarth/ElevationLayer>
32#include <osgEarth/ModelLayer>
33#include <osgEarthUtil/EarthManipulator>
34#if OSGEARTH_MIN_VERSION_REQUIRED(2, 5, 1)
35#include <osgEarthUtil/Sky>
36#else
37#include <osgEarthUtil/SkyNode>
38#endif
39#include <osgEarthUtil/AutoClipPlaneHandler>
40#include <osgEarthUtil/MouseCoordsTool>
41#include <osgEarthUtil/UTMGraticule>
42#include <osgEarthUtil/MGRSGraticule>
43#include <osgEarthUtil/GeodeticGraticule>
44#include <osgEarthUtil/LatLongFormatter>
45#include <osgEarthUtil/MGRSFormatter>
46#include <osgEarthUtil/GLSLColorFilter>
47#include <osgEarthUtil/VerticalScale>
48#include <osgEarthDrivers/gdal/GDALOptions>
49#include <osgEarthDrivers/engine_mp/MPTerrainEngineOptions>
50
51#include "Renderer.h"
52#include "ScaleBar.h"
53#include "Trace.h"
54
55#define MSECS_ELAPSED(t1, t2) \
56    ((t1).tv_sec == (t2).tv_sec ? (((t2).tv_usec - (t1).tv_usec)/1.0e+3) : \
57     (((t2).tv_sec - (t1).tv_sec))*1.0e+3 + (double)((t2).tv_usec - (t1).tv_usec)/1.0e+3)
58
59#define BASE_IMAGE "/usr/share/osgearth/data/world.tif"
60
61using namespace GeoVis;
62
63Renderer::Renderer() :
64    _needsRedraw(false),
65    _windowWidth(500),
66    _windowHeight(500),
67    _scaleBarUnits(UNITS_METERS)
68{
69    TRACE("Enter");
70
71    _bgColor[0] = 0;
72    _bgColor[1] = 0;
73    _bgColor[2] = 0;
74    _minFrameTime = 1.0/30.0;
75    _lastFrameTime = _minFrameTime;
76
77    char *base = getenv("MAP_BASE_URI");
78    if (base != NULL) {
79        _baseURI = base;
80        TRACE("Setting base URI: %s", _baseURI.c_str());
81    }
82
83#if 0
84    initViewer();
85
86    osgEarth::MapOptions mapOpts;
87    mapOpts.coordSysType() = osgEarth::MapOptions::CSTYPE_PROJECTED;
88    mapOpts.profile() = osgEarth::ProfileOptions("global-mercator");
89    osgEarth::Map *map = new osgEarth::Map(mapOpts);
90    _map = map;
91    osgEarth::Drivers::GDALOptions bopts;
92    bopts.url() = BASE_IMAGE;
93    addImageLayer("base", bopts);
94    osgEarth::Drivers::MPTerrainEngineOptions mpOpt;
95    // Set background layer color
96    mpOpt.color() = osg::Vec4(1, 1, 1, 1);
97    //mpOpt.minLOD() = 1;
98    osgEarth::MapNodeOptions mapNodeOpts(mpOpt);
99    mapNodeOpts.enableLighting() = false;
100    osgEarth::MapNode *mapNode = new osgEarth::MapNode(map, mapNodeOpts);
101    _mapNode = mapNode;
102    _sceneRoot = new osg::Group;
103    _sceneRoot->addChild(mapNode);
104    _viewer->setSceneData(_sceneRoot.get());
105
106    initEarthManipulator();
107    initControls();
108
109    finalizeViewer();
110#endif
111
112#ifdef DEBUG
113    if (_viewer.valid() && _viewer->getViewerStats() != NULL) {
114        TRACE("Enabling stats");
115        _viewer->getViewerStats()->collectStats("scene", true);
116    }
117#endif
118#if 0
119    if (_viewer.valid()) {
120        osgViewer::ViewerBase::Windows windows;
121        _viewer->getWindows(windows);
122        if (windows.size() == 1) {
123            windows[0]->setSyncToVBlank(false);
124        } else {
125            ERROR("Num windows: %lu", windows.size());
126        }
127    }
128#endif
129}
130
131osgGA::EventQueue *Renderer::getEventQueue()
132{
133    if (_viewer.valid()) {
134        osgViewer::ViewerBase::Windows windows;
135        _viewer->getWindows(windows);
136        if (windows.size() > 0) {
137            return windows[0]->getEventQueue();
138        }
139    }
140    return NULL;
141}
142
143Renderer::~Renderer()
144{
145    TRACE("Enter");
146
147    TRACE("Leave");
148}
149
150void Renderer::initColorMaps()
151{
152    if (!_colorMaps.empty())
153        return;
154
155    osg::TransferFunction1D *defaultColorMap = new osg::TransferFunction1D;
156    defaultColorMap->allocate(256);
157    defaultColorMap->setColor(0.00, osg::Vec4f(0,0,1,1), false);
158    defaultColorMap->setColor(0.25, osg::Vec4f(0,1,1,1), false);
159    defaultColorMap->setColor(0.50, osg::Vec4f(0,1,0,1), false);
160    defaultColorMap->setColor(0.75, osg::Vec4f(1,1,0,1), false);
161    defaultColorMap->setColor(1.00, osg::Vec4f(1,0,0,1), false);
162    defaultColorMap->updateImage();
163    addColorMap("default", defaultColorMap);
164    osg::TransferFunction1D *defaultGrayColorMap = new osg::TransferFunction1D;
165    defaultGrayColorMap->allocate(256);
166    defaultGrayColorMap->setColor(0, osg::Vec4f(0,0,0,1), false);
167    defaultGrayColorMap->setColor(1, osg::Vec4f(1,1,1,1), false);
168    defaultGrayColorMap->updateImage();
169    addColorMap("grayDefault", defaultGrayColorMap);
170}
171
172void Renderer::addColorMap(const ColorMapId& id, osg::TransferFunction1D *xfer)
173{
174    _colorMaps[id] = xfer;
175}
176
177void Renderer::deleteColorMap(const ColorMapId& id)
178{
179    ColorMapHashmap::iterator itr;
180    bool doAll = false;
181
182    if (id.compare("all") == 0) {
183        itr = _colorMaps.begin();
184        doAll = true;
185    } else {
186        itr = _colorMaps.find(id);
187    }
188
189    if (itr == _colorMaps.end()) {
190        ERROR("Unknown ColorMap %s", id.c_str());
191        return;
192    }
193
194    do {
195        if (itr->first.compare("default") == 0 ||
196            itr->first.compare("grayDefault") == 0) {
197            if (id.compare("all") != 0) {
198                WARN("Cannot delete a default color map");
199            }
200            continue;
201        }
202
203        TRACE("Deleting ColorMap %s", itr->first.c_str());
204        itr = _colorMaps.erase(itr);
205    } while (doAll && itr != _colorMaps.end());
206}
207
208void Renderer::setColorMapNumberOfTableEntries(const ColorMapId& id,
209                                               int numEntries)
210{
211    ColorMapHashmap::iterator itr;
212    bool doAll = false;
213
214    if (id.compare("all") == 0) {
215        itr = _colorMaps.begin();
216        doAll = true;
217    } else {
218        itr = _colorMaps.find(id);
219    }
220
221    if (itr == _colorMaps.end()) {
222        ERROR("Unknown ColorMap %s", id.c_str());
223        return;
224    }
225
226    do {
227        itr->second->allocate(numEntries);
228        itr->second->updateImage();
229    } while (doAll && ++itr != _colorMaps.end());
230}
231
232void Renderer::initViewer() {
233    if (_viewer.valid())
234        return;
235    _viewer = new osgViewer::Viewer();
236    _viewer->setThreadingModel(osgViewer::ViewerBase::SingleThreaded);
237    _viewer->getDatabasePager()->setUnrefImageDataAfterApplyPolicy(false, false);
238    _viewer->setReleaseContextAtEndOfFrameHint(false);
239    //_viewer->setLightingMode(osg::View::SKY_LIGHT);
240    _viewer->getCamera()->setClearColor(osg::Vec4(_bgColor[0], _bgColor[1], _bgColor[2], 1));
241    _viewer->getCamera()->setClearMask(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
242    _viewer->getCamera()->setNearFarRatio(0.00002);
243    _viewer->getCamera()->setSmallFeatureCullingPixelSize(-1.0f);
244    _stateManip = new osgGA::StateSetManipulator(_viewer->getCamera()->getOrCreateStateSet());
245    _viewer->addEventHandler(_stateManip);
246    //_viewer->addEventHandler(new osgViewer::StatsHandler());
247}
248
249void Renderer::finalizeViewer() {
250    initViewer();
251    int screen = 0;
252    const char *displayEnv = getenv("DISPLAY");
253    if (displayEnv != NULL) {
254        // 3 parts: host, display, screen
255        int part = 0;
256        for (size_t c = 0; c < strlen(displayEnv); c++) {
257            if (displayEnv[c] == ':') {
258                part = 1;
259            } else if (part == 1 && displayEnv[c] == '.') {
260                part = 2;
261            } else if (part == 2) {
262                screen = atoi(&displayEnv[c]);
263                break;
264            }
265        }
266    }
267    if (!_viewer->isRealized()) {
268#ifdef USE_OFFSCREEN_RENDERING
269#ifdef USE_PBUFFER
270        osg::ref_ptr<osg::GraphicsContext> pbuffer;
271        osg::ref_ptr<osg::GraphicsContext::Traits> traits = new osg::GraphicsContext::Traits;
272        traits->x = 0;
273        traits->y = 0;
274        traits->width = _windowWidth;
275        traits->height = _windowHeight;
276        traits->red = 8;
277        traits->green = 8;
278        traits->blue = 8;
279        traits->alpha = 8;
280        traits->windowDecoration = false;
281        traits->pbuffer = true;
282        traits->doubleBuffer = true;
283        traits->sharedContext = 0;
284
285        pbuffer = osg::GraphicsContext::createGraphicsContext(traits.get());
286        if (pbuffer.valid()) {
287            TRACE("Pixel buffer has been created successfully.");
288        } else {
289            ERROR("Pixel buffer has not been created successfully.");
290        }
291        osg::Camera *camera = new osg::Camera;
292        camera->setGraphicsContext(pbuffer.get());
293        camera->setViewport(new osg::Viewport(0, 0, _windowWidth, _windowHeight));
294        GLenum buffer = pbuffer->getTraits()->doubleBuffer ? GL_BACK : GL_FRONT;
295        camera->setDrawBuffer(buffer);
296        camera->setReadBuffer(buffer);
297        _captureCallback = new ScreenCaptureCallback();
298        camera->setFinalDrawCallback(_captureCallback.get());
299        _viewer->addSlave(camera, osg::Matrixd(), osg::Matrixd());
300#else
301        _viewer->getCamera()->setRenderTargetImplementation(osg::Camera::FRAME_BUFFER_OBJECT);
302        osg::Texture2D* texture2D = new osg::Texture2D;
303        texture2D->setTextureSize(_windowWidth, _windowHeight);
304        texture2D->setInternalFormat(GL_RGBA);
305        texture2D->setFilter(osg::Texture2D::MIN_FILTER, osg::Texture2D::NEAREST);
306        texture2D->setFilter(osg::Texture2D::MAG_FILTER, osg::Texture2D::NEAREST);
307
308        _viewer->getCamera()->setImplicitBufferAttachmentMask(0, 0);
309        _viewer->getCamera()->attach(osg::Camera::COLOR_BUFFER0, texture2D);
310        //_viewer->getCamera()->attach(osg::Camera::DEPTH_BUFFER, GL_DEPTH_COMPONENT24);
311        _viewer->getCamera()->attach(osg::Camera::PACKED_DEPTH_STENCIL_BUFFER, GL_DEPTH24_STENCIL8_EXT);
312        _captureCallback = new ScreenCaptureCallback(texture2D);
313        _viewer->getCamera()->setFinalDrawCallback(_captureCallback.get());
314        _viewer->setUpViewInWindow(0, 0, _windowWidth, _windowHeight, screen);
315#endif
316#else
317        _captureCallback = new ScreenCaptureCallback();
318        _viewer->getCamera()->setFinalDrawCallback(_captureCallback.get());
319        //_viewer->getCamera()->getDisplaySettings()->setDoubleBuffer(false);
320        _viewer->setUpViewInWindow(0, 0, _windowWidth, _windowHeight, screen);
321#endif
322        _viewer->realize();
323        initColorMaps();
324        // HACK: This seems to initialize something required for properly
325        // mapping mouse coords
326        assert(getEventQueue() != NULL);
327        getEventQueue()->mouseMotion(_windowWidth/2, _windowHeight/2);
328    }
329}
330
331void Renderer::initControls()
332{
333    if (_hbox.valid())
334        return;
335    _hbox =
336        new osgEarth::Util::Controls::HBox(osgEarth::Util::Controls::Control::ALIGN_RIGHT,
337                                           osgEarth::Util::Controls::Control::ALIGN_BOTTOM,
338                                           osgEarth::Util::Controls::Gutter(0, 2, 2, 0), 2.0f);
339    _copyrightLabel =
340        new osgEarth::Util::Controls::LabelControl("Map data © 2014 ACME Corp.", 12.0f);
341    _copyrightLabel->setForeColor(osg::Vec4f(1, 1, 1, 1));
342    _copyrightLabel->setHaloColor(osg::Vec4f(0, 0, 0, 1));
343    _copyrightLabel->setEncoding(osgText::String::ENCODING_UTF8);
344    _scaleLabel =
345        new osgEarth::Util::Controls::LabelControl("- km", 12.0f);
346    _scaleLabel->setForeColor(osg::Vec4f(1, 1, 1, 1));
347    _scaleLabel->setHaloColor(osg::Vec4f(0, 0, 0, 1));
348    _scaleBar =
349        new osgEarth::Util::Controls::Frame();
350    _scaleBar->setVertFill(true);
351    _scaleBar->setForeColor(osg::Vec4f(0, 0, 0, 1));
352    _scaleBar->setBackColor(osg::Vec4f(1, 1, 1, 0.6));
353    _scaleBar->setBorderColor(osg::Vec4f(0, 0, 0 ,1));
354    _scaleBar->setBorderWidth(1.0);
355    _hbox->addControl(_copyrightLabel.get());
356    _hbox->addControl(_scaleLabel.get());
357    _hbox->addControl(_scaleBar.get());
358    osgEarth::Util::Controls::ControlCanvas::get(_viewer.get(), true)->addControl(_hbox.get());
359    // Install an event callback to handle scale bar updates
360    // Can't use an update callback since that will trigger
361    // constant rendering
362    _mapNode->setEventCallback(new MapNodeCallback(this));
363}
364
365void Renderer::setGraticule(bool enable, GraticuleType type)
366{
367    if (!_mapNode.valid() || !_sceneRoot.valid())
368        return;
369    if (enable) {
370        if (_graticule.valid()) {
371            _sceneRoot->removeChild(_graticule.get());
372            _graticule = NULL;
373        }
374        switch (type) {
375        case GRATICULE_UTM: {
376            osgEarth::Util::UTMGraticule *gr = new osgEarth::Util::UTMGraticule(_mapNode.get());
377            _sceneRoot->addChild(gr);
378            _graticule = gr;
379        }
380            break;
381        case GRATICULE_MGRS: {
382            osgEarth::Util::MGRSGraticule *gr = new osgEarth::Util::MGRSGraticule(_mapNode.get());
383            _sceneRoot->addChild(gr);
384            _graticule = gr;
385        }
386            break;
387        case GRATICULE_GEODETIC:
388        default:
389            osgEarth::Util::GeodeticGraticule *gr = new osgEarth::Util::GeodeticGraticule(_mapNode.get());
390            osgEarth::Util::GeodeticGraticuleOptions opt = gr->getOptions();
391            opt.lineStyle()->getOrCreate<osgEarth::Symbology::LineSymbol>()->stroke()->color().set(1,0,0,1);
392            gr->setOptions(opt);
393            _sceneRoot->addChild(gr);
394            _graticule = gr;
395        }
396    } else if (_graticule.valid()) {
397        _sceneRoot->removeChild(_graticule.get());
398        _graticule = NULL;
399    }
400    _needsRedraw = true;
401}
402
403void Renderer::setReadout(int x, int y)
404{
405    if (!_coordsCallback.valid() || !_mapNode.valid() || !_viewer.valid())
406        return;
407
408    osgEarth::GeoPoint mapCoord;
409    if (mapMouseCoords(x, y, mapCoord)) {
410        _coordsCallback->set(mapCoord, _viewer->asView(), _mapNode);
411    } else {
412        _coordsCallback->reset(_viewer->asView(), _mapNode);
413    }
414    _needsRedraw = true;
415}
416
417void Renderer::clearReadout()
418{
419    if (_coordsCallback.valid()) {
420        _coordsCallback->reset(_viewer->asView(), _mapNode);
421    }
422    _needsRedraw = true;
423}
424
425void Renderer::setCoordinateReadout(bool state, CoordinateDisplayType type,
426                                    int precision)
427{
428    if (!state) {
429        if (_mouseCoordsTool.valid() && _viewer.valid()) {
430            _viewer->removeEventHandler(_mouseCoordsTool.get());
431            _mouseCoordsTool = NULL;
432        }
433        if (_coordsCallback.valid() && _viewer.valid()) {
434            osgEarth::Util::Controls::LabelControl *readout =
435                _coordsCallback->getLabel();
436            osgEarth::Util::Controls::ControlCanvas::get(_viewer.get(), true)->removeControl(readout);
437            _coordsCallback = NULL;
438        }
439    } else {
440        initMouseCoordsTool(type, precision);
441    }
442    _needsRedraw = true;
443}
444
445osgEarth::Util::MGRSFormatter::Precision
446Renderer::getMGRSPrecision(int precisionInMeters)
447{
448    switch (precisionInMeters) {
449    case 1:
450        return osgEarth::Util::MGRSFormatter::PRECISION_1M;
451    case 10:
452        return osgEarth::Util::MGRSFormatter::PRECISION_10M;
453    case 100:
454        return osgEarth::Util::MGRSFormatter::PRECISION_100M;
455    case 1000:
456        return osgEarth::Util::MGRSFormatter::PRECISION_1000M;
457    case 10000:
458        return osgEarth::Util::MGRSFormatter::PRECISION_10000M;
459    case 100000:
460        return osgEarth::Util::MGRSFormatter::PRECISION_100000M;
461    default:
462        ERROR("Invalid precision: %d", precisionInMeters);
463        return osgEarth::Util::MGRSFormatter::PRECISION_1M;
464    }
465}
466
467void Renderer::initMouseCoordsTool(CoordinateDisplayType type, int precision)
468{
469    if (!_viewer.valid())
470        return;
471    if (_mouseCoordsTool.valid()) {
472        _viewer->removeEventHandler(_mouseCoordsTool.get());
473    }
474    _mouseCoordsTool = new MouseCoordsTool(_mapNode.get());
475    osgEarth::Util::Controls::LabelControl *readout;
476    if (_coordsCallback.valid()) {
477        readout = _coordsCallback->getLabel();
478        _coordsCallback = NULL;
479    } else {
480        readout = new osgEarth::Util::Controls::LabelControl("", 12.0f);
481        osgEarth::Util::Controls::ControlCanvas::get(_viewer.get(), true)->addControl(readout);
482        readout->setForeColor(osg::Vec4f(1, 1, 1, 1));
483        readout->setHaloColor(osg::Vec4f(0, 0, 0, 1));
484    }
485
486    osgEarth::Util::Formatter *formatter = NULL;
487    if (type == COORDS_MGRS) {
488        osgEarth::Util::MGRSFormatter::Precision prec =
489            osgEarth::Util::MGRSFormatter::PRECISION_1M;
490        if (precision > 0) {
491            prec = getMGRSPrecision(precision);
492        }
493        unsigned int opts = 0u;
494        formatter = new osgEarth::Util::MGRSFormatter(prec, NULL, opts);
495    } else {
496        osgEarth::Util::LatLongFormatter::AngularFormat af;
497        unsigned int opts =  osgEarth::Util::LatLongFormatter::USE_SYMBOLS;
498        switch (type) {
499        case COORDS_LATLONG_DEGREES_DECIMAL_MINUTES:
500            af = osgEarth::Util::LatLongFormatter::FORMAT_DEGREES_DECIMAL_MINUTES;
501            break;
502        case COORDS_LATLONG_DEGREES_MINUTES_SECONDS:
503            af = osgEarth::Util::LatLongFormatter::FORMAT_DEGREES_MINUTES_SECONDS;
504            break;
505        default:
506            af = osgEarth::Util::LatLongFormatter::FORMAT_DECIMAL_DEGREES;
507        }
508        osgEarth::Util::LatLongFormatter *latlong = new osgEarth::Util::LatLongFormatter(af, opts);
509        if (precision > 0) {
510            latlong->setPrecision(precision);
511        }
512        formatter = latlong;
513    }
514    _coordsCallback = new MouseCoordsCallback(readout, formatter);
515
516    _mouseCoordsTool->addCallback(_coordsCallback.get());
517    _viewer->addEventHandler(_mouseCoordsTool.get());
518}
519
520void Renderer::initEarthManipulator()
521{
522    _manipulator = new osgEarth::Util::EarthManipulator;
523#if 1
524    osgEarth::Util::EarthManipulator::Settings *settings = _manipulator->getSettings();
525    settings->bindMouse(osgEarth::Util::EarthManipulator::ACTION_ROTATE,
526                        osgGA::GUIEventAdapter::MIDDLE_MOUSE_BUTTON,
527                        osgGA::GUIEventAdapter::MODKEY_ALT);
528    osgEarth::Util::EarthManipulator::ActionOptions options;
529    options.clear();
530    options.add(osgEarth::Util::EarthManipulator::OPTION_CONTINUOUS, true);
531    settings->bindMouse(osgEarth::Util::EarthManipulator::ACTION_ZOOM,
532                        osgGA::GUIEventAdapter::RIGHT_MOUSE_BUTTON,
533                        osgGA::GUIEventAdapter::MODKEY_ALT, options);
534    _manipulator->applySettings(settings);
535#endif
536    _viewer->setCameraManipulator(_manipulator.get());
537    _manipulator->setNode(NULL);
538    _manipulator->setNode(_sceneRoot.get());
539    _manipulator->computeHomePosition();
540}
541
542void Renderer::loadEarthFile(const char *path)
543{
544    TRACE("Loading %s", path);
545    osg::Node *node = osgDB::readNodeFile(path);
546    if (node == NULL) {
547        ERROR("Couldn't load %s", path);
548        return;
549    }
550    osgEarth::MapNode *mapNode = osgEarth::MapNode::findMapNode(node);
551    if (mapNode == NULL) {
552        ERROR("Couldn't find MapNode");
553        return;
554    } else {
555        initViewer();
556        _sceneRoot = new osg::Group;
557        _sceneRoot->addChild(node);
558        _map = mapNode->getMap();
559    }
560    _mapNode = mapNode;
561
562    if (_clipPlaneCullCallback.valid()) {
563        _viewer->getCamera()->removeCullCallback(_clipPlaneCullCallback.get());
564        _clipPlaneCullCallback = NULL;
565    }
566    if (_map->isGeocentric()) {
567        _clipPlaneCullCallback = new osgEarth::Util::AutoClipPlaneCullCallback(mapNode);
568        _viewer->getCamera()->addCullCallback(_clipPlaneCullCallback.get());
569    }
570    _viewer->setSceneData(_sceneRoot.get());
571
572    if (_mouseCoordsTool.valid()) {
573        initMouseCoordsTool();
574    }
575    initControls();
576    initEarthManipulator();
577   
578    _viewer->home();
579    finalizeViewer();
580    _needsRedraw = true;
581}
582
583void Renderer::resetMap(osgEarth::MapOptions::CoordinateSystemType type,
584                        const char *profile,
585                        double bounds[4])
586{
587    TRACE("Restting map with type %d, profile %s", type, profile);
588
589    osgEarth::MapOptions mapOpts;
590    mapOpts.coordSysType() = type;
591    if (profile != NULL) {
592        if (bounds != NULL) {
593            mapOpts.profile() = osgEarth::ProfileOptions();
594            if (strcmp(profile, "geodetic") == 0) {
595                mapOpts.profile()->srsString() = "epsg:4326";
596            } else if (strcmp(profile, "spherical-mercator") == 0) {
597                // Projection used by Google/Bing/OSM
598                // aka epsg:900913 meters in x/y
599                // aka WGS84 Web Mercator (Auxiliary Sphere)
600                // X/Y: -20037508.34m to 20037508.34m
601                mapOpts.profile()->srsString() = "epsg:3857";
602            } else {
603                mapOpts.profile()->srsString() = profile;
604            }
605            TRACE("Setting profile bounds: %g %g %g %g",
606                  bounds[0], bounds[1], bounds[2], bounds[3]);
607            mapOpts.profile()->bounds() =
608                osgEarth::Bounds(bounds[0], bounds[1], bounds[2], bounds[3]);
609        } else {
610            mapOpts.profile() = osgEarth::ProfileOptions(profile);
611        }
612    } else if (type == osgEarth::MapOptions::CSTYPE_PROJECTED) {
613        mapOpts.profile() = osgEarth::ProfileOptions("global-mercator");
614    }
615
616    initViewer();
617
618    //mapOpts.referenceURI() = _baseURI;
619    osgEarth::Map *map = new osgEarth::Map(mapOpts);
620    _map = map;
621    osgEarth::Drivers::GDALOptions bopts;
622    bopts.url() = BASE_IMAGE;
623    addImageLayer("base", bopts);
624    osgEarth::Drivers::MPTerrainEngineOptions mpOpt;
625    // Set background layer color
626    mpOpt.color() = osg::Vec4(1, 1, 1, 1);
627    //mpOpt.minLOD() = 1;
628    // Sets shader uniform for terrain renderer (config var defaults to false)
629    mpOpt.enableLighting() = false;
630    osgEarth::MapNodeOptions mapNodeOpts(mpOpt);
631    // Sets GL_LIGHTING state in MapNode's StateSet (config var defaults to true)
632    mapNodeOpts.enableLighting() = true;
633    osgEarth::MapNode *mapNode = new osgEarth::MapNode(map, mapNodeOpts);
634    _mapNode = mapNode;
635    if (_map->isGeocentric()) {
636#if OSGEARTH_MIN_VERSION_REQUIRED(2, 5, 1)
637        osgEarth::Util::SkyNode *sky = new osgEarth::Util::SkyNode::create(mapNode);
638        sky->addChild(mapNode.get());
639        _sceneRoot = sky;
640#else
641#if 0
642        // XXX: Crashes
643        osgEarth::Util::SkyNode *sky = new osgEarth::Util::SkyNode(map);
644        sky->addChild(mapNode.get());
645        _sceneRoot = sky;
646#else
647        _sceneRoot = new osg::Group();
648        _sceneRoot->addChild(_mapNode.get());
649#endif
650#endif
651    } else {
652        _sceneRoot = new osg::Group();
653        _sceneRoot->addChild(_mapNode.get());
654    }
655
656    if (_clipPlaneCullCallback.valid()) {
657        _viewer->getCamera()->removeCullCallback(_clipPlaneCullCallback.get());
658        _clipPlaneCullCallback = NULL;
659    }
660    if (_map->isGeocentric()) {
661        _clipPlaneCullCallback = new osgEarth::Util::AutoClipPlaneCullCallback(mapNode);
662        _viewer->getCamera()->addCullCallback(_clipPlaneCullCallback.get());
663    }
664    _viewer->setSceneData(_sceneRoot.get());
665    if (_mouseCoordsTool.valid()) {
666        initMouseCoordsTool();
667    }
668    initControls();
669    //_viewer->setSceneData(_sceneRoot.get());
670    initEarthManipulator();
671    _viewer->home();
672
673    finalizeViewer();
674    _needsRedraw = true;
675}
676
677void Renderer::clearMap()
678{
679    if (_map.valid()) {
680        _map->clear();
681        _needsRedraw = true;
682    }
683}
684
685void Renderer::setLighting(bool state)
686{
687    if (_mapNode.valid()) {
688        TRACE("Setting lighting: %d", state ? 1 : 0);
689        _mapNode->getOrCreateStateSet()
690            ->setMode(GL_LIGHTING, state ? 1 : 0);
691        _needsRedraw = true;
692    }
693}
694
695void Renderer::setViewerLightType(osg::View::LightingMode mode)
696{
697    if (_viewer.valid()) {
698        _viewer->setLightingMode(mode);
699        _needsRedraw = true;
700    }
701}
702
703void Renderer::setTerrainVerticalScale(double scale)
704{
705    if (_mapNode.valid()) {
706        if (!_verticalScale.valid()) {
707            _verticalScale = new osgEarth::Util::VerticalScale;
708            _mapNode->getTerrainEngine()->addEffect(_verticalScale);
709        }
710        _verticalScale->setScale(scale);
711        _needsRedraw = true;
712    }
713}
714
715void Renderer::setTerrainLighting(bool state)
716{
717#if 1
718    if (!_mapNode.valid()) {
719        ERROR("No map node");
720        return;
721    }
722    // XXX: HACK alert
723    // Find the terrain engine container (might be above one or more decorators)
724    osg::Group *group = _mapNode->getTerrainEngine();
725    while (group->getParent(0) != NULL && group->getParent(0) != _mapNode.get()) {
726        group = group->getParent(0);
727    }
728    if (group != NULL && group->getParent(0) == _mapNode.get()) {
729        TRACE("Setting terrain lighting: %d", state ? 1 : 0);
730        if (group->getOrCreateStateSet()->getUniform("oe_mode_GL_LIGHTING") != NULL) {
731            group->getStateSet()->getUniform("oe_mode_GL_LIGHTING")->set(state);
732        } else {
733            ERROR("Can't get terrain lighting uniform");
734        }
735    } else {
736        ERROR("Can't find terrain engine container");
737    }
738#else
739    if (_stateManip.valid()) {
740        _stateManip->setLightingEnabled(state);
741    }
742#endif
743    _needsRedraw = true;
744}
745
746void Renderer::setTerrainWireframe(bool state)
747{
748    if (!_map.valid()) {
749        ERROR("No map");
750        return;
751    }
752#if 0
753    if (!_mapNode.valid()) {
754        ERROR("No map node");
755        return;
756    }
757    TRACE("Setting terrain wireframe: %d", state ? 1 : 0);
758    osg::StateSet *state = _mapNode->getOrCreateStateSet();
759    osg::PolygonMode *pmode = dynamic_cast< osg::PolygonMode* >(state->getAttribute(osg::StateAttribute::POLYGONMODE));
760    if (pmode == NULL) {
761        pmode = new osg::PolygonMode;
762        state->setAttribute(pmode);
763    }
764    if (state) {
765        pmode->setMode(osg::PolygonMode::FRONT_AND_BACK, osg::PolygonMode::LINE);
766    } else {
767        pmode->setMode(osg::PolygonMode::FRONT_AND_BACK, osg::PolygonMode::FILL);
768    }
769    _needsRedraw = true;
770#else
771    if (_stateManip.valid()) {
772        _stateManip->setPolygonMode(state ? osg::PolygonMode::LINE : osg::PolygonMode::FILL);
773        _needsRedraw = true;
774    }
775#endif
776}
777
778void Renderer::saveNamedViewpoint(const char *name)
779{
780    _viewpoints[name] = getViewpoint();
781}
782
783bool Renderer::removeNamedViewpoint(const char *name)
784{
785    ViewpointHashmap::iterator itr = _viewpoints.find(name);
786    if (itr != _viewpoints.end()) {
787        _viewpoints.erase(name);
788        return true;
789    } else {
790        ERROR("Unknown viewpoint: '%s'", name);
791        return false;
792    }
793}
794
795bool Renderer::restoreNamedViewpoint(const char *name, double durationSecs)
796{
797    ViewpointHashmap::iterator itr = _viewpoints.find(name);
798    if (itr != _viewpoints.end()) {
799        setViewpoint(itr->second, durationSecs);
800        return true;
801    } else {
802        ERROR("Unknown viewpoint: '%s'", name);
803        return false;
804    }
805}
806
807void Renderer::setViewpoint(const osgEarth::Viewpoint& v, double durationSecs)
808{
809    if (_manipulator.valid()) {
810        TRACE("Setting viewpoint: %g %g %g %g %g %g",
811              v.x(), v.y(), v.z(), v.getHeading(), v.getPitch(), v.getRange());
812        _manipulator->setViewpoint(v, durationSecs);
813        _needsRedraw = true;
814    } else {
815        ERROR("No manipulator");
816    }
817}
818
819osgEarth::Viewpoint Renderer::getViewpoint()
820{
821    if (_manipulator.valid()) {
822        return _manipulator->getViewpoint();
823    } else {
824        // Uninitialized, invalid viewpoint
825        return osgEarth::Viewpoint();
826    }
827}
828
829/**
830 * \brief Map screen mouse coordinates to map coordinates
831 *
832 * This method assumes that mouse Y coordinates are 0 at the top
833 * of the screen and increase going down if invertY is set, and
834 * 0 at the bottom and increase going up otherwise.
835 */
836bool Renderer::mapMouseCoords(float mouseX, float mouseY,
837                              osgEarth::GeoPoint& map, bool invertY)
838{
839    if (!_mapNode.valid() || _mapNode->getTerrain() == NULL) {
840        ERROR("No map");
841        return false;
842    }
843    if (!_viewer.valid()) {
844        ERROR("No viewer");
845        return false;
846    }
847    if (invertY) {
848        mouseY = ((float)_windowHeight - mouseY);
849    }
850    osg::Vec3d world;
851    if (_mapNode->getTerrain()->getWorldCoordsUnderMouse(_viewer->asView(), mouseX, mouseY, world)) {
852        map.fromWorld(_mapNode->getMapSRS(), world);
853        return true;
854    }
855    return false;
856}
857
858void Renderer::addImageLayer(const char *name,
859                             osgEarth::TileSourceOptions& opts,
860                             bool makeShared,
861                             bool visible)
862{
863    if (!_map.valid()) {
864        ERROR("No map");
865        return;
866    }
867    TRACE("layer: %s", name);
868    if (!opts.tileSize().isSet()) {
869        opts.tileSize() = 256;
870    }
871    osgEarth::ImageLayerOptions layerOpts(name, opts);
872    if (makeShared) {
873        layerOpts.shared() = true;
874    }
875    if (!visible) {
876        layerOpts.visible() = false;
877    }
878    _map->addImageLayer(new osgEarth::ImageLayer(layerOpts));
879    _needsRedraw = true;
880}
881
882void Renderer::addColorFilter(const char *name,
883                              const char *shader)
884{
885    if (!_map.valid()) {
886        ERROR("No map");
887        return;
888    }
889    osgEarth::ImageLayer *layer = _map->getImageLayerByName(name);
890    if (layer == NULL) {
891        TRACE("Image layer not found: %s", name);
892        return;
893    }
894    osgEarth::Util::GLSLColorFilter *filter = new osgEarth::Util::GLSLColorFilter;
895    filter->setCode(shader);
896    //filter->setCode("color.rgb = color.r > 0.5 ? vec3(1.0) : vec3(0.0);");
897    layer->addColorFilter(filter);
898    _needsRedraw = true;
899}
900
901void Renderer::removeColorFilter(const char *name, int idx)
902{
903    if (!_map.valid()) {
904        ERROR("No map");
905        return;
906    }
907    osgEarth::ImageLayer *layer = _map->getImageLayerByName(name);
908    if (layer == NULL) {
909        TRACE("Image layer not found: %s", name);
910        return;
911    }
912    if (idx < 0) {
913        while (!layer->getColorFilters().empty()) {
914            layer->removeColorFilter(layer->getColorFilters()[0]);
915        }
916    } else {
917        layer->removeColorFilter(layer->getColorFilters().at(idx));
918    }
919    _needsRedraw = true;
920}
921
922void Renderer::removeImageLayer(const char *name)
923{
924    if (!_map.valid()) {
925        ERROR("No map");
926        return;
927    }
928    osgEarth::ImageLayer *layer = _map->getImageLayerByName(name);
929    if (layer != NULL) {
930        _map->removeImageLayer(layer);
931        _needsRedraw = true;
932    } else {
933        TRACE("Image layer not found: %s", name);
934    }
935}
936
937void Renderer::moveImageLayer(const char *name, unsigned int pos)
938{
939    if (!_map.valid()) {
940        ERROR("No map");
941        return;
942    }
943    osgEarth::ImageLayer *layer = _map->getImageLayerByName(name);
944    if (layer != NULL) {
945        _map->moveImageLayer(layer, pos);
946        _needsRedraw = true;
947    } else {
948        TRACE("Image layer not found: %s", name);
949    }
950}
951
952void Renderer::setImageLayerOpacity(const char *name, double opacity)
953{
954    if (!_map.valid()) {
955        ERROR("No map");
956        return;
957    }
958    osgEarth::ImageLayer *layer = _map->getImageLayerByName(name);
959    if (layer != NULL) {
960        layer->setOpacity(opacity);
961        _needsRedraw = true;
962    } else {
963        TRACE("Image layer not found: %s", name);
964    }
965}
966
967void Renderer::setImageLayerVisibility(const char *name, bool state)
968{
969#if OSGEARTH_MIN_VERSION_REQUIRED(2, 4, 0)
970    if (!_map.valid()) {
971        ERROR("No map");
972        return;
973    }
974    osgEarth::ImageLayer *layer = _map->getImageLayerByName(name);
975    if (layer != NULL) {
976        layer->setVisible(state);
977        _needsRedraw = true;
978    } else {
979        TRACE("Image layer not found: %s", name);
980    }
981#endif
982}
983
984void Renderer::addElevationLayer(const char *name,
985                                 osgEarth::TileSourceOptions& opts)
986{
987    if (!_map.valid()) {
988        ERROR("No map");
989        return;
990    }
991    TRACE("layer: %s", name);
992    if (!opts.tileSize().isSet()) {
993        opts.tileSize() = 15;
994    }
995    osgEarth::ElevationLayerOptions layerOpts(name, opts);
996    // XXX: GDAL does not report vertical datum, it should be specified here
997    // Common options: geodetic (default), egm96, egm84, egm2008
998    //layerOpts.verticalDatum() = "egm96";
999    _map->addElevationLayer(new osgEarth::ElevationLayer(layerOpts));
1000    _needsRedraw = true;
1001}
1002
1003void Renderer::removeElevationLayer(const char *name)
1004{
1005    if (!_map.valid()) {
1006        ERROR("No map");
1007        return;
1008    }
1009    osgEarth::ElevationLayer *layer = _map->getElevationLayerByName(name);
1010    if (layer != NULL) {
1011        _map->removeElevationLayer(layer);
1012        _needsRedraw = true;
1013    } else {
1014        TRACE("Elevation layer not found: %s", name);
1015    }
1016}
1017
1018void Renderer::moveElevationLayer(const char *name, unsigned int pos)
1019{
1020    if (!_map.valid()) {
1021        ERROR("No map");
1022        return;
1023    }
1024    osgEarth::ElevationLayer *layer = _map->getElevationLayerByName(name);
1025    if (layer != NULL) {
1026        _map->moveElevationLayer(layer, pos);
1027        _needsRedraw = true;
1028    } else {
1029        TRACE("Elevation layer not found: %s", name);
1030    }
1031}
1032
1033void Renderer::setElevationLayerVisibility(const char *name, bool state)
1034{
1035#if OSGEARTH_MIN_VERSION_REQUIRED(2, 4, 0)
1036    if (!_map.valid()) {
1037        ERROR("No map");
1038        return;
1039    }
1040    osgEarth::ElevationLayer *layer = _map->getElevationLayerByName(name);
1041    if (layer != NULL) {
1042        layer->setVisible(state);
1043        _needsRedraw = true;
1044    } else {
1045        TRACE("Elevation layer not found: %s", name);
1046    }
1047#endif
1048}
1049
1050void Renderer::addModelLayer(const char *name, osgEarth::ModelSourceOptions& opts)
1051{
1052    if (!_map.valid()) {
1053        ERROR("No map");
1054        return;
1055    }
1056    TRACE("layer: %s", name);
1057    osgEarth::ModelLayerOptions layerOpts(name, opts);
1058    _map->addModelLayer(new osgEarth::ModelLayer(layerOpts));
1059    _needsRedraw = true;
1060}
1061
1062void Renderer::removeModelLayer(const char *name)
1063{
1064    if (!_map.valid()) {
1065        ERROR("No map");
1066        return;
1067    }
1068    osgEarth::ModelLayer *layer = _map->getModelLayerByName(name);
1069    if (layer != NULL) {
1070        _map->removeModelLayer(layer);
1071        _needsRedraw = true;
1072    } else {
1073        TRACE("Model layer not found: %s", name);
1074    }
1075}
1076
1077void Renderer::moveModelLayer(const char *name, unsigned int pos)
1078{
1079    if (!_map.valid()) {
1080        ERROR("No map");
1081        return;
1082    }
1083    osgEarth::ModelLayer *layer = _map->getModelLayerByName(name);
1084    if (layer != NULL) {
1085        _map->moveModelLayer(layer, pos);
1086        _needsRedraw = true;
1087    } else {
1088        TRACE("Model layer not found: %s", name);
1089    }
1090}
1091
1092void Renderer::setModelLayerOpacity(const char *name, double opacity)
1093{
1094#if OSGEARTH_MIN_VERSION_REQUIRED(2, 5, 0)
1095    if (!_map.valid()) {
1096        ERROR("No map");
1097        return;
1098    }
1099    osgEarth::ModelLayer *layer = _map->getModelLayerByName(name);
1100    if (layer != NULL) {
1101        layer->setOpacity(opacity);
1102        _needsRedraw = true;
1103    } else {
1104        TRACE("Model layer not found: %s", name);
1105    }
1106#endif
1107}
1108
1109void Renderer::setModelLayerVisibility(const char *name, bool state)
1110{
1111#if OSGEARTH_MIN_VERSION_REQUIRED(2, 4, 0)
1112    if (!_map.valid()) {
1113        ERROR("No map");
1114        return;
1115    }
1116    osgEarth::ModelLayer *layer = _map->getModelLayerByName(name);
1117    if (layer != NULL) {
1118        layer->setVisible(state);
1119        _needsRedraw = true;
1120    } else {
1121        TRACE("Model layer not found: %s", name);
1122    }
1123#endif
1124}
1125
1126/**
1127 * \brief Resize the render window (image size for renderings)
1128 */
1129void Renderer::setWindowSize(int width, int height)
1130{
1131    if (_windowWidth == width &&
1132        _windowHeight == height) {
1133        TRACE("No change");
1134        return;
1135    }
1136
1137    TRACE("Setting window size to %dx%d", width, height);
1138
1139    _windowWidth = width;
1140    _windowHeight = height;
1141    if (_viewer.valid()) {
1142#ifdef USE_OFFSCREEN_RENDERING
1143#ifdef USE_PBUFFER
1144        osg::ref_ptr<osg::GraphicsContext> pbuffer;
1145        osg::ref_ptr<osg::GraphicsContext::Traits> traits = new osg::GraphicsContext::Traits;
1146        traits->x = 0;
1147        traits->y = 0;
1148        traits->width = _windowWidth;
1149        traits->height = _windowHeight;
1150        traits->red = 8;
1151        traits->green = 8;
1152        traits->blue = 8;
1153        traits->alpha = 8;
1154        traits->windowDecoration = false;
1155        traits->pbuffer = true;
1156        traits->doubleBuffer = true;
1157        traits->sharedContext = 0;
1158
1159        pbuffer = osg::GraphicsContext::createGraphicsContext(traits.get());
1160        if (pbuffer.valid()) {
1161            TRACE("Pixel buffer has been created successfully.");
1162        } else {
1163            ERROR("Pixel buffer has not been created successfully.");
1164        }
1165        osg::Camera *camera = new osg::Camera;
1166        camera->setGraphicsContext(pbuffer.get());
1167        camera->setViewport(new osg::Viewport(0, 0, _windowWidth, _windowHeight));
1168        GLenum buffer = pbuffer->getTraits()->doubleBuffer ? GL_BACK : GL_FRONT;
1169        camera->setDrawBuffer(buffer);
1170        camera->setReadBuffer(buffer);
1171        camera->setFinalDrawCallback(_captureCallback.get());
1172        _viewer->addSlave(camera, osg::Matrixd(), osg::Matrixd());
1173        _viewer->realize();
1174#else
1175        if (_captureCallback.valid()) {
1176            _captureCallback->getTexture()->setTextureSize(_windowWidth, _windowHeight);
1177        }
1178        osgViewer::ViewerBase::Windows windows;
1179        _viewer->getWindows(windows);
1180        if (windows.size() == 1) {
1181            windows[0]->setWindowRectangle(0, 0, _windowWidth, _windowHeight);
1182        } else {
1183            ERROR("Num windows: %lu", windows.size());
1184        }
1185#endif
1186#else
1187        osgViewer::ViewerBase::Windows windows;
1188        _viewer->getWindows(windows);
1189        if (windows.size() == 1) {
1190            windows[0]->setWindowRectangle(0, 0, _windowWidth, _windowHeight);
1191        } else {
1192            ERROR("Num windows: %lu", windows.size());
1193        }
1194#endif
1195        // HACK: Without this, the mouse coordinate mapping uses the old size
1196        // for 1 frame.
1197        assert(_viewer->getEventQueue() != NULL);
1198        //TRACE("Window EventQueue: %p", getEventQueue());
1199        //TRACE("Viewer EventQueue: %p", _viewer->getEventQueue());
1200        _viewer->getEventQueue()->windowResize(0, 0, _windowWidth, _windowHeight);
1201        _needsRedraw = true;
1202    }
1203}
1204
1205/**
1206 * \brief Set the orientation of the camera from a quaternion
1207 * rotation
1208 *
1209 * \param[in] quat A quaternion with scalar part first: w,x,y,z
1210 * \param[in] absolute Is rotation absolute or relative?
1211 */
1212void Renderer::setCameraOrientation(const double quat[4], bool absolute)
1213{
1214    if (_manipulator.valid()) {
1215        _manipulator->setRotation(osg::Quat(quat[1], quat[2], quat[3], quat[0]));
1216        _needsRedraw = true;
1217    }
1218}
1219
1220/**
1221 * \brief Reset pan, zoom, clipping planes and optionally rotation
1222 *
1223 * \param[in] resetOrientation Reset the camera rotation/orientation also
1224 */
1225void Renderer::resetCamera(bool resetOrientation)
1226{
1227    TRACE("Enter: resetOrientation=%d", resetOrientation ? 1 : 0);
1228    if (_viewer.valid()) {
1229        _viewer->home();
1230        _needsRedraw = true;
1231    }
1232}
1233
1234/**
1235 * \brief Perform a 2D translation of the camera
1236 *
1237 * x,y pan amount are specified as signed absolute pan amount in viewport
1238 * units -- i.e. 0 is no pan, .5 is half the viewport, 2 is twice the viewport,
1239 * etc.
1240 *
1241 * \param[in] x Viewport coordinate horizontal panning (positive number pans
1242 * camera left, object right)
1243 * \param[in] y Viewport coordinate vertical panning (positive number pans
1244 * camera up, object down)
1245 */
1246void Renderer::panCamera(double x, double y)
1247{
1248    TRACE("Enter: %g %g", x, y);
1249
1250    if (_manipulator.valid()) {
1251        // Wants mouse delta x,y in normalized screen coords
1252        _manipulator->pan(x, y);
1253        _needsRedraw = true;
1254    }
1255}
1256
1257void Renderer::rotateCamera(double x, double y)
1258{
1259    TRACE("Enter: %g %g", x, y);
1260
1261    if (_manipulator.valid()) {
1262        _manipulator->rotate(x, y);
1263        _needsRedraw = true;
1264    }
1265}
1266
1267/**
1268 * \brief Dolly camera or set orthographic scaling based on camera type
1269 *
1270 * \param[in] y Mouse y coordinate in normalized screen coords
1271 */
1272void Renderer::zoomCamera(double y)
1273{
1274    TRACE("Enter: y: %g", y);
1275
1276    if (_manipulator.valid()) {
1277        TRACE("camDist: %g", _manipulator->getDistance());
1278        // FIXME: zoom here wants y mouse coords in normalized viewport coords
1279#if 1
1280       _manipulator->zoom(0, y);
1281#else
1282        double dist = _manipulator->getDistance();
1283        dist *= (1.0 + y);
1284        _manipulator->setDistance(dist);
1285#endif
1286        _needsRedraw = true;
1287    }
1288}
1289
1290/**
1291 * \brief Dolly camera to set distance from focal point
1292 *
1293 * \param[in] dist distance in map? coordinates
1294 */
1295void Renderer::setCameraDistance(double dist)
1296{
1297    TRACE("Enter: dist: %g", dist);
1298
1299    if (_manipulator.valid()) {
1300        TRACE("camDist: %g", _manipulator->getDistance());
1301
1302        _manipulator->setDistance(dist);
1303
1304        _needsRedraw = true;
1305    }
1306}
1307
1308void Renderer::keyPress(int key)
1309{
1310    osgGA::EventQueue *queue = getEventQueue();
1311    if (queue != NULL) {
1312        queue->keyPress(key);
1313        _needsRedraw = true;
1314    }
1315}
1316
1317void Renderer::keyRelease(int key)
1318{
1319    osgGA::EventQueue *queue = getEventQueue();
1320    if (queue != NULL) {
1321        queue->keyRelease(key);
1322        _needsRedraw = true;
1323    }
1324}
1325
1326void Renderer::setThrowingEnabled(bool state)
1327{
1328    if (_manipulator.valid()) {
1329        _manipulator->getSettings()->setThrowingEnabled(state);
1330    }
1331}
1332
1333void Renderer::mouseDoubleClick(int button, double x, double y)
1334{
1335    osgGA::EventQueue *queue = getEventQueue();
1336    if (queue != NULL) {
1337        queue->mouseDoubleButtonPress((float)x, (float)y, button);
1338        _needsRedraw = true;
1339    }
1340}
1341
1342void Renderer::mouseClick(int button, double x, double y)
1343{
1344    osgGA::EventQueue *queue = getEventQueue();
1345    if (queue != NULL) {
1346        queue->mouseButtonPress((float)x, (float)y, button);
1347        _needsRedraw = true;
1348    }
1349}
1350
1351void Renderer::mouseDrag(int button, double x, double y)
1352{
1353    osgGA::EventQueue *queue = getEventQueue();
1354    if (queue != NULL) {
1355        queue->mouseMotion((float)x, (float)y);
1356        _needsRedraw = true;
1357    }
1358}
1359
1360void Renderer::mouseRelease(int button, double x, double y)
1361{
1362    osgGA::EventQueue *queue = getEventQueue();
1363    if (queue != NULL) {
1364        queue->mouseButtonRelease((float)x, (float)y, button);
1365        _needsRedraw = true;
1366    }
1367}
1368
1369void Renderer::mouseMotion(double x, double y)
1370{
1371    if (_mouseCoordsTool.valid()) {
1372#if 1
1373        osgGA::EventQueue *queue = getEventQueue();
1374        if (queue != NULL) {
1375            queue->mouseMotion((float)x, (float)y);
1376            _needsRedraw = true;
1377        }
1378#else
1379        if (_viewer.valid() && _coordsCallback.valid()) {
1380            osgEarth::GeoPoint mapPt;
1381            if (mapMouseCoords((float)x, (float)y, mapPt)) {
1382                _coordsCallback->set(mapPt, _viewer->asView(), _mapNode);
1383            } else {
1384                _coordsCallback->reset(_viewer->asView(), _mapNode);
1385            }
1386            _needsRedraw = true;
1387        }
1388#endif
1389    }
1390}
1391
1392void Renderer::mouseScroll(int direction)
1393{
1394    osgGA::EventQueue *queue = getEventQueue();
1395    if (queue != NULL) {
1396        queue->mouseScroll((direction > 0 ? osgGA::GUIEventAdapter::SCROLL_UP : osgGA::GUIEventAdapter::SCROLL_DOWN));
1397        _needsRedraw = true;
1398    }
1399}
1400
1401/**
1402 * \brief Set the RGB background color to render into the image
1403 */
1404void Renderer::setBackgroundColor(float color[3])
1405{
1406    _bgColor[0] = color[0];
1407    _bgColor[1] = color[1];
1408    _bgColor[2] = color[2];
1409
1410    if (_viewer.valid()) {
1411        _viewer->getCamera()->setClearColor(osg::Vec4(color[0], color[1], color[2], 1));
1412
1413        _needsRedraw = true;
1414    }
1415}
1416
1417/**
1418 * \brief Sets flag to trigger rendering next time render() is called
1419 */
1420void Renderer::eventuallyRender()
1421{
1422    _needsRedraw = true;
1423}
1424
1425/**
1426 * \brief Get a timeout in usecs for select()
1427 *
1428 * If the paging thread is idle, returns <0 indicating that the
1429 * select call can block until data is available.  Otherwise,
1430 * if the frame render time was faster than the target frame
1431 * rate, return the remaining frame time.
1432 */
1433long Renderer::getTimeout()
1434{
1435    if (!checkNeedToDoFrame())
1436        // <0 means no timeout, block until socket has data
1437        return -1L;
1438    if (_lastFrameTime < _minFrameTime) {
1439        return (long)1000000.0*(_minFrameTime - _lastFrameTime);
1440    } else {
1441        // No timeout (poll)
1442        return 0L;
1443    }
1444}
1445
1446/**
1447 * \brief Check if paging thread is quiescent
1448 */
1449bool Renderer::isPagerIdle()
1450{
1451    if (!_viewer.valid())
1452        return true;
1453    else
1454        return (!_viewer->getDatabasePager()->requiresUpdateSceneGraph() &&
1455                !_viewer->getDatabasePager()->getRequestsInProgress());
1456}
1457
1458/**
1459 * \brief Check is frame call is necessary to render and/or update
1460 * in response to events or timed actions
1461 */
1462bool Renderer::checkNeedToDoFrame()
1463{
1464    return (_needsRedraw ||
1465            (_viewer.valid() && _viewer->checkNeedToDoFrame()));
1466}
1467
1468/**
1469 * \brief MapNode event phase
1470 *
1471 * This is called by the MapNode's event callback during the event
1472 * traversal of the viewer
1473 */
1474void Renderer::mapNodeUpdate()
1475{
1476    computeMapScale();
1477}
1478
1479/**
1480 * \brief Cause the rendering to render a new image if needed
1481 *
1482 * The _needsRedraw flag indicates if a state change has occured since
1483 * the last rendered frame
1484 */
1485bool Renderer::render()
1486{
1487    if (_viewer.valid() && checkNeedToDoFrame()) {
1488        TRACE("Enter needsRedraw=%d",  _needsRedraw ? 1 : 0);
1489        osg::Timer_t startFrameTick = osg::Timer::instance()->tick();
1490        TRACE("Before frame()");
1491        _viewer->frame();
1492        TRACE("After frame()");
1493        osg::Timer_t endFrameTick = osg::Timer::instance()->tick();
1494        _lastFrameTime = osg::Timer::instance()->delta_s(startFrameTick, endFrameTick);
1495        TRACE("Frame time: %g sec", _lastFrameTime);
1496#if 0
1497        if (frameTime < minFrameTime) {
1498            TRACE("Sleeping for %g secs", minFrameTime-frameTime);
1499            OpenThreads::Thread::microSleep(static_cast<unsigned int>(1000000.0*(minFrameTime-frameTime)));
1500        }
1501#endif
1502#ifdef WANT_TRACE
1503        if (_viewer->getViewerStats() != NULL) {
1504            _viewer->getViewerStats()->report(std::cerr, _viewer->getViewerStats()->getLatestFrameNumber());
1505        }
1506#endif
1507        _needsRedraw = false;
1508        return true;
1509    } else
1510        return false;
1511}
1512
1513/**
1514 * \brief Read back the rendered framebuffer image
1515 */
1516osg::Image *Renderer::getRenderedFrame()
1517{
1518    if (_captureCallback.valid())
1519        return _captureCallback->getImage();
1520    else
1521        return NULL;
1522}
1523
1524void Renderer::setScaleBar(bool state)
1525{
1526    if (_scaleLabel.valid()) {
1527        _scaleLabel->setVisible(state);
1528    }
1529    if (_scaleBar.valid()) {
1530        _scaleBar->setVisible(state);
1531    }
1532    _needsRedraw = true;
1533}
1534
1535void Renderer::setScaleBarUnits(ScaleBarUnits units)
1536{
1537    _scaleBarUnits = units;
1538    _needsRedraw = true;
1539}
1540
1541/**
1542 * \brief Compute the scale ratio of the map based on a horizontal center line
1543 *
1544 * The idea here is to take 2 screen points on a horizontal line in the center
1545 * of the screen and convert to lat/long.  The lat/long coordinates are then
1546 * used to compute the great circle distance (assuming spherical earth) between
1547 * the points.
1548 *
1549 * We could use local projected map coordinates for the distance computation,
1550 * which would be faster; however, this would not show e.g. the change in
1551 * scale at different latitudes
1552 */
1553double Renderer::computeMapScale()
1554{
1555    if (!_scaleLabel.valid() || !_scaleLabel->visible()) {
1556        return -1.0;
1557    }
1558    if (!_mapNode.valid() || _mapNode->getTerrain() == NULL) {
1559        ERROR("No map");
1560        return -1.0;
1561    }
1562    if (!_viewer.valid()) {
1563        ERROR("No viewer");
1564        return -1.0;
1565    }
1566
1567    double x, y;
1568    double pixelWidth = _windowWidth * 0.1 * 2.0;
1569    if (pixelWidth < 10)
1570        pixelWidth = 10;
1571    if (pixelWidth > 150)
1572        pixelWidth = 150;
1573    x = (double)(_windowWidth -1)/2.0 - pixelWidth / 2.0;
1574    y = (double)(_windowHeight-1)/2.0;
1575
1576    osg::Vec3d world1, world2;
1577    if (!_mapNode->getTerrain()->getWorldCoordsUnderMouse(_viewer->asView(), x, y, world1)) {
1578        // off map
1579        TRACE("Off map coords: %g %g", x, y);
1580        _scaleLabel->setText("");
1581        _scaleBar->setWidth(0);
1582        return -1.0;
1583    }
1584    x += pixelWidth;
1585    if (!_mapNode->getTerrain()->getWorldCoordsUnderMouse(_viewer->asView(), x, y, world2)) {
1586        // off map
1587        TRACE("Off map coords: %g %g", x, y);
1588        _scaleLabel->setText("");
1589        _scaleBar->setWidth(0);
1590        return -1.0;
1591    }
1592
1593    TRACE("w1: %g %g %g w2: %g %g %g",
1594          world1.x(), world1.y(), world1.z(),
1595          world2.x(), world2.y(), world2.z());
1596
1597    double meters;
1598    double radius = 6378137.0;
1599    if (_mapNode->getMapSRS() &&
1600        _mapNode->getMapSRS()->getEllipsoid()) {
1601        radius = _mapNode->getMapSRS()->getEllipsoid()->getRadiusEquator();
1602    }
1603    if (!_map->isGeocentric() &&
1604        _mapNode->getMapSRS() &&
1605        _mapNode->getMapSRS()->isGeographic()) {
1606        TRACE("Map is geographic");
1607        // World cords are already lat/long
1608        // Compute great circle distance
1609        meters =
1610            osgEarth::GeoMath::distance(world1, world2, _mapNode->getMapSRS());
1611    } else if (_mapNode->getMapSRS()) {
1612        // Get map coords in lat/long
1613        osgEarth::GeoPoint mapPoint1, mapPoint2;
1614        mapPoint1.fromWorld(_mapNode->getMapSRS(), world1);
1615        mapPoint1.makeGeographic();
1616        mapPoint2.fromWorld(_mapNode->getMapSRS(), world2);
1617        mapPoint2.makeGeographic();
1618        // Compute great circle distance
1619        meters =
1620            osgEarth::GeoMath::distance(osg::DegreesToRadians(mapPoint1.y()),
1621                                        osg::DegreesToRadians(mapPoint1.x()),
1622                                        osg::DegreesToRadians(mapPoint2.y()),
1623                                        osg::DegreesToRadians(mapPoint2.x()),
1624                                        radius);
1625    } else {
1626        // Assume geocentric?
1627        ERROR("No map SRS");
1628        _scaleLabel->setText("");
1629        _scaleBar->setWidth(0);
1630        return -1.0;
1631    }
1632
1633    double scale = meters / pixelWidth;
1634    // 1mi = 5280 feet
1635    //double scaleMiles = scale / 1609.344; // International mile = 1609.344m
1636    //double scaleNauticalMiles = scale / 1852.0; // nautical mile = 1852m
1637    //double scaleUSSurveyMiles = scale / 1609.347218694; // US survey mile = 5280 US survey feet
1638    //double scaleUSSurveyFeet = scale * 3937.0/1200.0; // US survey foot = 1200/3937 m
1639    TRACE("m: %g px: %g m/px: %g", meters, pixelWidth, scale);
1640    switch (_scaleBarUnits) {
1641    case UNITS_NAUTICAL_MILES: {
1642        double nmi = meters / 1852.0;
1643        scale = nmi / pixelWidth;
1644        nmi = normalizeScaleNauticalMiles(nmi);
1645        pixelWidth = nmi / scale;
1646        if (_scaleLabel.valid()) {
1647            _scaleLabel->setText(osgEarth::Stringify()
1648                                 << nmi
1649                                 << " nmi");
1650        }
1651    }
1652        break;
1653    case UNITS_US_SURVEY_FEET: {
1654        double feet = meters * 3937.0/1200.0;
1655        scale = feet / pixelWidth;
1656        feet = normalizeScaleFeet(feet);
1657        pixelWidth = feet / scale;
1658        if (_scaleLabel.valid()) {
1659            if (feet >= 5280) {
1660                _scaleLabel->setText(osgEarth::Stringify()
1661                                     << feet / 5280.0
1662                                     << " miUS");
1663             } else {
1664                _scaleLabel->setText(osgEarth::Stringify()
1665                                     << feet
1666                                     << " ftUS");
1667            }
1668        }
1669    }
1670        break;
1671    case UNITS_INTL_FEET: {
1672        double feet = 5280.0 * meters / 1609.344;
1673        scale = feet / pixelWidth;
1674        feet = normalizeScaleFeet(feet);
1675        pixelWidth = feet / scale;
1676        if (_scaleLabel.valid()) {
1677            if (feet >= 5280) {
1678                _scaleLabel->setText(osgEarth::Stringify()
1679                                     << feet / 5280.0
1680                                     << " mi");
1681            } else {
1682                _scaleLabel->setText(osgEarth::Stringify()
1683                                     << feet
1684                                     << " ft");
1685            }
1686        }
1687    }
1688        break;
1689    case UNITS_METERS:
1690    default: {
1691        meters = normalizeScaleMeters(meters);
1692        pixelWidth = meters / scale;
1693        if (_scaleLabel.valid()) {
1694            if (meters >= 1000) {
1695                _scaleLabel->setText(osgEarth::Stringify()
1696                                     << meters / 1000.0
1697                                     << " km");
1698            } else {
1699                _scaleLabel->setText(osgEarth::Stringify()
1700                                     << meters
1701                                     << " m");
1702            }
1703        }
1704    }
1705        break;
1706    }
1707    if (_scaleBar.valid()) {
1708        _scaleBar->setWidth(pixelWidth);
1709    }
1710    return scale;
1711}
Note: See TracBrowser for help on using the repository browser.