diff --git a/data/assets/examples/globetranslation.asset b/data/assets/examples/globetranslation.asset new file mode 100644 index 0000000000..473c073275 --- /dev/null +++ b/data/assets/examples/globetranslation.asset @@ -0,0 +1,75 @@ +local assetHelper = asset.require('util/asset_helper') +local earth = asset.require('scene/solarsystem/planets/earth/earth') +local sunTransforms = asset.require('scene/solarsystem/sun/transforms') + + +local textures = asset.syncedResource({ + Name = "New Horizons Textures", + Type = "HttpSynchronization", + Identifier = "newhorizons_textures", + Version = 3 +}) + +local models = asset.syncedResource({ + Name = "New Horizons Model", + Type = "HttpSynchronization", + Identifier = "newhorizons_model", + Version = 1 +}) + +local NewHorizons_Example_Fixed_Height = { + Identifier = "NewHorizons_Example_Fixed_Height", + Parent = earth.Earth.Identifier, + Transform = { + Translation = { + Type = "GlobeTranslation", + Globe = earth.Earth.Identifier, + Longitude = 0.0, + Latitude = 0.0, + FixedAltitude = 10000000.0 + } + }, + Renderable = { + Type = "RenderableModel", + Body = "NEW HORIZONS", + Geometry = { + Type = "MultiModelGeometry", + GeometryFile = models .. "/NewHorizonsCleanModel.obj" + }, + ColorTexture = textures .. "/NHTexture.jpg", + }, + GUI = { + Path = "/Example" + } +} + +local NewHorizons_Example_Adaptive_Height = { + Identifier = "NewHorizons_Example_Adaptive_Height", + Parent = earth.Earth.Identifier, + Transform = { + Translation = { + Type = "GlobeTranslation", + Globe = earth.Earth.Identifier, + Longitude = 0.0, + Latitude = 0.0 + } + }, + Renderable = { + Type = "RenderableModel", + Body = "NEW HORIZONS", + Geometry = { + Type = "MultiModelGeometry", + GeometryFile = models .. "/NewHorizonsCleanModel.obj" + }, + ColorTexture = textures .. "/NHTexture.jpg", + }, + GUI = { + Path = "/Example" + } +} + +assetHelper.registerSceneGraphNodesAndExport(asset, { + NewHorizons_Example_Fixed_Height, + NewHorizons_Example_Adaptive_Height +}) + diff --git a/modules/globebrowsing/CMakeLists.txt b/modules/globebrowsing/CMakeLists.txt index 58c2c682da..685edd58a8 100644 --- a/modules/globebrowsing/CMakeLists.txt +++ b/modules/globebrowsing/CMakeLists.txt @@ -33,6 +33,7 @@ set(HEADER_FILES ${CMAKE_CURRENT_SOURCE_DIR}/src/ellipsoid.h ${CMAKE_CURRENT_SOURCE_DIR}/src/gdalwrapper.h ${CMAKE_CURRENT_SOURCE_DIR}/src/geodeticpatch.h + ${CMAKE_CURRENT_SOURCE_DIR}/src/globetranslation.h ${CMAKE_CURRENT_SOURCE_DIR}/src/gpulayergroup.h ${CMAKE_CURRENT_SOURCE_DIR}/src/layer.h ${CMAKE_CURRENT_SOURCE_DIR}/src/layeradjustment.h @@ -67,6 +68,7 @@ set(SOURCE_FILES ${CMAKE_CURRENT_SOURCE_DIR}/src/ellipsoid.cpp ${CMAKE_CURRENT_SOURCE_DIR}/src/gdalwrapper.cpp ${CMAKE_CURRENT_SOURCE_DIR}/src/geodeticpatch.cpp + ${CMAKE_CURRENT_SOURCE_DIR}/src/globetranslation.cpp ${CMAKE_CURRENT_SOURCE_DIR}/src/gpulayergroup.cpp ${CMAKE_CURRENT_SOURCE_DIR}/src/layer.cpp ${CMAKE_CURRENT_SOURCE_DIR}/src/layeradjustment.cpp diff --git a/modules/globebrowsing/globebrowsingmodule.cpp b/modules/globebrowsing/globebrowsingmodule.cpp index 704ee43acc..835158872d 100644 --- a/modules/globebrowsing/globebrowsingmodule.cpp +++ b/modules/globebrowsing/globebrowsingmodule.cpp @@ -28,6 +28,7 @@ #include #include #include +#include #include #include #include @@ -216,18 +217,18 @@ void GlobeBrowsingModule::internalInitialize(const ghoul::Dictionary& dict) { // Deinitialize global::callback::deinitialize.emplace_back([&]() { GdalWrapper::destroy(); }); - // Get factories auto fRenderable = FactoryManager::ref().factory(); ghoul_assert(fRenderable, "Renderable factory was not created"); - // Create factory for TileProviders + fRenderable->registerClass("RenderableGlobe"); + + auto fTranslation = FactoryManager::ref().factory(); + ghoul_assert(fTranslation, "Translation factory was not created"); + fTranslation->registerClass("GlobeTranslation"); + auto fTileProvider = std::make_unique>(); ghoul_assert(fTileProvider, "TileProvider factory was not created"); - // Register renderable class - fRenderable->registerClass("RenderableGlobe"); - - // Register TileProvider classes fTileProvider->registerClass( layergroupid::LAYER_TYPE_NAMES[static_cast( layergroupid::TypeID::DefaultTileLayer diff --git a/modules/globebrowsing/src/globetranslation.cpp b/modules/globebrowsing/src/globetranslation.cpp new file mode 100644 index 0000000000..8cab831e81 --- /dev/null +++ b/modules/globebrowsing/src/globetranslation.cpp @@ -0,0 +1,231 @@ +/***************************************************************************************** + * * + * OpenSpace * + * * + * Copyright (c) 2014-2018 * + * * + * Permission is hereby granted, free of charge, to any person obtaining a copy of this * + * software and associated documentation files (the "Software"), to deal in the Software * + * without restriction, including without limitation the rights to use, copy, modify, * + * merge, publish, distribute, sublicense, and/or sell copies of the Software, and to * + * permit persons to whom the Software is furnished to do so, subject to the following * + * conditions: * + * * + * The above copyright notice and this permission notice shall be included in all copies * + * or substantial portions of the Software. * + * * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, * + * INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A * + * PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT * + * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF * + * CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE * + * OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. * + ****************************************************************************************/ + +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +namespace { + constexpr openspace::properties::Property::PropertyInfo GlobeInfo = { + "Globe", + "Attached Globe", + "The globe on which the longitude/latitude is specified" + }; + + constexpr openspace::properties::Property::PropertyInfo LongitudeInfo = { + "Longitude", + "Longitude", + "The longitude of the location on the globe's surface. The value can range from " + "-180 to 180, with negative values representing the western hemisphere of the " + "globe. The default value is 0.0" + }; + + constexpr openspace::properties::Property::PropertyInfo LatitudeInfo = { + "Latitude", + "Latitude", + "The latitude of the location on the globe's surface. The value can range from " + "-90 to 90, with negative values representing the southern hemisphere of the " + "globe. The default value is 0.0" + }; + + constexpr openspace::properties::Property::PropertyInfo FixedAltitudeInfo = { + "FixedAltitude", + "Fixed Altitude", + "The altitude in meters of the location on the globe's surface. This value is " + "used if the 'UseFixedAltitude' property is 'true'. The default value is 10000km." + }; + + constexpr openspace::properties::Property::PropertyInfo UseFixedAltitudeInfo = { + "UseFixedAltitude", + "Use Fixed Altitude", + "If this value is 'true', the altitude specified in 'FixedAltitude' is used for " + "this translation. If it is 'false', the altitude will be computed based on the " + "height information that is available about the globe to which this translation " + "is attached. The default value is 'true'." + }; +} // namespace + +namespace openspace::globebrowsing { + +documentation::Documentation GlobeTranslation::Documentation() { + using namespace openspace::documentation; + + return { + "Spice Translation", + "space_translation_spicetranslation", + { + { + "Type", + new StringEqualVerifier("GlobeTranslation"), + Optional::No + }, + { + GlobeInfo.identifier, + new StringAnnotationVerifier( + "A valid scene graph node with a RenderableGlobe" + ), + Optional::No, + GlobeInfo.description + }, + { + LongitudeInfo.identifier, + new DoubleVerifier, + Optional::Yes, + LongitudeInfo.description + }, + { + LatitudeInfo.identifier, + new DoubleVerifier, + Optional::Yes, + LatitudeInfo.description, + }, + { + FixedAltitudeInfo.identifier, + new DoubleVerifier, + Optional::Yes, + FixedAltitudeInfo.description + }, + { + UseFixedAltitudeInfo.identifier, + new BoolVerifier, + Optional::Yes, + UseFixedAltitudeInfo.description + } + } + }; +} + +GlobeTranslation::GlobeTranslation(const ghoul::Dictionary& dictionary) + : _globe(GlobeInfo) + , _longitude(LongitudeInfo, 0.0, -180.0, 180.0) + , _latitude(LatitudeInfo, 0.0, -90.0, 90.0) + , _fixedAltitude(FixedAltitudeInfo, 1e8, 0.0, 1e12) + , _useFixedAltitude(UseFixedAltitudeInfo, false) +{ + documentation::testSpecificationAndThrow( + Documentation(), + dictionary, + "GlobeTranslation" + ); + + _globe = dictionary.value(GlobeInfo.identifier); + if (dictionary.hasKey(LongitudeInfo.identifier)) { + _longitude = dictionary.value(LongitudeInfo.identifier); + } + if (dictionary.hasKey(LatitudeInfo.identifier)) { + _latitude = dictionary.value(LatitudeInfo.identifier); + } + if (dictionary.hasKey(FixedAltitudeInfo.identifier)) { + _fixedAltitude = dictionary.value(FixedAltitudeInfo.identifier); + } + if (dictionary.hasKey(UseFixedAltitudeInfo.identifier)) { + _useFixedAltitude = dictionary.value(UseFixedAltitudeInfo.identifier); + } + + _globe.onChange([this]() { + fillAttachedNode(); + _positionIsDirty = true; + }); + + _longitude.onChange([this]() { _positionIsDirty = true; }); + _latitude.onChange([this]() { _positionIsDirty = true; }); + _fixedAltitude.onChange([this]() { _positionIsDirty = true; }); + _useFixedAltitude.onChange([this]() { _positionIsDirty = true; }); +} + +void GlobeTranslation::fillAttachedNode() { + SceneGraphNode* n = sceneGraphNode(_globe); + if (n->renderable() && dynamic_cast(n->renderable())) { + _attachedNode = dynamic_cast(n->renderable()); + } + else { + LERRORC( + "GlobeTranslation", + "Could not set attached node as it does not have a RenderableGlobe" + ); + // Reset the globe name to it's previous name + _globe = _attachedNode->identifier(); + } +} + +glm::dvec3 GlobeTranslation::position(const UpdateData&) const { + if (!_attachedNode) { + // @TODO(abock): The const cast should be removed on a redesign of the translation + // to make the position function not constant. Const casting this + // away is fine as the factories that create the translations don't + // create them as constant objects + const_cast(this)->fillAttachedNode(); + _positionIsDirty = true; + } + + if (!_useFixedAltitude) { + // If we don't use the fixed altitude, we have to compute the height every frame + _positionIsDirty = true; + } + + if (!_positionIsDirty) { + return _position; + } + + + GlobeBrowsingModule& mod = *(global::moduleEngine.module()); + + glm::vec3 pos = mod.cartesianCoordinatesFromGeo( + *_attachedNode, + _latitude, + _longitude, + _fixedAltitude + ); + + if (_useFixedAltitude) { + _position = glm::dvec3(pos); + _positionIsDirty = true; + + return _position; + } + else { + SurfacePositionHandle h = _attachedNode->calculateSurfacePositionHandle(pos); + + pos = mod.cartesianCoordinatesFromGeo( + *_attachedNode, + _latitude, + _longitude, + h.heightToSurface + ); + + _position = glm::dvec3(pos); + return _position; + } +} + +} // namespace openspace::globebrowsing diff --git a/modules/globebrowsing/src/globetranslation.h b/modules/globebrowsing/src/globetranslation.h new file mode 100644 index 0000000000..0486497a32 --- /dev/null +++ b/modules/globebrowsing/src/globetranslation.h @@ -0,0 +1,63 @@ +/***************************************************************************************** + * * + * OpenSpace * + * * + * Copyright (c) 2014-2018 * + * * + * Permission is hereby granted, free of charge, to any person obtaining a copy of this * + * software and associated documentation files (the "Software"), to deal in the Software * + * without restriction, including without limitation the rights to use, copy, modify, * + * merge, publish, distribute, sublicense, and/or sell copies of the Software, and to * + * permit persons to whom the Software is furnished to do so, subject to the following * + * conditions: * + * * + * The above copyright notice and this permission notice shall be included in all copies * + * or substantial portions of the Software. * + * * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, * + * INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A * + * PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT * + * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF * + * CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE * + * OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. * + ****************************************************************************************/ + +#ifndef __OPENSPACE_MODULE_GLOBEBROWSING___GLOBETRANSLATION___H__ +#define __OPENSPACE_MODULE_GLOBEBROWSING___GLOBETRANSLATION___H__ + +#include + +#include +#include +#include + +namespace openspace::globebrowsing { + +class RenderableGlobe; + +class GlobeTranslation : public Translation { +public: + GlobeTranslation(const ghoul::Dictionary& dictionary); + + glm::dvec3 position(const UpdateData& data) const override; + + static documentation::Documentation Documentation(); + +private: + void fillAttachedNode(); + + properties::StringProperty _globe; + properties::DoubleProperty _longitude; + properties::DoubleProperty _latitude; + properties::DoubleProperty _fixedAltitude; + properties::BoolProperty _useFixedAltitude; + + RenderableGlobe* _attachedNode = nullptr; + + mutable bool _positionIsDirty = true; + mutable glm::dvec3 _position; +}; + +} // namespace openspace::globebrowsing + +#endif // __OPENSPACE_MODULE_GLOBEBROWSING___GLOBETRANSLATION___H__