#include <Magnum/SceneGraph/Drawable.h>
template<UnsignedInt dimensions, class T>
Drawable class
Drawable.
Adds drawing functionality to the object. Each Drawable is part of some DrawableGroup and the whole group can be drawn with particular camera using Camera::
Subclassing
The class is used via subclassing and implementing the draw() function. The simplest option is to do it via single inheritance. Example drawable object that draws a blue sphere:
typedef SceneGraph::Object<SceneGraph::MatrixTransformation3D> Object3D; typedef SceneGraph::Scene<SceneGraph::MatrixTransformation3D> Scene3D; class RedCubeDrawable: public SceneGraph::Drawable3D { public: explicit RedCubeDrawable(Object3D& object, SceneGraph::DrawableGroup3D* group): SceneGraph::Drawable3D{object, group} { _mesh = MeshTools::compile(Primitives::cubeSolid()); } private: void draw(const Matrix4& transformationMatrix, SceneGraph::Camera3D& camera) override { using namespace Math::Literals; _shader.setDiffuseColor(0xa5c9ea_rgbf) .setTransformationMatrix(transformationMatrix) .setNormalMatrix(transformationMatrix.normalMatrix()) .setProjectionMatrix(camera.projectionMatrix()) .draw(_mesh); } GL::Mesh _mesh; Shaders::PhongGL _shader; };
For brevity the class has its own shader and mesh instance and a hardcoded light position, usually you'll have them stored in a central location, shared by multiple objects, and pass only references around.
The transformationMatrix
parameter in the draw() function contains transformation of the object (to which the drawable is attached) relative to camera
. The camera contains the projection matrix. Some shaders (like the Shaders::
Shaders::FlatGL3D shader; shader.setTransformationProjectionMatrix( camera.projectionMatrix()*transformationMatrix);
Drawing the scene
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 DrawableGroup::
Scene3D scene; SceneGraph::DrawableGroup3D drawables; Object3D* redCube = new Object3D{&scene}; (*redCube) .translate(Vector3::yAxis(-0.3f)) .rotateX(30.0_degf); new RedCubeDrawable{*redCube, &drawables}; // ...
The last thing you need is a camera attached to some object (thus using its transformation). With the camera and the drawable group you can perform drawing in your drawEvent() implementation. See Camera2D and Camera3D documentation for more information.
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(); }
Using multiple inheritance
Besides single inheritance, it's also possible to derive your class from both Object and Drawable. This for example allows you to directly access object transformation and parent/child relationship from within the drawable object.
class RedCube: public Object3D, public SceneGraph::Drawable3D { public: explicit RedCube(Object3D* parent, SceneGraph::DrawableGroup3D* group): Object3D{parent}, SceneGraph::Drawable3D{*this, group} { _mesh = MeshTools::compile(Primitives::cubeSolid()); } private: void draw(const Matrix4& transformationMatrix, SceneGraph::Camera3D& camera) override; GL::Mesh _mesh; Shaders::PhongGL _shader; };
The draw() implementation is the same as in the above snippet. Note that, in contrast to the single inheritance case, where the Drawable constructor got the holder object
, here we pass it *this
, because this class is both the holder object and the drawable. Instantiating the drawable object is then done in a single step:
(new RedCube(&scene, &drawables)) ->translate(Vector3::yAxis(-0.3f)) .rotateX(30.0_degf);
Using multiple drawable groups to improve performance
You can organize your drawables to multiple groups to minimize OpenGL state changes — for example put all objects using the same shader, the same light 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 draw() implementation. Example:
// ... Shaders::PhongGL _shader; SceneGraph::DrawableGroup3D _phongObjects, _transparentObjects; }; void MyApplication::drawEvent() { _shader.setProjectionMatrix(_camera->projectionMatrix()) .setLightPositions({_lightPositionRelativeToCamera}) .setLightColors({_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); // ... }
Custom draw order and object culling
By default all contents of a drawable group are drawn, in the order they were added. In some cases you may want to draw them in a different order (for example to have correctly sorted transparent objects) or draw just a subset (for example to cull invisible objects away). That can be achieved using Camera::
std::vector<std::pair<std::reference_wrapper<SceneGraph::Drawable3D>, Matrix4>> drawableTransformations = camera.drawableTransformations(drawableGroup); std::sort(drawableTransformations.begin(), drawableTransformations.end(), [](const std::pair<std::reference_wrapper<SceneGraph::Drawable3D>, Matrix4>& a, const std::pair<std::reference_wrapper<SceneGraph::Drawable3D>, Matrix4>& b) { return a.second.translation().z() < b.second.translation().z(); }); camera.draw(drawableTransformations);
Another use case is object-level culling — assuming each drawable instance provides an absolute AABB, one can calculate the transformations, cull them via e.g. Math::
struct CullableDrawable3D: SceneGraph::Drawable3D { Range3D aabb; /* Relative to world origin */ … }; /* Camera frustum relative to world origin */ auto frustum = Frustum::fromMatrix(camera.projectionMatrix()*camera.cameraMatrix()); /* Erase all items that don't pass the frustum check */ std::vector<std::pair<std::reference_wrapper<SceneGraph::Drawable3D>, Matrix4>> drawableTransformations = camera.drawableTransformations(drawableGroup); drawableTransformations.erase(std::remove_if( drawableTransformations.begin(), drawableTransformations.end(), [&](const std::pair<std::reference_wrapper<SceneGraph::Drawable3D>, Matrix4>& a) { Range3D aabb = static_cast<CullableDrawable3D&>(a.first.get()).aabb; return !Math::Intersection::rangeFrustum(aabb, frustum); }), drawableTransformations.end()); /* Draw just the visible part */ camera.draw(drawableTransformations);
Explicit template specializations
The following specializations are explicitly compiled into SceneGraph library. For other specializations (e.g. using Double type) you have to use Drawable.hpp implementation file to avoid linker errors. See also Template headers and implementation files for more information.
Base classes
-
template<UnsignedInt dimensions, class Derived, class T>class AbstractGroupedFeature<dimensions, Drawable<dimensions, T>, T>
- Base for grouped features.
Constructors, destructors, conversion operators
- Drawable(AbstractObject<dimensions, T>& object, DrawableGroup<dimensions, T>* drawables = nullptr) explicit
- Constructor.
Public functions
- auto drawables() -> DrawableGroup<dimensions, T>*
- Group containing this drawable.
- auto drawables() const -> const DrawableGroup<dimensions, T>*
- void draw(const MatrixTypeFor<dimensions, T>& transformationMatrix, Camera<dimensions, T>& camera) pure virtual
- Draw the object using given camera.
Function documentation
template<UnsignedInt dimensions, class T>
Magnum:: SceneGraph:: Drawable<dimensions, T>:: Drawable(AbstractObject<dimensions, T>& object,
DrawableGroup<dimensions, T>* drawables = nullptr) explicit
Constructor.
Parameters | |
---|---|
object | Object this drawable belongs to |
drawables | Group this drawable belongs to |
Adds the feature to the object and also to the group, if specified. Otherwise you can use DrawableGroup::
template<UnsignedInt dimensions, class T>
DrawableGroup<dimensions, T>* Magnum:: SceneGraph:: Drawable<dimensions, T>:: drawables()
Group containing this drawable.
If the drawable doesn't belong to any group, returns nullptr
.
template<UnsignedInt dimensions, class T>
const DrawableGroup<dimensions, T>* Magnum:: SceneGraph:: Drawable<dimensions, T>:: drawables() const
This is an overloaded member function, provided for convenience. It differs from the above function only in what argument(s) it accepts.
template<UnsignedInt dimensions, class T>
void Magnum:: SceneGraph:: Drawable<dimensions, T>:: draw(const MatrixTypeFor<dimensions, T>& transformationMatrix,
Camera<dimensions, T>& camera) pure virtual
Draw the object using given camera.
Parameters | |
---|---|
transformationMatrix | Object transformation relative to camera |
camera | Camera |
Projection matrix can be retrieved from Camera::