diff --git a/Jenkinsfile b/Jenkinsfile index 7dc878568d..9f1ae501d1 100644 --- a/Jenkinsfile +++ b/Jenkinsfile @@ -11,6 +11,7 @@ def modules = [ "multiresvolume", "spacecraftinstruments", "space", + "touch", "toyvolume", "volume" ]; diff --git a/data/assets/dawn.scene b/data/assets/dawn.scene index 6169619bb7..44a55008a6 100644 --- a/data/assets/dawn.scene +++ b/data/assets/dawn.scene @@ -39,7 +39,7 @@ asset.onInitialize(function () ) openspace.navigation.setCameraState({ - Focus = DawnAsset.Dawn.Identifier, + Anchor = DawnAsset.Dawn.Identifier, Position = { 526781518487.171326, 257168309890.072144, -1381125204152.817383 }, Rotation = { -0.106166, 0.981574, -0.084545, 0.134513 }, }) diff --git a/data/assets/default.scene b/data/assets/default.scene index acab28d799..2217ee6832 100644 --- a/data/assets/default.scene +++ b/data/assets/default.scene @@ -96,7 +96,7 @@ asset.onInitialize(function () ) openspace.navigation.setCameraState({ - Focus = earthAsset.Earth.Identifier, + Anchor = earthAsset.Earth.Identifier, Position = { 0, 0, 0 }, Rotation = { 0.758797, 0.221490, -0.605693, -0.091135 }, }) diff --git a/data/assets/gaia.scene b/data/assets/gaia.scene index d1e883a244..b59ac0eb37 100644 --- a/data/assets/gaia.scene +++ b/data/assets/gaia.scene @@ -33,7 +33,7 @@ asset.onInitialize(function () openspace.setPropertyValueSingle('Scene.Stars.Renderable.Enabled', false); openspace.navigation.setCameraState({ - Focus = "Gaia", + Anchor = "Gaia", Position = { 1000000000000.0, 1000000000000.0, 1000000000000.0 }, Rotation = { 0.683224, -0.765934, -0.601234, -0.418073 }, }) diff --git a/data/assets/juno.scene b/data/assets/juno.scene index 9e521e2f7a..4da231ede0 100644 --- a/data/assets/juno.scene +++ b/data/assets/juno.scene @@ -43,7 +43,7 @@ asset.onInitialize(function () ) openspace.navigation.setCameraState({ - Focus = junoAsset.Juno.Identifier, + Anchor = junoAsset.Juno.Identifier, Position = { 1837386367.601345, -389860693812.834839, 714830404470.398926 }, Rotation = { -0.336540, 0.711402, -0.099212, 0.608937 }, }) diff --git a/data/assets/messenger.scene b/data/assets/messenger.scene index 5aeaa37827..e24937fbc8 100644 --- a/data/assets/messenger.scene +++ b/data/assets/messenger.scene @@ -63,7 +63,7 @@ asset.onInitialize(function () ) openspace.navigation.setCameraState({ - Focus = "Mercury", + Anchor = "Mercury", Position = { 526781518487.171326, 257168309890.072144, -1381125204152.817383 }, Rotation = {0.180662, 0.021334, 0.979084, 0.091111}, }) diff --git a/data/assets/newhorizons.scene b/data/assets/newhorizons.scene index 834ffc1f11..4821bcd3b7 100644 --- a/data/assets/newhorizons.scene +++ b/data/assets/newhorizons.scene @@ -212,7 +212,7 @@ asset.onInitialize(function () 2160, 4320, 8640 }) - openspace.setPropertyValueSingle('NavigationHandler.OrbitalNavigator.FollowFocusNodeRotationDistance', 20.000000); + openspace.setPropertyValueSingle('NavigationHandler.OrbitalNavigator.FollowAnchorNodeRotationDistance', 20.000000); openspace.addVirtualProperty( "BoolProperty", @@ -225,7 +225,7 @@ asset.onInitialize(function () ) openspace.navigation.setCameraState({ - Focus = NewHorizonsAsset.NewHorizons.Identifier, + Anchor = NewHorizonsAsset.NewHorizons.Identifier, Position = { 4662120063743.592773, 1263245003503.724854, -955413856565.788086 }, Rotation = { 0.683224, -0.165934, 0.701234, 0.118073 }, }) diff --git a/data/assets/osirisrex.scene b/data/assets/osirisrex.scene index 81f0eef20c..118ef1b641 100644 --- a/data/assets/osirisrex.scene +++ b/data/assets/osirisrex.scene @@ -133,7 +133,7 @@ asset.onInitialize(function () ) openspace.navigation.setCameraState({ - Focus = OsirisRexAsset.OsirisRex.Identifier, + Anchor = OsirisRexAsset.OsirisRex.Identifier, Position = { 26974590199.661884, 76314608558.908020, -127086452897.101791 }, Rotation = { 0.729548, -0.126024, 0.416827, 0.527382 }, }) diff --git a/data/assets/rosetta.scene b/data/assets/rosetta.scene index 1272560a61..019e482565 100644 --- a/data/assets/rosetta.scene +++ b/data/assets/rosetta.scene @@ -137,7 +137,7 @@ asset.onInitialize(function () ) openspace.navigation.setCameraState({ - Focus = Comet67PAsset.Comet67P.Identifier, + Anchor = Comet67PAsset.Comet67P.Identifier, Position = { 526781518487.171326, 257168309890.072144, -1381125204152.817383 }, Rotation = { -0.106166, 0.981574, -0.084545, 0.134513 }, }) diff --git a/data/assets/util/webgui.asset b/data/assets/util/webgui.asset index bee410ad17..376f734696 100644 --- a/data/assets/util/webgui.asset +++ b/data/assets/util/webgui.asset @@ -1,7 +1,7 @@ local guiCustomization = asset.require('customization/gui') -- Select which commit hashes to use for the frontend and backend -local frontendHash = "abf5fe23ef29af408d6c071057f1cc706c9b09a3" +local frontendHash = "6a34f2b0c6cfde64b890f12aa5bfaa42ac61a40f" local backendHash = "6e773425b3e90ba93f0090e44427e474fe5c633f" local dataProvider = "data.openspaceproject.com/files/webgui" diff --git a/data/assets/voyager.scene b/data/assets/voyager.scene index c9f274aed1..68f3cfe55b 100644 --- a/data/assets/voyager.scene +++ b/data/assets/voyager.scene @@ -71,7 +71,7 @@ asset.onInitialize(function () ) openspace.navigation.setCameraState({ - Focus = VoyagerAsset.Voyager_1.Identifier, + Anchor = VoyagerAsset.Voyager_1.Identifier, Position = { 526781518487.171326, 257168309890.072144, -1381125204152.817383 }, Rotation = { -0.106166, 0.981574, -0.084545, 0.134513 }, }) diff --git a/include/openspace/interaction/navigationhandler.h b/include/openspace/interaction/navigationhandler.h index e064b1f379..f4de2d21ca 100644 --- a/include/openspace/interaction/navigationhandler.h +++ b/include/openspace/interaction/navigationhandler.h @@ -55,9 +55,7 @@ public: void deinitialize(); // Mutators - void setFocusNode(SceneGraphNode* node); void setCamera(Camera* camera); - void resetCameraDirection(); void setInterpolationTime(float durationInSeconds); void setCameraStateFromDictionary(const ghoul::Dictionary& cameraDict); @@ -69,12 +67,10 @@ public: // Accessors ghoul::Dictionary cameraStateDictionary(); - SceneGraphNode* focusNode() const; - glm::dvec3 focusNodeToCameraVector() const; - glm::quat focusNodeToCameraRotation() const; Camera* camera() const; const InputState& inputState() const; const OrbitalNavigator& orbitalNavigator() const; + OrbitalNavigator& orbitalNavigator(); KeyframeNavigator& keyframeNavigator() const; bool isKeyFrameInteractionEnabled() const; float interpolationTime() const; @@ -126,7 +122,6 @@ private: std::unique_ptr _orbitalNavigator; std::unique_ptr _keyframeNavigator; - properties::StringProperty _origin; properties::BoolProperty _useKeyFrameInteraction; }; diff --git a/include/openspace/interaction/orbitalnavigator.h b/include/openspace/interaction/orbitalnavigator.h index 3f0b14a5e6..b625dd68d0 100644 --- a/include/openspace/interaction/orbitalnavigator.h +++ b/include/openspace/interaction/orbitalnavigator.h @@ -31,8 +31,10 @@ #include #include #include +#include #include #include +#include #include #include @@ -51,28 +53,46 @@ public: OrbitalNavigator(); void updateStatesFromInput(const InputState& inputState, double deltaTime); - void updateCameraStateFromStates(Camera& camera, double deltaTime); + void updateCameraStateFromStates(double deltaTime); - void setFocusNode(SceneGraphNode* focusNode); - void startInterpolateCameraDirection(const Camera& camera); - float rotateToFocusInterpolationTime() const; - void setRotateToFocusInterpolationTime(float durationInSeconds); + Camera* camera() const; + void setCamera(Camera* camera); + + void setFocusNode(const std::string& focusNode); + void setAnchorNode(const std::string& anchorNode); + void setAimNode(const std::string& aimNode); + + void startRetargetAnchor(); + void startRetargetAim(); + float retargetInterpolationTime() const; + void setRetargetInterpolationTime(float durationInSeconds); JoystickCameraStates& joystickStates(); bool followingNodeRotation() const; - SceneGraphNode* focusNode() const; + const SceneGraphNode* anchorNode() const; + const SceneGraphNode* aimNode() const; bool hasRotationalFriction() const; bool hasZoomFriction() const; bool hasRollFriction() const; + glm::dvec3 anchorNodeToCameraVector() const; + glm::quat anchorNodeToCameraRotation() const; + private: struct CameraRotationDecomposition { glm::dquat localRotation; glm::dquat globalRotation; }; + struct CameraPose { + glm::dvec3 position; + glm::dquat rotation; + }; + + using Displacement = std::pair; + struct Friction : public properties::PropertyOwner { Friction(); @@ -83,9 +103,28 @@ private: properties::FloatProperty friction; }; + void setFocusNode(const SceneGraphNode* focusNode); + void setAnchorNode(const SceneGraphNode* anchorNode); + void setAimNode(const SceneGraphNode* aimNode); + + Camera* _camera; + Friction _friction; - properties::FloatProperty _followFocusNodeRotationDistance; + // Anchor: Node to follow and orbit. + properties::StringProperty _anchor; + + // Aim: Node to look at (when camera direction is reset), + // Empty string means same as anchor. + // If these are the same node we call it the `focus` node. + properties::StringProperty _aim; + + // Reset camera direction to the anchor node. + properties::TriggerProperty _retargetAnchor; + // Reset camera direction to the aim node. + properties::TriggerProperty _retargetAim; + + properties::FloatProperty _followAnchorNodeRotationDistance; properties::FloatProperty _minimumAllowedDistance; properties::FloatProperty _mouseSensitivity; @@ -95,33 +134,64 @@ private: properties::FloatProperty _stereoscopicDepthOfFocusSurface; properties::FloatProperty _staticViewScaleExponent; - properties::FloatProperty _rotateToFocusInterpolationTime; + properties::FloatProperty _retargetInterpolationTime; properties::FloatProperty _stereoInterpolationTime; + properties::FloatProperty _followRotationInterpolationTime; MouseCameraStates _mouseStates; JoystickCameraStates _joystickStates; - SceneGraphNode* _focusNode = nullptr; - glm::dvec3 _previousFocusNodePosition; - glm::dquat _previousFocusNodeRotation; + const SceneGraphNode* _anchorNode = nullptr; + const SceneGraphNode* _aimNode = nullptr; + + glm::dvec3 _previousAnchorNodePosition; + glm::dquat _previousAnchorNodeRotation; + + glm::dvec3 _previousAimNodePosition; + glm::dquat _previousAimNodeRotation; + double _currentCameraToSurfaceDistance = 0.0; bool _directlySetStereoDistance = false; - Interpolator _rotateToFocusNodeInterpolator; + Interpolator _retargetAimInterpolator; + Interpolator _retargetAnchorInterpolator; Interpolator _cameraToSurfaceDistanceInterpolator; Interpolator _followRotationInterpolator; /** - * Decomposes the cameras rotation in to a global and a local rotation defined by + * Decomposes the camera's rotation in to a global and a local rotation defined by * CameraRotationDecomposition. The global rotation defines the rotation so that the - * camera points towards the focus node in the direction opposite to the direction + * camera points towards the reference node in the direction opposite to the direction * out from the surface of the object. The local rotation defines the differential * from the global to the current total rotation so that * cameraRotation = globalRotation * localRotation. */ - CameraRotationDecomposition decomposeCameraRotation(const glm::dvec3& cameraPosition, - const glm::dquat& cameraRotation, const glm::dvec3& cameraLookUp, - const glm::dvec3& cameraViewDirection); + CameraRotationDecomposition decomposeCameraRotationSurface(const CameraPose pose, + const SceneGraphNode& reference); + + /** + * Decomposes the camera's rotation in to a global and a local rotation defined by + * CameraRotationDecomposition. The global rotation defines the rotation so that the + * camera points towards the reference node's origin. + * The local rotation defines the differential from the global to the current total + * rotation so that cameraRotation = globalRotation * localRotation. + */ + CameraRotationDecomposition decomposeCameraRotation(const CameraPose pose, + glm::dvec3 reference); + + /** + * Composes a pair of global and local rotations into a quaternion that can be used + * as the world rotation for a camera. + */ + glm::dquat composeCameraRotation(const CameraRotationDecomposition& composition); + + /* + * Moves and rotates the camera around the anchor node in order to maintain the + * screen space position of the aim node. Also interpolates to the aim node, when + * retargeting the aim. + */ + CameraPose followAim(CameraPose pose, glm::dvec3 cameraToAnchor, + Displacement anchorToAim); /* * Perform a camera roll on the local camera rotation @@ -137,47 +207,47 @@ private: const glm::dquat& localCameraRotation) const; /** - * Interpolates the local rotation towards a 0 rotation. - * \returns a modified local rotation interpolated towards 0. + * Interpolates the camera rotation based on active interpolators. + * \returns a new rotation quaternion */ glm::dquat interpolateLocalRotation(double deltaTime, const glm::dquat& localCameraRotation); - double interpolateCameraToSurfaceDistance(double deltaTime, - double currentDistance, - double targetDistance); + Displacement interpolateRetargetAim(double deltaTime, CameraPose pose, + glm::dvec3 cameraToAnchor, Displacement anchorToAim); + + double interpolateCameraToSurfaceDistance(double deltaTime, double currentDistance, + double targetDistance); /** - * Translates the horizontal direction. If far from the focus object, this will + * Translates the horizontal direction. If far from the anchor object, this will * result in an orbital rotation around the object. This function does not affect the * rotation but only the position. * \returns a position vector adjusted in the horizontal direction. */ glm::dvec3 translateHorizontally(double deltaTime, const glm::dvec3& cameraPosition, - const glm::dvec3& objectPosition, const glm::dquat& focusNodeRotationDiff, - const glm::dquat& globalCameraRotation, + const glm::dvec3& objectPosition, const glm::dquat& globalCameraRotation, const SurfacePositionHandle& positionHandle) const; /* - * Adds rotation to the camera position so that it follows the rotation of the focus - * node defined by the differential focusNodeRotationDiff. - * \returns a position updated with the rotation defined by focusNodeRotationDiff + * Adds rotation to the camera position so that it follows the rotation of the anchor + * node defined by the differential anchorNodeRotationDiff. + * \returns a position updated with the rotation defined by anchorNodeRotationDiff */ - glm::dvec3 followFocusNodeRotation(const glm::dvec3& cameraPosition, - const glm::dvec3& objectPosition, const glm::dquat& focusNodeRotationDiff) const; + glm::dvec3 followAnchorNodeRotation(const glm::dvec3& cameraPosition, + const glm::dvec3& objectPosition, const glm::dquat& anchorNodeRotationDiff) const; /** - * Updates the global rotation so that it points towards the focus node. - * \returns a global rotation quaternion defining a rotation towards the focus node. + * Updates the global rotation so that it points towards the anchor node. + * \returns a global rotation quaternion defining a rotation towards the anchor node. */ glm::dquat rotateGlobally(const glm::dquat& globalCameraRotation, - const glm::dvec3& objectPosition, const glm::dquat& focusNodeRotationDiff, - const glm::dvec3& cameraPosition, + const glm::dquat& aimNodeRotationDiff, const SurfacePositionHandle& positionHandle) const; /** - * Translates the camera position towards or away from the focus node. + * Translates the camera position towards or away from the anchor node. * \returns a position vector adjusted in the vertical direction. */ glm::dvec3 translateVertically(double deltaTime, const glm::dvec3& cameraPosition, @@ -189,7 +259,7 @@ private: * \returns a quaternion adjusted to rotate around the out vector of the surface. */ glm::dquat rotateHorizontally(double deltaTime, - const glm::dquat& globalCameraRotation, const glm::dvec3& cameraPosition, + const glm::dquat& globalCameraRotation, const SurfacePositionHandle& positionHandle) const; /** @@ -210,17 +280,15 @@ private: const SurfacePositionHandle& positionHandle); /** - * Get the vector from the camera to the surface of the focus object in world space. + * Get the vector from the camera to the surface of the anchor object in world space. */ - glm::dvec3 cameraToSurfaceVector( - const glm::dvec3& cameraPos, - const glm::dvec3& centerPos, - const SurfacePositionHandle& posHandle); + glm::dvec3 cameraToSurfaceVector(const glm::dvec3& cameraPos, + const glm::dvec3& centerPos, const SurfacePositionHandle& posHandle); /** * Calculates a SurfacePositionHandle given a camera position in world space. */ - SurfacePositionHandle calculateSurfacePositionHandle( + SurfacePositionHandle calculateSurfacePositionHandle(const SceneGraphNode& node, const glm::dvec3 cameraPositionWorldSpace); }; diff --git a/include/openspace/scene/scenegraphnode.h b/include/openspace/scene/scenegraphnode.h index b421f1008d..cc7fe86d04 100644 --- a/include/openspace/scene/scenegraphnode.h +++ b/include/openspace/scene/scenegraphnode.h @@ -96,7 +96,6 @@ public: void traversePostOrder(const std::function& fn); void update(const UpdateData& data); void render(const RenderData& data, RendererTasks& tasks); - void updateCamera(Camera* camera) const; void attachChild(std::unique_ptr child); std::unique_ptr detachChild(SceneGraphNode& child); @@ -109,7 +108,7 @@ public: void setDependencies(const std::vector& dependencies); SurfacePositionHandle calculateSurfacePositionHandle( - const glm::dvec3& targetModelSpace); + const glm::dvec3& targetModelSpace) const; const std::vector& dependencies() const; const std::vector& dependentNodes() const; diff --git a/include/openspace/util/camera.h b/include/openspace/util/camera.h index cf813d10a9..f40448ab8d 100644 --- a/include/openspace/util/camera.h +++ b/include/openspace/util/camera.h @@ -72,7 +72,6 @@ public: // Mutators void setPositionVec3(glm::dvec3 pos); - void setFocusPositionVec3(glm::dvec3 pos); void setRotation(glm::dquat rotation); void setScaling(float scaling); void setMaxFov(float fov); @@ -86,7 +85,6 @@ public: const glm::dvec3& positionVec3() const; glm::dvec3 eyePositionVec3() const; const glm::dvec3& unsynchedPositionVec3() const; - const glm::dvec3& focusPositionVec3() const; const glm::dvec3& viewDirectionWorldSpace() const; const glm::dvec3& lookUpVectorCameraSpace() const; const glm::dvec3& lookUpVectorWorldSpace() const; @@ -138,18 +136,6 @@ public: mutable std::mutex _mutex; } sgctInternal; - // Deprecated - // [[deprecated("Replaced by Camera::setPositionVec3()")]] - void setPosition(psc pos); - // [[deprecated("Replaced by Camera::setFocusPositionVec3()")]] - void setFocusPosition(psc pos); - // [[deprecated("Replaced by Camera::positionVec3()")]] - psc position() const; - // [[deprecated("Replaced by Camera::unsynchedPositionVec3()")]] - psc unsynchedPosition() const; - // [[deprecated("Replaced by Camera::focusPositionVec3()")]] - psc focusPosition() const; - const glm::mat4& sceneMatrix() const; // @TODO use Camera::SgctInternal interface instead // [[deprecated("Replaced by Camera::SgctInternal::viewMatrix()")]] const glm::mat4& viewMatrix() const; @@ -160,10 +146,11 @@ public: std::vector getSyncables(); -private: // Static constants static const glm::dvec3 ViewDirectionCameraSpace; - static const glm::dvec3 LookupVectorCameraSpace; + static const glm::dvec3 UpDirectionCameraSpace; + +private: SyncData _position = glm::dvec3(1.0, 1.0, 1.0); SyncData _rotation = glm::dquat(glm::dvec3(1.0, 1.0, 1.0)); diff --git a/modules/base/dashboard/dashboarditemangle.cpp b/modules/base/dashboard/dashboarditemangle.cpp index 608199bf59..ac618d491a 100644 --- a/modules/base/dashboard/dashboarditemangle.cpp +++ b/modules/base/dashboard/dashboarditemangle.cpp @@ -28,6 +28,7 @@ #include #include #include +#include #include #include #include @@ -380,7 +381,14 @@ std::pair DashboardItemAngle::positionAndLabel( case Type::Node: return { comp.node->worldPosition(), comp.node->guiName() }; case Type::Focus: - return { global::navigationHandler.focusNode()->worldPosition(), "focus" }; + { + const SceneGraphNode* node = + global::navigationHandler.orbitalNavigator().anchorNode(); + return { + node->worldPosition(), + "focus" + }; + } case Type::Camera: return { global::renderEngine.scene()->camera()->positionVec3(), "camera" }; default: diff --git a/modules/base/dashboard/dashboarditemdistance.cpp b/modules/base/dashboard/dashboarditemdistance.cpp index 71b83ce7f0..6915e6a4cc 100644 --- a/modules/base/dashboard/dashboarditemdistance.cpp +++ b/modules/base/dashboard/dashboarditemdistance.cpp @@ -28,6 +28,7 @@ #include #include #include +#include #include #include #include @@ -402,7 +403,7 @@ std::pair DashboardItemDistance::positionAndLabel( } case Type::Focus: return { - global::navigationHandler.focusNode()->worldPosition(), + global::navigationHandler.orbitalNavigator().anchorNode()->worldPosition(), "focus" }; case Type::Camera: diff --git a/modules/fieldlinessequence/rendering/renderablefieldlinessequence.cpp b/modules/fieldlinessequence/rendering/renderablefieldlinessequence.cpp index 5ea3a24486..8903e16684 100644 --- a/modules/fieldlinessequence/rendering/renderablefieldlinessequence.cpp +++ b/modules/fieldlinessequence/rendering/renderablefieldlinessequence.cpp @@ -29,6 +29,7 @@ #include #include #include +#include #include #include #include @@ -763,8 +764,10 @@ void RenderableFieldlinesSequence::definePropertyCallbackFunctions() { )); return; } - global::navigationHandler.setFocusNode(node->parent()); - global::navigationHandler.resetCameraDirection(); + global::navigationHandler.orbitalNavigator().setFocusNode( + node->parent()->identifier() + ); + global::navigationHandler.orbitalNavigator().startRetargetAnchor(); }); _pJumpToStartBtn.onChange([this] { diff --git a/modules/galaxy/rendering/galaxyraycaster.cpp b/modules/galaxy/rendering/galaxyraycaster.cpp index 79f6d90d32..193ad4f5ca 100644 --- a/modules/galaxy/rendering/galaxyraycaster.cpp +++ b/modules/galaxy/rendering/galaxyraycaster.cpp @@ -116,7 +116,7 @@ void GalaxyRaycaster::postRaycast(const RaycastData&, ghoul::opengl::ProgramObje bool GalaxyRaycaster::isCameraInside(const RenderData& data, glm::vec3& localPosition) { // Camera rig position in world coordinates. - const glm::vec4 rigWorldPos = glm::vec4(data.camera.position().vec3(), 1.0); + const glm::vec4 rigWorldPos = glm::vec4(data.camera.positionVec3(), 1.0); //rigWorldPos /= data.camera.scaling().x * pow(10.0, data.camera.scaling().y); //glm::mat4 invSgctMatrix = glm::inverse(data.camera.viewMatrix()); diff --git a/modules/galaxy/rendering/renderablegalaxy.cpp b/modules/galaxy/rendering/renderablegalaxy.cpp index ad1f16d8a2..bdd1f4a551 100644 --- a/modules/galaxy/rendering/renderablegalaxy.cpp +++ b/modules/galaxy/rendering/renderablegalaxy.cpp @@ -309,7 +309,7 @@ void RenderableGalaxy::update(const UpdateData& data) { void RenderableGalaxy::render(const RenderData& data, RendererTasks& tasks) { RaycasterTask task { _raycaster.get(), data }; - const glm::vec3 position = data.camera.position().vec3(); + const glm::vec3 position = data.camera.positionVec3(); const float length = safeLength(position); const glm::vec3 galaxySize = _volumeSize; diff --git a/modules/globebrowsing/globebrowsingmodule.cpp b/modules/globebrowsing/globebrowsingmodule.cpp index 835158872d..c614dc86d0 100644 --- a/modules/globebrowsing/globebrowsingmodule.cpp +++ b/modules/globebrowsing/globebrowsingmodule.cpp @@ -31,6 +31,8 @@ #include #include #include +#include +#include #include #include #include @@ -474,7 +476,7 @@ void GlobeBrowsingModule::goToGeodetic2(Camera& camera, globebrowsing::Geodetic2 const glm::dvec3 cameraPosition = global::navigationHandler.camera()->positionVec3(); const glm::dmat4 inverseModelTransform = - global::navigationHandler.focusNode()->inverseModelTransform(); + global::navigationHandler.orbitalNavigator().anchorNode()->inverseModelTransform(); const glm::dvec3 cameraPositionModelSpace = glm::dvec3(inverseModelTransform * glm::dvec4(cameraPosition, 1.0)); const SurfacePositionHandle posHandle = globe->calculateSurfacePositionHandle( @@ -557,7 +559,9 @@ GlobeBrowsingModule::castFocusNodeRenderableToGlobe() { using namespace globebrowsing; - const Renderable* renderable = global::navigationHandler.focusNode()->renderable(); + const Renderable* renderable = + global::navigationHandler.orbitalNavigator().anchorNode()->renderable(); + if (!renderable) { return nullptr; } diff --git a/modules/globebrowsing/globebrowsingmodule_lua.inl b/modules/globebrowsing/globebrowsingmodule_lua.inl index 5fb59b3bad..f9192e5777 100644 --- a/modules/globebrowsing/globebrowsingmodule_lua.inl +++ b/modules/globebrowsing/globebrowsingmodule_lua.inl @@ -208,7 +208,7 @@ int getGeoPositionForCamera(lua_State* L) { const glm::dvec3 cameraPosition = global::navigationHandler.camera()->positionVec3(); const glm::dmat4 inverseModelTransform = - global::navigationHandler.focusNode()->inverseModelTransform(); + global::navigationHandler.orbitalNavigator().anchorNode()->inverseModelTransform(); const glm::dvec3 cameraPositionModelSpace = glm::dvec3(inverseModelTransform * glm::dvec4(cameraPosition, 1.0)); const SurfacePositionHandle posHandle = globe->calculateSurfacePositionHandle( diff --git a/modules/globebrowsing/scripts/layer_support.lua b/modules/globebrowsing/scripts/layer_support.lua index 077585ae64..c60c487d0b 100644 --- a/modules/globebrowsing/scripts/layer_support.lua +++ b/modules/globebrowsing/scripts/layer_support.lua @@ -82,10 +82,11 @@ openspace.globebrowsing.documentation = { }, { Name = "addFocusNodeFromLatLong", - Arguments = "string, number, number, string", + Arguments = "string, string, number, number, number", Documentation = "Creates a new SceneGraphNode that can be used as focus node. " .. - "Usage: openspace.globebrowsing.addFocusNodeFromLatLong(\"Olympus Mons\", -18.65, 226.2, \"Mars\")" + "Usage: openspace.globebrowsing.addFocusNodeFromLatLong(" .. + "\"Olympus Mons\", \"Mars\", -18.65, 226.2, optionalAltitude)" }, { Name = "loadWMSServersFromFile", @@ -289,10 +290,10 @@ openspace.globebrowsing.addFocusNodesFromDirectory = function (dir, node_name) end end -openspace.globebrowsing.addFocusNodeFromLatLong = function (name, lat, long, globe_identifier) - openspace.printInfo("Creating focus node for '" .. name .. "'") +openspace.globebrowsing.addFocusNodeFromLatLong = function (name, globe_identifier, lat, long, altitude) + altitude = altitude or 0; - local a, b, c = openspace.globebrowsing.getGeoPosition(globe_identifier, lat, long, 0.0) + local a, b, c = openspace.globebrowsing.getGeoPosition(globe_identifier, lat, long, altitude) local p = { a, b, c } local identifier = globe_identifier .. "-" .. name @@ -301,8 +302,11 @@ openspace.globebrowsing.addFocusNodeFromLatLong = function (name, lat, long, glo Parent = globe_identifier, Transform = { Translation = { - Type = "StaticTranslation", - Position = { p[1], p[2], p[3] } + Type = "GlobeTranslation", + Globe = globe_identifier, + Latitude = lat, + Longitude = long, + FixedAltitude = altitude } }, GUI = { diff --git a/modules/globebrowsing/src/dashboarditemglobelocation.cpp b/modules/globebrowsing/src/dashboarditemglobelocation.cpp index d309626922..b3e10f28d7 100644 --- a/modules/globebrowsing/src/dashboarditemglobelocation.cpp +++ b/modules/globebrowsing/src/dashboarditemglobelocation.cpp @@ -30,6 +30,7 @@ #include #include #include +#include #include #include #include @@ -142,7 +143,7 @@ DashboardItemGlobeLocation::DashboardItemGlobeLocation( void DashboardItemGlobeLocation::render(glm::vec2& penPosition) { using namespace globebrowsing; - SceneGraphNode* n = global::navigationHandler.focusNode(); + const SceneGraphNode* n = global::navigationHandler.orbitalNavigator().anchorNode(); const RenderableGlobe* globe = dynamic_cast(n->renderable()); if (!globe) { return; diff --git a/modules/imgui/src/guiglobebrowsingcomponent.cpp b/modules/imgui/src/guiglobebrowsingcomponent.cpp index 8ac7e195de..4302cffb0c 100644 --- a/modules/imgui/src/guiglobebrowsingcomponent.cpp +++ b/modules/imgui/src/guiglobebrowsingcomponent.cpp @@ -29,6 +29,7 @@ #include #include #include +#include #include #include #include @@ -122,7 +123,9 @@ void GuiGlobeBrowsingComponent::render() { // node // Check if the focus node is a RenderableGlobe - const SceneGraphNode* const focus = global::navigationHandler.focusNode(); + const SceneGraphNode* const focus = + global::navigationHandler.orbitalNavigator().anchorNode(); + const auto it = std::find(nodes.cbegin(), nodes.cend(), focus); if (it != nodes.end()) { _currentNode = focus->identifier(); @@ -145,7 +148,9 @@ void GuiGlobeBrowsingComponent::render() { ImGui::SameLine(); bool selectFocusNode = ImGui::Button("From Focus"); if (selectFocusNode) { - const SceneGraphNode* const focus = global::navigationHandler.focusNode(); + const SceneGraphNode* const focus = + global::navigationHandler.orbitalNavigator().anchorNode(); + const auto it = std::find(nodes.cbegin(), nodes.cend(), focus); if (it != nodes.end()) { _currentNode = focus->identifier(); diff --git a/modules/imgui/src/guispacetimecomponent.cpp b/modules/imgui/src/guispacetimecomponent.cpp index 3b706c021c..e842675d06 100644 --- a/modules/imgui/src/guispacetimecomponent.cpp +++ b/modules/imgui/src/guispacetimecomponent.cpp @@ -29,6 +29,7 @@ #include #include #include +#include #include #include #include @@ -113,7 +114,8 @@ void GuiSpaceTimeComponent::render() { ImGui::NewLine(); ImGui::SetCursorPosY(ImGui::GetCursorPosY() + 10.f); - SceneGraphNode* currentFocus = global::navigationHandler.focusNode(); + const SceneGraphNode* currentFocus = + global::navigationHandler.orbitalNavigator().anchorNode(); std::string nodeNames; for (SceneGraphNode* n : nodes) { diff --git a/modules/multiresvolume/rendering/multiresvolumeraycaster.cpp b/modules/multiresvolume/rendering/multiresvolumeraycaster.cpp index 8eddda4f16..8c65973526 100644 --- a/modules/multiresvolume/rendering/multiresvolumeraycaster.cpp +++ b/modules/multiresvolume/rendering/multiresvolumeraycaster.cpp @@ -133,7 +133,7 @@ bool MultiresVolumeRaycaster::isCameraInside(const RenderData& data, glm::vec3& localPosition) { // Camera rig position in world coordinates. - glm::vec4 rigWorldPos = glm::vec4(data.camera.position().vec3(), 1.0); + glm::vec4 rigWorldPos = glm::vec4(data.camera.positionVec3(), 1.0); //rigWorldPos /= data.camera.scaling().x * pow(10.0, data.camera.scaling().y); //glm::mat4 invSgctMatrix = glm::inverse(data.camera.viewMatrix()); diff --git a/modules/server/src/topics/triggerpropertytopic.cpp b/modules/server/src/topics/triggerpropertytopic.cpp index 04fc148d65..b86d5b3f68 100644 --- a/modules/server/src/topics/triggerpropertytopic.cpp +++ b/modules/server/src/topics/triggerpropertytopic.cpp @@ -28,6 +28,8 @@ #include #include #include +#include +#include namespace { constexpr const char* PropertyKey = "property"; @@ -40,22 +42,19 @@ namespace openspace { void TriggerPropertyTopic::handleJson(const nlohmann::json& json) { try { const std::string& propertyKey = json.at(PropertyKey).get(); - - properties::Property* prop = property(propertyKey); - if (prop) { - LDEBUG("Triggering " + propertyKey); - prop->set("poke"); - } - else { - LWARNING("Could not find property " + propertyKey); - } + global::scriptEngine.queueScript( + fmt::format( + "openspace.setPropertyValueSingle(\"{}\", nil)", propertyKey + ), + scripting::ScriptEngine::RemoteScripting::Yes + ); } catch (const std::out_of_range& e) { - LERROR("Could not poke property -- key or value is missing in payload"); + LERROR("Could not trigger property -- key or value is missing in payload"); LERROR(e.what()); } catch (const ghoul::RuntimeError& e) { - LERROR("Could not poke property -- runtime error:"); + LERROR("Could not trigger property -- runtime error:"); LERROR(e.what()); } } diff --git a/modules/touch/ext/CMakeLists.txt b/modules/touch/ext/CMakeLists.txt index 1a839c86ff..44dd7cc798 100644 --- a/modules/touch/ext/CMakeLists.txt +++ b/modules/touch/ext/CMakeLists.txt @@ -75,4 +75,9 @@ add_library(libTUIO11 target_include_directories(libTUIO11 PUBLIC ${PROJECT_SOURCE_DIR}/libTUIO11/ ${PROJECT_SOURCE_DIR}/libTUIO11/oscpack - ${PROJECT_SOURCE_DIR}/libTUIO11/TUIO) \ No newline at end of file + ${PROJECT_SOURCE_DIR}/libTUIO11/TUIO) + +if (WIN32) + # Tuio dependencies + target_link_libraries(libTUIO11 PRIVATE winmm.lib wininet.lib ws2_32.lib) +endif() diff --git a/modules/touch/include/touchinteraction.h b/modules/touch/include/touchinteraction.h index 5ba2cf0ce2..ff6fd329ad 100644 --- a/modules/touch/include/touchinteraction.h +++ b/modules/touch/include/touchinteraction.h @@ -133,8 +133,8 @@ public: // Get & Setters Camera* getCamera(); - SceneGraphNode* getFocusNode(); - void setFocusNode(SceneGraphNode* focusNode); + const SceneGraphNode* getFocusNode(); + void setFocusNode(const SceneGraphNode* focusNode); void setCamera(Camera* camera); private: @@ -185,7 +185,6 @@ private: void resetToDefault(); Camera* _camera = nullptr; - SceneGraphNode* _focusNode = nullptr; // Property variables properties::StringProperty _origin; diff --git a/modules/touch/src/touchinteraction.cpp b/modules/touch/src/touchinteraction.cpp index d0878ed4c6..cdc5cb728e 100644 --- a/modules/touch/src/touchinteraction.cpp +++ b/modules/touch/src/touchinteraction.cpp @@ -1021,6 +1021,8 @@ void TouchInteraction::computeVelocities(const std::vector& list, { const TuioCursor& cursor = list.at(0); const int action = interpretInteraction(list, lastProcessed); + const SceneGraphNode* anchor = + global::navigationHandler.orbitalNavigator().anchorNode(); #ifdef TOUCH_DEBUG_PROPERTIES const std::map interactionNames = { @@ -1082,11 +1084,11 @@ void TouchInteraction::computeVelocities(const std::vector& list, ) / lastProcessed.size(); glm::dvec3 camPos = _camera->positionVec3(); - glm::dvec3 centerPos = _focusNode->worldPosition(); + glm::dvec3 centerPos = anchor->worldPosition(); glm::dvec3 currDistanceToFocusNode = camPos - centerPos; double distanceFromFocusSurface = - length(currDistanceToFocusNode) - _focusNode->boundingSphere(); + length(currDistanceToFocusNode) - anchor->boundingSphere(); double zoomFactor = (distance - lastDistance); #ifdef TOUCH_DEBUG_PROPERTIES pinchConsecCt++; @@ -1161,12 +1163,9 @@ void TouchInteraction::computeVelocities(const std::vector& list, // pick something in the scene as focus node if (_pickingSelected) { setFocusNode(_pickingSelected); - // cant do setFocusNode() since TouchInteraction is not subclass of - // InteractionMode - global::navigationHandler.setFocusNode(_focusNode); // rotate camera to look at new focus, using slerp quat - glm::dvec3 camToFocus = _focusNode->worldPosition() - + glm::dvec3 camToFocus = _pickingSelected->worldPosition() - _camera->positionVec3(); glm::dvec3 forward = glm::normalize(_camera->viewDirectionWorldSpace()); double angle = glm::angle(forward, camToFocus); @@ -1206,8 +1205,15 @@ double TouchInteraction::computeConstTimeDecayCoefficient(double velocity) { } double TouchInteraction::computeTapZoomDistance(double zoomGain) { - double dist = glm::distance(_camera->positionVec3(), _camera->focusPositionVec3()); - dist -= _focusNode->boundingSphere(); + const SceneGraphNode* anchor = + global::navigationHandler.orbitalNavigator().anchorNode(); + + double dist = glm::distance( + _camera->positionVec3(), + global::navigationHandler.orbitalNavigator().anchorNode()->worldPosition() + ); + + dist -= anchor->boundingSphere(); double newVelocity = dist * _tapZoomFactor; newVelocity *= std::max(_touchScreenSize.value() * 0.1, 1.0); @@ -1221,13 +1227,16 @@ double TouchInteraction::computeTapZoomDistance(double zoomGain) { void TouchInteraction::step(double dt) { using namespace glm; + const SceneGraphNode* anchor = + global::navigationHandler.orbitalNavigator().anchorNode(); + // since functions cant be called directly (TouchInteraction not a subclass of // InteractionMode) - setFocusNode(global::navigationHandler.focusNode()); - if (_focusNode && _camera) { + setFocusNode(global::navigationHandler.orbitalNavigator().anchorNode()); + if (anchor && _camera) { // Create variables from current state dvec3 camPos = _camera->positionVec3(); - dvec3 centerPos = _focusNode->worldPosition(); + dvec3 centerPos = anchor->worldPosition(); dvec3 directionToCenter = normalize(centerPos - camPos); dvec3 centerToCamera = camPos - centerPos; @@ -1245,7 +1254,7 @@ void TouchInteraction::step(double dt) { dquat globalCamRot = normalize(quat_cast(inverse(lookAtMat))); dquat localCamRot = inverse(globalCamRot) * _camera->rotationQuaternion(); - double boundingSphere = _focusNode->boundingSphere(); + double boundingSphere = anchor->boundingSphere(); dvec3 centerToBoundingSphere; double distance = std::max(length(centerToCamera) - boundingSphere, 0.0); _currentRadius = boundingSphere / @@ -1399,10 +1408,6 @@ void TouchInteraction::decelerate(double dt) { _vel.roll *= computeDecayCoeffFromFrametime(_constTimeDecayCoeff.roll, times); _vel.pan *= computeDecayCoeffFromFrametime(_constTimeDecayCoeff.pan, times); _vel.zoom *= computeDecayCoeffFromFrametime(_constTimeDecayCoeff.zoom, times); - - glm::dvec3 camPos = _camera->positionVec3(); - glm::dvec3 centerPos = _focusNode->worldPosition(); - glm::dvec3 centerToCamera = camPos - centerPos; } double TouchInteraction::computeDecayCoeffFromFrametime(double coeff, int times) { @@ -1489,14 +1494,14 @@ Camera* TouchInteraction::getCamera() { return _camera; } -SceneGraphNode* TouchInteraction::getFocusNode() { - return _focusNode; +const SceneGraphNode* TouchInteraction::getFocusNode() { + return global::navigationHandler.orbitalNavigator().anchorNode(); } void TouchInteraction::setCamera(Camera* camera) { _camera = camera; } -void TouchInteraction::setFocusNode(SceneGraphNode* focusNode) { - _focusNode = focusNode; +void TouchInteraction::setFocusNode(const SceneGraphNode* focusNode) { + global::navigationHandler.orbitalNavigator().setAnchorNode(focusNode->identifier()); } void FrameTimeAverage::updateWithNewFrame(double sample) { diff --git a/modules/touch/touchmodule.cpp b/modules/touch/touchmodule.cpp index 8e8e8f894d..b4022843ef 100644 --- a/modules/touch/touchmodule.cpp +++ b/modules/touch/touchmodule.cpp @@ -28,6 +28,7 @@ #include #include #include +#include #include #include #include @@ -119,7 +120,7 @@ TouchModule::TouchModule() : OpenSpaceModule("Touch") { global::callback::preSync.push_back([&]() { touch.setCamera(global::navigationHandler.camera()); - touch.setFocusNode(global::navigationHandler.focusNode()); + touch.setFocusNode(global::navigationHandler.orbitalNavigator().anchorNode()); if (hasNewInput() && global::windowDelegate.isMaster()) { touch.updateStateFromInput(listOfContactPoints, lastProcessed); diff --git a/modules/webbrowser/src/eventhandler.cpp b/modules/webbrowser/src/eventhandler.cpp index df9b6bdc7d..1e976d4fb5 100644 --- a/modules/webbrowser/src/eventhandler.cpp +++ b/modules/webbrowser/src/eventhandler.cpp @@ -215,6 +215,7 @@ bool EventHandler::mouseWheelCallback(glm::ivec2 delta) { bool EventHandler::charCallback(unsigned int charCode, KeyModifier modifier) { CefKeyEvent keyEvent; keyEvent.windows_key_code = charCode; + keyEvent.character = charCode; keyEvent.modifiers = static_cast(modifier); keyEvent.type = KEYEVENT_CHAR; // TODO(klas): figure out when to block diff --git a/src/engine/openspaceengine.cpp b/src/engine/openspaceengine.cpp index 078814d518..ab45c29135 100644 --- a/src/engine/openspaceengine.cpp +++ b/src/engine/openspaceengine.cpp @@ -39,6 +39,7 @@ #include #include #include +#include #include #include #include @@ -651,7 +652,14 @@ void OpenSpaceEngine::loadSingleAsset(const std::string& assetPath) { global::renderEngine.setCamera(camera); global::navigationHandler.setCamera(camera); - global::navigationHandler.setFocusNode(camera->parent()); + const SceneGraphNode* parent = camera->parent(); + if (parent) { + global::navigationHandler.orbitalNavigator().setFocusNode(parent->identifier()); + } else { + global::navigationHandler.orbitalNavigator().setFocusNode( + _scene->root()->identifier() + ); + } global::renderEngine.setScene(_scene.get()); diff --git a/src/interaction/externinteraction.cpp b/src/interaction/externinteraction.cpp index 5144789fbd..b3455fcd58 100644 --- a/src/interaction/externinteraction.cpp +++ b/src/interaction/externinteraction.cpp @@ -106,19 +106,22 @@ void ExternInteraction::scriptInteraction(datamessagestructures::ScriptMessage s datamessagestructures::CameraKeyframe ExternInteraction::generateCameraKeyframe() { datamessagestructures::CameraKeyframe kf; - SceneGraphNode* focusNode = global::navigationHandler.focusNode(); + const SceneGraphNode* focusNode = + global::navigationHandler.orbitalNavigator().anchorNode(); + if (!focusNode) { return kf; } //kf._position = global::navigationHandler.camera()->positionVec3(); - kf._position = global::navigationHandler.focusNodeToCameraVector(); + kf._position = global::navigationHandler.orbitalNavigator().anchorNodeToCameraVector(); kf._followNodeRotation = global::navigationHandler.orbitalNavigator().followingNodeRotation(); if (kf._followNodeRotation) { kf._position = glm::inverse(focusNode->worldRotationMatrix()) * kf._position; - kf._rotation = global::navigationHandler.focusNodeToCameraRotation(); + kf._rotation = + global::navigationHandler.orbitalNavigator().anchorNodeToCameraVector(); } else { kf._rotation = global::navigationHandler.camera()->rotationQuaternion(); diff --git a/src/interaction/navigationhandler.cpp b/src/interaction/navigationhandler.cpp index 06ba77616c..71bcaa1fd5 100644 --- a/src/interaction/navigationhandler.cpp +++ b/src/interaction/navigationhandler.cpp @@ -25,7 +25,6 @@ #include #include -#include #include #include #include @@ -41,18 +40,11 @@ namespace { constexpr const char* _loggerCat = "NavigationHandler"; - constexpr const char* KeyFocus = "Focus"; + constexpr const char* KeyAnchor = "Anchor"; + constexpr const char* KeyAim = "Aim"; constexpr const char* KeyPosition = "Position"; constexpr const char* KeyRotation = "Rotation"; - constexpr const openspace::properties::Property::PropertyInfo OriginInfo = { - "Origin", - "Origin", - "The name of the scene graph node that is the origin of the camera interaction. " - "The camera is always focussed on this object and every interaction is relative " - "towards this object. Any scene graph node can be the origin node." - }; - constexpr const openspace::properties::Property::PropertyInfo KeyFrameInfo = { "UseKeyFrameInteraction", "Use keyframe interaction", @@ -67,31 +59,14 @@ namespace openspace::interaction { NavigationHandler::NavigationHandler() : properties::PropertyOwner({ "NavigationHandler" }) - , _origin(OriginInfo) , _useKeyFrameInteraction(KeyFrameInfo, false) { - _origin.onChange([this]() { - if (_origin.value().empty()) { - return; - } - - SceneGraphNode* node = sceneGraphNode(_origin.value()); - if (!node) { - LWARNING(fmt::format( - "Could not find a node in scenegraph called '{}'", _origin.value() - )); - return; - } - setFocusNode(node); - resetCameraDirection(); - }); _inputState = std::make_unique(); _orbitalNavigator = std::make_unique(); _keyframeNavigator = std::make_unique(); // Add the properties - addProperty(_origin); addProperty(_useKeyFrameInteraction); addPropertySubOwner(*_orbitalNavigator); } @@ -113,23 +88,19 @@ void NavigationHandler::deinitialize() { global::parallelPeer.connectionEvent().unsubscribe("NavigationHandler"); } -void NavigationHandler::setFocusNode(SceneGraphNode* node) { - _orbitalNavigator->setFocusNode(node); - _camera->setFocusPositionVec3(focusNode()->worldPosition()); -} - void NavigationHandler::setCamera(Camera* camera) { _camera = camera; -} - -void NavigationHandler::resetCameraDirection() { - _orbitalNavigator->startInterpolateCameraDirection(*_camera); + _orbitalNavigator->setCamera(camera); } const OrbitalNavigator& NavigationHandler::orbitalNavigator() const { return *_orbitalNavigator; } +OrbitalNavigator& NavigationHandler::orbitalNavigator() { + return *_orbitalNavigator; +} + KeyframeNavigator& NavigationHandler::keyframeNavigator() const { return *_keyframeNavigator; } @@ -139,11 +110,11 @@ bool NavigationHandler::isKeyFrameInteractionEnabled() const { } float NavigationHandler::interpolationTime() const { - return _orbitalNavigator->rotateToFocusInterpolationTime(); + return _orbitalNavigator->retargetInterpolationTime(); } void NavigationHandler::setInterpolationTime(float durationInSeconds) { - _orbitalNavigator->setRotateToFocusInterpolationTime(durationInSeconds); + _orbitalNavigator->setRetargetInterpolationTime(durationInSeconds); } void NavigationHandler::updateCamera(double deltaTime) { @@ -154,14 +125,13 @@ void NavigationHandler::updateCamera(double deltaTime) { _cameraUpdatedFromScript = false; } else if ( ! _playbackModeEnabled ) { - if (_camera && focusNode()) { + if (_camera) { if (_useKeyFrameInteraction) { _keyframeNavigator->updateCamera(*_camera, _playbackModeEnabled); } else { _orbitalNavigator->updateStatesFromInput(*_inputState, deltaTime); - _orbitalNavigator->updateCameraStateFromStates(*_camera, deltaTime); - _camera->setFocusPositionVec3(focusNode()->worldPosition()); + _orbitalNavigator->updateCameraStateFromStates(deltaTime); } } } @@ -183,21 +153,6 @@ void NavigationHandler::stopPlayback() { _playbackModeEnabled = false; } -SceneGraphNode* NavigationHandler::focusNode() const { - return _orbitalNavigator->focusNode(); -} - -glm::dvec3 NavigationHandler::focusNodeToCameraVector() const { - return _camera->positionVec3() - focusNode()->worldPosition(); -} - -glm::quat NavigationHandler::focusNodeToCameraRotation() const { - glm::dmat4 invWorldRotation = glm::dmat4( - glm::inverse(focusNode()->worldRotationMatrix()) - ); - return glm::quat(invWorldRotation) * glm::quat(_camera->rotationQuaternion()); -} - Camera* NavigationHandler::camera() const { return _camera; } @@ -227,13 +182,15 @@ void NavigationHandler::setCameraStateFromDictionary(const ghoul::Dictionary& ca { bool readSuccessful = true; - std::string focus; + std::string anchor; + std::string aim; glm::dvec3 cameraPosition; glm::dvec4 cameraRotation; // Need to read the quaternion as a vector first. - readSuccessful &= cameraDict.getValue(KeyFocus, focus); + readSuccessful &= cameraDict.getValue(KeyAnchor, anchor); readSuccessful &= cameraDict.getValue(KeyPosition, cameraPosition); readSuccessful &= cameraDict.getValue(KeyRotation, cameraRotation); + cameraDict.getValue(KeyAim, aim); // Aim is not required if (!readSuccessful) { throw ghoul::RuntimeError( @@ -242,7 +199,9 @@ void NavigationHandler::setCameraStateFromDictionary(const ghoul::Dictionary& ca } // Set state - _origin = focus; + _orbitalNavigator->setAnchorNode(anchor); + _orbitalNavigator->setAimNode(aim); + _camera->setPositionVec3(cameraPosition); _camera->setRotation(glm::dquat( cameraRotation.x, cameraRotation.y, cameraRotation.z, cameraRotation.w)); @@ -260,7 +219,10 @@ ghoul::Dictionary NavigationHandler::cameraStateDictionary() { ghoul::Dictionary cameraDict; cameraDict.setValue(KeyPosition, cameraPosition); cameraDict.setValue(KeyRotation, cameraRotation); - cameraDict.setValue(KeyFocus, focusNode()->identifier()); + cameraDict.setValue(KeyAnchor, _orbitalNavigator->anchorNode()->identifier()); + if (_orbitalNavigator->aimNode()) { + cameraDict.setValue(KeyAim, _orbitalNavigator->aimNode()->identifier()); + } return cameraDict; } @@ -281,8 +243,16 @@ void NavigationHandler::saveCameraStateToFile(const std::string& filepath) { glm::dquat q = _camera->rotationQuaternion(); ofs << "return {" << std::endl; - ofs << " " << KeyFocus << " = " << "\"" << focusNode()->identifier() << "\"" + ofs << " " << KeyAnchor << " = " << "\"" << + _orbitalNavigator->anchorNode()->identifier() << "\"" << "," << std::endl; + + if (_orbitalNavigator->aimNode()) { + ofs << " " << KeyAim << " = " << "\"" << + _orbitalNavigator->aimNode()->identifier() << "\"" + << "," << std::endl; + } + ofs << " " << KeyPosition << " = {" << std::to_string(p.x) << ", " << std::to_string(p.y) << ", " @@ -391,11 +361,18 @@ scripting::LuaLibrary NavigationHandler::luaLibrary() { "Restore the camera state from file" }, { - "resetCameraDirection", - &luascriptfunctions::resetCameraDirection, + "retargetAnchor", + &luascriptfunctions::retargetAnchor, {}, "void", - "Reset the camera direction to point at the focus node" + "Reset the camera direction to point at the anchor node" + }, + { + "retargetAim", + &luascriptfunctions::retargetAim, + {}, + "void", + "Reset the camera direction to point at the aim node" }, { "bindJoystickAxis", diff --git a/src/interaction/navigationhandler_lua.inl b/src/interaction/navigationhandler_lua.inl index 74ea2c4681..6abe5b1d9e 100644 --- a/src/interaction/navigationhandler_lua.inl +++ b/src/interaction/navigationhandler_lua.inl @@ -86,10 +86,19 @@ int saveCameraStateToFile(lua_State* L) { return 0; } -int resetCameraDirection(lua_State* L) { - ghoul::lua::checkArgumentsAndThrow(L, 0, "lua::resetCameraDirection"); +int retargetAnchor(lua_State* L) { + ghoul::lua::checkArgumentsAndThrow(L, 0, "lua::retargetAnchor"); - global::navigationHandler.resetCameraDirection(); + global::navigationHandler.orbitalNavigator().startRetargetAnchor(); + + ghoul_assert(lua_gettop(L) == 0, "Incorrect number of items left on stack"); + return 0; +} + +int retargetAim(lua_State* L) { + ghoul::lua::checkArgumentsAndThrow(L, 0, "lua::retargetAim"); + + global::navigationHandler.orbitalNavigator().startRetargetAim(); ghoul_assert(lua_gettop(L) == 0, "Incorrect number of items left on stack"); return 0; diff --git a/src/interaction/orbitalnavigator.cpp b/src/interaction/orbitalnavigator.cpp index e2a5fe349f..3c02647633 100644 --- a/src/interaction/orbitalnavigator.cpp +++ b/src/interaction/orbitalnavigator.cpp @@ -26,9 +26,50 @@ #include #include +#include +#include #include namespace { + constexpr const char* _loggerCat = "OrbitalNavigator"; + + constexpr const double AngleEpsilon = 1E-7; + constexpr const double DistanceRatioAimThreshold = 1E-4; + + constexpr const openspace::properties::Property::PropertyInfo AnchorInfo = { + "Anchor", + "Anchor", + "The name of the scene graph node that is the origin of the camera interaction. " + "The camera follows, orbits and dollies towards this node. " + "Any scene graph node can be the anchor node." + }; + + constexpr const openspace::properties::Property::PropertyInfo AimInfo = { + "Aim", + "Aim", + "The name of the scene graph node that is the aim of the camera. " + "The camera direction is relative to the vector from the camera position " + "to this node." + }; + + constexpr const openspace::properties::Property::PropertyInfo + RetargetAnchorInfo = + { + "RetargetAnchor", + "Retarget Anchor", + "When triggered, this property starts an interpolation to reset the " + "camera direction to the anchor node." + }; + + constexpr const openspace::properties::Property::PropertyInfo + RetargetAimInfo = + { + "RetargetAim", + "Retarget Aim", + "When triggered, this property starts an interpolation to reset the " + "camera direction to the aim node." + }; + constexpr openspace::properties::Property::PropertyInfo RollFrictionInfo = { "RollFriction", "Roll Friction", @@ -76,9 +117,9 @@ namespace { "faster the camera movements will stop." }; - constexpr openspace::properties::Property::PropertyInfo FollowFocusNodeInfo = { - "FollowFocusNodeRotationDistance", - "Follow focus node rotation distance", + constexpr openspace::properties::Property::PropertyInfo FollowAnchorNodeInfo = { + "FollowAnchorNodeRotationDistance", + "Follow anchor node rotation distance", "" // @TODO Missing documentation }; @@ -93,24 +134,31 @@ namespace { "StereoInterpolationTime", "Stereo interpolation time", "The time to interpolate to a new stereoscopic depth " - "when the focus node is changed" + "when the anchor node is changed." }; constexpr openspace::properties::Property::PropertyInfo - RotateToFocusInterpolationTimeInfo = { - "RotateToFocusInterpolationTime", - "Rotate to focus interpolation time", + RetargetInterpolationTimeInfo = { + "RetargetAnchorInterpolationTime", + "Retarget interpolation time", "The time to interpolate the camera rotation " - "when the focus node is changed" - }; + "when the anchor or aim node is changed." + }; + + constexpr openspace::properties::Property::PropertyInfo + FollowRotationInterpolationTimeInfo = { + "FollowRotationInterpolationTime", + "Follow rotation interpolation time", + "The interpolation time when toggling following focus node rotation." + }; constexpr openspace::properties::Property::PropertyInfo UseAdaptiveStereoscopicDepthInfo = { "UseAdaptiveStereoscopicDepth", "Adaptive Steroscopic Depth", "Dynamically adjust the view scaling based on the distance to the surface of " - "the focus node. If enabled, view scale will be set to " - "StereoscopicDepthOfFocusSurface / distance. " + "the anchor and aim nodes. If enabled, view scale will be set to " + "StereoscopicDepthOfFocusSurface / min(anchorDistance, aimDistance). " "If disabled, view scale will be set to 10^StaticViewScaleExponent." }; @@ -126,8 +174,8 @@ namespace { StereoscopicDepthOfFocusSurfaceInfo = { "StereoscopicDepthOfFocusSurface", "Stereoscopic depth of the surface in focus", - "Set the stereoscopically perceived distance (in meters) to the surface of " - "the focus node. " + "Set the stereoscopically perceived distance (in meters) to the closest " + "point out of the surface of the anchor and the center of the aim node. " "Only used if UseAdaptiveStereoscopicDepthInfo is set to true." }; } // namespace @@ -149,18 +197,67 @@ OrbitalNavigator::Friction::Friction() OrbitalNavigator::OrbitalNavigator() : properties::PropertyOwner({ "OrbitalNavigator" }) - , _followFocusNodeRotationDistance(FollowFocusNodeInfo, 5.0f, 0.0f, 20.f) + , _anchor(AnchorInfo) + , _aim(AimInfo) + , _retargetAnchor(RetargetAnchorInfo) + , _retargetAim(RetargetAimInfo) + , _followAnchorNodeRotationDistance(FollowAnchorNodeInfo, 5.0f, 0.0f, 20.f) , _minimumAllowedDistance(MinimumDistanceInfo, 10.0f, 0.0f, 10000.f) , _mouseSensitivity(MouseSensitivityInfo, 15.0f, 1.0f, 50.f) , _joystickSensitivity(JoystickSensitivityInfo, 10.0f, 1.0f, 50.f) , _useAdaptiveStereoscopicDepth(UseAdaptiveStereoscopicDepthInfo, true) , _stereoscopicDepthOfFocusSurface(StereoscopicDepthOfFocusSurfaceInfo, 8, 0.25, 100) , _staticViewScaleExponent(StaticViewScaleExponentInfo, 0.f, -30, 10) - , _rotateToFocusInterpolationTime(RotateToFocusInterpolationTimeInfo, 2.0, 0.0, 10.0) + , _retargetInterpolationTime(RetargetInterpolationTimeInfo, 2.0, 0.0, 10.0) , _stereoInterpolationTime(StereoInterpolationTimeInfo, 8.0, 0.0, 10.0) + , _followRotationInterpolationTime(FollowRotationInterpolationTimeInfo, 1.0, 0.0, 10.0) , _mouseStates(_mouseSensitivity * 0.0001, 1 / (_friction.friction + 0.0000001)) , _joystickStates(_joystickSensitivity * 0.1, 1 / (_friction.friction + 0.0000001)) { + + _anchor.onChange([this]() { + if (_anchor.value().empty()) { + return; + } + SceneGraphNode* node = sceneGraphNode(_anchor.value()); + if (node) { + setAnchorNode(node); + } + else { + LERROR(fmt::format( + "No scenegraph node with identifier {} exists.", _anchor.value() + )); + } + }); + + _aim.onChange([this]() { + if (_aim.value().empty()) { + setAimNode(nullptr); + return; + } + SceneGraphNode* node = sceneGraphNode(_aim.value()); + if (node) { + setAimNode(node); + } + else { + LERROR(fmt::format( + "No scenegraph node with identifier {} exists.", _aim.value() + )); + } + }); + + _retargetAnchor.onChange([this]() { + startRetargetAnchor(); + }); + + _retargetAim.onChange([this]() { + if (_aimNode && _aimNode != _anchorNode) { + startRetargetAim(); + } else { + startRetargetAnchor(); + } + }); + _followRotationInterpolator.setTransferFunction([](double t) { const double res = 3.0 * t*t - 2.0 * t*t*t; return glm::clamp(res, 0.0, 1.0); @@ -183,7 +280,8 @@ OrbitalNavigator::OrbitalNavigator() auto smoothStepDerivedTranferFunction = [](double t) { return (6 * (t + t*t) / (1 - 3 * t*t + 2 * t*t*t)); }; - _rotateToFocusNodeInterpolator.setTransferFunction(smoothStepDerivedTranferFunction); + _retargetAnchorInterpolator.setTransferFunction(smoothStepDerivedTranferFunction); + _retargetAimInterpolator.setTransferFunction(smoothStepDerivedTranferFunction); _cameraToSurfaceDistanceInterpolator.setTransferFunction( smoothStepDerivedTranferFunction ); @@ -215,19 +313,38 @@ OrbitalNavigator::OrbitalNavigator() addPropertySubOwner(_friction); - addProperty(_followFocusNodeRotationDistance); + + addProperty(_anchor); + addProperty(_aim); + addProperty(_retargetAnchor); + addProperty(_retargetAim); + addProperty(_followAnchorNodeRotationDistance); addProperty(_minimumAllowedDistance); addProperty(_useAdaptiveStereoscopicDepth); addProperty(_staticViewScaleExponent); addProperty(_stereoscopicDepthOfFocusSurface); - addProperty(_rotateToFocusInterpolationTime); + addProperty(_retargetInterpolationTime); addProperty(_stereoInterpolationTime); + addProperty(_followRotationInterpolationTime); + addProperty(_mouseSensitivity); addProperty(_joystickSensitivity); } +glm::dvec3 OrbitalNavigator::anchorNodeToCameraVector() const { + return _camera->positionVec3() - anchorNode()->worldPosition(); +} + +glm::quat OrbitalNavigator::anchorNodeToCameraRotation() const { + glm::dmat4 invWorldRotation = glm::dmat4( + glm::inverse(anchorNode()->worldRotationMatrix()) + ); + return glm::quat(invWorldRotation) * glm::quat(_camera->rotationQuaternion()); +} + + void OrbitalNavigator::updateStatesFromInput(const InputState& inputState, double deltaTime) { @@ -235,132 +352,183 @@ void OrbitalNavigator::updateStatesFromInput(const InputState& inputState, _joystickStates.updateStateFromInput(inputState, deltaTime); } -void OrbitalNavigator::updateCameraStateFromStates(Camera& camera, double deltaTime) { - if (_focusNode) { - // Read the current state of the camera - glm::dvec3 camPos = camera.positionVec3(); - const glm::dvec3 centerPos = _focusNode->worldPosition(); - - // Follow focus nodes movement - glm::dvec3 focusNodeDiff = centerPos - _previousFocusNodePosition; - _previousFocusNodePosition = centerPos; - camPos += focusNodeDiff; - - // Calculate a position handle based on the camera position in world space - SurfacePositionHandle posHandle = calculateSurfacePositionHandle(camPos); - - // Decompose camera rotation so that we can handle global and local rotation - // individually. Then we combine them again when finished. - CameraRotationDecomposition camRot = decomposeCameraRotation( - camPos, - camera.rotationQuaternion(), - camera.lookUpVectorWorldSpace(), - camera.viewDirectionWorldSpace() - ); - - // Rotate with the object by finding a differential rotation from the previous - // to the current rotation - const glm::dmat3 objectStateMatrix = _focusNode->worldRotationMatrix(); - const glm::dquat objectRotation = glm::quat_cast(objectStateMatrix); - glm::dquat focusNodeRotationDiff = _previousFocusNodeRotation * - glm::inverse(objectRotation); - - _previousFocusNodeRotation = objectRotation; - - // Interpolate rotation differential so that the camera rotates with the object - // only if close enough - focusNodeRotationDiff = interpolateRotationDifferential( - deltaTime, - 1.0, - focusNodeRotationDiff, - centerPos, - camPos, - posHandle - ); - - // Update local rotation - camRot.localRotation = roll(deltaTime, camRot.localRotation); - camRot.localRotation = interpolateLocalRotation(deltaTime, camRot.localRotation); - camRot.localRotation = rotateLocally(deltaTime, camRot.localRotation); - - // Horizontal translation - camPos = translateHorizontally( - deltaTime, - camPos, - centerPos, - focusNodeRotationDiff, - camRot.globalRotation, - posHandle - ); - - // Horizontal translation by focus node rotation - camPos = followFocusNodeRotation( - camPos, - centerPos, - focusNodeRotationDiff - ); - - // Recalculate posHandle since horizontal position changed - posHandle = calculateSurfacePositionHandle(camPos); - - camRot.globalRotation = rotateGlobally( - camRot.globalRotation, - centerPos, - focusNodeRotationDiff, - camPos, - posHandle - ); - - // Rotate around the surface out direction - camRot.globalRotation = rotateHorizontally( - deltaTime, - camRot.globalRotation, - camPos, - posHandle - ); - - // Perform the vertical movements - camPos = translateVertically(deltaTime, camPos, centerPos, posHandle); - camPos = pushToSurface( - _minimumAllowedDistance, - camPos, - centerPos, - posHandle - ); - - // Update the camera state - camera.setPositionVec3(camPos); - camera.setRotation(camRot.globalRotation * camRot.localRotation); - - if (_useAdaptiveStereoscopicDepth) { - double targetCameraToSurfaceDistance = glm::length( - cameraToSurfaceVector(camPos, centerPos, posHandle) - ); - if (_directlySetStereoDistance) { - _currentCameraToSurfaceDistance = targetCameraToSurfaceDistance; - _directlySetStereoDistance = false; - } else { - _currentCameraToSurfaceDistance = interpolateCameraToSurfaceDistance( - deltaTime, - _currentCameraToSurfaceDistance, - targetCameraToSurfaceDistance); - } - - camera.setScaling( - _stereoscopicDepthOfFocusSurface / - static_cast(_currentCameraToSurfaceDistance) - ); - } else { - camera.setScaling(glm::pow(10.f, _staticViewScaleExponent)); - } +void OrbitalNavigator::updateCameraStateFromStates(double deltaTime) { + if (!(_anchorNode)) { + // Bail out if the anchor node is not set. + return; } + + const glm::dvec3 anchorPos = _anchorNode->worldPosition(); + const glm::dvec3 prevCameraPosition = _camera->positionVec3(); + const glm::dvec3 anchorDisplacement = anchorPos - _previousAnchorNodePosition; + + CameraPose pose = { + _camera->positionVec3() + anchorDisplacement, + _camera->rotationQuaternion() + }; + + if (_aimNode && _aimNode != _anchorNode) { + const glm::dvec3 aimPos = _aimNode->worldPosition(); + const glm::dvec3 cameraToAnchor = + _previousAnchorNodePosition - prevCameraPosition; + + Displacement anchorToAim = { + _previousAimNodePosition - _previousAnchorNodePosition, + aimPos - anchorPos + }; + + anchorToAim = interpolateRetargetAim( + deltaTime, + pose, + cameraToAnchor, + anchorToAim + ); + + pose = followAim(pose, cameraToAnchor, anchorToAim); + + _previousAimNodePosition = _aimNode->worldPosition(); + _previousAnchorNodeRotation = _anchorNode->worldRotationMatrix(); + } + + _previousAnchorNodePosition = _anchorNode->worldPosition(); + + // Calculate a position handle based on the camera position in world space + SurfacePositionHandle posHandle = + calculateSurfacePositionHandle(*_anchorNode, pose.position); + + // Decompose camera rotation so that we can handle global and local rotation + // individually. Then we combine them again when finished. + // Compensate for relative movement of aim node, + // in order to maintain the old global/local rotation. + CameraRotationDecomposition camRot = + decomposeCameraRotationSurface(pose, *_anchorNode); + + // Rotate with the object by finding a differential rotation from the previous + // to the current rotation. + glm::dquat anchorRotation = + glm::quat_cast(_anchorNode->worldRotationMatrix()); + + glm::dquat anchorNodeRotationDiff = + _previousAnchorNodeRotation * glm::inverse(anchorRotation); + + _previousAnchorNodeRotation = anchorRotation; + + // Interpolate rotation differential so that the camera rotates with the object + // only if close enough + anchorNodeRotationDiff = interpolateRotationDifferential( + deltaTime, + _followRotationInterpolationTime, + anchorNodeRotationDiff, + anchorPos, + pose.position, + posHandle + ); + + // Update local rotation based on user input + camRot.localRotation = roll(deltaTime, camRot.localRotation); + camRot.localRotation = interpolateLocalRotation(deltaTime, camRot.localRotation); + camRot.localRotation = rotateLocally(deltaTime, camRot.localRotation); + + // Horizontal translation based on user input + pose.position = translateHorizontally( + deltaTime, + pose.position, + anchorPos, + camRot.globalRotation, + posHandle + ); + + // Horizontal translation by focus node rotation + pose.position = followAnchorNodeRotation( + pose.position, + anchorPos, + anchorNodeRotationDiff + ); + + // Recalculate posHandle since horizontal position changed + posHandle = calculateSurfacePositionHandle(*_anchorNode, pose.position); + + + // Rotate globally to keep camera rotation fixed + // in the rotating reference frame of the anchor object. + camRot.globalRotation = rotateGlobally( + camRot.globalRotation, + anchorNodeRotationDiff, + posHandle + ); + + + // Rotate around the surface out direction based on user input + camRot.globalRotation = rotateHorizontally( + deltaTime, + camRot.globalRotation, + posHandle + ); + + // Perform the vertical movements based on user input + pose.position = translateVertically(deltaTime, pose.position, anchorPos, posHandle); + pose.position = pushToSurface( + _minimumAllowedDistance, + pose.position, + anchorPos, + posHandle + ); + + // Update the camera state + _camera->setPositionVec3(pose.position); + _camera->setRotation(composeCameraRotation(camRot)); + + if (_useAdaptiveStereoscopicDepth) { + double targetCameraToSurfaceDistance = glm::length( + cameraToSurfaceVector(pose.position, anchorPos, posHandle) + ); + + if (_aimNode) { + targetCameraToSurfaceDistance = std::min( + targetCameraToSurfaceDistance, + glm::distance(pose.position, _aimNode->worldPosition()) + ); + } + + if (_directlySetStereoDistance) { + _currentCameraToSurfaceDistance = targetCameraToSurfaceDistance; + _directlySetStereoDistance = false; + } else { + _currentCameraToSurfaceDistance = interpolateCameraToSurfaceDistance( + deltaTime, + _currentCameraToSurfaceDistance, + targetCameraToSurfaceDistance); + } + + _camera->setScaling( + _stereoscopicDepthOfFocusSurface / + static_cast(_currentCameraToSurfaceDistance) + ); + } else { + _camera->setScaling(glm::pow(10.f, _staticViewScaleExponent)); + } +} + +glm::dquat OrbitalNavigator::composeCameraRotation( + const CameraRotationDecomposition& decomposition) +{ + return decomposition.globalRotation * decomposition.localRotation; +} + +Camera* OrbitalNavigator::camera() const +{ + return _camera; +} + +void OrbitalNavigator::setCamera(Camera* camera) { + _camera = camera; } glm::dvec3 OrbitalNavigator::cameraToSurfaceVector(const glm::dvec3& cameraPos, const glm::dvec3& centerPos, const SurfacePositionHandle& posHandle) { - glm::dmat4 modelTransform = _focusNode->modelTransform(); + glm::dmat4 modelTransform = _anchorNode->modelTransform(); glm::dvec3 posDiff = cameraPos - centerPos; glm::dvec3 centerToActualSurfaceModelSpace = posHandle.centerToReferenceSurface + @@ -372,53 +540,102 @@ glm::dvec3 OrbitalNavigator::cameraToSurfaceVector(const glm::dvec3& cameraPos, return centerToActualSurface - posDiff; } -void OrbitalNavigator::setFocusNode(SceneGraphNode* focusNode) { - if (!_focusNode) { +void OrbitalNavigator::setFocusNode(const SceneGraphNode* focusNode) { + setAnchorNode(focusNode); + setAimNode(nullptr); +} + +void OrbitalNavigator::setFocusNode(const std::string& focusNode) { + _anchor.set(focusNode); + _aim.set(std::string("")); +} + +void OrbitalNavigator::setAnchorNode(const SceneGraphNode* anchorNode) { + if (!_anchorNode) { _directlySetStereoDistance = true; } - _focusNode = focusNode; + _anchorNode = anchorNode; - if (_focusNode) { - _previousFocusNodePosition = _focusNode->worldPosition(); - _previousFocusNodeRotation = glm::quat_cast(_focusNode->worldRotationMatrix()); + if (_anchorNode) { + _previousAnchorNodePosition = _anchorNode->worldPosition(); + _previousAnchorNodeRotation = glm::quat_cast(_anchorNode->worldRotationMatrix()); } } -void OrbitalNavigator::startInterpolateCameraDirection(const Camera& camera) { - const glm::dvec3 camPos = camera.positionVec3(); - const glm::dvec3 camDir = glm::normalize( - camera.rotationQuaternion() * glm::dvec3(0.0, 0.0, -1.0) - ); - const glm::dvec3 centerPos = _focusNode->worldPosition(); +void OrbitalNavigator::setAimNode(const SceneGraphNode* aimNode) { + _retargetAimInterpolator.end(); + _aimNode = aimNode; + + if (_aimNode) { + _previousAimNodePosition = _aimNode->worldPosition(); + } +} + +void OrbitalNavigator::setAnchorNode(const std::string& anchorNode) { + _anchor.set(anchorNode); +} + +void OrbitalNavigator::setAimNode(const std::string& aimNode) { + _aim.set(aimNode); +} + +void OrbitalNavigator::startRetargetAnchor() { + const glm::dvec3 camPos = _camera->positionVec3(); + const glm::dvec3 camDir = _camera->viewDirectionWorldSpace(); + + const glm::dvec3 centerPos = _anchorNode->worldPosition(); const glm::dvec3 directionToCenter = glm::normalize(centerPos - camPos); const double angle = glm::angle(camDir, directionToCenter); - // Minimum is two second. Otherwise proportional to angle - _rotateToFocusNodeInterpolator.setInterpolationTime(static_cast( - glm::max(angle, 1.0) * _rotateToFocusInterpolationTime + // Minimum is _rotateInterpolationTime seconds. Otherwise proportional to angle. + _retargetAnchorInterpolator.setInterpolationTime(static_cast( + glm::max(angle, 1.0) * _retargetInterpolationTime )); - _rotateToFocusNodeInterpolator.start(); + _retargetAnchorInterpolator.start(); _cameraToSurfaceDistanceInterpolator.setInterpolationTime(_stereoInterpolationTime); _cameraToSurfaceDistanceInterpolator.start(); } -float OrbitalNavigator::rotateToFocusInterpolationTime() const { - return _rotateToFocusInterpolationTime; +void OrbitalNavigator::startRetargetAim() { + const glm::dvec3 camPos = _camera->positionVec3(); + const glm::dvec3 camDir = _camera->viewDirectionWorldSpace(); + const glm::dvec3 centerPos = _aimNode->worldPosition(); + const glm::dvec3 directionToCenter = glm::normalize(centerPos - camPos); + + const double angle = glm::angle(camDir, directionToCenter); + + // Minimum is _rotateInterpolationTime seconds. Otherwise proportional to angle. + _retargetAimInterpolator.setInterpolationTime(static_cast( + glm::max(angle, 1.0) * _retargetInterpolationTime + )); + _retargetAimInterpolator.start(); + + _cameraToSurfaceDistanceInterpolator.setInterpolationTime(_stereoInterpolationTime); + _cameraToSurfaceDistanceInterpolator.start(); } -void OrbitalNavigator::setRotateToFocusInterpolationTime(float durationInSeconds) { - _rotateToFocusInterpolationTime = durationInSeconds; + +float OrbitalNavigator::retargetInterpolationTime() const { + return _retargetInterpolationTime; +} + +void OrbitalNavigator::setRetargetInterpolationTime(float durationInSeconds) { + _retargetInterpolationTime = durationInSeconds; } bool OrbitalNavigator::followingNodeRotation() const { return _followRotationInterpolator.value() >= 1.0; } -SceneGraphNode* OrbitalNavigator::focusNode() const { - return _focusNode; +const SceneGraphNode* OrbitalNavigator::anchorNode() const { + return _anchorNode; +} + +const SceneGraphNode* OrbitalNavigator::aimNode() const { + return _aimNode; } bool OrbitalNavigator::hasRotationalFriction() const { @@ -433,19 +650,22 @@ bool OrbitalNavigator::hasRollFriction() const { return _friction.roll; } -OrbitalNavigator::CameraRotationDecomposition OrbitalNavigator::decomposeCameraRotation( - const glm::dvec3& cameraPosition, - const glm::dquat& cameraRotation, - const glm::dvec3& cameraLookUp, - const glm::dvec3& cameraViewDirection) +OrbitalNavigator::CameraRotationDecomposition + OrbitalNavigator::decomposeCameraRotationSurface(CameraPose cameraPose, + const SceneGraphNode& reference) { - const glm::dmat4 inverseModelTransform = _focusNode->inverseModelTransform(); - const glm::dmat4 modelTransform = _focusNode->modelTransform(); + const glm::dvec3 cameraUp = cameraPose.rotation * Camera::UpDirectionCameraSpace; + + const glm::dvec3 cameraViewDirection = + cameraPose.rotation * glm::dvec3(0.0, 0.0, -1.0); + + const glm::dmat4 inverseModelTransform = reference.inverseModelTransform(); + const glm::dmat4 modelTransform = reference.modelTransform(); const glm::dvec3 cameraPositionModelSpace = glm::dvec3(inverseModelTransform * - glm::dvec4(cameraPosition, 1)); + glm::dvec4(cameraPose.position, 1)); const SurfacePositionHandle posHandle = - _focusNode->calculateSurfacePositionHandle(cameraPositionModelSpace); + reference.calculateSurfacePositionHandle(cameraPositionModelSpace); const glm::dvec3 directionFromSurfaceToCameraModelSpace = posHandle.referenceSurfaceOutDirection; @@ -457,17 +677,153 @@ OrbitalNavigator::CameraRotationDecomposition OrbitalNavigator::decomposeCameraR const glm::dmat4 lookAtMat = glm::lookAt( glm::dvec3(0.0, 0.0, 0.0), -directionFromSurfaceToCamera, - normalize(cameraViewDirection + cameraLookUp) + normalize(cameraViewDirection + cameraUp) ); const glm::dquat globalCameraRotation = glm::normalize( - glm::quat_cast(inverse(lookAtMat)) + glm::inverse(glm::quat_cast(lookAtMat)) ); const glm::dquat localCameraRotation = glm::inverse(globalCameraRotation) * - cameraRotation; + cameraPose.rotation; return { localCameraRotation, globalCameraRotation }; } +OrbitalNavigator::CameraRotationDecomposition + OrbitalNavigator::decomposeCameraRotation(CameraPose cameraPose, + glm::dvec3 reference) +{ + const glm::dvec3 cameraUp = cameraPose.rotation * glm::dvec3(0.0, 1.0, 0.0); + + const glm::dvec3 cameraViewDirection = cameraPose.rotation * + glm::dvec3(0.0, 0.0, -1.0); + + // To avoid problem with lookup in up direction we adjust is with the view direction + const glm::dmat4 lookAtMat = glm::lookAt( + glm::dvec3(0.0, 0.0, 0.0), + reference - cameraPose.position, + normalize(cameraViewDirection + cameraUp) + ); + const glm::dquat globalCameraRotation = glm::normalize( + glm::inverse(glm::quat_cast(lookAtMat)) + ); + const glm::dquat localCameraRotation = glm::inverse(globalCameraRotation) * + cameraPose.rotation; + + return { localCameraRotation, globalCameraRotation }; +} + +OrbitalNavigator::CameraPose OrbitalNavigator::followAim(CameraPose pose, + glm::dvec3 cameraToAnchor, + Displacement anchorToAim) +{ + CameraRotationDecomposition anchorDecomp = + decomposeCameraRotation(pose, pose.position + cameraToAnchor); + + const glm::dvec3 prevCameraToAim = cameraToAnchor + anchorToAim.first; + const double distanceRatio = + glm::length(anchorToAim.second) / glm::length(prevCameraToAim); + + // Make sure that the anchor and aim nodes are numerically distinguishable, + // otherwise, don't follow the aim. + if (distanceRatio > DistanceRatioAimThreshold) { + // Divide the action of following the aim into two actions: + // 1. Rotating camera around anchor, based on the aim's projection onto a sphere + // around the anchor, with radius = distance(camera, anchor). + // 2. Adjustment of the camera to account for radial displacement of the aim + + + // Step 1 (Rotation around anchor based on aim's projection) + glm::dvec3 newAnchorToProjectedAim = + glm::length(anchorToAim.first) * glm::normalize(anchorToAim.second); + const double spinRotationAngle = glm::angle( + glm::normalize(anchorToAim.first), glm::normalize(newAnchorToProjectedAim) + ); + + if (spinRotationAngle > AngleEpsilon) { + const glm::dvec3 spinRotationAxis = + glm::cross(anchorToAim.first, newAnchorToProjectedAim); + + const glm::dquat spinRotation = + glm::angleAxis(spinRotationAngle, glm::normalize(spinRotationAxis)); + + pose.position = + _anchorNode->worldPosition() - spinRotation * cameraToAnchor; + + anchorDecomp.globalRotation = spinRotation * anchorDecomp.globalRotation; + } + + // Step 2 (Adjustment for radial displacement) + const glm::dvec3 projectedAim = + _anchorNode->worldPosition() + newAnchorToProjectedAim; + + const glm::dvec3 intermediateCameraToAnchor = + _anchorNode->worldPosition() - pose.position; + + const glm::dvec3 intermediateCameraToProjectedAim = + projectedAim - pose.position; + + const double anchorAimAngle = glm::angle( + glm::normalize(intermediateCameraToAnchor), + glm::normalize(intermediateCameraToProjectedAim) + ); + double ratio = + glm::sin(anchorAimAngle) * glm::length(intermediateCameraToAnchor) / + glm::length(anchorToAim.second); + + // Equation has no solution if ratio > 1. + // To avoid a discontinuity in the camera behavior, + // fade out the distance correction influence when ratio approaches 1. + // CorrectionFactorExponent = 50.0 is picked arbitrarily, + // and gives a smooth result. + ratio = glm::clamp(ratio, -1.0, 1.0); + double CorrectionFactorExponent = 50.0; + double correctionFactor = + glm::clamp(1.0 - glm::pow(ratio, CorrectionFactorExponent), 0.0, 1.0); + + + // newCameraAnchorAngle has two solutions, depending on whether the camera is + // in the half-space closest to the anchor or aim. + double newCameraAnchorAngle = glm::asin(ratio); + if (glm::dot(intermediateCameraToAnchor, anchorToAim.second) <= 0 && + glm::dot(intermediateCameraToProjectedAim, anchorToAim.second) <= 0) + { + newCameraAnchorAngle = -glm::asin(ratio) + glm::pi(); + } + + const double prevCameraAimAngle = glm::angle( + glm::normalize(-intermediateCameraToAnchor), + glm::normalize(newAnchorToProjectedAim) + ); + + const double newCameraAimAngle = + glm::pi() - anchorAimAngle - newCameraAnchorAngle; + + double distanceRotationAngle = correctionFactor * (newCameraAimAngle - prevCameraAimAngle); + + if (glm::abs(distanceRotationAngle) > AngleEpsilon) { + glm::dvec3 distanceRotationAxis = glm::normalize( + glm::cross(intermediateCameraToAnchor, newAnchorToProjectedAim) + ); + const glm::dquat orbitRotation = + glm::angleAxis(distanceRotationAngle, distanceRotationAxis); + + pose.position = + _anchorNode->worldPosition() - + orbitRotation * intermediateCameraToAnchor; + + const glm::dquat aimAdjustRotation = + glm::angleAxis(distanceRotationAngle, distanceRotationAxis); + + anchorDecomp.globalRotation = aimAdjustRotation * anchorDecomp.globalRotation; + } + // End of step 2. + + pose.rotation = composeCameraRotation(anchorDecomp); + } + + return pose; +} + glm::dquat OrbitalNavigator::roll(double deltaTime, const glm::dquat& localCameraRotation) const { @@ -497,24 +853,37 @@ glm::dquat OrbitalNavigator::rotateLocally(double deltaTime, return localCameraRotation * joystickRotationDiff * mouseRotationDiff; } - glm::dquat OrbitalNavigator::interpolateLocalRotation(double deltaTime, - const glm::dquat& localCameraRotation) + const glm::dquat& localCameraRotation) { - if (_rotateToFocusNodeInterpolator.isInterpolating()) { - const double t = _rotateToFocusNodeInterpolator.value(); - _rotateToFocusNodeInterpolator.setDeltaTime(static_cast(deltaTime)); - _rotateToFocusNodeInterpolator.step(); + if (_retargetAnchorInterpolator.isInterpolating()) { + const double t = _retargetAnchorInterpolator.value(); + _retargetAnchorInterpolator.setDeltaTime(static_cast(deltaTime)); + _retargetAnchorInterpolator.step(); + + const glm::dvec3 localUp = + localCameraRotation * Camera::UpDirectionCameraSpace; + + const glm::dmat4 lookAtMat = glm::lookAt( + glm::dvec3(0.0, 0.0, 0.0), + Camera::ViewDirectionCameraSpace, + normalize(localUp) + ); + + const glm::dquat targetRotation = glm::normalize( + glm::inverse(glm::quat_cast(lookAtMat)) + ); + const glm::dquat result = glm::slerp( localCameraRotation, - glm::dquat(glm::dvec3(0.0)), - glm::min(t * _rotateToFocusNodeInterpolator.deltaTimeScaled(), 1.0)); + targetRotation, + glm::min(t * _retargetAnchorInterpolator.deltaTimeScaled(), 1.0)); // Retrieving the angle of a quaternion uses acos on the w component, which can // have numerical instability for values close to 1.0 constexpr double Epsilon = 1.0e-13; if (abs((abs(result.w) - 1.0)) < Epsilon || angle(result) < 0.01) { - _rotateToFocusNodeInterpolator.end(); + _retargetAnchorInterpolator.end(); } return result; } @@ -523,6 +892,71 @@ glm::dquat OrbitalNavigator::interpolateLocalRotation(double deltaTime, } } +OrbitalNavigator::Displacement OrbitalNavigator::interpolateRetargetAim( + double deltaTime, + CameraPose pose, + glm::dvec3 prevCameraToAnchor, + Displacement anchorToAim) +{ + + if (_retargetAimInterpolator.isInterpolating()) { + double t = _retargetAimInterpolator.value(); + _retargetAimInterpolator.setDeltaTime(static_cast(deltaTime)); + _retargetAimInterpolator.step(); + + const glm::dvec3 prevCameraToAim = prevCameraToAnchor + anchorToAim.first; + const double aimDistance = glm::length(prevCameraToAim); + const glm::dquat prevRotation = pose.rotation; + + // Introduce a virtual aim - a position straight ahead of the camera, + // that should be rotated around the camera, until it reaches the aim node. + + const glm::dvec3 prevCameraToVirtualAim = + aimDistance * (prevRotation * Camera::ViewDirectionCameraSpace); + + // Max angle: the maximum possible angle between anchor and aim, given that + // the camera orbits the anchor on a fixed distance. + const double maxAngle = + glm::atan(glm::length(anchorToAim.first), glm::length(prevCameraToAnchor)); + + // Requested angle: The angle between the vector straight ahead from the + // camera and the vector from camera to anchor should remain constant, in + // order for the anchor not to move in screen space. + const double requestedAngle = glm::angle( + glm::normalize(prevCameraToVirtualAim), + glm::normalize(prevCameraToAnchor) + ); + + if (requestedAngle <= maxAngle) { + glm::dvec3 aimPos = pose.position + prevCameraToAnchor + anchorToAim.second; + CameraRotationDecomposition aimDecomp = decomposeCameraRotation(pose, aimPos); + + const glm::dquat interpolatedRotation = glm::slerp( + prevRotation, + aimDecomp.globalRotation, + glm::min(t * _retargetAimInterpolator.deltaTimeScaled(), 1.0) + ); + + const glm::dvec3 recomputedCameraToVirtualAim = + aimDistance * (interpolatedRotation * Camera::ViewDirectionCameraSpace); + + return { + prevCameraToVirtualAim - prevCameraToAnchor, + recomputedCameraToVirtualAim - prevCameraToAnchor + }; + } + else { + // Bail out. + // Cannot put aim node in center without moving anchor in screen space. + // Future work: Rotate as much as possible, + // or possibly use some other DOF to find solution, like moving the camera. + _retargetAimInterpolator.end(); + } + } + return anchorToAim; +} + + double OrbitalNavigator::interpolateCameraToSurfaceDistance(double deltaTime, double currentDistance, double targetDistance @@ -553,19 +987,16 @@ double OrbitalNavigator::interpolateCameraToSurfaceDistance(double deltaTime, glm::dvec3 OrbitalNavigator::translateHorizontally(double deltaTime, const glm::dvec3& cameraPosition, const glm::dvec3& objectPosition, - const glm::dquat& /*focusNodeRotationDiff*/, const glm::dquat& globalCameraRotation, const SurfacePositionHandle& positionHandle) const { - const glm::dmat4 modelTransform = _focusNode->modelTransform(); + const glm::dmat4 modelTransform = _anchorNode->modelTransform(); const glm::dvec3 outDirection = glm::normalize(glm::dmat3(modelTransform) * positionHandle.referenceSurfaceOutDirection); // Vector logic const glm::dvec3 posDiff = cameraPosition - objectPosition; - // glm::dvec3 centerToReferenceSurface = glm::dmat3(modelTransform) * - // positionHandle.centerToReferenceSurface; const glm::dvec3 centerToActualSurfaceModelSpace = positionHandle.centerToReferenceSurface + positionHandle.referenceSurfaceOutDirection * positionHandle.heightToSurface; @@ -576,8 +1007,8 @@ glm::dvec3 OrbitalNavigator::translateHorizontally(double deltaTime, const double distFromSurfaceToCamera = glm::length(actualSurfaceToCamera); // Final values to be used - const double distFromCenterToSurface = length(centerToActualSurface); - const double distFromCenterToCamera = length(posDiff); + const double distFromCenterToSurface = glm::length(centerToActualSurface); + const double distFromCenterToCamera = glm::length(posDiff); const double speedScale = distFromCenterToSurface > 0.0 ? @@ -603,43 +1034,44 @@ glm::dvec3 OrbitalNavigator::translateHorizontally(double deltaTime, glm::inverse(globalCameraRotation); // Rotate and find the difference vector - const glm::dvec3 rotationDiffVec3 = (distFromCenterToCamera * outDirection) * - rotationDiffWorldSpace - - (distFromCenterToCamera * outDirection); + const glm::dvec3 rotationDiffVec3 = + (distFromCenterToCamera * outDirection) * rotationDiffWorldSpace - + (distFromCenterToCamera * outDirection); // Add difference to position return cameraPosition + rotationDiffVec3; } -glm::dvec3 OrbitalNavigator::followFocusNodeRotation(const glm::dvec3& cameraPosition, +glm::dvec3 OrbitalNavigator::followAnchorNodeRotation(const glm::dvec3& cameraPosition, const glm::dvec3& objectPosition, const glm::dquat& focusNodeRotationDiff) const { const glm::dvec3 posDiff = cameraPosition - objectPosition; - const glm::dvec3 rotationDiffVec3AroundCenter = posDiff * focusNodeRotationDiff - - posDiff; + + const glm::dvec3 rotationDiffVec3AroundCenter = + posDiff * focusNodeRotationDiff - posDiff; + return cameraPosition + rotationDiffVec3AroundCenter; } glm::dquat OrbitalNavigator::rotateGlobally(const glm::dquat& globalCameraRotation, - const glm::dvec3& /*objectPosition*/, const glm::dquat& focusNodeRotationDiff, - const glm::dvec3& /*cameraPosition*/, const SurfacePositionHandle& positionHandle) const { - const glm::dmat4 modelTransform = _focusNode->modelTransform(); + const glm::dmat4 modelTransform = _anchorNode->modelTransform(); const glm::dvec3 directionFromSurfaceToCamera = glm::dmat3(modelTransform) * positionHandle.referenceSurfaceOutDirection; const glm::dvec3 lookUpWhenFacingSurface = glm::inverse(focusNodeRotationDiff) * - globalCameraRotation * glm::dvec3(0.0, 1.0, 0.0); + globalCameraRotation * Camera::UpDirectionCameraSpace; + const glm::dmat4 lookAtMat = glm::lookAt( - glm::dvec3(0.0, 0.0, 0.0), + glm::dvec3(0.0), -directionFromSurfaceToCamera, lookUpWhenFacingSurface ); - return glm::normalize(glm::quat_cast(glm::inverse(lookAtMat))); + return glm::normalize(glm::inverse(glm::quat_cast(lookAtMat))); } glm::dvec3 OrbitalNavigator::translateVertically(double deltaTime, @@ -647,12 +1079,10 @@ glm::dvec3 OrbitalNavigator::translateVertically(double deltaTime, const glm::dvec3& objectPosition, const SurfacePositionHandle& positionHandle) const { - const glm::dmat4 modelTransform = _focusNode->modelTransform(); + const glm::dmat4 modelTransform = _anchorNode->modelTransform(); const glm::dvec3 posDiff = cameraPosition - objectPosition; - // glm::dvec3 centerToReferenceSurface = - // glm::dmat3(modelTransform) * positionHandle.centerToReferenceSurface; const glm::dvec3 centerToActualSurfaceModelSpace = positionHandle.centerToReferenceSurface + positionHandle.referenceSurfaceOutDirection * positionHandle.heightToSurface; @@ -669,10 +1099,9 @@ glm::dvec3 OrbitalNavigator::translateVertically(double deltaTime, glm::dquat OrbitalNavigator::rotateHorizontally(double deltaTime, const glm::dquat& globalCameraRotation, - const glm::dvec3& /*cameraPosition*/, const SurfacePositionHandle& positionHandle) const { - const glm::dmat4 modelTransform = _focusNode->modelTransform(); + const glm::dmat4 modelTransform = _anchorNode->modelTransform(); const glm::dvec3 directionFromSurfaceToCameraModelSpace = positionHandle.referenceSurfaceOutDirection; @@ -693,13 +1122,12 @@ glm::dvec3 OrbitalNavigator::pushToSurface(double minHeightAboveGround, const glm::dvec3& objectPosition, const SurfacePositionHandle& positionHandle) const { - const glm::dmat4 modelTransform = _focusNode->modelTransform(); + const glm::dmat4 modelTransform = _anchorNode->modelTransform(); const glm::dvec3 posDiff = cameraPosition - objectPosition; const glm::dvec3 referenceSurfaceOutDirection = glm::dmat3(modelTransform) * positionHandle.referenceSurfaceOutDirection; - // glm::dvec3 centerToReferenceSurface = - // glm::dmat3(modelTransform) * positionHandle.centerToReferenceSurface; + const glm::dvec3 centerToActualSurfaceModelSpace = positionHandle.centerToReferenceSurface + positionHandle.referenceSurfaceOutDirection * positionHandle.heightToSurface; @@ -721,12 +1149,12 @@ glm::dquat OrbitalNavigator::interpolateRotationDifferential(double deltaTime, const glm::dvec3& cameraPosition, const SurfacePositionHandle& positionHandle) { - const glm::dmat4 modelTransform = _focusNode->modelTransform(); + const glm::dmat4 modelTransform = _anchorNode->modelTransform(); const double maximumDistanceForRotation = glm::length( glm::dmat3(modelTransform) * positionHandle.centerToReferenceSurface - ) * _followFocusNodeRotationDistance; - const double distanceToCamera = glm::length(cameraPosition - objectPosition); + ) * _followAnchorNodeRotationDistance; + const double distanceToCamera = glm::distance(cameraPosition, objectPosition); // Interpolate with a negative delta time if distance is too large to follow const double interpolationSign = glm::sign( @@ -749,13 +1177,14 @@ glm::dquat OrbitalNavigator::interpolateRotationDifferential(double deltaTime, } SurfacePositionHandle OrbitalNavigator::calculateSurfacePositionHandle( + const SceneGraphNode& node, const glm::dvec3 cameraPositionWorldSpace) { - const glm::dmat4 inverseModelTransform = _focusNode->inverseModelTransform(); + const glm::dmat4 inverseModelTransform = node.inverseModelTransform(); const glm::dvec3 cameraPositionModelSpace = glm::dvec3(inverseModelTransform * glm::dvec4(cameraPositionWorldSpace, 1)); const SurfacePositionHandle posHandle = - _focusNode->calculateSurfacePositionHandle(cameraPositionModelSpace); + node.calculateSurfacePositionHandle(cameraPositionModelSpace); return posHandle; } diff --git a/src/interaction/sessionrecording.cpp b/src/interaction/sessionrecording.cpp index 82b7ce1d03..29a942772d 100644 --- a/src/interaction/sessionrecording.cpp +++ b/src/interaction/sessionrecording.cpp @@ -291,9 +291,11 @@ void SessionRecording::cleanUpPlayback() { Scene* scene = camera->parent()->scene(); if (!_timeline.empty()) { unsigned int p = _timeline[_idxTimeline_cameraPtrPrev].idxIntoKeyframeTypeArray; - global::navigationHandler.setFocusNode( - scene->sceneGraphNode(_keyframesCamera[p].focusNode) - ); + const SceneGraphNode* node = scene->sceneGraphNode(_keyframesCamera[p].focusNode); + if (node) { + global::navigationHandler.orbitalNavigator().setFocusNode(node->identifier()); + } + } global::scriptScheduler.stopPlayback(); @@ -428,8 +430,9 @@ void SessionRecording::saveCameraKeyframe() { return; } - SceneGraphNode* focusNode = global::navigationHandler.focusNode(); - if (!focusNode) { + const SceneGraphNode* anchorNode = + global::navigationHandler.orbitalNavigator().anchorNode(); + if (!anchorNode) { return; } @@ -1150,9 +1153,13 @@ bool SessionRecording::processCameraKeyframe(double now) { // the rendered objects will be unstable and actually incorrect Camera* camera = global::navigationHandler.camera(); Scene* scene = camera->parent()->scene(); - global::navigationHandler.setFocusNode(scene->sceneGraphNode( - _keyframesCamera[prevIdx].focusNode - )); + + const SceneGraphNode* node = + scene->sceneGraphNode(_keyframesCamera[prevIdx].focusNode); + + if (node) { + global::navigationHandler.orbitalNavigator().setFocusNode(node->identifier()); + } return interaction::KeyframeNavigator::updateCamera( global::navigationHandler.camera(), diff --git a/src/network/parallelpeer.cpp b/src/network/parallelpeer.cpp index ad76aa09d8..d288aae0da 100644 --- a/src/network/parallelpeer.cpp +++ b/src/network/parallelpeer.cpp @@ -585,20 +585,22 @@ const std::string& ParallelPeer::hostName() { } void ParallelPeer::sendCameraKeyframe() { - SceneGraphNode* focusNode = global::navigationHandler.focusNode(); + const SceneGraphNode* focusNode = + global::navigationHandler.orbitalNavigator().anchorNode(); if (!focusNode) { return; } // Create a keyframe with current position and orientation of camera datamessagestructures::CameraKeyframe kf; - kf._position = global::navigationHandler.focusNodeToCameraVector(); + kf._position = global::navigationHandler.orbitalNavigator().anchorNodeToCameraVector(); kf._followNodeRotation = global::navigationHandler.orbitalNavigator().followingNodeRotation(); if (kf._followNodeRotation) { kf._position = glm::inverse(focusNode->worldRotationMatrix()) * kf._position; - kf._rotation = global::navigationHandler.focusNodeToCameraRotation(); + kf._rotation = + global::navigationHandler.orbitalNavigator().anchorNodeToCameraRotation(); } else { kf._rotation = global::navigationHandler.camera()->rotationQuaternion(); diff --git a/src/properties/propertyowner.cpp b/src/properties/propertyowner.cpp index bd6d681fc9..2b89953a85 100644 --- a/src/properties/propertyowner.cpp +++ b/src/properties/propertyowner.cpp @@ -364,22 +364,22 @@ std::string PropertyOwner::generateJson() const { std::function createJson = [&createJson](properties::PropertyOwner* owner) -> std::string { - constexpr const char* replStr = R"("{}": "{}",)"; + constexpr const char* replStr = R"("{}": "{}")"; std::stringstream json; json << "{"; - json << fmt::format(replStr, "name", owner->identifier()); + json << fmt::format(replStr, "name", owner->identifier()) << ","; json << "\"properties\": ["; const std::vector& properties = owner->properties(); for (properties::Property* p : properties) { json << "{"; - json << fmt::format(replStr, "id", p->identifier()); - json << fmt::format(replStr, "type", p->className()); + json << fmt::format(replStr, "id", p->identifier()) << ","; + json << fmt::format(replStr, "type", p->className()) << ","; json << fmt::format( replStr, "fullyQualifiedId", p->fullyQualifiedIdentifier() - ); - json << fmt::format(replStr, "guiName", p->guiName()); + ) << ","; + json << fmt::format(replStr, "guiName", p->guiName()) << ","; json << fmt::format(replStr, "description", escapedJson(p->description())); json << "}"; if (p != properties.back()) { diff --git a/src/query/query.cpp b/src/query/query.cpp index 6bddf41612..bc94fb8722 100644 --- a/src/query/query.cpp +++ b/src/query/query.cpp @@ -37,6 +37,9 @@ Scene* sceneGraph() { SceneGraphNode* sceneGraphNode(const std::string& name) { const Scene* graph = sceneGraph(); + if (!graph) { + return nullptr; + } return graph->sceneGraphNode(name); } diff --git a/src/rendering/renderable.cpp b/src/rendering/renderable.cpp index 6329e42cef..b06b884b17 100644 --- a/src/rendering/renderable.cpp +++ b/src/rendering/renderable.cpp @@ -165,7 +165,7 @@ void Renderable::setPscUniforms(ghoul::opengl::ProgramObject& program, const Camera& camera, const PowerScaledCoordinate& position) { - program.setUniform("campos", camera.position().vec4()); + program.setUniform("campos", glm::vec4(camera.positionVec3(), 1.f)); program.setUniform("objpos", position.vec4()); program.setUniform("camrot", glm::mat4(camera.viewRotationMatrix())); program.setUniform("scaling", glm::vec2(1.f, 0.f)); diff --git a/src/scene/scenegraphnode.cpp b/src/scene/scenegraphnode.cpp index bd76b529a2..89c96ce9e8 100644 --- a/src/scene/scenegraphnode.cpp +++ b/src/scene/scenegraphnode.cpp @@ -588,7 +588,7 @@ void SceneGraphNode::setDependencies(const std::vector& depende } SurfacePositionHandle SceneGraphNode::calculateSurfacePositionHandle( - const glm::dvec3& targetModelSpace) + const glm::dvec3& targetModelSpace) const { if (_renderable) { return _renderable->calculateSurfacePositionHandle(targetModelSpace); @@ -809,19 +809,4 @@ const SceneGraphNode::PerformanceRecord& SceneGraphNode::performanceRecord() con return _performanceRecord; } -void SceneGraphNode::updateCamera(Camera* camera) const { - psc origin(worldPosition()); - //int i = 0; - // the camera position - - psc relative = camera->position(); - psc focus = camera->focusPosition(); - psc relative_focus = relative - focus; - - psc target = origin + relative_focus; - - camera->setPosition(target); - camera->setFocusPosition(origin); -} - } // namespace openspace diff --git a/src/util/camera.cpp b/src/util/camera.cpp index 558455a882..192071c9de 100644 --- a/src/util/camera.cpp +++ b/src/util/camera.cpp @@ -28,15 +28,14 @@ namespace openspace { -const glm::dvec3 Camera::ViewDirectionCameraSpace = glm::dvec3(0, 0, -1); -const glm::dvec3 Camera::LookupVectorCameraSpace = glm::dvec3(0, 1, 0); +const glm::dvec3 Camera::ViewDirectionCameraSpace = glm::dvec3(0.0, 0.0, -1.0); +const glm::dvec3 Camera::UpDirectionCameraSpace = glm::dvec3(0.0, 1.0, 0.0); Camera::Camera(const Camera& o) : sgctInternal(o.sgctInternal) , _position(o._position) , _rotation(o._rotation) , _scaling(o._scaling) - , _focusPosition(o._focusPosition) , _maxFov(o._maxFov) , _cachedViewDirection(o._cachedViewDirection) , _cachedLookupVector(o._cachedLookupVector) @@ -49,11 +48,6 @@ void Camera::setPositionVec3(glm::dvec3 pos) { _cachedCombinedViewMatrix.isDirty = true; } -void Camera::setFocusPositionVec3(glm::dvec3 pos) { - std::lock_guard _lock(_mutex); - _focusPosition = std::move(pos); -} - void Camera::setRotation(glm::dquat rotation) { std::lock_guard _lock(_mutex); _rotation = std::move(rotation); @@ -116,30 +110,26 @@ const glm::dvec3& Camera::unsynchedPositionVec3() const { return _position; } -const glm::dvec3& Camera::focusPositionVec3() const { - return _focusPosition; -} - const glm::dvec3& Camera::viewDirectionWorldSpace() const { if (_cachedViewDirection.isDirty) { _cachedViewDirection.datum = glm::normalize( static_cast(_rotation) * ViewDirectionCameraSpace ); - _cachedViewDirection.isDirty = true; + _cachedViewDirection.isDirty = false; } return _cachedViewDirection.datum; } const glm::dvec3& Camera::lookUpVectorCameraSpace() const { - return LookupVectorCameraSpace; + return UpDirectionCameraSpace; } const glm::dvec3& Camera::lookUpVectorWorldSpace() const { if (_cachedLookupVector.isDirty) { _cachedLookupVector.datum = glm::normalize( - static_cast(_rotation) * LookupVectorCameraSpace + static_cast(_rotation) * UpDirectionCameraSpace ); - _cachedLookupVector.isDirty = true; + _cachedLookupVector.isDirty = false; } return _cachedLookupVector.datum; @@ -170,6 +160,7 @@ const glm::dmat4& Camera::viewRotationMatrix() const { _cachedViewRotationMatrix.datum = glm::mat4_cast( glm::inverse(static_cast(_rotation)) ); + _cachedViewRotationMatrix.isDirty = false; } return _cachedViewRotationMatrix.datum; } @@ -177,6 +168,7 @@ const glm::dmat4& Camera::viewRotationMatrix() const { const glm::dmat4& Camera::viewScaleMatrix() const { if (_cachedViewScaleMatrix.isDirty) { _cachedViewScaleMatrix.datum = glm::scale(glm::mat4(1.f), glm::vec3(_scaling)); + _cachedViewScaleMatrix.isDirty = false; } return _cachedViewScaleMatrix.datum; } @@ -195,7 +187,7 @@ const glm::dmat4& Camera::combinedViewMatrix() const { glm::dmat4(viewScaleMatrix()) * glm::dmat4(viewRotationMatrix()) * cameraTranslation; - _cachedCombinedViewMatrix.isDirty = true; + _cachedCombinedViewMatrix.isDirty = false; } return _cachedCombinedViewMatrix.datum; } @@ -205,6 +197,8 @@ void Camera::invalidateCache() { _cachedLookupVector.isDirty = true; _cachedViewRotationMatrix.isDirty = true; _cachedCombinedViewMatrix.isDirty = true; + _cachedViewScaleMatrix.isDirty = true; + _cachedSinMaxFov.isDirty = true; } void Camera::serialize(std::ostream& os) const { @@ -270,29 +264,6 @@ const glm::mat4& Camera::SgctInternal::viewProjectionMatrix() const { return _cachedViewProjectionMatrix.datum; } -// Deprecated -void Camera::setPosition(psc pos) { - std::lock_guard _lock(_mutex); - _position = pos.dvec3(); -} - -void Camera::setFocusPosition(psc pos) { - std::lock_guard _lock(_mutex); - _focusPosition = pos.dvec3(); -} - -psc Camera::position() const { - return psc(static_cast(_position)); -} - -psc Camera::unsynchedPosition() const { - return psc(static_cast(_position)); -} - -psc Camera::focusPosition() const { - return psc(_focusPosition); -} - const glm::mat4& Camera::viewMatrix() const { return sgctInternal.viewMatrix(); }