From 06398dcf7c1cb21169e04c166ca932ad3273f466 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Vladim=C3=ADr=20Vondru=C5=A1?= Date: Wed, 31 Oct 2012 00:16:46 +0100 Subject: [PATCH] SceneGraph rework, part 3: splitting base object. Functionality present in Object is now split into three main components: * Object itself, handling parent/children relationships * Transformation implementation and interfaces for common functionality * Object features, providing transformation caching and base for cameras, collision shapes, rigid bodies etc. Some functionality depending on former implementation is temporarily disabled and will be reworked later. --- src/Physics/CMakeLists.txt | 11 +- src/Physics/Test/CMakeLists.txt | 2 - src/SceneGraph/AbstractFeature.h | 319 +++++++++++++ src/SceneGraph/AbstractObject.h | 130 ++++++ src/SceneGraph/AbstractTransformation.h | 169 +++++++ .../AbstractTranslationRotation2D.h | 56 +++ .../AbstractTranslationRotation3D.h | 57 +++ .../AbstractTranslationRotationScaling2D.h | 46 ++ .../AbstractTranslationRotationScaling3D.h | 46 ++ src/SceneGraph/CMakeLists.txt | 26 +- src/SceneGraph/MatrixTransformation2D.cpp | 26 ++ src/SceneGraph/MatrixTransformation2D.h | 136 ++++++ src/SceneGraph/MatrixTransformation3D.cpp | 26 ++ src/SceneGraph/MatrixTransformation3D.h | 125 ++++++ src/SceneGraph/Object.cpp | 147 ------ src/SceneGraph/Object.h | 419 +++++------------- src/SceneGraph/Object.hpp | 159 +++++++ src/SceneGraph/Scene.h | 27 +- src/SceneGraph/Test/CMakeLists.txt | 1 - src/SceneGraph/Test/ObjectTest.cpp | 152 ++++--- src/SceneGraph/Test/ObjectTest.h | 21 +- src/SceneGraph/Test/SceneTest.cpp | 6 +- 22 files changed, 1526 insertions(+), 581 deletions(-) create mode 100644 src/SceneGraph/AbstractFeature.h create mode 100644 src/SceneGraph/AbstractObject.h create mode 100644 src/SceneGraph/AbstractTransformation.h create mode 100644 src/SceneGraph/AbstractTranslationRotation2D.h create mode 100644 src/SceneGraph/AbstractTranslationRotation3D.h create mode 100644 src/SceneGraph/AbstractTranslationRotationScaling2D.h create mode 100644 src/SceneGraph/AbstractTranslationRotationScaling3D.h create mode 100644 src/SceneGraph/MatrixTransformation2D.cpp create mode 100644 src/SceneGraph/MatrixTransformation2D.h create mode 100644 src/SceneGraph/MatrixTransformation3D.cpp create mode 100644 src/SceneGraph/MatrixTransformation3D.h delete mode 100644 src/SceneGraph/Object.cpp create mode 100644 src/SceneGraph/Object.hpp diff --git a/src/Physics/CMakeLists.txt b/src/Physics/CMakeLists.txt index 1dc1bf4cf..fdfde2722 100644 --- a/src/Physics/CMakeLists.txt +++ b/src/Physics/CMakeLists.txt @@ -3,29 +3,20 @@ set(MagnumPhysics_SRCS AxisAlignedBox.cpp Box.cpp Capsule.cpp - DebugDrawResourceManager.cpp Line.cpp Plane.cpp Point.cpp - ShapedObject.cpp - ShapedObjectGroup.cpp ShapeGroup.cpp - Sphere.cpp - - Implementation/AbstractDebugRenderer.cpp - Implementation/BoxRenderer.cpp) + Sphere.cpp) set(MagnumPhysics_HEADERS AbstractShape.h AxisAlignedBox.h Box.h Capsule.h - DebugDrawResourceManager.h Line.h LineSegment.h Plane.h Point.h - ShapedObject.h - ShapedObjectGroup.h ShapeGroup.h Sphere.h diff --git a/src/Physics/Test/CMakeLists.txt b/src/Physics/Test/CMakeLists.txt index dd56a0631..4f8130a83 100644 --- a/src/Physics/Test/CMakeLists.txt +++ b/src/Physics/Test/CMakeLists.txt @@ -6,5 +6,3 @@ corrade_add_test2(PhysicsPlaneTest PlaneTest.cpp LIBRARIES MagnumPhysics) corrade_add_test2(PhysicsPointTest PointTest.cpp LIBRARIES MagnumPhysics) corrade_add_test2(PhysicsShapeGroupTest ShapeGroupTest.cpp LIBRARIES MagnumPhysics) corrade_add_test2(PhysicsSphereTest SphereTest.cpp LIBRARIES MagnumPhysics) - -corrade_add_test2(PhysicsShapedObjectTest ShapedObjectTest.cpp LIBRARIES MagnumPhysics) diff --git a/src/SceneGraph/AbstractFeature.h b/src/SceneGraph/AbstractFeature.h new file mode 100644 index 000000000..34e3feffd --- /dev/null +++ b/src/SceneGraph/AbstractFeature.h @@ -0,0 +1,319 @@ +#ifndef Magnum_SceneGraph_AbstractFeature_h +#define Magnum_SceneGraph_AbstractFeature_h +/* + Copyright © 2010, 2011, 2012 Vladimír Vondruš + + This file is part of Magnum. + + Magnum is free software: you can redistribute it and/or modify + it under the terms of the GNU Lesser General Public License version 3 + only, as published by the Free Software Foundation. + + Magnum is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU Lesser General Public License version 3 for more details. +*/ + +/** @file + * @brief Class Magnum::SceneGraph::AbstractFeature, alias Magnum::SceneGraph::AbstractFeature2D, Magnum::SceneGraph::AbstractFeature3D + */ + +#include +#include + +#include "AbstractObject.h" + +namespace Magnum { namespace SceneGraph { + +#ifndef DOXYGEN_GENERATING_OUTPUT +namespace Implementation { + enum class FeatureCachedTransformation: std::uint8_t { + Absolute = 1 << 0, + InvertedAbsolute = 1 << 1 + }; + + typedef Corrade::Containers::EnumSet FeatureCachedTransformations; + + CORRADE_ENUMSET_OPERATORS(FeatureCachedTransformations) +} +#endif + +/** +@brief Base for object features + +Contained in Object, takes care of transformation caching. See @ref scenegraph +for introduction. + +@section AbstractFeature-subclassing Subclassing + +Feature is templated on dimension count and underlying transformation type, so +it can be used only on object having transformation with the same dimension +count and type. + +@subsection AbstractFeature-subclassing-caching Caching transformations in features + +Features can cache absolute transformation of the object instead of computing +it from scratch every time to achieve better performance. See +@ref scenegraph-caching for introduction. + +In order to have caching, you must enable it first, because by default the +caching is disabled. You can enable it using setCachedTransformations() and +then implement corresponding cleaning function(s) -- either clean(), +cleanInverted() or both. Example: +@code +class CachingFeature: public SceneGraph::AbstractFeature3D<> { + public: + CachingFeature(SceneGraph::AbstractObject3D<>* object): SceneGraph::AbstractFeature3D<>(object) { + setCachedTransformations(CachedTransformation::Absolute); + } + + protected: + void clean(const Matrix4& absoluteTransformation) override { + absolutePosition = absoluteTransformation.translation(); + } + + private: + Vector3 absolutePosition; +}; +@endcode + +Before using the cached value explicitly request object cleaning by calling +`object()->setClean()`. + +@subsection AbstractFeature-subclassing-transformation Accessing object transformation + +Features has by default access only to AbstractObject, which is base of Object +not depending on any particular transformation implementation. This has the +advantage that features doesn't have to be implemented for all possible +transformation implementations, thus preventing code duplication. However it +is impossible to transform the object using only pointer to AbstractObject. + +The transformations have interfaces for common functionality, so the feature +can use that interface instead of being specialized for all relevant +transformation implementations. Using small trick we are able to get pointer +to both AbstractObject and needed transformation from one constructor +parameter: +@code +class TransformingFeature: public SceneGraph::AbstractFeature3D<> { + public: + template inline TransformingFeature(SceneGraph::Object* object): + SceneGraph::AbstractFeature3D<>(object), transformation(object) {} + + private: + SceneGraph::AbstractTranslationRotation3D<>* transformation; +}; +@endcode +If we take for example @ref Object "Object>", it is +derived from @ref AbstractObject "AbstractObject3D<>" and +@ref MatrixTransformation3D "MatrixTransformation3D<>", which is derived from +@ref AbstractTranslationRotationScaling3D "AbstractTranslationRotationScaling3D<>", +which is derived from +@ref AbstractTranslationRotation3D "AbstractTranslationRotation3D<>", +which is automatically extracted from the pointer in our constructor. + +@see AbstractFeature2D, AbstractFeature3D +*/ +template class AbstractFeature + #ifndef DOXYGEN_GENERATING_OUTPUT + : private Corrade::Containers::LinkedListItem, AbstractObject> + #endif +{ + friend class Corrade::Containers::LinkedList>; + friend class Corrade::Containers::LinkedListItem, AbstractObject>; + template friend class Object; + + public: + /** + * @brief Constructor + * @param object %Object holding this feature + */ + inline AbstractFeature(AbstractObject* object) { + object->Corrade::Containers::LinkedList>::insert(this); + } + + virtual ~AbstractFeature() = 0; + + /** @brief %Object holding this feature */ + inline AbstractObject* object() { + return Corrade::Containers::LinkedListItem, AbstractObject>::list(); + } + + /** @overload */ + inline const AbstractObject* object() const { + return Corrade::Containers::LinkedListItem, AbstractObject>::list(); + } + + /** @brief Previous feature or `nullptr`, if this is first feature */ + inline AbstractFeature* previousFeature() { + return Corrade::Containers::LinkedListItem, AbstractObject>::previous(); + } + + /** @overload */ + inline const AbstractFeature* previousFeature() const { + return Corrade::Containers::LinkedListItem, AbstractObject>::previous(); + } + + /** @brief Next feature or `nullptr`, if this is last feature */ + inline AbstractFeature* nextFeature() { + return Corrade::Containers::LinkedListItem, AbstractObject>::next(); + } + + /** @overload */ + inline const AbstractFeature* nextFeature() const { + return Corrade::Containers::LinkedListItem, AbstractObject>::next(); + } + + /** + * @{ @name Transformation caching + * + * See @ref scenegraph-caching for more information. + */ + + /** + * @brief Which transformation to cache in this feature + * + * @see @ref scenegraph-caching, CachedTransformations, + * setCachedTransformations(), clean(), cleanInverted() + * @todo Provide also simpler representations from which could benefit + * other transformation implementations, as they won't need to + * e.g. create transformation matrix from quaternion? + */ + #ifndef DOXYGEN_GENERATING_OUTPUT + typedef Implementation::FeatureCachedTransformation CachedTransformation; + #else + enum class CachedTransformation: std::uint8_t { + /** + * Absolute transformation is cached. + * + * If enabled, clean() is called when cleaning object. + */ + Absolute = 1 << 0, + + /** + * Inverted absolute transformation is cached. + * + * If enabled, cleanInverted() is called when cleaning object. + */ + InvertedAbsolute = 1 << 1 + }; + #endif + + /** + * @brief Which transformations to cache in this feature + * + * @see @ref scenegraph-caching, setCachedTransformations(), clean(), + * cleanInverted() + */ + #ifndef DOXYGEN_GENERATING_OUTPUT + typedef Implementation::FeatureCachedTransformations CachedTransformations; + #else + typedef Corrade::Containers::EnumSet CachedTransformations; + #endif + + /** + * @brief Which transformations are cached + * + * @see @ref scenegraph-caching, clean(), cleanInverted() + */ + inline CachedTransformations cachedTransformations() const { return _cachedTransformations; } + + protected: + /** + * @brief Set transformations to be cached + * + * Based on which transformation types are enabled, clean() or + * cleanInverted() is called when cleaning absolute object + * transformation. + * + * Nothing is enabled by default. + * @see @ref scenegraph-caching + */ + inline void setCachedTransformations(CachedTransformations transformations) { _cachedTransformations = transformations; } + + /** + * @brief Mark feature as dirty + * + * Reimplement only if you want to invalidate some external data when + * object is marked as dirty. All expensive computations should be + * done in clean() and cleanInverted(). + * + * Default implementation does nothing. + * @see @ref scenegraph-caching + */ + inline virtual void markDirty() {} + + /** + * @brief Clean data based on absolute transformation + * + * When object is cleaned and + * @ref CachedTransformation "CachedTransformation::Absolute" is + * enabled in setCachedTransformations(), this function is called to + * recalculate data based on absolute object transformation. + * + * Default implementation does nothing. + * @see @ref scenegraph-caching, cleanInverted() + */ + virtual void clean(const typename DimensionTraits::MatrixType& absoluteTransformation); + + /** + * @brief Clean data based on inverted absolute transformation + * + * When object is cleaned and + * @ref CachedTransformation "CachedTransformation::InvertedAbsolute" + * is enabled in setCachedTransformations(), this function is called + * to recalculate data based on inverted absolute object + * transformation. + * + * Default implementation does nothing. + * @see @ref scenegraph-caching, clean() + */ + virtual void cleanInverted(const typename DimensionTraits::MatrixType& invertedAbsoluteTransformation); + + /*@}*/ + + private: + CachedTransformations _cachedTransformations; +}; + +template inline AbstractFeature::~AbstractFeature() {} +template inline void AbstractFeature::clean(const typename DimensionTraits::MatrixType&) {} +template inline void AbstractFeature::cleanInverted(const typename DimensionTraits::MatrixType&) {} + +/** +@brief Base for two-dimensional features + +Convenience alternative to %AbstractFeature<2, T>. See AbstractFeature +for more information. +@note Not available on GCC < 4.7. Use %AbstractFeature<2, T> instead. +@see AbstractFeature3D +@todoc Remove workaround when Doxygen supports alias template +*/ +#ifndef DOXYGEN_GENERATING_OUTPUT +#ifndef MAGNUM_GCC46_COMPATIBILITY +template using AbstractFeature2D = AbstractFeature<2, T>; +#endif +#else +typedef AbstractFeature<2, T = GLfloat> AbstractFeature2D; +#endif + +/** +@brief Base for three-dimensional features + +Convenience alternative to %AbstractFeature<3, T>. See AbstractFeature +for more information. +@note Not available on GCC < 4.7. Use %AbstractFeature<3, T> instead. +@see AbstractFeature2D +@todoc Remove workaround when Doxygen supports alias template +*/ +#ifndef DOXYGEN_GENERATING_OUTPUT +#ifndef MAGNUM_GCC46_COMPATIBILITY +template using AbstractFeature3D = AbstractFeature<3, T>; +#endif +#else +typedef AbstractFeature<2, T = GLfloat> AbstractFeature3D; +#endif + +}} + +#endif diff --git a/src/SceneGraph/AbstractObject.h b/src/SceneGraph/AbstractObject.h new file mode 100644 index 000000000..7c58ae068 --- /dev/null +++ b/src/SceneGraph/AbstractObject.h @@ -0,0 +1,130 @@ +#ifndef Magnum_SceneGraph_AbstractObject_h +#define Magnum_SceneGraph_AbstractObject_h +/* + Copyright © 2010, 2011, 2012 Vladimír Vondruš + + This file is part of Magnum. + + Magnum is free software: you can redistribute it and/or modify + it under the terms of the GNU Lesser General Public License version 3 + only, as published by the Free Software Foundation. + + Magnum is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU Lesser General Public License version 3 for more details. +*/ + +/** @file + * @brief Class Magnum::SceneGraph::AbstractObject, alias Magnum::SceneGraph::AbstractObject2D, Magnum::SceneGraph::AbstractObject3D + */ + +#include + +#include "DimensionTraits.h" +#include "Magnum.h" + +#include "magnumCompatibility.h" + +namespace Magnum { namespace SceneGraph { + +template class AbstractFeature; + +/** +@brief Base for objects + +Provides minimal interface for features, not depending on object +transformation implementation. See Object or @ref scenegraph for more +information. + +@see AbstractObject2D, AbstractObject3D +*/ +template class AbstractObject + #ifndef DOXYGEN_GENERATING_OUTPUT + : private Corrade::Containers::LinkedList> + #endif +{ + friend class Corrade::Containers::LinkedList>; + friend class Corrade::Containers::LinkedListItem, AbstractObject>; + friend AbstractFeature::AbstractFeature(AbstractObject*); + + public: + /** @brief Feature object type */ + typedef AbstractFeature FeatureType; + + inline virtual ~AbstractObject() {} + + /** @brief Whether this object has features */ + inline bool hasFeatures() const { + return !Corrade::Containers::LinkedList>::isEmpty(); + } + + /** @brief First object feature or `nullptr`, if this object has no features */ + inline FeatureType* firstFeature() { + return Corrade::Containers::LinkedList>::first(); + } + + /** @overload */ + inline const FeatureType* firstFeature() const { + return Corrade::Containers::LinkedList>::first(); + } + + /** @brief Last object feature or `nullptr`, if this object has no features */ + inline FeatureType* lastFeature() { + return Corrade::Containers::LinkedList>::last(); + } + + /** @overload */ + inline const FeatureType* lastFeature() const { + return Corrade::Containers::LinkedList>::last(); + } + + /** @{ @name Object transformation */ + + /** + * @brief Transformation matrix relative to root object + * + * @see Object::absoluteTransformation() + */ + virtual typename DimensionTraits::MatrixType absoluteTransformationMatrix() const = 0; + + /*@}*/ +}; + +/** +@brief Base for two-dimensional objects + +Convenience alternative to %AbstractObject<2, T>. See AbstractObject +for more information. +@note Not available on GCC < 4.7. Use %AbstractObject<2, T> instead. +@see AbstractObject3D +@todoc Remove workaround when Doxygen supports alias template +*/ +#ifndef DOXYGEN_GENERATING_OUTPUT +#ifndef MAGNUM_GCC46_COMPATIBILITY +template using AbstractObject2D = AbstractObject<2, T>; +#endif +#else +typedef AbstractObject<2, T = GLfloat> AbstractObject2D; +#endif + +/** +@brief Base for three-dimensional objects + +Convenience alternative to %AbstractObject<3, T>. See AbstractObject +for more information. +@note Not available on GCC < 4.7. Use %AbstractObject<3, T> instead. +@see AbstractObject2D +@todoc Remove workaround when Doxygen supports alias template +*/ +#ifndef DOXYGEN_GENERATING_OUTPUT +#ifndef MAGNUM_GCC46_COMPATIBILITY +template using AbstractObject3D = AbstractObject<3, T>; +#endif +#else +typedef AbstractObject<3, T = GLfloat> AbstractObject3D; +#endif + +}} + +#endif diff --git a/src/SceneGraph/AbstractTransformation.h b/src/SceneGraph/AbstractTransformation.h new file mode 100644 index 000000000..a2c73d818 --- /dev/null +++ b/src/SceneGraph/AbstractTransformation.h @@ -0,0 +1,169 @@ +#ifndef Magnum_SceneGraph_AbstractTransformation_h +#define Magnum_SceneGraph_AbstractTransformation_h +/* + Copyright © 2010, 2011, 2012 Vladimír Vondruš + + This file is part of Magnum. + + Magnum is free software: you can redistribute it and/or modify + it under the terms of the GNU Lesser General Public License version 3 + only, as published by the Free Software Foundation. + + Magnum is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU Lesser General Public License version 3 for more details. +*/ + +/** @file + * @brief Class Magnum::SceneGraph::AbstractTransformation, enum Magnum::SceneGraph::TransformationType, alias Magnum::SceneGraph::AbstractTransformation2D, Magnum::SceneGraph::AbstractTransformation3D + */ + +#include + +#include "Magnum.h" +#include "DimensionTraits.h" + +namespace Magnum { namespace SceneGraph { + +template class Object; + +/** +@brief Base for transformations + +Provides transformation implementation for Object instances. See @ref scenegraph +for introduction. + +@section AbstractTransformation-subclassing Subclassing + +When sublassing, you have to: + +- Implement all members described in **Subclass implementation** group above +- Provide implicit (parameterless) constructor + +@see AbstractTransformation2D, AbstractTransformation3D +*/ +template class AbstractTransformation { + public: + /** @brief Underlying floating-point type */ + typedef T Type; + + /** @brief Dimension count */ + static const std::uint8_t Dimensions = dimensions; + + virtual ~AbstractTransformation() = 0; + + #ifdef DOXYGEN_GENERATING_OUTPUT + /** + * @{ @name Subclass implementation + * + * These members must be defined by the implementation. + */ + + /** + * @brief Transformation data type + * + * The type must satisfy the following requirements: + * + * - Default constructor must create identity transformation + * + * Defined in subclasses. + */ + typedef U DataType; + + /** + * @brief Convert transformation to matrix + * + * Defined in subclasses. + */ + static typename DimensionTraits::MatrixType toMatrix(const DataType& transformation); + + /** + * @brief Convert transformation from matrix + * + * Defined in subclasses. + */ + static DataType fromMatrix(const typename DimensionTraits::MatrixType& matrix); + + /** + * @brief Compose transformations + * + * Defined in subclasses. + */ + static DataType compose(const DataType& parent, const DataType& child); + + /** + * @brief Inverted transformation + * + * Defined in subclasses. + */ + static DataType inverted(const DataType& transformation); + + /** + * @brief %Object transformation + * + * Relative to parent. Defined in subclasses. + */ + DataType transformation() const; + + /** + * @brief Absolute transformation + * + * Relative to root object. Defined in subclasses. + */ + DataType absoluteTransformation() const; + + /*@}*/ + #endif +}; + +/** @brief Transformation type */ +enum class TransformationType: std::uint8_t { + /** Global transformation, applied after all other transformations. */ + Global = 0x00, + + /** Local transformation, applied before all other transformations. */ + Local = 0x01 +}; + +template inline AbstractTransformation::~AbstractTransformation() {} + +/** +@brief Base for two-dimensional transformations + +Convenience alternative to %AbstractTransformation<2, T>. See +AbstractTransformation for more information. +@note Not available on GCC < 4.7. Use %AbstractTransformation<2, T> + instead. +@see AbstractTransformation3D +@todoc Remove workaround when Doxygen supports alias template +*/ +#ifndef DOXYGEN_GENERATING_OUTPUT +#ifndef MAGNUM_GCC46_COMPATIBILITY +template using AbstractTransformation2D = AbstractTransformation<2, T>; +#endif +#else +typedef AbstractTransformation<2, T = GLfloat> AbstractTransformation2D; +#endif + +/** +@brief Base for three-dimensional transformations + +Convenience alternative to %AbstractTransformation<3, T>. See +AbstractTransformation for more information. +@note Not available on GCC < 4.7. Use %AbstractTransformation<3, T> + instead. +@see AbstractTransformation2D +@todoc Remove workaround when Doxygen supports alias template +*/ +#ifndef DOXYGEN_GENERATING_OUTPUT +#ifndef MAGNUM_GCC46_COMPATIBILITY +template using AbstractTransformation3D = AbstractTransformation<3, T>; +#endif +#else +typedef AbstractTransformation<3, T = GLfloat> AbstractTransformation3D; +#endif + +}} + +#endif diff --git a/src/SceneGraph/AbstractTranslationRotation2D.h b/src/SceneGraph/AbstractTranslationRotation2D.h new file mode 100644 index 000000000..22d407b14 --- /dev/null +++ b/src/SceneGraph/AbstractTranslationRotation2D.h @@ -0,0 +1,56 @@ +#ifndef Magnum_SceneGraph_AbstractTranslationRotation2D_h +#define Magnum_SceneGraph_AbstractTranslationRotation2D_h +/* + Copyright © 2010, 2011, 2012 Vladimír Vondruš + + This file is part of Magnum. + + Magnum is free software: you can redistribute it and/or modify + it under the terms of the GNU Lesser General Public License version 3 + only, as published by the Free Software Foundation. + + Magnum is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU Lesser General Public License version 3 for more details. +*/ + +/** @file + * @brief Class Magnum::SceneGraph::AbstractTranslationRotation2D + */ + +#include "AbstractTransformation.h" + +namespace Magnum { namespace SceneGraph { + +/** +@brief Base for two-dimensional transformations supporting translation and rotation + +@see AbstractTranslationRotation3D +*/ +template class AbstractTranslationRotation2D: public AbstractTransformation<2, T> { + public: + /** + * @brief Translate object + * @param vector Translation vector + * @param type Transformation type + * @return Pointer to self (for method chaining) + * + * @see Vector2::xAxis(), Vector2::yAxis() + */ + virtual AbstractTranslationRotation2D* translate(const Math::Vector2& vector, TransformationType type = TransformationType::Global) = 0; + + /** + * @brief Rotate object + * @param angle Angle in radians, counterclockwise + * @param type Transformation type + * @return Pointer to self (for method chaining) + * + * @see deg(), rad() + */ + virtual AbstractTranslationRotation2D* rotate(T angle, TransformationType type = TransformationType::Global) = 0; +}; + +}} + +#endif diff --git a/src/SceneGraph/AbstractTranslationRotation3D.h b/src/SceneGraph/AbstractTranslationRotation3D.h new file mode 100644 index 000000000..67178b269 --- /dev/null +++ b/src/SceneGraph/AbstractTranslationRotation3D.h @@ -0,0 +1,57 @@ +#ifndef Magnum_SceneGraph_AbstractTranslationRotation3D_h +#define Magnum_SceneGraph_AbstractTranslationRotation3D_h +/* + Copyright © 2010, 2011, 2012 Vladimír Vondruš + + This file is part of Magnum. + + Magnum is free software: you can redistribute it and/or modify + it under the terms of the GNU Lesser General Public License version 3 + only, as published by the Free Software Foundation. + + Magnum is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU Lesser General Public License version 3 for more details. +*/ + +/** @file + * @brief Class Magnum::SceneGraph::AbstractTranslationRotation3D + */ + +#include "AbstractTransformation.h" + +namespace Magnum { namespace SceneGraph { + +/** +@brief Base for three-dimensional transformations supporting translation and rotation + +@see AbstractTranslationRotation2D +*/ +template class AbstractTranslationRotation3D: public AbstractTransformation<3, T> { + public: + /** + * @brief Translate object + * @param vector Translation vector + * @param type Transformation type + * @return Pointer to self (for method chaining) + * + * @see Vector3::xAxis(), Vector3::yAxis(), Vector3::zAxis() + */ + virtual AbstractTranslationRotation3D* translate(const Math::Vector3& vector, TransformationType type = TransformationType::Global) = 0; + + /** + * @brief Rotate object + * @param angle Angle in radians, counterclockwise + * @param normalizedAxis Normalized rotation axis + * @param type Transformation type + * @return Pointer to self (for method chaining) + * + * @see deg(), rad(), Vector3::xAxis(), Vector3::yAxis(), Vector3::zAxis() + */ + virtual AbstractTranslationRotation3D* rotate(T angle, const Math::Vector3& normalizedAxis, TransformationType type = TransformationType::Global) = 0; +}; + +}} + +#endif diff --git a/src/SceneGraph/AbstractTranslationRotationScaling2D.h b/src/SceneGraph/AbstractTranslationRotationScaling2D.h new file mode 100644 index 000000000..6d5d3f66d --- /dev/null +++ b/src/SceneGraph/AbstractTranslationRotationScaling2D.h @@ -0,0 +1,46 @@ +#ifndef Magnum_SceneGraph_AbstractTranslationRotationScaling2D_h +#define Magnum_SceneGraph_AbstractTranslationRotationScaling2D_h +/* + Copyright © 2010, 2011, 2012 Vladimír Vondruš + + This file is part of Magnum. + + Magnum is free software: you can redistribute it and/or modify + it under the terms of the GNU Lesser General Public License version 3 + only, as published by the Free Software Foundation. + + Magnum is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU Lesser General Public License version 3 for more details. +*/ + +/** @file + * @brief Class Magnum::SceneGraph::AbstractTranslationRotationScaling2D + */ + +#include "AbstractTranslationRotation2D.h" + +namespace Magnum { namespace SceneGraph { + +/** +@brief Base for two-dimensional transformations supporting translation, rotation and scaling + +@see AbstractTranslationRotationScaling2D +*/ +template class AbstractTranslationRotationScaling2D: public AbstractTranslationRotation2D { + public: + /** + * @brief Scale object + * @param vector Scaling vector + * @param type Transformation type + * @return Pointer to self (for method chaining) + * + * @see Vector2::xScale(), Vector2::yScale() + */ + virtual AbstractTranslationRotationScaling2D* scale(const Math::Vector2& vector, TransformationType type = TransformationType::Global) = 0; +}; + +}} + +#endif diff --git a/src/SceneGraph/AbstractTranslationRotationScaling3D.h b/src/SceneGraph/AbstractTranslationRotationScaling3D.h new file mode 100644 index 000000000..c4c880ae5 --- /dev/null +++ b/src/SceneGraph/AbstractTranslationRotationScaling3D.h @@ -0,0 +1,46 @@ +#ifndef Magnum_SceneGraph_AbstractTranslationRotationScaling3D_h +#define Magnum_SceneGraph_AbstractTranslationRotationScaling3D_h +/* + Copyright © 2010, 2011, 2012 Vladimír Vondruš + + This file is part of Magnum. + + Magnum is free software: you can redistribute it and/or modify + it under the terms of the GNU Lesser General Public License version 3 + only, as published by the Free Software Foundation. + + Magnum is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU Lesser General Public License version 3 for more details. +*/ + +/** @file + * @brief Class Magnum::SceneGraph::AbstractTranslationRotationScaling3D + */ + +#include "AbstractTranslationRotation3D.h" + +namespace Magnum { namespace SceneGraph { + +/** +@brief Base for three-dimensional transformations supporting translation, rotation and scaling + +@see AbstractTranslationRotationScaling2D +*/ +template class AbstractTranslationRotationScaling3D: public AbstractTranslationRotation3D { + public: + /** + * @brief Scale object + * @param vector Scaling vector + * @param type Transformation type + * @return Pointer to self (for method chaining) + * + * @see Vector3::xScale(), Vector3::yScale(), Vector3::zScale() + */ + virtual AbstractTranslationRotationScaling3D* scale(const Math::Vector3& vector, TransformationType type = TransformationType::Global) = 0; +}; + +}} + +#endif diff --git a/src/SceneGraph/CMakeLists.txt b/src/SceneGraph/CMakeLists.txt index 7eb5ef7b6..12c1a2ede 100644 --- a/src/SceneGraph/CMakeLists.txt +++ b/src/SceneGraph/CMakeLists.txt @@ -1,25 +1,33 @@ -# Files shared between main library and unit test library -set(MagnumSceneGraph_SRCS - Camera.cpp) +set(MagnumSceneGraph_SRCS ) set(MagnumSceneGraph_HEADERS - Camera.h + AbstractFeature.h + AbstractObject.h + AbstractTransformation.h + AbstractTranslationRotation2D.h + AbstractTranslationRotation3D.h + AbstractTranslationRotationScaling2D.h + AbstractTranslationRotationScaling3D.h + MatrixTransformation2D.h + MatrixTransformation3D.h Object.h + Object.hpp Scene.h magnumSceneGraphVisibility.h) -add_library(MagnumSceneGraphObjects OBJECT ${MagnumSceneGraph_SRCS}) +# add_library(MagnumSceneGraphObjects OBJECT ${MagnumSceneGraph_SRCS}) # Files compiled with different flags for main library and unit test library set(MagnumSceneGraph_GracefulAssert_SRCS - Object.cpp) + MatrixTransformation2D.cpp + MatrixTransformation3D.cpp) # Set shared library flags for the objects, as they will be part of shared lib # TODO: fix when CMake sets target_EXPORTS for OBJECT targets as well -set_target_properties(MagnumSceneGraphObjects PROPERTIES COMPILE_FLAGS "-DMagnumSceneGraphObjects_EXPORTS ${CMAKE_SHARED_LIBRARY_CXX_FLAGS}") +# set_target_properties(MagnumSceneGraphObjects PROPERTIES COMPILE_FLAGS "-DMagnumSceneGraphObjects_EXPORTS ${CMAKE_SHARED_LIBRARY_CXX_FLAGS}") # SceneGraph library add_library(MagnumSceneGraph SHARED - $ +# $ ${MagnumSceneGraph_GracefulAssert_SRCS}) target_link_libraries(MagnumSceneGraph Magnum) @@ -31,7 +39,7 @@ if(BUILD_TESTS) # Library with graceful assert for testing add_library(MagnumSceneGraphTestLib SHARED - $ +# $ ${MagnumSceneGraph_GracefulAssert_SRCS}) set_target_properties(MagnumSceneGraphTestLib PROPERTIES COMPILE_FLAGS -DCORRADE_GRACEFUL_ASSERT) target_link_libraries(MagnumSceneGraphTestLib Magnum) diff --git a/src/SceneGraph/MatrixTransformation2D.cpp b/src/SceneGraph/MatrixTransformation2D.cpp new file mode 100644 index 000000000..340371b97 --- /dev/null +++ b/src/SceneGraph/MatrixTransformation2D.cpp @@ -0,0 +1,26 @@ +/* + Copyright © 2010, 2011, 2012 Vladimír Vondruš + + This file is part of Magnum. + + Magnum is free software: you can redistribute it and/or modify + it under the terms of the GNU Lesser General Public License version 3 + only, as published by the Free Software Foundation. + + Magnum is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU Lesser General Public License version 3 for more details. +*/ + +#include "MatrixTransformation2D.h" + +#include "Object.hpp" + +namespace Magnum { namespace SceneGraph { + +#ifndef DOXYGEN_GENERATING_OUTPUT +template class SCENEGRAPH_EXPORT Object>; +#endif + +}} diff --git a/src/SceneGraph/MatrixTransformation2D.h b/src/SceneGraph/MatrixTransformation2D.h new file mode 100644 index 000000000..e2e9b783f --- /dev/null +++ b/src/SceneGraph/MatrixTransformation2D.h @@ -0,0 +1,136 @@ +#ifndef Magnum_SceneGraph_MatrixTransformation2D_h +#define Magnum_SceneGraph_MatrixTransformation2D_h +/* + Copyright © 2010, 2011, 2012 Vladimír Vondruš + + This file is part of Magnum. + + Magnum is free software: you can redistribute it and/or modify + it under the terms of the GNU Lesser General Public License version 3 + only, as published by the Free Software Foundation. + + Magnum is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU Lesser General Public License version 3 for more details. +*/ + +/** @file + * @brief Class Magnum::SceneGraph::MatrixTransformation2D + */ + +#include "Math/Matrix3.h" +#include "AbstractTranslationRotationScaling2D.h" +#include "Object.h" + +namespace Magnum { namespace SceneGraph { + +/** +@brief Two-dimensional transformation implemented using matrices + +@see MatrixTransformation3D +*/ +template class MatrixTransformation2D: public AbstractTranslationRotationScaling2D { + public: + /** @brief Transformation matrix type */ + typedef typename DimensionTraits<2, T>::MatrixType DataType; + + #ifndef DOXYGEN_GENERATING_OUTPUT + inline constexpr static Math::Matrix3 fromMatrix(const Math::Matrix3& matrix) { + return matrix; + } + + inline constexpr static Math::Matrix3 toMatrix(const Math::Matrix3& transformation) { + return transformation; + } + + inline static Math::Matrix3 compose(const Math::Matrix3& parent, const Math::Matrix3& child) { + return parent*child; + } + + inline static Math::Matrix3 inverted(const Math::Matrix3& transformation) { + return transformation.inverted(); + } + + inline Math::Matrix3 transformation() const { + return _transformation; + } + #endif + + /** + * @brief Set transformation + * @return Pointer to self (for method chaining) + */ + MatrixTransformation2D* setTransformation(const Math::Matrix3& transformation) { + /* Setting transformation is forbidden for the scene */ + /** @todo Assert for this? */ + /** @todo Do this in some common code? */ + if(!static_cast>*>(this)->isScene()) { + _transformation = transformation; + static_cast>*>(this)->setDirty(); + } + + return this; + } + + /** + * @brief Multiply transformation + * @param transformation Transformation + * @param type Transformation type + * @return Pointer to self (for method chaining) + */ + inline MatrixTransformation2D* multiplyTransformation(const Math::Matrix3& transformation, TransformationType type = TransformationType::Global) { + setTransformation(type == TransformationType::Global ? + transformation*_transformation : _transformation*transformation); + return this; + } + + /** + * @copydoc AbstractTranslationRotationScaling2D::translate() + * Same as calling multiplyTransformation() with Matrix3::translation(). + */ + inline MatrixTransformation2D* translate(const Math::Vector2& vector, TransformationType type = TransformationType::Global) override { + multiplyTransformation(Math::Matrix3::translation(vector), type); + return this; + } + + /** + * @copydoc AbstractTranslationRotationScaling2D::rotate() + * Same as calling multiplyTransformation() with Matrix3::rotation(). + */ + inline MatrixTransformation2D* rotate(T angle, TransformationType type = TransformationType::Global) override { + multiplyTransformation(Math::Matrix3::rotation(angle), type); + return this; + } + + /** + * @copydoc AbstractTranslationRotationScaling2D::scale() + * Same as calling multiplyTransformation() with Matrix3::scaling(). + */ + inline MatrixTransformation2D* scale(const Math::Vector2& vector, TransformationType type = TransformationType::Global) override { + multiplyTransformation(Math::Matrix3::scaling(vector), type); + return this; + } + + /** + * @brief Move object in stacking order + * @param under Sibling object under which to move or `nullptr`, + * if you want to move it above all. + * @return Pointer to self (for method chaining) + */ + inline MatrixTransformation2D* move(Object>* under) { + static_cast*>(this)->Corrade::Containers::LinkedList>>::move(this, under); + return this; + } + + protected: + /* Allow construction only from Object */ + inline MatrixTransformation2D() {} + + private: + Math::Matrix3 _transformation; +}; + +}} + +#endif diff --git a/src/SceneGraph/MatrixTransformation3D.cpp b/src/SceneGraph/MatrixTransformation3D.cpp new file mode 100644 index 000000000..8c2d78a48 --- /dev/null +++ b/src/SceneGraph/MatrixTransformation3D.cpp @@ -0,0 +1,26 @@ +/* + Copyright © 2010, 2011, 2012 Vladimír Vondruš + + This file is part of Magnum. + + Magnum is free software: you can redistribute it and/or modify + it under the terms of the GNU Lesser General Public License version 3 + only, as published by the Free Software Foundation. + + Magnum is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU Lesser General Public License version 3 for more details. +*/ + +#include "MatrixTransformation3D.h" + +#include "Object.hpp" + +namespace Magnum { namespace SceneGraph { + +#ifndef DOXYGEN_GENERATING_OUTPUT +template class SCENEGRAPH_EXPORT Object>; +#endif + +}} diff --git a/src/SceneGraph/MatrixTransformation3D.h b/src/SceneGraph/MatrixTransformation3D.h new file mode 100644 index 000000000..493b275ef --- /dev/null +++ b/src/SceneGraph/MatrixTransformation3D.h @@ -0,0 +1,125 @@ +#ifndef Magnum_SceneGraph_MatrixTransformation3D_h +#define Magnum_SceneGraph_MatrixTransformation3D_h +/* + Copyright © 2010, 2011, 2012 Vladimír Vondruš + + This file is part of Magnum. + + Magnum is free software: you can redistribute it and/or modify + it under the terms of the GNU Lesser General Public License version 3 + only, as published by the Free Software Foundation. + + Magnum is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU Lesser General Public License version 3 for more details. +*/ + +/** @file + * @brief Class Magnum::SceneGraph::MatrixTransformation3D + */ + +#include "Math/Matrix4.h" +#include "AbstractTranslationRotationScaling3D.h" +#include "Object.h" + +namespace Magnum { namespace SceneGraph { + +/** +@brief Three-dimensional transformation implemented using matrices + +@see MatrixTransformation2D +*/ +template class MatrixTransformation3D: public AbstractTranslationRotationScaling3D { + public: + /** @brief Transformation matrix type */ + typedef typename DimensionTraits<3, T>::MatrixType DataType; + + #ifndef DOXYGEN_GENERATING_OUTPUT + inline constexpr static Math::Matrix4 fromMatrix(const Math::Matrix4& matrix) { + return matrix; + } + + inline constexpr static Math::Matrix4 toMatrix(const Math::Matrix4& transformation) { + return transformation; + } + + inline static Math::Matrix4 compose(const Math::Matrix4& parent, const Math::Matrix4& child) { + return parent*child; + } + + inline static Math::Matrix4 inverted(const Math::Matrix4& transformation) { + return transformation.inverted(); + } + + inline Math::Matrix4 transformation() const { + return _transformation; + } + #endif + + /** + * @brief Set transformation + * @return Pointer to self (for method chaining) + */ + MatrixTransformation3D* setTransformation(const Math::Matrix4& transformation) { + /* Setting transformation is forbidden for the scene */ + /** @todo Assert for this? */ + /** @todo Do this in some common code? */ + if(!static_cast>*>(this)->isScene()) { + _transformation = transformation; + static_cast>*>(this)->setDirty(); + } + + return this; + } + + /** + * @brief Multiply transformation + * @param transformation Transformation + * @param type Transformation type + * @return Pointer to self (for method chaining) + */ + inline MatrixTransformation3D* multiplyTransformation(const Math::Matrix4& transformation, TransformationType type = TransformationType::Global) { + setTransformation(type == TransformationType::Global ? + transformation*_transformation : _transformation*transformation); + return this; + } + + /** + * @copydoc AbstractTranslationRotationScaling3D::translate() + * Same as calling multiplyTransformation() with Matrix4::translation(). + */ + inline MatrixTransformation3D* translate(const Math::Vector3& vector, TransformationType type = TransformationType::Global) override { + multiplyTransformation(Math::Matrix4::translation(vector), type); + return this; + } + + /** + * @copydoc AbstractTranslationRotationScaling3D::rotate() + * Same as calling multiplyTransformation() with Matrix4::rotation(). + */ + inline MatrixTransformation3D* rotate(T angle, const Math::Vector3& normalizedAxis, TransformationType type = TransformationType::Global) override { + multiplyTransformation(Math::Matrix4::rotation(angle, normalizedAxis), type); + return this; + } + + /** + * @copydoc AbstractTranslationRotationScaling3D::scale() + * Same as calling multiplyTransformation() with Matrix4::scaling(). + */ + inline MatrixTransformation3D* scale(const Math::Vector3& vector, TransformationType type = TransformationType::Global) override { + multiplyTransformation(Math::Matrix4::scaling(vector), type); + return this; + } + + protected: + /* Allow construction only from Object */ + inline MatrixTransformation3D() {} + + private: + Math::Matrix4 _transformation; +}; + +}} + +#endif diff --git a/src/SceneGraph/Object.cpp b/src/SceneGraph/Object.cpp deleted file mode 100644 index 3cf9b2316..000000000 --- a/src/SceneGraph/Object.cpp +++ /dev/null @@ -1,147 +0,0 @@ -/* - Copyright © 2010, 2011, 2012 Vladimír Vondruš - - This file is part of Magnum. - - Magnum is free software: you can redistribute it and/or modify - it under the terms of the GNU Lesser General Public License version 3 - only, as published by the Free Software Foundation. - - Magnum is distributed in the hope that it will be useful, - but WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - GNU Lesser General Public License version 3 for more details. -*/ - -#include "Object.h" - -#include - -#include "Scene.h" -#include "Camera.h" - -using namespace std; -using namespace Magnum::Math; - -namespace Magnum { namespace SceneGraph { - -template typename AbstractObject::ObjectType* AbstractObject::setParent(ObjectType* parent) { - /* Skip if nothing to do or this is scene */ - if(this->parent() == parent || isScene()) return static_cast(this); - - /* Only Fry can be his own grandfather */ - ObjectType* p = parent; - while(p) { - /** @todo Assert for this */ - if(p == this) return static_cast(this); - p = p->parent(); - } - - /* Remove the object from old parent children list */ - if(this->parent()) - this->parent()->cut(static_cast(this)); - - /* Add the object to list of new parent */ - if(parent) - parent->insert(static_cast(this)); - - setDirty(); - return static_cast(this); -} - -template typename DimensionTraits::MatrixType AbstractObject::absoluteTransformation(CameraType* camera) { - /* Shortcut for absolute transformation of camera relative to itself */ - if(camera == this) return typename DimensionTraits::MatrixType(); - - typename DimensionTraits::MatrixType t = _transformation; - - ObjectType* p = parent(); - while(p != nullptr) { - t = p->transformation()*t; - - /* We got to the scene, multiply with camera matrix */ - if(p->isScene()) { - if(camera) { - CORRADE_ASSERT(camera->scene() == scene(), "Object::absoluteTransformation(): the camera is not part of the same scene as object!", t); - t = camera->cameraMatrix()*t; - } - - break; - } - - p = p->parent(); - } - - CORRADE_ASSERT(p != nullptr || camera == nullptr, "Object::absoluteTransformation(): the object is not part of camera scene!", t); - - return t; -} - -template typename AbstractObject::SceneType* AbstractObject::scene() { - /* Goes up the family tree until it finds object which is parent of itself - (that's the scene) */ - ObjectType* p = parent(); - while(p != nullptr) { - if(p->isScene()) return static_cast(p); - p = p->parent(); - } - - return nullptr; -} - -template typename AbstractObject::ObjectType* AbstractObject::setTransformation(const typename DimensionTraits::MatrixType& transformation) { - /* Setting transformation is forbidden for the scene */ - /** @todo Assert for this? */ - if(isScene()) return static_cast(this); - - _transformation = transformation; - setDirty(); - return static_cast(this); -} - -template void AbstractObject::setDirty() { - /* The object (and all its children) are already dirty, nothing to do */ - if(dirty) return; - - dirty = true; - - /* Make all children dirty */ - for(ObjectType* i = firstChild(); i; i = i->nextSibling()) - i->setDirty(); -} - -template void AbstractObject::setClean() { - /* The object (and all its parents) are already clean, nothing to do */ - if(!dirty) return; - - /* Collect all parents */ - stack objects; - ObjectType* p = static_cast(this); - for(;;) { - objects.push(p); - - /* Stop on root object / clean object */ - if(p->parent() == nullptr || !p->parent()->isDirty()) - break; - - p = p->parent(); - } - - /* Call setClean(const Matrix4&) for every parent and also this object */ - ObjectType* o = objects.top(); - objects.pop(); - typename DimensionTraits::MatrixType absoluteTransformation = o->absoluteTransformation(); - o->clean(absoluteTransformation); - while(!objects.empty()) { - o = objects.top(); - objects.pop(); - absoluteTransformation = absoluteTransformation*o->transformation(); - o->clean(absoluteTransformation); - } -} - -/* Explicitly instantiate the templates */ -template class AbstractObject<2>; -template class AbstractObject<3>; - -}} diff --git a/src/SceneGraph/Object.h b/src/SceneGraph/Object.h index 61038bc5c..73676b6f0 100644 --- a/src/SceneGraph/Object.h +++ b/src/SceneGraph/Object.h @@ -16,89 +16,71 @@ */ /** @file - * @brief Class Magnum::SceneGraph::AbstractObject, Magnum::SceneGraph::Object2D, Magnum::SceneGraph::Object3D + * @brief Class Magnum::SceneGraph::Object */ -#include +#include -#include "Math/Matrix3.h" -#include "Math/Matrix4.h" -#include "Magnum.h" -#include "DimensionTraits.h" +#include "AbstractFeature.h" +#include "AbstractObject.h" #include "magnumSceneGraphVisibility.h" namespace Magnum { namespace SceneGraph { -class Camera2D; -class Camera3D; -class Object2D; -class Object3D; -template class Scene; -typedef Scene<2> Scene2D; -typedef Scene<3> Scene3D; +template class Scene; #ifndef DOXYGEN_GENERATING_OUTPUT namespace Implementation { - template struct ObjectDimensionTraits {}; - - template<> struct ObjectDimensionTraits<2> { - typedef Object2D ObjectType; - typedef Camera2D CameraType; - typedef Scene2D SceneType; + enum class ObjectFlag: std::uint8_t { + Dirty = 1 << 0 }; - template<> struct ObjectDimensionTraits<3> { - typedef Object3D ObjectType; - typedef Camera3D CameraType; - typedef Scene3D SceneType; - }; + typedef Corrade::Containers::EnumSet ObjectFlags; + + CORRADE_ENUMSET_OPERATORS(ObjectFlags) } #endif /** -@todo User-specified Object implementation: -- for front-to-back sorting, LoD changes etc. -- for different parent/children implementation (e.g. no std::set, direct - access to scene etc.) -- for using doubles/halves instead of floats -- for using quat + position instead of matrices (where (asymmetric) scaling is - not needed) -*/ +@brief %Object -/** -@brief Base for all positioned objects +Base of scene graph. Contains specific transformation implementation, takes +care of parent/children relationships and contains features. See +@ref scenegraph for introduction. -@todo Transform transformation when changing parent, so the object stays in -place. - */ -template class SCENEGRAPH_EXPORT AbstractObject: public Corrade::Containers::LinkedList::ObjectType>, public Corrade::Containers::LinkedListItem::ObjectType, typename Implementation::ObjectDimensionTraits::ObjectType> { - #ifndef DOXYGEN_GENERATING_OUTPUT - AbstractObject(const AbstractObject& other) = delete; - AbstractObject(AbstractObject&& other) = delete; - AbstractObject& operator=(const AbstractObject& other) = delete; - AbstractObject& operator=(AbstractObject&& other) = delete; - #endif +@section Object-explicit-specializations Explicit template specializations - public: - static const std::uint8_t Dimensions = dimensions; /**< @brief %Object dimension count */ +The following specialization are explicitly compiled into SceneGraph library. +For other specializations you have to use Object.hpp implementation file to +avoid linker errors. See @ref compilation-speedup-hpp for more information. - /** @brief %Object type for given dimension count */ - typedef typename Implementation::ObjectDimensionTraits::ObjectType ObjectType; + - @ref MatrixTransformation2D "Object>" + - @ref MatrixTransformation3D "Object>" - /** @brief %Camera type for given dimension count */ - typedef typename Implementation::ObjectDimensionTraits::CameraType CameraType; +@see Scene, AbstractFeature, AbstractTransformation +*/ +template class Object: public AbstractObject, public Transformation + #ifndef DOXYGEN_GENERATING_OUTPUT + , private Corrade::Containers::LinkedList>, private Corrade::Containers::LinkedListItem, Object> + #endif +{ + friend class Corrade::Containers::LinkedList>; + friend class Corrade::Containers::LinkedListItem, Object>; - /** @brief %Scene type for given dimension count */ - typedef typename Implementation::ObjectDimensionTraits::SceneType SceneType; + #ifndef DOXYGEN_GENERATING_OUTPUT + Object(const Object& other) = delete; + Object(Object&& other) = delete; + Object& operator=(const Object& other) = delete; + Object& operator=(Object&& other) = delete; + #endif + public: /** * @brief Constructor * @param parent Parent object - * - * Sets all transformations to their default values. */ - inline AbstractObject(ObjectType* parent = nullptr): dirty(true) { + inline Object(Object* parent = nullptr): flags(Flag::Dirty) { setParent(parent); } @@ -108,9 +90,13 @@ template class SCENEGRAPH_EXPORT AbstractObject: public * Removes itself from parent's children list and destroys all own * children. */ - virtual ~AbstractObject() = 0; + inline virtual ~Object() {} - /** @{ @name Scene hierarchy */ + /** + * @{ @name Scene hierarchy + * + * See @ref scenegraph-hierarchy for more information. + */ /** @brief Whether this object is scene */ virtual inline bool isScene() const { return false; } @@ -119,296 +105,135 @@ template class SCENEGRAPH_EXPORT AbstractObject: public * @brief %Scene * @return %Scene or `nullptr`, if the object is not part of any scene. */ - SceneType* scene(); + Scene* scene(); + + /** @overload */ + const Scene* scene() const; /** @brief Parent object or `nullptr`, if this is root object */ - inline ObjectType* parent() { return Corrade::Containers::LinkedListItem::list(); } + inline Object* parent() { + return Corrade::Containers::LinkedListItem, Object>::list(); + } + + /** @overload */ + inline const Object* parent() const { + return Corrade::Containers::LinkedListItem, Object>::list(); + } /** @brief Previous sibling object or `nullptr`, if this is first object */ - inline ObjectType* previousSibling() { return Corrade::Containers::LinkedListItem::previous(); } + inline Object* previousSibling() { + return Corrade::Containers::LinkedListItem, Object>::previous(); + } + + /** @overload */ + inline const Object* previousSibling() const { + return Corrade::Containers::LinkedListItem, Object>::previous(); + } /** @brief Next sibling object or `nullptr`, if this is last object */ - inline ObjectType* nextSibling() { return Corrade::Containers::LinkedListItem::next(); } + inline Object* nextSibling() { + return Corrade::Containers::LinkedListItem, Object>::next(); + } + + /** @overload */ + inline const Object* nextSibling() const { + return Corrade::Containers::LinkedListItem, Object>::next(); + } /** @brief Whether this object has children */ - inline bool hasChildren() const { return !Corrade::Containers::LinkedList::isEmpty(); } + inline bool hasChildren() const { + return !Corrade::Containers::LinkedList>::isEmpty(); + } /** @brief First child object or `nullptr`, if this object has no children */ - inline ObjectType* firstChild() { return Corrade::Containers::LinkedList::first(); } + inline Object* firstChild() { + return Corrade::Containers::LinkedList>::first(); + } + + /** @overload */ + inline const Object* firstChild() const { + return Corrade::Containers::LinkedList>::first(); + } /** @brief Last child object or `nullptr`, if this object has no children */ - inline ObjectType* lastChild() { return Corrade::Containers::LinkedList::last(); } + inline Object* lastChild() { + return Corrade::Containers::LinkedList>::last(); + } + + /** @overload */ + inline const Object* lastChild() const { + return Corrade::Containers::LinkedList>::last(); + } /** * @brief Set parent object * @return Pointer to self (for method chaining) */ - ObjectType* setParent(ObjectType* parent); + Object* setParent(Object* parent); /*@}*/ - /** @{ @name Object transformation - * - * All transformations (except absoluteTransformation()) are relative - * to parent. - */ + /** @{ @name Object transformation */ - /** @brief Transformation type */ - enum class Transformation: char { - /** Global transformation, applied after all other transformations. */ - Global = 0x00, - - /** Local transformation, applied before all other transformations. */ - Local = 0x01 - }; - - /** @brief Transformation */ - inline typename DimensionTraits::MatrixType transformation() const { - return _transformation; + inline typename DimensionTraits::MatrixType absoluteTransformationMatrix() const override { + return Transformation::toMatrix(absoluteTransformation()); } /** - * @brief Absolute transformation + * @brief Transformation relative to root object * - * Returns absolute transformation matrix relative to the camera or - * root object, if no camera is specified. If the camera is specified, - * it should be part of the same scene as object. - * - * Note that the absolute transformation is computed from all parent - * objects every time it is asked, unless this function is - * reimplemented in a different way. - */ - virtual typename DimensionTraits::MatrixType absoluteTransformation(CameraType* camera = nullptr); - - /** - * @brief Set transformation - * @return Pointer to self (for method chaining) + * @see absoluteTransformationMatrix() */ - ObjectType* setTransformation(const typename DimensionTraits::MatrixType& transformation); - - /** - * @brief Multiply transformation - * @param transformation Transformation - * @param type Transformation type - * @return Pointer to self (for method chaining) - */ - inline ObjectType* multiplyTransformation(const typename DimensionTraits::MatrixType& transformation, Transformation type = Transformation::Global) { - setTransformation(type == Transformation::Global ? - transformation*_transformation : _transformation*transformation); - return static_cast(this); - } + typename Transformation::DataType absoluteTransformation() const; /*@}*/ /** - * @brief Draw object - * @param transformationMatrix %Matrix specifying object - * transformation relative to the scene. - * @param camera Active camera (containing - * projection matrix) - * - * Default implementation does nothing. - */ - virtual void draw(const typename DimensionTraits::MatrixType& transformationMatrix, CameraType* camera); - - /** @{ @name Caching helpers - * - * If the object (absolute) transformation or anything depending on it - * is used many times when drawing (such as e.g. position of light - * object), it's good to cache these values, so they don't have to be - * recalculated again on every request. - * - * If setDirty() is called on an object (or the object is transformed), - * it and all its children are marked as dirty. If any object is - * already dirty, it and all its children are skipped, because they - * are already dirty too. - * - * If setClean() is called on an object, it and all its parents are - * cleaned. If any object is already clean, it and all its parents are - * skipped, because they are already clean too. + * @{ @name Transformation caching * - * These functions are used to manage dirty status of the object. If - * the object doesn't cache anything, it's no need to bother about - * them, but if does, clean() should be reimplemented and used to - * regenerate the cache. - */ - - /** - * @brief Whether the object is dirty - * @return True, if transformation of the object, any parent or camera - * has changed since last asking, false otherwise. + * See @ref scenegraph-caching for more information. */ - inline bool isDirty() const { return dirty; } /** - * @brief Set object and all its children as dirty + * @brief Whether absolute transformation is dirty * - * Recursively calls setDirty() on every child. If the object is - * already marked as dirty, the function does nothing. It is usually - * not needed to reimplement this function, only if you for example - * need to reset some state on object which is not child of this. All - * computations should be done in setClean(). + * Returns `true` if transformation of the object or any parent has + * changed since last call to setClean(), `false` otherwise. * - * Reimplementations should call this function at the end, i.e.: - * @code - * void setDirty() { - * // ... + * All objects are dirty by default. * - * Object::setDirty(); - * } - * @endcode + * @see @ref scenegraph-caching */ - virtual void setDirty(); + inline bool isDirty() const { return !!(flags & Flag::Dirty); } /** - * @brief Set object and all its parents as clean + * @brief Set object absolute transformation as dirty * - * Recursively calls clean() on every parent which is not already - * clean. + * Calls AbstractFeature::markDirty() on all object features and + * recursively calls setDirty() on every child object which is not + * already dirty. If the object is already marked as dirty, the + * function does nothing. + * @see @ref scenegraph-caching, setClean(), isDirty() */ - void setClean(); + void setDirty(); - protected: /** - * @brief Clean the object - * - * When reimplementing, use absolute transformation passed as - * parameter instead of absoluteTransformation(), which is not - * efficient. The reimplementation should call this function at the - * beginning, i.e.: - * @code - * void clean(const Matrix4& absoluteTransformation) { - * Object::clean(absoluteTransformation); + * @brief Clean object absolute transformation * - * // ... - * } - * @endcode + * Calls AbstractFeature::clean() and/or AbstractFeature::cleanInverted() + * on all object features which have caching enabled and recursively + * calls setClean() on every parent which is not already clean. If the + * object is already clean, the function does nothing. + * @see @ref scenegraph-caching, setDirty(), isDirty() */ - virtual void clean(const typename DimensionTraits::MatrixType& absoluteTransformation); + void setClean(); /*@}*/ private: - /* Hide base class members, as they are aliased to more meaningful names */ - using Corrade::Containers::LinkedList::first; - using Corrade::Containers::LinkedList::last; - using Corrade::Containers::LinkedList::isEmpty; - using Corrade::Containers::LinkedList::insert; - using Corrade::Containers::LinkedList::cut; - using Corrade::Containers::LinkedList::move; - using Corrade::Containers::LinkedList::erase; - using Corrade::Containers::LinkedList::clear; - using Corrade::Containers::LinkedListItem::list; - using Corrade::Containers::LinkedListItem::previous; - using Corrade::Containers::LinkedListItem::next; - - typename DimensionTraits::MatrixType _transformation; - bool dirty; -}; - -template inline AbstractObject::~AbstractObject() {} - -/* Implementations for inline functions with unused parameters */ -template inline void AbstractObject::draw(const typename DimensionTraits::MatrixType&, CameraType*) {} -template inline void AbstractObject::clean(const typename DimensionTraits::MatrixType&) { dirty = false; } - -/** -@brief Two-dimensional object - -@see Object3D -*/ -class SCENEGRAPH_EXPORT Object2D: public AbstractObject<2> { - public: - /** @copydoc AbstractObject::AbstractObject() */ - inline Object2D(Object2D* parent = nullptr): AbstractObject<2>(parent) {} - - /** - * @brief Translate object - * @return Pointer to self (for method chaining) - * - * Same as calling multiplyTransformation() with Matrix3::translation(). - */ - inline Object2D* translate(const Vector2& vec, Transformation type = Transformation::Global) { - multiplyTransformation(Matrix3::translation(vec), type); - return this; - } - - /** - * @brief Scale object - * @return Pointer to self (for method chaining) - * - * Same as calling multiplyTransformation() with Matrix3::scaling(). - */ - inline Object2D* scale(const Vector2& vec, Transformation type = Transformation::Global) { - multiplyTransformation(Matrix3::scaling(vec), type); - return this; - } - - /** - * @brief Rotate object - * @return Pointer to self (for method chaining) - * - * Same as calling multiplyTransformation() with Matrix3::rotation(). - */ - inline Object2D* rotate(GLfloat angle, Transformation type = Transformation::Global) { - multiplyTransformation(Matrix3::rotation(angle), type); - return this; - } - - /** - * @brief Move object in stacking order - * @param under Sibling object under which to move or `nullptr`, - * if you want to move it above all. - * @return Pointer to self (for method chaining) - */ - inline Object2D* move(Object2D* under) { - parent()->Corrade::Containers::LinkedList::move(this, under); - return this; - } -}; - -/** -@brief Three-dimensional object - -@see Object2D -*/ -class SCENEGRAPH_EXPORT Object3D: public AbstractObject<3> { - public: - /** @copydoc AbstractObject::AbstractObject() */ - inline Object3D(Object3D* parent = nullptr): AbstractObject<3>(parent) {} - - /** - * @brief Translate object - * @return Pointer to self (for method chaining) - * - * Same as calling multiplyTransformation() with Matrix4::translation(). - */ - inline Object3D* translate(const Vector3& vec, Transformation type = Transformation::Global) { - multiplyTransformation(Matrix4::translation(vec), type); - return this; - } - - /** - * @brief Scale object - * @return Pointer to self (for method chaining) - * - * Same as calling multiplyTransformation() with Matrix4::scaling(). - */ - inline Object3D* scale(const Vector3& vec, Transformation type = Transformation::Global) { - multiplyTransformation(Matrix4::scaling(vec), type); - return this; - } - - /** - * @brief Rotate object - * @return Pointer to self (for method chaining) - * - * Same as calling multiplyTransformation() with Matrix4::rotation(). - */ - inline Object3D* rotate(GLfloat angle, const Vector3& vec, Transformation type = Transformation::Global) { - multiplyTransformation(Matrix4::rotation(angle, vec), type); - return this; - } + typedef Implementation::ObjectFlag Flag; + typedef Implementation::ObjectFlags Flags; + Flags flags; }; }} diff --git a/src/SceneGraph/Object.hpp b/src/SceneGraph/Object.hpp new file mode 100644 index 000000000..dc54f42ca --- /dev/null +++ b/src/SceneGraph/Object.hpp @@ -0,0 +1,159 @@ +#ifndef Magnum_SceneGraph_Object_hpp +#define Magnum_SceneGraph_Object_hpp +/* + Copyright © 2010, 2011, 2012 Vladimír Vondruš + + This file is part of Magnum. + + Magnum is free software: you can redistribute it and/or modify + it under the terms of the GNU Lesser General Public License version 3 + only, as published by the Free Software Foundation. + + Magnum is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU Lesser General Public License version 3 for more details. +*/ + +/** @file + * @brief @ref compilation-speedup-hpp "Template implementation" for Object.h + */ + +#include "Object.h" + +#include + +#include "Scene.h" + +namespace Magnum { namespace SceneGraph { + +template Scene* Object::scene() { + Object* p(this); + while(p && !p->isScene()) p = p->parent(); + return static_cast*>(p); +} + +template const Scene* Object::scene() const { + const Object* p(this); + while(p && !p->isScene()) p = p->parent(); + return static_cast*>(p); +} + +template Object* Object::setParent(Object* parent) { + /* Skip if parent is already parent or this is scene (which cannot have parent) */ + /** @todo Assert for setting parent to scene */ + if(this->parent() == parent || isScene()) return this; + + /* Object cannot be parented to its child */ + Object* p = parent; + while(p) { + /** @todo Assert for this */ + if(p == this) return this; + p = p->parent(); + } + + /* Remove the object from old parent children list */ + if(this->parent()) this->parent()->Corrade::Containers::LinkedList>::cut(this); + + /* Add the object to list of new parent */ + if(parent) parent->Corrade::Containers::LinkedList>::insert(this); + + setDirty(); + return this; +} + +template typename Transformation::DataType Object::absoluteTransformation() const { + if(!parent()) return Transformation::transformation(); + return Transformation::compose(parent()->absoluteTransformation(), Transformation::transformation()); +} + +template void Object::setDirty() { + /* The transformation of this object (and all children) is already dirty, + nothing to do */ + if(flags & Flag::Dirty) return; + + Object* self = static_cast*>(this); + + /* Make all features dirty */ + for(AbstractFeature* i = self->firstFeature(); i; i = i->nextFeature()) + i->markDirty(); + + /* Make all children dirty */ + for(Object* i = self->firstChild(); i; i = i->nextSibling()) + i->setDirty(); + + /* Mark object as dirty */ + flags |= Flag::Dirty; +} + +template void Object::setClean() { + /* The object (and all its parents) are already clean, nothing to do */ + if(!(flags & Flag::Dirty)) return; + + /* Collect all parents, compute base transformation */ + std::stack*> objects; + typename Transformation::DataType absoluteTransformation; + Object* p = static_cast*>(this); + for(;;) { + objects.push(p); + + p = p->parent(); + + /* On root object, base transformation is identity */ + if(!p) break; + + /* Parent object is clean, base transformation is its absolute + transformation */ + if(!p->isDirty()) { + absoluteTransformation = p->absoluteTransformation(); + break; + } + } + + /* Clean features on every collected object, going down from root object */ + while(!objects.empty()) { + Object* o = objects.top(); + objects.pop(); + + /* Compose transformations */ + absoluteTransformation = Transformation::compose(absoluteTransformation, o->transformation()); + + /* "Lazy storage" for transformation matrix and inverted transformation matrix */ + typedef typename AbstractFeature::CachedTransformation CachedTransformation; + typename AbstractFeature::CachedTransformations cached; + typename DimensionTraits::MatrixType + matrix, invertedMatrix; + + /* Clean all features */ + for(AbstractFeature* i = o->firstFeature(); i; i = i->nextFeature()) { + /* Cached absolute transformation, compute it if it wasn't + computed already */ + if(i->cachedTransformations() & CachedTransformation::Absolute) { + if(!(cached & CachedTransformation::Absolute)) { + cached |= CachedTransformation::Absolute; + matrix = Transformation::toMatrix(absoluteTransformation); + } + + i->clean(matrix); + } + + /* Cached inverse absolute transformation, compute it if it wasn't + computed already */ + if(i->cachedTransformations() & CachedTransformation::InvertedAbsolute) { + if(!(cached & CachedTransformation::InvertedAbsolute)) { + cached |= CachedTransformation::InvertedAbsolute; + invertedMatrix = Transformation::toMatrix(Transformation::inverted(absoluteTransformation)); + } + + i->cleanInverted(invertedMatrix); + } + } + + /* Mark object as clean */ + o->flags &= ~Flag::Dirty; + } +} + +}} + +#endif diff --git a/src/SceneGraph/Scene.h b/src/SceneGraph/Scene.h index 9966bca21..eecc231f0 100644 --- a/src/SceneGraph/Scene.h +++ b/src/SceneGraph/Scene.h @@ -16,7 +16,7 @@ */ /** @file - * @brief Class Magnum::SceneGraph::Scene, typedef Magnum::SceneGraph::Scene2D, Magnum::SceneGraph::Scene3D + * @brief Class Magnum::SceneGraph::Scene */ #include "Object.h" @@ -26,33 +26,14 @@ namespace Magnum { namespace SceneGraph { /** @brief %Scene -@see Scene2D, Scene3D +Basically Object which cannot have parent or non-default transformation. +See @ref scenegraph for introduction. */ -template class SCENEGRAPH_EXPORT Scene: public AbstractObject::ObjectType { +template class Scene: public Object { public: - /** @copydoc AbstractObject::isScene() */ inline bool isScene() const { return true; } - - /** @todo Some deleted functions belong only to Scene2D, some only to Scene3D - what to do? */ - #ifndef DOXYGEN_GENERATING_OUTPUT - void setParent(typename AbstractObject::ObjectType* parent) = delete; - void setTransformation(const typename DimensionTraits::MatrixType& transformation) = delete; - void multiplyTransformation(const typename DimensionTraits::MatrixType& transformation, typename AbstractObject::Transformation type = (DimensionTraits::Transformation::Global)) = delete; - void translate(const typename DimensionTraits::VectorType& vec, typename AbstractObject::Transformation type = AbstractObject::Transformation::Global) = delete; - void scale(const typename DimensionTraits::VectorType& vec, typename AbstractObject::Transformation type = AbstractObject::Transformation::Global) = delete; - void rotate(GLfloat angle, const typename DimensionTraits::VectorType& vec, typename AbstractObject::Transformation type = AbstractObject::Transformation::Global) = delete; - #endif - - private: - inline void draw(const typename DimensionTraits::MatrixType&, typename AbstractObject::CameraType*) {} }; -/** @brief Two-dimensional scene */ -typedef Scene<2> Scene2D; - -/** @brief Three-dimensional scene */ -typedef Scene<3> Scene3D; - }} #endif diff --git a/src/SceneGraph/Test/CMakeLists.txt b/src/SceneGraph/Test/CMakeLists.txt index e0db0910d..189382bdf 100644 --- a/src/SceneGraph/Test/CMakeLists.txt +++ b/src/SceneGraph/Test/CMakeLists.txt @@ -1,3 +1,2 @@ corrade_add_test2(SceneGraphObjectTest ObjectTest.cpp LIBRARIES MagnumSceneGraphTestLib) -corrade_add_test2(SceneGraphCameraTest CameraTest.cpp LIBRARIES MagnumSceneGraph) corrade_add_test2(SceneGraphSceneTest SceneTest.cpp LIBRARIES MagnumSceneGraph) diff --git a/src/SceneGraph/Test/ObjectTest.cpp b/src/SceneGraph/Test/ObjectTest.cpp index c838704f8..995d2aeff 100644 --- a/src/SceneGraph/Test/ObjectTest.cpp +++ b/src/SceneGraph/Test/ObjectTest.cpp @@ -18,7 +18,7 @@ #include #include "Math/Constants.h" -#include "SceneGraph/Camera.h" +#include "SceneGraph/MatrixTransformation3D.h" #include "SceneGraph/Scene.h" using namespace std; @@ -27,13 +27,14 @@ CORRADE_TEST_MAIN(Magnum::SceneGraph::Test::ObjectTest) namespace Magnum { namespace SceneGraph { namespace Test { +typedef SceneGraph::Object> Object3D; +typedef SceneGraph::Scene> Scene3D; + ObjectTest::ObjectTest() { addTests(&ObjectTest::parenting, - &ObjectTest::transformation, - &ObjectTest::absoluteTransformationWrongCamera, - &ObjectTest::absoluteTransformation, &ObjectTest::scene, - &ObjectTest::dirty); + &ObjectTest::absoluteTransformation, + &ObjectTest::caching); } void ObjectTest::parenting() { @@ -66,61 +67,19 @@ void ObjectTest::parenting() { CORRADE_VERIFY(!childOne->hasChildren()); } -void ObjectTest::transformation() { - Object3D o; - Object3D o2; - - o.setTransformation(Matrix4::translation(Vector3::xAxis(1.0f))); - o2.translate(Vector3::xAxis(1.0f)); - o.multiplyTransformation(Matrix4::rotation(deg(35.0f), Vector3::zAxis())); - o2.rotate(deg(35.0f), Vector3::zAxis()); - - CORRADE_COMPARE(o.transformation(), Matrix4::rotation(deg(35.0f), Vector3::zAxis())* - Matrix4::translation(Vector3::xAxis(1.0f))); - CORRADE_COMPARE(o2.transformation(), o.transformation()); - - o.multiplyTransformation(Matrix4::scaling(Vector3(2.0f)), Object3D::Transformation::Local); - o2.scale(Vector3(2.0f), Object3D::Transformation::Local); - CORRADE_COMPARE(o.transformation(), Matrix4::rotation(deg(35.0f), Vector3::zAxis())* - Matrix4::translation(Vector3::xAxis(1.0f))* - Matrix4::scaling(Vector3(2.0f))); - CORRADE_COMPARE(o2.transformation(), o.transformation()); -} - -void ObjectTest::absoluteTransformationWrongCamera() { - stringstream ss; - Error::setOutput(&ss); - - Scene3D s; - Object3D o(&s); - o.translate(Vector3::yAxis()); - Camera3D c; - CORRADE_COMPARE(o.absoluteTransformation(&c), Matrix4::translation(Vector3::yAxis())); - CORRADE_COMPARE(ss.str(), "Object::absoluteTransformation(): the camera is not part of the same scene as object!\n"); - - ss.str(""); - Object3D o2; - o2.translate(Vector3::xAxis()); - CORRADE_COMPARE(o2.absoluteTransformation(&c), Matrix4::translation(Vector3::xAxis())); - CORRADE_COMPARE(ss.str(), "Object::absoluteTransformation(): the object is not part of camera scene!\n"); -} - void ObjectTest::absoluteTransformation() { Scene3D s; - Camera3D c(&s); - c.translate(Vector3::zAxis(2.0f)); - CORRADE_COMPARE(s.absoluteTransformation(), Matrix4()); - CORRADE_COMPARE(c.absoluteTransformation(&c), Matrix4()); + /* Proper transformation composition */ Object3D o(&s); - o.scale(Vector3(2.0f)); + o.translate(Vector3::xAxis(2.0f)); Object3D o2(&o); - o.rotate(deg(90.0f), Vector3::yAxis()); + o2.rotate(deg(90.0f), Vector3::yAxis()); CORRADE_COMPARE(o2.absoluteTransformation(), - Matrix4::scaling(Vector3(2.0f))*Matrix4::rotation(deg(90.0f), Vector3::yAxis())); - CORRADE_COMPARE(o2.absoluteTransformation(&c), - (Matrix4::translation(Vector3::zAxis(2.0f)).inverted())*Matrix4::scaling(Vector3(2.0f))*Matrix4::rotation(deg(90.0f), Vector3::yAxis())); + Matrix4::translation(Vector3::xAxis(2.0f))*Matrix4::rotation(deg(90.0f), Vector3::yAxis())); + CORRADE_COMPARE(o2.absoluteTransformation(), o2.absoluteTransformationMatrix()); + /* Transformation of root object */ Object3D o3; o3.translate({1.0f, 2.0f, 3.0f}); CORRADE_COMPARE(o3.absoluteTransformation(), Matrix4::translation({1.0f, 2.0f, 3.0f})); @@ -128,6 +87,7 @@ void ObjectTest::absoluteTransformation() { void ObjectTest::scene() { Scene3D scene; + CORRADE_VERIFY(scene.scene() == &scene); Object3D* childOne = new Object3D(&scene); Object3D* childTwo = new Object3D(childOne); @@ -139,23 +99,81 @@ void ObjectTest::scene() { CORRADE_VERIFY(childOfOrphan->scene() == nullptr); } -void ObjectTest::dirty() { +void ObjectTest::caching() { Scene3D scene; - CleaningObject* childOne = new CleaningObject(&scene); + class CachingFeature: public AbstractFeature<3, GLfloat> { + public: + CachingFeature(AbstractObject<3, GLfloat>* object): AbstractFeature<3, GLfloat>(object) { + setCachedTransformations(CachedTransformation::Absolute); + } + + Matrix4 cleanedAbsoluteTransformation; + + void clean(const Matrix4& absoluteTransformation) override { + cleanedAbsoluteTransformation = absoluteTransformation; + } + }; + + class CachingInvertedFeature: public AbstractFeature<3, GLfloat> { + public: + CachingInvertedFeature(AbstractObject<3, GLfloat>* object): AbstractFeature<3, GLfloat>(object) { + setCachedTransformations(CachedTransformation::InvertedAbsolute); + } + + Matrix4 cleanedInvertedAbsoluteTransformation; + + void cleanInverted(const Matrix4& invertedAbsoluteTransformation) override { + cleanedInvertedAbsoluteTransformation = invertedAbsoluteTransformation; + } + }; + + class CachingObject: public Object3D, AbstractFeature<3, GLfloat> { + public: + inline CachingObject(Object3D* parent = nullptr): Object3D(parent), AbstractFeature<3, GLfloat>(this) { + setCachedTransformations(CachedTransformation::Absolute); + } + + Matrix4 cleanedAbsoluteTransformation; + + protected: + void clean(const Matrix4& absoluteTransformation) override { + cleanedAbsoluteTransformation = absoluteTransformation; + } + }; + + CachingObject* childOne = new CachingObject(&scene); childOne->scale(Vector3(2.0f)); - CleaningObject* childTwo = new CleaningObject(childOne); + + CachingObject* childTwo = new CachingObject(childOne); childTwo->translate(Vector3::xAxis(1.0f)); - CleaningObject* childThree = new CleaningObject(childTwo); + CachingFeature* childTwoFeature = new CachingFeature(childTwo); + CachingInvertedFeature* childTwoFeature2 = new CachingInvertedFeature(childTwo); + + CachingObject* childThree = new CachingObject(childTwo); childThree->rotate(deg(90.0f), Vector3::yAxis()); /* Object is dirty at the beginning */ CORRADE_VERIFY(scene.isDirty()); CORRADE_VERIFY(childOne->isDirty()); + CORRADE_VERIFY(childTwo->isDirty()); + CORRADE_VERIFY(childThree->isDirty()); /* Clean the object and all its dirty parents (but not children) */ - childOne->setClean(); - CORRADE_COMPARE(childOne->cleanedAbsoluteTransformation, childOne->absoluteTransformation()); + childTwo->setClean(); + CORRADE_VERIFY(!scene.isDirty()); + CORRADE_VERIFY(!childOne->isDirty()); + CORRADE_VERIFY(!childTwo->isDirty()); + CORRADE_VERIFY(childThree->isDirty()); + + /* Verify the right matrices were passed */ + CORRADE_COMPARE(childOne->cleanedAbsoluteTransformation, childOne->absoluteTransformationMatrix()); + CORRADE_COMPARE(childTwo->cleanedAbsoluteTransformation, childTwo->absoluteTransformationMatrix()); + CORRADE_COMPARE(childTwoFeature->cleanedAbsoluteTransformation, childTwo->absoluteTransformationMatrix()); + CORRADE_COMPARE(childTwoFeature2->cleanedInvertedAbsoluteTransformation, childTwo->absoluteTransformationMatrix().inverted()); + + /* Mark object and all its children as dirty (but not parents) */ + childTwo->setDirty(); CORRADE_VERIFY(!scene.isDirty()); CORRADE_VERIFY(!childOne->isDirty()); CORRADE_VERIFY(childTwo->isDirty()); @@ -169,24 +187,14 @@ void ObjectTest::dirty() { /* If any object in the hierarchy is already clean, it shouldn't clean it again */ childTwo->setClean(); CORRADE_COMPARE(childOne->cleanedAbsoluteTransformation, Matrix4(Matrix4::Zero)); - CORRADE_COMPARE(childTwo->cleanedAbsoluteTransformation, childTwo->absoluteTransformation()); - CORRADE_VERIFY(!childOne->isDirty()); - CORRADE_VERIFY(!childTwo->isDirty()); - CORRADE_VERIFY(childThree->isDirty()); - /* Mark object and all its children as dirty (but not parents) */ - childTwo->setDirty(); - CORRADE_VERIFY(!scene.isDirty()); - CORRADE_VERIFY(!childOne->isDirty()); - CORRADE_VERIFY(childTwo->isDirty()); - CORRADE_VERIFY(childThree->isDirty()); - - /* Reparent object => make it and its children dirty (but not parents) */ + /* Remove object from tree => make it and its children dirty */ childThree->setClean(); - CORRADE_COMPARE(childThree->cleanedAbsoluteTransformation, childThree->absoluteTransformation()); childTwo->setParent(nullptr); CORRADE_VERIFY(childTwo->isDirty()); CORRADE_VERIFY(!childOne->isDirty()); + + /* Add object to tree => make it and its children dirty, don't touch parents */ childTwo->setParent(&scene); CORRADE_VERIFY(!scene.isDirty()); CORRADE_VERIFY(childTwo->isDirty()); diff --git a/src/SceneGraph/Test/ObjectTest.h b/src/SceneGraph/Test/ObjectTest.h index 01149310f..b0294ca89 100644 --- a/src/SceneGraph/Test/ObjectTest.h +++ b/src/SceneGraph/Test/ObjectTest.h @@ -17,8 +17,6 @@ #include -#include "SceneGraph/Object.h" - namespace Magnum { namespace SceneGraph { namespace Test { class ObjectTest: public Corrade::TestSuite::Tester { @@ -26,24 +24,9 @@ class ObjectTest: public Corrade::TestSuite::Tester { ObjectTest(); void parenting(); - void transformation(); - void absoluteTransformationWrongCamera(); - void absoluteTransformation(); void scene(); - void dirty(); - - private: - class CleaningObject: public Object3D { - public: - CleaningObject(Object3D* parent = nullptr): Object3D(parent) {} - - inline void clean(const Matrix4& absoluteTransformation) { - Object3D::clean(absoluteTransformation); - - cleanedAbsoluteTransformation = absoluteTransformation; - } - Matrix4 cleanedAbsoluteTransformation; - }; + void absoluteTransformation(); + void caching(); }; }}} diff --git a/src/SceneGraph/Test/SceneTest.cpp b/src/SceneGraph/Test/SceneTest.cpp index 65bff5daa..301a82846 100644 --- a/src/SceneGraph/Test/SceneTest.cpp +++ b/src/SceneGraph/Test/SceneTest.cpp @@ -15,12 +15,17 @@ #include "SceneTest.h" +#include "Magnum.h" +#include "SceneGraph/MatrixTransformation3D.h" #include "SceneGraph/Scene.h" CORRADE_TEST_MAIN(Magnum::SceneGraph::Test::SceneTest) namespace Magnum { namespace SceneGraph { namespace Test { +typedef SceneGraph::Scene> Scene3D; +typedef SceneGraph::Object> Object3D; + SceneTest::SceneTest() { addTests(&SceneTest::transformation, &SceneTest::parent); @@ -36,7 +41,6 @@ void SceneTest::transformation() { void SceneTest::parent() { Scene3D scene; - CORRADE_VERIFY(scene.isScene()); /* Scene parent cannot be changed */ Object3D* scenePointer = &scene;