/***************************************************************************************** * * * OpenSpace * * * * Copyright (c) 2014-2015 * * * * 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 namespace { std::string _loggerCat = "RenderableFieldlines"; const float defaultFieldlineStepSize = 0.5f;; const glm::vec4 defaultFieldlineColor = glm::vec4(1.f, 0.f, 0.f, 1.f); const std::string keyVectorField = "VectorField"; const std::string keyVectorFieldType = "Type"; const std::string keyVectorFieldFile = "File"; const std::string keyVectorFieldVolumeModel = "Model"; const std::string keyVectorFieldVolumeVariable = "Variables"; const std::string keyFieldlines = "Fieldlines"; const std::string keyFieldlinesStepSize = "Stepsize"; const std::string keyFieldlinesClassification = "Classification"; const std::string keyFieldlinesColor = "Color"; const std::string keySeedPoints = "SeedPoints"; const std::string keySeedPointsType = "Type"; const std::string keySeedPointsFile = "File"; const std::string keySeedPointsTable = "SeedPoints"; const std::string seedPointsSourceFile = "File"; const std::string seedPointsSourceTable = "Table"; const std::string vectorFieldTypeVolumeKameleon = "VolumeKameleon"; const std::string vectorFieldKameleonModelBATSRUS = "BATSRUS"; const std::string vectorFieldKameleonVariableLorentz = "Lorentz"; const int SeedPointSourceFile = 0; const int SeedPointSourceTable = 1; } namespace openspace { RenderableFieldlines::RenderableFieldlines(const ghoul::Dictionary& dictionary) : Renderable(dictionary) , _stepSize("stepSize", "Fieldline Step Size", defaultFieldlineStepSize, 0.f, 10.f) , _classification("classification", "Fieldline Classification", true) , _fieldlineColor( "fieldlineColor", "Fieldline Color", defaultFieldlineColor, glm::vec4(0.f), glm::vec4(1.f) ) , _seedPointSource("source", "SeedPoint Source") , _seedPointSourceFile("sourceFile", "SeedPoint File") , _program(nullptr) , _seedPointsAreDirty(true) , _fieldLinesAreDirty(true) , _fieldlineVAO(0) , _vertexPositionBuffer(0) { ghoul_assert( dictionary.hasKeyAndValue(SceneGraphNode::KeyName), "Renderable does not have a name" ); std::string name; dictionary.getValue(SceneGraphNode::KeyName, name); _loggerCat = "RenderableFieldlines [" + name + "]"; bool success = dictionary.getValue(keyVectorField, _vectorFieldInfo); if (!success) { LERROR("Renderable does not contain a key for '" << keyVectorField << "'"); } success = dictionary.getValue(keyFieldlines, _fieldlineInfo); if (!success) { LERROR("Renderable does not contain a key for '" << keyFieldlines << "'"); } success = dictionary.getValue(keySeedPoints, _seedPointsInfo); if (!success) { LERROR("Renderable does not contain a key for '" << keySeedPoints << "'"); } // @TODO a non-magic number perhaps ---abock setBoundingSphere(PowerScaledScalar::CreatePSS(250.f*6371000.f)); _seedPointSource.addOption(SeedPointSourceFile, "File"); _seedPointSource.addOption(SeedPointSourceTable, "Lua Table"); initializeDefaultPropertyValues(); // @TODO hook up visibility changes ---abock auto dirtyFieldlines = [this]() { this->_fieldLinesAreDirty = true; }; auto dirtySeedpoints = [this]() { 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() { bool success; // Step size float stepSize; success = _fieldlineInfo.getValue(keyFieldlinesStepSize, stepSize); if (success) _stepSize = stepSize; // Classification bool classification; success = _fieldlineInfo.getValue(keyFieldlinesClassification, classification); if (success) _classification = classification; // Fieldline Color glm::vec4 color; success = _fieldlineInfo.getValue(keyFieldlinesColor, color); if (success) _fieldlineColor = color; // Seedpoints Type std::string sourceType; success = _seedPointsInfo.getValue(keySeedPointsType, sourceType); if (success) { if (sourceType == seedPointsSourceFile) { _seedPointSource = SeedPointSourceFile; std::string seedPointSourceFile; success = _seedPointsInfo.getValue(keySeedPointsFile, seedPointSourceFile); if (success) _seedPointSourceFile = absPath(seedPointSourceFile); } else if (sourceType == seedPointsSourceTable) _seedPointSource = SeedPointSourceTable; } } bool RenderableFieldlines::isReady() const { bool programReady = _program != nullptr; bool vectorFieldReady = !_vectorFieldInfo.empty(); bool fieldlineReady = !_fieldlineInfo.empty(); bool seedPointsReady = !_seedPointsInfo.empty(); return programReady && vectorFieldReady && fieldlineReady && seedPointsReady; } bool RenderableFieldlines::initialize() { if (_vectorFieldInfo.empty() || _fieldlineInfo.empty() || _seedPointsInfo.empty()) { return false; } _program = ghoul::opengl::ProgramObject::Build( "Fieldline", "${SHADERS}/modules/fieldlines/fieldline_vs.glsl", "${SHADERS}/modules/fieldlines/fieldline_fs.glsl", "${SHADERS}/modules/fieldlines/fieldline_gs.glsl" ); if (!_program) return false; return true; } bool RenderableFieldlines::deinitialize() { glDeleteVertexArrays(1, &_fieldlineVAO); _fieldlineVAO = 0; glDeleteBuffers(1, &_vertexPositionBuffer); _vertexPositionBuffer = 0; return true; } void RenderableFieldlines::render(const RenderData& data) { _program->activate(); _program->setUniform("modelViewProjection", data.camera.viewProjectionMatrix()); _program->setUniform("modelTransform", glm::mat4(1.0)); _program->setUniform("cameraViewDir", data.camera.viewDirection()); setPscUniforms(_program.get(), &data.camera, data.position); _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); _program->deactivate(); } void RenderableFieldlines::update(const UpdateData&) { if (_program->isDirty()) _program->rebuildFromFile(); if (_seedPointsAreDirty) { loadSeedPoints(); _seedPointsAreDirty = false; _fieldLinesAreDirty = true; } if (_fieldLinesAreDirty) { std::vector fieldlines = generateFieldlines(); if (fieldlines.empty()) return ; int prevEnd = 0; std::vector vertexData; // Arrange data for glMultiDrawArrays for (int 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("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), reinterpret_cast(0)); GLuint colorLocation = 1; glEnableVertexAttribArray(colorLocation); glVertexAttribPointer(colorLocation, 4, GL_FLOAT, GL_FALSE, sizeof(LinePoint), (void*)(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; default: ghoul_assert(false, "Missing case label"); } } void RenderableFieldlines::loadSeedPointsFromFile() { LINFO("Reading seed points from file '" << _seedPointSourceFile.value() << "'"); std::ifstream seedFile(_seedPointSourceFile); if (!seedFile.good()) LERROR("Could not open seed points file '" << _seedPointSourceFile.value() << "'"); else { std::string line; glm::vec3 point; while (std::getline(seedFile, line)) { std::stringstream s(line); s >> point.x; s >> point.y; s >> point.z; _seedPoints.push_back(std::move(point)); } } } void RenderableFieldlines::loadSeedPointsFromTable() { // @TODO needs testing ---abock LINFO("Loading provided list of seed points"); ghoul::Dictionary seedpointsDictionary; _seedPointsInfo.getValue(keySeedPointsTable, seedpointsDictionary); glm::vec3 seedPos; for (const std::string& index : seedpointsDictionary.keys()) { _fieldlineInfo.getValue(keySeedPointsTable + "." + index, seedPos); _seedPoints.push_back(seedPos); } } std::vector RenderableFieldlines::generateFieldlines() { std::string type; bool success = _vectorFieldInfo.getValue(keyVectorFieldType, type); if (!success) { LERROR(keyVectorField << " does not contain a '" << keyVectorFieldType << "' key"); return {}; } if (type == vectorFieldTypeVolumeKameleon) return generateFieldlinesVolumeKameleon(); else { LERROR(keyVectorField << "." << keyVectorFieldType << " does not name a valid type"); return {}; } } std::vector RenderableFieldlines::generateFieldlinesVolumeKameleon() { std::string model; bool success = _vectorFieldInfo.getValue(keyVectorFieldVolumeModel, model); if (!success) { LERROR(keyVectorField << " does not name a model"); return {}; } std::string fileName; success = _vectorFieldInfo.getValue(keyVectorFieldFile, fileName); if (!success) { LERROR(keyVectorField << " does not name a file"); return {}; } fileName = absPath(fileName); //KameleonWrapper::Model modelType; if (model != vectorFieldKameleonModelBATSRUS) { //modelType = KameleonWrapper::Model::BATSRUS; //else { LERROR(keyVectorField << "." << keyVectorFieldVolumeModel << " model '" << model << "' not supported"); return {}; } std::string v1 = keyVectorFieldVolumeVariable + ".1"; std::string v2 = keyVectorFieldVolumeVariable + ".2"; std::string v3 = keyVectorFieldVolumeVariable + ".3"; bool threeVariables = _vectorFieldInfo.hasKeyAndValue(v1) && _vectorFieldInfo.hasKeyAndValue(v2) && _vectorFieldInfo.hasKeyAndValue(v3); bool lorentzForce = _vectorFieldInfo.hasKeyAndValue(v1) && (_vectorFieldInfo.value(v1) == vectorFieldKameleonVariableLorentz); if (!threeVariables && !lorentzForce) { LERROR(keyVectorField << " does not name variables"); return {}; } if (threeVariables) { std::string xVariable, yVariable, zVariable; _vectorFieldInfo.getValue(v1, xVariable); _vectorFieldInfo.getValue(v2, yVariable); _vectorFieldInfo.getValue(v3, zVariable); KameleonWrapper kw(fileName); return kw.getClassifiedFieldLines(xVariable, yVariable, zVariable, _seedPoints, _stepSize); } if (lorentzForce) { KameleonWrapper kw(fileName); return kw.getLorentzTrajectories(_seedPoints, _fieldlineColor, _stepSize); } ghoul_assert(false, "Should not reach this"); return {}; } } // namespace openspace