mirror of https://github.com/mosra/magnum.git
33 changed files with 3009 additions and 857 deletions
@ -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. |
||||||
|
|
||||||
|
*/ |
||||||
|
} |
||||||
@ -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). |
||||||
|
*/ |
||||||
|
}} |
||||||
@ -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 |
||||||
@ -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 |
||||||
@ -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 |
||||||
@ -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 |
||||||
@ -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 |
||||||
@ -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 |
||||||
@ -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 |
||||||
@ -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 |
||||||
@ -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 |
||||||
@ -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 |
||||||
@ -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 |
|
||||||
@ -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 |
||||||
@ -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 |
||||||
|
|
||||||
|
}} |
||||||
@ -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 |
||||||
@ -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>; |
|
||||||
|
|
||||||
}} |
|
||||||
@ -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 |
||||||
Loading…
Reference in new issue