Magnum 2018.10 released
with animation and Vulkan interop
Among other highlights is a new glTF player app, HiDPI support, spline interpolation, a Box2D example and productivity improvements all across the board.
When looking at the changelog for the latest version, it’s hard to believe that only six months passed since the last release, 2018.04. The list is — as usual — very long, so I’m only cherry-picking the most interesting bits. Scroll way down for the full detailed change lists.
Animation import and playback
This long-awaited feature finally managed to rise to the top of the priority list and so the new release contains a brand-new Animation namespace. Some design ideas are borrowed from Ozz-Animation, with one end goal being high-performance playback of animations imported from glTF files (with other formats coming later). The other goal is being able to quickly iterate on hand-crafted animations of arbitrary values when writing a gameplay or UI transitions.
The animation library supports interleaved or separate keyframe data for
cache-optimized data access; float
s, std::chrono, frame index
(or just anything) for representing time, and yes, you can also animate
strings, enum values, bool
s or even the state of another animation —
and why not animating a time value to make the playback non-linear! There’s a
set of builtin interpolation modes —
constant, linear, spherical linear and spline-based; but you can also supply
your own interpolator function if you need some ease-in/ease-out, or, for
example, unpack a quaternion from a 10–10–10–2 representation first.
At the moment the Animation library is marked as experimental as its API is not set in stone yet. There’s a lot to explain, so stay tuned for detailed introductory blogposts (and examples) for all features. For a brief overview, check the Animation::Track and Animation::Player class docs.
Animation import is done through the new Trade::AnimationData class and at the moment the Trade::AbstractImporter interfaces handle just basic object transformation. Skinning and morphing will need some more-or-less breaking changes to some Trade APIs and so these features are scheduled for next releases. Along with that, the goal for the Trade library is allowing zero-copy asset import — for example playing back an animation directly from a memory-mapped glTF file, with no data copies in between. See mosra/magnum#240 for further work in this area.
Magnum Player
While the Animation API itself doesn’t have any dedicated example yet, there’s now a new app, Magnum Player, that can play back a scene file you throw at it. The final goal for this app will be showcasing the full Magnum feature set — debugging and introspection tools, material tweaking etc. Check out the online version below — it supports multi-file drag&drop, so simply drop a glTF file on it to play it. If you don’t have any glTF file handy, there’s the official Khronos glTF sample model repository on GitHub. Sketchfab also has 1000s of models downloadable as glTF.
Initial work on the Vulkan backend
After the hard work of removing mandatory OpenGL dependency was done in 2018.04, Magnum is slowly gaining bits and pieces needed for Vulkan support. In June I took over a maintainership of flextGL and added Vulkan support to it. Shortly after, Magnum gained a Vk library that provides platform-independent function pointer loading. It gives you a choice whether you want global function pointers (like with OpenGL) or manage them locally. See the original post about flextGL for details.
The Vk library also provides conversion of generic PixelFormat, SamplerFilter, MeshPrimitive, … enums to Vulkan-specific VkFormat, VkFilter, VkPrimitiveTopology, … values. That allows you to use Magnum asset management APIs to load image and scene data and use them directly without time-consuming manual format conversion. There is also a new example focused on rendering a simple triangle to an offscreen buffer using a handcrafted SPIR-V shader and then saving it as a PNG using the Magnum PngImageConverter plugin.
HiDPI support
Long gone are the days of a standard 1024×768 resolution and fixed 96 DPI — dense screens are now a common feature for higher-end laptops and desktops. In the 2018.10 release, Magnum is DPI-aware on macOS, iOS, Linux and Emscripten. The usability goal is that requesting an 800×600 window will make it the same physical size as an 800×600 window would have on a 96 DPI screen — so basically with no extra involvement from the user. For web and mobile, Magnum simply ensures that for given canvas / screen size you’ll get all the pixels that are there, with no scaling on top. If you have a HiDPI screen, check out the WebGL demos on the Showcase page — everything should be nicely crisp. This topic is way more complex than it might seem, see DPI awareness for a detailed overview of DPI-awareness on all platforms and what that means for you as a developer.
Unfortunately out-of-the-box Windows support didn’t make it to the release
(though you are able to force arbitrary scaling with a --magnum-dpi-scaling
parameter). Full Android support and advanced things like DPI change events
when dragging a window across differently dense monitors are also waiting to be
done, see mosra/magnum#243 help wanted for details.
Math goodies
Introduction of the Animation library required quite a few additions to the Math library — there’s a new Math::CubicHermite class for Cubic Hermite splines. As a generic base for TCB curves and Catmull-Rom splines they are easily convertible to and from Math::Bezier.
Cubic spline interpolation is henceforth referred to as splerp
— Thew (@AmazingThew) December 24, 2016
And because spline storage is useless on its own, the zoo of interpolation functions got extended with Math::splerp() variants. Besides that, the existing Math::lerp() was extended to allow linear interpolation of Math::CubicHermite points, if you ever need that, and there’s a new Math::select() utility that does constant interpolation of all existing math types. And also strings, enums or booleans. See the full list in the documentation. There’s also a recent blog post about neglected optimization opportunities in quaternion interpolation.
As a side-product of Squareys’ bachelor thesis, Magnum gained a large collection of cone intersection functions in the Math::Intersection namespace. The Math::Range class got intersection methods as well, along with other niceties.
Many projects either use or interface with the GLM library and so it made sense to be interoperable with it. Simply include one of the headers in the GlmIntegration library and you’ll get conversion of all vector, matrix and quaternion types and also an ability to print the GLM types using Utility::Debug:
#include <Magnum/GlmIntegration/GtcIntegration.h> … glm::mat3 a = glm::mat3(Matrix4::rotation(35.0_degf)); Quaternion b = Quaternion(glm::quat{4.0f, 1.0f, 2.0f, 3.0f}); Debug{} << glm::ivec3{1, 42, -3}; // prints ivec3(1, 42, -3)
Listing all the additions to Math library would be beyond overwhelming, jump to the complete changelog for the rest.
Little big details
GL::Mesh mesh = MeshTools::compile( Primitives::gradientVertical2D(0x2f83cc_srgbf, 0x3bd267_srgbf));
Yes, it’s now possible to get a GL::Mesh directly from Trade::MeshData with a single click — just use the brand new MeshTools::compile() reimplementation and it’ll drag all GL::Buffer instances along with itself, without you needing to manage them. Of course there are flexibility tradeoffs, so when using the mesh APIs directly, you have the option of GL::Mesh::addVertexBuffer() either taking a non-owning reference to the buffer or fully taking over its ownership.
There’s a new Containers::ScopedExit class that simply calls a passed exit / close / destroy function on given value at the end of scope. Very useful when interacting with low-level C APIs and much easier than wrestling with std::unique_ptr, trying to convince it to do the same.
int fd = open("file.dat", O_RDONLY); Containers::ScopedExit e{fd, close};
If you ever need to iterate on a array of interleaved values and take always the third value, there’s now Containers::StridedArrayView that abstracts it away. It’s used internally by the Animation::TrackView APIs to allow for both flexible and cache-efficient layout of keyframe data.
There’s a new Utility::format() family of functions for Python-style type-safe string formatting. The reason I’m adding this is because std::ostream (and to some extent printf()) is notoriously inefficient, negatively affecting executable size especially on asm.js / WebAssembly targets. However the full implementation didn’t make it into the release, only the surface APIs, Magnum is not ported away from streams just yet — there will be a detailed post about all this later 😉
std::string s = Utility::formatString("path {{ fill: #{:6x}; stroke: #{:6x}; }}", 0x33ff00, 0x00aa55); // path { fill: #33ff00; stroke: #00aa55; }
More of an internal thing, the DebugTools::CompareImage utility got a CompareImageToFile counterpart, together with other combinations. In subsequent updates, these will get used for fuzzy shader output verification — very important for implementing PBR shaders that are later on the roadmap.
Prototyping
Shown above is a new Primitives::gradient2D() function (together with its 3D counterpart), useful for simple backdrops. The Shaders::Phong shader got a long-requested support for multiple lights and there’s now alpha masking support in both Shaders::Phong and Shaders::Flat — useful for quick’n’dirty prototyping when you don’t want to bother yourself with depth sorting or OIT.
Asset management improvements
Since the TinyGltfImporter plugin initial release in 2018.04, it’s receiving an endless stream of updates. While the biggest new feature is animation import, it also received support for multi-primitive meshes, name mapping for all data, camera aspect ratio import and various conformance fixes and performance improvements. It’s now easier to access its internal state, in case you want to parse custom glTF properties or access data that the importer does not support yet.
To support loading data from memory, from AAssetManager
on Android or
for example voa drag&drop on Emscripten, all scene and image importers now
support file loading callbacks.
For you it means you can continue loading assets as usual — using their
filenames — and only set up a different file callback for each platform. The
implementation was done in a way that makes all existing (and future) plugins
implicitly work with file callbacks, moreover the
TinyGltfImporter,
AssimpImporter and
OpenGexImporter also use provided file
callbacks for external data referenced from scene files (such as images or data
buffers).
There’s finally a JpegImageConverter plugin
for compressing JPEG files, using a libJPEG implementation of your choice —
be it the vanilla implementation, libjpeg-turbo
or, for example, MozJPEG. Similarly, the
stb_image-based StbImageConverter
got updated to support JPEG output as well — and you can load either of them
using the JpegImageConverter
alias. Both plugins support specifying the
output quality via a runtime setting; more encoding options may be added in the
future.
std::unique_ptr<Trade::AbstractImageConverter> converter = manager.loadAndInstantiate("JpegImageConverter"); converter->configuration()->setValue("jpegQuality", 0.95f);
Among other things, the StbTrueTypeFont was
updated to a new version of stb_truetype
, gaining OTF support, and you can
now load it (along with the other HarfBuzzFont and
FreeTypeFont implementations) via the generic
OpenTypeFont
alias.
There’s always something to improve in the docs
If you happen to be using Magnum with a buildsystem other than CMake, there’s now a high-level guide, pointing out the biggest pain points. The Math::Matrix4 and Matrix3 docs are improved with equations visualizing most operations; the Math::Intersection and Math::Distance functions and Math::Constants got updated equations as well.
The Using the scene graph guide now has a visual intro, explaining the basic concepts; the JavaScript, HTML5 and WebGL and Android guides were extended with further tips and troubleshooting items. Oh, and the Shaders and Primitives docs now have images that look properly crisp on HiDPi screens.
Not all roads led to Rome
Magnum is now over eight years old and it became apparent that some early functionality didn’t stand the test of time — either because it depended on a now-outdated toolkit, because the required time investment for continued maintenance was not worth it or simply because it was a design experiment that failed. The following libraries are now marked as deprecated, are not built by default (in case they ever were) and will be completely removed in about six months time.
The
Shapes
obsolete library, together withDebugTools::ShapeRenderer
obsolete and theBulletIntegration::convertShape()
obsolete function. Failed design experiment that couldn’t ever be made performant (and abusing%
operators for collision queries was just plain wrong).Related geometry algorithms were moved to Math::Distance and Math::Intersection namespaces. If you need a full-fledged physics library, please have look at Bullet, which has Magnum integration in BulletIntegration (together with debug draw implemented in BulletIntegration::DebugDraw), or at Box2D, which has a Magnum example as well.
The
Platform::GlutApplication
obsolete application. It’s based on an outdated GLUT toolkit, has portability issues and doesn’t make sense on the path forward to Vulkan. Consider switching to either Platform::Sdl2Application or Platform::GlfwApplication.The
ColladaImporter
obsolete plugin, because it’s based on an outdated Qt4 toolkit. Moreover, due to the sheer complexity of the COLLADA format and poor conformance of various exporters it’s not feasible to maintain a builtin importer anymore. Consider either using AssimpImporter for COLLADA import or switching to better-designed and better-supported formats such as glTF or OpenGEX using TinyGltfImporter or OpenGexImporter. There’s also the official COLLADA2GLTF converter.
New examples
Two new examples were contributed by our great community, namely an integration of the Box2D physics engine and an advanced depth-aware mouse interaction example. Both are ported to WebGL and you can play with them right now:
HTTPS 🔒
The Magnum website is never storing any cookies or doing user tracking (and doesn’t plan to be doing that), so there’s no need to be worried about your data being compromised. Nevertheless, it’s now served over HTTPS, with a certificate from Let’s Encrypt. Some tradeoffs were made as it’s either full security or supporting the not-most-recent browsers (but not both), so if you experience any issues, please let us know.
Sometimes a hard kick is all it takes to get things done.
Contributions welcome
Magnum is now partnering with a few universities with a goal of improving computer graphics courses by offering students things that are fun to play with. You’re invited to the party as well — each GitHub repository now has issues marked with a help wanted label and these issues are specifically picked to be self-contained, excercise a well-defined area of knowledge and to not require deep understanding of Magnum internals. The most rewarding among these are various examples, you can also implement a fancy algorithm, integrate support for a new file format or share your expertise in an area you know the best. If you pick something, let us know and we’ll help you get on the right path.
There’s also a possibility to write a guest post for this very blog and share interesting details about a Magnum-related thing you’re working on.
Upgrading from previous versions
In contrast to 2018.04, this release is more of an evolutional one. Nevertheless, even though we’re always going to extreme lengths to preserve backwards compatibility, it may happen that some changes will have negative affect on your code. Please check the Deprecated APIs and Potential compatibility issues sections in the complete changelog below for more information.
Thanks to @matjam there’s now a PPA repository containing prebuilt packages for Ubuntu 14.04, 16.04 and 18.04. If you follow the #movingtogitlab movement, Magnum now has a mirror on GitLab, but note that primary development, roadmap and milestone planning is still happening on GitHub and will stay there for the foreseeable future.
The 2018.10 release is already available in Homebrew and ArchLinux AUR. At the time of writing, the PPA repository, Vcpkg and ArchLinux repos are not updated yet, we’re working on getting the latest version there as well.
Complete changelog
It’s longer than you might expect 😉
Special thanks
Lots of work in this release is done thanks to external contributors:
- Jonathan Hale (@Squareys) — tireless maintenance of all things VR, intersection algorithms, glTF, OpenGEX, Assimp importer updates and Vcpkg expertise
- @scturtle — the Mouse Interaction example
- Michal Mikula — the Box2D example
- Nathan Ollerenshaw (@matjam) — Ubuntu PPA repository maintenance
- Alexander F Rødseth (@xyproto) — continued ArchLinux
[community]
package maintenance - Patrick Werner (@boonto) — Android port of the Model Viewer example
- Ivan P. (@uzername) — ongoing effort with improving the documentation and making the library more approachable for newcomers
Again thanks a lot to everyone, not to forget all people who reported issues, suggested improvements or just wrote encouraging messages on the Gitter chat. Cheers!