From 7c5089617a6d2936f459e7a6f2fcc5f2dbe5b5e9 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Vladim=C3=ADr=20Vondru=C5=A1?= Date: Wed, 21 Sep 2022 01:15:26 +0200 Subject: [PATCH] sceneconverter: ability to apply a converter to each image in the scene. --- src/Magnum/SceneTools/Test/CMakeLists.txt | 16 + .../SceneTools/Test/SceneConverterTest.cpp | 285 ++++++++++++++---- .../Test/SceneConverterTestFiles/blue4x4.png | Bin 0 -> 77 bytes .../SceneConverterTestFiles/blue4x4x1.ktx2 | Bin 0 -> 252 bytes .../broken-image-2d.gltf | 10 + .../broken-image-3d.gltf | 26 ++ .../Test/SceneConverterTestFiles/dxt1.dds | Bin 0 -> 136 bytes .../SceneConverterTestFiles/image-dds.gltf | 13 + .../SceneConverterTestFiles/images-2d-1x1.bin | Bin 0 -> 138 bytes .../images-2d-1x1.gltf | 34 +++ .../SceneConverterTestFiles/images-2d.gltf | 14 + .../images-3d-1x1x1.bin | Bin 0 -> 510 bytes .../images-3d-1x1x1.gltf | 58 ++++ .../SceneConverterTestFiles/images-3d.gltf | 38 +++ .../Test/SceneConverterTestFiles/red2x2.png | Bin 0 -> 79 bytes .../SceneConverterTestFiles/red2x2x1.ktx2 | Bin 0 -> 216 bytes src/Magnum/SceneTools/Test/configure.h.cmake | 4 + src/Magnum/SceneTools/sceneconverter.cpp | 194 +++++++++++- 18 files changed, 617 insertions(+), 75 deletions(-) create mode 100644 src/Magnum/SceneTools/Test/SceneConverterTestFiles/blue4x4.png create mode 100644 src/Magnum/SceneTools/Test/SceneConverterTestFiles/blue4x4x1.ktx2 create mode 100644 src/Magnum/SceneTools/Test/SceneConverterTestFiles/broken-image-2d.gltf create mode 100644 src/Magnum/SceneTools/Test/SceneConverterTestFiles/broken-image-3d.gltf create mode 100644 src/Magnum/SceneTools/Test/SceneConverterTestFiles/dxt1.dds create mode 100644 src/Magnum/SceneTools/Test/SceneConverterTestFiles/image-dds.gltf create mode 100644 src/Magnum/SceneTools/Test/SceneConverterTestFiles/images-2d-1x1.bin create mode 100644 src/Magnum/SceneTools/Test/SceneConverterTestFiles/images-2d-1x1.gltf create mode 100644 src/Magnum/SceneTools/Test/SceneConverterTestFiles/images-2d.gltf create mode 100644 src/Magnum/SceneTools/Test/SceneConverterTestFiles/images-3d-1x1x1.bin create mode 100644 src/Magnum/SceneTools/Test/SceneConverterTestFiles/images-3d-1x1x1.gltf create mode 100644 src/Magnum/SceneTools/Test/SceneConverterTestFiles/images-3d.gltf create mode 100644 src/Magnum/SceneTools/Test/SceneConverterTestFiles/red2x2.png create mode 100644 src/Magnum/SceneTools/Test/SceneConverterTestFiles/red2x2x1.ktx2 diff --git a/src/Magnum/SceneTools/Test/CMakeLists.txt b/src/Magnum/SceneTools/Test/CMakeLists.txt index d146389fc..a9f30d050 100644 --- a/src/Magnum/SceneTools/Test/CMakeLists.txt +++ b/src/Magnum/SceneTools/Test/CMakeLists.txt @@ -59,9 +59,22 @@ corrade_add_test(SceneToolsSceneConverterTest SceneConverterTest.cpp # executable so plugin existence checks are consistent between the two ${MAGNUM_SCENECONVERTER_STATIC_PLUGINS} FILES + SceneConverterTestFiles/blue4x4.png + # magnum-imageconverter --layers blue4x4.png --array blue4x4x1.ktx2 -c writerName= + SceneConverterTestFiles/blue4x4x1.ktx2 + SceneConverterTestFiles/broken-image-2d.gltf + SceneConverterTestFiles/broken-image-3d.gltf SceneConverterTestFiles/broken-mesh.obj SceneConverterTestFiles/broken-scene.gltf + SceneConverterTestFiles/dxt1.dds SceneConverterTestFiles/empty.gltf + SceneConverterTestFiles/image-dds.gltf + SceneConverterTestFiles/images-2d.gltf + SceneConverterTestFiles/images-2d-1x1.bin + SceneConverterTestFiles/images-2d-1x1.gltf + SceneConverterTestFiles/images-3d.gltf + SceneConverterTestFiles/images-3d-1x1x1.bin + SceneConverterTestFiles/images-3d-1x1x1.gltf SceneConverterTestFiles/info-animations.txt SceneConverterTestFiles/info-cameras.txt SceneConverterTestFiles/info-images.txt @@ -90,6 +103,9 @@ corrade_add_test(SceneToolsSceneConverterTest SceneConverterTest.cpp SceneConverterTestFiles/quad.gltf SceneConverterTestFiles/quad.obj SceneConverterTestFiles/quad.ply + SceneConverterTestFiles/red2x2.png + # magnum-imageconverter --layers red2x2.png --array red2x2x1.ktx2 -c writerName= + SceneConverterTestFiles/red2x2x1.ktx2 SceneConverterTestFiles/two-quads-duplicates-fuzzy.bin SceneConverterTestFiles/two-quads-duplicates-fuzzy.gltf SceneConverterTestFiles/two-quads-duplicates.bin diff --git a/src/Magnum/SceneTools/Test/SceneConverterTest.cpp b/src/Magnum/SceneTools/Test/SceneConverterTest.cpp index 117e54142..4b9622333 100644 --- a/src/Magnum/SceneTools/Test/SceneConverterTest.cpp +++ b/src/Magnum/SceneTools/Test/SceneConverterTest.cpp @@ -36,6 +36,7 @@ #include "Magnum/Math/CubicHermite.h" #include "Magnum/Math/Matrix3.h" #include "Magnum/Math/Matrix4.h" +#include "Magnum/Trade/AbstractImageConverter.h" #include "Magnum/Trade/AbstractSceneConverter.h" #include "Magnum/SceneTools/Implementation/sceneConverterUtilities.h" @@ -116,7 +117,10 @@ const struct { const char* name; Containers::Array args; const char* requiresImporter; + const char* requiresImporter2; const char* requiresConverter; + /* One for image, one for file conversion */ + const char* requiresImageConverter[2]; const char* requiresMeshConverter; const char* expected; const char* expected2; @@ -124,23 +128,23 @@ const struct { } ConvertData[]{ {"one mesh", Containers::array({ Utility::Path::join(SCENETOOLS_TEST_DIR, "SceneConverterTestFiles/quad.obj"), Utility::Path::join(SCENETOOLS_TEST_OUTPUT_DIR, "SceneConverterTestFiles/quad.ply")}), - "ObjImporter", "StanfordSceneConverter", nullptr, + "ObjImporter", nullptr, "StanfordSceneConverter", {}, nullptr, "quad.ply", nullptr, {}}, {"one mesh, whole scene converter", Containers::array({ Utility::Path::join(SCENETOOLS_TEST_DIR, "SceneConverterTestFiles/quad.obj"), Utility::Path::join(SCENETOOLS_TEST_OUTPUT_DIR, "SceneConverterTestFiles/quad.gltf")}), - "ObjImporter", "GltfSceneConverter", nullptr, + "ObjImporter", nullptr, "GltfSceneConverter", {}, nullptr, "quad.gltf", "quad.bin", {}}, {"one mesh, explicit importer and converter", Containers::array({ "-I", "ObjImporter", "-C", "StanfordSceneConverter", Utility::Path::join(SCENETOOLS_TEST_DIR, "SceneConverterTestFiles/quad.obj"), Utility::Path::join(SCENETOOLS_TEST_OUTPUT_DIR, "SceneConverterTestFiles/quad.ply")}), - "ObjImporter", "StanfordSceneConverter", nullptr, + "ObjImporter", nullptr, "StanfordSceneConverter", {}, nullptr, "quad.ply", nullptr, {}}, {"one mesh, map", Containers::array({ Utility::Path::join(SCENETOOLS_TEST_DIR, "SceneConverterTestFiles/quad.obj"), Utility::Path::join(SCENETOOLS_TEST_OUTPUT_DIR, "SceneConverterTestFiles/quad.ply")}), - "ObjImporter", "StanfordSceneConverter", nullptr, + "ObjImporter", nullptr, "StanfordSceneConverter", {}, nullptr, "quad.ply", nullptr, {}}, {"one mesh, options", Containers::array({ @@ -149,7 +153,7 @@ const struct { just verify the (nonexistent) options arrive there */ "-i", "nonexistentOption=13", "-c", "nonexistentConverterOption=26", Utility::Path::join(SCENETOOLS_TEST_DIR, "SceneConverterTestFiles/quad.obj"), Utility::Path::join(SCENETOOLS_TEST_OUTPUT_DIR, "SceneConverterTestFiles/quad.ply")}), - "ObjImporter", "StanfordSceneConverter", nullptr, + "ObjImporter", nullptr, "StanfordSceneConverter", {}, nullptr, "quad.ply", nullptr, "Trade::AnySceneImporter::openFile(): option nonexistentOption not recognized by ObjImporter\n" "Trade::AnySceneConverter::beginFile(): option nonexistentConverterOption not recognized by StanfordSceneConverter\n"}, @@ -160,7 +164,7 @@ const struct { "-i", "nonexistentOption=13", "-c", "nonexistentConverterOption=26", "-I", "ObjImporter", "-C", "StanfordSceneConverter", Utility::Path::join(SCENETOOLS_TEST_DIR, "SceneConverterTestFiles/quad.obj"), Utility::Path::join(SCENETOOLS_TEST_OUTPUT_DIR, "SceneConverterTestFiles/quad.ply")}), - "ObjImporter", "StanfordSceneConverter", nullptr, + "ObjImporter", nullptr, "StanfordSceneConverter", {}, nullptr, "quad.ply", nullptr, "Option nonexistentOption not recognized by ObjImporter\n" "Option nonexistentConverterOption not recognized by StanfordSceneConverter\n"}, @@ -168,20 +172,20 @@ const struct { /* Removing the generator identifier to have the file fully roundtrip */ "-c", "generator=", Utility::Path::join(SCENETOOLS_TEST_DIR, "SceneConverterTestFiles/two-quads.gltf"), Utility::Path::join(SCENETOOLS_TEST_OUTPUT_DIR, "SceneConverterTestFiles/two-quads.gltf")}), - "GltfImporter", "GltfSceneConverter", nullptr, + "GltfImporter", nullptr, "GltfSceneConverter", {}, nullptr, /* There should be a minimal difference compared to the original */ "two-quads.gltf", "two-quads.bin", {}}, {"concatenate meshes without a scene", Containers::array({ "--concatenate-meshes", Utility::Path::join(SCENETOOLS_TEST_DIR, "SceneConverterTestFiles/two-triangles.obj"), Utility::Path::join(SCENETOOLS_TEST_OUTPUT_DIR, "SceneConverterTestFiles/quad-duplicates.ply")}), - "ObjImporter", "StanfordSceneConverter", nullptr, + "ObjImporter", nullptr, "StanfordSceneConverter", {}, nullptr, "quad-duplicates.ply", nullptr, {}}, {"concatenate meshes with a scene", Containers::array({ "--concatenate-meshes", Utility::Path::join(SCENETOOLS_TEST_DIR, "SceneConverterTestFiles/two-triangles-transformed.gltf"), Utility::Path::join(SCENETOOLS_TEST_OUTPUT_DIR, "SceneConverterTestFiles/quad-duplicates.ply")}), - "GltfImporter", "StanfordSceneConverter", nullptr, + "GltfImporter", nullptr, "StanfordSceneConverter", {}, nullptr, "quad-duplicates.ply", nullptr, {}}, /** @todo drop --mesh once it's not needed anymore again, then add a @@ -190,19 +194,19 @@ const struct { /* Only 0 gets picked from here, others ignored */ "--mesh", "0", "--only-mesh-attributes", "17,0,25-36", Utility::Path::join(SCENETOOLS_TEST_DIR, "SceneConverterTestFiles/quad-normals-texcoords.obj"), Utility::Path::join(SCENETOOLS_TEST_OUTPUT_DIR, "SceneConverterTestFiles/quad.ply")}), - "ObjImporter", "StanfordSceneConverter", nullptr, + "ObjImporter", nullptr, "StanfordSceneConverter", {}, nullptr, "quad.ply", nullptr, {}}, {"concatenate meshes, filter mesh attributes", Containers::array({ "--concatenate-meshes", "--only-mesh-attributes", "17,0,25-36", Utility::Path::join(SCENETOOLS_TEST_DIR, "SceneConverterTestFiles/quad-normals-texcoords.obj"), Utility::Path::join(SCENETOOLS_TEST_OUTPUT_DIR, "SceneConverterTestFiles/quad.ply")}), - "ObjImporter", "StanfordSceneConverter", nullptr, + "ObjImporter", nullptr, "StanfordSceneConverter", {}, nullptr, "quad.ply", nullptr, {}}, {"one implicit mesh, remove vertex duplicates", Containers::array({ "--remove-duplicate-vertices", Utility::Path::join(SCENETOOLS_TEST_DIR, "SceneConverterTestFiles/quad-duplicates.obj"), Utility::Path::join(SCENETOOLS_TEST_OUTPUT_DIR, "SceneConverterTestFiles/quad.ply")}), - "ObjImporter", "StanfordSceneConverter", nullptr, + "ObjImporter", nullptr, "StanfordSceneConverter", {}, nullptr, "quad.ply", nullptr, {}}, {"one implicit mesh, remove duplicate vertices, verbose", Containers::array({ @@ -210,7 +214,7 @@ const struct { AnySceneConverter delegation messages */ "--remove-duplicate-vertices", "-v", "-I", "ObjImporter", "-C", "StanfordSceneConverter", Utility::Path::join(SCENETOOLS_TEST_DIR, "SceneConverterTestFiles/quad-duplicates.obj"), Utility::Path::join(SCENETOOLS_TEST_OUTPUT_DIR, "SceneConverterTestFiles/quad.ply")}), - "ObjImporter", "StanfordSceneConverter", nullptr, + "ObjImporter", nullptr, "StanfordSceneConverter", {}, nullptr, "quad.ply", nullptr, "Mesh 0 duplicate removal: 6 -> 4 vertices\n"}, {"one selected mesh, remove duplicate vertices, verbose", Containers::array({ @@ -218,7 +222,7 @@ const struct { AnySceneConverter delegation messages */ "--mesh", "1", "--remove-duplicate-vertices", "-v", "-I", "GltfImporter", "-C", "StanfordSceneConverter", Utility::Path::join(SCENETOOLS_TEST_DIR, "SceneConverterTestFiles/two-quads-duplicates.gltf"), Utility::Path::join(SCENETOOLS_TEST_OUTPUT_DIR, "SceneConverterTestFiles/quad.ply")}), - "GltfImporter", "StanfordSceneConverter", nullptr, + "GltfImporter", nullptr, "StanfordSceneConverter", {}, nullptr, /* The second mesh in the glTF is deliberately the same as in quad-duplicates.obj, so this produces the same file */ "quad.ply", nullptr, @@ -230,7 +234,7 @@ const struct { /* Removing the generator identifier for a smaller file */ "-c", "generator=", Utility::Path::join(SCENETOOLS_TEST_DIR, "SceneConverterTestFiles/two-quads-duplicates.gltf"), Utility::Path::join(SCENETOOLS_TEST_OUTPUT_DIR, "SceneConverterTestFiles/two-quads.gltf")}), - "GltfImporter", "GltfSceneConverter", nullptr, + "GltfImporter", nullptr, "GltfSceneConverter", {}, nullptr, /* There should be a minimal difference compared to the original */ "two-quads.gltf", "two-quads.bin", "Mesh 0 duplicate removal: 5 -> 4 vertices\n" @@ -238,7 +242,7 @@ const struct { {"one implicit mesh, remove duplicate vertices fuzzy", Containers::array({ "--remove-duplicate-vertices-fuzzy", "1.0e-1", Utility::Path::join(SCENETOOLS_TEST_DIR, "SceneConverterTestFiles/quad-duplicates-fuzzy.obj"), Utility::Path::join(SCENETOOLS_TEST_OUTPUT_DIR, "SceneConverterTestFiles/quad.ply")}), - "ObjImporter", "StanfordSceneConverter", nullptr, + "ObjImporter", nullptr, "StanfordSceneConverter", {}, nullptr, "quad.ply", nullptr, {}}, {"one implicit mesh, remove duplicate vertices fuzzy, verbose", Containers::array({ @@ -246,7 +250,7 @@ const struct { AnySceneConverter delegation messages */ "--remove-duplicate-vertices-fuzzy", "1.0e-1", "-v", "-I", "ObjImporter", "-C", "StanfordSceneConverter", Utility::Path::join(SCENETOOLS_TEST_DIR, "SceneConverterTestFiles/quad-duplicates-fuzzy.obj"), Utility::Path::join(SCENETOOLS_TEST_OUTPUT_DIR, "SceneConverterTestFiles/quad.ply")}), - "ObjImporter", "StanfordSceneConverter", nullptr, + "ObjImporter", nullptr, "StanfordSceneConverter", {}, nullptr, "quad.ply", nullptr, "Mesh 0 fuzzy duplicate removal: 6 -> 4 vertices\n"}, {"one selected mesh, remove duplicate vertices fuzzy, verbose", Containers::array({ @@ -254,7 +258,7 @@ const struct { AnySceneConverter delegation messages */ "--mesh 1", "--remove-duplicate-vertices-fuzzy", "1.0e-1", "-v", "-I", "GltfImporter", "-C", "StanfordSceneConverter", Utility::Path::join(SCENETOOLS_TEST_DIR, "SceneConverterTestFiles/two-quads-duplicates-fuzzy.gltf"), Utility::Path::join(SCENETOOLS_TEST_OUTPUT_DIR, "SceneConverterTestFiles/quad.ply")}), - "GltfImporter", "StanfordSceneConverter", nullptr, + "GltfImporter", nullptr, "StanfordSceneConverter", {}, nullptr, /* The second mesh in the glTF is deliberately the same as in quad-duplicates-fuzzy.obj, so this produces the same file */ "quad.ply", nullptr, @@ -266,26 +270,26 @@ const struct { /* Removing the generator identifier for a smaller file */ "-c", "generator=", Utility::Path::join(SCENETOOLS_TEST_DIR, "SceneConverterTestFiles/two-quads-duplicates-fuzzy.gltf"), Utility::Path::join(SCENETOOLS_TEST_OUTPUT_DIR, "SceneConverterTestFiles/two-quads.gltf")}), - "GltfImporter", "GltfSceneConverter", nullptr, + "GltfImporter", nullptr, "GltfSceneConverter", {}, nullptr, "two-quads.gltf", "two-quads.bin", "Mesh 0 fuzzy duplicate removal: 5 -> 4 vertices\n" "Mesh 1 fuzzy duplicate removal: 6 -> 4 vertices\n"}, {"one implicit mesh, two converters", Containers::array({ "-C", "MeshOptimizerSceneConverter", Utility::Path::join(SCENETOOLS_TEST_DIR, "SceneConverterTestFiles/quad-strip.gltf"), Utility::Path::join(SCENETOOLS_TEST_OUTPUT_DIR, "SceneConverterTestFiles/quad.gltf")}), - "GltfImporter", "GltfSceneConverter", nullptr, + "GltfImporter", nullptr, "GltfSceneConverter", {}, nullptr, "quad.gltf", "quad.bin", {}}, {"one implicit mesh, two converters, explicit last", Containers::array({ "-C", "MeshOptimizerSceneConverter", "-C", "GltfSceneConverter", Utility::Path::join(SCENETOOLS_TEST_DIR, "SceneConverterTestFiles/quad-strip.gltf"), Utility::Path::join(SCENETOOLS_TEST_OUTPUT_DIR, "SceneConverterTestFiles/quad.gltf")}), - "GltfImporter", "GltfSceneConverter", nullptr, + "GltfImporter", nullptr, "GltfSceneConverter", {}, nullptr, "quad.gltf", "quad.bin", {}}, {"one implicit mesh, two converters, verbose", Containers::array({ "-C", "MeshOptimizerSceneConverter", "-v", Utility::Path::join(SCENETOOLS_TEST_DIR, "SceneConverterTestFiles/quad-strip.gltf"), Utility::Path::join(SCENETOOLS_TEST_OUTPUT_DIR, "SceneConverterTestFiles/quad.gltf")}), - "GltfImporter", "GltfSceneConverter", nullptr, + "GltfImporter", nullptr, "GltfSceneConverter", {}, nullptr, "quad.gltf", "quad.bin", /* While this looks like a no-op in the output, it converts a triangle strip to indexed triangles, which verifies that the output @@ -309,7 +313,7 @@ const struct { {"one implicit mesh, two converters, explicit last, verbose", Containers::array({ "-C", "MeshOptimizerSceneConverter", "-C", "GltfSceneConverter", "-v", Utility::Path::join(SCENETOOLS_TEST_DIR, "SceneConverterTestFiles/quad-strip.gltf"), Utility::Path::join(SCENETOOLS_TEST_OUTPUT_DIR, "SceneConverterTestFiles/quad.gltf")}), - "GltfImporter", "GltfSceneConverter", nullptr, + "GltfImporter", nullptr, "GltfSceneConverter", {}, nullptr, "quad.gltf", "quad.bin", /* As the importers and converters are specified explicitly, there's no messages from AnySceneConverter, OTOH as we have more than one -C @@ -334,7 +338,7 @@ const struct { "-C", "MeshOptimizerSceneConverter", "-c", "nonexistentMeshOptimizerOption=yes", Utility::Path::join(SCENETOOLS_TEST_DIR, "SceneConverterTestFiles/quad-strip.gltf"), Utility::Path::join(SCENETOOLS_TEST_OUTPUT_DIR, "SceneConverterTestFiles/quad.ply")}), - "GltfImporter", "GltfSceneConverter", nullptr, + "GltfImporter", nullptr, "GltfSceneConverter", {}, nullptr, "quad.ply", nullptr, "Option nonexistentMeshOptimizerOption not recognized by MeshOptimizerSceneConverter\n"}, {"one implicit mesh, two converters, explicit last, options for the first only", Containers::array({ @@ -342,7 +346,7 @@ const struct { "-c", "nonexistentMeshOptimizerOption=yes", "-C", "StanfordSceneConverter", Utility::Path::join(SCENETOOLS_TEST_DIR, "SceneConverterTestFiles/quad-strip.gltf"), Utility::Path::join(SCENETOOLS_TEST_OUTPUT_DIR, "SceneConverterTestFiles/quad.ply")}), - "GltfImporter", "GltfSceneConverter", nullptr, + "GltfImporter", nullptr, "GltfSceneConverter", {}, nullptr, "quad.ply", nullptr, "Option nonexistentMeshOptimizerOption not recognized by MeshOptimizerSceneConverter\n"}, {"one implicit mesh, two converters, options for both", Containers::array({ @@ -350,7 +354,7 @@ const struct { "-c", "nonexistentMeshOptimizerOption=yes", "-c", "nonexistentAnyConverterOption=no", Utility::Path::join(SCENETOOLS_TEST_DIR, "SceneConverterTestFiles/quad-strip.gltf"), Utility::Path::join(SCENETOOLS_TEST_OUTPUT_DIR, "SceneConverterTestFiles/quad.gltf")}), - "GltfImporter", "GltfSceneConverter", nullptr, + "GltfImporter", nullptr, "GltfSceneConverter", {}, nullptr, "quad.ply", nullptr, "Option nonexistentMeshOptimizerOption not recognized by MeshOptimizerSceneConverter\n" "Trade::AnySceneConverter::beginFile(): option nonexistentAnyConverterOption not recognized by GltfSceneConverter\n"}, @@ -360,7 +364,7 @@ const struct { "-C", "StanfordSceneConverter", "-c", "nonexistentStanfordConverterOption=no", Utility::Path::join(SCENETOOLS_TEST_DIR, "SceneConverterTestFiles/quad-strip.gltf"), Utility::Path::join(SCENETOOLS_TEST_OUTPUT_DIR, "SceneConverterTestFiles/quad.gltf")}), - "GltfImporter", "GltfSceneConverter", nullptr, + "GltfImporter", nullptr, "GltfSceneConverter", {}, nullptr, "quad.ply", nullptr, "Option nonexistentMeshOptimizerOption not recognized by MeshOptimizerSceneConverter\n" "Option nonexistentStanfordConverterOption not recognized by StanfordSceneConverter\n"}, @@ -368,7 +372,7 @@ const struct { "--remove-duplicate-vertices", "-C", "MeshOptimizerSceneConverter", "-v", Utility::Path::join(SCENETOOLS_TEST_DIR, "SceneConverterTestFiles/quad-duplicates.obj"), Utility::Path::join(SCENETOOLS_TEST_OUTPUT_DIR, "SceneConverterTestFiles/quad.ply")}), - "ObjImporter", "StanfordSceneConverter", nullptr, + "ObjImporter", nullptr, "StanfordSceneConverter", {}, nullptr, "quad.ply", nullptr, "Trade::AnySceneImporter::openFile(): using ObjImporter\n" "Mesh 0 duplicate removal: 6 -> 4 vertices\n" @@ -396,7 +400,7 @@ const struct { original */ "--remove-duplicate-vertices", "-c", "generator=", Utility::Path::join(SCENETOOLS_TEST_DIR, "SceneConverterTestFiles/quad-name-custom-attributes-duplicates.gltf"), Utility::Path::join(SCENETOOLS_TEST_OUTPUT_DIR, "SceneConverterTestFiles/quad-name-custom-attributes.gltf")}), - "GltfImporter", "GltfSceneConverter", nullptr, + "GltfImporter", nullptr, "GltfSceneConverter", {}, nullptr, /* The output should be mostly the same, except that there's now only 4 vertices instead of 6. The code that adds meshes manually instead of using addSupportedImporterContents() should take care of propagating @@ -408,7 +412,7 @@ const struct { original */ "--mesh", "0", "--remove-duplicate-vertices", "-c", "generator=", Utility::Path::join(SCENETOOLS_TEST_DIR, "SceneConverterTestFiles/quad-name-custom-attributes-duplicates.gltf"), Utility::Path::join(SCENETOOLS_TEST_OUTPUT_DIR, "SceneConverterTestFiles/quad-name-custom-attributes.gltf")}), - "GltfImporter", "GltfSceneConverter", nullptr, + "GltfImporter", nullptr, "GltfSceneConverter", {}, nullptr, /* The output should be mostly the same, except that there's now only 4 vertices instead of 6. The code that adds meshes manually instead of using addSupportedImporterContents() should take care of propagating @@ -418,7 +422,8 @@ const struct { {"mesh converter", Containers::array({ "-M", "MeshOptimizerSceneConverter", Utility::Path::join(SCENETOOLS_TEST_DIR, "SceneConverterTestFiles/quad-strip.gltf"), Utility::Path::join(SCENETOOLS_TEST_OUTPUT_DIR, "SceneConverterTestFiles/quad.gltf")}), - "GltfImporter", "GltfSceneConverter", "MeshOptimizerSceneConverter", + "GltfImporter", nullptr, "GltfSceneConverter", + {}, "MeshOptimizerSceneConverter", /* Converts a triangle strip to indexed triangles, which verifies that the output of MeshOptimizerSceneConverter got actually passed further and not discarded */ @@ -429,7 +434,8 @@ const struct { "-I", "GltfImporter", "-C", "GltfSceneConverter", "-c", "generator=", "-M", "MeshOptimizerSceneConverter", "-v", Utility::Path::join(SCENETOOLS_TEST_DIR, "SceneConverterTestFiles/two-quads.gltf"), Utility::Path::join(SCENETOOLS_TEST_OUTPUT_DIR, "SceneConverterTestFiles/two-quads.gltf")}), - "GltfImporter", "GltfSceneConverter", "MeshOptimizerSceneConverter", + "GltfImporter", nullptr, "GltfSceneConverter", + {}, "MeshOptimizerSceneConverter", "two-quads.gltf", "two-quads.bin", "Processing mesh 0 with MeshOptimizerSceneConverter...\n" "Trade::MeshOptimizerSceneConverter::convert(): processing stats:\n" @@ -466,7 +472,8 @@ const struct { "-M", "MeshOptimizerSceneConverter", "-m", "nonexistentSecondOption=yes", "-v", Utility::Path::join(SCENETOOLS_TEST_DIR, "SceneConverterTestFiles/quad-strip.gltf"), Utility::Path::join(SCENETOOLS_TEST_OUTPUT_DIR, "SceneConverterTestFiles/quad.gltf")}), - "GltfImporter", "GltfSceneConverter", "MeshOptimizerSceneConverter", + "GltfImporter", nullptr, "GltfSceneConverter", + {}, "MeshOptimizerSceneConverter", "quad.gltf", "quad.bin", "Processing mesh 0 (1/2) with MeshOptimizerSceneConverter...\n" "Option nonexistentFirstOption not recognized by MeshOptimizerSceneConverter\n" @@ -498,136 +505,263 @@ const struct { " 65536 -> 65536 shaded pixels\n" " 65536 -> 65536 covered pixels\n" " overdraw 1 -> 1\n"}, + {"2D image converter, two images", Containers::array({ + "-P", "StbResizeImageConverter", "-p", "size=\"1 1\"", + /* Removing the generator identifier for a smaller file, bundling the + images to avoid having too many files */ + "-c", "bundleImages,generator=", + Utility::Path::join(SCENETOOLS_TEST_DIR, "SceneConverterTestFiles/images-2d.gltf"), Utility::Path::join(SCENETOOLS_TEST_OUTPUT_DIR, "SceneConverterTestFiles/images-2d-1x1.gltf")}), + "GltfImporter", "PngImporter", "GltfSceneConverter", + {"StbResizeImageConverter", "PngImageConverter"}, nullptr, + "images-2d-1x1.gltf", "images-2d-1x1.bin", + {}}, + {"2D image converter, two images, verbose", Containers::array({ + "-I", "GltfImporter", "-C", "GltfSceneConverter", + "-P", "StbResizeImageConverter", "-p", "size=\"1 1\"", + /* Removing the generator identifier for a smaller file, bundling the + images to avoid having too many files */ + "-c", "bundleImages,generator=", "-v", + Utility::Path::join(SCENETOOLS_TEST_DIR, "SceneConverterTestFiles/images-2d.gltf"), Utility::Path::join(SCENETOOLS_TEST_OUTPUT_DIR, "SceneConverterTestFiles/images-2d-1x1.gltf")}), + "GltfImporter", "PngImporter", "GltfSceneConverter", + {"StbResizeImageConverter", "PngImageConverter"}, nullptr, + "images-2d-1x1.gltf", "images-2d-1x1.bin", + "Trade::AnyImageImporter::openFile(): using PngImporter\n" + "Processing 2D image 0 with StbResizeImageConverter...\n" + "Trade::AnyImageImporter::openFile(): using PngImporter\n" + "Processing 2D image 1 with StbResizeImageConverter...\n"}, + {"two 2D image converters, two images, verbose", Containers::array({ + "-I", "GltfImporter", "-C", "GltfSceneConverter", + "-P", "StbResizeImageConverter", "-p", "size=\"2 2\"", + "-P", "StbResizeImageConverter", "-p", "size=\"1 1\"", + /* Removing the generator identifier for a smaller file, bundling the + images to avoid having too many files */ + "-c", "bundleImages,generator=", "-v", + Utility::Path::join(SCENETOOLS_TEST_DIR, "SceneConverterTestFiles/images-2d.gltf"), Utility::Path::join(SCENETOOLS_TEST_OUTPUT_DIR, "SceneConverterTestFiles/images-2d-1x1.gltf")}), + "GltfImporter", "PngImporter", "GltfSceneConverter", + {"StbResizeImageConverter", "PngImageConverter"}, nullptr, + "images-2d-1x1.gltf", "images-2d-1x1.bin", + "Trade::AnyImageImporter::openFile(): using PngImporter\n" + "Processing 2D image 0 (1/2) with StbResizeImageConverter...\n" + "Processing 2D image 0 (2/2) with StbResizeImageConverter...\n" + "Trade::AnyImageImporter::openFile(): using PngImporter\n" + "Processing 2D image 1 (1/2) with StbResizeImageConverter...\n" + "Processing 2D image 1 (2/2) with StbResizeImageConverter...\n"}, + {"3D image converter, two images", Containers::array({ + "-i", "experimentalKhrTextureKtx", + "-P", "StbResizeImageConverter", "-p", "size=\"1 1\"", + /* Removing the generator identifier for a smaller file, bundling the + images to avoid having too many files */ + "-c", "experimentalKhrTextureKtx,imageConverter=KtxImageConverter,bundleImages,generator=", + Utility::Path::join(SCENETOOLS_TEST_DIR, "SceneConverterTestFiles/images-3d.gltf"), Utility::Path::join(SCENETOOLS_TEST_OUTPUT_DIR, "SceneConverterTestFiles/images-3d-1x1x1.gltf")}), + "GltfImporter", "PngImporter", "GltfSceneConverter", + {"StbResizeImageConverter", "KtxImageConverter"}, nullptr, + "images-3d-1x1x1.gltf", "images-3d-1x1x1.bin", + {}}, + {"3D image converter, two images, verbose", Containers::array({ + "-I", "GltfImporter", "-C", "GltfSceneConverter", + "-i", "experimentalKhrTextureKtx", + "-P", "StbResizeImageConverter", "-p", "size=\"1 1\"", + /* Removing the generator identifier for a smaller file, bundling the + images to avoid having too many files */ + "-c", "experimentalKhrTextureKtx,imageConverter=KtxImageConverter,bundleImages,generator=", "-v", + Utility::Path::join(SCENETOOLS_TEST_DIR, "SceneConverterTestFiles/images-3d.gltf"), Utility::Path::join(SCENETOOLS_TEST_OUTPUT_DIR, "SceneConverterTestFiles/images-3d-1x1x1.gltf")}), + "GltfImporter", "PngImporter", "GltfSceneConverter", + {"StbResizeImageConverter", "KtxImageConverter"}, nullptr, + "images-3d-1x1x1.gltf", "images-3d-1x1x1.bin", + "Trade::AnyImageImporter::openFile(): using KtxImporter\n" + "Processing 3D image 0 with StbResizeImageConverter...\n" + "Trade::AnyImageImporter::openFile(): using KtxImporter\n" + "Processing 3D image 1 with StbResizeImageConverter...\n"}, }; const struct { const char* name; Containers::Array args; const char* requiresImporter; + const char* requiresImageImporter; const char* requiresConverter; + const char* requiresImageConverter; Containers::String message; } ErrorData[]{ {"missing output argument", Containers::array({ Utility::Path::join(SCENETOOLS_TEST_DIR, "SceneConverterTestFiles/point.obj")}), - nullptr, nullptr, + nullptr, nullptr, nullptr, nullptr, /* The output should be optional only for --info, required otherwise. No need to test anything else as that's handled by Utility::Arguments already. Testing just a prefix of the message. */ "Missing command-line argument output\nUsage:\n "}, {"--mesh and --concatenate-meshes", Containers::array({ "--mesh", "0", "--concatenate-meshes", "a", "b"}), - nullptr, nullptr, + nullptr, nullptr, nullptr, nullptr, "The --mesh and --concatenate-meshes options are mutually exclusive\n"}, {"--mesh-level but no --mesh", Containers::array({ "--mesh-level", "0", "a", "b"}), - nullptr, nullptr, + nullptr, nullptr, nullptr, nullptr, "The --mesh-level option can only be used with --mesh\n"}, {"--only-mesh-attributes but no --mesh", Containers::array({ "--only-mesh-attributes", "0", "a", "b"}), - nullptr, nullptr, + nullptr, nullptr, nullptr, nullptr, "The --only-mesh-attributes option can only be used with --mesh or --concatenate-meshes\n"}, {"can't load importer plugin", Containers::array({ /* Override also the plugin directory for consistent output */ "--plugin-dir", "nonexistent", "-I", "NonexistentImporter", "whatever.obj", Utility::Path::join(SCENETOOLS_TEST_OUTPUT_DIR, "SceneConverterTestFiles/whatever.ply")}), - nullptr, nullptr, + nullptr, nullptr, nullptr, nullptr, "PluginManager::Manager::load(): plugin NonexistentImporter is not static and was not found in nonexistent/importers\n" "Available importer plugins: "}, {"can't open a file", Containers::array({ "noexistent.ffs", Utility::Path::join(SCENETOOLS_TEST_OUTPUT_DIR, "SceneConverterTestFiles/whatever.ply")}), - "AnySceneImporter", nullptr, + "AnySceneImporter", nullptr, nullptr, nullptr, "Trade::AnySceneImporter::openFile(): cannot determine the format of noexistent.ffs\n" "Cannot open file noexistent.ffs\n"}, {"can't map a file", Containers::array({ "noexistent.ffs", "--map", Utility::Path::join(SCENETOOLS_TEST_OUTPUT_DIR, "SceneConverterTestFiles/whatever.ply")}), - "AnySceneImporter", nullptr, + "AnySceneImporter", nullptr, nullptr, nullptr, "Utility::Path::mapRead(): can't open noexistent.ffs: error 2 (No such file or directory)\n" "Cannot memory-map file noexistent.ffs\n"}, {"no meshes found for concatenation", Containers::array({ "--concatenate-meshes", Utility::Path::join(SCENETOOLS_TEST_DIR, "SceneConverterTestFiles/empty.gltf"), Utility::Path::join(SCENETOOLS_TEST_OUTPUT_DIR, "SceneConverterTestFiles/whatever.ply")}), - "GltfImporter", nullptr, + "GltfImporter", nullptr, nullptr, nullptr, Utility::format("No meshes found in {}\n", Utility::Path::join(SCENETOOLS_TEST_DIR, "SceneConverterTestFiles/empty.gltf"))}, {"can't import a single mesh", Containers::array({ "-I", "ObjImporter", "--mesh", "0", Utility::Path::join(SCENETOOLS_TEST_DIR, "SceneConverterTestFiles/broken-mesh.obj"), Utility::Path::join(SCENETOOLS_TEST_OUTPUT_DIR, "SceneConverterTestFiles/whatever.ply")}), - "ObjImporter", nullptr, + "ObjImporter", nullptr, nullptr, nullptr, "Trade::ObjImporter::mesh(): wrong index count for point\n" "Cannot import the mesh\n"}, {"can't import a mesh for concatenation", Containers::array({ "-I", "ObjImporter", "--concatenate-meshes", Utility::Path::join(SCENETOOLS_TEST_DIR, "SceneConverterTestFiles/broken-mesh.obj"), Utility::Path::join(SCENETOOLS_TEST_OUTPUT_DIR, "SceneConverterTestFiles/whatever.ply")}), - "ObjImporter", nullptr, + "ObjImporter", nullptr, nullptr, nullptr, "Trade::ObjImporter::mesh(): wrong index count for point\n" "Cannot import mesh 0\n"}, {"can't import a scene for concatenation", Containers::array({ /** @todo change to an OBJ once ObjImporter imports materials (and thus scenes) */ "--concatenate-meshes", Utility::Path::join(SCENETOOLS_TEST_DIR, "SceneConverterTestFiles/broken-scene.gltf"), Utility::Path::join(SCENETOOLS_TEST_OUTPUT_DIR, "SceneConverterTestFiles/whatever.ply")}), - "GltfImporter", nullptr, + "GltfImporter", nullptr, nullptr, nullptr, "Trade::GltfImporter::scene(): mesh index 1 in node 0 out of range for 1 meshes\n" "Cannot import scene 0 for mesh concatenation\n"}, {"can't import a mesh for per-mesh processing", Containers::array({ "-I", "ObjImporter", "--remove-duplicate-vertices", Utility::Path::join(SCENETOOLS_TEST_DIR, "SceneConverterTestFiles/broken-mesh.obj"), Utility::Path::join(SCENETOOLS_TEST_OUTPUT_DIR, "SceneConverterTestFiles/whatever.ply")}), - "ObjImporter", nullptr, + "ObjImporter", nullptr, nullptr, nullptr, "Trade::ObjImporter::mesh(): wrong index count for point\n" "Cannot import mesh 0\n"}, {"invalid mesh attribute filter", Containers::array({ /** @todo drop --mesh once it's not needed anymore again */ "-I", "ObjImporter", "--mesh", "0", "--only-mesh-attributes", "LOLNEIN", Utility::Path::join(SCENETOOLS_TEST_DIR, "SceneConverterTestFiles/point.obj"), Utility::Path::join(SCENETOOLS_TEST_OUTPUT_DIR, "SceneConverterTestFiles/whatever.ply")}), - "ObjImporter", nullptr, + "ObjImporter", nullptr, nullptr, nullptr, "Utility::parseNumberSequence(): unrecognized character L in LOLNEIN\n"}, {"can't load converter plugin", Containers::array({ "-C", "NonexistentSceneConverter", Utility::Path::join(SCENETOOLS_TEST_DIR, "SceneConverterTestFiles/point.obj"), Utility::Path::join(SCENETOOLS_TEST_OUTPUT_DIR, "SceneConverterTestFiles/whatever.ply")}), - "ObjImporter", nullptr, + "ObjImporter", nullptr, nullptr, nullptr, Utility::format("PluginManager::Manager::load(): plugin NonexistentSceneConverter is not static and was not found in {}\n" "Available converter plugins: ", /* Just a prefix */ MAGNUM_PLUGINS_SCENECONVERTER_INSTALL_DIR)}, {"file coversion begin failed", Containers::array({ "-I", "ObjImporter", Utility::Path::join(SCENETOOLS_TEST_DIR, "SceneConverterTestFiles/point.obj"), Utility::Path::join(SCENETOOLS_TEST_OUTPUT_DIR, "SceneConverterTestFiles/whatever.fbx")}), - "ObjImporter", "AnySceneConverter", + "ObjImporter", nullptr, "AnySceneConverter", nullptr, Utility::format("Trade::AnySceneConverter::beginFile(): cannot determine the format of {0}\n" "Cannot begin conversion of file {0}\n", Utility::Path::join(SCENETOOLS_TEST_OUTPUT_DIR, "SceneConverterTestFiles/whatever.fbx"))}, {"file coversion end failed", Containers::array({ "-I", "GltfImporter", Utility::Path::join(SCENETOOLS_TEST_DIR, "SceneConverterTestFiles/empty.gltf"), Utility::Path::join(SCENETOOLS_TEST_OUTPUT_DIR, "SceneConverterTestFiles/whatever.ply")}), - "GltfImporter", "StanfordSceneConverter", + "GltfImporter", nullptr, "StanfordSceneConverter", nullptr, Utility::format("Trade::AbstractSceneConverter::endFile(): the converter requires exactly one mesh, got 0\n" "Cannot end conversion of file {0}\n", Utility::Path::join(SCENETOOLS_TEST_OUTPUT_DIR, "SceneConverterTestFiles/whatever.ply"))}, /** @todo importer conversion begin failed, once there's a plugin for which begin() can fail */ {"importer coversion end failed", Containers::array({ "-I", "GltfImporter", "-C", "MeshOptimizerSceneConverter", Utility::Path::join(SCENETOOLS_TEST_DIR, "SceneConverterTestFiles/empty.gltf"), Utility::Path::join(SCENETOOLS_TEST_OUTPUT_DIR, "SceneConverterTestFiles/whatever.ply")}), - "GltfImporter", "MeshOptimizerSceneConverter", + "GltfImporter", nullptr, "MeshOptimizerSceneConverter", nullptr, "Trade::AbstractSceneConverter::end(): the converter requires exactly one mesh, got 0\n" "Cannot end importer conversion\n"}, {"can't add importer contents", Containers::array({ "-I", "ObjImporter", "-C", "StanfordSceneConverter", Utility::Path::join(SCENETOOLS_TEST_DIR, "SceneConverterTestFiles/broken-mesh.obj"), Utility::Path::join(SCENETOOLS_TEST_OUTPUT_DIR, "SceneConverterTestFiles/whatever.ply")}), - "ObjImporter", "StanfordSceneConverter", + "ObjImporter", nullptr, "StanfordSceneConverter", nullptr, "Trade::ObjImporter::mesh(): wrong index count for point\n" "Cannot add importer contents\n"}, {"can't add processed meshes", Containers::array({ "-I", "ObjImporter", "-C", "StanfordSceneConverter", "--remove-duplicate-vertices", Utility::Path::join(SCENETOOLS_TEST_DIR, "SceneConverterTestFiles/two-triangles.obj"), Utility::Path::join(SCENETOOLS_TEST_OUTPUT_DIR, "SceneConverterTestFiles/whatever.ply")}), - "ObjImporter", "StanfordSceneConverter", + "ObjImporter", nullptr, "StanfordSceneConverter", nullptr, "Trade::AbstractSceneConverter::add(): the converter requires exactly one mesh, got 2\n" "Cannot add mesh 1\n"}, {"plugin doesn't support importer conversion", Containers::array({ /* Pass the same plugin twice, which means the first instance should get used for a mesh-to-mesh conversion */ "-I", "ObjImporter", "-C", "StanfordSceneConverter", "-C", "StanfordSceneConverter", Utility::Path::join(SCENETOOLS_TEST_DIR, "SceneConverterTestFiles/point.obj"), Utility::Path::join(SCENETOOLS_TEST_OUTPUT_DIR, "SceneConverterTestFiles/whatever.ply")}), - "ObjImporter", "StanfordSceneConverter", + "ObjImporter", nullptr, "StanfordSceneConverter", nullptr, "StanfordSceneConverter doesn't support importer conversion, only ConvertMeshToData\n"}, {"can't load mesh converter plugin", Containers::array({ "-M", "NonexistentSceneConverter", Utility::Path::join(SCENETOOLS_TEST_DIR, "SceneConverterTestFiles/point.obj"), Utility::Path::join(SCENETOOLS_TEST_OUTPUT_DIR, "SceneConverterTestFiles/whatever.ply")}), - "ObjImporter", nullptr, + "ObjImporter", nullptr, nullptr, nullptr, Utility::format("PluginManager::Manager::load(): plugin NonexistentSceneConverter is not static and was not found in {}\n" "Available mesh converter plugins: ", /* Just a prefix */ MAGNUM_PLUGINS_SCENECONVERTER_INSTALL_DIR)}, {"plugin doesn't support mesh conversion", Containers::array({ "-I", "ObjImporter", "-M", "StanfordSceneConverter", Utility::Path::join(SCENETOOLS_TEST_DIR, "SceneConverterTestFiles/point.obj"), Utility::Path::join(SCENETOOLS_TEST_OUTPUT_DIR, "SceneConverterTestFiles/whatever.ply")}), - "ObjImporter", "StanfordSceneConverter", + "ObjImporter", nullptr, "StanfordSceneConverter", nullptr, "StanfordSceneConverter doesn't support mesh conversion, only ConvertMeshToData\n"}, {"can't process a mesh", Containers::array({ "-I", "ObjImporter", "-M", "MeshOptimizerSceneConverter", Utility::Path::join(SCENETOOLS_TEST_DIR, "SceneConverterTestFiles/point.obj"), Utility::Path::join(SCENETOOLS_TEST_OUTPUT_DIR, "SceneConverterTestFiles/whatever.ply")}), - "ObjImporter", "MeshOptimizerSceneConverter", + "ObjImporter", nullptr, "MeshOptimizerSceneConverter", nullptr, "Trade::MeshOptimizerSceneConverter::convert(): expected a triangle mesh, got MeshPrimitive::Points\n" "Cannot process mesh 0 with MeshOptimizerSceneConverter\n"}, + {"can't import a 2D image for per-image processing", Containers::array({ + "-I", "GltfImporter", "-P", "NonexistentImageConverter", Utility::Path::join(SCENETOOLS_TEST_DIR, "SceneConverterTestFiles/broken-image-2d.gltf"), Utility::Path::join(SCENETOOLS_TEST_OUTPUT_DIR, "SceneConverterTestFiles/whatever.gltf")}), + "GltfImporter", "AnyImageImporter", nullptr, nullptr, + Utility::format("\n" /* Just a suffix */ + "Trade::AbstractImporter::openFile(): cannot open file {}\n" + "Cannot import 2D image 0\n", Utility::Path::join(SCENETOOLS_TEST_DIR, "SceneConverterTestFiles/nonexistent.png"))}, + {"can't import a 3D image for per-image processing", Containers::array({ + "-I", "GltfImporter", "-i", "experimentalKhrTextureKtx", "-P", "NonexistentImageConverter", Utility::Path::join(SCENETOOLS_TEST_DIR, "SceneConverterTestFiles/broken-image-3d.gltf"), Utility::Path::join(SCENETOOLS_TEST_OUTPUT_DIR, "SceneConverterTestFiles/whatever.gltf")}), + "GltfImporter", "AnyImageImporter", nullptr, nullptr, + Utility::format("\n" /* Just a suffix */ + "Trade::AbstractImporter::openFile(): cannot open file {}\n" + "Cannot import 3D image 0\n", Utility::Path::join(SCENETOOLS_TEST_DIR, "SceneConverterTestFiles/nonexistent.ktx2"))}, + {"can't load image converter plugin", Containers::array({ + "-P", "NonexistentImageConverter", Utility::Path::join(SCENETOOLS_TEST_DIR, "SceneConverterTestFiles/images-2d.gltf"), Utility::Path::join(SCENETOOLS_TEST_OUTPUT_DIR, "SceneConverterTestFiles/whatever.gltf")}), + "GltfImporter", "PngImporter", nullptr, nullptr, + Utility::format("PluginManager::Manager::load(): plugin NonexistentImageConverter is not static and was not found in {}\n" + "Available image converter plugins: ", /* Just a prefix */ + MAGNUM_PLUGINS_IMAGECONVERTER_INSTALL_DIR)}, + {"plugin doesn't support image conversion", Containers::array({ + "-I", "GltfImporter", "-P", "PngImageConverter", Utility::Path::join(SCENETOOLS_TEST_DIR, "SceneConverterTestFiles/images-2d.gltf"), Utility::Path::join(SCENETOOLS_TEST_OUTPUT_DIR, "SceneConverterTestFiles/whatever.gltf")}), + "GltfImporter", "PngImporter", nullptr, "PngImageConverter", + "PngImageConverter doesn't support 2D image conversion, only Convert2DToData\n"}, + {"plugin doesn't support compressed image conversion", Containers::array({ + "-I", "GltfImporter", "-P", "StbResizeImageConverter", Utility::Path::join(SCENETOOLS_TEST_DIR, "SceneConverterTestFiles/image-dds.gltf"), Utility::Path::join(SCENETOOLS_TEST_OUTPUT_DIR, "SceneConverterTestFiles/whatever.gltf")}), + "GltfImporter", "PngImporter", nullptr, "StbResizeImageConverter", + /** @todo add an ability to pass options to AnyImageImporter to + suppress this */ + "Trade::DdsImporter::openData(): block-compressed image is assumed to be encoded with Y down and Z forward, imported data will have wrong orientation. Enable assumeYUpZBackward to suppress this warning.\n" + "StbResizeImageConverter doesn't support compressed 2D image conversion, only Convert2D|Convert3D\n"}, + {"can't process a 2D image", Containers::array({ + "-I", "GltfImporter", "-P", "StbResizeImageConverter", Utility::Path::join(SCENETOOLS_TEST_DIR, "SceneConverterTestFiles/images-2d.gltf"), Utility::Path::join(SCENETOOLS_TEST_OUTPUT_DIR, "SceneConverterTestFiles/whatever.gltf")}), + "GltfImporter", "PngImporter", nullptr, "StbResizeImageConverter", + "Trade::StbResizeImageConverter::convert(): output size was not specified\n" + "Cannot process 2D image 0 with StbResizeImageConverter\n"}, + {"can't process a 3D image", Containers::array({ + "-I", "GltfImporter", "-i", "experimentalKhrTextureKtx", "-P", "StbResizeImageConverter", Utility::Path::join(SCENETOOLS_TEST_DIR, "SceneConverterTestFiles/images-3d.gltf"), Utility::Path::join(SCENETOOLS_TEST_OUTPUT_DIR, "SceneConverterTestFiles/whatever.gltf")}), + "GltfImporter", "PngImporter", nullptr, "StbResizeImageConverter", + "Trade::StbResizeImageConverter::convert(): output size was not specified\n" + "Cannot process 3D image 0 with StbResizeImageConverter\n"}, + {"can't add processed 2D images", Containers::array({ + "-P", "StbResizeImageConverter", "-p", "size=\"1 1\"", + "-I", "GltfImporter", "-C", "GltfSceneConverter", + "-c", "imageConverter=NonexistentImageConverter", + Utility::Path::join(SCENETOOLS_TEST_DIR, "SceneConverterTestFiles/images-2d.gltf"), Utility::Path::join(SCENETOOLS_TEST_OUTPUT_DIR, "SceneConverterTestFiles/whatever.gltf")}), + "ObjImporter", "PngImporter", "GltfSceneConverter", "StbResizeImageConverter", + "\n" /* Sust a suffix */ + "Trade::GltfSceneConverter::add(): can't load NonexistentImageConverter for image conversion\n" + "Cannot add 2D image 0\n"}, + {"can't add processed 3D images", Containers::array({ + "-P", "StbResizeImageConverter", "-p", "size=\"1 1\"", + "-I", "GltfImporter", "-i", "experimentalKhrTextureKtx", + "-C", "GltfSceneConverter", + "-c", "experimentalKhrTextureKtx,imageConverter=NonexistentImageConverter", + Utility::Path::join(SCENETOOLS_TEST_DIR, "SceneConverterTestFiles/images-3d.gltf"), Utility::Path::join(SCENETOOLS_TEST_OUTPUT_DIR, "SceneConverterTestFiles/whatever.gltf")}), + "ObjImporter", "PngImporter", "GltfSceneConverter", "StbResizeImageConverter", + "\n" /* Sust a suffix */ + "Trade::GltfSceneConverter::add(): can't load NonexistentImageConverter for image conversion\n" + "Cannot add 3D image 0\n"}, }; #endif @@ -1864,17 +1998,26 @@ void SceneConverterTest::convert() { /* Check if required plugins can be loaded. Catches also ABI and interface mismatch errors. */ PluginManager::Manager importerManager{MAGNUM_PLUGINS_IMPORTER_INSTALL_DIR}; + PluginManager::Manager imageConverterManager{MAGNUM_PLUGINS_IMAGECONVERTER_INSTALL_DIR}; PluginManager::Manager converterManager{MAGNUM_PLUGINS_SCENECONVERTER_INSTALL_DIR}; if(data.requiresImporter && !(importerManager.load(data.requiresImporter) & PluginManager::LoadState::Loaded)) CORRADE_SKIP(data.requiresImporter << "plugin can't be loaded."); + if(data.requiresImporter2 && !(importerManager.load(data.requiresImporter2) & PluginManager::LoadState::Loaded)) + CORRADE_SKIP(data.requiresImporter2 << "plugin can't be loaded."); if(data.requiresConverter && !(converterManager.load(data.requiresConverter) & PluginManager::LoadState::Loaded)) CORRADE_SKIP(data.requiresConverter << "plugin can't be loaded."); + if(data.requiresImageConverter[0] && !(imageConverterManager.load(data.requiresImageConverter[0]) & PluginManager::LoadState::Loaded)) + CORRADE_SKIP(data.requiresImageConverter[0] << "plugin can't be loaded."); + if(data.requiresImageConverter[1] && !(imageConverterManager.load(data.requiresImageConverter[1]) & PluginManager::LoadState::Loaded)) + CORRADE_SKIP(data.requiresImageConverter[1] << "plugin can't be loaded."); if(data.requiresMeshConverter && !(converterManager.load(data.requiresMeshConverter) & PluginManager::LoadState::Loaded)) CORRADE_SKIP(data.requiresMeshConverter << "plugin can't be loaded."); - /* AnySceneImporter & AnySceneConverter are required implicitly for - simplicity */ + /* AnySceneImporter, AnyImageImporter & AnySceneConverter are required + implicitly for simplicity */ if(!(importerManager.load("AnySceneImporter") & PluginManager::LoadState::Loaded)) CORRADE_SKIP("AnySceneImporter plugin can't be loaded."); + if(!(importerManager.load("AnyImageImporter") & PluginManager::LoadState::Loaded)) + CORRADE_SKIP("AnyImageImporter plugin can't be loaded."); if(!(converterManager.load("AnySceneConverter") & PluginManager::LoadState::Loaded)) CORRADE_SKIP("AnySceneConverter plugin can't be loaded."); @@ -1906,24 +2049,38 @@ void SceneConverterTest::error() { #else /* Check if required plugins can be loaded. Catches also ABI and interface mismatch errors. */ - PluginManager::Manager importerManager{MAGNUM_PLUGINS_IMPORTER_INSTALL_DIR}; + PluginManager::Manager importerManager{MAGNUM_PLUGINS_IMPORTER_INSTALL_DIR}; + PluginManager::Manager imageConverterManager{MAGNUM_PLUGINS_IMAGECONVERTER_INSTALL_DIR}; PluginManager::Manager converterManager{MAGNUM_PLUGINS_SCENECONVERTER_INSTALL_DIR}; if(data.requiresImporter && !(importerManager.load(data.requiresImporter) & PluginManager::LoadState::Loaded)) CORRADE_SKIP(data.requiresImporter << "plugin can't be loaded."); + if(data.requiresImageImporter && !(importerManager.load(data.requiresImageImporter) & PluginManager::LoadState::Loaded)) + CORRADE_SKIP(data.requiresImageImporter << "plugin can't be loaded."); if(data.requiresConverter && !(converterManager.load(data.requiresConverter) & PluginManager::LoadState::Loaded)) CORRADE_SKIP(data.requiresConverter << "plugin can't be loaded."); + if(data.requiresImageConverter && !(imageConverterManager.load(data.requiresImageConverter) & PluginManager::LoadState::Loaded)) + CORRADE_SKIP(data.requiresImageConverter << "plugin can't be loaded."); + /* AnyImageImporter is required implicitly for simplicity if any importer + is required */ + if(data.requiresImageImporter && !(importerManager.load("AnyImageImporter") & PluginManager::LoadState::Loaded)) + CORRADE_SKIP("AnyImageImporter plugin can't be loaded."); CORRADE_VERIFY(true); /* capture correct function name */ Containers::Pair output = call(data.args); - /* If the message ends with a \n, assume it's the whole message. Otherwise - it's just a prefix. */ - if(data.message.hasSuffix('\n')) + /* If the message begins with a \n, assume it's just a suffix */ + if(data.message.hasPrefix('\n')) + CORRADE_COMPARE_AS(output.second(), + data.message, + TestSuite::Compare::StringHasSuffix); + /* If it ends with a \n, assume it's the whole message */ + else if(data.message.hasSuffix('\n')) CORRADE_COMPARE(output.second(), data.message); + /* Otherwise it's just a prefix */ else CORRADE_COMPARE_AS(output.second(), - data.message, - TestSuite::Compare::StringHasPrefix); + data.message, + TestSuite::Compare::StringHasPrefix); /* It should return a non-zero code */ CORRADE_VERIFY(!output.first()); #endif diff --git a/src/Magnum/SceneTools/Test/SceneConverterTestFiles/blue4x4.png b/src/Magnum/SceneTools/Test/SceneConverterTestFiles/blue4x4.png new file mode 100644 index 0000000000000000000000000000000000000000..ef4d8dc404d6bb5afbdfe78bd24253d7b2b038ed GIT binary patch literal 77 zcmeAS@N?(olHy`uVBq!ia0vp^EFjFm1SHiab7}%95l|VV4~PH& literal 0 HcmV?d00001 diff --git a/src/Magnum/SceneTools/Test/SceneConverterTestFiles/blue4x4x1.ktx2 b/src/Magnum/SceneTools/Test/SceneConverterTestFiles/blue4x4x1.ktx2 new file mode 100644 index 0000000000000000000000000000000000000000..ef537490af5c18c2ec39dd4427f9f577f25fdaee GIT binary patch literal 252 zcmZ4O9TK5nWU!l;ONvXJfq{V$h*_W*NP+nvF%SlU3?TLa;w?Zd0mSIw3{=1XjRxrf z0VW0y21Z7RY0MBIAjuAA{Rgr*7}#OfF@X324D3uW1t7i<5Cff^UzC}eSCUwgnV-i{ MR0>uAlqCTH0FrGWKL7v# literal 0 HcmV?d00001 diff --git a/src/Magnum/SceneTools/Test/SceneConverterTestFiles/broken-image-2d.gltf b/src/Magnum/SceneTools/Test/SceneConverterTestFiles/broken-image-2d.gltf new file mode 100644 index 000000000..502c981e8 --- /dev/null +++ b/src/Magnum/SceneTools/Test/SceneConverterTestFiles/broken-image-2d.gltf @@ -0,0 +1,10 @@ +{ + "asset": { + "version": "2.0" + }, + "images": [ + { + "uri": "nonexistent.png" + } + ] +} diff --git a/src/Magnum/SceneTools/Test/SceneConverterTestFiles/broken-image-3d.gltf b/src/Magnum/SceneTools/Test/SceneConverterTestFiles/broken-image-3d.gltf new file mode 100644 index 000000000..b5e57628f --- /dev/null +++ b/src/Magnum/SceneTools/Test/SceneConverterTestFiles/broken-image-3d.gltf @@ -0,0 +1,26 @@ +{ + "asset": { + "version": "2.0" + }, + "extensionsUsed": [ + "KHR_texture_ktx" + ], + "extensionsRequired": [ + "KHR_texture_ktx" + ], + "textures": [ + { + "extensions": { + "KHR_texture_ktx": { + "source": 0, + "layer": 0 + } + } + } + ], + "images": [ + { + "uri": "nonexistent.ktx2" + } + ] +} diff --git a/src/Magnum/SceneTools/Test/SceneConverterTestFiles/dxt1.dds b/src/Magnum/SceneTools/Test/SceneConverterTestFiles/dxt1.dds new file mode 100644 index 0000000000000000000000000000000000000000..87e4939788af2ee953a756dda4364f3e3da618a9 GIT binary patch literal 136 zcmZ>930A0KU|?Vu;9_6`(#$~20mO_@45Ww#3P6=CK^JRAfSpr!A DH6jML literal 0 HcmV?d00001 diff --git a/src/Magnum/SceneTools/Test/SceneConverterTestFiles/image-dds.gltf b/src/Magnum/SceneTools/Test/SceneConverterTestFiles/image-dds.gltf new file mode 100644 index 000000000..35818ced7 --- /dev/null +++ b/src/Magnum/SceneTools/Test/SceneConverterTestFiles/image-dds.gltf @@ -0,0 +1,13 @@ +{ + "asset": { + "version": "2.0" + }, + "extensionsUsed": [ + "MSFT_texture_dds" + ], + "images": [ + { + "uri": "dxt1.dds" + } + ] +} diff --git a/src/Magnum/SceneTools/Test/SceneConverterTestFiles/images-2d-1x1.bin b/src/Magnum/SceneTools/Test/SceneConverterTestFiles/images-2d-1x1.bin new file mode 100644 index 0000000000000000000000000000000000000000..f581d1f495d11a5ac0ff39774ae11a5749d465f1 GIT binary patch literal 138 zcmeAS@N?(olHy`uVBq!ia0vp^j3CUx1SBVv2j2ryJf1F&AsjQ46B2&#GcYnUGQ>`K k9|aU=@O1TaS?83{)JcLNKh7UuU|?osWSH}Ke|%UU|@$RfbsveY7w1QPhadIWnP7?c3%mOQ5b literal 0 HcmV?d00001 diff --git a/src/Magnum/SceneTools/Test/SceneConverterTestFiles/images-3d-1x1x1.gltf b/src/Magnum/SceneTools/Test/SceneConverterTestFiles/images-3d-1x1x1.gltf new file mode 100644 index 000000000..3fae84d34 --- /dev/null +++ b/src/Magnum/SceneTools/Test/SceneConverterTestFiles/images-3d-1x1x1.gltf @@ -0,0 +1,58 @@ +{ + "asset": { + "version": "2.0" + }, + "extensionsUsed": [ + "KHR_texture_ktx" + ], + "extensionsRequired": [ + "KHR_texture_ktx" + ], + "buffers": [ + { + "uri": "images-3d-1x1x1.bin", + "byteLength": 510 + } + ], + "bufferViews": [ + { + "buffer": 0, + "byteOffset": 0, + "byteLength": 255 + }, + { + "buffer": 0, + "byteOffset": 255, + "byteLength": 255 + } + ], + "textures": [ + { + "extensions": { + "KHR_texture_ktx": { + "source": 0, + "layer": 0 + } + } + }, + { + "extensions": { + "KHR_texture_ktx": { + "source": 1, + "layer": 0 + } + } + } + ], + "images": [ + { + "mimeType": "image/ktx2", + "bufferView": 0, + "name": "A large blue image" + }, + { + "mimeType": "image/ktx2", + "bufferView": 1 + } + ] +} diff --git a/src/Magnum/SceneTools/Test/SceneConverterTestFiles/images-3d.gltf b/src/Magnum/SceneTools/Test/SceneConverterTestFiles/images-3d.gltf new file mode 100644 index 000000000..adb418fb8 --- /dev/null +++ b/src/Magnum/SceneTools/Test/SceneConverterTestFiles/images-3d.gltf @@ -0,0 +1,38 @@ +{ + "asset": { + "version": "2.0" + }, + "extensionsUsed": [ + "KHR_texture_ktx" + ], + "extensionsRequired": [ + "KHR_texture_ktx" + ], + "textures": [ + { + "extensions": { + "KHR_texture_ktx": { + "source": 0, + "layer": 0 + } + } + }, + { + "extensions": { + "KHR_texture_ktx": { + "source": 1, + "layer": 0 + } + } + } + ], + "images": [ + { + "uri": "blue4x4x1.ktx2", + "name": "A large blue image" + }, + { + "uri": "red2x2x1.ktx2" + } + ] +} diff --git a/src/Magnum/SceneTools/Test/SceneConverterTestFiles/red2x2.png b/src/Magnum/SceneTools/Test/SceneConverterTestFiles/red2x2.png new file mode 100644 index 0000000000000000000000000000000000000000..f758113c04ec7fe5d11aaf27859e723883f8a186 GIT binary patch literal 79 zcmeAS@N?(olHy`uVBq!ia0vp^Od!kwBL7~QRScxWJY5_^IA$jQIe!2Ij(|y^dRazh Up5xCufhrh0UHx3vIVCg!0174-cmMzZ literal 0 HcmV?d00001 diff --git a/src/Magnum/SceneTools/Test/SceneConverterTestFiles/red2x2x1.ktx2 b/src/Magnum/SceneTools/Test/SceneConverterTestFiles/red2x2x1.ktx2 new file mode 100644 index 0000000000000000000000000000000000000000..0fc34f882b0b4cfcbe4ca22979dc1eabdc66459b GIT binary patch literal 216 zcmZ4O9TK5nWU!l;ONvXJfq{V$h?$@mNP+nvF%SlU3?TLa;w?Zd0mSIw3{-#zjRxrf z0VW0y21Z7RY0MBIAjuAA{Rgr*7}#OfF@X324D28$fdPmw1jImR=NDzB=9MItWaj5F M6qN$SfO2pI0LX_E{{R30 literal 0 HcmV?d00001 diff --git a/src/Magnum/SceneTools/Test/configure.h.cmake b/src/Magnum/SceneTools/Test/configure.h.cmake index 01fccabe8..76936f87d 100644 --- a/src/Magnum/SceneTools/Test/configure.h.cmake +++ b/src/Magnum/SceneTools/Test/configure.h.cmake @@ -31,20 +31,24 @@ #ifdef CORRADE_IS_DEBUG_BUILD #define MAGNUM_PLUGINS_INSTALL_DIR "${CMAKE_INSTALL_PREFIX}/${MAGNUM_PLUGINS_DEBUG_BINARY_INSTALL_DIR}" #define MAGNUM_PLUGINS_IMPORTER_INSTALL_DIR "${CMAKE_INSTALL_PREFIX}/${MAGNUM_PLUGINS_IMPORTER_DEBUG_BINARY_INSTALL_DIR}" +#define MAGNUM_PLUGINS_IMAGECONVERTER_INSTALL_DIR "${CMAKE_INSTALL_PREFIX}/${MAGNUM_PLUGINS_IMAGECONVERTER_DEBUG_BINARY_INSTALL_DIR}" #define MAGNUM_PLUGINS_SCENECONVERTER_INSTALL_DIR "${CMAKE_INSTALL_PREFIX}/${MAGNUM_PLUGINS_SCENECONVERTER_DEBUG_BINARY_INSTALL_DIR}" #else #define MAGNUM_PLUGINS_INSTALL_DIR "${CMAKE_INSTALL_PREFIX}/${MAGNUM_PLUGINS_RELEASE_BINARY_INSTALL_DIR}" #define MAGNUM_PLUGINS_IMPORTER_INSTALL_DIR "${CMAKE_INSTALL_PREFIX}/${MAGNUM_PLUGINS_IMPORTER_RELEASE_BINARY_INSTALL_DIR}" +#define MAGNUM_PLUGINS_IMAGECONVERTER_INSTALL_DIR "${CMAKE_INSTALL_PREFIX}/${MAGNUM_PLUGINS_IMAGECONVERTER_RELEASE_BINARY_INSTALL_DIR}" #define MAGNUM_PLUGINS_SCENECONVERTER_INSTALL_DIR "${CMAKE_INSTALL_PREFIX}/${MAGNUM_PLUGINS_SCENECONVERTER_RELEASE_BINARY_INSTALL_DIR}" #endif #else #ifdef CORRADE_IS_DEBUG_BUILD #define MAGNUM_PLUGINS_INSTALL_DIR "${CMAKE_INSTALL_PREFIX}/${MAGNUM_PLUGINS_DEBUG_LIBRARY_INSTALL_DIR}" #define MAGNUM_PLUGINS_IMPORTER_INSTALL_DIR "${CMAKE_INSTALL_PREFIX}/${MAGNUM_PLUGINS_IMPORTER_DEBUG_LIBRARY_INSTALL_DIR}" +#define MAGNUM_PLUGINS_IMAGECONVERTER_INSTALL_DIR "${CMAKE_INSTALL_PREFIX}/${MAGNUM_PLUGINS_IMAGECONVERTER_DEBUG_LIBRARY_INSTALL_DIR}" #define MAGNUM_PLUGINS_SCENECONVERTER_INSTALL_DIR "${CMAKE_INSTALL_PREFIX}/${MAGNUM_PLUGINS_SCENECONVERTER_DEBUG_LIBRARY_INSTALL_DIR}" #else #define MAGNUM_PLUGINS_INSTALL_DIR "${CMAKE_INSTALL_PREFIX}/${MAGNUM_PLUGINS_RELEASE_LIBRARY_INSTALL_DIR}" #define MAGNUM_PLUGINS_IMPORTER_INSTALL_DIR "${CMAKE_INSTALL_PREFIX}/${MAGNUM_PLUGINS_IMPORTER_RELEASE_LIBRARY_INSTALL_DIR}" +#define MAGNUM_PLUGINS_IMAGECONVERTER_INSTALL_DIR "${CMAKE_INSTALL_PREFIX}/${MAGNUM_PLUGINS_IMAGECONVERTER_RELEASE_LIBRARY_INSTALL_DIR}" #define MAGNUM_PLUGINS_SCENECONVERTER_INSTALL_DIR "${CMAKE_INSTALL_PREFIX}/${MAGNUM_PLUGINS_SCENECONVERTER_RELEASE_LIBRARY_INSTALL_DIR}" #endif #endif diff --git a/src/Magnum/SceneTools/sceneconverter.cpp b/src/Magnum/SceneTools/sceneconverter.cpp index afe6decd9..241bdc8e5 100644 --- a/src/Magnum/SceneTools/sceneconverter.cpp +++ b/src/Magnum/SceneTools/sceneconverter.cpp @@ -117,11 +117,13 @@ magnum-sceneconverter chair.obj -C MeshOptimizerSceneConverter \ @code{.sh} magnum-sceneconverter [-h|--help] [-I|--importer PLUGIN] - [-C|--converter PLUGIN]... [-M|--mesh-converter PLUGIN]... - [--plugin-dir DIR] [--map] [--only-mesh-attributes N1,N2-N3…] - [--remove-duplicate-vertices] [--remove-duplicate-vertices-fuzzy EPSILON] + [-C|--converter PLUGIN]... [-P|--image-converter PLUGIN]... + [-M|--mesh-converter PLUGIN]... [--plugin-dir DIR] [--map] + [--only-mesh-attributes N1,N2-N3…] [--remove-duplicate-vertices] + [--remove-duplicate-vertices-fuzzy EPSILON] [-i|--importer-options key=val,key2=val2,…] [-c|--converter-options key=val,key2=val2,…]... + [-p|--image-converter-options key=val,key2=val2,…]... [-m|--mesh-converter-options key=val,key2=val2,…]... [--mesh ID] [--mesh-level INDEX] [--concatenate-meshes] [--info-animations] [--info-images] [--info-lights] [--info-cameras] [--info-materials] @@ -138,6 +140,8 @@ Arguments: - `-I`, `--importer PLUGIN` --- scene importer plugin (default: @ref Trade::AnySceneImporter "AnySceneImporter") - `-C`, `--converter PLUGIN` --- scene converter plugin(s) +- `-P`, `--image-converter PLUGIN` --- converter plugin(s) to apply to each + image in the scene - `-M`, `--mesh-converter PLUGIN` --- converter plugin(s) to apply to each mesh in the scene - `--plugin-dir DIR` --- override base plugin dir @@ -156,6 +160,8 @@ Arguments: pass to the importer - `-c`, `--converter-options key=val,key2=val2,…` --- configuration options to pass to scene converter(s) +- `-p`, `--image-converter-options key=val,key2=val2,…` --- configuration + options to pass to image converter(s) - `-m`, `--mesh-converter-options key=val,key2=val2,…` --- configuration options to pass to mesh converter(s) - `--mesh ID` --- convert just a single mesh instead of the whole scene @@ -206,10 +212,12 @@ converter doesn't support conversion to a file, @relativeref{Trade,AnySceneConverter} is used to save its output. If no `-C` is specified, @relativeref{Trade,AnySceneConverter} is used. -Similarly, the `-M` option (and correspondingly also `-m`) can be specified -multiple times in order to chain more mesh converters together. All mesh -converters in the chain have to support the ConvertMesh feature. If no `-M` is -specified, the imported meshes are passed directly to the scene converter. +Similarly, the `-P` / `-M` options (and correspondingly also `-p` / `-m`) can +be specified multiple times in order to chain more image / mesh converters +together. All image converters in the chain have to support the ConvertImage*D +feature for given image dimensions, all mesh converters in the chain have to +support the ConvertMesh feature. If no `-P` / `-M` is specified, the imported +images / meshes are passed directly to the scene converter. The `--remove-duplicate-vertices` operations are performed before passing them to any converter. @@ -244,6 +252,62 @@ bool isInfoRequested(const Utility::Arguments& args) { args.isSet("info"); } +template bool runImageConverters(PluginManager::Manager& imageConverterManager, const Utility::Arguments& args, const UnsignedInt i, Containers::Optional>& image) { + for(std::size_t j = 0, imageConverterCount = args.arrayValueCount("image-converter"); j != imageConverterCount; ++j) { + const Containers::StringView imageConverterName = args.arrayValue("image-converter", j); + if(args.isSet("verbose")) { + Debug d; + d << "Processing" << dimensions << Debug::nospace << "D image" << i; + if(imageConverterCount > 1) + d << "(" << Debug::nospace << (j+1) << Debug::nospace << "/" << Debug::nospace << imageConverterCount << Debug::nospace << ")"; + d << "with" << imageConverterName << Debug::nospace << "..."; + } + + Containers::Pointer imageConverter = imageConverterManager.loadAndInstantiate(imageConverterName); + if(!imageConverter) { + Debug{} << "Available image converter plugins:" << ", "_s.join(imageConverterManager.aliasList()); + return false; + } + + /* Set options, if passed. The AnyImageConverter check makes no + sense here, is just there because the helper wants it */ + if(args.isSet("verbose")) imageConverter->addFlags(Trade::ImageConverterFlag::Verbose); + if(j < args.arrayValueCount("image-converter-options")) + Implementation::setOptions(*imageConverter, "AnyImageConverter", args.arrayValue("image-converter-options", j)); + + Trade::ImageConverterFeatures expectedFeatures; + if(dimensions == 2) { + expectedFeatures = image->isCompressed() ? + Trade::ImageConverterFeature::ConvertCompressed2D : + Trade::ImageConverterFeature::Convert2D; + } else if(dimensions == 3) { + expectedFeatures = image->isCompressed() ? + Trade::ImageConverterFeature::ConvertCompressed3D : + Trade::ImageConverterFeature::Convert3D; + } else CORRADE_INTERNAL_ASSERT_UNREACHABLE(); /* LCOV_EXCL_LINE */ + /** @todo level-related features, once testable */ + if(!(imageConverter->features() >= expectedFeatures)) { + Error err; + err << imageConverterName << "doesn't support"; + /** @todo level-related message, once testable */ + if(image->isCompressed()) + err << "compressed"; + err << dimensions << Debug::nospace << "D image conversion, only" << Debug::packed << imageConverter->features(); + return false; + } + + /** @todo handle image levels here, once GltfSceneConverter is capable + of converting them (which needs AbstractImageConverter to be + reworked around ImageData) */ + if(!(image = imageConverter->convert(*image))) { + Error{} << "Cannot process" << dimensions << Debug::nospace << "D image" << i << "with" << imageConverterName; + return false; + } + } + + return true; +} + } int main(int argc, char** argv) { @@ -252,6 +316,7 @@ int main(int argc, char** argv) { .addArgument("output").setHelp("output", "output file; ignored if --info is present") .addOption('I', "importer", "AnySceneImporter").setHelp("importer", "scene importer plugin", "PLUGIN") .addArrayOption('C', "converter").setHelp("converter", "scene converter plugin(s)", "PLUGIN") + .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") #if defined(CORRADE_TARGET_UNIX) || (defined(CORRADE_TARGET_WINDOWS) && !defined(CORRADE_TARGET_WINDOWS_RT)) @@ -262,6 +327,7 @@ int main(int argc, char** argv) { .addOption("remove-duplicate-vertices-fuzzy").setHelp("remove-duplicate-vertices-fuzzy", "remove duplicate vertices with fuzzy comparison in all meshes after import", "EPSILON") .addOption('i', "importer-options").setHelp("importer-options", "configuration options to pass to the importer", "key=val,key2=val2,…") .addArrayOption('c', "converter-options").setHelp("converter-options", "configuration options to pass to the converter(s)", "key=val,key2=val2,…") + .addArrayOption('p', "image-converter-options").setHelp("image-converter-options", "configuration options to pass to the image converter(s)", "key=val,key2=val2,…") .addArrayOption('m', "mesh-converter-options").setHelp("mesh-converter-options", "configuration options to pass to the mesh converter(s)", "key=val,key2=val2,…") .addOption("mesh").setHelp("mesh", "convert just a single mesh instead of the whole scene, ignored if --concatenate-meshes is specified", "ID") .addOption("mesh-level").setHelp("mesh-level", "level to select for single-mesh conversion", "index") @@ -311,10 +377,12 @@ ConvertMeshToFile. If the last converter doesn't support conversion to a file, AnySceneConverter is used to save its output. If no -C is specified, AnySceneConverter is used. -Similarly, the -M option (and correspondingly also -m) can be specified -multiple times in order to chain more mesh converters together. All mesh -converters in the chain have to support the ConvertMesh feature. If no -M is -specified, the imported meshes are passed directly to the scene converter. +Similarly, the -P / -M options (and correspondingly also -p / -m) can be +specified multiple times in order to chain more image / mesh converters +together. All image converters in the chain have to support the ConvertImage*D +feature for given image dimensions, all mesh converters in the chain have to +support the ConvertMesh feature. If no -P / -M is specified, the imported +images / meshes are passed directly to the scene converter. The --remove-duplicate-vertices operations are performed on meshes before passing them to any converter. @@ -593,6 +661,59 @@ well, the IDs reference attributes of the first mesh.)") *previousImporter); } + /* Operations to perform on all images in the importer. If there are any, + images are supplied manually to the converter from the array below. */ + Containers::Array images2D; + Containers::Array images3D; + if(args.arrayValueCount("image-converter")) { + /** @todo implement once there's any file format capable of storing + these */ + if(importer->image1DCount()) { + Error{} << "Sorry, 1D image conversion is not implemented yet"; + return 1; + } + + for(UnsignedInt i = 0; i != importer->image2DCount(); ++i) { + Containers::Optional image; + { + /** @todo handle image levels once GltfSceneConverter can save + them (which needs AbstractImageConverter to be reworked + around ImageData) -- there could be an image2DOffsets + array saying which subrange is levels for which image */ + Trade::Implementation::Duration d{importConversionTime}; + if(!(image = importer->image2D(i))) { + Error{} << "Cannot import 2D image" << i; + return 1; + } + } + + if(!runImageConverters(imageConverterManager, args, i, image)) + return 1; + + arrayAppend(images2D, *std::move(image)); + } + + for(UnsignedInt i = 0; i != importer->image3DCount(); ++i) { + Containers::Optional image; + { + /** @todo handle image levels once GltfSceneConverter can save + them (which needs AbstractImageConverter to be reworked + around ImageData) -- there could be an image2DOffsets + array saying which subrange is levels for which image */ + Trade::Implementation::Duration d{importConversionTime}; + if(!(image = importer->image3D(i))) { + Error{} << "Cannot import 3D image" << i; + return 1; + } + } + + if(!runImageConverters(imageConverterManager, args, i, image)) + return 1; + + arrayAppend(images3D, *std::move(image)); + } + } + /* Operations to perform on all meshes in the importer. If there are any, meshes are supplied manually to the converter from the array below. */ Containers::Array meshes; @@ -751,6 +872,57 @@ well, the IDs reference attributes of the first mesh.)") as well */ Trade::SceneContents contents = ~Trade::SceneContents{}; + /* If there are any loose images from previous conversion steps, add + them directly, and clear the array so the next iteration (if any) + takes them from the importer instead */ + /** @todo 1D images, once there's any format that supports them */ + if(images2D) { + if(!(Trade::sceneContentsFor(*converter) & Trade::SceneContent::Images2D)) { + Warning{} << "Ignoring" << images2D.size() << "2D images not supported by the converter"; + } else for(UnsignedInt i = 0; i != images2D.size(); ++i) { + Trade::Implementation::Duration d{conversionTime}; + if(!converter->add(images2D[i], contents & Trade::SceneContent::Names ? importer->image2DName(i) : Containers::String{})) { + Error{} << "Cannot add 2D image" << i; + return 1; + } + } + + /* Ensure the images are not added by addSupportedImporterContents() + below. Do this also in case the converter actually doesn't + support image addition, as it would otherwise cause two warnings + about the same thing being printed. */ + contents &= ~Trade::SceneContent::Images2D; + + /* Delete the list to avoid adding them again for the next + converter (at which point they would be stale) */ + /** @todo this line is untested, needs first an importer->importer + converter supporting images */ + images2D = {}; + } + if(images3D) { + if(!(Trade::sceneContentsFor(*converter) & Trade::SceneContent::Images3D)) { + Warning{} << "Ignoring" << images3D.size() << "3D images not supported by the converter"; + } else for(UnsignedInt i = 0; i != images3D.size(); ++i) { + Trade::Implementation::Duration d{conversionTime}; + if(!converter->add(images3D[i], contents & Trade::SceneContent::Names ? importer->image3DName(i) : Containers::String{})) { + Error{} << "Cannot add 3D image" << i; + return 1; + } + } + + /* Ensure the images are not added by addSupportedImporterContents() + below. Do this also in case the converter actually doesn't + support image addition, as it would otherwise cause two warnings + about the same thing being printed. */ + contents &= ~Trade::SceneContent::Images3D; + + /* Delete the list to avoid adding them again for the next + converter (at which point they would be stale) */ + /** @todo this line is untested, needs first an importer->importer + converter supporting images */ + images3D = {}; + } + /* If there are any loose meshes from previous conversion steps, add them directly, and clear the array so the next iteration (if any) takes them from the importer instead */