Browse Source

Merge branch 'master' into compatibility

Vladimír Vondruš 14 years ago
parent
commit
43467dc801
  1. 79
      doc/compilation-speedup.dox
  2. 2
      doc/features.dox
  3. 3
      doc/namespaces.dox
  4. 267
      doc/scenegraph.dox
  5. 11
      src/Physics/CMakeLists.txt
  6. 2
      src/Physics/Test/CMakeLists.txt
  7. 319
      src/SceneGraph/AbstractFeature.h
  8. 239
      src/SceneGraph/AbstractGroupedFeature.h
  9. 196
      src/SceneGraph/AbstractObject.h
  10. 169
      src/SceneGraph/AbstractTransformation.h
  11. 56
      src/SceneGraph/AbstractTranslationRotation2D.h
  12. 100
      src/SceneGraph/AbstractTranslationRotation3D.h
  13. 46
      src/SceneGraph/AbstractTranslationRotationScaling2D.h
  14. 46
      src/SceneGraph/AbstractTranslationRotationScaling3D.h
  15. 21
      src/SceneGraph/CMakeLists.txt
  16. 115
      src/SceneGraph/Camera.cpp
  17. 215
      src/SceneGraph/Camera.h
  18. 145
      src/SceneGraph/Camera.hpp
  19. 192
      src/SceneGraph/Drawable.h
  20. 60
      src/SceneGraph/Light.h
  21. 12
      src/SceneGraph/MatrixTransformation2D.cpp
  22. 136
      src/SceneGraph/MatrixTransformation2D.h
  23. 26
      src/SceneGraph/MatrixTransformation3D.cpp
  24. 167
      src/SceneGraph/MatrixTransformation3D.h
  25. 147
      src/SceneGraph/Object.cpp
  26. 428
      src/SceneGraph/Object.h
  27. 291
      src/SceneGraph/Object.hpp
  28. 27
      src/SceneGraph/Scene.h
  29. 89
      src/SceneGraph/Test/CameraTest.cpp
  30. 1
      src/SceneGraph/Test/CameraTest.h
  31. 231
      src/SceneGraph/Test/ObjectTest.cpp
  32. 22
      src/SceneGraph/Test/ObjectTest.h
  33. 6
      src/SceneGraph/Test/SceneTest.cpp

79
doc/compilation-speedup.dox

@ -0,0 +1,79 @@
namespace Magnum {
/** @page compilation-speedup Speeding up compilation
@brief Techniques for reducing compilation times.
@section compilation-forward-declarations Forward declarations instead of includes
Essential thing when speeding up compilation is reducing number of `#``include`
directives in both headers and source files. %Magnum is strictly applying this
policy in all header files, so all types which are not directly used in the
header have only forward declarations.
For example, when including Magnum.h, you get shortcut typedefs for
floating-point vectors and matrices like @ref Vector3 and @ref Matrix4, but to
actually use any of them, you have to include the respective header, e.g.
Math/Vector3.h.
@section compilation-speedup-templates Templates
Many things in %Magnum are templated to allow handling of various types and
sizes of data, for example whole Scene graph can operate either with `float`s
or with `double`s. However, having templated classes and function usually
means that the compiler compiles the whole templated code again in each
compilation unit (i.e. source file). In linking stage of the application or
library the duplicates are just thrown out, which is a waste of compilation
time. A few techniques are employed in %Magnum to avoid this.
@subsection compilation-speedup-hpp Template headers and implementation files
When templated code is too large, it is not stored in header file, but in
so-called *template implementation file*. Generally, all header files in
%Magnum have `*.h` extension and all source files have `*.cpp` extension.
Template implementation files have `*.hpp` extension (hinting that they are
something between `*.h` and `*.cpp` files).
Template implementation file can be included along the header itself and it
will just work, but it doesn't positively affect compilation time. If you are
using one template specialization on many places, template implementation
files give you the ability to explicitly instantiate the template in some
source file. Then you can include only the header everywhere else and leave
the rest on the linker.
Templated classes which have implementation files state in their documentation
all common specializations that are already compiled in the libraries. So,
unless the templated class is too generic (ResourceManager for example) or you
need something special, you don't have to mess with Object implementation
files at all. See Color3 or SceneGraph::Object for an example.
Sometimes you however need to use your own specialization and that's why
template implementation files are included in the library. For example we want
to use @ref SceneGraph::Object "Object" from SceneGraph with
@ref SceneGraph::MatrixTransformation3D "MatrixTransformation3D" with
`GLdouble` as underlying type, because our scene will span the whole universe.
We include the implementation file in dedicated source file and explicitly
instantiate the template:
@code
// Object.cpp
#include "SceneGraph/Object.hpp"
using namespace Magnum::SceneGraph;
template class Object<MatrixTransformation3D<GLdouble>>;
@endcode
All other files using the same object specialization now need to include only
SceneGraph/Object.h header and if we compile our `Object.cpp` together with
the rest, the Object specialization will be compiled only once.
@subsection compilation-speedup-extern-templates Extern templates
Keyword `extern template` is new thing in C++11, attempting to solve
compilation time problems. However, when used on whole classes, on some
compilers it causes conflicting symbol errors, so in %Magnum its used only for
specific functions.
This is completely transparent to end user, so no special care is needed.
Extern template is used for example for @ref debugoperators "debug operators"
for common types of matrices and vectors.
*/
}

2
doc/features.dox

@ -3,6 +3,8 @@ namespace Magnum {
@brief Fundamental principles and design goals
- @subpage matrix-vector - @copybrief matrix-vector
- @subpage scenegraph - @copybrief scenegraph
- @subpage collision-detection - @copybrief collision-detection
- @subpage compilation-speedup - @copybrief compilation-speedup
*/
}

3
doc/namespaces.dox

@ -72,7 +72,8 @@ Basic primitives for testing purposes.
@namespace Magnum::SceneGraph
@brief %Scene graph library
Setting up and rendering the scene.
Managing object hierarchy, transformations and interactions. See
@ref scenegraph for introduction.
*/
/** @dir Shaders

267
doc/scenegraph.dox

@ -0,0 +1,267 @@
namespace Magnum { namespace SceneGraph {
/** @page scenegraph Using scene graph
@brief Overview of scene management capabilities.
@tableofcontents
%Scene graph provides way to hiearchically manage your objects, their
transformation, physics interaction, animation and rendering. There are
naturally many possible combinations (2D vs. 3D, different transformation
representations, animated vs. static, object can have collision shape,
participate in physics events, have forward vs. deferred rendering...). To
make everything possible without combinatiorial explosion and allow the users
to provide their own features, scene graph in %Magnum is composed of three
main components:
- objects, providing parent/children hierarchy
- transformations, implementing particular transformation type
- features, providing rendering capabilities, collision detection, physics
etc.
@section scenegraph-transformation Transformations
Transformation handles object position, rotation etc. and its basic property
is dimension count (2D or 3D) and underlying floating-point type (by default
`float`s are used everywhere, but you can use `double`s too).
%Scene graph has implementation of transformations in both 2D and 3D, using
either matrices or combination of position and rotation. Each implementation
has its own advantages and disadvantages -- for example when using matrices
you can have nearly arbitrary transformations, but composing transformations
and computing their inverse is costly operation. On the other hand quaternions
won't allow you to scale or shear objects, but are more memory efficient than
matrices.
It's also possible to implement your own transformation class for specific
needs, see @ref AbstractTransformation-subclassing
"AbstractTransformation documentation" for more information.
@section scenegraph-hierarchy Scene hierarchy
%Scene hierarchy is skeleton part of scene graph. In the root there is Scene
and its children are Object instances. The hierarchy has some transformation
type, identical for all objects (because for example having part of the tree
in 2D and part in 3D just wouldn't make sense). Common usage is to typedef
%Scene and %Object with desired transformation type:
@code
typedef SceneGraph::Scene<SceneGraph::MatrixTransformation3D<>> Scene3D;
typedef SceneGraph::Object<SceneGraph::MatrixTransformation3D<>> Object3D;
@endcode
Then you can start building the hierarchy by *parenting* one object to another.
Parent object can be either passed in constructor or using Object::setParent().
%Scene is always root object, so it naturally cannot have parent object.
@code
Scene3D scene;
Object3D* first = new Object3D(&scene);
Object3D* second = new Object3D(&first);
@endcode
Object3D children can be accessed using Object::firstChild() and
Object::lastChild(), then you can traverse siblings (objects with the same
parent) with Object::previousSibling() and Object::nextSibling(). For example
all children of an object can be traversed the following way:
@code
Object3D* o;
for(Object3D* child = o->firstChild(); child; child = child->nextSibling()) {
// ...
}
@endcode
The hierarchy takes care of memory management - when an object is destroyed,
all its children are destroyed too. See detailed explanation of
@ref scenegraph-object-construction-order "construction and destruction order"
for information about possible issues.
The object is derived from the transformation you specified earlier in the
`typedef`, so you can directly transform the objects using methods of given
transformation implementation. %Scene, as a root object, cannot have any
transformation. For convenience you can use method chaining:
@code
Object3D* next = new Object3D;
next->setParent(another)
->translate(Vector3::yAxis(3.0f))
->rotateY(deg(35.0f));
@endcode
@section scenegraph-features Object features
The object itself handles only parent/child relationship and transformation.
To make the object renderable, animatable, add collision shape to it etc., you
have to add a *feature* to it.
Each feature takes pointer to holder object in constructor, so adding a
feature to an object might look like this:
@code
Object3D* o;
MyFeature* feature = new MyFeature(o);
@endcode
Features of an object can be accessed using Object::firstFeature() and
Object::lastFeature(), then you can traverse the features using
AbstractFeature::previousFeature() and AbstractFeature::nextFeature(),
similarly to traversing object children:
@code
Object3D* o;
for(Object3D::FeatureType feature = o->firstFeature(); feature; feature = feature->nextFeature()) {
// ...
}
@endcode
Some features are passive, some active. Passive features can be just added to
an object like above, without any additional work (for example collision shape).
Active features require the user to implement some virtual function (for
example to draw the object on screen or perform animation step). To make things
convenient, features can be added directly to object itself using multiple
inheritance, so you can conveniently add all the active features you want and
implement needed functions in your own Object subclass without having to
subclass each feature individually (and making the code overly verbose).
Simplified example:
@code
class Bomb: public Object3D, Drawable, Animatable {
public:
inline Bomb(Object3D* parent): Object3D(parent), Drawable(this), Animatable(this) {}
protected:
void draw() {
// drawing implementation for Drawable feature
}
void animationStep() {
// animation step for Animatable feature
}
};
@endcode
From the outside there is no difference between features added as member and
features added using multiple inheritance, they can be both accessed from
feature list.
Similarly to object hierarchy, when destroying object, all its features (both
member and inherited) are destroyed. See detailed explanation of
@ref scenegraph-feature-construction-order "construction and destruction order"
for information about possible issues.
@section scenegraph-caching Transformation caching
Some features need to operate with absolute transformations and their
inversions - for example camera needs its inverse transformation to render the
scene, collision detection needs to know about positions of surrounding
objects etc. To avoid computing the transformations from scratch every time,
the feature can cache them.
The cached data stay until the object is marked as dirty - that is by changing
transformation, changing parent or explicitly calling Object::setDirty(). If
the object is marked as dirty, all its children are marked as dirty too and
AbstractFeature::markDirty() is called on every feature. Calling
Object::setClean() cleans the dirty object and all its dirty parents.
The function goes through all object features and calls AbstractFeature::clean()
or AbstractFeature::cleanInverted() depending on which caching is enabled on
given feature. If the object is already clean, Object::setClean() does nothing.
Most probably you will need caching in Object itself -- which doesn't support
it on its own -- however you can take advantage of multiple inheritance and
implement it using AbstractFeature. In order to have caching, you must enable
it first, because by default the caching is disabled. You can enable it using
AbstractFeature::setCachedTransformations() and then implement corresponding
cleaning function(s):
@code
class CachingObject: public Object3D, Object3D::FeatureType {
public:
CachingObject(Object3D* parent): Object3D::FeatureType(this) {
setCachedTransformations(CachedTransformation::Absolute);
}
protected:
void clean(const Matrix4& absoluteTransformation) override {
absolutePosition = absoluteTransformation.translation();
}
private:
Vector3 absolutePosition;
};
@endcode
When you need to use the cached value, you can explicitly request the cleanup
by calling Object::setClean(). Camera, for example, calls it automatically
before it starts rendering, as it needs its own inverse transformation to
properly draw the objects.
@section scenegraph-construction-order Construction and destruction order
There aren't any limitations and usage trade-offs of what you can and can't do
when working with objects and features, but there are two issues which you
should be aware of:
@subsection scenegraph-object-construction-order Object hierarchy
When objects are created on the heap (the preferred way, using `new`), they
can be constructed in any order and they will be destroyed when their parent
is destroyed. When creating them on the stack, however, they will be destroyed
when they go out of scope. Normally, the natural order of creation is not a
problem:
@code
{
Scene3D scene;
Object3D object(&scene);
}
@endcode
The object is created last, so it will be destroyed first, removing itself
from `scene`'s children list, causing no problems when destroying `scene`
object later. However, if their order is swapped, it will cause problems:
@code
{
Object3D object;
Scene3D scene;
object.setParent(&scene);
} // crash!
@endcode
The scene will be destroyed first, deleting all its children, which is wrong,
because `object` is created on stack. If this doesn't already crash, the
`object` destructor is called (again), making things even worse.
@subsection scenegraph-feature-construction-order Member and inherited features
When destroying the object, all its features are destroyed. For features added
as member it's no issue, features added using multiple inheritance must be
inherited after the %Object class:
@code
class MyObject: public Object3D, MyFeature {
public:
inline MyObject(Object3D* parent): Object3D(parent), MyFeature(this) {}
};
@endcode
When constructing MyObject, Object3D constructor is called first and then
MyFeature constructor adds itself to Object3D's list of features. When
destroying MyObject, its destructor is called and then the destructors of
ancestor classes -- first MyFeature destructor, which will remove itself from
Object3D's list, then Object3D destructor.
However, if we would inherit MyFeature first, it will cause problems:
@code
class MyObject: MyFeature, public Object3D {
public:
inline MyObject(Object3D* parent): MyFeature(this), Object3D(parent) {} // crash!
};
@endcode
MyFeature tries to add itself to feature list in not-yet-constructed Object3D,
causing undefined behavior. Then, if this doesn't already crash, Object3D is
created, creating empty feature list, making the feature invisible.
If we would construct them in swapped order (if it is even possible), it
wouldn't help either:
@code
class MyObject: MyFeature, public Object3D {
public:
inline MyObject(Object3D* parent): Object3D(parent), MyFeature(this) {}
// crash on destruction!
};
@endcode
On destruction, Object3D destructor is called first, deleting MyFeature,
which is wrong, because MyFeature is in the same object. After that (if the
program didn't already crash) destructor of MyFeature is called (again).
*/
}}

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

