Oct 24, 2018

Magnum 2018.10 released

with animation and Vulkan interop

Among oth­er high­lights is a new glTF play­er app, HiD­PI sup­port, spline in­ter­po­la­tion, a Box2D ex­am­ple and pro­duc­tiv­i­ty im­prove­ments all across the board.

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

An­i­ma­tion im­port and play­back

This long-await­ed fea­ture fi­nal­ly man­aged to rise to the top of the pri­or­i­ty list and so the new re­lease con­tains a brand-new An­i­ma­tion names­pace. Some de­sign ideas are bor­rowed from Ozz-An­i­ma­tion, with one end goal be­ing high-per­for­mance play­back of an­i­ma­tions im­port­ed from glTF files (with oth­er for­mats com­ing lat­er). The oth­er goal is be­ing able to quick­ly it­er­ate on hand-craft­ed an­i­ma­tions of ar­bi­trary val­ues when writ­ing a game­play or UI tran­si­tions.

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 tran­si­tion an­i­ma­tion track …

The an­i­ma­tion li­brary sup­ports in­ter­leaved or sep­a­rate keyframe da­ta for cache-op­ti­mized da­ta ac­cess; floats, std::chrono, frame in­dex (or just any­thing) for rep­re­sent­ing time, and yes, you can al­so an­i­mate strings, enum val­ues, bools or even the state of an­oth­er an­i­ma­tion — and why not an­i­mat­ing a time val­ue to make the play­back non-lin­ear! There’s a set of builtin in­ter­po­la­tion modes — con­stant, lin­ear, spher­i­cal lin­ear and spline-based; but you can al­so sup­ply your own in­ter­po­la­tor func­tion if you need some ease-in/ease-out, or, for ex­am­ple, un­pack a quater­nion from a 10–10–10–2 rep­re­sen­ta­tion first.

At the mo­ment the An­i­ma­tion li­brary is marked as ex­per­i­men­tal as its API is not set in stone yet. There’s a lot to ex­plain, so stay tuned for de­tailed in­tro­duc­to­ry blog­posts (and ex­am­ples) for all fea­tures. For a brief over­view, check the An­i­ma­tion::Track and An­i­ma­tion::Play­er class docs.

Animation::Player<nanoseconds, Float> player;

    [](Float, const Color3& c, Flat2D& shader) {
    }, shader);
player.setPlayCount(0) // repeat ∞ times
… and then use it to an­i­mate shad­er col­or

An­i­ma­tion im­port is done through the new Trade::An­i­ma­tion­Da­ta class and at the mo­ment the Trade::Ab­strac­tIm­porter in­ter­faces han­dle just ba­sic ob­ject trans­for­ma­tion. Skin­ning and mor­ph­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­leas­es. Along with that, the goal for the Trade li­brary is al­low­ing ze­ro-copy as­set im­port — for ex­am­ple play­ing back an an­i­ma­tion di­rect­ly from a mem­o­ry-mapped glTF file, with no da­ta copies in be­tween. See mosra/mag­num#240 for fur­ther work in this area.

Mag­num Play­er

While the An­i­ma­tion API it­self doesn’t have any ded­i­cat­ed ex­am­ple 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­te­ri­al tweak­ing etc. Check out the on­line ver­sion be­low — it sup­ports mul­ti-file drag&drop, so sim­ply drop a glTF file on it to play it. If you don’t have any glTF file handy, there’s the of­fi­cial Khronos glTF sam­ple mod­el re­pos­i­to­ry on GitHub. Sketch­fab al­so has 1000s of mod­els down­load­able as glTF.

Magnum Player screenshot
Mag­num Play­er wasm we­bgl2
A view­er and play­er for an­i­mat­ed glTF scenes. Drag&drop a file to load it.

Ini­tial work on the Vulkan back­end

Af­ter the hard work of re­mov­ing manda­to­ry OpenGL de­pen­den­cy was done in 2018.04, Mag­num is slow­ly gain­ing bits and pieces need­ed for Vulkan sup­port. In June I took over a main­tain­er­ship of flextGL and added Vulkan sup­port to it. Short­ly af­ter, Mag­num gained a Vk li­brary that pro­vides plat­form-in­de­pen­dent func­tion point­er load­ing. It gives you a choice whether you want glob­al func­tion point­ers (like with OpenGL) or man­age them lo­cal­ly. See the orig­i­nal post about flextGL for de­tails.

