Browse Source

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.
pull/7/head
Vladimír Vondruš 14 years ago
parent
commit
06398dcf7c
  1. 11
      src/Physics/CMakeLists.txt
  2. 2
      src/Physics/Test/CMakeLists.txt
  3. 319
      src/SceneGraph/AbstractFeature.h
  4. 130
      src/SceneGraph/AbstractObject.h
  5. 169
      src/SceneGraph/AbstractTransformation.h
  6. 56
      src/SceneGraph/AbstractTranslationRotation2D.h
  7. 57
      src/SceneGraph/AbstractTranslationRotation3D.h
  8. 46
      src/SceneGraph/AbstractTranslationRotationScaling2D.h
  9. 46
      src/SceneGraph/AbstractTranslationRotationScaling3D.h
  10. 26
      src/SceneGraph/CMakeLists.txt
  11. 26
      src/SceneGraph/MatrixTransformation2D.cpp
  12. 136
      src/SceneGraph/MatrixTransformation2D.h
  13. 26
      src/SceneGraph/MatrixTransformation3D.cpp
  14. 125
      src/SceneGraph/MatrixTransformation3D.h
  15. 147
      src/SceneGraph/Object.cpp
  16. 419
      src/SceneGraph/Object.h
  17. 159
      src/SceneGraph/Object.hpp
  18. 27
      src/SceneGraph/Scene.h
  19. 1
      src/SceneGraph/Test/CMakeLists.txt
  20. 152
      src/SceneGraph/Test/ObjectTest.cpp
  21. 21
      src/SceneGraph/Test/ObjectTest.h
  22. 6
      src/SceneGraph/Test/SceneTest.cpp

11
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

2
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)

319
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š <mosra@centrum.cz>
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 <Containers/EnumSet.h>
#include <Containers/LinkedList.h>
#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<FeatureCachedTransformation, std::uint8_t> 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<class T> inline TransformingFeature(SceneGraph::Object<T>* object):
SceneGraph::AbstractFeature3D<>(object), transformation(object) {}
private:
SceneGraph::AbstractTranslationRotation3D<>* transformation;
};
@endcode
If we take for example @ref Object "Object<MatrixTransformation3D<>>", 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<std::uint8_t dimensions, class T = GLfloat> class AbstractFeature
#ifndef DOXYGEN_GENERATING_OUTPUT
: private Corrade::Containers::LinkedListItem<AbstractFeature<dimensions, T>, AbstractObject<dimensions, T>>
#endif
{
friend class Corrade::Containers::LinkedList<AbstractFeature<dimensions, T>>;
friend class Corrade::Containers::LinkedListItem<AbstractFeature<dimensions, T>, AbstractObject<dimensions, T>>;
template<class Transformation> friend class Object;
public:
/**
* @brief Constructor
* @param object %Object holding this feature
*/
inline AbstractFeature(AbstractObject<dimensions, T>* object) {
object->Corrade::Containers::LinkedList<AbstractFeature<dimensions, T>>::insert(this);
}
virtual ~AbstractFeature() = 0;
/** @brief %Object holding this feature */
inline AbstractObject<dimensions, T>* object() {
return Corrade::Containers::LinkedListItem<AbstractFeature<dimensions, T>, AbstractObject<dimensions, T>>::list();
}
/** @overload */
inline const AbstractObject<dimensions, T>* object() const {
return Corrade::Containers::LinkedListItem<AbstractFeature<dimensions, T>, AbstractObject<dimensions, T>>::list();
}
/** @brief Previous feature or `nullptr`, if this is first feature */
inline AbstractFeature<dimensions, T>* previousFeature() {
return Corrade::Containers::LinkedListItem<AbstractFeature<dimensions, T>, AbstractObject<dimensions, T>>::previous();
}
/** @overload */
inline const AbstractFeature<dimensions, T>* previousFeature() const {
return Corrade::Containers::LinkedListItem<AbstractFeature<dimensions, T>, AbstractObject<dimensions, T>>::previous();
}
/** @brief Next feature or `nullptr`, if this is last feature */
inline AbstractFeature<dimensions, T>* nextFeature() {
return Corrade::Containers::LinkedListItem<AbstractFeature<dimensions, T>, AbstractObject<dimensions, T>>::next();
}
/** @overload */
inline const AbstractFeature<dimensions, T>* nextFeature() const {
return Corrade::Containers::LinkedListItem<AbstractFeature<dimensions, T>, AbstractObject<dimensions, T>>::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<CachedTransformation, std::uint8_t> 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<dimensions, T>::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<dimensions, T>::MatrixType& invertedAbsoluteTransformation);
/*@}*/
private:
CachedTransformations _cachedTransformations;
};
template<std::uint8_t dimensions, class T> inline AbstractFeature<dimensions, T>::~AbstractFeature() {}
template<std::uint8_t dimensions, class T> inline void AbstractFeature<dimensions, T>::clean(const typename DimensionTraits<dimensions, T>::MatrixType&) {}
template<std::uint8_t dimensions, class T> inline void AbstractFeature<dimensions, T>::cleanInverted(const typename DimensionTraits<dimensions, T>::MatrixType&) {}
/**
@brief Base for two-dimensional features
Convenience alternative to <tt>%AbstractFeature<2, T></tt>. See AbstractFeature
for more information.
@note Not available on GCC < 4.7. Use <tt>%AbstractFeature<2, T></tt> instead.
@see AbstractFeature3D
@todoc Remove workaround when Doxygen supports alias template
*/
#ifndef DOXYGEN_GENERATING_OUTPUT
#ifndef MAGNUM_GCC46_COMPATIBILITY
template<class T = GLfloat> using AbstractFeature2D = AbstractFeature<2, T>;
#endif
#else
typedef AbstractFeature<2, T = GLfloat> AbstractFeature2D;
#endif
/**
@brief Base for three-dimensional features
Convenience alternative to <tt>%AbstractFeature<3, T></tt>. See AbstractFeature
for more information.
@note Not available on GCC < 4.7. Use <tt>%AbstractFeature<3, T></tt> instead.
@see AbstractFeature2D
@todoc Remove workaround when Doxygen supports alias template
*/
#ifndef DOXYGEN_GENERATING_OUTPUT
#ifndef MAGNUM_GCC46_COMPATIBILITY
template<class T = GLfloat> using AbstractFeature3D = AbstractFeature<3, T>;
#endif
#else
typedef AbstractFeature<2, T = GLfloat> AbstractFeature3D;
#endif
}}
#endif

