diff --git a/doc/changelog.dox b/doc/changelog.dox index b57171cd5..4364d67d2 100644 --- a/doc/changelog.dox +++ b/doc/changelog.dox @@ -287,7 +287,8 @@ See also: well as support in @ref Trade::AnySceneImporter "AnySceneImporter" - The @ref Trade::AbstractSceneConverter plugin interface gained support for batch conversion of whole scenes --- meshes, hierarchies, materials, - textures, animations and other data + textures, animations and other data; @relativeref{Trade,AnySceneConverter} + is updated to support batch conversion as well - 1D and 3D image support in @ref Trade::AbstractImageConverter - @ref Trade::LightData got extended to support light attenuation and range parameters as well and spot light inner and outer angle diff --git a/src/MagnumPlugins/AnySceneConverter/AnySceneConverter.cpp b/src/MagnumPlugins/AnySceneConverter/AnySceneConverter.cpp index 2a84c7a4a..747350b79 100644 --- a/src/MagnumPlugins/AnySceneConverter/AnySceneConverter.cpp +++ b/src/MagnumPlugins/AnySceneConverter/AnySceneConverter.cpp @@ -25,15 +25,19 @@ #include "AnySceneConverter.h" +#include +#include #include #include #include #include #include /* for PluginMetadata::name() */ +#include /* CORRADE_UNUSED */ #include #include -#include "Magnum/Trade/ImageData.h" +#include "Magnum/Trade/MeshData.h" +#include "Magnum/Trade/SceneData.h" #include "MagnumPlugins/Implementation/propagateConfiguration.h" namespace Magnum { namespace Trade { @@ -47,7 +51,26 @@ AnySceneConverter::AnySceneConverter(PluginManager::AbstractManager& manager, co AnySceneConverter::~AnySceneConverter() = default; SceneConverterFeatures AnySceneConverter::doFeatures() const { - return SceneConverterFeature::ConvertMeshToFile; + /* Report that we can convert meshes and scenes to files, because that the + plugin can do always as it dispatches there. But everything else is + reported only once an actual converter plugin is loaded -- i.e., after beginFile() is called. + + Additionally, to allow using beginFile() + add(MeshData) + endFile() + with converters that only support ConvertMeshToFile, we "fake" report + AddMesh supported as well. If we wouldn't, the base class would try to + go through convertToFile() instead, causing the mesh to not be delegated + to _converter->add() but instead supplied to a whole new plugin + instance. Then, upon delegating to _converter->endFile(), it would fail + due to having 0 meshes. */ + + SceneConverterFeatures delegatedFeatures = + _converter ? _converter->features() : SceneConverterFeatures{}; + if(delegatedFeatures & SceneConverterFeature::ConvertMeshToFile) + delegatedFeatures |= SceneConverterFeature::AddMeshes; + + return SceneConverterFeature::ConvertMeshToFile| + SceneConverterFeature::ConvertMultipleToFile| + delegatedFeatures; } bool AnySceneConverter::doConvertToFile(const MeshData& mesh, const Containers::StringView filename) { @@ -94,6 +117,165 @@ bool AnySceneConverter::doConvertToFile(const MeshData& mesh, const Containers:: return converter->convertToFile(mesh, filename); } +void AnySceneConverter::doAbort() { + _converter->abort(); + _converter = {}; +} + +bool AnySceneConverter::doBeginFile(const Containers::StringView filename) { + CORRADE_INTERNAL_ASSERT(manager()); + + /* We don't detect any double extensions yet, so we can normalize just the + extension. In case we eventually might, it'd have to be split() instead + to save at least by normalizing just the filename and not the path. */ + const Containers::String normalizedExtension = Utility::String::lowercase(Utility::Path::splitExtension(filename).second()); + + /* Detect the plugin from extension */ + Containers::StringView plugin; + if(normalizedExtension == ".ply"_s) + plugin = "StanfordSceneConverter"_s; + else { + Error{} << "Trade::AnySceneConverter::beginFile(): cannot determine the format of" << filename; + return false; + } + + /* Try to load the plugin */ + if(!(manager()->load(plugin) & PluginManager::LoadState::Loaded)) { + Error{} << "Trade::AnySceneConverter::beginFile(): cannot load the" << plugin << "plugin"; + return false; + } + + const PluginManager::PluginMetadata* const metadata = manager()->metadata(plugin); + CORRADE_INTERNAL_ASSERT(metadata); + if(flags() & SceneConverterFlag::Verbose) { + Debug d; + d << "Trade::AnySceneConverter::beginFile(): using" << plugin; + if(plugin != metadata->name()) + d << "(provided by" << metadata->name() << Debug::nospace << ")"; + } + + /* Instantiate the plugin, propagate flags */ + Containers::Pointer converter = static_cast*>(manager())->instantiate(plugin); + converter->setFlags(flags()); + + /* Propagate configuration */ + Magnum::Implementation::propagateConfiguration("Trade::AnySceneConverter::beginFile():", {}, metadata->name(), configuration(), converter->configuration()); + + /* Try to begin the file (error output should be printed by the plugin + itself) */ + if(!converter->beginFile(filename)) return false; + + /* Success, save the instance */ + _converter = std::move(converter); + return true; +} + +bool AnySceneConverter::doEndFile(Containers::StringView) { + /* Destroy the converter instance after the operation finishes to avoid + keeping now-useless state around */ + const bool out = _converter->endFile(); + _converter = {}; + return out; +} + +/* It's easier to use CORRADE_UNUSED than to have #ifndef CORRADE_NO_ASSERT */ + +bool AnySceneConverter::doAdd(CORRADE_UNUSED const UnsignedInt id, const SceneData& scene, const Containers::StringView name) { + CORRADE_INTERNAL_ASSERT(id == _converter->sceneCount()); + return !!_converter->add(scene, name); +} + +void AnySceneConverter::doSetSceneFieldName(const UnsignedInt field, const Containers::StringView name) { + return _converter->setSceneFieldName(sceneFieldCustom(field), name); +} + +void AnySceneConverter::doSetObjectName(const UnsignedLong object, const Containers::StringView name) { + return _converter->setObjectName(object, name); +} + +void AnySceneConverter::doSetDefaultScene(const UnsignedInt id) { + return _converter->setDefaultScene(id); +} + +bool AnySceneConverter::doAdd(CORRADE_UNUSED const UnsignedInt id, const AnimationData& animation, const Containers::StringView name) { + CORRADE_INTERNAL_ASSERT(id == _converter->animationCount()); + return !!_converter->add(animation, name); +} + +bool AnySceneConverter::doAdd(CORRADE_UNUSED const UnsignedInt id, const LightData& light, const Containers::StringView name) { + CORRADE_INTERNAL_ASSERT(id == _converter->lightCount()); + return !!_converter->add(light, name); +} + +bool AnySceneConverter::doAdd(CORRADE_UNUSED const UnsignedInt id, const CameraData& camera, const Containers::StringView name) { + CORRADE_INTERNAL_ASSERT(id == _converter->cameraCount()); + return !!_converter->add(camera, name); +} + +bool AnySceneConverter::doAdd(CORRADE_UNUSED const UnsignedInt id, const SkinData2D& skin, const Containers::StringView name) { + CORRADE_INTERNAL_ASSERT(id == _converter->skin2DCount()); + return !!_converter->add(skin, name); +} + +bool AnySceneConverter::doAdd(CORRADE_UNUSED const UnsignedInt id, const SkinData3D& skin, const Containers::StringView name) { + CORRADE_INTERNAL_ASSERT(id == _converter->skin3DCount()); + return !!_converter->add(skin, name); +} + +bool AnySceneConverter::doAdd(CORRADE_UNUSED const UnsignedInt id, const MeshData& mesh, const Containers::StringView name) { + CORRADE_INTERNAL_ASSERT(id == _converter->meshCount()); + return !!_converter->add(mesh, name); +} + +bool AnySceneConverter::doAdd(CORRADE_UNUSED const UnsignedInt id, const Containers::Iterable meshLevels, const Containers::StringView name) { + CORRADE_INTERNAL_ASSERT(id == _converter->meshCount()); + return !!_converter->add(meshLevels, name); +} + +void AnySceneConverter::doSetMeshAttributeName(const UnsignedShort attribute, const Containers::StringView name) { + return _converter->setMeshAttributeName(meshAttributeCustom(attribute), name); +} + +bool AnySceneConverter::doAdd(CORRADE_UNUSED const UnsignedInt id, const MaterialData& material, const Containers::StringView name) { + CORRADE_INTERNAL_ASSERT(id == _converter->materialCount()); + return !!_converter->add(material, name); +} + +bool AnySceneConverter::doAdd(CORRADE_UNUSED const UnsignedInt id, const TextureData& texture, const Containers::StringView name) { + CORRADE_INTERNAL_ASSERT(id == _converter->textureCount()); + return !!_converter->add(texture, name); +} + +bool AnySceneConverter::doAdd(CORRADE_UNUSED const UnsignedInt id, const ImageData1D& image, const Containers::StringView name) { + CORRADE_INTERNAL_ASSERT(id == _converter->image1DCount()); + return !!_converter->add(image, name); +} + +bool AnySceneConverter::doAdd(CORRADE_UNUSED const UnsignedInt id, const Containers::Iterable imageLevels, const Containers::StringView name) { + CORRADE_INTERNAL_ASSERT(id == _converter->image1DCount()); + return !!_converter->add(imageLevels, name); +} + +bool AnySceneConverter::doAdd(CORRADE_UNUSED const UnsignedInt id, const ImageData2D& image, const Containers::StringView name) { + CORRADE_INTERNAL_ASSERT(id == _converter->image2DCount()); + return !!_converter->add(image, name); +} + +bool AnySceneConverter::doAdd(CORRADE_UNUSED const UnsignedInt id, const Containers::Iterable imageLevels, const Containers::StringView name) { + CORRADE_INTERNAL_ASSERT(id == _converter->image2DCount()); + return !!_converter->add(imageLevels, name); +} + +bool AnySceneConverter::doAdd(CORRADE_UNUSED const UnsignedInt id, const ImageData3D& image, const Containers::StringView name) { + CORRADE_INTERNAL_ASSERT(id == _converter->image3DCount()); + return !!_converter->add(image, name); +} + +bool AnySceneConverter::doAdd(CORRADE_UNUSED const UnsignedInt id, const Containers::Iterable imageLevels, const Containers::StringView name) { + CORRADE_INTERNAL_ASSERT(id == _converter->image3DCount()); + return !!_converter->add(imageLevels, name); +} + }} CORRADE_PLUGIN_REGISTER(AnySceneConverter, Magnum::Trade::AnySceneConverter, diff --git a/src/MagnumPlugins/AnySceneConverter/AnySceneConverter.h b/src/MagnumPlugins/AnySceneConverter/AnySceneConverter.h index c83e1bf04..d11296579 100644 --- a/src/MagnumPlugins/AnySceneConverter/AnySceneConverter.h +++ b/src/MagnumPlugins/AnySceneConverter/AnySceneConverter.h @@ -30,6 +30,8 @@ * @m_since{2020,06} */ +#include + #include "Magnum/Trade/AbstractSceneConverter.h" #include "MagnumPlugins/AnySceneConverter/configure.h" @@ -96,14 +98,20 @@ information. @section Trade-AnySceneConverter-proxy Interface proxying and option propagation -On a call to @ref convertToFile(), a target file format is detected from the -extension and a corresponding plugin is loaded. After that, flags set via -@ref setFlags() and options set through @ref configuration() are propagated to -the concrete implementation, with a warning emitted in case given option is not -present in the default configuration of the target plugin. +On a call to @ref convertToFile() or @ref beginFile(), a target file format is +detected from the extension and a corresponding plugin is loaded. After that, +flags set via @ref setFlags() and options set through @ref configuration() are +propagated to the concrete implementation. A warning is emitted in case an +option set is not present in the default configuration of the target plugin. + +The @ref features() initially report just +@ref SceneConverterFeature::ConvertMeshToFile and @relativeref{SceneConverterFeature,ConvertMultipleToFile} --- i.e., not +advertising support for any actual data types. These are included only once +@ref beginFile() is called, taken from the concrete plugin implementation. -The output of the @ref convertToFile() function called on the concrete -implementation is then proxied back. +Calls to the @ref endFile(), @ref add() and related functions are then proxied +to the concrete implementation. The @ref abort() function aborts and destroys +the internally instantiated plugin; @ref isConverting() works as usual. */ class MAGNUM_ANYSCENECONVERTER_EXPORT AnySceneConverter: public AbstractSceneConverter { public: @@ -118,6 +126,39 @@ class MAGNUM_ANYSCENECONVERTER_EXPORT AnySceneConverter: public AbstractSceneCon private: MAGNUM_ANYSCENECONVERTER_LOCAL SceneConverterFeatures doFeatures() const override; MAGNUM_ANYSCENECONVERTER_LOCAL bool doConvertToFile(const MeshData& mesh, Containers::StringView filename) override; + + void doAbort() override; + bool doBeginFile(Containers::StringView filename) override; + bool doEndFile(Containers::StringView filename) override; + + bool doAdd(UnsignedInt id, const SceneData& scene, Containers::StringView name) override; + void doSetSceneFieldName(UnsignedInt field, Containers::StringView name) override; + void doSetObjectName(UnsignedLong object, Containers::StringView name) override; + void doSetDefaultScene(UnsignedInt id) override; + + bool doAdd(UnsignedInt id, const AnimationData& animation, Containers::StringView name) override; + bool doAdd(UnsignedInt id, const LightData& light, Containers::StringView name) override; + bool doAdd(UnsignedInt id, const CameraData& camera, Containers::StringView name) override; + bool doAdd(UnsignedInt id, const SkinData2D& skin, Containers::StringView name) override; + bool doAdd(UnsignedInt id, const SkinData3D& skin, Containers::StringView name) override; + + bool doAdd(UnsignedInt id, const MeshData& mesh, Containers::StringView name) override; + bool doAdd(UnsignedInt id, Containers::Iterable meshLevels, Containers::StringView name) override; + void doSetMeshAttributeName(UnsignedShort attribute, Containers::StringView name) override; + + bool doAdd(UnsignedInt id, const MaterialData& material, Containers::StringView name) override; + bool doAdd(UnsignedInt id, const TextureData& texture, Containers::StringView name) override; + + bool doAdd(UnsignedInt id, const ImageData1D& image, Containers::StringView name) override; + bool doAdd(UnsignedInt id, Containers::Iterable imageLevels, Containers::StringView name) override; + + bool doAdd(UnsignedInt id, const ImageData2D& image, Containers::StringView name) override; + bool doAdd(UnsignedInt id, Containers::Iterable imageLevels, Containers::StringView name) override; + + bool doAdd(UnsignedInt id, const ImageData3D& image, Containers::StringView name) override; + bool doAdd(UnsignedInt id, Containers::Iterable imageLevels, Containers::StringView name) override; + + Containers::Pointer _converter; }; }} diff --git a/src/MagnumPlugins/AnySceneConverter/Test/AnySceneConverterTest.cpp b/src/MagnumPlugins/AnySceneConverter/Test/AnySceneConverterTest.cpp index bb7807562..1b65dda77 100644 --- a/src/MagnumPlugins/AnySceneConverter/Test/AnySceneConverterTest.cpp +++ b/src/MagnumPlugins/AnySceneConverter/Test/AnySceneConverterTest.cpp @@ -24,6 +24,7 @@ */ #include +#include #include #include #include @@ -45,13 +46,21 @@ struct AnySceneConverterTest: TestSuite::Tester { explicit AnySceneConverterTest(); void convert(); - void detect(); + void convertBeginEnd(); - void unknown(); + void detectConvert(); + void detectBeginEnd(); - void propagateFlags(); - void propagateConfiguration(); - void propagateConfigurationUnknown(); + void unknownConvert(); + void unknownBeginEnd(); + + void propagateFlagsConvert(); + void propagateFlagsBeginEnd(); + + void propagateConfigurationConvert(); + void propagateConfigurationBeginEnd(); + void propagateConfigurationUnknownConvert(); + void propagateConfigurationUnknownBeginEnd(); /* configuration propagation fully tested in AnySceneImporter, as there the plugins have configuration subgroups as well */ @@ -63,23 +72,42 @@ constexpr struct { const char* name; const char* filename; const char* plugin; -} DetectData[]{ +} DetectConvertData[]{ + {"Stanford PLY", "bunny.ply", "StanfordSceneConverter"}, + /* Have at least one test case with uppercase */ + {"Stanford PLY uppercase", "ARMADI~1.PLY", "StanfordSceneConverter"} +}; + +constexpr struct { + const char* name; + const char* filename; + const char* plugin; +} DetectBeginEndData[]{ {"Stanford PLY", "bunny.ply", "StanfordSceneConverter"}, /* Have at least one test case with uppercase */ {"Stanford PLY uppercase", "ARMADI~1.PLY", "StanfordSceneConverter"} }; AnySceneConverterTest::AnySceneConverterTest() { - addTests({&AnySceneConverterTest::convert}); + addTests({&AnySceneConverterTest::convert, + &AnySceneConverterTest::convertBeginEnd}); + + addInstancedTests({&AnySceneConverterTest::detectConvert}, + Containers::arraySize(DetectConvertData)); + + addInstancedTests({&AnySceneConverterTest::detectBeginEnd}, + Containers::arraySize(DetectBeginEndData)); - addInstancedTests({&AnySceneConverterTest::detect}, - Containers::arraySize(DetectData)); + addTests({&AnySceneConverterTest::unknownConvert, + &AnySceneConverterTest::unknownBeginEnd, - addTests({&AnySceneConverterTest::unknown, + &AnySceneConverterTest::propagateFlagsConvert, + &AnySceneConverterTest::propagateFlagsBeginEnd, - &AnySceneConverterTest::propagateFlags, - &AnySceneConverterTest::propagateConfiguration, - &AnySceneConverterTest::propagateConfigurationUnknown}); + &AnySceneConverterTest::propagateConfigurationConvert, + &AnySceneConverterTest::propagateConfigurationBeginEnd, + &AnySceneConverterTest::propagateConfigurationUnknownConvert, + &AnySceneConverterTest::propagateConfigurationUnknownBeginEnd}); /* Load the plugin directly from the build tree. Otherwise it's static and already loaded. */ @@ -121,8 +149,41 @@ void AnySceneConverterTest::convert() { CORRADE_COMPARE_AS(filename, Utility::Path::join(ANYSCENEIMPORTER_TEST_DIR, "triangle.ply"), TestSuite::Compare::File); } -void AnySceneConverterTest::detect() { - auto&& data = DetectData[testCaseInstanceId()]; +void AnySceneConverterTest::convertBeginEnd() { + PluginManager::Manager manager{MAGNUM_PLUGINS_SCENECONVERTER_INSTALL_DIR}; + #ifdef ANYSCENECONVERTER_PLUGIN_FILENAME + CORRADE_VERIFY(manager.load(ANYSCENECONVERTER_PLUGIN_FILENAME) & PluginManager::LoadState::Loaded); + #endif + + /* Catch also ABI and interface mismatch errors */ + if(!(manager.load("StanfordSceneConverter") & PluginManager::LoadState::Loaded)) + CORRADE_SKIP("StanfordSceneConverter plugin can't be loaded."); + + Containers::String filename = Utility::Path::join(ANYSCENECONVERTER_TEST_OUTPUT_DIR, "file.ply"); + if(Utility::Path::exists(filename)) + CORRADE_VERIFY(Utility::Path::remove(filename)); + + const Vector3 positions[] { + {-0.5f, -0.5f, 0.0f}, + { 0.5f, -0.5f, 0.0f}, + { 0.0f, 0.5f, 0.0f} + }; + const Trade::MeshData mesh{MeshPrimitive::Triangles, {}, positions, { + Trade::MeshAttributeData{Trade::MeshAttribute::Position, Containers::arrayView(positions)} + }}; + + Containers::Pointer converter = manager.instantiate("AnySceneConverter"); + CORRADE_VERIFY(converter->beginFile(filename)); + CORRADE_COMPARE(converter->add(mesh), 0); + CORRADE_VERIFY(converter->endFile()); + + /* This file is reused in AnySceneImporter tests, so it's worth to save it + here */ + CORRADE_COMPARE_AS(filename, Utility::Path::join(ANYSCENEIMPORTER_TEST_DIR, "triangle.ply"), TestSuite::Compare::File); +} + +void AnySceneConverterTest::detectConvert() { + auto&& data = DetectConvertData[testCaseInstanceId()]; setTestCaseDescription(data.name); Containers::Pointer converter = _manager.instantiate("AnySceneConverter"); @@ -143,7 +204,28 @@ void AnySceneConverterTest::detect() { #endif } -void AnySceneConverterTest::unknown() { +void AnySceneConverterTest::detectBeginEnd() { + auto&& data = DetectConvertData[testCaseInstanceId()]; + setTestCaseDescription(data.name); + + Containers::Pointer converter = _manager.instantiate("AnySceneConverter"); + + std::ostringstream out; + Error redirectError{&out}; + CORRADE_VERIFY(!converter->beginFile(data.filename)); + #ifndef CORRADE_PLUGINMANAGER_NO_DYNAMIC_PLUGIN_SUPPORT + CORRADE_COMPARE(out.str(), Utility::formatString( + "PluginManager::Manager::load(): plugin {0} is not static and was not found in nonexistent\n" + "Trade::AnySceneConverter::beginFile(): cannot load the {0} plugin\n", + data.plugin)); + #else + CORRADE_COMPARE(out.str(), Utility::formatString( + "PluginManager::Manager::load(): plugin {0} was not found\n" + "Trade::AnySceneConverter::beginFile(): cannot load the {0} plugin\n", data.plugin)); + #endif +} + +void AnySceneConverterTest::unknownConvert() { Containers::Pointer converter = _manager.instantiate("AnySceneConverter"); std::ostringstream out; @@ -152,7 +234,16 @@ void AnySceneConverterTest::unknown() { CORRADE_COMPARE(out.str(), "Trade::AnySceneConverter::convertToFile(): cannot determine the format of mesh.obj\n"); } -void AnySceneConverterTest::propagateFlags() { +void AnySceneConverterTest::unknownBeginEnd() { + Containers::Pointer converter = _manager.instantiate("AnySceneConverter"); + + std::ostringstream out; + Error redirectError{&out}; + CORRADE_VERIFY(!converter->beginFile("mesh.obj")); + CORRADE_COMPARE(out.str(), "Trade::AnySceneConverter::beginFile(): cannot determine the format of mesh.obj\n"); +} + +void AnySceneConverterTest::propagateFlagsConvert() { PluginManager::Manager manager{MAGNUM_PLUGINS_SCENECONVERTER_INSTALL_DIR}; #ifdef ANYSCENECONVERTER_PLUGIN_FILENAME CORRADE_VERIFY(manager.load(ANYSCENECONVERTER_PLUGIN_FILENAME) & PluginManager::LoadState::Loaded); @@ -190,7 +281,47 @@ void AnySceneConverterTest::propagateFlags() { CORRADE_SKIP("No plugin with verbose output available to test flag propagation."); } -void AnySceneConverterTest::propagateConfiguration() { +void AnySceneConverterTest::propagateFlagsBeginEnd() { + PluginManager::Manager manager{MAGNUM_PLUGINS_SCENECONVERTER_INSTALL_DIR}; + #ifdef ANYSCENECONVERTER_PLUGIN_FILENAME + CORRADE_VERIFY(manager.load(ANYSCENECONVERTER_PLUGIN_FILENAME) & PluginManager::LoadState::Loaded); + #endif + + /* Catch also ABI and interface mismatch errors */ + if(!(manager.load("StanfordSceneConverter") & PluginManager::LoadState::Loaded)) + CORRADE_SKIP("StanfordSceneConverter plugin can't be loaded."); + + Containers::String filename = Utility::Path::join(ANYSCENECONVERTER_TEST_OUTPUT_DIR, "file.ply"); + + const Vector3 positions[] { + {-0.5f, -0.5f, 0.0f}, + { 0.5f, -0.5f, 0.0f}, + { 0.0f, 0.5f, 0.0f} + }; + const Trade::MeshData mesh{MeshPrimitive::Triangles, {}, positions, { + Trade::MeshAttributeData{Trade::MeshAttribute::Position, Containers::arrayView(positions)} + }}; + + Containers::Pointer converter = manager.instantiate("AnySceneConverter"); + converter->setFlags(SceneConverterFlag::Verbose); + + std::ostringstream out; + { + Debug redirectOutput{&out}; + CORRADE_VERIFY(converter->beginFile(filename)); + } + CORRADE_VERIFY(converter->add(mesh)); + CORRADE_VERIFY(converter->endFile()); + CORRADE_VERIFY(Utility::Path::exists(filename)); + CORRADE_COMPARE(out.str(), + "Trade::AnySceneConverter::beginFile(): using StanfordSceneConverter\n"); + + /* We tested AnySceneConverter's verbose output, but can't actually test + the flag propagation in any way yet */ + CORRADE_SKIP("No plugin with verbose output available to test flag propagation."); +} + +void AnySceneConverterTest::propagateConfigurationConvert() { PluginManager::Manager manager{MAGNUM_PLUGINS_SCENECONVERTER_INSTALL_DIR}; #ifdef ANYSCENECONVERTER_PLUGIN_FILENAME CORRADE_VERIFY(manager.load(ANYSCENECONVERTER_PLUGIN_FILENAME) & PluginManager::LoadState::Loaded); @@ -223,7 +354,42 @@ void AnySceneConverterTest::propagateConfiguration() { CORRADE_COMPARE_AS(filename, Utility::Path::join(ANYSCENECONVERTER_TEST_DIR, "objectid.ply"), TestSuite::Compare::File); } -void AnySceneConverterTest::propagateConfigurationUnknown() { +void AnySceneConverterTest::propagateConfigurationBeginEnd() { + PluginManager::Manager manager{MAGNUM_PLUGINS_SCENECONVERTER_INSTALL_DIR}; + #ifdef ANYSCENECONVERTER_PLUGIN_FILENAME + CORRADE_VERIFY(manager.load(ANYSCENECONVERTER_PLUGIN_FILENAME) & PluginManager::LoadState::Loaded); + #endif + + /* Catch also ABI and interface mismatch errors */ + if(!(manager.load("StanfordSceneConverter") & PluginManager::LoadState::Loaded)) + CORRADE_SKIP("StanfordSceneConverter plugin can't be loaded."); + + Containers::String filename = Utility::Path::join(ANYSCENECONVERTER_TEST_OUTPUT_DIR, "file.ply"); + + const struct Data { + Vector3 position; + UnsignedInt objectId; + } data[] { + {{-0.5f, -0.5f, 0.0f}, 4678}, + {{ 0.5f, -0.5f, 0.0f}, 3232}, + {{ 0.0f, 0.5f, 0.0f}, 1536} + }; + const Trade::MeshData mesh{MeshPrimitive::Triangles, {}, data, { + Trade::MeshAttributeData{Trade::MeshAttribute::Position, Containers::stridedArrayView(data).slice(&Data::position)}, + Trade::MeshAttributeData{Trade::MeshAttribute::ObjectId, Containers::stridedArrayView(data).slice(&Data::objectId)}, + }}; + + Containers::Pointer converter = manager.instantiate("AnySceneConverter"); + converter->configuration().setValue("objectIdAttribute", "OID"); + CORRADE_VERIFY(converter->beginFile(filename)); + CORRADE_VERIFY(converter->add(mesh)); + CORRADE_VERIFY(converter->endFile()); + /* Compare to an expected output to ensure the custom attribute name was + used */ + CORRADE_COMPARE_AS(filename, Utility::Path::join(ANYSCENECONVERTER_TEST_DIR, "objectid.ply"), TestSuite::Compare::File); +} + +void AnySceneConverterTest::propagateConfigurationUnknownConvert() { PluginManager::Manager manager{MAGNUM_PLUGINS_SCENECONVERTER_INSTALL_DIR}; #ifdef ANYSCENECONVERTER_PLUGIN_FILENAME CORRADE_VERIFY(manager.load(ANYSCENECONVERTER_PLUGIN_FILENAME) & PluginManager::LoadState::Loaded); @@ -251,6 +417,38 @@ void AnySceneConverterTest::propagateConfigurationUnknown() { CORRADE_COMPARE(out.str(), "Trade::AnySceneConverter::convertToFile(): option noSuchOption not recognized by StanfordSceneConverter\n"); } +void AnySceneConverterTest::propagateConfigurationUnknownBeginEnd() { + PluginManager::Manager manager{MAGNUM_PLUGINS_SCENECONVERTER_INSTALL_DIR}; + #ifdef ANYSCENECONVERTER_PLUGIN_FILENAME + CORRADE_VERIFY(manager.load(ANYSCENECONVERTER_PLUGIN_FILENAME) & PluginManager::LoadState::Loaded); + #endif + + /* Catch also ABI and interface mismatch errors */ + if(!(manager.load("StanfordSceneConverter") & PluginManager::LoadState::Loaded)) + CORRADE_SKIP("StanfordSceneConverter plugin can't be loaded."); + + const Vector3 positions[] { + {-0.5f, -0.5f, 0.0f}, + { 0.5f, -0.5f, 0.0f}, + { 0.0f, 0.5f, 0.0f} + }; + const Trade::MeshData mesh{MeshPrimitive::Triangles, {}, positions, { + Trade::MeshAttributeData{Trade::MeshAttribute::Position, Containers::arrayView(positions)} + }}; + + Containers::Pointer converter = manager.instantiate("AnySceneConverter"); + converter->configuration().setValue("noSuchOption", "isHere"); + + std::ostringstream out; + { + Warning redirectWarning{&out}; + CORRADE_VERIFY(converter->beginFile(Utility::Path::join(ANYSCENECONVERTER_TEST_OUTPUT_DIR, "file.ply"))); + } + CORRADE_VERIFY(converter->add(mesh)); + CORRADE_VERIFY(converter->endFile()); + CORRADE_COMPARE(out.str(), "Trade::AnySceneConverter::beginFile(): option noSuchOption not recognized by StanfordSceneConverter\n"); +} + }}}} CORRADE_TEST_MAIN(Magnum::Trade::Test::AnySceneConverterTest)