Browse Source

sceneconverter: initial support for whole-scene conversion.

Quite boring commit message for such a FEATURE, eh?? Yes, the docs need
updating at least.
pull/594/head
Vladimír Vondruš 4 years ago
parent
commit
682ba2c41c
  1. 10
      doc/changelog.dox
  2. 6
      src/Magnum/SceneTools/Test/CMakeLists.txt
  3. 165
      src/Magnum/SceneTools/Test/SceneConverterTest.cpp
  4. BIN
      src/Magnum/SceneTools/Test/SceneConverterTestFiles/two-quads-duplicates-fuzzy.bin
  5. 33
      src/Magnum/SceneTools/Test/SceneConverterTestFiles/two-quads-duplicates-fuzzy.bin.in
  6. 99
      src/Magnum/SceneTools/Test/SceneConverterTestFiles/two-quads-duplicates-fuzzy.gltf
  7. BIN
      src/Magnum/SceneTools/Test/SceneConverterTestFiles/two-quads-duplicates.bin
  8. 33
      src/Magnum/SceneTools/Test/SceneConverterTestFiles/two-quads-duplicates.bin.in
  9. 99
      src/Magnum/SceneTools/Test/SceneConverterTestFiles/two-quads-duplicates.gltf
  10. BIN
      src/Magnum/SceneTools/Test/SceneConverterTestFiles/two-quads.bin
  11. 99
      src/Magnum/SceneTools/Test/SceneConverterTestFiles/two-quads.gltf
  12. 409
      src/Magnum/SceneTools/sceneconverter.cpp

10
doc/changelog.dox

@ -220,6 +220,8 @@ See also:
- New @ref SceneTools library that'll be a home for various whole-scene
optimization algorithms such as hierarchy flattening, redundant node
removal and such
- The @ref magnum-sceneconverter "magnum-sceneconverter" utility now supports
whole-scene conversion instead of operating just on single meshes
- The @ref magnum-sceneconverter "magnum-sceneconverter" utility gained an
experimental `--concatenate-meshes` option, which will flatten the mesh
hierarchy and concatenate all meshes together. Note that it doesn't
@ -1141,6 +1143,14 @@ See also:
`--only-mesh-attributes`, `--remove-duplicate-vertices` and
`--remove-duplicate-vertices-fuzzy`, respectively, to make it clear they
affect meshes and not other scene content
- The @ref magnum-sceneconverter "magnum-sceneconverter"
`--mesh-level` option now requires `--mesh` to be specified as well,
instead of being ignored if `--mesh` isn't present
- Because the @ref magnum-sceneconverter "magnum-sceneconverter"
`--only-mesh-attributes` option takes attribute IDs, it can only be used in
combination with `--mesh` or `--concatenate-meshes`. This restriction might
be eventually lifted again once it's possible to specify attributes by
name.
- Due to the rework of @ref Shaders::PhongGL to support directional and
attenuated point lights, the original behavior of unattenuated point lights
isn't available anymore. For backwards compatibility, light positions

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

@ -86,6 +86,12 @@ corrade_add_test(SceneToolsSceneConverterTest SceneConverterTest.cpp
SceneConverterTestFiles/quad.gltf
SceneConverterTestFiles/quad.obj
SceneConverterTestFiles/quad.ply
SceneConverterTestFiles/two-quads-duplicates-fuzzy.bin
SceneConverterTestFiles/two-quads-duplicates-fuzzy.gltf
SceneConverterTestFiles/two-quads-duplicates.bin
SceneConverterTestFiles/two-quads-duplicates.gltf
SceneConverterTestFiles/two-quads.bin
SceneConverterTestFiles/two-quads.gltf
SceneConverterTestFiles/two-triangles-transformed.bin
SceneConverterTestFiles/two-triangles-transformed.gltf
SceneConverterTestFiles/two-triangles.obj)

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