130
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š <mosra@centrum.cz>
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 <Containers/LinkedList.h>
#include "DimensionTraits.h"
#include "Magnum.h"
#include "magnumCompatibility.h"
namespace Magnum { namespace SceneGraph {
template<std::uint8_t, class> 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<std::uint8_t dimensions, class T = GLfloat> class AbstractObject
#ifndef DOXYGEN_GENERATING_OUTPUT
: private Corrade::Containers::LinkedList<AbstractFeature<dimensions, T>>
#endif
{
friend class Corrade::Containers::LinkedList<AbstractFeature<dimensions, T>>;
friend class Corrade::Containers::LinkedListItem<AbstractFeature<dimensions, T>, AbstractObject<dimensions, T>>;
friend AbstractFeature<dimensions, T>::AbstractFeature(AbstractObject<dimensions, T>*);
public:
/** @brief Feature object type */
typedef AbstractFeature<dimensions, T> FeatureType;
inline virtual ~AbstractObject() {}
/** @brief Whether this object has features */
inline bool hasFeatures() const {
return !Corrade::Containers::LinkedList<AbstractFeature<dimensions, T>>::isEmpty();
}
/** @brief First object feature or `nullptr`, if this object has no features */
inline FeatureType* firstFeature() {
return Corrade::Containers::LinkedList<AbstractFeature<dimensions, T>>::first();
}
/** @overload */
inline const FeatureType* firstFeature() const {
return Corrade::Containers::LinkedList<AbstractFeature<dimensions, T>>::first();
}
/** @brief Last object feature or `nullptr`, if this object has no features */
inline FeatureType* lastFeature() {
return Corrade::Containers::LinkedList<AbstractFeature<dimensions, T>>::last();
}
/** @overload */
inline const FeatureType* lastFeature() const {
return Corrade::Containers::LinkedList<AbstractFeature<dimensions, T>>::last();
}
/** @{ @name Object transformation */
/**
* @brief Transformation matrix relative to root object
*
* @see Object::absoluteTransformation()
*/
virtual typename DimensionTraits<dimensions, T>::MatrixType absoluteTransformationMatrix() const = 0;
/*@}*/
};
/**
@brief Base for two-dimensional objects
Convenience alternative to <tt>%AbstractObject<2, T></tt>. See AbstractObject
for more information.
@note Not available on GCC < 4.7. Use <tt>%AbstractObject<2, T></tt> instead.
@see AbstractObject3D
@todoc Remove workaround when Doxygen supports alias template
*/
#ifndef DOXYGEN_GENERATING_OUTPUT
#ifndef MAGNUM_GCC46_COMPATIBILITY
template<class T = GLfloat> using AbstractObject2D = AbstractObject<2, T>;
#endif
#else
typedef AbstractObject<2, T = GLfloat> AbstractObject2D;
#endif
/**
@brief Base for three-dimensional objects
Convenience alternative to <tt>%AbstractObject<3, T></tt>. See AbstractObject
for more information.
@note Not available on GCC < 4.7. Use <tt>%AbstractObject<3, T></tt> instead.
@see AbstractObject2D
@todoc Remove workaround when Doxygen supports alias template
*/
#ifndef DOXYGEN_GENERATING_OUTPUT
#ifndef MAGNUM_GCC46_COMPATIBILITY
template<class T = GLfloat> using AbstractObject3D = AbstractObject<3, T>;
#endif
#else
typedef AbstractObject<3, T = GLfloat> AbstractObject3D;
#endif
}}
#endif

