Divide InteractionHandler and Interaction modes into different files

This commit is contained in:
Erik Broberg
2016-06-22 14:42:24 -04:00
parent 6e5796107c
commit 60da53c00e
5 changed files with 607 additions and 496 deletions

View File

@@ -27,14 +27,15 @@
#include <openspace/interaction/keyboardcontroller.h>
#include <openspace/interaction/mousecontroller.h>
#include <openspace/interaction/interactionmode.h>
#include <openspace/network/parallelconnection.h>
#include <openspace/properties/propertyowner.h>
#include <openspace/properties/stringproperty.h>
#include <openspace/util/mouse.h>
#include <openspace/util/keys.h>
#include <list>
#include <modules/globebrowsing/globes/renderableglobe.h>
#include <mutex>
@@ -147,145 +148,6 @@ private:
#else // USE_OLD_INTERACTIONHANDLER
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);
// Mutators
void addKeyframe(const network::datamessagestructures::PositionKeyframe &kf);
void clearKeyframes();
// Accessors
const std::list<std::pair<Key, KeyModifier> >& getPressedKeys() const;
const std::list<MouseButton>& getPressedMouseButtons() const;
glm::dvec2 getMousePosition() const;
double getMouseScrollDelta() const;
std::vector<network::datamessagestructures::PositionKeyframe>& getKeyFrames() const;
bool isKeyPressed(std::pair<Key, KeyModifier> keyModPair) 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;
// Remote input via keyframes
std::vector<network::datamessagestructures::PositionKeyframe> _keyframes;
std::mutex _keyframeMutex;
};
class InteractionMode
{
public:
InteractionMode();
~InteractionMode();
// Mutators
virtual void setFocusNode(SceneGraphNode* focusNode);
// Accessors
SceneGraphNode* focusNode();
virtual void update(Camera& camera, const InputState& inputState, 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 scale) {
_scale = scale;
}
void set(T value, double dt) {
_targetValue = value;
_currentValue = _currentValue + (_targetValue - _currentValue) *
min(_scale * dt, 1.0); // less or equal to 1.0 keeps it stable
}
T get() {
return _currentValue;
}
private:
ScaleType _scale;
T _targetValue;
T _currentValue;
};
struct MouseState {
MouseState(double scale)
: velocity(scale)
, previousPosition(0.0, 0.0) {}
glm::dvec2 previousPosition;
DelayedVariable<glm::dvec2, double> velocity;
};
SceneGraphNode* _focusNode = nullptr;
};
class KeyframeInteractionMode : public InteractionMode
{
public:
KeyframeInteractionMode();
~KeyframeInteractionMode();
virtual void update(double deltaTime);
private:
double _currentKeyframeTime;
};
class OrbitalInteractionMode : public InteractionMode
{
public:
/*!
\param inputState
\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.
*/
OrbitalInteractionMode(double sensitivity, double velocityScaleFactor);
~OrbitalInteractionMode();
virtual void update(Camera& camera, const InputState& inputState, double deltaTime);
protected:
void updateMouseStatesFromInput(const InputState& inputState, double deltaTime);
void updateCameraStateFromMouseStates(Camera& camera);
double _sensitivity;
MouseState _globalRotationMouseState;
MouseState _localRotationMouseState;
MouseState _truckMovementMouseState;
MouseState _rollMouseState;
glm::dquat _localCameraRotation;
glm::dquat _globalCameraRotation;
};
class GlobeBrowsingInteractionMode : public OrbitalInteractionMode
{
public:
GlobeBrowsingInteractionMode(double sensitivity, double velocityScaleFactor);
~GlobeBrowsingInteractionMode();
virtual void setFocusNode(SceneGraphNode* focusNode);
virtual void update(Camera& camera, const InputState& inputState, double deltaTime);
private:
void updateCameraStateFromMouseStates(Camera& camera);
RenderableGlobe* _globe;
};
class InteractionHandler : public properties::PropertyOwner
{

View File

@@ -0,0 +1,191 @@
/*****************************************************************************************
* *
* OpenSpace *
* *
* Copyright (c) 2014-2016 *
* *
* 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 __INTERACTION_MODE_H__
#define __INTERACTION_MODE_H__
#include <openspace/interaction/keyboardcontroller.h>
#include <openspace/interaction/mousecontroller.h>
#include <openspace/network/parallelconnection.h>
#include <openspace/util/mouse.h>
#include <openspace/util/keys.h>
#include <modules/globebrowsing/globes/renderableglobe.h>
namespace openspace {
class Camera;
class SceneGraphNode;
namespace interaction {
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);
// Mutators
void addKeyframe(const network::datamessagestructures::PositionKeyframe &kf);
void clearKeyframes();
// Accessors
const std::list<std::pair<Key, KeyModifier> >& getPressedKeys() const;
const std::list<MouseButton>& getPressedMouseButtons() const;
glm::dvec2 getMousePosition() const;
double getMouseScrollDelta() const;
std::vector<network::datamessagestructures::PositionKeyframe>& getKeyFrames() const;
bool isKeyPressed(std::pair<Key, KeyModifier> keyModPair) 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;
// Remote input via keyframes
std::vector<network::datamessagestructures::PositionKeyframe> _keyframes;
std::mutex _keyframeMutex;
};
class InteractionMode
{
public:
InteractionMode();
~InteractionMode();
// Mutators
virtual void setFocusNode(SceneGraphNode* focusNode);
// Accessors
SceneGraphNode* focusNode();
virtual void update(Camera& camera, const InputState& inputState, 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 scale) {
_scale = scale;
}
void set(T value, double dt) {
_targetValue = value;
_currentValue = _currentValue + (_targetValue - _currentValue) *
min(_scale * dt, 1.0); // less or equal to 1.0 keeps it stable
}
T get() {
return _currentValue;
}
private:
ScaleType _scale;
T _targetValue;
T _currentValue;
};
struct MouseState {
MouseState(double scale)
: velocity(scale)
, previousPosition(0.0, 0.0) {}
glm::dvec2 previousPosition;
DelayedVariable<glm::dvec2, double> velocity;
};
SceneGraphNode* _focusNode = nullptr;
};
class KeyframeInteractionMode : public InteractionMode
{
public:
KeyframeInteractionMode();
~KeyframeInteractionMode();
virtual void update(double deltaTime);
private:
double _currentKeyframeTime;
};
class OrbitalInteractionMode : public InteractionMode
{
public:
/*!
\param inputState
\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.
*/
OrbitalInteractionMode(double sensitivity, double velocityScaleFactor);
~OrbitalInteractionMode();
virtual void update(Camera& camera, const InputState& inputState, double deltaTime);
protected:
void updateMouseStatesFromInput(const InputState& inputState, double deltaTime);
void updateCameraStateFromMouseStates(Camera& camera);
double _sensitivity;
MouseState _globalRotationMouseState;
MouseState _localRotationMouseState;
MouseState _truckMovementMouseState;
MouseState _rollMouseState;
glm::dquat _localCameraRotation;
glm::dquat _globalCameraRotation;
};
class GlobeBrowsingInteractionMode : public OrbitalInteractionMode
{
public:
GlobeBrowsingInteractionMode(double sensitivity, double velocityScaleFactor);
~GlobeBrowsingInteractionMode();
virtual void setFocusNode(SceneGraphNode* focusNode);
virtual void update(Camera& camera, const InputState& inputState, double deltaTime);
private:
void updateCameraStateFromMouseStates(Camera& camera);
RenderableGlobe* _globe;
};
} // namespace interaction
} // namespace openspace
#endif // __INTERACTION_MODE_H__

