diff --git a/include/openspace/navigation/path.h b/include/openspace/navigation/path.h index a376684cd4..fc9fd79a0e 100644 --- a/include/openspace/navigation/path.h +++ b/include/openspace/navigation/path.h @@ -75,6 +75,12 @@ public: */ CameraPose traversePath(double dt, float speedScale = 1.f); + /** + * Function that can be used to permaturely quit a path ,for example when skipping + * to the end + */ + void quitPath(); + /** * Return the identifer of the node that is the current appropriate anchor node, of * the start and end waypoint's reference node. Dtermined based on how far along the diff --git a/include/openspace/navigation/pathnavigator.h b/include/openspace/navigation/pathnavigator.h index 681e7acc03..8f3d8d1895 100644 --- a/include/openspace/navigation/pathnavigator.h +++ b/include/openspace/navigation/pathnavigator.h @@ -72,6 +72,7 @@ public: void abortPath(); void pausePath(); void continuePath(); + void skipToEnd(); Path::Type defaultPathType() const; double minValidBoundingSphere() const; @@ -79,6 +80,13 @@ public: const std::vector& relevantNodes(); + /** + * Find a node close to the given node. Closeness is determined by a factor times + * the bounding sphere of the object + * \return pointer to the SGN if one was found, nullptr otherwise + */ + static SceneGraphNode* findNodeNearTarget(const SceneGraphNode* node); + /** * \return The Lua library that contains all Lua functions available to affect the * path navigation @@ -99,6 +107,8 @@ private: bool _isPlaying = false; bool _startSimulationTimeOnFinish = false; + bool _setCameraToEndNextFrame = false; + properties::OptionProperty _defaultPathType; properties::BoolProperty _includeRoll; properties::FloatProperty _speedScale; diff --git a/src/navigation/path.cpp b/src/navigation/path.cpp index 10b9004471..48032b2d44 100644 --- a/src/navigation/path.cpp +++ b/src/navigation/path.cpp @@ -36,7 +36,6 @@ #include #include #include -#include #include #include #include @@ -178,6 +177,11 @@ CameraPose Path::traversePath(double dt, float speedScale) { return newPose; } +void Path::quitPath() { + _traveledDistance = pathLength(); + _shouldQuit = true; +} + std::string Path::currentAnchor() const { bool pastHalfway = (_traveledDistance / pathLength()) > 0.5; return (pastHalfway) ? _end.nodeIdentifier() : _start.nodeIdentifier(); @@ -440,50 +444,14 @@ Waypoint waypointFromCamera() { return Waypoint{ pos, rot, node }; } -SceneGraphNode* findNodeNearTarget(const SceneGraphNode* node) { - const std::vector& relevantNodes = - global::navigationHandler->pathNavigator().relevantNodes(); - - for (SceneGraphNode* n : relevantNodes) { - bool isSame = (n->identifier() == node->identifier()); - // If the nodes are in the very same position, they are probably representing - // the same object - isSame |= - glm::distance(n->worldPosition(), node->worldPosition()) < LengthEpsilon; - - if (isSame) { - continue; - } - - constexpr float proximityRadiusFactor = 3.f; - - const float bs = static_cast(n->boundingSphere()); - const float proximityRadius = proximityRadiusFactor * bs; - const glm::dvec3 posInModelCoords = - glm::inverse(n->modelTransform()) * glm::dvec4(node->worldPosition(), 1.0); - - bool isClose = collision::isPointInsideSphere( - posInModelCoords, - glm::dvec3(0.0, 0.0, 0.0), - proximityRadius - ); - - if (isClose) { - return n; - } - } - - return nullptr; -} - // Compute a target position close to the specified target node, using knowledge of // the start point and a desired distance from the node's center glm::dvec3 computeGoodStepDirection(const SceneGraphNode* targetNode, const Waypoint& startPoint) { const glm::dvec3 nodePos = targetNode->worldPosition(); - const SceneGraphNode* closeNode = findNodeNearTarget(targetNode); const SceneGraphNode* sun = sceneGraphNode(SunIdentifier); + const SceneGraphNode* closeNode = PathNavigator::findNodeNearTarget(targetNode); // @TODO (2021-07-09, emmbr): Not nice to depend on a specific scene graph node, // as it might not exist. Ideally, each SGN could know about their preferred diff --git a/src/navigation/pathnavigator.cpp b/src/navigation/pathnavigator.cpp index 42e6ca9f33..0caefdbfd4 100644 --- a/src/navigation/pathnavigator.cpp +++ b/src/navigation/pathnavigator.cpp @@ -38,6 +38,7 @@ #include #include #include +#include #include #include #include @@ -214,6 +215,19 @@ void PathNavigator::updateCamera(double deltaTime) { return; } + if (_setCameraToEndNextFrame) { + LDEBUG("Skipped to end of camera path"); + _currentPath->quitPath(); + camera()->setPose(_currentPath->endPoint().pose()); + global::navigationHandler->orbitalNavigator().setFocusNode( + _currentPath->endPoint().nodeIdentifier(), + false + ); + handlePathEnd(); + _setCameraToEndNextFrame = false; + return; + } + // Prevent long delta times due to e.g. computations from other actions to cause // really big jumps in the motion along the path // OBS! Causes problems if the general FPS is lower than 10, but then the user should @@ -241,19 +255,6 @@ void PathNavigator::updateCamera(double deltaTime) { if (_currentPath->hasReachedEnd()) { LINFO("Reached target"); handlePathEnd(); - - if (_applyIdleBehaviorOnFinish) { - constexpr const char ApplyIdleBehaviorScript[] = - "openspace.setPropertyValueSingle(" - "'NavigationHandler.OrbitalNavigator.IdleBehavior.ApplyIdleBehavior'," - "true" - ");"; - - global::scriptEngine->queueScript( - ApplyIdleBehaviorScript, - openspace::scripting::ScriptEngine::RemoteScripting::Yes - ); - } return; } } @@ -368,6 +369,14 @@ void PathNavigator::continuePath() { _isPlaying = true; } +void PathNavigator::skipToEnd() { + if (!openspace::global::navigationHandler->pathNavigator().isPlayingPath()) { + LWARNING("No camera path is currently active"); + } + + _setCameraToEndNextFrame = true; +} + Path::Type PathNavigator::defaultPathType() const { return static_cast(_defaultPathType.value()); } @@ -430,15 +439,25 @@ const std::vector& PathNavigator::relevantNodes() { void PathNavigator::handlePathEnd() { _isPlaying = false; + global::openSpaceEngine->resetMode(); if (_startSimulationTimeOnFinish) { openspace::global::scriptEngine->queueScript( "openspace.time.setPause(false)", scripting::ScriptEngine::RemoteScripting::Yes ); + _startSimulationTimeOnFinish = false; + } + + if (_applyIdleBehaviorOnFinish) { + global::scriptEngine->queueScript( + "openspace.setPropertyValueSingle(" + "'NavigationHandler.OrbitalNavigator.IdleBehavior.ApplyIdleBehavior'," + "true" + ");", + openspace::scripting::ScriptEngine::RemoteScripting::Yes + ); } - _startSimulationTimeOnFinish = false; - global::openSpaceEngine->resetMode(); } void PathNavigator::findRelevantNodes() { @@ -480,6 +499,43 @@ void PathNavigator::findRelevantNodes() { _relevantNodes = resultingNodes; } +SceneGraphNode* PathNavigator::findNodeNearTarget(const SceneGraphNode* node) { + constexpr float LengthEpsilon = 1e-5f; + const std::vector& relNodes = + global::navigationHandler->pathNavigator().relevantNodes(); + + for (SceneGraphNode* n : relNodes) { + bool isSame = (n->identifier() == node->identifier()); + // If the nodes are in the very same position, they are probably representing + // the same object + isSame |= + glm::distance(n->worldPosition(), node->worldPosition()) < LengthEpsilon; + + if (isSame) { + continue; + } + + constexpr float proximityRadiusFactor = 3.f; + + const float bs = static_cast(n->boundingSphere()); + const float proximityRadius = proximityRadiusFactor * bs; + const glm::dvec3 posInModelCoords = + glm::inverse(n->modelTransform()) * glm::dvec4(node->worldPosition(), 1.0); + + bool isClose = collision::isPointInsideSphere( + posInModelCoords, + glm::dvec3(0.0, 0.0, 0.0), + proximityRadius + ); + + if (isClose) { + return n; + } + } + + return nullptr; +} + void PathNavigator::removeRollRotation(CameraPose& pose, double deltaTime) { const glm::dvec3 anchorPos = anchor()->worldPosition(); const glm::dvec3 cameraDir = glm::normalize( @@ -504,6 +560,7 @@ scripting::LuaLibrary PathNavigator::luaLibrary() { codegen::lua::ContinuePath, codegen::lua::PausePath, codegen::lua::StopPath, + codegen::lua::SkipToEnd, codegen::lua::FlyTo, codegen::lua::FlyToHeight, codegen::lua::FlyToNavigationState, diff --git a/src/navigation/pathnavigator_lua.inl b/src/navigation/pathnavigator_lua.inl index 0a60a57bbb..a2ade6c473 100644 --- a/src/navigation/pathnavigator_lua.inl +++ b/src/navigation/pathnavigator_lua.inl @@ -46,6 +46,11 @@ namespace { openspace::global::navigationHandler->pathNavigator().abortPath(); } +// Immediately skips to the end of the current camera path, if one is being played. +[[codegen::luawrap]] void skipToEnd() { + openspace::global::navigationHandler->pathNavigator().skipToEnd(); +} + /** * Move the camera to the node with the specified identifier. The optional double * specifies the duration of the motion, in seconds. If the optional bool is set to true