From 0a2148b93fbfb39a425140c14a67e306710c1f98 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Vladim=C3=ADr=20Vondru=C5=A1?= Date: Mon, 19 Sep 2022 01:35:19 +0200 Subject: [PATCH] Trade: ability to feed the whole importer into a scene converter. Makes a lot of use cases significantly simpler -- apart from the trivial "convert an OBJ to a glTF" scenario, many processing steps are about passing most data through but only doing a pass on meshes, or images, or materials. And this simplifies that use case quite a lot. This is only "driver" code, with no new interfaces for the plugins. For them it still looks like all data, their names and related metadata were added one by one. Also, a suprisingly large amount of code for this feature. --- src/Magnum/Trade/AbstractSceneConverter.cpp | 494 ++++ src/Magnum/Trade/AbstractSceneConverter.h | 277 +++ .../Trade/Test/AbstractSceneConverterTest.cpp | 2049 ++++++++++++++++- 3 files changed, 2776 insertions(+), 44 deletions(-) diff --git a/src/Magnum/Trade/AbstractSceneConverter.cpp b/src/Magnum/Trade/AbstractSceneConverter.cpp index a9bae93eb..9660de2e5 100644 --- a/src/Magnum/Trade/AbstractSceneConverter.cpp +++ b/src/Magnum/Trade/AbstractSceneConverter.cpp @@ -39,9 +39,15 @@ #include "Magnum/PixelFormat.h" #include "Magnum/Trade/AbstractImporter.h" #include "Magnum/Trade/ArrayAllocator.h" +#include "Magnum/Trade/AnimationData.h" +#include "Magnum/Trade/CameraData.h" #include "Magnum/Trade/ImageData.h" +#include "Magnum/Trade/LightData.h" #include "Magnum/Trade/MeshData.h" +#include "Magnum/Trade/MaterialData.h" #include "Magnum/Trade/SceneData.h" +#include "Magnum/Trade/SkinData.h" +#include "Magnum/Trade/TextureData.h" #ifdef MAGNUM_BUILD_DEPRECATED /* needed by deprecated convertToFile() that takes a std::string */ @@ -62,6 +68,79 @@ namespace Magnum { namespace Trade { using namespace Containers::Literals; +SceneContents sceneContentsFor(const AbstractImporter& importer) { + CORRADE_ASSERT(importer.isOpened(), + "Trade::sceneContentsFor(): the importer is not opened", {}); + + SceneContents contents = SceneContent::Names; + if(importer.sceneCount()) + contents |= SceneContent::Scenes; + if(importer.animationCount()) + contents |= SceneContent::Animations; + if(importer.lightCount()) + contents |= SceneContent::Lights; + if(importer.cameraCount()) + contents |= SceneContent::Cameras; + if(importer.skin2DCount()) + contents |= SceneContent::Skins2D; + if(importer.skin3DCount()) + contents |= SceneContent::Skins3D; + if(importer.meshCount()) + contents |= SceneContent::Meshes; + if(importer.materialCount()) + contents |= SceneContent::Materials; + if(importer.textureCount()) + contents |= SceneContent::Textures; + if(importer.image1DCount()) + contents |= SceneContent::Images1D; + if(importer.image2DCount()) + contents |= SceneContent::Images2D; + if(importer.image3DCount()) + contents |= SceneContent::Images3D; + return contents; +} + +SceneContents sceneContentsFor(const AbstractSceneConverter& converter) { + const SceneConverterFeatures features = converter.features(); + SceneContents contents = SceneContent::Names; + if(features & SceneConverterFeature::AddScenes) + contents |= SceneContent::Scenes; + if(features & SceneConverterFeature::AddAnimations) + contents |= SceneContent::Animations; + if(features & SceneConverterFeature::AddLights) + contents |= SceneContent::Lights; + if(features & SceneConverterFeature::AddCameras) + contents |= SceneContent::Cameras; + if(features & SceneConverterFeature::AddSkins2D) + contents |= SceneContent::Skins2D; + if(features & SceneConverterFeature::AddSkins3D) + contents |= SceneContent::Skins3D; + if(features & (SceneConverterFeature::AddMeshes| + SceneConverterFeature::ConvertMesh| + SceneConverterFeature::ConvertMeshToFile| + SceneConverterFeature::ConvertMeshToData)) + contents |= SceneContent::Meshes; + if(features & SceneConverterFeature::AddMaterials) + contents |= SceneContent::Materials; + if(features & SceneConverterFeature::AddTextures) + contents |= SceneContent::Textures; + if(features & (SceneConverterFeature::AddImages1D| + SceneConverterFeature::AddCompressedImages1D)) + contents |= SceneContent::Images1D; + if(features & (SceneConverterFeature::AddImages2D| + SceneConverterFeature::AddCompressedImages2D)) + contents |= SceneContent::Images2D; + if(features & (SceneConverterFeature::AddImages3D| + SceneConverterFeature::AddCompressedImages3D)) + contents |= SceneContent::Images3D; + if(features & SceneConverterFeature::MeshLevels) + contents |= SceneContent::MeshLevels; + if(features & SceneConverterFeature::ImageLevels) + contents |= SceneContent::ImageLevels; + + return contents; +} + /* Gets allocated in begin*() and deallocated in end*() or abort(). The direct conversion functions such as convert(const MeshData&) don't directly need this state, but can indirectly delegate to it, such as when @@ -1186,6 +1265,371 @@ Containers::Optional AbstractSceneConverter::add(const Containers:: return add(imageLevels, {}); } +bool AbstractSceneConverter::addImporterContentsInternal(AbstractImporter& importer, const SceneContents contents, const bool noLevelsIfUnsupported) { + CORRADE_ASSERT(isConverting(), + "Trade::AbstractSceneConverter::addImporterContents(): no conversion in progress", {}); + CORRADE_ASSERT(importer.isOpened(), + "Trade::AbstractSceneConverter::addImporterContents(): the importer is not opened", {}); + const SceneContents contentsPresentExceptLevels = contents & sceneContentsFor(importer); + const SceneContents contentsSupported = sceneContentsFor(*this); + CORRADE_ASSERT(!(contentsPresentExceptLevels & ~contentsSupported), + "Trade::AbstractSceneConverter::addImporterContents(): unsupported contents" << Debug::packed << (contentsPresentExceptLevels & ~contentsSupported), {}); + + /* These are in dependency order -- i.e., images between textures that + reference them or scenes before animations that reference them. The + actual bound checks (if any) are left on concrete implementations. */ + + if(contents & SceneContent::Meshes) { + Containers::Array levels; + for(UnsignedInt i = 0, iMax = importer.meshCount(); i != iMax; ++i) { + UnsignedInt levelCount = contents & SceneContent::MeshLevels ? importer.meshLevelCount(i) : 1; + if(levelCount != 1 && !(contentsSupported & SceneContent::MeshLevels)) { + if(noLevelsIfUnsupported) { + Warning{} << "Trade::AbstractSceneConverter::addSupportedImporterContents(): ignoring extra" << levelCount - 1 << "levels of mesh" << i << "not supported by the converter"; + levelCount = 1; + /* Not an assert because having to check this in advance could + be prohibitively expensive */ + } else { + Error{} << "Trade::AbstractSceneConverter::addImporterContents(): mesh" << i << "contains" << levelCount << "levels but the converter doesn't support" << SceneConverterFeature::MeshLevels; + return false; + } + } + + arrayReserve(levels, levelCount); + arrayResize(levels, NoInit, 0); /** @todo arrayClear() */ + for(UnsignedInt j = 0; j != levelCount; ++j) { + Containers::Optional mesh = importer.mesh(i, j); + if(!mesh) return false; + + /* Propagate custom attribute names, skip ones that are empty. + Compared to data names this is done always to avoid + information loss. */ + for(UnsignedInt j = 0; j != mesh->attributeCount(); ++j) { + /** @todo have some kind of a map to not have to query the + same custom attribute again for each mesh */ + const Trade::MeshAttribute name = mesh->attributeName(j); + if(!isMeshAttributeCustom(name)) continue; + if(const Containers::String nameString = importer.meshAttributeName(name)) { + setMeshAttributeName(name, nameString); + } + } + + arrayAppend(levels, *std::move(mesh)); + } + + const Containers::String name = contents & SceneContent::Names ? importer.meshName(i) : Containers::String{}; + if(levelCount != 1) { + if(!add(levels, name)) + return false; + } else { + if(!add(levels[0], name)) + return false; + } + } + } + + if(contents & SceneContent::Images1D) { + Containers::Array levels; + for(UnsignedInt i = 0, iMax = importer.image1DCount(); i != iMax; ++i) { + UnsignedInt levelCount = contents & SceneContent::ImageLevels ? importer.image1DLevelCount(i) : 1; + if(levelCount != 1 && !(contentsSupported & SceneContent::ImageLevels)) { + if(noLevelsIfUnsupported) { + Warning{} << "Trade::AbstractSceneConverter::addSupportedImporterContents(): ignoring extra" << levelCount - 1 << "levels of 1D image" << i << "not supported by the converter"; + levelCount = 1; + /* Not an assert because having to check this in advance could + be prohibitively expensive (decoding an arbitrary amount of + images twice...) */ + } else { + Error{} << "Trade::AbstractSceneConverter::addImporterContents(): 1D image" << i << "contains" << levelCount << "levels but the converter doesn't support" << SceneConverterFeature::ImageLevels; + return false; + } + } + + arrayReserve(levels, levelCount); + arrayResize(levels, NoInit, 0); /** @todo arrayClear() */ + for(UnsignedInt j = 0; j != levelCount; ++j) { + Containers::Optional image = importer.image1D(i, j); + if(!image) return false; + + if(image->isCompressed() && !(features() & SceneConverterFeature::AddCompressedImages1D)) { + Error{} << "Trade::AbstractSceneConverter::addImporterContents(): 1D image" << i << "level" << j << "is compressed but the converter doesn't support" << SceneConverterFeature::AddCompressedImages1D; + return false; + } + + if(!image->isCompressed() && !(features() & SceneConverterFeature::AddImages1D)) { + Error{} << "Trade::AbstractSceneConverter::addImporterContents(): 1D image" << i << "level" << j << "is uncompressed but the converter doesn't support" << SceneConverterFeature::AddImages1D; + return false; + } + + arrayAppend(levels, *std::move(image)); + } + + const Containers::String name = contents & SceneContent::Names ? importer.image1DName(i) : Containers::String{}; + if(levelCount != 1) { + if(!add(levels, name)) + return false; + } else { + if(!add(levels[0], name)) + return false; + } + } + } + + if(contents & SceneContent::Images2D) { + Containers::Array levels; + for(UnsignedInt i = 0, iMax = importer.image2DCount(); i != iMax; ++i) { + UnsignedInt levelCount = contents & SceneContent::ImageLevels ? importer.image2DLevelCount(i) : 1; + if(levelCount != 1 && !(contentsSupported & SceneContent::ImageLevels)) { + if(noLevelsIfUnsupported) { + Warning{} << "Trade::AbstractSceneConverter::addSupportedImporterContents(): ignoring extra" << levelCount - 1 << "levels of 2D image" << i << "not supported by the converter"; + levelCount = 1; + /* Not an assert because having to check this in advance could + be prohibitively expensive (decoding an arbitrary amount of + images twice...) */ + } else { + Error{} << "Trade::AbstractSceneConverter::addImporterContents(): 2D image" << i << "contains" << levelCount << "levels but the converter doesn't support" << SceneConverterFeature::ImageLevels; + return false; + } + } + + arrayReserve(levels, levelCount); + arrayResize(levels, NoInit, 0); /** @todo arrayClear() */ + for(UnsignedInt j = 0; j != levelCount; ++j) { + Containers::Optional image = importer.image2D(i, j); + if(!image) return false; + + if(image->isCompressed() && !(features() & SceneConverterFeature::AddCompressedImages2D)) { + Error{} << "Trade::AbstractSceneConverter::addImporterContents(): 2D image" << i << "level" << j << "is compressed but the converter doesn't support" << SceneConverterFeature::AddCompressedImages2D; + return false; + } + + if(!image->isCompressed() && !(features() & SceneConverterFeature::AddImages2D)) { + Error{} << "Trade::AbstractSceneConverter::addImporterContents(): 2D image" << i << "level" << j << "is uncompressed but the converter doesn't support" << SceneConverterFeature::AddImages2D; + return false; + } + + arrayAppend(levels, *std::move(image)); + } + + const Containers::String name = contents & SceneContent::Names ? importer.image2DName(i) : Containers::String{}; + if(levelCount != 1) { + if(!add(levels, name)) + return false; + } else { + if(!add(levels[0], name)) + return false; + } + } + } + + if(contents & SceneContent::Images3D) { + Containers::Array levels; + for(UnsignedInt i = 0, iMax = importer.image3DCount(); i != iMax; ++i) { + UnsignedInt levelCount = contents & SceneContent::ImageLevels ? importer.image3DLevelCount(i) : 1; + if(levelCount != 1 && !(contentsSupported & SceneContent::ImageLevels)) { + if(noLevelsIfUnsupported) { + Warning{} << "Trade::AbstractSceneConverter::addSupportedImporterContents(): ignoring extra" << levelCount - 1 << "levels of 3D image" << i << "not supported by the converter"; + levelCount = 1; + /* Not an assert because having to check this in advance could + be prohibitively expensive (decoding an arbitrary amount of + images twice...) */ + } else { + Error{} << "Trade::AbstractSceneConverter::addImporterContents(): 3D image" << i << "contains" << levelCount << "levels but the converter doesn't support" << SceneConverterFeature::ImageLevels; + return false; + } + } + + arrayReserve(levels, levelCount); + arrayResize(levels, NoInit, 0); /** @todo arrayClear() */ + for(UnsignedInt j = 0; j != levelCount; ++j) { + Containers::Optional image = importer.image3D(i, j); + if(!image) return false; + + if(image->isCompressed() && !(features() & SceneConverterFeature::AddCompressedImages3D)) { + Error{} << "Trade::AbstractSceneConverter::addImporterContents(): 3D image" << i << "level" << j << "is compressed but the converter doesn't support" << SceneConverterFeature::AddCompressedImages3D; + return false; + } + + if(!image->isCompressed() && !(features() & SceneConverterFeature::AddImages3D)) { + Error{} << "Trade::AbstractSceneConverter::addImporterContents(): 3D image" << i << "level" << j << "is uncompressed but the converter doesn't support" << SceneConverterFeature::AddImages3D; + return false; + } + + arrayAppend(levels, *std::move(image)); + } + + const Containers::String name = contents & SceneContent::Names ? importer.image3DName(i) : Containers::String{}; + if(levelCount != 1) { + if(!add(levels, name)) + return false; + } else { + if(!add(levels[0], name)) + return false; + } + } + } + + if(contents & SceneContent::Textures) { + for(UnsignedInt i = 0, iMax = importer.textureCount(); i != iMax; ++i) { + const Containers::Optional texture = importer.texture(i); + if(!texture || !add(*texture, contents & SceneContent::Names ? importer.textureName(i) : Containers::String{})) + return false; + } + } + + if(contents & SceneContent::Materials) { + for(UnsignedInt i = 0, iMax = importer.materialCount(); i != iMax; ++i) { + const Containers::Optional material = importer.material(i); + if(!material || !add(*material, contents & SceneContent::Names ? importer.materialName(i) : Containers::String{})) + return false; + } + } + + if(contents & SceneContent::Lights) { + for(UnsignedInt i = 0, iMax = importer.lightCount(); i != iMax; ++i) { + const Containers::Optional light = importer.light(i); + if(!light || !add(*light, contents & SceneContent::Names ? importer.lightName(i) : Containers::String{})) + return false; + } + } + + if(contents & SceneContent::Cameras) { + for(UnsignedInt i = 0, iMax = importer.cameraCount(); i != iMax; ++i) { + const Containers::Optional camera = importer.camera(i); + if(!camera || !add(*camera, contents & SceneContent::Names ? importer.cameraName(i) : Containers::String{})) + return false; + } + } + + if(contents & SceneContent::Scenes) { + /* Propagate object names, skip ones that are empty */ + if(contents & SceneContent::Names) for(UnsignedLong i = 0, iMax = importer.objectCount(); i != iMax; ++i) { + if(const Containers::String name = importer.objectName(i)) + setObjectName(i, name); + } + + for(UnsignedInt i = 0, iMax = importer.sceneCount(); i != iMax; ++i) { + Containers::Optional scene = importer.scene(i); + if(!scene) return false; + + /* Propagate custom field names, skip ones that are empty. Compared + to data names this is done always to avoid information loss. */ + for(UnsignedInt j = 0; j != scene->fieldCount(); ++j) { + /** @todo have some kind of a map to not have to query the same + field again for each scene */ + const Trade::SceneField name = scene->fieldName(j); + if(!isSceneFieldCustom(name)) continue; + if(const Containers::String nameString = importer.sceneFieldName(name)) { + setSceneFieldName(name, nameString); + } + } + + if(!scene || !add(*scene, contents & SceneContent::Names ? importer.sceneName(i) : Containers::String{})) + return false; + } + + const Int defaultScene = importer.defaultScene(); + if(defaultScene != -1) + setDefaultScene(defaultScene); + } + + if(contents & SceneContent::Skins2D) { + for(UnsignedInt i = 0, iMax = importer.skin2DCount(); i != iMax; ++i) { + const Containers::Optional skin = importer.skin2D(i); + if(!skin || !add(*skin, contents & SceneContent::Names ? importer.skin2DName(i) : Containers::String{})) + return false; + } + } + + if(contents & SceneContent::Skins3D) { + for(UnsignedInt i = 0, iMax = importer.skin3DCount(); i != iMax; ++i) { + const Containers::Optional skin = importer.skin3D(i); + if(!skin || !add(*skin, contents & SceneContent::Names ? importer.skin3DName(i) : Containers::String{})) + return false; + } + } + + if(contents & SceneContent::Animations) { + for(UnsignedInt i = 0, iMax = importer.animationCount(); i != iMax; ++i) { + const Containers::Optional animation = importer.animation(i); + if(!animation || !add(*animation, contents & SceneContent::Names ? importer.animationName(i) : Containers::String{})) + return false; + } + } + + return true; +} + + +bool AbstractSceneConverter::addImporterContents(AbstractImporter& importer, const SceneContents contents) { + return addImporterContentsInternal(importer, contents, false); +} + +bool AbstractSceneConverter::addSupportedImporterContents(AbstractImporter& importer, const SceneContents contents) { + /* To avoid accidental differences in handling SceneConverterFeatures in + sceneContentsFor(const AbstractSceneConverter&) and here, this branches + on SceneContents instead of SceneConverterFeatures */ + const SceneContents wantedButUnsupported = contents & ~sceneContentsFor(*this); + + /* To avoid needlessly querying fooCount() several times (which might be + expensive in certain unfortunate cases), this basically unwraps the + contents of sceneContentsFor(const AbstractImporter&) and adds warnings + there */ + SceneContents used = contents; + UnsignedInt count; + if((wantedButUnsupported & SceneContent::Scenes) && (count = importer.sceneCount())) { + Warning{} << "Trade::AbstractSceneConverter::addSupportedImporterContents(): ignoring" << count << "scenes not supported by the converter"; + used &= ~SceneContent::Scenes; + } + if((wantedButUnsupported & SceneContent::Animations) && (count = importer.animationCount())) { + Warning{} << "Trade::AbstractSceneConverter::addSupportedImporterContents(): ignoring" << count << "animations not supported by the converter"; + used &= ~SceneContent::Animations; + } + if((wantedButUnsupported & SceneContent::Lights) && (count = importer.lightCount())) { + Warning{} << "Trade::AbstractSceneConverter::addSupportedImporterContents(): ignoring" << count << "lights not supported by the converter"; + used &= ~SceneContent::Lights; + } + if((wantedButUnsupported & SceneContent::Cameras) && (count = importer.cameraCount())) { + Warning{} << "Trade::AbstractSceneConverter::addSupportedImporterContents(): ignoring" << count << "cameras not supported by the converter"; + used &= ~SceneContent::Cameras; + } + if((wantedButUnsupported & SceneContent::Skins2D) && (count = importer.skin2DCount())) { + Warning{} << "Trade::AbstractSceneConverter::addSupportedImporterContents(): ignoring" << count << "2D skins not supported by the converter"; + used &= ~SceneContent::Skins2D; + } + if((wantedButUnsupported & SceneContent::Skins3D) && (count = importer.skin3DCount())) { + Warning{} << "Trade::AbstractSceneConverter::addSupportedImporterContents(): ignoring" << count << "3D skins not supported by the converter"; + used &= ~SceneContent::Skins3D; + } + if((wantedButUnsupported & SceneContent::Meshes) && (count = importer.meshCount())) { + Warning{} << "Trade::AbstractSceneConverter::addSupportedImporterContents(): ignoring" << count << "meshes not supported by the converter"; + used &= ~SceneContent::Meshes; + } + if((wantedButUnsupported & SceneContent::Materials) && (count = importer.materialCount())) { + Warning{} << "Trade::AbstractSceneConverter::addSupportedImporterContents(): ignoring" << count << "materials not supported by the converter"; + used &= ~SceneContent::Materials; + } + if((wantedButUnsupported & SceneContent::Textures) && (count = importer.textureCount())) { + Warning{} << "Trade::AbstractSceneConverter::addSupportedImporterContents(): ignoring" << count << "textures not supported by the converter"; + used &= ~SceneContent::Textures; + } + if((wantedButUnsupported & SceneContent::Images1D) && (count = importer.image1DCount())) { + Warning{} << "Trade::AbstractSceneConverter::addSupportedImporterContents(): ignoring" << count << "1D images not supported by the converter"; + used &= ~SceneContent::Images1D; + } + if((wantedButUnsupported & SceneContent::Images2D) && (count = importer.image2DCount())) { + Warning{} << "Trade::AbstractSceneConverter::addSupportedImporterContents(): ignoring" << count << "2D images not supported by the converter"; + used &= ~SceneContent::Images2D; + } + if((wantedButUnsupported & SceneContent::Images3D) && (count = importer.image3DCount())) { + Warning{} << "Trade::AbstractSceneConverter::addSupportedImporterContents(): ignoring" << count << "3D images not supported by the converter"; + used &= ~SceneContent::Images3D; + } + + /* MeshLevels and ImageLevels handled inside */ + return addImporterContentsInternal(importer, used, true); +} + Debug& operator<<(Debug& debug, const SceneConverterFeature value) { const bool packed = debug.immediateFlags() >= Debug::Flag::Packed; @@ -1275,4 +1719,54 @@ Debug& operator<<(Debug& debug, const SceneConverterFlags value) { SceneConverterFlag::Verbose}); } +Debug& operator<<(Debug& debug, const SceneContent value) { + const bool packed = debug.immediateFlags() >= Debug::Flag::Packed; + + if(!packed) + debug << "Trade::SceneContent" << Debug::nospace; + + switch(value) { + /* LCOV_EXCL_START */ + #define _c(v) case SceneContent::v: return debug << (packed ? "" : "::") << Debug::nospace << #v; + _c(Scenes) + _c(Animations) + _c(Lights) + _c(Cameras) + _c(Skins2D) + _c(Skins3D) + _c(Meshes) + _c(Materials) + _c(Textures) + _c(Images1D) + _c(Images2D) + _c(Images3D) + _c(MeshLevels) + _c(ImageLevels) + _c(Names) + #undef _c + /* LCOV_EXCL_STOP */ + } + + return debug << (packed ? "" : "(") << Debug::nospace << reinterpret_cast(UnsignedInt(value)) << Debug::nospace << (packed ? "" : ")"); +} + +Debug& operator<<(Debug& debug, const SceneContents value) { + return Containers::enumSetDebugOutput(debug, value, debug.immediateFlags() >= Debug::Flag::Packed ? "{}" : "Trade::SceneContents{}", { + SceneContent::Scenes, + SceneContent::Animations, + SceneContent::Lights, + SceneContent::Cameras, + SceneContent::Skins2D, + SceneContent::Skins3D, + SceneContent::Meshes, + SceneContent::Materials, + SceneContent::Textures, + SceneContent::Images1D, + SceneContent::Images2D, + SceneContent::Images3D, + SceneContent::MeshLevels, + SceneContent::ImageLevels, + SceneContent::Names}); +} + }} diff --git a/src/Magnum/Trade/AbstractSceneConverter.h b/src/Magnum/Trade/AbstractSceneConverter.h index 984cc1262..00e20434d 100644 --- a/src/Magnum/Trade/AbstractSceneConverter.h +++ b/src/Magnum/Trade/AbstractSceneConverter.h @@ -322,6 +322,213 @@ MAGNUM_TRADE_EXPORT Debug& operator<<(Debug& debug, SceneConverterFlag value); */ MAGNUM_TRADE_EXPORT Debug& operator<<(Debug& debug, SceneConverterFlags value); +/** +@brief Scene content +@m_since_latest + +Content to be taken from an @ref AbstractImporter and passed to an +@ref AbstractSceneConverter in @ref AbstractSceneConverter::addImporterContents() +and @relativeref{AbstractSceneConverter,addSupportedImporterContents()}. +@see @ref SceneContents, @ref sceneContentsFor(AbstractImporter&), + @ref sceneContentsFor(AbstractSceneConverter&) +*/ +enum class SceneContent: UnsignedInt { + /** @todo needs to be taken by addScene(), addMaterial() and addTexture() + as well */ + + /** + * Scenes. Passes @ref SceneData from @ref AbstractImporter::scene() to + * @ref AbstractSceneConverter::add(const SceneData&, Containers::StringView); @ref AbstractImporter::defaultScene() to + * @ref AbstractSceneConverter::setDefaultScene(); and for all custom + * fields, @ref AbstractImporter::sceneFieldName() to + * @ref AbstractSceneConverter::setSceneFieldName(). + */ + Scenes = 1 << 0, + + /** + * Animations. Passes @ref AnimationData from @ref AbstractImporter::animation() + * to @ref AbstractSceneConverter::add(const AnimationData&, Containers::StringView) + */ + Animations = 1 << 1, + + /** + * Lights. Passes @ref LightData from @ref AbstractImporter::light() to + * @ref AbstractSceneConverter::add(const LightData&, Containers::StringView). + */ + Lights = 1 << 2, + + /** + * Cameras. Passes @ref SceneData from @ref AbstractImporter::camera() to + * @ref AbstractSceneConverter::add(const CameraData&, Containers::StringView). + */ + Cameras = 1 << 3, + + /** + * 2D skins. Passes @ref SkinData2D from @ref AbstractImporter::skin2D() to + * @ref AbstractSceneConverter::add(const SkinData2D&, Containers::StringView). + */ + Skins2D = 1 << 4, + + /** + * 3D skins. Passes @ref SkinData3D from @ref AbstractImporter::skin2D() to + * @ref AbstractSceneConverter::add(const SkinData3D&, Containers::StringView). + */ + Skins3D = 1 << 5, + + /** + * Meshes. Passes @ref MeshData from @ref AbstractImporter::mesh(), + * to @ref AbstractSceneConverter::add(const MeshData&, Containers::StringView); and for all custom attributes, + * @ref AbstractImporter::meshAttributeName() to + * @ref AbstractSceneConverter::setMeshAttributeName(). + * @see @ref SceneContent::MeshLevels + */ + Meshes = 1 << 6, + + /** + * Materials. Passes @ref MaterialData from @ref AbstractImporter::material() + * to @ref AbstractSceneConverter::add(const MaterialData&, Containers::StringView). + */ + Materials = 1 << 7, + + /** + * Textures. Passes @ref TextureData from @ref AbstractImporter::texture() + * to @ref AbstractSceneConverter::add(const TextureData&, Containers::StringView). + */ + Textures = 1 << 8, + + /** + * 1D images. Passes @ref ImageData1D from @ref AbstractImporter::image1D() + * to @ref AbstractSceneConverter::add(const ImageData1D&, Containers::StringView). If the image is compressed and only + * @ref SceneConverterFeature::AddImages1D is supported or the image is + * uncompressed and only @ref SceneConverterFeature::AddCompressedImages1D + * is supported, results in an error. + * @see @ref ImageData::isCompressed(), @ref SceneContent::ImageLevels + */ + Images1D = 1 << 9, + + /** + * 2D images. Passes @ref ImageData2D from @ref AbstractImporter::image2D() + * to @ref AbstractSceneConverter::add(const ImageData2D&, Containers::StringView). If the image is compressed and only + * @ref SceneConverterFeature::AddImages2D is supported or the image is + * uncompressed and only @ref SceneConverterFeature::AddCompressedImages2D + * is supported, results in an error. + * @see @ref ImageData::isCompressed(), @ref SceneContent::ImageLevels + */ + Images2D = 1 << 10, + + /** + * 3D images. Passes @ref ImageData3D from @ref AbstractImporter::image3D() + * to @ref AbstractSceneConverter::add(const ImageData3D&, Containers::StringView). If the image is compressed and only + * @ref SceneConverterFeature::AddImages3D is supported or the image is + * uncompressed and only @ref SceneConverterFeature::AddCompressedImages3D + * is supported, results in an error. + * @see @ref ImageData::isCompressed(), @ref SceneContent::ImageLevels + */ + Images3D = 1 << 11, + + /** + * Multiple mesh levels. For every mesh gathers @ref MeshData from all + * @ref AbstractImporter::meshLevelCount() and passes them to + * @ref AbstractSceneConverter::add(Containers::Iterable, Containers::StringView) + * instead of passing just the first level to + * @ref AbstractSceneConverter::add(const MeshData&, Containers::StringView). + * @see @ref SceneContent::Meshes + */ + MeshLevels = 1 << 12, + + /** + * Multiple image levels. For every image gathers @ref ImageData1D / + * @ref ImageData2D / @ref ImageData3D from all + * @ref AbstractImporter::image1DLevelCount() / + * @relativeref{AbstractImporter,image2DLevelCount()} / + * @relativeref{AbstractImporter,image3DLevelCount()} and passes them to + * @ref AbstractSceneConverter::add(Containers::Iterable, Containers::StringView) / + * @ref AbstractSceneConverter::add(Containers::Iterable, Containers::StringView) "add(Containers::Iterable, Containers::StringView)" / + * @ref AbstractSceneConverter::add(Containers::Iterable, Containers::StringView) "add(Containers::Iterable, Containers::StringView)" + * instead of just passing the first level to + * + * @ref AbstractSceneConverter::add(const ImageData1D&, Containers::StringView) / + * @ref AbstractSceneConverter::add(const ImageData2D&, Containers::StringView) "add(const ImageData2D&, Containers::StringView)" / + * @ref AbstractSceneConverter::add(const ImageData2D&, Containers::StringView) "add(const ImageData2D&, Containers::StringView)". + * @see @ref SceneContent::Images1D, @relativeref{SceneContent,Images2D}, + * @relativeref{SceneContent,Images3D}, + */ + ImageLevels = 1 << 13, + + /** + * Data names. For every supported data queries also + * @ref AbstractImporter::sceneName(), + * @relativeref{AbstractImporter,objectName()}, + * @relativeref{AbstractImporter,animationName()}, + * @relativeref{AbstractImporter,lightName()}, + * @relativeref{AbstractImporter,cameraName()}, + * @relativeref{AbstractImporter,skin2DName()}, + * @relativeref{AbstractImporter,skin3DName()}, + * @relativeref{AbstractImporter,meshName()}, + * @relativeref{AbstractImporter,materialName()}, + * @relativeref{AbstractImporter,textureName()}, + * @relativeref{AbstractImporter,image1DName()}, + * @relativeref{AbstractImporter,image2DName()} or + * @relativeref{AbstractImporter,image3DName()} and passes them to the + * converter. @ref AbstractImporter::sceneFieldName() and + * @relativeref{AbstractImporter,meshAttributeName()} are however passed + * always to avoid information loss. + */ + Names = 1 << 14 +}; + +/** +@brief Scene contents +@m_since_latest + +Content to be taken from an @ref AbstractImporter and passed to an +@ref AbstractSceneConverter in @ref AbstractSceneConverter::addImporterContents() +and @relativeref{AbstractSceneConverter,addSupportedImporterContents()}. +@see @ref sceneContentsFor(AbstractImporter&), + @ref sceneContentsFor(AbstractSceneConverter&) +*/ +typedef Containers::EnumSet SceneContents; + +CORRADE_ENUMSET_OPERATORS(SceneContents) + +/** +@debugoperatorenum{SceneContent} +@m_since_latest +*/ +MAGNUM_TRADE_EXPORT Debug& operator<<(Debug& debug, SceneContent value); + +/** +@debugoperatorenum{SceneContents} +@m_since_latest +*/ +MAGNUM_TRADE_EXPORT Debug& operator<<(Debug& debug, SceneContents value); + +/** +@brief Scene contents for an importer +@m_since_latest + +Returns contents exposed by given importer, i.e. all for which the importer +returns a non-zero count. Expects that the importer is opened. +@ref SceneContent::Names is present always. As querying level count usually +involves parsing additional files and thus may be time- and memory-consuming +operation, @ref SceneContent::MeshLevels and +@relativeref{SceneContent,ImageLevels} is never present. +@see @ref sceneContentsFor(AbstractSceneConverter&), + @ref AbstractImporter::isOpened() +*/ +MAGNUM_TRADE_EXPORT SceneContents sceneContentsFor(const AbstractImporter& importer); + +/** +@brief Scene contents supported by a converter +@m_since_latest + +Returns contents supported by given converter, as exposed via +@ref AbstractSceneConverter::features(). @ref SceneContent::Names is present +always. +@see @ref sceneContentsFor(AbstractImporter&) +*/ +MAGNUM_TRADE_EXPORT SceneContents sceneContentsFor(const AbstractSceneConverter& converter); + #ifdef MAGNUM_BUILD_DEPRECATED namespace Implementation { /* Could be a concrete type as it's always only char, but that would mean @@ -1738,6 +1945,73 @@ class MAGNUM_TRADE_EXPORT AbstractSceneConverter: public PluginManager::Abstract Containers::Optional add(Containers::Iterable imageLevels); #endif + /** + * @brief Add importer contents + * @m_since_latest + * + * Adds all @p contents from the @p importer as described in particular + * @ref SceneContent value documentation. If any data causes a + * conversion error, the function immediately returns @cpp false @ce, + * leaving the conversion in an in-progress state, same as any other + * `add*()` function. + * + * Expects that a conversion is currently in progress and @p importer + * is opened. The union of @p contents and + * @ref sceneContentsFor(AbstractImporter&) for @p importer is expected + * to be a subset of @ref sceneContentsFor(AbstractSceneConverter&) for + * this converter --- i.e., there shouldn't be any data that the + * converter doesn't support. Any @p contents that are not in + * @ref sceneContentsFor(AbstractImporter&) for @p importer are + * ignored. If you want to add just contents supported by the coverter + * and ignore the rest with a warning, use + * @ref addSupportedImporterContents() instead. + * + * Additionally, if @p contents contains @ref SceneContent::MeshLevels + * / @relativeref{SceneContent,ImageLevels}, the importer has + * multi-level meshes / images, but @ref SceneConverterFeature::MeshLevels + * / @relativeref{SceneConverterFeature,ImageLevels} is not supported, + * the function prints a message to @relativeref{Magnum,Error} and + * returns @cpp false @ce. Similarly, if @p contents contains + * @ref SceneContent::Images1D / + * @relativeref{SceneContent,Images2D} / + * @relativeref{SceneContent,Images3D}, the importer has compressed + * images, but @ref SceneConverterFeature::AddCompressedImages1D / + * @relativeref{SceneConverterFeature,AddCompressedImages2D} / + * @relativeref{SceneConverterFeature,AddCompressedImages3D} isn't + * supported (or, conversely, the importer has uncompressed images but + * @ref SceneConverterFeature::AddImages1D / + * @relativeref{SceneConverterFeature,AddImages2D} / + * @relativeref{SceneConverterFeature,AddImages3D} isn't supported), + * the function prints a message to @relativeref{Magnum,Error} and + * returns @cpp false @ce. These cause a runtime error instead of an + * assertion because checking the prerequisites upfront could be + * prohibitively expensive due to having to load and parse image and + * other data more than once. + * @see @ref isConverting(), @ref AbstractImporter::isOpened() + */ + bool addImporterContents(AbstractImporter& importer, SceneContents contents = ~SceneContents{}); + + /** + * @brief Add supported importer contents + * @m_since_latest + * + * Compared to @ref addImporterContents(), data not supported by the + * converter (i.e., @p contents that are in + * @ref sceneContentsFor(AbstractImporter&) for @p importer but are not + * in @ref sceneContentsFor(AbstractSceneConverter&) for the + * converter) are ignored with a message printed to + * @relativeref{Magnum,Warning}. + * + * In case of @ref SceneContent::MeshLevels / + * @relativeref{SceneContent,ImageLevels}, if the converter doesn't + * support @ref SceneConverterFeature::MeshLevels / + * @relativeref{SceneConverterFeature,ImageLevels}, only the base level + * is added. The only case that still causes a failure is if the + * importer contains compressed images but converter supports only + * uncompressed and vice versa, same as with @ref addImporterContents(). + */ + bool addSupportedImporterContents(AbstractImporter& importer, SceneContents contents = ~SceneContents{}); + protected: /** * @brief Implementation for @ref convertToFile(const MeshData&, Containers::StringView) @@ -2108,6 +2382,9 @@ class MAGNUM_TRADE_EXPORT AbstractSceneConverter: public PluginManager::Abstract */ virtual bool doAdd(UnsignedInt id, Containers::Iterable imageLevels, Containers::StringView name); + /* Called from addImporterContents() and addSupportedImporterContents() */ + MAGNUM_TRADE_LOCAL bool addImporterContentsInternal(AbstractImporter& importer, SceneContents contents, bool noLevelsIfUnsupported); + SceneConverterFlags _flags; Containers::Pointer _state; }; diff --git a/src/Magnum/Trade/Test/AbstractSceneConverterTest.cpp b/src/Magnum/Trade/Test/AbstractSceneConverterTest.cpp index dc0519ea9..92e66753a 100644 --- a/src/Magnum/Trade/Test/AbstractSceneConverterTest.cpp +++ b/src/Magnum/Trade/Test/AbstractSceneConverterTest.cpp @@ -33,6 +33,8 @@ #include #include #include +#include +#include /** @todo remove once Debug is stream-free */ #include #include "Magnum/ImageView.h" @@ -60,6 +62,14 @@ namespace Magnum { namespace Trade { namespace Test { namespace { struct AbstractSceneConverterTest: TestSuite::Tester { explicit AbstractSceneConverterTest(); + void sceneContentsForImporterNone(); + void sceneContentsForImporterAll(); + void sceneContentsForImporterNotOpened(); + + void sceneContentsForConverterNone(); + void sceneContentsForConverterSingleMesh(); + void sceneContentsForConverterAll(); + void featuresNone(); void setFlags(); @@ -274,6 +284,21 @@ struct AbstractSceneConverterTest: TestSuite::Tester { void addImage2DThroughLevels(); void addImage3DThroughLevels(); + void addImporterContents(); + void addImporterContentsCustomSceneFields(); + void addImporterContentsCustomMeshAttributes(); + void addImporterContentsImportFail(); + void addImporterContentsConversionFail(); + void addImporterContentsNotConverting(); + void addImporterContentsNotOpened(); + void addImporterContentsNotSupported(); + void addImporterContentsNotSupportedLevels(); + void addImporterContentsNotSupportedUncompressedImage(); + void addImporterContentsNotSupportedCompressedImage(); + + void addSupportedImporterContents(); + void addSupportedImporterContentsLevels(); + void debugFeature(); void debugFeaturePacked(); void debugFeatures(); @@ -281,12 +306,217 @@ struct AbstractSceneConverterTest: TestSuite::Tester { void debugFeaturesSupersets(); void debugFlag(); void debugFlags(); + void debugContent(); + void debugContentPacked(); + void debugContents(); + void debugContentsPacked(); }; using namespace Containers::Literals; +const struct { + const char* name; + SceneContents contents; + const char* expected; +} AddImporterContentsData[]{ + {"scenes", SceneContent::Scenes, + "Adding scene 0 named with 0x5ce00000\n" + "Adding scene 1 named with 0x5ce00001\n" + "Setting default scene to 1\n"}, + {"scenes + names", SceneContent::Scenes|SceneContent::Names, + "Setting object 0 name to Object 0\n" + "Setting object 1 name to Object 1\n" + "Setting object 2 name to Object 2\n" + "Adding scene 0 named Scene 0 with 0x5ce00000\n" + "Adding scene 1 named Scene 1 with 0x5ce00001\n" + "Setting default scene to 1\n"}, + {"animations", SceneContent::Animations, + "Adding animation 0 named with 0x40100000\n" + "Adding animation 1 named with 0x40100001\n"}, + {"animations + names", SceneContent::Animations|SceneContent::Names, + "Adding animation 0 named Animation 0 with 0x40100000\n" + "Adding animation 1 named Animation 1 with 0x40100001\n"}, + {"lights", SceneContent::Lights, + "Adding light 0 named with 0x11600000\n" + "Adding light 1 named with 0x11600001\n"}, + {"lights + names", SceneContent::Lights|SceneContent::Names, + "Adding light 0 named Light 0 with 0x11600000\n" + "Adding light 1 named Light 1 with 0x11600001\n"}, + {"cameras", SceneContent::Cameras, + "Adding camera 0 named with 0xca0e0000\n" + "Adding camera 1 named with 0xca0e0001\n"}, + {"cameras + names", SceneContent::Cameras|SceneContent::Names, + "Adding camera 0 named Camera 0 with 0xca0e0000\n" + "Adding camera 1 named Camera 1 with 0xca0e0001\n"}, + {"2D skins", SceneContent::Skins2D, + "Adding 2D skin 0 named with 0x50102d00\n" + "Adding 2D skin 1 named with 0x50102d01\n"}, + {"2D skins + names", SceneContent::Skins2D|SceneContent::Names, + "Adding 2D skin 0 named 2D skin 0 with 0x50102d00\n" + "Adding 2D skin 1 named 2D skin 1 with 0x50102d01\n"}, + {"3D skins", SceneContent::Skins3D, + "Adding 3D skin 0 named with 0x50103d00\n" + "Adding 3D skin 1 named with 0x50103d01\n"}, + {"3D skins + names", SceneContent::Skins3D|SceneContent::Names, + "Adding 3D skin 0 named 3D skin 0 with 0x50103d00\n" + "Adding 3D skin 1 named 3D skin 1 with 0x50103d01\n"}, + {"meshes", SceneContent::Meshes, + "Adding mesh 0 named with 0xe500000\n" + "Adding mesh 1 named with 0xe500001\n"}, + {"meshes + names", SceneContent::Meshes|SceneContent::Names, + "Adding mesh 0 named Mesh 0 with 0xe500000\n" + "Adding mesh 1 named Mesh 1 with 0xe500001\n"}, + {"meshes + levels", SceneContent::Meshes|SceneContent::MeshLevels, + "Adding mesh 0 named with 0xe500000\n" + "Adding mesh 1 level 0 named with 0xe500001\n" + "Adding mesh 1 level 1 named with 0xe500011\n" + "Adding mesh 1 level 2 named with 0xe500021\n"}, + {"meshes + levels + names", SceneContent::Meshes|SceneContent::MeshLevels|SceneContent::Names, + "Adding mesh 0 named Mesh 0 with 0xe500000\n" + "Adding mesh 1 level 0 named Mesh 1 with 0xe500001\n" + "Adding mesh 1 level 1 named Mesh 1 with 0xe500011\n" + "Adding mesh 1 level 2 named Mesh 1 with 0xe500021\n"}, + {"materials", SceneContent::Materials, + "Adding material 0 named with 0xa7e0000\n" + "Adding material 1 named with 0xa7e0001\n"}, + {"materials + names", SceneContent::Materials|SceneContent::Names, + "Adding material 0 named Material 0 with 0xa7e0000\n" + "Adding material 1 named Material 1 with 0xa7e0001\n"}, + {"textures", SceneContent::Textures, + "Adding texture 0 named with 0x7e070000\n" + "Adding texture 1 named with 0x7e070001\n"}, + {"textures + names", SceneContent::Textures|SceneContent::Names, + "Adding texture 0 named Texture 0 with 0x7e070000\n" + "Adding texture 1 named Texture 1 with 0x7e070001\n"}, + {"1D images", SceneContent::Images1D, + "Adding 1D image 0 named with 0x10a91d00\n" + "Adding 1D image 1 named with 0x10a91d01\n"}, + {"1D images + names", SceneContent::Images1D|SceneContent::Names, + "Adding 1D image 0 named 1D image 0 with 0x10a91d00\n" + "Adding 1D image 1 named 1D image 1 with 0x10a91d01\n"}, + {"1D images + levels", SceneContent::Images1D|SceneContent::ImageLevels, + "Adding 1D image 0 named with 0x10a91d00\n" + "Adding 1D image 1 level 0 named with 0x10a91d01\n" + "Adding 1D image 1 level 1 named with 0x10a91d11\n" + "Adding 1D image 1 level 2 named with 0x10a91d21\n" + "Adding 1D image 1 level 3 named with 0x10a91d31\n"}, + {"1D images + levels + names", SceneContent::Images1D|SceneContent::ImageLevels|SceneContent::Names, + "Adding 1D image 0 named 1D image 0 with 0x10a91d00\n" + "Adding 1D image 1 level 0 named 1D image 1 with 0x10a91d01\n" + "Adding 1D image 1 level 1 named 1D image 1 with 0x10a91d11\n" + "Adding 1D image 1 level 2 named 1D image 1 with 0x10a91d21\n" + "Adding 1D image 1 level 3 named 1D image 1 with 0x10a91d31\n"}, + {"2D images", SceneContent::Images2D, + "Adding 2D image 0 named with 0x10a92d00\n" + "Adding 2D image 1 named with 0x10a92d01\n"}, + {"2D images + names", SceneContent::Images2D|SceneContent::Names, + "Adding 2D image 0 named 2D image 0 with 0x10a92d00\n" + "Adding 2D image 1 named 2D image 1 with 0x10a92d01\n"}, + {"2D images + levels", SceneContent::Images2D|SceneContent::ImageLevels, + "Adding 2D image 0 level 0 named with 0x10a92d00\n" + "Adding 2D image 0 level 1 named with 0x10a92d10\n" + "Adding 2D image 0 level 2 named with 0x10a92d20\n" + "Adding 2D image 1 named with 0x10a92d01\n"}, + {"2D images + names", SceneContent::Images2D|SceneContent::ImageLevels|SceneContent::Names, + "Adding 2D image 0 level 0 named 2D image 0 with 0x10a92d00\n" + "Adding 2D image 0 level 1 named 2D image 0 with 0x10a92d10\n" + "Adding 2D image 0 level 2 named 2D image 0 with 0x10a92d20\n" + "Adding 2D image 1 named 2D image 1 with 0x10a92d01\n"}, + {"3D images", SceneContent::Images3D, + "Adding 3D image 0 named with 0x10a93d00\n" + "Adding 3D image 1 named with 0x10a93d01\n"}, + {"3D images + names", SceneContent::Images3D|SceneContent::Names, + "Adding 3D image 0 named 3D image 0 with 0x10a93d00\n" + "Adding 3D image 1 named 3D image 1 with 0x10a93d01\n"}, + {"3D images + levels", SceneContent::Images3D|SceneContent::ImageLevels, + "Adding 3D image 0 named with 0x10a93d00\n" + "Adding 3D image 1 level 0 named with 0x10a93d01\n" + "Adding 3D image 1 level 1 named with 0x10a93d11\n"}, + {"3D images + names", SceneContent::Images3D|SceneContent::ImageLevels|SceneContent::Names, + "Adding 3D image 0 named 3D image 0 with 0x10a93d00\n" + "Adding 3D image 1 level 0 named 3D image 1 with 0x10a93d01\n" + "Adding 3D image 1 level 1 named 3D image 1 with 0x10a93d11\n"}, + {"names only", SceneContent::Names, + "" /* Nothing */}, +}; + +const struct { + const char* name; + SceneContents contents; +} AddImporterContentsFailData[]{ + {"scene", SceneContent::Scenes}, + {"animation", SceneContent::Animations}, + {"light", SceneContent::Lights}, + {"camera", SceneContent::Cameras}, + {"2D skin", SceneContent::Skins2D}, + {"3D skin", SceneContent::Skins3D}, + {"mesh", SceneContent::Meshes}, + {"mesh levels", SceneContent::Meshes|SceneContent::MeshLevels}, + {"material", SceneContent::Materials}, + {"texture", SceneContent::Textures}, + {"1D image", SceneContent::Images1D}, + {"1D image levels", SceneContent::Images1D|SceneContent::ImageLevels}, + {"2D image", SceneContent::Images2D}, + {"2D image levels", SceneContent::Images2D|SceneContent::ImageLevels}, + {"3D image", SceneContent::Images3D}, + {"3D image levels", SceneContent::Images3D|SceneContent::ImageLevels}, +}; + +const struct { + const char* name; + const char* except; + SceneConverterFeatures exceptFeatures; + SceneContents exceptContents; + SceneContents wantExceptContents; +} AddSupportedImporterContentsData[]{ + {"except scenes", "2 scenes", + SceneConverterFeature::AddScenes, SceneContent::Scenes, {}}, + {"except animations", "3 animations", + SceneConverterFeature::AddAnimations, SceneContent::Animations, {}}, + {"except lights", "4 lights", + SceneConverterFeature::AddLights, SceneContent::Lights, {}}, + {"except cameras", "5 cameras", + SceneConverterFeature::AddCameras, SceneContent::Cameras, {}}, + {"except 2D skins", "6 2D skins", + SceneConverterFeature::AddSkins2D, SceneContent::Skins2D, {}}, + {"except 3D skins", "7 3D skins", + SceneConverterFeature::AddSkins3D, SceneContent::Skins3D, {}}, + {"except meshes", "8 meshes", + SceneConverterFeature::AddMeshes, SceneContent::Meshes, {}}, + {"except materials", "9 materials", + SceneConverterFeature::AddMaterials, SceneContent::Materials, {}}, + {"except textures", "10 textures", + SceneConverterFeature::AddTextures, SceneContent::Textures, {}}, + {"except 1D images", "11 1D images", + SceneConverterFeature::AddImages1D, SceneContent::Images1D, {}}, + {"except 2D images", "12 2D images", + SceneConverterFeature::AddImages2D, SceneContent::Images2D, {}}, + {"except 3D images", "13 3D images", + SceneConverterFeature::AddImages3D, SceneContent::Images3D, {}}, + /* Should only warn about materials not supported by the converter, not + meshes because we don't want them anyway */ + {"except materials and meshes, without meshes", "9 materials", + SceneConverterFeature::AddMaterials|SceneConverterFeature::AddMeshes, + SceneContent::Materials|SceneContent::Meshes, + SceneContent::Meshes}, + /* Should only warn about materials not supported by the converter, nothing + about meshes (which are available in the importer always but not + passed to the converter) */ + {"except materials, without meshes", "9 materials", + SceneConverterFeature::AddMaterials, SceneContent::Materials, + SceneContent::Meshes}, +}; + AbstractSceneConverterTest::AbstractSceneConverterTest() { - addTests({&AbstractSceneConverterTest::featuresNone, + addTests({&AbstractSceneConverterTest::sceneContentsForImporterNone, + &AbstractSceneConverterTest::sceneContentsForImporterAll, + &AbstractSceneConverterTest::sceneContentsForImporterNotOpened, + + &AbstractSceneConverterTest::sceneContentsForConverterNone, + &AbstractSceneConverterTest::sceneContentsForConverterSingleMesh, + &AbstractSceneConverterTest::sceneContentsForConverterAll, + + &AbstractSceneConverterTest::featuresNone, &AbstractSceneConverterTest::setFlags, &AbstractSceneConverterTest::setFlagsNotImplemented, @@ -487,7 +717,30 @@ AbstractSceneConverterTest::AbstractSceneConverterTest() { &AbstractSceneConverterTest::addImage1DThroughLevels, &AbstractSceneConverterTest::addImage2DThroughLevels, - &AbstractSceneConverterTest::addImage3DThroughLevels, + &AbstractSceneConverterTest::addImage3DThroughLevels}); + + addInstancedTests({&AbstractSceneConverterTest::addImporterContents}, + Containers::arraySize(AddImporterContentsData)); + + addTests({&AbstractSceneConverterTest::addImporterContentsCustomSceneFields, + &AbstractSceneConverterTest::addImporterContentsCustomMeshAttributes}); + + addInstancedTests({&AbstractSceneConverterTest::addImporterContentsImportFail}, + Containers::arraySize(AddImporterContentsFailData)); + + addInstancedTests({&AbstractSceneConverterTest::addImporterContentsConversionFail}, + Containers::arraySize(AddImporterContentsFailData)); + + addTests({&AbstractSceneConverterTest::addImporterContentsNotConverting, + &AbstractSceneConverterTest::addImporterContentsNotOpened, + &AbstractSceneConverterTest::addImporterContentsNotSupported, &AbstractSceneConverterTest::addImporterContentsNotSupportedLevels, + &AbstractSceneConverterTest::addImporterContentsNotSupportedUncompressedImage, + &AbstractSceneConverterTest::addImporterContentsNotSupportedCompressedImage}); + + addInstancedTests({&AbstractSceneConverterTest::addSupportedImporterContents}, + Containers::arraySize(AddSupportedImporterContentsData)); + + addTests({&AbstractSceneConverterTest::addSupportedImporterContentsLevels, &AbstractSceneConverterTest::debugFeature, &AbstractSceneConverterTest::debugFeaturePacked, @@ -495,12 +748,141 @@ AbstractSceneConverterTest::AbstractSceneConverterTest() { &AbstractSceneConverterTest::debugFeaturesPacked, &AbstractSceneConverterTest::debugFeaturesSupersets, &AbstractSceneConverterTest::debugFlag, - &AbstractSceneConverterTest::debugFlags}); + &AbstractSceneConverterTest::debugFlags, + &AbstractSceneConverterTest::debugContent, + &AbstractSceneConverterTest::debugContentPacked, + &AbstractSceneConverterTest::debugContents, + &AbstractSceneConverterTest::debugContentsPacked}); /* Create testing dir */ Utility::Path::make(TRADE_TEST_OUTPUT_DIR); } +void AbstractSceneConverterTest::sceneContentsForImporterNone() { + struct: AbstractImporter { + ImporterFeatures doFeatures() const override { return {}; } + bool doIsOpened() const override { return true; } + void doClose() override {} + } importer; + + CORRADE_COMPARE(sceneContentsFor(importer), SceneContent::Names); +} + +void AbstractSceneConverterTest::sceneContentsForImporterAll() { + struct: AbstractImporter { + ImporterFeatures doFeatures() const override { return {}; } + bool doIsOpened() const override { return true; } + void doClose() override {} + + UnsignedInt doSceneCount() const override { return 1; } + UnsignedInt doAnimationCount() const override { return 1; } + UnsignedInt doLightCount() const override { return 1; } + UnsignedInt doCameraCount() const override { return 1; } + UnsignedInt doSkin2DCount() const override { return 1; } + UnsignedInt doSkin3DCount() const override { return 1; } + UnsignedInt doMeshCount() const override { return 1; } + UnsignedInt doMeshLevelCount(UnsignedInt) override { return 17; } + UnsignedInt doMaterialCount() const override { return 1; } + UnsignedInt doTextureCount() const override { return 1; } + UnsignedInt doImage1DCount() const override { return 1; } + UnsignedInt doImage1DLevelCount(UnsignedInt) override { return 17; } + UnsignedInt doImage2DCount() const override { return 1; } + UnsignedInt doImage2DLevelCount(UnsignedInt) override { return 17; } + UnsignedInt doImage3DCount() const override { return 1; } + UnsignedInt doImage3DLevelCount(UnsignedInt) override { return 17; } + } importer; + + CORRADE_COMPARE(sceneContentsFor(importer), + SceneContent::Scenes| + SceneContent::Animations| + SceneContent::Lights| + SceneContent::Cameras| + SceneContent::Skins2D| + SceneContent::Skins3D| + SceneContent::Meshes| + SceneContent::Materials| + SceneContent::Textures| + SceneContent::Images1D| + SceneContent::Images2D| + SceneContent::Images3D| + /* No mesh or image levels, even though reported */ + SceneContent::Names); +} + +void AbstractSceneConverterTest::sceneContentsForImporterNotOpened() { + CORRADE_SKIP_IF_NO_ASSERT(); + + struct: AbstractImporter { + ImporterFeatures doFeatures() const override { return {}; } + bool doIsOpened() const override { return false; } + void doClose() override {} + } importer; + + std::ostringstream out; + Error redirectError{&out}; + sceneContentsFor(importer); + CORRADE_COMPARE(out.str(), "Trade::sceneContentsFor(): the importer is not opened\n"); +} + +void AbstractSceneConverterTest::sceneContentsForConverterNone() { + struct: AbstractSceneConverter { + SceneConverterFeatures doFeatures() const override { + return SceneConverterFeature::ConvertMeshInPlace; + } + } converter; + + CORRADE_COMPARE(sceneContentsFor(converter), SceneContent::Names); +} + +void AbstractSceneConverterTest::sceneContentsForConverterSingleMesh() { + struct: AbstractSceneConverter { + SceneConverterFeatures doFeatures() const override { + return SceneConverterFeature::ConvertMeshToFile; + } + } converter; + + CORRADE_COMPARE(sceneContentsFor(converter), SceneContent::Meshes|SceneContent::Names); +} + +void AbstractSceneConverterTest::sceneContentsForConverterAll() { + struct: AbstractSceneConverter { + SceneConverterFeatures doFeatures() const override { + return + SceneConverterFeature::AddScenes| + SceneConverterFeature::AddAnimations| + SceneConverterFeature::AddLights| + SceneConverterFeature::AddCameras| + SceneConverterFeature::AddSkins2D| + SceneConverterFeature::AddSkins3D| + SceneConverterFeature::AddMeshes| + SceneConverterFeature::AddMaterials| + SceneConverterFeature::AddTextures| + SceneConverterFeature::AddImages1D| + SceneConverterFeature::AddImages2D| + SceneConverterFeature::AddImages3D| + SceneConverterFeature::MeshLevels| + SceneConverterFeature::ImageLevels; + } + } converter; + + CORRADE_COMPARE(sceneContentsFor(converter), + SceneContent::Scenes| + SceneContent::Animations| + SceneContent::Lights| + SceneContent::Cameras| + SceneContent::Skins2D| + SceneContent::Skins3D| + SceneContent::Meshes| + SceneContent::Materials| + SceneContent::Textures| + SceneContent::Images1D| + SceneContent::Images2D| + SceneContent::Images3D| + SceneContent::MeshLevels| + SceneContent::ImageLevels| + SceneContent::Names); +} + void AbstractSceneConverterTest::featuresNone() { CORRADE_SKIP_IF_NO_ASSERT(); @@ -5371,63 +5753,1642 @@ void AbstractSceneConverterTest::addImage3DThroughLevels() { CORRADE_COMPARE(converter.image3DCount(), 1); } -void AbstractSceneConverterTest::debugFeature() { - std::ostringstream out; +void AbstractSceneConverterTest::addImporterContents() { + auto&& data = AddImporterContentsData[testCaseInstanceId()]; + setTestCaseDescription(data.name); - Debug{&out} << SceneConverterFeature::ConvertMeshInPlace << SceneConverterFeature(0xdeaddead); - CORRADE_COMPARE(out.str(), "Trade::SceneConverterFeature::ConvertMeshInPlace Trade::SceneConverterFeature(0xdeaddead)\n"); -} + struct Importer: AbstractImporter { + explicit Importer(SceneContents contents): contents{contents} {} + + ImporterFeatures doFeatures() const override { return {}; } + bool doIsOpened() const override { return true; } + void doClose() override {} + + UnsignedLong doObjectCount() const override { + /* Counts are queried unconditionally in an assert, so can't + really check anything here. Returning 0 would be + counterproductive, the addImporterContentsImportFail() case + tests that the right *Count() gets called. */ + return 3; + } + Containers::String doObjectName(UnsignedLong id) override { + CORRADE_VERIFY(contents & SceneContent::Names); + return Utility::format("Object {}", id); + } + + UnsignedInt doSceneCount() const override { + /* Counts are queried unconditionally in an assert, so can't + really check anything here. Returning 0 would be + counterproductive, the addImporterContentsImportFail() case + tests that the right *Count() gets called. */ + return 2; + } + Int doDefaultScene() const override { + CORRADE_VERIFY(contents & SceneContent::Scenes); + return 1; + } + Containers::String doSceneName(UnsignedInt id) override { + CORRADE_VERIFY(contents & SceneContent::Names); + return Utility::format("Scene {}", id); + } + Containers::Optional doScene(UnsignedInt id) override { + return SceneData{SceneMappingType::UnsignedInt, 0, nullptr, {}, reinterpret_cast(0x5ce00000 + id)}; + } + + UnsignedInt doAnimationCount() const override { + /* Counts are queried unconditionally in an assert, so can't + really check anything here. Returning 0 would be + counterproductive, the addImporterContentsImportFail() case + tests that the right *Count() gets called. */ + return 2; + } + Containers::String doAnimationName(UnsignedInt id) override { + CORRADE_VERIFY(contents & SceneContent::Names); + return Utility::format("Animation {}", id); + } + Containers::Optional doAnimation(UnsignedInt id) override { + return AnimationData{nullptr, {}, reinterpret_cast(0x40100000 + id)}; + } + + UnsignedInt doLightCount() const override { + /* Counts are queried unconditionally in an assert, so can't + really check anything here. Returning 0 would be + counterproductive, the addImporterContentsImportFail() case + tests that the right *Count() gets called. */ + return 2; + } + Containers::String doLightName(UnsignedInt id) override { + CORRADE_VERIFY(contents & SceneContent::Names); + return Utility::format("Light {}", id); + } + Containers::Optional doLight(UnsignedInt id) override { + return LightData{LightData::Type::Point, {}, {}, reinterpret_cast(0x11600000 + id)}; + } + + UnsignedInt doCameraCount() const override { + /* Counts are queried unconditionally in an assert, so can't + really check anything here. Returning 0 would be + counterproductive, the addImporterContentsImportFail() case + tests that the right *Count() gets called. */ + return 2; + } + Containers::String doCameraName(UnsignedInt id) override { + CORRADE_VERIFY(contents & SceneContent::Names); + return Utility::format("Camera {}", id); + } + Containers::Optional doCamera(UnsignedInt id) override { + return CameraData{CameraType::Orthographic2D, {}, 0.0f, 0.0f, reinterpret_cast(0xca0e0000 + id)}; + } + + UnsignedInt doSkin2DCount() const override { + /* Counts are queried unconditionally in an assert, so can't + really check anything here. Returning 0 would be + counterproductive, the addImporterContentsImportFail() case + tests that the right *Count() gets called. */ + return 2; + } + Containers::String doSkin2DName(UnsignedInt id) override { + CORRADE_VERIFY(contents & SceneContent::Names); + return Utility::format("2D skin {}", id); + } + Containers::Optional doSkin2D(UnsignedInt id) override { + return SkinData2D{{}, {}, reinterpret_cast(0x50102d00 + id)}; + } + + UnsignedInt doSkin3DCount() const override { + /* Counts are queried unconditionally in an assert, so can't + really check anything here. Returning 0 would be + counterproductive, the addImporterContentsImportFail() case + tests that the right *Count() gets called. */ + return 2; + } + Containers::String doSkin3DName(UnsignedInt id) override { + CORRADE_VERIFY(contents & SceneContent::Names); + return Utility::format("3D skin {}", id); + } + Containers::Optional doSkin3D(UnsignedInt id) override { + return SkinData3D{{}, {}, reinterpret_cast(0x50103d00 + id)}; + } + + UnsignedInt doMeshCount() const override { + /* Counts are queried unconditionally in an assert, so can't + really check anything here. Returning 0 would be + counterproductive, the addImporterContentsImportFail() case + tests that the right *Count() gets called. */ + return 2; + } + UnsignedInt doMeshLevelCount(UnsignedInt id) override { + CORRADE_VERIFY(contents & SceneContent::MeshLevels); + return id == 1 ? 3 : 1; + } + Containers::String doMeshName(UnsignedInt id) override { + CORRADE_VERIFY(contents & SceneContent::Names); + return Utility::format("Mesh {}", id); + } + Containers::Optional doMesh(UnsignedInt id, UnsignedInt level) override { + return MeshData{{}, 0, reinterpret_cast(0x0e500000 + id + level*16)}; + } + + UnsignedInt doMaterialCount() const override { + /* Counts are queried unconditionally in an assert, so can't + really check anything here. Returning 0 would be + counterproductive, the addImporterContentsImportFail() case + tests that the right *Count() gets called. */ + return 2; + } + Containers::String doMaterialName(UnsignedInt id) override { + CORRADE_VERIFY(contents & SceneContent::Names); + return Utility::format("Material {}", id); + } + Containers::Optional doMaterial(UnsignedInt id) override { + return MaterialData{{}, {}, reinterpret_cast(0x0a7e0000 + id)}; + } + + UnsignedInt doTextureCount() const override { + /* Counts are queried unconditionally in an assert, so can't + really check anything here. Returning 0 would be + counterproductive, the addImporterContentsImportFail() case + tests that the right *Count() gets called. */ + return 2; + } + Containers::String doTextureName(UnsignedInt id) override { + CORRADE_VERIFY(contents & SceneContent::Names); + return Utility::format("Texture {}", id); + } + Containers::Optional doTexture(UnsignedInt id) override { + return TextureData{TextureType::Texture1D, SamplerFilter::Nearest, SamplerFilter::Nearest, SamplerMipmap::Nearest, SamplerWrapping::ClampToEdge, 0, reinterpret_cast(0x7e070000 + id)}; + } + + UnsignedInt doImage1DCount() const override { + /* Counts are queried unconditionally in an assert, so can't + really check anything here. Returning 0 would be + counterproductive, the addImporterContentsImportFail() case + tests that the right *Count() gets called. */ + return 2; + } + UnsignedInt doImage1DLevelCount(UnsignedInt id) override { + CORRADE_VERIFY(contents & SceneContent::ImageLevels); + return id == 1 ? 4 : 1; + } + Containers::String doImage1DName(UnsignedInt id) override { + CORRADE_VERIFY(contents & SceneContent::Names); + return Utility::format("1D image {}", id); + } + Containers::Optional doImage1D(UnsignedInt id, UnsignedInt level) override { + return ImageData1D{PixelFormat::RGBA8Unorm, 1, {}, "yes", {}, reinterpret_cast(0x10a91d00 + id + level*16)}; + } + + UnsignedInt doImage2DCount() const override { + /* Counts are queried unconditionally in an assert, so can't + really check anything here. Returning 0 would be + counterproductive, the addImporterContentsImportFail() case + tests that the right *Count() gets called. */ + return 2; + } + UnsignedInt doImage2DLevelCount(UnsignedInt id) override { + CORRADE_VERIFY(contents & SceneContent::ImageLevels); + return id == 0 ? 3 : 1; + } + Containers::String doImage2DName(UnsignedInt id) override { + CORRADE_VERIFY(contents & SceneContent::Names); + return Utility::format("2D image {}", id); + } + Containers::Optional doImage2D(UnsignedInt id, UnsignedInt level) override { + return ImageData2D{PixelFormat::RGBA8Unorm, {1, 1}, {}, "yes", {}, reinterpret_cast(0x10a92d00 + id + level*16)}; + } + + UnsignedInt doImage3DCount() const override { + /* Counts are queried unconditionally in an assert, so can't + really check anything here. Returning 0 would be + counterproductive, the addImporterContentsImportFail() case + tests that the right *Count() gets called. */ + return 2; + } + UnsignedInt doImage3DLevelCount(UnsignedInt id) override { + CORRADE_VERIFY(contents & SceneContent::ImageLevels); + return id == 1 ? 2 : 1; + } + Containers::String doImage3DName(UnsignedInt id) override { + CORRADE_VERIFY(contents & SceneContent::Names); + return Utility::format("3D image {}", id); + } + Containers::Optional doImage3D(UnsignedInt id, UnsignedInt level) override { + return ImageData3D{PixelFormat::RGBA8Unorm, {1, 1, 1}, {}, "yes", {}, reinterpret_cast(0x10a93d00 + id + level*16)}; + } + + SceneContents contents; + } importer{data.contents}; + + struct: AbstractSceneConverter { + SceneConverterFeatures doFeatures() const override { + return + SceneConverterFeature::ConvertMultipleToData| + SceneConverterFeature::AddScenes| + SceneConverterFeature::AddAnimations| + SceneConverterFeature::AddLights| + SceneConverterFeature::AddCameras| + SceneConverterFeature::AddSkins2D| + SceneConverterFeature::AddSkins3D| + SceneConverterFeature::AddMeshes| + SceneConverterFeature::AddMaterials| + SceneConverterFeature::AddTextures| + SceneConverterFeature::AddImages1D| + SceneConverterFeature::AddImages2D| + SceneConverterFeature::AddImages3D| + SceneConverterFeature::MeshLevels| + SceneConverterFeature::ImageLevels; + } + + bool doBeginData() override { return true; } + + void doSetObjectName(UnsignedLong id, Containers::StringView name) override { + Debug{} << "Setting object" << id << "name to" << name; + } + bool doAdd(UnsignedInt id, const SceneData& data, Containers::StringView name) override { + Debug{} << "Adding scene" << id << "named" << name << "with" << data.importerState(); + return true; + } + void doSetDefaultScene(UnsignedInt id) override { + Debug{} << "Setting default scene to" << id; + } + + bool doAdd(UnsignedInt id, const AnimationData& data, Containers::StringView name) override { + Debug{} << "Adding animation" << id << "named" << name << "with" << data.importerState(); + return true; + } + bool doAdd(UnsignedInt id, const LightData& data, Containers::StringView name) override { + Debug{} << "Adding light" << id << "named" << name << "with" << data.importerState(); + return true; + } + bool doAdd(UnsignedInt id, const CameraData& data, Containers::StringView name) override { + Debug{} << "Adding camera" << id << "named" << name << "with" << data.importerState(); + return true; + } + bool doAdd(UnsignedInt id, const SkinData2D& data, Containers::StringView name) override { + Debug{} << "Adding 2D skin" << id << "named" << name << "with" << data.importerState(); + return true; + } + bool doAdd(UnsignedInt id, const SkinData3D& data, Containers::StringView name) override { + Debug{} << "Adding 3D skin" << id << "named" << name << "with" << data.importerState(); + return true; + } + + bool doAdd(UnsignedInt id, const MeshData& data, Containers::StringView name) override { + Debug{} << "Adding mesh" << id << "named" << name << "with" << data.importerState(); + return true; + } + bool doAdd(UnsignedInt id, Containers::Iterable levels, Containers::StringView name) override { + for(std::size_t i = 0; i != levels.size(); ++i) + Debug{} << "Adding mesh" << id << "level" << i << "named" << name << "with" << levels[i].importerState(); + return true; + } + + bool doAdd(UnsignedInt id, const MaterialData& data, Containers::StringView name) override { + Debug{} << "Adding material" << id << "named" << name << "with" << data.importerState(); + return true; + } + bool doAdd(UnsignedInt id, const TextureData& data, Containers::StringView name) override { + Debug{} << "Adding texture" << id << "named" << name << "with" << data.importerState(); + return true; + } + + bool doAdd(UnsignedInt id, const ImageData1D& data, Containers::StringView name) override { + Debug{} << "Adding 1D image" << id << "named" << name << "with" << data.importerState(); + return true; + } + bool doAdd(UnsignedInt id, Containers::Iterable levels, Containers::StringView name) override { + for(std::size_t i = 0; i != levels.size(); ++i) + Debug{} << "Adding 1D image" << id << "level" << i << "named" << name << "with" << levels[i].importerState(); + return true; + } + + bool doAdd(UnsignedInt id, const ImageData2D& data, Containers::StringView name) override { + Debug{} << "Adding 2D image" << id << "named" << name << "with" << data.importerState(); + return true; + } + bool doAdd(UnsignedInt id, Containers::Iterable levels, Containers::StringView name) override { + for(std::size_t i = 0; i != levels.size(); ++i) + Debug{} << "Adding 2D image" << id << "level" << i << "named" << name << "with" << levels[i].importerState(); + return true; + } + + bool doAdd(UnsignedInt id, const ImageData3D& data, Containers::StringView name) override { + Debug{} << "Adding 3D image" << id << "named" << name << "with" << data.importerState(); + return true; + } + bool doAdd(UnsignedInt id, Containers::Iterable levels, Containers::StringView name) override { + for(std::size_t i = 0; i != levels.size(); ++i) + Debug{} << "Adding 3D image" << id << "level" << i << "named" << name << "with" << levels[i].importerState(); + return true; + } + } converter; + + CORRADE_VERIFY(converter.beginData()); -void AbstractSceneConverterTest::debugFeaturePacked() { std::ostringstream out; - /* Last is not packed, ones before should not make any flags persistent */ - Debug{&out} << Debug::packed << SceneConverterFeature::ConvertMeshInPlace << Debug::packed << SceneConverterFeature(0xdeaddead) << SceneConverterFeature::AddCameras; - CORRADE_COMPARE(out.str(), "ConvertMeshInPlace 0xdeaddead Trade::SceneConverterFeature::AddCameras\n"); + Debug redirectOutput{&out}; + CORRADE_VERIFY(converter.addImporterContents(importer, data.contents)); + CORRADE_COMPARE(out.str(), data.expected); } -void AbstractSceneConverterTest::debugFeatures() { - std::ostringstream out; +void AbstractSceneConverterTest::addImporterContentsCustomSceneFields() { + struct: AbstractImporter { + ImporterFeatures doFeatures() const override { return {}; } + bool doIsOpened() const override { return true; } + void doClose() override {} - Debug{&out} << (SceneConverterFeature::ConvertMesh|SceneConverterFeature::ConvertMeshToFile) << SceneConverterFeatures{}; - CORRADE_COMPARE(out.str(), "Trade::SceneConverterFeature::ConvertMesh|Trade::SceneConverterFeature::ConvertMeshToFile Trade::SceneConverterFeatures{}\n"); -} + UnsignedInt doSceneCount() const override { + return 3; + } + Containers::Optional doScene(UnsignedInt id) override { + if(id == 1 || id == 2) + return SceneData{SceneMappingType::UnsignedInt, 0, nullptr, { + SceneFieldData{SceneField::Translation, SceneMappingType::UnsignedInt, nullptr, SceneFieldType::Vector3, nullptr}, + SceneFieldData{sceneFieldCustom(34977), SceneMappingType::UnsignedInt, nullptr, SceneFieldType::Vector2b, nullptr}, + SceneFieldData{SceneField::Scaling, SceneMappingType::UnsignedInt, nullptr, SceneFieldType::Vector3, nullptr}, + SceneFieldData{sceneFieldCustom(5266), SceneMappingType::UnsignedInt, nullptr, SceneFieldType::Pointer, nullptr}, + }}; + return SceneData{SceneMappingType::UnsignedInt, 0, nullptr, {}}; + } + Containers::String doSceneFieldName(UnsignedInt name) override { + if(name == 34977) return "OffsetSmall"; + if(name == 5266) return "ValueData"; + CORRADE_FAIL_IF(true, "This should not be reached"); + CORRADE_INTERNAL_ASSERT_UNREACHABLE(); + } + } importer; + + struct: AbstractSceneConverter { + SceneConverterFeatures doFeatures() const override { + return + SceneConverterFeature::ConvertMultipleToData| + SceneConverterFeature::AddScenes; + } + + bool doBeginData() override { return true; } + + bool doAdd(UnsignedInt, const SceneData&, Containers::StringView) override { + Debug{} << "Adding scene"; + return true; + } + void doSetSceneFieldName(UnsignedInt field, Containers::StringView name) override { + Debug{} << "Setting field" << field << "name to" << name; + } + } converter; + + CORRADE_VERIFY(converter.beginData()); -void AbstractSceneConverterTest::debugFeaturesPacked() { std::ostringstream out; - /* Last is not packed, ones before should not make any flags persistent */ - Debug{&out} << Debug::packed << (SceneConverterFeature::ConvertMesh|SceneConverterFeature::ConvertMeshToFile) << Debug::packed << SceneConverterFeatures{} << SceneConverterFeature::AddLights; - CORRADE_COMPARE(out.str(), "ConvertMesh|ConvertMeshToFile {} Trade::SceneConverterFeature::AddLights\n"); + Debug redirectOutput{&out}; + CORRADE_VERIFY(converter.addImporterContents(importer)); + /* No error message, the importer is expected to print that on its own */ + CORRADE_COMPARE(out.str(), + "Adding scene\n" + /** @todo cache the names to avoid querying repeatedly */ + "Setting field 34977 name to OffsetSmall\n" + "Setting field 5266 name to ValueData\n" + "Adding scene\n" + "Setting field 34977 name to OffsetSmall\n" + "Setting field 5266 name to ValueData\n" + "Adding scene\n"); } -void AbstractSceneConverterTest::debugFeaturesSupersets() { - /* ConvertMeshToData is a superset of ConvertMeshToFile, so only one should - be printed */ - { - std::ostringstream out; - Debug{&out} << (SceneConverterFeature::ConvertMeshToData|SceneConverterFeature::ConvertMeshToFile); - CORRADE_COMPARE(out.str(), "Trade::SceneConverterFeature::ConvertMeshToData\n"); +void AbstractSceneConverterTest::addImporterContentsCustomMeshAttributes() { + struct: AbstractImporter { + ImporterFeatures doFeatures() const override { return {}; } + bool doIsOpened() const override { return true; } + void doClose() override {} - /* ConvertMultipleToData is a superset of ConvertMultipleToFile, so only - one should be printed */ - } { - std::ostringstream out; - Debug{&out} << (SceneConverterFeature::ConvertMultipleToData|SceneConverterFeature::ConvertMultipleToFile); - CORRADE_COMPARE(out.str(), "Trade::SceneConverterFeature::ConvertMultipleToData\n"); - } -} + UnsignedInt doMeshCount() const override { + return 2; + } + UnsignedInt doMeshLevelCount(UnsignedInt) override { + return 5; + } + Containers::Optional doMesh(UnsignedInt id, UnsignedInt level) override { + if(id == 1 && (level == 2 || level == 3)) + return MeshData{MeshPrimitive::Triangles, nullptr, { + MeshAttributeData{MeshAttribute::Position, VertexFormat::Vector3, nullptr}, + MeshAttributeData{meshAttributeCustom(31977), VertexFormat::Vector2b, nullptr}, + MeshAttributeData{MeshAttribute::Normal, VertexFormat::Vector3, nullptr}, + MeshAttributeData{meshAttributeCustom(5266), VertexFormat::ByteNormalized, nullptr}, + }}; + return MeshData{MeshPrimitive::Points, 0}; + } + Containers::String doMeshAttributeName(UnsignedShort name) override { + if(name == 31977) return "OffsetSmall"; + if(name == 5266) return "ValueData"; + CORRADE_FAIL_IF(true, "This should not be reached"); + CORRADE_INTERNAL_ASSERT_UNREACHABLE(); + } + } importer; -void AbstractSceneConverterTest::debugFlag() { - std::ostringstream out; + struct: AbstractSceneConverter { + SceneConverterFeatures doFeatures() const override { + return + SceneConverterFeature::ConvertMultipleToData| + SceneConverterFeature::AddMeshes| + SceneConverterFeature::MeshLevels; + } - Debug{&out} << SceneConverterFlag::Verbose << SceneConverterFlag(0xf0); - CORRADE_COMPARE(out.str(), "Trade::SceneConverterFlag::Verbose Trade::SceneConverterFlag(0xf0)\n"); -} + bool doBeginData() override { return true; } + + bool doAdd(UnsignedInt, Containers::Iterable, Containers::StringView) override { + Debug{} << "Adding mesh levels"; + return true; + } + void doSetMeshAttributeName(UnsignedShort attribute, Containers::StringView name) override { + Debug{} << "Setting attribute" << attribute << "name to" << name; + } + } converter; + + CORRADE_VERIFY(converter.beginData()); -void AbstractSceneConverterTest::debugFlags() { std::ostringstream out; + Debug redirectOutput{&out}; + CORRADE_VERIFY(converter.addImporterContents(importer)); + /* No error message, the importer is expected to print that on its own */ + CORRADE_COMPARE(out.str(), + "Adding mesh levels\n" + /** @todo cache the names to avoid querying repeatedly */ + "Setting attribute 31977 name to OffsetSmall\n" + "Setting attribute 5266 name to ValueData\n" + "Setting attribute 31977 name to OffsetSmall\n" + "Setting attribute 5266 name to ValueData\n" + "Adding mesh levels\n"); +} - Debug{&out} << (SceneConverterFlag::Verbose|SceneConverterFlag(0xf0)) << SceneConverterFlags{}; - CORRADE_COMPARE(out.str(), "Trade::SceneConverterFlag::Verbose|Trade::SceneConverterFlag(0xf0) Trade::SceneConverterFlags{}\n"); +void AbstractSceneConverterTest::addImporterContentsImportFail() { + auto&& data = AddImporterContentsFailData[testCaseInstanceId()]; + setTestCaseDescription(data.name); + + struct Importer: AbstractImporter { + explicit Importer(SceneContents contents): contents{contents} {} + + ImporterFeatures doFeatures() const override { return {}; } + bool doIsOpened() const override { return true; } + void doClose() override {} + + UnsignedInt doSceneCount() const override { + return contents & SceneContent::Scenes ? 4 : 0; + } + Containers::Optional doScene(UnsignedInt id) override { + if(id == 2) return {}; + return SceneData{SceneMappingType::UnsignedInt, 0, nullptr, {}}; + } + + UnsignedInt doAnimationCount() const override { + return contents & SceneContent::Animations ? 4 : 0; + } + Containers::Optional doAnimation(UnsignedInt id) override { + if(id == 2) return {}; + return AnimationData{nullptr, {}}; + } + + UnsignedInt doLightCount() const override { + return contents & SceneContent::Lights ? 4 : 0; + } + Containers::Optional doLight(UnsignedInt id) override { + if(id == 2) return {}; + return LightData{LightData::Type::Point, {}, {}}; + } + + UnsignedInt doCameraCount() const override { + return contents & SceneContent::Cameras ? 4 : 0; + } + Containers::Optional doCamera(UnsignedInt id) override { + if(id == 2) return {}; + return CameraData{CameraType::Orthographic2D, {}, 0.0f, 0.0f}; + } + + UnsignedInt doSkin2DCount() const override { + return contents & SceneContent::Skins2D ? 4 : 0; + } + Containers::Optional doSkin2D(UnsignedInt id) override { + if(id == 2) return {}; + return SkinData2D{{}, {}}; + } + + UnsignedInt doSkin3DCount() const override { + return contents & SceneContent::Skins3D ? 4 : 0; + } + Containers::Optional doSkin3D(UnsignedInt id) override { + if(id == 2) return {}; + return SkinData3D{{}, {}}; + } + + UnsignedInt doMeshCount() const override { + return contents & SceneContent::Meshes ? 4 : 0; + } + UnsignedInt doMeshLevelCount(UnsignedInt) override { + return contents & SceneContent::MeshLevels ? 5 : 1; + } + Containers::Optional doMesh(UnsignedInt id, UnsignedInt level) override { + if(id == 2) { + if(contents & SceneContent::MeshLevels) { + if(level == 3) return {}; + } else return {}; + } + + return MeshData{{}, 0}; + } + + UnsignedInt doMaterialCount() const override { + return contents & SceneContent::Materials ? 4 : 0; + } + Containers::Optional doMaterial(UnsignedInt id) override { + if(id == 2) return {}; + return MaterialData{{}, {}}; + } + + UnsignedInt doTextureCount() const override { + return contents & SceneContent::Textures ? 4 : 0; + } + Containers::Optional doTexture(UnsignedInt id) override { + if(id == 2) return {}; + return TextureData{TextureType::Texture1D, SamplerFilter::Nearest, SamplerFilter::Nearest, SamplerMipmap::Nearest, SamplerWrapping::ClampToEdge, 0}; + } + + UnsignedInt doImage1DCount() const override { + return contents & SceneContent::Images1D ? 4 : 0; + } + UnsignedInt doImage1DLevelCount(UnsignedInt) override { + return contents & SceneContent::ImageLevels ? 5 : 1; + } + Containers::Optional doImage1D(UnsignedInt id, UnsignedInt level) override { + if(id == 2) { + if(contents & SceneContent::ImageLevels) { + if(level == 3) return {}; + } else return {}; + } + + return ImageData1D{PixelFormat::RGBA8Unorm, 1, DataFlags{}, "yes"}; + } + + UnsignedInt doImage2DCount() const override { + return contents & SceneContent::Images2D ? 4 : 0; + } + UnsignedInt doImage2DLevelCount(UnsignedInt) override { + return contents & SceneContent::ImageLevels ? 5 : 1; + } + Containers::Optional doImage2D(UnsignedInt id, UnsignedInt level) override { + if(id == 2) { + if(contents & SceneContent::ImageLevels) { + if(level == 3) return {}; + } else return {}; + } + + return ImageData2D{PixelFormat::RGBA8Unorm, {1, 1}, DataFlags{}, "yes"}; + } + + UnsignedInt doImage3DCount() const override { + return contents & SceneContent::Images3D ? 4 : 0; + } + UnsignedInt doImage3DLevelCount(UnsignedInt) override { + return contents & SceneContent::ImageLevels ? 5 : 1; + } + Containers::Optional doImage3D(UnsignedInt id, UnsignedInt level) override { + if(id == 2) { + if(contents & SceneContent::ImageLevels) { + if(level == 3) return {}; + } else return {}; + } + + return ImageData3D{PixelFormat::RGBA8Unorm, {1, 1, 1}, DataFlags{}, "yes"}; + } + + SceneContents contents; + } importer{data.contents}; + + struct: AbstractSceneConverter { + SceneConverterFeatures doFeatures() const override { + return + SceneConverterFeature::ConvertMultipleToData| + SceneConverterFeature::AddScenes| + SceneConverterFeature::AddAnimations| + SceneConverterFeature::AddLights| + SceneConverterFeature::AddCameras| + SceneConverterFeature::AddSkins2D| + SceneConverterFeature::AddSkins3D| + SceneConverterFeature::AddMeshes| + SceneConverterFeature::AddMaterials| + SceneConverterFeature::AddTextures| + SceneConverterFeature::AddImages1D| + SceneConverterFeature::AddImages2D| + SceneConverterFeature::AddImages3D| + SceneConverterFeature::MeshLevels| + SceneConverterFeature::ImageLevels; + } + + bool doBeginData() override { return true; } + + bool doAdd(UnsignedInt, const SceneData&, Containers::StringView) override { + Debug{} << "Adding scene"; + return true; + } + + bool doAdd(UnsignedInt, const AnimationData&, Containers::StringView) override { + Debug{} << "Adding animation"; + return true; + } + bool doAdd(UnsignedInt, const LightData&, Containers::StringView) override { + Debug{} << "Adding light"; + return true; + } + bool doAdd(UnsignedInt, const CameraData&, Containers::StringView) override { + Debug{} << "Adding camera"; + return true; + } + bool doAdd(UnsignedInt, const SkinData2D&, Containers::StringView) override { + Debug{} << "Adding 2D skin"; + return true; + } + bool doAdd(UnsignedInt, const SkinData3D&, Containers::StringView) override { + Debug{} << "Adding 3D skin"; + return true; + } + + bool doAdd(UnsignedInt, const MeshData&, Containers::StringView) override { + Debug{} << "Adding mesh"; + return true; + } + bool doAdd(UnsignedInt, Containers::Iterable, Containers::StringView) override { + Debug{} << "Adding mesh levels"; + return true; + } + + bool doAdd(UnsignedInt, const MaterialData&, Containers::StringView) override { + Debug{} << "Adding material"; + return true; + } + bool doAdd(UnsignedInt, const TextureData&, Containers::StringView) override { + Debug{} << "Adding texture"; + return true; + } + + bool doAdd(UnsignedInt, const ImageData1D&, Containers::StringView) override { + Debug{} << "Adding 1D image"; + return true; + } + bool doAdd(UnsignedInt, Containers::Iterable, Containers::StringView) override { + Debug{} << "Adding 1D image levels"; + return true; + } + + bool doAdd(UnsignedInt, const ImageData2D&, Containers::StringView) override { + Debug{} << "Adding 2D image"; + return true; + } + bool doAdd(UnsignedInt, Containers::Iterable, Containers::StringView) override { + Debug{} << "Adding 2D image levels"; + return true; + } + bool doAdd(UnsignedInt, const ImageData3D&, Containers::StringView) override { + Debug{} << "Adding 3D image"; + return true; + } + bool doAdd(UnsignedInt, Containers::Iterable, Containers::StringView) override { + Debug{} << "Adding 3D image levels"; + return true; + } + } converter; + + CORRADE_VERIFY(converter.beginData()); + + std::ostringstream out; + Debug redirectOutput{&out}; + Error redirectError{&out}; + CORRADE_VERIFY(!converter.addImporterContents(importer, data.contents)); + /* No error message, the importer is expected to print that on its own */ + CORRADE_COMPARE(out.str(), Utility::format( + "Adding {0}\n" + "Adding {0}\n", data.name)); +} + +void AbstractSceneConverterTest::addImporterContentsConversionFail() { + auto&& data = AddImporterContentsFailData[testCaseInstanceId()]; + setTestCaseDescription(data.name); + + struct: AbstractImporter { + ImporterFeatures doFeatures() const override { return {}; } + bool doIsOpened() const override { return true; } + void doClose() override {} + + UnsignedInt doSceneCount() const override { + return 4; + } + Containers::Optional doScene(UnsignedInt) override { + return SceneData{SceneMappingType::UnsignedInt, 0, nullptr, {}}; + } + + UnsignedInt doAnimationCount() const override { + return 4; + } + Containers::Optional doAnimation(UnsignedInt) override { + return AnimationData{nullptr, {}}; + } + + UnsignedInt doLightCount() const override { + return 4; + } + Containers::Optional doLight(UnsignedInt) override { + return LightData{LightData::Type::Point, {}, {}}; + } + + UnsignedInt doCameraCount() const override { + return 4; + } + Containers::Optional doCamera(UnsignedInt) override { + return CameraData{CameraType::Orthographic2D, {}, 0.0f, 0.0f}; + } + + UnsignedInt doSkin2DCount() const override { + return 4; + } + Containers::Optional doSkin2D(UnsignedInt) override { + return SkinData2D{{}, {}}; + } + + UnsignedInt doSkin3DCount() const override { + return 4; + } + Containers::Optional doSkin3D(UnsignedInt) override { + return SkinData3D{{}, {}}; + } + + UnsignedInt doMeshCount() const override { + return 4; + } + UnsignedInt doMeshLevelCount(UnsignedInt) override { + return 5; + } + Containers::Optional doMesh(UnsignedInt, UnsignedInt) override { + return MeshData{{}, 0}; + } + + UnsignedInt doMaterialCount() const override { + return 4; + } + Containers::Optional doMaterial(UnsignedInt) override { + return MaterialData{{}, {}}; + } + + UnsignedInt doTextureCount() const override { + return 4; + } + Containers::Optional doTexture(UnsignedInt) override { + return TextureData{TextureType::Texture1D, SamplerFilter::Nearest, SamplerFilter::Nearest, SamplerMipmap::Nearest, SamplerWrapping::ClampToEdge, 0}; + } + + UnsignedInt doImage1DCount() const override { + return 4; + } + UnsignedInt doImage1DLevelCount(UnsignedInt) override { + return 5; + } + Containers::Optional doImage1D(UnsignedInt, UnsignedInt) override { + return ImageData1D{PixelFormat::RGBA8Unorm, 1, DataFlags{}, "yes"}; + } + + UnsignedInt doImage2DCount() const override { + return 4; + } + UnsignedInt doImage2DLevelCount(UnsignedInt) override { + return 5; + } + Containers::Optional doImage2D(UnsignedInt, UnsignedInt) override { + return ImageData2D{PixelFormat::RGBA8Unorm, {1, 1}, DataFlags{}, "yes"}; + } + + UnsignedInt doImage3DCount() const override { + return 4; + } + UnsignedInt doImage3DLevelCount(UnsignedInt) override { + return 5; + } + Containers::Optional doImage3D(UnsignedInt, UnsignedInt) override { + return ImageData3D{PixelFormat::RGBA8Unorm, {1, 1, 1}, DataFlags{}, "yes"}; + } + } importer; + + struct: AbstractSceneConverter { + SceneConverterFeatures doFeatures() const override { + return + SceneConverterFeature::ConvertMultipleToData| + SceneConverterFeature::AddScenes| + SceneConverterFeature::AddAnimations| + SceneConverterFeature::AddLights| + SceneConverterFeature::AddCameras| + SceneConverterFeature::AddSkins2D| + SceneConverterFeature::AddSkins3D| + SceneConverterFeature::AddMeshes| + SceneConverterFeature::AddMaterials| + SceneConverterFeature::AddTextures| + SceneConverterFeature::AddImages1D| + SceneConverterFeature::AddImages2D| + SceneConverterFeature::AddImages3D| + SceneConverterFeature::MeshLevels| + SceneConverterFeature::ImageLevels; + } + + bool doBeginData() override { return true; } + + bool doAdd(UnsignedInt id, const SceneData&, Containers::StringView) override { + if(id == 2) return false; + + Debug{} << "Adding scene"; + return true; + } + + bool doAdd(UnsignedInt id, const AnimationData&, Containers::StringView) override { + if(id == 2) return false; + + Debug{} << "Adding animation"; + return true; + } + bool doAdd(UnsignedInt id, const LightData&, Containers::StringView) override { + if(id == 2) return false; + + Debug{} << "Adding light"; + return true; + } + bool doAdd(UnsignedInt id, const CameraData&, Containers::StringView) override { + if(id == 2) return false; + + Debug{} << "Adding camera"; + return true; + } + bool doAdd(UnsignedInt id, const SkinData2D&, Containers::StringView) override { + if(id == 2) return false; + + Debug{} << "Adding 2D skin"; + return true; + } + bool doAdd(UnsignedInt id, const SkinData3D&, Containers::StringView) override { + if(id == 2) return false; + + Debug{} << "Adding 3D skin"; + return true; + } + + bool doAdd(UnsignedInt id, const MeshData&, Containers::StringView) override { + if(id == 2) return false; + + Debug{} << "Adding mesh"; + return true; + } + bool doAdd(UnsignedInt id, Containers::Iterable, Containers::StringView) override { + if(id == 2) return false; + + Debug{} << "Adding mesh levels"; + return true; + } + + bool doAdd(UnsignedInt id, const MaterialData&, Containers::StringView) override { + if(id == 2) return false; + + Debug{} << "Adding material"; + return true; + } + bool doAdd(UnsignedInt id, const TextureData&, Containers::StringView) override { + if(id == 2) return false; + + Debug{} << "Adding texture"; + return true; + } + + bool doAdd(UnsignedInt id, const ImageData1D&, Containers::StringView) override { + if(id == 2) return false; + + Debug{} << "Adding 1D image"; + return true; + } + bool doAdd(UnsignedInt id, Containers::Iterable, Containers::StringView) override { + if(id == 2) return false; + + Debug{} << "Adding 1D image levels"; + return true; + } + + bool doAdd(UnsignedInt id, const ImageData2D&, Containers::StringView) override { + if(id == 2) return false; + + Debug{} << "Adding 2D image"; + return true; + } + bool doAdd(UnsignedInt id, Containers::Iterable, Containers::StringView) override { + if(id == 2) return false; + + Debug{} << "Adding 2D image levels"; + return true; + } + bool doAdd(UnsignedInt id, const ImageData3D&, Containers::StringView) override { + if(id == 2) return false; + + Debug{} << "Adding 3D image"; + return true; + } + bool doAdd(UnsignedInt id, Containers::Iterable, Containers::StringView) override { + if(id == 2) return false; + + Debug{} << "Adding 3D image levels"; + return true; + } + } converter; + + CORRADE_VERIFY(converter.beginData()); + + std::ostringstream out; + Debug redirectOutput{&out}; + Error redirectError{&out}; + CORRADE_VERIFY(!converter.addImporterContents(importer, data.contents)); + /* No error message, the importer is expected to print that on its own */ + CORRADE_COMPARE(out.str(), Utility::format( + "Adding {0}\n" + "Adding {0}\n", data.name)); +} + +void AbstractSceneConverterTest::addImporterContentsNotConverting() { + CORRADE_SKIP_IF_NO_ASSERT(); + + struct: AbstractImporter { + ImporterFeatures doFeatures() const override { return {}; } + bool doIsOpened() const override { return true; } + void doClose() override {} + } importer; + + struct: AbstractSceneConverter { + SceneConverterFeatures doFeatures() const override { + return SceneConverterFeature::ConvertMultipleToData; + } + } converter; + + std::ostringstream out; + Error redirectError{&out}; + CORRADE_VERIFY(!converter.addImporterContents(importer, {})); + CORRADE_COMPARE(out.str(), "Trade::AbstractSceneConverter::addImporterContents(): no conversion in progress\n"); +} + +void AbstractSceneConverterTest::addImporterContentsNotOpened() { + CORRADE_SKIP_IF_NO_ASSERT(); + + struct: AbstractImporter { + ImporterFeatures doFeatures() const override { return {}; } + bool doIsOpened() const override { return false; } + void doClose() override {} + } importer; + + struct: AbstractSceneConverter { + SceneConverterFeatures doFeatures() const override { + return SceneConverterFeature::ConvertMultipleToData; + } + bool doBeginData() override { return true; } + } converter; + + CORRADE_VERIFY(converter.beginData()); + + std::ostringstream out; + Error redirectError{&out}; + CORRADE_VERIFY(!converter.addImporterContents(importer)); + CORRADE_COMPARE(out.str(), "Trade::AbstractSceneConverter::addImporterContents(): the importer is not opened\n"); +} + +void AbstractSceneConverterTest::addImporterContentsNotSupported() { + CORRADE_SKIP_IF_NO_ASSERT(); + + struct: AbstractImporter { + ImporterFeatures doFeatures() const override { return {}; } + bool doIsOpened() const override { return true; } + void doClose() override {} + + UnsignedInt doCameraCount() const override { return 2; } + UnsignedInt doLightCount() const override { return 4; } + UnsignedInt doMeshCount() const override { return 3; } + UnsignedInt doMaterialCount() const override { return 3; } + } importer; + + struct: AbstractSceneConverter { + SceneConverterFeatures doFeatures() const override { + return SceneConverterFeature::ConvertMultipleToData| + SceneConverterFeature::AddCameras| + SceneConverterFeature::AddMaterials; + } + bool doBeginData() override { return true; } + } converter; + + CORRADE_VERIFY(converter.beginData()); + + std::ostringstream out; + Error redirectError{&out}; + /* Scenes (which are not present in the input) should not be part of the + error, materials are in the input and supported, meshes and lights are + in the input but not supported so these should be printed */ + CORRADE_VERIFY(!converter.addImporterContents(importer, SceneContent::Scenes|SceneContent::Cameras|SceneContent::Meshes|SceneContent::Lights)); + CORRADE_COMPARE(out.str(), "Trade::AbstractSceneConverter::addImporterContents(): unsupported contents Lights|Meshes\n"); +} + +void AbstractSceneConverterTest::addImporterContentsNotSupportedLevels() { + struct: AbstractImporter { + ImporterFeatures doFeatures() const override { return {}; } + bool doIsOpened() const override { return true; } + void doClose() override {} + + UnsignedInt doMeshCount() const override { return 4; } + UnsignedInt doMeshLevelCount(UnsignedInt id) override { + return id == 2 ? 5 : 1; + } + Containers::Optional doMesh(UnsignedInt, UnsignedInt) override { + return MeshData{{}, 0}; + } + + UnsignedInt doImage1DCount() const override { return 4; } + UnsignedInt doImage1DLevelCount(UnsignedInt id) override { + return id == 2 ? 2 : 1; + } + Containers::Optional doImage1D(UnsignedInt, UnsignedInt) override { + return ImageData1D{PixelFormat::RGBA8Unorm, 1, DataFlags{}, "yes"}; + } + + UnsignedInt doImage2DCount() const override { return 4; } + UnsignedInt doImage2DLevelCount(UnsignedInt id) override { + return id == 2 ? 3 : 1; + } + Containers::Optional doImage2D(UnsignedInt, UnsignedInt) override { + return ImageData2D{PixelFormat::RGBA8Unorm, {1, 1}, DataFlags{}, "yes"}; + } + + UnsignedInt doImage3DCount() const override { return 4; } + UnsignedInt doImage3DLevelCount(UnsignedInt id) override { + return id == 2 ? 4 : 1; + } + Containers::Optional doImage3D(UnsignedInt, UnsignedInt) override { + return ImageData3D{PixelFormat::RGBA8Unorm, {1, 1, 1}, DataFlags{}, "yes"}; + } + } importer; + + struct: AbstractSceneConverter { + SceneConverterFeatures doFeatures() const override { + return SceneConverterFeature::ConvertMultipleToData| + SceneConverterFeature::AddMeshes| + SceneConverterFeature::AddImages1D| + SceneConverterFeature::AddImages2D| + SceneConverterFeature::AddImages3D; + } + bool doBeginData() override { return true; } + + bool doAdd(UnsignedInt, const MeshData&, Containers::StringView) override { + Debug{} << "Adding mesh"; + return true; + } + bool doAdd(UnsignedInt, const ImageData1D&, Containers::StringView) override { + Debug{} << "Adding 1D image"; + return true; + } + bool doAdd(UnsignedInt, const ImageData2D&, Containers::StringView) override { + Debug{} << "Adding 2D image"; + return true; + } + bool doAdd(UnsignedInt, const ImageData3D&, Containers::StringView) override { + Debug{} << "Adding 3D image"; + return true; + } + } converter; + + CORRADE_VERIFY(converter.beginData()); + + std::ostringstream out; + Debug redirectOutput{&out}; + Error redirectError{&out}; + CORRADE_VERIFY(!converter.addImporterContents(importer, SceneContent::Meshes|SceneContent::MeshLevels)); + CORRADE_VERIFY(!converter.addImporterContents(importer, SceneContent::Images1D|SceneContent::ImageLevels)); + CORRADE_VERIFY(!converter.addImporterContents(importer, SceneContent::Images2D|SceneContent::ImageLevels)); + CORRADE_VERIFY(!converter.addImporterContents(importer, SceneContent::Images3D|SceneContent::ImageLevels)); + CORRADE_COMPARE(out.str(), + "Adding mesh\n" + "Adding mesh\n" + "Trade::AbstractSceneConverter::addImporterContents(): mesh 2 contains 5 levels but the converter doesn't support Trade::SceneConverterFeature::MeshLevels\n" + "Adding 1D image\n" + "Adding 1D image\n" + "Trade::AbstractSceneConverter::addImporterContents(): 1D image 2 contains 2 levels but the converter doesn't support Trade::SceneConverterFeature::ImageLevels\n" + "Adding 2D image\n" + "Adding 2D image\n" + "Trade::AbstractSceneConverter::addImporterContents(): 2D image 2 contains 3 levels but the converter doesn't support Trade::SceneConverterFeature::ImageLevels\n" + "Adding 3D image\n" + "Adding 3D image\n" + "Trade::AbstractSceneConverter::addImporterContents(): 3D image 2 contains 4 levels but the converter doesn't support Trade::SceneConverterFeature::ImageLevels\n"); +} + +void AbstractSceneConverterTest::addImporterContentsNotSupportedUncompressedImage() { + struct: AbstractImporter { + ImporterFeatures doFeatures() const override { return {}; } + bool doIsOpened() const override { return true; } + void doClose() override {} + + UnsignedInt doImage1DCount() const override { return 4; } + UnsignedInt doImage1DLevelCount(UnsignedInt) override { return 3; } + Containers::Optional doImage1D(UnsignedInt id, UnsignedInt level) override { + return id == 2 && level == 1 ? + ImageData1D{PixelFormat::RGBA8Unorm, 1, DataFlags{}, "yes"} : + ImageData1D{CompressedPixelFormat::Bc1RGBUnorm, 1, DataFlags{}, "hello!!"}; + } + + UnsignedInt doImage2DCount() const override { return 5; } + UnsignedInt doImage2DLevelCount(UnsignedInt) override { return 4; } + Containers::Optional doImage2D(UnsignedInt id, UnsignedInt level) override { + return id == 3 && level == 2 ? + ImageData2D{PixelFormat::RGBA8Unorm, {1, 1}, DataFlags{}, "yes"} : + ImageData2D{CompressedPixelFormat::Bc1RGBUnorm, {1, 1}, DataFlags{}, "hello!!"}; + } + + UnsignedInt doImage3DCount() const override { return 6; } + UnsignedInt doImage3DLevelCount(UnsignedInt) override { return 5; } + Containers::Optional doImage3D(UnsignedInt id, UnsignedInt level) override { + return id == 4 && level == 3 ? + ImageData3D{PixelFormat::RGBA8Unorm, {1, 1, 1}, DataFlags{}, "yes"} : + ImageData3D{CompressedPixelFormat::Bc1RGBUnorm, {1, 1, 1}, DataFlags{}, "hello!!"}; + } + } importer; + + struct: AbstractSceneConverter { + SceneConverterFeatures doFeatures() const override { + return SceneConverterFeature::ConvertMultipleToData| + SceneConverterFeature::AddCompressedImages1D| + SceneConverterFeature::AddCompressedImages2D| + SceneConverterFeature::AddCompressedImages3D| + SceneConverterFeature::ImageLevels; + } + bool doBeginData() override { return true; } + + bool doAdd(UnsignedInt, Containers::Iterable, Containers::StringView) override { + Debug{} << "Added 1D image"; + return true; + } + bool doAdd(UnsignedInt, Containers::Iterable, Containers::StringView) override { + Debug{} << "Added 2D image"; + return true; + } + bool doAdd(UnsignedInt, Containers::Iterable, Containers::StringView) override { + Debug{} << "Added 3D image"; + return true; + } + } converter; + + CORRADE_VERIFY(converter.beginData()); + + std::ostringstream out; + Debug redirectOutput{&out}; + Error redirectError{&out}; + CORRADE_VERIFY(!converter.addImporterContents(importer, SceneContent::Images1D|SceneContent::ImageLevels)); + CORRADE_VERIFY(!converter.addImporterContents(importer, SceneContent::Images2D|SceneContent::ImageLevels)); + CORRADE_VERIFY(!converter.addImporterContents(importer, SceneContent::Images3D|SceneContent::ImageLevels)); + CORRADE_COMPARE(out.str(), + "Added 1D image\n" + "Added 1D image\n" + "Trade::AbstractSceneConverter::addImporterContents(): 1D image 2 level 1 is uncompressed but the converter doesn't support Trade::SceneConverterFeature::AddImages1D\n" + "Added 2D image\n" + "Added 2D image\n" + "Added 2D image\n" + "Trade::AbstractSceneConverter::addImporterContents(): 2D image 3 level 2 is uncompressed but the converter doesn't support Trade::SceneConverterFeature::AddImages2D\n" + "Added 3D image\n" + "Added 3D image\n" + "Added 3D image\n" + "Added 3D image\n" + "Trade::AbstractSceneConverter::addImporterContents(): 3D image 4 level 3 is uncompressed but the converter doesn't support Trade::SceneConverterFeature::AddImages3D\n"); +} + +void AbstractSceneConverterTest::addImporterContentsNotSupportedCompressedImage() { + struct: AbstractImporter { + ImporterFeatures doFeatures() const override { return {}; } + bool doIsOpened() const override { return true; } + void doClose() override {} + + UnsignedInt doImage1DCount() const override { return 4; } + UnsignedInt doImage1DLevelCount(UnsignedInt) override { return 3; } + Containers::Optional doImage1D(UnsignedInt id, UnsignedInt level) override { + return id == 2 && level == 1 ? + ImageData1D{CompressedPixelFormat::Bc1RGBUnorm, 1, DataFlags{}, "hello!!"} : + ImageData1D{PixelFormat::RGBA8Unorm, 1, DataFlags{}, "yes"}; + } + + UnsignedInt doImage2DCount() const override { return 5; } + UnsignedInt doImage2DLevelCount(UnsignedInt) override { return 4; } + Containers::Optional doImage2D(UnsignedInt id, UnsignedInt level) override { + return id == 3 && level == 2 ? + ImageData2D{CompressedPixelFormat::Bc1RGBUnorm, {1, 1}, DataFlags{}, "hello!!"} : + ImageData2D{PixelFormat::RGBA8Unorm, {1, 1}, DataFlags{}, "yes"}; + } + + UnsignedInt doImage3DCount() const override { return 6; } + UnsignedInt doImage3DLevelCount(UnsignedInt) override { return 5; } + Containers::Optional doImage3D(UnsignedInt id, UnsignedInt level) override { + return id == 4 && level == 3 ? + ImageData3D{CompressedPixelFormat::Bc1RGBUnorm, {1, 1, 1}, DataFlags{}, "hello!!"} : + ImageData3D{PixelFormat::RGBA8Unorm, {1, 1, 1}, DataFlags{}, "yes"}; + } + } importer; + + struct: AbstractSceneConverter { + SceneConverterFeatures doFeatures() const override { + return SceneConverterFeature::ConvertMultipleToData| + SceneConverterFeature::AddImages1D| + SceneConverterFeature::AddImages2D| + SceneConverterFeature::AddImages3D| + SceneConverterFeature::ImageLevels; + } + bool doBeginData() override { return true; } + + bool doAdd(UnsignedInt, Containers::Iterable, Containers::StringView) override { + Debug{} << "Added 1D image"; + return true; + } + bool doAdd(UnsignedInt, Containers::Iterable, Containers::StringView) override { + Debug{} << "Added 2D image"; + return true; + } + bool doAdd(UnsignedInt, Containers::Iterable, Containers::StringView) override { + Debug{} << "Added 3D image"; + return true; + } + } converter; + + CORRADE_VERIFY(converter.beginData()); + + std::ostringstream out; + Debug redirectOutput{&out}; + Error redirectError{&out}; + CORRADE_VERIFY(!converter.addImporterContents(importer, SceneContent::Images1D|SceneContent::ImageLevels)); + CORRADE_VERIFY(!converter.addImporterContents(importer, SceneContent::Images2D|SceneContent::ImageLevels)); + CORRADE_VERIFY(!converter.addImporterContents(importer, SceneContent::Images3D|SceneContent::ImageLevels)); + CORRADE_COMPARE(out.str(), + "Added 1D image\n" + "Added 1D image\n" + "Trade::AbstractSceneConverter::addImporterContents(): 1D image 2 level 1 is compressed but the converter doesn't support Trade::SceneConverterFeature::AddCompressedImages1D\n" + "Added 2D image\n" + "Added 2D image\n" + "Added 2D image\n" + "Trade::AbstractSceneConverter::addImporterContents(): 2D image 3 level 2 is compressed but the converter doesn't support Trade::SceneConverterFeature::AddCompressedImages2D\n" + "Added 3D image\n" + "Added 3D image\n" + "Added 3D image\n" + "Added 3D image\n" + "Trade::AbstractSceneConverter::addImporterContents(): 3D image 4 level 3 is compressed but the converter doesn't support Trade::SceneConverterFeature::AddCompressedImages3D\n"); +} + +void AbstractSceneConverterTest::addSupportedImporterContents() { + auto&& data = AddSupportedImporterContentsData[testCaseInstanceId()]; + setTestCaseDescription(data.name); + + struct: AbstractImporter { + ImporterFeatures doFeatures() const override { return {}; } + bool doIsOpened() const override { return true; } + void doClose() override {} + + UnsignedInt doSceneCount() const override { return 2; } + Containers::Optional doScene(UnsignedInt) override { + return SceneData{SceneMappingType::UnsignedInt, 0, nullptr, {}}; + } + + UnsignedInt doAnimationCount() const override { return 3; } + Containers::Optional doAnimation(UnsignedInt) override { + return AnimationData{nullptr, {}}; + } + + UnsignedInt doLightCount() const override { return 4; } + Containers::Optional doLight(UnsignedInt) override { + return LightData{LightData::Type::Point, {}, {}}; + } + + UnsignedInt doCameraCount() const override { return 5; } + Containers::Optional doCamera(UnsignedInt) override { + return CameraData{CameraType::Orthographic2D, {}, 0.0f, 0.0f}; + } + + UnsignedInt doSkin2DCount() const override { return 6; } + Containers::Optional doSkin2D(UnsignedInt) override { + return SkinData2D{{}, {}}; + } + + UnsignedInt doSkin3DCount() const override { return 7; } + Containers::Optional doSkin3D(UnsignedInt) override { + return SkinData3D{{}, {}}; + } + + UnsignedInt doMeshCount() const override { return 8; } + UnsignedInt doMeshLevelCount(UnsignedInt) override { + return 5; + } + Containers::Optional doMesh(UnsignedInt, UnsignedInt) override { + return MeshData{{}, 0}; + } + + UnsignedInt doMaterialCount() const override { return 9; } + Containers::Optional doMaterial(UnsignedInt) override { + return MaterialData{{}, {}}; + } + + UnsignedInt doTextureCount() const override { return 10; } + Containers::Optional doTexture(UnsignedInt) override { + return TextureData{TextureType::Texture1D, SamplerFilter::Nearest, SamplerFilter::Nearest, SamplerMipmap::Nearest, SamplerWrapping::ClampToEdge, 0}; + } + + UnsignedInt doImage1DCount() const override { return 11; } + UnsignedInt doImage1DLevelCount(UnsignedInt) override { + return 5; + } + Containers::Optional doImage1D(UnsignedInt, UnsignedInt) override { + return ImageData1D{PixelFormat::RGBA8Unorm, 1, DataFlags{}, "yes"}; + } + + UnsignedInt doImage2DCount() const override { return 12; } + UnsignedInt doImage2DLevelCount(UnsignedInt) override { + return 5; + } + Containers::Optional doImage2D(UnsignedInt, UnsignedInt) override { + return ImageData2D{PixelFormat::RGBA8Unorm, {1, 1}, DataFlags{}, "yes"}; + } + + UnsignedInt doImage3DCount() const override { return 13; } + UnsignedInt doImage3DLevelCount(UnsignedInt) override { + return 5; + } + Containers::Optional doImage3D(UnsignedInt, UnsignedInt) override { + return ImageData3D{PixelFormat::RGBA8Unorm, {1, 1, 1}, DataFlags{}, "yes"}; + } + } importer; + + struct Converter: AbstractSceneConverter { + explicit Converter(SceneConverterFeatures except): except{except} {} + + SceneConverterFeatures doFeatures() const override { + return ~except & + (SceneConverterFeature::ConvertMultipleToData| + SceneConverterFeature::AddScenes| + SceneConverterFeature::AddAnimations| + SceneConverterFeature::AddLights| + SceneConverterFeature::AddCameras| + SceneConverterFeature::AddSkins2D| + SceneConverterFeature::AddSkins3D| + SceneConverterFeature::AddMeshes| + SceneConverterFeature::AddMaterials| + SceneConverterFeature::AddTextures| + SceneConverterFeature::AddImages1D| + SceneConverterFeature::AddImages2D| + SceneConverterFeature::AddImages3D| + SceneConverterFeature::MeshLevels| + SceneConverterFeature::ImageLevels); + } + + bool doBeginData() override { return true; } + + bool doAdd(UnsignedInt, const SceneData&, Containers::StringView) override { + return true; + } + + bool doAdd(UnsignedInt, const AnimationData&, Containers::StringView) override { + return true; + } + bool doAdd(UnsignedInt, const LightData&, Containers::StringView) override { + return true; + } + bool doAdd(UnsignedInt, const CameraData&, Containers::StringView) override { + return true; + } + bool doAdd(UnsignedInt, const SkinData2D&, Containers::StringView) override { + return true; + } + bool doAdd(UnsignedInt, const SkinData3D&, Containers::StringView) override { + return true; + } + bool doAdd(UnsignedInt, Containers::Iterable, Containers::StringView) override { + return true; + } + bool doAdd(UnsignedInt, const MaterialData&, Containers::StringView) override { + return true; + } + bool doAdd(UnsignedInt, const TextureData&, Containers::StringView) override { + return true; + } + bool doAdd(UnsignedInt, Containers::Iterable, Containers::StringView) override { + return true; + } + bool doAdd(UnsignedInt, Containers::Iterable, Containers::StringView) override { + return true; + } + bool doAdd(UnsignedInt, Containers::Iterable, Containers::StringView) override { + return true; + } + + SceneConverterFeatures except; + } converter{data.exceptFeatures}; + + CORRADE_VERIFY(converter.beginData()); + + std::ostringstream out; + Warning redirectWarning{&out}; + CORRADE_VERIFY(converter.addSupportedImporterContents(importer, ~data.wantExceptContents)); + CORRADE_COMPARE(out.str(), Utility::formatString( + "Trade::AbstractSceneConverter::addSupportedImporterContents(): ignoring {} not supported by the converter\n", data.except)); + + /* All data except the one unsupported should be added */ + const SceneContents expectedConvertedExceptContents = data.exceptContents| data.wantExceptContents; + CORRADE_COMPARE(converter.sceneCount(), + expectedConvertedExceptContents & SceneContent::Scenes ? 0 : importer.sceneCount()); + CORRADE_COMPARE(converter.animationCount(), + expectedConvertedExceptContents & SceneContent::Animations ? 0 : importer.animationCount()); + CORRADE_COMPARE(converter.lightCount(), + expectedConvertedExceptContents & SceneContent::Lights ? 0 : importer.lightCount()); + CORRADE_COMPARE(converter.cameraCount(), + expectedConvertedExceptContents & SceneContent::Cameras ? 0 : importer.cameraCount()); + CORRADE_COMPARE(converter.skin2DCount(), + expectedConvertedExceptContents & SceneContent::Skins2D ? 0 : importer.skin2DCount()); + CORRADE_COMPARE(converter.skin3DCount(), + expectedConvertedExceptContents & SceneContent::Skins3D ? 0 : importer.skin3DCount()); + CORRADE_COMPARE(converter.meshCount(), + expectedConvertedExceptContents & SceneContent::Meshes ? 0 : importer.meshCount()); + CORRADE_COMPARE(converter.materialCount(), + expectedConvertedExceptContents & SceneContent::Materials ? 0 : importer.materialCount()); + CORRADE_COMPARE(converter.textureCount(), + expectedConvertedExceptContents & SceneContent::Textures ? 0 : importer.textureCount()); + CORRADE_COMPARE(converter.image1DCount(), + expectedConvertedExceptContents & SceneContent::Images1D ? 0 : importer.image1DCount()); + CORRADE_COMPARE(converter.image2DCount(), + expectedConvertedExceptContents & SceneContent::Images2D ? 0 : importer.image2DCount()); + CORRADE_COMPARE(converter.image3DCount(), + expectedConvertedExceptContents & SceneContent::Images3D ? 0 : importer.image3DCount()); +} + +void AbstractSceneConverterTest::addSupportedImporterContentsLevels() { + /* Similar to addImporterContentsNotSupportedLevels(), but not failing */ + + struct: AbstractImporter { + ImporterFeatures doFeatures() const override { return {}; } + bool doIsOpened() const override { return true; } + void doClose() override {} + + UnsignedInt doMeshCount() const override { return 4; } + UnsignedInt doMeshLevelCount(UnsignedInt id) override { + return id == 2 ? 5 : 1; + } + Containers::Optional doMesh(UnsignedInt, UnsignedInt) override { + return MeshData{{}, 0}; + } + + UnsignedInt doImage1DCount() const override { return 5; } + UnsignedInt doImage1DLevelCount(UnsignedInt id) override { + return id == 3 ? 6 : 1; + } + Containers::Optional doImage1D(UnsignedInt, UnsignedInt) override { + return ImageData1D{PixelFormat::RGBA8Unorm, 1, DataFlags{}, "yes"}; + } + + UnsignedInt doImage2DCount() const override { return 6; } + UnsignedInt doImage2DLevelCount(UnsignedInt id) override { + return id == 4 ? 7 : 1; + } + Containers::Optional doImage2D(UnsignedInt, UnsignedInt) override { + return ImageData2D{PixelFormat::RGBA8Unorm, {1, 1}, DataFlags{}, "yes"}; + } + + UnsignedInt doImage3DCount() const override { return 7; } + UnsignedInt doImage3DLevelCount(UnsignedInt id) override { + return id == 5 ? 8 : 1; + } + Containers::Optional doImage3D(UnsignedInt, UnsignedInt) override { + return ImageData3D{PixelFormat::RGBA8Unorm, {1, 1, 1}, DataFlags{}, "yes"}; + } + } importer; + + struct: AbstractSceneConverter { + SceneConverterFeatures doFeatures() const override { + return + SceneConverterFeature::ConvertMultipleToData| + SceneConverterFeature::AddMeshes| + SceneConverterFeature::AddImages1D| + SceneConverterFeature::AddImages2D| + SceneConverterFeature::AddImages3D; + } + + bool doBeginData() override { return true; } + + bool doAdd(UnsignedInt, const MeshData&, Containers::StringView) override { + return true; + } + bool doAdd(UnsignedInt, const ImageData1D&, Containers::StringView) override { + return true; + } + bool doAdd(UnsignedInt, const ImageData2D&, Containers::StringView) override { + return true; + } + bool doAdd(UnsignedInt, const ImageData3D&, Containers::StringView) override { + return true; + } + + SceneConverterFeatures except; + } converter; + + CORRADE_VERIFY(converter.beginData()); + + std::ostringstream out; + Warning redirectWarning{&out}; + CORRADE_VERIFY(converter.addSupportedImporterContents(importer)); + CORRADE_COMPARE(out.str(), + "Trade::AbstractSceneConverter::addSupportedImporterContents(): ignoring extra 4 levels of mesh 2 not supported by the converter\n" + "Trade::AbstractSceneConverter::addSupportedImporterContents(): ignoring extra 5 levels of 1D image 3 not supported by the converter\n" + "Trade::AbstractSceneConverter::addSupportedImporterContents(): ignoring extra 6 levels of 2D image 4 not supported by the converter\n" + "Trade::AbstractSceneConverter::addSupportedImporterContents(): ignoring extra 7 levels of 3D image 5 not supported by the converter\n"); + + /* All data should be added, just not the extra levels */ + CORRADE_COMPARE(converter.meshCount(), importer.meshCount()); + CORRADE_COMPARE(converter.image1DCount(), importer.image1DCount()); + CORRADE_COMPARE(converter.image2DCount(), importer.image2DCount()); + CORRADE_COMPARE(converter.image3DCount(), importer.image3DCount()); +} + +void AbstractSceneConverterTest::debugFeature() { + std::ostringstream out; + + Debug{&out} << SceneConverterFeature::ConvertMeshInPlace << SceneConverterFeature(0xdeaddead); + CORRADE_COMPARE(out.str(), "Trade::SceneConverterFeature::ConvertMeshInPlace Trade::SceneConverterFeature(0xdeaddead)\n"); +} + +void AbstractSceneConverterTest::debugFeaturePacked() { + std::ostringstream out; + /* Last is not packed, ones before should not make any flags persistent */ + Debug{&out} << Debug::packed << SceneConverterFeature::ConvertMeshInPlace << Debug::packed << SceneConverterFeature(0xdeaddead) << SceneConverterFeature::AddCameras; + CORRADE_COMPARE(out.str(), "ConvertMeshInPlace 0xdeaddead Trade::SceneConverterFeature::AddCameras\n"); +} + +void AbstractSceneConverterTest::debugFeatures() { + std::ostringstream out; + + Debug{&out} << (SceneConverterFeature::ConvertMesh|SceneConverterFeature::ConvertMeshToFile) << SceneConverterFeatures{}; + CORRADE_COMPARE(out.str(), "Trade::SceneConverterFeature::ConvertMesh|Trade::SceneConverterFeature::ConvertMeshToFile Trade::SceneConverterFeatures{}\n"); +} + +void AbstractSceneConverterTest::debugFeaturesPacked() { + std::ostringstream out; + /* Last is not packed, ones before should not make any flags persistent */ + Debug{&out} << Debug::packed << (SceneConverterFeature::ConvertMesh|SceneConverterFeature::ConvertMeshToFile) << Debug::packed << SceneConverterFeatures{} << SceneConverterFeature::AddLights; + CORRADE_COMPARE(out.str(), "ConvertMesh|ConvertMeshToFile {} Trade::SceneConverterFeature::AddLights\n"); +} + +void AbstractSceneConverterTest::debugFeaturesSupersets() { + /* ConvertMeshToData is a superset of ConvertMeshToFile, so only one should + be printed */ + { + std::ostringstream out; + Debug{&out} << (SceneConverterFeature::ConvertMeshToData|SceneConverterFeature::ConvertMeshToFile); + CORRADE_COMPARE(out.str(), "Trade::SceneConverterFeature::ConvertMeshToData\n"); + + /* ConvertMultipleToData is a superset of ConvertMultipleToFile, so only + one should be printed */ + } { + std::ostringstream out; + Debug{&out} << (SceneConverterFeature::ConvertMultipleToData|SceneConverterFeature::ConvertMultipleToFile); + CORRADE_COMPARE(out.str(), "Trade::SceneConverterFeature::ConvertMultipleToData\n"); + } +} + +void AbstractSceneConverterTest::debugFlag() { + std::ostringstream out; + + Debug{&out} << SceneConverterFlag::Verbose << SceneConverterFlag(0xf0); + CORRADE_COMPARE(out.str(), "Trade::SceneConverterFlag::Verbose Trade::SceneConverterFlag(0xf0)\n"); +} + +void AbstractSceneConverterTest::debugFlags() { + std::ostringstream out; + + Debug{&out} << (SceneConverterFlag::Verbose|SceneConverterFlag(0xf0)) << SceneConverterFlags{}; + CORRADE_COMPARE(out.str(), "Trade::SceneConverterFlag::Verbose|Trade::SceneConverterFlag(0xf0) Trade::SceneConverterFlags{}\n"); +} + +void AbstractSceneConverterTest::debugContent() { + std::ostringstream out; + + Debug{&out} << SceneContent::Skins3D << SceneContent(0xdeaddead); + CORRADE_COMPARE(out.str(), "Trade::SceneContent::Skins3D Trade::SceneContent(0xdeaddead)\n"); +} + +void AbstractSceneConverterTest::debugContentPacked() { + std::ostringstream out; + /* Last is not packed, ones before should not make any flags persistent */ + Debug{&out} << Debug::packed << SceneContent::Animations << Debug::packed << SceneContent(0xdeaddead) << SceneContent::Cameras; + CORRADE_COMPARE(out.str(), "Animations 0xdeaddead Trade::SceneContent::Cameras\n"); +} + +void AbstractSceneConverterTest::debugContents() { + std::ostringstream out; + + Debug{&out} << (SceneContent::Animations|SceneContent::MeshLevels) << SceneConverterFeatures{}; + CORRADE_COMPARE(out.str(), "Trade::SceneContent::Animations|Trade::SceneContent::MeshLevels Trade::SceneConverterFeatures{}\n"); +} + +void AbstractSceneConverterTest::debugContentsPacked() { + std::ostringstream out; + /* Last is not packed, ones before should not make any flags persistent */ + Debug{&out} << Debug::packed << (SceneContent::Animations|SceneContent::MeshLevels) << Debug::packed << SceneConverterFeatures{} << SceneContent::Lights; + CORRADE_COMPARE(out.str(), "Animations|MeshLevels {} Trade::SceneContent::Lights\n"); } }}}}