diff --git a/modules/fieldlinessequence/rendering/renderablefieldlinessequence.cpp b/modules/fieldlinessequence/rendering/renderablefieldlinessequence.cpp index 2c8fc0b729..2fb669f39f 100644 --- a/modules/fieldlinessequence/rendering/renderablefieldlinessequence.cpp +++ b/modules/fieldlinessequence/rendering/renderablefieldlinessequence.cpp @@ -34,6 +34,10 @@ namespace { std::string _loggerCat = "RenderableFieldlinesSequence"; + + const GLuint _VA_POSITION = 0; // MUST CORRESPOND TO THE SHADER PROGRAM + const GLuint _VA_COLOR = 1; // MUST CORRESPOND TO THE SHADER PROGRAM + const GLuint _VA_MASKING = 2; // MUST CORRESPOND TO THE SHADER PROGRAM } // namespace namespace openspace { @@ -58,13 +62,13 @@ void RenderableFieldlinesSequence::deinitialize() { } // Stall main thread until thread that's loading states is done! - while (/*!_newStateIsReady &&*/ _isLoadingStateFromDisk) { + while (_isLoadingStateFromDisk) { LWARNING("TRYING TO DESTROY CLASS WHEN A THREAD USING IT IS STILL ACTIVE"); } } bool RenderableFieldlinesSequence::isReady() const { - return _sourceFileType != INVALID; + return _isReady; } void RenderableFieldlinesSequence::render(const RenderData& data, RendererTasks&) { @@ -197,7 +201,7 @@ void RenderableFieldlinesSequence::update(const UpdateData& data) { if (_needsUpdate || _newStateIsReady) { if (_loadingStatesDynamically) { - _states[0] = std::move(_newState); + _states[0] = std::move(*_newState); } updateVertexPositionBuffer(); @@ -245,14 +249,12 @@ void RenderableFieldlinesSequence::updateActiveTriggerTimeIndex(const double CUR } } -// Reading state from disk. Thread safe! +// Reading state from disk. Must be thread safe! void RenderableFieldlinesSequence::readNewState(const std::string& FILEPATH) { - FieldlinesState newState; - - bool isSuccessful = newState.loadStateFromOsfls(FILEPATH); - _newState = std::move(newState); - - _newStateIsReady = true; + _newState = std::make_unique(); + if (_newState->loadStateFromOsfls(FILEPATH)) { + _newStateIsReady = true; + } _isLoadingStateFromDisk = false; } diff --git a/modules/fieldlinessequence/rendering/renderablefieldlinessequence.h b/modules/fieldlinessequence/rendering/renderablefieldlinessequence.h index 6302b43abf..dcf7363c90 100644 --- a/modules/fieldlinessequence/rendering/renderablefieldlinessequence.h +++ b/modules/fieldlinessequence/rendering/renderablefieldlinessequence.h @@ -39,14 +39,15 @@ #include -namespace openspace { +namespace { + enum class SourceFileType; +} -class ghoul::Dictionary; +namespace openspace { class RenderableFieldlinesSequence : public Renderable { public: RenderableFieldlinesSequence(const ghoul::Dictionary& dictionary); - // ~RenderableFieldlinesSequence(); void initialize() override; void deinitialize() override; @@ -56,59 +57,51 @@ public: void render(const RenderData& data, RendererTasks& rendererTask) override; void update(const UpdateData& data) override; private: - enum ColorMethod : int { + // ------------------------------------- ENUMS -------------------------------------// + enum ColorMethod : int { // Used to determine if lines should be colored UNIFORMLY or by an extraQuantity UNIFORM = 0, BY_QUANTITY }; - enum SourceFileType : int { - CDF = 0, - JSON, - OSFLS, - INVALID - }; + // ------------------------------------ STRINGS ------------------------------------// + std::string _name; // Name of the Node! - std::string _name; + // ------------------------------------- FLAGS -------------------------------------// + std::atomic _isLoadingStateFromDisk { false}; // Used for 'runtime-states'. True when loading a new state from disk on another thread. + bool _isReady = false; // If initialization proved successful + bool _loadingStatesDynamically = false; // False => states are stored in RAM (using 'in-RAM-states'), True => states are loaded from disk during runtime (using 'runtime-states') + bool _mustLoadNewStateFromDisk = false; // Used for 'runtime-states': True if new 'runtime-state' must be loaded from disk. False => the previous frame's state should still be shown + bool _needsUpdate = false; // Used for 'in-RAM-states' : True if new 'in-RAM-state' must be loaded. False => the previous frame's state should still be shown + std::atomic _newStateIsReady { false}; // Used for 'runtime-states'. True when finished loading a new state from disk on another thread. + bool _shouldUpdateColorBuffer = false; // True when new state is loaded or user change which quantity to color the lines by + bool _shouldUpdateMaskingBuffer = false; // True when new state is loaded or user change which quantity used for masking out line segments - int _activeStateIndex = -1; - int _activeTriggerTimeIndex = -1; - bool _loadingStatesDynamically = false; // False => loading osfls files into RAM in initializing step - bool _mustLoadNewStateFromDisk = false; - bool _needsUpdate = false; // If still in same state as previous frame == false - bool _shouldUpdateColorBuffer = false; - bool _shouldUpdateMaskingBuffer = false; - FieldlinesState _newState; - size_t _nStates = 0; - float _scalingFactor = 1.f; - double _sequenceEndTime; - SourceFileType _sourceFileType; + // --------------------------------- NUMERICALS ----------------------------------- // + int _activeStateIndex = -1; // Active index of _states. If(==-1)=>no state available for current time. Always the same as _activeTriggerTimeIndex if(_loadingStatesDynamically==true), else always = 0 + int _activeTriggerTimeIndex = -1; // Active index of _startTimes + size_t _nStates = 0; // Number of states in the sequence + float _scalingFactor = 1.f; // In setup it is used to scale JSON coordinates. During runtime it is used to scale domain limits. + double _sequenceEndTime; // Estimated end of sequence. + GLuint _vertexArrayObject = 0; // OpenGL Vertex Array Object + GLuint _vertexColorBuffer = 0; // OpenGL Vertex Buffer Object containing the extraQuantity values used for coloring the lines + GLuint _vertexMaskingBuffer = 0; // OpenGL Vertex Buffer Object containing the extraQuantity values used for masking out segments of the lines + GLuint _vertexPositionBuffer = 0; // OpenGL Vertex Buffer Object containing the vertex positions - std::atomic _isLoadingStateFromDisk{false}; - std::atomic _newStateIsReady{false}; - - std::unique_ptr _dictionary; - std::shared_ptr _transferFunction; // Transfer funtion (tf) + // ----------------------------------- POINTERS ------------------------------------// + std::unique_ptr _dictionary; // The Lua-Modfile-Dictionary used during initialization + std::unique_ptr _newState; // Used for 'runtime-states' when switching out current state to a new state std::unique_ptr _shaderProgram; + std::shared_ptr _transferFunction; // Transfer function used to color lines when _pColorMethod is set to BY_QUANTITY - std::vector _colorTablePaths {"${OPENSPACE_DATA}/colortables/kroyw.txt"}; // Default in case user doesn't specify otherwise - std::vector _sourceFiles; // Stored in RAM if files are loaded at runtime, else emptied after initialization - std::vector _startTimes; - std::vector _states; - std::vector _colorTableRanges; // Values represents min & max values represented in the color table - std::vector _maskingRanges; // Values represents min & max values for valid masking range + // ------------------------------------ VECTORS ----------------------------------- // + std::vector _colorTablePaths; // Paths to color tables. One for each 'extraQuantity' + std::vector _colorTableRanges; // Values represents min & max values represented in the color table + std::vector _maskingRanges; // Values represents min & max limits for valid masking range + std::vector _sourceFiles; // Stores the provided source file paths if using 'runtime-states', else emptied after initialization + std::vector _startTimes; // Contains the _triggerTimes for all FieldlineStates in the sequence + std::vector _states; // Stores the FieldlineStates - GLuint _vertexArrayObject = 0; - GLuint _vertexPositionBuffer = 0; - GLuint _vertexColorBuffer = 0; - GLuint _vertexMaskingBuffer = 0; - - // THESE MUST CORRESPOND TO THE SHADER PROGRAM - // TODO: THIS CAN BE DETERMINED BY ASKING THE SHADER PROGRAM TOO - const GLuint _VA_POSITION = 0; - const GLuint _VA_COLOR = 1; - const GLuint _VA_MASKING = 2; - - // ----------------------------- Properties ----------------------------- + // ---------------------------------- Properties ---------------------------------- // properties::PropertyOwner _pColorGroup; // Group to hold the color properties properties::OptionProperty _pColorMethod; // Uniform/transfer function/topology? properties::OptionProperty _pColorQuantity; // Index of the extra quantity to color lines by @@ -142,22 +135,24 @@ private: properties::TriggerProperty _pFocusOnOriginBtn; // Button which sets camera focus to parent node of the renderable properties::TriggerProperty _pJumpToStartBtn; // Button which executes a time jump to start of sequence + // --------------------- FUNCTIONS USED DURING INITIALIZATION --------------------- // void computeSequenceEndTime(); - bool extractInfoFromDictionary(const ghoul::Dictionary& dictionary); + void definePropertyCallbackFunctions(); + bool extractJsonInfoFromDictionary(fls::Model& model); + bool extractMandatoryInfoFromDictionary(SourceFileType& sourceFileType); + void extractOptionalInfoFromDictionary(std::string& outputFolderPath); + void extractOsflsInfoFromDictionary(); void extractTriggerTimesFromFileNames(); - void readNewState(const std::string& FILEPATH); + void setModelDependentConstants(); + void setupProperties(); + // ------------------------- FUNCTIONS USED DURING RUNTIME ------------------------ // inline bool isWithinSequenceInterval(const double CURRENT_TIME) const; - + void readNewState(const std::string& FILEPATH); void updateActiveTriggerTimeIndex(const double CURRENT_TIME); void updateVertexPositionBuffer(); void updateVertexColorBuffer(); void updateVertexMaskingBuffer(); - - void definePropertyCallbackFunctions(); - void setupProperties(); - - void setModelDependentConstants(); }; } // namespace openspace diff --git a/modules/fieldlinessequence/rendering/renderablefieldlinessequencesetup.cpp b/modules/fieldlinessequence/rendering/renderablefieldlinessequencesetup.cpp index ff1c7f14ad..5f960324b0 100644 --- a/modules/fieldlinessequence/rendering/renderablefieldlinessequencesetup.cpp +++ b/modules/fieldlinessequence/rendering/renderablefieldlinessequencesetup.cpp @@ -128,6 +128,13 @@ namespace { "timeJumpToStart", "Jump to Start Of Sequence", "Performs a time jump to the start of the sequence." }; + enum class SourceFileType : int { + CDF = 0, + JSON, + OSFLS, + INVALID + }; + float stringToFloat(const std::string INPUT, const float BACKUP_VALUE = 0.f) { float tmp; try { @@ -143,8 +150,8 @@ namespace { namespace openspace { -RenderableFieldlinesSequence::RenderableFieldlinesSequence(const ghoul::Dictionary& dictionary) - : Renderable(dictionary), +RenderableFieldlinesSequence::RenderableFieldlinesSequence(const ghoul::Dictionary& DICTIONARY) + : Renderable(DICTIONARY), _pColorGroup({ "Color" }), _pColorMethod(ColorMethodInfo, properties::OptionProperty::DisplayType::Radio), _pColorQuantity(ColorQuantityInfo, properties::OptionProperty::DisplayType::Dropdown), @@ -176,29 +183,37 @@ RenderableFieldlinesSequence::RenderableFieldlinesSequence(const ghoul::Dictiona _pFocusOnOriginBtn(OriginButtonInfo), _pJumpToStartBtn(TimeJumpButtonInfo) { - _dictionary = std::make_unique(dictionary); - // Set the default color table, just in case user defined paths are corrupt! - _transferFunction = std::make_shared(absPath(_colorTablePaths[0])); + _dictionary = std::make_unique(DICTIONARY); } void RenderableFieldlinesSequence::initialize() { LINFO("RenderableFieldlinesSequence::initialize()"); - if (!extractInfoFromDictionary(*_dictionary)) { - _sourceFileType = SourceFileType::INVALID; + // EXTRACT MANDATORY INFORMATION FROM DICTIONARY + SourceFileType sourceFileType = SourceFileType::INVALID; + if (!extractMandatoryInfoFromDictionary(sourceFileType)) { + return; } - // dictionary is no longer necessary as everything is extracted - _dictionary.reset(); + // Set the default color table, just in case the (optional) user defined paths are corrupt! + _colorTablePaths.push_back("${OPENSPACE_DATA}/colortables/kroyw.txt"); + _transferFunction = std::make_shared(absPath(_colorTablePaths[0])); - switch (_sourceFileType) { - case CDF: + // EXTRACT OPTIONAL INFORMATION FROM DICTIONARY + std::string outputFolderPath; + extractOptionalInfoFromDictionary(outputFolderPath); + const bool SHOULD_SAVE_STATES = !outputFolderPath.empty(); + + // EXTRACT SOURCE FILE TYPE SPECIFIC INFOMRATION FROM DICTIONARY & GET STATES FROM SOURCE + switch (sourceFileType) { + case SourceFileType::CDF: LERROR("CDF NOT YET IMPLEMENTED!"); return; break; - case JSON: + case SourceFileType::JSON: LERROR("JSON NOT YET IMPLEMENTED!"); return; break; - case OSFLS: + case SourceFileType::OSFLS: + extractOsflsInfoFromDictionary(); if (_loadingStatesDynamically) { extractTriggerTimesFromFileNames(); FieldlinesState newState; @@ -209,7 +224,7 @@ void RenderableFieldlinesSequence::initialize() { _activeStateIndex = 0; } else { LERROR("The provided .osfls files seem to be corrupt!"); - _sourceFileType = SourceFileType::INVALID; + sourceFileType = SourceFileType::INVALID; } } else { // Load states into RAM! @@ -228,6 +243,9 @@ void RenderableFieldlinesSequence::initialize() { return; } + // dictionary is no longer needed as everything is extracted + _dictionary.reset(); + // At this point there's at least one state loaded into memory! // No need to store source paths in memory if they are already in RAM! if (!_loadingStatesDynamically) { @@ -248,7 +266,7 @@ void RenderableFieldlinesSequence::initialize() { if (!_shaderProgram) { LERROR("Shader program failed initialization!"); - _sourceFileType = SourceFileType::INVALID; + sourceFileType = SourceFileType::INVALID; } //------------------ Initialize OpenGL VBOs and VAOs-------------------------------// @@ -259,42 +277,44 @@ void RenderableFieldlinesSequence::initialize() { // Needed for additive blending setRenderBin(Renderable::RenderBin::Overlay); + + _isReady = true; } -bool RenderableFieldlinesSequence::extractInfoFromDictionary( - const ghoul::Dictionary& dictionary) { +/* + * Returns false if it fails to extract mandatory information! + */ +bool RenderableFieldlinesSequence::extractMandatoryInfoFromDictionary( + SourceFileType& sourceFileType) { - std::string name; - dictionary.getValue(SceneGraphNode::KeyName, name); - _name = name; - name += ": "; + _dictionary->getValue(SceneGraphNode::KeyName, _name); // ------------------- EXTRACT MANDATORY VALUES FROM DICTIONARY ------------------- // std::string inputFileTypeString; - if (!dictionary.getValue(KEY_INPUT_FILE_TYPE, inputFileTypeString)) { - LERROR(name << "The field " << std::string(KEY_INPUT_FILE_TYPE) << " is missing!"); + if (!_dictionary->getValue(KEY_INPUT_FILE_TYPE, inputFileTypeString)) { + LERROR(_name << ": The field " << std::string(KEY_INPUT_FILE_TYPE) << " is missing!"); return false; } else { std::transform(inputFileTypeString.begin(), inputFileTypeString.end(), inputFileTypeString.begin(), ::tolower); // Verify that the input type is correct if (inputFileTypeString == VALUE_INPUT_FILE_TYPE_CDF) { - _sourceFileType = CDF; + sourceFileType = SourceFileType::CDF; } else if (inputFileTypeString == VALUE_INPUT_FILE_TYPE_JSON) { - _sourceFileType = JSON; + sourceFileType = SourceFileType::JSON; } else if (inputFileTypeString == VALUE_INPUT_FILE_TYPE_OSFLS) { - _sourceFileType = OSFLS; + sourceFileType = SourceFileType::OSFLS; } else { - LERROR(name << inputFileTypeString << " is not a recognised " + LERROR(_name << ": " << inputFileTypeString << " is not a recognised " << KEY_INPUT_FILE_TYPE); - _sourceFileType = INVALID; + sourceFileType = SourceFileType::INVALID; return false; } } std::string sourceFolderPath; - if (!dictionary.getValue(KEY_SOURCE_FOLDER, sourceFolderPath)) { - LERROR(name << "The field " << std::string(KEY_SOURCE_FOLDER) << " is missing!"); + if (!_dictionary->getValue(KEY_SOURCE_FOLDER, sourceFolderPath)) { + LERROR(_name << ": The field " << std::string(KEY_SOURCE_FOLDER) << " is missing!"); return false; } @@ -315,19 +335,25 @@ bool RenderableFieldlinesSequence::extractInfoFromDictionary( }), _sourceFiles.end()); // Ensure that there are available and valid source files left if (_sourceFiles.empty()) { - LERROR(name << sourceFolderPath << " contains no ." << inputFileTypeString + LERROR(_name << ": " << sourceFolderPath << " contains no ." << inputFileTypeString << " files!"); return false; } } else { - LERROR(name << "FieldlinesSequence" << sourceFolderPath + LERROR(_name << ": FieldlinesSequence" << sourceFolderPath << " is not a valid directory!"); return false; } + return true; +} + +void RenderableFieldlinesSequence::extractOptionalInfoFromDictionary( + std::string& outputFolderPath) { + // ------------------- EXTRACT OPTIONAL VALUES FROM DICTIONARY ------------------- // ghoul::Dictionary colorTablesPathsDictionary; - if (dictionary.getValue(KEY_COLOR_TABLE_PATHS, colorTablesPathsDictionary)) { + if (_dictionary->getValue(KEY_COLOR_TABLE_PATHS, colorTablesPathsDictionary)) { const size_t N_PROVIDED_PATHS = colorTablesPathsDictionary.size(); if (N_PROVIDED_PATHS > 0) { // Clear the default! It is already specified in the transferFunction @@ -340,7 +366,7 @@ bool RenderableFieldlinesSequence::extractInfoFromDictionary( } ghoul::Dictionary colorTablesRangesDictionary; - if (dictionary.getValue(KEY_COLOR_TABLE_RANGES, colorTablesRangesDictionary)) { + if (_dictionary->getValue(KEY_COLOR_TABLE_RANGES, colorTablesRangesDictionary)) { const size_t N_PROVIDED_RANGES = colorTablesRangesDictionary.size(); for (size_t i = 1; i <= N_PROVIDED_RANGES; ++i) { _colorTableRanges.push_back( @@ -350,30 +376,35 @@ bool RenderableFieldlinesSequence::extractInfoFromDictionary( _colorTableRanges.push_back(glm::vec2(0, 1)); } - // Extract info specific to each inputType - switch (_sourceFileType) { - case CDF: - LERROR(name << "CDF NOT YET IMPLEMENTED!"); - break; - case JSON: - LERROR(name << "JSON NOT YET IMPLEMENTED!"); - break; - case OSFLS: { - bool shouldLoadInRealtime = false; - if (dictionary.getValue(KEY_OSLFS_LOAD_AT_RUNTIME, shouldLoadInRealtime)) { - _loadingStatesDynamically = shouldLoadInRealtime; - } else { - LWARNING(name << KEY_OSLFS_LOAD_AT_RUNTIME << - " isn't specified! States from OSFLS files will be stored in RAM!"); - } - } break; - default: - break; + ghoul::Dictionary maskingRangesDictionary; + if (_dictionary->getValue(KEY_MASKING_RANGES, maskingRangesDictionary)) { + const size_t N_PROVIDED_RANGES = maskingRangesDictionary.size(); + for (size_t i = 1; i <= N_PROVIDED_RANGES; ++i) { + _maskingRanges.push_back( + maskingRangesDictionary.value(std::to_string(i))); + } + } else { + _maskingRanges.push_back(glm::vec2(-100000, 100000)); // Just some default values! } +} +/* + * Returns false if it fails to extract mandatory information! + */ +bool RenderableFieldlinesSequence::extractJsonInfoFromDictionary(fls::Model& model) { return true; } +void RenderableFieldlinesSequence::extractOsflsInfoFromDictionary() { + bool shouldLoadInRealtime = false; + if (_dictionary->getValue(KEY_OSLFS_LOAD_AT_RUNTIME, shouldLoadInRealtime)) { + _loadingStatesDynamically = shouldLoadInRealtime; + } else { + LWARNING(_name << ": " << KEY_OSLFS_LOAD_AT_RUNTIME << + " isn't specified! States will be stored in RAM!"); + } +} + void RenderableFieldlinesSequence::setupProperties() { // -------------- Add non-grouped properties (enablers and buttons) -------------- // addProperty(_pColorABlendEnabled); @@ -441,7 +472,6 @@ void RenderableFieldlinesSequence::setupProperties() { _pMaskingQuantity = 0; _pMaskingMin = std::to_string(_maskingRanges[_pMaskingQuantity].x); _pMaskingMax = std::to_string(_maskingRanges[_pMaskingQuantity].y); - } void RenderableFieldlinesSequence::definePropertyCallbackFunctions() {