The Vk li­brary al­so pro­vides con­ver­sion of gener­ic Pix­elFor­mat, Sam­pler­Fil­ter, Mesh­Prim­i­tive, … enums to Vulkan-spe­cif­ic Vk­For­mat, Vk­Fil­ter, VkPrim­i­tive­Topol­o­gy, … val­ues. That al­lows you to use Mag­num as­set man­age­ment APIs to load im­age and scene da­ta and use them di­rect­ly with­out time-con­sum­ing man­u­al for­mat con­ver­sion. There is al­so a new ex­am­ple fo­cused on ren­der­ing a sim­ple tri­an­gle to an off­screen buf­fer us­ing a hand­craft­ed SPIR-V shad­er and then sav­ing it as a PNG us­ing the Mag­num PngIm­age­Con­vert­er plug­in.

Vulkan functions in docs
Vulkan API map­ping
Like with OpenGL, Mag­num doc­u­men­ta­tion pro­vides a help­ful map­ping of Vulkan sym­bols to equiv­a­lent Mag­num APIs. Just search for them.
Vulkan Triangle screenshot
Vulkan Tri­an­gle
Di­rect­ly us­es Vulkan with hand-writ­ten SPIR-V to pro­duce a tri­an­gle off­screen, with sRGB-cor­rect blend­ing. That all in just 500 lines.

HiD­PI sup­port

Long gone are the days of a stan­dard 1024×768 res­o­lu­tion and fixed 96 DPI — dense screens are now a com­mon fea­ture for high­er-end lap­tops and desk­tops. In the 2018.10 re­lease, Mag­num is DPI-aware on mac­OS, iOS, Lin­ux and Em­scripten. The us­abil­i­ty goal is that re­quest­ing an 800×600 win­dow will make it the same phys­i­cal size as an 800×600 win­dow would have on a 96 DPI screen — so ba­si­cal­ly with no ex­tra in­volve­ment from the us­er. For web and mo­bile, Mag­num sim­ply en­sures that for giv­en can­vas / screen size you’ll get all the pix­els that are there, with no scal­ing on top. If you have a HiD­PI screen, check out the We­bGL demos on the Show­case page — ev­ery­thing should be nice­ly 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­vel­op­er.

Un­for­tu­nate­ly out-of-the-box Win­dows sup­port didn’t make it to the re­lease (though you are able to force ar­bi­trary scal­ing with a --magnum-dpi-scaling pa­ram­e­ter). Full An­droid sup­port and ad­vanced things like DPI change events when drag­ging a win­dow across dif­fer­ent­ly dense mon­i­tors are al­so wait­ing to be done, see mosra/mag­num#243 help want­ed for de­tails.

Math good­ies

In­tro­duc­tion of the An­i­ma­tion li­brary re­quired quite a few ad­di­tions to the Math li­brary — there’s a new Math::Cu­bicH­er­mite class for Cu­bic Her­mite splines. As a gener­ic base for TCB curves and Cat­mull-Rom splines they are eas­i­ly con­vert­ible to and from Math::Bezi­er.

And be­cause spline stor­age is use­less on its own, the zoo of in­ter­po­la­tion func­tions got ex­tend­ed with Math::splerp() vari­ants. Be­sides that, the ex­ist­ing Math::lerp() was ex­tend­ed to al­low lin­ear in­ter­po­la­tion of Math::Cu­bicH­er­mite points, if you ev­er need that, and there’s a new Math::se­lect() util­i­ty that does con­stant in­ter­po­la­tion of all ex­ist­ing math types. And al­so strings, enums or bool­eans. See the full list in the doc­u­men­ta­tion. There’s al­so a re­cent blog post about ne­glect­ed op­ti­miza­tion op­por­tu­ni­ties in quater­nion in­ter­po­la­tion.

As a side-prod­uct of Squareys’ bach­e­lor the­sis, Mag­num gained a large col­lec­tion of cone in­ter­sec­tion func­tions in the Math::In­ter­sec­tion names­pace. The Math::Range class got in­ter­sec­tion meth­ods as well, along with oth­er niceties.

