diff --git a/modules/skybrowser/include/browser.h b/modules/skybrowser/include/browser.h new file mode 100644 index 0000000000..a50fda6f2f --- /dev/null +++ b/modules/skybrowser/include/browser.h @@ -0,0 +1,107 @@ +/***************************************************************************************** + * * + * OpenSpace * + * * + * Copyright (c) 2014-2021 * + * * + * 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_SKYBROWSER___BROWSER___H__ +#define __OPENSPACE_MODULE_SKYBROWSER___BROWSER___H__ + +#include +#include +#include +#include +#include + +#ifdef _MSC_VER +#pragma warning (push) +#pragma warning (disable : 4100) +#endif // _MSC_VER + +#ifdef __clang__ +#pragma clang diagnostic push +#pragma clang diagnostic ignored "-Wunused-parameter" +#endif // __clang__ + +#include + +#ifdef __clang__ +#pragma clang diagnostic pop +#endif // __clang__ + +#ifdef _MSC_VER +#pragma warning (pop) +#endif // _MSC_VER + +namespace ghoul::opengl { class Texture; } + +namespace openspace { + + class BrowserInstance; + class RenderHandler; + class WebKeyboardHandler; + +class Browser { +public: + + Browser(const ghoul::Dictionary& dictionary); + Browser(Browser const&) = default; + ~Browser(); + + bool initializeGL(); + bool deinitializeGL(); + + void render(); + void update(); + bool isReady() const; + + glm::vec2 browserPixelDimensions() const; + + +protected: + properties::Vec2Property _dimensions; + properties::StringProperty _url; + properties::TriggerProperty _reload; + std::unique_ptr _browserInstance; + std::unique_ptr _texture; + + + class RenderHandler : public WebRenderHandler { + public: + void draw() override; + void render() override; + + void setTexture(GLuint t); + }; + +private: + void bindTexture(); + + CefRefPtr _renderHandler; + CefRefPtr _keyboardHandler; + + bool _isUrlDirty = false; + bool _isDimensionsDirty = false; +}; + +} // namespace openspace + +#endif // __OPENSPACE_MODULE_SKYBROWSER___BROWSER___H__ diff --git a/modules/skybrowser/include/screenspaceskybrowser2.h b/modules/skybrowser/include/screenspaceskybrowser2.h new file mode 100644 index 0000000000..4d88b2ba2c --- /dev/null +++ b/modules/skybrowser/include/screenspaceskybrowser2.h @@ -0,0 +1,89 @@ +#ifndef __OPENSPACE_MODULE_SKYBROWSER___SCREENSPACESKYBROWSER2___H__ +#define __OPENSPACE_MODULE_SKYBROWSER___SCREENSPACESKYBROWSER2___H__ + +#include +#include +#include +#include +#include +#include +#include + +namespace openspace { + class ScreenSpaceSkyTarget; + + class ScreenSpaceSkyBrowser2 : public ScreenSpaceRenderable, public WwtCommunicator + { + public: + // Constructor and destructor + ScreenSpaceSkyBrowser2(const ghoul::Dictionary& dictionary); + virtual ~ScreenSpaceSkyBrowser2(); + + // Inherited functions + bool initializeGL() override; + bool deinitializeGL() override; + glm::mat4 scaleMatrix() override; + void render() override; + void update() override; + + // Target - browser connection + bool connectToSkyTarget(); + bool isAnimated(); + void startFovAnimation(float fov); + void incrementallyAnimateToFov(float deltaTime); + glm::dvec2 fineTuneVector(glm::dvec2 drag); + + // Getters returning references + ScreenSpaceSkyTarget* getSkyTarget(); + properties::FloatProperty& getOpacity(); + + // Setters + void setVerticalFovWithScroll(float scroll); + void setScale(glm::vec2 scalingFactor); + void setScale(float scalingFactor); + + // Communication with WorldWide Telescope + void startSyncingWithWwt(); + + + // Mouse interaction with the browser. Returns 1 or -1 at the coordinate in + // image if the mouse is on a side of the browser + // __1__ + // y| -1 |_____|1 + // |__x -1 + glm::ivec2 isOnResizeArea(glm::vec2 screenSpaceCoord); + + private: + void syncWwtView(); + void bindTexture() override; + + // Resize functions + void saveResizeStartSize(); + void updateBrowserSize(); + + // Properties + properties::StringProperty _skyTargetId; + + // Flags + bool _isSyncedWithWwt{ false }; + bool _isFovAnimated{ false }; + float _endVfov{ 0.f }; + float _fovDiff{ 0.01f }; + + // Resizing of browser + glm::vec2 _originalDimensions; + float _originalScale; + float _resizeAreaPercentage{ 0.1f }; + + // Target & images + ScreenSpaceSkyTarget* _skyTarget{ nullptr }; + std::thread _wwtMessages; + + // Time variables + // For capping the calls to change the zoom from scrolling + constexpr static const std::chrono::milliseconds _timeUpdateInterval{ 10 }; + std::chrono::system_clock::time_point _lastUpdateTime; + }; +} + +#endif // __OPENSPACE_MODULE_SKYBROWSER___SCREENSPACESKYBROWSER2___H__ diff --git a/modules/skybrowser/include/wwtcommunicator.h b/modules/skybrowser/include/wwtcommunicator.h new file mode 100644 index 0000000000..cc6672f64d --- /dev/null +++ b/modules/skybrowser/include/wwtcommunicator.h @@ -0,0 +1,87 @@ +/***************************************************************************************** + * * + * OpenSpace * + * * + * Copyright (c) 2014-2021 * + * * + * 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_SKYBROWSER___WWTCOMMUNICATOR___H__ +#define __OPENSPACE_MODULE_SKYBROWSER___WWTCOMMUNICATOR___H__ + +#include +#include +#include +#include +#include +#include + +namespace openspace { + +class ImageData; + +class WwtCommunicator : public Browser { + +public: + + WwtCommunicator(const ghoul::Dictionary& dictionary); + WwtCommunicator(WwtCommunicator const&) = default; + virtual ~WwtCommunicator(); + + // Web page communication + + void setIdInBrowser(const std::string& id); + + // WorldWide Telescope communication + void displayImage(const std::string& url, const int i); + void removeSelectedImage(const int i); + void setImageLayerOrder(int i, int order); + + // Getters + const std::deque& getSelectedImages(); + glm::ivec3 borderColor() const; + float verticalFov() const; + glm::dvec2 fieldsOfView(); + bool hasLoadedImages() const; + + + // Setters + void setHasLoadedImages(bool isLoaded); + void setVerticalFov(float vfov); + void setWebpageBorderColor(glm::ivec3 color); + + // Display + void highlight(glm::ivec3 addition); + void removeHighlight(glm::ivec3 removal); + +protected: + void sendMessageToWwt(const ghoul::Dictionary& msg); + properties::FloatProperty _verticalFov; + properties::IVec3Property _borderColor; + + std::deque _selectedImages; + bool _hasLoadedImages{ false }; + +private: + void executeJavascript(const std::string& script) const; +}; + +} // namespace openspace + +#endif // __OPENSPACE_MODULE_SKYBROWSER___WWTCOMMUNICATOR___H__ diff --git a/modules/skybrowser/src/browser.cpp b/modules/skybrowser/src/browser.cpp new file mode 100644 index 0000000000..7dc2347dbd --- /dev/null +++ b/modules/skybrowser/src/browser.cpp @@ -0,0 +1,174 @@ +/***************************************************************************************** + * * + * OpenSpace * + * * + * Copyright (c) 2014-2021 * + * * + * 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 + +namespace { + constexpr const char* _loggerCat = "Browser"; + + const openspace::properties::Property::PropertyInfo DimensionsInfo = { + "Dimensions", + "Browser Dimensions", + "Set the dimensions of the web browser window." + }; + const openspace::properties::Property::PropertyInfo UrlInfo = { + "Url", + "URL", + "The URL to load" + }; + + const openspace::properties::Property::PropertyInfo ReloadInfo = { + "Reload", + "Reload", + "Reload the web browser" + }; + +} // namespace + +namespace openspace { + + void Browser::RenderHandler::draw() {} + + void Browser::RenderHandler::render() {} + + void Browser::RenderHandler::setTexture(GLuint t) { + _texture = t; + } + + + Browser::Browser(const ghoul::Dictionary& dictionary) + : _url(UrlInfo) + , _dimensions(DimensionsInfo, glm::vec2(0.f), glm::vec2(0.f), glm::vec2(3000.f)) + , _reload(ReloadInfo) + { + if (dictionary.hasValue(UrlInfo.identifier)) { + _url = dictionary.value(UrlInfo.identifier); + } + + glm::vec2 windowDimensions = global::windowDelegate->currentSubwindowSize(); + _dimensions = windowDimensions; + + _url.onChange([this]() { _isUrlDirty = true; }); + _dimensions.onChange([this]() { _isDimensionsDirty = true; }); + _reload.onChange([this]() { _browserInstance->reloadBrowser(); }); + + // Create browser and render handler + _renderHandler = new RenderHandler(); + _keyboardHandler = new WebKeyboardHandler(); + _browserInstance = std::make_unique( + _renderHandler, + _keyboardHandler + ); + + WebBrowserModule* webBrowser = global::moduleEngine->module(); + if (webBrowser) { + webBrowser->addBrowser(_browserInstance.get()); + } + } + + Browser::~Browser() { + // Delete + _browserInstance.reset(); + _texture.reset(); + } + + bool Browser::initializeGL() { + _texture = std::make_unique( + glm::uvec3(_dimensions.value(), 1.0f) + ); + + _renderHandler->setTexture(*_texture); + + _browserInstance->initialize(); + _browserInstance->loadUrl(_url); + return isReady(); + } + + + bool Browser::deinitializeGL() { + _renderHandler->setTexture(0); + _texture = nullptr; + + std::string urlString; + _url.getStringValue(urlString); + LDEBUG(fmt::format("Deinitializing ScreenSpaceBrowser: {}", urlString)); + + _browserInstance->close(true); + + WebBrowserModule* webBrowser = global::moduleEngine->module(); + if (webBrowser) { + webBrowser->removeBrowser(_browserInstance.get()); + _browserInstance.reset(); + } + else { + LWARNING("Could not find WebBrowserModule"); + } + + return true; + } + + void Browser::render() { + if (!_renderHandler->isTextureReady()) { + return; + } + _renderHandler->updateTexture(); + + } + + void Browser::update() { + if (_isUrlDirty) { + _browserInstance->loadUrl(_url); + _isUrlDirty = false; + } + + if (_isDimensionsDirty) { + _browserInstance->reshape(_dimensions.value()); + _isDimensionsDirty = false; + } + } + + bool Browser::isReady() const { + return _texture.get(); + } + + void Browser::bindTexture() { + _texture->bind(); + } + + glm::vec2 Browser::browserPixelDimensions() const { + return _dimensions.value(); + } + + +} // namespace openspace diff --git a/modules/skybrowser/src/screenspaceskybrowser2.cpp b/modules/skybrowser/src/screenspaceskybrowser2.cpp new file mode 100644 index 0000000000..748461d6a6 --- /dev/null +++ b/modules/skybrowser/src/screenspaceskybrowser2.cpp @@ -0,0 +1,335 @@ +#include + +#include +#include +#include +#include +#include +#include // formatJson +#include +#include + +namespace { + constexpr const char* _loggerCat = "ScreenSpaceSkyBrowser2"; + + constexpr const openspace::properties::Property::PropertyInfo BrowserDimensionInfo = + { + "BrowserDimensions", + "Browser Dimensions", + "The pixel dimensions of the sky browser." + }; + constexpr const openspace::properties::Property::PropertyInfo TargetIdInfo = + { + "TargetId", + "Target Id", + "The identifier of the target. It is used to synchronize the sky browser and the" + "sky target." + }; + constexpr const openspace::properties::Property::PropertyInfo BorderColorInfo = + { + "BorderColor", + "Border Color", + "The color of the border of the sky browser." + }; + constexpr const openspace::properties::Property::PropertyInfo VerticalFovInfo = + { + "VerticalFieldOfView", + "Vertical Field Of View", + "The vertical field of view in degrees." + }; + + struct [[codegen::Dictionary(ScreenSpaceSkyBrowser2)]] Parameters { + + // [[codegen::verbatim(BrowserDimensionInfo.description)]] + std::optional browserDimensions; + + // [[codegen::verbatim(VerticalFovInfo.description)]] + std::optional verticalFov; + + // [[codegen::verbatim(TargetIdInfo.description)]] + std::optional targetId; + + // [[codegen::verbatim(BorderColorInfo.description)]] + std::optional borderColor; + }; + +#include "ScreenSpaceSkyBrowser2_codegen.cpp" +} // namespace + +namespace openspace { + + ScreenSpaceSkyBrowser2::ScreenSpaceSkyBrowser2(const ghoul::Dictionary& dictionary) + : ScreenSpaceRenderable(dictionary), + WwtCommunicator(dictionary) + , _skyTargetId(TargetIdInfo) + { + // Make the color property display a color picker in the GUI + _borderColor.setViewOption("Color", true); + + // Handle target dimension property + const Parameters p = codegen::bake(dictionary); + _verticalFov = p.verticalFov.value_or(_verticalFov); + _borderColor = p.borderColor.value_or(_borderColor); + _skyTargetId = p.targetId.value_or(_skyTargetId); + + addProperty(_dimensions); + addProperty(_verticalFov); + addProperty(_borderColor); + addProperty(_skyTargetId); + + _verticalFov.onChange([&]() { + if (_skyTarget) { + _skyTarget->setScale(_verticalFov); + } + }); + _borderColor.onChange([&]() { + setWebpageBorderColor(_borderColor.value()); + }); + _skyTargetId.onChange([&]() { + connectToSkyTarget(); + }); + + // Set a unique identifier + std::string identifier; + if (dictionary.hasValue(KeyIdentifier)) { + identifier = dictionary.value(KeyIdentifier); + } + else { + identifier = "ScreenSpaceSkyBrowser22"; + } + identifier = makeUniqueIdentifier(identifier); + setIdentifier(identifier); + + glm::vec2 screenPosition = _cartesianPosition.value(); + _cartesianPosition.setValue(glm::vec3(screenPosition, skybrowser::ScreenSpaceZ)); + + // Always make sure that the target and browser are visible together + _enabled.onChange([&]() { + if (_skyTarget) { + _skyTarget->property("Enabled")->set(_enabled.value()); + } + }); + + // Ensure the color of the border is bright enough. + // Make sure the RGB color at least is 50% brightness + // By making each channel 50% bright in general + // 222 = sqrt(3*(0.5*256)^2) + while (glm::length(glm::vec3(_borderColor.value())) < 222.f) { + _borderColor = glm::vec3(rand() % 256, rand() % 256, rand() % 256); + } + } + + ScreenSpaceSkyBrowser2::~ScreenSpaceSkyBrowser2() { + // Set flag to false so the thread can exit + _isSyncedWithWwt = false; + if (_wwtMessages.joinable()) { + _wwtMessages.join(); + LINFO("Joined thread"); + } + } + + bool ScreenSpaceSkyBrowser2::initializeGL() { + + return Browser::initializeGL() && ScreenSpaceRenderable::initializeGL(); + } + + + void ScreenSpaceSkyBrowser2::startSyncingWithWwt() { + // If the camera is already synced, the browser is already syncing + if (!_isSyncedWithWwt) { + _isSyncedWithWwt = true; + // Set border color + setWebpageBorderColor(_borderColor.value()); + // Track target + syncWwtView(); + } + } + + glm::dvec2 ScreenSpaceSkyBrowser2::fineTuneVector(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{ (2 * skybrowser::windowRatio()), 2.f }; + glm::dvec2 result = - convertToScreenSpace * resultRelativeOs; + return result; + } + /* + void ScreenSpaceBrowser::setXCallback(std::function callback) { + _originalDimensions.onChange(std::move(callback)); + } + */ + bool ScreenSpaceSkyBrowser2::deinitializeGL() { + // Set flag to false so the thread can exit + _isSyncedWithWwt = false; + if (_wwtMessages.joinable()) { + _wwtMessages.join(); + LINFO("Joined thread"); + } + return Browser::deinitializeGL() && ScreenSpaceRenderable::deinitializeGL(); + } + + bool ScreenSpaceSkyBrowser2::connectToSkyTarget() { + _skyTarget = dynamic_cast( + global::renderEngine->screenSpaceRenderable(_skyTargetId.value())); + return _skyTarget; + } + + bool ScreenSpaceSkyBrowser2::isAnimated() + { + return _isFovAnimated; + } + + void ScreenSpaceSkyBrowser2::startFovAnimation(float fov) + { + _isFovAnimated = true; + _endVfov = fov; + } + + void ScreenSpaceSkyBrowser2::incrementallyAnimateToFov(float deltaTime) + { + // If distance too large, keep animating. Else, stop animation + float diff = verticalFov() - _endVfov; + const bool shouldAnimate = abs(diff) > _fovDiff; + + if (shouldAnimate) { + setVerticalFovWithScroll(diff); + } + else { + _isFovAnimated = false; + } + } + + void ScreenSpaceSkyBrowser2::render() { + Browser::render(); + + draw( + globalRotationMatrix() * + translationMatrix() * + localRotationMatrix() * + scaleMatrix() + ); + } + + void ScreenSpaceSkyBrowser2::update() { + Browser::update(); + ScreenSpaceRenderable::update(); + } + + ScreenSpaceSkyTarget* ScreenSpaceSkyBrowser2::getSkyTarget() { + return _skyTarget; + } + + void ScreenSpaceSkyBrowser2::setVerticalFovWithScroll(float scroll) { + // Cap how often the zoom is allowed to update + std::chrono::system_clock::time_point now = std::chrono::system_clock::now(); + std::chrono::system_clock::duration timeSinceLastUpdate = now - _lastUpdateTime; + + if (timeSinceLastUpdate > _timeUpdateInterval) { + // Make scroll more sensitive the smaller the FOV + float x = _verticalFov; + float zoomFactor = atan(x / 50.0) + exp(x / 40) - 0.999999; + float zoom = scroll > 0.0 ? -zoomFactor : zoomFactor; + _verticalFov = std::clamp(_verticalFov + zoom, 0.001f, 70.0f); + _lastUpdateTime = std::chrono::system_clock::now(); + } + } + + void ScreenSpaceSkyBrowser2::syncWwtView() { + + // Start a thread to enable user interaction while sending the calls to WWT + _wwtMessages = std::thread([&] { + while (_isSyncedWithWwt) { + if (_skyTarget) { + // Message WorldWide Telescope current view + glm::dvec3 cartesian = _skyTarget->directionEquatorial(); + + ghoul::Dictionary message = wwtmessage::moveCamera( + skybrowser::cartesianToSpherical(cartesian), + _verticalFov, + skybrowser::cameraRoll() + ); + sendMessageToWwt(message); + } + + // Sleep so we don't bombard WWT with too many messages + std::this_thread::sleep_for(std::chrono::milliseconds(50)); + } + }); + + } + + glm::ivec2 ScreenSpaceSkyBrowser2::isOnResizeArea(glm::vec2 coord) { + glm::ivec2 resizePosition = glm::ivec2{ 0 }; + // Make sure coordinate is on browser + if (!coordIsInsideCornersScreenSpace(coord)) return resizePosition; + + // TO DO: turn this into a vector and use prettier vector arithmetic + float resizeAreaY = screenSpaceDimensions().y * _resizeAreaPercentage; + float resizeAreaX = screenSpaceDimensions().x * _resizeAreaPercentage; + + const bool isOnTop = coord.y > upperRightCornerScreenSpace().y - resizeAreaY; + const bool isOnBottom = coord.y < lowerLeftCornerScreenSpace().y + resizeAreaY; + const bool isOnRight = coord.x > upperRightCornerScreenSpace().x - resizeAreaX; + const bool isOnLeft = coord.x < lowerLeftCornerScreenSpace().x + resizeAreaX; + + resizePosition.x = isOnRight ? 1 : isOnLeft ? -1 : 0; + resizePosition.y = isOnTop ? 1 : isOnBottom ? -1 : 0; + + return resizePosition; + } + // Scales the ScreenSpaceBrowser to a new ratio + void ScreenSpaceSkyBrowser2::setScale(glm::vec2 scalingFactor) { + + // Scale on the y axis, this is to ensure that _scale = 1 is + // equal to the height of the window + setScale(abs(scalingFactor.y)); + // Resize the dimensions of the texture on the x axis + glm::vec2 newSize = abs(scalingFactor) * _originalDimensions; + _texture->setDimensions(glm::ivec3(newSize, 1)); + _objectSize = _texture->dimensions(); + } + + glm::mat4 ScreenSpaceSkyBrowser2::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 + + float textureRatio = static_cast(_texture->dimensions().x) / + static_cast(_texture->dimensions().y); + + glm::mat4 scale = glm::scale( + glm::mat4(1.f), + glm::vec3(textureRatio * _scale, _scale, 1.f) + ); + return scale; + } + + void ScreenSpaceSkyBrowser2::saveResizeStartSize() { + _originalDimensions = _dimensions.value(); + _originalScale = _scale.value(); + } + + // Updates the browser size to match the size of the texture + void ScreenSpaceSkyBrowser2::updateBrowserSize() { + _dimensions = _texture->dimensions(); + } + + void ScreenSpaceSkyBrowser2::setScale(float scalingFactor) { + _scale = _originalScale * scalingFactor; + } + + properties::FloatProperty& ScreenSpaceSkyBrowser2::getOpacity() { + return _opacity; + } + + void ScreenSpaceSkyBrowser2::bindTexture() { + _texture->bind(); + } +} diff --git a/modules/skybrowser/src/wwtcommunicator.cpp b/modules/skybrowser/src/wwtcommunicator.cpp new file mode 100644 index 0000000000..0018a5ce18 --- /dev/null +++ b/modules/skybrowser/src/wwtcommunicator.cpp @@ -0,0 +1,183 @@ +/***************************************************************************************** + * * + * OpenSpace * + * * + * Copyright (c) 2014-2021 * + * * + * 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 // formatJson +#include + + +namespace { + constexpr const char* _loggerCat = "WwtCommunicator"; + + constexpr const openspace::properties::Property::PropertyInfo BorderColorInfo = + { + "BorderColor", + "Border Color", + "The color of the border of the sky browser." + }; + constexpr const openspace::properties::Property::PropertyInfo VerticalFovInfo = + { + "VerticalFieldOfView", + "Vertical Field Of View", + "The vertical field of view in degrees." + }; + +} // namespace + +namespace openspace { + + WwtCommunicator::WwtCommunicator(const ghoul::Dictionary& dictionary) + : Browser(dictionary), + _verticalFov(VerticalFovInfo, 10.f, 0.01f, 70.0f), + _borderColor(BorderColorInfo, glm::ivec3(200), glm::ivec3(0), glm::ivec3(255)) + { + + } + + WwtCommunicator::~WwtCommunicator() { + + } + + void WwtCommunicator::displayImage(const std::string& url, int i) + { + // Ensure there are no duplicates + auto it = std::find(std::begin(_selectedImages), std::end(_selectedImages), i); + if (it == std::end(_selectedImages)) { + // Push newly selected image to front + _selectedImages.push_front(i); + // Index of image is used as layer ID as it is unique in the image data set + sendMessageToWwt(wwtmessage::addImage(std::to_string(i), url)); + sendMessageToWwt(wwtmessage::setImageOpacity(std::to_string(i), 1.0)); + } + } + + void WwtCommunicator::removeSelectedImage(const int i) { + // Remove from selected list + auto it = std::find(std::begin(_selectedImages), std::end(_selectedImages), i); + + if (it != std::end(_selectedImages)) { + _selectedImages.erase(it); + sendMessageToWwt(wwtmessage::removeImage(std::to_string(i))); + } + } + + void WwtCommunicator::sendMessageToWwt(const ghoul::Dictionary& msg) { + std::string script = "sendMessageToWWT(" + ghoul::formatJson(msg) + ");"; + executeJavascript(script); + } + + const std::deque& WwtCommunicator::getSelectedImages() { + return _selectedImages; + } + + void WwtCommunicator::setVerticalFov(float vfov) { + _verticalFov = vfov; + } + + void WwtCommunicator::setWebpageBorderColor(glm::ivec3 color) { + std::string stringColor = std::to_string(color.x) + "," + + std::to_string(color.y) + "," + std::to_string(color.z); + std::string script = "document.body.style.backgroundColor = 'rgb(" + + stringColor + ")';"; + executeJavascript(script); + } + + void WwtCommunicator::highlight(glm::ivec3 addition) + { + glm::ivec3 color = glm::ivec3(_borderColor.value()); + setWebpageBorderColor(color + addition); + } + + void WwtCommunicator::removeHighlight(glm::ivec3 removal) + { + glm::ivec3 color = glm::ivec3(_borderColor.value()); + setWebpageBorderColor(color - removal); + } + + glm::dvec2 WwtCommunicator::fieldsOfView() { + float browserRatio = _dimensions.value().x / _dimensions.value().y; + glm::dvec2 browserFov = glm::dvec2(verticalFov() * browserRatio, verticalFov()); + + return browserFov; + } + + bool WwtCommunicator::hasLoadedImages() const { + return _hasLoadedImages; + } + + void WwtCommunicator::setImageLayerOrder(int i, int order) { + // Find in selected images list + auto current = std::find( + std::begin(_selectedImages), + std::end(_selectedImages), + i + ); + auto target = std::begin(_selectedImages) + order; + + // Make sure the image was found in the list + if (current != std::end(_selectedImages) && target != std::end(_selectedImages)) { + // Swap the two images + std::iter_swap(current, target); + } + + int reverseOrder = _selectedImages.size() - order - 1; + ghoul::Dictionary message = wwtmessage::setLayerOrder(std::to_string(i), + reverseOrder); + sendMessageToWwt(message); + } + + void WwtCommunicator::setHasLoadedImages(bool isLoaded) { + _hasLoadedImages = isLoaded; + } + + void WwtCommunicator::setIdInBrowser(const std::string& id) { + // Send ID to it's browser + executeJavascript("setId('" + id + "')"); + } + + void WwtCommunicator::executeJavascript(const std::string& script) const { + // Make sure that the browser has a main frame + const bool browserExists = _browserInstance && _browserInstance->getBrowser(); + const bool frameIsLoaded = browserExists && + _browserInstance->getBrowser()->GetMainFrame(); + + if (frameIsLoaded) { + CefRefPtr frame = _browserInstance->getBrowser()->GetMainFrame(); + frame->ExecuteJavaScript(script, frame->GetURL(), 0); + } + } + + glm::ivec3 WwtCommunicator::borderColor() const { + return _borderColor.value(); + } + + float WwtCommunicator::verticalFov() const { + return _verticalFov.value(); + } + +} // namespace openspace