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__