diff --git a/data/assets/util/webgui.asset b/data/assets/util/webgui.asset index 4acaca95e3..d6f0016f79 100644 --- a/data/assets/util/webgui.asset +++ b/data/assets/util/webgui.asset @@ -28,6 +28,14 @@ local showcomposer = asset.resource({ }) +local maps = asset.resource({ + Identifier = "userinterface_maps", + Name = "Userinterface Maps", + Type = "HttpSynchronization", + Version = 1 +}) + + asset.onInitialize(function() -- Unzip the frontend bundle local destFrontend = frontend .. "frontend" @@ -66,6 +74,10 @@ asset.onInitialize(function() directories[#directories + 1] = "showcomposer/projects" directories[#directories + 1] = openspace.absPath("${USER_SHOWCOMPOSER_PROJECTS}") + -- Add asset folders + directories[#directories + 1] = "assets/maps" + directories[#directories + 1] = maps + openspace.setPropertyValueSingle("Modules.WebGui.Directories", directories) openspace.setPropertyValueSingle("Modules.WebGui.DefaultEndpoint", "gui") openspace.setPropertyValueSingle("Modules.WebGui.ServerProcessEnabled", enabled) diff --git a/include/openspace/util/geodetic.h b/include/openspace/util/geodetic.h index ddc614b7a4..66b2a42a20 100644 --- a/include/openspace/util/geodetic.h +++ b/include/openspace/util/geodetic.h @@ -71,6 +71,19 @@ glm::vec3 cartesianCoordinatesFromGeo(const SceneGraphNode& sgn, double latitude */ glm::dvec3 geoPositionFromCamera(); +/** + * Returns the camera view direction relative to the current anchor node as geodetic + * coordinates. The returned value contains the latitude and longitude in degrees in the + * x, and y-coordinate respectively. + */ +glm::dvec3 geoViewFromCamera(); + +/** + * Return the coordinates where the sun is at its zenith at the current anchor at the + * current time. The returned value contains the latitude and longitude in degrees. + */ +glm::dvec2 subSolarCoordinates(); + /** * Returns the height of the camera relative to the provided SceneGraphNode \p sgn in * meters. If \p useHeightMap is provided as `true` and \p sgn is a globe with an existing diff --git a/modules/server/src/topics/cameratopic.cpp b/modules/server/src/topics/cameratopic.cpp index e26e728ff4..27c9622179 100644 --- a/modules/server/src/topics/cameratopic.cpp +++ b/modules/server/src/topics/cameratopic.cpp @@ -84,13 +84,28 @@ void CameraTopic::handleJson(const nlohmann::json& json) { void CameraTopic::sendCameraData() { #ifdef OPENSPACE_MODULE_SPACE_ENABLED glm::dvec3 position = geoPositionFromCamera(); + glm::dvec3 direction = geoViewFromCamera(); + const double viewLength = direction.z; std::pair altSimplified = simplifyDistance(position.z); + glm::dvec2 subSolar = subSolarCoordinates(); - const nlohmann::json jsonData = { + glm::dvec2 dir = glm::dvec2(direction) - glm::dvec2(position); + if (glm::length(dir) > 1e-6) { + // Avoid sending NaNs/null from bad normalization + dir = glm::normalize(dir); + } + + nlohmann::json jsonData = { { "latitude", position.x }, { "longitude", position.y }, { "altitude", altSimplified.first }, - { "altitudeUnit", altSimplified.second } + { "altitudeUnit", altSimplified.second }, + { "altitudeMeters", position.z }, + { "viewLatitude", dir.x }, + { "viewLongitude", dir.y }, + { "viewLength", viewLength }, + { "subSolarLatitude", subSolar.x }, + { "subSolarLongitude", subSolar.y }, }; _connection->sendJson(wrappedPayload(jsonData)); diff --git a/src/util/geodetic.cpp b/src/util/geodetic.cpp index 3d3038c16f..2648285a72 100644 --- a/src/util/geodetic.cpp +++ b/src/util/geodetic.cpp @@ -83,12 +83,12 @@ glm::dvec3 geoPositionFromCamera() { cameraPositionModelSpace ); - const Geodetic2 geo2 = renderable->ellipsoid().cartesianToGeodetic2( + const Geodetic2 geo = renderable->ellipsoid().cartesianToGeodetic2( posHandle.centerToReferenceSurface ); - const double lat = glm::degrees(geo2.lat); - const double lon = glm::degrees(geo2.lon); + const double lat = glm::degrees(geo.lat); + const double lon = glm::degrees(geo.lon); double altitude = glm::length( cameraPositionModelSpace - posHandle.centerToReferenceSurface @@ -103,6 +103,76 @@ glm::dvec3 geoPositionFromCamera() { return glm::dvec3(lat, lon, altitude); } +glm::dvec3 geoViewFromCamera() { + const SceneGraphNode* n = global::navigationHandler->orbitalNavigator().anchorNode(); + if (!n) { + return glm::dvec3(0.0); + } + + const glm::dmat4 inverseModelTransform = glm::inverse(n->modelTransform()); + + // Get the position of the camera in model space + const glm::dvec3 cameraPosition = global::navigationHandler->camera()->positionVec3(); + const glm::dvec3 cameraPositionModelSpace = + glm::dvec3(inverseModelTransform * glm::dvec4(cameraPosition, 1.0)); + + // Get the direction of the camera in model space + const glm::dvec3 cameraViewDirection = + global::navigationHandler->camera()->viewDirectionWorldSpace(); + // Scaling the cameraViewDirection to move the precision up a few decimals + const glm::dvec3 cameraViewPoint = cameraPosition + 10000.0 * cameraViewDirection; + glm::dvec3 cameraViewDirectionModelSpace = + glm::dvec3(inverseModelTransform * glm::dvec4(cameraViewPoint, 1.0)); + + + // `d` represents how much we are looking towards the center of the scene graph node + // The closer `d` is to -1 or 1, the more we are looking either towards the center or + // away from it + const glm::dmat3 rotationOnly = glm::mat3_cast(glm::quat_cast(n->modelTransform())); + const double d = glm::dot( + glm::normalize(cameraPositionModelSpace), + glm::normalize(glm::transpose(rotationOnly) * cameraViewDirection) + ); + + const SurfacePositionHandle posHandle = n->calculateSurfacePositionHandle( + cameraViewDirectionModelSpace + ); + + const Geodetic2 geo = n->ellipsoid().cartesianToGeodetic2( + posHandle.centerToReferenceSurface + ); + + const double lat = glm::degrees(geo.lat); + const double lon = glm::degrees(geo.lon); + return glm::dvec3(lat, lon, d); +} + +glm::dvec2 subSolarCoordinates() { + const SceneGraphNode* n = global::navigationHandler->orbitalNavigator().anchorNode(); + if (!n) { + return glm::dvec3(0.0); + } + + const glm::dmat4 inverseModelTransform = glm::inverse(n->modelTransform()); + // @TODO (2025-07-16, abock): For now we use the position of the SolarSystemBarycenter + // to calculate the Sun position. While this is not completely correct and precludes + // the calculation of subsolar coordinates on exoplanets, that is a problem left for + // later + const glm::dvec3 ssbPositionModelSpace = + glm::dvec3(inverseModelTransform * glm::dvec4(-n->worldPosition(), 1.0)); + + const SurfacePositionHandle sunPosition = n->calculateSurfacePositionHandle( + ssbPositionModelSpace + ); + const Geodetic2 subsolarCoordinates = n->ellipsoid().cartesianToGeodetic2( + sunPosition.centerToReferenceSurface + ); + + const double lat = glm::degrees(subsolarCoordinates.lat); + const double lon = glm::degrees(subsolarCoordinates.lon); + return glm::dvec2(lat, lon); +} + double altitudeFromCamera(const SceneGraphNode& sgn, bool useHeightMap) { const glm::dvec3 cameraPosition = global::navigationHandler->camera()->positionVec3();