Many projects ei­ther use or in­ter­face with the GLM li­brary and so it made sense to be in­ter­op­er­a­ble with it. Sim­ply in­clude one of the head­ers in the GlmInte­gra­tion li­brary and you’ll get con­ver­sion of all vec­tor, ma­trix and quater­nion types and al­so an abil­i­ty to print the GLM types us­ing Util­i­ty::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 li­brary would be be­yond over­whelm­ing, jump to the com­plete changel­og for the rest.

Lit­tle big de­tails

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

Yes, it’s now pos­si­ble to get a GL::Mesh di­rect­ly from Trade::Mesh­Da­ta with a sin­gle click — just use the brand new Mesh­Tools::com­pile() reim­ple­men­ta­tion and it’ll drag all GL::Buf­fer in­stances along with it­self, with­out you need­ing to man­age them. Of course there are flex­i­bil­i­ty trade­offs, so when us­ing the mesh APIs di­rect­ly, you have the op­tion of GL::Mesh::ad­dVer­texBuffer() ei­ther tak­ing a non-own­ing ref­er­ence to the buf­fer or ful­ly tak­ing over its own­er­ship.

There’s a new Con­tain­ers::Sco­pe­dEx­it class that sim­ply calls a passed ex­it / close / de­stroy func­tion on giv­en val­ue at the end of scope. Very use­ful when in­ter­act­ing with low-lev­el C APIs and much eas­i­er than wrestling with std::unique_p­tr, try­ing to con­vince it to do the same.

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

If you ev­er need to it­er­ate on a ar­ray of in­ter­leaved val­ues and take al­ways the third val­ue, there’s now Con­tain­ers::StridedAr­rayView that ab­stracts it away. It’s used in­ter­nal­ly by the An­i­ma­tion::Track­View APIs to al­low for both flex­i­ble and cache-ef­fi­cient lay­out of keyframe da­ta.

There’s a new Util­i­ty::for­mat() fam­i­ly of func­tions for Python-style type-safe string for­mat­ting. The rea­son I’m adding this is be­cause std::os­tream (and to some ex­tent printf()) is no­to­ri­ous­ly in­ef­fi­cient, neg­a­tive­ly af­fect­ing ex­e­cutable size es­pe­cial­ly on asm.js / We­bAssem­bly tar­gets. How­ev­er the full im­ple­men­ta­tion didn’t make it in­to the re­lease, on­ly the sur­face APIs, Mag­num is not port­ed away from streams just yet — there will be a de­tailed post about all this lat­er 😉

std::string s = Utility::formatString("path {{ fill: #{:6x}; stroke: #{:6x}; }}",
    0x33ff00, 0x00aa55);
// path { fill: #33ff00; stroke: #00aa55; }

More of an in­ter­nal thing, the De­bug­Tools::Com­pareIm­age util­i­ty got a Com­pareIm­ageToFile coun­ter­part, to­geth­er with oth­er com­bi­na­tions. In sub­se­quent up­dates, these will get used for fuzzy shad­er out­put ver­i­fi­ca­tion — very im­por­tant for im­ple­ment­ing PBR shaders that are lat­er on the roadmap.


Shown above is a new Prim­i­tives::gra­di­en­t2D() func­tion (to­geth­er with its 3D coun­ter­part), use­ful for sim­ple back­drops. The Shaders::Phong shad­er got a long-re­quest­ed sup­port for mul­ti­ple 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 TinyGlt­fIm­porter plug­in ini­tial re­lease in 2018.04, it’s re­ceiv­ing an end­less stream of up­dates. While the big­gest new fea­ture is an­i­ma­tion im­port, it al­so re­ceived sup­port for mul­ti-prim­i­tive mesh­es, name map­ping for all da­ta, cam­era as­pect ra­tio im­port and var­i­ous con­for­mance fix­es and per­for­mance im­prove­ments. It’s now eas­i­er to ac­cess its in­ter­nal state, in case you want to parse cus­tom glTF prop­er­ties or ac­cess da­ta that the im­porter does not sup­port yet.

