Re­designed geo­metry pipeline to­geth­er with massive ad­di­tions to im­port­er plu­gins, new de­bug­ging, visu­al­iz­a­tion and pro­fil­ing tools, new ex­amples in­clud­ing flu­id sim­u­la­tion and raytra­cing, in­stan­cing in built­in shaders and a gal­lery of cool pro­jects to get in­spired from.

Big re­works take time to fer­ment prop­erly, which is why this re­lease comes more than six months after the pre­vi­ous one, 2019.10. I dare to say this is the busiest re­lease of Mag­num yet, with work span­ning across sev­er­al areas of the pro­ject. Fol­low­ing are re­lease high­lights, for a de­tailed changelog that’s about 100 times big­ger please see links at the bot­tom.

New geo­metry pipeline

As­set im­port and geo­metry pro­cessing is one of the cent­ral parts of Mag­num. For 2020.06 it got re­writ­ten from a rather ba­sic dec­ade-old “toy en­gine” im­ple­ment­a­tion full of nes­ted std::vec­tors to an ef­fi­cient and flex­ible GPU-friendly design that’s ready for new paradigms such as mesh shaders. I won’t be re­it­er­at­ing everything that went in­to it again in this an­nounce­ment, please go see the in-depth in­tro­duc­tion art­icle for de­tailed in­form­a­tion:

New Geometry Pipeline in Magnum cover image
New Geo­metry Pipeline in Mag­num May 27, 2020
Flex­ible and ef­fi­cient mesh rep­res­ent­a­tion, cus­tom at­trib­utes, new data types and a ton of new pro­cessing, visu­al­iz­a­tion and ana­lyz­ing tools. GPU-friendly geo­metry stor­age as it should be in the 21st cen­tury.

Let’s fork C++ fur­ther

The move away from std::vec­tor, which is vis­ible es­pe­cially in the re­designed MeshTools namespace, means Mag­num had to provide a re­place­ment. Con­tain­ers::Ar­ray is around for quite a while — but for the pur­pose of cer­tain im­port­ers and Prim­it­ives that can’t eas­ily know the fi­nal in­dex/ver­tex count be­fore­hand — it had to be ex­ten­ded to sup­port ar­bit­rary grow­ing, like std::vec­tor has.

Containers::Array<Color3> palette;

arrayAppend<MyFancyAllocator>(palette, {
    0xa5c9ea_rgbf,
    0x3bd267_rgbf,
    0xc7cf2f_rgbf
});

A Con­tain­ers::Ar­ray in­stance isn’t tied to a par­tic­u­lar al­loc­at­or — and you can also switch to a dif­fer­ent al­loc­at­or at any point later.

The design is rather un­con­ven­tion­al in or­der to avoid the well-known short­com­ings of std::vec­tor, es­pe­cially when it comes to cus­tom al­loc­at­ors. The im­ple­ment­a­tion present in 2020.06 is still miss­ing ar­bit­rary in­ser­tion and de­le­tion which is why it’s not ad­vert­ised in more de­tail yet — once that’s done, ex­pect a ded­ic­ated art­icle to­geth­er with bench­marks and com­par­is­on to com­mon im­ple­ment­a­tions. Un­til then, see Grow­able ar­rays for an in­tro­duc­tion.

One of the last re­main­ing pieces of STL that are hold­ing us back not as light­weight & flex­ible as I’d want them to be are std::strings. After the re­lease cut I de­cided that new APIs are not go­ing to use those any­more, which means a re­place­ment is un­der­way. Ex­ist­ing APIs will get gradu­ally por­ted away, sim­il­arly as was done with std::vec­tor or std::unique_ptr in the past. Of course, as al­ways, an opt-in com­pat­ib­il­ity of the new APIs with std::string / std::string_view will be provided — the goal is not to ali­en­ate users of stand­ard C++, the goal is to be flex­ible and provide al­tern­at­ives.

Math and al­gorithm good­ies

