From dd1c8052ff968e9ec3f35577906a801c7f883f30 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Vladim=C3=ADr=20Vondru=C5=A1?= Date: Thu, 12 Jul 2018 02:38:12 +0200 Subject: [PATCH] Trade: initial AnimationData implementation. --- src/Magnum/Trade/AnimationData.cpp | 141 +++++++ src/Magnum/Trade/AnimationData.h | 386 ++++++++++++++++++++ src/Magnum/Trade/CMakeLists.txt | 2 + src/Magnum/Trade/Test/AnimationDataTest.cpp | 320 ++++++++++++++++ src/Magnum/Trade/Test/CMakeLists.txt | 8 + src/Magnum/Trade/Trade.h | 6 + 6 files changed, 863 insertions(+) create mode 100644 src/Magnum/Trade/AnimationData.cpp create mode 100644 src/Magnum/Trade/AnimationData.h create mode 100644 src/Magnum/Trade/Test/AnimationDataTest.cpp diff --git a/src/Magnum/Trade/AnimationData.cpp b/src/Magnum/Trade/AnimationData.cpp new file mode 100644 index 000000000..7ba70ac30 --- /dev/null +++ b/src/Magnum/Trade/AnimationData.cpp @@ -0,0 +1,141 @@ +/* + This file is part of Magnum. + + Copyright © 2010, 2011, 2012, 2013, 2014, 2015, 2016, 2017, 2018 + Vladimír Vondruš + + 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 "AnimationData.h" + +#include + +#include "Magnum/Math/Vector4.h" +#include "Magnum/Math/Quaternion.h" + +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() = default; + +AnimationData::AnimationData(AnimationData&&) noexcept = default; + +AnimationData& AnimationData::operator=(AnimationData&&) noexcept = default; + +AnimationTrackType AnimationData::trackType(UnsignedInt id) const { + CORRADE_ASSERT(id < _tracks.size(), "Trade::AnimationData::trackType(): index out of range", {}); + return _tracks[id]._type; +} + +AnimationTrackType AnimationData::trackResultType(UnsignedInt id) const { + CORRADE_ASSERT(id < _tracks.size(), "Trade::AnimationData::trackResultType(): index out of range", {}); + return _tracks[id]._resultType; +} + +AnimationTrackTarget AnimationData::trackTarget(UnsignedInt id) const { + CORRADE_ASSERT(id < _tracks.size(), "Trade::AnimationData::trackTarget(): index out of range", {}); + return _tracks[id]._target; +} + +UnsignedInt AnimationData::trackTargetId(UnsignedInt id) const { + CORRADE_ASSERT(id < _tracks.size(), "Trade::AnimationData::trackTargetId(): index out of range", {}); + return _tracks[id]._targetId; +} + +template auto animationInterpolatorFor(Animation::Interpolation interpolation) -> R(*)(const V&, const V&, Float) { + return Animation::interpolatorFor(interpolation); +} + +#ifndef DOXYGEN_GENERATING_OUTPUT +template MAGNUM_TRADE_EXPORT auto animationInterpolatorFor(Animation::Interpolation) -> bool(*)(const bool&, const bool&, Float); +template MAGNUM_TRADE_EXPORT auto animationInterpolatorFor(Animation::Interpolation) -> Float(*)(const Float&, const Float&, Float); +template MAGNUM_TRADE_EXPORT auto animationInterpolatorFor(Animation::Interpolation) -> UnsignedInt(*)(const UnsignedInt&, const UnsignedInt&, Float); +template MAGNUM_TRADE_EXPORT auto animationInterpolatorFor(Animation::Interpolation) -> Int(*)(const Int&, const Int&, Float); +template MAGNUM_TRADE_EXPORT auto animationInterpolatorFor, Math::BoolVector<2>>(Animation::Interpolation) -> Math::BoolVector<2>(*)(const Math::BoolVector<2>&, const Math::BoolVector<2>&, Float); +template MAGNUM_TRADE_EXPORT auto animationInterpolatorFor, Math::BoolVector<3>>(Animation::Interpolation) -> Math::BoolVector<3>(*)(const Math::BoolVector<3>&, const Math::BoolVector<3>&, Float); +template MAGNUM_TRADE_EXPORT auto animationInterpolatorFor, Math::BoolVector<4>>(Animation::Interpolation) -> Math::BoolVector<4>(*)(const Math::BoolVector<4>&, const Math::BoolVector<4>&, Float); +template MAGNUM_TRADE_EXPORT auto animationInterpolatorFor(Animation::Interpolation) -> Vector2(*)(const Vector2&, const Vector2&, Float); +template MAGNUM_TRADE_EXPORT auto animationInterpolatorFor(Animation::Interpolation) -> Vector2i(*)(const Vector2i&, const Vector2i&, Float); +template MAGNUM_TRADE_EXPORT auto animationInterpolatorFor(Animation::Interpolation) -> Vector2ui(*)(const Vector2ui&, const Vector2ui&, Float); +template MAGNUM_TRADE_EXPORT auto animationInterpolatorFor(Animation::Interpolation) -> Vector3(*)(const Vector3&, const Vector3&, Float); +template MAGNUM_TRADE_EXPORT auto animationInterpolatorFor(Animation::Interpolation) -> Vector3i(*)(const Vector3i&, const Vector3i&, Float); +template MAGNUM_TRADE_EXPORT auto animationInterpolatorFor(Animation::Interpolation) -> Vector3ui(*)(const Vector3ui&, const Vector3ui&, Float); +template MAGNUM_TRADE_EXPORT auto animationInterpolatorFor(Animation::Interpolation) -> Vector4(*)(const Vector4&, const Vector4&, Float); +template MAGNUM_TRADE_EXPORT auto animationInterpolatorFor(Animation::Interpolation) -> Vector4d(*)(const Vector4d&, const Vector4d&, Float); +template MAGNUM_TRADE_EXPORT auto animationInterpolatorFor(Animation::Interpolation) -> Vector4i(*)(const Vector4i&, const Vector4i&, Float); +template MAGNUM_TRADE_EXPORT auto animationInterpolatorFor(Animation::Interpolation) -> Vector4ui(*)(const Vector4ui&, const Vector4ui&, Float); +template MAGNUM_TRADE_EXPORT auto animationInterpolatorFor(Animation::Interpolation) -> Quaternion(*)(const Quaternion&, const Quaternion&, Float); +template MAGNUM_TRADE_EXPORT auto animationInterpolatorFor(Animation::Interpolation) -> DualQuaternion(*)(const DualQuaternion&, const DualQuaternion&, Float); + +Debug& operator<<(Debug& debug, const AnimationTrackType value) { + switch(value) { + /* LCOV_EXCL_START */ + #define _c(value) case AnimationTrackType::value: return debug << "Trade::AnimationTrackType::" #value; + _c(Bool) + _c(Float) + _c(UnsignedInt) + _c(Int) + _c(BoolVector2) + _c(BoolVector3) + _c(BoolVector4) + _c(Vector2) + _c(Vector2ui) + _c(Vector2i) + _c(Vector3) + _c(Vector3ui) + _c(Vector3i) + _c(Vector4) + _c(Vector4ui) + _c(Vector4i) + _c(Quaternion) + _c(DualQuaternion) + #undef _c + /* LCOV_EXCL_STOP */ + } + + return debug << "Trade::AnimationTrackType(" << Debug::nospace << reinterpret_cast(UnsignedByte(value)) << Debug::nospace << ")"; +} + +Debug& operator<<(Debug& debug, const AnimationTrackTarget value) { + if(UnsignedByte(value) >= UnsignedByte(AnimationTrackTarget::Custom)) + return debug << "Trade::AnimationTrackTarget::Custom(" << Debug::nospace << UnsignedByte(value) << Debug::nospace << ")"; + + switch(value) { + /* LCOV_EXCL_START */ + #define _c(value) case AnimationTrackTarget::value: return debug << "Trade::AnimationTrackTarget::" #value; + _c(Translation2D) + _c(Translation3D) + _c(Rotation2D) + _c(Rotation3D) + _c(Scaling2D) + _c(Scaling3D) + #undef _c + /* LCOV_EXCL_STOP */ + + /* To silence compiler warning about unhandled values */ + case AnimationTrackTarget::Custom: CORRADE_ASSERT_UNREACHABLE(); /* LCOV_EXCL_LINE */ + } + + return debug << "Trade::AnimationTrackTarget(" << Debug::nospace << reinterpret_cast(UnsignedByte(value)) << Debug::nospace << ")"; +} +#endif + +}} diff --git a/src/Magnum/Trade/AnimationData.h b/src/Magnum/Trade/AnimationData.h new file mode 100644 index 000000000..13113fbae --- /dev/null +++ b/src/Magnum/Trade/AnimationData.h @@ -0,0 +1,386 @@ +#ifndef Magnum_Trade_AnimationData_h +#define Magnum_Trade_AnimationData_h +/* + This file is part of Magnum. + + Copyright © 2010, 2011, 2012, 2013, 2014, 2015, 2016, 2017, 2018 + Vladimír Vondruš + + 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 Class @ref Magnum::Trade::AnimationTrackData, @ref Magnum::Trade::AnimationData, enum @ref Magnum::Trade::AnimationTrackType, @ref Magnum::Trade::AnimationTrackTarget, function @ref Magnum::Trade::animationInterpolatorFor() + */ + +#include "Magnum/Magnum.h" +#include "Magnum/Math/Math.h" +#include "Magnum/Animation/Track.h" +#include "Magnum/Trade/Trade.h" +#include "Magnum/Trade/visibility.h" + +namespace Magnum { namespace Trade { + +/** +@brief Type of animation track data + +@see @ref AnimationData +@experimental +*/ +enum class AnimationTrackType: UnsignedByte { + Bool, /**< @cpp bool @ce */ + Float, /**< @ref Magnum::Float "Float" */ + UnsignedInt, /**< @ref Magnum::UnsignedInt "UnsignedInt" */ + Int, /**< @ref Magnum::Int "Int" */ + BoolVector2, /**< @ref Math::BoolVector "Math::BoolVector<2>" */ + BoolVector3, /**< @ref Math::BoolVector "Math::BoolVector<3>" */ + BoolVector4, /**< @ref Math::BoolVector "Math::BoolVector<4>" */ + + /** + * @ref Magnum::Vector2 "Vector2". Usually used for + * @ref AnimationTrackTarget::Translation2D and + * @ref AnimationTrackTarget::Scaling2D. + */ + Vector2, + + Vector2ui, /**< @ref Magnum::Vector2ui "Vector2ui" */ + Vector2i, /**< @ref Magnum::Vector2i "Vector2i" */ + + /** + * @ref Magnum::Vector3 "Vector3". Usually used for + * @ref AnimationTrackTarget::Translation3D and + * @ref AnimationTrackTarget::Scaling3D. + */ + Vector3, + + Vector3ui, /**< @ref Magnum::Vector3ui "Vector3ui" */ + Vector3i, /**< @ref Magnum::Vector3i "Vector3i" */ + Vector4, /**< @ref Magnum::Vector4 "Vector4" */ + Vector4ui, /**< @ref Magnum::Vector4ui "Vector4ui" */ + Vector4i, /**< @ref Magnum::Vector4i "Vector4i" */ + + /** + * @ref Magnum::Quaternion "Quaternion". Usually used for + * @ref AnimationTrackTarget::Rotation3D. + */ + Quaternion, + + DualQuaternion /**< @ref Magnum::DualQuaternion "DualQuaternion" */ +}; + +/** @debugoperatorenum{AnimationTrackType} */ +MAGNUM_TRADE_EXPORT Debug& operator<<(Debug& debug, AnimationTrackType value); + +/** +@brief Target of an animation track + +@see @ref AnimationData +@experimental +*/ +enum class AnimationTrackTarget: UnsignedByte { + /** + * Modifies 2D object translation. Type is usually + * @ref Magnum::Vector2 "Vector2". + * + * @see @ref AnimationTrackType::Vector2, @ref ObjectData2D::translation() + */ + Translation2D, + + /** + * Modifies 3D object translation. Type is usually + * @ref Magnum::Vector3 "Vector3". + * + * @see @ref AnimationTrackType::Vector3, @ref ObjectData3D::translation() + */ + Translation3D, + + /** + * Modifies 2D object rotation. Type is usually + * @ref Magnum::Complex "Complex". + * + * @see @ref ObjectData2D::rotation() + */ + Rotation2D, + + /** + * Modifies 3D object rotation. Type is usually + * @ref Magnum::Quaternion "Quaternion". + * + * @see @ref AnimationTrackType::Quaternion, @ref ObjectData3D::rotation() + */ + Rotation3D, + + /** + * Modifies 2D object scaling. Type is usually + * @ref Magnum::Vector2 "Vector2". + * + * @see @ref AnimationTrackType::Vector2, @ref ObjectData2D::scaling() + */ + Scaling2D, + + /** + * Modifies 3D object scaling. Type is usually + * @ref Magnum::Vector3 "Vector3". + * + * @see @ref AnimationTrackType::Vector3, @ref ObjectData3D::scaling() + */ + Scaling3D, + + /** + * This and all higher values are for importer-specific targets. Can be of + * any type, @ref AnimationData::trackTargetId() might or might not point + * to an existing object. See documentation of particular importer for + * details. + */ + Custom = 128 +}; + +/** @debugoperatorenum{AnimationTrackTarget} */ +MAGNUM_TRADE_EXPORT Debug& operator<<(Debug& debug, AnimationTrackTarget value); + +/** +@brief Animation track data + +Used when constructing @ref AnimationData. Fields can be accessed through its +API. +@experimental +*/ +class AnimationTrackData { + public: + /** + * @brief Constructor + * @param type Value type + * @param resultType Result type + * @param target Track target + * @param targetId Track target ID + * @param view Type-erased @ref Animation::TrackView instance + */ + /*implicit*/ AnimationTrackData(AnimationTrackType type, AnimationTrackType resultType, AnimationTrackTarget target, UnsignedInt targetId, Animation::TrackViewStorage view) noexcept: _type{type}, _resultType{resultType}, _target{target}, _targetId{targetId}, _view{view} {} + + /** @overload + * + * Equivalent to the above with @p type used as both value type and + * result type. + */ + /*implicit*/ AnimationTrackData(AnimationTrackType type, AnimationTrackTarget target, UnsignedInt targetId, Animation::TrackViewStorage view) noexcept: _type{type}, _resultType{type}, _target{target}, _targetId{targetId}, _view{view} {} + + private: + friend AnimationData; + + AnimationTrackType _type, _resultType; + AnimationTrackTarget _target; + UnsignedInt _targetId; + Animation::TrackViewStorage _view; +}; + +/** +@brief Animation clip data + +@see @ref AbstractImporter::animation() +@experimental +*/ +class MAGNUM_TRADE_EXPORT AnimationData { + public: + /** + * @brief Constructor + * @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. + */ + explicit AnimationData(Containers::Array&& data, Containers::Array&& tracks, const void* importerState = nullptr) noexcept; + + ~AnimationData(); + + /** @brief Copying is not allowed */ + AnimationData(const AnimationData&) = delete; + + /** @brief Move constructor */ + AnimationData(AnimationData&&) noexcept; + + /** @brief Copying is not allowed */ + AnimationData& operator=(const AnimationData&) = delete; + + /** @brief Move assignment */ + AnimationData& operator=(AnimationData&&) noexcept; + + /** + * @brief Raw data + * + * Contains data for all tracks contained in this clip. + * @see @ref release() + */ + Containers::ArrayView data() + #ifndef CORRADE_GCC47_COMPATIBILITY + & + #endif + { return _data; } + #ifndef CORRADE_GCC47_COMPATIBILITY + Containers::ArrayView data() && = delete; /**< @overload */ + #endif + + /** @overload */ + Containers::ArrayView data() const + #ifndef CORRADE_GCC47_COMPATIBILITY + & + #endif + { return _data; } + #ifndef CORRADE_GCC47_COMPATIBILITY + Containers::ArrayView data() const && = delete; /**< @overload */ + #endif + + /** @brief Track count */ + UnsignedInt trackCount() const { return _tracks.size(); } + + /** + * @brief Track value type + * @param id Track index + * + * Data types are usually closely related to @ref trackTarget(), see + * @ref AnimationTrackTarget documentation for more information. + * @see @ref trackCount() + */ + AnimationTrackType trackType(UnsignedInt id) const; + + /** + * @brief Track result type + * @param id Track index + * + * In case track values are packed, track result type is different from + * @ref trackType(). Data types are usually closely related to + * @ref trackTarget(), see @ref AnimationTrackTarget documentation for + * more information. + * @see @ref trackCount() + */ + AnimationTrackType trackResultType(UnsignedInt id) const; + + /** + * @brief Track target + * @param id Track index + * + * Particular animation targets usually correspond to a common + * @ref trackType(), see @ref AnimationTrackTarget documentation for + * more information. + * @see @ref trackCount() + */ + AnimationTrackTarget trackTarget(UnsignedInt id) const; + + /** + * @brief Track target index + * @param id Track index + * + * For @ref trackTarget() with @ref AnimationTrackTarget::Translation2D, + * @ref AnimationTrackTarget::Translation3D, + * @ref AnimationTrackTarget::Rotation2D, + * @ref AnimationTrackTarget::Rotation3D, + * @ref AnimationTrackTarget::Scaling2D, + * @ref AnimationTrackTarget::Scaling3D specifies object which property + * is modified. + * @see @ref trackCount(), @ref AbstractImporter::object2D(), + * @ref AbstractImporter::object3D() + */ + UnsignedInt trackTargetId(UnsignedInt id) const; + + /** + * @brief Track data + * @tparam V Track value type + * @tparam R Track result type + * + * Expects that requested types are correct for given @ref trackType() + * and @ref trackResultType(). Note that the returned view is onto + * @ref data(), meaning you either need to ensure that the + * @cpp AnimationData @ce instance stays in scope for as long as you + * use the view or you need to release the data array using + * @ref release() and manage its lifetime yourself. + */ + template> Animation::TrackView track(UnsignedInt id) const; + + /** + * @brief Release data storage + * + * Releases the ownership of the data array and resets internal state + * to default. + * @see @ref data() + */ + Containers::Array release() { return std::move(_data); } + + /** + * @brief Importer-specific state + * + * See @ref AbstractImporter::importerState() for more information. + */ + const void* importerState() const { return _importerState; } + + private: + Containers::Array _data; + Containers::Array _tracks; + const void* _importerState; +}; + +/** @relatesalso AnimationData +@brief Animation interpolator function for given interpolation behavior + +To be used from importer plugins --- unlike @ref Animation::interpolatorFor() +guarantees that the returned function pointer is not instantiated inside plugin +binary to avoid dangling function pointers on plugin unload. See +@ref Animation::interpolatorFor() for more information. +@see @ref AnimationData +@experimental +*/ +template> MAGNUM_TRADE_EXPORT auto animationInterpolatorFor(Animation::Interpolation interpolation) -> R(*)(const V&, const V&, Float); + +#if !defined(CORRADE_NO_ASSERT) || defined(CORRADE_GRACEFUL_ASSERT) +namespace Implementation { + /* LCOV_EXCL_START */ + template AnimationTrackType animationTypeFor(); + template<> constexpr AnimationTrackType animationTypeFor() { return AnimationTrackType::Bool; } + template<> constexpr AnimationTrackType animationTypeFor() { return AnimationTrackType::Float; } + template<> constexpr AnimationTrackType animationTypeFor() { return AnimationTrackType::UnsignedInt; } + template<> constexpr AnimationTrackType animationTypeFor() { return AnimationTrackType::Int; } + template<> constexpr AnimationTrackType animationTypeFor>() { return AnimationTrackType::BoolVector2; } + template<> constexpr AnimationTrackType animationTypeFor>() { return AnimationTrackType::BoolVector3; } + template<> constexpr AnimationTrackType animationTypeFor>() { return AnimationTrackType::BoolVector4; } + template<> constexpr AnimationTrackType animationTypeFor() { return AnimationTrackType::Vector2; } + template<> constexpr AnimationTrackType animationTypeFor() { return AnimationTrackType::Vector2ui; } + template<> constexpr AnimationTrackType animationTypeFor() { return AnimationTrackType::Vector2i; } + template<> constexpr AnimationTrackType animationTypeFor() { return AnimationTrackType::Vector3; } + template<> constexpr AnimationTrackType animationTypeFor() { return AnimationTrackType::Vector3ui; } + template<> constexpr AnimationTrackType animationTypeFor() { return AnimationTrackType::Vector3i; } + template<> constexpr AnimationTrackType animationTypeFor() { return AnimationTrackType::Vector4; } + template<> constexpr AnimationTrackType animationTypeFor() { return AnimationTrackType::Vector4ui; } + template<> constexpr AnimationTrackType animationTypeFor() { return AnimationTrackType::Vector4i; } + template<> constexpr AnimationTrackType animationTypeFor() { return AnimationTrackType::Quaternion; } + template<> constexpr AnimationTrackType animationTypeFor() { return AnimationTrackType::DualQuaternion; } + /* LCOV_EXCL_STOP */ +} +#endif + +template Animation::TrackView AnimationData::track(UnsignedInt id) const { + CORRADE_ASSERT(id < _tracks.size(), "Trade::AnimationData::track(): index out of range", (Animation::TrackView{})); + CORRADE_ASSERT(Implementation::animationTypeFor() == _tracks[id]._type, "Trade::AnimationData::track(): improper type requested for" << _tracks[id]._type, (Animation::TrackView{})); + CORRADE_ASSERT(Implementation::animationTypeFor() == _tracks[id]._resultType, "Trade::AnimationData::track(): improper result type requested for" << _tracks[id]._resultType, (Animation::TrackView{})); + return static_cast&>(_tracks[id]._view); +} + +}} + +#endif diff --git a/src/Magnum/Trade/CMakeLists.txt b/src/Magnum/Trade/CMakeLists.txt index f135d741b..bfcfc0189 100644 --- a/src/Magnum/Trade/CMakeLists.txt +++ b/src/Magnum/Trade/CMakeLists.txt @@ -38,6 +38,7 @@ set(MagnumTrade_SRCS set(MagnumTrade_GracefulAssert_SRCS AbstractImageConverter.cpp AbstractImporter.cpp + AnimationData.cpp ImageData.cpp ObjectData2D.cpp ObjectData3D.cpp @@ -47,6 +48,7 @@ set(MagnumTrade_HEADERS AbstractImporter.h AbstractImageConverter.h AbstractMaterialData.h + AnimationData.h CameraData.h ImageData.h LightData.h diff --git a/src/Magnum/Trade/Test/AnimationDataTest.cpp b/src/Magnum/Trade/Test/AnimationDataTest.cpp new file mode 100644 index 000000000..62e95a636 --- /dev/null +++ b/src/Magnum/Trade/Test/AnimationDataTest.cpp @@ -0,0 +1,320 @@ +/* + This file is part of Magnum. + + Copyright © 2010, 2011, 2012, 2013, 2014, 2015, 2016, 2017, 2018 + Vladimír Vondruš + + 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 +#include + +#include "Magnum/Math/Quaternion.h" +#include "Magnum/Trade/AnimationData.h" + +namespace Magnum { namespace Trade { namespace Test { + +struct AnimationDataTest: TestSuite::Tester { + explicit AnimationDataTest(); + + void construct(); + void constructCopy(); + void constructMove(); + + void trackCustomResultType(); + + void trackWrongIndex(); + void trackWrongType(); + void trackWrongResultType(); + + void debugAnimationTrackType(); + void debugAnimationTrackTarget(); +}; + +AnimationDataTest::AnimationDataTest() { + addTests({&AnimationDataTest::construct, + &AnimationDataTest::constructCopy, + &AnimationDataTest::constructMove, + + &AnimationDataTest::trackCustomResultType, + + &AnimationDataTest::trackWrongIndex, + &AnimationDataTest::trackWrongType, + &AnimationDataTest::trackWrongResultType, + + &AnimationDataTest::debugAnimationTrackType, + &AnimationDataTest::debugAnimationTrackTarget}); +} + +using namespace Math::Literals; + +void AnimationDataTest::construct() { + /* Ain't the prettiest, but trust me: you won't do it like this in the + plugins anyway */ + struct Data { + Float time; + Vector3 position; + Quaternion rotation; + }; + Containers::Array buffer{sizeof(Data)*3}; + auto view = Containers::arrayCast(buffer); + view[0] = {0.0f, {3.0f, 1.0f, 0.1f}, Quaternion::rotation(45.0_degf, Vector3::yAxis())}; + view[1] = {5.0f, {0.3f, 0.6f, 1.0f}, Quaternion::rotation(20.0_degf, Vector3::yAxis())}; + view[2] = {7.5f, {1.0f, 0.3f, 2.1f}, Quaternion{}}; + + const int state = 5; + AnimationData data{std::move(buffer), Containers::Array{Containers::InPlaceInit, { + {AnimationTrackType::Vector3, + AnimationTrackTarget::Translation3D, 42, + Animation::TrackView{ + {&view[0].time, view.size(), sizeof(Data)}, + {&view[0].position, view.size(), sizeof(Data)}, + Animation::Interpolation::Constant, + animationInterpolatorFor(Animation::Interpolation::Constant)}}, + {AnimationTrackType::Quaternion, + AnimationTrackTarget::Rotation3D, 1337, + Animation::TrackView{ + {&view[0].time, view.size(), sizeof(Data)}, + {&view[0].rotation, view.size(), sizeof(Data)}, + Animation::Interpolation::Linear, + animationInterpolatorFor(Animation::Interpolation::Linear)}} + }}, &state}; + + CORRADE_COMPARE(data.data().size(), sizeof(Data)*3); + CORRADE_COMPARE(data.trackCount(), 2); + CORRADE_COMPARE(data.importerState(), &state); + + { + CORRADE_COMPARE(data.trackType(0), AnimationTrackType::Vector3); + CORRADE_COMPARE(data.trackResultType(0), AnimationTrackType::Vector3); + CORRADE_COMPARE(data.trackTarget(0), AnimationTrackTarget::Translation3D); + CORRADE_COMPARE(data.trackTargetId(0), 42); + + Animation::TrackView track = data.track(0); + CORRADE_COMPARE(track.keys().size(), 3); + CORRADE_COMPARE(track.values().size(), 3); + CORRADE_COMPARE(track.interpolation(), Animation::Interpolation::Constant); + CORRADE_COMPARE(track.at(2.5f), (Vector3{3.0f, 1.0f, 0.1f})); + } { + CORRADE_COMPARE(data.trackType(1), AnimationTrackType::Quaternion); + CORRADE_COMPARE(data.trackResultType(1), AnimationTrackType::Quaternion); + CORRADE_COMPARE(data.trackTarget(1), AnimationTrackTarget::Rotation3D); + CORRADE_COMPARE(data.trackTargetId(1), 1337); + + Animation::TrackView track = data.track(1); + CORRADE_COMPARE(track.keys().size(), 3); + CORRADE_COMPARE(track.values().size(), 3); + CORRADE_COMPARE(track.interpolation(), Animation::Interpolation::Linear); + CORRADE_COMPARE(track.at(2.5f), Quaternion::rotation(32.5_degf, Vector3::yAxis())); + } +} + +void AnimationDataTest::constructCopy() { + CORRADE_VERIFY(!(std::is_constructible{})); + CORRADE_VERIFY(!(std::is_assignable{})); +} + +void AnimationDataTest::constructMove() { + /* Ugh and here we go again! */ + struct Data { + Float time; + Vector3 position; + Quaternion rotation; + }; + Containers::Array buffer{sizeof(Data)*3}; + auto view = Containers::arrayCast(buffer); + view[0] = {0.0f, {3.0f, 1.0f, 0.1f}, Quaternion::rotation(45.0_degf, Vector3::yAxis())}; + view[1] = {5.0f, {0.3f, 0.6f, 1.0f}, Quaternion::rotation(20.0_degf, Vector3::yAxis())}; + view[2] = {7.5f, {1.0f, 0.3f, 2.1f}, Quaternion{}}; + + const int state = 5; + AnimationData a{std::move(buffer), Containers::Array{Containers::InPlaceInit, { + {AnimationTrackType::Vector3, + AnimationTrackTarget::Translation3D, 42, + Animation::TrackView{ + {&view[0].time, view.size(), sizeof(Data)}, + {&view[0].position, view.size(), sizeof(Data)}, + Animation::Interpolation::Constant, + animationInterpolatorFor(Animation::Interpolation::Constant)}}, + {AnimationTrackType::Quaternion, + AnimationTrackTarget::Rotation3D, 1337, + Animation::TrackView{ + {&view[0].time, view.size(), sizeof(Data)}, + {&view[0].rotation, view.size(), sizeof(Data)}, + Animation::Interpolation::Linear, + animationInterpolatorFor(Animation::Interpolation::Linear)}} + }}, &state}; + + AnimationData b{std::move(a)}; + + CORRADE_COMPARE(b.data().size(), sizeof(Data)*3); + CORRADE_COMPARE(b.trackCount(), 2); + CORRADE_COMPARE(b.importerState(), &state); + + { + CORRADE_COMPARE(b.trackType(0), AnimationTrackType::Vector3); + CORRADE_COMPARE(b.trackResultType(0), AnimationTrackType::Vector3); + CORRADE_COMPARE(b.trackTarget(0), AnimationTrackTarget::Translation3D); + CORRADE_COMPARE(b.trackTargetId(0), 42); + + Animation::TrackView track = b.track(0); + CORRADE_COMPARE(track.keys().size(), 3); + CORRADE_COMPARE(track.values().size(), 3); + CORRADE_COMPARE(track.interpolation(), Animation::Interpolation::Constant); + CORRADE_COMPARE(track.at(2.5f), (Vector3{3.0f, 1.0f, 0.1f})); + } { + CORRADE_COMPARE(b.trackType(1), AnimationTrackType::Quaternion); + CORRADE_COMPARE(b.trackResultType(1), AnimationTrackType::Quaternion); + CORRADE_COMPARE(b.trackTarget(1), AnimationTrackTarget::Rotation3D); + CORRADE_COMPARE(b.trackTargetId(1), 1337); + + Animation::TrackView track = b.track(1); + CORRADE_COMPARE(track.keys().size(), 3); + CORRADE_COMPARE(track.values().size(), 3); + CORRADE_COMPARE(track.interpolation(), Animation::Interpolation::Linear); + CORRADE_COMPARE(track.at(2.5f), Quaternion::rotation(32.5_degf, Vector3::yAxis())); + } + + int other; + AnimationData c{nullptr, nullptr, &other}; + c = std::move(b); + + CORRADE_COMPARE(c.data().size(), sizeof(Data)*3); + CORRADE_COMPARE(c.trackCount(), 2); + CORRADE_COMPARE(c.importerState(), &state); + + { + CORRADE_COMPARE(c.trackType(0), AnimationTrackType::Vector3); + CORRADE_COMPARE(c.trackResultType(0), AnimationTrackType::Vector3); + CORRADE_COMPARE(c.trackTarget(0), AnimationTrackTarget::Translation3D); + CORRADE_COMPARE(c.trackTargetId(0), 42); + + Animation::TrackView track = c.track(0); + CORRADE_COMPARE(track.keys().size(), 3); + CORRADE_COMPARE(track.values().size(), 3); + CORRADE_COMPARE(track.interpolation(), Animation::Interpolation::Constant); + CORRADE_COMPARE(track.at(2.5f), (Vector3{3.0f, 1.0f, 0.1f})); + } { + CORRADE_COMPARE(c.trackType(1), AnimationTrackType::Quaternion); + CORRADE_COMPARE(c.trackResultType(1), AnimationTrackType::Quaternion); + CORRADE_COMPARE(c.trackTarget(1), AnimationTrackTarget::Rotation3D); + CORRADE_COMPARE(c.trackTargetId(1), 1337); + + Animation::TrackView track = c.track(1); + CORRADE_COMPARE(track.keys().size(), 3); + CORRADE_COMPARE(track.values().size(), 3); + CORRADE_COMPARE(track.interpolation(), Animation::Interpolation::Linear); + CORRADE_COMPARE(track.at(2.5f), Quaternion::rotation(32.5_degf, Vector3::yAxis())); + } +} + +void AnimationDataTest::trackCustomResultType() { + using namespace Math::Literals; + + struct Data { + Float time; + Vector3i position; + }; + Containers::Array buffer{sizeof(Data)*3}; + auto view = Containers::arrayCast(buffer); + view[0] = {0.0f, {300, 100, 10}}; + view[1] = {5.0f, {30, 60, 100}}; + + AnimationData data{std::move(buffer), Containers::Array{Containers::InPlaceInit, { + {AnimationTrackType::Vector3i, + AnimationTrackType::Vector3, + AnimationTrackTarget::Scaling3D, 0, + Animation::TrackView{ + {&view[0].time, view.size(), sizeof(Data)}, + {&view[0].position, view.size(), sizeof(Data)}, + [](const Vector3i& a, const Vector3i& b, Float t) -> Vector3 { + return Math::lerp(Vector3{a}*0.01f, Vector3{b}*0.01f, t); + }}}} + }}; + + CORRADE_COMPARE((data.track(0).at(2.5f)), (Vector3{1.65f, 0.8f, 0.55f})); +} + +void AnimationDataTest::trackWrongIndex() { + std::ostringstream out; + Error redirectError{&out}; + + AnimationData data{nullptr, nullptr}; + data.trackType(0); + data.trackResultType(0); + data.trackTarget(0); + data.trackTargetId(0); + data.track(0); + + CORRADE_COMPARE(out.str(), + "Trade::AnimationData::trackType(): index out of range\n" + "Trade::AnimationData::trackResultType(): index out of range\n" + "Trade::AnimationData::trackTarget(): index out of range\n" + "Trade::AnimationData::trackTargetId(): index out of range\n" + "Trade::AnimationData::track(): index out of range\n"); +} + +void AnimationDataTest::trackWrongType() { + std::ostringstream out; + Error redirectError{&out}; + + AnimationData data{nullptr, Containers::Array{Containers::InPlaceInit, { + {AnimationTrackType::Vector3i, + AnimationTrackType::Vector3, + AnimationTrackTarget::Scaling3D, 0, {}} + }}}; + + data.track(0); + + CORRADE_COMPARE(out.str(), "Trade::AnimationData::track(): improper type requested for Trade::AnimationTrackType::Vector3i\n"); +} + +void AnimationDataTest::trackWrongResultType() { + std::ostringstream out; + Error redirectError{&out}; + + AnimationData data{nullptr, Containers::Array{Containers::InPlaceInit, { + {AnimationTrackType::Vector3i, + AnimationTrackType::Vector3, + AnimationTrackTarget::Scaling3D, 0, {}} + }}}; + + data.track(0); + + CORRADE_COMPARE(out.str(), "Trade::AnimationData::track(): improper result type requested for Trade::AnimationTrackType::Vector3\n"); +} + +void AnimationDataTest::debugAnimationTrackType() { + std::ostringstream out; + + Debug{&out} << AnimationTrackType::DualQuaternion << AnimationTrackType(0xde); + CORRADE_COMPARE(out.str(), "Trade::AnimationTrackType::DualQuaternion Trade::AnimationTrackType(0xde)\n"); +} + +void AnimationDataTest::debugAnimationTrackTarget() { + std::ostringstream out; + + Debug{&out} << AnimationTrackTarget::Rotation3D << AnimationTrackTarget(135) << AnimationTrackTarget(0x42); + CORRADE_COMPARE(out.str(), "Trade::AnimationTrackTarget::Rotation3D Trade::AnimationTrackTarget::Custom(135) Trade::AnimationTrackTarget(0x42)\n"); +} + +}}} + +CORRADE_TEST_MAIN(Magnum::Trade::Test::AnimationDataTest) diff --git a/src/Magnum/Trade/Test/CMakeLists.txt b/src/Magnum/Trade/Test/CMakeLists.txt index 7c5fc1483..9e49cc7cb 100644 --- a/src/Magnum/Trade/Test/CMakeLists.txt +++ b/src/Magnum/Trade/Test/CMakeLists.txt @@ -36,10 +36,13 @@ configure_file(${CMAKE_CURRENT_SOURCE_DIR}/configure.h.cmake corrade_add_test(TradeAbstractImageConverterTest AbstractImageConverterTest.cpp LIBRARIES MagnumTradeTestLib) target_include_directories(TradeAbstractImageConverterTest PRIVATE ${CMAKE_CURRENT_BINARY_DIR}) + corrade_add_test(TradeAbstractImporterTest AbstractImporterTest.cpp LIBRARIES MagnumTradeTestLib FILES file.bin) target_include_directories(TradeAbstractImporterTest PRIVATE ${CMAKE_CURRENT_BINARY_DIR}) + +corrade_add_test(TradeAnimationDataTest AnimationDataTest.cpp LIBRARIES MagnumTradeTestLib) corrade_add_test(TradeCameraDataTest CameraDataTest.cpp LIBRARIES MagnumTrade) corrade_add_test(TradeImageDataTest ImageDataTest.cpp LIBRARIES MagnumTradeTestLib) corrade_add_test(TradeLightDataTest LightDataTest.cpp LIBRARIES MagnumTrade) @@ -51,9 +54,14 @@ corrade_add_test(TradeObjectData3DTest ObjectData3DTest.cpp LIBRARIES MagnumTrad corrade_add_test(TradeSceneDataTest SceneDataTest.cpp LIBRARIES MagnumTrade) corrade_add_test(TradeTextureDataTest TextureDataTest.cpp LIBRARIES MagnumTrade) +set_property(TARGET + TradeAnimationDataTest + APPEND PROPERTY COMPILE_DEFINITIONS "CORRADE_GRACEFUL_ASSERT") + set_target_properties( TradeAbstractImageConverterTest TradeAbstractImporterTest + TradeAnimationDataTest TradeCameraDataTest TradeImageDataTest TradeLightDataTest diff --git a/src/Magnum/Trade/Trade.h b/src/Magnum/Trade/Trade.h index a808cd5a2..ee990d2b3 100644 --- a/src/Magnum/Trade/Trade.h +++ b/src/Magnum/Trade/Trade.h @@ -40,6 +40,12 @@ enum class ImporterFileCallbackPolicy: UnsignedByte; class AbstractImporter; class AbstractMaterialData; + +enum class AnimationTrackTarget: UnsignedByte; +enum class AnimationTrackType: UnsignedByte; +class AnimationTrackData; +class AnimationData; + class CameraData; template class ImageData;