#include #include #include #include #include #include #include #include #include #include #include #include #include #include #include // formatJson #include #include #include #include #include #include #include // Milliseconds #include #include // to adjust camera angle namespace { constexpr const char* _loggerCat = "ScreenSpaceSkyBrowser"; constexpr const openspace::properties::Property::PropertyInfo BrowserDimensionInfo = { "BrowserDimensions", "Browser Dimensions Info", "Set the dimensions of the SkyTarget according to the SkyBrowser ratio " }; constexpr const openspace::properties::Property::PropertyInfo ZoomInfo = { "Zoom", "Zoom Info", "tjobidabidobidabidopp plupp" }; constexpr const openspace::properties::Property::PropertyInfo TargetIDInfo = { "TargetID", "Target Info", "tjobidabidobidabidopp plupp" }; constexpr const openspace::properties::Property::PropertyInfo BorderColorInfo = { "BorderColor", "Border color Info", "tjobidabidobidabidopp plupp" }; struct [[codegen::Dictionary(ScreenSpaceSkyBrowser)]] Parameters { // [[codegen::verbatim(BrowserDimensionInfo.description)]] std::optional browserDimensions; // [[codegen::verbatim(ZoomInfo.description)]] std::optional zoom; // [[codegen::verbatim(TargetIDInfo.description)]] std::optional targetID; // [[codegen::verbatim(BorderColorInfo.description)]] std::optional borderColor; }; #include "screenspaceskybrowser_codegen.cpp" } // namespace namespace openspace { ScreenSpaceSkyBrowser::ScreenSpaceSkyBrowser(const ghoul::Dictionary& dictionary) : ScreenSpaceBrowser(dictionary) , _browserDimensions(BrowserDimensionInfo, _dimensions, glm::ivec2(0), glm::ivec2(300)) , _vfieldOfView(ZoomInfo, 10.f, 0.1f, 70.f) , _borderColor(BorderColorInfo, glm::ivec3(rand() % 256, rand() % 256, rand() % 256)) , _skyTargetID(TargetIDInfo) , _camIsSyncedWWT(true) , _skyTarget(nullptr) { // 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(_borderColor.value()) < 222) { _borderColor = glm::ivec3(rand() % 256, rand() % 256, rand() % 256); } // Handle target dimension property const Parameters p = codegen::bake(dictionary); _browserDimensions = p.browserDimensions.value_or(_browserDimensions); _browserDimensions.onChange([&]() { if (!_skyTarget) { setConnectedTarget(); } else { glm::vec2 dim = getBrowserPixelDimensions(); _skyTarget->setDimensions(dim); } }); addProperty(_browserDimensions); _vfieldOfView = p.zoom.value_or(_vfieldOfView); addProperty(_vfieldOfView); _skyTargetID = p.targetID.value_or(_skyTargetID); addProperty(_skyTargetID); _skyTargetID.onChange([&]() { setConnectedTarget(); }); _vfieldOfView.onChange([&]() { if (_skyTarget) { _skyTarget->updateFOV(_vfieldOfView); } }); std::string identifier; if (dictionary.hasValue(KeyIdentifier)) { identifier = dictionary.value(KeyIdentifier); } else { identifier = "ScreenSpaceSkyBrowser"; } identifier = makeUniqueIdentifier(identifier); setIdentifier(identifier); _cartesianPosition.setValue(glm::dvec3(_cartesianPosition.value().x, _cartesianPosition.value().y, skybrowser::SCREENSPACE_Z)); // Always make sure that the target and browser are visible together _enabled.onChange([&]() { if (_skyTarget) { _skyTarget->property("Enabled")->set(_enabled.value()); } }); } bool ScreenSpaceSkyBrowser::initializeGL() { global::moduleEngine->module()->addRenderable(this); setConnectedTarget(); if (_skyTarget) { _skyTarget->setDimensions(getBrowserPixelDimensions()); } WWTfollowCamera(); return ScreenSpaceBrowser::initializeGL(); } bool ScreenSpaceSkyBrowser::deinitializeGL() { // Set flag to false so the thread can exit _camIsSyncedWWT = false; if (_threadWWTMessages.joinable()) { _threadWWTMessages.join(); LINFO("Joined thread"); } return ScreenSpaceBrowser::deinitializeGL(); } bool ScreenSpaceSkyBrowser::setConnectedTarget() { setBorderColor(_borderColor.value()); _skyTarget = dynamic_cast(global::renderEngine->screenSpaceRenderable(_skyTargetID.value())); if (_skyTarget) { _skyTarget->setBorderColor(_borderColor.value()); _skyTarget->updateFOV(_vfieldOfView.value()); _skyTarget->setDimensions(getBrowserPixelDimensions()); } return _skyTarget != nullptr; } ScreenSpaceSkyTarget* ScreenSpaceSkyBrowser::getSkyTarget() { return _skyTarget; } float ScreenSpaceSkyBrowser::fieldOfView() const { return _vfieldOfView; } void ScreenSpaceSkyBrowser::setVerticalFieldOfView(float fov) { _vfieldOfView = fov; } void ScreenSpaceSkyBrowser::scrollZoom(float scroll) { // Make scroll more sensitive the smaller the FOV float x = _vfieldOfView; float zoomFactor = atan(x / 50.0) + exp(x / 40) - 0.999999; float zoom = scroll > 0.0 ? -zoomFactor : zoomFactor; _vfieldOfView = std::clamp(_vfieldOfView + zoom, 0.001f, 70.0f); } void ScreenSpaceSkyBrowser::executeJavascript(std::string script) const { //LINFOC(_loggerCat, "Executing javascript " + script); if (_browserInstance && _browserInstance->getBrowser() && _browserInstance->getBrowser()->GetMainFrame()) { CefRefPtr frame = _browserInstance->getBrowser()->GetMainFrame(); frame->ExecuteJavaScript(script, frame->GetURL(), 0); } } glm::ivec3 ScreenSpaceSkyBrowser::getColor() { return _borderColor.value(); } void ScreenSpaceSkyBrowser::setBorderColor(glm::ivec3 col) { std::string stringColor = std::to_string(col.x) + "," + std::to_string(col.y) + "," + std::to_string(col.z); std::string script = "document.body.style.backgroundColor = 'rgb(" + stringColor + ")';"; executeJavascript(script); } bool ScreenSpaceSkyBrowser::sendMessageToWWT(const ghoul::Dictionary& msg) { std::string script = "sendMessageToWWT(" + ghoul::formatJson(msg) + ");"; executeJavascript(script); return true; } ghoul::Dictionary ScreenSpaceSkyBrowser::createMessageForMovingWWTCamera(const glm::dvec2 celestCoords, const double fov, const bool moveInstantly) const { using namespace std::string_literals; ghoul::Dictionary msg; msg.setValue("event", "center_on_coordinates"s); msg.setValue("ra", celestCoords.x); msg.setValue("dec", celestCoords.y); msg.setValue("fov", fov); msg.setValue("instant", moveInstantly); return msg; } ghoul::Dictionary ScreenSpaceSkyBrowser::createMessageForLoadingWWTImgColl(const std::string& url) const { // https://docs.worldwidetelescope.org/data-guide/1/data-file-formats/collections/sample-blank-collection.wtml using namespace std::string_literals; ghoul::Dictionary msg; msg.setValue("event", "load_image_collection"s); msg.setValue("url", url); return msg; } ghoul::Dictionary ScreenSpaceSkyBrowser::createMessageForSettingForegroundWWT(const std::string& name) const { // https://docs.worldwidetelescope.org/data-guide/1/data-file-formats/collections/sample-blank-collection.wtml using namespace std::string_literals; ghoul::Dictionary msg; msg.setValue("event", "set_foreground_by_name"s); msg.setValue("name", name); return msg; } ghoul::Dictionary ScreenSpaceSkyBrowser::createMessageForSettingForegroundOpacityWWT(double val) const { // https://docs.worldwidetelescope.org/data-guide/1/data-file-formats/collections/sample-blank-collection.wtml using namespace std::string_literals; ghoul::Dictionary msg; msg.setValue("event", "set_foreground_opacity"s); msg.setValue("value", val); return msg; } ghoul::Dictionary ScreenSpaceSkyBrowser::createMessageForPausingWWTTime() const { using namespace std::string_literals; ghoul::Dictionary msg; msg.setValue("event", "pause_time"s); return msg; } void ScreenSpaceSkyBrowser::sendMouseEvent(CefStructBase event, int x, int y) const { //LINFOC(_loggerCat, "Executing javascript " + script); LINFO(std::to_string(_objectSize.x) + " " + std::to_string(_objectSize.y)); if (_browserInstance && _browserInstance->getBrowser() && _browserInstance->getBrowser()->GetHost()) { //_browserInstance->getBrowser()->GetHost()->SendMouseWheelEvent(event, x, y); //LINFOC(_loggerCat, "Sending scroll"); } } void ScreenSpaceSkyBrowser::WWTfollowCamera() { // Start a thread to enable user interaction while sending the calls to WWT _threadWWTMessages = std::thread([&] { while (_camIsSyncedWWT) { if (_skyTarget) { // Calculate the galactic coordinate of the target direction // with infinite radius glm::dvec3 camPos = global::navigationHandler->camera()->positionVec3(); constexpr double infinity = std::numeric_limits::max(); glm::dvec3 galCoord = camPos + (infinity * _skyTarget->getTargetDirection()); glm::dvec2 celestCoordsTarget = skybrowser::galacticCartesianToJ2000(galCoord); ghoul::Dictionary message = createMessageForMovingWWTCamera(celestCoordsTarget, _vfieldOfView); sendMessageToWWT(message); } // Sleep so we don't bombard WWT with too many messages std::this_thread::sleep_for(std::chrono::milliseconds(50)); } }); } /* void ScreenSpaceSkyBrowser::translate(glm::vec2 translation) { glm::vec3 position = _cartesianPosition; _cartesianPosition = glm::translate(glm::mat4(1.f), glm::vec3(translation, 0.0f)) * glm::vec4(position, 1.0f); }*/ glm::vec2 ScreenSpaceSkyBrowser::coordIsOnResizeArea(glm::vec2 coord) { glm::vec2 resizePosition = glm::vec2{ 0 }; // Make sure coord is on browser if (!coordIsInsideCornersScreenSpace(coord)) return resizePosition; float resizeButtonSize = 0.1f; bool isOnTop = coord.y > getUpperRightCornerScreenSpace().y - (getScreenSpaceDimensions().y * resizeButtonSize); bool isOnBottom = coord.y < getLowerLeftCornerScreenSpace().y + (getScreenSpaceDimensions().y * resizeButtonSize); bool isOnRight = coord.x > getUpperRightCornerScreenSpace().x - (getScreenSpaceDimensions().x * resizeButtonSize); bool isOnLeft = coord.x < getLowerLeftCornerScreenSpace().x + (getScreenSpaceDimensions().x * resizeButtonSize); resizePosition.x = isOnRight ? 1.f : isOnLeft ? -1.f : 0.f; resizePosition.y = isOnTop ? 1.f : isOnBottom ? -1.f : 0.f; return resizePosition; } // Scales the ScreenSpaceBrowser to a new ratio void ScreenSpaceSkyBrowser::scale(glm::vec2 scalingFactor) { // Scale on the y axis, this is to ensure that _scale = 1 is // equal to the height of the window scale(abs(scalingFactor.y)); // Resize the dimensions of the texture on the x axis glm::vec2 newSize = abs(scalingFactor) * _startDimensionsSize; _texture->setDimensions(glm::ivec3(newSize, 1)); _objectSize = _texture->dimensions(); _browserDimensions = newSize; } 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: eg 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 ScreenSpaceSkyBrowser::saveResizeStartSize() { _startDimensionsSize = glm::vec2(_dimensions.value().x, _dimensions.value().y); _startScale = _scale.value(); } // Updates the browser size to match the size of the texture void ScreenSpaceSkyBrowser::updateBrowserSize() { _dimensions = _texture->dimensions(); } void ScreenSpaceSkyBrowser::scale(float scalingFactor) { _scale = _startScale * scalingFactor; } glm::vec2 ScreenSpaceSkyBrowser::getBrowserPixelDimensions() { return _browserDimensions.value(); } }