Be­cause the new Trade::Mesh­Data APIs ex­pan­ded a lot on sup­por­ted ver­tex formats, the math lib­rary re­ceived batch Math::pack­In­to() / un­pack­In­to() func­tions that al­low for ef­fi­cient con­ver­sion between float­ing-point and 8-/16-bit packed or half-float types. When no con­ver­sion is needed, Util­ity::copy() from a new Util­ity/Al­gorithms.h head­er gives you a std::mem­cpy() / std::copy() al­tern­at­ive that works on mul­tiple di­men­sions and sparse data lay­outs as well.

ImageView2D src, dst;

Utility::copy(src.pixels().flipped<1>(), dst.pixels());

Mir­ror­ing an im­age — in an oneliner, with Con­tain­ers::StridedAr­rayView un­der­neath.

The MeshTools lib­rary, apart from be­ing ad­ap­ted for the re­designed work­flow, now con­tains MeshTools::con­cat­en­ate() for join­ing mul­tiple meshes to­geth­er, or for ex­ample MeshTools::gen­er­ateIn­dices() for con­vert­ing strips, loops or fans to plain in­dexed meshes.

What’s new in plu­gins

The more Mag­num gets used to im­port a in­creas­ingly broad range of data­sets in vari­ous formats, the more pre­vi­ously un­handled corner cases get dis­covered and fixed.

  • The As­simpIm­port­er now cor­rectly im­ports multi-prim­it­ive meshes, pre­serves al­pha in ma­ter­i­als and ac­counts for Y up / Z up ori­ent­a­tion over­ride, if a file defines it.
  • All im­port­ers now im­port both base col­or and tex­ture in­stead of either one or the oth­er. In­ter­est­ingly, this was a lim­it­a­tion that ori­gin­ated from the COL­LADA format — the early Trade APIs and the now-gone ColladaImporter plu­gin were mod­elled after it, how­ever when re­view­ing this design de­cision it turned out that COL­LADA is ac­tu­ally the only format with such re­stric­tion and every oth­er format (OBJ, glTF, OpenGEX, …) sup­ports com­bin­ing both.
  • Tiny­Glt­fIm­port­er failed to im­port in­ter­leaved meshes. This was a short­cut done to make the early im­ple­ment­a­tion sim­pler. To my sur­prise, ap­par­ently the vast ma­jor­ity of glTF mod­els is ex­por­ted de-in­ter­leaved and thus in­ef­fi­cient for the GPU, which ex­plains why this lim­it­a­tion went largely un­noticed since the ori­gin­al plu­gin re­lease in 2018.

Im­age im­port­ers wer­en’t left be­hind either — formats that sup­port it such as DDS or Basis Uni­ver­sal now can im­port par­tic­u­lar mip levels us­ing Trade::Ab­strac­tIm­port­er::im­age2D­Level­Count() and the second para­met­er of Trade::Ab­strac­tIm­port­er::im­age2D(), with this be­ing prox­ied in­to all scene im­port­ers as well. Vaguely re­lated to this, Dev­Il­Im­ageIm­port­er and StbIm­ageIm­port­er can now im­port frames of an­im­ated GIFs for a very crude video play­back.

Suited mainly for test­ing pur­poses, the whole Prim­it­ives lib­rary is now ex­posed through a Prim­it­i­veIm­port­er plu­gin. This can be used for ex­ample to ex­pose built­in prim­it­ives to ex­ist­ing Importer-based pipelines without hav­ing to add new code path for each.

As men­tioned in the New Geo­metry Pipeline in Mag­num art­icle already, there’s sev­er­al ad­di­tions and im­prove­ments that go to­geth­er with the new Trade::Mesh­Data APIs:

Fi­nally, there is a new scene con­vert­er plu­gin in­ter­face, with Mesh­Op­tim­izer­SceneCon­vert­er and Stan­ford­SceneCon­vert­er be­ing the first two plu­gins im­ple­ment­ing it. Apart from that, im­age im­port­ers now have a simple IcoIm­port­er for Win­dows *.ico files and there’s (also a very trivi­al) StlImport­er for bin­ary STL files, com­monly used in 3D print­ing.

