/* -*-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. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public License * along with this program. If not, see */ #pragma once #include #include #include #include #include #include #include #include #include #include #include #include #include namespace osgEarth { namespace Util { /** * A programmable manipulator suitable for use with geospatial terrains. * * You can use the "Settings" class to define custom input device bindings * and navigation parameters. Create one or more of these and call * applySettings() to "program" the manipulator at runtime. */ class OSGEARTH_EXPORT EarthManipulator : public osgGA::CameraManipulator { public: /** Bindable manipulator actions. */ enum ActionType { ACTION_NULL, ACTION_HOME, ACTION_GOTO, ACTION_PAN, ACTION_PAN_LEFT, ACTION_PAN_RIGHT, ACTION_PAN_UP, ACTION_PAN_DOWN, ACTION_ROTATE, ACTION_ROTATE_LEFT, ACTION_ROTATE_RIGHT, ACTION_ROTATE_UP, ACTION_ROTATE_DOWN, ACTION_ZOOM, ACTION_ZOOM_IN, ACTION_ZOOM_OUT, ACTION_EARTH_DRAG }; /** Vector of action types */ typedef std::vector ActionTypeVector; /** Bindable event types. */ enum EventType { EVENT_MOUSE_DOUBLE_CLICK = osgGA::GUIEventAdapter::DOUBLECLICK, EVENT_MOUSE_DRAG = osgGA::GUIEventAdapter::DRAG, EVENT_KEY_DOWN = osgGA::GUIEventAdapter::KEYDOWN, EVENT_SCROLL = osgGA::GUIEventAdapter::SCROLL, EVENT_MOUSE_CLICK = osgGA::GUIEventAdapter::USER << 1, EVENT_MULTI_DRAG = osgGA::GUIEventAdapter::USER << 2, // drag with 2 fingers EVENT_MULTI_PINCH = osgGA::GUIEventAdapter::USER << 3, // pinch with 2 fingers EVENT_MULTI_TWIST = osgGA::GUIEventAdapter::USER << 4, // drag 2 fingers in different directions EVENT_TOUCH_DRAG = osgGA::GUIEventAdapter::USER << 5 // drag 1 finger or pen with touch-screen }; /** Bindable mouse buttons. */ enum MouseEvent { MOUSE_LEFT_BUTTON = osgGA::GUIEventAdapter::LEFT_MOUSE_BUTTON, MOUSE_MIDDLE_BUTTON = osgGA::GUIEventAdapter::MIDDLE_MOUSE_BUTTON, MOUSE_RIGHT_BUTTON = osgGA::GUIEventAdapter::RIGHT_MOUSE_BUTTON }; /** Action options. Certain options are only meaningful to certain Actions. See the bind* documentation for information. */ enum ActionOptionType { OPTION_SCALE_X, // Sensitivity multiplier for horizontal input movements OPTION_SCALE_Y, // Sensitivity multiplier for vertical input movements OPTION_CONTINUOUS, // Whether to act as long as the button or key is depressed OPTION_SINGLE_AXIS, // If true, only operate on one axis at a time (the one with the larger value) OPTION_GOTO_RANGE_FACTOR, // for ACTION_GOTO, multiply the Range by this factor (to zoom in/out) OPTION_DURATION // Time it takes to complete the action (in seconds) }; /** Tethering options **/ enum TetherMode { TETHER_CENTER, // The camera will follow the center of the node. TETHER_CENTER_AND_ROTATION, // The camera will follow the node and all rotations made by the node TETHER_CENTER_AND_HEADING // The camera will follow the node and only follow heading rotation }; /** Camera projection matrix type **/ enum CameraProjection { PROJ_PERSPECTIVE, PROJ_ORTHOGRAPHIC }; struct OSGEARTH_EXPORT ActionOption { ActionOption() { } ActionOption( int option, bool value ) : _option(option), _bool_value(value) { } ActionOption( int option, int value ) : _option(option), _int_value(value) { } ActionOption( int option, double value ) : _option(option), _dbl_value(value) { } int option() const { return _option; } bool boolValue() const { return _bool_value; } int intValue() const { return _int_value; } double doubleValue() const { return _dbl_value; } private: int _option; union { bool _bool_value; int _int_value; double _dbl_value; }; }; struct OSGEARTH_EXPORT ActionOptions : public std::vector { void add( int option, bool value ) { push_back( ActionOption(option,value) ); } void add( int option, int value ) { push_back( ActionOption(option,value) ); } void add( int option, double value) { push_back( ActionOption(option,value) ); } }; protected: struct InputSpec { InputSpec( int event_type, int input_mask, int modkey_mask ) : _event_type(event_type), _input_mask(input_mask), _modkey_mask( modkey_mask ) { } InputSpec( const InputSpec& rhs ) : _event_type(rhs._event_type), _input_mask(rhs._input_mask), _modkey_mask(rhs._modkey_mask) { } bool operator == ( const InputSpec& rhs ) const { return _event_type == rhs._event_type && _input_mask == rhs._input_mask && ((_modkey_mask|osgGA::GUIEventAdapter::MODKEY_NUM_LOCK) == (rhs._modkey_mask|osgGA::GUIEventAdapter::MODKEY_NUM_LOCK)); } inline bool operator < (const InputSpec& rhs) const { if ( _event_type < rhs._event_type) return true; else if ( _event_type > rhs._event_type ) return false; else if ( _input_mask < rhs._input_mask ) return true; else if ( _input_mask > rhs._input_mask ) return false; else return ( _modkey_mask < rhs._modkey_mask ); } int _event_type; int _input_mask; int _modkey_mask; }; typedef std::list InputSpecs; enum Direction { DIR_NA, DIR_LEFT, DIR_RIGHT, DIR_UP, DIR_DOWN }; struct Action { Action( ActionType type =ACTION_NULL ); Action( ActionType type, const ActionOptions& options ); Action( const Action& rhs ); ActionType _type; Direction _dir; ActionOptions _options; bool getBoolOption( int option, bool defaultValue ) const; int getIntOption( int option, int defaultValue ) const; double getDoubleOption( int option, double defaultValue ) const; private: void init(); }; void dumpActionInfo( const Action& action, osg::NotifySeverity level ) const; static Action NullAction; public: class OSGEARTH_EXPORT Settings : public osg::Referenced, public Revisioned { public: // construct with default settings Settings(); // copy ctor Settings( const Settings& rhs ); /** dtor */ virtual ~Settings() { } // look for settings in an AP void apply(osg::ArgumentParser& args); /** * Assigns behavior to the action of dragging the mouse while depressing one or * more mouse buttons and modifier keys. * * @param action * The EarthManipulator::ActionType value to which to bind this mouse * input specification. * * @param button_mask * Mask of osgGA::GUIEventAdapter::MouseButtonMask values * * @param modkey_mask (default = 0L) * A mask of osgGA::GUIEventAdapter::ModKeyMask values defining a modifier key * combination to associate with the action. * * @param options * Action options. Valid options are: * OPTION_CONTINUOUS, OPTION_SCALE_X, OPTION_SCALE_Y */ void bindMouse( ActionType action, int button_mask, int modkey_mask = 0L, const ActionOptions& options =ActionOptions() ); /** * Assigns a bevahior to the action of clicking one or more mouse buttons. * * @param action * The EarthManipulator::ActionType value to which to bind this mouse click * input specification. * * @param button_mask * Mask of osgGA::GUIEventAdapter::MouseButtonMask values * * @param modkey_mask (default = 0L) * A mask of osgGA::GUIEventAdapter::ModKeyMask values defining a modifier key * combination to associate with the action. * * @param options * Action options. Valid options are: * OPTION_GOTO_RANGE_FACTOR, OPTION_DURATION */ void bindMouseClick( ActionType action, int button_mask, int modkey_mask =0L, const ActionOptions& options =ActionOptions() ); /** * Assigns a bevahior to the action of double-clicking one or more mouse buttons. * * @param action * The EarthManipulator::ActionType value to which to bind this double-click * input specification. * * @param button_mask * Mask of osgGA::GUIEventAdapter::MouseButtonMask values * * @param modkey_mask (default = 0L) * A mask of osgGA::GUIEventAdapter::ModKeyMask values defining a modifier key * combination to associate with the action. * * @param options * Action options. Valid options are: * OPTION_GOTO_RANGE_FACTOR, OPTION_DURATION */ void bindMouseDoubleClick( ActionType action, int button_mask, int modkey_mask =0L, const ActionOptions& options =ActionOptions() ); /** * Assigns a bevahior to the action of depressing a key. * * @param action * The EarthManipulator::ActionType value to which to bind this key * input specification. * * @param key * A osgGA::GUIEventAdapter::KeySymbol value * * @param modkey_mask (default = 0L) * A mask of osgGA::GUIEventAdapter::ModKeyMask values defining a modifier key * combination to associate with the action. * * @param options * Action options. Valid options are: * OPTION_CONTINUOUS */ void bindKey( ActionType action, int key, int modkey_mask =0L, const ActionOptions& options =ActionOptions() ); /** * Assigns a bevahior to operation of the mouse's scroll wheel. * * @param action * The EarthManipulator::ActionType value to which to bind this scroll * input specification. * * @param scrolling_motion * A osgGA::GUIEventAdapter::ScrollingMotion value * * @param modkey_mask (default = 0L) * A mask of osgGA::GUIEventAdapter::ModKeyMask values defining a modifier key * combination to associate with the action. * * @param options * Action options. Valid options are: * OPTION_SCALE_Y, OPTION_DURATION */ void bindScroll( ActionType action, int scrolling_motion, int modkey_mask =0L, const ActionOptions& options =ActionOptions() ); void bindPinch( ActionType action, const ActionOptions& =ActionOptions() ); void bindTwist( ActionType action, const ActionOptions& =ActionOptions() ); void bindMultiDrag( ActionType action, const ActionOptions& =ActionOptions() ); void bindTouchDrag( ActionType action, const ActionOptions& =ActionOptions() ); /** * Sets an overall mouse sensitivity factor. * * @param value * A scale factor to apply to mouse readings. * 1.0 = default; < 1.0 = less sensitive; > 1.0 = more sensitive. */ void setMouseSensitivity( double value ) { _mouse_sens = value; } double getMouseSensitivity() const { return _mouse_sens; } double& mouseSensitivity() { return _mouse_sens; } /** * Sets an overall touch sensitivity factor. * * @param value * A scale factor to apply to mouse readings. * 0.005 = default; < 0.005 = less sensitive; > 0.005 = more sensitive. */ void setTouchSensitivity( double value ) { _touch_sens = value; } double getTouchSensitivity() const { return _touch_sens; } double& touchSensitivity() { return _touch_sens; } /** * Sets the keyboard action sensitivity factor. This applies to navigation actions * that are bound to keyboard events. For example, you may bind the LEFT arrow to * the ACTION_PAN_LEFT action; this factor adjusts how much panning will occur during * each frame that the key is depressed. * * @param value * A scale factor to apply to keyboard-controller navigation. * 1.0 = default; < 1.0 = less sensitive; > 1.0 = more sensitive. */ void setKeyboardSensitivity( double value ) { _keyboard_sens = value; } double getKeyboardSensitivity() const { return _keyboard_sens; } double& keyboardSensitivity() { return _keyboard_sens; } /** * Sets the scroll-wheel sensitivity factor. This applies to navigation actions * that are bound to scrolling events. For example, you may bind the scroll wheel to * the ACTION_ZOOM_IN action; this factor adjusts how much zooming will occur each time * you click the scroll wheel. * * @param value * A scale factor to apply to scroll-wheel-controlled navigation. * 1.0 = default; < 1.0 = less sensitive; > 1.0 = more sensitive. */ void setScrollSensitivity( double value ) { _scroll_sens = value; } double getScrollSensitivity() const { return _scroll_sens; } double& scrollSensitivity() { return _scroll_sens; } /** * When set to true, prevents simultaneous control of pitch and azimuth. * * Usually you can alter pitch and azimuth at the same time. When this flag * is set, you can only control one at a time - if you start slewing the azimuth of the camera, * the pitch stays locked until you stop moving and then start slewing the pitch. * * Default = false. */ void setSingleAxisRotation( bool value ) { _single_axis_rotation = value; } bool getSingleAxisRotation() const { return _single_axis_rotation; } bool& singleAxisRotation() { return _single_axis_rotation; } /** * Sets whether to lock in a camera heading when performing panning operations (i.e., * changing the focal point). */ void setLockAzimuthWhilePanning( bool value ) { _lock_azim_while_panning = value; } bool getLockAzimuthWhilePanning() const { return _lock_azim_while_panning; } bool& lockAzimuthWhilePanning() { return _lock_azim_while_panning; } /** * Sets the minimum and maximum allowable local camera pitch, in degrees. * * By "local" we mean relative to the tangent plane passing through the focal point on * the surface of the terrain. * * Defaults are: Min = -90, Max = -10. */ void setMinMaxPitch( double min_pitch, double max_pitch ); /** Gets the minimum allowable local pitch, in degrees. */ double getMinPitch() const { return _min_pitch; } /** Gets the maximum allowable local pitch, in degrees. */ double getMaxPitch() const { return _max_pitch; } /** Gets the max x offset in world coordinates */ double getMaxXOffset() const { return _max_x_offset; } /** Gets the max y offset in world coordinates */ double getMaxYOffset() const { return _max_y_offset; } /** Gets the minimum distance from the focal point in world coordinates */ double getMinDistance() const {return _min_distance; } /** Gets the maximum distance from the focal point in world coordinates */ double getMaxDistance() const {return _max_distance; } /** Sets the min and max distance from the focal point in world coordinates */ void setMinMaxDistance( double min_distance, double max_distance); /** * Sets the maximum allowable offsets for the x and y camera offsets in world coordinates */ void setMaxOffset(double max_x_offset, double max_y_offset); /** Mode used for tethering to a node. */ void setTetherMode( TetherMode value ) { _tether_mode = value; } TetherMode getTetherMode() const { return _tether_mode; } TetherMode& tetherMode() { return _tether_mode; } /** Access to the list of Actions that will automatically break a tether */ ActionTypeVector& getBreakTetherActions() { return _breakTetherActions; } const ActionTypeVector& getBreakTetherActions() const { return _breakTetherActions; } /** Whether a setViewpoint transition whould "arc" */ void setArcViewpointTransitions( bool value ); bool getArcViewpointTransitions() const { return _arc_viewpoints; } /** Activates auto-duration for transitioned viewpoints. */ void setAutoViewpointDurationEnabled( bool value ); bool getAutoViewpointDurationEnabled() const { return _auto_vp_duration; } void setAutoViewpointDurationLimits( double minSeconds, double maxSeconds ); void getAutoViewpointDurationLimits( double& out_minSeconds, double& out_maxSeconds ) const { out_minSeconds = _min_vp_duration_s; out_maxSeconds = _max_vp_duration_s; } /** Whether to automatically adjust an orthographic camera so that it "tracks" the last known FOV and Aspect Ratio. */ bool getOrthoTracksPerspective() const { return _orthoTracksPerspective; } void setOrthoTracksPerspective(bool value) { _orthoTracksPerspective = value; } bool& orthoTracksPerspective() { return _orthoTracksPerspective; } /** Whether or not to keep the camera from going through the terrain surface */ bool getTerrainAvoidanceEnabled() const { return _terrainAvoidanceEnabled; } void setTerrainAvoidanceEnabled( bool value ) { _terrainAvoidanceEnabled = value; } bool& terrainAvoidanceEnabled() { return _terrainAvoidanceEnabled; } /** Minimum range for terrain avoidance checks in world coordinates */ double getTerrainAvoidanceMinimumDistance() const {return _terrainAvoidanceMinDistance; } void setTerrainAvoidanceMinimumDistance(double minDistance) { _terrainAvoidanceMinDistance = minDistance; } double& terrainAvoidanceMinimumDistance() { return _terrainAvoidanceMinDistance; } void setThrowingEnabled(bool throwingEnabled) { _throwingEnabled = throwingEnabled; } bool getThrowingEnabled () const { return _throwingEnabled; } bool& throwingEnabled() { return _throwingEnabled; } void setThrowDecayRate(double throwDecayRate) { _throwDecayRate = osg::clampBetween(throwDecayRate, 0.0, 1.0); } double getThrowDecayRate () const { return _throwDecayRate; } double& throwDecayRate() { return _throwDecayRate; } void setZoomToMouse(bool value) { _zoomToMouse = value; } bool getZoomToMouse() const { return _zoomToMouse; } bool& zoomToMouse() { return _zoomToMouse; } private: friend class EarthManipulator; typedef std::pair ActionBinding; typedef std::map ActionBindings; // Gets the action bound to the provided input specification, or NullAction if there is // to matching binding. const Action& getAction( int event_type, int input_mask, int modkey_mask ) const; void expandSpec( const InputSpec& input, InputSpecs& output ) const; void bind( const InputSpec& spec, const Action& action ); private: ActionBindings _bindings; bool _single_axis_rotation; bool _lock_azim_while_panning; double _mouse_sens; double _keyboard_sens; double _scroll_sens; double _touch_sens; double _min_pitch; double _max_pitch; double _max_x_offset; double _max_y_offset; double _min_distance; double _max_distance; TetherMode _tether_mode; ActionTypeVector _breakTetherActions; bool _arc_viewpoints; bool _auto_vp_duration; double _min_vp_duration_s, _max_vp_duration_s; bool _orthoTracksPerspective; bool _terrainAvoidanceEnabled; double _terrainAvoidanceMinDistance; bool _throwingEnabled; double _throwDecayRate; bool _zoomToMouse; }; public: EarthManipulator(); EarthManipulator(osg::ArgumentParser& args); EarthManipulator(const EarthManipulator& rhs); /** * Applies a new settings object to the manipulator, which takes effect immediately. */ void applySettings( Settings* settings ); /** * Gets a handle on the current manipulator settings object. */ Settings* getSettings() const; /** * Gets the current camera position. */ Viewpoint getViewpoint() const; /** * Sets the camera position, optionally moving it there over time. */ virtual void setViewpoint( const Viewpoint& vp, double duration_s =0.0 ); /** * Cancels a call to setViewpoint that resulted in an ongoing transition OR * attachment to a node. */ void clearViewpoint(); /** * Sets the viewpoint to activate when performing the ACTION_HOME action. */ void setHomeViewpoint( const Viewpoint& vp, double duration_s = 0.0 ); /** * Whether the manipulator is performing a viewpoint transition. */ bool isSettingViewpoint() const; /** * Whether the view is tethered to a node. */ bool isTethering() const; /** * Sets a callback to be invoked upon a tether or tether break */ class TetherCallback : public osg::Referenced { public: virtual void operator()(osg::Node* tetherNode) { } protected: virtual ~TetherCallback() { } }; void setTetherCallback(TetherCallback* cb) { _tetherCallback = cb; } TetherCallback* getTetherCallback() { return _tetherCallback.get(); } /** * Post-camera-update callback; use to access the camera position after * the call to updateCamera (for frame synchronization) */ class UpdateCameraCallback : public osg::Referenced { public: virtual void onUpdateCamera(const osg::Camera*) { } protected: virtual ~UpdateCameraCallback() { } }; void setUpdateCameraCallback(UpdateCameraCallback* cb) { _updateCameraCallback = cb; } UpdateCameraCallback* getUpdateCameraCallback() { return _updateCameraCallback.get(); } /** * Move the focal point of the camera using deltas (normalized screen coords). */ virtual void pan( double dx, double dy ); /** * Rotate the camera (dx = azimuth, dy = pitch) using deltas (radians). */ virtual void rotate( double dx, double dy ); /** * Zoom the camera using deltas (dy only) */ virtual void zoom( double dx, double dy, osg::View* view ); /** * Drag the earth using deltas */ virtual void drag( double dx, double dy, osg::View* view); /** * Converts screen coordinates (relative to the view's viewpoint) to world * coordinates. Note, this method will use the mask set by setTraversalMask(). * * @param x, y * Viewport coordinates * @param view * View for which to calculate world coordinates * @param out_coords * Output world coordinates (only valid if the method returns true) */ bool screenToWorld(float x, float y, osg::View* view, osg::Vec3d& out_coords ) const; /** * Gets the distance from the focal point in world coordiantes */ double getDistance() const { return _distance; } /** * Sets the distance from the focal point in world coordinates. * * The incoming distance value will be clamped within the valid range specified by the settings. */ void setDistance( double distance); /** * Gets the rotation of the manipulator. Note: This rotation is in addition to the rotation needed to center the view on the focal point. */ const osg::Quat& getRotation() { return _rotation; } /** * Sets the rotation of the manipulator. Note: This rotation is in addition to the rotation needed to center the view on the focal point. */ void setRotation( const osg::Quat& rotation) { _rotation = rotation; } /** * Gets the traversal node mask used to find root MapNode and CoordinateSystemNodes. Default is 0x1. */ osg::Node::NodeMask getFindNodeTraversalMask( ) { return _findNodeTraversalMask; } /** * Sets the traversal node mask used to find root MapNode and CoordinateSystemNode. Default is 0x1. * Use this method if you change MapNode or CoordinateSystemNode mask and want manipulator to work with them correctly. */ void setFindNodeTraversalMask( const osg::Node::NodeMask & nodeMask ) { _findNodeTraversalMask = nodeMask; } /** * Expressly set the initial vertical FOV. * If the manipulator detects that the camera has switched from persective * to orthographic projection, it will use the last know VFOV of the perspective * projection to match the zoom level in orthographic mode. However, if you start * in orthographic mode, it doesn't have this information; you can provide it * with this method. */ void setInitialVFOV(double vfov); /** * The last detected VFOV of a perspective camera (or the initial FOV if started in ortho) */ double getLastKnownVFOV() const { return _lastKnownVFOV; } /** * Assigns a NodeVisitor to use when OSG calls updateCamera() at the end of * the update traversal. This is useful if you have a Transform subclass that * overrides Transform::computeLocalToWorldMatrix and needs access to a * NodeVisitor. */ void setUpdateCameraNodeVisitor(osg::NodeVisitor* nv); public: // osgGA::CameraManipulator virtual const char* className() const { return "EarthManipulator"; } /** set the position of the matrix manipulator using a 4x4 Matrix.*/ virtual void setByMatrix(const osg::Matrixd& matrix); /** set the position of the matrix manipulator using a 4x4 Matrix.*/ virtual void setByInverseMatrix(const osg::Matrixd& matrix) { setByMatrix(osg::Matrixd::inverse(matrix)); } /** get the position of the manipulator as 4x4 Matrix.*/ virtual osg::Matrixd getMatrix() const; /** get the position of the manipulator as a inverse matrix of the manipulator, typically used as a model view matrix.*/ virtual osg::Matrixd getInverseMatrix() const; /** update the camera with the current values from this manipulator. Overloaded to support tethering, this method is called by Viewer or ComppositeViewer at the end of the update traversal. */ virtual void updateCamera(osg::Camera& camera); // Gets the stereo convergance mode. virtual osgUtil::SceneView::FusionDistanceMode getFusionDistanceMode() const { return osgUtil::SceneView::USE_FUSION_DISTANCE_VALUE; } // Gets the stereo convergance distance. virtual float getFusionDistanceValue() const { return _distance; } // Attach a node to the manipulator. virtual void setNode(osg::Node*); // Gets the node to which this manipulator is attached. virtual osg::Node* getNode(); // Move the camera to the default position. virtual void home(double /*unused*/); virtual void home(const osgGA::GUIEventAdapter& ea, osgGA::GUIActionAdapter& us); // Start/restart the manipulator. virtual void init(const osgGA::GUIEventAdapter& ea, osgGA::GUIActionAdapter& us); // handle events, return true if handled, false otherwise. virtual bool handle(const osgGA::GUIEventAdapter& ea, osgGA::GUIActionAdapter& us); // Get the keyboard and mouse usage of this manipulator. virtual void getUsage(osg::ApplicationUsage& usage) const; virtual void computeHomePosition(const osg::Camera *camera = NULL, bool useBoundingBox = false); // react to a tile-added event from the Terrain virtual void handleTileUpdate(const TileKey& key, osg::Node* tile, TerrainCallbackContext& context); // returns the absolute Euler angles composited from the composite rotation matrix. void getCompositeEulerAngles( double* out_azim, double* out_pitch =0L ) const; protected: virtual ~EarthManipulator(); bool intersect(const osg::Vec3d& start, const osg::Vec3d& end, osg::Vec3d& intersection, osg::Vec3d& normal) const; bool intersectLookVector(osg::Vec3d& eye, osg::Vec3d& out_target, osg::Vec3d& up) const; // resets the mouse event stack and pushes the provided event. void resetMouse( osgGA::GUIActionAdapter& aa, bool flushEventStack=true); // Reset the internal event stack. void flushMouseEventStack(); // Add the current mouse osgGA::GUIEvent to internal stack. void addMouseEvent(const osgGA::GUIEventAdapter& ea); // sets the camera position by doing a "look at" calculation, converting the center // point into a look vector and intersecting the terrain. void setByLookAt(const osg::Vec3d& eye, const osg::Vec3d& center, const osg::Vec3d& up); // sets the camera position by doing a "look at" calculation, but takes the target point // "as-is" and does not try to find an intersection. void setByLookAtRaw(const osg::Vec3d& eye, const osg::Vec3d& target, const osg::Vec3d& up); // checks to see whether the mouse is "moving". bool isMouseMoving(); // This sets the camera's roll based on your location on the globe. void recalculateRoll(); // Gets the matrix without a pre-MapNode transform (i.e., map world space) osg::Matrixd getWorldMatrix() const; // Gets the inverse matrix without a pre-MapNode transform (i.e., map world space) osg::Matrixd getWorldInverseMatrix() const; protected: enum TaskType { TASK_NONE, TASK_PAN, TASK_ROTATE, TASK_ZOOM }; struct Task : public osg::Referenced { Task() : _type(TASK_NONE) { } void set( TaskType type, double dx, double dy, double duration, double now ) { _type = type; _dx = dx; _dy = dy; _duration_s = duration; _time_last_service = now; } TaskType _type; double _dx, _dy; double _duration_s; double _time_last_service; }; // "ticks" the resident Task, which allows for multi-frame animation of navigation // movements. bool serviceTask(osg::View*); // returns the Euler Angles baked into _rotation, the local frame's rotation quaternion. void getEulerAngles(const osg::Quat& quat, double* azim, double* pitch) const; // Makes a quaternion from an azimuth and pitch. osg::Quat getQuaternion(double azim, double pitch) const; /** * Fire a ray from the current eyepoint along the current look vector, * intersect the terrain at the closest point, and reset the matrix parameters * based on that point. */ bool recalculateCenterFromLookVector(); void recalculateCenter(const osg::CoordinateFrame& frame); osg::Matrixd getRotation(const osg::Vec3d& center) const; osg::Quat computeCenterRotation(const osg::Vec3d& center) const; void updateTether(double t); void updateSetViewpoint(); bool isMouseClick( const osgGA::GUIEventAdapter* mouse_up_event ) const; void applyOptionsToDeltas( const Action& action, double& dx, double& dy ); void configureDefaultSettings(); void reinitialize(); bool established(); // sets the new center (focal) point and recalculates it's L2W matrix. void setCenter( const osg::Vec3d& center ); // creates a "local-to-world" transform relative to the input point. bool createLocalCoordFrame( const osg::Vec3d& worldPos, osg::CoordinateFrame& out_frame ) const; // returns an ActionType that would be initiated by the OSG UI event ActionType getActionTypeForEvent( const osgGA::GUIEventAdapter& ea ) const; public: void recalculateCenter() { recalculateCenter(_centerLocalToWorld); } const GeoPoint& centerMap() const { return _centerMap; } protected: typedef osgGA::GUIEventAdapter::TouchData::TouchPoint TouchPoint; typedef std::vector MultiTouchPoint; // one per ID (finger/touchpoint) typedef std::deque MultiTouchPointQueue; MultiTouchPointQueue _touchPointQueue; struct TouchEvent { TouchEvent() : _mbmask(0) { } EventType _eventType; unsigned _mbmask; float _dx, _dy; }; typedef std::vector TouchEvents; void addTouchEvents( const osgGA::GUIEventAdapter& ea ); bool parseTouchEvents( TouchEvents& ev ); // Applies an action using the raw input parameters. bool handleAction( const Action& action, double dx, double dy, double duration ); virtual bool handleMouseAction( const Action& action, osg::View* view ); virtual bool handleMouseClickAction( const Action& action ); virtual bool handleKeyboardAction( const Action& action, double duration_s = DBL_MAX ); virtual bool handleScrollAction( const Action& action, double duration_s = DBL_MAX ); virtual bool handlePointAction( const Action& type, float mx, float my, osg::View* view ); virtual void handleContinuousAction( const Action& action, osg::View* view ); virtual void handleMovementAction( const ActionType& type, double dx, double dy, osg::View* view ); protected: // makeshift "stack" of the last 2 incoming events. osg::ref_ptr _ga_t1; osg::ref_ptr _ga_t0; osg::ref_ptr _mouse_down_event; bool _pushed; osg::observer_ptr _node; osg::observer_ptr _mapNode; osg::ref_ptr _srs; double _time_s_now; bool _thrown; double _throw_dx; double _throw_dy; double _dx; double _dy; // The world coordinate of the Viewpoint focal point. osg::Vec3d _center; GeoPoint _centerMap; double _centerHeight; // local2world matrix for the center point. osg::CoordinateFrame _centerLocalToWorld; // Rotation offset to _rotation when tethering. osg::Quat _tetherRotation; // The initial offset applied to the tether rotation when orientation-tethering begins. // This is usually just the inverse of the first-calculated _tetherRotation. optional _tetherRotationOffset; // The rotation (heading and pitch) of the camera in the // earth-local frame defined by _centerRotation. osg::Quat _rotation; // The rotation that makes the camera look down on the focal // point on the earth. This is equivalent to a rotation by // latitude, longitude. osg::Quat _centerRotation; // distance from camera to center of rotation. double _distance; // XYZ offsets of the focal point in the local tangent plane coordinate system // of the focal point. osg::Vec3d _posOffset; // XY offsets (left/right, down/up) of the focal point in the plane normal to // the view heading. osg::Vec2d _viewOffset; osg::Vec3d _previousUp; osg::ref_ptr _task; osg::Timer_t _time_last_frame; bool _continuous; double _continuous_dx; double _continuous_dy; double _last_continuous_action_time; double _single_axis_x; double _single_axis_y; // the "pending" viewpoint is only used to enable setting the // viewpoint before the frame loop starts optional _pendingViewpoint; Duration _pendingViewpointDuration; optional _setVP0; // Starting viewpoint optional _setVP1; // Final viewpoint optional _setVPStartTime; // Time of call to setViewpoint Duration _setVPDuration; // Transition time for setViewpoint double _setVPAccel, _setVPAccel2; // Acceleration factors for setViewpoint double _setVPArcHeight; // Altitude arcing height for setViewpoint osg::Quat _tetherRotationVP0; // saves _tetherRotation at the start of a transition osg::Quat _tetherRotationVP1; // target _tetherRotation if not tethered TetherMode _lastTetherMode; osg::Matrix _mapNodeFrame, _mapNodeFrameInverse; // returns "t", the parametric coefficient of a timed transition. 1=finished. double setViewpointFrame(double time_s); void setLookAt(const osg::Vec3d& center, double azim, double pitch, double range, const osg::Vec3d& posoffset); void resetLookAt(); void collapseTetherRotationIntoRotation(); unsigned _frameCount; osg::ref_ptr _settings; osgEarth::optional _homeViewpoint; double _homeViewpointDuration; Action _last_action; EventType _last_event; double _time_s_last_event; double _lastKnownVFOV; /** updates a camera to switch between prospective and ortho. */ void updateProjection( osg::Camera* eventCamera ); // Support snappy transition when the pointer leaves and // returns to earth during a drag osg::Vec3d _lastPointOnEarth; osg::ref_ptr< TerrainCallback > _terrainCallback; // Traversal mask used in established and dtor methods to find MapNode and CoordinateSystemNode osg::Node::NodeMask _findNodeTraversalMask; osg::ref_ptr _tetherCallback; osg::ref_ptr _updateCameraCallback; bool _userWillCallUpdateCamera; osg::observer_ptr _updateCameraNodeVisitor; void collisionDetect(); void ctor_init(); }; } }