mirror of
https://github.com/OpenSpace/OpenSpace.git
synced 2026-01-06 11:39:49 -06:00
Eliminate duplicate frames at time transitions when saving screenshot frames during playback
This commit is contained in:
@@ -95,6 +95,16 @@ bool compareTimeWithKeyframeTime(double a, const KeyframeBase& b);
|
||||
*/
|
||||
bool compareKeyframeTimeWithTime(const KeyframeBase& a, double b);
|
||||
|
||||
/**
|
||||
* Return true if the timestamp of a is smaller than or equal to b.
|
||||
* This is used only in the mode of saving render frames during session recording
|
||||
* playback. This was necessary to correct a small timing issue caused by fixing
|
||||
* the application time according to the playback framerate. In normal operation,
|
||||
* the application time at the instant the keyframes are evaluated is always a
|
||||
* little bit newer than the first keyframe in the timeline.
|
||||
*/
|
||||
bool compareKeyframeTimeWithTime_playbackWithFrames(const KeyframeBase& a, double b);
|
||||
|
||||
} // namespace openspace
|
||||
|
||||
#include "timeline.inl"
|
||||
|
||||
@@ -121,13 +121,14 @@ public:
|
||||
|
||||
private:
|
||||
void progressTime(double dt);
|
||||
void applyKeyframeData(const TimeKeyframeData& keyframe);
|
||||
void applyKeyframeData(const TimeKeyframeData& keyframe, double dt);
|
||||
TimeKeyframeData interpolate(const Keyframe<TimeKeyframeData>& past,
|
||||
const Keyframe<TimeKeyframeData>& future, double time);
|
||||
|
||||
void addDeltaTimesKeybindings();
|
||||
void clearDeltaTimesKeybindings();
|
||||
double currentApplicationTimeForInterpolation() const;
|
||||
double previousApplicationTimeForInterpolation() const;
|
||||
|
||||
Timeline<TimeKeyframeData> _timeline;
|
||||
SyncData<Time> _currentTime;
|
||||
@@ -140,6 +141,7 @@ private:
|
||||
bool _lastTimePaused = false;
|
||||
double _lastDeltaTime = 0.0;
|
||||
double _lastTargetDeltaTime = 0.0;
|
||||
double _previousApplicationTime = 0.0;
|
||||
|
||||
bool _deltaTimeStepsChanged = false;
|
||||
std::vector<double> _deltaTimeSteps;
|
||||
|
||||
@@ -38,4 +38,8 @@ bool compareKeyframeTimeWithTime(const KeyframeBase& a, double b) {
|
||||
return a.timestamp < b;
|
||||
}
|
||||
|
||||
bool compareKeyframeTimeWithTime_playbackWithFrames(const KeyframeBase& a, double b) {
|
||||
return a.timestamp <= b;
|
||||
}
|
||||
|
||||
} // namespace
|
||||
|
||||
@@ -189,6 +189,7 @@ void TimeManager::preSynchronization(double dt) {
|
||||
_lastTimePaused = _timePaused;
|
||||
_deltaTimeStepsChanged = false;
|
||||
_timelineChanged = false;
|
||||
_previousApplicationTime = currentApplicationTimeForInterpolation();
|
||||
}
|
||||
|
||||
TimeKeyframeData TimeManager::interpolate(double applicationTime) {
|
||||
@@ -266,11 +267,14 @@ void TimeManager::progressTime(double dt) {
|
||||
const double now = currentApplicationTimeForInterpolation();
|
||||
const std::deque<Keyframe<TimeKeyframeData>>& keyframes = _timeline.keyframes();
|
||||
|
||||
std::function<bool(const KeyframeBase&, double)> comparisonFunc =
|
||||
(global::sessionRecording->isPlayingBack()) ?
|
||||
&compareKeyframeTimeWithTime_playbackWithFrames : &compareKeyframeTimeWithTime;
|
||||
auto firstFutureKeyframe = std::lower_bound(
|
||||
keyframes.begin(),
|
||||
keyframes.end(),
|
||||
now,
|
||||
&compareKeyframeTimeWithTime
|
||||
comparisonFunc
|
||||
);
|
||||
|
||||
const bool hasFutureKeyframes = firstFutureKeyframe != keyframes.end();
|
||||
@@ -297,7 +301,7 @@ void TimeManager::progressTime(double dt) {
|
||||
_deltaTime = interpolated.delta;
|
||||
}
|
||||
else if (!hasConsumedLastPastKeyframe) {
|
||||
applyKeyframeData(lastPastKeyframe->data);
|
||||
applyKeyframeData(lastPastKeyframe->data, dt);
|
||||
}
|
||||
else if (!isPaused()) {
|
||||
// If there are no keyframes to consider
|
||||
@@ -353,9 +357,15 @@ TimeKeyframeData TimeManager::interpolate(const Keyframe<TimeKeyframeData>& past
|
||||
return data;
|
||||
}
|
||||
|
||||
void TimeManager::applyKeyframeData(const TimeKeyframeData& keyframeData) {
|
||||
void TimeManager::applyKeyframeData(const TimeKeyframeData& keyframeData, double dt) {
|
||||
const Time& currentTime = keyframeData.time;
|
||||
_currentTime.data().setTime(currentTime.j2000Seconds());
|
||||
_deltaTime = _timePaused ? 0.0 : _targetDeltaTime;
|
||||
if (global::sessionRecording->isPlayingBack()) {
|
||||
_currentTime.data().advanceTime(dt * _deltaTime);
|
||||
}
|
||||
else {
|
||||
_currentTime.data().setTime(currentTime.j2000Seconds());
|
||||
}
|
||||
_timePaused = keyframeData.pause;
|
||||
_targetDeltaTime = keyframeData.delta;
|
||||
_deltaTime = _timePaused ? 0.0 : _targetDeltaTime;
|
||||
@@ -714,7 +724,7 @@ void TimeManager::interpolateDeltaTime(double newDeltaTime, double interpolation
|
||||
return;
|
||||
}
|
||||
|
||||
const double now = currentApplicationTimeForInterpolation();
|
||||
double now = currentApplicationTimeForInterpolation();
|
||||
Time newTime(
|
||||
time().j2000Seconds() + (_deltaTime + newDeltaTime) * 0.5 * interpolationDuration
|
||||
);
|
||||
@@ -724,6 +734,9 @@ void TimeManager::interpolateDeltaTime(double newDeltaTime, double interpolation
|
||||
|
||||
_targetDeltaTime = newDeltaTime;
|
||||
|
||||
if (global::sessionRecording->isPlayingBack()) {
|
||||
now = previousApplicationTimeForInterpolation();
|
||||
}
|
||||
addKeyframe(now, currentKeyframe);
|
||||
addKeyframe(now + interpolationDuration, futureKeyframe);
|
||||
}
|
||||
@@ -810,7 +823,7 @@ void TimeManager::interpolatePause(bool pause, double interpolationDuration) {
|
||||
return;
|
||||
}
|
||||
|
||||
const double now = currentApplicationTimeForInterpolation();
|
||||
double now = currentApplicationTimeForInterpolation();
|
||||
double targetDelta = pause ? 0.0 : _targetDeltaTime;
|
||||
Time newTime(
|
||||
time().j2000Seconds() + (_deltaTime + targetDelta) * 0.5 * interpolationDuration
|
||||
@@ -820,6 +833,9 @@ void TimeManager::interpolatePause(bool pause, double interpolationDuration) {
|
||||
TimeKeyframeData futureKeyframe = { newTime, _targetDeltaTime, pause, false };
|
||||
_timePaused = pause;
|
||||
|
||||
if (global::sessionRecording->isPlayingBack()) {
|
||||
now = previousApplicationTimeForInterpolation();
|
||||
}
|
||||
clearKeyframes();
|
||||
if (interpolationDuration > 0) {
|
||||
addKeyframe(now, currentKeyframe);
|
||||
@@ -837,4 +853,17 @@ double TimeManager::currentApplicationTimeForInterpolation() const
|
||||
}
|
||||
}
|
||||
|
||||
double TimeManager::previousApplicationTimeForInterpolation() const
|
||||
{
|
||||
//If playing back with frames, this function needs to be called when a time rate
|
||||
// interpolation (either speed change or pause) begins and ends. If the application
|
||||
// time of the interpolation keyframe timestamp (when it was added to timeline) is
|
||||
// exactly the same as when it is evaluated, then the interpolation math fails and
|
||||
// two identical frames are generated at the begin & end. This only happens when the
|
||||
// application time is forced to discrete intervals for a fixed rendering framerate.
|
||||
// Using the previous frame render time fixes this problem. This doesn't adversely
|
||||
// affect playback without frames.
|
||||
return _previousApplicationTime;
|
||||
}
|
||||
|
||||
} // namespace openspace
|
||||
|
||||
Reference in New Issue
Block a user