mirror of https://github.com/mosra/magnum.git
You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
189 lines
7.3 KiB
189 lines
7.3 KiB
|
8 years ago
|
#ifndef Magnum_Animation_Player_hpp
|
||
|
|
#define Magnum_Animation_Player_hpp
|
||
|
|
/*
|
||
|
|
This file is part of Magnum.
|
||
|
|
|
||
|
|
Copyright © 2010, 2011, 2012, 2013, 2014, 2015, 2016, 2017, 2018
|
||
|
|
Vladimír Vondruš <mosra@centrum.cz>
|
||
|
|
|
||
|
|
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.
|
||
|
|
*/
|
||
|
|
|
||
|
|
/** @file
|
||
|
|
* @brief @ref compilation-speedup-hpp "Template implementation" for @ref Animable.h and @ref AnimableGroup.h
|
||
|
|
*/
|
||
|
|
|
||
|
|
#include "Player.h"
|
||
|
|
|
||
|
|
namespace Magnum { namespace Animation {
|
||
|
|
|
||
|
|
namespace Implementation {
|
||
|
|
template<class T, class K> struct DefaultScaler {
|
||
|
|
static std::pair<UnsignedInt, K> scale(T time, K duration) {
|
||
|
|
const UnsignedInt playCount = time/duration;
|
||
|
|
const K factor = std::fmod(Double(time), Double(duration));
|
||
|
|
return {playCount, factor};
|
||
|
|
}
|
||
|
|
};
|
||
|
|
|
||
|
|
template<> struct DefaultScaler<std::chrono::nanoseconds, Float> {
|
||
|
|
static std::pair<UnsignedInt, Float> scale(std::chrono::nanoseconds time, Float duration) {
|
||
|
|
/* I hope I'm not having any catastrophic cancellation here :/ */
|
||
|
|
const std::chrono::nanoseconds durationNs{std::chrono::nanoseconds::rep(Double(duration)*1000000000.0)};
|
||
|
|
const UnsignedInt playCount = time/durationNs;
|
||
|
|
const Float factor = Float((time - playCount*durationNs).count()/1000000000.0);
|
||
|
|
return {playCount, factor};
|
||
|
|
}
|
||
|
|
};
|
||
|
|
}
|
||
|
|
|
||
|
|
#ifndef DOXYGEN_GENERATING_OUTPUT
|
||
|
|
template<class T, class K> struct Player<T, K>::Track {
|
||
|
|
/* Not sure why is this still needed for emplace_back(). It's 2018,
|
||
|
|
COME ON ¯\_(ツ)_/¯ */
|
||
|
|
/*implicit*/ Track(const TrackViewStorage<K>& track, void (*advancer)(const TrackViewStorage<K>&, K, std::size_t&, void*, void(*)(), void*), void* destination, void(*userCallback)(), void* userCallbackData, std::size_t hint) noexcept: track{track}, advancer{advancer}, destination{destination}, userCallback{userCallback}, userCallbackData{userCallbackData}, hint{hint} {}
|
||
|
|
|
||
|
|
TrackViewStorage<K> track;
|
||
|
|
void (*advancer)(const TrackViewStorage<K>&, K, std::size_t&, void*, void(*)(), void*);
|
||
|
|
void* destination;
|
||
|
|
void(*userCallback)();
|
||
|
|
void* userCallbackData;
|
||
|
|
std::size_t hint;
|
||
|
|
};
|
||
|
|
#endif
|
||
|
|
|
||
|
|
template<class T, class K> Player<T, K>::Player(Player<T, K>&&) noexcept = default;
|
||
|
|
|
||
|
|
template<class T, class K> Player<T, K>& Player<T, K>::operator=(Player<T, K>&&) noexcept = default;
|
||
|
|
|
||
|
|
template<class T, class K> Player<T, K>::Player(): Player<T, K>{Implementation::DefaultScaler<T, K>::scale} {}
|
||
|
|
|
||
|
|
template<class T, class K> Player<T, K>::Player(Scaler scaler): _scaler{scaler} {}
|
||
|
|
|
||
|
|
template<class T, class K> Player<T, K>::~Player() = default;
|
||
|
|
|
||
|
|
template<class T, class K> bool Player<T, K>::isEmpty() const {
|
||
|
|
return _tracks.empty();
|
||
|
|
}
|
||
|
|
|
||
|
|
template<class T, class K> std::size_t Player<T, K>::size() const {
|
||
|
|
return _tracks.size();
|
||
|
|
}
|
||
|
|
|
||
|
|
template<class T, class K> const TrackViewStorage<K>& Player<T, K>::track(std::size_t i) const {
|
||
|
|
CORRADE_ASSERT(i < _tracks.size(),
|
||
|
|
"Animation::Player::track(): index out of range", _tracks[i].track);
|
||
|
|
return _tracks[i].track;
|
||
|
|
}
|
||
|
|
|
||
|
|
template<class T, class K> Player<T, K>& Player<T, K>::addInternal(const TrackViewStorage<K>& track, void(*const advancer)(const TrackViewStorage<K>&, K, std::size_t&, void*, void(*)(), void*), void* const destination, void(*const userCallback)(), void* const userCallbackData) {
|
||
|
|
if(_tracks.empty() && _duration == Math::Range1D<K>{})
|
||
|
|
_duration = track.duration();
|
||
|
|
else
|
||
|
|
_duration = Math::join(track.duration(), _duration);
|
||
|
|
_tracks.emplace_back(track, advancer, destination, userCallback, userCallbackData, 0);
|
||
|
|
return *this;
|
||
|
|
}
|
||
|
|
|
||
|
|
template<class T, class K> Player<T, K>& Player<T, K>::play(T startTime) {
|
||
|
|
/* In case we were paused, move start time backwards by the duration that
|
||
|
|
was already played back */
|
||
|
|
if(_state == State::Paused) {
|
||
|
|
_startTime = startTime - _startTime;
|
||
|
|
_state = State::Playing;
|
||
|
|
return *this;
|
||
|
|
}
|
||
|
|
|
||
|
|
_state = State::Playing;
|
||
|
|
_startTime = startTime;
|
||
|
|
return *this;
|
||
|
|
}
|
||
|
|
|
||
|
|
template<class T, class K> Player<T, K>& Player<T, K>::pause(T pauseTime) {
|
||
|
|
/* Avoid breaking the pause state when not playing */
|
||
|
|
if(_state != State::Playing) return *this;
|
||
|
|
|
||
|
|
_state = State::Paused;
|
||
|
|
_pauseTime = pauseTime;
|
||
|
|
return *this;
|
||
|
|
}
|
||
|
|
|
||
|
|
template<class T, class K> Player<T, K>& Player<T, K>::stop() {
|
||
|
|
_state = State::Stopped;
|
||
|
|
return *this;
|
||
|
|
}
|
||
|
|
|
||
|
|
template<class T, class K> Player<T, K>& Player<T, K>::setState(State state, T time) {
|
||
|
|
switch(state) {
|
||
|
|
case State::Playing: return play(time);
|
||
|
|
case State::Paused: return pause(time);
|
||
|
|
case State::Stopped: return stop();
|
||
|
|
}
|
||
|
|
|
||
|
|
CORRADE_ASSERT_UNREACHABLE(); /* LCOV_EXCL_LINE */
|
||
|
|
}
|
||
|
|
|
||
|
|
template<class T, class K> Player<T, K>& Player<T, K>::advance(T time) {
|
||
|
|
/* Time to use for advancing the animation */
|
||
|
|
T timeToUse = time;
|
||
|
|
|
||
|
|
/* The animation was paused right before this iteration, "park" the
|
||
|
|
animation to the pause time. This time will be used by play() to offset
|
||
|
|
the playback when the animation is resumed.
|
||
|
|
|
||
|
|
std::chrono::duration doesn't have operator bool, so I need to compare
|
||
|
|
to default-constructed value. Ugh. */
|
||
|
|
if(_state == State::Paused && (_pauseTime != T{})) {
|
||
|
|
timeToUse = _pauseTime;
|
||
|
|
_startTime = _pauseTime - _startTime;
|
||
|
|
_pauseTime = {};
|
||
|
|
|
||
|
|
/* The animation was stopped by the user right before this iteration,
|
||
|
|
"park" the animation to the initial time */
|
||
|
|
} else if(_state == State::Stopped && (_startTime != T{})) {
|
||
|
|
_startTime = {};
|
||
|
|
timeToUse = {};
|
||
|
|
|
||
|
|
/* Otherwise, if the player is not playing or scheduled to start playing in
|
||
|
|
the future, do nothing */
|
||
|
|
} else if(_state != State::Playing || time < _startTime) return *this;
|
||
|
|
|
||
|
|
/* Calculate current play iteration and key value in that iteration. If we
|
||
|
|
exceeded play count, stop the animation and give out value at duration
|
||
|
|
end. */
|
||
|
|
UnsignedInt playCount;
|
||
|
|
K key;
|
||
|
|
std::tie(playCount, key) = _scaler(timeToUse - _startTime, _duration.size()[0]);
|
||
|
|
if(_playCount && playCount >= _playCount) {
|
||
|
|
_state = State::Stopped;
|
||
|
|
_startTime = {};
|
||
|
|
key = _duration.size()[0];
|
||
|
|
}
|
||
|
|
|
||
|
|
/* Advance all tracks. Properly handle durations that don't start at 0. */
|
||
|
|
for(Track& t: _tracks)
|
||
|
|
t.advancer(t.track, _duration.min()[0] + key, t.hint, t.destination, t.userCallback, t.userCallbackData);
|
||
|
|
|
||
|
|
return *this;
|
||
|
|
}
|
||
|
|
|
||
|
|
}}
|
||
|
|
|
||
|
|
#endif
|