mirror of https://github.com/mosra/magnum.git
4 changed files with 350 additions and 1 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). |
||||||
|
*/ |
||||||
|
}} |
||||||
Loading…
Reference in new issue