|
|
|
|
/*
|
|
|
|
|
This file is part of Magnum.
|
|
|
|
|
|
|
|
|
|
Copyright © 2010, 2011, 2012, 2013, 2014, 2015, 2016, 2017, 2018, 2019,
|
|
|
|
|
2020, 2021, 2022, 2023, 2024, 2025
|
|
|
|
|
Vladimír Vondruš <mosra@centrum.cz>
|
|
|
|
|
|
|
|
|
|
Permission is hereby granted, free of charge, to any person obtaining a
|
|
|
|
|
copy of this software and associated documentation files (the "Software"),
|
|
|
|
|
to deal in the Software without restriction, including without limitation
|
|
|
|
|
the rights to use, copy, modify, merge, publish, distribute, sublicense,
|
|
|
|
|
and/or sell copies of the Software, and to permit persons to whom the
|
|
|
|
|
Software is furnished to do so, subject to the following conditions:
|
|
|
|
|
|
|
|
|
|
The above copyright notice and this permission notice shall be included
|
|
|
|
|
in all copies or substantial portions of the Software.
|
|
|
|
|
|
|
|
|
|
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
|
|
|
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
|
|
|
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
|
|
|
|
|
THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
|
|
|
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
|
|
|
|
|
FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
|
|
|
|
|
DEALINGS IN THE SOFTWARE.
|
|
|
|
|
*/
|
|
|
|
|
|
|
|
|
|
namespace Magnum {
|
|
|
|
|
/** @page scenegraph Using the scene graph
|
|
|
|
|
@brief Overview of scene management capabilities.
|
|
|
|
|
|
|
|
|
|
@m_keywords{SceneGraph}
|
|
|
|
|
|
|
|
|
|
The scene graph provides a way to hierarchically manage your objects, their
|
|
|
|
|
transformation, animation and rendering, among other things. The library is
|
|
|
|
|
contained in the @ref SceneGraph namespace, see its documentation for more
|
|
|
|
|
information about building and usage with CMake.
|
|
|
|
|
|
|
|
|
|
@tableofcontents
|
|
|
|
|
@m_footernavigation
|
|
|
|
|
|
|
|
|
|
There are naturally many possible feature combinations (2D vs. 3D, different
|
|
|
|
|
transformation representations, animated vs. static, object can have collision
|
|
|
|
|
shape, participate in physics events, have forward vs. deferred rendering...)
|
|
|
|
|
and to make everything possible without combinatiorial explosion and allow the
|
|
|
|
|
users to provide their own features, a scene graph in Magnum is composed of
|
|
|
|
|
three main components:
|
|
|
|
|
|
|
|
|
|
- objects, providing parent/children hierarchy
|
|
|
|
|
- transformations, implementing particular transformation type
|
|
|
|
|
- features, providing rendering capabilities, audio, animation, physics etc.
|
|
|
|
|
|
|
|
|
|
@note Fully contained applications with initial scene graph setup are available
|
|
|
|
|
in `scenegraph2D` and `scenegraph3D` branches of
|
|
|
|
|
[Magnum Bootstrap](https://github.com/mosra/magnum-bootstrap) repository.
|
|
|
|
|
|
|
|
|
|
@section scenegraph-basic-concepts Basic concepts
|
|
|
|
|
|
|
|
|
|
@m_div{m-col-m-4 m-right-m}
|
|
|
|
|
@dotfile scenegraph-hierarchy.dot
|
|
|
|
|
@m_enddiv
|
|
|
|
|
|
|
|
|
|
The basic organization of a scene graph is as follows: a top-level scene object
|
|
|
|
|
@f$ \color{m-primary} s @f$ contains a hierarchy of objects @f$ o_i @f$. Each
|
|
|
|
|
object has a transformation @f$ \boldsymbol{T_i} @f$ relative to its parent ---
|
|
|
|
|
usually a transformation matrix. The whole scene is rendered using a camera
|
|
|
|
|
@f$ \color{m-primary} c @f$ with a projection matrix
|
|
|
|
|
@f$ \color{m-primary} \boldsymbol{P} @f$. The projection matrix defines things
|
|
|
|
|
like field-of-view, aspect ratio and near/far clipping planes. The final
|
|
|
|
|
projective object transform @f$ \boldsymbol{M_i} @f$, relative to camera, is
|
|
|
|
|
calculated as a combination of all relative transformations up to the scene
|
|
|
|
|
root (an absolute transformation), multiplied by the inverse of the camera's absolute
|
|
|
|
|
transformation. For the object @f$ o_3 @f$ its final transform @f$ \boldsymbol{M_3} @f$
|
|
|
|
|
is produced as follows: @f[
|
|
|
|
|
\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}
|
|
|
|
|
@f]
|
|
|
|
|
|
|
|
|
|
The inverse camera transformation @f$ \boldsymbol{C} @f$ is called a *camera
|
|
|
|
|
matrix*. It's useful for example to calculate light positions relative to a
|
|
|
|
|
camera.
|
|
|
|
|
|
|
|
|
|
@m_div{m-col-m-5 m-left-m}
|
|
|
|
|
@dotfile scenegraph-features.dot
|
|
|
|
|
@m_enddiv
|
|
|
|
|
|
|
|
|
|
The objects themselves handle only parent/child relationship and
|
|
|
|
|
transformation. *Features* add behavior to them. The camera
|
|
|
|
|
@f$ \color{m-primary} c @f$ is one of them, together with a *drawable*
|
|
|
|
|
@f$ \color{m-info} d_i @f$. 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 @f$ \color{m-success} g @f$. 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.
|
|
|
|
|
|
|
|
|
|
@m_div{m-clearfix-m} @m_enddiv
|
|
|
|
|
|
|
|
|
|
@section scenegraph-transformation Transformations
|
|
|
|
|
|
|
|
|
|
A transformation handles object position, rotation, etc. Its basic property
|
|
|
|
|
is a dimension count (2D or 3D) and an underlying numeric type. All classes
|
|
|
|
|
in @ref SceneGraph are templated on the underlying type. However, in most cases
|
|
|
|
|
@ref 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.
|
|
|
|
|
|
|
|
|
|
Magnum provides the following transformation classes. See documentation of each
|
|
|
|
|
class for more detailed information:
|
|
|
|
|
|
|
|
|
|
- @ref SceneGraph::BasicMatrixTransformation2D "SceneGraph::MatrixTransformation2D" ---
|
|
|
|
|
arbitrary 2D transformations but with slow inverse transformations and no
|
|
|
|
|
floating-point drift reduction
|
|
|
|
|
- @ref SceneGraph::BasicMatrixTransformation3D "SceneGraph::MatrixTransformation3D" ---
|
|
|
|
|
arbitrary 3D transformations but with slow inverse transformations and no
|
|
|
|
|
floating-point drift reduction
|
|
|
|
|
- @ref SceneGraph::BasicRigidMatrixTransformation2D "SceneGraph::RigidMatrixTransformation2D" ---
|
|
|
|
|
2D translation, rotation and reflection (no scaling), with relatively fast
|
|
|
|
|
inverse transformations and floating-point drift reduction
|
|
|
|
|
- @ref SceneGraph::BasicRigidMatrixTransformation3D "SceneGraph::RigidMatrixTransformation3D" ---
|
|
|
|
|
3D translation, rotation and reflection (no scaling), with relatively fast
|
|
|
|
|
inverse transformations and floating-point drift reduction
|
|
|
|
|
- @ref SceneGraph::BasicDualComplexTransformation "SceneGraph::DualComplexTransformation" ---
|
|
|
|
|
2D translation and rotation with fast inverse transformations and
|
|
|
|
|
floating-point drift reduction
|
|
|
|
|
- @ref SceneGraph::BasicDualQuaternionTransformation "SceneGraph::DualQuaternionTransformation" ---
|
|
|
|
|
3D translation and rotation with fast inverse transformation and
|
|
|
|
|
floating-point drift reduction
|
|
|
|
|
- @ref SceneGraph::BasicTranslationRotationScalingTransformation2D "SceneGraph::TranslationRotationScalingTransformation2D" ---
|
|
|
|
|
2D transformations with separate translation, rotation and scaling. Usable
|
|
|
|
|
for scenes with animation tracks that control these properties separately.
|
|
|
|
|
- @ref SceneGraph::BasicTranslationRotationScalingTransformation3D "SceneGraph::TranslationRotationScalingTransformation3D" ---
|
|
|
|
|
3D transformations with separate translation, rotation and scaling. Usable
|
|
|
|
|
for scenes with animation tracks that control these properties separately.
|
|
|
|
|
- @ref SceneGraph::TranslationTransformation "SceneGraph::TranslationTransformation*D" ---
|
|
|
|
|
Just 2D/3D translation (no rotation, scaling or anything else)
|
|
|
|
|
|
|
|
|
|
Common usage of transformation classes is to typedef @ref SceneGraph::Scene and
|
|
|
|
|
@ref SceneGraph::Object with desired transformation type to save unnecessary
|
|
|
|
|
typing later:
|
|
|
|
|
|
|
|
|
|
@snippet SceneGraph.cpp typedef
|
|
|
|
|
|
|
|
|
|
@attention Note that you have to include both @ref Magnum/SceneGraph/Object.h
|
|
|
|
|
and desired transformation class (e.g. @ref Magnum/SceneGraph/MatrixTransformation3D.h)
|
|
|
|
|
to be able to use the resulting type.
|
|
|
|
|
|
|
|
|
|
The object type is subclassed from the transformation type and so the
|
|
|
|
|
@cpp Object3D @ce type will then contain all members from both @ref SceneGraph::Object
|
|
|
|
|
and @ref SceneGraph::MatrixTransformation3D. For convenience you can use method
|
|
|
|
|
chaining:
|
|
|
|
|
|
|
|
|
|
@snippet SceneGraph.cpp method-chaining
|
|
|
|
|
|
|
|
|
|
@section scenegraph-hierarchy Scene hierarchy
|
|
|
|
|
|
|
|
|
|
Scene hierarchy is an essential part of the scene graph. In the root there is
|
|
|
|
|
a @ref SceneGraph::Scene, its children are @ref 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 @ref 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
|
|
|
|
|
@ref SceneGraph::Object::parent() and @ref SceneGraph::Object::children().
|
|
|
|
|
|
|
|
|
|
@snippet SceneGraph.cpp hierarchy
|
|
|
|
|
|
|
|
|
|
This hierarchy also 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"
|
|
|
|
|
below for information about possible issues. To reflect the implicit memory
|
|
|
|
|
management in the code better, you can use @ref SceneGraph::Object::addChild()
|
|
|
|
|
instead of the naked @cpp new @ce call from the code above:
|
|
|
|
|
|
|
|
|
|
@snippet SceneGraph.cpp hierarchy-addChild
|
|
|
|
|
|
|
|
|
|
@section scenegraph-features Object features
|
|
|
|
|
|
|
|
|
|
Magnum provides the following builtin features. See documentation of each class
|
|
|
|
|
for more detailed information and usage examples:
|
|
|
|
|
|
|
|
|
|
- @ref SceneGraph::Camera "SceneGraph::Camera*D" --- Handles projection
|
|
|
|
|
matrix, aspect ratio correction etc.. Used for rendering parts of the
|
|
|
|
|
scene.
|
|
|
|
|
- @ref SceneGraph::Drawable "SceneGraph::Drawable*D" --- Adds drawing
|
|
|
|
|
functionality to given object. Group of drawables can be then rendered
|
|
|
|
|
using the camera feature.
|
|
|
|
|
- @ref Audio::Listener "Audio::Listener*D" --- Handles audio listener
|
|
|
|
|
properties like position and orientation. Audio equivalent of a camera.
|
|
|
|
|
- @ref Audio::Playable "Audio::Playable*D" --- Handles audio source
|
|
|
|
|
properties. Audio equivalent of a drawable.
|
|
|
|
|
- @ref SceneGraph::Animable "SceneGraph::Animable*D" --- Adds animation
|
|
|
|
|
functionality to given object. Group of animables can be then controlled
|
|
|
|
|
using @ref SceneGraph::AnimableGroup "SceneGraph::AnimableGroup*D".
|
|
|
|
|
- @ref DebugTools::ObjectRenderer "DebugTools::ObjectRenderer*D",
|
|
|
|
|
@ref DebugTools::ForceRenderer "DebugTools::ForceRenderer*D" --- Visualize
|
|
|
|
|
object properties, object shape or force vector for debugging purposes. See
|
|
|
|
|
@ref debug-tools for more information.
|
|
|
|
|
|
|
|
|
|
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 @ref SceneGraph::Object::features().
|
|
|
|
|
|
|
|
|
|
@snippet SceneGraph.cpp feature
|
|
|
|
|
|
|
|
|
|
Some features are passive, others active. Passive features can just be 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 an 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 functions you need in your own @ref SceneGraph::Object
|
|
|
|
|
subclass without having to subclass each feature individually (and making the
|
|
|
|
|
code overly verbose). A simplified example:
|
|
|
|
|
|
|
|
|
|
@snippet SceneGraph.cpp feature-inherit
|
|
|
|
|
|
|
|
|
|
From the outside there is no difference between features added "at runtime" and
|
|
|
|
|
features added using multiple inheritance, they can be both accessed from the
|
|
|
|
|
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. Also, there is the
|
|
|
|
|
@ref SceneGraph::AbstractObject::addFeature() "addFeature()" counterpart to
|
|
|
|
|
@ref SceneGraph::Object::addChild() "addChild()":
|
|
|
|
|
|
|
|
|
|
@snippet SceneGraph.cpp feature-addFeature
|
|
|
|
|
|
|
|
|
|
@subsection scenegraph-features-caching 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
|
|
|
|
|
@ref SceneGraph::Object::setDirty(). If the object is marked as dirty, all its
|
|
|
|
|
children are marked as dirty as well and @ref SceneGraph::AbstractFeature::markDirty()
|
|
|
|
|
is called on every feature attached to them. Calling
|
|
|
|
|
@ref SceneGraph::Object::setClean() cleans the dirty object and all its
|
|
|
|
|
dirty parents --- it goes through all object features and calls
|
|
|
|
|
@ref SceneGraph::AbstractFeature::clean() or
|
|
|
|
|
@ref SceneGraph::AbstractFeature::cleanInverted() depending on which caching is
|
|
|
|
|
enabled on given feature. If the object is already clean,
|
|
|
|
|
@ref SceneGraph::Object::setClean() does nothing.
|
|
|
|
|
|
|
|
|
|
Usually you will need caching in the @ref SceneGraph::Object itself --- which
|
|
|
|
|
doesn't support it on its own --- however you can take advantage of multiple
|
|
|
|
|
inheritance and implement it using @ref SceneGraph::AbstractFeature. In order
|
|
|
|
|
to have caching, you must enable it first, because by default caching is
|
|
|
|
|
disabled. You can enable it using @ref SceneGraph::AbstractFeature::setCachedTransformations()
|
|
|
|
|
and then implement the corresponding cleaning function(s):
|
|
|
|
|
|
|
|
|
|
@snippet SceneGraph.cpp caching
|
|
|
|
|
|
|
|
|
|
When you need to use the cached value, you can explicitly request the cleanup
|
|
|
|
|
by calling @ref SceneGraph::Object::setClean(). @ref SceneGraph::Camera, for
|
|
|
|
|
example, calls it automatically before it starts rendering, as it needs
|
|
|
|
|
up-to-date @ref SceneGraph::Camera::cameraMatrix() to properly draw all
|
|
|
|
|
objects.
|
|
|
|
|
|
|
|
|
|
@subsection scenegraph-features-transformation Polymorphic access to object transformation
|
|
|
|
|
|
|
|
|
|
Features by default have access only to @ref SceneGraph::AbstractObject, which
|
|
|
|
|
doesn't know about any particular transformation implementation. This has the
|
|
|
|
|
advantage that features don't have to be implemented for all possible
|
|
|
|
|
transformation implementations. But, as a consequence, it is impossible to
|
|
|
|
|
transform the object using only a pointer to @ref SceneGraph::AbstractObject.
|
|
|
|
|
|
|
|
|
|
To solve this, the transformation classes are subclassed from interfaces
|
|
|
|
|
sharing common functionality, so the feature can use that interface instead of
|
|
|
|
|
being specialized for all relevant transformation implementations. The
|
|
|
|
|
following interfaces are available, each having its own set of virtual
|
|
|
|
|
functions to control the transformation:
|
|
|
|
|
|
|
|
|
|
- @ref SceneGraph::AbstractTransformation "SceneGraph::AbstractTransformation*D" ---
|
|
|
|
|
base for all transformations
|
|
|
|
|
- @ref SceneGraph::AbstractTranslation "SceneGraph::AbstractTranslation*D" ---
|
|
|
|
|
base for all transformations providing translation
|
|
|
|
|
- @ref SceneGraph::AbstractBasicTranslationRotation2D "SceneGraph::AbstractTranslationRotation2D",
|
|
|
|
|
@ref SceneGraph::AbstractBasicTranslationRotation3D "SceneGraph::AbstractTranslationRotation3D" ---
|
|
|
|
|
base for all transformations providing translation and rotation
|
|
|
|
|
- @ref SceneGraph::AbstractBasicTranslationRotationScaling2D "SceneGraph::AbstractBasicTranslationRotationScaling2D",
|
|
|
|
|
@ref SceneGraph::AbstractBasicTranslationRotationScaling3D "SceneGraph::AbstractBasicTranslationRotationScaling3D" ---
|
|
|
|
|
base for all transformations providing translation, rotation and scaling
|
|
|
|
|
|
|
|
|
|
These interfaces provide virtual functions which can be used to modify object
|
|
|
|
|
transformations. The virtual calls are used only when calling through the
|
|
|
|
|
interface and not when using the concrete implementation directly to avoid
|
|
|
|
|
negative performance effects. There are no functions to retrieve object
|
|
|
|
|
transformation, you need to use the above transformation caching mechanism for
|
|
|
|
|
that.
|
|
|
|
|
|
|
|
|
|
In the following example we are able to get pointer to both the
|
|
|
|
|
@ref SceneGraph::AbstractObject and the needed transformation from a single
|
|
|
|
|
constructor parameter using a trick:
|
|
|
|
|
|
|
|
|
|
@snippet SceneGraph.cpp transformation
|
|
|
|
|
|
|
|
|
|
If we take for example @ref SceneGraph::Object "SceneGraph::Object<MatrixTransformation3D>",
|
|
|
|
|
it is derived from @ref SceneGraph::AbstractObject "SceneGraph::AbstractObject3D"
|
|
|
|
|
and @ref SceneGraph::BasicMatrixTransformation3D "SceneGraph::MatrixTransformation3D".
|
|
|
|
|
Thus the reference to @ref SceneGraph::AbstractBasicTranslationRotation3D "SceneGraph::AbstractTranslationRotation3D",
|
|
|
|
|
is automatically extracted from the reference in our constructor.
|
|
|
|
|
|
|
|
|
|
@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 @cpp new @ce),
|
|
|
|
|
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:
|
|
|
|
|
|
|
|
|
|
@snippet SceneGraph.cpp construction-order
|
|
|
|
|
|
|
|
|
|
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:
|
|
|
|
|
|
|
|
|
|
@snippet SceneGraph.cpp construction-order-crash
|
|
|
|
|
|
|
|
|
|
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 a member it's not an issue, however features added using multiple
|
|
|
|
|
inheritance must be inherited after the Object class:
|
|
|
|
|
|
|
|
|
|
@snippet SceneGraph.cpp feature-construction-order
|
|
|
|
|
|
|
|
|
|
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:
|
|
|
|
|
|
|
|
|
|
@snippet SceneGraph.cpp feature-construction-order-crash
|
|
|
|
|
|
|
|
|
|
`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:
|
|
|
|
|
|
|
|
|
|
@snippet SceneGraph.cpp feature-construction-order-crash-destruction
|
|
|
|
|
|
|
|
|
|
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).
|
|
|
|
|
*/
|
|
|
|
|
}
|