To sup­port load­ing da­ta from mem­o­ry, from AAssetManager on An­droid or for ex­am­ple voa drag&drop on Em­scripten, all scene and im­age im­porters 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 file­names — and on­ly set up a dif­fer­ent file call­back for each plat­form. The im­ple­men­ta­tion was done in a way that makes all ex­ist­ing (and fu­ture) plug­ins im­plic­it­ly work with file call­backs, more­over the TinyGlt­fIm­porter, As­simpIm­porter and OpenGex­Im­porter al­so use pro­vid­ed file call­backs for ex­ter­nal da­ta ref­er­enced from scene files (such as im­ages or da­ta buf­fers).

There’s fi­nal­ly a JpegIm­age­Con­vert­er plug­in for com­press­ing JPEG files, us­ing a lib­JPEG im­ple­men­ta­tion of your choice — be it the vanil­la im­ple­men­ta­tion, lib­jpeg-tur­bo or, for ex­am­ple, MozJPEG. Sim­i­lar­ly, the stb_im­age-based StbIm­age­Con­vert­er got up­dat­ed to sup­port JPEG out­put as well — and you can load ei­ther of them us­ing the JpegImageConverter alias. Both plug­ins sup­port spec­i­fy­ing the out­put qual­i­ty via a run­time set­ting; more en­cod­ing op­tions may be added in the fu­ture.

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

Among oth­er things, the Stb­True­Type­Font was up­dat­ed 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­Buz­z­Font and FreeType­Font im­ple­men­ta­tions) via the gener­ic OpenTypeFont alias.

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

If you hap­pen to be us­ing Mag­num with a buildsys­tem oth­er than CMake, there’s now a high-lev­el guide, point­ing out the big­gest pain points. The Math::Ma­trix4 and Ma­trix3 docs are im­proved with equa­tions vi­su­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­dat­ed equa­tions as well.

The Us­ing the scene graph guide now has a vis­ual in­tro, ex­plain­ing the ba­sic con­cepts; the JavaScript, HTM­L5 and We­bGL and An­droid guides were ex­tend­ed with fur­ther tips and trou­bleshoot­ing items. Oh, and the Shaders and Prim­i­tives docs now have im­ages that look prop­er­ly crisp on HiD­Pi screens.

Not all roads led to Rome

Mag­num is now over eight years old and it be­came ap­par­ent that some ear­ly func­tion­al­i­ty didn’t stand the test of time — ei­ther be­cause it de­pend­ed on a now-out­dat­ed tool­kit, be­cause the re­quired time in­vest­ment for con­tin­ued main­te­nance was not worth it or sim­ply be­cause it was a de­sign ex­per­i­ment that failed. The fol­low­ing li­braries are now marked as dep­re­cat­ed, are not built by de­fault (in case they ev­er were) and will be com­plete­ly re­moved in about six months time.

  • The Shapes ob­so­lete li­brary, to­geth­er with DebugTools::ShapeRenderer ob­so­lete and the BulletIntegration::convertShape() ob­so­lete func­tion. Failed de­sign ex­per­i­ment that couldn’t ev­er be made per­for­mant (and abus­ing % op­er­a­tors for col­li­sion queries was just plain wrong).

    Re­lat­ed ge­om­e­try al­go­rithms were moved to Math::Dis­tance and Math::In­ter­sec­tion names­paces. If you need a full-fledged physics li­brary, please have look at Bul­let, which has Mag­num in­te­gra­tion in Bul­let­Inte­gra­tion (to­geth­er with de­bug draw im­ple­ment­ed in Bul­let­Inte­gra­tion::De­bug­Draw), or at Box2D, which has a Mag­num ex­am­ple as well.

  • The Platform::GlutApplication ob­so­lete ap­pli­ca­tion. It’s based on an out­dat­ed GLUT tool­kit, has porta­bil­i­ty is­sues and doesn’t make sense on the path for­ward to Vulkan. Con­sid­er switch­ing to ei­ther Plat­form::Sdl2Ap­pli­ca­tion or Plat­form::GlfwAp­pli­ca­tion.

  • The ColladaImporter ob­so­lete plug­in, be­cause it’s based on an out­dat­ed Qt4 tool­kit. More­over, due to the sheer com­plex­i­ty of the COL­LA­DA for­mat and poor con­for­mance of var­i­ous ex­porters it’s not fea­si­ble to main­tain a builtin im­porter any­more. Con­sid­er ei­ther us­ing As­simpIm­porter for COL­LA­DA im­port or switch­ing to bet­ter-de­signed and bet­ter-sup­port­ed for­mats such as glTF or OpenGEX us­ing TinyGlt­fIm­porter or OpenGex­Im­porter. There’s al­so the of­fi­cial COL­LADA2GLTF con­vert­er.