View File

@@ -35,6 +35,7 @@ set(OPENSPACE_SOURCE
${OPENSPACE_BASE_DIR}/src/interaction/controller.cpp
${OPENSPACE_BASE_DIR}/src/interaction/deviceidentifier.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/keyboardcontroller.cpp
${OPENSPACE_BASE_DIR}/src/interaction/luaconsole.cpp
@@ -109,6 +110,7 @@ set(OPENSPACE_HEADER
${OPENSPACE_BASE_DIR}/include/openspace/interaction/controller.h
${OPENSPACE_BASE_DIR}/include/openspace/interaction/deviceidentifier.h
${OPENSPACE_BASE_DIR}/include/openspace/interaction/interactionhandler.h
${OPENSPACE_BASE_DIR}/include/openspace/interaction/interactionmode.h
${OPENSPACE_BASE_DIR}/include/openspace/interaction/keyboardcontroller.h
${OPENSPACE_BASE_DIR}/include/openspace/interaction/luaconsole.h
${OPENSPACE_BASE_DIR}/include/openspace/interaction/mousecontroller.h

View File

@@ -613,363 +613,7 @@ void InteractionHandler::clearKeyframes(){
#else // USE_OLD_INTERACTIONHANDLER
// InputState
InputState::InputState() {
}
InputState::~InputState() {
}
void InputState::addKeyframe(const network::datamessagestructures::PositionKeyframe &kf) {
_keyframeMutex.lock();
//save a maximum of 10 samples (1 seconds of buffer)
if (_keyframes.size() >= 10) {
_keyframes.erase(_keyframes.begin());
}
_keyframes.push_back(kf);
_keyframeMutex.unlock();
}
void InputState::clearKeyframes() {
_keyframeMutex.lock();
_keyframes.clear();
_keyframeMutex.unlock();
}
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::isMouseButtonPressed(MouseButton mouseButton) const {
for (auto it = _mouseButtonsDown.begin(); it != _mouseButtonsDown.end(); it++) {
if (*it == mouseButton) {
return true;
}
}
return false;
}
InteractionMode::InteractionMode() {
}
InteractionMode::~InteractionMode() {
}
void InteractionMode::setFocusNode(SceneGraphNode* focusNode) {
_focusNode = focusNode;
}
SceneGraphNode* InteractionMode::focusNode() {
return _focusNode;
}
// KeyframeInteractionMode
KeyframeInteractionMode::KeyframeInteractionMode(){
}
KeyframeInteractionMode::~KeyframeInteractionMode() {
}
void KeyframeInteractionMode::update(double deltaTime) {
// TODO : Get keyframes from input state and use them to position and rotate the camera
}
// OrbitalInteractionMode
OrbitalInteractionMode::OrbitalInteractionMode(double sensitivity, double velocityScaleFactor)
: _sensitivity(sensitivity)
, _globalRotationMouseState(velocityScaleFactor)
, _localRotationMouseState(velocityScaleFactor)
, _truckMovementMouseState(velocityScaleFactor)
, _rollMouseState(velocityScaleFactor) {
}
OrbitalInteractionMode::~OrbitalInteractionMode() {
}
void OrbitalInteractionMode::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(
std::pair<Key, KeyModifier>(Key::LeftControl, KeyModifier::Control));
// Update the mouse states
if (button1Pressed) {
if (keyCtrlPressed) {
glm::dvec2 mousePositionDelta =
_localRotationMouseState.previousPosition - mousePosition;
_localRotationMouseState.velocity.set(mousePositionDelta * deltaTime * _sensitivity, deltaTime);
_globalRotationMouseState.previousPosition = mousePosition;
_globalRotationMouseState.velocity.set(glm::dvec2(0, 0), deltaTime);
}
else {
glm::dvec2 mousePositionDelta =
_globalRotationMouseState.previousPosition - mousePosition;
_globalRotationMouseState.velocity.set(mousePositionDelta * deltaTime * _sensitivity, deltaTime);
_localRotationMouseState.previousPosition = mousePosition;
_localRotationMouseState.velocity.set(glm::dvec2(0, 0), deltaTime);
}
}
else { // !button1Pressed
_localRotationMouseState.previousPosition = mousePosition;
_localRotationMouseState.velocity.set(glm::dvec2(0, 0), deltaTime);
_globalRotationMouseState.previousPosition = mousePosition;
_globalRotationMouseState.velocity.set(glm::dvec2(0, 0), deltaTime);
}
if (button2Pressed) {
glm::dvec2 mousePositionDelta =
_truckMovementMouseState.previousPosition - mousePosition;
_truckMovementMouseState.velocity.set(mousePositionDelta * deltaTime * _sensitivity, deltaTime);
}
else { // !button2Pressed
_truckMovementMouseState.previousPosition = mousePosition;
_truckMovementMouseState.velocity.set(glm::dvec2(0, 0), deltaTime);
}
if (button3Pressed) {
glm::dvec2 mousePositionDelta =
_rollMouseState.previousPosition - mousePosition;
_rollMouseState.velocity.set(mousePositionDelta * deltaTime * _sensitivity, deltaTime);
}
else { // !button3Pressed
_rollMouseState.previousPosition = mousePosition;
_rollMouseState.velocity.set(glm::dvec2(0, 0), deltaTime);
}
}
void OrbitalInteractionMode::updateCameraStateFromMouseStates(Camera& camera) {
if (_focusNode) {
// Declare variables to use in interaction calculations
glm::dvec3 centerPos = _focusNode->worldPosition().dvec3();
glm::dvec3 camPos = camera.positionVec3();
glm::dvec3 posDiff = camPos - centerPos;
glm::dvec3 newPosition = camPos;
{ // Do local rotation
glm::dvec3 eulerAngles(_localRotationMouseState.velocity.get().y, 0, 0);
glm::dquat rotationDiff = glm::dquat(eulerAngles);
_localCameraRotation = _localCameraRotation * rotationDiff;
}
{ // Do global rotation
glm::dvec3 eulerAngles(
-_globalRotationMouseState.velocity.get().y,
-_globalRotationMouseState.velocity.get().x,
0);
glm::dquat rotationDiffCamSpace = glm::dquat(eulerAngles);
glm::dquat newRotationCamspace =
_globalCameraRotation * rotationDiffCamSpace;
glm::dquat rotationDiffWorldSpace =
newRotationCamspace * glm::inverse(_globalCameraRotation);
glm::dvec3 rotationDiffVec3 = posDiff * rotationDiffWorldSpace - posDiff;
newPosition = camPos + rotationDiffVec3;
glm::dvec3 directionToCenter = glm::normalize(centerPos - newPosition);
glm::dvec3 lookUpWhenFacingCenter =
_globalCameraRotation * glm::dvec3(camera.lookUpVectorCameraSpace());
glm::dmat4 lookAtMat = glm::lookAt(
glm::dvec3(0, 0, 0),
directionToCenter,
lookUpWhenFacingCenter);
_globalCameraRotation =
glm::normalize(glm::quat_cast(glm::inverse(lookAtMat)));
}
{ // Move position towards or away from focus node
double boundingSphere = _focusNode->boundingSphere().lengthf();
glm::dvec3 centerToBoundingSphere =
glm::normalize(posDiff) *
static_cast<double>(_focusNode->boundingSphere().lengthf());
newPosition += -(posDiff - centerToBoundingSphere) *
_truckMovementMouseState.velocity.get().y;
}
{ // Do roll
glm::dquat cameraRollRotation =
glm::angleAxis(_rollMouseState.velocity.get().x, glm::normalize(posDiff));
_globalCameraRotation = cameraRollRotation * _globalCameraRotation;
}
// Update the camera state
camera.setRotation(_globalCameraRotation * _localCameraRotation);
camera.setPositionVec3(newPosition);
}
}
void OrbitalInteractionMode::update(Camera& camera, const InputState& inputState, double deltaTime) {
updateMouseStatesFromInput(inputState, deltaTime);
updateCameraStateFromMouseStates(camera);
}
GlobeBrowsingInteractionMode::GlobeBrowsingInteractionMode(double sensitivity, double velocityScaleFactor)
: OrbitalInteractionMode(sensitivity, velocityScaleFactor)
{
}
GlobeBrowsingInteractionMode::~GlobeBrowsingInteractionMode() {
}
void GlobeBrowsingInteractionMode::setFocusNode(SceneGraphNode* focusNode) {
_focusNode = focusNode;
Renderable* baseRenderable = _focusNode->renderable();
if (RenderableGlobe* globe = dynamic_cast<RenderableGlobe*>(baseRenderable)) {
_globe = globe;
}
else {
LWARNING("Focus node is not a renderable globe. GlobeBrowsingInteraction is not possible");
_globe = nullptr;
}
}
void GlobeBrowsingInteractionMode::updateCameraStateFromMouseStates(Camera& camera) {
if (_focusNode && _globe) {
// Declare variables to use in interaction calculations
glm::dvec3 centerPos = _focusNode->worldPosition().dvec3();
glm::dvec3 camPos = camera.positionVec3();
glm::dvec3 posDiff = camPos - centerPos;
glm::dvec3 newPosition = camPos;
glm::dvec3 centerToSurface = _globe->geodeticSurfaceProjection(camPos);
glm::dvec3 surfaceToCamera = camPos - (centerPos + centerToSurface);
glm::dvec3 directionFromSurfaceToCamera = glm::normalize(surfaceToCamera);
double distFromCenterToSurface =
glm::length(centerToSurface);
double distFromSurfaceToCamera = glm::length(surfaceToCamera);
double distFromCenterToCamera = glm::length(posDiff);
{ // Do local rotation
glm::dvec3 eulerAngles(_localRotationMouseState.velocity.get().y, 0, 0);
glm::dquat rotationDiff = glm::dquat(eulerAngles);
_localCameraRotation = _localCameraRotation * rotationDiff;
}
{ // Do global rotation
glm::dvec3 eulerAngles(
-_globalRotationMouseState.velocity.get().y,
-_globalRotationMouseState.velocity.get().x,
0);
glm::dquat rotationDiffCamSpace = glm::dquat(eulerAngles);
glm::dquat rotationDiffWorldSpace =
_globalCameraRotation *
rotationDiffCamSpace *
glm::inverse(_globalCameraRotation);
glm::dvec3 rotationDiffVec3 =
(distFromCenterToCamera * directionFromSurfaceToCamera)
* rotationDiffWorldSpace
- (distFromCenterToCamera * directionFromSurfaceToCamera);
rotationDiffVec3 *= glm::clamp(distFromSurfaceToCamera / distFromCenterToSurface, 0.0, 1.0);
newPosition = camPos + rotationDiffVec3;
glm::dvec3 newCenterToSurface = _globe->geodeticSurfaceProjection(newPosition);
glm::dvec3 newSurfaceToCamera = newPosition - (centerPos + newCenterToSurface);
glm::dvec3 newDirectionFromSurfaceToCamera = glm::normalize(newSurfaceToCamera);
glm::dvec3 lookUpWhenFacingSurface =
_globalCameraRotation * glm::dvec3(camera.lookUpVectorCameraSpace());
glm::dmat4 lookAtMat = glm::lookAt(
glm::dvec3(0, 0, 0),
-newDirectionFromSurfaceToCamera,
lookUpWhenFacingSurface);
_globalCameraRotation =
glm::normalize(glm::quat_cast(glm::inverse(lookAtMat)));
}
{ // Move position towards or away from focus node
newPosition += -(posDiff - centerToSurface) *
_truckMovementMouseState.velocity.get().y;
}
{ // Do roll
glm::dquat cameraRollRotation =
glm::angleAxis(_rollMouseState.velocity.get().x, directionFromSurfaceToCamera);
_globalCameraRotation = cameraRollRotation * _globalCameraRotation;
}
// Update the camera state
camera.setRotation(_globalCameraRotation * _localCameraRotation);
camera.setPositionVec3(newPosition);
}
}
void GlobeBrowsingInteractionMode::update(Camera& camera, const InputState& inputState, double deltaTime) {
updateMouseStatesFromInput(inputState, deltaTime);
updateCameraStateFromMouseStates(camera);
}
// InteractionHandler
InteractionHandler::InteractionHandler()

View File

@@ -0,0 +1,412 @@
/*****************************************************************************************
* *
* OpenSpace *
* *
* Copyright (c) 2014-2016 *
* *
* 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/util/time.h>
#include <openspace/util/keys.h>
#include <ghoul/logging/logmanager.h>
#include <glm/gtx/quaternion.hpp>
namespace {
const std::string _loggerCat = "InteractionMode";
}
namespace openspace {
namespace interaction {
// InputState
InputState::InputState() {
}
InputState::~InputState() {
}
void InputState::addKeyframe(const network::datamessagestructures::PositionKeyframe &kf) {
_keyframeMutex.lock();
//save a maximum of 10 samples (1 seconds of buffer)
if (_keyframes.size() >= 10) {
_keyframes.erase(_keyframes.begin());
}
_keyframes.push_back(kf);
_keyframeMutex.unlock();
}
void InputState::clearKeyframes() {
_keyframeMutex.lock();
_keyframes.clear();
_keyframeMutex.unlock();
}
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::isMouseButtonPressed(MouseButton mouseButton) const {
for (auto it = _mouseButtonsDown.begin(); it != _mouseButtonsDown.end(); it++) {
if (*it == mouseButton) {
return true;
}
}
return false;
}
InteractionMode::InteractionMode() {
}
InteractionMode::~InteractionMode() {
}
void InteractionMode::setFocusNode(SceneGraphNode* focusNode) {
_focusNode = focusNode;
}
SceneGraphNode* InteractionMode::focusNode() {
return _focusNode;
}
// KeyframeInteractionMode
KeyframeInteractionMode::KeyframeInteractionMode(){
}
KeyframeInteractionMode::~KeyframeInteractionMode() {
}
void KeyframeInteractionMode::update(double deltaTime) {
// TODO : Get keyframes from input state and use them to position and rotate the camera
}
// OrbitalInteractionMode
OrbitalInteractionMode::OrbitalInteractionMode(double sensitivity, double velocityScaleFactor)
: _sensitivity(sensitivity)
, _globalRotationMouseState(velocityScaleFactor)
, _localRotationMouseState(velocityScaleFactor)
, _truckMovementMouseState(velocityScaleFactor)
, _rollMouseState(velocityScaleFactor) {
}
OrbitalInteractionMode::~OrbitalInteractionMode() {
}
void OrbitalInteractionMode::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(
std::pair<Key, KeyModifier>(Key::LeftControl, KeyModifier::Control));
// Update the mouse states
if (button1Pressed) {
if (keyCtrlPressed) {
glm::dvec2 mousePositionDelta =
_localRotationMouseState.previousPosition - mousePosition;
_localRotationMouseState.velocity.set(mousePositionDelta * deltaTime * _sensitivity, deltaTime);
_globalRotationMouseState.previousPosition = mousePosition;
_globalRotationMouseState.velocity.set(glm::dvec2(0, 0), deltaTime);
}
else {
glm::dvec2 mousePositionDelta =
_globalRotationMouseState.previousPosition - mousePosition;
_globalRotationMouseState.velocity.set(mousePositionDelta * deltaTime * _sensitivity, deltaTime);
_localRotationMouseState.previousPosition = mousePosition;
_localRotationMouseState.velocity.set(glm::dvec2(0, 0), deltaTime);
}
}
else { // !button1Pressed
_localRotationMouseState.previousPosition = mousePosition;
_localRotationMouseState.velocity.set(glm::dvec2(0, 0), deltaTime);
_globalRotationMouseState.previousPosition = mousePosition;
_globalRotationMouseState.velocity.set(glm::dvec2(0, 0), deltaTime);
}
if (button2Pressed) {
glm::dvec2 mousePositionDelta =
_truckMovementMouseState.previousPosition - mousePosition;
_truckMovementMouseState.velocity.set(mousePositionDelta * deltaTime * _sensitivity, deltaTime);
}
else { // !button2Pressed
_truckMovementMouseState.previousPosition = mousePosition;
_truckMovementMouseState.velocity.set(glm::dvec2(0, 0), deltaTime);
}
if (button3Pressed) {
glm::dvec2 mousePositionDelta =
_rollMouseState.previousPosition - mousePosition;
_rollMouseState.velocity.set(mousePositionDelta * deltaTime * _sensitivity, deltaTime);
}
else { // !button3Pressed
_rollMouseState.previousPosition = mousePosition;
_rollMouseState.velocity.set(glm::dvec2(0, 0), deltaTime);
}
}
void OrbitalInteractionMode::updateCameraStateFromMouseStates(Camera& camera) {
if (_focusNode) {
// Declare variables to use in interaction calculations
glm::dvec3 centerPos = _focusNode->worldPosition().dvec3();
glm::dvec3 camPos = camera.positionVec3();
glm::dvec3 posDiff = camPos - centerPos;
glm::dvec3 newPosition = camPos;
{ // Do local rotation
glm::dvec3 eulerAngles(_localRotationMouseState.velocity.get().y, 0, 0);
glm::dquat rotationDiff = glm::dquat(eulerAngles);
_localCameraRotation = _localCameraRotation * rotationDiff;
}
{ // Do global rotation
glm::dvec3 eulerAngles(
-_globalRotationMouseState.velocity.get().y,
-_globalRotationMouseState.velocity.get().x,
0);
glm::dquat rotationDiffCamSpace = glm::dquat(eulerAngles);
glm::dquat newRotationCamspace =
_globalCameraRotation * rotationDiffCamSpace;
glm::dquat rotationDiffWorldSpace =
newRotationCamspace * glm::inverse(_globalCameraRotation);
glm::dvec3 rotationDiffVec3 = posDiff * rotationDiffWorldSpace - posDiff;
newPosition = camPos + rotationDiffVec3;
glm::dvec3 directionToCenter = glm::normalize(centerPos - newPosition);
glm::dvec3 lookUpWhenFacingCenter =
_globalCameraRotation * glm::dvec3(camera.lookUpVectorCameraSpace());
glm::dmat4 lookAtMat = glm::lookAt(
glm::dvec3(0, 0, 0),
directionToCenter,
lookUpWhenFacingCenter);
_globalCameraRotation =
glm::normalize(glm::quat_cast(glm::inverse(lookAtMat)));
}
{ // Move position towards or away from focus node
double boundingSphere = _focusNode->boundingSphere().lengthf();
glm::dvec3 centerToBoundingSphere =
glm::normalize(posDiff) *
static_cast<double>(_focusNode->boundingSphere().lengthf());
newPosition += -(posDiff - centerToBoundingSphere) *
_truckMovementMouseState.velocity.get().y;
}
{ // Do roll
glm::dquat cameraRollRotation =
glm::angleAxis(_rollMouseState.velocity.get().x, glm::normalize(posDiff));
_globalCameraRotation = cameraRollRotation * _globalCameraRotation;
}
// Update the camera state
camera.setRotation(_globalCameraRotation * _localCameraRotation);
camera.setPositionVec3(newPosition);
}
}
void OrbitalInteractionMode::update(Camera& camera, const InputState& inputState, double deltaTime) {
updateMouseStatesFromInput(inputState, deltaTime);
updateCameraStateFromMouseStates(camera);
}
GlobeBrowsingInteractionMode::GlobeBrowsingInteractionMode(double sensitivity, double velocityScaleFactor)
: OrbitalInteractionMode(sensitivity, velocityScaleFactor)
{
}
GlobeBrowsingInteractionMode::~GlobeBrowsingInteractionMode() {
}
void GlobeBrowsingInteractionMode::setFocusNode(SceneGraphNode* focusNode) {
_focusNode = focusNode;
Renderable* baseRenderable = _focusNode->renderable();
if (RenderableGlobe* globe = dynamic_cast<RenderableGlobe*>(baseRenderable)) {
_globe = globe;
}
else {
LWARNING("Focus node is not a renderable globe. GlobeBrowsingInteraction is not possible");
_globe = nullptr;
}
}
void GlobeBrowsingInteractionMode::updateCameraStateFromMouseStates(Camera& camera) {
if (_focusNode && _globe) {
// Declare variables to use in interaction calculations
glm::dvec3 centerPos = _focusNode->worldPosition().dvec3();
glm::dvec3 camPos = camera.positionVec3();
glm::dvec3 posDiff = camPos - centerPos;
glm::dvec3 newPosition = camPos;
glm::dvec3 centerToSurface = _globe->geodeticSurfaceProjection(camPos);
glm::dvec3 surfaceToCamera = camPos - (centerPos + centerToSurface);
glm::dvec3 directionFromSurfaceToCamera = glm::normalize(surfaceToCamera);
double distFromCenterToSurface =
glm::length(centerToSurface);
double distFromSurfaceToCamera = glm::length(surfaceToCamera);
double distFromCenterToCamera = glm::length(posDiff);
{ // Do local rotation
glm::dvec3 eulerAngles(_localRotationMouseState.velocity.get().y, 0, 0);
glm::dquat rotationDiff = glm::dquat(eulerAngles);
_localCameraRotation = _localCameraRotation * rotationDiff;
}
{ // Do global rotation
glm::dvec3 eulerAngles(
-_globalRotationMouseState.velocity.get().y,
-_globalRotationMouseState.velocity.get().x,
0);
glm::dquat rotationDiffCamSpace = glm::dquat(eulerAngles);
glm::dquat rotationDiffWorldSpace =
_globalCameraRotation *
rotationDiffCamSpace *
glm::inverse(_globalCameraRotation);
glm::dvec3 rotationDiffVec3 =
(distFromCenterToCamera * directionFromSurfaceToCamera)
* rotationDiffWorldSpace
- (distFromCenterToCamera * directionFromSurfaceToCamera);
rotationDiffVec3 *= glm::clamp(distFromSurfaceToCamera / distFromCenterToSurface, 0.0, 1.0);
newPosition = camPos + rotationDiffVec3;
glm::dvec3 newCenterToSurface = _globe->geodeticSurfaceProjection(newPosition);
glm::dvec3 newSurfaceToCamera = newPosition - (centerPos + newCenterToSurface);
glm::dvec3 newDirectionFromSurfaceToCamera = glm::normalize(newSurfaceToCamera);
glm::dvec3 lookUpWhenFacingSurface =
_globalCameraRotation * glm::dvec3(camera.lookUpVectorCameraSpace());
glm::dmat4 lookAtMat = glm::lookAt(
glm::dvec3(0, 0, 0),
-newDirectionFromSurfaceToCamera,
lookUpWhenFacingSurface);
_globalCameraRotation =
glm::normalize(glm::quat_cast(glm::inverse(lookAtMat)));
}
{ // Move position towards or away from focus node
newPosition += -(posDiff - centerToSurface) *
_truckMovementMouseState.velocity.get().y;
}
{ // Do roll
glm::dquat cameraRollRotation =
glm::angleAxis(_rollMouseState.velocity.get().x, directionFromSurfaceToCamera);
_globalCameraRotation = cameraRollRotation * _globalCameraRotation;
}
// Update the camera state
camera.setRotation(_globalCameraRotation * _localCameraRotation);
camera.setPositionVec3(newPosition);
}
}
void GlobeBrowsingInteractionMode::update(Camera& camera, const InputState& inputState, double deltaTime) {
updateMouseStatesFromInput(inputState, deltaTime);
updateCameraStateFromMouseStates(camera);
}
} // namespace interaction
} // namespace openspace