239
src/SceneGraph/AbstractGroupedFeature.h

@ -0,0 +1,239 @@
#ifndef Magnum_SceneGraph_AbstractGroupedFeature_h
#define Magnum_SceneGraph_AbstractGroupedFeature_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::AbstractGroupedFeature, Magnum::SceneGraph::FeatureGroup, alias Magnum::SceneGraph::AbstractGroupedFeature2D, Magnum::SceneGraph::AbstractGroupedFeature3D, Magnum::SceneGraph::FeatureGroup2D, Magnum::SceneGraph::FeatureGroup3D
*/
#include <algorithm>
#include <vector>
#include "AbstractFeature.h"
namespace Magnum { namespace SceneGraph {
template<std::uint8_t, class, class> class FeatureGroup;
/**
@brief Base for grouped features
Used together with FeatureGroup.
@section AbstractGroupedFeature-usage Usage
Usage is via subclassing the feature using [CRTP](http://en.wikipedia.org/wiki/Curiously_recurring_template_pattern)
and typedef'ing FeatureGroup to accept only given type, e.g.:
@code
class Drawable: public SceneGraph::AbstractGroupedFeature3D<Drawable> {
// ...
};
typedef SceneGraph::FeatureGroup3D<Drawable> DrawableGroup;
@endcode
@see AbstractGroupedFeature2D, AbstractGroupedFeature3D, FeatureGroup,
FeatureGroup2D, FeatureGroup3D
*/
template<std::uint8_t dimensions, class Derived, class T = GLfloat> class AbstractGroupedFeature: public AbstractFeature<dimensions, T> {
friend class FeatureGroup<dimensions, Derived, T>;
public:
/**
* @brief Constructor
* @param object %Object this feature belongs to
* @param group Group this feature belongs to
*
* Adds the feature to the object and to group, if specified.
* @see FeatureGroup::add(), FeatureGroup::remove()
*/
inline AbstractGroupedFeature(AbstractObject<dimensions, T>* object, FeatureGroup<dimensions, Derived, T>* group = nullptr): AbstractFeature<dimensions, T>(object), _group(nullptr) {
if(group) group->add(static_cast<Derived*>(this));
}
/**
* @brief Destructor
*
* Removes the feature from object and from group, if it belongs to
* any.
*/
inline ~AbstractGroupedFeature() {
if(_group) _group->remove(static_cast<Derived*>(this));
}
/** @brief Group this feature belongs to */
inline FeatureGroup<dimensions, Derived, T>* group() {
return _group;
}
/** @overload */
inline const FeatureGroup<dimensions, Derived, T>* group() const {
return _group;
}
private:
FeatureGroup<dimensions, Derived, T>* _group;
};
/**
@brief Base for two-dimensional grouped features
Convenience alternative to <tt>%AbstractGroupedFeature<2, Derived, T></tt>. See
AbstractGroupedFeature for more information.
@see AbstractGroupedFeature3D
@note Not available on GCC < 4.7. Use <tt>%AbstractGroupedFeature<2, Derived, T></tt>
instead.
@todoc Remove workaround when Doxygen supports alias template
*/
#ifndef DOXYGEN_GENERATING_OUTPUT
#ifndef MAGNUM_GCC46_COMPATIBILITY
template<class Derived, class T = GLfloat> using AbstractGroupedFeature2D = AbstractGroupedFeature<2, Derived, T>;
#endif
#else
typedef AbstractGroupedFeature<2, Derived, T = GLfloat> AbstractGroupedFeature2D;
#endif
/**
@brief Base for three-dimensional grouped features
Convenience alternative to <tt>%AbstractGroupedFeature<3, Derived, T></tt>. See
AbstractGroupedFeature for more information.
@see AbstractGroupedFeature2D
@note Not available on GCC < 4.7. Use <tt>%AbstractGroupedFeature<3, Derived, T></tt>
instead.
@todoc Remove workaround when Doxygen supports alias template
*/
#ifndef DOXYGEN_GENERATING_OUTPUT
#ifndef MAGNUM_GCC46_COMPATIBILITY
template<class Derived, class T = GLfloat> using AbstractGroupedFeature3D = AbstractGroupedFeature<3, Derived, T>;
#endif
#else
typedef AbstractGroupedFeature<3, Derived, T = GLfloat> AbstractGroupedFeature3D;
#endif
/**
@brief Group of features
See AbstractGroupedFeature for more information.
@see FeatureGroup2D, FeatureGroup3D
*/
template<std::uint8_t dimensions, class Feature, class T = GLfloat> class FeatureGroup {
friend class AbstractGroupedFeature<dimensions, Feature, T>;
public:
/**
* @brief Destructor
*
* Deletes all features belogning to this group.
*/
inline virtual ~FeatureGroup() {
for(auto i: features) {
i->_group = nullptr;
delete i;
}
}
/** @brief Whether the group is empty */
inline bool isEmpty() const { return features.empty(); }
/** @brief Count of features in the group */
inline std::size_t size() const { return features.size(); }
/** @brief Feature at given index */
inline Feature* operator[](std::size_t index) {
return features[index];
}
/** @overload */
inline const Feature* operator[](std::size_t index) const {
return features[index];
}
/**
* @brief Add feature to the group
*
* If the features is part of another group, it is removed from it.
*/
void add(Feature* feature) {
/** @todo Assert the same scene for all items? -- can't easily
watch when feature object is removed from hierarchy */
/* Remove from previous group */
if(feature->_group)
feature->_group->remove(feature);
/* Crossreference the feature and group together */
features.push_back(feature);
feature->_group = this;
}
/**
* @brief Remove feature from the group
*
* The feature must be part of the group.
*/
void remove(Feature* feature) {
CORRADE_ASSERT(feature->_group == this,
"SceneGraph::AbstractFeatureGroup::remove(): feature is not part of this group", );
/* Remove the feature and reset group pointer */
features.erase(std::find(features.begin(), features.end(), feature));
feature->_group = nullptr;
}
private:
std::vector<Feature*> features;
};
/**
@brief Base for two-dimensional object features
Convenience alternative to <tt>%FeatureGroup<2, Feature, T></tt>. See
AbstractGroupedFeature for more information.
@note Not available on GCC < 4.7. Use <tt>%FeatureGroup<2, Feature, T></tt>
instead.
@see FeatureGroup3D
@todoc Remove workaround when Doxygen supports alias template
*/
#ifndef DOXYGEN_GENERATING_OUTPUT
#ifndef MAGNUM_GCC46_COMPATIBILITY
template<class Feature, class T = GLfloat> using FeatureGroup2D = FeatureGroup<2, Feature, T>;
#endif
#else
typedef FeatureGroup<2, Feature, T = GLfloat> FeatureGroup2D;
#endif
/**
@brief Base for three-dimensional object features
Convenience alternative to <tt>%FeatureGroup<3, Feature, T></tt>. See
AbstractGroupedFeature for more information.
@note Not available on GCC < 4.7. Use <tt>%FeatureGroup<3, Feature, T></tt>
instead.
@see FeatureGroup2D
@todoc Remove workaround when Doxygen supports alias template
*/
#ifndef DOXYGEN_GENERATING_OUTPUT
#ifndef MAGNUM_GCC46_COMPATIBILITY
template<class Feature, class T = GLfloat> using FeatureGroup3D = FeatureGroup<3, Feature, T>;
#endif
#else
typedef FeatureGroup<3, Feature, T = GLfloat> FeatureGroup3D;
#endif
}}
#endif