169
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š <mosra@centrum.cz>
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 <vector>
#include "Magnum.h"
#include "DimensionTraits.h"
namespace Magnum { namespace SceneGraph {
template<class> 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<std::uint8_t dimensions, class T = GLfloat> 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<dimensions, T>::MatrixType toMatrix(const DataType& transformation);
/**
* @brief Convert transformation from matrix
*
* Defined in subclasses.
*/
static DataType fromMatrix(const typename DimensionTraits<dimensions, T>::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<std::uint8_t dimensions, class T> inline AbstractTransformation<dimensions, T>::~AbstractTransformation() {}
/**
@brief Base for two-dimensional transformations
Convenience alternative to <tt>%AbstractTransformation<2, T></tt>. See
AbstractTransformation for more information.
@note Not available on GCC < 4.7. Use <tt>%AbstractTransformation<2, T></tt>
instead.
@see AbstractTransformation3D
@todoc Remove workaround when Doxygen supports alias template
*/
#ifndef DOXYGEN_GENERATING_OUTPUT
#ifndef MAGNUM_GCC46_COMPATIBILITY
template<class T = GLfloat> using AbstractTransformation2D = AbstractTransformation<2, T>;
#endif
#else
typedef AbstractTransformation<2, T = GLfloat> AbstractTransformation2D;
#endif
/**
@brief Base for three-dimensional transformations
Convenience alternative to <tt>%AbstractTransformation<3, T></tt>. See
AbstractTransformation for more information.
@note Not available on GCC < 4.7. Use <tt>%AbstractTransformation<3, T></tt>
instead.
@see AbstractTransformation2D
@todoc Remove workaround when Doxygen supports alias template
*/
#ifndef DOXYGEN_GENERATING_OUTPUT
#ifndef MAGNUM_GCC46_COMPATIBILITY
template<class T = GLfloat> using AbstractTransformation3D = AbstractTransformation<3, T>;
#endif
#else
typedef AbstractTransformation<3, T = GLfloat> AbstractTransformation3D;
#endif
}}
#endif

56
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š <mosra@centrum.cz>
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 T = GLfloat> 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<T>* translate(const Math::Vector2<T>& 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<T>* rotate(T angle, TransformationType type = TransformationType::Global) = 0;
};
}}
#endif

57
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š <mosra@centrum.cz>
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 T = GLfloat> 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<T>* translate(const Math::Vector3<T>& 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<T>* rotate(T angle, const Math::Vector3<T>& normalizedAxis, TransformationType type = TransformationType::Global) = 0;
};
}}
#endif

46
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š <mosra@centrum.cz>
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 T = GLfloat> class AbstractTranslationRotationScaling2D: public AbstractTranslationRotation2D<T> {
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<T>* scale(const Math::Vector2<T>& vector, TransformationType type = TransformationType::Global) = 0;
};
}}
#endif

46
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š <mosra@centrum.cz>
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 T = GLfloat> class AbstractTranslationRotationScaling3D: public AbstractTranslationRotation3D<T> {
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<T>* scale(const Math::Vector3<T>& vector, TransformationType type = TransformationType::Global) = 0;
};
}}
#endif

26
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
$<TARGET_OBJECTS:MagnumSceneGraphObjects>
# $<TARGET_OBJECTS:MagnumSceneGraphObjects>
${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
$<TARGET_OBJECTS:MagnumSceneGraphObjects>
# $<TARGET_OBJECTS:MagnumSceneGraphObjects>
${MagnumSceneGraph_GracefulAssert_SRCS})
set_target_properties(MagnumSceneGraphTestLib PROPERTIES COMPILE_FLAGS -DCORRADE_GRACEFUL_ASSERT)
target_link_libraries(MagnumSceneGraphTestLib Magnum)

