mirror of
https://github.com/OpenSpace/OpenSpace.git
synced 2026-01-07 04:00:37 -06:00
Add functionality for reading and displaying OSFLS files during runtime
This commit is contained in:
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
Reference in New Issue
Block a user