diff --git a/include/openspace/interaction/interactionhandler.h b/include/openspace/interaction/interactionhandler.h index 4992fbc91a..b3a0a252d4 100644 --- a/include/openspace/interaction/interactionhandler.h +++ b/include/openspace/interaction/interactionhandler.h @@ -27,14 +27,15 @@ #include #include +#include #include #include #include #include #include + #include -#include #include @@ -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 >& getPressedKeys() const; - const std::list& getPressedMouseButtons() const; - glm::dvec2 getMousePosition() const; - double getMouseScrollDelta() const; - std::vector& getKeyFrames() const; - - bool isKeyPressed(std::pair keyModPair) const; - bool isMouseButtonPressed(MouseButton mouseButton) const; -private: - // Input from keyboard and mouse - std::list > _keysDown; - std::list _mouseButtonsDown; - glm::dvec2 _mousePosition; - double _mouseScrollDelta; - - // Remote input via keyframes - std::vector _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 - 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 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); + bool _cameraUpdatedFromScript = false; + std::multimap _keyLua; std::unique_ptr _inputState; @@ -348,7 +215,6 @@ private: // Properties properties::StringProperty _origin; properties::StringProperty _coordinateSystem; - }; #endif // USE_OLD_INTERACTIONHANDLER diff --git a/include/openspace/interaction/interactionmode.h b/include/openspace/interaction/interactionmode.h new file mode 100644 index 0000000000..9754fc902f --- /dev/null +++ b/include/openspace/interaction/interactionmode.h @@ -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 +#include +#include +#include +#include + +#include + + + +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 >& getPressedKeys() const; + const std::list& getPressedMouseButtons() const; + glm::dvec2 getMousePosition() const; + double getMouseScrollDelta() const; + std::vector& getKeyFrames() const; + + bool isKeyPressed(std::pair keyModPair) const; + bool isMouseButtonPressed(MouseButton mouseButton) const; + private: + // Input from keyboard and mouse + std::list > _keysDown; + std::list _mouseButtonsDown; + glm::dvec2 _mousePosition; + double _mouseScrollDelta; + + // Remote input via keyframes + std::vector _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 + 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 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__ diff --git a/include/openspace/util/camera.h b/include/openspace/util/camera.h index bf15d1492b..501567fd48 100644 --- a/include/openspace/util/camera.h +++ b/include/openspace/util/camera.h @@ -38,6 +38,8 @@ #include #include +#include + 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. diff --git a/modules/globebrowsing/globes/renderableglobe.cpp b/modules/globebrowsing/globes/renderableglobe.cpp index 9ebafe37a5..1dcbbaff4b 100644 --- a/modules/globebrowsing/globes/renderableglobe.cpp +++ b/modules/globebrowsing/globes/renderableglobe.cpp @@ -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) { diff --git a/modules/globebrowsing/tile/TileProviderManager.cpp b/modules/globebrowsing/tile/TileProviderManager.cpp index 2afe3845c4..2713d6c7fb 100644 --- a/modules/globebrowsing/tile/TileProviderManager.cpp +++ b/modules/globebrowsing/tile/TileProviderManager.cpp @@ -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(); } } } diff --git a/modules/globebrowsing/tile/TileProviderManager.h b/modules/globebrowsing/tile/TileProviderManager.h index cc74062146..4f4b875a97 100644 --- a/modules/globebrowsing/tile/TileProviderManager.h +++ b/modules/globebrowsing/tile/TileProviderManager.h @@ -64,7 +64,7 @@ namespace openspace { const std::vector > getActivatedLayerCategory(LayeredTextures::TextureCategory); - void prerender(); + void update(); private: static void initTexures(std::vector& destination, diff --git a/modules/globebrowsing/tile/temporaltileprovider.cpp b/modules/globebrowsing/tile/temporaltileprovider.cpp index 609ab3f858..aa3fbe28be 100644 --- a/modules/globebrowsing/tile/temporaltileprovider.cpp +++ b/modules/globebrowsing/tile/temporaltileprovider.cpp @@ -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 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(); diff --git a/modules/globebrowsing/tile/temporaltileprovider.h b/modules/globebrowsing/tile/temporaltileprovider.h index 283c42137c..8237a5d043 100644 --- a/modules/globebrowsing/tile/temporaltileprovider.h +++ b/modules/globebrowsing/tile/temporaltileprovider.h @@ -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 getAsyncTileReader(); diff --git a/modules/globebrowsing/tile/tileprovider.cpp b/modules/globebrowsing/tile/tileprovider.cpp index 870726751c..c02be6523a 100644 --- a/modules/globebrowsing/tile/tileprovider.cpp +++ b/modules/globebrowsing/tile/tileprovider.cpp @@ -68,7 +68,7 @@ namespace openspace { } - void CachingTileProvider::prerender() { + void CachingTileProvider::update() { initTexturesFromLoadedData(); if (_framesSinceLastRequestFlush++ > _framesUntilRequestFlush) { clearRequestQueue(); diff --git a/modules/globebrowsing/tile/tileprovider.h b/modules/globebrowsing/tile/tileprovider.h index 4e94ee8da3..76121c385c 100644 --- a/modules/globebrowsing/tile/tileprovider.h +++ b/modules/globebrowsing/tile/tileprovider.h @@ -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 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 getAsyncTileReader(); diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index 1d7d1a7670..f580bcd61d 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -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 diff --git a/src/interaction/interactionhandler.cpp b/src/interaction/interactionhandler.cpp index 0906a85133..f44fed20c1 100644 --- a/src/interaction/interactionhandler.cpp +++ b/src/interaction/interactionhandler.cpp @@ -36,6 +36,8 @@ #include +#include + 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, modifier)); - } - else if (action == KeyAction::Release) { - // Remove all key pressings for 'key' - _keysDown.remove_if([key](std::pair 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 >& InputState::getPressedKeys() const { - return _keysDown; -} - -const std::list& InputState::getPressedMouseButtons() const { - return _mouseButtonsDown; -} - -glm::dvec2 InputState::getMousePosition() const{ - return _mousePosition; -} - -double InputState::getMouseScrollDelta() const{ - return _mouseScrollDelta; -} - -bool InputState::isKeyPressed(std::pair 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::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(_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(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" } } }; diff --git a/src/interaction/interactionhandler_lua.inl b/src/interaction/interactionhandler_lua.inl index 9fdbf19a61..5c04ff4e50 100644 --- a/src/interaction/interactionhandler_lua.inl +++ b/src/interaction/interactionhandler_lua.inl @@ -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 /** diff --git a/src/interaction/interactionmode.cpp b/src/interaction/interactionmode.cpp new file mode 100644 index 0000000000..1b49934fd5 --- /dev/null +++ b/src/interaction/interactionmode.cpp @@ -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 +#include + +#include +#include +#include +#include +#include + +#include + +#include + + + +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, modifier)); + } + else if (action == KeyAction::Release) { + // Remove all key pressings for 'key' + _keysDown.remove_if([key](std::pair 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 >& InputState::getPressedKeys() const { + return _keysDown; + } + + const std::list& InputState::getPressedMouseButtons() const { + return _mouseButtonsDown; + } + + glm::dvec2 InputState::getMousePosition() const { + return _mousePosition; + } + + double InputState::getMouseScrollDelta() const { + return _mouseScrollDelta; + } + + bool InputState::isKeyPressed(std::pair 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::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(_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(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 diff --git a/src/util/camera.cpp b/src/util/camera.cpp index 2710cf1d36..09445d9e41 100644 --- a/src/util/camera.cpp +++ b/src/util/camera.cpp @@ -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 _lock(_mutex);