All im­port­er and con­vert­er plu­gins re­ceived a flag to en­able verb­ose out­put, which is also ex­posed as a --verbose op­tion in the mag­num-im­age­con­vert­er, mag­num-scenecon­vert­er and mag­num-play­er util­it­ies. The plu­gins use that to no­ti­fy you about longer-run­ning op­er­a­tions or pro­cessing stats. It’s prob­ably most help­ful in case of As­simp, which likes to crash or mis­be­have on cer­tain files.

Built­in shader im­prove­ments, new visu­al­iz­a­tion tools

Per­haps the most sig­ni­fic­ant shader ad­di­tion is in­stan­cing sup­port in Shaders::Phong and Shaders::Flat — while in­stan­cing alone was sup­por­ted in the GL lib­rary since 2014, the built­in shaders didn’t im­ple­ment this func­tion­al­ity un­til now. For show­case, the Bul­let Phys­ics and Box2D ex­amples are now re­im­ple­men­ted us­ing in­stan­cing, each of them us­ing just a single draw call for the whole scene. Try them out on­line:

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.
Bullet Physics example screenshot
Bul­let Phys­ics Ex­ample webgl1 phys­ics in­stan­cing
Shows a ro­tat­ing table full of cubes that you can shoot down.

Com­ple­ment­ing the glTF KHR_­tex­ture_trans­form ex­ten­sion sup­port, there’s now Shaders::Phong::set­Tex­tureMat­rix() to­geth­er with abil­ity to have in­stanced tex­ture off­set, and the same in Shaders::Flat.

With tan­gent and nor­mal map im­port be­ing done, Shaders::Phong nor­mal map sup­port ad­ded in 2019.10 can fi­nally be fully util­ized. This is closely tied with Shaders::MeshVisu­al­izer­3D now be­ing able to visu­al­ize not just wire­frame but also tan­gent, bit­an­gent and nor­mal dir­ec­tion — very use­ful for de­bug­ging those dreaded light­ing is­sues.

Shaders::Phong / Shaders::Flat can now out­put also per-ver­tex Ob­ject ID at­trib­ute, which means Shaders::MeshVisu­al­izer­3D can visu­al­ize that one as well, to­geth­er with ver­tex and prim­it­ive ID. This goes hand-in-hand with a new De­bug­Tools::ColorMap namespace that in­cludes also the very re­cog­niz­able Turbo colormap by Ant­on Mikhail­ov.

Cer­tain GL drivers con­tin­ue to be a hot mess

Even if all oth­er new fea­tures shown here wouldn’t be a con­vin­cing reas­on for you to up­grade, you’ll def­in­itely want to pick up these three work­arounds for bet­ter driver com­pat­ib­il­ity:

  • A hard-to-re­pro­duce syn­chron­iz­a­tion bug on In­tel Win­dows drivers makes the AR­B_­dir­ec­t_state_ac­cess ex­ten­sion ba­sic­ally un­us­able for any­thing re­lated to buf­fers or VAOs. Usu­ally mani­fes­ted as flick­er­ing in ImGui-based apps. A sub­set of this work­around was done for 2019.10 already but due to its semi-ran­dom nature it didn’t cov­er all cases. This work­around aban­donds all hope and com­pletely dis­ables DSA for af­fected code paths on these drivers.
  • In­tel Win­dows drivers don’t really re­spect ex­pli­cit uni­form loc­a­tions but in­stead only take it as a very vague sug­ges­tion. This bug was most cer­tainly also present since forever, but only be­came vis­ible after the latest ad­di­tions of tex­ture trans­form, nor­mal maps and in­stan­cing to built­in shaders, which caused the uni­form loc­a­tions to be any­thing but a con­tigu­ous in­creas­ing se­quence. Since there’s no ap­par­ent rhyme or reas­on in which the drivers al­loc­ate uni­form IDs, solu­tion was to dis­able the AR­B_­ex­pli­cit_uni­form_­loca­tion on In­tel Win­dows drivers al­to­geth­er.
  • It’s hard to find bugs in drivers that are cap­able of very little, but even then — while this bug was prob­ably present ever since Apple re­wrote their (de­prec­ated) GL 4.1 driver on top of Met­al, it got in­de­pend­ently dis­covered by two dif­fer­ent users only re­cently. When GL::Buf­fer­Tex­ture is bound, it causes all buf­fer modi­fic­a­tions to crash due to what I as­sume is cor­rup­tion of in­tern­al driver state. The work­around avoids the crash by un­bind­ing the tex­ture when up­dat­ing or map­ping a GL::Buf­fer.

