Eliminate duplicate frames at time transitions when saving screenshot frames during playback

This commit is contained in:
GPayne
2021-07-19 23:33:10 -06:00
parent e866a7da9e
commit 892adf62bf
4 changed files with 52 additions and 7 deletions

View File

@@ -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"

View File

@@ -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;

View File

@@ -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

View File

@@ -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