Magnum 2018.04 released
with custom renderer support
The new release brings more flexibility to asset management and rendering abstractions, improves plugin handling and showcases Leap Motion integration.
Two months after the previous release, Magnum comes with the biggest change in five years of its public history. With 2018.04 the OpenGL wrapping layer and asset management APIs are no longer a core part of the library, but completely optional — this finally allows for the Vulkan port implementation and also makes it possible to use Magnum in contexts where it was not possible before. The changelog since 2018.02 is almost as long as list of changes for whole three years before, so let’s dive right in!
OpenGL wrapping layer made optional
There’s a new GL library containing all buffer, mesh, texture and other APIs that were previously part of the core library. It’s built by default, but can be completely disabled. Some Magnum libraries still have a hard dependency on it, at the moment it’s Text and Shaders. This will change during the next releases, especially the Text library is scheduled to receive updates that separate the text layouting from GPU rendering. However, these updates will be rather incremental compared to this release.
Some Magnum libraries provide functionality that makes use of the GL library (for example MeshTools::compile() or DebugTools::textureSubImage()). This doesn’t make them tied to it, however — you can disable OpenGL integration and leave just the API-agnostic features there. Look in the documentation for details.
API-independent asset management
Besides having the GL library optional, the Trade library is now optional as well — so if you use Magnum for GPU data processing and don’t need any image decoding plugins or mesh import, you can use Magnum without any of that, with faster build times and smaller deploy sizes.
To make the asset management APIs independent on OpenGL, there are now generic PixelFormat / CompressedPixelFormat, MeshPrimitive, SamplerFilter, SamplerMipmap and SamplerWrapping enums. Each of these contains a set of most widely used formats/types and all image and scene importers now describe data using those generic types. The new mesh primitive and sampler types retain their original naming, because that’s in line with how most graphics APIs including Vulkan, Metal, or D3D name them. However I dropped the unnecessary pixel format / pixel type distinction enforced by OpenGL legacy and made just a single PixelFormat enum. Naming of its values is a result of finding a good compromise between readability and clarity with unambiguous implicit mapping to GPU APIs. It resembles MTLPixelFormat the most, which itself is a more readable alternative to what VkFormat or D3D formats offer.
const Color3 data[]{ 0xff0000_rgbf, 0x00ff00_rgbf, 0x00ffff_rgbf, 0xffff00_rgbf }; /* Three-component 32-bit floating point pixels */ ImageView2D image{PixelFormat::RGB32F, {2, 2}, data};
However, that does not mean you are now restricted to some “lowest common denominator” subset, as is unfortunately the case in some graphics API abstractions — if you feel the need for a BGR565 type or simply want to use the OpenGL types directly like before, just do it:
ImageView2D image{GL::PixelFormat::BGR, GL::PixelType::UnsignedShort565Rev, {32, 32}, data};
The Image, ImageView and Trade::ImageData APIs support both — if you pass them a generic format, the GL library converts it to the well-known GL::PixelFormat / GL::PixelType pair that OpenGL understands; if you pass it an implementation-specific format, it will use it as-is.
The translation to OpenGL-specific formats is done implicitly (i.e., all APIs in the GL library that take the generic enum have an overload that takes the OpenGL-specific value as well), but you can also request explicit conversion using GL::pixelFormat(), GL::pixelType(), GL::samplerWrapping() and other translation functions.
This is not limited to OpenGL, though — if you want to use the Magnum image classes with Metal-specific formats, for example, you can do so, too. In this case the constructor requires you to specify pixel size manually, as it needs it for various checks and operations later:
/* Default pixel storage, 8-bit sRGB + alpha, four bytes per pixel */ ImageView2D view{{}, MTLPixelFormatRGBA8Unorm_sRGB, {}, 4, {256, 256}, data};
Using custom renderers with Magnum
Making the GL library optional was primarily a first step towards Vulkan support (work on which will now be resumed, you can watch the progress in mosra/magnum#234), but this opens up more possibilites than that — you can now use Magnum in projects that have their own rendering engines! To prove the point, there’s now a new example that uses the sokol_gfx single-file graphics API abstraction library to render a triangle on screen without relying on Magnum OpenGL support at all.
The possibilities don’t end here — if the bgfx library is your renderer of choice, you can still make use of the well-documented Magnum Math library, SceneGraph or the OpenGexImporter and other plugins. Or use the TestSuite with Unreal Engine and compare your shader output against a ground truth using DebugTools::CompareImage, for example.
Plugin workflow improvements
A lot of effort went into improving the general plugin workflow. Until now, loading a file using plugins involved quite a lot of setup in order to find and load the correct plugin from the filesystem. Not anymore — loading a model from a glTF file is now just a matter of this:
PluginManager::Manager<Trade::AbstractImporter> manager; std::unique_ptr<Trade::AbstractImporter> importer = manager.loadAndInstantiate("GltfImporter"); importer->openFile("cave.gltf"); Containers::Optional<Trade::MeshData3D> data = importer->mesh3D(importer->mesh3DForName("treasure-chest"));
And the workflow is the same independently on the platform — thanks to @Squareys the Vcpkg packages also received updates that make the plugin “just work” on Windows as well, including deployment of all necessary files.
There’s more — plugin alias priorities, plugin-specific configuration and automagic import of static plugins. And if you like glTF, there’s now a fresh TinyGltfImporter plugin for importing this format. Further details about all plugin usability improvements are in a dedicated article.
Package updates
Thanks to @xyproto, the ArchLinux [community]
repository now contains
stable versions of Corrade, Magnum and Magnum Plugins packages, so if you are
on ArchLinux, you can just do this now:
sudo pacman -S magnum
The Homebrew packages are now providing stable versions so you no longer need
to install --HEAD
versions:
brew install mosra/magnum/magnum
The Homebrew and ArchLinux AUR packages are already updated to 2018.04, the
ArchLinux [community]
and Vcpkg packages are expecting an update soon (see
microsoft/vcpkg#3407). Besides these, Magnum provides in-tree Debian/
Ubuntu and Gentoo packages as usual, head over to the
documentation for details.
More? Yes, there’s more!
Thanks to @Squareys (again!), we have a new fun example integrating the Leap Motion SDK. See its full source in the documentation.
Android support got an overhaul, supporting the Gradle build and new Clang-based NDKs. There’s now a very detailed Android guide in the documentation, explaining all quirks and annoyances you might encounter. Other platform-specific guides will get gradually filled with useful information regarding specific targets.
The GL library has now an initial support for OpenGL ES 3.2 and OpenGL 4.6 and received various clarifications and updates regarding floating-point formats on embedded systems.
The Primitives namespace was simplified to a bunch of free functions instead of the OOP-heavy interface before. There are new cone, grid and 3D circle primitives and the documentation now shows how every primitive looks:
Among other things the Oculus VR example got simplified and there’s a second experimental version of the DartIntegration library by @costashatz.
Upgrading from previous versions
Because the API changed quite heavily, upgrading from previous versions is not as smooth as usual. Nevertheless, great care was taken to make this process as frictionless as possible.
First of all, if you are not on 2018.02
yet, please update to it first to reduce the amount of unwanted surprises.
Next, in case you use Magnum with the BUILD_DEPRECATED
option disabled
(it’s usually enabled by default), enable it again — it will help with the
upgrade a lot.
Update your copies of FindCorrade.cmake
, FindMagnum.cmake
and others
— in the ideal case just doing that should make your code compile and link
again, although with a lot of deprecation warnings. The deprecation warnings
include helpful messages showing what to use instead, a high-level overview of
the changes needed is below:
CMake setup before:
find_package(Magnum REQUIRED Sdl2Application) add_exectutable(my-app main.cpp) target_link_libraries(my-app PRIVATE Magnum::Application Magnum::Magnum)
With 2018.04:
find_package(Magnum REQUIRED GL Trade Sdl2Application) add_exectutable(my-app main.cpp) target_link_libraries(my-app PRIVATE Magnum::Application Magnum::GL Magnum::Trade Magnum::Magnum)
Includes before:
#include <Magnum/ImageView.h> #include <Magnum/PixelFormat.h> #include <Magnum/Buffer.h> #include <Magnum/Framebuffer.h> #include <Magnum/Mesh.h>
With 2018.04:
#include <Magnum/ImageView.h> #include <Magnum/PixelFormat.h> #include <Magnum/GL/Buffer.h> #include <Magnum/GL/Framebuffer.h> #include <Magnum/GL/Mesh.h>
Mesh setup before:
Buffer vertices, indices; vertices.setData(data, BufferUsage::StaticDraw); indices.setData(indexData, BufferUsage::StaticDraw); Mesh mesh{MeshPrimitive::Triangles}; mesh.addVertexBuffer(vertices, 0, ...) .setIndexBuffer(indices, 0, Mesh::IndexType::UnsignedShort);
With 2018.02:
GL::Buffer vertices; vertices.setData(data, GL::BufferUsage::StaticDraw); indices.setData(indexData, GL::BufferUsage::StaticDraw); GL::Mesh mesh{MeshPrimitive::Triangles}; mesh.addVertexBuffer(vertices, 0, ...) .setIndexBuffer(indices, 0, MeshIndexType::UnsignedShort);
Texture setup before:
ImageView2D image{ PixelFormat::Red, PixelType::UnsignedByte, size, data}; Texture2D t; t.setMagnificationFilter( Sampler::Filter::Linear) .setMinificationFilter( Sampler::Filter::Linear) .setWrapping(Sampler::Wrapping::ClampToEdge) .setStorage(1, TextureFormat::R8, size) .setSubImage(0, {}, image);
With 2018.02:
ImageView2D image{ PixelFormat::R8Unorm, size, data}; GL::Texture2D t; t.setMagnificationFilter( SamplerFilter::Linear) .setMinificationFilter( SamplerFilter::Linear) .setWrapping(SamplerWrapping::ClampToEdge) .setStorage(1, GL::TextureFormat::R8, size) .setSubImage(0, {}, image);
The above snippets assume you’d want to use the generic pixel format and sampler state enums as it’s less typing, but you can use their GL-specific alternatives as well. You also might not need to link to all the libraries shown above if you don’t use any functionality from them.
Look in the changelog for a full overview of all deprecated features and also backwards-incompatible changes.
Complete changelog
There’s a lot of small changes that would be too boring to mention in full here. As usual, you can find a complete list of changes since 2018.02 in the documentation:
Special thanks
Again a lot of work in this release is done thanks to external contributors:
- Tobias Stein (@NussknackerXXL) — initial implementation of the TinyGltfImporter plugin
- Jonathan Hale (@Squareys) — Oculus VR example simplification, contributions to the tiny_gltf importer plugin, Leap Motion example, Vcpkg package maintenance and much more
- Konstantinos Chatzilygeroudis (@costashatz) — improvements to the DART integration library
- Alexander F Rødseth (@xyproto) — ArchLinux
[community]
package maintenance
Thanks a lot to everyone, not to forget all people that reported issues or just wrote encouraging messages on the Gitter chat. Cheers!