diff --git a/data/assets/bastille-mas-model.scene b/data/assets/bastille-mas-model.scene index 6c21948fa7..61c3a486a0 100644 --- a/data/assets/bastille-mas-model.scene +++ b/data/assets/bastille-mas-model.scene @@ -9,6 +9,7 @@ asset.require('util/default_dashboard') asset.require('scene/solarsystem/sun/magnetogram_textures') asset.require('scene/solarsystem/sun/streamnodeslegend') asset.require('scene/solarsystem/sun/streamnodescutplane') +asset.require('scene/solarsystem/sun/streamnodes_sphere') --asset.require('examples/debugcoordinateaxes.asset') local suncameraState = asset.require('scene/solarsystem/sun/heliosphere/mas/bastille_day/Camerapositions/Earthpos'); local earthcameraState = asset.require('scene/solarsystem/sun/heliosphere/mas/bastille_day/Camerapositions/SunPos'); diff --git a/data/assets/scene/solarsystem/sun/cutplanes_textures.asset b/data/assets/scene/solarsystem/sun/cutplanes_textures.asset new file mode 100644 index 0000000000..7a8119d5da --- /dev/null +++ b/data/assets/scene/solarsystem/sun/cutplanes_textures.asset @@ -0,0 +1,8 @@ +local TexturesPath = asset.syncedResource({ + Type = "HttpSynchronization", + Name = "cutplanes_textures", + Identifier = "cutplanes_textures", + Version = 1 +}) + +asset.export("TexturesPath", TexturesPath) diff --git a/data/assets/scene/solarsystem/sun/cutplanes_textures_sphere.asset b/data/assets/scene/solarsystem/sun/cutplanes_textures_sphere.asset new file mode 100644 index 0000000000..c25e885643 --- /dev/null +++ b/data/assets/scene/solarsystem/sun/cutplanes_textures_sphere.asset @@ -0,0 +1,8 @@ +local TexturesPath = asset.syncedResource({ + Type = "HttpSynchronization", + Name = "cutplanes_textures", + Identifier = "cutplanes_textures", + Version = 3 +}) + +asset.export("TexturesPath", TexturesPath) diff --git a/data/assets/scene/solarsystem/sun/cutplanes_textures_sphere_small.asset b/data/assets/scene/solarsystem/sun/cutplanes_textures_sphere_small.asset new file mode 100644 index 0000000000..332a9c7317 --- /dev/null +++ b/data/assets/scene/solarsystem/sun/cutplanes_textures_sphere_small.asset @@ -0,0 +1,8 @@ +local TexturesPath = asset.syncedResource({ + Type = "HttpSynchronization", + Name = "cutplanes_textures", + Identifier = "cutplanes_textures", + Version = 4 +}) + +asset.export("TexturesPath", TexturesPath) diff --git a/data/assets/scene/solarsystem/sun/heliosphere/mas/bastille_day/StreamSelection/streamSelection1.json b/data/assets/scene/solarsystem/sun/heliosphere/mas/bastille_day/StreamSelection/streamSelection1.json index 65dc0be659..58dcb66d58 100644 --- a/data/assets/scene/solarsystem/sun/heliosphere/mas/bastille_day/StreamSelection/streamSelection1.json +++ b/data/assets/scene/solarsystem/sun/heliosphere/mas/bastille_day/StreamSelection/streamSelection1.json @@ -1 +1 @@ -{"test":["153","154","156","163"]} +{"test":["339","340","351","352","353","354","365","366","367"]} diff --git a/data/assets/scene/solarsystem/sun/heliosphere/mas/bastille_day/streamnodes.asset b/data/assets/scene/solarsystem/sun/heliosphere/mas/bastille_day/streamnodes.asset index 3842f61c42..cbb1de128d 100644 --- a/data/assets/scene/solarsystem/sun/heliosphere/mas/bastille_day/streamnodes.asset +++ b/data/assets/scene/solarsystem/sun/heliosphere/mas/bastille_day/streamnodes.asset @@ -41,8 +41,8 @@ local sunRadius = 6.957E8 -- Streamnodes from binaries local Streamnodes = { Identifier = "MAS_MHD_StreamNodes", - --wrong Parent = transforms.HNMReferenceFrame.Identifier, - Parent = sunTransforms.SunIAU.Identifier, + Parent = transforms.HNMReferenceFrame.Identifier, + --Parent = sunTransforms.SunIAU.Identifier, --Parent = sunTransforms.SolarSystemBarycenter.Identifier, --transforms.asset, referens till suntransforms.solarsystemBarycenter.identifier Renderable = { @@ -80,6 +80,12 @@ local Streamnodes = { Type = "StaticScale", Scale = 1 }, + Rotation = { + Type = "FixedRotation", + XAxis = {-0.63338087262755016203262119192353, -0.77384020972650618518999944537717, 0.0}, + YAxis = {0.77384020972650618518999944537717, -0.63338087262755016203262119192353, 0.0}, + ZAxis = {0.0, 0.0, 1.0} + } } } diff --git a/data/assets/scene/solarsystem/sun/streamnodes_sphere.asset b/data/assets/scene/solarsystem/sun/streamnodes_sphere.asset new file mode 100644 index 0000000000..f170609030 --- /dev/null +++ b/data/assets/scene/solarsystem/sun/streamnodes_sphere.asset @@ -0,0 +1,51 @@ +asset.info = { + Name = "Predictive Science Inc. Stream nodes Bastille Days", + Version = "1.0", + Description = " Stream nodes for the bastille day CME events ", + Author = "Christian Adamsson, Emilie Ho", + URL = "https://dx.doi.org/10.3847/1538-4357/aab36d", + License = "" +} + +asset.require("spice/base") +local assetHelper = asset.require("util/asset_helper") +local textures = asset.require('./cutplanes_textures_sphere').TexturesPath +local textures_smallsphere = asset.require('./cutplanes_textures_sphere_small').TexturesPath +local transforms = asset.require("./transforms") + +local Cutplane_sphere = { + Identifier = "Cutplane_sphere", + Parent = transforms.SunIAU.Identifier, + Renderable = { + Type = "RenderableTimeVaryingSphere", + Size = 1.571*10^11, + Enabled = true, + Texture = textures, + BlendMode = "Additive", + Opacity = 1.0, + Segments = 132 + }, + GUI = { + Name = "Sphere Slice", + Path = "/Solar System/Heliosphere" + }, +}; +local Cutplane_sphere_small = { + Identifier = "Cutplane_sphere_small", + Parent = transforms.SunIAU.Identifier, + Renderable = { + Type = "RenderableTimeVaryingSphere", + Size = 7.5*10^7, + Enabled = true, + Texture = textures_smallsphere, + BlendMode = "Additive", + Opacity = 1.0, + Segments = 132 + }, + GUI = { + Name = "Sphere Slice Small", + Path = "/Solar System/Heliosphere" + }, +}; + +assetHelper.registerSceneGraphNodesAndExport(asset, { Cutplane_sphere, Cutplane_sphere_small}) diff --git a/data/assets/scene/solarsystem/sun/streamnodescutplane.asset b/data/assets/scene/solarsystem/sun/streamnodescutplane.asset index 3eee354178..e5b835ea97 100644 --- a/data/assets/scene/solarsystem/sun/streamnodescutplane.asset +++ b/data/assets/scene/solarsystem/sun/streamnodescutplane.asset @@ -9,7 +9,7 @@ asset.info = { asset.require("spice/base") local assetHelper = asset.require("util/asset_helper") -local textures = asset.require('./streamnodes_textures').TexturesPath +local textures = asset.require('./cutplanes_textures').TexturesPath local transforms = asset.require("./transforms") local Cutplane = { @@ -22,10 +22,10 @@ local Cutplane = { } }, Renderable = { - Type = "RenderablePlaneImageLocal", + Type = "RenderableTimeVaryingPlaneImageLocal", Size = 1.3*10^11, - Enabled = false, - Texture = textures .. "/cutplane1.png", + Enabled = true, + Texture = textures, BlendMode = "Additive", Opacity = 0.65, }, @@ -35,22 +35,22 @@ local Cutplane = { }, }; -local Cutplane2 = { - Identifier = "Cutplane2", - Parent = transforms.SunIAU.Identifier, - Renderable = { - Type = "RenderablePlaneImageLocal", - Size = 1.3*10^11, - Enabled = false, - Texture = textures .. "/cutplane1.png", - BlendMode = "Additive", - Opacity = 0.65, - }, - GUI = { - Name = "Cutplane2", - Path = "/Solar System/Heliosphere" - }, -}; +--local Cutplane2 = { +-- Identifier = "Cutplane2", +-- Parent = transforms.SunIAU.Identifier, + -- Renderable = { +-- Type = "RenderablePlaneImageLocal", +-- Size = 1.3*10^11, +-- Enabled = false, +-- Texture = textures .. "/cutplane1.png", +-- BlendMode = "Additive", +-- Opacity = 0.65, +-- }, + -- GUI = { + -- Name = "Cutplane2", + -- Path = "/Solar System/Heliosphere" + -- }, +--}; assetHelper.registerSceneGraphNodesAndExport(asset, { Cutplane, Cutplane2 }) diff --git a/data/tasks/full_sync - kopia.task b/data/tasks/full_sync - kopia.task new file mode 100644 index 0000000000..87b6f2d08b --- /dev/null +++ b/data/tasks/full_sync - kopia.task @@ -0,0 +1,13 @@ +return {{ + Type = "KameleonVolumeToRawTask", + Input = "C:/Users/Chrad171/openspace/OpenSpace/sync/http/bastille_day_streamnodes_3D/2000-07-14T08-38-26-000.json", + -- Variable = "rho", + VariableVector = "ur uphi utheta", + InnerRadialLimit = 1.75, + FactorRSquared = "false", + Dimensions = {200, 100, 180}, + LowerDomainBound = {1, -90, 0}, + UpperDomainBound = {15, 90, 360}, + RawVolumeOutput = "${DATA}/mas/bastille/test/teststreams1.rawvolume", + DictionaryOutput = "${DATA}/mas/bastille/test/teststreams1.dictionary" +}} \ No newline at end of file diff --git a/data/tasks/testtask b/data/tasks/testtask new file mode 100644 index 0000000000..87b6f2d08b --- /dev/null +++ b/data/tasks/testtask @@ -0,0 +1,13 @@ +return {{ + Type = "KameleonVolumeToRawTask", + Input = "C:/Users/Chrad171/openspace/OpenSpace/sync/http/bastille_day_streamnodes_3D/2000-07-14T08-38-26-000.json", + -- Variable = "rho", + VariableVector = "ur uphi utheta", + InnerRadialLimit = 1.75, + FactorRSquared = "false", + Dimensions = {200, 100, 180}, + LowerDomainBound = {1, -90, 0}, + UpperDomainBound = {15, 90, 360}, + RawVolumeOutput = "${DATA}/mas/bastille/test/teststreams1.rawvolume", + DictionaryOutput = "${DATA}/mas/bastille/test/teststreams1.dictionary" +}} \ No newline at end of file diff --git a/data/tasks/testtask.task b/data/tasks/testtask.task new file mode 100644 index 0000000000..87b6f2d08b --- /dev/null +++ b/data/tasks/testtask.task @@ -0,0 +1,13 @@ +return {{ + Type = "KameleonVolumeToRawTask", + Input = "C:/Users/Chrad171/openspace/OpenSpace/sync/http/bastille_day_streamnodes_3D/2000-07-14T08-38-26-000.json", + -- Variable = "rho", + VariableVector = "ur uphi utheta", + InnerRadialLimit = 1.75, + FactorRSquared = "false", + Dimensions = {200, 100, 180}, + LowerDomainBound = {1, -90, 0}, + UpperDomainBound = {15, 90, 360}, + RawVolumeOutput = "${DATA}/mas/bastille/test/teststreams1.rawvolume", + DictionaryOutput = "${DATA}/mas/bastille/test/teststreams1.dictionary" +}} \ No newline at end of file diff --git a/modules/streamnodes/CMakeLists.txt b/modules/streamnodes/CMakeLists.txt index 3bed2ec546..9afd3d8322 100644 --- a/modules/streamnodes/CMakeLists.txt +++ b/modules/streamnodes/CMakeLists.txt @@ -27,12 +27,16 @@ include(${OPENSPACE_CMAKE_EXT_DIR}/module_definition.cmake) set(HEADER_FILES ${CMAKE_CURRENT_SOURCE_DIR}/rendering/renderablestreamnodes.h ${CMAKE_CURRENT_SOURCE_DIR}/rendering/renderablelighttravel.h + ${CMAKE_CURRENT_SOURCE_DIR}/rendering/renderabletimevaryingplaneimagelocal.h + ${CMAKE_CURRENT_SOURCE_DIR}/rendering/renderabletimevaryingsphere.h ) source_group("Header Files" FILES ${HEADER_FILES}) set(SOURCE_FILES ${CMAKE_CURRENT_SOURCE_DIR}/rendering/renderablestreamnodes.cpp ${CMAKE_CURRENT_SOURCE_DIR}/rendering/renderablelighttravel.cpp + ${CMAKE_CURRENT_SOURCE_DIR}/rendering/renderabletimevaryingplaneimagelocal.cpp + ${CMAKE_CURRENT_SOURCE_DIR}/rendering/renderabletimevaryingsphere.cpp ) source_group("Source Files" FILES ${SOURCE_FILES}) diff --git a/modules/streamnodes/rendering/renderabletimevaryingplaneimagelocal.cpp b/modules/streamnodes/rendering/renderabletimevaryingplaneimagelocal.cpp new file mode 100644 index 0000000000..31ea7fa728 --- /dev/null +++ b/modules/streamnodes/rendering/renderabletimevaryingplaneimagelocal.cpp @@ -0,0 +1,344 @@ +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +// Test debugging tools more then logmanager +#include +#include +#include + +#include +#include +#include +#include +#include +#include + +namespace { + constexpr const char* KeyLazyLoading = "LazyLoading"; + constexpr const char* _loggerCat = "RenderableTimeVaryingPlaneImageLocal"; + + 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 plane. This image has to be square." + }; + + constexpr openspace::properties::Property::PropertyInfo RenderableTypeInfo = { + "RenderableType", + "RenderableType", + "This value specifies if the plane should be rendered in the Background," + "Opaque, Transparent, or Overlay rendering step." + }; + +} // namespace + +namespace openspace { + + documentation::Documentation RenderableTimeVaryingPlaneImageLocal::Documentation() { + using namespace documentation; + return { + "Renderable Plane Image Local", + "base_renderable_plane_image_local", + { + { + TextureInfo.identifier, + new StringVerifier, + Optional::No, + TextureInfo.description + }, + { + RenderableTypeInfo.identifier, + new StringVerifier, + Optional::Yes, + RenderableTypeInfo.description + }, + { + KeyLazyLoading, + new BoolVerifier, + Optional::Yes, + "If this value is set to 'true', the image for this plane will not be " + "loaded at startup but rather when image is shown for the first time. " + "Additionally, if the plane is hidden, the image will automatically be " + "unloaded" + } + } + }; + } + + RenderableTimeVaryingPlaneImageLocal::RenderableTimeVaryingPlaneImageLocal(const ghoul::Dictionary& dictionary) + : RenderablePlane(dictionary) + , _texturePath(TextureInfo) + { + documentation::testSpecificationAndThrow( + Documentation(), + dictionary, + "RenderableTimeVaryingPlaneImageLocal" + ); + + addProperty(_blendMode); + + _texturePath = absPath(dictionary.value(TextureInfo.identifier)); + _textureFile = std::make_unique(_texturePath); + + addProperty(_texturePath); + _texturePath.onChange([this]() {loadTexture(); }); + _textureFile->setCallback( + [this](const ghoul::filesystem::File&) { _textureIsDirty = true; } + ); + + if (dictionary.hasKey(RenderableTypeInfo.identifier)) { + std::string renderType = dictionary.value( + RenderableTypeInfo.identifier + ); + if (renderType == "Background") { + setRenderBin(Renderable::RenderBin::Background); + } + else if (renderType == "Opaque") { + setRenderBin(Renderable::RenderBin::Opaque); + } + else if (renderType == "PreDeferredTransparent") { + setRenderBin(Renderable::RenderBin::PreDeferredTransparent); + } + else if (renderType == "PostDeferredTransparent") { + setRenderBin(Renderable::RenderBin::PostDeferredTransparent); + } + else if (renderType == "Overlay") { + setRenderBin(Renderable::RenderBin::Overlay); + } + } + else { + setRenderBin(Renderable::RenderBin::Opaque); + } + + if (dictionary.hasKey(KeyLazyLoading)) { + _isLoadingLazily = dictionary.value(KeyLazyLoading); + + if (_isLoadingLazily) { + _enabled.onChange([this]() { + if (!_enabled) { + BaseModule::TextureManager.release(_texture); + _texture = nullptr; + } + if (_enabled) { + _textureIsDirty = true; + } + }); + } + } + } + + bool RenderableTimeVaryingPlaneImageLocal::isReady() const { + return RenderablePlane::isReady(); + } + + void RenderableTimeVaryingPlaneImageLocal::initializeGL() { + RenderablePlane::initializeGL(); + LDEBUG("sourcefiles size:" + std::to_string(_sourceFiles.size())); + + if (!extractMandatoryInfoFromDictionary()) { + return; + } + extractTriggerTimesFromFileNames(); + computeSequenceEndTime(); + + if (!_isLoadingLazily) { + loadTexture(); + } + } + + bool RenderableTimeVaryingPlaneImageLocal::extractMandatoryInfoFromDictionary() + { + // Ensure that the source folder exists and then extract + // the files with the same extension as + ghoul::filesystem::Directory sourceFolder(_texturePath); + if (FileSys.directoryExists(sourceFolder)) { + // Extract all file paths from the provided folder + _sourceFiles = sourceFolder.readFiles( + ghoul::filesystem::Directory::Recursive::No, + ghoul::filesystem::Directory::Sort::Yes + ); + // Ensure that there are available and valid source files left + if (_sourceFiles.empty()) { + LERROR(fmt::format( + "{}: {} contains no {} files", + _identifier, _texturePath, "extension" + )); + return false; + } + } + else { + LERROR(fmt::format( + "{}: FieldlinesSequence {} is not a valid directory", + _identifier, + _texturePath + )); + return false; + } + LDEBUG("returning true"); + return true; + } + void RenderableTimeVaryingPlaneImageLocal::deinitializeGL() { + _textureFile = nullptr; + + BaseModule::TextureManager.release(_texture); + RenderablePlane::deinitializeGL(); + } + + void RenderableTimeVaryingPlaneImageLocal::bindTexture() { + _texture->bind(); + } + + void RenderableTimeVaryingPlaneImageLocal::update(const UpdateData& data) { + RenderablePlane::update(data); + if (!this->_enabled) { + return; + } + const double currentTime = data.time.j2000Seconds(); + const bool isInInterval = (currentTime >= _startTimes[0]) && + (currentTime < _sequenceEndTime); + //const bool isInInterval = true; + if (isInInterval) { + const size_t nextIdx = _activeTriggerTimeIndex + 1; + if ( + // true => Previous frame was not within the sequence interval + //_activeTriggerTimeIndex < 0 || + // true => We stepped back to a time represented by another state + currentTime < _startTimes[_activeTriggerTimeIndex] || + // true => We stepped forward to a time represented by another state + (nextIdx < _nStates && currentTime >= _startTimes[nextIdx])) + { + updateActiveTriggerTimeIndex(currentTime); + //LDEBUG("Vi borde uppdatera1"); + + // _mustLoadNewStateFromDisk = true; + LDEBUG("vi borde uppdatera"); + _needsUpdate = true; + + } // else {we're still in same state as previous frame (no changes needed)} + } + else { + //not in interval => set everything to false + //LDEBUG("not in interval"); + _activeTriggerTimeIndex = 0; + _needsUpdate = false; + } + + if ((_needsUpdate || _textureIsDirty) && !_isLoadingTexture) { + _isLoadingTexture = true; + loadTexture(); + _textureIsDirty = false; + } + } + // Extract J2000 time from file names + // Requires files to be named as such: 'YYYY-MM-DDTHH-MM-SS-XXX.json' + void RenderableTimeVaryingPlaneImageLocal::extractTriggerTimesFromFileNames() { + // number of characters in filename (excluding '.json') + constexpr const int FilenameSize = 23; + // size(".json") + constexpr const int ExtSize = 4; + + for (const std::string& filePath : _sourceFiles) { + LDEBUG("filepath " + filePath); + const size_t strLength = filePath.size(); + // Extract the filename from the path (without extension) + std::string timeString = filePath.substr( + strLength - FilenameSize - ExtSize, + FilenameSize - 1 + ); + // 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, "."); + const double triggerTime = Time::convertTime(timeString); + LDEBUG("timestring " + timeString); + _startTimes.push_back(triggerTime); + } + } + void RenderableTimeVaryingPlaneImageLocal::updateActiveTriggerTimeIndex(double currentTime) { + auto iter = std::upper_bound(_startTimes.begin(), _startTimes.end(), currentTime); + if (iter != _startTimes.end()) { + if (iter != _startTimes.begin()) { + _activeTriggerTimeIndex = static_cast( + std::distance(_startTimes.begin(), iter) + ) - 1; + } + else { + _activeTriggerTimeIndex = 0; + } + } + else { + _activeTriggerTimeIndex = static_cast(_nStates) - 1; + } + } + void RenderableTimeVaryingPlaneImageLocal::computeSequenceEndTime() { + if (_nStates > 1) { + const double lastTriggerTime = _startTimes[_nStates - 1]; + const double sequenceDuration = lastTriggerTime - _startTimes[0]; + const double averageStateDuration = sequenceDuration / + (static_cast(_nStates) - 1.0); + _sequenceEndTime = lastTriggerTime + averageStateDuration; + } + else { + // If there's just one state it should never disappear! + _sequenceEndTime = DBL_MAX; + } + } + void RenderableTimeVaryingPlaneImageLocal::loadTexture() { + if (_activeTriggerTimeIndex != -1) { + ghoul::opengl::Texture* t = _texture; + + unsigned int hash = ghoul::hashCRC32File(_sourceFiles[_activeTriggerTimeIndex]); + + _texture = BaseModule::TextureManager.request( + std::to_string(hash), + [path = _sourceFiles[_activeTriggerTimeIndex]]() -> std::unique_ptr { + std::unique_ptr texture = + ghoul::io::TextureReader::ref().loadTexture(absPath(path)); + + LDEBUGC( + "RenderableTimeVaryingPlaneImageLocal", + fmt::format("Loaded texture from '{}'", absPath(path)) + ); + texture->uploadTexture(); + texture->setFilter(ghoul::opengl::Texture::FilterMode::LinearMipMap); + texture->purgeFromRAM(); + + return texture; + } + ); + + BaseModule::TextureManager.release(t); + + _textureFile = std::make_unique(_sourceFiles[_activeTriggerTimeIndex]); + _textureFile->setCallback( + [&](const ghoul::filesystem::File&) { _textureIsDirty = true; } + ); + _isLoadingTexture = false; + } + } + +} // namespace openspace diff --git a/modules/streamnodes/rendering/renderabletimevaryingplaneimagelocal.h b/modules/streamnodes/rendering/renderabletimevaryingplaneimagelocal.h new file mode 100644 index 0000000000..f51b6cfdf4 --- /dev/null +++ b/modules/streamnodes/rendering/renderabletimevaryingplaneimagelocal.h @@ -0,0 +1,81 @@ +/***************************************************************************************** + * * + * OpenSpace * + * * + * Copyright (c) 2014-2020 * + * * + * 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___RenderableTimeVaryingPlaneImageLocal___H__ +#define __OPENSPACE_MODULE_BASE___RenderableTimeVaryingPlaneImageLocal___H__ + +#include + +namespace ghoul::filesystem { class File; } +namespace ghoul::opengl { class Texture; } + +namespace openspace { + + struct RenderData; + struct UpdateData; + + namespace documentation { struct Documentation; } + + class RenderableTimeVaryingPlaneImageLocal : public RenderablePlane { + public: + RenderableTimeVaryingPlaneImageLocal(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: + virtual void bindTexture() override; + + private: + void loadTexture(); + void extractTriggerTimesFromFileNames(); + bool extractMandatoryInfoFromDictionary(); + void updateActiveTriggerTimeIndex(double currenttime); + void computeSequenceEndTime(); + // Estimated end of sequence. + double _sequenceEndTime; + bool _needsUpdate = false; + std::vector _sourceFiles; + std::vector _startTimes; + int _activeTriggerTimeIndex = 0; + // Number of states in the sequence + size_t _nStates = 274; + properties::StringProperty _texturePath; + //properties::StringProperty _timestapsPath; + ghoul::opengl::Texture* _texture = nullptr; + std::unique_ptr _textureFile; + bool _isLoadingTexture = false; + bool _isLoadingLazily = false; + bool _textureIsDirty = false; + }; + +} // namespace openspace + +#endif // __OPENSPACE_MODULE_BASE___RenderableTimeVaryingPlaneImageLocal___H__ diff --git a/modules/streamnodes/rendering/renderabletimevaryingsphere.cpp b/modules/streamnodes/rendering/renderabletimevaryingsphere.cpp new file mode 100644 index 0000000000..648ea5f99c --- /dev/null +++ b/modules/streamnodes/rendering/renderabletimevaryingsphere.cpp @@ -0,0 +1,649 @@ +/***************************************************************************************** + * * + * OpenSpace * + * * + * Copyright (c) 2014-2020 * + * * + * 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 +#include +#include +#include +#include +#include +#include +#include +#include + +namespace { + constexpr const char* ProgramName = "Sphere"; + constexpr const char* _loggerCat = "RenderableTimeVaryingSphere"; + constexpr const std::array UniformNames = { + "opacity", "modelViewProjection", "modelViewRotation", "colorTexture", + "mirrorTexture" + }; + + 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." + }; + + 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 UseAdditiveBlendingInfo = { + "UseAdditiveBlending", + "Use Additive Blending", + "Render the object using additive blending." + }; + + 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." + }; + + constexpr openspace::properties::Property::PropertyInfo BackgroundInfo = { + "Background", + "Sets the current sphere rendering as a background rendering type", + "Enables/Disables background rendering." + }; +} // namespace + +namespace openspace { + +documentation::Documentation RenderableTimeVaryingSphere::Documentation() { + using namespace documentation; + return { + "RenderableTimeVaryingSphere", + "base_renderable_sphere", + { + { + SizeInfo.identifier, + new DoubleVerifier, + Optional::No, + SizeInfo.description + }, + { + SegmentsInfo.identifier, + new IntVerifier, + Optional::No, + SegmentsInfo.description + }, + { + TextureInfo.identifier, + new StringVerifier, + Optional::No, + TextureInfo.description + }, + { + OrientationInfo.identifier, + new StringInListVerifier({ "Inside", "Outside", "Both" }), + Optional::Yes, + OrientationInfo.description + }, + { + UseAdditiveBlendingInfo.identifier, + new BoolVerifier, + Optional::Yes, + UseAdditiveBlendingInfo.description + }, + { + MirrorTextureInfo.identifier, + new BoolVerifier, + Optional::Yes, + MirrorTextureInfo.description + }, + { + FadeOutThresholdInfo.identifier, + new DoubleInRangeVerifier(0.0, 1.0), + Optional::Yes, + FadeOutThresholdInfo.description + }, + { + FadeInThresholdInfo.identifier, + new DoubleVerifier, + Optional::Yes, + FadeInThresholdInfo.description + }, + { + DisableFadeInOutInfo.identifier, + new BoolVerifier, + Optional::Yes, + DisableFadeInOutInfo.description + }, + { + BackgroundInfo.identifier, + new BoolVerifier, + Optional::Yes, + BackgroundInfo.description + }, + } + }; +} + + +RenderableTimeVaryingSphere::RenderableTimeVaryingSphere(const ghoul::Dictionary& dictionary) + : Renderable(dictionary) + , _texturePath(TextureInfo) + , _orientation(OrientationInfo, properties::OptionProperty::DisplayType::Dropdown) + , _size(SizeInfo, 1.f, 0.f, 1e35f) + , _segments(SegmentsInfo, 8, 4, 1000) + , _mirrorTexture(MirrorTextureInfo, false) + , _useAdditiveBlending(UseAdditiveBlendingInfo, false) + , _disableFadeInDistance(DisableFadeInOutInfo, true) + , _backgroundRendering(BackgroundInfo, false) + , _fadeInThreshold(FadeInThresholdInfo, -1.f, 0.f, 1.f) + , _fadeOutThreshold(FadeOutThresholdInfo, -1.f, 0.f, 1.f) +{ + documentation::testSpecificationAndThrow( + Documentation(), + dictionary, + "RenderableTimeVaryingSphere" + ); + + addProperty(_opacity); + registerUpdateRenderBinFromOpacity(); + + _size = static_cast(dictionary.value(SizeInfo.identifier)); + _segments = static_cast(dictionary.value(SegmentsInfo.identifier)); + _texturePath = absPath(dictionary.value(TextureInfo.identifier)); + + _orientation.addOptions({ + { static_cast(Orientation::Outside), "Outside" }, + { static_cast(Orientation::Inside), "Inside" }, + { static_cast(Orientation::Both), "Both" } + }); + + if (dictionary.hasKey(OrientationInfo.identifier)) { + const std::string& v = dictionary.value(OrientationInfo.identifier); + if (v == "Inside") { + _orientation = static_cast(Orientation::Inside); + } + else if (v == "Outside") { + _orientation = static_cast(Orientation::Outside); + } + else if (v == "Both") { + _orientation = static_cast(Orientation::Both); + } + else { + throw ghoul::MissingCaseException(); + } + } + else { + _orientation = static_cast(Orientation::Outside); + } + addProperty(_orientation); + + addProperty(_size); + _size.onChange([this]() { _sphereIsDirty = true; }); + + addProperty(_segments); + _segments.onChange([this]() { _sphereIsDirty = true; }); + + addProperty(_texturePath); + _texturePath.onChange([this]() { loadTexture(); }); + + addProperty(_mirrorTexture); + addProperty(_useAdditiveBlending); + + + if (dictionary.hasKey(MirrorTextureInfo.identifier)) { + _mirrorTexture = dictionary.value(MirrorTextureInfo.identifier); + } + if (dictionary.hasKey(UseAdditiveBlendingInfo.identifier)) { + _useAdditiveBlending = dictionary.value(UseAdditiveBlendingInfo.identifier); + + if (_useAdditiveBlending) { + setRenderBin(Renderable::RenderBin::PreDeferredTransparent); + } + } + + if (dictionary.hasKey(FadeOutThresholdInfo.identifier)) { + _fadeOutThreshold = static_cast( + dictionary.value(FadeOutThresholdInfo.identifier) + ); + addProperty(_fadeOutThreshold); + } + + if (dictionary.hasKey(FadeInThresholdInfo.identifier)) { + _fadeInThreshold = static_cast( + dictionary.value(FadeInThresholdInfo.identifier) + ); + addProperty(_fadeInThreshold); + } + + if (dictionary.hasKey(FadeOutThresholdInfo.identifier) || + dictionary.hasKey(FadeInThresholdInfo.identifier)) { + _disableFadeInDistance.set(false); + addProperty(_disableFadeInDistance); + } + + if (dictionary.hasKey(BackgroundInfo.identifier)) { + _backgroundRendering = dictionary.value(BackgroundInfo.identifier); + + if (_backgroundRendering) { + setRenderBin(Renderable::RenderBin::Background); + } + } + + setRenderBinFromOpacity(); +} + +bool RenderableTimeVaryingSphere::isReady() const { + return _shader && _texture; +} + +void RenderableTimeVaryingSphere::initializeGL() { + _sphere = std::make_unique(_size, _segments); + _sphere->initialize(); + + _shader = BaseModule::ProgramObjectManager.request( + ProgramName, + []() -> std::unique_ptr { + return global::renderEngine.buildRenderProgram( + ProgramName, + absPath("${MODULE_BASE}/shaders/sphere_vs.glsl"), + absPath("${MODULE_BASE}/shaders/sphere_fs.glsl") + ); + } + ); + + ghoul::opengl::updateUniformLocations(*_shader, _uniformCache, UniformNames); + if (!extractMandatoryInfoFromDictionary()) { + return; + } + extractTriggerTimesFromFileNames(); + computeSequenceEndTime(); + loadTexture(); +} + +void RenderableTimeVaryingSphere::deinitializeGL() { + _texture = nullptr; + + BaseModule::ProgramObjectManager.release( + ProgramName, + [](ghoul::opengl::ProgramObject* p) { + global::renderEngine.removeRenderProgram(p); + } + ); + _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 = + 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); + + 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.value()); + + 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); + } + + bool usingFramebufferRenderer = global::renderEngine.rendererImplementation() == + RenderEngine::RendererImplementation::Framebuffer; + + bool usingABufferRenderer = global::renderEngine.rendererImplementation() == + RenderEngine::RendererImplementation::ABuffer; + + if (usingABufferRenderer && _useAdditiveBlending) { + _shader->setUniform("additiveBlending", true); + } + + if (usingFramebufferRenderer && _useAdditiveBlending) { + glBlendFunc(GL_SRC_ALPHA, GL_ONE); + glDepthMask(false); + } + + _sphere->render(); + + if (usingFramebufferRenderer && _useAdditiveBlending) { + 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); + } +} +bool RenderableTimeVaryingSphere::extractMandatoryInfoFromDictionary() +{ + // Ensure that the source folder exists and then extract + // the files with the same extension as + ghoul::filesystem::Directory sourceFolder(_texturePath); + if (FileSys.directoryExists(sourceFolder)) { + // Extract all file paths from the provided folder + _sourceFiles = sourceFolder.readFiles( + ghoul::filesystem::Directory::Recursive::No, + ghoul::filesystem::Directory::Sort::Yes + ); + // Ensure that there are available and valid source files left + if (_sourceFiles.empty()) { + LERROR(fmt::format( + "{}: {} contains no {} files", + _identifier, _texturePath, "extension" + )); + return false; + } + } + else { + LERROR(fmt::format( + "{}: FieldlinesSequence {} is not a valid directory", + _identifier, + _texturePath + )); + return false; + } + LDEBUG("returning true"); + return true; +} +void RenderableTimeVaryingSphere::update(const UpdateData& data) { + if (!this->_enabled) { + return; + } + if (_shader->isDirty()) { + _shader->rebuildFromFile(); + ghoul::opengl::updateUniformLocations(*_shader, _uniformCache, UniformNames); + } + const double currentTime = data.time.j2000Seconds(); + const bool isInInterval = (currentTime >= _startTimes[0]) && + (currentTime < _sequenceEndTime); + //const bool isInInterval = true; + if (isInInterval) { + const size_t nextIdx = _activeTriggerTimeIndex + 1; + if ( + // true => Previous frame was not within the sequence interval + //_activeTriggerTimeIndex < 0 || + // true => We stepped back to a time represented by another state + currentTime < _startTimes[_activeTriggerTimeIndex] || + // true => We stepped forward to a time represented by another state + (nextIdx < _nStates && currentTime >= _startTimes[nextIdx])) + { + updateActiveTriggerTimeIndex(currentTime); + //LDEBUG("Vi borde uppdatera1"); + + // _mustLoadNewStateFromDisk = true; + LDEBUG("vi borde uppdatera"); + _needsUpdate = true; + + } // else {we're still in same state as previous frame (no changes needed)} + } + else { + //not in interval => set everything to false + //LDEBUG("not in interval"); + _activeTriggerTimeIndex = 0; + _needsUpdate = false; + } + if ((_needsUpdate || _sphereIsDirty) && !_isLoadingTexture) { + _sphere = std::make_unique(_size, _segments); + _sphere->initialize(); + //_isLoadingTexture = true; + loadTexture(); + _sphereIsDirty = false; + } +} +// Extract J2000 time from file names + // Requires files to be named as such: 'YYYY-MM-DDTHH-MM-SS-XXX.json' +void RenderableTimeVaryingSphere::extractTriggerTimesFromFileNames() { + // number of characters in filename (excluding '.json') + constexpr const int FilenameSize = 23; + // size(".json") + constexpr const int ExtSize = 4; + + for (const std::string& filePath : _sourceFiles) { + LDEBUG("filepath " + filePath); + const size_t strLength = filePath.size(); + // Extract the filename from the path (without extension) + std::string timeString = filePath.substr( + strLength - FilenameSize - ExtSize, + FilenameSize - 1 + ); + // 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, "."); + const double triggerTime = Time::convertTime(timeString); + LDEBUG("timestring " + timeString); + _startTimes.push_back(triggerTime); + } +} +void RenderableTimeVaryingSphere::updateActiveTriggerTimeIndex(double currentTime) { + auto iter = std::upper_bound(_startTimes.begin(), _startTimes.end(), currentTime); + if (iter != _startTimes.end()) { + if (iter != _startTimes.begin()) { + _activeTriggerTimeIndex = static_cast( + std::distance(_startTimes.begin(), iter) + ) - 1; + } + else { + _activeTriggerTimeIndex = 0; + } + } + else { + _activeTriggerTimeIndex = static_cast(_nStates) - 1; + } +} +void RenderableTimeVaryingSphere::computeSequenceEndTime() { + if (_nStates > 1) { + const double lastTriggerTime = _startTimes[_nStates - 1]; + const double sequenceDuration = lastTriggerTime - _startTimes[0]; + const double averageStateDuration = sequenceDuration / + (static_cast(_nStates) - 1.0); + _sequenceEndTime = lastTriggerTime + averageStateDuration; + } + else { + // If there's just one state it should never disappear! + _sequenceEndTime = DBL_MAX; + } +} +void RenderableTimeVaryingSphere::loadTexture() { + if (_activeTriggerTimeIndex != -1) { + ghoul::opengl::Texture* t = _texture; + // std::unique_ptr texture = + // ghoul::io::TextureReader::ref().loadTexture(_sourceFiles[_activeTriggerTimeIndex]); + unsigned int hash = ghoul::hashCRC32File(_sourceFiles[_activeTriggerTimeIndex]); + + /* + if (texture) { + LDEBUGC( + "RenderableTimeVaryingSphere", + fmt::format("Loaded texture from '{}'", absPath(_sourceFiles[_activeTriggerTimeIndex])) + ); + texture->uploadTexture(); + texture->setFilter(ghoul::opengl::Texture::FilterMode::LinearMipMap); + //_texture = std::move(texture); + texture->purgeFromRAM(); + }*/ + _texture = BaseModule::TextureManager.request( + std::to_string(hash), + [path = _sourceFiles[_activeTriggerTimeIndex]]()->std::unique_ptr { + std::unique_ptr texture = + ghoul::io::TextureReader::ref().loadTexture(absPath(path)); + + LDEBUGC( + "RenderableTimeVaryingSphere", + fmt::format("Loaded texture from '{}'", absPath(path)) + ); + texture->uploadTexture(); + texture->setFilter(ghoul::opengl::Texture::FilterMode::LinearMipMap); + texture->purgeFromRAM(); + + + return texture; + } + ); + + BaseModule::TextureManager.release(t); + + _textureFile = std::make_unique(_sourceFiles[_activeTriggerTimeIndex]); + _textureFile->setCallback( + [&](const ghoul::filesystem::File&) { _sphereIsDirty = true; } + ); + _isLoadingTexture = false; + } +} + +} // namespace openspace diff --git a/modules/streamnodes/rendering/renderabletimevaryingsphere.h b/modules/streamnodes/rendering/renderabletimevaryingsphere.h new file mode 100644 index 0000000000..42e633758a --- /dev/null +++ b/modules/streamnodes/rendering/renderabletimevaryingsphere.h @@ -0,0 +1,108 @@ +/***************************************************************************************** + * * + * OpenSpace * + * * + * Copyright (c) 2014-2020 * + * * + * 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___RenderableTimeVaryingSphere___H__ +#define __OPENSPACE_MODULE_BASE___RenderableTimeVaryingSphere___H__ + +#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 RenderableTimeVaryingSphere : public Renderable { +public: + RenderableTimeVaryingSphere(const ghoul::Dictionary& dictionary); + + void initializeGL() override; + void deinitializeGL() override; + + bool isReady() const override; + + void render(const RenderData& data, RendererTasks& rendererTask) override; + void update(const UpdateData& data) override; + + static documentation::Documentation Documentation(); + +private: + void loadTexture(); + void extractTriggerTimesFromFileNames(); + bool extractMandatoryInfoFromDictionary(); + void updateActiveTriggerTimeIndex(double currenttime); + void computeSequenceEndTime(); + + // Estimated end of sequence. + double _sequenceEndTime; + bool _needsUpdate = false; + std::vector _sourceFiles; + std::vector _startTimes; + int _activeTriggerTimeIndex = 0; + // Number of states in the sequence + size_t _nStates = 272; + bool _isLoadingTexture = false; + + properties::StringProperty _texturePath; + properties::OptionProperty _orientation; + + properties::FloatProperty _size; + properties::IntProperty _segments; + + properties::BoolProperty _mirrorTexture; + properties::BoolProperty _useAdditiveBlending; + properties::BoolProperty _disableFadeInDistance; + properties::BoolProperty _backgroundRendering; + + properties::FloatProperty _fadeInThreshold; + properties::FloatProperty _fadeOutThreshold; + + ghoul::opengl::ProgramObject* _shader = nullptr; + //std::unique_ptr _texture; + ghoul::opengl::Texture* _texture = nullptr; + std::unique_ptr _textureFile; + std::unique_ptr _sphere; + + UniformCache(opacity, modelViewProjection, modelViewRotation, colorTexture, + _mirrorTexture) _uniformCache; + + bool _sphereIsDirty = false; +}; + +} // namespace openspace + +#endif // __OPENSPACE_MODULE_BASE___RenderableTimeVaryingSphere___H__ diff --git a/modules/streamnodes/streamnodesmodule.cpp b/modules/streamnodes/streamnodesmodule.cpp index 4674e87173..35c2c0c5af 100644 --- a/modules/streamnodes/streamnodesmodule.cpp +++ b/modules/streamnodes/streamnodesmodule.cpp @@ -25,6 +25,8 @@ #include #include #include +#include +#include #include #include #include @@ -62,6 +64,8 @@ void StreamNodesModule::internalInitialize(const ghoul::Dictionary&) { factory->registerClass("RenderableStreamNodes"); factory->registerClass("RenderableLightTravel"); + factory->registerClass("RenderableTimeVaryingPlaneImageLocal"); + factory->registerClass("RenderableTimeVaryingSphere"); } } // namespace openspace