mirror of
https://github.com/OpenSpace/OpenSpace.git
synced 2026-01-07 04:00:37 -06:00
# Conflicts: # include/openspace/network/messagestructures.h # src/interaction/sessionrecording.cpp
722 lines
23 KiB
C++
722 lines
23 KiB
C++
/*****************************************************************************************
|
|
* *
|
|
* OpenSpace *
|
|
* *
|
|
* Copyright (c) 2014-2020 *
|
|
* *
|
|
* Permission is hereby granted, free of charge, to any person obtaining a copy of this *
|
|
* software and associated documentation files (the "Software"), to deal in the Software *
|
|
* without restriction, including without limitation the rights to use, copy, modify, *
|
|
* merge, publish, distribute, sublicense, and/or sell copies of the Software, and to *
|
|
* permit persons to whom the Software is furnished to do so, subject to the following *
|
|
* conditions: *
|
|
* *
|
|
* The above copyright notice and this permission notice shall be included in all copies *
|
|
* or substantial portions of the Software. *
|
|
* *
|
|
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, *
|
|
* INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A *
|
|
* PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT *
|
|
* HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF *
|
|
* CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE *
|
|
* OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. *
|
|
****************************************************************************************/
|
|
|
|
#include <openspace/util/timemanager.h>
|
|
|
|
#include <openspace/engine/globals.h>
|
|
#include <openspace/engine/windowdelegate.h>
|
|
#include <openspace/network/parallelpeer.h>
|
|
#include <openspace/util/timeline.h>
|
|
#include <ghoul/logging/logmanager.h>
|
|
#include <ghoul/misc/profiling.h>
|
|
|
|
namespace {
|
|
// Properties for time interpolation
|
|
// These are used when setting the time from lua time interpolation functions,
|
|
// when called without arguments.
|
|
|
|
constexpr openspace::properties::Property::PropertyInfo
|
|
DefaultTimeInterpolationDurationInfo = {
|
|
"DefaultTimeInterpolationDuration",
|
|
"Default Time Interpolation Duration",
|
|
"The default duration taken to interpolate between times"
|
|
};
|
|
|
|
constexpr openspace::properties::Property::PropertyInfo
|
|
DefaultDeltaTimeInterpolationDurationInfo = {
|
|
"DefaultDeltaTimeInterpolationDuration",
|
|
"Default Delta Time Interpolation Duration",
|
|
"The default duration taken to interpolate between delta times"
|
|
};
|
|
|
|
constexpr openspace::properties::Property::PropertyInfo
|
|
DefaultPauseInterpolationDurationInfo = {
|
|
"DefaultPauseInterpolationDuration",
|
|
"Default Pause Interpolation Duration",
|
|
"The default duration taken to transition to the paused state, "
|
|
"when interpolating"
|
|
};
|
|
|
|
constexpr openspace::properties::Property::PropertyInfo
|
|
DefaultUnpauseInterpolationDurationInfo = {
|
|
"DefaultUnpauseInterpolationDuration",
|
|
"Default Unpause Interpolation Duration",
|
|
"The default duration taken to transition to the unpaused state, "
|
|
"when interpolating"
|
|
};
|
|
}
|
|
|
|
namespace openspace {
|
|
|
|
using datamessagestructures::TimeKeyframe;
|
|
|
|
TimeManager::TimeManager()
|
|
: properties::PropertyOwner({ "TimeManager" })
|
|
, _defaultTimeInterpolationDuration(DefaultTimeInterpolationDurationInfo,
|
|
2.f,
|
|
0.f,
|
|
5.f
|
|
)
|
|
, _defaultDeltaTimeInterpolationDuration(
|
|
DefaultDeltaTimeInterpolationDurationInfo,
|
|
1.f,
|
|
0.f,
|
|
5.f
|
|
)
|
|
, _defaultPauseInterpolationDuration(
|
|
DefaultPauseInterpolationDurationInfo,
|
|
0.5f,
|
|
0.f,
|
|
5.f
|
|
)
|
|
, _defaultUnpauseInterpolationDuration(
|
|
DefaultUnpauseInterpolationDurationInfo,
|
|
1.f,
|
|
0.f,
|
|
5.f
|
|
)
|
|
{
|
|
addProperty(_defaultTimeInterpolationDuration);
|
|
addProperty(_defaultDeltaTimeInterpolationDuration);
|
|
addProperty(_defaultPauseInterpolationDuration);
|
|
addProperty(_defaultUnpauseInterpolationDuration);
|
|
}
|
|
|
|
void TimeManager::interpolateTime(double targetTime, double durationSeconds) {
|
|
ghoul_precondition(durationSeconds > 0.f, "durationSeconds must be positive");
|
|
|
|
const double now = global::windowDelegate.applicationTime();
|
|
const bool pause = isPaused();
|
|
|
|
const TimeKeyframeData current = { time(), deltaTime(), false, false };
|
|
const TimeKeyframeData next = { Time(targetTime), targetDeltaTime(), pause, false };
|
|
|
|
clearKeyframes();
|
|
addKeyframe(now, current);
|
|
addKeyframe(now + durationSeconds, next);
|
|
}
|
|
|
|
void TimeManager::interpolateTimeRelative(double delta, double durationSeconds) {
|
|
ghoul_precondition(durationSeconds > 0.f, "durationSeconds must be positive");
|
|
|
|
const float duration = global::timeManager.defaultTimeInterpolationDuration();
|
|
|
|
const TimeKeyframeData predictedTime = interpolate(
|
|
global::windowDelegate.applicationTime() + duration
|
|
);
|
|
const double targetTime = predictedTime.time.j2000Seconds() + delta;
|
|
interpolateTime(targetTime, durationSeconds);
|
|
}
|
|
|
|
void TimeManager::preSynchronization(double dt) {
|
|
ZoneScoped
|
|
|
|
removeKeyframesBefore(_latestConsumedTimestamp);
|
|
progressTime(dt);
|
|
|
|
// Notify observers about time changes if any.
|
|
const double newTime = time().j2000Seconds();
|
|
|
|
if (newTime != _lastTime) {
|
|
ZoneScopedN("newTime != _lastTime")
|
|
using K = const CallbackHandle;
|
|
using V = TimeChangeCallback;
|
|
for (const std::pair<K, V>& it : _timeChangeCallbacks) {
|
|
ZoneScopedN("tcc")
|
|
it.second();
|
|
}
|
|
}
|
|
if (_deltaTime != _lastDeltaTime ||
|
|
_timePaused != _lastTimePaused ||
|
|
_targetDeltaTime != _lastTargetDeltaTime)
|
|
{
|
|
ZoneScopedN("delta time changed")
|
|
using K = const CallbackHandle;
|
|
using V = TimeChangeCallback;
|
|
for (const std::pair<K, V>& it : _deltaTimeChangeCallbacks) {
|
|
ZoneScopedN("dtcc")
|
|
it.second();
|
|
}
|
|
}
|
|
if (_deltaTimeStepsChanged) {
|
|
using K = const CallbackHandle;
|
|
using V = TimeChangeCallback;
|
|
for (const std::pair<K, V>& it : _deltaTimeStepsChangeCallbacks) {
|
|
it.second();
|
|
}
|
|
}
|
|
if (_timelineChanged) {
|
|
ZoneScopedN("timeline changed")
|
|
using K = const CallbackHandle;
|
|
using V = TimeChangeCallback;
|
|
for (const std::pair<K, V>& it : _timelineChangeCallbacks) {
|
|
ZoneScopedN("tlcc")
|
|
it.second();
|
|
}
|
|
}
|
|
|
|
_lastTime = newTime;
|
|
_lastDeltaTime = _deltaTime;
|
|
_lastTargetDeltaTime = _targetDeltaTime;
|
|
_lastTimePaused = _timePaused;
|
|
_deltaTimeStepsChanged = false;
|
|
_timelineChanged = false;
|
|
}
|
|
|
|
TimeKeyframeData TimeManager::interpolate(double applicationTime) {
|
|
const std::deque<Keyframe<TimeKeyframeData>>& keyframes = _timeline.keyframes();
|
|
|
|
auto firstFutureKeyframe = std::lower_bound(
|
|
keyframes.begin(),
|
|
keyframes.end(),
|
|
applicationTime,
|
|
&compareKeyframeTimeWithTime
|
|
);
|
|
|
|
const bool hasFutureKeyframes = firstFutureKeyframe != keyframes.end();
|
|
const bool hasPastKeyframes = firstFutureKeyframe != keyframes.begin();
|
|
|
|
const auto lastPastKeyframe = hasPastKeyframes ?
|
|
(firstFutureKeyframe - 1) :
|
|
keyframes.end();
|
|
|
|
if (hasPastKeyframes && hasFutureKeyframes && !firstFutureKeyframe->data.jump) {
|
|
return interpolate(
|
|
*lastPastKeyframe,
|
|
*firstFutureKeyframe,
|
|
applicationTime
|
|
);
|
|
}
|
|
else if (hasPastKeyframes) {
|
|
// Extrapolate based on last past keyframe
|
|
const double deltaApplicationTime = applicationTime - lastPastKeyframe->timestamp;
|
|
Time predictedTime(
|
|
lastPastKeyframe->data.time.j2000Seconds() +
|
|
deltaApplicationTime *
|
|
(lastPastKeyframe->data.pause ? 0.0 : lastPastKeyframe->data.delta)
|
|
);
|
|
return TimeKeyframeData {
|
|
predictedTime,
|
|
lastPastKeyframe->data.delta,
|
|
false,
|
|
false
|
|
};
|
|
}
|
|
// As the last option, fall back on the current time.
|
|
return TimeKeyframeData { _currentTime, _targetDeltaTime, _timePaused, false };
|
|
}
|
|
|
|
void TimeManager::progressTime(double dt) {
|
|
ZoneScoped
|
|
|
|
_integrateFromTime = static_cast<Time&>(_currentTime);
|
|
// Frames | 1 2 |
|
|
// |------------------------------------|
|
|
// Keyframes | a b c d e |
|
|
|
|
// 1: Previous frame
|
|
// 2: Current frame (now)
|
|
// lastPastKeyframe: c
|
|
// firstFutureKeyframe: d
|
|
// _latestConsumedTimestamp: a.timestamp
|
|
|
|
if (_shouldSetTime) {
|
|
// Setting the time using `setTimeNextFrame`
|
|
// will override any timeline operations.
|
|
_currentTime.data().setTime(_timeNextFrame.j2000Seconds());
|
|
_integrateFromTime.data().setTime(_timeNextFrame.j2000Seconds());
|
|
_shouldSetTime = false;
|
|
|
|
using K = const CallbackHandle;
|
|
using V = TimeChangeCallback;
|
|
for (const std::pair<K, V>& it : _timeJumpCallbacks) {
|
|
it.second();
|
|
}
|
|
return;
|
|
}
|
|
|
|
const double now = global::windowDelegate.applicationTime();
|
|
const std::deque<Keyframe<TimeKeyframeData>>& keyframes = _timeline.keyframes();
|
|
|
|
auto firstFutureKeyframe = std::lower_bound(
|
|
keyframes.begin(),
|
|
keyframes.end(),
|
|
now,
|
|
&compareKeyframeTimeWithTime
|
|
);
|
|
|
|
const bool hasFutureKeyframes = firstFutureKeyframe != keyframes.end();
|
|
const bool hasPastKeyframes = firstFutureKeyframe != keyframes.begin();
|
|
|
|
const auto lastPastKeyframe = hasPastKeyframes ?
|
|
(firstFutureKeyframe - 1) :
|
|
keyframes.end();
|
|
|
|
const bool hasConsumedLastPastKeyframe = hasPastKeyframes ?
|
|
(lastPastKeyframe->timestamp <= _latestConsumedTimestamp) :
|
|
true;
|
|
|
|
if (hasFutureKeyframes && hasPastKeyframes && !firstFutureKeyframe->data.jump) {
|
|
// If keyframes exist before and after this frame,
|
|
// interpolate between those.
|
|
TimeKeyframeData interpolated = interpolate(
|
|
*lastPastKeyframe,
|
|
*firstFutureKeyframe,
|
|
now
|
|
);
|
|
|
|
_currentTime.data().setTime(interpolated.time.j2000Seconds());
|
|
_deltaTime = interpolated.delta;
|
|
}
|
|
else if (!hasConsumedLastPastKeyframe) {
|
|
applyKeyframeData(lastPastKeyframe->data);
|
|
}
|
|
else if (!isPaused()) {
|
|
// If there are no keyframes to consider
|
|
// and time is not paused, just advance time.
|
|
_deltaTime = _targetDeltaTime;
|
|
_currentTime.data().advanceTime(dt * _deltaTime);
|
|
}
|
|
|
|
if (hasPastKeyframes) {
|
|
_latestConsumedTimestamp = lastPastKeyframe->timestamp;
|
|
}
|
|
}
|
|
|
|
TimeKeyframeData TimeManager::interpolate(const Keyframe<TimeKeyframeData>& past,
|
|
const Keyframe<TimeKeyframeData>& future,
|
|
double appTime)
|
|
{
|
|
// https://en.wikipedia.org/wiki/Spline_interpolation
|
|
// interpolatedTime = (1 - t)y1 + t*y2 + t(1 - t)(a(1 - t) + bt), where
|
|
// a = k1 * deltaAppTime - deltaSimTime,
|
|
// b = -k2 * deltaAppTime + deltaSimTime,
|
|
// with y1 = pastTime, y2 = futureTime, k1 = past dt, k2 = future dt.
|
|
|
|
const double pastSimTime = past.data.time.j2000Seconds();
|
|
const double futureSimTime = future.data.time.j2000Seconds();
|
|
const double pastDerivative = past.data.pause ? 0.0 : past.data.delta;
|
|
const double futureDerivative = future.data.pause ? 0.0 : future.data.delta;
|
|
|
|
const double deltaAppTime = future.timestamp - past.timestamp;
|
|
const double deltaSimTime = futureSimTime - pastSimTime;
|
|
|
|
const double t = (appTime - past.timestamp) / deltaAppTime;
|
|
const double a = pastDerivative * deltaAppTime - deltaSimTime;
|
|
const double b = -futureDerivative * deltaAppTime + deltaSimTime;
|
|
|
|
const double interpolatedTime =
|
|
(1 - t)*pastSimTime + t * futureSimTime + t * (1 - t)*(a*(1 - t) + b * t);
|
|
|
|
// Derivative of interpolated time.
|
|
// Division by deltaAppTime to get appTime derivative
|
|
// as opposed to t (in [0, 1]) derivative.
|
|
const double interpolatedDeltaTime =
|
|
(3 * a*t*t - 4 * a*t + a - 3 * b*t*t + 2 * b*t - pastSimTime + futureSimTime) /
|
|
deltaAppTime;
|
|
|
|
TimeKeyframeData data {
|
|
Time(interpolatedTime),
|
|
interpolatedDeltaTime,
|
|
past.data.pause,
|
|
past.data.jump
|
|
};
|
|
|
|
return data;
|
|
}
|
|
|
|
void TimeManager::applyKeyframeData(const TimeKeyframeData& keyframeData) {
|
|
const Time& currentTime = keyframeData.time;
|
|
_currentTime.data().setTime(currentTime.j2000Seconds());
|
|
_timePaused = keyframeData.pause;
|
|
_targetDeltaTime = keyframeData.delta;
|
|
_deltaTime = _timePaused ? 0.0 : _targetDeltaTime;
|
|
}
|
|
|
|
void TimeManager::addKeyframe(double timestamp, TimeKeyframeData time) {
|
|
_timeline.addKeyframe(timestamp, std::move(time));
|
|
_timelineChanged = true;
|
|
}
|
|
|
|
void TimeManager::removeKeyframesAfter(double timestamp, bool inclusive) {
|
|
size_t nKeyframes = _timeline.nKeyframes();
|
|
_timeline.removeKeyframesAfter(timestamp, inclusive);
|
|
if (nKeyframes != _timeline.nKeyframes()) {
|
|
_timelineChanged = true;
|
|
}
|
|
}
|
|
|
|
void TimeManager::removeKeyframesBefore(double timestamp, bool inclusive) {
|
|
size_t nKeyframes = _timeline.nKeyframes();
|
|
_timeline.removeKeyframesBefore(timestamp, inclusive);
|
|
if (nKeyframes != _timeline.nKeyframes()) {
|
|
_timelineChanged = true;
|
|
}
|
|
}
|
|
|
|
void TimeManager::clearKeyframes() {
|
|
size_t nKeyframes = _timeline.nKeyframes();
|
|
_timeline.clearKeyframes();
|
|
if (nKeyframes != _timeline.nKeyframes()) {
|
|
_timelineChanged = true;
|
|
}
|
|
}
|
|
|
|
void TimeManager::setTimeNextFrame(Time t) {
|
|
_shouldSetTime = true;
|
|
_timeNextFrame = std::move(t);
|
|
clearKeyframes();
|
|
}
|
|
|
|
void TimeManager::setDeltaTime(double deltaTime) {
|
|
interpolateDeltaTime(deltaTime, 0.0);
|
|
}
|
|
|
|
void TimeManager::setDeltaTimeSteps(std::vector<double> deltaTimes) {
|
|
std::vector<double> negatives;
|
|
negatives.reserve(deltaTimes.size());
|
|
std::transform(
|
|
deltaTimes.begin(),
|
|
deltaTimes.end(),
|
|
std::back_inserter(negatives),
|
|
[](double d) { return -d; }
|
|
);
|
|
|
|
deltaTimes.reserve(2 * deltaTimes.size());
|
|
deltaTimes.insert(deltaTimes.end(), negatives.begin(), negatives.end());
|
|
|
|
std::sort(deltaTimes.begin(), deltaTimes.end());
|
|
deltaTimes.erase(std::unique(deltaTimes.begin(), deltaTimes.end()), deltaTimes.end());
|
|
|
|
_deltaTimeSteps = std::move(deltaTimes);
|
|
_deltaTimeStepsChanged = true;
|
|
}
|
|
|
|
size_t TimeManager::nKeyframes() const {
|
|
return _timeline.nKeyframes();
|
|
}
|
|
|
|
const Time& TimeManager::time() const {
|
|
return _currentTime;
|
|
}
|
|
|
|
const Time& TimeManager::integrateFromTime() const {
|
|
return _integrateFromTime;
|
|
}
|
|
|
|
const Timeline<TimeKeyframeData>& TimeManager::timeline() const {
|
|
return _timeline;
|
|
}
|
|
|
|
std::vector<Syncable*> TimeManager::getSyncables() {
|
|
return { &_currentTime, &_integrateFromTime };
|
|
}
|
|
|
|
TimeManager::CallbackHandle TimeManager::addTimeChangeCallback(TimeChangeCallback cb) {
|
|
CallbackHandle handle = _nextCallbackHandle++;
|
|
_timeChangeCallbacks.emplace_back(handle, std::move(cb));
|
|
return handle;
|
|
}
|
|
|
|
TimeManager::CallbackHandle TimeManager::addDeltaTimeChangeCallback(TimeChangeCallback cb)
|
|
{
|
|
CallbackHandle handle = _nextCallbackHandle++;
|
|
_deltaTimeChangeCallbacks.emplace_back(handle, std::move(cb));
|
|
return handle;
|
|
}
|
|
|
|
TimeManager::CallbackHandle
|
|
TimeManager::addDeltaTimeStepsChangeCallback(TimeChangeCallback cb)
|
|
{
|
|
CallbackHandle handle = _nextCallbackHandle++;
|
|
_deltaTimeStepsChangeCallbacks.emplace_back(handle, std::move(cb));
|
|
return handle;
|
|
}
|
|
|
|
TimeManager::CallbackHandle TimeManager::addTimeJumpCallback(TimeChangeCallback cb) {
|
|
CallbackHandle handle = _nextCallbackHandle++;
|
|
_timeJumpCallbacks.emplace_back(handle, std::move(cb));
|
|
return handle;
|
|
}
|
|
|
|
TimeManager::CallbackHandle TimeManager::addTimelineChangeCallback(TimeChangeCallback cb)
|
|
{
|
|
CallbackHandle handle = _nextCallbackHandle++;
|
|
_timelineChangeCallbacks.push_back({ handle, std::move(cb) });
|
|
return handle;
|
|
}
|
|
|
|
void TimeManager::removeTimeChangeCallback(CallbackHandle handle) {
|
|
const auto it = std::find_if(
|
|
_timeChangeCallbacks.begin(),
|
|
_timeChangeCallbacks.end(),
|
|
[handle](const std::pair<CallbackHandle, TimeChangeCallback>& cb) {
|
|
return cb.first == handle;
|
|
}
|
|
);
|
|
|
|
ghoul_assert(
|
|
it != _timeChangeCallbacks.end(),
|
|
"handle must be a valid callback handle"
|
|
);
|
|
|
|
_timeChangeCallbacks.erase(it);
|
|
}
|
|
|
|
void TimeManager::removeDeltaTimeChangeCallback(CallbackHandle handle) {
|
|
const auto it = std::find_if(
|
|
_deltaTimeChangeCallbacks.begin(),
|
|
_deltaTimeChangeCallbacks.end(),
|
|
[handle](const std::pair<CallbackHandle, std::function<void()>>& cb) {
|
|
return cb.first == handle;
|
|
}
|
|
);
|
|
|
|
ghoul_assert(
|
|
it != _deltaTimeChangeCallbacks.end(),
|
|
"handle must be a valid callback handle"
|
|
);
|
|
|
|
_deltaTimeChangeCallbacks.erase(it);
|
|
}
|
|
|
|
void TimeManager::removeDeltaTimeStepsChangeCallback(CallbackHandle handle) {
|
|
const auto it = std::find_if(
|
|
_deltaTimeStepsChangeCallbacks.begin(),
|
|
_deltaTimeStepsChangeCallbacks.end(),
|
|
[handle](const std::pair<CallbackHandle, std::function<void()>>& cb) {
|
|
return cb.first == handle;
|
|
}
|
|
);
|
|
|
|
ghoul_assert(
|
|
it != _deltaTimeStepsChangeCallbacks.end(),
|
|
"handle must be a valid callback handle"
|
|
);
|
|
|
|
_deltaTimeStepsChangeCallbacks.erase(it);
|
|
}
|
|
|
|
void TimeManager::triggerPlaybackStart() {
|
|
_playbackModeEnabled = true;
|
|
}
|
|
|
|
void TimeManager::stopPlayback() {
|
|
_playbackModeEnabled = false;
|
|
}
|
|
|
|
void TimeManager::removeTimeJumpCallback(CallbackHandle handle) {
|
|
const auto it = std::find_if(
|
|
_timeJumpCallbacks.begin(),
|
|
_timeJumpCallbacks.end(),
|
|
[handle](const std::pair<CallbackHandle, std::function<void()>>& cb) {
|
|
return cb.first == handle;
|
|
}
|
|
);
|
|
|
|
ghoul_assert(
|
|
it != _timeJumpCallbacks.end(),
|
|
"handle must be a valid callback handle"
|
|
);
|
|
|
|
_timeJumpCallbacks.erase(it);
|
|
}
|
|
|
|
void TimeManager::removeTimelineChangeCallback(CallbackHandle handle) {
|
|
const auto it = std::find_if(
|
|
_timelineChangeCallbacks.begin(),
|
|
_timelineChangeCallbacks.end(),
|
|
[handle](const std::pair<CallbackHandle, std::function<void()>>& cb) {
|
|
return cb.first == handle;
|
|
}
|
|
);
|
|
|
|
ghoul_assert(
|
|
it != _timelineChangeCallbacks.end(),
|
|
"handle must be a valid callback handle"
|
|
);
|
|
|
|
_timelineChangeCallbacks.erase(it);
|
|
}
|
|
|
|
bool TimeManager::isPaused() const {
|
|
return _timePaused;
|
|
}
|
|
|
|
float TimeManager::defaultTimeInterpolationDuration() const {
|
|
return _defaultTimeInterpolationDuration;
|
|
}
|
|
|
|
float TimeManager::defaultDeltaTimeInterpolationDuration() const {
|
|
return _defaultDeltaTimeInterpolationDuration;
|
|
}
|
|
|
|
float TimeManager::defaultPauseInterpolationDuration() const {
|
|
return _defaultPauseInterpolationDuration;
|
|
}
|
|
|
|
float TimeManager::defaultUnpauseInterpolationDuration() const {
|
|
return _defaultUnpauseInterpolationDuration;
|
|
}
|
|
|
|
double TimeManager::deltaTime() const {
|
|
return _deltaTime;
|
|
}
|
|
|
|
double TimeManager::targetDeltaTime() const {
|
|
return _targetDeltaTime;
|
|
}
|
|
|
|
std::vector<double> TimeManager::deltaTimeSteps() const {
|
|
return _deltaTimeSteps;
|
|
}
|
|
|
|
void TimeManager::interpolateDeltaTime(double newDeltaTime, double interpolationDuration)
|
|
{
|
|
if (newDeltaTime == _targetDeltaTime) {
|
|
return;
|
|
}
|
|
|
|
clearKeyframes();
|
|
if (_timePaused || interpolationDuration <= 0.0) {
|
|
_targetDeltaTime = newDeltaTime;
|
|
_deltaTime = _timePaused ? 0.0 : _targetDeltaTime;
|
|
return;
|
|
}
|
|
|
|
const double now = global::windowDelegate.applicationTime();
|
|
Time newTime(
|
|
time().j2000Seconds() + (_deltaTime + newDeltaTime) * 0.5 * interpolationDuration
|
|
);
|
|
|
|
TimeKeyframeData currentKeyframe = { time(), _deltaTime, false, false };
|
|
TimeKeyframeData futureKeyframe = { newTime, newDeltaTime, false, false };
|
|
|
|
_targetDeltaTime = newDeltaTime;
|
|
|
|
addKeyframe(now, currentKeyframe);
|
|
addKeyframe(now + interpolationDuration, futureKeyframe);
|
|
}
|
|
|
|
std::optional<double> TimeManager::nextDeltaTimeStep() {
|
|
if (!hasNextDeltaTimeStep()) {
|
|
return std::nullopt;
|
|
}
|
|
std::vector<double>::iterator nextStepIterator = std::upper_bound(
|
|
_deltaTimeSteps.begin(),
|
|
_deltaTimeSteps.end(),
|
|
_targetDeltaTime
|
|
);
|
|
|
|
if (nextStepIterator == _deltaTimeSteps.end()) {
|
|
return std::nullopt; // should not get here
|
|
}
|
|
|
|
return *nextStepIterator;
|
|
}
|
|
|
|
std::optional<double> TimeManager::previousDeltaTimeStep() {
|
|
if (!hasPreviousDeltaTimeStep()) {
|
|
return std::nullopt;
|
|
}
|
|
std::vector<double>::iterator lowerBoundIterator = std::lower_bound(
|
|
_deltaTimeSteps.begin(),
|
|
_deltaTimeSteps.end(),
|
|
_targetDeltaTime
|
|
);
|
|
|
|
if (lowerBoundIterator == _deltaTimeSteps.begin()) {
|
|
return std::nullopt; // should not get here
|
|
}
|
|
|
|
std::vector<double>::iterator prevStepIterator = lowerBoundIterator - 1;
|
|
return *prevStepIterator;
|
|
}
|
|
|
|
bool TimeManager::hasNextDeltaTimeStep() const {
|
|
if (_deltaTimeSteps.empty())
|
|
return false;
|
|
|
|
return _targetDeltaTime < _deltaTimeSteps.back();
|
|
}
|
|
|
|
bool TimeManager::hasPreviousDeltaTimeStep() const {
|
|
if (_deltaTimeSteps.empty())
|
|
return false;
|
|
|
|
return _targetDeltaTime > _deltaTimeSteps.front();
|
|
}
|
|
|
|
void TimeManager::setNextDeltaTimeStep() {
|
|
interpolateNextDeltaTimeStep(0);
|
|
}
|
|
|
|
void TimeManager::setPreviousDeltaTimeStep() {
|
|
interpolatePreviousDeltaTimeStep(0);
|
|
}
|
|
|
|
void TimeManager::interpolateNextDeltaTimeStep(double durationSeconds) {
|
|
if (!hasNextDeltaTimeStep())
|
|
return;
|
|
|
|
double nextDeltaTime = nextDeltaTimeStep().value();
|
|
interpolateDeltaTime(nextDeltaTime, durationSeconds);
|
|
}
|
|
|
|
void TimeManager::interpolatePreviousDeltaTimeStep(double durationSeconds) {
|
|
if (!hasPreviousDeltaTimeStep())
|
|
return;
|
|
|
|
double previousDeltaTime = previousDeltaTimeStep().value();
|
|
interpolateDeltaTime(previousDeltaTime, durationSeconds);
|
|
}
|
|
|
|
void TimeManager::setPause(bool pause) {
|
|
interpolatePause(pause, 0);
|
|
}
|
|
|
|
void TimeManager::interpolatePause(bool pause, double interpolationDuration) {
|
|
if (pause == _timePaused) {
|
|
return;
|
|
}
|
|
|
|
const double now = global::windowDelegate.applicationTime();
|
|
double targetDelta = pause ? 0.0 : _targetDeltaTime;
|
|
Time newTime(
|
|
time().j2000Seconds() + (_deltaTime + targetDelta) * 0.5 * interpolationDuration
|
|
);
|
|
|
|
TimeKeyframeData currentKeyframe = { time(), _deltaTime, false, false };
|
|
TimeKeyframeData futureKeyframe = { newTime, _targetDeltaTime, pause, false };
|
|
_timePaused = pause;
|
|
|
|
clearKeyframes();
|
|
if (interpolationDuration > 0) {
|
|
addKeyframe(now, currentKeyframe);
|
|
}
|
|
addKeyframe(now + interpolationDuration, futureKeyframe);
|
|
}
|
|
|
|
} // namespace openspace
|