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
desired transformation type to save unnecessary typing later:
@code{.cpp}
typedef SceneGraph::Scene<SceneGraph::MatrixTransformation3D> Scene3D;
typedef SceneGraph::Object<SceneGraph::MatrixTransformation3D> Object3D;
@endcode
@snippet MagnumSceneGraph.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)
@ -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
chaining:
@code{.cpp}
Scene3D scene;
Object3D object;
object.setParent(&scene)
.rotateY(15.0_degf)
.translate(Vector3::xAxis(5.0f));
@endcode
@snippet MagnumSceneGraph.cpp method-chaining
@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
@ref SceneGraph::Object::children().
@code{.cpp}
Scene3D scene;
Object3D* first = new Object3D{&scene};
Object3D* second = new Object3D{first};
@endcode
@snippet MagnumSceneGraph.cpp hierarchy
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"
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 `new` call in the code above:
@code{.cpp}
Scene3D scene;
instead of the naked @cpp new @ce call in the code above:
Object3D& first = scene.addChild<Object3D>();
Object3D& second = first.addChild<Object3D>();
@endcode
@snippet MagnumSceneGraph.cpp hierarchy-addChild
@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
accessible through @ref SceneGraph::Object::features().
@code{.cpp}
Object3D& o;
new MyFeature{o, ...};
@endcode
@snippet MagnumSceneGraph.cpp feature
Some features are passive, some active. Passive features can be just added to
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
code overly verbose). Simplified example:
@code{.cpp}
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
@snippet MagnumSceneGraph.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
@ -227,10 +192,7 @@ for information about possible issues. Also, there is a
@ref SceneGraph::AbstractObject::addFeature() counterpart to
@ref SceneGraph::Object::addChild():
@code{.cpp}
Object3D& o;
o.addFeature<MyFeature>(...);
@endcode
@snippet MagnumSceneGraph.cpp feature-addFeature
@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()
and then implement corresponding cleaning function(s):
@code{.cpp}
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
@snippet MagnumSceneGraph.cpp caching
When you need to use the cached value, you can explicitly request the cleanup
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
constructor parameter using small trick:
@code{.cpp}
class TransformingFeature: public SceneGraph::AbstractFeature3D {
public:
template<class T> TransformingFeature(SceneGraph::Object<T>& object):
SceneGraph::AbstractFeature3D(object), transformation(object) {}
private:
SceneGraph::AbstractTranslationRotation3D& transformation;
};
@endcode
@snippet MagnumSceneGraph.cpp transformation
If we take for example @ref SceneGraph::Object "SceneGraph::Object<MatrixTransformation3D>",
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
not a problem:
@code{.cpp}
{
Scene3D scene;
Object3D object(&scene);
}
@endcode
@snippet MagnumSceneGraph.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 `scene`
object later. However, if their order is swapped, it will cause problems:
@code{.cpp}
{
Object3D object;
Scene3D scene;
object.setParent(&scene);
} // crash!
@endcode
@snippet MagnumSceneGraph.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
@ -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
inherited after the Object class:
@code{.cpp}
class MyObject: public Object3D, MyFeature {
public:
MyObject(Object3D* parent): Object3D(parent), MyFeature(*this) {}
};
@endcode
@snippet MagnumSceneGraph.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
@ -392,12 +313,7 @@ from `Object3D`'s list, then `Object3D` destructor.
However, if we would inherit `MyFeature` first, it will cause problems:
@code{.cpp}
class MyObject: MyFeature, public Object3D {
public:
MyObject(Object3D* parent): MyFeature(*this), Object3D(parent) {} // crash!
};
@endcode
@snippet MagnumSceneGraph.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,
@ -407,14 +323,7 @@ invisible.
If we would construct them in swapped order (if it is even possible), it
wouldn't help either:
@code{.cpp}
class MyObject: MyFeature, public Object3D {
public:
MyObject(Object3D* parent): Object3D(parent), MyFeature(*this) {}
// crash on destruction!
};
@endcode
@snippet MagnumSceneGraph.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

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")
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)
add_executable(getting-started getting-started.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(),
@ref cleanInverted() or both. Example:
@code{.cpp}
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
@snippet MagnumSceneGraph.cpp AbstractFeature-caching
Before using the cached value explicitly request object cleaning by calling
@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:
@code{.cpp}
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
@snippet MagnumSceneGraph.cpp AbstractFeature-object-transformation
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
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}
class Drawable: public SceneGraph::AbstractGroupedFeature3D<Drawable> {
// ...
};
typedef SceneGraph::FeatureGroup3D<Drawable> DrawableGroup;
@endcode
@snippet MagnumSceneGraph.cpp AbstractGroupedFeature-subclassing
@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.
Traversing through the feature list can be done using range-based for:
@code{.cpp}
AbstractObject3D object;
for(AbstractFeature3D& feature: object.features()) {
// ...
}
@endcode
@snippet MagnumSceneGraph.cpp AbstractObject-features-range
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()
and @ref AbstractFeature::previousFeature().
@code{.cpp}
for(AbstractFeature3D* feature = object.features().first(); feature; feature = feature->nextFeature()) {
// ...
}
@endcode
@snippet MagnumSceneGraph.cpp AbstractObject-features
@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
@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
@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
time and time delta. Example:
@code{.cpp}
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
@snippet MagnumSceneGraph.cpp Animable-usage-definition
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
@ -99,32 +83,14 @@ The animation is initially in stopped state and without repeat, see
@ref setState(), @ref setRepeated() and @ref setRepeatCount() for more
information.
@code{.cpp}
Scene3D scene;
SceneGraph::AnimableGroup3D animables;
(new AnimableObject(&scene, &animables))
->setState(SceneGraph::AnimationState::Running);
// ...
@endcode
@snippet MagnumSceneGraph.cpp Animable-usage
Animation step is performed by calling @ref AnimableGroup::step() in your draw
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
use @ref Timeline for that, see its documentation for more information.
@code{.cpp}
Timeline timeline;
timeline.start();
void MyApplication::drawEvent() {
animables.step(timeline.lastFrameTime(), timeline.lastFrameDuration());
// ...
timeline.nextFrame();
}
@endcode
@snippet MagnumSceneGraph-gl.cpp Animable-usage-timeline
@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:
@code{.cpp}
SceneGraph::Camera2D camera{&cameraObject};
camera.setProjectionMatrix(Matrix3::projection({4.0f/3.0f, 1.0f}))
.setAspectRatioPolicy(SceneGraph::AspectRatioPolicy::Extend);
@endcode
@snippet MagnumSceneGraph.cpp Camera-2D
Common setup example for 3D scenes:
@code{.cpp}
SceneGraph::Camera3D camera{&cameraObject};
camera.setProjectionMatrix(Matrix3::perspectiveProjection(35.0_degf, 1.0f, 0.001f, 100.0f))
.setAspectRatioPolicy(SceneGraph::AspectRatioPolicy::Extend);
@endcode
@snippet MagnumSceneGraph.cpp Camera-3D
@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
/* This is here to avoid ambiguity with deleted copy constructor when
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
~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
* position and Y up can be done using the following snippet:
*
* @code{.cpp}
* Vector2 position = (Vector2{event.position()}/defaultFramebuffer.viewport().size() - Vector2{0.5f})*Vector2::yScale(-1.0f)*camera.projectionSize();
* @endcode
* @snippet MagnumSceneGraph-gl.cpp Camera-projectionSize
*
* This is position relative to camera transformation, getting absolute
* transformation in 2D scene can be done for example using
* @ref SceneGraph::Object::absoluteTransformation():
*
* @code{.cpp}
* Vector2 absolutePosition = cameraObject->absoluteTransformation().transformPoint(position);
* @endcode
* @snippet MagnumSceneGraph-gl.cpp Camera-projectionSize-absolute
*
* @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
draws blue sphere:
@code{.cpp}
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
@snippet MagnumSceneGraph-gl.cpp Drawable-usage
The @p transformationMatrix parameter in @ref draw() function contains
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
matrices, see their respective documentation for details.
@code{.cpp}
Shaders::Flat3D shader;
shader.setTransformationProjectionMatrix(camera.projectionMatrix()*transformationMatrix);
@endcode
@snippet MagnumSceneGraph-gl.cpp Drawable-usage-shader
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
group. You can also use @ref DrawableGroup::add() and
@ref DrawableGroup::remove() instead of passing the group in the constructor.
@code{.cpp}
Scene3D scene;
SceneGraph::DrawableGroup3D drawables;
(new RedCube(&scene, &drawables))
->translate(Vector3::yAxis(-0.3f))
.rotateX(30.0_degf);
// ...
@endcode
@snippet MagnumSceneGraph-gl.cpp Drawable-usage-instance
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
@ -110,22 +74,7 @@ drawing in your @ref Platform::Sdl2Application::drawEvent() "drawEvent()"
implementation. See @ref Camera2D and @ref Camera3D documentation for more
information.
@code{.cpp}
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
@snippet MagnumSceneGraph-gl.cpp Drawable-usage-camera
@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
@ref draw() implementation. Example:
@code{.cpp}
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
@snippet MagnumSceneGraph-gl.cpp Drawable-multiple-groups
@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
*
* 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() {
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
unnecessary typing later, along with @ref Scene and possibly other types, e.g.:
@code{.cpp}
typedef SceneGraph::Scene<SceneGraph::MatrixTransformation3D> Scene3D;
typedef SceneGraph::Object<SceneGraph::MatrixTransformation3D> Object3D;
@endcode
@snippet MagnumSceneGraph.cpp Object-typedef
Uses @ref Corrade::Containers::LinkedList for efficient hierarchy management.
Traversing through the list of child objects can be done using range-based for:
@code{.cpp}
Object3D o;
for(Object3D& child: o.children()) {
// ...
}
@endcode
@snippet MagnumSceneGraph.cpp Object-children-range
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()
and @ref previousSibling().
@code{.cpp}
for(Object3D* child = o->children().first(); child; child = child->nextSibling()) {
// ...
}
@endcode
@snippet MagnumSceneGraph.cpp Object-children
@section SceneGraph-Object-explicit-specializations Explicit template specializations

Loading…
Cancel
Save