I made a brief de­tour on the way to Vulkan sup­port and equipped Mag­num with fea­tures that make plug­in work­flow nicer and open up new pos­si­bil­i­ties.

Au­tode­tect­ed plug­in paths

Un­til now, in or­der to load a dy­nam­ic plug­in, one had to spec­i­fy a path where to look for them. That was as flex­i­ble as it could get, yes, but in most cas­es the plug­ins would be in one or two com­mon places and so the plug­in us­age usu­al­ly in­volved just prop­a­gat­ing a well-known path from CMake via configure_file().

Well, not any­more!

Ev­ery plug­in in­ter­face now has a few plug­in search paths pre­de­fined, which will “just work” in most cas­es. For ex­am­ple, in case of *Im­porter plug­ins, the plug­ins are first searched in a magnum/importers/ sub­di­rec­to­ry next to the ex­e­cutable (which is a com­mon case when de­ploy­ing on Win­dows) and as a fall­back in /usr/lib/magnum/importers (or any oth­er sys­tem-wide lo­ca­tion de­pend­ing on CMAKE_INSTALL_PREFIX). See doc­u­men­ta­tion of var­i­ous plu­g­in­Search­Paths() func­tions for de­tails.

With this change in place, load­ing a mod­el from a glTF file is now just a mat­ter 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"));

The flex­i­bil­i­ty was not tak­en away, though — it’s still pos­si­ble to pass the plug­in di­rec­to­ry to the con­struc­tor the same as be­fore, if you need to.

Plug­in alias pri­or­i­ties

There’s usu­al­ly more than one plug­in avail­able to achieve a par­tic­u­lar goal — for ex­am­ple in or­der to open a PNG file, you can choose among PngIm­porter, Dev­Il­Im­ageIm­porter or StbIm­ageIm­porter. Rather than this be­ing an un­nec­es­sary re­dun­dan­cy, it al­lows you to pick a par­tic­u­lar per­for­mance / porta­bil­i­ty / fea­ture-set trade­off — a plug­in with no ex­ter­nal de­pen­den­cies for a web build or, on the oth­er hand, the fastest pos­si­ble im­ple­men­ta­tion for a tool that does heavy im­age pro­cess­ing.

To make this sim­pler in the code and de­fer the de­ci­sion to the buildsys­tem or app de­ploy­ment, all plug­ins that sup­port a par­tic­u­lar for­mat pro­vide a com­mon alias — in case of PNG im­ages, you can just load a "PngImporter" plug­in and if PngIm­porter is not avail­able, it will pick up any of the oth­er can­di­dates.

For greater con­trol you can now use set­Pre­ferred­Plu­g­ins(), giv­ing a pri­or­i­tized can­di­date list for a par­tic­u­lar alias. This is es­pe­cial­ly use­ful with font plug­ins, where you can get ad­vanced lay­out ca­pa­bil­i­ties if the Harf­Buz­z­Font plug­in is avail­able or at least a faster and smoother glyph ren­der­ing if you can get the FreeType­Font plug­in. If none of the sug­ges­tions is avail­able, it falls back to what­ev­er is left (which can be, for ex­am­ple, the Stb­True­Type­Font plug­in).

PluginManager::Manager<Text::AbstractFont> manager;
manager.setPreferredPlugins("TrueTypeFont", {"HarfBuzzFont", "FreeTypeFont"});

This of course works al­so in com­bi­na­tion with the Any­Im­porter plug­ins — the fol­low­ing snip­pet will use Dev­IL in­stead of the builtin DdsIm­porter to de­code a DXT-com­pressed tex­ture in­to plain RG­BA on load:

PluginManager::Manager<Trade::AbstractImporter> manager;
manager.setPreferredPlugins("DdsImporter", {"DevIlImageImporter"});
std::unique_ptr<Trade::AbstractImporter> importer =
    manager.loadAndInstantiate("AnyImageImporter");
importer->openFile("texture.dds");

Plug­in-spe­cif­ic con­fig­u­ra­tion

Be­cause it’s not pos­si­ble for a gen­er­al stat­i­cal­ly typed plug­in API to ex­pose all pos­si­ble knobs and switch­es that a file for­mat could sup­port, the plug­ins have a pos­si­bil­i­ty to sup­ply ad­di­tion­al con­fig­u­ra­tion via the con­fig­u­ra­tion() func­tion. For ex­am­ple, in the As­simpIm­porter plug­in you can tog­gle var­i­ous post­pro­cess­ing steps that are ap­plied to load­ed scene files:

std::unique_ptr<Trade::AbstractImporter> importer =
    manager.loadAndInstantiate("AssimpImporter");
importer->configuration().group("postprocess")->setValue("PreTransformVertices", true);

This is just the first spring flow­er, ex­pect more func­tion­al­i­ty be­ing ex­posed through plug­in-spe­cif­ic con­fig­u­ra­tion in the fu­ture — abil­i­ty to con­trol out­put qual­i­ty of im­age con­vert­ers, con­trol­ling ad­vanced text lay­out­ing func­tion­al­i­ty of font plug­ins, …

Au­tomag­ic im­port of stat­ic plug­ins

There are plat­forms on which us­ing dlopen() and equiv­a­lents is ei­ther straight im­pos­si­ble or too an­noy­ing. For such cas­es there are stat­ic plug­ins. If you are us­ing CMake, all you need to do is list the re­quired plug­ins in a find_package() call (which was not need­ed for dy­nam­ic plug­ins) and then target_link_libraries() them to your fi­nal ex­e­cutable:

find_package(MagnumPlugins REQUIRED
    TinyGltfImporter
    StbTrueTypeFont)

add_executable(my-app my-app.cpp)
target_link_libraries(my-app PRIVATE
    MagnumPlugins::TinyGltfImporter
    MagnumPlugins::StbTrueTypeFont)

That’s all, you don’t need to change a sin­gle bit of your C++ code, a CMake target_sources() com­mand does all the mag­ic be­hind. If you’re not us­ing CMake but for ex­am­ple Vis­ual Stu­dio with Vcp­kg, you need to ex­plic­it­ly #include the “stat­ic plug­in im­port” file in­stead:

/* No need to do this if you use CMake */
#include <MagnumPlugins/TinyGltfImporter/importStaticPlugin.cpp>
#include <MagnumPlugins/StbTrueTypeFont/importStaticPlugin.cpp>

Now, where do I get this?

If you are al­ready hap­pi­ly us­ing Mag­num, just grab lat­est master re­vi­sions and don’t for­get to up­date your lo­cal copies of FindCorrade.cmake, FindMagnum.cmake and FindMagnumPlugins.cmake mod­ules to make the new fea­tures work.

If you are not us­ing Mag­num yet, wel­come! We prob­a­bly al­ready have a pack­age for your plat­form to get you start­ed in no time. Head over to the Get­ting Start­ed guide and have fun!

There’s more!

Thanks to an ex­cep­tion­al first-time con­tri­bu­tion from @Nussknack­erXXL and help from @Squareys there’s now a fresh TinyGlt­fIm­porter plug­in for im­port­ing both text and bi­na­ry glTF 2.0 files. Be­sides that — and be­sides the on­go­ing Vulkan work — a lot of ef­fort went in­to the doc­u­men­ta­tion, see for ex­am­ple the new ex­ten­sive An­droid de­vel­op­ment guide.