Oct 24, 2018

Magnum 2018.10 released

with animation and Vulkan interop

Among oth­er high­lights is a new glTF play­er app, HiDPI sup­port, spline in­ter­pol­a­tion, a Box2D ex­ample and pro­ductiv­ity im­prove­ments all across the board.

When look­ing at the changelog for the latest ver­sion, it’s hard to be­lieve that only six months passed since the last re­lease, 2018.04. The list is — as usu­al — very long, so I’m only cherry-pick­ing the most in­ter­est­ing bits. Scroll way down for the full de­tailed change lists.

An­im­a­tion im­port and play­back

This long-awaited fea­ture fi­nally man­aged to rise to the top of the pri­or­ity list and so the new re­lease con­tains a brand-new An­im­a­tion namespace. Some design ideas are bor­rowed from Ozz-An­im­a­tion, with one end goal be­ing high-per­form­ance play­back of an­im­a­tions im­por­ted from glTF files (with oth­er formats com­ing later). The oth­er goal is be­ing able to quickly it­er­ate on hand-craf­ted an­im­a­tions of ar­bit­rary val­ues when writ­ing a game­play or UI trans­itions.

Animation::Track<Float, Color3> breathe{{
    {0.0f, 0x3bd267_srgbf},
    {0.9f, 0xacecbe_srgbf},
    {1.8f, 0x3bd267_srgbf}
}, Math::lerp};

Color3 color = breathe.at(0.7f);
Hand-craft a col­or trans­ition an­im­a­tion track …

The an­im­a­tion lib­rary sup­ports in­ter­leaved or sep­ar­ate key­frame data for cache-op­tim­ized data ac­cess; floats, std::chrono, frame in­dex (or just any­thing) for rep­res­ent­ing time, and yes, you can also an­im­ate strings, enum val­ues, bools or even the state of an­oth­er an­im­a­tion — and why not an­im­at­ing a time value to make the play­back non-lin­ear! There’s a set of built­in in­ter­pol­a­tion modes — con­stant, lin­ear, spher­ic­al lin­ear and spline-based; but you can also sup­ply your own in­ter­pol­at­or func­tion if you need some ease-in/ease-out, or, for ex­ample, un­pack a qua­ternion from a 10–10–10–2 rep­res­ent­a­tion first.

At the mo­ment the An­im­a­tion lib­rary is marked as ex­per­i­ment­al as its API is not set in stone yet. There’s a lot to ex­plain, so stay tuned for de­tailed in­tro­duct­ory blo­g­posts (and ex­amples) for all fea­tures. For a brief over­view, check the An­im­a­tion::Track and An­im­a­tion::Play­er class docs.

Animation::Player<nanoseconds, Float> player;

player.addWithCallback(breathe,
    [](Float, const Color3& c, Flat2D& shader) {
        shader.setColor(c);
    }, shader);
player.setPlayCount(0) // repeat ∞ times
      .play();
… and then use it to an­im­ate shader col­or

An­im­a­tion im­port is done through the new Trade::An­im­a­tionData class and at the mo­ment the Trade::Ab­strac­tIm­port­er in­ter­faces handle just ba­sic ob­ject trans­form­a­tion. Skin­ning and morph­ing will need some more-or-less break­ing changes to some Trade APIs and so these fea­tures are sched­uled for next re­leases. Along with that, the goal for the Trade lib­rary is al­low­ing zero-copy as­set im­port — for ex­ample play­ing back an an­im­a­tion dir­ectly from a memory-mapped glTF file, with no data cop­ies in between. See mosra/mag­num#240 for fur­ther work in this area.

Mag­num Play­er

While the An­im­a­tion API it­self doesn’t have any ded­ic­ated ex­ample yet, there’s now a new app, Mag­num Play­er, that can play back a scene file you throw at it. The fi­nal goal for this app will be show­cas­ing the full Mag­num fea­ture set — de­bug­ging and in­tro­spec­tion tools, ma­ter­i­al tweak­ing etc. Check out the on­line ver­sion be­low — it sup­ports 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 of­fi­cial Khro­nos glTF sample mod­el re­pos­it­ory on Git­Hub. Sketch­fab also has 1000s of mod­els down­load­able as glTF.

Magnum Player screenshot
Mag­num Play­er webgl2
A view­er and play­er for an­im­ated glTF scenes. Drag&drop a file to load it.

Ini­tial work on the Vulkan backend