For your amuse­ment, the list of all cur­rent OpenGL driver work­arounds is in the doc­u­ment­a­tion.

But there are also some non-neg­at­ive GL news, even

Last 50 frames:
  Frame time: 16.65 ms
  CPU duration: 14.72 ms
  GPU duration: 10.89 ms
  Vertex fetch ratio: 0.24
  Primitives clipped: 59.67 %

An ex­ample where pipeline stat­ist­ic quer­ies can be use­ful. Out­put from De­bug­Tools::FramePro­filer.

Work­ing on the geo­metry pipeline and large data­sets re­quired me to do vari­ous meas­ure­ments, which led to a new GL::Pipelin­eS­tat­ist­ic­sQuery class. It ex­poses the AR­B_pipeline_s­tat­ist­ic­s_query ex­ten­sion which doesn’t provide really much, but it’s at least some­thing — and sadly the only non-pro­pri­et­ary way to get any stats on NVidia drivers. A new De­bug­Tools::FramePro­filer class uses those quer­ies to give you a easy-to-in­teg­rate per-frame pro­fil­ing.

Apart from that, vari­ous tiny bits and pieces such as clip­ping plane sup­port were ad­ded, mostly on-de­mand based on needs of vari­ous users. See the changelog links at the bot­tom for the full list.

One quite minor but widely ap­pre­ci­ated change was turn­ing mesh.draw(shader) in­to shader.draw(mesh). The ori­gin­al was a res­ult of how the API evolved over the years, which is why I was blind to its coun­ter­in­tu­it­ive­ness un­til it was fi­nally poin­ted out to me. A good takeaway from that is — if you see any­thing in Mag­num APIs that feels strange or un­ne­ces­sar­ily com­plic­ated, please com­plain, no mat­ter how “noob” or in­ex­per­i­enced you might feel. Feed­back like this mat­ters a lot, and if I nev­er hear it, I might nev­er dis­cov­er the prob­lem.

One in­ter­est­ing pro­ject that is mak­ing great pro­gress re­cently is Mesa’s Zink OpenGL-over-Vulkan driver. Stable Mesa 20.1 doesn’t have it en­abled by de­fault yet and there it’s just at GL 2.1, but latest com­mits already bring it up to 3.1 sup­port. After fix­ing some bad as­sump­tions in con­text cre­ation routines in or­der to make pure GL 2.1 con­texts work again, Mag­num can now work with Zink as well.

Ap­plic­a­tion im­prove­ments

Thanks to a joint ef­fort from sev­er­al con­trib­ut­ors, Plat­form::GlfwAp­plic­a­tion and Plat­form::Sdl2Ap­plic­a­tion now sup­port curs­or man­age­ment, which is also used by the ImGui­In­teg­ra­tion lib­rary. Both ap­plic­a­tions can now also set win­dow icon, and if you are on Win­dows, you can use it to­geth­er with the new IcoIm­port­er to use one file to set an ex­ecut­able icon and a win­dow icon as well, op­tion­ally also provid­ing sev­er­al res­ol­u­tions to let the OS choose from.

The ImGui­In­teg­ra­tion lib­rary was switched to use built­in Shaders::Flat2D in­stead of a cus­tom shader, which re­moved quite some code and made it work on WebGL 1 as well. This was pos­sible thanks to built­in shaders re­ceiv­ing ver­tex col­or sup­port in the 2019.10 re­lease.

Win­dow­less apps, which are com­monly used for data pro­cessing or test­ing, got ex­ten­ded to sup­port con­text shar­ing. The Plat­form::Win­dow­lessE­glAp­plic­a­tion sup­ports EGL device se­lec­tion through the --magnum-device op­tion since 2019.10 and now it sup­ports also --magnum-cuda-device for fil­ter­ing only CUDA devices, if you’re run­ning on a ma­chine with NVidia GPUs.

