mirror of
https://github.com/OpenSpace/OpenSpace.git
synced 2026-02-22 04:49:12 -06:00
Merge pull request #1894 from OpenSpace/thesis/2021/skybrowser
Thesis/2021/skybrowser
This commit is contained in:
2
.gitignore
vendored
2
.gitignore
vendored
@@ -40,3 +40,5 @@ customization.lua
|
||||
# The COMMIT info is generated everytime CMake is run
|
||||
COMMIT.md
|
||||
*_codegen.cpp
|
||||
# SkyBrowser Module downloaded data
|
||||
/modules/skybrowser/wwtimagedata
|
||||
|
||||
BIN
data/assets/circle.png
Normal file
BIN
data/assets/circle.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 18 KiB |
29
data/assets/hoverCircle.asset
Normal file
29
data/assets/hoverCircle.asset
Normal file
@@ -0,0 +1,29 @@
|
||||
-- This asset requires OpenSpace to be built with the OPENSPACE_MODULE_SPOUT enabled
|
||||
|
||||
local transforms = asset.require("scene/solarsystem/sun/transforms")
|
||||
|
||||
local circle = {
|
||||
Identifier = "hoverCircle",
|
||||
Parent = transforms.SolarSystemBarycenter.Identifier,
|
||||
Renderable = {
|
||||
Type = "RenderablePlaneImageLocal",
|
||||
Size = 3.0E15,
|
||||
Origin = "Center",
|
||||
Billboard = true,
|
||||
Texture = "${ASSETS}/circle.png"
|
||||
},
|
||||
GUI = {
|
||||
Path = "/SkyBrowser"
|
||||
}
|
||||
}
|
||||
|
||||
asset.onInitialize(function()
|
||||
openspace.addSceneGraphNode(circle)
|
||||
openspace.skybrowser.setHoverCircle('hoverCircle')
|
||||
end)
|
||||
|
||||
asset.onDeinitialize(function()
|
||||
openspace.removeSceneGraphNode(circle)
|
||||
end)
|
||||
|
||||
asset.export(circle)
|
||||
@@ -3,7 +3,7 @@ asset.require("./static_server")
|
||||
local guiCustomization = asset.require("customization/gui")
|
||||
|
||||
-- Select which commit hashes to use for the frontend and backend
|
||||
local frontendHash = "8c3d18a4a2cb1df526e1c3f2ea228013ce89099e"
|
||||
local frontendHash = "ea33990de2e1477ecafa89861cf6f65e4c2fd46d"
|
||||
local dataProvider = "data.openspaceproject.com/files/webgui"
|
||||
|
||||
local frontend = asset.syncedResource({
|
||||
@@ -66,7 +66,7 @@ asset.onDeinitialize(function ()
|
||||
end
|
||||
end
|
||||
-- @TODO(maci, 2019-08-23) setting this value on exit was causing the server to restart
|
||||
-- on macos, which in turn, stopped the application from exiting.
|
||||
-- on macos, which in turn, stopped the application from exiting.
|
||||
-- need to address in webguimodule.cpp
|
||||
--openspace.setPropertyValueSingle("Modules.WebGui.Directories", newDirectories)
|
||||
end)
|
||||
|
||||
@@ -70,24 +70,46 @@ public:
|
||||
virtual void update();
|
||||
virtual bool isReady() const;
|
||||
bool isEnabled() const;
|
||||
bool isUsingRaeCoords() const;
|
||||
bool isFacingCamera() const;
|
||||
void setEnabled(bool isEnabled);
|
||||
float depth();
|
||||
|
||||
// Screen space functionality in these coords: [-1,1][-ratio,ratio]
|
||||
glm::vec2 screenSpacePosition();
|
||||
glm::vec2 screenSpaceDimensions();
|
||||
glm::vec2 upperRightCornerScreenSpace();
|
||||
glm::vec2 lowerLeftCornerScreenSpace();
|
||||
bool isIntersecting(glm::vec2 coord);
|
||||
void translate(glm::vec2 translation, glm::vec2 position);
|
||||
void setCartesianPosition(const glm::vec3& position);
|
||||
void setRaeFromCartesianPosition(const glm::vec3& position);
|
||||
glm::vec3 raePosition() const;
|
||||
|
||||
static documentation::Documentation Documentation();
|
||||
|
||||
protected:
|
||||
void createShaders();
|
||||
std::string makeUniqueIdentifier(std::string name);
|
||||
|
||||
glm::mat4 scaleMatrix();
|
||||
virtual glm::mat4 scaleMatrix();
|
||||
glm::mat4 globalRotationMatrix();
|
||||
glm::mat4 translationMatrix();
|
||||
glm::mat4 localRotationMatrix();
|
||||
|
||||
glm::vec3 raeToCartesian(const glm::vec3& rae) const;
|
||||
glm::vec3 cartesianToRae(const glm::vec3& cartesian) const;
|
||||
|
||||
void draw(glm::mat4 modelTransform);
|
||||
|
||||
virtual void bindTexture() = 0;
|
||||
virtual void unbindTexture();
|
||||
|
||||
glm::vec3 sphericalToRae(glm::vec3 spherical) const;
|
||||
glm::vec3 raeToSpherical(glm::vec3 rae) const;
|
||||
glm::vec3 cartesianToSpherical(const glm::vec3& cartesian) const;
|
||||
glm::vec3 sphericalToCartesian(glm::vec3 spherical) const;
|
||||
glm::vec3 sanitizeSphericalCoordinates(glm::vec3 spherical) const;
|
||||
float opacity() const;
|
||||
|
||||
properties::BoolProperty _enabled;
|
||||
|
||||
@@ -67,23 +67,20 @@ public:
|
||||
protected:
|
||||
virtual void bindTexture();
|
||||
virtual void unbindTexture();
|
||||
|
||||
protected:
|
||||
properties::OptionProperty _blendMode;
|
||||
|
||||
private:
|
||||
void createPlane();
|
||||
|
||||
properties::BoolProperty _billboard;
|
||||
|
||||
properties::OptionProperty _blendMode;
|
||||
properties::BoolProperty _mirrorBackside;
|
||||
properties::BoolProperty _billboard;
|
||||
properties::FloatProperty _size;
|
||||
properties::Vec3Property _multiplyColor;
|
||||
|
||||
|
||||
ghoul::opengl::ProgramObject* _shader = nullptr;
|
||||
|
||||
|
||||
GLuint _quad = 0;
|
||||
GLuint _vertexPositionBuffer = 0;
|
||||
|
||||
private:
|
||||
bool _planeIsDirty = false;
|
||||
};
|
||||
|
||||
|
||||
59
modules/skybrowser/CMakeLists.txt
Normal file
59
modules/skybrowser/CMakeLists.txt
Normal file
@@ -0,0 +1,59 @@
|
||||
##########################################################################################
|
||||
# #
|
||||
# OpenSpace #
|
||||
# #
|
||||
# Copyright (c) 2014-2022 #
|
||||
# #
|
||||
# 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(${OPENSPACE_CMAKE_EXT_DIR}/module_definition.cmake)
|
||||
|
||||
set(HEADER_FILES
|
||||
skybrowsermodule.h
|
||||
include/renderableskytarget.h
|
||||
include/wwtdatahandler.h
|
||||
include/utility.h
|
||||
include/targetbrowserpair.h
|
||||
include/wwtcommunicator.h
|
||||
include/browser.h
|
||||
include/screenspaceskybrowser.h
|
||||
ext/tinyxml2/tinyxml2.h
|
||||
)
|
||||
source_group("Header Files" FILES ${HEADER_FILES})
|
||||
|
||||
set(SOURCE_FILES
|
||||
skybrowsermodule.cpp
|
||||
skybrowsermodule_lua.inl
|
||||
src/renderableskytarget.cpp
|
||||
src/wwtdatahandler.cpp
|
||||
src/utility.cpp
|
||||
src/targetbrowserpair.cpp
|
||||
src/wwtcommunicator.cpp
|
||||
src/browser.cpp
|
||||
src/screenspaceskybrowser.cpp
|
||||
ext/tinyxml2/tinyxml2.cpp
|
||||
)
|
||||
source_group("Source Files" FILES ${SOURCE_FILES})
|
||||
|
||||
create_new_module(
|
||||
"SkyBrowser"
|
||||
skybrowser_module
|
||||
STATIC
|
||||
${HEADER_FILES} ${SOURCE_FILES}
|
||||
)
|
||||
2986
modules/skybrowser/ext/tinyxml2/tinyxml2.cpp
Normal file
2986
modules/skybrowser/ext/tinyxml2/tinyxml2.cpp
Normal file
File diff suppressed because it is too large
Load Diff
2380
modules/skybrowser/ext/tinyxml2/tinyxml2.h
Normal file
2380
modules/skybrowser/ext/tinyxml2/tinyxml2.h
Normal file
File diff suppressed because it is too large
Load Diff
4
modules/skybrowser/include.cmake
Normal file
4
modules/skybrowser/include.cmake
Normal file
@@ -0,0 +1,4 @@
|
||||
set(OPENSPACE_DEPENDENCIES
|
||||
webbrowser
|
||||
webgui
|
||||
)
|
||||
109
modules/skybrowser/include/browser.h
Normal file
109
modules/skybrowser/include/browser.h
Normal file
@@ -0,0 +1,109 @@
|
||||
/*****************************************************************************************
|
||||
* *
|
||||
* OpenSpace *
|
||||
* *
|
||||
* Copyright (c) 2014-2022 *
|
||||
* *
|
||||
* 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 <modules/webbrowser/include/webrenderhandler.h>
|
||||
#include <openspace/documentation/documentation.h>
|
||||
#include <openspace/properties/stringproperty.h>
|
||||
#include <openspace/properties/vector/vec2property.h>
|
||||
#include <openspace/properties/triggerproperty.h>
|
||||
|
||||
#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 <include/cef_client.h>
|
||||
|
||||
#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:
|
||||
explicit Browser(const ghoul::Dictionary& dictionary);
|
||||
virtual ~Browser();
|
||||
|
||||
bool initializeGL();
|
||||
bool deinitializeGL();
|
||||
|
||||
void render();
|
||||
void update();
|
||||
bool isReady() const;
|
||||
|
||||
void updateBrowserSize();
|
||||
|
||||
glm::vec2 browserPixelDimensions() const;
|
||||
float browserRatio() const;
|
||||
void setCallbackDimensions(const std::function<void(const glm::dvec2&)>& function);
|
||||
|
||||
protected:
|
||||
properties::Vec2Property _browserPixeldimensions;
|
||||
properties::StringProperty _url;
|
||||
properties::TriggerProperty _reload;
|
||||
|
||||
std::unique_ptr<ghoul::opengl::Texture> _texture;
|
||||
|
||||
void executeJavascript(const std::string& script) const;
|
||||
|
||||
private:
|
||||
class RenderHandler : public WebRenderHandler {
|
||||
public:
|
||||
void draw() override;
|
||||
void render() override;
|
||||
|
||||
void setTexture(GLuint t);
|
||||
};
|
||||
|
||||
std::unique_ptr<BrowserInstance> _browserInstance;
|
||||
CefRefPtr<RenderHandler> _renderHandler;
|
||||
CefRefPtr<WebKeyboardHandler> _keyboardHandler;
|
||||
|
||||
bool _isUrlDirty = false;
|
||||
bool _isDimensionsDirty = false;
|
||||
bool _shouldReload = false;
|
||||
};
|
||||
|
||||
} // namespace openspace
|
||||
|
||||
#endif // __OPENSPACE_MODULE_SKYBROWSER___BROWSER___H__
|
||||
79
modules/skybrowser/include/renderableskytarget.h
Normal file
79
modules/skybrowser/include/renderableskytarget.h
Normal file
@@ -0,0 +1,79 @@
|
||||
/*****************************************************************************************
|
||||
* *
|
||||
* OpenSpace *
|
||||
* *
|
||||
* Copyright (c) 2014-2022 *
|
||||
* *
|
||||
* 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___RENDERABLESKYTARGET___H__
|
||||
#define __OPENSPACE_MODULE_SKYBROWSER___RENDERABLESKYTARGET___H__
|
||||
|
||||
#include <modules/base/rendering/renderableplane.h>
|
||||
|
||||
#include <openspace/documentation/documentation.h>
|
||||
#include <openspace/properties/scalar/floatproperty.h>
|
||||
#include <openspace/properties/scalar/doubleproperty.h>
|
||||
|
||||
namespace openspace::documentation { struct Documentation; }
|
||||
|
||||
namespace openspace {
|
||||
|
||||
class ScreenSpaceSkyBrowser;
|
||||
|
||||
class RenderableSkyTarget : public RenderablePlane {
|
||||
public:
|
||||
explicit RenderableSkyTarget(const ghoul::Dictionary& dictionary);
|
||||
|
||||
void initializeGL() override;
|
||||
void deinitializeGL() override;
|
||||
void render(const RenderData& data, RendererTasks& rendererTask) override;
|
||||
void update(const UpdateData& data) override;
|
||||
void bindTexture() override; // Empty function but has to be defined
|
||||
|
||||
glm::ivec3 borderColor() const;
|
||||
float opacity() const;
|
||||
double animationSpeed() const;
|
||||
double stopAnimationThreshold() const;
|
||||
|
||||
void setDimensions(glm::vec2 dimensions);
|
||||
void setColor(glm::ivec3 color);
|
||||
void setOpacity(float opacity);
|
||||
void setVerticalFov(double fov);
|
||||
|
||||
// Display
|
||||
void highlight(const glm::ivec3& addition);
|
||||
void removeHighlight(const glm::ivec3& removal);
|
||||
|
||||
private:
|
||||
// Properties
|
||||
properties::FloatProperty _crossHairSize;
|
||||
properties::FloatProperty _showRectangleThreshold;
|
||||
properties::FloatProperty _lineWidth;
|
||||
properties::DoubleProperty _stopAnimationThreshold;
|
||||
properties::DoubleProperty _animationSpeed;
|
||||
|
||||
double _verticalFov = 10.0;
|
||||
|
||||
glm::ivec3 _borderColor = glm::ivec3(230);
|
||||
glm::vec2 _dimensions = glm::vec2(1.f);
|
||||
};
|
||||
} // namespace openspace
|
||||
|
||||
#endif // __OPENSPACE_MODULE_SKYBROWSER___RENDERABLESKYTARGET___H__
|
||||
85
modules/skybrowser/include/screenspaceskybrowser.h
Normal file
85
modules/skybrowser/include/screenspaceskybrowser.h
Normal file
@@ -0,0 +1,85 @@
|
||||
/*****************************************************************************************
|
||||
* *
|
||||
* OpenSpace *
|
||||
* *
|
||||
* Copyright (c) 2014-2022 *
|
||||
* *
|
||||
* 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___SCREENSPACESKYBROWSER___H__
|
||||
#define __OPENSPACE_MODULE_SKYBROWSER___SCREENSPACESKYBROWSER___H__
|
||||
|
||||
#include <openspace/rendering/screenspacerenderable.h>
|
||||
#include <modules/skybrowser/include/wwtcommunicator.h>
|
||||
|
||||
#include <openspace/documentation/documentation.h>
|
||||
#include <openspace/properties/scalar/doubleproperty.h>
|
||||
#include <openspace/properties/scalar/floatproperty.h>
|
||||
#include <openspace/properties/vector/vec2property.h>
|
||||
|
||||
namespace openspace {
|
||||
|
||||
class ScreenSpaceSkyBrowser : public ScreenSpaceRenderable, public WwtCommunicator
|
||||
{
|
||||
public:
|
||||
explicit ScreenSpaceSkyBrowser(const ghoul::Dictionary& dictionary);
|
||||
~ScreenSpaceSkyBrowser();
|
||||
|
||||
bool initializeGL() override;
|
||||
bool deinitializeGL() override;
|
||||
glm::mat4 scaleMatrix() override;
|
||||
void render() override;
|
||||
void update() override;
|
||||
|
||||
float opacity() const;
|
||||
glm::vec2 size() const;
|
||||
|
||||
void setVerticalFovWithScroll(float scroll);
|
||||
void setOpacity(float opacity);
|
||||
void setScreenSpaceSize(const glm::vec2& newSize);
|
||||
void updateScreenSpaceSize();
|
||||
|
||||
glm::dvec2 fineTuneVector(glm::dvec2 drag);
|
||||
void setIdInBrowser();
|
||||
|
||||
void updateTextureResolution();
|
||||
|
||||
// Copies rendered
|
||||
void addRenderCopy(const glm::vec3& raePosition, int nCopies);
|
||||
void removeRenderCopy();
|
||||
std::vector<std::pair<std::string, glm::dvec3>> renderCopies();
|
||||
void moveRenderCopy(int i, glm::vec3 raePosition);
|
||||
|
||||
private:
|
||||
properties::FloatProperty _textureQuality;
|
||||
properties::BoolProperty _renderOnlyOnMaster;
|
||||
std::vector<std::unique_ptr<properties::Vec3Property>> _renderCopies;
|
||||
|
||||
void bindTexture() override;
|
||||
|
||||
// Flags
|
||||
bool _isSyncedWithWwt = false;
|
||||
bool _textureDimensionsIsDirty = false;
|
||||
bool _sizeIsDirty = false;
|
||||
|
||||
glm::vec2 _size = glm::vec2(1.f, 1.f);
|
||||
};
|
||||
} // namespace openspace
|
||||
|
||||
#endif // __OPENSPACE_MODULE_SKYBROWSER___SCREENSPACESKYBROWSER___H__
|
||||
138
modules/skybrowser/include/targetbrowserpair.h
Normal file
138
modules/skybrowser/include/targetbrowserpair.h
Normal file
@@ -0,0 +1,138 @@
|
||||
/*****************************************************************************************
|
||||
* *
|
||||
* OpenSpace *
|
||||
* *
|
||||
* Copyright (c) 2014-2022 *
|
||||
* *
|
||||
* 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___TARGETBROWSERPAIR___H__
|
||||
#define __OPENSPACE_MODULE_SKYBROWSER___TARGETBROWSERPAIR___H__
|
||||
|
||||
#include <modules/skybrowser/include/utility.h>
|
||||
#include <openspace/documentation/documentation.h>
|
||||
#include <deque>
|
||||
#include <chrono>
|
||||
|
||||
namespace openspace {
|
||||
|
||||
class ScreenSpaceSkyBrowser;
|
||||
class RenderableSkyTarget;
|
||||
class ScreenSpaceRenderable;
|
||||
class ImageData;
|
||||
class SceneGraphNode;
|
||||
|
||||
class TargetBrowserPair {
|
||||
public:
|
||||
TargetBrowserPair(SceneGraphNode* target, ScreenSpaceSkyBrowser* browser);
|
||||
TargetBrowserPair& operator=(TargetBrowserPair other);
|
||||
|
||||
// Target & Browser
|
||||
void initialize();
|
||||
// Highlighting
|
||||
void removeHighlight(const glm::ivec3& color);
|
||||
void highlight(const glm::ivec3& color);
|
||||
// Animation
|
||||
void startAnimation(glm::dvec3 coordsEnd, double fovEnd);
|
||||
void incrementallyAnimateToCoordinate();
|
||||
void startFading(float goal, float fadeTime);
|
||||
void incrementallyFade();
|
||||
// Mouse interaction
|
||||
void startFinetuningTarget();
|
||||
void fineTuneTarget(const glm::vec2& startMouse, const glm::vec2& translation);
|
||||
void synchronizeAim();
|
||||
|
||||
// Browser
|
||||
void sendIdToBrowser();
|
||||
void updateBrowserSize();
|
||||
std::vector<std::pair<std::string, glm::dvec3>> renderCopies();
|
||||
|
||||
// Target
|
||||
void centerTargetOnScreen();
|
||||
double targetRoll();
|
||||
|
||||
bool hasFinishedFading() const;
|
||||
bool isFacingCamera() const;
|
||||
bool isUsingRadiusAzimuthElevation() const;
|
||||
bool isEnabled() const;
|
||||
|
||||
void setEnabled(bool enable);
|
||||
void setOpacity(float opacity);
|
||||
void setIsSyncedWithWwt(bool isSynced);
|
||||
void setVerticalFov(double vfov);
|
||||
void setEquatorialAim(const glm::dvec2& aim);
|
||||
void setBorderColor(const glm::ivec3& color);
|
||||
void setScreenSpaceSize(const glm::vec2& dimensions);
|
||||
void setVerticalFovWithScroll(float scroll);
|
||||
|
||||
double verticalFov() const;
|
||||
glm::ivec3 borderColor() const;
|
||||
glm::dvec2 targetDirectionEquatorial() const;
|
||||
glm::dvec3 targetDirectionGalactic() const;
|
||||
std::string browserGuiName() const;
|
||||
std::string browserId() const;
|
||||
std::string targetRenderableId() const;
|
||||
std::string targetNodeId() const;
|
||||
std::string selectedId();
|
||||
glm::vec2 size() const;
|
||||
|
||||
SceneGraphNode* targetNode() const;
|
||||
ScreenSpaceSkyBrowser* browser() const;
|
||||
const std::deque<int>& selectedImages() const;
|
||||
|
||||
// WorldWide Telescope image handling
|
||||
void setImageOrder(int i, int order);
|
||||
void selectImage(const ImageData& image, int i);
|
||||
void removeSelectedImage(int i);
|
||||
void loadImageCollection(const std::string& collection);
|
||||
void setImageOpacity(int i, float opacity);
|
||||
void hideChromeInterface(bool shouldHide);
|
||||
|
||||
friend bool operator==(const TargetBrowserPair& lhs,
|
||||
const TargetBrowserPair& rhs);
|
||||
friend bool operator!=(const TargetBrowserPair& lhs,
|
||||
const TargetBrowserPair& rhs);
|
||||
|
||||
private:
|
||||
void aimTargetGalactic(glm::dvec3 direction);
|
||||
|
||||
// Target and browser
|
||||
RenderableSkyTarget* _targetRenderable = nullptr;
|
||||
ScreenSpaceSkyBrowser* _browser = nullptr;
|
||||
SceneGraphNode* _targetNode = nullptr;
|
||||
|
||||
// Animation
|
||||
skybrowser::Animation<float> _fadeBrowser = skybrowser::Animation(0.f, 0.f, 0.0);
|
||||
skybrowser::Animation<float> _fadeTarget = skybrowser::Animation(0.f, 0.f, 0.0);
|
||||
skybrowser::Animation<double> _fovAnimation = skybrowser::Animation(0.0, 0.0, 0.0);
|
||||
skybrowser::Animation<glm::dvec3> _moveTarget =
|
||||
skybrowser::Animation(glm::dvec3(0.0), glm::dvec3(0.0), 0.0);
|
||||
bool _targetIsAnimating = false;
|
||||
|
||||
// Dragging
|
||||
glm::dvec3 _startTargetPosition = glm::dvec3(0.0);
|
||||
|
||||
glm::dvec2 _equatorialAim = glm::dvec2(0.0);
|
||||
glm::ivec3 _borderColor = glm::ivec3(255);
|
||||
glm::vec2 _dimensions = glm::vec2(0.5f);
|
||||
};
|
||||
|
||||
} // namespace openspace
|
||||
|
||||
#endif // __OPENSPACE_MODULE_SKYBROWSER___TARGETBROWSERPAIR___H__
|
||||
242
modules/skybrowser/include/utility.h
Normal file
242
modules/skybrowser/include/utility.h
Normal file
@@ -0,0 +1,242 @@
|
||||
/*****************************************************************************************
|
||||
* *
|
||||
* OpenSpace *
|
||||
* *
|
||||
* Copyright (c) 2014-2022 *
|
||||
* *
|
||||
* 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___UTILITY___H__
|
||||
#define __OPENSPACE_MODULE_SKYBROWSER___UTILITY___H__
|
||||
|
||||
#include <openspace/documentation/documentation.h>
|
||||
#include <openspace/util/distanceconstants.h>
|
||||
#include <chrono>
|
||||
|
||||
namespace openspace::skybrowser {
|
||||
// Constants
|
||||
constexpr const double ScreenSpaceZ = -2.1;
|
||||
constexpr const glm::dvec3 NorthPole = { 0.0, 0.0, 1.0 };
|
||||
constexpr const double CelestialSphereRadius = 4 * distanceconstants::Parsec;
|
||||
|
||||
// Conversion matrix - J2000 equatorial <-> galactic
|
||||
// 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
|
||||
});
|
||||
|
||||
// Galactic coordinates are projected onto the celestial sphere
|
||||
// Equatorial coordinates are unit length
|
||||
// Conversion spherical <-> Cartesian
|
||||
/**
|
||||
* Converts from Cartesian coordinates to spherical coordinates with unit length.
|
||||
* \param coords Cartesian coordinates
|
||||
* \return Spherical coordinates with unit length in degrees
|
||||
*/
|
||||
glm::dvec2 cartesianToSpherical(const glm::dvec3& coords);
|
||||
/**
|
||||
* Converts from spherical coordinates to Cartesian coordinates with unit length.
|
||||
* \param coords Spherical coordinates in degrees
|
||||
* \return Cartesian coordinates with unit length
|
||||
*/
|
||||
glm::dvec3 sphericalToCartesian(const glm::dvec2& coords);
|
||||
|
||||
// Conversion J2000 equatorial <-> galactic
|
||||
/**
|
||||
* Converts from Cartesian galactic coordinates to Cartesian equatorial coordinates in epoch
|
||||
* J2000 with unit length.
|
||||
* \param coords Cartesian galactic coordinates
|
||||
* \return Cartesian equatorial coordinates in the epoch of J2000 with unit length
|
||||
*/
|
||||
glm::dvec3 galacticToEquatorial(const glm::dvec3& coords);
|
||||
/**
|
||||
* Converts from Cartesian equatorial coordinates to Cartesian galactic
|
||||
* coordinates. The galactic coordinates vector has the length of the radius of the
|
||||
* Celestial sphere.
|
||||
* \param coords Cartesian equatorial coordinates
|
||||
* \return Cartesian galactic coordinates placed on the Celestial sphere
|
||||
*/
|
||||
glm::dvec3 equatorialToGalactic(const glm::dvec3& coords);
|
||||
|
||||
// Conversion to screenspace from local camera / pixels
|
||||
/**
|
||||
* Converts from local camera coordinates to screenspace coordinates. The screenspace
|
||||
* coordinates are placed on the screenspace plane which has the z-coordinate as -2.1.
|
||||
* \param coords Cartesian local camera coordinates
|
||||
* \return Cartesian galactic coordinates placed on the Celestial sphere
|
||||
*/
|
||||
glm::dvec3 localCameraToScreenSpace3d(const glm::dvec3& coords);
|
||||
/**
|
||||
* Converts from pixel coordinates to screenspace coordinates in 2D.
|
||||
* \param mouseCoordinate Pixel coordinate
|
||||
* \return Cartesian ScreenSpace coordinate
|
||||
*/
|
||||
glm::vec2 pixelToScreenSpace2d(const glm::vec2& mouseCoordinate);
|
||||
|
||||
// Conversion local camera space <-> galactic / equatorial
|
||||
/**
|
||||
* Converts from Cartesian equatorial coordinates in epoch J2000 to local camera space.
|
||||
* \param coords Cartesian equatorial coordinates in epoch J2000
|
||||
* \return Local camera coordinates with unit length
|
||||
*/
|
||||
glm::dvec3 equatorialToLocalCamera(const glm::dvec3& coords);
|
||||
/**
|
||||
* Converts from Cartesian galactic coordinates to local camera space with unit length.
|
||||
* \param coords Cartesian galactic coordinates
|
||||
* \return Cartesian local camera coordinates with unit length
|
||||
*/
|
||||
glm::dvec3 galacticToLocalCamera(const glm::dvec3& coords);
|
||||
/**
|
||||
* Converts from Cartesian local camera coordinates to galactic coordinates.
|
||||
* \param coords Cartesian local camera coordinates
|
||||
* \return Cartesian galactic coordinates placed on the Celestial sphere
|
||||
*/
|
||||
glm::dvec3 localCameraToGalactic(const glm::dvec3& coords);
|
||||
/**
|
||||
* Converts from local camera coordinates to Cartesian equatorial coordinates in the epoch
|
||||
* J2000.
|
||||
* \param coords Cartesian local camera coordinates
|
||||
* \return Cartesian equatorial coordinates with unit length
|
||||
*/
|
||||
glm::dvec3 localCameraToEquatorial(const glm::dvec3& coords);
|
||||
|
||||
// Camera roll and direction
|
||||
/**
|
||||
* Returns the angle between the up direction of the OpenSpace camera and the equatorial
|
||||
* North Pole direction.
|
||||
* \return Angle in degrees between the OpenSpace camera's up direction vector and the
|
||||
* equatorial North Pole direction.
|
||||
*/
|
||||
double targetRoll(const glm::dvec3& up, const glm::dvec3& forward);
|
||||
/**
|
||||
* Returns the view direction of the OpenSpace camera in galactic coordinates.
|
||||
* \return View direction of the OpenSpace camera in Cartesian galactic coordinates.
|
||||
*/
|
||||
glm::dvec3 cameraDirectionGalactic();
|
||||
/**
|
||||
* Returns the view direction of the OpenSpace camera in equatorial coordinates in epoch
|
||||
* J2000.
|
||||
* \return View direction of the OpenSpace camera in Cartesian equatorial coordinates in
|
||||
* epoch J2000.
|
||||
*/
|
||||
glm::dvec3 cameraDirectionEquatorial();
|
||||
|
||||
// Window and field of view
|
||||
/**
|
||||
* Returns the window ratio r which is calculated as x / y.
|
||||
* \return The window ratio x / y
|
||||
*/
|
||||
float windowRatio();
|
||||
/**
|
||||
* Returns the vertical and horizontal field of view of the OpenSpace window.
|
||||
* \return The horizontal and vertical field of view in degrees.
|
||||
*/
|
||||
glm::dvec2 fovWindow();
|
||||
/**
|
||||
* Returns true if the Cartesian equatorial coordinate is in the current view of the
|
||||
* camera.
|
||||
* \param equatorial Cartesian equatorial coordinates in epoch J2000
|
||||
* \return True if the coordinates are in the camera's current field of view
|
||||
*/
|
||||
bool isCoordinateInView(const glm::dvec3& equatorial);
|
||||
|
||||
// Animation for target and camera
|
||||
/**
|
||||
* Returns the angle between two vectors.
|
||||
* \param start Cartesian vector
|
||||
* \param end Cartesian vector
|
||||
* \return Angle between two vectors in radians
|
||||
*/
|
||||
double angleBetweenVectors(const glm::dvec3& start, const glm::dvec3& end);
|
||||
/**
|
||||
* Returns a 4x4 matrix for an incremental rotation of a vector. The matrix should be used
|
||||
* multiple times in order to animate.
|
||||
* \param start Cartesian vector
|
||||
* \param end Cartesian vector
|
||||
* \param percentage Percentage of the angle between the vectors that the matrix should
|
||||
* rotate
|
||||
* \return 4x4 matrix for incremental rotation animation of a vector
|
||||
*/
|
||||
glm::dmat4 incrementalAnimationMatrix(const glm::dvec3& start, const glm::dvec3& end,
|
||||
double percentage);
|
||||
/**
|
||||
* Returns the size in meters that for example a plane would need to have in order to
|
||||
* display a specified field of view.
|
||||
* \param fov The set field of view
|
||||
* \param worldPosition The galactic position of the plane
|
||||
* \return Field of view
|
||||
*/
|
||||
double sizeFromFov(double fov, glm::dvec3 worldPosition);
|
||||
|
||||
template<typename T>
|
||||
class Animation {
|
||||
public:
|
||||
Animation(T start, T goal, double time)
|
||||
: _start(start), _goal(goal)
|
||||
{
|
||||
_animationTime = std::chrono::milliseconds(static_cast<int>(time * 1000));
|
||||
}
|
||||
void start() {
|
||||
_isStarted = true;
|
||||
_startTime = std::chrono::system_clock::now();
|
||||
}
|
||||
void stop() {
|
||||
_isStarted = false;
|
||||
}
|
||||
bool isAnimating() const {
|
||||
bool timeLeft = timeSpent().count() < _animationTime.count() ? true : false;
|
||||
return timeLeft && _isStarted;
|
||||
}
|
||||
T getNewValue();
|
||||
glm::dmat4 getRotationMatrix();
|
||||
|
||||
private:
|
||||
std::chrono::duration<double, std::milli> timeSpent() const {
|
||||
using namespace std::chrono;
|
||||
system_clock::time_point now = system_clock::now();
|
||||
std::chrono::duration<double, std::milli> timeSpent = now - _startTime;
|
||||
return timeSpent;
|
||||
}
|
||||
double percentageSpent() const {
|
||||
return timeSpent().count() / _animationTime.count();
|
||||
}
|
||||
|
||||
double easeOutExpo(double x) {
|
||||
double epsilon = std::numeric_limits<double>::epsilon();
|
||||
return std::abs(x - 1.0) < epsilon ? 1.0 : 1.0 - pow(2.0, -10.0 * x);
|
||||
}
|
||||
|
||||
double easeInOutSine(double x) {
|
||||
return -(cos(glm::pi<double>() * x) - 1.0) / 2.0;
|
||||
}
|
||||
|
||||
// Animation
|
||||
bool _isStarted = false;
|
||||
double _lastPercentage = 0;
|
||||
T _goal;
|
||||
T _start;
|
||||
std::chrono::milliseconds _animationTime = std::chrono::milliseconds(2000);
|
||||
std::chrono::system_clock::time_point _startTime;
|
||||
};
|
||||
|
||||
} // namespace openspace::skybrowser
|
||||
|
||||
#endif // __OPENSPACE_MODULE_SKYBROWSER___UTILITY___H__
|
||||
114
modules/skybrowser/include/wwtcommunicator.h
Normal file
114
modules/skybrowser/include/wwtcommunicator.h
Normal file
@@ -0,0 +1,114 @@
|
||||
/*****************************************************************************************
|
||||
* *
|
||||
* OpenSpace *
|
||||
* *
|
||||
* Copyright (c) 2014-2022 *
|
||||
* *
|
||||
* 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 <modules/skybrowser/include/browser.h>
|
||||
|
||||
#include <openspace/properties/vector/ivec3property.h>
|
||||
#include <openspace/properties/vector/dvec2property.h>
|
||||
#include <openspace/properties/scalar/floatproperty.h>
|
||||
#include <openspace/documentation/documentation.h>
|
||||
#include <deque>
|
||||
|
||||
namespace openspace {
|
||||
|
||||
class WwtCommunicator : public Browser {
|
||||
public:
|
||||
explicit WwtCommunicator(const ghoul::Dictionary& dictionary);
|
||||
WwtCommunicator(const WwtCommunicator&) = default;
|
||||
~WwtCommunicator();
|
||||
|
||||
void update();
|
||||
void render();
|
||||
void initializeGL();
|
||||
void deinitializeGL();
|
||||
|
||||
// WorldWide Telescope communication
|
||||
void displayImage(const std::string& url, int i);
|
||||
void removeSelectedImage(int i);
|
||||
void setImageOrder(int i, int order);
|
||||
void loadImageCollection(const std::string& collection);
|
||||
void setImageOpacity(int i, float opacity);
|
||||
void hideChromeInterface(bool shouldHide);
|
||||
|
||||
bool hasLoadedImages() const;
|
||||
double verticalFov() const;
|
||||
glm::ivec3 borderColor() const;
|
||||
glm::dvec2 equatorialAim() const;
|
||||
glm::dvec2 fieldsOfView() const;
|
||||
const std::deque<int>& getSelectedImages() const;
|
||||
|
||||
void setHasLoadedImages(bool isLoaded);
|
||||
void setVerticalFov(double vfov);
|
||||
void setIsSyncedWithWwt(bool isSynced);
|
||||
void setEquatorialAim(glm::dvec2 equatorial);
|
||||
void setBorderColor(glm::ivec3 color);
|
||||
void setTargetRoll(double roll);
|
||||
|
||||
void highlight(const glm::ivec3& addition);
|
||||
// The removal parameter decides what will be removed from the border color
|
||||
void removeHighlight(const glm::ivec3& removal);
|
||||
void updateBorderColor();
|
||||
void updateAim();
|
||||
|
||||
protected:
|
||||
void setIdInBrowser(const std::string& id);
|
||||
|
||||
double _verticalFov = 10.0f;
|
||||
glm::ivec3 _borderColor = glm::ivec3(70);
|
||||
glm::dvec2 _equatorialAim = glm::dvec2(0.0);
|
||||
double _targetRoll = 0.0;
|
||||
bool _hasLoadedImages = false;
|
||||
std::deque<int> _selectedImages;
|
||||
|
||||
private:
|
||||
void setWebpageBorderColor(glm::ivec3 color);
|
||||
void sendMessageToWwt(const ghoul::Dictionary& msg);
|
||||
|
||||
// WorldWide Telescope messages
|
||||
ghoul::Dictionary moveCameraMessage(const glm::dvec2& celestCoords, double fov,
|
||||
double roll, bool shouldMoveInstantly = true);
|
||||
ghoul::Dictionary loadCollectionMessage(const std::string& url);
|
||||
ghoul::Dictionary setForegroundMessage(const std::string& name);
|
||||
ghoul::Dictionary addImageMessage(const std::string& id, const std::string& url);
|
||||
ghoul::Dictionary removeImageMessage(const std::string& id);
|
||||
ghoul::Dictionary setImageOpacityMessage(const std::string& id, double opacity);
|
||||
ghoul::Dictionary setLayerOrderMessage(const std::string& id, int version);
|
||||
|
||||
bool _isSyncedWithWwt = false;
|
||||
bool _borderColorIsDirty = false;
|
||||
bool _equatorialAimIsDirty = false;
|
||||
int messageCounter = 0;
|
||||
|
||||
// Time variables
|
||||
// For capping the message passing to WWT
|
||||
constexpr static const std::chrono::milliseconds TimeUpdateInterval =
|
||||
std::chrono::milliseconds(10);
|
||||
std::chrono::system_clock::time_point _lastUpdateTime;
|
||||
};
|
||||
} // namespace openspace
|
||||
|
||||
#endif // __OPENSPACE_MODULE_SKYBROWSER___WWTCOMMUNICATOR___H__
|
||||
88
modules/skybrowser/include/wwtdatahandler.h
Normal file
88
modules/skybrowser/include/wwtdatahandler.h
Normal file
@@ -0,0 +1,88 @@
|
||||
/*****************************************************************************************
|
||||
* *
|
||||
* OpenSpace *
|
||||
* *
|
||||
* Copyright (c) 2014-2022 *
|
||||
* *
|
||||
* 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___WWTDATAHANDLER___H__
|
||||
#define __OPENSPACE_MODULE_SKYBROWSER___WWTDATAHANDLER___H__
|
||||
|
||||
#include <modules/skybrowser/ext/tinyxml2/tinyxml2.h>
|
||||
#include <modules/space/speckloader.h>
|
||||
#include <openspace/documentation/documentation.h>
|
||||
#include <unordered_map>
|
||||
|
||||
namespace openspace::documentation { struct Documentation; }
|
||||
|
||||
namespace openspace::wwt {
|
||||
const std::string Thumbnail = "Thumbnail";
|
||||
const std::string Name = "Name";
|
||||
const std::string ImageSet = "ImageSet";
|
||||
const std::string Dec = "Dec";
|
||||
const std::string RA = "RA";
|
||||
const std::string Undefined = "";
|
||||
const std::string Folder = "Folder";
|
||||
const std::string Place = "Place";
|
||||
const std::string ThumbnailUrl = "ThumbnailUrl";
|
||||
const std::string Url = "Url";
|
||||
const std::string Credits = "Credits";
|
||||
const std::string CreditsUrl = "CreditsUrl";
|
||||
const std::string ZoomLevel = "ZoomLevel";
|
||||
const std::string DataSetType = "DataSetType";
|
||||
const std::string Sky = "Sky";
|
||||
} // namespace openspace::wwt
|
||||
|
||||
namespace openspace {
|
||||
|
||||
struct ImageData {
|
||||
std::string name = wwt::Undefined;
|
||||
std::string thumbnailUrl = wwt::Undefined;
|
||||
std::string imageUrl = wwt::Undefined;
|
||||
std::string credits = wwt::Undefined;
|
||||
std::string creditsUrl = wwt::Undefined;
|
||||
std::string collection = wwt::Undefined;
|
||||
bool hasCelestialCoords = false;
|
||||
float fov = 0.f;
|
||||
glm::dvec2 equatorialSpherical = glm::dvec2(0.0);
|
||||
glm::dvec3 equatorialCartesian = glm::dvec3(0.0);
|
||||
};
|
||||
|
||||
class WwtDataHandler {
|
||||
public:
|
||||
WwtDataHandler() = default;
|
||||
~WwtDataHandler();
|
||||
|
||||
void loadImages(const std::string& root, const std::filesystem::path& directory);
|
||||
int nLoadedImages() const;
|
||||
const ImageData& getImage(int i) const;
|
||||
|
||||
private:
|
||||
void saveImageFromNode(tinyxml2::XMLElement* node, std::string collection);
|
||||
void saveImagesFromXml(tinyxml2::XMLElement* root, std::string collection);
|
||||
|
||||
// Images
|
||||
std::vector<ImageData> _images;
|
||||
std::vector<tinyxml2::XMLDocument*> _xmls;
|
||||
};
|
||||
} // namespace openspace
|
||||
|
||||
#endif // __OPENSPACE_MODULE_SKYBROWSER___WWTDATAHANDLER___H__
|
||||
|
||||
114
modules/skybrowser/shaders/target_fs.glsl
Normal file
114
modules/skybrowser/shaders/target_fs.glsl
Normal file
@@ -0,0 +1,114 @@
|
||||
/*****************************************************************************************
|
||||
* *
|
||||
* OpenSpace *
|
||||
* *
|
||||
* Copyright (c) 2014-2022 *
|
||||
* *
|
||||
* 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. *
|
||||
****************************************************************************************/
|
||||
|
||||
|
||||
in vec4 vs_gPosition;
|
||||
in vec3 vs_gNormal;
|
||||
in float vs_screenSpaceDepth;
|
||||
in vec2 vs_st;
|
||||
in vec4 vs_position;
|
||||
|
||||
uniform float crossHairSize;
|
||||
uniform bool showRectangle;
|
||||
uniform float lineWidth;
|
||||
uniform vec2 dimensions;
|
||||
uniform vec4 lineColor;
|
||||
uniform float fov;
|
||||
|
||||
uniform bool additiveBlending;
|
||||
uniform float opacity = 1.0;
|
||||
uniform vec3 multiplyColor;
|
||||
|
||||
// A factor which states how much thicker vertical lines are rendered than horizontal
|
||||
// This compensates for the optical illusion that vertical lines appear thinner
|
||||
const float VerticalThickness = 1.1;
|
||||
|
||||
float createLine(float lineCenter, float lineWidth, float coord) {
|
||||
// Calculate edges of line
|
||||
float startEdge = lineCenter - (lineWidth * 0.5);
|
||||
float endEdge = lineCenter + (lineWidth * 0.5);
|
||||
|
||||
return step(startEdge, coord) - step(endEdge, coord);
|
||||
}
|
||||
|
||||
float createFilledRectangle(float width, float height, vec2 coord) {
|
||||
return createLine(0.5, width, coord.x) * createLine(0.5, height, coord.y);
|
||||
}
|
||||
|
||||
float createCrosshair(in float linewidth, in float ratio, in vec2 coord) {
|
||||
const float Center = 0.5;
|
||||
float crosshairVertical = createLine(Center, linewidth * VerticalThickness, coord.x);
|
||||
float crosshairHorizontal = createLine(Center, linewidth, coord.y);
|
||||
|
||||
return crosshairHorizontal + crosshairVertical;
|
||||
}
|
||||
|
||||
#include "fragment.glsl"
|
||||
|
||||
Fragment getFragment() {
|
||||
float ratio = dimensions.y / dimensions.x;
|
||||
float rectangle = 0.0;
|
||||
float maxWwtFov = 70;
|
||||
|
||||
float crosshair = createCrosshair(lineWidth, ratio, vs_st);
|
||||
float crossHairHeight = crossHairSize/maxWwtFov;
|
||||
float crossHairWidth = crossHairHeight * ratio;
|
||||
float crossHairBox = createFilledRectangle(crossHairHeight, crossHairWidth, vs_st);
|
||||
crosshair *= crossHairBox;
|
||||
|
||||
if (showRectangle) {
|
||||
float height = (fov * 0.5)/maxWwtFov;
|
||||
float width = height * ratio;
|
||||
float outerEdge = createFilledRectangle(height, width, vs_st);
|
||||
float lineWidthX = lineWidth * 2 * VerticalThickness;
|
||||
float lineWidthY = lineWidth * 2;
|
||||
float innerEdge = createFilledRectangle(height-lineWidthX, width-lineWidthY, vs_st);
|
||||
rectangle = outerEdge - innerEdge;
|
||||
}
|
||||
|
||||
float result = clamp(crosshair + rectangle, 0.0, 1.0);
|
||||
|
||||
Fragment frag;
|
||||
frag.color = lineColor;
|
||||
frag.color.a *= result;
|
||||
|
||||
frag.color.rgb *= multiplyColor;
|
||||
|
||||
frag.color.a *= opacity;
|
||||
if (frag.color.a == 0.0) {
|
||||
discard;
|
||||
}
|
||||
|
||||
frag.depth = vs_screenSpaceDepth;
|
||||
|
||||
if (additiveBlending) {
|
||||
frag.blend = BLEND_MODE_ADDITIVE;
|
||||
}
|
||||
|
||||
// G-Buffer
|
||||
frag.gPosition = vs_gPosition;
|
||||
frag.gNormal = vec4(vs_gNormal, 1.0);
|
||||
|
||||
return frag;
|
||||
}
|
||||
52
modules/skybrowser/shaders/target_vs.glsl
Normal file
52
modules/skybrowser/shaders/target_vs.glsl
Normal file
@@ -0,0 +1,52 @@
|
||||
/*****************************************************************************************
|
||||
* *
|
||||
* OpenSpace *
|
||||
* *
|
||||
* Copyright (c) 2014-2022 *
|
||||
* *
|
||||
* 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. *
|
||||
****************************************************************************************/
|
||||
#version __CONTEXT__
|
||||
|
||||
#include "PowerScaling/powerScaling_vs.hglsl"
|
||||
|
||||
layout(location = 0) in vec4 in_position;
|
||||
layout(location = 1) in vec2 in_st;
|
||||
|
||||
out vec4 vs_gPosition;
|
||||
out vec3 vs_gNormal;
|
||||
out float vs_screenSpaceDepth;
|
||||
out vec2 vs_st;
|
||||
|
||||
uniform mat4 modelViewProjectionTransform;
|
||||
uniform mat4 modelViewTransform;
|
||||
|
||||
void main() {
|
||||
vec4 position = vec4(in_position.xyz * pow(10, in_position.w), 1);
|
||||
vec4 positionClipSpace = modelViewProjectionTransform * position;
|
||||
vec4 positionScreenSpace = z_normalization(positionClipSpace);
|
||||
|
||||
gl_Position = positionScreenSpace;
|
||||
|
||||
// G-Buffer
|
||||
vs_gNormal = vec3(0.0);
|
||||
vs_gPosition = vec4(modelViewTransform * position); // Must be in SGCT eye space;
|
||||
|
||||
vs_st = in_st;
|
||||
vs_screenSpaceDepth = positionScreenSpace.w;
|
||||
}
|
||||
447
modules/skybrowser/skybrowsermodule.cpp
Normal file
447
modules/skybrowser/skybrowsermodule.cpp
Normal file
@@ -0,0 +1,447 @@
|
||||
/*****************************************************************************************
|
||||
* *
|
||||
* OpenSpace *
|
||||
* *
|
||||
* Copyright (c) 2014-2022 *
|
||||
* *
|
||||
* 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 <modules/skybrowser/skybrowsermodule.h>
|
||||
|
||||
#include <modules/skybrowser/include/renderableskytarget.h>
|
||||
#include <modules/skybrowser/include/screenspaceskybrowser.h>
|
||||
#include <modules/skybrowser/include/targetbrowserpair.h>
|
||||
#include <modules/skybrowser/include/wwtdatahandler.h>
|
||||
#include <openspace/camera/camera.h>
|
||||
#include <openspace/engine/globalscallbacks.h>
|
||||
#include <openspace/engine/windowdelegate.h>
|
||||
#include <openspace/navigation/navigationhandler.h>
|
||||
#include <openspace/scene/scene.h>
|
||||
#include <openspace/scene/scenegraphnode.h>
|
||||
#include <openspace/util/factorymanager.h>
|
||||
|
||||
#include "skybrowsermodule_lua.inl"
|
||||
|
||||
namespace {
|
||||
constexpr const openspace::properties::Property::PropertyInfo AllowRotationInfo = {
|
||||
"AllowCameraRotation",
|
||||
"Allow Camera Rotation",
|
||||
"Toggles if the camera should rotate to look at the sky target if it is going "
|
||||
"outside of the current field of view."
|
||||
};
|
||||
|
||||
constexpr const openspace::properties::Property::PropertyInfo CameraRotSpeedInfo = {
|
||||
"CameraRotationSpeed",
|
||||
"Camera Rotation Speed",
|
||||
"The speed of the rotation of the camera when the camera rotates to look at a "
|
||||
"coordinate which is outside of the field of view."
|
||||
};
|
||||
|
||||
constexpr const openspace::properties::Property::PropertyInfo TargetSpeedInfo = {
|
||||
"TargetAnimationSpeed",
|
||||
"Target Animation Speed",
|
||||
"This determines the speed of the animation of the sky target."
|
||||
};
|
||||
|
||||
constexpr const openspace::properties::Property::PropertyInfo BrowserSpeedInfo = {
|
||||
"BrowserAnimationSpeed",
|
||||
"Field Of View Animation Speed",
|
||||
"This determines the speed of the animation of the field of view in the browser."
|
||||
};
|
||||
|
||||
struct [[codegen::Dictionary(SkyBrowserModule)]] Parameters {
|
||||
|
||||
// [[codegen::verbatim(AllowRotationInfo.description)]]
|
||||
std::optional<bool> allowCameraRotation;
|
||||
|
||||
// [[codegen::verbatim(CameraRotSpeedInfo.description)]]
|
||||
std::optional<double> cameraRotSpeed;
|
||||
|
||||
// [[codegen::verbatim(TargetSpeedInfo.description)]]
|
||||
std::optional<double> targetSpeed;
|
||||
|
||||
// [[codegen::verbatim(BrowserSpeedInfo.description)]]
|
||||
std::optional<double> browserSpeed;
|
||||
};
|
||||
|
||||
#include "skybrowsermodule_codegen.cpp"
|
||||
} // namespace
|
||||
|
||||
namespace openspace {
|
||||
SkyBrowserModule::SkyBrowserModule()
|
||||
: OpenSpaceModule(SkyBrowserModule::Name)
|
||||
, _allowCameraRotation(AllowRotationInfo, true)
|
||||
, _cameraRotationSpeed(CameraRotSpeedInfo, 0.5, 0.0, 1.0)
|
||||
, _targetAnimationSpeed(TargetSpeedInfo, 0.2, 0.0, 1.0)
|
||||
, _browserAnimationSpeed(BrowserSpeedInfo, 5.0, 0.0, 10.0)
|
||||
{
|
||||
addProperty(_allowCameraRotation);
|
||||
addProperty(_cameraRotationSpeed);
|
||||
addProperty(_targetAnimationSpeed);
|
||||
addProperty(_browserAnimationSpeed);
|
||||
|
||||
// Set callback functions
|
||||
global::callback::mouseButton->emplace(global::callback::mouseButton->begin(),
|
||||
[&](MouseButton button, MouseAction action, KeyModifier modifier) -> bool {
|
||||
if (action == MouseAction::Press) {
|
||||
_cameraRotation.stop();
|
||||
return false;
|
||||
}
|
||||
else {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
);
|
||||
|
||||
global::callback::preSync->emplace_back([this]() {
|
||||
// Disable browser and targets when camera is outside of solar system
|
||||
bool camWasInSolarSystem = _isCameraInSolarSystem;
|
||||
glm::dvec3 cameraPos = global::navigationHandler->camera()->positionVec3();
|
||||
_isCameraInSolarSystem = glm::length(cameraPos) < SolarSystemRadius;
|
||||
bool vizModeChanged = _isCameraInSolarSystem != camWasInSolarSystem;
|
||||
|
||||
// Visualization mode changed. Start fading
|
||||
if (vizModeChanged && !_isCameraInSolarSystem) {
|
||||
// Camera moved into the solar system
|
||||
_isFading = true;
|
||||
_goal = Transparency::Transparent;
|
||||
|
||||
float transparency = [](Transparency goal) {
|
||||
switch (goal) {
|
||||
case Transparency::Transparent:
|
||||
return 0.f;
|
||||
|
||||
case Transparency::Opaque:
|
||||
return 1.f;
|
||||
}
|
||||
}(_goal);
|
||||
|
||||
std::for_each(
|
||||
_targetsBrowsers.begin(),
|
||||
_targetsBrowsers.end(),
|
||||
[&](const std::unique_ptr<TargetBrowserPair>& pair) {
|
||||
pair->startFading(transparency, 2.f);
|
||||
}
|
||||
);
|
||||
}
|
||||
// Fade pairs if the camera moved in or out the solar system
|
||||
if (_isFading) {
|
||||
incrementallyFadeBrowserTargets(_goal);
|
||||
}
|
||||
if (_isCameraInSolarSystem) {
|
||||
std::for_each(
|
||||
_targetsBrowsers.begin(),
|
||||
_targetsBrowsers.end(),
|
||||
[&](const std::unique_ptr<TargetBrowserPair>& pair) {
|
||||
pair->synchronizeAim();
|
||||
}
|
||||
);
|
||||
incrementallyAnimateTargets();
|
||||
}
|
||||
if (_cameraRotation.isAnimating() && _allowCameraRotation) {
|
||||
incrementallyRotateCamera();
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
void SkyBrowserModule::internalInitialize(const ghoul::Dictionary& dict) {
|
||||
const Parameters p = codegen::bake<Parameters>(dict);
|
||||
|
||||
// Register ScreenSpaceRenderable
|
||||
ghoul::TemplateFactory<ScreenSpaceRenderable>* fScreenSpaceRenderable =
|
||||
FactoryManager::ref().factory<ScreenSpaceRenderable>();
|
||||
ghoul_assert(fScreenSpaceRenderable, "ScreenSpaceRenderable factory was not created");
|
||||
|
||||
|
||||
// Register ScreenSpaceSkyBrowser
|
||||
fScreenSpaceRenderable->registerClass<ScreenSpaceSkyBrowser>("ScreenSpaceSkyBrowser");
|
||||
|
||||
// Register ScreenSpaceRenderable
|
||||
ghoul::TemplateFactory<Renderable>* fRenderable =
|
||||
FactoryManager::ref().factory<Renderable>();
|
||||
ghoul_assert(fRenderable, "Renderable factory was not created");
|
||||
|
||||
// Register ScreenSpaceSkyTarget
|
||||
fRenderable->registerClass<RenderableSkyTarget>("RenderableSkyTarget");
|
||||
|
||||
// Create data handler dynamically to avoid the linking error that
|
||||
// came up when including the include file in the module header file
|
||||
_dataHandler = std::make_unique<WwtDataHandler>();
|
||||
}
|
||||
|
||||
void SkyBrowserModule::addTargetBrowserPair(const std::string& targetId, const std::string& browserId) {
|
||||
if (!global::renderEngine->scene()) {
|
||||
return;
|
||||
}
|
||||
|
||||
SceneGraphNode* target = global::renderEngine->scene()->sceneGraphNode(targetId);
|
||||
ScreenSpaceSkyBrowser* browser = dynamic_cast<ScreenSpaceSkyBrowser*>(
|
||||
global::renderEngine->screenSpaceRenderable(browserId)
|
||||
);
|
||||
|
||||
// Ensure pair has both target and browser
|
||||
if (browser && target) {
|
||||
_targetsBrowsers.push_back(std::make_unique<TargetBrowserPair>(target, browser));
|
||||
}
|
||||
}
|
||||
|
||||
void SkyBrowserModule::removeTargetBrowserPair(const std::string& id) {
|
||||
TargetBrowserPair* found = getPair(id);
|
||||
if (!found) {
|
||||
return;
|
||||
}
|
||||
|
||||
auto it = std::remove_if(
|
||||
_targetsBrowsers.begin(),
|
||||
_targetsBrowsers.end(),
|
||||
[&](const std::unique_ptr<TargetBrowserPair>& pair) {
|
||||
return *found == *(pair.get());
|
||||
}
|
||||
);
|
||||
|
||||
_targetsBrowsers.erase(it, _targetsBrowsers.end());
|
||||
_mouseOnPair = nullptr;
|
||||
}
|
||||
|
||||
void SkyBrowserModule::lookAtTarget(const std::string& id) {
|
||||
TargetBrowserPair* pair = getPair(id);
|
||||
if (pair) {
|
||||
startRotatingCamera(pair->targetDirectionGalactic());
|
||||
}
|
||||
}
|
||||
|
||||
void SkyBrowserModule::setHoverCircle(SceneGraphNode* circle) {
|
||||
_hoverCircle = circle;
|
||||
}
|
||||
|
||||
void SkyBrowserModule::moveHoverCircle(int i) {
|
||||
const ImageData& image = _dataHandler->getImage(i);
|
||||
|
||||
// Only move and show circle if the image has coordinates
|
||||
if (_hoverCircle && image.hasCelestialCoords && _isCameraInSolarSystem) {
|
||||
// Make circle visible
|
||||
_hoverCircle->renderable()->property("Enabled")->set(true);
|
||||
|
||||
// Set the exact target position
|
||||
// Move it slightly outside of the celestial sphere so it doesn't overlap with
|
||||
// the target
|
||||
glm::dvec3 pos = skybrowser::equatorialToGalactic(image.equatorialCartesian);
|
||||
pos *= skybrowser::CelestialSphereRadius * 1.1;
|
||||
// Uris for properties
|
||||
std::string id = _hoverCircle->identifier();
|
||||
std::string positionUri = "Scene." + id + ".Translation.Position";
|
||||
std::string setValue = "openspace.setPropertyValueSingle('";
|
||||
|
||||
openspace::global::scriptEngine->queueScript(
|
||||
setValue + positionUri + "', " + ghoul::to_string(pos) + ");",
|
||||
scripting::ScriptEngine::RemoteScripting::Yes
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
void SkyBrowserModule::disableHoverCircle() {
|
||||
if (_hoverCircle && _hoverCircle->renderable()) {
|
||||
_hoverCircle->renderable()->property("Enabled")->set(false);
|
||||
}
|
||||
}
|
||||
|
||||
void SkyBrowserModule::loadImages(const std::string& root,
|
||||
const std::filesystem::path& directory) {
|
||||
_dataHandler->loadImages(root, directory);
|
||||
}
|
||||
|
||||
int SkyBrowserModule::nLoadedImages() {
|
||||
return _dataHandler->nLoadedImages();
|
||||
}
|
||||
|
||||
const std::unique_ptr<WwtDataHandler>& SkyBrowserModule::getWwtDataHandler() const {
|
||||
return _dataHandler;
|
||||
}
|
||||
|
||||
std::vector<std::unique_ptr<TargetBrowserPair>>& SkyBrowserModule::getPairs() {
|
||||
return _targetsBrowsers;
|
||||
}
|
||||
|
||||
int SkyBrowserModule::nPairs() {
|
||||
return static_cast<int>(_targetsBrowsers.size());
|
||||
}
|
||||
|
||||
TargetBrowserPair* SkyBrowserModule::getPair(const std::string& id) {
|
||||
auto it = std::find_if(
|
||||
_targetsBrowsers.begin(),
|
||||
_targetsBrowsers.end(),
|
||||
[&](const std::unique_ptr<TargetBrowserPair>& pair) {
|
||||
bool foundBrowser = pair->browserId() == id;
|
||||
bool foundTarget = pair->targetRenderableId() == id;
|
||||
bool foundTargetNode = pair->targetNodeId() == id;
|
||||
return foundBrowser || foundTarget || foundTargetNode;
|
||||
}
|
||||
);
|
||||
if (it == std::end(_targetsBrowsers)) {
|
||||
return nullptr;
|
||||
}
|
||||
else {
|
||||
return it->get();
|
||||
}
|
||||
}
|
||||
|
||||
void SkyBrowserModule::startRotatingCamera(glm::dvec3 endAnimation) {
|
||||
// Save coordinates to rotate to in galactic world coordinates
|
||||
glm::dvec3 start = skybrowser::cameraDirectionGalactic();
|
||||
double angle = skybrowser::angleBetweenVectors(start, endAnimation);
|
||||
double time = angle / _cameraRotationSpeed;
|
||||
_cameraRotation = skybrowser::Animation(start, endAnimation, time);
|
||||
_cameraRotation.start();
|
||||
}
|
||||
|
||||
void SkyBrowserModule::incrementallyRotateCamera() {
|
||||
if(_cameraRotation.isAnimating()) {
|
||||
glm::dmat4 rotMat = _cameraRotation.getRotationMatrix();
|
||||
// Rotate
|
||||
global::navigationHandler->camera()->rotate(glm::quat_cast(rotMat));
|
||||
}
|
||||
}
|
||||
|
||||
void SkyBrowserModule::incrementallyFadeBrowserTargets(Transparency goal)
|
||||
{
|
||||
bool isAllFinished = true;
|
||||
for (std::unique_ptr<TargetBrowserPair>& pair : _targetsBrowsers) {
|
||||
if (pair->isEnabled()) {
|
||||
bool isPairFinished = pair->hasFinishedFading();
|
||||
if (!isPairFinished) {
|
||||
pair->incrementallyFade();
|
||||
}
|
||||
else if (isPairFinished && goal == Transparency::Transparent) {
|
||||
pair->setEnabled(false);
|
||||
pair->setOpacity(1.0);
|
||||
|
||||
}
|
||||
isAllFinished &= isPairFinished;
|
||||
}
|
||||
}
|
||||
|
||||
// The transition is over when the fade is finished
|
||||
if (isAllFinished) {
|
||||
_isFading = false;
|
||||
}
|
||||
}
|
||||
|
||||
void SkyBrowserModule::incrementallyAnimateTargets() {
|
||||
for (std::unique_ptr<TargetBrowserPair>& pair : _targetsBrowsers) {
|
||||
if (pair->isEnabled()) {
|
||||
pair->incrementallyAnimateToCoordinate();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
double SkyBrowserModule::targetAnimationSpeed() const {
|
||||
return _targetAnimationSpeed;
|
||||
}
|
||||
|
||||
double SkyBrowserModule::browserAnimationSpeed() const {
|
||||
return _browserAnimationSpeed;
|
||||
}
|
||||
|
||||
void SkyBrowserModule::setSelectedBrowser(const std::string& id) {
|
||||
TargetBrowserPair* found = getPair(id);
|
||||
if (found) {
|
||||
_selectedBrowser = id;
|
||||
}
|
||||
}
|
||||
|
||||
std::string SkyBrowserModule::selectedBrowserId() const {
|
||||
return _selectedBrowser;
|
||||
}
|
||||
|
||||
std::string SkyBrowserModule::selectedTargetId() {
|
||||
TargetBrowserPair* found = getPair(_selectedBrowser);
|
||||
if (found) {
|
||||
return found->targetRenderableId();
|
||||
}
|
||||
else {
|
||||
return "";
|
||||
}
|
||||
}
|
||||
|
||||
glm::ivec3 SkyBrowserModule::highlight() const {
|
||||
return _highlightAddition;
|
||||
}
|
||||
|
||||
bool SkyBrowserModule::isCameraInSolarSystem() const {
|
||||
return _isCameraInSolarSystem;
|
||||
}
|
||||
|
||||
bool SkyBrowserModule::isSelectedPairUsingRae() {
|
||||
TargetBrowserPair* found = getPair(_selectedBrowser);
|
||||
if (found) {
|
||||
return found->isUsingRadiusAzimuthElevation();
|
||||
}
|
||||
else {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
bool SkyBrowserModule::isSelectedPairFacingCamera() {
|
||||
TargetBrowserPair* found = getPair(_selectedBrowser);
|
||||
if (found) {
|
||||
return found->isFacingCamera();
|
||||
}
|
||||
else {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
scripting::LuaLibrary SkyBrowserModule::luaLibrary() const {
|
||||
return {
|
||||
"skybrowser",
|
||||
{
|
||||
codegen::lua::StartSetup,
|
||||
codegen::lua::InitializeBrowser,
|
||||
codegen::lua::SendOutIdsToBrowsers,
|
||||
codegen::lua::GetListOfImages,
|
||||
codegen::lua::SetHoverCircle,
|
||||
codegen::lua::MoveCircleToHoverImage,
|
||||
codegen::lua::DisableHoverCircle,
|
||||
codegen::lua::LoadImagesToWWT,
|
||||
codegen::lua::SelectImage,
|
||||
codegen::lua::RemoveSelectedImageInBrowser,
|
||||
codegen::lua::AdjustCamera,
|
||||
codegen::lua::SetSelectedBrowser,
|
||||
codegen::lua::GetTargetData,
|
||||
codegen::lua::CreateTargetBrowserPair,
|
||||
codegen::lua::RemoveTargetBrowserPair,
|
||||
codegen::lua::SetOpacityOfImageLayer,
|
||||
codegen::lua::CenterTargetOnScreen,
|
||||
codegen::lua::SetImageLayerOrder,
|
||||
codegen::lua::AddPairToSkyBrowserModule,
|
||||
codegen::lua::SetEquatorialAim,
|
||||
codegen::lua::SetVerticalFov,
|
||||
codegen::lua::SetBorderColor,
|
||||
codegen::lua::TranslateScreenSpaceRenderable,
|
||||
codegen::lua::AddRenderCopy,
|
||||
codegen::lua::SetScreenSpaceSize,
|
||||
codegen::lua::RemoveRenderCopy,
|
||||
codegen::lua::StartFinetuningTarget,
|
||||
codegen::lua::FinetuneTargetPosition,
|
||||
codegen::lua::ScrollOverBrowser
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
} // namespace openspace
|
||||
146
modules/skybrowser/skybrowsermodule.h
Normal file
146
modules/skybrowser/skybrowsermodule.h
Normal file
@@ -0,0 +1,146 @@
|
||||
/*****************************************************************************************
|
||||
* *
|
||||
* OpenSpace *
|
||||
* *
|
||||
* Copyright (c) 2014-2022 *
|
||||
* *
|
||||
* 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___SKYBROWSERMODULE___H__
|
||||
#define __OPENSPACE_MODULE_SKYBROWSER___SKYBROWSERMODULE___H__
|
||||
|
||||
#include <modules/skybrowser/include/utility.h>
|
||||
|
||||
#include <openspace/util/openspacemodule.h>
|
||||
#include <openspace/documentation/documentation.h>
|
||||
#include <openspace/util/distanceconstants.h>
|
||||
#include <openspace/util/mouse.h>
|
||||
#include <openspace/properties/scalar/boolproperty.h>
|
||||
#include <openspace/properties/scalar/doubleproperty.h>
|
||||
#include <fstream>
|
||||
|
||||
namespace openspace {
|
||||
|
||||
class ScreenSpaceImageLocal;
|
||||
class WwtDataHandler;
|
||||
class TargetBrowserPair;
|
||||
class SceneGraphNode;
|
||||
class ImageData;
|
||||
|
||||
enum class Transparency {
|
||||
Transparent,
|
||||
Opaque
|
||||
};
|
||||
|
||||
enum class MouseInteraction {
|
||||
Hover,
|
||||
Drag,
|
||||
FineTune,
|
||||
};
|
||||
|
||||
class SkyBrowserModule : public OpenSpaceModule {
|
||||
public:
|
||||
constexpr static const char* Name = "SkyBrowser";
|
||||
const double SolarSystemRadius = 30.0 * distanceconstants::AstronomicalUnit;
|
||||
|
||||
SkyBrowserModule();
|
||||
|
||||
std::vector<std::unique_ptr<TargetBrowserPair>>& getPairs();
|
||||
int nPairs();
|
||||
TargetBrowserPair* getPair(const std::string& id);
|
||||
const std::unique_ptr<WwtDataHandler>& getWwtDataHandler() const;
|
||||
std::string selectedBrowserId() const;
|
||||
std::string selectedTargetId();
|
||||
glm::ivec3 highlight() const;
|
||||
|
||||
void setSelectedBrowser(const std::string& id);
|
||||
void setSelectedObject(); // Manage mouse interactions
|
||||
void setHoverCircle(SceneGraphNode* circle);
|
||||
|
||||
// Rotation, animation, placement
|
||||
void lookAtTarget(const std::string& id);
|
||||
void startRotatingCamera(glm::dvec3 endAnimation); // Pass in galactic coordinate
|
||||
void incrementallyRotateCamera();
|
||||
void incrementallyFadeBrowserTargets(Transparency goal);
|
||||
void incrementallyAnimateTargets();
|
||||
double targetAnimationSpeed() const;
|
||||
double browserAnimationSpeed() const;
|
||||
|
||||
bool isCameraInSolarSystem() const;
|
||||
bool isSelectedPairFacingCamera();
|
||||
bool isSelectedPairUsingRae();
|
||||
|
||||
// Managing the target browser pairs
|
||||
void removeTargetBrowserPair(const std::string& browserId);
|
||||
void addTargetBrowserPair(const std::string& targetId, const std::string& browserId);
|
||||
|
||||
// Hover circle
|
||||
void moveHoverCircle(int i);
|
||||
void disableHoverCircle();
|
||||
|
||||
// Image collection handling
|
||||
void loadImages(const std::string& root, const std::filesystem::path& directory);
|
||||
int nLoadedImages();
|
||||
|
||||
// Mouse interaction
|
||||
void handleMouseClick(const MouseButton& button);
|
||||
|
||||
scripting::LuaLibrary luaLibrary() const override;
|
||||
//std::vector<documentation::Documentation> documentations() const override;
|
||||
|
||||
protected:
|
||||
void internalInitialize(const ghoul::Dictionary& dict) override;
|
||||
|
||||
private:
|
||||
properties::BoolProperty _allowCameraRotation;
|
||||
properties::DoubleProperty _cameraRotationSpeed;
|
||||
properties::DoubleProperty _targetAnimationSpeed;
|
||||
properties::DoubleProperty _browserAnimationSpeed;
|
||||
glm::ivec3 _highlightAddition = glm::ivec3(35); // Highlight object when mouse hovers
|
||||
|
||||
// The browsers and targets
|
||||
std::vector<std::unique_ptr<TargetBrowserPair>> _targetsBrowsers;
|
||||
TargetBrowserPair* _mouseOnPair = nullptr;
|
||||
SceneGraphNode* _hoverCircle = nullptr;
|
||||
std::string _selectedBrowser = ""; // Currently selected browser
|
||||
|
||||
// Fading
|
||||
Transparency _goal = Transparency::Opaque;
|
||||
|
||||
// Flags
|
||||
bool _isCameraInSolarSystem = true; // Visualization modes
|
||||
bool _isFading = false;
|
||||
|
||||
// Mouse interaction
|
||||
MouseInteraction _interactionMode;
|
||||
glm::vec2 _mousePosition; // Current mouse position in screen space coordinates
|
||||
glm::vec2 _startMousePosition;
|
||||
glm::vec2 _startDragPosition;
|
||||
glm::dvec3 _startTargetPosition;
|
||||
|
||||
// Animation of rotation of camera to look at coordinate galactic coordinates
|
||||
skybrowser::Animation<glm::dvec3> _cameraRotation =
|
||||
skybrowser::Animation(glm::dvec3(0.0), glm::dvec3(0.0), 0.0);
|
||||
|
||||
// Data handler for the image collections
|
||||
std::unique_ptr<WwtDataHandler> _dataHandler;
|
||||
};
|
||||
} // namespace openspace
|
||||
|
||||
#endif // __OPENSPACE_MODULE_SKYBROWSER___SKYBROWSERMODULE___H__
|
||||
690
modules/skybrowser/skybrowsermodule_lua.inl
Normal file
690
modules/skybrowser/skybrowsermodule_lua.inl
Normal file
@@ -0,0 +1,690 @@
|
||||
/*****************************************************************************************
|
||||
* *
|
||||
* OpenSpace *
|
||||
* *
|
||||
* Copyright (c) 2014-2022 *
|
||||
* *
|
||||
* 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 <modules/skybrowser/skybrowsermodule.h>
|
||||
|
||||
#include <modules/skybrowser/include/utility.h>
|
||||
#include <modules/skybrowser/include/targetbrowserpair.h>
|
||||
#include <modules/skybrowser/include/wwtdatahandler.h>
|
||||
#include <openspace/engine/globals.h>
|
||||
#include <openspace/engine/moduleengine.h>
|
||||
#include <openspace/engine/windowdelegate.h>
|
||||
#include <openspace/rendering/renderengine.h>
|
||||
#include <openspace/scripting/scriptengine.h>
|
||||
#include <ghoul/filesystem/filesystem.h>
|
||||
#include <ghoul/logging/logmanager.h>
|
||||
|
||||
namespace {
|
||||
constexpr const char _loggerCat[] = "SkyBrowserModule";
|
||||
|
||||
using namespace openspace;
|
||||
|
||||
/**
|
||||
* Takes an index to an image and selects that image in the currently
|
||||
* selected sky browser.
|
||||
* \param i Index of image
|
||||
*/
|
||||
[[codegen::luawrap]] void selectImage(int i) {
|
||||
// Load image
|
||||
SkyBrowserModule* module = global::moduleEngine->module<SkyBrowserModule>();
|
||||
|
||||
if (module->isCameraInSolarSystem()) {
|
||||
TargetBrowserPair* selected = module->getPair(module->selectedBrowserId());
|
||||
if (selected) {
|
||||
const ImageData& image = module->getWwtDataHandler()->getImage(i);
|
||||
// Load image into browser
|
||||
std::string str = image.name;
|
||||
str.erase(std::remove_if(str.begin(), str.end(), [](char c) {
|
||||
// Check if character is ASCII - if it isn't, remove
|
||||
return !(c >= 0 && c < 128);
|
||||
}),
|
||||
str.end());
|
||||
LINFO("Loading image " + str);
|
||||
selected->selectImage(image, i);
|
||||
|
||||
bool isInView = skybrowser::isCoordinateInView(image.equatorialCartesian);
|
||||
// If the coordinate is not in view, rotate camera
|
||||
if (image.hasCelestialCoords && !isInView) {
|
||||
module->startRotatingCamera(
|
||||
skybrowser::equatorialToGalactic(
|
||||
image.equatorialCartesian * skybrowser::CelestialSphereRadius
|
||||
)
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
/**
|
||||
* Takes an identifier to a screen space renderable and adds it to the module.
|
||||
* \param id Identifier
|
||||
*/
|
||||
[[codegen::luawrap]] void setHoverCircle(std::string id) {
|
||||
SkyBrowserModule* module = global::moduleEngine->module<SkyBrowserModule>();
|
||||
|
||||
SceneGraphNode* circle = global::renderEngine->scene()->sceneGraphNode(id);
|
||||
module->setHoverCircle(circle);
|
||||
}
|
||||
/**
|
||||
* Moves the hover circle to the coordinate specified by the image index.
|
||||
* \param i Index of image
|
||||
*/
|
||||
[[codegen::luawrap]] void moveCircleToHoverImage(int i) {
|
||||
// Load image
|
||||
SkyBrowserModule* module = global::moduleEngine->module<SkyBrowserModule>();
|
||||
module->moveHoverCircle(i);
|
||||
}
|
||||
/**
|
||||
* Disables the hover circle, if there is one added to the sky browser
|
||||
* module.
|
||||
*/
|
||||
[[codegen::luawrap]] void disableHoverCircle() {
|
||||
SkyBrowserModule* module = global::moduleEngine->module<SkyBrowserModule>();
|
||||
module->disableHoverCircle();
|
||||
}
|
||||
/**
|
||||
* Takes an identifier to a sky browser or a sky target, an image index
|
||||
* and the order which it should have in the selected image list. The
|
||||
* image is then changed to have this order.
|
||||
* \param id Identifier
|
||||
* \param i Image index
|
||||
* \param order Order of image
|
||||
*/
|
||||
[[codegen::luawrap]] void setImageLayerOrder(std::string id, int i, int order) {
|
||||
|
||||
SkyBrowserModule* module = global::moduleEngine->module<SkyBrowserModule>();
|
||||
|
||||
if (module->getPair(id)) {
|
||||
module->getPair(id)->setImageOrder(i, order);
|
||||
}
|
||||
}
|
||||
/**
|
||||
* Takes an identifier to a sky browser or target and loads the WWT image
|
||||
* collection to that browser.
|
||||
* \param id Identifier
|
||||
*/
|
||||
[[codegen::luawrap]] void loadImagesToWWT(std::string id) {
|
||||
// Load images from url
|
||||
LINFO("Connection established to WorldWide Telescope application in " + id);
|
||||
LINFO("Loading image collections to " + id);
|
||||
|
||||
// Load the collections here because here we know that the browser can execute
|
||||
// javascript
|
||||
std::string root = "https://raw.githubusercontent.com/WorldWideTelescope/"
|
||||
"wwt-web-client/master/assets/webclient-explore-root.wtml";
|
||||
|
||||
SkyBrowserModule* module = global::moduleEngine->module<SkyBrowserModule>();
|
||||
|
||||
if (module->getPair(id)) {
|
||||
module->getPair(id)->hideChromeInterface(true);
|
||||
module->getPair(id)->loadImageCollection(root);
|
||||
}
|
||||
}
|
||||
/**
|
||||
* Starts the setup process of the sky browers. This function calls
|
||||
* the lua function 'sendOutIdsToBrowsers' in all nodes in the cluster.
|
||||
*/
|
||||
[[codegen::luawrap]] void startSetup() {
|
||||
// This is called when the sky_browser website is connected to OpenSpace
|
||||
// Set all border colors to the border color in the master node
|
||||
if (global::windowDelegate->isMaster()) {
|
||||
SkyBrowserModule* module = global::moduleEngine->module<SkyBrowserModule>();
|
||||
std::vector<std::unique_ptr<TargetBrowserPair>>& pairs = module->getPairs();
|
||||
for (std::unique_ptr<TargetBrowserPair>& pair : pairs) {
|
||||
std::string id = pair->browserId();
|
||||
glm::ivec3 color = pair->borderColor();
|
||||
std::string script = fmt::format(
|
||||
"openspace.skybrowser.setBorderColor('{}', {}, {}, {})",
|
||||
id,
|
||||
color.r,
|
||||
color.g,
|
||||
color.b
|
||||
);
|
||||
global::scriptEngine->queueScript(
|
||||
script,
|
||||
scripting::ScriptEngine::RemoteScripting::Yes
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
// To ensure each node in a cluster calls its own instance of the wwt application
|
||||
// Do not send this script to the other nodes
|
||||
global::scriptEngine->queueScript(
|
||||
"openspace.skybrowser.sendOutIdsToBrowsers();",
|
||||
scripting::ScriptEngine::RemoteScripting::No
|
||||
);
|
||||
}
|
||||
/**
|
||||
* Sends all sky browsers' identifiers to their respective CEF browser.
|
||||
*/
|
||||
[[codegen::luawrap]] void sendOutIdsToBrowsers() {
|
||||
// This is called when the sky_browser website is connected to OpenSpace
|
||||
// Send out identifiers to the browsers
|
||||
SkyBrowserModule* module = global::moduleEngine->module<SkyBrowserModule>();
|
||||
std::vector<std::unique_ptr<TargetBrowserPair>>& pairs = module->getPairs();
|
||||
for (std::unique_ptr<TargetBrowserPair>& pair : pairs) {
|
||||
pair->sendIdToBrowser();
|
||||
}
|
||||
}
|
||||
/**
|
||||
* Takes an identifier to a sky browser and starts the initialization
|
||||
* for that browser. That means that the browser starts to try to connect
|
||||
* to the AAS WorldWide Telescope application by sending it messages. And
|
||||
* that the target matches its appearance to its corresponding browser.
|
||||
* \param id Identifier
|
||||
*/
|
||||
[[codegen::luawrap]] void initializeBrowser(std::string id) {
|
||||
// Initialize browser with ID and its corresponding target
|
||||
LINFO("Initializing sky browser " + id);
|
||||
SkyBrowserModule* module = global::moduleEngine->module<SkyBrowserModule>();
|
||||
TargetBrowserPair* found = module->getPair(id);
|
||||
if (found) {
|
||||
found->setIsSyncedWithWwt(true);
|
||||
found->initialize();
|
||||
}
|
||||
}
|
||||
/**
|
||||
* Takes the identifier of the sky target and a sky browser and adds them
|
||||
* to the sky browser module.
|
||||
* \param targetId Identifier of target (either SceneGraphNode or Renderable)
|
||||
* \param browserId Identifier of browser
|
||||
*/
|
||||
[[codegen::luawrap]] void addPairToSkyBrowserModule(std::string targetId, std::string browserId) {
|
||||
SkyBrowserModule* module = global::moduleEngine->module<SkyBrowserModule>();
|
||||
|
||||
LINFO("Add browser " + browserId + " to sky browser module");
|
||||
LINFO("Add target " + targetId + " to sky browser module");
|
||||
|
||||
module->addTargetBrowserPair(targetId, browserId);
|
||||
}
|
||||
/**
|
||||
* Returns a list of all the loaded AAS WorldWide Telescope images that
|
||||
* have been loaded. Each image has a name, thumbnail url, equatorial
|
||||
* spherical coordinates RA and Dec, equatorial Cartesian coordinates,
|
||||
* if the image has celestial coordinates, credits text, credits url
|
||||
* and the identifier of the image which is a unique number.
|
||||
*/
|
||||
|
||||
[[codegen::luawrap]] ghoul::Dictionary getListOfImages() {
|
||||
// Send image list to GUI
|
||||
SkyBrowserModule* module = global::moduleEngine->module<SkyBrowserModule>();
|
||||
|
||||
// If no data has been loaded yet, download the data from the web!
|
||||
if (module->nLoadedImages() == 0) {
|
||||
std::string root = "https://raw.githubusercontent.com/WorldWideTelescope/"
|
||||
"wwt-web-client/master/assets/webclient-explore-root.wtml";
|
||||
|
||||
std::filesystem::path directory = absPath("${MODULE_SKYBROWSER}/wwtimagedata/");
|
||||
|
||||
module->loadImages(root, directory);
|
||||
}
|
||||
|
||||
// Create Lua table to send to the GUI
|
||||
ghoul::Dictionary list;
|
||||
|
||||
for (int i = 0; i < module->nLoadedImages(); i++) {
|
||||
const ImageData& img = module->getWwtDataHandler()->getImage(i);
|
||||
using namespace std::string_literals;
|
||||
|
||||
// Push ("Key", value)
|
||||
ghoul::Dictionary image;
|
||||
image.setValue("name", img.name);
|
||||
image.setValue("thumbnail", img.thumbnailUrl);
|
||||
image.setValue("url", img.imageUrl);
|
||||
image.setValue("ra", img.equatorialSpherical.x);
|
||||
image.setValue("dec", img.equatorialSpherical.y);
|
||||
image.setValue("fov", static_cast<double>(img.fov));
|
||||
image.setValue("cartesianDirection", img.equatorialCartesian);
|
||||
image.setValue("hasCelestialCoords", img.hasCelestialCoords);
|
||||
image.setValue("credits", img.credits);
|
||||
image.setValue("creditsUrl", img.creditsUrl);
|
||||
image.setValue("identifier", std::to_string(i));
|
||||
|
||||
// Index for current ImageData
|
||||
// Set table for the current ImageData
|
||||
list.setValue(std::to_string(i + 1), image);
|
||||
}
|
||||
|
||||
return list;
|
||||
}
|
||||
/**
|
||||
* Returns a table of data regarding the current view and the sky browsers
|
||||
* and targets.
|
||||
* \return Dictionary of data regarding the current targets
|
||||
*/
|
||||
[[codegen::luawrap]] ghoul::Dictionary getTargetData() {
|
||||
using namespace std::string_literals;
|
||||
SkyBrowserModule* module = global::moduleEngine->module<SkyBrowserModule>();
|
||||
ghoul::Dictionary data;
|
||||
|
||||
// The current viewport data for OpenSpace
|
||||
ghoul::Dictionary openSpace;
|
||||
|
||||
// Camera directions
|
||||
glm::dvec3 cartesianCam = skybrowser::cameraDirectionEquatorial();
|
||||
glm::dvec2 sphericalCam = skybrowser::cartesianToSpherical(cartesianCam);
|
||||
|
||||
// Calculate the smallest FOV of vertical and horizontal
|
||||
glm::dvec2 fovs = skybrowser::fovWindow();
|
||||
double FOV = std::min(fovs.x, fovs.y);
|
||||
|
||||
// Set window data
|
||||
openSpace.setValue("windowHFOV", FOV);
|
||||
openSpace.setValue("cartesianDirection", cartesianCam);
|
||||
openSpace.setValue("ra", sphericalCam.x);
|
||||
openSpace.setValue("dec", sphericalCam.y);
|
||||
openSpace.setValue("selectedBrowserId", module->selectedBrowserId());
|
||||
openSpace.setValue("selectedTargetId", module->selectedTargetId());
|
||||
openSpace.setValue("isFacingCamera", module->isSelectedPairFacingCamera());
|
||||
openSpace.setValue("isUsingRadiusAzimuthElevation", module->isSelectedPairUsingRae());
|
||||
openSpace.setValue("cameraInSolarSystem", module->isCameraInSolarSystem());
|
||||
// Set table for the current ImageData
|
||||
data.setValue("OpenSpace", openSpace);
|
||||
|
||||
// Pass data for all the browsers and the corresponding targets
|
||||
if (module->isCameraInSolarSystem()) {
|
||||
std::vector<std::unique_ptr<TargetBrowserPair>>& pairs = module->getPairs();
|
||||
|
||||
for (std::unique_ptr<TargetBrowserPair>& pair : pairs) {
|
||||
std::string id = pair->browserId();
|
||||
// Convert deque to vector so ghoul can read it
|
||||
std::vector<int> selectedImagesVector;
|
||||
const std::deque<int> selectedImages = pair->selectedImages();
|
||||
std::for_each(
|
||||
selectedImages.begin(),
|
||||
selectedImages.end(),
|
||||
[&](int i) {
|
||||
selectedImagesVector.push_back(i);
|
||||
}
|
||||
);
|
||||
|
||||
glm::dvec2 spherical = pair->targetDirectionEquatorial();
|
||||
glm::dvec3 cartesian = skybrowser::sphericalToCartesian(spherical);
|
||||
|
||||
ghoul::Dictionary target;
|
||||
// Set ("Key", value)
|
||||
target.setValue("id", id);
|
||||
target.setValue("name", pair->browserGuiName());
|
||||
target.setValue("FOV", static_cast<double>(pair->verticalFov()));
|
||||
target.setValue("selectedImages", selectedImagesVector);
|
||||
target.setValue("cartesianDirection", cartesian);
|
||||
target.setValue("ra", spherical.x);
|
||||
target.setValue("dec", spherical.y);
|
||||
target.setValue("roll", pair->targetRoll());
|
||||
target.setValue("color", pair->borderColor());
|
||||
target.setValue("size", glm::dvec2(pair->size()));
|
||||
std::vector<std::pair<std::string, glm::dvec3>> copies = pair->renderCopies();
|
||||
ghoul::Dictionary copiesData;
|
||||
for (int i = 0; i < copies.size(); i++) {
|
||||
copiesData.setValue(copies[i].first, copies[i].second);
|
||||
}
|
||||
// Set table for the current target
|
||||
target.setValue("renderCopies", copiesData);
|
||||
data.setValue(id, target);
|
||||
}
|
||||
}
|
||||
|
||||
return data;
|
||||
}
|
||||
/**
|
||||
* Takes an identifier to a sky browser or sky target. Rotates the camera
|
||||
* so that the target is placed in the center of the view.
|
||||
* \param id
|
||||
*/
|
||||
[[codegen::luawrap]] void adjustCamera(std::string id) {
|
||||
SkyBrowserModule* module = global::moduleEngine->module<SkyBrowserModule>();
|
||||
|
||||
if (module->isCameraInSolarSystem()) {
|
||||
module->lookAtTarget(id);
|
||||
}
|
||||
}
|
||||
/**
|
||||
* Takes an identifier to a sky browser or sky target, an index to an image
|
||||
* and a value for the opacity.
|
||||
* \param id Identifier
|
||||
* \param i Image index
|
||||
* \param opacity
|
||||
*/
|
||||
[[codegen::luawrap]] void setOpacityOfImageLayer(std::string id, int i, double opacity) {
|
||||
SkyBrowserModule* module = global::moduleEngine->module<SkyBrowserModule>();
|
||||
TargetBrowserPair* found = module->getPair(id);
|
||||
if (found) {
|
||||
found->setImageOpacity(i, opacity);
|
||||
}
|
||||
}
|
||||
/**
|
||||
* Takes an identifier to a sky browser and animates its corresponding
|
||||
* target to the center of the current view.
|
||||
* \param id Identifier
|
||||
*/
|
||||
[[codegen::luawrap]] void centerTargetOnScreen(std::string id) {
|
||||
SkyBrowserModule* module = global::moduleEngine->module<SkyBrowserModule>();
|
||||
TargetBrowserPair* pair = module->getPair(id);
|
||||
if (pair) {
|
||||
pair->centerTargetOnScreen();
|
||||
}
|
||||
}
|
||||
/**
|
||||
* Takes an identifier to a sky browser or target. Sets that sky browser
|
||||
* currently selected.
|
||||
* \param id
|
||||
*/
|
||||
[[codegen::luawrap]] void setSelectedBrowser(std::string id) {
|
||||
SkyBrowserModule* module = global::moduleEngine->module<SkyBrowserModule>();
|
||||
module->setSelectedBrowser(id);
|
||||
}
|
||||
/**
|
||||
* Creates a sky browser and a target.
|
||||
*/
|
||||
[[codegen::luawrap]] void createTargetBrowserPair() {
|
||||
SkyBrowserModule* module = global::moduleEngine->module<SkyBrowserModule>();
|
||||
|
||||
int noOfPairs = module->nPairs();
|
||||
std::string nameBrowser = "Sky Browser " + std::to_string(noOfPairs);
|
||||
std::string nameTarget = "Sky Target " + std::to_string(noOfPairs);
|
||||
std::string idBrowser = "SkyBrowser" + std::to_string(noOfPairs);
|
||||
std::string idTarget = "SkyTarget" + std::to_string(noOfPairs);
|
||||
// Determine starting point on screen for the target
|
||||
glm::vec3 positionBrowser = { -3.f, -3.f, -2.1f };
|
||||
glm::vec3 positionTarget = { 0.9f, 0.4f, -2.1f };
|
||||
glm::dvec3 galacticTarget = skybrowser::localCameraToGalactic(positionTarget);
|
||||
std::string guiPath = "/Sky Browser";
|
||||
std::string url = "https://data.openspaceproject.com/dist/skybrowser/page/";
|
||||
double fov = 70.0;
|
||||
double size = skybrowser::sizeFromFov(fov, galacticTarget);
|
||||
|
||||
const std::string browser = "{"
|
||||
"Identifier = '" + idBrowser + "',"
|
||||
"Type = 'ScreenSpaceSkyBrowser',"
|
||||
"Name = '" + nameBrowser + "',"
|
||||
"Url = '" + url + "',"
|
||||
"FaceCamera = false,"
|
||||
"CartesianPosition = " + ghoul::to_string(positionBrowser) +
|
||||
"}";
|
||||
|
||||
const std::string target = "{"
|
||||
"Identifier = '" + idTarget + "',"
|
||||
"Type = 'SkyTarget',"
|
||||
"Name = '" + nameTarget + "',"
|
||||
"Transform = {"
|
||||
"Translation = {"
|
||||
"Type = 'StaticTranslation',"
|
||||
"Position = {"
|
||||
+ std::to_string(galacticTarget.x) + ", "
|
||||
+ std::to_string(galacticTarget.y) + ", "
|
||||
+ std::to_string(galacticTarget.z) + ", "
|
||||
"},"
|
||||
"},"
|
||||
"Rotation = {"
|
||||
"Type = 'StaticRotation',"
|
||||
"Rotation = {0.0, 0.0, 0.0}"
|
||||
"}"
|
||||
"},"
|
||||
"Renderable = {"
|
||||
"Identifier = 'RenderableSkyTarget',"
|
||||
"Type = 'RenderableSkyTarget',"
|
||||
"Size = " + std::to_string(size) + ","
|
||||
"VerticalFieldOfView = " + std::to_string(fov) + ","
|
||||
"Origin = 'Center',"
|
||||
"Billboard = true,"
|
||||
"Opacity = 0.99"
|
||||
"},"
|
||||
"GUI = {"
|
||||
"Name = 'Sky Target', "
|
||||
"Path = '/SkyBrowser', "
|
||||
"}"
|
||||
"}";
|
||||
|
||||
global::scriptEngine->queueScript(
|
||||
"openspace.addScreenSpaceRenderable(" + browser + ");",
|
||||
scripting::ScriptEngine::RemoteScripting::No
|
||||
);
|
||||
|
||||
global::scriptEngine->queueScript(
|
||||
"openspace.addSceneGraphNode(" + target + ");",
|
||||
scripting::ScriptEngine::RemoteScripting::No
|
||||
);
|
||||
|
||||
global::scriptEngine->queueScript(
|
||||
"openspace.skybrowser.addPairToSkyBrowserModule('" + idTarget + "','"
|
||||
+ idBrowser + "');",
|
||||
scripting::ScriptEngine::RemoteScripting::No
|
||||
);
|
||||
|
||||
global::scriptEngine->queueScript(
|
||||
"openspace.skybrowser.setSelectedBrowser('" + idBrowser + "');",
|
||||
scripting::ScriptEngine::RemoteScripting::No
|
||||
);
|
||||
}
|
||||
/**
|
||||
* Takes in identifier to a sky browser or target and removes them.
|
||||
* \param id Identifier
|
||||
*/
|
||||
[[codegen::luawrap]] void removeTargetBrowserPair(std::string id) {
|
||||
SkyBrowserModule* module = global::moduleEngine->module<SkyBrowserModule>();
|
||||
TargetBrowserPair* found = module->getPair(id);
|
||||
if (found) {
|
||||
std::string browser = found->browserId();
|
||||
std::string target = found->targetNodeId();
|
||||
|
||||
module->removeTargetBrowserPair(id);
|
||||
|
||||
// Remove from engine
|
||||
global::scriptEngine->queueScript(
|
||||
"openspace.removeScreenSpaceRenderable('" + browser + "');",
|
||||
scripting::ScriptEngine::RemoteScripting::Yes
|
||||
);
|
||||
|
||||
global::scriptEngine->queueScript(
|
||||
"openspace.removeSceneGraphNode('" + target + "');",
|
||||
scripting::ScriptEngine::RemoteScripting::Yes
|
||||
);
|
||||
}
|
||||
}
|
||||
/**
|
||||
* Takes an identifier to a sky browser or sky target and the [x, y]
|
||||
* starting position and the [x, y] translation vector.
|
||||
* \param id Identifier
|
||||
* \param startX Starting x-position
|
||||
* \param startY Starting y-position
|
||||
* \param transX Translation x-value
|
||||
* \param transY Translation y-value
|
||||
*/
|
||||
[[codegen::luawrap]] void translateScreenSpaceRenderable(std::string id, float startX,
|
||||
float startY, float transX,
|
||||
float transY) {
|
||||
ScreenSpaceRenderable* renderable = global::renderEngine->screenSpaceRenderable(id);
|
||||
|
||||
if (renderable) {
|
||||
renderable->translate(glm::vec2(transX, transY), glm::vec2(startX, startY));
|
||||
}
|
||||
}
|
||||
/**
|
||||
* Takes an identifier to a sky browser or target and an index to an
|
||||
* image. Removes that image from that sky browser.
|
||||
* \param id Identifier
|
||||
* \i Index of image
|
||||
*/
|
||||
[[codegen::luawrap]] void removeSelectedImageInBrowser(std::string id, int i) {
|
||||
// Get browser
|
||||
SkyBrowserModule* module = global::moduleEngine->module<SkyBrowserModule>();
|
||||
const ImageData& image = module->getWwtDataHandler()->getImage(i);
|
||||
|
||||
TargetBrowserPair* pair = module->getPair(id);
|
||||
if (pair) {
|
||||
pair->removeSelectedImage(i);
|
||||
}
|
||||
}
|
||||
/**
|
||||
* Takes the identifier of a sky browser or a sky target and equatorial
|
||||
* coordinates Right Ascension and Declination. The target will animate to
|
||||
* this coordinate and the browser will display the coordinate.
|
||||
* \param id Identifier
|
||||
* \param ra Right Ascension
|
||||
* \param dec Declination
|
||||
*/
|
||||
[[codegen::luawrap]] void setEquatorialAim(std::string id, double ra, double dec) {
|
||||
// Get module
|
||||
SkyBrowserModule* module = global::moduleEngine->module<SkyBrowserModule>();
|
||||
|
||||
TargetBrowserPair* pair = module->getPair(id);
|
||||
if (pair) {
|
||||
pair->setEquatorialAim(glm::dvec2(ra, dec));
|
||||
}
|
||||
}
|
||||
/**
|
||||
* Takes an identifier to a sky browser or a sky target and a vertical
|
||||
* field of view. Changes the field of view as specified by the input.
|
||||
* \param id Identifier
|
||||
* \param vfov Vertical Field of View
|
||||
*/
|
||||
[[codegen::luawrap]] void setVerticalFov(std::string id, float vfov) {
|
||||
// Get module
|
||||
SkyBrowserModule* module = global::moduleEngine->module<SkyBrowserModule>();
|
||||
|
||||
TargetBrowserPair* pair = module->getPair(id);
|
||||
if (pair) {
|
||||
pair->setVerticalFov(vfov);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Takes an identifier to a sky browser or a sky target and a vertical
|
||||
* field of view. Changes the field of view as specified by the input.
|
||||
* \param id Identifier
|
||||
* \param vfov Vertical Field of View
|
||||
*/
|
||||
[[codegen::luawrap]] void scrollOverBrowser(std::string id, float scroll) {
|
||||
// Get module
|
||||
SkyBrowserModule* module = global::moduleEngine->module<SkyBrowserModule>();
|
||||
|
||||
TargetBrowserPair* pair = module->getPair(id);
|
||||
if (pair) {
|
||||
pair->setVerticalFovWithScroll(scroll);
|
||||
}
|
||||
}
|
||||
/**
|
||||
* Takes an identifier to a sky browser or a sky target and a rgb color
|
||||
* in the ranges [0, 255].
|
||||
* \param id Identifier
|
||||
* \param r Red
|
||||
* \param g Green
|
||||
* \param b Blue
|
||||
*/
|
||||
[[codegen::luawrap]] void setBorderColor(std::string id, int r, int g, int b) {
|
||||
glm::ivec3 color{ r, g, b };
|
||||
// Get module
|
||||
SkyBrowserModule* module = global::moduleEngine->module<SkyBrowserModule>();
|
||||
|
||||
TargetBrowserPair* pair = module->getPair(id);
|
||||
if (pair) {
|
||||
pair->setBorderColor(color);
|
||||
}
|
||||
}
|
||||
/**
|
||||
* Sets the screen space size of the sky browser to the numbers specified
|
||||
* by the input [x, y].
|
||||
* \param id
|
||||
* \param sizeX Size on the x-axis
|
||||
* \param sizeY Size on the y-axis
|
||||
*/
|
||||
[[codegen::luawrap]] void setScreenSpaceSize(std::string id, float sizeX, float sizeY) {
|
||||
// Get module
|
||||
SkyBrowserModule* module = global::moduleEngine->module<SkyBrowserModule>();
|
||||
|
||||
TargetBrowserPair* pair = module->getPair(id);
|
||||
if (pair) {
|
||||
pair->setScreenSpaceSize(glm::vec2(sizeX, sizeY));
|
||||
}
|
||||
}
|
||||
/**
|
||||
* Takes an identifier to a sky browser and adds a rendered copy to it.
|
||||
* \param id Identifier
|
||||
* \param raePosition Position in radius, azimuth, elevation coordinates
|
||||
* \param nCopies Number of copies
|
||||
*/
|
||||
[[codegen::luawrap]] void addRenderCopy(std::string id,
|
||||
int nCopies = 1, glm::vec3 raePosition = glm::vec3(2.1f, 0.f, 0.f)) {
|
||||
// Get module
|
||||
SkyBrowserModule* module = global::moduleEngine->module<SkyBrowserModule>();
|
||||
|
||||
TargetBrowserPair* pair = module->getPair(id);
|
||||
if (pair) {
|
||||
pair->browser()->addRenderCopy(raePosition, nCopies);
|
||||
}
|
||||
}
|
||||
/**
|
||||
* Takes an identifier to a sky browser and removes the latest added
|
||||
* rendered copy to it.
|
||||
* \param id Identifier
|
||||
*/
|
||||
[[codegen::luawrap]] void removeRenderCopy(std::string id) {
|
||||
// Get module
|
||||
SkyBrowserModule* module = global::moduleEngine->module<SkyBrowserModule>();
|
||||
|
||||
TargetBrowserPair* pair = module->getPair(id);
|
||||
if (pair) {
|
||||
pair->browser()->removeRenderCopy();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Starts the finetuning of the target
|
||||
* rendered copy to it.
|
||||
* \param id Identifier to browser
|
||||
*/
|
||||
[[codegen::luawrap]] void startFinetuningTarget(std::string id) {
|
||||
// Get module
|
||||
|
||||
SkyBrowserModule* module = global::moduleEngine->module<SkyBrowserModule>();
|
||||
TargetBrowserPair* pair = module->getPair(id);
|
||||
if (pair) {
|
||||
pair->startFinetuningTarget();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Finetunes the target depending on a mouse drag.
|
||||
* rendered copy to it.
|
||||
* \param id Identifier to browser
|
||||
* \param start Start pixel position of drag
|
||||
* \param end Current pixel position of mouse during drag
|
||||
*/
|
||||
[[codegen::luawrap]] void finetuneTargetPosition(std::string id, glm::vec2 start,
|
||||
glm::vec2 end) {
|
||||
// Get module
|
||||
|
||||
SkyBrowserModule* module = global::moduleEngine->module<SkyBrowserModule>();
|
||||
TargetBrowserPair* pair = module->getPair(id);
|
||||
if (pair) {
|
||||
glm::vec2 startScreenSpace = skybrowser::pixelToScreenSpace2d(start);
|
||||
glm::vec2 endScreenSpace = skybrowser::pixelToScreenSpace2d(end);
|
||||
glm::vec2 translation = endScreenSpace - startScreenSpace;
|
||||
pair->fineTuneTarget(startScreenSpace, translation);
|
||||
}
|
||||
}
|
||||
|
||||
#include "skybrowsermodule_lua_codegen.cpp"
|
||||
|
||||
} // namespace
|
||||
|
||||
219
modules/skybrowser/src/browser.cpp
Normal file
219
modules/skybrowser/src/browser.cpp
Normal file
@@ -0,0 +1,219 @@
|
||||
/*****************************************************************************************
|
||||
* *
|
||||
* OpenSpace *
|
||||
* *
|
||||
* Copyright (c) 2014-2022 *
|
||||
* *
|
||||
* 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 <modules/skybrowser/include/browser.h>
|
||||
|
||||
#include <modules/webbrowser/webbrowsermodule.h>
|
||||
#include <modules/webbrowser/include/webkeyboardhandler.h>
|
||||
#include <modules/webbrowser/include/browserinstance.h>
|
||||
#include <openspace/engine/windowdelegate.h>
|
||||
#include <openspace/engine/globals.h>
|
||||
#include <openspace/engine/moduleengine.h>
|
||||
#include <ghoul/opengl/texture.h>
|
||||
#include <ghoul/logging/logmanager.h>
|
||||
|
||||
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"
|
||||
};
|
||||
|
||||
struct [[codegen::Dictionary(Browser)]] Parameters {
|
||||
// [[codegen::verbatim(DimensionsInfo.description)]]
|
||||
std::optional<glm::vec2> dimensions;
|
||||
|
||||
// [[codegen::verbatim(UrlInfo.description)]]
|
||||
std::optional<std::string> url;
|
||||
|
||||
// [[codegen::verbatim(ReloadInfo.description)]]
|
||||
std::optional<bool> reload;
|
||||
};
|
||||
|
||||
#include "browser_codegen.cpp"
|
||||
|
||||
} // 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)
|
||||
, _browserPixeldimensions(DimensionsInfo, glm::vec2(500.f), glm::vec2(10.f), glm::vec2(3000.f))
|
||||
, _reload(ReloadInfo)
|
||||
{
|
||||
if (dictionary.hasValue<std::string>(UrlInfo.identifier)) {
|
||||
_url = dictionary.value<std::string>(UrlInfo.identifier);
|
||||
}
|
||||
// Handle target dimension property
|
||||
const Parameters p = codegen::bake<Parameters>(dictionary);
|
||||
_url = p.url.value_or(_url);
|
||||
_browserPixeldimensions = p.dimensions.value_or(_browserPixeldimensions);
|
||||
|
||||
glm::vec2 windowDimensions = global::windowDelegate->currentSubwindowSize();
|
||||
_browserPixeldimensions = windowDimensions;
|
||||
|
||||
_url.onChange([this]() { _isUrlDirty = true; });
|
||||
_browserPixeldimensions.onChange([this]() { _isDimensionsDirty = true; });
|
||||
_reload.onChange([this]() { _shouldReload = true; });
|
||||
|
||||
// Create browser and render handler
|
||||
_renderHandler = new RenderHandler();
|
||||
_keyboardHandler = new WebKeyboardHandler();
|
||||
_browserInstance = std::make_unique<BrowserInstance>(
|
||||
_renderHandler.get(),
|
||||
_keyboardHandler.get()
|
||||
);
|
||||
|
||||
WebBrowserModule* webBrowser = global::moduleEngine->module<WebBrowserModule>();
|
||||
if (webBrowser) {
|
||||
webBrowser->addBrowser(_browserInstance.get());
|
||||
}
|
||||
}
|
||||
|
||||
Browser::~Browser() {}
|
||||
|
||||
bool Browser::initializeGL() {
|
||||
_texture = std::make_unique<ghoul::opengl::Texture>(
|
||||
glm::uvec3(glm::ivec2(_browserPixeldimensions.value()), 1),
|
||||
GL_TEXTURE_2D
|
||||
);
|
||||
|
||||
_renderHandler->setTexture(*_texture);
|
||||
|
||||
_browserInstance->initialize();
|
||||
_browserInstance->loadUrl(_url);
|
||||
return isReady();
|
||||
}
|
||||
|
||||
bool Browser::deinitializeGL() {
|
||||
_renderHandler->setTexture(0);
|
||||
|
||||
_texture = nullptr;
|
||||
|
||||
LDEBUG(fmt::format("Deinitializing browser: {}", _url.value()));
|
||||
|
||||
_browserInstance->close(true);
|
||||
|
||||
WebBrowserModule* webBrowser = global::moduleEngine->module<WebBrowserModule>();
|
||||
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) {
|
||||
if (_browserPixeldimensions.value().x > 0 &&
|
||||
_browserPixeldimensions.value().y > 0)
|
||||
{
|
||||
_browserInstance->reshape(_browserPixeldimensions.value());
|
||||
_isDimensionsDirty = false;
|
||||
}
|
||||
}
|
||||
|
||||
if (_shouldReload) {
|
||||
_browserInstance->reloadBrowser();
|
||||
_shouldReload = false;
|
||||
}
|
||||
}
|
||||
|
||||
bool Browser::isReady() const {
|
||||
return _texture.get();
|
||||
}
|
||||
|
||||
glm::vec2 Browser::browserPixelDimensions() const {
|
||||
return _browserPixeldimensions.value();
|
||||
}
|
||||
|
||||
// Updates the browser size to match the size of the texture
|
||||
void Browser::updateBrowserSize() {
|
||||
_browserPixeldimensions = _texture->dimensions();
|
||||
}
|
||||
|
||||
float Browser::browserRatio() const {
|
||||
return static_cast<float>(_texture->dimensions().x) /
|
||||
static_cast<float>(_texture->dimensions().y);
|
||||
}
|
||||
|
||||
void Browser::setCallbackDimensions(
|
||||
const std::function<void(const glm::dvec2&)>& function)
|
||||
{
|
||||
_browserPixeldimensions.onChange([&]() {
|
||||
function(_browserPixeldimensions.value());
|
||||
});
|
||||
}
|
||||
|
||||
void Browser::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) {
|
||||
_browserInstance->getBrowser()->GetMainFrame()->ExecuteJavaScript(
|
||||
script,
|
||||
_browserInstance->getBrowser()->GetMainFrame()->GetURL(),
|
||||
0
|
||||
);
|
||||
}
|
||||
}
|
||||
} // namespace openspace
|
||||
277
modules/skybrowser/src/renderableskytarget.cpp
Normal file
277
modules/skybrowser/src/renderableskytarget.cpp
Normal file
@@ -0,0 +1,277 @@
|
||||
/*****************************************************************************************
|
||||
* *
|
||||
* OpenSpace *
|
||||
* *
|
||||
* Copyright (c) 2014-2022 *
|
||||
* *
|
||||
* 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 <modules/skybrowser/include/renderableskytarget.h>
|
||||
|
||||
#include <modules/skybrowser/skybrowsermodule.h>
|
||||
#include <modules/skybrowser/include/utility.h>
|
||||
#include <modules/base/basemodule.h>
|
||||
#include <openspace/camera/camera.h>
|
||||
#include <openspace/engine/globals.h>
|
||||
#include <openspace/engine/moduleengine.h>
|
||||
#include <openspace/navigation/navigationhandler.h>
|
||||
#include <openspace/rendering/renderengine.h>
|
||||
#include <openspace/rendering/helper.h>
|
||||
#include <openspace/scene/scene.h>
|
||||
#include <openspace/util/updatestructures.h>
|
||||
#include <openspace/scripting/scriptengine.h>
|
||||
#include <ghoul/filesystem/filesystem.h>
|
||||
#include <ghoul/opengl/programobject.h>
|
||||
#include <ghoul/opengl/texture.h>
|
||||
#include <ghoul/opengl/textureunit.h>
|
||||
|
||||
#pragma optimize("", off)
|
||||
namespace {
|
||||
constexpr const char* _loggerCat = "RenderableSkyTarget";
|
||||
|
||||
enum BlendMode {
|
||||
Normal = 0,
|
||||
Additive
|
||||
};
|
||||
|
||||
constexpr const openspace::properties::Property::PropertyInfo crossHairSizeInfo =
|
||||
{
|
||||
"CrosshairSize",
|
||||
"Crosshair Size",
|
||||
"Determines the size of the crosshair. The size is determined in fov (degrees). "
|
||||
};
|
||||
|
||||
constexpr const openspace::properties::Property::PropertyInfo RectangleThresholdInfo =
|
||||
{
|
||||
"RectangleThreshold",
|
||||
"Rectangle Threshold",
|
||||
"When the field of view is larger than the rectangle threshold, a rectangle will"
|
||||
"be rendered in the target."
|
||||
};
|
||||
|
||||
constexpr const openspace::properties::Property::PropertyInfo AnimationSpeedInfo = {
|
||||
"AnimationSpeed",
|
||||
"Animation Speed",
|
||||
"The factor which is multiplied with the animation speed of the target."
|
||||
};
|
||||
|
||||
constexpr const openspace::properties::Property::PropertyInfo AnimationThresholdInfo =
|
||||
{
|
||||
"AnimationThreshold",
|
||||
"Animation Threshold",
|
||||
"The threshold for when the target is determined to have appeared at its "
|
||||
"destination. Angle in radians between the destination and the target position "
|
||||
"in equatorial Cartesian coordinate system."
|
||||
};
|
||||
|
||||
constexpr const openspace::properties::Property::PropertyInfo LineWidthInfo = {
|
||||
"LineWidth",
|
||||
"Line Width",
|
||||
"The thickness of the line of the target. The larger number, the thicker line."
|
||||
};
|
||||
|
||||
struct [[codegen::Dictionary(RenderableSkyTarget)]] Parameters {
|
||||
// [[codegen::verbatim(crossHairSizeInfo.description)]]
|
||||
std::optional<float> crossHairSize;
|
||||
|
||||
// [[codegen::verbatim(RectangleThresholdInfo.description)]]
|
||||
std::optional<float> rectangleThreshold;
|
||||
|
||||
// [[codegen::verbatim(AnimationSpeedInfo.description)]]
|
||||
std::optional<double> animationSpeed;
|
||||
|
||||
// [[codegen::verbatim(AnimationThresholdInfo.description)]]
|
||||
std::optional<float> animationThreshold;
|
||||
|
||||
// [[codegen::verbatim(LineWidthInfo.description)]]
|
||||
std::optional<float> lineWidth;
|
||||
};
|
||||
|
||||
#include "renderableskytarget_codegen.cpp"
|
||||
} //namespace
|
||||
|
||||
namespace openspace {
|
||||
RenderableSkyTarget::RenderableSkyTarget(const ghoul::Dictionary& dictionary)
|
||||
: RenderablePlane(dictionary)
|
||||
, _crossHairSize(crossHairSizeInfo, 2.f, 1.f, 10.f)
|
||||
, _showRectangleThreshold(RectangleThresholdInfo, 5.f, 0.1f, 70.f)
|
||||
, _stopAnimationThreshold(AnimationThresholdInfo, 5.0f, 1.f, 10.f)
|
||||
, _animationSpeed(AnimationSpeedInfo, 5.0, 0.1, 10.0)
|
||||
, _lineWidth(LineWidthInfo, 13.f, 1.f, 100.f)
|
||||
, _borderColor(220, 220, 220)
|
||||
{
|
||||
// Handle target dimension property
|
||||
const Parameters p = codegen::bake<Parameters>(dictionary);
|
||||
_crossHairSize = p.crossHairSize.value_or(_crossHairSize);
|
||||
_showRectangleThreshold = p.rectangleThreshold.value_or(_showRectangleThreshold);
|
||||
_stopAnimationThreshold = p.crossHairSize.value_or(_stopAnimationThreshold);
|
||||
_animationSpeed = p.animationSpeed.value_or(_animationSpeed);
|
||||
|
||||
addProperty(_crossHairSize);
|
||||
addProperty(_showRectangleThreshold);
|
||||
addProperty(_stopAnimationThreshold);
|
||||
addProperty(_animationSpeed);
|
||||
addProperty(_lineWidth);
|
||||
}
|
||||
|
||||
void RenderableSkyTarget::bindTexture() {}
|
||||
|
||||
void RenderableSkyTarget::initializeGL() {
|
||||
glGenVertexArrays(1, &_quad); // generate array
|
||||
glGenBuffers(1, &_vertexPositionBuffer); // generate buffer
|
||||
createPlane();
|
||||
|
||||
std::string ProgramName = identifier() + "Shader";
|
||||
|
||||
_shader = BaseModule::ProgramObjectManager.request(
|
||||
ProgramName,
|
||||
[&]() -> std::unique_ptr<ghoul::opengl::ProgramObject> {
|
||||
return global::renderEngine->buildRenderProgram(
|
||||
ProgramName,
|
||||
absPath("${MODULE_SKYBROWSER}/shaders/target_vs.glsl"),
|
||||
absPath("${MODULE_SKYBROWSER}/shaders/target_fs.glsl")
|
||||
);
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
void RenderableSkyTarget::deinitializeGL() {
|
||||
RenderablePlane::deinitializeGL();
|
||||
}
|
||||
|
||||
void RenderableSkyTarget::setColor(glm::ivec3 color) {
|
||||
_borderColor = std::move(color);
|
||||
}
|
||||
|
||||
glm::ivec3 RenderableSkyTarget::borderColor() const {
|
||||
return _borderColor;
|
||||
}
|
||||
|
||||
|
||||
void RenderableSkyTarget::render(const RenderData& data, RendererTasks&) {
|
||||
ZoneScoped
|
||||
bool showRectangle = _verticalFov > _showRectangleThreshold;
|
||||
|
||||
glm::vec4 color = { glm::vec3(_borderColor) / 255.f, _opacity.value() };
|
||||
|
||||
_shader->activate();
|
||||
_shader->setUniform("opacity", _opacity);
|
||||
|
||||
_shader->setUniform("crossHairSize", _crossHairSize);
|
||||
_shader->setUniform("showRectangle", showRectangle);
|
||||
_shader->setUniform("lineWidth", _lineWidth * 0.0001f);
|
||||
_shader->setUniform("dimensions", _dimensions);
|
||||
_shader->setUniform("lineColor", color);
|
||||
_shader->setUniform("fov", static_cast<float>(_verticalFov));
|
||||
|
||||
glm::dvec3 objectPositionWorld = glm::dvec3(
|
||||
glm::translate(
|
||||
glm::dmat4(1.0),
|
||||
data.modelTransform.translation) * glm::dvec4(0.0, 0.0, 0.0, 1.0)
|
||||
);
|
||||
|
||||
glm::dvec3 normal = glm::normalize(data.camera.positionVec3() - objectPositionWorld);
|
||||
glm::dvec3 newRight = glm::normalize(
|
||||
glm::cross(data.camera.lookUpVectorWorldSpace(), normal)
|
||||
);
|
||||
glm::dvec3 newUp = glm::cross(normal, newRight);
|
||||
|
||||
glm::dmat4 cameraOrientedRotation = glm::dmat4(1.0);
|
||||
cameraOrientedRotation[0] = glm::dvec4(newRight, 0.0);
|
||||
cameraOrientedRotation[1] = glm::dvec4(newUp, 0.0);
|
||||
cameraOrientedRotation[2] = glm::dvec4(normal, 0.0);
|
||||
|
||||
const glm::dmat4 rotationTransform = _billboard ?
|
||||
cameraOrientedRotation :
|
||||
glm::dmat4(data.modelTransform.rotation);
|
||||
|
||||
const glm::dmat4 modelTransform =
|
||||
glm::translate(glm::dmat4(1.0), data.modelTransform.translation) *
|
||||
rotationTransform *
|
||||
glm::scale(glm::dmat4(1.0), glm::dvec3(data.modelTransform.scale)) *
|
||||
glm::dmat4(1.0);
|
||||
const glm::dmat4 modelViewTransform =
|
||||
data.camera.combinedViewMatrix() * modelTransform;
|
||||
|
||||
_shader->setUniform("modelViewProjectionTransform",
|
||||
data.camera.projectionMatrix() * glm::mat4(modelViewTransform));
|
||||
|
||||
_shader->setUniform("modelViewTransform",
|
||||
glm::mat4(data.camera.combinedViewMatrix() * glm::dmat4(modelViewTransform)));
|
||||
|
||||
_shader->setUniform("multiplyColor", _multiplyColor);
|
||||
|
||||
bool additiveBlending = (_blendMode == static_cast<int>(BlendMode::Additive));
|
||||
if (additiveBlending) {
|
||||
glDepthMask(false);
|
||||
glBlendFunc(GL_SRC_ALPHA, GL_ONE);
|
||||
}
|
||||
|
||||
glBindVertexArray(_quad);
|
||||
glEnable(GL_LINE_SMOOTH);
|
||||
glDrawArrays(GL_TRIANGLES, 0, 6);
|
||||
glDisable(GL_LINE_SMOOTH);
|
||||
glBindVertexArray(0);
|
||||
|
||||
if (additiveBlending) {
|
||||
glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
|
||||
glDepthMask(true);
|
||||
}
|
||||
|
||||
_shader->deactivate();
|
||||
}
|
||||
|
||||
void RenderableSkyTarget::update(const UpdateData& data) {
|
||||
RenderablePlane::update(data);
|
||||
|
||||
}
|
||||
|
||||
void RenderableSkyTarget::setDimensions(glm::vec2 dimensions) {
|
||||
// To avoid flooring of the size of the target, multiply by factor of 100
|
||||
// Object size is really the pixel size so this calculation is not exact
|
||||
_dimensions = glm::ivec2(dimensions * 100.f);
|
||||
}
|
||||
|
||||
void RenderableSkyTarget::highlight(const glm::ivec3& addition) {
|
||||
_borderColor += addition;
|
||||
}
|
||||
|
||||
void RenderableSkyTarget::removeHighlight(const glm::ivec3& removal) {
|
||||
_borderColor -= removal;
|
||||
}
|
||||
|
||||
float RenderableSkyTarget::opacity() const {
|
||||
return _opacity;
|
||||
}
|
||||
|
||||
double RenderableSkyTarget::animationSpeed() const {
|
||||
return _animationSpeed;
|
||||
}
|
||||
|
||||
double RenderableSkyTarget::stopAnimationThreshold() const {
|
||||
return _stopAnimationThreshold * 0.0001;
|
||||
}
|
||||
|
||||
void RenderableSkyTarget::setOpacity(float opacity) {
|
||||
_opacity = opacity;
|
||||
}
|
||||
|
||||
void RenderableSkyTarget::setVerticalFov(double fov) {
|
||||
_verticalFov = fov;
|
||||
}
|
||||
} // namespace openspace
|
||||
338
modules/skybrowser/src/screenspaceskybrowser.cpp
Normal file
338
modules/skybrowser/src/screenspaceskybrowser.cpp
Normal file
@@ -0,0 +1,338 @@
|
||||
/*****************************************************************************************
|
||||
* *
|
||||
* OpenSpace *
|
||||
* *
|
||||
* Copyright (c) 2014-2022 *
|
||||
* *
|
||||
* 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 <modules/skybrowser/include/screenspaceskybrowser.h>
|
||||
|
||||
#include <modules/skybrowser/skybrowsermodule.h>
|
||||
#include <modules/skybrowser/include/utility.h>
|
||||
#include <openspace/engine/globals.h>
|
||||
#include <openspace/engine/windowdelegate.h>
|
||||
#include <openspace/engine/moduleengine.h>
|
||||
#include <openspace/rendering/renderengine.h>
|
||||
#include <ghoul/logging/logmanager.h>
|
||||
#include <ghoul/misc/dictionaryjsonformatter.h>
|
||||
#include <ghoul/opengl/texture.h>
|
||||
#include <optional>
|
||||
#include <glm/gtx/color_space.hpp>
|
||||
#include <random>
|
||||
|
||||
namespace {
|
||||
constexpr const char* _loggerCat = "ScreenSpaceSkyBrowser";
|
||||
|
||||
constexpr const 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."
|
||||
};
|
||||
|
||||
constexpr const openspace::properties::Property::PropertyInfo RenderCopyInfo = {
|
||||
"RenderCopy",
|
||||
"RAE Position Of A Copy Of The Sky Browser",
|
||||
"Render 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."
|
||||
};
|
||||
|
||||
constexpr const openspace::properties::Property::PropertyInfo RenderOnMasterInfo = {
|
||||
"RenderOnlyOnMaster",
|
||||
"Render Only On Master",
|
||||
"Render the interactive sky browser only on the master node (this setting won't "
|
||||
"affect the copies). This setting allows mouse interactions in a dome environment."
|
||||
};
|
||||
|
||||
struct [[codegen::Dictionary(ScreenSpaceSkyBrowser)]] Parameters {
|
||||
// [[codegen::verbatim(TextureQualityInfo.description)]]
|
||||
std::optional<float> textureQuality;
|
||||
|
||||
// [[codegen::verbatim(RenderOnMasterInfo.description)]]
|
||||
std::optional<bool> renderOnlyOnMaster;
|
||||
};
|
||||
|
||||
#include "screenspaceskybrowser_codegen.cpp"
|
||||
} // namespace
|
||||
|
||||
glm::ivec3 randomBorderColor(glm::ivec3 highlight) {
|
||||
// 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<float> 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::ivec3 rgbColor;
|
||||
glm::ivec3 highlighted;
|
||||
do {
|
||||
glm::vec3 hsvColor = glm::vec3(hue(rd), saturation, value);
|
||||
rgbColor = glm::ivec3(glm::rgbColor(hsvColor) * 255.f);
|
||||
highlighted = rgbColor + highlight;
|
||||
} while (highlighted.x < 255 && highlighted.y < 255 && highlighted.z < 255);
|
||||
|
||||
return rgbColor;
|
||||
}
|
||||
|
||||
namespace openspace {
|
||||
|
||||
ScreenSpaceSkyBrowser::ScreenSpaceSkyBrowser(const ghoul::Dictionary& dictionary)
|
||||
: ScreenSpaceRenderable(dictionary)
|
||||
, WwtCommunicator(dictionary)
|
||||
, _textureQuality(TextureQualityInfo, 0.5f, 0.25f, 1.f)
|
||||
, _renderOnlyOnMaster(RenderOnMasterInfo, false)
|
||||
{
|
||||
_identifier = makeUniqueIdentifier(_identifier);
|
||||
|
||||
// Handle target dimension property
|
||||
const Parameters p = codegen::bake<Parameters>(dictionary);
|
||||
_textureQuality = p.textureQuality.value_or(_textureQuality);
|
||||
_renderOnlyOnMaster = p.renderOnlyOnMaster.value_or(_renderOnlyOnMaster);
|
||||
|
||||
addProperty(_url);
|
||||
addProperty(_browserPixeldimensions);
|
||||
addProperty(_reload);
|
||||
addProperty(_textureQuality);
|
||||
addProperty(_renderOnlyOnMaster);
|
||||
|
||||
_textureQuality.onChange([this]() {
|
||||
_textureDimensionsIsDirty = true;
|
||||
});
|
||||
|
||||
// Ensure that the browser is placed at the z-coordinate of the screen space plane
|
||||
glm::vec2 screenPosition = _cartesianPosition.value();
|
||||
_cartesianPosition = glm::vec3(screenPosition, skybrowser::ScreenSpaceZ);
|
||||
|
||||
if (global::windowDelegate->isMaster()) {
|
||||
SkyBrowserModule* module = global::moduleEngine->module<SkyBrowserModule>();
|
||||
_borderColor = randomBorderColor(module->highlight());
|
||||
}
|
||||
_scale = _size.y * 0.5;
|
||||
}
|
||||
|
||||
ScreenSpaceSkyBrowser::~ScreenSpaceSkyBrowser() {
|
||||
SkyBrowserModule* module = global::moduleEngine->module<SkyBrowserModule>();
|
||||
|
||||
if (module && module->getPair(identifier())) {
|
||||
module->removeTargetBrowserPair(identifier());
|
||||
}
|
||||
}
|
||||
|
||||
bool ScreenSpaceSkyBrowser::initializeGL() {
|
||||
WwtCommunicator::initializeGL();
|
||||
ScreenSpaceRenderable::initializeGL();
|
||||
updateTextureResolution();
|
||||
return true;
|
||||
}
|
||||
|
||||
glm::dvec2 ScreenSpaceSkyBrowser::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.f * skybrowser::windowRatio()), 2.f };
|
||||
glm::dvec2 result = - convertToScreenSpace * resultRelativeOs;
|
||||
return result;
|
||||
}
|
||||
|
||||
void ScreenSpaceSkyBrowser::setIdInBrowser() {
|
||||
WwtCommunicator::setIdInBrowser(identifier());
|
||||
}
|
||||
|
||||
void ScreenSpaceSkyBrowser::updateTextureResolution() {
|
||||
// Scale texture depending on the height of the window
|
||||
// Set texture size to the actual pixel size it covers
|
||||
glm::vec2 pixels = glm::vec2(global::windowDelegate->currentSubwindowSize());
|
||||
|
||||
// If the scale is 1, it covers half the window. Hence multiplication with 2
|
||||
float newResY = pixels.y * 2.f * _scale;
|
||||
float ratio = _size.x / _size.y;
|
||||
float newResX = newResY * ratio;
|
||||
glm::vec2 newSize = glm::vec2(newResX , newResY) * _textureQuality.value();
|
||||
|
||||
_browserPixeldimensions = glm::ivec2(newSize);
|
||||
_texture->setDimensions(glm::ivec3(newSize, 1));
|
||||
_objectSize = glm::ivec3(_texture->dimensions());
|
||||
}
|
||||
|
||||
void ScreenSpaceSkyBrowser::addRenderCopy(const glm::vec3& raePosition, int nCopies) {
|
||||
int start = _renderCopies.size();
|
||||
for (int i = 0; i < nCopies; i++) {
|
||||
openspace::properties::Property::PropertyInfo info = RenderCopyInfo;
|
||||
float azimuth = i * glm::two_pi<float>() / nCopies;
|
||||
glm::vec3 position = raePosition + glm::vec3(0.f, azimuth, 0.f);
|
||||
std::string id = "RenderCopy" + std::to_string(start + i);
|
||||
info.identifier = id.c_str();
|
||||
_renderCopies.push_back(
|
||||
std::make_unique<properties::Vec3Property>(
|
||||
info,
|
||||
position,
|
||||
glm::vec3(0.f, -glm::pi<float>(), -glm::half_pi<float>()),
|
||||
glm::vec3(10.f, glm::pi<float>(), glm::half_pi<float>())
|
||||
)
|
||||
);
|
||||
addProperty(_renderCopies.back().get());
|
||||
}
|
||||
}
|
||||
|
||||
void ScreenSpaceSkyBrowser::removeRenderCopy() {
|
||||
if (_renderCopies.size() > 0) {
|
||||
removeProperty(_renderCopies.back().get());
|
||||
_renderCopies.pop_back();
|
||||
}
|
||||
}
|
||||
|
||||
std::vector<std::pair<std::string, glm::dvec3>> ScreenSpaceSkyBrowser::renderCopies()
|
||||
{
|
||||
std::vector<std::pair<std::string, glm::dvec3>> vec;
|
||||
std::for_each(
|
||||
_renderCopies.begin(),
|
||||
_renderCopies.end(),
|
||||
[&](const std::unique_ptr<properties::Vec3Property>& copy) {
|
||||
std::pair<std::string, glm::dvec3> pair = {
|
||||
copy.get()->identifier(),
|
||||
glm::dvec3(copy.get()->value())
|
||||
};
|
||||
vec.push_back(pair);
|
||||
});
|
||||
return vec;
|
||||
}
|
||||
|
||||
void ScreenSpaceSkyBrowser::moveRenderCopy(int i, glm::vec3 raePosition) {
|
||||
if (i < _renderCopies.size() && i >= 0) {
|
||||
_renderCopies[i].get()->set(raePosition);
|
||||
}
|
||||
}
|
||||
|
||||
bool ScreenSpaceSkyBrowser::deinitializeGL() {
|
||||
ScreenSpaceRenderable::deinitializeGL();
|
||||
WwtCommunicator::deinitializeGL();
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
void ScreenSpaceSkyBrowser::render() {
|
||||
WwtCommunicator::render();
|
||||
|
||||
// If the sky browser only should be rendered on master, don't use the
|
||||
// global rotation
|
||||
if (_renderOnlyOnMaster && global::windowDelegate->isMaster()) {
|
||||
draw(
|
||||
translationMatrix() *
|
||||
localRotationMatrix() *
|
||||
scaleMatrix()
|
||||
);
|
||||
}
|
||||
else if(!_renderOnlyOnMaster) {
|
||||
draw(
|
||||
globalRotationMatrix() *
|
||||
translationMatrix() *
|
||||
localRotationMatrix() *
|
||||
scaleMatrix()
|
||||
);
|
||||
}
|
||||
|
||||
// Render a copy that is not interactive
|
||||
for (const std::unique_ptr<properties::Vec3Property>& copy : _renderCopies) {
|
||||
glm::vec3 spherical = sphericalToCartesian(raeToSpherical(copy.get()->value()));
|
||||
glm::mat4 localRotation = glm::inverse(glm::lookAt(
|
||||
glm::vec3(0.f),
|
||||
glm::normalize(spherical),
|
||||
glm::vec3(0.f, 1.f, 0.f)
|
||||
));
|
||||
draw(
|
||||
globalRotationMatrix() *
|
||||
glm::translate(glm::mat4(1.f), spherical) *
|
||||
localRotation *
|
||||
scaleMatrix()
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
void ScreenSpaceSkyBrowser::update() {
|
||||
// Texture of window is 1x1 when minimized
|
||||
bool isWindow = global::windowDelegate->currentSubwindowSize() != glm::ivec2(1);
|
||||
bool isWindowResized = global::windowDelegate->windowHasResized();
|
||||
if ((isWindowResized && isWindow) || _textureDimensionsIsDirty) {
|
||||
updateTextureResolution();
|
||||
_textureDimensionsIsDirty = false;
|
||||
}
|
||||
if (_sizeIsDirty) {
|
||||
updateScreenSpaceSize();
|
||||
_sizeIsDirty = false;
|
||||
}
|
||||
|
||||
WwtCommunicator::update();
|
||||
ScreenSpaceRenderable::update();
|
||||
}
|
||||
|
||||
void 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);
|
||||
}
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
void ScreenSpaceSkyBrowser::setOpacity(float opacity) {
|
||||
_opacity = opacity;
|
||||
}
|
||||
|
||||
void ScreenSpaceSkyBrowser::setScreenSpaceSize(const glm::vec2& newSize) {
|
||||
_size = newSize;
|
||||
_sizeIsDirty = true;
|
||||
}
|
||||
|
||||
void ScreenSpaceSkyBrowser::updateScreenSpaceSize() {
|
||||
_scale = abs(_size.y) * 0.5f;
|
||||
updateTextureResolution();
|
||||
}
|
||||
|
||||
float ScreenSpaceSkyBrowser::opacity() const {
|
||||
return _opacity;
|
||||
}
|
||||
|
||||
glm::vec2 ScreenSpaceSkyBrowser::size() const {
|
||||
return _size;
|
||||
}
|
||||
} // namespace openspace
|
||||
365
modules/skybrowser/src/targetbrowserpair.cpp
Normal file
365
modules/skybrowser/src/targetbrowserpair.cpp
Normal file
@@ -0,0 +1,365 @@
|
||||
/*****************************************************************************************
|
||||
* *
|
||||
* OpenSpace *
|
||||
* *
|
||||
* Copyright (c) 2014-2022 *
|
||||
* *
|
||||
* 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 <modules/skybrowser/include/targetbrowserpair.h>
|
||||
|
||||
#include <modules/skybrowser/include/screenspaceskybrowser.h>
|
||||
#include <modules/skybrowser/skybrowsermodule.h>
|
||||
#include <modules/skybrowser/include/renderableskytarget.h>
|
||||
#include <modules/skybrowser/include/utility.h>
|
||||
#include <modules/skybrowser/include/wwtdatahandler.h>
|
||||
#include <openspace/engine/globals.h>
|
||||
#include <openspace/engine/moduleengine.h>
|
||||
#include <openspace/navigation/navigationhandler.h>
|
||||
#include <openspace/camera/camera.h>
|
||||
#include <openspace/rendering/screenspacerenderable.h>
|
||||
#include <openspace/scripting/scriptengine.h>
|
||||
#include <ghoul/misc/assert.h>
|
||||
#include <glm/gtc/constants.hpp>
|
||||
#include <functional>
|
||||
|
||||
namespace openspace {
|
||||
|
||||
TargetBrowserPair::TargetBrowserPair(SceneGraphNode* targetNode,
|
||||
ScreenSpaceSkyBrowser* browser)
|
||||
: _targetNode(targetNode), _browser(browser)
|
||||
{
|
||||
ghoul_assert(browser != nullptr, "Sky browser is null pointer!");
|
||||
ghoul_assert(targetNode != nullptr, "Sky target is null pointer!");
|
||||
|
||||
_targetRenderable = dynamic_cast<RenderableSkyTarget*>(_targetNode->renderable());
|
||||
}
|
||||
|
||||
TargetBrowserPair& TargetBrowserPair::operator=(TargetBrowserPair other) {
|
||||
std::swap(_targetNode, other._targetNode);
|
||||
std::swap(_browser, other._browser);
|
||||
return *this;
|
||||
}
|
||||
|
||||
void TargetBrowserPair::setImageOrder(int i, int order) {
|
||||
_browser->setImageOrder(i, order);
|
||||
}
|
||||
|
||||
void TargetBrowserPair::removeHighlight(const glm::ivec3& color) {
|
||||
_targetRenderable->removeHighlight(color);
|
||||
_browser->removeHighlight(color);
|
||||
}
|
||||
|
||||
void TargetBrowserPair::highlight(const glm::ivec3& color) {
|
||||
_browser->highlight(color);
|
||||
_targetRenderable->highlight(color);
|
||||
}
|
||||
|
||||
void TargetBrowserPair::aimTargetGalactic(glm::dvec3 direction) {
|
||||
std::string id = _targetNode->identifier();
|
||||
// Uris for properties
|
||||
std::string positionUri = "Scene." + id + ".Translation.Position";
|
||||
glm::dvec3 positionCelestial = glm::normalize(direction) *
|
||||
skybrowser::CelestialSphereRadius;
|
||||
std::string setValue = "openspace.setPropertyValueSingle('";
|
||||
|
||||
openspace::global::scriptEngine->queueScript(
|
||||
setValue + positionUri + "', " + ghoul::to_string(positionCelestial) + ");",
|
||||
scripting::ScriptEngine::RemoteScripting::Yes
|
||||
);
|
||||
}
|
||||
|
||||
void TargetBrowserPair::startFinetuningTarget() {
|
||||
_startTargetPosition = _targetNode->worldPosition();
|
||||
}
|
||||
|
||||
// The fine tune of the target is a way to "drag and drop" the target with right click
|
||||
// drag on the sky browser window. This is to be able to drag the target around when it
|
||||
// has a very small field of view
|
||||
void TargetBrowserPair::fineTuneTarget(const glm::vec2& startMouse,
|
||||
const glm::vec2& translation)
|
||||
{
|
||||
glm::vec2 fineTune = _browser->fineTuneVector(translation);
|
||||
glm::vec2 endMouse = startMouse + fineTune;
|
||||
|
||||
// Translation world
|
||||
glm::dvec3 startWorld = skybrowser::localCameraToGalactic(
|
||||
glm::vec3(startMouse, skybrowser::ScreenSpaceZ)
|
||||
);
|
||||
glm::dvec3 endWorld = skybrowser::localCameraToGalactic(
|
||||
glm::vec3(endMouse, skybrowser::ScreenSpaceZ)
|
||||
);
|
||||
|
||||
glm::dvec3 translationWorld = endWorld - startWorld;
|
||||
aimTargetGalactic(_startTargetPosition + translationWorld);
|
||||
}
|
||||
|
||||
void TargetBrowserPair::synchronizeAim() {
|
||||
if (!_moveTarget.isAnimating()) {
|
||||
_browser->setEquatorialAim(targetDirectionEquatorial());
|
||||
_browser->setTargetRoll(targetRoll());
|
||||
_targetRenderable->setVerticalFov(_browser->verticalFov());
|
||||
}
|
||||
}
|
||||
|
||||
void TargetBrowserPair::setEnabled(bool enable) {
|
||||
_browser->setEnabled(enable);
|
||||
_targetRenderable->property("Enabled")->set(false);
|
||||
}
|
||||
|
||||
void TargetBrowserPair::setOpacity(float opacity)
|
||||
{
|
||||
_browser->property("Opacity")->set(opacity);
|
||||
_targetRenderable->property("Opacity")->set(opacity);
|
||||
}
|
||||
|
||||
bool TargetBrowserPair::isEnabled() const {
|
||||
return _targetRenderable->isEnabled() || _browser->isEnabled();
|
||||
}
|
||||
|
||||
void TargetBrowserPair::initialize() {
|
||||
_targetRenderable->setColor(_browser->borderColor());
|
||||
_targetRenderable->setDimensions(_browser->screenSpaceDimensions());
|
||||
_browser->updateBorderColor();
|
||||
}
|
||||
|
||||
glm::ivec3 TargetBrowserPair::borderColor() const {
|
||||
return _browser->borderColor();
|
||||
}
|
||||
|
||||
glm::dvec2 TargetBrowserPair::targetDirectionEquatorial() const {
|
||||
glm::dvec3 cartesian = skybrowser::galacticToEquatorial(
|
||||
glm::normalize(_targetNode->worldPosition())
|
||||
);
|
||||
return skybrowser::cartesianToSpherical(cartesian);
|
||||
}
|
||||
|
||||
glm::dvec3 TargetBrowserPair::targetDirectionGalactic() const {
|
||||
return glm::normalize(_targetNode->worldPosition());
|
||||
}
|
||||
|
||||
std::string TargetBrowserPair::browserGuiName() const {
|
||||
return _browser->guiName();
|
||||
}
|
||||
|
||||
std::string TargetBrowserPair::browserId() const {
|
||||
return _browser->identifier();
|
||||
}
|
||||
|
||||
std::string TargetBrowserPair::targetRenderableId() const {
|
||||
return _targetRenderable->identifier();
|
||||
}
|
||||
|
||||
std::string TargetBrowserPair::targetNodeId() const {
|
||||
return _targetNode->identifier();
|
||||
}
|
||||
|
||||
glm::vec2 TargetBrowserPair::size() const {
|
||||
return _browser->size();
|
||||
}
|
||||
|
||||
double TargetBrowserPair::verticalFov() const {
|
||||
return _browser->verticalFov();
|
||||
}
|
||||
|
||||
const std::deque<int>& TargetBrowserPair::selectedImages() const {
|
||||
return _browser->getSelectedImages();
|
||||
}
|
||||
|
||||
void TargetBrowserPair::selectImage(const ImageData& image, int i) {
|
||||
// Load image into browser
|
||||
_browser->displayImage(image.imageUrl, i);
|
||||
|
||||
// If the image has coordinates, move the target
|
||||
if (image.hasCelestialCoords) {
|
||||
|
||||
// Animate the target to the image coordinate position
|
||||
// unlock();
|
||||
glm::dvec3 galactic = skybrowser::equatorialToGalactic(image.equatorialCartesian);
|
||||
startAnimation(galactic * skybrowser::CelestialSphereRadius, image.fov);
|
||||
}
|
||||
}
|
||||
|
||||
void TargetBrowserPair::removeSelectedImage(int i) {
|
||||
_browser->removeSelectedImage(i);
|
||||
}
|
||||
|
||||
void TargetBrowserPair::loadImageCollection(const std::string& collection) {
|
||||
_browser->loadImageCollection(collection);
|
||||
}
|
||||
|
||||
void TargetBrowserPair::setImageOpacity(int i, float opacity) {
|
||||
_browser->setImageOpacity(i, opacity);
|
||||
}
|
||||
|
||||
void TargetBrowserPair::hideChromeInterface(bool shouldHide) {
|
||||
_browser->hideChromeInterface(shouldHide);
|
||||
}
|
||||
|
||||
void TargetBrowserPair::sendIdToBrowser() {
|
||||
_browser->setIdInBrowser();
|
||||
}
|
||||
|
||||
void TargetBrowserPair::updateBrowserSize() {
|
||||
_browser->updateBrowserSize();
|
||||
}
|
||||
|
||||
std::vector<std::pair<std::string, glm::dvec3>> TargetBrowserPair::renderCopies()
|
||||
{
|
||||
return _browser->renderCopies();
|
||||
}
|
||||
|
||||
void TargetBrowserPair::setIsSyncedWithWwt(bool isSynced) {
|
||||
_browser->setIsSyncedWithWwt(isSynced);
|
||||
}
|
||||
|
||||
void TargetBrowserPair::setVerticalFov(double vfov) {
|
||||
_browser->setVerticalFov(vfov);
|
||||
_targetRenderable->setVerticalFov(vfov);
|
||||
}
|
||||
|
||||
void TargetBrowserPair::setEquatorialAim(const glm::dvec2& aim) {
|
||||
_equatorialAim = aim;
|
||||
aimTargetGalactic(
|
||||
skybrowser::equatorialToGalactic(skybrowser::sphericalToCartesian(aim))
|
||||
);
|
||||
_browser->setEquatorialAim(aim);
|
||||
}
|
||||
|
||||
void TargetBrowserPair::setBorderColor(const glm::ivec3& color) {
|
||||
_borderColor = color;
|
||||
_targetRenderable->setColor(color);
|
||||
_browser->setBorderColor(color);
|
||||
}
|
||||
|
||||
void TargetBrowserPair::setScreenSpaceSize(const glm::vec2& dimensions) {
|
||||
_browser->setScreenSpaceSize(dimensions);
|
||||
_targetRenderable->setDimensions(dimensions);
|
||||
}
|
||||
|
||||
void TargetBrowserPair::setVerticalFovWithScroll(float scroll) {
|
||||
_browser->setVerticalFovWithScroll(scroll);
|
||||
}
|
||||
|
||||
void TargetBrowserPair::incrementallyAnimateToCoordinate() {
|
||||
// Animate the target before the field of view starts to animate
|
||||
if (_moveTarget.isAnimating()) {
|
||||
aimTargetGalactic(_moveTarget.getNewValue());
|
||||
}
|
||||
else if (!_moveTarget.isAnimating() && _targetIsAnimating) {
|
||||
// Set the finished position
|
||||
aimTargetGalactic(_moveTarget.getNewValue());
|
||||
_fovAnimation.start();
|
||||
_targetIsAnimating = false;
|
||||
}
|
||||
if (_fovAnimation.isAnimating()) {
|
||||
_browser->setVerticalFov(_fovAnimation.getNewValue());
|
||||
_targetRenderable->setVerticalFov(_browser->verticalFov());
|
||||
}
|
||||
}
|
||||
|
||||
void TargetBrowserPair::startFading(float goal, float fadeTime) {
|
||||
_fadeTarget = skybrowser::Animation(_targetRenderable->opacity(), goal, fadeTime);
|
||||
_fadeBrowser = skybrowser::Animation(_browser->opacity(), goal, fadeTime);
|
||||
_fadeTarget.start();
|
||||
_fadeBrowser.start();
|
||||
}
|
||||
|
||||
void TargetBrowserPair::startAnimation(glm::dvec3 galacticCoords, double fovEnd) {
|
||||
SkyBrowserModule* module = global::moduleEngine->module<SkyBrowserModule>();
|
||||
double fovSpeed = module->browserAnimationSpeed();
|
||||
// The speed is given degrees /sec
|
||||
double fovTime = abs(_browser->verticalFov() - fovEnd) / fovSpeed;
|
||||
// Fov animation
|
||||
_fovAnimation = skybrowser::Animation(_browser->verticalFov(), fovEnd, fovTime);
|
||||
|
||||
// Target animation
|
||||
glm::dvec3 start = glm::normalize(_targetNode->worldPosition()) *
|
||||
skybrowser::CelestialSphereRadius;
|
||||
double targetSpeed = module->targetAnimationSpeed();
|
||||
double angle = skybrowser::angleBetweenVectors(start, galacticCoords);
|
||||
_moveTarget = skybrowser::Animation(start, galacticCoords, angle / targetSpeed);
|
||||
_moveTarget.start();
|
||||
_targetIsAnimating = true;
|
||||
}
|
||||
|
||||
void TargetBrowserPair::centerTargetOnScreen() {
|
||||
// Get camera direction in celestial spherical coordinates
|
||||
glm::dvec3 viewDirection = skybrowser::cameraDirectionGalactic();
|
||||
// Keep the current fov
|
||||
float currentFov = verticalFov();
|
||||
startAnimation(viewDirection, currentFov);
|
||||
}
|
||||
|
||||
double TargetBrowserPair::targetRoll()
|
||||
{
|
||||
// To remove the lag effect when moving the camera while having a locked
|
||||
// target, send the locked coordinates to wwt
|
||||
glm::dvec3 normal = glm::normalize(
|
||||
_targetNode->worldPosition() -
|
||||
global::navigationHandler->camera()->positionVec3()
|
||||
);
|
||||
glm::dvec3 right = glm::normalize(
|
||||
glm::cross(
|
||||
global::navigationHandler->camera()->lookUpVectorWorldSpace(),
|
||||
normal
|
||||
)
|
||||
);
|
||||
glm::dvec3 up = glm::normalize(glm::cross(normal, right));
|
||||
return skybrowser::targetRoll(up, normal);
|
||||
}
|
||||
|
||||
bool TargetBrowserPair::hasFinishedFading() const {
|
||||
return !_fadeBrowser.isAnimating() && !_fadeTarget.isAnimating();
|
||||
}
|
||||
|
||||
bool TargetBrowserPair::isFacingCamera() const {
|
||||
return _browser->isFacingCamera();
|
||||
}
|
||||
|
||||
bool TargetBrowserPair::isUsingRadiusAzimuthElevation() const {
|
||||
return _browser->isUsingRaeCoords();
|
||||
}
|
||||
|
||||
SceneGraphNode* TargetBrowserPair::targetNode() const {
|
||||
return _targetNode;
|
||||
}
|
||||
|
||||
ScreenSpaceSkyBrowser* TargetBrowserPair::browser() const {
|
||||
return _browser;
|
||||
}
|
||||
|
||||
void TargetBrowserPair::incrementallyFade()
|
||||
{
|
||||
if (_fadeBrowser.isAnimating()) {
|
||||
_browser->setOpacity(_fadeBrowser.getNewValue());
|
||||
}
|
||||
if (_fadeTarget.isAnimating()) {
|
||||
_targetRenderable->setOpacity(_fadeTarget.getNewValue());
|
||||
}
|
||||
}
|
||||
|
||||
bool operator==(const TargetBrowserPair& lhs, const TargetBrowserPair& rhs) {
|
||||
return lhs._targetNode == rhs._targetNode && lhs._browser == rhs._browser;
|
||||
}
|
||||
|
||||
bool operator!=(const TargetBrowserPair& lhs, const TargetBrowserPair& rhs) {
|
||||
return !(lhs == rhs);
|
||||
}
|
||||
} // namespace openspace
|
||||
269
modules/skybrowser/src/utility.cpp
Normal file
269
modules/skybrowser/src/utility.cpp
Normal file
@@ -0,0 +1,269 @@
|
||||
/*****************************************************************************************
|
||||
* *
|
||||
* OpenSpace *
|
||||
* *
|
||||
* Copyright (c) 2014-2022 *
|
||||
* *
|
||||
* 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 <modules/skybrowser/include/utility.h>
|
||||
|
||||
#include <openspace/engine/globals.h>
|
||||
#include <openspace/engine/windowdelegate.h>
|
||||
#include <openspace/navigation/navigationhandler.h>
|
||||
#include <openspace/camera/camera.h>
|
||||
#include <glm/gtx/vector_angle.hpp>
|
||||
#include <cmath>
|
||||
|
||||
namespace openspace::skybrowser {
|
||||
|
||||
// Converts from spherical coordinates in the unit of degrees to cartesian coordianates
|
||||
glm::dvec3 sphericalToCartesian(const glm::dvec2& coords) {
|
||||
glm::dvec2 coordsRadians = glm::radians(coords);
|
||||
|
||||
glm::dvec3 cartesian = glm::dvec3(
|
||||
cos(coordsRadians.x) * cos(coordsRadians.y),
|
||||
sin(coordsRadians.x) * cos(coordsRadians.y),
|
||||
sin(coordsRadians.y)
|
||||
);
|
||||
|
||||
return cartesian;
|
||||
}
|
||||
|
||||
// Converts from cartesian coordianates to spherical in the unit of degrees
|
||||
glm::dvec2 cartesianToSpherical(const glm::dvec3& coord) {
|
||||
// Equatorial coordinates RA = right ascension, Dec = declination
|
||||
double ra = atan2(coord.y, coord.x);
|
||||
double dec = atan2(coord.z, glm::sqrt((coord.x * coord.x) + (coord.y * coord.y)));
|
||||
|
||||
ra = ra > 0 ? ra : ra + (2.0 * glm::pi<double>());
|
||||
|
||||
glm::dvec2 celestialCoords{ ra, dec };
|
||||
|
||||
return glm::degrees(celestialCoords);
|
||||
}
|
||||
|
||||
glm::dvec3 galacticToEquatorial(const glm::dvec3& coords) {
|
||||
return glm::transpose(conversionMatrix) * glm::normalize(coords);
|
||||
}
|
||||
|
||||
glm::dvec3 equatorialToGalactic(const glm::dvec3& coords) {
|
||||
// On the unit sphere
|
||||
glm::dvec3 rGalactic = conversionMatrix * glm::normalize(coords);
|
||||
return rGalactic;
|
||||
}
|
||||
|
||||
glm::dvec3 localCameraToScreenSpace3d(const glm::dvec3& coords) {
|
||||
// Ensure that if the coord is behind the camera,
|
||||
// the converted coordinate will be there too
|
||||
double zCoord = coords.z > 0 ? -ScreenSpaceZ : ScreenSpaceZ;
|
||||
|
||||
// Calculate screen space coords x and y
|
||||
double tanX = coords.x / coords.z;
|
||||
double tanY = coords.y / coords.z;
|
||||
|
||||
glm::dvec3 screenSpace = glm::dvec3(zCoord * tanX, zCoord * tanY, zCoord);
|
||||
|
||||
return screenSpace;
|
||||
}
|
||||
|
||||
glm::dvec3 localCameraToGalactic(const glm::dvec3& coords) {
|
||||
glm::dvec3 camPos = global::navigationHandler->camera()->positionVec3();
|
||||
glm::dvec4 coordsVec4 = glm::dvec4(coords, 1.0) ;
|
||||
glm::dmat4 camMat = glm::inverse(
|
||||
global::navigationHandler->camera()->combinedViewMatrix()
|
||||
);
|
||||
// Subtract gamera position to get the view direction
|
||||
glm::dvec3 galactic = glm::dvec3(camMat * coordsVec4) - camPos;
|
||||
|
||||
return glm::normalize(galactic) * skybrowser::CelestialSphereRadius;
|
||||
}
|
||||
|
||||
glm::dvec3 localCameraToEquatorial(const glm::dvec3& coords) {
|
||||
// Calculate the galactic coordinate of the target direction
|
||||
// projected onto the celestial sphere
|
||||
glm::dvec3 camPos = global::navigationHandler->camera()->positionVec3();
|
||||
glm::dvec3 galactic = camPos + skybrowser::localCameraToGalactic(coords);
|
||||
|
||||
return skybrowser::galacticToEquatorial(galactic);
|
||||
}
|
||||
|
||||
glm::dvec3 equatorialToLocalCamera(const glm::dvec3& coords) {
|
||||
// Transform equatorial J2000 to galactic coord with infinite radius
|
||||
glm::dvec3 galactic = equatorialToGalactic(coords) * CelestialSphereRadius;
|
||||
glm::dvec3 localCamera = galacticToLocalCamera(galactic);
|
||||
return localCamera;
|
||||
}
|
||||
|
||||
glm::dvec3 galacticToLocalCamera(const glm::dvec3& coords) {
|
||||
// Transform vector to camera's local coordinate system
|
||||
glm::dmat4 camMat = global::navigationHandler->camera()->combinedViewMatrix();
|
||||
glm::dvec3 viewDirectionLocal = camMat * glm::dvec4(coords, 1.0);
|
||||
|
||||
return glm::normalize(viewDirectionLocal);
|
||||
}
|
||||
|
||||
double targetRoll(const glm::dvec3& up, const glm::dvec3& forward) {
|
||||
glm::dvec3 upJ2000 = skybrowser::galacticToEquatorial(up);
|
||||
glm::dvec3 forwardJ2000 = skybrowser::galacticToEquatorial(forward);
|
||||
|
||||
glm::dvec3 crossUpNorth = glm::cross(upJ2000, NorthPole);
|
||||
double dotNorthUp = glm::dot(NorthPole, upJ2000);
|
||||
double dotCrossUpNorthForward = glm::dot(crossUpNorth, forwardJ2000);
|
||||
|
||||
return glm::degrees(atan2(dotCrossUpNorthForward, dotNorthUp));
|
||||
}
|
||||
|
||||
glm::dvec3 cameraDirectionEquatorial() {
|
||||
// Get the view direction of the screen in cartesian J2000 coordinates
|
||||
return galacticToEquatorial(cameraDirectionGalactic());
|
||||
}
|
||||
|
||||
glm::dvec3 cameraDirectionGalactic() {
|
||||
// Get the view direction of the screen in galactic coordinates
|
||||
glm::dvec3 camPos = global::navigationHandler->camera()->positionVec3();
|
||||
glm::dvec3 view = global::navigationHandler->camera()->viewDirectionWorldSpace();
|
||||
glm::dvec3 galCoord = camPos + (skybrowser::CelestialSphereRadius * view);
|
||||
|
||||
return galCoord;
|
||||
}
|
||||
|
||||
float windowRatio() {
|
||||
glm::vec2 windowRatio = global::windowDelegate->currentWindowSize();
|
||||
return windowRatio.x / windowRatio.y;
|
||||
}
|
||||
|
||||
bool isCoordinateInView(const glm::dvec3& equatorial) {
|
||||
// Check if image coordinate is within current FOV
|
||||
glm::dvec3 localCamera = equatorialToLocalCamera(equatorial);
|
||||
glm::dvec3 coordsScreen = localCameraToScreenSpace3d(localCamera);
|
||||
double r = static_cast<float>(windowRatio());
|
||||
|
||||
bool isCoordInView = abs(coordsScreen.x) < r && abs(coordsScreen.y) < 1.f &&
|
||||
coordsScreen.z < 0;
|
||||
|
||||
return isCoordInView;
|
||||
}
|
||||
|
||||
// Transforms a pixel coordinate to a screen space coordinate
|
||||
glm::vec2 pixelToScreenSpace2d(const glm::vec2& mouseCoordinate) {
|
||||
glm::vec2 size = glm::vec2(global::windowDelegate->currentWindowSize());
|
||||
// Change origin to middle of the window
|
||||
glm::vec2 screenSpacePos = mouseCoordinate - (size / 2.0f);
|
||||
// Ensure the upper right corner is positive on the y axis
|
||||
screenSpacePos *= glm::vec2(1.0f, -1.0f);
|
||||
// Transform pixel coordinates to screen space coordinates [-1,1][-ratio, ratio]
|
||||
screenSpacePos /= (0.5f * size.y);
|
||||
return screenSpacePos;
|
||||
}
|
||||
|
||||
// The horizontal and vertical fov of the OpenSpace window
|
||||
glm::dvec2 fovWindow() {
|
||||
// OpenSpace FOV
|
||||
glm::dvec2 windowDim = glm::dvec2(global::windowDelegate->currentWindowSize());
|
||||
double windowRatio = windowDim.y / windowDim.x;
|
||||
double hFov = global::windowDelegate->getHorizFieldOfView();
|
||||
glm::dvec2 OpenSpaceFOV = glm::dvec2(hFov, hFov * windowRatio);
|
||||
return OpenSpaceFOV;
|
||||
}
|
||||
|
||||
double angleBetweenVectors(const glm::dvec3& start, const glm::dvec3& end) {
|
||||
// Find smallest angle between the two vectors
|
||||
double cos = glm::dot(glm::normalize(start), glm::normalize(end));
|
||||
// Ensure cos is within defined interval [-1,1]
|
||||
return std::acos(std::clamp(cos, -1.0, 1.0));
|
||||
}
|
||||
|
||||
glm::dmat4 incrementalAnimationMatrix(const glm::dvec3& start, const glm::dvec3& end,
|
||||
double percentage)
|
||||
{
|
||||
double smallestAngle = angleBetweenVectors(start, end);
|
||||
// Calculate rotation this frame
|
||||
double rotationAngle = smallestAngle * percentage;
|
||||
|
||||
// Create the rotation matrix
|
||||
glm::dvec3 rotationAxis = glm::normalize(glm::cross(start, end));
|
||||
return glm::rotate(rotationAngle, rotationAxis);
|
||||
}
|
||||
double sizeFromFov(double fov, glm::dvec3 worldPosition) {
|
||||
|
||||
// Calculate the size with trigonometry
|
||||
// /|
|
||||
// /_| Adjacent is the horizontal line, opposite the vertical
|
||||
// \ | Calculate for half the triangle first, then multiply with 2
|
||||
// \|
|
||||
double adjacent = glm::length(worldPosition);
|
||||
double opposite = 2 * adjacent * glm::tan(glm::radians(fov * 0.5));
|
||||
return opposite;
|
||||
}
|
||||
|
||||
float Animation<float>::getNewValue() {
|
||||
if (!isAnimating()) {
|
||||
return _goal;
|
||||
}
|
||||
else {
|
||||
float percentage = static_cast<float>(percentageSpent());
|
||||
float diff = (_goal - _start) * easeOutExpo(percentage);
|
||||
return _start + diff;
|
||||
}
|
||||
}
|
||||
|
||||
double Animation<double>::getNewValue() {
|
||||
if (!isAnimating()) {
|
||||
return _goal;
|
||||
}
|
||||
else {
|
||||
double percentage = percentageSpent();
|
||||
double diff = (_goal - _start) * easeOutExpo(percentage);
|
||||
return _start + diff;
|
||||
}
|
||||
}
|
||||
|
||||
glm::dmat4 Animation<glm::dvec3>::getRotationMatrix() {
|
||||
if (!isAnimating()) {
|
||||
return glm::dmat4(1.0);
|
||||
}
|
||||
double percentage = easeInOutSine(percentageSpent());
|
||||
double increment = percentage - _lastPercentage;
|
||||
_lastPercentage = percentage;
|
||||
|
||||
glm::dmat4 rotMat = skybrowser::incrementalAnimationMatrix(
|
||||
glm::normalize(_start),
|
||||
glm::normalize(_goal),
|
||||
increment
|
||||
);
|
||||
return rotMat;
|
||||
}
|
||||
|
||||
glm::dvec3 Animation<glm::dvec3>::getNewValue() {
|
||||
if (!isAnimating()) {
|
||||
return _goal;
|
||||
}
|
||||
glm::dmat4 rotMat = skybrowser::incrementalAnimationMatrix(
|
||||
glm::normalize(_start),
|
||||
glm::normalize(_goal),
|
||||
easeOutExpo(percentageSpent())
|
||||
);
|
||||
// Rotate direction
|
||||
return glm::dvec3(rotMat * glm::dvec4(_start, 1.0));;
|
||||
}
|
||||
} // namespace openspace
|
||||
|
||||
|
||||
|
||||
|
||||
314
modules/skybrowser/src/wwtcommunicator.cpp
Normal file
314
modules/skybrowser/src/wwtcommunicator.cpp
Normal file
@@ -0,0 +1,314 @@
|
||||
/*****************************************************************************************
|
||||
* *
|
||||
* OpenSpace *
|
||||
* *
|
||||
* Copyright (c) 2014-2022 *
|
||||
* *
|
||||
* 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 <modules/skybrowser/include/wwtcommunicator.h>
|
||||
|
||||
#include <modules/webbrowser/webbrowsermodule.h>
|
||||
#include <modules/webbrowser/include/webkeyboardhandler.h>
|
||||
#include <modules/webbrowser/include/browserinstance.h>
|
||||
#include <ghoul/misc/dictionaryjsonformatter.h>
|
||||
#include <modules/skybrowser/include/utility.h>
|
||||
|
||||
namespace {
|
||||
constexpr const char* _loggerCat = "WwtCommunicator";
|
||||
} // namespace
|
||||
|
||||
namespace openspace {
|
||||
|
||||
WwtCommunicator::WwtCommunicator(const ghoul::Dictionary& dictionary)
|
||||
: Browser(dictionary) {}
|
||||
|
||||
WwtCommunicator::~WwtCommunicator() {}
|
||||
|
||||
void WwtCommunicator::displayImage(const std::string& url, int i) {
|
||||
// Ensure there are no duplicates
|
||||
auto it = std::find(_selectedImages.begin(), _selectedImages.end(), i);
|
||||
if (it == _selectedImages.end()) {
|
||||
// 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(addImageMessage(std::to_string(i), url));
|
||||
sendMessageToWwt(setImageOpacityMessage(std::to_string(i), 1.0));
|
||||
}
|
||||
}
|
||||
|
||||
void WwtCommunicator::removeSelectedImage(int i) {
|
||||
// Remove from selected list
|
||||
auto it = std::find(_selectedImages.begin(), _selectedImages.end(), i);
|
||||
|
||||
if (it != std::end(_selectedImages)) {
|
||||
_selectedImages.erase(it);
|
||||
sendMessageToWwt(removeImageMessage(std::to_string(i)));
|
||||
}
|
||||
}
|
||||
|
||||
void WwtCommunicator::sendMessageToWwt(const ghoul::Dictionary& msg) {
|
||||
std::string script = "sendMessageToWWT(" + ghoul::formatJson(msg) + ");";
|
||||
executeJavascript(script);
|
||||
}
|
||||
|
||||
const std::deque<int>& WwtCommunicator::getSelectedImages() const {
|
||||
return _selectedImages;
|
||||
}
|
||||
|
||||
void WwtCommunicator::setTargetRoll(double roll)
|
||||
{
|
||||
_targetRoll = roll;
|
||||
}
|
||||
|
||||
void WwtCommunicator::setVerticalFov(double vfov) {
|
||||
_verticalFov = vfov;
|
||||
_equatorialAimIsDirty = true;
|
||||
}
|
||||
|
||||
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::setIsSyncedWithWwt(bool isSynced) {
|
||||
_isSyncedWithWwt = isSynced;
|
||||
}
|
||||
|
||||
void WwtCommunicator::setEquatorialAim(glm::dvec2 equatorial) {
|
||||
_equatorialAim = std::move(equatorial);
|
||||
_equatorialAimIsDirty = true;
|
||||
}
|
||||
|
||||
void WwtCommunicator::setBorderColor(glm::ivec3 color) {
|
||||
_borderColor = std::move(color);
|
||||
_borderColorIsDirty = true;
|
||||
}
|
||||
|
||||
void WwtCommunicator::highlight(const glm::ivec3& addition) {
|
||||
setWebpageBorderColor(_borderColor + addition);
|
||||
}
|
||||
|
||||
void WwtCommunicator::removeHighlight(const glm::ivec3& removal) {
|
||||
setWebpageBorderColor(_borderColor - removal);
|
||||
}
|
||||
|
||||
void WwtCommunicator::updateBorderColor() {
|
||||
setWebpageBorderColor(_borderColor);
|
||||
}
|
||||
|
||||
void WwtCommunicator::updateAim() {
|
||||
// Message WorldWide Telescope current view
|
||||
ghoul::Dictionary message = moveCameraMessage(
|
||||
_equatorialAim,
|
||||
_verticalFov,
|
||||
_targetRoll
|
||||
);
|
||||
sendMessageToWwt(message);
|
||||
}
|
||||
|
||||
glm::dvec2 WwtCommunicator::fieldsOfView() const {
|
||||
glm::dvec2 browserFov = glm::dvec2(verticalFov() * browserRatio(), verticalFov());
|
||||
|
||||
return browserFov;
|
||||
}
|
||||
|
||||
bool WwtCommunicator::hasLoadedImages() const {
|
||||
return _hasLoadedImages;
|
||||
}
|
||||
|
||||
glm::dvec2 WwtCommunicator::equatorialAim() const {
|
||||
return _equatorialAim;
|
||||
}
|
||||
|
||||
void WwtCommunicator::setImageOrder(int i, int order) {
|
||||
// Find in selected images list
|
||||
auto current = std::find(
|
||||
_selectedImages.begin(),
|
||||
_selectedImages.end(),
|
||||
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 = setLayerOrderMessage(std::to_string(i), reverseOrder);
|
||||
sendMessageToWwt(message);
|
||||
}
|
||||
|
||||
void WwtCommunicator::loadImageCollection(const std::string& collection) {
|
||||
sendMessageToWwt(loadCollectionMessage(collection));
|
||||
_hasLoadedImages = true;
|
||||
}
|
||||
|
||||
void WwtCommunicator::setImageOpacity(int i, float opacity) {
|
||||
ghoul::Dictionary msg = setImageOpacityMessage(std::to_string(i), opacity);
|
||||
sendMessageToWwt(msg);
|
||||
}
|
||||
|
||||
void WwtCommunicator::hideChromeInterface(bool shouldHide) {
|
||||
std::string script = "sendMessageToWWT({event : \"modify_settings\", "
|
||||
"settings : [[\"hideAllChrome\", true]], target: \"app\"});";
|
||||
executeJavascript(script);
|
||||
}
|
||||
|
||||
void WwtCommunicator::update() {
|
||||
Browser::update();
|
||||
// Cap how messages are passed
|
||||
std::chrono::system_clock::time_point now = std::chrono::system_clock::now();
|
||||
std::chrono::system_clock::duration timeSinceLastUpdate = now - _lastUpdateTime;
|
||||
|
||||
if (timeSinceLastUpdate > TimeUpdateInterval) {
|
||||
if (_equatorialAimIsDirty) {
|
||||
updateAim();
|
||||
_equatorialAimIsDirty = false;
|
||||
}
|
||||
if (_borderColorIsDirty) {
|
||||
updateBorderColor();
|
||||
_borderColorIsDirty = false;
|
||||
}
|
||||
_lastUpdateTime = std::chrono::system_clock::now();
|
||||
}
|
||||
}
|
||||
|
||||
void WwtCommunicator::render() {
|
||||
Browser::render();
|
||||
}
|
||||
|
||||
void WwtCommunicator::initializeGL() {
|
||||
Browser::initializeGL();
|
||||
}
|
||||
|
||||
void WwtCommunicator::deinitializeGL() {
|
||||
Browser::deinitializeGL();
|
||||
}
|
||||
|
||||
void WwtCommunicator::setHasLoadedImages(bool isLoaded) {
|
||||
_hasLoadedImages = isLoaded;
|
||||
}
|
||||
|
||||
void WwtCommunicator::setIdInBrowser(const std::string& id) {
|
||||
// Send ID to it's browser
|
||||
executeJavascript("setId('" + id + "')");
|
||||
}
|
||||
|
||||
glm::ivec3 WwtCommunicator::borderColor() const {
|
||||
return _borderColor;
|
||||
}
|
||||
|
||||
double WwtCommunicator::verticalFov() const {
|
||||
return _verticalFov;
|
||||
}
|
||||
|
||||
// WWT messages
|
||||
ghoul::Dictionary WwtCommunicator::moveCameraMessage(const glm::dvec2& celestCoords,
|
||||
double fov, double roll,
|
||||
bool shouldMoveInstantly)
|
||||
{
|
||||
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("roll", roll);
|
||||
msg.setValue("instant", shouldMoveInstantly);
|
||||
|
||||
return msg;
|
||||
}
|
||||
|
||||
ghoul::Dictionary WwtCommunicator::loadCollectionMessage(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 WwtCommunicator::setForegroundMessage(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 WwtCommunicator::addImageMessage(const std::string& id,
|
||||
const std::string& url)
|
||||
{
|
||||
using namespace std::string_literals;
|
||||
ghoul::Dictionary msg;
|
||||
msg.setValue("event", "image_layer_create"s);
|
||||
msg.setValue("id", id);
|
||||
msg.setValue("url", url);
|
||||
msg.setValue("mode", "preloaded"s);
|
||||
msg.setValue("goto", false);
|
||||
|
||||
return msg;
|
||||
}
|
||||
|
||||
ghoul::Dictionary WwtCommunicator::removeImageMessage(const std::string& imageId) {
|
||||
using namespace std::string_literals;
|
||||
ghoul::Dictionary msg;
|
||||
msg.setValue("event", "image_layer_remove"s);
|
||||
msg.setValue("id", imageId);
|
||||
|
||||
return msg;
|
||||
}
|
||||
|
||||
ghoul::Dictionary WwtCommunicator::setImageOpacityMessage(const std::string& imageId,
|
||||
double opacity)
|
||||
{
|
||||
using namespace std::string_literals;
|
||||
ghoul::Dictionary msg;
|
||||
msg.setValue("event", "image_layer_set"s);
|
||||
msg.setValue("id", imageId);
|
||||
msg.setValue("setting", "opacity"s);
|
||||
msg.setValue("value", opacity);
|
||||
|
||||
return msg;
|
||||
}
|
||||
|
||||
ghoul::Dictionary WwtCommunicator::setLayerOrderMessage(const std::string& id, int order) {
|
||||
// The lower the layer order, the more towards the back the image is placed
|
||||
// 0 is the background
|
||||
using namespace std::string_literals;
|
||||
ghoul::Dictionary msg;
|
||||
msg.setValue("event", "image_layer_order"s);
|
||||
msg.setValue("id", id);
|
||||
msg.setValue("order", order);
|
||||
msg.setValue("version", messageCounter);
|
||||
|
||||
messageCounter++;
|
||||
|
||||
return msg;
|
||||
}
|
||||
} // namespace openspace
|
||||
378
modules/skybrowser/src/wwtdatahandler.cpp
Normal file
378
modules/skybrowser/src/wwtdatahandler.cpp
Normal file
@@ -0,0 +1,378 @@
|
||||
/*****************************************************************************************
|
||||
* *
|
||||
* OpenSpace *
|
||||
* *
|
||||
* Copyright (c) 2014-2022 *
|
||||
* *
|
||||
* 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 <modules/skybrowser/include/wwtdatahandler.h>
|
||||
|
||||
#include <modules/skybrowser/include/utility.h>
|
||||
#include <modules/space/speckloader.h>
|
||||
#include <openspace/util/httprequest.h>
|
||||
#include <ghoul/filesystem/filesystem.h>
|
||||
#include <ghoul/logging/logmanager.h>
|
||||
#include <filesystem>
|
||||
#include <algorithm>
|
||||
#include <sys/types.h>
|
||||
#include <sys/stat.h>
|
||||
|
||||
namespace {
|
||||
constexpr const char* _loggerCat = "WwtDataHandler";
|
||||
} //namespace
|
||||
|
||||
namespace openspace {
|
||||
|
||||
bool hasAttribute(const tinyxml2::XMLElement* element, const std::string_view& name) {
|
||||
return element->FindAttribute(std::string(name).c_str());
|
||||
}
|
||||
|
||||
std::string attribute(const tinyxml2::XMLElement* element, const std::string& name) {
|
||||
if (hasAttribute(element, name)) {
|
||||
return element->FindAttribute(name.c_str())->Value();
|
||||
}
|
||||
return wwt::Undefined;
|
||||
}
|
||||
|
||||
// Parsing and downloading of wtml files
|
||||
bool downloadFile(const std::string& url, const std::filesystem::path& fileDestination) {
|
||||
// Get the web page and save to file
|
||||
HttpFileDownload wtmlRoot(
|
||||
url,
|
||||
fileDestination,
|
||||
HttpFileDownload::Overwrite::Yes
|
||||
);
|
||||
wtmlRoot.start(std::chrono::milliseconds(10000));
|
||||
return wtmlRoot.wait();
|
||||
}
|
||||
|
||||
bool directoryExists(const std::filesystem::path& path) {
|
||||
return std::filesystem::exists(path) && std::filesystem::is_directory(path);
|
||||
}
|
||||
|
||||
std::string createSearchableString(std::string str) {
|
||||
// Remove white spaces and all special characters
|
||||
str.erase(
|
||||
std::remove_if(
|
||||
str.begin(),
|
||||
str.end(),
|
||||
[](char c) {
|
||||
const bool isNumberOrLetter = std::isdigit(c) || std::isalpha(c);
|
||||
return !isNumberOrLetter;
|
||||
}
|
||||
),
|
||||
str.end()
|
||||
);
|
||||
// Make the word lower case
|
||||
std::transform(
|
||||
str.begin(),
|
||||
str.end(),
|
||||
str.begin(),
|
||||
[](char c) {
|
||||
return std::tolower(c);
|
||||
}
|
||||
);
|
||||
return str;
|
||||
}
|
||||
|
||||
tinyxml2::XMLElement* getDirectChildNode(tinyxml2::XMLElement* node,
|
||||
const std::string& name)
|
||||
{
|
||||
while (node && node->Name() != name) {
|
||||
node = node->FirstChildElement();
|
||||
}
|
||||
return node;
|
||||
}
|
||||
|
||||
tinyxml2::XMLElement* getChildNode(tinyxml2::XMLElement* node,
|
||||
const std::string& name)
|
||||
{
|
||||
tinyxml2::XMLElement* child = node->FirstChildElement();
|
||||
tinyxml2::XMLElement* imageSet = nullptr;
|
||||
|
||||
// Traverse the children and look at all their first child to find ImageSet
|
||||
while (child) {
|
||||
imageSet = getDirectChildNode(child, name);
|
||||
// Found
|
||||
if (imageSet) {
|
||||
break;
|
||||
}
|
||||
child = child->NextSiblingElement();
|
||||
}
|
||||
return imageSet;
|
||||
}
|
||||
|
||||
std::string getChildNodeContentFromImageSet(tinyxml2::XMLElement* imageSet,
|
||||
const std::string& elementName)
|
||||
{
|
||||
// Find the thumbnail image url
|
||||
// The thumbnail is the last node so traverse backwards for speed
|
||||
tinyxml2::XMLElement* imageSetChild = imageSet->FirstChildElement(
|
||||
elementName.c_str()
|
||||
);
|
||||
|
||||
if (imageSetChild && imageSetChild->GetText()) {
|
||||
return imageSetChild->GetText();
|
||||
}
|
||||
return wwt::Undefined;
|
||||
}
|
||||
|
||||
std::string getUrlFromPlace(tinyxml2::XMLElement* place) {
|
||||
// If the place has a thumbnail url, return it
|
||||
if (hasAttribute(place, wwt::Thumbnail)) {
|
||||
return attribute(place, wwt::Thumbnail);
|
||||
}
|
||||
|
||||
// If the place doesn't have a thumbnail url data attribute,
|
||||
// Load the image set it stores instead
|
||||
tinyxml2::XMLElement* imageSet = getChildNode(place, wwt::ImageSet);
|
||||
|
||||
// If there is an imageSet, collect thumbnail url
|
||||
if (imageSet) {
|
||||
return getChildNodeContentFromImageSet(imageSet, wwt::ThumbnailUrl);
|
||||
}
|
||||
// If it doesn't contain an ImageSet, it doesn't have an url
|
||||
return wwt::Undefined;
|
||||
}
|
||||
|
||||
|
||||
void parseWtmlsFromDisc(std::vector<tinyxml2::XMLDocument*>& _xmls,
|
||||
const std::filesystem::path& directory)
|
||||
{
|
||||
for (const auto& entry : std::filesystem::directory_iterator(directory)) {
|
||||
tinyxml2::XMLDocument* document = new tinyxml2::XMLDocument();
|
||||
std::string path = entry.path().u8string();
|
||||
tinyxml2::XMLError successCode = document->LoadFile(path.c_str());
|
||||
|
||||
if (successCode == tinyxml2::XMLError::XML_SUCCESS) {
|
||||
_xmls.push_back(document);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
bool downloadAndParseWtmlFilesFromUrl(std::vector<tinyxml2::XMLDocument*>& _xmls,
|
||||
const std::filesystem::path& directory, const std::string& url,
|
||||
const std::string& fileName)
|
||||
{
|
||||
// Look for WWT image data folder, create folder if it doesn't exist
|
||||
if (!directoryExists(directory)) {
|
||||
std::string newDir = directory.string();
|
||||
// Remove the '/' at the end
|
||||
newDir.pop_back();
|
||||
LINFO("Creating directory" + newDir);
|
||||
std::filesystem::create_directory(newDir);
|
||||
}
|
||||
|
||||
// Download file from url
|
||||
std::filesystem::path file = directory.string() + fileName + ".aspx";
|
||||
if (!downloadFile(url, file)) {
|
||||
LINFO(
|
||||
fmt::format("Couldn't download file '{}' to directory '{}'", url, directory)
|
||||
);
|
||||
return false;
|
||||
}
|
||||
|
||||
// Parse file to XML
|
||||
using namespace tinyxml2;
|
||||
tinyxml2::XMLDocument* doc = new tinyxml2::XMLDocument();
|
||||
doc->LoadFile(file.string().c_str());
|
||||
|
||||
// Search XML file for folders with urls
|
||||
XMLElement* root = doc->RootElement();
|
||||
XMLElement* element = root->FirstChildElement(wwt::Folder.c_str());
|
||||
const bool folderExists = element;
|
||||
const bool folderContainNoUrls = folderExists && !hasAttribute(element, wwt::Url);
|
||||
|
||||
// If the file contains no folders, or there are folders but without urls,
|
||||
// stop recursion
|
||||
if (!folderExists || folderContainNoUrls) {
|
||||
_xmls.push_back(doc);
|
||||
LINFO("Saving " + url);
|
||||
return true;
|
||||
}
|
||||
|
||||
// Iterate through all the folders in the XML file
|
||||
while (element && std::string(element->Value()) == wwt::Folder) {
|
||||
// If folder contains urls, download and parse those urls
|
||||
if (hasAttribute(element, wwt::Url) && hasAttribute(element, wwt::Name)) {
|
||||
std::string url = attribute(element, wwt::Url);
|
||||
std::string fileName = attribute(element, wwt::Name);
|
||||
downloadAndParseWtmlFilesFromUrl(_xmls, directory, url, fileName);
|
||||
}
|
||||
element = element->NextSiblingElement();
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
WwtDataHandler::~WwtDataHandler() {
|
||||
// Call destructor of all allocated xmls
|
||||
_xmls.clear();
|
||||
}
|
||||
|
||||
void WwtDataHandler::loadImages(const std::string& root,
|
||||
const std::filesystem::path& directory)
|
||||
{
|
||||
// Collect the wtml files, either by reading from disc or from a url
|
||||
if (directoryExists(directory) && !std::filesystem::is_empty(directory)) {
|
||||
parseWtmlsFromDisc(_xmls, directory);
|
||||
LINFO("Loading images from directory");
|
||||
}
|
||||
else {
|
||||
downloadAndParseWtmlFilesFromUrl(_xmls, directory, root, "root");
|
||||
LINFO("Loading images from url");
|
||||
}
|
||||
|
||||
// Traverse through the collected wtml documents and collect the images
|
||||
for (tinyxml2::XMLDocument* doc : _xmls) {
|
||||
tinyxml2::XMLElement* root = doc->FirstChildElement();
|
||||
std::string collectionName = attribute(root, wwt::Name);
|
||||
saveImagesFromXml(root, collectionName);
|
||||
}
|
||||
|
||||
// Sort images in alphabetical order
|
||||
std::sort(
|
||||
_images.begin(),
|
||||
_images.end(),
|
||||
[](ImageData& a, ImageData& b) {
|
||||
// If the first character in the names are lowercase, make it upper case
|
||||
if (std::islower(a.name[0])) {
|
||||
// convert string to upper case
|
||||
a.name[0] = ::toupper(a.name[0]);
|
||||
}
|
||||
if (std::islower(b.name[0])) {
|
||||
b.name[0] = ::toupper(b.name[0]);
|
||||
}
|
||||
return a.name < b.name;
|
||||
}
|
||||
);
|
||||
|
||||
LINFO(fmt::format("Loaded {} WorldWide Telescope images.", _images.size()));
|
||||
}
|
||||
|
||||
int WwtDataHandler::nLoadedImages() const {
|
||||
return _images.size();
|
||||
}
|
||||
|
||||
const ImageData& WwtDataHandler::getImage(const int i) const {
|
||||
ghoul_assert(i < _images.size(), "Index outside of image vector boundaries!");
|
||||
return _images[i];
|
||||
}
|
||||
|
||||
void WwtDataHandler::saveImageFromNode(tinyxml2::XMLElement* node,
|
||||
std::string collection)
|
||||
{
|
||||
// Collect the image set of the node. The structure is different depending on if
|
||||
// it is a Place or an ImageSet
|
||||
std::string thumbnailUrl = { wwt::Undefined };
|
||||
tinyxml2::XMLElement* imageSet{ nullptr };
|
||||
std::string type = std::string(node->Name());
|
||||
|
||||
if (type == wwt::ImageSet) {
|
||||
thumbnailUrl = getChildNodeContentFromImageSet(node, wwt::ThumbnailUrl);
|
||||
imageSet = node;
|
||||
}
|
||||
else if (type == wwt::Place) {
|
||||
thumbnailUrl = getUrlFromPlace(node);
|
||||
imageSet = getChildNode(node, wwt::ImageSet);
|
||||
}
|
||||
|
||||
// Only collect the images that have a thumbnail image, that are sky images and
|
||||
// that have an image
|
||||
const bool hasThumbnailUrl = thumbnailUrl != wwt::Undefined;
|
||||
const bool isSkyImage = attribute(node, wwt::DataSetType) == wwt::Sky;
|
||||
const bool hasImageUrl = imageSet ? hasAttribute(imageSet, wwt::Url) : false;
|
||||
|
||||
if (!(hasThumbnailUrl && isSkyImage && hasImageUrl)) {
|
||||
return;
|
||||
}
|
||||
|
||||
// Collect name, image url and credits
|
||||
std::string name = attribute(node, wwt::Name);
|
||||
std::string imageUrl = attribute(imageSet, wwt::Url);
|
||||
std::string credits = getChildNodeContentFromImageSet(imageSet, wwt::Credits);
|
||||
std::string creditsUrl = getChildNodeContentFromImageSet(
|
||||
imageSet, wwt::CreditsUrl
|
||||
);
|
||||
|
||||
// Collect equatorial coordinates. All-sky surveys do not have this kind of
|
||||
// coordinate
|
||||
bool hasCelestialCoords = hasAttribute(node, wwt::RA) &&
|
||||
hasAttribute(node, wwt::Dec);
|
||||
glm::dvec2 equatorialSpherical{ 0.0 };
|
||||
glm::dvec3 equatorialCartesian{ 0.0 };
|
||||
|
||||
if (hasCelestialCoords) {
|
||||
// The RA from WWT is in the unit hours:
|
||||
// to convert to degrees, multiply with 360 (deg) /24 (h) = 15
|
||||
double ra = 15.0 * std::stod(attribute(node, wwt::RA));
|
||||
double dec = std::stod(attribute(node, wwt::Dec));
|
||||
equatorialSpherical = { ra, dec };
|
||||
equatorialCartesian = skybrowser::sphericalToCartesian(
|
||||
equatorialSpherical
|
||||
);
|
||||
}
|
||||
|
||||
// Collect field of view. The WWT definition of ZoomLevel is: VFOV = ZoomLevel / 6
|
||||
float fov = 0.f;
|
||||
if (hasAttribute(node, wwt::ZoomLevel)) {
|
||||
fov = std::stof(attribute(node, wwt::ZoomLevel)) / 6.0f;
|
||||
}
|
||||
|
||||
ImageData image = {
|
||||
name,
|
||||
thumbnailUrl,
|
||||
imageUrl,
|
||||
credits,
|
||||
creditsUrl,
|
||||
collection,
|
||||
hasCelestialCoords,
|
||||
fov,
|
||||
equatorialSpherical,
|
||||
equatorialCartesian,
|
||||
};
|
||||
|
||||
_images.push_back(image);
|
||||
}
|
||||
|
||||
void WwtDataHandler::saveImagesFromXml(tinyxml2::XMLElement* root,
|
||||
std::string collection)
|
||||
{
|
||||
// Get direct child of node called Place
|
||||
using namespace tinyxml2;
|
||||
XMLElement* node = root->FirstChildElement();
|
||||
|
||||
// Iterate through all siblings of node. If sibling is folder, open recursively.
|
||||
// If sibling is image, save it.
|
||||
while (node) {
|
||||
const std::string name = node->Name();
|
||||
// If node is an image or place, load it
|
||||
if (name == wwt::ImageSet || name == wwt::Place) {
|
||||
saveImageFromNode(node, collection);
|
||||
}
|
||||
// If node is another folder, open recursively
|
||||
else if (name == wwt::Folder) {
|
||||
std::string newCollectionName = collection + "/";
|
||||
newCollectionName += attribute(node, wwt::Name);
|
||||
|
||||
saveImagesFromXml(node, newCollectionName);
|
||||
}
|
||||
node = node->NextSiblingElement();
|
||||
}
|
||||
}
|
||||
} // namespace openspace
|
||||
@@ -72,6 +72,11 @@ public:
|
||||
void update() override;
|
||||
bool isReady() const override;
|
||||
|
||||
protected:
|
||||
properties::Vec2Property _dimensions;
|
||||
std::unique_ptr<BrowserInstance> _browserInstance;
|
||||
std::unique_ptr<ghoul::opengl::Texture> _texture;
|
||||
|
||||
private:
|
||||
class ScreenSpaceRenderHandler : public WebRenderHandler {
|
||||
public:
|
||||
@@ -81,21 +86,19 @@ private:
|
||||
void setTexture(GLuint t);
|
||||
};
|
||||
|
||||
CefRefPtr<ScreenSpaceRenderHandler> _renderHandler;
|
||||
|
||||
private:
|
||||
void bindTexture() override;
|
||||
|
||||
properties::StringProperty _url;
|
||||
properties::Vec2Property _dimensions;
|
||||
properties::TriggerProperty _reload;
|
||||
|
||||
CefRefPtr<ScreenSpaceRenderHandler> _renderHandler;
|
||||
CefRefPtr<WebKeyboardHandler> _keyboardHandler;
|
||||
std::unique_ptr<BrowserInstance> _browserInstance;
|
||||
std::unique_ptr<ghoul::opengl::Texture> _texture;
|
||||
|
||||
|
||||
bool _isUrlDirty = false;
|
||||
bool _isDimensionsDirty = false;
|
||||
};
|
||||
|
||||
} // namespace openspace
|
||||
|
||||
#endif // __OPENSPACE_MODULE_WEBBROWSER___SCREEN_SPACE_BROWSER___H__
|
||||
|
||||
@@ -1116,6 +1116,7 @@ void RenderEngine::removeScreenSpaceRenderable(ScreenSpaceRenderable* s) {
|
||||
|
||||
if (it != global::screenSpaceRenderables->end()) {
|
||||
global::eventEngine->publishEvent<events::EventScreenSpaceRenderableRemoved>(s);
|
||||
s->deinitializeGL();
|
||||
s->deinitialize();
|
||||
global::screenSpaceRootPropertyOwner->removePropertySubOwner(s);
|
||||
global::screenSpaceRenderables->erase(it);
|
||||
|
||||
@@ -152,84 +152,6 @@ namespace {
|
||||
return glm::mod(value - min, max - min) + min;
|
||||
}
|
||||
|
||||
glm::vec3 sanitizeSphericalCoordinates(glm::vec3 spherical) {
|
||||
const float r = spherical.x;
|
||||
float phi = spherical.z;
|
||||
|
||||
// Sanitize coordinates.
|
||||
float theta = wrap(spherical.y, 0.f, glm::two_pi<float>());
|
||||
if (theta > glm::pi<float>()) {
|
||||
theta = glm::two_pi<float>() - theta;
|
||||
phi += glm::pi<float>();
|
||||
}
|
||||
|
||||
return glm::vec3(r, theta, phi);
|
||||
}
|
||||
|
||||
glm::vec3 sphericalToCartesian(glm::vec3 spherical) {
|
||||
// First convert to ISO convention spherical coordinates according to
|
||||
// https://en.wikipedia.org/wiki/Spherical_coordinate_system
|
||||
// (radius, theta, phi), where theta is the polar angle from the z axis,
|
||||
// and phi is the azimuth.
|
||||
|
||||
const glm::vec3 sanitized = sanitizeSphericalCoordinates(std::move(spherical));
|
||||
const float x = sanitized[0] * sin(sanitized[1]) * cos(sanitized[2]);
|
||||
const float y = sanitized[0] * sin(sanitized[1]) * sin(sanitized[2]);
|
||||
const float z = sanitized[0] * cos(sanitized[1]);
|
||||
|
||||
// Now, convert rotate the coordinate system, so that z maps to y,
|
||||
// and y maps to -z. We want the pole to be in y instead of z.
|
||||
return glm::vec3(x, -z, y);
|
||||
}
|
||||
|
||||
glm::vec3 cartesianToSpherical(const glm::vec3& cartesian) {
|
||||
// Rotate cartesian coordinates.
|
||||
glm::vec3 rotated = glm::vec3(cartesian.x, cartesian.z, -cartesian.y);
|
||||
|
||||
const float r = sqrt(
|
||||
pow(rotated.x, 2.f) + pow(rotated.y, 2.f) + pow(rotated.z, 2.f)
|
||||
);
|
||||
const float theta = acos(rotated.z/r);
|
||||
const float phi = atan2(rotated.y, rotated.x);
|
||||
return sanitizeSphericalCoordinates(glm::vec3(r, theta, phi));
|
||||
}
|
||||
|
||||
// Radius, azimiuth, elevation to spherical coordinates.
|
||||
glm::vec3 raeToSpherical(glm::vec3 rae) {
|
||||
//return rae;
|
||||
const float r = rae.x;
|
||||
|
||||
// Polar angle, theta, is elevation + pi/2.
|
||||
const float theta = rae.z + glm::half_pi<float>();
|
||||
|
||||
// Azimuth in ISO spherical coordiantes (phi) is angle from x,
|
||||
// as opposed to from negative y on screen.
|
||||
const float phi = rae.y - glm::half_pi<float>();
|
||||
|
||||
return glm::vec3(r, theta, phi);
|
||||
}
|
||||
|
||||
// Spherical coordinates to radius, azimuth and elevation.
|
||||
glm::vec3 sphericalToRae(glm::vec3 spherical) {
|
||||
//return spherical;
|
||||
const float r = spherical.x;
|
||||
|
||||
// Azimuth on screen is angle from negative y, as opposed to from x.
|
||||
float azimuth = spherical.z + glm::half_pi<float>();
|
||||
|
||||
// Elevation is polar angle - pi/2
|
||||
float elevation = wrap(
|
||||
spherical.y - glm::half_pi<float>(),
|
||||
-glm::pi<float>(),
|
||||
glm::pi<float>()
|
||||
);
|
||||
|
||||
return glm::vec3(
|
||||
r,
|
||||
wrap(azimuth, -glm::pi<float>(), glm::pi<float>()),
|
||||
wrap(elevation, -glm::pi<float>(), glm::pi<float>())
|
||||
);
|
||||
}
|
||||
|
||||
struct [[codegen::Dictionary(ScreenSpaceRenderable)]] Parameters {
|
||||
// The type of the Screenspace renderable that is to be created. The available
|
||||
@@ -494,6 +416,18 @@ bool ScreenSpaceRenderable::isEnabled() const {
|
||||
return _enabled;
|
||||
}
|
||||
|
||||
bool ScreenSpaceRenderable::isUsingRaeCoords() const {
|
||||
return _useRadiusAzimuthElevation;
|
||||
}
|
||||
|
||||
bool ScreenSpaceRenderable::isFacingCamera() const {
|
||||
return _faceCamera;
|
||||
}
|
||||
|
||||
void ScreenSpaceRenderable::setEnabled(bool isEnabled) {
|
||||
_enabled = isEnabled;
|
||||
}
|
||||
|
||||
float ScreenSpaceRenderable::depth() {
|
||||
return _useRadiusAzimuthElevation ?
|
||||
_raePosition.value().x :
|
||||
@@ -539,20 +473,60 @@ glm::mat4 ScreenSpaceRenderable::scaleMatrix() {
|
||||
|
||||
glm::mat4 scale = glm::scale(
|
||||
glm::mat4(1.f),
|
||||
glm::vec3(_scale, _scale * textureRatio, 1.f)
|
||||
glm::vec3(_scale, textureRatio*_scale, 1.f)
|
||||
);
|
||||
|
||||
// Simulate orthographic projection by distance to plane.
|
||||
if (!_usePerspectiveProjection) {
|
||||
float distance = _useRadiusAzimuthElevation ?
|
||||
_raePosition.value().x :
|
||||
-_cartesianPosition.value().z;
|
||||
scale = glm::scale(scale, glm::vec3(distance));
|
||||
}
|
||||
|
||||
return scale;
|
||||
}
|
||||
|
||||
glm::vec2 ScreenSpaceRenderable::screenSpacePosition() {
|
||||
return glm::vec2(_cartesianPosition.value());
|
||||
}
|
||||
|
||||
glm::vec2 ScreenSpaceRenderable::screenSpaceDimensions() {
|
||||
float ratio = static_cast<float>(_objectSize.x) / static_cast<float>(_objectSize.y);
|
||||
return glm::vec2(2.f * _scale * ratio, 2.f * _scale);
|
||||
}
|
||||
|
||||
glm::vec2 ScreenSpaceRenderable::upperRightCornerScreenSpace() {
|
||||
return screenSpacePosition() + (screenSpaceDimensions() / 2.0f);
|
||||
}
|
||||
|
||||
glm::vec2 ScreenSpaceRenderable::lowerLeftCornerScreenSpace() {
|
||||
return screenSpacePosition() - (screenSpaceDimensions() / 2.0f);
|
||||
}
|
||||
|
||||
bool ScreenSpaceRenderable::isIntersecting(glm::vec2 coord) {
|
||||
bool isUnderTopBorder = coord.x < upperRightCornerScreenSpace().x;
|
||||
bool isLeftToRightBorder = coord.y < upperRightCornerScreenSpace().y;
|
||||
bool isRightToLeftBorder = coord.x > lowerLeftCornerScreenSpace().x;
|
||||
bool isOverBottomBorder = coord.y > lowerLeftCornerScreenSpace().y;
|
||||
|
||||
return isUnderTopBorder && isLeftToRightBorder &&
|
||||
isRightToLeftBorder && isOverBottomBorder;
|
||||
}
|
||||
|
||||
void ScreenSpaceRenderable::translate(glm::vec2 translation, glm::vec2 position) {
|
||||
glm::mat4 translationMatrix = glm::translate(
|
||||
glm::mat4(1.f),
|
||||
glm::vec3(translation, 0.0f)
|
||||
);
|
||||
glm::vec4 origin = glm::vec4(position, _cartesianPosition.value().z, 1.0f);
|
||||
_cartesianPosition = translationMatrix * origin;
|
||||
}
|
||||
|
||||
void ScreenSpaceRenderable::setCartesianPosition(const glm::vec3& position) {
|
||||
_cartesianPosition = position;
|
||||
}
|
||||
|
||||
void ScreenSpaceRenderable::setRaeFromCartesianPosition(const glm::vec3& position) {
|
||||
_raePosition = cartesianToRae(position);
|
||||
}
|
||||
|
||||
glm::vec3 ScreenSpaceRenderable::raePosition() const {
|
||||
return _raePosition;
|
||||
}
|
||||
|
||||
glm::mat4 ScreenSpaceRenderable::globalRotationMatrix() {
|
||||
// We do not want the screen space planes to be affected by
|
||||
// 1) The global rotation of the view applied in the render engine
|
||||
@@ -587,6 +561,14 @@ glm::mat4 ScreenSpaceRenderable::localRotationMatrix() {
|
||||
return rotation * glm::mat4(glm::quat(glm::vec3(pitch, yaw, roll)));
|
||||
}
|
||||
|
||||
glm::vec3 ScreenSpaceRenderable::raeToCartesian(const glm::vec3& rae) const {
|
||||
return sphericalToCartesian(raeToSpherical(rae));
|
||||
}
|
||||
|
||||
glm::vec3 ScreenSpaceRenderable::cartesianToRae(const glm::vec3& cartesian) const {
|
||||
return sphericalToRae(cartesianToSpherical(cartesian));
|
||||
}
|
||||
|
||||
glm::mat4 ScreenSpaceRenderable::translationMatrix() {
|
||||
glm::vec3 translation = _useRadiusAzimuthElevation ?
|
||||
sphericalToCartesian(raeToSpherical(_raePosition)) :
|
||||
@@ -625,8 +607,87 @@ void ScreenSpaceRenderable::draw(glm::mat4 modelTransform) {
|
||||
|
||||
void ScreenSpaceRenderable::unbindTexture() {}
|
||||
|
||||
glm::vec3 ScreenSpaceRenderable::sanitizeSphericalCoordinates(glm::vec3 spherical) const {
|
||||
const float r = spherical.x;
|
||||
float phi = spherical.z;
|
||||
|
||||
// Sanitize coordinates.
|
||||
float theta = wrap(spherical.y, 0.f, glm::two_pi<float>());
|
||||
if (theta > glm::pi<float>()) {
|
||||
theta = glm::two_pi<float>() - theta;
|
||||
phi += glm::pi<float>();
|
||||
}
|
||||
|
||||
return glm::vec3(r, theta, phi);
|
||||
}
|
||||
|
||||
glm::vec3 ScreenSpaceRenderable::sphericalToCartesian(glm::vec3 spherical) const {
|
||||
// First convert to ISO convention spherical coordinates according to
|
||||
// https://en.wikipedia.org/wiki/Spherical_coordinate_system
|
||||
// (radius, theta, phi), where theta is the polar angle from the z axis,
|
||||
// and phi is the azimuth.
|
||||
|
||||
const glm::vec3 sanitized = sanitizeSphericalCoordinates(std::move(spherical));
|
||||
const float x = sanitized[0] * sin(sanitized[1]) * cos(sanitized[2]);
|
||||
const float y = sanitized[0] * sin(sanitized[1]) * sin(sanitized[2]);
|
||||
const float z = sanitized[0] * cos(sanitized[1]);
|
||||
|
||||
// Now, convert rotate the coordinate system, so that z maps to y,
|
||||
// and y maps to -z. We want the pole to be in y instead of z.
|
||||
return glm::vec3(x, -z, y);
|
||||
}
|
||||
|
||||
glm::vec3 ScreenSpaceRenderable::cartesianToSpherical(const glm::vec3& cartesian) const {
|
||||
// Rotate cartesian coordinates.
|
||||
glm::vec3 rotated = glm::vec3(cartesian.x, cartesian.z, -cartesian.y);
|
||||
|
||||
const float r = sqrt(
|
||||
pow(rotated.x, 2.f) + pow(rotated.y, 2.f) + pow(rotated.z, 2.f)
|
||||
);
|
||||
const float theta = acos(rotated.z / r);
|
||||
const float phi = atan2(rotated.y, rotated.x);
|
||||
return sanitizeSphericalCoordinates(glm::vec3(r, theta, phi));
|
||||
}
|
||||
|
||||
// Radius, azimiuth, elevation to spherical coordinates.
|
||||
glm::vec3 ScreenSpaceRenderable::raeToSpherical(glm::vec3 rae) const {
|
||||
//return rae;
|
||||
const float r = rae.x;
|
||||
|
||||
// Polar angle, theta, is elevation + pi/2.
|
||||
const float theta = rae.z + glm::half_pi<float>();
|
||||
|
||||
// Azimuth in ISO spherical coordiantes (phi) is angle from x,
|
||||
// as opposed to from negative y on screen.
|
||||
const float phi = rae.y - glm::half_pi<float>();
|
||||
|
||||
return glm::vec3(r, theta, phi);
|
||||
}
|
||||
|
||||
// Spherical coordinates to radius, azimuth and elevation.
|
||||
glm::vec3 ScreenSpaceRenderable::sphericalToRae(glm::vec3 spherical) const {
|
||||
//return spherical;
|
||||
const float r = spherical.x;
|
||||
|
||||
// Azimuth on screen is angle from negative y, as opposed to from x.
|
||||
float azimuth = spherical.z + glm::half_pi<float>();
|
||||
|
||||
// Elevation is polar angle - pi/2
|
||||
float elevation = wrap(
|
||||
spherical.y - glm::half_pi<float>(),
|
||||
-glm::pi<float>(),
|
||||
glm::pi<float>()
|
||||
);
|
||||
|
||||
return glm::vec3(
|
||||
r,
|
||||
wrap(azimuth, -glm::pi<float>(), glm::pi<float>()),
|
||||
wrap(elevation, -glm::pi<float>(), glm::pi<float>())
|
||||
);
|
||||
}
|
||||
|
||||
|
||||
float ScreenSpaceRenderable::opacity() const {
|
||||
return _opacity * _fade;
|
||||
}
|
||||
|
||||
} // namespace openspace
|
||||
|
||||
Reference in New Issue
Block a user