Move StopBehavior to OrbitalNavigator and rename to IdleBehavior

This commit is contained in:
Emma Broman
2021-07-07 12:55:31 +02:00
parent de8005be4d
commit aeb0a2cf7f
4 changed files with 134 additions and 117 deletions

View File

@@ -33,6 +33,7 @@
#include <openspace/interaction/mousecamerastates.h>
#include <openspace/interaction/scriptcamerastates.h>
#include <openspace/interaction/websocketcamerastates.h>
#include <openspace/properties/optionproperty.h>
#include <openspace/properties/stringproperty.h>
#include <openspace/properties/scalar/boolproperty.h>
#include <openspace/properties/scalar/floatproperty.h>
@@ -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 <code>cameraRotation = globalRotation * localRotation</code>.
*/
@@ -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

View File

@@ -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<Path> _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;

View File

@@ -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>(_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

View File

@@ -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<std::string>{
@@ -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",