New ex­am­ples

Two new ex­am­ples were con­trib­uted by our great com­mu­ni­ty, name­ly an in­te­gra­tion of the Box2D physics en­gine and an ad­vanced depth-aware mouse in­ter­ac­tion ex­am­ple. Both are port­ed to We­bGL and you can play with them right now:

Box2D example screenshot
Box2D Ex­am­ple wasm we­bgl1 physics
Builds a pyra­mid out of cubes and al­lows you to de­stroy it af­ter.
Mouse Interaction example screenshot
Mouse In­ter­ac­tion Ex­am­ple wasm we­bgl2
Blender-in­spired depth-aware pan­ning, zoom­ing and ro­ta­tion for easy scene nav­i­ga­tion.


The Mag­num web­site is nev­er stor­ing any cook­ies or do­ing us­er track­ing (and doesn’t plan to be do­ing that), so there’s no need to be wor­ried about your da­ta be­ing com­pro­mised. Nev­er­the­less, it’s now served over HTTPS, with a cer­tifi­cate from Let’s En­crypt. Some trade­offs were made as it’s ei­ther full se­cu­ri­ty or sup­port­ing the not-most-re­cent browsers (but not both), so if you ex­pe­ri­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­si­ties with a goal of im­prov­ing com­put­er graph­ics cour­ses by of­fer­ing stu­dents things that are fun to play with. You’re in­vit­ed to the par­ty as well — each GitHub re­pos­i­to­ry now has is­sues marked with a help want­ed la­bel and these is­sues are specif­i­cal­ly picked to be self-con­tained, ex­cer­cise a well-de­fined area of knowl­edge and to not re­quire deep un­der­stand­ing of Mag­num in­ter­nals. The most re­ward­ing among these are var­i­ous ex­am­ples, you can al­so im­ple­ment a fan­cy al­go­rithm, in­te­grate sup­port for a new file for­mat or share your ex­per­tise 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 al­so a pos­si­bil­i­ty to write a guest post for this very blog and share in­ter­est­ing de­tails about a Mag­num-re­lat­ed 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 evo­lu­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­i­bil­i­ty, it may hap­pen that some changes will have neg­a­tive af­fect on your code. Please check the Dep­re­cat­ed APIs and Po­ten­tial com­pat­i­bil­i­ty is­sues sec­tions in the com­plete changel­og be­low for more in­for­ma­tion.

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

The 2018.10 re­lease is al­ready avail­able in Home­brew and Arch­Lin­ux AUR. At the time of writ­ing, the PPA repos­i­to­ry, Vcp­kg and Arch­Lin­ux re­pos are not up­dat­ed yet, we’re work­ing on get­ting the lat­est ver­sion there as well.

Com­plete changel­og

It’s longer than you might ex­pect 😉

Spe­cial thanks

Lots of work in this re­lease is done thanks to ex­ter­nal con­trib­u­tors:

  • Jonathan Hale (@Squareys) — tire­less main­te­nance of all things VR, in­ter­sec­tion al­go­rithms, glTF, OpenGEX, As­simp im­porter up­dates and Vcp­kg ex­per­tise
  • @sc­tur­tle — the Mouse In­ter­ac­tion ex­am­ple
  • Michal Miku­la — the Box2D ex­am­ple
  • Nathan Olleren­shaw (@mat­jam) — Ubun­tu PPA repos­i­to­ry main­te­nance
  • Alex­an­der F Rød­seth (@xypro­to) — con­tin­ued Arch­Lin­ux [community] pack­age main­te­nance
  • Patrick Wer­ner (@boon­to) — An­droid port of the Mod­el view­er ex­am­ple
  • Ivan P. (@uzer­name) — on­go­ing ef­fort with im­prov­ing the doc­u­men­ta­tion and mak­ing the li­brary more ap­proach­able for new­com­ers

Again thanks a lot to ev­ery­one, not to for­get all peo­ple who re­port­ed is­sues, sug­gest­ed im­prove­ments or just wrote en­cour­ag­ing mes­sages on the Git­ter chat. Cheers!