mirror of
https://github.com/OpenSpace/OpenSpace.git
synced 2026-02-18 02:49:03 -06:00
Feature/navigation state (#930)
* Replace setCameraState with setNavigationState + equivalents * Add documentation and verification of NavigationState-related interfaces * Documentation and verification fixes * Change reference frame behavior * Scene fixes * Replace earthrise recording with navigation state and time
This commit is contained in:
committed by
Alexander Bock
parent
b25b205e99
commit
f43bcadee3
@@ -12,14 +12,19 @@ local Keybindings = {
|
||||
{
|
||||
Key = "E",
|
||||
Command = "openspace.time.setPause(true);" ..
|
||||
"openspace.setPropertyValue('*Trail.Renderable.Enabled', false)"..
|
||||
"openspace.setPropertyValue('Scene.Apollo8LaunchTrail.Renderable.Enabled', false)"..
|
||||
"openspace.sessionRecording.startPlayback('apollo8')",
|
||||
"openspace.time.setDeltaTime(1);" ..
|
||||
"openspace.time.setTime('1968 DEC 24 16:37:31');" ..
|
||||
"openspace.navigation.setNavigationState({" ..
|
||||
" Anchor = 'Apollo8'," ..
|
||||
" Position = { 1.494592E1, 3.236777E1, -4.171296E1 }," ..
|
||||
" ReferenceFrame = 'Root'," ..
|
||||
" Up = { 0.960608E0, -0.212013E0, 0.179675E0 }" ..
|
||||
"});" ..
|
||||
"openspace.setPropertyValue('*Trail.Renderable.Enabled', false)",
|
||||
Documentation = "Jump to right before the earthrise photo",
|
||||
Name = "Set Earthrise time",
|
||||
GuiPath = "/Missions/Apollo/8",
|
||||
Local = false
|
||||
|
||||
},
|
||||
{
|
||||
Key = "U",
|
||||
@@ -115,13 +120,7 @@ asset.onInitialize(function ()
|
||||
openspace.setPropertyValueSingle('NavigationHandler.OrbitalNavigator.MinimumAllowedDistance', 0.000000);
|
||||
openspace.setPropertyValueSingle('Scene.Moon.Renderable.LodScaleFactor', 24.0);
|
||||
|
||||
openspace.navigation.setCameraState({
|
||||
Anchor = earthAsset.Earth.Identifier,
|
||||
Position = { 0, 0, 0 },
|
||||
Rotation = { 0.758797, 0.221490, -0.605693, -0.091135 },
|
||||
})
|
||||
|
||||
openspace.globebrowsing.goToGeo(20, -60, 15000000)
|
||||
openspace.globebrowsing.goToGeo(earthAsset.Earth.Identifier, 20, -60, 15000000)
|
||||
end)
|
||||
|
||||
asset.onDeinitialize(function ()
|
||||
|
||||
@@ -96,12 +96,7 @@ asset.onInitialize(function ()
|
||||
-- openspace.setPropertyValueSingle('Scene.Moon.Renderable.Layers.HeightLayers.LRO_NAC_Apollo_11.Enabled', true);
|
||||
-- openspace.setPropertyValueSingle('Scene.Moon.Renderable.Layers.ColorLayers.A11_M177481212_p_longlat.Enabled', true);
|
||||
|
||||
openspace.navigation.setCameraState({
|
||||
Anchor = moonAsset.Moon.Identifier,
|
||||
Position = { 0, 0, 0 },
|
||||
Rotation = { 0, 0, 0, 0 },
|
||||
})
|
||||
openspace.globebrowsing.goToGeo(20, -60, 15000000)
|
||||
openspace.globebrowsing.goToGeo(moonAsset.Moon.Identifier, 20, -60, 15000000)
|
||||
|
||||
openspace.setPropertyValueSingle("Scene.Moon.Renderable.PerformShading", false)
|
||||
end)
|
||||
|
||||
@@ -10,10 +10,9 @@ asset.onInitialize(function ()
|
||||
|
||||
openspace.markInterestingNodes({ "Dawn", "Ceres", "Vesta" })
|
||||
|
||||
openspace.navigation.setCameraState({
|
||||
openspace.navigation.setNavigationState({
|
||||
Anchor = DawnAsset.Dawn.Identifier,
|
||||
Position = { 526781518487.171326, 257168309890.072144, -1381125204152.817383 },
|
||||
Rotation = { -0.106166, 0.981574, -0.084545, 0.134513 },
|
||||
})
|
||||
end)
|
||||
|
||||
|
||||
@@ -1,20 +1,15 @@
|
||||
asset.require('./base')
|
||||
|
||||
local earthAsset = asset.require('scene/solarsystem/planets/earth/earth')
|
||||
|
||||
asset.onInitialize(function ()
|
||||
local now = openspace.time.currentWallTime()
|
||||
-- Jump back one day to show a complete planet
|
||||
-- Jump back one day to be able to show complete weather data on Earth.
|
||||
openspace.time.setTime(openspace.time.advancedTime(now, "-1d"))
|
||||
|
||||
openspace.globebrowsing.goToGeo("Earth", 58.5877, 16.1924, 20000000)
|
||||
|
||||
openspace.markInterestingNodes({ "Earth", "Mars", "Moon", "Sun" })
|
||||
|
||||
openspace.navigation.setCameraState({
|
||||
Anchor = earthAsset.Earth.Identifier,
|
||||
Position = { 0, 0, 0 },
|
||||
Rotation = { 0.758797, 0.221490, -0.605693, -0.091135 },
|
||||
})
|
||||
|
||||
openspace.globebrowsing.goToGeo(58.5877, 16.1924, 20000000)
|
||||
end)
|
||||
|
||||
asset.onDeinitialize(function ()
|
||||
|
||||
@@ -1,8 +1,9 @@
|
||||
local assetHelper = asset.require('util/asset_helper')
|
||||
local sceneHelper = asset.require('util/scene_helper')
|
||||
local propertyHelper = asset.require('util/property_helper')
|
||||
local debugHelper = asset.require('util/debug_helper')
|
||||
|
||||
-- At this point, a sceene needs basic spice data to load.
|
||||
-- At this point, a scene needs basic spice data to load.
|
||||
asset.require('spice/base')
|
||||
|
||||
asset.require('util/default_keybindings')
|
||||
@@ -12,10 +13,15 @@ asset.require('util/default_joystick')
|
||||
asset.require('util/webgui')
|
||||
local spheres = asset.require('examples/spheres')
|
||||
|
||||
debugHelper.registerCartesianAxes(asset, {
|
||||
Parent = "Root",
|
||||
Scale = 10
|
||||
})
|
||||
|
||||
asset.onInitialize(function ()
|
||||
openspace.navigation.setCameraState({
|
||||
Anchor = spheres.ExampleSphere1.Identifier,
|
||||
Position = { 20, 0, 0 },
|
||||
Rotation = { 0.758797, 0.221490, -0.605693, -0.091135 }
|
||||
openspace.navigation.setNavigationState({
|
||||
Anchor = "Root",
|
||||
Position = { 20, 20, 20 },
|
||||
Up = {0, 1, 0},
|
||||
})
|
||||
end)
|
||||
|
||||
@@ -35,10 +35,9 @@ asset.onInitialize(function ()
|
||||
|
||||
openspace.markInterestingNodes({ "Gaia" })
|
||||
|
||||
openspace.navigation.setCameraState({
|
||||
openspace.navigation.setNavigationState({
|
||||
Anchor = earthAsset.Earth.Identifier,
|
||||
Position = { 1000000000000.0, 1000000000000.0, 1000000000000.0 },
|
||||
Rotation = { 0.683224, -0.765934, -0.601234, -0.418073 },
|
||||
})
|
||||
end)
|
||||
|
||||
|
||||
@@ -60,10 +60,11 @@ asset.onInitialize(function ()
|
||||
|
||||
openspace.markInterestingNodes({ "Insight" })
|
||||
|
||||
openspace.navigation.setCameraState({
|
||||
openspace.navigation.setNavigationState({
|
||||
Anchor = insightAsset.Insight.Identifier,
|
||||
Position = { 0, 0, 0 },
|
||||
Rotation = { 0.758797, 0.221490, -0.605693, -0.091135 },
|
||||
Position = { 8.430115E0, -1.791710E1, 2.813660E0 },
|
||||
ReferenceFrame = "Root",
|
||||
Up = { 0.494659E0,0.357162E0,0.792306E0 },
|
||||
})
|
||||
|
||||
end)
|
||||
|
||||
@@ -18,10 +18,11 @@ asset.onInitialize(function ()
|
||||
28800, 57600, 115200, 230400, 460800, 921600, 1843200, 3686400, 7372800, 14745600
|
||||
})
|
||||
|
||||
openspace.navigation.setCameraState({
|
||||
openspace.navigation.setNavigationState({
|
||||
Anchor = junoAsset.Juno.Identifier,
|
||||
Position = { 1837386367.601345, -389860693812.834839, 714830404470.398926 },
|
||||
Rotation = { -0.336540, 0.711402, -0.099212, 0.608937 },
|
||||
Position = { 1.243398E8, 7.176068E7, -1.519733E7 },
|
||||
ReferenceFrame = "Root",
|
||||
Up = { -0.377400E0, 0.764573E0, 0.522492E0 },
|
||||
})
|
||||
end)
|
||||
|
||||
|
||||
@@ -33,10 +33,11 @@ asset.onInitialize(function ()
|
||||
28800, 57600, 115200, 230400, 460800, 921600, 1843200, 3686400, 7372800, 14745600
|
||||
})
|
||||
|
||||
openspace.navigation.setCameraState({
|
||||
openspace.navigation.setNavigationState({
|
||||
Anchor = "Mercury",
|
||||
Position = { 526781518487.171326, 257168309890.072144, -1381125204152.817383 },
|
||||
Rotation = {0.180662, 0.021334, 0.979084, 0.091111},
|
||||
Position = { 2.423690E11, 1.979038E11, -2.241483E10 },
|
||||
ReferenceFrame = "Root",
|
||||
Up = { -0.492046E0, 0.666088E0, 0.560551E0 }
|
||||
})
|
||||
end)
|
||||
|
||||
|
||||
@@ -255,10 +255,11 @@ asset.onInitialize(function ()
|
||||
openspace.setPropertyValueSingle('Scene.Charon.Renderable.Enabled', false)
|
||||
openspace.setPropertyValueSingle("Scene.PlutoBarycenterTrail.Renderable.Enabled", false)
|
||||
|
||||
openspace.navigation.setCameraState({
|
||||
Anchor = NewHorizonsAsset.NewHorizons.Identifier,
|
||||
Position = { 4662120063743.592773, 1263245003503.724854, -955413856565.788086 },
|
||||
Rotation = { 0.683224, -0.165934, 0.701234, 0.118073 },
|
||||
openspace.navigation.setNavigationState({
|
||||
Anchor = "NewHorizons",
|
||||
ReferenceFrame = "Root",
|
||||
Position = { -6.572656E1, -7.239404E1, -2.111890E1 },
|
||||
Up = { 0.102164, -0.362945, 0.926193 }
|
||||
})
|
||||
end)
|
||||
|
||||
|
||||
@@ -134,10 +134,9 @@ asset.onInitialize(function ()
|
||||
|
||||
openspace.markInterestingNodes({ "OsirisRex", "BennuBarycenter", "Earth" })
|
||||
|
||||
openspace.navigation.setCameraState({
|
||||
openspace.navigation.setNavigationState({
|
||||
Anchor = OsirisRexAsset.OsirisRex.Identifier,
|
||||
Position = { 26974590199.661884, 76314608558.908020, -127086452897.101791 },
|
||||
Rotation = { 0.729548, -0.126024, 0.416827, 0.527382 },
|
||||
Position = { 26974590199.661884, 76314608558.908020, -127086452897.101791 }
|
||||
})
|
||||
end)
|
||||
|
||||
|
||||
@@ -134,10 +134,11 @@ asset.onInitialize(function ()
|
||||
28800, 57600, 115200, 230400, 460800, 921600, 1843200, 3686400, 7372800, 14745600
|
||||
})
|
||||
|
||||
openspace.navigation.setCameraState({
|
||||
openspace.navigation.setNavigationState({
|
||||
Anchor = Comet67PAsset.Comet67P.Identifier,
|
||||
Position = { 526781518487.171326, 257168309890.072144, -1381125204152.817383 },
|
||||
Rotation = { -0.106166, 0.981574, -0.084545, 0.134513 },
|
||||
ReferenceFrame = "Root",
|
||||
Position = { -7.294781E5 , -6.657894E5, 2.509047E6 },
|
||||
Up = { 0.146529E0, 0.944727E0, 0.293290E0 }
|
||||
})
|
||||
|
||||
openspace.setPropertyValue('Scene.67P.Renderable.PerformShading', false);
|
||||
|
||||
@@ -2,36 +2,36 @@
|
||||
|
||||
|
||||
local heightmaps = asset.syncedResource({
|
||||
Name = "Apollo Globebrowsing",
|
||||
Name = "Apollo Globebrowsing Heightmaps",
|
||||
Type = "HttpSynchronization",
|
||||
Identifier = "apollo_globebrowsing_heightmaps",
|
||||
Version = 1
|
||||
})
|
||||
|
||||
local basemaps = asset.syncedResource({
|
||||
Name = "Apollo Globebrowsing",
|
||||
Name = "Apollo Globebrowsing Basemaps",
|
||||
Type = "HttpSynchronization",
|
||||
Identifier = "apollo_globebrowsing_basemaps",
|
||||
Version = 1
|
||||
})
|
||||
|
||||
local naclighting = asset.syncedResource({
|
||||
Name = "Apollo Globebrowsing",
|
||||
Name = "Apollo Globebrowsing NAC Lighting",
|
||||
Type = "HttpSynchronization",
|
||||
Identifier = "apollo_globebrowsing_naclighting",
|
||||
Version = 1
|
||||
})
|
||||
|
||||
local stations = asset.syncedResource({
|
||||
Name = "Apollo 17 Globebrowsing",
|
||||
Name = "Apollo 17 Globebrowsing Stations",
|
||||
Type = "HttpSynchronization",
|
||||
Identifier = "apollo_17_stations",
|
||||
Version = 1
|
||||
})
|
||||
|
||||
asset.onInitialize(function ()
|
||||
openspace.globebrowsing.addBlendingLayersFromDirectory(heightmaps,"Moon")
|
||||
openspace.globebrowsing.addBlendingLayersFromDirectory(basemaps,"Moon")
|
||||
openspace.globebrowsing.addBlendingLayersFromDirectory(naclighting,"Moon")
|
||||
openspace.globebrowsing.addBlendingLayersFromDirectory(stations,"Moon")
|
||||
openspace.globebrowsing.addBlendingLayersFromDirectory(heightmaps, "Moon")
|
||||
openspace.globebrowsing.addBlendingLayersFromDirectory(basemaps, "Moon")
|
||||
openspace.globebrowsing.addBlendingLayersFromDirectory(naclighting, "Moon")
|
||||
openspace.globebrowsing.addBlendingLayersFromDirectory(stations, "Moon")
|
||||
end)
|
||||
|
||||
@@ -14,7 +14,7 @@ local carpoGroup = {
|
||||
{
|
||||
Identifier = "Carpo",
|
||||
Parent = {
|
||||
Name = parentIdentifier,
|
||||
Identifier = parentIdentifier,
|
||||
Spice = parentSpice
|
||||
},
|
||||
Spice = "CARPO",
|
||||
|
||||
@@ -47,10 +47,10 @@ asset.onInitialize(function ()
|
||||
"Earth", "Voyager 1", "Voyager 2", "Jupiter", "Saturn", "Uranus", "Neptune"
|
||||
})
|
||||
|
||||
openspace.navigation.setCameraState({
|
||||
openspace.navigation.setNavigationState({
|
||||
Anchor = VoyagerAsset.Voyager_1.Identifier,
|
||||
Position = { 526781518487.171326, 257168309890.072144, -1381125204152.817383 },
|
||||
Rotation = { -0.106166, 0.981574, -0.084545, 0.134513 },
|
||||
ReferenceFrame = "Root",
|
||||
Position = { 526781518487.171326, 257168309890.072144, -1381125204152.817383 }
|
||||
})
|
||||
end)
|
||||
|
||||
|
||||
@@ -51,7 +51,7 @@ public:
|
||||
|
||||
private:
|
||||
std::function<T(float)> _transferFunction;
|
||||
float _t = 0.f;
|
||||
float _t = 1.f;
|
||||
float _interpolationTime = 1.f;
|
||||
float _scaledDeltaTime = 0.f;
|
||||
};
|
||||
|
||||
@@ -25,13 +25,17 @@
|
||||
#ifndef __OPENSPACE_CORE___NAVIGATIONHANDLER___H__
|
||||
#define __OPENSPACE_CORE___NAVIGATIONHANDLER___H__
|
||||
|
||||
#include <openspace/properties/propertyowner.h>
|
||||
|
||||
#include <openspace/documentation/documentation.h>
|
||||
#include <openspace/interaction/inputstate.h>
|
||||
#include <openspace/interaction/joystickcamerastates.h>
|
||||
#include <openspace/interaction/orbitalnavigator.h>
|
||||
#include <openspace/interaction/keyframenavigator.h>
|
||||
#include <openspace/properties/propertyowner.h>
|
||||
#include <openspace/properties/stringproperty.h>
|
||||
#include <openspace/properties/scalar/boolproperty.h>
|
||||
#include <openspace/util/mouse.h>
|
||||
#include <openspace/util/keys.h>
|
||||
#include <optional>
|
||||
|
||||
namespace openspace {
|
||||
class Camera;
|
||||
@@ -48,6 +52,25 @@ class OrbitalNavigator;
|
||||
|
||||
class NavigationHandler : public properties::PropertyOwner {
|
||||
public:
|
||||
struct NavigationState {
|
||||
NavigationState() = default;
|
||||
NavigationState(const ghoul::Dictionary& dictionary);
|
||||
NavigationState(std::string anchor, std::string aim, std::string referenceFrame,
|
||||
glm::dvec3 position, std::optional<glm::dvec3> up = std::nullopt,
|
||||
double yaw = 0.0, double pitch = 0.0);
|
||||
|
||||
ghoul::Dictionary dictionary() const;
|
||||
static documentation::Documentation Documentation();
|
||||
|
||||
std::string anchor;
|
||||
std::string aim;
|
||||
std::string referenceFrame;
|
||||
glm::dvec3 position;
|
||||
std::optional<glm::dvec3> up;
|
||||
double yaw = 0.0;
|
||||
double pitch = 0.0;
|
||||
};
|
||||
|
||||
NavigationHandler();
|
||||
~NavigationHandler();
|
||||
|
||||
@@ -55,10 +78,10 @@ public:
|
||||
void deinitialize();
|
||||
|
||||
// Mutators
|
||||
void setNavigationStateNextFame(NavigationState state);
|
||||
void setCamera(Camera* camera);
|
||||
void setInterpolationTime(float durationInSeconds);
|
||||
|
||||
void setCameraStateFromDictionary(const ghoul::Dictionary& cameraDict);
|
||||
void updateCamera(double deltaTime);
|
||||
void setEnableKeyFrameInteraction();
|
||||
void setDisableKeyFrameInteraction();
|
||||
@@ -66,12 +89,11 @@ public:
|
||||
void stopPlayback();
|
||||
|
||||
// Accessors
|
||||
ghoul::Dictionary cameraStateDictionary();
|
||||
Camera* camera() const;
|
||||
const InputState& inputState() const;
|
||||
const OrbitalNavigator& orbitalNavigator() const;
|
||||
OrbitalNavigator& orbitalNavigator();
|
||||
KeyframeNavigator& keyframeNavigator() const;
|
||||
KeyframeNavigator& keyframeNavigator();
|
||||
bool isKeyFrameInteractionEnabled() const;
|
||||
float interpolationTime() const;
|
||||
|
||||
@@ -100,10 +122,14 @@ public:
|
||||
void clearJoystickButtonCommand(int button);
|
||||
std::vector<std::string> joystickButtonCommand(int button) const;
|
||||
|
||||
NavigationState navigationState(const SceneGraphNode& referenceFrame) const;
|
||||
|
||||
void saveNavigationState(const std::string& filepath,
|
||||
const std::string& referenceFrameIdentifier);
|
||||
|
||||
void saveCameraStateToFile(const std::string& filepath);
|
||||
void restoreCameraStateFromFile(const std::string& filepath);
|
||||
void loadNavigationState(const std::string& filepath);
|
||||
|
||||
void setNavigationStateNextFrame(NavigationState state);
|
||||
|
||||
/**
|
||||
* \return The Lua library that contains all Lua functions available to affect the
|
||||
@@ -112,15 +138,18 @@ public:
|
||||
static scripting::LuaLibrary luaLibrary();
|
||||
|
||||
private:
|
||||
bool _cameraUpdatedFromScript = false;
|
||||
void applyNavigationState(const NavigationHandler::NavigationState& ns);
|
||||
|
||||
bool _playbackModeEnabled = false;
|
||||
|
||||
std::unique_ptr<InputState> _inputState;
|
||||
InputState _inputState;
|
||||
Camera* _camera = nullptr;
|
||||
std::function<void()> _playbackEndCallback;
|
||||
|
||||
std::unique_ptr<OrbitalNavigator> _orbitalNavigator;
|
||||
std::unique_ptr<KeyframeNavigator> _keyframeNavigator;
|
||||
OrbitalNavigator _orbitalNavigator;
|
||||
KeyframeNavigator _keyframeNavigator;
|
||||
|
||||
std::optional<NavigationState> _pendingNavigationState;
|
||||
|
||||
properties::BoolProperty _useKeyFrameInteraction;
|
||||
};
|
||||
|
||||
@@ -38,6 +38,8 @@
|
||||
#include <ghoul/glm.h>
|
||||
#include <glm/gtx/quaternion.hpp>
|
||||
|
||||
#include <optional>
|
||||
|
||||
namespace openspace {
|
||||
class SceneGraphNode;
|
||||
class Camera;
|
||||
@@ -58,6 +60,7 @@ public:
|
||||
|
||||
Camera* camera() const;
|
||||
void setCamera(Camera* camera);
|
||||
void clearPreviousState();
|
||||
|
||||
void setFocusNode(const std::string& focusNode);
|
||||
void setAnchorNode(const std::string& anchorNode);
|
||||
@@ -70,6 +73,7 @@ public:
|
||||
void resetNodeMovements();
|
||||
|
||||
JoystickCameraStates& joystickStates();
|
||||
const JoystickCameraStates& joystickStates() const;
|
||||
|
||||
bool followingNodeRotation() const;
|
||||
const SceneGraphNode* anchorNode() const;
|
||||
@@ -146,10 +150,9 @@ private:
|
||||
const SceneGraphNode* _anchorNode = nullptr;
|
||||
const SceneGraphNode* _aimNode = nullptr;
|
||||
|
||||
glm::dvec3 _previousAnchorNodePosition;
|
||||
glm::dquat _previousAnchorNodeRotation;
|
||||
|
||||
glm::dvec3 _previousAimNodePosition;
|
||||
std::optional<glm::dvec3>_previousAnchorNodePosition;
|
||||
std::optional<glm::dquat> _previousAnchorNodeRotation;
|
||||
std::optional<glm::dvec3> _previousAimNodePosition;
|
||||
|
||||
double _currentCameraToSurfaceDistance = 0.0;
|
||||
bool _directlySetStereoDistance = false;
|
||||
|
||||
@@ -38,6 +38,7 @@
|
||||
#include <openspace/util/factorymanager.h>
|
||||
#include <ghoul/filesystem/filesystem.h>
|
||||
#include <ghoul/logging/logmanager.h>
|
||||
#include <ghoul/fmt.h>
|
||||
#include <ghoul/misc/templatefactory.h>
|
||||
#include <ghoul/misc/assert.h>
|
||||
#include <ghoul/systemcapabilities/generalcapabilitiescomponent.h>
|
||||
@@ -338,16 +339,24 @@ scripting::LuaLibrary GlobeBrowsingModule::luaLibrary() const {
|
||||
"goToGeo",
|
||||
&globebrowsing::luascriptfunctions::goToGeo,
|
||||
{},
|
||||
"number, number, number",
|
||||
"Go to geographic coordinates latitude and longitude"
|
||||
"[string], number, number, [number]",
|
||||
"Go to geographic coordinates of a globe. The first (optional) argument is "
|
||||
"the identifier of a scene graph node that has a RenderableGlobe attached. "
|
||||
"If no globe is passed in, the current anchor will be used. "
|
||||
"The second argument is latitude and the third is longitude (degrees). "
|
||||
"North and East are expressed as positive angles, while South and West are "
|
||||
"negative. The optional fourh argument is the altitude in meters. If no "
|
||||
"altitude is provided, the altitude will be kept as the current distance to "
|
||||
"the surface of the specified globe."
|
||||
},
|
||||
{
|
||||
"getGeoPosition",
|
||||
&globebrowsing::luascriptfunctions::getGeoPosition,
|
||||
{},
|
||||
"name, latitude, longitude, altitude",
|
||||
"Returns the specified surface position on the globe as three floating point "
|
||||
"values"
|
||||
"string, number, number, number",
|
||||
"Returns the specified surface position on the globe identified by the first "
|
||||
"argument, as three floating point values - latitude, longitude and altitude "
|
||||
"(degrees and meters)."
|
||||
},
|
||||
{
|
||||
"getGeoPositionForCamera",
|
||||
@@ -355,7 +364,7 @@ scripting::LuaLibrary GlobeBrowsingModule::luaLibrary() const {
|
||||
{},
|
||||
"void",
|
||||
"Get geographic coordinates of the camera poosition in latitude, "
|
||||
"longitude, and altitude"
|
||||
"longitude, and altitude (degrees and meters)."
|
||||
},
|
||||
{
|
||||
"loadWMSCapabilities",
|
||||
@@ -395,29 +404,29 @@ scripting::LuaLibrary GlobeBrowsingModule::luaLibrary() const {
|
||||
return res;
|
||||
}
|
||||
|
||||
void GlobeBrowsingModule::goToChunk(int x, int y, int level) {
|
||||
Camera* cam = global::navigationHandler.camera();
|
||||
goToChunk(*cam, globebrowsing::TileIndex(x,y,level), glm::vec2(0.5f, 0.5f), true);
|
||||
void GlobeBrowsingModule::goToChunk(const globebrowsing::RenderableGlobe& globe,
|
||||
int x, int y, int level)
|
||||
{
|
||||
goToChunk(globe, globebrowsing::TileIndex(x, y, level), glm::vec2(0.5f, 0.5f), true);
|
||||
}
|
||||
|
||||
void GlobeBrowsingModule::goToGeo(double latitude, double longitude) {
|
||||
void GlobeBrowsingModule::goToGeo(const globebrowsing::RenderableGlobe& globe,
|
||||
double latitude, double longitude)
|
||||
{
|
||||
using namespace globebrowsing;
|
||||
Camera* cam = global::navigationHandler.camera();
|
||||
goToGeodetic2(
|
||||
*cam,
|
||||
globe,
|
||||
Geodetic2{ glm::radians(latitude), glm::radians(longitude) },
|
||||
true
|
||||
);
|
||||
}
|
||||
|
||||
void GlobeBrowsingModule::goToGeo(double latitude, double longitude,
|
||||
double altitude)
|
||||
void GlobeBrowsingModule::goToGeo(const globebrowsing::RenderableGlobe& globe,
|
||||
double latitude, double longitude, double altitude)
|
||||
{
|
||||
using namespace globebrowsing;
|
||||
|
||||
Camera* cam = global::navigationHandler.camera();
|
||||
goToGeodetic3(
|
||||
*cam,
|
||||
globe,
|
||||
{
|
||||
Geodetic2{ glm::radians(latitude), glm::radians(longitude) },
|
||||
altitude
|
||||
@@ -437,32 +446,15 @@ glm::vec3 GlobeBrowsingModule::cartesianCoordinatesFromGeo(
|
||||
altitude
|
||||
};
|
||||
|
||||
const glm::dvec3 positionModelSpace = globe.ellipsoid().cartesianPosition(pos);
|
||||
//glm::dmat4 modelTransform = globe.modelTransform();
|
||||
//glm::dvec3 positionWorldSpace = glm::dvec3(modelTransform *
|
||||
//glm::dvec4(positionModelSpace, 1.0));
|
||||
|
||||
return glm::vec3(positionModelSpace);
|
||||
return glm::vec3(globe.ellipsoid().cartesianPosition(pos));
|
||||
}
|
||||
|
||||
void GlobeBrowsingModule::goToChunk(Camera& camera, const globebrowsing::TileIndex& ti,
|
||||
void GlobeBrowsingModule::goToChunk(const globebrowsing::RenderableGlobe& globe,
|
||||
const globebrowsing::TileIndex& ti,
|
||||
glm::vec2 uv, bool doResetCameraDirection)
|
||||
{
|
||||
using namespace globebrowsing;
|
||||
|
||||
const RenderableGlobe* globe = castFocusNodeRenderableToGlobe();
|
||||
if (!globe) {
|
||||
LERROR("Focus node must have a RenderableGlobe renderable.");
|
||||
return;
|
||||
}
|
||||
|
||||
// Camera position in model space
|
||||
const glm::dvec3 camPos = camera.positionVec3();
|
||||
const glm::dmat4 inverseModelTransform = glm::inverse(globe->modelTransform());
|
||||
const glm::dvec3 cameraPositionModelSpace = glm::dvec3(
|
||||
inverseModelTransform * glm::dvec4(camPos, 1)
|
||||
);
|
||||
|
||||
const GeodeticPatch patch(ti);
|
||||
const Geodetic2 corner = patch.corner(SOUTH_WEST);
|
||||
Geodetic2 positionOnPatch = patch.size();
|
||||
@@ -473,32 +465,50 @@ void GlobeBrowsingModule::goToChunk(Camera& camera, const globebrowsing::TileInd
|
||||
corner.lon + positionOnPatch.lon
|
||||
};
|
||||
|
||||
const glm::dvec3 positionOnEllipsoid = globe->ellipsoid().geodeticSurfaceProjection(
|
||||
// Compute altitude
|
||||
const glm::dvec3 cameraPosition = global::navigationHandler.camera()->positionVec3();
|
||||
SceneGraphNode* globeSceneGraphNode = dynamic_cast<SceneGraphNode*>(globe.owner());
|
||||
if (!globeSceneGraphNode) {
|
||||
LERROR(
|
||||
"Cannot go to chunk. The renderable is not attached to a scene graph node."
|
||||
);
|
||||
return;
|
||||
}
|
||||
const glm::dmat4 inverseModelTransform = globeSceneGraphNode->inverseModelTransform();
|
||||
const glm::dvec3 cameraPositionModelSpace =
|
||||
glm::dvec3(inverseModelTransform * glm::dvec4(cameraPosition, 1.0));
|
||||
const SurfacePositionHandle posHandle = globe.calculateSurfacePositionHandle(
|
||||
cameraPositionModelSpace
|
||||
);
|
||||
const double altitude = glm::length(cameraPositionModelSpace - positionOnEllipsoid);
|
||||
|
||||
const Geodetic2 geo2 = globe.ellipsoid().cartesianToGeodetic2(
|
||||
posHandle.centerToReferenceSurface
|
||||
);
|
||||
|
||||
goToGeodetic3(camera, {pointPosition, altitude}, doResetCameraDirection);
|
||||
const double altitude = glm::length(
|
||||
cameraPositionModelSpace - posHandle.centerToReferenceSurface
|
||||
);
|
||||
|
||||
goToGeodetic3(globe, { pointPosition, altitude }, doResetCameraDirection);
|
||||
}
|
||||
|
||||
void GlobeBrowsingModule::goToGeodetic2(Camera& camera, globebrowsing::Geodetic2 geo2,
|
||||
void GlobeBrowsingModule::goToGeodetic2(const globebrowsing::RenderableGlobe& globe,
|
||||
globebrowsing::Geodetic2 geo2,
|
||||
bool doResetCameraDirection)
|
||||
{
|
||||
using namespace globebrowsing;
|
||||
|
||||
const RenderableGlobe* globe = castFocusNodeRenderableToGlobe();
|
||||
if (!globe) {
|
||||
LERROR("Focus node must have a RenderableGlobe renderable.");
|
||||
return;
|
||||
const glm::dvec3 cameraPosition = global::navigationHandler.camera()->positionVec3();
|
||||
SceneGraphNode* globeSceneGraphNode = dynamic_cast<SceneGraphNode*>(globe.owner());
|
||||
if (!globeSceneGraphNode) {
|
||||
LERROR("Error when going to Geodetic2");
|
||||
}
|
||||
|
||||
interaction::NavigationHandler& nav = global::navigationHandler;
|
||||
const glm::dvec3 cameraPosition = nav.camera()->positionVec3();
|
||||
const glm::dmat4 inverseModelTransform =
|
||||
nav.orbitalNavigator().anchorNode()->inverseModelTransform();
|
||||
const glm::dmat4 inverseModelTransform = globeSceneGraphNode->inverseModelTransform();
|
||||
|
||||
const glm::dvec3 cameraPositionModelSpace =
|
||||
glm::dvec3(inverseModelTransform * glm::dvec4(cameraPosition, 1.0));
|
||||
const SurfacePositionHandle posHandle = globe->calculateSurfacePositionHandle(
|
||||
const SurfacePositionHandle posHandle = globe.calculateSurfacePositionHandle(
|
||||
cameraPositionModelSpace
|
||||
);
|
||||
|
||||
@@ -506,71 +516,58 @@ void GlobeBrowsingModule::goToGeodetic2(Camera& camera, globebrowsing::Geodetic2
|
||||
posHandle.referenceSurfaceOutDirection * posHandle.heightToSurface;
|
||||
const double altitude = glm::length(cameraPositionModelSpace - centerToActualSurface);
|
||||
|
||||
goToGeodetic3(camera, { geo2, altitude }, doResetCameraDirection);
|
||||
goToGeodetic3(globe, { geo2, altitude }, doResetCameraDirection);
|
||||
}
|
||||
|
||||
void GlobeBrowsingModule::goToGeodetic3(Camera& camera, globebrowsing::Geodetic3 geo3,
|
||||
void GlobeBrowsingModule::goToGeodetic3(const globebrowsing::RenderableGlobe& globe,
|
||||
globebrowsing::Geodetic3 geo3,
|
||||
bool doResetCameraDirection)
|
||||
{
|
||||
using namespace globebrowsing;
|
||||
const glm::dvec3 positionModelSpace = globe.ellipsoid().cartesianPosition(geo3);
|
||||
|
||||
const RenderableGlobe* globe = castFocusNodeRenderableToGlobe();
|
||||
if (!globe) {
|
||||
LERROR("Focus node must have a RenderableGlobe renderable.");
|
||||
return;
|
||||
}
|
||||
|
||||
const glm::dvec3 positionModelSpace = globe->ellipsoid().cartesianPosition(geo3);
|
||||
const glm::dmat4 modelTransform = globe->modelTransform();
|
||||
const glm::dvec3 positionWorldSpace = glm::dvec3(modelTransform *
|
||||
glm::dvec4(positionModelSpace, 1.0));
|
||||
camera.setPositionVec3(positionWorldSpace);
|
||||
const glm::dvec3 slightlyNorth = globe.ellipsoid().cartesianSurfacePosition(
|
||||
Geodetic2{ geo3.geodetic2.lat + 0.001, geo3.geodetic2.lon }
|
||||
);
|
||||
|
||||
if (doResetCameraDirection) {
|
||||
resetCameraDirection(camera, geo3.geodetic2);
|
||||
}
|
||||
interaction::NavigationHandler::NavigationState state;
|
||||
state.anchor = globe.owner()->identifier();
|
||||
state.referenceFrame = globe.owner()->identifier();
|
||||
state.position = positionModelSpace;
|
||||
state.up = slightlyNorth;
|
||||
|
||||
global::navigationHandler.setNavigationStateNextFrame(state);
|
||||
}
|
||||
|
||||
void GlobeBrowsingModule::resetCameraDirection(Camera& camera,
|
||||
globebrowsing::Geodetic2 geo2)
|
||||
glm::dquat GlobeBrowsingModule::lookDownCameraRotation(
|
||||
const globebrowsing::RenderableGlobe& globe,
|
||||
glm::dvec3 cameraModelSpace,
|
||||
globebrowsing::Geodetic2 geo2)
|
||||
{
|
||||
using namespace globebrowsing;
|
||||
|
||||
const RenderableGlobe* globe = castFocusNodeRenderableToGlobe();
|
||||
if (!globe) {
|
||||
LERROR("Focus node must have a RenderableGlobe renderable.");
|
||||
return;
|
||||
}
|
||||
|
||||
// Camera is described in world space
|
||||
const glm::dmat4 modelTransform = globe->modelTransform();
|
||||
const glm::dmat4 modelTransform = globe.modelTransform();
|
||||
|
||||
// Lookup vector
|
||||
const glm::dvec3 positionModelSpace = globe->ellipsoid().cartesianSurfacePosition(
|
||||
const glm::dvec3 positionModelSpace = globe.ellipsoid().cartesianSurfacePosition(
|
||||
geo2
|
||||
);
|
||||
const glm::dvec3 slightlyNorth = globe->ellipsoid().cartesianSurfacePosition(
|
||||
const glm::dvec3 slightlyNorth = globe.ellipsoid().cartesianSurfacePosition(
|
||||
Geodetic2{ geo2.lat + 0.001, geo2.lon }
|
||||
);
|
||||
const glm::dvec3 lookUpModelSpace = glm::normalize(
|
||||
slightlyNorth - positionModelSpace
|
||||
);
|
||||
const glm::dvec3 lookUpWorldSpace = glm::dmat3(modelTransform) * lookUpModelSpace;
|
||||
|
||||
// Lookat vector
|
||||
const glm::dvec3 lookAtWorldSpace = glm::dvec3(
|
||||
modelTransform * glm::dvec4(positionModelSpace, 1.0)
|
||||
);
|
||||
|
||||
// Eye position
|
||||
const glm::dvec3 eye = camera.positionVec3();
|
||||
|
||||
// Matrix
|
||||
const glm::dmat4 lookAtMatrix = glm::lookAt(eye, lookAtWorldSpace, lookUpWorldSpace);
|
||||
const glm::dmat4 lookAtMatrix =
|
||||
glm::lookAt(cameraModelSpace, positionModelSpace, lookUpModelSpace);
|
||||
|
||||
// Set rotation
|
||||
const glm::dquat rotation = glm::quat_cast(inverse(lookAtMatrix));
|
||||
camera.setRotation(rotation);
|
||||
return rotation;
|
||||
}
|
||||
|
||||
const globebrowsing::RenderableGlobe*
|
||||
@@ -732,5 +729,4 @@ uint64_t GlobeBrowsingModule::wmsCacheSize() const {
|
||||
return size * 1024 * 1024;
|
||||
}
|
||||
|
||||
|
||||
} // namespace openspace
|
||||
|
||||
@@ -53,9 +53,12 @@ public:
|
||||
|
||||
GlobeBrowsingModule();
|
||||
|
||||
void goToChunk(int x, int y, int level);
|
||||
void goToGeo(double latitude, double longitude);
|
||||
void goToGeo(double latitude, double longitude, double altitude);
|
||||
void goToChunk(const globebrowsing::RenderableGlobe& globe, int x, int y, int level);
|
||||
void goToGeo(const globebrowsing::RenderableGlobe& globe,
|
||||
double latitude, double longitude);
|
||||
|
||||
void goToGeo(const globebrowsing::RenderableGlobe& globe,
|
||||
double latitude, double longitude, double altitude);
|
||||
|
||||
glm::vec3 cartesianCoordinatesFromGeo(const globebrowsing::RenderableGlobe& globe,
|
||||
double latitude, double longitude, double altitude);
|
||||
@@ -94,13 +97,17 @@ protected:
|
||||
void internalInitialize(const ghoul::Dictionary&) override;
|
||||
|
||||
private:
|
||||
void goToChunk(Camera& camera, const globebrowsing::TileIndex& ti, glm::vec2 uv,
|
||||
bool doResetCameraDirection);
|
||||
void goToGeodetic2(Camera& camera, globebrowsing::Geodetic2 geo2,
|
||||
bool doResetCameraDirection);
|
||||
void goToGeodetic3(Camera& camera, globebrowsing::Geodetic3 geo3,
|
||||
bool doResetCameraDirection);
|
||||
void resetCameraDirection(Camera& camera, globebrowsing::Geodetic2 geo2);
|
||||
void goToChunk(const globebrowsing::RenderableGlobe& globe,
|
||||
const globebrowsing::TileIndex& ti, glm::vec2 uv, bool doResetCameraDirection);
|
||||
|
||||
void goToGeodetic2(const globebrowsing::RenderableGlobe& globe,
|
||||
globebrowsing::Geodetic2 geo2, bool doResetCameraDirection);
|
||||
|
||||
void goToGeodetic3(const globebrowsing::RenderableGlobe& globe,
|
||||
globebrowsing::Geodetic3 geo3, bool doResetCameraDirection);
|
||||
|
||||
glm::dquat lookDownCameraRotation(const globebrowsing::RenderableGlobe& globe,
|
||||
glm::dvec3 cameraPositionModelSpace, globebrowsing::Geodetic2 geo2);
|
||||
|
||||
/**
|
||||
\return a comma separated list of layer group names.
|
||||
|
||||
@@ -128,31 +128,79 @@ int deleteLayer(lua_State* L) {
|
||||
}
|
||||
|
||||
int goToChunk(lua_State* L) {
|
||||
ghoul::lua::checkArgumentsAndThrow(L, 3, "lua::goToChunk");
|
||||
ghoul::lua::checkArgumentsAndThrow(L, 4, "lua::goToChunk");
|
||||
|
||||
const int x = ghoul::lua::value<int>(L, 1);
|
||||
const int y = ghoul::lua::value<int>(L, 2);
|
||||
const int level = ghoul::lua::value<int>(L, 3);
|
||||
lua_pop(L, 3);
|
||||
const std::string& globeIdentifier = ghoul::lua::value<std::string>(L, 1);
|
||||
const int x = ghoul::lua::value<int>(L, 2);
|
||||
const int y = ghoul::lua::value<int>(L, 3);
|
||||
const int level = ghoul::lua::value<int>(L, 4);
|
||||
lua_pop(L, 4);
|
||||
|
||||
global::moduleEngine.module<GlobeBrowsingModule>()->goToChunk(x, y, level);
|
||||
SceneGraphNode* n = sceneGraphNode(globeIdentifier);
|
||||
if (!n) {
|
||||
return ghoul::lua::luaError(L, "Unknown globe name: " + globeIdentifier);
|
||||
}
|
||||
|
||||
const RenderableGlobe* globe = dynamic_cast<const RenderableGlobe*>(n->renderable());
|
||||
if (!globe) {
|
||||
return ghoul::lua::luaError(L, "Identifier must be a RenderableGlobe");
|
||||
}
|
||||
|
||||
global::moduleEngine.module<GlobeBrowsingModule>()->goToChunk(*globe, x, y, level);
|
||||
|
||||
ghoul_assert(lua_gettop(L) == 0, "Incorrect number of items left on stack");
|
||||
return 0;
|
||||
}
|
||||
|
||||
int goToGeo(lua_State* L) {
|
||||
const int nArguments = ghoul::lua::checkArgumentsAndThrow(L, 2, 3, "lua::goToGeo");
|
||||
const int nArguments = ghoul::lua::checkArgumentsAndThrow(L, { 2, 4 }, "lua::goToGeo");
|
||||
|
||||
const double latitude = ghoul::lua::value<double>(L, 1);
|
||||
const double longitude = ghoul::lua::value<double>(L, 2);
|
||||
// Check if the user provided a Scene graph node identifier as the first argument.
|
||||
// lua_isstring returns true for both numbers and strings, so better use !lua_isnumber
|
||||
const bool providedGlobeIdentifier = !lua_isnumber(L, 1);
|
||||
const int parameterOffset = providedGlobeIdentifier ? 1 : 0;
|
||||
|
||||
if (nArguments == 2) {
|
||||
global::moduleEngine.module<GlobeBrowsingModule>()->goToGeo(latitude, longitude);
|
||||
const SceneGraphNode* n;
|
||||
if (providedGlobeIdentifier) {
|
||||
const std::string& globeIdentifier = ghoul::lua::value<std::string>(L, 1);
|
||||
n = sceneGraphNode(globeIdentifier);
|
||||
if (!n) {
|
||||
return ghoul::lua::luaError(L, "Unknown globe name: " + globeIdentifier);
|
||||
}
|
||||
}
|
||||
else if (nArguments == 3) {
|
||||
const double altitude = ghoul::lua::value<double>(L, 3);
|
||||
else {
|
||||
n = global::navigationHandler.orbitalNavigator().anchorNode();
|
||||
if (!n) {
|
||||
return ghoul::lua::luaError(L, "No anchor node is set.");
|
||||
}
|
||||
}
|
||||
|
||||
const double latitude = ghoul::lua::value<double>(L, parameterOffset + 1);
|
||||
const double longitude = ghoul::lua::value<double>(L, parameterOffset + 2);
|
||||
|
||||
const RenderableGlobe* globe = dynamic_cast<const RenderableGlobe*>(n->renderable());
|
||||
if (!globe) {
|
||||
if (providedGlobeIdentifier) {
|
||||
return ghoul::lua::luaError(L, "Identifier must be a RenderableGlobe");
|
||||
}
|
||||
else {
|
||||
return ghoul::lua::luaError(L,
|
||||
"Current anchor node is not a RenderableGlobe. "
|
||||
"Either change the anchor to a globe, or specify a globe identifier "
|
||||
"as the first argument"
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
if (nArguments == parameterOffset + 2) {
|
||||
global::moduleEngine.module<GlobeBrowsingModule>()->goToGeo(
|
||||
*globe, latitude, longitude
|
||||
);
|
||||
}
|
||||
else if (nArguments == parameterOffset + 3) {
|
||||
const double altitude = ghoul::lua::value<double>(L, parameterOffset + 3);
|
||||
global::moduleEngine.module<GlobeBrowsingModule>()->goToGeo(
|
||||
*globe,
|
||||
latitude,
|
||||
longitude,
|
||||
altitude
|
||||
@@ -168,19 +216,19 @@ int goToGeo(lua_State* L) {
|
||||
int getGeoPosition(lua_State* L) {
|
||||
ghoul::lua::checkArgumentsAndThrow(L, 4, "lua::getGeoPosition");
|
||||
|
||||
const std::string& name = ghoul::lua::value<std::string>(L, 1);
|
||||
const std::string& globeIdentifier = ghoul::lua::value<std::string>(L, 1);
|
||||
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);
|
||||
lua_pop(L, 4);
|
||||
|
||||
SceneGraphNode* n = sceneGraphNode(name);
|
||||
SceneGraphNode* n = sceneGraphNode(globeIdentifier);
|
||||
if (!n) {
|
||||
return ghoul::lua::luaError(L, "Unknown globe name: " + name);
|
||||
return ghoul::lua::luaError(L, "Unknown globe identifier: " + globeIdentifier);
|
||||
}
|
||||
const RenderableGlobe* globe = dynamic_cast<const RenderableGlobe*>(n->renderable());
|
||||
if (!globe) {
|
||||
return ghoul::lua::luaError(L, "Name must be a RenderableGlobe");
|
||||
return ghoul::lua::luaError(L, "Identifier must be a RenderableGlobe");
|
||||
}
|
||||
|
||||
GlobeBrowsingModule& mod = *(global::moduleEngine.module<GlobeBrowsingModule>());
|
||||
|
||||
@@ -161,6 +161,11 @@ GlobeTranslation::GlobeTranslation(const ghoul::Dictionary& dictionary)
|
||||
_latitude.onChange([this]() { _positionIsDirty = true; });
|
||||
_fixedAltitude.onChange([this]() { _positionIsDirty = true; });
|
||||
_useFixedAltitude.onChange([this]() { _positionIsDirty = true; });
|
||||
|
||||
addProperty(_longitude);
|
||||
addProperty(_latitude);
|
||||
addProperty(_fixedAltitude);
|
||||
addProperty(_useFixedAltitude);
|
||||
}
|
||||
|
||||
void GlobeTranslation::fillAttachedNode() {
|
||||
|
||||
Binary file not shown.
@@ -58,6 +58,9 @@ namespace openspace {
|
||||
void registerCoreClasses(documentation::DocumentationEngine& engine) {
|
||||
engine.addDocumentation(LogFactoryDocumentation());
|
||||
engine.addDocumentation(Mission::Documentation());
|
||||
engine.addDocumentation(
|
||||
interaction::NavigationHandler::NavigationState::Documentation()
|
||||
);
|
||||
engine.addDocumentation(Renderable::Documentation());
|
||||
engine.addDocumentation(Rotation::Documentation());
|
||||
engine.addDocumentation(Scale::Documentation());
|
||||
|
||||
@@ -1013,8 +1013,6 @@ void OpenSpaceEngine::preSynchronization() {
|
||||
}
|
||||
|
||||
global::renderEngine.updateScene();
|
||||
//_navigationHandler->updateCamera(dt);
|
||||
|
||||
|
||||
if (_scene) {
|
||||
Camera* camera = _scene->camera();
|
||||
|
||||
@@ -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",
|
||||
|
||||
@@ -26,8 +26,8 @@
|
||||
|
||||
namespace openspace::luascriptfunctions {
|
||||
|
||||
int restoreCameraStateFromFile(lua_State* L) {
|
||||
ghoul::lua::checkArgumentsAndThrow(L, 1, "lua::restoreCameraStateFromFile");
|
||||
int loadNavigationState(lua_State* L) {
|
||||
ghoul::lua::checkArgumentsAndThrow(L, 1, "lua::loadNavigationState");
|
||||
|
||||
const std::string& cameraStateFilePath = ghoul::lua::value<std::string>(
|
||||
L,
|
||||
@@ -39,27 +39,35 @@ int restoreCameraStateFromFile(lua_State* L) {
|
||||
return ghoul::lua::luaError(L, "filepath string is empty");
|
||||
}
|
||||
|
||||
global::navigationHandler.restoreCameraStateFromFile(cameraStateFilePath);
|
||||
global::navigationHandler.loadNavigationState(cameraStateFilePath);
|
||||
|
||||
ghoul_assert(lua_gettop(L) == 0, "Incorrect number of items left on stack");
|
||||
return 0;
|
||||
}
|
||||
|
||||
int setCameraState(lua_State* L) {
|
||||
ghoul::lua::checkArgumentsAndThrow(L, 1, "lua::setCameraState");
|
||||
int setNavigationState(lua_State* L) {
|
||||
ghoul::lua::checkArgumentsAndThrow(L, 1, "lua::setNavigationState");
|
||||
|
||||
try {
|
||||
ghoul::Dictionary dictionary;
|
||||
ghoul::lua::luaDictionaryFromState(L, dictionary);
|
||||
global::navigationHandler.setCameraStateFromDictionary(dictionary);
|
||||
} catch (const ghoul::RuntimeError& e) {
|
||||
ghoul::Dictionary navigationStateDictionary;
|
||||
ghoul::lua::luaDictionaryFromState(L, navigationStateDictionary);
|
||||
|
||||
using namespace openspace::documentation;
|
||||
|
||||
TestResult r = testSpecification(
|
||||
interaction::NavigationHandler::NavigationState::Documentation(),
|
||||
navigationStateDictionary
|
||||
);
|
||||
|
||||
if (!r.success) {
|
||||
lua_settop(L, 0);
|
||||
return ghoul::lua::luaError(
|
||||
L,
|
||||
fmt::format("Could not set camera state: {}", e.what())
|
||||
fmt::format("Could not set camera state: {}", ghoul::to_string(r))
|
||||
);
|
||||
}
|
||||
|
||||
global::navigationHandler.setNavigationStateNextFrame(navigationStateDictionary);
|
||||
|
||||
// @CLEANUP: When luaDictionaryFromState doesn't leak space anymore, remove the next
|
||||
// line ---abock(2018-02-15)
|
||||
lua_settop(L, 0);
|
||||
@@ -67,21 +75,27 @@ int setCameraState(lua_State* L) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
int saveCameraStateToFile(lua_State* L) {
|
||||
ghoul::lua::checkArgumentsAndThrow(L, 1, "lua::saveCameraStateToFile");
|
||||
|
||||
const std::string& cameraStateFilePath = ghoul::lua::value<std::string>(
|
||||
int saveNavigationState(lua_State* L) {
|
||||
const int n = ghoul::lua::checkArgumentsAndThrow(
|
||||
L,
|
||||
1,
|
||||
ghoul::lua::PopValue::Yes
|
||||
{ 1, 2 },
|
||||
"lua::saveNavigationState"
|
||||
);
|
||||
|
||||
const std::string& cameraStateFilePath = ghoul::lua::value<std::string>(L, 1);
|
||||
|
||||
std::string referenceFrame = "";
|
||||
if (n > 1) {
|
||||
referenceFrame = ghoul::lua::value<std::string>(L, 2);
|
||||
}
|
||||
|
||||
if (cameraStateFilePath.empty()) {
|
||||
return ghoul::lua::luaError(L, "filepath string is empty");
|
||||
}
|
||||
|
||||
global::navigationHandler.saveCameraStateToFile(cameraStateFilePath);
|
||||
global::navigationHandler.saveNavigationState(cameraStateFilePath, referenceFrame);
|
||||
|
||||
lua_settop(L, 0);
|
||||
ghoul_assert(lua_gettop(L) == 0, "Incorrect number of items left on stack");
|
||||
return 0;
|
||||
}
|
||||
|
||||
@@ -365,20 +365,26 @@ void OrbitalNavigator::updateCameraStateFromStates(double deltaTime) {
|
||||
|
||||
const glm::dvec3 anchorPos = _anchorNode->worldPosition();
|
||||
const glm::dvec3 prevCameraPosition = _camera->positionVec3();
|
||||
const glm::dvec3 anchorDisplacement = anchorPos - _previousAnchorNodePosition;
|
||||
const glm::dvec3 anchorDisplacement = _previousAnchorNodePosition.has_value() ?
|
||||
(anchorPos - _previousAnchorNodePosition.value()) :
|
||||
glm::dvec3(0.0);
|
||||
|
||||
CameraPose pose = {
|
||||
_camera->positionVec3() + anchorDisplacement,
|
||||
_camera->rotationQuaternion()
|
||||
};
|
||||
|
||||
if (_aimNode && _aimNode != _anchorNode) {
|
||||
const bool hasPreviousPositions =
|
||||
_previousAnchorNodePosition.has_value() &&
|
||||
_previousAimNodePosition.has_value();
|
||||
|
||||
if (_aimNode && _aimNode != _anchorNode && hasPreviousPositions) {
|
||||
const glm::dvec3 aimPos = _aimNode->worldPosition();
|
||||
const glm::dvec3 cameraToAnchor =
|
||||
_previousAnchorNodePosition - prevCameraPosition;
|
||||
_previousAnchorNodePosition.value() - prevCameraPosition;
|
||||
|
||||
Displacement anchorToAim = {
|
||||
_previousAimNodePosition - _previousAnchorNodePosition,
|
||||
_previousAimNodePosition.value() - _previousAnchorNodePosition.value(),
|
||||
aimPos - anchorPos
|
||||
};
|
||||
|
||||
@@ -413,8 +419,9 @@ void OrbitalNavigator::updateCameraStateFromStates(double deltaTime) {
|
||||
glm::dquat anchorRotation =
|
||||
glm::quat_cast(_anchorNode->worldRotationMatrix());
|
||||
|
||||
glm::dquat anchorNodeRotationDiff =
|
||||
_previousAnchorNodeRotation * glm::inverse(anchorRotation);
|
||||
glm::dquat anchorNodeRotationDiff = _previousAnchorNodeRotation.has_value() ?
|
||||
_previousAnchorNodeRotation.value() * glm::inverse(anchorRotation) :
|
||||
glm::dquat();
|
||||
|
||||
_previousAnchorNodeRotation = anchorRotation;
|
||||
|
||||
@@ -563,11 +570,17 @@ void OrbitalNavigator::setAnchorNode(const SceneGraphNode* anchorNode) {
|
||||
_anchorNode = anchorNode;
|
||||
|
||||
if (_anchorNode) {
|
||||
_previousAnchorNodePosition = _anchorNode->worldPosition();
|
||||
_previousAnchorNodeRotation = glm::quat_cast(_anchorNode->worldRotationMatrix());
|
||||
_previousAnchorNodePosition.reset();
|
||||
_previousAnchorNodeRotation.reset();
|
||||
}
|
||||
}
|
||||
|
||||
void OrbitalNavigator::clearPreviousState() {
|
||||
_previousAnchorNodePosition.reset();
|
||||
_previousAnchorNodeRotation.reset();
|
||||
_previousAimNodePosition.reset();
|
||||
}
|
||||
|
||||
void OrbitalNavigator::setAimNode(const SceneGraphNode* aimNode) {
|
||||
_retargetAimInterpolator.end();
|
||||
_aimNode = aimNode;
|
||||
@@ -652,6 +665,9 @@ void OrbitalNavigator::setRetargetInterpolationTime(float durationInSeconds) {
|
||||
}
|
||||
|
||||
bool OrbitalNavigator::followingNodeRotation() const {
|
||||
if (_aimNode != nullptr && _aimNode != _anchorNode) {
|
||||
return false;
|
||||
}
|
||||
return _followRotationInterpolator.value() >= 1.0;
|
||||
}
|
||||
|
||||
@@ -1219,4 +1235,8 @@ JoystickCameraStates& OrbitalNavigator::joystickStates() {
|
||||
return _joystickStates;
|
||||
}
|
||||
|
||||
const JoystickCameraStates& OrbitalNavigator::joystickStates() const {
|
||||
return _joystickStates;
|
||||
}
|
||||
|
||||
} // namespace openspace::interaction
|
||||
|
||||
@@ -374,14 +374,14 @@ void SceneGraphNode::update(const UpdateData& data) {
|
||||
}
|
||||
UpdateData newUpdateData = data;
|
||||
|
||||
_worldRotationCached = calculateWorldRotation();
|
||||
_worldScaleCached = calculateWorldScale();
|
||||
// Assumes _worldRotationCached and _worldScaleCached have been calculated for parent
|
||||
_worldPositionCached = calculateWorldPosition();
|
||||
_worldRotationCached = calculateWorldRotation();
|
||||
_worldScaleCached = calculateWorldScale();
|
||||
|
||||
newUpdateData.modelTransform.translation = worldPosition();
|
||||
newUpdateData.modelTransform.rotation = worldRotationMatrix();
|
||||
newUpdateData.modelTransform.scale = worldScale();
|
||||
newUpdateData.modelTransform.translation = _worldPositionCached;
|
||||
newUpdateData.modelTransform.rotation = _worldRotationCached;
|
||||
newUpdateData.modelTransform.scale = _worldScaleCached;
|
||||
|
||||
glm::dmat4 translation = glm::translate(
|
||||
glm::dmat4(1.0),
|
||||
@@ -655,7 +655,7 @@ bool SceneGraphNode::hasGuiHintHidden() const {
|
||||
glm::dvec3 SceneGraphNode::calculateWorldPosition() const {
|
||||
// recursive up the hierarchy if there are parents available
|
||||
if (_parent) {
|
||||
const glm::dvec3 wp = _parent->calculateWorldPosition();
|
||||
const glm::dvec3 wp = _parent->worldPosition();
|
||||
const glm::dmat3 wrot = _parent->worldRotationMatrix();
|
||||
const double ws = _parent->worldScale();
|
||||
const glm::dvec3 p = position();
|
||||
@@ -684,7 +684,7 @@ bool SceneGraphNode::isTimeFrameActive(const Time& time) const {
|
||||
glm::dmat3 SceneGraphNode::calculateWorldRotation() const {
|
||||
// recursive up the hierarchy if there are parents available
|
||||
if (_parent) {
|
||||
return _parent->calculateWorldRotation() * rotationMatrix();
|
||||
return _parent->worldRotationMatrix() * rotationMatrix();
|
||||
}
|
||||
else {
|
||||
return rotationMatrix();
|
||||
@@ -694,7 +694,7 @@ glm::dmat3 SceneGraphNode::calculateWorldRotation() const {
|
||||
double SceneGraphNode::calculateWorldScale() const {
|
||||
// recursive up the hierarchy if there are parents available
|
||||
if (_parent) {
|
||||
return _parent->calculateWorldScale() * scale();
|
||||
return _parent->worldScale() * scale();
|
||||
}
|
||||
else {
|
||||
return scale();
|
||||
|
||||
Reference in New Issue
Block a user