From 10b4c069ebe82198360eb04e0510080bdcefd8c9 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Vladim=C3=ADr=20Vondru=C5=A1?= Date: Thu, 5 Jul 2018 20:51:30 +0200 Subject: [PATCH] Trade: support separate translation/rotation/scaling in ObjectData. Doesn't make any backwards-incompatible change -- plugins can still export the transformation as matrix and users can still access the combined one even if separate transformations are used. Yay! --- doc/changelog.dox | 7 ++ doc/snippets/MagnumTrade.cpp | 24 ++++ src/Magnum/Trade/CMakeLists.txt | 6 +- src/Magnum/Trade/MeshObjectData2D.cpp | 2 + src/Magnum/Trade/MeshObjectData2D.h | 16 ++- src/Magnum/Trade/MeshObjectData3D.cpp | 2 + src/Magnum/Trade/MeshObjectData3D.h | 16 ++- src/Magnum/Trade/ObjectData2D.cpp | 53 ++++++++- src/Magnum/Trade/ObjectData2D.h | 130 ++++++++++++++++++++- src/Magnum/Trade/ObjectData3D.cpp | 53 ++++++++- src/Magnum/Trade/ObjectData3D.h | 130 ++++++++++++++++++++- src/Magnum/Trade/Test/CMakeLists.txt | 4 +- src/Magnum/Trade/Test/ObjectData2DTest.cpp | 121 ++++++++++++++++++- src/Magnum/Trade/Test/ObjectData3DTest.cpp | 122 ++++++++++++++++++- 14 files changed, 663 insertions(+), 23 deletions(-) diff --git a/doc/changelog.dox b/doc/changelog.dox index 148bf7cd20..f8692c8dd5 100644 --- a/doc/changelog.dox +++ b/doc/changelog.dox @@ -68,6 +68,13 @@ See also: @ref Platform::AndroidApplication. This also makes the default framebuffer parameters consistent across the implementations. +@subsubsection changelog-latest-new-trade Trade library + +- @ref Trade::ObjectData2D and @ref Trade::ObjectData3D now support also + separate translation / rotation / scaling specification instead of a + combined transformation matrix. See @ref Trade::ObjectData2D::transformation() + and @ref Trade::ObjectData3D::transformation() for more information. + @subsection changelog-latest-changes Changes and improvements @subsubsection changelog-latest-changes-audio Audio library diff --git a/doc/snippets/MagnumTrade.cpp b/doc/snippets/MagnumTrade.cpp index 4cf0daaa9c..7a10ffdb92 100644 --- a/doc/snippets/MagnumTrade.cpp +++ b/doc/snippets/MagnumTrade.cpp @@ -26,6 +26,8 @@ #include "Magnum/PixelFormat.h" #include "Magnum/Trade/AbstractImporter.h" #include "Magnum/Trade/ImageData.h" +#include "Magnum/Trade/ObjectData2D.h" +#include "Magnum/Trade/ObjectData3D.h" #ifdef MAGNUM_TARGET_GL #include "Magnum/GL/Texture.h" #endif @@ -67,4 +69,26 @@ else } #endif +{ +Trade::ObjectData2D& foo(); +Trade::ObjectData2D& data = foo(); +/* [ObjectData2D-transformation] */ +Matrix3 transformation = + Matrix3::from(data.rotation().toMatrix(), data.translation())* + Matrix3::scaling(data.scaling()); +/* [ObjectData2D-transformation] */ +static_cast(transformation); +} + +{ +Trade::ObjectData3D& bar(); +Trade::ObjectData3D& data = bar(); +/* [ObjectData3D-transformation] */ +Matrix4 transformation = + Matrix4::from(data.rotation().toMatrix(), data.translation())* + Matrix4::scaling(data.scaling()); +/* [ObjectData3D-transformation] */ +static_cast(transformation); +} + } diff --git a/src/Magnum/Trade/CMakeLists.txt b/src/Magnum/Trade/CMakeLists.txt index e4dbc704e4..285c960898 100644 --- a/src/Magnum/Trade/CMakeLists.txt +++ b/src/Magnum/Trade/CMakeLists.txt @@ -34,14 +34,14 @@ set(MagnumTrade_SRCS MeshData3D.cpp MeshObjectData2D.cpp MeshObjectData3D.cpp - ObjectData2D.cpp - ObjectData3D.cpp PhongMaterialData.cpp SceneData.cpp TextureData.cpp) set(MagnumTrade_GracefulAssert_SRCS - ImageData.cpp) + ImageData.cpp + ObjectData2D.cpp + ObjectData3D.cpp) set(MagnumTrade_HEADERS AbstractImporter.h diff --git a/src/Magnum/Trade/MeshObjectData2D.cpp b/src/Magnum/Trade/MeshObjectData2D.cpp index 33b25b63fc..dcc2a59a1f 100644 --- a/src/Magnum/Trade/MeshObjectData2D.cpp +++ b/src/Magnum/Trade/MeshObjectData2D.cpp @@ -29,4 +29,6 @@ namespace Magnum { namespace Trade { MeshObjectData2D::MeshObjectData2D(std::vector children, const Matrix3& transformation, const UnsignedInt instance, const Int material, const void* const importerState): ObjectData2D{std::move(children), transformation, ObjectInstanceType2D::Mesh, instance, importerState}, _material{material} {} +MeshObjectData2D::MeshObjectData2D(std::vector children, const Vector2& translation, const Complex& rotation, const Vector2& scaling, const UnsignedInt instance, const Int material, const void* const importerState): ObjectData2D{std::move(children), translation, rotation, scaling, ObjectInstanceType2D::Mesh, instance, importerState}, _material{material} {} + }} diff --git a/src/Magnum/Trade/MeshObjectData2D.h b/src/Magnum/Trade/MeshObjectData2D.h index 408285684c..9a1b871c17 100644 --- a/src/Magnum/Trade/MeshObjectData2D.h +++ b/src/Magnum/Trade/MeshObjectData2D.h @@ -42,7 +42,7 @@ Provides access to material information for given mesh instance. class MAGNUM_TRADE_EXPORT MeshObjectData2D: public ObjectData2D { public: /** - * @brief Constructor + * @brief Construct with combined transformation * @param children Child objects * @param transformation Transformation (relative to parent) * @param instance Instance ID @@ -53,6 +53,20 @@ class MAGNUM_TRADE_EXPORT MeshObjectData2D: public ObjectData2D { */ explicit MeshObjectData2D(std::vector children, const Matrix3& transformation, UnsignedInt instance, Int material, const void* importerState = nullptr); + /** + * @brief Construct with separate transformations + * @param children Child objects + * @param translation Translation (relative to parent) + * @param rotation Rotation (relative to parent) + * @param scaling Scaling (relative to parent) + * @param instance Instance ID + * @param material Material ID or `-1` + * @param importerState Importer-specific state + * + * Creates object with mesh instance type. + */ + explicit MeshObjectData2D(std::vector children, const Vector2& translation, const Complex& rotation, const Vector2& scaling, UnsignedInt instance, Int material, const void* importerState = nullptr); + /** @brief Copying is not allowed */ MeshObjectData2D(const MeshObjectData2D&) = delete; diff --git a/src/Magnum/Trade/MeshObjectData3D.cpp b/src/Magnum/Trade/MeshObjectData3D.cpp index 5421bcbb97..6f54ff6b43 100644 --- a/src/Magnum/Trade/MeshObjectData3D.cpp +++ b/src/Magnum/Trade/MeshObjectData3D.cpp @@ -29,4 +29,6 @@ namespace Magnum { namespace Trade { MeshObjectData3D::MeshObjectData3D(std::vector children, const Matrix4& transformation, const UnsignedInt instance, const Int material, const void* const importerState): ObjectData3D{std::move(children), transformation, ObjectInstanceType3D::Mesh, instance, importerState}, _material{material} {} +MeshObjectData3D::MeshObjectData3D(std::vector children, const Vector3& translation, const Quaternion& rotation, const Vector3& scaling, const UnsignedInt instance, const Int material, const void* const importerState): ObjectData3D{std::move(children), translation, rotation, scaling, ObjectInstanceType3D::Mesh, instance, importerState}, _material{material} {} + }} diff --git a/src/Magnum/Trade/MeshObjectData3D.h b/src/Magnum/Trade/MeshObjectData3D.h index c90d244fbc..212f8d40f5 100644 --- a/src/Magnum/Trade/MeshObjectData3D.h +++ b/src/Magnum/Trade/MeshObjectData3D.h @@ -42,7 +42,7 @@ Provides access to material information for given mesh instance. class MAGNUM_TRADE_EXPORT MeshObjectData3D: public ObjectData3D { public: /** - * @brief Constructor + * @brief Construct with combined transformation * @param children Child objects * @param transformation Transformation (relative to parent) * @param instance Instance ID @@ -53,6 +53,20 @@ class MAGNUM_TRADE_EXPORT MeshObjectData3D: public ObjectData3D { */ explicit MeshObjectData3D(std::vector children, const Matrix4& transformation, UnsignedInt instance, Int material, const void* importerState = nullptr); + /** + * @brief Construct with separate transformations + * @param children Child objects + * @param translation Translation (relative to parent) + * @param rotation Rotation (relative to parent) + * @param scaling Scaling (relative to parent) + * @param instance Instance ID + * @param material Material ID or `-1` + * @param importerState Importer-specific state + * + * Creates object with mesh instance type. + */ + explicit MeshObjectData3D(std::vector children, const Vector3& translation, const Quaternion& rotation, const Vector3& scaling, UnsignedInt instance, Int material, const void* importerState = nullptr); + /** @brief Copying is not allowed */ MeshObjectData3D(const MeshObjectData3D&) = delete; diff --git a/src/Magnum/Trade/ObjectData2D.cpp b/src/Magnum/Trade/ObjectData2D.cpp index 7980fb0bf8..f19a47e0b9 100644 --- a/src/Magnum/Trade/ObjectData2D.cpp +++ b/src/Magnum/Trade/ObjectData2D.cpp @@ -25,11 +25,17 @@ #include "ObjectData2D.h" +#include + namespace Magnum { namespace Trade { -ObjectData2D::ObjectData2D(std::vector children, const Matrix3& transformation, const ObjectInstanceType2D instanceType, const UnsignedInt instance, const void* const importerState): _children{std::move(children)}, _transformation{transformation}, _instanceType{instanceType}, _instance{Int(instance)}, _importerState{importerState} {} +ObjectData2D::ObjectData2D(std::vector children, const Matrix3& transformation, const ObjectInstanceType2D instanceType, const UnsignedInt instance, const void* const importerState): _children{std::move(children)}, _transformation{transformation}, _instanceType{instanceType}, _flags{}, _instance{Int(instance)}, _importerState{importerState} {} + +ObjectData2D::ObjectData2D(std::vector children, const Vector2& translation, const Complex& rotation, const Vector2& scaling, const ObjectInstanceType2D instanceType, const UnsignedInt instance, const void* const importerState): _children{std::move(children)}, _transformation{translation, rotation, scaling}, _instanceType{instanceType}, _flags{ObjectFlag2D::HasTransformationRotationScaling}, _instance{Int(instance)}, _importerState{importerState} {} + +ObjectData2D::ObjectData2D(std::vector children, const Matrix3& transformation, const void* const importerState): _children{std::move(children)}, _transformation{transformation}, _instanceType{ObjectInstanceType2D::Empty}, _flags{}, _instance{-1}, _importerState{importerState} {} -ObjectData2D::ObjectData2D(std::vector children, const Matrix3& transformation, const void* const importerState): _children{std::move(children)}, _transformation{transformation}, _instanceType{ObjectInstanceType2D::Empty}, _instance{-1}, _importerState{importerState} {} +ObjectData2D::ObjectData2D(std::vector children, const Vector2& translation, const Complex& rotation, const Vector2& scaling, const void* const importerState): _children{std::move(children)}, _transformation{translation, rotation, scaling}, _instanceType{ObjectInstanceType2D::Empty}, _flags{ObjectFlag2D::HasTransformationRotationScaling}, _instance{-1}, _importerState{importerState} {} ObjectData2D::ObjectData2D(ObjectData2D&&) #if !defined(__GNUC__) || __GNUC__*100 + __GNUC_MINOR__ != 409 @@ -45,6 +51,32 @@ ObjectData2D& ObjectData2D::operator=(ObjectData2D&&) #endif = default; +Vector2 ObjectData2D::translation() const { + CORRADE_ASSERT(_flags & ObjectFlag2D::HasTransformationRotationScaling, + "Trade::ObjectData2D::translation(): object has only a combined transformation", {}); + return _transformation.trs.translation; +} + +Complex ObjectData2D::rotation() const { + CORRADE_ASSERT(_flags & ObjectFlag2D::HasTransformationRotationScaling, + "Trade::ObjectData2D::rotation(): object has only a combined transformation", {}); + return _transformation.trs.rotation; +} + +Vector2 ObjectData2D::scaling() const { + CORRADE_ASSERT(_flags & ObjectFlag2D::HasTransformationRotationScaling, + "Trade::ObjectData2D::scaling(): object has only a combined transformation", {}); + return _transformation.trs.scaling; +} + +Matrix3 ObjectData2D::transformation() const { + if(_flags & ObjectFlag2D::HasTransformationRotationScaling) + return Matrix3::from(_transformation.trs.rotation.toMatrix(), + _transformation.trs.translation)* + Matrix3::scaling(_transformation.trs.scaling); + return _transformation.matrix; +} + #ifndef DOXYGEN_GENERATING_OUTPUT Debug& operator<<(Debug& debug, ObjectInstanceType2D value) { switch(value) { @@ -59,6 +91,23 @@ Debug& operator<<(Debug& debug, ObjectInstanceType2D value) { return debug << "Trade::ObjectInstanceType2D(" << Debug::nospace << reinterpret_cast(UnsignedByte(value)) << Debug::nospace << ")"; } + +Debug& operator<<(Debug& debug, ObjectFlag2D value) { + switch(value) { + /* LCOV_EXCL_START */ + #define _c(value) case ObjectFlag2D::value: return debug << "Trade::ObjectFlag2D::" #value; + _c(HasTransformationRotationScaling) + #undef _c + /* LCOV_EXCL_STOP */ + } + + return debug << "Trade::ObjectFlag2D(" << Debug::nospace << reinterpret_cast(UnsignedByte(value)) << Debug::nospace << ")"; +} + +Debug& operator<<(Debug& debug, ObjectFlags2D value) { + return enumSetDebugOutput(debug, value, "Trade::ObjectFlags2D{}", { + ObjectFlag2D::HasTransformationRotationScaling}); +} #endif }} diff --git a/src/Magnum/Trade/ObjectData2D.h b/src/Magnum/Trade/ObjectData2D.h index 83f1d5c44c..1455472f28 100644 --- a/src/Magnum/Trade/ObjectData2D.h +++ b/src/Magnum/Trade/ObjectData2D.h @@ -33,6 +33,7 @@ #include "Magnum/Magnum.h" #include "Magnum/Math/Matrix3.h" +#include "Magnum/Math/Complex.h" #include "Magnum/Trade/visibility.h" namespace Magnum { namespace Trade { @@ -54,6 +55,31 @@ enum class ObjectInstanceType2D: UnsignedByte { Empty /**< Empty */ }; +/** +@brief 2D object flag + +@see @ref ObjectFlags2D, @ref ObjectData2D::flags() +*/ +enum class ObjectFlag2D: UnsignedByte { + /** + * The object provides separate translation / rotation / scaling + * properties. The @ref ObjectData2D::transformation() matrix returns them + * combined, but it's possible to access particular parts of the + * transformation using @ref ObjectData2D::translation(), + * @ref ObjectData2D::rotation() and @ref ObjectData2D::scaling(). + */ + HasTransformationRotationScaling = 1 << 0 +}; + +/** +@brief 2D object flags + +@see @ref ObjectData2D::flags() +*/ +typedef Containers::EnumSet ObjectFlags2D; + +CORRADE_ENUMSET_OPERATORS(ObjectFlags2D) + /** @brief Two-dimensional object data @@ -63,7 +89,7 @@ Provides access to object transformation and hierarchy. class MAGNUM_TRADE_EXPORT ObjectData2D { public: /** - * @brief Constructor + * @brief Construct with combined transformation * @param children Child objects * @param transformation Transformation (relative to parent) * @param instanceType Instance type @@ -73,13 +99,35 @@ class MAGNUM_TRADE_EXPORT ObjectData2D { explicit ObjectData2D(std::vector children, const Matrix3& transformation, ObjectInstanceType2D instanceType, UnsignedInt instance, const void* importerState = nullptr); /** - * @brief Constructor for empty instance + * @brief Construct with separate transformations + * @param children Child objects + * @param translation Translation (relative to parent) + * @param rotation Rotation (relative to parent) + * @param scaling Scaling (relative to parent) + * @param instanceType Instance type + * @param instance Instance ID + * @param importerState Importer-specific state + */ + explicit ObjectData2D(std::vector children, const Vector2& translation, const Complex& rotation, const Vector2& scaling, ObjectInstanceType2D instanceType, UnsignedInt instance, const void* importerState = nullptr); + + /** + * @brief Construct empty instance with combined transformation * @param children Child objects * @param transformation Transformation (relative to parent) * @param importerState Importer-specific state */ explicit ObjectData2D(std::vector children, const Matrix3& transformation, const void* importerState = nullptr); + /** + * @brief Construct empty instance with separate transformations + * @param children Child objects + * @param translation Translation (relative to parent) + * @param rotation Rotation (relative to parent) + * @param scaling Scaling (relative to parent) + * @param importerState Importer-specific state + */ + explicit ObjectData2D(std::vector children, const Vector2& translation, const Complex& rotation, const Vector2& scaling, const void* importerState = nullptr); + /** @brief Copying is not allowed */ ObjectData2D(const ObjectData2D&) = delete; @@ -113,8 +161,62 @@ class MAGNUM_TRADE_EXPORT ObjectData2D { std::vector& children() { return _children; } const std::vector& children() const { return _children; } /**< @overload */ - /** @brief Transformation (relative to parent) */ - Matrix3 transformation() const { return _transformation; } + /** @brief Flags */ + ObjectFlags2D flags() const { return _flags; } + + /** + * @brief Translation (relative to parent) + * + * Available only if @ref ObjectFlag2D::HasTransformationRotationScaling + * is set, use @ref transformation() otherwise. Applied as last in the + * final transformation, see @ref transformation() for more + * information. + * @see @ref flags(), @ref rotation(), @ref scaling() + */ + Vector2 translation() const; + + /** + * @brief Rotation (relative to parent) + * + * Available only if @ref ObjectFlag2D::HasTransformationRotationScaling + * is set, use @ref transformation() otherwise. Applied second in the + * final transformation, see @ref transformation() for more + * information. + * @see @ref flags(), @ref translation(), @ref scaling() + */ + Complex rotation() const; + + /** + * @brief Scaling (relative to parent) + * + * Available only if @ref ObjectFlag2D::HasTransformationRotationScaling + * is set, use @ref transformation() otherwise. Applied as first in the + * final transformation, see @ref transformation() for more + * information. + * @see @ref flags(), @ref translation(), @ref rotation() + */ + Vector2 scaling() const; + + /** + * @brief Transformation (relative to parent) + * + * If @ref ObjectFlag2D::HasTransformationRotationScaling is not set, + * returns the imported object transformation matrix. Otherwise + * calculates the final transformation matrix @f$ \boldsymbol{M} @f$ + * from translation, rotation and scaling matrices @f$ \boldsymbol{T} @f$, + * @f$ \boldsymbol{R} @f$, @f$ \boldsymbol{S} @f$ created from + * @ref translation(), @ref rotation() and @ref scaling() in the + * following order: @f[ + * \boldsymbol{M} = \boldsymbol{T} \boldsymbol{R} \boldsymbol{S} + * @f] + * + * The corresponding code is as follows: + * + * @snippet MagnumTrade.cpp ObjectData2D-transformation + * + * @see @ref flags() + */ + Matrix3 transformation() const; /** * @brief Instance type @@ -141,8 +243,20 @@ class MAGNUM_TRADE_EXPORT ObjectData2D { private: std::vector _children; - Matrix3 _transformation; + union Transformation { + Transformation(const Matrix3& matrix): matrix{matrix} {} + Transformation(const Vector2& translation, const Complex& rotation, const Vector2& scaling): trs{translation, rotation, scaling} {} + ~Transformation() {} + + Matrix3 matrix; + struct { + Vector2 translation; + Complex rotation; + Vector2 scaling; + } trs; + } _transformation; ObjectInstanceType2D _instanceType; + ObjectFlags2D _flags; Int _instance; const void* _importerState; }; @@ -150,6 +264,12 @@ class MAGNUM_TRADE_EXPORT ObjectData2D { /** @debugoperatorenum{ObjectInstanceType2D} */ MAGNUM_TRADE_EXPORT Debug& operator<<(Debug& debug, ObjectInstanceType2D value); +/** @debugoperatorenum{ObjectFlag2D} */ +MAGNUM_TRADE_EXPORT Debug& operator<<(Debug& debug, ObjectFlag2D value); + +/** @debugoperatorenum{ObjectFlags2D} */ +MAGNUM_TRADE_EXPORT Debug& operator<<(Debug& debug, ObjectFlags2D value); + }} #endif diff --git a/src/Magnum/Trade/ObjectData3D.cpp b/src/Magnum/Trade/ObjectData3D.cpp index 1b01c0a89d..c5c0c9c7f8 100644 --- a/src/Magnum/Trade/ObjectData3D.cpp +++ b/src/Magnum/Trade/ObjectData3D.cpp @@ -25,11 +25,17 @@ #include "ObjectData3D.h" +#include + namespace Magnum { namespace Trade { -ObjectData3D::ObjectData3D(std::vector children, const Matrix4& transformation, const ObjectInstanceType3D instanceType, const UnsignedInt instance, const void* const importerState): _children{std::move(children)}, _transformation{transformation}, _instanceType{instanceType}, _instance{Int(instance)}, _importerState{importerState} {} +ObjectData3D::ObjectData3D(std::vector children, const Matrix4& transformation, const ObjectInstanceType3D instanceType, const UnsignedInt instance, const void* const importerState): _children{std::move(children)}, _transformation{transformation}, _instanceType{instanceType}, _flags{}, _instance{Int(instance)}, _importerState{importerState} {} + +ObjectData3D::ObjectData3D(std::vector children, const Vector3& translation, const Quaternion& rotation, const Vector3& scaling, const ObjectInstanceType3D instanceType, const UnsignedInt instance, const void* const importerState): _children{std::move(children)}, _transformation{translation, rotation, scaling}, _instanceType{instanceType}, _flags{ObjectFlag3D::HasTransformationRotationScaling}, _instance{Int(instance)}, _importerState{importerState} {} + +ObjectData3D::ObjectData3D(std::vector children, const Matrix4& transformation, const void* const importerState): _children{std::move(children)}, _transformation{transformation}, _instanceType{ObjectInstanceType3D::Empty}, _flags{}, _instance{-1}, _importerState{importerState} {} -ObjectData3D::ObjectData3D(std::vector children, const Matrix4& transformation, const void* const importerState): _children{std::move(children)}, _transformation{transformation}, _instanceType{ObjectInstanceType3D::Empty}, _instance{-1}, _importerState{importerState} {} +ObjectData3D::ObjectData3D(std::vector children, const Vector3& translation, const Quaternion& rotation, const Vector3& scaling, const void* const importerState): _children{std::move(children)}, _transformation{translation, rotation, scaling}, _instanceType{ObjectInstanceType3D::Empty}, _flags{ObjectFlag3D::HasTransformationRotationScaling}, _instance{-1}, _importerState{importerState} {} ObjectData3D::ObjectData3D(ObjectData3D&&) #if !defined(__GNUC__) || __GNUC__*100 + __GNUC_MINOR__ != 409 @@ -45,6 +51,32 @@ ObjectData3D& ObjectData3D::operator=(ObjectData3D&&) #endif = default; +Vector3 ObjectData3D::translation() const { + CORRADE_ASSERT(_flags & ObjectFlag3D::HasTransformationRotationScaling, + "Trade::ObjectData3D::translation(): object has only a combined transformation", {}); + return _transformation.trs.translation; +} + +Quaternion ObjectData3D::rotation() const { + CORRADE_ASSERT(_flags & ObjectFlag3D::HasTransformationRotationScaling, + "Trade::ObjectData3D::rotation(): object has only a combined transformation", {}); + return _transformation.trs.rotation; +} + +Vector3 ObjectData3D::scaling() const { + CORRADE_ASSERT(_flags & ObjectFlag3D::HasTransformationRotationScaling, + "Trade::ObjectData3D::scaling(): object has only a combined transformation", {}); + return _transformation.trs.scaling; +} + +Matrix4 ObjectData3D::transformation() const { + if(_flags & ObjectFlag3D::HasTransformationRotationScaling) + return Matrix4::from(_transformation.trs.rotation.toMatrix(), + _transformation.trs.translation)* + Matrix4::scaling(_transformation.trs.scaling); + return _transformation.matrix; +} + #ifndef DOXYGEN_GENERATING_OUTPUT Debug& operator<<(Debug& debug, ObjectInstanceType3D value) { switch(value) { @@ -60,6 +92,23 @@ Debug& operator<<(Debug& debug, ObjectInstanceType3D value) { return debug << "Trade::ObjectInstanceType3D(" << Debug::nospace << reinterpret_cast(UnsignedByte(value)) << Debug::nospace << ")"; } + +Debug& operator<<(Debug& debug, ObjectFlag3D value) { + switch(value) { + /* LCOV_EXCL_START */ + #define _c(value) case ObjectFlag3D::value: return debug << "Trade::ObjectFlag3D::" #value; + _c(HasTransformationRotationScaling) + #undef _c + /* LCOV_EXCL_STOP */ + } + + return debug << "Trade::ObjectFlag3D(" << Debug::nospace << reinterpret_cast(UnsignedByte(value)) << Debug::nospace << ")"; +} + +Debug& operator<<(Debug& debug, ObjectFlags3D value) { + return enumSetDebugOutput(debug, value, "Trade::ObjectFlags3D{}", { + ObjectFlag3D::HasTransformationRotationScaling}); +} #endif }} diff --git a/src/Magnum/Trade/ObjectData3D.h b/src/Magnum/Trade/ObjectData3D.h index 96276e59a8..70043798f9 100644 --- a/src/Magnum/Trade/ObjectData3D.h +++ b/src/Magnum/Trade/ObjectData3D.h @@ -33,6 +33,7 @@ #include "Magnum/Magnum.h" #include "Magnum/Math/Matrix4.h" +#include "Magnum/Math/Quaternion.h" #include "Magnum/Trade/visibility.h" namespace Magnum { namespace Trade { @@ -55,6 +56,31 @@ enum class ObjectInstanceType3D: UnsignedByte { Empty /**< Empty */ }; +/** +@brief 3D object flag + +@see @ref ObjectFlags3D, @ref ObjectData3D::flags() +*/ +enum class ObjectFlag3D: UnsignedByte { + /** + * The object provides separate translation / rotation / scaling + * properties. The @ref ObjectData3D::transformation() matrix returns them + * combined, but it's possible to access particular parts of the + * transformation using @ref ObjectData3D::translation(), + * @ref ObjectData3D::rotation() and @ref ObjectData3D::scaling(). + */ + HasTransformationRotationScaling = 1 << 0 +}; + +/** +@brief 3D object flags + +@see @ref ObjectData3D::flags() +*/ +typedef Containers::EnumSet ObjectFlags3D; + +CORRADE_ENUMSET_OPERATORS(ObjectFlags3D) + /** @brief Three-dimensional object data @@ -64,7 +90,7 @@ Provides access to object transformation and hierarchy. class MAGNUM_TRADE_EXPORT ObjectData3D { public: /** - * @brief Constructor + * @brief Construct with combined transformation * @param children Child objects * @param transformation Transformation (relative to parent) * @param instanceType Instance type @@ -74,13 +100,35 @@ class MAGNUM_TRADE_EXPORT ObjectData3D { explicit ObjectData3D(std::vector children, const Matrix4& transformation, ObjectInstanceType3D instanceType, UnsignedInt instance, const void* importerState = nullptr); /** - * @brief Constructor for empty instance + * @brief Construct with separate transformations + * @param children Child objects + * @param translation Translation (relative to parent) + * @param rotation Rotation (relative to parent) + * @param scaling Scaling (relative to parent) + * @param instanceType Instance type + * @param instance Instance ID + * @param importerState Importer-specific state + */ + explicit ObjectData3D(std::vector children, const Vector3& translation, const Quaternion& rotation, const Vector3& scaling, ObjectInstanceType3D instanceType, UnsignedInt instance, const void* importerState = nullptr); + + /** + * @brief Construct empty instance with combined transformation * @param children Child objects * @param transformation Transformation (relative to parent) * @param importerState Importer-specific state */ explicit ObjectData3D(std::vector children, const Matrix4& transformation, const void* importerState = nullptr); + /** + * @brief Construct empty instance with separate transformations + * @param children Child objects + * @param translation Translation (relative to parent) + * @param rotation Rotation (relative to parent) + * @param scaling Scaling (relative to parent) + * @param importerState Importer-specific state + */ + explicit ObjectData3D(std::vector children, const Vector3& translation, const Quaternion& rotation, const Vector3& scaling, const void* importerState = nullptr); + /** @brief Copying is not allowed */ ObjectData3D(const ObjectData3D&) = delete; @@ -114,8 +162,62 @@ class MAGNUM_TRADE_EXPORT ObjectData3D { std::vector& children() { return _children; } const std::vector& children() const { return _children; } /**< @overload */ - /** @brief Transformation (relative to parent) */ - Matrix4 transformation() const { return _transformation; } + /** @brief Flags */ + ObjectFlags3D flags() const { return _flags; } + + /** + * @brief Translation (relative to parent) + * + * Available only if @ref ObjectFlag3D::HasTransformationRotationScaling + * is set, use @ref transformation() otherwise. Applied as last in the + * final transformation, see @ref transformation() for more + * information. + * @see @ref flags(), @ref rotation(), @ref scaling() + */ + Vector3 translation() const; + + /** + * @brief Rotation (relative to parent) + * + * Available only if @ref ObjectFlag3D::HasTransformationRotationScaling + * is set, use @ref transformation() otherwise. Applied second in the + * final transformation, see @ref transformation() for more + * information. + * @see @ref flags(), @ref translation(), @ref scaling() + */ + Quaternion rotation() const; + + /** + * @brief Scaling (relative to parent) + * + * Available only if @ref ObjectFlag3D::HasTransformationRotationScaling + * is set, use @ref transformation() otherwise. Applied as first in the + * final transformation, see @ref transformation() for more + * information. + * @see @ref flags(), @ref translation(), @ref rotation() + */ + Vector3 scaling() const; + + /** + * @brief Transformation (relative to parent) + * + * If @ref ObjectFlag3D::HasTransformationRotationScaling is not set, + * returns the imported object transformation matrix. Otherwise + * calculates the final transformation matrix @f$ \boldsymbol{M} @f$ + * from translation, rotation and scaling matrices @f$ \boldsymbol{T} @f$, + * @f$ \boldsymbol{R} @f$, @f$ \boldsymbol{S} @f$ created from + * @ref translation(), @ref rotation() and @ref scaling() in the + * following order: @f[ + * \boldsymbol{M} = \boldsymbol{T} \boldsymbol{R} \boldsymbol{S} + * @f] + * + * The corresponding code is as follows: + * + * @snippet MagnumTrade.cpp ObjectData3D-transformation + * + * @see @ref flags() + */ + Matrix4 transformation() const; /** * @brief Instance type @@ -140,8 +242,20 @@ class MAGNUM_TRADE_EXPORT ObjectData3D { private: std::vector _children; - Matrix4 _transformation; + union Transformation { + Transformation(const Matrix4& matrix): matrix{matrix} {} + Transformation(const Vector3& translation, const Quaternion& rotation, const Vector3& scaling): trs{translation, rotation, scaling} {} + ~Transformation() {} + + Matrix4 matrix; + struct { + Vector3 translation; + Quaternion rotation; + Vector3 scaling; + } trs; + } _transformation; ObjectInstanceType3D _instanceType; + ObjectFlags3D _flags; Int _instance; const void* _importerState; }; @@ -149,6 +263,12 @@ class MAGNUM_TRADE_EXPORT ObjectData3D { /** @debugoperatorenum{ObjectInstanceType3D} */ MAGNUM_TRADE_EXPORT Debug& operator<<(Debug& debug, ObjectInstanceType3D value); +/** @debugoperatorenum{ObjectFlag3D} */ +MAGNUM_TRADE_EXPORT Debug& operator<<(Debug& debug, ObjectFlag3D value); + +/** @debugoperatorenum{ObjectFlags3D} */ +MAGNUM_TRADE_EXPORT Debug& operator<<(Debug& debug, ObjectFlags3D value); + }} #endif diff --git a/src/Magnum/Trade/Test/CMakeLists.txt b/src/Magnum/Trade/Test/CMakeLists.txt index 7ba133bd18..5c782c0a3b 100644 --- a/src/Magnum/Trade/Test/CMakeLists.txt +++ b/src/Magnum/Trade/Test/CMakeLists.txt @@ -46,8 +46,8 @@ corrade_add_test(TradeLightDataTest LightDataTest.cpp LIBRARIES MagnumTrade) corrade_add_test(TradeMaterialDataTest MaterialDataTest.cpp LIBRARIES MagnumTrade) corrade_add_test(TradeMeshData2DTest MeshData2DTest.cpp LIBRARIES MagnumTrade) corrade_add_test(TradeMeshData3DTest MeshData3DTest.cpp LIBRARIES MagnumTrade) -corrade_add_test(TradeObjectData2DTest ObjectData2DTest.cpp LIBRARIES MagnumTrade) -corrade_add_test(TradeObjectData3DTest ObjectData3DTest.cpp LIBRARIES MagnumTrade) +corrade_add_test(TradeObjectData2DTest ObjectData2DTest.cpp LIBRARIES MagnumTradeTestLib) +corrade_add_test(TradeObjectData3DTest ObjectData3DTest.cpp LIBRARIES MagnumTradeTestLib) corrade_add_test(TradeSceneDataTest SceneDataTest.cpp LIBRARIES MagnumTrade) corrade_add_test(TradeTextureDataTest TextureDataTest.cpp LIBRARIES MagnumTrade) diff --git a/src/Magnum/Trade/Test/ObjectData2DTest.cpp b/src/Magnum/Trade/Test/ObjectData2DTest.cpp index 4bbfe9f247..21e8a0a8c4 100644 --- a/src/Magnum/Trade/Test/ObjectData2DTest.cpp +++ b/src/Magnum/Trade/Test/ObjectData2DTest.cpp @@ -35,50 +35,104 @@ class ObjectData2DTest: public TestSuite::Tester { explicit ObjectData2DTest(); void constructEmpty(); + void constructEmptyTransformations(); void constructMesh(); + void constructMeshTransformations(); void constructCamera(); void constructCopy(); + void constructMoveTransformations(); void constructMoveMesh(); + void accessInvalidTransformations(); + void debugType(); + void debugFlag(); + void debugFlags(); }; ObjectData2DTest::ObjectData2DTest() { addTests({&ObjectData2DTest::constructEmpty, + &ObjectData2DTest::constructEmptyTransformations, &ObjectData2DTest::constructMesh, + &ObjectData2DTest::constructMeshTransformations, &ObjectData2DTest::constructCamera, &ObjectData2DTest::constructCopy, + &ObjectData2DTest::constructMoveTransformations, &ObjectData2DTest::constructMoveMesh, - &ObjectData2DTest::debugType}); + &ObjectData2DTest::accessInvalidTransformations, + + &ObjectData2DTest::debugType, + &ObjectData2DTest::debugFlag, + &ObjectData2DTest::debugFlags}); } +using namespace Math::Literals; + void ObjectData2DTest::constructEmpty() { const int a{}; const ObjectData2D data{{0, 2, 3}, Matrix3::translation(Vector2::xAxis(-4.0f)), &a}; CORRADE_COMPARE(data.children(), (std::vector{0, 2, 3})); + CORRADE_COMPARE(data.flags(), ObjectFlags2D{}); CORRADE_COMPARE(data.transformation(), Matrix3::translation(Vector2::xAxis(-4.0f))); CORRADE_COMPARE(data.instanceType(), ObjectInstanceType2D::Empty); CORRADE_COMPARE(data.instance(), -1); } +void ObjectData2DTest::constructEmptyTransformations() { + const int a{}; + const ObjectData2D data{{0, 2, 3}, Vector2::xAxis(-4.0f), Complex::rotation(32.5_degf), Vector2::yScale(1.5f), &a}; + + CORRADE_COMPARE(data.children(), (std::vector{0, 2, 3})); + CORRADE_COMPARE(data.flags(), ObjectFlag2D::HasTransformationRotationScaling); + CORRADE_COMPARE(data.translation(), Vector2::xAxis(-4.0f)); + CORRADE_COMPARE(data.rotation(), Complex::rotation(32.5_degf)); + CORRADE_COMPARE(data.scaling(), Vector2::yScale(1.5f)); + CORRADE_COMPARE(data.transformation(), + Matrix3::translation(Vector2::xAxis(-4.0f))* + Matrix3::rotation(32.5_degf)* + Matrix3::scaling(Vector2::yScale(1.5f))); + CORRADE_COMPARE(data.instanceType(), ObjectInstanceType2D::Empty); + CORRADE_COMPARE(data.instance(), -1); +} + void ObjectData2DTest::constructMesh() { const int a{}; const MeshObjectData2D data{{1, 3}, Matrix3::translation(Vector2::yAxis(5.0f)), 13, 42, &a}; CORRADE_COMPARE(data.children(), (std::vector{1, 3})); + CORRADE_COMPARE(data.flags(), ObjectFlags2D{}); CORRADE_COMPARE(data.transformation(), Matrix3::translation(Vector2::yAxis(5.0f))); CORRADE_COMPARE(data.instanceType(), ObjectInstanceType2D::Mesh); CORRADE_COMPARE(data.instance(), 13); CORRADE_COMPARE(data.material(), 42); } +void ObjectData2DTest::constructMeshTransformations() { + const int a{}; + const MeshObjectData2D data{{0, 2, 3}, Vector2::xAxis(-4.0f), Complex::rotation(32.5_degf), Vector2::yScale(1.5f), 13, 42, &a}; + + CORRADE_COMPARE(data.children(), (std::vector{0, 2, 3})); + CORRADE_COMPARE(data.flags(), ObjectFlag2D::HasTransformationRotationScaling); + CORRADE_COMPARE(data.translation(), Vector2::xAxis(-4.0f)); + CORRADE_COMPARE(data.rotation(), Complex::rotation(32.5_degf)); + CORRADE_COMPARE(data.scaling(), Vector2::yScale(1.5f)); + CORRADE_COMPARE(data.transformation(), + Matrix3::translation(Vector2::xAxis(-4.0f))* + Matrix3::rotation(32.5_degf)* + Matrix3::scaling(Vector2::yScale(1.5f))); + CORRADE_COMPARE(data.instanceType(), ObjectInstanceType2D::Mesh); + CORRADE_COMPARE(data.instance(), 13); + CORRADE_COMPARE(data.material(), 42); +} + void ObjectData2DTest::constructCamera() { const int a{}; const ObjectData2D data{{1, 3}, Matrix3::translation(Vector2::yAxis(5.0f)), ObjectInstanceType2D::Camera, 42, &a}; CORRADE_COMPARE(data.children(), (std::vector{1, 3})); + CORRADE_COMPARE(data.flags(), ObjectFlags2D{}); CORRADE_COMPARE(data.transformation(), Matrix3::translation(Vector2::yAxis(5.0f))); CORRADE_COMPARE(data.instanceType(), ObjectInstanceType2D::Camera); CORRADE_COMPARE(data.instance(), 42); @@ -91,6 +145,42 @@ void ObjectData2DTest::constructCopy() { CORRADE_VERIFY(!(std::is_assignable{})); } +void ObjectData2DTest::constructMoveTransformations() { + const int a{}; + ObjectData2D data{{1, 3}, Vector2::xAxis(-4.0f), Complex::rotation(32.5_degf), Vector2::yScale(1.5f), ObjectInstanceType2D::Camera, 13, &a}; + + ObjectData2D b{std::move(data)}; + + CORRADE_COMPARE(b.children(), (std::vector{1, 3})); + CORRADE_COMPARE(b.flags(), ObjectFlag2D::HasTransformationRotationScaling); + CORRADE_COMPARE(b.translation(), Vector2::xAxis(-4.0f)); + CORRADE_COMPARE(b.rotation(), Complex::rotation(32.5_degf)); + CORRADE_COMPARE(b.scaling(), Vector2::yScale(1.5f)); + CORRADE_COMPARE(b.transformation(), + Matrix3::translation(Vector2::xAxis(-4.0f))* + Matrix3::rotation(32.5_degf)* + Matrix3::scaling(Vector2::yScale(1.5f))); + CORRADE_COMPARE(b.instanceType(), ObjectInstanceType2D::Camera); + CORRADE_COMPARE(b.instance(), 13); + + const int c{}; + ObjectData2D d{{0, 1}, Matrix3{}, ObjectInstanceType2D::Empty, 27, &c}; + + d = std::move(b); + + CORRADE_COMPARE(d.children(), (std::vector{1, 3})); + CORRADE_COMPARE(d.flags(), ObjectFlag2D::HasTransformationRotationScaling); + CORRADE_COMPARE(d.translation(), Vector2::xAxis(-4.0f)); + CORRADE_COMPARE(d.rotation(), Complex::rotation(32.5_degf)); + CORRADE_COMPARE(d.scaling(), Vector2::yScale(1.5f)); + CORRADE_COMPARE(d.transformation(), + Matrix3::translation(Vector2::xAxis(-4.0f))* + Matrix3::rotation(32.5_degf)* + Matrix3::scaling(Vector2::yScale(1.5f))); + CORRADE_COMPARE(d.instanceType(), ObjectInstanceType2D::Camera); + CORRADE_COMPARE(d.instance(), 13); +} + void ObjectData2DTest::constructMoveMesh() { const int a{}; MeshObjectData2D data{{1, 3}, Matrix3::translation(Vector2::yAxis(5.0f)), 13, 42, &a}; @@ -98,6 +188,7 @@ void ObjectData2DTest::constructMoveMesh() { MeshObjectData2D b{std::move(data)}; CORRADE_COMPARE(b.children(), (std::vector{1, 3})); + CORRADE_COMPARE(b.flags(), ObjectFlags2D{}); CORRADE_COMPARE(b.transformation(), Matrix3::translation(Vector2::yAxis(5.0f))); CORRADE_COMPARE(b.instanceType(), ObjectInstanceType2D::Mesh); CORRADE_COMPARE(b.instance(), 13); @@ -109,18 +200,46 @@ void ObjectData2DTest::constructMoveMesh() { d = std::move(b); CORRADE_COMPARE(d.children(), (std::vector{1, 3})); + CORRADE_COMPARE(d.flags(), ObjectFlags2D{}); CORRADE_COMPARE(d.transformation(), Matrix3::translation(Vector2::yAxis(5.0f))); CORRADE_COMPARE(d.instanceType(), ObjectInstanceType2D::Mesh); CORRADE_COMPARE(d.instance(), 13); CORRADE_COMPARE(d.material(), 42); } +void ObjectData2DTest::accessInvalidTransformations() { + std::ostringstream out; + Error redirectOutput{&out}; + + const ObjectData2D data{{}, Matrix3{}}; + data.translation(); + data.rotation(); + data.scaling(); + + CORRADE_COMPARE(out.str(), + "Trade::ObjectData2D::translation(): object has only a combined transformation\n" + "Trade::ObjectData2D::rotation(): object has only a combined transformation\n" + "Trade::ObjectData2D::scaling(): object has only a combined transformation\n"); +} + void ObjectData2DTest::debugType() { std::ostringstream o; Debug(&o) << ObjectInstanceType2D::Empty << ObjectInstanceType2D(0xbe); CORRADE_COMPARE(o.str(), "Trade::ObjectInstanceType2D::Empty Trade::ObjectInstanceType2D(0xbe)\n"); } +void ObjectData2DTest::debugFlag() { + std::ostringstream o; + Debug(&o) << ObjectFlag2D::HasTransformationRotationScaling << ObjectFlag2D(0xbe); + CORRADE_COMPARE(o.str(), "Trade::ObjectFlag2D::HasTransformationRotationScaling Trade::ObjectFlag2D(0xbe)\n"); +} + +void ObjectData2DTest::debugFlags() { + std::ostringstream o; + Debug(&o) << (ObjectFlag2D::HasTransformationRotationScaling|ObjectFlags2D{}) << ObjectFlags2D{}; + CORRADE_COMPARE(o.str(), "Trade::ObjectFlag2D::HasTransformationRotationScaling Trade::ObjectFlags2D{}\n"); +} + }}} CORRADE_TEST_MAIN(Magnum::Trade::Test::ObjectData2DTest) diff --git a/src/Magnum/Trade/Test/ObjectData3DTest.cpp b/src/Magnum/Trade/Test/ObjectData3DTest.cpp index 38b7317c54..51cba4e066 100644 --- a/src/Magnum/Trade/Test/ObjectData3DTest.cpp +++ b/src/Magnum/Trade/Test/ObjectData3DTest.cpp @@ -35,52 +35,106 @@ class ObjectData3DTest: public TestSuite::Tester { explicit ObjectData3DTest(); void constructEmpty(); + void constructEmptyTransformations(); void constructMesh(); + void constructMeshTransformations(); void constructCamera(); void constructLight(); void constructCopy(); + void constructMoveTransformations(); void constructMoveMesh(); + void accessInvalidTransformations(); + void debugType(); + void debugFlag(); + void debugFlags(); }; ObjectData3DTest::ObjectData3DTest() { addTests({&ObjectData3DTest::constructEmpty, + &ObjectData3DTest::constructEmptyTransformations, &ObjectData3DTest::constructMesh, + &ObjectData3DTest::constructMeshTransformations, &ObjectData3DTest::constructCamera, &ObjectData3DTest::constructLight, &ObjectData3DTest::constructCopy, + &ObjectData3DTest::constructMoveTransformations, &ObjectData3DTest::constructMoveMesh, - &ObjectData3DTest::debugType}); + &ObjectData3DTest::accessInvalidTransformations, + + &ObjectData3DTest::debugType, + &ObjectData3DTest::debugFlag, + &ObjectData3DTest::debugFlags}); } +using namespace Math::Literals; + void ObjectData3DTest::constructEmpty() { const int a{}; const ObjectData3D data{{0, 2, 3}, Matrix4::translation(Vector3::xAxis(-4.0f)), &a}; CORRADE_COMPARE(data.children(), (std::vector{0, 2, 3})); + CORRADE_COMPARE(data.flags(), ObjectFlags3D{}); CORRADE_COMPARE(data.transformation(), Matrix4::translation(Vector3::xAxis(-4.0f))); CORRADE_COMPARE(data.instanceType(), ObjectInstanceType3D::Empty); CORRADE_COMPARE(data.instance(), -1); } +void ObjectData3DTest::constructEmptyTransformations() { + const int a{}; + const ObjectData3D data{{0, 2, 3}, Vector3::xAxis(-4.0f), Quaternion::rotation(32.5_degf, Vector3::zAxis()), Vector3::yScale(1.5f), &a}; + + CORRADE_COMPARE(data.children(), (std::vector{0, 2, 3})); + CORRADE_COMPARE(data.flags(), ObjectFlag3D::HasTransformationRotationScaling); + CORRADE_COMPARE(data.translation(), Vector3::xAxis(-4.0f)); + CORRADE_COMPARE(data.rotation(), Quaternion::rotation(32.5_degf, Vector3::zAxis())); + CORRADE_COMPARE(data.scaling(), Vector3::yScale(1.5f)); + CORRADE_COMPARE(data.transformation(), + Matrix4::translation(Vector3::xAxis(-4.0f))* + Matrix4::rotationZ(32.5_degf)* + Matrix4::scaling(Vector3::yScale(1.5f))); + CORRADE_COMPARE(data.instanceType(), ObjectInstanceType3D::Empty); + CORRADE_COMPARE(data.instance(), -1); +} + void ObjectData3DTest::constructMesh() { const int a{}; const MeshObjectData3D data{{1, 3}, Matrix4::translation(Vector3::yAxis(5.0f)), 13, 42, &a}; CORRADE_COMPARE(data.children(), (std::vector{1, 3})); + CORRADE_COMPARE(data.flags(), ObjectFlags3D{}); CORRADE_COMPARE(data.transformation(), Matrix4::translation(Vector3::yAxis(5.0f))); CORRADE_COMPARE(data.instanceType(), ObjectInstanceType3D::Mesh); CORRADE_COMPARE(data.instance(), 13); CORRADE_COMPARE(data.material(), 42); } +void ObjectData3DTest::constructMeshTransformations() { + const int a{}; + const MeshObjectData3D data{{1, 3}, Vector3::xAxis(-4.0f), Quaternion::rotation(32.5_degf, Vector3::zAxis()), Vector3::yScale(1.5f), 13, 42, &a}; + + CORRADE_COMPARE(data.children(), (std::vector{1, 3})); + CORRADE_COMPARE(data.flags(), ObjectFlag3D::HasTransformationRotationScaling); + CORRADE_COMPARE(data.translation(), Vector3::xAxis(-4.0f)); + CORRADE_COMPARE(data.rotation(), Quaternion::rotation(32.5_degf, Vector3::zAxis())); + CORRADE_COMPARE(data.scaling(), Vector3::yScale(1.5f)); + CORRADE_COMPARE(data.transformation(), + Matrix4::translation(Vector3::xAxis(-4.0f))* + Matrix4::rotationZ(32.5_degf)* + Matrix4::scaling(Vector3::yScale(1.5f))); + CORRADE_COMPARE(data.instanceType(), ObjectInstanceType3D::Mesh); + CORRADE_COMPARE(data.instance(), 13); + CORRADE_COMPARE(data.material(), 42); +} + void ObjectData3DTest::constructCamera() { const int a{}; const ObjectData3D data{{1, 3}, Matrix4::translation(Vector3::yAxis(5.0f)), ObjectInstanceType3D::Camera, 42, &a}; CORRADE_COMPARE(data.children(), (std::vector{1, 3})); + CORRADE_COMPARE(data.flags(), ObjectFlags3D{}); CORRADE_COMPARE(data.transformation(), Matrix4::translation(Vector3::yAxis(5.0f))); CORRADE_COMPARE(data.instanceType(), ObjectInstanceType3D::Camera); CORRADE_COMPARE(data.instance(), 42); @@ -91,6 +145,7 @@ void ObjectData3DTest::constructLight() { const ObjectData3D data{{1, 3}, Matrix4::translation(Vector3::yAxis(5.0f)), ObjectInstanceType3D::Light, 42, &a}; CORRADE_COMPARE(data.children(), (std::vector{1, 3})); + CORRADE_COMPARE(data.flags(), ObjectFlags3D{}); CORRADE_COMPARE(data.transformation(), Matrix4::translation(Vector3::yAxis(5.0f))); CORRADE_COMPARE(data.instanceType(), ObjectInstanceType3D::Light); CORRADE_COMPARE(data.instance(), 42); @@ -103,6 +158,42 @@ void ObjectData3DTest::constructCopy() { CORRADE_VERIFY(!(std::is_assignable{})); } +void ObjectData3DTest::constructMoveTransformations() { + const int a{}; + ObjectData3D data{{1, 3}, Vector3::xAxis(-4.0f), Quaternion::rotation(32.5_degf, Vector3::zAxis()), Vector3::yScale(1.5f), ObjectInstanceType3D::Light, 13, &a}; + + ObjectData3D b{std::move(data)}; + + CORRADE_COMPARE(b.children(), (std::vector{1, 3})); + CORRADE_COMPARE(b.flags(), ObjectFlag3D::HasTransformationRotationScaling); + CORRADE_COMPARE(b.translation(), Vector3::xAxis(-4.0f)); + CORRADE_COMPARE(b.rotation(), Quaternion::rotation(32.5_degf, Vector3::zAxis())); + CORRADE_COMPARE(b.scaling(), Vector3::yScale(1.5f)); + CORRADE_COMPARE(b.transformation(), + Matrix4::translation(Vector3::xAxis(-4.0f))* + Matrix4::rotationZ(32.5_degf)* + Matrix4::scaling(Vector3::yScale(1.5f))); + CORRADE_COMPARE(b.instanceType(), ObjectInstanceType3D::Light); + CORRADE_COMPARE(b.instance(), 13); + + const int c{}; + ObjectData3D d{{0, 1}, Matrix4{}, ObjectInstanceType3D::Empty, 27, &c}; + + d = std::move(b); + + CORRADE_COMPARE(d.children(), (std::vector{1, 3})); + CORRADE_COMPARE(d.flags(), ObjectFlag3D::HasTransformationRotationScaling); + CORRADE_COMPARE(d.translation(), Vector3::xAxis(-4.0f)); + CORRADE_COMPARE(d.rotation(), Quaternion::rotation(32.5_degf, Vector3::zAxis())); + CORRADE_COMPARE(d.scaling(), Vector3::yScale(1.5f)); + CORRADE_COMPARE(d.transformation(), + Matrix4::translation(Vector3::xAxis(-4.0f))* + Matrix4::rotationZ(32.5_degf)* + Matrix4::scaling(Vector3::yScale(1.5f))); + CORRADE_COMPARE(d.instanceType(), ObjectInstanceType3D::Light); + CORRADE_COMPARE(d.instance(), 13); +} + void ObjectData3DTest::constructMoveMesh() { const int a{}; MeshObjectData3D data{{1, 3}, Matrix4::translation(Vector3::yAxis(5.0f)), 13, 42, &a}; @@ -110,6 +201,7 @@ void ObjectData3DTest::constructMoveMesh() { MeshObjectData3D b{std::move(data)}; CORRADE_COMPARE(b.children(), (std::vector{1, 3})); + CORRADE_COMPARE(data.flags(), ObjectFlags3D{}); CORRADE_COMPARE(b.transformation(), Matrix4::translation(Vector3::yAxis(5.0f))); CORRADE_COMPARE(b.instanceType(), ObjectInstanceType3D::Mesh); CORRADE_COMPARE(b.instance(), 13); @@ -121,18 +213,46 @@ void ObjectData3DTest::constructMoveMesh() { d = std::move(b); CORRADE_COMPARE(d.children(), (std::vector{1, 3})); + CORRADE_COMPARE(data.flags(), ObjectFlags3D{}); CORRADE_COMPARE(d.transformation(), Matrix4::translation(Vector3::yAxis(5.0f))); CORRADE_COMPARE(d.instanceType(), ObjectInstanceType3D::Mesh); CORRADE_COMPARE(d.instance(), 13); CORRADE_COMPARE(d.material(), 42); } +void ObjectData3DTest::accessInvalidTransformations() { + std::ostringstream out; + Error redirectOutput{&out}; + + const ObjectData3D data{{}, Matrix4{}}; + data.translation(); + data.rotation(); + data.scaling(); + + CORRADE_COMPARE(out.str(), + "Trade::ObjectData3D::translation(): object has only a combined transformation\n" + "Trade::ObjectData3D::rotation(): object has only a combined transformation\n" + "Trade::ObjectData3D::scaling(): object has only a combined transformation\n"); +} + void ObjectData3DTest::debugType() { std::ostringstream o; Debug(&o) << ObjectInstanceType3D::Light << ObjectInstanceType3D(0xbe); CORRADE_COMPARE(o.str(), "Trade::ObjectInstanceType3D::Light Trade::ObjectInstanceType3D(0xbe)\n"); } +void ObjectData3DTest::debugFlag() { + std::ostringstream o; + Debug(&o) << ObjectFlag3D::HasTransformationRotationScaling << ObjectFlag3D(0xbe); + CORRADE_COMPARE(o.str(), "Trade::ObjectFlag3D::HasTransformationRotationScaling Trade::ObjectFlag3D(0xbe)\n"); +} + +void ObjectData3DTest::debugFlags() { + std::ostringstream o; + Debug(&o) << (ObjectFlag3D::HasTransformationRotationScaling|ObjectFlags3D{}) << ObjectFlags3D{}; + CORRADE_COMPARE(o.str(), "Trade::ObjectFlag3D::HasTransformationRotationScaling Trade::ObjectFlags3D{}\n"); +} + }}} CORRADE_TEST_MAIN(Magnum::Trade::Test::ObjectData3DTest)