mirror of
https://github.com/OpenSpace/OpenSpace.git
synced 2026-01-07 12:10:52 -06:00
Merge branch 'feature/globebrowsing' of github.com:OpenSpace/OpenSpace-Development into feature/globebrowsing
This commit is contained in:
@@ -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
|
||||
{
|
||||
@@ -332,9 +194,14 @@ public:
|
||||
void mousePositionCallback(double x, double y);
|
||||
void mouseScrollWheelCallback(double pos);
|
||||
|
||||
void saveCameraPosition(const std::string& filepath = "");
|
||||
void restoreCameraPosition(const std::string& filepath = "");
|
||||
|
||||
private:
|
||||
void setInteractionMode(std::shared_ptr<InteractionMode> interactionMode);
|
||||
|
||||
bool _cameraUpdatedFromScript = false;
|
||||
|
||||
std::multimap<KeyWithModifier, std::string > _keyLua;
|
||||
|
||||
std::unique_ptr<InputState> _inputState;
|
||||
@@ -348,7 +215,6 @@ private:
|
||||
// Properties
|
||||
properties::StringProperty _origin;
|
||||
properties::StringProperty _coordinateSystem;
|
||||
|
||||
};
|
||||
|
||||
#endif // USE_OLD_INTERACTIONHANDLER
|
||||
|
||||
197
include/openspace/interaction/interactionmode.h
Normal file
197
include/openspace/interaction/interactionmode.h
Normal file
@@ -0,0 +1,197 @@
|
||||
/*****************************************************************************************
|
||||
* *
|
||||
* 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;
|
||||
virtual void initialize(const Camera& camera) = 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);
|
||||
virtual void initialize(const Camera& camera);
|
||||
|
||||
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);
|
||||
virtual void initialize(const Camera& camera);
|
||||
|
||||
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__
|
||||
@@ -38,6 +38,8 @@
|
||||
#include <glm/gtc/type_ptr.hpp>
|
||||
#include <glm/gtc/quaternion.hpp>
|
||||
|
||||
#include <iostream>
|
||||
|
||||
namespace openspace {
|
||||
/**
|
||||
This class still needs some more love. Suggested improvements:
|
||||
@@ -122,6 +124,9 @@ namespace openspace {
|
||||
void serialize(SyncBuffer* syncBuffer);
|
||||
void deserialize(SyncBuffer* syncBuffer);
|
||||
|
||||
void serialize(std::ostream& os) const;
|
||||
void deserialize(std::istream& is);
|
||||
|
||||
/**
|
||||
Handles SGCT's internal matrices. Also caches a calculated viewProjection
|
||||
matrix. This is the data that is different for different cameras within
|
||||
@@ -171,6 +176,7 @@ namespace openspace {
|
||||
const glm::mat4& projectionMatrix() const;
|
||||
[[deprecated("Replaced by Camera::SgctInternal::viewProjectionMatrix()")]]
|
||||
const glm::mat4& viewProjectionMatrix() const;
|
||||
|
||||
private:
|
||||
/**
|
||||
Class encapsulating data that needs to be synched between SGCT nodes.
|
||||
|
||||
@@ -177,7 +177,7 @@ namespace openspace {
|
||||
_chunkedLodGlobe->lodScaleFactor = lodScaleFactor.value();
|
||||
_chunkedLodGlobe->atmosphereEnabled = atmosphereEnabled.value();
|
||||
|
||||
_tileProviderManager->prerender();
|
||||
_tileProviderManager->update();
|
||||
}
|
||||
|
||||
glm::dvec3 RenderableGlobe::projectOnEllipsoid(glm::dvec3 position) {
|
||||
|
||||
@@ -138,11 +138,11 @@ namespace openspace {
|
||||
return _layerCategories[category];
|
||||
}
|
||||
|
||||
void TileProviderManager::prerender() {
|
||||
void TileProviderManager::update() {
|
||||
for each (auto layerCategory in _layerCategories) {
|
||||
for each (auto tileProviderWithName in layerCategory) {
|
||||
if (tileProviderWithName.isActive) {
|
||||
tileProviderWithName.tileProvider->prerender();
|
||||
tileProviderWithName.tileProvider->update();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -64,7 +64,7 @@ namespace openspace {
|
||||
const std::vector<std::shared_ptr<TileProvider> >
|
||||
getActivatedLayerCategory(LayeredTextures::TextureCategory);
|
||||
|
||||
void prerender();
|
||||
void update();
|
||||
|
||||
private:
|
||||
static void initTexures(std::vector<TileProviderWithName>& destination,
|
||||
|
||||
@@ -109,8 +109,8 @@ namespace openspace {
|
||||
|
||||
TileDepthTransform TemporalTileProvider::depthTransform() {
|
||||
if (_currentTileProvider == nullptr) {
|
||||
LDEBUG("Warning: had to call prerender from depthTransform()");
|
||||
prerender();
|
||||
LDEBUG("Warning: had to call update from depthTransform()");
|
||||
update();
|
||||
}
|
||||
|
||||
return _currentTileProvider->depthTransform();
|
||||
@@ -118,8 +118,8 @@ namespace openspace {
|
||||
|
||||
Tile::Status TemporalTileProvider::getTileStatus(const ChunkIndex& chunkIndex) {
|
||||
if (_currentTileProvider == nullptr) {
|
||||
LDEBUG("Warning: had to call prerender from getTileStatus()");
|
||||
prerender();
|
||||
LDEBUG("Warning: had to call update from getTileStatus()");
|
||||
update();
|
||||
}
|
||||
|
||||
return _currentTileProvider->getTileStatus(chunkIndex);
|
||||
@@ -127,8 +127,8 @@ namespace openspace {
|
||||
|
||||
Tile TemporalTileProvider::getTile(const ChunkIndex& chunkIndex) {
|
||||
if (_currentTileProvider == nullptr) {
|
||||
LDEBUG("Warning: had to call prerender from getTile()");
|
||||
prerender();
|
||||
LDEBUG("Warning: had to call update from getTile()");
|
||||
update();
|
||||
}
|
||||
|
||||
return _currentTileProvider->getTile(chunkIndex);
|
||||
@@ -136,15 +136,15 @@ namespace openspace {
|
||||
|
||||
|
||||
|
||||
void TemporalTileProvider::prerender() {
|
||||
void TemporalTileProvider::update() {
|
||||
_currentTileProvider = getTileProvider();
|
||||
_currentTileProvider->prerender();
|
||||
_currentTileProvider->update();
|
||||
}
|
||||
|
||||
std::shared_ptr<AsyncTileDataProvider> TemporalTileProvider::getAsyncTileReader() {
|
||||
if (_currentTileProvider == nullptr) {
|
||||
LDEBUG("Warning: had to call prerender from getAsyncTileReader()");
|
||||
prerender();
|
||||
LDEBUG("Warning: had to call update from getAsyncTileReader()");
|
||||
update();
|
||||
}
|
||||
|
||||
return _currentTileProvider->getAsyncTileReader();
|
||||
|
||||
@@ -123,7 +123,7 @@ namespace openspace {
|
||||
virtual Tile getTile(const ChunkIndex& chunkIndex);
|
||||
virtual Tile::Status getTileStatus(const ChunkIndex& chunkIndex);
|
||||
virtual TileDepthTransform depthTransform();
|
||||
virtual void prerender();
|
||||
virtual void update();
|
||||
virtual std::shared_ptr<AsyncTileDataProvider> getAsyncTileReader();
|
||||
|
||||
|
||||
|
||||
@@ -68,7 +68,7 @@ namespace openspace {
|
||||
}
|
||||
|
||||
|
||||
void CachingTileProvider::prerender() {
|
||||
void CachingTileProvider::update() {
|
||||
initTexturesFromLoadedData();
|
||||
if (_framesSinceLastRequestFlush++ > _framesUntilRequestFlush) {
|
||||
clearRequestQueue();
|
||||
|
||||
@@ -70,7 +70,7 @@ namespace openspace {
|
||||
virtual Tile getTile(const ChunkIndex& chunkIndex) = 0;
|
||||
virtual Tile::Status getTileStatus(const ChunkIndex& index) = 0;
|
||||
virtual TileDepthTransform depthTransform() = 0;
|
||||
virtual void prerender() = 0;
|
||||
virtual void update() = 0;
|
||||
virtual std::shared_ptr<AsyncTileDataProvider> getAsyncTileReader() = 0;
|
||||
};
|
||||
|
||||
@@ -96,7 +96,7 @@ namespace openspace {
|
||||
virtual Tile getTile(const ChunkIndex& chunkIndex);
|
||||
virtual Tile::Status getTileStatus(const ChunkIndex& index);
|
||||
virtual TileDepthTransform depthTransform();
|
||||
virtual void prerender();
|
||||
virtual void update();
|
||||
virtual std::shared_ptr<AsyncTileDataProvider> getAsyncTileReader();
|
||||
|
||||
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -36,6 +36,8 @@
|
||||
|
||||
#include <glm/gtx/quaternion.hpp>
|
||||
|
||||
#include <fstream>
|
||||
|
||||
namespace {
|
||||
const std::string _loggerCat = "InteractionHandler";
|
||||
}
|
||||
@@ -613,380 +615,6 @@ 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
|
||||
// Shrink interaction ellipsoid to enable interaction below height = 0
|
||||
double ellipsoidShrinkTerm = 10000.0;
|
||||
double minHeightAboveGround = 100.0;
|
||||
|
||||
glm::dvec3 centerPos = _focusNode->worldPosition().dvec3();
|
||||
glm::dvec3 camPos = camera.positionVec3();
|
||||
glm::dvec3 posDiff = camPos - centerPos;
|
||||
|
||||
glm::dvec3 directionFromSurfaceToCamera =
|
||||
_globe->ellipsoid().geodeticSurfaceNormal(
|
||||
_globe->ellipsoid().cartesianToGeodetic2(camPos));
|
||||
glm::dvec3 centerToEllipsoidSurface = _globe->projectOnEllipsoid(camPos) -
|
||||
directionFromSurfaceToCamera * ellipsoidShrinkTerm;
|
||||
glm::dvec3 ellipsoidSurfaceToCamera = camPos - (centerPos + centerToEllipsoidSurface);
|
||||
|
||||
double distFromCenterToSurface =
|
||||
glm::length(centerToEllipsoidSurface);
|
||||
double distFromEllipsoidSurfaceToCamera = glm::length(ellipsoidSurfaceToCamera);
|
||||
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 = glm::dvec3(
|
||||
-_globalRotationMouseState.velocity.get().y,
|
||||
-_globalRotationMouseState.velocity.get().x,
|
||||
0) * glm::clamp(distFromEllipsoidSurfaceToCamera / 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 = camPos + rotationDiffVec3;
|
||||
|
||||
directionFromSurfaceToCamera =
|
||||
_globe->ellipsoid().geodeticSurfaceNormal(
|
||||
_globe->ellipsoid().cartesianToGeodetic2(camPos));
|
||||
centerToEllipsoidSurface = _globe->projectOnEllipsoid(camPos) -
|
||||
directionFromSurfaceToCamera * ellipsoidShrinkTerm;
|
||||
ellipsoidSurfaceToCamera = camPos - (centerPos + centerToEllipsoidSurface);
|
||||
|
||||
glm::dvec3 lookUpWhenFacingSurface =
|
||||
_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
|
||||
distFromEllipsoidSurfaceToCamera = glm::length(ellipsoidSurfaceToCamera);
|
||||
camPos += -directionFromSurfaceToCamera * distFromEllipsoidSurfaceToCamera *
|
||||
_truckMovementMouseState.velocity.get().y;
|
||||
}
|
||||
{ // Do roll
|
||||
glm::dquat cameraRollRotation =
|
||||
glm::angleAxis(_rollMouseState.velocity.get().x, directionFromSurfaceToCamera);
|
||||
_globalCameraRotation = cameraRollRotation * _globalCameraRotation;
|
||||
}
|
||||
{ // Push up to surface
|
||||
ellipsoidSurfaceToCamera = camPos - (centerPos + centerToEllipsoidSurface);
|
||||
|
||||
distFromEllipsoidSurfaceToCamera = glm::length(ellipsoidSurfaceToCamera);
|
||||
double heightToSurface = _globe->getHeight(camPos) + ellipsoidShrinkTerm;
|
||||
double heightToSurfaceAndPadding = heightToSurface + minHeightAboveGround;
|
||||
camPos += directionFromSurfaceToCamera *
|
||||
glm::max(heightToSurfaceAndPadding - distFromEllipsoidSurfaceToCamera, 0.0);
|
||||
}
|
||||
// Update the camera state
|
||||
camera.setRotation(_globalCameraRotation * _localCameraRotation);
|
||||
camera.setPositionVec3(camPos);
|
||||
}
|
||||
}
|
||||
|
||||
void GlobeBrowsingInteractionMode::update(Camera& camera, const InputState& inputState, double deltaTime) {
|
||||
updateMouseStatesFromInput(inputState, deltaTime);
|
||||
updateCameraStateFromMouseStates(camera);
|
||||
}
|
||||
|
||||
// InteractionHandler
|
||||
InteractionHandler::InteractionHandler()
|
||||
@@ -1060,7 +688,13 @@ void InteractionHandler::unlockControls() {
|
||||
void InteractionHandler::update(double deltaTime) {
|
||||
ghoul_assert(_inputState != nullptr, "InputState cannot be null!");
|
||||
ghoul_assert(_camera != nullptr, "Camera cannot be null!");
|
||||
_currentInteractionMode->update(*_camera, *_inputState, deltaTime);
|
||||
|
||||
if (_cameraUpdatedFromScript) {
|
||||
_cameraUpdatedFromScript = false;
|
||||
}
|
||||
else {
|
||||
_currentInteractionMode->update(*_camera, *_inputState, deltaTime);
|
||||
}
|
||||
}
|
||||
|
||||
SceneGraphNode* const InteractionHandler::focusNode() const {
|
||||
@@ -1101,6 +735,39 @@ void InteractionHandler::keyboardCallback(Key key, KeyModifier modifier, KeyActi
|
||||
}
|
||||
}
|
||||
|
||||
void InteractionHandler::saveCameraPosition(const std::string& filepath) {
|
||||
if (!filepath.empty()) {
|
||||
auto fullpath = absPath(filepath);
|
||||
LDEBUG("Saving camera position: " << fullpath);
|
||||
std::ofstream ofs(fullpath.c_str());
|
||||
_camera->serialize(ofs);
|
||||
ofs.close();
|
||||
}
|
||||
}
|
||||
|
||||
void InteractionHandler::restoreCameraPosition(const std::string& filepath) {
|
||||
if (!filepath.empty()) {
|
||||
auto fullpath = absPath(filepath);
|
||||
LDEBUG("Reading camera position: " << fullpath);
|
||||
std::ifstream ifs(fullpath.c_str());
|
||||
Camera c;
|
||||
c.deserialize(ifs);
|
||||
ifs.close();
|
||||
|
||||
// uff, need to do this ...
|
||||
c.preSynchronization();
|
||||
c.postSynchronizationPreDraw();
|
||||
|
||||
auto p = c.positionVec3();
|
||||
auto r = c.rotationQuaternion();
|
||||
|
||||
_camera->setPositionVec3(p);
|
||||
_camera->setRotation(r);
|
||||
_currentInteractionMode->initialize(*_camera);
|
||||
_cameraUpdatedFromScript = true;
|
||||
}
|
||||
}
|
||||
|
||||
void InteractionHandler::resetKeyBindings() {
|
||||
_keyLua.clear();
|
||||
}
|
||||
@@ -1133,6 +800,18 @@ scripting::ScriptEngine::LuaLibrary InteractionHandler::luaLibrary() {
|
||||
&luascriptfunctions::setInteractionMode,
|
||||
"string",
|
||||
"Set the interaction mode for the camera"
|
||||
},
|
||||
{
|
||||
"saveCameraPosition",
|
||||
&luascriptfunctions::saveCameraPosition,
|
||||
"string",
|
||||
"Save the current camera position to file"
|
||||
},
|
||||
{
|
||||
"restoreCameraPosition",
|
||||
&luascriptfunctions::restoreCameraPosition,
|
||||
"string",
|
||||
"Restore the camera position from file"
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
@@ -143,6 +143,39 @@ int setInteractionMode(lua_State* L) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
int restoreCameraPosition(lua_State* L) {
|
||||
using ghoul::lua::luaTypeToString;
|
||||
const std::string _loggerCat = "lua.setCameraPosition";
|
||||
|
||||
int nArguments = lua_gettop(L);
|
||||
if (nArguments != 1)
|
||||
return luaL_error(L, "Expected %i arguments, got %i", 1, nArguments);
|
||||
|
||||
std::string cameraPosFilePath = luaL_checkstring(L, -1);
|
||||
|
||||
if (cameraPosFilePath.empty())
|
||||
return luaL_error(L, "filepath string is empty");
|
||||
|
||||
OsEng.interactionHandler().restoreCameraPosition(cameraPosFilePath);
|
||||
return 0;
|
||||
}
|
||||
|
||||
int saveCameraPosition(lua_State* L) {
|
||||
using ghoul::lua::luaTypeToString;
|
||||
const std::string _loggerCat = "lua.setCameraPosition";
|
||||
|
||||
int nArguments = lua_gettop(L);
|
||||
if (nArguments != 1)
|
||||
return luaL_error(L, "Expected %i arguments, got %i", 1, nArguments);
|
||||
|
||||
std::string cameraPosFilePath = luaL_checkstring(L, -1);
|
||||
|
||||
if (cameraPosFilePath.empty())
|
||||
return luaL_error(L, "filepath string is empty");
|
||||
|
||||
OsEng.interactionHandler().saveCameraPosition(cameraPosFilePath);
|
||||
}
|
||||
|
||||
#ifdef USE_OLD_INTERACTIONHANDLER
|
||||
|
||||
/**
|
||||
|
||||
436
src/interaction/interactionmode.cpp
Normal file
436
src/interaction/interactionmode.cpp
Normal file
@@ -0,0 +1,436 @@
|
||||
/*****************************************************************************************
|
||||
* *
|
||||
* 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
|
||||
}
|
||||
void KeyframeInteractionMode::initialize(const Camera& camera) {
|
||||
LWARNING("KeyframeInteractionMode::initialize not implemented yet");
|
||||
}
|
||||
|
||||
|
||||
// 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) {
|
||||
using namespace glm;
|
||||
if (_focusNode) {
|
||||
// Declare variables to use in interaction calculations
|
||||
dvec3 centerPos = _focusNode->worldPosition().dvec3();
|
||||
dvec3 camPos = camera.positionVec3();
|
||||
dvec3 posDiff = camPos - centerPos;
|
||||
dvec3 newPosition = camPos;
|
||||
|
||||
{ // Do local rotation
|
||||
dvec3 eulerAngles(_localRotationMouseState.velocity.get().y, 0, 0);
|
||||
dquat rotationDiff = dquat(eulerAngles);
|
||||
|
||||
_localCameraRotation = _localCameraRotation * rotationDiff;
|
||||
}
|
||||
{ // Do global rotation
|
||||
dvec2 smoothMouseVelocity = _globalRotationMouseState.velocity.get();
|
||||
dvec3 eulerAngles(smoothMouseVelocity.x, smoothMouseVelocity.y, 0);
|
||||
dquat rotationDiffCamSpace = dquat(eulerAngles);
|
||||
|
||||
dquat newRotationCamspace = _globalCameraRotation * rotationDiffCamSpace;
|
||||
dquat rotationDiffWorldSpace = newRotationCamspace * inverse(_globalCameraRotation);
|
||||
dvec3 rotationDiffVec3 = posDiff * rotationDiffWorldSpace - posDiff;
|
||||
|
||||
newPosition = camPos + rotationDiffVec3;
|
||||
|
||||
dvec3 directionToCenter = normalize(centerPos - newPosition);
|
||||
|
||||
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
|
||||
double boundingSphere = _focusNode->boundingSphere().lengthf();
|
||||
dvec3 centerToBoundingSphere =
|
||||
normalize(posDiff) *
|
||||
static_cast<double>(_focusNode->boundingSphere().lengthf());
|
||||
newPosition += -(posDiff - centerToBoundingSphere) *
|
||||
_truckMovementMouseState.velocity.get().y;
|
||||
}
|
||||
{ // Do roll
|
||||
dquat cameraRollRotation =
|
||||
angleAxis(_rollMouseState.velocity.get().x, 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);
|
||||
}
|
||||
|
||||
void OrbitalInteractionMode::initialize(const Camera& camera) {
|
||||
LWARNING("Interaction has not implemented sync to camera. I.e. interaction \
|
||||
mode may contain staate that is not transferred when reading camera state from disk");
|
||||
|
||||
// @Todo : extract _localCameraRotation and _globalCameraRotation from camera state
|
||||
|
||||
}
|
||||
|
||||
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
|
||||
// Shrink interaction ellipsoid to enable interaction below height = 0
|
||||
double ellipsoidShrinkTerm = 10000.0;
|
||||
double minHeightAboveGround = 100.0;
|
||||
|
||||
glm::dvec3 centerPos = _focusNode->worldPosition().dvec3();
|
||||
glm::dvec3 camPos = camera.positionVec3();
|
||||
glm::dvec3 posDiff = camPos - centerPos;
|
||||
|
||||
glm::dvec3 directionFromSurfaceToCamera =
|
||||
_globe->ellipsoid().geodeticSurfaceNormal(
|
||||
_globe->ellipsoid().cartesianToGeodetic2(camPos));
|
||||
glm::dvec3 centerToEllipsoidSurface = _globe->projectOnEllipsoid(camPos) -
|
||||
directionFromSurfaceToCamera * ellipsoidShrinkTerm;
|
||||
glm::dvec3 ellipsoidSurfaceToCamera = camPos - (centerPos + centerToEllipsoidSurface);
|
||||
|
||||
double distFromCenterToSurface =
|
||||
glm::length(centerToEllipsoidSurface);
|
||||
double distFromEllipsoidSurfaceToCamera = glm::length(ellipsoidSurfaceToCamera);
|
||||
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 = glm::dvec3(
|
||||
-_globalRotationMouseState.velocity.get().y,
|
||||
-_globalRotationMouseState.velocity.get().x,
|
||||
0) * glm::clamp(distFromEllipsoidSurfaceToCamera / 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 = camPos + rotationDiffVec3;
|
||||
|
||||
directionFromSurfaceToCamera =
|
||||
_globe->ellipsoid().geodeticSurfaceNormal(
|
||||
_globe->ellipsoid().cartesianToGeodetic2(camPos));
|
||||
centerToEllipsoidSurface = _globe->projectOnEllipsoid(camPos) -
|
||||
directionFromSurfaceToCamera * ellipsoidShrinkTerm;
|
||||
ellipsoidSurfaceToCamera = camPos - (centerPos + centerToEllipsoidSurface);
|
||||
|
||||
glm::dvec3 lookUpWhenFacingSurface =
|
||||
_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
|
||||
distFromEllipsoidSurfaceToCamera = glm::length(ellipsoidSurfaceToCamera);
|
||||
camPos += -directionFromSurfaceToCamera * distFromEllipsoidSurfaceToCamera *
|
||||
_truckMovementMouseState.velocity.get().y;
|
||||
}
|
||||
{ // Do roll
|
||||
glm::dquat cameraRollRotation =
|
||||
glm::angleAxis(_rollMouseState.velocity.get().x, directionFromSurfaceToCamera);
|
||||
_globalCameraRotation = cameraRollRotation * _globalCameraRotation;
|
||||
}
|
||||
{ // Push up to surface
|
||||
ellipsoidSurfaceToCamera = camPos - (centerPos + centerToEllipsoidSurface);
|
||||
|
||||
distFromEllipsoidSurfaceToCamera = glm::length(ellipsoidSurfaceToCamera);
|
||||
double heightToSurface = _globe->getHeight(camPos) + ellipsoidShrinkTerm;
|
||||
double heightToSurfaceAndPadding = heightToSurface + minHeightAboveGround;
|
||||
camPos += directionFromSurfaceToCamera *
|
||||
glm::max(heightToSurfaceAndPadding - distFromEllipsoidSurfaceToCamera, 0.0);
|
||||
}
|
||||
// Update the camera state
|
||||
camera.setRotation(_globalCameraRotation * _localCameraRotation);
|
||||
camera.setPositionVec3(camPos);
|
||||
}
|
||||
}
|
||||
|
||||
void GlobeBrowsingInteractionMode::update(Camera& camera, const InputState& inputState, double deltaTime) {
|
||||
updateMouseStatesFromInput(inputState, deltaTime);
|
||||
updateCameraStateFromMouseStates(camera);
|
||||
}
|
||||
|
||||
|
||||
} // namespace interaction
|
||||
} // namespace openspace
|
||||
@@ -190,6 +190,22 @@ namespace openspace {
|
||||
_cachedViewDirection.isDirty = true;
|
||||
}
|
||||
|
||||
void Camera::serialize(std::ostream& os) const {
|
||||
Vec3 p = positionVec3();
|
||||
Quat q = rotationQuaternion();
|
||||
os << p.x << " " << p.y << " " << p.z << std::endl;
|
||||
os << q.x << " " << q.y << " " << q.z << " " << q.w << std::endl;
|
||||
}
|
||||
|
||||
void Camera::deserialize(std::istream& is) {
|
||||
Vec3 p;
|
||||
Quat q;
|
||||
is >> p.x >> p.y >> p.z;
|
||||
is >> q.x >> q.y >> q.z >> q.w;
|
||||
setPositionVec3(p);
|
||||
setRotation(q);
|
||||
}
|
||||
|
||||
void Camera::preSynchronization() {
|
||||
std::lock_guard<std::mutex> _lock(_mutex);
|
||||
|
||||
|
||||
Reference in New Issue
Block a user