Browse Source

sceneconverter: add a --prefer option for plugin overrides.

The main use case is being able to specify what concrete plugin gets
used for a particular alias, e.g. to be able to use SpngImporter instead
of PngImporter for faster PNG image loading.

So far the option is implemented only here, as the imageconverter,
shaderconverter and other tools don't really deal with plugins
that delegate to other plugins. Yet.
pull/620/head
Vladimír Vondruš 3 years ago
parent
commit
13d0b0b1c7
  1. 3
      doc/changelog.dox
  2. 2
      src/Magnum/SceneTools/Test/CMakeLists.txt
  3. 107
      src/Magnum/SceneTools/Test/SceneConverterTest.cpp
  4. 4
      src/Magnum/SceneTools/Test/SceneConverterTestFiles/info-preferred-importer-plugin.txt
  5. BIN
      src/Magnum/SceneTools/Test/SceneConverterTestFiles/rgba.png
  6. 59
      src/Magnum/SceneTools/sceneconverter.cpp

3
doc/changelog.dox

@ -666,6 +666,9 @@ See also:
`--info-materials`, `--info-meshes`, `--info-skins` and `--info-textures`
for printing information just about particular data type, with `--info`
being a shortcut for all specified together
- Added a `--prefer` option to @ref magnum-sceneconverter "magnum-sceneconverter",
allowing to specify what plugins should be preferred for particular import
and conversion plugin aliases
@subsubsection changelog-latest-changes-shaders Shaders library

2
src/Magnum/SceneTools/Test/CMakeLists.txt

@ -124,6 +124,7 @@ if(CORRADE_TARGET_UNIX AND NOT CORRADE_PLUGINMANAGER_NO_DYNAMIC_PLUGIN_SUPPORT)
SceneConverterTestFiles/info-image-converter.txt
SceneConverterTestFiles/info-importer.txt
SceneConverterTestFiles/info-importer-ignored-input-output.txt
SceneConverterTestFiles/info-preferred-importer-plugin.txt
SceneConverterTestFiles/materials-3d.gltf
SceneConverterTestFiles/materials-pbr.gltf
SceneConverterTestFiles/materials-phong.mtl
@ -149,6 +150,7 @@ if(CORRADE_TARGET_UNIX AND NOT CORRADE_PLUGINMANAGER_NO_DYNAMIC_PLUGIN_SUPPORT)
SceneConverterTestFiles/red2x2.png
# magnum-imageconverter --layers red2x2.png --array red2x2x1.ktx2 -c writerName=
SceneConverterTestFiles/red2x2x1.ktx2
SceneConverterTestFiles/rgba.png # copied from PngImporter tests
SceneConverterTestFiles/two-quads-duplicates-fuzzy.bin
SceneConverterTestFiles/two-quads-duplicates-fuzzy.gltf
SceneConverterTestFiles/two-quads-duplicates.bin

107
src/Magnum/SceneTools/Test/SceneConverterTest.cpp

