mirror of
https://github.com/OpenSpace/OpenSpace.git
synced 2026-03-14 17:40:26 -05:00
Add code for different path segments from Ingelas old branch
This commit is contained in:
@@ -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})
|
||||
|
||||
|
||||
@@ -24,7 +24,7 @@
|
||||
|
||||
#include <modules/autonavigation/autonavigationhandler.h>
|
||||
|
||||
#include <modules/autonavigation/transferfunctions.h>
|
||||
#include <modules/autonavigation/helperfunctions.h>
|
||||
#include <openspace/engine/globals.h>
|
||||
#include <openspace/engine/windowdelegate.h>
|
||||
#include <openspace/interaction/navigationhandler.h>
|
||||
@@ -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<double> height)
|
||||
{
|
||||
glm::dvec3 targetPos = node->worldPosition();
|
||||
glm::dvec3 targetToPrevVector = prevPos - targetPos;
|
||||
|
||||
double radius = static_cast<double>(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<double>(node->boundingSphere());
|
||||
|
||||
// move target position out from surface, along vector to camera
|
||||
targetPos += glm::normalize(targetToPrevVector) * (radius + height);
|
||||
|
||||
return targetPos;
|
||||
}
|
||||
|
||||
} // namespace openspace::autonavigation
|
||||
|
||||
@@ -25,6 +25,7 @@
|
||||
#ifndef __OPENSPACE_MODULE___AUTONAVIGATIONHANDLER___H__
|
||||
#define __OPENSPACE_MODULE___AUTONAVIGATIONHANDLER___H__
|
||||
|
||||
#include <modules/autonavigation/pathsegment.h>
|
||||
#include <modules/autonavigation/pathspecification.h>
|
||||
#include <openspace/interaction/interpolator.h>
|
||||
#include <openspace/properties/propertyowner.h>
|
||||
@@ -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<double> 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<PathSegment> _pathSegments;
|
||||
|
||||
double _pathDuration;
|
||||
|
||||
134
modules/autonavigation/helperfunctions.cpp
Normal file
134
modules/autonavigation/helperfunctions.cpp
Normal file
@@ -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 <modules/autonavigation/helperfunctions.h>
|
||||
|
||||
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<glm::dvec3> &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<glm::dvec3> &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
|
||||
|
||||
75
modules/autonavigation/helperfunctions.h
Normal file
75
modules/autonavigation/helperfunctions.h
Normal file
@@ -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 <ghoul/glm.h>
|
||||
#include <ghoul/misc/assert.h>
|
||||
#include <ghoul/logging/logmanager.h>
|
||||
#include <vector>
|
||||
#include <cmath>
|
||||
|
||||
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<glm::dvec3> &controlPoints);
|
||||
|
||||
glm::dvec3 piecewiseLinear(double t, const std::vector<glm::dvec3> &controlPoints);
|
||||
|
||||
} // namespace
|
||||
#endif
|
||||
186
modules/autonavigation/pathsegment.cpp
Normal file
186
modules/autonavigation/pathsegment.cpp
Normal file
@@ -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 <modules/autonavigation/pathsegment.h>
|
||||
|
||||
#include <modules/autonavigation/helperfunctions.h>
|
||||
#include <openspace/engine/globals.h>
|
||||
#include <openspace/interaction/navigationhandler.h>
|
||||
#include <openspace/query/query.h>
|
||||
#include <openspace/scene/scenegraphnode.h>
|
||||
#include <openspace/util/camera.h>
|
||||
#include <ghoul/Misc/interpolator.h>
|
||||
#include <ghoul/logging/logmanager.h>
|
||||
|
||||
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
|
||||
74
modules/autonavigation/pathsegment.h
Normal file
74
modules/autonavigation/pathsegment.h
Normal file
@@ -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 <ghoul/glm.h>
|
||||
#include <vector>
|
||||
|
||||
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<glm::dvec3> _controlPoints;
|
||||
};
|
||||
|
||||
} // namespace openspace::autonavigation
|
||||
|
||||
#endif // __OPENSPACE_MODULE___PATHSEGMENT___H__
|
||||
Reference in New Issue
Block a user