Interaction Updates (#353)

* Interaction speed is not dependent on framerate

* Split up interaction code in files and perform smooth interpolation when changing focus

* Abstract interaction code in to functions.

* Interpolation time is dependent on angle to focus node.

* Use correct delta time when interpolating

* Fix bug regarding decomposition of camera rotation.

* Make orbital interaction mode behave as globe browsing and no longer use interactiondepth below ellipsoid.

* Do not always rotate with object. Depending on distance

* Remove interaction depth below ellipsoid. Now able to interact without renderable

* Remove specification of interactionDepthBelowEllipsoid and cameraMinHeight

* Remove GlobeBrowsingInteractionMode

* Rename OrbitalInteractionMode to OrbitalNavigator and no longer extend interactionmode.

* Move properties from interaction handler to orbital navigator

* Use smooth step for follow rotation interpolator

* Rename KeyframeInteractionMode to KeyframeNavigator

* Rename files

* Clean up.

* Separate mousestate from orbitalnavigator

* Clean up

* Split keybindingmanager from interactionhandler interactionhandler

* Rename interactionhandler to navigationhandler

* Rename files

* Clean up

* Take back usage of gotochunk and gotogeo

* Rename lua library navigation

* Move functionality from navigationhandler to keyframenavigator

* Update scripts for navigation

* Comment code

* Clean up

* Solve but that caused NaN values for camera position when being in center of globe and setting focus to the globe.

* Update jenkins file to remove build folder before building.

* Fix error in jenkins script

* Update jenkins file

* Update jenkins file

* Revert jenkins file

* I hope this makes Jenkins happy.

* Line endings God damnit

* Line endings

* Clean up

* Fix compilation issue

* Take back default scene.

* Fix indentation

* Move functions goToGeo and goToChunk to GlobeBrowsingModule.

* Include algorithm for std::find

* Remove auto and other clean up
This commit is contained in:
Kalle Bladin
2017-07-14 17:17:17 +02:00
committed by GitHub
parent 4ecb98f06f
commit 2e4f31ded8
68 changed files with 2764 additions and 1977 deletions

View File

@@ -78,7 +78,7 @@ function postInitialization()
openspace.setPropertyValue("Earth.RenderableGlobe.Debug.levelByProjectedAreaElseDistance", false)
openspace.setPropertyValue("Earth.RenderableGlobe.Layers.ColorLayers.blendTileLevels", true)
openspace.resetCameraDirection()
openspace.globebrowsing.goToGeo(0, 0, 20000000)
openspace.printInfo("Done setting default values")
end
@@ -89,7 +89,7 @@ return {
CommonFolder = "common",
Camera = {
Focus = "Earth",
Position = {30000000, 0, 0},
Position = {0, 0, 0},
Rotation = {0.758797, 0.221490, -0.605693, -0.091135},
},

View File

@@ -63,8 +63,6 @@ return {
Renderable = {
Type = "RenderableGlobe",
Radii = earthEllipsoid,
CameraMinHeight = 300,
InteractionDepthBelowEllipsoid = 0, -- Useful when having negative height map values
SegmentsPerPatch = 64,
Layers = {
ColorLayers = {

View File

@@ -23,8 +23,6 @@ return {
Renderable = {
Type = "RenderableGlobe",
Radii = {2631000, 2631000, 2631000},
CameraMinHeight = 300,
InteractionDepthBelowEllipsoid = 0, -- Useful when having negative height map values
SegmentsPerPatch = 64,
Layers = {
ColorLayers = {

View File

@@ -23,8 +23,6 @@ return {
Renderable = {
Type = "RenderableGlobe",
Radii = {1561000, 1561000, 1561000},
CameraMinHeight = 300,
InteractionDepthBelowEllipsoid = 0, -- Useful when having negative height map values
SegmentsPerPatch = 64,
Layers = {
ColorLayers = {

View File

@@ -23,8 +23,6 @@ return {
Renderable = {
Type = "RenderableGlobe",
Radii = {2631000, 2631000, 2631000},
CameraMinHeight = 300,
InteractionDepthBelowEllipsoid = 0, -- Useful when having negative height map values
SegmentsPerPatch = 64,
Layers = {
ColorLayers = {

View File

@@ -23,8 +23,6 @@ return {
Renderable = {
Type = "RenderableGlobe",
Radii = {1821300, 1821300, 1821300},
CameraMinHeight = 300,
InteractionDepthBelowEllipsoid = 0, -- Useful when having negative height map values
SegmentsPerPatch = 64,
Layers = {
ColorLayers = {

View File

@@ -34,8 +34,6 @@ return {
Renderable = {
Type = "RenderableGlobe",
Radii = {71492000, 71492000, 66854000},
CameraMinHeight = 300,
InteractionDepthBelowEllipsoid = 0, -- Useful when having negative height map values
SegmentsPerPatch = 64,
Layers = {
ColorLayers = {

View File

@@ -32,11 +32,7 @@ return {
Renderable = {
Type = "RenderableGlobe",
Radii = marsEllipsoid,
CameraMinHeight = 10,
SegmentsPerPatch = 90,
-- Allows camera to go down 10000 meters below the reference ellipsoid
-- Useful when having negative height map values
InteractionDepthBelowEllipsoid = 10000,
Layers = {
ColorLayers = {
{

View File

@@ -19,10 +19,7 @@ return {
Renderable = {
Type = "RenderableGlobe",
Radii = {1738140, 1738140, 1735970}, -- Moons's radius
CameraMinHeight = 300,
SegmentsPerPatch = 64,
-- Allows camera to go down 10000 meters below the reference ellipsoid
InteractionDepthBelowEllipsoid = 10000, -- Useful when having negative height map values
Layers = {
ColorLayers = {
{

View File

@@ -31,8 +31,6 @@ return {
Renderable = {
Type = "RenderableGlobe",
Radii = {24764000, 24764000, 24314000},
CameraMinHeight = 300,
InteractionDepthBelowEllipsoid = 0, -- Useful when having negative height map values
SegmentsPerPatch = 64,
Layers = {
ColorLayers = {

View File

@@ -31,8 +31,6 @@ return {
Renderable = {
Type = "RenderableGlobe",
Radii = {60268000, 60268000, 54364000},
CameraMinHeight = 300,
InteractionDepthBelowEllipsoid = 0, -- Useful when having negative height map values
SegmentsPerPatch = 64,
Layers = {
ColorLayers = {

View File

@@ -31,8 +31,6 @@ return {
Renderable = {
Type = "RenderableGlobe",
Radii = {25559000, 25559000, 24973000},
CameraMinHeight = 300,
InteractionDepthBelowEllipsoid = 0, -- Useful when having negative height map values
SegmentsPerPatch = 64,
Layers = {
ColorLayers = {

View File

@@ -36,8 +36,6 @@ return {
Renderable = {
Type = "RenderableGlobe",
Radii = {6051900, 6051900, 6051800},
CameraMinHeight = 300,
InteractionDepthBelowEllipsoid = 0, -- Useful when having negative height map values
SegmentsPerPatch = 64,
Layers = {
ColorLayers = {

View File

@@ -62,7 +62,10 @@ class SyncEngine;
class TimeManager;
class WindowWrapper;
namespace interaction { class InteractionHandler; }
namespace interaction {
class NavigationHandler;
class KeyBindingManager;
}
namespace gui { class GUI; }
namespace properties { class PropertyOwner; }
namespace scripting {
@@ -123,7 +126,8 @@ public:
TimeManager& timeManager();
WindowWrapper& windowWrapper();
ghoul::fontrendering::FontManager& fontManager();
interaction::InteractionHandler& interactionHandler();
interaction::NavigationHandler& navigationHandler();
interaction::KeyBindingManager& keyBindingManager();
properties::PropertyOwner& globalPropertyOwner();
scripting::ScriptEngine& scriptEngine();
scripting::ScriptScheduler& scriptScheduler();
@@ -197,7 +201,8 @@ private:
std::unique_ptr<WindowWrapper> _windowWrapper;
std::unique_ptr<ghoul::cmdparser::CommandlineParser> _commandlineParser;
std::unique_ptr<ghoul::fontrendering::FontManager> _fontManager;
std::unique_ptr<interaction::InteractionHandler> _interactionHandler;
std::unique_ptr<interaction::NavigationHandler> _navigationHandler;
std::unique_ptr<interaction::KeyBindingManager> _keyBindingManager;
std::unique_ptr<scripting::ScriptEngine> _scriptEngine;
std::unique_ptr<scripting::ScriptScheduler> _scriptScheduler;
std::unique_ptr<VirtualPropertyManager> _virtualPropertyManager;

View File

@@ -33,7 +33,7 @@
namespace openspace {
namespace interaction {
class InteractionHandler;
class NavigationHandler;
class Controller {
public:
@@ -41,10 +41,10 @@ public:
_handler(nullptr)
{}
void setHandler(InteractionHandler* handler);
void setHandler(NavigationHandler* handler);
protected:
InteractionHandler* _handler;
NavigationHandler* _handler;
};
} // namespace interaction

View File

@@ -0,0 +1,59 @@
/*****************************************************************************************
* *
* OpenSpace *
* *
* Copyright (c) 2014-2017 *
* *
* Permission is hereby granted, free of charge, to any person obtaining a copy of this *
* software and associated documentation files (the "Software"), to deal in the Software *
* without restriction, including without limitation the rights to use, copy, modify, *
* merge, publish, distribute, sublicense, and/or sell copies of the Software, and to *
* permit persons to whom the Software is furnished to do so, subject to the following *
* conditions: *
* *
* The above copyright notice and this permission notice shall be included in all copies *
* or substantial portions of the Software. *
* *
* 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. *
****************************************************************************************/
#ifndef __OPENSPACE_CORE___DELAYEDVARIABLE___H__
#define __OPENSPACE_CORE___DELAYEDVARIABLE___H__
namespace openspace {
namespace interaction {
/**
* Class that acts as a smoothing filter to a variable. The filter has a step
* response on a form that resembles the function y = 1-e^(-t/scale). The variable
* will be updated as soon as it is set to a value (calling the set() function).
*/
template <typename T, typename ScaleType>
class DelayedVariable {
public:
DelayedVariable(ScaleType scaleFactor, ScaleType friction);
void set(T value, double dt);
void decelerate(double dt);
void setHard(T value);
void setFriction(ScaleType friction);
void setScaleFactor(ScaleType scaleFactor);
T get() const;
private:
ScaleType _scaleFactor;
ScaleType _friction;
T _targetValue;
T _currentValue;
};
} // namespace interaction
} // namespace openspace
#include "delayedvariable.inl"
#endif // __OPENSPACE_CORE___DELAYEDVARIABLE___H__

View File

@@ -0,0 +1,76 @@
/*****************************************************************************************
* *
* OpenSpace *
* *
* Copyright (c) 2014-2017 *
* *
* Permission is hereby granted, free of charge, to any person obtaining a copy of this *
* software and associated documentation files (the "Software"), to deal in the Software *
* without restriction, including without limitation the rights to use, copy, modify, *
* merge, publish, distribute, sublicense, and/or sell copies of the Software, and to *
* permit persons to whom the Software is furnished to do so, subject to the following *
* conditions: *
* *
* The above copyright notice and this permission notice shall be included in all copies *
* or substantial portions of the Software. *
* *
* 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. *
****************************************************************************************/
#include <ghoul/misc/assert.h>
#include <ghoul/glm.h>
namespace openspace {
namespace interaction {
template <typename T, typename ScaleType>
DelayedVariable<T, ScaleType>::DelayedVariable(ScaleType scaleFactor, ScaleType friction)
: _scaleFactor(std::move(scaleFactor))
, _friction(friction)
{
ghoul_assert(_friction >= ScaleType(0.0), "Friction must be positive");
}
template <typename T, typename ScaleType>
void DelayedVariable<T, ScaleType>::set(T value, double dt) {
_targetValue = value;
_currentValue = _currentValue + (_targetValue - _currentValue) *
glm::min(_scaleFactor * dt, 1.0); // less or equal to 1.0 keeps it stable
}
template <typename T, typename ScaleType>
void DelayedVariable<T, ScaleType>::decelerate(double dt) {
_currentValue = _currentValue + (- _currentValue) *
glm::min(_scaleFactor * _friction * dt, 1.0);
// less or equal to 1.0 keeps it stable
}
template <typename T, typename ScaleType>
void DelayedVariable<T, ScaleType>::setHard(T value) {
_targetValue = value;
_currentValue = value;
}
template <typename T, typename ScaleType>
void DelayedVariable<T, ScaleType>::setFriction(ScaleType friction) {
_friction = friction;
ghoul_assert(_friction >= ScaleType(0.0), "Friction must be positive");
}
template <typename T, typename ScaleType>
void DelayedVariable<T, ScaleType>::setScaleFactor(ScaleType scaleFactor) {
_scaleFactor = scaleFactor;
}
template <typename T, typename ScaleType>
T DelayedVariable<T, ScaleType>::get() const {
return _currentValue;
}
} // namespace interaction
} // namespace openspace

View File

@@ -0,0 +1,70 @@
/*****************************************************************************************
* *
* OpenSpace *
* *
* Copyright (c) 2014-2017 *
* *
* Permission is hereby granted, free of charge, to any person obtaining a copy of this *
* software and associated documentation files (the "Software"), to deal in the Software *
* without restriction, including without limitation the rights to use, copy, modify, *
* merge, publish, distribute, sublicense, and/or sell copies of the Software, and to *
* permit persons to whom the Software is furnished to do so, subject to the following *
* conditions: *
* *
* The above copyright notice and this permission notice shall be included in all copies *
* or substantial portions of the Software. *
* *
* 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. *
****************************************************************************************/
#ifndef __OPENSPACE_CORE___INPUTSTATE___H__
#define __OPENSPACE_CORE___INPUTSTATE___H__
#include <openspace/util/keys.h>
#include <openspace/util/mouse.h>
#include <glm/glm.hpp>
#include <list>
namespace openspace {
namespace interaction {
class InputState {
public:
InputState() = default;
~InputState() = default;
// Callback functions
void keyboardCallback(Key key, KeyModifier modifier, KeyAction action);
void mouseButtonCallback(MouseButton button, MouseAction action);
void mousePositionCallback(double mouseX, double mouseY);
void mouseScrollWheelCallback(double mouseScrollDelta);
// Accessors
const std::list<std::pair<Key, KeyModifier>>& getPressedKeys() const;
const std::list<MouseButton>& getPressedMouseButtons() const;
glm::dvec2 getMousePosition() const;
double getMouseScrollDelta() const;
bool isKeyPressed(std::pair<Key, KeyModifier> keyModPair) const;
bool isKeyPressed(Key key) const;
bool isMouseButtonPressed(MouseButton mouseButton) const;
private:
// Input from keyboard and mouse
std::list<std::pair<Key, KeyModifier>> _keysDown;
std::list<MouseButton> _mouseButtonsDown;
glm::dvec2 _mousePosition;
double _mouseScrollDelta;
};
} // namespace interaction
} // namespace openspace
#endif // __OPENSPACE_CORE___INPUTSTATE___H__

View File

@@ -1,286 +0,0 @@
/*****************************************************************************************
* *
* OpenSpace *
* *
* Copyright (c) 2014-2017 *
* *
* Permission is hereby granted, free of charge, to any person obtaining a copy of this *
* software and associated documentation files (the "Software"), to deal in the Software *
* without restriction, including without limitation the rights to use, copy, modify, *
* merge, publish, distribute, sublicense, and/or sell copies of the Software, and to *
* permit persons to whom the Software is furnished to do so, subject to the following *
* conditions: *
* *
* The above copyright notice and this permission notice shall be included in all copies *
* or substantial portions of the Software. *
* *
* 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. *
****************************************************************************************/
#ifndef __OPENSPACE_CORE___INTERACTIONMODE___H__
#define __OPENSPACE_CORE___INTERACTIONMODE___H__
#include <openspace/network/parallelconnection.h>
#include <openspace/util/mouse.h>
#include <openspace/util/keys.h>
#include <openspace/util/timeline.h>
#ifdef OPENSPACE_MODULE_GLOBEBROWSING_ENABLED
#include <modules/globebrowsing/tile/tileindex.h>
#include <modules/globebrowsing/geometry/geodetic2.h>
#include <modules/globebrowsing/geometry/geodetic3.h>
#endif
#include <list>
namespace openspace {
class Camera;
class SceneGraphNode;
namespace globebrowsing {
class RenderableGlobe;
}
namespace interaction {
template <typename T>
class Interpolator
{
public:
Interpolator(std::function<T(double)> transferFunction)
: _t(0.0)
, _transferFunction(transferFunction) {};
~Interpolator() {};
void start() { _t = 0.0; };
void end() { _t = 1.0; };
void step(double delta) { _t += delta; };
T value() { return _transferFunction(_t); };
bool isInterpolating() { return _t < 1.0; };
private:
std::function<T(double)> _transferFunction;
double _t;
};
class InputState
{
public:
InputState();
~InputState();
// Callback functions
void keyboardCallback(Key key, KeyModifier modifier, KeyAction action);
void mouseButtonCallback(MouseButton button, MouseAction action);
void mousePositionCallback(double mouseX, double mouseY);
void mouseScrollWheelCallback(double mouseScrollDelta);
// Accessors
const std::list<std::pair<Key, KeyModifier> >& getPressedKeys() const;
const std::list<MouseButton>& getPressedMouseButtons() const;
glm::dvec2 getMousePosition() const;
double getMouseScrollDelta() const;
const std::vector<datamessagestructures::CameraKeyframe>& keyframes() const;
bool isKeyPressed(std::pair<Key, KeyModifier> keyModPair) const;
bool isKeyPressed(Key key) const;
bool isMouseButtonPressed(MouseButton mouseButton) const;
private:
// Input from keyboard and mouse
std::list<std::pair<Key, KeyModifier> > _keysDown;
std::list<MouseButton> _mouseButtonsDown;
glm::dvec2 _mousePosition;
double _mouseScrollDelta;
};
class InteractionMode {
public:
InteractionMode();
virtual ~InteractionMode();
// Mutators
virtual void setFocusNode(SceneGraphNode* focusNode);
// Accessors
SceneGraphNode* focusNode();
Interpolator<double>& rotateToFocusNodeInterpolator();
virtual bool followingNodeRotation() const = 0;
virtual void updateMouseStatesFromInput(const InputState& inputState, double deltaTime) = 0;
virtual void updateCameraStateFromMouseStates(Camera& camera, double deltaTime) = 0;
protected:
/**
Inner class that acts as a smoothing filter to a variable. The filter has a step
response on a form that resembles the function y = 1-e^(-t/scale). The variable
will be updates as soon as it is set to a value (calling the set() function).
*/
template <typename T, typename ScaleType>
class DelayedVariable {
public:
DelayedVariable(ScaleType scaleFactor, ScaleType friction) {
_scaleFactor = scaleFactor;
_friction = glm::max(friction, ScaleType(0.0));
}
void set(T value, double dt) {
_targetValue = value;
_currentValue = _currentValue + (_targetValue - _currentValue) *
std::min(_scaleFactor * dt, 1.0); // less or equal to 1.0 keeps it stable
}
void decelerate(double dt) {
_currentValue = _currentValue + (- _currentValue) *
std::min(_scaleFactor * _friction * dt, 1.0); // less or equal to 1.0 keeps it stable
}
void setHard(T value) {
_targetValue = value;
_currentValue = value;
}
void setFriction(ScaleType friction) {
_friction = glm::max(friction, ScaleType(0.0));
}
void setScaleFactor(ScaleType scaleFactor) {
_scaleFactor = scaleFactor;
}
T get() {
return _currentValue;
}
private:
ScaleType _scaleFactor;
ScaleType _friction;
T _targetValue;
T _currentValue;
};
struct MouseState {
MouseState(double scaleFactor)
: velocity(scaleFactor, 1)
, previousPosition(0.0, 0.0) {}
void setFriction(double friction) {
velocity.setFriction(friction);
}
void setVelocityScaleFactor(double scaleFactor) {
velocity.setScaleFactor(scaleFactor);
}
glm::dvec2 previousPosition;
DelayedVariable<glm::dvec2, double> velocity;
};
SceneGraphNode* _focusNode = nullptr;
glm::dvec3 _previousFocusNodePosition;
glm::dquat _previousFocusNodeRotation;
Interpolator<double> _rotateToFocusNodeInterpolator;
};
class KeyframeInteractionMode : public InteractionMode
{
public:
struct CameraPose {
glm::dvec3 position;
glm::quat rotation;
std::string focusNode;
bool followFocusNodeRotation;
};
KeyframeInteractionMode();
~KeyframeInteractionMode();
virtual void updateMouseStatesFromInput(const InputState& inputState, double deltaTime);
virtual void updateCameraStateFromMouseStates(Camera& camera, double deltaTime);
bool followingNodeRotation() const override;
Timeline<CameraPose>& timeline();
private:
Timeline<CameraPose> _cameraPoseTimeline;
};
class GlobeBrowsingInteractionMode;
class OrbitalInteractionMode : public InteractionMode
{
public:
class MouseStates
{
public:
/**
\param sensitivity
\param velocityScaleFactor can be set to 60 to remove the inertia of the
interaction. Lower value will make it harder to move the camera.
*/
MouseStates(double sensitivity, double velocityScaleFactor);
void updateMouseStatesFromInput(const InputState& inputState, double deltaTime);
void setRotationalFriction(double friction);
void setHorizontalFriction(double friction);
void setVerticalFriction(double friction);
void setSensitivity(double sensitivity);
void setVelocityScaleFactor(double scaleFactor);
glm::dvec2 synchedGlobalRotationMouseVelocity();
glm::dvec2 synchedLocalRotationMouseVelocity();
glm::dvec2 synchedTruckMovementMouseVelocity();
glm::dvec2 synchedLocalRollMouseVelocity();
glm::dvec2 synchedGlobalRollMouseVelocity();
private:
double _sensitivity;
MouseState _globalRotationMouseState;
MouseState _localRotationMouseState;
MouseState _truckMovementMouseState;
MouseState _localRollMouseState;
MouseState _globalRollMouseState;
};
OrbitalInteractionMode(std::shared_ptr<MouseStates> mouseStates);
~OrbitalInteractionMode();
//virtual void update(Camera& camera, const InputState& inputState, double deltaTime);
virtual void updateMouseStatesFromInput(const InputState& inputState, double deltaTime);
virtual void updateCameraStateFromMouseStates(Camera& camera, double deltaTime);
bool followingNodeRotation() const override;
protected:
//void updateCameraStateFromMouseStates(Camera& camera, double deltaTime);
std::shared_ptr<MouseStates> _mouseStates;
};
class GlobeBrowsingInteractionMode : public OrbitalInteractionMode
{
public:
GlobeBrowsingInteractionMode(std::shared_ptr<MouseStates> mouseStates);
~GlobeBrowsingInteractionMode();
virtual void setFocusNode(SceneGraphNode* focusNode);
//virtual void update(Camera& camera, const InputState& inputState, double deltaTime);
virtual void updateCameraStateFromMouseStates(Camera& camera, double deltaTime);
bool followingNodeRotation() const override;
#ifdef OPENSPACE_MODULE_GLOBEBROWSING_ENABLED
void goToChunk(Camera& camera, globebrowsing::TileIndex ti, glm::vec2 uv,
bool resetCameraDirection);
void goToGeodetic2(Camera& camera, globebrowsing::Geodetic2 geo2,
bool resetCameraDirection);
void goToGeodetic3(Camera& camera, globebrowsing::Geodetic3 geo3);
void resetCameraDirection(Camera& camera, globebrowsing::Geodetic2 geo2);
#endif
private:
//void updateCameraStateFromMouseStates(Camera& camera, double deltaTime);
#ifdef OPENSPACE_MODULE_GLOBEBROWSING_ENABLED
globebrowsing::RenderableGlobe* _globe;
#endif
};
} // namespace interaction
} // namespace openspace
#endif // __OPENSPACE_CORE___INTERACTIONMODE___H__

View File

@@ -0,0 +1,65 @@
/*****************************************************************************************
* *
* OpenSpace *
* *
* Copyright (c) 2014-2017 *
* *
* Permission is hereby granted, free of charge, to any person obtaining a copy of this *
* software and associated documentation files (the "Software"), to deal in the Software *
* without restriction, including without limitation the rights to use, copy, modify, *
* merge, publish, distribute, sublicense, and/or sell copies of the Software, and to *
* permit persons to whom the Software is furnished to do so, subject to the following *
* conditions: *
* *
* The above copyright notice and this permission notice shall be included in all copies *
* or substantial portions of the Software. *
* *
* 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. *
****************************************************************************************/
#ifndef __OPENSPACE_CORE___INTERPOLATOR___H__
#define __OPENSPACE_CORE___INTERPOLATOR___H__
#include <functional>
namespace openspace {
namespace interaction {
/*
* Interpolates a typename T using a transfer function.
*/
template <typename T>
class Interpolator {
public:
Interpolator();
~Interpolator() = default;
void start();
void end();
void setDeltaTime(float deltaTime);
void setTransferFunction(std::function<T(float)> transferFunction);
void setInterpolationTime(float interpolationTime);
void step();
float deltaTimeScaled() const;
T value() const;
bool isInterpolating() const;
private:
std::function<T(float)> _transferFunction;
float _t;
float _interpolationTime;
float _scaledDeltaTime;
};
} // namespace interaction
} // namespace openspace
#include "interpolator.inl"
#endif // __OPENSPACE_CORE___INTERPOLATOR___H__

View File

@@ -0,0 +1,85 @@
/*****************************************************************************************
* *
* OpenSpace *
* *
* Copyright (c) 2014-2017 *
* *
* Permission is hereby granted, free of charge, to any person obtaining a copy of this *
* software and associated documentation files (the "Software"), to deal in the Software *
* without restriction, including without limitation the rights to use, copy, modify, *
* merge, publish, distribute, sublicense, and/or sell copies of the Software, and to *
* permit persons to whom the Software is furnished to do so, subject to the following *
* conditions: *
* *
* The above copyright notice and this permission notice shall be included in all copies *
* or substantial portions of the Software. *
* *
* 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. *
****************************************************************************************/
#include <ghoul/misc/assert.h>
#include <functional>
namespace openspace {
namespace interaction {
template <typename T>
Interpolator<T>::Interpolator()
: _transferFunction([](float t){ return t; })
, _t(0.0)
, _interpolationTime(1.0) {};
template <typename T>
void Interpolator<T>::start() {
_t = 0.0;
};
template <typename T>
void Interpolator<T>::end() {
_t = 1.0;
}
template <typename T>
void Interpolator<T>::setDeltaTime(float deltaTime) {
_scaledDeltaTime = deltaTime / _interpolationTime;
}
template <typename T>
void Interpolator<T>::setTransferFunction(std::function<T(float)> transferFunction) {
_transferFunction = transferFunction;
}
template <typename T>
void Interpolator<T>::setInterpolationTime(float interpolationTime) {
_interpolationTime = interpolationTime;
}
template <typename T>
void Interpolator<T>::step() {
_t += _scaledDeltaTime;
_t = glm::clamp(_t, 0.0f, 1.0f);
}
template <typename T>
float Interpolator<T>::deltaTimeScaled() const {
return _scaledDeltaTime;
}
template <typename T>
T Interpolator<T>::value() const {
return _transferFunction(_t);
}
template <typename T>
bool Interpolator<T>::isInterpolating() const {
return _t < 1.0 && _t >= 0.0;
}
} // namespace interaction
} // namespace openspace

View File

@@ -0,0 +1,87 @@
/*****************************************************************************************
* *
* OpenSpace *
* *
* Copyright (c) 2014-2017 *
* *
* Permission is hereby granted, free of charge, to any person obtaining a copy of this *
* software and associated documentation files (the "Software"), to deal in the Software *
* without restriction, including without limitation the rights to use, copy, modify, *
* merge, publish, distribute, sublicense, and/or sell copies of the Software, and to *
* permit persons to whom the Software is furnished to do so, subject to the following *
* conditions: *
* *
* The above copyright notice and this permission notice shall be included in all copies *
* or substantial portions of the Software. *
* *
* 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. *
****************************************************************************************/
#ifndef __OPENSPACE_CORE___KEYBINDINGMANAGER___H__
#define __OPENSPACE_CORE___KEYBINDINGMANAGER___H__
#include <openspace/documentation/documentationgenerator.h>
#include <openspace/scripting/lualibrary.h>
#include <openspace/util/keys.h>
#include <ghoul/misc/boolean.h>
namespace openspace {
class Camera;
class SceneGraphNode;
namespace interaction {
class KeyBindingManager : public DocumentationGenerator
{
public:
KeyBindingManager();
~KeyBindingManager() = default;
void resetKeyBindings();
void bindKeyLocal(
Key key,
KeyModifier modifier,
std::string luaCommand,
std::string documentation = ""
);
void bindKey(
Key key,
KeyModifier modifier,
std::string luaCommand,
std::string documentation = ""
);
static scripting::LuaLibrary luaLibrary();
// Callback functions
void keyboardCallback(Key key, KeyModifier modifier, KeyAction action);
private:
using Synchronized = ghoul::Boolean;
struct KeyInformation {
std::string command;
Synchronized synchronization;
std::string documentation;
};
std::string generateJson() const override;
bool _cameraUpdatedFromScript = false;
std::multimap<KeyWithModifier, KeyInformation> _keyLua;
};
} // namespace interaction
} // namespace openspace
#endif // __OPENSPACE_CORE___KEYBINDINGMANAGER___H__

View File

@@ -0,0 +1,69 @@
/*****************************************************************************************
* *
* OpenSpace *
* *
* Copyright (c) 2014-2017 *
* *
* Permission is hereby granted, free of charge, to any person obtaining a copy of this *
* software and associated documentation files (the "Software"), to deal in the Software *
* without restriction, including without limitation the rights to use, copy, modify, *
* merge, publish, distribute, sublicense, and/or sell copies of the Software, and to *
* permit persons to whom the Software is furnished to do so, subject to the following *
* conditions: *
* *
* The above copyright notice and this permission notice shall be included in all copies *
* or substantial portions of the Software. *
* *
* 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. *
****************************************************************************************/
#ifndef __OPENSPACE_CORE___KEYFRAMENAVIGATOR___H__
#define __OPENSPACE_CORE___KEYFRAMENAVIGATOR___H__
#include <openspace/util/timeline.h>
#include <openspace/network/parallelconnection.h>
#include <glm/glm.hpp>
#include <glm/gtx/quaternion.hpp>
namespace openspace {
class Camera;
namespace interaction {
class KeyframeNavigator
{
public:
struct CameraPose {
glm::dvec3 position;
glm::quat rotation;
std::string focusNode;
bool followFocusNodeRotation;
};
KeyframeNavigator() = default;
~KeyframeNavigator() = default;
void updateCamera(Camera& camera);
Timeline<CameraPose>& timeline();
void addKeyframe(double timestamp, KeyframeNavigator::CameraPose pose);
void removeKeyframesAfter(double timestamp);
void clearKeyframes();
size_t nKeyframes() const;
const std::vector<datamessagestructures::CameraKeyframe>& keyframes() const;
private:
Timeline<CameraPose> _cameraPoseTimeline;
};
} // namespace interaction
} // namespace openspace
#endif // __OPENSPACE_CORE___KEYFRAMENAVIGATOR___H__

View File

@@ -0,0 +1,80 @@
/*****************************************************************************************
* *
* OpenSpace *
* *
* Copyright (c) 2014-2017 *
* *
* Permission is hereby granted, free of charge, to any person obtaining a copy of this *
* software and associated documentation files (the "Software"), to deal in the Software *
* without restriction, including without limitation the rights to use, copy, modify, *
* merge, publish, distribute, sublicense, and/or sell copies of the Software, and to *
* permit persons to whom the Software is furnished to do so, subject to the following *
* conditions: *
* *
* The above copyright notice and this permission notice shall be included in all copies *
* or substantial portions of the Software. *
* *
* 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. *
****************************************************************************************/
#ifndef __OPENSPACE_CORE___MOUSESTATE___H__
#define __OPENSPACE_CORE___MOUSESTATE___H__
#include <openspace/interaction/delayedvariable.h>
#include <openspace/interaction/inputstate.h>
#include <glm/glm.hpp>
namespace openspace {
namespace interaction {
struct MouseState {
MouseState(double scaleFactor);
void setFriction(double friction);
void setVelocityScaleFactor(double scaleFactor);
glm::dvec2 previousPosition;
DelayedVariable<glm::dvec2, double> velocity;
};
class MouseStates
{
public:
/**
\param sensitivity
\param velocityScaleFactor can be set to 60 to remove the inertia of the
interaction. Lower value will make it harder to move the camera.
*/
MouseStates(double sensitivity, double velocityScaleFactor);
void updateMouseStatesFromInput(const InputState& inputState, double deltaTime);
void setRotationalFriction(double friction);
void setHorizontalFriction(double friction);
void setVerticalFriction(double friction);
void setSensitivity(double sensitivity);
void setVelocityScaleFactor(double scaleFactor);
glm::dvec2 globalRotationMouseVelocity() const;
glm::dvec2 localRotationMouseVelocity() const;
glm::dvec2 truckMovementMouseVelocity() const;
glm::dvec2 localRollMouseVelocity() const;
glm::dvec2 globalRollMouseVelocity() const;
private:
double _sensitivity;
MouseState _globalRotationMouseState;
MouseState _localRotationMouseState;
MouseState _truckMovementMouseState;
MouseState _localRollMouseState;
MouseState _globalRollMouseState;
};
} // namespace interaction
} // namespace openspace
#endif // __OPENSPACE_CORE___MOUSESTATE___H__

View File

@@ -22,14 +22,13 @@
* OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. *
****************************************************************************************/
#ifndef __OPENSPACE_CORE___INTERACTIONHANDLER___H__
#define __OPENSPACE_CORE___INTERACTIONHANDLER___H__
#ifndef __OPENSPACE_CORE___NAVIGATIONHANDLER___H__
#define __OPENSPACE_CORE___NAVIGATIONHANDLER___H__
#include <openspace/documentation/documentationgenerator.h>
#include <openspace/properties/propertyowner.h>
#include <openspace/interaction/interactionmode.h>
#include <openspace/network/parallelconnection.h>
#include <openspace/interaction/orbitalnavigator.h>
#include <openspace/interaction/keyframenavigator.h>
#include <openspace/properties/stringproperty.h>
#include <openspace/properties/scalar/boolproperty.h>
#include <openspace/properties/scalar/floatproperty.h>
@@ -38,10 +37,6 @@
#include <ghoul/misc/boolean.h>
#include <list>
#include <mutex>
namespace openspace {
class Camera;
@@ -49,11 +44,10 @@ class SceneGraphNode;
namespace interaction {
class InteractionHandler : public properties::PropertyOwner, public DocumentationGenerator
{
class NavigationHandler : public properties::PropertyOwner {
public:
InteractionHandler();
~InteractionHandler();
NavigationHandler();
~NavigationHandler();
void initialize();
void deinitialize();
@@ -63,39 +57,9 @@ public:
void setCamera(Camera* camera);
void resetCameraDirection();
// Interaction mode setters
void setCameraStateFromDictionary(const ghoul::Dictionary& cameraDict);
InteractionMode* interactionMode();
void setCameraStateFromDictionary(const ghoul::Dictionary& cameraDict);
void goToChunk(int x, int y, int level);
void goToGeo(double latitude, double longitude);
void resetKeyBindings();
void addKeyframe(double timestamp, KeyframeInteractionMode::CameraPose pose);
void removeKeyframesAfter(double timestamp);
void clearKeyframes();
size_t nKeyframes() const;
const std::vector<datamessagestructures::CameraKeyframe>& keyframes() const;
void bindKeyLocal(
Key key,
KeyModifier modifier,
std::string luaCommand,
std::string documentation = ""
);
void bindKey(
Key key,
KeyModifier modifier,
std::string luaCommand,
std::string documentation = ""
);
void lockControls();
void unlockControls();
//void update(double deltaTime);
void updateCamera(double deltaTime);
void updateInputStates(double timeSinceLastUpdate);
// Accessors
ghoul::Dictionary getCameraStateDictionary();
@@ -104,15 +68,8 @@ public:
glm::quat focusNodeToCameraRotation() const;
Camera* camera() const;
const InputState& inputState() const;
/**
* Returns the Lua library that contains all Lua functions available to affect the
* interaction. The functions contained are
* - openspace::luascriptfunctions::setOrigin
* \return The Lua library that contains all Lua functions available to affect the
* interaction
*/
static scripting::LuaLibrary luaLibrary();
const OrbitalNavigator& orbitalNavigator() const;
KeyframeNavigator& keyframeNavigator() const;
// Callback functions
void keyboardCallback(Key key, KeyModifier modifier, KeyAction action);
@@ -123,47 +80,26 @@ public:
void saveCameraStateToFile(const std::string& filepath);
void restoreCameraStateFromFile(const std::string& filepath);
/**
* \return The Lua library that contains all Lua functions available to affect the
* interaction
*/
static scripting::LuaLibrary luaLibrary();
private:
using Synchronized = ghoul::Boolean;
struct KeyInformation {
std::string command;
Synchronized synchronization;
std::string documentation;
};
std::string generateJson() const override;
void setInteractionMode(InteractionMode* interactionMode);
bool _cameraUpdatedFromScript = false;
std::multimap<KeyWithModifier, KeyInformation> _keyLua;
std::unique_ptr<InputState> _inputState;
Camera* _camera;
InteractionMode* _currentInteractionMode;
std::shared_ptr<OrbitalInteractionMode::MouseStates> _mouseStates;
std::unique_ptr<OrbitalInteractionMode> _orbitalInteractionMode;
std::unique_ptr<GlobeBrowsingInteractionMode> _globeBrowsingInteractionMode;
std::unique_ptr<KeyframeInteractionMode> _keyframeInteractionMode;
std::unique_ptr<OrbitalNavigator> _orbitalNavigator;
std::unique_ptr<KeyframeNavigator> _keyframeNavigator;
// Properties
properties::StringProperty _origin;
properties::OptionProperty _interactionModeOption;
properties::BoolProperty _rotationalFriction;
properties::BoolProperty _horizontalFriction;
properties::BoolProperty _verticalFriction;
properties::FloatProperty _sensitivity;
properties::FloatProperty _rapidness;
properties::BoolProperty _useKeyFrameInteraction;
};
} // namespace interaction
} // namespace openspace
#endif // __OPENSPACE_CORE___INTERACTIONHANDLER___H__
#endif // __OPENSPACE_CORE___NAVIGATIONHANDLER___H__

View File

@@ -0,0 +1,197 @@
/*****************************************************************************************
* *
* OpenSpace *
* *
* Copyright (c) 2014-2017 *
* *
* Permission is hereby granted, free of charge, to any person obtaining a copy of this *
* software and associated documentation files (the "Software"), to deal in the Software *
* without restriction, including without limitation the rights to use, copy, modify, *
* merge, publish, distribute, sublicense, and/or sell copies of the Software, and to *
* permit persons to whom the Software is furnished to do so, subject to the following *
* conditions: *
* *
* The above copyright notice and this permission notice shall be included in all copies *
* or substantial portions of the Software. *
* *
* 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. *
****************************************************************************************/
#ifndef __OPENSPACE_CORE___ORBITALNAVIGATOR___H__
#define __OPENSPACE_CORE___ORBITALNAVIGATOR___H__
#include <openspace/interaction/delayedvariable.h>
#include <openspace/interaction/inputstate.h>
#include <openspace/interaction/interpolator.h>
#include <openspace/interaction/mousestate.h>
#include <openspace/properties/propertyowner.h>
#include <openspace/properties/scalar/boolproperty.h>
#include <openspace/properties/scalar/floatproperty.h>
#include <glm/glm.hpp>
#include <glm/gtx/quaternion.hpp>
namespace openspace {
class SceneGraphNode;
class Camera;
class SurfacePositionHandle;
namespace interaction {
class OrbitalNavigator : public properties::PropertyOwner {
public:
OrbitalNavigator();
~OrbitalNavigator();
void updateMouseStatesFromInput(const InputState& inputState, double deltaTime);
void updateCameraStateFromMouseStates(Camera& camera, double deltaTime);
void setFocusNode(SceneGraphNode* focusNode);
void startInterpolateCameraDirection(const Camera& camera);
bool followingNodeRotation() const;
SceneGraphNode* focusNode() const;
private:
struct CameraRotationDecomposition {
glm::dquat localRotation;
glm::dquat globalRotation;
};
// Properties
properties::BoolProperty _rotationalFriction;
properties::BoolProperty _horizontalFriction;
properties::BoolProperty _verticalFriction;
properties::FloatProperty _followFocusNodeRotationDistance;
properties::FloatProperty _minimumAllowedDistance;
properties::FloatProperty _sensitivity;
properties::FloatProperty _motionLag;
MouseStates _mouseStates;
SceneGraphNode* _focusNode = nullptr;
glm::dvec3 _previousFocusNodePosition;
glm::dquat _previousFocusNodeRotation;
Interpolator<double> _rotateToFocusNodeInterpolator;
Interpolator<double> _followRotationInterpolator;
/**
* Decomposes the cameras rotation in to a global and a local rotation defined by
* CameraRotationDecomposition. The global rotation defines the rotation so that the
* camera points towards the focus node in the direction opposite to the direction
* out from the surface of the object. The local rotation defines the differential
* from the global to the current total rotation so that
* <code>cameraRotation = globalRotation * localRotation</code>.
*/
CameraRotationDecomposition decomposeCameraRotation(
const glm::dvec3& cameraPosition,
const glm::dquat& cameraRotation,
const glm::dvec3& cameraLookUp,
const glm::dvec3& cameraViewDirection);
/*
* Perform a camera roll on the local camera rotation
* \returns a local camera rotation modified with a roll.
*/
glm::dquat roll(double deltaTime, const glm::dquat& localCameraRotation) const;
/**
* Performs rotation around the cameras x and y axes.
* \returns a local camera rotation modified with two degrees of freedom.
*/
glm::dquat rotateLocally(double deltaTime,
const glm::dquat& localCameraRotation) const;
/**
* Interpolates the local rotation towards a 0 rotation.
* \returns a modified local rotation interpolated towards 0.
*/
glm::dquat interpolateLocalRotation(double deltaTime,
const glm::dquat& localCameraRotation);
/**
* Translates the horizontal direction. If far from the focus object, this will
* result in an orbital rotation around the object. This function does not affect the
* rotation but only the position.
* \returns a position vector adjusted in the horizontal direction.
*/
glm::dvec3 translateHorizontally(double deltaTime, const glm::dvec3& cameraPosition,
const glm::dvec3& objectPosition,
const glm::dquat& focusNodeRotationDiff,
const glm::dquat& globalCameraRotation,
const SurfacePositionHandle& positionHandle) const;
/*
* Adds rotation to the camera position so that it follows the rotation of the focus
* node defined by the differential focusNodeRotationDiff.
* \returns a position updated with the rotation defined by focusNodeRotationDiff
*/
glm::dvec3 followFocusNodeRotation(const glm::dvec3& cameraPosition,
const glm::dvec3& objectPosition,
const glm::dquat& focusNodeRotationDiff) const;
/**
* Updates the global rotation so that it points towards the focus node.
* \returns a global rotation quaternion defining a rotation towards the focus node.
*/
glm::dquat rotateGlobally(const glm::dquat& globalCameraRotation,
const glm::dvec3& objectPosition,
const glm::dquat& focusNodeRotationDiff,
const glm::dvec3& cameraPosition,
const SurfacePositionHandle& positionHandle) const;
/**
* Translates the camera position towards or away from the focus node.
* \returns a position vector adjusted in the vertical direction.
*/
glm::dvec3 translateVertically(double deltaTime, const glm::dvec3& cameraPosition,
const glm::dvec3& objectPosition,
const SurfacePositionHandle& positionHandle) const;
/**
* Rotates the camera around the out vector of the surface.
* \returns a quaternion adjusted to rotate around the out vector of the surface.
*/
glm::dquat rotateHorizontally(double deltaTime,
const glm::dquat& globalCameraRotation,
const glm::dvec3& cameraPosition,
const SurfacePositionHandle& positionHandle) const;
/**
* Push the camera out to the surface of the object.
* \returns a position vector adjusted to be at least minHeightAboveGround meters
* above the actual surface of the object
*/
glm::dvec3 pushToSurface(double minHeightAboveGround,
const glm::dvec3& cameraPosition,
const glm::dvec3& objectPosition,
const SurfacePositionHandle& positionHandle) const;
/**
* Interpolates between rotationDiff and a 0 rotation.
*/
glm::dquat interpolateRotationDifferential(
double deltaTime, double interpolationTime,
const glm::dquat& rotationDiff,
const glm::dvec3& objectPosition,
const glm::dvec3& cameraPosition,
const SurfacePositionHandle& positionHandle);
/**
* Calculates a SurfacePositionHandle given a camera position in world space.
*/
SurfacePositionHandle calculateSurfacePositionHandle(
const glm::dvec3 cameraPositionWorldSpace);
};
} // namespace interaction
} // namespace openspace
#endif // __OPENSPACE_CORE___ORBITALNAVIGATOR___H__

View File

@@ -42,6 +42,7 @@ namespace openspace {
struct RenderData;
struct UpdateData;
struct RendererTasks;
struct SurfacePositionHandle;
namespace documentation { struct Documentation; }
@@ -78,6 +79,8 @@ public:
virtual void render(const RenderData& data);
virtual void render(const RenderData& data, RendererTasks& rendererTask);
virtual void update(const UpdateData& data);
virtual SurfacePositionHandle calculateSurfacePositionHandle(
const glm::dvec3& targetModelSpace);
RenderBin renderBin() const;
void setRenderBin(RenderBin bin);

View File

@@ -48,6 +48,7 @@ class Translation;
class Scale;
class Scene;
struct UpdateData;
struct SurfacePositionHandle;
namespace documentation { struct Documentation; }
@@ -91,6 +92,8 @@ public:
void removeDependency(SceneGraphNode& dependency, UpdateScene updateScene = UpdateScene::Yes);
void clearDependencies(UpdateScene updateScene = UpdateScene::Yes);
void setDependencies(const std::vector<SceneGraphNode*>& dependencies, UpdateScene updateScene = UpdateScene::Yes);
SurfacePositionHandle calculateSurfacePositionHandle(
const glm::dvec3& targetModelSpace);
const std::vector<SceneGraphNode*>& dependencies() const;
const std::vector<SceneGraphNode*>& dependentNodes() const;

View File

@@ -75,6 +75,21 @@ struct RaycastData {
std::string namespaceName;
};
/**
* Defines the position of an object relative to a surface. The surface is defined as
* a reference surface together with a height offset from that reference surface.
*/
struct SurfacePositionHandle {
/// Vector from the center of the object to the reference surface of the object
glm::dvec3 centerToReferenceSurface;
/// Direction out from the reference. Can conincide with the surface normal but does
/// not have to.
glm::dvec3 referenceSurfaceOutDirection;
/// Height from the reference surface out to the actual surface in the direction of
/// the surface normal. Can be positive or negative.
double heightToSurface;
};
} // namespace openspace
#endif // __OPENSPACE_CORE___UPDATESTRUCTURES___H__

View File

@@ -102,7 +102,7 @@ Chunk::BoundingHeights Chunk::getBoundingHeights() const {
// a single raster image. If it is not we will just use the first raster
// (that is channel 0).
const size_t HeightChannel = 0;
const LayerGroup& heightmaps = layerManager->layerGroup(layergroupid::HeightLayers);
const LayerGroup& heightmaps = layerManager->layerGroup(layergroupid::GroupID::HeightLayers);
std::vector<ChunkTileSettingsPair> chunkTileSettingPairs =
tileselector::getTilesAndSettingsUnsorted(
heightmaps, _tileIndex);

View File

@@ -27,11 +27,10 @@
#include <modules/globebrowsing/geometry/geodetic2.h>
#include <modules/globebrowsing/tile/quad.h>
#include <modules/globebrowsing/tile/tileindex.h>
namespace openspace {
namespace globebrowsing {
struct TileIndex;
class GeodeticPatch {
public:

View File

@@ -25,8 +25,11 @@
#include <modules/globebrowsing/globebrowsingmodule.h>
#include <modules/globebrowsing/cache/memoryawaretilecache.h>
#include <modules/globebrowsing/geometry/geodetic2.h>
#include <modules/globebrowsing/geometry/geodeticpatch.h>
#include <modules/globebrowsing/globes/renderableglobe.h>
#include <modules/globebrowsing/other/distanceswitch.h>
#include <modules/globebrowsing/tile/tileindex.h>
#include <modules/globebrowsing/tile/rawtiledatareader/gdalwrapper.h>
#include <modules/globebrowsing/tile/tileprovider/defaulttileprovider.h>
#include <modules/globebrowsing/tile/tileprovider/singleimageprovider.h>
@@ -37,11 +40,11 @@
#include <modules/globebrowsing/tile/tileprovider/tileprovider.h>
#include <modules/globebrowsing/tile/tileprovider/tileproviderbylevel.h>
#include <modules/globebrowsing/tile/tileprovider/tileproviderbyindex.h>
#include <modules/globebrowsing/rendering/layer/layermanager.h>
#include <modules/globebrowsing/rendering/layer/layer.h>
#include <openspace/engine/openspaceengine.h>
#include <openspace/interaction/navigationhandler.h>
#include <openspace/util/factorymanager.h>
#include <ghoul/misc/templatefactory.h>
@@ -51,6 +54,10 @@
#include "globebrowsingmodule_lua.inl"
namespace {
const char* _loggerCat = "GlobeBrowsingModule";
}
namespace openspace {
GlobeBrowsingModule::GlobeBrowsingModule() : OpenSpaceModule(Name) {}
@@ -146,6 +153,18 @@ scripting::LuaLibrary GlobeBrowsingModule::luaLibrary() const {
"any of " + listLayerGroups + ". The third argument is the dictionary"
"defining the layer."
},
{
"goToChunk",
&globebrowsing::luascriptfunctions::goToChunk,
"void",
"Go to chunk with given index x, y, level"
},
{
"goToGeo",
&globebrowsing::luascriptfunctions::goToGeo,
"void",
"Go to geographic coordinates latitude and longitude"
}
},
{
"${MODULE_GLOBEBROWSING}/scripts/layer_support.lua"
@@ -156,6 +175,163 @@ scripting::LuaLibrary GlobeBrowsingModule::luaLibrary() const {
};
}
void GlobeBrowsingModule::goToChunk(int x, int y, int level) {
using namespace globebrowsing;
Camera* cam = OsEng.navigationHandler().camera();
goToChunk(*cam, TileIndex(x,y,level), glm::vec2(0.5, 0.5), true);
}
void GlobeBrowsingModule::goToGeo(double latitude, double longitude) {
using namespace globebrowsing;
Camera* cam = OsEng.navigationHandler().camera();
goToGeodetic2(*cam, Geodetic2(latitude, longitude) / 180 * glm::pi<double>(), true);
}
void GlobeBrowsingModule::goToGeo(double latitude, double longitude,
double altitude)
{
using namespace globebrowsing;
Camera* cam = OsEng.navigationHandler().camera();
goToGeodetic3(
*cam,
{
Geodetic2(latitude, longitude) / 180 * glm::pi<double>(),
altitude
},
true
);
}
void GlobeBrowsingModule::goToChunk(Camera& camera, globebrowsing::TileIndex ti,
glm::vec2 uv, bool resetCameraDirection)
{
using namespace globebrowsing;
RenderableGlobe* globe = castFocusNodeRenderableToGlobe();
if (!globe) {
LERROR("Focus node must have a RenderableGlobe renderable.");
return;
}
// Camera position in model space
glm::dvec3 camPos = camera.positionVec3();
glm::dmat4 inverseModelTransform = globe->inverseModelTransform();
glm::dvec3 cameraPositionModelSpace =
glm::dvec3(inverseModelTransform * glm::dvec4(camPos, 1));
GeodeticPatch patch(ti);
Geodetic2 corner = patch.getCorner(SOUTH_WEST);
Geodetic2 positionOnPatch = patch.getSize();
positionOnPatch.lat *= uv.y;
positionOnPatch.lon *= uv.x;
Geodetic2 pointPosition = corner + positionOnPatch;
glm::dvec3 positionOnEllipsoid =
globe->ellipsoid().geodeticSurfaceProjection(cameraPositionModelSpace);
double altitude = glm::length(cameraPositionModelSpace - positionOnEllipsoid);
goToGeodetic3(camera, {pointPosition, altitude}, resetCameraDirection);
}
void GlobeBrowsingModule::goToGeodetic2(Camera& camera,
globebrowsing::Geodetic2 geo2,
bool resetCameraDirection)
{
using namespace globebrowsing;
RenderableGlobe* globe = castFocusNodeRenderableToGlobe();
if (!globe) {
LERROR("Focus node must have a RenderableGlobe renderable.");
return;
}
// Camera position in model space
glm::dvec3 camPos = camera.positionVec3();
glm::dmat4 inverseModelTransform = globe->inverseModelTransform();
glm::dvec3 cameraPositionModelSpace =
glm::dvec3(inverseModelTransform * glm::dvec4(camPos, 1));
glm::dvec3 positionOnEllipsoid =
globe->ellipsoid().geodeticSurfaceProjection(cameraPositionModelSpace);
double altitude = glm::length(cameraPositionModelSpace - positionOnEllipsoid);
goToGeodetic3(camera, {geo2, altitude}, resetCameraDirection);
}
void GlobeBrowsingModule::goToGeodetic3(Camera& camera, globebrowsing::Geodetic3 geo3,
bool resetCameraDirection)
{
using namespace globebrowsing;
RenderableGlobe* globe = castFocusNodeRenderableToGlobe();
if (!globe) {
LERROR("Focus node must have a RenderableGlobe renderable.");
return;
}
glm::dvec3 positionModelSpace = globe->ellipsoid().cartesianPosition(geo3);
glm::dmat4 modelTransform = globe->modelTransform();
glm::dvec3 positionWorldSpace = modelTransform * glm::dvec4(positionModelSpace, 1.0);
camera.setPositionVec3(positionWorldSpace);
if (resetCameraDirection) {
this->resetCameraDirection(camera, geo3.geodetic2);
}
}
void GlobeBrowsingModule::resetCameraDirection(Camera& camera, globebrowsing::Geodetic2 geo2)
{
using namespace globebrowsing;
RenderableGlobe* globe = castFocusNodeRenderableToGlobe();
if (!globe) {
LERROR("Focus node must have a RenderableGlobe renderable.");
return;
}
// Camera is described in world space
glm::dmat4 modelTransform = globe->modelTransform();
// Lookup vector
glm::dvec3 positionModelSpace = globe->ellipsoid().cartesianSurfacePosition(geo2);
glm::dvec3 slightlyNorth = globe->ellipsoid().cartesianSurfacePosition(
Geodetic2(geo2.lat + 0.001, geo2.lon));
glm::dvec3 lookUpModelSpace = glm::normalize(slightlyNorth - positionModelSpace);
glm::dvec3 lookUpWorldSpace = glm::dmat3(modelTransform) * lookUpModelSpace;
// Lookat vector
glm::dvec3 lookAtWorldSpace = modelTransform * glm::dvec4(positionModelSpace, 1.0);
// Eye position
glm::dvec3 eye = camera.positionVec3();
// Matrix
glm::dmat4 lookAtMatrix = glm::lookAt(
eye, lookAtWorldSpace, lookUpWorldSpace);
// Set rotation
glm::dquat rotation = glm::quat_cast(inverse(lookAtMatrix));
camera.setRotation(rotation);
}
globebrowsing::RenderableGlobe* GlobeBrowsingModule::castFocusNodeRenderableToGlobe() {
using namespace globebrowsing;
Renderable* baseRenderable = OsEng.navigationHandler().focusNode()->renderable();
if (!baseRenderable) {
return nullptr;
}
if (globebrowsing::RenderableGlobe* globe =
dynamic_cast<RenderableGlobe*>(baseRenderable))
{
return globe;
}
else {
return nullptr;
}
}
std::string GlobeBrowsingModule::layerGroupNamesList() {
std::string listLayerGroups("");
for (int i = 0; i < globebrowsing::layergroupid::NUM_LAYER_GROUPS - 1; ++i) {

View File

@@ -26,11 +26,17 @@
#define __OPENSPACE_MODULE_GLOBEBROWSING___GLOBEBROWSING_MODULE___H__
#include <openspace/util/openspacemodule.h>
#include <ghoul/glm.h>
#include <memory>
namespace openspace {
class Camera;
namespace globebrowsing {
class RenderableGlobe;
class TileIndex;
class Geodetic2;
class Geodetic3;
namespace cache {
class MemoryAwareTileCache;
}
@@ -42,14 +48,26 @@ public:
GlobeBrowsingModule();
globebrowsing::cache::MemoryAwareTileCache* tileCache();
void goToChunk(int x, int y, int level);
void goToGeo(double latitude, double longitude);
void goToGeo(double latitude, double longitude, double altitude);
globebrowsing::cache::MemoryAwareTileCache* tileCache();
scripting::LuaLibrary luaLibrary() const override;
protected:
void internalInitialize() override;
private:
void goToChunk(Camera& camera, globebrowsing::TileIndex ti, glm::vec2 uv,
bool resetCameraDirection);
void goToGeodetic2(Camera& camera, globebrowsing::Geodetic2 geo2,
bool resetCameraDirection);
void goToGeodetic3(Camera& camera, globebrowsing::Geodetic3 geo3,
bool resetCameraDirection);
void resetCameraDirection(Camera& camera, globebrowsing::Geodetic2 geo2);
globebrowsing::RenderableGlobe* castFocusNodeRenderableToGlobe();
/**
\return a comma separated list of layer group names.
*/

View File

@@ -29,6 +29,7 @@
#include <modules/globebrowsing/rendering/layer/layer.h>
#include <openspace/engine/openspaceengine.h>
#include <openspace/engine/moduleengine.h>
#include <openspace/rendering/renderengine.h>
#include <openspace/rendering/renderable.h>
#include <openspace/scene/scene.h>
@@ -135,6 +136,47 @@ int deleteLayer(lua_State* L) {
return 0;
}
int goToChunk(lua_State* L) {
using ghoul::lua::luaTypeToString;
// Check arguments
int nArguments = lua_gettop(L);
if (nArguments != 3) {
return luaL_error(L, "Expected %i arguments, got %i", 3, nArguments);
}
int x = static_cast<int>(lua_tonumber(L, 1));
int y = static_cast<int>(lua_tonumber(L, 2));
int level = static_cast<int>(lua_tonumber(L, 3));
OsEng.moduleEngine().module<GlobeBrowsingModule>()->goToChunk(x, y, level);
return 0;
}
int goToGeo(lua_State* L) {
using ghoul::lua::luaTypeToString;
int nArguments = lua_gettop(L);
if (nArguments != 2 && nArguments != 3) {
return luaL_error(L, "Expected 2 or 3 arguments.");
}
double latitude = static_cast<int>(lua_tonumber(L, 1));
double longitude = static_cast<int>(lua_tonumber(L, 2));
if (nArguments == 2) {
OsEng.moduleEngine().module<GlobeBrowsingModule>()->goToGeo(latitude, longitude);
}
else if (nArguments == 3) {
double altitude = static_cast<int>(lua_tonumber(L, 3));
OsEng.moduleEngine().module<GlobeBrowsingModule>()->goToGeo(latitude, longitude,
altitude);
}
return 0;
}
} // namespace luascriptfunctions
} // nameapace globebrowsing

View File

@@ -174,7 +174,7 @@ float ChunkedLodGlobe::getHeight(glm::dvec3 position) const {
// Get the tile providers for the height maps
const std::vector<std::shared_ptr<Layer>>& heightMapLayers =
_layerManager->layerGroup(layergroupid::HeightLayers).activeLayers();
_layerManager->layerGroup(layergroupid::GroupID::HeightLayers).activeLayers();
for (const std::shared_ptr<Layer>& layer : heightMapLayers) {
tileprovider::TileProvider* tileProvider = layer->tileProvider();

View File

@@ -32,8 +32,6 @@
namespace {
const char* keyFrame = "Frame";
const char* keyRadii = "Radii";
const char* keyInteractionDepthBelowEllipsoid = "InteractionDepthBelowEllipsoid";
const char* keyCameraMinHeight = "CameraMinHeight";
const char* keySegmentsPerPatch = "SegmentsPerPatch";
const char* keyLayers = "Layers";
}
@@ -86,15 +84,6 @@ RenderableGlobe::RenderableGlobe(const ghoul::Dictionary& dictionary)
double patchSegmentsd;
dictionary.getValue(keySegmentsPerPatch, patchSegmentsd);
int patchSegments = patchSegmentsd;
if (!dictionary.getValue(keyInteractionDepthBelowEllipsoid,
_interactionDepthBelowEllipsoid)) {
_interactionDepthBelowEllipsoid = 0;
}
float cameraMinHeight;
dictionary.getValue(keyCameraMinHeight, cameraMinHeight);
_generalProperties.cameraMinHeight.set(cameraMinHeight);
// Init layer manager
ghoul::Dictionary layersDictionary;
@@ -262,8 +251,33 @@ const std::shared_ptr<const Camera> RenderableGlobe::savedCamera() const {
return _savedCamera;
}
double RenderableGlobe::interactionDepthBelowEllipsoid() {
return _interactionDepthBelowEllipsoid;
SurfacePositionHandle RenderableGlobe::calculateSurfacePositionHandle(
const glm::dvec3& targetModelSpace)
{
glm::dvec3 centerToEllipsoidSurface =
_ellipsoid.geodeticSurfaceProjection(targetModelSpace);
glm::dvec3 ellipsoidSurfaceToTarget = targetModelSpace - centerToEllipsoidSurface;
// ellipsoidSurfaceOutDirection will point towards the target, we want the outward
// direction. Therefore it must be flipped in case the target is under the reference
// ellipsoid so that it always points outwards
glm::dvec3 ellipsoidSurfaceOutDirection = glm::normalize(ellipsoidSurfaceToTarget);
if (glm::dot(ellipsoidSurfaceOutDirection, centerToEllipsoidSurface) < 0) {
ellipsoidSurfaceOutDirection *= -1.0;
}
double heightToSurface = getHeight(targetModelSpace);
heightToSurface = glm::isnan(heightToSurface) ? 0.0 : heightToSurface;
centerToEllipsoidSurface = glm::isnan(glm::length(centerToEllipsoidSurface)) ?
(glm::dvec3(0.0, 1.0, 0.0) * static_cast<double>(boundingSphere())) :
centerToEllipsoidSurface;
ellipsoidSurfaceOutDirection = glm::isnan(glm::length(ellipsoidSurfaceOutDirection)) ?
glm::dvec3(0.0, 1.0, 0.0) : ellipsoidSurfaceOutDirection;
return {
centerToEllipsoidSurface,
ellipsoidSurfaceOutDirection,
heightToSurface
};
}
void RenderableGlobe::setSaveCamera(std::shared_ptr<Camera> camera) {

View File

@@ -107,7 +107,11 @@ public:
double interactionDepthBelowEllipsoid();
// Setters
void setSaveCamera(std::shared_ptr<Camera> camera);
void setSaveCamera(std::shared_ptr<Camera> camera);
virtual SurfacePositionHandle calculateSurfacePositionHandle(
const glm::dvec3& targetModelSpace) override;
private:
// Globes. These are renderables inserted in a distance switch so that the heavier
// <code>ChunkedLodGlobe</code> does not have to be rendered at far distances.
@@ -119,7 +123,6 @@ private:
DistanceSwitch _distanceSwitch;
std::shared_ptr<Camera> _savedCamera;
double _interactionDepthBelowEllipsoid;
std::string _frame;
double _time;

View File

@@ -228,9 +228,9 @@ void ChunkRenderer::renderChunkGlobally(const Chunk& chunk, const RenderData& da
programObject->setUniform("radiiSquared", glm::vec3(ellipsoid.radiiSquared()));
if (_layerManager->layerGroup(
layergroupid::NightLayers).activeLayers().size() > 0 ||
layergroupid::GroupID::NightLayers).activeLayers().size() > 0 ||
_layerManager->layerGroup(
layergroupid::WaterMasks).activeLayers().size() > 0 ||
layergroupid::GroupID::WaterMasks).activeLayers().size() > 0 ||
chunk.owner().generalProperties().atmosphereEnabled ||
chunk.owner().generalProperties().performShading)
{

View File

@@ -58,7 +58,7 @@ void GPULayerGroup::bind(ghoul::opengl::ProgramObject* programObject,
int pileSize = layerGroup.pileSize();
for (size_t i = 0; i < _gpuActiveLayers.size(); ++i) {
// should maybe a proper GPULayer factory
_gpuActiveLayers[i] = (category == layergroupid::HeightLayers) ?
_gpuActiveLayers[i] = (category == layergroupid::GroupID::HeightLayers) ?
std::make_unique<GPUHeightLayer>() :
std::make_unique<GPULayer>();
std::string nameExtension = "[" + std::to_string(i) + "].";

View File

@@ -30,7 +30,7 @@
#include <openspace/engine/settingsengine.h>
#include <openspace/engine/virtualpropertymanager.h>
#include <openspace/engine/wrapper/windowwrapper.h>
#include <openspace/interaction/interactionhandler.h>
#include <openspace/interaction/navigationhandler.h>
#include <openspace/interaction/luaconsole.h>
#include <openspace/network/parallelconnection.h>
#include <openspace/rendering/renderengine.h>
@@ -70,7 +70,7 @@ OnScreenGUIModule::OnScreenGUIModule()
std::vector<properties::PropertyOwner*> res = {
&(OsEng.windowWrapper()),
&(OsEng.settingsEngine()),
&(OsEng.interactionHandler()),
&(OsEng.navigationHandler()),
&(OsEng.renderEngine()),
&(OsEng.parallelConnection()),
&(OsEng.console())

View File

@@ -25,7 +25,7 @@
#include <modules/onscreengui/include/guiorigincomponent.h>
#include <openspace/engine/openspaceengine.h>
#include <openspace/interaction/interactionhandler.h>
#include <openspace/interaction/navigationhandler.h>
#include <openspace/rendering/renderengine.h>
#include <openspace/scene/scenegraphnode.h>
#include <openspace/scene/scene.h>
@@ -42,7 +42,7 @@ GuiOriginComponent::GuiOriginComponent()
{}
void GuiOriginComponent::render() {
SceneGraphNode* currentFocus = OsEng.interactionHandler().focusNode();
SceneGraphNode* currentFocus = OsEng.navigationHandler().focusNode();
std::vector<SceneGraphNode*> nodes =
OsEng.renderEngine().scene()->allSceneGraphNodes();
@@ -70,7 +70,7 @@ void GuiOriginComponent::render() {
bool hasChanged = ImGui::Combo("Origin", &currentPosition, nodeNames.c_str());
if (hasChanged) {
OsEng.scriptEngine().queueScript(
"openspace.setPropertyValue('Interaction.origin', '" +
"openspace.setPropertyValue('NavigationHandler.origin', '" +
nodes[currentPosition]->name() + "');",
scripting::ScriptEngine::RemoteScripting::Yes
);

View File

@@ -26,7 +26,7 @@
#include <openspace/engine/openspaceengine.h>
#include <openspace/util/timemanager.h>
#include <openspace/interaction/interactionhandler.h>
#include <openspace/interaction/navigationhandler.h>
#include <openspace/network/parallelconnection.h>
#include <openspace/network/messagestructures.h>
@@ -83,7 +83,7 @@ void GuiParallelComponent::renderClientWithHost() {
renderClientCommon();
const size_t nTimeKeyframes = OsEng.timeManager().nKeyframes();
const size_t nCameraKeyframes = OsEng.interactionHandler().nKeyframes();
const size_t nCameraKeyframes = OsEng.navigationHandler().keyframeNavigator().nKeyframes();
std::string timeKeyframeInfo = "TimeKeyframes : " + std::to_string(nTimeKeyframes);
std::string cameraKeyframeInfo = "CameraKeyframes : " + std::to_string(nCameraKeyframes);

View File

@@ -31,8 +31,8 @@
#include <openspace/util/camera.h>
#include <openspace/scene/scenegraphnode.h>
#include <openspace/engine/wrapper/windowwrapper.h>
#include <openspace/interaction/interactionmode.h>
#include <openspace/interaction/interactionhandler.h>
#include <openspace/interaction/orbitalnavigator.h>
#include <openspace/interaction/navigationhandler.h>
#include <openspace/properties/propertyowner.h>
#include <openspace/properties/vectorproperty.h>
#include <openspace/properties/scalar/boolproperty.h>

View File

@@ -25,7 +25,7 @@
#include <modules/touch/include/TouchInteraction.h>
#include <modules/onscreengui/onscreenguimodule.h>
#include <openspace/interaction/interactionmode.h>
#include <openspace/interaction/orbitalnavigator.h>
#include <openspace/engine/openspaceengine.h>
#include <openspace/query/query.h>
#include <openspace/rendering/renderengine.h>
@@ -530,7 +530,7 @@ void TouchInteraction::computeVelocities(const std::vector<TuioCursor>& list, co
case PICK: { // pick something in the scene as focus node
if (_selected.size() == 1 && _selected.at(0).node) {
setFocusNode(_selected.at(0).node);
OsEng.interactionHandler().setFocusNode(_focusNode); // cant do setFocusNode() since TouchInteraction is not subclass of InteractionMode
OsEng.navigationHandler().setFocusNode(_focusNode); // cant do setFocusNode() since TouchInteraction is not subclass of InteractionMode
// rotate camera to look at new focus, using slerp quat
glm::dvec3 camToFocus = _focusNode->worldPosition() - _camera->positionVec3();
@@ -556,7 +556,7 @@ void TouchInteraction::computeVelocities(const std::vector<TuioCursor>& list, co
void TouchInteraction::step(double dt) {
using namespace glm;
setFocusNode(OsEng.interactionHandler().focusNode()); // since functions cant be called directly (TouchInteraction not a subclass of InteractionMode)
setFocusNode(OsEng.navigationHandler().focusNode()); // since functions cant be called directly (TouchInteraction not a subclass of InteractionMode)
if (_focusNode && _camera) {
// Create variables from current state
dvec3 camPos = _camera->positionVec3();
@@ -776,4 +776,4 @@ void TouchInteraction::setFocusNode(SceneGraphNode* focusNode) {
_focusNode = focusNode;
}
} // openspace namespace
} // openspace namespace

View File

@@ -27,7 +27,7 @@
#include <openspace/engine/openspaceengine.h>
#include <openspace/engine/settingsengine.h>
#include <openspace/engine/wrapper/windowwrapper.h>
#include <openspace/interaction/interactionhandler.h>
#include <openspace/interaction/navigationhandler.h>
#include <openspace/rendering/renderengine.h>
#include <openspace/rendering/screenspacerenderable.h>
@@ -156,4 +156,4 @@ TuioEar::TuioEar() {
_tuioClient = new TuioClient(_oscReceiver);
_tuioClient->addTuioListener(this);
_tuioClient->connect();
}
}

View File

@@ -27,7 +27,7 @@
#include <openspace/engine/openspaceengine.h>
#include <openspace/engine/settingsengine.h>
#include <openspace/engine/wrapper/windowwrapper.h>
#include <openspace/interaction/interactionhandler.h>
#include <openspace/interaction/navigationhandler.h>
#include <openspace/rendering/renderengine.h>
#include <openspace/rendering/screenspacerenderable.h>
@@ -119,8 +119,8 @@ TouchModule::TouchModule()
OsEng.registerModuleCallback(
OpenSpaceEngine::CallbackOption::PreSync,
[&]() {
touch.setCamera(OsEng.interactionHandler().camera());
touch.setFocusNode(OsEng.interactionHandler().focusNode());
touch.setCamera(OsEng.navigationHandler().camera());
touch.setFocusNode(OsEng.navigationHandler().focusNode());
if (hasNewInput() && OsEng.windowWrapper().isMaster()) {
touch.updateStateFromInput(listOfContactPoints, lastProcessed);

View File

@@ -52,18 +52,18 @@ helper.setCommonKeys = function()
openspace.bindKey(
"f",
helper.property.invert('Interaction.horizontalFriction'),
helper.property.invert('NavigationHandler.OrbitalNavigator.horizontalFriction'),
"Toggles the horizontal friction of the camera. If it is disabled, the camera rotates around the focus object indefinitely."
)
openspace.bindKey(
"Shift+f",
helper.property.invert('Interaction.verticalFriction'),
helper.property.invert('NavigationHandler.OrbitalNavigator.verticalFriction'),
"Toggles the vertical friction of the camera. If it is disabled, the camera rises up from or closes in towards the focus object indefinitely."
)
openspace.bindKey(
"Ctrl+f",
helper.property.invert('Interaction.rotationalFriction'),
helper.property.invert('NavigationHandler.OrbitalNavigator.rotationalFriction'),
"Toggles the rotational friction of the camera. If it is disabled, the camera rotates around its own axis indefinitely."
)

View File

@@ -42,10 +42,15 @@ set(OPENSPACE_SOURCE
${OPENSPACE_BASE_DIR}/src/engine/wrapper/sgctwindowwrapper.cpp
${OPENSPACE_BASE_DIR}/src/engine/wrapper/windowwrapper.cpp
${OPENSPACE_BASE_DIR}/src/interaction/controller.cpp
${OPENSPACE_BASE_DIR}/src/interaction/interactionhandler.cpp
${OPENSPACE_BASE_DIR}/src/interaction/interactionmode.cpp
${OPENSPACE_BASE_DIR}/src/interaction/interactionhandler_lua.inl
${OPENSPACE_BASE_DIR}/src/interaction/inputstate.cpp
${OPENSPACE_BASE_DIR}/src/interaction/keybindingmanager.cpp
${OPENSPACE_BASE_DIR}/src/interaction/keybindingmanager_lua.inl
${OPENSPACE_BASE_DIR}/src/interaction/keyframenavigator.cpp
${OPENSPACE_BASE_DIR}/src/interaction/luaconsole.cpp
${OPENSPACE_BASE_DIR}/src/interaction/navigationhandler.cpp
${OPENSPACE_BASE_DIR}/src/interaction/navigationhandler_lua.inl
${OPENSPACE_BASE_DIR}/src/interaction/mousestate.cpp
${OPENSPACE_BASE_DIR}/src/interaction/orbitalnavigator.cpp
${OPENSPACE_BASE_DIR}/src/mission/mission.cpp
${OPENSPACE_BASE_DIR}/src/mission/missionmanager.cpp
${OPENSPACE_BASE_DIR}/src/mission/missionmanager_lua.inl
@@ -181,9 +186,17 @@ set(OPENSPACE_HEADER
${OPENSPACE_BASE_DIR}/include/openspace/engine/wrapper/sgctwindowwrapper.h
${OPENSPACE_BASE_DIR}/include/openspace/engine/wrapper/windowwrapper.h
${OPENSPACE_BASE_DIR}/include/openspace/interaction/controller.h
${OPENSPACE_BASE_DIR}/include/openspace/interaction/interactionhandler.h
${OPENSPACE_BASE_DIR}/include/openspace/interaction/interactionmode.h
${OPENSPACE_BASE_DIR}/include/openspace/interaction/delayedvariable.h
${OPENSPACE_BASE_DIR}/include/openspace/interaction/delayedvariable.inl
${OPENSPACE_BASE_DIR}/include/openspace/interaction/inputstate.h
${OPENSPACE_BASE_DIR}/include/openspace/interaction/interpolator.h
${OPENSPACE_BASE_DIR}/include/openspace/interaction/interpolator.inl
${OPENSPACE_BASE_DIR}/include/openspace/interaction/keybindingmanager.h
${OPENSPACE_BASE_DIR}/include/openspace/interaction/keyframenavigator.h
${OPENSPACE_BASE_DIR}/include/openspace/interaction/luaconsole.h
${OPENSPACE_BASE_DIR}/include/openspace/interaction/mousestate.h
${OPENSPACE_BASE_DIR}/include/openspace/interaction/navigationhandler.h
${OPENSPACE_BASE_DIR}/include/openspace/interaction/orbitalnavigator.h
${OPENSPACE_BASE_DIR}/include/openspace/mission/mission.h
${OPENSPACE_BASE_DIR}/include/openspace/mission/missionmanager.h
${OPENSPACE_BASE_DIR}/include/openspace/network/networkengine.h

View File

@@ -30,7 +30,8 @@
#include <openspace/engine/moduleengine.h>
#include <openspace/engine/openspaceengine.h>
#include <openspace/engine/wrapper/windowwrapper.h>
#include <openspace/interaction/interactionhandler.h>
#include <openspace/interaction/navigationhandler.h>
#include <openspace/interaction/keybindingmanager.h>
#include <openspace/mission/mission.h>
#include <openspace/mission/missionmanager.h>
#include <openspace/rendering/renderable.h>
@@ -69,7 +70,8 @@ void registerCoreClasses(scripting::ScriptEngine& engine) {
engine.addLibrary(RenderEngine::luaLibrary());
engine.addLibrary(Scene::luaLibrary());
engine.addLibrary(Time::luaLibrary());
engine.addLibrary(interaction::InteractionHandler::luaLibrary());
engine.addLibrary(interaction::KeyBindingManager::luaLibrary());
engine.addLibrary(interaction::NavigationHandler::luaLibrary());
engine.addLibrary(ParallelConnection::luaLibrary());
engine.addLibrary(ModuleEngine::luaLibrary());
engine.addLibrary(scripting::ScriptScheduler::luaLibrary());

View File

@@ -36,7 +36,8 @@
#include <openspace/engine/syncengine.h>
#include <openspace/engine/virtualpropertymanager.h>
#include <openspace/engine/wrapper/windowwrapper.h>
#include <openspace/interaction/interactionhandler.h>
#include <openspace/interaction/navigationhandler.h>
#include <openspace/interaction/keybindingmanager.h>
#include <openspace/interaction/luaconsole.h>
#include <openspace/network/networkengine.h>
#include <openspace/network/parallelconnection.h>
@@ -140,7 +141,8 @@ OpenSpaceEngine::OpenSpaceEngine(std::string programName,
, _commandlineParser(new ghoul::cmdparser::CommandlineParser(
programName, ghoul::cmdparser::CommandlineParser::AllowUnknownCommands::Yes
))
, _interactionHandler(new interaction::InteractionHandler)
, _navigationHandler(new interaction::NavigationHandler)
, _keyBindingManager(new interaction::KeyBindingManager)
, _scriptEngine(new scripting::ScriptEngine)
, _scriptScheduler(new scripting::ScriptScheduler)
, _virtualPropertyManager(new VirtualPropertyManager)
@@ -151,10 +153,10 @@ OpenSpaceEngine::OpenSpaceEngine(std::string programName,
, _shutdown({false, 0.f, 0.f})
, _isFirstRenderingFirstFrame(true)
{
_interactionHandler->setPropertyOwner(_globalPropertyNamespace.get());
_navigationHandler->setPropertyOwner(_globalPropertyNamespace.get());
// New property subowners also have to be added to the OnScreenGuiModule callback!
_globalPropertyNamespace->addPropertySubOwner(_interactionHandler.get());
_globalPropertyNamespace->addPropertySubOwner(_navigationHandler.get());
_globalPropertyNamespace->addPropertySubOwner(_settingsEngine.get());
_globalPropertyNamespace->addPropertySubOwner(_renderEngine.get());
_globalPropertyNamespace->addPropertySubOwner(_windowWrapper.get());
@@ -206,6 +208,7 @@ void OpenSpaceEngine::create(int argc, char** argv,
requestClose = false;
LDEBUG("Initialize FileSystem");
ghoul::initialize();
// Initialize the LogManager and add the console log as this will be used every time
@@ -219,7 +222,6 @@ void OpenSpaceEngine::create(int argc, char** argv,
);
LogMgr.addLog(std::make_unique<ConsoleLog>());
#ifdef __APPLE__
ghoul::filesystem::File app(argv[0]);
std::string dirName = app.directoryName();
@@ -515,9 +517,9 @@ void OpenSpaceEngine::initialize() {
_settingsEngine->initialize();
_settingsEngine->setModules(_moduleEngine->modules());
// Initialize the InteractionHandler
_interactionHandler->initialize();
// Initialize the NavigationHandler
_navigationHandler->initialize();
// Load a light and a monospaced font
loadFonts();
@@ -601,7 +603,11 @@ void OpenSpaceEngine::loadScene(const std::string& scenePath) {
_renderEngine->startFading(1, 3.0);
scene->initialize();
_interactionHandler->setCamera(scene->camera());
// Update the scene so that position of objects are set in case they are used in
// post sync scripts
_renderEngine->updateScene();
_navigationHandler->setCamera(scene->camera());
_navigationHandler->setFocusNode(scene->camera()->parent());
try {
runPostInitializationScripts(scenePath);
@@ -612,7 +618,7 @@ void OpenSpaceEngine::loadScene(const std::string& scenePath) {
// Write keyboard documentation.
if (configurationManager().hasKey(ConfigurationManager::KeyKeyboardShortcuts)) {
interactionHandler().writeDocumentation(
keyBindingManager().writeDocumentation(
absPath(configurationManager().value<std::string>(
ConfigurationManager::KeyKeyboardShortcuts
))
@@ -638,7 +644,7 @@ void OpenSpaceEngine::loadScene(const std::string& scenePath) {
void OpenSpaceEngine::deinitialize() {
LTRACE("OpenSpaceEngine::deinitialize(begin)");
_interactionHandler->deinitialize();
_navigationHandler->deinitialize();
_renderEngine->deinitialize();
LTRACE("OpenSpaceEngine::deinitialize(end)");
@@ -1120,10 +1126,8 @@ void OpenSpaceEngine::preSynchronization() {
);
}
_interactionHandler->updateInputStates(dt);
_renderEngine->updateScene();
_interactionHandler->updateCamera(dt);
_navigationHandler->updateCamera(dt);
_renderEngine->camera()->invalidateCache();
_parallelConnection->preSynchronization();
@@ -1158,9 +1162,6 @@ void OpenSpaceEngine::postSynchronizationPreDraw() {
_renderEngine->camera()->invalidateCache();
}
// Step the camera using the current mouse velocities which are synced
//_interactionHandler->updateCamera();
for (const auto& func : _moduleCallbacks.postSyncPreDraw) {
func();
}
@@ -1250,7 +1251,8 @@ void OpenSpaceEngine::keyboardCallback(Key key, KeyModifier mod, KeyAction actio
return;
}
_interactionHandler->keyboardCallback(key, mod, action);
_navigationHandler->keyboardCallback(key, mod, action);
_keyBindingManager->keyboardCallback(key, mod, action);
}
void OpenSpaceEngine::charCallback(unsigned int codepoint, KeyModifier modifier) {
@@ -1272,7 +1274,7 @@ void OpenSpaceEngine::mouseButtonCallback(MouseButton button, MouseAction action
}
}
_interactionHandler->mouseButtonCallback(button, action);
_navigationHandler->mouseButtonCallback(button, action);
}
void OpenSpaceEngine::mousePositionCallback(double x, double y) {
@@ -1280,7 +1282,7 @@ void OpenSpaceEngine::mousePositionCallback(double x, double y) {
func(x, y);
}
_interactionHandler->mousePositionCallback(x, y);
_navigationHandler->mousePositionCallback(x, y);
}
void OpenSpaceEngine::mouseScrollWheelCallback(double posX, double posY) {
@@ -1291,7 +1293,7 @@ void OpenSpaceEngine::mouseScrollWheelCallback(double posX, double posY) {
}
}
_interactionHandler->mouseScrollWheelCallback(posY);
_navigationHandler->mouseScrollWheelCallback(posY);
}
void OpenSpaceEngine::encode() {
@@ -1501,9 +1503,14 @@ ghoul::fontrendering::FontManager& OpenSpaceEngine::fontManager() {
return *_fontManager;
}
interaction::InteractionHandler& OpenSpaceEngine::interactionHandler() {
ghoul_assert(_interactionHandler, "InteractionHandler must not be nullptr");
return *_interactionHandler;
interaction::NavigationHandler& OpenSpaceEngine::navigationHandler() {
ghoul_assert(_navigationHandler, "NavigationHandler must not be nullptr");
return *_navigationHandler;
}
interaction::KeyBindingManager& OpenSpaceEngine::keyBindingManager() {
ghoul_assert(_keyBindingManager, "KeyBindingManager must not be nullptr");
return *_keyBindingManager;
}
properties::PropertyOwner& OpenSpaceEngine::globalPropertyOwner() {

View File

@@ -24,12 +24,12 @@
#include <openspace/interaction/controller.h>
#include <openspace/interaction/interactionhandler.h>
#include <openspace/interaction/navigationhandler.h>
namespace openspace {
namespace interaction {
void Controller::setHandler(InteractionHandler* handler)
void Controller::setHandler(NavigationHandler* handler)
{
_handler = handler;
}

View File

@@ -0,0 +1,97 @@
/*****************************************************************************************
* *
* OpenSpace *
* *
* Copyright (c) 2014-2017 *
* *
* Permission is hereby granted, free of charge, to any person obtaining a copy of this *
* software and associated documentation files (the "Software"), to deal in the Software *
* without restriction, including without limitation the rights to use, copy, modify, *
* merge, publish, distribute, sublicense, and/or sell copies of the Software, and to *
* permit persons to whom the Software is furnished to do so, subject to the following *
* conditions: *
* *
* The above copyright notice and this permission notice shall be included in all copies *
* or substantial portions of the Software. *
* *
* 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. *
****************************************************************************************/
#include <openspace/interaction/inputstate.h>
#include <ghoul/logging/logmanager.h>
#include <algorithm>
namespace openspace {
namespace interaction {
void InputState::keyboardCallback(Key key, KeyModifier modifier, KeyAction action) {
if (action == KeyAction::Press) {
_keysDown.emplace_back(key, modifier);
}
else if (action == KeyAction::Release) {
// Remove all key pressings for 'key'
_keysDown.remove_if([key](std::pair<Key, KeyModifier> keyModPair)
{ return keyModPair.first == key; });
}
}
void InputState::mouseButtonCallback(MouseButton button, MouseAction action) {
if (action == MouseAction::Press) {
_mouseButtonsDown.push_back(button);
}
else if (action == MouseAction::Release) {
// Remove all key pressings for 'button'
_mouseButtonsDown.remove_if([button](MouseButton buttonInList)
{ return button == buttonInList; });
}
}
void InputState::mousePositionCallback(double mouseX, double mouseY) {
_mousePosition = glm::dvec2(mouseX, mouseY);
}
void InputState::mouseScrollWheelCallback(double mouseScrollDelta) {
_mouseScrollDelta = mouseScrollDelta;
}
const std::list<std::pair<Key, KeyModifier> >& InputState::getPressedKeys() const {
return _keysDown;
}
const std::list<MouseButton>& InputState::getPressedMouseButtons() const {
return _mouseButtonsDown;
}
glm::dvec2 InputState::getMousePosition() const {
return _mousePosition;
}
double InputState::getMouseScrollDelta() const {
return _mouseScrollDelta;
}
bool InputState::isKeyPressed(std::pair<Key, KeyModifier> keyModPair) const {
return std::find(_keysDown.begin(), _keysDown.end(), keyModPair) != _keysDown.end();
}
bool InputState::isKeyPressed(Key key) const {
return std::find_if(_keysDown.begin(), _keysDown.end(),
[key](const std::pair<Key, KeyModifier>& keyModPair) {
return key == keyModPair.first;
}) != _keysDown.end();
}
bool InputState::isMouseButtonPressed(MouseButton mouseButton) const {
return std::find(_mouseButtonsDown.begin(), _mouseButtonsDown.end(),
mouseButton) != _mouseButtonsDown.end();
}
} // namespace interaction
} // namespace openspace

View File

@@ -1,563 +0,0 @@
/*****************************************************************************************
* *
* OpenSpace *
* *
* Copyright (c) 2014-2017 *
* *
* Permission is hereby granted, free of charge, to any person obtaining a copy of this *
* software and associated documentation files (the "Software"), to deal in the Software *
* without restriction, including without limitation the rights to use, copy, modify, *
* merge, publish, distribute, sublicense, and/or sell copies of the Software, and to *
* permit persons to whom the Software is furnished to do so, subject to the following *
* conditions: *
* *
* The above copyright notice and this permission notice shall be included in all copies *
* or substantial portions of the Software. *
* *
* 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. *
****************************************************************************************/
#include <openspace/interaction/interactionhandler.h>
#include <openspace/network/parallelconnection.h>
#include <openspace/openspace.h>
#include <openspace/engine/openspaceengine.h>
#include <openspace/interaction/interactionhandler.h>
#include <openspace/interaction/interactionmode.h>
#include <openspace/query/query.h>
#include <openspace/rendering/renderengine.h>
#include <openspace/scene/scenegraphnode.h>
#include <openspace/util/time.h>
#include <openspace/util/keys.h>
#include <ghoul/filesystem/filesystem.h>
#include <ghoul/logging/logmanager.h>
#include <ghoul/misc/interpolator.h>
#include <glm/gtx/quaternion.hpp>
#include <glm/gtx/string_cast.hpp>
#ifdef OPENSPACE_MODULE_GLOBEBROWSING_ENABLED
#include <modules/globebrowsing/geometry/geodetic2.h>
#endif
#include <ghoul/glm.h>
#include <fstream>
namespace {
const char* _loggerCat = "InteractionHandler";
const char* KeyFocus = "Focus";
const char* KeyPosition = "Position";
const char* KeyRotation = "Rotation";
const char* MainTemplateFilename = "${OPENSPACE_DATA}/web/keybindings/main.hbs";
const char* KeybindingTemplateFilename = "${OPENSPACE_DATA}/web/keybindings/keybinding.hbs";
const char* JsFilename = "${OPENSPACE_DATA}/web/keybindings/script.js";
const int IdOrbitalInteractionMode = 0;
const char* KeyOrbitalInteractionMode = "Orbital";
const int IdGlobeBrowsingInteractionMode = 1;
const char* KeyGlobeBrowsingInteractionMode = "GlobeBrowsing";
const int IdKeyframeInteractionMode = 2;
const char* KeyKeyframeInteractionMode = "Keyframe";
} // namespace
#include "interactionhandler_lua.inl"
namespace openspace {
namespace interaction {
// InteractionHandler
InteractionHandler::InteractionHandler()
: properties::PropertyOwner("Interaction")
, DocumentationGenerator(
"Documentation",
"keybindings",
{
{ "keybindingTemplate", KeybindingTemplateFilename },
{ "mainTemplate", MainTemplateFilename }
},
JsFilename
)
, _origin("origin", "Origin", "")
, _rotationalFriction("rotationalFriction", "Rotational Friction", true)
, _horizontalFriction("horizontalFriction", "Horizontal Friction", true)
, _verticalFriction("verticalFriction", "Vertical Friction", true)
, _sensitivity("sensitivity", "Sensitivity", 0.5f, 0.001f, 1.f)
, _rapidness("rapidness", "Rapidness", 1.f, 0.1f, 60.f)
, _interactionModeOption(
"interactionMode",
"Interaction Mode",
properties::OptionProperty::DisplayType::Dropdown
)
{
_origin.onChange([this]() {
SceneGraphNode* node = sceneGraphNode(_origin.value());
if (!node) {
LWARNING("Could not find a node in scenegraph called '" << _origin.value() << "'");
return;
}
setFocusNode(node);
resetCameraDirection();
});
// Create the interactionModes
_inputState = std::make_unique<InputState>();
// Inject the same mouse states to both orbital and global interaction mode
_mouseStates = std::make_unique<OrbitalInteractionMode::MouseStates>(_sensitivity * pow(10.0,-4), 1);
_orbitalInteractionMode = std::make_unique<OrbitalInteractionMode>(_mouseStates);
_globeBrowsingInteractionMode = std::make_unique<GlobeBrowsingInteractionMode>(_mouseStates);
_keyframeInteractionMode = std::make_unique<KeyframeInteractionMode>();
_interactionModeOption.addOption(IdOrbitalInteractionMode, KeyOrbitalInteractionMode);
_interactionModeOption.addOption(IdGlobeBrowsingInteractionMode, KeyGlobeBrowsingInteractionMode);
_interactionModeOption.addOption(IdKeyframeInteractionMode, KeyKeyframeInteractionMode);
// Set the interactionMode
_currentInteractionMode = _orbitalInteractionMode.get();
// Define lambda functions for changed properties
_rotationalFriction.onChange([&]() {
_mouseStates->setRotationalFriction(_rotationalFriction);
});
_horizontalFriction.onChange([&]() {
_mouseStates->setHorizontalFriction(_horizontalFriction);
});
_verticalFriction.onChange([&]() {
_mouseStates->setVerticalFriction(_verticalFriction);
});
_sensitivity.onChange([&]() {
_mouseStates->setSensitivity(_sensitivity * pow(10.0,-4));
});
_rapidness.onChange([&]() {
_mouseStates->setVelocityScaleFactor(_rapidness);
});
_interactionModeOption.onChange([this]() {
switch(_interactionModeOption.value()) {
case IdGlobeBrowsingInteractionMode:
setInteractionMode(_globeBrowsingInteractionMode.get());
break;
case IdKeyframeInteractionMode:
setInteractionMode(_keyframeInteractionMode.get());
break;
case IdOrbitalInteractionMode:
default:
setInteractionMode(_orbitalInteractionMode.get());
break;
}
});
// Add the properties
addProperty(_origin);
addProperty(_interactionModeOption);
addProperty(_rotationalFriction);
addProperty(_horizontalFriction);
addProperty(_verticalFriction);
addProperty(_sensitivity);
addProperty(_rapidness);
}
InteractionHandler::~InteractionHandler() {
}
void InteractionHandler::initialize() {
OsEng.parallelConnection().connectionEvent()->subscribe("interactionHandler", "statusChanged", [this]() {
if (OsEng.parallelConnection().status() == ParallelConnection::Status::ClientWithHost) {
setInteractionMode(_keyframeInteractionMode.get());
} else if (_currentInteractionMode == _keyframeInteractionMode.get()) {
setInteractionMode(_orbitalInteractionMode.get());
}
});
}
void InteractionHandler::deinitialize() {
OsEng.parallelConnection().connectionEvent()->unsubscribe("interactionHandler");
}
void InteractionHandler::setFocusNode(SceneGraphNode* node) {
_currentInteractionMode->setFocusNode(node);
}
void InteractionHandler::setCamera(Camera* camera) {
_camera = camera;
setFocusNode(_camera->parent());
}
void InteractionHandler::resetCameraDirection() {
LINFO("Setting camera direction to point at focus node.");
_currentInteractionMode->rotateToFocusNodeInterpolator().start();
}
void InteractionHandler::setInteractionMode(InteractionMode* interactionMode) {
// Focus node is passed over from the previous interaction mode
SceneGraphNode* focusNode = _currentInteractionMode->focusNode();
// Set the interaction mode
_currentInteractionMode = interactionMode;
// Update the focusnode for the new interaction mode
_currentInteractionMode->setFocusNode(focusNode);
}
InteractionMode * InteractionHandler::interactionMode() {
return _currentInteractionMode;
}
void InteractionHandler::goToChunk(int x, int y, int level) {
if (_currentInteractionMode == _globeBrowsingInteractionMode.get()) {
#ifdef OPENSPACE_MODULE_GLOBEBROWSING_ENABLED
_globeBrowsingInteractionMode->goToChunk(*_camera, globebrowsing::TileIndex(x,y,level), glm::vec2(0.5,0.5), true);
#endif
} else {
LWARNING("Interaction mode must be set to 'GlobeBrowsing'");
}
}
void InteractionHandler::goToGeo(double latitude, double longitude) {
if (_currentInteractionMode == _globeBrowsingInteractionMode.get()) {
#ifdef OPENSPACE_MODULE_GLOBEBROWSING_ENABLED
_globeBrowsingInteractionMode->goToGeodetic2(
*_camera,
globebrowsing::Geodetic2(latitude, longitude) / 180 * glm::pi<double>(), true
);
#endif
} else {
LWARNING("Interaction mode must be set to 'GlobeBrowsing'");
}
}
void InteractionHandler::lockControls() {
}
void InteractionHandler::unlockControls() {
}
void InteractionHandler::updateInputStates(double timeSinceLastUpdate) {
ghoul_assert(_inputState != nullptr, "InputState cannot be null!");
ghoul_assert(_camera != nullptr, "Camera cannot be null!");
_currentInteractionMode->updateMouseStatesFromInput(*_inputState, timeSinceLastUpdate);
}
void InteractionHandler::updateCamera(double deltaTime) {
ghoul_assert(_inputState != nullptr, "InputState cannot be null!");
ghoul_assert(_camera != nullptr, "Camera cannot be null!");
if (_cameraUpdatedFromScript) {
_cameraUpdatedFromScript = false;
}
else {
if (_camera && focusNode()) {
_currentInteractionMode->updateCameraStateFromMouseStates(*_camera, deltaTime);
_camera->setFocusPositionVec3(focusNode()->worldPosition());
}
}
}
SceneGraphNode* InteractionHandler::focusNode() const {
return _currentInteractionMode->focusNode();
}
glm::dvec3 InteractionHandler::focusNodeToCameraVector() const {
return _camera->positionVec3() - focusNode()->worldPosition();
}
glm::quat InteractionHandler::focusNodeToCameraRotation() const {
glm::dmat4 invWorldRotation = glm::inverse(focusNode()->worldRotationMatrix());
return glm::quat(invWorldRotation) * glm::quat(_camera->rotationQuaternion());
}
Camera* InteractionHandler::camera() const {
return _camera;
}
const InputState& InteractionHandler::inputState() const {
return *_inputState;
}
void InteractionHandler::mouseButtonCallback(MouseButton button, MouseAction action) {
_inputState->mouseButtonCallback(button, action);
}
void InteractionHandler::mousePositionCallback(double x, double y) {
_inputState->mousePositionCallback(x, y);
}
void InteractionHandler::mouseScrollWheelCallback(double pos) {
_inputState->mouseScrollWheelCallback(pos);
}
void InteractionHandler::keyboardCallback(Key key, KeyModifier modifier, KeyAction action) {
_inputState->keyboardCallback(key, modifier, action);
if (action == KeyAction::Press || action == KeyAction::Repeat) {
// iterate over key bindings
auto ret = _keyLua.equal_range({ key, modifier });
for (auto it = ret.first; it != ret.second; ++it) {
auto remote = it->second.synchronization ?
scripting::ScriptEngine::RemoteScripting::Yes :
scripting::ScriptEngine::RemoteScripting::No;
OsEng.scriptEngine().queueScript(it->second.command, remote);
}
}
}
void InteractionHandler::setCameraStateFromDictionary(const ghoul::Dictionary& cameraDict) {
bool readSuccessful = true;
std::string focus;
glm::dvec3 cameraPosition;
glm::dvec4 cameraRotation; // Need to read the quaternion as a vector first.
readSuccessful &= cameraDict.getValue(KeyFocus, focus);
readSuccessful &= cameraDict.getValue(KeyPosition, cameraPosition);
readSuccessful &= cameraDict.getValue(KeyRotation, cameraRotation);
if (!readSuccessful) {
throw ghoul::RuntimeError(
"Position, Rotation and Focus need to be defined for camera dictionary.");
}
SceneGraphNode* node = sceneGraphNode(focus);
if (!node) {
throw ghoul::RuntimeError(
"Could not find a node in scenegraph called '" + focus + "'");
}
// Set state
setFocusNode(node);
_camera->setPositionVec3(cameraPosition);
_camera->setRotation(glm::dquat(
cameraRotation.x, cameraRotation.y, cameraRotation.z, cameraRotation.w));
}
ghoul::Dictionary InteractionHandler::getCameraStateDictionary() {
glm::dvec3 cameraPosition;
glm::dquat quat;
glm::dvec4 cameraRotation;
cameraPosition = _camera->positionVec3();
quat = _camera->rotationQuaternion();
cameraRotation = glm::dvec4(quat.w, quat.x, quat.y, quat.z);
ghoul::Dictionary cameraDict;
cameraDict.setValue(KeyPosition, cameraPosition);
cameraDict.setValue(KeyRotation, cameraRotation);
cameraDict.setValue(KeyFocus, focusNode()->name());
return cameraDict;
}
void InteractionHandler::saveCameraStateToFile(const std::string& filepath) {
if (!filepath.empty()) {
auto fullpath = absPath(filepath);
LINFO("Saving camera position: " << filepath);
ghoul::Dictionary cameraDict = getCameraStateDictionary();
// TODO : Should get the camera state as a dictionary and save the dictionary to
// a file in form of a lua state and not use ofstreams here.
std::ofstream ofs(fullpath.c_str());
glm::dvec3 p = _camera->positionVec3();
glm::dquat q = _camera->rotationQuaternion();
ofs << "return {" << std::endl;
ofs << " " << KeyFocus << " = " << "\"" << focusNode()->name() << "\"" << "," << std::endl;
ofs << " " << KeyPosition << " = {"
<< std::to_string(p.x) << ", "
<< std::to_string(p.y) << ", "
<< std::to_string(p.z) << "}," << std::endl;
ofs << " " << KeyRotation << " = {"
<< std::to_string(q.w) << ", "
<< std::to_string(q.x) << ", "
<< std::to_string(q.y) << ", "
<< std::to_string(q.z) << "}," << std::endl;
ofs << "}"<< std::endl;
ofs.close();
}
}
void InteractionHandler::restoreCameraStateFromFile(const std::string& filepath) {
LINFO("Reading camera state from file: " << filepath);
if (!FileSys.fileExists(filepath))
throw ghoul::FileNotFoundError(filepath, "CameraFilePath");
ghoul::Dictionary cameraDict;
try {
ghoul::lua::loadDictionaryFromFile(filepath, cameraDict);
setCameraStateFromDictionary(cameraDict);
_cameraUpdatedFromScript = true;
}
catch (ghoul::RuntimeError& e) {
LWARNING("Unable to set camera position");
LWARNING(e.message);
}
}
void InteractionHandler::resetKeyBindings() {
_keyLua.clear();
}
void InteractionHandler::bindKeyLocal(Key key, KeyModifier modifier,
std::string luaCommand, std::string documentation)
{
_keyLua.insert({
{ key, modifier },
{ std::move(luaCommand), Synchronized::No, std::move(documentation) }
});
}
void InteractionHandler::bindKey(Key key, KeyModifier modifier,
std::string luaCommand, std::string documentation)
{
_keyLua.insert({
{ key, modifier },
{ std::move(luaCommand), Synchronized::Yes, std::move(documentation) }
});
}
std::string InteractionHandler::generateJson() const {
std::stringstream json;
json << "[";
bool first = true;
for (const auto& p : _keyLua) {
if (!first) {
json << ",";
}
first = false;
json << "{";
json << "\"key\": \"" << std::to_string(p.first) << "\",";
json << "\"script\": \"" << p.second.command << "\",";
json << "\"remoteScripting\": " << (p.second.synchronization ? "true," : "false,");
json << "\"documentation\": \"" << p.second.documentation << "\"";
json << "}";
}
json << "]";
std::string jsonString = "";
for (const char& c : json.str()) {
if (c == '\'') {
jsonString += "\\'";
} else {
jsonString += c;
}
}
return jsonString;
}
scripting::LuaLibrary InteractionHandler::luaLibrary() {
return{
"",
{
{
"clearKeys",
&luascriptfunctions::clearKeys,
"",
"Clear all key bindings"
},
{
"bindKey",
&luascriptfunctions::bindKey,
"string, string [,string]",
"Binds a key by name to a lua string command to execute both locally "
"and to broadcast to clients if this is the host of a parallel session. "
"The first argument is the key, the second argument is the Lua command "
"that is to be executed, and the optional third argument is a human "
"readable description of the command for documentation purposes."
},
{
"bindKeyLocal",
&luascriptfunctions::bindKeyLocal,
"string, string [,string]",
"Binds a key by name to a lua string command to execute only locally. "
"The first argument is the key, the second argument is the Lua command "
"that is to be executed, and the optional third argument is a human "
"readable description of the command for documentation purposes."
},
{
"saveCameraStateToFile",
&luascriptfunctions::saveCameraStateToFile,
"string",
"Save the current camera state to file"
},
{
"restoreCameraStateFromFile",
&luascriptfunctions::restoreCameraStateFromFile,
"string",
"Restore the camera state from file"
},
{
"resetCameraDirection",
&luascriptfunctions::resetCameraDirection,
"void",
"Reset the camera direction to point at the focus node"
},
{
"goToChunk",
&luascriptfunctions::goToChunk,
"void",
"Go to chunk with given index x, y, level"
},
{
"goToGeo",
&luascriptfunctions::goToGeo,
"void",
"Go to geographic coordinates latitude and longitude"
},
}
};
}
void InteractionHandler::addKeyframe(double timestamp, KeyframeInteractionMode::CameraPose pose) {
if (!_keyframeInteractionMode) {
return;
}
_keyframeInteractionMode->timeline().addKeyframe(timestamp, pose);
}
void InteractionHandler::removeKeyframesAfter(double timestamp) {
if (!_keyframeInteractionMode) {
return;
}
_keyframeInteractionMode->timeline().removeKeyframesAfter(timestamp);
}
void InteractionHandler::clearKeyframes() {
if (!_keyframeInteractionMode) {
return;
}
_keyframeInteractionMode->timeline().clearKeyframes();
}
size_t InteractionHandler::nKeyframes() const {
if (!_keyframeInteractionMode) {
return 0;
}
return _keyframeInteractionMode->timeline().keyframes().size();
}
} // namespace interaction
} // namespace openspace

View File

@@ -1,800 +0,0 @@
/*****************************************************************************************
* *
* OpenSpace *
* *
* Copyright (c) 2014-2017 *
* *
* Permission is hereby granted, free of charge, to any person obtaining a copy of this *
* software and associated documentation files (the "Software"), to deal in the Software *
* without restriction, including without limitation the rights to use, copy, modify, *
* merge, publish, distribute, sublicense, and/or sell copies of the Software, and to *
* permit persons to whom the Software is furnished to do so, subject to the following *
* conditions: *
* *
* The above copyright notice and this permission notice shall be included in all copies *
* or substantial portions of the Software. *
* *
* 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. *
****************************************************************************************/
#include <openspace/interaction/interactionmode.h>
#include <openspace/interaction/interactionhandler.h>
#include <openspace/engine/openspaceengine.h>
#include <openspace/query/query.h>
#include <openspace/rendering/renderengine.h>
#include <openspace/scene/scenegraphnode.h>
#include <openspace/scene/scene.h>
#include <openspace/util/time.h>
#include <openspace/util/keys.h>
#include <ghoul/logging/logmanager.h>
#include <glm/gtx/quaternion.hpp>
#ifdef OPENSPACE_MODULE_GLOBEBROWSING_ENABLED
#include <modules/globebrowsing/globes/renderableglobe.h>
#include <modules/globebrowsing/globes/chunkedlodglobe.h>
#include <modules/globebrowsing/geometry/geodetic2.h>
#endif
#include <glm/gtx/string_cast.hpp>
namespace {
const std::string _loggerCat = "InteractionMode";
}
namespace openspace {
namespace interaction {
// InputState
InputState::InputState() {
}
InputState::~InputState() {
}
void InputState::keyboardCallback(Key key, KeyModifier modifier, KeyAction action) {
if (action == KeyAction::Press) {
_keysDown.push_back(std::pair<Key, KeyModifier>(key, modifier));
}
else if (action == KeyAction::Release) {
// Remove all key pressings for 'key'
_keysDown.remove_if([key](std::pair<Key, KeyModifier> keyModPair)
{ return keyModPair.first == key; });
}
}
void InputState::mouseButtonCallback(MouseButton button, MouseAction action) {
if (action == MouseAction::Press) {
_mouseButtonsDown.push_back(button);
}
else if (action == MouseAction::Release) {
// Remove all key pressings for 'button'
_mouseButtonsDown.remove_if([button](MouseButton buttonInList)
{ return button == buttonInList; });
}
}
void InputState::mousePositionCallback(double mouseX, double mouseY) {
_mousePosition = glm::dvec2(mouseX, mouseY);
}
void InputState::mouseScrollWheelCallback(double mouseScrollDelta) {
_mouseScrollDelta = mouseScrollDelta;
}
const std::list<std::pair<Key, KeyModifier> >& InputState::getPressedKeys() const {
return _keysDown;
}
const std::list<MouseButton>& InputState::getPressedMouseButtons() const {
return _mouseButtonsDown;
}
glm::dvec2 InputState::getMousePosition() const {
return _mousePosition;
}
double InputState::getMouseScrollDelta() const {
return _mouseScrollDelta;
}
bool InputState::isKeyPressed(std::pair<Key, KeyModifier> keyModPair) const {
for (auto it = _keysDown.begin(); it != _keysDown.end(); it++) {
if (*it == keyModPair) {
return true;
}
}
return false;
}
bool InputState::isKeyPressed(Key key) const {
for (auto it = _keysDown.begin(); it != _keysDown.end(); it++) {
if ((*it).first == key) {
return true;
}
}
return false;
}
bool InputState::isMouseButtonPressed(MouseButton mouseButton) const {
for (auto it = _mouseButtonsDown.begin(); it != _mouseButtonsDown.end(); it++) {
if (*it == mouseButton) {
return true;
}
}
return false;
}
InteractionMode::InteractionMode()
: _rotateToFocusNodeInterpolator(Interpolator<double>([](double t){
return pow(t, 2);
})) {
}
InteractionMode::~InteractionMode() {
}
void InteractionMode::setFocusNode(SceneGraphNode* focusNode) {
_focusNode = focusNode;
if (_focusNode != nullptr) {
_previousFocusNodePosition = _focusNode->worldPosition();
_previousFocusNodeRotation = glm::quat_cast(_focusNode->worldRotationMatrix());
}
}
SceneGraphNode* InteractionMode::focusNode() {
return _focusNode;
}
Interpolator<double>& InteractionMode::rotateToFocusNodeInterpolator() {
return _rotateToFocusNodeInterpolator;
};
// KeyframeInteractionMode
KeyframeInteractionMode::KeyframeInteractionMode()
{
}
KeyframeInteractionMode::~KeyframeInteractionMode() {
}
void KeyframeInteractionMode::updateMouseStatesFromInput(const InputState&, double) {
// Do nothing.
}
void KeyframeInteractionMode::updateCameraStateFromMouseStates(Camera& camera, double) {
double now = OsEng.runTime();
if (_cameraPoseTimeline.nKeyframes() == 0) {
return;
}
const Keyframe<CameraPose>* nextKeyframe = _cameraPoseTimeline.firstKeyframeAfter(now);
const Keyframe<CameraPose>* prevKeyframe = _cameraPoseTimeline.lastKeyframeBefore(now);
double nextTime = 0;
double prevTime = 0;
double t = 0;
if (nextKeyframe) {
nextTime = nextKeyframe->timestamp;
} else {
return;
}
if (prevKeyframe) {
prevTime = prevKeyframe->timestamp;
t = (now - prevTime) / (nextTime - prevTime);
} else {
// If there is no keyframe before: Only use the next keyframe.
prevTime = nextTime;
prevKeyframe = nextKeyframe;
t = 1;
}
_cameraPoseTimeline.removeKeyframesBefore(prevTime);
const CameraPose& prevPose = prevKeyframe->data;
const CameraPose& nextPose = nextKeyframe->data;
Scene* scene = camera.parent()->scene();
SceneGraphNode* prevFocusNode = scene->sceneGraphNode(prevPose.focusNode);
SceneGraphNode* nextFocusNode = scene->sceneGraphNode(nextPose.focusNode);
if (!prevFocusNode || !nextFocusNode) {
return;
}
glm::dvec3 prevKeyframeCameraPosition = prevPose.position;
glm::dvec3 nextKeyframeCameraPosition = nextPose.position;
glm::dquat prevKeyframeCameraRotation = prevPose.rotation;
glm::dquat nextKeyframeCameraRotation = nextPose.rotation;
// Transform position and rotation based on focus node rotation (if following rotation)
if (prevPose.followFocusNodeRotation) {
prevKeyframeCameraRotation = prevFocusNode->worldRotationMatrix() * glm::dmat3(glm::dquat(prevPose.rotation));
prevKeyframeCameraPosition = prevFocusNode->worldRotationMatrix() * prevPose.position;
}
if (nextPose.followFocusNodeRotation) {
nextKeyframeCameraRotation = nextFocusNode->worldRotationMatrix() * glm::dmat3(glm::dquat(nextPose.rotation));
nextKeyframeCameraPosition = nextFocusNode->worldRotationMatrix() * nextPose.position;
}
// Transform position based on focus node position
prevKeyframeCameraPosition += prevFocusNode->worldPosition();
nextKeyframeCameraPosition += nextFocusNode->worldPosition();
// Linear interpolation
camera.setPositionVec3(prevKeyframeCameraPosition * (1 - t) + nextKeyframeCameraPosition * t);
camera.setRotation(glm::slerp(prevKeyframeCameraRotation, nextKeyframeCameraRotation, t));
}
bool KeyframeInteractionMode::followingNodeRotation() const {
return false;
}
Timeline<KeyframeInteractionMode::CameraPose>& KeyframeInteractionMode::timeline() {
return _cameraPoseTimeline;
}
// OrbitalInteractionMode
OrbitalInteractionMode::MouseStates::MouseStates(double sensitivity, double velocityScaleFactor)
: _sensitivity(sensitivity)
, _globalRotationMouseState(velocityScaleFactor)
, _localRotationMouseState(velocityScaleFactor)
, _truckMovementMouseState(velocityScaleFactor)
, _localRollMouseState(velocityScaleFactor)
, _globalRollMouseState(velocityScaleFactor)
{}
void OrbitalInteractionMode::MouseStates::updateMouseStatesFromInput(const InputState& inputState, double deltaTime) {
#ifdef OPENSPACE_MODULE_TOUCH_ENABLED
ghoul::any t = property("Global Properties.Touch.TouchInteraction.TouchEvents")->get();
if (!*(ghoul::any_cast<bool>(&t))) {
#endif // OPENSPACE_MODULE_GLOBEBROWSING_ENABLED
glm::dvec2 mousePosition = inputState.getMousePosition();
bool button1Pressed = inputState.isMouseButtonPressed(MouseButton::Button1);
bool button2Pressed = inputState.isMouseButtonPressed(MouseButton::Button2);
bool button3Pressed = inputState.isMouseButtonPressed(MouseButton::Button3);
bool keyCtrlPressed = inputState.isKeyPressed(Key::LeftControl);
bool keyShiftPressed = inputState.isKeyPressed(Key::LeftShift);
// Update the mouse states
if (button1Pressed && !keyShiftPressed) {
if (keyCtrlPressed) {
glm::dvec2 mousePositionDelta =
_localRotationMouseState.previousPosition - mousePosition;
_localRotationMouseState.velocity.set(mousePositionDelta * _sensitivity, deltaTime);
_globalRotationMouseState.previousPosition = mousePosition;
_globalRotationMouseState.velocity.decelerate(deltaTime);
}
else {
glm::dvec2 mousePositionDelta =
_globalRotationMouseState.previousPosition - mousePosition;
_globalRotationMouseState.velocity.set(mousePositionDelta * _sensitivity, deltaTime);
_localRotationMouseState.previousPosition = mousePosition;
_localRotationMouseState.velocity.decelerate(deltaTime);
}
}
else { // !button1Pressed
_localRotationMouseState.previousPosition = mousePosition;
_localRotationMouseState.velocity.decelerate(deltaTime);
_globalRotationMouseState.previousPosition = mousePosition;
_globalRotationMouseState.velocity.decelerate(deltaTime);
}
if (button2Pressed) {
glm::dvec2 mousePositionDelta =
_truckMovementMouseState.previousPosition - mousePosition;
_truckMovementMouseState.velocity.set(mousePositionDelta * _sensitivity, deltaTime);
}
else { // !button2Pressed
_truckMovementMouseState.previousPosition = mousePosition;
_truckMovementMouseState.velocity.decelerate(deltaTime);
}
if (button3Pressed || (keyShiftPressed && button1Pressed)) {
if (keyCtrlPressed) {
glm::dvec2 mousePositionDelta =
_localRollMouseState.previousPosition - mousePosition;
_localRollMouseState.velocity.set(mousePositionDelta * _sensitivity, deltaTime);
_globalRollMouseState.previousPosition = mousePosition;
_globalRollMouseState.velocity.decelerate(deltaTime);
}
else {
glm::dvec2 mousePositionDelta =
_globalRollMouseState.previousPosition - mousePosition;
_globalRollMouseState.velocity.set(mousePositionDelta * _sensitivity, deltaTime);
_localRollMouseState.previousPosition = mousePosition;
_localRollMouseState.velocity.decelerate(deltaTime);
}
}
else { // !button3Pressed
_globalRollMouseState.previousPosition = mousePosition;
_globalRollMouseState.velocity.decelerate(deltaTime);
_localRollMouseState.previousPosition = mousePosition;
_localRollMouseState.velocity.decelerate(deltaTime);
}
#ifdef OPENSPACE_MODULE_TOUCH_ENABLED
}
#endif // OPENSPACE_MODULE_TOUCH_ENABLED
}
void OrbitalInteractionMode::MouseStates::setRotationalFriction(double friction) {
_localRotationMouseState.setFriction(friction);
_localRollMouseState.setFriction(friction);
_globalRollMouseState.setFriction(friction);
}
void OrbitalInteractionMode::MouseStates::setHorizontalFriction(double friction) {
_globalRotationMouseState.setFriction(friction);
}
void OrbitalInteractionMode::MouseStates::setVerticalFriction(double friction) {
_truckMovementMouseState.setFriction(friction);
}
void OrbitalInteractionMode::MouseStates::setSensitivity(double sensitivity) {
_sensitivity = sensitivity;
}
void OrbitalInteractionMode::MouseStates::setVelocityScaleFactor(double scaleFactor) {
_globalRotationMouseState.setVelocityScaleFactor(scaleFactor);
_localRotationMouseState.setVelocityScaleFactor(scaleFactor);
_truckMovementMouseState.setVelocityScaleFactor(scaleFactor);
_localRollMouseState.setVelocityScaleFactor(scaleFactor);
_globalRollMouseState.setVelocityScaleFactor(scaleFactor);
}
glm::dvec2 OrbitalInteractionMode::MouseStates::synchedGlobalRotationMouseVelocity() {
return _globalRotationMouseState.velocity.get();
}
glm::dvec2 OrbitalInteractionMode::MouseStates::synchedLocalRotationMouseVelocity() {
return _localRotationMouseState.velocity.get();
}
glm::dvec2 OrbitalInteractionMode::MouseStates::synchedTruckMovementMouseVelocity() {
return _truckMovementMouseState.velocity.get();
}
glm::dvec2 OrbitalInteractionMode::MouseStates::synchedLocalRollMouseVelocity() {
return _localRollMouseState.velocity.get();
}
glm::dvec2 OrbitalInteractionMode::MouseStates::synchedGlobalRollMouseVelocity() {
return _globalRollMouseState.velocity.get();
}
OrbitalInteractionMode::OrbitalInteractionMode(
std::shared_ptr<MouseStates> mouseStates)
: InteractionMode()
, _mouseStates(mouseStates) {
}
OrbitalInteractionMode::~OrbitalInteractionMode() {
}
void OrbitalInteractionMode::updateCameraStateFromMouseStates(Camera& camera, double deltaTime) {
// Update synched data
using namespace glm;
if (_focusNode) {
// Read the current state of the camera and focus node
dvec3 camPos = camera.positionVec3();
// Follow focus nodes movement
dvec3 centerPos = _focusNode->worldPosition();
dvec3 focusNodeDiff = centerPos - _previousFocusNodePosition;
_previousFocusNodePosition = centerPos;
camPos += focusNodeDiff;
dquat totalRotation = camera.rotationQuaternion();
dvec3 directionToCenter = normalize(centerPos - camPos);
dvec3 lookUp = camera.lookUpVectorWorldSpace();
double boundingSphere = _focusNode->boundingSphere();
dvec3 camDirection = camera.viewDirectionWorldSpace();
// Declare other variables used in interaction calculations
double minHeightAboveBoundingSphere = 1;
dvec3 centerToCamera = camPos - centerPos;
dvec3 centerToBoundingSphere;
// Create the internal representation of the local and global camera rotations
dmat4 lookAtMat = lookAt(
dvec3(0, 0, 0),
directionToCenter,
normalize(camDirection + lookUp)); // To avoid problem with lookup in up direction
dquat globalCameraRotation = normalize(quat_cast(inverse(lookAtMat)));
dquat localCameraRotation = inverse(globalCameraRotation) * totalRotation;
{ // Do local roll
glm::dquat cameraRollRotation =
glm::angleAxis(_mouseStates->synchedLocalRollMouseVelocity().x, dvec3(0, 0, 1));
localCameraRotation = localCameraRotation * cameraRollRotation;
}
if (!_rotateToFocusNodeInterpolator.isInterpolating())
{ // Do local rotation
dvec3 eulerAngles(_mouseStates->synchedLocalRotationMouseVelocity().y, _mouseStates->synchedLocalRotationMouseVelocity().x, 0);
dquat rotationDiff = dquat(eulerAngles);
localCameraRotation = localCameraRotation * rotationDiff;
}
else
{ // Interpolate local rotation to focus node
double t = _rotateToFocusNodeInterpolator.value();
localCameraRotation = slerp(localCameraRotation, dquat(dvec3(0.0)), t);
_rotateToFocusNodeInterpolator.step(deltaTime * 0.1); // Should probably depend on dt
// This is a fast but ugly solution to slow regaining of control...
if (t > 0.18) {
_rotateToFocusNodeInterpolator.end();
}
}
{ // Do global rotation
dvec2 smoothMouseVelocity = _mouseStates->synchedGlobalRotationMouseVelocity();
dvec3 eulerAngles(-smoothMouseVelocity.y, -smoothMouseVelocity.x, 0);
dquat rotationDiffCamSpace = dquat(eulerAngles);
dquat newRotationCamspace = globalCameraRotation * rotationDiffCamSpace;
dquat rotationDiffWorldSpace = newRotationCamspace * inverse(globalCameraRotation);
dvec3 rotationDiffVec3 = centerToCamera * rotationDiffWorldSpace - centerToCamera;
camPos += rotationDiffVec3;
dvec3 centerToCamera = camPos - centerPos;
directionToCenter = normalize(-centerToCamera);
dvec3 lookUpWhenFacingCenter =
globalCameraRotation * dvec3(camera.lookUpVectorCameraSpace());
dmat4 lookAtMat = lookAt(
dvec3(0, 0, 0),
directionToCenter,
lookUpWhenFacingCenter);
globalCameraRotation = normalize(quat_cast(inverse(lookAtMat)));
}
{ // Move position towards or away from focus node
centerToBoundingSphere =
-directionToCenter *
boundingSphere;
camPos += -(centerToCamera - centerToBoundingSphere) *
_mouseStates->synchedTruckMovementMouseVelocity().y;
}
{ // Roll around sphere normal
dquat cameraRollRotation =
angleAxis(_mouseStates->synchedGlobalRollMouseVelocity().x, -directionToCenter);
globalCameraRotation = cameraRollRotation * globalCameraRotation;
}
{ // Push up to surface
dvec3 sphereSurfaceToCamera = camPos - (centerPos + centerToBoundingSphere);
double distFromSphereSurfaceToCamera = length(sphereSurfaceToCamera);
camPos += -directionToCenter *
max(minHeightAboveBoundingSphere - distFromSphereSurfaceToCamera, 0.0);
}
// Update the camera state
camera.setPositionVec3(camPos);
camera.setRotation(globalCameraRotation * localCameraRotation);
}
}
bool OrbitalInteractionMode::followingNodeRotation() const {
return false;
}
void OrbitalInteractionMode::updateMouseStatesFromInput(const InputState& inputState, double deltaTime) {
_mouseStates->updateMouseStatesFromInput(inputState, deltaTime);
}
GlobeBrowsingInteractionMode::GlobeBrowsingInteractionMode(std::shared_ptr<MouseStates> mouseStates)
: OrbitalInteractionMode(mouseStates)
{
}
GlobeBrowsingInteractionMode::~GlobeBrowsingInteractionMode() {
}
void GlobeBrowsingInteractionMode::setFocusNode(SceneGraphNode* focusNode) {
InteractionMode::setFocusNode(focusNode);
#ifdef OPENSPACE_MODULE_GLOBEBROWSING_ENABLED
Renderable* baseRenderable = _focusNode->renderable();
if (globebrowsing::RenderableGlobe* globe = dynamic_cast<globebrowsing::RenderableGlobe*>(baseRenderable)) {
_globe = globe;
}
else {
LWARNING("Focus node is not a renderable globe. GlobeBrowsingInteraction is not possible");
_globe = nullptr;
}
#endif // OPENSPACE_MODULE_GLOBEBROWSING_ENABLED
}
void GlobeBrowsingInteractionMode::updateCameraStateFromMouseStates(Camera& camera, double deltaTime) {
#ifdef OPENSPACE_MODULE_GLOBEBROWSING_ENABLED
using namespace glm;
if (_focusNode && _globe) {
// Declare variables to use in interaction calculations
// Shrink interaction ellipsoid to enable interaction below height = 0
double ellipsoidShrinkTerm = _globe->interactionDepthBelowEllipsoid();
double minHeightAboveGround = _globe->generalProperties().cameraMinHeight;
// Read the current state of the camera and focusnode
dvec3 camPos = camera.positionVec3();
dvec3 centerPos = _focusNode->worldPosition();
// Follow focus nodes movement
dvec3 focusNodeDiff = centerPos - _previousFocusNodePosition;
_previousFocusNodePosition = centerPos;
camPos += focusNodeDiff;
dquat totalRotation = camera.rotationQuaternion();
dvec3 lookUp = camera.lookUpVectorWorldSpace();
dvec3 camDirection = camera.viewDirectionWorldSpace();
// Sampling of height is done in the reference frame of the globe.
// Hence, the camera position needs to be transformed with the inverse model matrix
glm::dmat4 inverseModelTransform = _globe->inverseModelTransform();
glm::dmat4 modelTransform = _globe->modelTransform();
glm::dvec3 cameraPositionModelSpace =
glm::dvec3(inverseModelTransform * glm::dvec4(camPos, 1));
dvec3 posDiff = camPos - centerPos;
dvec3 directionFromSurfaceToCameraModelSpace =
_globe->ellipsoid().geodeticSurfaceNormal(
_globe->ellipsoid().cartesianToGeodetic2(cameraPositionModelSpace));
dvec3 directionFromSurfaceToCamera =
normalize(dmat3(modelTransform) * directionFromSurfaceToCameraModelSpace);
dvec3 centerToEllipsoidSurface = dmat3(modelTransform) * (_globe->projectOnEllipsoid(cameraPositionModelSpace) -
directionFromSurfaceToCameraModelSpace * ellipsoidShrinkTerm);
dvec3 ellipsoidSurfaceToCamera = camPos - (centerPos + centerToEllipsoidSurface);
double heightToSurface =
_globe->getHeight(cameraPositionModelSpace) + ellipsoidShrinkTerm;
double distFromCenterToSurface =
length(centerToEllipsoidSurface);
double distFromEllipsoidSurfaceToCamera = length(ellipsoidSurfaceToCamera);
double distFromCenterToCamera = length(posDiff);
double distFromSurfaceToCamera =
distFromEllipsoidSurfaceToCamera - heightToSurface;
// Create the internal representation of the local and global camera rotations
dmat4 lookAtMat = lookAt(
dvec3(0.0, 0.0, 0.0),
-directionFromSurfaceToCamera,
normalize(camDirection + lookUp)); // To avoid problem with lookup in up direction
dquat globalCameraRotation = normalize(quat_cast(inverse(lookAtMat)));
dquat localCameraRotation = inverse(globalCameraRotation) * totalRotation;
// Rotate with the globe
dmat3 globeStateMatrix = _focusNode->worldRotationMatrix();
dquat globeRotation = quat_cast(globeStateMatrix);
dquat focusNodeRotationDiff = _previousFocusNodeRotation * inverse(globeRotation);
_previousFocusNodeRotation = globeRotation;
{ // Do local roll
glm::dquat cameraRollRotation =
glm::angleAxis(_mouseStates->synchedLocalRollMouseVelocity().x, dvec3(0, 0, 1));
localCameraRotation = localCameraRotation * cameraRollRotation;
}
if(!_rotateToFocusNodeInterpolator.isInterpolating())
{ // Do local rotation
glm::dvec3 eulerAngles(_mouseStates->synchedLocalRotationMouseVelocity().y, _mouseStates->synchedLocalRotationMouseVelocity().x, 0);
glm::dquat rotationDiff = glm::dquat(eulerAngles);
localCameraRotation = localCameraRotation * rotationDiff;
}
else
{ // Interpolate local rotation to focus node
double t = _rotateToFocusNodeInterpolator.value();
localCameraRotation = slerp(localCameraRotation, dquat(dvec3(0.0)), t);
_rotateToFocusNodeInterpolator.step(0.002); // Should probably depend on dt
// This is a fast but ugly solution to slow regaining of control...
if (t > 0.2) {
_rotateToFocusNodeInterpolator.end();
}
}
{ // Do global rotation (horizontal movement)
glm::dvec3 eulerAngles = glm::dvec3(
-_mouseStates->synchedGlobalRotationMouseVelocity().y,
-_mouseStates->synchedGlobalRotationMouseVelocity().x,
0) * glm::clamp(distFromSurfaceToCamera / distFromCenterToSurface, 0.0, 1.0);
glm::dquat rotationDiffCamSpace = glm::dquat(eulerAngles);
glm::dquat rotationDiffWorldSpace =
globalCameraRotation *
rotationDiffCamSpace *
glm::inverse(globalCameraRotation);
glm::dvec3 rotationDiffVec3 =
(distFromCenterToCamera * directionFromSurfaceToCamera)
* rotationDiffWorldSpace
- (distFromCenterToCamera * directionFromSurfaceToCamera);
camPos += rotationDiffVec3;
posDiff = camPos - centerPos;
glm::dvec3 rotationDiffVec3AroundCenter =
posDiff
* focusNodeRotationDiff
- (posDiff);
camPos += rotationDiffVec3AroundCenter;
cameraPositionModelSpace =
glm::dvec3(inverseModelTransform * glm::dvec4(camPos, 1));
directionFromSurfaceToCameraModelSpace =
_globe->ellipsoid().geodeticSurfaceNormal(
_globe->ellipsoid().cartesianToGeodetic2(cameraPositionModelSpace));
directionFromSurfaceToCamera =
normalize(dmat3(modelTransform) * directionFromSurfaceToCameraModelSpace);
centerToEllipsoidSurface = dmat3(modelTransform) * (_globe->projectOnEllipsoid(cameraPositionModelSpace) -
directionFromSurfaceToCameraModelSpace * ellipsoidShrinkTerm);
ellipsoidSurfaceToCamera = camPos - (centerPos + centerToEllipsoidSurface);
glm::dvec3 lookUpWhenFacingSurface =
inverse(focusNodeRotationDiff) * globalCameraRotation * glm::dvec3(camera.lookUpVectorCameraSpace());
glm::dmat4 lookAtMat = glm::lookAt(
glm::dvec3(0, 0, 0),
-directionFromSurfaceToCamera,
lookUpWhenFacingSurface);
globalCameraRotation =
glm::normalize(glm::quat_cast(glm::inverse(lookAtMat)));
}
{ // Move position towards or away from focus node
camPos += -directionFromSurfaceToCamera * distFromSurfaceToCamera *
_mouseStates->synchedTruckMovementMouseVelocity().y;
}
{ // Roll around ellipsoid normal
glm::dquat cameraRollRotation =
glm::angleAxis(_mouseStates->synchedGlobalRollMouseVelocity().x, directionFromSurfaceToCamera);
globalCameraRotation = cameraRollRotation * globalCameraRotation;
}
{ // Push up to surface
ellipsoidSurfaceToCamera = camPos - (centerPos + centerToEllipsoidSurface);
// Sampling of height is done in the reference frame of the globe.
// Hence, the camera position needs to be transformed with the inverse model matrix
glm::dmat4 inverseModelTransform = _globe->inverseModelTransform();
glm::dvec3 cameraPositionModelSpace =
glm::dvec3(inverseModelTransform * glm::dvec4(camPos, 1));
distFromEllipsoidSurfaceToCamera = glm::length(ellipsoidSurfaceToCamera);
double heightToSurface =
_globe->getHeight(cameraPositionModelSpace) + ellipsoidShrinkTerm;
double heightToSurfaceAndPadding = heightToSurface + minHeightAboveGround;
camPos += directionFromSurfaceToCamera *
glm::max(heightToSurfaceAndPadding - distFromEllipsoidSurfaceToCamera, 0.0);
}
// Update the camera state
camera.setPositionVec3(camPos);
camera.setRotation(globalCameraRotation * localCameraRotation);
}
#endif // OPENSPACE_MODULE_GLOBEBROWSING_ENABLED
}
bool GlobeBrowsingInteractionMode::followingNodeRotation() const {
return true;
}
#ifdef OPENSPACE_MODULE_GLOBEBROWSING_ENABLED
void GlobeBrowsingInteractionMode::goToChunk(Camera& camera,
globebrowsing::TileIndex ti, glm::vec2 uv, bool resetCameraDirection) {
using namespace globebrowsing;
// Camera position in model space
glm::dvec3 camPos = camera.positionVec3();
glm::dmat4 inverseModelTransform = _globe->inverseModelTransform();
glm::dvec3 cameraPositionModelSpace =
glm::dvec3(inverseModelTransform * glm::dvec4(camPos, 1));
GeodeticPatch patch(ti);
Geodetic2 corner = patch.getCorner(SOUTH_WEST);
Geodetic2 positionOnPatch = patch.getSize();
positionOnPatch.lat *= uv.y;
positionOnPatch.lon *= uv.x;
Geodetic2 pointPosition = corner + positionOnPatch;
glm::dvec3 positionOnEllipsoid =
_globe->ellipsoid().geodeticSurfaceProjection(cameraPositionModelSpace);
double altitude = glm::length(cameraPositionModelSpace - positionOnEllipsoid);
goToGeodetic3(camera, {pointPosition, altitude});
if (resetCameraDirection) {
this->resetCameraDirection(camera, pointPosition);
}
}
void GlobeBrowsingInteractionMode::goToGeodetic2(Camera& camera,
globebrowsing::Geodetic2 geo2, bool resetCameraDirection) {
using namespace globebrowsing;
// Camera position in model space
glm::dvec3 camPos = camera.positionVec3();
glm::dmat4 inverseModelTransform = _globe->inverseModelTransform();
glm::dvec3 cameraPositionModelSpace =
glm::dvec3(inverseModelTransform * glm::dvec4(camPos, 1));
glm::dvec3 positionOnEllipsoid =
_globe->ellipsoid().geodeticSurfaceProjection(cameraPositionModelSpace);
double altitude = glm::length(cameraPositionModelSpace - positionOnEllipsoid);
goToGeodetic3(camera, {geo2, altitude});
if (resetCameraDirection) {
this->resetCameraDirection(camera, geo2);
}
}
void GlobeBrowsingInteractionMode::goToGeodetic3(Camera& camera, globebrowsing::Geodetic3 geo3) {
glm::dvec3 positionModelSpace = _globe->ellipsoid().cartesianPosition(geo3);
glm::dmat4 modelTransform = _globe->modelTransform();
glm::dvec3 positionWorldSpace = modelTransform * glm::dvec4(positionModelSpace, 1.0);
camera.setPositionVec3(positionWorldSpace);
}
void GlobeBrowsingInteractionMode::resetCameraDirection(Camera& camera, globebrowsing::Geodetic2 geo2) {
using namespace globebrowsing;
// Camera is described in world space
glm::dmat4 modelTransform = _globe->modelTransform();
// Lookup vector
glm::dvec3 positionModelSpace = _globe->ellipsoid().cartesianSurfacePosition(geo2);
glm::dvec3 slightlyNorth = _globe->ellipsoid().cartesianSurfacePosition(
Geodetic2(geo2.lat + 0.001, geo2.lon));
glm::dvec3 lookUpModelSpace = glm::normalize(slightlyNorth - positionModelSpace);
glm::dvec3 lookUpWorldSpace = glm::dmat3(modelTransform) * lookUpModelSpace;
// Lookat vector
glm::dvec3 lookAtWorldSpace = modelTransform * glm::dvec4(positionModelSpace, 1.0);
// Eye position
glm::dvec3 eye = camera.positionVec3();
// Matrix
glm::dmat4 lookAtMatrix = glm::lookAt(
eye, lookAtWorldSpace, lookUpWorldSpace);
// Set rotation
glm::dquat rotation = glm::quat_cast(inverse(lookAtMatrix));
camera.setRotation(rotation);
}
#endif // OPENSPACE_MODULE_GLOBEBROWSING_ENABLED
} // namespace interaction
} // namespace openspace

View File

@@ -0,0 +1,178 @@
/*****************************************************************************************
* *
* OpenSpace *
* *
* Copyright (c) 2014-2017 *
* *
* Permission is hereby granted, free of charge, to any person obtaining a copy of this *
* software and associated documentation files (the "Software"), to deal in the Software *
* without restriction, including without limitation the rights to use, copy, modify, *
* merge, publish, distribute, sublicense, and/or sell copies of the Software, and to *
* permit persons to whom the Software is furnished to do so, subject to the following *
* conditions: *
* *
* The above copyright notice and this permission notice shall be included in all copies *
* or substantial portions of the Software. *
* *
* 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. *
****************************************************************************************/
#include <openspace/interaction/keybindingmanager.h>
#include <openspace/openspace.h>
#include <openspace/engine/openspaceengine.h>
#include <openspace/scripting/scriptengine.h>
#include <openspace/query/query.h>
#include <openspace/util/keys.h>
#include <ghoul/filesystem/filesystem.h>
#include <ghoul/logging/logmanager.h>
#include <glm/gtx/string_cast.hpp>
#include <ghoul/glm.h>
#include <fstream>
namespace {
const char* MainTemplateFilename = "${OPENSPACE_DATA}/web/keybindings/main.hbs";
const char* KeybindingTemplateFilename = "${OPENSPACE_DATA}/web/keybindings/keybinding.hbs";
const char* JsFilename = "${OPENSPACE_DATA}/web/keybindings/script.js";
} // namespace
#include "keybindingmanager_lua.inl"
namespace openspace {
namespace interaction {
KeyBindingManager::KeyBindingManager()
: DocumentationGenerator(
"Documentation",
"keybindings",
{
{ "keybindingTemplate", KeybindingTemplateFilename },
{ "mainTemplate", MainTemplateFilename }
},
JsFilename
)
{ }
void KeyBindingManager::keyboardCallback(Key key, KeyModifier modifier, KeyAction action) {
if (action == KeyAction::Press || action == KeyAction::Repeat) {
// iterate over key bindings
std::pair<std::multimap<KeyWithModifier, KeyInformation>::iterator,
std::multimap<KeyWithModifier, KeyInformation>::iterator> ret =
_keyLua.equal_range({ key, modifier });
for (std::multimap<KeyWithModifier, KeyInformation>::iterator it = ret.first;
it != ret.second;
++it)
{
scripting::ScriptEngine::RemoteScripting remote =
it->second.synchronization ?
scripting::ScriptEngine::RemoteScripting::Yes :
scripting::ScriptEngine::RemoteScripting::No;
OsEng.scriptEngine().queueScript(it->second.command, remote);
}
}
}
void KeyBindingManager::resetKeyBindings() {
_keyLua.clear();
}
void KeyBindingManager::bindKeyLocal(Key key, KeyModifier modifier,
std::string luaCommand, std::string documentation)
{
_keyLua.insert({
{ key, modifier },
{
std::move(luaCommand),
Synchronized::No,
std::move(documentation)
}
});
}
void KeyBindingManager::bindKey(Key key, KeyModifier modifier,
std::string luaCommand, std::string documentation)
{
_keyLua.insert({
{ key, modifier },
{
std::move(luaCommand),
Synchronized::Yes,
std::move(documentation)
}
});
}
std::string KeyBindingManager::generateJson() const {
std::stringstream json;
json << "[";
bool first = true;
for (const std::pair<KeyWithModifier, KeyInformation>& p : _keyLua) {
if (!first) {
json << ",";
}
first = false;
json << "{";
json << "\"key\": \"" << std::to_string(p.first) << "\",";
json << "\"script\": \"" << p.second.command << "\",";
json << "\"remoteScripting\": " << (p.second.synchronization ? "true," : "false,");
json << "\"documentation\": \"" << p.second.documentation << "\"";
json << "}";
}
json << "]";
std::string jsonString = "";
for (const char& c : json.str()) {
if (c == '\'') {
jsonString += "\\'";
} else {
jsonString += c;
}
}
return jsonString;
}
scripting::LuaLibrary KeyBindingManager::luaLibrary() {
return {
"",
{
{
"clearKeys",
&luascriptfunctions::clearKeys,
"",
"Clear all key bindings"
},
{
"bindKey",
&luascriptfunctions::bindKey,
"string, string [,string]",
"Binds a key by name to a lua string command to execute both locally "
"and to broadcast to clients if this is the host of a parallel session. "
"The first argument is the key, the second argument is the Lua command "
"that is to be executed, and the optional third argument is a human "
"readable description of the command for documentation purposes."
},
{
"bindKeyLocal",
&luascriptfunctions::bindKeyLocal,
"string, string [,string]",
"Binds a key by name to a lua string command to execute only locally. "
"The first argument is the key, the second argument is the Lua command "
"that is to be executed, and the optional third argument is a human "
"readable description of the command for documentation purposes."
},
}
};
}
} // namespace interaction
} // namespace openspace

View File

@@ -25,38 +25,6 @@
namespace openspace {
namespace luascriptfunctions {
/**
* \ingroup LuaScripts
* setOrigin():
* Set the origin of the camera
*/
int setOrigin(lua_State* L) {
using ghoul::lua::luaTypeToString;
int nArguments = lua_gettop(L);
if (nArguments != 1)
return luaL_error(L, "Expected %i arguments, got %i", 1, nArguments);
const int type = lua_type(L, -1);
if (type != LUA_TSTRING)
return luaL_error(L, "Expected string, got %i", type);
std::string s = luaL_checkstring(L, -1);
SceneGraphNode* node = sceneGraphNode(s);
if (!node) {
LWARNINGC(
"lua.setOrigin",
"Could not find a node in scenegraph called '" << s << "'"
);
return 0;
}
OsEng.interactionHandler().setFocusNode(node);
return 0;
}
/**
* \ingroup LuaScripts
* bindKey():
@@ -95,7 +63,7 @@ int bindKey(lua_State* L) {
documentation = luaL_checkstring(L, DocumentationLocation);
}
OsEng.interactionHandler().bindKey(
OsEng.keyBindingManager().bindKey(
iKey.key,
iKey.modifier,
std::move(command),
@@ -141,7 +109,7 @@ int bindKeyLocal(lua_State* L) {
documentation = luaL_checkstring(L, DocumentationLocation);
}
OsEng.interactionHandler().bindKeyLocal(
OsEng.keyBindingManager().bindKeyLocal(
iKey.key,
iKey.modifier,
std::move(command),
@@ -151,7 +119,6 @@ int bindKeyLocal(lua_State* L) {
return 0;
}
/**
* \ingroup LuaScripts
* clearKeys():
@@ -164,89 +131,11 @@ int clearKeys(lua_State* L) {
if (nArguments != 0)
return luaL_error(L, "Expected %i arguments, got %i", 0, nArguments);
OsEng.interactionHandler().resetKeyBindings();
OsEng.keyBindingManager().resetKeyBindings();
return 0;
}
int goToChunk(lua_State* L) {
using ghoul::lua::luaTypeToString;
int nArguments = lua_gettop(L);
if (nArguments != 3)
return luaL_error(L, "Expected %i arguments, got %i", 3, nArguments);
int x = static_cast<int>(lua_tonumber(L, 1));
int y = static_cast<int>(lua_tonumber(L, 2));
int level = static_cast<int>(lua_tonumber(L, 3));
OsEng.interactionHandler().goToChunk(x, y, level);
return 0;
}
int goToGeo(lua_State* L) {
using ghoul::lua::luaTypeToString;
int nArguments = lua_gettop(L);
if (nArguments != 2)
return luaL_error(L, "Expected %i arguments, got %i", 2, nArguments);
double latitude = static_cast<int>(lua_tonumber(L, 1));
double longitude = static_cast<int>(lua_tonumber(L, 2));
OsEng.interactionHandler().goToGeo(latitude, longitude);
return 0;
}
int restoreCameraStateFromFile(lua_State* L) {
using ghoul::lua::luaTypeToString;
int nArguments = lua_gettop(L);
if (nArguments != 1)
return luaL_error(L, "Expected %i arguments, got %i", 1, nArguments);
std::string cameraStateFilePath = luaL_checkstring(L, -1);
if (cameraStateFilePath.empty()) {
return luaL_error(L, "filepath string is empty");
}
OsEng.interactionHandler().restoreCameraStateFromFile(cameraStateFilePath);
return 0;
}
int saveCameraStateToFile(lua_State* L) {
using ghoul::lua::luaTypeToString;
int nArguments = lua_gettop(L);
if (nArguments != 1)
return luaL_error(L, "Expected %i arguments, got %i", 1, nArguments);
std::string cameraStateFilePath = luaL_checkstring(L, -1);
if (cameraStateFilePath.empty()) {
return luaL_error(L, "filepath string is empty");
}
OsEng.interactionHandler().saveCameraStateToFile(cameraStateFilePath);
return 0;
}
int resetCameraDirection(lua_State* L) {
using ghoul::lua::luaTypeToString;
int nArguments = lua_gettop(L);
if (nArguments != 0) {
return luaL_error(L, "Expected %i arguments, got %i", 0, nArguments);
}
OsEng.interactionHandler().resetCameraDirection();
return 0;
}
} // namespace luascriptfunctions
} // namespace openspace

View File

@@ -0,0 +1,127 @@
/*****************************************************************************************
* *
* OpenSpace *
* *
* Copyright (c) 2014-2017 *
* *
* Permission is hereby granted, free of charge, to any person obtaining a copy of this *
* software and associated documentation files (the "Software"), to deal in the Software *
* without restriction, including without limitation the rights to use, copy, modify, *
* merge, publish, distribute, sublicense, and/or sell copies of the Software, and to *
* permit persons to whom the Software is furnished to do so, subject to the following *
* conditions: *
* *
* The above copyright notice and this permission notice shall be included in all copies *
* or substantial portions of the Software. *
* *
* 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. *
****************************************************************************************/
#include <openspace/interaction/keyframenavigator.h>
#include <openspace/engine/openspaceengine.h>
#include <openspace/scene/scenegraphnode.h>
#include <openspace/scene/scene.h>
#include <openspace/util/camera.h>
#include <openspace/util/time.h>
#include <ghoul/logging/logmanager.h>
#include <glm/gtx/quaternion.hpp>
namespace openspace {
namespace interaction {
void KeyframeNavigator::updateCamera(Camera& camera) {
double now = OsEng.runTime();
if (_cameraPoseTimeline.nKeyframes() == 0) {
return;
}
const Keyframe<CameraPose>* nextKeyframe = _cameraPoseTimeline.firstKeyframeAfter(now);
const Keyframe<CameraPose>* prevKeyframe = _cameraPoseTimeline.lastKeyframeBefore(now);
double nextTime = 0;
double prevTime = 0;
double t = 0;
if (nextKeyframe) {
nextTime = nextKeyframe->timestamp;
} else {
return;
}
if (prevKeyframe) {
prevTime = prevKeyframe->timestamp;
t = (now - prevTime) / (nextTime - prevTime);
} else {
// If there is no keyframe before: Only use the next keyframe.
prevTime = nextTime;
prevKeyframe = nextKeyframe;
t = 1;
}
_cameraPoseTimeline.removeKeyframesBefore(prevTime);
const CameraPose& prevPose = prevKeyframe->data;
const CameraPose& nextPose = nextKeyframe->data;
Scene* scene = camera.parent()->scene();
SceneGraphNode* prevFocusNode = scene->sceneGraphNode(prevPose.focusNode);
SceneGraphNode* nextFocusNode = scene->sceneGraphNode(nextPose.focusNode);
if (!prevFocusNode || !nextFocusNode) {
return;
}
glm::dvec3 prevKeyframeCameraPosition = prevPose.position;
glm::dvec3 nextKeyframeCameraPosition = nextPose.position;
glm::dquat prevKeyframeCameraRotation = prevPose.rotation;
glm::dquat nextKeyframeCameraRotation = nextPose.rotation;
// Transform position and rotation based on focus node rotation (if following rotation)
if (prevPose.followFocusNodeRotation) {
prevKeyframeCameraRotation = prevFocusNode->worldRotationMatrix() * glm::dmat3(glm::dquat(prevPose.rotation));
prevKeyframeCameraPosition = prevFocusNode->worldRotationMatrix() * prevPose.position;
}
if (nextPose.followFocusNodeRotation) {
nextKeyframeCameraRotation = nextFocusNode->worldRotationMatrix() * glm::dmat3(glm::dquat(nextPose.rotation));
nextKeyframeCameraPosition = nextFocusNode->worldRotationMatrix() * nextPose.position;
}
// Transform position based on focus node position
prevKeyframeCameraPosition += prevFocusNode->worldPosition();
nextKeyframeCameraPosition += nextFocusNode->worldPosition();
// Linear interpolation
camera.setPositionVec3(prevKeyframeCameraPosition * (1 - t) + nextKeyframeCameraPosition * t);
camera.setRotation(glm::slerp(prevKeyframeCameraRotation, nextKeyframeCameraRotation, t));
}
Timeline<KeyframeNavigator::CameraPose>& KeyframeNavigator::timeline() {
return _cameraPoseTimeline;
}
void KeyframeNavigator::addKeyframe(double timestamp, KeyframeNavigator::CameraPose pose) {
timeline().addKeyframe(timestamp, pose);
}
void KeyframeNavigator::removeKeyframesAfter(double timestamp) {
timeline().removeKeyframesAfter(timestamp);
}
void KeyframeNavigator::clearKeyframes() {
timeline().clearKeyframes();
}
size_t KeyframeNavigator::nKeyframes() const {
return _cameraPoseTimeline.nKeyframes();
}
} // namespace interaction
} // namespace openspace

View File

@@ -0,0 +1,177 @@
/*****************************************************************************************
* *
* OpenSpace *
* *
* Copyright (c) 2014-2017 *
* *
* Permission is hereby granted, free of charge, to any person obtaining a copy of this *
* software and associated documentation files (the "Software"), to deal in the Software *
* without restriction, including without limitation the rights to use, copy, modify, *
* merge, publish, distribute, sublicense, and/or sell copies of the Software, and to *
* permit persons to whom the Software is furnished to do so, subject to the following *
* conditions: *
* *
* The above copyright notice and this permission notice shall be included in all copies *
* or substantial portions of the Software. *
* *
* 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. *
****************************************************************************************/
#include <openspace/interaction/mousestate.h>
namespace openspace {
namespace interaction {
MouseState::MouseState(double scaleFactor)
: velocity(scaleFactor, 1)
, previousPosition(0.0, 0.0)
{ }
void MouseState::setFriction(double friction) {
velocity.setFriction(friction);
}
void MouseState::setVelocityScaleFactor(double scaleFactor) {
velocity.setScaleFactor(scaleFactor);
}
MouseStates::MouseStates(double sensitivity, double velocityScaleFactor)
: _sensitivity(sensitivity)
, _globalRotationMouseState(velocityScaleFactor)
, _localRotationMouseState(velocityScaleFactor)
, _truckMovementMouseState(velocityScaleFactor)
, _localRollMouseState(velocityScaleFactor)
, _globalRollMouseState(velocityScaleFactor)
{ }
void MouseStates::updateMouseStatesFromInput(const InputState& inputState,
double deltaTime)
{
glm::dvec2 mousePosition = inputState.getMousePosition();
bool button1Pressed = inputState.isMouseButtonPressed(MouseButton::Button1);
bool button2Pressed = inputState.isMouseButtonPressed(MouseButton::Button2);
bool button3Pressed = inputState.isMouseButtonPressed(MouseButton::Button3);
bool keyCtrlPressed = inputState.isKeyPressed(Key::LeftControl);
bool keyShiftPressed = inputState.isKeyPressed(Key::LeftShift);
// Update the mouse states
if (button1Pressed && !keyShiftPressed) {
if (keyCtrlPressed) {
glm::dvec2 mousePositionDelta =
_localRotationMouseState.previousPosition - mousePosition;
_localRotationMouseState.velocity.set(
mousePositionDelta * _sensitivity, deltaTime);
_globalRotationMouseState.previousPosition = mousePosition;
_globalRotationMouseState.velocity.decelerate(deltaTime);
}
else {
glm::dvec2 mousePositionDelta =
_globalRotationMouseState.previousPosition - mousePosition;
_globalRotationMouseState.velocity.set(
mousePositionDelta * _sensitivity, deltaTime);
_localRotationMouseState.previousPosition = mousePosition;
_localRotationMouseState.velocity.decelerate(deltaTime);
}
}
else { // !button1Pressed
_localRotationMouseState.previousPosition = mousePosition;
_localRotationMouseState.velocity.decelerate(deltaTime);
_globalRotationMouseState.previousPosition = mousePosition;
_globalRotationMouseState.velocity.decelerate(deltaTime);
}
if (button2Pressed) {
glm::dvec2 mousePositionDelta =
_truckMovementMouseState.previousPosition - mousePosition;
_truckMovementMouseState.velocity.set(
mousePositionDelta * _sensitivity, deltaTime);
}
else { // !button2Pressed
_truckMovementMouseState.previousPosition = mousePosition;
_truckMovementMouseState.velocity.decelerate(deltaTime);
}
if (button3Pressed || (keyShiftPressed && button1Pressed)) {
if (keyCtrlPressed) {
glm::dvec2 mousePositionDelta =
_localRollMouseState.previousPosition - mousePosition;
_localRollMouseState.velocity.set(
mousePositionDelta * _sensitivity, deltaTime);
_globalRollMouseState.previousPosition = mousePosition;
_globalRollMouseState.velocity.decelerate(deltaTime);
}
else {
glm::dvec2 mousePositionDelta =
_globalRollMouseState.previousPosition - mousePosition;
_globalRollMouseState.velocity.set(
mousePositionDelta * _sensitivity, deltaTime);
_localRollMouseState.previousPosition = mousePosition;
_localRollMouseState.velocity.decelerate(deltaTime);
}
}
else { // !button3Pressed
_globalRollMouseState.previousPosition = mousePosition;
_globalRollMouseState.velocity.decelerate(deltaTime);
_localRollMouseState.previousPosition = mousePosition;
_localRollMouseState.velocity.decelerate(deltaTime);
}
}
void MouseStates::setRotationalFriction(double friction) {
_localRotationMouseState.setFriction(friction);
_localRollMouseState.setFriction(friction);
_globalRollMouseState.setFriction(friction);
}
void MouseStates::setHorizontalFriction(double friction) {
_globalRotationMouseState.setFriction(friction);
}
void MouseStates::setVerticalFriction(double friction) {
_truckMovementMouseState.setFriction(friction);
}
void MouseStates::setSensitivity(double sensitivity) {
_sensitivity = sensitivity;
}
void MouseStates::setVelocityScaleFactor(double scaleFactor) {
_globalRotationMouseState.setVelocityScaleFactor(scaleFactor);
_localRotationMouseState.setVelocityScaleFactor(scaleFactor);
_truckMovementMouseState.setVelocityScaleFactor(scaleFactor);
_localRollMouseState.setVelocityScaleFactor(scaleFactor);
_globalRollMouseState.setVelocityScaleFactor(scaleFactor);
}
glm::dvec2 MouseStates::globalRotationMouseVelocity() const{
return _globalRotationMouseState.velocity.get();
}
glm::dvec2 MouseStates::localRotationMouseVelocity() const{
return _localRotationMouseState.velocity.get();
}
glm::dvec2 MouseStates::truckMovementMouseVelocity() const{
return _truckMovementMouseState.velocity.get();
}
glm::dvec2 MouseStates::localRollMouseVelocity() const{
return _localRollMouseState.velocity.get();
}
glm::dvec2 MouseStates::globalRollMouseVelocity() const{
return _globalRollMouseState.velocity.get();
}
} // namespace interaction
} // namespace openspace

View File

@@ -0,0 +1,299 @@
/*****************************************************************************************
* *
* OpenSpace *
* *
* Copyright (c) 2014-2017 *
* *
* Permission is hereby granted, free of charge, to any person obtaining a copy of this *
* software and associated documentation files (the "Software"), to deal in the Software *
* without restriction, including without limitation the rights to use, copy, modify, *
* merge, publish, distribute, sublicense, and/or sell copies of the Software, and to *
* permit persons to whom the Software is furnished to do so, subject to the following *
* conditions: *
* *
* The above copyright notice and this permission notice shall be included in all copies *
* or substantial portions of the Software. *
* *
* 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. *
****************************************************************************************/
#include <openspace/interaction/navigationhandler.h>
#include <openspace/openspace.h>
#include <openspace/engine/openspaceengine.h>
#include <openspace/query/query.h>
#include <openspace/rendering/renderengine.h>
#include <openspace/scene/scenegraphnode.h>
#include <openspace/util/time.h>
#include <openspace/util/keys.h>
#include <ghoul/filesystem/filesystem.h>
#include <ghoul/logging/logmanager.h>
#include <ghoul/glm.h>
#include <glm/gtx/quaternion.hpp>
#include <fstream>
namespace {
const char* _loggerCat = "NavigationHandler";
const char* KeyFocus = "Focus";
const char* KeyPosition = "Position";
const char* KeyRotation = "Rotation";
} // namespace
#include "navigationhandler_lua.inl"
namespace openspace {
namespace interaction {
NavigationHandler::NavigationHandler()
: properties::PropertyOwner("NavigationHandler")
, _origin("origin", "Origin", "")
, _useKeyFrameInteraction("useKeyFrameInteraction", "Use keyframe interaction", false)
{
_origin.onChange([this]() {
SceneGraphNode* node = sceneGraphNode(_origin.value());
if (!node) {
LWARNING("Could not find a node in scenegraph called '" << _origin.value() << "'");
return;
}
setFocusNode(node);
resetCameraDirection();
});
_inputState = std::make_unique<InputState>();
_orbitalNavigator = std::make_unique<OrbitalNavigator>();
_keyframeNavigator = std::make_unique<KeyframeNavigator>();
// Add the properties
addProperty(_origin);
addProperty(_useKeyFrameInteraction);
addPropertySubOwner(*_orbitalNavigator);
}
NavigationHandler::~NavigationHandler()
{ }
void NavigationHandler::initialize() {
OsEng.parallelConnection().connectionEvent()->subscribe("NavigationHandler", "statusChanged", [this]() {
if (OsEng.parallelConnection().status() == ParallelConnection::Status::ClientWithHost) {
_useKeyFrameInteraction = true;
}
});
}
void NavigationHandler::deinitialize() {
OsEng.parallelConnection().connectionEvent()->unsubscribe("NavigationHandler");
}
void NavigationHandler::setFocusNode(SceneGraphNode* node) {
_orbitalNavigator->setFocusNode(node);
_camera->setFocusPositionVec3(focusNode()->worldPosition());
}
void NavigationHandler::setCamera(Camera* camera) {
_camera = camera;
//setFocusNode(_camera->parent());
}
void NavigationHandler::resetCameraDirection() {
LINFO("Setting camera direction to point at focus node.");
_orbitalNavigator->startInterpolateCameraDirection(*_camera);
}
const OrbitalNavigator& NavigationHandler::orbitalNavigator() const {
return *_orbitalNavigator;
}
KeyframeNavigator& NavigationHandler::keyframeNavigator() const {
return *_keyframeNavigator;
}
void NavigationHandler::updateCamera(double deltaTime) {
ghoul_assert(_inputState != nullptr, "InputState cannot be null!");
ghoul_assert(_camera != nullptr, "Camera cannot be null!");
if (_cameraUpdatedFromScript) {
_cameraUpdatedFromScript = false;
}
else {
if (_camera && focusNode()) {
if (_useKeyFrameInteraction) {
_keyframeNavigator->updateCamera(*_camera);
}
else {
_orbitalNavigator->updateMouseStatesFromInput(*_inputState, deltaTime);
_orbitalNavigator->updateCameraStateFromMouseStates(*_camera, deltaTime);
}
_camera->setFocusPositionVec3(focusNode()->worldPosition());
}
}
}
SceneGraphNode* NavigationHandler::focusNode() const {
return _orbitalNavigator->focusNode();
}
glm::dvec3 NavigationHandler::focusNodeToCameraVector() const {
return _camera->positionVec3() - focusNode()->worldPosition();
}
glm::quat NavigationHandler::focusNodeToCameraRotation() const {
glm::dmat4 invWorldRotation = glm::inverse(focusNode()->worldRotationMatrix());
return glm::quat(invWorldRotation) * glm::quat(_camera->rotationQuaternion());
}
Camera* NavigationHandler::camera() const {
return _camera;
}
const InputState& NavigationHandler::inputState() const {
return *_inputState;
}
void NavigationHandler::mouseButtonCallback(MouseButton button, MouseAction action) {
_inputState->mouseButtonCallback(button, action);
}
void NavigationHandler::mousePositionCallback(double x, double y) {
_inputState->mousePositionCallback(x, y);
}
void NavigationHandler::mouseScrollWheelCallback(double pos) {
_inputState->mouseScrollWheelCallback(pos);
}
void NavigationHandler::keyboardCallback(Key key, KeyModifier modifier, KeyAction action) {
_inputState->keyboardCallback(key, modifier, action);
}
void NavigationHandler::setCameraStateFromDictionary(const ghoul::Dictionary& cameraDict) {
bool readSuccessful = true;
std::string focus;
glm::dvec3 cameraPosition;
glm::dvec4 cameraRotation; // Need to read the quaternion as a vector first.
readSuccessful &= cameraDict.getValue(KeyFocus, focus);
readSuccessful &= cameraDict.getValue(KeyPosition, cameraPosition);
readSuccessful &= cameraDict.getValue(KeyRotation, cameraRotation);
if (!readSuccessful) {
throw ghoul::RuntimeError(
"Position, Rotation and Focus need to be defined for camera dictionary.");
}
SceneGraphNode* node = sceneGraphNode(focus);
if (!node) {
throw ghoul::RuntimeError(
"Could not find a node in scenegraph called '" + focus + "'");
}
// Set state
setFocusNode(node);
_camera->setPositionVec3(cameraPosition);
_camera->setRotation(glm::dquat(
cameraRotation.x, cameraRotation.y, cameraRotation.z, cameraRotation.w));
}
ghoul::Dictionary NavigationHandler::getCameraStateDictionary() {
glm::dvec3 cameraPosition;
glm::dquat quat;
glm::dvec4 cameraRotation;
cameraPosition = _camera->positionVec3();
quat = _camera->rotationQuaternion();
cameraRotation = glm::dvec4(quat.w, quat.x, quat.y, quat.z);
ghoul::Dictionary cameraDict;
cameraDict.setValue(KeyPosition, cameraPosition);
cameraDict.setValue(KeyRotation, cameraRotation);
cameraDict.setValue(KeyFocus, focusNode()->name());
return cameraDict;
}
void NavigationHandler::saveCameraStateToFile(const std::string& filepath) {
if (!filepath.empty()) {
std::string fullpath = absPath(filepath);
LINFO("Saving camera position: " << filepath);
ghoul::Dictionary cameraDict = getCameraStateDictionary();
// TODO : Should get the camera state as a dictionary and save the dictionary to
// a file in form of a lua state and not use ofstreams here.
std::ofstream ofs(fullpath.c_str());
glm::dvec3 p = _camera->positionVec3();
glm::dquat q = _camera->rotationQuaternion();
ofs << "return {" << std::endl;
ofs << " " << KeyFocus << " = " << "\"" << focusNode()->name() << "\"" << "," << std::endl;
ofs << " " << KeyPosition << " = {"
<< std::to_string(p.x) << ", "
<< std::to_string(p.y) << ", "
<< std::to_string(p.z) << "}," << std::endl;
ofs << " " << KeyRotation << " = {"
<< std::to_string(q.w) << ", "
<< std::to_string(q.x) << ", "
<< std::to_string(q.y) << ", "
<< std::to_string(q.z) << "}," << std::endl;
ofs << "}"<< std::endl;
ofs.close();
}
}
void NavigationHandler::restoreCameraStateFromFile(const std::string& filepath) {
LINFO("Reading camera state from file: " << filepath);
if (!FileSys.fileExists(filepath))
throw ghoul::FileNotFoundError(filepath, "CameraFilePath");
ghoul::Dictionary cameraDict;
try {
ghoul::lua::loadDictionaryFromFile(filepath, cameraDict);
setCameraStateFromDictionary(cameraDict);
_cameraUpdatedFromScript = true;
}
catch (ghoul::RuntimeError& e) {
LWARNING("Unable to set camera position");
LWARNING(e.message);
}
}
scripting::LuaLibrary NavigationHandler::luaLibrary() {
return {
"navigation",
{
{
"saveCameraStateToFile",
&luascriptfunctions::saveCameraStateToFile,
"string",
"Save the current camera state to file"
},
{
"restoreCameraStateFromFile",
&luascriptfunctions::restoreCameraStateFromFile,
"string",
"Restore the camera state from file"
},
{
"resetCameraDirection",
&luascriptfunctions::resetCameraDirection,
"void",
"Reset the camera direction to point at the focus node"
}
}
};
}
} // namespace interaction
} // namespace openspace

View File

@@ -0,0 +1,113 @@
/*****************************************************************************************
* *
* OpenSpace *
* *
* Copyright (c) 2014-2017 *
* *
* Permission is hereby granted, free of charge, to any person obtaining a copy of this *
* software and associated documentation files (the "Software"), to deal in the Software *
* without restriction, including without limitation the rights to use, copy, modify, *
* merge, publish, distribute, sublicense, and/or sell copies of the Software, and to *
* permit persons to whom the Software is furnished to do so, subject to the following *
* conditions: *
* *
* The above copyright notice and this permission notice shall be included in all copies *
* or substantial portions of the Software. *
* *
* 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. *
****************************************************************************************/
namespace openspace {
namespace luascriptfunctions {
/**
* \ingroup LuaScripts
* setOrigin():
* Set the origin of the camera
*/
int setOrigin(lua_State* L) {
using ghoul::lua::luaTypeToString;
int nArguments = lua_gettop(L);
if (nArguments != 1) {
return luaL_error(L, "Expected %i arguments, got %i", 1, nArguments);
}
const int type = lua_type(L, -1);
if (type != LUA_TSTRING) {
return luaL_error(L, "Expected string, got %i", type);
}
std::string s = luaL_checkstring(L, -1);
SceneGraphNode* node = sceneGraphNode(s);
if (!node) {
LWARNINGC(
"lua.setOrigin",
"Could not find a node in scenegraph called '" << s << "'"
);
return 0;
}
OsEng.navigationHandler().setFocusNode(node);
return 0;
}
int restoreCameraStateFromFile(lua_State* L) {
using ghoul::lua::luaTypeToString;
int nArguments = lua_gettop(L);
if (nArguments != 1) {
return luaL_error(L, "Expected %i arguments, got %i", 1, nArguments);
}
std::string cameraStateFilePath = luaL_checkstring(L, -1);
if (cameraStateFilePath.empty()) {
return luaL_error(L, "filepath string is empty");
}
OsEng.navigationHandler().restoreCameraStateFromFile(cameraStateFilePath);
return 0;
}
int saveCameraStateToFile(lua_State* L) {
using ghoul::lua::luaTypeToString;
int nArguments = lua_gettop(L);
if (nArguments != 1) {
return luaL_error(L, "Expected %i arguments, got %i", 1, nArguments);
}
std::string cameraStateFilePath = luaL_checkstring(L, -1);
if (cameraStateFilePath.empty()) {
return luaL_error(L, "filepath string is empty");
}
OsEng.navigationHandler().saveCameraStateToFile(cameraStateFilePath);
return 0;
}
int resetCameraDirection(lua_State* L) {
using ghoul::lua::luaTypeToString;
int nArguments = lua_gettop(L);
if (nArguments != 0) {
return luaL_error(L, "Expected %i arguments, got %i", 0, nArguments);
}
OsEng.navigationHandler().resetCameraDirection();
return 0;
}
} // namespace luascriptfunctions
} // namespace openspace

View File

@@ -0,0 +1,532 @@
/*****************************************************************************************
* *
* OpenSpace *
* *
* Copyright (c) 2014-2017 *
* *
* Permission is hereby granted, free of charge, to any person obtaining a copy of this *
* software and associated documentation files (the "Software"), to deal in the Software *
* without restriction, including without limitation the rights to use, copy, modify, *
* merge, publish, distribute, sublicense, and/or sell copies of the Software, and to *
* permit persons to whom the Software is furnished to do so, subject to the following *
* conditions: *
* *
* The above copyright notice and this permission notice shall be included in all copies *
* or substantial portions of the Software. *
* *
* 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. *
****************************************************************************************/
#include <openspace/interaction/orbitalnavigator.h>
#include <openspace/scene/scenegraphnode.h>
#include <openspace/util/updatestructures.h>
#include <openspace/rendering/renderable.h>
#include <ghoul/logging/logmanager.h>
#include <glm/gtx/quaternion.hpp>
#include <glm/gtx/vector_angle.hpp>
namespace {
const char* _loggerCat = "OrbitalNavigator";
}
namespace openspace {
namespace interaction {
OrbitalNavigator::OrbitalNavigator()
: properties::PropertyOwner("OrbitalNavigator")
, _rotationalFriction("rotationalFriction", "Rotational friction", true)
, _horizontalFriction("horizontalFriction", "Horizontal friction", true)
, _verticalFriction("verticalFriction", "Vertical friction", true)
, _followFocusNodeRotationDistance("followFocusNodeRotationDistance",
"Follow focus node rotation distance", 2.0f, 0.0f, 10.f)
, _minimumAllowedDistance("minimumAllowedDistance",
"Minimum allowed distance", 10.0f, 0.0f, 10000.f)
, _sensitivity("sensitivity", "Sensitivity", 20.0f, 1.0f, 50.f)
, _motionLag("motionLag", "Motion lag", 0.5f, 0.f, 1.f)
, _mouseStates(_sensitivity * pow(10.0,-4), 1 / (_motionLag + 0.0000001))
{
auto smoothStep =
[](double t) {
double res = 3.0 * t*t - 2.0 * t*t*t;
return glm::clamp(res, 0.0, 1.0);
};
_followRotationInterpolator.setTransferFunction(smoothStep);
// The transfer function is used here to get a different interpolation than the one
// obtained from newValue = lerp(0, currentValue, dt). That one will result in an
// exponentially decreasing value but we want to be able to control it. Either as
// a linear interpolation or a smooth step interpolation. Therefore we use
// newValue = lerp(0, currentValue * f(t) * dt) where f(t) is the transfer function
// and lerp is a linear iterpolation
// lerp(endValue, startValue, interpolationParameter).
//
// The transfer functions are derived from:
// f(t) = d/dt ( ln(1 / f_orig(t)) ) where f_orig is the transfer function that would
// be used if the interpolation was sinply linear between a start value and an end
// value instead of current value and end value (0) as we use it when inerpoláting.
// As an example f_orig(t) = 1 - t yields f(t) = 1 / (1 - t) which results in a linear
// interpolation from 1 to 0.
auto smoothStepDerivedTranferFunction =
[](double t) {
return (6 * (t + t*t) / (1 - 3 * t*t + 2 * t*t*t));
};
_rotateToFocusNodeInterpolator.setTransferFunction(smoothStepDerivedTranferFunction);
// Define callback functions for changed properties
_rotationalFriction.onChange([&]() {
_mouseStates.setRotationalFriction(_rotationalFriction);
});
_horizontalFriction.onChange([&]() {
_mouseStates.setHorizontalFriction(_horizontalFriction);
});
_verticalFriction.onChange([&]() {
_mouseStates.setVerticalFriction(_verticalFriction);
});
_sensitivity.onChange([&]() {
_mouseStates.setSensitivity(_sensitivity * pow(10.0,-4));
});
_motionLag.onChange([&]() {
_mouseStates.setVelocityScaleFactor(1 / (_motionLag + 0.0000001));
});
// Add the properties
addProperty(_rotationalFriction);
addProperty(_horizontalFriction);
addProperty(_verticalFriction);
addProperty(_followFocusNodeRotationDistance);
addProperty(_minimumAllowedDistance);
addProperty(_sensitivity);
addProperty(_motionLag);
}
OrbitalNavigator::~OrbitalNavigator()
{ }
void OrbitalNavigator::updateMouseStatesFromInput(const InputState& inputState,
double deltaTime)
{
_mouseStates.updateMouseStatesFromInput(inputState, deltaTime);
}
void OrbitalNavigator::updateCameraStateFromMouseStates(Camera& camera,
double deltaTime)
{
if (_focusNode) {
// Read the current state of the camera
glm::dvec3 camPos = camera.positionVec3();
glm::dvec3 centerPos = _focusNode->worldPosition();
// Follow focus nodes movement
glm::dvec3 focusNodeDiff = centerPos - _previousFocusNodePosition;
_previousFocusNodePosition = centerPos;
camPos += focusNodeDiff;
// Calculate a position handle based on the camera position in world space
SurfacePositionHandle posHandle = calculateSurfacePositionHandle(camPos);
// Decompose camera rotation so that we can handle global and local rotation
// individually. Then we combine them again when finished.
CameraRotationDecomposition camRot = decomposeCameraRotation(
camPos,
camera.rotationQuaternion(),
camera.lookUpVectorWorldSpace(),
camera.viewDirectionWorldSpace()
);
// Rotate with the object by finding a differential rotation from the previous
// to the current rotation
glm::dmat3 objectStateMatrix = _focusNode->worldRotationMatrix();
glm::dquat objectRotation = glm::quat_cast(objectStateMatrix);
glm::dquat focusNodeRotationDiff =
_previousFocusNodeRotation * glm::inverse(objectRotation);
_previousFocusNodeRotation = objectRotation;
// Interpolate rotation differential so that the camera rotates with the object
// only if close enough
focusNodeRotationDiff = interpolateRotationDifferential(
deltaTime,
1.0,
focusNodeRotationDiff,
centerPos,
camPos,
posHandle
);
// Update local rotation
camRot.localRotation = roll(deltaTime, camRot.localRotation);
camRot.localRotation = interpolateLocalRotation(deltaTime, camRot.localRotation);
camRot.localRotation = rotateLocally(deltaTime, camRot.localRotation);
// Horizontal translation
camPos = translateHorizontally(
deltaTime,
camPos,
centerPos,
focusNodeRotationDiff,
camRot.globalRotation,
posHandle
);
// Horizontal translation by focus node rotation
camPos = followFocusNodeRotation(
camPos,
centerPos,
focusNodeRotationDiff
);
// Recalculate posHandle since horizontal position changed
posHandle = calculateSurfacePositionHandle(camPos);
camRot.globalRotation = rotateGlobally(
camRot.globalRotation,
centerPos,
focusNodeRotationDiff,
camPos,
posHandle
);
// Rotate around the surface out direction
camRot.globalRotation = rotateHorizontally(
deltaTime,
camRot.globalRotation,
camPos,
posHandle
);
// Perform the vertical movements
camPos = translateVertically(deltaTime, camPos, centerPos, posHandle);
camPos = pushToSurface(
_minimumAllowedDistance,
camPos,
centerPos,
posHandle
);
// Update the camera state
camera.setPositionVec3(camPos);
camera.setRotation(camRot.globalRotation * camRot.localRotation);
}
}
void OrbitalNavigator::setFocusNode(SceneGraphNode* focusNode) {
_focusNode = focusNode;
if (_focusNode != nullptr) {
_previousFocusNodePosition = _focusNode->worldPosition();
_previousFocusNodeRotation = glm::quat_cast(_focusNode->worldRotationMatrix());
}
}
void OrbitalNavigator::startInterpolateCameraDirection(const Camera& camera) {
glm::dvec3 camPos = camera.positionVec3();
glm::dvec3 camDir = glm::normalize(
camera.rotationQuaternion() * glm::dvec3(0.0, 0.0, -1.0)
);
glm::dvec3 centerPos = _focusNode->worldPosition();
glm::dvec3 directionToCenter = glm::normalize(centerPos - camPos);
double angle = glm::angle(camDir, directionToCenter);
// Minimum is two second. Otherwise proportional to angle
_rotateToFocusNodeInterpolator.setInterpolationTime(glm::max(angle * 2.0, 2.0));
_rotateToFocusNodeInterpolator.start();
}
bool OrbitalNavigator::followingNodeRotation() const {
return _followRotationInterpolator.value() >= 1.0;
}
SceneGraphNode* OrbitalNavigator::focusNode() const {
return _focusNode;
}
OrbitalNavigator::CameraRotationDecomposition
OrbitalNavigator::decomposeCameraRotation(
const glm::dvec3& cameraPosition,
const glm::dquat& cameraRotation,
const glm::dvec3& cameraLookUp,
const glm::dvec3& cameraViewDirection)
{
glm::dmat4 inverseModelTransform = _focusNode->inverseModelTransform();
glm::dmat4 modelTransform = _focusNode->modelTransform();
glm::dvec3 cameraPositionModelSpace =
glm::dvec3(inverseModelTransform * glm::dvec4(cameraPosition, 1));
SurfacePositionHandle posHandle =
_focusNode->calculateSurfacePositionHandle(cameraPositionModelSpace);
glm::dvec3 directionFromSurfaceToCameraModelSpace =
posHandle.referenceSurfaceOutDirection;
glm::dvec3 directionFromSurfaceToCamera =
glm::normalize(glm::dmat3(modelTransform) *
directionFromSurfaceToCameraModelSpace);
// To avoid problem with lookup in up direction we adjust is with the view direction
glm::dmat4 lookAtMat = glm::lookAt(
glm::dvec3(0.0, 0.0, 0.0),
-directionFromSurfaceToCamera,
normalize(cameraViewDirection + cameraLookUp));
glm::dquat globalCameraRotation = glm::normalize(glm::quat_cast(inverse(lookAtMat)));
glm::dquat localCameraRotation = glm::inverse(globalCameraRotation) * cameraRotation;
return { localCameraRotation, globalCameraRotation };
}
glm::dquat OrbitalNavigator::roll(double deltaTime,
const glm::dquat& localCameraRotation) const
{
glm::dquat rollQuat = glm::angleAxis(
_mouseStates.localRollMouseVelocity().x * deltaTime,
glm::dvec3(0.0, 0.0, 1.0)
);
return localCameraRotation * rollQuat;
}
glm::dquat OrbitalNavigator::rotateLocally(double deltaTime,
const glm::dquat& localCameraRotation) const
{
glm::dvec3 eulerAngles(
_mouseStates.localRotationMouseVelocity().y,
_mouseStates.localRotationMouseVelocity().x,
0.0
);
glm::dquat rotationDiff = glm::dquat(eulerAngles * deltaTime);
return localCameraRotation * rotationDiff;
}
glm::dquat OrbitalNavigator::interpolateLocalRotation(
double deltaTime,
const glm::dquat& localCameraRotation)
{
if (_rotateToFocusNodeInterpolator.isInterpolating()) {
double t = _rotateToFocusNodeInterpolator.value();
_rotateToFocusNodeInterpolator.setDeltaTime(deltaTime);
_rotateToFocusNodeInterpolator.step();
glm::dquat result = glm::slerp(
localCameraRotation,
glm::dquat(glm::dvec3(0.0)),
glm::min(t * _rotateToFocusNodeInterpolator.deltaTimeScaled(), 1.0));
if (angle(result) < 0.01) {
_rotateToFocusNodeInterpolator.end();
}
return result;
}
else {
return localCameraRotation;
}
}
glm::dvec3 OrbitalNavigator::translateHorizontally(
double deltaTime,
const glm::dvec3& cameraPosition,
const glm::dvec3& objectPosition,
const glm::dquat& focusNodeRotationDiff,
const glm::dquat& globalCameraRotation,
const SurfacePositionHandle& positionHandle) const
{
glm::dmat4 modelTransform = _focusNode->modelTransform();
glm::dvec3 outDirection =
glm::normalize(glm::dmat3(modelTransform) *
positionHandle.referenceSurfaceOutDirection);
// Vector logic
glm::dvec3 posDiff = cameraPosition - objectPosition;
glm::dvec3 centerToReferenceSurface = glm::dmat3(modelTransform) *
positionHandle.centerToReferenceSurface;
glm::dvec3 centerToActualSurfaceModelSpace = positionHandle.centerToReferenceSurface +
positionHandle.referenceSurfaceOutDirection * positionHandle.heightToSurface;
glm::dvec3 centerToActualSurface = glm::dmat3(modelTransform) *
centerToActualSurfaceModelSpace;
glm::dvec3 actualSurfaceToCamera = posDiff - centerToActualSurface;
double distFromSurfaceToCamera = glm::length(actualSurfaceToCamera);
// Final values to be used
double distFromCenterToSurface = length(centerToActualSurface);
double distFromCenterToCamera = length(posDiff);
double speedScale =
distFromCenterToSurface > 0.0 ?
glm::clamp(distFromSurfaceToCamera / distFromCenterToSurface, 0.0, 1.0) :
1.0;
// Get rotation in camera space
glm::dvec3 eulerAngles = glm::dvec3(
-_mouseStates.globalRotationMouseVelocity().y * deltaTime,
-_mouseStates.globalRotationMouseVelocity().x * deltaTime,
0) * speedScale;
glm::dquat rotationDiffCamSpace = glm::dquat(eulerAngles);
// Transform to world space
glm::dquat rotationDiffWorldSpace =
globalCameraRotation *
rotationDiffCamSpace *
glm::inverse(globalCameraRotation);
// Rotate and find the difference vector
glm::dvec3 rotationDiffVec3 =
(distFromCenterToCamera * outDirection)
* rotationDiffWorldSpace
- (distFromCenterToCamera * outDirection);
// Add difference to position
return cameraPosition + rotationDiffVec3;
}
glm::dvec3 OrbitalNavigator::followFocusNodeRotation(
const glm::dvec3& cameraPosition,
const glm::dvec3& objectPosition,
const glm::dquat& focusNodeRotationDiff) const
{
glm::dvec3 posDiff = cameraPosition - objectPosition;
glm::dvec3 rotationDiffVec3AroundCenter =
posDiff
* focusNodeRotationDiff
- (posDiff);
return cameraPosition + rotationDiffVec3AroundCenter;
}
glm::dquat OrbitalNavigator::rotateGlobally(
const glm::dquat& globalCameraRotation,
const glm::dvec3& objectPosition,
const glm::dquat& focusNodeRotationDiff,
const glm::dvec3& cameraPosition,
const SurfacePositionHandle& positionHandle) const
{
glm::dmat4 modelTransform = _focusNode->modelTransform();
glm::dvec3 directionFromSurfaceToCamera =
glm::dmat3(modelTransform) * positionHandle.referenceSurfaceOutDirection;
glm::dvec3 lookUpWhenFacingSurface =
glm::inverse(focusNodeRotationDiff) *
globalCameraRotation * glm::dvec3(0.0, 1.0, 0.0);
glm::dmat4 lookAtMat = glm::lookAt(
glm::dvec3(0.0, 0.0, 0.0),
-directionFromSurfaceToCamera,
lookUpWhenFacingSurface);
return glm::normalize(glm::quat_cast(glm::inverse(lookAtMat)));
}
glm::dvec3 OrbitalNavigator::translateVertically(
double deltaTime,
const glm::dvec3& cameraPosition,
const glm::dvec3& objectPosition,
const SurfacePositionHandle& positionHandle) const
{
glm::dmat4 modelTransform = _focusNode->modelTransform();
glm::dvec3 posDiff = cameraPosition - objectPosition;
glm::dvec3 centerToReferenceSurface =
glm::dmat3(modelTransform) * positionHandle.centerToReferenceSurface;
glm::dvec3 centerToActualSurfaceModelSpace =
positionHandle.centerToReferenceSurface +
positionHandle.referenceSurfaceOutDirection * positionHandle.heightToSurface;
glm::dvec3 centerToActualSurface =
glm::dmat3(modelTransform) * centerToActualSurfaceModelSpace;
glm::dvec3 actualSurfaceToCamera = posDiff - centerToActualSurface;
return cameraPosition -
actualSurfaceToCamera * _mouseStates.truckMovementMouseVelocity().y * deltaTime;
}
glm::dquat OrbitalNavigator::rotateHorizontally(
double deltaTime,
const glm::dquat& globalCameraRotation,
const glm::dvec3& cameraPosition,
const SurfacePositionHandle& positionHandle) const
{
glm::dmat4 modelTransform = _focusNode->modelTransform();
glm::dvec3 directionFromSurfaceToCameraModelSpace =
positionHandle.referenceSurfaceOutDirection;
glm::dvec3 directionFromSurfaceToCamera =
glm::normalize(glm::dmat3(modelTransform) * directionFromSurfaceToCameraModelSpace);
glm::dquat cameraRollRotation =
glm::angleAxis(
_mouseStates.globalRollMouseVelocity().x *
deltaTime, directionFromSurfaceToCamera
);
return cameraRollRotation * globalCameraRotation;
}
glm::dvec3 OrbitalNavigator::pushToSurface(
double minHeightAboveGround,
const glm::dvec3& cameraPosition,
const glm::dvec3& objectPosition,
const SurfacePositionHandle& positionHandle) const
{
glm::dmat4 modelTransform = _focusNode->modelTransform();
glm::dvec3 posDiff = cameraPosition - objectPosition;
glm::dvec3 referenceSurfaceOutDirection =
glm::dmat3(modelTransform) * positionHandle.referenceSurfaceOutDirection;
glm::dvec3 centerToReferenceSurface =
glm::dmat3(modelTransform) * positionHandle.centerToReferenceSurface;
glm::dvec3 centerToActualSurfaceModelSpace =
positionHandle.centerToReferenceSurface +
positionHandle.referenceSurfaceOutDirection * positionHandle.heightToSurface;
glm::dvec3 centerToActualSurface =
glm::dmat3(modelTransform) * centerToActualSurfaceModelSpace;
glm::dvec3 actualSurfaceToCamera = posDiff - centerToActualSurface;
double surfaceToCameraSigned =
glm::length(actualSurfaceToCamera) *
glm::sign(dot(actualSurfaceToCamera, referenceSurfaceOutDirection));
return cameraPosition + referenceSurfaceOutDirection *
glm::max(minHeightAboveGround - surfaceToCameraSigned, 0.0);
}
glm::dquat OrbitalNavigator::interpolateRotationDifferential(
double deltaTime,
double interpolationTime,
const glm::dquat& rotationDiff,
const glm::dvec3& objectPosition,
const glm::dvec3& cameraPosition,
const SurfacePositionHandle& positionHandle)
{
glm::dmat4 modelTransform = _focusNode->modelTransform();
double maximumDistanceForRotation = glm::length(
glm::dmat3(modelTransform) * positionHandle.centerToReferenceSurface
) * _followFocusNodeRotationDistance;
double distanceToCamera = glm::length(cameraPosition - objectPosition);
// Interpolate with a negative delta time if distance is too large to follow
double interpolationSign = glm::sign(maximumDistanceForRotation - distanceToCamera);
_followRotationInterpolator.setInterpolationTime(interpolationTime);
_followRotationInterpolator.setDeltaTime(interpolationSign * deltaTime);
_followRotationInterpolator.step();
return glm::slerp(
glm::dquat(glm::dvec3(0.0)),
rotationDiff,
_followRotationInterpolator.value()
);
}
SurfacePositionHandle OrbitalNavigator::calculateSurfacePositionHandle(
const glm::dvec3 cameraPositionWorldSpace)
{
glm::dmat4 inverseModelTransform = _focusNode->inverseModelTransform();
glm::dvec3 cameraPositionModelSpace =
glm::dvec3(inverseModelTransform * glm::dvec4(cameraPositionWorldSpace, 1));
SurfacePositionHandle posHandle =
_focusNode->calculateSurfacePositionHandle(cameraPositionModelSpace);
return posHandle;
}
} // namespace interaction
} // namespace openspace

View File

@@ -66,8 +66,8 @@
#include <openspace/openspace.h>
#include <openspace/engine/openspaceengine.h>
#include <openspace/engine/wrapper/windowwrapper.h>
#include <openspace/interaction/interactionhandler.h>
#include <openspace/interaction/interactionmode.h>
#include <openspace/interaction/navigationhandler.h>
#include <openspace/interaction/keyframenavigator.h>
#include <openspace/scene/scenegraphnode.h>
#include <openspace/scripting/script_helper.h>
#include <openspace/util/time.h>
@@ -580,14 +580,16 @@ void ParallelConnection::dataMessageReceived(const std::vector<char>& messageCon
datamessagestructures::CameraKeyframe kf(buffer);
kf._timestamp = calculateBufferedKeyframeTime(kf._timestamp);
OsEng.interactionHandler().removeKeyframesAfter(kf._timestamp);
interaction::KeyframeInteractionMode::CameraPose pose;
OsEng.navigationHandler().keyframeNavigator().removeKeyframesAfter(
kf._timestamp);
interaction::KeyframeNavigator::CameraPose pose;
pose.focusNode = kf._focusNode;
pose.position = kf._position;
pose.rotation = kf._rotation;
pose.followFocusNodeRotation = kf._followNodeRotation;
OsEng.interactionHandler().addKeyframe(kf._timestamp, pose);
OsEng.navigationHandler().keyframeNavigator().addKeyframe(
kf._timestamp, pose);
break;
}
case datamessagestructures::Type::TimeData: {
@@ -755,7 +757,7 @@ void ParallelConnection::connectionStatusMessageReceived(const std::vector<char>
setStatus(status);
OsEng.interactionHandler().clearKeyframes();
OsEng.navigationHandler().keyframeNavigator().clearKeyframes();
OsEng.timeManager().clearKeyframes();
}
@@ -1012,7 +1014,7 @@ void ParallelConnection::sendScript(std::string script) {
}
void ParallelConnection::resetTimeOffset() {
OsEng.interactionHandler().clearKeyframes();
OsEng.navigationHandler().keyframeNavigator().clearKeyframes();
OsEng.timeManager().clearKeyframes();
std::lock_guard<std::mutex> latencyLock(_latencyMutex);
_latencyDiffs.clear();
@@ -1082,21 +1084,21 @@ const std::string& ParallelConnection::hostName() {
}
void ParallelConnection::sendCameraKeyframe() {
SceneGraphNode* focusNode = OsEng.interactionHandler().focusNode();
SceneGraphNode* focusNode = OsEng.navigationHandler().focusNode();
if (!focusNode) {
return;
}
// Create a keyframe with current position and orientation of camera
datamessagestructures::CameraKeyframe kf;
kf._position = OsEng.interactionHandler().focusNodeToCameraVector();
kf._position = OsEng.navigationHandler().focusNodeToCameraVector();
kf._followNodeRotation = OsEng.interactionHandler().interactionMode()->followingNodeRotation();
kf._followNodeRotation = OsEng.navigationHandler().orbitalNavigator().followingNodeRotation();
if (kf._followNodeRotation) {
kf._position = glm::inverse(focusNode->worldRotationMatrix()) * kf._position;
kf._rotation = OsEng.interactionHandler().focusNodeToCameraRotation();
kf._rotation = OsEng.navigationHandler().focusNodeToCameraRotation();
} else {
kf._rotation = OsEng.interactionHandler().camera()->rotationQuaternion();
kf._rotation = OsEng.navigationHandler().camera()->rotationQuaternion();
}
kf._focusNode = focusNode->name();

View File

@@ -26,7 +26,7 @@
#include <openspace/engine/openspaceengine.h>
#include <openspace/engine/virtualpropertymanager.h>
#include <openspace/interaction/interactionhandler.h>
#include <openspace/interaction/navigationhandler.h>
#include <openspace/rendering/renderengine.h>
#include <openspace/rendering/renderable.h>
#include <openspace/scene/scene.h>

View File

@@ -158,6 +158,17 @@ void Renderable::render(const RenderData& data, RendererTasks&) {
void Renderable::render(const RenderData&) {}
SurfacePositionHandle Renderable::calculateSurfacePositionHandle(
const glm::dvec3& targetModelSpace)
{
glm::dvec3 directionFromCenterToTarget = glm::normalize(targetModelSpace);
return {
directionFromCenterToTarget * static_cast<double>(boundingSphere()),
directionFromCenterToTarget,
0.0
};
}
void Renderable::setPscUniforms(ghoul::opengl::ProgramObject& program,
const Camera& camera,
const PowerScaledCoordinate& position)

View File

@@ -32,7 +32,7 @@
#include <openspace/engine/configurationmanager.h>
#include <openspace/engine/openspaceengine.h>
#include <openspace/engine/wrapper/windowwrapper.h>
#include <openspace/interaction/interactionhandler.h>
#include <openspace/interaction/navigationhandler.h>
#include <openspace/interaction/luaconsole.h>
#include <openspace/mission/missionmanager.h>
#include <openspace/performance/performancemanager.h>

View File

@@ -28,7 +28,7 @@
#include <openspace/engine/configurationmanager.h>
#include <openspace/engine/openspaceengine.h>
#include <openspace/engine/wrapper/windowwrapper.h>
#include <openspace/interaction/interactionhandler.h>
#include <openspace/interaction/navigationhandler.h>
#include <openspace/query/query.h>
#include <openspace/rendering/renderengine.h>
#include <openspace/scene/scenegraphnode.h>

View File

@@ -477,6 +477,21 @@ void SceneGraphNode::setDependencies(const std::vector<SceneGraphNode*>& depende
}
}
SurfacePositionHandle SceneGraphNode::calculateSurfacePositionHandle(
const glm::dvec3& targetModelSpace)
{
if (_renderable) {
return _renderable->calculateSurfacePositionHandle(targetModelSpace);
}
else {
return {
glm::dvec3(0.0, 0.0, 0.0),
glm::normalize(targetModelSpace),
0.0
};
}
}
const std::vector<SceneGraphNode*>& SceneGraphNode::dependencies() const {
return _dependencies;
}

View File

@@ -27,7 +27,6 @@
#include <openspace/util/syncbuffer.h>
#include <openspace/query/query.h>
#include <openspace/engine/openspaceengine.h>
#include <openspace/interaction/interactionhandler.h>
#include <glm/gtc/matrix_transform.hpp>