source: geovis/trunk/Picker.cpp @ 5941

Last change on this file since 5941 was 5941, checked in by ldelgass, 9 years ago

Fix for feature highlight

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