If you don’t use the built­in ap­plic­a­tion wrap­pers, there’s a new base-gtkmm boot­strap pro­ject to get you star­ted us­ing GTKmm, join­ing base-wx­wid­gets ad­ded in the pre­vi­ous re­lease, and with a QtQuick boot­strap be­ing worked on for the next.

A gigaton of cool new ex­amples

The Con­trib­ut­or of the Year award goes to Nghia Truong — sub­mit­ting five ex­tremely in­ter­est­ing ex­amples, each im­ple­ment­ing a dif­fer­ent al­gorithm com­pletely from scratch, with many more good­ies prom­ised. All of them are now avail­able as WebGL demos, feel free to try them out:

2D Fluid Simulation example screenshot
2D Flu­id Sim­u­la­tion Ex­ample webgl2 phys­ics ui
2D flu­id sim­u­la­tion us­ing the APIC meth­od.
3D Fluid Simulation example screenshot
3D Flu­id Sim­u­la­tion Ex­ample webgl2 phys­ics ui
SPH flu­id sim­u­la­tion with a dy­nam­ic bound­ary.
Ray Tracing example screenshot
Ray Tra­cing webgl1
Simple it­er­at­ive CPU ray tra­cing.
Octree example screenshot
Octree webgl1 phys­ics in­stan­cing
Loose octree for ac­cel­er­at­ing col­li­sion de­tec­tion
Arc Ball example screenshot
Ar­cBall Cam­era Ex­ample webgl2
Im­ple­ment­a­tion of Ken Shoe­make’s ar­cball cam­era with smooth nav­ig­a­tion fea­ture.
WebXR example screenshot
WebXR Ex­ample webgl1 webxr
A ba­sic demon­stra­tion of how to use the Em­scripten WebXR lib­rary with Mag­num.

Apart from these, the ori­gin­al Web­VR API got ob­sol­eted by WebXR, and so fol­lows our ex­ample, linked above — cur­rently you can try it out in Chrome, and it also works in the An­droid browser.

Build­sys­tem im­prove­ments

Clang-CL is now an of­fi­cially sup­por­ted and tested com­piler, in case you want to build for Win­dows but hate both neither MinGW nor MS­VC suits your needs. Some work was done for MinGW Clang sup­port but ser­i­ous un­tackled is­sues still re­main, so GCC is still the only sup­por­ted com­piler un­der MinGW.

Be­cause de­pend­ency man­age­ment is hard un­less you have a sys­tem-wide pack­age man­ager or Vcp­kg do­ing the work for you, cer­tain de­pend­en­cies such as Open­AL, Basis Uni­ver­sal, mesh­op­tim­izer or ImGui can now be bundled as CMake sub­pro­jects. In ad­di­tion, vari­ous fixes were done in CMake Find mod­ules for stat­ic­ally-linked de­pend­en­cies that are com­monly used when dis­trib­ut­ing pro­ject bin­ar­ies.

To bet­ter track ver­sions of your de­pend­en­cies, all Mag­num pro­jects now con­tain a version.h head­er con­tain­ing the ex­act com­mit the lib­rary was built from.

Built­in CMake An­droid sup­port, which got broken with the in­tro­duc­tion of NDK r19, is work­ing with CMake 3.16+ again, only with min­im­al work­arounds. Build­ing doc­u­ment­a­tion and the An­droid troubleshoot­ing guide were up­dated to re­flect this fact.

New and im­proved tools

Go­ing with the new Trade::Ab­stractS­ceneCon­vert­er plu­gin in­ter­face, there’s a mag­num-scenecon­vert­er util­ity as well. Cur­rently the only sup­por­ted out­put is PLY (through Stan­ford­SceneCon­vert­er) and the amount of op­er­a­tions is lim­ited, but this area is go­ing to ex­pand over time, like it did for im­age con­ver­sion plu­gins. In ad­di­tion the tool also ex­poses vari­ous MeshTools al­gorithms such as du­plic­ate re­mov­al or at­trib­ute fil­ter­ing.