26
src/SceneGraph/MatrixTransformation2D.cpp

@ -0,0 +1,26 @@
/*
Copyright © 2010, 2011, 2012 Vladimír Vondruš <mosra@centrum.cz>
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<MatrixTransformation2D<>>;
#endif
}}

136
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š <mosra@centrum.cz>
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 T = GLfloat> class MatrixTransformation2D: public AbstractTranslationRotationScaling2D<T> {
public:
/** @brief Transformation matrix type */
typedef typename DimensionTraits<2, T>::MatrixType DataType;
#ifndef DOXYGEN_GENERATING_OUTPUT
inline constexpr static Math::Matrix3<T> fromMatrix(const Math::Matrix3<T>& matrix) {
return matrix;
}
inline constexpr static Math::Matrix3<T> toMatrix(const Math::Matrix3<T>& transformation) {
return transformation;
}
inline static Math::Matrix3<T> compose(const Math::Matrix3<T>& parent, const Math::Matrix3<T>& child) {
return parent*child;
}
inline static Math::Matrix3<T> inverted(const Math::Matrix3<T>& transformation) {
return transformation.inverted();
}
inline Math::Matrix3<T> transformation() const {
return _transformation;
}
#endif
/**
* @brief Set transformation
* @return Pointer to self (for method chaining)
*/
MatrixTransformation2D<T>* setTransformation(const Math::Matrix3<T>& transformation) {
/* Setting transformation is forbidden for the scene */
/** @todo Assert for this? */
/** @todo Do this in some common code? */
if(!static_cast<Object<MatrixTransformation2D<T>>*>(this)->isScene()) {
_transformation = transformation;
static_cast<Object<MatrixTransformation2D<T>>*>(this)->setDirty();
}
return this;
}
/**
* @brief Multiply transformation
* @param transformation Transformation
* @param type Transformation type
* @return Pointer to self (for method chaining)
*/
inline MatrixTransformation2D<T>* multiplyTransformation(const Math::Matrix3<T>& 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<T>* translate(const Math::Vector2<T>& vector, TransformationType type = TransformationType::Global) override {
multiplyTransformation(Math::Matrix3<T>::translation(vector), type);
return this;
}
/**
* @copydoc AbstractTranslationRotationScaling2D::rotate()
* Same as calling multiplyTransformation() with Matrix3::rotation().
*/
inline MatrixTransformation2D<T>* rotate(T angle, TransformationType type = TransformationType::Global) override {
multiplyTransformation(Math::Matrix3<T>::rotation(angle), type);
return this;
}
/**
* @copydoc AbstractTranslationRotationScaling2D::scale()
* Same as calling multiplyTransformation() with Matrix3::scaling().
*/
inline MatrixTransformation2D<T>* scale(const Math::Vector2<T>& vector, TransformationType type = TransformationType::Global) override {
multiplyTransformation(Math::Matrix3<T>::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<T>* move(Object<MatrixTransformation2D<T>>* under) {
static_cast<Object<MatrixTransformation2D>*>(this)->Corrade::Containers::LinkedList<Object<MatrixTransformation2D<T>>>::move(this, under);
return this;
}
protected:
/* Allow construction only from Object */
inline MatrixTransformation2D() {}
private:
Math::Matrix3<T> _transformation;
};
}}
#endif

26
src/SceneGraph/MatrixTransformation3D.cpp