196
src/SceneGraph/AbstractObject.h

@ -0,0 +1,196 @@
#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();
}
/**
* @brief %Scene object
* @return Root object which is also scene or `nullptr`, if the object
* is not part of any scene.
*
* @todo Rename to scene() when I fully understand and fix covariant
* return issues.
*/
virtual AbstractObject<dimensions, T>* sceneObject() = 0;
/** @overload */
virtual const AbstractObject<dimensions, T>* sceneObject() const = 0;
/** @{ @name Object transformation */
/**
* @brief Transformation matrix relative to root object
*
* @see Object::absoluteTransformation()
*/
virtual typename DimensionTraits<dimensions, T>::MatrixType absoluteTransformationMatrix() const = 0;
/**
* @brief Transformation matrices of given set of objects relative to this object
*
* All transformations are premultiplied with @p initialTransformationMatrix,
* if specified.
* @warning This function cannot check if all objects are of the same
* Object type, use typesafe Object::transformations() when
* possible.
*/
virtual std::vector<typename DimensionTraits<dimensions, T>::MatrixType> transformationMatrices(const std::vector<AbstractObject<dimensions, T>*>& objects, const typename DimensionTraits<dimensions, T>::MatrixType& initialTransformationMatrix = typename DimensionTraits<dimensions, T>::MatrixType()) const = 0;
/*@}*/
/**
* @{ @name Transformation caching
*
* See @ref scenegraph-caching for more information.
*/
/**
* @brief Whether absolute transformation is dirty
*
* Returns `true` if transformation of the object or any parent has
* changed since last call to setClean(), `false` otherwise.
*
* All objects are dirty by default.
*
* @see @ref scenegraph-caching
*/
virtual bool isDirty() const = 0;
/**
* @brief Set object absolute transformation as dirty
*
* 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()
*/
virtual void setDirty() = 0;
/**
* @brief Clean object absolute transformation
*
* 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 setClean() = 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

100
src/SceneGraph/AbstractTranslationRotation3D.h

@ -0,0 +1,100 @@
#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"
#include "Math/Vector3.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;
/**
* @brief Rotate object around X axis
* @param angle Angle in radians, counterclockwise
* @param type Transformation type
* @return Pointer to self (for method chaining)
*
* In some implementations faster than calling
* `rotate(angle, Vector3::xAxis())`.
* @see deg(), rad()
*/
virtual AbstractTranslationRotation3D<T>* rotateX(T angle, TransformationType type = TransformationType::Global) {
return rotate(angle, Math::Vector3<T>::xAxis(), type);
}
/**
* @brief Rotate object around Y axis
* @param angle Angle in radians, counterclockwise
* @param type Transformation type
* @return Pointer to self (for method chaining)
*
* In some implementations faster than calling
* `rotate(angle, Vector3::yAxis())`.
* @see deg(), rad()
*/
virtual AbstractTranslationRotation3D<T>* rotateY(T angle, TransformationType type = TransformationType::Global) {
return rotate(angle, Math::Vector3<T>::yAxis(), type);
}
/**
* @brief Rotate object around Z axis
* @param angle Angle in radians, counterclockwise
* @param type Transformation type
* @return Pointer to self (for method chaining)
*
* In some implementations faster than calling
* `rotate(angle, Vector3::zAxis())`.
* @see deg(), rad()
*/
virtual AbstractTranslationRotation3D<T>* rotateZ(T angle, TransformationType type = TransformationType::Global) {
return rotate(angle, Math::Vector3<T>::zAxis(), type);
}
};
}}
#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

21
src/SceneGraph/CMakeLists.txt

@ -1,11 +1,21 @@
# Files shared between main library and unit test library
set(MagnumSceneGraph_SRCS
Camera.cpp
Light.cpp)
Camera.cpp)
set(MagnumSceneGraph_HEADERS
AbstractFeature.h
AbstractGroupedFeature.h
AbstractObject.h
AbstractTransformation.h
AbstractTranslationRotation2D.h
AbstractTranslationRotation3D.h
AbstractTranslationRotationScaling2D.h
AbstractTranslationRotationScaling3D.h
Camera.h
Light.h
Camera.hpp
Drawable.h
MatrixTransformation2D.h
MatrixTransformation3D.h
Object.h
Object.hpp
Scene.h
magnumSceneGraphVisibility.h)
@ -13,7 +23,8 @@ 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

115
src/SceneGraph/Camera.cpp

@ -14,121 +14,16 @@
*/
#include "Camera.h"
#include "Scene.h"
using namespace std;
#include "Camera.hpp"
namespace Magnum { namespace SceneGraph {
#ifndef DOXYGEN_GENERATING_OUTPUT
namespace Implementation {
template<class MatrixType> MatrixType aspectRatioFix(AspectRatioPolicy aspectRatioPolicy, const Vector2& projectionScale, const Math::Vector2<GLsizei>& viewport) {
/* Don't divide by zero / don't preserve anything */
if(projectionScale.x() == 0 || projectionScale.y() == 0 || viewport.x() == 0 || viewport.y() == 0 || aspectRatioPolicy == AspectRatioPolicy::NotPreserved)
return MatrixType();
Vector2 relativeAspectRatio = Vector2::from(viewport)*projectionScale;
/* Extend on larger side = scale larger side down
Clip on smaller side = scale smaller side up */
return Camera<MatrixType::Size-1>::aspectRatioScale(
(relativeAspectRatio.x() > relativeAspectRatio.y()) == (aspectRatioPolicy == AspectRatioPolicy::Extend) ?
Vector2(relativeAspectRatio.y()/relativeAspectRatio.x(), 1.0f) :
Vector2(1.0f, relativeAspectRatio.x()/relativeAspectRatio.y()));
}
/* Explicitly instantiate the templates */
template Matrix3 aspectRatioFix<Matrix3>(AspectRatioPolicy, const Vector2&, const Math::Vector2<GLsizei>&);
template Matrix4 aspectRatioFix<Matrix4>(AspectRatioPolicy, const Vector2&, const Math::Vector2<GLsizei>&);
}
template class SCENEGRAPH_EXPORT AbstractCamera<2, GLfloat>;
template class SCENEGRAPH_EXPORT AbstractCamera<3, GLfloat>;
template class SCENEGRAPH_EXPORT Camera2D<GLfloat>;
template class SCENEGRAPH_EXPORT Camera3D<GLfloat>;
#endif
template<std::uint8_t dimensions> AbstractCamera<dimensions>::AbstractCamera(typename AbstractObject<dimensions>::ObjectType* parent): AbstractObject<dimensions>::ObjectType(parent), _aspectRatioPolicy(AspectRatioPolicy::NotPreserved) {}
template<std::uint8_t dimensions> typename AbstractObject<dimensions>::CameraType* AbstractCamera<dimensions>::setAspectRatioPolicy(AspectRatioPolicy policy) {
_aspectRatioPolicy = policy;
fixAspectRatio();
return static_cast<typename AbstractObject<dimensions>::CameraType*>(this);
}
template<std::uint8_t dimensions> void AbstractCamera<dimensions>::setViewport(const Math::Vector2<GLsizei>& size) {
_viewport = size;
fixAspectRatio();
}
template<std::uint8_t dimensions> void AbstractCamera<dimensions>::clean(const typename DimensionTraits<dimensions, GLfloat>::MatrixType& absoluteTransformation) {
AbstractObject<dimensions>::ObjectType::clean(absoluteTransformation);
_cameraMatrix = absoluteTransformation.inverted();
}
template<std::uint8_t dimensions> void AbstractCamera<dimensions>::draw() {
typename AbstractObject<dimensions>::SceneType* s = this->scene();
CORRADE_ASSERT(s, "Camera: cannot draw without camera attached to scene", );
/* Recursively draw child objects */
drawChildren(s, cameraMatrix());
}
template<std::uint8_t dimensions> void AbstractCamera<dimensions>::drawChildren(typename AbstractObject<dimensions>::ObjectType* object, const typename DimensionTraits<dimensions, GLfloat>::MatrixType& transformationMatrix) {
for(typename AbstractObject<dimensions>::ObjectType* i = object->firstChild(); i; i = i->nextSibling()) {
/* Transformation matrix for the object */
typename DimensionTraits<dimensions, GLfloat>::MatrixType matrix = transformationMatrix*i->transformation();
/* Draw the object and its children */
i->draw(matrix, static_cast<typename AbstractObject<dimensions>::CameraType*>(this));
drawChildren(i, matrix);
}
}
Camera2D* Camera2D::setProjection(const Vector2& size) {
/* Scale the volume down so it fits in (-1, 1) in all directions */
rawProjectionMatrix = Matrix3::scaling(2.0f/size);
fixAspectRatio();
return this;
}
Camera3D* Camera3D::setOrthographic(const Vector2& size, GLfloat near, GLfloat far) {
_near = near;
_far = far;
Vector2 xyScale = 2.0f/size;
GLfloat zScale = 2.0f/(near-far);
rawProjectionMatrix = Matrix4(
xyScale.x(), 0.0f, 0.0f, 0.0f,
0.0f, xyScale.y(), 0.0f, 0.0f,
0.0f, 0.0f, zScale, 0.0f,
0.0f, 0.0f, near*zScale-1, 1.0f
);
fixAspectRatio();
return this;
}
Camera3D* Camera3D::setPerspective(GLfloat fov, GLfloat near, GLfloat far) {
_near = near;
_far = far;
GLfloat xyScale = 1.0f/tan(fov/2); /* == near/size */
GLfloat zScale = 1.0f/(near-far);
rawProjectionMatrix = Matrix4(
xyScale, 0.0f, 0.0f, 0.0f,
0.0f, xyScale, 0.0f, 0.0f,
0.0f, 0.0f, (far+near)*zScale, -1.0f,
0.0f, 0.0f, (2*far*near)*zScale, 0.0f
);
fixAspectRatio();
return this;
}
/* Explicitly instantiate the templates */
template class AbstractCamera<2>;
template class AbstractCamera<3>;
}}

215
src/SceneGraph/Camera.h

