mirror of
https://github.com/OpenSpace/OpenSpace.git
synced 2026-05-07 03:49:43 -05:00
Remove old autonavigation module files
This commit is contained in:
@@ -1,59 +0,0 @@
|
||||
##########################################################################################
|
||||
# #
|
||||
# OpenSpace #
|
||||
# #
|
||||
# Copyright (c) 2014-2021 #
|
||||
# #
|
||||
# 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}/helperfunctions.h
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/path.h
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/pathcreator.h
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/pathcurve.h
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/pathnavigationhandler.h
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/waypoint.h
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/curves/avoidcollisioncurve.h
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/curves/zoomoutoverviewcurve.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}/helperfunctions.cpp
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/path.cpp
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/pathcreator.cpp
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/pathcurve.cpp
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/pathnavigationhandler.cpp
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/waypoint.cpp
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/curves/avoidcollisioncurve.cpp
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/curves/zoomoutoverviewcurve.cpp
|
||||
)
|
||||
source_group("Source Files" FILES ${SOURCE_FILES})
|
||||
|
||||
create_new_module(
|
||||
"AutoNavigation"
|
||||
autonavigation_module
|
||||
STATIC
|
||||
${HEADER_FILES} ${SOURCE_FILES}
|
||||
)
|
||||
@@ -1,160 +0,0 @@
|
||||
/*****************************************************************************************
|
||||
* *
|
||||
* OpenSpace *
|
||||
* *
|
||||
* Copyright (c) 2014-2021 *
|
||||
* *
|
||||
* 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 openspace {
|
||||
|
||||
AutoNavigationModule::AutoNavigationModule()
|
||||
: OpenSpaceModule(Name)
|
||||
{
|
||||
addPropertySubOwner(_pathNavigationHandler);
|
||||
}
|
||||
|
||||
pathnavigation::PathNavigationHandler& AutoNavigationModule::PathNavigationHandler() {
|
||||
return _pathNavigationHandler;
|
||||
}
|
||||
|
||||
scripting::LuaLibrary AutoNavigationModule::luaLibrary() const {
|
||||
scripting::LuaLibrary res;
|
||||
res.name = "autonavigation";
|
||||
res.scripts = {
|
||||
absPath("${MODULE_AUTONAVIGATION}/scripts/rendering.lua")
|
||||
};
|
||||
res.functions = {
|
||||
{
|
||||
"isFlying",
|
||||
&pathnavigation::luascriptfunctions::isFlying,
|
||||
{},
|
||||
"",
|
||||
"Returns true if a camera path is currently running, and false otherwise."
|
||||
},
|
||||
{
|
||||
"continuePath",
|
||||
&pathnavigation::luascriptfunctions::continuePath,
|
||||
{},
|
||||
"",
|
||||
"Continue playing a paused camera path."
|
||||
},
|
||||
{
|
||||
"pausePath",
|
||||
&pathnavigation::luascriptfunctions::pausePath,
|
||||
{},
|
||||
"",
|
||||
"Pause a playing camera path."
|
||||
},
|
||||
{
|
||||
"stopPath",
|
||||
&pathnavigation::luascriptfunctions::stopPath,
|
||||
{},
|
||||
"",
|
||||
"Stops a path, if one is being played."
|
||||
},
|
||||
{
|
||||
"goTo",
|
||||
&pathnavigation::luascriptfunctions::goTo,
|
||||
{},
|
||||
"string [, bool, double]",
|
||||
"Move the camera to the node with the specified name. The optional double "
|
||||
"specifies the duration of the motion. If the optional bool is set to true "
|
||||
"the target up vector for camera is set based on the target node. Either of "
|
||||
"the optional parameters can be left out."
|
||||
},
|
||||
{
|
||||
"goToHeight",
|
||||
&pathnavigation::luascriptfunctions::goToHeight,
|
||||
{},
|
||||
"string, double [, bool, double]",
|
||||
"Move the camera to the node with the specified name. The second input "
|
||||
"parameter is the desired target height. The optional double "
|
||||
"specifies the duration of the motion. If the optional bool is set to true "
|
||||
"the target up vector for camera is set based on the target node. Either of "
|
||||
"the optional parameters can be left out."
|
||||
},
|
||||
{
|
||||
"goToGeo",
|
||||
&pathnavigation::luascriptfunctions::goToGeo,
|
||||
{},
|
||||
"string, double, double, double [, bool, double]",
|
||||
"Move the camera to the globe with the name given by the input string. "
|
||||
"The next three input parameters are latitude, longitude and altitude. "
|
||||
"The optional double specifies the duration of the motion. If the optional "
|
||||
"bool is set to true the target up vector for camera is set based on the "
|
||||
"target node. Either of the optional parameters can be left out."
|
||||
},
|
||||
{
|
||||
"generatePath",
|
||||
&pathnavigation::luascriptfunctions::generatePath,
|
||||
{},
|
||||
"table",
|
||||
"Generate the path as described by the lua table input argument. "
|
||||
},
|
||||
{
|
||||
"getPathPositions",
|
||||
&pathnavigation::luascriptfunctions::getPathPositions,
|
||||
{},
|
||||
"number",
|
||||
"FOR DEBUG. Sample positions along the path. The input argument is the "
|
||||
"number of samples per path segment. "
|
||||
},
|
||||
{
|
||||
"getPathOrientations",
|
||||
&pathnavigation::luascriptfunctions::getPathOrientations,
|
||||
{},
|
||||
"number",
|
||||
"FOR DEBUG. Sample orientations along the path. The input argument is the "
|
||||
"number of samples per path segment. "
|
||||
},
|
||||
{
|
||||
"getPathViewDirections",
|
||||
&pathnavigation::luascriptfunctions::getPathViewDirections,
|
||||
{},
|
||||
"number",
|
||||
"FOR DEBUG. Sample view directions along the path. The input argument is "
|
||||
"the number of samples per path segment. "
|
||||
},
|
||||
{
|
||||
"getControlPoints",
|
||||
&pathnavigation::luascriptfunctions::getControlPoints,
|
||||
{},
|
||||
"",
|
||||
"FOR DEBUG. Get control point positions from all pathsegments"
|
||||
}
|
||||
};
|
||||
return res;
|
||||
}
|
||||
|
||||
void AutoNavigationModule::internalInitialize(const ghoul::Dictionary&) {
|
||||
global::callback::preSync->emplace_back([this]() {
|
||||
_pathNavigationHandler.updateCamera(global::windowDelegate->deltaTime());
|
||||
});
|
||||
}
|
||||
|
||||
} // namespace openspace
|
||||
@@ -1,53 +0,0 @@
|
||||
/*****************************************************************************************
|
||||
* *
|
||||
* OpenSpace *
|
||||
* *
|
||||
* Copyright (c) 2014-2021 *
|
||||
* *
|
||||
* 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 <modules/autonavigation/pathnavigationhandler.h>
|
||||
|
||||
namespace openspace {
|
||||
|
||||
class AutoNavigationModule : public OpenSpaceModule {
|
||||
public:
|
||||
constexpr static const char* Name = "AutoNavigation";
|
||||
|
||||
AutoNavigationModule();
|
||||
virtual ~AutoNavigationModule() = default;
|
||||
|
||||
pathnavigation::PathNavigationHandler& PathNavigationHandler();
|
||||
|
||||
scripting::LuaLibrary luaLibrary() const override;
|
||||
|
||||
private:
|
||||
void internalInitialize(const ghoul::Dictionary&) override;
|
||||
|
||||
pathnavigation::PathNavigationHandler _pathNavigationHandler;
|
||||
};
|
||||
|
||||
} // namespace openspace
|
||||
|
||||
#endif // __OPENSPACE_MODULE_AUTONAVIGATION___AUTONAVIGATION_MODULE___H__
|
||||
@@ -1,421 +0,0 @@
|
||||
/*****************************************************************************************
|
||||
* *
|
||||
* OpenSpace *
|
||||
* *
|
||||
* Copyright (c) 2014-2021 *
|
||||
* *
|
||||
* 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/globebrowsing/globebrowsingmodule.h>
|
||||
#include <modules/globebrowsing/src/renderableglobe.h>
|
||||
#include <openspace/engine/globals.h>
|
||||
#include <openspace/engine/moduleengine.h>
|
||||
#include <openspace/navigation/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 {
|
||||
constexpr const char* _loggerCat = "PathNavigation";
|
||||
constexpr const double Epsilon = 1e-12;
|
||||
} // namespace
|
||||
|
||||
namespace openspace::pathnavigation::luascriptfunctions {
|
||||
|
||||
int isFlying(lua_State* L) {
|
||||
ghoul::lua::checkArgumentsAndThrow(L, 0, "lua::isFlying");
|
||||
|
||||
AutoNavigationModule* module = global::moduleEngine->module<AutoNavigationModule>();
|
||||
bool hasFinished = module->PathNavigationHandler().hasFinished();
|
||||
|
||||
ghoul::lua::push(L, !hasFinished);
|
||||
ghoul_assert(lua_gettop(L) == 1, "Incorrect number of items left on stack");
|
||||
return 1;
|
||||
}
|
||||
|
||||
int continuePath(lua_State* L) {
|
||||
ghoul::lua::checkArgumentsAndThrow(L, 0, "lua::continuePath");
|
||||
|
||||
AutoNavigationModule* module = global::moduleEngine->module<AutoNavigationModule>();
|
||||
module->PathNavigationHandler().continuePath();
|
||||
|
||||
ghoul_assert(lua_gettop(L) == 0, "Incorrect number of items left on stack");
|
||||
return 0;
|
||||
}
|
||||
|
||||
int pausePath(lua_State* L) {
|
||||
ghoul::lua::checkArgumentsAndThrow(L, 0, "lua::pausePath");
|
||||
|
||||
AutoNavigationModule* module = global::moduleEngine->module<AutoNavigationModule>();
|
||||
module->PathNavigationHandler().pausePath();
|
||||
|
||||
ghoul_assert(lua_gettop(L) == 0, "Incorrect number of items left on stack");
|
||||
return 0;
|
||||
}
|
||||
|
||||
int stopPath(lua_State* L) {
|
||||
ghoul::lua::checkArgumentsAndThrow(L, 0, "lua::stopPath");
|
||||
|
||||
AutoNavigationModule* module = global::moduleEngine->module<AutoNavigationModule>();
|
||||
module->PathNavigationHandler().abortPath();
|
||||
|
||||
ghoul_assert(lua_gettop(L) == 0, "Incorrect number of items left on stack");
|
||||
return 0;
|
||||
}
|
||||
|
||||
// All the goTo function has the same two optional input parameters at the end. The
|
||||
// purpose of this function is to handle these input parameters and add the result
|
||||
// to the dictionary specifying the instruction for a camera path.
|
||||
int handleOptionalGoToParameters(lua_State* L, const int startLocation,
|
||||
const int nArguments,
|
||||
ghoul::Dictionary& resultInstruction)
|
||||
{
|
||||
const bool firstIsNumber = (lua_isnumber(L, startLocation) != 0);
|
||||
const bool firstIsBool = (lua_isboolean(L, startLocation) != 0);
|
||||
|
||||
if (!(firstIsNumber || firstIsBool)) {
|
||||
const char* msg = lua_pushfstring(
|
||||
L,
|
||||
"%s or %s expected, got %s",
|
||||
lua_typename(L, LUA_TNUMBER),
|
||||
lua_typename(L, LUA_TBOOLEAN),
|
||||
luaL_typename(L, -1)
|
||||
);
|
||||
return ghoul::lua::luaError(
|
||||
L, fmt::format("bad argument #{} ({})", startLocation, msg)
|
||||
);
|
||||
}
|
||||
|
||||
int location = startLocation;
|
||||
|
||||
if (firstIsBool) {
|
||||
const bool useUpFromTarget = (lua_toboolean(L, location) == 1);
|
||||
resultInstruction.setValue("UseTargetUpDirection", useUpFromTarget);
|
||||
|
||||
if (nArguments > startLocation) {
|
||||
location++;
|
||||
}
|
||||
}
|
||||
|
||||
if (firstIsNumber || nArguments > startLocation) {
|
||||
double duration = ghoul::lua::value<double>(L, location);
|
||||
if (duration <= Epsilon) {
|
||||
lua_settop(L, 0);
|
||||
return ghoul::lua::luaError(L, "Duration must be larger than zero.");
|
||||
}
|
||||
resultInstruction.setValue("Duration", duration);
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int goTo(lua_State* L) {
|
||||
int nArguments = ghoul::lua::checkArgumentsAndThrow(L, { 1, 3 }, "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);
|
||||
}
|
||||
|
||||
using namespace std::string_literals;
|
||||
ghoul::Dictionary insDict;
|
||||
insDict.setValue("Type", "Node"s);
|
||||
insDict.setValue("Target", nodeIdentifier);
|
||||
|
||||
if (nArguments > 1) {
|
||||
int result = handleOptionalGoToParameters(L, 2, nArguments, insDict);
|
||||
if (result != 0) {
|
||||
return result; // An error occurred
|
||||
}
|
||||
}
|
||||
|
||||
AutoNavigationModule* module = global::moduleEngine->module<AutoNavigationModule>();
|
||||
module->PathNavigationHandler().createPath(insDict);
|
||||
|
||||
if (module->PathNavigationHandler().hasCurrentPath()) {
|
||||
module->PathNavigationHandler().startPath();
|
||||
}
|
||||
|
||||
lua_settop(L, 0);
|
||||
ghoul_assert(lua_gettop(L) == 0, "Incorrect number of items left on stack");
|
||||
return 0;
|
||||
}
|
||||
|
||||
int goToHeight(lua_State* L) {
|
||||
int nArguments = ghoul::lua::checkArgumentsAndThrow(L, { 2, 4 }, "lua::goToHeight");
|
||||
|
||||
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);
|
||||
}
|
||||
|
||||
double height = ghoul::lua::value<double>(L, 2);
|
||||
|
||||
using namespace std::string_literals;
|
||||
ghoul::Dictionary insDict;
|
||||
insDict.setValue("Type", "Node"s);
|
||||
insDict.setValue("Target", nodeIdentifier);
|
||||
insDict.setValue("Height", height);
|
||||
|
||||
if (nArguments > 2) {
|
||||
int result = handleOptionalGoToParameters(L, 3, nArguments, insDict);
|
||||
if (result != 0) {
|
||||
return result; // An error occurred
|
||||
}
|
||||
}
|
||||
|
||||
AutoNavigationModule* module = global::moduleEngine->module<AutoNavigationModule>();
|
||||
module->PathNavigationHandler().createPath(insDict);
|
||||
|
||||
if (module->PathNavigationHandler().hasCurrentPath()) {
|
||||
module->PathNavigationHandler().startPath();
|
||||
}
|
||||
|
||||
lua_settop(L, 0);
|
||||
ghoul_assert(lua_gettop(L) == 0, "Incorrect number of items left on stack");
|
||||
return 0;
|
||||
}
|
||||
|
||||
// @TODO (emmbr 2020-11-06) Ideally, this module shouldn't depend on things from
|
||||
// Globebrowsing, but we want it for an istallation. Later on, move this functionality
|
||||
// somewhere else. Maybe combine with the existing "goToGeo" in globebrowsing?
|
||||
int goToGeo(lua_State* L) {
|
||||
int nArguments = ghoul::lua::checkArgumentsAndThrow(L, { 4, 6 }, "lua::goToGeo");
|
||||
|
||||
const std::string& nodeIdentifier = ghoul::lua::value<std::string>(L, 1);
|
||||
const SceneGraphNode* n = sceneGraphNode(nodeIdentifier);
|
||||
if (!n) {
|
||||
lua_settop(L, 0);
|
||||
return ghoul::lua::luaError(L, "Unknown globe name: " + nodeIdentifier);
|
||||
}
|
||||
|
||||
const double latitude = ghoul::lua::value<double>(L, 2);
|
||||
const double longitude = ghoul::lua::value<double>(L, 3);
|
||||
const double altitude = ghoul::lua::value<double>(L, 4);
|
||||
|
||||
using RenderableGlobe = openspace::globebrowsing::RenderableGlobe;
|
||||
const RenderableGlobe* globe = dynamic_cast<const RenderableGlobe*>(n->renderable());
|
||||
if (!globe) {
|
||||
return ghoul::lua::luaError(L, "Identifier must be a RenderableGlobe");
|
||||
}
|
||||
|
||||
// Compute the relative position based on the input values
|
||||
glm::dvec3 positionModelCoords = global::moduleEngine->module<GlobeBrowsingModule>()
|
||||
->cartesianCoordinatesFromGeo(
|
||||
*globe,
|
||||
latitude,
|
||||
longitude,
|
||||
altitude
|
||||
);
|
||||
|
||||
using namespace std::string_literals;
|
||||
ghoul::Dictionary insDict;
|
||||
insDict.setValue("Type", "Node"s);
|
||||
insDict.setValue("Target", nodeIdentifier);
|
||||
insDict.setValue("Position", positionModelCoords);
|
||||
|
||||
if (nArguments > 4) {
|
||||
int result = handleOptionalGoToParameters(L, 5, nArguments, insDict);
|
||||
if (result != 0) {
|
||||
return result; // An error occurred
|
||||
}
|
||||
}
|
||||
|
||||
AutoNavigationModule* module = global::moduleEngine->module<AutoNavigationModule>();
|
||||
module->PathNavigationHandler().createPath(insDict);
|
||||
|
||||
if (module->PathNavigationHandler().hasCurrentPath()) {
|
||||
module->PathNavigationHandler().startPath();
|
||||
}
|
||||
|
||||
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);
|
||||
|
||||
AutoNavigationModule* module = global::moduleEngine->module<AutoNavigationModule>();
|
||||
module->PathNavigationHandler().createPath(dictionary);
|
||||
|
||||
if (module->PathNavigationHandler().hasCurrentPath()) {
|
||||
module->PathNavigationHandler().startPath();
|
||||
}
|
||||
|
||||
lua_settop(L, 0);
|
||||
ghoul_assert(lua_gettop(L) == 0, "Incorrect number of items left on stack");
|
||||
return 0;
|
||||
}
|
||||
|
||||
// TODO: remove when not needed
|
||||
// Created for debugging. Access info for rendereable path
|
||||
int getPathPositions(lua_State* L) {
|
||||
ghoul::lua::checkArgumentsAndThrow(L, 1, "lua::getPathPositions");
|
||||
|
||||
const int pointsPerSegment = (int)ghoul::lua::value<lua_Number>(L, 1);
|
||||
|
||||
// Get sample positions from the current curve
|
||||
AutoNavigationModule* module = global::moduleEngine->module<AutoNavigationModule>();
|
||||
PathNavigationHandler& handler = module->PathNavigationHandler();
|
||||
std::vector<glm::dvec3> points = handler.curvePositions(pointsPerSegment);
|
||||
|
||||
// Push the points to the Lua stack:
|
||||
lua_settop(L, 0);
|
||||
const auto pushVector = [](lua_State* L, const glm::dvec3& v) {
|
||||
lua_newtable(L);
|
||||
ghoul::lua::push(L, 1, v.x);
|
||||
lua_rawset(L, -3);
|
||||
ghoul::lua::push(L, 2, v.y);
|
||||
lua_rawset(L, -3);
|
||||
ghoul::lua::push(L, 3, v.z);
|
||||
lua_rawset(L, -3);
|
||||
};
|
||||
|
||||
lua_newtable(L);
|
||||
for (int i = 0; i < points.size(); ++i) {
|
||||
ghoul::lua::push(L, i+1);
|
||||
pushVector(L, points[i]);
|
||||
lua_rawset(L, -3);
|
||||
}
|
||||
|
||||
ghoul_assert(lua_gettop(L) == 1, "Incorrect number of items left on stack");
|
||||
return 1;
|
||||
}
|
||||
|
||||
// TODO: remove when not needed
|
||||
// Created for debugging. Access info for rendereable path
|
||||
int getPathOrientations(lua_State* L) {
|
||||
ghoul::lua::checkArgumentsAndThrow(L, 1, "lua::getPathPositions");
|
||||
|
||||
const int pointsPerSegment = (int)ghoul::lua::value<lua_Number>(L, 1);
|
||||
|
||||
// Get sample positions from the current curve
|
||||
AutoNavigationModule* module = global::moduleEngine->module<AutoNavigationModule>();
|
||||
PathNavigationHandler& handler = module->PathNavigationHandler();
|
||||
std::vector<glm::dquat> orientations = handler.curveOrientations(pointsPerSegment);
|
||||
|
||||
// Push the rotation to the Lua stack:
|
||||
lua_settop(L, 0);
|
||||
const auto pushVector = [](lua_State* L, const glm::dquat& v) {
|
||||
lua_newtable(L);
|
||||
ghoul::lua::push(L, 1, v.w);
|
||||
lua_rawset(L, -4);
|
||||
ghoul::lua::push(L, 2, v.x);
|
||||
lua_rawset(L, -4);
|
||||
ghoul::lua::push(L, 3, v.y);
|
||||
lua_rawset(L, -4);
|
||||
ghoul::lua::push(L, 4, v.z);
|
||||
lua_rawset(L, -4);
|
||||
};
|
||||
|
||||
lua_newtable(L);
|
||||
for (int i = 0; i < orientations.size(); ++i) {
|
||||
ghoul::lua::push(L, i + 1);
|
||||
pushVector(L, orientations[i]);
|
||||
lua_rawset(L, -4);
|
||||
}
|
||||
|
||||
ghoul_assert(lua_gettop(L) == 1, "Incorrect number of items left on stack");
|
||||
return 1;
|
||||
}
|
||||
|
||||
// TODO: remove when not needed
|
||||
// Created for debugging. Access info for rendereable path
|
||||
int getPathViewDirections(lua_State* L) {
|
||||
ghoul::lua::checkArgumentsAndThrow(L, 1, "lua::getPathViewDirections");
|
||||
|
||||
const int pointsPerSegment = (int)ghoul::lua::value<lua_Number>(L, 1);
|
||||
|
||||
// Get sample positions from the current curve
|
||||
AutoNavigationModule* module = global::moduleEngine->module<AutoNavigationModule>();
|
||||
PathNavigationHandler& handler = module->PathNavigationHandler();
|
||||
std::vector<glm::dvec3> viewDirections = handler.curveViewDirections(pointsPerSegment);
|
||||
|
||||
// Push the points to the Lua stack:
|
||||
lua_settop(L, 0);
|
||||
const auto pushVector = [](lua_State* L, const glm::dvec3& v) {
|
||||
lua_newtable(L);
|
||||
ghoul::lua::push(L, 1, v.x);
|
||||
lua_rawset(L, -3);
|
||||
ghoul::lua::push(L, 2, v.y);
|
||||
lua_rawset(L, -3);
|
||||
ghoul::lua::push(L, 3, v.z);
|
||||
lua_rawset(L, -3);
|
||||
};
|
||||
|
||||
// Push the rotation to the Lua stack:
|
||||
lua_newtable(L);
|
||||
for (int i = 0; i < viewDirections.size(); ++i) {
|
||||
ghoul::lua::push(L, i + 1);
|
||||
pushVector(L, viewDirections[i]);
|
||||
lua_rawset(L, -3);
|
||||
}
|
||||
|
||||
ghoul_assert(lua_gettop(L) == 1, "Incorrect number of items left on stack");
|
||||
return 1;
|
||||
}
|
||||
|
||||
// TODO: remove when not needed
|
||||
// Created for debugging. Access info for rendering of control points
|
||||
int getControlPoints(lua_State* L) {
|
||||
ghoul::lua::checkArgumentsAndThrow(L, 0, "lua::getControlPoints");
|
||||
|
||||
// Get sample positions from the current curve
|
||||
AutoNavigationModule* module = global::moduleEngine->module<AutoNavigationModule>();
|
||||
PathNavigationHandler& handler = module->PathNavigationHandler();
|
||||
std::vector<glm::dvec3> points = handler.controlPoints();
|
||||
|
||||
// Push the points to the Lua stack:
|
||||
lua_settop(L, 0);
|
||||
const auto pushVector = [](lua_State* L, const glm::dvec3& v) {
|
||||
lua_newtable(L);
|
||||
ghoul::lua::push(L, 1, v.x);
|
||||
lua_rawset(L, -3);
|
||||
ghoul::lua::push(L, 2, v.y);
|
||||
lua_rawset(L, -3);
|
||||
ghoul::lua::push(L, 3, v.z);
|
||||
lua_rawset(L, -3);
|
||||
};
|
||||
|
||||
lua_newtable(L);
|
||||
for (int i = 0; i < points.size(); ++i) {
|
||||
ghoul::lua::push(L, i+1);
|
||||
pushVector(L, points[i]);
|
||||
lua_rawset(L, -3);
|
||||
}
|
||||
|
||||
ghoul_assert(lua_gettop(L) == 1, "Incorrect number of items left on stack");
|
||||
return 1;
|
||||
}
|
||||
|
||||
} // namespace openspace::pathnavigation::luascriptfunctions
|
||||
@@ -1,190 +0,0 @@
|
||||
/*****************************************************************************************
|
||||
* *
|
||||
* OpenSpace *
|
||||
* *
|
||||
* Copyright (c) 2014-2021 *
|
||||
* *
|
||||
* 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/curves/avoidcollisioncurve.h>
|
||||
|
||||
#include <modules/autonavigation/autonavigationmodule.h>
|
||||
#include <modules/autonavigation/helperfunctions.h>
|
||||
#include <modules/autonavigation/waypoint.h>
|
||||
#include <openspace/engine/globals.h>
|
||||
#include <openspace/engine/moduleengine.h>
|
||||
#include <openspace/query/query.h>
|
||||
#include <openspace/scene/scenegraphnode.h>
|
||||
#include <ghoul/logging/logmanager.h>
|
||||
#include <glm/gtx/projection.hpp>
|
||||
#include <algorithm>
|
||||
#include <vector>
|
||||
|
||||
namespace {
|
||||
constexpr const char* _loggerCat = "AvoidCollisionCurve";
|
||||
|
||||
constexpr const double CloseToNodeThresholdFactor = 5.0;
|
||||
constexpr const double AvoidCollisionDistanceFactor = 3.0;
|
||||
constexpr const double CollisionBufferSizeFactor = 1.0;
|
||||
constexpr const int MaxAvoidCollisionSteps = 10;
|
||||
} // namespace
|
||||
|
||||
namespace openspace::pathnavigation {
|
||||
|
||||
AvoidCollisionCurve::AvoidCollisionCurve(const Waypoint& start, const Waypoint& end) {
|
||||
auto module = global::moduleEngine->module<AutoNavigationModule>();
|
||||
_relevantNodes = module->PathNavigationHandler().relevantNodes();
|
||||
|
||||
const glm::dvec3 startNodeCenter = start.node()->worldPosition();
|
||||
const glm::dvec3 endNodeCenter = end.node()->worldPosition();
|
||||
const double startNodeRadius = start.validBoundingSphere;
|
||||
const double endNodeRadius = end.validBoundingSphere;
|
||||
const glm::dvec3 startViewDir = start.rotation() * glm::dvec3(0.0, 0.0, -1.0);
|
||||
|
||||
// Add control points for a catmull-rom spline, first and last will not be intersected
|
||||
_points.push_back(start.position());
|
||||
_points.push_back(start.position());
|
||||
|
||||
// Add an extra point to first go backwards if starting close to planet
|
||||
glm::dvec3 nodeToStart = start.position() - startNodeCenter;
|
||||
double distanceToStartNode = glm::length(nodeToStart);
|
||||
|
||||
if (distanceToStartNode < CloseToNodeThresholdFactor * startNodeRadius) {
|
||||
double distance = startNodeRadius;
|
||||
glm::dvec3 newPos = start.position() + distance * glm::normalize(nodeToStart);
|
||||
_points.push_back(newPos);
|
||||
}
|
||||
|
||||
// Add point for moving out if the end state is in opposite direction
|
||||
glm::dvec3 startToEnd = end.position() - start.position();
|
||||
double cosAngleToTarget = glm::dot(normalize(-startViewDir), normalize(startToEnd));
|
||||
bool targetInOppositeDirection = cosAngleToTarget > 0.7;
|
||||
|
||||
if (targetInOppositeDirection) {
|
||||
const glm::dquat midleRot = glm::slerp(start.rotation(), end.rotation(), 0.5);
|
||||
const glm::dvec3 middleViewDir = midleRot * glm::dvec3(0.0, 0.0, -1.0);
|
||||
const double stepOutDistance = 0.4 * glm::length(startToEnd);
|
||||
|
||||
glm::dvec3 newPos = start.position() + 0.2 * startToEnd -
|
||||
stepOutDistance * glm::normalize(middleViewDir);
|
||||
|
||||
_points.push_back(newPos);
|
||||
}
|
||||
|
||||
// Add an extra point to approach target
|
||||
const glm::dvec3 nodeToEnd = end.position() - endNodeCenter;
|
||||
const double distanceToEndNode = glm::length(nodeToEnd);
|
||||
|
||||
if (distanceToEndNode < CloseToNodeThresholdFactor * endNodeRadius) {
|
||||
double distance = endNodeRadius;
|
||||
glm::dvec3 newPos = end.position() + distance * glm::normalize(nodeToEnd);
|
||||
_points.push_back(newPos);
|
||||
}
|
||||
|
||||
_points.push_back(end.position());
|
||||
_points.push_back(end.position());
|
||||
|
||||
// Create extra points to avoid collision
|
||||
removeCollisions();
|
||||
|
||||
initializeParameterData();
|
||||
}
|
||||
|
||||
// Try to reduce the risk of collision by approximating the curve with linear segments.
|
||||
// If a collision happens, create a new point for the path to go through, in an attempt to
|
||||
// avoid that collision
|
||||
void AvoidCollisionCurve::removeCollisions(int step) {
|
||||
if (step > MaxAvoidCollisionSteps) {
|
||||
return;
|
||||
}
|
||||
|
||||
const int nSegments = static_cast<int>( _points.size() - 3);
|
||||
for (int i = 0; i < nSegments; ++i) {
|
||||
const glm::dvec3 lineStart = _points[i + 1];
|
||||
const glm::dvec3 lineEnd = _points[i + 2];
|
||||
|
||||
for (SceneGraphNode* node : _relevantNodes) {
|
||||
// Do collision check in relative coordinates, to avoid huge numbers
|
||||
const glm::dmat4 modelTransform = node->modelTransform();
|
||||
glm::dvec3 p1 = glm::inverse(modelTransform) * glm::dvec4(lineStart, 1.0);
|
||||
glm::dvec3 p2 = glm::inverse(modelTransform) * glm::dvec4(lineEnd, 1.0);
|
||||
|
||||
// Sphere to check for collision
|
||||
double radius = node->boundingSphere();
|
||||
glm::dvec3 center = glm::dvec3(0.0, 0.0, 0.0);
|
||||
|
||||
// Add a buffer to avoid passing too close to the node.
|
||||
// Dont't add it if any point is inside the buffer
|
||||
double buffer = CollisionBufferSizeFactor * node->boundingSphere();
|
||||
bool p1IsInside = helpers::isPointInsideSphere(p1, center, radius + buffer);
|
||||
bool p2IsInside = helpers::isPointInsideSphere(p2, center, radius + buffer);
|
||||
if (!p1IsInside && !p2IsInside) {
|
||||
radius += buffer;
|
||||
}
|
||||
|
||||
glm::dvec3 intersectionPointModelCoords;
|
||||
bool collision = helpers::lineSphereIntersection(
|
||||
p1,
|
||||
p2,
|
||||
center,
|
||||
radius,
|
||||
intersectionPointModelCoords
|
||||
);
|
||||
|
||||
if (collision) {
|
||||
glm::dvec3 collisionPoint = modelTransform *
|
||||
glm::dvec4(intersectionPointModelCoords, 1.0);
|
||||
|
||||
// before collision response, make sure none of the points are inside the node
|
||||
bool isStartInsideNode = helpers::isPointInsideSphere(p1, center, radius);
|
||||
bool isEndInsideNode = helpers::isPointInsideSphere(p2, center, radius);
|
||||
if (isStartInsideNode || isEndInsideNode) {
|
||||
LWARNING(fmt::format(
|
||||
"Something went wrong! "
|
||||
"At least one point in the path is inside node: {}",
|
||||
node->identifier()
|
||||
));
|
||||
break;
|
||||
}
|
||||
|
||||
// To avoid collision, take a step in an orhtogonal direction of the
|
||||
// collision point and add a new point
|
||||
const glm::dvec3 lineDirection = glm::normalize(lineEnd - lineStart);
|
||||
const glm::dvec3 nodeCenter = node->worldPosition();
|
||||
const glm::dvec3 collisionPointToCenter = nodeCenter - collisionPoint;
|
||||
|
||||
const glm::dvec3 parallell = glm::proj(collisionPointToCenter, lineDirection);
|
||||
const glm::dvec3 orthogonal = collisionPointToCenter - parallell;
|
||||
|
||||
const double avoidCollisionDistance = AvoidCollisionDistanceFactor * radius;
|
||||
|
||||
glm::dvec3 extraKnot = collisionPoint -
|
||||
avoidCollisionDistance * glm::normalize(orthogonal);
|
||||
|
||||
_points.insert(_points.begin() + i + 2, extraKnot);
|
||||
|
||||
step++;
|
||||
removeCollisions(step);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
} // namespace openspace::pathnavigation
|
||||
@@ -1,46 +0,0 @@
|
||||
/*****************************************************************************************
|
||||
* *
|
||||
* OpenSpace *
|
||||
* *
|
||||
* Copyright (c) 2014-2021 *
|
||||
* *
|
||||
* 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___AVOIDCOLLISIONCURVE___H__
|
||||
#define __OPENSPACE_MODULE_AUTONAVIGATION___AVOIDCOLLISIONCURVE___H__
|
||||
|
||||
#include <modules/autonavigation/pathcurve.h>
|
||||
|
||||
namespace openspace::pathnavigation {
|
||||
|
||||
struct WayPoint;
|
||||
|
||||
class AvoidCollisionCurve : public PathCurve {
|
||||
public:
|
||||
AvoidCollisionCurve(const Waypoint& start, const Waypoint& end);
|
||||
|
||||
private:
|
||||
void removeCollisions(int step = 0);
|
||||
|
||||
std::vector<SceneGraphNode*> _relevantNodes;
|
||||
};
|
||||
|
||||
} // namespace openspace::pathnavigation
|
||||
|
||||
#endif // __OPENSPACE_MODULE_AUTONAVIGATION___AVOIDCOLLISIONCURVE___H__
|
||||
@@ -1,104 +0,0 @@
|
||||
/*****************************************************************************************
|
||||
* *
|
||||
* OpenSpace *
|
||||
* *
|
||||
* Copyright (c) 2014-2021 *
|
||||
* *
|
||||
* 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/curves/zoomoutoverviewcurve.h>
|
||||
|
||||
#include <modules/autonavigation/autonavigationmodule.h>
|
||||
#include <modules/autonavigation/helperfunctions.h>
|
||||
#include <modules/autonavigation/waypoint.h>
|
||||
#include <openspace/engine/globals.h>
|
||||
#include <openspace/engine/moduleengine.h>
|
||||
#include <openspace/query/query.h>
|
||||
#include <openspace/scene/scenegraphnode.h>
|
||||
#include <ghoul/logging/logmanager.h>
|
||||
#include <glm/gtx/projection.hpp>
|
||||
#include <algorithm>
|
||||
#include <vector>
|
||||
|
||||
namespace {
|
||||
constexpr const char* _loggerCat = "ZoomOutOverviewCurve";
|
||||
} // namespace
|
||||
|
||||
namespace openspace::pathnavigation {
|
||||
|
||||
// Go far out to get a view of both tagets, aimed to match lookAt orientation
|
||||
ZoomOutOverviewCurve::ZoomOutOverviewCurve(const Waypoint& start, const Waypoint& end) {
|
||||
const double startNodeRadius = start.validBoundingSphere;
|
||||
const double endNodeRadius = end.validBoundingSphere;
|
||||
|
||||
const double endTangentsLengthFactor = 2.0;
|
||||
const double startTangentLength = endTangentsLengthFactor * startNodeRadius;
|
||||
const double endTangentLength = endTangentsLengthFactor * endNodeRadius;
|
||||
|
||||
const glm::dvec3 startNodePos = start.node()->worldPosition();
|
||||
const glm::dvec3 endNodePos = end.node()->worldPosition();
|
||||
const glm::dvec3 startTangentDir = normalize(start.position() - startNodePos);
|
||||
const glm::dvec3 endTangentDir = normalize(end.position() - endNodePos);
|
||||
|
||||
// Start by going outwards
|
||||
_points.push_back(start.position());
|
||||
_points.push_back(start.position());
|
||||
_points.push_back(start.position() + startTangentLength * startTangentDir);
|
||||
|
||||
// Zoom out
|
||||
if (start.nodeIdentifier != end.nodeIdentifier) {
|
||||
const glm::dvec3 n1 = startTangentDir;
|
||||
const glm::dvec3 n2 = endTangentDir;
|
||||
|
||||
// Decide the step direction for the "overview point" based on the directions
|
||||
// at the start and end of the path, to try to get a nice curve shape
|
||||
glm::dvec3 goodStepDirection;
|
||||
if (glm::dot(n1, n2) < 0.0) {
|
||||
// Facing in different directions => step in direction of the cross product
|
||||
goodStepDirection = glm::normalize(glm::cross(-n1, n2));
|
||||
}
|
||||
else {
|
||||
goodStepDirection = glm::normalize(n1 + n2);
|
||||
}
|
||||
|
||||
// Find a direction that is orthogonal to the line between the start and end position
|
||||
const glm::dvec3 startPosToEndPos = end.position() - start.position();
|
||||
const glm::dvec3 outwardStepVector =
|
||||
0.5 * glm::length(startPosToEndPos) * goodStepDirection;
|
||||
|
||||
const glm::dvec3 projectedVec = glm::proj(outwardStepVector, startPosToEndPos);
|
||||
const glm::dvec3 orthogonalComponent = outwardStepVector - projectedVec;
|
||||
const glm::dvec3 stepDirection = glm::normalize(orthogonalComponent);
|
||||
|
||||
// Step half-way along the line between the position and then orthogonally
|
||||
const glm::dvec3 extraKnot = start.position() + 0.5 * startPosToEndPos
|
||||
+ 1.5 * glm::length(startPosToEndPos) * stepDirection;
|
||||
|
||||
_points.push_back(extraKnot);
|
||||
}
|
||||
|
||||
// Closing in on end node
|
||||
_points.push_back(end.position() + endTangentLength * endTangentDir);
|
||||
_points.push_back(end.position());
|
||||
_points.push_back(end.position());
|
||||
|
||||
initializeParameterData();
|
||||
}
|
||||
|
||||
} // namespace openspace::pathnavigation
|
||||
@@ -1,41 +0,0 @@
|
||||
/*****************************************************************************************
|
||||
* *
|
||||
* OpenSpace *
|
||||
* *
|
||||
* Copyright (c) 2014-2021 *
|
||||
* *
|
||||
* 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___ZOOMOUTOVERVIEWCURVE___H__
|
||||
#define __OPENSPACE_MODULE_AUTONAVIGATION___ZOOMOUTOVERVIEWCURVE___H__
|
||||
|
||||
#include <modules/autonavigation/pathcurve.h>
|
||||
|
||||
namespace openspace::pathnavigation {
|
||||
|
||||
struct WayPoint;
|
||||
|
||||
class ZoomOutOverviewCurve : public PathCurve {
|
||||
public:
|
||||
ZoomOutOverviewCurve(const Waypoint& start, const Waypoint& end);
|
||||
};
|
||||
|
||||
} // namespace openspace::pathnavigation
|
||||
|
||||
#endif // __OPENSPACE_MODULE_AUTONAVIGATION___ZOOMOUTOVERVIEWCURVE___H__
|
||||
@@ -1,294 +0,0 @@
|
||||
/*****************************************************************************************
|
||||
* *
|
||||
* OpenSpace *
|
||||
* *
|
||||
* Copyright (c) 2014-2021 *
|
||||
* *
|
||||
* 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>
|
||||
#include <ghoul/logging/logmanager.h>
|
||||
#include <ghoul/misc/easing.h>
|
||||
|
||||
namespace {
|
||||
constexpr const char* _loggerCat = "Helpers";
|
||||
const double Epsilon = 1E-7;
|
||||
} // namespace
|
||||
|
||||
namespace openspace::pathnavigation::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));
|
||||
}
|
||||
|
||||
glm::dquat lookAtQuaternion(glm::dvec3 eye, glm::dvec3 center, glm::dvec3 up) {
|
||||
glm::dmat4 lookAtMat = glm::lookAt(eye, center, up);
|
||||
return glm::normalize(glm::inverse(glm::quat_cast(lookAtMat)));
|
||||
}
|
||||
|
||||
glm::dvec3 viewDirection(const glm::dquat& q) {
|
||||
return glm::normalize(q * glm::dvec3(0.0, 0.0, -1.0));
|
||||
};
|
||||
|
||||
/*
|
||||
* Calculate the intersection of a line and a sphere
|
||||
* The line segment is defined from p1 to p2
|
||||
* The sphere is of radius r and centered at sc
|
||||
* There are potentially two points of intersection given by
|
||||
* p = p1 + mu1 (p2 - p1)
|
||||
* p = p1 + mu2 (p2 - p1)
|
||||
* Source: http://paulbourke.net/geometry/circlesphere/raysphere.c
|
||||
*/
|
||||
bool lineSphereIntersection(glm::dvec3 p1, glm::dvec3 p2, glm::dvec3 sc,
|
||||
double r, glm::dvec3& intersectionPoint)
|
||||
{
|
||||
long double a, b, c;
|
||||
glm::dvec3 dp = p2 - p1;
|
||||
|
||||
a = dp.x * dp.x + dp.y * dp.y + dp.z * dp.z;
|
||||
b = 2 * (dp.x * (p1.x - sc.x) + dp.y * (p1.y - sc.y) + dp.z * (p1.z - sc.z));
|
||||
c = sc.x * sc.x + sc.y * sc.y + sc.z * sc.z;
|
||||
c += p1.x * p1.x + p1.y * p1.y + p1.z * p1.z;
|
||||
c -= 2 * (sc.x * p1.x + sc.y * p1.y + sc.z * p1.z);
|
||||
c -= r * r;
|
||||
|
||||
long double intersectionTest = b * b - 4.0 * a * c;
|
||||
|
||||
// no intersection
|
||||
if (std::abs(a) < Epsilon || intersectionTest < 0.0) {
|
||||
return false;
|
||||
}
|
||||
else {
|
||||
// only care about the first intersection point if we have two
|
||||
const double t = (-b - std::sqrt(intersectionTest)) / (2.0 *a);
|
||||
|
||||
if (t <= Epsilon || t >= abs(1.0 - Epsilon)) return false;
|
||||
|
||||
intersectionPoint = p1 + t * dp;
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
bool isPointInsideSphere(const glm::dvec3& p, const glm::dvec3& c, double r) {
|
||||
const glm::dvec3 v = c - p;
|
||||
const long double squaredDistance = v.x * v.x + v.y * v.y + v.z * v.z;
|
||||
const long double squaredRadius = r * r;
|
||||
|
||||
return (squaredDistance <= squaredRadius);
|
||||
}
|
||||
|
||||
double simpsonsRule(double t0, double t1, int n, std::function<double(double)> f) {
|
||||
const double h = (t1 - t0) / static_cast<double>(n);
|
||||
const double endpoints = f(t0) + f(t1);
|
||||
double times4 = 0.0;
|
||||
double times2 = 0.0;
|
||||
|
||||
// weight 4
|
||||
for (int i = 1; i < n; i += 2) {
|
||||
double t = t0 + i * h;
|
||||
times4 += f(t);
|
||||
}
|
||||
|
||||
// weight 2
|
||||
for (int i = 2; i < n; i += 2) {
|
||||
double t = t0 + i * h;
|
||||
times2 += f(t);
|
||||
}
|
||||
|
||||
return (h / 3) * (endpoints + 4 * times4 + 2 * times2);
|
||||
}
|
||||
|
||||
/*
|
||||
* Approximate area under a function using 5-point Gaussian quadrature
|
||||
* https://en.wikipedia.org/wiki/Gaussian_quadrature
|
||||
*/
|
||||
double fivePointGaussianQuadrature(double t0, double t1,
|
||||
std::function<double(double)> f)
|
||||
{
|
||||
struct GaussLengendreCoefficient {
|
||||
double abscissa; // xi
|
||||
double weight; // wi
|
||||
};
|
||||
|
||||
static constexpr GaussLengendreCoefficient coefficients[] = {
|
||||
{ 0.0, 0.5688889 },
|
||||
{ -0.5384693, 0.47862867 },
|
||||
{ 0.5384693, 0.47862867 },
|
||||
{ -0.90617985, 0.23692688 },
|
||||
{ 0.90617985, 0.23692688 }
|
||||
};
|
||||
|
||||
const double a = t0;
|
||||
const double b = t1;
|
||||
double sum = 0.0;
|
||||
for (auto coefficient : coefficients) {
|
||||
// change of interval to [a, b] from [-1, 1] (also 0.5 * (b - a) below)
|
||||
double const t = 0.5 * ((b - a) * coefficient.abscissa + (b + a));
|
||||
sum += f(t) * coefficient.weight;
|
||||
}
|
||||
return 0.5 * (b - a) * sum;
|
||||
}
|
||||
|
||||
} // helpers
|
||||
|
||||
namespace openspace::pathnavigation::interpolation {
|
||||
|
||||
glm::dquat easedSlerp(const glm::dquat q1, const glm::dquat q2, double t) {
|
||||
double tScaled = helpers::shiftAndScale(t, 0.1, 0.9);
|
||||
tScaled = ghoul::sineEaseInOut(tScaled);
|
||||
return glm::slerp(q1, q2, tScaled);
|
||||
}
|
||||
|
||||
// Based on implementation by Mika Rantanen
|
||||
// https://qroph.github.io/2018/07/30/smooth-paths-using-catmull-rom-splines.html
|
||||
glm::dvec3 catmullRom(double t, const glm::dvec3& p0, const glm::dvec3& p1,
|
||||
const glm::dvec3& p2, const glm::dvec3& p3, double alpha)
|
||||
{
|
||||
glm::dvec3 m01, m02, m23, m13;
|
||||
|
||||
double t01 = pow(glm::distance(p0, p1), alpha);
|
||||
double t12 = pow(glm::distance(p1, p2), alpha);
|
||||
double t23 = pow(glm::distance(p2, p3), alpha);
|
||||
|
||||
m01 = (t01 > Epsilon) ? (p1 - p0) / t01 : glm::dvec3{};
|
||||
m23 = (t23 > Epsilon) ? (p3 - p2) / t23 : glm::dvec3{};
|
||||
m02 = (t01 + t12 > Epsilon) ? (p2 - p0) / (t01 + t12) : glm::dvec3{};
|
||||
m13 = (t12 + t23 > Epsilon) ? (p3 - p1) / (t12 + t23) : glm::dvec3{};
|
||||
|
||||
glm::dvec3 m1 = p2 - p1 + t12 * (m01 - m02);
|
||||
glm::dvec3 m2 = p2 - p1 + t12 * (m23 - m13);
|
||||
|
||||
glm::dvec3 a = 2.0 * (p1 - p2) + m1 + m2;
|
||||
glm::dvec3 b = -3.0 * (p1 - p2) - m1 - m1 - m2;
|
||||
glm::dvec3 c = m1;
|
||||
glm::dvec3 d = p1;
|
||||
|
||||
return a * t * t * t
|
||||
+ b * t * t
|
||||
+ c * t
|
||||
+ d;
|
||||
}
|
||||
|
||||
glm::dvec3 cubicBezier(double t, const glm::dvec3& cp1, const glm::dvec3& cp2,
|
||||
const glm::dvec3& cp3, const glm::dvec3& cp4)
|
||||
{
|
||||
ghoul_assert(t >= 0 && t <= 1.0, "Interpolation variable out of range [0, 1]");
|
||||
|
||||
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 linear(double t, const glm::dvec3 &cp1, const glm::dvec3 &cp2) {
|
||||
ghoul_assert(t >= 0 && t <= 1.0, "Interpolation variable out of range [0, 1]");
|
||||
return cp1 * (1.0 - t) + cp2 * t;
|
||||
}
|
||||
|
||||
glm::dvec3 hermite(double t, const glm::dvec3 &p1, const glm::dvec3 &p2,
|
||||
const glm::dvec3 &tangent1, const glm::dvec3 &tangent2)
|
||||
{
|
||||
ghoul_assert(t >= 0 && t <= 1.0, "Interpolation variable out of range [0, 1]");
|
||||
|
||||
if (t <= 0.0) return p1;
|
||||
if (t >= 1.0) return p2;
|
||||
|
||||
const double t2 = t * t;
|
||||
const double t3 = t2 * t;
|
||||
|
||||
// calculate basis functions
|
||||
double const a0 = (2.0 * t3) - (3.0 * t2) + 1.0;
|
||||
double const a1 = (-2.0 * t3) + (3.0 * t2);
|
||||
double const b0 = t3 - (2.0 * t2) + t;
|
||||
double const b1 = t3 - t2;
|
||||
|
||||
return (a0 * p1) + (a1 * p2) + (b0 * tangent1) + (b1 * tangent2);
|
||||
}
|
||||
|
||||
// uniform if tKnots are equally spaced, or else non uniform
|
||||
glm::dvec3 piecewiseCubicBezier(double t, const std::vector<glm::dvec3>& points,
|
||||
const std::vector<double>& tKnots)
|
||||
{
|
||||
ghoul_assert(
|
||||
points.size() > 4,
|
||||
"Minimum of four control points needed for interpolation"
|
||||
);
|
||||
ghoul_assert(
|
||||
(points.size() - 1) % 3 == 0,
|
||||
"A vector containing 3n + 1 control points must be provided"
|
||||
);
|
||||
int nrSegments = (points.size() - 1) / 3;
|
||||
ghoul_assert(
|
||||
rSegments == (tKnots.size() - 1),
|
||||
"Number of interval times must match number of segments"
|
||||
);
|
||||
|
||||
if (t <= 0.0) return points.front();
|
||||
if (t >= 1.0) return points.back();
|
||||
|
||||
// compute current segment index
|
||||
std::vector<double>::const_iterator segmentEndIt =
|
||||
std::lower_bound(tKnots.begin(), tKnots.end(), t);
|
||||
unsigned int segmentIdx = (segmentEndIt - 1) - tKnots.begin();
|
||||
|
||||
double segmentStart = tKnots[segmentIdx];
|
||||
double segmentDuration = (tKnots[segmentIdx + 1] - tKnots[segmentIdx]);
|
||||
double tScaled = (t - segmentStart) / segmentDuration;
|
||||
|
||||
unsigned int idx = segmentIdx * 3;
|
||||
|
||||
// Interpolate using De Casteljau's algorithm
|
||||
return interpolation::cubicBezier(
|
||||
tScaled,
|
||||
points[idx],
|
||||
points[idx + 1],
|
||||
points[idx + 2],
|
||||
points[idx + 3]
|
||||
);
|
||||
}
|
||||
|
||||
glm::dvec3 piecewiseLinear(double t, const std::vector<glm::dvec3>& points,
|
||||
const std::vector<double>& tKnots)
|
||||
{
|
||||
ghoul_assert(points.size() == tKnots.size(), "Must have equal number of points and times!");
|
||||
ghoul_assert(points.size() > 2, "Minimum of two control points needed for interpolation!");
|
||||
|
||||
size_t nrSegments = points.size() - 1;
|
||||
|
||||
if (t <= 0.0) return points.front();
|
||||
if (t >= 1.0) return points.back();
|
||||
|
||||
// compute current segment index
|
||||
std::vector<double>::const_iterator segmentEndIt =
|
||||
std::lower_bound(tKnots.begin(), tKnots.end(), t);
|
||||
unsigned int idx = (segmentEndIt - 1) - tKnots.begin();
|
||||
|
||||
double segmentStart = tKnots[idx];
|
||||
double segmentDuration = (tKnots[idx + 1] - tKnots[idx]);
|
||||
double tScaled = (t - segmentStart) / segmentDuration;
|
||||
|
||||
return interpolation::linear(tScaled, points[idx], points[idx + 1]);
|
||||
}
|
||||
|
||||
} // interpolation
|
||||
@@ -1,83 +0,0 @@
|
||||
/*****************************************************************************************
|
||||
* *
|
||||
* OpenSpace *
|
||||
* *
|
||||
* Copyright (c) 2014-2021 *
|
||||
* *
|
||||
* 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___HELPERFUNCTIONS___H__
|
||||
#define __OPENSPACE_MODULE_AUTONAVIGATION___HELPERFUNCTIONS___H__
|
||||
|
||||
#include <ghoul/glm.h>
|
||||
#include <ghoul/logging/logmanager.h>
|
||||
#include <ghoul/misc/assert.h>
|
||||
#include <algorithm>
|
||||
#include <cmath>
|
||||
#include <functional>
|
||||
#include <vector>
|
||||
|
||||
namespace openspace::pathnavigation::helpers {
|
||||
|
||||
// Make interpolator parameter t [0,1] progress only inside a subinterval
|
||||
double shiftAndScale(double t, double newStart, double newEnd);
|
||||
|
||||
glm::dquat lookAtQuaternion(glm::dvec3 eye, glm::dvec3 center, glm::dvec3 up);
|
||||
|
||||
glm::dvec3 viewDirection(const glm::dquat& q);
|
||||
|
||||
bool lineSphereIntersection(glm::dvec3 linePoint1, glm::dvec3 linePoint2,
|
||||
glm::dvec3 sphereCenter, double spehereRadius, glm::dvec3& intersectionPoint);
|
||||
|
||||
bool isPointInsideSphere(const glm::dvec3& p, const glm::dvec3& c, double r);
|
||||
|
||||
double simpsonsRule(double t0, double t1, int n, std::function<double(double)> f);
|
||||
|
||||
double fivePointGaussianQuadrature(double t0, double t1,
|
||||
std::function<double(double)> f);
|
||||
} // namespace openspace::pathnavigation::helpers
|
||||
|
||||
namespace openspace::pathnavigation::interpolation {
|
||||
|
||||
glm::dquat easedSlerp(const glm::dquat q1, const glm::dquat q2, double t);
|
||||
|
||||
// TODO: make all these into template functions.
|
||||
// Alternatively, add cubicBezier interpolation in ghoul and only use
|
||||
// ghoul's interpolator methods
|
||||
|
||||
// Centripetal version alpha = 0, uniform for alpha = 0.5 and chordal for alpha = 1
|
||||
glm::dvec3 catmullRom(double t, const glm::dvec3& p0, const glm::dvec3& p1,
|
||||
const glm::dvec3& p2, const glm::dvec3& p3, double alpha = 0.5);
|
||||
|
||||
glm::dvec3 cubicBezier(double t, const glm::dvec3& cp1, const glm::dvec3& cp2,
|
||||
const glm::dvec3& cp3, const glm::dvec3& cp4);
|
||||
|
||||
glm::dvec3 linear(double t, const glm::dvec3& cp1, const glm::dvec3& cp2);
|
||||
|
||||
glm::dvec3 hermite(double t, const glm::dvec3 &cp1, const glm::dvec3 &cp2,
|
||||
const glm::dvec3 &tangent1, const glm::dvec3 &tangent2);
|
||||
|
||||
glm::dvec3 piecewiseCubicBezier(double t, const std::vector<glm::dvec3>& points,
|
||||
const std::vector<double>& tKnots);
|
||||
|
||||
glm::dvec3 piecewiseLinear(double t, const std::vector<glm::dvec3>& points,
|
||||
const std::vector<double>& tKnots);
|
||||
|
||||
} // namespace openspace::pathnavigation::interpolation
|
||||
#endif // __OPENSPACE_MODULE_AUTONAVIGATION___HELPERFUNCTIONS___H__
|
||||
@@ -1,5 +0,0 @@
|
||||
set(DEFAULT_MODULE ON)
|
||||
|
||||
set(OPENSPACE_DEPENDENCIES
|
||||
#add possible other modules that this module depends upon
|
||||
)
|
||||
@@ -1,222 +0,0 @@
|
||||
/*****************************************************************************************
|
||||
* *
|
||||
* OpenSpace *
|
||||
* *
|
||||
* Copyright (c) 2014-2021 *
|
||||
* *
|
||||
* 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/path.h>
|
||||
|
||||
#include <modules/autonavigation/autonavigationmodule.h>
|
||||
#include <modules/autonavigation/helperfunctions.h>
|
||||
#include <modules/autonavigation/pathcurve.h>
|
||||
#include <modules/autonavigation/curves/avoidcollisioncurve.h>
|
||||
#include <modules/autonavigation/curves/zoomoutoverviewcurve.h>
|
||||
#include <openspace/engine/globals.h>
|
||||
#include <openspace/engine/moduleengine.h>
|
||||
#include <openspace/scene/scenegraphnode.h>
|
||||
#include <ghoul/logging/logmanager.h>
|
||||
#include <ghoul/misc/interpolator.h>
|
||||
|
||||
namespace {
|
||||
constexpr const char* _loggerCat = "Path";
|
||||
} // namespace
|
||||
|
||||
namespace openspace::pathnavigation {
|
||||
|
||||
Path::Path(Waypoint start, Waypoint end, CurveType type,
|
||||
std::optional<double> duration)
|
||||
: _start(start), _end(end), _curveType(type)
|
||||
{
|
||||
switch (_curveType) {
|
||||
case CurveType::AvoidCollision:
|
||||
_curve = std::make_unique<AvoidCollisionCurve>(_start, _end);
|
||||
break;
|
||||
case CurveType::Linear:
|
||||
_curve = std::make_unique<LinearCurve>(_start, _end);
|
||||
break;
|
||||
case CurveType::ZoomOutOverview:
|
||||
_curve = std::make_unique<ZoomOutOverviewCurve>(_start, _end);
|
||||
break;
|
||||
default:
|
||||
LERROR("Could not create curve. Type does not exist!");
|
||||
throw ghoul::MissingCaseException();
|
||||
}
|
||||
|
||||
const auto defaultDuration = [](double pathlength) {
|
||||
auto module = global::moduleEngine->module<AutoNavigationModule>();
|
||||
const double speedScale = module->PathNavigationHandler().speedScale();
|
||||
return std::log(pathlength) / speedScale;
|
||||
};
|
||||
|
||||
_duration = duration.value_or(defaultDuration(pathLength()));
|
||||
|
||||
// Compute speed factor to match the generated path length and duration, by
|
||||
// traversing the path and computing how much faster/slower it should be
|
||||
const int nSteps = 500;
|
||||
const double dt = (_duration / nSteps) > 0.01 ? (_duration / nSteps) : 0.01;
|
||||
|
||||
while (!hasReachedEnd()) {
|
||||
traversePath(dt);
|
||||
}
|
||||
|
||||
_speedFactorFromDuration = _progressedTime / _duration;
|
||||
|
||||
// Reset playback variables
|
||||
_traveledDistance = 0.0;
|
||||
_progressedTime = 0.0;
|
||||
}
|
||||
|
||||
Waypoint Path::startPoint() const { return _start; }
|
||||
|
||||
Waypoint Path::endPoint() const { return _end; }
|
||||
|
||||
double Path::duration() const { return _duration; }
|
||||
|
||||
double Path::pathLength() const { return _curve->length(); }
|
||||
|
||||
std::vector<glm::dvec3> Path::controlPoints() const {
|
||||
return _curve->points();
|
||||
}
|
||||
|
||||
CameraPose Path::traversePath(double dt) {
|
||||
const double speed = _speedFactorFromDuration * speedAlongPath(_traveledDistance);
|
||||
const double displacement = dt * speed;
|
||||
|
||||
_progressedTime += dt;
|
||||
_traveledDistance += displacement;
|
||||
|
||||
return interpolatedPose(_traveledDistance);
|
||||
}
|
||||
|
||||
std::string Path::currentAnchor() const {
|
||||
bool pastHalfway = (_traveledDistance / pathLength()) > 0.5;
|
||||
return (pastHalfway) ? _end.nodeIdentifier : _start.nodeIdentifier;
|
||||
}
|
||||
|
||||
bool Path::hasReachedEnd() const {
|
||||
return (_traveledDistance / pathLength()) >= 1.0;
|
||||
}
|
||||
|
||||
CameraPose Path::interpolatedPose(double distance) const {
|
||||
const double relativeDistance = distance / pathLength();
|
||||
CameraPose cs;
|
||||
cs.position = _curve->positionAt(relativeDistance);
|
||||
cs.rotation = interpolateRotation(relativeDistance);
|
||||
return cs;
|
||||
}
|
||||
|
||||
glm::dquat Path::interpolateRotation(double t) const {
|
||||
switch (_curveType) {
|
||||
case CurveType::AvoidCollision:
|
||||
case CurveType::Linear:
|
||||
return interpolation::easedSlerp(_start.rotation(), _end.rotation(), t);
|
||||
case CurveType::ZoomOutOverview:
|
||||
{
|
||||
const double t1 = 0.2;
|
||||
const double t2 = 0.8;
|
||||
|
||||
const glm::dvec3 startPos = _curve->positionAt(0.0);
|
||||
const glm::dvec3 endPos = _curve->positionAt(1.0);
|
||||
const glm::dvec3 startNodePos = _start.node()->worldPosition();
|
||||
const glm::dvec3 endNodePos = _end.node()->worldPosition();
|
||||
|
||||
glm::dvec3 lookAtPos;
|
||||
if (t < t1) {
|
||||
// Compute a position in front of the camera at the start orientation
|
||||
const double inFrontDistance = glm::distance(startPos, startNodePos);
|
||||
const glm::dvec3 viewDir = helpers::viewDirection(_start.rotation());
|
||||
const glm::dvec3 inFrontOfStart = startPos + inFrontDistance * viewDir;
|
||||
|
||||
const double tScaled = ghoul::cubicEaseInOut(t / t1);
|
||||
lookAtPos =
|
||||
ghoul::interpolateLinear(tScaled, inFrontOfStart, startNodePos);
|
||||
}
|
||||
else if (t <= t2) {
|
||||
const double tScaled = ghoul::cubicEaseInOut((t - t1) / (t2 - t1));
|
||||
lookAtPos = ghoul::interpolateLinear(tScaled, startNodePos, endNodePos);
|
||||
}
|
||||
else if (t > t2) {
|
||||
// Compute a position in front of the camera at the end orientation
|
||||
const double inFrontDistance = glm::distance(endPos, endNodePos);
|
||||
const glm::dvec3 viewDir = helpers::viewDirection(_end.rotation());
|
||||
const glm::dvec3 inFrontOfEnd = endPos + inFrontDistance * viewDir;
|
||||
|
||||
const double tScaled = ghoul::cubicEaseInOut((t - t2) / (1.0 - t2));
|
||||
lookAtPos = ghoul::interpolateLinear(tScaled, endNodePos, inFrontOfEnd);
|
||||
}
|
||||
|
||||
// Handle up vector separately
|
||||
glm::dvec3 startUp = _start.rotation() * glm::dvec3(0.0, 1.0, 0.0);
|
||||
glm::dvec3 endUp = _end.rotation() * glm::dvec3(0.0, 1.0, 0.0);
|
||||
|
||||
double tUp = helpers::shiftAndScale(t, t1, t2);
|
||||
tUp = ghoul::sineEaseInOut(tUp);
|
||||
glm::dvec3 up = ghoul::interpolateLinear(tUp, startUp, endUp);
|
||||
|
||||
return helpers::lookAtQuaternion(_curve->positionAt(t), lookAtPos, up);
|
||||
}
|
||||
default:
|
||||
throw ghoul::MissingCaseException();
|
||||
}
|
||||
}
|
||||
|
||||
double Path::speedAlongPath(double traveledDistance) {
|
||||
const glm::dvec3 endNodePos = _end.node()->worldPosition();
|
||||
const glm::dvec3 startNodePos = _start.node()->worldPosition();
|
||||
|
||||
const CameraPose prevPose = interpolatedPose(traveledDistance);
|
||||
const double distanceToEndNode = glm::distance(prevPose.position, endNodePos);
|
||||
const double distanceToStartNode = glm::distance(prevPose.position, startNodePos);
|
||||
|
||||
// Decide which is the closest node
|
||||
SceneGraphNode* closestNode = _start.node();
|
||||
glm::dvec3 closestPos = startNodePos;
|
||||
|
||||
if (distanceToEndNode < distanceToStartNode) {
|
||||
closestPos = endNodePos;
|
||||
closestNode = _end.node();
|
||||
}
|
||||
|
||||
const double distanceToClosestNode = glm::distance(closestPos, prevPose.position);
|
||||
double speed = distanceToClosestNode;
|
||||
|
||||
// Dampen speed in beginning of path
|
||||
const double startUpDistance = 2.0 * _start.node()->boundingSphere();
|
||||
if (traveledDistance < startUpDistance) {
|
||||
speed *= traveledDistance / startUpDistance + 0.01;
|
||||
}
|
||||
|
||||
// Dampen speed in end of path
|
||||
// Note: this leads to problems when the full length of the path is really big
|
||||
const double closeUpDistance = 2.0 * _end.node()->boundingSphere();
|
||||
if (traveledDistance > (pathLength() - closeUpDistance)) {
|
||||
const double remainingDistance = pathLength() - traveledDistance;
|
||||
speed *= remainingDistance / closeUpDistance + 0.01;
|
||||
}
|
||||
|
||||
// TODO: also dampen speed based on curvature, or make sure the curve has a rounder shape
|
||||
|
||||
// TODO: check for when path is shorter than the starUpDistance or closeUpDistance variables
|
||||
|
||||
return speed;
|
||||
}
|
||||
|
||||
} // namespace openspace::pathnavigation
|
||||
@@ -1,79 +0,0 @@
|
||||
/*****************************************************************************************
|
||||
* *
|
||||
* OpenSpace *
|
||||
* *
|
||||
* Copyright (c) 2014-2021 *
|
||||
* *
|
||||
* 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___PATH___H__
|
||||
#define __OPENSPACE_MODULE_AUTONAVIGATION___PATH___H__
|
||||
|
||||
#include <modules/autonavigation/pathcurve.h>
|
||||
#include <modules/autonavigation/waypoint.h>
|
||||
|
||||
namespace openspace::pathnavigation {
|
||||
|
||||
class Path {
|
||||
public:
|
||||
enum CurveType {
|
||||
AvoidCollision,
|
||||
Linear,
|
||||
ZoomOutOverview
|
||||
};
|
||||
|
||||
Path(Waypoint start, Waypoint end, CurveType type,
|
||||
std::optional<double> duration = std::nullopt);
|
||||
|
||||
// TODO: add a constructor that takes an instruction and curve type?
|
||||
|
||||
Waypoint startPoint() const;
|
||||
Waypoint endPoint() const;
|
||||
double duration() const;
|
||||
double pathLength() const;
|
||||
|
||||
std::vector<glm::dvec3> controlPoints() const;
|
||||
|
||||
CameraPose traversePath(double dt);
|
||||
std::string currentAnchor() const;
|
||||
bool hasReachedEnd() const;
|
||||
|
||||
CameraPose interpolatedPose(double distance) const;
|
||||
|
||||
private:
|
||||
glm::dquat interpolateRotation(double u) const;
|
||||
double speedAlongPath(double traveledDistance);
|
||||
|
||||
Waypoint _start;
|
||||
Waypoint _end;
|
||||
double _duration;
|
||||
CurveType _curveType;
|
||||
|
||||
std::unique_ptr<PathCurve> _curve;
|
||||
|
||||
double _speedFactorFromDuration = 1.0;
|
||||
|
||||
// Playback variables
|
||||
double _traveledDistance = 0.0;
|
||||
double _progressedTime = 0.0; // Time since playback started
|
||||
};
|
||||
|
||||
} // namespace openspace::pathnavigation
|
||||
|
||||
#endif // __OPENSPACE_MODULE_AUTONAVIGATION___PATH___H__
|
||||
@@ -1,251 +0,0 @@
|
||||
/*****************************************************************************************
|
||||
* *
|
||||
* OpenSpace *
|
||||
* *
|
||||
* Copyright (c) 2014-2021 *
|
||||
* *
|
||||
* 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/pathcreator.h>
|
||||
|
||||
#include <modules/autonavigation/autonavigationmodule.h>
|
||||
#include <modules/autonavigation/helperfunctions.h>
|
||||
#include <modules/autonavigation/waypoint.h>
|
||||
#include <openspace/engine/globals.h>
|
||||
#include <openspace/engine/moduleengine.h>
|
||||
#include <openspace/navigation/navigationhandler.h>
|
||||
#include <openspace/scene/scenegraphnode.h>
|
||||
#include <openspace/query/query.h>
|
||||
#include <openspace/util/camera.h>
|
||||
#include <ghoul/logging/logmanager.h>
|
||||
|
||||
namespace {
|
||||
constexpr const char* _loggerCat = "PathCreator";
|
||||
constexpr const float Epsilon = 1e-5f;
|
||||
|
||||
// TODO: where should this documentation be?
|
||||
struct [[codegen::Dictionary(PathInstruction)]] Parameters {
|
||||
enum class Type {
|
||||
Node,
|
||||
NavigationState
|
||||
};
|
||||
Type type;
|
||||
|
||||
// The desired duration traversing the specified path segment should take
|
||||
std::optional<float> duration;
|
||||
|
||||
// (Node): The target node of the camera path. Not optional for 'Node' instructions
|
||||
std::optional<std::string> target;
|
||||
|
||||
// (Node): An optional position in relation to the target node, in model
|
||||
// coordinates (meters)
|
||||
std::optional<glm::dvec3> position;
|
||||
|
||||
// (Node): An optional height in relation to the target node, in meters
|
||||
std::optional<double> height;
|
||||
|
||||
// (Node): If true, the up direction of the node is taken into account when
|
||||
// computing the wayopoint for this instruction
|
||||
std::optional<bool> useTargetUpDirection;
|
||||
|
||||
// (NavigationState): A navigation state that will be the target
|
||||
// of this path segment
|
||||
std::optional<ghoul::Dictionary> navigationState
|
||||
[[codegen::reference("core_navigation_state")]];
|
||||
|
||||
// A navigation state that determines the start state for the camera path
|
||||
std::optional<ghoul::Dictionary> startState
|
||||
[[codegen::reference("core_navigation_state")]];
|
||||
};
|
||||
#include "pathinstruction_codegen.cpp"
|
||||
} // namespace
|
||||
|
||||
namespace openspace::pathnavigation {
|
||||
|
||||
using NavigationState = interaction::NavigationHandler::NavigationState;
|
||||
|
||||
Path PathCreator::createPath(const ghoul::Dictionary& dictionary,
|
||||
Path::CurveType curveType)
|
||||
{
|
||||
const Parameters p = codegen::bake<Parameters>(dictionary);
|
||||
|
||||
std::optional<float> duration = p.duration;
|
||||
|
||||
bool hasStart = p.startState.has_value();
|
||||
Waypoint startPoint = hasStart ? Waypoint(p.startState.value()) : waypointFromCamera();
|
||||
|
||||
// TODO: also handle curve type here
|
||||
|
||||
std::vector<Waypoint> waypoints;
|
||||
switch (p.type) {
|
||||
case Parameters::Type::NavigationState: {
|
||||
if (!p.navigationState.has_value()) {
|
||||
throw ghoul::RuntimeError("A navigation state is required");
|
||||
}
|
||||
|
||||
const NavigationState navigationState =
|
||||
NavigationState(p.navigationState.value());
|
||||
|
||||
waypoints = { Waypoint(navigationState) };
|
||||
break;
|
||||
}
|
||||
case Parameters::Type::Node: {
|
||||
if (!p.target.has_value()) {
|
||||
throw ghoul::RuntimeError("A target node is required");
|
||||
}
|
||||
|
||||
const std::string nodeIdentifier = p.target.value();
|
||||
const SceneGraphNode* targetNode = sceneGraphNode(nodeIdentifier);
|
||||
|
||||
if (!targetNode) {
|
||||
throw ghoul::RuntimeError(fmt::format(
|
||||
"Could not find target node '{}'", nodeIdentifier
|
||||
));
|
||||
}
|
||||
|
||||
NodeInfo info {
|
||||
nodeIdentifier,
|
||||
p.position,
|
||||
p.height,
|
||||
p.useTargetUpDirection.value_or(false)
|
||||
};
|
||||
|
||||
waypoints = { computeDefaultWaypoint(info, startPoint) };
|
||||
break;
|
||||
}
|
||||
default: {
|
||||
LERROR(fmt::format("Uknown instruciton type: {}", p.type));
|
||||
throw ghoul::MissingCaseException();
|
||||
}
|
||||
}
|
||||
|
||||
// TODO: allow for an instruction to represent a list of waypoints
|
||||
Waypoint waypointToAdd = waypoints[0];
|
||||
|
||||
return Path(startPoint, waypointToAdd, curveType, duration);
|
||||
}
|
||||
|
||||
Waypoint PathCreator::waypointFromCamera() {
|
||||
Camera* camera = global::navigationHandler->camera();
|
||||
const glm::dvec3 pos = camera->positionVec3();
|
||||
const glm::dquat rot = camera->rotationQuaternion();
|
||||
const std::string node = global::navigationHandler->anchorNode()->identifier();
|
||||
return Waypoint{ pos, rot, node };
|
||||
}
|
||||
|
||||
Waypoint PathCreator::computeDefaultWaypoint(const NodeInfo& info,
|
||||
const Waypoint& startPoint)
|
||||
{
|
||||
const SceneGraphNode* targetNode = sceneGraphNode(info.identifier);
|
||||
if (!targetNode) {
|
||||
LERROR(fmt::format("Could not find target node '{}'", info.identifier));
|
||||
return Waypoint();
|
||||
}
|
||||
|
||||
glm::dvec3 targetPos;
|
||||
if (info.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() * info.position.value();
|
||||
}
|
||||
else {
|
||||
const glm::dvec3 nodePos = targetNode->worldPosition();
|
||||
const glm::dvec3 sunPos = glm::dvec3(0.0, 0.0, 0.0);
|
||||
const SceneGraphNode* closeNode = findNodeNearTarget(targetNode);
|
||||
|
||||
glm::dvec3 stepDirection;
|
||||
if (closeNode) {
|
||||
// If the node is close to another node in the scene, make sure that the
|
||||
// position is set to minimize risk of collision
|
||||
stepDirection = glm::normalize(nodePos - closeNode->worldPosition());
|
||||
}
|
||||
else if (glm::length(sunPos - nodePos) < Epsilon) {
|
||||
// Special case for when the target is the Sun. Assumption: want an overview of
|
||||
// the solar system, and not stay in the orbital plane
|
||||
stepDirection = glm::dvec3(0.0, 0.0, 1.0);
|
||||
}
|
||||
else {
|
||||
// Go to a point that is being lit up by the sun, slightly offsetted from sun
|
||||
// direction
|
||||
const glm::dvec3 prevPos = startPoint.position();
|
||||
const glm::dvec3 targetToPrev = prevPos - nodePos;
|
||||
const glm::dvec3 targetToSun = sunPos - nodePos;
|
||||
|
||||
constexpr const float defaultPositionOffsetAngle = -30.f; // degrees
|
||||
constexpr const float angle = glm::radians(defaultPositionOffsetAngle);
|
||||
const glm::dvec3 axis = glm::normalize(glm::cross(targetToPrev, targetToSun));
|
||||
const glm::dquat offsetRotation = angleAxis(static_cast<double>(angle), axis);
|
||||
|
||||
stepDirection = glm::normalize(offsetRotation * targetToSun);
|
||||
}
|
||||
|
||||
const double radius = Waypoint::findValidBoundingSphere(targetNode);
|
||||
const double defaultHeight = 2.0 * radius;
|
||||
const double height = info.height.value_or(defaultHeight);
|
||||
|
||||
targetPos = nodePos + stepDirection * (radius + height);
|
||||
}
|
||||
|
||||
glm::dvec3 up = global::navigationHandler->camera()->lookUpVectorWorldSpace();
|
||||
if (info.useTargetUpDirection) {
|
||||
// @TODO (emmbr 2020-11-17) For now, this is hardcoded to look good for Earth,
|
||||
// which is where it matters the most. A better solution would be to make each
|
||||
// sgn aware of its own 'up' and query
|
||||
up = targetNode->worldRotationMatrix() * glm::dvec3(0.0, 0.0, 1.0);
|
||||
}
|
||||
|
||||
const glm::dvec3 lookAtPos = targetNode->worldPosition();
|
||||
const glm::dquat targetRot = helpers::lookAtQuaternion(targetPos, lookAtPos, up);
|
||||
|
||||
return Waypoint(targetPos, targetRot, info.identifier);
|
||||
}
|
||||
|
||||
SceneGraphNode* PathCreator::findNodeNearTarget(const SceneGraphNode* node) {
|
||||
auto module = global::moduleEngine->module<AutoNavigationModule>();
|
||||
const std::vector<SceneGraphNode*>& relevantNodes =
|
||||
module->PathNavigationHandler().relevantNodes();
|
||||
|
||||
for (SceneGraphNode* n : relevantNodes) {
|
||||
if (n->identifier() == node->identifier()) {
|
||||
continue;
|
||||
}
|
||||
|
||||
constexpr const float proximityRadiusFactor = 3.f;
|
||||
|
||||
const float bs = static_cast<float>(n->boundingSphere());
|
||||
const float proximityRadius = proximityRadiusFactor * bs;
|
||||
const glm::dvec3 posInModelCoords =
|
||||
glm::inverse(n->modelTransform()) * glm::dvec4(node->worldPosition(), 1.0);
|
||||
|
||||
bool isClose = helpers::isPointInsideSphere(
|
||||
posInModelCoords,
|
||||
glm::dvec3(0.0, 0.0, 0.0),
|
||||
proximityRadius
|
||||
);
|
||||
|
||||
if (isClose) {
|
||||
return n;
|
||||
}
|
||||
}
|
||||
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
} // namespace openspace::pathnavigation
|
||||
@@ -1,59 +0,0 @@
|
||||
/*****************************************************************************************
|
||||
* *
|
||||
* OpenSpace *
|
||||
* *
|
||||
* Copyright (c) 2014-2021 *
|
||||
* *
|
||||
* 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___PATHCREATOR___H__
|
||||
#define __OPENSPACE_MODULE_AUTONAVIGATION___PATHCREATOR___H__
|
||||
|
||||
#include <modules/autonavigation/path.h>
|
||||
|
||||
namespace openspace::pathnavigation {
|
||||
|
||||
struct Waypoint;
|
||||
|
||||
class PathCreator {
|
||||
public:
|
||||
// Create a path from a dictionary containing the instruction
|
||||
static Path createPath(const ghoul::Dictionary& dictionary,
|
||||
Path::CurveType curveType);
|
||||
|
||||
private:
|
||||
struct NodeInfo {
|
||||
std::string identifier;
|
||||
std::optional<glm::dvec3> position;
|
||||
std::optional<double> height;
|
||||
bool useTargetUpDirection;
|
||||
};
|
||||
|
||||
static Waypoint waypointFromCamera();
|
||||
static Waypoint computeDefaultWaypoint(const NodeInfo& info,
|
||||
const Waypoint& startPoint);
|
||||
|
||||
// Test if the node lies within a given proximity radius of any relevant node
|
||||
// in the scene
|
||||
static SceneGraphNode* findNodeNearTarget(const SceneGraphNode* node);
|
||||
};
|
||||
|
||||
} // namespace openspace::pathnavigation
|
||||
|
||||
#endif // __OPENSPACE_MODULE_AUTONAVIGATION___PATHCREATOR___H__
|
||||
@@ -1,235 +0,0 @@
|
||||
/*****************************************************************************************
|
||||
* *
|
||||
* OpenSpace *
|
||||
* *
|
||||
* Copyright (c) 2014-2021 *
|
||||
* *
|
||||
* 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/pathcurve.h>
|
||||
|
||||
#include <modules/autonavigation/helperfunctions.h>
|
||||
#include <openspace/query/query.h>
|
||||
#include <openspace/scene/scenegraphnode.h>
|
||||
#include <ghoul/logging/logmanager.h>
|
||||
#include <glm/gtx/projection.hpp>
|
||||
#include <algorithm>
|
||||
#include <vector>
|
||||
|
||||
namespace {
|
||||
constexpr const char* _loggerCat = "PathCurve";
|
||||
constexpr const int NrSamplesPerSegment = 100;
|
||||
} // namespace
|
||||
|
||||
namespace openspace::pathnavigation {
|
||||
|
||||
PathCurve::~PathCurve() {}
|
||||
|
||||
const double PathCurve::length() const {
|
||||
return _totalLength;
|
||||
}
|
||||
|
||||
glm::dvec3 PathCurve::positionAt(double relativeDistance) {
|
||||
const double u = curveParameter(relativeDistance * _totalLength);
|
||||
return interpolate(u);
|
||||
}
|
||||
|
||||
// Compute the curve parameter from an arc length value, using a combination of
|
||||
// Newton's method and bisection. Source:
|
||||
// https://www.geometrictools.com/Documentation/MovingAlongCurveSpecifiedSpeed.pdf
|
||||
// Input s is a length value, in the range [0, _totalLength]
|
||||
// Returns curve parameter in range [0, 1]
|
||||
double PathCurve::curveParameter(double s) {
|
||||
if (s <= 0.0) return 0.0;
|
||||
if (s >= _totalLength) return 1.0;
|
||||
|
||||
unsigned int segmentIndex = 1;
|
||||
while (s > _lengthSums[segmentIndex]) {
|
||||
segmentIndex++;
|
||||
}
|
||||
|
||||
const int startIndex = (segmentIndex - 1) * NrSamplesPerSegment;
|
||||
const int endIndex = segmentIndex * NrSamplesPerSegment + 1;
|
||||
|
||||
const double segmentS = s - _lengthSums[segmentIndex - 1];
|
||||
const double uMin = _curveParameterSteps[segmentIndex - 1];
|
||||
const double uMax = _curveParameterSteps[segmentIndex];
|
||||
|
||||
// Use samples to find an initial guess for Newton's method
|
||||
// Find first sample with s larger than input s
|
||||
auto sampleIterator = std::upper_bound(
|
||||
_parameterSamples.begin() + startIndex,
|
||||
_parameterSamples.begin() + endIndex,
|
||||
ParameterPair{ 0.0 , s }, // 0.0 is a dummy value for u
|
||||
[](ParameterPair lhs, ParameterPair rhs) {
|
||||
return lhs.s < rhs.s;
|
||||
}
|
||||
);
|
||||
|
||||
const ParameterPair& sample = *sampleIterator;
|
||||
const ParameterPair& prevSample = *(sampleIterator - 1);
|
||||
const double uPrev = prevSample.u;
|
||||
const double sPrev = prevSample.s;
|
||||
const double slope = (sample.u - uPrev) / (sample.s - sPrev);
|
||||
double u = uPrev + slope * (s - sPrev);
|
||||
|
||||
constexpr const int maxIterations = 50;
|
||||
|
||||
// Initialize root bounding limits for bisection
|
||||
double lower = uMin;
|
||||
double upper = uMax;
|
||||
|
||||
for (int i = 0; i < maxIterations; ++i) {
|
||||
double F = arcLength(uMin, u) - segmentS;
|
||||
|
||||
// The error we tolerate, in meters. Note that distances are very large
|
||||
constexpr const double tolerance = 0.005;
|
||||
if (std::abs(F) <= tolerance) {
|
||||
return u;
|
||||
}
|
||||
|
||||
// Generate a candidate for Newton's method
|
||||
double dfdu = approximatedDerivative(u); // > 0
|
||||
double uCandidate = u - F / dfdu;
|
||||
|
||||
// Update root-bounding interval and test candidate
|
||||
if (F > 0) { // => candidate < u <= upper
|
||||
upper = u;
|
||||
u = (uCandidate <= lower) ? (upper + lower) / 2.0 : uCandidate;
|
||||
}
|
||||
else { // F < 0 => lower <= u < candidate
|
||||
lower = u;
|
||||
u = (uCandidate >= upper) ? (upper + lower) / 2.0 : uCandidate;
|
||||
}
|
||||
}
|
||||
|
||||
// No root was found based on the number of iterations and tolerance. However, it is
|
||||
// safe to report the last computed u value, since it is within the segment interval
|
||||
return u;
|
||||
}
|
||||
|
||||
std::vector<glm::dvec3> PathCurve::points() {
|
||||
return _points;
|
||||
}
|
||||
|
||||
void PathCurve::initializeParameterData() {
|
||||
_nSegments = static_cast<int>(_points.size() - 3);
|
||||
|
||||
ghoul_assert(_nSegments > 0, "Cannot have a curve with zero segments!");
|
||||
|
||||
_curveParameterSteps.clear();
|
||||
_lengthSums.clear();
|
||||
_parameterSamples.clear();
|
||||
|
||||
// Evenly space out parameter intervals
|
||||
_curveParameterSteps.reserve(_nSegments + 1);
|
||||
const double dt = 1.0 / _nSegments;
|
||||
_curveParameterSteps.push_back(0.0);
|
||||
for (unsigned int i = 1; i < _nSegments; i++) {
|
||||
_curveParameterSteps.push_back(dt * i);
|
||||
}
|
||||
_curveParameterSteps.push_back(1.0);
|
||||
|
||||
// Arc lengths
|
||||
_lengthSums.reserve(_nSegments + 1);
|
||||
_lengthSums.push_back(0.0);
|
||||
for (unsigned int i = 1; i <= _nSegments; i++) {
|
||||
double u = _curveParameterSteps[i];
|
||||
double uPrev = _curveParameterSteps[i - 1];
|
||||
double length = arcLength(uPrev, u);
|
||||
_lengthSums.push_back(_lengthSums[i - 1] + length);
|
||||
}
|
||||
_totalLength = _lengthSums.back();
|
||||
|
||||
// Compute a map of arc lengths s and curve parameters u, for reparameterization
|
||||
_parameterSamples.reserve(NrSamplesPerSegment * _nSegments + 1);
|
||||
const double uStep = 1.0 / (_nSegments * NrSamplesPerSegment);
|
||||
for (unsigned int i = 0; i < _nSegments; i++) {
|
||||
double uStart = _curveParameterSteps[i];
|
||||
double sStart = _lengthSums[i];
|
||||
for (int j = 0; j < NrSamplesPerSegment; ++j) {
|
||||
double u = uStart + j * uStep;
|
||||
double s = sStart + arcLength(uStart, u);
|
||||
_parameterSamples.push_back({ u, s });
|
||||
}
|
||||
}
|
||||
_parameterSamples.push_back({ 1.0, _totalLength });
|
||||
}
|
||||
|
||||
double PathCurve::approximatedDerivative(double u, double h) {
|
||||
if (u <= h) {
|
||||
return (1.0 / h) * glm::length(interpolate(0.0 + h) - interpolate(0.0));
|
||||
}
|
||||
if (u >= 1.0 - h) {
|
||||
return (1.0 / h) * glm::length(interpolate(1.0) - interpolate(1.0 - h));
|
||||
}
|
||||
return (0.5 / h) * glm::length(interpolate(u + h) - interpolate(u - h));
|
||||
}
|
||||
|
||||
double PathCurve::arcLength(double limit) {
|
||||
return arcLength(0.0, limit);
|
||||
}
|
||||
|
||||
double PathCurve::arcLength(double lowerLimit, double upperLimit) {
|
||||
return helpers::fivePointGaussianQuadrature(
|
||||
lowerLimit,
|
||||
upperLimit,
|
||||
[this](double u) { return approximatedDerivative(u); }
|
||||
);
|
||||
}
|
||||
|
||||
glm::dvec3 PathCurve::interpolate(double u) {
|
||||
ghoul_assert(u >= 0 && u <= 1.0, "Interpolation variable out of range [0, 1]");
|
||||
|
||||
if (u < 0.0) {
|
||||
return _points[1];
|
||||
}
|
||||
if (u > 1.0) {
|
||||
return *(_points.end() - 2);
|
||||
}
|
||||
|
||||
std::vector<double>::iterator segmentEndIt =
|
||||
std::lower_bound(_curveParameterSteps.begin(), _curveParameterSteps.end(), u);
|
||||
|
||||
const int index =
|
||||
static_cast<int>((segmentEndIt - 1) - _curveParameterSteps.begin());
|
||||
|
||||
double segmentStart = _curveParameterSteps[index];
|
||||
double segmentDuration = (_curveParameterSteps[index + 1] - segmentStart);
|
||||
double uSegment = (u - segmentStart) / segmentDuration;
|
||||
|
||||
return interpolation::catmullRom(
|
||||
uSegment,
|
||||
_points[index],
|
||||
_points[index + 1],
|
||||
_points[index + 2],
|
||||
_points[index + 3],
|
||||
1.0 // chordal version
|
||||
);
|
||||
}
|
||||
|
||||
LinearCurve::LinearCurve(const Waypoint& start, const Waypoint& end) {
|
||||
_points.push_back(start.position());
|
||||
_points.push_back(start.position());
|
||||
_points.push_back(end.position());
|
||||
_points.push_back(end.position());
|
||||
initializeParameterData();
|
||||
}
|
||||
|
||||
} // namespace openspace::pathnavigation
|
||||
@@ -1,80 +0,0 @@
|
||||
/*****************************************************************************************
|
||||
* *
|
||||
* OpenSpace *
|
||||
* *
|
||||
* Copyright (c) 2014-2021 *
|
||||
* *
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy of this *
|
||||
* software and associated documentation files (the "Software"), to deal in the Software *
|
||||
* without restriction, including without limitation the rights to use, copy, modify, *
|
||||
* merge, publish, distribute, sublicense, and/or sell copies of the Software, and to *
|
||||
* permit persons to whom the Software is furnished to do so, subject to the following *
|
||||
* conditions: *
|
||||
* *
|
||||
* The above copyright notice and this permission notice shall be included in all copies *
|
||||
* or substantial portions of the Software. *
|
||||
* *
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, *
|
||||
* INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A *
|
||||
* PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT *
|
||||
* HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF *
|
||||
* CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE *
|
||||
* OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. *
|
||||
****************************************************************************************/
|
||||
|
||||
#ifndef __OPENSPACE_MODULE_AUTONAVIGATION___PATHCURVE___H__
|
||||
#define __OPENSPACE_MODULE_AUTONAVIGATION___PATHCURVE___H__
|
||||
|
||||
#include <modules/autonavigation/waypoint.h>
|
||||
#include <ghoul/glm.h>
|
||||
#include <vector>
|
||||
|
||||
namespace openspace::pathnavigation {
|
||||
|
||||
class PathCurve {
|
||||
public:
|
||||
virtual ~PathCurve() = 0;
|
||||
|
||||
const double length() const;
|
||||
glm::dvec3 positionAt(double relativeDistance);
|
||||
|
||||
// Compute curve parameter u that matches the input arc length s
|
||||
double curveParameter(double s);
|
||||
|
||||
virtual glm::dvec3 interpolate(double u);
|
||||
|
||||
std::vector<glm::dvec3> points();
|
||||
|
||||
protected:
|
||||
// Precompute information related to the pspline parameters, that are
|
||||
// needed for arc length reparameterization. Must be called after
|
||||
// control point creation
|
||||
void initializeParameterData();
|
||||
|
||||
double approximatedDerivative(double u, double h = 0.0001);
|
||||
double arcLength(double limit = 1.0);
|
||||
double arcLength(double lowerLimit, double upperLimit);
|
||||
|
||||
std::vector<glm::dvec3> _points;
|
||||
unsigned int _nSegments;
|
||||
|
||||
std::vector<double> _curveParameterSteps; // per segment
|
||||
std::vector<double> _lengthSums; // per segment
|
||||
double _totalLength;
|
||||
|
||||
struct ParameterPair {
|
||||
double u; // curve parameter
|
||||
double s; // arc length parameter
|
||||
};
|
||||
|
||||
std::vector<ParameterPair> _parameterSamples;
|
||||
};
|
||||
|
||||
class LinearCurve : public PathCurve {
|
||||
public:
|
||||
LinearCurve(const Waypoint& start, const Waypoint& end);
|
||||
};
|
||||
|
||||
} // namespace openspace::pathnavigation
|
||||
|
||||
#endif // __OPENSPACE_MODULE_AUTONAVIGATION___PATHCURVE___H__
|
||||
@@ -1,503 +0,0 @@
|
||||
/*****************************************************************************************
|
||||
* *
|
||||
* OpenSpace *
|
||||
* *
|
||||
* Copyright (c) 2014-2021 *
|
||||
* *
|
||||
* 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/pathnavigationhandler.h>
|
||||
|
||||
#include <modules/autonavigation/helperfunctions.h>
|
||||
#include <modules/autonavigation/pathcreator.h>
|
||||
#include <openspace/engine/globals.h>
|
||||
#include <openspace/navigation/navigationhandler.h>
|
||||
#include <openspace/rendering/renderengine.h>
|
||||
#include <openspace/scene/scene.h>
|
||||
#include <openspace/scene/scenegraphnode.h>
|
||||
#include <openspace/util/camera.h>
|
||||
#include <openspace/util/timemanager.h>
|
||||
#include <ghoul/logging/logmanager.h>
|
||||
#include <glm/gtx/quaternion.hpp>
|
||||
#include <vector>
|
||||
|
||||
namespace {
|
||||
constexpr const char* _loggerCat = "PathNavigationHandler";
|
||||
|
||||
constexpr openspace::properties::Property::PropertyInfo DefaultCurveOptionInfo = {
|
||||
"DefaultCurveOption",
|
||||
"Default Curve Option",
|
||||
"The defualt curve type chosen when generating a path, if none is specified"
|
||||
// TODO: right now there is no way to specify a type for a single path
|
||||
};
|
||||
|
||||
constexpr openspace::properties::Property::PropertyInfo IncludeRollInfo = {
|
||||
"IncludeRoll",
|
||||
"Include Roll",
|
||||
"If disabled, roll is removed from the interpolation of camera orientation"
|
||||
};
|
||||
|
||||
constexpr openspace::properties::Property::PropertyInfo StopBehaviorInfo = {
|
||||
"StopBehavior",
|
||||
"Stop Behavior",
|
||||
"A camera motion behavior that is applied when no path is being played"
|
||||
};
|
||||
|
||||
constexpr openspace::properties::Property::PropertyInfo
|
||||
ApplyStopBehaviorWhenIdleInfo =
|
||||
{
|
||||
"ApplyStopBehaviorWhenIdle",
|
||||
"Apply Stop Behavior When Idle",
|
||||
"If enabled, the camera is controlled using the set stop behavior when "
|
||||
"no path is playing"
|
||||
};
|
||||
|
||||
constexpr openspace::properties::Property::PropertyInfo SpeedScaleInfo = {
|
||||
"SpeedScale",
|
||||
"Speed Scale",
|
||||
"Scale factor that affects the default speed for a camera path."
|
||||
};
|
||||
|
||||
constexpr openspace::properties::Property::PropertyInfo OrbitSpeedFactorInfo = {
|
||||
"OrbitSpeedFactor",
|
||||
"Orbit Speed Factor",
|
||||
"Controls the speed of the orbiting around an anchor."
|
||||
};
|
||||
|
||||
constexpr const openspace::properties::Property::PropertyInfo MinBoundingSphereInfo = {
|
||||
"MinimalValidBoundingSphere",
|
||||
"Minimal Valid Bounding Sphere",
|
||||
"The minimal allowed value for a bounding sphere, in meters. Used for "
|
||||
"computation of target positions and path generation, to avoid issues when "
|
||||
"there is no bounding sphere."
|
||||
};
|
||||
|
||||
constexpr openspace::properties::Property::PropertyInfo RelevantNodeTagsInfo = {
|
||||
"RelevantNodeTags",
|
||||
"Relevant Node Tags",
|
||||
"List of tags for the nodes that are relevant for path creation, for example "
|
||||
"when avoiding collisions."
|
||||
};
|
||||
} // namespace
|
||||
|
||||
namespace openspace::pathnavigation {
|
||||
|
||||
PathNavigationHandler::PathNavigationHandler()
|
||||
: properties::PropertyOwner({ "PathNavigationHandler" })
|
||||
, _defaultCurveOption(
|
||||
DefaultCurveOptionInfo,
|
||||
properties::OptionProperty::DisplayType::Dropdown
|
||||
)
|
||||
, _includeRoll(IncludeRollInfo, false)
|
||||
, _stopBehavior(
|
||||
StopBehaviorInfo,
|
||||
properties::OptionProperty::DisplayType::Dropdown
|
||||
)
|
||||
, _applyStopBehaviorWhenIdle(ApplyStopBehaviorWhenIdleInfo, false)
|
||||
, _speedScale(SpeedScaleInfo, 1.f, 0.01f, 2.f)
|
||||
, _orbitSpeedFactor(OrbitSpeedFactorInfo, 0.5, 0.0, 20.0)
|
||||
, _minValidBoundingSphere(MinBoundingSphereInfo, 10.0, 1.0, 3e10)
|
||||
, _relevantNodeTags(RelevantNodeTagsInfo)
|
||||
{
|
||||
_defaultCurveOption.addOptions({
|
||||
{ Path::CurveType::AvoidCollision, "AvoidCollision" },
|
||||
{ Path::CurveType::Linear, "Linear" },
|
||||
{ Path::CurveType::ZoomOutOverview, "ZoomOutOverview"}
|
||||
});
|
||||
addProperty(_defaultCurveOption);
|
||||
|
||||
addProperty(_includeRoll);
|
||||
addProperty(_speedScale);
|
||||
|
||||
addProperty(_applyStopBehaviorWhenIdle);
|
||||
|
||||
// Must be listed in the same order as in enum definition
|
||||
_stopBehavior.addOptions({
|
||||
{ StopBehavior::None, "None" },
|
||||
{ StopBehavior::Orbit, "Orbit" }
|
||||
});
|
||||
_stopBehavior = StopBehavior::None;
|
||||
addProperty(_stopBehavior);
|
||||
|
||||
addProperty(_orbitSpeedFactor);
|
||||
|
||||
addProperty(_minValidBoundingSphere);
|
||||
|
||||
_relevantNodeTags = std::vector<std::string>{
|
||||
"planet_solarSystem",
|
||||
"moon_solarSystem"
|
||||
};;
|
||||
_relevantNodeTags.onChange([this]() { findRelevantNodes(); });
|
||||
addProperty(_relevantNodeTags);
|
||||
}
|
||||
|
||||
PathNavigationHandler::~PathNavigationHandler() {} // NOLINT
|
||||
|
||||
Camera* PathNavigationHandler::camera() const {
|
||||
return global::navigationHandler->camera();
|
||||
}
|
||||
|
||||
const SceneGraphNode* PathNavigationHandler::anchor() const {
|
||||
return global::navigationHandler->anchorNode();
|
||||
}
|
||||
|
||||
double PathNavigationHandler::speedScale() const {
|
||||
return _speedScale;
|
||||
}
|
||||
|
||||
bool PathNavigationHandler::hasCurrentPath() const {
|
||||
return _currentPath != nullptr;
|
||||
}
|
||||
|
||||
bool PathNavigationHandler::hasFinished() const {
|
||||
if (!hasCurrentPath()) {
|
||||
return true;
|
||||
}
|
||||
|
||||
return _currentPath->hasReachedEnd();
|
||||
}
|
||||
|
||||
void PathNavigationHandler::updateCamera(double deltaTime) {
|
||||
ghoul_assert(camera() != nullptr, "Camera must not be nullptr");
|
||||
|
||||
if (!_isPlaying) {
|
||||
// TODO: Determine how this should work
|
||||
if (hasFinished() && _applyStopBehaviorWhenIdle) {
|
||||
applyStopBehavior(deltaTime);
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
if (!hasCurrentPath()) {
|
||||
return;
|
||||
}
|
||||
|
||||
// If for some reason the time is no longer paused, pause it again
|
||||
if (!global::timeManager->isPaused()) {
|
||||
global::timeManager->setPause(true);
|
||||
LINFO("Cannot start simulation time during camera motion");
|
||||
}
|
||||
|
||||
CameraPose newPose = _currentPath->traversePath(deltaTime);
|
||||
const std::string newAnchor = _currentPath->currentAnchor();
|
||||
|
||||
// Set anchor node in orbitalNavigator, to render visible nodes and add activate
|
||||
// navigation when we reach the end.
|
||||
std::string currentAnchor = anchor()->identifier();
|
||||
if (currentAnchor != newAnchor) {
|
||||
global::navigationHandler->orbitalNavigator().setAnchorNode(newAnchor);
|
||||
}
|
||||
|
||||
if (!_includeRoll) {
|
||||
removeRollRotation(newPose, deltaTime);
|
||||
}
|
||||
|
||||
camera()->setPositionVec3(newPose.position);
|
||||
camera()->setRotation(newPose.rotation);
|
||||
|
||||
if (_currentPath->hasReachedEnd()) {
|
||||
LINFO("Reached end of path");
|
||||
_isPlaying = false;
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
void PathNavigationHandler::createPath(const ghoul::Dictionary& dictionary) {
|
||||
// TODO: Improve how curve types are handled
|
||||
const int curveType = _defaultCurveOption;
|
||||
|
||||
clearPath();
|
||||
try {
|
||||
_currentPath = std::make_unique<Path>(
|
||||
PathCreator::createPath(dictionary, Path::CurveType(curveType))
|
||||
);
|
||||
}
|
||||
catch (const ghoul::RuntimeError& e) {
|
||||
LERROR("Could not create path");
|
||||
return;
|
||||
}
|
||||
|
||||
LINFO("Successfully generated camera path");
|
||||
}
|
||||
|
||||
void PathNavigationHandler::clearPath() {
|
||||
LINFO("Clearing path...");
|
||||
_currentPath = nullptr;
|
||||
}
|
||||
|
||||
void PathNavigationHandler::startPath() {
|
||||
if (!hasCurrentPath()) {
|
||||
LERROR("There is no path to start");
|
||||
return;
|
||||
}
|
||||
|
||||
//OBS! Until we can handle simulation time: early out if not paused
|
||||
if (!global::timeManager->isPaused()) {
|
||||
LERROR("Simulation time must be paused to run a camera path");
|
||||
return;
|
||||
}
|
||||
|
||||
LINFO("Starting path...");
|
||||
_isPlaying = true;
|
||||
}
|
||||
|
||||
void PathNavigationHandler::abortPath() {
|
||||
if (!_isPlaying) {
|
||||
LWARNING("No camera path is playing");
|
||||
return;
|
||||
}
|
||||
_isPlaying = false;
|
||||
LINFO("Aborted camera path");
|
||||
}
|
||||
|
||||
void PathNavigationHandler::pausePath() {
|
||||
if (hasFinished()) {
|
||||
LERROR("No path to pause (path is empty or has finished).");
|
||||
return;
|
||||
}
|
||||
|
||||
if (!_isPlaying) {
|
||||
LERROR("Cannot pause a path that is not playing");
|
||||
return;
|
||||
}
|
||||
|
||||
LINFO("Path paused");
|
||||
_isPlaying = false;
|
||||
}
|
||||
|
||||
void PathNavigationHandler::continuePath() {
|
||||
if (hasFinished()) {
|
||||
LERROR("No path to resume (path is empty or has finished).");
|
||||
return;
|
||||
}
|
||||
|
||||
if (_isPlaying) {
|
||||
LERROR("Cannot resume a path that is already playing");
|
||||
return;
|
||||
}
|
||||
|
||||
LINFO("Continuing path...");
|
||||
_isPlaying = true;
|
||||
}
|
||||
|
||||
// Created for debugging
|
||||
std::vector<glm::dvec3> PathNavigationHandler::curvePositions(int nSteps) const {
|
||||
if (!hasCurrentPath()) {
|
||||
LERROR("There is no current path to sample points from.");
|
||||
return {};
|
||||
}
|
||||
|
||||
std::vector<glm::dvec3> positions;
|
||||
const double du = 1.0 / nSteps;
|
||||
const double length = _currentPath->pathLength();
|
||||
for (double u = 0.0; u < 1.0; u += du) {
|
||||
glm::dvec3 position = _currentPath->interpolatedPose(u * length).position;
|
||||
positions.push_back(position);
|
||||
}
|
||||
positions.push_back(_currentPath->endPoint().position());
|
||||
|
||||
return positions;
|
||||
}
|
||||
|
||||
// Created for debugging
|
||||
std::vector<glm::dquat> PathNavigationHandler::curveOrientations(int nSteps) const {
|
||||
if (!hasCurrentPath()) {
|
||||
LERROR("There is no current path to sample points from.");
|
||||
return {};
|
||||
}
|
||||
|
||||
std::vector<glm::dquat> orientations;
|
||||
const double du = 1.0 / nSteps;
|
||||
const double length = _currentPath->pathLength();
|
||||
for (double u = 0.0; u <= 1.0; u += du) {
|
||||
const glm::dquat orientation =
|
||||
_currentPath->interpolatedPose(u * length).rotation;
|
||||
orientations.push_back(orientation);
|
||||
}
|
||||
orientations.push_back(_currentPath->endPoint().rotation());
|
||||
|
||||
return orientations;
|
||||
}
|
||||
|
||||
|
||||
// Created for debugging
|
||||
std::vector<glm::dvec3> PathNavigationHandler::curveViewDirections(int nSteps) const {
|
||||
if (!hasCurrentPath()) {
|
||||
LERROR("There is no current path to sample points from.");
|
||||
return {};
|
||||
}
|
||||
|
||||
std::vector<glm::dvec3> viewDirections;
|
||||
const double du = 1.0 / nSteps;
|
||||
for (double u = 0.0; u < 1.0; u += du) {
|
||||
const glm::dquat orientation = _currentPath->interpolatedPose(u).rotation;
|
||||
const glm::dvec3 direction = glm::normalize(
|
||||
orientation * glm::dvec3(0.0, 0.0, -1.0)
|
||||
);
|
||||
viewDirections.push_back(direction);
|
||||
}
|
||||
|
||||
const glm::dquat orientation = _currentPath->interpolatedPose(1.0).rotation;
|
||||
const glm::dvec3 direction = glm::normalize(
|
||||
orientation * glm::dvec3(0.0, 0.0, -1.0)
|
||||
);
|
||||
viewDirections.push_back(direction);
|
||||
|
||||
return viewDirections;
|
||||
}
|
||||
|
||||
// Created for debugging
|
||||
std::vector<glm::dvec3> PathNavigationHandler::controlPoints() const {
|
||||
if (!hasCurrentPath()) {
|
||||
LERROR("There is no current path to sample points from.");
|
||||
return {};
|
||||
}
|
||||
|
||||
std::vector<glm::dvec3> points;
|
||||
const std::vector<glm::dvec3> curvePoints = _currentPath->controlPoints();
|
||||
points.insert(points.end(), curvePoints.begin(), curvePoints.end());
|
||||
|
||||
return points;
|
||||
}
|
||||
|
||||
double PathNavigationHandler::minValidBoundingSphere() const {
|
||||
return _minValidBoundingSphere;
|
||||
}
|
||||
|
||||
const std::vector<SceneGraphNode*>& PathNavigationHandler::relevantNodes() {
|
||||
if (!_hasInitializedRelevantNodes) {
|
||||
findRelevantNodes();
|
||||
_hasInitializedRelevantNodes = true;
|
||||
}
|
||||
|
||||
return _relevantNodes;
|
||||
}
|
||||
|
||||
void PathNavigationHandler::findRelevantNodes() {
|
||||
const std::vector<SceneGraphNode*>& allNodes =
|
||||
global::renderEngine->scene()->allSceneGraphNodes();
|
||||
|
||||
const std::vector<std::string> relevantTags = _relevantNodeTags;
|
||||
|
||||
if (allNodes.empty() || relevantTags.empty()) {
|
||||
_relevantNodes = std::vector<SceneGraphNode*>();
|
||||
return;
|
||||
}
|
||||
|
||||
auto isRelevant = [&](const SceneGraphNode* node) {
|
||||
const std::vector<std::string> tags = node->tags();
|
||||
auto result = std::find_first_of(
|
||||
relevantTags.begin(),
|
||||
relevantTags.end(),
|
||||
tags.begin(),
|
||||
tags.end()
|
||||
);
|
||||
|
||||
// does not match any tags => not interesting
|
||||
if (result == relevantTags.end()) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return node->renderable() && (node->boundingSphere() > 0.0);
|
||||
};
|
||||
|
||||
std::vector<SceneGraphNode*> resultingNodes;
|
||||
std::copy_if(
|
||||
allNodes.begin(),
|
||||
allNodes.end(),
|
||||
std::back_inserter(resultingNodes),
|
||||
isRelevant
|
||||
);
|
||||
|
||||
_relevantNodes = resultingNodes;
|
||||
}
|
||||
|
||||
void PathNavigationHandler::removeRollRotation(CameraPose& pose, double deltaTime) {
|
||||
const glm::dvec3 anchorPos = anchor()->worldPosition();
|
||||
const glm::dvec3 cameraDir = glm::normalize(
|
||||
pose.rotation * Camera::ViewDirectionCameraSpace
|
||||
);
|
||||
const double anchorToPosDistance = glm::distance(anchorPos, pose.position);
|
||||
const double notTooCloseDistance = deltaTime * anchorToPosDistance;
|
||||
glm::dvec3 lookAtPos = pose.position + notTooCloseDistance * cameraDir;
|
||||
glm::dquat rollFreeRotation = helpers::lookAtQuaternion(
|
||||
pose.position,
|
||||
lookAtPos,
|
||||
camera()->lookUpVectorWorldSpace()
|
||||
);
|
||||
pose.rotation = rollFreeRotation;
|
||||
}
|
||||
|
||||
void PathNavigationHandler::applyStopBehavior(double deltaTime) {
|
||||
switch (_stopBehavior) {
|
||||
case StopBehavior::None:
|
||||
// Do nothing
|
||||
break;
|
||||
case StopBehavior::Orbit:
|
||||
orbitAnchorNode(deltaTime);
|
||||
break;
|
||||
default:
|
||||
throw ghoul::MissingCaseException();
|
||||
}
|
||||
}
|
||||
|
||||
void PathNavigationHandler::orbitAnchorNode(double deltaTime) {
|
||||
ghoul_assert(anchor() != nullptr, "Node to orbit must be set!");
|
||||
|
||||
const glm::dvec3 prevPosition = camera()->positionVec3();
|
||||
const glm::dquat prevRotation = camera()->rotationQuaternion();
|
||||
const glm::dvec3 nodeCenter = anchor()->worldPosition();
|
||||
|
||||
const double speedFactor = 0.1 * _orbitSpeedFactor;
|
||||
|
||||
// Compute orbit speed based on factor and distance to surface
|
||||
const double orbitRadius = glm::distance(prevPosition, nodeCenter);
|
||||
const double distanceToSurface = orbitRadius - anchor()->boundingSphere();
|
||||
const double orbitSpeed = distanceToSurface * speedFactor;
|
||||
|
||||
// Compute a new position along the orbit
|
||||
const glm::dvec3 up = camera()->lookUpVectorWorldSpace();
|
||||
const glm::dquat lookAtNodeRotation = helpers::lookAtQuaternion(
|
||||
prevPosition,
|
||||
nodeCenter,
|
||||
up
|
||||
);
|
||||
const glm::dvec3 targetForward = lookAtNodeRotation * glm::dvec3(0.0, 0.0, -1.0);
|
||||
const glm::dvec3 rightOrbitTangent = glm::normalize(glm::cross(targetForward, up));
|
||||
|
||||
glm::dvec3 newPosition = prevPosition + orbitSpeed * deltaTime * rightOrbitTangent;
|
||||
|
||||
// Adjust for numerical error - make sure we stay at the same height
|
||||
const glm::dvec3 nodeToNewPos = newPosition - nodeCenter;
|
||||
const double targetHeight = glm::distance(prevPosition, nodeCenter);
|
||||
const double heightDiff = glm::length(nodeToNewPos) - targetHeight;
|
||||
newPosition -= heightDiff * glm::normalize(nodeToNewPos);
|
||||
|
||||
// Rotate along the orbit, but keep relative orientation with regards to the anchor
|
||||
const glm::dquat localRotation = glm::inverse(lookAtNodeRotation) * prevRotation;
|
||||
const glm::dquat newLookAtRotation =
|
||||
helpers::lookAtQuaternion(newPosition, nodeCenter, up);
|
||||
|
||||
const glm::dquat newRotation = newLookAtRotation * localRotation;
|
||||
|
||||
camera()->setPositionVec3(newPosition);
|
||||
camera()->setRotation(newRotation);
|
||||
}
|
||||
|
||||
} // namespace openspace::pathnavigation
|
||||
@@ -1,107 +0,0 @@
|
||||
/*****************************************************************************************
|
||||
* *
|
||||
* OpenSpace *
|
||||
* *
|
||||
* Copyright (c) 2014-2021 *
|
||||
* *
|
||||
* 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___PATHNAVIGATIONHANDLER___H__
|
||||
#define __OPENSPACE_MODULE_AUTONAVIGATION___PATHNAVIGATIONHANDLER___H__
|
||||
|
||||
#include <openspace/properties/propertyowner.h>
|
||||
|
||||
#include <modules/autonavigation/path.h>
|
||||
#include <openspace/properties/list/stringlistproperty.h>
|
||||
#include <openspace/properties/optionproperty.h>
|
||||
#include <openspace/properties/scalar/boolproperty.h>
|
||||
#include <openspace/properties/scalar/doubleproperty.h>
|
||||
#include <openspace/properties/scalar/floatproperty.h>
|
||||
#include <ghoul/glm.h>
|
||||
|
||||
namespace openspace {
|
||||
class Camera;
|
||||
} // namespace openspace
|
||||
|
||||
namespace openspace::pathnavigation {
|
||||
|
||||
class PathNavigationHandler : public properties::PropertyOwner {
|
||||
public:
|
||||
enum StopBehavior {
|
||||
None = 0,
|
||||
Orbit
|
||||
};
|
||||
|
||||
PathNavigationHandler();
|
||||
~PathNavigationHandler();
|
||||
|
||||
// Accessors
|
||||
Camera* camera() const;
|
||||
const SceneGraphNode* anchor() const;
|
||||
double speedScale() const;
|
||||
|
||||
bool hasCurrentPath() const;
|
||||
bool hasFinished() const;
|
||||
|
||||
void updateCamera(double deltaTime);
|
||||
void createPath(const ghoul::Dictionary& dictionary);
|
||||
void clearPath();
|
||||
void startPath();
|
||||
void abortPath();
|
||||
void pausePath();
|
||||
void continuePath();
|
||||
|
||||
// TODO: remove functions for debugging
|
||||
std::vector<glm::dvec3> curvePositions(int nSteps) const;
|
||||
std::vector<glm::dquat> curveOrientations(int nSteps) const;
|
||||
std::vector<glm::dvec3> curveViewDirections(int nSteps) const;
|
||||
std::vector<glm::dvec3> controlPoints() const;
|
||||
|
||||
double minValidBoundingSphere() const;
|
||||
const std::vector<SceneGraphNode*>& relevantNodes();
|
||||
|
||||
private:
|
||||
void findRelevantNodes();
|
||||
|
||||
void removeRollRotation(CameraPose& pose, double deltaTime);
|
||||
void applyStopBehavior(double deltaTime);
|
||||
|
||||
void orbitAnchorNode(double deltaTime);
|
||||
|
||||
std::unique_ptr<Path> _currentPath = nullptr;
|
||||
bool _isPlaying = false;
|
||||
|
||||
properties::OptionProperty _defaultCurveOption;
|
||||
properties::BoolProperty _includeRoll;
|
||||
properties::FloatProperty _speedScale;
|
||||
properties::FloatProperty _orbitSpeedFactor;
|
||||
|
||||
properties::BoolProperty _applyStopBehaviorWhenIdle;
|
||||
properties::OptionProperty _stopBehavior;
|
||||
|
||||
properties::DoubleProperty _minValidBoundingSphere;
|
||||
properties::StringListProperty _relevantNodeTags;
|
||||
|
||||
std::vector<SceneGraphNode*> _relevantNodes;
|
||||
bool _hasInitializedRelevantNodes = false;
|
||||
};
|
||||
|
||||
} // namespace openspace::pathnavigation
|
||||
|
||||
#endif // __OPENSPACE_MODULE_AUTONAVIGATION___PATHNAVIGATIONHANDLER___H__
|
||||
@@ -1,208 +0,0 @@
|
||||
openspace.autonavigation.documentation = {
|
||||
{
|
||||
Name = "removeRenderedPath",
|
||||
Arguments = "",
|
||||
Documentation = "Remove the rendered path, if any."
|
||||
},
|
||||
{
|
||||
Name = "renderPath",
|
||||
Arguments = "int, [bool], [bool], [number]",
|
||||
Documentation = "Render the currently active path, using linear segments. " ..
|
||||
"The first argurment is the number of samples per path segment. " ..
|
||||
"The optional second argument can be used to render the points of " ..
|
||||
"the path as spheres, if set to true." ..
|
||||
"The optional third argument will visualize camera orientation if set to true." ..
|
||||
"The optional fourth input affects the size of the points and orientation lines."
|
||||
},
|
||||
{
|
||||
Name = "removeControlPoints",
|
||||
Arguments = "",
|
||||
Documentation = "Remove the rendered control points, if any."
|
||||
},
|
||||
{
|
||||
Name = "renderControlPoints",
|
||||
Arguments = "[number]",
|
||||
Documentation = "Test method for rendering the control points of the camera " ..
|
||||
"path spline. The optional input is used to specify the radius of the points."
|
||||
},
|
||||
}
|
||||
|
||||
openspace.autonavigation.removeRenderedPath = function ()
|
||||
local path_identifier = "Camera_Path"
|
||||
if openspace.hasSceneGraphNode(path_identifier) then
|
||||
openspace.removeSceneGraphNode(path_identifier)
|
||||
end
|
||||
end
|
||||
|
||||
openspace.autonavigation.renderPath = function (nrLinesPerSegment, renderPoints, renderOrientations, sphereRadius)
|
||||
local path_identifier = "Camera_Path"
|
||||
local label_point = "Point"
|
||||
local label_line = "Line"
|
||||
local lineColor = {1.0, 1.0, 0.0}
|
||||
local lineWidth = 4
|
||||
local sphereTexture = openspace.absPath("${MODULES}/autonavigation/textures/red.png")
|
||||
local sphereSegments = 50
|
||||
local label_orientation = "Orientation"
|
||||
local orientationLineColor = {1.0, 0.0, 0.0}
|
||||
local orientationLineWidth = 2
|
||||
|
||||
if (sphereRadius == nil) then
|
||||
sphereRadius = 2000000 -- TODO: better default
|
||||
end
|
||||
|
||||
local orientationLineLength = 30 * sphereRadius
|
||||
|
||||
if openspace.hasSceneGraphNode(path_identifier) then
|
||||
openspace.removeSceneGraphNode(path_identifier)
|
||||
end
|
||||
|
||||
local path = { Identifier = path_identifier }
|
||||
openspace.addSceneGraphNode(path)
|
||||
|
||||
local points = openspace.autonavigation.getPathPositions(nrLinesPerSegment)
|
||||
local viewDirections = openspace.autonavigation.getPathViewDirections(nrLinesPerSegment)
|
||||
local nrPoints = 0
|
||||
for _ in pairs(points) do
|
||||
nrPoints = nrPoints + 1
|
||||
end
|
||||
|
||||
-- points
|
||||
for key, point in pairs(points) do
|
||||
local node = {
|
||||
Identifier = label_point .. key,
|
||||
Transform = {
|
||||
Translation = {
|
||||
Type = "StaticTranslation",
|
||||
Position = point
|
||||
}
|
||||
},
|
||||
Parent = path_identifier
|
||||
}
|
||||
|
||||
if renderPoints then
|
||||
node.Renderable = {
|
||||
Type = "RenderableSphere",
|
||||
Enabled = true,
|
||||
Segments = sphereSegments,
|
||||
Size = sphereRadius,
|
||||
Texture = sphereTexture,
|
||||
Opacity = 1
|
||||
}
|
||||
end
|
||||
|
||||
openspace.addSceneGraphNode(node)
|
||||
end
|
||||
|
||||
-- lines between points
|
||||
for i = 1,(nrPoints-1) do
|
||||
local node = {
|
||||
Identifier = label_line .. i,
|
||||
Renderable = {
|
||||
Enabled = true,
|
||||
Type = "RenderableNodeLine",
|
||||
StartNode = label_point .. i,
|
||||
EndNode = label_point .. (i+1),
|
||||
Color = lineColor,
|
||||
LineWidth = lineWidth,
|
||||
Opacity = 0.5
|
||||
},
|
||||
Parent = path_identifier
|
||||
}
|
||||
openspace.addSceneGraphNode(node)
|
||||
end
|
||||
|
||||
-- lines for view direction
|
||||
if renderOrientations then
|
||||
|
||||
for key, point in pairs(points) do
|
||||
local viewPos = {
|
||||
tonumber(point[1]) + tonumber(viewDirections[key][1]) * orientationLineLength,
|
||||
tonumber(point[2]) + tonumber(viewDirections[key][2]) * orientationLineLength,
|
||||
tonumber(point[3]) + tonumber(viewDirections[key][3]) * orientationLineLength
|
||||
}
|
||||
|
||||
local node = {
|
||||
Identifier = label_orientation .. label_point .. key,
|
||||
Transform = {
|
||||
Translation = {
|
||||
Type = "StaticTranslation",
|
||||
Position = viewPos
|
||||
}
|
||||
},
|
||||
Parent = path_identifier
|
||||
}
|
||||
openspace.addSceneGraphNode(node)
|
||||
end
|
||||
|
||||
for i = 1,nrPoints do
|
||||
local node = {
|
||||
Identifier = label_orientation .. label_line .. i,
|
||||
Renderable = {
|
||||
Enabled = true,
|
||||
Type = "RenderableNodeLine",
|
||||
StartNode = label_point .. i,
|
||||
EndNode = label_orientation .. label_point .. i,
|
||||
Color = orientationLineColor,
|
||||
LineWidth = orientationLineWidth,
|
||||
Opacity = 1
|
||||
},
|
||||
Parent = path_identifier
|
||||
}
|
||||
openspace.addSceneGraphNode(node)
|
||||
end
|
||||
end
|
||||
|
||||
end
|
||||
|
||||
openspace.autonavigation.removeControlPoints = function ()
|
||||
local base_identifier = "Path_Control_Points"
|
||||
if openspace.hasSceneGraphNode(base_identifier) then
|
||||
openspace.removeSceneGraphNode(base_identifier)
|
||||
end
|
||||
end
|
||||
|
||||
openspace.autonavigation.renderControlPoints = function (sphereRadius)
|
||||
local base_identifier = "Path_Control_Points"
|
||||
local label_point = "ControlPoint"
|
||||
local sphereTexture = openspace.absPath("${MODULES}/autonavigation/textures/yellow.png")
|
||||
local sphereSegments = 50
|
||||
|
||||
if (sphereRadius == nil) then
|
||||
sphereRadius = 2000000 -- TODO: better default
|
||||
end
|
||||
|
||||
if openspace.hasSceneGraphNode(base_identifier) then
|
||||
openspace.removeSceneGraphNode(base_identifier)
|
||||
end
|
||||
|
||||
local base = { Identifier = base_identifier}
|
||||
openspace.addSceneGraphNode(base)
|
||||
|
||||
local points = openspace.autonavigation.getControlPoints()
|
||||
|
||||
for key, point in pairs(points) do
|
||||
local node = {
|
||||
Identifier = label_point .. key,
|
||||
Transform = {
|
||||
Translation = {
|
||||
Type = "StaticTranslation",
|
||||
Position = point
|
||||
}
|
||||
},
|
||||
Renderable = {
|
||||
Type = "RenderableSphere",
|
||||
Enabled = true,
|
||||
Segments = sphereSegments,
|
||||
Size = sphereRadius,
|
||||
Texture = sphereTexture,
|
||||
Opacity = 1
|
||||
},
|
||||
Parent = base_identifier,
|
||||
GUI = {
|
||||
Name = "Control Point" .. key,
|
||||
Path = "/Camera Paths Debug/",
|
||||
}
|
||||
}
|
||||
openspace.addSceneGraphNode(node)
|
||||
end
|
||||
end
|
||||
Binary file not shown.
|
Before Width: | Height: | Size: 357 B |
Binary file not shown.
|
Before Width: | Height: | Size: 350 B |
@@ -1,145 +0,0 @@
|
||||
/*****************************************************************************************
|
||||
* *
|
||||
* OpenSpace *
|
||||
* *
|
||||
* Copyright (c) 2014-2021 *
|
||||
* *
|
||||
* 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/waypoint.h>
|
||||
|
||||
#include <modules/autonavigation/autonavigationmodule.h>
|
||||
#include <openspace/engine/globals.h>
|
||||
#include <openspace/engine/moduleengine.h>
|
||||
#include <openspace/scene/scenegraphnode.h>
|
||||
#include <openspace/query/query.h>
|
||||
#include <ghoul/logging/logmanager.h>
|
||||
|
||||
namespace {
|
||||
constexpr const char* _loggerCat = "Waypoint";
|
||||
} // namespace
|
||||
|
||||
namespace openspace::pathnavigation {
|
||||
|
||||
Waypoint::Waypoint(const glm::dvec3& pos, const glm::dquat& rot, const std::string& ref)
|
||||
: nodeIdentifier(ref)
|
||||
{
|
||||
pose.position = pos;
|
||||
pose.rotation = rot;
|
||||
|
||||
const SceneGraphNode* node = sceneGraphNode(nodeIdentifier);
|
||||
if (!node) {
|
||||
LERROR(fmt::format("Could not find node '{}'", nodeIdentifier));
|
||||
return;
|
||||
}
|
||||
validBoundingSphere = findValidBoundingSphere(node);
|
||||
}
|
||||
|
||||
Waypoint::Waypoint(const NavigationState& ns) {
|
||||
// OBS! The following code to get the position and rotation is exactly the same as in
|
||||
// NavigationHandler::applyNavigationState. Should probably be made into a function.
|
||||
// TODO: make that function
|
||||
const SceneGraphNode* referenceFrame = sceneGraphNode(ns.referenceFrame);
|
||||
const SceneGraphNode* anchorNode = sceneGraphNode(ns.anchor);
|
||||
|
||||
if (!anchorNode) {
|
||||
LERROR(fmt::format("Could not find node '{}' to target", ns.anchor));
|
||||
return;
|
||||
}
|
||||
|
||||
nodeIdentifier = ns.anchor;
|
||||
validBoundingSphere = findValidBoundingSphere(anchorNode);
|
||||
|
||||
const glm::dvec3 anchorWorldPosition = anchorNode->worldPosition();
|
||||
const glm::dmat3 referenceFrameTransform = referenceFrame->worldRotationMatrix();
|
||||
|
||||
pose.position = anchorWorldPosition +
|
||||
glm::dvec3(referenceFrameTransform * glm::dvec4(ns.position, 1.0));
|
||||
|
||||
glm::dvec3 up = ns.up.has_value() ?
|
||||
glm::normalize(referenceFrameTransform * ns.up.value()) :
|
||||
glm::dvec3(0.0, 1.0, 0.0);
|
||||
|
||||
// Construct vectors of a "neutral" view, i.e. when the aim is centered in view.
|
||||
glm::dvec3 neutralView =
|
||||
glm::normalize(anchorWorldPosition - pose.position);
|
||||
|
||||
glm::dquat neutralCameraRotation = glm::inverse(glm::quat_cast(glm::lookAt(
|
||||
glm::dvec3(0.0),
|
||||
neutralView,
|
||||
up
|
||||
)));
|
||||
|
||||
glm::dquat pitchRotation = glm::angleAxis(ns.pitch, glm::dvec3(1.f, 0.f, 0.f));
|
||||
glm::dquat yawRotation = glm::angleAxis(ns.yaw, glm::dvec3(0.f, -1.f, 0.f));
|
||||
|
||||
pose.rotation = neutralCameraRotation * yawRotation * pitchRotation;
|
||||
}
|
||||
|
||||
glm::dvec3 Waypoint::position() const {
|
||||
return pose.position;
|
||||
}
|
||||
|
||||
glm::dquat Waypoint::rotation() const {
|
||||
return pose.rotation;
|
||||
}
|
||||
|
||||
SceneGraphNode* Waypoint::node() const {
|
||||
return sceneGraphNode(nodeIdentifier);
|
||||
}
|
||||
|
||||
double Waypoint::findValidBoundingSphere(const SceneGraphNode* node) {
|
||||
double bs = static_cast<double>(node->boundingSphere());
|
||||
|
||||
auto module = global::moduleEngine->module<AutoNavigationModule>();
|
||||
const double minValidBoundingSphere =
|
||||
module->PathNavigationHandler().minValidBoundingSphere();
|
||||
|
||||
if (bs < minValidBoundingSphere) {
|
||||
// If the bs of the target is too small, try to find a good value in a child node.
|
||||
// Only check the closest children, to avoid deep traversal in the scene graph.
|
||||
// Also, the possibility to find a bounding sphere represents the visual size of
|
||||
// the target well is higher for these nodes.
|
||||
for (SceneGraphNode* child : node->children()) {
|
||||
bs = static_cast<double>(child->boundingSphere());
|
||||
if (bs > minValidBoundingSphere) {
|
||||
LWARNING(fmt::format(
|
||||
"The scene graph node '{}' has no, or a very small, bounding sphere. "
|
||||
"Using bounding sphere of child node '{}' in computations.",
|
||||
node->identifier(),
|
||||
child->identifier()
|
||||
));
|
||||
|
||||
return bs;
|
||||
}
|
||||
}
|
||||
|
||||
LWARNING(fmt::format(
|
||||
"The scene graph node '{}' has no, or a very small, bounding sphere. Using "
|
||||
"minimal value. This might lead to unexpected results.",
|
||||
node->identifier())
|
||||
);
|
||||
|
||||
bs = minValidBoundingSphere;
|
||||
}
|
||||
|
||||
return bs;
|
||||
}
|
||||
|
||||
} // namespace openspace::pathnavigation
|
||||
@@ -1,60 +0,0 @@
|
||||
/*****************************************************************************************
|
||||
* *
|
||||
* OpenSpace *
|
||||
* *
|
||||
* Copyright (c) 2014-2021 *
|
||||
* *
|
||||
* 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___WAYPOINT___H__
|
||||
#define __OPENSPACE_MODULE_AUTONAVIGATION___WAYPOINT___H__
|
||||
|
||||
#include <openspace/navigation/navigationhandler.h>
|
||||
#include <ghoul/glm.h>
|
||||
|
||||
namespace openspace::pathnavigation {
|
||||
|
||||
struct CameraPose {
|
||||
glm::dvec3 position;
|
||||
glm::dquat rotation;
|
||||
};
|
||||
|
||||
struct Waypoint {
|
||||
using NavigationState = interaction::NavigationHandler::NavigationState;
|
||||
|
||||
// TODO: create waypoints from a dictionary and skip instructions?
|
||||
|
||||
Waypoint() = default;
|
||||
Waypoint(const glm::dvec3& pos, const glm::dquat& rot, const std::string& ref);
|
||||
Waypoint(const NavigationState& ns);
|
||||
|
||||
static double findValidBoundingSphere(const SceneGraphNode* node);
|
||||
|
||||
glm::dvec3 position() const;
|
||||
glm::dquat rotation() const;
|
||||
SceneGraphNode* node() const;
|
||||
|
||||
CameraPose pose;
|
||||
std::string nodeIdentifier;
|
||||
double validBoundingSphere = 0.0; // to be able to handle nodes with faulty bounding spheres
|
||||
};
|
||||
|
||||
} // namespace openspace::pathnavigation
|
||||
|
||||
#endif // __OPENSPACE_MODULE_AUTONAVIGATION___WAYPOINT___H__
|
||||
Reference in New Issue
Block a user