[5873] | 1 | /* -*- mode: c++; c-basic-offset: 4; indent-tabs-mode: nil -*- */ |
---|
| 2 | /* |
---|
| 3 | * Copyright (C) 2015 HUBzero Foundation, LLC |
---|
| 4 | * |
---|
| 5 | * Author: Leif Delgass <ldelgass@purdue.edu> |
---|
| 6 | */ |
---|
| 7 | #include <vector> |
---|
[5890] | 8 | #include <cstdio> |
---|
[5873] | 9 | #include <cassert> |
---|
| 10 | |
---|
| 11 | #include <osgEarth/Registry> |
---|
| 12 | #include <osgEarth/ShaderGenerator> |
---|
| 13 | #include <osgEarth/ObjectIndex> |
---|
| 14 | #include <osgEarthFeatures/Feature> |
---|
| 15 | #include <osgEarthFeatures/FeatureIndex> |
---|
[5890] | 16 | #include <osgEarthFeatures/FeatureSourceIndexNode> |
---|
[5873] | 17 | #include <osgEarthAnnotation/AnnotationNode> |
---|
| 18 | #include <osgEarthAnnotation/LabelNode> |
---|
| 19 | #include <osgEarthSymbology/Color> |
---|
| 20 | #include <osgEarthSymbology/Style> |
---|
| 21 | #include <osgEarthSymbology/TextSymbol> |
---|
| 22 | |
---|
| 23 | #include "Picker.h" |
---|
[5944] | 24 | #include "Placard.h" |
---|
[5873] | 25 | #include "Renderer.h" |
---|
[5890] | 26 | #include "RendererCmd.h" |
---|
[5873] | 27 | #include "Trace.h" |
---|
| 28 | |
---|
| 29 | using namespace GeoVis; |
---|
| 30 | |
---|
| 31 | static const char *highlightVert = |
---|
[5939] | 32 | "#version 130\n" |
---|
[5873] | 33 | "uniform uint objectid_to_highlight; \n" |
---|
| 34 | "uint oe_index_objectid; // Stage global containing object id \n" |
---|
| 35 | "flat out int selected; \n" |
---|
| 36 | "void checkForHighlight(inout vec4 vertex) \n" |
---|
| 37 | "{ \n" |
---|
| 38 | " selected = (objectid_to_highlight > 1u && objectid_to_highlight == oe_index_objectid) ? 1 : 0; \n" |
---|
| 39 | "} \n"; |
---|
| 40 | |
---|
| 41 | static const char *highlightFrag = |
---|
[5939] | 42 | "#version 130\n" |
---|
[5873] | 43 | "flat in int selected; \n" |
---|
| 44 | "void highlightFragment(inout vec4 color) \n" |
---|
| 45 | "{ \n" |
---|
| 46 | " if ( selected == 1 ) \n" |
---|
| 47 | " color.rgb = mix(color.rgb, clamp(vec3(0.5,0.5,2.0)*(1.0-color.rgb), 0.0, 1.0), 0.5); \n" |
---|
| 48 | "} \n"; |
---|
| 49 | |
---|
| 50 | static osg::Uniform *s_highlightUniform = NULL; |
---|
| 51 | |
---|
| 52 | static void installHighlighter(osg::StateSet *stateSet, int attrLocation) |
---|
| 53 | { |
---|
[5938] | 54 | TRACE("GLSL Version: %s", GLSL_VERSION_STR); |
---|
[5873] | 55 | // This shader program will highlight the selected object. |
---|
| 56 | osgEarth::VirtualProgram *vp = osgEarth::VirtualProgram::getOrCreate(stateSet); |
---|
| 57 | vp->setFunction("checkForHighlight", highlightVert, osgEarth::ShaderComp::LOCATION_VERTEX_CLIP); |
---|
| 58 | vp->setFunction("highlightFragment", highlightFrag, osgEarth::ShaderComp::LOCATION_FRAGMENT_COLORING); |
---|
| 59 | |
---|
| 60 | // Since we're accessing object IDs, we need to load the indexing shader as well: |
---|
| 61 | osgEarth::Registry::objectIndex()->loadShaders(vp); |
---|
| 62 | |
---|
| 63 | // A uniform that will tell the shader which object to highlight: |
---|
[5941] | 64 | if (s_highlightUniform == NULL) { |
---|
| 65 | s_highlightUniform = new osg::Uniform("objectid_to_highlight", 0u); |
---|
| 66 | } |
---|
[5873] | 67 | stateSet->addUniform(s_highlightUniform); |
---|
| 68 | } |
---|
| 69 | |
---|
[5938] | 70 | void GeoVis::setHighlightByObjectID(osgEarth::ObjectID id) |
---|
| 71 | { |
---|
| 72 | if (s_highlightUniform != NULL) { |
---|
| 73 | s_highlightUniform->set(id); |
---|
| 74 | } |
---|
| 75 | } |
---|
| 76 | |
---|
| 77 | void GeoVis::clearHighlight() |
---|
| 78 | { |
---|
| 79 | if (s_highlightUniform != NULL) { |
---|
| 80 | s_highlightUniform->set(0U); |
---|
| 81 | } |
---|
| 82 | } |
---|
| 83 | |
---|
[5873] | 84 | HoverCallback::HoverCallback(Renderer *renderer) : |
---|
| 85 | _renderer(renderer) |
---|
| 86 | { |
---|
| 87 | installHighlighter(renderer->getMapNode()->getOrCreateStateSet(), |
---|
| 88 | osgEarth::Registry::objectIndex()->getObjectIDAttribLocation()); |
---|
| 89 | } |
---|
| 90 | |
---|
| 91 | void HoverCallback::onHit(osgEarth::ObjectID id) |
---|
| 92 | { |
---|
| 93 | // First see whether it's a feature: |
---|
| 94 | osgEarth::Features::FeatureIndex *index = osgEarth::Registry::objectIndex()->get<osgEarth::Features::FeatureIndex>(id); |
---|
| 95 | osgEarth::Features::Feature *feature = index ? index->getFeature(id) : 0L; |
---|
| 96 | INFO("Hover hit"); |
---|
| 97 | if (feature) { |
---|
| 98 | INFO("Hit feature ID: %lu (of %d), oid: %lu", feature->getFID(), index->size(), id); |
---|
| 99 | const osgEarth::Features::AttributeTable &attrs = feature->getAttrs(); |
---|
| 100 | for (osgEarth::Features::AttributeTable::const_iterator itr = attrs.begin(); itr != attrs.end(); ++itr) { |
---|
| 101 | INFO(" attr: %s", itr->first.c_str()); |
---|
| 102 | switch (itr->second.first) { |
---|
| 103 | case osgEarth::Features::ATTRTYPE_STRING: |
---|
| 104 | INFO(" value: %s", itr->second.getString().c_str()); |
---|
| 105 | break; |
---|
| 106 | case osgEarth::Features::ATTRTYPE_INT: |
---|
| 107 | INFO(" value: %d", itr->second.getInt()); |
---|
| 108 | break; |
---|
| 109 | case osgEarth::Features::ATTRTYPE_DOUBLE: |
---|
| 110 | INFO(" value: %g", itr->second.getDouble()); |
---|
| 111 | break; |
---|
| 112 | case osgEarth::Features::ATTRTYPE_BOOL: |
---|
| 113 | INFO(" value: %s", itr->second.getBool() ? "true" : "false"); |
---|
| 114 | break; |
---|
| 115 | default: |
---|
| 116 | INFO(" value: unknown type"); |
---|
| 117 | } |
---|
| 118 | } |
---|
| 119 | //INFO("Feature name: %s", feature->getString("name").c_str()); |
---|
| 120 | } else { |
---|
| 121 | osgEarth::Annotation::AnnotationNode* anno = |
---|
| 122 | osgEarth::Registry::objectIndex()->get<osgEarth::Annotation::AnnotationNode>(id); |
---|
| 123 | |
---|
| 124 | std::set<osgEarth::Annotation::AnnotationNode*> toUnHover; |
---|
| 125 | std::set<osgEarth::Annotation::AnnotationNode*>& hovered = _renderer->getHovered(); |
---|
| 126 | for (std::set<osgEarth::Annotation::AnnotationNode*>::iterator itr = hovered.begin(); |
---|
| 127 | itr != hovered.end(); ++itr) { |
---|
| 128 | toUnHover.insert(*itr); |
---|
| 129 | } |
---|
| 130 | if (anno != NULL && anno->getDecoration().empty()) { |
---|
| 131 | INFO("Hit AnnotationNode: %p", anno); |
---|
| 132 | #if 0 |
---|
| 133 | if (hovered.find(anno) == hovered.end()) { |
---|
| 134 | hovered.insert(anno); |
---|
| 135 | anno->setDecoration("hover"); |
---|
| 136 | _renderer->eventuallyRender(); |
---|
| 137 | } |
---|
| 138 | toUnHover.erase(anno); |
---|
| 139 | #endif |
---|
| 140 | } |
---|
| 141 | #if 0 |
---|
| 142 | for (std::set<osgEarth::Annotation::AnnotationNode *>::iterator itr = toUnHover.begin(); |
---|
| 143 | itr != toUnHover.end(); ++itr) { |
---|
| 144 | hovered.erase(*itr); |
---|
| 145 | (*itr)->clearDecoration(); |
---|
| 146 | _renderer->eventuallyRender(); |
---|
| 147 | } |
---|
| 148 | #endif |
---|
| 149 | } |
---|
[5941] | 150 | setHighlightByObjectID(id); |
---|
[5873] | 151 | } |
---|
| 152 | |
---|
| 153 | void HoverCallback::onMiss() |
---|
| 154 | { |
---|
| 155 | INFO("Hover miss"); |
---|
| 156 | std::set<osgEarth::Annotation::AnnotationNode*> toUnHover; |
---|
| 157 | std::set<osgEarth::Annotation::AnnotationNode*>& hovered = _renderer->getHovered(); |
---|
| 158 | for (std::set<osgEarth::Annotation::AnnotationNode*>::iterator itr = hovered.begin(); |
---|
| 159 | itr != hovered.end(); ++itr) { |
---|
| 160 | toUnHover.insert(*itr); |
---|
| 161 | } |
---|
| 162 | for (std::set<osgEarth::Annotation::AnnotationNode *>::iterator itr = toUnHover.begin(); |
---|
| 163 | itr != toUnHover.end(); ++itr) { |
---|
| 164 | hovered.erase(*itr); |
---|
| 165 | (*itr)->clearDecoration(); |
---|
| 166 | _renderer->eventuallyRender(); |
---|
| 167 | } |
---|
[5941] | 168 | clearHighlight(); |
---|
[5873] | 169 | } |
---|
| 170 | |
---|
| 171 | bool HoverCallback::accept(const osgGA::GUIEventAdapter& ea, const osgGA::GUIActionAdapter& aa) |
---|
| 172 | { |
---|
| 173 | // Pick whenever mouse moves |
---|
| 174 | return ea.getEventType() == ea.MOVE; |
---|
| 175 | } |
---|
| 176 | |
---|
| 177 | SelectCallback::SelectCallback(Renderer *renderer) : |
---|
| 178 | _renderer(renderer) |
---|
| 179 | { |
---|
| 180 | installHighlighter(renderer->getMapNode()->getOrCreateStateSet(), |
---|
| 181 | osgEarth::Registry::objectIndex()->getObjectIDAttribLocation()); |
---|
[5890] | 182 | installHighlighter(renderer->getPlaceNodes()->getOrCreateStateSet(), |
---|
| 183 | osgEarth::Registry::objectIndex()->getObjectIDAttribLocation()); |
---|
[5873] | 184 | } |
---|
| 185 | |
---|
| 186 | void SelectCallback::onHit(osgEarth::ObjectID id) |
---|
| 187 | { |
---|
| 188 | // First see whether it's a feature: |
---|
| 189 | osgEarth::Features::FeatureIndex *index = osgEarth::Registry::objectIndex()->get<osgEarth::Features::FeatureIndex>(id); |
---|
| 190 | osgEarth::Features::Feature *feature = index ? index->getFeature(id) : 0L; |
---|
[5944] | 191 | INFO("Select hit oid: %lu at %g,%g", id, _pickPoint.x(), _pickPoint.y()); |
---|
[5873] | 192 | if (feature) { |
---|
[5944] | 193 | osgEarth::Features::FeatureSourceIndex *fsi = dynamic_cast<osgEarth::Features::FeatureSourceIndex *>(index); |
---|
| 194 | std::string layerName; |
---|
| 195 | if (fsi) { |
---|
| 196 | osgEarth::Features::FeatureSource *source = fsi->getFeatureSource(); |
---|
| 197 | if (source) { |
---|
| 198 | layerName = source->getFeatureSourceOptions().name().get(); |
---|
[5873] | 199 | } |
---|
| 200 | } |
---|
[5944] | 201 | INFO("Hit feature ID: %lu (of %d) layer: %s", feature->getFID(), index->size(), layerName.c_str()); |
---|
| 202 | |
---|
[5873] | 203 | clearSelection(); |
---|
[5944] | 204 | |
---|
| 205 | const osgEarth::Features::AttributeTable &attrs = feature->getAttrs(); |
---|
| 206 | Placard placard = _renderer->getPlacardConfig(layerName); |
---|
| 207 | if (placard.getNumEntries() == 0) { |
---|
| 208 | placard.addAllAttributes(attrs); |
---|
| 209 | } |
---|
| 210 | PlacardNode *label = |
---|
[5969] | 211 | new PlacardNode(_renderer->getMapNode(), _pickPoint, placard, attrs); |
---|
[5873] | 212 | label->getOrCreateStateSet()->setRenderBinDetails(INT_MAX, "RenderBin"); |
---|
| 213 | _renderer->getAnnotations()->addChild(label); |
---|
[5944] | 214 | |
---|
[5890] | 215 | char mesg[256]; |
---|
| 216 | snprintf(mesg, sizeof(mesg), "nv>select feature %u %lu %d {%s}\n", id, feature->getFID(), index->size(), layerName.c_str()); |
---|
| 217 | size_t length = strlen(mesg); |
---|
| 218 | queueResponse(mesg, length, Response::VOLATILE, Response::DATA); |
---|
[5873] | 219 | } else { |
---|
| 220 | clearSelection(); |
---|
[5890] | 221 | osgEarth::Annotation::AnnotationNode* anno = |
---|
| 222 | osgEarth::Registry::objectIndex()->get<osgEarth::Annotation::AnnotationNode>(id); |
---|
| 223 | if (anno != NULL) { |
---|
[5942] | 224 | INFO("Hit AnnotationNode: %p, \"%s\"", anno, anno->getName().c_str()); |
---|
[5890] | 225 | char mesg[256]; |
---|
| 226 | snprintf(mesg, sizeof(mesg), "nv>select annotation %u {%s}\n", id, anno->getName().c_str()); |
---|
| 227 | size_t length = strlen(mesg); |
---|
| 228 | queueResponse(mesg, length, Response::VOLATILE, Response::DATA); |
---|
| 229 | } |
---|
[5873] | 230 | } |
---|
[5941] | 231 | setHighlightByObjectID(id); |
---|
[5873] | 232 | _renderer->eventuallyRender(); |
---|
| 233 | } |
---|
| 234 | |
---|
| 235 | void SelectCallback::onMiss() |
---|
| 236 | { |
---|
| 237 | INFO("Select miss"); |
---|
| 238 | clearSelection(); |
---|
[5941] | 239 | clearHighlight(); |
---|
[5890] | 240 | |
---|
| 241 | static const char *mesg = "nv>select clear\n"; |
---|
| 242 | static const size_t length = strlen(mesg); |
---|
| 243 | queueResponse(mesg, length, Response::STATIC, Response::DATA); |
---|
| 244 | |
---|
[5873] | 245 | _renderer->eventuallyRender(); |
---|
| 246 | } |
---|
| 247 | |
---|
| 248 | void SelectCallback::clearSelection() |
---|
| 249 | { |
---|
| 250 | osg::Group *nodes = _renderer->getAnnotations(); |
---|
| 251 | std::vector<osg::Node *> toRemove; |
---|
| 252 | for (unsigned int i = 0; i < nodes->getNumChildren(); i++) { |
---|
| 253 | osg::Node *node = nodes->getChild(i); |
---|
[5961] | 254 | if (dynamic_cast<osgEarth::Annotation::OrthoNode *>(node) != NULL) { |
---|
[5873] | 255 | toRemove.push_back(node); |
---|
| 256 | } |
---|
| 257 | } |
---|
| 258 | for (std::vector<osg::Node *>::iterator itr = toRemove.begin(); |
---|
| 259 | itr != toRemove.end(); ++itr) { |
---|
| 260 | nodes->removeChild(*itr); |
---|
| 261 | } |
---|
| 262 | } |
---|
| 263 | |
---|
| 264 | bool SelectCallback::accept(const osgGA::GUIEventAdapter& ea, const osgGA::GUIActionAdapter& aa) |
---|
| 265 | { |
---|
| 266 | _renderer->mapMouseCoords(ea.getX(), ea.getY(), _pickPoint, false); |
---|
| 267 | // Pick on click |
---|
| 268 | return ea.getEventType() == ea.RELEASE; |
---|
| 269 | } |
---|
| 270 | |
---|
| 271 | void DeleteCallback::onHit(osgEarth::ObjectID id) |
---|
| 272 | { |
---|
| 273 | // First see whether it's a feature: |
---|
| 274 | osgEarth::Features::FeatureIndex *index = osgEarth::Registry::objectIndex()->get<osgEarth::Features::FeatureIndex>(id); |
---|
| 275 | osgEarth::Features::Feature *feature = index ? index->getFeature(id) : 0L; |
---|
| 276 | |
---|
| 277 | if (feature) { |
---|
| 278 | TRACE("Hit feature ID: %lu, oid: %lu name: %s", feature->getFID(), id); |
---|
| 279 | TRACE("Feature name: %s", feature->getString("name").c_str()); |
---|
| 280 | } else { |
---|
| 281 | TRACE("Picker hit!"); |
---|
| 282 | osgEarth::Annotation::AnnotationNode* anno = |
---|
| 283 | osgEarth::Registry::objectIndex()->get<osgEarth::Annotation::AnnotationNode>(id); |
---|
| 284 | std::set<osgEarth::Annotation::AnnotationNode*>& hovered = _renderer->getHovered(); |
---|
| 285 | anno->clearDecoration(); |
---|
| 286 | _renderer->getPlaceNodes()->removeChild(anno); |
---|
| 287 | if (hovered.find(anno) != hovered.end()) { |
---|
| 288 | hovered.erase(anno); |
---|
| 289 | } |
---|
| 290 | } |
---|
| 291 | } |
---|