Add functionality for reading and displaying OSFLS files during runtime

This commit is contained in:
Oskar Carlbaum
2017-09-21 03:36:05 +02:00
parent eea7c7306a
commit ec8c3c5ea7
2 changed files with 112 additions and 22 deletions

View File

@@ -105,8 +105,19 @@ void RenderableFieldlinesSequence::initialize() {
break;
case OSFLS:
if (_isLoadingStatesAtRuntime) {
LERROR("OSFLS LOAD AT RUNTIME NOT YET IMPLEMENTED!");
extractTriggerTimesFromFileNames();
FieldlinesState newState;
bool loadedSuccessfully = newState.loadStateFromOsfls(_sourceFiles[0]);
if (loadedSuccessfully) {
_states.push_back(newState);
_nStates = _startTimes.size();
_activeStateIndex = 0;
} else {
LERROR("The provided .osfls files seem to be corrupt!");
_sourceFileType = INVALID;
}
} else {
// Load states into RAM!
for (string filePath : _sourceFiles) {
FieldlinesState newState;
bool loadedSuccessfully = newState.loadStateFromOsfls(filePath);
@@ -122,6 +133,11 @@ void RenderableFieldlinesSequence::initialize() {
break;
}
// No need to store source paths in memory if their states are already in RAM!
if (!_isLoadingStatesAtRuntime) {
_sourceFiles.clear();
}
computeSequenceEndTime();
// HANDLE PROPERTIES
@@ -163,6 +179,11 @@ void RenderableFieldlinesSequence::deinitialize() {
renderEngine.removeRenderProgram(_shaderProgram);
_shaderProgram = nullptr;
}
// Stall main thread until thread that's loading states is done!
while (/*!_newStateIsReady &&*/ _isLoadingStateFromDisk) {
LWARNING("TRYING TO DESTROY CLASS WHEN A THREAD USING IT IS STILL ACTIVE");
}
}
bool RenderableFieldlinesSequence::isReady() const {
@@ -170,7 +191,7 @@ bool RenderableFieldlinesSequence::isReady() const {
}
void RenderableFieldlinesSequence::render(const RenderData& data, RendererTasks&) {
if (_activeStateIndex != -1) {
if (_activeTriggerTimeIndex != -1) {
_shaderProgram->activate();
const glm::dmat4 ROT_MAT = glm::dmat4(data.modelTransform.rotation);
@@ -216,21 +237,44 @@ void RenderableFieldlinesSequence::update(const UpdateData& data) {
const double CURRENT_TIME = data.time.j2000Seconds();
// Check if current time in OpenSpace is within sequence interval
if (isWithinSequenceInterval(CURRENT_TIME)) {
const int NEXT_IDX = _activeStateIndex + 1;
if (_activeStateIndex < 0 // true => Previous frame was not within the sequence interval
|| CURRENT_TIME < _startTimes[_activeStateIndex] // true => OpenSpace has stepped back to a time represented by another state
const int NEXT_IDX = _activeTriggerTimeIndex + 1;
if (_activeTriggerTimeIndex < 0 // true => Previous frame was not within the sequence interval
|| CURRENT_TIME < _startTimes[_activeTriggerTimeIndex] // true => OpenSpace has stepped back to a time represented by another state
|| (NEXT_IDX < _nStates && CURRENT_TIME >= _startTimes[NEXT_IDX])) { // true => OpenSpace has stepped forward to a time represented by another state
updateActiveStateIndex(CURRENT_TIME);
_needsUpdate = true;
updateActiveTriggerTimeIndex(CURRENT_TIME);
if (_isLoadingStatesAtRuntime) {
_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
_activeStateIndex = -1;
_needsUpdate = false;
_activeTriggerTimeIndex = -1;
_mustLoadNewStateFromDisk = false;
_needsUpdate = false;
}
if (_needsUpdate) {
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 (_isLoadingStatesAtRuntime) {
_states[0] = std::move(_newState);
}
glBindVertexArray(_vertexArrayObject);
glBindBuffer(GL_ARRAY_BUFFER, _vertexPositionBuffer);
@@ -249,6 +293,7 @@ void RenderableFieldlinesSequence::update(const UpdateData& data) {
// Everything is set and ready for rendering!
_needsUpdate = false;
_newStateIsReady = false;
}
}
}
@@ -258,19 +303,30 @@ inline bool RenderableFieldlinesSequence::isWithinSequenceInterval(const double
}
// Assumes we already know that CURRENT_TIME is within the sequence interval
void RenderableFieldlinesSequence::updateActiveStateIndex(const double CURRENT_TIME) {
void RenderableFieldlinesSequence::updateActiveTriggerTimeIndex(const double CURRENT_TIME) {
auto iter = std::upper_bound(_startTimes.begin(), _startTimes.end(), CURRENT_TIME);
if (iter != _startTimes.end()) {
if ( iter != _startTimes.begin()) {
_activeStateIndex = std::distance(_startTimes.begin(), iter) - 1;
_activeTriggerTimeIndex = std::distance(_startTimes.begin(), iter) - 1;
} else {
_activeStateIndex = 0;
_activeTriggerTimeIndex = 0;
}
} else {
_activeStateIndex = static_cast<int>(_nStates) - 1;
_activeTriggerTimeIndex = static_cast<int>(_nStates) - 1;
}
}
// Reading state from disk. Thread safe!
void RenderableFieldlinesSequence::readNewState(const std::string& FILEPATH) {
FieldlinesState newState;
bool isSuccessful = newState.loadStateFromOsfls(FILEPATH);
_newState = std::move(newState);
_newStateIsReady = true;
_isLoadingStateFromDisk = false;
}
bool RenderableFieldlinesSequence::extractInfoFromDictionary(
const ghoul::Dictionary& dictionary) {
@@ -347,7 +403,7 @@ bool RenderableFieldlinesSequence::extractInfoFromDictionary(
_isLoadingStatesAtRuntime = shouldLoadInRealtime;
} else {
LWARNING(name << KEY_OSLFS_LOAD_AT_RUNTIME <<
" isn't specified! Fieldline states will be stored in RAM");
" isn't specified! OSFLS files will be loaded during runtime!");
}
} break;
default:
@@ -368,4 +424,26 @@ void RenderableFieldlinesSequence::computeSequenceEndTime() {
}
}
// Extract J2000 time from file names
// Requires files to be named as such: 'YYYY-MM-DDTHH-MM-SS-XXX.osfls'
void RenderableFieldlinesSequence::extractTriggerTimesFromFileNames() {
const size_t FILENAME_SIZE = 23; // number of characters in filename (excluding '.osfls')
const size_t EXT_SIZE = 6; // size(".osfls")
for (const std::string& FILEPATH : _sourceFiles) {
const size_t STR_LENGTH = FILEPATH.size();
// Extract the filename from the path (without extension)
std::string timeString = FILEPATH.substr(STR_LENGTH - FILENAME_SIZE - EXT_SIZE,
FILENAME_SIZE - 1);
// Ensure the separators are correct
timeString.replace( 4, 1, "-");
timeString.replace( 7, 1, "-");
timeString.replace(13, 1, ":");
timeString.replace(16, 1, ":");
timeString.replace(19, 1, ".");
const double TRIGGER_TIME = Time::convertTime(timeString);
_startTimes.push_back(TRIGGER_TIME);
}
}
} // namespace openspace

View File

@@ -32,6 +32,8 @@
#include <modules/fieldlinessequence/util/fieldlinesstate.h>
#include <atomic>
namespace openspace {
class RenderableFieldlinesSequence : public Renderable {
@@ -54,12 +56,19 @@ private:
INVALID
};
int _activeStateIndex = -1;
bool _needsUpdate = false; // If still in same state as previous frame == false
bool _isLoadingStatesAtRuntime = false; // False => loading osfls at runtime
size_t _nStates = 0;
double _sequenceEndTime;
SourceFileType _sourceFileType;
int _activeStateIndex = -1;
int _activeTriggerTimeIndex = -1;
bool _isLoadingStatesAtRuntime = true; // False => loading osfls at runtime
bool _mustLoadNewStateFromDisk = false; // If still in same state as previous frame == false
bool _needsUpdate = false; // If still in same state as previous frame == false
FieldlinesState _newState;
size_t _nStates = 0;
double _sequenceEndTime;
SourceFileType _sourceFileType;
std::atomic<bool> _isLoadingStateFromDisk{false};
std::atomic<bool> _newStateIsReady{false};
std::unique_ptr<ghoul::opengl::ProgramObject> _shaderProgram;
@@ -88,8 +97,11 @@ private:
void computeSequenceEndTime();
bool extractInfoFromDictionary(const ghoul::Dictionary& dictionary);
void extractTriggerTimesFromFileNames();
void readNewState(const std::string& FILEPATH);
inline bool isWithinSequenceInterval(const double CURRENT_TIME);
inline void updateActiveStateIndex(const double CURRENT_TIME);
inline void updateActiveTriggerTimeIndex(const double CURRENT_TIME);
};
} // namespace openspace