From 3e283d16309149f27dcd1a989517bef104b4772d Mon Sep 17 00:00:00 2001 From: Emma Broman Date: Fri, 10 Jan 2020 14:57:48 -0500 Subject: [PATCH 001/912] Add code for camera path generation module Add all code in the state it was in Jan 10 2020 when branch cleanup was performed --- modules/autonavigation/CMakeLists.txt | 49 +++ .../autonavigation/autonavigationhandler.cpp | 332 ++++++++++++++++++ .../autonavigation/autonavigationhandler.h | 91 +++++ .../autonavigation/autonavigationmodule.cpp | 88 +++++ modules/autonavigation/autonavigationmodule.h | 55 +++ .../autonavigationmodule_lua.inl | 150 ++++++++ modules/autonavigation/include.cmake | 5 + modules/autonavigation/pathspecification.cpp | 209 +++++++++++ modules/autonavigation/pathspecification.h | 88 +++++ modules/autonavigation/transferfunctions.cpp | 76 ++++ modules/autonavigation/transferfunctions.h | 49 +++ 11 files changed, 1192 insertions(+) create mode 100644 modules/autonavigation/CMakeLists.txt create mode 100644 modules/autonavigation/autonavigationhandler.cpp create mode 100644 modules/autonavigation/autonavigationhandler.h create mode 100644 modules/autonavigation/autonavigationmodule.cpp create mode 100644 modules/autonavigation/autonavigationmodule.h create mode 100644 modules/autonavigation/autonavigationmodule_lua.inl create mode 100644 modules/autonavigation/include.cmake create mode 100644 modules/autonavigation/pathspecification.cpp create mode 100644 modules/autonavigation/pathspecification.h create mode 100644 modules/autonavigation/transferfunctions.cpp create mode 100644 modules/autonavigation/transferfunctions.h diff --git a/modules/autonavigation/CMakeLists.txt b/modules/autonavigation/CMakeLists.txt new file mode 100644 index 0000000000..6d346304cc --- /dev/null +++ b/modules/autonavigation/CMakeLists.txt @@ -0,0 +1,49 @@ +########################################################################################## +# # +# OpenSpace # +# # +# Copyright (c) 2014-2019 # +# # +# Permission is hereby granted, free of charge, to any person obtaining a copy of this # +# software and associated documentation files (the "Software"), to deal in the Software # +# without restriction, including without limitation the rights to use, copy, modify, # +# merge, publish, distribute, sublicense, and/or sell copies of the Software, and to # +# permit persons to whom the Software is furnished to do so, subject to the following # +# conditions: # +# # +# The above copyright notice and this permission notice shall be included in all copies # +# or substantial portions of the Software. # +# # +# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, # +# INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A # +# PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT # +# HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF # +# CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE # +# OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. # +########################################################################################## + +include(${OPENSPACE_CMAKE_EXT_DIR}/module_definition.cmake) + +set(HEADER_FILES + ${CMAKE_CURRENT_SOURCE_DIR}/autonavigationmodule.h + ${CMAKE_CURRENT_SOURCE_DIR}/autonavigationhandler.h + ${CMAKE_CURRENT_SOURCE_DIR}/transferfunctions.h + ${CMAKE_CURRENT_SOURCE_DIR}/pathspecification.h +) +source_group("Header Files" FILES ${HEADER_FILES}) + +set(SOURCE_FILES + ${CMAKE_CURRENT_SOURCE_DIR}/autonavigationmodule.cpp + ${CMAKE_CURRENT_SOURCE_DIR}/autonavigationmodule_lua.inl + ${CMAKE_CURRENT_SOURCE_DIR}/autonavigationhandler.cpp + ${CMAKE_CURRENT_SOURCE_DIR}/transferfunctions.cpp + ${CMAKE_CURRENT_SOURCE_DIR}/pathspecification.cpp +) +source_group("Source Files" FILES ${SOURCE_FILES}) + +create_new_module( + "AutoNavigation" + autonavigation_module + STATIC + ${HEADER_FILES} ${SOURCE_FILES} +) diff --git a/modules/autonavigation/autonavigationhandler.cpp b/modules/autonavigation/autonavigationhandler.cpp new file mode 100644 index 0000000000..5b776a9914 --- /dev/null +++ b/modules/autonavigation/autonavigationhandler.cpp @@ -0,0 +1,332 @@ +/***************************************************************************************** + * * + * OpenSpace * + * * + * Copyright (c) 2014-2019 * + * * + * Permission is hereby granted, free of charge, to any person obtaining a copy of this * + * software and associated documentation files (the "Software"), to deal in the Software * + * without restriction, including without limitation the rights to use, copy, modify, * + * merge, publish, distribute, sublicense, and/or sell copies of the Software, and to * + * permit persons to whom the Software is furnished to do so, subject to the following * + * conditions: * + * * + * The above copyright notice and this permission notice shall be included in all copies * + * or substantial portions of the Software. * + * * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, * + * INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A * + * PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT * + * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF * + * CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE * + * OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. * + ****************************************************************************************/ + +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +namespace { + constexpr const char* _loggerCat = "AutoNavigationHandler"; +} // namespace + +namespace openspace::autonavigation { + +AutoNavigationHandler::AutoNavigationHandler() + : properties::PropertyOwner({ "AutoNavigationHandler" }) +{ + // Add the properties + // TODO +} + +AutoNavigationHandler::~AutoNavigationHandler() {} // NOLINT + +Camera* AutoNavigationHandler::camera() const { + return global::navigationHandler.camera(); +} + +const double AutoNavigationHandler::pathDuration() const { + return _pathDuration; +} + +PathSegment& AutoNavigationHandler::currentPathSegment() { + for (PathSegment& ps : _pathSegments) { + double endTime = ps.startTime + ps.duration; + if (endTime > _currentTime) { + return ps; + } + } +} + +void AutoNavigationHandler::updateCamera(double deltaTime) { + ghoul_assert(camera() != nullptr, "Camera must not be nullptr"); + + if (!_isPlaying || _pathSegments.empty()) return; + + PathSegment cps = currentPathSegment(); + + // INTERPOLATE (TODO: make a function, and allow different methods) + + 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 + // 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; + global::navigationHandler.orbitalNavigator().setAnchorNode(cs.referenceNode); + + // TODO: add different ways to interpolate later + glm::dvec3 cameraPosition = cps.start.position * (1.0 - t) + cps.end.position * t; + glm::dquat cameraRotation = + glm::slerp(cps.start.rotation, cps.end.rotation, t); + + camera()->setPositionVec3(cameraPosition); + camera()->setRotation(cameraRotation); + + _currentTime += deltaTime; + + // reached the end of the path => stop playing + if (_currentTime > _pathDuration) { + _isPlaying = false; + // TODO: implement suitable stop behaviour + } +} + +void AutoNavigationHandler::createPath(PathSpecification& spec) { + clearPath(); + bool success = true; + for (int i = 0; i < spec.instructions()->size(); i++) { + const Instruction& ins = spec.instructions()->at(i); + success = handleInstruction(ins, i); + + if (!success) + break; + } + + if (success) { + LINFO("Succefully generated camera path. Starting."); + startPath(); + } + else + LINFO("Could not create path."); +} + +void AutoNavigationHandler::clearPath() { + _pathSegments.clear(); + _pathDuration = 0.0; + _currentTime = 0.0; +} + +void AutoNavigationHandler::startPath() { + ghoul_assert(!_pathSegments.empty(), "Cannot start an empty path"); + + _pathDuration = 0.0; + for (auto ps : _pathSegments) { + _pathDuration += ps.duration; + } + _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; + } + + return cs; +} + +bool AutoNavigationHandler::handleInstruction(const Instruction& instruction, int index) +{ + CameraState startState = getStartState(); + CameraState endState; + double duration, startTime; + bool success = true; + + switch (instruction.type) + { + case InstructionType::TargetNode: + success = endFromTargetNodeInstruction( + endState, startState, instruction, index + ); + break; + + case InstructionType::NavigationState: + success = endFromNavigationStateInstruction( + endState, instruction, index + ); + break; + + default: + LERROR(fmt::format("Non-implemented instruction type: {}.", instruction.type)); + success = false; + break; + } + + if (!success) return false; + + // compute duration + if (instruction.props->duration.has_value()) { + duration = instruction.props->duration.value(); + if (duration <= 0) { + LERROR(fmt::format("Failed creating path segment number {}. Duration can not be negative.", index + 1)); + return false; + } + } + else { + // TODO: compute default duration + duration = 5.0; + } + + // compute startTime + startTime = 0.0; + if (!_pathSegments.empty()) { + PathSegment& last = _pathSegments.back(); + startTime = last.startTime + last.duration; + } + + // create new path + _pathSegments.push_back( + PathSegment{ startState, endState, duration, startTime } + ); + + return true; +} + +bool AutoNavigationHandler::endFromTargetNodeInstruction( + CameraState& endState, CameraState& prevState, const Instruction& instruction, int index) +{ + TargetNodeInstructionProps* props = + dynamic_cast(instruction.props.get()); + + if (!props) { + LERROR(fmt::format("Could not handle target node instruction (number {}).", index + 1)); + return false; + } + + // Compute end state + std::string& identifier = props->targetNode; + const SceneGraphNode* targetNode = sceneGraphNode(identifier); + if (!targetNode) { + LERROR(fmt::format("Failed handling instruction number {}. Could not find node '{}' to target", index + 1, identifier)); + return false; + } + + glm::dvec3 targetPos; + if (props->position.has_value()) { + // note that the anchor and reference frame is our targetnode. + // The position in instruction is given is relative coordinates. + targetPos = targetNode->worldPosition() + targetNode->worldRotationMatrix() * props->position.value(); + } + else { + bool hasHeight = props->height.has_value(); + + // TODO: compute defualt height in a better way + double defaultHeight = 2 * targetNode->boundingSphere(); + double height = hasHeight? props->height.value() : defaultHeight; + + targetPos = computeTargetPositionAtNode( + targetNode, + prevState.position, + height + ); + } + + glm::dmat4 lookAtMat = glm::lookAt( + targetPos, + targetNode->worldPosition(), + camera()->lookUpVectorWorldSpace() + ); + + glm::dquat targetRot = glm::normalize(glm::inverse(glm::quat_cast(lookAtMat))); + + endState = CameraState{ targetPos, targetRot, identifier }; + + return true; +} + +bool AutoNavigationHandler::endFromNavigationStateInstruction( + CameraState& endState, const Instruction& instruction, int index) +{ + NavigationStateInstructionProps* props = + dynamic_cast(instruction.props.get()); + + if (!props) { + LERROR(fmt::format("Could not handle navigation state instruction (number {}).", index + 1)); + return false; + } + + interaction::NavigationHandler::NavigationState ns = props->navState; + + // OBS! The following code is exactly the same as used in NavigationHandler::applyNavigationState. Should probably be made into a function. + const SceneGraphNode* referenceFrame = sceneGraphNode(ns.referenceFrame); + const SceneGraphNode* anchorNode = sceneGraphNode(ns.anchor); // The anchor is also the target + + if (!anchorNode) { + LERROR(fmt::format("Failed handling instruction number {}. Could not find node '{}' to target", index + 1, ns.anchor)); + return false; + } + + const glm::dvec3 anchorWorldPosition = anchorNode->worldPosition(); + const glm::dmat3 referenceFrameTransform = referenceFrame->worldRotationMatrix(); + + const glm::dvec3 targetPositionWorld = anchorWorldPosition + + glm::dvec3(referenceFrameTransform * glm::dvec4(ns.position, 1.0)); + + glm::dvec3 up = ns.up.has_value() ? + glm::normalize(referenceFrameTransform * ns.up.value()) : + glm::dvec3(0.0, 1.0, 0.0); + + // Construct vectors of a "neutral" view, i.e. when the aim is centered in view. + glm::dvec3 neutralView = + glm::normalize(anchorWorldPosition - targetPositionWorld); + + glm::dquat neutralCameraRotation = glm::inverse(glm::quat_cast(glm::lookAt( + glm::dvec3(0.0), + neutralView, + up + ))); + + glm::dquat pitchRotation = glm::angleAxis(ns.pitch, glm::dvec3(1.f, 0.f, 0.f)); + glm::dquat yawRotation = glm::angleAxis(ns.yaw, glm::dvec3(0.f, -1.f, 0.f)); + + glm::quat targetRotation = neutralCameraRotation * yawRotation * pitchRotation; + + endState = CameraState{ targetPositionWorld, targetRotation, ns.anchor }; + return true; +} + +glm::dvec3 AutoNavigationHandler::computeTargetPositionAtNode( + const SceneGraphNode* node, glm::dvec3 prevPos, double height) +{ + // TODO: compute actual distance above surface and validate negative values + + glm::dvec3 targetPos = node->worldPosition(); + glm::dvec3 targetToPrevVector = prevPos - targetPos; + + double radius = static_cast(node->boundingSphere()); + + // move target position out from surface, along vector to camera + targetPos += glm::normalize(targetToPrevVector) * (radius + height); + + return targetPos; +} + +} // namespace openspace::autonavigation diff --git a/modules/autonavigation/autonavigationhandler.h b/modules/autonavigation/autonavigationhandler.h new file mode 100644 index 0000000000..3d4260e063 --- /dev/null +++ b/modules/autonavigation/autonavigationhandler.h @@ -0,0 +1,91 @@ +/***************************************************************************************** + * * + * OpenSpace * + * * + * Copyright (c) 2014-2019 * + * * + * Permission is hereby granted, free of charge, to any person obtaining a copy of this * + * software and associated documentation files (the "Software"), to deal in the Software * + * without restriction, including without limitation the rights to use, copy, modify, * + * merge, publish, distribute, sublicense, and/or sell copies of the Software, and to * + * permit persons to whom the Software is furnished to do so, subject to the following * + * conditions: * + * * + * The above copyright notice and this permission notice shall be included in all copies * + * or substantial portions of the Software. * + * * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, * + * INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A * + * PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT * + * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF * + * CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE * + * OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. * + ****************************************************************************************/ + +#ifndef __OPENSPACE_MODULE___AUTONAVIGATIONHANDLER___H__ +#define __OPENSPACE_MODULE___AUTONAVIGATIONHANDLER___H__ + +#include +#include +#include +#include +#include + +namespace openspace { + class Camera; +} // namespace openspace + +namespace openspace::autonavigation { + +struct CameraState { + glm::dvec3 position; + glm::dquat rotation; + std::string referenceNode; +}; + +struct PathSegment { + CameraState start; + CameraState end; + double duration; + double startTime; +}; + +class AutoNavigationHandler : public properties::PropertyOwner { +public: + AutoNavigationHandler(); + ~AutoNavigationHandler(); + + // Mutators + + // Accessors + Camera* camera() const; + const double pathDuration() const; + PathSegment& currentPathSegment(); + + void updateCamera(double deltaTime); + void createPath(PathSpecification& spec); + void clearPath(); + void startPath(); + +private: + CameraState getStartState(); + + bool handleInstruction(const Instruction& instruction, int index); + + bool endFromTargetNodeInstruction(CameraState& endState, CameraState& prevState, const Instruction& instruction, int index); + + bool endFromNavigationStateInstruction(CameraState& endState, const Instruction& instruction, int index); + + glm::dvec3 computeTargetPositionAtNode(const SceneGraphNode* node, + glm::dvec3 prevPos, double height); + + std::vector _pathSegments; + + double _pathDuration; + double _currentTime; + bool _isPlaying = false; +}; + +} // namespace openspace::autonavigation + +#endif // __OPENSPACE_CORE___NAVIGATIONHANDLER___H__ diff --git a/modules/autonavigation/autonavigationmodule.cpp b/modules/autonavigation/autonavigationmodule.cpp new file mode 100644 index 0000000000..c1861544b5 --- /dev/null +++ b/modules/autonavigation/autonavigationmodule.cpp @@ -0,0 +1,88 @@ +/***************************************************************************************** + * * + * OpenSpace * + * * + * Copyright (c) 2014-2019 * + * * + * Permission is hereby granted, free of charge, to any person obtaining a copy of this * + * software and associated documentation files (the "Software"), to deal in the Software * + * without restriction, including without limitation the rights to use, copy, modify, * + * merge, publish, distribute, sublicense, and/or sell copies of the Software, and to * + * permit persons to whom the Software is furnished to do so, subject to the following * + * conditions: * + * * + * The above copyright notice and this permission notice shall be included in all copies * + * or substantial portions of the Software. * + * * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, * + * INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A * + * PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT * + * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF * + * CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE * + * OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. * + ****************************************************************************************/ + +#include + +#include +#include +#include +#include +#include + +namespace { + constexpr const char* _loggerCat = "AutoNavigationModule"; +} // namespace + +namespace openspace { + +AutoNavigationModule::AutoNavigationModule() : OpenSpaceModule(Name) {} + +autonavigation::AutoNavigationHandler& AutoNavigationModule::AutoNavigationHandler() { + return _autoNavigationHandler; +} + +std::vector AutoNavigationModule::documentations() const { + return { + // TODO: call documentation methods for the other classes in this module + }; +} + +scripting::LuaLibrary AutoNavigationModule::luaLibrary() const { + scripting::LuaLibrary res; + res.name = "autonavigation"; + res.functions = { + { + "goTo", + &autonavigation::luascriptfunctions::goTo, + {}, + "string, [double]", + "TODO: Description. Go to the node with the given name with optional duration." + }, + { + "generatePath", + &autonavigation::luascriptfunctions::generatePath, + {}, + "table", + "Generate the path as described by the lua table input argument. TODO: Describe how a path instruction is defined?. " //TODO also make this one start path from file + }, + { + "generatePathFromFile", + &autonavigation::luascriptfunctions::generatePathFromFile, + {}, + "string", + "Read an input file with lua instructions and use those to generate a camera path. TODO: Describe how a path instruction is defined?. " //TODO also make this one start path from file + } + }; + return res; +} + +void AutoNavigationModule::internalInitialize(const ghoul::Dictionary&) { + global::callback::preSync.emplace_back([this]() { + _autoNavigationHandler.updateCamera(global::windowDelegate.deltaTime()); + }); + + // TODO: register other classes (that are Factory created) and used in this module, if any +} + +} // namespace openspace diff --git a/modules/autonavigation/autonavigationmodule.h b/modules/autonavigation/autonavigationmodule.h new file mode 100644 index 0000000000..c6914e7cba --- /dev/null +++ b/modules/autonavigation/autonavigationmodule.h @@ -0,0 +1,55 @@ +/***************************************************************************************** + * * + * OpenSpace * + * * + * Copyright (c) 2014-2019 * + * * + * Permission is hereby granted, free of charge, to any person obtaining a copy of this * + * software and associated documentation files (the "Software"), to deal in the Software * + * without restriction, including without limitation the rights to use, copy, modify, * + * merge, publish, distribute, sublicense, and/or sell copies of the Software, and to * + * permit persons to whom the Software is furnished to do so, subject to the following * + * conditions: * + * * + * The above copyright notice and this permission notice shall be included in all copies * + * or substantial portions of the Software. * + * * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, * + * INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A * + * PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT * + * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF * + * CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE * + * OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. * + ****************************************************************************************/ + +#ifndef __OPENSPACE_MODULE_AUTONAVIGATION___AUTONAVIGATION_MODULE___H__ +#define __OPENSPACE_MODULE_AUTONAVIGATION___AUTONAVIGATION_MODULE___H__ + +#include + +#include +#include + +namespace openspace { + +class AutoNavigationModule : public OpenSpaceModule { +public: + constexpr static const char* Name = "AutoNavigation"; + + AutoNavigationModule(); + virtual ~AutoNavigationModule() = default; + + autonavigation::AutoNavigationHandler& AutoNavigationHandler(); + + std::vector documentations() const override; + scripting::LuaLibrary luaLibrary() const override; + +private: + void internalInitialize(const ghoul::Dictionary&) override; + + autonavigation::AutoNavigationHandler _autoNavigationHandler; +}; + +} // namespace openspace + +#endif // __OPENSPACE_MODULE_AUTONAVIGATION___AUTONAVIGATION_MODULE___H__ diff --git a/modules/autonavigation/autonavigationmodule_lua.inl b/modules/autonavigation/autonavigationmodule_lua.inl new file mode 100644 index 0000000000..fb9449cd4d --- /dev/null +++ b/modules/autonavigation/autonavigationmodule_lua.inl @@ -0,0 +1,150 @@ +/***************************************************************************************** + * * + * OpenSpace * + * * + * Copyright (c) 2014-2019 * + * * + * Permission is hereby granted, free of charge, to any person obtaining a copy of this * + * software and associated documentation files (the "Software"), to deal in the Software * + * without restriction, including without limitation the rights to use, copy, modify, * + * merge, publish, distribute, sublicense, and/or sell copies of the Software, and to * + * permit persons to whom the Software is furnished to do so, subject to the following * + * conditions: * + * * + * The above copyright notice and this permission notice shall be included in all copies * + * or substantial portions of the Software. * + * * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, * + * INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A * + * PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT * + * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF * + * CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE * + * OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. * + ****************************************************************************************/ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +namespace openspace::autonavigation::luascriptfunctions { + + const double EPSILON = 1e-12; + + int goTo(lua_State* L) { + int nArguments = ghoul::lua::checkArgumentsAndThrow(L, { 1, 2 }, "lua::goTo"); + + const std::string& nodeIdentifier = ghoul::lua::value(L, 1); + + if (!sceneGraphNode(nodeIdentifier)) { + lua_settop(L, 0); + return ghoul::lua::luaError(L, "Unknown node name: " + nodeIdentifier); + } + + ghoul::Dictionary insDict; + insDict.setValue("Target", nodeIdentifier); + + if (nArguments > 1) { + double duration = ghoul::lua::value(L, 2); + if (duration <= EPSILON) { + lua_settop(L, 0); + return ghoul::lua::luaError(L, "Duration must be larger than zero."); + } + insDict.setValue("Duration", duration); + } + + PathSpecification spec = PathSpecification(Instruction{insDict}); + + AutoNavigationModule* module = global::moduleEngine.module(); + AutoNavigationHandler& handler = module->AutoNavigationHandler(); + handler.createPath(spec); + + lua_settop(L, 0); + ghoul_assert(lua_gettop(L) == 0, "Incorrect number of items left on stack"); + return 0; + } + + int generatePath(lua_State* L) { + ghoul::lua::checkArgumentsAndThrow(L, 1, "lua::generatePath"); + + ghoul::Dictionary dictionary; + ghoul::lua::luaDictionaryFromState(L, dictionary); + PathSpecification spec(dictionary); + + if (spec.instructions()->empty()) { + lua_settop(L, 0); + return ghoul::lua::luaError( + L, fmt::format("No instructions for camera path generation were provided.") + ); + } + + AutoNavigationModule* module = global::moduleEngine.module(); + AutoNavigationHandler& handler = module->AutoNavigationHandler(); + handler.createPath(spec); + + lua_settop(L, 0); + ghoul_assert(lua_gettop(L) == 0, "Incorrect number of items left on stack"); + return 0; + } + + int generatePathFromFile(lua_State* L) { + ghoul::lua::checkArgumentsAndThrow(L, 1, "lua::generatePathFromFile"); + + const std::string& filepath = ghoul::lua::value(L, 1, ghoul::lua::PopValue::Yes); + + if (filepath.empty()) { + return ghoul::lua::luaError(L, "filepath string is empty"); + } + + const std::string absolutePath = absPath(filepath); + + LINFOC("AutoNavigationModule", fmt::format("Reading path instructions from file: {}", absolutePath)); + + if (!FileSys.fileExists(absolutePath)) { + throw ghoul::FileNotFoundError(absolutePath, "PathSpecification"); + } + + // Try to read the dictionary + ghoul::Dictionary dictionary; + try { + ghoul::lua::loadDictionaryFromFile(absolutePath, dictionary); + openspace::documentation::testSpecificationAndThrow( + PathSpecification::Documentation(), + dictionary, + "PathSpecification" + ); + } + catch (ghoul::RuntimeError& e) { + return ghoul::lua::luaError( + L, fmt::format("Unable to read dictionary from file: {}", e.message) + ); + } + + PathSpecification spec(dictionary); + + if (spec.instructions()->empty()) { + return ghoul::lua::luaError( + L, fmt::format("No instructions for camera path generation were provided.") + ); + } + + LINFOC("AutoNavigationModule", "Reading succeeded. Creating path"); + + AutoNavigationModule* module = global::moduleEngine.module(); + AutoNavigationHandler& handler = module->AutoNavigationHandler(); + handler.createPath(spec); + + ghoul_assert(lua_gettop(L) == 0, "Incorrect number of items left on stack"); + return 0; + } + +} // namespace openspace::autonavigation::luascriptfunctions diff --git a/modules/autonavigation/include.cmake b/modules/autonavigation/include.cmake new file mode 100644 index 0000000000..54b51765a2 --- /dev/null +++ b/modules/autonavigation/include.cmake @@ -0,0 +1,5 @@ +set(DEFAULT_MODULE ON) + +set(OPENSPACE_DEPENDENCIES + #add possible other modules that this module depends upon +) diff --git a/modules/autonavigation/pathspecification.cpp b/modules/autonavigation/pathspecification.cpp new file mode 100644 index 0000000000..9fbaea5c05 --- /dev/null +++ b/modules/autonavigation/pathspecification.cpp @@ -0,0 +1,209 @@ +/***************************************************************************************** + * * + * OpenSpace * + * * + * Copyright (c) 2014-2019 * + * * + * Permission is hereby granted, free of charge, to any person obtaining a copy of this * + * software and associated documentation files (the "Software"), to deal in the Software * + * without restriction, including without limitation the rights to use, copy, modify, * + * merge, publish, distribute, sublicense, and/or sell copies of the Software, and to * + * permit persons to whom the Software is furnished to do so, subject to the following * + * conditions: * + * * + * The above copyright notice and this permission notice shall be included in all copies * + * or substantial portions of the Software. * + * * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, * + * INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A * + * PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT * + * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF * + * CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE * + * OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. * + ****************************************************************************************/ + +#include + +#include +#include + +namespace { + constexpr const char* _loggerCat = "PathInstruction"; + + constexpr const char* KeyInstructions = "Instructions"; + + constexpr const char* KeyTarget = "Target"; + constexpr const char* KeyDuration = "Duration"; + constexpr const char* KeyPosition = "Position"; + constexpr const char* KeyHeight = "Height"; + + constexpr const char* KeyNavigationState = "NavigationState"; +} // namespace + +namespace openspace::autonavigation { + +documentation::Documentation TargetNodeInstructionDocumentation() { + using namespace documentation; + + return { + "Target Node Instruction", + "target_node_instruction", + { + { + KeyTarget, + new StringVerifier, + Optional::No, + "The identifier of the target node." + }, + { + KeyDuration, + new DoubleVerifier, + Optional::Yes, + "The desired duration for the camera movement." + }, + { + KeyPosition, + new Vector3Verifier, + Optional::Yes, + "The desired final position for the camera movement, given in model space." + }, + { + KeyHeight, + new DoubleVerifier, + Optional::Yes, + "The desired height from surface for final position (meters). Will be ignored if a target position is set. " + }, + } + }; +} + +InstructionProps::InstructionProps(const ghoul::Dictionary& dictionary) { + // TODO: validate against some documentation? + + if (dictionary.hasValue(KeyDuration)) { + duration = dictionary.value(KeyDuration); + } +} + +TargetNodeInstructionProps::TargetNodeInstructionProps( + const ghoul::Dictionary& dictionary) : InstructionProps(dictionary) +{ + try { + documentation::testSpecificationAndThrow( + TargetNodeInstructionDocumentation(), + dictionary, + "Target Node Instruction" + ); + } + catch (ghoul::RuntimeError& e) { + LERROR(fmt::format("Unable to generate target node instruction from dictionary. Does not match documentation: {}", e.message)); + return; + } + + if (!dictionary.hasValue(KeyTarget)) { + throw ghoul::RuntimeError( + "A camera path instruction requires a target node, to go to or use as reference frame." + ); + } + + targetNode = dictionary.value(KeyTarget); + + if (dictionary.hasValue(KeyPosition)) { + position = dictionary.value(KeyPosition); + } + + if (dictionary.hasValue(KeyHeight)) { + height = dictionary.value(KeyHeight); + } +} + +NavigationStateInstructionProps::NavigationStateInstructionProps( + const ghoul::Dictionary& dictionary) : InstructionProps(dictionary) +{ + if (dictionary.hasValue(KeyNavigationState)) { + auto navStateDict = dictionary.value(KeyNavigationState); + + try { + openspace::documentation::testSpecificationAndThrow( + NavigationState::Documentation(), + navStateDict, + "NavigationState" + ); + } + catch (ghoul::RuntimeError& e) { + LERROR(fmt::format("Unable to generate navigation state instruction from dictionary. Does not match documentation: {}", e.message)); + return; + } + + navState = NavigationState(navStateDict); + } +} + +Instruction::Instruction(const ghoul::Dictionary& dictionary) { + + // TODO: test against some documentation? + + if (dictionary.hasValue(KeyTarget)) { + type = InstructionType::TargetNode; + props = std::make_shared(dictionary); + } + else if (dictionary.hasValue(KeyNavigationState)) { + type = InstructionType::NavigationState; + props = std::make_shared(dictionary); + } + else { + throw ghoul::RuntimeError( + "Could not deduce instruction type." + ); + } +} + +PathSpecification::PathSpecification(const ghoul::Dictionary& dictionary) { + try { + documentation::testSpecificationAndThrow( + Documentation(), + dictionary, + "Path Specification" + ); + } + catch (ghoul::RuntimeError& e) { + LERROR(fmt::format("Unable to generate path specification from dictionary. Does not match documentation: {}", e.message)); + return; + } + + ghoul::Dictionary instructions = dictionary.value(KeyInstructions); + + for (size_t i = 1; i <= instructions.size(); ++i) { + ghoul::Dictionary insDict = instructions.value(std::to_string(i)); + + _instructions.push_back(Instruction{ insDict }); + } +} + +PathSpecification::PathSpecification(const Instruction instruction) { + _instructions.push_back(instruction); +} + +const std::vector* PathSpecification::instructions() const { + return &_instructions; +} + +documentation::Documentation PathSpecification::Documentation() { + using namespace documentation; + + return { + "Path Specification", + "camera_path_specification", + { + { + KeyInstructions, + new TableVerifier, + Optional::No, + "A list of path instructions." + }, + } + }; +} + + +} // namespace openspace::autonavigation diff --git a/modules/autonavigation/pathspecification.h b/modules/autonavigation/pathspecification.h new file mode 100644 index 0000000000..363d85785e --- /dev/null +++ b/modules/autonavigation/pathspecification.h @@ -0,0 +1,88 @@ +/***************************************************************************************** + * * + * OpenSpace * + * * + * Copyright (c) 2014-2019 * + * * + * Permission is hereby granted, free of charge, to any person obtaining a copy of this * + * software and associated documentation files (the "Software"), to deal in the Software * + * without restriction, including without limitation the rights to use, copy, modify, * + * merge, publish, distribute, sublicense, and/or sell copies of the Software, and to * + * permit persons to whom the Software is furnished to do so, subject to the following * + * conditions: * + * * + * The above copyright notice and this permission notice shall be included in all copies * + * or substantial portions of the Software. * + * * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, * + * INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A * + * PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT * + * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF * + * CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE * + * OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. * + ****************************************************************************************/ + +#ifndef __OPENSPACE_MODULE___PATHINSTRUCTION___H__ +#define __OPENSPACE_MODULE___PATHINSTRUCTION___H__ + +#include +#include +#include +#include + +namespace openspace::autonavigation { + +enum InstructionType { TargetNode, NavigationState }; + +struct InstructionProps { + InstructionProps() = default; + InstructionProps(const ghoul::Dictionary& dictionary); + virtual ~InstructionProps() {} // abstract + + std::optional duration; +}; + +struct TargetNodeInstructionProps : public InstructionProps { + TargetNodeInstructionProps(const ghoul::Dictionary& dictionary); + + std::string targetNode; + std::optional position; // relative to target node (model space) + std::optional height; +}; + +struct NavigationStateInstructionProps : public InstructionProps { + using NavigationState = interaction::NavigationHandler::NavigationState; + + NavigationStateInstructionProps(const ghoul::Dictionary& dictionary); + + NavigationState navState; +}; + +struct Instruction { + Instruction() = default; + Instruction(const ghoul::Dictionary& dictionary); + + InstructionType type; + std::shared_ptr props; +}; + +class PathSpecification { + +public: + PathSpecification() = default; + PathSpecification(const ghoul::Dictionary& dictionary); + PathSpecification(const Instruction instruction); + + static documentation::Documentation Documentation(); + + // Accessors + const std::vector* instructions() const; + +private: + std::vector _instructions; + // TODO: maxSpeed or speedFactor or something? +}; + +} // namespace openspace::autonavigation + +#endif // __OPENSPACE_MODULE___NAVIGATIONHANDLER___H__ diff --git a/modules/autonavigation/transferfunctions.cpp b/modules/autonavigation/transferfunctions.cpp new file mode 100644 index 0000000000..9497432457 --- /dev/null +++ b/modules/autonavigation/transferfunctions.cpp @@ -0,0 +1,76 @@ +/***************************************************************************************** + * * + * OpenSpace * + * * + * Copyright (c) 2014-2019 * + * * + * Permission is hereby granted, free of charge, to any person obtaining a copy of this * + * software and associated documentation files (the "Software"), to deal in the Software * + * without restriction, including without limitation the rights to use, copy, modify, * + * merge, publish, distribute, sublicense, and/or sell copies of the Software, and to * + * permit persons to whom the Software is furnished to do so, subject to the following * + * conditions: * + * * + * The above copyright notice and this permission notice shall be included in all copies * + * or substantial portions of the Software. * + * * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, * + * INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A * + * PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT * + * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF * + * CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE * + * OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. * + ****************************************************************************************/ + +#include + +#include + +namespace openspace::autonavigation::transferfunctions { + +double linear(double t) { return t; }; + +double step(double t) { return (t > 0.5); }; + +double circularEaseOut(double p){ + return sqrt((2 - p) * p); +} + +double cubicEaseIn(double t) { return (t*t*t); }; + +double cubicEaseOut(double t) { + double p = 1 - t; + return (p*p*p); +}; + +double cubicEaseInOut(double t) { + if (t < 0.5) { + return 4 * t * t * t; + } + else { + double f = ((2 * t) - 2); + return 0.5 * f * f * f + 1; + } +}; + +double quadraticEaseInOut(double t) { + if (t < 0.5) { + return 2 * t * t; + } + else { + return (-2 * t * t) + (4 * t) - 1; + } +} + +double exponentialEaseInOut(double t) { + if (t == 0.0 || t == 1.0) return t; + + if (t < 0.5) { + return 0.5 * glm::pow(2, (20 * t) - 10); + } + else { + return -0.5 * glm::pow(2, (-20 * t) + 10) + 1; + } +}; + +} // namespace diff --git a/modules/autonavigation/transferfunctions.h b/modules/autonavigation/transferfunctions.h new file mode 100644 index 0000000000..abac0c7c6b --- /dev/null +++ b/modules/autonavigation/transferfunctions.h @@ -0,0 +1,49 @@ +/***************************************************************************************** + * * + * OpenSpace * + * * + * Copyright (c) 2014-2019 * + * * + * Permission is hereby granted, free of charge, to any person obtaining a copy of this * + * software and associated documentation files (the "Software"), to deal in the Software * + * without restriction, including without limitation the rights to use, copy, modify, * + * merge, publish, distribute, sublicense, and/or sell copies of the Software, and to * + * permit persons to whom the Software is furnished to do so, subject to the following * + * conditions: * + * * + * The above copyright notice and this permission notice shall be included in all copies * + * or substantial portions of the Software. * + * * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, * + * INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A * + * PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT * + * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF * + * CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE * + * OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. * + ****************************************************************************************/ + +#ifndef __OPENSPACE_MODULE___TRANSFERFUNCTIONS___H__ +#define __OPENSPACE_MODULE___TRANSFERFUNCTIONS___H__ + +namespace openspace::autonavigation::transferfunctions { + +// Based on functions from https://github.com/warrenm/AHEasing/blob/master/AHEasing/easing.c + +double linear(double t); + +double step(double t); + +double circularEaseOut(double p); + +double cubicEaseIn(double t); + +double cubicEaseOut(double t); + +double cubicEaseInOut(double t); + +double quadraticEaseInOut(double t); + +double exponentialEaseInOut(double t); + +} +#endif From 0a984df58cd3a7f89c8f473d28586364870bc0a6 Mon Sep 17 00:00:00 2001 From: Emma Broman Date: Fri, 10 Jan 2020 15:48:37 -0500 Subject: [PATCH 002/912] Add code for different path segments from Ingelas old branch --- modules/autonavigation/CMakeLists.txt | 6 +- .../autonavigation/autonavigationhandler.cpp | 192 ++++++++++-------- .../autonavigation/autonavigationhandler.h | 30 ++- modules/autonavigation/helperfunctions.cpp | 134 ++++++++++++ modules/autonavigation/helperfunctions.h | 75 +++++++ modules/autonavigation/pathsegment.cpp | 186 +++++++++++++++++ modules/autonavigation/pathsegment.h | 74 +++++++ 7 files changed, 595 insertions(+), 102 deletions(-) create mode 100644 modules/autonavigation/helperfunctions.cpp create mode 100644 modules/autonavigation/helperfunctions.h create mode 100644 modules/autonavigation/pathsegment.cpp create mode 100644 modules/autonavigation/pathsegment.h diff --git a/modules/autonavigation/CMakeLists.txt b/modules/autonavigation/CMakeLists.txt index 6d346304cc..2ec485c136 100644 --- a/modules/autonavigation/CMakeLists.txt +++ b/modules/autonavigation/CMakeLists.txt @@ -27,8 +27,9 @@ include(${OPENSPACE_CMAKE_EXT_DIR}/module_definition.cmake) set(HEADER_FILES ${CMAKE_CURRENT_SOURCE_DIR}/autonavigationmodule.h ${CMAKE_CURRENT_SOURCE_DIR}/autonavigationhandler.h - ${CMAKE_CURRENT_SOURCE_DIR}/transferfunctions.h + ${CMAKE_CURRENT_SOURCE_DIR}/helperfunctions.h ${CMAKE_CURRENT_SOURCE_DIR}/pathspecification.h + ${CMAKE_CURRENT_SOURCE_DIR}/pathsegment.h ) source_group("Header Files" FILES ${HEADER_FILES}) @@ -36,8 +37,9 @@ set(SOURCE_FILES ${CMAKE_CURRENT_SOURCE_DIR}/autonavigationmodule.cpp ${CMAKE_CURRENT_SOURCE_DIR}/autonavigationmodule_lua.inl ${CMAKE_CURRENT_SOURCE_DIR}/autonavigationhandler.cpp - ${CMAKE_CURRENT_SOURCE_DIR}/transferfunctions.cpp + ${CMAKE_CURRENT_SOURCE_DIR}/helperfunctions.cpp ${CMAKE_CURRENT_SOURCE_DIR}/pathspecification.cpp + ${CMAKE_CURRENT_SOURCE_DIR}/pathsegment.cpp ) source_group("Source Files" FILES ${SOURCE_FILES}) diff --git a/modules/autonavigation/autonavigationhandler.cpp b/modules/autonavigation/autonavigationhandler.cpp index 5b776a9914..30b0e28500 100644 --- a/modules/autonavigation/autonavigationhandler.cpp +++ b/modules/autonavigation/autonavigationhandler.cpp @@ -24,7 +24,7 @@ #include -#include +#include #include #include #include @@ -60,49 +60,13 @@ const double AutoNavigationHandler::pathDuration() const { PathSegment& AutoNavigationHandler::currentPathSegment() { for (PathSegment& ps : _pathSegments) { - double endTime = ps.startTime + ps.duration; + double endTime = ps.startTime() + ps.duration(); if (endTime > _currentTime) { return ps; } } } -void AutoNavigationHandler::updateCamera(double deltaTime) { - ghoul_assert(camera() != nullptr, "Camera must not be nullptr"); - - if (!_isPlaying || _pathSegments.empty()) return; - - PathSegment cps = currentPathSegment(); - - // INTERPOLATE (TODO: make a function, and allow different methods) - - 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 - // 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; - global::navigationHandler.orbitalNavigator().setAnchorNode(cs.referenceNode); - - // TODO: add different ways to interpolate later - glm::dvec3 cameraPosition = cps.start.position * (1.0 - t) + cps.end.position * t; - glm::dquat cameraRotation = - glm::slerp(cps.start.rotation, cps.end.rotation, t); - - camera()->setPositionVec3(cameraPosition); - camera()->setRotation(cameraRotation); - - _currentTime += deltaTime; - - // reached the end of the path => stop playing - if (_currentTime > _pathDuration) { - _isPlaying = false; - // TODO: implement suitable stop behaviour - } -} - void AutoNavigationHandler::createPath(PathSpecification& spec) { clearPath(); bool success = true; @@ -110,18 +74,69 @@ void AutoNavigationHandler::createPath(PathSpecification& spec) { const Instruction& ins = spec.instructions()->at(i); success = handleInstruction(ins, i); - if (!success) + if (!success) break; } - if (success) { - LINFO("Succefully generated camera path. Starting."); + if (success) startPath(); - } - else + else LINFO("Could not create path."); } +void AutoNavigationHandler::updateCamera(double deltaTime) { + ghoul_assert(camera() != nullptr, "Camera must not be nullptr"); + + if (!_isPlaying || _pathSegments.empty()) return; + + PathSegment cps = currentPathSegment(); + + // Interpolation variable + double t = (_currentTime - cps.startTime()) / cps.duration(); + t = std::max(0.0, std::min(t, 1.0)); + + // 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(); + global::navigationHandler.orbitalNavigator().setAnchorNode(cs.referenceNode); + + glm::dvec3 cameraPosition = cps.getPositionAt(t); + glm::dquat cameraRotation = cps.getRotationAt(t); + + camera()->setPositionVec3(cameraPosition); + camera()->setRotation(cameraRotation); + + _currentTime += deltaTime; + + // reached the end of the path => stop playing + if (_currentTime > _pathDuration) { + _isPlaying = false; + // TODO: implement suitable stop behaviour + } +} +//TODO: remove! No londer used +void AutoNavigationHandler::addToPath(const SceneGraphNode* node, const double duration) { + ghoul_assert(node != nullptr, "Target node must not be nullptr"); + ghoul_assert(duration > 0, "Duration must be larger than zero."); + + CameraState start = getStartState(); + + glm::dvec3 targetPos = computeTargetPositionAtNode(node, start.position); + CameraState end = cameraStateFromTargetPosition( + targetPos, node->worldPosition(), node->identifier()); + + // compute startTime + double startTime = 0.0; + if (!_pathSegments.empty()) { + PathSegment last = _pathSegments.back(); + startTime = last.startTime() + last.duration(); + } + + PathSegment newSegment{ start, end, duration, startTime }; + _pathSegments.push_back(newSegment); +} + void AutoNavigationHandler::clearPath() { _pathSegments.clear(); _pathDuration = 0.0; @@ -133,12 +148,52 @@ void AutoNavigationHandler::startPath() { _pathDuration = 0.0; for (auto ps : _pathSegments) { - _pathDuration += ps.duration; + _pathDuration += ps.duration(); } _currentTime = 0.0; _isPlaying = true; } +glm::dvec3 AutoNavigationHandler::computeTargetPositionAtNode( + const SceneGraphNode* node, glm::dvec3 prevPos, std::optional height) +{ + glm::dvec3 targetPos = node->worldPosition(); + glm::dvec3 targetToPrevVector = prevPos - targetPos; + + double radius = static_cast(node->boundingSphere()); + + double desiredDistance; + if (height.has_value()) { + desiredDistance = height.value(); + } + else { + desiredDistance = 2 * radius; + } + + // TODO: compute actual distance above surface and validate negative values + + // move target position out from surface, along vector to camera + targetPos += glm::normalize(targetToPrevVector) * (radius + desiredDistance); + + return targetPos; +} + +CameraState AutoNavigationHandler::cameraStateFromTargetPosition( + glm::dvec3 targetPos, glm::dvec3 lookAtPos, std::string node) +{ + ghoul_assert(camera() != nullptr, "Camera must not be nullptr"); + + glm::dmat4 lookAtMat = glm::lookAt( + targetPos, + lookAtPos, + camera()->lookUpVectorWorldSpace() + ); + + glm::dquat targetRot = glm::normalize(glm::inverse(glm::quat_cast(lookAtMat))); + + return CameraState{ targetPos, targetRot, node}; +} + CameraState AutoNavigationHandler::getStartState() { CameraState cs; if (_pathSegments.empty()) { @@ -147,7 +202,7 @@ CameraState AutoNavigationHandler::getStartState() { cs.referenceNode = global::navigationHandler.anchorNode()->identifier(); } else { - cs = _pathSegments.back().end; + cs = _pathSegments.back().end(); } return cs; @@ -155,28 +210,30 @@ CameraState AutoNavigationHandler::getStartState() { bool AutoNavigationHandler::handleInstruction(const Instruction& instruction, int index) { - CameraState startState = getStartState(); - CameraState endState; + CameraState startState, endState; double duration, startTime; bool success = true; + startState = getStartState(); + switch (instruction.type) { case InstructionType::TargetNode: + LINFO("Handle target node instruction"); success = endFromTargetNodeInstruction( endState, startState, instruction, index ); break; case InstructionType::NavigationState: + LINFO("Handle navigation state instruction"); success = endFromNavigationStateInstruction( endState, instruction, index ); break; default: - LERROR(fmt::format("Non-implemented instruction type: {}.", instruction.type)); - success = false; + // TODO: error message break; } @@ -199,7 +256,7 @@ bool AutoNavigationHandler::handleInstruction(const Instruction& instruction, in startTime = 0.0; if (!_pathSegments.empty()) { PathSegment& last = _pathSegments.back(); - startTime = last.startTime + last.duration; + startTime = last.startTime() + last.duration(); } // create new path @@ -236,28 +293,15 @@ bool AutoNavigationHandler::endFromTargetNodeInstruction( targetPos = targetNode->worldPosition() + targetNode->worldRotationMatrix() * props->position.value(); } else { - bool hasHeight = props->height.has_value(); - - // TODO: compute defualt height in a better way - double defaultHeight = 2 * targetNode->boundingSphere(); - double height = hasHeight? props->height.value() : defaultHeight; - targetPos = computeTargetPositionAtNode( targetNode, prevState.position, - height + props->height ); } - glm::dmat4 lookAtMat = glm::lookAt( - targetPos, - targetNode->worldPosition(), - camera()->lookUpVectorWorldSpace() - ); - - glm::dquat targetRot = glm::normalize(glm::inverse(glm::quat_cast(lookAtMat))); - - endState = CameraState{ targetPos, targetRot, identifier }; + endState = cameraStateFromTargetPosition( + targetPos, targetNode->worldPosition(), identifier); return true; } @@ -313,20 +357,4 @@ bool AutoNavigationHandler::endFromNavigationStateInstruction( return true; } -glm::dvec3 AutoNavigationHandler::computeTargetPositionAtNode( - const SceneGraphNode* node, glm::dvec3 prevPos, double height) -{ - // TODO: compute actual distance above surface and validate negative values - - glm::dvec3 targetPos = node->worldPosition(); - glm::dvec3 targetToPrevVector = prevPos - targetPos; - - double radius = static_cast(node->boundingSphere()); - - // move target position out from surface, along vector to camera - targetPos += glm::normalize(targetToPrevVector) * (radius + height); - - return targetPos; -} - } // namespace openspace::autonavigation diff --git a/modules/autonavigation/autonavigationhandler.h b/modules/autonavigation/autonavigationhandler.h index 3d4260e063..f880ba5383 100644 --- a/modules/autonavigation/autonavigationhandler.h +++ b/modules/autonavigation/autonavigationhandler.h @@ -25,6 +25,7 @@ #ifndef __OPENSPACE_MODULE___AUTONAVIGATIONHANDLER___H__ #define __OPENSPACE_MODULE___AUTONAVIGATIONHANDLER___H__ +#include #include #include #include @@ -37,18 +38,7 @@ namespace openspace { namespace openspace::autonavigation { -struct CameraState { - glm::dvec3 position; - glm::dquat rotation; - std::string referenceNode; -}; - -struct PathSegment { - CameraState start; - CameraState end; - double duration; - double startTime; -}; +struct CameraState; class AutoNavigationHandler : public properties::PropertyOwner { public: @@ -62,23 +52,27 @@ public: const double pathDuration() const; PathSegment& currentPathSegment(); - void updateCamera(double deltaTime); void createPath(PathSpecification& spec); + + void updateCamera(double deltaTime); + void addToPath(const SceneGraphNode* node, double duration = 5.0); // TODO: remove void clearPath(); void startPath(); + // TODO: move to privates + glm::dvec3 computeTargetPositionAtNode(const SceneGraphNode* node, + const glm::dvec3 prevPos, std::optional height = std::nullopt); + // TODO: move to privates + CameraState cameraStateFromTargetPosition(glm::dvec3 targetPos, + glm::dvec3 lookAtPos, std::string node); + private: CameraState getStartState(); bool handleInstruction(const Instruction& instruction, int index); - bool endFromTargetNodeInstruction(CameraState& endState, CameraState& prevState, const Instruction& instruction, int index); - bool endFromNavigationStateInstruction(CameraState& endState, const Instruction& instruction, int index); - glm::dvec3 computeTargetPositionAtNode(const SceneGraphNode* node, - glm::dvec3 prevPos, double height); - std::vector _pathSegments; double _pathDuration; diff --git a/modules/autonavigation/helperfunctions.cpp b/modules/autonavigation/helperfunctions.cpp new file mode 100644 index 0000000000..932728ca75 --- /dev/null +++ b/modules/autonavigation/helperfunctions.cpp @@ -0,0 +1,134 @@ +/***************************************************************************************** + * * + * OpenSpace * + * * + * Copyright (c) 2014-2019 * + * * + * Permission is hereby granted, free of charge, to any person obtaining a copy of this * + * software and associated documentation files (the "Software"), to deal in the Software * + * without restriction, including without limitation the rights to use, copy, modify, * + * merge, publish, distribute, sublicense, and/or sell copies of the Software, and to * + * permit persons to whom the Software is furnished to do so, subject to the following * + * conditions: * + * * + * The above copyright notice and this permission notice shall be included in all copies * + * or substantial portions of the Software. * + * * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, * + * INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A * + * PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT * + * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF * + * CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE * + * OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. * + ****************************************************************************************/ + +#include + +namespace openspace::autonavigation::helpers { + + // Shift and scale to a subinterval [start,end] + double shiftAndScale(double t, double start, double end) { + ghoul_assert(0.0 < start && start < end && end < 1.0, + "Values must be 0.0 < start < end < 1.0!"); + double tScaled = t / (end - start) - start; + return std::max(0.0, std::min(tScaled, 1.0)); + } + +} // helpers + +namespace openspace::autonavigation::easingfunctions { + +double linear(double t) { return t; }; + +double step(double t) { return (t > 0.5); }; + +double circularEaseOut(double p){ + return sqrt((2 - p) * p); +} + +double cubicEaseIn(double t) { return (t*t*t); }; + +double cubicEaseOut(double t) { + double p = 1 - t; + return (p*p*p); +} + +double cubicEaseInOut(double t) { + if (t < 0.5) { + return 4 * t * t * t; + } + else { + double f = ((2 * t) - 2); + return 0.5 * f * f * f + 1; + } +} + +double quadraticEaseInOut(double t) { + if (t < 0.5) { + return 2 * t * t; + } + else { + return (-2 * t * t) + (4 * t) - 1; + } +} + +double exponentialEaseInOut(double t) { + if (t == 0.0 || t == 1.0) return t; + + if (t < 0.5) { + return 0.5 * glm::pow(2, (20 * t) - 10); + } + else { + return -0.5 * glm::pow(2, (-20 * t) + 10) + 1; + } +} + +} // easingfunctions + +namespace openspace::autonavigation::interpolator { + + // TODO: make into template function + glm::dvec3 cubicBezier(double t, const glm::dvec3 &cp1, const glm::dvec3 &cp2, + const glm::dvec3 &cp3, const glm::dvec3 &cp4 ) + { + double a = 1.0 - t; + return cp1 * a * a * a + + cp2 * t * a * a * 3.0 + + cp3 * t * t * a * 3.0 + + cp4 * t * t * t; + } + + glm::dvec3 piecewiseCubicBezier(double t, const std::vector &controlPoints) { + size_t n = controlPoints.size(); + ghoul_assert(n > 4, "Minimum of four control points needed for interpolation!"); + + double n_seg = (n - 1.0) / 3.0; + ghoul_assert(std::fmod(n_seg, 1.0) == 0, "A vector containing 3n + 1 control points must be provided!"); + + // for control points equally spaced in time + double t_seg = std::fmod(t * n_seg, 1.0); + t_seg = std::max(0.0, std::min(t_seg, 1.0)); + + size_t idx = std::floor(t * n_seg) * 3; + + return cubicBezier(t_seg, controlPoints[idx], controlPoints[idx + 1], + controlPoints[idx + 2], controlPoints[idx + 3]); + } + + glm::dvec3 piecewiseLinear(double t, const std::vector &controlPoints) { + size_t n = controlPoints.size(); + ghoul_assert(n > 2, "Minimum of two control points needed for interpolation!"); + + size_t n_seg = n - 1; + + // for control points equally spaced in time + double t_seg = std::fmod( t*n_seg, 1.0 ); + t_seg = std::max(0.0, std::min(t_seg, 1.0)); + + size_t idx = std::floor(t*n_seg); + + return (1.0 - t_seg) * controlPoints[idx] + t_seg * controlPoints[idx + 1]; + } + +} // interpolator + diff --git a/modules/autonavigation/helperfunctions.h b/modules/autonavigation/helperfunctions.h new file mode 100644 index 0000000000..38e897a338 --- /dev/null +++ b/modules/autonavigation/helperfunctions.h @@ -0,0 +1,75 @@ +/***************************************************************************************** + * * + * OpenSpace * + * * + * Copyright (c) 2014-2019 * + * * + * Permission is hereby granted, free of charge, to any person obtaining a copy of this * + * software and associated documentation files (the "Software"), to deal in the Software * + * without restriction, including without limitation the rights to use, copy, modify, * + * merge, publish, distribute, sublicense, and/or sell copies of the Software, and to * + * permit persons to whom the Software is furnished to do so, subject to the following * + * conditions: * + * * + * The above copyright notice and this permission notice shall be included in all copies * + * or substantial portions of the Software. * + * * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, * + * INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A * + * PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT * + * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF * + * CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE * + * OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. * + ****************************************************************************************/ + +#ifndef __OPENSPACE_MODULE___EASINGFUNCTIONS___H__ +#define __OPENSPACE_MODULE___EASINGFUNCTIONS___H__ + +#include +#include +#include +#include +#include + +namespace openspace::autonavigation::helpers { + + // Make interpolator parameter t [0,1] progress only inside a subinterval + double shiftAndScale(double t, double newStart, double newEnd); + +} // helpers + +namespace openspace::autonavigation::easingfunctions { + +// Based on functions from https://github.com/warrenm/AHEasing/blob/master/AHEasing/easing.c + +double linear(double t); + +double step(double t); + +double circularEaseOut(double p); + +double cubicEaseIn(double t); + +double cubicEaseOut(double t); + +double cubicEaseInOut(double t); + +double quadraticEaseInOut(double t); + +double exponentialEaseInOut(double t); + +} + +// TODO: include interpolator.h to helperfunctions +// error when interpolator.h is included and used both here and in pathsegment +namespace openspace::autonavigation::interpolator { + + glm::dvec3 cubicBezier(double t, const glm::dvec3 &cp1, const glm::dvec3 &cp2, + const glm::dvec3 &cp3, const glm::dvec3 &cp4); + + glm::dvec3 piecewiseCubicBezier(double t, const std::vector &controlPoints); + + glm::dvec3 piecewiseLinear(double t, const std::vector &controlPoints); + +} // namespace +#endif diff --git a/modules/autonavigation/pathsegment.cpp b/modules/autonavigation/pathsegment.cpp new file mode 100644 index 0000000000..7203b589fc --- /dev/null +++ b/modules/autonavigation/pathsegment.cpp @@ -0,0 +1,186 @@ +/***************************************************************************************** + * * + * OpenSpace * + * * + * Copyright (c) 2014-2019 * + * * + * Permission is hereby granted, free of charge, to any person obtaining a copy of this * + * software and associated documentation files (the "Software"), to deal in the Software * + * without restriction, including without limitation the rights to use, copy, modify, * + * merge, publish, distribute, sublicense, and/or sell copies of the Software, and to * + * permit persons to whom the Software is furnished to do so, subject to the following * + * conditions: * + * * + * The above copyright notice and this permission notice shall be included in all copies * + * or substantial portions of the Software. * + * * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, * + * INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A * + * PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT * + * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF * + * CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE * + * OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. * + ****************************************************************************************/ + +#include + +#include +#include +#include +#include +#include +#include +#include +#include + +namespace { + constexpr const char* _loggerCat = "PathSegment"; +} // namespace + +namespace openspace::autonavigation { + +PathSegment::PathSegment( + CameraState start, CameraState end, double duration, double startTime, CurveType type) + : _start(start), _end(end), _duration(duration), _startTime(startTime), _curveType(type) +{ + switch(_curveType) { + case Bezier: + generateBezier(); + break; + case Bezier2: + generateBezier2(); + break; + case Linear: + break; + case Linear2: + generateLinear2(); + break; + default: + LERROR(fmt::format("Cannot create curve of type {}. Type does not exist!", _curveType)); + } +} + +CameraState PathSegment::start() const { return _start; } + +CameraState PathSegment::end() const { return _end; } + +double PathSegment::duration() const { return _duration; } + +double PathSegment::startTime() const { return _startTime; } + +glm::vec3 PathSegment::getPositionAt(double t) { + t = easingfunctions::cubicEaseInOut(t); + + switch(_curveType) { + case Bezier: + return interpolator::cubicBezier(t, + _controlPoints[0], _controlPoints[1], _controlPoints[2], _controlPoints[3]); + break; + case Bezier2: + return interpolator::piecewiseCubicBezier(t, _controlPoints); + break; + case Linear: + return ghoul::interpolateLinear(t, _start.position, _end.position); + break; + case Linear2: + return interpolator::piecewiseLinear(t, _controlPoints); + break; + default: + LERROR(fmt::format("Cannot get position for curve type {}. Type does not exist!", _curveType)); + } +} + +glm::dquat PathSegment::getRotationAt(double t) { + double tRot = helpers::shiftAndScale(t, 0.1, 0.9); + tRot = easingfunctions::cubicEaseInOut(tRot); + + switch (_curveType) { + case Linear2: + return getLookAtRotation( + tRot, + getPositionAt(t), + global::navigationHandler.camera()->lookUpVectorWorldSpace() + ); + break; + default: + return glm::slerp(_start.rotation, _end.rotation, tRot); + } +} + +glm::dquat PathSegment::getLookAtRotation(double t, glm::dvec3 currentPos, glm::dvec3 up) { + glm::dvec3 startLookAtPos = sceneGraphNode(_start.referenceNode)->worldPosition(); + glm::dvec3 endLookAtPos = sceneGraphNode(_end.referenceNode)->worldPosition(); + glm::dvec3 lookAtPos = ghoul::interpolateLinear(t, startLookAtPos, endLookAtPos); + + glm::dmat4 lookAtMat = glm::lookAt( + currentPos, + lookAtPos, + up + ); + + return glm::normalize(glm::inverse(glm::quat_cast(lookAtMat))); +} + +// A single bezier segment with four control points +void PathSegment::generateBezier() { + glm::dvec3 startNodePos = sceneGraphNode(_start.referenceNode)->worldPosition(); + glm::dvec3 endNodePos = sceneGraphNode(_end.referenceNode)->worldPosition(); + // vectors pointing away from target nodes + glm::dvec3 startDirection =_start.position - startNodePos; + glm::dvec3 endDirection = _end.position - endNodePos; + + _controlPoints.push_back(_start.position); + _controlPoints.push_back(_start.position + 10.0 * startDirection); + _controlPoints.push_back(_end.position + 10.0 * endDirection); + _controlPoints.push_back(_end.position); +} + +void PathSegment::generateBezier2() { + // START: + glm::dvec3 startNodePos = sceneGraphNode(_start.referenceNode)->worldPosition(); + glm::dvec3 startDirection = _start.position - startNodePos; + + // END: + glm::dvec3 endNodePos = sceneGraphNode(_end.referenceNode)->worldPosition(); + glm::dvec3 endDirection = _end.position - endNodePos; + + // MIDDLE: one knot and two control points parallell to target nodes + glm::dvec3 AB = endNodePos - startNodePos; + glm::dvec3 C = normalize(startDirection + endDirection); + glm::dvec3 CparAB = glm::dot(C, normalize(AB))* normalize(AB); + glm::dvec3 CortAB = normalize(C - CparAB); + double d = length(AB); + + // TODO: set points that actually look good + _controlPoints.push_back(_start.position); + _controlPoints.push_back(_start.position + 2.0 * startDirection); + + _controlPoints.push_back(_start.position + 1.5 * d * CortAB); + _controlPoints.push_back(_start.position + 1.5 * d * CortAB + 0.5 * AB); + _controlPoints.push_back(_end.position + 1.5 * d * CortAB); + + _controlPoints.push_back(_end.position + 2.0 * endDirection); + _controlPoints.push_back(_end.position); +} + +void PathSegment::generateLinear2() { + // START: + glm::dvec3 startNodePos = sceneGraphNode(_start.referenceNode)->worldPosition(); + glm::dvec3 startDirection = _start.position - startNodePos; + + // END: + glm::dvec3 endNodePos = sceneGraphNode(_end.referenceNode)->worldPosition(); + glm::dvec3 endDirection = _end.position - endNodePos; + + // MIDDLE: + glm::dvec3 AB = endNodePos - startNodePos; + glm::dvec3 C = normalize(startDirection + endDirection); + glm::dvec3 CparAB = glm::dot(C, normalize(AB))* normalize(AB); + glm::dvec3 CortAB = normalize(C - CparAB); + double d = length(AB); + + _controlPoints.push_back(_start.position); + _controlPoints.push_back(_start.position + 2.0 * d * CortAB + 0.5 * AB); //TODO: use scale instead of 2.0 + _controlPoints.push_back(_end.position); +} +} // namespace openspace::autonavigation diff --git a/modules/autonavigation/pathsegment.h b/modules/autonavigation/pathsegment.h new file mode 100644 index 0000000000..a86998cbd6 --- /dev/null +++ b/modules/autonavigation/pathsegment.h @@ -0,0 +1,74 @@ +/***************************************************************************************** + * * + * OpenSpace * + * * + * Copyright (c) 2014-2019 * + * * + * Permission is hereby granted, free of charge, to any person obtaining a copy of this * + * software and associated documentation files (the "Software"), to deal in the Software * + * without restriction, including without limitation the rights to use, copy, modify, * + * merge, publish, distribute, sublicense, and/or sell copies of the Software, and to * + * permit persons to whom the Software is furnished to do so, subject to the following * + * conditions: * + * * + * The above copyright notice and this permission notice shall be included in all copies * + * or substantial portions of the Software. * + * * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, * + * INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A * + * PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT * + * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF * + * CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE * + * OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. * + ****************************************************************************************/ + +#ifndef __OPENSPACE_MODULE___PATHSEGMENT___H__ +#define __OPENSPACE_MODULE___PATHSEGMENT___H__ + +#include +#include + +namespace openspace::autonavigation { + +struct CameraState { + glm::dvec3 position; + glm::dquat rotation; + std::string referenceNode; +}; + +enum CurveType { + Bezier, + Bezier2, + Linear, + Linear2 +}; + +class PathSegment { +public: + PathSegment(CameraState start, CameraState end, double duration, double startTime, CurveType type = Bezier); + + CameraState start() const; + CameraState end() const; + double duration() const; + double startTime() const; + + glm::vec3 getPositionAt(double t); + glm::dquat getRotationAt(double t); + glm::dquat getLookAtRotation(double t, glm::dvec3 currentPos, glm::dvec3 up); + +private: + void generateBezier(); + void generateBezier2(); + void generateLinear2(); + + CameraState _start; + CameraState _end; + double _duration; + double _startTime; + CurveType _curveType; + std::vector _controlPoints; +}; + +} // namespace openspace::autonavigation + +#endif // __OPENSPACE_MODULE___PATHSEGMENT___H__ From 24d916a03e39787e65bb6f5db099046f2e54bad2 Mon Sep 17 00:00:00 2001 From: Emma Broman Date: Fri, 10 Jan 2020 17:11:54 -0500 Subject: [PATCH 003/912] Remove code that should not have been part of previous commit Forgot to merge master into this branch before setting up the new and cleaned repo. The code that was removed from this commit shall not be part of the changes in this branch. --- .../autonavigation/autonavigationhandler.cpp | 132 +++++++----------- .../autonavigation/autonavigationhandler.h | 16 +-- 2 files changed, 56 insertions(+), 92 deletions(-) diff --git a/modules/autonavigation/autonavigationhandler.cpp b/modules/autonavigation/autonavigationhandler.cpp index 30b0e28500..151855ef7d 100644 --- a/modules/autonavigation/autonavigationhandler.cpp +++ b/modules/autonavigation/autonavigationhandler.cpp @@ -67,23 +67,6 @@ PathSegment& AutoNavigationHandler::currentPathSegment() { } } -void AutoNavigationHandler::createPath(PathSpecification& spec) { - clearPath(); - bool success = true; - for (int i = 0; i < spec.instructions()->size(); i++) { - const Instruction& ins = spec.instructions()->at(i); - success = handleInstruction(ins, i); - - if (!success) - break; - } - - if (success) - startPath(); - else - LINFO("Could not create path."); -} - void AutoNavigationHandler::updateCamera(double deltaTime) { ghoul_assert(camera() != nullptr, "Camera must not be nullptr"); @@ -115,26 +98,24 @@ void AutoNavigationHandler::updateCamera(double deltaTime) { // TODO: implement suitable stop behaviour } } -//TODO: remove! No londer used -void AutoNavigationHandler::addToPath(const SceneGraphNode* node, const double duration) { - ghoul_assert(node != nullptr, "Target node must not be nullptr"); - ghoul_assert(duration > 0, "Duration must be larger than zero."); - CameraState start = getStartState(); +void AutoNavigationHandler::createPath(PathSpecification& spec) { + clearPath(); + bool success = true; + for (int i = 0; i < spec.instructions()->size(); i++) { + const Instruction& ins = spec.instructions()->at(i); + success = handleInstruction(ins, i); - glm::dvec3 targetPos = computeTargetPositionAtNode(node, start.position); - CameraState end = cameraStateFromTargetPosition( - targetPos, node->worldPosition(), node->identifier()); - - // compute startTime - double startTime = 0.0; - if (!_pathSegments.empty()) { - PathSegment last = _pathSegments.back(); - startTime = last.startTime() + last.duration(); + if (!success) + break; } - PathSegment newSegment{ start, end, duration, startTime }; - _pathSegments.push_back(newSegment); + if (success) { + LINFO("Succefully generated camera path. Starting."); + startPath(); + } + else + LINFO("Could not create path."); } void AutoNavigationHandler::clearPath() { @@ -154,46 +135,6 @@ void AutoNavigationHandler::startPath() { _isPlaying = true; } -glm::dvec3 AutoNavigationHandler::computeTargetPositionAtNode( - const SceneGraphNode* node, glm::dvec3 prevPos, std::optional height) -{ - glm::dvec3 targetPos = node->worldPosition(); - glm::dvec3 targetToPrevVector = prevPos - targetPos; - - double radius = static_cast(node->boundingSphere()); - - double desiredDistance; - if (height.has_value()) { - desiredDistance = height.value(); - } - else { - desiredDistance = 2 * radius; - } - - // TODO: compute actual distance above surface and validate negative values - - // move target position out from surface, along vector to camera - targetPos += glm::normalize(targetToPrevVector) * (radius + desiredDistance); - - return targetPos; -} - -CameraState AutoNavigationHandler::cameraStateFromTargetPosition( - glm::dvec3 targetPos, glm::dvec3 lookAtPos, std::string node) -{ - ghoul_assert(camera() != nullptr, "Camera must not be nullptr"); - - glm::dmat4 lookAtMat = glm::lookAt( - targetPos, - lookAtPos, - camera()->lookUpVectorWorldSpace() - ); - - glm::dquat targetRot = glm::normalize(glm::inverse(glm::quat_cast(lookAtMat))); - - return CameraState{ targetPos, targetRot, node}; -} - CameraState AutoNavigationHandler::getStartState() { CameraState cs; if (_pathSegments.empty()) { @@ -210,30 +151,28 @@ CameraState AutoNavigationHandler::getStartState() { bool AutoNavigationHandler::handleInstruction(const Instruction& instruction, int index) { - CameraState startState, endState; + CameraState startState = getStartState(); + CameraState endState; double duration, startTime; bool success = true; - startState = getStartState(); - switch (instruction.type) { case InstructionType::TargetNode: - LINFO("Handle target node instruction"); success = endFromTargetNodeInstruction( endState, startState, instruction, index ); break; case InstructionType::NavigationState: - LINFO("Handle navigation state instruction"); success = endFromNavigationStateInstruction( endState, instruction, index ); break; default: - // TODO: error message + LERROR(fmt::format("Non-implemented instruction type: {}.", instruction.type)); + success = false; break; } @@ -293,15 +232,28 @@ bool AutoNavigationHandler::endFromTargetNodeInstruction( targetPos = targetNode->worldPosition() + targetNode->worldRotationMatrix() * props->position.value(); } else { + bool hasHeight = props->height.has_value(); + + // TODO: compute defualt height in a better way + double defaultHeight = 2 * targetNode->boundingSphere(); + double height = hasHeight? props->height.value() : defaultHeight; + targetPos = computeTargetPositionAtNode( targetNode, prevState.position, - props->height + height ); } - endState = cameraStateFromTargetPosition( - targetPos, targetNode->worldPosition(), identifier); + glm::dmat4 lookAtMat = glm::lookAt( + targetPos, + targetNode->worldPosition(), + camera()->lookUpVectorWorldSpace() + ); + + glm::dquat targetRot = glm::normalize(glm::inverse(glm::quat_cast(lookAtMat))); + + endState = CameraState{ targetPos, targetRot, identifier }; return true; } @@ -357,4 +309,20 @@ bool AutoNavigationHandler::endFromNavigationStateInstruction( return true; } +glm::dvec3 AutoNavigationHandler::computeTargetPositionAtNode( + const SceneGraphNode* node, glm::dvec3 prevPos, double height) +{ + // TODO: compute actual distance above surface and validate negative values + + glm::dvec3 targetPos = node->worldPosition(); + glm::dvec3 targetToPrevVector = prevPos - targetPos; + + double radius = static_cast(node->boundingSphere()); + + // move target position out from surface, along vector to camera + targetPos += glm::normalize(targetToPrevVector) * (radius + height); + + return targetPos; +} + } // namespace openspace::autonavigation diff --git a/modules/autonavigation/autonavigationhandler.h b/modules/autonavigation/autonavigationhandler.h index f880ba5383..12e44dd6c6 100644 --- a/modules/autonavigation/autonavigationhandler.h +++ b/modules/autonavigation/autonavigationhandler.h @@ -52,27 +52,23 @@ public: const double pathDuration() const; PathSegment& currentPathSegment(); - void createPath(PathSpecification& spec); - void updateCamera(double deltaTime); - void addToPath(const SceneGraphNode* node, double duration = 5.0); // TODO: remove + void createPath(PathSpecification& spec); void clearPath(); void startPath(); - // TODO: move to privates - glm::dvec3 computeTargetPositionAtNode(const SceneGraphNode* node, - const glm::dvec3 prevPos, std::optional height = std::nullopt); - // TODO: move to privates - CameraState cameraStateFromTargetPosition(glm::dvec3 targetPos, - glm::dvec3 lookAtPos, std::string node); - private: CameraState getStartState(); bool handleInstruction(const Instruction& instruction, int index); + bool endFromTargetNodeInstruction(CameraState& endState, CameraState& prevState, const Instruction& instruction, int index); + bool endFromNavigationStateInstruction(CameraState& endState, const Instruction& instruction, int index); + glm::dvec3 computeTargetPositionAtNode(const SceneGraphNode* node, + glm::dvec3 prevPos, double height); + std::vector _pathSegments; double _pathDuration; From d1969a7943dcd79e55b23a6553b8dd2527616fff Mon Sep 17 00:00:00 2001 From: Emma Broman Date: Tue, 14 Jan 2020 08:50:47 -0500 Subject: [PATCH 004/912] Fix inconsistent naming of variables --- modules/autonavigation/helperfunctions.cpp | 22 +++++++++++----------- 1 file changed, 11 insertions(+), 11 deletions(-) diff --git a/modules/autonavigation/helperfunctions.cpp b/modules/autonavigation/helperfunctions.cpp index 932728ca75..c57845ed77 100644 --- a/modules/autonavigation/helperfunctions.cpp +++ b/modules/autonavigation/helperfunctions.cpp @@ -102,16 +102,16 @@ namespace openspace::autonavigation::interpolator { size_t n = controlPoints.size(); ghoul_assert(n > 4, "Minimum of four control points needed for interpolation!"); - double n_seg = (n - 1.0) / 3.0; - ghoul_assert(std::fmod(n_seg, 1.0) == 0, "A vector containing 3n + 1 control points must be provided!"); + double nSeg = (n - 1.0) / 3.0; + ghoul_assert(std::fmod(nSeg, 1.0) == 0, "A vector containing 3n + 1 control points must be provided!"); // for control points equally spaced in time - double t_seg = std::fmod(t * n_seg, 1.0); - t_seg = std::max(0.0, std::min(t_seg, 1.0)); + double tSeg = std::fmod(t * nSeg, 1.0); + tSeg = std::max(0.0, std::min(tSeg, 1.0)); - size_t idx = std::floor(t * n_seg) * 3; + size_t idx = std::floor(t * nSeg) * 3; - return cubicBezier(t_seg, controlPoints[idx], controlPoints[idx + 1], + return cubicBezier(tSeg, controlPoints[idx], controlPoints[idx + 1], controlPoints[idx + 2], controlPoints[idx + 3]); } @@ -119,15 +119,15 @@ namespace openspace::autonavigation::interpolator { size_t n = controlPoints.size(); ghoul_assert(n > 2, "Minimum of two control points needed for interpolation!"); - size_t n_seg = n - 1; + size_t nSeg = n - 1; // for control points equally spaced in time - double t_seg = std::fmod( t*n_seg, 1.0 ); - t_seg = std::max(0.0, std::min(t_seg, 1.0)); + double tSeg = std::fmod( t*nSeg, 1.0 ); + tSeg = std::max(0.0, std::min(tSeg, 1.0)); - size_t idx = std::floor(t*n_seg); + size_t idx = std::floor(t*nSeg); - return (1.0 - t_seg) * controlPoints[idx] + t_seg * controlPoints[idx + 1]; + return (1.0 - tSeg) * controlPoints[idx] + tSeg * controlPoints[idx + 1]; } } // interpolator From 1b2fb0d06a2cc0a7356cea377ba8db97e9f7355d Mon Sep 17 00:00:00 2001 From: Emma Broman Date: Tue, 14 Jan 2020 09:25:07 -0500 Subject: [PATCH 005/912] Minor cleanup Compute paht duration on the fly rather than storing a variable --- modules/autonavigation/autonavigationhandler.cpp | 14 ++++++-------- modules/autonavigation/autonavigationhandler.h | 1 - 2 files changed, 6 insertions(+), 9 deletions(-) diff --git a/modules/autonavigation/autonavigationhandler.cpp b/modules/autonavigation/autonavigationhandler.cpp index 5b776a9914..3b78b21eb4 100644 --- a/modules/autonavigation/autonavigationhandler.cpp +++ b/modules/autonavigation/autonavigationhandler.cpp @@ -55,7 +55,11 @@ Camera* AutoNavigationHandler::camera() const { } const double AutoNavigationHandler::pathDuration() const { - return _pathDuration; + double sum = 0.0; + for (auto ps : _pathSegments) { + sum += ps.duration; + } + return sum; } PathSegment& AutoNavigationHandler::currentPathSegment() { @@ -97,7 +101,7 @@ void AutoNavigationHandler::updateCamera(double deltaTime) { _currentTime += deltaTime; // reached the end of the path => stop playing - if (_currentTime > _pathDuration) { + if (_currentTime > pathDuration()) { _isPlaying = false; // TODO: implement suitable stop behaviour } @@ -124,17 +128,11 @@ void AutoNavigationHandler::createPath(PathSpecification& spec) { void AutoNavigationHandler::clearPath() { _pathSegments.clear(); - _pathDuration = 0.0; _currentTime = 0.0; } void AutoNavigationHandler::startPath() { ghoul_assert(!_pathSegments.empty(), "Cannot start an empty path"); - - _pathDuration = 0.0; - for (auto ps : _pathSegments) { - _pathDuration += ps.duration; - } _currentTime = 0.0; _isPlaying = true; } diff --git a/modules/autonavigation/autonavigationhandler.h b/modules/autonavigation/autonavigationhandler.h index 3d4260e063..45addaf075 100644 --- a/modules/autonavigation/autonavigationhandler.h +++ b/modules/autonavigation/autonavigationhandler.h @@ -81,7 +81,6 @@ private: std::vector _pathSegments; - double _pathDuration; double _currentTime; bool _isPlaying = false; }; From 76ed9f594d47cebebc91c9fc2c37297784963691 Mon Sep 17 00:00:00 2001 From: Emma Broman Date: Tue, 14 Jan 2020 15:08:32 -0500 Subject: [PATCH 006/912] Add first prototype of pause/continue behavior --- .../autonavigation/autonavigationhandler.cpp | 81 ++++++++++++++----- .../autonavigation/autonavigationhandler.h | 11 ++- .../autonavigation/autonavigationmodule.cpp | 7 ++ .../autonavigationmodule_lua.inl | 10 +++ 4 files changed, 85 insertions(+), 24 deletions(-) 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"); From 2810bbdde2f1f176338a1ed449cec708e07e98ac Mon Sep 17 00:00:00 2001 From: Emma Broman Date: Tue, 14 Jan 2020 17:17:01 -0500 Subject: [PATCH 007/912] Add possiblity to add pauses in path spec (just a duration) --- modules/autonavigation/autonavigationhandler.cpp | 5 +++++ modules/autonavigation/autonavigationhandler.h | 2 +- modules/autonavigation/pathspecification.cpp | 9 +++++++++ modules/autonavigation/pathspecification.h | 9 ++++++++- 4 files changed, 23 insertions(+), 2 deletions(-) diff --git a/modules/autonavigation/autonavigationhandler.cpp b/modules/autonavigation/autonavigationhandler.cpp index 75505c1b98..e1b36722aa 100644 --- a/modules/autonavigation/autonavigationhandler.cpp +++ b/modules/autonavigation/autonavigationhandler.cpp @@ -211,6 +211,11 @@ bool AutoNavigationHandler::handleInstruction(const Instruction& instruction, in ); break; + case InstructionType::Pause: + endState = startState; + success = true; + break; + default: LERROR(fmt::format("Non-implemented instruction type: {}.", instruction.type)); success = false; diff --git a/modules/autonavigation/autonavigationhandler.h b/modules/autonavigation/autonavigationhandler.h index d36f832cac..885ca65fbf 100644 --- a/modules/autonavigation/autonavigationhandler.h +++ b/modules/autonavigation/autonavigationhandler.h @@ -87,7 +87,7 @@ private: bool _isPlaying = false; int _activeSegmentIndex = 0; - bool _stopAtTargets = true; // TODO: ask if this should be a setting for the module or the path + bool _stopAtTargets = false; // TODO: ask if this should be a setting for the module or the path }; } // namespace openspace::autonavigation diff --git a/modules/autonavigation/pathspecification.cpp b/modules/autonavigation/pathspecification.cpp index 9fbaea5c05..a77fedf0f3 100644 --- a/modules/autonavigation/pathspecification.cpp +++ b/modules/autonavigation/pathspecification.cpp @@ -139,10 +139,15 @@ NavigationStateInstructionProps::NavigationStateInstructionProps( } } +PauseInstructionProps::PauseInstructionProps(const ghoul::Dictionary& dictionary) + : InstructionProps(dictionary) +{ } + Instruction::Instruction(const ghoul::Dictionary& dictionary) { // TODO: test against some documentation? + // Deduce the instruction type based on the fields in the dictionary if (dictionary.hasValue(KeyTarget)) { type = InstructionType::TargetNode; props = std::make_shared(dictionary); @@ -151,6 +156,10 @@ Instruction::Instruction(const ghoul::Dictionary& dictionary) { type = InstructionType::NavigationState; props = std::make_shared(dictionary); } + else if (dictionary.hasValue(KeyDuration)) { + type = InstructionType::Pause; + props = std::make_shared(dictionary); + } else { throw ghoul::RuntimeError( "Could not deduce instruction type." diff --git a/modules/autonavigation/pathspecification.h b/modules/autonavigation/pathspecification.h index 363d85785e..cbf679c1dd 100644 --- a/modules/autonavigation/pathspecification.h +++ b/modules/autonavigation/pathspecification.h @@ -32,7 +32,7 @@ namespace openspace::autonavigation { -enum InstructionType { TargetNode, NavigationState }; +enum InstructionType { TargetNode, NavigationState, Pause }; struct InstructionProps { InstructionProps() = default; @@ -58,6 +58,13 @@ struct NavigationStateInstructionProps : public InstructionProps { NavigationState navState; }; +struct PauseInstructionProps : public InstructionProps { + PauseInstructionProps(const ghoul::Dictionary& dictionary); + + // For now, a pause instruction does not have any special props. + // Might be added later +}; + struct Instruction { Instruction() = default; Instruction(const ghoul::Dictionary& dictionary); From 6213d8713da31811e27d378a202047b0223a1a05 Mon Sep 17 00:00:00 2001 From: Emma Broman Date: Wed, 15 Jan 2020 10:55:04 -0500 Subject: [PATCH 008/912] read stop at targets setting from path spec --- .../autonavigation/autonavigationhandler.cpp | 4 +++- modules/autonavigation/autonavigationhandler.h | 2 +- modules/autonavigation/pathspecification.cpp | 18 ++++++++++++++++++ modules/autonavigation/pathspecification.h | 2 ++ 4 files changed, 24 insertions(+), 2 deletions(-) diff --git a/modules/autonavigation/autonavigationhandler.cpp b/modules/autonavigation/autonavigationhandler.cpp index e1b36722aa..66a9328063 100644 --- a/modules/autonavigation/autonavigationhandler.cpp +++ b/modules/autonavigation/autonavigationhandler.cpp @@ -140,7 +140,8 @@ void AutoNavigationHandler::createPath(PathSpecification& spec) { break; } - // TODO: set stop at target variable based on spec + // OBS! Would it be better to save the spec in the handler class? + _stopAtTargets = spec.stopAtTargets(); if (success) { LINFO("Succefully generated camera path."); @@ -213,6 +214,7 @@ bool AutoNavigationHandler::handleInstruction(const Instruction& instruction, in case InstructionType::Pause: endState = startState; + // TODO: implement more complex behavior later success = true; break; diff --git a/modules/autonavigation/autonavigationhandler.h b/modules/autonavigation/autonavigationhandler.h index 885ca65fbf..0cbbaa90b7 100644 --- a/modules/autonavigation/autonavigationhandler.h +++ b/modules/autonavigation/autonavigationhandler.h @@ -87,7 +87,7 @@ private: bool _isPlaying = false; int _activeSegmentIndex = 0; - bool _stopAtTargets = false; // TODO: ask if this should be a setting for the module or the path + bool _stopAtTargets; }; } // namespace openspace::autonavigation diff --git a/modules/autonavigation/pathspecification.cpp b/modules/autonavigation/pathspecification.cpp index a77fedf0f3..73df55beb3 100644 --- a/modules/autonavigation/pathspecification.cpp +++ b/modules/autonavigation/pathspecification.cpp @@ -31,6 +31,7 @@ namespace { constexpr const char* _loggerCat = "PathInstruction"; constexpr const char* KeyInstructions = "Instructions"; + constexpr const char* KeyStopAtTargets = "StopAtTargets"; constexpr const char* KeyTarget = "Target"; constexpr const char* KeyDuration = "Duration"; @@ -180,6 +181,7 @@ PathSpecification::PathSpecification(const ghoul::Dictionary& dictionary) { return; } + // Read instructions from dictionary ghoul::Dictionary instructions = dictionary.value(KeyInstructions); for (size_t i = 1; i <= instructions.size(); ++i) { @@ -187,16 +189,26 @@ PathSpecification::PathSpecification(const ghoul::Dictionary& dictionary) { _instructions.push_back(Instruction{ insDict }); } + + // Read stop at targets flag + if (dictionary.hasValue(KeyStopAtTargets)) { + _stopAtTargets = dictionary.value(KeyStopAtTargets); + } } PathSpecification::PathSpecification(const Instruction instruction) { _instructions.push_back(instruction); + _stopAtTargets = false; } const std::vector* PathSpecification::instructions() const { return &_instructions; } +const bool PathSpecification::stopAtTargets() const { + return _stopAtTargets; +} + documentation::Documentation PathSpecification::Documentation() { using namespace documentation; @@ -210,6 +222,12 @@ documentation::Documentation PathSpecification::Documentation() { Optional::No, "A list of path instructions." }, + { + KeyStopAtTargets, + new BoolVerifier, + Optional::Yes, + "A bool that decides whether we should pause when reaching a target when playing a path." + }, } }; } diff --git a/modules/autonavigation/pathspecification.h b/modules/autonavigation/pathspecification.h index cbf679c1dd..8d20eac7f9 100644 --- a/modules/autonavigation/pathspecification.h +++ b/modules/autonavigation/pathspecification.h @@ -84,9 +84,11 @@ public: // Accessors const std::vector* instructions() const; + const bool stopAtTargets() const; private: std::vector _instructions; + bool _stopAtTargets = false; // default is tp play the entire path without stops // TODO: maxSpeed or speedFactor or something? }; From d4c1bb519945fd1019c19df8b6d26e76463fc10c Mon Sep 17 00:00:00 2001 From: Emma Broman Date: Wed, 15 Jan 2020 11:47:08 -0500 Subject: [PATCH 009/912] Remove risk of access violation and add a comment --- modules/autonavigation/autonavigationhandler.cpp | 3 +-- modules/autonavigation/autonavigationhandler.h | 1 + 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/modules/autonavigation/autonavigationhandler.cpp b/modules/autonavigation/autonavigationhandler.cpp index 66a9328063..1ed7804b5e 100644 --- a/modules/autonavigation/autonavigationhandler.cpp +++ b/modules/autonavigation/autonavigationhandler.cpp @@ -191,8 +191,7 @@ void AutoNavigationHandler::continuePath() { bool AutoNavigationHandler::handleInstruction(const Instruction& instruction, int index) { - CameraState& currentLast = _pathSegments.back().end; - CameraState startState = _pathSegments.empty() ? currentCameraState() : currentLast; + CameraState startState = _pathSegments.empty() ? currentCameraState() : _pathSegments.back().end; CameraState endState; double duration, startTime; diff --git a/modules/autonavigation/autonavigationhandler.h b/modules/autonavigation/autonavigationhandler.h index 0cbbaa90b7..a089adb6bc 100644 --- a/modules/autonavigation/autonavigationhandler.h +++ b/modules/autonavigation/autonavigationhandler.h @@ -81,6 +81,7 @@ private: glm::dvec3 computeTargetPositionAtNode(const SceneGraphNode* node, glm::dvec3 prevPos, double height); + // This list essentially represents the camera path std::vector _pathSegments; double _currentTime; From d1b3e4e7ce07c343d67471004092d564be7f2030 Mon Sep 17 00:00:00 2001 From: Emma Broman Date: Wed, 15 Jan 2020 14:12:04 -0500 Subject: [PATCH 010/912] cleanup path segment creation and move duration default to PathSegment --- .../autonavigation/autonavigationhandler.cpp | 135 +++++++++++------- .../autonavigation/autonavigationhandler.h | 6 +- modules/autonavigation/pathsegment.cpp | 11 +- modules/autonavigation/pathsegment.h | 5 +- 4 files changed, 101 insertions(+), 56 deletions(-) diff --git a/modules/autonavigation/autonavigationhandler.cpp b/modules/autonavigation/autonavigationhandler.cpp index 1d65d20e98..bc6d9f9e78 100644 --- a/modules/autonavigation/autonavigationhandler.cpp +++ b/modules/autonavigation/autonavigationhandler.cpp @@ -188,30 +188,27 @@ void AutoNavigationHandler::continuePath() { bool AutoNavigationHandler::handleInstruction(const Instruction& instruction, int index) { - CameraState startState = _pathSegments.empty() ? currentCameraState() : _pathSegments.back().end(); + // compute startTime + double startTime = 0.0; + if (!_pathSegments.empty()) { + PathSegment& last = _pathSegments.back(); + startTime = last.startTime() + last.duration(); + } - CameraState endState; - double duration, startTime; bool success = true; switch (instruction.type) { case InstructionType::TargetNode: - success = endFromTargetNodeInstruction( - endState, startState, instruction, index - ); + success = handleTargetNodeInstruction(instruction, index, startTime); break; case InstructionType::NavigationState: - success = endFromNavigationStateInstruction( - endState, instruction, index - ); + success = handleNavigationStateInstruction(instruction, index, startTime); break; case InstructionType::Pause: - endState = startState; - // TODO: implement more complex behavior later - success = true; + success = handlePauseInstruction(instruction, index, startTime); break; default: @@ -222,37 +219,13 @@ bool AutoNavigationHandler::handleInstruction(const Instruction& instruction, in if (!success) return false; - // compute duration - if (instruction.props->duration.has_value()) { - duration = instruction.props->duration.value(); - if (duration <= 0) { - LERROR(fmt::format("Failed creating path segment number {}. Duration can not be negative.", index + 1)); - return false; - } - } - else { - // TODO: compute default duration - duration = 5.0; - } - - // compute startTime - startTime = 0.0; - if (!_pathSegments.empty()) { - PathSegment& last = _pathSegments.back(); - startTime = last.startTime() + last.duration(); - } - - // create new path - _pathSegments.push_back( - PathSegment{ startState, endState, duration, startTime } - ); - return true; } -bool AutoNavigationHandler::endFromTargetNodeInstruction( - CameraState& endState, CameraState& prevState, const Instruction& instruction, int index) +bool AutoNavigationHandler::handleTargetNodeInstruction( + const Instruction& instruction, int index, double startTime) { + // Verify instruction type TargetNodeInstructionProps* props = dynamic_cast(instruction.props.get()); @@ -261,11 +234,18 @@ bool AutoNavigationHandler::endFromTargetNodeInstruction( return false; } + CameraState startState = + _pathSegments.empty() ? currentCameraState() : _pathSegments.back().end(); + // Compute end state std::string& identifier = props->targetNode; const SceneGraphNode* targetNode = sceneGraphNode(identifier); if (!targetNode) { - LERROR(fmt::format("Failed handling instruction number {}. Could not find node '{}' to target", index + 1, identifier)); + LERROR(fmt::format( + "Failed handling instruction number {}. Could not find node '{}' to target", + index + 1, + identifier) + ); return false; } @@ -273,7 +253,8 @@ bool AutoNavigationHandler::endFromTargetNodeInstruction( if (props->position.has_value()) { // note that the anchor and reference frame is our targetnode. // The position in instruction is given is relative coordinates. - targetPos = targetNode->worldPosition() + targetNode->worldRotationMatrix() * props->position.value(); + targetPos = targetNode->worldPosition() + + targetNode->worldRotationMatrix() * props->position.value(); } else { bool hasHeight = props->height.has_value(); @@ -284,7 +265,7 @@ bool AutoNavigationHandler::endFromTargetNodeInstruction( targetPos = computeTargetPositionAtNode( targetNode, - prevState.position, + startState.position, height ); } @@ -297,30 +278,49 @@ bool AutoNavigationHandler::endFromTargetNodeInstruction( glm::dquat targetRot = glm::normalize(glm::inverse(glm::quat_cast(lookAtMat))); - endState = CameraState{ targetPos, targetRot, identifier }; + CameraState endState = CameraState{ targetPos, targetRot, identifier }; + + // TODO: Handle duration better + PathSegment newSegment{ startState, endState, startTime }; + if (instruction.props->duration.has_value()) { + newSegment.setDuration(instruction.props->duration.value()); + } + _pathSegments.push_back(newSegment); return true; } -bool AutoNavigationHandler::endFromNavigationStateInstruction( - CameraState& endState, const Instruction& instruction, int index) +bool AutoNavigationHandler::handleNavigationStateInstruction( + const Instruction& instruction, int index, double startTime) { + // Verify instruction type NavigationStateInstructionProps* props = dynamic_cast(instruction.props.get()); if (!props) { - LERROR(fmt::format("Could not handle navigation state instruction (number {}).", index + 1)); + LERROR(fmt::format( + "Could not handle navigation state instruction (number {}).", + index + 1) + ); return false; } + CameraState startState = + _pathSegments.empty() ? currentCameraState() : _pathSegments.back().end(); + interaction::NavigationHandler::NavigationState ns = props->navState; - // OBS! The following code is exactly the same as used in NavigationHandler::applyNavigationState. Should probably be made into a function. + // OBS! The following code is exactly the same as used in + // NavigationHandler::applyNavigationState. Should probably be made into a function. const SceneGraphNode* referenceFrame = sceneGraphNode(ns.referenceFrame); const SceneGraphNode* anchorNode = sceneGraphNode(ns.anchor); // The anchor is also the target if (!anchorNode) { - LERROR(fmt::format("Failed handling instruction number {}. Could not find node '{}' to target", index + 1, ns.anchor)); + LERROR(fmt::format( + "Failed handling instruction number {}. Could not find node '{}' to target", + index + 1, + ns.anchor) + ); return false; } @@ -349,7 +349,44 @@ bool AutoNavigationHandler::endFromNavigationStateInstruction( glm::quat targetRotation = neutralCameraRotation * yawRotation * pitchRotation; - endState = CameraState{ targetPositionWorld, targetRotation, ns.anchor }; + CameraState endState = CameraState{ targetPositionWorld, targetRotation, ns.anchor }; + + // TODO: Handle duration better + PathSegment newSegment{ startState, endState, startTime }; + if (instruction.props->duration.has_value()) { + newSegment.setDuration(instruction.props->duration.value()); + } + _pathSegments.push_back(newSegment); + + return true; +} + +bool AutoNavigationHandler::handlePauseInstruction( + const Instruction& instruction, int index, double startTime) +{ + // Verify instruction type + PauseInstructionProps* props = + dynamic_cast(instruction.props.get()); + + if (!props) { + LERROR(fmt::format("Could not handle pause instruction (number {}).", index + 1)); + return false; + } + + CameraState startState = + _pathSegments.empty() ? currentCameraState() : _pathSegments.back().end(); + + CameraState endState = startState; + + // TODO: implement more complex behavior later + + // TODO: Handle duration better + PathSegment newSegment{ startState, endState, startTime }; + if (instruction.props->duration.has_value()) { + newSegment.setDuration(instruction.props->duration.value()); + } + _pathSegments.push_back(newSegment); + return true; } diff --git a/modules/autonavigation/autonavigationhandler.h b/modules/autonavigation/autonavigationhandler.h index 299764e598..03ef2bcfe2 100644 --- a/modules/autonavigation/autonavigationhandler.h +++ b/modules/autonavigation/autonavigationhandler.h @@ -64,9 +64,9 @@ public: private: bool handleInstruction(const Instruction& instruction, int index); - bool endFromTargetNodeInstruction(CameraState& endState, CameraState& prevState, const Instruction& instruction, int index); - - bool endFromNavigationStateInstruction(CameraState& endState, const Instruction& instruction, int index); + bool handleTargetNodeInstruction(const Instruction& instruction, int index, double startTime); + bool handleNavigationStateInstruction(const Instruction& instruction, int index, double startTime); + bool handlePauseInstruction(const Instruction& instruction, int index, double startTime); glm::dvec3 computeTargetPositionAtNode(const SceneGraphNode* node, glm::dvec3 prevPos, double height); diff --git a/modules/autonavigation/pathsegment.cpp b/modules/autonavigation/pathsegment.cpp index f8f23de200..df04e4eee8 100644 --- a/modules/autonavigation/pathsegment.cpp +++ b/modules/autonavigation/pathsegment.cpp @@ -40,9 +40,12 @@ namespace { namespace openspace::autonavigation { PathSegment::PathSegment( - CameraState start, CameraState end, double duration, double startTime, CurveType type) - : _start(start), _end(end), _duration(duration), _startTime(startTime), _curveType(type) + CameraState start, CameraState end, double startTime, CurveType type) + : _start(start), _end(end), _startTime(startTime), _curveType(type) { + // TODO: compute duration based on method later + _duration = 5; + switch(_curveType) { case Bezier: generateBezier(); @@ -64,6 +67,10 @@ void PathSegment::setStart(CameraState cs) { _start = std::move(cs); } +void PathSegment::setDuration(double d) { + _duration = d; +} + const CameraState PathSegment::start() const { return _start; } const CameraState PathSegment::end() const { return _end; } diff --git a/modules/autonavigation/pathsegment.h b/modules/autonavigation/pathsegment.h index 8868a05b54..23b3b75f2d 100644 --- a/modules/autonavigation/pathsegment.h +++ b/modules/autonavigation/pathsegment.h @@ -45,10 +45,11 @@ enum CurveType { class PathSegment { public: - PathSegment(CameraState start, CameraState end, double duration, double startTime, CurveType type = Bezier); + PathSegment(CameraState start, CameraState end, double startTime, CurveType type = Bezier); // Mutators void setStart(CameraState cs); + void setDuration(double d); // Accessors const CameraState start() const; @@ -67,7 +68,7 @@ private: CameraState _start; CameraState _end; - double _duration; + double _duration; double _startTime; CurveType _curveType; std::vector _controlPoints; From 42c797093adebd743403a1641fc03fcfabc5f30c Mon Sep 17 00:00:00 2001 From: Emma Broman Date: Wed, 15 Jan 2020 14:23:28 -0500 Subject: [PATCH 011/912] minor cleanup --- .../autonavigation/autonavigationhandler.cpp | 38 ++++++++----------- .../autonavigation/autonavigationhandler.h | 6 +-- 2 files changed, 18 insertions(+), 26 deletions(-) diff --git a/modules/autonavigation/autonavigationhandler.cpp b/modules/autonavigation/autonavigationhandler.cpp index bc6d9f9e78..5de0772d01 100644 --- a/modules/autonavigation/autonavigationhandler.cpp +++ b/modules/autonavigation/autonavigationhandler.cpp @@ -200,15 +200,15 @@ bool AutoNavigationHandler::handleInstruction(const Instruction& instruction, in switch (instruction.type) { case InstructionType::TargetNode: - success = handleTargetNodeInstruction(instruction, index, startTime); + success = handleTargetNodeInstruction(instruction, startTime); break; case InstructionType::NavigationState: - success = handleNavigationStateInstruction(instruction, index, startTime); + success = handleNavigationStateInstruction(instruction, startTime); break; case InstructionType::Pause: - success = handlePauseInstruction(instruction, index, startTime); + success = handlePauseInstruction(instruction, startTime); break; default: @@ -217,20 +217,23 @@ bool AutoNavigationHandler::handleInstruction(const Instruction& instruction, in break; } - if (!success) return false; + if (!success) { + LERROR(fmt::format("Failed handling instruction number {}.", index + 1)); + return false; + } return true; } bool AutoNavigationHandler::handleTargetNodeInstruction( - const Instruction& instruction, int index, double startTime) + const Instruction& instruction, double startTime) { // Verify instruction type TargetNodeInstructionProps* props = dynamic_cast(instruction.props.get()); if (!props) { - LERROR(fmt::format("Could not handle target node instruction (number {}).", index + 1)); + LERROR("Could not handle target node instruction."); return false; } @@ -241,11 +244,7 @@ bool AutoNavigationHandler::handleTargetNodeInstruction( std::string& identifier = props->targetNode; const SceneGraphNode* targetNode = sceneGraphNode(identifier); if (!targetNode) { - LERROR(fmt::format( - "Failed handling instruction number {}. Could not find node '{}' to target", - index + 1, - identifier) - ); + LERROR(fmt::format("Could not find node '{}' to target", identifier)); return false; } @@ -291,17 +290,14 @@ bool AutoNavigationHandler::handleTargetNodeInstruction( } bool AutoNavigationHandler::handleNavigationStateInstruction( - const Instruction& instruction, int index, double startTime) + const Instruction& instruction, double startTime) { // Verify instruction type NavigationStateInstructionProps* props = dynamic_cast(instruction.props.get()); if (!props) { - LERROR(fmt::format( - "Could not handle navigation state instruction (number {}).", - index + 1) - ); + LERROR(fmt::format("Could not handle navigation state instruction.")); return false; } @@ -316,11 +312,7 @@ bool AutoNavigationHandler::handleNavigationStateInstruction( const SceneGraphNode* anchorNode = sceneGraphNode(ns.anchor); // The anchor is also the target if (!anchorNode) { - LERROR(fmt::format( - "Failed handling instruction number {}. Could not find node '{}' to target", - index + 1, - ns.anchor) - ); + LERROR(fmt::format("Could not find node '{}' to target", ns.anchor)); return false; } @@ -362,14 +354,14 @@ bool AutoNavigationHandler::handleNavigationStateInstruction( } bool AutoNavigationHandler::handlePauseInstruction( - const Instruction& instruction, int index, double startTime) + const Instruction& instruction, double startTime) { // Verify instruction type PauseInstructionProps* props = dynamic_cast(instruction.props.get()); if (!props) { - LERROR(fmt::format("Could not handle pause instruction (number {}).", index + 1)); + LERROR(fmt::format("Could not handle pause instruction.")); return false; } diff --git a/modules/autonavigation/autonavigationhandler.h b/modules/autonavigation/autonavigationhandler.h index 03ef2bcfe2..dd94127569 100644 --- a/modules/autonavigation/autonavigationhandler.h +++ b/modules/autonavigation/autonavigationhandler.h @@ -64,9 +64,9 @@ public: private: bool handleInstruction(const Instruction& instruction, int index); - bool handleTargetNodeInstruction(const Instruction& instruction, int index, double startTime); - bool handleNavigationStateInstruction(const Instruction& instruction, int index, double startTime); - bool handlePauseInstruction(const Instruction& instruction, int index, double startTime); + bool handleTargetNodeInstruction(const Instruction& instruction, double startTime); + bool handleNavigationStateInstruction(const Instruction& instruction, double startTime); + bool handlePauseInstruction(const Instruction& instruction, double startTime); glm::dvec3 computeTargetPositionAtNode(const SceneGraphNode* node, glm::dvec3 prevPos, double height); From 83037f8c4718c458926fd07282747e9f4fb0274e Mon Sep 17 00:00:00 2001 From: Emma Broman Date: Wed, 15 Jan 2020 18:16:24 -0500 Subject: [PATCH 012/912] Refactor path curve generation in PathSegment --- .../autonavigation/autonavigationhandler.cpp | 3 +- modules/autonavigation/pathsegment.cpp | 177 +++++++++--------- modules/autonavigation/pathsegment.h | 46 ++++- 3 files changed, 133 insertions(+), 93 deletions(-) diff --git a/modules/autonavigation/autonavigationhandler.cpp b/modules/autonavigation/autonavigationhandler.cpp index 5de0772d01..3815de8d1d 100644 --- a/modules/autonavigation/autonavigationhandler.cpp +++ b/modules/autonavigation/autonavigationhandler.cpp @@ -56,7 +56,7 @@ Camera* AutoNavigationHandler::camera() const { const double AutoNavigationHandler::pathDuration() const { double sum = 0.0; - for (auto ps : _pathSegments) { + for (const PathSegment& ps : _pathSegments) { sum += ps.duration(); } return sum; @@ -66,7 +66,6 @@ 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]; diff --git a/modules/autonavigation/pathsegment.cpp b/modules/autonavigation/pathsegment.cpp index df04e4eee8..69a18adf94 100644 --- a/modules/autonavigation/pathsegment.cpp +++ b/modules/autonavigation/pathsegment.cpp @@ -39,6 +39,93 @@ namespace { namespace openspace::autonavigation { +// TODO: move all these later -------------------- +PathCurve::~PathCurve() {} + +BezierCurve::BezierCurve(CameraState& start, CameraState& end) { + glm::dvec3 startNodePos = sceneGraphNode(start.referenceNode)->worldPosition(); + glm::dvec3 endNodePos = sceneGraphNode(start.referenceNode)->worldPosition(); + // vectors pointing away from target nodes + glm::dvec3 startDirection = start.position - startNodePos; + glm::dvec3 endDirection = end.position - endNodePos; + + _points.push_back(start.position); + _points.push_back(start.position + 10.0 * startDirection); + _points.push_back(end.position + 10.0 * endDirection); + _points.push_back(end.position); +} + +glm::dvec3 BezierCurve::interpolate(double t) { + return interpolator::cubicBezier(t, + _points[0], _points[1], _points[2], _points[3]); +} + +Bezier2Curve::Bezier2Curve(CameraState& start, CameraState& end) { + // START: + glm::dvec3 startNodePos = sceneGraphNode(start.referenceNode)->worldPosition(); + glm::dvec3 startDirection = start.position - startNodePos; + + // END: + glm::dvec3 endNodePos = sceneGraphNode(end.referenceNode)->worldPosition(); + glm::dvec3 endDirection = end.position - endNodePos; + + // MIDDLE: one knot and two control points parallell to target nodes + glm::dvec3 AB = endNodePos - startNodePos; + glm::dvec3 C = normalize(startDirection + endDirection); + glm::dvec3 CparAB = glm::dot(C, normalize(AB))* normalize(AB); + glm::dvec3 CortAB = normalize(C - CparAB); + double d = length(AB); + + // TODO: set points that actually look good + _points.push_back(start.position); + _points.push_back(start.position + 2.0 * startDirection); + + _points.push_back(start.position + 1.5 * d * CortAB); + _points.push_back(start.position + 1.5 * d * CortAB + 0.5 * AB); + _points.push_back(end.position + 1.5 * d * CortAB); + + _points.push_back(end.position + 2.0 * endDirection); + _points.push_back(end.position); +} + +glm::dvec3 Bezier2Curve::interpolate(double t) { + return interpolator::piecewiseCubicBezier(t, _points); +} + +LinearCurve::LinearCurve(CameraState& start, CameraState& end) { + _points.push_back(start.position); + _points.push_back(end.position); +} + +glm::dvec3 LinearCurve::interpolate(double t) { + return ghoul::interpolateLinear(t, _points[0], _points[1]); +} + +Linear2Curve::Linear2Curve(CameraState& start, CameraState& end) { + // START: + glm::dvec3 startNodePos = sceneGraphNode(start.referenceNode)->worldPosition(); + glm::dvec3 startDirection = start.position - startNodePos; + + // END: + glm::dvec3 endNodePos = sceneGraphNode(end.referenceNode)->worldPosition(); + glm::dvec3 endDirection = end.position - endNodePos; + + // MIDDLE: + glm::dvec3 AB = endNodePos - startNodePos; + glm::dvec3 C = normalize(startDirection + endDirection); + glm::dvec3 CparAB = glm::dot(C, normalize(AB))* normalize(AB); + glm::dvec3 CortAB = normalize(C - CparAB); + double d = length(AB); + + _points.push_back(start.position); + _points.push_back(start.position + 2.0 * d * CortAB + 0.5 * AB); //TODO: use scale instead of 2.0 + _points.push_back(end.position); +} + +glm::dvec3 Linear2Curve::interpolate(double t) { + return interpolator::piecewiseLinear(t, _points); +} + PathSegment::PathSegment( CameraState start, CameraState end, double startTime, CurveType type) : _start(start), _end(end), _startTime(startTime), _curveType(type) @@ -46,17 +133,18 @@ PathSegment::PathSegment( // TODO: compute duration based on method later _duration = 5; - switch(_curveType) { + switch(type) { case Bezier: - generateBezier(); + _curve = std::make_shared(start, end); break; case Bezier2: - generateBezier2(); + _curve = std::make_shared(start, end); break; case Linear: + _curve = std::make_shared(start, end); break; case Linear2: - generateLinear2(); + _curve = std::make_shared(start, end); break; default: LERROR(fmt::format("Cannot create curve of type {}. Type does not exist!", _curveType)); @@ -81,24 +169,7 @@ const double PathSegment::startTime() const { return _startTime; } const glm::vec3 PathSegment::getPositionAt(double t) const { t = easingfunctions::cubicEaseInOut(t); - - switch(_curveType) { - case Bezier: - return interpolator::cubicBezier(t, - _controlPoints[0], _controlPoints[1], _controlPoints[2], _controlPoints[3]); - break; - case Bezier2: - return interpolator::piecewiseCubicBezier(t, _controlPoints); - break; - case Linear: - return ghoul::interpolateLinear(t, _start.position, _end.position); - break; - case Linear2: - return interpolator::piecewiseLinear(t, _controlPoints); - break; - default: - LERROR(fmt::format("Cannot get position for curve type {}. Type does not exist!", _curveType)); - } + return _curve.get()->interpolate(t); } const glm::dquat PathSegment::getRotationAt(double t) const { @@ -134,66 +205,4 @@ const glm::dquat PathSegment::getLookAtRotation( return glm::normalize(glm::inverse(glm::quat_cast(lookAtMat))); } -// A single bezier segment with four control points -void PathSegment::generateBezier() { - glm::dvec3 startNodePos = sceneGraphNode(_start.referenceNode)->worldPosition(); - glm::dvec3 endNodePos = sceneGraphNode(_end.referenceNode)->worldPosition(); - // vectors pointing away from target nodes - glm::dvec3 startDirection =_start.position - startNodePos; - glm::dvec3 endDirection = _end.position - endNodePos; - - _controlPoints.push_back(_start.position); - _controlPoints.push_back(_start.position + 10.0 * startDirection); - _controlPoints.push_back(_end.position + 10.0 * endDirection); - _controlPoints.push_back(_end.position); -} - -void PathSegment::generateBezier2() { - // START: - glm::dvec3 startNodePos = sceneGraphNode(_start.referenceNode)->worldPosition(); - glm::dvec3 startDirection = _start.position - startNodePos; - - // END: - glm::dvec3 endNodePos = sceneGraphNode(_end.referenceNode)->worldPosition(); - glm::dvec3 endDirection = _end.position - endNodePos; - - // MIDDLE: one knot and two control points parallell to target nodes - glm::dvec3 AB = endNodePos - startNodePos; - glm::dvec3 C = normalize(startDirection + endDirection); - glm::dvec3 CparAB = glm::dot(C, normalize(AB))* normalize(AB); - glm::dvec3 CortAB = normalize(C - CparAB); - double d = length(AB); - - // TODO: set points that actually look good - _controlPoints.push_back(_start.position); - _controlPoints.push_back(_start.position + 2.0 * startDirection); - - _controlPoints.push_back(_start.position + 1.5 * d * CortAB); - _controlPoints.push_back(_start.position + 1.5 * d * CortAB + 0.5 * AB); - _controlPoints.push_back(_end.position + 1.5 * d * CortAB); - - _controlPoints.push_back(_end.position + 2.0 * endDirection); - _controlPoints.push_back(_end.position); -} - -void PathSegment::generateLinear2() { - // START: - glm::dvec3 startNodePos = sceneGraphNode(_start.referenceNode)->worldPosition(); - glm::dvec3 startDirection = _start.position - startNodePos; - - // END: - glm::dvec3 endNodePos = sceneGraphNode(_end.referenceNode)->worldPosition(); - glm::dvec3 endDirection = _end.position - endNodePos; - - // MIDDLE: - glm::dvec3 AB = endNodePos - startNodePos; - glm::dvec3 C = normalize(startDirection + endDirection); - glm::dvec3 CparAB = glm::dot(C, normalize(AB))* normalize(AB); - glm::dvec3 CortAB = normalize(C - CparAB); - double d = length(AB); - - _controlPoints.push_back(_start.position); - _controlPoints.push_back(_start.position + 2.0 * d * CortAB + 0.5 * AB); //TODO: use scale instead of 2.0 - _controlPoints.push_back(_end.position); -} } // namespace openspace::autonavigation diff --git a/modules/autonavigation/pathsegment.h b/modules/autonavigation/pathsegment.h index 23b3b75f2d..d533f28f86 100644 --- a/modules/autonavigation/pathsegment.h +++ b/modules/autonavigation/pathsegment.h @@ -43,9 +43,44 @@ enum CurveType { Linear2 }; +// TODO: move to their own file +class PathCurve { +public: + virtual ~PathCurve() = 0; + virtual glm::dvec3 interpolate(double t) = 0; +protected: + // the points used for creating the curve (e.g. control points of a Bezier curve) + std::vector _points; +}; + +class BezierCurve : public PathCurve { +public : + BezierCurve(CameraState& start, CameraState& end); + glm::dvec3 interpolate(double t); +}; + +class Bezier2Curve : public PathCurve { +public: + Bezier2Curve(CameraState& start, CameraState& end); + glm::dvec3 interpolate(double t); +}; + +class LinearCurve : public PathCurve { +public: + LinearCurve(CameraState& start, CameraState& end); + glm::dvec3 interpolate(double t); +}; + +class Linear2Curve : public PathCurve { +public: + Linear2Curve(CameraState& start, CameraState& end); + glm::dvec3 interpolate(double t); +}; + class PathSegment { public: - PathSegment(CameraState start, CameraState end, double startTime, CurveType type = Bezier); + PathSegment(CameraState start, CameraState end, double startTime, CurveType type = Linear2); + ~PathSegment() = default; // Mutators void setStart(CameraState cs); @@ -62,16 +97,13 @@ public: const glm::dquat getLookAtRotation(double t, glm::dvec3 currentPos, glm::dvec3 up) const; private: - void generateBezier(); - void generateBezier2(); - void generateLinear2(); - CameraState _start; CameraState _end; double _duration; double _startTime; - CurveType _curveType; - std::vector _controlPoints; + CurveType _curveType; // Ideally, we don't want to need to save this + + std::shared_ptr _curve; // OBS! Does it make more sense to use unique_ptr? However, then PathSegments cannot be copied. }; } // namespace openspace::autonavigation From b15fff68c3abb1b9601bea52f52507692e0f3ea9 Mon Sep 17 00:00:00 2001 From: Emma Broman Date: Wed, 15 Jan 2020 18:39:43 -0500 Subject: [PATCH 013/912] Fix so all paths return a value --- modules/autonavigation/autonavigationhandler.cpp | 3 +++ 1 file changed, 3 insertions(+) diff --git a/modules/autonavigation/autonavigationhandler.cpp b/modules/autonavigation/autonavigationhandler.cpp index 1ed7804b5e..1b5f255b0d 100644 --- a/modules/autonavigation/autonavigationhandler.cpp +++ b/modules/autonavigation/autonavigationhandler.cpp @@ -74,6 +74,7 @@ const int AutoNavigationHandler::currentPathSegmentIndex() const { return i; } } + return -1; } CameraState AutoNavigationHandler::currentCameraState() { @@ -91,6 +92,8 @@ void AutoNavigationHandler::updateCamera(double deltaTime) { const int currentIndex = currentPathSegmentIndex(); + if (currentIndex < 0) return; // no path + if (_stopAtTargets && (currentIndex != _activeSegmentIndex)) { _activeSegmentIndex = currentIndex; pausePath(); From 6a6d2ca9731d40cc61f5699e248d0a998666c1a7 Mon Sep 17 00:00:00 2001 From: Emma Broman Date: Thu, 16 Jan 2020 10:30:13 -0500 Subject: [PATCH 014/912] Split path curve code into separate file --- modules/autonavigation/CMakeLists.txt | 2 + modules/autonavigation/pathcurves.cpp | 126 +++++++++++++++++++++++++ modules/autonavigation/pathcurves.h | 70 ++++++++++++++ modules/autonavigation/pathsegment.cpp | 90 +----------------- modules/autonavigation/pathsegment.h | 34 +------ 5 files changed, 201 insertions(+), 121 deletions(-) create mode 100644 modules/autonavigation/pathcurves.cpp create mode 100644 modules/autonavigation/pathcurves.h diff --git a/modules/autonavigation/CMakeLists.txt b/modules/autonavigation/CMakeLists.txt index 2ec485c136..a208c5c93b 100644 --- a/modules/autonavigation/CMakeLists.txt +++ b/modules/autonavigation/CMakeLists.txt @@ -30,6 +30,7 @@ set(HEADER_FILES ${CMAKE_CURRENT_SOURCE_DIR}/helperfunctions.h ${CMAKE_CURRENT_SOURCE_DIR}/pathspecification.h ${CMAKE_CURRENT_SOURCE_DIR}/pathsegment.h + ${CMAKE_CURRENT_SOURCE_DIR}/pathcurves.h ) source_group("Header Files" FILES ${HEADER_FILES}) @@ -40,6 +41,7 @@ set(SOURCE_FILES ${CMAKE_CURRENT_SOURCE_DIR}/helperfunctions.cpp ${CMAKE_CURRENT_SOURCE_DIR}/pathspecification.cpp ${CMAKE_CURRENT_SOURCE_DIR}/pathsegment.cpp + ${CMAKE_CURRENT_SOURCE_DIR}/pathcurves.cpp ) source_group("Source Files" FILES ${SOURCE_FILES}) diff --git a/modules/autonavigation/pathcurves.cpp b/modules/autonavigation/pathcurves.cpp new file mode 100644 index 0000000000..412ee55cb0 --- /dev/null +++ b/modules/autonavigation/pathcurves.cpp @@ -0,0 +1,126 @@ +/***************************************************************************************** + * * + * OpenSpace * + * * + * Copyright (c) 2014-2019 * + * * + * Permission is hereby granted, free of charge, to any person obtaining a copy of this * + * software and associated documentation files (the "Software"), to deal in the Software * + * without restriction, including without limitation the rights to use, copy, modify, * + * merge, publish, distribute, sublicense, and/or sell copies of the Software, and to * + * permit persons to whom the Software is furnished to do so, subject to the following * + * conditions: * + * * + * The above copyright notice and this permission notice shall be included in all copies * + * or substantial portions of the Software. * + * * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, * + * INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A * + * PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT * + * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF * + * CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE * + * OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. * + ****************************************************************************************/ + +#include + +#include +#include +#include +#include +#include +#include + +namespace { + constexpr const char* _loggerCat = "PathCurve"; +} // namespace + +namespace openspace::autonavigation { + +PathCurve::~PathCurve() {} + +BezierCurve::BezierCurve(CameraState& start, CameraState& end) { + glm::dvec3 startNodePos = sceneGraphNode(start.referenceNode)->worldPosition(); + glm::dvec3 endNodePos = sceneGraphNode(start.referenceNode)->worldPosition(); + // vectors pointing away from target nodes + glm::dvec3 startDirection = start.position - startNodePos; + glm::dvec3 endDirection = end.position - endNodePos; + + _points.push_back(start.position); + _points.push_back(start.position + 10.0 * startDirection); + _points.push_back(end.position + 10.0 * endDirection); + _points.push_back(end.position); +} + +glm::dvec3 BezierCurve::interpolate(double t) { + return interpolator::cubicBezier(t, + _points[0], _points[1], _points[2], _points[3]); +} + +Bezier2Curve::Bezier2Curve(CameraState& start, CameraState& end) { + // START: + glm::dvec3 startNodePos = sceneGraphNode(start.referenceNode)->worldPosition(); + glm::dvec3 startDirection = start.position - startNodePos; + + // END: + glm::dvec3 endNodePos = sceneGraphNode(end.referenceNode)->worldPosition(); + glm::dvec3 endDirection = end.position - endNodePos; + + // MIDDLE: one knot and two control points parallell to target nodes + glm::dvec3 AB = endNodePos - startNodePos; + glm::dvec3 C = normalize(startDirection + endDirection); + glm::dvec3 CparAB = glm::dot(C, normalize(AB))* normalize(AB); + glm::dvec3 CortAB = normalize(C - CparAB); + double d = length(AB); + + // TODO: set points that actually look good + _points.push_back(start.position); + _points.push_back(start.position + 2.0 * startDirection); + + _points.push_back(start.position + 1.5 * d * CortAB); + _points.push_back(start.position + 1.5 * d * CortAB + 0.5 * AB); + _points.push_back(end.position + 1.5 * d * CortAB); + + _points.push_back(end.position + 2.0 * endDirection); + _points.push_back(end.position); +} + +glm::dvec3 Bezier2Curve::interpolate(double t) { + return interpolator::piecewiseCubicBezier(t, _points); +} + +LinearCurve::LinearCurve(CameraState& start, CameraState& end) { + _points.push_back(start.position); + _points.push_back(end.position); +} + +glm::dvec3 LinearCurve::interpolate(double t) { + return ghoul::interpolateLinear(t, _points[0], _points[1]); +} + +Linear2Curve::Linear2Curve(CameraState& start, CameraState& end) { + // START: + glm::dvec3 startNodePos = sceneGraphNode(start.referenceNode)->worldPosition(); + glm::dvec3 startDirection = start.position - startNodePos; + + // END: + glm::dvec3 endNodePos = sceneGraphNode(end.referenceNode)->worldPosition(); + glm::dvec3 endDirection = end.position - endNodePos; + + // MIDDLE: + glm::dvec3 AB = endNodePos - startNodePos; + glm::dvec3 C = normalize(startDirection + endDirection); + glm::dvec3 CparAB = glm::dot(C, normalize(AB))* normalize(AB); + glm::dvec3 CortAB = normalize(C - CparAB); + double d = length(AB); + + _points.push_back(start.position); + _points.push_back(start.position + 2.0 * d * CortAB + 0.5 * AB); //TODO: use scale instead of 2.0 + _points.push_back(end.position); +} + +glm::dvec3 Linear2Curve::interpolate(double t) { + return interpolator::piecewiseLinear(t, _points); +} + +} // namespace openspace::autonavigation diff --git a/modules/autonavigation/pathcurves.h b/modules/autonavigation/pathcurves.h new file mode 100644 index 0000000000..ecd407eed2 --- /dev/null +++ b/modules/autonavigation/pathcurves.h @@ -0,0 +1,70 @@ +/***************************************************************************************** + * * + * OpenSpace * + * * + * Copyright (c) 2014-2019 * + * * + * Permission is hereby granted, free of charge, to any person obtaining a copy of this * + * software and associated documentation files (the "Software"), to deal in the Software * + * without restriction, including without limitation the rights to use, copy, modify, * + * merge, publish, distribute, sublicense, and/or sell copies of the Software, and to * + * permit persons to whom the Software is furnished to do so, subject to the following * + * conditions: * + * * + * The above copyright notice and this permission notice shall be included in all copies * + * or substantial portions of the Software. * + * * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, * + * INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A * + * PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT * + * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF * + * CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE * + * OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. * + ****************************************************************************************/ + +#ifndef __OPENSPACE_MODULE_AUTONAVIGATION___PATHCURVE___H__ +#define __OPENSPACE_MODULE_AUTONAVIGATION___PATHCURVE___H__ + +#include +#include + +namespace openspace::autonavigation { + +struct CameraState; + +class PathCurve { +public: + virtual ~PathCurve() = 0; + virtual glm::dvec3 interpolate(double t) = 0; +protected: + // the points used for creating the curve (e.g. control points of a Bezier curve) + std::vector _points; +}; + +class BezierCurve : public PathCurve { +public : + BezierCurve(CameraState& start, CameraState& end); + glm::dvec3 interpolate(double t); +}; + +class Bezier2Curve : public PathCurve { +public: + Bezier2Curve(CameraState& start, CameraState& end); + glm::dvec3 interpolate(double t); +}; + +class LinearCurve : public PathCurve { +public: + LinearCurve(CameraState& start, CameraState& end); + glm::dvec3 interpolate(double t); +}; + +class Linear2Curve : public PathCurve { +public: + Linear2Curve(CameraState& start, CameraState& end); + glm::dvec3 interpolate(double t); +}; + +} // namespace openspace::autonavigation + +#endif // __OPENSPACE_MODULE_AUTONAVIGATION___PATHCURVE___H__ diff --git a/modules/autonavigation/pathsegment.cpp b/modules/autonavigation/pathsegment.cpp index 69a18adf94..0aec433300 100644 --- a/modules/autonavigation/pathsegment.cpp +++ b/modules/autonavigation/pathsegment.cpp @@ -25,6 +25,7 @@ #include #include +#include #include #include #include @@ -39,93 +40,6 @@ namespace { namespace openspace::autonavigation { -// TODO: move all these later -------------------- -PathCurve::~PathCurve() {} - -BezierCurve::BezierCurve(CameraState& start, CameraState& end) { - glm::dvec3 startNodePos = sceneGraphNode(start.referenceNode)->worldPosition(); - glm::dvec3 endNodePos = sceneGraphNode(start.referenceNode)->worldPosition(); - // vectors pointing away from target nodes - glm::dvec3 startDirection = start.position - startNodePos; - glm::dvec3 endDirection = end.position - endNodePos; - - _points.push_back(start.position); - _points.push_back(start.position + 10.0 * startDirection); - _points.push_back(end.position + 10.0 * endDirection); - _points.push_back(end.position); -} - -glm::dvec3 BezierCurve::interpolate(double t) { - return interpolator::cubicBezier(t, - _points[0], _points[1], _points[2], _points[3]); -} - -Bezier2Curve::Bezier2Curve(CameraState& start, CameraState& end) { - // START: - glm::dvec3 startNodePos = sceneGraphNode(start.referenceNode)->worldPosition(); - glm::dvec3 startDirection = start.position - startNodePos; - - // END: - glm::dvec3 endNodePos = sceneGraphNode(end.referenceNode)->worldPosition(); - glm::dvec3 endDirection = end.position - endNodePos; - - // MIDDLE: one knot and two control points parallell to target nodes - glm::dvec3 AB = endNodePos - startNodePos; - glm::dvec3 C = normalize(startDirection + endDirection); - glm::dvec3 CparAB = glm::dot(C, normalize(AB))* normalize(AB); - glm::dvec3 CortAB = normalize(C - CparAB); - double d = length(AB); - - // TODO: set points that actually look good - _points.push_back(start.position); - _points.push_back(start.position + 2.0 * startDirection); - - _points.push_back(start.position + 1.5 * d * CortAB); - _points.push_back(start.position + 1.5 * d * CortAB + 0.5 * AB); - _points.push_back(end.position + 1.5 * d * CortAB); - - _points.push_back(end.position + 2.0 * endDirection); - _points.push_back(end.position); -} - -glm::dvec3 Bezier2Curve::interpolate(double t) { - return interpolator::piecewiseCubicBezier(t, _points); -} - -LinearCurve::LinearCurve(CameraState& start, CameraState& end) { - _points.push_back(start.position); - _points.push_back(end.position); -} - -glm::dvec3 LinearCurve::interpolate(double t) { - return ghoul::interpolateLinear(t, _points[0], _points[1]); -} - -Linear2Curve::Linear2Curve(CameraState& start, CameraState& end) { - // START: - glm::dvec3 startNodePos = sceneGraphNode(start.referenceNode)->worldPosition(); - glm::dvec3 startDirection = start.position - startNodePos; - - // END: - glm::dvec3 endNodePos = sceneGraphNode(end.referenceNode)->worldPosition(); - glm::dvec3 endDirection = end.position - endNodePos; - - // MIDDLE: - glm::dvec3 AB = endNodePos - startNodePos; - glm::dvec3 C = normalize(startDirection + endDirection); - glm::dvec3 CparAB = glm::dot(C, normalize(AB))* normalize(AB); - glm::dvec3 CortAB = normalize(C - CparAB); - double d = length(AB); - - _points.push_back(start.position); - _points.push_back(start.position + 2.0 * d * CortAB + 0.5 * AB); //TODO: use scale instead of 2.0 - _points.push_back(end.position); -} - -glm::dvec3 Linear2Curve::interpolate(double t) { - return interpolator::piecewiseLinear(t, _points); -} - PathSegment::PathSegment( CameraState start, CameraState end, double startTime, CurveType type) : _start(start), _end(end), _startTime(startTime), _curveType(type) @@ -169,7 +83,7 @@ const double PathSegment::startTime() const { return _startTime; } const glm::vec3 PathSegment::getPositionAt(double t) const { t = easingfunctions::cubicEaseInOut(t); - return _curve.get()->interpolate(t); + return _curve->interpolate(t); } const glm::dquat PathSegment::getRotationAt(double t) const { diff --git a/modules/autonavigation/pathsegment.h b/modules/autonavigation/pathsegment.h index d533f28f86..6a5771e486 100644 --- a/modules/autonavigation/pathsegment.h +++ b/modules/autonavigation/pathsegment.h @@ -43,39 +43,7 @@ enum CurveType { Linear2 }; -// TODO: move to their own file -class PathCurve { -public: - virtual ~PathCurve() = 0; - virtual glm::dvec3 interpolate(double t) = 0; -protected: - // the points used for creating the curve (e.g. control points of a Bezier curve) - std::vector _points; -}; - -class BezierCurve : public PathCurve { -public : - BezierCurve(CameraState& start, CameraState& end); - glm::dvec3 interpolate(double t); -}; - -class Bezier2Curve : public PathCurve { -public: - Bezier2Curve(CameraState& start, CameraState& end); - glm::dvec3 interpolate(double t); -}; - -class LinearCurve : public PathCurve { -public: - LinearCurve(CameraState& start, CameraState& end); - glm::dvec3 interpolate(double t); -}; - -class Linear2Curve : public PathCurve { -public: - Linear2Curve(CameraState& start, CameraState& end); - glm::dvec3 interpolate(double t); -}; +class PathCurve; class PathSegment { public: From adae43c68fa10a916f45805449b0c9a38131d494 Mon Sep 17 00:00:00 2001 From: Emma Broman Date: Thu, 16 Jan 2020 11:03:51 -0500 Subject: [PATCH 015/912] Update error checks, since ghoul_assert only runs in Debug --- .../autonavigation/autonavigationhandler.cpp | 20 +++++++++++++------ 1 file changed, 14 insertions(+), 6 deletions(-) diff --git a/modules/autonavigation/autonavigationhandler.cpp b/modules/autonavigation/autonavigationhandler.cpp index 1b5f255b0d..1089371661 100644 --- a/modules/autonavigation/autonavigationhandler.cpp +++ b/modules/autonavigation/autonavigationhandler.cpp @@ -162,24 +162,32 @@ void AutoNavigationHandler::clearPath() { } void AutoNavigationHandler::startPath() { - ghoul_assert(!_pathSegments.empty(), "Cannot start an empty path"); + if (_pathSegments.empty()) { + LERROR("Cannot start an empty path."); + return; + } LINFO("Starting path..."); _currentTime = 0.0; _isPlaying = true; } void AutoNavigationHandler::pausePath() { - ghoul_assert(!_isPlaying, "Cannot pause a path that isn't playing"); + if (!_isPlaying) { + LERROR("Cannot pause a path that isn't playing"); + return; + } 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 (_pathSegments.empty() || hasFinished()) { + LERROR("No path to resume (path is empty or has finished)."); + return; + } - if (hasFinished()) { - LERROR("Path has ended, cannot continue."); + if (_isPlaying) { + LERROR("Cannot resume a path that is already playing"); return; } From b9a34cd0f0592c063fb273b742f2479a59aa166c Mon Sep 17 00:00:00 2001 From: Emma Broman Date: Thu, 16 Jan 2020 14:21:50 -0500 Subject: [PATCH 016/912] Fix issues with circular dependencies --- modules/autonavigation/CMakeLists.txt | 1 + modules/autonavigation/camerastate.h | 41 ++++++++++++++++++++++ modules/autonavigation/helperfunctions.cpp | 6 +++- modules/autonavigation/helperfunctions.h | 5 +++ modules/autonavigation/pathcurves.cpp | 4 +-- modules/autonavigation/pathcurves.h | 3 +- modules/autonavigation/pathsegment.cpp | 3 +- modules/autonavigation/pathsegment.h | 7 +--- 8 files changed, 56 insertions(+), 14 deletions(-) create mode 100644 modules/autonavigation/camerastate.h diff --git a/modules/autonavigation/CMakeLists.txt b/modules/autonavigation/CMakeLists.txt index a208c5c93b..cc0390db8c 100644 --- a/modules/autonavigation/CMakeLists.txt +++ b/modules/autonavigation/CMakeLists.txt @@ -27,6 +27,7 @@ include(${OPENSPACE_CMAKE_EXT_DIR}/module_definition.cmake) set(HEADER_FILES ${CMAKE_CURRENT_SOURCE_DIR}/autonavigationmodule.h ${CMAKE_CURRENT_SOURCE_DIR}/autonavigationhandler.h + ${CMAKE_CURRENT_SOURCE_DIR}/camerastate.h ${CMAKE_CURRENT_SOURCE_DIR}/helperfunctions.h ${CMAKE_CURRENT_SOURCE_DIR}/pathspecification.h ${CMAKE_CURRENT_SOURCE_DIR}/pathsegment.h diff --git a/modules/autonavigation/camerastate.h b/modules/autonavigation/camerastate.h new file mode 100644 index 0000000000..0512f8fc7f --- /dev/null +++ b/modules/autonavigation/camerastate.h @@ -0,0 +1,41 @@ +/***************************************************************************************** + * * + * OpenSpace * + * * + * Copyright (c) 2014-2019 * + * * + * Permission is hereby granted, free of charge, to any person obtaining a copy of this * + * software and associated documentation files (the "Software"), to deal in the Software * + * without restriction, including without limitation the rights to use, copy, modify, * + * merge, publish, distribute, sublicense, and/or sell copies of the Software, and to * + * permit persons to whom the Software is furnished to do so, subject to the following * + * conditions: * + * * + * The above copyright notice and this permission notice shall be included in all copies * + * or substantial portions of the Software. * + * * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, * + * INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A * + * PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT * + * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF * + * CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE * + * OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. * + ****************************************************************************************/ + +#ifndef __OPENSPACE_MODULE___CAMERASTATE___H__ +#define __OPENSPACE_MODULE___CAMERASTATE___H__ + +#include +#include + +namespace openspace::autonavigation { + +struct CameraState { + glm::dvec3 position; + glm::dquat rotation; + std::string referenceNode; +}; + +} // namespace openspace::autonavigation + +#endif // __OPENSPACE_MODULE___CAMERASTATE___H__ diff --git a/modules/autonavigation/helperfunctions.cpp b/modules/autonavigation/helperfunctions.cpp index c57845ed77..402594472d 100644 --- a/modules/autonavigation/helperfunctions.cpp +++ b/modules/autonavigation/helperfunctions.cpp @@ -98,6 +98,10 @@ namespace openspace::autonavigation::interpolator { + cp4 * t * t * t; } + glm::dvec3 linear(double t, const glm::dvec3 &cp1, const glm::dvec3 &cp2) { + return cp1 * t + cp2 * (1.0 - t); + } + glm::dvec3 piecewiseCubicBezier(double t, const std::vector &controlPoints) { size_t n = controlPoints.size(); ghoul_assert(n > 4, "Minimum of four control points needed for interpolation!"); @@ -127,7 +131,7 @@ namespace openspace::autonavigation::interpolator { size_t idx = std::floor(t*nSeg); - return (1.0 - tSeg) * controlPoints[idx] + tSeg * controlPoints[idx + 1]; + return linear(tSeg, controlPoints[idx], controlPoints[idx + 1]); } } // interpolator diff --git a/modules/autonavigation/helperfunctions.h b/modules/autonavigation/helperfunctions.h index 38e897a338..abf6b84407 100644 --- a/modules/autonavigation/helperfunctions.h +++ b/modules/autonavigation/helperfunctions.h @@ -62,11 +62,16 @@ double exponentialEaseInOut(double t); // TODO: include interpolator.h to helperfunctions // error when interpolator.h is included and used both here and in pathsegment + +// TODO: also make these template functions instead + namespace openspace::autonavigation::interpolator { glm::dvec3 cubicBezier(double t, const glm::dvec3 &cp1, const glm::dvec3 &cp2, const glm::dvec3 &cp3, const glm::dvec3 &cp4); + glm::dvec3 linear(double t, const glm::dvec3 &cp1, const glm::dvec3 &cp2); + glm::dvec3 piecewiseCubicBezier(double t, const std::vector &controlPoints); glm::dvec3 piecewiseLinear(double t, const std::vector &controlPoints); diff --git a/modules/autonavigation/pathcurves.cpp b/modules/autonavigation/pathcurves.cpp index 412ee55cb0..668bd284d6 100644 --- a/modules/autonavigation/pathcurves.cpp +++ b/modules/autonavigation/pathcurves.cpp @@ -25,10 +25,8 @@ #include #include -#include #include #include -#include #include namespace { @@ -95,7 +93,7 @@ LinearCurve::LinearCurve(CameraState& start, CameraState& end) { } glm::dvec3 LinearCurve::interpolate(double t) { - return ghoul::interpolateLinear(t, _points[0], _points[1]); + return interpolator::linear(t, _points[0], _points[1]); } Linear2Curve::Linear2Curve(CameraState& start, CameraState& end) { diff --git a/modules/autonavigation/pathcurves.h b/modules/autonavigation/pathcurves.h index ecd407eed2..65bf5f4bcd 100644 --- a/modules/autonavigation/pathcurves.h +++ b/modules/autonavigation/pathcurves.h @@ -25,13 +25,12 @@ #ifndef __OPENSPACE_MODULE_AUTONAVIGATION___PATHCURVE___H__ #define __OPENSPACE_MODULE_AUTONAVIGATION___PATHCURVE___H__ +#include #include #include namespace openspace::autonavigation { -struct CameraState; - class PathCurve { public: virtual ~PathCurve() = 0; diff --git a/modules/autonavigation/pathsegment.cpp b/modules/autonavigation/pathsegment.cpp index 0aec433300..d19e1e4c2b 100644 --- a/modules/autonavigation/pathsegment.cpp +++ b/modules/autonavigation/pathsegment.cpp @@ -31,7 +31,6 @@ #include #include #include -#include #include namespace { @@ -108,7 +107,7 @@ const glm::dquat PathSegment::getLookAtRotation( { glm::dvec3 startLookAtPos = sceneGraphNode(_start.referenceNode)->worldPosition(); glm::dvec3 endLookAtPos = sceneGraphNode(_end.referenceNode)->worldPosition(); - glm::dvec3 lookAtPos = ghoul::interpolateLinear(t, startLookAtPos, endLookAtPos); + glm::dvec3 lookAtPos = interpolator::linear(t, startLookAtPos, endLookAtPos); glm::dmat4 lookAtMat = glm::lookAt( currentPos, diff --git a/modules/autonavigation/pathsegment.h b/modules/autonavigation/pathsegment.h index 6a5771e486..cf8eab49a7 100644 --- a/modules/autonavigation/pathsegment.h +++ b/modules/autonavigation/pathsegment.h @@ -25,17 +25,12 @@ #ifndef __OPENSPACE_MODULE___PATHSEGMENT___H__ #define __OPENSPACE_MODULE___PATHSEGMENT___H__ +#include #include #include namespace openspace::autonavigation { -struct CameraState { - glm::dvec3 position; - glm::dquat rotation; - std::string referenceNode; -}; - enum CurveType { Bezier, Bezier2, From b528c6d4abe93352edb0e34833913d7f832bbb85 Mon Sep 17 00:00:00 2001 From: Emma Broman Date: Thu, 16 Jan 2020 14:38:12 -0500 Subject: [PATCH 017/912] Refactor helper interpolating functions a bit --- modules/autonavigation/helperfunctions.cpp | 43 +++++++++++----------- modules/autonavigation/helperfunctions.h | 2 +- modules/autonavigation/pathcurves.cpp | 8 ++-- modules/autonavigation/pathsegment.cpp | 2 +- 4 files changed, 28 insertions(+), 27 deletions(-) diff --git a/modules/autonavigation/helperfunctions.cpp b/modules/autonavigation/helperfunctions.cpp index 402594472d..b89682a48e 100644 --- a/modules/autonavigation/helperfunctions.cpp +++ b/modules/autonavigation/helperfunctions.cpp @@ -85,9 +85,10 @@ double exponentialEaseInOut(double t) { } // easingfunctions -namespace openspace::autonavigation::interpolator { +namespace openspace::autonavigation::interpolation { - // TODO: make into template function + // TODO: make all these into template functions + glm::dvec3 cubicBezier(double t, const glm::dvec3 &cp1, const glm::dvec3 &cp2, const glm::dvec3 &cp3, const glm::dvec3 &cp4 ) { @@ -99,39 +100,39 @@ namespace openspace::autonavigation::interpolator { } glm::dvec3 linear(double t, const glm::dvec3 &cp1, const glm::dvec3 &cp2) { - return cp1 * t + cp2 * (1.0 - t); + return cp1 * (1.0 - t) + cp2 * t; } - glm::dvec3 piecewiseCubicBezier(double t, const std::vector &controlPoints) { - size_t n = controlPoints.size(); + glm::dvec3 piecewiseCubicBezier(double t, const std::vector &points) { + size_t n = points.size(); ghoul_assert(n > 4, "Minimum of four control points needed for interpolation!"); - double nSeg = (n - 1.0) / 3.0; - ghoul_assert(std::fmod(nSeg, 1.0) == 0, "A vector containing 3n + 1 control points must be provided!"); + double nrSegments = (n - 1.0) / 3.0; + ghoul_assert(std::fmod(nrSegments, 1.0) == 0, "A vector containing 3n + 1 control points must be provided!"); - // for control points equally spaced in time - double tSeg = std::fmod(t * nSeg, 1.0); - tSeg = std::max(0.0, std::min(tSeg, 1.0)); + // for points equally spaced in time + double tSegment = std::fmod(t * nrSegments, 1.0); + tSegment = std::max(0.0, std::min(tSegment, 1.0)); - size_t idx = std::floor(t * nSeg) * 3; + size_t idx = std::floor(t * nrSegments) * 3; - return cubicBezier(tSeg, controlPoints[idx], controlPoints[idx + 1], - controlPoints[idx + 2], controlPoints[idx + 3]); + return cubicBezier(tSegment, points[idx], points[idx + 1], + points[idx + 2], points[idx + 3]); } - glm::dvec3 piecewiseLinear(double t, const std::vector &controlPoints) { - size_t n = controlPoints.size(); + glm::dvec3 piecewiseLinear(double t, const std::vector &points) { + size_t n = points.size(); ghoul_assert(n > 2, "Minimum of two control points needed for interpolation!"); - size_t nSeg = n - 1; + size_t nrSegments = n - 1; - // for control points equally spaced in time - double tSeg = std::fmod( t*nSeg, 1.0 ); - tSeg = std::max(0.0, std::min(tSeg, 1.0)); + // for points equally spaced in time + double tSegment = std::fmod( t*nrSegments, 1.0 ); + tSegment = std::max(0.0, std::min(tSegment, 1.0)); - size_t idx = std::floor(t*nSeg); + size_t idx = std::floor(t*nrSegments); - return linear(tSeg, controlPoints[idx], controlPoints[idx + 1]); + return linear(tSegment, points[idx], points[idx + 1]); } } // interpolator diff --git a/modules/autonavigation/helperfunctions.h b/modules/autonavigation/helperfunctions.h index abf6b84407..c800fcaf79 100644 --- a/modules/autonavigation/helperfunctions.h +++ b/modules/autonavigation/helperfunctions.h @@ -65,7 +65,7 @@ double exponentialEaseInOut(double t); // TODO: also make these template functions instead -namespace openspace::autonavigation::interpolator { +namespace openspace::autonavigation::interpolation { glm::dvec3 cubicBezier(double t, const glm::dvec3 &cp1, const glm::dvec3 &cp2, const glm::dvec3 &cp3, const glm::dvec3 &cp4); diff --git a/modules/autonavigation/pathcurves.cpp b/modules/autonavigation/pathcurves.cpp index 668bd284d6..5c7060d5aa 100644 --- a/modules/autonavigation/pathcurves.cpp +++ b/modules/autonavigation/pathcurves.cpp @@ -51,7 +51,7 @@ BezierCurve::BezierCurve(CameraState& start, CameraState& end) { } glm::dvec3 BezierCurve::interpolate(double t) { - return interpolator::cubicBezier(t, + return interpolation::cubicBezier(t, _points[0], _points[1], _points[2], _points[3]); } @@ -84,7 +84,7 @@ Bezier2Curve::Bezier2Curve(CameraState& start, CameraState& end) { } glm::dvec3 Bezier2Curve::interpolate(double t) { - return interpolator::piecewiseCubicBezier(t, _points); + return interpolation::piecewiseCubicBezier(t, _points); } LinearCurve::LinearCurve(CameraState& start, CameraState& end) { @@ -93,7 +93,7 @@ LinearCurve::LinearCurve(CameraState& start, CameraState& end) { } glm::dvec3 LinearCurve::interpolate(double t) { - return interpolator::linear(t, _points[0], _points[1]); + return interpolation::linear(t, _points[0], _points[1]); } Linear2Curve::Linear2Curve(CameraState& start, CameraState& end) { @@ -118,7 +118,7 @@ Linear2Curve::Linear2Curve(CameraState& start, CameraState& end) { } glm::dvec3 Linear2Curve::interpolate(double t) { - return interpolator::piecewiseLinear(t, _points); + return interpolation::piecewiseLinear(t, _points); } } // namespace openspace::autonavigation diff --git a/modules/autonavigation/pathsegment.cpp b/modules/autonavigation/pathsegment.cpp index d19e1e4c2b..8a8066f202 100644 --- a/modules/autonavigation/pathsegment.cpp +++ b/modules/autonavigation/pathsegment.cpp @@ -107,7 +107,7 @@ const glm::dquat PathSegment::getLookAtRotation( { glm::dvec3 startLookAtPos = sceneGraphNode(_start.referenceNode)->worldPosition(); glm::dvec3 endLookAtPos = sceneGraphNode(_end.referenceNode)->worldPosition(); - glm::dvec3 lookAtPos = interpolator::linear(t, startLookAtPos, endLookAtPos); + glm::dvec3 lookAtPos = interpolation::linear(t, startLookAtPos, endLookAtPos); glm::dmat4 lookAtMat = glm::lookAt( currentPos, From 146fc4ce8d0999e0d25e7d7ff4465ebfae1becdf Mon Sep 17 00:00:00 2001 From: Emma Broman Date: Mon, 20 Jan 2020 11:48:55 -0500 Subject: [PATCH 018/912] Approximate curve length --- modules/autonavigation/pathcurves.cpp | 24 ++++++++++++++++++------ modules/autonavigation/pathcurves.h | 13 ++++++++----- modules/autonavigation/pathsegment.cpp | 7 ++++++- modules/autonavigation/pathsegment.h | 2 +- 4 files changed, 33 insertions(+), 13 deletions(-) diff --git a/modules/autonavigation/pathcurves.cpp b/modules/autonavigation/pathcurves.cpp index 5c7060d5aa..d55414384c 100644 --- a/modules/autonavigation/pathcurves.cpp +++ b/modules/autonavigation/pathcurves.cpp @@ -37,6 +37,18 @@ namespace openspace::autonavigation { PathCurve::~PathCurve() {} +// Approximate the curve length by dividing the curve into smaller linear +// segments and accumulate their length +double PathCurve::arcLength(double tLimit) { + double dt = 0.01; // TODO: choose a good dt + double sum = 0.0; + for (double t = 0.0; t <= tLimit - dt; t += dt) { + double ds = glm::length(valueAt(t + dt) - valueAt(t)); + sum += ds; + } + return sum; +} + BezierCurve::BezierCurve(CameraState& start, CameraState& end) { glm::dvec3 startNodePos = sceneGraphNode(start.referenceNode)->worldPosition(); glm::dvec3 endNodePos = sceneGraphNode(start.referenceNode)->worldPosition(); @@ -50,7 +62,7 @@ BezierCurve::BezierCurve(CameraState& start, CameraState& end) { _points.push_back(end.position); } -glm::dvec3 BezierCurve::interpolate(double t) { +glm::dvec3 BezierCurve::valueAt(double t) { return interpolation::cubicBezier(t, _points[0], _points[1], _points[2], _points[3]); } @@ -69,7 +81,7 @@ Bezier2Curve::Bezier2Curve(CameraState& start, CameraState& end) { glm::dvec3 C = normalize(startDirection + endDirection); glm::dvec3 CparAB = glm::dot(C, normalize(AB))* normalize(AB); glm::dvec3 CortAB = normalize(C - CparAB); - double d = length(AB); + double d = glm::length(AB); // TODO: set points that actually look good _points.push_back(start.position); @@ -83,7 +95,7 @@ Bezier2Curve::Bezier2Curve(CameraState& start, CameraState& end) { _points.push_back(end.position); } -glm::dvec3 Bezier2Curve::interpolate(double t) { +glm::dvec3 Bezier2Curve::valueAt(double t) { return interpolation::piecewiseCubicBezier(t, _points); } @@ -92,7 +104,7 @@ LinearCurve::LinearCurve(CameraState& start, CameraState& end) { _points.push_back(end.position); } -glm::dvec3 LinearCurve::interpolate(double t) { +glm::dvec3 LinearCurve::valueAt(double t) { return interpolation::linear(t, _points[0], _points[1]); } @@ -110,14 +122,14 @@ Linear2Curve::Linear2Curve(CameraState& start, CameraState& end) { glm::dvec3 C = normalize(startDirection + endDirection); glm::dvec3 CparAB = glm::dot(C, normalize(AB))* normalize(AB); glm::dvec3 CortAB = normalize(C - CparAB); - double d = length(AB); + double d = glm::length(AB); _points.push_back(start.position); _points.push_back(start.position + 2.0 * d * CortAB + 0.5 * AB); //TODO: use scale instead of 2.0 _points.push_back(end.position); } -glm::dvec3 Linear2Curve::interpolate(double t) { +glm::dvec3 Linear2Curve::valueAt(double t) { return interpolation::piecewiseLinear(t, _points); } diff --git a/modules/autonavigation/pathcurves.h b/modules/autonavigation/pathcurves.h index 65bf5f4bcd..41f3e21f24 100644 --- a/modules/autonavigation/pathcurves.h +++ b/modules/autonavigation/pathcurves.h @@ -31,10 +31,13 @@ namespace openspace::autonavigation { +// OBS! Path curves has curve parameter in range [0,1] class PathCurve { public: virtual ~PathCurve() = 0; - virtual glm::dvec3 interpolate(double t) = 0; + virtual glm::dvec3 valueAt(double t) = 0; + + double arcLength(double tLimit = 1.0); protected: // the points used for creating the curve (e.g. control points of a Bezier curve) std::vector _points; @@ -43,25 +46,25 @@ protected: class BezierCurve : public PathCurve { public : BezierCurve(CameraState& start, CameraState& end); - glm::dvec3 interpolate(double t); + glm::dvec3 valueAt(double t); }; class Bezier2Curve : public PathCurve { public: Bezier2Curve(CameraState& start, CameraState& end); - glm::dvec3 interpolate(double t); + glm::dvec3 valueAt(double t); }; class LinearCurve : public PathCurve { public: LinearCurve(CameraState& start, CameraState& end); - glm::dvec3 interpolate(double t); + glm::dvec3 valueAt(double t); }; class Linear2Curve : public PathCurve { public: Linear2Curve(CameraState& start, CameraState& end); - glm::dvec3 interpolate(double t); + glm::dvec3 valueAt(double t); }; } // namespace openspace::autonavigation diff --git a/modules/autonavigation/pathsegment.cpp b/modules/autonavigation/pathsegment.cpp index 8a8066f202..b1f20c6073 100644 --- a/modules/autonavigation/pathsegment.cpp +++ b/modules/autonavigation/pathsegment.cpp @@ -61,7 +61,12 @@ PathSegment::PathSegment( break; default: LERROR(fmt::format("Cannot create curve of type {}. Type does not exist!", _curveType)); + return; } + + // TEST: + double length = _curve->arcLength(); + LINFO(fmt::format("Curve length: {}", length)); } void PathSegment::setStart(CameraState cs) { @@ -82,7 +87,7 @@ const double PathSegment::startTime() const { return _startTime; } const glm::vec3 PathSegment::getPositionAt(double t) const { t = easingfunctions::cubicEaseInOut(t); - return _curve->interpolate(t); + return _curve->valueAt(t); } const glm::dquat PathSegment::getRotationAt(double t) const { diff --git a/modules/autonavigation/pathsegment.h b/modules/autonavigation/pathsegment.h index cf8eab49a7..054f330736 100644 --- a/modules/autonavigation/pathsegment.h +++ b/modules/autonavigation/pathsegment.h @@ -42,7 +42,7 @@ class PathCurve; class PathSegment { public: - PathSegment(CameraState start, CameraState end, double startTime, CurveType type = Linear2); + PathSegment(CameraState start, CameraState end, double startTime, CurveType type = Linear); ~PathSegment() = default; // Mutators From a197ed0e7f356f5d542db31b4a0c503de17a52f4 Mon Sep 17 00:00:00 2001 From: Emma Broman Date: Wed, 22 Jan 2020 11:09:05 -0500 Subject: [PATCH 019/912] Member variable for curve length and prepare for duration to be based on curve length --- modules/autonavigation/pathsegment.cpp | 11 +++++------ modules/autonavigation/pathsegment.h | 1 + 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/modules/autonavigation/pathsegment.cpp b/modules/autonavigation/pathsegment.cpp index b1f20c6073..a7d1c66655 100644 --- a/modules/autonavigation/pathsegment.cpp +++ b/modules/autonavigation/pathsegment.cpp @@ -43,9 +43,6 @@ PathSegment::PathSegment( CameraState start, CameraState end, double startTime, CurveType type) : _start(start), _end(end), _startTime(startTime), _curveType(type) { - // TODO: compute duration based on method later - _duration = 5; - switch(type) { case Bezier: _curve = std::make_shared(start, end); @@ -64,9 +61,11 @@ PathSegment::PathSegment( return; } - // TEST: - double length = _curve->arcLength(); - LINFO(fmt::format("Curve length: {}", length)); + _length = _curve->arcLength(); + //LINFO(fmt::format("Curve length: {}", _length)); + + // TODO: compute default duration based on curve length + _duration = 5; } void PathSegment::setStart(CameraState cs) { diff --git a/modules/autonavigation/pathsegment.h b/modules/autonavigation/pathsegment.h index 054f330736..907909eff6 100644 --- a/modules/autonavigation/pathsegment.h +++ b/modules/autonavigation/pathsegment.h @@ -64,6 +64,7 @@ private: CameraState _end; double _duration; double _startTime; + double _length; CurveType _curveType; // Ideally, we don't want to need to save this std::shared_ptr _curve; // OBS! Does it make more sense to use unique_ptr? However, then PathSegments cannot be copied. From 54dac17bfc6c24e5718a3f37957a96ac61c1f6d6 Mon Sep 17 00:00:00 2001 From: Emma Broman Date: Wed, 22 Jan 2020 11:17:40 -0500 Subject: [PATCH 020/912] Minor refactor --- modules/autonavigation/autonavigationhandler.cpp | 10 +++++----- modules/autonavigation/pathsegment.cpp | 2 ++ 2 files changed, 7 insertions(+), 5 deletions(-) diff --git a/modules/autonavigation/autonavigationhandler.cpp b/modules/autonavigation/autonavigationhandler.cpp index 5285af9716..4f1226d625 100644 --- a/modules/autonavigation/autonavigationhandler.cpp +++ b/modules/autonavigation/autonavigationhandler.cpp @@ -94,10 +94,12 @@ void AutoNavigationHandler::updateCamera(double deltaTime) { if (currentIndex < 0) return; // no path - if (_stopAtTargets && (currentIndex != _activeSegmentIndex)) { + if (currentIndex != _activeSegmentIndex) { _activeSegmentIndex = currentIndex; - pausePath(); - return; + if (_stopAtTargets) { + pausePath(); + return; + } } const PathSegment& cps = _pathSegments[currentIndex]; @@ -120,8 +122,6 @@ void AutoNavigationHandler::updateCamera(double deltaTime) { _currentTime += deltaTime; - _activeSegmentIndex = currentIndex; - if (hasFinished()) { LINFO("Reached end of path."); _isPlaying = false; diff --git a/modules/autonavigation/pathsegment.cpp b/modules/autonavigation/pathsegment.cpp index a7d1c66655..3138c417be 100644 --- a/modules/autonavigation/pathsegment.cpp +++ b/modules/autonavigation/pathsegment.cpp @@ -65,6 +65,8 @@ PathSegment::PathSegment( //LINFO(fmt::format("Curve length: {}", _length)); // TODO: compute default duration based on curve length + // Also, when compensatng for simulation time later we need to make a guess for + // the duration, based on the current position of the target. _duration = 5; } From 48b1249355ae0697213678e69a220aec7c89b527 Mon Sep 17 00:00:00 2001 From: Emma Broman Date: Thu, 23 Jan 2020 08:32:41 -0500 Subject: [PATCH 021/912] Add scene to use for camera path testing --- data/assets/camera_paths_test.scene | 89 +++++++++++++++++++++++++++++ openspace.cfg | 3 + 2 files changed, 92 insertions(+) create mode 100644 data/assets/camera_paths_test.scene diff --git a/data/assets/camera_paths_test.scene b/data/assets/camera_paths_test.scene new file mode 100644 index 0000000000..ee91baa52b --- /dev/null +++ b/data/assets/camera_paths_test.scene @@ -0,0 +1,89 @@ + +local assetHelper = asset.require('util/asset_helper') +local sceneHelper = asset.require('util/scene_helper') +local propertyHelper = asset.require('util/property_helper') + +local sunTransforms = asset.require('scene/solarsystem/sun/transforms') + +-- Specifying which other assets should be loaded in this scene +asset.require('spice/base') + + +local earthAsset = asset.require('scene/solarsystem/planets/earth/earth') + +asset.require('scene/solarsystem/sun/sun') +asset.require('scene/solarsystem/sun/glare') +asset.require('scene/solarsystem/planets') +asset.request('scene/milkyway/milkyway/volume') + +-- TODO: add non planet object +local modelFolder = asset.syncedResource({ + Name = "Apollo Models", + Type = "HttpSynchronization", + Identifier = "apollo_11_models", + Version = 1 +}) + +local object = { + Identifier = "ExampleModel", + Transform = { + Translation = { + Type = "StaticTranslation", + Position = {100000000, 1000, 10000} + } + }, + Renderable = { + Type = "RenderableModel", + Geometry = { + Type = "MultiModelGeometry", + GeometryFile = modelFolder .. "/Apollo_CSM_shrunk_rotated_xy_double_size.obj" + }, + ColorTexture = modelFolder .. "/gray.png", + LightSources = assetHelper.getDefaultLightSources(sunTransforms.SolarSystemBarycenter.Identifier) + }, + GUI = { + Name = "Example Model", + Path = "" + } +} +assetHelper.registerSceneGraphNodesAndExport(asset, { object }) + +assetHelper.requestAll(asset, 'scene/digitaluniverse') + +-- Load default key bindings applicable to most scenes +asset.require('util/default_keybindings') +asset.require('util/default_dashboard') + +-- Load web gui +local webGui = asset.require('util/webgui') + +asset.request('customization/globebrowsing') + +-- Keybindings that are specific for this scene +local Keybindings = { + { + Key = "right", + Name = "Continue camera path", + Command = "openspace.autonavigation.continuePath()", + Documentation = "Continue a paused camera path.", + GuiPath = "/Interaction", + Local = false + }, +} + +asset.onInitialize(function () + webGui.setCefRoute("onscreen") + + local now = openspace.time.currentWallTime() + -- Jump back one day to be able to show complete weather data on Earth. + openspace.time.setTime(openspace.time.advancedTime(now, "-1d")) + + openspace.globebrowsing.goToGeo("Earth", 58.5877, 16.1924, 20000000) + + sceneHelper.bindKeys(Keybindings) + openspace.setDefaultGuiSorting() +end) + +asset.onDeinitialize(function () + sceneHelper.unbindKeys(Keybindings) +end) diff --git a/openspace.cfg b/openspace.cfg index b47a47aa89..1008543f5f 100644 --- a/openspace.cfg +++ b/openspace.cfg @@ -43,6 +43,9 @@ SGCTConfig = sgct.config.single{} -- Sets the scene that is to be loaded by OpenSpace. A scene file is a description -- of all entities that will be visible during an instance of OpenSpace +-- Scene used for testing camera paths (we can add what we want!) +-- Asset = "camera_paths_test" + Asset = "default" -- Asset = "default_full" -- Asset = "newhorizons" From 39a995fbe426f01a0284779ebef5c44f0e286158 Mon Sep 17 00:00:00 2001 From: Emma Broman Date: Thu, 23 Jan 2020 11:16:27 -0500 Subject: [PATCH 022/912] Use test scene instead of default --- openspace.cfg | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/openspace.cfg b/openspace.cfg index 1008543f5f..c808b328c2 100644 --- a/openspace.cfg +++ b/openspace.cfg @@ -44,9 +44,9 @@ SGCTConfig = sgct.config.single{} -- of all entities that will be visible during an instance of OpenSpace -- Scene used for testing camera paths (we can add what we want!) --- Asset = "camera_paths_test" +Asset = "camera_paths_test" -Asset = "default" +-- Asset = "default" -- Asset = "default_full" -- Asset = "newhorizons" -- Asset = "rosetta" From 98d8c0159ca2dd3d2a679dc44fec5d91c8565c95 Mon Sep 17 00:00:00 2001 From: Emma Broman Date: Thu, 23 Jan 2020 14:46:30 -0500 Subject: [PATCH 023/912] Update current time before we start stepping along the curve. Did not reach end before --- modules/autonavigation/autonavigationhandler.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/modules/autonavigation/autonavigationhandler.cpp b/modules/autonavigation/autonavigationhandler.cpp index 4f1226d625..04e0bea04b 100644 --- a/modules/autonavigation/autonavigationhandler.cpp +++ b/modules/autonavigation/autonavigationhandler.cpp @@ -102,6 +102,8 @@ void AutoNavigationHandler::updateCamera(double deltaTime) { } } + _currentTime += deltaTime; + const PathSegment& cps = _pathSegments[currentIndex]; // Interpolation variable @@ -120,8 +122,6 @@ void AutoNavigationHandler::updateCamera(double deltaTime) { camera()->setPositionVec3(cameraPosition); camera()->setRotation(cameraRotation); - _currentTime += deltaTime; - if (hasFinished()) { LINFO("Reached end of path."); _isPlaying = false; From 4507ac69fe4977bcb04f13c4f9a53b8ec8f10e08 Mon Sep 17 00:00:00 2001 From: Lingis Date: Thu, 23 Jan 2020 15:37:10 -0500 Subject: [PATCH 024/912] Bezier3 added and set as default curve --- modules/autonavigation/pathcurves.cpp | 43 ++++++++++++++++++++++++++ modules/autonavigation/pathcurves.h | 6 ++++ modules/autonavigation/pathsegment.cpp | 3 ++ modules/autonavigation/pathsegment.h | 3 +- 4 files changed, 54 insertions(+), 1 deletion(-) diff --git a/modules/autonavigation/pathcurves.cpp b/modules/autonavigation/pathcurves.cpp index 5c7060d5aa..ba93fa344d 100644 --- a/modules/autonavigation/pathcurves.cpp +++ b/modules/autonavigation/pathcurves.cpp @@ -87,6 +87,49 @@ glm::dvec3 Bezier2Curve::interpolate(double t) { return interpolation::piecewiseCubicBezier(t, _points); } +Bezier3Curve::Bezier3Curve(CameraState& start, CameraState& end) { + // TODO: CALCULATE AND SET CONDITION BOOLS IN CURVE CONSTRUCTOR + glm::dvec3 startNodePos = sceneGraphNode(start.referenceNode)->worldPosition(); + glm::dvec3 startDirection = start.position - startNodePos; + double startRadius = sceneGraphNode(start.referenceNode)->boundingSphere(); + + glm::dvec3 endNodePos = sceneGraphNode(end.referenceNode)->worldPosition(); + glm::dvec3 endDirection = end.position - endNodePos; + + glm::dvec3 nodePosDiff = endNodePos - startNodePos; + double cosStartAngle = glm::dot(normalize(startDirection), normalize(nodePosDiff)); + + // TODO: Test with raycaster, value can vary depending on how close we are to surface!! + bool TARGET_BEHIND_STARTNODE = cosStartAngle < -0.8; + bool TARGET_IN_OPPOSITE_DIRECTION = cosStartAngle > 0.8; + + + // SET CONTROL POINTS + _points.push_back(start.position); + _points.push_back(start.position + 2.0 * startRadius * normalize(startDirection)); + + if (TARGET_BEHIND_STARTNODE) + { + glm::dvec3 parallell = normalize(nodePosDiff) * glm::dot(startDirection, normalize(nodePosDiff)); + glm::dvec3 orthogonal = normalize(startDirection - parallell); + //Point on the side of start node + double dist = 5.0 * startRadius; + glm::dvec3 extraKnot = startNodePos + dist * orthogonal; + + _points.push_back(extraKnot - parallell); + _points.push_back(extraKnot); + _points.push_back(extraKnot + parallell); + } + + _points.push_back(end.position + 2.0 * endDirection); + _points.push_back(end.position); +} + + +glm::dvec3 Bezier3Curve::interpolate(double t) { + return interpolation::piecewiseCubicBezier(t, _points); +} + LinearCurve::LinearCurve(CameraState& start, CameraState& end) { _points.push_back(start.position); _points.push_back(end.position); diff --git a/modules/autonavigation/pathcurves.h b/modules/autonavigation/pathcurves.h index 65bf5f4bcd..e82c9a8ccb 100644 --- a/modules/autonavigation/pathcurves.h +++ b/modules/autonavigation/pathcurves.h @@ -52,6 +52,12 @@ public: glm::dvec3 interpolate(double t); }; +class Bezier3Curve : public PathCurve { +public: + Bezier3Curve(CameraState& start, CameraState& end); + glm::dvec3 interpolate(double t); +}; + class LinearCurve : public PathCurve { public: LinearCurve(CameraState& start, CameraState& end); diff --git a/modules/autonavigation/pathsegment.cpp b/modules/autonavigation/pathsegment.cpp index 8a8066f202..aaad5742f5 100644 --- a/modules/autonavigation/pathsegment.cpp +++ b/modules/autonavigation/pathsegment.cpp @@ -53,6 +53,9 @@ PathSegment::PathSegment( case Bezier2: _curve = std::make_shared(start, end); break; + case Bezier3: + _curve = std::make_shared(start, end); + break; case Linear: _curve = std::make_shared(start, end); break; diff --git a/modules/autonavigation/pathsegment.h b/modules/autonavigation/pathsegment.h index cf8eab49a7..cc559acf7a 100644 --- a/modules/autonavigation/pathsegment.h +++ b/modules/autonavigation/pathsegment.h @@ -34,6 +34,7 @@ namespace openspace::autonavigation { enum CurveType { Bezier, Bezier2, + Bezier3, Linear, Linear2 }; @@ -42,7 +43,7 @@ class PathCurve; class PathSegment { public: - PathSegment(CameraState start, CameraState end, double startTime, CurveType type = Linear2); + PathSegment(CameraState start, CameraState end, double startTime, CurveType type = Bezier3); ~PathSegment() = default; // Mutators From 5fb34e24f805f439c416ef3941af6ee07feb5514 Mon Sep 17 00:00:00 2001 From: Emma Broman Date: Thu, 23 Jan 2020 16:07:56 -0500 Subject: [PATCH 025/912] Add posibility to include a start navigation state in path spec --- .../autonavigation/autonavigationhandler.cpp | 87 +++++++++++-------- .../autonavigation/autonavigationhandler.h | 4 + modules/autonavigation/pathsegment.cpp | 52 ++++++----- modules/autonavigation/pathsegment.h | 6 +- modules/autonavigation/pathspecification.cpp | 38 +++++++- modules/autonavigation/pathspecification.h | 13 +-- 6 files changed, 133 insertions(+), 67 deletions(-) diff --git a/modules/autonavigation/autonavigationhandler.cpp b/modules/autonavigation/autonavigationhandler.cpp index 04e0bea04b..0a2f42f5a1 100644 --- a/modules/autonavigation/autonavigationhandler.cpp +++ b/modules/autonavigation/autonavigationhandler.cpp @@ -142,6 +142,12 @@ void AutoNavigationHandler::createPath(PathSpecification& spec) { // OBS! Would it be better to save the spec in the handler class? _stopAtTargets = spec.stopAtTargets(); + // Check if we have a specified start state. If so, update the first segment + if (spec.hasStartState() && _pathSegments.size() > 0) { + CameraState startState = cameraStateFromNavigationState(spec.startState()); + _pathSegments[0].setStart(startState); + } + if (success) { LINFO("Succefully generated camera path."); startPath(); @@ -193,6 +199,8 @@ void AutoNavigationHandler::continuePath() { // the old camera state. _pathSegments[_activeSegmentIndex].setStart(currentCameraState()); + LINFO(fmt::format("Next segment: {}", _activeSegmentIndex)); + _isPlaying = true; } @@ -315,43 +323,7 @@ bool AutoNavigationHandler::handleNavigationStateInstruction( _pathSegments.empty() ? currentCameraState() : _pathSegments.back().end(); interaction::NavigationHandler::NavigationState ns = props->navState; - - // OBS! The following code is exactly the same as used in - // NavigationHandler::applyNavigationState. Should probably be made into a function. - const SceneGraphNode* referenceFrame = sceneGraphNode(ns.referenceFrame); - const SceneGraphNode* anchorNode = sceneGraphNode(ns.anchor); // The anchor is also the target - - if (!anchorNode) { - LERROR(fmt::format("Could not find node '{}' to target", ns.anchor)); - return false; - } - - const glm::dvec3 anchorWorldPosition = anchorNode->worldPosition(); - const glm::dmat3 referenceFrameTransform = referenceFrame->worldRotationMatrix(); - - const glm::dvec3 targetPositionWorld = anchorWorldPosition + - glm::dvec3(referenceFrameTransform * glm::dvec4(ns.position, 1.0)); - - glm::dvec3 up = ns.up.has_value() ? - glm::normalize(referenceFrameTransform * ns.up.value()) : - glm::dvec3(0.0, 1.0, 0.0); - - // Construct vectors of a "neutral" view, i.e. when the aim is centered in view. - glm::dvec3 neutralView = - glm::normalize(anchorWorldPosition - targetPositionWorld); - - glm::dquat neutralCameraRotation = glm::inverse(glm::quat_cast(glm::lookAt( - glm::dvec3(0.0), - neutralView, - up - ))); - - glm::dquat pitchRotation = glm::angleAxis(ns.pitch, glm::dvec3(1.f, 0.f, 0.f)); - glm::dquat yawRotation = glm::angleAxis(ns.yaw, glm::dvec3(0.f, -1.f, 0.f)); - - glm::quat targetRotation = neutralCameraRotation * yawRotation * pitchRotation; - - CameraState endState = CameraState{ targetPositionWorld, targetRotation, ns.anchor }; + CameraState endState = cameraStateFromNavigationState(ns); // TODO: Handle duration better PathSegment newSegment{ startState, endState, startTime }; @@ -408,4 +380,45 @@ glm::dvec3 AutoNavigationHandler::computeTargetPositionAtNode( return targetPos; } +CameraState AutoNavigationHandler::cameraStateFromNavigationState( + const interaction::NavigationHandler::NavigationState& ns) +{ + // OBS! The following code is exactly the same as used in + // NavigationHandler::applyNavigationState. Should probably be made into a function. + const SceneGraphNode* referenceFrame = sceneGraphNode(ns.referenceFrame); + const SceneGraphNode* anchorNode = sceneGraphNode(ns.anchor); // The anchor is also the target + + if (!anchorNode) { + LERROR(fmt::format("Could not find node '{}' to target. Returning empty state.", ns.anchor)); + return CameraState{}; + } + + const glm::dvec3 anchorWorldPosition = anchorNode->worldPosition(); + const glm::dmat3 referenceFrameTransform = referenceFrame->worldRotationMatrix(); + + const glm::dvec3 targetPositionWorld = anchorWorldPosition + + glm::dvec3(referenceFrameTransform * glm::dvec4(ns.position, 1.0)); + + glm::dvec3 up = ns.up.has_value() ? + glm::normalize(referenceFrameTransform * ns.up.value()) : + glm::dvec3(0.0, 1.0, 0.0); + + // Construct vectors of a "neutral" view, i.e. when the aim is centered in view. + glm::dvec3 neutralView = + glm::normalize(anchorWorldPosition - targetPositionWorld); + + glm::dquat neutralCameraRotation = glm::inverse(glm::quat_cast(glm::lookAt( + glm::dvec3(0.0), + neutralView, + up + ))); + + glm::dquat pitchRotation = glm::angleAxis(ns.pitch, glm::dvec3(1.f, 0.f, 0.f)); + glm::dquat yawRotation = glm::angleAxis(ns.yaw, glm::dvec3(0.f, -1.f, 0.f)); + + glm::quat targetRotation = neutralCameraRotation * yawRotation * pitchRotation; + + return CameraState{ targetPositionWorld, targetRotation, ns.anchor }; +} + } // namespace openspace::autonavigation diff --git a/modules/autonavigation/autonavigationhandler.h b/modules/autonavigation/autonavigationhandler.h index dd94127569..17f6951c22 100644 --- a/modules/autonavigation/autonavigationhandler.h +++ b/modules/autonavigation/autonavigationhandler.h @@ -39,6 +39,7 @@ namespace openspace { namespace openspace::autonavigation { struct CameraState; +struct interaction::NavigationHandler::NavigationState; class AutoNavigationHandler : public properties::PropertyOwner { public: @@ -71,6 +72,9 @@ private: glm::dvec3 computeTargetPositionAtNode(const SceneGraphNode* node, glm::dvec3 prevPos, double height); + CameraState cameraStateFromNavigationState( + const interaction::NavigationHandler::NavigationState& ns); + // This list essentially represents the camera path std::vector _pathSegments; diff --git a/modules/autonavigation/pathsegment.cpp b/modules/autonavigation/pathsegment.cpp index 3138c417be..556888abc5 100644 --- a/modules/autonavigation/pathsegment.cpp +++ b/modules/autonavigation/pathsegment.cpp @@ -42,27 +42,8 @@ namespace openspace::autonavigation { PathSegment::PathSegment( CameraState start, CameraState end, double startTime, CurveType type) : _start(start), _end(end), _startTime(startTime), _curveType(type) -{ - switch(type) { - case Bezier: - _curve = std::make_shared(start, end); - break; - case Bezier2: - _curve = std::make_shared(start, end); - break; - case Linear: - _curve = std::make_shared(start, end); - break; - case Linear2: - _curve = std::make_shared(start, end); - break; - default: - LERROR(fmt::format("Cannot create curve of type {}. Type does not exist!", _curveType)); - return; - } - - _length = _curve->arcLength(); - //LINFO(fmt::format("Curve length: {}", _length)); +{ + initCurve(); // TODO: compute default duration based on curve length // Also, when compensatng for simulation time later we need to make a guess for @@ -72,6 +53,8 @@ PathSegment::PathSegment( void PathSegment::setStart(CameraState cs) { _start = std::move(cs); + initCurve(); + // TODO later: maybe recompute duration as well... } void PathSegment::setDuration(double d) { @@ -124,4 +107,31 @@ const glm::dquat PathSegment::getLookAtRotation( return glm::normalize(glm::inverse(glm::quat_cast(lookAtMat))); } +// Initialise the curve, based on the start, end state and curve type +void PathSegment::initCurve() { + // in case there already is a curve object, reset the pointer. + _curve.reset(); + + switch (_curveType) { + case Bezier: + _curve = std::make_shared(_start, _end); + break; + case Bezier2: + _curve = std::make_shared(_start, _end); + break; + case Linear: + _curve = std::make_shared(_start, _end); + break; + case Linear2: + _curve = std::make_shared(_start, _end); + break; + default: + LERROR(fmt::format("Cannot create curve of type {}. Type does not exist!", _curveType)); + return; + } + + _length = _curve->arcLength(); + //LINFO(fmt::format("Curve length: {}", _length)); +} + } // namespace openspace::autonavigation diff --git a/modules/autonavigation/pathsegment.h b/modules/autonavigation/pathsegment.h index 907909eff6..597043edb4 100644 --- a/modules/autonavigation/pathsegment.h +++ b/modules/autonavigation/pathsegment.h @@ -60,12 +60,14 @@ public: const glm::dquat getLookAtRotation(double t, glm::dvec3 currentPos, glm::dvec3 up) const; private: + void initCurve(); + CameraState _start; CameraState _end; - double _duration; double _startTime; double _length; - CurveType _curveType; // Ideally, we don't want to need to save this + double _duration; + CurveType _curveType; std::shared_ptr _curve; // OBS! Does it make more sense to use unique_ptr? However, then PathSegments cannot be copied. }; diff --git a/modules/autonavigation/pathspecification.cpp b/modules/autonavigation/pathspecification.cpp index 73df55beb3..aa373fbcd4 100644 --- a/modules/autonavigation/pathspecification.cpp +++ b/modules/autonavigation/pathspecification.cpp @@ -32,6 +32,7 @@ namespace { constexpr const char* KeyInstructions = "Instructions"; constexpr const char* KeyStopAtTargets = "StopAtTargets"; + constexpr const char* KeyStartState = "StartState"; constexpr const char* KeyTarget = "Target"; constexpr const char* KeyDuration = "Duration"; @@ -126,7 +127,7 @@ NavigationStateInstructionProps::NavigationStateInstructionProps( try { openspace::documentation::testSpecificationAndThrow( - NavigationState::Documentation(), + interaction::NavigationHandler::NavigationState::Documentation(), navStateDict, "NavigationState" ); @@ -136,7 +137,7 @@ NavigationStateInstructionProps::NavigationStateInstructionProps( return; } - navState = NavigationState(navStateDict); + navState = interaction::NavigationHandler::NavigationState(navStateDict); } } @@ -194,6 +195,25 @@ PathSpecification::PathSpecification(const ghoul::Dictionary& dictionary) { if (dictionary.hasValue(KeyStopAtTargets)) { _stopAtTargets = dictionary.value(KeyStopAtTargets); } + + // Read start state + if (dictionary.hasValue(KeyStartState)) { + auto navStateDict = dictionary.value(KeyStartState); + + try { + openspace::documentation::testSpecificationAndThrow( + interaction::NavigationHandler::NavigationState::Documentation(), + navStateDict, + "NavigationState" + ); + } + catch (ghoul::RuntimeError& e) { + LERROR(fmt::format("Unable to read start navigation state. Does not match documentation: {}", e.message)); + return; + } + + _startState = interaction::NavigationHandler::NavigationState(navStateDict); + } } PathSpecification::PathSpecification(const Instruction instruction) { @@ -209,6 +229,14 @@ const bool PathSpecification::stopAtTargets() const { return _stopAtTargets; } +const interaction::NavigationHandler::NavigationState& PathSpecification::startState() const { + return _startState.value(); +} + +const bool PathSpecification::hasStartState() const { + return _startState.has_value(); +} + documentation::Documentation PathSpecification::Documentation() { using namespace documentation; @@ -228,6 +256,12 @@ documentation::Documentation PathSpecification::Documentation() { Optional::Yes, "A bool that decides whether we should pause when reaching a target when playing a path." }, + { + KeyStartState, + new TableVerifier, + Optional::Yes, + "A navigation state that determines the start state for the camera path." + }, } }; } diff --git a/modules/autonavigation/pathspecification.h b/modules/autonavigation/pathspecification.h index 8d20eac7f9..3b4b8b03b8 100644 --- a/modules/autonavigation/pathspecification.h +++ b/modules/autonavigation/pathspecification.h @@ -51,11 +51,9 @@ struct TargetNodeInstructionProps : public InstructionProps { }; struct NavigationStateInstructionProps : public InstructionProps { - using NavigationState = interaction::NavigationHandler::NavigationState; - NavigationStateInstructionProps(const ghoul::Dictionary& dictionary); - NavigationState navState; + interaction::NavigationHandler::NavigationState navState; }; struct PauseInstructionProps : public InstructionProps { @@ -84,11 +82,16 @@ public: // Accessors const std::vector* instructions() const; - const bool stopAtTargets() const; + const bool stopAtTargets() const; + const interaction::NavigationHandler::NavigationState& startState() const; + + const bool hasStartState() const; private: std::vector _instructions; - bool _stopAtTargets = false; // default is tp play the entire path without stops + bool _stopAtTargets = false; // default is to play the entire path without stops + std::optional _startState; + // TODO: maxSpeed or speedFactor or something? }; From aa8a2a98b3306faf24d406a300e791c3d628542b Mon Sep 17 00:00:00 2001 From: Emma Broman Date: Thu, 23 Jan 2020 17:36:06 -0500 Subject: [PATCH 026/912] Move path instruction class to its own file --- modules/autonavigation/CMakeLists.txt | 2 + .../autonavigation/autonavigationhandler.cpp | 2 + .../autonavigation/autonavigationhandler.h | 5 +- .../autonavigationmodule_lua.inl | 1 + modules/autonavigation/instruction.cpp | 170 ++++++++++++++++++ modules/autonavigation/instruction.h | 74 ++++++++ modules/autonavigation/pathspecification.cpp | 135 +------------- modules/autonavigation/pathspecification.h | 46 +---- 8 files changed, 258 insertions(+), 177 deletions(-) create mode 100644 modules/autonavigation/instruction.cpp create mode 100644 modules/autonavigation/instruction.h diff --git a/modules/autonavigation/CMakeLists.txt b/modules/autonavigation/CMakeLists.txt index cc0390db8c..3a0241d38e 100644 --- a/modules/autonavigation/CMakeLists.txt +++ b/modules/autonavigation/CMakeLists.txt @@ -29,6 +29,7 @@ set(HEADER_FILES ${CMAKE_CURRENT_SOURCE_DIR}/autonavigationhandler.h ${CMAKE_CURRENT_SOURCE_DIR}/camerastate.h ${CMAKE_CURRENT_SOURCE_DIR}/helperfunctions.h + ${CMAKE_CURRENT_SOURCE_DIR}/instruction.h ${CMAKE_CURRENT_SOURCE_DIR}/pathspecification.h ${CMAKE_CURRENT_SOURCE_DIR}/pathsegment.h ${CMAKE_CURRENT_SOURCE_DIR}/pathcurves.h @@ -40,6 +41,7 @@ set(SOURCE_FILES ${CMAKE_CURRENT_SOURCE_DIR}/autonavigationmodule_lua.inl ${CMAKE_CURRENT_SOURCE_DIR}/autonavigationhandler.cpp ${CMAKE_CURRENT_SOURCE_DIR}/helperfunctions.cpp + ${CMAKE_CURRENT_SOURCE_DIR}/instruction.cpp ${CMAKE_CURRENT_SOURCE_DIR}/pathspecification.cpp ${CMAKE_CURRENT_SOURCE_DIR}/pathsegment.cpp ${CMAKE_CURRENT_SOURCE_DIR}/pathcurves.cpp diff --git a/modules/autonavigation/autonavigationhandler.cpp b/modules/autonavigation/autonavigationhandler.cpp index 0a2f42f5a1..a6c6d397ad 100644 --- a/modules/autonavigation/autonavigationhandler.cpp +++ b/modules/autonavigation/autonavigationhandler.cpp @@ -25,6 +25,8 @@ #include #include +#include +#include #include #include #include diff --git a/modules/autonavigation/autonavigationhandler.h b/modules/autonavigation/autonavigationhandler.h index 17f6951c22..3cb5163a68 100644 --- a/modules/autonavigation/autonavigationhandler.h +++ b/modules/autonavigation/autonavigationhandler.h @@ -26,8 +26,8 @@ #define __OPENSPACE_MODULE___AUTONAVIGATIONHANDLER___H__ #include -#include #include +#include #include #include #include @@ -39,7 +39,8 @@ namespace openspace { namespace openspace::autonavigation { struct CameraState; -struct interaction::NavigationHandler::NavigationState; +struct Instruction; +class PathSpecification; class AutoNavigationHandler : public properties::PropertyOwner { public: diff --git a/modules/autonavigation/autonavigationmodule_lua.inl b/modules/autonavigation/autonavigationmodule_lua.inl index c400c1bcad..b247ae3fd3 100644 --- a/modules/autonavigation/autonavigationmodule_lua.inl +++ b/modules/autonavigation/autonavigationmodule_lua.inl @@ -23,6 +23,7 @@ ****************************************************************************************/ #include +#include #include #include #include diff --git a/modules/autonavigation/instruction.cpp b/modules/autonavigation/instruction.cpp new file mode 100644 index 0000000000..e898f61dc4 --- /dev/null +++ b/modules/autonavigation/instruction.cpp @@ -0,0 +1,170 @@ +/***************************************************************************************** + * * + * OpenSpace * + * * + * Copyright (c) 2014-2019 * + * * + * Permission is hereby granted, free of charge, to any person obtaining a copy of this * + * software and associated documentation files (the "Software"), to deal in the Software * + * without restriction, including without limitation the rights to use, copy, modify, * + * merge, publish, distribute, sublicense, and/or sell copies of the Software, and to * + * permit persons to whom the Software is furnished to do so, subject to the following * + * conditions: * + * * + * The above copyright notice and this permission notice shall be included in all copies * + * or substantial portions of the Software. * + * * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, * + * INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A * + * PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT * + * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF * + * CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE * + * OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. * + ****************************************************************************************/ + +#include + +#include +#include + +namespace { + constexpr const char* _loggerCat = "PathInstruction"; + + constexpr const char* KeyTarget = "Target"; + constexpr const char* KeyDuration = "Duration"; + constexpr const char* KeyPosition = "Position"; + constexpr const char* KeyHeight = "Height"; + + constexpr const char* KeyNavigationState = "NavigationState"; +} // namespace + +namespace openspace::autonavigation { + +documentation::Documentation TargetNodeInstructionDocumentation() { + using namespace documentation; + + return { + "Target Node Instruction", + "target_node_instruction", + { + { + KeyTarget, + new StringVerifier, + Optional::No, + "The identifier of the target node." + }, + { + KeyDuration, + new DoubleVerifier, + Optional::Yes, + "The desired duration for the camera movement." + }, + { + KeyPosition, + new Vector3Verifier, + Optional::Yes, + "The desired final position for the camera movement, given in model space." + }, + { + KeyHeight, + new DoubleVerifier, + Optional::Yes, + "The desired height from surface for final position (meters). Will be ignored if a target position is set. " + }, + } + }; +} + +InstructionProps::InstructionProps(const ghoul::Dictionary& dictionary) { + // TODO: validate against some documentation? + + if (dictionary.hasValue(KeyDuration)) { + duration = dictionary.value(KeyDuration); + } +} + +InstructionProps::~InstructionProps() {} + +TargetNodeInstructionProps::TargetNodeInstructionProps( + const ghoul::Dictionary& dictionary) : InstructionProps(dictionary) +{ + try { + documentation::testSpecificationAndThrow( + TargetNodeInstructionDocumentation(), + dictionary, + "Target Node Instruction" + ); + } + catch (ghoul::RuntimeError& e) { + LERROR(fmt::format("Unable to generate target node instruction from dictionary. Does not match documentation: {}", e.message)); + return; + } + + if (!dictionary.hasValue(KeyTarget)) { + throw ghoul::RuntimeError( + "A camera path instruction requires a target node, to go to or use as reference frame." + ); + } + + targetNode = dictionary.value(KeyTarget); + + if (dictionary.hasValue(KeyPosition)) { + position = dictionary.value(KeyPosition); + } + + if (dictionary.hasValue(KeyHeight)) { + height = dictionary.value(KeyHeight); + } +} + +NavigationStateInstructionProps::NavigationStateInstructionProps( + const ghoul::Dictionary& dictionary) : InstructionProps(dictionary) +{ + if (dictionary.hasValue(KeyNavigationState)) { + auto navStateDict = dictionary.value(KeyNavigationState); + + try { + openspace::documentation::testSpecificationAndThrow( + interaction::NavigationHandler::NavigationState::Documentation(), + navStateDict, + "NavigationState" + ); + } + catch (ghoul::RuntimeError& e) { + LERROR(fmt::format("Unable to generate navigation state instruction from dictionary. Does not match documentation: {}", e.message)); + return; + } + + navState = interaction::NavigationHandler::NavigationState(navStateDict); + } +} + +PauseInstructionProps::PauseInstructionProps(const ghoul::Dictionary& dictionary) + : InstructionProps(dictionary) +{ } + +Instruction::Instruction(const ghoul::Dictionary& dictionary) { + + // TODO: test against some documentation? + + // Deduce the instruction type based on the fields in the dictionary + if (dictionary.hasValue(KeyTarget)) { + type = InstructionType::TargetNode; + props = std::make_shared(dictionary); + } + else if (dictionary.hasValue(KeyNavigationState)) { + type = InstructionType::NavigationState; + props = std::make_shared(dictionary); + } + else if (dictionary.hasValue(KeyDuration)) { + type = InstructionType::Pause; + props = std::make_shared(dictionary); + } + else { + throw ghoul::RuntimeError( + "Could not deduce instruction type." + ); + } +} + +} // namespace openspace::autonavigation diff --git a/modules/autonavigation/instruction.h b/modules/autonavigation/instruction.h new file mode 100644 index 0000000000..e4f561d008 --- /dev/null +++ b/modules/autonavigation/instruction.h @@ -0,0 +1,74 @@ +/***************************************************************************************** + * * + * OpenSpace * + * * + * Copyright (c) 2014-2019 * + * * + * Permission is hereby granted, free of charge, to any person obtaining a copy of this * + * software and associated documentation files (the "Software"), to deal in the Software * + * without restriction, including without limitation the rights to use, copy, modify, * + * merge, publish, distribute, sublicense, and/or sell copies of the Software, and to * + * permit persons to whom the Software is furnished to do so, subject to the following * + * conditions: * + * * + * The above copyright notice and this permission notice shall be included in all copies * + * or substantial portions of the Software. * + * * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, * + * INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A * + * PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT * + * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF * + * CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE * + * OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. * + ****************************************************************************************/ + +#ifndef __OPENSPACE_MODULE___PATHINSTRUCTION___H__ +#define __OPENSPACE_MODULE___PATHINSTRUCTION___H__ + +#include +#include + +namespace openspace::autonavigation { + +enum InstructionType { TargetNode, NavigationState, Pause }; + +struct InstructionProps { + InstructionProps() = default; + InstructionProps(const ghoul::Dictionary& dictionary); + virtual ~InstructionProps() = 0; + + std::optional duration; +}; + +struct TargetNodeInstructionProps : public InstructionProps { + TargetNodeInstructionProps(const ghoul::Dictionary& dictionary); + + std::string targetNode; + std::optional position; // relative to target node (model space) + std::optional height; +}; + +struct NavigationStateInstructionProps : public InstructionProps { + NavigationStateInstructionProps(const ghoul::Dictionary& dictionary); + + interaction::NavigationHandler::NavigationState navState; +}; + +struct PauseInstructionProps : public InstructionProps { + PauseInstructionProps(const ghoul::Dictionary& dictionary); + + // For now, a pause instruction does not have any special props. + // Might be added later +}; + +struct Instruction { + Instruction() = default; + Instruction(const ghoul::Dictionary& dictionary); + + InstructionType type; + std::shared_ptr props; +}; + +} // namespace openspace::autonavigation + +#endif // __OPENSPACE_MODULE___PATHINSTRUCTION___H__ diff --git a/modules/autonavigation/pathspecification.cpp b/modules/autonavigation/pathspecification.cpp index aa373fbcd4..1543f547b0 100644 --- a/modules/autonavigation/pathspecification.cpp +++ b/modules/autonavigation/pathspecification.cpp @@ -24,151 +24,21 @@ #include +#include #include #include namespace { - constexpr const char* _loggerCat = "PathInstruction"; + constexpr const char* _loggerCat = "PathSpecification"; constexpr const char* KeyInstructions = "Instructions"; constexpr const char* KeyStopAtTargets = "StopAtTargets"; constexpr const char* KeyStartState = "StartState"; - constexpr const char* KeyTarget = "Target"; - constexpr const char* KeyDuration = "Duration"; - constexpr const char* KeyPosition = "Position"; - constexpr const char* KeyHeight = "Height"; - - constexpr const char* KeyNavigationState = "NavigationState"; } // namespace namespace openspace::autonavigation { -documentation::Documentation TargetNodeInstructionDocumentation() { - using namespace documentation; - - return { - "Target Node Instruction", - "target_node_instruction", - { - { - KeyTarget, - new StringVerifier, - Optional::No, - "The identifier of the target node." - }, - { - KeyDuration, - new DoubleVerifier, - Optional::Yes, - "The desired duration for the camera movement." - }, - { - KeyPosition, - new Vector3Verifier, - Optional::Yes, - "The desired final position for the camera movement, given in model space." - }, - { - KeyHeight, - new DoubleVerifier, - Optional::Yes, - "The desired height from surface for final position (meters). Will be ignored if a target position is set. " - }, - } - }; -} - -InstructionProps::InstructionProps(const ghoul::Dictionary& dictionary) { - // TODO: validate against some documentation? - - if (dictionary.hasValue(KeyDuration)) { - duration = dictionary.value(KeyDuration); - } -} - -TargetNodeInstructionProps::TargetNodeInstructionProps( - const ghoul::Dictionary& dictionary) : InstructionProps(dictionary) -{ - try { - documentation::testSpecificationAndThrow( - TargetNodeInstructionDocumentation(), - dictionary, - "Target Node Instruction" - ); - } - catch (ghoul::RuntimeError& e) { - LERROR(fmt::format("Unable to generate target node instruction from dictionary. Does not match documentation: {}", e.message)); - return; - } - - if (!dictionary.hasValue(KeyTarget)) { - throw ghoul::RuntimeError( - "A camera path instruction requires a target node, to go to or use as reference frame." - ); - } - - targetNode = dictionary.value(KeyTarget); - - if (dictionary.hasValue(KeyPosition)) { - position = dictionary.value(KeyPosition); - } - - if (dictionary.hasValue(KeyHeight)) { - height = dictionary.value(KeyHeight); - } -} - -NavigationStateInstructionProps::NavigationStateInstructionProps( - const ghoul::Dictionary& dictionary) : InstructionProps(dictionary) -{ - if (dictionary.hasValue(KeyNavigationState)) { - auto navStateDict = dictionary.value(KeyNavigationState); - - try { - openspace::documentation::testSpecificationAndThrow( - interaction::NavigationHandler::NavigationState::Documentation(), - navStateDict, - "NavigationState" - ); - } - catch (ghoul::RuntimeError& e) { - LERROR(fmt::format("Unable to generate navigation state instruction from dictionary. Does not match documentation: {}", e.message)); - return; - } - - navState = interaction::NavigationHandler::NavigationState(navStateDict); - } -} - -PauseInstructionProps::PauseInstructionProps(const ghoul::Dictionary& dictionary) - : InstructionProps(dictionary) -{ } - -Instruction::Instruction(const ghoul::Dictionary& dictionary) { - - // TODO: test against some documentation? - - // Deduce the instruction type based on the fields in the dictionary - if (dictionary.hasValue(KeyTarget)) { - type = InstructionType::TargetNode; - props = std::make_shared(dictionary); - } - else if (dictionary.hasValue(KeyNavigationState)) { - type = InstructionType::NavigationState; - props = std::make_shared(dictionary); - } - else if (dictionary.hasValue(KeyDuration)) { - type = InstructionType::Pause; - props = std::make_shared(dictionary); - } - else { - throw ghoul::RuntimeError( - "Could not deduce instruction type." - ); - } -} - PathSpecification::PathSpecification(const ghoul::Dictionary& dictionary) { try { documentation::testSpecificationAndThrow( @@ -266,5 +136,4 @@ documentation::Documentation PathSpecification::Documentation() { }; } - } // namespace openspace::autonavigation diff --git a/modules/autonavigation/pathspecification.h b/modules/autonavigation/pathspecification.h index 3b4b8b03b8..b9c0e808a0 100644 --- a/modules/autonavigation/pathspecification.h +++ b/modules/autonavigation/pathspecification.h @@ -22,9 +22,10 @@ * OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. * ****************************************************************************************/ -#ifndef __OPENSPACE_MODULE___PATHINSTRUCTION___H__ -#define __OPENSPACE_MODULE___PATHINSTRUCTION___H__ +#ifndef __OPENSPACE_MODULE___PATHSPECIFICATION___H__ +#define __OPENSPACE_MODULE___PATHSPECIFICATION___H__ +#include #include #include #include @@ -32,45 +33,6 @@ namespace openspace::autonavigation { -enum InstructionType { TargetNode, NavigationState, Pause }; - -struct InstructionProps { - InstructionProps() = default; - InstructionProps(const ghoul::Dictionary& dictionary); - virtual ~InstructionProps() {} // abstract - - std::optional duration; -}; - -struct TargetNodeInstructionProps : public InstructionProps { - TargetNodeInstructionProps(const ghoul::Dictionary& dictionary); - - std::string targetNode; - std::optional position; // relative to target node (model space) - std::optional height; -}; - -struct NavigationStateInstructionProps : public InstructionProps { - NavigationStateInstructionProps(const ghoul::Dictionary& dictionary); - - interaction::NavigationHandler::NavigationState navState; -}; - -struct PauseInstructionProps : public InstructionProps { - PauseInstructionProps(const ghoul::Dictionary& dictionary); - - // For now, a pause instruction does not have any special props. - // Might be added later -}; - -struct Instruction { - Instruction() = default; - Instruction(const ghoul::Dictionary& dictionary); - - InstructionType type; - std::shared_ptr props; -}; - class PathSpecification { public: @@ -97,4 +59,4 @@ private: } // namespace openspace::autonavigation -#endif // __OPENSPACE_MODULE___NAVIGATIONHANDLER___H__ +#endif // __OPENSPACE_MODULE___PATHSPECIFICATION___H__ From 965ce2a6a73ac00acd7dbac69232ed91800ba3e6 Mon Sep 17 00:00:00 2001 From: Emma Broman Date: Fri, 24 Jan 2020 10:30:25 -0500 Subject: [PATCH 027/912] Prototype solution to provide curve type (kinda ugly) --- .../autonavigation/autonavigationhandler.cpp | 112 +++++++++++------- .../autonavigation/autonavigationhandler.h | 9 +- modules/autonavigation/pathsegment.h | 3 +- modules/autonavigation/pathspecification.cpp | 16 +++ modules/autonavigation/pathspecification.h | 2 + 5 files changed, 94 insertions(+), 48 deletions(-) diff --git a/modules/autonavigation/autonavigationhandler.cpp b/modules/autonavigation/autonavigationhandler.cpp index a6c6d397ad..f8aa7f00b8 100644 --- a/modules/autonavigation/autonavigationhandler.cpp +++ b/modules/autonavigation/autonavigationhandler.cpp @@ -36,6 +36,7 @@ #include #include #include +#include namespace { constexpr const char* _loggerCat = "AutoNavigationHandler"; @@ -43,6 +44,35 @@ namespace { namespace openspace::autonavigation { +// Temporary function to convert a string to one of the bools above. +// TODO: move to a better place / rewrite +CurveType stringToCurveType(std::string str) { + if (str.empty()) + return CurveType::None; + + std::transform(str.begin(), str.end(), str.begin(), ::tolower); + + if (str == "bezier") { + return CurveType::Bezier; + } + else if (str == "bezier2") { + return CurveType::Bezier2; + } + else if (str == "bezier3") { + return CurveType::Bezier3; + } + else if (str == "linear") { + return CurveType::Linear; + } + else if (str == "linear2") { + return CurveType::Linear2; + } + else { + LERROR(fmt::format("'{}' is not a valid curve type! Choosing default.", str)); + return CurveType::None; + } +} + AutoNavigationHandler::AutoNavigationHandler() : properties::PropertyOwner({ "AutoNavigationHandler" }) { @@ -132,6 +162,9 @@ void AutoNavigationHandler::updateCamera(double deltaTime) { void AutoNavigationHandler::createPath(PathSpecification& spec) { clearPath(); + + _pathCurveType = stringToCurveType(spec.curveType()); + bool success = true; for (int i = 0; i < spec.instructions()->size(); i++) { const Instruction& ins = spec.instructions()->at(i); @@ -200,35 +233,23 @@ void AutoNavigationHandler::continuePath() { // Recompute start camera state for the upcoming path segment, to avoid clipping to // the old camera state. _pathSegments[_activeSegmentIndex].setStart(currentCameraState()); - - LINFO(fmt::format("Next segment: {}", _activeSegmentIndex)); - _isPlaying = true; } -bool AutoNavigationHandler::handleInstruction(const Instruction& instruction, int index) -{ - // compute startTime - double startTime = 0.0; - if (!_pathSegments.empty()) { - PathSegment& last = _pathSegments.back(); - startTime = last.startTime() + last.duration(); - } - +bool AutoNavigationHandler::handleInstruction(const Instruction& instruction, int index) { bool success = true; - switch (instruction.type) { case InstructionType::TargetNode: - success = handleTargetNodeInstruction(instruction, startTime); + success = handleTargetNodeInstruction(instruction); break; case InstructionType::NavigationState: - success = handleNavigationStateInstruction(instruction, startTime); + success = handleNavigationStateInstruction(instruction); break; case InstructionType::Pause: - success = handlePauseInstruction(instruction, startTime); + success = handlePauseInstruction(instruction); break; default: @@ -245,9 +266,7 @@ bool AutoNavigationHandler::handleInstruction(const Instruction& instruction, in return true; } -bool AutoNavigationHandler::handleTargetNodeInstruction( - const Instruction& instruction, double startTime) -{ +bool AutoNavigationHandler::handleTargetNodeInstruction(const Instruction& instruction) { // Verify instruction type TargetNodeInstructionProps* props = dynamic_cast(instruction.props.get()); @@ -299,18 +318,12 @@ bool AutoNavigationHandler::handleTargetNodeInstruction( CameraState endState = CameraState{ targetPos, targetRot, identifier }; - // TODO: Handle duration better - PathSegment newSegment{ startState, endState, startTime }; - if (instruction.props->duration.has_value()) { - newSegment.setDuration(instruction.props->duration.value()); - } - _pathSegments.push_back(newSegment); - + addSegment(startState, endState, instruction.props->duration); return true; } bool AutoNavigationHandler::handleNavigationStateInstruction( - const Instruction& instruction, double startTime) + const Instruction& instruction) { // Verify instruction type NavigationStateInstructionProps* props = @@ -327,18 +340,11 @@ bool AutoNavigationHandler::handleNavigationStateInstruction( interaction::NavigationHandler::NavigationState ns = props->navState; CameraState endState = cameraStateFromNavigationState(ns); - // TODO: Handle duration better - PathSegment newSegment{ startState, endState, startTime }; - if (instruction.props->duration.has_value()) { - newSegment.setDuration(instruction.props->duration.value()); - } - _pathSegments.push_back(newSegment); - + addSegment(startState, endState, instruction.props->duration); return true; } -bool AutoNavigationHandler::handlePauseInstruction( - const Instruction& instruction, double startTime) +bool AutoNavigationHandler::handlePauseInstruction(const Instruction& instruction) { // Verify instruction type PauseInstructionProps* props = @@ -349,21 +355,39 @@ bool AutoNavigationHandler::handlePauseInstruction( return false; } - CameraState startState = - _pathSegments.empty() ? currentCameraState() : _pathSegments.back().end(); + CameraState startState =_pathSegments.empty() + ? currentCameraState() + : _pathSegments.back().end(); CameraState endState = startState; // TODO: implement more complex behavior later - // TODO: Handle duration better - PathSegment newSegment{ startState, endState, startTime }; - if (instruction.props->duration.has_value()) { - newSegment.setDuration(instruction.props->duration.value()); + addSegment(startState, endState, instruction.props->duration); + return true; +} + +void AutoNavigationHandler::addSegment(CameraState& start, + CameraState& end, std::optional duration) +{ + // compute startTime + double startTime = 0.0; + if (!_pathSegments.empty()) { + PathSegment& last = _pathSegments.back(); + startTime = last.startTime() + last.duration(); + } + + bool hasType = (_pathCurveType != CurveType::None); + + PathSegment newSegment = hasType + ? PathSegment{ start, end, startTime, _pathCurveType } + : PathSegment{ start, end, startTime }; + + // TODO: handle duration better + if (duration.has_value()) { + newSegment.setDuration(duration.value()); } _pathSegments.push_back(newSegment); - - return true; } glm::dvec3 AutoNavigationHandler::computeTargetPositionAtNode( diff --git a/modules/autonavigation/autonavigationhandler.h b/modules/autonavigation/autonavigationhandler.h index 3cb5163a68..dbd7316c18 100644 --- a/modules/autonavigation/autonavigationhandler.h +++ b/modules/autonavigation/autonavigationhandler.h @@ -66,9 +66,11 @@ public: private: bool handleInstruction(const Instruction& instruction, int index); - bool handleTargetNodeInstruction(const Instruction& instruction, double startTime); - bool handleNavigationStateInstruction(const Instruction& instruction, double startTime); - bool handlePauseInstruction(const Instruction& instruction, double startTime); + bool handleTargetNodeInstruction(const Instruction& instruction); + bool handleNavigationStateInstruction(const Instruction& instruction); + bool handlePauseInstruction(const Instruction& instruction); + + void addSegment(CameraState& start, CameraState& end, std::optional duration); glm::dvec3 computeTargetPositionAtNode(const SceneGraphNode* node, glm::dvec3 prevPos, double height); @@ -78,6 +80,7 @@ private: // This list essentially represents the camera path std::vector _pathSegments; + CurveType _pathCurveType; // TEST: create a path with just one type of curve double _currentTime; bool _isPlaying = false; diff --git a/modules/autonavigation/pathsegment.h b/modules/autonavigation/pathsegment.h index 285bfe6d49..d5aff050f6 100644 --- a/modules/autonavigation/pathsegment.h +++ b/modules/autonavigation/pathsegment.h @@ -36,7 +36,8 @@ enum CurveType { Bezier2, Bezier3, Linear, - Linear2 + Linear2, + None }; class PathCurve; diff --git a/modules/autonavigation/pathspecification.cpp b/modules/autonavigation/pathspecification.cpp index 1543f547b0..6d1a23ddf5 100644 --- a/modules/autonavigation/pathspecification.cpp +++ b/modules/autonavigation/pathspecification.cpp @@ -34,6 +34,7 @@ namespace { constexpr const char* KeyInstructions = "Instructions"; constexpr const char* KeyStopAtTargets = "StopAtTargets"; constexpr const char* KeyStartState = "StartState"; + constexpr const char* KeyCurveType = "CurveType"; } // namespace @@ -66,6 +67,11 @@ PathSpecification::PathSpecification(const ghoul::Dictionary& dictionary) { _stopAtTargets = dictionary.value(KeyStopAtTargets); } + // Read desired curve type + if (dictionary.hasValue(KeyCurveType)) { + _curveType = dictionary.value(KeyCurveType); + } + // Read start state if (dictionary.hasValue(KeyStartState)) { auto navStateDict = dictionary.value(KeyStartState); @@ -99,6 +105,10 @@ const bool PathSpecification::stopAtTargets() const { return _stopAtTargets; } +const std::string& PathSpecification::curveType() const { + return _curveType; +} + const interaction::NavigationHandler::NavigationState& PathSpecification::startState() const { return _startState.value(); } @@ -132,6 +142,12 @@ documentation::Documentation PathSpecification::Documentation() { Optional::Yes, "A navigation state that determines the start state for the camera path." }, + { + KeyCurveType, + new StringVerifier, + Optional::Yes, + "A string describing one of the available curve types (TODO: refer to later documentation)." + }, } }; } diff --git a/modules/autonavigation/pathspecification.h b/modules/autonavigation/pathspecification.h index b9c0e808a0..5ab7ae0441 100644 --- a/modules/autonavigation/pathspecification.h +++ b/modules/autonavigation/pathspecification.h @@ -45,6 +45,7 @@ public: // Accessors const std::vector* instructions() const; const bool stopAtTargets() const; + const std::string& curveType() const; const interaction::NavigationHandler::NavigationState& startState() const; const bool hasStartState() const; @@ -52,6 +53,7 @@ public: private: std::vector _instructions; bool _stopAtTargets = false; // default is to play the entire path without stops + std::string _curveType = std::string(); std::optional _startState; // TODO: maxSpeed or speedFactor or something? From c44fff4b22cae9bd25f15bfaaf2604d7def109eb Mon Sep 17 00:00:00 2001 From: Emma Broman Date: Fri, 24 Jan 2020 11:07:25 -0500 Subject: [PATCH 028/912] Temporary solution for handling pauses in instruction --- .../autonavigation/autonavigationhandler.cpp | 25 +++++++++++++++---- .../autonavigation/autonavigationhandler.h | 2 ++ modules/autonavigation/instruction.h | 2 +- modules/autonavigation/pathcurves.cpp | 9 +++++++ modules/autonavigation/pathcurves.h | 8 ++++++ modules/autonavigation/pathsegment.cpp | 17 +++++++------ modules/autonavigation/pathsegment.h | 5 ++-- 7 files changed, 53 insertions(+), 15 deletions(-) diff --git a/modules/autonavigation/autonavigationhandler.cpp b/modules/autonavigation/autonavigationhandler.cpp index f8aa7f00b8..d96c09712d 100644 --- a/modules/autonavigation/autonavigationhandler.cpp +++ b/modules/autonavigation/autonavigationhandler.cpp @@ -253,7 +253,7 @@ bool AutoNavigationHandler::handleInstruction(const Instruction& instruction, in break; default: - LERROR(fmt::format("Non-implemented instruction type: {}.", instruction.type)); + LERROR("Non-implemented instruction type."); success = false; break; } @@ -355,15 +355,13 @@ bool AutoNavigationHandler::handlePauseInstruction(const Instruction& instructio return false; } - CameraState startState =_pathSegments.empty() + CameraState state =_pathSegments.empty() ? currentCameraState() : _pathSegments.back().end(); - CameraState endState = startState; - // TODO: implement more complex behavior later - addSegment(startState, endState, instruction.props->duration); + addPause(state, instruction.props->duration); return true; } @@ -390,6 +388,23 @@ void AutoNavigationHandler::addSegment(CameraState& start, _pathSegments.push_back(newSegment); } +void AutoNavigationHandler::addPause(CameraState& state, std::optional duration) { + // compute startTime + double startTime = 0.0; + if (!_pathSegments.empty()) { + PathSegment& last = _pathSegments.back(); + startTime = last.startTime() + last.duration(); + } + + PathSegment newSegment = PathSegment{ state, state, startTime, CurveType::Pause }; + + // TODO: handle duration better + if (duration.has_value()) { + newSegment.setDuration(duration.value()); + } + _pathSegments.push_back(newSegment); +} + glm::dvec3 AutoNavigationHandler::computeTargetPositionAtNode( const SceneGraphNode* node, glm::dvec3 prevPos, double height) { diff --git a/modules/autonavigation/autonavigationhandler.h b/modules/autonavigation/autonavigationhandler.h index dbd7316c18..0082173e10 100644 --- a/modules/autonavigation/autonavigationhandler.h +++ b/modules/autonavigation/autonavigationhandler.h @@ -72,6 +72,8 @@ private: void addSegment(CameraState& start, CameraState& end, std::optional duration); + void addPause(CameraState& state, std::optional duration); + glm::dvec3 computeTargetPositionAtNode(const SceneGraphNode* node, glm::dvec3 prevPos, double height); diff --git a/modules/autonavigation/instruction.h b/modules/autonavigation/instruction.h index e4f561d008..3a8785d642 100644 --- a/modules/autonavigation/instruction.h +++ b/modules/autonavigation/instruction.h @@ -30,7 +30,7 @@ namespace openspace::autonavigation { -enum InstructionType { TargetNode, NavigationState, Pause }; +enum class InstructionType { TargetNode, NavigationState, Pause }; struct InstructionProps { InstructionProps() = default; diff --git a/modules/autonavigation/pathcurves.cpp b/modules/autonavigation/pathcurves.cpp index 438adc6e42..ef3b8992e9 100644 --- a/modules/autonavigation/pathcurves.cpp +++ b/modules/autonavigation/pathcurves.cpp @@ -175,4 +175,13 @@ glm::dvec3 Linear2Curve::valueAt(double t) { return interpolation::piecewiseLinear(t, _points); } +// TODO: Iprove handling of pauses +PauseCurve::PauseCurve(CameraState& state) { + _points.push_back(state.position); +} + +glm::dvec3 PauseCurve::valueAt(double t) { + return _points[0]; +} + } // namespace openspace::autonavigation diff --git a/modules/autonavigation/pathcurves.h b/modules/autonavigation/pathcurves.h index d0245d75b4..f165160783 100644 --- a/modules/autonavigation/pathcurves.h +++ b/modules/autonavigation/pathcurves.h @@ -73,6 +73,14 @@ public: glm::dvec3 valueAt(double t); }; +// OBS! This is a temporary class specialised for handling pauses. +// TODO: handle better in the future. +class PauseCurve : public PathCurve { +public: + PauseCurve(CameraState& state); + glm::dvec3 valueAt(double t); +}; + } // namespace openspace::autonavigation #endif // __OPENSPACE_MODULE_AUTONAVIGATION___PATHCURVE___H__ diff --git a/modules/autonavigation/pathsegment.cpp b/modules/autonavigation/pathsegment.cpp index 1495a6a0f8..917d2b6fdc 100644 --- a/modules/autonavigation/pathsegment.cpp +++ b/modules/autonavigation/pathsegment.cpp @@ -79,7 +79,7 @@ const glm::dquat PathSegment::getRotationAt(double t) const { tRot = easingfunctions::cubicEaseInOut(tRot); switch (_curveType) { - case Linear2: + case CurveType::Linear2: return getLookAtRotation( tRot, getPositionAt(t), @@ -113,23 +113,26 @@ void PathSegment::initCurve() { _curve.reset(); switch (_curveType) { - case Bezier: + case CurveType::Bezier: _curve = std::make_shared(_start, _end); break; - case Bezier2: + case CurveType::Bezier2: _curve = std::make_shared(_start, _end); break; - case Bezier3: + case CurveType::Bezier3: _curve = std::make_shared(_start, _end); break; - case Linear: + case CurveType::Linear: _curve = std::make_shared(_start, _end); break; - case Linear2: + case CurveType::Linear2: _curve = std::make_shared(_start, _end); break; + case CurveType::Pause: + _curve = std::make_shared(_start); + break; default: - LERROR(fmt::format("Cannot create curve of type {}. Type does not exist!", _curveType)); + LERROR("Could not create curve. Type does not exist!"); return; } diff --git a/modules/autonavigation/pathsegment.h b/modules/autonavigation/pathsegment.h index d5aff050f6..8cdfa5f668 100644 --- a/modules/autonavigation/pathsegment.h +++ b/modules/autonavigation/pathsegment.h @@ -31,12 +31,13 @@ namespace openspace::autonavigation { -enum CurveType { +enum class CurveType { Bezier, Bezier2, Bezier3, Linear, Linear2, + Pause, // OBS! Temporary special case for handling pauses None }; @@ -44,7 +45,7 @@ class PathCurve; class PathSegment { public: - PathSegment(CameraState start, CameraState end, double startTime, CurveType type = Bezier3); + PathSegment(CameraState start, CameraState end, double startTime, CurveType type = CurveType::Bezier3); ~PathSegment() = default; // Mutators From da8a77312209777da8eab3aeb404ccd5863fd57d Mon Sep 17 00:00:00 2001 From: Emma Broman Date: Fri, 24 Jan 2020 12:01:22 -0500 Subject: [PATCH 029/912] Prevent steppnig out of bounds for t=1.0 in piecewise interpolation --- modules/autonavigation/helperfunctions.cpp | 19 +++++++++++++++---- 1 file changed, 15 insertions(+), 4 deletions(-) diff --git a/modules/autonavigation/helperfunctions.cpp b/modules/autonavigation/helperfunctions.cpp index b89682a48e..a063c5a2d6 100644 --- a/modules/autonavigation/helperfunctions.cpp +++ b/modules/autonavigation/helperfunctions.cpp @@ -107,14 +107,20 @@ namespace openspace::autonavigation::interpolation { size_t n = points.size(); ghoul_assert(n > 4, "Minimum of four control points needed for interpolation!"); - double nrSegments = (n - 1.0) / 3.0; - ghoul_assert(std::fmod(nrSegments, 1.0) == 0, "A vector containing 3n + 1 control points must be provided!"); + ghoul_assert((n - 1) % 3 == 0, "A vector containing 3n + 1 control points must be provided!"); + + size_t nrSegments = (size_t)std::floor((n - 1) / 3.0); // for points equally spaced in time - double tSegment = std::fmod(t * nrSegments, 1.0); + double tSegment = std::fmod(t*nrSegments, 1.0); tSegment = std::max(0.0, std::min(tSegment, 1.0)); - size_t idx = std::floor(t * nrSegments) * 3; + size_t idx = std::floor(t*nrSegments) * 3; + + // prevent stepping past the last segment if t = 1.0 + if (idx > n - 4) { + return points.back(); + } return cubicBezier(tSegment, points[idx], points[idx + 1], points[idx + 2], points[idx + 3]); @@ -132,6 +138,11 @@ namespace openspace::autonavigation::interpolation { size_t idx = std::floor(t*nrSegments); + // prevent stepping past the last segment if t = 1.0 + if (idx > n - 1) { + return points.back(); + } + return linear(tSegment, points[idx], points[idx + 1]); } From e209e7cd6a911754e297d8526b828588abae170f Mon Sep 17 00:00:00 2001 From: Emma Broman Date: Sun, 26 Jan 2020 16:30:20 -0500 Subject: [PATCH 030/912] Add funciton and keybinding to stop playing a path --- data/assets/camera_paths_test.scene | 8 ++++++++ modules/autonavigation/autonavigationhandler.cpp | 4 ++++ modules/autonavigation/autonavigationhandler.h | 1 + modules/autonavigation/autonavigationmodule.cpp | 7 +++++++ modules/autonavigation/autonavigationmodule_lua.inl | 12 +++++++++++- 5 files changed, 31 insertions(+), 1 deletion(-) diff --git a/data/assets/camera_paths_test.scene b/data/assets/camera_paths_test.scene index ee91baa52b..d8ef10a6b3 100644 --- a/data/assets/camera_paths_test.scene +++ b/data/assets/camera_paths_test.scene @@ -69,6 +69,14 @@ local Keybindings = { GuiPath = "/Interaction", Local = false }, + { + Key = "Q", + Name = "Stop camera path", + Command = "openspace.autonavigation.stopPath()", + Documentation = "Stop a camera path.", + GuiPath = "/Interaction", + Local = false + }, } asset.onInitialize(function () diff --git a/modules/autonavigation/autonavigationhandler.cpp b/modules/autonavigation/autonavigationhandler.cpp index d96c09712d..957593cb06 100644 --- a/modules/autonavigation/autonavigationhandler.cpp +++ b/modules/autonavigation/autonavigationhandler.cpp @@ -236,6 +236,10 @@ void AutoNavigationHandler::continuePath() { _isPlaying = true; } +void AutoNavigationHandler::stopPath() { + _isPlaying = false; +} + bool AutoNavigationHandler::handleInstruction(const Instruction& instruction, int index) { bool success = true; switch (instruction.type) diff --git a/modules/autonavigation/autonavigationhandler.h b/modules/autonavigation/autonavigationhandler.h index 0082173e10..1e131f8c1d 100644 --- a/modules/autonavigation/autonavigationhandler.h +++ b/modules/autonavigation/autonavigationhandler.h @@ -62,6 +62,7 @@ public: void startPath(); void pausePath(); void continuePath(); + void stopPath(); private: bool handleInstruction(const Instruction& instruction, int index); diff --git a/modules/autonavigation/autonavigationmodule.cpp b/modules/autonavigation/autonavigationmodule.cpp index 4bc4f73142..1a10a4f5fb 100644 --- a/modules/autonavigation/autonavigationmodule.cpp +++ b/modules/autonavigation/autonavigationmodule.cpp @@ -59,6 +59,13 @@ scripting::LuaLibrary AutoNavigationModule::luaLibrary() const { "", "Continue playing a paused camera path." }, + { + "stopPath", + &autonavigation::luascriptfunctions::stopPath, + {}, + "", + "Stops a path, if one is being played." + }, { "goTo", &autonavigation::luascriptfunctions::goTo, diff --git a/modules/autonavigation/autonavigationmodule_lua.inl b/modules/autonavigation/autonavigationmodule_lua.inl index b247ae3fd3..e9b1457844 100644 --- a/modules/autonavigation/autonavigationmodule_lua.inl +++ b/modules/autonavigation/autonavigationmodule_lua.inl @@ -42,7 +42,7 @@ namespace openspace::autonavigation::luascriptfunctions { const double EPSILON = 1e-12; int continuePath(lua_State* L) { - int nArguments = ghoul::lua::checkArgumentsAndThrow(L, 0, "lua::continuePath"); + ghoul::lua::checkArgumentsAndThrow(L, 0, "lua::continuePath"); AutoNavigationModule* module = global::moduleEngine.module(); AutoNavigationHandler& handler = module->AutoNavigationHandler(); @@ -51,6 +51,16 @@ namespace openspace::autonavigation::luascriptfunctions { return 0; } + int stopPath(lua_State* L) { + ghoul::lua::checkArgumentsAndThrow(L, 0, "lua::stopPath"); + + AutoNavigationModule* module = global::moduleEngine.module(); + AutoNavigationHandler& handler = module->AutoNavigationHandler(); + handler.stopPath(); + + return 0; + } + int goTo(lua_State* L) { int nArguments = ghoul::lua::checkArgumentsAndThrow(L, { 1, 2 }, "lua::goTo"); From c1c96cd81d618e72eec7ea14476de471c1da00a5 Mon Sep 17 00:00:00 2001 From: Lingis Date: Sun, 26 Jan 2020 17:22:25 -0500 Subject: [PATCH 031/912] Bezier3 with more special cases handled and lookAt rotation --- modules/autonavigation/pathcurves.cpp | 36 +++++++++++++++++++++++--- modules/autonavigation/pathsegment.cpp | 12 ++++++--- 2 files changed, 40 insertions(+), 8 deletions(-) diff --git a/modules/autonavigation/pathcurves.cpp b/modules/autonavigation/pathcurves.cpp index 438adc6e42..a5e0cf6860 100644 --- a/modules/autonavigation/pathcurves.cpp +++ b/modules/autonavigation/pathcurves.cpp @@ -104,23 +104,26 @@ Bezier3Curve::Bezier3Curve(CameraState& start, CameraState& end) { glm::dvec3 startNodePos = sceneGraphNode(start.referenceNode)->worldPosition(); glm::dvec3 startDirection = start.position - startNodePos; double startRadius = sceneGraphNode(start.referenceNode)->boundingSphere(); + double endRadius = sceneGraphNode(end.referenceNode)->boundingSphere(); glm::dvec3 endNodePos = sceneGraphNode(end.referenceNode)->worldPosition(); glm::dvec3 endDirection = end.position - endNodePos; glm::dvec3 nodePosDiff = endNodePos - startNodePos; double cosStartAngle = glm::dot(normalize(startDirection), normalize(nodePosDiff)); + double cosEndAngle = glm::dot(normalize(endDirection), normalize(nodePosDiff)); - // TODO: Test with raycaster, value can vary depending on how close we are to surface!! + // TODO: Test with raycaster, test is dependent on start position bool TARGET_BEHIND_STARTNODE = cosStartAngle < -0.8; - bool TARGET_IN_OPPOSITE_DIRECTION = cosStartAngle > 0.8; + bool TARGET_ON_BACKSIDE = cosEndAngle > 0.8; + bool TARGET_IN_OPPOSITE_DIRECTION = cosStartAngle > 0.7; // SET CONTROL POINTS _points.push_back(start.position); _points.push_back(start.position + 2.0 * startRadius * normalize(startDirection)); - if (TARGET_BEHIND_STARTNODE) + if ( TARGET_BEHIND_STARTNODE ) { glm::dvec3 parallell = normalize(nodePosDiff) * glm::dot(startDirection, normalize(nodePosDiff)); glm::dvec3 orthogonal = normalize(startDirection - parallell); @@ -133,7 +136,32 @@ Bezier3Curve::Bezier3Curve(CameraState& start, CameraState& end) { _points.push_back(extraKnot + parallell); } - _points.push_back(end.position + 2.0 * endDirection); + if (TARGET_IN_OPPOSITE_DIRECTION && ! TARGET_ON_BACKSIDE) { + glm::dvec3 parallell = normalize(nodePosDiff * glm::dot(startDirection, normalize(nodePosDiff))); + glm::dvec3 orthogonal = normalize(normalize(startDirection) - parallell); + // Distant middle point + double dist = 0.5 * length(nodePosDiff); + glm::dvec3 extraKnot = startNodePos - dist * parallell + 3.0 * dist * orthogonal; + + _points.push_back(extraKnot - 0.5 * dist * parallell); + _points.push_back(extraKnot); + _points.push_back(extraKnot + 0.5 * dist * parallell); + } + + if (TARGET_ON_BACKSIDE) + { + glm::dvec3 parallell = normalize(nodePosDiff) * glm::dot(endDirection, normalize(nodePosDiff)); + glm::dvec3 orthogonal = normalize(endDirection - parallell); + //Point on the side of start node + double dist = 5.0 * endRadius; + glm::dvec3 extraKnot = endNodePos + dist * orthogonal; + + _points.push_back(extraKnot - parallell); + _points.push_back(extraKnot); + _points.push_back(extraKnot + parallell); + } + + _points.push_back(end.position + 2.0 * endRadius * normalize(endDirection)); _points.push_back(end.position); } diff --git a/modules/autonavigation/pathsegment.cpp b/modules/autonavigation/pathsegment.cpp index 721dd18e9d..9538d4c273 100644 --- a/modules/autonavigation/pathsegment.cpp +++ b/modules/autonavigation/pathsegment.cpp @@ -95,19 +95,23 @@ const glm::vec3 PathSegment::getPositionAt(double t) const { } const glm::dquat PathSegment::getRotationAt(double t) const { - double tRot = helpers::shiftAndScale(t, 0.1, 0.9); - tRot = easingfunctions::cubicEaseInOut(tRot); + double tSlerp = helpers::shiftAndScale(t, 0.1, 0.9); + tSlerp = easingfunctions::cubicEaseInOut(tSlerp); + + double tLookAt = helpers::shiftAndScale(t, 0.2, 0.8); + tLookAt = easingfunctions::cubicEaseInOut(tLookAt); switch (_curveType) { case Linear2: + case Bezier3: return getLookAtRotation( - tRot, + tLookAt, getPositionAt(t), global::navigationHandler.camera()->lookUpVectorWorldSpace() ); break; default: - return glm::slerp(_start.rotation, _end.rotation, tRot); + return glm::slerp(_start.rotation, _end.rotation, tSlerp); } } From fdb9903713a35a731fc3e84ebf67a2036c35e1a7 Mon Sep 17 00:00:00 2001 From: Emma Broman Date: Thu, 30 Jan 2020 09:45:17 -0500 Subject: [PATCH 032/912] Provide path position function, to use for rendering of path --- .../autonavigation/autonavigationhandler.cpp | 20 +++++++++++ .../autonavigation/autonavigationhandler.h | 4 ++- .../autonavigation/autonavigationmodule.cpp | 7 ++++ .../autonavigationmodule_lua.inl | 33 +++++++++++++++++++ 4 files changed, 63 insertions(+), 1 deletion(-) diff --git a/modules/autonavigation/autonavigationhandler.cpp b/modules/autonavigation/autonavigationhandler.cpp index 957593cb06..c40e32ba07 100644 --- a/modules/autonavigation/autonavigationhandler.cpp +++ b/modules/autonavigation/autonavigationhandler.cpp @@ -240,6 +240,26 @@ void AutoNavigationHandler::stopPath() { _isPlaying = false; } +std::vector AutoNavigationHandler::getCurvePositions(int nPerSegment) { + std::vector positions; + + if (_pathSegments.empty()) { + LERROR("There is no current path to sample points from."); + return positions; + } + + const double dt = 1.0 / nPerSegment; + + for (PathSegment &p : _pathSegments) { + for (double t = 0.0; t < 1.0; t += dt) { + auto position = p.getPositionAt(t); + positions.push_back(position); + } + } + + return positions; +} + bool AutoNavigationHandler::handleInstruction(const Instruction& instruction, int index) { bool success = true; switch (instruction.type) diff --git a/modules/autonavigation/autonavigationhandler.h b/modules/autonavigation/autonavigationhandler.h index 1e131f8c1d..09c2dcd902 100644 --- a/modules/autonavigation/autonavigationhandler.h +++ b/modules/autonavigation/autonavigationhandler.h @@ -54,7 +54,7 @@ public: const double pathDuration() const; const bool hasFinished() const; const int currentPathSegmentIndex() const; - CameraState currentCameraState(); + CameraState currentCameraState(); void updateCamera(double deltaTime); void createPath(PathSpecification& spec); @@ -64,6 +64,8 @@ public: void continuePath(); void stopPath(); + std::vector getCurvePositions(int nPerSegment); + private: bool handleInstruction(const Instruction& instruction, int index); diff --git a/modules/autonavigation/autonavigationmodule.cpp b/modules/autonavigation/autonavigationmodule.cpp index 1a10a4f5fb..fc9bb5980b 100644 --- a/modules/autonavigation/autonavigationmodule.cpp +++ b/modules/autonavigation/autonavigationmodule.cpp @@ -86,6 +86,13 @@ scripting::LuaLibrary AutoNavigationModule::luaLibrary() const { {}, "string", "Read an input file with lua instructions and use those to generate a camera path. TODO: Describe how a path instruction is defined?. " //TODO also make this one start path from file + }, + { + "getPathPositions", + &autonavigation::luascriptfunctions::getPathPositions, + {}, + "number", + "TODO: " } }; return res; diff --git a/modules/autonavigation/autonavigationmodule_lua.inl b/modules/autonavigation/autonavigationmodule_lua.inl index e9b1457844..9ad9a095f5 100644 --- a/modules/autonavigation/autonavigationmodule_lua.inl +++ b/modules/autonavigation/autonavigationmodule_lua.inl @@ -168,4 +168,37 @@ namespace openspace::autonavigation::luascriptfunctions { return 0; } + int getPathPositions(lua_State* L) { + ghoul::lua::checkArgumentsAndThrow(L, 1, "lua::getPathPositions"); + + const int pointsPerSegment = (int)ghoul::lua::value(L, 1); + + // Get sample positions from the current curve + AutoNavigationModule* module = global::moduleEngine.module(); + AutoNavigationHandler& handler = module->AutoNavigationHandler(); + std::vector points = handler.getCurvePositions(pointsPerSegment); + + // Push the points to the Lua stack: + lua_settop(L, 0); + const auto pushVector = [](lua_State* L, const glm::dvec3& v) { + lua_newtable(L); + ghoul::lua::push(L, 1, v.x); + lua_rawset(L, -3); + ghoul::lua::push(L, 2, v.y); + lua_rawset(L, -3); + ghoul::lua::push(L, 3, v.z); + lua_rawset(L, -3); + }; + + lua_newtable(L); + for (int i = 0; i < points.size(); ++i) { + ghoul::lua::push(L, i); + pushVector(L, points[i]); + lua_rawset(L, -3); + } + + ghoul_assert(lua_gettop(L) == 1, "Incorrect number of items left on stack"); + return 1; + } + } // namespace openspace::autonavigation::luascriptfunctions From cf8472cf340327e010d6ae331e1c2133f732e22f Mon Sep 17 00:00:00 2001 From: Lingis Date: Mon, 3 Feb 2020 10:58:58 -0500 Subject: [PATCH 033/912] Added accessors for rendering control points, for debugging --- .../autonavigation/autonavigationhandler.cpp | 20 +++++++++++ .../autonavigation/autonavigationhandler.h | 4 ++- .../autonavigation/autonavigationmodule.cpp | 7 ++++ .../autonavigationmodule_lua.inl | 35 +++++++++++++++++++ modules/autonavigation/pathcurves.cpp | 6 ++++ modules/autonavigation/pathcurves.h | 1 + modules/autonavigation/pathsegment.cpp | 5 +++ modules/autonavigation/pathsegment.h | 1 + 8 files changed, 78 insertions(+), 1 deletion(-) diff --git a/modules/autonavigation/autonavigationhandler.cpp b/modules/autonavigation/autonavigationhandler.cpp index c40e32ba07..155d432b95 100644 --- a/modules/autonavigation/autonavigationhandler.cpp +++ b/modules/autonavigation/autonavigationhandler.cpp @@ -240,6 +240,8 @@ void AutoNavigationHandler::stopPath() { _isPlaying = false; } +// TODO: remove when not needed +// Created for debugging std::vector AutoNavigationHandler::getCurvePositions(int nPerSegment) { std::vector positions; @@ -260,6 +262,24 @@ std::vector AutoNavigationHandler::getCurvePositions(int nPerSegment return positions; } +// TODO: remove when not needed +// Created for debugging +std::vector AutoNavigationHandler::getControlPoints() { + std::vector points; + + if (_pathSegments.empty()) { + LERROR("There is no current path to sample points from."); + return points; + } + + for (PathSegment &p : _pathSegments) { + std::vector curvePoints = p.getControlPoints(); + points.insert(points.end(), curvePoints.begin(), curvePoints.end()); + } + + return points; +} + bool AutoNavigationHandler::handleInstruction(const Instruction& instruction, int index) { bool success = true; switch (instruction.type) diff --git a/modules/autonavigation/autonavigationhandler.h b/modules/autonavigation/autonavigationhandler.h index 09c2dcd902..26c878a506 100644 --- a/modules/autonavigation/autonavigationhandler.h +++ b/modules/autonavigation/autonavigationhandler.h @@ -64,7 +64,9 @@ public: void continuePath(); void stopPath(); - std::vector getCurvePositions(int nPerSegment); + // TODO: remove functions for debugging + std::vector getCurvePositions(int nPerSegment); //debug + std::vector getControlPoints(); //debug private: bool handleInstruction(const Instruction& instruction, int index); diff --git a/modules/autonavigation/autonavigationmodule.cpp b/modules/autonavigation/autonavigationmodule.cpp index fc9bb5980b..27ab1554a6 100644 --- a/modules/autonavigation/autonavigationmodule.cpp +++ b/modules/autonavigation/autonavigationmodule.cpp @@ -93,6 +93,13 @@ scripting::LuaLibrary AutoNavigationModule::luaLibrary() const { {}, "number", "TODO: " + }, + { + "getControlPoints", + &autonavigation::luascriptfunctions::getControlPoints, + {}, + "", + "FOR DEBUG. Get control point positions from all pathsegments" } }; return res; diff --git a/modules/autonavigation/autonavigationmodule_lua.inl b/modules/autonavigation/autonavigationmodule_lua.inl index 9ad9a095f5..a4b1d4b728 100644 --- a/modules/autonavigation/autonavigationmodule_lua.inl +++ b/modules/autonavigation/autonavigationmodule_lua.inl @@ -168,6 +168,8 @@ namespace openspace::autonavigation::luascriptfunctions { return 0; } + // TODO: remove when not needed + // Created for debugging. Access info for rendereable path int getPathPositions(lua_State* L) { ghoul::lua::checkArgumentsAndThrow(L, 1, "lua::getPathPositions"); @@ -201,4 +203,37 @@ namespace openspace::autonavigation::luascriptfunctions { return 1; } + // TODO: remove when not needed + // Created for debugging. Access info for rendering of control points + int getControlPoints(lua_State* L) { + ghoul::lua::checkArgumentsAndThrow(L, 0, "lua::getControlPoints"); + + // Get sample positions from the current curve + AutoNavigationModule* module = global::moduleEngine.module(); + AutoNavigationHandler& handler = module->AutoNavigationHandler(); + std::vector points = handler.getControlPoints(); + + // Push the points to the Lua stack: + lua_settop(L, 0); + const auto pushVector = [](lua_State* L, const glm::dvec3& v) { + lua_newtable(L); + ghoul::lua::push(L, 1, v.x); + lua_rawset(L, -3); + ghoul::lua::push(L, 2, v.y); + lua_rawset(L, -3); + ghoul::lua::push(L, 3, v.z); + lua_rawset(L, -3); + }; + + lua_newtable(L); + for (int i = 0; i < points.size(); ++i) { + ghoul::lua::push(L, i); + pushVector(L, points[i]); + lua_rawset(L, -3); + } + + ghoul_assert(lua_gettop(L) == 1, "Incorrect number of items left on stack"); + return 1; + } + } // namespace openspace::autonavigation::luascriptfunctions diff --git a/modules/autonavigation/pathcurves.cpp b/modules/autonavigation/pathcurves.cpp index ee43404c84..b2d8fa87e4 100644 --- a/modules/autonavigation/pathcurves.cpp +++ b/modules/autonavigation/pathcurves.cpp @@ -49,6 +49,12 @@ double PathCurve::arcLength(double tLimit) { return sum; } +// TODO: remove when not needed +// Created for debugging +std::vector PathCurve::getPoints() { + return _points; +} + BezierCurve::BezierCurve(CameraState& start, CameraState& end) { glm::dvec3 startNodePos = sceneGraphNode(start.referenceNode)->worldPosition(); glm::dvec3 endNodePos = sceneGraphNode(start.referenceNode)->worldPosition(); diff --git a/modules/autonavigation/pathcurves.h b/modules/autonavigation/pathcurves.h index f165160783..a8a280f0d4 100644 --- a/modules/autonavigation/pathcurves.h +++ b/modules/autonavigation/pathcurves.h @@ -36,6 +36,7 @@ class PathCurve { public: virtual ~PathCurve() = 0; virtual glm::dvec3 valueAt(double t) = 0; + std::vector getPoints(); // for debugging double arcLength(double tLimit = 1.0); protected: diff --git a/modules/autonavigation/pathsegment.cpp b/modules/autonavigation/pathsegment.cpp index 120c061ce1..3c2f1ae79c 100644 --- a/modules/autonavigation/pathsegment.cpp +++ b/modules/autonavigation/pathsegment.cpp @@ -69,6 +69,11 @@ const double PathSegment::duration() const { return _duration; } const double PathSegment::startTime() const { return _startTime; } +// TODO: remove function for debugging +const std::vector PathSegment::getControlPoints() const { + return _curve->getPoints(); +} + const glm::vec3 PathSegment::getPositionAt(double t) const { t = easingfunctions::cubicEaseInOut(t); return _curve->valueAt(t); diff --git a/modules/autonavigation/pathsegment.h b/modules/autonavigation/pathsegment.h index 8cdfa5f668..383b20117f 100644 --- a/modules/autonavigation/pathsegment.h +++ b/modules/autonavigation/pathsegment.h @@ -58,6 +58,7 @@ public: const double duration() const; const double startTime() const; + const std::vector getControlPoints() const; // TODO: remove this debugging function const glm::vec3 getPositionAt(double t) const; const glm::dquat getRotationAt(double t) const; const glm::dquat getLookAtRotation(double t, glm::dvec3 currentPos, glm::dvec3 up) const; From 40d634d24997eedde9efaf738901c1e24561a3e3 Mon Sep 17 00:00:00 2001 From: Emma Broman Date: Mon, 3 Feb 2020 17:11:36 -0500 Subject: [PATCH 034/912] Fix precision error by getPosition returning vec3 instead of dvec3 --- modules/autonavigation/pathsegment.cpp | 4 +++- modules/autonavigation/pathsegment.h | 2 +- 2 files changed, 4 insertions(+), 2 deletions(-) diff --git a/modules/autonavigation/pathsegment.cpp b/modules/autonavigation/pathsegment.cpp index 120c061ce1..9d3761bf0b 100644 --- a/modules/autonavigation/pathsegment.cpp +++ b/modules/autonavigation/pathsegment.cpp @@ -69,12 +69,14 @@ const double PathSegment::duration() const { return _duration; } const double PathSegment::startTime() const { return _startTime; } -const glm::vec3 PathSegment::getPositionAt(double t) const { +const glm::dvec3 PathSegment::getPositionAt(double t) const { t = easingfunctions::cubicEaseInOut(t); return _curve->valueAt(t); } const glm::dquat PathSegment::getRotationAt(double t) const { + // TODO: improve how rotation is computed + double tSlerp = helpers::shiftAndScale(t, 0.1, 0.9); tSlerp = easingfunctions::cubicEaseInOut(tSlerp); diff --git a/modules/autonavigation/pathsegment.h b/modules/autonavigation/pathsegment.h index 8cdfa5f668..7f21f0eadf 100644 --- a/modules/autonavigation/pathsegment.h +++ b/modules/autonavigation/pathsegment.h @@ -58,7 +58,7 @@ public: const double duration() const; const double startTime() const; - const glm::vec3 getPositionAt(double t) const; + const glm::dvec3 getPositionAt(double t) const; const glm::dquat getRotationAt(double t) const; const glm::dquat getLookAtRotation(double t, glm::dvec3 currentPos, glm::dvec3 up) const; From d60ff85b5a367444319b621a5f7802073b6b195e Mon Sep 17 00:00:00 2001 From: Emma Broman Date: Tue, 4 Feb 2020 18:07:47 -0500 Subject: [PATCH 035/912] Remove custom easing functions. Use ghoul's instead --- modules/autonavigation/helperfunctions.cpp | 49 ---------------------- modules/autonavigation/pathsegment.cpp | 7 ++-- 2 files changed, 4 insertions(+), 52 deletions(-) diff --git a/modules/autonavigation/helperfunctions.cpp b/modules/autonavigation/helperfunctions.cpp index a063c5a2d6..453ce20018 100644 --- a/modules/autonavigation/helperfunctions.cpp +++ b/modules/autonavigation/helperfunctions.cpp @@ -36,55 +36,6 @@ namespace openspace::autonavigation::helpers { } // helpers -namespace openspace::autonavigation::easingfunctions { - -double linear(double t) { return t; }; - -double step(double t) { return (t > 0.5); }; - -double circularEaseOut(double p){ - return sqrt((2 - p) * p); -} - -double cubicEaseIn(double t) { return (t*t*t); }; - -double cubicEaseOut(double t) { - double p = 1 - t; - return (p*p*p); -} - -double cubicEaseInOut(double t) { - if (t < 0.5) { - return 4 * t * t * t; - } - else { - double f = ((2 * t) - 2); - return 0.5 * f * f * f + 1; - } -} - -double quadraticEaseInOut(double t) { - if (t < 0.5) { - return 2 * t * t; - } - else { - return (-2 * t * t) + (4 * t) - 1; - } -} - -double exponentialEaseInOut(double t) { - if (t == 0.0 || t == 1.0) return t; - - if (t < 0.5) { - return 0.5 * glm::pow(2, (20 * t) - 10); - } - else { - return -0.5 * glm::pow(2, (-20 * t) + 10) + 1; - } -} - -} // easingfunctions - namespace openspace::autonavigation::interpolation { // TODO: make all these into template functions diff --git a/modules/autonavigation/pathsegment.cpp b/modules/autonavigation/pathsegment.cpp index 9d3761bf0b..491650ec79 100644 --- a/modules/autonavigation/pathsegment.cpp +++ b/modules/autonavigation/pathsegment.cpp @@ -32,6 +32,7 @@ #include #include #include +#include namespace { constexpr const char* _loggerCat = "PathSegment"; @@ -70,7 +71,7 @@ const double PathSegment::duration() const { return _duration; } const double PathSegment::startTime() const { return _startTime; } const glm::dvec3 PathSegment::getPositionAt(double t) const { - t = easingfunctions::cubicEaseInOut(t); + t = ghoul::cubicEaseInOut(t); return _curve->valueAt(t); } @@ -78,10 +79,10 @@ const glm::dquat PathSegment::getRotationAt(double t) const { // TODO: improve how rotation is computed double tSlerp = helpers::shiftAndScale(t, 0.1, 0.9); - tSlerp = easingfunctions::cubicEaseInOut(tSlerp); + tSlerp = ghoul::cubicEaseInOut(tSlerp); double tLookAt = helpers::shiftAndScale(t, 0.2, 0.8); - tLookAt = easingfunctions::cubicEaseInOut(tLookAt); + tLookAt = ghoul::cubicEaseInOut(tLookAt); switch (_curveType) { case CurveType::Linear2: From 750ce7f9486c1fb40efe34a57e6eb67e757bbb8d Mon Sep 17 00:00:00 2001 From: Emma Broman Date: Wed, 5 Feb 2020 11:28:22 -0500 Subject: [PATCH 036/912] Replace lookAtRotation to a version that also works with navigationStates --- modules/autonavigation/pathsegment.cpp | 79 +++++++++++++++++--------- modules/autonavigation/pathsegment.h | 7 ++- 2 files changed, 56 insertions(+), 30 deletions(-) diff --git a/modules/autonavigation/pathsegment.cpp b/modules/autonavigation/pathsegment.cpp index fbc6dcb1d9..f0787c8604 100644 --- a/modules/autonavigation/pathsegment.cpp +++ b/modules/autonavigation/pathsegment.cpp @@ -75,48 +75,73 @@ const std::vector PathSegment::getControlPoints() const { return _curve->getPoints(); } -const glm::dvec3 PathSegment::getPositionAt(double t) const { +glm::dvec3 PathSegment::getPositionAt(double t) const { t = ghoul::cubicEaseInOut(t); return _curve->valueAt(t); } -const glm::dquat PathSegment::getRotationAt(double t) const { +glm::dquat PathSegment::getRotationAt(double t) const { // TODO: improve how rotation is computed - double tSlerp = helpers::shiftAndScale(t, 0.1, 0.9); - tSlerp = ghoul::cubicEaseInOut(tSlerp); - - double tLookAt = helpers::shiftAndScale(t, 0.2, 0.8); - tLookAt = ghoul::cubicEaseInOut(tLookAt); - switch (_curveType) { - case CurveType::Linear2: - case CurveType::Bezier3: - return getLookAtRotation( - tLookAt, - getPositionAt(t), - global::navigationHandler.camera()->lookUpVectorWorldSpace() - ); + case CurveType::Bezier3: + { + return piecewiseSlerpRotation(t); break; + } default: + { + double tSlerp = helpers::shiftAndScale(t, 0.1, 0.9); + tSlerp = ghoul::cubicEaseInOut(tSlerp); return glm::slerp(_start.rotation, _end.rotation, tSlerp); } + } } -const glm::dquat PathSegment::getLookAtRotation( - double t, glm::dvec3 currentPos, glm::dvec3 up) const -{ - glm::dvec3 startLookAtPos = sceneGraphNode(_start.referenceNode)->worldPosition(); - glm::dvec3 endLookAtPos = sceneGraphNode(_end.referenceNode)->worldPosition(); - glm::dvec3 lookAtPos = interpolation::linear(t, startLookAtPos, endLookAtPos); +const glm::dquat PathSegment::piecewiseSlerpRotation(double t) const { + // breakpoints for subintervals + const double t1 = 0.3; + const double t2 = 0.8; // TODO: these should probably be based on distance - glm::dmat4 lookAtMat = glm::lookAt( - currentPos, - lookAtPos, - up - ); + glm::dvec3 posAtT1 = getPositionAt(t1); + glm::dvec3 posAtT2 = getPositionAt(t2); - return glm::normalize(glm::inverse(glm::quat_cast(lookAtMat))); + // TODO: test looking at start resp. end position instead + glm::dvec3 startNodePos = sceneGraphNode(_start.referenceNode)->worldPosition(); + glm::dvec3 endNodePos = sceneGraphNode(_end.referenceNode)->worldPosition(); + + glm::dvec3 startUpVec = _start.rotation * glm::dvec3(0.0, 1.0, 0.0); + glm::dmat4 lookAtStartMat = glm::lookAt(posAtT1, startNodePos, startUpVec); + glm::dquat lookAtStartQuad = glm::normalize(glm::inverse(glm::quat_cast(lookAtStartMat))); + + glm::dvec3 endUpVec = _end.rotation * glm::dvec3(0.0, 1.0, 0.0); + glm::dmat4 lookAtEndMat = glm::lookAt(posAtT2, endNodePos, endUpVec); + glm::dquat lookAtEndQuad = glm::normalize(glm::inverse(glm::quat_cast(lookAtEndMat))); + + // Interpolate based on the subintervals + double tScaled; + glm::dquat startQuad, endQuad; + + if (t < t1) { + tScaled = t / t1; + startQuad = _start.rotation; + endQuad = lookAtStartQuad; + } + else if (t < t2) { + tScaled = (t - t1) / (t2 - t1); + startQuad = lookAtStartQuad; + endQuad = lookAtEndQuad; + } + else { // t <= 1.0 + tScaled = (t - t2) / (1.0 - t2); + startQuad = lookAtEndQuad; + endQuad = _end.rotation; + } + + // TODO: experiment with easing functions + tScaled = ghoul::quadraticEaseInOut(tScaled); + + return glm::slerp(startQuad, endQuad, tScaled); } // Initialise the curve, based on the start, end state and curve type diff --git a/modules/autonavigation/pathsegment.h b/modules/autonavigation/pathsegment.h index 4fcc03b926..5fb34cf6e8 100644 --- a/modules/autonavigation/pathsegment.h +++ b/modules/autonavigation/pathsegment.h @@ -59,11 +59,12 @@ public: const double startTime() const; const std::vector getControlPoints() const; // TODO: remove this debugging function - const glm::dvec3 getPositionAt(double t) const; - const glm::dquat getRotationAt(double t) const; - const glm::dquat getLookAtRotation(double t, glm::dvec3 currentPos, glm::dvec3 up) const; + + glm::dvec3 getPositionAt(double t) const; + glm::dquat getRotationAt(double t) const; private: + const glm::dquat piecewiseSlerpRotation(double t) const; void initCurve(); CameraState _start; From 2b00e3054bcc2202f284d7f15d058fd7df529cf1 Mon Sep 17 00:00:00 2001 From: Emma Broman Date: Wed, 5 Feb 2020 13:47:11 -0500 Subject: [PATCH 037/912] Remove forgotten comment --- modules/autonavigation/pathsegment.cpp | 1 - 1 file changed, 1 deletion(-) diff --git a/modules/autonavigation/pathsegment.cpp b/modules/autonavigation/pathsegment.cpp index f0787c8604..ec5c4ff1a4 100644 --- a/modules/autonavigation/pathsegment.cpp +++ b/modules/autonavigation/pathsegment.cpp @@ -106,7 +106,6 @@ const glm::dquat PathSegment::piecewiseSlerpRotation(double t) const { glm::dvec3 posAtT1 = getPositionAt(t1); glm::dvec3 posAtT2 = getPositionAt(t2); - // TODO: test looking at start resp. end position instead glm::dvec3 startNodePos = sceneGraphNode(_start.referenceNode)->worldPosition(); glm::dvec3 endNodePos = sceneGraphNode(_end.referenceNode)->worldPosition(); From e28435994feffae7cf3fc0a18233cfe51408118c Mon Sep 17 00:00:00 2001 From: Emma Broman Date: Wed, 5 Feb 2020 13:50:16 -0500 Subject: [PATCH 038/912] Fix warning related to implicit conversion from int to char --- modules/autonavigation/autonavigationhandler.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/modules/autonavigation/autonavigationhandler.cpp b/modules/autonavigation/autonavigationhandler.cpp index 155d432b95..7bedce3f2d 100644 --- a/modules/autonavigation/autonavigationhandler.cpp +++ b/modules/autonavigation/autonavigationhandler.cpp @@ -303,7 +303,7 @@ bool AutoNavigationHandler::handleInstruction(const Instruction& instruction, in } if (!success) { - LERROR(fmt::format("Failed handling instruction number {}.", index + 1)); + LERROR(fmt::format("Failed handling instruction number {}.", std::to_string(index + 1))); return false; } From ad09a0396edf6e05b4318480e4678f13d72170aa Mon Sep 17 00:00:00 2001 From: Emma Broman Date: Wed, 5 Feb 2020 14:35:51 -0500 Subject: [PATCH 039/912] Set the GuiScale to 1.0, to fix supersized GUI --- openspace.cfg | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/openspace.cfg b/openspace.cfg index e3eaaf4871..dfc39fb643 100644 --- a/openspace.cfg +++ b/openspace.cfg @@ -144,7 +144,7 @@ ModuleConfigurations = { WebSocketInterface = "DefaultWebSocketInterface" }, CefWebGui = { - GuiScale = 2.1, + GuiScale = 1.0, Enabled = true, Visible = true } From ef076acadb7c36f364050f8e4f298daa3aafb8b8 Mon Sep 17 00:00:00 2001 From: Lingis Date: Wed, 5 Feb 2020 16:09:33 -0500 Subject: [PATCH 040/912] readability and fixed correct direction of tangents --- modules/autonavigation/pathcurves.cpp | 136 ++++++++++++++------------ 1 file changed, 72 insertions(+), 64 deletions(-) diff --git a/modules/autonavigation/pathcurves.cpp b/modules/autonavigation/pathcurves.cpp index b2d8fa87e4..aa78c6e114 100644 --- a/modules/autonavigation/pathcurves.cpp +++ b/modules/autonavigation/pathcurves.cpp @@ -1,26 +1,26 @@ /***************************************************************************************** - * * - * OpenSpace * - * * - * Copyright (c) 2014-2019 * - * * - * Permission is hereby granted, free of charge, to any person obtaining a copy of this * - * software and associated documentation files (the "Software"), to deal in the Software * - * without restriction, including without limitation the rights to use, copy, modify, * - * merge, publish, distribute, sublicense, and/or sell copies of the Software, and to * - * permit persons to whom the Software is furnished to do so, subject to the following * - * conditions: * - * * - * The above copyright notice and this permission notice shall be included in all copies * - * or substantial portions of the Software. * - * * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, * - * INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A * - * PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT * - * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF * - * CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE * - * OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. * - ****************************************************************************************/ +* * +* OpenSpace * +* * +* Copyright (c) 2014-2019 * +* * +* Permission is hereby granted, free of charge, to any person obtaining a copy of this * +* software and associated documentation files (the "Software"), to deal in the Software * +* without restriction, including without limitation the rights to use, copy, modify, * +* merge, publish, distribute, sublicense, and/or sell copies of the Software, and to * +* permit persons to whom the Software is furnished to do so, subject to the following * +* conditions: * +* * +* The above copyright notice and this permission notice shall be included in all copies * +* or substantial portions of the Software. * +* * +* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, * +* INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A * +* PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT * +* HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF * +* CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE * +* OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. * +****************************************************************************************/ #include @@ -28,9 +28,10 @@ #include #include #include +#include namespace { - constexpr const char* _loggerCat = "PathCurve"; +constexpr const char* _loggerCat = "PathCurve"; } // namespace namespace openspace::autonavigation { @@ -106,60 +107,67 @@ glm::dvec3 Bezier2Curve::valueAt(double t) { } Bezier3Curve::Bezier3Curve(CameraState& start, CameraState& end) { - // TODO: CALCULATE AND SET CONDITION BOOLS IN CURVE CONSTRUCTOR + //TODO try to clean up using variable for nodes glm::dvec3 startNodePos = sceneGraphNode(start.referenceNode)->worldPosition(); - glm::dvec3 startDirection = start.position - startNodePos; - double startRadius = sceneGraphNode(start.referenceNode)->boundingSphere(); - double endRadius = sceneGraphNode(end.referenceNode)->boundingSphere(); - glm::dvec3 endNodePos = sceneGraphNode(end.referenceNode)->worldPosition(); - glm::dvec3 endDirection = end.position - endNodePos; + glm::dvec3 startNodeToStartPos = start.position - startNodePos; + glm::dvec3 endNodeToEndPos = end.position - endNodePos; + double startNodeRadius = sceneGraphNode(start.referenceNode)->boundingSphere(); + double endNodeRadius = sceneGraphNode(end.referenceNode)->boundingSphere(); - glm::dvec3 nodePosDiff = endNodePos - startNodePos; - double cosStartAngle = glm::dot(normalize(startDirection), normalize(nodePosDiff)); - double cosEndAngle = glm::dot(normalize(endDirection), normalize(nodePosDiff)); + glm::dvec3 startNodeToEndNode = endNodePos - startNodePos; + glm::dvec3 startToEnd = end.position - start.position; + double distance = length(startToEnd); - // TODO: Test with raycaster, test is dependent on start position - bool TARGET_BEHIND_STARTNODE = cosStartAngle < -0.8; - bool TARGET_ON_BACKSIDE = cosEndAngle > 0.8; + // Assuming we move straigh out to point to a distance proportional to radius, angle is enough to check collision risk + double cosStartAngle = glm::dot(normalize(startNodeToStartPos), normalize(startToEnd)); + double cosEndAngle = glm::dot(normalize(endNodeToEndPos), normalize(startToEnd)); + double cosStartDir = glm::dot(normalize(startNodeToStartPos), normalize(endNodeToEndPos)); + + //TODO: investigate suitable values, could be risky close to surface.. + bool TARGET_BEHIND_STARTNODE = cosStartAngle < -0.8; + bool TARGET_BEHIND_ENDNODE = cosEndAngle > 0.8; bool TARGET_IN_OPPOSITE_DIRECTION = cosStartAngle > 0.7; + // Variables that determine behaviour close to start and end + double startTangentLength = 2.0 * startNodeRadius; + double endTangentLength = 2.0 * endNodeRadius; // SET CONTROL POINTS + // Start by going outwards _points.push_back(start.position); - _points.push_back(start.position + 2.0 * startRadius * normalize(startDirection)); + _points.push_back(start.position + startTangentLength * normalize(startNodeToStartPos)); - if ( TARGET_BEHIND_STARTNODE ) - { - glm::dvec3 parallell = normalize(nodePosDiff) * glm::dot(startDirection, normalize(nodePosDiff)); - glm::dvec3 orthogonal = normalize(startDirection - parallell); - //Point on the side of start node - double dist = 5.0 * startRadius; + // Avoid collision with startnode by control points on the side of it + if (TARGET_BEHIND_STARTNODE) { + glm::dvec3 parallell = glm::proj(startNodeToStartPos, startNodeToEndNode); + glm::dvec3 orthogonal = normalize(startNodeToStartPos - parallell); + double dist = 5.0 * startNodeRadius; glm::dvec3 extraKnot = startNodePos + dist * orthogonal; - - _points.push_back(extraKnot - parallell); - _points.push_back(extraKnot); + _points.push_back(extraKnot + parallell); - } - - if (TARGET_IN_OPPOSITE_DIRECTION && ! TARGET_ON_BACKSIDE) { - glm::dvec3 parallell = normalize(nodePosDiff * glm::dot(startDirection, normalize(nodePosDiff))); - glm::dvec3 orthogonal = normalize(normalize(startDirection) - parallell); - // Distant middle point - double dist = 0.5 * length(nodePosDiff); - glm::dvec3 extraKnot = startNodePos - dist * parallell + 3.0 * dist * orthogonal; - - _points.push_back(extraKnot - 0.5 * dist * parallell); _points.push_back(extraKnot); - _points.push_back(extraKnot + 0.5 * dist * parallell); + _points.push_back(extraKnot - parallell); } - - if (TARGET_ON_BACKSIDE) - { - glm::dvec3 parallell = normalize(nodePosDiff) * glm::dot(endDirection, normalize(nodePosDiff)); - glm::dvec3 orthogonal = normalize(endDirection - parallell); - //Point on the side of start node - double dist = 5.0 * endRadius; + + // Zoom out, to get a better understanding in a 180 degree turn situation + if (TARGET_IN_OPPOSITE_DIRECTION) { + glm::dvec3 parallell = glm::proj(startNodeToStartPos, startNodeToEndNode); + glm::dvec3 orthogonal = normalize(startNodeToStartPos - parallell); + double dist = 0.5 * length(startNodeToEndNode); + // Distant middle point + glm::dvec3 extraKnot = startNodePos + dist * normalize(parallell) + 3.0 * dist * orthogonal; + + _points.push_back(extraKnot - 0.3 * dist * normalize(parallell)); + _points.push_back(extraKnot); + _points.push_back(extraKnot + 0.3 * dist * normalize(parallell)); + } + + // Avoid collision with endnode by adding a control points on the side of it + if (TARGET_BEHIND_ENDNODE) { + glm::dvec3 parallell = normalize(startNodeToEndNode) * glm::dot(endNodeToEndPos, normalize(startNodeToEndNode)); + glm::dvec3 orthogonal = normalize(endNodeToEndPos - parallell); + double dist = 5.0 * endNodeRadius; glm::dvec3 extraKnot = endNodePos + dist * orthogonal; _points.push_back(extraKnot - parallell); @@ -167,7 +175,7 @@ Bezier3Curve::Bezier3Curve(CameraState& start, CameraState& end) { _points.push_back(extraKnot + parallell); } - _points.push_back(end.position + 2.0 * endRadius * normalize(endDirection)); + _points.push_back(end.position + endTangentLength * normalize(endNodeToEndPos)); _points.push_back(end.position); } From 2370679fce4da4f24e747bdbd28804087c3a7d7c Mon Sep 17 00:00:00 2001 From: Lingis Date: Wed, 5 Feb 2020 16:41:29 -0500 Subject: [PATCH 041/912] Removed Bezier, Bezier2 and Linear2 curve types --- .../autonavigation/autonavigationhandler.cpp | 11 +-- modules/autonavigation/pathcurves.cpp | 75 ------------------- modules/autonavigation/pathcurves.h | 18 ----- modules/autonavigation/pathsegment.cpp | 9 --- modules/autonavigation/pathsegment.h | 3 - 5 files changed, 1 insertion(+), 115 deletions(-) diff --git a/modules/autonavigation/autonavigationhandler.cpp b/modules/autonavigation/autonavigationhandler.cpp index 7bedce3f2d..a8dcd213cb 100644 --- a/modules/autonavigation/autonavigationhandler.cpp +++ b/modules/autonavigation/autonavigationhandler.cpp @@ -52,21 +52,12 @@ CurveType stringToCurveType(std::string str) { std::transform(str.begin(), str.end(), str.begin(), ::tolower); - if (str == "bezier") { - return CurveType::Bezier; - } - else if (str == "bezier2") { - return CurveType::Bezier2; - } - else if (str == "bezier3") { + if (str == "bezier3") { return CurveType::Bezier3; } else if (str == "linear") { return CurveType::Linear; } - else if (str == "linear2") { - return CurveType::Linear2; - } else { LERROR(fmt::format("'{}' is not a valid curve type! Choosing default.", str)); return CurveType::None; diff --git a/modules/autonavigation/pathcurves.cpp b/modules/autonavigation/pathcurves.cpp index b2d8fa87e4..148da030bb 100644 --- a/modules/autonavigation/pathcurves.cpp +++ b/modules/autonavigation/pathcurves.cpp @@ -55,56 +55,6 @@ std::vector PathCurve::getPoints() { return _points; } -BezierCurve::BezierCurve(CameraState& start, CameraState& end) { - glm::dvec3 startNodePos = sceneGraphNode(start.referenceNode)->worldPosition(); - glm::dvec3 endNodePos = sceneGraphNode(start.referenceNode)->worldPosition(); - // vectors pointing away from target nodes - glm::dvec3 startDirection = start.position - startNodePos; - glm::dvec3 endDirection = end.position - endNodePos; - - _points.push_back(start.position); - _points.push_back(start.position + 10.0 * startDirection); - _points.push_back(end.position + 10.0 * endDirection); - _points.push_back(end.position); -} - -glm::dvec3 BezierCurve::valueAt(double t) { - return interpolation::cubicBezier(t, - _points[0], _points[1], _points[2], _points[3]); -} - -Bezier2Curve::Bezier2Curve(CameraState& start, CameraState& end) { - // START: - glm::dvec3 startNodePos = sceneGraphNode(start.referenceNode)->worldPosition(); - glm::dvec3 startDirection = start.position - startNodePos; - - // END: - glm::dvec3 endNodePos = sceneGraphNode(end.referenceNode)->worldPosition(); - glm::dvec3 endDirection = end.position - endNodePos; - - // MIDDLE: one knot and two control points parallell to target nodes - glm::dvec3 AB = endNodePos - startNodePos; - glm::dvec3 C = normalize(startDirection + endDirection); - glm::dvec3 CparAB = glm::dot(C, normalize(AB))* normalize(AB); - glm::dvec3 CortAB = normalize(C - CparAB); - double d = glm::length(AB); - - // TODO: set points that actually look good - _points.push_back(start.position); - _points.push_back(start.position + 2.0 * startDirection); - - _points.push_back(start.position + 1.5 * d * CortAB); - _points.push_back(start.position + 1.5 * d * CortAB + 0.5 * AB); - _points.push_back(end.position + 1.5 * d * CortAB); - - _points.push_back(end.position + 2.0 * endDirection); - _points.push_back(end.position); -} - -glm::dvec3 Bezier2Curve::valueAt(double t) { - return interpolation::piecewiseCubicBezier(t, _points); -} - Bezier3Curve::Bezier3Curve(CameraState& start, CameraState& end) { // TODO: CALCULATE AND SET CONDITION BOOLS IN CURVE CONSTRUCTOR glm::dvec3 startNodePos = sceneGraphNode(start.referenceNode)->worldPosition(); @@ -184,31 +134,6 @@ glm::dvec3 LinearCurve::valueAt(double t) { return interpolation::linear(t, _points[0], _points[1]); } -Linear2Curve::Linear2Curve(CameraState& start, CameraState& end) { - // START: - glm::dvec3 startNodePos = sceneGraphNode(start.referenceNode)->worldPosition(); - glm::dvec3 startDirection = start.position - startNodePos; - - // END: - glm::dvec3 endNodePos = sceneGraphNode(end.referenceNode)->worldPosition(); - glm::dvec3 endDirection = end.position - endNodePos; - - // MIDDLE: - glm::dvec3 AB = endNodePos - startNodePos; - glm::dvec3 C = normalize(startDirection + endDirection); - glm::dvec3 CparAB = glm::dot(C, normalize(AB))* normalize(AB); - glm::dvec3 CortAB = normalize(C - CparAB); - double d = glm::length(AB); - - _points.push_back(start.position); - _points.push_back(start.position + 2.0 * d * CortAB + 0.5 * AB); //TODO: use scale instead of 2.0 - _points.push_back(end.position); -} - -glm::dvec3 Linear2Curve::valueAt(double t) { - return interpolation::piecewiseLinear(t, _points); -} - // TODO: Iprove handling of pauses PauseCurve::PauseCurve(CameraState& state) { _points.push_back(state.position); diff --git a/modules/autonavigation/pathcurves.h b/modules/autonavigation/pathcurves.h index a8a280f0d4..56d9353811 100644 --- a/modules/autonavigation/pathcurves.h +++ b/modules/autonavigation/pathcurves.h @@ -44,18 +44,6 @@ protected: std::vector _points; }; -class BezierCurve : public PathCurve { -public : - BezierCurve(CameraState& start, CameraState& end); - glm::dvec3 valueAt(double t); -}; - -class Bezier2Curve : public PathCurve { -public: - Bezier2Curve(CameraState& start, CameraState& end); - glm::dvec3 valueAt(double t); -}; - class Bezier3Curve : public PathCurve { public: Bezier3Curve(CameraState& start, CameraState& end); @@ -68,12 +56,6 @@ public: glm::dvec3 valueAt(double t); }; -class Linear2Curve : public PathCurve { -public: - Linear2Curve(CameraState& start, CameraState& end); - glm::dvec3 valueAt(double t); -}; - // OBS! This is a temporary class specialised for handling pauses. // TODO: handle better in the future. class PauseCurve : public PathCurve { diff --git a/modules/autonavigation/pathsegment.cpp b/modules/autonavigation/pathsegment.cpp index ec5c4ff1a4..117ecfe5b8 100644 --- a/modules/autonavigation/pathsegment.cpp +++ b/modules/autonavigation/pathsegment.cpp @@ -149,21 +149,12 @@ void PathSegment::initCurve() { _curve.reset(); switch (_curveType) { - case CurveType::Bezier: - _curve = std::make_shared(_start, _end); - break; - case CurveType::Bezier2: - _curve = std::make_shared(_start, _end); - break; case CurveType::Bezier3: _curve = std::make_shared(_start, _end); break; case CurveType::Linear: _curve = std::make_shared(_start, _end); break; - case CurveType::Linear2: - _curve = std::make_shared(_start, _end); - break; case CurveType::Pause: _curve = std::make_shared(_start); break; diff --git a/modules/autonavigation/pathsegment.h b/modules/autonavigation/pathsegment.h index 5fb34cf6e8..f0b32892a0 100644 --- a/modules/autonavigation/pathsegment.h +++ b/modules/autonavigation/pathsegment.h @@ -32,11 +32,8 @@ namespace openspace::autonavigation { enum class CurveType { - Bezier, - Bezier2, Bezier3, Linear, - Linear2, Pause, // OBS! Temporary special case for handling pauses None }; From 473d32de87074f60c18f1bf733f495fcac7fee4e Mon Sep 17 00:00:00 2001 From: Emma Broman Date: Thu, 6 Feb 2020 11:45:27 -0500 Subject: [PATCH 042/912] Also remove easing functions in header file... (woops) --- modules/autonavigation/helperfunctions.h | 22 ---------------------- 1 file changed, 22 deletions(-) diff --git a/modules/autonavigation/helperfunctions.h b/modules/autonavigation/helperfunctions.h index c800fcaf79..25fdd69e74 100644 --- a/modules/autonavigation/helperfunctions.h +++ b/modules/autonavigation/helperfunctions.h @@ -38,28 +38,6 @@ namespace openspace::autonavigation::helpers { } // helpers -namespace openspace::autonavigation::easingfunctions { - -// Based on functions from https://github.com/warrenm/AHEasing/blob/master/AHEasing/easing.c - -double linear(double t); - -double step(double t); - -double circularEaseOut(double p); - -double cubicEaseIn(double t); - -double cubicEaseOut(double t); - -double cubicEaseInOut(double t); - -double quadraticEaseInOut(double t); - -double exponentialEaseInOut(double t); - -} - // TODO: include interpolator.h to helperfunctions // error when interpolator.h is included and used both here and in pathsegment From bf434ee3ee650bf1c1537ee4b7c99f58972caec5 Mon Sep 17 00:00:00 2001 From: Lingis Date: Thu, 6 Feb 2020 13:37:10 -0500 Subject: [PATCH 043/912] Same anchor bug fix and readability in bezier3 --- modules/autonavigation/pathcurves.cpp | 110 +++++++++++++------------- 1 file changed, 56 insertions(+), 54 deletions(-) diff --git a/modules/autonavigation/pathcurves.cpp b/modules/autonavigation/pathcurves.cpp index 032166b99f..ec038b4825 100644 --- a/modules/autonavigation/pathcurves.cpp +++ b/modules/autonavigation/pathcurves.cpp @@ -57,75 +57,77 @@ std::vector PathCurve::getPoints() { } Bezier3Curve::Bezier3Curve(CameraState& start, CameraState& end) { - //TODO try to clean up using variable for nodes glm::dvec3 startNodePos = sceneGraphNode(start.referenceNode)->worldPosition(); glm::dvec3 endNodePos = sceneGraphNode(end.referenceNode)->worldPosition(); - glm::dvec3 startNodeToStartPos = start.position - startNodePos; - glm::dvec3 endNodeToEndPos = end.position - endNodePos; double startNodeRadius = sceneGraphNode(start.referenceNode)->boundingSphere(); double endNodeRadius = sceneGraphNode(end.referenceNode)->boundingSphere(); - glm::dvec3 startNodeToEndNode = endNodePos - startNodePos; - glm::dvec3 startToEnd = end.position - start.position; - double distance = length(startToEnd); + glm::dvec3 startNodeToStartPos = start.position - startNodePos; + glm::dvec3 endNodeToEndPos = end.position - endNodePos; - // Assuming we move straigh out to point to a distance proportional to radius, angle is enough to check collision risk - double cosStartAngle = glm::dot(normalize(startNodeToStartPos), normalize(startToEnd)); - double cosEndAngle = glm::dot(normalize(endNodeToEndPos), normalize(startToEnd)); - double cosStartDir = glm::dot(normalize(startNodeToStartPos), normalize(endNodeToEndPos)); - - //TODO: investigate suitable values, could be risky close to surface.. - bool TARGET_BEHIND_STARTNODE = cosStartAngle < -0.8; - bool TARGET_BEHIND_ENDNODE = cosEndAngle > 0.8; - bool TARGET_IN_OPPOSITE_DIRECTION = cosStartAngle > 0.7; - - // Variables that determine behaviour close to start and end double startTangentLength = 2.0 * startNodeRadius; double endTangentLength = 2.0 * endNodeRadius; + glm::dvec3 startTangentDirection = normalize(startNodeToStartPos); + glm::dvec3 endTangentDirection = normalize(endNodeToEndPos); - // SET CONTROL POINTS // Start by going outwards _points.push_back(start.position); - _points.push_back(start.position + startTangentLength * normalize(startNodeToStartPos)); + _points.push_back(start.position + startTangentLength * startTangentDirection); - // Avoid collision with startnode by control points on the side of it - if (TARGET_BEHIND_STARTNODE) { - glm::dvec3 parallell = glm::proj(startNodeToStartPos, startNodeToEndNode); - glm::dvec3 orthogonal = normalize(startNodeToStartPos - parallell); - double dist = 5.0 * startNodeRadius; - glm::dvec3 extraKnot = startNodePos + dist * orthogonal; + if (start.referenceNode != end.referenceNode) { - _points.push_back(extraKnot + parallell); - _points.push_back(extraKnot); - _points.push_back(extraKnot - parallell); + glm::dvec3 startNodeToEndNode = endNodePos - startNodePos; + glm::dvec3 startToEndDirection = normalize(end.position - start.position); + + // Assuming we move straigh out to point to a distance proportional to radius, angle is enough to check collision risk + double cosStartAngle = glm::dot(startTangentDirection, startToEndDirection); + double cosEndAngle = glm::dot(endTangentDirection, startToEndDirection); + double cosStartDir = glm::dot(startTangentDirection, endTangentDirection); + + //TODO: investigate suitable values, could be risky close to surface.. + bool TARGET_BEHIND_STARTNODE = cosStartAngle < -0.8; + bool TARGET_BEHIND_ENDNODE = cosEndAngle > 0.8; + bool TARGET_IN_OPPOSITE_DIRECTION = cosStartAngle > 0.7; + + // Avoid collision with startnode by adding control points on the side of it + if (TARGET_BEHIND_STARTNODE) { + glm::dvec3 parallell = glm::proj(startNodeToStartPos, startNodeToEndNode); + glm::dvec3 orthogonal = normalize(startNodeToStartPos - parallell); + double dist = 5.0 * startNodeRadius; + glm::dvec3 extraKnot = startNodePos + dist * orthogonal; + + _points.push_back(extraKnot + parallell); + _points.push_back(extraKnot); + _points.push_back(extraKnot - parallell); + } + + // Zoom out, to get a better understanding in a 180 degree turn situation + if (TARGET_IN_OPPOSITE_DIRECTION) { + glm::dvec3 parallell = glm::proj(startNodeToStartPos, startNodeToEndNode); + glm::dvec3 orthogonal = normalize(startNodeToStartPos - parallell); + double dist = 0.5 * length(startNodeToEndNode); + // Distant middle point + glm::dvec3 extraKnot = startNodePos + dist * normalize(parallell) + 3.0 * dist * orthogonal; + + _points.push_back(extraKnot - 0.3 * dist * normalize(parallell)); + _points.push_back(extraKnot); + _points.push_back(extraKnot + 0.3 * dist * normalize(parallell)); + } + + // Avoid collision with endnode by adding control points on the side of it + if (TARGET_BEHIND_ENDNODE) { + glm::dvec3 parallell = glm::proj(endNodeToEndPos, startNodeToEndNode); + glm::dvec3 orthogonal = normalize(endNodeToEndPos - parallell); + double dist = 5.0 * endNodeRadius; + glm::dvec3 extraKnot = endNodePos + dist * orthogonal; + + _points.push_back(extraKnot - parallell); + _points.push_back(extraKnot); + _points.push_back(extraKnot + parallell); + } } - // Zoom out, to get a better understanding in a 180 degree turn situation - if (TARGET_IN_OPPOSITE_DIRECTION) { - glm::dvec3 parallell = glm::proj(startNodeToStartPos, startNodeToEndNode); - glm::dvec3 orthogonal = normalize(startNodeToStartPos - parallell); - double dist = 0.5 * length(startNodeToEndNode); - // Distant middle point - glm::dvec3 extraKnot = startNodePos + dist * normalize(parallell) + 3.0 * dist * orthogonal; - - _points.push_back(extraKnot - 0.3 * dist * normalize(parallell)); - _points.push_back(extraKnot); - _points.push_back(extraKnot + 0.3 * dist * normalize(parallell)); - } - - // Avoid collision with endnode by adding a control points on the side of it - if (TARGET_BEHIND_ENDNODE) { - glm::dvec3 parallell = normalize(startNodeToEndNode) * glm::dot(endNodeToEndPos, normalize(startNodeToEndNode)); - glm::dvec3 orthogonal = normalize(endNodeToEndPos - parallell); - double dist = 5.0 * endNodeRadius; - glm::dvec3 extraKnot = endNodePos + dist * orthogonal; - - _points.push_back(extraKnot - parallell); - _points.push_back(extraKnot); - _points.push_back(extraKnot + parallell); - } - - _points.push_back(end.position + endTangentLength * normalize(endNodeToEndPos)); + _points.push_back(end.position + endTangentLength * endTangentDirection); _points.push_back(end.position); } From ffe417823f91a3b6992e09c8c47851648d15a4c6 Mon Sep 17 00:00:00 2001 From: Emma Broman Date: Tue, 11 Feb 2020 11:35:56 -0500 Subject: [PATCH 044/912] Refactor piecewise slerp a bit --- modules/autonavigation/helperfunctions.cpp | 5 ++ modules/autonavigation/helperfunctions.h | 2 + modules/autonavigation/pathsegment.cpp | 55 ++++++++++------------ 3 files changed, 33 insertions(+), 29 deletions(-) diff --git a/modules/autonavigation/helperfunctions.cpp b/modules/autonavigation/helperfunctions.cpp index 453ce20018..5722567fee 100644 --- a/modules/autonavigation/helperfunctions.cpp +++ b/modules/autonavigation/helperfunctions.cpp @@ -34,6 +34,11 @@ namespace openspace::autonavigation::helpers { return std::max(0.0, std::min(tScaled, 1.0)); } + glm::dquat getLookAtQuaternion(glm::dvec3 eye, glm::dvec3 center, glm::dvec3 up) { + glm::dmat4 lookAtMat = glm::lookAt(eye, center, up); + return glm::normalize(glm::inverse(glm::quat_cast(lookAtMat))); + } + } // helpers namespace openspace::autonavigation::interpolation { diff --git a/modules/autonavigation/helperfunctions.h b/modules/autonavigation/helperfunctions.h index 25fdd69e74..2aeff1180f 100644 --- a/modules/autonavigation/helperfunctions.h +++ b/modules/autonavigation/helperfunctions.h @@ -36,6 +36,8 @@ namespace openspace::autonavigation::helpers { // Make interpolator parameter t [0,1] progress only inside a subinterval double shiftAndScale(double t, double newStart, double newEnd); + glm::dquat getLookAtQuaternion(glm::dvec3 eye, glm::dvec3 center, glm::dvec3 up); + } // helpers // TODO: include interpolator.h to helperfunctions diff --git a/modules/autonavigation/pathsegment.cpp b/modules/autonavigation/pathsegment.cpp index 117ecfe5b8..747ae2a206 100644 --- a/modules/autonavigation/pathsegment.cpp +++ b/modules/autonavigation/pathsegment.cpp @@ -98,49 +98,46 @@ glm::dquat PathSegment::getRotationAt(double t) const { } } +// Interpolate between a number of keyframes for orientation using SLERP const glm::dquat PathSegment::piecewiseSlerpRotation(double t) const { // breakpoints for subintervals const double t1 = 0.3; const double t2 = 0.8; // TODO: these should probably be based on distance - - glm::dvec3 posAtT1 = getPositionAt(t1); - glm::dvec3 posAtT2 = getPositionAt(t2); + std::vector times{ 0.0, t1, t2, 1.0 }; glm::dvec3 startNodePos = sceneGraphNode(_start.referenceNode)->worldPosition(); glm::dvec3 endNodePos = sceneGraphNode(_end.referenceNode)->worldPosition(); glm::dvec3 startUpVec = _start.rotation * glm::dvec3(0.0, 1.0, 0.0); - glm::dmat4 lookAtStartMat = glm::lookAt(posAtT1, startNodePos, startUpVec); - glm::dquat lookAtStartQuad = glm::normalize(glm::inverse(glm::quat_cast(lookAtStartMat))); - glm::dvec3 endUpVec = _end.rotation * glm::dvec3(0.0, 1.0, 0.0); - glm::dmat4 lookAtEndMat = glm::lookAt(posAtT2, endNodePos, endUpVec); - glm::dquat lookAtEndQuad = glm::normalize(glm::inverse(glm::quat_cast(lookAtEndMat))); - // Interpolate based on the subintervals - double tScaled; - glm::dquat startQuad, endQuad; + glm::dquat lookAtStartQ = + helpers::getLookAtQuaternion(getPositionAt(t1), startNodePos, startUpVec); - if (t < t1) { - tScaled = t / t1; - startQuad = _start.rotation; - endQuad = lookAtStartQuad; - } - else if (t < t2) { - tScaled = (t - t1) / (t2 - t1); - startQuad = lookAtStartQuad; - endQuad = lookAtEndQuad; - } - else { // t <= 1.0 - tScaled = (t - t2) / (1.0 - t2); - startQuad = lookAtEndQuad; - endQuad = _end.rotation; + glm::dquat lookAtEndQ = + helpers::getLookAtQuaternion(getPositionAt(t2), endNodePos, endUpVec); + + std::vector keyframes{ + _start.rotation, + lookAtStartQ, + lookAtEndQ, + _end.rotation + }; + + ghoul_assert(keyframes.size() == times.size(), "Must have one time value per keyframe."); + + // Find the current segment and compute interpolation + glm::dquat result; + for (int i = 0; i < keyframes.size() - 1; ++i) { + if (t <= times[i + 1]) { + double tScaled = (t - times[i]) / (times[i + 1] - times[i]); + tScaled = ghoul::quadraticEaseInOut(tScaled); + result = glm::slerp(keyframes[i], keyframes[i + 1], tScaled); + break; + } } - // TODO: experiment with easing functions - tScaled = ghoul::quadraticEaseInOut(tScaled); - - return glm::slerp(startQuad, endQuad, tScaled); + return result; } // Initialise the curve, based on the start, end state and curve type From 5f006f139b5a7f4199b60ebfc6bf108bf57224ab Mon Sep 17 00:00:00 2001 From: Lingis Date: Wed, 12 Feb 2020 10:12:47 -0500 Subject: [PATCH 045/912] Not correct, wrong shape of curve created in Bezier::valueAt --- modules/autonavigation/helperfunctions.cpp | 30 ++++++----- modules/autonavigation/helperfunctions.h | 4 +- modules/autonavigation/pathcurves.cpp | 63 +++++++++++++++++++++- modules/autonavigation/pathcurves.h | 6 +++ 4 files changed, 88 insertions(+), 15 deletions(-) diff --git a/modules/autonavigation/helperfunctions.cpp b/modules/autonavigation/helperfunctions.cpp index 453ce20018..59144cfdef 100644 --- a/modules/autonavigation/helperfunctions.cpp +++ b/modules/autonavigation/helperfunctions.cpp @@ -53,29 +53,35 @@ namespace openspace::autonavigation::interpolation { glm::dvec3 linear(double t, const glm::dvec3 &cp1, const glm::dvec3 &cp2) { return cp1 * (1.0 - t) + cp2 * t; } - - glm::dvec3 piecewiseCubicBezier(double t, const std::vector &points) { + /* + // Interpolate a list of control points and knot times + glm::dvec3 piecewiseCubicBezier(double t, std::vector ×, const std::vector &points) { size_t n = points.size(); ghoul_assert(n > 4, "Minimum of four control points needed for interpolation!"); - ghoul_assert((n - 1) % 3 == 0, "A vector containing 3n + 1 control points must be provided!"); size_t nrSegments = (size_t)std::floor((n - 1) / 3.0); - - // for points equally spaced in time - double tSegment = std::fmod(t*nrSegments, 1.0); - tSegment = std::max(0.0, std::min(tSegment, 1.0)); - - size_t idx = std::floor(t*nrSegments) * 3; - - // prevent stepping past the last segment if t = 1.0 - if (idx > n - 4) { + int nrTimes = times.size(); + ghoul_assert(nrSegments == nrTimes - 1, "Number of interval times must match number of intervals"); + + if (abs(1.0-t) < 0.00001) return points.back(); + if (nrTimes < 1) { + return points[0]; } + + // find current segmant and scale time + std::vector::iterator low = std::lower_bound(times.begin(), times.end(), t); + int segmentIdx = low - times.begin(); + + double tSegment = (t - times[segmentIdx]) / (times[segmentIdx + 1] - times[segmentIdx]); + + int idx = segmentIdx * 3; return cubicBezier(tSegment, points[idx], points[idx + 1], points[idx + 2], points[idx + 3]); } + */ glm::dvec3 piecewiseLinear(double t, const std::vector &points) { size_t n = points.size(); diff --git a/modules/autonavigation/helperfunctions.h b/modules/autonavigation/helperfunctions.h index 25fdd69e74..d6be2342c5 100644 --- a/modules/autonavigation/helperfunctions.h +++ b/modules/autonavigation/helperfunctions.h @@ -30,6 +30,7 @@ #include #include #include +#include namespace openspace::autonavigation::helpers { @@ -50,7 +51,8 @@ namespace openspace::autonavigation::interpolation { glm::dvec3 linear(double t, const glm::dvec3 &cp1, const glm::dvec3 &cp2); - glm::dvec3 piecewiseCubicBezier(double t, const std::vector &controlPoints); + // Inside bezier3 + // glm::dvec3 piecewiseCubicBezier(double t, std::vector ×, const std::vector &points); glm::dvec3 piecewiseLinear(double t, const std::vector &controlPoints); diff --git a/modules/autonavigation/pathcurves.cpp b/modules/autonavigation/pathcurves.cpp index ec038b4825..a13776ceee 100644 --- a/modules/autonavigation/pathcurves.cpp +++ b/modules/autonavigation/pathcurves.cpp @@ -129,10 +129,69 @@ Bezier3Curve::Bezier3Curve(CameraState& start, CameraState& end) { _points.push_back(end.position + endTangentLength * endTangentDirection); _points.push_back(end.position); + + + // initial interval times + int nrIntervals = (int)std::floor((_points.size() - 1) / 3.0); //TODO valivate! + + for (double time = 0.0; time <= 1.0; time += 1.0 / nrIntervals) { + _intervalTimes.push_back(time); + } + // TODO: test that time == 1 is included + + reparameterizeByArcLength(); +} +/* +glm::dvec3 Bezier3Curve::valueAt(double t) { + return interpolation::piecewiseCubicBezier(t, _intervalTimes, _points); +} +*/ + +// Interpolate a list of control points and knot times +glm::dvec3 Bezier3Curve::valueAt(double t) { + size_t n = _points.size(); + ghoul_assert(n > 4, "Minimum of four control points needed for interpolation!"); + ghoul_assert((n - 1) % 3 == 0, "A vector containing 3n + 1 control points must be provided!"); + + size_t nrSegments = (size_t)std::floor((n - 1) / 3.0); + int nrTimes = _intervalTimes.size(); + ghoul_assert(nrSegments == nrTimes - 1, "Number of interval times must match number of intervals"); + + if (abs(1.0 - t) < 0.00001) + return _points.back(); + if (nrTimes < 1) { + return _points[0]; + } + + // find current segmant and scale time + std::vector::iterator low = std::lower_bound(_intervalTimes.begin(), _intervalTimes.end(), t); + int segmentIdx = low - _intervalTimes.begin(); + + double tSegment = (t - _intervalTimes[segmentIdx]) / (_intervalTimes[segmentIdx + 1] - _intervalTimes[segmentIdx]); + + int idx = segmentIdx * 3; + + return interpolation::cubicBezier(tSegment, _points[idx], _points[idx + 1], + _points[idx + 2], _points[idx + 3]); } -glm::dvec3 Bezier3Curve::valueAt(double t) { - return interpolation::piecewiseCubicBezier(t, _points); +void Bezier3Curve::reparameterizeByArcLength() { + // For bezier every third is a knot shaed beetween segments + int nrIntervals = (int)std::floor((float)(_points.size() - 1) / 3.0); //TODO valivate! + + if (nrIntervals == 1) { + return; + } + + double totalLength = arcLength(1.0); //THIS IS WHERE IT GOES INTO INTERPOLATOR WITHOUT ANY VALUES IN TIMES VECTOR IF >! SEGMENTS OTHERWISE IN PATHSEGMENT::INITCURVE + std::vector newTimes; + + // Save time as relative curve length from start to each knot + for ( double time : _intervalTimes) { + newTimes.push_back(arcLength(time) / totalLength); + } + + _intervalTimes.swap(newTimes); } LinearCurve::LinearCurve(CameraState& start, CameraState& end) { diff --git a/modules/autonavigation/pathcurves.h b/modules/autonavigation/pathcurves.h index 56d9353811..051eb39ac6 100644 --- a/modules/autonavigation/pathcurves.h +++ b/modules/autonavigation/pathcurves.h @@ -39,6 +39,7 @@ public: std::vector getPoints(); // for debugging double arcLength(double tLimit = 1.0); + protected: // the points used for creating the curve (e.g. control points of a Bezier curve) std::vector _points; @@ -48,6 +49,11 @@ class Bezier3Curve : public PathCurve { public: Bezier3Curve(CameraState& start, CameraState& end); glm::dvec3 valueAt(double t); + +private: + void reparameterizeByArcLength(); + + std::vector _intervalTimes; }; class LinearCurve : public PathCurve { From 066b91b07601cb7d877bef347c9821526c9e290d Mon Sep 17 00:00:00 2001 From: Lingis Date: Wed, 12 Feb 2020 10:12:47 -0500 Subject: [PATCH 046/912] Not correct, wrong shape of curve created in Bezier::valueAt --- modules/autonavigation/helperfunctions.cpp | 30 ++++++----- modules/autonavigation/helperfunctions.h | 4 +- modules/autonavigation/pathcurves.cpp | 63 +++++++++++++++++++++- modules/autonavigation/pathcurves.h | 6 +++ 4 files changed, 88 insertions(+), 15 deletions(-) diff --git a/modules/autonavigation/helperfunctions.cpp b/modules/autonavigation/helperfunctions.cpp index 5722567fee..8a4c9786fa 100644 --- a/modules/autonavigation/helperfunctions.cpp +++ b/modules/autonavigation/helperfunctions.cpp @@ -58,29 +58,35 @@ namespace openspace::autonavigation::interpolation { glm::dvec3 linear(double t, const glm::dvec3 &cp1, const glm::dvec3 &cp2) { return cp1 * (1.0 - t) + cp2 * t; } - - glm::dvec3 piecewiseCubicBezier(double t, const std::vector &points) { + /* + // Interpolate a list of control points and knot times + glm::dvec3 piecewiseCubicBezier(double t, std::vector ×, const std::vector &points) { size_t n = points.size(); ghoul_assert(n > 4, "Minimum of four control points needed for interpolation!"); - ghoul_assert((n - 1) % 3 == 0, "A vector containing 3n + 1 control points must be provided!"); size_t nrSegments = (size_t)std::floor((n - 1) / 3.0); - - // for points equally spaced in time - double tSegment = std::fmod(t*nrSegments, 1.0); - tSegment = std::max(0.0, std::min(tSegment, 1.0)); - - size_t idx = std::floor(t*nrSegments) * 3; - - // prevent stepping past the last segment if t = 1.0 - if (idx > n - 4) { + int nrTimes = times.size(); + ghoul_assert(nrSegments == nrTimes - 1, "Number of interval times must match number of intervals"); + + if (abs(1.0-t) < 0.00001) return points.back(); + if (nrTimes < 1) { + return points[0]; } + + // find current segmant and scale time + std::vector::iterator low = std::lower_bound(times.begin(), times.end(), t); + int segmentIdx = low - times.begin(); + + double tSegment = (t - times[segmentIdx]) / (times[segmentIdx + 1] - times[segmentIdx]); + + int idx = segmentIdx * 3; return cubicBezier(tSegment, points[idx], points[idx + 1], points[idx + 2], points[idx + 3]); } + */ glm::dvec3 piecewiseLinear(double t, const std::vector &points) { size_t n = points.size(); diff --git a/modules/autonavigation/helperfunctions.h b/modules/autonavigation/helperfunctions.h index 2aeff1180f..becccc27c0 100644 --- a/modules/autonavigation/helperfunctions.h +++ b/modules/autonavigation/helperfunctions.h @@ -30,6 +30,7 @@ #include #include #include +#include namespace openspace::autonavigation::helpers { @@ -52,7 +53,8 @@ namespace openspace::autonavigation::interpolation { glm::dvec3 linear(double t, const glm::dvec3 &cp1, const glm::dvec3 &cp2); - glm::dvec3 piecewiseCubicBezier(double t, const std::vector &controlPoints); + // Inside bezier3 + // glm::dvec3 piecewiseCubicBezier(double t, std::vector ×, const std::vector &points); glm::dvec3 piecewiseLinear(double t, const std::vector &controlPoints); diff --git a/modules/autonavigation/pathcurves.cpp b/modules/autonavigation/pathcurves.cpp index ec038b4825..a13776ceee 100644 --- a/modules/autonavigation/pathcurves.cpp +++ b/modules/autonavigation/pathcurves.cpp @@ -129,10 +129,69 @@ Bezier3Curve::Bezier3Curve(CameraState& start, CameraState& end) { _points.push_back(end.position + endTangentLength * endTangentDirection); _points.push_back(end.position); + + + // initial interval times + int nrIntervals = (int)std::floor((_points.size() - 1) / 3.0); //TODO valivate! + + for (double time = 0.0; time <= 1.0; time += 1.0 / nrIntervals) { + _intervalTimes.push_back(time); + } + // TODO: test that time == 1 is included + + reparameterizeByArcLength(); +} +/* +glm::dvec3 Bezier3Curve::valueAt(double t) { + return interpolation::piecewiseCubicBezier(t, _intervalTimes, _points); +} +*/ + +// Interpolate a list of control points and knot times +glm::dvec3 Bezier3Curve::valueAt(double t) { + size_t n = _points.size(); + ghoul_assert(n > 4, "Minimum of four control points needed for interpolation!"); + ghoul_assert((n - 1) % 3 == 0, "A vector containing 3n + 1 control points must be provided!"); + + size_t nrSegments = (size_t)std::floor((n - 1) / 3.0); + int nrTimes = _intervalTimes.size(); + ghoul_assert(nrSegments == nrTimes - 1, "Number of interval times must match number of intervals"); + + if (abs(1.0 - t) < 0.00001) + return _points.back(); + if (nrTimes < 1) { + return _points[0]; + } + + // find current segmant and scale time + std::vector::iterator low = std::lower_bound(_intervalTimes.begin(), _intervalTimes.end(), t); + int segmentIdx = low - _intervalTimes.begin(); + + double tSegment = (t - _intervalTimes[segmentIdx]) / (_intervalTimes[segmentIdx + 1] - _intervalTimes[segmentIdx]); + + int idx = segmentIdx * 3; + + return interpolation::cubicBezier(tSegment, _points[idx], _points[idx + 1], + _points[idx + 2], _points[idx + 3]); } -glm::dvec3 Bezier3Curve::valueAt(double t) { - return interpolation::piecewiseCubicBezier(t, _points); +void Bezier3Curve::reparameterizeByArcLength() { + // For bezier every third is a knot shaed beetween segments + int nrIntervals = (int)std::floor((float)(_points.size() - 1) / 3.0); //TODO valivate! + + if (nrIntervals == 1) { + return; + } + + double totalLength = arcLength(1.0); //THIS IS WHERE IT GOES INTO INTERPOLATOR WITHOUT ANY VALUES IN TIMES VECTOR IF >! SEGMENTS OTHERWISE IN PATHSEGMENT::INITCURVE + std::vector newTimes; + + // Save time as relative curve length from start to each knot + for ( double time : _intervalTimes) { + newTimes.push_back(arcLength(time) / totalLength); + } + + _intervalTimes.swap(newTimes); } LinearCurve::LinearCurve(CameraState& start, CameraState& end) { diff --git a/modules/autonavigation/pathcurves.h b/modules/autonavigation/pathcurves.h index 56d9353811..051eb39ac6 100644 --- a/modules/autonavigation/pathcurves.h +++ b/modules/autonavigation/pathcurves.h @@ -39,6 +39,7 @@ public: std::vector getPoints(); // for debugging double arcLength(double tLimit = 1.0); + protected: // the points used for creating the curve (e.g. control points of a Bezier curve) std::vector _points; @@ -48,6 +49,11 @@ class Bezier3Curve : public PathCurve { public: Bezier3Curve(CameraState& start, CameraState& end); glm::dvec3 valueAt(double t); + +private: + void reparameterizeByArcLength(); + + std::vector _intervalTimes; }; class LinearCurve : public PathCurve { From 3ead8a92ddb3011e59ca27e091a8090487888f3b Mon Sep 17 00:00:00 2001 From: Emma Broman Date: Wed, 12 Feb 2020 15:34:14 -0500 Subject: [PATCH 047/912] Reparameterization of Bezier3 now works --- modules/autonavigation/helperfunctions.cpp | 30 +---------------- modules/autonavigation/helperfunctions.h | 4 +-- modules/autonavigation/pathcurves.cpp | 39 ++++++++++------------ modules/autonavigation/pathsegment.cpp | 3 +- 4 files changed, 21 insertions(+), 55 deletions(-) diff --git a/modules/autonavigation/helperfunctions.cpp b/modules/autonavigation/helperfunctions.cpp index 8a4c9786fa..12bb0e1cde 100644 --- a/modules/autonavigation/helperfunctions.cpp +++ b/modules/autonavigation/helperfunctions.cpp @@ -58,36 +58,8 @@ namespace openspace::autonavigation::interpolation { glm::dvec3 linear(double t, const glm::dvec3 &cp1, const glm::dvec3 &cp2) { return cp1 * (1.0 - t) + cp2 * t; } - /* - // Interpolate a list of control points and knot times - glm::dvec3 piecewiseCubicBezier(double t, std::vector ×, const std::vector &points) { - size_t n = points.size(); - ghoul_assert(n > 4, "Minimum of four control points needed for interpolation!"); - ghoul_assert((n - 1) % 3 == 0, "A vector containing 3n + 1 control points must be provided!"); - - size_t nrSegments = (size_t)std::floor((n - 1) / 3.0); - int nrTimes = times.size(); - ghoul_assert(nrSegments == nrTimes - 1, "Number of interval times must match number of intervals"); - - if (abs(1.0-t) < 0.00001) - return points.back(); - if (nrTimes < 1) { - return points[0]; - } - - // find current segmant and scale time - std::vector::iterator low = std::lower_bound(times.begin(), times.end(), t); - int segmentIdx = low - times.begin(); - - double tSegment = (t - times[segmentIdx]) / (times[segmentIdx + 1] - times[segmentIdx]); - - int idx = segmentIdx * 3; - - return cubicBezier(tSegment, points[idx], points[idx + 1], - points[idx + 2], points[idx + 3]); - } - */ + // TODO: remove glm::dvec3 piecewiseLinear(double t, const std::vector &points) { size_t n = points.size(); ghoul_assert(n > 2, "Minimum of two control points needed for interpolation!"); diff --git a/modules/autonavigation/helperfunctions.h b/modules/autonavigation/helperfunctions.h index becccc27c0..8e70638651 100644 --- a/modules/autonavigation/helperfunctions.h +++ b/modules/autonavigation/helperfunctions.h @@ -53,9 +53,7 @@ namespace openspace::autonavigation::interpolation { glm::dvec3 linear(double t, const glm::dvec3 &cp1, const glm::dvec3 &cp2); - // Inside bezier3 - // glm::dvec3 piecewiseCubicBezier(double t, std::vector ×, const std::vector &points); - + // TODO: remove glm::dvec3 piecewiseLinear(double t, const std::vector &controlPoints); } // namespace diff --git a/modules/autonavigation/pathcurves.cpp b/modules/autonavigation/pathcurves.cpp index a13776ceee..8979e7254e 100644 --- a/modules/autonavigation/pathcurves.cpp +++ b/modules/autonavigation/pathcurves.cpp @@ -141,42 +141,37 @@ Bezier3Curve::Bezier3Curve(CameraState& start, CameraState& end) { reparameterizeByArcLength(); } -/* -glm::dvec3 Bezier3Curve::valueAt(double t) { - return interpolation::piecewiseCubicBezier(t, _intervalTimes, _points); -} -*/ // Interpolate a list of control points and knot times glm::dvec3 Bezier3Curve::valueAt(double t) { - size_t n = _points.size(); - ghoul_assert(n > 4, "Minimum of four control points needed for interpolation!"); - ghoul_assert((n - 1) % 3 == 0, "A vector containing 3n + 1 control points must be provided!"); + size_t nrPoints = _points.size(); + size_t nrTimes = _intervalTimes.size(); + unsigned int nrSegments = (unsigned int)std::floor((nrPoints - 1) / 3.0); - size_t nrSegments = (size_t)std::floor((n - 1) / 3.0); - int nrTimes = _intervalTimes.size(); - ghoul_assert(nrSegments == nrTimes - 1, "Number of interval times must match number of intervals"); + ghoul_assert(nrPoints > 4, "Minimum of four control points needed for interpolation!"); + ghoul_assert((nrPoints - 1) % 3 == 0, "A vector containing 3n + 1 control points must be provided!"); + ghoul_assert(nrSegments == (nrTimes - 1), "Number of interval times must match number of intervals"); - if (abs(1.0 - t) < 0.00001) - return _points.back(); - if (nrTimes < 1) { - return _points[0]; - } + if (abs(t) < 0.000001) + return _points.front(); - // find current segmant and scale time - std::vector::iterator low = std::lower_bound(_intervalTimes.begin(), _intervalTimes.end(), t); - int segmentIdx = low - _intervalTimes.begin(); + // compute current segment, by first finding iterator to the first time that is larger than t + std::vector::iterator segmentEndIt = + std::lower_bound(_intervalTimes.begin(), _intervalTimes.end(), t); + unsigned int segmentIdx = (segmentEndIt - 1) - _intervalTimes.begin(); - double tSegment = (t - _intervalTimes[segmentIdx]) / (_intervalTimes[segmentIdx + 1] - _intervalTimes[segmentIdx]); + double segmentStart = _intervalTimes[segmentIdx]; + double segmentDuration = (_intervalTimes[segmentIdx + 1] - _intervalTimes[segmentIdx]); + double tSegment = (t - segmentStart) / segmentDuration; - int idx = segmentIdx * 3; + unsigned int idx = segmentIdx * 3; return interpolation::cubicBezier(tSegment, _points[idx], _points[idx + 1], _points[idx + 2], _points[idx + 3]); } void Bezier3Curve::reparameterizeByArcLength() { - // For bezier every third is a knot shaed beetween segments + // For bezier every third is a knot shared beetween segments int nrIntervals = (int)std::floor((float)(_points.size() - 1) / 3.0); //TODO valivate! if (nrIntervals == 1) { diff --git a/modules/autonavigation/pathsegment.cpp b/modules/autonavigation/pathsegment.cpp index 747ae2a206..8de41fd5f0 100644 --- a/modules/autonavigation/pathsegment.cpp +++ b/modules/autonavigation/pathsegment.cpp @@ -76,7 +76,8 @@ const std::vector PathSegment::getControlPoints() const { } glm::dvec3 PathSegment::getPositionAt(double t) const { - t = ghoul::cubicEaseInOut(t); + //t = ghoul::cubicEaseInOut(t); + // TODO: move along curve based on displacement instead return _curve->valueAt(t); } From 1f2840c32003c7be20c24bf8db540b5675e7b906 Mon Sep 17 00:00:00 2001 From: Emma Broman Date: Thu, 13 Feb 2020 14:37:08 -0500 Subject: [PATCH 048/912] Preparatory refactoring for computing curve position absed on travelled distance --- .../autonavigation/autonavigationhandler.cpp | 2 + modules/autonavigation/pathcurves.cpp | 81 +++++++++---------- modules/autonavigation/pathcurves.h | 24 ++++-- modules/autonavigation/pathsegment.cpp | 5 +- modules/autonavigation/pathsegment.h | 2 +- 5 files changed, 59 insertions(+), 55 deletions(-) diff --git a/modules/autonavigation/autonavigationhandler.cpp b/modules/autonavigation/autonavigationhandler.cpp index a8dcd213cb..46a8bcda7b 100644 --- a/modules/autonavigation/autonavigationhandler.cpp +++ b/modules/autonavigation/autonavigationhandler.cpp @@ -133,6 +133,8 @@ void AutoNavigationHandler::updateCamera(double deltaTime) { double t = (_currentTime - cps.startTime()) / cps.duration(); t = std::max(0.0, std::min(t, 1.0)); + // TODO: compute interpolation variable using travelled distnace instead + // 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. diff --git a/modules/autonavigation/pathcurves.cpp b/modules/autonavigation/pathcurves.cpp index 8979e7254e..71d99dc531 100644 --- a/modules/autonavigation/pathcurves.cpp +++ b/modules/autonavigation/pathcurves.cpp @@ -38,12 +38,17 @@ namespace openspace::autonavigation { PathCurve::~PathCurve() {} +const double PathCurve::length() const { + return _length; +} + // Approximate the curve length by dividing the curve into smaller linear // segments and accumulate their length -double PathCurve::arcLength(double tLimit) { - double dt = 0.01; // TODO: choose a good dt +double PathCurve::arcLength(double limit) { + // TODO: possibly use Simpson's method instead + double dt = 0.01; double sum = 0.0; - for (double t = 0.0; t <= tLimit - dt; t += dt) { + for (double t = 0.0; t <= limit - dt; t += dt) { double ds = glm::length(valueAt(t + dt) - valueAt(t)); sum += ds; } @@ -105,7 +110,7 @@ Bezier3Curve::Bezier3Curve(CameraState& start, CameraState& end) { if (TARGET_IN_OPPOSITE_DIRECTION) { glm::dvec3 parallell = glm::proj(startNodeToStartPos, startNodeToEndNode); glm::dvec3 orthogonal = normalize(startNodeToStartPos - parallell); - double dist = 0.5 * length(startNodeToEndNode); + double dist = 0.5 * glm::length(startNodeToEndNode); // Distant middle point glm::dvec3 extraKnot = startNodePos + dist * normalize(parallell) + 3.0 * dist * orthogonal; @@ -130,63 +135,53 @@ Bezier3Curve::Bezier3Curve(CameraState& start, CameraState& end) { _points.push_back(end.position + endTangentLength * endTangentDirection); _points.push_back(end.position); - - // initial interval times - int nrIntervals = (int)std::floor((_points.size() - 1) / 3.0); //TODO valivate! + _nrSegments = (unsigned int)std::floor((_points.size() - 1) / 3.0); - for (double time = 0.0; time <= 1.0; time += 1.0 / nrIntervals) { - _intervalTimes.push_back(time); + // default values for the curve parameter - equally spaced + for (double t = 0.0; t <= 1.0; t += 1.0 / _nrSegments) { + _parameterIntervals.push_back(t); } - // TODO: test that time == 1 is included - - reparameterizeByArcLength(); + + _length = arcLength(1.0); + + initParameterIntervals(); } // Interpolate a list of control points and knot times -glm::dvec3 Bezier3Curve::valueAt(double t) { +glm::dvec3 Bezier3Curve::valueAt(double s) { size_t nrPoints = _points.size(); - size_t nrTimes = _intervalTimes.size(); - unsigned int nrSegments = (unsigned int)std::floor((nrPoints - 1) / 3.0); + size_t nrTimes = _parameterIntervals.size(); ghoul_assert(nrPoints > 4, "Minimum of four control points needed for interpolation!"); ghoul_assert((nrPoints - 1) % 3 == 0, "A vector containing 3n + 1 control points must be provided!"); - ghoul_assert(nrSegments == (nrTimes - 1), "Number of interval times must match number of intervals"); + ghoul_assert(_nrSegments == (nrTimes - 1), "Number of interval times must match number of intervals"); - if (abs(t) < 0.000001) + if (abs(s) < 0.000001) return _points.front(); - // compute current segment, by first finding iterator to the first time that is larger than t + // compute current segment, by first finding iterator to the first value that is larger than s std::vector::iterator segmentEndIt = - std::lower_bound(_intervalTimes.begin(), _intervalTimes.end(), t); - unsigned int segmentIdx = (segmentEndIt - 1) - _intervalTimes.begin(); + std::lower_bound(_parameterIntervals.begin(), _parameterIntervals.end(), s); + unsigned int segmentIdx = (segmentEndIt - 1) - _parameterIntervals.begin(); - double segmentStart = _intervalTimes[segmentIdx]; - double segmentDuration = (_intervalTimes[segmentIdx + 1] - _intervalTimes[segmentIdx]); - double tSegment = (t - segmentStart) / segmentDuration; + double segmentStart = _parameterIntervals[segmentIdx]; + double segmentDuration = (_parameterIntervals[segmentIdx + 1] - _parameterIntervals[segmentIdx]); + double sScaled = (s - segmentStart) / segmentDuration; unsigned int idx = segmentIdx * 3; - return interpolation::cubicBezier(tSegment, _points[idx], _points[idx + 1], + // Interpolate using De Casteljau's algorithm + return interpolation::cubicBezier(sScaled, _points[idx], _points[idx + 1], _points[idx + 2], _points[idx + 3]); } -void Bezier3Curve::reparameterizeByArcLength() { - // For bezier every third is a knot shared beetween segments - int nrIntervals = (int)std::floor((float)(_points.size() - 1) / 3.0); //TODO valivate! - - if (nrIntervals == 1) { - return; +// compute curve parameter intervals based on relative arc length +void Bezier3Curve::initParameterIntervals() { + std::vector newIntervals; + for (double t = 0.0; t <= 1.0; t += 1.0 / _nrSegments) { + newIntervals.push_back(arcLength(t) / _length); } - - double totalLength = arcLength(1.0); //THIS IS WHERE IT GOES INTO INTERPOLATOR WITHOUT ANY VALUES IN TIMES VECTOR IF >! SEGMENTS OTHERWISE IN PATHSEGMENT::INITCURVE - std::vector newTimes; - - // Save time as relative curve length from start to each knot - for ( double time : _intervalTimes) { - newTimes.push_back(arcLength(time) / totalLength); - } - - _intervalTimes.swap(newTimes); + _parameterIntervals.swap(newIntervals); } LinearCurve::LinearCurve(CameraState& start, CameraState& end) { @@ -194,8 +189,8 @@ LinearCurve::LinearCurve(CameraState& start, CameraState& end) { _points.push_back(end.position); } -glm::dvec3 LinearCurve::valueAt(double t) { - return interpolation::linear(t, _points[0], _points[1]); +glm::dvec3 LinearCurve::valueAt(double s) { + return interpolation::linear(s, _points[0], _points[1]); } // TODO: Iprove handling of pauses @@ -203,7 +198,7 @@ PauseCurve::PauseCurve(CameraState& state) { _points.push_back(state.position); } -glm::dvec3 PauseCurve::valueAt(double t) { +glm::dvec3 PauseCurve::valueAt(double s) { return _points[0]; } diff --git a/modules/autonavigation/pathcurves.h b/modules/autonavigation/pathcurves.h index 051eb39ac6..6f2bfe5887 100644 --- a/modules/autonavigation/pathcurves.h +++ b/modules/autonavigation/pathcurves.h @@ -35,31 +35,39 @@ namespace openspace::autonavigation { class PathCurve { public: virtual ~PathCurve() = 0; - virtual glm::dvec3 valueAt(double t) = 0; - std::vector getPoints(); // for debugging - double arcLength(double tLimit = 1.0); + const double length() const; + double arcLength(double limit = 1.0); + + // comppute the value at the relative length s [0,1] + virtual glm::dvec3 valueAt(double s) = 0; + + std::vector getPoints(); // for debugging protected: // the points used for creating the curve (e.g. control points of a Bezier curve) std::vector _points; + + // the total length of the curve (approximated) + double _length; }; class Bezier3Curve : public PathCurve { public: Bezier3Curve(CameraState& start, CameraState& end); - glm::dvec3 valueAt(double t); + glm::dvec3 valueAt(double s); private: - void reparameterizeByArcLength(); + void initParameterIntervals(); - std::vector _intervalTimes; + std::vector _parameterIntervals; + unsigned int _nrSegments; }; class LinearCurve : public PathCurve { public: LinearCurve(CameraState& start, CameraState& end); - glm::dvec3 valueAt(double t); + glm::dvec3 valueAt(double s); }; // OBS! This is a temporary class specialised for handling pauses. @@ -67,7 +75,7 @@ public: class PauseCurve : public PathCurve { public: PauseCurve(CameraState& state); - glm::dvec3 valueAt(double t); + glm::dvec3 valueAt(double s); }; } // namespace openspace::autonavigation diff --git a/modules/autonavigation/pathsegment.cpp b/modules/autonavigation/pathsegment.cpp index 8de41fd5f0..11c1a211a7 100644 --- a/modules/autonavigation/pathsegment.cpp +++ b/modules/autonavigation/pathsegment.cpp @@ -70,6 +70,8 @@ const double PathSegment::duration() const { return _duration; } const double PathSegment::startTime() const { return _startTime; } +const double PathSegment::length() const { return _curve->length(); } + // TODO: remove function for debugging const std::vector PathSegment::getControlPoints() const { return _curve->getPoints(); @@ -160,9 +162,6 @@ void PathSegment::initCurve() { LERROR("Could not create curve. Type does not exist!"); return; } - - _length = _curve->arcLength(); - //LINFO(fmt::format("Curve length: {}", _length)); } } // namespace openspace::autonavigation diff --git a/modules/autonavigation/pathsegment.h b/modules/autonavigation/pathsegment.h index f0b32892a0..a5d645ccd0 100644 --- a/modules/autonavigation/pathsegment.h +++ b/modules/autonavigation/pathsegment.h @@ -54,6 +54,7 @@ public: const CameraState end() const; const double duration() const; const double startTime() const; + const double length() const; const std::vector getControlPoints() const; // TODO: remove this debugging function @@ -67,7 +68,6 @@ private: CameraState _start; CameraState _end; double _startTime; - double _length; double _duration; CurveType _curveType; From 9600459635a0c448f1897b0372aee75dabb78513 Mon Sep 17 00:00:00 2001 From: Emma Broman Date: Thu, 13 Feb 2020 16:22:57 -0500 Subject: [PATCH 049/912] Move along curve with constant speed, based on displacement along the curve --- .../autonavigation/autonavigationhandler.cpp | 42 ++++++++++--------- .../autonavigation/autonavigationhandler.h | 6 ++- modules/autonavigation/pathsegment.cpp | 12 +++++- modules/autonavigation/pathsegment.h | 3 ++ 4 files changed, 40 insertions(+), 23 deletions(-) diff --git a/modules/autonavigation/autonavigationhandler.cpp b/modules/autonavigation/autonavigationhandler.cpp index 46a8bcda7b..2405d4ec85 100644 --- a/modules/autonavigation/autonavigationhandler.cpp +++ b/modules/autonavigation/autonavigationhandler.cpp @@ -113,28 +113,37 @@ void AutoNavigationHandler::updateCamera(double deltaTime) { if (!_isPlaying || _pathSegments.empty()) return; - const int currentIndex = currentPathSegmentIndex(); + _currentTime += deltaTime; - if (currentIndex < 0) return; // no path + PathSegment& cps = _pathSegments[_currentSegmentIndex]; + + // Have we walked past the current segment? + if (_currentTime > cps.endTime()) { + _currentSegmentIndex++; + _distanceAlongCurrentSegment = 0.0; + + // WStepped past the last segment + if (_currentSegmentIndex > _pathSegments.size() - 1) { + LINFO("Reached end of path."); + _isPlaying = false; + return; + } + + cps = _pathSegments[_currentSegmentIndex]; - if (currentIndex != _activeSegmentIndex) { - _activeSegmentIndex = currentIndex; if (_stopAtTargets) { pausePath(); return; } } - _currentTime += deltaTime; + double prevDistance = _distanceAlongCurrentSegment; + double displacement = deltaTime * cps.speedAt(prevDistance / cps.length()); + _distanceAlongCurrentSegment += displacement; - const PathSegment& cps = _pathSegments[currentIndex]; - - // Interpolation variable - double t = (_currentTime - cps.startTime()) / cps.duration(); + double t = _distanceAlongCurrentSegment / cps.length(); t = std::max(0.0, std::min(t, 1.0)); - // TODO: compute interpolation variable using travelled distnace instead - // 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. @@ -146,11 +155,6 @@ void AutoNavigationHandler::updateCamera(double deltaTime) { camera()->setPositionVec3(cameraPosition); camera()->setRotation(cameraRotation); - - if (hasFinished()) { - LINFO("Reached end of path."); - _isPlaying = false; - } } void AutoNavigationHandler::createPath(PathSpecification& spec) { @@ -188,7 +192,7 @@ void AutoNavigationHandler::clearPath() { LINFO("Clearing path..."); _pathSegments.clear(); _currentTime = 0.0; - _activeSegmentIndex = 0; + _currentSegmentIndex = 0; } void AutoNavigationHandler::startPath() { @@ -206,7 +210,7 @@ void AutoNavigationHandler::pausePath() { LERROR("Cannot pause a path that isn't playing"); return; } - LINFO(fmt::format("Paused path at target {} / {}", _activeSegmentIndex, _pathSegments.size())); + LINFO(fmt::format("Paused path at target {} / {}", _currentSegmentIndex, _pathSegments.size())); _isPlaying = false; } @@ -225,7 +229,7 @@ void AutoNavigationHandler::continuePath() { // Recompute start camera state for the upcoming path segment, to avoid clipping to // the old camera state. - _pathSegments[_activeSegmentIndex].setStart(currentCameraState()); + _pathSegments[_currentSegmentIndex].setStart(currentCameraState()); _isPlaying = true; } diff --git a/modules/autonavigation/autonavigationhandler.h b/modules/autonavigation/autonavigationhandler.h index 26c878a506..e4d780dc3b 100644 --- a/modules/autonavigation/autonavigationhandler.h +++ b/modules/autonavigation/autonavigationhandler.h @@ -89,10 +89,12 @@ private: std::vector _pathSegments; CurveType _pathCurveType; // TEST: create a path with just one type of curve - double _currentTime; bool _isPlaying = false; - int _activeSegmentIndex = 0; + double _currentTime; + double _distanceAlongCurrentSegment = 0.0; + unsigned int _currentSegmentIndex = 0; + bool _stopAtTargets; }; diff --git a/modules/autonavigation/pathsegment.cpp b/modules/autonavigation/pathsegment.cpp index 11c1a211a7..4767d0c63e 100644 --- a/modules/autonavigation/pathsegment.cpp +++ b/modules/autonavigation/pathsegment.cpp @@ -70,6 +70,8 @@ const double PathSegment::duration() const { return _duration; } const double PathSegment::startTime() const { return _startTime; } +const double PathSegment::endTime() const { return _startTime + _duration; } + const double PathSegment::length() const { return _curve->length(); } // TODO: remove function for debugging @@ -77,9 +79,15 @@ const std::vector PathSegment::getControlPoints() const { return _curve->getPoints(); } +// Speed function for a segment, per curve parameter t in range [0,1], +// which is the relative arc length. OBS! If integrated over the full +// length of the curve it must match the total duration +const double PathSegment::speedAt(double t) const { + // TODO: implement dampening near start and end + return length() / _duration; +} + glm::dvec3 PathSegment::getPositionAt(double t) const { - //t = ghoul::cubicEaseInOut(t); - // TODO: move along curve based on displacement instead return _curve->valueAt(t); } diff --git a/modules/autonavigation/pathsegment.h b/modules/autonavigation/pathsegment.h index a5d645ccd0..108f5aa61f 100644 --- a/modules/autonavigation/pathsegment.h +++ b/modules/autonavigation/pathsegment.h @@ -54,10 +54,13 @@ public: const CameraState end() const; const double duration() const; const double startTime() const; + const double endTime() const; const double length() const; const std::vector getControlPoints() const; // TODO: remove this debugging function + const double speedAt(double t) const; + glm::dvec3 getPositionAt(double t) const; glm::dquat getRotationAt(double t) const; From 32139120f8680fecffd16c174e58afb9f92906da Mon Sep 17 00:00:00 2001 From: Emma Broman Date: Fri, 14 Feb 2020 10:41:47 -0500 Subject: [PATCH 050/912] Speed function for acceleration and deacceleration --- .../autonavigation/autonavigationhandler.cpp | 12 ++--- modules/autonavigation/pathsegment.cpp | 52 ++++++++++++++++--- modules/autonavigation/pathsegment.h | 4 +- 3 files changed, 55 insertions(+), 13 deletions(-) diff --git a/modules/autonavigation/autonavigationhandler.cpp b/modules/autonavigation/autonavigationhandler.cpp index 2405d4ec85..477f6805df 100644 --- a/modules/autonavigation/autonavigationhandler.cpp +++ b/modules/autonavigation/autonavigationhandler.cpp @@ -138,20 +138,20 @@ void AutoNavigationHandler::updateCamera(double deltaTime) { } double prevDistance = _distanceAlongCurrentSegment; - double displacement = deltaTime * cps.speedAt(prevDistance / cps.length()); + double displacement = deltaTime * cps.speedAtTime(_currentTime - cps.startTime()); _distanceAlongCurrentSegment += displacement; - double t = _distanceAlongCurrentSegment / cps.length(); - t = std::max(0.0, std::min(t, 1.0)); + double relativeDisplacement = _distanceAlongCurrentSegment / cps.length(); + relativeDisplacement = std::max(0.0, std::min(relativeDisplacement, 1.0)); // 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(); + CameraState cs = (relativeDisplacement < 0.5) ? cps.start() : cps.end(); global::navigationHandler.orbitalNavigator().setAnchorNode(cs.referenceNode); - glm::dvec3 cameraPosition = cps.getPositionAt(t); - glm::dquat cameraRotation = cps.getRotationAt(t); + glm::dvec3 cameraPosition = cps.getPositionAt(relativeDisplacement); + glm::dquat cameraRotation = cps.getRotationAt(relativeDisplacement); camera()->setPositionVec3(cameraPosition); camera()->setRotation(cameraRotation); diff --git a/modules/autonavigation/pathsegment.cpp b/modules/autonavigation/pathsegment.cpp index 4767d0c63e..a0273f7e4d 100644 --- a/modules/autonavigation/pathsegment.cpp +++ b/modules/autonavigation/pathsegment.cpp @@ -79,12 +79,28 @@ const std::vector PathSegment::getControlPoints() const { return _curve->getPoints(); } -// Speed function for a segment, per curve parameter t in range [0,1], -// which is the relative arc length. OBS! If integrated over the full -// length of the curve it must match the total duration -const double PathSegment::speedAt(double t) const { - // TODO: implement dampening near start and end - return length() / _duration; + +// Speed function for the segment, where time is a value in the range [0, duration] +// OBS! If integrated over the curve it must match the total length or the curve +const double PathSegment::speedAtTime(double time) const { + + // TODO: validate time + + double speed = speedFunction(time / _duration); + + // apply duration constraint (eq. 14 in Eberly) + double speedSum = 0.0; + const int steps = 100; + double dt = duration() / steps; + for (double t = 0.0; t <= 1.0; t += 1.0 / steps) { + speedSum += dt * speedFunction(t); + } + + // TODO: save speed sum value somewhere + + speed = (length() * speedFunction(time / _duration)) / speedSum; + + return speed; } glm::dvec3 PathSegment::getPositionAt(double t) const { @@ -172,4 +188,28 @@ void PathSegment::initCurve() { } } +// The speed function, describing the shape of the speed curve with values in range [0,1] +// OBS! The value must later be normalised so that the speed matches the duration of the segment. +double PathSegment::speedFunction(double t) const { + const double t1 = 0.25; + const double t2 = 0.75; // > t1 + // TODO: more advanced computation of limits + + double speed = 1.0; + double tScaled; + + // accelerate + if (t < t1) { + tScaled = t / t1; + speed = ghoul::cubicEaseInOut(tScaled); + } + // deaccelerate + else if (t > t2) { + tScaled = (t - t2) / (1.0 - t2); + speed = 1.0 - ghoul::cubicEaseInOut(tScaled); + } + + return speed; +} + } // namespace openspace::autonavigation diff --git a/modules/autonavigation/pathsegment.h b/modules/autonavigation/pathsegment.h index 108f5aa61f..b8621f2632 100644 --- a/modules/autonavigation/pathsegment.h +++ b/modules/autonavigation/pathsegment.h @@ -59,7 +59,7 @@ public: const std::vector getControlPoints() const; // TODO: remove this debugging function - const double speedAt(double t) const; + const double speedAtTime(double time) const; glm::dvec3 getPositionAt(double t) const; glm::dquat getRotationAt(double t) const; @@ -68,6 +68,8 @@ private: const glm::dquat piecewiseSlerpRotation(double t) const; void initCurve(); + double speedFunction(double t) const; + CameraState _start; CameraState _end; double _startTime; From caa05d09bd43439a087b3a568c98c196f2306f74 Mon Sep 17 00:00:00 2001 From: Emma Broman Date: Fri, 14 Feb 2020 11:19:00 -0500 Subject: [PATCH 051/912] Refactor speed function --- modules/autonavigation/pathsegment.cpp | 65 ++++++++++++++------------ modules/autonavigation/pathsegment.h | 15 +++++- 2 files changed, 47 insertions(+), 33 deletions(-) diff --git a/modules/autonavigation/pathsegment.cpp b/modules/autonavigation/pathsegment.cpp index a0273f7e4d..67c3461ddd 100644 --- a/modules/autonavigation/pathsegment.cpp +++ b/modules/autonavigation/pathsegment.cpp @@ -50,6 +50,8 @@ PathSegment::PathSegment( // Also, when compensatng for simulation time later we need to make a guess for // the duration, based on the current position of the target. _duration = 5; + + _speedFunction = SpeedFunction(_duration); } void PathSegment::setStart(CameraState cs) { @@ -60,6 +62,7 @@ void PathSegment::setStart(CameraState cs) { void PathSegment::setDuration(double d) { _duration = d; + _speedFunction = SpeedFunction(_duration); } const CameraState PathSegment::start() const { return _start; } @@ -79,28 +82,16 @@ const std::vector PathSegment::getControlPoints() const { return _curve->getPoints(); } - -// Speed function for the segment, where time is a value in the range [0, duration] -// OBS! If integrated over the curve it must match the total length or the curve -const double PathSegment::speedAtTime(double time) const { - - // TODO: validate time - - double speed = speedFunction(time / _duration); - - // apply duration constraint (eq. 14 in Eberly) - double speedSum = 0.0; - const int steps = 100; - double dt = duration() / steps; - for (double t = 0.0; t <= 1.0; t += 1.0 / steps) { - speedSum += dt * speedFunction(t); - } - - // TODO: save speed sum value somewhere - - speed = (length() * speedFunction(time / _duration)) / speedSum; - - return speed; +/* + * Get speed at time value in the range [0, duration] + * OBS! If integrated over the curve it must match the total length or the curve. + * Thus, we scale according to the constraint in eq. 14 in Eberly 2007 + * (https://www.geometrictools.com/Documentation/MovingAlongCurveSpecifiedSpeed.pdf) + */ +double PathSegment::speedAtTime(double time) { + ghoul_assert(time >= 0 && time <= _duration, "Time out of range [0, duration]"); + double t = time / _duration; + return (length() * _speedFunction.value(t)) / _speedFunction.integratedSum; } glm::dvec3 PathSegment::getPositionAt(double t) const { @@ -188,24 +179,36 @@ void PathSegment::initCurve() { } } -// The speed function, describing the shape of the speed curve with values in range [0,1] -// OBS! The value must later be normalised so that the speed matches the duration of the segment. -double PathSegment::speedFunction(double t) const { - const double t1 = 0.25; - const double t2 = 0.75; // > t1 - // TODO: more advanced computation of limits + +PathSegment::SpeedFunction::SpeedFunction(double duration) { + // apply duration constraint (eq. 14 in Eberly) + double speedSum = 0.0; + const int steps = 100; + double dt = duration / steps; + for (double t = 0.0; t <= 1.0; t += 1.0 / steps) { + speedSum += dt * value(t); + } + + integratedSum = speedSum; +} + +double PathSegment::SpeedFunction::value(double t) { + ghoul_assert(t >= 0 && t <= 1, "Variable t out of range [0,1]"); + + const double t1 = 0.2; + const double t2 = 0.8; // > t1 + // TODO: more advanced computation of limits, possibly based on distances double speed = 1.0; - double tScaled; // accelerate if (t < t1) { - tScaled = t / t1; + double tScaled = t / t1; speed = ghoul::cubicEaseInOut(tScaled); } // deaccelerate else if (t > t2) { - tScaled = (t - t2) / (1.0 - t2); + double tScaled = (t - t2) / (1.0 - t2); speed = 1.0 - ghoul::cubicEaseInOut(tScaled); } diff --git a/modules/autonavigation/pathsegment.h b/modules/autonavigation/pathsegment.h index b8621f2632..c6081ed96a 100644 --- a/modules/autonavigation/pathsegment.h +++ b/modules/autonavigation/pathsegment.h @@ -59,7 +59,7 @@ public: const std::vector getControlPoints() const; // TODO: remove this debugging function - const double speedAtTime(double time) const; + double speedAtTime(double time); glm::dvec3 getPositionAt(double t) const; glm::dquat getRotationAt(double t) const; @@ -68,7 +68,16 @@ private: const glm::dquat piecewiseSlerpRotation(double t) const; void initCurve(); - double speedFunction(double t) const; + // The speed function describing the shape of the speed curve. Values in [0,1]. + struct SpeedFunction { + SpeedFunction() = default; + SpeedFunction(double duration); + double value(double t); + + // store the sum of the function over the duration of the segment, + // so we don't need to recompue it every time we access the speed + double integratedSum; + }; CameraState _start; CameraState _end; @@ -76,6 +85,8 @@ private: double _duration; CurveType _curveType; + SpeedFunction _speedFunction; + std::shared_ptr _curve; // OBS! Does it make more sense to use unique_ptr? However, then PathSegments cannot be copied. }; From b71a5519bacb3a696da03f347149b773f15c1ca4 Mon Sep 17 00:00:00 2001 From: Emma Broman Date: Fri, 14 Feb 2020 13:27:21 -0500 Subject: [PATCH 052/912] Update anchor halfway, not every frame --- modules/autonavigation/autonavigationhandler.cpp | 11 ++++++----- 1 file changed, 6 insertions(+), 5 deletions(-) diff --git a/modules/autonavigation/autonavigationhandler.cpp b/modules/autonavigation/autonavigationhandler.cpp index 477f6805df..466c39d727 100644 --- a/modules/autonavigation/autonavigationhandler.cpp +++ b/modules/autonavigation/autonavigationhandler.cpp @@ -144,11 +144,12 @@ void AutoNavigationHandler::updateCamera(double deltaTime) { double relativeDisplacement = _distanceAlongCurrentSegment / cps.length(); relativeDisplacement = std::max(0.0, std::min(relativeDisplacement, 1.0)); - // 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 = (relativeDisplacement < 0.5) ? cps.start() : cps.end(); - global::navigationHandler.orbitalNavigator().setAnchorNode(cs.referenceNode); + // When halfway along a curve, set anchor node in orbitalNavigator, to render + // visible nodes and add possibility to navigate when we reach the end. + if (abs(relativeDisplacement - 0.5) < 0.001) { + std::string newAnchor = cps.end().referenceNode; + global::navigationHandler.orbitalNavigator().setAnchorNode(newAnchor); + } glm::dvec3 cameraPosition = cps.getPositionAt(relativeDisplacement); glm::dquat cameraRotation = cps.getRotationAt(relativeDisplacement); From 3425604a9d3689eefa011364c7897b10690dd295 Mon Sep 17 00:00:00 2001 From: Emma Broman Date: Fri, 14 Feb 2020 15:00:09 -0500 Subject: [PATCH 053/912] Improve arc length approximation by using Simpson's method --- modules/autonavigation/pathcurves.cpp | 33 +++++++++++++++++++-------- 1 file changed, 24 insertions(+), 9 deletions(-) diff --git a/modules/autonavigation/pathcurves.cpp b/modules/autonavigation/pathcurves.cpp index 71d99dc531..3a2ff06d8b 100644 --- a/modules/autonavigation/pathcurves.cpp +++ b/modules/autonavigation/pathcurves.cpp @@ -42,17 +42,29 @@ const double PathCurve::length() const { return _length; } -// Approximate the curve length by dividing the curve into smaller linear -// segments and accumulate their length +// Approximate the curve length using Simpson's rule double PathCurve::arcLength(double limit) { - // TODO: possibly use Simpson's method instead - double dt = 0.01; - double sum = 0.0; - for (double t = 0.0; t <= limit - dt; t += dt) { - double ds = glm::length(valueAt(t + dt) - valueAt(t)); - sum += ds; + const int n = 30; // resolution, must be even for Simpson's rule + const double h = limit / (double)n; + + // Points to be multiplied by 1 + double endPoints = glm::length(valueAt(0.0 + h) - valueAt(0.0)) + glm::length(valueAt(1.0) - valueAt(1.0 - h)); + + // Points to be multiplied by 4 + double times4 = 0.0; + for (int i = 1; i < n; i += 2) { + float t = h * i; + times4 += glm::length(valueAt(t + h) - valueAt(t)); } - return sum; + + // Points to be multiplied by 2 + double times2 = 0.0; + for (int i = 2; i < n; i += 2) { + float t = h * i; + times2 += glm::length(valueAt(t + h) - valueAt(t)); + } + + return (h / 3.0) * (endPoints + 4.0 * times4 + 2.0 *times2); } // TODO: remove when not needed @@ -159,6 +171,9 @@ glm::dvec3 Bezier3Curve::valueAt(double s) { if (abs(s) < 0.000001) return _points.front(); + if (abs(1.0 - s) < 0.000001) + return _points.back(); + // compute current segment, by first finding iterator to the first value that is larger than s std::vector::iterator segmentEndIt = std::lower_bound(_parameterIntervals.begin(), _parameterIntervals.end(), s); From 65cf8a79dae981423ba85899e67cade886be29da Mon Sep 17 00:00:00 2001 From: Emma Broman Date: Mon, 17 Feb 2020 10:30:36 -0500 Subject: [PATCH 054/912] Rename interpolation parameter to u, to reduce confusion about what is related to time --- .../autonavigation/autonavigationhandler.cpp | 6 +-- modules/autonavigation/pathcurves.cpp | 16 +++---- modules/autonavigation/pathcurves.h | 8 ++-- modules/autonavigation/pathsegment.cpp | 47 +++++++++---------- modules/autonavigation/pathsegment.h | 6 +-- 5 files changed, 41 insertions(+), 42 deletions(-) diff --git a/modules/autonavigation/autonavigationhandler.cpp b/modules/autonavigation/autonavigationhandler.cpp index 466c39d727..6adea0ed73 100644 --- a/modules/autonavigation/autonavigationhandler.cpp +++ b/modules/autonavigation/autonavigationhandler.cpp @@ -248,11 +248,11 @@ std::vector AutoNavigationHandler::getCurvePositions(int nPerSegment return positions; } - const double dt = 1.0 / nPerSegment; + const double du = 1.0 / nPerSegment; for (PathSegment &p : _pathSegments) { - for (double t = 0.0; t < 1.0; t += dt) { - auto position = p.getPositionAt(t); + for (double u = 0.0; u < 1.0; u += du) { + auto position = p.getPositionAt(u); positions.push_back(position); } } diff --git a/modules/autonavigation/pathcurves.cpp b/modules/autonavigation/pathcurves.cpp index 3a2ff06d8b..d0b22063a4 100644 --- a/modules/autonavigation/pathcurves.cpp +++ b/modules/autonavigation/pathcurves.cpp @@ -160,7 +160,7 @@ Bezier3Curve::Bezier3Curve(CameraState& start, CameraState& end) { } // Interpolate a list of control points and knot times -glm::dvec3 Bezier3Curve::valueAt(double s) { +glm::dvec3 Bezier3Curve::valueAt(double u) { size_t nrPoints = _points.size(); size_t nrTimes = _parameterIntervals.size(); @@ -168,20 +168,20 @@ glm::dvec3 Bezier3Curve::valueAt(double s) { ghoul_assert((nrPoints - 1) % 3 == 0, "A vector containing 3n + 1 control points must be provided!"); ghoul_assert(_nrSegments == (nrTimes - 1), "Number of interval times must match number of intervals"); - if (abs(s) < 0.000001) + if (abs(u) < 0.000001) return _points.front(); - if (abs(1.0 - s) < 0.000001) + if (abs(1.0 - u) < 0.000001) return _points.back(); // compute current segment, by first finding iterator to the first value that is larger than s std::vector::iterator segmentEndIt = - std::lower_bound(_parameterIntervals.begin(), _parameterIntervals.end(), s); + std::lower_bound(_parameterIntervals.begin(), _parameterIntervals.end(), u); unsigned int segmentIdx = (segmentEndIt - 1) - _parameterIntervals.begin(); double segmentStart = _parameterIntervals[segmentIdx]; double segmentDuration = (_parameterIntervals[segmentIdx + 1] - _parameterIntervals[segmentIdx]); - double sScaled = (s - segmentStart) / segmentDuration; + double sScaled = (u - segmentStart) / segmentDuration; unsigned int idx = segmentIdx * 3; @@ -204,8 +204,8 @@ LinearCurve::LinearCurve(CameraState& start, CameraState& end) { _points.push_back(end.position); } -glm::dvec3 LinearCurve::valueAt(double s) { - return interpolation::linear(s, _points[0], _points[1]); +glm::dvec3 LinearCurve::valueAt(double u) { + return interpolation::linear(u, _points[0], _points[1]); } // TODO: Iprove handling of pauses @@ -213,7 +213,7 @@ PauseCurve::PauseCurve(CameraState& state) { _points.push_back(state.position); } -glm::dvec3 PauseCurve::valueAt(double s) { +glm::dvec3 PauseCurve::valueAt(double u) { return _points[0]; } diff --git a/modules/autonavigation/pathcurves.h b/modules/autonavigation/pathcurves.h index 6f2bfe5887..1c1d530074 100644 --- a/modules/autonavigation/pathcurves.h +++ b/modules/autonavigation/pathcurves.h @@ -40,7 +40,7 @@ public: double arcLength(double limit = 1.0); // comppute the value at the relative length s [0,1] - virtual glm::dvec3 valueAt(double s) = 0; + virtual glm::dvec3 valueAt(double u) = 0; std::vector getPoints(); // for debugging @@ -55,7 +55,7 @@ protected: class Bezier3Curve : public PathCurve { public: Bezier3Curve(CameraState& start, CameraState& end); - glm::dvec3 valueAt(double s); + glm::dvec3 valueAt(double u); private: void initParameterIntervals(); @@ -67,7 +67,7 @@ private: class LinearCurve : public PathCurve { public: LinearCurve(CameraState& start, CameraState& end); - glm::dvec3 valueAt(double s); + glm::dvec3 valueAt(double u); }; // OBS! This is a temporary class specialised for handling pauses. @@ -75,7 +75,7 @@ public: class PauseCurve : public PathCurve { public: PauseCurve(CameraState& state); - glm::dvec3 valueAt(double s); + glm::dvec3 valueAt(double u); }; } // namespace openspace::autonavigation diff --git a/modules/autonavigation/pathsegment.cpp b/modules/autonavigation/pathsegment.cpp index 67c3461ddd..a8e9c20ad5 100644 --- a/modules/autonavigation/pathsegment.cpp +++ b/modules/autonavigation/pathsegment.cpp @@ -94,34 +94,33 @@ double PathSegment::speedAtTime(double time) { return (length() * _speedFunction.value(t)) / _speedFunction.integratedSum; } -glm::dvec3 PathSegment::getPositionAt(double t) const { - return _curve->valueAt(t); +glm::dvec3 PathSegment::getPositionAt(double u) const { + return _curve->valueAt(u); } -glm::dquat PathSegment::getRotationAt(double t) const { +glm::dquat PathSegment::getRotationAt(double u) const { // TODO: improve how rotation is computed switch (_curveType) { case CurveType::Bezier3: { - return piecewiseSlerpRotation(t); + return piecewiseSlerpRotation(u); break; } default: { - double tSlerp = helpers::shiftAndScale(t, 0.1, 0.9); - tSlerp = ghoul::cubicEaseInOut(tSlerp); - return glm::slerp(_start.rotation, _end.rotation, tSlerp); + double uSlerp = helpers::shiftAndScale(u, 0.1, 0.9); + uSlerp = ghoul::cubicEaseInOut(uSlerp); + return glm::slerp(_start.rotation, _end.rotation, uSlerp); } } } // Interpolate between a number of keyframes for orientation using SLERP -const glm::dquat PathSegment::piecewiseSlerpRotation(double t) const { +const glm::dquat PathSegment::piecewiseSlerpRotation(double u) const { // breakpoints for subintervals - const double t1 = 0.3; - const double t2 = 0.8; // TODO: these should probably be based on distance - std::vector times{ 0.0, t1, t2, 1.0 }; + const double u1 = 0.3; + const double u2 = 0.8; // TODO: these should probably be based on distance glm::dvec3 startNodePos = sceneGraphNode(_start.referenceNode)->worldPosition(); glm::dvec3 endNodePos = sceneGraphNode(_end.referenceNode)->worldPosition(); @@ -130,27 +129,27 @@ const glm::dquat PathSegment::piecewiseSlerpRotation(double t) const { glm::dvec3 endUpVec = _end.rotation * glm::dvec3(0.0, 1.0, 0.0); glm::dquat lookAtStartQ = - helpers::getLookAtQuaternion(getPositionAt(t1), startNodePos, startUpVec); + helpers::getLookAtQuaternion(getPositionAt(u1), startNodePos, startUpVec); glm::dquat lookAtEndQ = - helpers::getLookAtQuaternion(getPositionAt(t2), endNodePos, endUpVec); + helpers::getLookAtQuaternion(getPositionAt(u2), endNodePos, endUpVec); - std::vector keyframes{ - _start.rotation, - lookAtStartQ, - lookAtEndQ, - _end.rotation + std::vector> keyframes{ + {_start.rotation, 0.0}, + {lookAtStartQ, u1}, + {lookAtEndQ, u2}, + {_end.rotation, 1.0}, }; - ghoul_assert(keyframes.size() == times.size(), "Must have one time value per keyframe."); - // Find the current segment and compute interpolation glm::dquat result; for (int i = 0; i < keyframes.size() - 1; ++i) { - if (t <= times[i + 1]) { - double tScaled = (t - times[i]) / (times[i + 1] - times[i]); - tScaled = ghoul::quadraticEaseInOut(tScaled); - result = glm::slerp(keyframes[i], keyframes[i + 1], tScaled); + double ui = keyframes[i].second; + double uNext = keyframes[i + 1].second; + if (u <= uNext) { + double uScaled = (u - ui) / (uNext - ui); + uScaled = ghoul::quadraticEaseInOut(uScaled); + result = glm::slerp(keyframes[i].first, keyframes[i + 1].first, uScaled); break; } } diff --git a/modules/autonavigation/pathsegment.h b/modules/autonavigation/pathsegment.h index c6081ed96a..d824a541f4 100644 --- a/modules/autonavigation/pathsegment.h +++ b/modules/autonavigation/pathsegment.h @@ -61,11 +61,11 @@ public: double speedAtTime(double time); - glm::dvec3 getPositionAt(double t) const; - glm::dquat getRotationAt(double t) const; + glm::dvec3 getPositionAt(double u) const; + glm::dquat getRotationAt(double u) const; private: - const glm::dquat piecewiseSlerpRotation(double t) const; + const glm::dquat piecewiseSlerpRotation(double u) const; void initCurve(); // The speed function describing the shape of the speed curve. Values in [0,1]. From 95449a633c295e16361f69e49911dba0b427795a Mon Sep 17 00:00:00 2001 From: Emma Broman Date: Mon, 17 Feb 2020 10:43:15 -0500 Subject: [PATCH 055/912] Reset current distance along segment when clearing a path --- modules/autonavigation/autonavigationhandler.cpp | 1 + 1 file changed, 1 insertion(+) diff --git a/modules/autonavigation/autonavigationhandler.cpp b/modules/autonavigation/autonavigationhandler.cpp index 6adea0ed73..dfd504556e 100644 --- a/modules/autonavigation/autonavigationhandler.cpp +++ b/modules/autonavigation/autonavigationhandler.cpp @@ -194,6 +194,7 @@ void AutoNavigationHandler::clearPath() { _pathSegments.clear(); _currentTime = 0.0; _currentSegmentIndex = 0; + _distanceAlongCurrentSegment = 0.0; } void AutoNavigationHandler::startPath() { From 566b9959ec58c66a30edc7624319832b5b86fe66 Mon Sep 17 00:00:00 2001 From: Emma Broman Date: Mon, 17 Feb 2020 10:51:56 -0500 Subject: [PATCH 056/912] Minor refactor --- modules/autonavigation/autonavigationhandler.cpp | 2 +- modules/autonavigation/pathsegment.cpp | 4 ++-- modules/autonavigation/pathsegment.h | 2 +- 3 files changed, 4 insertions(+), 4 deletions(-) diff --git a/modules/autonavigation/autonavigationhandler.cpp b/modules/autonavigation/autonavigationhandler.cpp index dfd504556e..9a349e725d 100644 --- a/modules/autonavigation/autonavigationhandler.cpp +++ b/modules/autonavigation/autonavigationhandler.cpp @@ -141,7 +141,7 @@ void AutoNavigationHandler::updateCamera(double deltaTime) { double displacement = deltaTime * cps.speedAtTime(_currentTime - cps.startTime()); _distanceAlongCurrentSegment += displacement; - double relativeDisplacement = _distanceAlongCurrentSegment / cps.length(); + double relativeDisplacement = _distanceAlongCurrentSegment / cps.pathLength(); relativeDisplacement = std::max(0.0, std::min(relativeDisplacement, 1.0)); // When halfway along a curve, set anchor node in orbitalNavigator, to render diff --git a/modules/autonavigation/pathsegment.cpp b/modules/autonavigation/pathsegment.cpp index a8e9c20ad5..de2a4870d4 100644 --- a/modules/autonavigation/pathsegment.cpp +++ b/modules/autonavigation/pathsegment.cpp @@ -75,7 +75,7 @@ const double PathSegment::startTime() const { return _startTime; } const double PathSegment::endTime() const { return _startTime + _duration; } -const double PathSegment::length() const { return _curve->length(); } +const double PathSegment::pathLength() const { return _curve->length(); } // TODO: remove function for debugging const std::vector PathSegment::getControlPoints() const { @@ -91,7 +91,7 @@ const std::vector PathSegment::getControlPoints() const { double PathSegment::speedAtTime(double time) { ghoul_assert(time >= 0 && time <= _duration, "Time out of range [0, duration]"); double t = time / _duration; - return (length() * _speedFunction.value(t)) / _speedFunction.integratedSum; + return (pathLength() * _speedFunction.value(t)) / _speedFunction.integratedSum; } glm::dvec3 PathSegment::getPositionAt(double u) const { diff --git a/modules/autonavigation/pathsegment.h b/modules/autonavigation/pathsegment.h index d824a541f4..6e206ae2cc 100644 --- a/modules/autonavigation/pathsegment.h +++ b/modules/autonavigation/pathsegment.h @@ -55,7 +55,7 @@ public: const double duration() const; const double startTime() const; const double endTime() const; - const double length() const; + const double pathLength() const; const std::vector getControlPoints() const; // TODO: remove this debugging function From 6fc12fd2c7ec2c3506ea4780b90305c3d5ac12d8 Mon Sep 17 00:00:00 2001 From: Emma Broman Date: Mon, 17 Feb 2020 12:53:59 -0500 Subject: [PATCH 057/912] Compute length for other curve types --- modules/autonavigation/pathcurves.cpp | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/modules/autonavigation/pathcurves.cpp b/modules/autonavigation/pathcurves.cpp index d0b22063a4..c5cb77edd9 100644 --- a/modules/autonavigation/pathcurves.cpp +++ b/modules/autonavigation/pathcurves.cpp @@ -161,6 +161,8 @@ Bezier3Curve::Bezier3Curve(CameraState& start, CameraState& end) { // Interpolate a list of control points and knot times glm::dvec3 Bezier3Curve::valueAt(double u) { + ghoul_assert(u >= 0 && u <= 1.0, "Interpolation variable out of range [0, 1]"); + size_t nrPoints = _points.size(); size_t nrTimes = _parameterIntervals.size(); @@ -202,18 +204,22 @@ void Bezier3Curve::initParameterIntervals() { LinearCurve::LinearCurve(CameraState& start, CameraState& end) { _points.push_back(start.position); _points.push_back(end.position); + _length = glm::distance(end.position, start.position); } glm::dvec3 LinearCurve::valueAt(double u) { + ghoul_assert(u >= 0 && u <= 1.0, "Interpolation variable out of range [0, 1]"); return interpolation::linear(u, _points[0], _points[1]); } // TODO: Iprove handling of pauses PauseCurve::PauseCurve(CameraState& state) { _points.push_back(state.position); + _length = 1.0; // OBS! Length of a pause curve makes no sense, but it also doesn't matter } glm::dvec3 PauseCurve::valueAt(double u) { + ghoul_assert(u >= 0 && u <= 1.0, "Interpolation variable out of range [0, 1]"); return _points[0]; } From 45309a899676415723589d40858b1f9400043ab9 Mon Sep 17 00:00:00 2001 From: Emma Broman Date: Mon, 17 Feb 2020 13:28:28 -0500 Subject: [PATCH 058/912] Clean up helperfunctions a bit --- modules/autonavigation/helperfunctions.cpp | 25 +--------------------- modules/autonavigation/helperfunctions.h | 14 +++++------- 2 files changed, 6 insertions(+), 33 deletions(-) diff --git a/modules/autonavigation/helperfunctions.cpp b/modules/autonavigation/helperfunctions.cpp index 12bb0e1cde..d60fc35302 100644 --- a/modules/autonavigation/helperfunctions.cpp +++ b/modules/autonavigation/helperfunctions.cpp @@ -42,8 +42,6 @@ namespace openspace::autonavigation::helpers { } // helpers namespace openspace::autonavigation::interpolation { - - // TODO: make all these into template functions glm::dvec3 cubicBezier(double t, const glm::dvec3 &cp1, const glm::dvec3 &cp2, const glm::dvec3 &cp3, const glm::dvec3 &cp4 ) @@ -59,26 +57,5 @@ namespace openspace::autonavigation::interpolation { return cp1 * (1.0 - t) + cp2 * t; } - // TODO: remove - glm::dvec3 piecewiseLinear(double t, const std::vector &points) { - size_t n = points.size(); - ghoul_assert(n > 2, "Minimum of two control points needed for interpolation!"); - - size_t nrSegments = n - 1; - - // for points equally spaced in time - double tSegment = std::fmod( t*nrSegments, 1.0 ); - tSegment = std::max(0.0, std::min(tSegment, 1.0)); - - size_t idx = std::floor(t*nrSegments); - - // prevent stepping past the last segment if t = 1.0 - if (idx > n - 1) { - return points.back(); - } - - return linear(tSegment, points[idx], points[idx + 1]); - } - -} // interpolator +} // interpolation diff --git a/modules/autonavigation/helperfunctions.h b/modules/autonavigation/helperfunctions.h index 8e70638651..c3051d2577 100644 --- a/modules/autonavigation/helperfunctions.h +++ b/modules/autonavigation/helperfunctions.h @@ -39,22 +39,18 @@ namespace openspace::autonavigation::helpers { glm::dquat getLookAtQuaternion(glm::dvec3 eye, glm::dvec3 center, glm::dvec3 up); -} // helpers - -// TODO: include interpolator.h to helperfunctions -// error when interpolator.h is included and used both here and in pathsegment - -// TODO: also make these template functions instead +} // namespace namespace openspace::autonavigation::interpolation { + // TODO: make all these into template functions. + // Alternatively, add cubicBezier interpolation in ghoul and only use + // ghoul's interpolator methods + glm::dvec3 cubicBezier(double t, const glm::dvec3 &cp1, const glm::dvec3 &cp2, const glm::dvec3 &cp3, const glm::dvec3 &cp4); glm::dvec3 linear(double t, const glm::dvec3 &cp1, const glm::dvec3 &cp2); - // TODO: remove - glm::dvec3 piecewiseLinear(double t, const std::vector &controlPoints); - } // namespace #endif From 3282f08ae62aee0292548a5c553a5f0db0f06729 Mon Sep 17 00:00:00 2001 From: Emma Broman Date: Mon, 17 Feb 2020 13:56:15 -0500 Subject: [PATCH 059/912] remove extra blank line --- modules/autonavigation/pathsegment.cpp | 1 - 1 file changed, 1 deletion(-) diff --git a/modules/autonavigation/pathsegment.cpp b/modules/autonavigation/pathsegment.cpp index de2a4870d4..86a7eb9596 100644 --- a/modules/autonavigation/pathsegment.cpp +++ b/modules/autonavigation/pathsegment.cpp @@ -178,7 +178,6 @@ void PathSegment::initCurve() { } } - PathSegment::SpeedFunction::SpeedFunction(double duration) { // apply duration constraint (eq. 14 in Eberly) double speedSum = 0.0; From ff419371fa70c05c69c17a2da854e95accd1e31c Mon Sep 17 00:00:00 2001 From: Emma Broman Date: Mon, 17 Feb 2020 15:02:43 -0500 Subject: [PATCH 060/912] Remove a non-used function --- modules/autonavigation/autonavigationhandler.cpp | 11 ----------- modules/autonavigation/autonavigationhandler.h | 1 - 2 files changed, 12 deletions(-) diff --git a/modules/autonavigation/autonavigationhandler.cpp b/modules/autonavigation/autonavigationhandler.cpp index 9a349e725d..8a449b8e73 100644 --- a/modules/autonavigation/autonavigationhandler.cpp +++ b/modules/autonavigation/autonavigationhandler.cpp @@ -89,17 +89,6 @@ 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 i; - } - } - return -1; -} - CameraState AutoNavigationHandler::currentCameraState() { CameraState cs; cs.position = camera()->positionVec3(); diff --git a/modules/autonavigation/autonavigationhandler.h b/modules/autonavigation/autonavigationhandler.h index e4d780dc3b..46fb3b8a32 100644 --- a/modules/autonavigation/autonavigationhandler.h +++ b/modules/autonavigation/autonavigationhandler.h @@ -53,7 +53,6 @@ public: Camera* camera() const; const double pathDuration() const; const bool hasFinished() const; - const int currentPathSegmentIndex() const; CameraState currentCameraState(); void updateCamera(double deltaTime); From b15689cd50efe91c0fa64b96c0fa6405c34033ea Mon Sep 17 00:00:00 2001 From: Emma Broman Date: Mon, 24 Feb 2020 08:49:29 -0500 Subject: [PATCH 061/912] Make sure that the anchor node is always updated --- modules/autonavigation/autonavigationhandler.cpp | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) diff --git a/modules/autonavigation/autonavigationhandler.cpp b/modules/autonavigation/autonavigationhandler.cpp index 8a449b8e73..c94a9660af 100644 --- a/modules/autonavigation/autonavigationhandler.cpp +++ b/modules/autonavigation/autonavigationhandler.cpp @@ -135,10 +135,12 @@ void AutoNavigationHandler::updateCamera(double deltaTime) { // When halfway along a curve, set anchor node in orbitalNavigator, to render // visible nodes and add possibility to navigate when we reach the end. - if (abs(relativeDisplacement - 0.5) < 0.001) { - std::string newAnchor = cps.end().referenceNode; - global::navigationHandler.orbitalNavigator().setAnchorNode(newAnchor); - } + std::string targetAnchor = cps.end().referenceNode; + std::string currentAnchor = global::navigationHandler.anchorNode()->identifier(); + + if ((relativeDisplacement > 0.5) && (currentAnchor != targetAnchor)) { + global::navigationHandler.orbitalNavigator().setAnchorNode(targetAnchor); + } glm::dvec3 cameraPosition = cps.getPositionAt(relativeDisplacement); glm::dquat cameraRotation = cps.getRotationAt(relativeDisplacement); From 50d7bdafcf7d4f9c98b380aefd530dd2d82b93b0 Mon Sep 17 00:00:00 2001 From: Emma Broman Date: Tue, 25 Feb 2020 10:35:48 -0500 Subject: [PATCH 062/912] minor cleanup --- data/assets/camera_paths_test.scene | 10 +++++++++- modules/autonavigation/autonavigationhandler.cpp | 6 +++--- modules/autonavigation/autonavigationhandler.h | 6 ++---- modules/autonavigation/autonavigationmodule.cpp | 6 +++--- modules/autonavigation/pathsegment.h | 2 +- 5 files changed, 18 insertions(+), 12 deletions(-) diff --git a/data/assets/camera_paths_test.scene b/data/assets/camera_paths_test.scene index d8ef10a6b3..34c2241a26 100644 --- a/data/assets/camera_paths_test.scene +++ b/data/assets/camera_paths_test.scene @@ -16,7 +16,7 @@ asset.require('scene/solarsystem/sun/glare') asset.require('scene/solarsystem/planets') asset.request('scene/milkyway/milkyway/volume') --- TODO: add non planet object +-- Add non planet object local modelFolder = asset.syncedResource({ Name = "Apollo Models", Type = "HttpSynchronization", @@ -77,6 +77,14 @@ local Keybindings = { GuiPath = "/Interaction", Local = false }, + { + Key = "h", + Name="Toggle Planet Trails", + Command = "local list = openspace.getProperty('{planetTrail_solarSystem}.Renderable.Enabled'); for _,v in pairs(list) do openspace.setPropertyValueSingle(v, not openspace.getPropertyValue(v)) end", + Documentation = "Toggles the visibility of planet trails", + GuiPath = "/Rendering", + Local = false + }, } asset.onInitialize(function () diff --git a/modules/autonavigation/autonavigationhandler.cpp b/modules/autonavigation/autonavigationhandler.cpp index c94a9660af..bdaf74dde8 100644 --- a/modules/autonavigation/autonavigationhandler.cpp +++ b/modules/autonavigation/autonavigationhandler.cpp @@ -77,7 +77,7 @@ Camera* AutoNavigationHandler::camera() const { return global::navigationHandler.camera(); } -const double AutoNavigationHandler::pathDuration() const { +double AutoNavigationHandler::pathDuration() const { double sum = 0.0; for (const PathSegment& ps : _pathSegments) { sum += ps.duration(); @@ -85,7 +85,7 @@ const double AutoNavigationHandler::pathDuration() const { return sum; } -const bool AutoNavigationHandler::hasFinished() const { +bool AutoNavigationHandler::hasFinished() const { return _currentTime > pathDuration(); } @@ -333,7 +333,7 @@ bool AutoNavigationHandler::handleTargetNodeInstruction(const Instruction& instr // TODO: compute defualt height in a better way double defaultHeight = 2 * targetNode->boundingSphere(); - double height = hasHeight? props->height.value() : defaultHeight; + double height = hasHeight ? props->height.value() : defaultHeight; targetPos = computeTargetPositionAtNode( targetNode, diff --git a/modules/autonavigation/autonavigationhandler.h b/modules/autonavigation/autonavigationhandler.h index 46fb3b8a32..85a81bbb98 100644 --- a/modules/autonavigation/autonavigationhandler.h +++ b/modules/autonavigation/autonavigationhandler.h @@ -47,12 +47,10 @@ public: AutoNavigationHandler(); ~AutoNavigationHandler(); - // Mutators - // Accessors Camera* camera() const; - const double pathDuration() const; - const bool hasFinished() const; + double pathDuration() const; + bool hasFinished() const; CameraState currentCameraState(); void updateCamera(double deltaTime); diff --git a/modules/autonavigation/autonavigationmodule.cpp b/modules/autonavigation/autonavigationmodule.cpp index 27ab1554a6..f587d4c4e0 100644 --- a/modules/autonavigation/autonavigationmodule.cpp +++ b/modules/autonavigation/autonavigationmodule.cpp @@ -78,21 +78,21 @@ scripting::LuaLibrary AutoNavigationModule::luaLibrary() const { &autonavigation::luascriptfunctions::generatePath, {}, "table", - "Generate the path as described by the lua table input argument. TODO: Describe how a path instruction is defined?. " //TODO also make this one start path from file + "Generate the path as described by the lua table input argument. TODO: Describe how a path instruction is defined?. " }, { "generatePathFromFile", &autonavigation::luascriptfunctions::generatePathFromFile, {}, "string", - "Read an input file with lua instructions and use those to generate a camera path. TODO: Describe how a path instruction is defined?. " //TODO also make this one start path from file + "Read an input file with lua instructions and use those to generate a camera path. TODO: Describe how a path instruction is defined?. " }, { "getPathPositions", &autonavigation::luascriptfunctions::getPathPositions, {}, "number", - "TODO: " + "FOR DEBUG. Sample positions along the path. The input argument is the number of samples per path segment. " }, { "getControlPoints", diff --git a/modules/autonavigation/pathsegment.h b/modules/autonavigation/pathsegment.h index 6e206ae2cc..cd43901768 100644 --- a/modules/autonavigation/pathsegment.h +++ b/modules/autonavigation/pathsegment.h @@ -87,7 +87,7 @@ private: SpeedFunction _speedFunction; - std::shared_ptr _curve; // OBS! Does it make more sense to use unique_ptr? However, then PathSegments cannot be copied. + std::shared_ptr _curve; }; } // namespace openspace::autonavigation From 3f5123ffb62c48a063485f0bb59fe71d6b0ceebf Mon Sep 17 00:00:00 2001 From: Emma Broman Date: Tue, 25 Feb 2020 17:02:24 -0500 Subject: [PATCH 063/912] Epsilon variable for double comparsons --- modules/autonavigation/pathcurves.cpp | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/modules/autonavigation/pathcurves.cpp b/modules/autonavigation/pathcurves.cpp index c5cb77edd9..ed780e5b52 100644 --- a/modules/autonavigation/pathcurves.cpp +++ b/modules/autonavigation/pathcurves.cpp @@ -31,7 +31,8 @@ #include namespace { -constexpr const char* _loggerCat = "PathCurve"; + constexpr const char* _loggerCat = "PathCurve"; + const double Epsilon = 1E-7; } // namespace namespace openspace::autonavigation { @@ -170,10 +171,10 @@ glm::dvec3 Bezier3Curve::valueAt(double u) { ghoul_assert((nrPoints - 1) % 3 == 0, "A vector containing 3n + 1 control points must be provided!"); ghoul_assert(_nrSegments == (nrTimes - 1), "Number of interval times must match number of intervals"); - if (abs(u) < 0.000001) + if (abs(u) < Epsilon) return _points.front(); - if (abs(1.0 - u) < 0.000001) + if (abs(1.0 - u) < Epsilon) return _points.back(); // compute current segment, by first finding iterator to the first value that is larger than s From cee455f6496f40d2e28bfe26ff11ab5dc8a76ae2 Mon Sep 17 00:00:00 2001 From: Emma Broman Date: Wed, 26 Feb 2020 09:00:36 -0500 Subject: [PATCH 064/912] Avoid errors for targets without bounding spheres --- .../autonavigation/autonavigationhandler.cpp | 54 ++++++++++++++----- .../autonavigation/autonavigationhandler.h | 3 +- 2 files changed, 44 insertions(+), 13 deletions(-) diff --git a/modules/autonavigation/autonavigationhandler.cpp b/modules/autonavigation/autonavigationhandler.cpp index bdaf74dde8..3007edb6d7 100644 --- a/modules/autonavigation/autonavigationhandler.cpp +++ b/modules/autonavigation/autonavigationhandler.cpp @@ -316,6 +316,7 @@ bool AutoNavigationHandler::handleTargetNodeInstruction(const Instruction& instr // Compute end state std::string& identifier = props->targetNode; const SceneGraphNode* targetNode = sceneGraphNode(identifier); + if (!targetNode) { LERROR(fmt::format("Could not find node '{}' to target", identifier)); return false; @@ -329,16 +330,10 @@ bool AutoNavigationHandler::handleTargetNodeInstruction(const Instruction& instr targetNode->worldRotationMatrix() * props->position.value(); } else { - bool hasHeight = props->height.has_value(); - - // TODO: compute defualt height in a better way - double defaultHeight = 2 * targetNode->boundingSphere(); - double height = hasHeight ? props->height.value() : defaultHeight; - targetPos = computeTargetPositionAtNode( targetNode, startState.position, - height + props->height ); } @@ -439,15 +434,50 @@ void AutoNavigationHandler::addPause(CameraState& state, std::optional d _pathSegments.push_back(newSegment); } -glm::dvec3 AutoNavigationHandler::computeTargetPositionAtNode( - const SceneGraphNode* node, glm::dvec3 prevPos, double height) -{ - // TODO: compute actual distance above surface and validate negative values +double AutoNavigationHandler::findValidBoundingSphere(const SceneGraphNode* node) { + const double minAllowedValue = 10.0; // TODO: investigate suitable values (Also, make a property?) + double bs = static_cast(node->boundingSphere()); + if (bs < minAllowedValue) { + + // If the bs of the target is too small, try to find a good value in a child node. + // Only check the closest children, to avoid deep traversal in the scene graph. Also, + // the possibility to find a bounding sphere represents the visual size of the + // target well is higher for these nodes. + for (SceneGraphNode* child : node->children()) { + bs = static_cast(child->boundingSphere()); + if (bs > minAllowedValue) { + LWARNING(fmt::format( + "The scene graph node '{}' has no, or a very small, bounding sphere. Using bounding sphere of child node '{}' in computations.", + node->identifier(), + child->identifier() + )); + + return bs; + } + } + + LWARNING(fmt::format("The scene graph node '{}' has no, or a very small," + "bounding sphere. This might lead to unexpected results.", node->identifier())); + + bs = minAllowedValue; + } + + return bs; +} + +glm::dvec3 AutoNavigationHandler::computeTargetPositionAtNode( + const SceneGraphNode* node, glm::dvec3 prevPos, std::optional heightOptional) +{ glm::dvec3 targetPos = node->worldPosition(); glm::dvec3 targetToPrevVector = prevPos - targetPos; + // TODO: compute position in a more clever way - double radius = static_cast(node->boundingSphere()); + const double radius = findValidBoundingSphere(node); + const double defaultHeight = 2 * radius; + + bool hasHeight = heightOptional.has_value(); + double height = hasHeight ? heightOptional.value() : defaultHeight; // move target position out from surface, along vector to camera targetPos += glm::normalize(targetToPrevVector) * (radius + height); diff --git a/modules/autonavigation/autonavigationhandler.h b/modules/autonavigation/autonavigationhandler.h index 85a81bbb98..d912025d79 100644 --- a/modules/autonavigation/autonavigationhandler.h +++ b/modules/autonavigation/autonavigationhandler.h @@ -76,8 +76,9 @@ private: void addPause(CameraState& state, std::optional duration); + double findValidBoundingSphere(const SceneGraphNode* node); glm::dvec3 computeTargetPositionAtNode(const SceneGraphNode* node, - glm::dvec3 prevPos, double height); + glm::dvec3 prevPos, std::optional height); CameraState cameraStateFromNavigationState( const interaction::NavigationHandler::NavigationState& ns); From 6ec519b0cab5dbf4809551a4db37e9924ca21dc2 Mon Sep 17 00:00:00 2001 From: Emma Broman Date: Wed, 26 Feb 2020 09:49:17 -0500 Subject: [PATCH 065/912] Make minimal allowed bounding sphere a property --- .../autonavigation/autonavigationhandler.cpp | 19 +++++++++++++------ .../autonavigation/autonavigationhandler.h | 2 ++ .../autonavigation/autonavigationmodule.cpp | 4 +++- 3 files changed, 18 insertions(+), 7 deletions(-) diff --git a/modules/autonavigation/autonavigationhandler.cpp b/modules/autonavigation/autonavigationhandler.cpp index 3007edb6d7..f0ff3fc755 100644 --- a/modules/autonavigation/autonavigationhandler.cpp +++ b/modules/autonavigation/autonavigationhandler.cpp @@ -40,6 +40,14 @@ namespace { constexpr const char* _loggerCat = "AutoNavigationHandler"; + + constexpr const openspace::properties::Property::PropertyInfo MinimalBoundingSphereInfo = { + "MinimalBoundingSphere", + "Minimal BoundingSphere", + "The minimal allowed value for a bounding sphere. Used for computation of target " + "positions and path generation, to avoid issues when there is no bounding sphere." + }; + } // namespace namespace openspace::autonavigation { @@ -66,9 +74,9 @@ CurveType stringToCurveType(std::string str) { AutoNavigationHandler::AutoNavigationHandler() : properties::PropertyOwner({ "AutoNavigationHandler" }) + , _minAllowedBoundingSphere(MinimalBoundingSphereInfo, 10.0, 1.0, 3e10) { - // Add the properties - // TODO + addProperty(_minAllowedBoundingSphere); } AutoNavigationHandler::~AutoNavigationHandler() {} // NOLINT @@ -435,10 +443,9 @@ void AutoNavigationHandler::addPause(CameraState& state, std::optional d } double AutoNavigationHandler::findValidBoundingSphere(const SceneGraphNode* node) { - const double minAllowedValue = 10.0; // TODO: investigate suitable values (Also, make a property?) double bs = static_cast(node->boundingSphere()); - if (bs < minAllowedValue) { + if (bs < _minAllowedBoundingSphere) { // If the bs of the target is too small, try to find a good value in a child node. // Only check the closest children, to avoid deep traversal in the scene graph. Also, @@ -446,7 +453,7 @@ double AutoNavigationHandler::findValidBoundingSphere(const SceneGraphNode* node // target well is higher for these nodes. for (SceneGraphNode* child : node->children()) { bs = static_cast(child->boundingSphere()); - if (bs > minAllowedValue) { + if (bs > _minAllowedBoundingSphere) { LWARNING(fmt::format( "The scene graph node '{}' has no, or a very small, bounding sphere. Using bounding sphere of child node '{}' in computations.", node->identifier(), @@ -460,7 +467,7 @@ double AutoNavigationHandler::findValidBoundingSphere(const SceneGraphNode* node LWARNING(fmt::format("The scene graph node '{}' has no, or a very small," "bounding sphere. This might lead to unexpected results.", node->identifier())); - bs = minAllowedValue; + bs = _minAllowedBoundingSphere; } return bs; diff --git a/modules/autonavigation/autonavigationhandler.h b/modules/autonavigation/autonavigationhandler.h index d912025d79..f1efcea511 100644 --- a/modules/autonavigation/autonavigationhandler.h +++ b/modules/autonavigation/autonavigationhandler.h @@ -94,6 +94,8 @@ private: unsigned int _currentSegmentIndex = 0; bool _stopAtTargets; + + properties::DoubleProperty _minAllowedBoundingSphere; }; } // namespace openspace::autonavigation diff --git a/modules/autonavigation/autonavigationmodule.cpp b/modules/autonavigation/autonavigationmodule.cpp index f587d4c4e0..7e0cecc865 100644 --- a/modules/autonavigation/autonavigationmodule.cpp +++ b/modules/autonavigation/autonavigationmodule.cpp @@ -36,7 +36,9 @@ namespace { namespace openspace { -AutoNavigationModule::AutoNavigationModule() : OpenSpaceModule(Name) {} +AutoNavigationModule::AutoNavigationModule() : OpenSpaceModule(Name) { + addPropertySubOwner(_autoNavigationHandler); +} autonavigation::AutoNavigationHandler& AutoNavigationModule::AutoNavigationHandler() { return _autoNavigationHandler; From 0db62b740c0dc0f33afc4fd3d53de4b77da4df0c Mon Sep 17 00:00:00 2001 From: Emma Broman Date: Wed, 26 Feb 2020 10:52:37 -0500 Subject: [PATCH 066/912] Simplify curve type handling using properties (Temporary solution) --- .../autonavigation/autonavigationhandler.cpp | 42 +++++++------------ .../autonavigation/autonavigationhandler.h | 2 + modules/autonavigation/pathsegment.h | 8 ++-- modules/autonavigation/pathspecification.cpp | 16 ------- modules/autonavigation/pathspecification.h | 2 - 5 files changed, 22 insertions(+), 48 deletions(-) diff --git a/modules/autonavigation/autonavigationhandler.cpp b/modules/autonavigation/autonavigationhandler.cpp index f0ff3fc755..49fde4df3b 100644 --- a/modules/autonavigation/autonavigationhandler.cpp +++ b/modules/autonavigation/autonavigationhandler.cpp @@ -48,35 +48,28 @@ namespace { "positions and path generation, to avoid issues when there is no bounding sphere." }; + constexpr const openspace::properties::Property::PropertyInfo DefaultCurveOptionInfo = { + "DefaultCurveOption", + "Default Curve Option", + "The defualt curve type chosen when generating a path, if none is specified." + }; + } // namespace namespace openspace::autonavigation { -// Temporary function to convert a string to one of the bools above. -// TODO: move to a better place / rewrite -CurveType stringToCurveType(std::string str) { - if (str.empty()) - return CurveType::None; - - std::transform(str.begin(), str.end(), str.begin(), ::tolower); - - if (str == "bezier3") { - return CurveType::Bezier3; - } - else if (str == "linear") { - return CurveType::Linear; - } - else { - LERROR(fmt::format("'{}' is not a valid curve type! Choosing default.", str)); - return CurveType::None; - } -} - AutoNavigationHandler::AutoNavigationHandler() : properties::PropertyOwner({ "AutoNavigationHandler" }) , _minAllowedBoundingSphere(MinimalBoundingSphereInfo, 10.0, 1.0, 3e10) + , _defaultCurveOption(DefaultCurveOptionInfo, properties::OptionProperty::DisplayType::Dropdown) { addProperty(_minAllowedBoundingSphere); + + _defaultCurveOption.addOptions({ + { CurveType::Bezier3, "Bezier3" }, + { CurveType::Linear, "Linear"} + }); + addProperty(_defaultCurveOption); } AutoNavigationHandler::~AutoNavigationHandler() {} // NOLINT @@ -160,8 +153,6 @@ void AutoNavigationHandler::updateCamera(double deltaTime) { void AutoNavigationHandler::createPath(PathSpecification& spec) { clearPath(); - _pathCurveType = stringToCurveType(spec.curveType()); - bool success = true; for (int i = 0; i < spec.instructions()->size(); i++) { const Instruction& ins = spec.instructions()->at(i); @@ -412,11 +403,10 @@ void AutoNavigationHandler::addSegment(CameraState& start, startTime = last.startTime() + last.duration(); } - bool hasType = (_pathCurveType != CurveType::None); + // TODO: Improve how curve types are handled + const int curveType = _defaultCurveOption; - PathSegment newSegment = hasType - ? PathSegment{ start, end, startTime, _pathCurveType } - : PathSegment{ start, end, startTime }; + PathSegment newSegment{ start, end, startTime, CurveType(curveType) }; // TODO: handle duration better if (duration.has_value()) { diff --git a/modules/autonavigation/autonavigationhandler.h b/modules/autonavigation/autonavigationhandler.h index f1efcea511..7cea27ca0c 100644 --- a/modules/autonavigation/autonavigationhandler.h +++ b/modules/autonavigation/autonavigationhandler.h @@ -28,6 +28,7 @@ #include #include #include +#include #include #include #include @@ -96,6 +97,7 @@ private: bool _stopAtTargets; properties::DoubleProperty _minAllowedBoundingSphere; + properties::OptionProperty _defaultCurveOption; }; } // namespace openspace::autonavigation diff --git a/modules/autonavigation/pathsegment.h b/modules/autonavigation/pathsegment.h index cd43901768..9e7ad773fe 100644 --- a/modules/autonavigation/pathsegment.h +++ b/modules/autonavigation/pathsegment.h @@ -31,18 +31,18 @@ namespace openspace::autonavigation { -enum class CurveType { +// TODO: place this enum somewhere else +enum CurveType { Bezier3, Linear, - Pause, // OBS! Temporary special case for handling pauses - None + Pause // OBS! Temporary special case for handling pauses }; class PathCurve; class PathSegment { public: - PathSegment(CameraState start, CameraState end, double startTime, CurveType type = CurveType::Bezier3); + PathSegment(CameraState start, CameraState end, double startTime, CurveType type); ~PathSegment() = default; // Mutators diff --git a/modules/autonavigation/pathspecification.cpp b/modules/autonavigation/pathspecification.cpp index 6d1a23ddf5..1543f547b0 100644 --- a/modules/autonavigation/pathspecification.cpp +++ b/modules/autonavigation/pathspecification.cpp @@ -34,7 +34,6 @@ namespace { constexpr const char* KeyInstructions = "Instructions"; constexpr const char* KeyStopAtTargets = "StopAtTargets"; constexpr const char* KeyStartState = "StartState"; - constexpr const char* KeyCurveType = "CurveType"; } // namespace @@ -67,11 +66,6 @@ PathSpecification::PathSpecification(const ghoul::Dictionary& dictionary) { _stopAtTargets = dictionary.value(KeyStopAtTargets); } - // Read desired curve type - if (dictionary.hasValue(KeyCurveType)) { - _curveType = dictionary.value(KeyCurveType); - } - // Read start state if (dictionary.hasValue(KeyStartState)) { auto navStateDict = dictionary.value(KeyStartState); @@ -105,10 +99,6 @@ const bool PathSpecification::stopAtTargets() const { return _stopAtTargets; } -const std::string& PathSpecification::curveType() const { - return _curveType; -} - const interaction::NavigationHandler::NavigationState& PathSpecification::startState() const { return _startState.value(); } @@ -142,12 +132,6 @@ documentation::Documentation PathSpecification::Documentation() { Optional::Yes, "A navigation state that determines the start state for the camera path." }, - { - KeyCurveType, - new StringVerifier, - Optional::Yes, - "A string describing one of the available curve types (TODO: refer to later documentation)." - }, } }; } diff --git a/modules/autonavigation/pathspecification.h b/modules/autonavigation/pathspecification.h index 5ab7ae0441..b9c0e808a0 100644 --- a/modules/autonavigation/pathspecification.h +++ b/modules/autonavigation/pathspecification.h @@ -45,7 +45,6 @@ public: // Accessors const std::vector* instructions() const; const bool stopAtTargets() const; - const std::string& curveType() const; const interaction::NavigationHandler::NavigationState& startState() const; const bool hasStartState() const; @@ -53,7 +52,6 @@ public: private: std::vector _instructions; bool _stopAtTargets = false; // default is to play the entire path without stops - std::string _curveType = std::string(); std::optional _startState; // TODO: maxSpeed or speedFactor or something? From 607404d99633fda4c47d07bb4c4eb231d21a4d5b Mon Sep 17 00:00:00 2001 From: Emma Broman Date: Wed, 26 Feb 2020 11:15:45 -0500 Subject: [PATCH 067/912] Simplify test scene so it only contains our keybinding as extra --- data/assets/camera_paths_test.scene | 74 ++--------------------------- 1 file changed, 3 insertions(+), 71 deletions(-) diff --git a/data/assets/camera_paths_test.scene b/data/assets/camera_paths_test.scene index 34c2241a26..90463865c3 100644 --- a/data/assets/camera_paths_test.scene +++ b/data/assets/camera_paths_test.scene @@ -1,63 +1,6 @@ +asset.require('./base') -local assetHelper = asset.require('util/asset_helper') local sceneHelper = asset.require('util/scene_helper') -local propertyHelper = asset.require('util/property_helper') - -local sunTransforms = asset.require('scene/solarsystem/sun/transforms') - --- Specifying which other assets should be loaded in this scene -asset.require('spice/base') - - -local earthAsset = asset.require('scene/solarsystem/planets/earth/earth') - -asset.require('scene/solarsystem/sun/sun') -asset.require('scene/solarsystem/sun/glare') -asset.require('scene/solarsystem/planets') -asset.request('scene/milkyway/milkyway/volume') - --- Add non planet object -local modelFolder = asset.syncedResource({ - Name = "Apollo Models", - Type = "HttpSynchronization", - Identifier = "apollo_11_models", - Version = 1 -}) - -local object = { - Identifier = "ExampleModel", - Transform = { - Translation = { - Type = "StaticTranslation", - Position = {100000000, 1000, 10000} - } - }, - Renderable = { - Type = "RenderableModel", - Geometry = { - Type = "MultiModelGeometry", - GeometryFile = modelFolder .. "/Apollo_CSM_shrunk_rotated_xy_double_size.obj" - }, - ColorTexture = modelFolder .. "/gray.png", - LightSources = assetHelper.getDefaultLightSources(sunTransforms.SolarSystemBarycenter.Identifier) - }, - GUI = { - Name = "Example Model", - Path = "" - } -} -assetHelper.registerSceneGraphNodesAndExport(asset, { object }) - -assetHelper.requestAll(asset, 'scene/digitaluniverse') - --- Load default key bindings applicable to most scenes -asset.require('util/default_keybindings') -asset.require('util/default_dashboard') - --- Load web gui -local webGui = asset.require('util/webgui') - -asset.request('customization/globebrowsing') -- Keybindings that are specific for this scene local Keybindings = { @@ -76,28 +19,17 @@ local Keybindings = { Documentation = "Stop a camera path.", GuiPath = "/Interaction", Local = false - }, - { - Key = "h", - Name="Toggle Planet Trails", - Command = "local list = openspace.getProperty('{planetTrail_solarSystem}.Renderable.Enabled'); for _,v in pairs(list) do openspace.setPropertyValueSingle(v, not openspace.getPropertyValue(v)) end", - Documentation = "Toggles the visibility of planet trails", - GuiPath = "/Rendering", - Local = false - }, + } } asset.onInitialize(function () - webGui.setCefRoute("onscreen") + sceneHelper.bindKeys(Keybindings) local now = openspace.time.currentWallTime() -- Jump back one day to be able to show complete weather data on Earth. openspace.time.setTime(openspace.time.advancedTime(now, "-1d")) openspace.globebrowsing.goToGeo("Earth", 58.5877, 16.1924, 20000000) - - sceneHelper.bindKeys(Keybindings) - openspace.setDefaultGuiSorting() end) asset.onDeinitialize(function () From 9c65138d73c6fe855d9dcb427f5dfeda36136096 Mon Sep 17 00:00:00 2001 From: Emma Broman Date: Wed, 26 Feb 2020 11:35:57 -0500 Subject: [PATCH 068/912] Bug fix: No longer use reference for current path segment, since its value is set later in the code --- modules/autonavigation/autonavigationhandler.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/modules/autonavigation/autonavigationhandler.cpp b/modules/autonavigation/autonavigationhandler.cpp index 49fde4df3b..5b19dee732 100644 --- a/modules/autonavigation/autonavigationhandler.cpp +++ b/modules/autonavigation/autonavigationhandler.cpp @@ -105,14 +105,14 @@ void AutoNavigationHandler::updateCamera(double deltaTime) { _currentTime += deltaTime; - PathSegment& cps = _pathSegments[_currentSegmentIndex]; + PathSegment cps = _pathSegments[_currentSegmentIndex]; // Have we walked past the current segment? if (_currentTime > cps.endTime()) { _currentSegmentIndex++; _distanceAlongCurrentSegment = 0.0; - // WStepped past the last segment + // Stepped past the last segment if (_currentSegmentIndex > _pathSegments.size() - 1) { LINFO("Reached end of path."); _isPlaying = false; From 384d904e529df34e7838f5b1f7754775e8d1ef7a Mon Sep 17 00:00:00 2001 From: Emma Broman Date: Thu, 27 Feb 2020 10:04:39 -0500 Subject: [PATCH 069/912] Include the ISS in our test scene --- data/assets/camera_paths_test.scene | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/data/assets/camera_paths_test.scene b/data/assets/camera_paths_test.scene index 90463865c3..744ee031f0 100644 --- a/data/assets/camera_paths_test.scene +++ b/data/assets/camera_paths_test.scene @@ -2,6 +2,9 @@ asset.require('./base') local sceneHelper = asset.require('util/scene_helper') +asset.require('scene/solarsystem/planets/earth/earth') +asset.require('scene/solarsystem/planets/earth/satellites/satellites.asset') + -- Keybindings that are specific for this scene local Keybindings = { { @@ -30,6 +33,8 @@ asset.onInitialize(function () openspace.time.setTime(openspace.time.advancedTime(now, "-1d")) openspace.globebrowsing.goToGeo("Earth", 58.5877, 16.1924, 20000000) + + openspace.setPropertyValue("{earth_satellites}.Renderable.Enabled", false) end) asset.onDeinitialize(function () From aa0ae9bc73e3579d84b155d08aa0086096a6d30f Mon Sep 17 00:00:00 2001 From: Emma Broman Date: Thu, 27 Feb 2020 10:07:04 -0500 Subject: [PATCH 070/912] rename function valueAt to positionAt --- modules/autonavigation/pathcurves.cpp | 12 ++++++------ modules/autonavigation/pathcurves.h | 9 +++++---- modules/autonavigation/pathsegment.cpp | 2 +- 3 files changed, 12 insertions(+), 11 deletions(-) diff --git a/modules/autonavigation/pathcurves.cpp b/modules/autonavigation/pathcurves.cpp index ed780e5b52..44e157a3b4 100644 --- a/modules/autonavigation/pathcurves.cpp +++ b/modules/autonavigation/pathcurves.cpp @@ -49,20 +49,20 @@ double PathCurve::arcLength(double limit) { const double h = limit / (double)n; // Points to be multiplied by 1 - double endPoints = glm::length(valueAt(0.0 + h) - valueAt(0.0)) + glm::length(valueAt(1.0) - valueAt(1.0 - h)); + double endPoints = glm::length(positionAt(0.0 + h) - positionAt(0.0)) + glm::length(positionAt(1.0) - positionAt(1.0 - h)); // Points to be multiplied by 4 double times4 = 0.0; for (int i = 1; i < n; i += 2) { float t = h * i; - times4 += glm::length(valueAt(t + h) - valueAt(t)); + times4 += glm::length(positionAt(t + h) - positionAt(t)); } // Points to be multiplied by 2 double times2 = 0.0; for (int i = 2; i < n; i += 2) { float t = h * i; - times2 += glm::length(valueAt(t + h) - valueAt(t)); + times2 += glm::length(positionAt(t + h) - positionAt(t)); } return (h / 3.0) * (endPoints + 4.0 * times4 + 2.0 *times2); @@ -161,7 +161,7 @@ Bezier3Curve::Bezier3Curve(CameraState& start, CameraState& end) { } // Interpolate a list of control points and knot times -glm::dvec3 Bezier3Curve::valueAt(double u) { +glm::dvec3 Bezier3Curve::positionAt(double u) { ghoul_assert(u >= 0 && u <= 1.0, "Interpolation variable out of range [0, 1]"); size_t nrPoints = _points.size(); @@ -208,7 +208,7 @@ LinearCurve::LinearCurve(CameraState& start, CameraState& end) { _length = glm::distance(end.position, start.position); } -glm::dvec3 LinearCurve::valueAt(double u) { +glm::dvec3 LinearCurve::positionAt(double u) { ghoul_assert(u >= 0 && u <= 1.0, "Interpolation variable out of range [0, 1]"); return interpolation::linear(u, _points[0], _points[1]); } @@ -219,7 +219,7 @@ PauseCurve::PauseCurve(CameraState& state) { _length = 1.0; // OBS! Length of a pause curve makes no sense, but it also doesn't matter } -glm::dvec3 PauseCurve::valueAt(double u) { +glm::dvec3 PauseCurve::positionAt(double u) { ghoul_assert(u >= 0 && u <= 1.0, "Interpolation variable out of range [0, 1]"); return _points[0]; } diff --git a/modules/autonavigation/pathcurves.h b/modules/autonavigation/pathcurves.h index 1c1d530074..9a56c2bb79 100644 --- a/modules/autonavigation/pathcurves.h +++ b/modules/autonavigation/pathcurves.h @@ -40,7 +40,7 @@ public: double arcLength(double limit = 1.0); // comppute the value at the relative length s [0,1] - virtual glm::dvec3 valueAt(double u) = 0; + virtual glm::dvec3 positionAt(double u) = 0; std::vector getPoints(); // for debugging @@ -55,7 +55,7 @@ protected: class Bezier3Curve : public PathCurve { public: Bezier3Curve(CameraState& start, CameraState& end); - glm::dvec3 valueAt(double u); + glm::dvec3 positionAt(double u); private: void initParameterIntervals(); @@ -67,7 +67,8 @@ private: class LinearCurve : public PathCurve { public: LinearCurve(CameraState& start, CameraState& end); - glm::dvec3 valueAt(double u); + glm::dvec3 positionAt(double u); + }; // OBS! This is a temporary class specialised for handling pauses. @@ -75,7 +76,7 @@ public: class PauseCurve : public PathCurve { public: PauseCurve(CameraState& state); - glm::dvec3 valueAt(double u); + glm::dvec3 positionAt(double u); }; } // namespace openspace::autonavigation diff --git a/modules/autonavigation/pathsegment.cpp b/modules/autonavigation/pathsegment.cpp index 86a7eb9596..a4566344c6 100644 --- a/modules/autonavigation/pathsegment.cpp +++ b/modules/autonavigation/pathsegment.cpp @@ -95,7 +95,7 @@ double PathSegment::speedAtTime(double time) { } glm::dvec3 PathSegment::getPositionAt(double u) const { - return _curve->valueAt(u); + return _curve->positionAt(u); } glm::dquat PathSegment::getRotationAt(double u) const { From d606a569bc9debba13216b872372df34228fa93d Mon Sep 17 00:00:00 2001 From: Emma Broman Date: Thu, 27 Feb 2020 10:48:44 -0500 Subject: [PATCH 071/912] minor refactor --- modules/autonavigation/autonavigationhandler.cpp | 1 + modules/autonavigation/pathcurves.cpp | 4 ++-- modules/autonavigation/pathcurves.h | 12 +++++++++--- modules/autonavigation/pathsegment.h | 10 +--------- 4 files changed, 13 insertions(+), 14 deletions(-) diff --git a/modules/autonavigation/autonavigationhandler.cpp b/modules/autonavigation/autonavigationhandler.cpp index 5b19dee732..1f57ea9073 100644 --- a/modules/autonavigation/autonavigationhandler.cpp +++ b/modules/autonavigation/autonavigationhandler.cpp @@ -26,6 +26,7 @@ #include #include +#include #include #include #include diff --git a/modules/autonavigation/pathcurves.cpp b/modules/autonavigation/pathcurves.cpp index 44e157a3b4..ceac66e908 100644 --- a/modules/autonavigation/pathcurves.cpp +++ b/modules/autonavigation/pathcurves.cpp @@ -74,7 +74,7 @@ std::vector PathCurve::getPoints() { return _points; } -Bezier3Curve::Bezier3Curve(CameraState& start, CameraState& end) { +Bezier3Curve::Bezier3Curve(const CameraState& start, const CameraState& end) { glm::dvec3 startNodePos = sceneGraphNode(start.referenceNode)->worldPosition(); glm::dvec3 endNodePos = sceneGraphNode(end.referenceNode)->worldPosition(); double startNodeRadius = sceneGraphNode(start.referenceNode)->boundingSphere(); @@ -202,7 +202,7 @@ void Bezier3Curve::initParameterIntervals() { _parameterIntervals.swap(newIntervals); } -LinearCurve::LinearCurve(CameraState& start, CameraState& end) { +LinearCurve::LinearCurve(const CameraState& start, const CameraState& end) { _points.push_back(start.position); _points.push_back(end.position); _length = glm::distance(end.position, start.position); diff --git a/modules/autonavigation/pathcurves.h b/modules/autonavigation/pathcurves.h index 9a56c2bb79..a986ceb9d0 100644 --- a/modules/autonavigation/pathcurves.h +++ b/modules/autonavigation/pathcurves.h @@ -31,7 +31,12 @@ namespace openspace::autonavigation { -// OBS! Path curves has curve parameter in range [0,1] +enum CurveType { + Bezier3, + Linear, + Pause // OBS! Temporary special case for handling pauses +}; + class PathCurve { public: virtual ~PathCurve() = 0; @@ -50,11 +55,12 @@ protected: // the total length of the curve (approximated) double _length; + }; class Bezier3Curve : public PathCurve { public: - Bezier3Curve(CameraState& start, CameraState& end); + Bezier3Curve(const CameraState& start, const CameraState& end); glm::dvec3 positionAt(double u); private: @@ -66,7 +72,7 @@ private: class LinearCurve : public PathCurve { public: - LinearCurve(CameraState& start, CameraState& end); + LinearCurve(const CameraState& start, const CameraState& end); glm::dvec3 positionAt(double u); }; diff --git a/modules/autonavigation/pathsegment.h b/modules/autonavigation/pathsegment.h index 9e7ad773fe..e06c2d2cb7 100644 --- a/modules/autonavigation/pathsegment.h +++ b/modules/autonavigation/pathsegment.h @@ -26,20 +26,12 @@ #define __OPENSPACE_MODULE___PATHSEGMENT___H__ #include +#include #include #include namespace openspace::autonavigation { -// TODO: place this enum somewhere else -enum CurveType { - Bezier3, - Linear, - Pause // OBS! Temporary special case for handling pauses -}; - -class PathCurve; - class PathSegment { public: PathSegment(CameraState start, CameraState end, double startTime, CurveType type); From d704c95ca0cd12ccf11a1f81e649394d7f934e44 Mon Sep 17 00:00:00 2001 From: Emma Broman Date: Thu, 27 Feb 2020 13:30:39 -0500 Subject: [PATCH 072/912] Move rotation logic into its own class --- modules/autonavigation/CMakeLists.txt | 2 + modules/autonavigation/pathcurves.cpp | 11 +- modules/autonavigation/pathcurves.h | 13 +- modules/autonavigation/pathsegment.cpp | 57 +-------- modules/autonavigation/pathsegment.h | 1 - .../autonavigation/rotationinterpolator.cpp | 113 ++++++++++++++++++ modules/autonavigation/rotationinterpolator.h | 60 ++++++++++ 7 files changed, 192 insertions(+), 65 deletions(-) create mode 100644 modules/autonavigation/rotationinterpolator.cpp create mode 100644 modules/autonavigation/rotationinterpolator.h diff --git a/modules/autonavigation/CMakeLists.txt b/modules/autonavigation/CMakeLists.txt index 3a0241d38e..93d4c4bbbb 100644 --- a/modules/autonavigation/CMakeLists.txt +++ b/modules/autonavigation/CMakeLists.txt @@ -33,6 +33,7 @@ set(HEADER_FILES ${CMAKE_CURRENT_SOURCE_DIR}/pathspecification.h ${CMAKE_CURRENT_SOURCE_DIR}/pathsegment.h ${CMAKE_CURRENT_SOURCE_DIR}/pathcurves.h + ${CMAKE_CURRENT_SOURCE_DIR}/rotationinterpolator.h ) source_group("Header Files" FILES ${HEADER_FILES}) @@ -45,6 +46,7 @@ set(SOURCE_FILES ${CMAKE_CURRENT_SOURCE_DIR}/pathspecification.cpp ${CMAKE_CURRENT_SOURCE_DIR}/pathsegment.cpp ${CMAKE_CURRENT_SOURCE_DIR}/pathcurves.cpp + ${CMAKE_CURRENT_SOURCE_DIR}/rotationinterpolator.cpp ) source_group("Source Files" FILES ${SOURCE_FILES}) diff --git a/modules/autonavigation/pathcurves.cpp b/modules/autonavigation/pathcurves.cpp index ceac66e908..1268e11611 100644 --- a/modules/autonavigation/pathcurves.cpp +++ b/modules/autonavigation/pathcurves.cpp @@ -68,6 +68,10 @@ double PathCurve::arcLength(double limit) { return (h / 3.0) * (endPoints + 4.0 * times4 + 2.0 *times2); } +glm::dquat PathCurve::rotationAt(double u) { + return _rotationInterpolator.rotationAt(u); +} + // TODO: remove when not needed // Created for debugging std::vector PathCurve::getPoints() { @@ -75,6 +79,9 @@ std::vector PathCurve::getPoints() { } Bezier3Curve::Bezier3Curve(const CameraState& start, const CameraState& end) { + // default rotation interpolation + _rotationInterpolator = RotationInterpolator{ start, end, this, PiecewiseSlerp }; + glm::dvec3 startNodePos = sceneGraphNode(start.referenceNode)->worldPosition(); glm::dvec3 endNodePos = sceneGraphNode(end.referenceNode)->worldPosition(); double startNodeRadius = sceneGraphNode(start.referenceNode)->boundingSphere(); @@ -206,6 +213,7 @@ LinearCurve::LinearCurve(const CameraState& start, const CameraState& end) { _points.push_back(start.position); _points.push_back(end.position); _length = glm::distance(end.position, start.position); + _rotationInterpolator = RotationInterpolator{ start, end, this, Slerp }; } glm::dvec3 LinearCurve::positionAt(double u) { @@ -214,9 +222,10 @@ glm::dvec3 LinearCurve::positionAt(double u) { } // TODO: Iprove handling of pauses -PauseCurve::PauseCurve(CameraState& state) { +PauseCurve::PauseCurve(const CameraState& state) { _points.push_back(state.position); _length = 1.0; // OBS! Length of a pause curve makes no sense, but it also doesn't matter + _rotationInterpolator = RotationInterpolator{ state, state, this, Fixed }; } glm::dvec3 PauseCurve::positionAt(double u) { diff --git a/modules/autonavigation/pathcurves.h b/modules/autonavigation/pathcurves.h index a986ceb9d0..e5239ff511 100644 --- a/modules/autonavigation/pathcurves.h +++ b/modules/autonavigation/pathcurves.h @@ -26,6 +26,7 @@ #define __OPENSPACE_MODULE_AUTONAVIGATION___PATHCURVE___H__ #include +#include #include #include @@ -44,18 +45,17 @@ public: const double length() const; double arcLength(double limit = 1.0); - // comppute the value at the relative length s [0,1] + // u is interpolation parameter in [0,1] (relative length) virtual glm::dvec3 positionAt(double u) = 0; + glm::dquat rotationAt(double u); std::vector getPoints(); // for debugging protected: - // the points used for creating the curve (e.g. control points of a Bezier curve) std::vector _points; + double _length; // the total length of the curve (approximated) - // the total length of the curve (approximated) - double _length; - + RotationInterpolator _rotationInterpolator; }; class Bezier3Curve : public PathCurve { @@ -74,14 +74,13 @@ class LinearCurve : public PathCurve { public: LinearCurve(const CameraState& start, const CameraState& end); glm::dvec3 positionAt(double u); - }; // OBS! This is a temporary class specialised for handling pauses. // TODO: handle better in the future. class PauseCurve : public PathCurve { public: - PauseCurve(CameraState& state); + PauseCurve(const CameraState& state); glm::dvec3 positionAt(double u); }; diff --git a/modules/autonavigation/pathsegment.cpp b/modules/autonavigation/pathsegment.cpp index a4566344c6..f8d919b913 100644 --- a/modules/autonavigation/pathsegment.cpp +++ b/modules/autonavigation/pathsegment.cpp @@ -99,62 +99,7 @@ glm::dvec3 PathSegment::getPositionAt(double u) const { } glm::dquat PathSegment::getRotationAt(double u) const { - // TODO: improve how rotation is computed - - switch (_curveType) { - case CurveType::Bezier3: - { - return piecewiseSlerpRotation(u); - break; - } - default: - { - double uSlerp = helpers::shiftAndScale(u, 0.1, 0.9); - uSlerp = ghoul::cubicEaseInOut(uSlerp); - return glm::slerp(_start.rotation, _end.rotation, uSlerp); - } - } -} - -// Interpolate between a number of keyframes for orientation using SLERP -const glm::dquat PathSegment::piecewiseSlerpRotation(double u) const { - // breakpoints for subintervals - const double u1 = 0.3; - const double u2 = 0.8; // TODO: these should probably be based on distance - - glm::dvec3 startNodePos = sceneGraphNode(_start.referenceNode)->worldPosition(); - glm::dvec3 endNodePos = sceneGraphNode(_end.referenceNode)->worldPosition(); - - glm::dvec3 startUpVec = _start.rotation * glm::dvec3(0.0, 1.0, 0.0); - glm::dvec3 endUpVec = _end.rotation * glm::dvec3(0.0, 1.0, 0.0); - - glm::dquat lookAtStartQ = - helpers::getLookAtQuaternion(getPositionAt(u1), startNodePos, startUpVec); - - glm::dquat lookAtEndQ = - helpers::getLookAtQuaternion(getPositionAt(u2), endNodePos, endUpVec); - - std::vector> keyframes{ - {_start.rotation, 0.0}, - {lookAtStartQ, u1}, - {lookAtEndQ, u2}, - {_end.rotation, 1.0}, - }; - - // Find the current segment and compute interpolation - glm::dquat result; - for (int i = 0; i < keyframes.size() - 1; ++i) { - double ui = keyframes[i].second; - double uNext = keyframes[i + 1].second; - if (u <= uNext) { - double uScaled = (u - ui) / (uNext - ui); - uScaled = ghoul::quadraticEaseInOut(uScaled); - result = glm::slerp(keyframes[i].first, keyframes[i + 1].first, uScaled); - break; - } - } - - return result; + return _curve->rotationAt(u); } // Initialise the curve, based on the start, end state and curve type diff --git a/modules/autonavigation/pathsegment.h b/modules/autonavigation/pathsegment.h index e06c2d2cb7..71dca7c3d4 100644 --- a/modules/autonavigation/pathsegment.h +++ b/modules/autonavigation/pathsegment.h @@ -57,7 +57,6 @@ public: glm::dquat getRotationAt(double u) const; private: - const glm::dquat piecewiseSlerpRotation(double u) const; void initCurve(); // The speed function describing the shape of the speed curve. Values in [0,1]. diff --git a/modules/autonavigation/rotationinterpolator.cpp b/modules/autonavigation/rotationinterpolator.cpp new file mode 100644 index 0000000000..cd37e5eae3 --- /dev/null +++ b/modules/autonavigation/rotationinterpolator.cpp @@ -0,0 +1,113 @@ +/***************************************************************************************** +* * +* OpenSpace * +* * +* Copyright (c) 2014-2019 * +* * +* Permission is hereby granted, free of charge, to any person obtaining a copy of this * +* software and associated documentation files (the "Software"), to deal in the Software * +* without restriction, including without limitation the rights to use, copy, modify, * +* merge, publish, distribute, sublicense, and/or sell copies of the Software, and to * +* permit persons to whom the Software is furnished to do so, subject to the following * +* conditions: * +* * +* The above copyright notice and this permission notice shall be included in all copies * +* or substantial portions of the Software. * +* * +* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, * +* INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A * +* PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT * +* HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF * +* CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE * +* OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. * +****************************************************************************************/ + +#include + +#include +#include +#include +#include + +namespace { + constexpr const char* _loggerCat = "RotationInterpolator"; +} // namespace + +namespace openspace::autonavigation { + +RotationInterpolator::RotationInterpolator( + const CameraState& start, const CameraState& end, PathCurve* curve, RotationMethod method) + : _start(start), _end(end), _method(method) +{ + _curve = curve; +} + +glm::dquat RotationInterpolator::rotationAt(double u) { + switch (_method) + { + case Slerp: + return easedSlerp(u); + break; + case PiecewiseSlerp: + return piecewiseSlerp(u); + break; + case Fixed: + return _start.rotation; + break; + default: + LERROR("Non-implemented orientation interpolation method!"); + return _start.rotation; + break; + } +} + +glm::dquat RotationInterpolator::easedSlerp(double u) { + double uScaled = helpers::shiftAndScale(u, 0.1, 0.9); + uScaled = ghoul::cubicEaseInOut(uScaled); + return glm::slerp(_start.rotation, _end.rotation, uScaled); +} + +// Interpolate between a number of keyframes for orientation using SLERP +glm::dquat RotationInterpolator::piecewiseSlerp(double u) { + ghoul_assert(_curve, "Rotation interpolation requires access to curve positions."); + + // breakpoints for subintervals + const double u1 = 0.3; + const double u2 = 0.8; // TODO: these should probably be based on distance + + glm::dvec3 startNodePos = sceneGraphNode(_start.referenceNode)->worldPosition(); + glm::dvec3 endNodePos = sceneGraphNode(_end.referenceNode)->worldPosition(); + + glm::dvec3 startUpVec = _start.rotation * glm::dvec3(0.0, 1.0, 0.0); + glm::dvec3 endUpVec = _end.rotation * glm::dvec3(0.0, 1.0, 0.0); + + glm::dquat lookAtStartQ = + helpers::getLookAtQuaternion(_curve->positionAt(u1), startNodePos, startUpVec); + + glm::dquat lookAtEndQ = + helpers::getLookAtQuaternion(_curve->positionAt(u2), endNodePos, endUpVec); + + std::vector> keyframes{ + {_start.rotation, 0.0}, + {lookAtStartQ, u1}, + {lookAtEndQ, u2}, + {_end.rotation, 1.0} + }; + + // Find the current segment and compute interpolation + glm::dquat result; + for (int i = 0; i < keyframes.size() - 1; ++i) { + double ui = keyframes[i].second; + double uNext = keyframes[i + 1].second; + if (u <= uNext) { + double uScaled = (u - ui) / (uNext - ui); + uScaled = ghoul::quadraticEaseInOut(uScaled); + result = glm::slerp(keyframes[i].first, keyframes[i + 1].first, uScaled); + break; + } + } + + return result; +} + +} // namespace openspace::autonavigation diff --git a/modules/autonavigation/rotationinterpolator.h b/modules/autonavigation/rotationinterpolator.h new file mode 100644 index 0000000000..7eacd70849 --- /dev/null +++ b/modules/autonavigation/rotationinterpolator.h @@ -0,0 +1,60 @@ +/***************************************************************************************** + * * + * OpenSpace * + * * + * Copyright (c) 2014-2019 * + * * + * Permission is hereby granted, free of charge, to any person obtaining a copy of this * + * software and associated documentation files (the "Software"), to deal in the Software * + * without restriction, including without limitation the rights to use, copy, modify, * + * merge, publish, distribute, sublicense, and/or sell copies of the Software, and to * + * permit persons to whom the Software is furnished to do so, subject to the following * + * conditions: * + * * + * The above copyright notice and this permission notice shall be included in all copies * + * or substantial portions of the Software. * + * * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, * + * INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A * + * PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT * + * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF * + * CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE * + * OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. * + ****************************************************************************************/ + +#ifndef __OPENSPACE_MODULE_AUTONAVIGATION___ROTATIONINTERPOLATOR___H__ +#define __OPENSPACE_MODULE_AUTONAVIGATION___ROTATIONINTERPOLATOR___H__ + +#include + +namespace openspace::autonavigation { + +enum RotationMethod { + Slerp, + PiecewiseSlerp, + Fixed +}; + +class PathCurve; + +class RotationInterpolator { +public: + RotationInterpolator() = default; + RotationInterpolator(const CameraState& start, const CameraState& end, + PathCurve* curve, RotationMethod method); + + glm::dquat rotationAt(double u); + +private: + CameraState _start; + CameraState _end; + PathCurve* _curve = nullptr; + RotationMethod _method; + + glm::dquat easedSlerp(double u); + glm::dquat piecewiseSlerp(double u); +}; + +} // namespace openspace::autonavigation + +#endif // __OPENSPACE_MODULE_AUTONAVIGATION___ROTATIONINTERPOLATOR___H__ From 4c9874c8d330b90a419aeda34c6663e36e4c720e Mon Sep 17 00:00:00 2001 From: Emma Broman Date: Thu, 27 Feb 2020 13:47:12 -0500 Subject: [PATCH 073/912] Some cleanup --- .../autonavigation/autonavigationhandler.cpp | 34 +++++++++---------- .../autonavigation/autonavigationhandler.h | 6 ++-- modules/autonavigation/pathsegment.cpp | 12 +++---- modules/autonavigation/pathsegment.h | 4 +-- modules/autonavigation/pathspecification.h | 5 +-- 5 files changed, 29 insertions(+), 32 deletions(-) diff --git a/modules/autonavigation/autonavigationhandler.cpp b/modules/autonavigation/autonavigationhandler.cpp index 1f57ea9073..79c7020287 100644 --- a/modules/autonavigation/autonavigationhandler.cpp +++ b/modules/autonavigation/autonavigationhandler.cpp @@ -106,10 +106,10 @@ void AutoNavigationHandler::updateCamera(double deltaTime) { _currentTime += deltaTime; - PathSegment cps = _pathSegments[_currentSegmentIndex]; + PathSegment currentSegment = _pathSegments[_currentSegmentIndex]; // Have we walked past the current segment? - if (_currentTime > cps.endTime()) { + if (_currentTime > currentSegment.endTime()) { _currentSegmentIndex++; _distanceAlongCurrentSegment = 0.0; @@ -120,7 +120,7 @@ void AutoNavigationHandler::updateCamera(double deltaTime) { return; } - cps = _pathSegments[_currentSegmentIndex]; + currentSegment = _pathSegments[_currentSegmentIndex]; if (_stopAtTargets) { pausePath(); @@ -128,27 +128,25 @@ void AutoNavigationHandler::updateCamera(double deltaTime) { } } + // compute interpolated camera state double prevDistance = _distanceAlongCurrentSegment; - double displacement = deltaTime * cps.speedAtTime(_currentTime - cps.startTime()); + double displacement = deltaTime * currentSegment.speedAtTime(_currentTime - currentSegment.startTime()); _distanceAlongCurrentSegment += displacement; - double relativeDisplacement = _distanceAlongCurrentSegment / cps.pathLength(); + double relativeDisplacement = _distanceAlongCurrentSegment / currentSegment.pathLength(); relativeDisplacement = std::max(0.0, std::min(relativeDisplacement, 1.0)); - // When halfway along a curve, set anchor node in orbitalNavigator, to render - // visible nodes and add possibility to navigate when we reach the end. - std::string targetAnchor = cps.end().referenceNode; + CameraState newState = currentSegment.interpolate(relativeDisplacement); + + // Set anchor node in orbitalNavigator, to render visible nodes and add activate + // navigation when we reach the end. std::string currentAnchor = global::navigationHandler.anchorNode()->identifier(); + if (currentAnchor != newState.referenceNode) { + global::navigationHandler.orbitalNavigator().setAnchorNode(newState.referenceNode); + } - if ((relativeDisplacement > 0.5) && (currentAnchor != targetAnchor)) { - global::navigationHandler.orbitalNavigator().setAnchorNode(targetAnchor); - } - - glm::dvec3 cameraPosition = cps.getPositionAt(relativeDisplacement); - glm::dquat cameraRotation = cps.getRotationAt(relativeDisplacement); - - camera()->setPositionVec3(cameraPosition); - camera()->setRotation(cameraRotation); + camera()->setPositionVec3(newState.position); + camera()->setRotation(newState.rotation); } void AutoNavigationHandler::createPath(PathSpecification& spec) { @@ -244,7 +242,7 @@ std::vector AutoNavigationHandler::getCurvePositions(int nPerSegment for (PathSegment &p : _pathSegments) { for (double u = 0.0; u < 1.0; u += du) { - auto position = p.getPositionAt(u); + auto position = p.interpolate(u).position; positions.push_back(position); } } diff --git a/modules/autonavigation/autonavigationhandler.h b/modules/autonavigation/autonavigationhandler.h index 7cea27ca0c..2a3e168133 100644 --- a/modules/autonavigation/autonavigationhandler.h +++ b/modules/autonavigation/autonavigationhandler.h @@ -84,13 +84,11 @@ private: CameraState cameraStateFromNavigationState( const interaction::NavigationHandler::NavigationState& ns); - // This list essentially represents the camera path + // this list essentially represents the camera path std::vector _pathSegments; - CurveType _pathCurveType; // TEST: create a path with just one type of curve bool _isPlaying = false; - - double _currentTime; + double _currentTime = 0.0; double _distanceAlongCurrentSegment = 0.0; unsigned int _currentSegmentIndex = 0; diff --git a/modules/autonavigation/pathsegment.cpp b/modules/autonavigation/pathsegment.cpp index f8d919b913..9202b52504 100644 --- a/modules/autonavigation/pathsegment.cpp +++ b/modules/autonavigation/pathsegment.cpp @@ -94,12 +94,12 @@ double PathSegment::speedAtTime(double time) { return (pathLength() * _speedFunction.value(t)) / _speedFunction.integratedSum; } -glm::dvec3 PathSegment::getPositionAt(double u) const { - return _curve->positionAt(u); -} - -glm::dquat PathSegment::getRotationAt(double u) const { - return _curve->rotationAt(u); +CameraState PathSegment::interpolate(double u) const { + CameraState cs; + cs.position = _curve->positionAt(u); + cs.rotation = _curve->rotationAt(u); + cs.referenceNode = (u > 0.5) ? _end.referenceNode : _start.referenceNode; + return cs; } // Initialise the curve, based on the start, end state and curve type diff --git a/modules/autonavigation/pathsegment.h b/modules/autonavigation/pathsegment.h index 71dca7c3d4..dcfd1fbe38 100644 --- a/modules/autonavigation/pathsegment.h +++ b/modules/autonavigation/pathsegment.h @@ -53,8 +53,8 @@ public: double speedAtTime(double time); - glm::dvec3 getPositionAt(double u) const; - glm::dquat getRotationAt(double u) const; + // Interpolate along the curve to get the current camera state + CameraState interpolate(double u) const; private: void initCurve(); diff --git a/modules/autonavigation/pathspecification.h b/modules/autonavigation/pathspecification.h index b9c0e808a0..7e775b7672 100644 --- a/modules/autonavigation/pathspecification.h +++ b/modules/autonavigation/pathspecification.h @@ -34,6 +34,7 @@ namespace openspace::autonavigation { class PathSpecification { + using NavigationState = interaction::NavigationHandler::NavigationState; public: PathSpecification() = default; @@ -45,14 +46,14 @@ public: // Accessors const std::vector* instructions() const; const bool stopAtTargets() const; - const interaction::NavigationHandler::NavigationState& startState() const; + const NavigationState& startState() const; const bool hasStartState() const; private: std::vector _instructions; bool _stopAtTargets = false; // default is to play the entire path without stops - std::optional _startState; + std::optional _startState; // TODO: maxSpeed or speedFactor or something? }; From db51a51a5ef98d9800b9ed7b4658c305c9a9049f Mon Sep 17 00:00:00 2001 From: Lingis Date: Wed, 4 Mar 2020 16:32:33 -0500 Subject: [PATCH 074/912] LookAt orientation interpolator option added, for debug --- .../autonavigation/rotationinterpolator.cpp | 21 +++++++++++++++++++ modules/autonavigation/rotationinterpolator.h | 4 +++- 2 files changed, 24 insertions(+), 1 deletion(-) diff --git a/modules/autonavigation/rotationinterpolator.cpp b/modules/autonavigation/rotationinterpolator.cpp index cd37e5eae3..3c80c7a0b9 100644 --- a/modules/autonavigation/rotationinterpolator.cpp +++ b/modules/autonavigation/rotationinterpolator.cpp @@ -54,6 +54,9 @@ glm::dquat RotationInterpolator::rotationAt(double u) { case Fixed: return _start.rotation; break; + case LookAt: + return lookAtInterpolator(u); + break; default: LERROR("Non-implemented orientation interpolation method!"); return _start.rotation; @@ -67,6 +70,24 @@ glm::dquat RotationInterpolator::easedSlerp(double u) { return glm::slerp(_start.rotation, _end.rotation, uScaled); } +// Look at start node until tStart, then turn to look at end node from tEnd +// Will overwrite rotation of navigation states! +glm::dquat RotationInterpolator::lookAtInterpolator(double u) { + double tStart = 0.15; + double tEnd = 0.7; + double uNew = helpers::shiftAndScale(u, tStart, tEnd); + uNew = ghoul::cubicEaseInOut(uNew); + + + glm::dvec3 startNodePos = sceneGraphNode(_start.referenceNode)->worldPosition(); + glm::dvec3 endNodePos = sceneGraphNode(_end.referenceNode)->worldPosition(); + glm::dvec3 lookAtPos = interpolation::linear(uNew, startNodePos, endNodePos); + + glm::dvec3 startUpVec = _start.rotation * glm::dvec3(0.0, 1.0, 0.0); + + return helpers::getLookAtQuaternion(_curve->positionAt(u), lookAtPos, startUpVec); +} + // Interpolate between a number of keyframes for orientation using SLERP glm::dquat RotationInterpolator::piecewiseSlerp(double u) { ghoul_assert(_curve, "Rotation interpolation requires access to curve positions."); diff --git a/modules/autonavigation/rotationinterpolator.h b/modules/autonavigation/rotationinterpolator.h index 7eacd70849..a0b4968e39 100644 --- a/modules/autonavigation/rotationinterpolator.h +++ b/modules/autonavigation/rotationinterpolator.h @@ -32,7 +32,8 @@ namespace openspace::autonavigation { enum RotationMethod { Slerp, PiecewiseSlerp, - Fixed + Fixed, + LookAt }; class PathCurve; @@ -52,6 +53,7 @@ private: RotationMethod _method; glm::dquat easedSlerp(double u); + glm::dquat lookAtInterpolator(double u); // for debug glm::dquat piecewiseSlerp(double u); }; From 3a38792f67a6d6c7ff9d2aa0bb8aecdc57f061db Mon Sep 17 00:00:00 2001 From: Emma Broman Date: Fri, 6 Mar 2020 11:52:16 -0500 Subject: [PATCH 075/912] consistent types --- modules/autonavigation/pathcurves.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/modules/autonavigation/pathcurves.cpp b/modules/autonavigation/pathcurves.cpp index 1268e11611..d7969616a2 100644 --- a/modules/autonavigation/pathcurves.cpp +++ b/modules/autonavigation/pathcurves.cpp @@ -54,14 +54,14 @@ double PathCurve::arcLength(double limit) { // Points to be multiplied by 4 double times4 = 0.0; for (int i = 1; i < n; i += 2) { - float t = h * i; + double t = h * i; times4 += glm::length(positionAt(t + h) - positionAt(t)); } // Points to be multiplied by 2 double times2 = 0.0; for (int i = 2; i < n; i += 2) { - float t = h * i; + double t = h * i; times2 += glm::length(positionAt(t + h) - positionAt(t)); } From a38aa3ff8e9325792939eae96026b359685e69c1 Mon Sep 17 00:00:00 2001 From: Emma Broman Date: Fri, 6 Mar 2020 16:54:21 -0500 Subject: [PATCH 076/912] Make that we always reach the target (u = 1.0) --- .../autonavigation/autonavigationhandler.cpp | 42 ++++++++++--------- 1 file changed, 22 insertions(+), 20 deletions(-) diff --git a/modules/autonavigation/autonavigationhandler.cpp b/modules/autonavigation/autonavigationhandler.cpp index 79c7020287..87b06a500b 100644 --- a/modules/autonavigation/autonavigationhandler.cpp +++ b/modules/autonavigation/autonavigationhandler.cpp @@ -104,10 +104,30 @@ void AutoNavigationHandler::updateCamera(double deltaTime) { if (!_isPlaying || _pathSegments.empty()) return; - _currentTime += deltaTime; + //TODO: early out if simulation time is not stopped. PathSegment currentSegment = _pathSegments[_currentSegmentIndex]; + // compute interpolated camera state + double prevDistance = _distanceAlongCurrentSegment; + double displacement = deltaTime * currentSegment.speedAtTime(_currentTime - currentSegment.startTime()); + _distanceAlongCurrentSegment += displacement; + + double relativeDisplacement = _distanceAlongCurrentSegment / currentSegment.pathLength(); + relativeDisplacement = std::max(0.0, std::min(relativeDisplacement, 1.0)); + + CameraState newState = currentSegment.interpolate(relativeDisplacement); + + // Set anchor node in orbitalNavigator, to render visible nodes and add activate + // navigation when we reach the end. + std::string currentAnchor = global::navigationHandler.anchorNode()->identifier(); + if (currentAnchor != newState.referenceNode) { + global::navigationHandler.orbitalNavigator().setAnchorNode(newState.referenceNode); + } + + camera()->setPositionVec3(newState.position); + camera()->setRotation(newState.rotation); + // Have we walked past the current segment? if (_currentTime > currentSegment.endTime()) { _currentSegmentIndex++; @@ -128,25 +148,7 @@ void AutoNavigationHandler::updateCamera(double deltaTime) { } } - // compute interpolated camera state - double prevDistance = _distanceAlongCurrentSegment; - double displacement = deltaTime * currentSegment.speedAtTime(_currentTime - currentSegment.startTime()); - _distanceAlongCurrentSegment += displacement; - - double relativeDisplacement = _distanceAlongCurrentSegment / currentSegment.pathLength(); - relativeDisplacement = std::max(0.0, std::min(relativeDisplacement, 1.0)); - - CameraState newState = currentSegment.interpolate(relativeDisplacement); - - // Set anchor node in orbitalNavigator, to render visible nodes and add activate - // navigation when we reach the end. - std::string currentAnchor = global::navigationHandler.anchorNode()->identifier(); - if (currentAnchor != newState.referenceNode) { - global::navigationHandler.orbitalNavigator().setAnchorNode(newState.referenceNode); - } - - camera()->setPositionVec3(newState.position); - camera()->setRotation(newState.rotation); + _currentTime += deltaTime; } void AutoNavigationHandler::createPath(PathSpecification& spec) { From edaf37b260c3c8bc84df6beee4484ede4f6d4bf3 Mon Sep 17 00:00:00 2001 From: Emma Broman Date: Mon, 9 Mar 2020 13:52:23 -0400 Subject: [PATCH 077/912] Early out if simulation not paused. Also, temporary pause on play, for testing --- modules/autonavigation/autonavigationhandler.cpp | 13 +++++++++++-- 1 file changed, 11 insertions(+), 2 deletions(-) diff --git a/modules/autonavigation/autonavigationhandler.cpp b/modules/autonavigation/autonavigationhandler.cpp index 87b06a500b..b7ee3ff8d9 100644 --- a/modules/autonavigation/autonavigationhandler.cpp +++ b/modules/autonavigation/autonavigationhandler.cpp @@ -33,6 +33,7 @@ #include #include #include +#include #include #include #include @@ -104,8 +105,6 @@ void AutoNavigationHandler::updateCamera(double deltaTime) { if (!_isPlaying || _pathSegments.empty()) return; - //TODO: early out if simulation time is not stopped. - PathSegment currentSegment = _pathSegments[_currentSegmentIndex]; // compute interpolated camera state @@ -193,6 +192,16 @@ void AutoNavigationHandler::startPath() { LERROR("Cannot start an empty path."); return; } + + // TODO: remove this line at the end of our project. Used to simplify testing + global::timeManager.setPause(true); + + //OBS! Until we can handle simulation time: early out if not paused + if (!global::timeManager.isPaused()) { + LERROR("Simulation time must be paused to run a camera path."); + return; + } + LINFO("Starting path..."); _currentTime = 0.0; _isPlaying = true; From 24e29955a379eeb3ee907849453b6418dc895883 Mon Sep 17 00:00:00 2001 From: Emma Broman Date: Mon, 9 Mar 2020 17:52:00 -0400 Subject: [PATCH 078/912] refactor --- modules/autonavigation/CMakeLists.txt | 1 + .../autonavigation/autonavigationhandler.cpp | 160 +++++------------- .../autonavigation/autonavigationhandler.h | 21 +-- modules/autonavigation/camerastate.cpp | 80 +++++++++ modules/autonavigation/camerastate.h | 8 +- 5 files changed, 140 insertions(+), 130 deletions(-) create mode 100644 modules/autonavigation/camerastate.cpp diff --git a/modules/autonavigation/CMakeLists.txt b/modules/autonavigation/CMakeLists.txt index 93d4c4bbbb..09d08feee4 100644 --- a/modules/autonavigation/CMakeLists.txt +++ b/modules/autonavigation/CMakeLists.txt @@ -41,6 +41,7 @@ set(SOURCE_FILES ${CMAKE_CURRENT_SOURCE_DIR}/autonavigationmodule.cpp ${CMAKE_CURRENT_SOURCE_DIR}/autonavigationmodule_lua.inl ${CMAKE_CURRENT_SOURCE_DIR}/autonavigationhandler.cpp + ${CMAKE_CURRENT_SOURCE_DIR}/camerastate.cpp ${CMAKE_CURRENT_SOURCE_DIR}/helperfunctions.cpp ${CMAKE_CURRENT_SOURCE_DIR}/instruction.cpp ${CMAKE_CURRENT_SOURCE_DIR}/pathspecification.cpp diff --git a/modules/autonavigation/autonavigationhandler.cpp b/modules/autonavigation/autonavigationhandler.cpp index b7ee3ff8d9..956d82ed29 100644 --- a/modules/autonavigation/autonavigationhandler.cpp +++ b/modules/autonavigation/autonavigationhandler.cpp @@ -100,6 +100,10 @@ CameraState AutoNavigationHandler::currentCameraState() { return cs; } +CameraState AutoNavigationHandler::lastState() { + return _pathSegments.empty() ? currentCameraState() : _pathSegments.back().end(); +} + void AutoNavigationHandler::updateCamera(double deltaTime) { ghoul_assert(camera() != nullptr, "Camera must not be nullptr"); @@ -167,7 +171,7 @@ void AutoNavigationHandler::createPath(PathSpecification& spec) { // Check if we have a specified start state. If so, update the first segment if (spec.hasStartState() && _pathSegments.size() > 0) { - CameraState startState = cameraStateFromNavigationState(spec.startState()); + CameraState startState{ spec.startState() }; _pathSegments[0].setStart(startState); } @@ -279,20 +283,20 @@ std::vector AutoNavigationHandler::getControlPoints() { return points; } -bool AutoNavigationHandler::handleInstruction(const Instruction& instruction, int index) { +bool AutoNavigationHandler::handleInstruction(const Instruction& ins, int index) { bool success = true; - switch (instruction.type) + switch (ins.type) { case InstructionType::TargetNode: - success = handleTargetNodeInstruction(instruction); + success = handleTargetNodeInstruction(ins); break; case InstructionType::NavigationState: - success = handleNavigationStateInstruction(instruction); + success = handleNavigationStateInstruction(ins); break; case InstructionType::Pause: - success = handlePauseInstruction(instruction); + success = handlePauseInstruction(ins); break; default: @@ -309,19 +313,16 @@ bool AutoNavigationHandler::handleInstruction(const Instruction& instruction, in return true; } -bool AutoNavigationHandler::handleTargetNodeInstruction(const Instruction& instruction) { +bool AutoNavigationHandler::handleTargetNodeInstruction(const Instruction& ins) { // Verify instruction type TargetNodeInstructionProps* props = - dynamic_cast(instruction.props.get()); + dynamic_cast(ins.props.get()); if (!props) { LERROR("Could not handle target node instruction."); return false; } - CameraState startState = - _pathSegments.empty() ? currentCameraState() : _pathSegments.back().end(); - // Compute end state std::string& identifier = props->targetNode; const SceneGraphNode* targetNode = sceneGraphNode(identifier); @@ -339,11 +340,18 @@ bool AutoNavigationHandler::handleTargetNodeInstruction(const Instruction& instr targetNode->worldRotationMatrix() * props->position.value(); } else { - targetPos = computeTargetPositionAtNode( - targetNode, - startState.position, - props->height - ); + glm::dvec3 nodePos = targetNode->worldPosition(); + glm::dvec3 nodeToPrev= lastState().position - nodePos; + // TODO: compute position in a more clever way + + const double radius = findValidBoundingSphere(targetNode); + const double defaultHeight = 2 * radius; + + bool hasHeight = props->height.has_value(); + double height = hasHeight ? props->height.value() : defaultHeight; + + // move target position out from surface, along vector to camera + targetPos = nodePos + glm::normalize(nodeToPrev) * (radius + height); } glm::dmat4 lookAtMat = glm::lookAt( @@ -354,57 +362,54 @@ bool AutoNavigationHandler::handleTargetNodeInstruction(const Instruction& instr glm::dquat targetRot = glm::normalize(glm::inverse(glm::quat_cast(lookAtMat))); - CameraState endState = CameraState{ targetPos, targetRot, identifier }; + CameraState endState{ targetPos, targetRot, identifier }; - addSegment(startState, endState, instruction.props->duration); + addSegment(endState, ins.props->duration); return true; } -bool AutoNavigationHandler::handleNavigationStateInstruction( - const Instruction& instruction) -{ +bool AutoNavigationHandler::handleNavigationStateInstruction(const Instruction& ins) { // Verify instruction type NavigationStateInstructionProps* props = - dynamic_cast(instruction.props.get()); + dynamic_cast(ins.props.get()); if (!props) { LERROR(fmt::format("Could not handle navigation state instruction.")); return false; } - CameraState startState = - _pathSegments.empty() ? currentCameraState() : _pathSegments.back().end(); + CameraState endState{ props->navState }; - interaction::NavigationHandler::NavigationState ns = props->navState; - CameraState endState = cameraStateFromNavigationState(ns); - - addSegment(startState, endState, instruction.props->duration); + addSegment(endState, ins.props->duration); return true; } -bool AutoNavigationHandler::handlePauseInstruction(const Instruction& instruction) -{ +bool AutoNavigationHandler::handlePauseInstruction(const Instruction& ins) { // Verify instruction type PauseInstructionProps* props = - dynamic_cast(instruction.props.get()); + dynamic_cast(ins.props.get()); if (!props) { LERROR(fmt::format("Could not handle pause instruction.")); return false; } - CameraState state =_pathSegments.empty() - ? currentCameraState() - : _pathSegments.back().end(); - // TODO: implement more complex behavior later - addPause(state, instruction.props->duration); + addPause(ins.props->duration); return true; } -void AutoNavigationHandler::addSegment(CameraState& start, - CameraState& end, std::optional duration) +void AutoNavigationHandler::addPause(std::optional duration) { + CameraState current = currentCameraState(); + CameraState state =_pathSegments.empty() ? current : _pathSegments.back().end(); + + // TODO: implement more complex pause behaviour + + addSegment(state, duration); +} + +void AutoNavigationHandler::addSegment(CameraState& state, std::optional duration) { // compute startTime double startTime = 0.0; @@ -416,24 +421,7 @@ void AutoNavigationHandler::addSegment(CameraState& start, // TODO: Improve how curve types are handled const int curveType = _defaultCurveOption; - PathSegment newSegment{ start, end, startTime, CurveType(curveType) }; - - // TODO: handle duration better - if (duration.has_value()) { - newSegment.setDuration(duration.value()); - } - _pathSegments.push_back(newSegment); -} - -void AutoNavigationHandler::addPause(CameraState& state, std::optional duration) { - // compute startTime - double startTime = 0.0; - if (!_pathSegments.empty()) { - PathSegment& last = _pathSegments.back(); - startTime = last.startTime() + last.duration(); - } - - PathSegment newSegment = PathSegment{ state, state, startTime, CurveType::Pause }; + PathSegment newSegment{ lastState(), state, startTime, CurveType(curveType) }; // TODO: handle duration better if (duration.has_value()) { @@ -473,64 +461,4 @@ double AutoNavigationHandler::findValidBoundingSphere(const SceneGraphNode* node return bs; } -glm::dvec3 AutoNavigationHandler::computeTargetPositionAtNode( - const SceneGraphNode* node, glm::dvec3 prevPos, std::optional heightOptional) -{ - glm::dvec3 targetPos = node->worldPosition(); - glm::dvec3 targetToPrevVector = prevPos - targetPos; - // TODO: compute position in a more clever way - - const double radius = findValidBoundingSphere(node); - const double defaultHeight = 2 * radius; - - bool hasHeight = heightOptional.has_value(); - double height = hasHeight ? heightOptional.value() : defaultHeight; - - // move target position out from surface, along vector to camera - targetPos += glm::normalize(targetToPrevVector) * (radius + height); - - return targetPos; -} - -CameraState AutoNavigationHandler::cameraStateFromNavigationState( - const interaction::NavigationHandler::NavigationState& ns) -{ - // OBS! The following code is exactly the same as used in - // NavigationHandler::applyNavigationState. Should probably be made into a function. - const SceneGraphNode* referenceFrame = sceneGraphNode(ns.referenceFrame); - const SceneGraphNode* anchorNode = sceneGraphNode(ns.anchor); // The anchor is also the target - - if (!anchorNode) { - LERROR(fmt::format("Could not find node '{}' to target. Returning empty state.", ns.anchor)); - return CameraState{}; - } - - const glm::dvec3 anchorWorldPosition = anchorNode->worldPosition(); - const glm::dmat3 referenceFrameTransform = referenceFrame->worldRotationMatrix(); - - const glm::dvec3 targetPositionWorld = anchorWorldPosition + - glm::dvec3(referenceFrameTransform * glm::dvec4(ns.position, 1.0)); - - glm::dvec3 up = ns.up.has_value() ? - glm::normalize(referenceFrameTransform * ns.up.value()) : - glm::dvec3(0.0, 1.0, 0.0); - - // Construct vectors of a "neutral" view, i.e. when the aim is centered in view. - glm::dvec3 neutralView = - glm::normalize(anchorWorldPosition - targetPositionWorld); - - glm::dquat neutralCameraRotation = glm::inverse(glm::quat_cast(glm::lookAt( - glm::dvec3(0.0), - neutralView, - up - ))); - - glm::dquat pitchRotation = glm::angleAxis(ns.pitch, glm::dvec3(1.f, 0.f, 0.f)); - glm::dquat yawRotation = glm::angleAxis(ns.yaw, glm::dvec3(0.f, -1.f, 0.f)); - - glm::quat targetRotation = neutralCameraRotation * yawRotation * pitchRotation; - - return CameraState{ targetPositionWorld, targetRotation, ns.anchor }; -} - } // namespace openspace::autonavigation diff --git a/modules/autonavigation/autonavigationhandler.h b/modules/autonavigation/autonavigationhandler.h index 2a3e168133..899d7795f7 100644 --- a/modules/autonavigation/autonavigationhandler.h +++ b/modules/autonavigation/autonavigationhandler.h @@ -52,7 +52,8 @@ public: Camera* camera() const; double pathDuration() const; bool hasFinished() const; - CameraState currentCameraState(); + CameraState currentCameraState(); + CameraState lastState(); void updateCamera(double deltaTime); void createPath(PathSpecification& spec); @@ -67,22 +68,16 @@ public: std::vector getControlPoints(); //debug private: - bool handleInstruction(const Instruction& instruction, int index); + bool handleInstruction(const Instruction& ins, int index); - bool handleTargetNodeInstruction(const Instruction& instruction); - bool handleNavigationStateInstruction(const Instruction& instruction); - bool handlePauseInstruction(const Instruction& instruction); + bool handleTargetNodeInstruction(const Instruction& ins); + bool handleNavigationStateInstruction(const Instruction& ins); + bool handlePauseInstruction(const Instruction& ins); - void addSegment(CameraState& start, CameraState& end, std::optional duration); - - void addPause(CameraState& state, std::optional duration); + void addPause(std::optional duration); + void addSegment(CameraState& state, std::optional duration); double findValidBoundingSphere(const SceneGraphNode* node); - glm::dvec3 computeTargetPositionAtNode(const SceneGraphNode* node, - glm::dvec3 prevPos, std::optional height); - - CameraState cameraStateFromNavigationState( - const interaction::NavigationHandler::NavigationState& ns); // this list essentially represents the camera path std::vector _pathSegments; diff --git a/modules/autonavigation/camerastate.cpp b/modules/autonavigation/camerastate.cpp new file mode 100644 index 0000000000..4b45633f71 --- /dev/null +++ b/modules/autonavigation/camerastate.cpp @@ -0,0 +1,80 @@ +/***************************************************************************************** + * * + * OpenSpace * + * * + * Copyright (c) 2014-2019 * + * * + * Permission is hereby granted, free of charge, to any person obtaining a copy of this * + * software and associated documentation files (the "Software"), to deal in the Software * + * without restriction, including without limitation the rights to use, copy, modify, * + * merge, publish, distribute, sublicense, and/or sell copies of the Software, and to * + * permit persons to whom the Software is furnished to do so, subject to the following * + * conditions: * + * * + * The above copyright notice and this permission notice shall be included in all copies * + * or substantial portions of the Software. * + * * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, * + * INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A * + * PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT * + * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF * + * CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE * + * OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. * + ****************************************************************************************/ + +#include + +#include +#include +#include + +namespace { + constexpr const char* _loggerCat = "CameraState"; +} // namespace + +namespace openspace::autonavigation { + +CameraState::CameraState(const glm::dvec3& pos, const glm::dquat& rot, const std::string& ref) + : position(pos), rotation(rot), referenceNode(ref) +{} + +CameraState::CameraState(const NavigationState& ns) { + // OBS! The following code is exactly the same as used in + // NavigationHandler::applyNavigationState. Should probably be made into a function. + const SceneGraphNode* referenceFrame = sceneGraphNode(ns.referenceFrame); + const SceneGraphNode* anchorNode = sceneGraphNode(ns.anchor); // The anchor is also the target + + if (!anchorNode) { + LERROR(fmt::format("Could not find node '{}' to target. Returning empty state.", ns.anchor)); + return; + } + + const glm::dvec3 anchorWorldPosition = anchorNode->worldPosition(); + const glm::dmat3 referenceFrameTransform = referenceFrame->worldRotationMatrix(); + + position = anchorWorldPosition + + glm::dvec3(referenceFrameTransform * glm::dvec4(ns.position, 1.0)); + + glm::dvec3 up = ns.up.has_value() ? + glm::normalize(referenceFrameTransform * ns.up.value()) : + glm::dvec3(0.0, 1.0, 0.0); + + // Construct vectors of a "neutral" view, i.e. when the aim is centered in view. + glm::dvec3 neutralView = + glm::normalize(anchorWorldPosition - position); + + glm::dquat neutralCameraRotation = glm::inverse(glm::quat_cast(glm::lookAt( + glm::dvec3(0.0), + neutralView, + up + ))); + + glm::dquat pitchRotation = glm::angleAxis(ns.pitch, glm::dvec3(1.f, 0.f, 0.f)); + glm::dquat yawRotation = glm::angleAxis(ns.yaw, glm::dvec3(0.f, -1.f, 0.f)); + + rotation = neutralCameraRotation * yawRotation * pitchRotation; + + referenceNode = ns.referenceFrame; +} + +} // namespace openspace::autonavigation diff --git a/modules/autonavigation/camerastate.h b/modules/autonavigation/camerastate.h index 0512f8fc7f..543e90bc83 100644 --- a/modules/autonavigation/camerastate.h +++ b/modules/autonavigation/camerastate.h @@ -25,15 +25,21 @@ #ifndef __OPENSPACE_MODULE___CAMERASTATE___H__ #define __OPENSPACE_MODULE___CAMERASTATE___H__ +#include #include #include namespace openspace::autonavigation { struct CameraState { + using NavigationState = interaction::NavigationHandler::NavigationState; + CameraState() = default; + CameraState(const glm::dvec3& pos, const glm::dquat& rot, const std::string& ref); + CameraState(const NavigationState& ns); + glm::dvec3 position; glm::dquat rotation; - std::string referenceNode; + std::string referenceNode; // OBS! A bit unclear name, since position and rotation is in world coords }; } // namespace openspace::autonavigation From ed47272e8e7a13810d6fbf472260e4996585bb98 Mon Sep 17 00:00:00 2001 From: Emma Broman Date: Tue, 10 Mar 2020 11:06:46 -0400 Subject: [PATCH 079/912] Replace CameraState with a Waypoint and CameraPose struct --- modules/autonavigation/CMakeLists.txt | 4 +- .../autonavigation/autonavigationhandler.cpp | 76 +++++++++---------- .../autonavigation/autonavigationhandler.h | 8 +- modules/autonavigation/camerastate.h | 30 +++++--- modules/autonavigation/pathcurves.cpp | 40 +++++----- modules/autonavigation/pathcurves.h | 8 +- modules/autonavigation/pathsegment.cpp | 17 +++-- modules/autonavigation/pathsegment.h | 18 ++--- .../autonavigation/rotationinterpolator.cpp | 26 +++---- modules/autonavigation/rotationinterpolator.h | 8 +- .../{camerastate.cpp => waypoint.cpp} | 27 ++++--- modules/autonavigation/waypoint.h | 57 ++++++++++++++ 12 files changed, 198 insertions(+), 121 deletions(-) rename modules/autonavigation/{camerastate.cpp => waypoint.cpp} (84%) create mode 100644 modules/autonavigation/waypoint.h diff --git a/modules/autonavigation/CMakeLists.txt b/modules/autonavigation/CMakeLists.txt index 09d08feee4..df7a1321f9 100644 --- a/modules/autonavigation/CMakeLists.txt +++ b/modules/autonavigation/CMakeLists.txt @@ -27,13 +27,13 @@ include(${OPENSPACE_CMAKE_EXT_DIR}/module_definition.cmake) set(HEADER_FILES ${CMAKE_CURRENT_SOURCE_DIR}/autonavigationmodule.h ${CMAKE_CURRENT_SOURCE_DIR}/autonavigationhandler.h - ${CMAKE_CURRENT_SOURCE_DIR}/camerastate.h ${CMAKE_CURRENT_SOURCE_DIR}/helperfunctions.h ${CMAKE_CURRENT_SOURCE_DIR}/instruction.h ${CMAKE_CURRENT_SOURCE_DIR}/pathspecification.h ${CMAKE_CURRENT_SOURCE_DIR}/pathsegment.h ${CMAKE_CURRENT_SOURCE_DIR}/pathcurves.h ${CMAKE_CURRENT_SOURCE_DIR}/rotationinterpolator.h + ${CMAKE_CURRENT_SOURCE_DIR}/waypoint.h ) source_group("Header Files" FILES ${HEADER_FILES}) @@ -41,13 +41,13 @@ set(SOURCE_FILES ${CMAKE_CURRENT_SOURCE_DIR}/autonavigationmodule.cpp ${CMAKE_CURRENT_SOURCE_DIR}/autonavigationmodule_lua.inl ${CMAKE_CURRENT_SOURCE_DIR}/autonavigationhandler.cpp - ${CMAKE_CURRENT_SOURCE_DIR}/camerastate.cpp ${CMAKE_CURRENT_SOURCE_DIR}/helperfunctions.cpp ${CMAKE_CURRENT_SOURCE_DIR}/instruction.cpp ${CMAKE_CURRENT_SOURCE_DIR}/pathspecification.cpp ${CMAKE_CURRENT_SOURCE_DIR}/pathsegment.cpp ${CMAKE_CURRENT_SOURCE_DIR}/pathcurves.cpp ${CMAKE_CURRENT_SOURCE_DIR}/rotationinterpolator.cpp + ${CMAKE_CURRENT_SOURCE_DIR}/waypoint.cpp ) source_group("Source Files" FILES ${SOURCE_FILES}) diff --git a/modules/autonavigation/autonavigationhandler.cpp b/modules/autonavigation/autonavigationhandler.cpp index 956d82ed29..e38e825563 100644 --- a/modules/autonavigation/autonavigationhandler.cpp +++ b/modules/autonavigation/autonavigationhandler.cpp @@ -81,27 +81,25 @@ Camera* AutoNavigationHandler::camera() const { } double AutoNavigationHandler::pathDuration() const { - double sum = 0.0; - for (const PathSegment& ps : _pathSegments) { - sum += ps.duration(); - } - return sum; + if (_pathSegments.empty()) + return 0.0; + + return _pathSegments.back().endTime(); } bool AutoNavigationHandler::hasFinished() const { return _currentTime > pathDuration(); } -CameraState AutoNavigationHandler::currentCameraState() { - CameraState cs; - cs.position = camera()->positionVec3(); - cs.rotation = camera()->rotationQuaternion(); - cs.referenceNode = global::navigationHandler.anchorNode()->identifier(); - return cs; +Waypoint AutoNavigationHandler::wayPointFromCamera() { + glm::dvec3 pos = camera()->positionVec3(); + glm::dquat rot = camera()->rotationQuaternion(); + std::string node = global::navigationHandler.anchorNode()->identifier(); + return Waypoint{pos, rot, node}; } -CameraState AutoNavigationHandler::lastState() { - return _pathSegments.empty() ? currentCameraState() : _pathSegments.back().end(); +Waypoint AutoNavigationHandler::lastWayPoint() { + return _pathSegments.empty() ? wayPointFromCamera() : _pathSegments.back().end(); } void AutoNavigationHandler::updateCamera(double deltaTime) { @@ -119,17 +117,18 @@ void AutoNavigationHandler::updateCamera(double deltaTime) { double relativeDisplacement = _distanceAlongCurrentSegment / currentSegment.pathLength(); relativeDisplacement = std::max(0.0, std::min(relativeDisplacement, 1.0)); - CameraState newState = currentSegment.interpolate(relativeDisplacement); + CameraPose newPose = currentSegment.interpolate(relativeDisplacement); + std::string newAnchor = currentSegment.getCurrentAnchor(relativeDisplacement); // Set anchor node in orbitalNavigator, to render visible nodes and add activate // navigation when we reach the end. std::string currentAnchor = global::navigationHandler.anchorNode()->identifier(); - if (currentAnchor != newState.referenceNode) { - global::navigationHandler.orbitalNavigator().setAnchorNode(newState.referenceNode); + if (currentAnchor != newAnchor) { + global::navigationHandler.orbitalNavigator().setAnchorNode(newAnchor); } - camera()->setPositionVec3(newState.position); - camera()->setRotation(newState.rotation); + camera()->setPositionVec3(newPose.position); + camera()->setRotation(newPose.rotation); // Have we walked past the current segment? if (_currentTime > currentSegment.endTime()) { @@ -169,9 +168,9 @@ void AutoNavigationHandler::createPath(PathSpecification& spec) { // OBS! Would it be better to save the spec in the handler class? _stopAtTargets = spec.stopAtTargets(); - // Check if we have a specified start state. If so, update the first segment + // Check if we have a specified start navigation state. If so, update first segment if (spec.hasStartState() && _pathSegments.size() > 0) { - CameraState startState{ spec.startState() }; + Waypoint startState{ spec.startState() }; _pathSegments[0].setStart(startState); } @@ -233,9 +232,8 @@ void AutoNavigationHandler::continuePath() { LINFO("Continuing path..."); - // Recompute start camera state for the upcoming path segment, to avoid clipping to - // the old camera state. - _pathSegments[_currentSegmentIndex].setStart(currentCameraState()); + // Recompute start camera state for the upcoming path segment, + _pathSegments[_currentSegmentIndex].setStart(wayPointFromCamera()); _isPlaying = true; } @@ -340,8 +338,10 @@ bool AutoNavigationHandler::handleTargetNodeInstruction(const Instruction& ins) targetNode->worldRotationMatrix() * props->position.value(); } else { + // TODO: Instead of this case, allow the curve to set its final position + glm::dvec3 nodePos = targetNode->worldPosition(); - glm::dvec3 nodeToPrev= lastState().position - nodePos; + glm::dvec3 nodeToPrev= lastWayPoint().position() - nodePos; // TODO: compute position in a more clever way const double radius = findValidBoundingSphere(targetNode); @@ -362,7 +362,7 @@ bool AutoNavigationHandler::handleTargetNodeInstruction(const Instruction& ins) glm::dquat targetRot = glm::normalize(glm::inverse(glm::quat_cast(lookAtMat))); - CameraState endState{ targetPos, targetRot, identifier }; + Waypoint endState{ targetPos, targetRot, identifier }; addSegment(endState, ins.props->duration); return true; @@ -378,7 +378,7 @@ bool AutoNavigationHandler::handleNavigationStateInstruction(const Instruction& return false; } - CameraState endState{ props->navState }; + Waypoint endState{ props->navState }; addSegment(endState, ins.props->duration); return true; @@ -401,27 +401,27 @@ bool AutoNavigationHandler::handlePauseInstruction(const Instruction& ins) { } void AutoNavigationHandler::addPause(std::optional duration) { - CameraState current = currentCameraState(); - CameraState state =_pathSegments.empty() ? current : _pathSegments.back().end(); + double startTime = pathDuration(); + Waypoint waypoint = lastWayPoint(); + PathSegment newSegment{ waypoint, waypoint, startTime, CurveType::Pause }; - // TODO: implement more complex pause behaviour + // TODO: implement more complex behavior later - addSegment(state, duration); + // TODO: handle duration better + if (duration.has_value()) { + newSegment.setDuration(duration.value()); + } + _pathSegments.push_back(newSegment); } -void AutoNavigationHandler::addSegment(CameraState& state, std::optional duration) +void AutoNavigationHandler::addSegment(Waypoint& waypoint, std::optional duration) { - // compute startTime - double startTime = 0.0; - if (!_pathSegments.empty()) { - PathSegment& last = _pathSegments.back(); - startTime = last.startTime() + last.duration(); - } + double startTime = pathDuration(); // TODO: Improve how curve types are handled const int curveType = _defaultCurveOption; - PathSegment newSegment{ lastState(), state, startTime, CurveType(curveType) }; + PathSegment newSegment{ lastWayPoint(), waypoint, startTime, CurveType(curveType) }; // TODO: handle duration better if (duration.has_value()) { diff --git a/modules/autonavigation/autonavigationhandler.h b/modules/autonavigation/autonavigationhandler.h index 899d7795f7..15a0ab1b62 100644 --- a/modules/autonavigation/autonavigationhandler.h +++ b/modules/autonavigation/autonavigationhandler.h @@ -39,7 +39,7 @@ namespace openspace { namespace openspace::autonavigation { -struct CameraState; +struct Waypoint; struct Instruction; class PathSpecification; @@ -52,8 +52,8 @@ public: Camera* camera() const; double pathDuration() const; bool hasFinished() const; - CameraState currentCameraState(); - CameraState lastState(); + Waypoint wayPointFromCamera(); + Waypoint lastWayPoint(); void updateCamera(double deltaTime); void createPath(PathSpecification& spec); @@ -75,7 +75,7 @@ private: bool handlePauseInstruction(const Instruction& ins); void addPause(std::optional duration); - void addSegment(CameraState& state, std::optional duration); + void addSegment(Waypoint& state, std::optional duration); double findValidBoundingSphere(const SceneGraphNode* node); diff --git a/modules/autonavigation/camerastate.h b/modules/autonavigation/camerastate.h index 543e90bc83..b5eca2b709 100644 --- a/modules/autonavigation/camerastate.h +++ b/modules/autonavigation/camerastate.h @@ -22,8 +22,8 @@ * OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. * ****************************************************************************************/ -#ifndef __OPENSPACE_MODULE___CAMERASTATE___H__ -#define __OPENSPACE_MODULE___CAMERASTATE___H__ +#ifndef __OPENSPACE_MODULE___WAYPOINT___H__ +#define __OPENSPACE_MODULE___WAYPOINT___H__ #include #include @@ -31,17 +31,27 @@ namespace openspace::autonavigation { -struct CameraState { - using NavigationState = interaction::NavigationHandler::NavigationState; - CameraState() = default; - CameraState(const glm::dvec3& pos, const glm::dquat& rot, const std::string& ref); - CameraState(const NavigationState& ns); - +struct CameraPose { glm::dvec3 position; glm::dquat rotation; - std::string referenceNode; // OBS! A bit unclear name, since position and rotation is in world coords +}; + +struct WayPoint { + using NavigationState = interaction::NavigationHandler::NavigationState; + + // TODO: create waypoints from a dictionary + + WayPoint() = default; + WayPoint(const glm::dvec3& pos, const glm::dquat& rot, const std::string& ref); + WayPoint(const NavigationState& ns); + + glm::dvec3 position() const; + glm::dquat rotation() const; + + CameraPose pose; + std::string node; }; } // namespace openspace::autonavigation -#endif // __OPENSPACE_MODULE___CAMERASTATE___H__ +#endif // __OPENSPACE_MODULE___WAYPOINT___H__ diff --git a/modules/autonavigation/pathcurves.cpp b/modules/autonavigation/pathcurves.cpp index d7969616a2..88e5b4171e 100644 --- a/modules/autonavigation/pathcurves.cpp +++ b/modules/autonavigation/pathcurves.cpp @@ -78,17 +78,17 @@ std::vector PathCurve::getPoints() { return _points; } -Bezier3Curve::Bezier3Curve(const CameraState& start, const CameraState& end) { +Bezier3Curve::Bezier3Curve(const Waypoint& start, const Waypoint& end) { // default rotation interpolation _rotationInterpolator = RotationInterpolator{ start, end, this, PiecewiseSlerp }; - glm::dvec3 startNodePos = sceneGraphNode(start.referenceNode)->worldPosition(); - glm::dvec3 endNodePos = sceneGraphNode(end.referenceNode)->worldPosition(); - double startNodeRadius = sceneGraphNode(start.referenceNode)->boundingSphere(); - double endNodeRadius = sceneGraphNode(end.referenceNode)->boundingSphere(); + glm::dvec3 startNodePos = sceneGraphNode(start.node)->worldPosition(); + glm::dvec3 endNodePos = sceneGraphNode(end.node)->worldPosition(); + double startNodeRadius = sceneGraphNode(start.node)->boundingSphere(); + double endNodeRadius = sceneGraphNode(end.node)->boundingSphere(); - glm::dvec3 startNodeToStartPos = start.position - startNodePos; - glm::dvec3 endNodeToEndPos = end.position - endNodePos; + glm::dvec3 startNodeToStartPos = start.position() - startNodePos; + glm::dvec3 endNodeToEndPos = end.position() - endNodePos; double startTangentLength = 2.0 * startNodeRadius; double endTangentLength = 2.0 * endNodeRadius; @@ -96,13 +96,13 @@ Bezier3Curve::Bezier3Curve(const CameraState& start, const CameraState& end) { glm::dvec3 endTangentDirection = normalize(endNodeToEndPos); // Start by going outwards - _points.push_back(start.position); - _points.push_back(start.position + startTangentLength * startTangentDirection); + _points.push_back(start.position()); + _points.push_back(start.position() + startTangentLength * startTangentDirection); - if (start.referenceNode != end.referenceNode) { + if (start.node != end.node) { glm::dvec3 startNodeToEndNode = endNodePos - startNodePos; - glm::dvec3 startToEndDirection = normalize(end.position - start.position); + glm::dvec3 startToEndDirection = normalize(end.position() - start.position()); // Assuming we move straigh out to point to a distance proportional to radius, angle is enough to check collision risk double cosStartAngle = glm::dot(startTangentDirection, startToEndDirection); @@ -152,8 +152,8 @@ Bezier3Curve::Bezier3Curve(const CameraState& start, const CameraState& end) { } } - _points.push_back(end.position + endTangentLength * endTangentDirection); - _points.push_back(end.position); + _points.push_back(end.position() + endTangentLength * endTangentDirection); + _points.push_back(end.position()); _nrSegments = (unsigned int)std::floor((_points.size() - 1) / 3.0); @@ -209,10 +209,10 @@ void Bezier3Curve::initParameterIntervals() { _parameterIntervals.swap(newIntervals); } -LinearCurve::LinearCurve(const CameraState& start, const CameraState& end) { - _points.push_back(start.position); - _points.push_back(end.position); - _length = glm::distance(end.position, start.position); +LinearCurve::LinearCurve(const Waypoint& start, const Waypoint& end) { + _points.push_back(start.position()); + _points.push_back(end.position()); + _length = glm::distance(end.position(), start.position()); _rotationInterpolator = RotationInterpolator{ start, end, this, Slerp }; } @@ -222,10 +222,10 @@ glm::dvec3 LinearCurve::positionAt(double u) { } // TODO: Iprove handling of pauses -PauseCurve::PauseCurve(const CameraState& state) { - _points.push_back(state.position); +PauseCurve::PauseCurve(const Waypoint& waypoint) { + _points.push_back(waypoint.position()); _length = 1.0; // OBS! Length of a pause curve makes no sense, but it also doesn't matter - _rotationInterpolator = RotationInterpolator{ state, state, this, Fixed }; + _rotationInterpolator = RotationInterpolator{ waypoint, waypoint, this, Fixed }; } glm::dvec3 PauseCurve::positionAt(double u) { diff --git a/modules/autonavigation/pathcurves.h b/modules/autonavigation/pathcurves.h index e5239ff511..9b998453c5 100644 --- a/modules/autonavigation/pathcurves.h +++ b/modules/autonavigation/pathcurves.h @@ -25,7 +25,7 @@ #ifndef __OPENSPACE_MODULE_AUTONAVIGATION___PATHCURVE___H__ #define __OPENSPACE_MODULE_AUTONAVIGATION___PATHCURVE___H__ -#include +#include #include #include #include @@ -60,7 +60,7 @@ protected: class Bezier3Curve : public PathCurve { public: - Bezier3Curve(const CameraState& start, const CameraState& end); + Bezier3Curve(const Waypoint& start, const Waypoint& end); glm::dvec3 positionAt(double u); private: @@ -72,7 +72,7 @@ private: class LinearCurve : public PathCurve { public: - LinearCurve(const CameraState& start, const CameraState& end); + LinearCurve(const Waypoint& start, const Waypoint& end); glm::dvec3 positionAt(double u); }; @@ -80,7 +80,7 @@ public: // TODO: handle better in the future. class PauseCurve : public PathCurve { public: - PauseCurve(const CameraState& state); + PauseCurve(const Waypoint& state); glm::dvec3 positionAt(double u); }; diff --git a/modules/autonavigation/pathsegment.cpp b/modules/autonavigation/pathsegment.cpp index 9202b52504..b4b9557f6d 100644 --- a/modules/autonavigation/pathsegment.cpp +++ b/modules/autonavigation/pathsegment.cpp @@ -41,7 +41,7 @@ namespace { namespace openspace::autonavigation { PathSegment::PathSegment( - CameraState start, CameraState end, double startTime, CurveType type) + Waypoint start, Waypoint end, double startTime, CurveType type) : _start(start), _end(end), _startTime(startTime), _curveType(type) { initCurve(); @@ -54,7 +54,7 @@ PathSegment::PathSegment( _speedFunction = SpeedFunction(_duration); } -void PathSegment::setStart(CameraState cs) { +void PathSegment::setStart(Waypoint cs) { _start = std::move(cs); initCurve(); // TODO later: maybe recompute duration as well... @@ -65,9 +65,9 @@ void PathSegment::setDuration(double d) { _speedFunction = SpeedFunction(_duration); } -const CameraState PathSegment::start() const { return _start; } +const Waypoint PathSegment::start() const { return _start; } -const CameraState PathSegment::end() const { return _end; } +const Waypoint PathSegment::end() const { return _end; } const double PathSegment::duration() const { return _duration; } @@ -94,14 +94,17 @@ double PathSegment::speedAtTime(double time) { return (pathLength() * _speedFunction.value(t)) / _speedFunction.integratedSum; } -CameraState PathSegment::interpolate(double u) const { - CameraState cs; +CameraPose PathSegment::interpolate(double u) const { + CameraPose cs; cs.position = _curve->positionAt(u); cs.rotation = _curve->rotationAt(u); - cs.referenceNode = (u > 0.5) ? _end.referenceNode : _start.referenceNode; return cs; } +std::string PathSegment::getCurrentAnchor(double u) const { + return (u > 0.5) ? _end.node : _start.node; +} + // Initialise the curve, based on the start, end state and curve type void PathSegment::initCurve() { // in case there already is a curve object, reset the pointer. diff --git a/modules/autonavigation/pathsegment.h b/modules/autonavigation/pathsegment.h index dcfd1fbe38..5a1a23f3ab 100644 --- a/modules/autonavigation/pathsegment.h +++ b/modules/autonavigation/pathsegment.h @@ -25,7 +25,7 @@ #ifndef __OPENSPACE_MODULE___PATHSEGMENT___H__ #define __OPENSPACE_MODULE___PATHSEGMENT___H__ -#include +#include #include #include #include @@ -34,16 +34,16 @@ namespace openspace::autonavigation { class PathSegment { public: - PathSegment(CameraState start, CameraState end, double startTime, CurveType type); + PathSegment(Waypoint start, Waypoint end, double startTime, CurveType type); ~PathSegment() = default; // Mutators - void setStart(CameraState cs); + void setStart(Waypoint cs); void setDuration(double d); // Accessors - const CameraState start() const; - const CameraState end() const; + const Waypoint start() const; + const Waypoint end() const; const double duration() const; const double startTime() const; const double endTime() const; @@ -53,8 +53,8 @@ public: double speedAtTime(double time); - // Interpolate along the curve to get the current camera state - CameraState interpolate(double u) const; + CameraPose interpolate(double u) const; + std::string getCurrentAnchor(double u) const; private: void initCurve(); @@ -70,8 +70,8 @@ private: double integratedSum; }; - CameraState _start; - CameraState _end; + Waypoint _start; + Waypoint _end; double _startTime; double _duration; CurveType _curveType; diff --git a/modules/autonavigation/rotationinterpolator.cpp b/modules/autonavigation/rotationinterpolator.cpp index 3c80c7a0b9..1d41a48ef4 100644 --- a/modules/autonavigation/rotationinterpolator.cpp +++ b/modules/autonavigation/rotationinterpolator.cpp @@ -36,7 +36,7 @@ namespace { namespace openspace::autonavigation { RotationInterpolator::RotationInterpolator( - const CameraState& start, const CameraState& end, PathCurve* curve, RotationMethod method) + const Waypoint& start, const Waypoint& end, PathCurve* curve, RotationMethod method) : _start(start), _end(end), _method(method) { _curve = curve; @@ -52,14 +52,14 @@ glm::dquat RotationInterpolator::rotationAt(double u) { return piecewiseSlerp(u); break; case Fixed: - return _start.rotation; + return _start.rotation(); break; case LookAt: return lookAtInterpolator(u); break; default: LERROR("Non-implemented orientation interpolation method!"); - return _start.rotation; + return _start.rotation(); break; } } @@ -67,7 +67,7 @@ glm::dquat RotationInterpolator::rotationAt(double u) { glm::dquat RotationInterpolator::easedSlerp(double u) { double uScaled = helpers::shiftAndScale(u, 0.1, 0.9); uScaled = ghoul::cubicEaseInOut(uScaled); - return glm::slerp(_start.rotation, _end.rotation, uScaled); + return glm::slerp(_start.rotation(), _end.rotation(), uScaled); } // Look at start node until tStart, then turn to look at end node from tEnd @@ -79,11 +79,11 @@ glm::dquat RotationInterpolator::lookAtInterpolator(double u) { uNew = ghoul::cubicEaseInOut(uNew); - glm::dvec3 startNodePos = sceneGraphNode(_start.referenceNode)->worldPosition(); - glm::dvec3 endNodePos = sceneGraphNode(_end.referenceNode)->worldPosition(); + glm::dvec3 startNodePos = sceneGraphNode(_start.node)->worldPosition(); + glm::dvec3 endNodePos = sceneGraphNode(_end.node)->worldPosition(); glm::dvec3 lookAtPos = interpolation::linear(uNew, startNodePos, endNodePos); - glm::dvec3 startUpVec = _start.rotation * glm::dvec3(0.0, 1.0, 0.0); + glm::dvec3 startUpVec = _start.rotation() * glm::dvec3(0.0, 1.0, 0.0); return helpers::getLookAtQuaternion(_curve->positionAt(u), lookAtPos, startUpVec); } @@ -96,11 +96,11 @@ glm::dquat RotationInterpolator::piecewiseSlerp(double u) { const double u1 = 0.3; const double u2 = 0.8; // TODO: these should probably be based on distance - glm::dvec3 startNodePos = sceneGraphNode(_start.referenceNode)->worldPosition(); - glm::dvec3 endNodePos = sceneGraphNode(_end.referenceNode)->worldPosition(); + glm::dvec3 startNodePos = sceneGraphNode(_start.node)->worldPosition(); + glm::dvec3 endNodePos = sceneGraphNode(_end.node)->worldPosition(); - glm::dvec3 startUpVec = _start.rotation * glm::dvec3(0.0, 1.0, 0.0); - glm::dvec3 endUpVec = _end.rotation * glm::dvec3(0.0, 1.0, 0.0); + glm::dvec3 startUpVec = _start.rotation() * glm::dvec3(0.0, 1.0, 0.0); + glm::dvec3 endUpVec = _end.rotation() * glm::dvec3(0.0, 1.0, 0.0); glm::dquat lookAtStartQ = helpers::getLookAtQuaternion(_curve->positionAt(u1), startNodePos, startUpVec); @@ -109,10 +109,10 @@ glm::dquat RotationInterpolator::piecewiseSlerp(double u) { helpers::getLookAtQuaternion(_curve->positionAt(u2), endNodePos, endUpVec); std::vector> keyframes{ - {_start.rotation, 0.0}, + {_start.rotation(), 0.0}, {lookAtStartQ, u1}, {lookAtEndQ, u2}, - {_end.rotation, 1.0} + {_end.rotation(), 1.0} }; // Find the current segment and compute interpolation diff --git a/modules/autonavigation/rotationinterpolator.h b/modules/autonavigation/rotationinterpolator.h index a0b4968e39..cb2b661303 100644 --- a/modules/autonavigation/rotationinterpolator.h +++ b/modules/autonavigation/rotationinterpolator.h @@ -25,7 +25,7 @@ #ifndef __OPENSPACE_MODULE_AUTONAVIGATION___ROTATIONINTERPOLATOR___H__ #define __OPENSPACE_MODULE_AUTONAVIGATION___ROTATIONINTERPOLATOR___H__ -#include +#include namespace openspace::autonavigation { @@ -41,14 +41,14 @@ class PathCurve; class RotationInterpolator { public: RotationInterpolator() = default; - RotationInterpolator(const CameraState& start, const CameraState& end, + RotationInterpolator(const Waypoint& start, const Waypoint& end, PathCurve* curve, RotationMethod method); glm::dquat rotationAt(double u); private: - CameraState _start; - CameraState _end; + Waypoint _start; + Waypoint _end; PathCurve* _curve = nullptr; RotationMethod _method; diff --git a/modules/autonavigation/camerastate.cpp b/modules/autonavigation/waypoint.cpp similarity index 84% rename from modules/autonavigation/camerastate.cpp rename to modules/autonavigation/waypoint.cpp index 4b45633f71..6f1bce544b 100644 --- a/modules/autonavigation/camerastate.cpp +++ b/modules/autonavigation/waypoint.cpp @@ -22,23 +22,26 @@ * OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. * ****************************************************************************************/ -#include +#include #include #include #include namespace { - constexpr const char* _loggerCat = "CameraState"; + constexpr const char* _loggerCat = "Waypoint"; } // namespace namespace openspace::autonavigation { -CameraState::CameraState(const glm::dvec3& pos, const glm::dquat& rot, const std::string& ref) - : position(pos), rotation(rot), referenceNode(ref) -{} +Waypoint::Waypoint(const glm::dvec3& pos, const glm::dquat& rot, const std::string& ref) + : node(ref) +{ + pose.position = pos; + pose.rotation = rot; +} -CameraState::CameraState(const NavigationState& ns) { +Waypoint::Waypoint(const NavigationState& ns) { // OBS! The following code is exactly the same as used in // NavigationHandler::applyNavigationState. Should probably be made into a function. const SceneGraphNode* referenceFrame = sceneGraphNode(ns.referenceFrame); @@ -52,7 +55,7 @@ CameraState::CameraState(const NavigationState& ns) { const glm::dvec3 anchorWorldPosition = anchorNode->worldPosition(); const glm::dmat3 referenceFrameTransform = referenceFrame->worldRotationMatrix(); - position = anchorWorldPosition + + pose.position = anchorWorldPosition + glm::dvec3(referenceFrameTransform * glm::dvec4(ns.position, 1.0)); glm::dvec3 up = ns.up.has_value() ? @@ -61,7 +64,7 @@ CameraState::CameraState(const NavigationState& ns) { // Construct vectors of a "neutral" view, i.e. when the aim is centered in view. glm::dvec3 neutralView = - glm::normalize(anchorWorldPosition - position); + glm::normalize(anchorWorldPosition - pose.position); glm::dquat neutralCameraRotation = glm::inverse(glm::quat_cast(glm::lookAt( glm::dvec3(0.0), @@ -72,9 +75,13 @@ CameraState::CameraState(const NavigationState& ns) { glm::dquat pitchRotation = glm::angleAxis(ns.pitch, glm::dvec3(1.f, 0.f, 0.f)); glm::dquat yawRotation = glm::angleAxis(ns.yaw, glm::dvec3(0.f, -1.f, 0.f)); - rotation = neutralCameraRotation * yawRotation * pitchRotation; + pose.rotation = neutralCameraRotation * yawRotation * pitchRotation; - referenceNode = ns.referenceFrame; + node = ns.referenceFrame; } +glm::dvec3 Waypoint::position() const { return pose.position; } + +glm::dquat Waypoint::rotation() const { return pose.rotation; } + } // namespace openspace::autonavigation diff --git a/modules/autonavigation/waypoint.h b/modules/autonavigation/waypoint.h new file mode 100644 index 0000000000..53bf38bbf2 --- /dev/null +++ b/modules/autonavigation/waypoint.h @@ -0,0 +1,57 @@ +/***************************************************************************************** + * * + * OpenSpace * + * * + * Copyright (c) 2014-2019 * + * * + * Permission is hereby granted, free of charge, to any person obtaining a copy of this * + * software and associated documentation files (the "Software"), to deal in the Software * + * without restriction, including without limitation the rights to use, copy, modify, * + * merge, publish, distribute, sublicense, and/or sell copies of the Software, and to * + * permit persons to whom the Software is furnished to do so, subject to the following * + * conditions: * + * * + * The above copyright notice and this permission notice shall be included in all copies * + * or substantial portions of the Software. * + * * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, * + * INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A * + * PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT * + * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF * + * CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE * + * OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. * + ****************************************************************************************/ + +#ifndef __OPENSPACE_MODULE___WAYPOINT___H__ +#define __OPENSPACE_MODULE___WAYPOINT___H__ + +#include +#include +#include + +namespace openspace::autonavigation { + +struct CameraPose { + glm::dvec3 position; + glm::dquat rotation; +}; + +struct Waypoint { + using NavigationState = interaction::NavigationHandler::NavigationState; + + // TODO: create waypoints from a dictionary + + Waypoint() = default; + Waypoint(const glm::dvec3& pos, const glm::dquat& rot, const std::string& ref); + Waypoint(const NavigationState& ns); + + glm::dvec3 position() const; + glm::dquat rotation() const; + + CameraPose pose; + std::string node; +}; + +} // namespace openspace::autonavigation + +#endif // __OPENSPACE_MODULE___WAYPOINT___H__ From 1524a207f7880662328de0b709eb8d62494a2f8f Mon Sep 17 00:00:00 2001 From: Emma Broman Date: Tue, 10 Mar 2020 15:08:51 -0400 Subject: [PATCH 080/912] store the valid bounding sphere once computed --- .../autonavigation/autonavigationhandler.cpp | 66 +++++-------------- .../autonavigation/autonavigationhandler.h | 7 +- modules/autonavigation/pathcurves.cpp | 13 ++-- modules/autonavigation/pathsegment.cpp | 4 +- .../autonavigation/rotationinterpolator.cpp | 9 ++- modules/autonavigation/waypoint.cpp | 64 ++++++++++++++++-- modules/autonavigation/waypoint.h | 20 +++++- 7 files changed, 109 insertions(+), 74 deletions(-) diff --git a/modules/autonavigation/autonavigationhandler.cpp b/modules/autonavigation/autonavigationhandler.cpp index e38e825563..6ce9978661 100644 --- a/modules/autonavigation/autonavigationhandler.cpp +++ b/modules/autonavigation/autonavigationhandler.cpp @@ -91,17 +91,6 @@ bool AutoNavigationHandler::hasFinished() const { return _currentTime > pathDuration(); } -Waypoint AutoNavigationHandler::wayPointFromCamera() { - glm::dvec3 pos = camera()->positionVec3(); - glm::dquat rot = camera()->rotationQuaternion(); - std::string node = global::navigationHandler.anchorNode()->identifier(); - return Waypoint{pos, rot, node}; -} - -Waypoint AutoNavigationHandler::lastWayPoint() { - return _pathSegments.empty() ? wayPointFromCamera() : _pathSegments.back().end(); -} - void AutoNavigationHandler::updateCamera(double deltaTime) { ghoul_assert(camera() != nullptr, "Camera must not be nullptr"); @@ -170,7 +159,7 @@ void AutoNavigationHandler::createPath(PathSpecification& spec) { // Check if we have a specified start navigation state. If so, update first segment if (spec.hasStartState() && _pathSegments.size() > 0) { - Waypoint startState{ spec.startState() }; + Waypoint startState{ spec.startState() , _minAllowedBoundingSphere}; _pathSegments[0].setStart(startState); } @@ -281,6 +270,17 @@ std::vector AutoNavigationHandler::getControlPoints() { return points; } +Waypoint AutoNavigationHandler::wayPointFromCamera() { + glm::dvec3 pos = camera()->positionVec3(); + glm::dquat rot = camera()->rotationQuaternion(); + std::string node = global::navigationHandler.anchorNode()->identifier(); + return Waypoint{ pos, rot, node, _minAllowedBoundingSphere }; +} + +Waypoint AutoNavigationHandler::lastWayPoint() { + return _pathSegments.empty() ? wayPointFromCamera() : _pathSegments.back().end(); +} + bool AutoNavigationHandler::handleInstruction(const Instruction& ins, int index) { bool success = true; switch (ins.type) @@ -341,10 +341,10 @@ bool AutoNavigationHandler::handleTargetNodeInstruction(const Instruction& ins) // TODO: Instead of this case, allow the curve to set its final position glm::dvec3 nodePos = targetNode->worldPosition(); - glm::dvec3 nodeToPrev= lastWayPoint().position() - nodePos; + glm::dvec3 nodeToPrev = lastWayPoint().position() - nodePos; // TODO: compute position in a more clever way - const double radius = findValidBoundingSphere(targetNode); + const double radius = WaypointNodeDetails::findValidBoundingSphere(targetNode, _minAllowedBoundingSphere); const double defaultHeight = 2 * radius; bool hasHeight = props->height.has_value(); @@ -362,7 +362,7 @@ bool AutoNavigationHandler::handleTargetNodeInstruction(const Instruction& ins) glm::dquat targetRot = glm::normalize(glm::inverse(glm::quat_cast(lookAtMat))); - Waypoint endState{ targetPos, targetRot, identifier }; + Waypoint endState{ targetPos, targetRot, identifier, _minAllowedBoundingSphere }; addSegment(endState, ins.props->duration); return true; @@ -378,7 +378,7 @@ bool AutoNavigationHandler::handleNavigationStateInstruction(const Instruction& return false; } - Waypoint endState{ props->navState }; + Waypoint endState{ props->navState , _minAllowedBoundingSphere }; addSegment(endState, ins.props->duration); return true; @@ -414,8 +414,7 @@ void AutoNavigationHandler::addPause(std::optional duration) { _pathSegments.push_back(newSegment); } -void AutoNavigationHandler::addSegment(Waypoint& waypoint, std::optional duration) -{ +void AutoNavigationHandler::addSegment(Waypoint& waypoint, std::optional duration) { double startTime = pathDuration(); // TODO: Improve how curve types are handled @@ -430,35 +429,4 @@ void AutoNavigationHandler::addSegment(Waypoint& waypoint, std::optional _pathSegments.push_back(newSegment); } -double AutoNavigationHandler::findValidBoundingSphere(const SceneGraphNode* node) { - double bs = static_cast(node->boundingSphere()); - - if (bs < _minAllowedBoundingSphere) { - - // If the bs of the target is too small, try to find a good value in a child node. - // Only check the closest children, to avoid deep traversal in the scene graph. Also, - // the possibility to find a bounding sphere represents the visual size of the - // target well is higher for these nodes. - for (SceneGraphNode* child : node->children()) { - bs = static_cast(child->boundingSphere()); - if (bs > _minAllowedBoundingSphere) { - LWARNING(fmt::format( - "The scene graph node '{}' has no, or a very small, bounding sphere. Using bounding sphere of child node '{}' in computations.", - node->identifier(), - child->identifier() - )); - - return bs; - } - } - - LWARNING(fmt::format("The scene graph node '{}' has no, or a very small," - "bounding sphere. This might lead to unexpected results.", node->identifier())); - - bs = _minAllowedBoundingSphere; - } - - return bs; -} - } // namespace openspace::autonavigation diff --git a/modules/autonavigation/autonavigationhandler.h b/modules/autonavigation/autonavigationhandler.h index 15a0ab1b62..4944ae1fa2 100644 --- a/modules/autonavigation/autonavigationhandler.h +++ b/modules/autonavigation/autonavigationhandler.h @@ -52,8 +52,6 @@ public: Camera* camera() const; double pathDuration() const; bool hasFinished() const; - Waypoint wayPointFromCamera(); - Waypoint lastWayPoint(); void updateCamera(double deltaTime); void createPath(PathSpecification& spec); @@ -68,6 +66,9 @@ public: std::vector getControlPoints(); //debug private: + Waypoint wayPointFromCamera(); + Waypoint lastWayPoint(); + bool handleInstruction(const Instruction& ins, int index); bool handleTargetNodeInstruction(const Instruction& ins); @@ -77,8 +78,6 @@ private: void addPause(std::optional duration); void addSegment(Waypoint& state, std::optional duration); - double findValidBoundingSphere(const SceneGraphNode* node); - // this list essentially represents the camera path std::vector _pathSegments; diff --git a/modules/autonavigation/pathcurves.cpp b/modules/autonavigation/pathcurves.cpp index 88e5b4171e..b39bf1efd0 100644 --- a/modules/autonavigation/pathcurves.cpp +++ b/modules/autonavigation/pathcurves.cpp @@ -82,10 +82,10 @@ Bezier3Curve::Bezier3Curve(const Waypoint& start, const Waypoint& end) { // default rotation interpolation _rotationInterpolator = RotationInterpolator{ start, end, this, PiecewiseSlerp }; - glm::dvec3 startNodePos = sceneGraphNode(start.node)->worldPosition(); - glm::dvec3 endNodePos = sceneGraphNode(end.node)->worldPosition(); - double startNodeRadius = sceneGraphNode(start.node)->boundingSphere(); - double endNodeRadius = sceneGraphNode(end.node)->boundingSphere(); + glm::dvec3 startNodePos = start.node()->worldPosition(); + glm::dvec3 endNodePos = end.node()->worldPosition(); + double startNodeRadius = start.nodeDetails.validBoundingSphere; + double endNodeRadius = end.nodeDetails.validBoundingSphere; glm::dvec3 startNodeToStartPos = start.position() - startNodePos; glm::dvec3 endNodeToEndPos = end.position() - endNodePos; @@ -99,7 +99,10 @@ Bezier3Curve::Bezier3Curve(const Waypoint& start, const Waypoint& end) { _points.push_back(start.position()); _points.push_back(start.position() + startTangentLength * startTangentDirection); - if (start.node != end.node) { + const std::string& startNode = start.nodeDetails.identifier; + const std::string& endNode = end.nodeDetails.identifier; + + if (startNode != endNode) { glm::dvec3 startNodeToEndNode = endNodePos - startNodePos; glm::dvec3 startToEndDirection = normalize(end.position() - start.position()); diff --git a/modules/autonavigation/pathsegment.cpp b/modules/autonavigation/pathsegment.cpp index b4b9557f6d..b468e0f333 100644 --- a/modules/autonavigation/pathsegment.cpp +++ b/modules/autonavigation/pathsegment.cpp @@ -28,8 +28,6 @@ #include #include #include -#include -#include #include #include #include @@ -102,7 +100,7 @@ CameraPose PathSegment::interpolate(double u) const { } std::string PathSegment::getCurrentAnchor(double u) const { - return (u > 0.5) ? _end.node : _start.node; + return (u > 0.5) ? _end.nodeDetails.identifier : _start.nodeDetails.identifier; } // Initialise the curve, based on the start, end state and curve type diff --git a/modules/autonavigation/rotationinterpolator.cpp b/modules/autonavigation/rotationinterpolator.cpp index 1d41a48ef4..2417caa044 100644 --- a/modules/autonavigation/rotationinterpolator.cpp +++ b/modules/autonavigation/rotationinterpolator.cpp @@ -78,9 +78,8 @@ glm::dquat RotationInterpolator::lookAtInterpolator(double u) { double uNew = helpers::shiftAndScale(u, tStart, tEnd); uNew = ghoul::cubicEaseInOut(uNew); - - glm::dvec3 startNodePos = sceneGraphNode(_start.node)->worldPosition(); - glm::dvec3 endNodePos = sceneGraphNode(_end.node)->worldPosition(); + glm::dvec3 startNodePos = _start.node()->worldPosition(); + glm::dvec3 endNodePos = _end.node()->worldPosition(); glm::dvec3 lookAtPos = interpolation::linear(uNew, startNodePos, endNodePos); glm::dvec3 startUpVec = _start.rotation() * glm::dvec3(0.0, 1.0, 0.0); @@ -96,8 +95,8 @@ glm::dquat RotationInterpolator::piecewiseSlerp(double u) { const double u1 = 0.3; const double u2 = 0.8; // TODO: these should probably be based on distance - glm::dvec3 startNodePos = sceneGraphNode(_start.node)->worldPosition(); - glm::dvec3 endNodePos = sceneGraphNode(_end.node)->worldPosition(); + glm::dvec3 startNodePos = _start.node()->worldPosition(); + glm::dvec3 endNodePos = _end.node()->worldPosition(); glm::dvec3 startUpVec = _start.rotation() * glm::dvec3(0.0, 1.0, 0.0); glm::dvec3 endUpVec = _end.rotation() * glm::dvec3(0.0, 1.0, 0.0); diff --git a/modules/autonavigation/waypoint.cpp b/modules/autonavigation/waypoint.cpp index 6f1bce544b..b9b5a336c3 100644 --- a/modules/autonavigation/waypoint.cpp +++ b/modules/autonavigation/waypoint.cpp @@ -34,21 +34,71 @@ namespace { namespace openspace::autonavigation { -Waypoint::Waypoint(const glm::dvec3& pos, const glm::dquat& rot, const std::string& ref) - : node(ref) +WaypointNodeDetails::WaypointNodeDetails(const std::string nodeIdentifier, + const double minBoundingSphere) +{ + const SceneGraphNode* node = sceneGraphNode(nodeIdentifier); + if (!node) { + LERROR(fmt::format("Could not find node '{}'.", nodeIdentifier)); + return; + } + + identifier = nodeIdentifier; + validBoundingSphere = findValidBoundingSphere(node, minBoundingSphere); + + // TEST: + LINFO(fmt::format("Created waypoint node '{}' with boudning sphere: {}", nodeIdentifier, validBoundingSphere)); +} + +double WaypointNodeDetails::findValidBoundingSphere(const SceneGraphNode* node, + const double minBoundingSphere) +{ + double bs = static_cast(node->boundingSphere()); + + if (bs < minBoundingSphere) { + + // If the bs of the target is too small, try to find a good value in a child node. + // Only check the closest children, to avoid deep traversal in the scene graph. Also, + // the possibility to find a bounding sphere represents the visual size of the + // target well is higher for these nodes. + for (SceneGraphNode* child : node->children()) { + bs = static_cast(child->boundingSphere()); + if (bs > minBoundingSphere) { + LWARNING(fmt::format( + "The scene graph node '{}' has no, or a very small, bounding sphere. Using bounding sphere of child node '{}' in computations.", + node->identifier(), + child->identifier() + )); + + return bs; + } + } + + LWARNING(fmt::format("The scene graph node '{}' has no, or a very small," + "bounding sphere. This might lead to unexpected results.", node->identifier())); + + bs = minBoundingSphere; + } + + return bs; +} + +Waypoint::Waypoint(const glm::dvec3& pos, const glm::dquat& rot, const std::string& ref, + const double minBoundingSphere) + : nodeDetails(ref, minBoundingSphere) { pose.position = pos; pose.rotation = rot; } -Waypoint::Waypoint(const NavigationState& ns) { +Waypoint::Waypoint(const NavigationState& ns, const double minBoundingSphere) { // OBS! The following code is exactly the same as used in // NavigationHandler::applyNavigationState. Should probably be made into a function. const SceneGraphNode* referenceFrame = sceneGraphNode(ns.referenceFrame); const SceneGraphNode* anchorNode = sceneGraphNode(ns.anchor); // The anchor is also the target if (!anchorNode) { - LERROR(fmt::format("Could not find node '{}' to target. Returning empty state.", ns.anchor)); + LERROR(fmt::format("Could not find node '{}' to target.", ns.anchor)); return; } @@ -77,11 +127,15 @@ Waypoint::Waypoint(const NavigationState& ns) { pose.rotation = neutralCameraRotation * yawRotation * pitchRotation; - node = ns.referenceFrame; + nodeDetails = WaypointNodeDetails{ ns.referenceFrame, minBoundingSphere }; } glm::dvec3 Waypoint::position() const { return pose.position; } glm::dquat Waypoint::rotation() const { return pose.rotation; } +SceneGraphNode* Waypoint::node() const { + return sceneGraphNode(nodeDetails.identifier); +} + } // namespace openspace::autonavigation diff --git a/modules/autonavigation/waypoint.h b/modules/autonavigation/waypoint.h index 53bf38bbf2..d2ed6ee77c 100644 --- a/modules/autonavigation/waypoint.h +++ b/modules/autonavigation/waypoint.h @@ -36,20 +36,34 @@ struct CameraPose { glm::dquat rotation; }; +// The waypoint node is the anchor or target node. +struct WaypointNodeDetails { + WaypointNodeDetails() = default; + WaypointNodeDetails(const std::string nodeIdentifier, const double minBoundingSphere); + + static double findValidBoundingSphere(const SceneGraphNode* node, + const double minBoundingSphere); + + std::string identifier; + double validBoundingSphere; // to be able to handle nodes with faulty bounding spheres +}; + struct Waypoint { using NavigationState = interaction::NavigationHandler::NavigationState; // TODO: create waypoints from a dictionary Waypoint() = default; - Waypoint(const glm::dvec3& pos, const glm::dquat& rot, const std::string& ref); - Waypoint(const NavigationState& ns); + Waypoint(const glm::dvec3& pos, const glm::dquat& rot, const std::string& ref, + const double minBoundingSphere); + Waypoint(const NavigationState& ns, const double minBoundingSphere); glm::dvec3 position() const; glm::dquat rotation() const; + SceneGraphNode* node() const; CameraPose pose; - std::string node; + WaypointNodeDetails nodeDetails; }; } // namespace openspace::autonavigation From 103679c6f1c20959a695fca2bf9d40114afddfc8 Mon Sep 17 00:00:00 2001 From: Emma Broman Date: Tue, 10 Mar 2020 15:09:09 -0400 Subject: [PATCH 081/912] remove test log --- modules/autonavigation/waypoint.cpp | 3 --- 1 file changed, 3 deletions(-) diff --git a/modules/autonavigation/waypoint.cpp b/modules/autonavigation/waypoint.cpp index b9b5a336c3..53abc173cd 100644 --- a/modules/autonavigation/waypoint.cpp +++ b/modules/autonavigation/waypoint.cpp @@ -45,9 +45,6 @@ WaypointNodeDetails::WaypointNodeDetails(const std::string nodeIdentifier, identifier = nodeIdentifier; validBoundingSphere = findValidBoundingSphere(node, minBoundingSphere); - - // TEST: - LINFO(fmt::format("Created waypoint node '{}' with boudning sphere: {}", nodeIdentifier, validBoundingSphere)); } double WaypointNodeDetails::findValidBoundingSphere(const SceneGraphNode* node, From 9ed740f57d6c5eef6b38d67fa012d4db49e68963 Mon Sep 17 00:00:00 2001 From: Emma Broman Date: Tue, 10 Mar 2020 15:53:03 -0400 Subject: [PATCH 082/912] Adapt to code standard --- modules/autonavigation/waypoint.cpp | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/modules/autonavigation/waypoint.cpp b/modules/autonavigation/waypoint.cpp index 53abc173cd..2fd8fc8173 100644 --- a/modules/autonavigation/waypoint.cpp +++ b/modules/autonavigation/waypoint.cpp @@ -35,7 +35,7 @@ namespace { namespace openspace::autonavigation { WaypointNodeDetails::WaypointNodeDetails(const std::string nodeIdentifier, - const double minBoundingSphere) + const double minBoundingSphere) { const SceneGraphNode* node = sceneGraphNode(nodeIdentifier); if (!node) { @@ -48,7 +48,7 @@ WaypointNodeDetails::WaypointNodeDetails(const std::string nodeIdentifier, } double WaypointNodeDetails::findValidBoundingSphere(const SceneGraphNode* node, - const double minBoundingSphere) + const double minBoundingSphere) { double bs = static_cast(node->boundingSphere()); @@ -81,7 +81,7 @@ double WaypointNodeDetails::findValidBoundingSphere(const SceneGraphNode* node, } Waypoint::Waypoint(const glm::dvec3& pos, const glm::dquat& rot, const std::string& ref, - const double minBoundingSphere) + const double minBoundingSphere) : nodeDetails(ref, minBoundingSphere) { pose.position = pos; From 3b0312e168db7ba4d5b5d6ab46cb143744ab2874 Mon Sep 17 00:00:00 2001 From: Emma Broman Date: Tue, 10 Mar 2020 16:12:53 -0400 Subject: [PATCH 083/912] reorder includes --- modules/autonavigation/pathcurves.h | 2 +- modules/autonavigation/pathsegment.h | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/modules/autonavigation/pathcurves.h b/modules/autonavigation/pathcurves.h index 9b998453c5..8bb7c88167 100644 --- a/modules/autonavigation/pathcurves.h +++ b/modules/autonavigation/pathcurves.h @@ -25,8 +25,8 @@ #ifndef __OPENSPACE_MODULE_AUTONAVIGATION___PATHCURVE___H__ #define __OPENSPACE_MODULE_AUTONAVIGATION___PATHCURVE___H__ -#include #include +#include #include #include diff --git a/modules/autonavigation/pathsegment.h b/modules/autonavigation/pathsegment.h index 5a1a23f3ab..7943888403 100644 --- a/modules/autonavigation/pathsegment.h +++ b/modules/autonavigation/pathsegment.h @@ -25,8 +25,8 @@ #ifndef __OPENSPACE_MODULE___PATHSEGMENT___H__ #define __OPENSPACE_MODULE___PATHSEGMENT___H__ -#include #include +#include #include #include From 76de04d1d7f9814c2ae4a3ef95afcc4f893b017f Mon Sep 17 00:00:00 2001 From: Emma Broman Date: Mon, 30 Mar 2020 14:05:44 +0200 Subject: [PATCH 084/912] Make PathSegment responsible for path traversal --- .../autonavigation/autonavigationhandler.cpp | 66 ++++++------------- .../autonavigation/autonavigationhandler.h | 5 +- modules/autonavigation/pathcurves.cpp | 2 +- modules/autonavigation/pathsegment.cpp | 50 +++++++++++--- modules/autonavigation/pathsegment.h | 19 +++--- 5 files changed, 73 insertions(+), 69 deletions(-) diff --git a/modules/autonavigation/autonavigationhandler.cpp b/modules/autonavigation/autonavigationhandler.cpp index 6ce9978661..6eafba9cec 100644 --- a/modules/autonavigation/autonavigationhandler.cpp +++ b/modules/autonavigation/autonavigationhandler.cpp @@ -80,15 +80,9 @@ Camera* AutoNavigationHandler::camera() const { return global::navigationHandler.camera(); } -double AutoNavigationHandler::pathDuration() const { - if (_pathSegments.empty()) - return 0.0; - - return _pathSegments.back().endTime(); -} - bool AutoNavigationHandler::hasFinished() const { - return _currentTime > pathDuration(); + int lastIndex = (int)_pathSegments.size() - 1; + return _currentSegmentIndex > lastIndex; } void AutoNavigationHandler::updateCamera(double deltaTime) { @@ -96,18 +90,10 @@ void AutoNavigationHandler::updateCamera(double deltaTime) { if (!_isPlaying || _pathSegments.empty()) return; - PathSegment currentSegment = _pathSegments[_currentSegmentIndex]; + std::unique_ptr ¤tSegment = _pathSegments[_currentSegmentIndex]; - // compute interpolated camera state - double prevDistance = _distanceAlongCurrentSegment; - double displacement = deltaTime * currentSegment.speedAtTime(_currentTime - currentSegment.startTime()); - _distanceAlongCurrentSegment += displacement; - - double relativeDisplacement = _distanceAlongCurrentSegment / currentSegment.pathLength(); - relativeDisplacement = std::max(0.0, std::min(relativeDisplacement, 1.0)); - - CameraPose newPose = currentSegment.interpolate(relativeDisplacement); - std::string newAnchor = currentSegment.getCurrentAnchor(relativeDisplacement); + CameraPose newPose = currentSegment->traversePath(deltaTime); + std::string newAnchor = currentSegment->getCurrentAnchor(); // Set anchor node in orbitalNavigator, to render visible nodes and add activate // navigation when we reach the end. @@ -119,27 +105,20 @@ void AutoNavigationHandler::updateCamera(double deltaTime) { camera()->setPositionVec3(newPose.position); camera()->setRotation(newPose.rotation); - // Have we walked past the current segment? - if (_currentTime > currentSegment.endTime()) { + if (currentSegment->hasReachedEnd()) { _currentSegmentIndex++; - _distanceAlongCurrentSegment = 0.0; - // Stepped past the last segment - if (_currentSegmentIndex > _pathSegments.size() - 1) { + if (hasFinished()) { LINFO("Reached end of path."); _isPlaying = false; return; } - currentSegment = _pathSegments[_currentSegmentIndex]; - if (_stopAtTargets) { pausePath(); return; } } - - _currentTime += deltaTime; } void AutoNavigationHandler::createPath(PathSpecification& spec) { @@ -160,7 +139,7 @@ void AutoNavigationHandler::createPath(PathSpecification& spec) { // Check if we have a specified start navigation state. If so, update first segment if (spec.hasStartState() && _pathSegments.size() > 0) { Waypoint startState{ spec.startState() , _minAllowedBoundingSphere}; - _pathSegments[0].setStart(startState); + _pathSegments[0]->setStart(startState); } if (success) { @@ -174,9 +153,7 @@ void AutoNavigationHandler::createPath(PathSpecification& spec) { void AutoNavigationHandler::clearPath() { LINFO("Clearing path..."); _pathSegments.clear(); - _currentTime = 0.0; _currentSegmentIndex = 0; - _distanceAlongCurrentSegment = 0.0; } void AutoNavigationHandler::startPath() { @@ -195,7 +172,6 @@ void AutoNavigationHandler::startPath() { } LINFO("Starting path..."); - _currentTime = 0.0; _isPlaying = true; } @@ -222,7 +198,7 @@ void AutoNavigationHandler::continuePath() { LINFO("Continuing path..."); // Recompute start camera state for the upcoming path segment, - _pathSegments[_currentSegmentIndex].setStart(wayPointFromCamera()); + _pathSegments[_currentSegmentIndex]->setStart(wayPointFromCamera()); _isPlaying = true; } @@ -242,9 +218,9 @@ std::vector AutoNavigationHandler::getCurvePositions(int nPerSegment const double du = 1.0 / nPerSegment; - for (PathSegment &p : _pathSegments) { + for (std::unique_ptr &p : _pathSegments) { for (double u = 0.0; u < 1.0; u += du) { - auto position = p.interpolate(u).position; + glm::dvec3 position = p->interpolatedPose(u).position; positions.push_back(position); } } @@ -262,8 +238,8 @@ std::vector AutoNavigationHandler::getControlPoints() { return points; } - for (PathSegment &p : _pathSegments) { - std::vector curvePoints = p.getControlPoints(); + for (std::unique_ptr &p : _pathSegments) { + std::vector curvePoints = p->getControlPoints(); points.insert(points.end(), curvePoints.begin(), curvePoints.end()); } @@ -278,7 +254,7 @@ Waypoint AutoNavigationHandler::wayPointFromCamera() { } Waypoint AutoNavigationHandler::lastWayPoint() { - return _pathSegments.empty() ? wayPointFromCamera() : _pathSegments.back().end(); + return _pathSegments.empty() ? wayPointFromCamera() : _pathSegments.back()->end(); } bool AutoNavigationHandler::handleInstruction(const Instruction& ins, int index) { @@ -401,32 +377,30 @@ bool AutoNavigationHandler::handlePauseInstruction(const Instruction& ins) { } void AutoNavigationHandler::addPause(std::optional duration) { - double startTime = pathDuration(); Waypoint waypoint = lastWayPoint(); - PathSegment newSegment{ waypoint, waypoint, startTime, CurveType::Pause }; + PathSegment segment = PathSegment(waypoint, waypoint, CurveType::Pause); // TODO: implement more complex behavior later // TODO: handle duration better if (duration.has_value()) { - newSegment.setDuration(duration.value()); + segment.setDuration(duration.value()); } - _pathSegments.push_back(newSegment); + _pathSegments.push_back(std::unique_ptr(new PathSegment(segment))); } void AutoNavigationHandler::addSegment(Waypoint& waypoint, std::optional duration) { - double startTime = pathDuration(); // TODO: Improve how curve types are handled const int curveType = _defaultCurveOption; - PathSegment newSegment{ lastWayPoint(), waypoint, startTime, CurveType(curveType) }; + PathSegment segment = PathSegment(lastWayPoint(), waypoint, CurveType(curveType)); // TODO: handle duration better if (duration.has_value()) { - newSegment.setDuration(duration.value()); + segment.setDuration(duration.value()); } - _pathSegments.push_back(newSegment); + _pathSegments.push_back(std::unique_ptr(new PathSegment(segment))); } } // namespace openspace::autonavigation diff --git a/modules/autonavigation/autonavigationhandler.h b/modules/autonavigation/autonavigationhandler.h index 4944ae1fa2..5463e3d288 100644 --- a/modules/autonavigation/autonavigationhandler.h +++ b/modules/autonavigation/autonavigationhandler.h @@ -50,7 +50,6 @@ public: // Accessors Camera* camera() const; - double pathDuration() const; bool hasFinished() const; void updateCamera(double deltaTime); @@ -79,11 +78,9 @@ private: void addSegment(Waypoint& state, std::optional duration); // this list essentially represents the camera path - std::vector _pathSegments; + std::vector> _pathSegments; bool _isPlaying = false; - double _currentTime = 0.0; - double _distanceAlongCurrentSegment = 0.0; unsigned int _currentSegmentIndex = 0; bool _stopAtTargets; diff --git a/modules/autonavigation/pathcurves.cpp b/modules/autonavigation/pathcurves.cpp index b39bf1efd0..6f505075dd 100644 --- a/modules/autonavigation/pathcurves.cpp +++ b/modules/autonavigation/pathcurves.cpp @@ -80,7 +80,7 @@ std::vector PathCurve::getPoints() { Bezier3Curve::Bezier3Curve(const Waypoint& start, const Waypoint& end) { // default rotation interpolation - _rotationInterpolator = RotationInterpolator{ start, end, this, PiecewiseSlerp }; + _rotationInterpolator = RotationInterpolator{ start, end, this, LookAt }; glm::dvec3 startNodePos = start.node()->worldPosition(); glm::dvec3 endNodePos = end.node()->worldPosition(); diff --git a/modules/autonavigation/pathsegment.cpp b/modules/autonavigation/pathsegment.cpp index b468e0f333..85d8561b3f 100644 --- a/modules/autonavigation/pathsegment.cpp +++ b/modules/autonavigation/pathsegment.cpp @@ -39,8 +39,8 @@ namespace { namespace openspace::autonavigation { PathSegment::PathSegment( - Waypoint start, Waypoint end, double startTime, CurveType type) - : _start(start), _end(end), _startTime(startTime), _curveType(type) + Waypoint start, Waypoint end, CurveType type) + : _start(start), _end(end), _curveType(type) { initCurve(); @@ -69,10 +69,6 @@ const Waypoint PathSegment::end() const { return _end; } const double PathSegment::duration() const { return _duration; } -const double PathSegment::startTime() const { return _startTime; } - -const double PathSegment::endTime() const { return _startTime + _duration; } - const double PathSegment::pathLength() const { return _curve->length(); } // TODO: remove function for debugging @@ -80,6 +76,38 @@ const std::vector PathSegment::getControlPoints() const { return _curve->getPoints(); } +CameraPose PathSegment::traversePath(double dt) { + // In case there is an error in the speed VS distance VS duration relation, + // make sure that we actually reach the end point. + double displacement; + if (_currentTime > _duration && !hasReachedEnd()) { + // TODO: reach the target in a reasonable amount of time and with smooth motion + LWARNING("Did not reach the target in the given duration. Moving toward the target in constant speed. TODO: fix so that this does not happen"); // TODO: fix and then remove this + displacement = dt * speedAtTime(_duration - _duration * dt); + } + else { + displacement = dt * speedAtTime(_currentTime); + } + + _traveledDistance += displacement; + + double relativeDisplacement = _traveledDistance / pathLength(); + relativeDisplacement = std::max(0.0, std::min(relativeDisplacement, 1.0)); + + // TEST: + //LINFO("-----------------------------------"); + //LINFO(fmt::format("u = {}", relativeDisplacement)); + //LINFO(fmt::format("currentTime = {}", _currentTime)); + + _currentTime += dt; + + return interpolatedPose(relativeDisplacement); +} + +bool PathSegment::hasReachedEnd() { + return (_traveledDistance / pathLength()) >= 1.0; +} + /* * Get speed at time value in the range [0, duration] * OBS! If integrated over the curve it must match the total length or the curve. @@ -92,15 +120,16 @@ double PathSegment::speedAtTime(double time) { return (pathLength() * _speedFunction.value(t)) / _speedFunction.integratedSum; } -CameraPose PathSegment::interpolate(double u) const { +CameraPose PathSegment::interpolatedPose(double u) const { CameraPose cs; cs.position = _curve->positionAt(u); cs.rotation = _curve->rotationAt(u); return cs; } -std::string PathSegment::getCurrentAnchor(double u) const { - return (u > 0.5) ? _end.nodeDetails.identifier : _start.nodeDetails.identifier; +std::string PathSegment::getCurrentAnchor() const { + bool pastHalfway = (_traveledDistance / pathLength()) > 0.5; + return (pastHalfway) ? _end.nodeDetails.identifier : _start.nodeDetails.identifier; } // Initialise the curve, based on the start, end state and curve type @@ -126,9 +155,10 @@ void PathSegment::initCurve() { PathSegment::SpeedFunction::SpeedFunction(double duration) { // apply duration constraint (eq. 14 in Eberly) - double speedSum = 0.0; const int steps = 100; double dt = duration / steps; + // TODO: better approximation + double speedSum = 0.0; for (double t = 0.0; t <= 1.0; t += 1.0 / steps) { speedSum += dt * value(t); } diff --git a/modules/autonavigation/pathsegment.h b/modules/autonavigation/pathsegment.h index 7943888403..6f36a8392d 100644 --- a/modules/autonavigation/pathsegment.h +++ b/modules/autonavigation/pathsegment.h @@ -34,27 +34,27 @@ namespace openspace::autonavigation { class PathSegment { public: - PathSegment(Waypoint start, Waypoint end, double startTime, CurveType type); + PathSegment(Waypoint start, Waypoint end, CurveType type); ~PathSegment() = default; // Mutators - void setStart(Waypoint cs); + void setStart(Waypoint wp); void setDuration(double d); // Accessors const Waypoint start() const; const Waypoint end() const; const double duration() const; - const double startTime() const; - const double endTime() const; const double pathLength() const; const std::vector getControlPoints() const; // TODO: remove this debugging function - double speedAtTime(double time); + CameraPose traversePath(double dt); - CameraPose interpolate(double u) const; - std::string getCurrentAnchor(double u) const; + bool hasReachedEnd(); + double speedAtTime(double time); + CameraPose interpolatedPose(double u) const; + std::string getCurrentAnchor() const; private: void initCurve(); @@ -72,13 +72,16 @@ private: Waypoint _start; Waypoint _end; - double _startTime; double _duration; CurveType _curveType; SpeedFunction _speedFunction; std::shared_ptr _curve; + + // Playback variables + double _traveledDistance = 0.0; + double _currentTime = 0.0; // Time since playback started }; } // namespace openspace::autonavigation From 39151bda2e145f4b2455eaa79b52a618db135366 Mon Sep 17 00:00:00 2001 From: Emma Broman Date: Mon, 30 Mar 2020 14:41:36 +0200 Subject: [PATCH 085/912] Minor refactor --- .../autonavigation/autonavigationhandler.cpp | 4 +--- .../autonavigation/autonavigationhandler.h | 2 +- modules/autonavigation/pathcurves.cpp | 2 -- modules/autonavigation/pathsegment.cpp | 23 +++++++++---------- modules/autonavigation/pathsegment.h | 10 ++++---- .../autonavigation/rotationinterpolator.cpp | 10 ++++---- 6 files changed, 22 insertions(+), 29 deletions(-) diff --git a/modules/autonavigation/autonavigationhandler.cpp b/modules/autonavigation/autonavigationhandler.cpp index 6eafba9cec..0a64da5059 100644 --- a/modules/autonavigation/autonavigationhandler.cpp +++ b/modules/autonavigation/autonavigationhandler.cpp @@ -133,7 +133,6 @@ void AutoNavigationHandler::createPath(PathSpecification& spec) { break; } - // OBS! Would it be better to save the spec in the handler class? _stopAtTargets = spec.stopAtTargets(); // Check if we have a specified start navigation state. If so, update first segment @@ -389,8 +388,7 @@ void AutoNavigationHandler::addPause(std::optional duration) { _pathSegments.push_back(std::unique_ptr(new PathSegment(segment))); } -void AutoNavigationHandler::addSegment(Waypoint& waypoint, std::optional duration) { - +void AutoNavigationHandler::addSegment(Waypoint& waypoint, std::optional duration){ // TODO: Improve how curve types are handled const int curveType = _defaultCurveOption; diff --git a/modules/autonavigation/autonavigationhandler.h b/modules/autonavigation/autonavigationhandler.h index 5463e3d288..3f0d25882d 100644 --- a/modules/autonavigation/autonavigationhandler.h +++ b/modules/autonavigation/autonavigationhandler.h @@ -91,4 +91,4 @@ private: } // namespace openspace::autonavigation -#endif // __OPENSPACE_CORE___NAVIGATIONHANDLER___H__ +#endif // __OPENSPACE_MODULE___AUTONAVIGATIONHANDLER___H__ diff --git a/modules/autonavigation/pathcurves.cpp b/modules/autonavigation/pathcurves.cpp index 6f505075dd..9fcf4487ad 100644 --- a/modules/autonavigation/pathcurves.cpp +++ b/modules/autonavigation/pathcurves.cpp @@ -79,7 +79,6 @@ std::vector PathCurve::getPoints() { } Bezier3Curve::Bezier3Curve(const Waypoint& start, const Waypoint& end) { - // default rotation interpolation _rotationInterpolator = RotationInterpolator{ start, end, this, LookAt }; glm::dvec3 startNodePos = start.node()->worldPosition(); @@ -110,7 +109,6 @@ Bezier3Curve::Bezier3Curve(const Waypoint& start, const Waypoint& end) { // Assuming we move straigh out to point to a distance proportional to radius, angle is enough to check collision risk double cosStartAngle = glm::dot(startTangentDirection, startToEndDirection); double cosEndAngle = glm::dot(endTangentDirection, startToEndDirection); - double cosStartDir = glm::dot(startTangentDirection, endTangentDirection); //TODO: investigate suitable values, could be risky close to surface.. bool TARGET_BEHIND_STARTNODE = cosStartAngle < -0.8; diff --git a/modules/autonavigation/pathsegment.cpp b/modules/autonavigation/pathsegment.cpp index 85d8561b3f..b792f5a483 100644 --- a/modules/autonavigation/pathsegment.cpp +++ b/modules/autonavigation/pathsegment.cpp @@ -80,13 +80,13 @@ CameraPose PathSegment::traversePath(double dt) { // In case there is an error in the speed VS distance VS duration relation, // make sure that we actually reach the end point. double displacement; - if (_currentTime > _duration && !hasReachedEnd()) { + if (_progressedTime > _duration && !hasReachedEnd()) { // TODO: reach the target in a reasonable amount of time and with smooth motion LWARNING("Did not reach the target in the given duration. Moving toward the target in constant speed. TODO: fix so that this does not happen"); // TODO: fix and then remove this displacement = dt * speedAtTime(_duration - _duration * dt); } else { - displacement = dt * speedAtTime(_currentTime); + displacement = dt * speedAtTime(_progressedTime); } _traveledDistance += displacement; @@ -99,12 +99,17 @@ CameraPose PathSegment::traversePath(double dt) { //LINFO(fmt::format("u = {}", relativeDisplacement)); //LINFO(fmt::format("currentTime = {}", _currentTime)); - _currentTime += dt; + _progressedTime += dt; return interpolatedPose(relativeDisplacement); } -bool PathSegment::hasReachedEnd() { +std::string PathSegment::getCurrentAnchor() const { + bool pastHalfway = (_traveledDistance / pathLength()) > 0.5; + return (pastHalfway) ? _end.nodeDetails.identifier : _start.nodeDetails.identifier; +} + +bool PathSegment::hasReachedEnd() const { return (_traveledDistance / pathLength()) >= 1.0; } @@ -114,7 +119,7 @@ bool PathSegment::hasReachedEnd() { * Thus, we scale according to the constraint in eq. 14 in Eberly 2007 * (https://www.geometrictools.com/Documentation/MovingAlongCurveSpecifiedSpeed.pdf) */ -double PathSegment::speedAtTime(double time) { +double PathSegment::speedAtTime(double time) const { ghoul_assert(time >= 0 && time <= _duration, "Time out of range [0, duration]"); double t = time / _duration; return (pathLength() * _speedFunction.value(t)) / _speedFunction.integratedSum; @@ -127,14 +132,8 @@ CameraPose PathSegment::interpolatedPose(double u) const { return cs; } -std::string PathSegment::getCurrentAnchor() const { - bool pastHalfway = (_traveledDistance / pathLength()) > 0.5; - return (pastHalfway) ? _end.nodeDetails.identifier : _start.nodeDetails.identifier; -} - // Initialise the curve, based on the start, end state and curve type void PathSegment::initCurve() { - // in case there already is a curve object, reset the pointer. _curve.reset(); switch (_curveType) { @@ -166,7 +165,7 @@ PathSegment::SpeedFunction::SpeedFunction(double duration) { integratedSum = speedSum; } -double PathSegment::SpeedFunction::value(double t) { +double PathSegment::SpeedFunction::value(double t) const { ghoul_assert(t >= 0 && t <= 1, "Variable t out of range [0,1]"); const double t1 = 0.2; diff --git a/modules/autonavigation/pathsegment.h b/modules/autonavigation/pathsegment.h index 6f36a8392d..9b6b0d8444 100644 --- a/modules/autonavigation/pathsegment.h +++ b/modules/autonavigation/pathsegment.h @@ -50,11 +50,11 @@ public: const std::vector getControlPoints() const; // TODO: remove this debugging function CameraPose traversePath(double dt); + std::string getCurrentAnchor() const; + bool hasReachedEnd() const; - bool hasReachedEnd(); - double speedAtTime(double time); + double speedAtTime(double time) const; CameraPose interpolatedPose(double u) const; - std::string getCurrentAnchor() const; private: void initCurve(); @@ -63,7 +63,7 @@ private: struct SpeedFunction { SpeedFunction() = default; SpeedFunction(double duration); - double value(double t); + double value(double t) const; // store the sum of the function over the duration of the segment, // so we don't need to recompue it every time we access the speed @@ -81,7 +81,7 @@ private: // Playback variables double _traveledDistance = 0.0; - double _currentTime = 0.0; // Time since playback started + double _progressedTime = 0.0; // Time since playback started }; } // namespace openspace::autonavigation diff --git a/modules/autonavigation/rotationinterpolator.cpp b/modules/autonavigation/rotationinterpolator.cpp index 2417caa044..a40b592449 100644 --- a/modules/autonavigation/rotationinterpolator.cpp +++ b/modules/autonavigation/rotationinterpolator.cpp @@ -37,10 +37,8 @@ namespace openspace::autonavigation { RotationInterpolator::RotationInterpolator( const Waypoint& start, const Waypoint& end, PathCurve* curve, RotationMethod method) - : _start(start), _end(end), _method(method) -{ - _curve = curve; -} + : _start(start), _end(end), _method(method), _curve(curve) +{} glm::dquat RotationInterpolator::rotationAt(double u) { switch (_method) @@ -70,8 +68,8 @@ glm::dquat RotationInterpolator::easedSlerp(double u) { return glm::slerp(_start.rotation(), _end.rotation(), uScaled); } -// Look at start node until tStart, then turn to look at end node from tEnd -// Will overwrite rotation of navigation states! +// Look at start node until tStart, then turn to look at end node from tEnd. +// OBS! Will overwrite rotation of navigation states!! glm::dquat RotationInterpolator::lookAtInterpolator(double u) { double tStart = 0.15; double tEnd = 0.7; From 3ddb4e65751624eca2e619d60ea290db0bd79103 Mon Sep 17 00:00:00 2001 From: Emma Broman Date: Tue, 31 Mar 2020 11:27:30 +0200 Subject: [PATCH 086/912] Simplify speed function --- modules/autonavigation/pathsegment.cpp | 15 +++++++-------- 1 file changed, 7 insertions(+), 8 deletions(-) diff --git a/modules/autonavigation/pathsegment.cpp b/modules/autonavigation/pathsegment.cpp index b792f5a483..5bcbc2e642 100644 --- a/modules/autonavigation/pathsegment.cpp +++ b/modules/autonavigation/pathsegment.cpp @@ -168,23 +168,22 @@ PathSegment::SpeedFunction::SpeedFunction(double duration) { double PathSegment::SpeedFunction::value(double t) const { ghoul_assert(t >= 0 && t <= 1, "Variable t out of range [0,1]"); - const double t1 = 0.2; - const double t2 = 0.8; // > t1 - // TODO: more advanced computation of limits, possibly based on distances - + const double tPeak = 0.5; double speed = 1.0; // accelerate - if (t < t1) { - double tScaled = t / t1; + if (t <= tPeak) { + double tScaled = t / tPeak; speed = ghoul::cubicEaseInOut(tScaled); } // deaccelerate - else if (t > t2) { - double tScaled = (t - t2) / (1.0 - t2); + else { + double tScaled = (t - tPeak) / (1.0 - tPeak); speed = 1.0 - ghoul::cubicEaseInOut(tScaled); } + // avoid zero speed + speed += 0.001; return speed; } From 413bf210d083e69240f48a2872f28c4c46730434 Mon Sep 17 00:00:00 2001 From: Emma Broman Date: Wed, 1 Apr 2020 14:50:35 +0200 Subject: [PATCH 087/912] Set anchor correctly when creating waypoints from navstates --- modules/autonavigation/waypoint.cpp | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/modules/autonavigation/waypoint.cpp b/modules/autonavigation/waypoint.cpp index 2fd8fc8173..0ae0ab9944 100644 --- a/modules/autonavigation/waypoint.cpp +++ b/modules/autonavigation/waypoint.cpp @@ -91,6 +91,7 @@ Waypoint::Waypoint(const glm::dvec3& pos, const glm::dquat& rot, const std::stri Waypoint::Waypoint(const NavigationState& ns, const double minBoundingSphere) { // OBS! The following code is exactly the same as used in // NavigationHandler::applyNavigationState. Should probably be made into a function. + // TODO: make that function const SceneGraphNode* referenceFrame = sceneGraphNode(ns.referenceFrame); const SceneGraphNode* anchorNode = sceneGraphNode(ns.anchor); // The anchor is also the target @@ -124,7 +125,7 @@ Waypoint::Waypoint(const NavigationState& ns, const double minBoundingSphere) { pose.rotation = neutralCameraRotation * yawRotation * pitchRotation; - nodeDetails = WaypointNodeDetails{ ns.referenceFrame, minBoundingSphere }; + nodeDetails = WaypointNodeDetails{ ns.anchor, minBoundingSphere }; } glm::dvec3 Waypoint::position() const { return pose.position; } From c4c46a2043179899c31e66b993a46240b35d9291 Mon Sep 17 00:00:00 2001 From: Emma Broman Date: Wed, 1 Apr 2020 19:44:58 +0200 Subject: [PATCH 088/912] Add property to remove roll --- .../autonavigation/autonavigationhandler.cpp | 20 +++++++++++++++++++ .../autonavigation/autonavigationhandler.h | 2 ++ 2 files changed, 22 insertions(+) diff --git a/modules/autonavigation/autonavigationhandler.cpp b/modules/autonavigation/autonavigationhandler.cpp index 0a64da5059..c0297ca84a 100644 --- a/modules/autonavigation/autonavigationhandler.cpp +++ b/modules/autonavigation/autonavigationhandler.cpp @@ -56,6 +56,12 @@ namespace { "The defualt curve type chosen when generating a path, if none is specified." }; + constexpr const openspace::properties::Property::PropertyInfo IncludeRollInfo = { + "IncludeRollInfo", + "Include Roll", + "If disabled, roll is removed from the interpolation of camera orientation." + }; + } // namespace namespace openspace::autonavigation { @@ -64,6 +70,7 @@ AutoNavigationHandler::AutoNavigationHandler() : properties::PropertyOwner({ "AutoNavigationHandler" }) , _minAllowedBoundingSphere(MinimalBoundingSphereInfo, 10.0, 1.0, 3e10) , _defaultCurveOption(DefaultCurveOptionInfo, properties::OptionProperty::DisplayType::Dropdown) + , _includeRoll(IncludeRollInfo, false) { addProperty(_minAllowedBoundingSphere); @@ -72,6 +79,7 @@ AutoNavigationHandler::AutoNavigationHandler() { CurveType::Linear, "Linear"} }); addProperty(_defaultCurveOption); + addProperty(_includeRoll); } AutoNavigationHandler::~AutoNavigationHandler() {} // NOLINT @@ -102,6 +110,18 @@ void AutoNavigationHandler::updateCamera(double deltaTime) { global::navigationHandler.orbitalNavigator().setAnchorNode(newAnchor); } + if (!_includeRoll) { + const double notTooCloseDistance = 100.0; + glm::dvec3 cameraDir = glm::normalize(newPose.rotation * Camera::ViewDirectionCameraSpace); + glm::dvec3 lookAtPos = newPose.position + notTooCloseDistance * cameraDir; + glm::dquat rollFreeRotation = helpers::getLookAtQuaternion( + newPose.position, + lookAtPos, + camera()->lookUpVectorWorldSpace() + ); + newPose.rotation = rollFreeRotation; + } + camera()->setPositionVec3(newPose.position); camera()->setRotation(newPose.rotation); diff --git a/modules/autonavigation/autonavigationhandler.h b/modules/autonavigation/autonavigationhandler.h index 3f0d25882d..e9e3be473f 100644 --- a/modules/autonavigation/autonavigationhandler.h +++ b/modules/autonavigation/autonavigationhandler.h @@ -87,6 +87,8 @@ private: properties::DoubleProperty _minAllowedBoundingSphere; properties::OptionProperty _defaultCurveOption; + properties::BoolProperty _includeRoll; + }; } // namespace openspace::autonavigation From 82c0ae659be917c12659847c075108d96fbf814a Mon Sep 17 00:00:00 2001 From: Emma Broman Date: Thu, 2 Apr 2020 21:00:23 +0200 Subject: [PATCH 089/912] Bug fix: distance to lookAt pos in roll removal too small for alrge distances --- modules/autonavigation/autonavigationhandler.cpp | 9 +++++++-- modules/autonavigation/autonavigationhandler.h | 1 + 2 files changed, 8 insertions(+), 2 deletions(-) diff --git a/modules/autonavigation/autonavigationhandler.cpp b/modules/autonavigation/autonavigationhandler.cpp index c0297ca84a..6f2a115e6f 100644 --- a/modules/autonavigation/autonavigationhandler.cpp +++ b/modules/autonavigation/autonavigationhandler.cpp @@ -88,6 +88,10 @@ Camera* AutoNavigationHandler::camera() const { return global::navigationHandler.camera(); } +const SceneGraphNode* AutoNavigationHandler::anchor() const { + return global::navigationHandler.anchorNode(); +} + bool AutoNavigationHandler::hasFinished() const { int lastIndex = (int)_pathSegments.size() - 1; return _currentSegmentIndex > lastIndex; @@ -105,13 +109,14 @@ void AutoNavigationHandler::updateCamera(double deltaTime) { // Set anchor node in orbitalNavigator, to render visible nodes and add activate // navigation when we reach the end. - std::string currentAnchor = global::navigationHandler.anchorNode()->identifier(); + std::string currentAnchor = anchor()->identifier(); if (currentAnchor != newAnchor) { global::navigationHandler.orbitalNavigator().setAnchorNode(newAnchor); } if (!_includeRoll) { - const double notTooCloseDistance = 100.0; + glm::dvec3 anchorPos = anchor()->worldPosition(); + const double notTooCloseDistance = deltaTime * glm::distance(anchorPos, newPose.position); glm::dvec3 cameraDir = glm::normalize(newPose.rotation * Camera::ViewDirectionCameraSpace); glm::dvec3 lookAtPos = newPose.position + notTooCloseDistance * cameraDir; glm::dquat rollFreeRotation = helpers::getLookAtQuaternion( diff --git a/modules/autonavigation/autonavigationhandler.h b/modules/autonavigation/autonavigationhandler.h index e9e3be473f..cb31edd707 100644 --- a/modules/autonavigation/autonavigationhandler.h +++ b/modules/autonavigation/autonavigationhandler.h @@ -50,6 +50,7 @@ public: // Accessors Camera* camera() const; + const SceneGraphNode* anchor() const; bool hasFinished() const; void updateCamera(double deltaTime); From fbd383491ae7c279bd8ebac785fb74929ebc3f63 Mon Sep 17 00:00:00 2001 From: Emma Broman Date: Thu, 2 Apr 2020 21:15:08 +0200 Subject: [PATCH 090/912] make sure to always include 1.0 in _parameterIntervals --- modules/autonavigation/pathcurves.cpp | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) diff --git a/modules/autonavigation/pathcurves.cpp b/modules/autonavigation/pathcurves.cpp index 9fcf4487ad..c06a19dc44 100644 --- a/modules/autonavigation/pathcurves.cpp +++ b/modules/autonavigation/pathcurves.cpp @@ -204,9 +204,14 @@ glm::dvec3 Bezier3Curve::positionAt(double u) { // compute curve parameter intervals based on relative arc length void Bezier3Curve::initParameterIntervals() { std::vector newIntervals; - for (double t = 0.0; t <= 1.0; t += 1.0 / _nrSegments) { - newIntervals.push_back(arcLength(t) / _length); + double dt = 1.0 / _nrSegments; + + newIntervals.push_back(0.0); + for (int i = 1; i < _nrSegments; i++) { + newIntervals.push_back(arcLength(dt * i) / _length); } + newIntervals.push_back(1.0); + _parameterIntervals.swap(newIntervals); } From 387c2adbee1387b1ee65461ce7381db52cf9194b Mon Sep 17 00:00:00 2001 From: Emma Broman Date: Sun, 5 Apr 2020 14:33:14 +0200 Subject: [PATCH 091/912] Remove old pause impl. and make property for stopAtTargets --- .../autonavigation/autonavigationhandler.cpp | 61 ++++++------------- .../autonavigation/autonavigationhandler.h | 9 +-- modules/autonavigation/instruction.cpp | 8 --- modules/autonavigation/instruction.h | 9 +-- modules/autonavigation/pathspecification.cpp | 6 +- modules/autonavigation/pathspecification.h | 4 +- 6 files changed, 29 insertions(+), 68 deletions(-) diff --git a/modules/autonavigation/autonavigationhandler.cpp b/modules/autonavigation/autonavigationhandler.cpp index 6f2a115e6f..c4c7bab8aa 100644 --- a/modules/autonavigation/autonavigationhandler.cpp +++ b/modules/autonavigation/autonavigationhandler.cpp @@ -62,6 +62,13 @@ namespace { "If disabled, roll is removed from the interpolation of camera orientation." }; + constexpr const openspace::properties::Property::PropertyInfo StopAtTargetsPerDefaultInfo = { + "StopAtTargetsPerDefault", + "Stop At Targets Per Default", + "Applied during path creation. If enabled, stops are automatically added between" + " the path segments. The user must then choose to continue the apth after reaching a target" + }; + } // namespace namespace openspace::autonavigation { @@ -71,6 +78,7 @@ AutoNavigationHandler::AutoNavigationHandler() , _minAllowedBoundingSphere(MinimalBoundingSphereInfo, 10.0, 1.0, 3e10) , _defaultCurveOption(DefaultCurveOptionInfo, properties::OptionProperty::DisplayType::Dropdown) , _includeRoll(IncludeRollInfo, false) + , _stopAtTargetsPerDefault(StopAtTargetsPerDefaultInfo, false) { addProperty(_minAllowedBoundingSphere); @@ -80,6 +88,7 @@ AutoNavigationHandler::AutoNavigationHandler() }); addProperty(_defaultCurveOption); addProperty(_includeRoll); + addProperty(_stopAtTargetsPerDefault); } AutoNavigationHandler::~AutoNavigationHandler() {} // NOLINT @@ -139,7 +148,7 @@ void AutoNavigationHandler::updateCamera(double deltaTime) { return; } - if (_stopAtTargets) { + if (_stopAtTargetsPerDefault) { pausePath(); return; } @@ -149,6 +158,9 @@ void AutoNavigationHandler::updateCamera(double deltaTime) { void AutoNavigationHandler::createPath(PathSpecification& spec) { clearPath(); + if(spec.stopAtTargetsSpecified()) + _stopAtTargetsPerDefault = spec.stopAtTargets(); + bool success = true; for (int i = 0; i < spec.instructions()->size(); i++) { const Instruction& ins = spec.instructions()->at(i); @@ -158,8 +170,6 @@ void AutoNavigationHandler::createPath(PathSpecification& spec) { break; } - _stopAtTargets = spec.stopAtTargets(); - // Check if we have a specified start navigation state. If so, update first segment if (spec.hasStartState() && _pathSegments.size() > 0) { Waypoint startState{ spec.startState() , _minAllowedBoundingSphere}; @@ -293,10 +303,6 @@ bool AutoNavigationHandler::handleInstruction(const Instruction& ins, int index) success = handleNavigationStateInstruction(ins); break; - case InstructionType::Pause: - success = handlePauseInstruction(ins); - break; - default: LERROR("Non-implemented instruction type."); success = false; @@ -363,8 +369,7 @@ bool AutoNavigationHandler::handleTargetNodeInstruction(const Instruction& ins) glm::dquat targetRot = glm::normalize(glm::inverse(glm::quat_cast(lookAtMat))); Waypoint endState{ targetPos, targetRot, identifier, _minAllowedBoundingSphere }; - - addSegment(endState, ins.props->duration); + addSegment(endState, ins); return true; } @@ -379,49 +384,19 @@ bool AutoNavigationHandler::handleNavigationStateInstruction(const Instruction& } Waypoint endState{ props->navState , _minAllowedBoundingSphere }; - - addSegment(endState, ins.props->duration); + addSegment(endState, ins); return true; } -bool AutoNavigationHandler::handlePauseInstruction(const Instruction& ins) { - // Verify instruction type - PauseInstructionProps* props = - dynamic_cast(ins.props.get()); - - if (!props) { - LERROR(fmt::format("Could not handle pause instruction.")); - return false; - } - - // TODO: implement more complex behavior later - - addPause(ins.props->duration); - return true; -} - -void AutoNavigationHandler::addPause(std::optional duration) { - Waypoint waypoint = lastWayPoint(); - PathSegment segment = PathSegment(waypoint, waypoint, CurveType::Pause); - - // TODO: implement more complex behavior later - - // TODO: handle duration better - if (duration.has_value()) { - segment.setDuration(duration.value()); - } - _pathSegments.push_back(std::unique_ptr(new PathSegment(segment))); -} - -void AutoNavigationHandler::addSegment(Waypoint& waypoint, std::optional duration){ +void AutoNavigationHandler::addSegment(Waypoint& waypoint, const Instruction& ins){ // TODO: Improve how curve types are handled const int curveType = _defaultCurveOption; PathSegment segment = PathSegment(lastWayPoint(), waypoint, CurveType(curveType)); // TODO: handle duration better - if (duration.has_value()) { - segment.setDuration(duration.value()); + if (ins.props->duration.has_value()) { + segment.setDuration(ins.props->duration.value()); } _pathSegments.push_back(std::unique_ptr(new PathSegment(segment))); } diff --git a/modules/autonavigation/autonavigationhandler.h b/modules/autonavigation/autonavigationhandler.h index cb31edd707..b36cd56572 100644 --- a/modules/autonavigation/autonavigationhandler.h +++ b/modules/autonavigation/autonavigationhandler.h @@ -73,23 +73,20 @@ private: bool handleTargetNodeInstruction(const Instruction& ins); bool handleNavigationStateInstruction(const Instruction& ins); - bool handlePauseInstruction(const Instruction& ins); - void addPause(std::optional duration); - void addSegment(Waypoint& state, std::optional duration); + void addSegment(Waypoint& state, const Instruction& ins); // this list essentially represents the camera path std::vector> _pathSegments; + bool _isPlaying = false; unsigned int _currentSegmentIndex = 0; - bool _stopAtTargets; - properties::DoubleProperty _minAllowedBoundingSphere; properties::OptionProperty _defaultCurveOption; properties::BoolProperty _includeRoll; - + properties::BoolProperty _stopAtTargetsPerDefault; }; } // namespace openspace::autonavigation diff --git a/modules/autonavigation/instruction.cpp b/modules/autonavigation/instruction.cpp index e898f61dc4..c11b7eb210 100644 --- a/modules/autonavigation/instruction.cpp +++ b/modules/autonavigation/instruction.cpp @@ -139,10 +139,6 @@ NavigationStateInstructionProps::NavigationStateInstructionProps( } } -PauseInstructionProps::PauseInstructionProps(const ghoul::Dictionary& dictionary) - : InstructionProps(dictionary) -{ } - Instruction::Instruction(const ghoul::Dictionary& dictionary) { // TODO: test against some documentation? @@ -156,10 +152,6 @@ Instruction::Instruction(const ghoul::Dictionary& dictionary) { type = InstructionType::NavigationState; props = std::make_shared(dictionary); } - else if (dictionary.hasValue(KeyDuration)) { - type = InstructionType::Pause; - props = std::make_shared(dictionary); - } else { throw ghoul::RuntimeError( "Could not deduce instruction type." diff --git a/modules/autonavigation/instruction.h b/modules/autonavigation/instruction.h index 3a8785d642..f7509416c6 100644 --- a/modules/autonavigation/instruction.h +++ b/modules/autonavigation/instruction.h @@ -30,7 +30,7 @@ namespace openspace::autonavigation { -enum class InstructionType { TargetNode, NavigationState, Pause }; +enum class InstructionType { TargetNode, NavigationState }; struct InstructionProps { InstructionProps() = default; @@ -54,13 +54,6 @@ struct NavigationStateInstructionProps : public InstructionProps { interaction::NavigationHandler::NavigationState navState; }; -struct PauseInstructionProps : public InstructionProps { - PauseInstructionProps(const ghoul::Dictionary& dictionary); - - // For now, a pause instruction does not have any special props. - // Might be added later -}; - struct Instruction { Instruction() = default; Instruction(const ghoul::Dictionary& dictionary); diff --git a/modules/autonavigation/pathspecification.cpp b/modules/autonavigation/pathspecification.cpp index 1543f547b0..59ed958a76 100644 --- a/modules/autonavigation/pathspecification.cpp +++ b/modules/autonavigation/pathspecification.cpp @@ -96,7 +96,11 @@ const std::vector* PathSpecification::instructions() const { } const bool PathSpecification::stopAtTargets() const { - return _stopAtTargets; + return _stopAtTargets.value(); +} + +const bool PathSpecification::stopAtTargetsSpecified() const { + return _stopAtTargets.has_value(); } const interaction::NavigationHandler::NavigationState& PathSpecification::startState() const { diff --git a/modules/autonavigation/pathspecification.h b/modules/autonavigation/pathspecification.h index 7e775b7672..bf9aca8f36 100644 --- a/modules/autonavigation/pathspecification.h +++ b/modules/autonavigation/pathspecification.h @@ -46,13 +46,13 @@ public: // Accessors const std::vector* instructions() const; const bool stopAtTargets() const; + const bool stopAtTargetsSpecified() const; const NavigationState& startState() const; - const bool hasStartState() const; private: std::vector _instructions; - bool _stopAtTargets = false; // default is to play the entire path without stops + std::optional _stopAtTargets; std::optional _startState; // TODO: maxSpeed or speedFactor or something? From df5b518f07f6ce8305f26f2e707093bad6a800c2 Mon Sep 17 00:00:00 2001 From: Emma Broman Date: Mon, 6 Apr 2020 16:25:20 +0200 Subject: [PATCH 092/912] refactor instructions --- .../autonavigation/autonavigationhandler.cpp | 117 ++----------- .../autonavigation/autonavigationhandler.h | 9 +- .../autonavigationmodule_lua.inl | 2 +- modules/autonavigation/instruction.cpp | 164 +++++++++--------- modules/autonavigation/instruction.h | 47 +++-- modules/autonavigation/pathspecification.cpp | 94 +++++++--- modules/autonavigation/pathspecification.h | 9 +- 7 files changed, 194 insertions(+), 248 deletions(-) diff --git a/modules/autonavigation/autonavigationhandler.cpp b/modules/autonavigation/autonavigationhandler.cpp index c4c7bab8aa..081e42de68 100644 --- a/modules/autonavigation/autonavigationhandler.cpp +++ b/modules/autonavigation/autonavigationhandler.cpp @@ -102,7 +102,7 @@ const SceneGraphNode* AutoNavigationHandler::anchor() const { } bool AutoNavigationHandler::hasFinished() const { - int lastIndex = (int)_pathSegments.size() - 1; + unsigned int lastIndex = (unsigned int)_pathSegments.size() - 1; return _currentSegmentIndex > lastIndex; } @@ -163,11 +163,13 @@ void AutoNavigationHandler::createPath(PathSpecification& spec) { bool success = true; for (int i = 0; i < spec.instructions()->size(); i++) { - const Instruction& ins = spec.instructions()->at(i); - success = handleInstruction(ins, i); - - if (!success) - break; + const Instruction* ins = spec.instruction(i); + if (ins) { + // TODO: allow for a list of waypoints + std::vector waypoints = ins->getWaypoints(); + if (waypoints.size() > 0) + addSegment(waypoints[0], ins); + } } // Check if we have a specified start navigation state. If so, update first segment @@ -291,112 +293,15 @@ Waypoint AutoNavigationHandler::lastWayPoint() { return _pathSegments.empty() ? wayPointFromCamera() : _pathSegments.back()->end(); } -bool AutoNavigationHandler::handleInstruction(const Instruction& ins, int index) { - bool success = true; - switch (ins.type) - { - case InstructionType::TargetNode: - success = handleTargetNodeInstruction(ins); - break; - - case InstructionType::NavigationState: - success = handleNavigationStateInstruction(ins); - break; - - default: - LERROR("Non-implemented instruction type."); - success = false; - break; - } - - if (!success) { - LERROR(fmt::format("Failed handling instruction number {}.", std::to_string(index + 1))); - return false; - } - - return true; -} - -bool AutoNavigationHandler::handleTargetNodeInstruction(const Instruction& ins) { - // Verify instruction type - TargetNodeInstructionProps* props = - dynamic_cast(ins.props.get()); - - if (!props) { - LERROR("Could not handle target node instruction."); - return false; - } - - // Compute end state - std::string& identifier = props->targetNode; - const SceneGraphNode* targetNode = sceneGraphNode(identifier); - - if (!targetNode) { - LERROR(fmt::format("Could not find node '{}' to target", identifier)); - return false; - } - - glm::dvec3 targetPos; - if (props->position.has_value()) { - // note that the anchor and reference frame is our targetnode. - // The position in instruction is given is relative coordinates. - targetPos = targetNode->worldPosition() + - targetNode->worldRotationMatrix() * props->position.value(); - } - else { - // TODO: Instead of this case, allow the curve to set its final position - - glm::dvec3 nodePos = targetNode->worldPosition(); - glm::dvec3 nodeToPrev = lastWayPoint().position() - nodePos; - // TODO: compute position in a more clever way - - const double radius = WaypointNodeDetails::findValidBoundingSphere(targetNode, _minAllowedBoundingSphere); - const double defaultHeight = 2 * radius; - - bool hasHeight = props->height.has_value(); - double height = hasHeight ? props->height.value() : defaultHeight; - - // move target position out from surface, along vector to camera - targetPos = nodePos + glm::normalize(nodeToPrev) * (radius + height); - } - - glm::dmat4 lookAtMat = glm::lookAt( - targetPos, - targetNode->worldPosition(), - camera()->lookUpVectorWorldSpace() - ); - - glm::dquat targetRot = glm::normalize(glm::inverse(glm::quat_cast(lookAtMat))); - - Waypoint endState{ targetPos, targetRot, identifier, _minAllowedBoundingSphere }; - addSegment(endState, ins); - return true; -} - -bool AutoNavigationHandler::handleNavigationStateInstruction(const Instruction& ins) { - // Verify instruction type - NavigationStateInstructionProps* props = - dynamic_cast(ins.props.get()); - - if (!props) { - LERROR(fmt::format("Could not handle navigation state instruction.")); - return false; - } - - Waypoint endState{ props->navState , _minAllowedBoundingSphere }; - addSegment(endState, ins); - return true; -} - -void AutoNavigationHandler::addSegment(Waypoint& waypoint, const Instruction& ins){ +void AutoNavigationHandler::addSegment(Waypoint& waypoint, const Instruction* ins){ // TODO: Improve how curve types are handled const int curveType = _defaultCurveOption; PathSegment segment = PathSegment(lastWayPoint(), waypoint, CurveType(curveType)); // TODO: handle duration better - if (ins.props->duration.has_value()) { - segment.setDuration(ins.props->duration.value()); + if (ins->duration.has_value()) { + segment.setDuration(ins->duration.value()); } _pathSegments.push_back(std::unique_ptr(new PathSegment(segment))); } diff --git a/modules/autonavigation/autonavigationhandler.h b/modules/autonavigation/autonavigationhandler.h index b36cd56572..13076d7226 100644 --- a/modules/autonavigation/autonavigationhandler.h +++ b/modules/autonavigation/autonavigationhandler.h @@ -68,18 +68,11 @@ public: private: Waypoint wayPointFromCamera(); Waypoint lastWayPoint(); - - bool handleInstruction(const Instruction& ins, int index); - - bool handleTargetNodeInstruction(const Instruction& ins); - bool handleNavigationStateInstruction(const Instruction& ins); - - void addSegment(Waypoint& state, const Instruction& ins); + void addSegment(Waypoint& waypoint, const Instruction* ins); // this list essentially represents the camera path std::vector> _pathSegments; - bool _isPlaying = false; unsigned int _currentSegmentIndex = 0; diff --git a/modules/autonavigation/autonavigationmodule_lua.inl b/modules/autonavigation/autonavigationmodule_lua.inl index a4b1d4b728..b2282c8e6f 100644 --- a/modules/autonavigation/autonavigationmodule_lua.inl +++ b/modules/autonavigation/autonavigationmodule_lua.inl @@ -83,7 +83,7 @@ namespace openspace::autonavigation::luascriptfunctions { insDict.setValue("Duration", duration); } - PathSpecification spec = PathSpecification(Instruction{insDict}); + PathSpecification spec = PathSpecification(TargetNodeInstruction{insDict}); AutoNavigationModule* module = global::moduleEngine.module(); AutoNavigationHandler& handler = module->AutoNavigationHandler(); diff --git a/modules/autonavigation/instruction.cpp b/modules/autonavigation/instruction.cpp index c11b7eb210..0c2ee308aa 100644 --- a/modules/autonavigation/instruction.cpp +++ b/modules/autonavigation/instruction.cpp @@ -25,6 +25,11 @@ #include #include +#include +#include +#include +#include +#include #include namespace { @@ -40,73 +45,30 @@ namespace { namespace openspace::autonavigation { -documentation::Documentation TargetNodeInstructionDocumentation() { - using namespace documentation; - - return { - "Target Node Instruction", - "target_node_instruction", - { - { - KeyTarget, - new StringVerifier, - Optional::No, - "The identifier of the target node." - }, - { - KeyDuration, - new DoubleVerifier, - Optional::Yes, - "The desired duration for the camera movement." - }, - { - KeyPosition, - new Vector3Verifier, - Optional::Yes, - "The desired final position for the camera movement, given in model space." - }, - { - KeyHeight, - new DoubleVerifier, - Optional::Yes, - "The desired height from surface for final position (meters). Will be ignored if a target position is set. " - }, - } - }; -} - -InstructionProps::InstructionProps(const ghoul::Dictionary& dictionary) { - // TODO: validate against some documentation? - +Instruction::Instruction(const ghoul::Dictionary& dictionary) { if (dictionary.hasValue(KeyDuration)) { duration = dictionary.value(KeyDuration); } + + // TODO: include info about pauses/stops } -InstructionProps::~InstructionProps() {} +Instruction::~Instruction() {} -TargetNodeInstructionProps::TargetNodeInstructionProps( - const ghoul::Dictionary& dictionary) : InstructionProps(dictionary) +TargetNodeInstruction::TargetNodeInstruction(const ghoul::Dictionary& dictionary) + : Instruction(dictionary) { - try { - documentation::testSpecificationAndThrow( - TargetNodeInstructionDocumentation(), - dictionary, - "Target Node Instruction" - ); - } - catch (ghoul::RuntimeError& e) { - LERROR(fmt::format("Unable to generate target node instruction from dictionary. Does not match documentation: {}", e.message)); - return; - } - if (!dictionary.hasValue(KeyTarget)) { throw ghoul::RuntimeError( "A camera path instruction requires a target node, to go to or use as reference frame." ); } - targetNode = dictionary.value(KeyTarget); + nodeIdentifier = dictionary.value(KeyTarget); + + if (!sceneGraphNode(nodeIdentifier)) { + throw ghoul::RuntimeError(fmt::format("Could not find target node '{}'", nodeIdentifier)); + } if (dictionary.hasValue(KeyPosition)) { position = dictionary.value(KeyPosition); @@ -117,46 +79,76 @@ TargetNodeInstructionProps::TargetNodeInstructionProps( } } -NavigationStateInstructionProps::NavigationStateInstructionProps( - const ghoul::Dictionary& dictionary) : InstructionProps(dictionary) +std::vector TargetNodeInstruction::getWaypoints() const { + const SceneGraphNode* targetNode = sceneGraphNode(nodeIdentifier); + if (!targetNode) { + LERROR(fmt::format("Could not find target node '{}'", nodeIdentifier)); + return std::vector(); + } + + glm::dvec3 targetPos; + if (position.has_value()) { + // note that the anchor and reference frame is our targetnode. + // The position in instruction is given is relative coordinates. + targetPos = targetNode->worldPosition() + + targetNode->worldRotationMatrix() * position.value(); + } + else { + // TODO: Instead of this case, allow the curve to set its final position + + //glm::dvec3 nodePos = targetNode->worldPosition(); + //glm::dvec3 nodeToPrev = lastWayPoint().position() - nodePos; + //// TODO: compute position in a more clever way + + //const double radius = WaypointNodeDetails::findValidBoundingSphere(targetNode, _minAllowedBoundingSphere); + //const double defaultHeight = 2 * radius; + + //bool hasHeight = props->height.has_value(); + //double height = hasHeight ? props->height.value() : defaultHeight; + + //// move target position out from surface, along vector to camera + //targetPos = nodePos + glm::normalize(nodeToPrev) * (radius + height); + + + // OBS! TEMPORARY!! TODO: fix so that a camera pose is optional in Waypoint + const double radius = WaypointNodeDetails::findValidBoundingSphere(targetNode, 10.0); + targetPos = targetNode->worldPosition() + 3 * radius * glm::dvec3(1.0, 0.0, 0.0); + } + + Camera* camera = global::navigationHandler.camera(); + + glm::dmat4 lookAtMat = glm::lookAt( + targetPos, + targetNode->worldPosition(), + camera->lookUpVectorWorldSpace() + ); + + glm::dquat targetRot = glm::normalize(glm::inverse(glm::quat_cast(lookAtMat))); + + Waypoint wp{ targetPos, targetRot, nodeIdentifier, 10.0 }; // TODO: make property for min valid boudnignsphere + + return std::vector({ wp }); +} + +NavigationStateInstruction::NavigationStateInstruction( + const ghoul::Dictionary& dictionary): Instruction(dictionary) { if (dictionary.hasValue(KeyNavigationState)) { auto navStateDict = dictionary.value(KeyNavigationState); - try { - openspace::documentation::testSpecificationAndThrow( - interaction::NavigationHandler::NavigationState::Documentation(), - navStateDict, - "NavigationState" - ); - } - catch (ghoul::RuntimeError& e) { - LERROR(fmt::format("Unable to generate navigation state instruction from dictionary. Does not match documentation: {}", e.message)); - return; - } + openspace::documentation::testSpecificationAndThrow( + NavigationState::Documentation(), + navStateDict, + "NavigationState" + ); - navState = interaction::NavigationHandler::NavigationState(navStateDict); + navigationState = NavigationState(navStateDict); } } -Instruction::Instruction(const ghoul::Dictionary& dictionary) { - - // TODO: test against some documentation? - - // Deduce the instruction type based on the fields in the dictionary - if (dictionary.hasValue(KeyTarget)) { - type = InstructionType::TargetNode; - props = std::make_shared(dictionary); - } - else if (dictionary.hasValue(KeyNavigationState)) { - type = InstructionType::NavigationState; - props = std::make_shared(dictionary); - } - else { - throw ghoul::RuntimeError( - "Could not deduce instruction type." - ); - } +std::vector NavigationStateInstruction::getWaypoints() const { + Waypoint wp{ navigationState, 10.0 }; // TODO: make property for min valid boudnignsphere + return std::vector({ wp }); } } // namespace openspace::autonavigation diff --git a/modules/autonavigation/instruction.h b/modules/autonavigation/instruction.h index f7509416c6..abf12ac8d2 100644 --- a/modules/autonavigation/instruction.h +++ b/modules/autonavigation/instruction.h @@ -25,42 +25,53 @@ #ifndef __OPENSPACE_MODULE___PATHINSTRUCTION___H__ #define __OPENSPACE_MODULE___PATHINSTRUCTION___H__ +#include #include #include namespace openspace::autonavigation { -enum class InstructionType { TargetNode, NavigationState }; +struct Instruction { + Instruction() = default; + Instruction(const ghoul::Dictionary& dictionary); + virtual ~Instruction(); -struct InstructionProps { - InstructionProps() = default; - InstructionProps(const ghoul::Dictionary& dictionary); - virtual ~InstructionProps() = 0; + virtual std::vector getWaypoints() const = 0; + + // TODO + //static documentation::Documentation Documentation(); std::optional duration; + + // TODO: include pause information }; -struct TargetNodeInstructionProps : public InstructionProps { - TargetNodeInstructionProps(const ghoul::Dictionary& dictionary); +struct TargetNodeInstruction : public Instruction { + TargetNodeInstruction(const ghoul::Dictionary& dictionary); - std::string targetNode; + std::vector getWaypoints() const override; + + // TODO + //static documentation::Documentation Documentation(); + + std::string nodeIdentifier; std::optional position; // relative to target node (model space) std::optional height; }; -struct NavigationStateInstructionProps : public InstructionProps { - NavigationStateInstructionProps(const ghoul::Dictionary& dictionary); +struct NavigationStateInstruction : public Instruction { + using NavigationState = interaction::NavigationHandler::NavigationState; - interaction::NavigationHandler::NavigationState navState; + NavigationStateInstruction(const ghoul::Dictionary& dictionary); + + std::vector getWaypoints() const override; + + // TODO + //static documentation::Documentation Documentation(); + + NavigationState navigationState; }; -struct Instruction { - Instruction() = default; - Instruction(const ghoul::Dictionary& dictionary); - - InstructionType type; - std::shared_ptr props; -}; } // namespace openspace::autonavigation diff --git a/modules/autonavigation/pathspecification.cpp b/modules/autonavigation/pathspecification.cpp index 59ed958a76..d0806d5e5e 100644 --- a/modules/autonavigation/pathspecification.cpp +++ b/modules/autonavigation/pathspecification.cpp @@ -35,66 +35,76 @@ namespace { constexpr const char* KeyStopAtTargets = "StopAtTargets"; constexpr const char* KeyStartState = "StartState"; + // Instruction Types + constexpr const char* KeyType = "Type"; + constexpr const char* KeyTypeTargetNode = "Node"; + constexpr const char* KeyTypeNavigationState = "NavigationState"; + } // namespace namespace openspace::autonavigation { -PathSpecification::PathSpecification(const ghoul::Dictionary& dictionary) { - try { - documentation::testSpecificationAndThrow( - Documentation(), - dictionary, - "Path Specification" - ); - } - catch (ghoul::RuntimeError& e) { - LERROR(fmt::format("Unable to generate path specification from dictionary. Does not match documentation: {}", e.message)); - return; - } +using NavigationState = interaction::NavigationHandler::NavigationState; - // Read instructions from dictionary - ghoul::Dictionary instructions = dictionary.value(KeyInstructions); +PathSpecification::PathSpecification(const ghoul::Dictionary& dictionary) { + documentation::testSpecificationAndThrow( + Documentation(), + dictionary, + "Path Specification" + ); + + ghoul::Dictionary instructions = + dictionary.value(KeyInstructions); for (size_t i = 1; i <= instructions.size(); ++i) { - ghoul::Dictionary insDict = instructions.value(std::to_string(i)); + ghoul::Dictionary insDict = + instructions.value(std::to_string(i)); - _instructions.push_back(Instruction{ insDict }); + if (!insDict.hasValue(KeyType)) { + throw ghoul::RuntimeError( + "Each instruction must have a specified type." + ); + } + + std::string type = insDict.value(KeyType); + tryReadInstruction(i, type, insDict); } - // Read stop at targets flag if (dictionary.hasValue(KeyStopAtTargets)) { _stopAtTargets = dictionary.value(KeyStopAtTargets); } - // Read start state if (dictionary.hasValue(KeyStartState)) { auto navStateDict = dictionary.value(KeyStartState); try { openspace::documentation::testSpecificationAndThrow( - interaction::NavigationHandler::NavigationState::Documentation(), + NavigationState::Documentation(), navStateDict, "NavigationState" ); } catch (ghoul::RuntimeError& e) { - LERROR(fmt::format("Unable to read start navigation state. Does not match documentation: {}", e.message)); + LERROR(fmt::format("Unable to read start navigation state. {}", e.message)); return; } - _startState = interaction::NavigationHandler::NavigationState(navStateDict); + _startState = NavigationState(navStateDict); } } -PathSpecification::PathSpecification(const Instruction instruction) { - _instructions.push_back(instruction); - _stopAtTargets = false; +PathSpecification::PathSpecification(const TargetNodeInstruction instruction) { + _instructions.push_back(std::make_unique(instruction)); } -const std::vector* PathSpecification::instructions() const { +const std::vector>* PathSpecification::instructions() const { return &_instructions; } +const Instruction* PathSpecification::instruction(int i) const { + return (_instructions.size() > i) ? _instructions[i].get() : nullptr; +} + const bool PathSpecification::stopAtTargets() const { return _stopAtTargets.value(); } @@ -103,7 +113,7 @@ const bool PathSpecification::stopAtTargetsSpecified() const { return _stopAtTargets.has_value(); } -const interaction::NavigationHandler::NavigationState& PathSpecification::startState() const { +const NavigationState& PathSpecification::startState() const { return _startState.value(); } @@ -140,4 +150,36 @@ documentation::Documentation PathSpecification::Documentation() { }; } +void PathSpecification::tryReadInstruction(int index, std::string type, + ghoul::Dictionary& dictionary) +{ + // create correct type of instruction and present and throw error with useful + // error message if we failed. + if (type == KeyTypeTargetNode) { + try { + _instructions.push_back(std::make_unique(dictionary)); + } + catch (ghoul::RuntimeError& e) { + throw ghoul::RuntimeError( + fmt::format("Failed reading instruction {}: {}", index, e.message)); + } + } + else if (type == KeyTypeNavigationState) { + try { + _instructions.push_back( + std::make_unique(dictionary)); + } + catch (ghoul::RuntimeError& e) { + throw ghoul::RuntimeError( + fmt::format("Failed reading instruction {}: {}", index, e.message)); + } + } + else { + throw ghoul::RuntimeError(fmt::format( + "Failed reading instruction {}: Uknown instruction type '{}'", + index, type) + ); + } +} + } // namespace openspace::autonavigation diff --git a/modules/autonavigation/pathspecification.h b/modules/autonavigation/pathspecification.h index bf9aca8f36..8da8c205fc 100644 --- a/modules/autonavigation/pathspecification.h +++ b/modules/autonavigation/pathspecification.h @@ -39,19 +39,22 @@ class PathSpecification { public: PathSpecification() = default; PathSpecification(const ghoul::Dictionary& dictionary); - PathSpecification(const Instruction instruction); + PathSpecification(const TargetNodeInstruction instruction); static documentation::Documentation Documentation(); // Accessors - const std::vector* instructions() const; + const std::vector>* instructions() const; + const Instruction* instruction(int i) const; const bool stopAtTargets() const; const bool stopAtTargetsSpecified() const; const NavigationState& startState() const; const bool hasStartState() const; private: - std::vector _instructions; + void tryReadInstruction(int index, std::string type, ghoul::Dictionary& dictionary); + + std::vector> _instructions; std::optional _stopAtTargets; std::optional _startState; From 141df97471a2ecf45d7f3af7f343c57af340453a Mon Sep 17 00:00:00 2001 From: Emma Broman Date: Mon, 6 Apr 2020 17:22:29 +0200 Subject: [PATCH 093/912] Move min boundingsphere property to module class and access globally --- .../autonavigation/autonavigationhandler.cpp | 14 ++------- .../autonavigation/autonavigationhandler.h | 1 - .../autonavigation/autonavigationmodule.cpp | 18 ++++++++++- modules/autonavigation/autonavigationmodule.h | 3 ++ modules/autonavigation/instruction.cpp | 7 ++--- modules/autonavigation/waypoint.cpp | 31 ++++++++++--------- modules/autonavigation/waypoint.h | 10 +++--- 7 files changed, 46 insertions(+), 38 deletions(-) diff --git a/modules/autonavigation/autonavigationhandler.cpp b/modules/autonavigation/autonavigationhandler.cpp index 081e42de68..c1846593e6 100644 --- a/modules/autonavigation/autonavigationhandler.cpp +++ b/modules/autonavigation/autonavigationhandler.cpp @@ -43,13 +43,6 @@ namespace { constexpr const char* _loggerCat = "AutoNavigationHandler"; - constexpr const openspace::properties::Property::PropertyInfo MinimalBoundingSphereInfo = { - "MinimalBoundingSphere", - "Minimal BoundingSphere", - "The minimal allowed value for a bounding sphere. Used for computation of target " - "positions and path generation, to avoid issues when there is no bounding sphere." - }; - constexpr const openspace::properties::Property::PropertyInfo DefaultCurveOptionInfo = { "DefaultCurveOption", "Default Curve Option", @@ -75,13 +68,10 @@ namespace openspace::autonavigation { AutoNavigationHandler::AutoNavigationHandler() : properties::PropertyOwner({ "AutoNavigationHandler" }) - , _minAllowedBoundingSphere(MinimalBoundingSphereInfo, 10.0, 1.0, 3e10) , _defaultCurveOption(DefaultCurveOptionInfo, properties::OptionProperty::DisplayType::Dropdown) , _includeRoll(IncludeRollInfo, false) , _stopAtTargetsPerDefault(StopAtTargetsPerDefaultInfo, false) { - addProperty(_minAllowedBoundingSphere); - _defaultCurveOption.addOptions({ { CurveType::Bezier3, "Bezier3" }, { CurveType::Linear, "Linear"} @@ -174,7 +164,7 @@ void AutoNavigationHandler::createPath(PathSpecification& spec) { // Check if we have a specified start navigation state. If so, update first segment if (spec.hasStartState() && _pathSegments.size() > 0) { - Waypoint startState{ spec.startState() , _minAllowedBoundingSphere}; + Waypoint startState{ spec.startState() }; _pathSegments[0]->setStart(startState); } @@ -286,7 +276,7 @@ Waypoint AutoNavigationHandler::wayPointFromCamera() { glm::dvec3 pos = camera()->positionVec3(); glm::dquat rot = camera()->rotationQuaternion(); std::string node = global::navigationHandler.anchorNode()->identifier(); - return Waypoint{ pos, rot, node, _minAllowedBoundingSphere }; + return Waypoint{ pos, rot, node }; } Waypoint AutoNavigationHandler::lastWayPoint() { diff --git a/modules/autonavigation/autonavigationhandler.h b/modules/autonavigation/autonavigationhandler.h index 13076d7226..a2e22f715f 100644 --- a/modules/autonavigation/autonavigationhandler.h +++ b/modules/autonavigation/autonavigationhandler.h @@ -76,7 +76,6 @@ private: bool _isPlaying = false; unsigned int _currentSegmentIndex = 0; - properties::DoubleProperty _minAllowedBoundingSphere; properties::OptionProperty _defaultCurveOption; properties::BoolProperty _includeRoll; properties::BoolProperty _stopAtTargetsPerDefault; diff --git a/modules/autonavigation/autonavigationmodule.cpp b/modules/autonavigation/autonavigationmodule.cpp index 7e0cecc865..5ec7575bac 100644 --- a/modules/autonavigation/autonavigationmodule.cpp +++ b/modules/autonavigation/autonavigationmodule.cpp @@ -32,18 +32,34 @@ namespace { constexpr const char* _loggerCat = "AutoNavigationModule"; + + constexpr const openspace::properties::Property::PropertyInfo MinBoundingSphereInfo = { + "MinimalValidBoundingSphere", + "Minimal Valid Bounding Sphere", + "The minimal allowed value for a bounding sphere. Used for computation of target " + "positions and path generation, to avoid issues when there is no bounding sphere." + }; } // namespace namespace openspace { -AutoNavigationModule::AutoNavigationModule() : OpenSpaceModule(Name) { +AutoNavigationModule::AutoNavigationModule() + : OpenSpaceModule(Name), + _minValidBoundingSphere(MinBoundingSphereInfo, 10.0, 1.0, 3e10) +{ addPropertySubOwner(_autoNavigationHandler); + + addProperty(_minValidBoundingSphere); } autonavigation::AutoNavigationHandler& AutoNavigationModule::AutoNavigationHandler() { return _autoNavigationHandler; } +double AutoNavigationModule::minValidBoundingSphere() const { + return _minValidBoundingSphere; +} + std::vector AutoNavigationModule::documentations() const { return { // TODO: call documentation methods for the other classes in this module diff --git a/modules/autonavigation/autonavigationmodule.h b/modules/autonavigation/autonavigationmodule.h index c6914e7cba..ce7077bea7 100644 --- a/modules/autonavigation/autonavigationmodule.h +++ b/modules/autonavigation/autonavigationmodule.h @@ -40,6 +40,7 @@ public: virtual ~AutoNavigationModule() = default; autonavigation::AutoNavigationHandler& AutoNavigationHandler(); + double minValidBoundingSphere() const; std::vector documentations() const override; scripting::LuaLibrary luaLibrary() const override; @@ -48,6 +49,8 @@ private: void internalInitialize(const ghoul::Dictionary&) override; autonavigation::AutoNavigationHandler _autoNavigationHandler; + + properties::DoubleProperty _minValidBoundingSphere; }; } // namespace openspace diff --git a/modules/autonavigation/instruction.cpp b/modules/autonavigation/instruction.cpp index 0c2ee308aa..e14ca380e7 100644 --- a/modules/autonavigation/instruction.cpp +++ b/modules/autonavigation/instruction.cpp @@ -111,7 +111,7 @@ std::vector TargetNodeInstruction::getWaypoints() const { // OBS! TEMPORARY!! TODO: fix so that a camera pose is optional in Waypoint - const double radius = WaypointNodeDetails::findValidBoundingSphere(targetNode, 10.0); + const double radius = WaypointNodeDetails::findValidBoundingSphere(targetNode); targetPos = targetNode->worldPosition() + 3 * radius * glm::dvec3(1.0, 0.0, 0.0); } @@ -125,8 +125,7 @@ std::vector TargetNodeInstruction::getWaypoints() const { glm::dquat targetRot = glm::normalize(glm::inverse(glm::quat_cast(lookAtMat))); - Waypoint wp{ targetPos, targetRot, nodeIdentifier, 10.0 }; // TODO: make property for min valid boudnignsphere - + Waypoint wp{ targetPos, targetRot, nodeIdentifier }; return std::vector({ wp }); } @@ -147,7 +146,7 @@ NavigationStateInstruction::NavigationStateInstruction( } std::vector NavigationStateInstruction::getWaypoints() const { - Waypoint wp{ navigationState, 10.0 }; // TODO: make property for min valid boudnignsphere + Waypoint wp{ navigationState}; return std::vector({ wp }); } diff --git a/modules/autonavigation/waypoint.cpp b/modules/autonavigation/waypoint.cpp index 0ae0ab9944..78d8573465 100644 --- a/modules/autonavigation/waypoint.cpp +++ b/modules/autonavigation/waypoint.cpp @@ -24,6 +24,9 @@ #include +#include +#include +#include #include #include #include @@ -34,8 +37,7 @@ namespace { namespace openspace::autonavigation { -WaypointNodeDetails::WaypointNodeDetails(const std::string nodeIdentifier, - const double minBoundingSphere) +WaypointNodeDetails::WaypointNodeDetails(const std::string nodeIdentifier) { const SceneGraphNode* node = sceneGraphNode(nodeIdentifier); if (!node) { @@ -44,15 +46,17 @@ WaypointNodeDetails::WaypointNodeDetails(const std::string nodeIdentifier, } identifier = nodeIdentifier; - validBoundingSphere = findValidBoundingSphere(node, minBoundingSphere); + validBoundingSphere = findValidBoundingSphere(node); } -double WaypointNodeDetails::findValidBoundingSphere(const SceneGraphNode* node, - const double minBoundingSphere) +double WaypointNodeDetails::findValidBoundingSphere(const SceneGraphNode* node) { double bs = static_cast(node->boundingSphere()); - if (bs < minBoundingSphere) { + const double minValidBoundingSphere = + global::moduleEngine.module()->minValidBoundingSphere(); + + if (bs < minValidBoundingSphere) { // If the bs of the target is too small, try to find a good value in a child node. // Only check the closest children, to avoid deep traversal in the scene graph. Also, @@ -60,7 +64,7 @@ double WaypointNodeDetails::findValidBoundingSphere(const SceneGraphNode* node, // target well is higher for these nodes. for (SceneGraphNode* child : node->children()) { bs = static_cast(child->boundingSphere()); - if (bs > minBoundingSphere) { + if (bs > minValidBoundingSphere) { LWARNING(fmt::format( "The scene graph node '{}' has no, or a very small, bounding sphere. Using bounding sphere of child node '{}' in computations.", node->identifier(), @@ -72,23 +76,22 @@ double WaypointNodeDetails::findValidBoundingSphere(const SceneGraphNode* node, } LWARNING(fmt::format("The scene graph node '{}' has no, or a very small," - "bounding sphere. This might lead to unexpected results.", node->identifier())); + "bounding sphere. Using minimal value. This might lead to unexpected results.", node->identifier())); - bs = minBoundingSphere; + bs = minValidBoundingSphere; } return bs; } -Waypoint::Waypoint(const glm::dvec3& pos, const glm::dquat& rot, const std::string& ref, - const double minBoundingSphere) - : nodeDetails(ref, minBoundingSphere) +Waypoint::Waypoint(const glm::dvec3& pos, const glm::dquat& rot, const std::string& ref) + : nodeDetails(ref) { pose.position = pos; pose.rotation = rot; } -Waypoint::Waypoint(const NavigationState& ns, const double minBoundingSphere) { +Waypoint::Waypoint(const NavigationState& ns) { // OBS! The following code is exactly the same as used in // NavigationHandler::applyNavigationState. Should probably be made into a function. // TODO: make that function @@ -125,7 +128,7 @@ Waypoint::Waypoint(const NavigationState& ns, const double minBoundingSphere) { pose.rotation = neutralCameraRotation * yawRotation * pitchRotation; - nodeDetails = WaypointNodeDetails{ ns.anchor, minBoundingSphere }; + nodeDetails = WaypointNodeDetails{ ns.anchor }; } glm::dvec3 Waypoint::position() const { return pose.position; } diff --git a/modules/autonavigation/waypoint.h b/modules/autonavigation/waypoint.h index d2ed6ee77c..7d9717f1f2 100644 --- a/modules/autonavigation/waypoint.h +++ b/modules/autonavigation/waypoint.h @@ -39,10 +39,9 @@ struct CameraPose { // The waypoint node is the anchor or target node. struct WaypointNodeDetails { WaypointNodeDetails() = default; - WaypointNodeDetails(const std::string nodeIdentifier, const double minBoundingSphere); + WaypointNodeDetails(const std::string nodeIdentifier); - static double findValidBoundingSphere(const SceneGraphNode* node, - const double minBoundingSphere); + static double findValidBoundingSphere(const SceneGraphNode* node); std::string identifier; double validBoundingSphere; // to be able to handle nodes with faulty bounding spheres @@ -54,9 +53,8 @@ struct Waypoint { // TODO: create waypoints from a dictionary Waypoint() = default; - Waypoint(const glm::dvec3& pos, const glm::dquat& rot, const std::string& ref, - const double minBoundingSphere); - Waypoint(const NavigationState& ns, const double minBoundingSphere); + Waypoint(const glm::dvec3& pos, const glm::dquat& rot, const std::string& ref); + Waypoint(const NavigationState& ns); glm::dvec3 position() const; glm::dquat rotation() const; From 66fbd340455bae3b875b8c05ab9d1b48efb77c6f Mon Sep 17 00:00:00 2001 From: Emma Broman Date: Tue, 7 Apr 2020 10:17:16 +0200 Subject: [PATCH 094/912] Start refactoring pauses and do some cleanup --- .../autonavigation/autonavigationhandler.cpp | 134 +++++++++++++----- .../autonavigation/autonavigationhandler.h | 18 ++- modules/autonavigation/instruction.cpp | 19 ++- modules/autonavigation/instruction.h | 2 + modules/autonavigation/pathspecification.cpp | 4 +- 5 files changed, 139 insertions(+), 38 deletions(-) diff --git a/modules/autonavigation/autonavigationhandler.cpp b/modules/autonavigation/autonavigationhandler.cpp index c1846593e6..c0e20d8b5c 100644 --- a/modules/autonavigation/autonavigationhandler.cpp +++ b/modules/autonavigation/autonavigationhandler.cpp @@ -58,7 +58,7 @@ namespace { constexpr const openspace::properties::Property::PropertyInfo StopAtTargetsPerDefaultInfo = { "StopAtTargetsPerDefault", "Stop At Targets Per Default", - "Applied during path creation. If enabled, stops are automatically added between" + "Applied during path creation. If enabled, stops are automatically added between" " the path segments. The user must then choose to continue the apth after reaching a target" }; @@ -101,6 +101,11 @@ void AutoNavigationHandler::updateCamera(double deltaTime) { if (!_isPlaying || _pathSegments.empty()) return; + if (_activeStop) { + applyStopBehaviour(deltaTime); + return; + } + std::unique_ptr ¤tSegment = _pathSegments[_currentSegmentIndex]; CameraPose newPose = currentSegment->traversePath(deltaTime); @@ -114,16 +119,7 @@ void AutoNavigationHandler::updateCamera(double deltaTime) { } if (!_includeRoll) { - glm::dvec3 anchorPos = anchor()->worldPosition(); - const double notTooCloseDistance = deltaTime * glm::distance(anchorPos, newPose.position); - glm::dvec3 cameraDir = glm::normalize(newPose.rotation * Camera::ViewDirectionCameraSpace); - glm::dvec3 lookAtPos = newPose.position + notTooCloseDistance * cameraDir; - glm::dquat rollFreeRotation = helpers::getLookAtQuaternion( - newPose.position, - lookAtPos, - camera()->lookUpVectorWorldSpace() - ); - newPose.rotation = rollFreeRotation; + removeRollRotation(newPose, deltaTime); } camera()->setPositionVec3(newPose.position); @@ -138,8 +134,10 @@ void AutoNavigationHandler::updateCamera(double deltaTime) { return; } - if (_stopAtTargetsPerDefault) { - pausePath(); + int stopIndex = _currentSegmentIndex - 1; + + if (_stops[stopIndex].shouldStop) { + pauseAtTarget(stopIndex); return; } } @@ -148,17 +146,30 @@ void AutoNavigationHandler::updateCamera(double deltaTime) { void AutoNavigationHandler::createPath(PathSpecification& spec) { clearPath(); - if(spec.stopAtTargetsSpecified()) + if (spec.stopAtTargetsSpecified()) { _stopAtTargetsPerDefault = spec.stopAtTargets(); + LINFO("Property for stop at targets per default was overridden by path specification."); + } + + const int nrInstructions = spec.instructions()->size(); + + for (int i = 0; i < nrInstructions; i++) { + const Instruction* instruction = spec.instruction(i); + if (instruction) { + std::vector waypoints = instruction->getWaypoints(); + + if (waypoints.size() == 0) { + LWARNING(fmt::format("No path segment was created from instruction {}. No waypoints could be created.", i)); + return; + } - bool success = true; - for (int i = 0; i < spec.instructions()->size(); i++) { - const Instruction* ins = spec.instruction(i); - if (ins) { // TODO: allow for a list of waypoints - std::vector waypoints = ins->getWaypoints(); - if (waypoints.size() > 0) - addSegment(waypoints[0], ins); + addSegment(waypoints[0], instruction); + + // Add info about stops between segments + if (i < nrInstructions - 1) { + addStopDetails(instruction); + } } } @@ -168,12 +179,10 @@ void AutoNavigationHandler::createPath(PathSpecification& spec) { _pathSegments[0]->setStart(startState); } - if (success) { - LINFO("Succefully generated camera path."); - startPath(); - } - else - LERROR("Could not create path."); + LINFO(fmt::format( + "Succefully generated camera path with {} segments.", _pathSegments.size() + )); + startPath(); } void AutoNavigationHandler::clearPath() { @@ -188,6 +197,11 @@ void AutoNavigationHandler::startPath() { return; } + ghoul_assert( + _stops.size() == (_pathSegments.size() - 1), + "Must have exactly one stop entry between every segment." + ); + // TODO: remove this line at the end of our project. Used to simplify testing global::timeManager.setPause(true); @@ -199,15 +213,31 @@ void AutoNavigationHandler::startPath() { LINFO("Starting path..."); _isPlaying = true; + _activeStop = nullptr; } -void AutoNavigationHandler::pausePath() { - if (!_isPlaying) { +void AutoNavigationHandler::pauseAtTarget(int i) { + if (!_isPlaying || _activeStop) { LERROR("Cannot pause a path that isn't playing"); return; } - LINFO(fmt::format("Paused path at target {} / {}", _currentSegmentIndex, _pathSegments.size())); - _isPlaying = false; + + _activeStop = &_stops[i]; + + if (!_activeStop) return; + + bool hasDuration = _activeStop->duration.has_value(); + std::string infoString = hasDuration + ? fmt::format("{} seconds", _activeStop->duration.value()) + : "until continued"; + + LINFO(fmt::format("Paused path at target {} / {} ({})", + _currentSegmentIndex, + _pathSegments.size(), + infoString + )); + + _progressedTimeInStop = 0.0; } void AutoNavigationHandler::continuePath() { @@ -216,7 +246,7 @@ void AutoNavigationHandler::continuePath() { return; } - if (_isPlaying) { + if (_isPlaying && !_activeStop) { LERROR("Cannot resume a path that is already playing"); return; } @@ -225,7 +255,7 @@ void AutoNavigationHandler::continuePath() { // Recompute start camera state for the upcoming path segment, _pathSegments[_currentSegmentIndex]->setStart(wayPointFromCamera()); - _isPlaying = true; + _activeStop = nullptr; } void AutoNavigationHandler::stopPath() { @@ -283,6 +313,30 @@ Waypoint AutoNavigationHandler::lastWayPoint() { return _pathSegments.empty() ? wayPointFromCamera() : _pathSegments.back()->end(); } +void AutoNavigationHandler::removeRollRotation(CameraPose& pose, double deltaTime) { + glm::dvec3 anchorPos = anchor()->worldPosition(); + const double notTooCloseDistance = deltaTime * glm::distance(anchorPos, pose.position); + glm::dvec3 cameraDir = glm::normalize(pose.rotation * Camera::ViewDirectionCameraSpace); + glm::dvec3 lookAtPos = pose.position + notTooCloseDistance * cameraDir; + glm::dquat rollFreeRotation = helpers::getLookAtQuaternion( + pose.position, + lookAtPos, + camera()->lookUpVectorWorldSpace() + ); + pose.rotation = rollFreeRotation; +} + +void AutoNavigationHandler::applyStopBehaviour(double deltaTime) { + _progressedTimeInStop += deltaTime; + // TODO: Apply pause behaviour + + if (!_activeStop->duration.has_value()) return; + + if (_progressedTimeInStop >= _activeStop->duration.value()) { + continuePath(); + } +} + void AutoNavigationHandler::addSegment(Waypoint& waypoint, const Instruction* ins){ // TODO: Improve how curve types are handled const int curveType = _defaultCurveOption; @@ -296,4 +350,18 @@ void AutoNavigationHandler::addSegment(Waypoint& waypoint, const Instruction* in _pathSegments.push_back(std::unique_ptr(new PathSegment(segment))); } +void AutoNavigationHandler::addStopDetails(const Instruction* ins) { + StopDetails stopEntry{ _stopAtTargetsPerDefault }; + + if (ins->stopAtTarget.has_value()) { + stopEntry.shouldStop = ins->stopAtTarget.value(); + } + + if (stopEntry.shouldStop) { + stopEntry.duration = ins->stopDuration; + } + + _stops.push_back(stopEntry); +} + } // namespace openspace::autonavigation diff --git a/modules/autonavigation/autonavigationhandler.h b/modules/autonavigation/autonavigationhandler.h index a2e22f715f..63d3abc8fa 100644 --- a/modules/autonavigation/autonavigationhandler.h +++ b/modules/autonavigation/autonavigationhandler.h @@ -57,7 +57,7 @@ public: void createPath(PathSpecification& spec); void clearPath(); void startPath(); - void pausePath(); + void pauseAtTarget(int i); void continuePath(); void stopPath(); @@ -68,11 +68,27 @@ public: private: Waypoint wayPointFromCamera(); Waypoint lastWayPoint(); + void removeRollRotation(CameraPose& pose, double deltaTime); + + void applyStopBehaviour(double deltaTime); + void addSegment(Waypoint& waypoint, const Instruction* ins); + void addStopDetails(const Instruction* ins); // this list essentially represents the camera path std::vector> _pathSegments; + struct StopDetails { + bool shouldStop; + std::optional duration; + // TODO: behaviour + }; + + std::vector _stops; // 1 between every segment + + StopDetails* _activeStop = nullptr; + double _progressedTimeInStop = 0.0; + bool _isPlaying = false; unsigned int _currentSegmentIndex = 0; diff --git a/modules/autonavigation/instruction.cpp b/modules/autonavigation/instruction.cpp index e14ca380e7..d17e728bb0 100644 --- a/modules/autonavigation/instruction.cpp +++ b/modules/autonavigation/instruction.cpp @@ -35,8 +35,11 @@ namespace { constexpr const char* _loggerCat = "PathInstruction"; - constexpr const char* KeyTarget = "Target"; constexpr const char* KeyDuration = "Duration"; + constexpr const char* KeyStopAtTarget = "StopAtTarget"; + constexpr const char* KeyStopDetails = "StopDetails"; + + constexpr const char* KeyTarget = "Target"; constexpr const char* KeyPosition = "Position"; constexpr const char* KeyHeight = "Height"; @@ -51,6 +54,18 @@ Instruction::Instruction(const ghoul::Dictionary& dictionary) { } // TODO: include info about pauses/stops + if (dictionary.hasValue(KeyStopAtTarget)) { + stopAtTarget = dictionary.value(KeyStopAtTarget); + } + + if (dictionary.hasValue(KeyStopDetails)) { + ghoul::Dictionary stopDictionary = + dictionary.value(KeyStopDetails); + + if (stopDictionary.hasValue(KeyDuration)) { + stopDuration = stopDictionary.value(KeyDuration); + } + } } Instruction::~Instruction() {} @@ -146,7 +161,7 @@ NavigationStateInstruction::NavigationStateInstruction( } std::vector NavigationStateInstruction::getWaypoints() const { - Waypoint wp{ navigationState}; + Waypoint wp{ navigationState }; return std::vector({ wp }); } diff --git a/modules/autonavigation/instruction.h b/modules/autonavigation/instruction.h index abf12ac8d2..a768f3791d 100644 --- a/modules/autonavigation/instruction.h +++ b/modules/autonavigation/instruction.h @@ -44,6 +44,8 @@ struct Instruction { std::optional duration; // TODO: include pause information + std::optional stopAtTarget; + std::optional stopDuration; // only relevant is stopAtTarget true }; struct TargetNodeInstruction : public Instruction { diff --git a/modules/autonavigation/pathspecification.cpp b/modules/autonavigation/pathspecification.cpp index d0806d5e5e..f16077546e 100644 --- a/modules/autonavigation/pathspecification.cpp +++ b/modules/autonavigation/pathspecification.cpp @@ -150,11 +150,11 @@ documentation::Documentation PathSpecification::Documentation() { }; } +// create correct type of instruction and present and throw error with useful +// error message if we failed. void PathSpecification::tryReadInstruction(int index, std::string type, ghoul::Dictionary& dictionary) { - // create correct type of instruction and present and throw error with useful - // error message if we failed. if (type == KeyTypeTargetNode) { try { _instructions.push_back(std::make_unique(dictionary)); From 9d10abcff3330e9d7a33605a96ddf63011547846 Mon Sep 17 00:00:00 2001 From: Emma Broman Date: Tue, 7 Apr 2020 16:43:55 +0200 Subject: [PATCH 095/912] make sure that the stopAtTargets property is applied --- modules/autonavigation/autonavigationhandler.cpp | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/modules/autonavigation/autonavigationhandler.cpp b/modules/autonavigation/autonavigationhandler.cpp index c0e20d8b5c..ea9ad99115 100644 --- a/modules/autonavigation/autonavigationhandler.cpp +++ b/modules/autonavigation/autonavigationhandler.cpp @@ -351,7 +351,8 @@ void AutoNavigationHandler::addSegment(Waypoint& waypoint, const Instruction* in } void AutoNavigationHandler::addStopDetails(const Instruction* ins) { - StopDetails stopEntry{ _stopAtTargetsPerDefault }; + StopDetails stopEntry; + stopEntry.shouldStop = _stopAtTargetsPerDefault.value(); if (ins->stopAtTarget.has_value()) { stopEntry.shouldStop = ins->stopAtTarget.value(); From a9005cdcbb4a1d1478a045fe24aab334a2da3c94 Mon Sep 17 00:00:00 2001 From: Emma Broman Date: Wed, 8 Apr 2020 16:27:10 +0200 Subject: [PATCH 096/912] Started implementing pause/stop behaviors --- modules/autonavigation/CMakeLists.txt | 2 + modules/autonavigation/atnodenavigator.cpp | 113 ++++++++++++++++++ modules/autonavigation/atnodenavigator.h | 61 ++++++++++ .../autonavigation/autonavigationhandler.cpp | 92 +++++++++----- .../autonavigation/autonavigationhandler.h | 11 +- .../autonavigationmodule_lua.inl | 2 +- modules/autonavigation/instruction.cpp | 5 + modules/autonavigation/instruction.h | 6 +- modules/autonavigation/pathcurves.cpp | 12 -- modules/autonavigation/pathcurves.h | 9 -- modules/autonavigation/pathsegment.cpp | 3 - 11 files changed, 254 insertions(+), 62 deletions(-) create mode 100644 modules/autonavigation/atnodenavigator.cpp create mode 100644 modules/autonavigation/atnodenavigator.h diff --git a/modules/autonavigation/CMakeLists.txt b/modules/autonavigation/CMakeLists.txt index df7a1321f9..7384b72e14 100644 --- a/modules/autonavigation/CMakeLists.txt +++ b/modules/autonavigation/CMakeLists.txt @@ -25,6 +25,7 @@ include(${OPENSPACE_CMAKE_EXT_DIR}/module_definition.cmake) set(HEADER_FILES + ${CMAKE_CURRENT_SOURCE_DIR}/atnodenavigator.h ${CMAKE_CURRENT_SOURCE_DIR}/autonavigationmodule.h ${CMAKE_CURRENT_SOURCE_DIR}/autonavigationhandler.h ${CMAKE_CURRENT_SOURCE_DIR}/helperfunctions.h @@ -38,6 +39,7 @@ set(HEADER_FILES source_group("Header Files" FILES ${HEADER_FILES}) set(SOURCE_FILES + ${CMAKE_CURRENT_SOURCE_DIR}/atnodenavigator.cpp ${CMAKE_CURRENT_SOURCE_DIR}/autonavigationmodule.cpp ${CMAKE_CURRENT_SOURCE_DIR}/autonavigationmodule_lua.inl ${CMAKE_CURRENT_SOURCE_DIR}/autonavigationhandler.cpp diff --git a/modules/autonavigation/atnodenavigator.cpp b/modules/autonavigation/atnodenavigator.cpp new file mode 100644 index 0000000000..6919677727 --- /dev/null +++ b/modules/autonavigation/atnodenavigator.cpp @@ -0,0 +1,113 @@ +/***************************************************************************************** + * * + * OpenSpace * + * * + * Copyright (c) 2014-2019 * + * * + * Permission is hereby granted, free of charge, to any person obtaining a copy of this * + * software and associated documentation files (the "Software"), to deal in the Software * + * without restriction, including without limitation the rights to use, copy, modify, * + * merge, publish, distribute, sublicense, and/or sell copies of the Software, and to * + * permit persons to whom the Software is furnished to do so, subject to the following * + * conditions: * + * * + * The above copyright notice and this permission notice shall be included in all copies * + * or substantial portions of the Software. * + * * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, * + * INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A * + * PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT * + * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF * + * CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE * + * OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. * + ****************************************************************************************/ + +#include + +#include +#include +#include +#include +#include +#include + +namespace { + constexpr const char* _loggerCat = "AtAnchorNavigator"; + + constexpr const openspace::properties::Property::PropertyInfo OrbitSpeedFactorInfo = { + "OrbitSpeedFactor", + "Orbit Speed Factor", + "Controls the speed of the orbiting around an anchor." + }; +} // namespace + +namespace openspace::autonavigation { + +AtNodeNavigator::AtNodeNavigator() + : properties::PropertyOwner({ "AtAnchorNavigator" }) + , _orbitSpeedFactor(OrbitSpeedFactorInfo, 0.5, 0.0, 20.0) +{ + addProperty(_orbitSpeedFactor); + _behavior = Behavior::None; +} + +AtNodeNavigator::~AtNodeNavigator() {} // NOLINT + +void AtNodeNavigator::setNode(std::string identifier) { + SceneGraphNode* node = sceneGraphNode(identifier); + if (!node) { + LERROR(fmt::format( + "Could not find scene graph node '{}' to navigate in relation to.", identifier + )); + return; + } + _node = node; +} + +void AtNodeNavigator::setBehavior(Behavior behavior) { + _behavior = behavior; +} + +void AtNodeNavigator::updateCamera(double deltaTime) { + switch (_behavior) + { + case Behavior::Orbit: + orbitNode(deltaTime); + break; + case Behavior::None: + // Do nothing + break; + default: + LERROR("Unknown behaviour type!"); + break; + } +} + +Camera* AtNodeNavigator::camera() const { + return global::navigationHandler.camera(); +} + +// Move along the right vector for the camera, while looking at the center of the node +void AtNodeNavigator::orbitNode(double deltaTime) { + ghoul_assert(_node != nullptr, "Node to orbit must be set!") + glm::dvec3 prevPosition = camera()->positionVec3(); + glm::dquat prevRotation = camera()->rotationQuaternion(); + + glm::dvec3 nodeCenter = _node->worldPosition(); + double distanceToNode = glm::distance(prevPosition, nodeCenter) - _node->boundingSphere(); + double orbitSpeed = distanceToNode * 0.1 * _orbitSpeedFactor; + + const glm::dvec3 up = camera()->lookUpVectorWorldSpace(); + glm::dvec3 forward = prevRotation * glm::dvec3(0.0, 0.0, -1.0); + glm::dvec3 right = glm::normalize(glm::cross(forward, up)); + + glm::dvec3 newPosition = prevPosition + orbitSpeed * deltaTime * right; + + glm::dmat4 lookAtMat = glm::lookAt(newPosition, nodeCenter, up); + glm::dquat newRotation = glm::normalize(glm::inverse(glm::quat_cast(lookAtMat))); + + camera()->setPositionVec3(newPosition); + camera()->setRotation(newRotation); +} + +} // namespace openspace::autonavigation diff --git a/modules/autonavigation/atnodenavigator.h b/modules/autonavigation/atnodenavigator.h new file mode 100644 index 0000000000..96e3cfad70 --- /dev/null +++ b/modules/autonavigation/atnodenavigator.h @@ -0,0 +1,61 @@ +/***************************************************************************************** + * * + * OpenSpace * + * * + * Copyright (c) 2014-2019 * + * * + * Permission is hereby granted, free of charge, to any person obtaining a copy of this * + * software and associated documentation files (the "Software"), to deal in the Software * + * without restriction, including without limitation the rights to use, copy, modify, * + * merge, publish, distribute, sublicense, and/or sell copies of the Software, and to * + * permit persons to whom the Software is furnished to do so, subject to the following * + * conditions: * + * * + * The above copyright notice and this permission notice shall be included in all copies * + * or substantial portions of the Software. * + * * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, * + * INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A * + * PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT * + * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF * + * CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE * + * OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. * + ****************************************************************************************/ + +#ifndef __OPENSPACE_MODULE___ATNODENAVIGATOR___H__ +#define __OPENSPACE_MODULE___ATNODENAVIGATOR___H__ + +#include + +namespace openspace::autonavigation { + +class AtNodeNavigator : public properties::PropertyOwner { +public: + enum Behavior { + Orbit, + None + }; + + AtNodeNavigator(); + ~AtNodeNavigator(); + + void setNode(std::string identifier); + void setBehavior(Behavior behavior); + + void updateCamera(double deltaTime); + +private: + Camera* camera() const; + + //Behaviors + void orbitNode(double deltaTime); + + SceneGraphNode* _node; + Behavior _behavior; + + properties::DoubleProperty _orbitSpeedFactor; +}; + +} // namespace openspace::autonavigation + +#endif // __OPENSPACE_MODULE___ATNODENAVIGATOR___H__ diff --git a/modules/autonavigation/autonavigationhandler.cpp b/modules/autonavigation/autonavigationhandler.cpp index ea9ad99115..f6d5753b33 100644 --- a/modules/autonavigation/autonavigationhandler.cpp +++ b/modules/autonavigation/autonavigationhandler.cpp @@ -34,7 +34,6 @@ #include #include #include -#include #include #include #include @@ -59,7 +58,13 @@ namespace { "StopAtTargetsPerDefault", "Stop At Targets Per Default", "Applied during path creation. If enabled, stops are automatically added between" - " the path segments. The user must then choose to continue the apth after reaching a target" + " the path segments. The user must then choose to continue the path after reaching a target" + }; + + constexpr const openspace::properties::Property::PropertyInfo DefaultStopBehaviorInfo = { + "DefaultStopBehavior", + "Default Stop Behavior", + "The default camera behavior that is applied when the camera reaches and stops at a target." }; } // namespace @@ -71,14 +76,24 @@ AutoNavigationHandler::AutoNavigationHandler() , _defaultCurveOption(DefaultCurveOptionInfo, properties::OptionProperty::DisplayType::Dropdown) , _includeRoll(IncludeRollInfo, false) , _stopAtTargetsPerDefault(StopAtTargetsPerDefaultInfo, false) + , _defaultStopBehavior(DefaultStopBehaviorInfo, properties::OptionProperty::DisplayType::Dropdown) { + addPropertySubOwner(_atNodeNavigator); + _defaultCurveOption.addOptions({ { CurveType::Bezier3, "Bezier3" }, { CurveType::Linear, "Linear"} }); addProperty(_defaultCurveOption); + addProperty(_includeRoll); addProperty(_stopAtTargetsPerDefault); + + _defaultStopBehavior.addOptions({ + { AtNodeNavigator::Behavior::None, "None" }, + { AtNodeNavigator::Behavior::Orbit, "Orbit" }, + }); + addProperty(_defaultStopBehavior); } AutoNavigationHandler::~AutoNavigationHandler() {} // NOLINT @@ -151,7 +166,7 @@ void AutoNavigationHandler::createPath(PathSpecification& spec) { LINFO("Property for stop at targets per default was overridden by path specification."); } - const int nrInstructions = spec.instructions()->size(); + const int nrInstructions = (int)spec.instructions()->size(); for (int i = 0; i < nrInstructions; i++) { const Instruction* instruction = spec.instruction(i); @@ -168,7 +183,7 @@ void AutoNavigationHandler::createPath(PathSpecification& spec) { // Add info about stops between segments if (i < nrInstructions - 1) { - addStopDetails(instruction); + addStopDetails(waypoints.back(), instruction); } } } @@ -188,6 +203,7 @@ void AutoNavigationHandler::createPath(PathSpecification& spec) { void AutoNavigationHandler::clearPath() { LINFO("Clearing path..."); _pathSegments.clear(); + _stops.clear(); _currentSegmentIndex = 0; } @@ -216,30 +232,6 @@ void AutoNavigationHandler::startPath() { _activeStop = nullptr; } -void AutoNavigationHandler::pauseAtTarget(int i) { - if (!_isPlaying || _activeStop) { - LERROR("Cannot pause a path that isn't playing"); - return; - } - - _activeStop = &_stops[i]; - - if (!_activeStop) return; - - bool hasDuration = _activeStop->duration.has_value(); - std::string infoString = hasDuration - ? fmt::format("{} seconds", _activeStop->duration.value()) - : "until continued"; - - LINFO(fmt::format("Paused path at target {} / {} ({})", - _currentSegmentIndex, - _pathSegments.size(), - infoString - )); - - _progressedTimeInStop = 0.0; -} - void AutoNavigationHandler::continuePath() { if (_pathSegments.empty() || hasFinished()) { LERROR("No path to resume (path is empty or has finished)."); @@ -258,7 +250,7 @@ void AutoNavigationHandler::continuePath() { _activeStop = nullptr; } -void AutoNavigationHandler::stopPath() { +void AutoNavigationHandler::abortPath() { _isPlaying = false; } @@ -326,9 +318,41 @@ void AutoNavigationHandler::removeRollRotation(CameraPose& pose, double deltaTim pose.rotation = rollFreeRotation; } +void AutoNavigationHandler::pauseAtTarget(int i) { + if (!_isPlaying || _activeStop) { + LERROR("Cannot pause a path that isn't playing"); + return; + } + + if (i < 0 || i > _stops.size() - 1) { + LERROR("Invalid target number: " + std::to_string(i)); + return; + } + + _activeStop = &_stops[i]; + + if (!_activeStop) return; + + _atNodeNavigator.setBehavior(_activeStop->behavior); + _atNodeNavigator.setNode(_pathSegments[i]->end().nodeDetails.identifier); + + bool hasDuration = _activeStop->duration.has_value(); + + std::string infoString = hasDuration ? + fmt::format("{} seconds", _activeStop->duration.value()) : "until continued"; + + LINFO(fmt::format("Paused path at target {} / {} ({})", + _currentSegmentIndex, + _pathSegments.size(), + infoString + )); + + _progressedTimeInStop = 0.0; +} + void AutoNavigationHandler::applyStopBehaviour(double deltaTime) { _progressedTimeInStop += deltaTime; - // TODO: Apply pause behaviour + _atNodeNavigator.updateCamera(deltaTime); if (!_activeStop->duration.has_value()) return; @@ -350,7 +374,7 @@ void AutoNavigationHandler::addSegment(Waypoint& waypoint, const Instruction* in _pathSegments.push_back(std::unique_ptr(new PathSegment(segment))); } -void AutoNavigationHandler::addStopDetails(const Instruction* ins) { +void AutoNavigationHandler::addStopDetails(Waypoint& endWaypoint, const Instruction* ins) { StopDetails stopEntry; stopEntry.shouldStop = _stopAtTargetsPerDefault.value(); @@ -360,6 +384,12 @@ void AutoNavigationHandler::addStopDetails(const Instruction* ins) { if (stopEntry.shouldStop) { stopEntry.duration = ins->stopDuration; + + std::string anchorIdentifier = endWaypoint.nodeDetails.identifier; + stopEntry.behavior = AtNodeNavigator::Behavior(_defaultStopBehavior.value()); // OBS! Will this work if they're not in the same order?? + if (ins->stopBehavior.has_value()) { + // TODO: + } } _stops.push_back(stopEntry); diff --git a/modules/autonavigation/autonavigationhandler.h b/modules/autonavigation/autonavigationhandler.h index 63d3abc8fa..9e019920d5 100644 --- a/modules/autonavigation/autonavigationhandler.h +++ b/modules/autonavigation/autonavigationhandler.h @@ -25,6 +25,7 @@ #ifndef __OPENSPACE_MODULE___AUTONAVIGATIONHANDLER___H__ #define __OPENSPACE_MODULE___AUTONAVIGATIONHANDLER___H__ +#include #include #include #include @@ -57,9 +58,8 @@ public: void createPath(PathSpecification& spec); void clearPath(); void startPath(); - void pauseAtTarget(int i); void continuePath(); - void stopPath(); + void abortPath(); // TODO: remove functions for debugging std::vector getCurvePositions(int nPerSegment); //debug @@ -69,11 +69,12 @@ private: Waypoint wayPointFromCamera(); Waypoint lastWayPoint(); void removeRollRotation(CameraPose& pose, double deltaTime); + void pauseAtTarget(int i); void applyStopBehaviour(double deltaTime); void addSegment(Waypoint& waypoint, const Instruction* ins); - void addStopDetails(const Instruction* ins); + void addStopDetails(Waypoint& endWaypoint, const Instruction* ins); // this list essentially represents the camera path std::vector> _pathSegments; @@ -81,11 +82,12 @@ private: struct StopDetails { bool shouldStop; std::optional duration; - // TODO: behaviour + AtNodeNavigator::Behavior behavior; }; std::vector _stops; // 1 between every segment + AtNodeNavigator _atNodeNavigator; // responsible for navigation during stops StopDetails* _activeStop = nullptr; double _progressedTimeInStop = 0.0; @@ -95,6 +97,7 @@ private: properties::OptionProperty _defaultCurveOption; properties::BoolProperty _includeRoll; properties::BoolProperty _stopAtTargetsPerDefault; + properties::OptionProperty _defaultStopBehavior; }; } // namespace openspace::autonavigation diff --git a/modules/autonavigation/autonavigationmodule_lua.inl b/modules/autonavigation/autonavigationmodule_lua.inl index b2282c8e6f..9e7319c966 100644 --- a/modules/autonavigation/autonavigationmodule_lua.inl +++ b/modules/autonavigation/autonavigationmodule_lua.inl @@ -56,7 +56,7 @@ namespace openspace::autonavigation::luascriptfunctions { AutoNavigationModule* module = global::moduleEngine.module(); AutoNavigationHandler& handler = module->AutoNavigationHandler(); - handler.stopPath(); + handler.abortPath(); return 0; } diff --git a/modules/autonavigation/instruction.cpp b/modules/autonavigation/instruction.cpp index d17e728bb0..e9ff070c54 100644 --- a/modules/autonavigation/instruction.cpp +++ b/modules/autonavigation/instruction.cpp @@ -38,6 +38,7 @@ namespace { constexpr const char* KeyDuration = "Duration"; constexpr const char* KeyStopAtTarget = "StopAtTarget"; constexpr const char* KeyStopDetails = "StopDetails"; + constexpr const char* KeyBehavior = "Behavior"; constexpr const char* KeyTarget = "Target"; constexpr const char* KeyPosition = "Position"; @@ -65,6 +66,10 @@ Instruction::Instruction(const ghoul::Dictionary& dictionary) { if (stopDictionary.hasValue(KeyDuration)) { stopDuration = stopDictionary.value(KeyDuration); } + + if (stopDictionary.hasValue(KeyBehavior)) { + stopBehavior = stopDictionary.value(KeyBehavior); + } } } diff --git a/modules/autonavigation/instruction.h b/modules/autonavigation/instruction.h index a768f3791d..ce7f7ff615 100644 --- a/modules/autonavigation/instruction.h +++ b/modules/autonavigation/instruction.h @@ -43,9 +43,11 @@ struct Instruction { std::optional duration; - // TODO: include pause information std::optional stopAtTarget; - std::optional stopDuration; // only relevant is stopAtTarget true + + // only relevant is stopAtTarget true + std::optional stopDuration; + std::optional stopBehavior; }; struct TargetNodeInstruction : public Instruction { diff --git a/modules/autonavigation/pathcurves.cpp b/modules/autonavigation/pathcurves.cpp index c06a19dc44..e13e1abb7f 100644 --- a/modules/autonavigation/pathcurves.cpp +++ b/modules/autonavigation/pathcurves.cpp @@ -227,16 +227,4 @@ glm::dvec3 LinearCurve::positionAt(double u) { return interpolation::linear(u, _points[0], _points[1]); } -// TODO: Iprove handling of pauses -PauseCurve::PauseCurve(const Waypoint& waypoint) { - _points.push_back(waypoint.position()); - _length = 1.0; // OBS! Length of a pause curve makes no sense, but it also doesn't matter - _rotationInterpolator = RotationInterpolator{ waypoint, waypoint, this, Fixed }; -} - -glm::dvec3 PauseCurve::positionAt(double u) { - ghoul_assert(u >= 0 && u <= 1.0, "Interpolation variable out of range [0, 1]"); - return _points[0]; -} - } // namespace openspace::autonavigation diff --git a/modules/autonavigation/pathcurves.h b/modules/autonavigation/pathcurves.h index 8bb7c88167..d0fac7ac11 100644 --- a/modules/autonavigation/pathcurves.h +++ b/modules/autonavigation/pathcurves.h @@ -35,7 +35,6 @@ namespace openspace::autonavigation { enum CurveType { Bezier3, Linear, - Pause // OBS! Temporary special case for handling pauses }; class PathCurve { @@ -76,14 +75,6 @@ public: glm::dvec3 positionAt(double u); }; -// OBS! This is a temporary class specialised for handling pauses. -// TODO: handle better in the future. -class PauseCurve : public PathCurve { -public: - PauseCurve(const Waypoint& state); - glm::dvec3 positionAt(double u); -}; - } // namespace openspace::autonavigation #endif // __OPENSPACE_MODULE_AUTONAVIGATION___PATHCURVE___H__ diff --git a/modules/autonavigation/pathsegment.cpp b/modules/autonavigation/pathsegment.cpp index 5bcbc2e642..97e146d45a 100644 --- a/modules/autonavigation/pathsegment.cpp +++ b/modules/autonavigation/pathsegment.cpp @@ -143,9 +143,6 @@ void PathSegment::initCurve() { case CurveType::Linear: _curve = std::make_shared(_start, _end); break; - case CurveType::Pause: - _curve = std::make_shared(_start); - break; default: LERROR("Could not create curve. Type does not exist!"); return; From a13ec5971476500374495f3316adbdf9ea129220 Mon Sep 17 00:00:00 2001 From: Emma Broman Date: Wed, 8 Apr 2020 19:38:28 +0200 Subject: [PATCH 097/912] Allow instructions to change the stop behavior --- modules/autonavigation/atnodenavigator.cpp | 6 ++-- modules/autonavigation/atnodenavigator.h | 2 +- .../autonavigation/autonavigationhandler.cpp | 31 +++++++++++++++++-- 3 files changed, 32 insertions(+), 7 deletions(-) diff --git a/modules/autonavigation/atnodenavigator.cpp b/modules/autonavigation/atnodenavigator.cpp index 6919677727..1ae223fba0 100644 --- a/modules/autonavigation/atnodenavigator.cpp +++ b/modules/autonavigation/atnodenavigator.cpp @@ -32,7 +32,7 @@ #include namespace { - constexpr const char* _loggerCat = "AtAnchorNavigator"; + constexpr const char* _loggerCat = "AtNodeNavigator"; constexpr const openspace::properties::Property::PropertyInfo OrbitSpeedFactorInfo = { "OrbitSpeedFactor", @@ -44,7 +44,7 @@ namespace { namespace openspace::autonavigation { AtNodeNavigator::AtNodeNavigator() - : properties::PropertyOwner({ "AtAnchorNavigator" }) + : properties::PropertyOwner({ "AtNodeNavigator" }) , _orbitSpeedFactor(OrbitSpeedFactorInfo, 0.5, 0.0, 20.0) { addProperty(_orbitSpeedFactor); @@ -78,7 +78,7 @@ void AtNodeNavigator::updateCamera(double deltaTime) { // Do nothing break; default: - LERROR("Unknown behaviour type!"); + LERROR("Behavior type not implemented."); break; } } diff --git a/modules/autonavigation/atnodenavigator.h b/modules/autonavigation/atnodenavigator.h index 96e3cfad70..761e6fd7da 100644 --- a/modules/autonavigation/atnodenavigator.h +++ b/modules/autonavigation/atnodenavigator.h @@ -32,8 +32,8 @@ namespace openspace::autonavigation { class AtNodeNavigator : public properties::PropertyOwner { public: enum Behavior { + None = 0, Orbit, - None }; AtNodeNavigator(); diff --git a/modules/autonavigation/autonavigationhandler.cpp b/modules/autonavigation/autonavigationhandler.cpp index f6d5753b33..fe999966c9 100644 --- a/modules/autonavigation/autonavigationhandler.cpp +++ b/modules/autonavigation/autonavigationhandler.cpp @@ -89,10 +89,12 @@ AutoNavigationHandler::AutoNavigationHandler() addProperty(_includeRoll); addProperty(_stopAtTargetsPerDefault); + // Must be listed in the same order as in enum definition _defaultStopBehavior.addOptions({ { AtNodeNavigator::Behavior::None, "None" }, - { AtNodeNavigator::Behavior::Orbit, "Orbit" }, + { AtNodeNavigator::Behavior::Orbit, "Orbit" } }); + _defaultStopBehavior = AtNodeNavigator::Behavior::None; addProperty(_defaultStopBehavior); } @@ -386,9 +388,32 @@ void AutoNavigationHandler::addStopDetails(Waypoint& endWaypoint, const Instruct stopEntry.duration = ins->stopDuration; std::string anchorIdentifier = endWaypoint.nodeDetails.identifier; - stopEntry.behavior = AtNodeNavigator::Behavior(_defaultStopBehavior.value()); // OBS! Will this work if they're not in the same order?? + stopEntry.behavior = AtNodeNavigator::Behavior(_defaultStopBehavior.value()); + if (ins->stopBehavior.has_value()) { - // TODO: + std::string behaviorString = ins->stopBehavior.value(); + + // This is a bit ugly, since it relies on the OptionProperty::Option and + // AtNodeNavigator::Behavior being implicitly converted to the same int value. + // TODO: Come up with a nicer solution (this get to work for now...) + + using Option = properties::OptionProperty::Option; + std::vector