diff --git a/src/Magnum/SceneTools/Test/SceneConverterTest.cpp b/src/Magnum/SceneTools/Test/SceneConverterTest.cpp index 6dfe492b9..ca86917a6 100644 --- a/src/Magnum/SceneTools/Test/SceneConverterTest.cpp +++ b/src/Magnum/SceneTools/Test/SceneConverterTest.cpp @@ -117,29 +117,30 @@ const struct { Containers::Array args; const char* requiresImporter; const char* requiresConverter; + const char* requiresMeshConverter; const char* expected; const char* expected2; Containers::String message; } 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", + "ObjImporter", "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", + "ObjImporter", "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", + "ObjImporter", "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", + "ObjImporter", "StanfordSceneConverter", nullptr, "quad.ply", nullptr, {}}, {"one mesh, options", Containers::array({ @@ -148,7 +149,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", + "ObjImporter", "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"}, @@ -159,7 +160,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", + "ObjImporter", "StanfordSceneConverter", nullptr, "quad.ply", nullptr, "Option nonexistentOption not recognized by ObjImporter\n" "Option nonexistentConverterOption not recognized by StanfordSceneConverter\n"}, @@ -167,20 +168,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", + "GltfImporter", "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", + "ObjImporter", "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", + "GltfImporter", "StanfordSceneConverter", nullptr, "quad-duplicates.ply", nullptr, {}}, /** @todo drop --mesh once it's not needed anymore again, then add a @@ -189,19 +190,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", + "ObjImporter", "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", + "ObjImporter", "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", + "ObjImporter", "StanfordSceneConverter", nullptr, "quad.ply", nullptr, {}}, {"one implicit mesh, remove duplicate vertices, verbose", Containers::array({ @@ -209,7 +210,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", + "ObjImporter", "StanfordSceneConverter", nullptr, "quad.ply", nullptr, "Mesh 0 duplicate removal: 6 -> 4 vertices\n"}, {"one selected mesh, remove duplicate vertices, verbose", Containers::array({ @@ -217,7 +218,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", + "GltfImporter", "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, @@ -229,7 +230,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", + "GltfImporter", "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" @@ -237,7 +238,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", + "ObjImporter", "StanfordSceneConverter", nullptr, "quad.ply", nullptr, {}}, {"one implicit mesh, remove duplicate vertices fuzzy, verbose", Containers::array({ @@ -245,7 +246,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", + "ObjImporter", "StanfordSceneConverter", nullptr, "quad.ply", nullptr, "Mesh 0 fuzzy duplicate removal: 6 -> 4 vertices\n"}, {"one selected mesh, remove duplicate vertices fuzzy, verbose", Containers::array({ @@ -253,7 +254,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", + "GltfImporter", "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, @@ -265,26 +266,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", + "GltfImporter", "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", + "GltfImporter", "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", + "GltfImporter", "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", + "GltfImporter", "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 @@ -308,7 +309,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", + "GltfImporter", "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 @@ -333,7 +334,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", + "GltfImporter", "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({ @@ -341,7 +342,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", + "GltfImporter", "GltfSceneConverter", nullptr, "quad.ply", nullptr, "Option nonexistentMeshOptimizerOption not recognized by MeshOptimizerSceneConverter\n"}, {"one implicit mesh, two converters, options for both", Containers::array({ @@ -349,7 +350,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", + "GltfImporter", "GltfSceneConverter", nullptr, "quad.ply", nullptr, "Option nonexistentMeshOptimizerOption not recognized by MeshOptimizerSceneConverter\n" "Trade::AnySceneConverter::beginFile(): option nonexistentAnyConverterOption not recognized by GltfSceneConverter\n"}, @@ -359,7 +360,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", + "GltfImporter", "GltfSceneConverter", nullptr, "quad.ply", nullptr, "Option nonexistentMeshOptimizerOption not recognized by MeshOptimizerSceneConverter\n" "Option nonexistentStanfordConverterOption not recognized by StanfordSceneConverter\n"}, @@ -367,7 +368,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", + "ObjImporter", "StanfordSceneConverter", nullptr, "quad.ply", nullptr, "Trade::AnySceneImporter::openFile(): using ObjImporter\n" "Mesh 0 duplicate removal: 6 -> 4 vertices\n" @@ -395,7 +396,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", + "GltfImporter", "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 @@ -407,13 +408,96 @@ 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", + "GltfImporter", "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 mesh names and custom attributes as well. */ "quad-name-custom-attributes.gltf", "quad-name-custom-attributes.bin", {}}, + {"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", + /* Converts a triangle strip to indexed triangles, which verifies that + the output of MeshOptimizerSceneConverter got actually passed + further and not discarded */ + "quad.gltf", "quad.bin", + {}}, + {"mesh converter, two meshes, verbose", Containers::array({ + /* Removing the generator identifier for a smaller file */ + "-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", + "two-quads.gltf", "two-quads.bin", + "Processing mesh 0 with MeshOptimizerSceneConverter...\n" + "Trade::MeshOptimizerSceneConverter::convert(): processing stats:\n" + " vertex cache:\n" + " 4 -> 4 transformed vertices\n" + " 1 -> 1 executed warps\n" + " ACMR 2 -> 2\n" + " ATVR 1 -> 1\n" + " vertex fetch:\n" + " 64 -> 64 bytes fetched\n" + " overfetch 1.33333 -> 1.33333\n" + " overdraw:\n" + " 65536 -> 65536 shaded pixels\n" + " 65536 -> 65536 covered pixels\n" + " overdraw 1 -> 1\n" + "Processing mesh 1 with MeshOptimizerSceneConverter...\n" + "Trade::MeshOptimizerSceneConverter::convert(): processing stats:\n" + " vertex cache:\n" + " 4 -> 4 transformed vertices\n" + " 1 -> 1 executed warps\n" + " ACMR 2 -> 2\n" + " ATVR 1 -> 1\n" + " vertex fetch:\n" + " 64 -> 64 bytes fetched\n" + " overfetch 1.33333 -> 1.33333\n" + " overdraw:\n" + " 65536 -> 65536 shaded pixels\n" + " 65536 -> 65536 covered pixels\n" + " overdraw 1 -> 1\n"}, + {"two mesh converters, two options, one mesh, verbose", Containers::array({ + "-I", "GltfImporter", "-C", "GltfSceneConverter", + "-M", "MeshOptimizerSceneConverter", + "-m", "nonexistentFirstOption=yes", + "-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", + "quad.gltf", "quad.bin", + "Processing mesh 0 (1/2) with MeshOptimizerSceneConverter...\n" + "Option nonexistentFirstOption not recognized by MeshOptimizerSceneConverter\n" + "Trade::MeshOptimizerSceneConverter::convert(): processing stats:\n" + " vertex cache:\n" + " 4 -> 4 transformed vertices\n" + " 1 -> 1 executed warps\n" + " ACMR 2 -> 2\n" + " ATVR 1 -> 1\n" + " vertex fetch:\n" + " 64 -> 64 bytes fetched\n" + " overfetch 1.33333 -> 1.33333\n" + " overdraw:\n" + " 65536 -> 65536 shaded pixels\n" + " 65536 -> 65536 covered pixels\n" + " overdraw 1 -> 1\n" + "Processing mesh 0 (2/2) with MeshOptimizerSceneConverter...\n" + "Option nonexistentSecondOption not recognized by MeshOptimizerSceneConverter\n" + "Trade::MeshOptimizerSceneConverter::convert(): processing stats:\n" + " vertex cache:\n" + " 4 -> 4 transformed vertices\n" + " 1 -> 1 executed warps\n" + " ACMR 2 -> 2\n" + " ATVR 1 -> 1\n" + " vertex fetch:\n" + " 64 -> 64 bytes fetched\n" + " overfetch 1.33333 -> 1.33333\n" + " overdraw:\n" + " 65536 -> 65536 shaded pixels\n" + " 65536 -> 65536 covered pixels\n" + " overdraw 1 -> 1\n"}, }; const struct { @@ -538,6 +622,30 @@ const struct { "-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", "StanfordSceneConverter doesn't support importer conversion, only ConvertMeshToData\n"}, + {"can't load mesh converter plugin", Containers::array({ + /* Override also the plugin directory for consistent output, however + then the importer plugin has to be loaded through an absolute file + path (unless using static plugins) */ + "--plugin-dir", "nonexistent", "-I", + #ifndef MAGNUM_BUILD_STATIC + Utility::Path::join(MAGNUM_PLUGINS_IMPORTER_INSTALL_DIR, "ObjImporter" + Trade::AbstractImporter::pluginSuffix()), + #else + "ObjImporter", + #endif + "-M", "NonexistentSceneConverter", Utility::Path::join(SCENETOOLS_TEST_DIR, "SceneConverterTestFiles/point.obj"), Utility::Path::join(SCENETOOLS_TEST_OUTPUT_DIR, "SceneConverterTestFiles/whatever.ply")}), + "ObjImporter", nullptr, + /* Just a prefix */ + "PluginManager::Manager::load(): plugin NonexistentSceneConverter is not static and was not found in nonexistent/sceneconverters\n" + "Available mesh converter plugins: "}, + {"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", + "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", + "Trade::MeshOptimizerSceneConverter::convert(): expected a triangle mesh, got MeshPrimitive::Points\n" + "Cannot process mesh 0 with MeshOptimizerSceneConverter\n"}, }; #endif @@ -1779,6 +1887,8 @@ void SceneConverterTest::convert() { CORRADE_SKIP(data.requiresImporter << "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.requiresMeshConverter && !(converterManager.load(data.requiresMeshConverter) & PluginManager::LoadState::Loaded)) + CORRADE_SKIP(data.requiresMeshConverter << "plugin can't be loaded."); /* AnySceneImporter & AnySceneConverter are required implicitly for simplicity */ if(!(importerManager.load("AnySceneImporter") & PluginManager::LoadState::Loaded)) diff --git a/src/Magnum/SceneTools/sceneconverter.cpp b/src/Magnum/SceneTools/sceneconverter.cpp index 50dfa3386..4211298f3 100644 --- a/src/Magnum/SceneTools/sceneconverter.cpp +++ b/src/Magnum/SceneTools/sceneconverter.cpp @@ -117,12 +117,13 @@ magnum-sceneconverter chair.obj -C MeshOptimizerSceneConverter \ @code{.sh} magnum-sceneconverter [-h|--help] [-I|--importer PLUGIN] - [-C|--converter PLUGIN]... [--plugin-dir DIR] [--map] - [--only-mesh-attributes N1,N2-N3…] [--remove-duplicate-vertices] - [--remove-duplicate-vertices-fuzzy EPSILON] + [-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] [-i|--importer-options key=val,key2=val2,…] - [-c|--converter-options key=val,key2=val2,…]... [--mesh ID] - [--mesh-level INDEX] [--concatenate-meshes] [--info-animations] + [-c|--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] [--info-meshes] [--info-objects] [--info-scenes] [--info-skins] [--info-textures] [--info] [--color on|4bit|off|auto] [--bounds] @@ -137,6 +138,8 @@ Arguments: - `-I`, `--importer PLUGIN` --- scene importer plugin (default: @ref Trade::AnySceneImporter "AnySceneImporter") - `-C`, `--converter PLUGIN` --- scene converter plugin(s) +- `-M`, `--mesh-converter PLUGIN` --- converter plugin(s) to apply to each + mesh in the scene - `--plugin-dir DIR` --- override base plugin dir - `--map` --- memory-map the input for zero-copy import (works only for standalone files) @@ -152,7 +155,9 @@ Arguments: - `-i`, `--importer-options key=val,key2=val2,…` --- configuration options to pass to the importer - `-c`, `--converter-options key=val,key2=val2,…` --- configuration options - to pass to the converter(s) + to pass to scene 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 - `--mesh-level LEVEL` --- level to select for single-mesh conversion - `--concatenate-meshes` -- flatten mesh hierarchy and concatenate them all @@ -183,29 +188,39 @@ and both `--info-*` options are specified, the output will also list reference count (for example, `--info-scenes` together with `--info-meshes` will print how many objects reference given mesh). -The `-i` / `--importer-options` and `-c` / `--converter-options` arguments -accept a comma-separated list of key/value pairs to set in the importer / -converter plugin configuration. If the `=` character is omitted, it's -equivalent to saying `key=true`; configuration subgroups are delimited with -`/`. - -It's possible to specify the `-C` / `--converter` option (and correspondingly -also `-c` / `--converter-options`) multiple times in order to chain more -converters together. All converters in the chain have to support the -@ref Trade::SceneConverterFeature::ConvertMesh feature, -the last converter either @ref Trade::SceneConverterFeature::ConvertMesh or -@ref Trade::SceneConverterFeature::ConvertMeshToFile. If the last converter -doesn't support conversion to a file, -@ref Trade::AnySceneConverter "AnySceneConverter" is used to save its output; -if no `-C` / `--converter` is specified, -@ref Trade::AnySceneConverter "AnySceneConverter" is used. +The `-i`, `-c` and `-m` arguments accept a comma-separated list of key/value +pairs to set in the importer / converter plugin configuration. If the `=` +character is omitted, it's equivalent to saying `key=true`; configuration +subgroups are delimited with `/`. + +It's possible to specify the `-C` option (and correspondingly also `-c`) +multiple times in order to chain more converters together. All converters in +the chain have to support the +@ref Trade::SceneConverterFeature::ConvertMultiple or +@relativeref{Trade::SceneCOnverterFeature,ConvertMesh} feature, the last +converter either @ref Trade::SceneConverterFeature::ConvertMultiple, +@relativeref{Trade::SceneCOnverterFeature,ConvertMesh}, +@relativeref{Trade::SceneCOnverterFeature,ConvertMultipleToFile} or +@relativeref{Trade::SceneCOnverterFeature,ConvertMeshToFile}. If the last +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. + +The `--remove-duplicate-vertices` operations are performed before passing them +to any converter. If `--concatenate-meshes` is given, all meshes of the input file are -concatenated into a single mesh using @ref MeshTools::concatenate(), with the -scene hierarchy transformation baked in using -@ref SceneTools::flattenMeshHierarchy3D(). Only attributes that are present in -the first mesh are taken, if `--only-mesh-attributes` is specified as well, the -IDs reference attributes of the first mesh. +first concatenated into a single mesh using @ref MeshTools::concatenate(), with +the scene hierarchy transformation baked in using +@ref SceneTools::flattenMeshHierarchy3D(), and then passed through the +remaining operations. Only attributes that are present in the first mesh are +taken, if `--only-mesh-attributes` is specified as well, the IDs reference +attributes of the first mesh. */ } @@ -237,6 +252,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('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)) .addBooleanOption("map").setHelp("map", "memory-map the input for zero-copy import (works only for standalone files)") @@ -246,6 +262,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('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") .addBooleanOption("concatenate-meshes").setHelp("concatenate-meshes", "flatten mesh hierarchy and concatenate them all together") @@ -281,24 +298,32 @@ and both --info-* options are specified, the output will also list reference count (for example, --info-scenes together with --info-meshes will print how many objects reference given mesh). -The -i / --importer-options and -c / --converter-options arguments accept a -comma-separated list of key/value pairs to set in the importer / converter -plugin configuration. If the = character is omitted, it's equivalent to saying -key=true; configuration subgroups are delimited with /. - -It's possible to specify the -C / --converter option (and correspondingly also --c / --converter-options) multiple times in order to chain more converters -together. All converters in the chain have to support the ConvertMesh feature, -the last converter either ConvertMesh or ConvertMeshToFile. If the last -converter doesn't support conversion to a file, AnySceneConverter is used to -save its output; if no -C / --converter is specified, AnySceneConverter is -used. - -If --concatenate-meshes is given, all meshes of the input file are concatenated -into a single mesh, with the scene hierarchy transformation baked in. Only -attributes that are present in the first mesh are taken, if ---only-mesh-attributes is specified as well, the IDs reference attributes of -the first mesh.)") +The -i, -c and -m arguments accept a comma-separated list of key/value +pairs to set in the importer / converter plugin configuration. If the = +character is omitted, it's equivalent to saying key=true; configuration +subgroups are delimited with /. + +It's possible to specify the -C option (and correspondingly also -c) multiple +times in order to chain more scene converters together. All converters in the +chain have to support the ConvertMultiple or ConvertMesh feature, the last +converter either ConvertMultiple, ConvertMesh, ConvertMultipleToFile or +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. + +The --remove-duplicate-vertices operations are performed on meshes before +passing them to any converter. + +If --concatenate-meshes is given, all meshes of the input file are first +concatenated into a single mesh, with the scene hierarchy transformation baked +in, and then passed through the remaining operations. Only attributes that are +present in the first mesh are taken, if --only-mesh-attributes is specified as +well, the IDs reference attributes of the first mesh.)") .parse(argc, argv); /* Colored output. Enable only if a TTY. */ @@ -572,7 +597,8 @@ the first mesh.)") meshes are supplied manually to the converter from the array below. */ Containers::Array meshes; if(args.isSet("remove-duplicate-vertices") || - args.value("remove-duplicate-vertices-fuzzy")) + args.value("remove-duplicate-vertices-fuzzy") || + args.arrayValueCount("mesh-converter")) { for(UnsignedInt i = 0; i != importer->meshCount(); ++i) { Containers::Optional mesh; @@ -586,30 +612,71 @@ the first mesh.)") } } - const UnsignedInt beforeVertexCount = mesh->vertexCount(); - const bool fuzzy = !!args.value("remove-duplicate-vertices-fuzzy"); + /* Duplicate removal */ + if(args.isSet("remove-duplicate-vertices") || + args.value("remove-duplicate-vertices-fuzzy")) + { + const UnsignedInt beforeVertexCount = mesh->vertexCount(); + const bool fuzzy = !!args.value("remove-duplicate-vertices-fuzzy"); - /** @todo accept two values for float and double fuzzy comparison, - or maybe also different for positions, normals and texcoords? - ugh... */ - if(fuzzy) { - Trade::Implementation::Duration d{conversionTime}; - mesh = MeshTools::removeDuplicatesFuzzy(*std::move(mesh), args.value("remove-duplicate-vertices-fuzzy")); - } else { - Trade::Implementation::Duration d{conversionTime}; - mesh = MeshTools::removeDuplicates(*std::move(mesh)); + /** @todo accept two values for float and double fuzzy + comparison, or maybe also different for positions, normals + and texcoords? ugh... */ + if(fuzzy) { + Trade::Implementation::Duration d{conversionTime}; + mesh = MeshTools::removeDuplicatesFuzzy(*std::move(mesh), args.value("remove-duplicate-vertices-fuzzy")); + } else { + Trade::Implementation::Duration d{conversionTime}; + mesh = MeshTools::removeDuplicates(*std::move(mesh)); + } + + if(args.isSet("verbose")) { + Debug d; + /* Mesh index 0 would be confusing in case of + --concatenate-meshes and plain wrong with --mesh, so + don't even print it */ + if(singleMesh) + d << (fuzzy ? "Fuzzy duplicate removal:" : "Duplicate removal:"); + else + d << "Mesh" << i << (fuzzy ? "fuzzy duplicate removal:" : "duplicate removal:"); + d << beforeVertexCount << "->" << mesh->vertexCount() << "vertices"; + } } - if(args.isSet("verbose")) { - Debug d; - /* Mesh index 0 would be confusing in case of - --concatenate-meshes and plain wrong with --mesh, so - don't even print it */ - if(singleMesh) - d << (fuzzy ? "Fuzzy duplicate removal:" : "Duplicate removal:"); - else - d << "Mesh" << i << (fuzzy ? "fuzzy duplicate removal:" : "duplicate removal:"); - d << beforeVertexCount << "->" << mesh->vertexCount() << "vertices"; + /* Arbitrary mesh converters */ + for(std::size_t j = 0, meshConverterCount = args.arrayValueCount("mesh-converter"); j != meshConverterCount; ++j) { + const Containers::StringView meshConverterName = args.arrayValue("mesh-converter", j); + if(args.isSet("verbose")) { + Debug d; + d << "Processing mesh" << i; + if(meshConverterCount > 1) + d << "(" << Debug::nospace << (j+1) << Debug::nospace << "/" << Debug::nospace << meshConverterCount << Debug::nospace << ")"; + d << "with" << meshConverterName << Debug::nospace << "..."; + } + + Containers::Pointer meshConverter = converterManager.loadAndInstantiate(meshConverterName); + if(!meshConverter) { + Debug{} << "Available mesh converter plugins:" << ", "_s.join(converterManager.aliasList()); + return 2; + } + + /* Set options, if passed. The AnySceneConverter check makes no + sense here, is just there because the helper wants it */ + if(args.isSet("verbose")) meshConverter->addFlags(Trade::SceneConverterFlag::Verbose); + if(j < args.arrayValueCount("mesh-converter-options")) + Implementation::setOptions(*meshConverter, "AnySceneConverter", args.arrayValue("mesh-converter-options", j)); + + if(!(meshConverter->features() & (Trade::SceneConverterFeature::ConvertMesh))) { + Error{} << meshConverterName << "doesn't support mesh conversion, only" << Debug::packed << meshConverter->features(); + return 1; + } + + /** @todo handle mesh levels here, once any plugin is capable + of converting them */ + if(!(mesh = meshConverter->convert(*mesh))) { + Error{} << "Cannot process mesh" << i << "with" << meshConverterName; + return 1; + } } arrayAppend(meshes, *std::move(mesh));