Feature guide » Using the scene graph

Overview of scene management capabilities.

THe Scene graph provides way to hierarchically manage your objects, their transformation, animation and rendering, among other things. The library is contained in the SceneGraph namespace, see its documentation for more information about building and usage with CMake.


  • objects, providing parent/children hierarchy
  • transformations, implementing particular transformation type
  • features, providing rendering capabilities, audio, animation, physics etc.

Basic concepts

SceneGraph hierarchy o1 o₁ o2 o₂ o1->o2 T₂ o3 o₃ o1->o3 T₃ o4 o₄ o5 o₅ o4->o5 T₅ s s s->o1 T₁ s->o4 T₄ c c c->o5 P

The basic organization of a scene graph is as follows: a top-level scene object $ \color{m-primary} s $ contains a hierarchy of objects $ o_i $ . Each object has a transformation $ \boldsymbol{T_i} $ relative to its parent — usually a transformation matrix. The whole scene is rendered using a camera $ \color{m-primary} c $ with a projection matrix $ \color{m-primary} \boldsymbol{P} $ . The projection matrix defines things like field-of-view, aspect ratio and near/far clipping planes. The final projective object transform $ \boldsymbol{M_i} $ , relative to camera, is calculated as a combination of all relative transformations up to the scene root (an absolute transformation), multiplied by an inverse of camera absolute transformation. For the object $ o_3 $ it's as follows:

\[ \begin{array}{rcl} \boldsymbol{M_3} & = & {\color{m-primary} \boldsymbol{P}} ~ (\color{m-success} \boldsymbol{T_4} ~ \boldsymbol{T_5})^{-1} ~ {\color{m-warning} \boldsymbol{T_1} ~ \boldsymbol{T_3}} \\ & = & {\color{m-primary} \boldsymbol{P}} \underbrace{\color{m-success} \boldsymbol{T_5}^{-1} ~ \boldsymbol{T_4}^{-1}}_{\boldsymbol{C}} {\color{m-warning} \boldsymbol{T_1} ~ \boldsymbol{T_3}} \end{array} \]

The inverse camera transformation $ \boldsymbol{C} $ is called a camera matrix. It's useful for example to calculate light positions relative to a camera.

SceneGraph transformations o1 o₁ o2 o₂ o1->o2 o3 o₃ o1->o3 o4 o₄ o5 o₅ o4->o5 c c s s s->o1 s->o4 d3 d₃ c->o5 d3->o3 d1 d₁ drawables g d₃ d₁ d1->o1 drawables:d3->d3 drawables:d1->d1

The objects themselves handle only parent/child relationship and transformation. Features add behavior to them. The camera $ \color{m-primary} c $ is one of them, together with a drawable $ \color{m-info} d_i $ . A drawable makes it possible to draw things on screen using a camera. It's not possible to just "draw the graph", the drawables are grouped into a drawable group $ \color{m-success} g $ . You can have just one, drawing everything at once, or group the drawables by a shader / transparency etc. It's also possible to have multiple cameras and switch among them.

Besides drawables, there are other features for animation, audio, physics etc.


A transformation handles object position, rotation etc. and its basic property is a dimension count (2D or 3D) and an underlying numeric type. All classes in SceneGraph are templated on the underlying type. However, in most cases Float is used and thus nearly all classes have convenience aliases so you don't have to explicitly specify it.

Scene graph has various transformation implementations for both 2D and 3D. Each implementation has its own advantages and disadvantages — for example when using matrices you can have nearly arbitrary transformations, but composing transformations, calculating their inverse and accounting for floating-point drift is a rather costly operation. On the other hand, quaternions for example won't allow you to scale or shear objects, but have far better performance characteristics.

It's also possible to implement your own transformation class for specific needs, see the source of builtin transformation classes for more information.


Common usage of transformation classes is to typedef SceneGraph::Scene and SceneGraph::Object with desired transformation type to save unnecessary typing later:



Scene3D scene;

Object3D object;

Scene hierarchy

Scene hierarchy is an essential part of the scene graph. In the root there is a SceneGraph::Scene, its children are SceneGraph::Object instances. The whole hierarchy has a single 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).

Build the hierarchy by parenting one object to another. Parent object can be either passed in the constructor or set using SceneGraph::Object::setParent(). The scene is always a root object, so it naturally cannot have any parent or transformation. Parent and children relationships can be observed through SceneGraph::Object::parent() and SceneGraph::Object::children().

Scene3D scene;

Object3D* first = new Object3D{&scene};
Object3D* second = new Object3D{first};

