Browse Source

SceneGraph: compile code snippets to ensure their validity.

pull/255/head
Vladimír Vondruš 8 years ago
parent
commit
bbe6b5a56b
  1. 121
      doc/scenegraph.dox
  2. 22
      doc/snippets/CMakeLists.txt
  3. 209
      doc/snippets/MagnumSceneGraph-gl.cpp
  4. 332
      doc/snippets/MagnumSceneGraph.cpp
  5. 27
      src/Magnum/SceneGraph/AbstractFeature.h
  6. 10
      src/Magnum/SceneGraph/AbstractGroupedFeature.h
  7. 13
      src/Magnum/SceneGraph/AbstractObject.h
  8. 42
      src/Magnum/SceneGraph/Animable.h
  9. 22
      src/Magnum/SceneGraph/Camera.h
  10. 86
      src/Magnum/SceneGraph/Drawable.h
  11. 18
      src/Magnum/SceneGraph/Object.h

121
doc/scenegraph.dox

@ -96,10 +96,7 @@ class for more detailed information:
Common usage of transformation classes is to typedef Scene and Object with Common usage of transformation classes is to typedef Scene and Object with
desired transformation type to save unnecessary typing later: desired transformation type to save unnecessary typing later:
@code{.cpp} @snippet MagnumSceneGraph.cpp typedef
typedef SceneGraph::Scene<SceneGraph::MatrixTransformation3D> Scene3D;
typedef SceneGraph::Object<SceneGraph::MatrixTransformation3D> Object3D;
@endcode
@attention Note that you have to include both @ref Magnum/SceneGraph/Object.h @attention Note that you have to include both @ref Magnum/SceneGraph/Object.h
and desired transformation class (e.g. @ref Magnum/SceneGraph/MatrixTransformation3D.h) and desired transformation class (e.g. @ref Magnum/SceneGraph/MatrixTransformation3D.h)
@ -110,14 +107,7 @@ The object type is subclassed from the transformation type and so the
and @ref SceneGraph::MatrixTransformation3D. For convenience you can use method and @ref SceneGraph::MatrixTransformation3D. For convenience you can use method
chaining: chaining:
@code{.cpp} @snippet MagnumSceneGraph.cpp method-chaining
Scene3D scene;
Object3D object;
object.setParent(&scene)
.rotateY(15.0_degf)
.translate(Vector3::xAxis(5.0f));
@endcode
@section scenegraph-hierarchy Scene hierarchy @section scenegraph-hierarchy Scene hierarchy
@ -134,26 +124,16 @@ naturally cannot have parent object. Parent and children relationships can be
observed through @ref SceneGraph::Object::parent() and observed through @ref SceneGraph::Object::parent() and
@ref SceneGraph::Object::children(). @ref SceneGraph::Object::children().
@code{.cpp} @snippet MagnumSceneGraph.cpp hierarchy
Scene3D scene;
Object3D* first = new Object3D{&scene};
Object3D* second = new Object3D{first};
@endcode
The hierarchy takes care of memory management --- when an object is destroyed, The hierarchy takes care of memory management --- when an object is destroyed,
all its children are destroyed too. See detailed explanation of all its children are destroyed too. See detailed explanation of
@ref scenegraph-object-construction-order "construction and destruction order" @ref scenegraph-object-construction-order "construction and destruction order"
below for information about possible issues. To reflect the implicit memory below for information about possible issues. To reflect the implicit memory
management in the code better, you can use @ref SceneGraph::Object::addChild() management in the code better, you can use @ref SceneGraph::Object::addChild()
instead of the naked `new` call in the code above: instead of the naked @cpp new @ce call in the code above:
@code{.cpp}
Scene3D scene;
Object3D& first = scene.addChild<Object3D>(); @snippet MagnumSceneGraph.cpp hierarchy-addChild
Object3D& second = first.addChild<Object3D>();
@endcode
@section scenegraph-features Object features @section scenegraph-features Object features
@ -187,10 +167,7 @@ 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 don't even need to keep the pointer to it. List of object features is
accessible through @ref SceneGraph::Object::features(). accessible through @ref SceneGraph::Object::features().
@code{.cpp} @snippet MagnumSceneGraph.cpp feature
Object3D& o;
new MyFeature{o, ...};
@endcode
Some features are passive, some active. Passive features can be just added to Some features are passive, some active. Passive features can be just added to
an object, with no additional work except for possible configuration (for an object, with no additional work except for possible configuration (for
@ -202,19 +179,7 @@ features you want and implement needed functions in your own @ref SceneGraph::Ob
subclass without having to subclass each feature individually (and making the subclass without having to subclass each feature individually (and making the
code overly verbose). Simplified example: code overly verbose). Simplified example:
@code{.cpp} @snippet MagnumSceneGraph.cpp feature-inherit
class BouncingBall: public Object3D, SceneGraph::Drawable3D, SceneGraph::Animable3D {
public:
explicit BouncingBall(Object3D* parent): Object3D{parent}, SceneGraph::Drawable3D{*this}, SceneGraph::Animable3D{*this} {}
private:
// drawing implementation for Drawable feature
void draw(...) override;
// animation step for Animable feature
void animationStep(...) override;
};
@endcode
From the outside there is no difference between features added "at runtime" and From the outside there is no difference between features added "at runtime" and
features added using multiple inheritance, they can be both accessed from features added using multiple inheritance, they can be both accessed from
@ -227,10 +192,7 @@ for information about possible issues. Also, there is a
@ref SceneGraph::AbstractObject::addFeature() counterpart to @ref SceneGraph::AbstractObject::addFeature() counterpart to
@ref SceneGraph::Object::addChild(): @ref SceneGraph::Object::addChild():
@code{.cpp} @snippet MagnumSceneGraph.cpp feature-addFeature
Object3D& o;
o.addFeature<MyFeature>(...);
@endcode
@subsection scenegraph-features-caching Transformation caching in features @subsection scenegraph-features-caching Transformation caching in features
@ -258,22 +220,7 @@ to have caching, you must enable it first, because by default the caching is
disabled. You can enable it using @ref SceneGraph::AbstractFeature::setCachedTransformations() disabled. You can enable it using @ref SceneGraph::AbstractFeature::setCachedTransformations()
and then implement corresponding cleaning function(s): and then implement corresponding cleaning function(s):
@code{.cpp} @snippet MagnumSceneGraph.cpp caching
class CachingObject: public Object3D, SceneGraph::AbstractFeature3D {
public:
explicit CachingObject(Object3D* parent): Object3D{parent}, SceneGraph::AbstractFeature3D{*this} {
setCachedTransformations(SceneGraph::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 When you need to use the cached value, you can explicitly request the cleanup
by calling @ref SceneGraph::Object::setClean(). @ref SceneGraph::Camera3D "Camera", by calling @ref SceneGraph::Object::setClean(). @ref SceneGraph::Camera3D "Camera",
@ -316,16 +263,7 @@ In the following example we are able to get pointer to both
@ref SceneGraph::AbstractObject and needed transformation from one @ref SceneGraph::AbstractObject and needed transformation from one
constructor parameter using small trick: constructor parameter using small trick:
@code{.cpp} @snippet MagnumSceneGraph.cpp transformation
class TransformingFeature: public SceneGraph::AbstractFeature3D {
public:
template<class T> TransformingFeature(SceneGraph::Object<T>& object):
SceneGraph::AbstractFeature3D(object), transformation(object) {}
private:
SceneGraph::AbstractTranslationRotation3D& transformation;
};
@endcode
If we take for example @ref SceneGraph::Object "SceneGraph::Object<MatrixTransformation3D>", If we take for example @ref SceneGraph::Object "SceneGraph::Object<MatrixTransformation3D>",
it is derived from @ref SceneGraph::AbstractObject "SceneGraph::AbstractObject3D" it is derived from @ref SceneGraph::AbstractObject "SceneGraph::AbstractObject3D"
@ -347,25 +285,13 @@ 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 destroyed when they go out of scope. Normally, the natural order of creation is
not a problem: not a problem:
@code{.cpp} @snippet MagnumSceneGraph.cpp construction-order
{
Scene3D scene;
Object3D object(&scene);
}
@endcode
The object is created last, so it will be destroyed first, removing itself The object is created last, so it will be destroyed first, removing itself
from `scene`'s children list, causing no problems when destroying `scene` from `scene`'s children list, causing no problems when destroying `scene`
object later. However, if their order is swapped, it will cause problems: object later. However, if their order is swapped, it will cause problems:
@code{.cpp} @snippet MagnumSceneGraph.cpp construction-order-crash
{
Object3D object;
Scene3D scene;
object.setParent(&scene);
} // crash!
@endcode
The scene will be destroyed first, deleting all its children, which is wrong, 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 because `object` is created on stack. If this doesn't already crash, the
@ -377,12 +303,7 @@ 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 as member it's no issue, features added using multiple inheritance must be
inherited after the Object class: inherited after the Object class:
@code{.cpp} @snippet MagnumSceneGraph.cpp feature-construction-order
class MyObject: public Object3D, MyFeature {
public:
MyObject(Object3D* parent): Object3D(parent), MyFeature(*this) {}
};
@endcode
When constructing `MyObject`, `Object3D` constructor is called first and then When constructing `MyObject`, `Object3D` constructor is called first and then
`MyFeature` constructor adds itself to `Object3D`'s list of features. When `MyFeature` constructor adds itself to `Object3D`'s list of features. When
@ -392,12 +313,7 @@ from `Object3D`'s list, then `Object3D` destructor.
However, if we would inherit `MyFeature` first, it will cause problems: However, if we would inherit `MyFeature` first, it will cause problems:
@code{.cpp} @snippet MagnumSceneGraph.cpp feature-construction-order-crash
class MyObject: MyFeature, public Object3D {
public:
MyObject(Object3D* parent): MyFeature(*this), Object3D(parent) {} // crash!
};
@endcode
`MyFeature` tries to add itself to feature list in not-yet-constructed `MyFeature` tries to add itself to feature list in not-yet-constructed
`Object3D`, causing undefined behavior. Then, if this doesn't already crash, `Object3D`, causing undefined behavior. Then, if this doesn't already crash,
@ -407,14 +323,7 @@ invisible.
If we would construct them in swapped order (if it is even possible), it If we would construct them in swapped order (if it is even possible), it
wouldn't help either: wouldn't help either:
@code{.cpp} @snippet MagnumSceneGraph.cpp feature-construction-order-crash-destruction
class MyObject: MyFeature, public Object3D {
public:
MyObject(Object3D* parent): Object3D(parent), MyFeature(*this) {}
// crash on destruction!
};
@endcode
On destruction, `Object3D` destructor is called first, deleting `MyFeature`, On destruction, `Object3D` destructor is called first, deleting `MyFeature`,
which is wrong, because `MyFeature` is in the same object. After that (if the which is wrong, because `MyFeature` is in the same object. After that (if the

22
doc/snippets/CMakeLists.txt

@ -87,6 +87,28 @@ if(WITH_DEBUGTOOLS AND Corrade_TestSuite_FOUND AND NOT CORRADE_TARGET_IOS)
set_target_properties(debugtools-compareimage PROPERTIES FOLDER "Magnum/doc/snippets") set_target_properties(debugtools-compareimage PROPERTIES FOLDER "Magnum/doc/snippets")
endif() endif()
if(WITH_SCENEGRAPH)
add_library(snippets-MagnumSceneGraph STATIC
MagnumSceneGraph.cpp)
target_link_libraries(snippets-MagnumSceneGraph PRIVATE MagnumSceneGraph)
set_target_properties(snippets-MagnumSceneGraph
PROPERTIES FOLDER "Magnum/doc/snippets")
if(WITH_SDL2APPLICATION AND WITH_GL AND WITH_PRIMITIVES AND WITH_SHADERS AND WITH_MESHTOOLS)
add_library(snippets-MagnumSceneGraph-gl STATIC
MagnumSceneGraph-gl.cpp)
target_link_libraries(snippets-MagnumSceneGraph-gl PRIVATE
MagnumGL
MagnumMeshTools
MagnumPrimitives
MagnumSceneGraph
MagnumSdl2Application
MagnumShaders)
set_target_properties(snippets-MagnumSceneGraph-gl
PROPERTIES FOLDER "Magnum/doc/snippets")
endif()
endif()
if(WITH_SDL2APPLICATION) if(WITH_SDL2APPLICATION)
add_executable(getting-started getting-started.cpp) add_executable(getting-started getting-started.cpp)
add_executable(getting-started-blue getting-started-blue.cpp) add_executable(getting-started-blue getting-started-blue.cpp)

209
doc/snippets/MagnumSceneGraph-gl.cpp

@ -0,0 +1,209 @@
/*
This file is part of Magnum.
Copyright © 2010, 2011, 2012, 2013, 2014, 2015, 2016, 2017, 2018
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.
*/
#include "Magnum/Timeline.h"
#include "Magnum/GL/Buffer.h"
#include "Magnum/GL/Mesh.h"
#include "Magnum/GL/DefaultFramebuffer.h"
#include "Magnum/GL/Renderer.h"
#include "Magnum/Math/Color.h"
#include "Magnum/MeshTools/Compile.h"
#include "Magnum/Primitives/Cube.h"
#include "Magnum/Platform/Sdl2Application.h"
#include "Magnum/SceneGraph/AnimableGroup.h"
#include "Magnum/SceneGraph/Camera.h"
#include "Magnum/SceneGraph/Drawable.h"
#include "Magnum/SceneGraph/MatrixTransformation2D.h"
#include "Magnum/SceneGraph/MatrixTransformation3D.h"
#include "Magnum/SceneGraph/Object.h"
#include "Magnum/SceneGraph/Scene.h"
#include "Magnum/Shaders/Flat.h"
#include "Magnum/Shaders/Phong.h"
#include "Magnum/Trade/MeshData3D.h"
using namespace Magnum;
using namespace Magnum::Math::Literals;
struct MyApplication: Platform::Application {
explicit MyApplication(const Arguments& arguments);
void drawEvent() override;
void mousePressEvent(MouseEvent& event) override;
SceneGraph::AnimableGroup3D animables;
Timeline timeline;
SceneGraph::Object<SceneGraph::MatrixTransformation2D>* cameraObject{};
SceneGraph::Camera2D camera{*cameraObject};
};
/* [Animable-usage-timeline] */
MyApplication::MyApplication(const Arguments& arguments): Platform::Application{arguments} {
// ...
timeline.start();
}
void MyApplication::drawEvent() {
animables.step(timeline.previousFrameTime(), timeline.previousFrameDuration());
// ...
timeline.nextFrame();
}
/* [Animable-usage-timeline] */
void MyApplication::mousePressEvent(MouseEvent& event) {
/* [Camera-projectionSize] */
Vector2 position = (Vector2{event.position()}/Vector2{GL::defaultFramebuffer.viewport().size()}
- Vector2{0.5f})*Vector2::yScale(-1.0f)*camera.projectionSize();
/* [Camera-projectionSize] */
/* [Camera-projectionSize-absolute] */
Vector2 absolutePosition = cameraObject->absoluteTransformation().transformPoint(position);
/* [Camera-projectionSize-absolute] */
static_cast<void>(absolutePosition);
}
/* [Drawable-usage] */
typedef SceneGraph::Object<SceneGraph::MatrixTransformation3D> Object3D;
typedef SceneGraph::Scene<SceneGraph::MatrixTransformation3D> Scene3D;
class RedCube: public Object3D, public SceneGraph::Drawable3D {
public:
explicit RedCube(Object3D* parent, SceneGraph::DrawableGroup3D* group): Object3D{parent}, SceneGraph::Drawable3D{*this, group} {
std::tie(_mesh, _vertices, _indices) = MeshTools::compile(Primitives::cubeSolid(), GL::BufferUsage::StaticDraw);
}
private:
void draw(const Matrix4& transformationMatrix, SceneGraph::Camera3D& camera) override {
_shader.setDiffuseColor(Color3::fromHsv(216.0_degf, 0.85f, 1.0f))
.setLightPosition(camera.cameraMatrix().transformPoint({5.0f, 5.0f, 7.0f}))
.setTransformationMatrix(transformationMatrix)
.setNormalMatrix(transformationMatrix.rotation())
.setProjectionMatrix(camera.projectionMatrix());
_mesh.draw(_shader);
}
GL::Mesh _mesh;
std::unique_ptr<GL::Buffer> _vertices, _indices;
Shaders::Phong _shader;
};
/* [Drawable-usage] */
void draw(const Matrix4&, SceneGraph::Camera3D&);
void draw(const Matrix4& transformationMatrix, SceneGraph::Camera3D& camera) {
/* [Drawable-usage-shader] */
Shaders::Flat3D shader;
shader.setTransformationProjectionMatrix(camera.projectionMatrix()*transformationMatrix);
/* [Drawable-usage-shader] */
}
namespace A {
struct MyApplication: Platform::Application {
explicit MyApplication(const Arguments& arguments);
void drawEvent() override;
Scene3D _scene;
SceneGraph::Object<SceneGraph::MatrixTransformation3D>* _cameraObject;
SceneGraph::Camera3D* _camera;
SceneGraph::DrawableGroup3D _drawables;
};
/* [Drawable-usage-camera] */
MyApplication::MyApplication(const Arguments& arguments): Platform::Application{arguments} {
// ...
_cameraObject = new Object3D{&_scene};
_cameraObject->translate(Vector3::zAxis(5.0f));
_camera = new SceneGraph::Camera3D(*_cameraObject);
_camera->setProjectionMatrix(Matrix4::perspectiveProjection(35.0_degf, 1.0f, 0.001f, 100.0f));
}
void MyApplication::drawEvent() {
_camera->draw(_drawables);
// ...
swapBuffers();
}
/* [Drawable-usage-camera] */
}
namespace B {
struct MyApplication: Platform::Application {
explicit MyApplication(const Arguments& arguments);
void drawEvent() override;
SceneGraph::Object<SceneGraph::MatrixTransformation2D>* _cameraObject;
SceneGraph::Camera3D* _camera;
Vector3 _lightPositionRelativeToCamera, _lightColor, _ambientColor;
/* [Drawable-multiple-groups] */
// ...
Shaders::Phong _shader;
SceneGraph::DrawableGroup3D _phongObjects, _transparentObjects;
};
void MyApplication::drawEvent() {
_shader.setProjectionMatrix(_camera->projectionMatrix())
.setLightPosition(_lightPositionRelativeToCamera)
.setLightColor(_lightColor)
.setAmbientColor(_ambientColor);
/* Each drawable sets only unique properties such as transformation matrix
and diffuse color */
_camera->draw(_phongObjects);
GL::Renderer::enable(GL::Renderer::Feature::Blending);
/* Also here */
_camera->draw(_transparentObjects);
GL::Renderer::disable(GL::Renderer::Feature::Blending);
// ...
}
/* [Drawable-multiple-groups] */
}
int main() {
/* [Drawable-usage-instance] */
Scene3D scene;
SceneGraph::DrawableGroup3D drawables;
(new RedCube(&scene, &drawables))
->translate(Vector3::yAxis(-0.3f))
.rotateX(30.0_degf);
// ...
/* [Drawable-usage-instance] */
return 0; /* on iOS SDL redefines main to SDL_main and then return is needed */
}

332
doc/snippets/MagnumSceneGraph.cpp

@ -0,0 +1,332 @@
/*
This file is part of Magnum.
Copyright © 2010, 2011, 2012, 2013, 2014, 2015, 2016, 2017, 2018
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.
*/
#include "Magnum/Math/Matrix4.h"
#include "Magnum/SceneGraph/Animable.h"
#include "Magnum/SceneGraph/AnimableGroup.h"
#include "Magnum/SceneGraph/AbstractGroupedFeature.h"
#include "Magnum/SceneGraph/AbstractTranslationRotation3D.h"
#include "Magnum/SceneGraph/MatrixTransformation2D.h"
#include "Magnum/SceneGraph/MatrixTransformation3D.h"
#include "Magnum/SceneGraph/Camera.h"
#include "Magnum/SceneGraph/Drawable.h"
#include "Magnum/SceneGraph/Object.h"
#include "Magnum/SceneGraph/Scene.h"
using namespace Magnum;
using namespace Magnum::Math::Literals;
/* [AbstractFeature-caching] */
class CachingFeature: public SceneGraph::AbstractFeature3D {
public:
explicit CachingFeature(SceneGraph::AbstractObject3D& object): SceneGraph::AbstractFeature3D{object} {
setCachedTransformations(SceneGraph::CachedTransformation::Absolute);
}
private:
void clean(const Matrix4& absoluteTransformationMatrix) override {
_absolutePosition = absoluteTransformationMatrix.translation();
}
Vector3 _absolutePosition;
};
/* [AbstractFeature-caching] */
/* [AbstractFeature-object-transformation] */
class TransformingFeature: public SceneGraph::AbstractFeature3D {
public:
template<class T> explicit TransformingFeature(SceneGraph::Object<T>& object):
SceneGraph::AbstractFeature3D{object}, _transformation{object} {}
private:
SceneGraph::AbstractTranslationRotation3D& _transformation;
};
/* [AbstractFeature-object-transformation] */
/* [AbstractGroupedFeature-subclassing] */
class Drawable: public SceneGraph::AbstractGroupedFeature3D<Drawable> {
// ...
};
typedef SceneGraph::FeatureGroup3D<Drawable> DrawableGroup;
/* [AbstractGroupedFeature-subclassing] */
/* [Animable-usage-definition] */
typedef SceneGraph::Object<SceneGraph::MatrixTransformation3D> Object3D;
typedef SceneGraph::Scene<SceneGraph::MatrixTransformation3D> Scene3D;
class AnimableObject: public Object3D, public SceneGraph::Animable3D {
public:
AnimableObject(Object3D* parent = nullptr, SceneGraph::AnimableGroup3D* group = nullptr): Object3D{parent}, SceneGraph::Animable3D{*this, group} {
setDuration(10.0f);
// ...
}
private:
void animationStep(Float, Float delta) override {
rotateX(15.0_degf*delta); // rotate at 15 degrees per second
}
};
/* [Animable-usage-definition] */
/* [typedef] */
typedef SceneGraph::Scene<SceneGraph::MatrixTransformation3D> Scene3D;
typedef SceneGraph::Object<SceneGraph::MatrixTransformation3D> Object3D;
/* [typedef] */
/* [Object-typedef] */
typedef SceneGraph::Scene<SceneGraph::MatrixTransformation3D> Scene3D;
typedef SceneGraph::Object<SceneGraph::MatrixTransformation3D> Object3D;
/* [Object-typedef] */
/* [feature-inherit] */
class BouncingBall: public Object3D, SceneGraph::Drawable3D, SceneGraph::Animable3D {
public:
explicit BouncingBall(Object3D* parent):
Object3D{parent}, SceneGraph::Drawable3D{*this}, SceneGraph::Animable3D{*this} {}
private:
// drawing implementation for Drawable feature
void draw(const Matrix4&, SceneGraph::Camera3D&) override;
// animation step for Animable feature
void animationStep(Float, Float) override;
};
/* [feature-inherit] */
/* [caching] */
class CachingObject: public Object3D, SceneGraph::AbstractFeature3D {
public:
explicit CachingObject(Object3D* parent): Object3D{parent}, SceneGraph::AbstractFeature3D{*this} {
setCachedTransformations(SceneGraph::CachedTransformation::Absolute);
}
protected:
void clean(const Matrix4& absoluteTransformation) override {
_absolutePosition = absoluteTransformation.translation();
}
private:
Vector3 _absolutePosition;
};
/* [caching] */
namespace {
/* [transformation] */
class TransformingFeature: public SceneGraph::AbstractFeature3D {
public:
template<class T> explicit TransformingFeature(SceneGraph::Object<T>& object):
SceneGraph::AbstractFeature3D(object), _transformation(object) {}
private:
SceneGraph::AbstractTranslationRotation3D& _transformation;
};
/* [transformation] */
}
int main() {
{
/* [method-chaining] */
Scene3D scene;
Object3D object;
object.setParent(&scene)
.rotateY(15.0_degf)
.translate(Vector3::xAxis(5.0f));
/* [method-chaining] */
}
{
/* [hierarchy] */
Scene3D scene;
Object3D* first = new Object3D{&scene};
Object3D* second = new Object3D{first};
/* [hierarchy] */
static_cast<void>(second);
}
{
/* [hierarchy-addChild] */
Scene3D scene;
Object3D& first = scene.addChild<Object3D>();
Object3D& second = first.addChild<Object3D>();
/* [hierarchy-addChild] */
static_cast<void>(second);
}
{
struct MyFeature {
explicit MyFeature(SceneGraph::AbstractObject3D&, int, int) {}
};
int some{}, params{};
{
/* [feature] */
Object3D o;
new MyFeature{o, some, params};
/* [feature] */
}
{
/* [feature-addFeature] */
Object3D o;
o.addFeature<MyFeature>(some, params);
/* [feature-addFeature] */
}
}
{
/* [construction-order] */
{
Scene3D scene;
Object3D object(&scene);
}
/* [construction-order] */
}
{
/* [construction-order-crash] */
{
Object3D object;
Scene3D scene;
object.setParent(&scene);
} // crash!
/* [construction-order-crash] */
}
{
struct MyFeature {
explicit MyFeature(...) {}
};
{
/* [feature-construction-order] */
class MyObject: public Object3D, MyFeature {
public:
MyObject(Object3D* parent): Object3D(parent), MyFeature{*this} {}
};
/* [feature-construction-order] */
}
{
/* [feature-construction-order-crash] */
class MyObject: MyFeature, public Object3D {
public:
MyObject(Object3D* parent): MyFeature{*this}, Object3D(parent) {} // crash!
};
/* [feature-construction-order-crash] */
}
{
#ifdef __GNUC__
#pragma GCC diagnostic push
#pragma GCC diagnostic ignored "-Wreorder"
#endif
/* [feature-construction-order-crash-destruction] */
class MyObject: MyFeature, public Object3D {
public:
MyObject(Object3D* parent): Object3D(parent), MyFeature(*this) {}
// crash on destruction!
};
/* [feature-construction-order-crash-destruction] */
}
#ifdef __GNUC__
#pragma GCC diagnostic pop
#endif
}
{
SceneGraph::Object<SceneGraph::MatrixTransformation3D> object;
#ifdef __GNUC__
#pragma GCC diagnostic push
#pragma GCC diagnostic ignored "-Wunused-variable"
#endif
/* [AbstractObject-features-range] */
for(SceneGraph::AbstractFeature3D& feature: object.features()) {
// ...
}
/* [AbstractObject-features-range] */
{
/* [Object-children-range] */
Object3D o;
for(Object3D& child: o.children()) {
// ...
}
/* [Object-children-range] */
}
#ifdef __GNUC__
#pragma GCC diagnostic pop
#endif
/* [AbstractObject-features] */
for(SceneGraph::AbstractFeature3D* feature = object.features().first(); feature; feature = feature->nextFeature()) {
// ...
}
/* [AbstractObject-features] */
{
Object3D o;
/* [Object-children] */
for(Object3D* child = o.children().first(); child; child = child->nextSibling()) {
// ...
}
/* [Object-children] */
}
{
/* [Animable-usage] */
Scene3D scene;
SceneGraph::AnimableGroup3D animables;
(new AnimableObject(&scene, &animables))
->setState(SceneGraph::AnimationState::Running);
// ...
/* [Animable-usage] */
}
{
SceneGraph::Object<SceneGraph::MatrixTransformation2D> cameraObject;
/* [Camera-2D] */
SceneGraph::Camera2D camera{cameraObject};
camera.setProjectionMatrix(Matrix3::projection({4.0f/3.0f, 1.0f}))
.setAspectRatioPolicy(SceneGraph::AspectRatioPolicy::Extend);
/* [Camera-2D] */
}
{
SceneGraph::Object<SceneGraph::MatrixTransformation3D> cameraObject;
/* [Camera-3D] */
SceneGraph::Camera3D camera{cameraObject};
camera.setProjectionMatrix(Matrix4::perspectiveProjection(35.0_degf, 1.0f, 0.001f, 100.0f))
.setAspectRatioPolicy(SceneGraph::AspectRatioPolicy::Extend);
/* [Camera-3D] */
}
}
}

27
src/Magnum/SceneGraph/AbstractFeature.h

@ -101,21 +101,7 @@ caching is disabled. You can enable it using @ref setCachedTransformations()
and then implement corresponding cleaning function(s) --- either @ref clean(), and then implement corresponding cleaning function(s) --- either @ref clean(),
@ref cleanInverted() or both. Example: @ref cleanInverted() or both. Example:
@code{.cpp} @snippet MagnumSceneGraph.cpp AbstractFeature-caching
class CachingFeature: public SceneGraph::AbstractFeature3D {
public:
explicit CachingFeature(SceneGraph::AbstractObject3D& object): SceneGraph::AbstractFeature3D{object} {
setCachedTransformations(CachedTransformation::Absolute);
}
private:
void clean(const Matrix4& absoluteTransformationMatrix) override {
_absolutePosition = absoluteTransformationMatrix.translation();
}
Vector3 _absolutePosition;
};
@endcode
Before using the cached value explicitly request object cleaning by calling Before using the cached value explicitly request object cleaning by calling
@cpp object()->setClean() @ce. @cpp object()->setClean() @ce.
@ -127,16 +113,7 @@ know about any used transformation. By using small template trick in the
constructor it is possible to gain access to transformation interface in the constructor it is possible to gain access to transformation interface in the
constructor: constructor:
@code{.cpp} @snippet MagnumSceneGraph.cpp AbstractFeature-object-transformation
class TransformingFeature: public SceneGraph::AbstractFeature3D {
public:
template<class T> explicit TransformingFeature(SceneGraph::Object<T>& object):
SceneGraph::AbstractFeature3D{object}, _transformation{object} {}
private:
SceneGraph::AbstractTranslationRotation3D& _transformation;
};
@endcode
See @ref scenegraph-features-transformation for more detailed information. See @ref scenegraph-features-transformation for more detailed information.

10
src/Magnum/SceneGraph/AbstractGroupedFeature.h

@ -44,15 +44,9 @@ Used together with @ref FeatureGroup.
@section SceneGraph-AbstractGroupedFeature-subclassing Subclassing @section SceneGraph-AbstractGroupedFeature-subclassing Subclassing
Usage is via subclassing the feature using [CRTP](http://en.wikipedia.org/wiki/Curiously_recurring_template_pattern) Usage is via subclassing the feature using [CRTP](http://en.wikipedia.org/wiki/Curiously_recurring_template_pattern)
and typedef'ing @ref FeatureGroup to accept only given type, e.g.: and @cpp typedef @ce'ing @ref FeatureGroup to accept only given type, e.g.:
@code{.cpp} @snippet MagnumSceneGraph.cpp AbstractGroupedFeature-subclassing
class Drawable: public SceneGraph::AbstractGroupedFeature3D<Drawable> {
// ...
};
typedef SceneGraph::FeatureGroup3D<Drawable> DrawableGroup;
@endcode
@section SceneGraph-AbstractGroupedFeature-explicit-specializations Explicit template specializations @section SceneGraph-AbstractGroupedFeature-explicit-specializations Explicit template specializations

13
src/Magnum/SceneGraph/AbstractObject.h

@ -49,22 +49,13 @@ subclass instead. See also @ref scenegraph for more information.
Uses @ref Corrade::Containers::LinkedList for efficient feature management. Uses @ref Corrade::Containers::LinkedList for efficient feature management.
Traversing through the feature list can be done using range-based for: Traversing through the feature list can be done using range-based for:
@code{.cpp} @snippet MagnumSceneGraph.cpp AbstractObject-features-range
AbstractObject3D object;
for(AbstractFeature3D& feature: object.features()) {
// ...
}
@endcode
Or, if you need more flexibility, like in the following code. It is also Or, if you need more flexibility, like in the following code. It is also
possible to go in reverse order using @ref Corrade::Containers::LinkedList::last() possible to go in reverse order using @ref Corrade::Containers::LinkedList::last()
and @ref AbstractFeature::previousFeature(). and @ref AbstractFeature::previousFeature().
@code{.cpp} @snippet MagnumSceneGraph.cpp AbstractObject-features
for(AbstractFeature3D* feature = object.features().first(); feature; feature = feature->nextFeature()) {
// ...
}
@endcode
@section SceneGraph-AbstractObject-explicit-specializations Explicit template specializations @section SceneGraph-AbstractObject-explicit-specializations Explicit template specializations

42
src/Magnum/SceneGraph/Animable.h

@ -65,7 +65,7 @@ MAGNUM_SCENEGRAPH_EXPORT Debug& operator<<(Debug& debug, AnimationState value);
Adds animation feature to object. Each Animable is part of some Adds animation feature to object. Each Animable is part of some
@ref AnimableGroup, which takes care of running the animations. @ref AnimableGroup, which takes care of running the animations.
@section SceneGraph-Animable Usage @section SceneGraph-Animable-usage Usage
First thing is to add @ref Animable feature to some object and implement First thing is to add @ref Animable feature to some object and implement
@ref animationStep(). You can do it conveniently using multiple inheritance @ref animationStep(). You can do it conveniently using multiple inheritance
@ -73,23 +73,7 @@ First thing is to add @ref Animable feature to some object and implement
to implement your animation, the function provides both absolute animation to implement your animation, the function provides both absolute animation
time and time delta. Example: time and time delta. Example:
@code{.cpp} @snippet MagnumSceneGraph.cpp Animable-usage-definition
typedef SceneGraph::Object<SceneGraph::MatrixTransformation3D> Object3D;
typedef SceneGraph::Scene<SceneGraph::MatrixTransformation3D> Scene3D;
class AnimableObject: public Object3D, SceneGraph::Animable3D {
public:
AnimableObject(Object3D* parent = nullptr, SceneGraph::AnimableGroup3D* group = nullptr): Object3D{parent}, SceneGraph::Animable3D{*this, group} {
setDuration(10.0f);
// ...
}
private:
void animationStep(Float time, Float delta) override {
rotateX(15.0_degf*delta); // rotate at 15 degrees per second
}
}
@endcode
Similarly to @ref Drawable feature, there is no way to just animate all the Similarly to @ref Drawable feature, there is no way to just animate all the
objects in the scene. You need to create animable group and use it to control objects in the scene. You need to create animable group and use it to control
@ -99,32 +83,14 @@ The animation is initially in stopped state and without repeat, see
@ref setState(), @ref setRepeated() and @ref setRepeatCount() for more @ref setState(), @ref setRepeated() and @ref setRepeatCount() for more
information. information.
@code{.cpp} @snippet MagnumSceneGraph.cpp Animable-usage
Scene3D scene;
SceneGraph::AnimableGroup3D animables;
(new AnimableObject(&scene, &animables))
->setState(SceneGraph::AnimationState::Running);
// ...
@endcode
Animation step is performed by calling @ref AnimableGroup::step() in your draw Animation step is performed by calling @ref AnimableGroup::step() in your draw
event implementation. The function expects absolute time from relative to some event implementation. The function expects absolute time from relative to some
fixed point in the past and time delta (i.e. duration of the frame). You can fixed point in the past and time delta (i.e. duration of the frame). You can
use @ref Timeline for that, see its documentation for more information. use @ref Timeline for that, see its documentation for more information.
@code{.cpp} @snippet MagnumSceneGraph-gl.cpp Animable-usage-timeline
Timeline timeline;
timeline.start();
void MyApplication::drawEvent() {
animables.step(timeline.lastFrameTime(), timeline.lastFrameDuration());
// ...
timeline.nextFrame();
}
@endcode
@section SceneGraph-Animable-multiple-groups Using multiple animable groups to improve performance @section SceneGraph-Animable-multiple-groups Using multiple animable groups to improve performance

22
src/Magnum/SceneGraph/Camera.h

@ -65,19 +65,11 @@ ratio correction.
Common setup example for 2D scenes: Common setup example for 2D scenes:
@code{.cpp} @snippet MagnumSceneGraph.cpp Camera-2D
SceneGraph::Camera2D camera{&cameraObject};
camera.setProjectionMatrix(Matrix3::projection({4.0f/3.0f, 1.0f}))
.setAspectRatioPolicy(SceneGraph::AspectRatioPolicy::Extend);
@endcode
Common setup example for 3D scenes: Common setup example for 3D scenes:
@code{.cpp} @snippet MagnumSceneGraph.cpp Camera-3D
SceneGraph::Camera3D camera{&cameraObject};
camera.setProjectionMatrix(Matrix3::perspectiveProjection(35.0_degf, 1.0f, 0.001f, 100.0f))
.setAspectRatioPolicy(SceneGraph::AspectRatioPolicy::Extend);
@endcode
@section SceneGraph-Camera-explicit-specializations Explicit template specializations @section SceneGraph-Camera-explicit-specializations Explicit template specializations
@ -107,7 +99,7 @@ template<UnsignedInt dimensions, class T> class Camera: public AbstractFeature<d
#ifndef DOXYGEN_GENERATING_OUTPUT #ifndef DOXYGEN_GENERATING_OUTPUT
/* This is here to avoid ambiguity with deleted copy constructor when /* This is here to avoid ambiguity with deleted copy constructor when
passing `*this` from class subclassing both Camera and AbstractObject */ passing `*this` from class subclassing both Camera and AbstractObject */
template<class U, class = typename std::enable_if<std::is_base_of<AbstractObject<dimensions, T>, U>::value>::type> Camera(U& object): Camera<dimensions, T>{static_cast<AbstractObject<dimensions, T>&>(object)} {} template<class U, class = typename std::enable_if<std::is_base_of<AbstractObject<dimensions, T>, U>::value>::type> explicit Camera(U& object): Camera<dimensions, T>{static_cast<AbstractObject<dimensions, T>&>(object)} {}
#endif #endif
~Camera(); ~Camera();
@ -168,17 +160,13 @@ template<UnsignedInt dimensions, class T> class Camera: public AbstractFeature<d
* to floating-point coordinates on near XY plane with origin at camera * to floating-point coordinates on near XY plane with origin at camera
* position and Y up can be done using the following snippet: * position and Y up can be done using the following snippet:
* *
* @code{.cpp} * @snippet MagnumSceneGraph-gl.cpp Camera-projectionSize
* Vector2 position = (Vector2{event.position()}/defaultFramebuffer.viewport().size() - Vector2{0.5f})*Vector2::yScale(-1.0f)*camera.projectionSize();
* @endcode
* *
* This is position relative to camera transformation, getting absolute * This is position relative to camera transformation, getting absolute
* transformation in 2D scene can be done for example using * transformation in 2D scene can be done for example using
* @ref SceneGraph::Object::absoluteTransformation(): * @ref SceneGraph::Object::absoluteTransformation():
* *
* @code{.cpp} * @snippet MagnumSceneGraph-gl.cpp Camera-projectionSize-absolute
* Vector2 absolutePosition = cameraObject->absoluteTransformation().transformPoint(position);
* @endcode
* *
* @see @ref projectionMatrix() * @see @ref projectionMatrix()
*/ */

86
src/Magnum/SceneGraph/Drawable.h

@ -47,31 +47,7 @@ First thing is to add @ref Drawable feature to some object and implement
(see @ref scenegraph-features for introduction). Example drawable object that (see @ref scenegraph-features for introduction). Example drawable object that
draws blue sphere: draws blue sphere:
@code{.cpp} @snippet MagnumSceneGraph-gl.cpp Drawable-usage
typedef SceneGraph::Object<SceneGraph::MatrixTransformation3D> Object3D;
typedef SceneGraph::Scene<SceneGraph::MatrixTransformation3D> Scene3D;
class RedCube: public Object3D, public SceneGraph::Drawable3D {
public:
explicit RedCube(Object3D* parent, SceneGraph::DrawableGroup3D* group): Object3D{parent}, SceneGraph::Drawable3D{*this, group} {
std::tie(_mesh, _vertices, _indices) = MeshTools::compile(Primitives::UVSphere::solid(16, 32), BufferUsage::StaticDraw);
}
private:
void draw(const Matrix4& transformationMatrix, Camera3D& camera) override {
_shader.setDiffuseColor(Color3::fromHSV(216.0_degf, 0.85f, 1.0f))
.setLightPosition(camera.cameraMatrix().transformPoint({5.0f, 5.0f, 7.0f}))
.setTransformationMatrix(transformationMatrix)
.setNormalMatrix(transformationMatrix.rotation())
.setProjectionMatrix(camera.projectionMatrix());
_mesh.draw(_shader);
}
Mesh _mesh;
std::unique_ptr<Buffer> _vertices, _indices;
Shaders::Phong _shader;
}
@endcode
The @p transformationMatrix parameter in @ref draw() function contains The @p transformationMatrix parameter in @ref draw() function contains
transformation of the object (to which the drawable is attached) relative to transformation of the object (to which the drawable is attached) relative to
@ -83,26 +59,14 @@ that case you need to combine the two matrices manually like in the following
code. Some shaders have additional requirements for various transformation code. Some shaders have additional requirements for various transformation
matrices, see their respective documentation for details. matrices, see their respective documentation for details.
@code{.cpp} @snippet MagnumSceneGraph-gl.cpp Drawable-usage-shader
Shaders::Flat3D shader;
shader.setTransformationProjectionMatrix(camera.projectionMatrix()*transformationMatrix);
@endcode
There is no way to just draw all the drawables in the scene, you need to create There is no way to just draw all the drawables in the scene, you need to create
some drawable group and add the drawable objects to both the scene and the some drawable group and add the drawable objects to both the scene and the
group. You can also use @ref DrawableGroup::add() and group. You can also use @ref DrawableGroup::add() and
@ref DrawableGroup::remove() instead of passing the group in the constructor. @ref DrawableGroup::remove() instead of passing the group in the constructor.
@code{.cpp} @snippet MagnumSceneGraph-gl.cpp Drawable-usage-instance
Scene3D scene;
SceneGraph::DrawableGroup3D drawables;
(new RedCube(&scene, &drawables))
->translate(Vector3::yAxis(-0.3f))
.rotateX(30.0_degf);
// ...
@endcode
The last thing you need is camera attached to some object (thus using its The last thing you need is camera attached to some object (thus using its
transformation). Using the camera and the drawable group you can perform transformation). Using the camera and the drawable group you can perform
@ -110,22 +74,7 @@ drawing in your @ref Platform::Sdl2Application::drawEvent() "drawEvent()"
implementation. See @ref Camera2D and @ref Camera3D documentation for more implementation. See @ref Camera2D and @ref Camera3D documentation for more
information. information.
@code{.cpp} @snippet MagnumSceneGraph-gl.cpp Drawable-usage-camera
auto cameraObject = new Object3D(&scene);
cameraObject->translate(Vector3::zAxis(5.0f));
auto camera = new SceneGraph::Camera3D(&cameraObject);
camera->setProjectionMatrix(Matrix4::perspectiveProjection(35.0_degf, 1.0f, 0.001f, 100.0f));
// ...
void MyApplication::drawEvent() {
camera->draw(drawables);
// ...
swapBuffers();
}
@endcode
@section SceneGraph-Drawable-multiple-groups Using multiple drawable groups to improve performance @section SceneGraph-Drawable-multiple-groups Using multiple drawable groups to improve performance
@ -135,30 +84,7 @@ setup etc into one group, then put all transparent into another and set common
parameters once for whole group instead of setting them again in each parameters once for whole group instead of setting them again in each
@ref draw() implementation. Example: @ref draw() implementation. Example:
@code{.cpp} @snippet MagnumSceneGraph-gl.cpp Drawable-multiple-groups
Shaders::PhongShader shader;
SceneGraph::DrawableGroup3D phongObjects, transparentObjects;
void MyApplication::drawEvent() {
shader.setProjectionMatrix(camera->projectionMatrix())
.setLightPosition(lightPositionRelativeToCamera)
.setLightColor(lightColor)
.setAmbientColor(ambientColor);
// Each drawable sets only unique properties such as transformation matrix
// and diffuse color
camera.draw(phongObjects);
Renderer::enable(Renderer::Feature::Blending);
// Also here
camera.draw(transparentObjects);
Renderer::disable(Renderer::Feature::Blending);
// ...
}
@endcode
@section SceneGraph-Drawable-explicit-specializations Explicit template specializations @section SceneGraph-Drawable-explicit-specializations Explicit template specializations
@ -188,7 +114,7 @@ template<UnsignedInt dimensions, class T> class Drawable: public AbstractGrouped
/** /**
* @brief Group containing this drawable * @brief Group containing this drawable
* *
* If the drawable doesn't belong to any group, returns `nullptr`. * If the drawable doesn't belong to any group, returns @cpp nullptr @ce.
*/ */
DrawableGroup<dimensions, T>* drawables() { DrawableGroup<dimensions, T>* drawables() {
return AbstractGroupedFeature<dimensions, Drawable<dimensions, T>, T>::group(); return AbstractGroupedFeature<dimensions, Drawable<dimensions, T>, T>::group();

18
src/Magnum/SceneGraph/Object.h

@ -59,30 +59,18 @@ for introduction.
Common usage is to typedef @ref Object with desired transformation type to save Common usage is to typedef @ref Object with desired transformation type to save
unnecessary typing later, along with @ref Scene and possibly other types, e.g.: unnecessary typing later, along with @ref Scene and possibly other types, e.g.:
@code{.cpp} @snippet MagnumSceneGraph.cpp Object-typedef
typedef SceneGraph::Scene<SceneGraph::MatrixTransformation3D> Scene3D;
typedef SceneGraph::Object<SceneGraph::MatrixTransformation3D> Object3D;
@endcode
Uses @ref Corrade::Containers::LinkedList for efficient hierarchy management. Uses @ref Corrade::Containers::LinkedList for efficient hierarchy management.
Traversing through the list of child objects can be done using range-based for: Traversing through the list of child objects can be done using range-based for:
@code{.cpp} @snippet MagnumSceneGraph.cpp Object-children-range
Object3D o;
for(Object3D& child: o.children()) {
// ...
}
@endcode
Or, if you need more flexibility, like in the following code. It is also Or, if you need more flexibility, like in the following code. It is also
possible to go in reverse order using @ref Corrade::Containers::LinkedList::last() possible to go in reverse order using @ref Corrade::Containers::LinkedList::last()
and @ref previousSibling(). and @ref previousSibling().
@code{.cpp} @snippet MagnumSceneGraph.cpp Object-children
for(Object3D* child = o->children().first(); child; child = child->nextSibling()) {
// ...
}
@endcode
@section SceneGraph-Object-explicit-specializations Explicit template specializations @section SceneGraph-Object-explicit-specializations Explicit template specializations

Loading…
Cancel
Save