/***************************************************************************************** * * * OpenSpace * * * * Copyright (c) 2014-2024 * * * * 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 namespace { constexpr std::string_view _loggerCat = "RenderableFieldlines"; constexpr float DefaultFieldlineStepSize = 0.5f; const glm::vec4 DefaultFieldlineColor = glm::vec4(1.f, 0.f, 0.f, 1.f); constexpr std::string_view KeyVectorField = "VectorField"; constexpr std::string_view KeyVectorFieldType = "Type"; constexpr std::string_view KeyVectorFieldFile = "File"; constexpr std::string_view KeyVectorFieldVolumeModel = "Model"; constexpr std::string_view KeyVectorFieldVolumeVariable = "Variables"; constexpr std::string_view KeyFieldlines = "Fieldlines"; constexpr std::string_view KeyFieldlinesColor = "Color"; constexpr std::string_view KeySeedPoints = "SeedPoints"; constexpr std::string_view KeySeedPointsType = "Type"; constexpr std::string_view KeySeedPointsFile = "File"; constexpr std::string_view KeySeedPointsTable = "SeedPoints"; constexpr std::string_view SeedPointsSourceFile = "File"; constexpr std::string_view SeedPointsSourceTable = "Table"; constexpr std::string_view VectorFieldTypeVolumeKameleon = "VolumeKameleon"; constexpr std::string_view VectorFieldKameleonModelBATSRUS = "BATSRUS"; constexpr std::string_view VectorFieldKameleonVariableLorentz = "Lorentz"; constexpr int SeedPointSourceFile = 0; constexpr int SeedPointSourceTable = 1; constexpr openspace::properties::Property::PropertyInfo StepSizeInfo = { "Stepsize", "Fieldline Step Size.", "", // @TODO Missing documentation openspace::properties::Property::Visibility::User }; constexpr openspace::properties::Property::PropertyInfo Classification = { "Classification", "Fieldline Classification", "", // @TODO Missing documentation openspace::properties::Property::Visibility::AdvancedUser }; constexpr openspace::properties::Property::PropertyInfo FieldlineColorInfo = { "FieldlineColor", "Fieldline Color", "", // @TODO Missing documentation openspace::properties::Property::Visibility::AdvancedUser }; constexpr openspace::properties::Property::PropertyInfo SeedPointSourceInfo = { "Source", "SeedPoint Source", "", // @TODO Missing documentation openspace::properties::Property::Visibility::AdvancedUser }; constexpr openspace::properties::Property::PropertyInfo SeedPointFileInfo = { "SourceFile", "SeedPoint File", "", // @TODO Missing documentation openspace::properties::Property::Visibility::AdvancedUser }; } // namespace namespace openspace { RenderableFieldlines::RenderableFieldlines(const ghoul::Dictionary& dictionary) : Renderable(dictionary) , _stepSize(StepSizeInfo, DefaultFieldlineStepSize, 0.f, 10.f) , _classification(Classification, true) , _fieldlineColor( FieldlineColorInfo, DefaultFieldlineColor, glm::vec4(0.f), glm::vec4(1.f) ) , _seedPointSource(SeedPointSourceInfo) , _seedPointSourceFile(SeedPointFileInfo) { std::string identifier = dictionary.value(SceneGraphNode::KeyIdentifier); setIdentifier(identifier); if (!dictionary.hasValue(KeyVectorField)) { LERROR(std::format("Renderable does not contain a key for '{}'", KeyVectorField)); } else { _vectorFieldInfo = dictionary.value(KeyVectorField); } if (!dictionary.hasValue(KeyFieldlines)) { LERROR(std::format("Renderable does not contain a key for '{}'", KeyFieldlines)); } else { _fieldlineInfo = dictionary.value(KeyFieldlines); } if (!dictionary.hasValue(KeySeedPoints)) { LERROR(std::format("Renderable does not contain a key for '{}'", KeySeedPoints)); } else { _seedPointsInfo = dictionary.value(KeySeedPoints); } // @TODO a non-magic number perhaps ---abock setBoundingSphere(250.f*6371000.f); _seedPointSource.addOption(SeedPointSourceFile, "File"); _seedPointSource.addOption(SeedPointSourceTable, "Lua Table"); initializeDefaultPropertyValues(); // @TODO hook up visibility changes ---abock auto dirtyFieldlines = [this]() { _fieldLinesAreDirty = true; }; auto dirtySeedpoints = [this]() { _seedPointsAreDirty = true; }; _stepSize.onChange(dirtyFieldlines); addProperty(_stepSize); addProperty(_classification); _fieldlineColor.setViewOption(properties::Property::ViewOptions::Color); addProperty(_fieldlineColor); _seedPointSource.onChange(dirtySeedpoints); addProperty(_seedPointSource); _seedPointSourceFile.onChange(dirtySeedpoints); addProperty(_seedPointSourceFile); } void RenderableFieldlines::initializeDefaultPropertyValues() { if (_fieldlineInfo.hasKey("Stepsize")) { _stepSize = static_cast( _fieldlineInfo.value("Stepsize") ); } if (_fieldlineInfo.hasKey("Classification")) { _classification = _fieldlineInfo.value("Classification"); } if (_fieldlineInfo.hasKey(KeyFieldlinesColor)) { _fieldlineColor = _fieldlineInfo.value(KeyFieldlinesColor); } if (_seedPointsInfo.hasValue(KeySeedPointsType)) { std::string sourceType = _seedPointsInfo.value(KeySeedPointsType); if (sourceType == SeedPointsSourceFile) { _seedPointSource = SeedPointSourceFile; if (_seedPointsInfo.hasValue(KeySeedPointsFile)) { std::string seedPointSourceFile = _seedPointsInfo.value( KeySeedPointsFile ); _seedPointSourceFile = absPath(seedPointSourceFile).string(); } } else if (sourceType == SeedPointsSourceTable) { _seedPointSource = SeedPointSourceTable; } } } bool RenderableFieldlines::isReady() const { const bool programReady = _program != nullptr; const bool vectorFieldReady = !_vectorFieldInfo.isEmpty(); const bool fieldlineReady = !_fieldlineInfo.isEmpty(); const bool seedPointsReady = !_seedPointsInfo.isEmpty(); return programReady && vectorFieldReady && fieldlineReady && seedPointsReady; } void RenderableFieldlines::initializeGL() { if (_vectorFieldInfo.isEmpty() || _fieldlineInfo.isEmpty() || _seedPointsInfo.isEmpty()) { throw ghoul::RuntimeError("Error initializing"); } _program = global::renderEngine->buildRenderProgram( "Fieldline", absPath("${MODULE_FIELDLINES}/shaders/fieldline_vs.glsl"), absPath("${MODULE_FIELDLINES}/shaders/fieldline_fs.glsl"), absPath("${MODULE_FIELDLINES}/shaders/fieldline_gs.glsl") ); } void RenderableFieldlines::deinitializeGL() { glDeleteVertexArrays(1, &_fieldlineVAO); _fieldlineVAO = 0; glDeleteBuffers(1, &_vertexPositionBuffer); _vertexPositionBuffer = 0; if (_program) { global::renderEngine->removeRenderProgram(_program.get()); _program = nullptr; } } void RenderableFieldlines::render(const RenderData& data, RendererTasks&) { _program->activate(); _program->setUniform("modelViewProjection", data.camera.viewProjectionMatrix()); _program->setUniform("modelTransform", glm::mat4(1.0)); _program->setUniform( "cameraViewDir", glm::vec3(data.camera.viewDirectionWorldSpace()) ); glDisable(GL_CULL_FACE); _program->setUniform("campos", glm::vec4(data.camera.positionVec3(), 1.f)); _program->setUniform("objpos", glm::vec4(data.modelTransform.translation, 0.f)); _program->setUniform("camrot", glm::mat4(data.camera.viewRotationMatrix())); _program->setUniform("scaling", glm::vec2(1.f, 0.f)); _program->setUniform("classification", _classification); if (!_classification) { _program->setUniform("fieldLineColor", _fieldlineColor); } glBindVertexArray(_fieldlineVAO); glMultiDrawArrays( GL_LINE_STRIP_ADJACENCY, &_lineStart[0], &_lineCount[0], static_cast(_lineStart.size()) ); glBindVertexArray(0); glEnable(GL_CULL_FACE); _program->deactivate(); } void RenderableFieldlines::update(const UpdateData&) { if (_program->isDirty()) { _program->rebuildFromFile(); } if (_seedPointsAreDirty) { loadSeedPoints(); _seedPointsAreDirty = false; _fieldLinesAreDirty = true; } if (_fieldLinesAreDirty) { const std::vector& fieldlines = generateFieldlines(); if (fieldlines.empty()) { return; } int prevEnd = 0; std::vector vertexData; // Arrange data for glMultiDrawArrays for (size_t j = 0; j < fieldlines.size(); j++) { _lineStart.push_back(prevEnd); _lineCount.push_back(static_cast(fieldlines[j].size())); prevEnd = prevEnd + static_cast(fieldlines[j].size()); vertexData.insert( vertexData.end(), fieldlines[j].begin(), fieldlines[j].end() ); } LDEBUG(std::format("Number of vertices: {}", vertexData.size())); if (_fieldlineVAO == 0) { glGenVertexArrays(1, &_fieldlineVAO); } glBindVertexArray(_fieldlineVAO); if (_vertexPositionBuffer == 0) { glGenBuffers(1, &_vertexPositionBuffer); } glBindBuffer(GL_ARRAY_BUFFER, _vertexPositionBuffer); glBufferData( GL_ARRAY_BUFFER, vertexData.size() * sizeof(LinePoint), &vertexData.front(), GL_STATIC_DRAW ); GLuint vertexLocation = 0; glEnableVertexAttribArray(vertexLocation); glVertexAttribPointer( vertexLocation, 3, GL_FLOAT, GL_FALSE, sizeof(LinePoint), nullptr ); GLuint colorLocation = 1; glEnableVertexAttribArray(colorLocation); glVertexAttribPointer( colorLocation, 4, GL_FLOAT, GL_FALSE, sizeof(LinePoint), reinterpret_cast(sizeof(glm::vec3)) ); glBindBuffer(GL_ARRAY_BUFFER, 0); glBindVertexArray(0); _fieldLinesAreDirty = false; } } void RenderableFieldlines::loadSeedPoints() { _seedPoints.clear(); switch (_seedPointSource.value()) { case SeedPointSourceFile: loadSeedPointsFromFile(); break; case SeedPointSourceTable: loadSeedPointsFromTable(); break; } } void RenderableFieldlines::loadSeedPointsFromFile() { LINFO(std::format("Reading seed points from '{}'", _seedPointSourceFile.value())); std::ifstream seedFile(_seedPointSourceFile); if (!seedFile.good()) LERROR(std::format( "Could not open seed points file '{}'", _seedPointSourceFile.value() )); else { std::string line; while (ghoul::getline(seedFile, line)) { std::stringstream s(line); glm::vec3 point; s >> point.x; s >> point.y; s >> point.z; _seedPoints.push_back(std::move(point)); } } } void RenderableFieldlines::loadSeedPointsFromTable() { // @TODO needs testing ---abock if (!_seedPointsInfo.hasValue(KeySeedPointsTable)) { return; } LINFO("Loading provided list of seed points"); ghoul::Dictionary seedpointsDictionary = _seedPointsInfo.value(KeySeedPointsType); for (std::string_view index : seedpointsDictionary.keys()) { std::string key = std::format("{}.{}", KeySeedPointsTable, index); // (2020-12-31, abock) Looks to me as if this should be seedpointsDictionary if (_fieldlineInfo.hasValue(key)) { glm::dvec3 seedPos = _fieldlineInfo.value(key); _seedPoints.push_back(seedPos); } } } std::vector RenderableFieldlines::generateFieldlines() { if (!_vectorFieldInfo.hasValue(KeyVectorFieldType)) { LERROR(std::format( "'{}' does not contain a '{}' key", KeyVectorField, KeyVectorFieldType )); return {}; } std::string type = _vectorFieldInfo.value(KeyVectorFieldType); if (type == VectorFieldTypeVolumeKameleon) { return generateFieldlinesVolumeKameleon(); } else { LERROR(std::format( "{}.{} does not name a valid type", KeyVectorField, KeyVectorFieldType )); return {}; } } std::vector RenderableFieldlines::generateFieldlinesVolumeKameleon() { if (!_vectorFieldInfo.hasValue(KeyVectorFieldVolumeModel)) { LERROR(std::format("'{}' does not name a model", KeyVectorField)); return {}; } std::string model = _vectorFieldInfo.value(KeyVectorFieldVolumeModel); if (!_vectorFieldInfo.hasValue(KeyVectorFieldFile)) { LERROR(std::format("'{}' does not name a file", KeyVectorField)); return {}; } std::filesystem::path fileName = absPath( _vectorFieldInfo.value(KeyVectorFieldFile) ); //KameleonWrapper::Model modelType; if (model != VectorFieldKameleonModelBATSRUS) { //modelType = KameleonWrapper::Model::BATSRUS; //else { LERROR(std::format( "{}.{} model '{}' not supported", KeyVectorField, KeyVectorFieldVolumeModel, model )); return {}; } const std::string v1 = std::string(KeyVectorFieldVolumeVariable) + ".1"; const std::string v2 = std::string(KeyVectorFieldVolumeVariable) + ".2"; const std::string v3 = std::string(KeyVectorFieldVolumeVariable) + ".3"; const bool threeVariables = _vectorFieldInfo.hasValue(v1) && _vectorFieldInfo.hasValue(v2) && _vectorFieldInfo.hasValue(v3); const bool lorentzForce = _vectorFieldInfo.hasValue(v1) && (_vectorFieldInfo.value(v1) == VectorFieldKameleonVariableLorentz); if (!threeVariables && !lorentzForce) { LERROR(std::format("'{}' does not name variables", KeyVectorField)); return {}; } if (threeVariables) { std::string xVariable = _vectorFieldInfo.value(v1); std::string yVariable = _vectorFieldInfo.value(v2); std::string zVariable = _vectorFieldInfo.value(v3); KameleonWrapper kw = KameleonWrapper(fileName); return kw.classifiedFieldLines( xVariable, yVariable, zVariable, _seedPoints, _stepSize ); } if (lorentzForce) { KameleonWrapper kw = KameleonWrapper(fileName); return kw.lorentzTrajectories(_seedPoints, _fieldlineColor, _stepSize); } ghoul_assert(false, "Should not reach this"); return {}; } } // namespace openspace