Primitives
Importing mesh data, 3D transformations and input handling.
This example shows how to easily create a GL::
Setting up
The new includes are for a Primitives::
#include <Magnum/GL/DefaultFramebuffer.h> #include <Magnum/GL/Mesh.h> #include <Magnum/GL/Renderer.h> #include <Magnum/Math/Color.h> #include <Magnum/Math/Matrix4.h> #include <Magnum/MeshTools/Compile.h> #include <Magnum/Platform/Sdl2Application.h> #include <Magnum/Primitives/Cube.h> #include <Magnum/Shaders/PhongGL.h> #include <Magnum/Trade/MeshData.h>
This time, for user interactivity we'll need to implement pointer event handlers and store transformation & projection Matrix4 plus the current Color3.
class PrimitivesExample: public Platform::Application { public: explicit PrimitivesExample(const Arguments& arguments); private: void drawEvent() override; void pointerReleaseEvent(PointerEvent& event) override; void pointerMoveEvent(PointerMoveEvent& event) override; GL::Mesh _mesh; Shaders::PhongGL _shader; Matrix4 _transformation, _projection; Color3 _color; };
Because we are displaying a 3D scene, we need to enable depth test to have the cube rendered in proper Z order. The GL::
PrimitivesExample::PrimitivesExample(const Arguments& arguments): Platform::Application{arguments, Configuration{} .setTitle("Magnum Primitives Example")} { GL::Renderer::enable(GL::Renderer::Feature::DepthTest); GL::Renderer::enable(GL::Renderer::Feature::FaceCulling);
Preparing the mesh
Perhaps the biggest leap from previous examples is that setting up the mesh is now just a simple oneliner:
_mesh = MeshTools::compile(Primitives::cubeSolid());
The main responsibility here lies on Trade::
The Primitives::
While this admittedly skipped quite a few intermediate steps compared to manual mesh setup from the previous examples, in practice you'll mostly either deal with raw attribute arrays or already have a Trade::
Transformation and projection
As a final step in the constructor we specify the initial cube transformation, rotating it slightly for a 3D look. Earlier we used color literals from Math::
_transformation = Matrix4::rotationX(30.0_degf)*Matrix4::rotationY(40.0_degf);
Then we set up a perspective projection with an aspect ratio matching the window size and an initial color with an orange hue. For a more thorough introduction to Magnum's vector math library please see Operations with matrices and vectors and 2D and 3D transformations.
_projection = Matrix4::perspectiveProjection( 35.0_degf, Vector2{windowSize()}.aspectRatio(), 0.01f, 100.0f)* Matrix4::translation(Vector3::zAxis(-10.0f)); _color = Color3::fromHsv({35.0_degf, 1.0f, 1.0f}); }
Rendering
In the draw event we clear the framebuffer, now also clearing the depth buffer as we need it for the depth test. For the shader we hardcode a directional light source, set the cached color, transformation and projection matrices including a normal matrix and draw the mesh.
void PrimitivesExample::drawEvent() { GL::defaultFramebuffer.clear( GL::FramebufferClear::Color|GL::FramebufferClear::Depth); _shader.setLightPositions({{1.4f, 1.0f, 0.75f, 0.0f}}) .setDiffuseColor(_color) .setAmbientColor(Color3::fromHsv({_color.hue(), 1.0f, 0.3f})) .setTransformationMatrix(_transformation) .setNormalMatrix(_transformation.normalMatrix()) .setProjectionMatrix(_projection) .draw(_mesh); swapBuffers(); }
Pointer event handling
On a click or tap (pointer press + release) we want to shift the color hue and on drag we'll rotate the object using a bunch of vector and matrix operations. Platform::
To save resources, the application doesn't redraw constantly by default, which means we have to trigger a redraw() in order to see any effect from the events. In case you'd want to draw constantly — for example in order to perform an animation — you can call redraw() directly in the drawEvent().
void PrimitivesExample::pointerReleaseEvent(PointerEvent& event) { if(!event.isPrimary() || !(event.pointer() & (Pointer::MouseLeft|Pointer::Finger))) return; _color = Color3::fromHsv({_color.hue() + 50.0_degf, 1.0f, 1.0f}); event.setAccepted(); redraw(); } void PrimitivesExample::pointerMoveEvent(PointerMoveEvent& event) { if(!event.isPrimary() || !(event.pointers() & (Pointer::MouseLeft|Pointer::Finger))) return; Vector2 delta = 3.0f*Vector2{event.relativePosition()}/Vector2{windowSize()}; _transformation = Matrix4::rotationX(Rad{delta.y()})* _transformation* Matrix4::rotationY(Rad{delta.x()}); event.setAccepted(); redraw(); }
The main function is just the macro call, as previously.
MAGNUM_APPLICATION_MAIN(PrimitivesExample)
Compilation
Compilation is pretty straigtforward, similar to the Triangle example. Again omitting the basic setup described in the Getting Started Guide, we now use also the MeshTools
, Primitives
and Trade
libraries:
find_package(Corrade REQUIRED Main) find_package(Magnum REQUIRED GL MeshTools Primitives Shaders Trade Sdl2Application) set_directory_properties(PROPERTIES CORRADE_USE_PEDANTIC_FLAGS ON) add_executable(magnum-primitives WIN32 PrimitivesExample.cpp) target_link_libraries(magnum-primitives PRIVATE Corrade::Main Magnum::Application Magnum::GL Magnum::Magnum Magnum::MeshTools Magnum::Primitives Magnum::Shaders Magnum::Trade)
You can now try using different primitives from the Primitives namespace or render the mesh with a different shader from Shaders namespace. The full file content is linked below. Full source code is also available in the magnum-examples GitHub repository.
The ports branch contains additional patches for iOS, Android and Emscripten support that aren't present in master
in order to keep the example code as simple as possible.