After the hard work of re­mov­ing man­dat­ory OpenGL de­pend­ency was done in 2018.04, Mag­num is slowly gain­ing bits and pieces needed for Vulkan sup­port. In June I took over a main­tain­er­ship of flex­t­GL and ad­ded Vulkan sup­port to it. Shortly after, Mag­num gained a Vk lib­rary that provides plat­form-in­de­pend­ent func­tion point­er load­ing. It gives you a choice wheth­er you want glob­al func­tion point­ers (like with OpenGL) or man­age them loc­ally. See the ori­gin­al post about flex­t­GL for de­tails.

The Vk lib­rary also provides con­ver­sion of gen­er­ic Pixel­Format, Sampler­Fil­ter, MeshPrim­it­ive, … enums to Vulkan-spe­cif­ic Vk­Format, Vk­Fil­ter, VkPrim­it­iv­eTo­po­logy, … val­ues. That al­lows you to use Mag­num as­set man­age­ment APIs to load im­age and scene data and use them dir­ectly without time-con­sum­ing manu­al format con­ver­sion. There is also a new ex­ample fo­cused on ren­der­ing a simple tri­angle to an off­screen buf­fer us­ing a hand­craf­ted SPIR-V shader and then sav­ing it as a PNG us­ing the Mag­num PngIm­age­Con­vert­er plu­gin.

Vulkan functions in docs
Vulkan API map­ping
Like with OpenGL, Mag­num doc­u­ment­a­tion provides a help­ful map­ping of Vulkan sym­bols to equi­val­ent Mag­num APIs. Just search for them.
Vulkan Triangle screenshot
Vulkan Tri­angle
Dir­ectly uses Vulkan with hand-writ­ten SPIR-V to pro­duce a tri­angle off­screen, with sR­GB-cor­rect blend­ing. That all in just 500 lines.

HiDPI sup­port

Long gone are the days of a stand­ard 1024×768 res­ol­u­tion and fixed 96 DPI — dense screens are now a com­mon fea­ture for high­er-end laptops and desktops. In the 2018.10 re­lease, Mag­num is DPI-aware on ma­cOS, iOS, Linux and Em­scripten. The us­ab­il­ity goal is that re­quest­ing an 800×600 win­dow will make it the same phys­ic­al size as an 800×600 win­dow would have on a 96 DPI screen — so ba­sic­ally with no ex­tra in­volve­ment from the user. For web and mo­bile, Mag­num simply en­sures that for giv­en can­vas / screen size you’ll get all the pixels that are there, with no scal­ing on top. If you have a HiDPI screen, check out the WebGL demos on the Show­case page — everything should be nicely crisp. This top­ic is way more com­plex than it might seem, see DPI aware­ness for a de­tailed over­view of DPI-aware­ness on all plat­forms and what that means for you as a de­veloper.

Un­for­tu­nately out-of-the-box Win­dows sup­port didn’t make it to the re­lease (though you are able to force ar­bit­rary scal­ing with a --magnum-dpi-scaling para­met­er). Full An­droid sup­port and ad­vanced things like DPI change events when drag­ging a win­dow across dif­fer­ently dense mon­it­ors are also wait­ing to be done, see mosra/mag­num#243 help wanted for de­tails.

Math good­ies

In­tro­duc­tion of the An­im­a­tion lib­rary re­quired quite a few ad­di­tions to the Math lib­rary — there’s a new Math::Cu­bicH­ermite class for Cu­bic Hermite splines. As a gen­er­ic base for TCB curves and Cat­mull-Rom splines they are eas­ily con­vert­ible to and from Math::Bez­i­er.

And be­cause spline stor­age is use­less on its own, the zoo of in­ter­pol­a­tion func­tions got ex­ten­ded with Math::splerp() vari­ants. Be­sides that, the ex­ist­ing Math::lerp() was ex­ten­ded to al­low lin­ear in­ter­pol­a­tion of Math::Cu­bicH­ermite points, if you ever need that, and there’s a new Math::se­lect() util­ity that does con­stant in­ter­pol­a­tion of all ex­ist­ing math types. And also strings, enums or booleans. See the full list in the doc­u­ment­a­tion. There’s also a re­cent blog post about neg­lec­ted op­tim­iz­a­tion op­por­tun­it­ies in qua­ternion in­ter­pol­a­tion.

As a side-product of Squareys’ bach­el­or thes­is, Mag­num gained a large col­lec­tion of cone in­ter­sec­tion func­tions in the Math::In­ter­sec­tion namespace. The Math::Range class got in­ter­sec­tion meth­ods as well, along with oth­er niceties.

Many pro­jects either use or in­ter­face with the GLM lib­rary and so it made sense to be in­ter­op­er­able with it. Simply in­clude one of the head­ers in the GlmIn­teg­ra­tion lib­rary and you’ll get con­ver­sion of all vec­tor, mat­rix and qua­ternion types and also an abil­ity to print the GLM types us­ing Util­ity::De­bug:

#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)

List­ing all the ad­di­tions to Math lib­rary would be bey­ond over­whelm­ing, jump to the com­plete changelog for the rest.

Little big de­tails

GL::Mesh mesh = MeshTools::compile(
    Primitives::gradientVertical2D(0x2f83cc_srgbf, 0x3bd267_srgbf));

Yes, it’s now pos­sible to get a GL::Mesh dir­ectly from Trade::Mesh­Data with a single click — just use the brand new MeshTools::com­pile() re­im­ple­ment­a­tion and it’ll drag all GL::Buf­fer in­stances along with it­self, without you need­ing to man­age them. Of course there are flex­ib­il­ity tradeoffs, so when us­ing the mesh APIs dir­ectly, you have the op­tion of GL::Mesh::ad­dVer­t­ex­Buf­fer() either tak­ing a non-own­ing ref­er­ence to the buf­fer or fully tak­ing over its own­er­ship.

There’s a new Con­tain­ers::Sco­pe­dExit class that simply calls a passed exit / close / des­troy func­tion on giv­en value at the end of scope. Very use­ful when in­ter­act­ing with low-level C APIs and much easi­er than wrest­ling with std::unique_ptr, try­ing to con­vince it to do the same.

int fd = open("file.dat", O_RDONLY);
Containers::ScopedExit e{fd, close};

If you ever need to it­er­ate on a ar­ray of in­ter­leaved val­ues and take al­ways the third value, there’s now Con­tain­ers::StridedAr­rayView that ab­stracts it away. It’s used in­tern­ally by the An­im­a­tion::Track­View APIs to al­low for both flex­ible and cache-ef­fi­cient lay­out of key­frame data.

There’s a new Util­ity::format() fam­ily of func­tions for Py­thon-style type-safe string format­ting. The reas­on I’m adding this is be­cause std::os­tream (and to some ex­tent printf()) is no­tori­ously in­ef­fi­cient, neg­at­ively af­fect­ing ex­ecut­able size es­pe­cially on asm.js / WebAssembly tar­gets. How­ever the full im­ple­ment­a­tion didn’t make it in­to the re­lease, only the sur­face APIs, Mag­num is not por­ted away from streams just yet — there will be a de­tailed 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 in­tern­al thing, the De­bug­Tools::Com­pareIm­age util­ity got a Com­pareIm­ageTo­File coun­ter­part, to­geth­er with oth­er com­bin­a­tions. In sub­sequent up­dates, these will get used for fuzzy shader out­put veri­fic­a­tion — very im­port­ant for im­ple­ment­ing PBR shaders that are later on the roadmap.

Pro­to­typ­ing

Shown above is a new Prim­it­ives::gradi­en­t2D() func­tion (to­geth­er with its 3D coun­ter­part), use­ful for simple back­drops. The Shaders::Phong shader got a long-re­ques­ted sup­port for mul­tiple lights and there’s now al­pha mask­ing sup­port in both Shaders::Phong and Shaders::Flat — use­ful for quick’n’dirty pro­to­typ­ing when you don’t want to both­er your­self with depth sort­ing or OIT.

As­set man­age­ment im­prove­ments

Since the Tiny­Glt­fIm­port­er plu­gin ini­tial re­lease in 2018.04, it’s re­ceiv­ing an end­less stream of up­dates. While the biggest new fea­ture is an­im­a­tion im­port, it also re­ceived sup­port for multi-prim­it­ive meshes, name map­ping for all data, cam­era as­pect ra­tio im­port and vari­ous con­form­ance fixes and per­form­ance im­prove­ments. It’s now easi­er to ac­cess its in­tern­al state, in case you want to parse cus­tom glTF prop­er­ties or ac­cess data that the im­port­er does not sup­port yet.

To sup­port load­ing data from memory, from AAssetManager on An­droid or for ex­ample voa drag&drop on Em­scripten, all scene and im­age im­port­ers now sup­port file load­ing call­backs. For you it means you can con­tin­ue load­ing as­sets as usu­al — us­ing their fi­le­names — and only set up a dif­fer­ent file call­back for each plat­form. The im­ple­ment­a­tion was done in a way that makes all ex­ist­ing (and fu­ture) plu­gins im­pli­citly work with file call­backs, moreover the Tiny­Glt­fIm­port­er, As­simpIm­port­er and OpenGex­Im­port­er also use provided file call­backs for ex­tern­al data ref­er­enced from scene files (such as im­ages or data buf­fers).