@ -151,7 +151,7 @@ const struct {
"ObjImporter", "StanfordSceneConverter",
"quad.ply", nullptr,
"Trade::AnySceneImporter::openFile(): option nonexistentOption not recognized by ObjImporter\n"
"Trade::AnySceneConverter::convertToFile(): option nonexistentConverterOption not recognized by StanfordSceneConverter\n"},
"Trade::AnySceneConverter::beginFile(): option nonexistentConverterOption not recognized by StanfordSceneConverter\n"},
{"one mesh, options, explicit importer and converter", Containers::array<Containers::String>({
/* Same here, since we have option propagation tested in
Magnum/Test/ConverterUtilitiesTest.cpp already, to verify it's
@ -163,6 +163,14 @@ const struct {
"quad.ply", nullptr,
"Option nonexistentOption not recognized by ObjImporter\n"
"Option nonexistentConverterOption not recognized by StanfordSceneConverter\n"},
{"two meshes + scene", Containers::array<Containers::String>({
/* 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",
/* There should be a minimal difference compared to the original */
"two-quads.gltf", "two-quads.bin",
{}},
{"concatenate meshes without a scene", Containers::array<Containers::String>({
"--concatenate-meshes",
Utility::Path::join(SCENETOOLS_TEST_DIR, "SceneConverterTestFiles/two-triangles.obj"), Utility::Path::join(SCENETOOLS_TEST_OUTPUT_DIR, "SceneConverterTestFiles/quad-duplicates.ply")}),
@ -175,54 +183,105 @@ const struct {
"GltfImporter", "StanfordSceneConverter",
"quad-duplicates.ply", nullptr,
{}},
{"filter mesh attributes", Containers::array<Containers::String>({
/** @todo drop --mesh once it's not needed anymore again, then add a
multi-mesh variant */
{"one mesh, filter mesh attributes", Containers::array<Containers::String>({
/* Only 0 gets picked from here, others ignored */
"--only-mesh-attributes", "17,0,25-36",
"--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",
"quad.ply", nullptr,
{}},
{"concatenate meshes, filter mesh attributes", Containers::array<Containers::String>({
"--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",
"quad.ply", nullptr,
{}},
{"remove duplicate vertices", Containers::array<Containers::String>({
{"one implicit mesh, remove vertex duplicates", Containers::array<Containers::String>({
"--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",
"quad.ply", nullptr,
{}},
{"remove duplicate vertices, verbose", Containers::array<Containers::String>({
{"one implicit mesh, remove duplicate vertices, verbose", Containers::array<Containers::String>({
/* Forcing the importer and converter to avoid AnySceneImporter /
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",
"quad.ply", nullptr,
"Mesh 0 duplicate removal: 6 -> 4 vertices\n"},
{"one selected mesh, remove duplicate vertices, verbose", Containers::array<Containers::String>({
/* Forcing the importer and converter to avoid AnySceneImporter /
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",
/* The second mesh in the glTF is deliberately the same as in
quad-duplicates.obj, so this produces the same file */
"quad.ply", nullptr,
"Duplicate removal: 6 -> 4 vertices\n"},
{"remove duplicate vertices fuzzy", Containers::array<Containers::String>({
{"two meshes + scene, remove duplicate vertices, verbose", Containers::array<Containers::String>({
/* Forcing the importer and converter to avoid AnySceneImporter /
AnySceneConverter delegation messages */
"--remove-duplicate-vertices", "-v", "-I", "GltfImporter", "-C", "GltfSceneConverter",
/* 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",
/* There should be a minimal difference compared to the original */
"two-quads.gltf", "two-quads.bin",
"Mesh 0 duplicate removal: 5 -> 4 vertices\n"
"Mesh 1 duplicate removal: 6 -> 4 vertices\n"},
{"one implicit mesh, remove duplicate vertices fuzzy", Containers::array<Containers::String>({
"--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",
"quad.ply", nullptr,
{}},
{"remove duplicate vertices fuzzy, verbose", Containers::array<Containers::String>({
{"one implicit mesh, remove duplicate vertices fuzzy, verbose", Containers::array<Containers::String>({
/* Forcing the importer and converter to avoid AnySceneImporter /
AnySceneConverter delegation messages */
"--remove-duplicate-vertices-fuzzy 1.0e-1", "-v", "-I", "ObjImporter", "-C", "StanfordSceneConverter",
"--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",
"quad.ply", nullptr,
"Mesh 0 fuzzy duplicate removal: 6 -> 4 vertices\n"},
{"one selected mesh, remove duplicate vertices fuzzy, verbose", Containers::array<Containers::String>({
/* Forcing the importer and converter to avoid AnySceneImporter /
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",
/* 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,
"Fuzzy duplicate removal: 6 -> 4 vertices\n"},
{"one mesh, two converters", Containers::array<Containers::String>({
{"two meshes + scene, remove duplicate vertices fuzzy, verbose", Containers::array<Containers::String>({
/* Forcing the importer and converter to avoid AnySceneImporter /
AnySceneConverter delegation messages */
"--remove-duplicate-vertices-fuzzy", "1.0e-1", "-v", "-I", "GltfImporter", "-C", "GltfSceneConverter",
/* 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",
"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<Containers::String>({
"-C", "MeshOptimizerSceneConverter",
Utility::Path::join(SCENETOOLS_TEST_DIR, "SceneConverterTestFiles/quad-strip.gltf"), Utility::Path::join(SCENETOOLS_TEST_OUTPUT_DIR, "SceneConverterTestFiles/quad.gltf")}),
"GltfImporter", "GltfSceneConverter",
"quad.gltf", "quad.bin",
{}},
{"one mesh, two converters, explicit last", Containers::array<Containers::String>({
{"one implicit mesh, two converters, explicit last", Containers::array<Containers::String>({
"-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",
"quad.gltf", "quad.bin",
{}},
{"one mesh, two converters, verbose", Containers::array<Containers::String>({
{"one implicit mesh, two converters, verbose", Containers::array<Containers::String>({
"-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",
@ -245,8 +304,8 @@ const struct {
" 65536 -> 65536 shaded pixels\n"
" 65536 -> 65536 covered pixels\n"
" overdraw 1 -> 1\n"
"Trade::AnySceneConverter::convertToFile(): using GltfSceneConverter\n"},
{"one mesh, two converters, explicit last, verbose", Containers::array<Containers::String>({
"Trade::AnySceneConverter::beginFile(): using GltfSceneConverter\n"},
{"one implicit mesh, two converters, explicit last, verbose", Containers::array<Containers::String>({
"-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",
@ -270,14 +329,14 @@ const struct {
" 65536 -> 65536 covered pixels\n"
" overdraw 1 -> 1\n"
"Saving output (2/2) with GltfSceneConverter...\n"},
{"one mesh, two converters, options for the first only", Containers::array<Containers::String>({
{"one implicit mesh, two converters, options for the first only", Containers::array<Containers::String>({
"-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",
"quad.ply", nullptr,
"Option nonexistentMeshOptimizerOption not recognized by MeshOptimizerSceneConverter\n"},
{"one mesh, two converters, explicit last, options for the first only", Containers::array<Containers::String>({
{"one implicit mesh, two converters, explicit last, options for the first only", Containers::array<Containers::String>({
"-C", "MeshOptimizerSceneConverter",
"-c", "nonexistentMeshOptimizerOption=yes",
"-C", "StanfordSceneConverter",
@ -285,7 +344,7 @@ const struct {
"GltfImporter", "GltfSceneConverter",
"quad.ply", nullptr,
"Option nonexistentMeshOptimizerOption not recognized by MeshOptimizerSceneConverter\n"},
{"one mesh, two converters, options for both", Containers::array<Containers::String>({
{"one implicit mesh, two converters, options for both", Containers::array<Containers::String>({
"-C", "MeshOptimizerSceneConverter",
"-c", "nonexistentMeshOptimizerOption=yes",
"-c", "nonexistentAnyConverterOption=no",
@ -293,8 +352,8 @@ const struct {
"GltfImporter", "GltfSceneConverter",
"quad.ply", nullptr,
"Option nonexistentMeshOptimizerOption not recognized by MeshOptimizerSceneConverter\n"
"Trade::AnySceneConverter::convertToFile(): option nonexistentAnyConverterOption not recognized by GltfSceneConverter\n"},
{"one mesh, two converters, explicit last, options for both", Containers::array<Containers::String>({
"Trade::AnySceneConverter::beginFile(): option nonexistentAnyConverterOption not recognized by GltfSceneConverter\n"},
{"one implicit mesh, two converters, explicit last, options for both", Containers::array<Containers::String>({
"-C", "MeshOptimizerSceneConverter",
"-c", "nonexistentMeshOptimizerOption=yes",
"-C", "StanfordSceneConverter",
@ -311,7 +370,7 @@ const struct {
"ObjImporter", "StanfordSceneConverter",
"quad.ply", nullptr,
"Trade::AnySceneImporter::openFile(): using ObjImporter\n"
"Duplicate removal: 6 -> 4 vertices\n"
"Mesh 0 duplicate removal: 6 -> 4 vertices\n"
/** @todo this only verifies that the result of duplicate removal is
properly passed to MeshOptimizer, but not that the MeshOptimizer
output is properly passed to StanfordSceneConverter -- needs to
@ -330,7 +389,7 @@ const struct {
" 65536 -> 65536 shaded pixels\n"
" 65536 -> 65536 covered pixels\n"
" overdraw 1 -> 1\n"
"Trade::AnySceneConverter::convertToFile(): using StanfordSceneConverter\n"},
"Trade::AnySceneConverter::beginFile(): using StanfordSceneConverter\n"},
};
const struct {
@ -347,6 +406,18 @@ const struct {
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<Containers::String>({
"--mesh", "0", "--concatenate-meshes", "a", "b"}),
nullptr, nullptr,
"The --mesh and --concatenate-meshes options are mutually exclusive\n"},
{"--mesh-level but no --mesh", Containers::array<Containers::String>({
"--mesh-level", "0", "a", "b"}),
nullptr, nullptr,
"The --mesh-level option can only be used with --mesh\n"},
{"--only-mesh-attributes but no --mesh", Containers::array<Containers::String>({
"--only-mesh-attributes", "0", "a", "b"}),
nullptr, nullptr,
"The --only-mesh-attributes option can only be used with --mesh or --concatenate-meshes\n"},
{"can't load importer plugin", Containers::array<Containers::String>({
/* 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")}),
@ -363,12 +434,13 @@ const struct {
"AnySceneImporter", 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", Containers::array<Containers::String>({
{"no meshes found for concatenation", Containers::array<Containers::String>({
"--concatenate-meshes",
Utility::Path::join(SCENETOOLS_TEST_DIR, "SceneConverterTestFiles/empty.gltf"), Utility::Path::join(SCENETOOLS_TEST_OUTPUT_DIR, "SceneConverterTestFiles/whatever.ply")}),
"GltfImporter", nullptr,
Utility::format("No meshes found in {}\n", Utility::Path::join(SCENETOOLS_TEST_DIR, "SceneConverterTestFiles/empty.gltf"))},
{"can't import a mesh", Containers::array<Containers::String>({
"-I", "ObjImporter", Utility::Path::join(SCENETOOLS_TEST_DIR, "SceneConverterTestFiles/broken-mesh.obj"), Utility::Path::join(SCENETOOLS_TEST_OUTPUT_DIR, "SceneConverterTestFiles/whatever.ply")}),
{"can't import a single mesh", Containers::array<Containers::String>({
"-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,
"Trade::ObjImporter::mesh(): wrong index count for point\n"
"Cannot import the mesh\n"},
@ -384,8 +456,14 @@ const struct {
"GltfImporter", 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"},
{"invalid attribute filter", Containers::array<Containers::String>({
"-I", "ObjImporter", "--only-mesh-attributes", "LOLNEIN", Utility::Path::join(SCENETOOLS_TEST_DIR, "SceneConverterTestFiles/point.obj"), Utility::Path::join(SCENETOOLS_TEST_OUTPUT_DIR, "SceneConverterTestFiles/whatever.ply")}),
{"can't import a mesh for per-mesh processing", Containers::array<Containers::String>({
"-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,
"Trade::ObjImporter::mesh(): wrong index count for point\n"
"Cannot import mesh 0\n"},
{"invalid mesh attribute filter", Containers::array<Containers::String>({
/** @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,
"Utility::parseNumberSequence(): unrecognized character L in LOLNEIN\n"},
{"can't load converter plugin", Containers::array<Containers::String>({
@ -403,22 +481,39 @@ const struct {
/* Just a prefix */
"PluginManager::Manager::load(): plugin NonexistentSceneConverter is not static and was not found in nonexistent/sceneconverters\n"
"Available converter plugins: "},
{"file coversion failed", Containers::array<Containers::String>({
{"file coversion begin failed", Containers::array<Containers::String>({
"-I", "ObjImporter", Utility::Path::join(SCENETOOLS_TEST_DIR, "SceneConverterTestFiles/point.obj"), Utility::Path::join(SCENETOOLS_TEST_OUTPUT_DIR, "SceneConverterTestFiles/whatever.fbx")}),
"ObjImporter", "AnySceneConverter",
Utility::format("Trade::AnySceneConverter::convertToFile(): cannot determine the format of {0}\n"
"Cannot save file {0}\n", Utility::Path::join(SCENETOOLS_TEST_OUTPUT_DIR, "SceneConverterTestFiles/whatever.fbx"))},
{"mesh coversion failed", Containers::array<Containers::String>({
"-I", "ObjImporter", "-C", "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"
"MeshOptimizerSceneConverter cannot convert the mesh\n"},
{"plugin doesn't support mesh conversion", Containers::array<Containers::String>({
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<Containers::String>({
"-I", "GltfImporter", Utility::Path::join(SCENETOOLS_TEST_DIR, "SceneConverterTestFiles/empty.gltf"), Utility::Path::join(SCENETOOLS_TEST_OUTPUT_DIR, "SceneConverterTestFiles/whatever.ply")}),
"GltfImporter", "StanfordSceneConverter",
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<Containers::String>({
"-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",
"Trade::AbstractSceneConverter::end(): the converter requires exactly one mesh, got 0\n"
"Cannot end importer conversion\n"},
{"can't add importer contents", Containers::array<Containers::String>({
"-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",
"Trade::ObjImporter::mesh(): wrong index count for point\n"
"Cannot add importer contents\n"},
{"can't add processed meshes", Containers::array<Containers::String>({
"-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",
"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<Containers::String>({
/* 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",
"StanfordSceneConverter doesn't support mesh conversion, only Trade::SceneConverterFeature::ConvertMeshToData\n"},
"StanfordSceneConverter doesn't support importer conversion, only Trade::SceneConverterFeature::ConvertMeshToData\n"},
};
#endif

BIN
src/Magnum/SceneTools/Test/SceneConverterTestFiles/two-quads-duplicates-fuzzy.bin

Binary file not shown.

33
src/Magnum/SceneTools/Test/SceneConverterTestFiles/two-quads-duplicates-fuzzy.bin.in

@ -0,0 +1,33 @@
type = "6I 3f3f3f 3f3f 6I 3f3f3f 3f3f3f"
input = [
# 2 2--4
# |\ \ |
# | \ \|
# 0--1 3
0, 1, 2, 2, 3, 4,
-10, -10, 10,
10, -10, 10,
-10, 10, 10,
9.9, -9.9, 10,
10, 10, 10,
# 2 3--5
# |\ \ |
# | \ \|
# 0--1 4
0, 1, 2, 3, 4, 5,
-1, -1, 0,
1, -1, 0,
-1, 1, 0,
-0.9, 0.9, 0,
0.9, -0.9, 0,
1, 1, 0,
]
# kate: hl python

99
src/Magnum/SceneTools/Test/SceneConverterTestFiles/two-quads-duplicates-fuzzy.gltf

@ -0,0 +1,99 @@
{
"asset": {
"version": "2.0"
},
"buffers": [
{
"uri": "two-quads-duplicates-fuzzy.bin",
"byteLength": 180
}
],
"bufferViews": [
{
"buffer": 0,
"byteOffset": 0,
"byteLength": 24
},
{
"buffer": 0,
"byteOffset": 24,
"byteLength": 60
},
{
"buffer": 0,
"byteOffset": 84,
"byteLength": 24
},
{
"buffer": 0,
"byteOffset": 108,
"byteLength": 72
}
],
"accessors": [
{
"bufferView": 0,
"componentType": 5125,
"count": 6,
"type": "SCALAR"
},
{
"bufferView": 1,
"componentType": 5126,
"count": 5,
"type": "VEC3"
},
{
"bufferView": 2,
"componentType": 5125,
"count": 6,
"type": "SCALAR"
},
{
"bufferView": 3,
"componentType": 5126,
"count": 6,
"type": "VEC3"
}
],
"meshes": [
{
"primitives": [
{
"indices": 0,
"attributes": {
"POSITION": 1
}
}
]
},
{
"primitives": [
{
"indices": 2,
"attributes": {
"POSITION": 3
}
}
]
}
],
"nodes": [
{
"translation": [0, 0, 1],
"mesh": 0
},
{
"children": [2]
},
{
"scale": [0.1, 0.1, 0.1],
"mesh": 1
}
],
"scenes": [
{
"nodes": [0, 1]
}
]
}

BIN
src/Magnum/SceneTools/Test/SceneConverterTestFiles/two-quads-duplicates.bin

Binary file not shown.

33
src/Magnum/SceneTools/Test/SceneConverterTestFiles/two-quads-duplicates.bin.in

@ -0,0 +1,33 @@
type = "6I 3f3f3f 3f3f 6I 3f3f3f 3f3f3f"
input = [
# 2 2--4
# |\ \ |
# | \ \|
# 0--1 3
0, 1, 2, 2, 3, 4,
-10, -10, 10,
10, -10, 10,
-10, 10, 10,
10, -10, 10,
10, 10, 10,
# 2 3--5
# |\ \ |
# | \ \|
# 0--1 4
0, 1, 2, 3, 4, 5,
-1, -1, 0,
1, -1, 0,
-1, 1, 0,
-1, 1, 0,
1, -1, 0,
1, 1, 0,
]
# kate: hl python

99
src/Magnum/SceneTools/Test/SceneConverterTestFiles/two-quads-duplicates.gltf

@ -0,0 +1,99 @@
{
"asset": {
"version": "2.0"
},
"buffers": [
{
"uri": "two-quads-duplicates.bin",
"byteLength": 180
}
],
"bufferViews": [
{
"buffer": 0,
"byteOffset": 0,
"byteLength": 24
},
{
"buffer": 0,
"byteOffset": 24,
"byteLength": 60
},
{
"buffer": 0,
"byteOffset": 84,
"byteLength": 24
},
{
"buffer": 0,
"byteOffset": 108,
"byteLength": 72
}
],
"accessors": [
{
"bufferView": 0,
"componentType": 5125,
"count": 6,
"type": "SCALAR"
},
{
"bufferView": 1,
"componentType": 5126,
"count": 5,
"type": "VEC3"
},
{
"bufferView": 2,
"componentType": 5125,
"count": 6,
"type": "SCALAR"
},
{
"bufferView": 3,
"componentType": 5126,
"count": 6,
"type": "VEC3"
}
],
"meshes": [
{
"primitives": [
{
"indices": 0,
"attributes": {
"POSITION": 1
}
}
]
},
{
"primitives": [
{
"indices": 2,
"attributes": {
"POSITION": 3
}
}
]
}
],
"nodes": [
{
"translation": [0, 0, 1],
"mesh": 0
},
{
"children": [2]
},
{
"scale": [0.1, 0.1, 0.1],
"mesh": 1
}
],
"scenes": [
{
"nodes": [0, 1]
}
]
}

BIN
src/Magnum/SceneTools/Test/SceneConverterTestFiles/two-quads.bin

Binary file not shown.

99
src/Magnum/SceneTools/Test/SceneConverterTestFiles/two-quads.gltf

@ -0,0 +1,99 @@
{
"asset": {
"version": "2.0"
},
"buffers": [
{
"uri": "two-quads.bin",
"byteLength": 144
}
],
"bufferViews": [
{
"buffer": 0,
"byteOffset": 0,
"byteLength": 24
},
{
"buffer": 0,
"byteOffset": 24,
"byteLength": 48
},
{
"buffer": 0,
"byteOffset": 72,
"byteLength": 24
},
{
"buffer": 0,
"byteOffset": 96,
"byteLength": 48
}
],
"accessors": [
{
"bufferView": 0,
"componentType": 5125,
"count": 6,
"type": "SCALAR"
},
{
"bufferView": 1,
"componentType": 5126,
"count": 4,
"type": "VEC3"
},
{
"bufferView": 2,
"componentType": 5125,
"count": 6,
"type": "SCALAR"
},
{
"bufferView": 3,
"componentType": 5126,
"count": 4,
"type": "VEC3"
}
],
"meshes": [
{
"primitives": [
{
"indices": 0,
"attributes": {
"POSITION": 1
}
}
]
},
{
"primitives": [
{
"indices": 2,
"attributes": {
"POSITION": 3
}
}
]
}
],
"nodes": [
{
"translation": [0, 0, 1],
"mesh": 0
},
{
"children": [2]
},
{
"scale": [0.1, 0.1, 0.1],
"mesh": 1
}
],
"scenes": [
{
"nodes": [0, 1]
}
]
}

409
src/Magnum/SceneTools/sceneconverter.cpp

@ -33,11 +33,13 @@
#include <Corrade/Utility/Path.h>
#include "Magnum/MeshTools/Concatenate.h"
#include "Magnum/MeshTools/Reference.h"
#include "Magnum/MeshTools/RemoveDuplicates.h"
#include "Magnum/MeshTools/Transform.h"
#include "Magnum/SceneTools/FlattenMeshHierarchy.h"
#include "Magnum/Trade/AbstractImporter.h"
#include "Magnum/Trade/MeshData.h"
#include "Magnum/Trade/AbstractImageConverter.h"
#include "Magnum/Trade/AbstractSceneConverter.h"
#include "Magnum/Implementation/converterUtilities.h"
@ -142,18 +144,17 @@ Arguments:
given IDs in the output. See @ref Utility::String::parseNumberSequence()
for syntax description.
- `--remove-duplicate-vertices` --- remove duplicate vertices using
@ref MeshTools::removeDuplicates(const Trade::MeshData&) after import
@ref MeshTools::removeDuplicates(const Trade::MeshData&) in all meshes
after import
- `--remove-duplicate-vertices-fuzzy EPSILON` --- remove duplicate vertices
using @ref MeshTools::removeDuplicatesFuzzy(const Trade::MeshData&, Float, Double)
after import
in all meshes after import
- `-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)
- `--mesh ID` --- mesh to import (default: `0`), ignored if
`--concatenate-meshes` is specified
- `--mesh-level INDEX` --- mesh level to import (default: `0`), ignored if
`--concatenate-meshes` is specified
- `--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
together @m_class{m-label m-warning} **experimental**
- `--info-animations` --- print into about animations in the input file and
@ -241,12 +242,12 @@ int main(int argc, char** argv) {
.addBooleanOption("map").setHelp("map", "memory-map the input for zero-copy import (works only for standalone files)")
#endif
.addOption("only-mesh-attributes").setHelp("only-mesh-attributes", "include only mesh attributes of given IDs in the output", "N1,N2-N3…")
.addBooleanOption("remove-duplicate-vertices").setHelp("remove-duplicate-vertices", "remove duplicate vertices in the mesh after import")
.addOption("remove-duplicate-vertices-fuzzy").setHelp("remove-duplicate-vertices-fuzzy", "remove duplicate vertices with fuzzy comparison in the mesh after import", "EPSILON")
.addBooleanOption("remove-duplicate-vertices").setHelp("remove-duplicate-vertices", "remove duplicate vertices in all meshes after import")
.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,…")
.addOption("mesh", "0").setHelp("mesh", "mesh to import, ignored if --concatenate-meshes is specified", "ID")
.addOption("mesh-level", "0").setHelp("mesh-level", "mesh level to import, ignored if --concatenate-meshes is specified", "INDEX")
.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")
.addBooleanOption("info-animations").setHelp("info-animations", "print info about animations in the input file and exit")
.addBooleanOption("info-images").setHelp("info-images", "print info about images in the input file and exit")
@ -333,16 +334,38 @@ the first mesh.)")
if(isInfoRequested(args))
Warning{} << "Ignoring output file for --info:" << args.value<Containers::StringView>("output");
}
if(args.isSet("concatenate-meshes") && args.value<Containers::StringView>("mesh")) {
Error{} << "The --mesh and --concatenate-meshes options are mutually exclusive";
return 1;
}
if(args.value<Containers::StringView>("mesh-level") && !args.value<Containers::StringView>("mesh")) {
Error{} << "The --mesh-level option can only be used with --mesh";
return 1;
}
/** @todo remove this once only-attributes can work with attribute names
and thus for more meshes */
if(args.value<Containers::StringView>("only-mesh-attributes") && !args.value<Containers::StringView>("mesh") && !args.isSet("concatenate-meshes")) {
Error{} << "The --only-mesh-attributes option can only be used with --mesh or --concatenate-meshes";
return 1;
}
/* Importer manager */
PluginManager::Manager<Trade::AbstractImporter> importerManager{
args.value("plugin-dir").empty() ? Containers::String{} :
Utility::Path::join(args.value("plugin-dir"), Utility::Path::split(Trade::AbstractImporter::pluginSearchPaths().back()).second())};
/* Scene converter manager */
/* Image converter manager for potential dependencies. Needs to be
constructed before the scene converter manager for proper destruction
order. */
PluginManager::Manager<Trade::AbstractImageConverter> imageConverterManager{
args.value("plugin-dir").empty() ? Containers::String{} :
Utility::Path::join(args.value("plugin-dir"), Trade::AbstractImageConverter::pluginSearchPaths().back())};
/* Scene converter manager, register the image converter manager with it */
PluginManager::Manager<Trade::AbstractSceneConverter> converterManager{
args.value("plugin-dir").empty() ? Containers::String{} :
Utility::Path::join(args.value("plugin-dir"), Utility::Path::split(Trade::AbstractSceneConverter::pluginSearchPaths().back()).second())};
converterManager.registerExternalManager(imageConverterManager);
Containers::Pointer<Trade::AbstractImporter> importer = importerManager.loadAndInstantiate(args.value("importer"));
if(!importer) {
@ -354,14 +377,19 @@ the first mesh.)")
if(args.isSet("verbose")) importer->addFlags(Trade::ImporterFlag::Verbose);
Implementation::setOptions(*importer, "AnySceneImporter", args.value("importer-options"));
/* Wow, C++, you suck. This implicitly initializes to random shit?! */
std::chrono::high_resolution_clock::duration importTime{};
/* Wow, C++, you suck. This implicitly initializes to random shit?!
Also, because of addSupportedImporterContents() it's not really possible
to distinguish between time spent importing and time spent converting.
So it's lumped into a single variable. Steps that are really just
conversion are measured separately. */
std::chrono::high_resolution_clock::duration importConversionTime{};
/* Open the file or map it if requested */
#if defined(CORRADE_TARGET_UNIX) || (defined(CORRADE_TARGET_WINDOWS) && !defined(CORRADE_TARGET_WINDOWS_RT))
Containers::Optional<Containers::Array<const char, Utility::Path::MapDeleter>> mapped;
if(args.isSet("map")) {
Trade::Implementation::Duration d{importTime};
Trade::Implementation::Duration d{importConversionTime};
if(!(mapped = Utility::Path::mapRead(args.value("input"))) || !importer->openMemory(*mapped)) {
Error() << "Cannot memory-map file" << args.value("input");
return 3;
@ -369,7 +397,7 @@ the first mesh.)")
} else
#endif
{
Trade::Implementation::Duration d{importTime};
Trade::Implementation::Duration d{importConversionTime};
if(!importer->openFile(args.value("input"))) {
Error() << "Cannot open file" << args.value("input");
return 3;
@ -378,122 +406,180 @@ the first mesh.)")
/* Print file info, if requested */
if(isInfoRequested(args)) {
const bool error = SceneTools::Implementation::printInfo(useColor, useColor24, args, *importer, importTime);
const bool error = SceneTools::Implementation::printInfo(useColor, useColor24, args, *importer, importConversionTime);
if(args.isSet("profile")) {
Debug{} << "Import took" << UnsignedInt(std::chrono::duration_cast<std::chrono::milliseconds>(importTime).count())/1.0e3f << "seconds";
Debug{} << "Import took" << UnsignedInt(std::chrono::duration_cast<std::chrono::milliseconds>(importConversionTime).count())/1.0e3f << "seconds";
}
return error ? 1 : 0;
}
if(!importer->meshCount()) {
Error{} << "No meshes found in" << args.value("input");
return 1;
}
/* Wow, C++, you suck. This implicitly initializes to random shit?! */
std::chrono::high_resolution_clock::duration conversionTime{};
Containers::Optional<Trade::MeshData> mesh;
/* Concatenate input meshes, if requested */
if(args.isSet("concatenate-meshes")) {
Containers::Array<Trade::MeshData> meshes;
arrayReserve(meshes, importer->meshCount());
for(std::size_t i = 0, iMax = importer->meshCount(); i != iMax; ++i) {
Trade::Implementation::Duration d{importTime};
Containers::Optional<Trade::MeshData> meshToConcatenate = importer->mesh(i);
if(!meshToConcatenate) {
Error{} << "Cannot import mesh" << i;
/* Take a single mesh or concatenate all meshes together, if requested.
After that, the importer is changed to one that contains just a single
mesh... */
bool singleMesh = false;
if(args.isSet("concatenate-meshes") || args.value<Containers::StringView>("mesh")) {
singleMesh = true;
/* ... and subsequent conversion deals with just meshes, throwing away
materials and everything else (if present). */
Containers::Optional<Trade::MeshData> mesh;
/* Concatenate all meshes together */
if(args.isSet("concatenate-meshes")) {
if(!importer->meshCount()) {
Error{} << "No meshes found in" << args.value("input");
return 1;
}
arrayAppend(meshes, *std::move(meshToConcatenate));
}
/* If there's a scene, use it to flatten mesh hierarchy. If not, assume
all meshes are in the root. */
/** @todo make it possible to choose the scene */
if(importer->defaultScene() != -1) {
Containers::Optional<Trade::SceneData> scene;
{
Trade::Implementation::Duration d{importTime};
if(!(scene = importer->scene(importer->defaultScene()))) {
Error{} << "Cannot import scene" << importer->defaultScene() << "for mesh concatenation";
Containers::Array<Trade::MeshData> meshes;
arrayReserve(meshes, importer->meshCount());
/** @todo handle mesh levels here, once any plugin is capable of
importing them */
for(std::size_t i = 0, iMax = importer->meshCount(); i != iMax; ++i) {
Trade::Implementation::Duration d{importConversionTime};
Containers::Optional<Trade::MeshData> meshToConcatenate = importer->mesh(i);
if(!meshToConcatenate) {
Error{} << "Cannot import mesh" << i;
return 1;
}
arrayAppend(meshes, *std::move(meshToConcatenate));
}
/* If there's a scene, use it to flatten mesh hierarchy. If not,
assume all meshes are in the root. */
/** @todo make it possible to choose the scene */
if(importer->defaultScene() != -1) {
Containers::Optional<Trade::SceneData> scene;
{
Trade::Implementation::Duration d{importConversionTime};
if(!(scene = importer->scene(importer->defaultScene()))) {
Error{} << "Cannot import scene" << importer->defaultScene() << "for mesh concatenation";
return 1;
}
}
Containers::Array<Trade::MeshData> flattenedMeshes;
{
Trade::Implementation::Duration d{conversionTime};
/** @todo once there are 2D scenes, check the scene is 3D */
for(const Containers::Triple<UnsignedInt, Int, Matrix4>& meshTransformation: SceneTools::flattenMeshHierarchy3D(*scene))
arrayAppend(flattenedMeshes, MeshTools::transform3D(meshes[meshTransformation.first()], meshTransformation.third()));
}
meshes = std::move(flattenedMeshes);
}
Containers::Array<Trade::MeshData> flattenedMeshes;
{
Trade::Implementation::Duration d{conversionTime};
/** @todo once there are 2D scenes, check the scene is 3D */
for(const Containers::Triple<UnsignedInt, Int, Matrix4>& meshTransformation: SceneTools::flattenMeshHierarchy3D(*scene))
arrayAppend(flattenedMeshes, MeshTools::transform3D(meshes[meshTransformation.first()], meshTransformation.third()));
/** @todo this will assert if the meshes have incompatible primitives
(such as some triangles, some lines), or if they have
loops/strips/fans -- handle that explicitly */
mesh = MeshTools::concatenate(meshes);
}
meshes = std::move(flattenedMeshes);
}
/* Concatenate all meshes together */
{
Trade::Implementation::Duration d{conversionTime};
/** @todo this will assert if the meshes have incompatible primitives
(such as some triangles, some lines), or if they have
loops/strips/fans -- handle that explicitly */
mesh = MeshTools::concatenate(meshes);
/* Otherwise import just one */
} else {
Trade::Implementation::Duration d{importConversionTime};
if(!(mesh = importer->mesh(args.value<UnsignedInt>("mesh"), args.value<UnsignedInt>("mesh-level")))) {
Error{} << "Cannot import the mesh";
return 4;
}
}
/* Otherwise import just one */
} else {
Trade::Implementation::Duration d{importTime};
if(!(mesh = importer->mesh(args.value<UnsignedInt>("mesh"), args.value<UnsignedInt>("mesh-level")))) {
Error{} << "Cannot import the mesh";
return 4;
/* Filter mesh attributes, if requested */
/** @todo move outside of the --mesh / --concatenate-meshes branch once
it's possible to filter attributes by name */
if(Containers::StringView onlyAttributes = args.value<Containers::StringView>("only-mesh-attributes")) {
const Containers::Optional<Containers::Array<UnsignedInt>> only = Utility::String::parseNumberSequence(onlyAttributes, 0, mesh->attributeCount());
if(!only) return 2;
/** @todo use MeshTools::filterOnlyAttributes() once it has a
rvalue overload that transfers ownership */
Containers::Array<Trade::MeshAttributeData> attributes;
arrayReserve(attributes, only->size());
for(UnsignedInt i: *only)
arrayAppend(attributes, mesh->attributeData(i));
const Trade::MeshIndexData indices{mesh->indices()};
const UnsignedInt vertexCount = mesh->vertexCount();
mesh = Trade::MeshData{mesh->primitive(),
mesh->releaseIndexData(), indices,
mesh->releaseVertexData(), std::move(attributes),
vertexCount};
}
}
/* Filter mesh attributes, if requested */
if(const Containers::StringView onlyMeshAttributes = args.value<Containers::StringView>("only-mesh-attributes")) {
const Containers::Optional<Containers::Array<UnsignedInt>> only = Utility::String::parseNumberSequence(onlyMeshAttributes, 0, mesh->attributeCount());
if(!only) return 2;
/** @todo use MeshTools::filterOnlyAttributes() once it has a rvalue
overload that transfers ownership */
Containers::Array<Trade::MeshAttributeData> attributes;
arrayReserve(attributes, only->size());
for(UnsignedInt i: *only)
arrayAppend(attributes, mesh->attributeData(i));
const Trade::MeshIndexData indices{mesh->indices()};
const UnsignedInt vertexCount = mesh->vertexCount();
mesh = Trade::MeshData{mesh->primitive(),
mesh->releaseIndexData(), indices,
mesh->releaseVertexData(), std::move(attributes),
vertexCount};
}
/* Create an importer instance that contains just the single mesh for
further step, without any other data. Simpler than having to
special-case the single-mesh case in all following steps. */
struct SingleMeshImporter: Trade::AbstractImporter {
explicit SingleMeshImporter(Trade::MeshData&& mesh): mesh{std::move(mesh)} {}
/* Remove duplicate vertices, if requested */
if(args.isSet("remove-duplicate-vertices")) {
const UnsignedInt beforeVertexCount = mesh->vertexCount();
{
Trade::Implementation::Duration d{conversionTime};
mesh = MeshTools::removeDuplicates(*std::move(mesh));
}
if(args.isSet("verbose"))
Debug{} << "Duplicate removal:" << beforeVertexCount << "->" << mesh->vertexCount() << "vertices";
Trade::ImporterFeatures doFeatures() const override { return {}; } /* LCOV_EXCL_LINE */
bool doIsOpened() const override { return true; }
void doClose() override {} /* LCOV_EXCL_LINE */
UnsignedInt doMeshCount() const override { return 1; }
Containers::Optional<Trade::MeshData> doMesh(UnsignedInt, UnsignedInt) override {
return MeshTools::reference(mesh);
}
Trade::MeshData mesh;
};
importer.emplace<SingleMeshImporter>(*std::move(mesh));
}
/* Remove duplicate vertices with fuzzy comparison, if requested */
/** @todo accept two values for float and double fuzzy comparison */
if(args.value<Containers::StringView>("remove-duplicate-vertices-fuzzy")) {
const UnsignedInt beforeVertexCount = mesh->vertexCount();
{
Trade::Implementation::Duration d{conversionTime};
mesh = MeshTools::removeDuplicatesFuzzy(*std::move(mesh), args.value<Float>("remove-duplicate-vertices-fuzzy"));
/* 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<Trade::MeshData> meshes;
if(args.isSet("remove-duplicate-vertices") ||
args.value<Containers::StringView>("remove-duplicate-vertices-fuzzy"))
{
for(UnsignedInt i = 0; i != importer->meshCount(); ++i) {
Containers::Optional<Trade::MeshData> mesh;
{
/** @todo handle mesh levels here, once any plugin is capable
of importing them */
Trade::Implementation::Duration d{importConversionTime};
if(!(mesh = importer->mesh(i))) {
Error{} << "Cannot import mesh" << i;
return 1;
}
}
const UnsignedInt beforeVertexCount = mesh->vertexCount();
const bool fuzzy = !!args.value<Containers::StringView>("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<Float>("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";
}
arrayAppend(meshes, *std::move(mesh));
}
if(args.isSet("verbose"))
Debug{} << "Fuzzy duplicate removal:" << beforeVertexCount << "->" << mesh->vertexCount() << "vertices";
}
/* Assume there's always one passed --converter option less, and the last
@ -516,43 +602,118 @@ the first mesh.)")
if(i < args.arrayValueCount("converter-options"))
Implementation::setOptions(*converter, "AnySceneConverter", args.arrayValue("converter-options", i));
/* This is the last --converter (or the implicit AnySceneConverter at
the end), output to a file and exit the loop */
if(i + 1 >= converterCount && ((converter->features() & Trade::SceneConverterFeature::ConvertMeshToFile) || converter->features() >= (Trade::SceneConverterFeature::ConvertMultipleToFile|Trade::SceneConverterFeature::AddMeshes))) {
/* No verbose output for just one converter */
if(converterCount > 1 && args.isSet("verbose"))
Debug{} << "Saving output (" << Debug::nospace << (i+1) << Debug::nospace << "/" << Debug::nospace << converterCount << Debug::nospace << ") with" << converterName << Debug::nospace << "...";
/* Decide if this is the last converter, capable of saving to a file */
const bool isLastConverter = i + 1 >= converterCount && (converter->features() & (Trade::SceneConverterFeature::ConvertMeshToFile|Trade::SceneConverterFeature::ConvertMultipleToFile));
Trade::Implementation::Duration d{conversionTime};
if(!converter->convertToFile(*mesh, args.value("output"))) {
Error{} << "Cannot save file" << args.value("output");
return 5;
/* No verbose output for just one converter */
if(converterCount > 1 && args.isSet("verbose")) {
if(isLastConverter) {
Debug{} << "Saving output (" << Debug::nospace << (i+1) << Debug::nospace << "/" << Debug::nospace << converterCount << Debug::nospace << ") with" << converterName << Debug::nospace << "...";
} else {
CORRADE_INTERNAL_ASSERT(i < converterCount);
Debug{} << "Processing (" << Debug::nospace << (i+1) << Debug::nospace << "/" << Debug::nospace << converterCount << Debug::nospace << ") with" << converterName << Debug::nospace << "...";
}
}
break;
/* This is the last --converter (or the implicit AnySceneConverter at
the end), output to a file */
if(isLastConverter) {
{
Trade::Implementation::Duration d{conversionTime};
if(!converter->beginFile(args.value("output"))) {
Error{} << "Cannot begin conversion of file" << args.value("output");
return 1;
}
}
/* This is not the last converter, expect that it's capable of
ConvertMesh */
converting to an importer instance (or a MeshData wrapped in an
importer instance) */
} else {
CORRADE_INTERNAL_ASSERT(i < converterCount);
if(converterCount > 1 && args.isSet("verbose"))
Debug{} << "Processing (" << Debug::nospace << (i+1) << Debug::nospace << "/" << Debug::nospace << converterCount << Debug::nospace << ") with" << converterName << Debug::nospace << "...";
if(!(converter->features() & Trade::SceneConverterFeature::ConvertMesh)) {
Error{} << converterName << "doesn't support mesh conversion, only" << converter->features();
if(!(converter->features() & (Trade::SceneConverterFeature::ConvertMesh|Trade::SceneConverterFeature::ConvertMultiple))) {
Error{} << converterName << "doesn't support importer conversion, only" << converter->features();
return 6;
}
{
Trade::Implementation::Duration d{conversionTime};
if(!converter->begin()) {
Error{} << "Cannot begin importer conversion";
return 1;
}
}
}
/* Contents to convert, by default all of them */
/** @todo make it possible to filter this on the command line, once the
converters receive this for SceneData, MaterialData and TextureData
as well */
Trade::SceneContents contents = ~Trade::SceneContents{};
/* If there are any loose meshes from previous conversion steps, add
them directly instead, and clear the array so the next iteration (if
any) takes them from the importer instead */
if(meshes) {
if(!(Trade::sceneContentsFor(*converter) & Trade::SceneContent::Meshes)) {
Warning{} << "Ignoring" << meshes.size() << "meshes not supported by the converter";
} else for(UnsignedInt i = 0; i != meshes.size(); ++i) {
Trade::Implementation::Duration d{conversionTime};
if(!converter->add(meshes[i])) {
Error{} << "Cannot add mesh" << i;
return 1;
}
}
/* Ensure the meshes are not added by addSupportedImporterContents()
below. Do this also in case the converter actually doesn't
support mesh addition, as it would otherwise cause two warnings
about the same thing being printed. */
contents &= ~Trade::SceneContent::Meshes;
/* 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 two chained conversion steps
that each change the output to verify the old meshes don't get
reused in the next step again */
meshes = {};
}
{
Trade::Implementation::Duration d{importConversionTime};
if(!converter->addSupportedImporterContents(*importer, contents)) {
Error{} << "Cannot add importer contents";
return 5;
}
}
/* This is the last --converter (or the implicit AnySceneConverter at
the end), end the file and exit the loop */
if(isLastConverter) {
Trade::Implementation::Duration d{conversionTime};
if(!converter->endFile()) {
Error{} << "Cannot end conversion of file" << args.value("output");
return 5;
}
break;
/* This is not the last converter, save the resulting importer instance
for the next loop iteration. By design, the importer should not
depend on any data from the converter instance, only on the
converter plugin, so we should be fine replacing the converter with
a different one in the next iteration and keeping just the importer
returned from it. */
} else {
Trade::Implementation::Duration d{conversionTime};
if(!(mesh = converter->convert(*mesh))) {
Error{} << converterName << "cannot convert the mesh";
return 7;
if(!(importer = converter->end())) {
Error{} << "Cannot end importer conversion";
return 1;
}
}
}
if(args.isSet("profile")) {
Debug{} << "Import took" << UnsignedInt(std::chrono::duration_cast<std::chrono::milliseconds>(importTime).count())/1.0e3f << "seconds, conversion"
Debug{} << "Import and conversion took" << UnsignedInt(std::chrono::duration_cast<std::chrono::milliseconds>(importConversionTime).count())/1.0e3f << "seconds, conversion"
<< UnsignedInt(std::chrono::duration_cast<std::chrono::milliseconds>(conversionTime).count())/1.0e3f << "seconds";
}
}

Loading…
Cancel
Save