@ -16,10 +16,14 @@
*/
/** @file
* @brief Class Magnum::SceneGraph::AbstractCamera, Magnum::SceneGraph::Camera2D, Magnum::SceneGraph::Camera3D
* @brief Class Magnum::SceneGraph::AbstractCamera, Magnum::SceneGraph::Camera2D, Magnum::SceneGraph::Camera3D, alias Magnum::SceneGraph::AbstractCamera2D, Magnum::SceneGraph::AbstractCamera3D
*/
#include "Object.h"
#include "Math/Matrix3.h"
#include "Math/Matrix4.h"
#include "AbstractFeature.h"
#include "magnumSceneGraphVisibility.h"
#ifdef WIN32 /* I so HATE windows.h */
#undef near
@ -28,6 +32,14 @@
namespace Magnum { namespace SceneGraph {
template<std::uint8_t, class> class Drawable;
template<std::uint8_t, class, class> class FeatureGroup;
#ifndef MAGNUM_GCC46_COMPATIBILITY
template<std::uint8_t dimensions, class T = GLfloat> using DrawableGroup = FeatureGroup<dimensions, Drawable<dimensions, T>, T>;
#else
template<std::uint8_t, class> class DrawableGroup;
#endif
/** @todo Export implementation symbols only for tests */
#ifndef DOXYGEN_GENERATING_OUTPUT
@ -36,18 +48,28 @@ namespace Implementation {
NotPreserved, Extend, Clip
};
template<class MatrixType> MatrixType aspectRatioFix(AspectRatioPolicy aspectRatioPolicy, const Vector2& projectionScale, const Math::Vector2<GLsizei>& viewport);
/* These templates are instantiated in source file */
extern template SCENEGRAPH_EXPORT Matrix3 aspectRatioFix<Matrix3>(AspectRatioPolicy, const Vector2&, const Math::Vector2<GLsizei>&);
extern template SCENEGRAPH_EXPORT Matrix4 aspectRatioFix<Matrix4>(AspectRatioPolicy, const Vector2&, const Math::Vector2<GLsizei>&);
template<std::uint8_t dimensions, class T> typename DimensionTraits<dimensions, T>::MatrixType aspectRatioFix(AspectRatioPolicy aspectRatioPolicy, const Math::Vector2<T>& projectionScale, const Math::Vector2<GLsizei>& viewport);
}
#endif
/**
@brief %Camera object
*/
template<std::uint8_t dimensions> class SCENEGRAPH_EXPORT AbstractCamera: public AbstractObject<dimensions>::ObjectType {
@brief Base for cameras
See Drawable documentation for more information.
@section AbstractCamera-explicit-specializations Explicit template specializations
The following specialization are explicitly compiled into SceneGraph library.
For other specializations you have to use Camera.hpp implementation file to
avoid linker errors. See @ref compilation-speedup-hpp for more information.
- @ref AbstractCamera "AbstractCamera<2>"
- @ref AbstractCamera "AbstractCamera<3>"
@see Camera2D, Camera3D, Drawable, DrawableGroup, AbstractCamera2D,
AbstractCamera3D
*/
template<std::uint8_t dimensions, class T = GLfloat> class SCENEGRAPH_EXPORT AbstractCamera: public AbstractFeature<dimensions, T> {
public:
/**
* @brief Aspect ratio policy
@ -64,8 +86,13 @@ template<std::uint8_t dimensions> class SCENEGRAPH_EXPORT AbstractCamera: public
};
#endif
/** @copydoc AbstractObject::AbstractObject() */
AbstractCamera(typename AbstractObject<dimensions>::ObjectType* parent = nullptr);
/**
* @brief Constructor
* @param object Object holding the camera
*/
inline AbstractCamera(AbstractObject<dimensions, T>* object): AbstractFeature<dimensions, T>(object), _aspectRatioPolicy(AspectRatioPolicy::NotPreserved) {
AbstractFeature<dimensions, T>::setCachedTransformations(AbstractFeature<dimensions, T>::CachedTransformation::InvertedAbsolute);
}
virtual ~AbstractCamera() = 0;
@ -76,7 +103,7 @@ template<std::uint8_t dimensions> class SCENEGRAPH_EXPORT AbstractCamera: public
* @brief Set aspect ratio policy
* @return Pointer to self (for method chaining)
*/
typename AbstractObject<dimensions>::CameraType* setAspectRatioPolicy(AspectRatioPolicy policy);
AbstractCamera<dimensions, T>* setAspectRatioPolicy(AspectRatioPolicy policy);
/**
* @brief Camera matrix
@ -84,8 +111,8 @@ template<std::uint8_t dimensions> class SCENEGRAPH_EXPORT AbstractCamera: public
* Camera matrix describes world position relative to the camera and is
* applied as first.
*/
inline typename DimensionTraits<dimensions, GLfloat>::MatrixType cameraMatrix() {
this->setClean();
inline typename DimensionTraits<dimensions, T>::MatrixType cameraMatrix() {
AbstractFeature<dimensions, T>::object()->setClean();
return _cameraMatrix;
}
@ -96,7 +123,7 @@ template<std::uint8_t dimensions> class SCENEGRAPH_EXPORT AbstractCamera: public
* as last.
* @see projectionSize()
*/
inline typename DimensionTraits<dimensions, GLfloat>::MatrixType projectionMatrix() const { return _projectionMatrix; }
inline typename DimensionTraits<dimensions, T>::MatrixType projectionMatrix() const { return _projectionMatrix; }
/**
* @brief Size of (near) XY plane in current projection
@ -104,8 +131,8 @@ template<std::uint8_t dimensions> class SCENEGRAPH_EXPORT AbstractCamera: public
* Returns size of near XY plane computed from projection matrix.
* @see projectionMatrix()
*/
inline Vector2 projectionSize() const {
return {2.0f/_projectionMatrix[0].x(), 2.0f/_projectionMatrix[1].y()};
inline Math::Vector2<T> projectionSize() const {
return {T(2.0)/_projectionMatrix[0].x(), T(2.0)/_projectionMatrix[1].y()};
}
/** @brief Viewport size */
@ -121,79 +148,95 @@ template<std::uint8_t dimensions> class SCENEGRAPH_EXPORT AbstractCamera: public
virtual void setViewport(const Math::Vector2<GLsizei>& size);
/**
* @brief Draw the scene
* @brief Draw
*
* Draws the scene using drawChildren().
* Draws given group of drawables.
*/
virtual void draw();
using AbstractObject<dimensions>::ObjectType::draw; /* Don't hide Object's draw() */
virtual void draw(DrawableGroup<dimensions, T>& group);
protected:
/**
* Recalculates camera matrix.
*/
void clean(const typename DimensionTraits<dimensions, GLfloat>::MatrixType& absoluteTransformation);
/**
* @brief Draw object children
*
* Recursively draws all children of the object.
*/
void drawChildren(typename AbstractObject<dimensions>::ObjectType* object, const typename DimensionTraits<dimensions, GLfloat>::MatrixType& transformationMatrix);
/** Recalculates camera matrix */
inline void cleanInverted(const typename DimensionTraits<dimensions, T>::MatrixType& invertedAbsoluteTransformation) override {
_cameraMatrix = invertedAbsoluteTransformation;
}
#ifndef DOXYGEN_GENERATING_OUTPUT
inline void fixAspectRatio() {
_projectionMatrix = Implementation::aspectRatioFix<typename DimensionTraits<dimensions, GLfloat>::MatrixType>(_aspectRatioPolicy, {rawProjectionMatrix[0].x(), rawProjectionMatrix[1].y()}, _viewport)*rawProjectionMatrix;
_projectionMatrix = Implementation::aspectRatioFix<dimensions, T>(_aspectRatioPolicy, {rawProjectionMatrix[0].x(), rawProjectionMatrix[1].y()}, _viewport)*rawProjectionMatrix;
}
typename DimensionTraits<dimensions, GLfloat>::MatrixType rawProjectionMatrix;
typename DimensionTraits<dimensions, T>::MatrixType rawProjectionMatrix;
AspectRatioPolicy _aspectRatioPolicy;
#endif
private:
typename DimensionTraits<dimensions, GLfloat>::MatrixType _projectionMatrix;
typename DimensionTraits<dimensions, GLfloat>::MatrixType _cameraMatrix;
typename DimensionTraits<dimensions, T>::MatrixType _projectionMatrix;
typename DimensionTraits<dimensions, T>::MatrixType _cameraMatrix;
Math::Vector2<GLsizei> _viewport;
};
template<std::uint8_t dimensions> inline AbstractCamera<dimensions>::~AbstractCamera() {}
template<std::uint8_t dimensions, class T> inline AbstractCamera<dimensions, T>::~AbstractCamera() {}
/**
@brief Base for two-dimensional cameras
Convenience alternative to <tt>%AbstractCamera<2, T></tt>. See AbstractCamera
for more information.
@note Not available on GCC < 4.7. Use <tt>%AbstractCamera<2, T></tt> instead.
@see AbstractCamera3D
@todoc Remove workaround when Doxygen supports alias template
*/
#ifndef DOXYGEN_GENERATING_OUTPUT
namespace Implementation {
template<std::uint8_t dimensions> class Camera {};
#ifndef MAGNUM_GCC46_COMPATIBILITY
template<class T = GLfloat> using AbstractCamera2D = AbstractCamera<2, T>;
#endif
#else
typedef AbstractCamera<2, T = GLfloat> AbstractCamera2D;
#endif
template<> class Camera<2> {
public:
inline constexpr static Matrix3 aspectRatioScale(const Vector2& scale) {
return Matrix3::scaling({scale.x(), scale.y()});
}
};
template<> class Camera<3> {
public:
inline constexpr static Matrix4 aspectRatioScale(const Vector2& scale) {
return Matrix4::scaling({scale.x(), scale.y(), 1.0f});
}
};
}
/**
@brief Base for three-dimensional cameras
Convenience alternative to <tt>%AbstractCamera<3, T></tt>. See AbstractCamera
for more information.
@note Not available on GCC < 4.7. Use <tt>%AbstractCamera<3, T></tt> instead.
@see AbstractCamera2D
@todoc Remove workaround when Doxygen supports alias template
*/
#ifndef DOXYGEN_GENERATING_OUTPUT
#ifndef MAGNUM_GCC46_COMPATIBILITY
template<class T = GLfloat> using AbstractCamera3D = AbstractCamera<3, T>;
#endif
#else
typedef AbstractCamera<3, T = GLfloat> AbstractCamera3D;
#endif
/**
@brief %Camera for two-dimensional scenes
@brief Camera for two-dimensional scenes
See Drawable documentation for more information.
@section Object-explicit-specializations Explicit template specializations
@see Camera3D
The following specialization are explicitly compiled into SceneGraph library.
For other specializations you have to use Camera.hpp implementation file to
avoid linker errors. See @ref compilation-speedup-hpp for more information.
- @ref Camera2D "Camera2D<GLfloat>"
@see Camera3D, Drawable, DrawableGroup
*/
class SCENEGRAPH_EXPORT Camera2D: public AbstractCamera<2> {
template<class T = GLfloat> class SCENEGRAPH_EXPORT Camera2D: public AbstractCamera<2, T> {
public:
/**
* @brief Constructor
* @param parent Parent object
* @param object %Object holding this feature
*
* Sets orthographic projection to the default OpenGL cube (range @f$ [-1; 1] @f$ in all directions).
* @see setOrthographic()
*/
inline Camera2D(Object2D* parent = nullptr): AbstractCamera<2>(parent) {}
inline Camera2D(AbstractObject<2, T>* object): AbstractCamera<2, T>(object) {}
/**
* @brief Set projection
@ -203,24 +246,42 @@ class SCENEGRAPH_EXPORT Camera2D: public AbstractCamera<2> {
* The area of given size will be scaled down to range @f$ [-1; 1] @f$
* on all directions.
*/
Camera2D* setProjection(const Vector2& size);
Camera2D<T>* setProjection(const Math::Vector2<T>& size);
/* Overloads to remove WTF-factor from method chaining order */
#ifndef DOXYGEN_GENERATING_OUTPUT
inline Camera2D<T>* setAspectRatioPolicy(typename AbstractCamera<2, T>::AspectRatioPolicy policy) {
AbstractCamera<2, T>::setAspectRatioPolicy(policy);
return this;
}
#endif
};
/**
@brief %Camera for three-dimensional scenes
@brief Camera for three-dimensional scenes
See Drawable documentation for more information.
@section Object-explicit-specializations Explicit template specializations
The following specialization are explicitly compiled into SceneGraph library.
For other specializations you have to use Camera.hpp implementation file to
avoid linker errors. See @ref compilation-speedup-hpp for more information.
- @ref Camera3D "Camera3D<GLfloat>"
@see Camera2D
@see Camera2D, Drawable, DrawableGroup
*/
class SCENEGRAPH_EXPORT Camera3D: public AbstractCamera<3> {
template<class T = GLfloat> class SCENEGRAPH_EXPORT Camera3D: public AbstractCamera<3, T> {
public:
/**
* @brief Constructor
* @param parent Parent object
* @param object %Object holding this feature
*
* Sets orthographic projection to the default OpenGL cube (range @f$ [-1; 1] @f$ in all directions).
* @see setOrthographic(), setPerspective()
*/
inline Camera3D(Object3D* parent = nullptr): AbstractCamera<3>(parent), _near(0.0f), _far(0.0f) {}
inline Camera3D(AbstractObject<3, T>* object): AbstractCamera<3, T>(object), _near(0.0f), _far(0.0f) {}
/**
* @brief Set orthographic projection
@ -232,7 +293,7 @@ class SCENEGRAPH_EXPORT Camera3D: public AbstractCamera<3> {
* The volume of given size will be scaled down to range @f$ [-1; 1] @f$
* on all directions.
*/
Camera3D* setOrthographic(const Vector2& size, GLfloat near, GLfloat far);
Camera3D<T>* setOrthographic(const Math::Vector2<T>& size, T near, T far);
/**
* @brief Set perspective projection
@ -243,18 +304,32 @@ class SCENEGRAPH_EXPORT Camera3D: public AbstractCamera<3> {
*
* @todo Aspect ratio
*/
Camera3D* setPerspective(GLfloat fov, GLfloat near, GLfloat far);
Camera3D<T>* setPerspective(T fov, T near, T far);
/** @brief Near clipping plane */
inline GLfloat near() const { return _near; }
inline T near() const { return _near; }
/** @brief Far clipping plane */
inline GLfloat far() const { return _far; }
inline T far() const { return _far; }
/* Overloads to remove WTF-factor from method chaining order */
#ifndef DOXYGEN_GENERATING_OUTPUT
inline Camera3D<T>* setAspectRatioPolicy(typename AbstractCamera<3, T>::AspectRatioPolicy policy) {
AbstractCamera<3, T>::setAspectRatioPolicy(policy);
return this;
}
#endif
private:
GLfloat _near, _far;
T _near, _far;
};
/* Make implementers' life easier */
#ifndef MAGNUM_GCC46_COMPATIBILITY
template<class T = GLfloat> using DrawableGroup2D = DrawableGroup<2, T>;
template<class T = GLfloat> using DrawableGroup3D = DrawableGroup<3, T>;
#endif
}}
#endif

145
src/SceneGraph/Camera.hpp

@ -0,0 +1,145 @@
#ifndef Magnum_SceneGraph_Camera_hpp
#define Magnum_SceneGraph_Camera_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 Camera.h
*/
#include "Camera.h"
#include <algorithm>
#include "Drawable.h"
#include "Scene.h"
using namespace std;
namespace Magnum { namespace SceneGraph {
#ifndef DOXYGEN_GENERATING_OUTPUT
namespace Implementation {
template<std::uint8_t dimensions, class T> class Camera {};
template<class T> class Camera<2, T> {
public:
inline constexpr static Math::Matrix3<T> aspectRatioScale(const Math::Vector2<T>& scale) {
return Math::Matrix3<T>::scaling({scale.x(), scale.y()});
}
};
template<class T> class Camera<3, T> {
public:
inline constexpr static Math::Matrix4<T> aspectRatioScale(const Math::Vector2<T>& scale) {
return Math::Matrix4<T>::scaling({scale.x(), scale.y(), 1.0f});
}
};
template<std::uint8_t dimensions, class T> typename DimensionTraits<dimensions, T>::MatrixType aspectRatioFix(AspectRatioPolicy aspectRatioPolicy, const Math::Vector2<T>& projectionScale, const Math::Vector2<GLsizei>& viewport) {
/* Don't divide by zero / don't preserve anything */
if(projectionScale.x() == 0 || projectionScale.y() == 0 || viewport.x() == 0 || viewport.y() == 0 || aspectRatioPolicy == AspectRatioPolicy::NotPreserved)
return {};
Math::Vector2<T> relativeAspectRatio = Math::Vector2<T>::from(viewport)*projectionScale;
/* Extend on larger side = scale larger side down
Clip on smaller side = scale smaller side up */
return Camera<dimensions, T>::aspectRatioScale(
(relativeAspectRatio.x() > relativeAspectRatio.y()) == (aspectRatioPolicy == AspectRatioPolicy::Extend) ?
Vector2(relativeAspectRatio.y()/relativeAspectRatio.x(), T(1.0)) :
Vector2(T(1.0), relativeAspectRatio.x()/relativeAspectRatio.y()));
}
}
#endif
template<std::uint8_t dimensions, class T> AbstractCamera<dimensions, T>* AbstractCamera<dimensions, T>::setAspectRatioPolicy(AspectRatioPolicy policy) {
_aspectRatioPolicy = policy;
fixAspectRatio();
return this;
}
template<std::uint8_t dimensions, class T> void AbstractCamera<dimensions, T>::setViewport(const Math::Vector2<GLsizei>& size) {
_viewport = size;
fixAspectRatio();
}
template<std::uint8_t dimensions, class T> void AbstractCamera<dimensions, T>::draw(DrawableGroup<dimensions, T>& group) {
AbstractObject<dimensions, T>* scene = AbstractFeature<dimensions, T>::object()->sceneObject();
CORRADE_ASSERT(scene, "Camera::draw(): cannot draw when camera is not part of any scene", );
/* Compute camera matrix */
AbstractFeature<dimensions, T>::object()->setClean();
/* Compute transformations of all objects in the group relative to the camera */
std::vector<AbstractObject<dimensions, T>*> objects(group.size());
for(std::size_t i = 0; i != group.size(); ++i)
objects[i] = group[i]->object();
std::vector<typename DimensionTraits<dimensions, T>::MatrixType> transformations =
scene->transformationMatrices(objects, _cameraMatrix);
/* Perform the drawing */
for(std::size_t i = 0; i != transformations.size(); ++i)
group[i]->draw(transformations[i], this);
}
template<class T> Camera2D<T>* Camera2D<T>::setProjection(const Math::Vector2<T>& size) {
/* Scale the volume down so it fits in (-1, 1) in all directions */
AbstractCamera<2, T>::rawProjectionMatrix = Math::Matrix3<T>::scaling(2.0f/size);
AbstractCamera<2, T>::fixAspectRatio();
return this;
}
template<class T> Camera3D<T>* Camera3D<T>::setOrthographic(const Math::Vector2<T>& size, T near, T far) {
_near = near;
_far = far;
Math::Vector2<T> xyScale = T(2.0)/size;
T zScale = T(2.0)/(near-far);
AbstractCamera<3, T>::rawProjectionMatrix = Math::Matrix4<T>(
xyScale.x(), T(0.0), T(0.0), T(0.0),
T(0.0), xyScale.y(), T(0.0), T(0.0),
T(0.0), T(0.0), zScale, T(0.0),
T(0.0), T(0.0), near*zScale-1, T(1.0)
);
AbstractCamera<3, T>::fixAspectRatio();
return this;
}
template<class T> Camera3D<T>* Camera3D<T>::setPerspective(T fov, T near, T far) {
_near = near;
_far = far;
T xyScale = T(1.0)/tan(fov/2); /* == near/size */
T zScale = T(1.0)/(near-far);
AbstractCamera<3, T>::rawProjectionMatrix = Matrix4(
xyScale, T(0.0), T(0.0), T(0.0),
T(0.0), xyScale, T(0.0), T(0.0),
T(0.0), T(0.0), (far+near)*zScale, T(-1.0),
T(0.0), T(0.0), (2*far*near)*zScale, T(0.0)
);
AbstractCamera<3, T>::fixAspectRatio();
return this;
}
}}
#endif

192
src/SceneGraph/Drawable.h

@ -0,0 +1,192 @@
#ifndef Magnum_SceneGraph_Drawable_h
#define Magnum_SceneGraph_Drawable_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::Drawable, Magnum::SceneGraph::DrawableGroup, alias Magnum::SceneGraph::Drawable2D, Magnum::SceneGraph::Drawable3D, Magnum::SceneGraph::DrawableGroup2D, Magnum::SceneGraph::DrawableGroup3D
*/
#include "AbstractGroupedFeature.h"
namespace Magnum { namespace SceneGraph {
template<std::uint8_t, class> class AbstractCamera;
template<std::uint8_t, class> class Drawable;
#ifndef MAGNUM_GCC46_COMPATIBILITY
template<std::uint8_t dimensions, class T = GLfloat> using DrawableGroup = FeatureGroup<dimensions, Drawable<dimensions, T>, T>;
#else
template<std::uint8_t, class> class DrawableGroup;
#endif
/**
@brief %Drawable
Adds drawing function to object. Each %Drawable is part of some DrawableGroup
and the whole group is drawn with particular camera using AbstractCamera::draw().
@section Drawable-usage Usage
First thing is add Drawable feature to some object and implement draw(). You
can do it conveniently using multiple inheritance (see @ref scenegraph-features
for introduction). Example:
@code
typedef SceneGraph::Object<SceneGraph::MatrixTransformation3D<>> Object3D;
typedef SceneGraph::Scene<SceneGraph::MatrixTransformation3D<>> Scene3D;
class DrawableObject: public Object3D, SceneGraph::Drawable3D<> {
public:
DrawableObject(Object* parent, SceneGraph::DrawableGroup3D<>* group): Object3D(parent), SceneGraph::Drawable3D<>(this, group) {
// ...
}
void draw(const Matrix4& transformationMatrix, AbstractCamera3D<>* camera) override {
// ...
}
}
@endcode
Then you add these objects to your scene and some drawable group and transform
them as you like:
@code
Scene3D scene;
SceneGraph::DrawableGroup3D<> group;
(new DrawableObject(&scene, &group))
->translate(Vector3::yAxis(-0.3f))
->rotateX(deg(30.0f));
(new AnotherDrawableObject(&scene, &group))
->translate(Vector3::zAxis(0.5f));
// ...
@endcode
The last thing you need is Camera attached to some object (thus using its
transformation) and with it you can perform drawing in your draw event:
@code
Camera3D<> camera(&cameraObject);
void MyApplication::drawEvent() {
camera.draw(&group);
}
@endcode
@see Drawable2D, Drawable3D, DrawableGroup2D, DrawableGroup3D
*/
template<std::uint8_t dimensions, class T = GLfloat> class Drawable: public AbstractGroupedFeature<dimensions, Drawable<dimensions, T>, T> {
public:
/** @copydoc AbstractGroupedFeature::AbstractGroupedFeature() */
inline Drawable(AbstractObject<dimensions, T>* object, DrawableGroup<dimensions, T>* group = nullptr): AbstractGroupedFeature<dimensions, Drawable<dimensions, T>, T>(object, group) {}
/**
* @brief Draw the object using given camera
* @param transformationMatrix %Object transformation relative
* to camera
* @param camera Camera
*
* Projection matrix can be retrieved from AbstractCamera::projectionMatrix().
*/
virtual void draw(const typename DimensionTraits<dimensions, T>::MatrixType& transformationMatrix, AbstractCamera<dimensions, T>* camera) = 0;
};
/**
@brief Two-dimensional drawable
Convenience alternative to <tt>%Drawable<2, T></tt>. See Drawable for more
information.
@note Not available on GCC < 4.7. Use <tt>%Drawable<2, T></tt> instead.
@see Drawable3D
@todoc Remove workaround when Doxygen supports alias template
*/
#ifndef DOXYGEN_GENERATING_OUTPUT
#ifndef MAGNUM_GCC46_COMPATIBILITY
template<class T = GLfloat> using Drawable2D = Drawable<2, T>;
#endif
#else
typedef Drawable<2, T = GLfloat> Drawable2D;
#endif
/**
@brief Three-dimensional drawable
Convenience alternative to <tt>%Drawable<3, T></tt>. See Drawable for more
information.
@note Not available on GCC < 4.7. Use <tt>%Drawable<3, T></tt> instead.
@see Drawable2D
@todoc Remove workaround when Doxygen supports alias template
*/
#ifndef DOXYGEN_GENERATING_OUTPUT
#ifndef MAGNUM_GCC46_COMPATIBILITY
template<class T = GLfloat> using Drawable3D = Drawable<3, T>;
#endif
#else
typedef Drawable<3, T = GLfloat> Drawable3D;
#endif
/**
@brief Group of drawables
See Drawable for more information.
@see DrawableGroup2D, DrawableGroup3D
@todoc Remove workaround when Doxygen supports alias template
*/
#if !defined(MAGNUM_GCC46_COMPATIBILITY) && !defined(DOXYGEN_GENERATING_OUTPUT)
template<std::uint8_t dimensions, class T = GLfloat> using DrawableGroup = FeatureGroup<dimensions, Drawable<dimensions, T>, T>;
#else
template<std::uint8_t dimensions, class T = GLfloat> class DrawableGroup: public FeatureGroup<dimensions, Drawable<dimensions, T>, T> {};
#endif
/**
@brief Group of two-dimensional drawables
Convenience alternative to <tt>%DrawableGroup<2, T></tt>. See Drawable for
more information.
@note Not available on GCC < 4.7. Use <tt>%Drawable<2, T></tt> instead.
@see DrawableGroup3D
@todoc Remove workaround when Doxygen supports alias template
*/
#ifndef DOXYGEN_GENERATING_OUTPUT
#ifndef MAGNUM_GCC46_COMPATIBILITY
template<class T = GLfloat> using DrawableGroup2D = DrawableGroup<2, T>;
#endif
#else
typedef DrawableGroup<2, T = GLfloat> DrawableGroup2D;
#endif
/**
@brief Group of three-dimensional drawables
Convenience alternative to <tt>%DrawableGroup<3, T></tt>. See Drawable for
more information.
@note Not available on GCC < 4.7. Use <tt>%Drawable<3, T></tt> instead.
@see DrawableGroup2D
@todoc Remove workaround when Doxygen supports alias template
*/
#ifndef DOXYGEN_GENERATING_OUTPUT
#ifndef MAGNUM_GCC46_COMPATIBILITY
template<class T = GLfloat> using DrawableGroup3D = DrawableGroup<3, T>;
#endif
#else
typedef DrawableGroup<3, T = GLfloat> DrawableGroup3D;
#endif
/* Make implementers' life easier */
#ifndef MAGNUM_GCC46_COMPATIBILITY
template<class T = GLfloat> using AbstractCamera2D = AbstractCamera<2, T>;
template<class T = GLfloat> using AbstractCamera3D = AbstractCamera<3, T>;
#endif
}}
#endif

60
src/SceneGraph/Light.h

@ -1,60 +0,0 @@
#ifndef Magnum_SceneGraph_Light_h
#define Magnum_SceneGraph_Light_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::Light
*/
#include "Math/Point3D.h"
#include "Object.h"
namespace Magnum { namespace SceneGraph {
/**
* @brief Basic light object
*
* Provides cached light position.
*/
class SCENEGRAPH_EXPORT Light: public Object3D {
public:
/**
* @brief Constructor
* @param parent Parent object
*/
inline Light(Object3D* parent = nullptr): Object3D(parent) {}
/**
* @brief Light position relative to root object (scene)
*/
inline Point3D position() {
setClean();
return _position;
}
protected:
/**
* Recomputes light position.
*/
void clean(const Matrix4& absoluteTransformation);
private:
Point3D _position;
};
}}
#endif

12
src/SceneGraph/Light.cpp → src/SceneGraph/MatrixTransformation2D.cpp

@ -13,14 +13,14 @@
GNU Lesser General Public License version 3 for more details.
*/
#include "Light.h"
#include "MatrixTransformation2D.h"
namespace Magnum { namespace SceneGraph {
#include "Object.hpp"
void Light::clean(const Matrix4& absoluteTransformation) {
Object3D::clean(absoluteTransformation);
namespace Magnum { namespace SceneGraph {
_position = absoluteTransformation[3];
}
#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
}}

167
src/SceneGraph/MatrixTransformation3D.h

@ -0,0 +1,167 @@
#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;
}
/**
* @brief Rotate object around X axis
* @param angle Angle in radians, counterclockwise
* @param type Transformation type
* @return Pointer to self (for method chaining)
*
* Same as calling multiplyTransformation() with Matrix4::rotationX().
* @see deg(), rad()
*/
inline MatrixTransformation3D<T>* rotateX(T angle, TransformationType type = TransformationType::Global) override {
multiplyTransformation(Math::Matrix4<T>::rotationX(angle), type);
return this;
}
/**
* @brief Rotate object around Y axis
* @param angle Angle in radians, counterclockwise
* @param type Transformation type
* @return Pointer to self (for method chaining)
*
* Same as calling multiplyTransformation() with Matrix4::rotationY().
* @see deg(), rad()
*/
inline MatrixTransformation3D<T>* rotateY(T angle, TransformationType type = TransformationType::Global) override {
multiplyTransformation(Math::Matrix4<T>::rotationY(angle), type);
return this;
}
/**
* @brief Rotate object around Z axis
* @param angle Angle in radians, counterclockwise
* @param type Transformation type
* @return Pointer to self (for method chaining)
*
* Same as calling multiplyTransformation() with Matrix4::rotationZ().
* @see deg(), rad()
*/
inline MatrixTransformation3D<T>* rotateZ(T angle, TransformationType type = TransformationType::Global) override {
multiplyTransformation(Math::Matrix4<T>::rotationZ(angle), 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>;
}}

428
src/SceneGraph/Object.h

@ -16,89 +16,73 @@
*/
/** @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,
Visited = 1 << 1,
Joint = 1 << 2
};
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): counter(0xFFFFu), flags(Flag::Dirty) {
setParent(parent);
}
@ -108,9 +92,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 +107,116 @@ 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(); }
/** @brief Last child object or `nullptr`, if this object has no children */
inline ObjectType* lastChild() { return Corrade::Containers::LinkedList<ObjectType>::last(); }
/**
* @brief Set parent object
* @return Pointer to self (for method chaining)
*/
ObjectType* setParent(ObjectType* parent);
/*@}*/
/** @{ @name Object transformation
*
* All transformations (except absoluteTransformation()) are relative
* to parent.
*/
/** @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 Object<Transformation>* firstChild() {
return Corrade::Containers::LinkedList<Object<Transformation>>::first();
}
/**
* @brief Absolute transformation
*
* 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)
*/
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);
/** @overload */
inline const Object<Transformation>* firstChild() const {
return Corrade::Containers::LinkedList<Object<Transformation>>::first();
}
/*@}*/
/**
* @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.
*
* 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.
*/
inline bool isDirty() const { return dirty; }
/**
* @brief Set object and all its children as 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().
*
* Reimplementations should call this function at the end, i.e.:
* @code
* void setDirty() {
* // ...
*
* Object::setDirty();
* }
* @endcode
*/
virtual void setDirty();
/** @brief Last child object or `nullptr`, if this object has no children */
inline Object<Transformation>* lastChild() {
return Corrade::Containers::LinkedList<Object<Transformation>>::last();
}
/**
* @brief Set object and all its parents as clean
*
* Recursively calls clean() on every parent which is not already
* clean.
*/
void setClean();
/** @overload */
inline const Object<Transformation>* lastChild() const {
return Corrade::Containers::LinkedList<Object<Transformation>>::last();
}
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);
*
* // ...
* }
* @endcode
* @brief Set parent object
* @return Pointer to self (for method chaining)
*/
virtual void clean(const typename DimensionTraits<dimensions, GLfloat>::MatrixType& absoluteTransformation);
Object<Transformation>* setParent(Object<Transformation>* parent);
/*@}*/
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) {}
/** @{ @name Object transformation */
/**
* @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;
inline typename DimensionTraits<Transformation::Dimensions, typename Transformation::Type>::MatrixType absoluteTransformationMatrix() const override {
return Transformation::toMatrix(absoluteTransformation());
}
/**
* @brief Scale object
* @return Pointer to self (for method chaining)
* @brief Transformation relative to root object
*
* Same as calling multiplyTransformation() with Matrix3::scaling().
* @see absoluteTransformationMatrix()
*/
inline Object2D* scale(const Vector2& vec, Transformation type = Transformation::Global) {
multiplyTransformation(Matrix3::scaling(vec), type);
return this;
}
typename Transformation::DataType absoluteTransformation() const;
/**
* @brief Rotate object
* @return Pointer to self (for method chaining)
* @brief Transformations of given group of objects relative to this object
*
* Same as calling multiplyTransformation() with Matrix3::rotation().
* All transformations can be premultiplied with @p initialTransformation,
* if specified.
* @see AbstractObject::transformationMatrices()
*/
inline Object2D* rotate(GLfloat angle, Transformation type = Transformation::Global) {
multiplyTransformation(Matrix3::rotation(angle), type);
return this;
}
/* `objects` passed by copy intentionally (to allow move from
transformationMatrices() and avoid copy in the function itself) */
std::vector<typename Transformation::DataType> transformations(std::vector<Object<Transformation>*> objects, const typename Transformation::DataType& initialTransformation = typename Transformation::DataType()) const;
/**
* @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
inline bool isDirty() const override { return !!(flags & Flag::Dirty); }
void setDirty() override;
void setClean() override;
@see Object2D
*/
class SCENEGRAPH_EXPORT Object3D: public AbstractObject<3> {
public:
/** @copydoc AbstractObject::AbstractObject() */
inline Object3D(Object3D* parent = nullptr): AbstractObject<3>(parent) {}
private:
Object<Transformation>* sceneObject() override;
const Object<Transformation>* sceneObject() const override;
/**
* @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;
}
std::vector<typename DimensionTraits<Transformation::Dimensions, typename Transformation::Type>::MatrixType> transformationMatrices(const std::vector<AbstractObject<Transformation::Dimensions, typename Transformation::Type>*>& objects, const typename DimensionTraits<Transformation::Dimensions, typename Transformation::Type>::MatrixType& initialTransformationMatrix = typename DimensionTraits<Transformation::Dimensions, typename Transformation::Type>::MatrixType()) const override;
/**
* @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;
}
typename Transformation::DataType computeJointTransformation(const std::vector<Object<Transformation>*>& jointObjects, std::vector<typename Transformation::DataType>& jointTransformations, const std::size_t joint, const typename Transformation::DataType& initialTransformation) const;
/**
* @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;
std::uint16_t counter;
Flags flags;
};
}}

291
src/SceneGraph/Object.hpp

@ -0,0 +1,291 @@
#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() {
return static_cast<Scene<Transformation>*>(sceneObject());
}
template<class Transformation> const Scene<Transformation>* Object<Transformation>::scene() const {
return static_cast<const Scene<Transformation>*>(sceneObject());
}
template<class Transformation> Object<Transformation>* Object<Transformation>::sceneObject() {
Object<Transformation>* p(this);
while(p && !p->isScene()) p = p->parent();
return p;
}
template<class Transformation> const Object<Transformation>* Object<Transformation>::sceneObject() const {
const Object<Transformation>* p(this);
while(p && !p->isScene()) p = p->parent();
return 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;
}
}
template<class Transformation> std::vector<typename DimensionTraits<Transformation::Dimensions, typename Transformation::Type>::MatrixType> Object<Transformation>::transformationMatrices(const std::vector<AbstractObject<Transformation::Dimensions, typename Transformation::Type>*>& objects, const typename DimensionTraits<Transformation::Dimensions, typename Transformation::Type>::MatrixType& initialTransformationMatrix) const {
std::vector<Object<Transformation>*> castObjects(objects.size());
for(std::size_t i = 0; i != objects.size(); ++i)
/** @todo Ensure this doesn't crash, somehow */
castObjects[i] = static_cast<Object<Transformation>*>(objects[i]);
std::vector<typename Transformation::DataType> transformations = this->transformations(std::move(castObjects), Transformation::fromMatrix(initialTransformationMatrix));
std::vector<typename DimensionTraits<Transformation::Dimensions, typename Transformation::Type>::MatrixType> transformationMatrices(transformations.size());
for(std::size_t i = 0; i != objects.size(); ++i)
transformationMatrices[i] = Transformation::toMatrix(transformations[i]);
return transformationMatrices;
}
template<class Transformation> std::vector<typename Transformation::DataType> Object<Transformation>::transformations(std::vector<Object<Transformation>*> objects, const typename Transformation::DataType& initialTransformation) const {
/* Remember object count for later */
std::size_t objectCount = objects.size();
/* Create initial list of joints from original objects */
std::vector<Object<Transformation>*> jointObjects(objects.size());
for(std::size_t i = 0; i != jointObjects.size(); ++i) {
jointObjects[i] = static_cast<Object<Transformation>*>(objects[i]);
CORRADE_INTERNAL_ASSERT(jointObjects[i]->counter == 0xFFFFu);
jointObjects[i]->counter = i;
jointObjects[i]->flags |= Flag::Joint;
}
/* Scene object */
const Scene<Transformation>* scene = this->scene();
/* Nearest common ancestor not yet implemented - assert this is done on scene */
CORRADE_ASSERT(scene == this, "SceneGraph::Object::transformationMatrices(): currently implemented only for Scene", {});
/* Mark all objects up the hierarchy as visited */
auto it = objects.begin();
while(!objects.empty()) {
/* Mark the object as visited */
CORRADE_INTERNAL_ASSERT(!((*it)->flags & Flag::Visited));
(*it)->flags |= Flag::Visited;
Object<Transformation>* parent = (*it)->parent();
/* If this is root object, remove from list */
if(!parent) {
CORRADE_ASSERT(*it == scene, "SceneGraph::Object::transformations(): the objects are not part of the same tree", {});
it = objects.erase(it);
/* Parent is an joint or already visited - remove current from list */
} else if(parent->flags & (Flag::Visited|Flag::Joint)) {
it = objects.erase(it);
/* If not already marked as joint, mark it as such and add it to
list of joint objects */
if(!(parent->flags & Flag::Joint)) {
CORRADE_INTERNAL_ASSERT(parent->counter == 0xFFFFu);
parent->counter = jointObjects.size();
parent->flags |= Flag::Joint;
jointObjects.push_back(parent);
}
/* Else go up the hierarchy */
} else *it = parent;
/* Cycle if reached end */
if(it == objects.end()) it = objects.begin();
}
CORRADE_ASSERT(objects.size() < 0xFFFFu, "SceneGraph::Object::transformations(): too large scene", {});
/* Array of absolute transformations in joints */
std::vector<typename Transformation::DataType> jointTransformations(jointObjects.size());
/* Compute transformations for all joints */
for(std::size_t i = 0; i != jointTransformations.size(); ++i)
computeJointTransformation(jointObjects, jointTransformations, i, initialTransformation);
/* All visited marks are now cleaned, clean joint marks and counters */
for(auto i: jointObjects) {
CORRADE_INTERNAL_ASSERT(i->flags & Flag::Joint);
i->flags &= ~Flag::Joint;
i->counter = 0xFFFFu;
}
/* Shrink the array to contain only transformations of requested objects and return */
jointTransformations.resize(objectCount);
return jointTransformations;
}
template<class Transformation> typename Transformation::DataType Object<Transformation>::computeJointTransformation(const std::vector<Object<Transformation>*>& jointObjects, std::vector<typename Transformation::DataType>& jointTransformations, const std::size_t joint, const typename Transformation::DataType& initialTransformation) const {
Object<Transformation>* o = jointObjects[joint];
/* Transformation already computed ("unvisited" by this function before), done */
if(!(o->flags & Flag::Visited)) return jointTransformations[joint];
/* Initialize transformation */
jointTransformations[joint] = o->transformation();
/* Go up until next joint or root */
for(;;) {
/* Clean visited mark */
CORRADE_INTERNAL_ASSERT(o->flags & Flag::Visited);
o->flags &= ~Flag::Visited;
Object<Transformation>* parent = o->parent();
/* Root object, compose transformation with initial, done */
if(!parent) {
CORRADE_INTERNAL_ASSERT(o->isScene());
return (jointTransformations[joint] =
Transformation::compose(initialTransformation, jointTransformations[joint]));
/* Joint object, compose transformation with the joint, done */
} else if(parent->flags & Flag::Joint) {
return (jointTransformations[joint] =
Transformation::compose(computeJointTransformation(jointObjects, jointTransformations, parent->counter, initialTransformation), jointTransformations[joint]));
/* Else compose transformation with parent, go up the hierarchy */
} else {
jointTransformations[joint] = Transformation::compose(parent->transformation(), jointTransformations[joint]);
o = parent;
}
}
}
}}
#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