There’s fi­nally a Jpe­gIm­age­Con­vert­er plu­gin for com­press­ing JPEG files, us­ing a lib­JPEG im­ple­ment­a­tion of your choice — be it the vanilla im­ple­ment­a­tion, lib­jpeg-turbo or, for ex­ample, MozJPEG. Sim­il­arly, the stb_im­age-based StbIm­age­Con­vert­er got up­dated to sup­port JPEG out­put as well — and you can load either of them us­ing the JpegImageConverter ali­as. Both plu­gins sup­port spe­cify­ing the out­put qual­ity via a runtime set­ting; more en­cod­ing op­tions may be ad­ded in the fu­ture.

std::unique_ptr<Trade::AbstractImageConverter> converter =
    manager.loadAndInstantiate("JpegImageConverter");
converter->configuration()->setValue("jpegQuality", 0.95f);

Among oth­er things, the Stb­TrueTypeFont was up­dated to a new ver­sion of stb_truetype, gain­ing OTF sup­port, and you can now load it (along with the oth­er Harf­BuzzFont and Free­TypeFont im­ple­ment­a­tions) via the gen­er­ic OpenTypeFont ali­as.

There’s al­ways some­thing to im­prove in the docs

If you hap­pen to be us­ing Mag­num with a build­sys­tem oth­er than CMake, there’s now a high-level guide, point­ing out the biggest pain points. The Math::Mat­rix4 and Mat­rix3 docs are im­proved with equa­tions visu­al­iz­ing most op­er­a­tions; the Math::In­ter­sec­tion and Math::Dis­tance func­tions and Math::Con­stants got up­dated equa­tions as well.

The Us­ing the scene graph guide now has a visu­al in­tro, ex­plain­ing the ba­sic con­cepts; the JavaS­cript, HTM­L5 and WebGL and An­droid guides were ex­ten­ded with fur­ther tips and troubleshoot­ing items. Oh, and the Shaders and Prim­it­ives docs now have im­ages that look prop­erly crisp on HiDPi screens.

Not all roads led to Rome

Mag­num is now over eight years old and it be­came ap­par­ent that some early func­tion­al­ity didn’t stand the test of time — either be­cause it de­pended on a now-out­dated toolkit, be­cause the re­quired time in­vest­ment for con­tin­ued main­ten­ance was not worth it or simply be­cause it was a design ex­per­i­ment that failed. The fol­low­ing lib­rar­ies are now marked as de­prec­ated, are not built by de­fault (in case they ever were) and will be com­pletely re­moved in about six months time.

  • The Shapes ob­sol­ete lib­rary, to­geth­er with DebugTools::ShapeRenderer ob­sol­ete and the BulletIntegration::convertShape() ob­sol­ete func­tion. Failed design ex­per­i­ment that couldn’t ever be made per­form­ant (and ab­us­ing % op­er­at­ors for col­li­sion quer­ies was just plain wrong).

    Re­lated geo­metry al­gorithms were moved to Math::Dis­tance and Math::In­ter­sec­tion namespaces. If you need a full-fledged phys­ics lib­rary, please have look at Bul­let, which has Mag­num in­teg­ra­tion in Bul­letInteg­ra­tion (to­geth­er with de­bug draw im­ple­men­ted in Bul­letInteg­ra­tion::De­bug­Draw), or at Box2D, which has a Mag­num ex­ample as well.

  • The Platform::GlutApplication ob­sol­ete ap­plic­a­tion. It’s based on an out­dated GLUT toolkit, has port­ab­il­ity is­sues and doesn’t make sense on the path for­ward to Vulkan. Con­sider switch­ing to either Plat­form::Sdl2Ap­plic­a­tion or Plat­form::GlfwAp­plic­a­tion.

  • The ColladaImporter ob­sol­ete plu­gin, be­cause it’s based on an out­dated Qt4 toolkit. Moreover, due to the sheer com­plex­ity of the COL­LADA format and poor con­form­ance of vari­ous ex­port­ers it’s not feas­ible to main­tain a built­in im­port­er any­more. Con­sider either us­ing As­simpIm­port­er for COL­LADA im­port or switch­ing to bet­ter-de­signed and bet­ter-sup­por­ted formats such as glTF or OpenGEX us­ing Tiny­Glt­fIm­port­er or OpenGex­Im­port­er. There’s also the of­fi­cial COL­LADA2GLTF con­vert­er.

New ex­amples

Two new ex­amples were con­trib­uted by our great com­munity, namely an in­teg­ra­tion of the Box2D phys­ics en­gine and an ad­vanced depth-aware mouse in­ter­ac­tion ex­ample. Both are por­ted to WebGL and you can play with them right now:

Box2D example screenshot
Box2D Ex­ample webgl1 phys­ics in­stan­cing
Builds a pyr­am­id out of cubes and al­lows you to des­troy it after.
Mouse Interaction example screenshot
Mouse In­ter­ac­tion Ex­ample webgl2
Blender-in­spired depth-aware pan­ning, zoom­ing and ro­ta­tion for easy scene nav­ig­a­tion.

HT­TPS 🔒

The Mag­num web­site is nev­er stor­ing any cook­ies or do­ing user track­ing (and doesn’t plan to be do­ing that), so there’s no need to be wor­ried about your data be­ing com­prom­ised. Nev­er­the­less, it’s now served over HT­TPS, with a cer­ti­fic­ate from Let’s En­crypt. Some tradeoffs were made as it’s either full se­cur­ity or sup­port­ing the not-most-re­cent browsers (but not both), so if you ex­per­i­ence any is­sues, please let us know.

Some­times a hard kick is all it takes to get things done.

Con­tri­bu­tions wel­come

Mag­num is now part­ner­ing with a few uni­ver­sit­ies with a goal of im­prov­ing com­puter graph­ics courses by of­fer­ing stu­dents things that are fun to play with. You’re in­vited to the party as well — each Git­Hub re­pos­it­ory now has is­sues marked with a help wanted la­bel and these is­sues are spe­cific­ally picked to be self-con­tained, ex­cer­cise a well-defined area of know­ledge and to not re­quire deep un­der­stand­ing of Mag­num in­tern­als. The most re­ward­ing among these are vari­ous ex­amples, you can also im­ple­ment a fancy al­gorithm, in­teg­rate sup­port for a new file format or share your ex­pert­ise in an area you know the best. If you pick some­thing, let us know and we’ll help you get on the right path.

There’s also a pos­sib­il­ity to write a guest post for this very blog and share in­ter­est­ing de­tails about a Mag­num-re­lated thing you’re work­ing on.

Up­grad­ing from pre­vi­ous ver­sions

In con­trast to 2018.04, this re­lease is more of an evol­u­tion­al one. Nev­er­the­less, even though we’re al­ways go­ing to ex­treme lengths to pre­serve back­wards com­pat­ib­il­ity, it may hap­pen that some changes will have neg­at­ive af­fect on your code. Please check the De­prec­ated APIs and Po­ten­tial com­pat­ib­il­ity is­sues sec­tions in the com­plete changelog be­low for more in­form­a­tion.

Thanks to @mat­jam there’s now a PPA re­pos­it­ory con­tain­ing pre­b­uilt pack­ages for Ubuntu 14.04, 16.04 and 18.04. If you fol­low the #mov­ing­to­git­lab move­ment, Mag­num now has a mir­ror on Git­Lab, but note that primary de­vel­op­ment, roadmap and mile­stone plan­ning is still hap­pen­ing on Git­Hub and will stay there for the fore­see­able fu­ture.

The 2018.10 re­lease is already avail­able in Homebrew and Arch­Linux AUR. At the time of writ­ing, the PPA re­pos­it­ory, Vcp­kg and Arch­Linux re­pos are not up­dated yet, we’re work­ing on get­ting the latest ver­sion there as well.

Com­plete changelog

It’s longer than you might ex­pect 😉

Spe­cial thanks

Lots of work in this re­lease is done thanks to ex­tern­al con­trib­ut­ors:

  • Jonath­an Hale (@Squareys) — tire­less main­ten­ance of all things VR, in­ter­sec­tion al­gorithms, glTF, OpenGEX, As­simp im­port­er up­dates and Vcp­kg ex­pert­ise
  • @scturtle — the Mouse In­ter­ac­tion ex­ample
  • Michal Mikula — the Box2D ex­ample
  • Nath­an Oller­en­shaw (@mat­jam) — Ubuntu PPA re­pos­it­ory main­ten­ance
  • Al­ex­an­der F Rød­seth (@xyproto) — con­tin­ued Arch­Linux [community] pack­age main­ten­ance
  • Patrick Wern­er (@boonto) — An­droid port of the Mod­el View­er ex­ample
  • Ivan P. (@uzer­name) — on­go­ing ef­fort with im­prov­ing the doc­u­ment­a­tion and mak­ing the lib­rary more ap­proach­able for new­comers

Again thanks a lot to every­one, not to for­get all people who re­por­ted is­sues, sug­ges­ted im­prove­ments or just wrote en­cour­aging mes­sages on the Git­ter chat. Cheers!