source: geovis/trunk/Picker.cpp

Last change on this file was 6281, checked in by ldelgass, 8 years ago

Update select protocol response to use add|delete|set sub-command

  • Property svn:eol-style set to native
File size: 10.9 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 "Placard.h"
25#include "Renderer.h"
26#include "RendererCmd.h"
27#include "Trace.h"
28
29using namespace GeoVis;
30
31static const char *highlightVert =
32    "#version 130\n"
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
41static const char *highlightFrag =
42    "#version 130\n"
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
50static osg::Uniform *s_highlightUniform = NULL;
51
52static void installHighlighter(osg::StateSet *stateSet, int attrLocation)
53{
54    TRACE("GLSL Version: %s", GLSL_VERSION_STR);
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:
64    if (s_highlightUniform == NULL) {
65        s_highlightUniform = new osg::Uniform("objectid_to_highlight", 0u);
66    }
67    stateSet->addUniform(s_highlightUniform);
68}
69
70void GeoVis::setHighlightByObjectID(osgEarth::ObjectID id)
71{
72    if (s_highlightUniform != NULL) {
73        s_highlightUniform->set(id);
74    }
75}
76
77void GeoVis::clearHighlight()
78{
79    if (s_highlightUniform != NULL) {
80        s_highlightUniform->set(0U);
81    }
82}
83
84HoverCallback::HoverCallback(Renderer *renderer) :
85    _renderer(renderer)
86{
87    installHighlighter(renderer->getMapNode()->getOrCreateStateSet(),
88                       osgEarth::Registry::objectIndex()->getObjectIDAttribLocation());
89}
90
91void 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    TRACE("Hover hit");
97    if (feature) {
98        TRACE("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            TRACE(" attr: %s", itr->first.c_str());
102            switch (itr->second.first) {
103            case osgEarth::Features::ATTRTYPE_STRING:
104                TRACE(" value: %s", itr->second.getString().c_str());
105                break;
106            case osgEarth::Features::ATTRTYPE_INT:
107                TRACE(" value: %d", itr->second.getInt());
108                break;
109            case osgEarth::Features::ATTRTYPE_DOUBLE:
110                TRACE(" value: %g", itr->second.getDouble());
111                break;
112            case osgEarth::Features::ATTRTYPE_BOOL:
113                TRACE(" value: %s", itr->second.getBool() ? "true" : "false");
114                break;
115            default:
116                TRACE(" value: unknown type");
117            }
118        }
119        //TRACE("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) {
131            TRACE("Hit AnnotationNode: %p", anno);
132#if 0
133            if (anno->getDecoration().empty() &&
134                hovered.find(anno) == hovered.end()) {
135                hovered.insert(anno);
136                anno->setDecoration("hover");
137                _renderer->eventuallyRender();
138            }
139            toUnHover.erase(anno);
140#endif
141        }
142#if 0
143        for (std::set<osgEarth::Annotation::AnnotationNode *>::iterator itr = toUnHover.begin();
144             itr != toUnHover.end(); ++itr) {
145            hovered.erase(*itr);
146            (*itr)->clearDecoration();
147            _renderer->eventuallyRender();
148        }
149#endif
150    }
151    setHighlightByObjectID(id);
152}
153
154void HoverCallback::onMiss()
155{
156    TRACE("Hover miss");
157    std::set<osgEarth::Annotation::AnnotationNode*> toUnHover;
158    std::set<osgEarth::Annotation::AnnotationNode*>& hovered = _renderer->getHovered();
159    for (std::set<osgEarth::Annotation::AnnotationNode*>::iterator itr = hovered.begin();
160         itr != hovered.end(); ++itr) {
161        toUnHover.insert(*itr);
162    }
163    for (std::set<osgEarth::Annotation::AnnotationNode *>::iterator itr = toUnHover.begin();
164         itr != toUnHover.end(); ++itr) {
165        hovered.erase(*itr);
166#ifndef NEW_ANNOTATION_API
167        (*itr)->clearDecoration();
168#endif
169        _renderer->eventuallyRender();
170    }
171    clearHighlight();
172}
173
174bool HoverCallback::accept(const osgGA::GUIEventAdapter& ea, const osgGA::GUIActionAdapter& aa)
175{
176    // Pick whenever mouse moves
177    return ea.getEventType() == ea.MOVE;
178}
179
180SelectCallback::SelectCallback(Renderer *renderer) :
181    _renderer(renderer),
182    _modifier(false)
183{
184    installHighlighter(renderer->getMapNode()->getOrCreateStateSet(),
185                       osgEarth::Registry::objectIndex()->getObjectIDAttribLocation());
186    installHighlighter(renderer->getPlaceNodes()->getOrCreateStateSet(),
187                       osgEarth::Registry::objectIndex()->getObjectIDAttribLocation());
188}
189
190void SelectCallback::onHit(osgEarth::ObjectID id)
191{
192    // First see whether it's a feature:
193    osgEarth::Features::FeatureIndex *index = osgEarth::Registry::objectIndex()->get<osgEarth::Features::FeatureIndex>(id);
194    osgEarth::Features::Feature *feature = index ? index->getFeature(id) : 0L;
195    TRACE("Select hit oid: %lu at %g,%g", id, _pickPoint.x(), _pickPoint.y());
196    if (feature) {
197        osgEarth::Features::FeatureSourceIndex *fsi = dynamic_cast<osgEarth::Features::FeatureSourceIndex *>(index);
198        std::string layerName;
199        if (fsi) {
200            osgEarth::Features::FeatureSource *source = fsi->getFeatureSource();
201            if (source) {
202                layerName = source->getFeatureSourceOptions().name().get();
203            }
204        }
205        TRACE("Hit FID: %lu (of %d) layer: %s", feature->getFID(), index->size(), layerName.c_str());
206
207        std::vector<unsigned long> fids;
208        fids.push_back(feature->getFID());
209        _renderer->selectFeatures(fids, layerName.c_str(), !_modifier);
210        _renderer->addPlacard(_pickPoint, feature, layerName.c_str());
211
212        char mesg[256];
213        if (_modifier) {
214            // Check if already selected
215            snprintf(mesg, sizeof(mesg), "nv>select feature add {%lu} {%s}\n", feature->getFID(), layerName.c_str());
216        } else {
217            snprintf(mesg, sizeof(mesg), "nv>select feature set {%lu} {%s}\n", feature->getFID(), layerName.c_str());
218        }
219        size_t length = strlen(mesg);
220        queueResponse(mesg, length, Response::VOLATILE, Response::DATA);
221    } else {
222        _renderer->clearSelection();
223        osgEarth::Annotation::AnnotationNode* anno =
224            osgEarth::Registry::objectIndex()->get<osgEarth::Annotation::AnnotationNode>(id);
225        if (anno != NULL) {
226            TRACE("Hit AnnotationNode: %p, \"%s\"", anno, anno->getName().c_str());
227            char mesg[256];
228            snprintf(mesg, sizeof(mesg), "nv>select annotation {%s}\n", anno->getName().c_str());
229            size_t length = strlen(mesg);
230            queueResponse(mesg, length, Response::VOLATILE, Response::DATA);
231        }
232    }
233    setHighlightByObjectID(id);
234    _renderer->pickPending(false);
235    _renderer->eventuallyRender();
236}
237
238void SelectCallback::onMiss()
239{
240    TRACE("Select miss");
241    _renderer->clearSelection();
242
243    static const char *mesg = "nv>select clear\n";
244    static const size_t length = strlen(mesg);
245    queueResponse(mesg, length, Response::STATIC, Response::DATA);
246
247    _renderer->pickPending(false);
248    _renderer->eventuallyRender();
249}
250
251bool SelectCallback::accept(const osgGA::GUIEventAdapter& ea, const osgGA::GUIActionAdapter& aa)
252{
253    if (ea.getEventType() == ea.PUSH) {
254        _x = ea.getX();
255        _y = ea.getY();
256        // Modifier-click?
257        _modifier = (ea.getModKeyMask() & osgGA::GUIEventAdapter::MODKEY_CTRL) != 0;
258        return false;
259    } else if (ea.getEventType() == ea.RELEASE) {
260        // Don't pick if mouse was dragged more than 2 pixels
261        if (fabsf(ea.getX()-_x) > 2 || fabsf(ea.getY()-_y) > 2) {
262            return false;
263        } else {
264            _renderer->mapMouseCoords(ea.getX(), ea.getY(), _pickPoint, false);
265            _renderer->pickPending(true);
266            TRACE("Pending pick at %g %g", ea.getX(), ea.getY());
267            return true;
268        }
269    }
270    return false;
271}
272
273void DeleteCallback::onHit(osgEarth::ObjectID id)
274{
275    // First see whether it's a feature:
276    osgEarth::Features::FeatureIndex *index = osgEarth::Registry::objectIndex()->get<osgEarth::Features::FeatureIndex>(id);
277    osgEarth::Features::Feature *feature = index ? index->getFeature(id) : 0L;
278
279    if (feature) {
280        TRACE("Hit feature ID: %lu, oid: %lu name: %s", feature->getFID(), id);
281        TRACE("Feature name: %s", feature->getString("name").c_str());
282    } else {
283        TRACE("Picker hit!");
284        osgEarth::Annotation::AnnotationNode* anno =
285            osgEarth::Registry::objectIndex()->get<osgEarth::Annotation::AnnotationNode>(id);
286        std::set<osgEarth::Annotation::AnnotationNode*>& hovered = _renderer->getHovered();
287#ifndef NEW_ANNOTATION_API
288        anno->clearDecoration();
289#endif
290        _renderer->getPlaceNodes()->removeChild(anno);
291        if (hovered.find(anno) != hovered.end()) {
292            hovered.erase(anno);
293        }
294    }
295}
Note: See TracBrowser for help on using the repository browser.