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"); } }}}}