diff --git a/modules/base/rendering/renderabletrailtrajectory.cpp b/modules/base/rendering/renderabletrailtrajectory.cpp index 8799a98c06..68120b0427 100644 --- a/modules/base/rendering/renderabletrailtrajectory.cpp +++ b/modules/base/rendering/renderabletrailtrajectory.cpp @@ -83,6 +83,14 @@ namespace { "'false', only the trail until the current time in the application will be shown" }; + constexpr openspace::properties::Property::PropertyInfo SweepChunkSizeInfo = { + "SweepChunkSize", + "Sweep Chunk Size", + "The number of vertices that will be calculated each frame whenever the trail " + "needs to be recalculated. " + "A greater value will result in more calculations per frame." + }; + struct [[codegen::Dictionary(RenderableTrailTrajectory)]] Parameters { // [[codegen::verbatim(StartTimeInfo.description)]] std::string startTime [[codegen::annotation("A valid date in ISO 8601 format")]]; @@ -98,6 +106,9 @@ namespace { // [[codegen::verbatim(RenderFullPathInfo.description)]] std::optional showFullTrail; + + // [[codegen::verbatim(SweepChunkSizeInfo.description)]] + std::optional sweepChunkSize; }; #include "renderabletrailtrajectory_codegen.cpp" } // namespace @@ -118,21 +129,23 @@ RenderableTrailTrajectory::RenderableTrailTrajectory(const ghoul::Dictionary& di , _sampleInterval(SampleIntervalInfo, 2.0, 2.0, 1e6) , _timeStampSubsamplingFactor(TimeSubSampleInfo, 1, 1, 1000000000) , _renderFullTrail(RenderFullPathInfo, false) + , _maxVertex(glm::vec3(-std::numeric_limits::max())) + , _minVertex(glm::vec3(std::numeric_limits::max())) { const Parameters p = codegen::bake(dictionary); - _translation->onParameterChange([this]() { _needsFullSweep = true; }); + _translation->onParameterChange([this]() { reset(); }); _startTime = p.startTime; - _startTime.onChange([this] { _needsFullSweep = true; }); + _startTime.onChange([this] { reset(); }); addProperty(_startTime); _endTime = p.endTime; - _endTime.onChange([this] { _needsFullSweep = true; }); + _endTime.onChange([this] { reset(); }); addProperty(_endTime); _sampleInterval = p.sampleInterval; - _sampleInterval.onChange([this] { _needsFullSweep = true; }); + _sampleInterval.onChange([this] { reset(); }); addProperty(_sampleInterval); _timeStampSubsamplingFactor = @@ -143,6 +156,8 @@ RenderableTrailTrajectory::RenderableTrailTrajectory(const ghoul::Dictionary& di _renderFullTrail = p.showFullTrail.value_or(_renderFullTrail); addProperty(_renderFullTrail); + _sweepChunkSize = p.sweepChunkSize.value_or(_sweepChunkSize); + // We store the vertices with ascending temporal order _primaryRenderInformation.sorting = RenderInformation::VertexSorting::OldestFirst; } @@ -171,32 +186,76 @@ void RenderableTrailTrajectory::deinitializeGL() { RenderableTrail::deinitializeGL(); } +void RenderableTrailTrajectory::reset() { + _needsFullSweep = true; + _sweepIteration = 0; + _maxVertex = glm::vec3(-std::numeric_limits::max()); + _minVertex = glm::vec3(std::numeric_limits::max()); +} + void RenderableTrailTrajectory::update(const UpdateData& data) { if (_needsFullSweep) { - // Convert the start and end time from string representations to J2000 seconds - _start = SpiceManager::ref().ephemerisTimeFromDate(_startTime); - _end = SpiceManager::ref().ephemerisTimeFromDate(_endTime); - const double totalSampleInterval = _sampleInterval / _timeStampSubsamplingFactor; - // How many values do we need to compute given the distance between the start and - // end date and the desired sample interval - const int nValues = static_cast((_end - _start) / totalSampleInterval); + if (_sweepIteration == 0) { + // Max number of vertices + constexpr unsigned int maxNumberOfVertices = 1000000; - // Make space for the vertices - _vertexArray.clear(); - _vertexArray.resize(nValues); + // Convert the start and end time from string representations to J2000 seconds + _start = SpiceManager::ref().ephemerisTimeFromDate(_startTime); + _end = SpiceManager::ref().ephemerisTimeFromDate(_endTime); + double timespan = _end - _start; - // ... fill all of the values - for (int i = 0; i < nValues; ++i) { + _totalSampleInterval = _sampleInterval / _timeStampSubsamplingFactor; + + // Cap _numberOfVertices in order to prevent overflow and extreme performance + // degredation/RAM usage + _numberOfVertices = std::min( + static_cast(timespan / _totalSampleInterval), + maxNumberOfVertices + ); + + // We need to recalcuate the _totalSampleInterval if _numberOfVertices eqals + // maxNumberOfVertices. If we don't do this the position for each vertex + // will not be correct for the number of vertices we are doing along the trail. + _totalSampleInterval = (_numberOfVertices == maxNumberOfVertices) ? + (timespan / _numberOfVertices) : _totalSampleInterval; + + // Make space for the vertices + _vertexArray.clear(); + _vertexArray.resize(_numberOfVertices); + } + + // Calculate sweeping range for this iteration + unsigned int startIndex = _sweepIteration * _sweepChunkSize; + unsigned int nextIndex = (_sweepIteration + 1) * _sweepChunkSize; + unsigned int stopIndex = std::min(nextIndex, _numberOfVertices); + + // Calculate all vertex positions + for (int i = startIndex; i < stopIndex; ++i) { const glm::vec3 p = _translation->position({ {}, - Time(_start + i * totalSampleInterval), + Time(_start + i * _totalSampleInterval), Time(0.0) }); _vertexArray[i] = { p.x, p.y, p.z }; - } - // ... and upload them to the GPU + // Set max and min vertex for bounding sphere calculations + _maxVertex = glm::max(_maxVertex, p); + _minVertex = glm::min(_minVertex, p); + } + ++_sweepIteration; + + if (stopIndex == _numberOfVertices) { + _sweepIteration = 0; + setBoundingSphere(glm::distance(_maxVertex, _minVertex) / 2.f); + } + else { + // Early return as we don't need to render if we are still + // doing full sweep calculations + return; + } + + // Upload vertices to the GPU glBindVertexArray(_primaryRenderInformation._vaoID); glBindBuffer(GL_ARRAY_BUFFER, _primaryRenderInformation._vBufferID); glBufferData( @@ -299,24 +358,6 @@ void RenderableTrailTrajectory::update(const UpdateData& data) { glBindVertexArray(0); - // Updating bounding sphere - glm::vec3 maxVertex(-std::numeric_limits::max()); - glm::vec3 minVertex(std::numeric_limits::max()); - - auto setMax = [&maxVertex, &minVertex](const TrailVBOLayout& vertexData) { - maxVertex.x = std::max(maxVertex.x, vertexData.x); - maxVertex.y = std::max(maxVertex.y, vertexData.y); - maxVertex.z = std::max(maxVertex.z, vertexData.z); - - minVertex.x = std::min(minVertex.x, vertexData.x); - minVertex.y = std::min(minVertex.y, vertexData.y); - minVertex.z = std::min(minVertex.z, vertexData.z); - }; - - std::for_each(_vertexArray.begin(), _vertexArray.end(), setMax); - - setBoundingSphere(glm::distance(maxVertex, minVertex) / 2.f); - } } // namespace openspace diff --git a/modules/base/rendering/renderabletrailtrajectory.h b/modules/base/rendering/renderabletrailtrajectory.h index d349e9e619..704e072dae 100644 --- a/modules/base/rendering/renderabletrailtrajectory.h +++ b/modules/base/rendering/renderabletrailtrajectory.h @@ -60,6 +60,13 @@ public: static documentation::Documentation Documentation(); private: + + /// Reset some variables to default state + void reset(); + + /// The number of vertices that we calculate during each frame of the full sweep pass + unsigned int _sweepChunkSize = 200; + /// The start time of the trail properties::StringProperty _startTime; /// The end time of the trail @@ -84,6 +91,19 @@ private: double _start = 0.0; /// The conversion of the _endTime into the internal time format double _end = 0.0; + + /// Tracks sweep iteration, is used to calculate which vertices to work on per frame + int _sweepIteration = 0; + + /// How many points do we need to compute given the distance between the + /// start and end date and the desired sample interval + unsigned int _numberOfVertices = 0; + + double _totalSampleInterval = 0.0; + + /// Max and min vertex used to calculate the bounding sphere + glm::vec3 _maxVertex; + glm::vec3 _minVertex; }; } // namespace openspace