From e78121febc18722680adb47d2f05ec692b288163 Mon Sep 17 00:00:00 2001 From: Emil Axelsson Date: Wed, 24 Apr 2019 13:34:01 +0200 Subject: [PATCH] Feature/screenspace renderables (#830) * Add ability to position screen space renderables in 3D * Independent face camera setting * More intuitive local rotation * Cleanup * Introduce global and master rotation. Remove DisableSceneOnMaster. Revisit screenspace renderables. --- data/assets/examples/slidedeck.asset | 6 +- include/openspace/engine/configuration.h | 4 +- include/openspace/rendering/renderengine.h | 10 +- .../rendering/screenspacerenderable.h | 52 +-- .../base/rendering/screenspaceframebuffer.cpp | 5 +- modules/base/shaders/screenspace_fs.glsl | 6 +- modules/base/shaders/screenspace_vs.glsl | 4 +- modules/webbrowser/src/screenspacebrowser.cpp | 7 +- openspace.cfg | 6 +- src/engine/configuration.cpp | 19 +- src/engine/configuration_doc.inl | 28 +- src/rendering/renderengine.cpp | 112 +++++- src/rendering/screenspacerenderable.cpp | 370 +++++++++++------- 13 files changed, 417 insertions(+), 212 deletions(-) diff --git a/data/assets/examples/slidedeck.asset b/data/assets/examples/slidedeck.asset index 2db61a0b7a..14073d5251 100644 --- a/data/assets/examples/slidedeck.asset +++ b/data/assets/examples/slidedeck.asset @@ -3,8 +3,10 @@ local deck = nil asset.onInitialize(function () deck = helper.createDeck("example", { - FlatScreen = false, - SphericalPosition = {0.0, 3.1415 / 2}, + UseRadiusAzimuthElevation = true, + RadiusAzimuthElevation = {1.0, 0.0, 0.0}, -- use for dome + UsePerspectiveProjection = true, + FaceCamera = true, Scale = 0.7 }) diff --git a/include/openspace/engine/configuration.h b/include/openspace/engine/configuration.h index 7868fd9db4..24e14f219f 100644 --- a/include/openspace/engine/configuration.h +++ b/include/openspace/engine/configuration.h @@ -85,7 +85,9 @@ struct Configuration { bool usePerSceneCache = false; bool isRenderingOnMasterDisabled = false; - bool isSceneTranslationOnMasterDisabled = false; + glm::dvec3 globalRotation; + glm::dvec3 screenSpaceRotation; + glm::dvec3 masterRotation; bool isConsoleDisabled = false; std::map moduleConfigurations; diff --git a/include/openspace/rendering/renderengine.h b/include/openspace/rendering/renderengine.h index fd71f3880d..311c352a40 100644 --- a/include/openspace/rendering/renderengine.h +++ b/include/openspace/rendering/renderengine.h @@ -31,6 +31,7 @@ #include #include #include +#include #include namespace ghoul { @@ -150,6 +151,10 @@ public: glm::ivec2 renderingResolution() const; glm::ivec2 fontResolution() const; + glm::mat4 globalRotation() const; + glm::mat4 screenSpaceRotation() const; + glm::mat4 nodeRotation() const; + private: void setRenderer(std::unique_ptr renderer); RendererImplementation rendererFromString(const std::string& renderingMethod) const; @@ -181,7 +186,6 @@ private: properties::BoolProperty _applyWarping; properties::BoolProperty _showFrameNumber; properties::BoolProperty _disableMasterRendering; - properties::BoolProperty _disableSceneTranslationOnMaster; float _globalBlackOutFactor = 1.f; float _fadeDuration = 2.f; @@ -192,6 +196,10 @@ private: properties::FloatProperty _hdrBackground; properties::FloatProperty _gamma; + properties::Vec3Property _globalRotation; + properties::Vec3Property _screenSpaceRotation; + properties::Vec3Property _masterRotation; + uint64_t _frameNumber = 0; std::vector _programs; diff --git a/include/openspace/rendering/screenspacerenderable.h b/include/openspace/rendering/screenspacerenderable.h index 773387baef..6f780f29b4 100644 --- a/include/openspace/rendering/screenspacerenderable.h +++ b/include/openspace/rendering/screenspacerenderable.h @@ -30,7 +30,7 @@ #include #include #include -#include +#include #include #include #include @@ -45,7 +45,7 @@ namespace documentation { struct Documentation; } * The base class for screen space images and screen space framebuffers. * This base class handles general functionality specific to planes that are rendered in * front of the camera. It implements protected methods and properties for converting - * the planes from Spherical to Euclidean coordinates and back. It also specifies the + * the planes from Spherical to Cartesian coordinates and back. It also specifies the * interface that its children need to implement. */ class ScreenSpaceRenderable : public properties::PropertyOwner { @@ -69,36 +69,16 @@ public: virtual void update(); virtual bool isReady() const; bool isEnabled() const; - - glm::vec3 euclideanPosition() const; - glm::vec3 sphericalPosition() const; - float depth() const; + float depth(); static documentation::Documentation Documentation(); protected: - void useEuclideanCoordinates(bool b); - - /** - * Converts Spherical coordinates to Euclidean. - * \param spherical The coordinates theta and phi - * \param radius The radius position value of the plane - * \return The x and y position value of the plane - */ - //glm::vec2 toEuclidean(const glm::vec2& spherical, float radius); - - /** - * Converts Euclidean coordinates to Spherical. - * \param euclidean The coordinates x and y - * \return The spherical coordinates theta and phi. - */ - //glm::vec2 toSpherical(const glm::vec2& euclidean); - - void createShaders(); glm::mat4 scaleMatrix(); - glm::mat4 rotationMatrix(); + glm::mat4 globalRotationMatrix(); glm::mat4 translationMatrix(); + glm::mat4 localRotationMatrix(); void draw(glm::mat4 modelTransform); @@ -106,10 +86,21 @@ protected: virtual void unbindTexture(); properties::BoolProperty _enabled; - properties::BoolProperty _useFlatScreen; - properties::Vec2Property _euclideanPosition; - properties::Vec2Property _sphericalPosition; - properties::FloatProperty _depth; + properties::BoolProperty _usePerspectiveProjection; + properties::BoolProperty _useRadiusAzimuthElevation; + properties::BoolProperty _faceCamera; + + // x, y, z + properties::Vec3Property _cartesianPosition; + + // Radius, azimuth, elevation, + // where azimuth is relative to negative y axis and + // elevation is angle from plane with normal z. + properties::Vec3Property _raePosition; + + // Local rotation (roll, pitch, yaw) + properties::Vec3Property _localRotation; + properties::FloatProperty _scale; properties::FloatProperty _alpha; properties::TriggerProperty _delete; @@ -118,10 +109,7 @@ protected: UniformCache(occlusionDepth, alpha, modelTransform, viewProj, texture) _uniformCache; std::unique_ptr _shader; - bool _useEuclideanCoordinates = true; glm::vec2 _originalViewportSize; - - float _radius; }; } // namespace openspace diff --git a/modules/base/rendering/screenspaceframebuffer.cpp b/modules/base/rendering/screenspaceframebuffer.cpp index 3761e9458c..28ba4d80e0 100644 --- a/modules/base/rendering/screenspaceframebuffer.cpp +++ b/modules/base/rendering/screenspaceframebuffer.cpp @@ -134,13 +134,14 @@ void ScreenSpaceFramebuffer::render() { static_cast(resolution.y) ); - const glm::mat4 rotation = rotationMatrix(); + const glm::mat4 globalRotation = globalRotationMatrix(); const glm::mat4 translation = translationMatrix(); + const glm::mat4 localRotation = localRotationMatrix(); const glm::mat4 scale = glm::scale( scaleMatrix(), glm::vec3((1.f / xratio), (1.f / yratio), 1.f) ); - const glm::mat4 modelTransform = rotation*translation*scale; + const glm::mat4 modelTransform = globalRotation*translation*localRotation*scale; draw(modelTransform); } } diff --git a/modules/base/shaders/screenspace_fs.glsl b/modules/base/shaders/screenspace_fs.glsl index 4f88ec4be0..6d4e66856c 100644 --- a/modules/base/shaders/screenspace_fs.glsl +++ b/modules/base/shaders/screenspace_fs.glsl @@ -36,15 +36,13 @@ uniform float Alpha; Fragment getFragment() { Fragment frag; - // power scale coordinates for depth. w value is set to 1.0. - float depth = (1.0 + log(abs(OcclusionDepth) + 1/pow(k, 1.0))/log(k)) / 27.0; frag.color = texture(texture1, vs_st); - frag.color.a = (frag.color.a != 0.0) ? Alpha : frag.color.a; + frag.color.a = Alpha * frag.color.a; if (frag.color.a == 0.0) { discard; } - frag.depth = denormalizeFloat(depth); + frag.depth = vs_position.z; return frag; } diff --git a/modules/base/shaders/screenspace_vs.glsl b/modules/base/shaders/screenspace_vs.glsl index 594cc5ad1b..075110baf2 100644 --- a/modules/base/shaders/screenspace_vs.glsl +++ b/modules/base/shaders/screenspace_vs.glsl @@ -24,7 +24,7 @@ #version __CONTEXT__ -layout(location = 0) in vec2 in_position; +layout(location = 0) in vec3 in_position; layout(location = 1) in vec2 in_st; out vec2 vs_st; @@ -36,6 +36,6 @@ uniform mat4 ViewProjectionMatrix; void main() { vs_st = in_st; - vs_position = ViewProjectionMatrix * ModelTransform * vec4(in_position, 0.0, 1.0); + vs_position = ViewProjectionMatrix * ModelTransform * vec4(in_position, 1.0); gl_Position = vec4(vs_position); } diff --git a/modules/webbrowser/src/screenspacebrowser.cpp b/modules/webbrowser/src/screenspacebrowser.cpp index 494b101a8d..21c4e622bf 100644 --- a/modules/webbrowser/src/screenspacebrowser.cpp +++ b/modules/webbrowser/src/screenspacebrowser.cpp @@ -137,7 +137,12 @@ void ScreenSpaceBrowser::render() { return; } _renderHandler->updateTexture(); - draw(rotationMatrix() * translationMatrix() * scaleMatrix()); + draw( + globalRotationMatrix() * + translationMatrix() * + localRotationMatrix() * + scaleMatrix() + ); } void ScreenSpaceBrowser::update() { diff --git a/openspace.cfg b/openspace.cfg index f16cda6c48..6dc78bf322 100644 --- a/openspace.cfg +++ b/openspace.cfg @@ -166,12 +166,16 @@ LogEachOpenGLCall = false ShutdownCountdown = 3 ScreenshotUseDate = true + -- OnScreenTextScaling = "framebuffer" -- PerSceneCache = true -- DisableRenderingOnMaster = true --- DisableSceneOnMaster = true -- DisableInGameConsole = true +GlobalRotation = {0.0, 0.0, 0.0} +MasterRotation = {0.0, 0.0, 0.0} +ScreenSpaceRotation = {0.0, 0.0, 0.0} + RenderingMethod = "Framebuffer" OpenGLDebugContext = { Activate = false, diff --git a/src/engine/configuration.cpp b/src/engine/configuration.cpp index cb2038692c..d8d8682c51 100644 --- a/src/engine/configuration.cpp +++ b/src/engine/configuration.cpp @@ -63,7 +63,9 @@ namespace { constexpr const char* KeyOnScreenTextScaling = "OnScreenTextScaling"; constexpr const char* KeyRenderingMethod = "RenderingMethod"; constexpr const char* KeyDisableRenderingOnMaster = "DisableRenderingOnMaster"; - constexpr const char* KeyDisableSceneOnMaster = "DisableSceneOnMaster"; + constexpr const char* KeyGlobalRotation = "GlobalRotation"; + constexpr const char* KeyScreenSpaceRotation = "ScreenSpaceRotation"; + constexpr const char* KeyMasterRotation = "MasterRotation"; constexpr const char* KeyDisableInGameConsole = "DisableInGameConsole"; constexpr const char* KeyScreenshotUseDate = "ScreenshotUseDate"; constexpr const char* KeyHttpProxy = "HttpProxy"; @@ -127,8 +129,16 @@ namespace { ); } + if constexpr (std::is_same_v) { + ghoul::Dictionary d = ghoul::lua::value(L); + glm::dvec3 res; + res.x = d.value("1"); + res.y = d.value("2"); + res.z = d.value("3"); + value = res; + } // NOLINTNEXTLINE - if constexpr (std::is_same_v>) { + else if constexpr (std::is_same_v>) { ghoul::Dictionary d = ghoul::lua::value(L); std::vector res; @@ -288,7 +298,10 @@ void parseLuaState(Configuration& configuration) { getValue(s, KeyOnScreenTextScaling, c.onScreenTextScaling); getValue(s, KeyPerSceneCache, c.usePerSceneCache); getValue(s, KeyDisableRenderingOnMaster, c.isRenderingOnMasterDisabled); - getValue(s, KeyDisableSceneOnMaster, c.isSceneTranslationOnMasterDisabled); + + getValue(s, KeyGlobalRotation, c.globalRotation); + getValue(s, KeyScreenSpaceRotation, c.screenSpaceRotation); + getValue(s, KeyMasterRotation, c.masterRotation); getValue(s, KeyDisableInGameConsole, c.isConsoleDisabled); getValue(s, KeyRenderingMethod, c.renderingMethod); diff --git a/src/engine/configuration_doc.inl b/src/engine/configuration_doc.inl index ca3d2e1b73..184d357b8d 100644 --- a/src/engine/configuration_doc.inl +++ b/src/engine/configuration_doc.inl @@ -257,14 +257,28 @@ documentation::Documentation Configuration::Documentation = { "the master computer does not have the resources to render a scene." }, { - KeyDisableSceneOnMaster, - new BoolVerifier, + KeyGlobalRotation, + new DoubleVector3Verifier, Optional::Yes, - "Toggles whether a potential scene transformation matrix, for example as " - "specified in an SGCT configuration file, should apply to the master node. " - "With some configurations, applying such a transformation complicates the " - "interaction and it is thus desired to disable the transformation. The " - "default is false." + "Applies a global view rotation. Use this to rotate the position of the " + "focus node away from the default location on the screen. This setting " + "persists even when a new focus node is selected. Defined using roll, pitch, " + "yaw in radians" + }, + { + KeyMasterRotation, + new DoubleVector3Verifier, + Optional::Yes, + "Applies a view rotation for only the master node, defined using " + "roll, pitch yaw in radians. This can be used to compensate the master view " + "direction for tilted display systems in clustered immersive environments." + }, + { + KeyScreenSpaceRotation, + new DoubleVector3Verifier, + Optional::Yes, + "Applies a global rotation for all screenspace renderables. Defined using " + "roll, pitch, yaw in radians." }, { KeyScreenshotUseDate, diff --git a/src/rendering/renderengine.cpp b/src/rendering/renderengine.cpp index bac7b78e7c..f98fcc6fe4 100644 --- a/src/rendering/renderengine.cpp +++ b/src/rendering/renderengine.cpp @@ -158,14 +158,28 @@ namespace { "master node is not required and performance can be gained by disabling it." }; - constexpr openspace::properties::Property::PropertyInfo DisableTranslationInfo = { - "DisableSceneTranslationOnMaster", - "Disable Scene Translation on Master", - "If this value is enabled, any scene translations such as specified in, for " - "example an SGCT configuration, is disabled for the master node. This setting " - "can be useful if a planetarium environment requires a scene translation to be " - "applied, which would otherwise make interacting through the master node " - "difficult." + constexpr openspace::properties::Property::PropertyInfo GlobalRotationInfo = { + "GlobalRotation", + "Global Rotation", + "Applies a global view rotation. Use this to rotate the position of the " + "focus node away from the default location on the screen. This setting " + "persists even when a new focus node is selected. Defined using pitch, yaw, " + "roll in radians" + }; + + constexpr openspace::properties::Property::PropertyInfo ScreenSpaceRotationInfo = { + "ScreenSpaceRotation", + "Screen Space Rotation", + "Applies a rotation to all screen space renderables. " + "Defined using pitch, yaw, roll in radians." + }; + + constexpr openspace::properties::Property::PropertyInfo MasterRotationInfo = { + "MasterRotation", + "Master Rotation", + "Applies a view rotation for only the master node, defined using " + "pitch, yaw, roll in radians. This can be used to compensate the master view " + "direction for tilted display systems in clustered immersive environments." }; constexpr openspace::properties::Property::PropertyInfo AaSamplesInfo = { @@ -211,7 +225,24 @@ RenderEngine::RenderEngine() , _applyWarping(ApplyWarpingInfo, false) , _showFrameNumber(ShowFrameNumberInfo, false) , _disableMasterRendering(DisableMasterInfo, false) - , _disableSceneTranslationOnMaster(DisableTranslationInfo, false) + , _globalRotation( + GlobalRotationInfo, + glm::vec3(0.f), + glm::vec3(-glm::pi()), + glm::vec3(glm::pi()) + ) + , _screenSpaceRotation( + ScreenSpaceRotationInfo, + glm::vec3(0.f), + glm::vec3(-glm::pi()), + glm::vec3(glm::pi()) + ) + , _masterRotation( + MasterRotationInfo, + glm::vec3(0.f), + glm::vec3(-glm::pi()), + glm::vec3(glm::pi()) + ) , _nAaSamples(AaSamplesInfo, 4, 1, 8) , _hdrExposure(HDRExposureInfo, 0.4f, 0.01f, 10.0f) , _hdrBackground(BackgroundExposureInfo, 2.8f, 0.01f, 10.0f) @@ -264,7 +295,9 @@ RenderEngine::RenderEngine() addProperty(_showFrameNumber); - addProperty(_disableSceneTranslationOnMaster); + addProperty(_globalRotation); + addProperty(_screenSpaceRotation); + addProperty(_masterRotation); addProperty(_disableMasterRendering); } @@ -294,8 +327,10 @@ void RenderEngine::setRendererFromString(const std::string& renderingMethod) { void RenderEngine::initialize() { // We have to perform these initializations here as the OsEng has not been initialized // in our constructor - _disableSceneTranslationOnMaster = - global::configuration.isSceneTranslationOnMasterDisabled; + _globalRotation = static_cast(global::configuration.globalRotation); + _screenSpaceRotation = + static_cast(global::configuration.screenSpaceRotation); + _masterRotation = static_cast(global::configuration.masterRotation); _disableMasterRendering = global::configuration.isRenderingOnMasterDisabled; #ifdef GHOUL_USE_DEVIL @@ -450,6 +485,39 @@ glm::ivec2 RenderEngine::fontResolution() const { } } +glm::mat4 RenderEngine::globalRotation() const { + glm::vec3 rot = _globalRotation; + + glm::quat pitch = glm::angleAxis(rot.x, glm::vec3(1.f, 0.f, 0.f)); + glm::quat yaw = glm::angleAxis(rot.y, glm::vec3(0.f, 1.f, 0.f)); + glm::quat roll = glm::angleAxis(rot.z, glm::vec3(0.f, 0.f, 1.f)); + + return glm::mat4_cast(glm::normalize(pitch * yaw * roll)); +} + +glm::mat4 RenderEngine::screenSpaceRotation() const { + glm::vec3 rot = _screenSpaceRotation; + + glm::quat pitch = glm::angleAxis(rot.x, glm::vec3(1.f, 0.f, 0.f)); + glm::quat yaw = glm::angleAxis(rot.y, glm::vec3(0.f, 1.f, 0.f)); + glm::quat roll = glm::angleAxis(rot.z, glm::vec3(0.f, 0.f, 1.f)); + + return glm::mat4_cast(glm::normalize(pitch * yaw * roll)); +} + +glm::mat4 RenderEngine::nodeRotation() const { + if (!global::windowDelegate.isMaster()) { + return glm::mat4(1.f); + } + glm::vec3 rot = _masterRotation; + + glm::quat pitch = glm::angleAxis(rot.x, glm::vec3(1.f, 0.f, 0.f)); + glm::quat yaw = glm::angleAxis(rot.y, glm::vec3(0.f, 1.f, 0.f)); + glm::quat roll = glm::angleAxis(rot.z, glm::vec3(0.f, 0.f, 1.f)); + + return glm::mat4_cast(glm::normalize(pitch * yaw * roll)); +} + void RenderEngine::updateFade() { // Temporary fade funtionality constexpr const float FadedIn = 1.0; @@ -492,15 +560,18 @@ void RenderEngine::render(const glm::mat4& sceneMatrix, const glm::mat4& viewMat const glm::mat4& projectionMatrix) { LTRACE("RenderEngine::render(begin)"); + const WindowDelegate& delegate = global::windowDelegate; + + const glm::mat4 globalRot = globalRotation(); + const glm::mat4 nodeRot = nodeRotation(); + glm::mat4 combinedGlobalRot = nodeRot * globalRot; + if (_camera) { - if (_disableSceneTranslationOnMaster && delegate.isMaster()) { - _camera->sgctInternal.setViewMatrix(viewMatrix); - } - else { - _camera->sgctInternal.setViewMatrix(viewMatrix * sceneMatrix); - _camera->sgctInternal.setSceneMatrix(sceneMatrix); - } + _camera->sgctInternal.setViewMatrix( + viewMatrix * combinedGlobalRot * sceneMatrix + ); + _camera->sgctInternal.setSceneMatrix(combinedGlobalRot * sceneMatrix); _camera->sgctInternal.setProjectionMatrix(projectionMatrix); _camera->invalidateCache(); } @@ -539,7 +610,8 @@ void RenderEngine::render(const glm::mat4& sceneMatrix, const glm::mat4& viewMat ssrs.begin(), ssrs.end(), [](ScreenSpaceRenderable* lhs, ScreenSpaceRenderable* rhs) { - return lhs->depth() < rhs->depth(); + // Render back to front. + return lhs->depth() > rhs->depth(); } ); diff --git a/src/rendering/screenspacerenderable.cpp b/src/rendering/screenspacerenderable.cpp index b29744be24..60a8bb8f8e 100644 --- a/src/rendering/screenspacerenderable.cpp +++ b/src/rendering/screenspacerenderable.cpp @@ -41,7 +41,6 @@ namespace { constexpr const char* KeyType = "Type"; constexpr const char* KeyTag = "Tag"; - constexpr const float PlaneDepth = -2.f; constexpr const std::array UniformNames = { "OcclusionDepth", "Alpha", "ModelTransform", "ViewProjectionMatrix", "texture1" @@ -53,38 +52,38 @@ namespace { "This setting determines whether this sceen space plane will be visible or not." }; - constexpr openspace::properties::Property::PropertyInfo FlatScreenInfo = { - "FlatScreen", - "Flat Screen specification", + constexpr openspace::properties::Property::PropertyInfo + UseRadiusAzimuthElevationInfo = { + "UseRadiusAzimuthElevation", + "Use Radius Azimuth and Elevation", "This value determines whether the location of this screen space plane will be " - "specified in a two-dimensional Euclidean plane (if this is set to 'true') or " - "specified in spherical coordinates. By switching this value, the correct " - "property will be shown or hidden. The Euclidean coordinate system is useful if " - "a regular rendering is applied, whereas the spherical coordinates are most " - "useful in a planetarium environment." + "specified using radius, azimuth and elevation (if this is set to 'true') or " + "using cartesian coordinates. By switching this value, the correct property will " + "be shown or hidden. The Cartesian coordinate system is useful if a regular " + "rendering is applied, whereas the radius azimuth elevation are most useful in a " + "planetarium environment." }; - constexpr openspace::properties::Property::PropertyInfo EuclideanPositionInfo = { - "EuclideanPosition", - "Euclidean coordinates", - "This value determines the position of this screen space plane in Euclidean " - "two-dimensional coordinates." + constexpr openspace::properties::Property::PropertyInfo + UsePerspectiveProjectionInfo = { + "UsePerspectiveProjection", + "Use Perspective Projection", + "Determines whetether the z/radius values affects the size of the plane or not." }; - constexpr openspace::properties::Property::PropertyInfo SphericalPositionInfo = { - "SphericalPosition", - "Spherical coordinates", - "This value determines the position of this screen space plane in a spherical " - "coordinate system." + constexpr openspace::properties::Property::PropertyInfo CartesianPositionInfo = { + "CartesianPosition", + "Cartesian coordinates", + "This value determines the position of this screen space plane in Cartesian " + "three-dimensional coordinates (meters)." }; - constexpr openspace::properties::Property::PropertyInfo DepthInfo = { - "Depth", - "Depth value", - "This value determines the depth of the plane. This value does not change the " - "apparent size of the plane, but is only used to sort the planes correctly. The " - "plane with a lower value will be shown in front of a plane with a higher depth " - "value." + constexpr openspace::properties::Property::PropertyInfo RadiusAzimuthElevationInfo = { + "RadiusAzimuthElevation", + "Radius Azimuth Elevation", + "This value determines the position of this screen space plane in a " + "coordinate system based on radius (meters), azimuth (radians) and elevation " + "(radians)." }; constexpr openspace::properties::Property::PropertyInfo ScaleInfo = { @@ -95,6 +94,13 @@ namespace { "the image being displayed." }; + constexpr openspace::properties::Property::PropertyInfo LocalRotationInfo = { + "Rotation", + "Local rotation", + "An euler rotation (x, y, z) to apply to the plane." + }; + + constexpr openspace::properties::Property::PropertyInfo AlphaInfo = { "Alpha", "Transparency", @@ -110,22 +116,98 @@ namespace { "scene." }; - glm::vec2 toEuclidean(const glm::vec2& spherical, float r) { - const float x = r * sin(spherical[0]) * sin(spherical[1]); - const float y = r * cos(spherical[1]); + constexpr openspace::properties::Property::PropertyInfo FaceCameraInfo = { + "FaceCamera", + "Face Camera", + "If enabled, the local rotation is applied after the plane is rotated to face " + "the camera." + }; - return glm::vec2(x, y); + float wrap(float value, float min, float max) { + return glm::mod(value - min, max - min) + min; } - glm::vec3 toSpherical(const glm::vec2& euclidean) { - const float r = -sqrt( - pow(euclidean[0], 2) + pow(euclidean[1], 2) + pow(PlaneDepth, 2) + glm::vec3 sanitizeSphericalCoordinates(glm::vec3 spherical) { + const float r = spherical.x; + float phi = spherical.z; + + // Sanitize coordinates. + float theta = wrap(spherical.y, 0.0, glm::two_pi()); + if (theta > glm::pi()) { + theta = glm::two_pi() - theta; + phi += glm::pi(); + } + + 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 = atan2(-PlaneDepth, euclidean[0]) - glm::half_pi(); - const float phi = acos(euclidean[1] / r); - - return glm::vec3(theta, phi, r); + 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(); + + // 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(); + + 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(); + + // Elevation is polar angle - pi/2 + float elevation = wrap( + spherical.y - glm::half_pi(), + -glm::pi(), + glm::pi() + ); + + return glm::vec3( + r, + wrap(azimuth, -glm::pi(), glm::pi()), + wrap(elevation, -glm::pi(), glm::pi()) + ); + } + + } // namespace namespace openspace { @@ -169,28 +251,28 @@ documentation::Documentation ScreenSpaceRenderable::Documentation() { EnabledInfo.description }, { - FlatScreenInfo.identifier, + UseRadiusAzimuthElevationInfo.identifier, new BoolVerifier, Optional::Yes, - FlatScreenInfo.description + UseRadiusAzimuthElevationInfo.description }, { - EuclideanPositionInfo.identifier, - new DoubleVector2Verifier, + FaceCameraInfo.identifier, + new BoolVerifier, Optional::Yes, - EuclideanPositionInfo.description + FaceCameraInfo.description }, { - SphericalPositionInfo.identifier, - new DoubleVector2Verifier, + CartesianPositionInfo.identifier, + new DoubleVector3Verifier, Optional::Yes, - SphericalPositionInfo.description + CartesianPositionInfo.description }, { - DepthInfo.identifier, - new DoubleVerifier, + RadiusAzimuthElevationInfo.identifier, + new DoubleVector3Verifier, Optional::Yes, - DepthInfo.description + RadiusAzimuthElevationInfo.description }, { ScaleInfo.identifier, @@ -235,24 +317,30 @@ std::unique_ptr ScreenSpaceRenderable::createFromDictiona ScreenSpaceRenderable::ScreenSpaceRenderable(const ghoul::Dictionary& dictionary) : properties::PropertyOwner({ "" }) , _enabled(EnabledInfo, true) - , _useFlatScreen(FlatScreenInfo, true) - , _euclideanPosition( - EuclideanPositionInfo, - glm::vec2(0.f), - glm::vec2(-4.f), - glm::vec2(4.f) + , _useRadiusAzimuthElevation(UseRadiusAzimuthElevationInfo, false) + , _usePerspectiveProjection(UsePerspectiveProjectionInfo, false) + , _faceCamera(FaceCameraInfo, true) + , _cartesianPosition( + CartesianPositionInfo, + glm::vec3(0.f, 0.f, -2.f), + glm::vec3(-4.f, -4.f, -10.f), + glm::vec3(4.f, 4.f, 0.f) ) - , _sphericalPosition( - SphericalPositionInfo, - glm::vec2(0.f, glm::half_pi()), - glm::vec2(-glm::pi()), - glm::vec2(glm::pi()) + , _raePosition( + RadiusAzimuthElevationInfo, + glm::vec3(2.f, 0.f, 0.f), + glm::vec3(0.f, -glm::pi(), -glm::half_pi()), + glm::vec3(10.f, glm::pi(), glm::half_pi()) + ) + , _localRotation( + LocalRotationInfo, + glm::vec3(0.f), + glm::vec3(-glm::pi()), + glm::vec3(glm::pi()) ) - , _depth(DepthInfo, 0.f, 0.f, 1.f) , _scale(ScaleInfo, 0.25f, 0.f, 2.f) , _alpha(AlphaInfo, 1.f, 0.f, 1.f) , _delete(DeleteInfo) - , _radius(PlaneDepth) { if (dictionary.hasKey(KeyIdentifier)) { setIdentifier(dictionary.value(KeyIdentifier)); @@ -264,46 +352,47 @@ ScreenSpaceRenderable::ScreenSpaceRenderable(const ghoul::Dictionary& dictionary addProperty(_enabled); - addProperty(_useFlatScreen); - addProperty(_euclideanPosition); + addProperty(_useRadiusAzimuthElevation); + addProperty(_usePerspectiveProjection); + addProperty(_faceCamera); + addProperty(_cartesianPosition); + addProperty(_raePosition); // Setting spherical/euclidean onchange handler - _useFlatScreen.onChange([this]() { - if (_useFlatScreen) { - addProperty(_euclideanPosition); - removeProperty(_sphericalPosition); + _useRadiusAzimuthElevation.onChange([this]() { + if (_useRadiusAzimuthElevation) { + _raePosition = sphericalToRae(cartesianToSpherical(_cartesianPosition)); } else { - removeProperty(_euclideanPosition); - addProperty(_sphericalPosition); + _cartesianPosition = sphericalToCartesian(raeToSpherical(_raePosition)); } - useEuclideanCoordinates(_useFlatScreen); }); - addProperty(_depth); addProperty(_scale); addProperty(_alpha); + addProperty(_localRotation); if (dictionary.hasKey(EnabledInfo.identifier)) { _enabled = dictionary.value(EnabledInfo.identifier); } - if (dictionary.hasKey(FlatScreenInfo.identifier)) { - _useFlatScreen = dictionary.value(FlatScreenInfo.identifier); + if (dictionary.hasKey(UseRadiusAzimuthElevationInfo.identifier)) { + _useRadiusAzimuthElevation = dictionary.value( + UseRadiusAzimuthElevationInfo.identifier + ); } - useEuclideanCoordinates(_useFlatScreen); - if (_useFlatScreen) { - if (dictionary.hasKey(EuclideanPositionInfo.identifier)) { - _euclideanPosition = dictionary.value( - EuclideanPositionInfo.identifier + if (_useRadiusAzimuthElevation) { + if (dictionary.hasKey(RadiusAzimuthElevationInfo.identifier)) { + _raePosition = dictionary.value( + RadiusAzimuthElevationInfo.identifier ); } } else { - if (dictionary.hasKey(SphericalPositionInfo.identifier)) { - _sphericalPosition = dictionary.value( - SphericalPositionInfo.identifier + if (dictionary.hasKey(CartesianPositionInfo.identifier)) { + _cartesianPosition = dictionary.value( + CartesianPositionInfo.identifier ); } } @@ -312,14 +401,22 @@ ScreenSpaceRenderable::ScreenSpaceRenderable(const ghoul::Dictionary& dictionary _scale = static_cast(dictionary.value(ScaleInfo.identifier)); } - if (dictionary.hasKey(DepthInfo.identifier)) { - _depth = static_cast(dictionary.value(DepthInfo.identifier)); - } - if (dictionary.hasKey(AlphaInfo.identifier)) { _alpha = static_cast(dictionary.value(AlphaInfo.identifier)); } + if (dictionary.hasKey(UsePerspectiveProjectionInfo.identifier)) { + _usePerspectiveProjection = static_cast( + dictionary.value(UsePerspectiveProjectionInfo.identifier) + ); + } + + if (dictionary.hasKey(FaceCameraInfo.identifier)) { + _faceCamera = static_cast( + dictionary.value(FaceCameraInfo.identifier) + ); + } + if (dictionary.hasKeyAndValue(KeyTag)) { std::string tagName = dictionary.value(KeyTag); if (!tagName.empty()) { @@ -355,9 +452,7 @@ bool ScreenSpaceRenderable::initialize() { bool ScreenSpaceRenderable::initializeGL() { _originalViewportSize = global::windowDelegate.currentWindowResolution(); - createShaders(); - return isReady(); } @@ -375,7 +470,7 @@ bool ScreenSpaceRenderable::deinitializeGL() { } void ScreenSpaceRenderable::render() { - draw(rotationMatrix() * translationMatrix() * scaleMatrix()); + draw(globalRotationMatrix() * translationMatrix() * localRotationMatrix() * scaleMatrix()); } bool ScreenSpaceRenderable::isReady() const { @@ -388,27 +483,10 @@ bool ScreenSpaceRenderable::isEnabled() const { return _enabled; } -glm::vec3 ScreenSpaceRenderable::euclideanPosition() const { - return glm::vec3(_euclideanPosition.value(), _depth.value()); -} - -glm::vec3 ScreenSpaceRenderable::sphericalPosition() const { - return glm::vec3(_sphericalPosition.value(), _depth.value()); -} - -float ScreenSpaceRenderable::depth() const { - return _depth; -} - -void ScreenSpaceRenderable::useEuclideanCoordinates(bool b) { - _useEuclideanCoordinates = b; - if (_useEuclideanCoordinates) { - _euclideanPosition = toEuclidean(_sphericalPosition, _radius); - } else { - glm::vec3 sph = toSpherical(_euclideanPosition); - _sphericalPosition = sph; - _radius = sph.z; - } +float ScreenSpaceRenderable::depth() { + return _useRadiusAzimuthElevation ? + _raePosition.value().x : + cartesianToSpherical(_cartesianPosition).x; } void ScreenSpaceRenderable::createShaders() { @@ -442,7 +520,7 @@ glm::mat4 ScreenSpaceRenderable::scaleMatrix() { float scalingRatioX = _originalViewportSize.x / resolution.x; float scalingRatioY = _originalViewportSize.y / resolution.y; - return glm::scale( + glm::mat4 scale = glm::scale( glm::mat4(1.f), glm::vec3( _scale * scalingRatioX, @@ -450,45 +528,65 @@ glm::mat4 ScreenSpaceRenderable::scaleMatrix() { 1.f ) ); -} -glm::mat4 ScreenSpaceRenderable::rotationMatrix() { - // Get the scene transform - glm::mat4 rotation = glm::inverse(global::windowDelegate.modelMatrix()); - if (!_useEuclideanCoordinates) { - glm::vec2 position = _sphericalPosition.value(); - - rotation = glm::rotate(rotation, position.x, glm::vec3(0.f, 1.f, 0.f)); - rotation = glm::rotate( - rotation, - position.y - glm::half_pi(), - glm::vec3(1.f, 0.f, 0.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 rotation; + return scale; +} + +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 + // 2) sgct's scene matrix (also called model matrix by sgct) + + glm::mat4 inverseRotation = glm::inverse( + global::renderEngine.globalRotation() * + global::windowDelegate.modelMatrix() + ); + + // The rotation of all screen space renderables is adjustable in the render engine: + return global::renderEngine.screenSpaceRotation() * inverseRotation; +} + +glm::mat4 ScreenSpaceRenderable::localRotationMatrix() { + glm::mat4 rotation = glm::mat4(1.f); + if (_faceCamera) { + glm::vec3 translation = _useRadiusAzimuthElevation ? + sphericalToCartesian(raeToSpherical(_raePosition)) : + _cartesianPosition; + + rotation = glm::inverse(glm::lookAt( + glm::vec3(0.f), + glm::normalize(translation), + glm::vec3(0.f, 1.f, 0.f) + )); + } + + float roll = _localRotation.value().x; + float pitch = _localRotation.value().y; + float yaw = _localRotation.value().z; + return rotation * glm::mat4(glm::quat(glm::vec3(pitch, yaw, roll))); } glm::mat4 ScreenSpaceRenderable::translationMatrix() { - glm::mat4 translation(1.0); - if (!_useEuclideanCoordinates) { - translation = glm::translate(translation, glm::vec3(0.0f, 0.0f, PlaneDepth)); - } else { - translation = glm::translate( - glm::mat4(1.f), - glm::vec3(_euclideanPosition.value(), PlaneDepth) - ); - } + glm::vec3 translation = _useRadiusAzimuthElevation ? + sphericalToCartesian(raeToSpherical(_raePosition)) : + _cartesianPosition; - return translation; + return glm::translate(glm::mat4(1.f), translation); } void ScreenSpaceRenderable::draw(glm::mat4 modelTransform) { - //glEnable(GL_DEPTH_TEST); glDisable(GL_CULL_FACE); _shader->activate(); - _shader->setUniform(_uniformCache.occlusionDepth, 1.f - _depth); + _shader->setUniform(_uniformCache.alpha, _alpha); _shader->setUniform(_uniformCache.modelTransform, modelTransform);