diff --git a/modules/autonavigation/autonavigationhandler.cpp b/modules/autonavigation/autonavigationhandler.cpp index 3b78b21eb4..75505c1b98 100644 --- a/modules/autonavigation/autonavigationhandler.cpp +++ b/modules/autonavigation/autonavigationhandler.cpp @@ -62,29 +62,50 @@ const double AutoNavigationHandler::pathDuration() const { return sum; } -PathSegment& AutoNavigationHandler::currentPathSegment() { - for (PathSegment& ps : _pathSegments) { +const bool AutoNavigationHandler::hasFinished() const { + return _currentTime > pathDuration(); +} + +const int AutoNavigationHandler::currentPathSegmentIndex() const { + for (int i = 0; i < _pathSegments.size(); ++i) { + const PathSegment& ps = _pathSegments[i]; double endTime = ps.startTime + ps.duration; if (endTime > _currentTime) { - return ps; + return i; } } } +CameraState AutoNavigationHandler::currentCameraState() { + CameraState cs; + cs.position = camera()->positionVec3(); + cs.rotation = camera()->rotationQuaternion(); + cs.referenceNode = global::navigationHandler.anchorNode()->identifier(); + return cs; +} + void AutoNavigationHandler::updateCamera(double deltaTime) { ghoul_assert(camera() != nullptr, "Camera must not be nullptr"); if (!_isPlaying || _pathSegments.empty()) return; - PathSegment cps = currentPathSegment(); + const int currentIndex = currentPathSegmentIndex(); + + if (_stopAtTargets && (currentIndex != _activeSegmentIndex)) { + _activeSegmentIndex = currentIndex; + pausePath(); + return; + } // INTERPOLATE (TODO: make a function, and allow different methods) + const PathSegment& cps = _pathSegments[currentIndex]; + double t = (_currentTime - cps.startTime) / cps.duration; t = transferfunctions::cubicEaseInOut(t); // TEST t = std::max(0.0, std::min(t, 1.0)); - // TODO: don't set every frame and + // TODO: don't set every frame // Set anchor node in orbitalNavigator, to render visible nodes and // add possibility to navigate when we reach the end. CameraState cs = (t < 0.5) ? cps.start : cps.end; @@ -100,10 +121,11 @@ void AutoNavigationHandler::updateCamera(double deltaTime) { _currentTime += deltaTime; - // reached the end of the path => stop playing - if (_currentTime > pathDuration()) { + _activeSegmentIndex = currentIndex; + + if (hasFinished()) { + LINFO("Reached end of path."); _isPlaying = false; - // TODO: implement suitable stop behaviour } } @@ -118,42 +140,59 @@ void AutoNavigationHandler::createPath(PathSpecification& spec) { break; } + // TODO: set stop at target variable based on spec + if (success) { - LINFO("Succefully generated camera path. Starting."); + LINFO("Succefully generated camera path."); startPath(); } else - LINFO("Could not create path."); + LERROR("Could not create path."); } void AutoNavigationHandler::clearPath() { + LINFO("Clearing path..."); _pathSegments.clear(); _currentTime = 0.0; + _activeSegmentIndex = 0; } void AutoNavigationHandler::startPath() { ghoul_assert(!_pathSegments.empty(), "Cannot start an empty path"); + LINFO("Starting path..."); _currentTime = 0.0; _isPlaying = true; } -CameraState AutoNavigationHandler::getStartState() { - CameraState cs; - if (_pathSegments.empty()) { - cs.position = camera()->positionVec3(); - cs.rotation = camera()->rotationQuaternion(); - cs.referenceNode = global::navigationHandler.anchorNode()->identifier(); - } - else { - cs = _pathSegments.back().end; +void AutoNavigationHandler::pausePath() { + ghoul_assert(!_isPlaying, "Cannot pause a path that isn't playing"); + LINFO(fmt::format("Paused path at target {} / {}", _activeSegmentIndex, _pathSegments.size())); + _isPlaying = false; +} + +void AutoNavigationHandler::continuePath() { + ghoul_assert(_isPlaying, "Cannot start a path that is already playing"); + ghoul_assert(!_pathSegments.empty(), "No path to continue on"); + + if (hasFinished()) { + LERROR("Path has ended, cannot continue."); + return; } - return cs; + LINFO("Continuing path..."); + + // Recompute start camera state for the upcoming path segment, to avoid clipping to + // the old camera state. + _pathSegments[_activeSegmentIndex].start = currentCameraState(); // TODO: adapt to new PathSegment code + + _isPlaying = true; } bool AutoNavigationHandler::handleInstruction(const Instruction& instruction, int index) { - CameraState startState = getStartState(); + CameraState& currentLast = _pathSegments.back().end; + CameraState startState = _pathSegments.empty() ? currentCameraState() : currentLast; + CameraState endState; double duration, startTime; bool success = true; diff --git a/modules/autonavigation/autonavigationhandler.h b/modules/autonavigation/autonavigationhandler.h index 45addaf075..d36f832cac 100644 --- a/modules/autonavigation/autonavigationhandler.h +++ b/modules/autonavigation/autonavigationhandler.h @@ -60,16 +60,18 @@ public: // Accessors Camera* camera() const; const double pathDuration() const; - PathSegment& currentPathSegment(); + const bool hasFinished() const; + const int currentPathSegmentIndex() const; + CameraState currentCameraState(); void updateCamera(double deltaTime); void createPath(PathSpecification& spec); void clearPath(); void startPath(); + void pausePath(); + void continuePath(); private: - CameraState getStartState(); - bool handleInstruction(const Instruction& instruction, int index); bool endFromTargetNodeInstruction(CameraState& endState, CameraState& prevState, const Instruction& instruction, int index); @@ -83,6 +85,9 @@ private: double _currentTime; bool _isPlaying = false; + + int _activeSegmentIndex = 0; + bool _stopAtTargets = true; // TODO: ask if this should be a setting for the module or the path }; } // namespace openspace::autonavigation diff --git a/modules/autonavigation/autonavigationmodule.cpp b/modules/autonavigation/autonavigationmodule.cpp index c1861544b5..4bc4f73142 100644 --- a/modules/autonavigation/autonavigationmodule.cpp +++ b/modules/autonavigation/autonavigationmodule.cpp @@ -52,6 +52,13 @@ scripting::LuaLibrary AutoNavigationModule::luaLibrary() const { scripting::LuaLibrary res; res.name = "autonavigation"; res.functions = { + { + "continuePath", + &autonavigation::luascriptfunctions::continuePath, + {}, + "", + "Continue playing a paused camera path." + }, { "goTo", &autonavigation::luascriptfunctions::goTo, diff --git a/modules/autonavigation/autonavigationmodule_lua.inl b/modules/autonavigation/autonavigationmodule_lua.inl index fb9449cd4d..c400c1bcad 100644 --- a/modules/autonavigation/autonavigationmodule_lua.inl +++ b/modules/autonavigation/autonavigationmodule_lua.inl @@ -40,6 +40,16 @@ namespace openspace::autonavigation::luascriptfunctions { const double EPSILON = 1e-12; + int continuePath(lua_State* L) { + int nArguments = ghoul::lua::checkArgumentsAndThrow(L, 0, "lua::continuePath"); + + AutoNavigationModule* module = global::moduleEngine.module(); + AutoNavigationHandler& handler = module->AutoNavigationHandler(); + handler.continuePath(); + + return 0; + } + int goTo(lua_State* L) { int nArguments = ghoul::lua::checkArgumentsAndThrow(L, { 1, 2 }, "lua::goTo");