Merging Master

This commit is contained in:
Jonathas Costa
2019-07-22 16:47:58 -04:00
parent 20d24764e7
commit a6b90991b2
119 changed files with 1812 additions and 1660 deletions

View File

@@ -27,14 +27,17 @@
#include <openspace/engine/globals.h>
#include <openspace/scene/scenegraphnode.h>
#include <openspace/scripting/lualibrary.h>
#include <openspace/interaction/orbitalnavigator.h>
#include <openspace/interaction/keyframenavigator.h>
#include <openspace/interaction/inputstate.h>
#include <openspace/network/parallelpeer.h>
#include <openspace/scene/scenegraphnode.h>
#include <openspace/scene/scene.h>
#include <openspace/util/camera.h>
#include <openspace/documentation/verifier.h>
#include <openspace/query/query.h>
#include <ghoul/filesystem/file.h>
#include <ghoul/filesystem/filesystem.h>
#include <ghoul/logging/logmanager.h>
#include <ghoul/misc/dictionaryluaformatter.h>
#include <glm/gtx/vector_angle.hpp>
#include <fstream>
namespace {
@@ -43,7 +46,11 @@ namespace {
constexpr const char* KeyAnchor = "Anchor";
constexpr const char* KeyAim = "Aim";
constexpr const char* KeyPosition = "Position";
constexpr const char* KeyRotation = "Rotation";
constexpr const char* KeyUp = "Up";
constexpr const char* KeyYaw = "Yaw";
constexpr const char* KeyPitch = "Pitch";
constexpr const char* KeyReferenceFrame = "ReferenceFrame";
const double Epsilon = 1E-7;
constexpr const openspace::properties::Property::PropertyInfo KeyFrameInfo = {
"UseKeyFrameInteraction",
@@ -51,24 +58,101 @@ namespace {
"If this is set to 'true' the entire interaction is based off key frames rather "
"than using the mouse interaction."
};
} // namespace
#include "navigationhandler_lua.inl"
namespace openspace::interaction {
ghoul::Dictionary
openspace::interaction::NavigationHandler::NavigationState::dictionary() const
{
ghoul::Dictionary cameraDict;
cameraDict.setValue(KeyPosition, position);
cameraDict.setValue(KeyAnchor, anchor);
if (anchor != referenceFrame) {
cameraDict.setValue(KeyReferenceFrame, referenceFrame);
}
if (!aim.empty()) {
cameraDict.setValue(KeyAim, aim);
}
if (up.has_value()) {
cameraDict.setValue(KeyUp, up.value());
if (std::abs(yaw) > Epsilon) {
cameraDict.setValue(KeyYaw, yaw);
}
if (std::abs(pitch) > Epsilon) {
cameraDict.setValue(KeyPitch, pitch);
}
}
return cameraDict;
}
openspace::interaction::NavigationHandler::NavigationState::NavigationState(
const ghoul::Dictionary& dictionary)
{
const bool hasAnchor = dictionary.hasValue<std::string>(KeyAnchor);
const bool hasPosition = dictionary.hasValue<glm::dvec3>(KeyPosition);
if (!hasAnchor || !hasPosition) {
throw ghoul::RuntimeError(
"Position and Anchor need to be defined for navigation dictionary."
);
}
anchor = dictionary.value<std::string>(KeyAnchor);
position = dictionary.value<glm::dvec3>(KeyPosition);
if (dictionary.hasValue<std::string>(KeyReferenceFrame)) {
referenceFrame = dictionary.value<std::string>(KeyReferenceFrame);
}
else {
referenceFrame = anchor;
}
if (dictionary.hasValue<std::string>(KeyAim)) {
aim = dictionary.value<std::string>(KeyAim);
}
if (dictionary.hasValue<glm::dvec3>(KeyUp)) {
up = dictionary.value<glm::dvec3>(KeyUp);
if (dictionary.hasValue<double>(KeyYaw)) {
yaw = dictionary.value<double>(KeyYaw);
}
if (dictionary.hasValue<double>(KeyPitch)) {
pitch = dictionary.value<double>(KeyPitch);
}
}
}
openspace::interaction::NavigationHandler::NavigationState::NavigationState(
std::string anchor,
std::string aim,
std::string referenceFrame,
glm::dvec3 position,
std::optional<glm::dvec3> up,
double yaw,
double pitch)
: anchor(std::move(anchor))
, aim(std::move(aim))
, referenceFrame(std::move(referenceFrame))
, position(std::move(position))
, up(std::move(up))
, yaw(yaw)
, pitch(pitch)
{}
NavigationHandler::NavigationHandler()
: properties::PropertyOwner({ "NavigationHandler" })
, _useKeyFrameInteraction(KeyFrameInfo, false)
{
_inputState = std::make_unique<InputState>();
_orbitalNavigator = std::make_unique<OrbitalNavigator>();
_keyframeNavigator = std::make_unique<KeyframeNavigator>();
// Add the properties
addProperty(_useKeyFrameInteraction);
addPropertySubOwner(*_orbitalNavigator);
addPropertySubOwner(_orbitalNavigator);
}
NavigationHandler::~NavigationHandler() {} // NOLINT
@@ -90,19 +174,25 @@ void NavigationHandler::deinitialize() {
void NavigationHandler::setCamera(Camera* camera) {
_camera = camera;
_orbitalNavigator->setCamera(camera);
_orbitalNavigator.setCamera(camera);
}
const OrbitalNavigator& NavigationHandler::orbitalNavigator() const {
return *_orbitalNavigator;
void NavigationHandler::setNavigationStateNextFrame(
NavigationHandler::NavigationState state)
{
_pendingNavigationState = std::move(state);
}
OrbitalNavigator& NavigationHandler::orbitalNavigator() {
return *_orbitalNavigator;
return _orbitalNavigator;
}
KeyframeNavigator& NavigationHandler::keyframeNavigator() const {
return *_keyframeNavigator;
const OrbitalNavigator& NavigationHandler::orbitalNavigator() const {
return _orbitalNavigator;
}
KeyframeNavigator& NavigationHandler::keyframeNavigator() {
return _keyframeNavigator;
}
bool NavigationHandler::isKeyFrameInteractionEnabled() const {
@@ -110,33 +200,93 @@ bool NavigationHandler::isKeyFrameInteractionEnabled() const {
}
float NavigationHandler::interpolationTime() const {
return _orbitalNavigator->retargetInterpolationTime();
return _orbitalNavigator.retargetInterpolationTime();
}
void NavigationHandler::setInterpolationTime(float durationInSeconds) {
_orbitalNavigator->setRetargetInterpolationTime(durationInSeconds);
_orbitalNavigator.setRetargetInterpolationTime(durationInSeconds);
}
void NavigationHandler::updateCamera(double deltaTime) {
ghoul_assert(_inputState != nullptr, "InputState must not be nullptr");
ghoul_assert(_camera != nullptr, "Camera must not be nullptr");
if (_cameraUpdatedFromScript) {
_cameraUpdatedFromScript = false;
}
else {
if (!_playbackModeEnabled && _camera) {
if (_useKeyFrameInteraction) {
_keyframeNavigator->updateCamera(*_camera, _playbackModeEnabled);
}
else {
_orbitalNavigator->updateStatesFromInput(*_inputState, deltaTime);
_orbitalNavigator->updateCameraStateFromStates(deltaTime);
}
if (_pendingNavigationState.has_value()) {
applyNavigationState(_pendingNavigationState.value());
_orbitalNavigator.resetVelocities();
_pendingNavigationState.reset();
} else if (!_playbackModeEnabled && _camera) {
if (_useKeyFrameInteraction) {
_keyframeNavigator.updateCamera(*_camera, _playbackModeEnabled);
}
else {
_orbitalNavigator.updateStatesFromInput(_inputState, deltaTime);
_orbitalNavigator.updateCameraStateFromStates(deltaTime);
}
}
}
void NavigationHandler::applyNavigationState(const NavigationHandler::NavigationState& ns)
{
const SceneGraphNode* referenceFrame = sceneGraphNode(ns.referenceFrame);
const SceneGraphNode* anchor = sceneGraphNode(ns.anchor);
if (!anchor) {
LERROR(fmt::format(
"Could not find scene graph node '{}' used as anchor.", ns.referenceFrame
));
return;
}
if (!ns.aim.empty() && !sceneGraphNode(ns.aim)) {
LERROR(fmt::format(
"Could not find scene graph node '{}' used as aim.", ns.referenceFrame
));
return;
}
if (!referenceFrame) {
LERROR(fmt::format(
"Could not find scene graph node '{}' used as reference frame.",
ns.referenceFrame)
);
return;
}
const glm::dvec3 anchorWorldPosition = anchor->worldPosition();
const glm::dmat3 referenceFrameTransform = referenceFrame->worldRotationMatrix();
_orbitalNavigator.setAnchorNode(ns.anchor);
_orbitalNavigator.setAimNode(ns.aim);
const SceneGraphNode* anchorNode = _orbitalNavigator.anchorNode();
const SceneGraphNode* aimNode = _orbitalNavigator.aimNode();
if (!aimNode) {
aimNode = anchorNode;
}
const glm::dvec3 cameraPositionWorld = 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(aimNode->worldPosition() - cameraPositionWorld);
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));
_camera->setPositionVec3(cameraPositionWorld);
_camera->setRotation(neutralCameraRotation * yawRotation * pitchRotation);
_orbitalNavigator.clearPreviousState();
}
void NavigationHandler::setEnableKeyFrameInteraction() {
_useKeyFrameInteraction = true;
}
@@ -150,8 +300,8 @@ void NavigationHandler::triggerPlaybackStart() {
}
void NavigationHandler::stopPlayback() {
_orbitalNavigator->resetVelocities();
_orbitalNavigator->resetNodeMovements();
_orbitalNavigator.resetVelocities();
_orbitalNavigator.resetNodeMovements();
_playbackModeEnabled = false;
}
@@ -160,131 +310,120 @@ Camera* NavigationHandler::camera() const {
}
const InputState& NavigationHandler::inputState() const {
return *_inputState;
return _inputState;
}
void NavigationHandler::mouseButtonCallback(MouseButton button, MouseAction action) {
_inputState->mouseButtonCallback(button, action);
_inputState.mouseButtonCallback(button, action);
}
void NavigationHandler::mousePositionCallback(double x, double y) {
_inputState->mousePositionCallback(x, y);
_inputState.mousePositionCallback(x, y);
}
void NavigationHandler::mouseScrollWheelCallback(double pos) {
_inputState->mouseScrollWheelCallback(pos);
_inputState.mouseScrollWheelCallback(pos);
}
void NavigationHandler::keyboardCallback(Key key, KeyModifier modifier, KeyAction action)
{
_inputState->keyboardCallback(key, modifier, action);
_inputState.keyboardCallback(key, modifier, action);
}
void NavigationHandler::setCameraStateFromDictionary(const ghoul::Dictionary& cameraDict)
NavigationHandler::NavigationState NavigationHandler::navigationState(
const SceneGraphNode& referenceFrame) const
{
bool readSuccessful = true;
const SceneGraphNode* anchor = _orbitalNavigator.anchorNode();
const SceneGraphNode* aim = _orbitalNavigator.aimNode();
std::string anchor;
std::string aim;
glm::dvec3 cameraPosition;
glm::dvec4 cameraRotation; // Need to read the quaternion as a vector first.
readSuccessful &= cameraDict.getValue(KeyAnchor, anchor);
readSuccessful &= cameraDict.getValue(KeyPosition, cameraPosition);
readSuccessful &= cameraDict.getValue(KeyRotation, cameraRotation);
cameraDict.getValue(KeyAim, aim); // Aim is not required
if (!readSuccessful) {
throw ghoul::RuntimeError(
"Position, Rotation and Focus need to be defined for camera dictionary."
);
if (!aim) {
aim = anchor;
}
// Set state
_orbitalNavigator->setAnchorNode(anchor);
_orbitalNavigator->setAimNode(aim);
const glm::dquat invNeutralRotation = glm::quat_cast(glm::lookAt(
glm::dvec3(0.0, 0.0, 0.0),
aim->worldPosition() - _camera->positionVec3(),
glm::normalize(_camera->lookUpVectorWorldSpace())
));
_camera->setPositionVec3(cameraPosition);
_camera->setRotation(glm::dquat(
cameraRotation.x, cameraRotation.y, cameraRotation.z, cameraRotation.w));
glm::dquat localRotation = invNeutralRotation * _camera->rotationQuaternion();
glm::dvec3 eulerAngles = glm::eulerAngles(localRotation);
const double pitch = eulerAngles.x;
const double yaw = -eulerAngles.y;
// Need to compensate by redisual roll left in local rotation:
const glm::dquat unroll = glm::angleAxis(eulerAngles.z, glm::dvec3(0, 0, 1));
const glm::dvec3 neutralUp =
glm::inverse(invNeutralRotation) * unroll * _camera->lookUpVectorCameraSpace();
const glm::dmat3 invReferenceFrameTransform =
glm::inverse(referenceFrame.worldRotationMatrix());
const glm::dvec3 position = invReferenceFrameTransform *
(glm::dvec4(_camera->positionVec3() - anchor->worldPosition(), 1.0));
return NavigationState(
_orbitalNavigator.anchorNode()->identifier(),
_orbitalNavigator.aimNode() ?
_orbitalNavigator.aimNode()->identifier() : "",
referenceFrame.identifier(),
position,
invReferenceFrameTransform * neutralUp, yaw, pitch
);
}
ghoul::Dictionary NavigationHandler::cameraStateDictionary() {
glm::dvec3 cameraPosition;
glm::dquat quat;
glm::dvec4 cameraRotation;
void NavigationHandler::saveNavigationState(const std::string& filepath,
const std::string& referenceFrameIdentifier)
{
const SceneGraphNode* referenceFrame = _orbitalNavigator.followingNodeRotation() ?
_orbitalNavigator.anchorNode() :
sceneGraph()->root();
cameraPosition = _camera->positionVec3();
quat = _camera->rotationQuaternion();
cameraRotation = glm::dvec4(quat.w, quat.x, quat.y, quat.z);
ghoul::Dictionary cameraDict;
cameraDict.setValue(KeyPosition, cameraPosition);
cameraDict.setValue(KeyRotation, cameraRotation);
cameraDict.setValue(KeyAnchor, _orbitalNavigator->anchorNode()->identifier());
if (_orbitalNavigator->aimNode()) {
cameraDict.setValue(KeyAim, _orbitalNavigator->aimNode()->identifier());
}
return cameraDict;
}
void NavigationHandler::saveCameraStateToFile(const std::string& filepath) {
if (!filepath.empty()) {
std::string fullpath = absPath(filepath);
LINFO(fmt::format("Saving camera position: {}", filepath));
ghoul::Dictionary cameraDict = cameraStateDictionary();
// TODO(abock): Should get the camera state as a dictionary and save the
// dictionary to a file in form of a lua state and not use ofstreams here.
std::ofstream ofs(fullpath.c_str());
glm::dvec3 p = _camera->positionVec3();
glm::dquat q = _camera->rotationQuaternion();
ofs << "return {" << std::endl;
ofs << " " << KeyAnchor << " = " << "\"" <<
_orbitalNavigator->anchorNode()->identifier() << "\""
<< "," << std::endl;
if (_orbitalNavigator->aimNode()) {
ofs << " " << KeyAim << " = " << "\"" <<
_orbitalNavigator->aimNode()->identifier() << "\""
<< "," << std::endl;
if (!referenceFrameIdentifier.empty()) {
referenceFrame = sceneGraphNode(referenceFrameIdentifier);
if (!referenceFrame) {
LERROR(fmt::format(
"Could not find node '{}' to use as reference frame",
referenceFrameIdentifier
));
return;
}
}
ofs << " " << KeyPosition << " = {"
<< std::to_string(p.x) << ", "
<< std::to_string(p.y) << ", "
<< std::to_string(p.z) << "}," << std::endl;
ofs << " " << KeyRotation << " = {"
<< std::to_string(q.w) << ", "
<< std::to_string(q.x) << ", "
<< std::to_string(q.y) << ", "
<< std::to_string(q.z) << "}," << std::endl;
ofs << "}"<< std::endl;
if (!filepath.empty()) {
std::string absolutePath = absPath(filepath);
LINFO(fmt::format("Saving camera position: {}", absolutePath));
ghoul::Dictionary cameraDict = navigationState(*referenceFrame).dictionary();
ghoul::DictionaryLuaFormatter formatter;
std::ofstream ofs(absolutePath.c_str());
ofs << "return " << formatter.format(cameraDict);
ofs.close();
}
}
void NavigationHandler::restoreCameraStateFromFile(const std::string& filepath) {
LINFO(fmt::format("Reading camera state from file: {}", filepath));
if (!FileSys.fileExists(filepath)) {
throw ghoul::FileNotFoundError(filepath, "CameraFilePath");
void NavigationHandler::loadNavigationState(const std::string& filepath) {
const std::string absolutePath = absPath(filepath);
LINFO(fmt::format("Reading camera state from file: {}", absolutePath));
if (!FileSys.fileExists(absolutePath)) {
throw ghoul::FileNotFoundError(absolutePath, "NavigationState");
}
ghoul::Dictionary cameraDict;
ghoul::Dictionary navigationStateDictionary;
try {
ghoul::lua::loadDictionaryFromFile(filepath, cameraDict);
setCameraStateFromDictionary(cameraDict);
_cameraUpdatedFromScript = true;
ghoul::lua::loadDictionaryFromFile(absolutePath, navigationStateDictionary);
openspace::documentation::testSpecificationAndThrow(
NavigationState::Documentation(),
navigationStateDictionary,
"NavigationState"
);
setNavigationStateNextFrame(NavigationState(navigationStateDictionary));
}
catch (ghoul::RuntimeError& e) {
LWARNING("Unable to set camera position");
LWARNING(e.message);
LERROR(fmt::format("Unable to set camera position: {}", e.message));
}
}
@@ -293,7 +432,7 @@ void NavigationHandler::setJoystickAxisMapping(int axis,
JoystickCameraStates::AxisInvert shouldInvert,
JoystickCameraStates::AxisNormalize shouldNormalize)
{
_orbitalNavigator->joystickStates().setAxisMapping(
_orbitalNavigator.joystickStates().setAxisMapping(
axis,
mapping,
shouldInvert,
@@ -304,15 +443,15 @@ void NavigationHandler::setJoystickAxisMapping(int axis,
JoystickCameraStates::AxisInformation
NavigationHandler::joystickAxisMapping(int axis) const
{
return _orbitalNavigator->joystickStates().axisMapping(axis);
return _orbitalNavigator.joystickStates().axisMapping(axis);
}
void NavigationHandler::setJoystickAxisDeadzone(int axis, float deadzone) {
_orbitalNavigator->joystickStates().setDeadzone(axis, deadzone);
_orbitalNavigator.joystickStates().setDeadzone(axis, deadzone);
}
float NavigationHandler::joystickAxisDeadzone(int axis) const {
return _orbitalNavigator->joystickStates().deadzone(axis);
return _orbitalNavigator.joystickStates().deadzone(axis);
}
void NavigationHandler::bindJoystickButtonCommand(int button, std::string command,
@@ -320,7 +459,7 @@ void NavigationHandler::bindJoystickButtonCommand(int button, std::string comman
JoystickCameraStates::ButtonCommandRemote remote,
std::string documentation)
{
_orbitalNavigator->joystickStates().bindButtonCommand(
_orbitalNavigator.joystickStates().bindButtonCommand(
button,
std::move(command),
action,
@@ -330,11 +469,68 @@ void NavigationHandler::bindJoystickButtonCommand(int button, std::string comman
}
void NavigationHandler::clearJoystickButtonCommand(int button) {
_orbitalNavigator->joystickStates().clearButtonCommand(button);
_orbitalNavigator.joystickStates().clearButtonCommand(button);
}
std::vector<std::string> NavigationHandler::joystickButtonCommand(int button) const {
return _orbitalNavigator->joystickStates().buttonCommand(button);
return _orbitalNavigator.joystickStates().buttonCommand(button);
}
documentation::Documentation NavigationHandler::NavigationState::Documentation() {
using namespace documentation;
return {
"Navigation State",
"core_navigation_state",
{
{
KeyAnchor,
new StringVerifier,
Optional::No,
"The identifier of the anchor node."
},
{
KeyAim,
new StringVerifier,
Optional::Yes,
"The identifier of the aim node, if used."
},
{
KeyReferenceFrame,
new StringVerifier,
Optional::Yes,
"The identifier of the scene graph node to use as reference frame. "
"If not specified, this will be the same as the anchor."
},
{
KeyPosition,
new DoubleVector3Verifier,
Optional::No,
"The position of the camera relative to the anchor node, "
"expressed in meters in the specified reference frame."
},
{
KeyUp,
new DoubleVector3Verifier,
Optional::Yes,
"The up vector expressed in the coordinate system of the reference frame."
},
{
KeyYaw,
new DoubleVerifier,
Optional::Yes,
"The yaw angle in radians. "
"Positive angle means yawing camera to the right."
},
{
KeyPitch,
new DoubleVerifier,
Optional::Yes,
"The pitch angle in radians. "
"Positive angle means pitching camera upwards."
},
}
};
}
scripting::LuaLibrary NavigationHandler::luaLibrary() {
@@ -342,25 +538,33 @@ scripting::LuaLibrary NavigationHandler::luaLibrary() {
"navigation",
{
{
"setCameraState",
&luascriptfunctions::setCameraState,
"setNavigationState",
&luascriptfunctions::setNavigationState,
{},
"object",
"Set the camera state"
"table",
"Set the navigation state. "
"The argument must be a valid Navigation State."
},
{
"saveCameraStateToFile",
&luascriptfunctions::saveCameraStateToFile,
"saveNavigationState",
&luascriptfunctions::saveNavigationState,
{},
"string",
"Save the current camera state to file"
"string, [string]",
"Save the current navigation state to a file with the path given by the "
"first argument. The optoinal second argument is the scene graph node to "
"use as reference frame. By default, the reference frame will picked "
"based on whether the orbital navigator is currently following the "
"anchor node rotation. If it is, the anchor will be chosen as reference "
"frame. If not, the reference frame will be set to the scene graph root."
},
{
"restoreCameraStateFromFile",
&luascriptfunctions::restoreCameraStateFromFile,
"loadNavigationState",
&luascriptfunctions::loadNavigationState,
{},
"string",
"Restore the camera state from file"
"Load a navigation state from file. The file should be a lua file "
"returning the navigation state as a table formatted as a "
"Navigation State, such as the output files of saveNavigationState."
},
{
"retargetAnchor",