diff --git a/application/F3DOptionsTools.cxx b/application/F3DOptionsTools.cxx index 6b8041ed7a..a2bff104b5 100644 --- a/application/F3DOptionsTools.cxx +++ b/application/F3DOptionsTools.cxx @@ -65,6 +65,7 @@ static inline const std::array CLIOptions = {{ { "no-background", "", "No background when render to file", "", "1" }, { "help", "h", "Print help", "", "" }, { "version", "", "Print version details", "", "" }, { "list-readers", "", "Print the list of readers", "", "" }, + { "force-reader", "", "Force a specific reader to be used, disrigarding the file extension", "", "1"}, { "list-bindings", "", "Print the list of interaction bindings and exits, ignored with `--no-render`, only considers the first file group.", "", "1" }, { "config", "", "Specify the configuration file to use. absolute/relative path or filename/filestem to search in configuration file locations", "", "" }, { "no-config", "", "Do not read the configuration file", "", "1" }, diff --git a/application/F3DOptionsTools.h b/application/F3DOptionsTools.h index 9969f8818a..1005475125 100644 --- a/application/F3DOptionsTools.h +++ b/application/F3DOptionsTools.h @@ -80,6 +80,7 @@ static inline const std::map LibOptionsNames { "animation-autoplay", "scene.animation.autoplay" }, { "animation-index", "scene.animation.index" }, { "animation-speed-factor", "scene.animation.speed_factor" }, + { "force-reader", "scene.force_reader" }, { "font-file", "ui.font_file" }, { "font-scale", "ui.scale" }, { "point-sprites", "model.point_sprites.enable" }, diff --git a/application/testing/CMakeLists.txt b/application/testing/CMakeLists.txt index 73fcc24afc..04272c63c0 100644 --- a/application/testing/CMakeLists.txt +++ b/application/testing/CMakeLists.txt @@ -1121,6 +1121,16 @@ if(F3D_PLUGIN_BUILD_ALEMBIC AND F3D_PLUGIN_BUILD_ASSIMP) f3d_test(NAME TestReadersListMultiplePlugins ARGS --list-readers --load-plugins=assimp,alembic NO_BASELINE REGEXP_FAIL "Plugin failed to load") endif() +if(F3D_PLUGIN_BUILD_EXODUS) + f3d_test(NAME TestForceReaderExodusFail DATA BoxAnimated.gltf ARGS --force-reader=ExodusII NO_BASELINE REGEXP "failed to load scene") + f3d_test(NAME TestForceReaderExodusPass DATA disk_out_ref.ex2 ARGS --force-reader=ExodusII NO_BASELINE REGEXP_FAIL "failed to load scene") +endif() + +if(F3D_PLUGIN_BUILD_DRACO) + f3d_test(NAME TestForceReaderGLTFDraco DATA BoxAnimated.gltf ARGS --force-reader=GLTFDraco NO_BASELINE REGEXP_FAIL "failed to load scene") + f3d_test(NAME TestForceReaderGLTF DATA BoxAnimated.gltf ARGS --force-reader=GLTF NO_BASELINE REGEXP_FAIL "failed to load scene") +endif() + # Test bindings-list display f3d_test(NAME TestBindingsList ARGS --list-bindings REGEXP "Any.5 Toggle Orthographic Projection") diff --git a/doc/libf3d/OPTIONS.md b/doc/libf3d/OPTIONS.md index 9f14a6be86..8438e7b851 100644 --- a/doc/libf3d/OPTIONS.md +++ b/doc/libf3d/OPTIONS.md @@ -28,6 +28,7 @@ See the [APIs](#APIs) details below for more info. | scene.animation.time | double
optional
load | Set the animation time to load. | \-\-animation-time | | scene.camera.index | int
optional
load | Select the scene camera to use when available in the file.
The default scene always uses automatic camera. | \-\-camera-index | | scene.up_direction | direction
+Y
load | Define the Up direction. It impacts the grid, the axis, the HDRI and the camera. | \-\-up | +| scene.force_reader | string
optional
load | Force a specific reader to be used, disrigarding the file extension. | \-\-force-reader | | scene.camera.orthographic | bool
optional
load | Set to true to force orthographic projection. Model specified by default, which is false if not specified. | \-\-camera\-orthographic | ## Interactor Options diff --git a/doc/user/OPTIONS.md b/doc/user/OPTIONS.md index b191850ffe..81cab4ec8c 100644 --- a/doc/user/OPTIONS.md +++ b/doc/user/OPTIONS.md @@ -14,6 +14,7 @@ F3D behavior can be fully controlled from the command line using the following o | -h, \-\-help | | Print _help_ and exit. Ignore `--verbose`. | | \-\-version | | Show _version_ information and exit. Ignore `--verbose`. | | \-\-list-readers | | List available _readers_ and exit. Ignore `--verbose`. | +| \-\-force-reader=\ | string
- | Force a specific reader to be used, disrigarding the file extension. | | \-\-list-bindings | | List available _bindings_ and exit. Ignore `--verbose`. | | \-\-list-rendering-backends | | List available _rendering backends_ and exit. Ignore `--verbose`. | | \-\-config=\ | string
config | Specify the [configuration file](CONFIGURATION_FILE.md) to use. Supports absolute/relative path but also filename/filestem to search for in standard configuration file locations. | diff --git a/library/options.json b/library/options.json index da404c9a8d..dc92cafc1a 100644 --- a/library/options.json +++ b/library/options.json @@ -25,6 +25,9 @@ "orthographic": { "type": "bool" } + }, + "force_reader": { + "type": "string" } }, "render": { diff --git a/library/plugin/plugin.h b/library/plugin/plugin.h index 5a172b079b..14efac42ba 100644 --- a/library/plugin/plugin.h +++ b/library/plugin/plugin.h @@ -37,7 +37,7 @@ class plugin /** * Get the name of this plugin */ - const std::string& getName() + const std::string& getName() const { return this->Name; } @@ -45,7 +45,7 @@ class plugin /** * Get the description of this plugin */ - const std::string& getDescription() + const std::string& getDescription() const { return this->Description; } @@ -53,7 +53,7 @@ class plugin /** * Get the version of this plugin */ - const std::string& getVersion() + const std::string& getVersion() const { return this->Version; } @@ -61,7 +61,7 @@ class plugin /** * Get the list of readers created by this plugin */ - const std::vector>& getReaders() + const std::vector>& getReaders() const { return this->Readers; } @@ -71,7 +71,7 @@ class plugin * Set/Get the origin of this plugin, usually static, system or an actual path * Set by the engine. */ - const std::string& getOrigin() + const std::string& getOrigin() const { return this->Origin; } diff --git a/library/private/factory.h b/library/private/factory.h index c1edca7f1d..ddd883e99d 100644 --- a/library/private/factory.h +++ b/library/private/factory.h @@ -17,6 +17,8 @@ #include "reader.h" #include +#include +#include #include namespace f3d @@ -44,7 +46,7 @@ class factory /** * Get the reader that can read the given file, nullptr if none */ - reader* getReader(const std::string& fileName); + reader* getReader(const std::string& fileName, std::optional forceReader); /** * Get the list of the registered plugins diff --git a/library/src/factory.cxx.in b/library/src/factory.cxx.in index 97a58bffee..91c9994ecb 100644 --- a/library/src/factory.cxx.in +++ b/library/src/factory.cxx.in @@ -40,19 +40,26 @@ factory::plugin_initializer_t factory::getStaticInitializer(const std::string& p } //---------------------------------------------------------------------------- -reader* factory::getReader(const std::string& fileName) +reader* factory::getReader(const std::string& fileName, std::optional forceReader) { int bestScore = -1; reader* bestReader = nullptr; - for (auto p : this->Plugins) + for (const auto* plugin : this->Plugins) { - for (auto r : p->getReaders()) + for (const auto& reader : plugin->getReaders()) { - if (r->getScore() > bestScore && r->canRead(fileName)) + if (forceReader.has_value()) { - bestScore = r->getScore(); - bestReader = r.get(); + if (reader->getName() == *forceReader) + { + return reader.get(); + } + } + else if (reader->getScore() > bestScore && reader->canRead(fileName)) + { + bestScore = reader->getScore(); + bestReader = reader.get(); } } } diff --git a/library/src/scene_impl.cxx b/library/src/scene_impl.cxx index ff10327b42..d58d00724a 100644 --- a/library/src/scene_impl.cxx +++ b/library/src/scene_impl.cxx @@ -4,6 +4,7 @@ #include "interactor_impl.h" #include "log.h" #include "options.h" +#include "scene.h" #include "window_impl.h" #include "factory.h" @@ -11,6 +12,7 @@ #include "vtkF3DMemoryMesh.h" #include "vtkF3DMetaImporter.h" +#include #include #include #include @@ -234,7 +236,8 @@ scene& scene_impl::add(const std::vector& filePaths) } // Recover the importer for the provided file path - f3d::reader* reader = f3d::factory::instance()->getReader(filePath.string()); + f3d::reader* reader = f3d::factory::instance()->getReader( + filePath.string(), this->Internals->Options.scene.force_reader); if (reader) { log::debug( @@ -316,7 +319,8 @@ scene& scene_impl::clear() //---------------------------------------------------------------------------- bool scene_impl::supports(const fs::path& filePath) { - return f3d::factory::instance()->getReader(filePath.string()) != nullptr; + return f3d::factory::instance()->getReader( + filePath.string(), this->Internals->Options.scene.force_reader) != nullptr; } //---------------------------------------------------------------------------- diff --git a/library/testing/CMakeLists.txt b/library/testing/CMakeLists.txt index 9d577e3133..0565a95541 100644 --- a/library/testing/CMakeLists.txt +++ b/library/testing/CMakeLists.txt @@ -15,6 +15,7 @@ list(APPEND libf3dSDKTests_list TestSDKMultiColoring.cxx TestSDKOptions.cxx TestSDKOptionsIO.cxx + TestSDKReaderSelection.cxx TestSDKRenderFinalShader.cxx TestSDKUtils.cxx TestSDKWindowAuto.cxx diff --git a/library/testing/TestSDKReaderSelection.cxx b/library/testing/TestSDKReaderSelection.cxx new file mode 100644 index 0000000000..70aaa09532 --- /dev/null +++ b/library/testing/TestSDKReaderSelection.cxx @@ -0,0 +1,45 @@ +#include "PseudoUnitTest.h" +#include "TestSDKHelpers.h" + +#include +#include +#include +#include + +namespace fs = std::filesystem; +int TestSDKReaderSelection(int argc, char* argv[]) +{ + PseudoUnitTest test; + + f3d::log::setVerboseLevel(f3d::log::VerboseLevel::DEBUG); + f3d::engine::autoloadPlugins(); + + // Test file path setup + std::string monkey = std::string(argv[1]) + "data/red_translucent_monkey.gltf"; + + // Test default reader (no preference) + { + f3d::engine engine = f3d::engine::create(true); + f3d::scene& scene = engine.getScene(); + test("add with a single path", [&]() { scene.add(fs::path(monkey)); }); + } + + // Test Draco reader; GLTF is by-default + { + f3d::engine engine = f3d::engine::create(true); + engine.getOptions().scene.force_reader = "GLTFDraco"; + f3d::scene& scene = engine.getScene(); + test("Draco reader works", [&]() { scene.add(fs::path(monkey)); }); + test("Reader is GLTFDraco", engine.getOptions().scene.force_reader == "GLTFDraco"); + } + + // Test GLTF reader; + { + f3d::engine engine = f3d::engine::create(true); + engine.getOptions().scene.force_reader = "GLTF"; + f3d::scene& scene = engine.getScene(); + test("GLTF reader works", [&]() { scene.add(fs::path(monkey)); }); + test("Reader is GLTF", engine.getOptions().scene.force_reader == "GLTF"); + } + return test.result(); +}