diff --git a/modules/skybrowser/include/renderableskybrowser.h b/modules/skybrowser/include/renderableskybrowser.h index a98693820a..3e53aee0de 100644 --- a/modules/skybrowser/include/renderableskybrowser.h +++ b/modules/skybrowser/include/renderableskybrowser.h @@ -50,6 +50,10 @@ namespace openspace { void update(const UpdateData& data) override; + void executeJavascript(std::string script) const; + bool sendMessageToWWT(const ghoul::Dictionary& msg); + + protected: properties::Vec2Property _dimensions; diff --git a/modules/skybrowser/include/screenspaceskybrowser.h b/modules/skybrowser/include/screenspaceskybrowser.h index 89be3644e0..1f26d67467 100644 --- a/modules/skybrowser/include/screenspaceskybrowser.h +++ b/modules/skybrowser/include/screenspaceskybrowser.h @@ -20,17 +20,7 @@ namespace openspace { // Communication with the webpage and WWT void executeJavascript(std::string script) const; - ghoul::Dictionary createMessageForMovingWWTCamera(const glm::dvec2 celestCoords, const double fov, const bool moveInstantly = true) const; - ghoul::Dictionary createMessageForPausingWWTTime() const; - ghoul::Dictionary createMessageForLoadingWWTImgColl(const std::string& url) const; - ghoul::Dictionary createMessageForSettingForegroundWWT(const std::string& name) const; - ghoul::Dictionary createMessageForSettingForegroundOpacityWWT(double val) const; - ghoul::Dictionary createMessageForAddingImageLayerWWT(ImageData& url); - ghoul::Dictionary createMessageForRemovingImageLayerWWT(const std::string& id) const; - ghoul::Dictionary createMessageForSettingOpacityLayerWWT(const ImageData& id, double opacity) const; - bool sendMessageToWWT(const ghoul::Dictionary& msg); - void sendMouseEvent(CefStructBase event, int x, int y) const; void WWTfollowCamera(); float fieldOfView() const; void setVerticalFieldOfView(float fov); @@ -38,6 +28,7 @@ namespace openspace { ScreenSpaceSkyTarget* getSkyTarget(); bool hasLoadedCollections(); void setHasLoadedCollections(bool isLoaded); + void addImage(ImageData& image); // Translation //void translate(glm::vec2 translation); diff --git a/modules/skybrowser/include/utility.h b/modules/skybrowser/include/utility.h index 92cbe93b76..3490aba153 100644 --- a/modules/skybrowser/include/utility.h +++ b/modules/skybrowser/include/utility.h @@ -1,3 +1,4 @@ +#include #include #include #include @@ -7,34 +8,51 @@ #include -namespace openspace::skybrowser { - const double SCREENSPACE_Z = -2.1; - const double RAD_TO_DEG = 180.0 / M_PI; - const double DEG_TO_RAD = M_PI / 180.0; - const glm::dvec3 NORTH_POLE = { 0.0 , 0.0 , 1.0 }; - constexpr double infinity = std::numeric_limits::max(); +namespace openspace { + namespace skybrowser { + const double SCREENSPACE_Z = -2.1; + const double RAD_TO_DEG = 180.0 / M_PI; + const double DEG_TO_RAD = M_PI / 180.0; + const glm::dvec3 NORTH_POLE = { 0.0 , 0.0 , 1.0 }; + constexpr double infinity = std::numeric_limits::max(); - // Conversion matrix from this paper: https://arxiv.org/abs/1010.3773v1 - const glm::dmat3 conversionMatrix = glm::dmat3({ - -0.054875539390, 0.494109453633, -0.867666135681, // col 0 - -0.873437104725, -0.444829594298, -0.198076389622, // col 1 - -0.483834991775, 0.746982248696, 0.455983794523 // col 2 - }); + // Conversion matrix from this paper: https://arxiv.org/abs/1010.3773v1 + const glm::dmat3 conversionMatrix = glm::dmat3({ + -0.054875539390, 0.494109453633, -0.867666135681, // col 0 + -0.873437104725, -0.444829594298, -0.198076389622, // col 1 + -0.483834991775, 0.746982248696, 0.455983794523 // col 2 + }); - // J2000 to galactic conversion and vice versa - glm::dvec2 cartesianToSpherical(glm::dvec3 cartesianCoords); - glm::dvec3 sphericalToCartesian(glm::dvec2 sphericalCoords); - glm::dvec3 galacticCartesianToJ2000Cartesian(glm::dvec3 rGal); - glm::dvec2 galacticCartesianToJ2000Spherical(glm::dvec3 rGal); - glm::dvec3 galacticCartesianToCameraLocalCartesian(glm::dvec3 galCoords); - glm::dvec3 J2000SphericalToGalacticCartesian(glm::dvec2 coords, double distance = infinity); - glm::dvec3 J2000CartesianToGalacticCartesian(glm::dvec3 coords, double distance = infinity); - // Convert J2000, spherical or Cartesian, to screen space - glm::dvec3 J2000SphericalToScreenSpace(glm::dvec2 coords); - glm::dvec3 J2000CartesianToScreenSpace(glm::dvec3 coords); - - glm::dvec3 galacticToScreenSpace(glm::dvec3 galacticCoord); + // J2000 to galactic conversion and vice versa + glm::dvec2 cartesianToSpherical(glm::dvec3 cartesianCoords); + glm::dvec3 sphericalToCartesian(glm::dvec2 sphericalCoords); + glm::dvec3 galacticCartesianToJ2000Cartesian(glm::dvec3 rGal); + glm::dvec2 galacticCartesianToJ2000Spherical(glm::dvec3 rGal); + glm::dvec3 galacticCartesianToCameraLocalCartesian(glm::dvec3 galCoords); + glm::dvec3 J2000SphericalToGalacticCartesian(glm::dvec2 coords, + double distance = infinity); + glm::dvec3 J2000CartesianToGalacticCartesian(glm::dvec3 coords, + double distance = infinity); + // Convert J2000, spherical or Cartesian, to screen space + glm::dvec3 J2000SphericalToScreenSpace(glm::dvec2 coords); + glm::dvec3 J2000CartesianToScreenSpace(glm::dvec3 coords); + glm::dvec3 galacticToScreenSpace(glm::dvec3 galacticCoord); + } + namespace wwtmessage { + // WWT messages + ghoul::Dictionary moveCamera(const glm::dvec2 celestCoords, + const double fov, const bool moveInstantly = true); + ghoul::Dictionary loadCollection(const std::string& url); + ghoul::Dictionary setForeground(const std::string& name); + ghoul::Dictionary createImageLayer(ImageData& image, int id = 0); + ghoul::Dictionary removeImageLayer(const std::string& id); + ghoul::Dictionary setLayerOpacity(const ImageData& image, + double opacity); + ghoul::Dictionary setForegroundOpacity(double val); + } } + + diff --git a/modules/skybrowser/include/wwtdatahandler.h b/modules/skybrowser/include/wwtdatahandler.h index 196e12f3b0..90cc02e2cc 100644 --- a/modules/skybrowser/include/wwtdatahandler.h +++ b/modules/skybrowser/include/wwtdatahandler.h @@ -62,8 +62,9 @@ namespace openspace { std::string creditsUrl; glm::dvec2 celestCoords; std::string collection; - float zoomLevel; - bool hasCoords; + float fov; + bool hasCelestCoords{ false }; + bool has3dCoords{ false }; glm::dvec3 position3d; int id{ NO_ID }; }; diff --git a/modules/skybrowser/skybrowsermodule.cpp b/modules/skybrowser/skybrowsermodule.cpp index 3d5e463443..980d6c64c7 100644 --- a/modules/skybrowser/skybrowsermodule.cpp +++ b/modules/skybrowser/skybrowsermodule.cpp @@ -34,6 +34,7 @@ #include #include #include +#include #include #include #include @@ -144,6 +145,14 @@ namespace openspace { "Add one or multiple exoplanet systems to the scene, as specified by the " "input. An input string should be the name of the system host star" }, + { + "create3dSkyBrowser", + &skybrowser::luascriptfunctions::create3dSkyBrowser, + {}, + "string or list of strings", + "Add one or multiple exoplanet systems to the scene, as specified by the " + "input. An input string should be the name of the system host star" + }, }; return res; @@ -391,6 +400,10 @@ void SkyBrowserModule::addRenderable(ScreenSpaceRenderable* object) { } } +void SkyBrowserModule::add3dBrowser(SceneGraphNode* node) { + browsers3d.push_back(node); +} + ScreenSpaceSkyBrowser* SkyBrowserModule::to_browser(ScreenSpaceRenderable* ptr) { return dynamic_cast(ptr); } @@ -468,9 +481,10 @@ int SkyBrowserModule::loadImages(const std::string& root, const std::string& dir dataHandler->loadSpeckData(speckOpenClusters); int nLoadedImages; + // Read from disc - bool loadedImages = dataHandler->loadWTMLCollectionsFromDirectory(directory); + // Reading from url if there is no directory if (loadedImages) { LINFO("Loading images from directory"); diff --git a/modules/skybrowser/skybrowsermodule.h b/modules/skybrowser/skybrowsermodule.h index 9ef6d61ba4..164c273f8e 100644 --- a/modules/skybrowser/skybrowsermodule.h +++ b/modules/skybrowser/skybrowsermodule.h @@ -44,6 +44,7 @@ class ScreenSpaceSkyTarget; class RenderableSkyBrowser; class ScreenSpaceRenderable; class WWTDataHandler; +class SceneGraphNode; class SkyBrowserModule : public OpenSpaceModule { @@ -64,6 +65,7 @@ public: void setSelectedBrowser(int i); int getSelectedBrowserIndex(); int loadImages(const std::string& root, const std::string& directory); + void add3dBrowser(SceneGraphNode* node); scripting::LuaLibrary luaLibrary() const override; //std::vector documentations() const override; @@ -81,6 +83,7 @@ protected: // Renderable vector and ptr to where mouse is std::vector renderables; std::vector browsers; + std::vector browsers3d; ScreenSpaceRenderable* _mouseOnObject; // Dragging glm::vec2 startDragMousePos; diff --git a/modules/skybrowser/skybrowsermodule_lua.inl b/modules/skybrowser/skybrowsermodule_lua.inl index 6bb76ebae2..e6bee6bc33 100644 --- a/modules/skybrowser/skybrowsermodule_lua.inl +++ b/modules/skybrowser/skybrowsermodule_lua.inl @@ -2,28 +2,29 @@ #include #include #include +#include +#include +#include +#include +#include #include #include #include #include +#include +#include +#include #include #include #include #include #include #include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include #include #include +#include +#include +#include #include namespace { @@ -39,36 +40,39 @@ namespace openspace::skybrowser::luascriptfunctions { const int i = ghoul::lua::value(L, 1); SkyBrowserModule* module = global::moduleEngine->module(); ScreenSpaceSkyBrowser* selectedBrowser = module->getSkyBrowsers()[module->getSelectedBrowserIndex()]; - ScreenSpaceSkyTarget* selectedTarget = selectedBrowser->getSkyTarget(); - - ImageData& resultImage = module->getWWTDataHandler()->getLoadedImages()[i]; + if (selectedBrowser) { + ScreenSpaceSkyTarget* selectedTarget = selectedBrowser->getSkyTarget(); - // Load image, if the image has not been loaded yet - if (resultImage.id == ImageData::NO_ID) { - LINFO("Loading image " + resultImage.name); - selectedBrowser->sendMessageToWWT(selectedBrowser->createMessageForAddingImageLayerWWT(resultImage)); - selectedBrowser->sendMessageToWWT(selectedBrowser->createMessageForSettingOpacityLayerWWT(resultImage, 1.0)); - } - - // If the image has coordinates, move the target - if (resultImage.hasCoords) { - // Animate the target to the image coord position - // In WWT, the definition of ZoomLevel is: VFOV = ZoomLevel / 6 - if (selectedTarget) { - selectedTarget->unlock(); - selectedTarget->startAnimation(resultImage.celestCoords, resultImage.zoomLevel / 6); - glm::dvec3 imgCoordsOnScreen = J2000SphericalToScreenSpace(resultImage.celestCoords); - glm::vec2 windowRatio = global::windowDelegate->currentWindowSize(); - float r = windowRatio.x / windowRatio.y; - // Check if image coordinate is within current FOV - bool coordIsWithinView = (abs(imgCoordsOnScreen.x) < r && abs(imgCoordsOnScreen.y) < 1.f && imgCoordsOnScreen.z < 0); - bool coordIsBehindCamera = imgCoordsOnScreen.z > 0; - // If the coordinate is not in view, rotate camera - if (!coordIsWithinView || coordIsBehindCamera) { - module->startRotation(resultImage.celestCoords); + ImageData& resultImage = module->getWWTDataHandler()->getLoadedImages()[i]; + + // Load image, if the image has not been loaded yet + if (resultImage.id == ImageData::NO_ID) { + LINFO("Loading image " + resultImage.name); + selectedBrowser->addImage(resultImage); + } + + // If the image has coordinates, move the target + if (resultImage.hasCelestCoords) { + // Animate the target to the image coord position + if (selectedTarget) { + selectedTarget->unlock(); + selectedTarget->startAnimation(resultImage.celestCoords, resultImage.fov); + glm::dvec3 imgCoordsOnScreen = J2000SphericalToScreenSpace(resultImage.celestCoords); + glm::vec2 windowRatio = global::windowDelegate->currentWindowSize(); + float r = windowRatio.x / windowRatio.y; + // Check if image coordinate is within current FOV + bool coordIsWithinView = (abs(imgCoordsOnScreen.x) < r && abs(imgCoordsOnScreen.y) < 1.f && imgCoordsOnScreen.z < 0); + bool coordIsBehindCamera = imgCoordsOnScreen.z > 0; + // If the coordinate is not in view, rotate camera + if (!coordIsWithinView || coordIsBehindCamera) { + module->startRotation(resultImage.celestCoords); + } } } - } + } + else { + LINFO("No browser selected!"); + } return 0; } @@ -81,7 +85,7 @@ namespace openspace::skybrowser::luascriptfunctions { const ImageData& resultImage = module->getWWTDataHandler()->getLoadedImages()[i]; // Only move and show circle if the image has coordinates - if (resultImage.hasCoords) { + if (resultImage.hasCelestCoords) { // Make circle visible ScreenSpaceImageLocal* hoverCircle = dynamic_cast(global::renderEngine->screenSpaceRenderable("HoverCircle")); hoverCircle->property("Enabled")->set(true); @@ -139,7 +143,7 @@ namespace openspace::skybrowser::luascriptfunctions { std::string root = "https://raw.githubusercontent.com/WorldWideTelescope/wwt-web-client/master/assets/webclient-explore-root.wtml"; for (ScreenSpaceSkyBrowser* browser : module->getSkyBrowsers()) { if (!browser->hasLoadedCollections()) { - browser->sendMessageToWWT(browser->createMessageForLoadingWWTImgColl(root)); + browser->sendMessageToWWT(wwtmessage::loadCollection(root)); browser->setHasLoadedCollections(true); } } @@ -167,36 +171,42 @@ namespace openspace::skybrowser::luascriptfunctions { for (int i = 0; i < images.size(); i++) { std::string name = images[i].name != "" ? images[i].name : "undefined"; - std::string url = images[i].thumbnailUrl != "" ? images[i].thumbnailUrl : "undefined"; + std::string thumbnail = images[i].thumbnailUrl != "" ? images[i].thumbnailUrl : "undefined"; glm::dvec3 cartCoords = skybrowser::sphericalToCartesian(images[i].celestCoords); std::vector cartCoordsVec = { cartCoords.x, cartCoords.y, cartCoords.z }; + glm::dvec3 position = images[i].position3d; + std::vector position3d = { position.x, position.y, position.z }; // Index for current ImageData ghoul::lua::push(L, i + 1); lua_newtable(L); // Push ("Key", value) - ghoul::lua::push(L, "Name", name); + ghoul::lua::push(L, "name", name); lua_settable(L, -3); - ghoul::lua::push(L, "Thumbnail", url); + ghoul::lua::push(L, "thumbnail", thumbnail); lua_settable(L, -3); - ghoul::lua::push(L, "RA", images[i].celestCoords.x); + ghoul::lua::push(L, "ra", images[i].celestCoords.x); lua_settable(L, -3); - ghoul::lua::push(L, "Dec", images[i].celestCoords.y); + ghoul::lua::push(L, "dec", images[i].celestCoords.y); lua_settable(L, -3); - ghoul::lua::push(L, "CartesianDirection", cartCoordsVec); + ghoul::lua::push(L, "cartesianDirection", cartCoordsVec); lua_settable(L, -3); - ghoul::lua::push(L, "HasCoords", images[i].hasCoords); + ghoul::lua::push(L, "hasCelestialCoords", images[i].hasCelestCoords); lua_settable(L, -3); - ghoul::lua::push(L, "Credits", images[i].credits); + ghoul::lua::push(L, "credits", images[i].credits); lua_settable(L, -3); - ghoul::lua::push(L, "CreditsUrl", images[i].creditsUrl); + ghoul::lua::push(L, "creditsUrl", images[i].creditsUrl); lua_settable(L, -3); - ghoul::lua::push(L, "Index", i); + ghoul::lua::push(L, "identifier", std::to_string(i)); + lua_settable(L, -3); + ghoul::lua::push(L, "has3dCoords", images[i].has3dCoords); + lua_settable(L, -3); + ghoul::lua::push(L, "position3d", position3d); lua_settable(L, -3); // Set table for the current ImageData lua_settable(L, -3); } - + return 1; } @@ -229,15 +239,15 @@ namespace openspace::skybrowser::luascriptfunctions { float VFOV = HFOV * (windowRatio.y / windowRatio.x); double FOV = std::min(HFOV, VFOV); // Push window data - ghoul::lua::push(L, "WindowHFOV", FOV); + ghoul::lua::push(L, "windowHFOV", FOV); lua_settable(L, -3); - ghoul::lua::push(L, "CartesianDirection", viewDirCelestVec); + ghoul::lua::push(L, "cartesianDirection", viewDirCelestVec); lua_settable(L, -3); - ghoul::lua::push(L, "RA", sphericalJ2000.x); + ghoul::lua::push(L, "ra", sphericalJ2000.x); lua_settable(L, -3); - ghoul::lua::push(L, "Dec", sphericalJ2000.y); + ghoul::lua::push(L, "dec", sphericalJ2000.y); lua_settable(L, -3); - ghoul::lua::push(L, "SelectedBrowserIndex", module->getSelectedBrowserIndex()); + ghoul::lua::push(L, "selectedBrowserIndex", module->getSelectedBrowserIndex()); lua_settable(L, -3); // Set table for the current ImageData lua_settable(L, -3); @@ -262,13 +272,13 @@ namespace openspace::skybrowser::luascriptfunctions { // Push ("Key", value) ghoul::lua::push(L, "FOV", browser->fieldOfView()); lua_settable(L, -3); - ghoul::lua::push(L, "CartesianDirection", celestialCartVec); + ghoul::lua::push(L, "cartesianDirection", celestialCartVec); lua_settable(L, -3); - ghoul::lua::push(L, "RA", celestialSpherical.x); + ghoul::lua::push(L, "ra", celestialSpherical.x); lua_settable(L, -3); - ghoul::lua::push(L, "Dec", celestialSpherical.y); + ghoul::lua::push(L, "dec", celestialSpherical.y); lua_settable(L, -3); - ghoul::lua::push(L, "Color", colorVec); + ghoul::lua::push(L, "color", colorVec); lua_settable(L, -3); // Set table for the current target @@ -299,6 +309,81 @@ namespace openspace::skybrowser::luascriptfunctions { } return 0; } + + int create3dSkyBrowser(lua_State* L) { + ghoul::lua::checkArgumentsAndThrow(L, 1, "lua::create3dSkyBrowser"); + // Image index to place in 3D + const int i = ghoul::lua::value(L, 1); + SkyBrowserModule* module = global::moduleEngine->module(); + ImageData image = module->getWWTDataHandler()->getLoadedImages()[i]; + + // If the image has a 3D position, add it to the scene graph + if (image.has3dCoords) { + std::string id = "SkyBrowser" + std::to_string(i); + glm::dvec3 position = image.position3d * distanceconstants::Parsec; + std::string translation = ghoul::to_string(position); + std::string guiPath = "/SkyBrowser"; + + // Calculate the size of the plane with trigonometry + // Calculate in equatorial coordinate system since the FOV is from Earth + // /| + // /_| Adjacent is the horizontal line, opposite the vertical + // \ | Calculate for half the triangle first, then multiply with 2 + // \| + glm::dvec3 j2000 = skybrowser::galacticCartesianToJ2000Cartesian(position); + double adjacent = glm::length(j2000); + double opposite = 2 * adjacent * glm::tan(image.fov * 0.5); + + // Calculate rotation to make the plane face the solar system barycenter + glm::dvec3 normal = glm::normalize(-position); + glm::dvec3 newRight = glm::normalize( + glm::cross(glm::dvec3(0.0, 0.0, 1.0), normal) + ); + glm::dvec3 newUp = glm::cross(normal, newRight); + + glm::dmat3 originOrientedRotation = glm::dmat3(1.0); + originOrientedRotation[0] = newRight; + originOrientedRotation[1] = newUp; + originOrientedRotation[2] = normal; + + + const std::string browser = "{" + "Identifier = '" + id + "'," + "Parent = 'SolarSystemBarycenter'," + "Renderable = {" + "Type = 'RenderableSkyBrowser'," + "Size = " + std::to_string(opposite) +"," + "Origin = 'Center'," + "Billboard = false," + "Url = 'http://localhost:8000'" + "}," + "Transform = {" + "Translation = {" + "Type = 'StaticTranslation'," + "Position = " + translation + "" + "}," + "Rotation = {" + "Type = 'StaticRotation'," + "Rotation = " + ghoul::to_string(originOrientedRotation) + "" + "}" + "}," + "GUI = {" + "Name = '" + image.name + "'," + "Path = '" + guiPath + "'" + "}" + "}"; + LINFO(browser); + openspace::global::scriptEngine->queueScript( + "openspace.addSceneGraphNode(" + browser + ");", + scripting::ScriptEngine::RemoteScripting::Yes + ); + } + else { + LINFO("Image has no 3D coordinate!"); + } + + return 0; + } } diff --git a/modules/skybrowser/src/renderableskybrowser.cpp b/modules/skybrowser/src/renderableskybrowser.cpp index 302111d3b6..2dd630d3b9 100644 --- a/modules/skybrowser/src/renderableskybrowser.cpp +++ b/modules/skybrowser/src/renderableskybrowser.cpp @@ -6,12 +6,14 @@ #include #include #include +#include // formatJson +#include #include #include -#include +#include #include #include -#include + namespace { @@ -112,6 +114,7 @@ namespace openspace { _browserInstance->initialize(); _browserInstance->loadUrl(_url); + _dimensions = _texture->dimensions(); } void RenderableSkyBrowser::deinitializeGL() { @@ -153,7 +156,6 @@ namespace openspace { } } - void RenderableSkyBrowser::bindTexture() { if (_texture) { _texture->bind(); @@ -162,4 +164,18 @@ namespace openspace { glBindTexture(GL_TEXTURE_2D, 0); } } + + void RenderableSkyBrowser::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); + } + } + + bool RenderableSkyBrowser::sendMessageToWWT(const ghoul::Dictionary& msg) { + std::string script = "sendMessageToWWT(" + ghoul::formatJson(msg) + ");"; + executeJavascript(script); + return true; + } } // namespace diff --git a/modules/skybrowser/src/screenspaceskybrowser.cpp b/modules/skybrowser/src/screenspaceskybrowser.cpp index 63bf9c9809..2ede8d52e1 100644 --- a/modules/skybrowser/src/screenspaceskybrowser.cpp +++ b/modules/skybrowser/src/screenspaceskybrowser.cpp @@ -230,113 +230,14 @@ namespace openspace { 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; - - glm::dvec3 camUpJ2000 = skybrowser::galacticCartesianToJ2000Cartesian(global::navigationHandler->camera()->lookUpVectorWorldSpace()); - glm::dvec3 camForwardJ2000 = skybrowser::galacticCartesianToJ2000Cartesian(global::navigationHandler->camera()->viewDirectionWorldSpace()); - double angle = glm::degrees(atan2(glm::dot(glm::cross(camUpJ2000, skybrowser::NORTH_POLE), camForwardJ2000), glm::dot(skybrowser::NORTH_POLE, camUpJ2000))); - - msg.setValue("event", "center_on_coordinates"s); - msg.setValue("ra", celestCoords.x); - msg.setValue("dec", celestCoords.y); - msg.setValue("fov", fov); - msg.setValue("roll", angle); - msg.setValue("instant", moveInstantly); - - return msg; - } - - ghoul::Dictionary ScreenSpaceSkyBrowser::createMessageForLoadingWWTImgColl(const std::string& url) const { - using namespace std::string_literals; - ghoul::Dictionary msg; - msg.setValue("event", "load_image_collection"s); - msg.setValue("url", url); - msg.setValue("loadChildFolders", true); - - return msg; - } - - ghoul::Dictionary ScreenSpaceSkyBrowser::createMessageForSettingForegroundWWT(const std::string& name) const { - 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::createMessageForAddingImageLayerWWT(ImageData& image) { - std::string idString = std::to_string(_imageId); - using namespace std::string_literals; - ghoul::Dictionary msg; - image.id = _imageId; - msg.setValue("event", "image_layer_create"s); - msg.setValue("id", idString); - msg.setValue("url", image.imageUrl); - _imageId++; - return msg; - } - - ghoul::Dictionary ScreenSpaceSkyBrowser::createMessageForRemovingImageLayerWWT(const std::string& id) const { - using namespace std::string_literals; - ghoul::Dictionary msg; - msg.setValue("event", "image_layer_remove"s); - msg.setValue("id", id); - - return msg; - } - - ghoul::Dictionary ScreenSpaceSkyBrowser::createMessageForSettingOpacityLayerWWT(const ImageData& image, double opacity) const { - std::string idString = std::to_string(image.id); - using namespace std::string_literals; - ghoul::Dictionary msg; - msg.setValue("event", "image_layer_set"s); - msg.setValue("id", idString); - msg.setValue("setting", "opacity"s); - msg.setValue("value", opacity); - - return msg; - } - - ghoul::Dictionary ScreenSpaceSkyBrowser::createMessageForSettingForegroundOpacityWWT(double val) const { - 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) { - ghoul::Dictionary message = createMessageForMovingWWTCamera(_skyTarget->getTargetDirectionCelestial(), _vfieldOfView); + glm::dvec2 aim = _skyTarget->getTargetDirectionCelestial(); + ghoul::Dictionary message = wwtmessage::moveCamera(aim, _vfieldOfView); sendMessageToWWT(message); } @@ -414,4 +315,10 @@ namespace openspace { glm::vec2 ScreenSpaceSkyBrowser::getBrowserPixelDimensions() { return _browserDimensions.value(); } + + void ScreenSpaceSkyBrowser::addImage(ImageData& image) { + sendMessageToWWT(wwtmessage::createImageLayer(image, _imageId)); + sendMessageToWWT(wwtmessage::setLayerOpacity(image, 1.0)); + _imageId++; + } } diff --git a/modules/skybrowser/src/utility.cpp b/modules/skybrowser/src/utility.cpp index d6a358fc8a..2cd298d0ee 100644 --- a/modules/skybrowser/src/utility.cpp +++ b/modules/skybrowser/src/utility.cpp @@ -12,86 +12,177 @@ namespace openspace::skybrowser { - glm::dvec3 sphericalToCartesian(glm::dvec2 sphericalCoords) { + glm::dvec3 sphericalToCartesian(glm::dvec2 sphericalCoords) { - glm::dvec3 cartesian = glm::dvec3( - cos(sphericalCoords.x * DEG_TO_RAD) * cos(sphericalCoords.y * DEG_TO_RAD), - sin(sphericalCoords.x * DEG_TO_RAD) * cos(sphericalCoords.y * DEG_TO_RAD), - sin(sphericalCoords.y * DEG_TO_RAD) - ); + glm::dvec3 cartesian = glm::dvec3( + cos(sphericalCoords.x * DEG_TO_RAD) * cos(sphericalCoords.y * DEG_TO_RAD), + sin(sphericalCoords.x * DEG_TO_RAD) * cos(sphericalCoords.y * DEG_TO_RAD), + sin(sphericalCoords.y * DEG_TO_RAD) + ); - return cartesian; - } + return cartesian; + } - glm::dvec2 cartesianToSpherical(glm::dvec3 cartesianCoords) { + glm::dvec2 cartesianToSpherical(glm::dvec3 cartesianCoords) { - double ra = atan2(cartesianCoords[1], cartesianCoords[0]); - double dec = atan2(cartesianCoords[2], glm::sqrt((cartesianCoords[0] * cartesianCoords[0]) + (cartesianCoords[1] * cartesianCoords[1]))); + double ra = atan2(cartesianCoords[1], cartesianCoords[0]); + double dec = atan2(cartesianCoords[2], glm::sqrt((cartesianCoords[0] * cartesianCoords[0]) + (cartesianCoords[1] * cartesianCoords[1]))); - ra = ra > 0 ? ra : ra + (2 * M_PI); + ra = ra > 0 ? ra : ra + (2 * M_PI); - return glm::dvec2(RAD_TO_DEG * ra, RAD_TO_DEG * dec); - } + return glm::dvec2(RAD_TO_DEG * ra, RAD_TO_DEG * dec); + } - glm::dvec3 galacticCartesianToJ2000Cartesian(glm::dvec3 rGal) { - return glm::transpose(conversionMatrix) * rGal; - } + glm::dvec3 galacticCartesianToJ2000Cartesian(glm::dvec3 rGal) { + return glm::transpose(conversionMatrix) * rGal; + } - glm::dvec2 galacticCartesianToJ2000Spherical(glm::dvec3 rGal) { - return cartesianToSpherical(galacticCartesianToJ2000Cartesian(rGal)); - } + glm::dvec2 galacticCartesianToJ2000Spherical(glm::dvec3 rGal) { + return cartesianToSpherical(galacticCartesianToJ2000Cartesian(rGal)); + } - glm::dvec3 J2000SphericalToGalacticCartesian(glm::dvec2 coords, double distance) { - glm::dvec3 rGalactic = conversionMatrix * sphericalToCartesian(coords); // on the unit sphere - return distance * rGalactic; - } + glm::dvec3 J2000SphericalToGalacticCartesian(glm::dvec2 coords, double distance) { + glm::dvec3 rGalactic = conversionMatrix * sphericalToCartesian(coords); // on the unit sphere + return distance * rGalactic; + } - glm::dvec3 J2000CartesianToGalacticCartesian(glm::dvec3 coords, double distance) { - glm::dvec3 rGalactic = conversionMatrix * glm::normalize(coords); // on the unit sphere - return distance * rGalactic; - } + glm::dvec3 J2000CartesianToGalacticCartesian(glm::dvec3 coords, double distance) { + glm::dvec3 rGalactic = conversionMatrix * glm::normalize(coords); // on the unit sphere + return distance * rGalactic; + } - glm::dvec3 galacticToScreenSpace(glm::dvec3 imageCoordsGalacticCartesian) { + glm::dvec3 galacticToScreenSpace(glm::dvec3 imageCoordsGalacticCartesian) { - glm::dvec3 viewDirectionLocal = galacticCartesianToCameraLocalCartesian(imageCoordsGalacticCartesian); - // Ensure that if the coord is behind the camera, the converted coord will be there too - double zCoord = viewDirectionLocal.z > 0 ? -SCREENSPACE_Z : SCREENSPACE_Z; + glm::dvec3 viewDirectionLocal = galacticCartesianToCameraLocalCartesian(imageCoordsGalacticCartesian); + // Ensure that if the coord is behind the camera, the converted coord will be there too + double zCoord = viewDirectionLocal.z > 0 ? -SCREENSPACE_Z : SCREENSPACE_Z; - // Calculate screen space coords x and y - long double tan_x = viewDirectionLocal.x / viewDirectionLocal.z; - long double tan_y = viewDirectionLocal.y / viewDirectionLocal.z; + // Calculate screen space coords x and y + long double tan_x = viewDirectionLocal.x / viewDirectionLocal.z; + long double tan_y = viewDirectionLocal.y / viewDirectionLocal.z; - glm::dvec2 angleCoordsLocal = glm::dvec2(std::atanl(tan_x), std::atanl(tan_y)); - glm::dvec3 imageCoordsScreenSpace = glm::dvec3(zCoord * static_cast(std::tanl(angleCoordsLocal.x)), zCoord * static_cast(std::tanl(angleCoordsLocal.y)), zCoord); + glm::dvec2 angleCoordsLocal = glm::dvec2(std::atanl(tan_x), std::atanl(tan_y)); + glm::dvec3 imageCoordsScreenSpace = glm::dvec3(zCoord * static_cast(std::tanl(angleCoordsLocal.x)), zCoord * static_cast(std::tanl(angleCoordsLocal.y)), zCoord); - return imageCoordsScreenSpace; - } - - glm::dvec3 galacticCartesianToCameraLocalCartesian(glm::dvec3 galCoords) { - // Transform vector to camera's local coordinate system - glm::dvec3 camPos = global::navigationHandler->camera()->positionVec3(); - glm::dvec3 camToCoordsDir = glm::normalize(galCoords - camPos); - glm::dmat4 camMat = global::navigationHandler->camera()->viewRotationMatrix(); - glm::dvec3 viewDirectionLocal = camMat * glm::dvec4(camToCoordsDir, 1.0); - viewDirectionLocal = glm::normalize(viewDirectionLocal); - return viewDirectionLocal; - } - - glm::dvec3 J2000CartesianToScreenSpace(glm::dvec3 coords) { - // Transform equatorial J2000 to galactic coord with infinite radius - glm::dvec3 imageCoordsGalacticCartesian = J2000CartesianToGalacticCartesian(coords, infinity); - // Transform galactic coord to screen space - return galacticToScreenSpace(imageCoordsGalacticCartesian); - } - - glm::dvec3 J2000SphericalToScreenSpace(glm::dvec2 coords) { - // Transform equatorial J2000 to galactic coord with infinite radius - glm::dvec3 imageCoordsGalacticCartesian = J2000SphericalToGalacticCartesian(coords, infinity); - // Transform galactic coord to screen space - return galacticToScreenSpace(imageCoordsGalacticCartesian); - } - + return imageCoordsScreenSpace; } + glm::dvec3 galacticCartesianToCameraLocalCartesian(glm::dvec3 galCoords) { + // Transform vector to camera's local coordinate system + glm::dvec3 camPos = global::navigationHandler->camera()->positionVec3(); + glm::dvec3 camToCoordsDir = glm::normalize(galCoords - camPos); + glm::dmat4 camMat = global::navigationHandler->camera()->viewRotationMatrix(); + glm::dvec3 viewDirectionLocal = camMat * glm::dvec4(camToCoordsDir, 1.0); + viewDirectionLocal = glm::normalize(viewDirectionLocal); + return viewDirectionLocal; + } + + glm::dvec3 J2000CartesianToScreenSpace(glm::dvec3 coords) { + // Transform equatorial J2000 to galactic coord with infinite radius + glm::dvec3 imageCoordsGalacticCartesian = J2000CartesianToGalacticCartesian(coords, infinity); + // Transform galactic coord to screen space + return galacticToScreenSpace(imageCoordsGalacticCartesian); + } + + glm::dvec3 J2000SphericalToScreenSpace(glm::dvec2 coords) { + // Transform equatorial J2000 to galactic coord with infinite radius + glm::dvec3 imageCoordsGalacticCartesian = J2000SphericalToGalacticCartesian(coords, infinity); + // Transform galactic coord to screen space + return galacticToScreenSpace(imageCoordsGalacticCartesian); + } +} + +// WWT messages +namespace openspace::wwtmessage { + // WWT messages + ghoul::Dictionary moveCamera(const glm::dvec2 celestCoords, const double fov, const bool moveInstantly) { + using namespace std::string_literals; + ghoul::Dictionary msg; + + // Calculate roll between up vector of camera and J2000 equatorial north + glm::dvec3 upVector = global::navigationHandler->camera()->lookUpVectorWorldSpace(); + glm::dvec3 viewVector = global::navigationHandler->camera()->viewDirectionWorldSpace(); + glm::dvec3 camUpJ2000 = skybrowser::galacticCartesianToJ2000Cartesian(upVector); + glm::dvec3 camForwardJ2000 = skybrowser::galacticCartesianToJ2000Cartesian(viewVector); + + glm::dvec3 crossUpNorth = glm::cross(camUpJ2000, skybrowser::NORTH_POLE); + double dotNorthUp = glm::dot(skybrowser::NORTH_POLE, camUpJ2000); + double dotCrossUpNorthForward = glm::dot(crossUpNorth, camForwardJ2000); + double roll = glm::degrees(atan2(dotCrossUpNorthForward, dotNorthUp)); + + // Create message + msg.setValue("event", "center_on_coordinates"s); + msg.setValue("ra", celestCoords.x); + msg.setValue("dec", celestCoords.y); + msg.setValue("fov", fov); + msg.setValue("roll", roll); + msg.setValue("instant", moveInstantly); + + return msg; + } + + ghoul::Dictionary loadCollection(const std::string& url) { + using namespace std::string_literals; + ghoul::Dictionary msg; + msg.setValue("event", "load_image_collection"s); + msg.setValue("url", url); + msg.setValue("loadChildFolders", true); + + return msg; + } + + ghoul::Dictionary setForeground(const std::string& name) { + using namespace std::string_literals; + ghoul::Dictionary msg; + msg.setValue("event", "set_foreground_by_name"s); + msg.setValue("name", name); + + return msg; + } + + ghoul::Dictionary createImageLayer(ImageData& image, int id) { + std::string idString = std::to_string(id); + image.id = id; + + using namespace std::string_literals; + ghoul::Dictionary msg; + msg.setValue("event", "image_layer_create"s); + msg.setValue("id", idString); + msg.setValue("url", image.imageUrl); + return msg; + } + + ghoul::Dictionary removeImageLayer(const std::string& id) { + using namespace std::string_literals; + ghoul::Dictionary msg; + msg.setValue("event", "image_layer_remove"s); + msg.setValue("id", id); + + return msg; + } + + ghoul::Dictionary setLayerOpacity(const ImageData& image, double opacity) { + std::string idString = std::to_string(image.id); + using namespace std::string_literals; + ghoul::Dictionary msg; + msg.setValue("event", "image_layer_set"s); + msg.setValue("id", idString); + msg.setValue("setting", "opacity"s); + msg.setValue("value", opacity); + + return msg; + } + + ghoul::Dictionary setForegroundOpacity(double val) { + using namespace std::string_literals; + ghoul::Dictionary msg; + msg.setValue("event", "set_foreground_opacity"s); + msg.setValue("value", val); + + return msg; + } +} + + diff --git a/modules/skybrowser/src/wwtdatahandler.cpp b/modules/skybrowser/src/wwtdatahandler.cpp index 333f4e8b37..57abb11be4 100644 --- a/modules/skybrowser/src/wwtdatahandler.cpp +++ b/modules/skybrowser/src/wwtdatahandler.cpp @@ -272,15 +272,16 @@ namespace openspace { ImageData& img) { // Get attributes for the image img.name = node->FindAttribute("Name") ? node->FindAttribute("Name")->Value() : ""; - img.hasCoords = node->FindAttribute("RA") && node->FindAttribute("Dec"); - if (img.hasCoords) { + img.hasCelestCoords = node->FindAttribute("RA") && node->FindAttribute("Dec"); + if (img.hasCelestCoords) { // The RA from WWT is in the unit hours: to convert to degrees, multiply with 360 (deg) /24 (h) = 15 img.celestCoords.x = 15.0f * std::stof(node->FindAttribute("RA")->Value()); img.celestCoords.y = std::stof(node->FindAttribute("Dec")->Value()); } img.collection = collectionName; img.thumbnailUrl = thumbnail; - img.zoomLevel = node->FindAttribute("ZoomLevel") ? std::stof(node->FindAttribute("ZoomLevel")->Value()) : 0.f; + // In WWT, the definition of ZoomLevel is: VFOV = ZoomLevel / 6 + img.fov = node->FindAttribute("ZoomLevel") ? std::stof(node->FindAttribute("ZoomLevel")->Value()) / 6: 0.f; img.credits = credits; img.creditsUrl = creditsUrl; img.imageUrl = imageUrl; @@ -290,6 +291,7 @@ namespace openspace { auto it = _3dPositions.find(str); if (it != _3dPositions.end()) { img.position3d = it->second; + img.has3dCoords = true; nImagesWith3dPositions++; } }