mag­num-im­age­con­vert­er can now con­sume and pro­duce raw pixel data of a spe­cified format, which is use­ful when deal­ing with low-level pipelines that don’t un­der­stand high-level im­age con­tain­er formats. For data dia­gnost­ic and de­bug­ging, both mag­num-im­age­con­vert­er and mag­num-scenecon­vert­er learned a new --info op­tion that prints in­form­a­tion about file con­tents, pixel / ver­tex formats and data lay­out.

The mag­num-play­er util­ity is heav­ily used for in­tern­al test­ing and thus ab­sorbed ba­sic­ally all new en­gine ad­di­tions — it can dis­play nor­mal maps, ex­poses all visu­al­iz­a­tion fea­tures of Shaders::MeshVisu­al­izer­3D and you can run De­bug­Tools::FramePro­filer with the P key. Only the nat­ive app has the new ad­di­tions right now, the web ver­sion wasn’t up­dated yet.

Mag­num Pro­ject Spot­light

To give you an idea what Mag­num is used for, here’s a se­lec­tion of cur­rently act­ive or re­cently pub­lished Mag­num-based pro­jects, with many more get­ting ready to ap­pear next. Presen­ted in no par­tic­u­lar or­der:


Ternarii

An ad­dict­ive and col­or­ful Tet­ris-in­spired puzzle game, run­ning in a browser. My highest score so far is 63k, beat me!


Oberon

A work-in-pro­gress light­weight game en­gine with an em­phas­is on us­ab­il­ity and per­form­ance.


Sculptron

GPU-based sculpt/an­im­ate ap­plic­a­tion. Cur­rently in al­pha.


[redacted]

Vhite Rab­bit’s yet-un­re­leased Web-fo­cused Game En­gine.


MINI

An N-body grav­ity sim­u­lat­or, cur­rently in pro­cess of be­ing por­ted to Mag­num. This an­nounce­ment cov­er im­age is a still frame from one of the sim­u­la­tions.

Quadratic Approximation of Cubic Curves

A simple de­gree re­duc­tion tech­nique for con­vert­ing piece­wise cu­bic splines in­to piece­wise quad­rat­ic splines that main­tain para­met­er­iz­a­tion and C^1 con­tinu­ity.


Sequentity

A single-file, im­me­di­ate-mode se­quen­cer wid­get for C++17, Dear ImGui and EnTT. Ex­ample powered by Mag­num.


Stillleben

Gen­er­ates real­ist­ic ar­range­ments of ri­gid bod­ies and provides vari­ous out­puts that can be used to train deep learn­ing mod­els.

mpFluid CAVE Front End

In­ter­act­ive Ex­plor­a­tion and Com­pu­ta­tion­al Steer­ing in CAVE-like En­vir­on­ments for High-Per­form­ance Flu­id Sim­u­la­tions. The CAVE-like en­vir­on­ment was fea­tured pre­vi­ously here.

If you have a pro­ject that you want to have men­tioned in the next Pro­ject Spot­light, let us know! For pro­jects hos­ted on Git­Hub don’t for­get to add the #mag­num tag to make them easi­er to dis­cov­er.

Full changelog

There’s so much hap­pen­ing that this art­icle is, as al­ways, just a dis­tilled ver­sion of the changelog — and I’m sure I for­got to men­tion some of the hid­den gems:

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

If you use Homebrew, Arch­Linux AUR or build your own *.deb pack­ages, the 2020.06 re­lease is already avail­able there. Vcp­kg pack­age up­date is cur­rently wait­ing for a merge in mi­crosoft/vcp­kg#12211, MSYS pack­ages are al­most ready in msys2/MINGW-pack­ages#6641 and Arch­Linux com­munity pack­age up­dates will fol­low shortly. If you use Mag­num Singles, those have all ad­di­tions from 2020.06 present.

If you have your code already us­ing the new Trade::Mesh­Data APIs, con­grats — you’re 95% there. If not, the new re­lease should mostly com­pile with your ex­ist­ing code, only emit a lot of de­prec­a­tion warn­ings where each will tell you what API to use in­stead.

Thank you

A sig­ni­fic­ant por­tion of the work on Mag­num is be­ing done by ex­tern­al con­trib­ut­ors, and this re­lease is no ex­cep­tion — thanks, every­body (and apo­lo­gies to those I for­got):