From 4a06f4d0485ffc91ba99b1190e3f899875908654 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Vladim=C3=ADr=20Vondru=C5=A1?= Date: Sun, 18 Jul 2021 18:13:40 +0200 Subject: [PATCH] Trade: improve docs for the main importer / converter plugins. Interestingly enough, there were no docs whatsoever for image and scene conversion, and neither it was mentioned what all scene data can be imported. Sigh. --- doc/snippets/MagnumTrade.cpp | 64 ++++++++ src/Magnum/Trade/AbstractImageConverter.h | 90 ++++++++++- src/Magnum/Trade/AbstractImporter.h | 183 ++++++++++++++++++---- src/Magnum/Trade/AbstractSceneConverter.h | 85 +++++++++- 4 files changed, 382 insertions(+), 40 deletions(-) diff --git a/doc/snippets/MagnumTrade.cpp b/doc/snippets/MagnumTrade.cpp index e101b52f5..3b76af343 100644 --- a/doc/snippets/MagnumTrade.cpp +++ b/doc/snippets/MagnumTrade.cpp @@ -29,6 +29,7 @@ #include #include "Magnum/FileCallback.h" +#include "Magnum/Image.h" #include "Magnum/ImageView.h" #include "Magnum/Mesh.h" #include "Magnum/PixelFormat.h" @@ -37,7 +38,9 @@ #include "Magnum/Math/Swizzle.h" #include "Magnum/MeshTools/Interleave.h" #include "Magnum/MeshTools/Transform.h" +#include "Magnum/Trade/AbstractImageConverter.h" #include "Magnum/Trade/AbstractImporter.h" +#include "Magnum/Trade/AbstractSceneConverter.h" #include "Magnum/Trade/AnimationData.h" #include "Magnum/Trade/ImageData.h" #include "Magnum/Trade/LightData.h" @@ -74,6 +77,33 @@ using namespace Magnum::Math::Literals; int main() { +{ +Vector2i size; +/* [AbstractImageConverter-usage-file] */ +PluginManager::Manager manager; +Containers::Pointer converter = + manager.loadAndInstantiate("AnyImageConverter"); + +Image2D image{PixelFormat::RGBA8Unorm, size, DOXYGEN_IGNORE({})}; +if(!converter || !converter->convertToFile(image, "image.png")) + Fatal{} << "Can't save image.png with AnyImageConverter"; +/* [AbstractImageConverter-usage-file] */ +} + +{ +Image2D image{{}, {}, {}}; +/* [AbstractImageConverter-usage-image] */ +PluginManager::Manager manager; +Containers::Pointer converter = + manager.loadAndInstantiate("StbDxtImageConverter"); + +Containers::Optional compressed; +if(!converter || !(compressed = converter->convert(image))) + Fatal{} << "Can't convert the image with StbDxtImageConverter"; +CORRADE_INTERNAL_ASSERT(compressed->isCompressed()); +/* [AbstractImageConverter-usage-image] */ +} + { /* [AbstractImporter-usage] */ PluginManager::Manager manager; @@ -159,6 +189,40 @@ importer->setFileCallback([](const std::string& filename, /* [AbstractImporter-setFileCallback-template] */ } +{ +/* [AbstractSceneConverter-usage-file] */ +PluginManager::Manager manager; +Containers::Pointer converter = + manager.loadAndInstantiate("AnySceneConverter"); + +Trade::MeshData mesh = DOXYGEN_IGNORE(Trade::MeshData{{}, {}}); +if(!converter || !converter->convertToFile(mesh, "mesh.ply")) + Fatal{} << "Can't save mesh.ply with AnySceneConverter"; +/* [AbstractSceneConverter-usage-file] */ +} + +{ +Trade::MeshData mesh{{}, {}}; +/* [AbstractSceneConverter-usage-mesh] */ +PluginManager::Manager manager; +Containers::Pointer converter = + manager.loadAndInstantiate("MeshOptimizerSceneConverter"); + +Containers::Optional optimized; +if(!converter || !(optimized = converter->convert(mesh))) + Fatal{} << "Can't optimize the mesh with MeshOptimizerSceneConverter"; +/* [AbstractSceneConverter-usage-mesh] */ +} + +{ +Trade::MeshData mesh{{}, {}}; +Containers::Pointer converter; +/* [AbstractSceneConverter-usage-mesh-in-place] */ +if(!converter || !converter->convertInPlace(mesh)) + Fatal{} << "Can't optimize the mesh with MeshOptimizerSceneConverter"; +/* [AbstractSceneConverter-usage-mesh-in-place] */ +} + { UnsignedInt id{}; Containers::Pointer importer; diff --git a/src/Magnum/Trade/AbstractImageConverter.h b/src/Magnum/Trade/AbstractImageConverter.h index 0e4ad45a2..6a5c621c6 100644 --- a/src/Magnum/Trade/AbstractImageConverter.h +++ b/src/Magnum/Trade/AbstractImageConverter.h @@ -294,9 +294,72 @@ MAGNUM_TRADE_EXPORT Debug& operator<<(Debug& debug, ImageConverterFlags value); /** @brief Base for image converter plugins -Provides functionality for converting images between various internal formats -or compressing them. See @ref plugins for more information and `*ImageConverter` -classes in @ref Trade namespace for available image converter plugins. +Provides functionality for converting images between various formats, +compressing them or saving to files. + +The interface supports two main kinds of operation, with implementations +commonly advertising support for either one or the other via @ref features(): + +- Saving a (compressed) 1D/2D/3D image to a file / data using + @ref convertToFile() / @ref convertToData(). This is mostly for exporting + the image data to a common format like JPEG or PNG in order to be used with + an external tool. Advertised with + @ref ImageConverterFeature::Convert1DToFile / + @relativeref{ImageConverterFeature,Convert2DToFile} / + @relativeref{ImageConverterFeature,Convert3DToFile} or + @ref ImageConverterFeature::Convert1DToData / + @relativeref{ImageConverterFeature,Convert2DToData} / + @relativeref{ImageConverterFeature,Convert3DToData} and + @ref ImageConverterFeature::ConvertCompressed1DToFile / + @relativeref{ImageConverterFeature,ConvertCompressed2DToFile} / + @relativeref{ImageConverterFeature,ConvertCompressed3DToFile} or + @ref ImageConverterFeature::ConvertCompressed1DToData + @relativeref{ImageConverterFeature,ConvertCompressed2DToData} / + @relativeref{ImageConverterFeature,ConvertCompressed3DToData} for + compressed input images. +- Performing an operation on the image data itself using @ref convert(), from + which you get an @ref ImageData back again. This includes operations like + pixel format conversion or for example resampling. Advertised with + @ref ImageConverterFeature::Convert1D / + @relativeref{ImageConverterFeature,Convert2D} / + @relativeref{ImageConverterFeature,Convert3D} and + @ref ImageConverterFeature::ConvertCompressed1D / + @relativeref{ImageConverterFeature,ConvertCompressed2D} / + @relativeref{ImageConverterFeature,ConvertCompressed3D} for compressed + input images. + +@section Trade-AbstractImageConverter-usage Usage + +Image converters are commonly implemented as plugins, which means the concrete +converter implementation is loaded and instantiated through a +@relativeref{Corrade,PluginManager::Manager}. Then, based on the intent and on +what the particular converter supports, @ref convertToFile(), +@ref convertToData() or @ref convert() gets called. + +As each converter has different requirements and supports different pixel +formats, you're expected to perform error handling on the application side --- +if a conversion fails, you get an empty +@relativeref{Corrade,Containers::Optional} / +@relativeref{Corrade,Containers::Array} or @cpp false @ce and a reason printed +to the error output. Everything else (using a feature not implemented in the +converter, ...) is treated as a programmer error and will produce the usual +assertions. + +@subsection Trade-AbstractImageConverter-usage-file Saving an image to a file + +In the following example an 8-bit RGBA image is saved as a PNG using the +@ref AnyImageConverter plugin, together with all needed error handling. In this +case we *know* that @ref AnyImageConverter supports +@ref ImageConverterFeature::Convert2DToFile, however in a more general case and +especially when dealing with compressed image formats it might be good to check +against the reported @ref features() first. + +@snippet MagnumTrade.cpp AbstractImageConverter-usage-file + +See @ref plugins for more information about general plugin usage, +@ref file-formats to compare implementations of common file formats and the +list of @m_class{m-doc} [derived classes](#derived-classes) for all available +image converter plugins. @m_class{m-note m-success} @@ -305,6 +368,25 @@ classes in @ref Trade namespace for available image converter plugins. exposing functionality of all image converter plugins on a command line as well as performing introspection of image files. +@subsection Trade-AbstractImageConverter-usage-image Converting image data + +In the following snippet we use @ref StbDxtImageConverter to convert the same +8-bit RGBA image as above to a block-compressed one with +@ref CompressedPixelFormat::Bc3RGBAUnorm. While @ref AnyImageConverter can +detect the desired format when writing to a file and act accordingly, here it +would have no way to know what we want and so we request the concrete plugin +name directly. Here we again know that @ref StbDxtImageConverter gives us back +a compressed image and so we can put in just a sanity assert, but in the +general case it's converter-dependent and may even rely on configuration +options set for the plugin. + +@snippet MagnumTrade.cpp AbstractImageConverter-usage-image + +Commonly, when operating directly on the image data, each plugin exposes a set +of configuration options to specify what actually gets done and how, and the +default setup may not even do anything. See @ref plugins-configuration for +details and a usage example. + @section Trade-AbstractImageConverter-data-dependency Data dependency The instances returned from various functions *by design* have no dependency on @@ -349,7 +431,7 @@ checked by the implementation: @par Dangling function pointers on plugin unload As @ref Trade-AbstractImageConverter-data-dependency "mentioned above", - @ref Corrade::Containers::Array instances returned from plugin + @relativeref{Corrade,Containers::Array} instances returned from plugin implementations are not allowed to use anything else than the default deleter, otherwise this could cause dangling function pointer call on array destruction if the plugin gets unloaded before the array is destroyed. This diff --git a/src/Magnum/Trade/AbstractImporter.h b/src/Magnum/Trade/AbstractImporter.h index 310143e79..93c350a5c 100644 --- a/src/Magnum/Trade/AbstractImporter.h +++ b/src/Magnum/Trade/AbstractImporter.h @@ -149,19 +149,82 @@ template struct OptionalButAlsoPointer: Containers::Optional { /** @brief Base for importer plugins -Provides interface for importing 2D/3D scene, camera, light, animation, mesh, -material, texture and image data. +Provides interface for importing generic scene data such as images, meshes or +animations. + +A scene file can generally have an arbitrary amount of data of particular kind +and so the importer provides a set of @cpp thing(id) @ce accessors, where the +ID is from @cpp 0 @ce to @cpp thingCount() - 1 @ce. Certain kinds of data +can also reference each other via the ID (for example a material specifies the +ID of a texture it uses). The following kinds of data can be imported: + +- @ref AnimationData using @ref animation(UnsignedInt) up to + @ref animationCount(). Each animation then references the 2D/3D object + transformation it affects via its ID. +- @ref CameraData using @ref camera(UnsignedInt) up to @ref cameraCount() +- @ref ImageData1D / @ref ImageData2D / @ref ImageData3D using + @ref image1D(UnsignedInt, UnsignedInt) / + @ref image2D(UnsignedInt, UnsignedInt) / + @ref image3D(UnsignedInt, UnsignedInt) up to @ref image1DCount() / + @ref image2DCount() / @ref image3DCount(). Each image can also have + multiple (mip) levels which are requested through the second parameter up + to @ref image1DLevelCount() / @ref image2DLevelCount() / + @ref image3DLevelCount(). +- @ref LightData using @ref light(UnsignedInt) up to @ref lightCount() +- @ref MaterialData using @ref material(UnsignedInt) up to + @ref materialCount(). A material can then reference textures via their IDs. +- @ref MeshData using @ref mesh(UnsignedInt, UnsignedInt) up to + @ref meshCount(). Similarly as with images, each mesh can also have + multiple levels (LODs or for example separate edge/face data), which are + requested through the second parameter up to @ref meshLevelCount(). +- @ref ObjectData2D / @ref ObjectData3D using + @ref object2D(UnsignedInt) / @ref object3D(UnsignedInt) up to + @ref object2DCount() / @ref object3DCount(). An object can then reference + its child objects, mesh and a material, camera, light or a skin associated + with it via their IDs. +- @ref SceneData using @ref scene(UnsignedInt) up to @ref sceneCount(), with + the default scene index exposed through @ref defaultScene(). A scene then + references its child 2D/3D objects via their IDs. +- @ref SkinData2D / @ref SkinData3D using @ref skin2D(UnsignedInt) / + @ref skin3D(UnsignedInt) up to @ref skin2DCount() / @ref skin3DCount() +- @ref TextureData using @ref texture(UnsignedInt) up to @ref textureCount(). + Each texture then references the 1D/2D/3D image it uses via its ID. + +Except for pure image formats that always have at least one image, in general +there's no guarantee that an imported file has always a mesh, a scene or a +camera. So unless the particular importer documentation says otherwise, you're +expected to always check the count before attempting an import. @section Trade-AbstractImporter-usage Usage -Importers are most commonly implemented as plugins. For example, loading an -image from the filesystem using the @ref AnyImageImporter plugin can be done -like this, completely with all error handling: +Importers are commonly implemented as plugins, which means the concrete +importer implementation is loaded and instantiated through a @relativeref{Corrade,PluginManager::Manager}. A file is opened using either +@ref openFile(), @ref openData() or, in rare cases, @ref openState() amd it +stays open until the importer is destroyed, @ref close() is called or another +file is opened. + +With a file open you can then query the importer for particular data. Where +possible, the import is performed lazily only when you actually request that +particular data, and thus it can be assumed that opening a file is relatively +cheap. + +Since the importers deal with untrusted external data, it's needed to perform +explicit error handling on the application side. There are two cases where it +can fail --- during opening, in which case the function returns @cpp false @ce, +and during the actual data import, in which case you get an empty @relativeref{Corrade,Containers::Optional}. In both cases the actual failure +reason is printed to the error output. Everything else (IDs out of bounds, +calling functions without a file open, accessing an empty optional, ...) is +treated as a programmer error and will produce the usual assertions. + +In the following example an image is loaded from the filesystem using the +@ref AnyImageImporter plugin, completely with all needed error handling: @snippet MagnumTrade.cpp AbstractImporter-usage -See @ref plugins for more information about general plugin usage and -`*Importer` classes in the @ref Trade namespace for available importer plugins. +See @ref plugins for more information about general plugin usage, +@ref file-formats to compare implementations of common file formats and the +list of @m_class{m-doc} [derived classes](#derived-classes) for all available +importer plugins. @m_class{m-note m-success} @@ -170,6 +233,46 @@ See @ref plugins for more information about general plugin usage and @ref magnum-sceneconverter "magnum-sceneconverter" tools which you can use to perform introspection of image and scene files. +@subsection Trade-AbstractImporter-usage-name-mapping Mapping between IDs and string names + +Certain file formats have the ability to assign string names to objects, +materials and other parts of the scene. These are not imported as part of the +various @ref Trade::MeshData etc. structures, instead the importer itself +allows you to map IDs to names and vice versa. As a convenience feature, it's +also possible to import data directly using their name instead of having to map +the name to an IDs first. In that case the function will return an empty +@relativeref{Corrade,Containers::Optional} both if the import failed or if the +name doesn't exist. + +- Animation names can be retrieved using @ref animationName() and mapped to + an ID using @ref animationForName(), imported with @ref animation(const std::string&) +- Camera names using @ref cameraName() & @ref cameraForName(), imported with + @ref camera(const std::string&) +- Image names using @ref image1DName() / @ref image2DName() / + @ref image3DName() & @ref image1DForName() / @ref image2DForName() / + @ref image3DForName(), imported with + @ref image1D(const std::string&, UnsignedInt) / + @ref image2D(const std::string&, UnsignedInt) / + @ref image3D(const std::string&, UnsignedInt) +- Light names using @ref lightName() & @ref lightForName(), imported with + @ref light(const std::string&) +- Material names using @ref materialName() & @ref materialForName(), imported + with @ref material(const std::string&) +- Mesh names using @ref meshName() & @ref meshForName(), imported with + @ref mesh(const std::string&, UnsignedInt). Meshes themselves can have + custom attributes, for which the name mapping can be retrieved using + @ref meshAttributeName() and @ref meshAttributeForName(). +- Objects names using @ref object2DName() / @ref object3DName() & + @ref object2DForName() / @ref object3DForName(), imported with + @ref object2D(const std::string&) / @ref object3D(const std::string&) +- Scene names using @ref sceneName() & @ref sceneForName(), imported with + @ref scene(const std::string&) +- Skin names using @ref skin2DName() / @ref skin3DName() & + @ref skin2DForName() / @ref skin3DForName(), imported with + @ref skin2D(const std::string&) / @ref skin3D(const std::string&) +- Texture names using @ref textureName() & @ref textureForName(), imported + with @ref texture(const std::string&) + @subsection Trade-AbstractImporter-usage-callbacks Loading data from memory, using file callbacks Besides loading data directly from the filesystem using @ref openFile() like @@ -239,17 +342,17 @@ details about concrete types returned and accepted by these functions. @subsection Trade-AbstractImporter-usage-casting Polymorphic imported data types -Some data access functions return @ref Corrade::Containers::Pointer instead of -@ref Corrade::Containers::Optional because the result might be a particular -subclass of given type. Those functions are @ref material(), @ref object2D() +Some data access functions return @relativeref{Corrade,Containers::Pointer} +instead of @relativeref{Corrade,Containers::Optional} because the result might +be a particular subclass of given type. Those functions are @ref object2D() and @ref object3D(). You can cast the abstract base to a concrete type depending on its reported type, for example: @snippet MagnumTrade.cpp AbstractImporter-usage-cast Another option is making use of the @ref Containers::pointerCast() utility, but -note that in that case the original @ref Corrade::Containers::Pointer will be -* *moved into* a new instance and that might not be desirable. +note that in that case the original @relativeref{Corrade,Containers::Pointer} +will have to be *moved into* a new instance, which might not be desirable. @section Trade-AbstractImporter-data-dependency Data dependency @@ -257,12 +360,12 @@ The `*Data` instances returned from various functions *by design* have no dependency on the importer instance and neither on the dynamic plugin module. In other words, you don't need to keep the importer instance (or the plugin manager instance) around in order to have the `*Data` instances valid. -Moreover, all @ref Corrade::Containers::Array instances returned through -@ref ImageData, @ref AnimationData, @ref MaterialData, @ref MeshData and -@ref SkinData are only allowed to have default deleters (or be non-owning -instances created from @ref Corrade::Containers::ArrayView) --- this is to -avoid potential dangling function pointer calls when destructing such instances -after the plugin module has been unloaded. +Moreover, all @relativeref{Corrade,Containers::Array} instances returned +through @ref ImageData, @ref AnimationData, @ref MaterialData, @ref MeshData +and @ref SkinData are only allowed to have default deleters (or be non-owning +instances created from @relativeref{Corrade,Containers::ArrayView}) --- this is +to avoid potential dangling function pointer calls when destructing such +instances after the plugin module has been unloaded. The only exception are various `importerState()` functions @ref Trade-AbstractImporter-usage-state "described above", but in that case the @@ -317,7 +420,7 @@ checked by the implementation: @par Dangling function pointers on plugin unload As @ref Trade-AbstractImporter-data-dependency "mentioned above", - @ref Corrade::Containers::Array instances returned from plugin + @relativeref{Corrade,Containers::Array} instances returned from plugin implementations are not allowed to use anything else than the default deleter or the deleter used by @ref Trade::ArrayAllocator, otherwise this could cause dangling function pointer call on array destruction if the @@ -602,7 +705,8 @@ class MAGNUM_TRADE_EXPORT AbstractImporter: public PluginManager::AbstractManagi * @brief Scene name * @param id Scene ID, from range [0, @ref sceneCount()). * - * Expects that a file is opened. + * Expects that a file is opened. If the scene has no name or the + * importer doesn't support scene names, returns an empty string. * @see @ref sceneForName() */ std::string sceneName(UnsignedInt id); @@ -649,7 +753,8 @@ class MAGNUM_TRADE_EXPORT AbstractImporter: public PluginManager::AbstractManagi * @brief Animation name * @param id Animation ID, from range [0, @ref animationCount()). * - * Expects that a file is opened. + * Expects that a file is opened. If the animation has no name or the + * importer doesn't support animation names, returns an empty string. * @see @ref animationForName() */ std::string animationName(UnsignedInt id); @@ -696,7 +801,8 @@ class MAGNUM_TRADE_EXPORT AbstractImporter: public PluginManager::AbstractManagi * @brief Light name * @param id Light ID, from range [0, @ref lightCount()). * - * Expects that a file is opened. + * Expects that a file is opened. If the light has no name or the + * importer doesn't support light names, returns an empty string. * @see @ref lightForName() */ std::string lightName(UnsignedInt id); @@ -743,7 +849,8 @@ class MAGNUM_TRADE_EXPORT AbstractImporter: public PluginManager::AbstractManagi * @brief Camera name * @param id Camera ID, from range [0, @ref cameraCount()). * - * Expects that a file is opened. + * Expects that a file is opened. If the camera has no name or the + * importer doesn't support camera names, returns an empty string. * @see @ref cameraForName() */ std::string cameraName(UnsignedInt id); @@ -790,7 +897,8 @@ class MAGNUM_TRADE_EXPORT AbstractImporter: public PluginManager::AbstractManagi * @brief Two-dimensional object name * @param id Object ID, from range [0, @ref object2DCount()). * - * Expects that a file is opened. + * Expects that a file is opened. If the object has no name or the + * importer doesn't support object names, returns an empty string. * @see @ref object2DForName() */ std::string object2DName(UnsignedInt id); @@ -837,7 +945,8 @@ class MAGNUM_TRADE_EXPORT AbstractImporter: public PluginManager::AbstractManagi * @brief Three-dimensional object name * @param id Object ID, from range [0, @ref object3DCount()). * - * Expects that a file is opened. + * Expects that a file is opened. If the object has no name or the + * importer doesn't support object names, returns an empty string. * @see @ref object3DForName() */ std::string object3DName(UnsignedInt id); @@ -887,7 +996,8 @@ class MAGNUM_TRADE_EXPORT AbstractImporter: public PluginManager::AbstractManagi * @param id Skin ID, from range [0, @ref skin2DCount()). * @m_since_latest * - * Expects that a file is opened. + * Expects that a file is opened. If the skin has no name or the + * importer doesn't support skin names, returns an empty string. * @see @ref skin2DForName() */ std::string skin2DName(UnsignedInt id); @@ -938,7 +1048,8 @@ class MAGNUM_TRADE_EXPORT AbstractImporter: public PluginManager::AbstractManagi * @param id Skin ID, from range [0, @ref skin3DCount()). * @m_since_latest * - * Expects that a file is opened. + * Expects that a file is opened. If the skin has no name or the + * importer doesn't support skin names, returns an empty string. * @see @ref skin3DForName() */ std::string skin3DName(UnsignedInt id); @@ -1000,7 +1111,8 @@ class MAGNUM_TRADE_EXPORT AbstractImporter: public PluginManager::AbstractManagi * @param id Mesh ID, from range [0, @ref meshCount()). * @m_since{2020,06} * - * Expects that a file is opened. + * Expects that a file is opened. If the mesh has no name or the + * importer doesn't support mesh names, returns an empty string. * @see @ref meshForName() */ std::string meshName(UnsignedInt id); @@ -1164,7 +1276,8 @@ class MAGNUM_TRADE_EXPORT AbstractImporter: public PluginManager::AbstractManagi * @brief Material name * @param id Material ID, from range [0, @ref materialCount()). * - * Expects that a file is opened. + * Expects that a file is opened. If the material has no name or the + * importer doesn't support material names, returns an empty string. * @see @ref materialForName(), @ref material(const std::string&) */ std::string materialName(UnsignedInt id); @@ -1221,7 +1334,8 @@ class MAGNUM_TRADE_EXPORT AbstractImporter: public PluginManager::AbstractManagi * @brief Texture name * @param id Texture ID, from range [0, @ref textureCount()). * - * Expects that a file is opened. + * Expects that a file is opened. If the texture has no name or the + * importer doesn't support texture names, returns an empty string. * @see @ref textureForName() */ std::string textureName(UnsignedInt id); @@ -1279,7 +1393,8 @@ class MAGNUM_TRADE_EXPORT AbstractImporter: public PluginManager::AbstractManagi * @brief One-dimensional image name * @param id Image ID, from range [0, @ref image1DCount()). * - * Expects that a file is opened. + * Expects that a file is opened. If the image has no name or the + * importer doesn't support image names, returns an empty string. * @see @ref image1DForName() */ std::string image1DName(UnsignedInt id); @@ -1339,7 +1454,8 @@ class MAGNUM_TRADE_EXPORT AbstractImporter: public PluginManager::AbstractManagi * @brief Two-dimensional image name * @param id Image ID, from range [0, @ref image2DCount()). * - * Expects that a file is opened. + * Expects that a file is opened. If the image has no name or the + * importer doesn't support image names, returns an empty string. * @see @ref image2DForName() */ std::string image2DName(UnsignedInt id); @@ -1399,7 +1515,8 @@ class MAGNUM_TRADE_EXPORT AbstractImporter: public PluginManager::AbstractManagi * @brief Three-dimensional image name * @param id Image ID, from range [0, @ref image3DCount()). * - * Expects that a file is opened. + * Expects that a file is opened. If the image has no name or the + * importer doesn't support image names, returns an empty string. * @see @ref image3DForName() */ std::string image3DName(UnsignedInt id); diff --git a/src/Magnum/Trade/AbstractSceneConverter.h b/src/Magnum/Trade/AbstractSceneConverter.h index c1f13c7f7..4022a0019 100644 --- a/src/Magnum/Trade/AbstractSceneConverter.h +++ b/src/Magnum/Trade/AbstractSceneConverter.h @@ -134,9 +134,58 @@ MAGNUM_TRADE_EXPORT Debug& operator<<(Debug& debug, SceneConverterFlags value); @m_since{2020,06} Provides functionality for converting meshes and other scene data between -various formats or performing optimizations and other operations on them. See -@ref plugins for more information and `*SceneConverter` classes in the -@ref Trade namespace for available scene converter plugins. +various formats or performing optimizations and other operations on them. + +The interface supports three main kinds of operation, with implementations +advertising support for a subset of them via @ref features(): + +- Saving a mesh to a file / data using + @ref convertToFile(const MeshData&, Containers::StringView) / + @ref convertToData(const MeshData&). This is mostly for exporting the mesh + data to a common format like OBJ or PLY in order to be used with an + external tool. Advertised with @ref SceneConverterFeature::ConvertMeshToFile + or @ref SceneConverterFeature::ConvertMeshToData +- Performing an operation on the mesh data itself using + @ref convert(const MeshData&), from which you get a @ref MeshData again. + This includes operations like mesh decimation or topology cleanup. + Advertised with @ref SceneConverterFeature::ConvertMesh. +- Performing an operation on the mesh data *in place* using + @ref convertInPlace(MeshData&). This is for operations like vertex cache + optimization that don't need to change the mesh topology, only modify or + shuffle the data around. Advertised with + @ref SceneConverterFeature::ConvertMeshInPlace. + +@section Trade-AbstractSceneConverter-usage Usage + +Scene converters are commonly implemented as plugins, which means the concrete +converter implementation is loaded and instantiated through a +@relativeref{Corrade,PluginManager::Manager}. Then, based on the intent and on +what the particular converter supports, @ref convertToFile(), +@ref convertToData(), @ref convert() or @ref convertInPlace() gets called. + +As each converter has different requirements on the input data layout and +vertex formats, you're expected to perform error handling on the application +side --- if a conversion fails, you get an empty +@relativeref{Corrade,Containers::Optional} / +@relativeref{Corrade,Containers::Array} or @cpp false @ce and a reason printed +to the error output. Everything else (using a feature not implemented in the +converter, ...) is treated as a programmer error and will produce the usual +assertions. + +@subsection Trade-AbstractSceneConverter-usage-file Saving a mesh to a file + +In the following example a mesh is saved to a PLY file using the +@ref AnySceneConverter plugin, together with all needed error handling. In this +case we *know* that @ref AnySceneConverter supports +@ref SceneConverterFeature::ConvertMeshToFile, however in a more general case +it might be good to check against the reported @ref features() first. + +@snippet MagnumTrade.cpp AbstractSceneConverter-usage-file + +See @ref plugins for more information about general plugin usage, +@ref file-formats to compare implementations of common file formats and the +list of @m_class{m-doc} [derived classes](#derived-classes) for all available +scene converter plugins. @m_class{m-note m-success} @@ -145,6 +194,36 @@ various formats or performing optimizations and other operations on them. See exposing functionality of all scene converter plugins on a command line as well as performing introspection of scene files. +@subsection Trade-AbstractSceneConverter-usage-mesh Converting mesh data + +In the following snippet we use the @ref MeshOptimizerSceneConverter to perform +a set of optimizations on the mesh to make it render faster. While +@ref AnySceneConverter can detect the desired format while writing to a file, +here it would have no way to know what we want and so we request the concrete +plugin name directly. + +@snippet MagnumTrade.cpp AbstractSceneConverter-usage-mesh + +Commonly, when operating directly on the mesh data, each plugin exposes a set +of configuration options to specify what actually gets done and how, and the +default setup may not even do anything. See @ref plugins-configuration for +details and a usage example. + +@subsection Trade-AbstractSceneConverter-usage-mesh-in-place Converting mesh data in-place + +Certain operations such as buffer reordering can be performed by directly +modifying the input data instead of having to allocate a copy of the whole +mesh. For that, there's @ref convertInPlace(), however compared to +@ref convert() it imposes additional requirements on the input. Depending on +the converter, it might require that either the index or the vertex data are +mutable, or that the mesh is interleaved and so on, so be sure to check the +plugin docs before use. + +An equivalent to the above operation, but performed in-place, would be the +following: + +@snippet MagnumTrade.cpp AbstractSceneConverter-usage-mesh-in-place + @section Trade-AbstractSceneConverter-data-dependency Data dependency The instances returned from various functions *by design* have no dependency on