source: geovis/trunk/Picker.cpp @ 5939

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

Revert change to shader version string (highlight shader requires version 1.30)

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