Browse Source

Trade: ability to feed the whole importer into a scene converter.

Makes a lot of use cases significantly simpler -- apart from the trivial
"convert an OBJ to a glTF" scenario, many processing steps are about
passing most data through but only doing a pass on meshes, or images, or
materials. And this simplifies that use case quite a lot.

This is only "driver" code, with no new interfaces for the plugins. For
them it still looks like all data, their names and related metadata were
added one by one.

Also, a suprisingly large amount of code for this feature.
pull/594/head
Vladimír Vondruš 4 years ago
parent
commit
0a2148b93f
  1. 494
      src/Magnum/Trade/AbstractSceneConverter.cpp
  2. 277
      src/Magnum/Trade/AbstractSceneConverter.h
  3. 2049
      src/Magnum/Trade/Test/AbstractSceneConverterTest.cpp

494
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<UnsignedInt> 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<Trade::MeshData> 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<Trade::MeshData> 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<Trade::ImageData1D> 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<Trade::ImageData1D> 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<Trade::ImageData2D> 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<Trade::ImageData2D> 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<Trade::ImageData3D> 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<Trade::ImageData3D> 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<Trade::TextureData> 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<Trade::MaterialData> 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<Trade::LightData> 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<Trade::CameraData> 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<Trade::SceneData> 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<Trade::SkinData2D> 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<Trade::SkinData3D> 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<Trade::AnimationData> 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<void*>(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});
}
}}

277
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<const MeshData>, 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<const ImageData1D>, Containers::StringView) /
* @ref AbstractSceneConverter::add(Containers::Iterable<const ImageData2D>, Containers::StringView) "add(Containers::Iterable<const ImageData2D>, Containers::StringView)" /
* @ref AbstractSceneConverter::add(Containers::Iterable<const ImageData3D>, Containers::StringView) "add(Containers::Iterable<const ImageData3D>, 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<SceneContent> 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<UnsignedInt> add(Containers::Iterable<const CompressedImageView3D> 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<const ImageData3D> 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> _state;
};

2049
src/Magnum/Trade/Test/AbstractSceneConverterTest.cpp

File diff suppressed because it is too large Load Diff
Loading…
Cancel
Save