diff --git a/modules/fieldlinessequence/rendering/renderablestreamnodes.cpp b/modules/fieldlinessequence/rendering/renderablestreamnodes.cpp index 383e56dc31..0ba29a96e8 100644 --- a/modules/fieldlinessequence/rendering/renderablestreamnodes.cpp +++ b/modules/fieldlinessequence/rendering/renderablestreamnodes.cpp @@ -24,6 +24,7 @@ //including our own h file #include +//includes from fieldlinessequence, might not need all of them #include #include #include @@ -36,16 +37,30 @@ #include #include #include +//test debugging tools more then logmanager +#include +#include + + #include #include #include #include #include +//this is a call to use the nlohmann json file using json = nlohmann::json; + namespace { + //log category constexpr const char* _loggerCat = "RenderableStreamNodes"; + //gl variables for shaders, probably needed some of them atleast + constexpr const GLuint VaPosition = 0; // MUST CORRESPOND TO THE SHADER PROGRAM + constexpr const GLuint VaColor = 1; // MUST CORRESPOND TO THE SHADER PROGRAM + constexpr const GLuint VaMasking = 2; // MUST CORRESPOND TO THE SHADER PROGRAM + + // ----- KEYS POSSIBLE IN MODFILE. EXPECTED DATA TYPE OF VALUE IN [BRACKETS] ----- // // ---------------------------- MANDATORY MODFILE KEYS ---------------------------- // // [STRING] "cdf", "json" or "osfls" @@ -81,18 +96,32 @@ namespace { "Color of particles." }; constexpr openspace::properties::Property::PropertyInfo StreamsenabledInfo = { - "Streamsenabled", + "streamsEnabled", "Stream Direction", "Toggles the rendering of moving particles along the lines. Can, for example, " "illustrate magnetic flow." }; enum class SourceFileType : int { - Cdf, Json = 0, + Cdf, Osfls, Invalid }; + + float stringToFloat(const std::string input, const float backupValue = 0.f) { + float tmp; + try { + tmp = std::stof(input); + } + catch (const std::invalid_argument& ia) { + LWARNING(fmt::format( + "Invalid argument: {}. '{}' is NOT a valid number", ia.what(), input + )); + return backupValue; + } + return tmp; + } } //namespace namespace openspace { @@ -141,10 +170,17 @@ namespace openspace { // Setup shader program _shaderProgram = global::renderEngine.buildRenderProgram( - "FieldlinesSequence", - absPath("${MODULE_FIELDLINESSEQUENCE}/shaders/fieldlinessequence_vs.glsl"), - absPath("${MODULE_FIELDLINESSEQUENCE}/shaders/fieldlinessequence_fs.glsl") + "Streamnodes", + absPath("${MODULE_FIELDLINESSEQUENCE}/shaders/streamnodes_vs.glsl"), + absPath("${MODULE_FIELDLINESSEQUENCE}/shaders/streamnodes_fs.glsl") ); + glGenVertexArrays(1, &_vertexArrayObject); + glGenBuffers(1, &_vertexPositionBuffer); + glGenBuffers(1, &_vertexColorBuffer); + glGenBuffers(1, &_vertexMaskingBuffer); + + // Probably not needed, seems to be needed for additive blending + setRenderBin(Renderable::RenderBin::Overlay); } /** @@ -153,8 +189,8 @@ namespace openspace { * Returns false if it fails to extract mandatory information! */ bool RenderableStreamNodes::extractMandatoryInfoFromDictionary( - SourceFileType& SourceFileType){ - + SourceFileType& sourceFileType) +{ _dictionary->getValue(SceneGraphNode::KeyIdentifier, _identifier); // ------------------- EXTRACT MANDATORY VALUES FROM DICTIONARY ------------------- // @@ -165,51 +201,193 @@ bool RenderableStreamNodes::extractMandatoryInfoFromDictionary( else{ // Verify that the input type is corrects if (inputFileTypeString == ValueInputFileTypeJson) { - //sourceFileType = SourceFileType::Json; + sourceFileType = SourceFileType::Json; } else { LERROR(fmt::format( "{}: {} is not a recognized {}", _identifier, inputFileTypeString, KeyInputFileType )); - //sourceFileType = SourceFileType::Invalid; + sourceFileType = SourceFileType::Invalid; return false; } } + + std::string sourceFolderPath; + if (!_dictionary->getValue(KeySourceFolder, sourceFolderPath)) { + LERROR(fmt::format("{}: The field {} is missing", _identifier, KeySourceFolder)); + return false; + } + + // Ensure that the source folder exists and then extract + // the files with the same extension as + ghoul::filesystem::Directory sourceFolder(sourceFolderPath); + 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, sourceFolderPath, inputFileTypeString + )); + return false; + } + } + else { + LERROR(fmt::format( + "{}: FieldlinesSequence {} is not a valid directory", + _identifier, + sourceFolderPath + )); + return false; + } + return true; +} + +//void RenderableStreamNodes::extractOptionalInfoFromDictionary( + // std::string& outputFolderPath) +//{ + // ------------------- EXTRACT OPTIONAL VALUES FROM DICTIONARY ------------------- // + // bool streamsEnabled; + //if (_dictionary->getValue(KeyStreamsEnabled, streamsEnabledValue)) { + //_pStreamsEnabled = streamsEnabledValue; + //} +//} + +bool RenderableStreamNodes::extractJsonInfoFromDictionary(fls::Model& model){ + std::string modelStr; + if (_dictionary->getValue(KeyJsonSimulationModel, modelStr)) { + std::transform( + modelStr.begin(), + modelStr.end(), + modelStr.begin(), + [](char c) { return static_cast(::tolower(c)); } + ); + model = fls::stringToModel(modelStr); + } + else { + LERROR(fmt::format( + "{}: Must specify '{}'", _identifier, KeyJsonSimulationModel + )); + return false; + } + + float scaleFactor; + if (_dictionary->getValue(KeyJsonScalingFactor, scaleFactor)) { + _scalingFactor = scaleFactor; + } + else { + LWARNING(fmt::format( + "{}: Does not provide scalingFactor. Assumes coordinates are in meters", + _identifier + )); + } + return true; } void RenderableStreamNodes::setupProperties() { // -------------- Add non-grouped properties (enablers and buttons) -------------- // - addPropertySubOwner(_pStreamGroup); _pStreamGroup.addProperty(_pStreamColor); _pStreamGroup.addProperty(_pStreamsEnabled); - } -std::vector RenderableStreamNodes::LoadJsonfile() { - /* if (path.empty()) { + + +void RenderableStreamNodes::deinitializeGL() { + if (_shaderProgram) { + global::renderEngine.removeRenderProgram(_shaderProgram.get()); + _shaderProgram = nullptr; + } +} + +bool RenderableStreamNodes::isReady() const { + return _shaderProgram != nullptr; +} + +// Extract J2000 time from file names +// Requires files to be named as such: 'YYYY-MM-DDTHH-MM-SS-XXX.osfls' +void RenderableStreamNodes::extractTriggerTimesFromFileNames() { + // number of characters in filename (excluding '.osfls') + constexpr const int FilenameSize = 23; + // size(".osfls") + constexpr const int ExtSize = 6; + + for (const std::string& filePath : _sourceFiles) { + 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); + _startTimes.push_back(triggerTime); + } +} +void RenderableStreamNodes::render(const RenderData& data, RendererTasks&) { + if (_activeTriggerTimeIndex != -1) { + _shaderProgram->activate(); + + // Calculate Model View MatrixProjection + const glm::dmat4 rotMat = glm::dmat4(data.modelTransform.rotation); + const glm::dmat4 modelMat = + glm::translate(glm::dmat4(1.0), data.modelTransform.translation) * + rotMat * + glm::dmat4(glm::scale(glm::dmat4(1), glm::dvec3(data.modelTransform.scale))); + const glm::dmat4 modelViewMat = data.camera.combinedViewMatrix() * modelMat; + + _shaderProgram->setUniform("modelViewProjection", + data.camera.sgctInternal.projectionMatrix() * glm::mat4(modelViewMat)); + + _shaderProgram->setUniform("lineColor", _pStreamColor); + // Flow/Particles + _shaderProgram->setUniform("usingParticles", _pStreamsEnabled); + } +} + +void RenderableStreamNodes::update(const UpdateData& data) { + if (_shaderProgram->isDirty()) { + _shaderProgram->rebuildFromFile(); + } +} + + + + std::vector RenderableStreamNodes::LoadJsonfile() { + /*if (path.empty()) { + return std::vector(); + } + */ + std::string data; + std::ifstream streamdata("data.json", std::ifstream::binary); + json jsonobj; + streamdata >> jsonobj; + + //printDebug(jsonobj["stream0"]); + LDEBUG(jsonobj["stream0"]); + log(ghoul::logging::LogLevel::Debug, _loggerCat, jsonobj["stream0"]); + //for + //LWARNING(fmt::format("Testar json", data)); + + //LWARNING(fmt::format("Testar json")); + return std::vector(); } - */ - /* std::string data; - std::ifstream streamdata("data.json", std::ifstream::binary); - streamdata >> data; - LWARNING(fmt::format("Testar json", data)); - */ - LWARNING(fmt::format("Testar json")); - - return std::vector(); - - -} bool RenderableStreamNodes::loadJsonStatesIntoRAM(const std::string& outputFolder) { return true; } -bool RenderableStreamNodes::extractJsonInfoFromDictionary(fls::Model& model) -{ - return false; -} + } // namespace openspace diff --git a/modules/fieldlinessequence/rendering/renderablestreamnodes.h b/modules/fieldlinessequence/rendering/renderablestreamnodes.h index 293266986c..3bd6729217 100644 --- a/modules/fieldlinessequence/rendering/renderablestreamnodes.h +++ b/modules/fieldlinessequence/rendering/renderablestreamnodes.h @@ -42,6 +42,7 @@ class RenderableStreamNodes : public Renderable { public : RenderableStreamNodes(const ghoul::Dictionary& dictionary); + //these two are needed for startup and close i think. void initializeGL() override; void deinitializeGL() override; @@ -56,7 +57,6 @@ private: enum class ColorMethod : int { Uniform = 0, ByQuantity - }; // ------------------------------------ STRINGS ------------------------------------// @@ -67,6 +67,24 @@ private: // loaded from disk during runtime (using 'runtime-states') bool _loadingStatesDynamically = false; + // --------------------------------- NUMERICALS ----------------------------------- // + // In setup it is used to scale JSON coordinates. During runtime it is used to scale + // domain limits. + float _scalingFactor = 1.f; + // Active index of _startTimes + int _activeTriggerTimeIndex = -1; + + + + GLuint _vertexArrayObject = 0; + // OpenGL Vertex Buffer Object containing the extraQuantity values used for coloring + // the lines + GLuint _vertexColorBuffer = 0; + // OpenGL Vertex Buffer Object containing the extraQuantity values used for masking + // out segments of the lines + GLuint _vertexMaskingBuffer = 0; + // OpenGL Vertex Buffer Object containing the vertex positions + GLuint _vertexPositionBuffer = 0; // ---------------------------------- Properties ---------------------------------- // properties::Vec4Property _pStreamColor; // Toggle flow [ON/OFF] @@ -79,6 +97,10 @@ private: // initialization std::vector _sourceFiles; + // ------------------------------------ VECTORS ----------------------------------- // + // Contains the _triggerTimes for all FieldlineStates in the sequence + std::vector _startTimes; + // ----------------------------------- POINTERS ------------------------------------// // The Lua-Modfile-Dictionary used during initialization std::unique_ptr _dictionary; @@ -87,13 +109,14 @@ private: // --------------------- FUNCTIONS USED DURING INITIALIZATION --------------------- // bool extractJsonInfoFromDictionary(fls::Model& model); - bool extractMandatoryInfoFromDictionary(SourceFileType& sourceFileType); + bool extractMandatoryInfoFromDictionary(SourceFileType& sourceFileType); void extractOptionalInfoFromDictionary(std::string& outputFolderPath); bool loadJsonStatesIntoRAM(const std::string& outputFolder); bool extractJsonInfoFromDictionary(fls::Model& model); //std::vector LoadJsonfile(const std::string& filename); std::vector LoadJsonfile(); void setupProperties(); + void extractTriggerTimesFromFileNames() // ------------------------- FUNCTIONS USED DURING RUNTIME ------------------------ //