diff --git a/doc/scenegraph.dox b/doc/scenegraph.dox index 23dc73db4..0f9752d2c 100644 --- a/doc/scenegraph.dox +++ b/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 Scene3D; -typedef SceneGraph::Object 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& second = first.addChild(); -@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(...); -@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 TransformingFeature(SceneGraph::Object& 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", 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 diff --git a/doc/snippets/CMakeLists.txt b/doc/snippets/CMakeLists.txt index 36328357f..3bd9c70e5 100644 --- a/doc/snippets/CMakeLists.txt +++ b/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) diff --git a/doc/snippets/MagnumSceneGraph-gl.cpp b/doc/snippets/MagnumSceneGraph-gl.cpp new file mode 100644 index 000000000..d1600b679 --- /dev/null +++ b/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š + + 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* 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(absolutePosition); +} + +/* [Drawable-usage] */ +typedef SceneGraph::Object Object3D; +typedef SceneGraph::Scene 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 _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* _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* _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 */ +} diff --git a/doc/snippets/MagnumSceneGraph.cpp b/doc/snippets/MagnumSceneGraph.cpp new file mode 100644 index 000000000..3f5ac596f --- /dev/null +++ b/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š + + 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 explicit TransformingFeature(SceneGraph::Object& object): + SceneGraph::AbstractFeature3D{object}, _transformation{object} {} + + private: + SceneGraph::AbstractTranslationRotation3D& _transformation; +}; +/* [AbstractFeature-object-transformation] */ + +/* [AbstractGroupedFeature-subclassing] */ +class Drawable: public SceneGraph::AbstractGroupedFeature3D { + // ... +}; + +typedef SceneGraph::FeatureGroup3D DrawableGroup; +/* [AbstractGroupedFeature-subclassing] */ + +/* [Animable-usage-definition] */ +typedef SceneGraph::Object Object3D; +typedef SceneGraph::Scene 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 Scene3D; +typedef SceneGraph::Object Object3D; +/* [typedef] */ + +/* [Object-typedef] */ +typedef SceneGraph::Scene Scene3D; +typedef SceneGraph::Object 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 explicit TransformingFeature(SceneGraph::Object& 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(second); +} + +{ +/* [hierarchy-addChild] */ +Scene3D scene; + +Object3D& first = scene.addChild(); +Object3D& second = first.addChild(); +/* [hierarchy-addChild] */ +static_cast(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(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 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 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 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] */ +} + +} + +} diff --git a/src/Magnum/SceneGraph/AbstractFeature.h b/src/Magnum/SceneGraph/AbstractFeature.h index 2802aa604..808444d8a 100644 --- a/src/Magnum/SceneGraph/AbstractFeature.h +++ b/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 explicit TransformingFeature(SceneGraph::Object& 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. diff --git a/src/Magnum/SceneGraph/AbstractGroupedFeature.h b/src/Magnum/SceneGraph/AbstractGroupedFeature.h index eb3e02e11..49962177e 100644 --- a/src/Magnum/SceneGraph/AbstractGroupedFeature.h +++ b/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 { - // ... -}; - -typedef SceneGraph::FeatureGroup3D DrawableGroup; -@endcode +@snippet MagnumSceneGraph.cpp AbstractGroupedFeature-subclassing @section SceneGraph-AbstractGroupedFeature-explicit-specializations Explicit template specializations diff --git a/src/Magnum/SceneGraph/AbstractObject.h b/src/Magnum/SceneGraph/AbstractObject.h index b3da4c601..dafa4d2af 100644 --- a/src/Magnum/SceneGraph/AbstractObject.h +++ b/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 diff --git a/src/Magnum/SceneGraph/Animable.h b/src/Magnum/SceneGraph/Animable.h index 85d5ff8b8..3d0854538 100644 --- a/src/Magnum/SceneGraph/Animable.h +++ b/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 Object3D; -typedef SceneGraph::Scene 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 diff --git a/src/Magnum/SceneGraph/Camera.h b/src/Magnum/SceneGraph/Camera.h index 58523d1c6..8ed5c9898 100644 --- a/src/Magnum/SceneGraph/Camera.h +++ b/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 class Camera: public AbstractFeature, U>::value>::type> Camera(U& object): Camera{static_cast&>(object)} {} + template, U>::value>::type> explicit Camera(U& object): Camera{static_cast&>(object)} {} #endif ~Camera(); @@ -168,17 +160,13 @@ template class Camera: public AbstractFeatureabsoluteTransformation().transformPoint(position); - * @endcode + * @snippet MagnumSceneGraph-gl.cpp Camera-projectionSize-absolute * * @see @ref projectionMatrix() */ diff --git a/src/Magnum/SceneGraph/Drawable.h b/src/Magnum/SceneGraph/Drawable.h index 9723e3fb5..b6d64ea25 100644 --- a/src/Magnum/SceneGraph/Drawable.h +++ b/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 Object3D; -typedef SceneGraph::Scene 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 _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 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* drawables() { return AbstractGroupedFeature, T>::group(); diff --git a/src/Magnum/SceneGraph/Object.h b/src/Magnum/SceneGraph/Object.h index cd0eadd41..2adbde0f9 100644 --- a/src/Magnum/SceneGraph/Object.h +++ b/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 Scene3D; -typedef SceneGraph::Object 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