diff --git a/data/assets/examples/spheres.asset b/data/assets/examples/spheres.asset index df6afcfe81..1d3dd15195 100644 --- a/data/assets/examples/spheres.asset +++ b/data/assets/examples/spheres.asset @@ -13,7 +13,7 @@ for z = 1,3 do } }, Renderable = { - Type = "RenderableSphere", + Type = "RenderableSphereImageLocal", Size = 0.20 + i * 0.01, Segments = 80, Opacity = 1, diff --git a/data/assets/nightsky/cardinal_directions.asset b/data/assets/nightsky/cardinal_directions.asset index e30d87ec94..3edce459fe 100644 --- a/data/assets/nightsky/cardinal_directions.asset +++ b/data/assets/nightsky/cardinal_directions.asset @@ -48,7 +48,7 @@ local CardinalDirectionSphere = { } }, Renderable = { - Type = "RenderableSphere", + Type = "RenderableSphereImageLocal", Enabled = asset.enabled, Size = 50000, Segments = 80, diff --git a/data/assets/nightsky/ecliptic_band.asset b/data/assets/nightsky/ecliptic_band.asset index e7fbc319bd..90a89193cd 100644 --- a/data/assets/nightsky/ecliptic_band.asset +++ b/data/assets/nightsky/ecliptic_band.asset @@ -54,7 +54,7 @@ local EclipticBand = { } }, Renderable = { - Type = "RenderableSphere", + Type = "RenderableSphereImageLocal", Texture = textures .. "band2x.png", Size = 4.28601E17, Segments = 50, diff --git a/data/assets/nightsky/light_pollution.asset b/data/assets/nightsky/light_pollution.asset index a8850826dd..d2b6eace04 100644 --- a/data/assets/nightsky/light_pollution.asset +++ b/data/assets/nightsky/light_pollution.asset @@ -33,7 +33,7 @@ local LightPollutionSphere = { } }, Renderable = { - Type = "RenderableSphere", + Type = "RenderableSphereImageLocal", Size = earthAsset.Earth.Renderable.Radii[1] * 1.05, Segments = 40, Opacity = 0.0, diff --git a/data/assets/scene/digitaluniverse/backgroundradiation.asset b/data/assets/scene/digitaluniverse/backgroundradiation.asset index 32409116e5..0fd43a12ea 100644 --- a/data/assets/scene/digitaluniverse/backgroundradiation.asset +++ b/data/assets/scene/digitaluniverse/backgroundradiation.asset @@ -22,7 +22,7 @@ local WMAP = { } }, Renderable = { - Type = "RenderableSphere", + Type = "RenderableSphereImageLocal", Enabled = false, Size = 3975.41417036064E23, Segments = 80, @@ -47,7 +47,7 @@ local CBE = { } }, Renderable = { - Type = "RenderableSphere", + Type = "RenderableSphereImageLocal", Enabled = false, Size = 3975.41417036064E23, Segments = 80, @@ -72,7 +72,7 @@ local Planck = { } }, Renderable = { - Type = "RenderableSphere", + Type = "RenderableSphereImageLocal", Enabled = true, Size = 3975.41417036064E23, Segments = 80, @@ -98,7 +98,7 @@ local HAlpha = { } }, Renderable = { - Type = "RenderableSphere", + Type = "RenderableSphereImageLocal", Enabled = false, Size = 9.2E21, Segments = 40, @@ -141,10 +141,10 @@ asset.meta = { Name = "Background Radiation", Version = "2.1", Description = [[Various AllSky images for the Milky Way and observable Universe. - Included: Wilkinson Microwave Anisotropy Probe (WMAP), Cosmic Background Explorer, - Planck, and H Alpha

Data Reference: Planck/ESA and the Planck - Collaboration, Wilkinson Microwave Anisotropy Probe/NASA, Doug - Finkbeiner (Princeton)]], + Included: Wilkinson Microwave Anisotropy Probe (WMAP), Cosmic Background Explorer, + Planck, and H Alpha

Data Reference: Planck/ESA and the Planck + Collaboration, Wilkinson Microwave Anisotropy Probe/NASA, Doug + Finkbeiner (Princeton)]], Author = "Brian Abbott (AMNH), OpenSpace Team", URL = "https://www.amnh.org/research/hayden-planetarium/digital-universe", License = "AMNH Digital Universe" diff --git a/data/assets/scene/digitaluniverse/backgroundradiation_multiverse.asset b/data/assets/scene/digitaluniverse/backgroundradiation_multiverse.asset index 70d83e7716..6633d975c9 100644 --- a/data/assets/scene/digitaluniverse/backgroundradiation_multiverse.asset +++ b/data/assets/scene/digitaluniverse/backgroundradiation_multiverse.asset @@ -19,7 +19,7 @@ local PlanckMultiverse1 = { } }, Renderable = { - Type = "RenderableSphere", + Type = "RenderableSphereImageLocal", Enabled = false, Size = 3975.41417036064E23, Segments = 80, @@ -48,7 +48,7 @@ local PlanckMultiverse2 = { } }, Renderable = { - Type = "RenderableSphere", + Type = "RenderableSphereImageLocal", Enabled = false, Size = 3975.41417036064E23, Segments = 80, @@ -77,7 +77,7 @@ local PlanckMultiverse3 = { } }, Renderable = { - Type = "RenderableSphere", + Type = "RenderableSphereImageLocal", Enabled = false, Size = 3975.41417036064E23, Segments = 80, @@ -106,7 +106,7 @@ local PlanckMultiverse4 = { } }, Renderable = { - Type = "RenderableSphere", + Type = "RenderableSphereImageLocal", Enabled = false, Size = 3975.41417036064E23, Segments = 80, diff --git a/data/assets/scene/digitaluniverse/milkyway_sphere.asset b/data/assets/scene/digitaluniverse/milkyway_sphere.asset index 65ed5cab71..1c4f51a7ad 100644 --- a/data/assets/scene/digitaluniverse/milkyway_sphere.asset +++ b/data/assets/scene/digitaluniverse/milkyway_sphere.asset @@ -15,7 +15,7 @@ local Sphere = { } }, Renderable = { - Type = "RenderableSphere", + Type = "RenderableSphereImageLocal", Size = 9.2E21, Segments = 40, Opacity = 0.25, diff --git a/data/assets/scene/milkyway/milkyway/eso.asset b/data/assets/scene/milkyway/milkyway/eso.asset index 8112beb9ab..47a51aee35 100644 --- a/data/assets/scene/milkyway/milkyway/eso.asset +++ b/data/assets/scene/milkyway/milkyway/eso.asset @@ -15,7 +15,7 @@ local Object = { } }, Renderable = { - Type = "RenderableSphere", + Type = "RenderableSphereImageLocal", Size = 9.2E20, Segments = 40, Opacity = 0.4, diff --git a/data/assets/scene/solarsystem/telescopes/jwst/jwst.asset b/data/assets/scene/solarsystem/telescopes/jwst/jwst.asset index 364181f76e..4c992a9fcf 100644 --- a/data/assets/scene/solarsystem/telescopes/jwst/jwst.asset +++ b/data/assets/scene/solarsystem/telescopes/jwst/jwst.asset @@ -43,7 +43,7 @@ local JWSTBand = { } }, Renderable = { - Type = "RenderableSphere", + Type = "RenderableSphereImageLocal", Texture = band .. "JWST-band.png", Size = 9.2E15, Segments = 50, diff --git a/modules/base/CMakeLists.txt b/modules/base/CMakeLists.txt index f37c8bc1a0..a0a14901f6 100644 --- a/modules/base/CMakeLists.txt +++ b/modules/base/CMakeLists.txt @@ -55,6 +55,8 @@ set(HEADER_FILES rendering/renderableplanetimevaryingimage.h rendering/renderableprism.h rendering/renderablesphere.h + rendering/renderablesphereimagelocal.h + rendering/renderablesphereimageonline.h rendering/renderabletimevaryingsphere.h rendering/renderabletrail.h rendering/renderabletrailorbit.h @@ -111,6 +113,8 @@ set(SOURCE_FILES rendering/renderableplanetimevaryingimage.cpp rendering/renderableprism.cpp rendering/renderablesphere.cpp + rendering/renderablesphereimagelocal.cpp + rendering/renderablesphereimageonline.cpp rendering/renderabletimevaryingsphere.cpp rendering/renderabletrail.cpp rendering/renderabletrailorbit.cpp diff --git a/modules/base/basemodule.cpp b/modules/base/basemodule.cpp index 7b8ef29316..0ab2488a03 100644 --- a/modules/base/basemodule.cpp +++ b/modules/base/basemodule.cpp @@ -48,7 +48,8 @@ #include #include #include -#include +#include +#include #include #include #include @@ -146,7 +147,8 @@ void BaseModule::internalInitialize(const ghoul::Dictionary&) { "RenderableTimeVaryingSphere" ); fRenderable->registerClass("RenderableRadialGrid"); - fRenderable->registerClass("RenderableSphere"); + fRenderable->registerClass("RenderableSphereImageLocal"); + fRenderable->registerClass("RenderableSphereImageOnline"); fRenderable->registerClass("RenderableSphericalGrid"); fRenderable->registerClass("RenderableTrailOrbit"); fRenderable->registerClass("RenderableTrailTrajectory"); @@ -226,6 +228,8 @@ std::vector BaseModule::documentations() const { RenderablePrism::Documentation(), RenderableRadialGrid::Documentation(), RenderableSphere::Documentation(), + RenderableSphereImageLocal::Documentation(), + RenderableSphereImageOnline::Documentation(), RenderableSphericalGrid::Documentation(), RenderableTimeVaryingSphere::Documentation(), RenderableTrailOrbit::Documentation(), diff --git a/modules/base/rendering/renderablesphere.cpp b/modules/base/rendering/renderablesphere.cpp index 640a768048..c734654687 100644 --- a/modules/base/rendering/renderablesphere.cpp +++ b/modules/base/rendering/renderablesphere.cpp @@ -31,13 +31,12 @@ #include #include #include -#include #include +#include #include -#include -#include -#include +#include #include +#include #include namespace { @@ -46,29 +45,27 @@ namespace { "colorTexture", "mirrorTexture" }; + constexpr openspace::properties::Property::PropertyInfo SizeInfo = { + "Size", + "Size (in meters)", + "This value specifies the radius of the sphere in meters", + openspace::properties::Property::Visibility::AdvancedUser + }; + + constexpr openspace::properties::Property::PropertyInfo SegmentsInfo = { + "Segments", + "Number of Segments", + "This value specifies the number of segments that the sphere is separated in", + // @VISIBILITY(2.67) + openspace::properties::Property::Visibility::AdvancedUser + }; + enum class Orientation : int { Outside = 0, Inside, Both }; - constexpr openspace::properties::Property::PropertyInfo TextureInfo = { - "Texture", - "Texture", - "This value specifies an image that is loaded from disk and is used as a texture " - "that is applied to this sphere. This image is expected to be an equirectangular " - "projection", - // @VISIBILITY(2.33) - openspace::properties::Property::Visibility::User - }; - - constexpr openspace::properties::Property::PropertyInfo MirrorTextureInfo = { - "MirrorTexture", - "Mirror Texture", - "Mirror the texture along the x-axis", - openspace::properties::Property::Visibility::User - }; - constexpr openspace::properties::Property::PropertyInfo OrientationInfo = { "Orientation", "Orientation", @@ -77,30 +74,20 @@ namespace { openspace::properties::Property::Visibility::AdvancedUser }; - constexpr openspace::properties::Property::PropertyInfo SegmentsInfo = { - "Segments", - "Number of Segments", - "This value specifies the number of segments that the sphere is separated in", - // @VISIBILITY(2.67) + constexpr openspace::properties::Property::PropertyInfo MirrorTextureInfo = { + "MirrorTexture", + "Mirror Texture", + "Mirror the texture along the x-axis", + openspace::properties::Property::Visibility::AdvancedUser + }; + + constexpr openspace::properties::Property::PropertyInfo DisableFadeInOutInfo = { + "DisableFadeInOut", + "Disable Fade-In/Fade-Out effects", + "Enables/Disables the fade in and out effects", openspace::properties::Property::Visibility::User }; - constexpr openspace::properties::Property::PropertyInfo SizeInfo = { - "Size", - "Size (in meters)", - "This value specifies the radius of the sphere in meters", - openspace::properties::Property::Visibility::AdvancedUser - }; - - constexpr openspace::properties::Property::PropertyInfo FadeOutThresholdInfo = { - "FadeOutThreshold", - "Fade-Out Threshold", - "This value determines percentage of the sphere that is visible before starting " - "to fade it out. A negative or zero value means no fading out will happen. This " - "is also the default", - openspace::properties::Property::Visibility::AdvancedUser - }; - constexpr openspace::properties::Property::PropertyInfo FadeInThresholdInfo = { "FadeInThreshold", "Fade-In Threshold", @@ -111,11 +98,13 @@ namespace { openspace::properties::Property::Visibility::AdvancedUser }; - constexpr openspace::properties::Property::PropertyInfo DisableFadeInOutInfo = { - "DisableFadeInOut", - "Disable Fade-In/Fade-Out effects", - "Enables/Disables the fade in and out effects", - openspace::properties::Property::Visibility::User + constexpr openspace::properties::Property::PropertyInfo FadeOutThresholdInfo = { + "FadeOutThreshold", + "Fade-Out Threshold", + "This value determines percentage of the sphere that is visible before starting " + "to fade it out. A negative or zero value means no fading out will happen. This " + "is also the default", + openspace::properties::Property::Visibility::AdvancedUser }; struct [[codegen::Dictionary(RenderableSphere)]] Parameters { @@ -125,9 +114,6 @@ namespace { // [[codegen::verbatim(SegmentsInfo.description)]] int segments; - // [[codegen::verbatim(TextureInfo.description)]] - std::string texture; - enum class [[codegen::map(Orientation)]] Orientation { Outside, Inside, @@ -140,14 +126,14 @@ namespace { // [[codegen::verbatim(MirrorTextureInfo.description)]] std::optional mirrorTexture; - // [[codegen::verbatim(FadeOutThresholdInfo.description)]] - std::optional fadeOutThreshold [[codegen::inrange(0.0, 1.0)]]; + // [[codegen::verbatim(DisableFadeInOutInfo.description)]] + std::optional disableFadeInOut; // [[codegen::verbatim(FadeInThresholdInfo.description)]] std::optional fadeInThreshold; - // [[codegen::verbatim(DisableFadeInOutInfo.description)]] - std::optional disableFadeInOut; + // [[codegen::verbatim(FadeOutThresholdInfo.description)]] + std::optional fadeOutThreshold [[codegen::inrange(0.0, 1.0)]]; }; #include "renderablesphere_codegen.cpp" } // namespace @@ -160,10 +146,9 @@ documentation::Documentation RenderableSphere::Documentation() { RenderableSphere::RenderableSphere(const ghoul::Dictionary& dictionary) : Renderable(dictionary) - , _texturePath(TextureInfo) - , _orientation(OrientationInfo, properties::OptionProperty::DisplayType::Dropdown) , _size(SizeInfo, 1.f, 0.f, 1e25f) , _segments(SegmentsInfo, 8, 4, 1000) + , _orientation(OrientationInfo, properties::OptionProperty::DisplayType::Dropdown) , _mirrorTexture(MirrorTextureInfo, false) , _disableFadeInDistance(DisableFadeInOutInfo, false) , _fadeInThreshold(FadeInThresholdInfo, -1.f, -0.1f, 1.f, 0.001f) @@ -174,23 +159,6 @@ RenderableSphere::RenderableSphere(const ghoul::Dictionary& dictionary) addProperty(Fadeable::_opacity); _size = p.size; - _segments = p.segments; - _texturePath = p.texture; - - _orientation.addOptions({ - { static_cast(Orientation::Outside), "Outside" }, - { static_cast(Orientation::Inside), "Inside" }, - { static_cast(Orientation::Both), "Both" } - }); - - if (p.orientation.has_value()) { - _orientation = static_cast(codegen::map(*p.orientation)); - } - else { - _orientation = static_cast(Orientation::Outside); - } - addProperty(_orientation); - _size.setExponent(15.f); _size.onChange([this]() { setBoundingSphere(_size); @@ -198,29 +166,39 @@ RenderableSphere::RenderableSphere(const ghoul::Dictionary& dictionary) }); addProperty(_size); + _segments = p.segments; + _segments.onChange([this]() { + _sphereIsDirty = true; + }); addProperty(_segments); - _segments.onChange([this]() { _sphereIsDirty = true; }); - addProperty(_texturePath); - _texturePath.onChange([this]() { loadTexture(); }); + _orientation.addOptions({ + { static_cast(Orientation::Outside), "Outside" }, + { static_cast(Orientation::Inside), "Inside" }, + { static_cast(Orientation::Both), "Both" } + }); + _orientation = p.orientation.has_value() ? + static_cast(codegen::map(*p.orientation)): + static_cast(Orientation::Outside); + addProperty(_orientation); _mirrorTexture = p.mirrorTexture.value_or(_mirrorTexture); addProperty(_mirrorTexture); - _fadeOutThreshold = p.fadeOutThreshold.value_or(_fadeOutThreshold); - addProperty(_fadeOutThreshold); + _disableFadeInDistance = p.disableFadeInOut.value_or(_disableFadeInDistance); + addProperty(_disableFadeInDistance); _fadeInThreshold = p.fadeInThreshold.value_or(_fadeInThreshold); addProperty(_fadeInThreshold); - _disableFadeInDistance = p.disableFadeInOut.value_or(_disableFadeInDistance); - addProperty(_disableFadeInDistance); + _fadeOutThreshold = p.fadeOutThreshold.value_or(_fadeOutThreshold); + addProperty(_fadeOutThreshold); setBoundingSphere(_size); } bool RenderableSphere::isReady() const { - return _shader && _texture; + return _shader != nullptr; } void RenderableSphere::initializeGL() { @@ -239,12 +217,10 @@ void RenderableSphere::initializeGL() { ); ghoul::opengl::updateUniformLocations(*_shader, _uniformCache, UniformNames); - - loadTexture(); } void RenderableSphere::deinitializeGL() { - _texture = nullptr; + _sphere = nullptr; BaseModule::ProgramObjectManager.release( "Sphere", @@ -252,6 +228,7 @@ void RenderableSphere::deinitializeGL() { global::renderEngine->removeRenderProgram(p); } ); + _shader = nullptr; } @@ -366,21 +343,21 @@ void RenderableSphere::render(const RenderData& data, RendererTasks&) { glDisable(GL_CULL_FACE); } - if (_renderBin == Renderable::RenderBin::PreDeferredTransparent) { + if (renderBin() == Renderable::RenderBin::PreDeferredTransparent) { glBlendFunc(GL_SRC_ALPHA, GL_ONE); glDepthMask(false); } _sphere->render(); - if (_renderBin == Renderable::RenderBin::PreDeferredTransparent) { - glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA); - glDepthMask(true); - } - _shader->setIgnoreUniformLocationError(IgnoreError::No); _shader->deactivate(); + // Reset + global::renderEngine->openglStateCache().resetBlendState(); + global::renderEngine->openglStateCache().resetDepthState(); + unbindTexture(); + if (orientation == Orientation::Inside) { glCullFace(GL_BACK); } @@ -402,28 +379,8 @@ void RenderableSphere::update(const UpdateData&) { } } -void RenderableSphere::bindTexture() { - _texture->bind(); -} - -void RenderableSphere::unbindTexture() {} - -void RenderableSphere::loadTexture() { - if (!_texturePath.value().empty()) { - std::unique_ptr texture = - ghoul::io::TextureReader::ref().loadTexture(_texturePath, 2); - - if (texture) { - LDEBUGC( - "RenderableSphere", - fmt::format("Loaded texture from {}", absPath(_texturePath)) - ); - texture->uploadTexture(); - texture->setFilter(ghoul::opengl::Texture::FilterMode::LinearMipMap); - texture->purgeFromRAM(); - _texture = std::move(texture); - } - } +void RenderableSphere::unbindTexture() { + glBindTexture(GL_TEXTURE_2D, 0); } } // namespace openspace diff --git a/modules/base/rendering/renderablesphere.h b/modules/base/rendering/renderablesphere.h index 1cf492b135..d75dc69369 100644 --- a/modules/base/rendering/renderablesphere.h +++ b/modules/base/rendering/renderablesphere.h @@ -27,16 +27,10 @@ #include -#include #include -#include -#include #include -namespace ghoul::opengl { - class ProgramObject; - class Texture; -} // namespace ghoul::opengl +namespace ghoul::opengl { class ProgramObject; } namespace openspace { @@ -61,33 +55,27 @@ public: static documentation::Documentation Documentation(); protected: - virtual void bindTexture(); + virtual void bindTexture() = 0; virtual void unbindTexture(); -private: - void loadTexture(); - - properties::StringProperty _texturePath; - properties::OptionProperty _orientation; - properties::FloatProperty _size; properties::IntProperty _segments; + properties::OptionProperty _orientation; properties::BoolProperty _mirrorTexture; - properties::BoolProperty _disableFadeInDistance; + properties::BoolProperty _disableFadeInDistance; properties::FloatProperty _fadeInThreshold; properties::FloatProperty _fadeOutThreshold; +private: ghoul::opengl::ProgramObject* _shader = nullptr; - std::unique_ptr _texture; std::unique_ptr _sphere; + bool _sphereIsDirty = false; UniformCache(opacity, modelViewProjection, modelViewTransform, modelViewRotation, colorTexture, mirrorTexture) _uniformCache; - - bool _sphereIsDirty = false; }; } // namespace openspace diff --git a/modules/base/rendering/renderablesphereimagelocal.cpp b/modules/base/rendering/renderablesphereimagelocal.cpp new file mode 100644 index 0000000000..13b8f64ed9 --- /dev/null +++ b/modules/base/rendering/renderablesphereimagelocal.cpp @@ -0,0 +1,134 @@ +/***************************************************************************************** + * * + * OpenSpace * + * * + * Copyright (c) 2014-2023 * + * * + * Permission is hereby granted, free of charge, to any person obtaining a copy of this * + * software and associated documentation files (the "Software"), to deal in the Software * + * without restriction, including without limitation the rights to use, copy, modify, * + * merge, publish, distribute, sublicense, and/or sell copies of the Software, and to * + * permit persons to whom the Software is furnished to do so, subject to the following * + * conditions: * + * * + * The above copyright notice and this permission notice shall be included in all copies * + * or substantial portions of the Software. * + * * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, * + * INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A * + * PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT * + * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF * + * CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE * + * OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. * + ****************************************************************************************/ + +#include + +#include +#include +#include +#include +#include +#include +#include + +namespace { + constexpr openspace::properties::Property::PropertyInfo TextureInfo = { + "Texture", + "Texture", + "This value specifies an image that is loaded from disk and is used as a texture " + "that is applied to this sphere. This image is expected to be an equirectangular " + "projection", + // @VISIBILITY(2.33) + openspace::properties::Property::Visibility::User + }; + + struct [[codegen::Dictionary(RenderableSphere)]] Parameters { + // [[codegen::verbatim(TextureInfo.description)]] + std::string texture; + + // If this value is set to 'true', the image for this sphere will not be loaded at + // startup but rather when the image is shown for the first time. Additionally, if + // the sphere is disabled, the image will automatically be unloaded + std::optional lazyLoading; + }; +#include "renderablesphereimagelocal_codegen.cpp" +} // namespace + +namespace openspace { + +documentation::Documentation RenderableSphereImageLocal::Documentation() { + return codegen::doc("base_renderable_sphere_image_local"); +} + +RenderableSphereImageLocal::RenderableSphereImageLocal(const ghoul::Dictionary& dictionary) + : RenderableSphere(dictionary) + , _texturePath(TextureInfo) +{ + const Parameters p = codegen::bake(dictionary); + + _texturePath = p.texture; + _texturePath.onChange([this]() { + loadTexture(); + }); + addProperty(_texturePath); +} + +bool RenderableSphereImageLocal::isReady() const { + return RenderableSphere::isReady() && _texture; +} + +void RenderableSphereImageLocal::initializeGL() { + RenderableSphere::initializeGL(); + + if (!_isLoadingLazily) { + loadTexture(); + } +} + +void RenderableSphereImageLocal::deinitializeGL() { + _texture = nullptr; + + RenderableSphere::deinitializeGL(); +} + +void RenderableSphereImageLocal::update(const UpdateData& data) { + RenderableSphere::update(data); + + if (_textureIsDirty) { + loadTexture(); + _textureIsDirty = false; + } +} + +void RenderableSphereImageLocal::bindTexture() { + _texture->bind(); +} + +void RenderableSphereImageLocal::loadTexture() { + if (_texturePath.value().empty()) { + return; + } + + std::unique_ptr texture = + ghoul::io::TextureReader::ref().loadTexture(_texturePath, 2); + + if (!texture) { + LWARNINGC( + "RenderableSphereImageLocal", + fmt::format("Could not load texture from {}", absPath(_texturePath)) + ); + return; + } + + LDEBUGC( + "RenderableSphereImageLocal", + fmt::format("Loaded texture from {}", absPath(_texturePath)) + ); + texture->uploadTexture(); + texture->setFilter(ghoul::opengl::Texture::FilterMode::LinearMipMap); + texture->purgeFromRAM(); + _texture = std::move(texture); +} + +} // namespace openspace diff --git a/modules/base/rendering/renderablesphereimagelocal.h b/modules/base/rendering/renderablesphereimagelocal.h new file mode 100644 index 0000000000..2b7fe624c0 --- /dev/null +++ b/modules/base/rendering/renderablesphereimagelocal.h @@ -0,0 +1,67 @@ +/***************************************************************************************** + * * + * OpenSpace * + * * + * Copyright (c) 2014-2023 * + * * + * 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_BASE___RENDERABLESPHEREIMAGELOCAL___H__ +#define __OPENSPACE_MODULE_BASE___RENDERABLESPHEREIMAGELOCAL___H__ + +#include + +namespace ghoul::opengl { class Texture; } + +namespace openspace { + +struct RenderData; +struct UpdateData; + +namespace documentation { struct Documentation; } + +class RenderableSphereImageLocal : public RenderableSphere { +public: + RenderableSphereImageLocal(const ghoul::Dictionary& dictionary); + + void initializeGL() override; + void deinitializeGL() override; + + bool isReady() const override; + + void update(const UpdateData& data) override; + + static documentation::Documentation Documentation(); + +protected: + void bindTexture() override; + +private: + void loadTexture(); + + properties::StringProperty _texturePath; + + std::unique_ptr _texture; + bool _isLoadingLazily = false; + bool _textureIsDirty = false; +}; + +} // namespace openspace + +#endif // __OPENSPACE_MODULE_BASE___RENDERABLESPHEREIMAGELOCAL___H__ diff --git a/modules/base/rendering/renderablesphereimageonline.cpp b/modules/base/rendering/renderablesphereimageonline.cpp new file mode 100644 index 0000000000..1cc0e04d86 --- /dev/null +++ b/modules/base/rendering/renderablesphereimageonline.cpp @@ -0,0 +1,163 @@ +/***************************************************************************************** + * * + * OpenSpace * + * * + * Copyright (c) 2014-2023 * + * * + * Permission is hereby granted, free of charge, to any person obtaining a copy of this * + * software and associated documentation files (the "Software"), to deal in the Software * + * without restriction, including without limitation the rights to use, copy, modify, * + * merge, publish, distribute, sublicense, and/or sell copies of the Software, and to * + * permit persons to whom the Software is furnished to do so, subject to the following * + * conditions: * + * * + * The above copyright notice and this permission notice shall be included in all copies * + * or substantial portions of the Software. * + * * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, * + * INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A * + * PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT * + * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF * + * CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE * + * OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. * + ****************************************************************************************/ + +#include + +#include +#include +#include +#include +#include +#include +#include + +namespace { + constexpr openspace::properties::Property::PropertyInfo TextureInfo = { + "URL", + "Image URL", + "Sets the URL of the texture that is displayed on this sphere. If " + "this value is changed, the image at the new path will automatically be loaded " + "and displayed. This image is expected to be an equirectangular projection", + // @VISIBILITY(2.25) + openspace::properties::Property::Visibility::User + }; + + struct [[codegen::Dictionary(RenderableSphere)]] Parameters { + // [[codegen::verbatim(TextureInfo.description)]] + std::string url [[codegen::key("URL")]]; + }; +#include "renderablesphereimageonline_codegen.cpp" +} // namespace + +namespace openspace { + +documentation::Documentation RenderableSphereImageOnline::Documentation() { + return codegen::doc("base_renderable_sphere_image_online"); +} + +RenderableSphereImageOnline::RenderableSphereImageOnline(const ghoul::Dictionary& dictionary) + : RenderableSphere(dictionary) + , _textureUrl(TextureInfo) +{ + const Parameters p = codegen::bake(dictionary); + + _textureUrl = p.url; + _textureUrl.onChange([this]() { + _textureIsDirty = true; + }); + addProperty(_textureUrl); +} + +void RenderableSphereImageOnline::deinitializeGL() { + _texture = nullptr; + + RenderableSphere::deinitializeGL(); +} + +void RenderableSphereImageOnline::update(const UpdateData& data) { + RenderableSphere::update(data); + + if (!_textureIsDirty) { + return; + } + + if (!_imageFuture.valid()) { + std::future future = downloadImageToMemory( + _textureUrl + ); + if (future.valid()) { + _imageFuture = std::move(future); + } + } + + if (_imageFuture.valid() && DownloadManager::futureReady(_imageFuture)) { + DownloadManager::MemoryFile imageFile = _imageFuture.get(); + + if (imageFile.corrupted) { + LERRORC( + "RenderableSphereImageOnline", + fmt::format("Error loading image from URL '{}'", _textureUrl) + ); + return; + } + + try { + std::unique_ptr texture = + ghoul::io::TextureReader::ref().loadTexture( + reinterpret_cast(imageFile.buffer), + imageFile.size, + 2, + imageFile.format + ); + + if (texture) { + // Images don't need to start on 4-byte boundaries, for example if the + // image is only RGB + glPixelStorei(GL_UNPACK_ALIGNMENT, 1); + + texture->uploadTexture(); + texture->setFilter(ghoul::opengl::Texture::FilterMode::LinearMipMap); + texture->purgeFromRAM(); + + _texture = std::move(texture); + _textureIsDirty = false; + } + } + catch (const ghoul::io::TextureReader::InvalidLoadException& e) { + _textureIsDirty = false; + LERRORC(e.component, e.message); + } + } +} + +void RenderableSphereImageOnline::bindTexture() { + if (_texture) { + _texture->bind(); + } + else { + unbindTexture(); + } +} + +std::future +RenderableSphereImageOnline::downloadImageToMemory(const std::string& url) +{ + return global::downloadManager->fetchFile( + url, + [url](const DownloadManager::MemoryFile&) { + LDEBUGC( + "RenderableSphereImageOnline", + fmt::format("Download to memory finished for image '{}'", url) + ); + }, + [url](const std::string& err) { + LDEBUGC( + "RenderableSphereImageOnline", + fmt::format("Download to memory failed for image '{}': {}", url, err) + ); + } + ); +} + +} // namespace openspace diff --git a/modules/base/rendering/renderablesphereimageonline.h b/modules/base/rendering/renderablesphereimageonline.h new file mode 100644 index 0000000000..0aec90ba0c --- /dev/null +++ b/modules/base/rendering/renderablesphereimageonline.h @@ -0,0 +1,67 @@ +/***************************************************************************************** + * * + * OpenSpace * + * * + * Copyright (c) 2014-2023 * + * * + * 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_BASE___RENDERABLESPHEREIMAGEONLINE___H__ +#define __OPENSPACE_MODULE_BASE___RENDERABLESPHEREIMAGEONLINE___H__ + +#include + +#include + +namespace ghoul::opengl { class Texture; } + +namespace openspace { + +struct RenderData; +struct UpdateData; + +namespace documentation { struct Documentation; } + +class RenderableSphereImageOnline : public RenderableSphere { +public: + RenderableSphereImageOnline(const ghoul::Dictionary& dictionary); + + void deinitializeGL() override; + + void update(const UpdateData& data) override; + + static documentation::Documentation Documentation(); + +protected: + void bindTexture() override; + +private: + std::future downloadImageToMemory( + const std::string& url); + + properties::StringProperty _textureUrl; + + std::future _imageFuture; + std::unique_ptr _texture; + bool _textureIsDirty = false; +}; + +} // namespace openspace + +#endif // __OPENSPACE_MODULE_BASE___RENDERABLESPHEREIMAGEONLINE___H__ diff --git a/modules/base/rendering/renderabletimevaryingsphere.cpp b/modules/base/rendering/renderabletimevaryingsphere.cpp index 634db73cc8..8f616136b4 100644 --- a/modules/base/rendering/renderabletimevaryingsphere.cpp +++ b/modules/base/rendering/renderabletimevaryingsphere.cpp @@ -24,35 +24,31 @@ #include -#include #include #include -#include -#include #include #include #include -#include #include -#include #include #include -#include -#include -#include -#include namespace { - constexpr std::array UniformNames = { - "opacity", "modelViewProjection", "modelViewRotation", "colorTexture", - "mirrorTexture" - }; + // Extract J2000 time from file names + // Requires files to be named as such: 'YYYY-MM-DDTHH-MM-SS-XXX.png' + double extractTriggerTimeFromFileName(const std::string& filePath) { + // Extract the filename from the path (without extension) + std::string timeString = std::filesystem::path(filePath).stem().string(); - enum class Orientation { - Outside = 0, - Inside, - Both - }; + // Ensure the separators are correct + timeString.replace(4, 1, "-"); + timeString.replace(7, 1, "-"); + timeString.replace(13, 1, ":"); + timeString.replace(16, 1, ":"); + timeString.replace(19, 1, "."); + + return openspace::Time::convertTime(timeString); + } constexpr openspace::properties::Property::PropertyInfo TextureSourceInfo = { "TextureSource", @@ -63,185 +59,36 @@ namespace { openspace::properties::Property::Visibility::AdvancedUser }; - constexpr openspace::properties::Property::PropertyInfo MirrorTextureInfo = { - "MirrorTexture", - "Mirror Texture", - "Mirror the texture along the x-axis", - openspace::properties::Property::Visibility::User - }; - - constexpr openspace::properties::Property::PropertyInfo OrientationInfo = { - "Orientation", - "Orientation", - "Specifies whether the texture is applied to the inside of the sphere, the " - "outside of the sphere, or both", - openspace::properties::Property::Visibility::AdvancedUser - }; - - constexpr openspace::properties::Property::PropertyInfo SegmentsInfo = { - "Segments", - "Number of Segments", - "This value specifies the number of segments that the sphere is separated in", - // @VISIBILITY(2.67) - openspace::properties::Property::Visibility::User - }; - - constexpr openspace::properties::Property::PropertyInfo SizeInfo = { - "Size", - "Size (in meters)", - "This value specifies the radius of the sphere in meters", - openspace::properties::Property::Visibility::AdvancedUser - }; - - constexpr openspace::properties::Property::PropertyInfo FadeOutThresholdInfo = { - "FadeOutThreshold", - "Fade-Out Threshold", - "This value determines percentage of the sphere is visible before starting " - "fading-out it", - openspace::properties::Property::Visibility::AdvancedUser - }; - - constexpr openspace::properties::Property::PropertyInfo FadeInThresholdInfo = { - "FadeInThreshold", - "Fade-In Threshold", - "Distance from center of MilkyWay from where the astronomical object starts to " - "fade in", - openspace::properties::Property::Visibility::AdvancedUser - }; - - constexpr openspace::properties::Property::PropertyInfo DisableFadeInOutInfo = { - "DisableFadeInOut", - "Disable Fade-In/Fade-Out effects", - "Enables/Disables the Fade-In/Out effects", - // @VISIBILITY(2.33) - openspace::properties::Property::Visibility::User - }; - struct [[codegen::Dictionary(RenderableTimeVaryingSphere)]] Parameters { - // [[codegen::verbatim(SizeInfo.description)]] - float size; - - // [[codegen::verbatim(SegmentsInfo.description)]] - int segments; - // [[codegen::verbatim(TextureSourceInfo.description)]] std::string textureSource; - - enum class [[codegen::map(Orientation)]] Orientation { - Outside, - Inside, - Both - }; - - // [[codegen::verbatim(OrientationInfo.description)]] - std::optional orientation; - - // [[codegen::verbatim(MirrorTextureInfo.description)]] - std::optional mirrorTexture; - - // [[codegen::verbatim(FadeOutThresholdInfo.description)]] - std::optional fadeOutThreshold [[codegen::inrange(0.0, 1.0)]]; - - // [[codegen::verbatim(FadeInThresholdInfo.description)]] - std::optional fadeInThreshold; - - // [[codegen::verbatim(DisableFadeInOutInfo.description)]] - std::optional disableFadeInOut; }; #include "renderabletimevaryingsphere_codegen.cpp" } // namespace namespace openspace { -double extractTriggerTimeFromFileName(const std::string& filePath); - documentation::Documentation RenderableTimeVaryingSphere::Documentation() { return codegen::doc("base_renderable_time_varying_sphere"); } RenderableTimeVaryingSphere::RenderableTimeVaryingSphere( const ghoul::Dictionary& dictionary) - : Renderable(dictionary) + : RenderableSphere(dictionary) , _textureSourcePath(TextureSourceInfo) - , _orientation(OrientationInfo, properties::OptionProperty::DisplayType::Dropdown) - , _size(SizeInfo, 1.f, 0.f, 1e35f) - , _segments(SegmentsInfo, 8, 4, 1000) - , _mirrorTexture(MirrorTextureInfo, false) - , _disableFadeInDistance(DisableFadeInOutInfo, true) - , _fadeInThreshold(FadeInThresholdInfo, -1.f, -1.f, 1.f) - , _fadeOutThreshold(FadeOutThresholdInfo, -1.f, -1.f, 1.f) { const Parameters p = codegen::bake(dictionary); - addProperty(Fadeable::_opacity); - - _size = p.size; - _segments = p.segments; _textureSourcePath = p.textureSource; - - _orientation.addOptions({ - { static_cast(Orientation::Outside), "Outside" }, - { static_cast(Orientation::Inside), "Inside" }, - { static_cast(Orientation::Both), "Both" } - }); - - if (p.orientation.has_value()) { - _orientation = static_cast(codegen::map(*p.orientation)); - } - else { - _orientation = static_cast(Orientation::Outside); - } - addProperty(_orientation); - - _size.setExponent(20.f); - _size.onChange([this]() { - setBoundingSphere(_size); - _sphereIsDirty = true; - }); - addProperty(_size); - - addProperty(_segments); - _segments.onChange([this]() { _sphereIsDirty = true; }); - - addProperty(_mirrorTexture); - addProperty(_fadeOutThreshold); - addProperty(_fadeInThreshold); - - _mirrorTexture = p.mirrorTexture.value_or(_mirrorTexture); - _fadeOutThreshold = p.fadeOutThreshold.value_or(_fadeOutThreshold); - _fadeInThreshold = p.fadeInThreshold.value_or(_fadeInThreshold); - - if (_fadeOutThreshold || _fadeInThreshold) { - _disableFadeInDistance = false; - addProperty(_disableFadeInDistance); - } - - setBoundingSphere(_size); } bool RenderableTimeVaryingSphere::isReady() const { - return _shader && _texture; + return RenderableSphere::isReady() && _texture; } void RenderableTimeVaryingSphere::initializeGL() { - _sphere = std::make_unique(_size, _segments); - _sphere->initialize(); + RenderableSphere::initializeGL(); - _shader = BaseModule::ProgramObjectManager.request( - "Timevarying Sphere", - []() -> std::unique_ptr { - return global::renderEngine->buildRenderProgram( - "Timevarying Sphere", - absPath("${MODULE_BASE}/shaders/sphere_vs.glsl"), - absPath("${MODULE_BASE}/shaders/sphere_fs.glsl") - ); - } - ); - _shader->setIgnoreUniformLocationError( - ghoul::opengl::ProgramObject::IgnoreError::Yes - ); - - ghoul::opengl::updateUniformLocations(*_shader, _uniformCache, UniformNames); extractMandatoryInfoFromSourceFolder(); computeSequenceEndTime(); loadTexture(); @@ -249,134 +96,9 @@ void RenderableTimeVaryingSphere::initializeGL() { void RenderableTimeVaryingSphere::deinitializeGL() { _texture = nullptr; - - BaseModule::ProgramObjectManager.release( - "Timevarying Sphere", - [](ghoul::opengl::ProgramObject* p) { - global::renderEngine->removeRenderProgram(p); - } - ); _files.clear(); - _shader = nullptr; -} -void RenderableTimeVaryingSphere::render(const RenderData& data, RendererTasks&) { - Orientation orientation = static_cast(_orientation.value()); - - glm::dmat4 modelTransform = - glm::translate(glm::dmat4(1.0), data.modelTransform.translation) * - glm::dmat4(data.modelTransform.rotation) * - glm::scale(glm::dmat4(1.0), glm::dvec3(data.modelTransform.scale)); - - glm::dmat3 modelRotation = data.modelTransform.rotation; - - _shader->activate(); - - glm::mat4 modelViewProjection = data.camera.projectionMatrix() * - glm::mat4(data.camera.combinedViewMatrix() * modelTransform); - _shader->setUniform(_uniformCache.modelViewProjection, modelViewProjection); - - glm::mat3 modelViewRotation = glm::mat3( - glm::dmat3(data.camera.viewRotationMatrix()) * modelRotation - ); - _shader->setUniform(_uniformCache.modelViewRotation, modelViewRotation); - - float adjustedOpacity = opacity(); - - if (!_disableFadeInDistance) { - if (_fadeInThreshold > -1.0) { - const float logDistCamera = glm::log(static_cast( - glm::distance(data.camera.positionVec3(), data.modelTransform.translation) - )); - const float startLogFadeDistance = glm::log(_size * _fadeInThreshold); - const float stopLogFadeDistance = startLogFadeDistance + 1.f; - - if (logDistCamera > startLogFadeDistance && - logDistCamera < stopLogFadeDistance) - { - const float fadeFactor = glm::clamp( - (logDistCamera - startLogFadeDistance) / - (stopLogFadeDistance - startLogFadeDistance), - 0.f, - 1.f - ); - adjustedOpacity *= fadeFactor; - } - else if (logDistCamera <= startLogFadeDistance) { - adjustedOpacity = 0.f; - } - } - - if (_fadeOutThreshold > -1.0) { - const float logDistCamera = glm::log(static_cast( - glm::distance(data.camera.positionVec3(), data.modelTransform.translation) - )); - const float startLogFadeDistance = glm::log(_size * _fadeOutThreshold); - const float stopLogFadeDistance = startLogFadeDistance + 1.f; - - if (logDistCamera > startLogFadeDistance && - logDistCamera < stopLogFadeDistance) - { - const float fadeFactor = glm::clamp( - (logDistCamera - startLogFadeDistance) / - (stopLogFadeDistance - startLogFadeDistance), - 0.f, - 1.f - ); - adjustedOpacity *= (1.f - fadeFactor); - } - else if (logDistCamera >= stopLogFadeDistance) { - adjustedOpacity = 0.f; - } - } - } - // Performance wise - if (adjustedOpacity < 0.01f) { - return; - } - - _shader->setUniform(_uniformCache.opacity, adjustedOpacity); - _shader->setUniform(_uniformCache._mirrorTexture, _mirrorTexture); - - ghoul::opengl::TextureUnit unit; - unit.activate(); - _texture->bind(); - _shader->setUniform(_uniformCache.colorTexture, unit); - - // Setting these states should not be necessary, - // since they are the default state in OpenSpace. - glEnable(GL_CULL_FACE); - glCullFace(GL_BACK); - - if (orientation == Orientation::Inside) { - glCullFace(GL_FRONT); - } - else if (orientation == Orientation::Both) { - glDisable(GL_CULL_FACE); - } - - if (_renderBin == Renderable::RenderBin::PreDeferredTransparent) { - glBlendFunc(GL_SRC_ALPHA, GL_ONE); - glDepthMask(false); - } - - _sphere->render(); - - if (_renderBin == Renderable::RenderBin::PreDeferredTransparent) { - glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA); - glDepthMask(true); - } - - _shader->setIgnoreUniformLocationError(ghoul::opengl::ProgramObject::IgnoreError::No); - _shader->deactivate(); - - if (orientation == Orientation::Inside) { - glCullFace(GL_BACK); - } - else if (orientation == Orientation::Both) { - glEnable(GL_CULL_FACE); - } - glDisable(GL_CULL_FACE); + RenderableSphere::deinitializeGL(); } void RenderableTimeVaryingSphere::extractMandatoryInfoFromSourceFolder() { @@ -386,7 +108,7 @@ void RenderableTimeVaryingSphere::extractMandatoryInfoFromSourceFolder() { fs::path sourceFolder = absPath(_textureSourcePath); if (!std::filesystem::is_directory(sourceFolder)) { throw ghoul::RuntimeError( - "Source folder for timevaryingsphere is not a valid directory" + "Source folder for RenderableTimeVaryingSphere is not a valid directory" ); } // Extract all file paths from the provided folder @@ -410,7 +132,8 @@ void RenderableTimeVaryingSphere::extractMandatoryInfoFromSourceFolder() { } std::sort( - _files.begin(), _files.end(), + _files.begin(), + _files.end(), [](const FileData& a, const FileData& b) { return a.time < b.time; } @@ -418,19 +141,14 @@ void RenderableTimeVaryingSphere::extractMandatoryInfoFromSourceFolder() { // Ensure that there are available and valid source files left if (_files.empty()) { throw ghoul::RuntimeError( - "Source folder for timevaryingsphere contains no files" + "Source folder for RenderableTimeVaryingSphere contains no files" ); } } void RenderableTimeVaryingSphere::update(const UpdateData& data) { - if (!_enabled) { - return; - } - if (_shader->isDirty()) { - _shader->rebuildFromFile(); - ghoul::opengl::updateUniformLocations(*_shader, _uniformCache, UniformNames); - } + RenderableSphere::update(data); + const double currentTime = data.time.j2000Seconds(); const bool isInInterval = (currentTime >= _files[0].time) && (currentTime < _sequenceEndTime); @@ -443,40 +161,33 @@ void RenderableTimeVaryingSphere::update(const UpdateData& data) { (nextIdx < _files.size() && currentTime >= _files[nextIdx].time)) { updateActiveTriggerTimeIndex(currentTime); - _sphereIsDirty = true; + _textureIsDirty = true; } // else {we're still in same state as previous frame (no changes needed)} } else { // not in interval => set everything to false _activeTriggerTimeIndex = 0; } - if (_sphereIsDirty) { - _sphere = std::make_unique(_size, _segments); - _sphere->initialize(); + if (_textureIsDirty) { loadTexture(); - _sphereIsDirty = false; + _textureIsDirty = false; } } -// Extract J2000 time from file names -// Requires files to be named as such: 'YYYY-MM-DDTHH-MM-SS-XXX.png' -double extractTriggerTimeFromFileName(const std::string& filePath) { - // Extract the filename from the path (without extension) - std::string timeString = std::filesystem::path(filePath).stem().string(); - - // Ensure the separators are correct - timeString.replace(4, 1, "-"); - timeString.replace(7, 1, "-"); - timeString.replace(13, 1, ":"); - timeString.replace(16, 1, ":"); - timeString.replace(19, 1, "."); - - return Time::convertTime(timeString); +void RenderableTimeVaryingSphere::bindTexture() { + if (_texture) { + _texture->bind(); + } + else { + unbindTexture(); + } } void RenderableTimeVaryingSphere::updateActiveTriggerTimeIndex(double currentTime) { auto iter = std::upper_bound( - _files.begin(), _files.end(), currentTime, + _files.begin(), + _files.end(), + currentTime, [](double value, const FileData& f) { return value < f.time; } @@ -499,8 +210,8 @@ void RenderableTimeVaryingSphere::computeSequenceEndTime() { if (_files.size() > 1) { const double lastTriggerTime = _files[_files.size() - 1].time; const double sequenceDuration = lastTriggerTime - _files[0].time; - const double averageStateDuration = sequenceDuration / - (static_cast(_files.size()) - 1.0); + const double averageStateDuration = + sequenceDuration / (static_cast(_files.size()) - 1.0); _sequenceEndTime = lastTriggerTime + averageStateDuration; } } @@ -510,4 +221,5 @@ void RenderableTimeVaryingSphere::loadTexture() { _texture = _files[_activeTriggerTimeIndex].texture.get(); } } + } // namespace openspace diff --git a/modules/base/rendering/renderabletimevaryingsphere.h b/modules/base/rendering/renderabletimevaryingsphere.h index 36d411b5e9..d7f9aeeb13 100644 --- a/modules/base/rendering/renderabletimevaryingsphere.h +++ b/modules/base/rendering/renderabletimevaryingsphere.h @@ -25,28 +25,18 @@ #ifndef __OPENSPACE_MODULE_BASE___RENDERABLETIMEVARYINGSPHERE___H__ #define __OPENSPACE_MODULE_BASE___RENDERABLETIMEVARYINGSPHERE___H__ -#include +#include -#include -#include -#include -#include -#include - -namespace ghoul::opengl { - class ProgramObject; - class Texture; -} // namespace ghoul::opengl +namespace ghoul::opengl { class Texture; } namespace openspace { -class Sphere; struct RenderData; struct UpdateData; namespace documentation { struct Documentation; } -class RenderableTimeVaryingSphere : public Renderable { +class RenderableTimeVaryingSphere : public RenderableSphere { public: RenderableTimeVaryingSphere(const ghoul::Dictionary& dictionary); @@ -55,11 +45,13 @@ public: bool isReady() const override; - void render(const RenderData& data, RendererTasks& rendererTask) override; void update(const UpdateData& data) override; static documentation::Documentation Documentation(); +protected: + void bindTexture() override; + private: struct FileData { std::string path; @@ -77,25 +69,8 @@ private: int _activeTriggerTimeIndex = 0; properties::StringProperty _textureSourcePath; - properties::OptionProperty _orientation; - - properties::FloatProperty _size; - properties::IntProperty _segments; - - properties::BoolProperty _mirrorTexture; - properties::BoolProperty _disableFadeInDistance; - - properties::FloatProperty _fadeInThreshold; - properties::FloatProperty _fadeOutThreshold; - - ghoul::opengl::ProgramObject* _shader = nullptr; ghoul::opengl::Texture* _texture = nullptr; - std::unique_ptr _sphere; - - UniformCache(opacity, modelViewProjection, modelViewRotation, colorTexture, - _mirrorTexture) _uniformCache; - - bool _sphereIsDirty = false; + bool _textureIsDirty = false; }; } // namespace openspace diff --git a/modules/spout/renderablespherespout.cpp b/modules/spout/renderablespherespout.cpp index b956c9428e..9aaf5cffc2 100644 --- a/modules/spout/renderablespherespout.cpp +++ b/modules/spout/renderablespherespout.cpp @@ -29,7 +29,6 @@ #include #include #include -#include #include namespace openspace { @@ -102,7 +101,7 @@ void RenderableSphereSpout::bindTexture() { glBindTexture(GL_TEXTURE_2D, _spoutReceiver.spoutTexture()); } else { - RenderableSphere::bindTexture(); + RenderableSphere::unbindTexture(); } } diff --git a/modules/video/include/renderablevideoplane.h b/modules/video/include/renderablevideoplane.h index b569d2a71e..c9b145f1ad 100644 --- a/modules/video/include/renderablevideoplane.h +++ b/modules/video/include/renderablevideoplane.h @@ -48,7 +48,7 @@ public: static documentation::Documentation Documentation(); protected: - virtual void bindTexture() override; + void bindTexture() override; private: VideoPlayer _videoPlayer; diff --git a/modules/video/include/renderablevideosphere.h b/modules/video/include/renderablevideosphere.h index e1a9c8723f..c7db3a3fa5 100644 --- a/modules/video/include/renderablevideosphere.h +++ b/modules/video/include/renderablevideosphere.h @@ -25,29 +25,15 @@ #ifndef __OPENSPACE_MODULE_VIDEO___RENDERABLEVIDEOSPHERE___H__ #define __OPENSPACE_MODULE_VIDEO___RENDERABLEVIDEOSPHERE___H__ -#include +#include #include -#include -#include -#include -#include -#include - -namespace ghoul::opengl { - class ProgramObject; - class Texture; -} // namespace ghoul::opengl namespace openspace { -class Sphere; -struct RenderData; -struct UpdateData; - namespace documentation { struct Documentation; } -class RenderableVideoSphere : public Renderable { +class RenderableVideoSphere : public RenderableSphere { public: RenderableVideoSphere(const ghoul::Dictionary& dictionary); @@ -56,37 +42,16 @@ public: bool isReady() const override; - virtual void render(const RenderData& data, RendererTasks& rendererTask) override; - virtual void update(const UpdateData& data) override; + void render(const RenderData& data, RendererTasks& rendererTask) override; + void update(const UpdateData& data) override; static documentation::Documentation Documentation(); protected: - void bindTexture(); - void unbindTexture(); + void bindTexture() override; private: VideoPlayer _videoPlayer; - - properties::OptionProperty _orientation; - - properties::FloatProperty _size; - properties::IntProperty _segments; - - properties::BoolProperty _mirrorTexture; - properties::BoolProperty _disableFadeInDistance; - - properties::FloatProperty _fadeInThreshold; - properties::FloatProperty _fadeOutThreshold; - - ghoul::opengl::ProgramObject* _shader = nullptr; - - std::unique_ptr _sphere; - - UniformCache(opacity, modelViewProjection, modelViewTransform, modelViewRotation, - colorTexture, mirrorTexture) _uniformCache; - - bool _sphereIsDirty = false; }; } // namespace openspace diff --git a/modules/video/src/renderablevideoplane.cpp b/modules/video/src/renderablevideoplane.cpp index 5bcba06397..6c694c32e8 100644 --- a/modules/video/src/renderablevideoplane.cpp +++ b/modules/video/src/renderablevideoplane.cpp @@ -74,7 +74,7 @@ void RenderableVideoPlane::update(const UpdateData& data) { return; } - // Shape the plane based on the aspect ration of the video + // Shape the plane based on the aspect ratio of the video glm::vec2 textureDim = glm::vec2(_videoPlayer.frameTexture()->dimensions()); if (_textureDimensions != textureDim) { float aspectRatio = textureDim.x / textureDim.y; diff --git a/modules/video/src/renderablevideosphere.cpp b/modules/video/src/renderablevideosphere.cpp index 3ce47e9960..f0ab52f706 100644 --- a/modules/video/src/renderablevideosphere.cpp +++ b/modules/video/src/renderablevideosphere.cpp @@ -24,115 +24,16 @@ #include -#include #include #include -#include -#include #include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -namespace { - constexpr std::array UniformNames = { - "opacity", "modelViewProjection", "modelViewTransform", "modelViewRotation", - "colorTexture", "mirrorTexture" - }; - - enum class Orientation : int { - Outside = 0, - Inside, - Both - }; - - constexpr openspace::properties::Property::PropertyInfo MirrorTextureInfo = { - "MirrorTexture", - "Mirror Texture", - "Mirror the texture along the x-axis" - }; - - constexpr openspace::properties::Property::PropertyInfo OrientationInfo = { - "Orientation", - "Orientation", - "Specifies whether the texture is applied to the inside of the sphere, the " - "outside of the sphere, or both" - }; - - constexpr openspace::properties::Property::PropertyInfo SegmentsInfo = { - "Segments", - "Number of Segments", - "This value specifies the number of segments that the sphere is separated in" - }; - - constexpr openspace::properties::Property::PropertyInfo SizeInfo = { - "Size", - "Size (in meters)", - "This value specifies the radius of the sphere in meters" - }; - - constexpr openspace::properties::Property::PropertyInfo FadeOutThresholdInfo = { - "FadeOutThreshold", - "Fade-Out Threshold", - "This value determines percentage of the sphere is visible before starting " - "fading-out it" - }; - - constexpr openspace::properties::Property::PropertyInfo FadeInThresholdInfo = { - "FadeInThreshold", - "Fade-In Threshold", - "Distance from center of MilkyWay from where the astronomical object starts to " - "fade in" - }; - - constexpr openspace::properties::Property::PropertyInfo DisableFadeInOutInfo = { - "DisableFadeInOut", - "Disable Fade-In/Fade-Out effects", - "Enables/Disables the Fade-In/Out effects" - }; - - struct [[codegen::Dictionary(RenderableVideoSphere)]] Parameters { - // [[codegen::verbatim(SizeInfo.description)]] - float size; - - // [[codegen::verbatim(SegmentsInfo.description)]] - int segments; - - enum class [[codegen::map(Orientation)]] Orientation { - Outside, - Inside, - Both - }; - - // [[codegen::verbatim(OrientationInfo.description)]] - std::optional orientation; - - // [[codegen::verbatim(MirrorTextureInfo.description)]] - std::optional mirrorTexture; - - // [[codegen::verbatim(FadeOutThresholdInfo.description)]] - std::optional fadeOutThreshold [[codegen::inrange(0.0, 1.0)]]; - - // [[codegen::verbatim(FadeInThresholdInfo.description)]] - std::optional fadeInThreshold; - - // [[codegen::verbatim(DisableFadeInOutInfo.description)]] - std::optional disableFadeInOut; - }; -#include "renderablevideosphere_codegen.cpp" -} // namespace namespace openspace { documentation::Documentation RenderableVideoSphere::Documentation() { - documentation::Documentation doc = - codegen::doc("renderable_video_sphere"); + documentation::Documentation doc = RenderableSphere::Documentation(); + doc.name = "RenderableVideoSphere"; + doc.id = "video_renderablevideosphere"; documentation::Documentation vp = VideoPlayer::Documentation(); doc.entries.insert(doc.entries.end(), vp.entries.begin(), vp.entries.end()); @@ -141,261 +42,42 @@ documentation::Documentation RenderableVideoSphere::Documentation() { } RenderableVideoSphere::RenderableVideoSphere(const ghoul::Dictionary& dictionary) - : Renderable(dictionary) + : RenderableSphere(dictionary) , _videoPlayer(dictionary) - , _orientation(OrientationInfo, properties::OptionProperty::DisplayType::Dropdown) - , _size(SizeInfo, 1.f, 0.f, 1e25f) - , _segments(SegmentsInfo, 8, 4, 1000) - , _mirrorTexture(MirrorTextureInfo, false) - , _disableFadeInDistance(DisableFadeInOutInfo, true) - , _fadeInThreshold(FadeInThresholdInfo, -1.f, 0.f, 1.f) - , _fadeOutThreshold(FadeOutThresholdInfo, -1.f, 0.f, 1.f) { - const Parameters p = codegen::bake(dictionary); - - addProperty(_opacity); - - _size = p.size; - _segments = p.segments; - - _orientation.addOptions({ - { static_cast(Orientation::Outside), "Outside" }, - { static_cast(Orientation::Inside), "Inside" }, - { static_cast(Orientation::Both), "Both" } - }); - - if (p.orientation.has_value()) { - _orientation = static_cast(codegen::map(*p.orientation)); - } - else { - _orientation = static_cast(Orientation::Outside); - } - addProperty(_orientation); - - _size.setExponent(15.f); - _size.onChange([this]() { - setBoundingSphere(_size); - _sphereIsDirty = true; - }); - addProperty(_size); - - addProperty(_segments); - _segments.onChange([this]() { _sphereIsDirty = true; }); - - addProperty(_mirrorTexture); - - _mirrorTexture = p.mirrorTexture.value_or(_mirrorTexture); - - bool hasGivenFadeOut = p.fadeOutThreshold.has_value(); - if (hasGivenFadeOut) { - _fadeOutThreshold = *p.fadeOutThreshold; - addProperty(_fadeOutThreshold); - } - - bool hasGivenFadeIn = p.fadeInThreshold.has_value(); - if (hasGivenFadeIn) { - _fadeInThreshold = *p.fadeInThreshold; - addProperty(_fadeInThreshold); - } - - if (hasGivenFadeIn || hasGivenFadeOut) { - _disableFadeInDistance = false; - addProperty(_disableFadeInDistance); - } - - setBoundingSphere(_size); - setRenderBinFromOpacity(); addPropertySubOwner(_videoPlayer); } - bool RenderableVideoSphere::isReady() const { - return _shader && _videoPlayer.isInitialized(); + return RenderableSphere::isReady() && _videoPlayer.isInitialized(); } void RenderableVideoSphere::initializeGL() { - _sphere = std::make_unique(_size, _segments); - _sphere->initialize(); - - _shader = BaseModule::ProgramObjectManager.request( - "Sphere", - []() -> std::unique_ptr { - return global::renderEngine->buildRenderProgram( - "Sphere", - absPath("${MODULE_BASE}/shaders/sphere_vs.glsl"), - absPath("${MODULE_BASE}/shaders/sphere_fs.glsl") - ); - } - ); - - ghoul::opengl::updateUniformLocations(*_shader, _uniformCache, UniformNames); - + RenderableSphere::initializeGL(); _videoPlayer.initialize(); } void RenderableVideoSphere::deinitializeGL() { _videoPlayer.destroy(); - BaseModule::ProgramObjectManager.release( - "Sphere", - [](ghoul::opengl::ProgramObject* p) { - global::renderEngine->removeRenderProgram(p); - } - ); - _shader = nullptr; + RenderableSphere::deinitializeGL(); } -void RenderableVideoSphere::render(const RenderData& data, RendererTasks&) { - Orientation orientation = static_cast(_orientation.value()); - - glm::dmat4 modelTransform = - glm::translate(glm::dmat4(1.0), data.modelTransform.translation) * - glm::dmat4(data.modelTransform.rotation) * - glm::scale(glm::dmat4(1.0), glm::dvec3(data.modelTransform.scale)); - - glm::dmat3 modelRotation = - glm::dmat3(data.modelTransform.rotation); - - // Activate shader - using IgnoreError = ghoul::opengl::ProgramObject::IgnoreError; - _shader->activate(); - _shader->setIgnoreUniformLocationError(IgnoreError::Yes); - - glm::mat4 modelViewProjection = data.camera.projectionMatrix() * - glm::mat4(data.camera.combinedViewMatrix() * modelTransform); - _shader->setUniform(_uniformCache.modelViewProjection, modelViewProjection); - - const glm::dmat4 modelViewTransform = - data.camera.combinedViewMatrix() * modelTransform; - _shader->setUniform(_uniformCache.modelViewTransform, glm::mat4(modelViewTransform)); - - glm::mat3 modelViewRotation = glm::mat3( - glm::dmat3(data.camera.viewRotationMatrix()) * modelRotation - ); - _shader->setUniform(_uniformCache.modelViewRotation, modelViewRotation); - - float adjustedOpacity = opacity(); - - if (!_disableFadeInDistance) { - if (_fadeInThreshold > -1.0) { - const double d = glm::distance( - data.camera.positionVec3(), - data.modelTransform.translation - ); - const float logDist = - d > 0.0 ? - std::log(static_cast(d)) : - -std::numeric_limits::max(); - - const float startLogFadeDistance = glm::log(_size * _fadeInThreshold); - const float stopLogFadeDistance = startLogFadeDistance + 1.f; - - if (logDist > startLogFadeDistance && logDist < stopLogFadeDistance) { - const float fadeFactor = glm::clamp( - (logDist - startLogFadeDistance) / - (stopLogFadeDistance - startLogFadeDistance), - 0.f, - 1.f - ); - adjustedOpacity *= fadeFactor; - } - else if (logDist <= startLogFadeDistance) { - adjustedOpacity = 0.f; - } - } - - if (_fadeOutThreshold > -1.0) { - const double d = glm::distance( - data.camera.positionVec3(), - data.modelTransform.translation - ); - const float logDist = - d > 0.0 ? - std::log(static_cast(d)) : - -std::numeric_limits::max(); - const float startLogFadeDistance = glm::log(_size * _fadeOutThreshold); - const float stopLogFadeDistance = startLogFadeDistance + 1.f; - - if (logDist > startLogFadeDistance && logDist < stopLogFadeDistance) { - const float fadeFactor = glm::clamp( - (logDist - startLogFadeDistance) / - (stopLogFadeDistance - startLogFadeDistance), - 0.f, - 1.f - ); - adjustedOpacity *= (1.f - fadeFactor); - } - else if (logDist >= stopLogFadeDistance) { - adjustedOpacity = 0.f; - } - } - } - // Performance wise - if (adjustedOpacity < 0.01f) { - return; - } - - _shader->setUniform(_uniformCache.opacity, adjustedOpacity); - _shader->setUniform(_uniformCache.mirrorTexture, _mirrorTexture.value()); - - ghoul::opengl::TextureUnit unit; - unit.activate(); - bindTexture(); - defer{ unbindTexture(); }; - _shader->setUniform(_uniformCache.colorTexture, unit); - - // Setting these states should not be necessary, - // since they are the default state in OpenSpace. - glEnable(GL_CULL_FACE); - glCullFace(GL_BACK); - - if (orientation == Orientation::Inside) { - glCullFace(GL_FRONT); - } - else if (orientation == Orientation::Both) { - glDisable(GL_CULL_FACE); - } - - if (_renderBin == Renderable::RenderBin::PreDeferredTransparent) { - glBlendFunc(GL_SRC_ALPHA, GL_ONE); - glDepthMask(false); - } - - _sphere->render(); - - if (_renderBin == Renderable::RenderBin::PreDeferredTransparent) { - glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA); - glDepthMask(true); - } - - _shader->setIgnoreUniformLocationError(IgnoreError::No); - _shader->deactivate(); - - if (orientation == Orientation::Inside) { - glCullFace(GL_BACK); - } - else if (orientation == Orientation::Both) { - glEnable(GL_CULL_FACE); +void RenderableVideoSphere::render(const RenderData& data, RendererTasks& rendererTask) { + if (_videoPlayer.isInitialized()) { + RenderableSphere::render(data, rendererTask); } } void RenderableVideoSphere::update(const UpdateData&) { + if (!_videoPlayer.isInitialized()) { + return; + } + _videoPlayer.update(); - - if (_shader->isDirty()) { - _shader->rebuildFromFile(); - ghoul::opengl::updateUniformLocations(*_shader, _uniformCache, UniformNames); - } - - if (_sphereIsDirty) { - _sphere = std::make_unique(_size, _segments); - _sphere->initialize(); - _sphereIsDirty = false; - } } void RenderableVideoSphere::bindTexture() { _videoPlayer.frameTexture().get()->bind(); } -void RenderableVideoSphere::unbindTexture() {} } // namespace openspace