@ -0,0 +1,26 @@
/*
Copyright © 2010, 2011, 2012 Vladimír Vondruš <mosra@centrum.cz>
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<MatrixTransformation3D<>>;
#endif
}}

125
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š <mosra@centrum.cz>
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 T = GLfloat> class MatrixTransformation3D: public AbstractTranslationRotationScaling3D<T> {
public:
/** @brief Transformation matrix type */
typedef typename DimensionTraits<3, T>::MatrixType DataType;
#ifndef DOXYGEN_GENERATING_OUTPUT
inline constexpr static Math::Matrix4<T> fromMatrix(const Math::Matrix4<T>& matrix) {
return matrix;
}
inline constexpr static Math::Matrix4<T> toMatrix(const Math::Matrix4<T>& transformation) {
return transformation;
}
inline static Math::Matrix4<T> compose(const Math::Matrix4<T>& parent, const Math::Matrix4<T>& child) {
return parent*child;
}
inline static Math::Matrix4<T> inverted(const Math::Matrix4<T>& transformation) {
return transformation.inverted();
}
inline Math::Matrix4<T> transformation() const {
return _transformation;
}
#endif
/**
* @brief Set transformation
* @return Pointer to self (for method chaining)
*/
MatrixTransformation3D<T>* setTransformation(const Math::Matrix4<T>& transformation) {
/* Setting transformation is forbidden for the scene */
/** @todo Assert for this? */
/** @todo Do this in some common code? */
if(!static_cast<Object<MatrixTransformation3D<T>>*>(this)->isScene()) {
_transformation = transformation;
static_cast<Object<MatrixTransformation3D<T>>*>(this)->setDirty();
}
return this;
}
/**
* @brief Multiply transformation
* @param transformation Transformation
* @param type Transformation type
* @return Pointer to self (for method chaining)
*/
inline MatrixTransformation3D<T>* multiplyTransformation(const Math::Matrix4<T>& 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<T>* translate(const Math::Vector3<T>& vector, TransformationType type = TransformationType::Global) override {
multiplyTransformation(Math::Matrix4<T>::translation(vector), type);
return this;
}
/**
* @copydoc AbstractTranslationRotationScaling3D::rotate()
* Same as calling multiplyTransformation() with Matrix4::rotation().
*/
inline MatrixTransformation3D<T>* rotate(T angle, const Math::Vector3<T>& normalizedAxis, TransformationType type = TransformationType::Global) override {
multiplyTransformation(Math::Matrix4<T>::rotation(angle, normalizedAxis), type);
return this;
}
/**
* @copydoc AbstractTranslationRotationScaling3D::scale()
* Same as calling multiplyTransformation() with Matrix4::scaling().
*/
inline MatrixTransformation3D<T>* scale(const Math::Vector3<T>& vector, TransformationType type = TransformationType::Global) override {
multiplyTransformation(Math::Matrix4<T>::scaling(vector), type);
return this;
}
protected:
/* Allow construction only from Object */
inline MatrixTransformation3D() {}
private:
Math::Matrix4<T> _transformation;
};
}}
#endif

147
src/SceneGraph/Object.cpp

@ -1,147 +0,0 @@
/*
Copyright © 2010, 2011, 2012 Vladimír Vondruš <mosra@centrum.cz>
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 <stack>
#include "Scene.h"
#include "Camera.h"
using namespace std;
using namespace Magnum::Math;
namespace Magnum { namespace SceneGraph {
template<std::uint8_t dimensions> typename AbstractObject<dimensions>::ObjectType* AbstractObject<dimensions>::setParent(ObjectType* parent) {
/* Skip if nothing to do or this is scene */
if(this->parent() == parent || isScene()) return static_cast<ObjectType*>(this);
/* Only Fry can be his own grandfather */
ObjectType* p = parent;
while(p) {
/** @todo Assert for this */
if(p == this) return static_cast<ObjectType*>(this);
p = p->parent();
}
/* Remove the object from old parent children list */
if(this->parent())
this->parent()->cut(static_cast<ObjectType*>(this));
/* Add the object to list of new parent */
if(parent)
parent->insert(static_cast<ObjectType*>(this));
setDirty();
return static_cast<ObjectType*>(this);
}
template<std::uint8_t dimensions> typename DimensionTraits<dimensions, GLfloat>::MatrixType AbstractObject<dimensions>::absoluteTransformation(CameraType* camera) {
/* Shortcut for absolute transformation of camera relative to itself */
if(camera == this) return typename DimensionTraits<dimensions, GLfloat>::MatrixType();
typename DimensionTraits<dimensions, GLfloat>::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<std::uint8_t dimensions> typename AbstractObject<dimensions>::SceneType* AbstractObject<dimensions>::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<SceneType*>(p);
p = p->parent();
}
return nullptr;
}
template<std::uint8_t dimensions> typename AbstractObject<dimensions>::ObjectType* AbstractObject<dimensions>::setTransformation(const typename DimensionTraits<dimensions, GLfloat>::MatrixType& transformation) {
/* Setting transformation is forbidden for the scene */
/** @todo Assert for this? */
if(isScene()) return static_cast<ObjectType*>(this);
_transformation = transformation;
setDirty();
return static_cast<ObjectType*>(this);
}
template<std::uint8_t dimensions> void AbstractObject<dimensions>::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<std::uint8_t dimensions> void AbstractObject<dimensions>::setClean() {
/* The object (and all its parents) are already clean, nothing to do */
if(!dirty) return;
/* Collect all parents */
stack<ObjectType*> objects;
ObjectType* p = static_cast<ObjectType*>(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<dimensions, GLfloat>::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>;
}}