This hierarchy also takes care of memory management — when an object is destroyed, all its children are destroyed too. See detailed explanation of construction and destruction order below for information about possible issues. To reflect the implicit memory management in the code better, you can use SceneGraph::Object::addChild() instead of the naked new call from the code above:

Scene3D scene;

Object3D& first = scene.addChild<Object3D>();
Object3D& second = first.addChild<Object3D>();

Object features


Each feature takes a reference to holder object in its constructor, so adding a feature to an object might look just like the following, as in some cases you don't even need to keep the pointer to it. List of object features is accessible through SceneGraph::Object::features().

Object3D o;
new MyFeature{o, some, params};

Some features are passive, some active. Passive features can be just added to an object, with no additional work except for possible configuration (for example a debug renderer). 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 add all the active features you want and implement needed functions in your own SceneGraph::Object subclass without having to subclass each feature individually (and making the code overly verbose). Simplified example:

        explicit BouncingBall(Object3D* parent):

        // drawing implementation for Drawable feature
        void draw(const Matrix4&, SceneGraph::Camera3D&) override;

        // animation step for Animable feature
        void animationStep(Float, Float) override;


Similarly to object hierarchy, when destroying object, all its features (both member and inherited) are destroyed. See detailed explanation of construction and destruction order for information about possible issues. Also, there is the addFeature() counterpart to addChild():

Object3D o;
o.addFeature<MyFeature>(some, params);

Transformation caching in features

Some features need to operate with absolute transformations and their inversions — for example the camera needs its inverse transformation (camera matrix) 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 its transformation, its parent or by explicitly calling SceneGraph::Object::setDirty(). If the object is marked as dirty, all its children are marked as dirty as well and SceneGraph::AbstractFeature::markDirty() is called on every feature attached to them. Calling SceneGraph::Object::setClean() cleans the dirty object and all its dirty parents — it goes through all object features and calls SceneGraph::AbstractFeature::clean() or SceneGraph::AbstractFeature::cleanInverted() depending on which caching is enabled on given feature. If the object is already clean, SceneGraph::Object::setClean() does nothing.

Usually you will need caching in the SceneGraph::Object itself — which doesn't support it on its own — however you can take advantage of multiple inheritance and implement it using SceneGraph::AbstractFeature. In order to have caching, you must enable it first, because by default caching is disabled. You can enable it using SceneGraph::AbstractFeature::setCachedTransformations() and then implement the corresponding cleaning function(s):

class CachingObject: public Object3D, SceneGraph::AbstractFeature3D {

        void clean(const Matrix4& absoluteTransformation) override {
            _absolutePosition = absoluteTransformation.translation();

        Vector3 _absolutePosition;

When you need to use the cached value, you can explicitly request the cleanup by calling SceneGraph::Object::setClean(). SceneGraph::Camera, for example, calls it automatically before it starts rendering, as it needs up-to-date SceneGraph::Camera::cameraMatrix() to properly draw all objects.

Polymorphic access to object transformation




In the following example we are able to get pointer to both the SceneGraph::AbstractObject and the needed transformation from a single constructor parameter using a trick:

class TransformingFeature: public SceneGraph::AbstractFeature3D {
        template<class T> explicit TransformingFeature(SceneGraph::Object<T>& object):
            SceneGraph::AbstractFeature3D(object), _transformation(object) {}

        SceneGraph::AbstractTranslationRotation3D& _transformation;

If we take for example SceneGraph::Object<MatrixTransformation3D>, it is derived from SceneGraph::AbstractObject3D and SceneGraph::MatrixTransformation3D. Thus the reference to SceneGraph::AbstractTranslationRotation3D, is automatically extracted from the reference in our constructor.

Construction and destruction order


Object hierarchy


    Scene3D scene;
    Object3D object(&scene);

The object is created last, so it will be destroyed first, removing itself from scene's children list, causing no problems when destroying the scene object later. However, if their order is swapped, it will cause problems:

    Object3D object;
    Scene3D scene;

} // crash!


Member and inherited features

When destroying the object, all its features are destroyed. For features added as a member it's not an issue, however features added using multiple inheritance must be inherited after the Object class:

class MyObject: public Object3D, MyFeature {
        MyObject(Object3D* parent): Object3D(parent), MyFeature{*this} {}

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.


class MyObject: MyFeature, public Object3D {
        MyObject(Object3D* parent): MyFeature{*this}, Object3D(parent) {} // crash!



class MyObject: MyFeature, public Object3D {

        // crash on destruction!
