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

Last change on this file since 4036 was 4036, checked in by ldelgass, 11 years ago

Add type parameter to layer add, first pass at feature layers.

File size: 16.5 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
13#include <GL/gl.h>
14
15#ifdef WANT_TRACE
16#include <sys/time.h>
17#endif
18
19#include <osgGA/StateSetManipulator>
20
21#include <osgEarth/Version>
22#include <osgEarth/MapNode>
23#include <osgEarth/TerrainLayer>
24#include <osgEarth/ImageLayer>
25#include <osgEarth/ElevationLayer>
26#include <osgEarth/ModelLayer>
27#include <osgEarthUtil/EarthManipulator>
28#include <osgEarthUtil/AutoClipPlaneHandler>
29#include <osgEarthUtil/MouseCoordsTool>
30
31#include "Renderer.h"
32#include "Trace.h"
33
34#define MSECS_ELAPSED(t1, t2) \
35    ((t1).tv_sec == (t2).tv_sec ? (((t2).tv_usec - (t1).tv_usec)/1.0e+3) : \
36     (((t2).tv_sec - (t1).tv_sec))*1.0e+3 + (double)((t2).tv_usec - (t1).tv_usec)/1.0e+3)
37
38using namespace GeoVis;
39
40Renderer::Renderer() :
41    _needsRedraw(true),
42    _windowWidth(500),
43    _windowHeight(500)
44{
45    _bgColor[0] = 0;
46    _bgColor[1] = 0;
47    _bgColor[2] = 0;
48    _minFrameTime = 1.0/30.0;
49    _lastFrameTime = _minFrameTime;
50    _viewer = new osgViewer::Viewer();
51    _viewer->setThreadingModel(osgViewer::ViewerBase::SingleThreaded);
52    _viewer->getDatabasePager()->setUnrefImageDataAfterApplyPolicy(false, false);
53    _viewer->setReleaseContextAtEndOfFrameHint(false);
54    setBackgroundColor(_bgColor);
55    _captureCallback = new ScreenCaptureCallback();
56    _viewer->getCamera()->setPostDrawCallback(_captureCallback.get());
57    osgEarth::MapOptions mapOpts;
58    mapOpts.coordSysType() = osgEarth::MapOptions::CSTYPE_PROJECTED;
59    mapOpts.profile() = osgEarth::ProfileOptions("global-geodetic");
60    osgEarth::Map *map = new osgEarth::Map(mapOpts);
61    _map = map;
62    osgEarth::MapNodeOptions mapNodeOpts;
63    mapNodeOpts.enableLighting() = false;
64    osgEarth::MapNode *mapNode = new osgEarth::MapNode(map, mapNodeOpts);
65    _mapNode = mapNode;
66    _sceneRoot = mapNode;
67    _viewer->setSceneData(_sceneRoot.get());
68    _manipulator = new osgEarth::Util::EarthManipulator;
69    _viewer->setCameraManipulator(_manipulator.get());
70    _viewer->addEventHandler(new osgGA::StateSetManipulator(_viewer->getCamera()->getOrCreateStateSet()));
71    _coordsCallback = new MouseCoordsCallback();
72    _mouseCoordsTool = new osgEarth::Util::MouseCoordsTool(mapNode);
73    _mouseCoordsTool->addCallback(_coordsCallback);
74    _viewer->addEventHandler(_mouseCoordsTool);
75    _viewer->getCamera()->setNearFarRatio(0.00002);
76    _viewer->getCamera()->setSmallFeatureCullingPixelSize(-1.0f);
77    _viewer->setUpViewInWindow(0, 0, _windowWidth, _windowHeight);
78    _viewer->realize();
79#ifdef DEBUG
80    if (_viewer->getViewerStats() != NULL) {
81        TRACE("Enabling stats");
82        _viewer->getViewerStats()->collectStats("scene", true);
83    }
84#endif
85#if 0
86    osgViewer::ViewerBase::Windows windows;
87    _viewer->getWindows(windows);
88    if (windows.size() == 1) {
89        windows[0]->setSyncToVBlank(false);
90    } else {
91        ERROR("Num windows: %lu", windows.size());
92    }
93#endif
94}
95
96osgGA::EventQueue *Renderer::getEventQueue()
97{
98    osgViewer::ViewerBase::Windows windows;
99    _viewer->getWindows(windows);
100    return windows[0]->getEventQueue();
101}
102
103Renderer::~Renderer()
104{
105    TRACE("Enter");
106
107    TRACE("Leave");
108}
109
110void Renderer::loadEarthFile(const char *path)
111{
112    TRACE("Loading %s", path);
113    osg::Node *node = osgDB::readNodeFile(path);
114    if (node == NULL) {
115        ERROR("Couldn't load %s", path);
116        return;
117    }
118    osgEarth::MapNode *mapNode = osgEarth::MapNode::findMapNode(node);
119    if (mapNode == NULL) {
120        ERROR("Couldn't find MapNode");
121        return;
122    } else {
123        _sceneRoot = node;
124        _map = mapNode->getMap();
125    }
126    _mapNode = mapNode;
127    if (_mouseCoordsTool.valid())
128        _viewer->removeEventHandler(_mouseCoordsTool.get());
129    _mouseCoordsTool = new osgEarth::Util::MouseCoordsTool(mapNode);
130    _mouseCoordsTool->addCallback(_coordsCallback.get());
131    if (_clipPlaneCullCallback.valid()) {
132        _viewer->getCamera()->removeCullCallback(_clipPlaneCullCallback.get());
133        _clipPlaneCullCallback = NULL;
134    }
135    if (_map->isGeocentric()) {
136        _clipPlaneCullCallback = new osgEarth::Util::AutoClipPlaneCullCallback(mapNode);
137        _viewer->getCamera()->addCullCallback(_clipPlaneCullCallback.get());
138    }
139    _viewer->addEventHandler(_mouseCoordsTool.get());
140    _viewer->setSceneData(_sceneRoot.get());
141    _manipulator = new osgEarth::Util::EarthManipulator;
142    _viewer->setCameraManipulator(_manipulator.get());
143    _manipulator->setNode(NULL);
144    _manipulator->setNode(_sceneRoot.get());
145    _manipulator->computeHomePosition();
146    _viewer->home();
147    _needsRedraw = true;
148}
149
150void Renderer::resetMap(osgEarth::MapOptions::CoordinateSystemType type, const char *profile)
151{
152    TRACE("Restting map with type %d, profile %s", type, profile);
153
154    osgEarth::MapOptions mapOpts;
155    mapOpts.coordSysType() = type;
156    if (profile != NULL) {
157        mapOpts.profile() = osgEarth::ProfileOptions(profile);
158    } else if (type == osgEarth::MapOptions::CSTYPE_PROJECTED) {
159        mapOpts.profile() = osgEarth::ProfileOptions("global-geodetic");
160    }
161    osgEarth::Map *map = new osgEarth::Map(mapOpts);
162    _map = map;
163    osgEarth::MapNodeOptions mapNodeOpts;
164    mapNodeOpts.enableLighting() = false;
165    osgEarth::MapNode *mapNode = new osgEarth::MapNode(map, mapNodeOpts);
166    _mapNode = mapNode;
167    _sceneRoot = mapNode;
168    if (_mouseCoordsTool.valid())
169        _viewer->removeEventHandler(_mouseCoordsTool.get());
170    _mouseCoordsTool = new osgEarth::Util::MouseCoordsTool(mapNode);
171    _mouseCoordsTool->addCallback(_coordsCallback.get());
172    _viewer->addEventHandler(_mouseCoordsTool.get());
173
174    if (_clipPlaneCullCallback.valid()) {
175        _viewer->getCamera()->removeCullCallback(_clipPlaneCullCallback.get());
176        _clipPlaneCullCallback = NULL;
177    }
178    if (_map->isGeocentric()) {
179        _clipPlaneCullCallback = new osgEarth::Util::AutoClipPlaneCullCallback(mapNode);
180        _viewer->getCamera()->addCullCallback(_clipPlaneCullCallback.get());
181    }
182    _viewer->setSceneData(_sceneRoot.get());
183    _manipulator = new osgEarth::Util::EarthManipulator;
184    _viewer->setCameraManipulator(_manipulator.get());
185    _manipulator->setNode(NULL);
186    _manipulator->setNode(_sceneRoot.get());
187    _manipulator->computeHomePosition();
188    _viewer->home();
189    _needsRedraw = true;
190}
191
192void Renderer::clearMap()
193{
194    if (_map.valid()) {
195        _map->clear();
196    }
197}
198
199bool Renderer::mapMouseCoords(float mouseX, float mouseY, osgEarth::GeoPoint& map)
200{
201    osg::Vec3d world;
202    if (_mapNode->getTerrain()->getWorldCoordsUnderMouse(_viewer->asView(), mouseX, mouseY, world)) {
203        map.fromWorld(_mapNode->getMapSRS(), world);
204        return true;
205    }
206    return false;
207}
208
209void Renderer::addImageLayer(const char *name, const osgEarth::TileSourceOptions& opts)
210{
211    osgEarth::ImageLayerOptions layerOpts(name, opts);
212    _map->addImageLayer(new osgEarth::ImageLayer(layerOpts));
213}
214
215void Renderer::removeImageLayer(const char *name)
216{
217    osgEarth::ImageLayer *layer = _map->getImageLayerByName(name);
218    if (layer != NULL) {
219        _map->removeImageLayer(layer);
220    }
221}
222
223void Renderer::moveImageLayer(const char *name, unsigned int pos)
224{
225    osgEarth::ImageLayer *layer = _map->getImageLayerByName(name);
226    if (layer != NULL) {
227        _map->moveImageLayer(layer, pos);
228    }
229}
230
231void Renderer::setImageLayerOpacity(const char *name, double opacity)
232{
233    osgEarth::ImageLayer *layer = _map->getImageLayerByName(name);
234    if (layer != NULL) {
235        layer->setOpacity(opacity);
236    }
237}
238
239void Renderer::setImageLayerVisibility(const char *name, bool state)
240{
241#if OSGEARTH_MIN_VERSION_REQUIRED(2, 4, 0)
242    osgEarth::ImageLayer *layer = _map->getImageLayerByName(name);
243    if (layer != NULL) {
244        layer->setVisible(state);
245    }
246#endif
247}
248
249void Renderer::addElevationLayer(const char *name, const osgEarth::TileSourceOptions& opts)
250{
251    osgEarth::ElevationLayerOptions layerOpts(name, opts);
252    _map->addElevationLayer(new osgEarth::ElevationLayer(layerOpts));
253}
254
255void Renderer::removeElevationLayer(const char *name)
256{
257    osgEarth::ElevationLayer *layer = _map->getElevationLayerByName(name);
258    if (layer != NULL) {
259        _map->removeElevationLayer(layer);
260    }
261}
262
263void Renderer::moveElevationLayer(const char *name, unsigned int pos)
264{
265    osgEarth::ElevationLayer *layer = _map->getElevationLayerByName(name);
266    if (layer != NULL) {
267        _map->moveElevationLayer(layer, pos);
268    }
269}
270
271void Renderer::setElevationLayerVisibility(const char *name, bool state)
272{
273#if OSGEARTH_MIN_VERSION_REQUIRED(2, 4, 0)
274    osgEarth::ElevationLayer *layer = _map->getElevationLayerByName(name);
275    if (layer != NULL) {
276        layer->setVisible(state);
277    }
278#endif
279}
280
281void Renderer::addModelLayer(const char *name, const osgEarth::ModelSourceOptions& opts)
282{
283    osgEarth::ModelLayerOptions layerOpts(name, opts);
284    _map->addModelLayer(new osgEarth::ModelLayer(layerOpts));
285}
286
287void Renderer::removeModelLayer(const char *name)
288{
289    osgEarth::ModelLayer *layer = _map->getModelLayerByName(name);
290    if (layer != NULL) {
291        _map->removeModelLayer(layer);
292    }
293}
294
295void Renderer::moveModelLayer(const char *name, unsigned int pos)
296{
297    osgEarth::ModelLayer *layer = _map->getModelLayerByName(name);
298    if (layer != NULL) {
299        _map->moveModelLayer(layer, pos);
300    }
301}
302
303void Renderer::setModelLayerVisibility(const char *name, bool state)
304{
305#if OSGEARTH_MIN_VERSION_REQUIRED(2, 4, 0)
306    osgEarth::ModelLayer *layer = _map->getModelLayerByName(name);
307    if (layer != NULL) {
308        layer->setVisible(state);
309    }
310#endif
311}
312
313/**
314 * \brief Resize the render window (image size for renderings)
315 */
316void Renderer::setWindowSize(int width, int height)
317{
318    if (_windowWidth == width &&
319        _windowHeight == height) {
320        TRACE("No change");
321        return;
322    }
323
324    TRACE("Setting window size to %dx%d", width, height);
325
326    _windowWidth = width;
327    _windowHeight = height;
328    osgViewer::ViewerBase::Windows windows;
329    _viewer->getWindows(windows);
330    if (windows.size() == 1) {
331        windows[0]->setWindowRectangle(0, 0, _windowWidth, _windowHeight);
332    } else {
333        ERROR("Num windows: %lu", windows.size());
334    }
335    _needsRedraw = true;
336}
337
338/**
339 * \brief Set the orientation of the camera from a quaternion
340 * rotation
341 *
342 * \param[in] quat A quaternion with scalar part first: w,x,y,z
343 * \param[in] absolute Is rotation absolute or relative?
344 */
345void Renderer::setCameraOrientation(const double quat[4], bool absolute)
346{
347    _manipulator->setRotation(osg::Quat(quat[1], quat[2], quat[3], quat[0]));
348    _needsRedraw = true;
349}
350
351/**
352 * \brief Reset pan, zoom, clipping planes and optionally rotation
353 *
354 * \param[in] resetOrientation Reset the camera rotation/orientation also
355 */
356void Renderer::resetCamera(bool resetOrientation)
357{
358    TRACE("Enter: resetOrientation=%d", resetOrientation ? 1 : 0);
359    _viewer->home();
360    _needsRedraw = true;
361}
362
363/**
364 * \brief Perform a 2D translation of the camera
365 *
366 * x,y pan amount are specified as signed absolute pan amount in viewport
367 * units -- i.e. 0 is no pan, .5 is half the viewport, 2 is twice the viewport,
368 * etc.
369 *
370 * \param[in] x Viewport coordinate horizontal panning (positive number pans
371 * camera left, object right)
372 * \param[in] y Viewport coordinate vertical panning (positive number pans
373 * camera up, object down)
374 * \param[in] absolute Control if pan amount is relative to current or absolute
375 */
376void Renderer::panCamera(double x, double y, bool absolute)
377{
378    TRACE("Enter: %g %g, abs: %d",
379          x, y, (absolute ? 1 : 0));
380
381    _manipulator->pan(x, y);
382    _needsRedraw = true;
383}
384
385void Renderer::rotateCamera(double x, double y, bool absolute)
386{
387    TRACE("Enter: %g %g, abs: %d",
388          x, y, (absolute ? 1 : 0));
389
390    _manipulator->rotate(x, y);
391    _needsRedraw = true;
392}
393
394/**
395 * \brief Dolly camera or set orthographic scaling based on camera type
396 *
397 * \param[in] z Ratio to change zoom (greater than 1 is zoom in, less than 1 is zoom out)
398 * \param[in] absolute Control if zoom factor is relative to current setting or absolute
399 */
400void Renderer::zoomCamera(double z, bool absolute)
401{
402    TRACE("Enter: z: %g, abs: %d",
403          z, (absolute ? 1 : 0));
404
405    // FIXME: zoom here wants y mouse coords in normalized viewport coords
406
407    _manipulator->zoom(0, z);
408    _needsRedraw = true;
409}
410
411void Renderer::keyPress(int key)
412{
413    getEventQueue()->keyPress(key);
414    _needsRedraw = true;
415}
416
417void Renderer::keyRelease(int key)
418{
419    getEventQueue()->keyRelease(key);
420    _needsRedraw = true;
421}
422
423void Renderer::setThrowingEnabled(bool state)
424{
425    _manipulator->getSettings()->setThrowingEnabled(state);
426}
427
428void Renderer::mouseDoubleClick(int button, double x, double y)
429{
430    getEventQueue()->mouseDoubleButtonPress((float)x, (float)y, button);
431    _needsRedraw = true;
432}
433
434void Renderer::mouseClick(int button, double x, double y)
435{
436    getEventQueue()->mouseButtonPress((float)x, (float)y, button);
437    _needsRedraw = true;
438}
439
440void Renderer::mouseDrag(int button, double x, double y)
441{
442    getEventQueue()->mouseMotion((float)x, (float)y);
443    _needsRedraw = true;
444}
445
446void Renderer::mouseRelease(int button, double x, double y)
447{
448    getEventQueue()->mouseButtonRelease((float)x, (float)y, button);
449    _needsRedraw = true;
450}
451
452void Renderer::mouseMotion(double x, double y)
453{
454    //getEventQueue()->mouseMotion((float)x, (float)y);
455    //return;
456    osgEarth::GeoPoint map;
457    if (mapMouseCoords(x, y, map)) {
458        _coordsCallback->set(map, _viewer.get(), _mapNode);
459    } else {
460        _coordsCallback->reset(_viewer.get(), _mapNode);
461    }
462}
463
464void Renderer::mouseScroll(int direction)
465{
466    getEventQueue()->mouseScroll((direction > 0 ? osgGA::GUIEventAdapter::SCROLL_UP : osgGA::GUIEventAdapter::SCROLL_DOWN));
467    _needsRedraw = true;
468}
469
470/**
471 * \brief Set the RGB background color to render into the image
472 */
473void Renderer::setBackgroundColor(float color[3])
474{
475    _bgColor[0] = color[0];
476    _bgColor[1] = color[1];
477    _bgColor[2] = color[2];
478
479    _viewer->getCamera()->setClearColor(osg::Vec4(color[0], color[1], color[2], 1));
480
481    _needsRedraw = true;
482}
483
484/**
485 * \brief Sets flag to trigger rendering next time render() is called
486 */
487void Renderer::eventuallyRender()
488{
489    _needsRedraw = true;
490}
491
492/**
493 * \brief Get a timeout in usecs for select()
494 *
495 * If the paging thread is idle, returns <0 indicating that the
496 * select call can block until data is available.  Otherwise,
497 * if the frame render time was faster than the target frame
498 * rate, return the remaining frame time.
499 */
500long Renderer::getTimeout()
501{
502    if (!checkNeedToDoFrame())
503        // <0 means no timeout, block until socket has data
504        return -1L;
505    if (_lastFrameTime < _minFrameTime) {
506        return (long)1000000.0*(_minFrameTime - _lastFrameTime);
507    } else {
508        // No timeout (poll)
509        return 0L;
510    }
511}
512
513/**
514 * \brief Check if paging thread is quiescent
515 */
516bool Renderer::isPagerIdle()
517{
518    return (!_viewer->getDatabasePager()->requiresUpdateSceneGraph() &&
519            !_viewer->getDatabasePager()->getRequestsInProgress());
520}
521
522/**
523 * \brief Check is frame call is necessary to render and/or update
524 * in response to events or timed actions
525 */
526bool Renderer::checkNeedToDoFrame()
527{
528    return (_needsRedraw ||
529            _viewer->checkNeedToDoFrame());
530}
531
532/**
533 * \brief Cause the rendering to render a new image if needed
534 *
535 * The _needsRedraw flag indicates if a state change has occured since
536 * the last rendered frame
537 */
538bool Renderer::render()
539{
540    if (checkNeedToDoFrame()) {
541        TRACE("Enter needsRedraw=%d",  _needsRedraw ? 1 : 0);
542
543        osg::Timer_t startFrameTick = osg::Timer::instance()->tick();
544        TRACE("Before frame()");
545        _viewer->frame();
546        TRACE("After frame()");
547        osg::Timer_t endFrameTick = osg::Timer::instance()->tick();
548        _lastFrameTime = osg::Timer::instance()->delta_s(startFrameTick, endFrameTick);
549        TRACE("Frame time: %g sec", _lastFrameTime);
550#if 0
551        if (frameTime < minFrameTime) {
552            TRACE("Sleeping for %g secs", minFrameTime-frameTime);
553            OpenThreads::Thread::microSleep(static_cast<unsigned int>(1000000.0*(minFrameTime-frameTime)));
554        }
555#endif
556#ifdef WANT_TRACE
557        if (_viewer->getViewerStats() != NULL) {
558            _viewer->getViewerStats()->report(std::cerr, _viewer->getViewerStats()->getLatestFrameNumber());
559        }
560#endif
561        _needsRedraw = false;
562        return true;
563    } else
564        return false;
565}
566
567/**
568 * \brief Read back the rendered framebuffer image
569 */
570osg::Image *Renderer::getRenderedFrame()
571{
572    return _captureCallback->getImage();
573}
Note: See TracBrowser for help on using the repository browser.