419
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 <Containers/LinkedList.h>
#include <Containers/EnumSet.h>
#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<std::uint8_t dimensions> class Scene;
typedef Scene<2> Scene2D;
typedef Scene<3> Scene3D;
template<class Transformation> class Scene;
#ifndef DOXYGEN_GENERATING_OUTPUT
namespace Implementation {
template<std::uint8_t dimensions> 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<ObjectFlag, std::uint8_t> 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<std::uint8_t dimensions> class SCENEGRAPH_EXPORT AbstractObject: public Corrade::Containers::LinkedList<typename Implementation::ObjectDimensionTraits<dimensions>::ObjectType>, public Corrade::Containers::LinkedListItem<typename Implementation::ObjectDimensionTraits<dimensions>::ObjectType, typename Implementation::ObjectDimensionTraits<dimensions>::ObjectType> {
#ifndef DOXYGEN_GENERATING_OUTPUT
AbstractObject(const AbstractObject<dimensions>& other) = delete;
AbstractObject(AbstractObject<dimensions>&& other) = delete;
AbstractObject<dimensions>& operator=(const AbstractObject<dimensions>& other) = delete;
AbstractObject<dimensions>& operator=(AbstractObject<dimensions>&& 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<Dimensions>::ObjectType ObjectType;
- @ref MatrixTransformation2D "Object<MatrixTransformation2D<>>"
- @ref MatrixTransformation3D "Object<MatrixTransformation3D<>>"
/** @brief %Camera type for given dimension count */
typedef typename Implementation::ObjectDimensionTraits<Dimensions>::CameraType CameraType;
@see Scene, AbstractFeature, AbstractTransformation
*/
template<class Transformation> class Object: public AbstractObject<Transformation::Dimensions, typename Transformation::Type>, public Transformation
#ifndef DOXYGEN_GENERATING_OUTPUT
, private Corrade::Containers::LinkedList<Object<Transformation>>, private Corrade::Containers::LinkedListItem<Object<Transformation>, Object<Transformation>>
#endif
{
friend class Corrade::Containers::LinkedList<Object<Transformation>>;
friend class Corrade::Containers::LinkedListItem<Object<Transformation>, Object<Transformation>>;
/** @brief %Scene type for given dimension count */
typedef typename Implementation::ObjectDimensionTraits<Dimensions>::SceneType SceneType;
#ifndef DOXYGEN_GENERATING_OUTPUT
Object(const Object<Transformation>& other) = delete;
Object(Object<Transformation>&& other) = delete;
Object<Transformation>& operator=(const Object<Transformation>& other) = delete;
Object<Transformation>& operator=(Object<Transformation>&& 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<Transformation>* parent = nullptr): flags(Flag::Dirty) {
setParent(parent);
}
@ -108,9 +90,13 @@ template<std::uint8_t dimensions> 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<std::uint8_t dimensions> class SCENEGRAPH_EXPORT AbstractObject: public
* @brief %Scene
* @return %Scene or `nullptr`, if the object is not part of any scene.
*/
SceneType* scene();
Scene<Transformation>* scene();
/** @overload */
const Scene<Transformation>* scene() const;
/** @brief Parent object or `nullptr`, if this is root object */
inline ObjectType* parent() { return Corrade::Containers::LinkedListItem<ObjectType, ObjectType>::list(); }
inline Object<Transformation>* parent() {
return Corrade::Containers::LinkedListItem<Object<Transformation>, Object<Transformation>>::list();
}
/** @overload */
inline const Object<Transformation>* parent() const {
return Corrade::Containers::LinkedListItem<Object<Transformation>, Object<Transformation>>::list();
}
/** @brief Previous sibling object or `nullptr`, if this is first object */
inline ObjectType* previousSibling() { return Corrade::Containers::LinkedListItem<ObjectType, ObjectType>::previous(); }
inline Object<Transformation>* previousSibling() {
return Corrade::Containers::LinkedListItem<Object<Transformation>, Object<Transformation>>::previous();
}
/** @overload */
inline const Object<Transformation>* previousSibling() const {
return Corrade::Containers::LinkedListItem<Object<Transformation>, Object<Transformation>>::previous();
}
/** @brief Next sibling object or `nullptr`, if this is last object */
inline ObjectType* nextSibling() { return Corrade::Containers::LinkedListItem<ObjectType, ObjectType>::next(); }
inline Object<Transformation>* nextSibling() {
return Corrade::Containers::LinkedListItem<Object<Transformation>, Object<Transformation>>::next();
}
/** @overload */
inline const Object<Transformation>* nextSibling() const {
return Corrade::Containers::LinkedListItem<Object<Transformation>, Object<Transformation>>::next();
}
/** @brief Whether this object has children */
inline bool hasChildren() const { return !Corrade::Containers::LinkedList<ObjectType>::isEmpty(); }
inline bool hasChildren() const {
return !Corrade::Containers::LinkedList<Object<Transformation>>::isEmpty();
}
/** @brief First child object or `nullptr`, if this object has no children */
inline ObjectType* firstChild() { return Corrade::Containers::LinkedList<ObjectType>::first(); }
inline Object<Transformation>* firstChild() {
return Corrade::Containers::LinkedList<Object<Transformation>>::first();
}
/** @overload */
inline const Object<Transformation>* firstChild() const {
return Corrade::Containers::LinkedList<Object<Transformation>>::first();
}
/** @brief Last child object or `nullptr`, if this object has no children */
inline ObjectType* lastChild() { return Corrade::Containers::LinkedList<ObjectType>::last(); }
inline Object<Transformation>* lastChild() {
return Corrade::Containers::LinkedList<Object<Transformation>>::last();
}
/** @overload */
inline const Object<Transformation>* lastChild() const {
return Corrade::Containers::LinkedList<Object<Transformation>>::last();
}
/**
* @brief Set parent object
* @return Pointer to self (for method chaining)
*/
ObjectType* setParent(ObjectType* parent);
Object<Transformation>* setParent(Object<Transformation>* 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<dimensions, GLfloat>::MatrixType transformation() const {
return _transformation;
inline typename DimensionTraits<Transformation::Dimensions, typename Transformation::Type>::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<dimensions, GLfloat>::MatrixType absoluteTransformation(CameraType* camera = nullptr);
/**
* @brief Set transformation
* @return Pointer to self (for method chaining)
* @see absoluteTransformationMatrix()
*/
ObjectType* setTransformation(const typename DimensionTraits<dimensions, GLfloat>::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<dimensions, GLfloat>::MatrixType& transformation, Transformation type = Transformation::Global) {
setTransformation(type == Transformation::Global ?
transformation*_transformation : _transformation*transformation);
return static_cast<ObjectType*>(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<dimensions, GLfloat>::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<dimensions, GLfloat>::MatrixType& absoluteTransformation);
void setClean();
/*@}*/
private:
/* Hide base class members, as they are aliased to more meaningful names */
using Corrade::Containers::LinkedList<ObjectType>::first;
using Corrade::Containers::LinkedList<ObjectType>::last;
using Corrade::Containers::LinkedList<ObjectType>::isEmpty;
using Corrade::Containers::LinkedList<ObjectType>::insert;
using Corrade::Containers::LinkedList<ObjectType>::cut;
using Corrade::Containers::LinkedList<ObjectType>::move;
using Corrade::Containers::LinkedList<ObjectType>::erase;
using Corrade::Containers::LinkedList<ObjectType>::clear;
using Corrade::Containers::LinkedListItem<ObjectType, ObjectType>::list;
using Corrade::Containers::LinkedListItem<ObjectType, ObjectType>::previous;
using Corrade::Containers::LinkedListItem<ObjectType, ObjectType>::next;
typename DimensionTraits<dimensions, GLfloat>::MatrixType _transformation;
bool dirty;
};
template<std::uint8_t dimensions> inline AbstractObject<dimensions>::~AbstractObject() {}
/* Implementations for inline functions with unused parameters */
template<std::uint8_t dimensions> inline void AbstractObject<dimensions>::draw(const typename DimensionTraits<dimensions, GLfloat>::MatrixType&, CameraType*) {}
template<std::uint8_t dimensions> inline void AbstractObject<dimensions>::clean(const typename DimensionTraits<dimensions, GLfloat>::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<Object2D>::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;
};
}}

159
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š <mosra@centrum.cz>
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 <stack>
#include "Scene.h"
namespace Magnum { namespace SceneGraph {
template<class Transformation> Scene<Transformation>* Object<Transformation>::scene() {
Object<Transformation>* p(this);
while(p && !p->isScene()) p = p->parent();
return static_cast<Scene<Transformation>*>(p);
}
template<class Transformation> const Scene<Transformation>* Object<Transformation>::scene() const {
const Object<Transformation>* p(this);
while(p && !p->isScene()) p = p->parent();
return static_cast<const Scene<Transformation>*>(p);
}
template<class Transformation> Object<Transformation>* Object<Transformation>::setParent(Object<Transformation>* 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<Transformation>* 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<Object<Transformation>>::cut(this);
/* Add the object to list of new parent */
if(parent) parent->Corrade::Containers::LinkedList<Object<Transformation>>::insert(this);
setDirty();
return this;
}
template<class Transformation> typename Transformation::DataType Object<Transformation>::absoluteTransformation() const {
if(!parent()) return Transformation::transformation();
return Transformation::compose(parent()->absoluteTransformation(), Transformation::transformation());
}
template<class Transformation> void Object<Transformation>::setDirty() {
/* The transformation of this object (and all children) is already dirty,
nothing to do */
if(flags & Flag::Dirty) return;
Object<Transformation>* self = static_cast<Object<Transformation>*>(this);
/* Make all features dirty */
for(AbstractFeature<Transformation::Dimensions, typename Transformation::Type>* i = self->firstFeature(); i; i = i->nextFeature())
i->markDirty();
/* Make all children dirty */
for(Object<Transformation>* i = self->firstChild(); i; i = i->nextSibling())
i->setDirty();
/* Mark object as dirty */
flags |= Flag::Dirty;
}
template<class Transformation> void Object<Transformation>::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<Object<Transformation>*> objects;
typename Transformation::DataType absoluteTransformation;
Object<Transformation>* p = static_cast<Object<Transformation>*>(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<Transformation>* 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<Transformation::Dimensions, typename Transformation::Type>::CachedTransformation CachedTransformation;
typename AbstractFeature<Transformation::Dimensions, typename Transformation::Type>::CachedTransformations cached;
typename DimensionTraits<Transformation::Dimensions, typename Transformation::Type>::MatrixType
matrix, invertedMatrix;
/* Clean all features */
for(AbstractFeature<Transformation::Dimensions, typename Transformation::Type>* 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

27
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<std::uint8_t dimensions> class SCENEGRAPH_EXPORT Scene: public AbstractObject<dimensions>::ObjectType {
template<class Transformation> class Scene: public Object<Transformation> {
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<dimensions>::ObjectType* parent) = delete;
void setTransformation(const typename DimensionTraits<dimensions, GLfloat>::MatrixType& transformation) = delete;
void multiplyTransformation(const typename DimensionTraits<dimensions, GLfloat>::MatrixType& transformation, typename AbstractObject<dimensions>::Transformation type = (DimensionTraits<dimensions, GLfloat>::Transformation::Global)) = delete;
void translate(const typename DimensionTraits<dimensions, GLfloat>::VectorType& vec, typename AbstractObject<dimensions>::Transformation type = AbstractObject<dimensions>::Transformation::Global) = delete;
void scale(const typename DimensionTraits<dimensions, GLfloat>::VectorType& vec, typename AbstractObject<dimensions>::Transformation type = AbstractObject<dimensions>::Transformation::Global) = delete;
void rotate(GLfloat angle, const typename DimensionTraits<dimensions, GLfloat>::VectorType& vec, typename AbstractObject<dimensions>::Transformation type = AbstractObject<dimensions>::Transformation::Global) = delete;
#endif
private:
inline void draw(const typename DimensionTraits<dimensions, GLfloat>::MatrixType&, typename AbstractObject<dimensions>::CameraType*) {}
};
/** @brief Two-dimensional scene */
typedef Scene<2> Scene2D;
/** @brief Three-dimensional scene */
typedef Scene<3> Scene3D;
}}
#endif

1
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)

152
src/SceneGraph/Test/ObjectTest.cpp

@ -18,7 +18,7 @@
#include <sstream>
#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<SceneGraph::MatrixTransformation3D<GLfloat>> Object3D;
typedef SceneGraph::Scene<SceneGraph::MatrixTransformation3D<GLfloat>> 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());

21
src/SceneGraph/Test/ObjectTest.h

@ -17,8 +17,6 @@
#include <TestSuite/Tester.h>
#include "SceneGraph/Object.h"
namespace Magnum { namespace SceneGraph { namespace Test {
class ObjectTest: public Corrade::TestSuite::Tester<ObjectTest> {
@ -26,24 +24,9 @@ class ObjectTest: public Corrade::TestSuite::Tester<ObjectTest> {
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();
};
}}}

6
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<SceneGraph::MatrixTransformation3D<GLfloat>> Scene3D;
typedef SceneGraph::Object<SceneGraph::MatrixTransformation3D<GLfloat>> 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;

Loading…
Cancel
Save