/***************************************************************************************** * * * OpenSpace * * * * Copyright (c) 2014-2023 * * * * 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 #include #include namespace { constexpr openspace::properties::Property::PropertyInfo TextureQualityInfo = { "TextureQuality", "Quality of Texture", "A parameter to set the resolution of the texture. 1 is full resolution and " "slower frame rate. Lower value means lower resolution of texture and faster " "frame rate", openspace::properties::Property::Visibility::AdvancedUser }; constexpr openspace::properties::Property::PropertyInfo DisplayCopyInfo = { "DisplayCopy", "Display Copy Position", "Display a copy of this sky browser at an additional position. This copy will " "not be interactive. The position is in RAE (Radius, Azimuth, Elevation) " "coordinates or Cartesian, depending on if the browser uses RAE or Cartesian " "coordinates", // @VISIBILITY(2.67) openspace::properties::Property::Visibility::User }; constexpr openspace::properties::Property::PropertyInfo DisplayCopyShowInfo = { "ShowDisplayCopy", "Show Display Copy", "Show the display copy", openspace::properties::Property::Visibility::AdvancedUser }; constexpr openspace::properties::Property::PropertyInfo IsHiddenInfo = { "IsHidden", "Is Hidden", "If checked, the browser will be not be displayed. If it is not checked, it will " "be", openspace::properties::Property::Visibility::AdvancedUser }; constexpr openspace::properties::Property::PropertyInfo PointSpacecraftInfo = { "PointSpacecraft", "Point Spacecraft", "If checked, spacecrafts will point towards the coordinate of an image upon " "selection.", // @VISIBILITY(?) openspace::properties::Property::Visibility::User }; struct [[codegen::Dictionary(ScreenSpaceSkyBrowser)]] Parameters { // [[codegen::verbatim(TextureQualityInfo.description)]] std::optional textureQuality; // [[codegen::verbatim(IsHiddenInfo.description)]] std::optional isHidden; // [[codegen::verbatim(PointSpacecraftInfo.description)]] std::optional pointSpacecraft; }; #include "screenspaceskybrowser_codegen.cpp" glm::ivec3 randomBorderColor() { // Generate a random border color with sufficient lightness and a n std::random_device rd; // Hue is in the unit degrees [0, 360] std::uniform_real_distribution hue(0.f, 360.f); // Value in saturation are in the unit percent [0,1] float value = 0.9f; // Brightness float saturation = 0.5f; glm::vec3 hsvColor = glm::vec3(hue(rd), saturation, value); glm::ivec3 rgbColor = glm::ivec3(glm::rgbColor(hsvColor) * 255.f); return rgbColor; } } // namespace namespace openspace { documentation::Documentation ScreenSpaceSkyBrowser::Documentation() { return codegen::doc("skybrowser_screenspaceskybrowser"); } ScreenSpaceSkyBrowser::ScreenSpaceSkyBrowser(const ghoul::Dictionary& dictionary) : ScreenSpaceRenderable(dictionary) , WwtCommunicator(dictionary) , _textureQuality(TextureQualityInfo, 1.f, 0.25f, 1.f) , _isHidden(IsHiddenInfo, true) , _isPointingSpacecraft(PointSpacecraftInfo, false) { _identifier = makeUniqueIdentifier(_identifier); // Handle target dimension property const Parameters p = codegen::bake(dictionary); _textureQuality = p.textureQuality.value_or(_textureQuality); _isHidden = p.isHidden.value_or(_isHidden); _isPointingSpacecraft = p.pointSpacecraft.value_or(_isPointingSpacecraft); addProperty(_isHidden); addProperty(_url); addProperty(_browserDimensions); addProperty(_reload); addProperty(_textureQuality); addProperty(_verticalFov); addProperty(_isPointingSpacecraft); _textureQuality.onChange([this]() { _isDimensionsDirty = true; }); if (global::windowDelegate->isMaster()) { _borderColor = randomBorderColor(); } _useRadiusAzimuthElevation.onChange( [this]() { std::for_each( _displayCopies.begin(), _displayCopies.end(), [this](std::unique_ptr& copy) { if (_useRadiusAzimuthElevation) { *copy = sphericalToRae(cartesianToSpherical(copy->value())); } else { *copy = sphericalToCartesian(raeToSpherical(copy->value())); } }); }); } ScreenSpaceSkyBrowser::~ScreenSpaceSkyBrowser() { SkyBrowserModule* module = global::moduleEngine->module(); if (module && module->pair(identifier())) { module->removeTargetBrowserPair(identifier()); } } bool ScreenSpaceSkyBrowser::initializeGL() { WwtCommunicator::initializeGL(); ScreenSpaceRenderable::initializeGL(); return true; } glm::dvec2 ScreenSpaceSkyBrowser::fineTuneVector(const glm::dvec2& drag) { // Fine tuning of target glm::dvec2 wwtFov = fieldsOfView(); glm::dvec2 openSpaceFOV = skybrowser::fovWindow(); glm::dvec2 browserDim = screenSpaceDimensions(); glm::dvec2 angleResult = wwtFov * (drag / browserDim); glm::dvec2 resultRelativeOs = angleResult / openSpaceFOV; // Convert to screen space coordinate system glm::dvec2 convertToScreenSpace = glm::dvec2((2.f * skybrowser::windowRatio()), 2.f); glm::dvec2 result = -convertToScreenSpace * resultRelativeOs; return result; } bool ScreenSpaceSkyBrowser::isInitialized() const { return _isInitialized; } bool ScreenSpaceSkyBrowser::isPointingSpacecraft() const { return _isPointingSpacecraft; } void ScreenSpaceSkyBrowser::setIdInBrowser() const { int currentNode = global::windowDelegate->currentNode(); WwtCommunicator::setIdInBrowser(fmt::format("{}_{}", identifier(), currentNode)); } void ScreenSpaceSkyBrowser::setIsInitialized(bool isInitialized) { _isInitialized = isInitialized; } void ScreenSpaceSkyBrowser::setPointSpaceCraft(bool shouldPoint) { _isPointingSpacecraft = shouldPoint; } void ScreenSpaceSkyBrowser::updateTextureResolution() { // Check if texture quality has changed. If it has, adjust accordingly if (std::abs(_textureQuality.value() - _lastTextureQuality) > glm::epsilon()) { float diffTextureQuality = _textureQuality / _lastTextureQuality; glm::vec2 newRes = glm::vec2(_browserDimensions.value()) * diffTextureQuality; _browserDimensions = glm::ivec2(newRes); _lastTextureQuality = _textureQuality.value(); } _objectSize = glm::ivec3(_browserDimensions.value(), 1); // The radius has to be updated when the texture resolution has changed _radiusIsDirty = true; _borderRadiusTimer = 0; } void ScreenSpaceSkyBrowser::addDisplayCopy(const glm::vec3& raePosition, int nCopies) { size_t start = _displayCopies.size(); for (int i = 0; i < nCopies; i++) { openspace::properties::Property::PropertyInfo info = DisplayCopyInfo; float azimuth = i * glm::two_pi() / nCopies; glm::vec3 position = raePosition + glm::vec3(0.f, azimuth, 0.f); std::string idDisplayCopy = "DisplayCopy" + std::to_string(start + i); info.identifier = idDisplayCopy.c_str(); _displayCopies.push_back( std::make_unique( info, position, glm::vec3(-4.f, -4.f, -10.f), glm::vec3(4.f, 4.f, glm::half_pi()) ) ); openspace::properties::Property::PropertyInfo showInfo = DisplayCopyShowInfo; std::string idDisplayCopyVisible = "ShowDisplayCopy" + std::to_string(start + i); showInfo.identifier = idDisplayCopyVisible.c_str(); _showDisplayCopies.push_back( std::make_unique( showInfo, true ) ); addProperty(_displayCopies.back().get()); addProperty(_showDisplayCopies.back().get()); } } void ScreenSpaceSkyBrowser::removeDisplayCopy() { if (!_displayCopies.empty()) { removeProperty(_displayCopies.back().get()); removeProperty(_showDisplayCopies.back().get()); _displayCopies.pop_back(); _showDisplayCopies.pop_back(); } } std::vector> ScreenSpaceSkyBrowser::displayCopies() const { std::vector> vec; using vec3Property = std::unique_ptr; for (const vec3Property& copy : _displayCopies) { vec.push_back({ copy->identifier(), copy->value() }); } return vec; } std::vector> ScreenSpaceSkyBrowser::showDisplayCopies() const { std::vector> vec; using boolProperty = std::unique_ptr; for (const boolProperty& copy : _showDisplayCopies) { vec.push_back({copy->identifier(), copy->value()}); } return vec; } bool ScreenSpaceSkyBrowser::deinitializeGL() { ScreenSpaceRenderable::deinitializeGL(); WwtCommunicator::deinitializeGL(); return true; } void ScreenSpaceSkyBrowser::render() { WwtCommunicator::render(); if (!_isHidden) { draw( globalRotationMatrix() * translationMatrix() * localRotationMatrix() * scaleMatrix() ); } // Render the display copies for (size_t i = 0; i < _displayCopies.size(); i++) { if (_showDisplayCopies[i]->value()) { glm::vec3 coordinates = _displayCopies[i]->value(); if (_useRadiusAzimuthElevation) { coordinates = sphericalToCartesian(raeToSpherical(coordinates)); } glm::mat4 localRotation = glm::mat4(1.f); if (_faceCamera) { localRotation = glm::inverse(glm::lookAt( glm::vec3(0.f), glm::normalize(coordinates), glm::vec3(0.f, 1.f, 0.f) )); } draw( globalRotationMatrix() * glm::translate(glm::mat4(1.f), coordinates) * localRotation * scaleMatrix() ); } } } void ScreenSpaceSkyBrowser::update() { // Check for dirty flags if (_isDimensionsDirty) { updateTextureResolution(); } if (_shouldReload) { _isInitialized = false; } // After the texture has been updated, wait a little bit before updating the border // radius so the browser has time to update its size if (_radiusIsDirty && _isInitialized && _borderRadiusTimer == RadiusTimeOut) { setBorderRadius(_borderRadius); _radiusIsDirty = false; _borderRadiusTimer = -1; } _borderRadiusTimer++; ScreenSpaceRenderable::update(); WwtCommunicator::update(); } double ScreenSpaceSkyBrowser::setVerticalFovWithScroll(float scroll) { // Make scroll more sensitive the smaller the FOV double x = _verticalFov; double zoomFactor = atan(x / 50.0) + exp(x / 40.0) - 0.99999999999999999999999999999; double zoom = scroll > 0.0 ? zoomFactor : -zoomFactor; _verticalFov = std::clamp(_verticalFov + zoom, 0.0, 70.0); return _verticalFov; } void ScreenSpaceSkyBrowser::bindTexture() { _texture->bind(); } glm::mat4 ScreenSpaceSkyBrowser::scaleMatrix() { // To ensure the plane has the right ratio // The _scale tells us how much of the windows height the browser covers: e.g. a // browser that covers 0.25 of the height of the window will have scale = 0.25 glm::mat4 scale = glm::scale( glm::mat4(1.f), glm::vec3(browserRatio() * _scale, _scale, 1.f) ); return scale; } float ScreenSpaceSkyBrowser::opacity() const noexcept { return _opacity; } } // namespace openspace