89
src/SceneGraph/Test/CameraTest.cpp

@ -17,11 +17,21 @@
#include "Math/Constants.h"
#include "SceneGraph/Camera.h"
#include "SceneGraph/Camera.hpp" /* only for aspectRatioFix(), so it doesn't have to be exported */
#include "SceneGraph/Drawable.h"
#include "SceneGraph/MatrixTransformation2D.h"
#include "SceneGraph/MatrixTransformation3D.h"
CORRADE_TEST_MAIN(Magnum::SceneGraph::Test::CameraTest)
namespace Magnum { namespace SceneGraph { namespace Test {
typedef SceneGraph::Object<SceneGraph::MatrixTransformation2D<>> Object2D;
typedef SceneGraph::Object<SceneGraph::MatrixTransformation3D<>> Object3D;
typedef SceneGraph::Scene<SceneGraph::MatrixTransformation3D<>> Scene3D;
typedef SceneGraph::Camera2D<> Camera2D;
typedef SceneGraph::Camera3D<> Camera3D;
CameraTest::CameraTest() {
addTests(&CameraTest::fixAspectRatio,
&CameraTest::defaultProjection2D,
@ -29,7 +39,8 @@ CameraTest::CameraTest() {
&CameraTest::projection2D,
&CameraTest::orthographic,
&CameraTest::perspective,
&CameraTest::projectionSizeViewport);
&CameraTest::projectionSizeViewport,
&CameraTest::draw);
}
void CameraTest::fixAspectRatio() {
@ -41,54 +52,57 @@ void CameraTest::fixAspectRatio() {
Vector2 projectionScaleZeroX(0.0f, 0.5f);
Math::Vector2<GLsizei> sizeZeroY(400, 0);
Math::Vector2<GLsizei> sizeZeroX(0, 300);
CORRADE_COMPARE(Implementation::aspectRatioFix<Matrix4>(Implementation::AspectRatioPolicy::Clip, projectionScaleZeroX, size), Matrix4());
CORRADE_COMPARE(Implementation::aspectRatioFix<Matrix4>(Implementation::AspectRatioPolicy::Clip, projectionScaleZeroY, size), Matrix4());
CORRADE_COMPARE(Implementation::aspectRatioFix<Matrix4>(Implementation::AspectRatioPolicy::Clip, projectionScale, sizeZeroY), Matrix4());
CORRADE_COMPARE(Implementation::aspectRatioFix<Matrix4>(Implementation::AspectRatioPolicy::Extend, projectionScale, sizeZeroX), Matrix4());
CORRADE_COMPARE((Implementation::aspectRatioFix<3, GLfloat>(Implementation::AspectRatioPolicy::Clip, projectionScaleZeroX, size)), Matrix4());
CORRADE_COMPARE((Implementation::aspectRatioFix<3, GLfloat>(Implementation::AspectRatioPolicy::Clip, projectionScaleZeroY, size)), Matrix4());
CORRADE_COMPARE((Implementation::aspectRatioFix<3, GLfloat>(Implementation::AspectRatioPolicy::Clip, projectionScale, sizeZeroY)), Matrix4());
CORRADE_COMPARE((Implementation::aspectRatioFix<3, GLfloat>(Implementation::AspectRatioPolicy::Extend, projectionScale, sizeZeroX)), Matrix4());
/* Not preserved */
CORRADE_COMPARE(Implementation::aspectRatioFix<Matrix4>(Implementation::AspectRatioPolicy::NotPreserved, projectionScale, size), Matrix4());
CORRADE_COMPARE((Implementation::aspectRatioFix<3, GLfloat>(Implementation::AspectRatioPolicy::NotPreserved, projectionScale, size)), Matrix4());
/* Clip */
Matrix4 expectedClip(1.0f, 0.0f, 0.0f, 0.0f,
0.0f, 4.0f/3.0f, 0.0f, 0.0f,
0.0f, 0.0f, 1.0f, 0.0f,
0.0f, 0.0f, 0.0f, 1.0f);
CORRADE_COMPARE(Implementation::aspectRatioFix<Matrix4>(Implementation::AspectRatioPolicy::Clip, Vector2(0.5f), size), expectedClip);
CORRADE_COMPARE((Implementation::aspectRatioFix<3, GLfloat>(Implementation::AspectRatioPolicy::Clip, Vector2(0.5f), size)), expectedClip);
Matrix4 expectedClipRectangle(1.0f, 0.0f, 0.0f, 0.0f,
0.0f, 2.0f, 0.0f, 0.0f,
0.0f, 0.0f, 1.0f, 0.0f,
0.0f, 0.0f, 0.0f, 1.0f);
CORRADE_COMPARE(Implementation::aspectRatioFix<Matrix4>(Implementation::AspectRatioPolicy::Clip, projectionScale, size), expectedClipRectangle);
CORRADE_COMPARE((Implementation::aspectRatioFix<3, GLfloat>(Implementation::AspectRatioPolicy::Clip, projectionScale, size)), expectedClipRectangle);
/* Extend */
Matrix4 expectedExtend(3.0f/4.0f, 0.0f, 0.0f, 0.0f,
0.0f, 1.0f, 0.0f, 0.0f,
0.0f, 0.0f, 1.0f, 0.0f,
0.0f, 0.0f, 0.0f, 1.0f);
CORRADE_COMPARE(Implementation::aspectRatioFix<Matrix4>(Implementation::AspectRatioPolicy::Extend, Vector2(0.5f), size), expectedExtend);
CORRADE_COMPARE((Implementation::aspectRatioFix<3, GLfloat>(Implementation::AspectRatioPolicy::Extend, Vector2(0.5f), size)), expectedExtend);
Matrix4 expectedExtendRectangle(0.5f, 0.0f, 0.0f, 0.0f,
0.0f, 1.0f, 0.0f, 0.0f,
0.0f, 0.0f, 1.0f, 0.0f,
0.0f, 0.0f, 0.0f, 1.0f);
CORRADE_COMPARE(Implementation::aspectRatioFix<Matrix4>(Implementation::AspectRatioPolicy::Extend, projectionScale, size), expectedExtendRectangle);
CORRADE_COMPARE((Implementation::aspectRatioFix<3, GLfloat>(Implementation::AspectRatioPolicy::Extend, projectionScale, size)), expectedExtendRectangle);
}
void CameraTest::defaultProjection2D() {
Camera2D camera;
Object2D o;
Camera2D camera(&o);
CORRADE_COMPARE(camera.projectionMatrix(), Matrix3());
CORRADE_COMPARE(camera.projectionSize(), Vector2(2.0f));
}
void CameraTest::defaultProjection3D() {
Camera3D camera;
Object3D o;
Camera3D camera(&o);
CORRADE_COMPARE(camera.projectionMatrix(), Matrix4());
CORRADE_COMPARE(camera.projectionSize(), Vector2(2.0f));
}
void CameraTest::projection2D() {
Vector2 projectionSize(4.0f, 3.0f);
Camera2D camera;
Object2D o;
Camera2D camera(&o);
camera.setProjection(projectionSize);
Matrix3 a(2.0f/4.0f, 0.0f, 0.0f,
@ -101,7 +115,8 @@ void CameraTest::projection2D() {
void CameraTest::orthographic() {
Vector2 projectionSize(5);
Camera3D camera;
Object3D o;
Camera3D camera(&o);
camera.setOrthographic(projectionSize, 1, 9);
Matrix4 a(0.4f, 0.0f, 0.0f, 0.0f,
@ -125,7 +140,8 @@ void CameraTest::orthographic() {
}
void CameraTest::perspective() {
Camera3D camera;
Object3D o;
Camera3D camera(&o);
camera.setPerspective(deg(27.0f), 32.0f, 100);
Matrix4 a(4.1652994f, 0.0f, 0.0f, 0.0f,
@ -138,7 +154,8 @@ void CameraTest::perspective() {
}
void CameraTest::projectionSizeViewport() {
Camera3D camera;
Object3D o;
Camera3D camera(&o);
camera.setViewport({200, 300});
CORRADE_COMPARE(camera.projectionSize(), Vector2(2.0f, 2.0f));
@ -149,4 +166,44 @@ void CameraTest::projectionSizeViewport() {
CORRADE_COMPARE(camera.projectionSize(), Vector2(4.0f/3.0f, 2.0f));
}
void CameraTest::draw() {
class Drawable: public SceneGraph::Drawable<3> {
public:
inline Drawable(AbstractObject<3>* object, DrawableGroup<3>* group, Matrix4& result): SceneGraph::Drawable<3>(object, group), result(result) {}
protected:
void draw(const Matrix4& transformationMatrix, AbstractCamera<3>*) {
result = transformationMatrix;
}
private:
Matrix4& result;
};
DrawableGroup<3> group;
Scene3D scene;
Object3D first(&scene);
Matrix4 firstTransformation;
first.scale(Vector3(5.0f));
new Drawable(&first, &group, firstTransformation);
Object3D second(&scene);
Matrix4 secondTransformation;
second.translate(Vector3::yAxis(3.0f));
new Drawable(&second, &group, secondTransformation);
Object3D third(&second);
Matrix4 thirdTransformation;
third.translate(Vector3::zAxis(-1.5f));
new Drawable(&third, &group, thirdTransformation);
Camera3D camera(&third);
camera.draw(group);
CORRADE_COMPARE(firstTransformation, Matrix4::translation({0.0f, -3.0f, 1.5f})*Matrix4::scaling(Vector3(5.0f)));
CORRADE_COMPARE(secondTransformation, Matrix4::translation(Vector3::zAxis(1.5f)));
CORRADE_COMPARE(thirdTransformation, Matrix4());
}
}}}

1
src/SceneGraph/Test/CameraTest.h

@ -30,6 +30,7 @@ class CameraTest: public Corrade::TestSuite::Tester<CameraTest> {
void orthographic();
void perspective();
void projectionSizeViewport();
void draw();
};
}}}

231
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,15 @@ 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::transformations,
&ObjectTest::caching);
}
void ObjectTest::parenting() {
@ -66,96 +68,181 @@ 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::scene() {
Scene3D scene;
CORRADE_VERIFY(scene.scene() == &scene);
void ObjectTest::absoluteTransformationWrongCamera() {
stringstream ss;
Error::setOutput(&ss);
Object3D* childOne = new Object3D(&scene);
Object3D* childTwo = new Object3D(childOne);
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");
Object3D orphan;
Object3D* childOfOrphan = new Object3D(&orphan);
CORRADE_VERIFY(childTwo->scene() == &scene);
CORRADE_VERIFY(childOfOrphan->scene() == nullptr);
}
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.rotateY(deg(90.0f));
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::rotationY(deg(90.0f)));
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}));
}
void ObjectTest::scene() {
Scene3D scene;
Object3D* childOne = new Object3D(&scene);
Object3D* childTwo = new Object3D(childOne);
void ObjectTest::transformations() {
Scene3D s;
Matrix4 initial = Matrix4::rotationX(deg(90.0f)).inverted();
/* Scene alone */
CORRADE_COMPARE(s.transformations({&s}, initial), vector<Matrix4>{initial});
/* One object */
Object3D first(&s);
first.rotateZ(deg(30.0f));
Object3D second(&first);
second.scale(Vector3(0.5f));
CORRADE_COMPARE(s.transformations({&second}, initial), vector<Matrix4>{
initial*Matrix4::rotationZ(deg(30.0f))*Matrix4::scaling(Vector3(0.5f))
});
/* One object and scene */
CORRADE_COMPARE(s.transformations({&second, &s}, initial), (vector<Matrix4>{
initial*Matrix4::rotationZ(deg(30.0f))*Matrix4::scaling(Vector3(0.5f)),
initial
}));
/* Two objects with foreign joint */
Object3D third(&first);
third.translate(Vector3::xAxis(5.0f));
CORRADE_COMPARE(s.transformations({&second, &third}, initial), (vector<Matrix4>{
initial*Matrix4::rotationZ(deg(30.0f))*Matrix4::scaling(Vector3(0.5f)),
initial*Matrix4::rotationZ(deg(30.0f))*Matrix4::translation(Vector3::xAxis(5.0f)),
}));
/* Three objects with joint as one of them */
CORRADE_COMPARE(s.transformations({&second, &third, &first}, initial), (vector<Matrix4>{
initial*Matrix4::rotationZ(deg(30.0f))*Matrix4::scaling(Vector3(0.5f)),
initial*Matrix4::rotationZ(deg(30.0f))*Matrix4::translation(Vector3::xAxis(5.0f)),
initial*Matrix4::rotationZ(deg(30.0f)),
}));
{
CORRADE_EXPECT_FAIL("Transformations not relative to scene are not yet implemented.");
/* Transformation relative to another object */
CORRADE_COMPARE(second.transformations({&third}), vector<Matrix4>{
Matrix4::scaling(Vector3(0.5f)).inverted()*Matrix4::translation(Vector3::xAxis(5.0f))
});
/* Transformation relative to another object, not part of any scene (but should work) */
Object3D orphanParent1;
orphanParent1.rotate(deg(31.0f), Vector3(1.0f).normalized());
Object3D orphanParent(&orphanParent1);
Object3D orphan1(&orphanParent);
orphan1.scale(Vector3::xScale(3.0f));
Object3D orphan2(&orphanParent);
orphan2.translate(Vector3::zAxis(5.0f));
CORRADE_COMPARE(orphan1.transformations({&orphan2}), vector<Matrix4>{
Matrix4::scaling(Vector3::xScale(3.0f)).inverted()*Matrix4::translation(Vector3::zAxis(5.0f))
});
}
ostringstream o;
Error::setOutput(&o);
/* Transformation of objects not part of the same scene */
Object3D orphan;
Object3D* childOfOrphan = new Object3D(&orphan);
CORRADE_VERIFY(childTwo->scene() == &scene);
CORRADE_VERIFY(childOfOrphan->scene() == nullptr);
CORRADE_COMPARE(s.transformations({&orphan}), vector<Matrix4>());
CORRADE_COMPARE(o.str(), "SceneGraph::Object::transformations(): the objects are not part of the same tree\n");
}
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 +256,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());

22
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,10 @@ 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 transformations();
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