From aeb0a2cf7fd37c4bf8633707bcc5e0aaa3576475 Mon Sep 17 00:00:00 2001 From: Emma Broman Date: Wed, 7 Jul 2021 12:55:31 +0200 Subject: [PATCH] Move StopBehavior to OrbitalNavigator and rename to IdleBehavior --- .../openspace/navigation/orbitalnavigator.h | 25 +++- include/openspace/navigation/pathnavigator.h | 16 +-- src/navigation/orbitalnavigator.cpp | 108 +++++++++++++++++- src/navigation/pathnavigator.cpp | 102 ----------------- 4 files changed, 134 insertions(+), 117 deletions(-) diff --git a/include/openspace/navigation/orbitalnavigator.h b/include/openspace/navigation/orbitalnavigator.h index d7ef74d42b..ff934ff4b9 100644 --- a/include/openspace/navigation/orbitalnavigator.h +++ b/include/openspace/navigation/orbitalnavigator.h @@ -33,6 +33,7 @@ #include #include #include +#include #include #include #include @@ -56,6 +57,10 @@ class InputState; class OrbitalNavigator : public properties::PropertyOwner { public: + enum IdleBehavior { + Orbit = 0 + }; + OrbitalNavigator(); void updateStatesFromInput(const InputState& inputState, double deltaTime); @@ -144,6 +149,9 @@ private: properties::DoubleProperty _flightDestinationFactor; properties::BoolProperty _applyLinearFlight; + properties::BoolProperty _applyIdleBehavior; + properties::OptionProperty _idleBehavior; + properties::FloatProperty _velocitySensitivity; properties::FloatProperty _mouseSensitivity; properties::FloatProperty _joystickSensitivity; @@ -193,7 +201,7 @@ private: /** * 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. + * camera points towards the reference position. * The local rotation defines the differential from the global to the current total * rotation so that cameraRotation = globalRotation * localRotation. */ @@ -328,6 +336,21 @@ private: */ SurfacePositionHandle calculateSurfacePositionHandle(const SceneGraphNode& node, const glm::dvec3 cameraPositionWorldSpace); + + /** + * IdleBehavior + * Apply the currently selected idle behavior to the position and rotations + */ + void applyIdleBehavior(double deltaTime, glm::dvec3& position, + glm::dquat& localRotation, glm::dquat& globalRotation); + + /** + * IdleBehavior + * Orbit the current anchor node, in a right-bound orbit, by updating the position + * and global rotation of the camera + */ + void orbitAnchor(double deltaTime, glm::dvec3& position, + glm::dquat& globalRotation, double speedScale); }; } // namespace openspace::interaction diff --git a/include/openspace/navigation/pathnavigator.h b/include/openspace/navigation/pathnavigator.h index 87a191ef27..c06821618f 100644 --- a/include/openspace/navigation/pathnavigator.h +++ b/include/openspace/navigation/pathnavigator.h @@ -50,11 +50,6 @@ class Path; class PathNavigator : public properties::PropertyOwner { public: - enum StopBehavior { - None = 0, - Orbit - }; - PathNavigator(); ~PathNavigator(); @@ -86,12 +81,12 @@ public: static scripting::LuaLibrary luaLibrary(); private: + /** + * Populate list of nodes that are relevant for collision checks, etc + */ void findRelevantNodes(); void removeRollRotation(CameraPose& pose, double deltaTime); - void applyStopBehavior(double deltaTime); - - void orbitAnchorNode(double deltaTime); std::unique_ptr _currentPath = nullptr; bool _isPlaying = false; @@ -99,11 +94,6 @@ private: properties::OptionProperty _defaultCurveOption; properties::BoolProperty _includeRoll; properties::FloatProperty _speedScale; - properties::FloatProperty _orbitSpeedFactor; - - properties::BoolProperty _applyStopBehaviorWhenIdle; - properties::OptionProperty _stopBehavior; - properties::DoubleProperty _minValidBoundingSphere; properties::StringListProperty _relevantNodeTags; diff --git a/src/navigation/orbitalnavigator.cpp b/src/navigation/orbitalnavigator.cpp index 9cd547d410..ea4dadca03 100644 --- a/src/navigation/orbitalnavigator.cpp +++ b/src/navigation/orbitalnavigator.cpp @@ -152,6 +152,20 @@ namespace { "'FlightDestinationDistance' while facing the anchor" }; + constexpr openspace::properties::Property::PropertyInfo ApplyIdleBehaviorInfo = { + "ApplyIdleBehavior", + "Apply Idle Behavior", + "When set to true, the chosen idle behavior will be applied to the camera, " + "moving the camera accordingly. " + }; + + constexpr openspace::properties::Property::PropertyInfo IdleBehaviorInfo = { + "IdleBehvaior", + "Idle Behavior", + "The chosen camera behavior that will be applied when 'ApplyIdleBehavior' is " + "set to true. Each option represents a predefined camera behavior." + }; + constexpr openspace::properties::Property::PropertyInfo FlightDestinationDistInfo = { "FlightDestinationDistance", "Flight Destination Distance", @@ -250,6 +264,8 @@ OrbitalNavigator::OrbitalNavigator() , _flightDestinationDistance(FlightDestinationDistInfo, 2e8f, 10.f, 1e10f) , _flightDestinationFactor(FlightDestinationFactorInfo, 1E-4, 1E-6, 0.5, 1E-3) , _applyLinearFlight(ApplyLinearFlightInfo, false) + , _applyIdleBehavior(ApplyIdleBehaviorInfo, false) + , _idleBehavior(IdleBehaviorInfo) , _velocitySensitivity(VelocityZoomControlInfo, 3.5f, 0.001f, 20.f) , _mouseSensitivity(MouseSensitivityInfo, 15.f, 1.f, 50.f) , _joystickSensitivity(JoystickSensitivityInfo, 10.f, 1.0f, 50.f) @@ -388,6 +404,14 @@ OrbitalNavigator::OrbitalNavigator() addProperty(_flightDestinationFactor); addProperty(_applyLinearFlight); + addProperty(_applyIdleBehavior); + + _idleBehavior.addOptions({ + { IdleBehavior::Orbit, "Orbit" } + }); + _idleBehavior = IdleBehavior::Orbit; + addProperty(_idleBehavior); + addProperty(_useAdaptiveStereoscopicDepth); addProperty(_staticViewScaleExponent); _stereoscopicDepthOfFocusSurface.setExponent(3.f); @@ -569,6 +593,18 @@ void OrbitalNavigator::updateCameraStateFromStates(double deltaTime) { posHandle ); + // Apply any automatic idle behavior. Note that the idle behavior is aborted if there + // is no input from interaction. So, it assumes that all the previous effects from + // user input resulted in no change + if (_applyIdleBehavior) { + applyIdleBehavior( + deltaTime, + pose.position, + camRot.localRotation, + camRot.globalRotation + ); + } + // Horizontal translation by focus node rotation pose.position = followAnchorNodeRotation( pose.position, @@ -1234,7 +1270,7 @@ glm::dvec3 OrbitalNavigator::translateHorizontally(double deltaTime, const glm::dquat websocketRotationDiffCamSpace = glm::dquat(glm::dvec3( -_websocketStates.globalRotationVelocity().y * deltaTime, -_websocketStates.globalRotationVelocity().x * deltaTime, - 0) * speedScale + 0.0) * speedScale ); // Transform to world space @@ -1434,4 +1470,74 @@ const ScriptCameraStates& OrbitalNavigator::scriptStates() const { return _scriptStates; } +void OrbitalNavigator::applyIdleBehavior(double deltaTime, glm::dvec3& position, + glm::dquat& localRotation, + glm::dquat& globalRotation) +{ + const glm::dvec3 posDiff = position - _anchorNode->worldPosition(); + + SurfacePositionHandle posHandle = + calculateSurfacePositionHandle(*_anchorNode, position); + + const glm::dvec3 centerToActualSurfaceModelSpace = + posHandle.centerToReferenceSurface + + posHandle.referenceSurfaceOutDirection * posHandle.heightToSurface; + + const glm::dvec3 centerToActualSurface = glm::dmat3(_anchorNode->modelTransform()) * + centerToActualSurfaceModelSpace; + const glm::dvec3 actualSurfaceToCamera = posDiff - centerToActualSurface; + + const double distFromSurfaceToCamera = glm::length(actualSurfaceToCamera); + const double distFromCenterToSurface = glm::length(centerToActualSurface); + + double speedScale = + distFromCenterToSurface > 0.0 ? + glm::clamp(distFromSurfaceToCamera / distFromCenterToSurface, 0.0, 1.0) : + 1.0; + + speedScale *= 0.1; // without this scaleing, the motion is way too fast + + // Apply the chosen behavior + const IdleBehavior chosen = static_cast(_idleBehavior.value()); + switch (chosen) { + case IdleBehavior::Orbit: + orbitAnchor(deltaTime, position, globalRotation, speedScale); + break; + default: + throw ghoul::MissingCaseException(); + } +} + +void OrbitalNavigator::orbitAnchor(double deltaTime, glm::dvec3& position, + glm::dquat& globalRotation, double speedScale) +{ + ghoul_assert(_anchorNode != nullptr, "Node to orbit must be set!"); + + // Get position on the surface of the node corresponding to current camera position + SurfacePositionHandle posHandle = calculateSurfacePositionHandle( + *_anchorNode, position); + + const glm::dmat4 modelTransform = _anchorNode->modelTransform(); + const glm::dvec3 outDirection = glm::normalize(glm::dmat3(modelTransform) * + posHandle.referenceSurfaceOutDirection); + + // Apply a rotation to the right + // (Note that we could also let the user decide which direction to rotate) + const glm::dvec3 eulerAngles = glm::dvec3(0.0, -1.0, 0.0) * deltaTime * speedScale; + const glm::dquat rotationDiffCameraSpace = glm::dquat(eulerAngles); + + const glm::dquat rotationDiffWorldSpace = globalRotation * + rotationDiffCameraSpace * + glm::inverse(globalRotation); + + // Rotate to find the difference in position + const glm::dvec3 anchorPos = _anchorNode->worldPosition(); + const double distFromCenterToCamera = glm::length(position - anchorPos); + const glm::dvec3 rotationDiffVec3 = + (distFromCenterToCamera * outDirection) * rotationDiffWorldSpace - + (distFromCenterToCamera * outDirection); + + position += rotationDiffVec3; +} + } // namespace openspace::interaction diff --git a/src/navigation/pathnavigator.cpp b/src/navigation/pathnavigator.cpp index ffabc2559e..d4eb531b0e 100644 --- a/src/navigation/pathnavigator.cpp +++ b/src/navigation/pathnavigator.cpp @@ -54,21 +54,6 @@ namespace { "If disabled, roll is removed from the interpolation of camera orientation" }; - constexpr openspace::properties::Property::PropertyInfo StopBehaviorInfo = { - "StopBehavior", - "Stop Behavior", - "A camera motion behavior that is applied when no path is being played" - }; - - constexpr openspace::properties::Property::PropertyInfo - ApplyStopBehaviorWhenIdleInfo = - { - "ApplyStopBehaviorWhenIdle", - "Apply Stop Behavior When Idle", - "If enabled, the camera is controlled using the set stop behavior when " - "no path is playing" - }; - constexpr openspace::properties::Property::PropertyInfo SpeedScaleInfo = { "SpeedScale", "Speed Scale", @@ -77,12 +62,6 @@ namespace { "value is larger than or smaller than one." }; - constexpr openspace::properties::Property::PropertyInfo OrbitSpeedFactorInfo = { - "OrbitSpeedFactor", - "Orbit Speed Factor", - "Controls the speed of the orbiting around an anchor." - }; - constexpr const openspace::properties::Property::PropertyInfo MinBoundingSphereInfo = { "MinimalValidBoundingSphere", "Minimal Valid Bounding Sphere", @@ -110,13 +89,7 @@ PathNavigator::PathNavigator() properties::OptionProperty::DisplayType::Dropdown ) , _includeRoll(IncludeRollInfo, false) - , _stopBehavior( - StopBehaviorInfo, - properties::OptionProperty::DisplayType::Dropdown - ) - , _applyStopBehaviorWhenIdle(ApplyStopBehaviorWhenIdleInfo, false) , _speedScale(SpeedScaleInfo, 1.f, 0.01f, 2.f) - , _orbitSpeedFactor(OrbitSpeedFactorInfo, 0.5, 0.0, 20.0) , _minValidBoundingSphere(MinBoundingSphereInfo, 10.0, 1.0, 3e10) , _relevantNodeTags(RelevantNodeTagsInfo) { @@ -129,20 +102,6 @@ PathNavigator::PathNavigator() addProperty(_includeRoll); addProperty(_speedScale); - - // OBS! Stop behavior is broken as of core merge - //addProperty(_applyStopBehaviorWhenIdle); - - //// Must be listed in the same order as in enum definition - //_stopBehavior.addOptions({ - // { StopBehavior::None, "None" }, - // { StopBehavior::Orbit, "Orbit" } - //}); - //_stopBehavior = StopBehavior::None; - //addProperty(_stopBehavior); - - //addProperty(_orbitSpeedFactor); - addProperty(_minValidBoundingSphere); _relevantNodeTags = std::vector{ @@ -194,11 +153,6 @@ void PathNavigator::updateCamera(double deltaTime) { } if (!_isPlaying) { - //// TODO: Determine how this should work - //// OBS! Stop behavior is broken as of core merge - //if (hasFinished() && _applyStopBehaviorWhenIdle) { - // applyStopBehavior(deltaTime); - //} return; } @@ -395,62 +349,6 @@ void PathNavigator::removeRollRotation(CameraPose& pose, double deltaTime) { pose.rotation = rollFreeRotation; } -void PathNavigator::applyStopBehavior(double deltaTime) { - switch (_stopBehavior) { - case StopBehavior::None: - // Do nothing - break; - case StopBehavior::Orbit: - orbitAnchorNode(deltaTime); - break; - default: - throw ghoul::MissingCaseException(); - } -} - -void PathNavigator::orbitAnchorNode(double deltaTime) { - ghoul_assert(anchor() != nullptr, "Node to orbit must be set!"); - - const glm::dvec3 prevPosition = camera()->positionVec3(); - const glm::dquat prevRotation = camera()->rotationQuaternion(); - const glm::dvec3 nodeCenter = anchor()->worldPosition(); - - const double speedFactor = 0.1 * _orbitSpeedFactor; - - // Compute orbit speed based on factor and distance to surface - const double orbitRadius = glm::distance(prevPosition, nodeCenter); - const double distanceToSurface = orbitRadius - anchor()->boundingSphere(); - const double orbitSpeed = distanceToSurface * speedFactor; - - // Compute a new position along the orbit - const glm::dvec3 up = camera()->lookUpVectorWorldSpace(); - const glm::dquat lookAtNodeRotation = ghoul::lookAtQuaternion( - prevPosition, - nodeCenter, - up - ); - const glm::dvec3 targetForward = lookAtNodeRotation * glm::dvec3(0.0, 0.0, -1.0); - const glm::dvec3 rightOrbitTangent = glm::normalize(glm::cross(targetForward, up)); - - glm::dvec3 newPosition = prevPosition + orbitSpeed * deltaTime * rightOrbitTangent; - - // Adjust for numerical error - make sure we stay at the same height - const glm::dvec3 nodeToNewPos = newPosition - nodeCenter; - const double targetHeight = glm::distance(prevPosition, nodeCenter); - const double heightDiff = glm::length(nodeToNewPos) - targetHeight; - newPosition -= heightDiff * glm::normalize(nodeToNewPos); - - // Rotate along the orbit, but keep relative orientation with regards to the anchor - const glm::dquat localRotation = glm::inverse(lookAtNodeRotation) * prevRotation; - const glm::dquat newLookAtRotation = - ghoul::lookAtQuaternion(newPosition, nodeCenter, up); - - const glm::dquat newRotation = newLookAtRotation * localRotation; - - camera()->setPositionVec3(newPosition); - camera()->setRotation(newRotation); -} - scripting::LuaLibrary PathNavigator::luaLibrary() { return { "pathnavigation",