315 lines
9.9 KiB
Plaintext
315 lines
9.9 KiB
Plaintext
|
/* -*-c++-*- */
|
||
|
/* osgEarth - Geospatial SDK for OpenSceneGraph
|
||
|
* Copyright 2020 Pelican Mapping
|
||
|
* http://osgearth.org
|
||
|
*
|
||
|
* osgEarth is free software; you can redistribute it and/or modify
|
||
|
* it under the terms of the GNU Lesser General Public License as published by
|
||
|
* the Free Software Foundation; either version 2 of the License, or
|
||
|
* (at your option) any later version.
|
||
|
*
|
||
|
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||
|
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||
|
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||
|
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||
|
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
|
||
|
* FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
|
||
|
* IN THE SOFTWARE.
|
||
|
*
|
||
|
* You should have received a copy of the GNU Lesser General Public License
|
||
|
* along with this program. If not, see <http://www.gnu.org/licenses/>
|
||
|
*/
|
||
|
#pragma once
|
||
|
|
||
|
#include <osgEarth/Common>
|
||
|
#include <osgEarth/EarthManipulator>
|
||
|
#include <osgEarth/Sky>
|
||
|
#include <osgEarth/SimpleOceanLayer>
|
||
|
#include <osgEarth/Viewpoint>
|
||
|
#include <osgGA/GUIEventHandler>
|
||
|
#include <osgViewer/View>
|
||
|
|
||
|
namespace osgEarth {
|
||
|
class MapNode;
|
||
|
}
|
||
|
namespace osg {
|
||
|
class ArgumentParser;
|
||
|
}
|
||
|
namespace osgViewer {
|
||
|
class ViewerBase;
|
||
|
};
|
||
|
|
||
|
struct EnumClassHash
|
||
|
{
|
||
|
template <typename T>
|
||
|
std::size_t operator()(T t) const noexcept
|
||
|
{
|
||
|
return static_cast<std::size_t>(t);
|
||
|
}
|
||
|
};
|
||
|
|
||
|
/**
|
||
|
* This is a collection of resources used by the osgEarth example applications.
|
||
|
*/
|
||
|
namespace osgEarth { namespace Util
|
||
|
{
|
||
|
using namespace osgEarth;
|
||
|
|
||
|
/**
|
||
|
* Parses a set of built-in example arguments. Any Controls created by parsing
|
||
|
* command-line parameters will appear in the lower-left corner of the display.
|
||
|
*/
|
||
|
class OSGEARTH_EXPORT MapNodeHelper
|
||
|
{
|
||
|
public:
|
||
|
/**
|
||
|
* Loads a map file and processes all the built-in example command line
|
||
|
* arguemnts and XML externals. Disables the Controls library.
|
||
|
*/
|
||
|
osg::ref_ptr<osg::Node> load(
|
||
|
osg::ArgumentParser& args,
|
||
|
osgViewer::ViewerBase* viewer) const;
|
||
|
|
||
|
//! Takes an existing map node and processes all the built-in example
|
||
|
//! command line arguments.
|
||
|
void parse(
|
||
|
MapNode* mapNode,
|
||
|
osg::ArgumentParser& args,
|
||
|
osgViewer::View* view,
|
||
|
osg::Group* parentGroup) const;
|
||
|
|
||
|
//! Configures a view with a stock set of event handlers that are useful
|
||
|
//! for demos, and performs some other common view configuration for osgEarth.
|
||
|
void configureView(osgViewer::View* view) const;
|
||
|
|
||
|
//! Enumerates the command line arguments that this helper will process
|
||
|
std::string usage() const;
|
||
|
};
|
||
|
|
||
|
|
||
|
#if 0
|
||
|
/**
|
||
|
* Creates a UI Control with a list of clickable viewpoints.
|
||
|
*/
|
||
|
class OSGEARTH_EXPORT ViewpointControlFactory
|
||
|
{
|
||
|
public:
|
||
|
Control* create(
|
||
|
const std::vector<Viewpoint>& list,
|
||
|
osgViewer::View* view) const;
|
||
|
};
|
||
|
|
||
|
/**
|
||
|
* Creates UI controls that show the map coordinates under the mouse
|
||
|
*/
|
||
|
class OSGEARTH_EXPORT MouseCoordsControlFactory
|
||
|
{
|
||
|
public:
|
||
|
Control* create(
|
||
|
MapNode* mapNode,
|
||
|
osgViewer::View* view ) const;
|
||
|
};
|
||
|
|
||
|
|
||
|
/**
|
||
|
* Creates a UI Control reflecting all the named Annotations found in a
|
||
|
* scene graph.
|
||
|
*/
|
||
|
class OSGEARTH_EXPORT AnnotationGraphControlFactory
|
||
|
{
|
||
|
public:
|
||
|
Control* create(
|
||
|
osg::Node* node,
|
||
|
osgViewer::View* view) const;
|
||
|
};
|
||
|
|
||
|
|
||
|
/**
|
||
|
* Creates a set of controls for manipulating the Sky model.
|
||
|
*/
|
||
|
class OSGEARTH_EXPORT SkyControlFactory
|
||
|
{
|
||
|
public:
|
||
|
static Control* create(SkyNode* sky);
|
||
|
};
|
||
|
|
||
|
|
||
|
/**
|
||
|
* Creates a set of controls for manipulating the Ocean surface model.
|
||
|
*/
|
||
|
class OSGEARTH_EXPORT OceanControlFactory
|
||
|
{
|
||
|
public:
|
||
|
static Control* create(SimpleOceanLayer* ocean);
|
||
|
};
|
||
|
|
||
|
|
||
|
/**
|
||
|
* Creates UI controls displaying the map attribution
|
||
|
*/
|
||
|
class OSGEARTH_EXPORT AttributionControlFactory
|
||
|
{
|
||
|
public:
|
||
|
Control* create(MapNode* mapNode) const;
|
||
|
};
|
||
|
#endif
|
||
|
|
||
|
/**
|
||
|
* Helper class for easily creating event handlers
|
||
|
*/
|
||
|
struct EventRouter : public osgGA::GUIEventHandler, public osgGA::GUIEventAdapter
|
||
|
{
|
||
|
using Function = std::function<void()>;
|
||
|
using ViewFunction = std::function<void(osg::View*, float, float)>;
|
||
|
|
||
|
template<typename FUNC_TYPE>
|
||
|
struct Action {
|
||
|
FUNC_TYPE func;
|
||
|
bool eat = true;
|
||
|
Action(const FUNC_TYPE& f, bool e) : func(f), eat(e) { }
|
||
|
};
|
||
|
|
||
|
struct Push {
|
||
|
Push() : _value(false) { }
|
||
|
bool _value;
|
||
|
int _modkeymask;
|
||
|
void set(bool value, int mask) { _value = value, _modkeymask = mask; }
|
||
|
operator bool() { return _value; }
|
||
|
};
|
||
|
|
||
|
//std::unordered_map<KeySymbol, std::list<Action<Function>>, EnumClassHash> _keypress_simple;
|
||
|
std::unordered_map<KeySymbol, std::list<Action<ViewFunction>>, EnumClassHash> _keypress;
|
||
|
std::unordered_map<int, Push> _pushes;
|
||
|
std::unordered_map<int, std::list<Action<ViewFunction>>> _click;
|
||
|
std::list<Action<ViewFunction>> _move;
|
||
|
std::list<Action<ViewFunction>> _drag;
|
||
|
|
||
|
//EventRouter& onKeyPress(KeySymbol key, const Function& func, bool eat=true) {
|
||
|
// _keypress_simple[key].emplace_back(Action<Function>(func, eat));
|
||
|
// return *this;
|
||
|
//}
|
||
|
|
||
|
EventRouter& onKeyPress(KeySymbol key, const ViewFunction& func, bool eat=true) {
|
||
|
_keypress[key].emplace_back(Action<ViewFunction>(func, eat));
|
||
|
return *this;
|
||
|
}
|
||
|
|
||
|
EventRouter& popKeyPress(KeySymbol key) {
|
||
|
if (!_keypress[key].empty())
|
||
|
_keypress[key].pop_back();
|
||
|
return *this;
|
||
|
}
|
||
|
|
||
|
EventRouter& onClick(const ViewFunction& func, bool eat=true) {
|
||
|
onClick(LEFT_MOUSE_BUTTON, 0, func, eat);
|
||
|
return *this;
|
||
|
}
|
||
|
|
||
|
EventRouter& onClick(int button, const ViewFunction& func, bool eat=true) {
|
||
|
_click[button].emplace_back(Action<ViewFunction>(func, eat));
|
||
|
return *this;
|
||
|
}
|
||
|
|
||
|
EventRouter& onClick(int button, int modkey, const ViewFunction& func, bool eat=true) {
|
||
|
_click[button | (modkey<<3)].emplace_back(Action<ViewFunction>(func, eat));
|
||
|
return *this;
|
||
|
}
|
||
|
|
||
|
EventRouter& popClick(int button = LEFT_MOUSE_BUTTON, int modkey = 0) {
|
||
|
if (!_click[button | (modkey << 3)].empty())
|
||
|
_click[button | (modkey << 3)].pop_back();
|
||
|
_pushes.clear();
|
||
|
return *this;
|
||
|
}
|
||
|
|
||
|
EventRouter& onMove(const ViewFunction& func, bool eat=false) {
|
||
|
_move.emplace_back(Action<ViewFunction>(func, eat));
|
||
|
return *this;
|
||
|
}
|
||
|
|
||
|
EventRouter& popMove() {
|
||
|
if (!_move.empty())
|
||
|
_move.pop_back();
|
||
|
return *this;
|
||
|
}
|
||
|
|
||
|
EventRouter& onDrag(const ViewFunction& func, bool eat=false) {
|
||
|
_drag.emplace_back(Action<ViewFunction>(func, eat));
|
||
|
return *this;
|
||
|
}
|
||
|
|
||
|
EventRouter& popDrag() {
|
||
|
if (!_drag.empty())
|
||
|
_drag.pop_back();
|
||
|
return *this;
|
||
|
}
|
||
|
|
||
|
bool handle(const osgGA::GUIEventAdapter& ea, osgGA::GUIActionAdapter& aa) override
|
||
|
{
|
||
|
unsigned count = 0u;
|
||
|
|
||
|
if (ea.getEventType() == ea.KEYUP) {
|
||
|
//for (auto& action : _keypress_simple[(KeySymbol)ea.getKey()]) {
|
||
|
// action.func();
|
||
|
// if (action.eat) count++;
|
||
|
//}
|
||
|
for (auto& action : _keypress[(KeySymbol)ea.getKey()]) {
|
||
|
action.func(aa.asView(), ea.getX(), ea.getY());
|
||
|
if (action.eat) count++;
|
||
|
}
|
||
|
}
|
||
|
else if (ea.getEventType() == ea.PUSH) {
|
||
|
_pushes[ea.getButton()].set(true, ea.getModKeyMask());
|
||
|
int combo = ea.getButton() | ea.getModKeyMask();
|
||
|
for (auto& action : _click[combo]) {
|
||
|
if (action.eat) count++;
|
||
|
}
|
||
|
}
|
||
|
else if (ea.getEventType() == ea.RELEASE) {
|
||
|
Push& push = _pushes[ea.getButton()];
|
||
|
if (push == true) {
|
||
|
int combo = ea.getButton() | push._modkeymask;
|
||
|
push.set(false, 0);
|
||
|
for (auto& action : _click[combo]) {
|
||
|
action.func(aa.asView(), ea.getX(), ea.getY());
|
||
|
if (action.eat) count++;
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
else if (ea.getEventType() == ea.MOVE) {
|
||
|
for (auto& action : _move) {
|
||
|
action.func(aa.asView(), ea.getX(), ea.getY());
|
||
|
if (action.eat) count++;
|
||
|
}
|
||
|
}
|
||
|
else if (ea.getEventType() == ea.DRAG) {
|
||
|
for (auto& action : _drag) {
|
||
|
action.func(aa.asView(), ea.getX(), ea.getY());
|
||
|
if (action.eat) count++;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
return count > 0u;
|
||
|
}
|
||
|
|
||
|
//! Accesses the router on the given view, creating one if necessary
|
||
|
static EventRouter& get(osgViewer::View* v) {
|
||
|
OE_HARD_ASSERT(v != nullptr);
|
||
|
for (auto& eh : v->getEventHandlers()) {
|
||
|
EventRouter* r = dynamic_cast<EventRouter*>(eh.get());
|
||
|
if (r)
|
||
|
return *r;
|
||
|
}
|
||
|
EventRouter* r = new EventRouter();
|
||
|
v->getEventHandlers().push_front(r);
|
||
|
return *r;
|
||
|
}
|
||
|
|
||
|
static EventRouter& get(osg::ref_ptr<osgViewer::View>& v) {
|
||
|
return get(v.get());
|
||
|
}
|
||
|
};
|
||
|
|
||
|
|
||
|
} } // namespace osgEarth::Util
|