diff --git a/src/Magnum/Trade/AnimationData.cpp b/src/Magnum/Trade/AnimationData.cpp index 7ba70ac30..18fbdf1d0 100644 --- a/src/Magnum/Trade/AnimationData.cpp +++ b/src/Magnum/Trade/AnimationData.cpp @@ -32,7 +32,17 @@ namespace Magnum { namespace Trade { -AnimationData::AnimationData(Containers::Array&& data, Containers::Array&& tracks, const void* importerState) noexcept: _data{std::move(data)}, _tracks{std::move(tracks)}, _importerState{importerState} {} +AnimationData::AnimationData(Containers::Array&& data, Containers::Array&& tracks, const Range1D& duration, const void* importerState) noexcept: _duration{duration}, _data{std::move(data)}, _tracks{std::move(tracks)}, _importerState{importerState} {} + +AnimationData::AnimationData(Containers::Array&& data, Containers::Array&& tracks, const void* importerState) noexcept: _data{std::move(data)}, _tracks{std::move(tracks)}, _importerState{importerState} { + if(!_tracks.empty()) { + /* Reset duration to duration of the first track so it properly support + cases where tracks don't start at 0 */ + _duration = _tracks.front()._view.duration(); + for(std::size_t i = 1; i != _tracks.size(); ++i) + _duration = Math::join(_duration, _tracks[i]._view.duration()); + } +} AnimationData::~AnimationData() = default; diff --git a/src/Magnum/Trade/AnimationData.h b/src/Magnum/Trade/AnimationData.h index 13ba5cf22..d0e7d11a7 100644 --- a/src/Magnum/Trade/AnimationData.h +++ b/src/Magnum/Trade/AnimationData.h @@ -207,17 +207,31 @@ class AnimationTrackData { class MAGNUM_TRADE_EXPORT AnimationData { public: /** - * @brief Constructor + * @brief Construct with implicit duration * @param data Buffer containing all keyframe data for this * animation clip * @param tracks Track data * @param importerState Importer-specific state * * Each item of @p track should have an @ref Animation::TrackView - * instance pointing its key/value views to @p data. + * instance pointing its key/value views to @p data. The @ref duration() + * is automatically calculated from durations of all tracks. */ explicit AnimationData(Containers::Array&& data, Containers::Array&& tracks, const void* importerState = nullptr) noexcept; + /** + * @brief Construct with explicit duration + * @param data Buffer containing all keyframe data for this + * animation clip + * @param tracks Track data + * @param duration Animation track duration + * @param importerState Importer-specific state + * + * Each item of @p track should have an @ref Animation::TrackView + * instance pointing its key/value views to @p data. + */ + explicit AnimationData(Containers::Array&& data, Containers::Array&& tracks, const Range1D& duration, const void* importerState = nullptr) noexcept; + ~AnimationData(); /** @brief Copying is not allowed */ @@ -257,6 +271,9 @@ class MAGNUM_TRADE_EXPORT AnimationData { Containers::ArrayView data() const && = delete; /**< @overload */ #endif + /** @brief Duration */ + Range1D duration() const { return _duration; } + /** @brief Track count */ UnsignedInt trackCount() const { return _tracks.size(); } @@ -340,6 +357,7 @@ class MAGNUM_TRADE_EXPORT AnimationData { const void* importerState() const { return _importerState; } private: + Range1D _duration; Containers::Array _data; Containers::Array _tracks; const void* _importerState; diff --git a/src/Magnum/Trade/Test/AnimationDataTest.cpp b/src/Magnum/Trade/Test/AnimationDataTest.cpp index 5744d77fc..06ff6b2dd 100644 --- a/src/Magnum/Trade/Test/AnimationDataTest.cpp +++ b/src/Magnum/Trade/Test/AnimationDataTest.cpp @@ -35,6 +35,8 @@ struct AnimationDataTest: TestSuite::Tester { explicit AnimationDataTest(); void construct(); + void constructImplicitDuration(); + void constructImplicitDurationEmpty(); void constructCopy(); void constructMove(); @@ -52,6 +54,8 @@ struct AnimationDataTest: TestSuite::Tester { AnimationDataTest::AnimationDataTest() { addTests({&AnimationDataTest::construct, + &AnimationDataTest::constructImplicitDuration, + &AnimationDataTest::constructImplicitDurationEmpty, &AnimationDataTest::constructCopy, &AnimationDataTest::constructMove, @@ -99,8 +103,9 @@ void AnimationDataTest::construct() { {&view[0].rotation, view.size(), sizeof(Data)}, Animation::Interpolation::Linear, animationInterpolatorFor(Animation::Interpolation::Linear)}} - }}, &state}; + }}, {-1.0f, 7.0f}, &state}; + CORRADE_COMPARE(data.duration(), (Range1D{-1.0f, 7.0f})); CORRADE_COMPARE(data.data().size(), sizeof(Data)*3); CORRADE_COMPARE(data.trackCount(), 2); CORRADE_COMPARE(data.importerState(), &state); @@ -130,6 +135,71 @@ void AnimationDataTest::construct() { } } +void AnimationDataTest::constructImplicitDuration() { + /* Ain't the prettiest, but trust me: you won't do it like this in the + plugins anyway */ + struct Data { + Float time; + bool value; + }; + Containers::Array buffer{sizeof(Data)*4}; + auto view = Containers::arrayCast(buffer); + view[0] = {1.0f, true}; + view[1] = {5.0f, false}; + view[2] = {3.0f, true}; + view[3] = {7.0f, false}; + + const int state = 5; + AnimationData data{std::move(buffer), Containers::Array{Containers::InPlaceInit, { + {AnimationTrackType::Bool, + AnimationTrackTarget(129), 0, + Animation::TrackView{ + {&view[0].time, 2, sizeof(Data)}, + {&view[0].value, 2, sizeof(Data)}, + Animation::Interpolation::Constant}}, + {AnimationTrackType::Bool, + AnimationTrackTarget(130), 1, + Animation::TrackView{ + {&view[2].time, 2, sizeof(Data)}, + {&view[2].value, 2, sizeof(Data)}, + Animation::Interpolation::Linear}} + }}, &state}; + + CORRADE_COMPARE(data.duration(), (Range1D{1.0f, 7.0f})); + CORRADE_COMPARE(data.trackCount(), 2); + CORRADE_COMPARE(data.importerState(), &state); + { + CORRADE_COMPARE(data.trackType(0), AnimationTrackType::Bool); + CORRADE_COMPARE(data.trackResultType(0), AnimationTrackType::Bool); + CORRADE_COMPARE(data.trackTarget(0), AnimationTrackTarget(129)); + CORRADE_COMPARE(data.trackTargetId(0), 0); + + Animation::TrackView track = data.track(0); + CORRADE_COMPARE(track.duration(), (Range1D{1.0f, 5.0f})); + CORRADE_COMPARE(track.keys().size(), 2); + CORRADE_COMPARE(track.values().size(), 2); + CORRADE_COMPARE(track.interpolation(), Animation::Interpolation::Constant); + CORRADE_COMPARE(track.at(6.0f), false); + } { + CORRADE_COMPARE(data.trackType(1), AnimationTrackType::Bool); + CORRADE_COMPARE(data.trackResultType(1), AnimationTrackType::Bool); + CORRADE_COMPARE(data.trackTarget(1), AnimationTrackTarget(130)); + CORRADE_COMPARE(data.trackTargetId(1), 1); + + Animation::TrackView track = data.track(1); + CORRADE_COMPARE(track.duration(), (Range1D{3.0f, 7.0f})); + CORRADE_COMPARE(track.keys().size(), 2); + CORRADE_COMPARE(track.values().size(), 2); + CORRADE_COMPARE(track.interpolation(), Animation::Interpolation::Linear); + CORRADE_COMPARE(track.at(4.5f), true); + } +} + +void AnimationDataTest::constructImplicitDurationEmpty() { + AnimationData data{nullptr, nullptr}; + CORRADE_COMPARE(data.duration(), Range1D{}); +} + void AnimationDataTest::constructCopy() { CORRADE_VERIFY(!(std::is_constructible{})); CORRADE_VERIFY(!(std::is_assignable{})); @@ -164,10 +234,11 @@ void AnimationDataTest::constructMove() { {&view[0].rotation, view.size(), sizeof(Data)}, Animation::Interpolation::Linear, animationInterpolatorFor(Animation::Interpolation::Linear)}} - }}, &state}; + }}, {-1.0f, 7.0f}, &state}; AnimationData b{std::move(a)}; + CORRADE_COMPARE(b.duration(), (Range1D{-1.0f, 7.0f})); CORRADE_COMPARE(b.data().size(), sizeof(Data)*3); CORRADE_COMPARE(b.trackCount(), 2); CORRADE_COMPARE(b.importerState(), &state); @@ -200,6 +271,7 @@ void AnimationDataTest::constructMove() { AnimationData c{nullptr, nullptr, &other}; c = std::move(b); + CORRADE_COMPARE(c.duration(), (Range1D{-1.0f, 7.0f})); CORRADE_COMPARE(c.data().size(), sizeof(Data)*3); CORRADE_COMPARE(c.trackCount(), 2); CORRADE_COMPARE(c.importerState(), &state);