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:
Emil Axelsson
2019-07-16 13:13:33 +02:00
committed by Alexander Bock
parent b25b205e99
commit f43bcadee3
29 changed files with 698 additions and 374 deletions

View File

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

View File

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

View File

@@ -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>());

View File

@@ -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() {