Add code for camera path generation module

Add all code in the state it was in Jan 10 2020 when branch cleanup was performed
This commit is contained in:
Emma Broman
2020-01-10 14:57:48 -05:00
parent d125694062
commit 3e283d1630
11 changed files with 1192 additions and 0 deletions

View File

@@ -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}
)

View File

@@ -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 <modules/autonavigation/autonavigationhandler.h>
#include <modules/autonavigation/transferfunctions.h>
#include <openspace/engine/globals.h>
#include <openspace/engine/windowdelegate.h>
#include <openspace/interaction/navigationhandler.h>
#include <openspace/scene/scenegraphnode.h>
#include <openspace/util/camera.h>
#include <openspace/query/query.h>
#include <ghoul/logging/logmanager.h>
#include <glm/gtx/vector_angle.hpp>
#include <glm/gtx/quaternion.hpp>
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<TargetNodeInstructionProps*>(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<NavigationStateInstructionProps*>(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<double>(node->boundingSphere());
// move target position out from surface, along vector to camera
targetPos += glm::normalize(targetToPrevVector) * (radius + height);
return targetPos;
}
} // namespace openspace::autonavigation

View File

@@ -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 <modules/autonavigation/pathspecification.h>
#include <openspace/interaction/interpolator.h>
#include <openspace/properties/propertyowner.h>
#include <openspace/scene/scenegraphnode.h>
#include <ghoul/glm.h>
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<PathSegment> _pathSegments;
double _pathDuration;
double _currentTime;
bool _isPlaying = false;
};
} // namespace openspace::autonavigation
#endif // __OPENSPACE_CORE___NAVIGATIONHANDLER___H__

View File

@@ -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 <modules/autonavigation/autonavigationmodule.h>
#include <modules/autonavigation/autonavigationmodule_lua.inl>
#include <openspace/engine/globals.h>
#include <openspace/engine/globalscallbacks.h>
#include <openspace/engine/windowdelegate.h>
#include <ghoul/logging/logmanager.h>
namespace {
constexpr const char* _loggerCat = "AutoNavigationModule";
} // namespace
namespace openspace {
AutoNavigationModule::AutoNavigationModule() : OpenSpaceModule(Name) {}
autonavigation::AutoNavigationHandler& AutoNavigationModule::AutoNavigationHandler() {
return _autoNavigationHandler;
}
std::vector<documentation::Documentation> 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

View File

@@ -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 <openspace/util/openspacemodule.h>
#include <openspace/documentation/documentation.h>
#include <modules/autonavigation/autonavigationhandler.h>
namespace openspace {
class AutoNavigationModule : public OpenSpaceModule {
public:
constexpr static const char* Name = "AutoNavigation";
AutoNavigationModule();
virtual ~AutoNavigationModule() = default;
autonavigation::AutoNavigationHandler& AutoNavigationHandler();
std::vector<documentation::Documentation> 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__

View File

@@ -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 <modules/autonavigation/pathspecification.h>
#include <openspace/engine/globals.h>
#include <openspace/engine/moduleengine.h>
#include <openspace/interaction/navigationhandler.h>
#include <openspace/scene/scenegraphnode.h>
#include <openspace/scripting/lualibrary.h>
#include <openspace/util/camera.h>
#include <openspace/util/updatestructures.h>
#include <openspace/query/query.h>
#include <ghoul/filesystem/file.h>
#include <ghoul/filesystem/filesystem.h>
#include <ghoul/logging/logmanager.h>
#include <glm/gtx/vector_angle.hpp>
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<std::string>(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<double>(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<AutoNavigationModule>();
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<AutoNavigationModule>();
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<std::string>(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<AutoNavigationModule>();
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

View File

@@ -0,0 +1,5 @@
set(DEFAULT_MODULE ON)
set(OPENSPACE_DEPENDENCIES
#add possible other modules that this module depends upon
)

View File

@@ -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 <modules/autonavigation/pathspecification.h>
#include <openspace/documentation/verifier.h>
#include <ghoul/logging/logmanager.h>
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<double>,
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<double>(KeyDuration)) {
duration = dictionary.value<double>(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<std::string>(KeyTarget)) {
throw ghoul::RuntimeError(
"A camera path instruction requires a target node, to go to or use as reference frame."
);
}
targetNode = dictionary.value<std::string>(KeyTarget);
if (dictionary.hasValue<glm::dvec3>(KeyPosition)) {
position = dictionary.value<glm::dvec3>(KeyPosition);
}
if (dictionary.hasValue<double>(KeyHeight)) {
height = dictionary.value<double>(KeyHeight);
}
}
NavigationStateInstructionProps::NavigationStateInstructionProps(
const ghoul::Dictionary& dictionary) : InstructionProps(dictionary)
{
if (dictionary.hasValue<ghoul::Dictionary>(KeyNavigationState)) {
auto navStateDict = dictionary.value<ghoul::Dictionary>(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<std::string>(KeyTarget)) {
type = InstructionType::TargetNode;
props = std::make_shared<TargetNodeInstructionProps>(dictionary);
}
else if (dictionary.hasValue<ghoul::Dictionary>(KeyNavigationState)) {
type = InstructionType::NavigationState;
props = std::make_shared<NavigationStateInstructionProps>(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<ghoul::Dictionary>(KeyInstructions);
for (size_t i = 1; i <= instructions.size(); ++i) {
ghoul::Dictionary insDict = instructions.value<ghoul::Dictionary>(std::to_string(i));
_instructions.push_back(Instruction{ insDict });
}
}
PathSpecification::PathSpecification(const Instruction instruction) {
_instructions.push_back(instruction);
}
const std::vector<Instruction>* 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

View File

@@ -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 <openspace/documentation/documentation.h>
#include <openspace/interaction/navigationhandler.h>
#include <ghoul/glm.h>
#include <optional>
namespace openspace::autonavigation {
enum InstructionType { TargetNode, NavigationState };
struct InstructionProps {
InstructionProps() = default;
InstructionProps(const ghoul::Dictionary& dictionary);
virtual ~InstructionProps() {} // abstract
std::optional<double> duration;
};
struct TargetNodeInstructionProps : public InstructionProps {
TargetNodeInstructionProps(const ghoul::Dictionary& dictionary);
std::string targetNode;
std::optional<glm::dvec3> position; // relative to target node (model space)
std::optional<double> 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<InstructionProps> props;
};
class PathSpecification {
public:
PathSpecification() = default;
PathSpecification(const ghoul::Dictionary& dictionary);
PathSpecification(const Instruction instruction);
static documentation::Documentation Documentation();
// Accessors
const std::vector<Instruction>* instructions() const;
private:
std::vector<Instruction> _instructions;
// TODO: maxSpeed or speedFactor or something?
};
} // namespace openspace::autonavigation
#endif // __OPENSPACE_MODULE___NAVIGATIONHANDLER___H__

View File

@@ -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 <modules/autonavigation/transferfunctions.h>
#include <ghoul/glm.h>
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

View File

@@ -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