/* -*- mode: c++; c-basic-offset: 4; indent-tabs-mode: nil -*- */ /* * Copyright (C) 2004-2015 HUBzero Foundation, LLC * * Author: Leif Delgass */ #ifndef GEOVIS_RENDERER_H #define GEOVIS_RENDERER_H #include // For struct timeval #include // For time_t #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #define USE_RTT_PICKER #include "Types.h" #include "Trace.h" #include "MouseCoordsTool.h" #include "ScaleBar.h" #include "Placard.h" // Controls if TGA format is sent to client //#define RENDER_TARGA #define TARGA_BYTES_PER_PIXEL 3 namespace GeoVis { class ScreenCaptureCallback : public osg::Camera::DrawCallback { public: ScreenCaptureCallback(osg::Texture2D *texture = NULL) : osg::Camera::DrawCallback(), _texture(texture) { _image = new osg::Image; } virtual void operator()(osg::RenderInfo &renderInfo) const { FRAME("Enter ScreenCaptureCallback"); int width, height; if (renderInfo.getCurrentCamera() == NULL) { ERROR("No camera"); return; } if (renderInfo.getCurrentCamera()->getViewport() == NULL) { ERROR("No viewport"); return; } width = (int)renderInfo.getCurrentCamera()->getViewport()->width(); height = (int)renderInfo.getCurrentCamera()->getViewport()->height(); FRAME("readPixels: %d x %d", width, height); #if 0 //USE_OFFSCREEN_FRAMEBUFFER _image = _texture->getImage(); #else #ifdef RENDER_TARGA _image->readPixels(0, 0, width, height, GL_BGR, GL_UNSIGNED_BYTE); #else _image->readPixels(0, 0, width, height, GL_RGB, GL_UNSIGNED_BYTE); #endif #endif } osg::Image *getImage() { return _image.get(); } osg::Texture2D *getTexture() { return _texture.get(); } private: osg::ref_ptr _texture; osg::ref_ptr _image; }; /** * \brief GIS Renderer */ class Renderer { public: typedef std::string ColorMapId; typedef std::string ViewpointId; enum GraticuleType { GRATICULE_UTM, GRATICULE_MGRS, GRATICULE_GEODETIC, GRATICULE_SHADER }; enum CoordinateDisplayType { COORDS_LATLONG_DECIMAL_DEGREES, COORDS_LATLONG_DEGREES_DECIMAL_MINUTES, COORDS_LATLONG_DEGREES_MINUTES_SECONDS, COORDS_MGRS }; enum SelectMode { SELECT_OFF, SELECT_ON }; Renderer(); virtual ~Renderer(); void setResourcePath(const std::string& path) { TRACE("Set resource path to %s", path.c_str()); _resourcePath = path; } void setCacheBaseDirectory(const std::string& path) { TRACE("Set cache base dir to %s", path.c_str()); _cacheBaseDir = path; } std::string getCacheDirectory() const { return _cacheDir; } void setupCache(); std::string getIconFile(const char *name) const; std::string getBaseImage() const; std::string getPinIcon() const; void setAttribution(const std::string& attrib); // Colormaps void addColorMap(const ColorMapId& id, osg::TransferFunction1D *xfer); void deleteColorMap(const ColorMapId& id); void setColorMapNumberOfTableEntries(const ColorMapId& id, int numEntries); bool renderColorMap(const ColorMapId& id, int width, int height, osg::Image *imgData, bool opaque, float bgColor[3], bool bgr = false, int bytesPerPixel = 3) const; void getColorMapRange(const ColorMapId& id, float *min, float *max) const; std::string getColorMapFilePath(const ColorMapId& id) const; void saveCLRFile(const std::string& path, osg::TransferFunction1D *xfer); // Scene void loadEarthFile(const char *path); void resetMap(osgEarth::MapOptions::CoordinateSystemType type, const osg::Vec4f& bgColor = osg::Vec4f(1,1,1,1), const char *profile = NULL, double bounds[4] = NULL); void clearMap(); // Map options void setCoordinateReadout(bool state, CoordinateDisplayType type = COORDS_LATLONG_DECIMAL_DEGREES, int precision = -1); void setReadout(int mouseX, int mouseY); void clearReadout(); void setScaleBar(bool state); void setScaleBarUnits(ScaleBarUnits units); void setGraticule(bool enable, GraticuleType type = GRATICULE_SHADER); void setViewerLightType(osg::View::LightingMode mode); void setLighting(bool state); void setTerrainColor(const osg::Vec4f& color); void setTerrainEdges(bool state) {} void setTerrainLineColor(const osg::Vec4f& color) {} void setTerrainLineWidth(float width) {} void setTerrainLighting(bool state); void setTerrainVerticalScale(double scale); void setTerrainWireframe(bool state); void setEphemerisTime(time_t utcTime); void setEphemerisTime(int year, int month, int day, double hours); void setSkyAmbient(float ambientLevel); // Image raster layers int getNumImageLayers() const { return (_map.valid() ? _map->getNumImageLayers() : 0); } void getImageLayerNames(std::vector& names) const { if (_map.valid()) { osgEarth::ImageLayerVector layerVector; _map->getImageLayers(layerVector); osgEarth::ImageLayerVector::const_iterator itr; for (itr = layerVector.begin(); itr != layerVector.end(); ++itr) { //osgEarth::UID uid = (*itr)->getUID(); names.push_back((*itr)->getName()); } } } bool addImageLayer(const char *name, osgEarth::TileSourceOptions& opts, unsigned int pos = UINT_MAX, bool enableCache = true, bool coverage = false, bool makeShared = false, bool visible = true, unsigned int minLOD = 0, unsigned int maxLOD = 23); void removeImageLayer(const char *name); void moveImageLayer(const char *name, unsigned int pos); bool getImageLayerExtent(const char *name, osgEarth::GeoExtent &ext); void setImageLayerVisibleRange(const char *name, float min, float max); void setImageLayerLODRange(const char *name, int min, int max); void setImageLayerOpacity(const char *name, double opacity); void setImageLayerVisibility(const char *name, bool state); void addColorFilter(const char *name, const char *shader); void removeColorFilter(const char *name, int idx = -1); // Sequence control bool layerHasSequence(const char *name); bool sequencePause(const char *name); bool sequencePlay(const char *name); bool sequenceSeek(const char *name, unsigned int frame); // Elevation raster layers int getNumElevationLayers() const { return (_map.valid() ? _map->getNumElevationLayers() : 0); } void getElevationLayerNames(std::vector& names) const { if (_map.valid()) { osgEarth::ElevationLayerVector layerVector; _map->getElevationLayers(layerVector); osgEarth::ElevationLayerVector::const_iterator itr; for (itr = layerVector.begin(); itr != layerVector.end(); ++itr) { //osgEarth::UID uid = (*itr)->getUID(); names.push_back((*itr)->getName()); } } } void addElevationLayer(const char *name, osgEarth::TileSourceOptions& opts, unsigned int pos = UINT_MAX, bool enableCache = true, bool visible = true, const char *verticalDatum = NULL, unsigned int minLOD = 0, unsigned int maxLOD = 23); void removeElevationLayer(const char *name); void moveElevationLayer(const char *name, unsigned int pos); bool getElevationLayerExtent(const char *name, osgEarth::GeoExtent &ext); void setElevationLayerVisibleRange(const char *name, float min, float max); void setElevationLayerVisibility(const char *name, bool state); // Terrain mask layers int getNumTerrainMaskLayers() const { if (!_map.valid()) return 0; osgEarth::MaskLayerVector layers; _map->getTerrainMaskLayers(layers); return (int)layers.size(); } void getTerrainMaskLayerNames(std::vector& names) const { if (_map.valid()) { osgEarth::MaskLayerVector layerVector; _map->getTerrainMaskLayers(layerVector); osgEarth::MaskLayerVector::const_iterator itr; for (itr = layerVector.begin(); itr != layerVector.end(); ++itr) { //osgEarth::UID uid = (*itr)->getUID(); names.push_back((*itr)->getName()); } } } osgEarth::MaskLayer *getTerrainMaskLayerByName(const std::string& name) { if (!_map.valid()) return NULL; osgEarth::MaskLayerVector layerVector; _map->getTerrainMaskLayers(layerVector); osgEarth::MaskLayerVector::iterator itr; for (itr = layerVector.begin(); itr != layerVector.end(); ++itr) { if ((*itr)->getName() == name) { return (*itr).get(); } } return NULL; } void addTerrainMaskLayer(const char *name, osgEarth::MaskSourceOptions& opts, unsigned int minLOD = 0); void removeTerrainMaskLayer(const char *name); bool getTerrainMaskLayerExtent(const char *name, osgEarth::GeoExtent &ext); // Model layers int getNumModelLayers() const { return (_map.valid() ? _map->getNumModelLayers() : 0); } void getModelLayerNames(std::vector& names) const { if (_map.valid()) { osgEarth::ModelLayerVector layerVector; _map->getModelLayers(layerVector); osgEarth::ModelLayerVector::const_iterator itr; for (itr = layerVector.begin(); itr != layerVector.end(); ++itr) { //osgEarth::UID uid = (*itr)->getUID(); names.push_back((*itr)->getName()); } } } void addModelLayer(const char *name, osgEarth::ModelSourceOptions& opts, unsigned int pos = UINT_MAX, bool enableCache = true, bool lighting = true, bool visible = true, bool terrainPatch = false); void removeModelLayer(const char *name); void moveModelLayer(const char *name, unsigned int pos); bool getModelLayerExtent(const char *name, osgEarth::GeoExtent &ext); //void setModelLayerVisibleRange(const char *name, float min, float max); void setModelLayerOpacity(const char *name, double opacity); void setModelLayerVisibility(const char *name, bool state); // Render window void setWindowSize(int width, int height); int getWindowWidth() const { return _windowWidth; } int getWindowHeight() const { return _windowHeight; } // Camera void saveNamedViewpoint(const char *name); bool restoreNamedViewpoint(const char *name, double durationSecs); bool removeNamedViewpoint(const char *name); osgEarth::Viewpoint getViewpoint(); void setViewpoint(const osgEarth::Viewpoint& v, double durationSecs = 0.0); double getMaxDistanceFromExtent(const osgEarth::GeoExtent& extent); void setViewpointFromExtent(const osgEarth::GeoExtent& ext, double durationSecs = 0.0); void setViewpointFromRect(double xmin, double ymin, double xmax, double ymax, const osgEarth::SpatialReference *srs = NULL, double durationSecs = 0.0); void resetCamera(bool resetOrientation = true); void setCameraOrientation(const double quat[4], bool absolute = true); void panCamera(double x, double y); void rotateCamera(double x, double y); void zoomCamera(double z); void setCameraDistance(double dist); // Keyboard events void keyPress(int key); void keyRelease(int key); // Mouse events void setThrowingEnabled(bool state); void mouseClick(int button, double x, double y); void mouseDoubleClick(int button, double x, double y); void mouseDrag(int button, double x, double y); void mouseRelease(int button, double x, double y); void mouseMotion(double x, double y); void mouseScroll(int direction); void pickPending(bool value) { _pickPending = value; } // Rendering an image void setBackgroundColor(float color[3]); void eventuallyRender(); bool render(); osg::Image *getRenderedFrame(); bool mapMouseCoords(float mouseX, float mouseY, osgEarth::GeoPoint &pt, bool invertY = true); double computeMapScale(); const osgEarth::SpatialReference *getMapSRS() { if (_mapNode.valid()) { return _mapNode->getMapSRS(); } else { return NULL; } } void addPlaceNode(double latitude, double longitude, char *labelText); void hoverPlaceNode(int x, int y, bool invertY = true); void deletePlaceNode(int x, int y, bool invertY = true); bool getMousePoint(double *x, double *y, double *z) { return (_coordsCallback.valid() && _coordsCallback->report(x, y, z)); } bool mouseToLatLong(int mouseX, int mouseY, double *latitude, double *longitude); bool getWorldCoords(const osgEarth::GeoPoint& mapPt, osg::Vec3d *world); bool worldToScreen(const osg::Vec3d& world, osg::Vec3d *screen, bool invertY = true); bool worldToScreen(std::vector& coords, bool invertY = true); void setMaximumFrameRateInHertz(double rate) { if (rate > 60.0) rate = 60.0; if (rate < 0.25) rate = 0.25; _minFrameTime = 1.0/rate; if (_viewer.valid()) { osgUtil::IncrementalCompileOperation *op = _viewer->getDatabasePager()->getIncrementalCompileOperation(); if (op != NULL) { TRACE("Setting DB Pager target frame rate to %g", rate); op->setTargetFrameRate(rate); } } TRACE("Frame rate target: %.2f Hz", (float)getMaximumFrameRateInHertz()); TRACE("Frame time target: %.2f msec", _minFrameTime * 1000.0f); } void setMaximumBitrate(double bitsPerSecond) { TRACE("Setting max bitrate to %g", bitsPerSecond); unsigned long bitsPerFrame = (_windowWidth * _windowHeight * 3 + 16) * 8; double fps = bitsPerSecond / ((double)bitsPerFrame); setMaximumFrameRateInHertz(fps); TRACE("Bandwidth target: %.2f Mbps", (float)(getMaximumBitrate()/1.0e6)); } double getMaximumFrameRateInHertz() { return (1.0/_minFrameTime); } double getMaximumBitrate() { unsigned long bitsPerFrame = (_windowWidth * _windowHeight * 3 + 16) * 8; return ((double)bitsPerFrame * getMaximumFrameRateInHertz()); } void markFrameStart(); void markFrameEnd(); void setIdleTimeout(long timeout) { if (timeout != -1L && (double)timeout < _minFrameTime) { ERROR("Timeout must be more than %g sec", _minFrameTime); return; } TRACE("Setting idle timeout to %ld sec", timeout); _idleTimeout = timeout; } long getIdleTimeout() { return _idleTimeout; } void getTimeout(struct timeval *tv); void mapNodeUpdate(); std::string getCanonicalPath(const std::string& url) const; void writeScene(const std::string& file); // Placard info window void enablePlacard(const char *layerName, bool state); void setPlacardConfig(const Placard& placardConf, const char *layerName); Placard getPlacardConfig(const char *layerName) { Placard ret; PlacardHashmap::iterator itr = _placardConfigs.find(layerName); if (itr != _placardConfigs.end()) { ret = itr->second; } return ret; } // Selection void setSelectMode(SelectMode mode); void selectFeatures(std::vector& featureIDs, const char *layerName, bool clear = true); void deselectFeatures(std::vector& featureIDs, const char *layerName); void addPlacard(const osgEarth::GeoPoint& location, osgEarth::Features::Feature *feature, const char *layerName); void clearSelection(); void addRhumbBox(double latMin, double latMax, double longMin, double longMax); void initBoxSelection(int x, int y); void updateBoxSelection(int x, int y); void getBoxSelection(double *latMin, double *latMax, double *longMin, double *longMax, const osgEarth::SpatialReference *outSRS = NULL); void clearBoxSelection(); // These methods are based on the deprecated annotation API bool select(osgEarth::Annotation::AnnotationNode *node) { if (_selected.find(node) == _selected.end()) { _selected.insert(node); return true; } else { return false; } } std::set& getHovered() { return _hovered; } std::set& getSelected() { return _selected; } // Scene Graph osg::Group *getSceneRoot() { return _sceneRoot.get(); } osgEarth::MapNode *getMapNode() { return _mapNode.get(); } osg::Group *getAnnotations() { initAnnotations(); return _annotations.get(); } osg::Group *getPlaceNodes() { initAnnotations(); return _placeNodes.get(); } private: typedef std::tr1::unordered_map > ColorMapHashmap; typedef std::tr1::unordered_map ViewpointHashmap; typedef std::tr1::unordered_map PlacardHashmap; typedef std::tr1::unordered_map > FeatureSelectionHashmap; void initAnnotations(); void clearSelectionAnnotationNodes(); void initViewer(); void finalizeViewer(); void initControls(); void updateDebugLabel(); void initEarthManipulator(); void initMouseCoordsTool(CoordinateDisplayType type = COORDS_LATLONG_DECIMAL_DEGREES, int precision = -1); osgEarth::Util::MGRSFormatter::Precision getMGRSPrecision(int precisionInMeters); void initColorMaps(); void initCamera(); bool isPagerIdle(); bool checkNeedToDoFrame(); osgGA::EventQueue *getEventQueue(); bool _needsRedraw; int _windowWidth, _windowHeight; double _mapScale; float _bgColor[3]; long _idleTimeout; double _minFrameTime; double _lastFrameTime; double _renderTime; osg::Timer_t _startFrameTime; osg::Timer_t _renderStartTime; osg::Timer_t _renderStopTime; ColorMapHashmap _colorMaps; std::string _resourcePath; std::string _cacheBaseDir; std::string _cacheDir; std::string _baseURI; std::string _attribution; osg::ref_ptr _sceneRoot; osg::ref_ptr _graticule; osg::ref_ptr _annotations; PlacardHashmap _placardConfigs; double _anchorLat, _anchorLong; SelectMode _selectMode; osg::ref_ptr _selectionBox; #ifdef USE_RTT_PICKER osg::ref_ptr _picker; #endif bool _pickPending; osg::ref_ptr _placeNodes; FeatureSelectionHashmap _selectedFeatures; std::set _hovered; std::set _selected; osg::ref_ptr _mapNode; osg::ref_ptr _map; osg::ref_ptr _skyNode; osg::ref_ptr _viewer; osg::ref_ptr _captureCallback; osg::ref_ptr _clipPlaneCullCallback; osg::ref_ptr _coordsBox; osg::ref_ptr _mouseCoordsTool; osg::ref_ptr _coordsCallback; osg::ref_ptr _debugBox; osg::ref_ptr _debugLabel; osg::ref_ptr _copyrightScaleBox; osg::ref_ptr _copyrightLabel; osg::ref_ptr _scaleLabel; osg::ref_ptr _scaleBar; ScaleBarUnits _scaleBarUnits; osg::ref_ptr _manipulator; osg::ref_ptr _stateManip; osg::ref_ptr _verticalScale; ViewpointHashmap _viewpoints; }; class MapNodeCallback : public osg::NodeCallback { public: MapNodeCallback(Renderer *renderer) : _renderer(renderer) {} virtual void operator()(osg::Node* node, osg::NodeVisitor* nv) { _renderer->mapNodeUpdate(); traverse(node, nv); } private: Renderer *_renderer; }; class SelectPlaceNodesVisitor : public osg::NodeVisitor { public: SelectPlaceNodesVisitor(Renderer *renderer, double latMin, double latMax, double longMin, double longMax) : osg::NodeVisitor(osg::NodeVisitor::TRAVERSE_ALL_CHILDREN), _renderer(renderer), _latMin(latMin), _latMax(latMax), _longMin(longMin), _longMax(longMax) {} virtual void apply(osg::Node& node); private: Renderer *_renderer; double _latMin, _latMax, _longMin, _longMax; }; } #endif