mirror of
https://github.com/OpenSpace/OpenSpace.git
synced 2026-05-09 04:49:43 -05:00
Merge pull request #403 from OpenSpace/feature/fieldlineSequence
Feature/fieldlinesSequence
This commit is contained in:
@@ -0,0 +1,9 @@
|
||||
width 5
|
||||
lower 0.0
|
||||
upper 1.0
|
||||
mappingkey 0.0 0 0 0 255
|
||||
mappingkey 0.25 255 0 0 255
|
||||
mappingkey 0.5 255 140 0 255
|
||||
mappingkey 0.75 255 255 0 255
|
||||
mappingkey 1.0 255 255 255 255
|
||||
|
||||
@@ -0,0 +1,54 @@
|
||||
##########################################################################################
|
||||
# #
|
||||
# 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(${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
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/util/kameleonfieldlinehelper.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
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/util/commons.cpp
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/util/kameleonfieldlinehelper.cpp
|
||||
)
|
||||
source_group("Source Files" FILES ${SOURCE_FILES})
|
||||
|
||||
set(SHADER_FILES
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/shaders/fieldlinessequence_vs.glsl
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/shaders/fieldlinessequence_fs.glsl
|
||||
)
|
||||
source_group("Shader Files" FILES ${SHADER_FILES})
|
||||
|
||||
create_new_module(
|
||||
"FieldlinesSequence"
|
||||
fieldlinessequence_module
|
||||
${HEADER_FILES} ${SOURCE_FILES} ${SHADER_FILES}
|
||||
)
|
||||
@@ -0,0 +1,47 @@
|
||||
/*****************************************************************************************
|
||||
* *
|
||||
* 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/fieldlinessequencemodule.h>
|
||||
|
||||
#include <openspace/engine/openspaceengine.h>
|
||||
#include <openspace/rendering/renderable.h>
|
||||
#include <openspace/util/factorymanager.h>
|
||||
|
||||
#include <ghoul/misc/assert.h>
|
||||
|
||||
#include <modules/fieldlinessequence/rendering/renderablefieldlinessequence.h>
|
||||
|
||||
namespace openspace {
|
||||
|
||||
FieldlinesSequenceModule::FieldlinesSequenceModule()
|
||||
: OpenSpaceModule("FieldlinesSequence") {}
|
||||
|
||||
void FieldlinesSequenceModule::internalInitialize() {
|
||||
auto fRenderable = FactoryManager::ref().factory<Renderable>();
|
||||
ghoul_assert(fRenderable, "No renderable factory existed");
|
||||
|
||||
fRenderable->registerClass<RenderableFieldlinesSequence>("RenderableFieldlinesSequence");
|
||||
}
|
||||
|
||||
} // namespace openspace
|
||||
@@ -0,0 +1,42 @@
|
||||
/*****************************************************************************************
|
||||
* *
|
||||
* 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___FIELDLINESSEQUENCEMODULE___H__
|
||||
#define __OPENSPACE_MODULE_FIELDLINESSEQUENCE___FIELDLINESSEQUENCEMODULE___H__
|
||||
|
||||
#include <openspace/util/openspacemodule.h>
|
||||
|
||||
namespace openspace {
|
||||
|
||||
class FieldlinesSequenceModule : public OpenSpaceModule {
|
||||
public:
|
||||
FieldlinesSequenceModule();
|
||||
|
||||
protected:
|
||||
void internalInitialize() override;
|
||||
};
|
||||
|
||||
} // namespace openspace
|
||||
|
||||
#endif // __OPENSPACE_MODULE_FIELDLINESSEQUENCE___FIELDLINESSEQUENCEMODULE___H__
|
||||
@@ -0,0 +1,3 @@
|
||||
set (OPENSPACE_DEPENDENCIES
|
||||
space
|
||||
)
|
||||
@@ -0,0 +1,319 @@
|
||||
/*****************************************************************************************
|
||||
* *
|
||||
* 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/rendering/renderengine.h>
|
||||
#include <openspace/util/updatestructures.h>
|
||||
|
||||
#include <ghoul/opengl/programobject.h>
|
||||
#include <ghoul/opengl/textureunit.h>
|
||||
|
||||
namespace {
|
||||
std::string _loggerCat = "RenderableFieldlinesSequence";
|
||||
|
||||
const GLuint VaPosition = 0; // MUST CORRESPOND TO THE SHADER PROGRAM
|
||||
const GLuint VaColor = 1; // MUST CORRESPOND TO THE SHADER PROGRAM
|
||||
const GLuint VaMasking = 2; // MUST CORRESPOND TO THE SHADER PROGRAM
|
||||
} // namespace
|
||||
|
||||
namespace openspace {
|
||||
|
||||
void RenderableFieldlinesSequence::deinitialize() {
|
||||
glDeleteVertexArrays(1, &_vertexArrayObject);
|
||||
_vertexArrayObject = 0;
|
||||
|
||||
glDeleteBuffers(1, &_vertexPositionBuffer);
|
||||
_vertexPositionBuffer = 0;
|
||||
|
||||
glDeleteBuffers(1, &_vertexColorBuffer);
|
||||
_vertexColorBuffer = 0;
|
||||
|
||||
glDeleteBuffers(1, &_vertexMaskingBuffer);
|
||||
_vertexMaskingBuffer = 0;
|
||||
|
||||
RenderEngine& renderEngine = OsEng.renderEngine();
|
||||
if (_shaderProgram) {
|
||||
renderEngine.removeRenderProgram(_shaderProgram);
|
||||
_shaderProgram = nullptr;
|
||||
}
|
||||
|
||||
// Stall main thread until thread that's loading states is done!
|
||||
while (_isLoadingStateFromDisk) {
|
||||
LWARNING("TRYING TO DESTROY CLASS WHEN A THREAD USING IT IS STILL ACTIVE");
|
||||
}
|
||||
}
|
||||
|
||||
bool RenderableFieldlinesSequence::isReady() const {
|
||||
return _isReady;
|
||||
}
|
||||
|
||||
void RenderableFieldlinesSequence::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("colorMethod", _pColorMethod);
|
||||
_shaderProgram->setUniform("lineColor", _pColorUniform);
|
||||
_shaderProgram->setUniform("usingDomain", _pDomainEnabled);
|
||||
_shaderProgram->setUniform("usingMasking", _pMaskingEnabled);
|
||||
|
||||
if (_pColorMethod == ColorMethod::ByQuantity) {
|
||||
ghoul::opengl::TextureUnit textureUnit;
|
||||
textureUnit.activate();
|
||||
_transferFunction->bind(); // Calls update internally
|
||||
_shaderProgram->setUniform("colorTable", textureUnit);
|
||||
_shaderProgram->setUniform("colorTableRange",
|
||||
_colorTableRanges[_pColorQuantity]);
|
||||
}
|
||||
|
||||
if (_pMaskingEnabled) {
|
||||
_shaderProgram->setUniform("maskingRange", _maskingRanges[_pMaskingQuantity]);
|
||||
}
|
||||
|
||||
_shaderProgram->setUniform("domainLimR", _pDomainR.value() * _scalingFactor);
|
||||
_shaderProgram->setUniform("domainLimX", _pDomainX.value() * _scalingFactor);
|
||||
_shaderProgram->setUniform("domainLimY", _pDomainY.value() * _scalingFactor);
|
||||
_shaderProgram->setUniform("domainLimZ", _pDomainZ.value() * _scalingFactor);
|
||||
|
||||
// Flow/Particles
|
||||
_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));
|
||||
|
||||
bool additiveBlending = false;
|
||||
if (_pColorABlendEnabled) {
|
||||
const auto renderer = OsEng.renderEngine().rendererImplementation();
|
||||
bool usingFBufferRenderer = renderer ==
|
||||
RenderEngine::RendererImplementation::Framebuffer;
|
||||
|
||||
bool usingABufferRenderer = renderer ==
|
||||
RenderEngine::RendererImplementation::ABuffer;
|
||||
|
||||
if (usingABufferRenderer) {
|
||||
_shaderProgram->setUniform("usingAdditiveBlending", _pColorABlendEnabled);
|
||||
}
|
||||
|
||||
additiveBlending = usingFBufferRenderer;
|
||||
if (additiveBlending) {
|
||||
glDepthMask(false);
|
||||
glBlendFunc(GL_SRC_ALPHA, GL_ONE);
|
||||
}
|
||||
}
|
||||
|
||||
glBindVertexArray(_vertexArrayObject);
|
||||
glMultiDrawArrays(
|
||||
GL_LINE_STRIP, //_drawingOutputType,
|
||||
_states[_activeStateIndex].lineStart().data(),
|
||||
_states[_activeStateIndex].lineCount().data(),
|
||||
static_cast<GLsizei>(_states[_activeStateIndex].lineStart().size())
|
||||
);
|
||||
|
||||
glBindVertexArray(0);
|
||||
_shaderProgram->deactivate();
|
||||
|
||||
if (additiveBlending) {
|
||||
glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
|
||||
glDepthMask(true);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void RenderableFieldlinesSequence::update(const UpdateData& data) {
|
||||
// This node shouldn't do anything if its been disabled from the gui!
|
||||
if (!_enabled) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (_shaderProgram->isDirty()) {
|
||||
_shaderProgram->rebuildFromFile();
|
||||
}
|
||||
|
||||
const double currentTime = data.time.j2000Seconds();
|
||||
// Check if current time in OpenSpace is within sequence interval
|
||||
if (isWithinSequenceInterval(currentTime)) {
|
||||
const int nextIdx = _activeTriggerTimeIndex + 1;
|
||||
if (_activeTriggerTimeIndex < 0 // true => Previous frame was not within the sequence interval
|
||||
|| currentTime < _startTimes[_activeTriggerTimeIndex] // true => OpenSpace has stepped back to a time represented by another state
|
||||
|| (nextIdx < _nStates && currentTime >= _startTimes[nextIdx])) { // true => OpenSpace has stepped forward to a time represented by another state
|
||||
|
||||
updateActiveTriggerTimeIndex(currentTime);
|
||||
|
||||
if (_loadingStatesDynamically) {
|
||||
_mustLoadNewStateFromDisk = true;
|
||||
} else {
|
||||
_needsUpdate = true;
|
||||
_activeStateIndex = _activeTriggerTimeIndex;
|
||||
}
|
||||
} // else {we're still in same state as previous frame (no changes needed)}
|
||||
} else {
|
||||
// Not in interval => set everything to false
|
||||
_activeTriggerTimeIndex = -1;
|
||||
_mustLoadNewStateFromDisk = false;
|
||||
_needsUpdate = false;
|
||||
}
|
||||
|
||||
if (_mustLoadNewStateFromDisk) {
|
||||
if (!_isLoadingStateFromDisk && !_newStateIsReady) {
|
||||
_isLoadingStateFromDisk = true;
|
||||
_mustLoadNewStateFromDisk = false;
|
||||
const std::string filePath = _sourceFiles[_activeTriggerTimeIndex];
|
||||
std::thread readBinaryThread([this, filePath] {
|
||||
this->readNewState(filePath);
|
||||
});
|
||||
readBinaryThread.detach();
|
||||
}
|
||||
}
|
||||
|
||||
if (_needsUpdate || _newStateIsReady) {
|
||||
if (_loadingStatesDynamically) {
|
||||
_states[0] = std::move(*_newState);
|
||||
}
|
||||
|
||||
updateVertexPositionBuffer();
|
||||
|
||||
if (_states[_activeStateIndex].nExtraQuantities() > 0) {
|
||||
_shouldUpdateColorBuffer = true;
|
||||
_shouldUpdateMaskingBuffer = true;
|
||||
}
|
||||
|
||||
// Everything is set and ready for rendering!
|
||||
_needsUpdate = false;
|
||||
_newStateIsReady = false;
|
||||
}
|
||||
|
||||
if (_shouldUpdateColorBuffer) {
|
||||
updateVertexColorBuffer();
|
||||
_shouldUpdateColorBuffer = false;
|
||||
}
|
||||
|
||||
if (_shouldUpdateMaskingBuffer) {
|
||||
updateVertexMaskingBuffer();
|
||||
_shouldUpdateMaskingBuffer = false;
|
||||
}
|
||||
}
|
||||
|
||||
inline bool RenderableFieldlinesSequence::isWithinSequenceInterval(const double currentTime) const {
|
||||
return (currentTime >= _startTimes[0]) && (currentTime < _sequenceEndTime);
|
||||
}
|
||||
|
||||
// Assumes we already know that currentTime is within the sequence interval
|
||||
void RenderableFieldlinesSequence::updateActiveTriggerTimeIndex(const double currentTime) {
|
||||
auto iter = std::upper_bound(_startTimes.begin(), _startTimes.end(), currentTime);
|
||||
if (iter != _startTimes.end()) {
|
||||
if ( iter != _startTimes.begin()) {
|
||||
_activeTriggerTimeIndex =
|
||||
static_cast<int>(std::distance(_startTimes.begin(), iter)) - 1;
|
||||
} else {
|
||||
_activeTriggerTimeIndex = 0;
|
||||
}
|
||||
} else {
|
||||
_activeTriggerTimeIndex = static_cast<int>(_nStates) - 1;
|
||||
}
|
||||
}
|
||||
|
||||
// Reading state from disk. Must be thread safe!
|
||||
void RenderableFieldlinesSequence::readNewState(const std::string& filePath) {
|
||||
_newState = std::make_unique<FieldlinesState>();
|
||||
if (_newState->loadStateFromOsfls(filePath)) {
|
||||
_newStateIsReady = true;
|
||||
}
|
||||
_isLoadingStateFromDisk = false;
|
||||
}
|
||||
|
||||
// Unbind buffers and arrays
|
||||
inline void unbindGL() {
|
||||
glBindBuffer(GL_ARRAY_BUFFER, 0);
|
||||
glBindVertexArray(0);
|
||||
}
|
||||
|
||||
void RenderableFieldlinesSequence::updateVertexPositionBuffer() {
|
||||
glBindVertexArray(_vertexArrayObject);
|
||||
glBindBuffer(GL_ARRAY_BUFFER, _vertexPositionBuffer);
|
||||
|
||||
const std::vector<glm::vec3>& vertexPosVec =
|
||||
_states[_activeStateIndex].vertexPositions();
|
||||
|
||||
glBufferData(GL_ARRAY_BUFFER, vertexPosVec.size() * sizeof(glm::vec3),
|
||||
&vertexPosVec.front(), GL_STATIC_DRAW);
|
||||
|
||||
glEnableVertexAttribArray(VaPosition);
|
||||
glVertexAttribPointer(VaPosition, 3, GL_FLOAT, GL_FALSE, 0, 0);
|
||||
|
||||
unbindGL();
|
||||
}
|
||||
|
||||
void RenderableFieldlinesSequence::updateVertexColorBuffer() {
|
||||
glBindVertexArray(_vertexArrayObject);
|
||||
glBindBuffer(GL_ARRAY_BUFFER, _vertexColorBuffer);
|
||||
|
||||
bool isSuccessful;
|
||||
const std::vector<float>& quantityVec =
|
||||
_states[_activeStateIndex].extraQuantity(_pColorQuantity, isSuccessful);
|
||||
|
||||
if (isSuccessful) {
|
||||
glBufferData(GL_ARRAY_BUFFER, quantityVec.size() * sizeof(float),
|
||||
&quantityVec.front(), GL_STATIC_DRAW);
|
||||
|
||||
glEnableVertexAttribArray(VaColor);
|
||||
glVertexAttribPointer(VaColor, 1, GL_FLOAT, GL_FALSE, 0, 0);
|
||||
|
||||
unbindGL();
|
||||
}
|
||||
}
|
||||
|
||||
void RenderableFieldlinesSequence::updateVertexMaskingBuffer() {
|
||||
glBindVertexArray(_vertexArrayObject);
|
||||
glBindBuffer(GL_ARRAY_BUFFER, _vertexMaskingBuffer);
|
||||
|
||||
bool isSuccessful;
|
||||
const std::vector<float>& quantityVec =
|
||||
_states[_activeStateIndex].extraQuantity(_pMaskingQuantity, isSuccessful);
|
||||
|
||||
if (isSuccessful) {
|
||||
glBufferData(GL_ARRAY_BUFFER, quantityVec.size() * sizeof(float),
|
||||
&quantityVec.front(), GL_STATIC_DRAW);
|
||||
|
||||
glEnableVertexAttribArray(VaMasking);
|
||||
glVertexAttribPointer(VaMasking, 1, GL_FLOAT, GL_FALSE, 0, 0);
|
||||
|
||||
unbindGL();
|
||||
}
|
||||
}
|
||||
|
||||
} // namespace openspace
|
||||
@@ -0,0 +1,170 @@
|
||||
/*****************************************************************************************
|
||||
* *
|
||||
* 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___RENDERABLEFIELDLINESSEQUENCE___H__
|
||||
#define __OPENSPACE_MODULE_FIELDLINESSEQUENCE___RENDERABLEFIELDLINESSEQUENCE___H__
|
||||
|
||||
#include <openspace/rendering/renderable.h>
|
||||
|
||||
#include <openspace/properties/optionproperty.h>
|
||||
#include <openspace/properties/scalar/intproperty.h>
|
||||
#include <openspace/properties/stringproperty.h>
|
||||
#include <openspace/properties/triggerproperty.h>
|
||||
#include <openspace/properties/vector/vec2property.h>
|
||||
#include <openspace/properties/vector/vec4property.h>
|
||||
#include <openspace/rendering/transferfunction.h>
|
||||
|
||||
#include <modules/fieldlinessequence/util/fieldlinesstate.h>
|
||||
|
||||
#include <atomic>
|
||||
|
||||
namespace {
|
||||
enum class SourceFileType;
|
||||
}
|
||||
|
||||
namespace openspace {
|
||||
|
||||
class RenderableFieldlinesSequence : public Renderable {
|
||||
public:
|
||||
RenderableFieldlinesSequence(const ghoul::Dictionary& dictionary);
|
||||
|
||||
void initialize() override;
|
||||
void deinitialize() override;
|
||||
|
||||
bool isReady() const override;
|
||||
|
||||
void render(const RenderData& data, RendererTasks& rendererTask) override;
|
||||
void update(const UpdateData& data) override;
|
||||
private:
|
||||
// ------------------------------------- ENUMS -------------------------------------//
|
||||
enum ColorMethod : int { // Used to determine if lines should be colored UNIFORMLY or by an extraQuantity
|
||||
Uniform = 0,
|
||||
ByQuantity
|
||||
};
|
||||
|
||||
// ------------------------------------ STRINGS ------------------------------------//
|
||||
std::string _name; // Name of the Node!
|
||||
|
||||
// ------------------------------------- FLAGS -------------------------------------//
|
||||
std::atomic<bool> _isLoadingStateFromDisk { false}; // Used for 'runtime-states'. True when loading a new state from disk on another thread.
|
||||
bool _isReady = false; // If initialization proved successful
|
||||
bool _loadingStatesDynamically = false; // False => states are stored in RAM (using 'in-RAM-states'), True => states are loaded from disk during runtime (using 'runtime-states')
|
||||
bool _mustLoadNewStateFromDisk = false; // Used for 'runtime-states': True if new 'runtime-state' must be loaded from disk. False => the previous frame's state should still be shown
|
||||
bool _needsUpdate = false; // Used for 'in-RAM-states' : True if new 'in-RAM-state' must be loaded. False => the previous frame's state should still be shown
|
||||
std::atomic<bool> _newStateIsReady { false}; // Used for 'runtime-states'. True when finished loading a new state from disk on another thread.
|
||||
bool _shouldUpdateColorBuffer = false; // True when new state is loaded or user change which quantity to color the lines by
|
||||
bool _shouldUpdateMaskingBuffer = false; // True when new state is loaded or user change which quantity used for masking out line segments
|
||||
|
||||
// --------------------------------- NUMERICALS ----------------------------------- //
|
||||
int _activeStateIndex = -1; // Active index of _states. If(==-1)=>no state available for current time. Always the same as _activeTriggerTimeIndex if(_loadingStatesDynamically==true), else always = 0
|
||||
int _activeTriggerTimeIndex = -1; // Active index of _startTimes
|
||||
size_t _nStates = 0; // Number of states in the sequence
|
||||
float _scalingFactor = 1.f; // In setup it is used to scale JSON coordinates. During runtime it is used to scale domain limits.
|
||||
double _sequenceEndTime; // Estimated end of sequence.
|
||||
GLuint _vertexArrayObject = 0; // OpenGL Vertex Array Object
|
||||
GLuint _vertexColorBuffer = 0; // OpenGL Vertex Buffer Object containing the extraQuantity values used for coloring the lines
|
||||
GLuint _vertexMaskingBuffer = 0; // OpenGL Vertex Buffer Object containing the extraQuantity values used for masking out segments of the lines
|
||||
GLuint _vertexPositionBuffer = 0; // OpenGL Vertex Buffer Object containing the vertex positions
|
||||
|
||||
// ----------------------------------- POINTERS ------------------------------------//
|
||||
std::unique_ptr<ghoul::Dictionary> _dictionary; // The Lua-Modfile-Dictionary used during initialization
|
||||
std::unique_ptr<FieldlinesState> _newState; // Used for 'runtime-states' when switching out current state to a new state
|
||||
std::unique_ptr<ghoul::opengl::ProgramObject> _shaderProgram;
|
||||
std::shared_ptr<TransferFunction> _transferFunction; // Transfer function used to color lines when _pColorMethod is set to BY_QUANTITY
|
||||
|
||||
// ------------------------------------ VECTORS ----------------------------------- //
|
||||
std::vector<std::string> _colorTablePaths; // Paths to color tables. One for each 'extraQuantity'
|
||||
std::vector<glm::vec2> _colorTableRanges; // Values represents min & max values represented in the color table
|
||||
std::vector<glm::vec2> _maskingRanges; // Values represents min & max limits for valid masking range
|
||||
std::vector<std::string> _sourceFiles; // Stores the provided source file paths if using 'runtime-states', else emptied after initialization
|
||||
std::vector<double> _startTimes; // Contains the _triggerTimes for all FieldlineStates in the sequence
|
||||
std::vector<FieldlinesState> _states; // Stores the FieldlineStates
|
||||
|
||||
// ---------------------------------- Properties ---------------------------------- //
|
||||
properties::PropertyOwner _pColorGroup; // Group to hold the color properties
|
||||
properties::OptionProperty _pColorMethod; // Uniform/transfer function/topology?
|
||||
properties::OptionProperty _pColorQuantity; // Index of the extra quantity to color lines by
|
||||
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::BoolProperty _pColorABlendEnabled; // Whether or not to use additive blending
|
||||
|
||||
properties::BoolProperty _pDomainEnabled; // Whether or not to use Domain
|
||||
properties::PropertyOwner _pDomainGroup; // Group to hold the Domain properties
|
||||
properties::Vec2Property _pDomainX; // Domain Limits along x-axis
|
||||
properties::Vec2Property _pDomainY; // Domain Limits along y-axis
|
||||
properties::Vec2Property _pDomainZ; // Domain Limits along z-axis
|
||||
properties::Vec2Property _pDomainR; // Domain Limits radially
|
||||
|
||||
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::BoolProperty _pMaskingEnabled; // Whether or not to use masking
|
||||
properties::PropertyOwner _pMaskingGroup; // Group to hold the masking properties
|
||||
properties::StringProperty _pMaskingMin; // Lower limit for allowed values
|
||||
properties::StringProperty _pMaskingMax; // Upper limit for allowed values
|
||||
properties::OptionProperty _pMaskingQuantity; // Index of the extra quantity to use for masking
|
||||
|
||||
properties::TriggerProperty _pFocusOnOriginBtn; // Button which sets camera focus to parent node of the renderable
|
||||
properties::TriggerProperty _pJumpToStartBtn; // Button which executes a time jump to start of sequence
|
||||
|
||||
// --------------------- FUNCTIONS USED DURING INITIALIZATION --------------------- //
|
||||
void addStateToSequence(FieldlinesState& STATE);
|
||||
void computeSequenceEndTime();
|
||||
void definePropertyCallbackFunctions();
|
||||
bool extractCdfInfoFromDictionary(std::string& seedFilePath, std::string& tracingVar,
|
||||
std::vector<std::string>& extraVars);
|
||||
bool extractJsonInfoFromDictionary(fls::Model& model);
|
||||
void extractMagnitudeVarsFromStrings(std::vector<std::string>& extraVars,
|
||||
std::vector<std::string>& extraMagVars);
|
||||
bool extractMandatoryInfoFromDictionary(SourceFileType& sourceFileType);
|
||||
void extractOptionalInfoFromDictionary(std::string& outputFolderPath);
|
||||
void extractOsflsInfoFromDictionary();
|
||||
bool extractSeedPointsFromFile(const std::string& path, std::vector<glm::vec3>& outVec);
|
||||
void extractTriggerTimesFromFileNames();
|
||||
bool loadJsonStatesIntoRAM(const std::string& outputFolder);
|
||||
void loadOsflsStatesIntoRAM(const std::string& outputFolder);
|
||||
bool getStatesFromCdfFiles(const std::string& outputFolder);
|
||||
void setModelDependentConstants();
|
||||
void setupProperties();
|
||||
bool prepareForOsflsStreaming();
|
||||
|
||||
// ------------------------- FUNCTIONS USED DURING RUNTIME ------------------------ //
|
||||
inline bool isWithinSequenceInterval(const double currentTime) const;
|
||||
void readNewState(const std::string& filePath);
|
||||
void updateActiveTriggerTimeIndex(const double currentTime);
|
||||
void updateVertexPositionBuffer();
|
||||
void updateVertexColorBuffer();
|
||||
void updateVertexMaskingBuffer();
|
||||
};
|
||||
|
||||
} // namespace openspace
|
||||
|
||||
#endif // __OPENSPACE_MODULE_FIELDLINESSEQUENCE___RENDERABLEFIELDLINESSEQUENCE___H__
|
||||
@@ -0,0 +1,831 @@
|
||||
/*****************************************************************************************
|
||||
* *
|
||||
* 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 <modules/fieldlinessequence/util/kameleonfieldlinehelper.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>
|
||||
|
||||
#include <fstream>
|
||||
#include <sstream>
|
||||
|
||||
namespace {
|
||||
std::string _loggerCat = "RenderableFieldlinesSequence";
|
||||
|
||||
// ----- KEYS POSSIBLE IN MODFILE. EXPECTED DATA TYPE OF VALUE IN [BRACKETS] ----- //
|
||||
// ---------------------------- MANDATORY MODFILE KEYS ---------------------------- //
|
||||
const char* KeyInputFileType = "InputFileType"; // [STRING] "cdf", "json" or "osfls"
|
||||
const char* KeySourceFolder = "SourceFolder"; // [STRING] should be path to folder containing the input files
|
||||
|
||||
// ---------------------- MANDATORY INPUT TYPE SPECIFIC KEYS ---------------------- //
|
||||
const char* KeyCdfSeedPointFile = "SeedPointFile"; // [STRING] Path to a .txt file containing seed points
|
||||
const char* KeyJsonSimulationModel = "SimulationModel"; // [STRING] Currently supports: "batsrus", "enlil" & "pfss"
|
||||
|
||||
// ----------------------- OPTIONAL INPUT TYPE SPECIFIC KEYS ---------------------- //
|
||||
const char* KeyCdfExtraVariables = "ExtraVariables"; // [STRING ARRAY]
|
||||
const char* KeyCdfTracingVariable = "TracingVariable"; // [STRING]
|
||||
const char* KeyJsonScalingFactor = "ScaleToMeters"; // [STRING]
|
||||
const char* KeyOslfsLoadAtRuntime = "LoadAtRuntime"; // [BOOLEAN] If value False => Load in initializing step and store in RAM
|
||||
|
||||
// ---------------------------- OPTIONAL MODFILE KEYS ---------------------------- //
|
||||
const char* KeyColorTablePaths = "ColorTablePaths"; // [STRING ARRAY] Values should be paths to .txt files
|
||||
const char* KeyColorTableRanges = "ColorTableRanges";// [VEC2 ARRAY] Values should be entered as {X, Y}, where X & Y are numbers
|
||||
const char* KeyMaskingRanges = "MaskingRanges"; // [VEC2 ARRAY] Values should be entered as {X, Y}, where X & Y are numbers
|
||||
const char* KeyOutputFolder = "OutputFolder"; // [STRING] Value should be path to folder where states are saved (JSON/CDF input => osfls output & oslfs input => JSON output)
|
||||
|
||||
// ------------- POSSIBLE STRING VALUES FOR CORRESPONDING MODFILE KEY ------------- //
|
||||
const char* ValueInputFileTypeCdf = "cdf";
|
||||
const char* ValueInputFileTypeJson = "json";
|
||||
const char* ValueInputFileTypeOsfls = "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 ColorUseABlendingInfo = {
|
||||
"aBlendingEnabled", "Additive Blending", "Activate/deactivate additive blending."
|
||||
};
|
||||
static const openspace::properties::Property::PropertyInfo DomainEnabledInfo = {
|
||||
"domainEnabled", "Domain Limits", "Enable/Disable domain limits"
|
||||
};
|
||||
static const openspace::properties::Property::PropertyInfo DomainXInfo = {
|
||||
"limitsX", "X-limits", "Valid range along the X-axis. [Min, Max]"
|
||||
};
|
||||
static const openspace::properties::Property::PropertyInfo DomainYInfo = {
|
||||
"limitsY", "Y-limits", "Valid range along the Y-axis. [Min, Max]"
|
||||
};
|
||||
static const openspace::properties::Property::PropertyInfo DomainZInfo = {
|
||||
"limitsZ", "Z-limits", "Valid range along the Z-axis. [Min, Max]"
|
||||
};
|
||||
static const openspace::properties::Property::PropertyInfo DomainRInfo = {
|
||||
"limitsR", "Radial limits", "Valid radial range. [Min, Max]"
|
||||
};
|
||||
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 MaskingEnabledInfo = {
|
||||
"maskingEnabled", "Masking",
|
||||
"Enable/disable masking. Use masking to show lines where a given quantity is within a given range, e.g. if you only want to see where the temperature is between 10 and 20 degrees. Also used for masking out line topologies like solar wind & closed lines."
|
||||
};
|
||||
static const openspace::properties::Property::PropertyInfo MaskingMinInfo = {
|
||||
"maskingMinLimit", "Lower Limit", "Lower limit of the valid masking range"
|
||||
};
|
||||
static const openspace::properties::Property::PropertyInfo MaskingMaxInfo = {
|
||||
"maskingMaxLimit", "Upper Limit", "Upper limit of the valid masking range"
|
||||
};
|
||||
static const openspace::properties::Property::PropertyInfo MaskingQuantityInfo = {
|
||||
"maskingQuantity", "Quantity used for Masking", "Quantity used for masking."
|
||||
};
|
||||
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."
|
||||
};
|
||||
|
||||
enum class SourceFileType : int {
|
||||
Cdf = 0,
|
||||
Json,
|
||||
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("Invalid argument: " << ia.what() << ". '" << input <<
|
||||
"' is NOT a valid number!");
|
||||
return backupValue;
|
||||
}
|
||||
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)),
|
||||
_pColorABlendEnabled(ColorUseABlendingInfo, true),
|
||||
_pDomainEnabled(DomainEnabledInfo, true),
|
||||
_pDomainGroup({ "Domain" }),
|
||||
_pDomainX(DomainXInfo),
|
||||
_pDomainY(DomainYInfo),
|
||||
_pDomainZ(DomainZInfo),
|
||||
_pDomainR(DomainRInfo),
|
||||
_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),
|
||||
_pMaskingEnabled(MaskingEnabledInfo, false),
|
||||
_pMaskingGroup({ "Masking" }),
|
||||
_pMaskingMin(MaskingMinInfo),
|
||||
_pMaskingMax(MaskingMaxInfo),
|
||||
_pMaskingQuantity(MaskingQuantityInfo, properties::OptionProperty::DisplayType::Dropdown),
|
||||
_pFocusOnOriginBtn(OriginButtonInfo),
|
||||
_pJumpToStartBtn(TimeJumpButtonInfo) {
|
||||
|
||||
_dictionary = std::make_unique<ghoul::Dictionary>(dictionary);
|
||||
}
|
||||
|
||||
void RenderableFieldlinesSequence::initialize() {
|
||||
LINFO("RenderableFieldlinesSequence::initialize()");
|
||||
|
||||
// EXTRACT MANDATORY INFORMATION FROM DICTIONARY
|
||||
SourceFileType sourceFileType = SourceFileType::Invalid;
|
||||
if (!extractMandatoryInfoFromDictionary(sourceFileType)) {
|
||||
return;
|
||||
}
|
||||
|
||||
// Set a default color table, just in case the (optional) user defined paths are corrupt/not provided!
|
||||
_colorTablePaths.push_back("${OPENSPACE_DATA}/scene/fieldlinessequence/colortables/kroyw.txt");
|
||||
_transferFunction = std::make_shared<TransferFunction>(absPath(_colorTablePaths[0]));
|
||||
|
||||
// EXTRACT OPTIONAL INFORMATION FROM DICTIONARY
|
||||
std::string outputFolderPath;
|
||||
extractOptionalInfoFromDictionary(outputFolderPath);
|
||||
|
||||
// EXTRACT SOURCE FILE TYPE SPECIFIC INFOMRATION FROM DICTIONARY & GET STATES FROM SOURCE
|
||||
switch (sourceFileType) {
|
||||
case SourceFileType::Cdf:
|
||||
if (!getStatesFromCdfFiles(outputFolderPath)) {
|
||||
return;
|
||||
}
|
||||
break;
|
||||
case SourceFileType::Json:
|
||||
if (!loadJsonStatesIntoRAM(outputFolderPath)) {
|
||||
return;
|
||||
}
|
||||
break;
|
||||
case SourceFileType::Osfls:
|
||||
extractOsflsInfoFromDictionary();
|
||||
if (_loadingStatesDynamically) {
|
||||
if (!prepareForOsflsStreaming()) {
|
||||
return;
|
||||
}
|
||||
} else {
|
||||
loadOsflsStatesIntoRAM(outputFolderPath);
|
||||
}
|
||||
break;
|
||||
default:
|
||||
return;
|
||||
}
|
||||
|
||||
// dictionary is no longer needed as everything is extracted
|
||||
_dictionary.reset();
|
||||
|
||||
// No need to store source paths in memory if they are already in RAM!
|
||||
if (!_loadingStatesDynamically) {
|
||||
_sourceFiles.clear();
|
||||
}
|
||||
|
||||
// At this point there should be at least one state loaded into memory!
|
||||
if (_states.size() == 0) {
|
||||
LERROR("Wasn't able to extract any valid states from provided source files!");
|
||||
return;
|
||||
}
|
||||
|
||||
computeSequenceEndTime();
|
||||
setModelDependentConstants();
|
||||
|
||||
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);
|
||||
glGenBuffers(1, &_vertexMaskingBuffer);
|
||||
|
||||
// Needed for additive blending
|
||||
setRenderBin(Renderable::RenderBin::Overlay);
|
||||
|
||||
_isReady = true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Extracts the general information (from the lua modfile) that is mandatory for the class
|
||||
* to function; such as the file type and the location of the source files.
|
||||
* Returns false if it fails to extract mandatory information!
|
||||
*/
|
||||
bool RenderableFieldlinesSequence::extractMandatoryInfoFromDictionary(
|
||||
SourceFileType& sourceFileType) {
|
||||
|
||||
_dictionary->getValue(SceneGraphNode::KeyName, _name);
|
||||
|
||||
// ------------------- EXTRACT MANDATORY VALUES FROM DICTIONARY ------------------- //
|
||||
std::string inputFileTypeString;
|
||||
if (!_dictionary->getValue(KeyInputFileType, inputFileTypeString)) {
|
||||
LERROR(_name << ": The field " << std::string(KeyInputFileType) << " is missing!");
|
||||
return false;
|
||||
} else {
|
||||
std::transform(inputFileTypeString.begin(), inputFileTypeString.end(),
|
||||
inputFileTypeString.begin(), ::tolower);
|
||||
// Verify that the input type is correct
|
||||
if (inputFileTypeString == ValueInputFileTypeCdf) {
|
||||
sourceFileType = SourceFileType::Cdf;
|
||||
} else if (inputFileTypeString == ValueInputFileTypeJson) {
|
||||
sourceFileType = SourceFileType::Json;
|
||||
} else if (inputFileTypeString == ValueInputFileTypeOsfls) {
|
||||
sourceFileType = SourceFileType::Osfls;
|
||||
} else {
|
||||
LERROR(_name << ": " << inputFileTypeString << " is not a recognised "
|
||||
<< KeyInputFileType);
|
||||
sourceFileType = SourceFileType::Invalid;
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
std::string sourceFolderPath;
|
||||
if (!_dictionary->getValue(KeySourceFolder, sourceFolderPath)) {
|
||||
LERROR(_name << ": The field " << std::string(KeySourceFolder) << " 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 extLength = inputFileTypeString.length();
|
||||
std::string sub = str.substr(str.length() - extLength, extLength);
|
||||
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;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
void RenderableFieldlinesSequence::extractOptionalInfoFromDictionary(
|
||||
std::string& outputFolderPath) {
|
||||
|
||||
// ------------------- EXTRACT OPTIONAL VALUES FROM DICTIONARY ------------------- //
|
||||
if (_dictionary->getValue(KeyOutputFolder, outputFolderPath)) {
|
||||
ghoul::filesystem::Directory outputFolder(outputFolderPath);
|
||||
if (FileSys.directoryExists(outputFolder)) {
|
||||
outputFolderPath = absPath(outputFolderPath);
|
||||
} else {
|
||||
LERROR(_name << ": The specified output path: '" << outputFolderPath << "', does not exist!");
|
||||
outputFolderPath = "";
|
||||
}
|
||||
}
|
||||
|
||||
ghoul::Dictionary colorTablesPathsDictionary;
|
||||
if (_dictionary->getValue(KeyColorTablePaths, colorTablesPathsDictionary)) {
|
||||
const size_t nProvidedPaths = colorTablesPathsDictionary.size();
|
||||
if (nProvidedPaths > 0) {
|
||||
// Clear the default! It is already specified in the transferFunction
|
||||
_colorTablePaths.clear();
|
||||
for (size_t i = 1; i <= nProvidedPaths; ++i) {
|
||||
_colorTablePaths.push_back(
|
||||
colorTablesPathsDictionary.value<std::string>(std::to_string(i)));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
ghoul::Dictionary colorTablesRangesDictionary;
|
||||
if (_dictionary->getValue(KeyColorTableRanges, colorTablesRangesDictionary)) {
|
||||
const size_t nProvidedRanges = colorTablesRangesDictionary.size();
|
||||
for (size_t i = 1; i <= nProvidedRanges; ++i) {
|
||||
_colorTableRanges.push_back(
|
||||
colorTablesRangesDictionary.value<glm::vec2>(std::to_string(i)));
|
||||
}
|
||||
} else {
|
||||
_colorTableRanges.push_back(glm::vec2(0, 1));
|
||||
}
|
||||
|
||||
ghoul::Dictionary maskingRangesDictionary;
|
||||
if (_dictionary->getValue(KeyMaskingRanges, maskingRangesDictionary)) {
|
||||
const size_t nProvidedRanges = maskingRangesDictionary.size();
|
||||
for (size_t i = 1; i <= nProvidedRanges; ++i) {
|
||||
_maskingRanges.push_back(
|
||||
maskingRangesDictionary.value<glm::vec2>(std::to_string(i)));
|
||||
}
|
||||
} else {
|
||||
_maskingRanges.push_back(glm::vec2(-100000, 100000)); // Just some default values!
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns false if it fails to extract mandatory information!
|
||||
*/
|
||||
bool RenderableFieldlinesSequence::extractJsonInfoFromDictionary(fls::Model& model) {
|
||||
std::string modelStr;
|
||||
if (_dictionary->getValue(KeyJsonSimulationModel, modelStr)) {
|
||||
std::transform(modelStr.begin(), modelStr.end(), modelStr.begin(), ::tolower);
|
||||
model = fls::stringToModel(modelStr);
|
||||
} else {
|
||||
LERROR(_name << ": Must specify '" << KeyJsonSimulationModel << "'");
|
||||
return false;
|
||||
}
|
||||
|
||||
float scaleFactor;
|
||||
if (_dictionary->getValue(KeyJsonScalingFactor, scaleFactor)) {
|
||||
_scalingFactor = scaleFactor;
|
||||
} else {
|
||||
LWARNING(_name << ": Does not provide scalingFactor! " <<
|
||||
"Assumes coordinates are already expressed in meters!");
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
bool RenderableFieldlinesSequence::loadJsonStatesIntoRAM(const std::string& outputFolder) {
|
||||
fls::Model model;
|
||||
if (!extractJsonInfoFromDictionary(model)) {
|
||||
return false;
|
||||
}
|
||||
// Load states into RAM!
|
||||
for (std::string filePath : _sourceFiles) {
|
||||
FieldlinesState newState;
|
||||
bool loadedSuccessfully = newState.loadStateFromJson(filePath, model,
|
||||
_scalingFactor);
|
||||
if (loadedSuccessfully) {
|
||||
addStateToSequence(newState);
|
||||
if (!outputFolder.empty()) {
|
||||
newState.saveStateToOsfls(outputFolder);
|
||||
}
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
bool RenderableFieldlinesSequence::prepareForOsflsStreaming() {
|
||||
extractTriggerTimesFromFileNames();
|
||||
FieldlinesState newState;
|
||||
if (!newState.loadStateFromOsfls(_sourceFiles[0])) {
|
||||
LERROR("The provided .osfls files seem to be corrupt!");
|
||||
return false;
|
||||
}
|
||||
_states.push_back(newState);
|
||||
_nStates = _startTimes.size();
|
||||
_activeStateIndex = 0;
|
||||
return true;
|
||||
|
||||
}
|
||||
|
||||
void RenderableFieldlinesSequence::loadOsflsStatesIntoRAM(const std::string& outputFolder) {
|
||||
// Load states from .osfls files into RAM!
|
||||
for (const std::string filePath : _sourceFiles) {
|
||||
FieldlinesState newState;
|
||||
if (newState.loadStateFromOsfls(filePath)) {
|
||||
addStateToSequence(newState);
|
||||
if (!outputFolder.empty()) {
|
||||
ghoul::filesystem::File tmpFile(filePath);
|
||||
newState.saveStateToJson(outputFolder + tmpFile.baseName());
|
||||
}
|
||||
} else {
|
||||
LWARNING("Failed to load state from: " << filePath);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void RenderableFieldlinesSequence::extractOsflsInfoFromDictionary() {
|
||||
bool shouldLoadInRealtime = false;
|
||||
if (_dictionary->getValue(KeyOslfsLoadAtRuntime, shouldLoadInRealtime)) {
|
||||
_loadingStatesDynamically = shouldLoadInRealtime;
|
||||
} else {
|
||||
LWARNING(_name << ": " << KeyOslfsLoadAtRuntime <<
|
||||
" isn't specified! States will be stored in RAM!");
|
||||
}
|
||||
}
|
||||
|
||||
void RenderableFieldlinesSequence::setupProperties() {
|
||||
bool hasExtras = _states[0].nExtraQuantities() > 0;
|
||||
|
||||
// -------------- Add non-grouped properties (enablers and buttons) -------------- //
|
||||
addProperty(_pColorABlendEnabled);
|
||||
addProperty(_pDomainEnabled);
|
||||
addProperty(_pFlowEnabled);
|
||||
if (hasExtras) { addProperty(_pMaskingEnabled); }
|
||||
addProperty(_pFocusOnOriginBtn);
|
||||
addProperty(_pJumpToStartBtn);
|
||||
|
||||
// ----------------------------- Add Property Groups ----------------------------- //
|
||||
addPropertySubOwner(_pColorGroup);
|
||||
addPropertySubOwner(_pDomainGroup);
|
||||
addPropertySubOwner(_pFlowGroup);
|
||||
if (hasExtras) { addPropertySubOwner(_pMaskingGroup); }
|
||||
|
||||
// ------------------------- Add Properties to the groups ------------------------- //
|
||||
_pColorGroup.addProperty(_pColorUniform);
|
||||
_pDomainGroup.addProperty(_pDomainX);
|
||||
_pDomainGroup.addProperty(_pDomainY);
|
||||
_pDomainGroup.addProperty(_pDomainZ);
|
||||
_pDomainGroup.addProperty(_pDomainR);
|
||||
_pFlowGroup.addProperty(_pFlowReversed);
|
||||
_pFlowGroup.addProperty(_pFlowColor);
|
||||
_pFlowGroup.addProperty(_pFlowParticleSize);
|
||||
_pFlowGroup.addProperty(_pFlowParticleSpacing);
|
||||
_pFlowGroup.addProperty(_pFlowSpeed);
|
||||
if (hasExtras) {
|
||||
_pColorGroup.addProperty(_pColorMethod);
|
||||
_pColorGroup.addProperty(_pColorQuantity);
|
||||
_pColorGroup.addProperty(_pColorQuantityMin);
|
||||
_pColorGroup.addProperty(_pColorQuantityMax);
|
||||
_pColorGroup.addProperty(_pColorTablePath);
|
||||
_pMaskingGroup.addProperty(_pMaskingMin);
|
||||
_pMaskingGroup.addProperty(_pMaskingMax);
|
||||
_pMaskingGroup.addProperty(_pMaskingQuantity);
|
||||
|
||||
// --------------------- Add Options to OptionProperties --------------------- //
|
||||
_pColorMethod.addOption(ColorMethod::Uniform, "Uniform");
|
||||
_pColorMethod.addOption(ColorMethod::ByQuantity, "By Quantity");
|
||||
// Add option for each extra quantity. Assumes there are just as many names to
|
||||
// extra quantities as there are extra quantities. Also assume that all states in
|
||||
// the given sequence have the same extra quantities! */
|
||||
const size_t nExtraQuantities = _states[0].nExtraQuantities();
|
||||
const std::vector<std::string>& extraNamesVec = _states[0].extraQuantityNames();
|
||||
for (int i = 0; i < nExtraQuantities; ++i) {
|
||||
_pColorQuantity.addOption(i, extraNamesVec[i]);
|
||||
_pMaskingQuantity.addOption(i, extraNamesVec[i]);
|
||||
}
|
||||
// Each quantity should have its own color table and color table range, no more, no less
|
||||
_colorTablePaths.resize(nExtraQuantities, _colorTablePaths.back());
|
||||
_colorTableRanges.resize(nExtraQuantities, _colorTableRanges.back());
|
||||
_maskingRanges.resize(nExtraQuantities, _maskingRanges.back());
|
||||
}
|
||||
|
||||
definePropertyCallbackFunctions();
|
||||
|
||||
if (hasExtras) {
|
||||
// Set defaults
|
||||
_pColorQuantity = 0;
|
||||
_pColorQuantityMin = std::to_string(_colorTableRanges[0].x);
|
||||
_pColorQuantityMax = std::to_string(_colorTableRanges[0].y);
|
||||
_pColorTablePath = _colorTablePaths[0];
|
||||
|
||||
_pMaskingQuantity = 0;
|
||||
_pMaskingMin = std::to_string(_maskingRanges[0].x);
|
||||
_pMaskingMax = std::to_string(_maskingRanges[0].y);
|
||||
}
|
||||
}
|
||||
|
||||
void RenderableFieldlinesSequence::definePropertyCallbackFunctions() {
|
||||
// Add Property Callback Functions
|
||||
bool hasExtras = _states[0].nExtraQuantities() > 0;
|
||||
if (hasExtras) {
|
||||
_pColorQuantity.onChange([this] {
|
||||
_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] {
|
||||
float f = stringToFloat(_pColorQuantityMin, _colorTableRanges[_pColorQuantity].x);
|
||||
_pColorQuantityMin = std::to_string(f);
|
||||
_colorTableRanges[_pColorQuantity].x = f;
|
||||
});
|
||||
|
||||
_pColorQuantityMax.onChange([this] {
|
||||
float f = stringToFloat(_pColorQuantityMax, _colorTableRanges[_pColorQuantity].y);
|
||||
_pColorQuantityMax = std::to_string(f);
|
||||
_colorTableRanges[_pColorQuantity].y = f;
|
||||
});
|
||||
|
||||
_pMaskingQuantity.onChange([this] {
|
||||
_shouldUpdateMaskingBuffer = true;
|
||||
_pMaskingMin = std::to_string(_maskingRanges[_pMaskingQuantity].x);
|
||||
_pMaskingMax = std::to_string(_maskingRanges[_pMaskingQuantity].y);
|
||||
});
|
||||
|
||||
_pMaskingMin.onChange([this] {
|
||||
float f = stringToFloat(_pMaskingMin, _maskingRanges[_pMaskingQuantity].x);
|
||||
_pMaskingMin = std::to_string(f);
|
||||
_maskingRanges[_pMaskingQuantity].x = f;
|
||||
});
|
||||
|
||||
_pMaskingMax.onChange([this] {
|
||||
float f = stringToFloat(_pMaskingMax, _maskingRanges[_pMaskingQuantity].y);
|
||||
_pMaskingMax = std::to_string(f);
|
||||
_maskingRanges[_pMaskingQuantity].y = f;
|
||||
});
|
||||
}
|
||||
|
||||
_pFocusOnOriginBtn.onChange([this] {
|
||||
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] {
|
||||
OsEng.timeManager().time().setTime(_startTimes[0]);
|
||||
});
|
||||
}
|
||||
|
||||
// Calculate expected end time.
|
||||
void RenderableFieldlinesSequence::computeSequenceEndTime() {
|
||||
if (_nStates > 1) {
|
||||
const double lastTriggerTime = _startTimes[_nStates - 1];
|
||||
const double sequenceDuration = lastTriggerTime - _startTimes[0];
|
||||
const double averageStateDuration = sequenceDuration /
|
||||
(static_cast<double>(_nStates) - 1.0);
|
||||
_sequenceEndTime = lastTriggerTime + averageStateDuration;
|
||||
} else {
|
||||
// If there's just one state it should never disappear!
|
||||
_sequenceEndTime = DBL_MAX;
|
||||
}
|
||||
}
|
||||
|
||||
void RenderableFieldlinesSequence::setModelDependentConstants() {
|
||||
const fls::Model simulationModel = _states[0].model();
|
||||
float limit = 100.f; // Just used as a default value.
|
||||
switch (simulationModel) {
|
||||
case fls::Model::Batsrus:
|
||||
_scalingFactor = fls::ReToMeter;
|
||||
limit = 300; // Should include a long magnetotail
|
||||
break;
|
||||
case fls::Model::Enlil:
|
||||
_pFlowReversed = true;
|
||||
_scalingFactor = fls::AuToMeter;
|
||||
limit = 50; // Should include Plutos furthest distance from the Sun
|
||||
break;
|
||||
case fls::Model::Pfss:
|
||||
_scalingFactor = fls::RsToMeter;
|
||||
limit = 100; // Just a default value far away from the solar surface
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
_pDomainX.setMinValue(glm::vec2(-limit)); _pDomainX.setMaxValue(glm::vec2(limit));
|
||||
_pDomainY.setMinValue(glm::vec2(-limit)); _pDomainY.setMaxValue(glm::vec2(limit));
|
||||
_pDomainZ.setMinValue(glm::vec2(-limit)); _pDomainZ.setMaxValue(glm::vec2(limit));
|
||||
// Radial should range from 0 out to a corner of the cartesian box: sqrt(3) = 1.732..., 1.75 is a nice and round number
|
||||
_pDomainR.setMinValue(glm::vec2(0)); _pDomainR.setMaxValue(glm::vec2(limit*1.75f));
|
||||
|
||||
_pDomainX = glm::vec2(-limit, limit);
|
||||
_pDomainY = glm::vec2(-limit, limit);
|
||||
_pDomainZ = glm::vec2(-limit, limit);
|
||||
_pDomainR = glm::vec2(0, limit * 1.5f);
|
||||
}
|
||||
|
||||
// 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 filenameSize = 23; // number of characters in filename (excluding '.osfls')
|
||||
const size_t extSize = 6; // size(".osfls")
|
||||
|
||||
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 RenderableFieldlinesSequence::addStateToSequence(FieldlinesState& state) {
|
||||
_states.push_back(state);
|
||||
_startTimes.push_back(state.triggerTime());
|
||||
_nStates++;
|
||||
}
|
||||
|
||||
bool RenderableFieldlinesSequence::getStatesFromCdfFiles(const std::string& outputFolder) {
|
||||
|
||||
std::string seedFilePath;
|
||||
std::string tracingVar;
|
||||
std::vector<std::string> extraVars;
|
||||
if (!extractCdfInfoFromDictionary(seedFilePath, tracingVar, extraVars)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
std::vector<glm::vec3> seedPoints;
|
||||
if (!extractSeedPointsFromFile(seedFilePath, seedPoints)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
std::vector<std::string> extraMagVars;
|
||||
extractMagnitudeVarsFromStrings(extraVars, extraMagVars);
|
||||
|
||||
// Load states into RAM!
|
||||
for (std::string cdfPath : _sourceFiles) {
|
||||
|
||||
FieldlinesState newState;
|
||||
bool isSuccessful = fls::convertCdfToFieldlinesState(newState, cdfPath,
|
||||
seedPoints, tracingVar, extraVars, extraMagVars);
|
||||
|
||||
if (isSuccessful) {
|
||||
addStateToSequence(newState);
|
||||
if (!outputFolder.empty()) {
|
||||
newState.saveStateToOsfls(outputFolder);
|
||||
}
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
/*
|
||||
* Returns false if it fails to extract mandatory information!
|
||||
*/
|
||||
bool RenderableFieldlinesSequence::extractCdfInfoFromDictionary(
|
||||
std::string& seedFilePath,
|
||||
std::string& tracingVar,
|
||||
std::vector<std::string>& extraVars) {
|
||||
|
||||
if (_dictionary->getValue(KeyCdfSeedPointFile, seedFilePath)) {
|
||||
ghoul::filesystem::File seedPointFile(seedFilePath);
|
||||
if (FileSys.fileExists(seedPointFile)) {
|
||||
seedFilePath = absPath(seedFilePath);
|
||||
} else {
|
||||
LERROR(_name << ": The specified seed point file: '" << seedFilePath
|
||||
<< "', does not exist!");
|
||||
return false;
|
||||
}
|
||||
} else {
|
||||
LERROR(_name << ": Must specify '" << KeyCdfSeedPointFile << "'");
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!_dictionary->getValue(KeyCdfTracingVariable, tracingVar)) {
|
||||
tracingVar = "b"; // Magnetic field variable as default
|
||||
LWARNING(_name << ": No '" << KeyCdfTracingVariable << "', using default: "
|
||||
<< tracingVar);
|
||||
}
|
||||
|
||||
ghoul::Dictionary extraQuantityNamesDictionary;
|
||||
if (_dictionary->getValue(KeyCdfExtraVariables, extraQuantityNamesDictionary)) {
|
||||
const size_t nProvidedExtras = extraQuantityNamesDictionary.size();
|
||||
for (size_t i = 1; i <= nProvidedExtras; ++i) {
|
||||
extraVars.push_back(
|
||||
extraQuantityNamesDictionary.value<std::string>(std::to_string(i)));
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool RenderableFieldlinesSequence::extractSeedPointsFromFile(
|
||||
const std::string& path,
|
||||
std::vector<glm::vec3>& outVec) {
|
||||
|
||||
std::ifstream seedFile(FileSys.relativePath(path));
|
||||
if (!seedFile.good()) {
|
||||
LERROR("Could not open seed points file '" << path << "'");
|
||||
return false;
|
||||
}
|
||||
|
||||
LDEBUG("Reading seed points from file '" << path << "'");
|
||||
std::string line;
|
||||
while (std::getline(seedFile, line)) {
|
||||
glm::vec3 point;
|
||||
std::stringstream ss(line);
|
||||
ss >> point.x;
|
||||
ss >> point.y;
|
||||
ss >> point.z;
|
||||
outVec.push_back(std::move(point));
|
||||
}
|
||||
|
||||
if (outVec.size() == 0) {
|
||||
LERROR("Found no seed points in: " << path);
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
void RenderableFieldlinesSequence::extractMagnitudeVarsFromStrings(
|
||||
std::vector<std::string>& extraVars,
|
||||
std::vector<std::string>& extraMagVars) {
|
||||
|
||||
for (int i = 0; i < extraVars.size(); i++) {
|
||||
const std::string str = extraVars[i];
|
||||
// Check if string is in the format specified for magnitude variables
|
||||
if (str.substr(0, 2) == "|(" && str.substr(str.size() - 2, 2) == ")|") {
|
||||
std::istringstream ss(str.substr(2, str.size() - 4));
|
||||
std::string magVar;
|
||||
size_t counter = 0;
|
||||
while(std::getline(ss, magVar, ',')) {
|
||||
magVar.erase(std::remove_if(magVar.begin(), magVar.end(), ::isspace),
|
||||
magVar.end());
|
||||
extraMagVars.push_back(magVar);
|
||||
counter++;
|
||||
if (counter == 3) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (counter != 3 && counter > 0) {
|
||||
extraMagVars.erase(extraMagVars.end() - counter, extraMagVars.end());
|
||||
}
|
||||
extraVars.erase(extraVars.begin() + i);
|
||||
i--;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
} // namespace openspace
|
||||
@@ -0,0 +1,48 @@
|
||||
/*****************************************************************************************
|
||||
* *
|
||||
* OpenSpace *
|
||||
* *
|
||||
* Copyright (c) 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 "fragment.glsl"
|
||||
|
||||
in vec4 vs_color;
|
||||
in float vs_depth;
|
||||
|
||||
uniform bool usingAdditiveBlending;
|
||||
|
||||
Fragment getFragment() {
|
||||
if (vs_color.a == 0) {
|
||||
discard;
|
||||
}
|
||||
|
||||
vec4 fragColor = vs_color;
|
||||
|
||||
Fragment frag;
|
||||
frag.depth = vs_depth;
|
||||
frag.color = fragColor;
|
||||
|
||||
if (usingAdditiveBlending) {
|
||||
frag.blend = BLEND_MODE_ADDITIVE;
|
||||
}
|
||||
|
||||
return frag;
|
||||
}
|
||||
@@ -0,0 +1,126 @@
|
||||
/*****************************************************************************************
|
||||
* *
|
||||
* OpenSpace *
|
||||
* *
|
||||
* Copyright (c) 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. *
|
||||
****************************************************************************************/
|
||||
|
||||
#version __CONTEXT__
|
||||
|
||||
// General Uniforms that's always needed
|
||||
uniform vec4 lineColor;
|
||||
uniform mat4 modelViewProjection;
|
||||
|
||||
// Uniforms needed to color by quantity
|
||||
uniform int colorMethod;
|
||||
uniform sampler1D colorTable;
|
||||
uniform vec2 colorTableRange;
|
||||
|
||||
// Uniforms needed for Particle Flow
|
||||
uniform vec4 flowColor;
|
||||
uniform int particleSize;
|
||||
uniform int particleSpeed;
|
||||
uniform int particleSpacing;
|
||||
uniform double time;
|
||||
uniform bool usingParticles;
|
||||
|
||||
// Masking Uniforms
|
||||
uniform bool usingMasking;
|
||||
uniform vec2 maskingRange;
|
||||
|
||||
// Domain Uniforms
|
||||
uniform bool usingDomain;
|
||||
uniform vec2 domainLimX;
|
||||
uniform vec2 domainLimY;
|
||||
uniform vec2 domainLimZ;
|
||||
uniform vec2 domainLimR;
|
||||
|
||||
// Inputs
|
||||
layout(location = 0) in vec3 in_position; // Should be provided in meters
|
||||
layout(location = 1) in float in_color_scalar; // The extra value used to color lines. Location must correspond to _VA_COLOR in renderablefieldlinessequence.h
|
||||
layout(location = 2) in float in_masking_scalar; // The extra value used to mask out parts of lines. Location must correspond to _VA_MASKING in renderablefieldlinessequence.h
|
||||
|
||||
// These should correspond to the enum 'ColorMethod' in renderablefieldlinesequence.cpp
|
||||
const int uniformColor = 0;
|
||||
const int colorByQuantity = 1;
|
||||
|
||||
out vec4 vs_color;
|
||||
out float vs_depth;
|
||||
|
||||
|
||||
vec4 getTransferFunctionColor() {
|
||||
// Remap the color scalar to a [0,1] range
|
||||
const float lookUpVal = (in_color_scalar - colorTableRange.x) /
|
||||
(colorTableRange.y - colorTableRange.x);
|
||||
return texture(colorTable, lookUpVal);
|
||||
}
|
||||
|
||||
bool isPartOfParticle(const double time, const int vertexId, const int particleSize,
|
||||
const int particleSpeed, const int particleSpacing) {
|
||||
const int modulusResult = int(double(particleSpeed) * time + vertexId) % particleSpacing;
|
||||
return modulusResult > 0 && modulusResult <= particleSize;
|
||||
}
|
||||
|
||||
void main() {
|
||||
|
||||
bool hasColor = true;
|
||||
|
||||
if (usingMasking && (in_masking_scalar < maskingRange.x ||
|
||||
in_masking_scalar > maskingRange.y )) {
|
||||
hasColor = false;
|
||||
}
|
||||
|
||||
if (usingDomain && hasColor) {
|
||||
const float radius = length(in_position);
|
||||
|
||||
if (in_position.x < domainLimX.x || in_position.x > domainLimX.y ||
|
||||
in_position.y < domainLimY.x || in_position.y > domainLimY.y ||
|
||||
in_position.z < domainLimZ.x || in_position.z > domainLimZ.y ||
|
||||
radius < domainLimR.x || radius > domainLimR.y) {
|
||||
|
||||
hasColor = false;
|
||||
}
|
||||
}
|
||||
|
||||
if (hasColor) {
|
||||
const bool isParticle = usingParticles && isPartOfParticle(time, gl_VertexID,
|
||||
particleSize,
|
||||
particleSpeed,
|
||||
particleSpacing);
|
||||
|
||||
if (isParticle) {
|
||||
vs_color = flowColor;
|
||||
} else {
|
||||
vs_color = lineColor;
|
||||
}
|
||||
|
||||
if (colorMethod == colorByQuantity) {
|
||||
const vec4 quantityColor = getTransferFunctionColor();
|
||||
vs_color = vec4(quantityColor.xyz, vs_color.a * quantityColor.a);
|
||||
}
|
||||
} else {
|
||||
vs_color = vec4(0);
|
||||
}
|
||||
|
||||
vec4 position_in_meters = vec4(in_position, 1);
|
||||
vec4 positionClipSpace = modelViewProjection * position_in_meters;
|
||||
gl_Position = vec4(positionClipSpace.xy, 0, positionClipSpace.w);
|
||||
vs_depth = gl_Position.w;
|
||||
}
|
||||
@@ -0,0 +1,42 @@
|
||||
/*****************************************************************************************
|
||||
* *
|
||||
* 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/util/commons.h>
|
||||
|
||||
namespace openspace {
|
||||
namespace fls {
|
||||
|
||||
Model stringToModel(const std::string s) {
|
||||
if (s == "batsrus") {
|
||||
return Model::Batsrus;
|
||||
} else if (s == "enlil") {
|
||||
return Model::Enlil;
|
||||
} else if (s == "pfss") {
|
||||
return Model::Pfss;
|
||||
}
|
||||
return Model::Invalid;
|
||||
}
|
||||
|
||||
} // namespace fls
|
||||
} // 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
|
||||
};
|
||||
|
||||
Model stringToModel(const std::string s);
|
||||
|
||||
const float AuToMeter = 149597870700.f; // Astronomical Units
|
||||
const float ReToMeter = 6371000.f; // Earth radius
|
||||
const float RsToMeter = 695700000.f; // Sun radius
|
||||
|
||||
} // namespace fls
|
||||
} // namespace openspace
|
||||
|
||||
#endif // __OPENSPACE_MODULE_FIELDLINESSEQUENCE___COMMONS___H__
|
||||
@@ -0,0 +1,385 @@
|
||||
/*****************************************************************************************
|
||||
* *
|
||||
* 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/util/fieldlinesstate.h>
|
||||
|
||||
#include <ext/json/json.hpp>
|
||||
|
||||
#include <openspace/util/time.h>
|
||||
|
||||
#include <ghoul/logging/logmanager.h>
|
||||
|
||||
#include <fstream>
|
||||
|
||||
namespace {
|
||||
std::string _loggerCat = "FieldlinesState";
|
||||
const int CurrentVersion = 0;
|
||||
using json = nlohmann::json;
|
||||
}
|
||||
|
||||
namespace openspace {
|
||||
|
||||
/**
|
||||
* Converts all glm::vec3 in _vertexPositions from spherical (radius, latitude, longitude)
|
||||
* coordinates into cartesian coordinates. The longitude and latitude coordinates are
|
||||
* expected to be in degrees. scale is an optional scaling factor.
|
||||
*/
|
||||
void FieldlinesState::convertLatLonToCartesian(const float scale /* = 1.f */) {
|
||||
for (glm::vec3& p : _vertexPositions) {
|
||||
|
||||
const float r = p.x * scale;
|
||||
const float lat = glm::radians(p.y);
|
||||
const float lon = glm::radians(p.z);
|
||||
const float rCosLat = r * cos(lat);
|
||||
|
||||
p = glm::vec3(rCosLat * cos(lon), rCosLat* sin(lon), r * sin(lat));
|
||||
}
|
||||
}
|
||||
|
||||
void FieldlinesState::scalePositions(const float scale) {
|
||||
for (glm::vec3& p : _vertexPositions) {
|
||||
p *= scale;
|
||||
}
|
||||
}
|
||||
|
||||
bool FieldlinesState::loadStateFromOsfls(const std::string& pathToOsflsFile) {
|
||||
std::ifstream ifs(pathToOsflsFile, std::ifstream::binary);
|
||||
if (!ifs.is_open()) {
|
||||
LERRORC("FieldlinesState", "Couldn't open file: " + pathToOsflsFile);
|
||||
return false;
|
||||
}
|
||||
|
||||
int binFileVersion;
|
||||
ifs.read( reinterpret_cast<char*>(&binFileVersion), sizeof(int));
|
||||
|
||||
switch (binFileVersion) {
|
||||
case 0 : {
|
||||
// No need to put everything in this scope now, as only version 0 exists!
|
||||
}
|
||||
break;
|
||||
default :
|
||||
LERRORC("FieldlinesState","VERSION OF BINARY FILE WAS NOT RECOGNISED!");
|
||||
return false;
|
||||
}
|
||||
|
||||
// Define tmp variables to store meta data in
|
||||
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*>(&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(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)*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(nPoints);
|
||||
ifs.read( reinterpret_cast<char*>(vec.data()), sizeof(float) * nPoints);
|
||||
}
|
||||
|
||||
// Read all extra quantities' names. Stored as multiple c-strings
|
||||
std::string allNamesInOne;
|
||||
char* s = new char[byteSizeAllNames];
|
||||
ifs.read(s, byteSizeAllNames);
|
||||
allNamesInOne.assign(s, byteSizeAllNames);
|
||||
delete[] s;
|
||||
|
||||
size_t offset = 0;
|
||||
for (size_t i = 0; i < nExtras; ++i) {
|
||||
auto endOfVarName = allNamesInOne.find('\0', offset);
|
||||
endOfVarName -= offset;
|
||||
std::string varName = allNamesInOne.substr(offset, endOfVarName);
|
||||
offset += varName.size() + 1;
|
||||
_extraQuantityNames.push_back(varName);
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool FieldlinesState::loadStateFromJson(const std::string& pathToJsonFile,
|
||||
const fls::Model Model,
|
||||
const float coordToMeters = 1.f) {
|
||||
|
||||
// --------------------- ENSURE FILE IS VALID, THEN PARSE IT --------------------- //
|
||||
std::ifstream ifs(pathToJsonFile);
|
||||
|
||||
if (!ifs.is_open()) {
|
||||
LERROR("FAILED TO OPEN FILE: " << pathToJsonFile);
|
||||
return false;
|
||||
}
|
||||
|
||||
json jFile;
|
||||
ifs >> jFile;
|
||||
// -------------------------------------------------------------------------------- //
|
||||
|
||||
_model = Model;
|
||||
|
||||
const std::string sData = "data";
|
||||
const std::string sTrace = "trace";
|
||||
|
||||
// ----- EXTRACT THE EXTRA QUANTITY NAMES & TRIGGER TIME (same for all lines) ----- //
|
||||
{
|
||||
const json jTmp = *jFile.begin(); // First field line in the file
|
||||
_triggerTime = Time::convertTime(jTmp["time"]);
|
||||
|
||||
const std::string sColumns = "columns";
|
||||
auto variableNameVec = jTmp[sTrace][sColumns];
|
||||
const size_t nVariables = variableNameVec.size();
|
||||
const size_t nPosComponents = 3; // x,y,z
|
||||
|
||||
if (nVariables < nPosComponents) {
|
||||
LERROR(pathToJsonFile + ": Each field '" + sColumns +
|
||||
"' must contain the variables: 'x', 'y' and 'z' (order is important).");
|
||||
return false;
|
||||
}
|
||||
|
||||
for (size_t i = nPosComponents ; i < nVariables ; i++) {
|
||||
_extraQuantityNames.push_back(variableNameVec[i]);
|
||||
}
|
||||
}
|
||||
|
||||
const size_t nExtras = _extraQuantityNames.size();
|
||||
_extraQuantities.resize(nExtras);
|
||||
|
||||
size_t lineStartIdx = 0;
|
||||
// Loop through all fieldlines
|
||||
for (json::iterator lineIter = jFile.begin(); lineIter != jFile.end(); ++lineIter) {
|
||||
// The 'data' field in the 'trace' variable contains all vertex positions and the
|
||||
// extra quantities. Each element is an array related to one vertex point.
|
||||
const std::vector<std::vector<float>> jData = (*lineIter)[sTrace][sData];
|
||||
const size_t nPoints = jData.size();
|
||||
|
||||
for (size_t j = 0; j < nPoints; ++j) {
|
||||
const std::vector<float>& variables = jData[j];
|
||||
|
||||
// Expects the x, y and z variables to be stored first!
|
||||
const size_t xIdx = 0, yIdx = 1, zIdx = 2;
|
||||
_vertexPositions.push_back(coordToMeters * glm::vec3(variables[xIdx],
|
||||
variables[yIdx],
|
||||
variables[zIdx]));
|
||||
|
||||
// Add the extra quantites. Stored in the same array as the x,y,z variables.
|
||||
// Hence index of the first extra quantity = 3
|
||||
for (size_t xtraIdx = 3, k = 0 ; k < nExtras; ++k, ++xtraIdx) {
|
||||
_extraQuantities[k].push_back(variables[xtraIdx]);
|
||||
}
|
||||
}
|
||||
_lineCount.push_back(static_cast<GLsizei>(nPoints));
|
||||
_lineStart.push_back(static_cast<GLsizei>(lineStartIdx));
|
||||
lineStartIdx += nPoints;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param absPath must be the path to the file (incl. filename but excl. extension!)
|
||||
* Directory must exist! File is created (or overwritten if already existing).
|
||||
* File is structured like this: (for version 0)
|
||||
* 0. int - version number of binary state file! (in case something needs to be altered in the future, then increase CurrentVersion)
|
||||
* 1. double - _triggerTime
|
||||
* 2. int - _model
|
||||
* 3. bool - _isMorphable
|
||||
* 4. size_t - Number of lines in the state == _lineStart.size() == _lineCount.size()
|
||||
* 5. size_t - Total number of vertex points == _vertexPositions.size() == _extraQuantities[i].size()
|
||||
* 6. size_t - Number of extra quantites == _extraQuantities.size() == _extraQuantityNames.size()
|
||||
* 7. site_t - Number of total bytes that ALL _extraQuantityNames consists of (Each such name is stored as a c_str which means it ends with the null char '\0' )
|
||||
* 7. std::vector<GLint> - _lineStart
|
||||
* 8. std::vector<GLsizei> - _lineCount
|
||||
* 9. std::vector<glm::vec3> - _vertexPositions
|
||||
* 10. std::vector<float> - _extraQuantities
|
||||
* 11. array of c_str - Strings naming the extra quantities (elements of _extraQuantityNames). Each string ends with null char '\0'
|
||||
*/
|
||||
void FieldlinesState::saveStateToOsfls(const std::string& absPath) {
|
||||
// ------------------------------- Create the file ------------------------------- //
|
||||
std::string pathSafeTimeString = Time(_triggerTime).ISO8601();
|
||||
pathSafeTimeString.replace(13, 1, "-");
|
||||
pathSafeTimeString.replace(16, 1, "-");
|
||||
pathSafeTimeString.replace(19, 1, "-");
|
||||
const std::string fileName = pathSafeTimeString + ".osfls";
|
||||
|
||||
std::ofstream ofs(absPath + fileName, std::ofstream::binary | std::ofstream::trunc);
|
||||
if (!ofs.is_open()) {
|
||||
LERROR("Failed to save state to binary file: " << absPath << fileName);
|
||||
return;
|
||||
}
|
||||
|
||||
// --------- Add each string of _extraQuantityNames into one long string --------- //
|
||||
std::string allExtraQuantityNamesInOne = "";
|
||||
for (std::string str : _extraQuantityNames) {
|
||||
allExtraQuantityNamesInOne += str + '\0'; // Add the null char '\0' for easier reading
|
||||
}
|
||||
|
||||
const size_t nLines = _lineStart.size();
|
||||
const size_t nPoints = _vertexPositions.size();
|
||||
const size_t nExtras = _extraQuantities.size();
|
||||
const size_t nStringBytes = allExtraQuantityNamesInOne.size();
|
||||
|
||||
//------------------------------ WRITE EVERYTHING TO FILE ------------------------------
|
||||
// WHICH VERSION OF BINARY FIELDLINES STATE FILE - IN CASE STRUCTURE CHANGES IN THE FUTURE
|
||||
ofs.write( (char*)(&CurrentVersion), sizeof( int ) );
|
||||
|
||||
//-------------------- WRITE META DATA FOR STATE --------------------------------
|
||||
ofs.write( reinterpret_cast<char*>(&_triggerTime), sizeof( _triggerTime ) );
|
||||
ofs.write( reinterpret_cast<char*>(&_model), sizeof( int ) );
|
||||
ofs.write( reinterpret_cast<char*>(&_isMorphable), sizeof( bool ) );
|
||||
|
||||
ofs.write( reinterpret_cast<const char*>(&nLines), sizeof( size_t ) );
|
||||
ofs.write( reinterpret_cast<const char*>(&nPoints), sizeof( size_t ) );
|
||||
ofs.write( reinterpret_cast<const char*>(&nExtras), sizeof( size_t ) );
|
||||
ofs.write( reinterpret_cast<const char*>(&nStringBytes), sizeof( size_t ) );
|
||||
|
||||
//---------------------- WRITE ALL ARRAYS OF DATA --------------------------------
|
||||
ofs.write( reinterpret_cast<char*>(_lineStart.data()), sizeof(GLint) * nLines);
|
||||
ofs.write( reinterpret_cast<char*>(_lineCount.data()), sizeof(GLsizei) * nLines);
|
||||
ofs.write( reinterpret_cast<char*>(_vertexPositions.data()), sizeof(glm::vec3) * nPoints);
|
||||
// Write the data for each vector in _extraQuantities
|
||||
for (std::vector<float>& vec : _extraQuantities) {
|
||||
ofs.write( reinterpret_cast<char*>(vec.data()), sizeof(float) * nPoints);
|
||||
}
|
||||
ofs.write( allExtraQuantityNamesInOne.c_str(), nStringBytes);
|
||||
}
|
||||
|
||||
// TODO: This should probably be rewritten, but this is the way the files were structured by CCMC
|
||||
// Structure of File! NO TRAILING COMMAS ALLOWED!
|
||||
// Additional info can be stored within each line as the code only extracts the keys it needs (time, trace & data)
|
||||
// The key/name of each line ("0" & "1" in the example below) is arbitrary
|
||||
// {
|
||||
// "0":{
|
||||
// "time": "YYYY-MM-DDTHH:MM:SS.XXX",
|
||||
// "trace": {
|
||||
// "columns": ["x","y","z","s","temperature","rho","j_para"],
|
||||
// "data": [[8.694,127.853,115.304,0.0,0.047,9.249,-5e-10],...,[8.698,127.253,114.768,0.800,0.0,9.244,-5e-10]]
|
||||
// },
|
||||
// },
|
||||
// "1":{
|
||||
// "time": "YYYY-MM-DDTHH:MM:SS.XXX
|
||||
// "trace": {
|
||||
// "columns": ["x","y","z","s","temperature","rho","j_para"],
|
||||
// "data": [[8.694,127.853,115.304,0.0,0.047,9.249,-5e-10],...,[8.698,127.253,114.768,0.800,0.0,9.244,-5e-10]]
|
||||
// },
|
||||
// }
|
||||
// }
|
||||
void FieldlinesState::saveStateToJson(const std::string& absPath) {
|
||||
// Create the file
|
||||
const std::string ext = ".json";
|
||||
std::ofstream ofs(absPath + ext, std::ofstream::trunc);
|
||||
if (!ofs.is_open()) {
|
||||
LERROR("Failed to save state to json file at location: " << absPath << ext);
|
||||
return;
|
||||
}
|
||||
LINFO("Saving fieldline state to: " << absPath << ext );
|
||||
|
||||
json jColumns = {"x", "y", "z"};
|
||||
for (std::string s : _extraQuantityNames) {
|
||||
jColumns.push_back(s);
|
||||
}
|
||||
|
||||
json jFile;
|
||||
|
||||
const std::string timeStr = Time(_triggerTime).ISO8601();
|
||||
const size_t nLines = _lineStart.size();
|
||||
const size_t nPoints = _vertexPositions.size();
|
||||
const size_t nExtras = _extraQuantities.size();
|
||||
|
||||
size_t pointIndex = 0;
|
||||
for (size_t lineIndex = 0; lineIndex < nLines; lineIndex++) {
|
||||
json jData = json::array();
|
||||
for (size_t i = 0; i < _lineCount[lineIndex]; i++, pointIndex++) {
|
||||
const glm::vec3 pos = _vertexPositions[pointIndex];
|
||||
json jDataElement = {pos.x, pos.y, pos.z};
|
||||
|
||||
for (size_t extraIndex = 0; extraIndex < nExtras; extraIndex++) {
|
||||
jDataElement.push_back(_extraQuantities[extraIndex][pointIndex]);
|
||||
}
|
||||
jData.push_back(jDataElement);
|
||||
}
|
||||
|
||||
jFile[std::to_string(lineIndex)] = {
|
||||
{"time", timeStr},
|
||||
{"trace", {
|
||||
{"columns", jColumns},
|
||||
{"data", jData}
|
||||
}}
|
||||
};
|
||||
}
|
||||
|
||||
//------------------------------ WRITE EVERYTHING TO FILE ------------------------------
|
||||
const int indentationSpaces = 2;
|
||||
ofs << std::setw(indentationSpaces) << jFile << std::endl;
|
||||
|
||||
LINFO("Saved fieldline state to: " << absPath << ext );
|
||||
}
|
||||
|
||||
// Returns one of the extra quantity vectors, _extraQuantities[index].
|
||||
// If index is out of scope an empty vector is returned and the referenced bool will be false.
|
||||
const std::vector<float>& FieldlinesState::extraQuantity(const size_t index,
|
||||
bool& isSuccessful) const {
|
||||
if (index < _extraQuantities.size()) {
|
||||
isSuccessful = true;
|
||||
return _extraQuantities[index];
|
||||
}
|
||||
LERROR("Provided Index was out of scope!");
|
||||
isSuccessful = false;
|
||||
// return empty vector which goes out of scope hence unusable!
|
||||
return std::vector<float>();
|
||||
}
|
||||
|
||||
/** Moves the points in @param line over to _vertexPositions and updates _lineStart & _lineCount accordingly.
|
||||
*/
|
||||
void FieldlinesState::addLine(std::vector<glm::vec3>& line) {
|
||||
const size_t nNewPoints = line.size();
|
||||
const size_t nOldPoints = _vertexPositions.size();
|
||||
_lineStart.push_back(static_cast<GLint>(nOldPoints));
|
||||
_lineCount.push_back(static_cast<GLsizei>(nNewPoints));
|
||||
_vertexPositions.reserve(nOldPoints + nNewPoints);
|
||||
_vertexPositions.insert(_vertexPositions.end(), std::make_move_iterator(line.begin()),
|
||||
std::make_move_iterator(line.end()));
|
||||
line.clear();
|
||||
}
|
||||
|
||||
void FieldlinesState::setExtraQuantityNames(std::vector<std::string>& names) {
|
||||
_extraQuantityNames = std::move(names);
|
||||
names.clear();
|
||||
_extraQuantities.resize(_extraQuantityNames.size());
|
||||
}
|
||||
} // namespace openspace
|
||||
@@ -0,0 +1,90 @@
|
||||
/*****************************************************************************************
|
||||
* *
|
||||
* 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___FIELDLINESSTATE___H__
|
||||
#define __OPENSPACE_MODULE_FIELDLINESSEQUENCE___FIELDLINESSTATE___H__
|
||||
|
||||
#include <modules/fieldlinessequence/util/commons.h>
|
||||
|
||||
#include <ghoul/opengl/ghoul_gl.h>
|
||||
#include <ghoul/glm.h>
|
||||
|
||||
#include <string>
|
||||
#include <vector>
|
||||
|
||||
#ifdef OPENSPACE_MODULE_KAMELEON_ENABLED
|
||||
namespace ccmc {
|
||||
class Kameleon;
|
||||
}
|
||||
#endif // OPENSPACE_MODULE_KAMELEON_ENABLED
|
||||
|
||||
namespace openspace {
|
||||
|
||||
class FieldlinesState {
|
||||
public:
|
||||
void convertLatLonToCartesian(const float scale = 1.f);
|
||||
void scalePositions(const float scale);
|
||||
|
||||
bool loadStateFromOsfls(const std::string& pathToOsflsFile);
|
||||
void saveStateToOsfls(const std::string& pathToOsflsFile);
|
||||
|
||||
bool loadStateFromJson(const std::string& pathToJsonFile,
|
||||
const fls::Model model, const float coordToMeters);
|
||||
void saveStateToJson(const std::string& pathToJsonFile);
|
||||
|
||||
// ----------------------------------- GETTERS ----------------------------------- //
|
||||
const std::vector<std::vector<float>>& extraQuantities() const { return _extraQuantities; }
|
||||
const std::vector<std::string>& extraQuantityNames() const { return _extraQuantityNames; }
|
||||
const std::vector<GLsizei>& lineCount() const { return _lineCount; }
|
||||
const std::vector<GLint>& lineStart() const { return _lineStart; }
|
||||
fls::Model model() const { return _model; }
|
||||
size_t nExtraQuantities() const { return _extraQuantities.size(); }
|
||||
double triggerTime() const { return _triggerTime; }
|
||||
const std::vector<glm::vec3>& vertexPositions() const { return _vertexPositions; }
|
||||
|
||||
// Special getter. Returns extraQuantities[INDEX].
|
||||
const std::vector<float>& extraQuantity(const size_t INDEX, bool& isSuccesful) const;
|
||||
|
||||
void setModel(const fls::Model m) { _model = m; }
|
||||
void setTriggerTime(const double t) { _triggerTime = t; }
|
||||
void setExtraQuantityNames(std::vector<std::string>& names);
|
||||
|
||||
void addLine(std::vector<glm::vec3>& line);
|
||||
void appendToExtra(size_t idx, float val) { _extraQuantities[idx].push_back(val); }
|
||||
|
||||
private:
|
||||
bool _isMorphable = false;
|
||||
double _triggerTime = -1.0;
|
||||
fls::Model _model;
|
||||
|
||||
std::vector<std::vector<float>> _extraQuantities;
|
||||
std::vector<std::string> _extraQuantityNames;
|
||||
std::vector<GLsizei> _lineCount;
|
||||
std::vector<GLint> _lineStart;
|
||||
std::vector<glm::vec3> _vertexPositions;
|
||||
};
|
||||
|
||||
} // namespace openspace
|
||||
|
||||
#endif // __OPENSPACE_MODULE_FIELDLINESSEQUENCE___FIELDLINESSTATE___H__
|
||||
@@ -0,0 +1,342 @@
|
||||
/*****************************************************************************************
|
||||
* *
|
||||
* 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/util/kameleonfieldlinehelper.h>
|
||||
|
||||
#include <modules/fieldlinessequence/util/commons.h>
|
||||
#include <modules/fieldlinessequence/util/fieldlinesstate.h>
|
||||
|
||||
#ifdef OPENSPACE_MODULE_KAMELEON_ENABLED
|
||||
#include <ccmc/Kameleon.h>
|
||||
#include <ccmc/KameleonInterpolator.h>
|
||||
#include <modules/kameleon/include/kameleonhelper.h>
|
||||
#endif // OPENSPACE_MODULE_KAMELEON_ENABLED
|
||||
|
||||
#include <ghoul/logging/logmanager.h>
|
||||
|
||||
#include <memory>
|
||||
|
||||
namespace {
|
||||
std::string _loggerCat = "FieldlinesSequence[ Kameleon ]";
|
||||
|
||||
const std::string TAsPOverRho = "T = p/rho";
|
||||
const std::string JParallelB = "Current: mag(J||B)";
|
||||
const float ToKelvin = 72429735.6984f; // <-- [nPa]/[amu/cm^3] * ToKelvin => Temperature in Kelvin
|
||||
}
|
||||
|
||||
namespace openspace {
|
||||
namespace fls {
|
||||
|
||||
// -------------------- DECLARE FUNCTIONS USED (ONLY) IN THIS FILE -------------------- //
|
||||
#ifdef OPENSPACE_MODULE_KAMELEON_ENABLED
|
||||
bool addLinesToState(ccmc::Kameleon* kameleon, const std::vector<glm::vec3>& seeds,
|
||||
const std::string tracingVar, FieldlinesState& state);
|
||||
void addExtraQuantities(ccmc::Kameleon* kameleon,
|
||||
std::vector<std::string>& extraScalarVars,
|
||||
std::vector<std::string>& extraMagVars,
|
||||
FieldlinesState& state);
|
||||
void prepareStateAndKameleonForExtras(ccmc::Kameleon* kameleon,
|
||||
std::vector<std::string>& extraScalarVars,
|
||||
std::vector<std::string>& extraMagVars,
|
||||
FieldlinesState& state);
|
||||
#endif // OPENSPACE_MODULE_KAMELEON_ENABLED
|
||||
// ----------------------------------------------------------------------------------- //
|
||||
|
||||
/** Traces field lines from the provided cdf file using kameleon and stores the data in the provided FieldlinesState.
|
||||
* Returns `false` if it fails to create a valid state. Requires the kameleon module to be activated!
|
||||
* @param state, FieldlineState which should hold the extracted data
|
||||
* @param cdfPath, std::string of the absolute path to a .cdf file
|
||||
* @param seedPoints, vector of seed points from which to trace field lines
|
||||
* @param tracingVar, which quantity to trace lines from. Typically "b" for magnetic field lines and "u" for velocity flow lines
|
||||
* @param extraVars, extra scalar quantities to be stored in the FieldlinesState; e.g. "T" for temperature, "rho" for density or "P" for pressure
|
||||
* @param extraMagVars, variables which should be used for extracting magnitudes, must be a multiple of 3; e.g. "ux", "uy" & "uz" to get the magnitude of the velocity vector at each line vertex
|
||||
*/
|
||||
bool convertCdfToFieldlinesState(FieldlinesState& state, const std::string cdfPath,
|
||||
const std::vector<glm::vec3>& seedPoints,
|
||||
const std::string tracingVar,
|
||||
std::vector<std::string>& extraVars,
|
||||
std::vector<std::string>& extraMagVars) {
|
||||
|
||||
#ifndef OPENSPACE_MODULE_KAMELEON_ENABLED
|
||||
|
||||
LERROR("CDF inputs provided but Kameleon module is deactivated!");
|
||||
return false;
|
||||
|
||||
#else // OPENSPACE_MODULE_KAMELEON_ENABLED
|
||||
|
||||
// Create Kameleon object and open CDF file!
|
||||
std::unique_ptr<ccmc::Kameleon> kameleon =
|
||||
kameleonHelper::createKameleonObject(cdfPath);
|
||||
|
||||
state.setModel(fls::stringToModel(kameleon->getModelName()));
|
||||
state.setTriggerTime(kameleonHelper::getTime(kameleon.get()));
|
||||
|
||||
if (addLinesToState(kameleon.get(), seedPoints, tracingVar, state)) {
|
||||
// The line points are in their RAW format (unscaled & maybe spherical)
|
||||
// Before we scale to meters (and maybe cartesian) we must extract
|
||||
// the extraQuantites, as the iterpolator needs the unaltered positions
|
||||
addExtraQuantities(kameleon.get(), extraVars, extraMagVars, state);
|
||||
switch (state.model()) {
|
||||
case fls::Batsrus:
|
||||
state.scalePositions(fls::ReToMeter);
|
||||
break;
|
||||
case fls::Enlil :
|
||||
state.convertLatLonToCartesian(fls::AuToMeter);
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
#endif // OPENSPACE_MODULE_KAMELEON_ENABLED
|
||||
}
|
||||
|
||||
#ifdef OPENSPACE_MODULE_KAMELEON_ENABLED
|
||||
/**
|
||||
* Traces and adds line vertices to state.
|
||||
* Vertices are not scaled to meters nor converted from spherical into cartesian coordinates.
|
||||
* Note that extraQuantities will NOT be set!
|
||||
*/
|
||||
bool addLinesToState(ccmc::Kameleon* kameleon, const std::vector<glm::vec3>& seedPoints,
|
||||
const std::string tracingVar, FieldlinesState& state) {
|
||||
|
||||
float innerBoundaryLimit;
|
||||
|
||||
switch (state.model()) {
|
||||
case fls::Model::Batsrus :
|
||||
innerBoundaryLimit = 2.5f; // TODO specify in Lua?
|
||||
break;
|
||||
case fls::Model::Enlil :
|
||||
innerBoundaryLimit = 0.11f; // TODO specify in Lua?
|
||||
break;
|
||||
default:
|
||||
LERROR("OpenSpace's fieldlines sequence currently only supports CDFs from the"
|
||||
<< " BATSRUS and ENLIL models!");
|
||||
return false;
|
||||
}
|
||||
|
||||
// ---------------------------- LOAD TRACING VARIABLE ---------------------------- //
|
||||
if (!kameleon->loadVariable(tracingVar)) {
|
||||
LERROR("FAILED TO LOAD TRACING VARIABLE: " + tracingVar);
|
||||
return false;
|
||||
}
|
||||
|
||||
bool isSuccesful = false;
|
||||
LINFO("TRACING FIELD LINES!");
|
||||
// LOOP THROUGH THE SEED POINTS, TRACE LINES, CONVERT POINTS TO glm::vec3 AND STORE //
|
||||
for (glm::vec3 seed : seedPoints) {
|
||||
//--------------------------------------------------------------------------//
|
||||
// We have to create a new tracer (or actually a new interpolator) for each //
|
||||
// new line, otherwise some issues occur //
|
||||
//--------------------------------------------------------------------------//
|
||||
std::unique_ptr<ccmc::Interpolator> interpolator =
|
||||
std::make_unique<ccmc::KameleonInterpolator>(kameleon->model);
|
||||
ccmc::Tracer tracer(kameleon, interpolator.get());
|
||||
tracer.setInnerBoundary(innerBoundaryLimit); // TODO specify in Lua?
|
||||
ccmc::Fieldline ccmcFieldline = tracer.bidirectionalTrace(tracingVar, seed.x,
|
||||
seed.y,
|
||||
seed.z);
|
||||
const std::vector<ccmc::Point3f>& positions = ccmcFieldline.getPositions();
|
||||
|
||||
const size_t nLinePoints = positions.size();
|
||||
|
||||
std::vector<glm::vec3> vertices;
|
||||
vertices.reserve(nLinePoints);
|
||||
for (const ccmc::Point3f& p : positions) {
|
||||
vertices.emplace_back(p.component1, p.component2, p.component3);
|
||||
}
|
||||
state.addLine(vertices);
|
||||
isSuccesful = (nLinePoints > 0) ? true : isSuccesful;
|
||||
}
|
||||
|
||||
return isSuccesful;
|
||||
}
|
||||
#endif // OPENSPACE_MODULE_KAMELEON_ENABLED
|
||||
|
||||
/**
|
||||
* Loops through state's _vertexPositions and extracts corresponding 'extraQuantities'
|
||||
* from the kameleon object using a ccmc::interpolator.
|
||||
* Note that the positions MUST be unaltered (NOT scaled NOR converted to different
|
||||
* coordinate system)!
|
||||
*
|
||||
* @param kameleon raw pointer to an already opened Kameleon object
|
||||
* @param extraScalarVars vector of strings. Strings should be names of a scalar quantities
|
||||
* to load into _extraQuantites; such as: "T" for temperature or "rho" for density.
|
||||
* @param extraMagVars vector of strings. Size must be multiple of 3. Strings should be
|
||||
* names of the components needed to calculate magnitude. E.g. {"ux", "uy", "uz"} will
|
||||
* calculate: sqrt(ux*ux + uy*uy + uz*uz). Magnitude will be stored in _extraQuantities
|
||||
* @param state, The FieldlinesState which the extra quantities should be added to.
|
||||
*/
|
||||
#ifdef OPENSPACE_MODULE_KAMELEON_ENABLED
|
||||
void addExtraQuantities(ccmc::Kameleon* kameleon,
|
||||
std::vector<std::string>& extraScalarVars,
|
||||
std::vector<std::string>& extraMagVars, FieldlinesState& state) {
|
||||
|
||||
prepareStateAndKameleonForExtras(kameleon, extraScalarVars, extraMagVars, state);
|
||||
|
||||
const size_t nXtraScalars = extraScalarVars.size();
|
||||
const size_t nXtraMagnitudes = extraMagVars.size() / 3;
|
||||
|
||||
std::unique_ptr<ccmc::Interpolator> interpolator =
|
||||
std::make_unique<ccmc::KameleonInterpolator>(kameleon->model);
|
||||
|
||||
// ------ Extract all the extraQuantities from kameleon and store in state! ------ //
|
||||
for (const glm::vec3 p : state.vertexPositions()) {
|
||||
// Load the scalars!
|
||||
for (size_t i = 0; i < nXtraScalars; i++) {
|
||||
float val;
|
||||
if (extraScalarVars[i] == TAsPOverRho) {
|
||||
val = interpolator->interpolate("p", p.x, p.y, p.z);
|
||||
val *= ToKelvin;
|
||||
val /= interpolator->interpolate("rho", p.x, p.y, p.z);
|
||||
} else {
|
||||
val = interpolator->interpolate(extraScalarVars[i], p.x, p.y, p.z);
|
||||
|
||||
// When measuring density in ENLIL CCMC multiply by the radius^2
|
||||
if (extraScalarVars[i] == "rho" && state.model() == fls::Model::Enlil) {
|
||||
val *= std::pow(p.x * fls::AuToMeter, 2.0f);
|
||||
}
|
||||
}
|
||||
state.appendToExtra(i, val);
|
||||
}
|
||||
// Calculate and store the magnitudes!
|
||||
for (size_t i = 0; i < nXtraMagnitudes; ++i) {
|
||||
const size_t idx = i*3;
|
||||
|
||||
const float x = interpolator->interpolate(extraMagVars[idx] , p.x, p.y, p.z);
|
||||
const float y = interpolator->interpolate(extraMagVars[idx+1], p.x, p.y, p.z);
|
||||
const float z = interpolator->interpolate(extraMagVars[idx+2], p.x, p.y, p.z);
|
||||
float val;
|
||||
// When looking at the current's magnitude in Batsrus, CCMC staff are
|
||||
// only interested in the magnitude parallel to the magnetic field
|
||||
if (state.extraQuantityNames()[nXtraScalars + i] == JParallelB) {
|
||||
const glm::vec3 normMagnetic = glm::normalize(glm::vec3(
|
||||
interpolator->interpolate("bx", p.x, p.y, p.z),
|
||||
interpolator->interpolate("by", p.x, p.y, p.z),
|
||||
interpolator->interpolate("bz", p.x, p.y, p.z)));
|
||||
// Magnitude of the part of the current vector that's parallel to
|
||||
// the magnetic field vector!
|
||||
val = glm::dot(glm::vec3(x,y,z), normMagnetic);
|
||||
|
||||
} else {
|
||||
val = std::sqrt(x*x + y*y + z*z);
|
||||
}
|
||||
state.appendToExtra(i + nXtraScalars, val);
|
||||
}
|
||||
}
|
||||
}
|
||||
#endif // OPENSPACE_MODULE_KAMELEON_ENABLED
|
||||
|
||||
/** Validate the provided extra quantity variables -> load the data from the validated
|
||||
* quantities into the kameleon object & add the quantity names into the state's
|
||||
* _extraQuantityNames vector.
|
||||
*
|
||||
* @param kameleon, raw pointer to an already opened kameleon object
|
||||
* @param extraScalarVars, names of scalar quantities to add to state; e.g "rho" for density
|
||||
* @param extraMagVars, names of the variables used for calculating magnitudes. Must be multiple of 3.
|
||||
*/
|
||||
#ifdef OPENSPACE_MODULE_KAMELEON_ENABLED
|
||||
void prepareStateAndKameleonForExtras(ccmc::Kameleon* kameleon,
|
||||
std::vector<std::string>& extraScalarVars,
|
||||
std::vector<std::string>& extraMagVars,
|
||||
FieldlinesState& state) {
|
||||
std::vector<std::string> extraQuantityNames;
|
||||
fls::Model model = fls::stringToModel(kameleon->getModelName());
|
||||
|
||||
// Load the existing SCALAR variables into kameleon.
|
||||
// Remove non-existing variables from vector
|
||||
for (int i = 0; i < extraScalarVars.size(); i++) {
|
||||
std::string& str = extraScalarVars[i];
|
||||
bool isSuccesful = kameleon->doesVariableExist(str) && kameleon->loadVariable(str);
|
||||
if (!isSuccesful &&
|
||||
(model == fls::Model::Batsrus && (str == TAsPOverRho || str == "T" ))) {
|
||||
LDEBUG("BATSRUS doesn't contain variable T for temperature. Trying to "
|
||||
<< "calculate it using the ideal gas law: T = pressure/density");
|
||||
const std::string p = "p", r = "rho";
|
||||
isSuccesful = kameleon->doesVariableExist(p) && kameleon->loadVariable(p)
|
||||
&& kameleon->doesVariableExist(r) && kameleon->loadVariable(r);
|
||||
str = TAsPOverRho;
|
||||
}
|
||||
if (!isSuccesful) {
|
||||
LWARNING("FAILED TO LOAD EXTRA VARIABLE: '" << str << "'. Ignoring it!");
|
||||
extraScalarVars.erase(extraScalarVars.begin() + i);
|
||||
--i;
|
||||
} else {
|
||||
extraQuantityNames.push_back(str);
|
||||
}
|
||||
}
|
||||
|
||||
// Load the existing magnitude variables (should be provided in multiple of 3)
|
||||
// into kameleon. Remove non-existing variables from vector
|
||||
if (extraMagVars.size() % 3 == 0) {
|
||||
for (int i = 0; i < static_cast<int>(extraMagVars.size()); i += 3) {
|
||||
std::string s1 = extraMagVars[i];
|
||||
std::string s2 = extraMagVars[i+1];
|
||||
std::string s3 = extraMagVars[i+2];
|
||||
bool isSuccesful = kameleon->doesVariableExist(s1) &&
|
||||
kameleon->doesVariableExist(s2) &&
|
||||
kameleon->doesVariableExist(s3) &&
|
||||
kameleon->loadVariable(s1) &&
|
||||
kameleon->loadVariable(s2) &&
|
||||
kameleon->loadVariable(s3);
|
||||
std::string name = "Magnitude of (" + s1 + ", "+ s2 + ", "+ s3 + ")";
|
||||
if (isSuccesful && model == fls::Model::Batsrus && s1 == "jx" && s2 == "jy"
|
||||
&& s3 == "jz") {
|
||||
// CCMC isn't really interested in the magnitude of current, but by the
|
||||
// magnitude of the part of the current's vector that is parallel to the
|
||||
// magnetic field => ensure that the magnetic variables are loaded
|
||||
isSuccesful = kameleon->doesVariableExist("bx") &&
|
||||
kameleon->doesVariableExist("by") &&
|
||||
kameleon->doesVariableExist("bz") &&
|
||||
kameleon->loadVariable("bx") &&
|
||||
kameleon->loadVariable("by") &&
|
||||
kameleon->loadVariable("bz");
|
||||
name = JParallelB;
|
||||
}
|
||||
if (!isSuccesful) {
|
||||
LWARNING("FAILED TO LOAD AT LEAST ONE OF THE MAGNITUDE VARIABLES: "
|
||||
<< s1 << ", " << s2 << " & " << s3
|
||||
<< ". Removing ability to store corresponding magnitude!");
|
||||
extraMagVars.erase(extraMagVars.begin() + i, extraMagVars.begin() + i + 3);
|
||||
i -= 3;
|
||||
} else {
|
||||
extraQuantityNames.push_back(name);
|
||||
}
|
||||
}
|
||||
} else {
|
||||
// WRONG NUMBER OF MAGNITUDE VARIABLES.. REMOVE ALL!
|
||||
extraMagVars.clear();
|
||||
LWARNING("Wrong number of variables provided for storing magnitudes. "
|
||||
<< "Expects multiple of 3 but " << extraMagVars.size()
|
||||
<< " are provided");
|
||||
}
|
||||
state.setExtraQuantityNames(extraQuantityNames);
|
||||
}
|
||||
#endif // OPENSPACE_MODULE_KAMELEON_ENABLED
|
||||
|
||||
} // namespace fls
|
||||
} // namespace openspace
|
||||
@@ -0,0 +1,48 @@
|
||||
/*****************************************************************************************
|
||||
* *
|
||||
* 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___KAMELEONFIELDLINEHELPER___H__
|
||||
#define __OPENSPACE_MODULE_FIELDLINESSEQUENCE___KAMELEONFIELDLINEHELPER___H__
|
||||
|
||||
#include <ghoul/glm.h>
|
||||
|
||||
#include <string>
|
||||
#include <vector>
|
||||
|
||||
namespace openspace {
|
||||
|
||||
class FieldlinesState;
|
||||
|
||||
namespace fls {
|
||||
|
||||
bool convertCdfToFieldlinesState(FieldlinesState& state, const std::string cdfPath,
|
||||
const std::vector<glm::vec3>& seedPoints,
|
||||
const std::string tracingVar,
|
||||
std::vector<std::string>& extraVars,
|
||||
std::vector<std::string>& extraMagVars);
|
||||
|
||||
} // namespace fls
|
||||
} // namespace openspace
|
||||
|
||||
#endif // __OPENSPACE_MODULE_FIELDLINESSEQUENCE___KAMELEONFIELDLINEHELPER___H__
|
||||
@@ -26,11 +26,13 @@ include(${OPENSPACE_CMAKE_EXT_DIR}/module_definition.cmake)
|
||||
|
||||
set(HEADER_FILES
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/include/kameleonwrapper.h
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/include/kameleonhelper.h
|
||||
)
|
||||
source_group("Header Files" FILES ${HEADER_FILES})
|
||||
|
||||
set(SOURCE_FILES
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/src/kameleonwrapper.cpp
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/src/kameleonhelper.cpp
|
||||
)
|
||||
source_group("Source Files" FILES ${SOURCE_FILES})
|
||||
|
||||
|
||||
@@ -0,0 +1,42 @@
|
||||
/*****************************************************************************************
|
||||
* *
|
||||
* 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_KAMELEON___KAMELEONHELPER___H__
|
||||
#define __OPENSPACE_MODULE_KAMELEON___KAMELEONHELPER___H__
|
||||
|
||||
#include <memory>
|
||||
#include <string>
|
||||
|
||||
namespace ccmc {
|
||||
class Kameleon;
|
||||
} // namespace ccmc
|
||||
|
||||
namespace openspace::kameleonHelper {
|
||||
|
||||
std::unique_ptr<ccmc::Kameleon> createKameleonObject(const std::string& cdfFilePath);
|
||||
double getTime(ccmc::Kameleon* kameleon);
|
||||
|
||||
} //namespace openspace::kameleonHelper
|
||||
|
||||
#endif // __OPENSPACE_MODULE_KAMELEON___KAMELEONHELPER___H__
|
||||
@@ -0,0 +1,152 @@
|
||||
/*****************************************************************************************
|
||||
* *
|
||||
* 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/kameleon/include/kameleonhelper.h>
|
||||
|
||||
#include <openspace/util/time.h>
|
||||
|
||||
#include <ccmc/Kameleon.h>
|
||||
#include <ghoul/logging/logmanager.h>
|
||||
|
||||
|
||||
namespace {
|
||||
std::string _loggerCat = "KameleonHelper";
|
||||
}
|
||||
|
||||
namespace openspace::kameleonHelper {
|
||||
|
||||
/**
|
||||
* Opens a ccmc::Kameleon object from the provided path to a .cdf file.
|
||||
* Path should be absolute.
|
||||
*
|
||||
* Returns 'nullptr' if the file fails to open!
|
||||
*/
|
||||
std::unique_ptr<ccmc::Kameleon> createKameleonObject(const std::string& cdfFilePath) {
|
||||
|
||||
// ---------------------------- CREATE KAMELEON OBJECT ---------------------------- //
|
||||
std::unique_ptr<ccmc::Kameleon> kameleon = std::make_unique<ccmc::Kameleon>();
|
||||
LDEBUG("\tOpening the cdf file: " << cdfFilePath);
|
||||
long kamStatus = kameleon->open(cdfFilePath);
|
||||
|
||||
if (kamStatus != ccmc::FileReader::OK) {
|
||||
LERROR("Failed to create a Kameleon Object from file: " << cdfFilePath);
|
||||
return nullptr;
|
||||
}
|
||||
LDEBUG("\tSuccessfully opened : " << cdfFilePath);
|
||||
return kameleon;
|
||||
}
|
||||
|
||||
/**
|
||||
* Extract the time for the simulation. Time is returned as a J2000 double.
|
||||
*
|
||||
* *NOTE!* The function has only been tested for some BATSRUS and ENLIL and may need to
|
||||
* be updated to work with other models!
|
||||
*/
|
||||
double getTime(ccmc::Kameleon* kameleon) {
|
||||
// Inspiration from 'void KameleonInterpolator::setEphemTime()' which doesn't seem to
|
||||
// exist in the version of Kameleon that is included in OpenSpace. Alterations
|
||||
// done to fit here.
|
||||
// As a new version of Kameleon is included in OpenSpace this function may prove to be
|
||||
// redundant!
|
||||
|
||||
std::string seqStartStr;
|
||||
double seqStartDbl;
|
||||
if (kameleon->doesAttributeExist("start_time")){
|
||||
seqStartStr =
|
||||
kameleon->getGlobalAttribute("start_time").getAttributeString();
|
||||
} else if (kameleon->doesAttributeExist("tim_rundate_cal")) {
|
||||
seqStartStr =
|
||||
kameleon->getGlobalAttribute("tim_rundate_cal").getAttributeString();
|
||||
const size_t N_CHARS = seqStartStr.length();
|
||||
if (N_CHARS < 19) {
|
||||
// Fall through to add the required characters
|
||||
switch (N_CHARS) {
|
||||
case 10 : // YYYY-MM-DD => YYYY-MM-DDTHH
|
||||
seqStartStr += "T00";
|
||||
case 13 : // YYYY-MM-DDTHH => YYYY-MM-DDTHH:
|
||||
seqStartStr += ":";
|
||||
case 14 : // YYYY-MM-DDTHH: => YYYY-MM-DDTHH:MM
|
||||
seqStartStr += "00";
|
||||
case 16 : // YYYY-MM-DDTHH:MM => YYYY-MM-DDTHH:MM:
|
||||
seqStartStr += ":";
|
||||
case 17 : // YYYY-MM-DDTHH:MM: => YYYY-MM-DDTHH:MM:SS
|
||||
seqStartStr += "00";
|
||||
// case 19 : // YYYY-MM-DDTHH:MM:SS => YYYY-MM-DDTHH:MM:SS.000
|
||||
// seqStartStr += ".000";
|
||||
// case 23 : // YYYY-MM-DDTHH:MM:SS. => YYYY-MM-DDTHH:MM:SS.000Z
|
||||
// seqStartStr += "Z";
|
||||
default :
|
||||
break;
|
||||
}
|
||||
}
|
||||
// else if (seqStartStr.length() < 19 && kameleon->doesAttributeExist("tim_crstart_cal")) {
|
||||
// seqStartStr =
|
||||
// kameleon->getGlobalAttribute("tim_crstart_cal").getAttributeString();
|
||||
// }
|
||||
} else if (kameleon->doesAttributeExist("tim_obsdate_cal")) {
|
||||
seqStartStr =
|
||||
kameleon->getGlobalAttribute("tim_obsdate_cal").getAttributeString();
|
||||
} else if (kameleon->doesAttributeExist("tim_crstart_cal")) {
|
||||
seqStartStr =
|
||||
kameleon->getGlobalAttribute("tim_crstart_cal").getAttributeString();
|
||||
} else {
|
||||
LWARNING("No starting time attribute could be found in the .cdf file.\n\t" <<
|
||||
"Starting time is set to 01.JAN.2000 12:00.");
|
||||
seqStartDbl = 0.0;
|
||||
}
|
||||
|
||||
if (seqStartStr.length() == 19){
|
||||
seqStartStr += ".000Z";
|
||||
}
|
||||
|
||||
if (seqStartStr.length() == 24){
|
||||
seqStartDbl =
|
||||
Time::convertTime(
|
||||
seqStartStr.substr(0, seqStartStr.length() - 2));
|
||||
} else {
|
||||
LWARNING("No starting time attribute could be found in the .cdf file.\n\t" <<
|
||||
"Starting time is set to 01.JAN.2000 12:00.");
|
||||
seqStartDbl = 0.0;
|
||||
}
|
||||
|
||||
double stateStartOffset;
|
||||
|
||||
if (kameleon->doesAttributeExist("elapsed_time_in_seconds")) {
|
||||
stateStartOffset = static_cast<double>(
|
||||
kameleon->getGlobalAttribute(
|
||||
"elapsed_time_in_seconds").getAttributeFloat());
|
||||
} else if (kameleon->doesAttributeExist("time_physical_time")) {
|
||||
stateStartOffset = static_cast<double>(
|
||||
kameleon->getGlobalAttribute(
|
||||
"time_physical_time").getAttributeFloat());
|
||||
} else {
|
||||
stateStartOffset = 0.0;
|
||||
LWARNING("No time offset attribute could be found in the .cdf file.\n\t" <<
|
||||
"The current state starts the same time as the sequence!");
|
||||
}
|
||||
|
||||
return seqStartDbl + stateStartOffset;
|
||||
}
|
||||
|
||||
} // namespace openspace::kameleonHelper {
|
||||
Reference in New Issue
Block a user