mirror of
https://github.com/OpenSpace/OpenSpace.git
synced 2026-04-24 04:58:59 -05:00
REFACTOR: split renderablefieldlinessequence.cpp into two files and give propertiy variables a 'p' as prefix
This commit is contained in:
@@ -27,11 +27,13 @@ include(${OPENSPACE_CMAKE_EXT_DIR}/module_definition.cmake)
|
||||
set(HEADER_FILES
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/rendering/renderablefieldlinessequence.h
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/util/fieldlinesstate.h
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/util/commons.h
|
||||
)
|
||||
source_group("Header Files" FILES ${HEADER_FILES})
|
||||
|
||||
set(SOURCE_FILES
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/rendering/renderablefieldlinessequence.cpp
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/rendering/renderablefieldlinessequencesetup.cpp
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/util/fieldlinesstate.cpp
|
||||
)
|
||||
source_group("Source Files" FILES ${SOURCE_FILES})
|
||||
|
||||
@@ -25,268 +25,19 @@
|
||||
#include <modules/fieldlinessequence/rendering/renderablefieldlinessequence.h>
|
||||
|
||||
#include <openspace/engine/openspaceengine.h>
|
||||
#include <openspace/interaction/navigationhandler.h>
|
||||
#include <openspace/rendering/renderengine.h>
|
||||
#include <openspace/scene/scene.h>
|
||||
#include <openspace/scene/scenegraphnode.h>
|
||||
#include <openspace/util/timemanager.h>
|
||||
#include <openspace/util/updatestructures.h>
|
||||
|
||||
#include <ghoul/filesystem/filesystem.h>
|
||||
#include <ghoul/opengl/programobject.h>
|
||||
#include <ghoul/opengl/textureunit.h>
|
||||
|
||||
using std::string;
|
||||
|
||||
namespace {
|
||||
std::string _loggerCat = "RenderableFieldlinesSequence";
|
||||
|
||||
// ----- KEYS POSSIBLE IN MODFILE. EXPECTED DATA TYPE OF VALUE IN [BRACKETS] ----- //
|
||||
// ---------------------------- MANDATORY MODFILE KEYS ---------------------------- //
|
||||
const char* KEY_INPUT_FILE_TYPE = "InputFileType"; // [STRING]
|
||||
const char* KEY_SOURCE_FOLDER = "SourceFolder"; // [STRING]
|
||||
|
||||
// ---------------------------- OPTIONAL MODFILE KEYS ---------------------------- //
|
||||
const char* KEY_COLOR_TABLE_PATHS = "ColorTablePaths"; // [STRING ARRAY] Values should be paths to .txt files
|
||||
const char* KEY_COLOR_TABLE_RANGES = "ColorTableRanges";// [VEC2 ARRAY] Values should be paths to .txt files
|
||||
const char* KEY_OSLFS_LOAD_AT_RUNTIME = "LoadAtRuntime"; // [BOOLEAN] If value False => Load in initializing step and store in RAM
|
||||
|
||||
// ------------- POSSIBLE STRING VALUES FOR CORRESPONDING MODFILE KEY ------------- //
|
||||
const char* VALUE_INPUT_FILE_TYPE_CDF = "cdf";
|
||||
const char* VALUE_INPUT_FILE_TYPE_JSON = "json";
|
||||
const char* VALUE_INPUT_FILE_TYPE_OSFLS = "osfls";
|
||||
|
||||
static const openspace::properties::Property::PropertyInfo ColorMethodInfo = {
|
||||
"colorMethod", "Color Method", "Color lines uniformly or using color tables based on extra quantities like e.g. temperature or particle density."
|
||||
};
|
||||
static const openspace::properties::Property::PropertyInfo ColorQuantityInfo = {
|
||||
"colorQuantity", "Quantity to Color By", "Quantity used to color lines if the \"By Quantity\" color method is selected."
|
||||
};
|
||||
static const openspace::properties::Property::PropertyInfo ColorQuantityMinInfo = {
|
||||
"colorQuantityMin", "ColorTable Min Value", "Value to map to the lowest end of the color table."
|
||||
};
|
||||
static const openspace::properties::Property::PropertyInfo ColorQuantityMaxInfo = {
|
||||
"colorQuantityMax", "ColorTable Max Value", "Value to map to the highest end of the color table."
|
||||
};
|
||||
static const openspace::properties::Property::PropertyInfo ColorTablePathInfo = {
|
||||
"colorTablePath", "Path to Color Table", "Color Table/Transfer Function to use for \"By Quantity\" coloring."
|
||||
};
|
||||
static const openspace::properties::Property::PropertyInfo ColorUniformInfo = {
|
||||
"uniform", "Uniform Line Color", "The uniform color of lines shown when \"Color Method\" is set to \"Uniform\"."
|
||||
};
|
||||
static const openspace::properties::Property::PropertyInfo FlowColorInfo = {
|
||||
"color", "Color", "Color of particles."
|
||||
};
|
||||
static const openspace::properties::Property::PropertyInfo EnableFlowInfo = {
|
||||
"Enable", "ON/OFF",
|
||||
"Toggles the rendering of moving particles along the lines. Can e.g. illustrate magnetic flow."
|
||||
};
|
||||
static const openspace::properties::Property::PropertyInfo ReverseFlowInfo = {
|
||||
"reversed", "Reversed Flow", "Toggle to make the flow move in the opposite direction."
|
||||
};
|
||||
static const openspace::properties::Property::PropertyInfo ParticleSizeInfo = {
|
||||
"particleSize", "Particle Size", "Size of the particles."
|
||||
};
|
||||
static const openspace::properties::Property::PropertyInfo ParticleSpacingInfo = {
|
||||
"particleSpacing", "Particle Spacing", "Spacing inbetween particles."
|
||||
};
|
||||
static const openspace::properties::Property::PropertyInfo FlowSpeedInfo = {
|
||||
"speed", "Speed", "Speed of the flow."
|
||||
};
|
||||
static const openspace::properties::Property::PropertyInfo OriginButtonInfo = {
|
||||
"focusCameraOnParent", "Focus Camera", "Focus camera on parent."
|
||||
};
|
||||
static const openspace::properties::Property::PropertyInfo TimeJumpButtonInfo = {
|
||||
"timeJumpToStart", "Jump to Start Time", "Performs a time jump to the start of the sequence."
|
||||
};
|
||||
|
||||
enum ColorMethod { UNIFORM = 0, BY_QUANTITY };
|
||||
} // namespace
|
||||
|
||||
namespace openspace {
|
||||
|
||||
RenderableFieldlinesSequence::RenderableFieldlinesSequence(const ghoul::Dictionary& dictionary)
|
||||
: Renderable(dictionary),
|
||||
_colorGroup({ "Color" }),
|
||||
_colorMethod(ColorMethodInfo, properties::OptionProperty::DisplayType::Radio),
|
||||
_colorQuantity(ColorQuantityInfo, properties::OptionProperty::DisplayType::Dropdown),
|
||||
_colorQuantityMin(ColorQuantityMinInfo),
|
||||
_colorQuantityMax(ColorQuantityMaxInfo),
|
||||
_colorTablePath(ColorTablePathInfo),
|
||||
_colorUniform(ColorUniformInfo, glm::vec4(0.75f, 0.5f, 0.0f, 0.5f),
|
||||
glm::vec4(0.f), glm::vec4(1.f)),
|
||||
_flowColor(FlowColorInfo, glm::vec4(0.8f, 0.7f, 0.0f, 0.6f),
|
||||
glm::vec4(0.f), glm::vec4(1.f)),
|
||||
_flowEnabled(EnableFlowInfo, true),
|
||||
_flowGroup({ "Flow" }),
|
||||
_flowParticleSize(ParticleSizeInfo, 5, 0, 500),
|
||||
_flowParticleSpacing(ParticleSpacingInfo, 60, 0, 500),
|
||||
_flowReversed(ReverseFlowInfo, false),
|
||||
_flowSpeed(FlowSpeedInfo, 20, 0, 1000),
|
||||
_focusOnOriginBtn(OriginButtonInfo),
|
||||
_jumpToStartBtn(TimeJumpButtonInfo) {
|
||||
|
||||
// Set the default color table, just in case user defined paths are corrupt!
|
||||
_transferFunction = std::make_shared<TransferFunction>(absPath(_colorTablePaths[0]));
|
||||
|
||||
if(!extractInfoFromDictionary(dictionary)) {
|
||||
_sourceFileType = INVALID;
|
||||
}
|
||||
}
|
||||
|
||||
void RenderableFieldlinesSequence::initialize() {
|
||||
switch (_sourceFileType) {
|
||||
case CDF:
|
||||
LERROR("CDF NOT YET IMPLEMENTED!");
|
||||
break;
|
||||
case JSON:
|
||||
LERROR("JSON NOT YET IMPLEMENTED!");
|
||||
break;
|
||||
case OSFLS:
|
||||
if (_isLoadingStatesAtRuntime) {
|
||||
extractTriggerTimesFromFileNames();
|
||||
FieldlinesState newState;
|
||||
bool loadedSuccessfully = newState.loadStateFromOsfls(_sourceFiles[0]);
|
||||
if (loadedSuccessfully) {
|
||||
_states.push_back(newState);
|
||||
_nStates = _startTimes.size();
|
||||
_activeStateIndex = 0;
|
||||
} else {
|
||||
LERROR("The provided .osfls files seem to be corrupt!");
|
||||
_sourceFileType = INVALID;
|
||||
}
|
||||
} else {
|
||||
// Load states into RAM!
|
||||
for (string filePath : _sourceFiles) {
|
||||
FieldlinesState newState;
|
||||
bool loadedSuccessfully = newState.loadStateFromOsfls(filePath);
|
||||
if (loadedSuccessfully) {
|
||||
_states.push_back(newState);
|
||||
_startTimes.push_back(newState.triggerTime());
|
||||
_nStates++;
|
||||
}
|
||||
}
|
||||
}
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
// 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 (!_isLoadingStatesAtRuntime) {
|
||||
_sourceFiles.clear();
|
||||
}
|
||||
|
||||
computeSequenceEndTime();
|
||||
|
||||
// --------------------- HANDLE PROPERTIES --------------------- //
|
||||
// Add Property Groups
|
||||
addPropertySubOwner(_colorGroup);
|
||||
addPropertySubOwner(_flowGroup);
|
||||
|
||||
// Add non-grouped properties
|
||||
addProperty(_focusOnOriginBtn);
|
||||
addProperty(_jumpToStartBtn);
|
||||
|
||||
// Add Properties to the groups
|
||||
_colorGroup.addProperty(_colorMethod);
|
||||
_colorGroup.addProperty(_colorQuantity);
|
||||
_colorGroup.addProperty(_colorQuantityMin);
|
||||
_colorGroup.addProperty(_colorQuantityMax);
|
||||
_colorGroup.addProperty(_colorTablePath);
|
||||
_colorGroup.addProperty(_colorUniform);
|
||||
|
||||
_flowGroup.addProperty(_flowEnabled);
|
||||
_flowGroup.addProperty(_flowReversed);
|
||||
_flowGroup.addProperty(_flowColor);
|
||||
_flowGroup.addProperty(_flowParticleSize);
|
||||
_flowGroup.addProperty(_flowParticleSpacing);
|
||||
_flowGroup.addProperty(_flowSpeed);
|
||||
|
||||
// Add Options to OptionProperties
|
||||
_colorMethod.addOption(ColorMethod::UNIFORM, "Uniform");
|
||||
_colorMethod.addOption(ColorMethod::BY_QUANTITY, "By Quantity");
|
||||
|
||||
// Add option for each extra quantity. We assume that there are just as many names to
|
||||
// extra quantities as there are extra quantities. We also assume that all states in
|
||||
// the given sequence have the same extra quantities!
|
||||
const size_t N_EXTRA_QUANTITIES = _states[0].nExtraQuantities();
|
||||
auto EXTRA_VARIABLE_NAMES_VEC = _states[0].extraQuantityNames();
|
||||
for (size_t i = 0; i < N_EXTRA_QUANTITIES; ++i) {
|
||||
_colorQuantity.addOption(i, EXTRA_VARIABLE_NAMES_VEC[i]);
|
||||
}
|
||||
// Each quantity should have its own color table and color table range, no more, no less
|
||||
_colorTablePaths.resize(N_EXTRA_QUANTITIES, _colorTablePaths.back());
|
||||
_colorTableRanges.resize(N_EXTRA_QUANTITIES, _colorTableRanges.back());
|
||||
|
||||
// Add Property Callback Functions
|
||||
_colorQuantity.onChange([this] {
|
||||
LDEBUG("CHANGED COLORING QUANTITY");
|
||||
_shouldUpdateColorBuffer = true;
|
||||
_colorQuantityMin = std::to_string(_colorTableRanges[_colorQuantity].x);
|
||||
_colorQuantityMax = std::to_string(_colorTableRanges[_colorQuantity].y);
|
||||
_activeColorTable = &_colorTablePaths[_colorQuantity];
|
||||
_colorTablePath = *_activeColorTable;
|
||||
});
|
||||
|
||||
_colorTablePath.onChange([this] {
|
||||
// TOGGLE ACTIVE SHADER PROGRAM
|
||||
_transferFunction->setPath(_colorTablePath);
|
||||
*_activeColorTable = _colorTablePath;
|
||||
});
|
||||
|
||||
_colorQuantityMin.onChange([this] {
|
||||
LDEBUG("CHANGED MIN VALUE");
|
||||
// TODO CHECK IF VALID NUMBER!
|
||||
// _updateTransferFunctionMin = true;
|
||||
_colorTableRanges[_colorQuantity].x = std::stof(_colorQuantityMin);
|
||||
});
|
||||
|
||||
_colorQuantityMax.onChange([this] {
|
||||
LDEBUG("CHANGED MAX VALUE");
|
||||
// TODO CHECK IF VALID NUMBER!
|
||||
// _updateTransferFunctionMin = true;
|
||||
_colorTableRanges[_colorQuantity].y = std::stof(_colorQuantityMax);
|
||||
});
|
||||
|
||||
_focusOnOriginBtn.onChange([this] {
|
||||
LDEBUG("SET FOCUS NODE TO PARENT");
|
||||
// TODO CHECK IF VALID NUMBER!
|
||||
// _updateTransferFunctionMin = true;
|
||||
//OsEng.navigationHandler().setFocusNode();
|
||||
SceneGraphNode* node = OsEng.renderEngine().scene()->sceneGraphNode(_name);
|
||||
if (!node) {
|
||||
LWARNING("Could not find a node in scenegraph called '" << _name << "'");
|
||||
return;
|
||||
}
|
||||
OsEng.navigationHandler().setFocusNode(node->parent());
|
||||
OsEng.navigationHandler().resetCameraDirection();
|
||||
});
|
||||
|
||||
_jumpToStartBtn.onChange([this] {
|
||||
LDEBUG("Jump in time to start of sequence!");
|
||||
OsEng.timeManager().time().setTime(_startTimes[0]);
|
||||
});
|
||||
|
||||
// Setup shader program
|
||||
_shaderProgram = OsEng.renderEngine().buildRenderProgram(
|
||||
"FieldlinesSequence",
|
||||
"${MODULE_FIELDLINESSEQUENCE}/shaders/fieldlinessequence_vs.glsl",
|
||||
"${MODULE_FIELDLINESSEQUENCE}/shaders/fieldlinessequence_fs.glsl"
|
||||
);
|
||||
|
||||
if (!_shaderProgram) {
|
||||
LERROR("Shader program failed initialization!");
|
||||
_sourceFileType = INVALID;
|
||||
}
|
||||
|
||||
//------------------ Initialize OpenGL VBOs and VAOs-------------------------------//
|
||||
glGenVertexArrays(1, &_vertexArrayObject);
|
||||
glGenBuffers(1, &_vertexPositionBuffer);
|
||||
glGenBuffers(1, &_vertexColorBuffer);
|
||||
}
|
||||
|
||||
void RenderableFieldlinesSequence::deinitialize() {
|
||||
glDeleteVertexArrays(1, &_vertexArrayObject);
|
||||
_vertexArrayObject = 0;
|
||||
@@ -294,6 +45,9 @@ void RenderableFieldlinesSequence::deinitialize() {
|
||||
glDeleteBuffers(1, &_vertexPositionBuffer);
|
||||
_vertexPositionBuffer = 0;
|
||||
|
||||
glDeleteBuffers(1, &_vertexColorBuffer);
|
||||
_vertexColorBuffer = 0;
|
||||
|
||||
RenderEngine& renderEngine = OsEng.renderEngine();
|
||||
if (_shaderProgram) {
|
||||
renderEngine.removeRenderProgram(_shaderProgram);
|
||||
@@ -326,26 +80,25 @@ void RenderableFieldlinesSequence::render(const RenderData& data, RendererTasks&
|
||||
_shaderProgram->setUniform("modelViewProjection",
|
||||
data.camera.sgctInternal.projectionMatrix() * glm::mat4(MODEL_VIEW_MAT));
|
||||
|
||||
_shaderProgram->setUniform("colorMethod", _pColorMethod);
|
||||
_shaderProgram->setUniform("lineColor", _pColorUniform);
|
||||
|
||||
_shaderProgram->setUniform("colorMethod", _colorMethod);
|
||||
_shaderProgram->setUniform("lineColor", _colorUniform);
|
||||
|
||||
if (_colorMethod == ColorMethod::BY_QUANTITY) {
|
||||
if (_pColorMethod == ColorMethod::BY_QUANTITY) {
|
||||
ghoul::opengl::TextureUnit textureUnit;
|
||||
textureUnit.activate();
|
||||
_transferFunction->bind(); // Calls update internally
|
||||
_shaderProgram->setUniform("colorTable", textureUnit);
|
||||
_shaderProgram->setUniform("colorTableRange",
|
||||
_colorTableRanges[_colorQuantity]);
|
||||
_colorTableRanges[_pColorQuantity]);
|
||||
}
|
||||
|
||||
// Flow/Particles
|
||||
_shaderProgram->setUniform("usingParticles", _flowEnabled);
|
||||
_shaderProgram->setUniform("flowColor", _flowColor);
|
||||
_shaderProgram->setUniform("particleSize", _flowParticleSize);
|
||||
_shaderProgram->setUniform("particleSpeed", _flowSpeed);
|
||||
_shaderProgram->setUniform("particleSpacing", _flowParticleSpacing);
|
||||
_shaderProgram->setUniform("time", OsEng.runTime() * (_flowReversed ? -1 : 1));
|
||||
_shaderProgram->setUniform("flowColor", _pFlowColor);
|
||||
_shaderProgram->setUniform("usingParticles", _pFlowEnabled);
|
||||
_shaderProgram->setUniform("particleSize", _pFlowParticleSize);
|
||||
_shaderProgram->setUniform("particleSpacing", _pFlowParticleSpacing);
|
||||
_shaderProgram->setUniform("particleSpeed", _pFlowSpeed);
|
||||
_shaderProgram->setUniform("time", OsEng.runTime() * (_pFlowReversed ? -1 : 1));
|
||||
|
||||
glBindVertexArray(_vertexArrayObject);
|
||||
glMultiDrawArrays(
|
||||
@@ -377,7 +130,7 @@ void RenderableFieldlinesSequence::update(const UpdateData& data) {
|
||||
|
||||
updateActiveTriggerTimeIndex(CURRENT_TIME);
|
||||
|
||||
if (_isLoadingStatesAtRuntime) {
|
||||
if (_loadingStatesDynamically) {
|
||||
_mustLoadNewStateFromDisk = true;
|
||||
} else {
|
||||
_needsUpdate = true;
|
||||
@@ -404,7 +157,7 @@ void RenderableFieldlinesSequence::update(const UpdateData& data) {
|
||||
}
|
||||
|
||||
if (_needsUpdate || _newStateIsReady) {
|
||||
if (_isLoadingStatesAtRuntime) {
|
||||
if (_loadingStatesDynamically) {
|
||||
_states[0] = std::move(_newState);
|
||||
}
|
||||
|
||||
@@ -413,7 +166,7 @@ void RenderableFieldlinesSequence::update(const UpdateData& data) {
|
||||
if (_states[_activeStateIndex].nExtraQuantities() > 0) {
|
||||
_shouldUpdateColorBuffer = true;
|
||||
} else {
|
||||
_colorMethod = ColorMethod::UNIFORM;
|
||||
_pColorMethod = ColorMethod::UNIFORM;
|
||||
}
|
||||
|
||||
// Everything is set and ready for rendering!
|
||||
@@ -437,7 +190,8 @@ void RenderableFieldlinesSequence::updateActiveTriggerTimeIndex(const double CUR
|
||||
auto iter = std::upper_bound(_startTimes.begin(), _startTimes.end(), CURRENT_TIME);
|
||||
if (iter != _startTimes.end()) {
|
||||
if ( iter != _startTimes.begin()) {
|
||||
_activeTriggerTimeIndex = std::distance(_startTimes.begin(), iter) - 1;
|
||||
_activeTriggerTimeIndex =
|
||||
static_cast<int>(std::distance(_startTimes.begin(), iter)) - 1;
|
||||
} else {
|
||||
_activeTriggerTimeIndex = 0;
|
||||
}
|
||||
@@ -485,7 +239,7 @@ void RenderableFieldlinesSequence::updateVertexColorBuffer() {
|
||||
|
||||
bool isSuccessful;
|
||||
const std::vector<float>& QUANTITY_VEC =
|
||||
_states[_activeStateIndex].extraQuantity(_colorQuantity, isSuccessful);
|
||||
_states[_activeStateIndex].extraQuantity(_pColorQuantity, isSuccessful);
|
||||
|
||||
if (isSuccessful) {
|
||||
glBufferData(GL_ARRAY_BUFFER, QUANTITY_VEC.size() * sizeof(float),
|
||||
@@ -498,150 +252,7 @@ void RenderableFieldlinesSequence::updateVertexColorBuffer() {
|
||||
}
|
||||
}
|
||||
|
||||
bool RenderableFieldlinesSequence::extractInfoFromDictionary(
|
||||
const ghoul::Dictionary& dictionary) {
|
||||
|
||||
string name;
|
||||
dictionary.getValue(SceneGraphNode::KeyName, name);
|
||||
_name = name;
|
||||
name += ": ";
|
||||
|
||||
// ------------------- EXTRACT MANDATORY VALUES FROM DICTIONARY ------------------- //
|
||||
string inputFileTypeValue;
|
||||
if(!dictionary.getValue(KEY_INPUT_FILE_TYPE, inputFileTypeValue)) {
|
||||
LERROR(name << "The field " << string(KEY_INPUT_FILE_TYPE) << " is missing!");
|
||||
return false;
|
||||
} else {
|
||||
std::transform(inputFileTypeValue.begin(), inputFileTypeValue.end(),
|
||||
inputFileTypeValue.begin(), ::tolower);
|
||||
// Verify that the input type is correct
|
||||
if (inputFileTypeValue == VALUE_INPUT_FILE_TYPE_CDF) {
|
||||
_sourceFileType = CDF;
|
||||
} else if (inputFileTypeValue == VALUE_INPUT_FILE_TYPE_JSON) {
|
||||
_sourceFileType = JSON;
|
||||
} else if (inputFileTypeValue == VALUE_INPUT_FILE_TYPE_OSFLS) {
|
||||
_sourceFileType = OSFLS;
|
||||
} else {
|
||||
LERROR(name << inputFileTypeValue << " is not a recognised "
|
||||
<< KEY_INPUT_FILE_TYPE);
|
||||
_sourceFileType = INVALID;
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
string sourceFolderPath;
|
||||
if(!dictionary.getValue(KEY_SOURCE_FOLDER, sourceFolderPath)) {
|
||||
LERROR(name << "The field " << string(KEY_SOURCE_FOLDER) << " is missing!");
|
||||
return false;
|
||||
}
|
||||
|
||||
// Ensure that the source folder exists and then extract
|
||||
// the files with the same extension as <inputFileTypeValue>
|
||||
ghoul::filesystem::Directory sourceFolder(sourceFolderPath);
|
||||
if (FileSys.directoryExists(sourceFolder)) {
|
||||
// Extract all file paths from the provided folder (Non-recursively! Sorted!)
|
||||
_sourceFiles = sourceFolder.readFiles(ghoul::Boolean::No, ghoul::Boolean::Yes);
|
||||
|
||||
// Remove all files that don't have <inputFileTypeValue> as extension
|
||||
_sourceFiles.erase(std::remove_if(_sourceFiles.begin(), _sourceFiles.end(),
|
||||
[inputFileTypeValue] (string str) {
|
||||
const size_t EXT_LENGTH = inputFileTypeValue.length();
|
||||
string sub = str.substr(str.length() - EXT_LENGTH, EXT_LENGTH);
|
||||
std::transform(sub.begin(), sub.end(), sub.begin(), ::tolower);
|
||||
return sub != inputFileTypeValue;
|
||||
}), _sourceFiles.end());
|
||||
// Ensure that there are available and valid source files left
|
||||
if (_sourceFiles.empty()) {
|
||||
LERROR(name << sourceFolderPath << " contains no ." << inputFileTypeValue
|
||||
<< " files!");
|
||||
return false;
|
||||
}
|
||||
} else {
|
||||
LERROR(name << "FieldlinesSequence" << sourceFolderPath
|
||||
<< " is not a valid directory!");
|
||||
return false;
|
||||
}
|
||||
|
||||
// Extract optional info from modfile
|
||||
ghoul::Dictionary 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
|
||||
_colorTablePaths.clear();
|
||||
for (size_t i = 1; i <= N_PROVIDED_PATHS; ++i) {
|
||||
_colorTablePaths.push_back(
|
||||
colorTablesPathsDictionary.value<std::string>( std::to_string(i) ) );
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
ghoul::Dictionary 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(
|
||||
colorTablesRangesDictionary.value<glm::vec2>( std::to_string(i) ) );
|
||||
}
|
||||
} else {
|
||||
_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)) {
|
||||
_isLoadingStatesAtRuntime = shouldLoadInRealtime;
|
||||
} else {
|
||||
LWARNING(name << KEY_OSLFS_LOAD_AT_RUNTIME <<
|
||||
" isn't specified! OSFLS files will be loaded during runtime!");
|
||||
}
|
||||
} break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
// Calculate expected end time.
|
||||
void RenderableFieldlinesSequence::computeSequenceEndTime() {
|
||||
if (_nStates > 1) {
|
||||
const double LAST_TRIGGER_TIME = _startTimes[_nStates - 1];
|
||||
const double SEQUENCE_DURATION = LAST_TRIGGER_TIME - _startTimes[0];
|
||||
const double AVERAGE_STATE_DURATION = SEQUENCE_DURATION / (static_cast<double>(_nStates) - 1.0);
|
||||
_sequenceEndTime = LAST_TRIGGER_TIME + AVERAGE_STATE_DURATION;
|
||||
} else {
|
||||
// If there's just one state it should never disappear!
|
||||
_sequenceEndTime = DBL_MAX;
|
||||
}
|
||||
}
|
||||
|
||||
// Extract J2000 time from file names
|
||||
// Requires files to be named as such: 'YYYY-MM-DDTHH-MM-SS-XXX.osfls'
|
||||
void RenderableFieldlinesSequence::extractTriggerTimesFromFileNames() {
|
||||
const size_t FILENAME_SIZE = 23; // number of characters in filename (excluding '.osfls')
|
||||
const size_t EXT_SIZE = 6; // size(".osfls")
|
||||
|
||||
for (const std::string& FILEPATH : _sourceFiles) {
|
||||
const size_t STR_LENGTH = FILEPATH.size();
|
||||
// Extract the filename from the path (without extension)
|
||||
std::string timeString = FILEPATH.substr(STR_LENGTH - FILENAME_SIZE - EXT_SIZE,
|
||||
FILENAME_SIZE - 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 TRIGGER_TIME = Time::convertTime(timeString);
|
||||
_startTimes.push_back(TRIGGER_TIME);
|
||||
}
|
||||
}
|
||||
|
||||
} // namespace openspace
|
||||
|
||||
@@ -34,13 +34,14 @@
|
||||
#include <openspace/properties/vector/vec4property.h>
|
||||
#include <openspace/rendering/transferfunction.h>
|
||||
|
||||
|
||||
#include <modules/fieldlinessequence/util/fieldlinesstate.h>
|
||||
|
||||
#include <atomic>
|
||||
|
||||
namespace openspace {
|
||||
|
||||
class ghoul::Dictionary;
|
||||
|
||||
class RenderableFieldlinesSequence : public Renderable {
|
||||
public:
|
||||
RenderableFieldlinesSequence(const ghoul::Dictionary& dictionary);
|
||||
@@ -54,6 +55,11 @@ public:
|
||||
void render(const RenderData& data, RendererTasks& rendererTask) override;
|
||||
void update(const UpdateData& data) override;
|
||||
private:
|
||||
enum ColorMethod : int {
|
||||
UNIFORM = 0,
|
||||
BY_QUANTITY
|
||||
};
|
||||
|
||||
enum SourceFileType : int {
|
||||
CDF = 0,
|
||||
JSON,
|
||||
@@ -63,12 +69,12 @@ private:
|
||||
|
||||
std::string _name;
|
||||
|
||||
int _activeStateIndex = -1;
|
||||
int _activeTriggerTimeIndex = -1;
|
||||
bool _isLoadingStatesAtRuntime = true; // False => loading osfls at runtime
|
||||
bool _mustLoadNewStateFromDisk = false;
|
||||
bool _needsUpdate = false; // If still in same state as previous frame == false
|
||||
bool _shouldUpdateColorBuffer = false;
|
||||
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;
|
||||
FieldlinesState _newState;
|
||||
size_t _nStates = 0;
|
||||
double _sequenceEndTime;
|
||||
@@ -77,10 +83,10 @@ private:
|
||||
std::atomic<bool> _isLoadingStateFromDisk{false};
|
||||
std::atomic<bool> _newStateIsReady{false};
|
||||
|
||||
std::unique_ptr<ghoul::Dictionary> _dictionary;
|
||||
std::shared_ptr<TransferFunction> _transferFunction; // Transfer funtion (tf)
|
||||
std::unique_ptr<ghoul::opengl::ProgramObject> _shaderProgram;
|
||||
|
||||
std::string* _activeColorTable;
|
||||
std::vector<std::string> _colorTablePaths {"${OPENSPACE_DATA}/colortables/kroyw.txt"}; // Default in case user doesn't specify otherwise
|
||||
std::vector<std::string> _sourceFiles; // Stored in RAM if files are loaded at runtime, else emptied after initialization
|
||||
std::vector<double> _startTimes;
|
||||
@@ -97,24 +103,24 @@ private:
|
||||
const GLuint _VA_COLOR = 1;
|
||||
|
||||
// ----------------------------- Properties -----------------------------
|
||||
properties::PropertyOwner _colorGroup; // Group to hold the color properties
|
||||
properties::OptionProperty _colorMethod; // Uniform/transfer function/topology?
|
||||
properties::OptionProperty _colorQuantity; // Index of the extra quantity to color lines by
|
||||
properties::StringProperty _colorQuantityMin; // Color table/transfer function min
|
||||
properties::StringProperty _colorQuantityMax; // Color table/transfer function max
|
||||
properties::StringProperty _colorTablePath; // Color table/transfer function for "By Quantity" coloring
|
||||
properties::Vec4Property _colorUniform; // Uniform Field Line Color
|
||||
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
|
||||
properties::StringProperty _pColorQuantityMin; // Color table/transfer function min
|
||||
properties::StringProperty _pColorQuantityMax; // Color table/transfer function max
|
||||
properties::StringProperty _pColorTablePath; // Color table/transfer function for "By Quantity" coloring
|
||||
properties::Vec4Property _pColorUniform; // Uniform Field Line Color
|
||||
|
||||
properties::Vec4Property _flowColor; // Simulated particles' color
|
||||
properties::BoolProperty _flowEnabled; // Toggle flow [ON/OFF]
|
||||
properties::PropertyOwner _flowGroup; // Gropu to hold the flow/particle properties
|
||||
properties::IntProperty _flowParticleSize; // Size of simulated flow particles
|
||||
properties::IntProperty _flowParticleSpacing; // Size of simulated flow particles
|
||||
properties::BoolProperty _flowReversed; // Toggle flow direction [FORWARDS/BACKWARDS]
|
||||
properties::IntProperty _flowSpeed; // Speed of simulated flow
|
||||
properties::Vec4Property _pFlowColor; // Simulated particles' color
|
||||
properties::BoolProperty _pFlowEnabled; // Toggle flow [ON/OFF]
|
||||
properties::PropertyOwner _pFlowGroup; // Group to hold the flow/particle properties
|
||||
properties::IntProperty _pFlowParticleSize; // Size of simulated flow particles
|
||||
properties::IntProperty _pFlowParticleSpacing; // Size of simulated flow particles
|
||||
properties::BoolProperty _pFlowReversed; // Toggle flow direction [FORWARDS/BACKWARDS]
|
||||
properties::IntProperty _pFlowSpeed; // Speed of simulated flow
|
||||
|
||||
properties::TriggerProperty _focusOnOriginBtn; // Button which sets camera focus to parent node of the renderable
|
||||
properties::TriggerProperty _jumpToStartBtn; // Button which executes a time jump to start of sequence
|
||||
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
|
||||
|
||||
void computeSequenceEndTime();
|
||||
bool extractInfoFromDictionary(const ghoul::Dictionary& dictionary);
|
||||
@@ -126,6 +132,10 @@ private:
|
||||
void updateActiveTriggerTimeIndex(const double CURRENT_TIME);
|
||||
void updateVertexPositionBuffer();
|
||||
void updateVertexColorBuffer();
|
||||
void updateVertexMaskingBuffer();
|
||||
|
||||
void definePropertyCallbackFunctions();
|
||||
void setupProperties();
|
||||
};
|
||||
|
||||
} // namespace openspace
|
||||
|
||||
@@ -0,0 +1,460 @@
|
||||
/*****************************************************************************************
|
||||
* *
|
||||
* OpenSpace *
|
||||
* *
|
||||
* Copyright (c) 2014-2017 *
|
||||
* *
|
||||
* 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 <modules/fieldlinessequence/rendering/renderablefieldlinessequence.h>
|
||||
|
||||
#include <openspace/engine/openspaceengine.h>
|
||||
#include <openspace/interaction/navigationhandler.h>
|
||||
#include <openspace/scene/scene.h>
|
||||
#include <openspace/scene/scenegraphnode.h>
|
||||
#include <openspace/util/timemanager.h>
|
||||
|
||||
#include <ghoul/filesystem/filesystem.h>
|
||||
#include <ghoul/opengl/programobject.h>
|
||||
|
||||
namespace {
|
||||
std::string _loggerCat = "RenderableFieldlinesSequence";
|
||||
|
||||
// ----- KEYS POSSIBLE IN MODFILE. EXPECTED DATA TYPE OF VALUE IN [BRACKETS] ----- //
|
||||
// ---------------------------- MANDATORY MODFILE KEYS ---------------------------- //
|
||||
const char* KEY_INPUT_FILE_TYPE = "InputFileType"; // [STRING]
|
||||
const char* KEY_SOURCE_FOLDER = "SourceFolder"; // [STRING]
|
||||
|
||||
// ---------------------------- OPTIONAL MODFILE KEYS ---------------------------- //
|
||||
const char* KEY_COLOR_TABLE_PATHS = "ColorTablePaths"; // [STRING ARRAY] Values should be paths to .txt files
|
||||
const char* KEY_COLOR_TABLE_RANGES = "ColorTableRanges";// [VEC2 ARRAY] Values should be entered as {X, Y}, where X & Y are numbers
|
||||
const char* KEY_OSLFS_LOAD_AT_RUNTIME = "LoadAtRuntime"; // [BOOLEAN] If value False => Load in initializing step and store in RAM
|
||||
|
||||
// ------------- POSSIBLE STRING VALUES FOR CORRESPONDING MODFILE KEY ------------- //
|
||||
const char* VALUE_INPUT_FILE_TYPE_CDF = "cdf";
|
||||
const char* VALUE_INPUT_FILE_TYPE_JSON = "json";
|
||||
const char* VALUE_INPUT_FILE_TYPE_OSFLS = "osfls";
|
||||
|
||||
// --------------------------------- Property Info -------------------------------- //
|
||||
static const openspace::properties::Property::PropertyInfo ColorMethodInfo = {
|
||||
"colorMethod", "Color Method", "Color lines uniformly or using color tables based on extra quantities like e.g. temperature or particle density."
|
||||
};
|
||||
static const openspace::properties::Property::PropertyInfo ColorQuantityInfo = {
|
||||
"colorQuantity", "Quantity to Color By", "Quantity used to color lines if the \"By Quantity\" color method is selected."
|
||||
};
|
||||
static const openspace::properties::Property::PropertyInfo ColorQuantityMinInfo = {
|
||||
"colorQuantityMin", "ColorTable Min Value", "Value to map to the lowest end of the color table."
|
||||
};
|
||||
static const openspace::properties::Property::PropertyInfo ColorQuantityMaxInfo = {
|
||||
"colorQuantityMax", "ColorTable Max Value", "Value to map to the highest end of the color table."
|
||||
};
|
||||
static const openspace::properties::Property::PropertyInfo ColorTablePathInfo = {
|
||||
"colorTablePath", "Path to Color Table", "Color Table/Transfer Function to use for \"By Quantity\" coloring."
|
||||
};
|
||||
static const openspace::properties::Property::PropertyInfo ColorUniformInfo = {
|
||||
"uniform", "Uniform Line Color", "The uniform color of lines shown when \"Color Method\" is set to \"Uniform\"."
|
||||
};
|
||||
static const openspace::properties::Property::PropertyInfo FlowColorInfo = {
|
||||
"color", "Color", "Color of particles."
|
||||
};
|
||||
static const openspace::properties::Property::PropertyInfo FlowEnabledInfo = {
|
||||
"flowEnabled", "Flow Direction",
|
||||
"Toggles the rendering of moving particles along the lines. Can e.g. illustrate magnetic flow."
|
||||
};
|
||||
static const openspace::properties::Property::PropertyInfo FlowReversedInfo = {
|
||||
"reversed", "Reversed Flow", "Toggle to make the flow move in the opposite direction."
|
||||
};
|
||||
static const openspace::properties::Property::PropertyInfo FlowParticleSizeInfo = {
|
||||
"particleSize", "Particle Size", "Size of the particles."
|
||||
};
|
||||
static const openspace::properties::Property::PropertyInfo FlowParticleSpacingInfo = {
|
||||
"particleSpacing", "Particle Spacing", "Spacing inbetween particles."
|
||||
};
|
||||
static const openspace::properties::Property::PropertyInfo FlowSpeedInfo = {
|
||||
"speed", "Speed", "Speed of the flow."
|
||||
};
|
||||
static const openspace::properties::Property::PropertyInfo OriginButtonInfo = {
|
||||
"focusCameraOnParent", "Focus Camera", "Focus camera on parent."
|
||||
};
|
||||
static const openspace::properties::Property::PropertyInfo TimeJumpButtonInfo = {
|
||||
"timeJumpToStart", "Jump to Start Of Sequence", "Performs a time jump to the start of the sequence."
|
||||
};
|
||||
|
||||
float stringToFloat(const std::string INPUT, const float BACKUP_VALUE = 0.f) {
|
||||
float tmp;
|
||||
try {
|
||||
tmp = std::stof(INPUT);
|
||||
} catch (const std::invalid_argument& ia) {
|
||||
LWARNING("Invalid argument: " << ia.what() << ". '" << INPUT <<
|
||||
"' is NOT a valid number!");
|
||||
return BACKUP_VALUE;
|
||||
}
|
||||
return tmp;
|
||||
}
|
||||
} // namespace
|
||||
|
||||
namespace openspace {
|
||||
|
||||
RenderableFieldlinesSequence::RenderableFieldlinesSequence(const ghoul::Dictionary& dictionary)
|
||||
: Renderable(dictionary),
|
||||
_pColorGroup({ "Color" }),
|
||||
_pColorMethod(ColorMethodInfo, properties::OptionProperty::DisplayType::Radio),
|
||||
_pColorQuantity(ColorQuantityInfo, properties::OptionProperty::DisplayType::Dropdown),
|
||||
_pColorQuantityMin(ColorQuantityMinInfo),
|
||||
_pColorQuantityMax(ColorQuantityMaxInfo),
|
||||
_pColorTablePath(ColorTablePathInfo),
|
||||
_pColorUniform(ColorUniformInfo, glm::vec4(0.75f, 0.5f, 0.0f, 0.5f),
|
||||
glm::vec4(0.f), glm::vec4(1.f)),
|
||||
_pFlowColor(FlowColorInfo, glm::vec4(0.8f, 0.7f, 0.0f, 0.6f),
|
||||
glm::vec4(0.f), glm::vec4(1.f)),
|
||||
_pFlowEnabled(FlowEnabledInfo, true),
|
||||
_pFlowGroup({ "Flow" }),
|
||||
_pFlowParticleSize(FlowParticleSizeInfo, 5, 0, 500),
|
||||
_pFlowParticleSpacing(FlowParticleSpacingInfo, 60, 0, 500),
|
||||
_pFlowReversed(FlowReversedInfo, false),
|
||||
_pFlowSpeed(FlowSpeedInfo, 20, 0, 1000),
|
||||
_pFocusOnOriginBtn(OriginButtonInfo),
|
||||
_pJumpToStartBtn(TimeJumpButtonInfo) {
|
||||
|
||||
_dictionary = std::make_unique<ghoul::Dictionary>(dictionary);
|
||||
// Set the default color table, just in case user defined paths are corrupt!
|
||||
_transferFunction = std::make_shared<TransferFunction>(absPath(_colorTablePaths[0]));
|
||||
}
|
||||
|
||||
void RenderableFieldlinesSequence::initialize() {
|
||||
LINFO("RenderableFieldlinesSequence::initialize()");
|
||||
|
||||
if (!extractInfoFromDictionary(*_dictionary)) {
|
||||
_sourceFileType = SourceFileType::INVALID;
|
||||
}
|
||||
|
||||
// dictionary is no longer necessary as everything is extracted
|
||||
_dictionary.reset();
|
||||
|
||||
switch (_sourceFileType) {
|
||||
case CDF:
|
||||
LERROR("CDF NOT YET IMPLEMENTED!"); return;
|
||||
break;
|
||||
case JSON:
|
||||
LERROR("JSON NOT YET IMPLEMENTED!"); return;
|
||||
break;
|
||||
case OSFLS:
|
||||
if (_loadingStatesDynamically) {
|
||||
extractTriggerTimesFromFileNames();
|
||||
FieldlinesState newState;
|
||||
bool loadedSuccessfully = newState.loadStateFromOsfls(_sourceFiles[0]);
|
||||
if (loadedSuccessfully) {
|
||||
_states.push_back(newState);
|
||||
_nStates = _startTimes.size();
|
||||
_activeStateIndex = 0;
|
||||
} else {
|
||||
LERROR("The provided .osfls files seem to be corrupt!");
|
||||
_sourceFileType = SourceFileType::INVALID;
|
||||
}
|
||||
} else {
|
||||
// Load states into RAM!
|
||||
for (std::string filePath : _sourceFiles) {
|
||||
FieldlinesState newState;
|
||||
bool loadedSuccessfully = newState.loadStateFromOsfls(filePath);
|
||||
if (loadedSuccessfully) {
|
||||
_states.push_back(newState);
|
||||
_startTimes.push_back(newState.triggerTime());
|
||||
_nStates++;
|
||||
}
|
||||
}
|
||||
}
|
||||
break;
|
||||
default:
|
||||
return;
|
||||
}
|
||||
|
||||
// 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) {
|
||||
_sourceFiles.clear();
|
||||
}
|
||||
|
||||
computeSequenceEndTime();
|
||||
|
||||
setupProperties();
|
||||
|
||||
// Setup shader program
|
||||
_shaderProgram = OsEng.renderEngine().buildRenderProgram(
|
||||
"FieldlinesSequence",
|
||||
"${MODULE_FIELDLINESSEQUENCE}/shaders/fieldlinessequence_vs.glsl",
|
||||
"${MODULE_FIELDLINESSEQUENCE}/shaders/fieldlinessequence_fs.glsl"
|
||||
);
|
||||
|
||||
if (!_shaderProgram) {
|
||||
LERROR("Shader program failed initialization!");
|
||||
_sourceFileType = SourceFileType::INVALID;
|
||||
}
|
||||
|
||||
//------------------ Initialize OpenGL VBOs and VAOs-------------------------------//
|
||||
glGenVertexArrays(1, &_vertexArrayObject);
|
||||
glGenBuffers(1, &_vertexPositionBuffer);
|
||||
glGenBuffers(1, &_vertexColorBuffer);
|
||||
}
|
||||
|
||||
bool RenderableFieldlinesSequence::extractInfoFromDictionary(
|
||||
const ghoul::Dictionary& dictionary) {
|
||||
|
||||
std::string name;
|
||||
dictionary.getValue(SceneGraphNode::KeyName, name);
|
||||
_name = name;
|
||||
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!");
|
||||
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;
|
||||
} else if (inputFileTypeString == VALUE_INPUT_FILE_TYPE_JSON) {
|
||||
_sourceFileType = JSON;
|
||||
} else if (inputFileTypeString == VALUE_INPUT_FILE_TYPE_OSFLS) {
|
||||
_sourceFileType = OSFLS;
|
||||
} else {
|
||||
LERROR(name << inputFileTypeString << " is not a recognised "
|
||||
<< KEY_INPUT_FILE_TYPE);
|
||||
_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!");
|
||||
return false;
|
||||
}
|
||||
|
||||
// Ensure that the source folder exists and then extract
|
||||
// the files with the same extension as <inputFileTypeString>
|
||||
ghoul::filesystem::Directory sourceFolder(sourceFolderPath);
|
||||
if (FileSys.directoryExists(sourceFolder)) {
|
||||
// Extract all file paths from the provided folder (Non-recursively! Sorted!)
|
||||
_sourceFiles = sourceFolder.readFiles(ghoul::Boolean::No, ghoul::Boolean::Yes);
|
||||
|
||||
// Remove all files that don't have <inputFileTypeString> as extension
|
||||
_sourceFiles.erase(std::remove_if(_sourceFiles.begin(), _sourceFiles.end(),
|
||||
[inputFileTypeString](std::string str) {
|
||||
const size_t EXT_LENGTH = inputFileTypeString.length();
|
||||
std::string sub = str.substr(str.length() - EXT_LENGTH, EXT_LENGTH);
|
||||
std::transform(sub.begin(), sub.end(), sub.begin(), ::tolower);
|
||||
return sub != inputFileTypeString;
|
||||
}), _sourceFiles.end());
|
||||
// Ensure that there are available and valid source files left
|
||||
if (_sourceFiles.empty()) {
|
||||
LERROR(name << sourceFolderPath << " contains no ." << inputFileTypeString
|
||||
<< " files!");
|
||||
return false;
|
||||
}
|
||||
} else {
|
||||
LERROR(name << "FieldlinesSequence" << sourceFolderPath
|
||||
<< " is not a valid directory!");
|
||||
return false;
|
||||
}
|
||||
|
||||
// ------------------- EXTRACT OPTIONAL VALUES FROM DICTIONARY ------------------- //
|
||||
ghoul::Dictionary 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
|
||||
_colorTablePaths.clear();
|
||||
for (size_t i = 1; i <= N_PROVIDED_PATHS; ++i) {
|
||||
_colorTablePaths.push_back(
|
||||
colorTablesPathsDictionary.value<std::string>(std::to_string(i)));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
ghoul::Dictionary 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(
|
||||
colorTablesRangesDictionary.value<glm::vec2>(std::to_string(i)));
|
||||
}
|
||||
} else {
|
||||
_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;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
void RenderableFieldlinesSequence::setupProperties() {
|
||||
// -------------- Add non-grouped properties (enablers and buttons) -------------- //
|
||||
addProperty(_pFlowEnabled);
|
||||
addProperty(_pFocusOnOriginBtn);
|
||||
addProperty(_pJumpToStartBtn);
|
||||
|
||||
// ----------------------------- Add Property Groups ----------------------------- //
|
||||
addPropertySubOwner(_pColorGroup);
|
||||
addPropertySubOwner(_pFlowGroup);
|
||||
|
||||
// ------------------------- Add Properties to the groups ------------------------- //
|
||||
_pColorGroup.addProperty(_pColorMethod);
|
||||
_pColorGroup.addProperty(_pColorQuantity);
|
||||
_pColorGroup.addProperty(_pColorQuantityMin);
|
||||
_pColorGroup.addProperty(_pColorQuantityMax);
|
||||
_pColorGroup.addProperty(_pColorTablePath);
|
||||
_pColorGroup.addProperty(_pColorUniform);
|
||||
|
||||
_pFlowGroup.addProperty(_pFlowReversed);
|
||||
_pFlowGroup.addProperty(_pFlowColor);
|
||||
_pFlowGroup.addProperty(_pFlowParticleSize);
|
||||
_pFlowGroup.addProperty(_pFlowParticleSpacing);
|
||||
_pFlowGroup.addProperty(_pFlowSpeed);
|
||||
|
||||
// ----------------------- Add Options to OptionProperties ----------------------- //
|
||||
_pColorMethod.addOption(ColorMethod::UNIFORM, "Uniform");
|
||||
_pColorMethod.addOption(ColorMethod::BY_QUANTITY, "By Quantity");
|
||||
|
||||
/* Add option for each extra quantity. We assume that there are just as many names to
|
||||
extra quantities as there are extra quantities. We also assume that all states in
|
||||
the given sequence have the same extra quantities! */
|
||||
const size_t N_EXTRA_QUANTITIES = _states[0].nExtraQuantities();
|
||||
auto EXTRA_VARIABLE_NAMES_VEC = _states[0].extraQuantityNames();
|
||||
for (int i = 0; i < N_EXTRA_QUANTITIES; ++i) {
|
||||
_pColorQuantity.addOption(i, EXTRA_VARIABLE_NAMES_VEC[i]);
|
||||
}
|
||||
// Each quantity should have its own color table and color table range, no more, no less
|
||||
_colorTablePaths.resize(N_EXTRA_QUANTITIES, _colorTablePaths.back());
|
||||
_colorTableRanges.resize(N_EXTRA_QUANTITIES, _colorTableRanges.back());
|
||||
|
||||
definePropertyCallbackFunctions();
|
||||
|
||||
// Set defaults
|
||||
_pColorQuantity = 0;
|
||||
_pColorQuantityMin = std::to_string(_colorTableRanges[_pColorQuantity].x);
|
||||
_pColorQuantityMax = std::to_string(_colorTableRanges[_pColorQuantity].y);
|
||||
_pColorTablePath = _colorTablePaths[_pColorQuantity];
|
||||
|
||||
}
|
||||
|
||||
void RenderableFieldlinesSequence::definePropertyCallbackFunctions() {
|
||||
// Add Property Callback Functions
|
||||
_pColorQuantity.onChange([this] {
|
||||
LDEBUG("CHANGED COLORING QUANTITY");
|
||||
_shouldUpdateColorBuffer = true;
|
||||
_pColorQuantityMin = std::to_string(_colorTableRanges[_pColorQuantity].x);
|
||||
_pColorQuantityMax = std::to_string(_colorTableRanges[_pColorQuantity].y);
|
||||
_pColorTablePath = _colorTablePaths[_pColorQuantity];
|
||||
});
|
||||
|
||||
_pColorTablePath.onChange([this] {
|
||||
_transferFunction->setPath(_pColorTablePath);
|
||||
_colorTablePaths[_pColorQuantity] = _pColorTablePath;
|
||||
});
|
||||
|
||||
_pColorQuantityMin.onChange([this] {
|
||||
LDEBUG("CHANGED MIN VALUE");
|
||||
float f = stringToFloat(_pColorQuantityMin, _colorTableRanges[_pColorQuantity].x);
|
||||
_pColorQuantityMin = std::to_string(f);
|
||||
_colorTableRanges[_pColorQuantity].x = f;
|
||||
});
|
||||
|
||||
_pColorQuantityMax.onChange([this] {
|
||||
LDEBUG("CHANGED MAX VALUE");
|
||||
float f = stringToFloat(_pColorQuantityMax, _colorTableRanges[_pColorQuantity].y);
|
||||
_pColorQuantityMax = std::to_string(f);
|
||||
_colorTableRanges[_pColorQuantity].y = f;
|
||||
});
|
||||
|
||||
_pFocusOnOriginBtn.onChange([this] {
|
||||
LDEBUG("SET FOCUS NODE TO PARENT");
|
||||
SceneGraphNode* node = OsEng.renderEngine().scene()->sceneGraphNode(_name);
|
||||
if (!node) {
|
||||
LWARNING("Could not find a node in scenegraph called '" << _name << "'");
|
||||
return;
|
||||
}
|
||||
OsEng.navigationHandler().setFocusNode(node->parent());
|
||||
OsEng.navigationHandler().resetCameraDirection();
|
||||
});
|
||||
|
||||
_pJumpToStartBtn.onChange([this] {
|
||||
LDEBUG("Jump in time to start of sequence!");
|
||||
OsEng.timeManager().time().setTime(_startTimes[0]);
|
||||
});
|
||||
}
|
||||
|
||||
// Calculate expected end time.
|
||||
void RenderableFieldlinesSequence::computeSequenceEndTime() {
|
||||
if (_nStates > 1) {
|
||||
const double LAST_TRIGGER_TIME = _startTimes[_nStates - 1];
|
||||
const double SEQUENCE_DURATION = LAST_TRIGGER_TIME - _startTimes[0];
|
||||
const double AVERAGE_STATE_DURATION = SEQUENCE_DURATION /
|
||||
(static_cast<double>(_nStates) - 1.0);
|
||||
_sequenceEndTime = LAST_TRIGGER_TIME + AVERAGE_STATE_DURATION;
|
||||
} else {
|
||||
// If there's just one state it should never disappear!
|
||||
_sequenceEndTime = DBL_MAX;
|
||||
}
|
||||
}
|
||||
|
||||
// Extract J2000 time from file names
|
||||
// Requires files to be named as such: 'YYYY-MM-DDTHH-MM-SS-XXX.osfls'
|
||||
void RenderableFieldlinesSequence::extractTriggerTimesFromFileNames() {
|
||||
const size_t FILENAME_SIZE = 23; // number of characters in filename (excluding '.osfls')
|
||||
const size_t EXT_SIZE = 6; // size(".osfls")
|
||||
|
||||
for (const std::string& FILEPATH : _sourceFiles) {
|
||||
const size_t STR_LENGTH = FILEPATH.size();
|
||||
// Extract the filename from the path (without extension)
|
||||
std::string timeString = FILEPATH.substr(STR_LENGTH - FILENAME_SIZE - EXT_SIZE,
|
||||
FILENAME_SIZE - 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 TRIGGER_TIME = Time::convertTime(timeString);
|
||||
_startTimes.push_back(TRIGGER_TIME);
|
||||
}
|
||||
}
|
||||
|
||||
} // namespace openspace
|
||||
@@ -0,0 +1,49 @@
|
||||
/*****************************************************************************************
|
||||
* *
|
||||
* OpenSpace *
|
||||
* *
|
||||
* Copyright (c) 2014-2017 *
|
||||
* *
|
||||
* 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_FIELDLINESSEQUENCE___COMMONS___H__
|
||||
#define __OPENSPACE_MODULE_FIELDLINESSEQUENCE___COMMONS___H__
|
||||
|
||||
#include <string>
|
||||
|
||||
namespace openspace {
|
||||
namespace fls { // (F)ield(L)ines(S)equence
|
||||
|
||||
enum Model : int {
|
||||
BATSRUS = 0,
|
||||
ENLIL,
|
||||
PFSS,
|
||||
INVALID
|
||||
};
|
||||
|
||||
|
||||
|
||||
const float A_U_TO_METER = 149597870700.f; // Astronomical Units
|
||||
const float R_E_TO_METER = 6371000.f; // Earth radius
|
||||
const float R_S_TO_METER = 695700000.f; // Sun radius
|
||||
|
||||
} // namespace fls
|
||||
} // namespace openspace
|
||||
|
||||
#endif // __OPENSPACE_MODULE_FIELDLINESSEQUENCE___COMMONS___H__
|
||||
@@ -28,6 +28,11 @@
|
||||
|
||||
#include <fstream>
|
||||
|
||||
namespace {
|
||||
std::string _loggerCat = "FieldlinesState";
|
||||
const int CURRENT_VERSION = 0;
|
||||
}
|
||||
|
||||
namespace openspace {
|
||||
|
||||
FieldlinesState::FieldlinesState() {}
|
||||
@@ -56,37 +61,37 @@ bool FieldlinesState::loadStateFromOsfls(const std::string& PATH_TO_OSFLS_FILE)
|
||||
}
|
||||
|
||||
// Define tmp variables to store meta data in
|
||||
size_t numLines;
|
||||
size_t numPoints;
|
||||
size_t numExtras;
|
||||
size_t nLines;
|
||||
size_t nPoints;
|
||||
size_t nExtras;
|
||||
size_t byteSizeAllNames;
|
||||
|
||||
// Read single value variables
|
||||
ifs.read( reinterpret_cast<char*>(&_triggerTime), sizeof(double));
|
||||
ifs.read( reinterpret_cast<char*>(&_model), sizeof(int));
|
||||
ifs.read( reinterpret_cast<char*>(&_isMorphable), sizeof(bool));
|
||||
ifs.read( reinterpret_cast<char*>(&numLines), sizeof(size_t));
|
||||
ifs.read( reinterpret_cast<char*>(&numPoints), sizeof(size_t));
|
||||
ifs.read( reinterpret_cast<char*>(&numExtras), sizeof(size_t));
|
||||
ifs.read( reinterpret_cast<char*>(&nLines), sizeof(size_t));
|
||||
ifs.read( reinterpret_cast<char*>(&nPoints), sizeof(size_t));
|
||||
ifs.read( reinterpret_cast<char*>(&nExtras), sizeof(size_t));
|
||||
ifs.read( reinterpret_cast<char*>(&byteSizeAllNames), sizeof(size_t));
|
||||
|
||||
// RESERVE/RESIZE vectors
|
||||
// TODO: Do this without initializing values? Resize is slower than just using reserve, due to initialization of all values
|
||||
_lineStart.resize(numLines);
|
||||
_lineCount.resize(numLines);
|
||||
_vertexPositions.resize(numPoints);
|
||||
_extraQuantities.resize(numExtras);
|
||||
_extraQuantityNames.reserve(numExtras);
|
||||
_lineStart.resize(nLines);
|
||||
_lineCount.resize(nLines);
|
||||
_vertexPositions.resize(nPoints);
|
||||
_extraQuantities.resize(nExtras);
|
||||
_extraQuantityNames.reserve(nExtras);
|
||||
|
||||
// Read vertex position data
|
||||
ifs.read( reinterpret_cast<char*>(_lineStart.data()), sizeof(GLint)*numLines);
|
||||
ifs.read( reinterpret_cast<char*>(_lineCount.data()), sizeof(GLsizei)*numLines);
|
||||
ifs.read( reinterpret_cast<char*>(_vertexPositions.data()), sizeof(glm::vec3)*numPoints);
|
||||
ifs.read( reinterpret_cast<char*>(_lineStart.data()), sizeof(GLint)*nLines);
|
||||
ifs.read( reinterpret_cast<char*>(_lineCount.data()), sizeof(GLsizei)*nLines);
|
||||
ifs.read( reinterpret_cast<char*>(_vertexPositions.data()), sizeof(glm::vec3)*nPoints);
|
||||
|
||||
// Read all extra quantities
|
||||
for (std::vector<float>& vec : _extraQuantities) {
|
||||
vec.resize(numPoints);
|
||||
ifs.read( reinterpret_cast<char*>(vec.data()), sizeof(float) * numPoints);
|
||||
vec.resize(nPoints);
|
||||
ifs.read( reinterpret_cast<char*>(vec.data()), sizeof(float) * nPoints);
|
||||
}
|
||||
|
||||
// Read all extra quantities' names. Stored as multiple c-strings
|
||||
@@ -97,7 +102,7 @@ bool FieldlinesState::loadStateFromOsfls(const std::string& PATH_TO_OSFLS_FILE)
|
||||
delete[] s;
|
||||
|
||||
size_t offset = 0;
|
||||
for (size_t i = 0; i < numExtras; ++i) {
|
||||
for (size_t i = 0; i < nExtras; ++i) {
|
||||
auto endOfVarName = allNamesInOne.find('\0', offset);
|
||||
endOfVarName -= offset;
|
||||
std::string varName = allNamesInOne.substr(offset, endOfVarName);
|
||||
|
||||
@@ -25,6 +25,8 @@
|
||||
#ifndef __OPENSPACE_MODULE_FIELDLINESSEQUENCE___FIELDLINESSTATE___H__
|
||||
#define __OPENSPACE_MODULE_FIELDLINESSEQUENCE___FIELDLINESSTATE___H__
|
||||
|
||||
#include <modules/fieldlinessequence/util/commons.h>
|
||||
|
||||
#include <ghoul/opengl/ghoul_gl.h>
|
||||
#include <ghoul/glm.h>
|
||||
|
||||
@@ -37,12 +39,6 @@ namespace openspace {
|
||||
|
||||
class FieldlinesState {
|
||||
public:
|
||||
enum Model : int {
|
||||
batsrus = 0,
|
||||
enlil = 1,
|
||||
pfss = 2
|
||||
};
|
||||
|
||||
FieldlinesState();
|
||||
FieldlinesState(const std::string& PATH_TO_OSFLS_FILE, bool& loadSucessful);
|
||||
|
||||
@@ -54,7 +50,7 @@ public:
|
||||
const vector<GLsizei>& lineCount() const { return _lineCount; }
|
||||
const vector<GLint>& lineStart() const { return _lineStart; }
|
||||
size_t nExtraQuantities() const { return _extraQuantities.size(); }
|
||||
Model model() const { return _model; }
|
||||
fls::Model model() const { return _model; }
|
||||
double triggerTime() const { return _triggerTime; }
|
||||
const vector<glm::vec3>& vertexPositions() const { return _vertexPositions; }
|
||||
|
||||
@@ -62,15 +58,15 @@ public:
|
||||
const vector<float>& extraQuantity(const size_t INDEX, bool& isSuccesful) const;
|
||||
|
||||
private:
|
||||
bool _isMorphable = false;
|
||||
double _triggerTime = -1.0;
|
||||
Model _model;
|
||||
bool _isMorphable = false;
|
||||
double _triggerTime = -1.0;
|
||||
fls::Model _model;
|
||||
|
||||
vector<glm::vec3> _vertexPositions;
|
||||
vector<GLint> _lineStart;
|
||||
vector<GLsizei> _lineCount;
|
||||
vector<vector<float>> _extraQuantities;
|
||||
vector<std::string> _extraQuantityNames;
|
||||
vector<glm::vec3> _vertexPositions;
|
||||
vector<GLint> _lineStart;
|
||||
vector<GLsizei> _lineCount;
|
||||
vector<vector<float>> _extraQuantities;
|
||||
vector<std::string> _extraQuantityNames;
|
||||
// TODO: Maybe introduce a vector containing seed point indices
|
||||
};
|
||||
|
||||
|
||||
Reference in New Issue
Block a user