@ -108,7 +108,16 @@ const struct {
"-I", "ObjImporter", "--info", Utility::Path::join(SCENETOOLS_TEST_DIR, "SceneConverterTestFiles/point.obj"), "whatever.ply"
}},
"ObjImporter", nullptr, nullptr,
"info-data-ignored-output.txt"}
"info-data-ignored-output.txt"},
{"data, preferred importer plugin", {InPlaceInit, {
"-I", "AnyImageImporter", "--info",
/* Tested thoroughly in convert(preferred importer plugin), here it
just verifies that the option has an effect on --info as well */
"--prefer", "PngImporter:StbImageImporter", "-v",
Utility::Path::join(SCENETOOLS_TEST_DIR, "SceneConverterTestFiles/blue4x4.png")
}},
"StbImageImporter", nullptr, nullptr,
"info-preferred-importer-plugin.txt"},
};
const struct {
@ -786,6 +795,77 @@ const struct {
/* Compared to "data unsupported by the converter" this message is
printed by sceneconverter itself, not the converter interface */
"Ignoring 1 materials not supported by the converter\n"},
{"preferred importer plugin", {InPlaceInit, {
/* First is not found, second should be always found, third might
be also but shouldn't be picked. The trailing comma should be allowed, using the plugin itself in the list should work too. */
"--prefer", "PngImporter:Sdl3ImageImporter,StbImageImporter,SpngImporter,",
"-v",
Utility::Path::join(SCENETOOLS_TEST_DIR, "SceneConverterTestFiles/images-2d.gltf"),
Utility::Path::join(SCENETOOLS_TEST_OUTPUT_DIR, "SceneConverterTestFiles/images-2d.gltf")
}},
"GltfImporter", "StbImageImporter", "GltfSceneConverter",
{"PngImageConverter", nullptr}, nullptr,
/* Not checking either of the files, the verbose output is enough to
verify */
nullptr, nullptr,
"Trade::AnySceneImporter::openFile(): using GltfImporter\n"
"Trade::AnySceneConverter::beginFile(): using GltfSceneConverter\n"
"Trade::AbstractSceneConverter::addImporterContents(): adding 2D image 0 out of 2\n"
"Trade::AnyImageImporter::openFile(): using PngImporter (provided by StbImageImporter)\n"
"Trade::AbstractSceneConverter::addImporterContents(): adding 2D image 1 out of 2\n"
"Trade::AnyImageImporter::openFile(): using PngImporter (provided by StbImageImporter)\n"},
{"preferred image converter plugin", {InPlaceInit, {
/* The main logic was tested in "preferred importer plugin" above,
this just verifies that it works for image converters too. The
converter doesn't use AnyImageConverter so we can't rely on
verbose output, instead we convert a RGBA image to a JPEG
(embedded in a glTF) and check the warning message. */
"--prefer", "JpegImageConverter:StbImageConverter",
"-I", "PngImporter",
"-c", "imageConverter=JpegImageConverter",
Utility::Path::join(SCENETOOLS_TEST_DIR, "SceneConverterTestFiles/rgba.png"),
Utility::Path::join(SCENETOOLS_TEST_OUTPUT_DIR, "SceneConverterTestFiles/rgba.gltf")
}},
"PngImporter", nullptr, "GltfSceneConverter",
{"StbImageConverter", nullptr}, nullptr,
/* Not checking either of the files, the warning output is enough to
verify */
nullptr, nullptr,
"Trade::StbImageConverter::convertToData(): ignoring alpha channel for BMP/JPEG output\n"},
{"preferred scene converter plugin", {InPlaceInit, {
/* There aren't any alternative implementations for any scene
converters so can only ensure the code doesn't blow up if scene
converters are passed to --prefer */
"--prefer", "GltfSceneConverter:",
/* Removing the generator identifier for a roundtrip */
"-c", "generator=",
Utility::Path::join(SCENETOOLS_TEST_DIR, "SceneConverterTestFiles/empty.gltf"),
Utility::Path::join(SCENETOOLS_TEST_OUTPUT_DIR, "SceneConverterTestFiles/empty.gltf")
}},
"GltfImporter", nullptr, "GltfSceneConverter",
{}, nullptr,
/* It should give back the same file */
"empty.gltf", nullptr,
{}},
{"multiple --prefer options", {InPlaceInit, {
/* Basically a combination of "preferred importer plugin" and
"preferred image converter plugin" cases */
"--prefer", "PngImporter:StbImageImporter",
"--prefer", "JpegImageConverter:StbImageConverter",
"-I", "AnyImageImporter",
"-c", "imageConverter=JpegImageConverter", "-v",
Utility::Path::join(SCENETOOLS_TEST_DIR, "SceneConverterTestFiles/rgba.png"),
Utility::Path::join(SCENETOOLS_TEST_OUTPUT_DIR, "SceneConverterTestFiles/rgba.gltf")
}},
"PngImporter", nullptr, "GltfSceneConverter",
{"StbImageConverter", nullptr}, nullptr,
/* Not checking either of the files, the warning output is enough to
verify */
nullptr, nullptr,
"Trade::AnyImageImporter::openFile(): using PngImporter (provided by StbImageImporter)\n"
"Trade::AnySceneConverter::beginFile(): using GltfSceneConverter\n"
"Trade::AbstractSceneConverter::addImporterContents(): adding 2D image 0 out of 1\n"
"Trade::StbImageConverter::convertToData(): ignoring alpha channel for BMP/JPEG output\n"},
};
const struct {
@ -820,6 +900,27 @@ const struct {
}},
nullptr, nullptr, nullptr, nullptr,
"The --only-mesh-attributes option can only be used with --mesh or --concatenate-meshes\n"},
{"--prefer without a colon", {InPlaceInit, {
"--prefer", "PngImporter=StbImageImporter", "a", "b",
}},
nullptr, nullptr, nullptr, nullptr,
"Invalid --prefer option PngImporter=StbImageImporter\n"},
{"--prefer alias suffix unknown", {InPlaceInit, {
"--prefer", "TrueTypeFont:HarfBuzzFont", "a", "b",
}},
nullptr, nullptr, nullptr, nullptr,
"Alias TrueTypeFont not recognized for a --prefer option\n"},
{"--prefer alias name not found", {InPlaceInit, {
"--prefer", "FbxSceneConverter:UfbxSceneConverter", "a", "b",
}},
nullptr, nullptr, nullptr, nullptr,
"Alias FbxSceneConverter not found for a --prefer option\n"},
{"--prefer plugin doesn't provide alias", {InPlaceInit, {
"--prefer", "GltfImporter:UfbxImporter", "a", "b",
}},
/* UfbxImporter is not really an image importer but it works here */
"GltfImporter", "UfbxImporter", nullptr, nullptr,
"UfbxImporter doesn't provide GltfImporter for a --prefer option\n"},
{"can't load importer plugin", {InPlaceInit, {
/* Override also the plugin directory for consistent output */
"--plugin-dir", "nonexistent", "-I", "NonexistentImporter", "whatever.obj",
@ -1221,7 +1322,9 @@ void SceneConverterTest::convert() {
CORRADE_COMPARE(output.second(), data.message);
CORRADE_VERIFY(output.first());
CORRADE_COMPARE_AS(Utility::Path::join({SCENETOOLS_TEST_OUTPUT_DIR, "SceneConverterTestFiles", data.expected}),
/* In some cases the test verifies only the printed output and doesn't
check any file */
if(data.expected) CORRADE_COMPARE_AS(Utility::Path::join({SCENETOOLS_TEST_OUTPUT_DIR, "SceneConverterTestFiles", data.expected}),
Utility::Path::join({SCENETOOLS_TEST_DIR, "SceneConverterTestFiles", data.expected}),
TestSuite::Compare::File);
if(data.expected2) CORRADE_COMPARE_AS(Utility::Path::join({SCENETOOLS_TEST_OUTPUT_DIR, "SceneConverterTestFiles", data.expected2}),

4
src/Magnum/SceneTools/Test/SceneConverterTestFiles/info-preferred-importer-plugin.txt

@ -0,0 +1,4 @@
Trade::AnyImageImporter::openFile(): using PngImporter (provided by StbImageImporter)
2D image 0:
Level 0: {4, 4} @ RGB8Unorm (0.0 kB)
Total image data size: 0.0 kB

BIN
src/Magnum/SceneTools/Test/SceneConverterTestFiles/rgba.png

Binary file not shown.

After

Width:  |  Height:  |  Size: 92 B

59
src/Magnum/SceneTools/sceneconverter.cpp

@ -179,7 +179,8 @@ magnum-sceneconverter scene.gltf scene.decimated.gltf \
@code{.sh}
magnum-sceneconverter [-h|--help] [-I|--importer PLUGIN]
[-C|--converter PLUGIN]... [-P|--image-converter PLUGIN]...
[-M|--mesh-converter PLUGIN]... [--plugin-dir DIR] [--map]
[-M|--mesh-converter PLUGIN]... [--plugin-dir DIR]
[--prefer alias:plugin1,plugin2,]... [--map]
[--only-mesh-attributes N1,N2-N3] [--remove-duplicate-vertices]
[--remove-duplicate-vertices-fuzzy EPSILON] [--phong-to-pbr]
[-i|--importer-options key=val,key2=val2,]
@ -209,6 +210,8 @@ Arguments:
- `-M`, `--mesh-converter PLUGIN` --- converter plugin(s) to apply to each
mesh in the scene
- `--plugin-dir DIR` --- override base plugin dir
- `--prefer alias:plugin1,plugin2,` --- prefer particular plugins for given
alias(es)
- `--map` --- memory-map the input for zero-copy import (works only for
standalone files)
- `--only-mesh-attributes N1,N2-N3` --- include only mesh attributes of
@ -413,6 +416,7 @@ int main(int argc, char** argv) {
.addArrayOption('P', "image-converter").setHelp("image-converter", "converter plugin(s) to apply to each image in the scene", "PLUGIN")
.addArrayOption('M', "mesh-converter").setHelp("mesh-converter", "converter plugin(s) to apply to each mesh in the scene", "PLUGIN")
.addOption("plugin-dir").setHelp("plugin-dir", "override base plugin dir", "DIR")
.addArrayOption("prefer").setHelp("prefer", "prefer particular plugins for given alias(es)", "alias:plugin1,plugin2,…")
#if defined(CORRADE_TARGET_UNIX) || (defined(CORRADE_TARGET_WINDOWS) && !defined(CORRADE_TARGET_WINDOWS_RT))
.addBooleanOption("map").setHelp("map", "memory-map the input for zero-copy import (works only for standalone files)")
#endif
@ -578,6 +582,59 @@ well, the IDs reference attributes of the first mesh.)")
Utility::Path::join(args.value("plugin-dir"), Utility::Path::split(Trade::AbstractSceneConverter::pluginSearchPaths().back()).second())};
converterManager.registerExternalManager(imageConverterManager);
/* Set preferred plugins */
for(std::size_t i = 0, iMax = args.arrayValueCount("prefer"); i != iMax; ++i) {
const auto value = args.arrayValue<Containers::StringView>("prefer", i);
const Containers::Array3<Containers::StringView> aliasNames = value.partition(':');
if(!aliasNames[1]) {
Error{} << "Invalid --prefer option" << value;
return 1;
}
/* Figure out manager name */
PluginManager::AbstractManager* manager;
if(aliasNames[0].hasSuffix("Importer"_s))
manager = &importerManager;
else if(aliasNames[0].hasSuffix("ImageConverter"_s))
manager = &imageConverterManager;
else if(aliasNames[0].hasSuffix("SceneConverter"_s))
manager = &converterManager;
else {
Error{} << "Alias" << aliasNames[0] << "not recognized for a --prefer option";
return 1;
}
/* The alias has to be found, otherwise it'd assert */
if(manager->loadState(aliasNames[0]) == PluginManager::LoadState::NotFound) {
Error{} << "Alias" << aliasNames[0] << "not found for a --prefer option";
return 1;
}
/* Check that the names actually provide given alias, otherwise it'd
assert */
const Containers::Array<Containers::StringView> names = aliasNames[2].splitWithoutEmptyParts(',');
for(const Containers::StringView name: names) {
/* Not found plugins are allowed in the list */
const PluginManager::PluginMetadata* const metadata = manager->metadata(name);
if(!metadata)
continue;
bool found = false;
for(const Containers::StringView provides: metadata->provides()) {
if(provides == aliasNames[0]) {
found = true;
break;
}
}
if(!found) {
Error{} << name << "doesn't provide" << aliasNames[0] << "for a --prefer option";
return 1;
}
}
manager->setPreferredPlugins(aliasNames[0], names);
}
/* Print plugin info, if requested */
/** @todo these all duplicate plugin loading & option setting, move to
some helpers (shared among all command-line tools)? */

Loading…
Cancel
Save