Browse Source

Trade: implement Importer interfaces for the new MeshData.

Deprecating of the old ones comes later.
pull/371/head
Vladimír Vondruš 6 years ago
parent
commit
fb1fdf6105
  1. 3
      doc/changelog.dox
  2. 54
      src/Magnum/Trade/AbstractImporter.cpp
  3. 117
      src/Magnum/Trade/AbstractImporter.h
  4. 23
      src/Magnum/Trade/MeshData.h
  5. 320
      src/Magnum/Trade/Test/AbstractImporterTest.cpp

3
doc/changelog.dox

@ -162,7 +162,8 @@ See also:
- A new, redesigned @ref Trade::MeshData class that allows much more flexible
access to vertex/index data without unnecessary allocations and data
conversions or copies
conversions or copies. Importers expose it through the new
@ref Trade::AbstractImporter::mesh() family of APIs.
- Ability to import image mip levels via an additional parameter in
@ref Trade::AbstractImporter::image2D(),
@ref Trade::AbstractImporter::image2DLevelCount() and similar APIs for 1D

54
src/Magnum/Trade/AbstractImporter.cpp

@ -38,6 +38,7 @@
#include "Magnum/Trade/CameraData.h"
#include "Magnum/Trade/ImageData.h"
#include "Magnum/Trade/LightData.h"
#include "Magnum/Trade/MeshData.h"
#include "Magnum/Trade/MeshData2D.h"
#include "Magnum/Trade/MeshData3D.h"
#include "Magnum/Trade/ObjectData2D.h"
@ -403,6 +404,59 @@ Containers::Pointer<ObjectData3D> AbstractImporter::doObject3D(UnsignedInt) {
CORRADE_ASSERT(false, "Trade::AbstractImporter::object3D(): not implemented", {});
}
UnsignedInt AbstractImporter::meshCount() const {
CORRADE_ASSERT(isOpened(), "Trade::AbstractImporter::meshCount(): no file opened", {});
return doMeshCount();
}
UnsignedInt AbstractImporter::doMeshCount() const { return 0; }
Int AbstractImporter::meshForName(const std::string& name) {
CORRADE_ASSERT(isOpened(), "Trade::AbstractImporter::meshForName(): no file opened", {});
return doMeshForName(name);
}
Int AbstractImporter::doMeshForName(const std::string&) { return -1; }
std::string AbstractImporter::meshName(const UnsignedInt id) {
CORRADE_ASSERT(isOpened(), "Trade::AbstractImporter::meshName(): no file opened", {});
CORRADE_ASSERT(id < doMeshCount(), "Trade::AbstractImporter::meshName(): index" << id << "out of range for" << doMeshCount() << "entries", {});
return doMeshName(id);
}
std::string AbstractImporter::doMeshName(UnsignedInt) { return {}; }
Containers::Optional<MeshData> AbstractImporter::mesh(const UnsignedInt id) {
CORRADE_ASSERT(isOpened(), "Trade::AbstractImporter::mesh(): no file opened", {});
CORRADE_ASSERT(id < doMeshCount(), "Trade::AbstractImporter::mesh(): index" << id << "out of range for" << doMeshCount() << "entries", {});
Containers::Optional<MeshData> mesh = doMesh(id);
CORRADE_ASSERT(!mesh || (!mesh->_indexData.deleter() && !mesh->_vertexData.deleter() && !mesh->_attributes.deleter()), "Trade::AbstractImporter::mesh(): implementation is not allowed to use a custom Array deleter", {});
return mesh;
}
Containers::Optional<MeshData> AbstractImporter::doMesh(UnsignedInt) {
CORRADE_ASSERT(false, "Trade::AbstractImporter::mesh(): not implemented", {});
}
MeshAttribute AbstractImporter::meshAttributeForName(const std::string& name) {
const MeshAttribute out = doMeshAttributeForName(name);
CORRADE_ASSERT(out == MeshAttribute{} || isMeshAttributeCustom(out),
"Trade::AbstractImporter::meshAttributeForName(): implementation-returned" << out << "is neither custom nor invalid", {});
return out;
}
MeshAttribute AbstractImporter::doMeshAttributeForName(const std::string&) {
return {};
}
std::string AbstractImporter::meshAttributeName(MeshAttribute name) {
CORRADE_ASSERT(isMeshAttributeCustom(name),
"Trade::AbstractImporter::meshAttributeName():" << name << "is not custom", {});
return doMeshAttributeName(meshAttributeCustom(name));
}
std::string AbstractImporter::doMeshAttributeName(UnsignedShort) { return {}; }
UnsignedInt AbstractImporter::mesh2DCount() const {
CORRADE_ASSERT(isOpened(), "Trade::AbstractImporter::mesh2DCount(): no file opened", {});
return doMesh2DCount();

117
src/Magnum/Trade/AbstractImporter.h

@ -159,8 +159,8 @@ expose internal state through various accessors:
imported by @ref image1D(), @ref image2D() or @ref image3D()
- @ref LightData::importerState() can expose importer state for a light
imported by @ref light()
- @ref MeshData3D::importerState() can expose importer state for a mesh
imported by @ref mesh2D() or @ref mesh3D()
- @ref MeshData::importerState() can expose importer state for a mesh
imported by @ref mesh()
- @ref ObjectData3D::importerState() can expose importer state for an object
imported by @ref object2D() or @ref object3D()
- @ref SceneData::importerState() can expose importer state for a scene
@ -680,6 +680,72 @@ class MAGNUM_TRADE_EXPORT AbstractImporter: public PluginManager::AbstractManagi
*/
Containers::Pointer<ObjectData3D> object3D(UnsignedInt id);
/**
* @brief Mesh count
* @m_since_latest
*
* Expects that a file is opened.
*/
UnsignedInt meshCount() const;
/**
* @brief Mesh ID for given name
* @m_since_latest
*
* If no mesh for given name exists, returns @cpp -1 @ce. Expects that
* a file is opened.
* @see @ref meshName()
*/
Int meshForName(const std::string& name);
/**
* @brief Mesh name
* @param id Mesh ID, from range [0, @ref meshCount()).
* @m_since_latest
*
* Expects that a file is opened.
* @see @ref meshForName()
*/
std::string meshName(UnsignedInt id);
/**
* @brief Mesh
* @param id Mesh ID, from range [0, @ref meshCount()).
* @m_since_latest
*
* Returns given mesh or @ref Containers::NullOpt if importing failed.
* Expects that a file is opened.
*/
Containers::Optional<MeshData> mesh(UnsignedInt id);
/**
* @brief Mesh attribute for given name
* @m_since_latest
*
* If the name is not recognized, returns a zero (invalid)
* @ref MeshAttribute, otherwise returns a custom mesh attribute. Note
* that the value returned by this function may depend on whether a
* file is opened or not and also be different for different files ---
* see documentation of a particular importer for more information.
* @see @ref isMeshAttributeCustom()
*/
MeshAttribute meshAttributeForName(const std::string& name);
/**
* @brief String name for given custom mesh attribute
* @m_since_latest
*
* Given a custom @p name returned by @ref mesh() in a @ref MeshData,
* returns a string identifier. If a string representation is not
* available or @p name is not recognized, returns an empty string.
* Expects that @p name is custom. Note that the value returned by
* this function may depend on whether a file is opened or not and also
* be different for different files --- see documentation of a
* particular importer for more information.
* @see @ref isMeshAttributeCustom()
*/
std::string meshAttributeName(MeshAttribute name);
/**
* @brief Two-dimensional mesh count
*
@ -1164,6 +1230,53 @@ class MAGNUM_TRADE_EXPORT AbstractImporter: public PluginManager::AbstractManagi
/** @brief Implementation for @ref object3D() */
virtual Containers::Pointer<ObjectData3D> doObject3D(UnsignedInt id);
/**
* @brief Implementation for @ref meshCount()
* @m_since_latest
*
* Default implementation returns @cpp 0 @ce.
*/
virtual UnsignedInt doMeshCount() const;
/**
* @brief Implementation for @ref meshForName()
* @m_since_latest
*
* Default implementation returns @cpp -1 @ce.
*/
virtual Int doMeshForName(const std::string& name);
/**
* @brief Implementation for @ref meshName()
* @m_since_latest
*
* Default implementation returns an empty string.
*/
virtual std::string doMeshName(UnsignedInt id);
/**
* @brief Implementation for @ref mesh()
* @m_since_latest
*/
virtual Containers::Optional<MeshData> doMesh(UnsignedInt id);
/**
* @brief Implementation for @ref meshAttributeForName()
* @m_since_latest
*
* Default implementation returns an invalid (zero) value.
*/
virtual MeshAttribute doMeshAttributeForName(const std::string& name);
/**
* @brief Implementation for @ref meshAttributeName()
* @m_since_latest
*
* Receives the custom ID extracted via @ref meshAttributeCustom(MeshAttribute).
* Default implementation returns an empty string.
*/
virtual std::string doMeshAttributeName(UnsignedShort name);
/**
* @brief Implementation for @ref mesh2DCount()
*

23
src/Magnum/Trade/MeshData.h

@ -44,11 +44,16 @@ namespace Magnum { namespace Trade {
@brief Mesh attribute name
@m_since_latest
@see @ref MeshData, @ref MeshAttributeData, @ref VertexFormat
@see @ref MeshData, @ref MeshAttributeData, @ref VertexFormat,
@ref AbstractImporter::meshAttributeForName(),
@ref AbstractImporter::meshAttributeName()
*/
/* 16 bits because 8 bits is not enough to cover all potential per-edge,
per-face, per-instance and per-meshlet attributes */
enum class MeshAttribute: UnsignedShort {
/* 0 reserved for an invalid value (returned from
AbstractImporter::meshAttributeForName()) */
/**
* Position. Type is usually @ref Magnum::Vector2 "Vector2" for 2D and
* @ref Magnum::Vector3 "Vector3" for 3D. Corresponds to
@ -57,7 +62,7 @@ enum class MeshAttribute: UnsignedShort {
* @ref MeshData::positions2DAsArray(),
* @ref MeshData::positions3DAsArray()
*/
Position,
Position = 1,
/**
* Normal. Type is usually @ref Magnum::Vector3 "Vector3". Corresponds to
@ -243,7 +248,8 @@ class MAGNUM_TRADE_EXPORT MeshAttributeData {
@m_since_latest
Provides access to mesh vertex and index data, together with additional
information such as primitive type.
information such as primitive type. Populated instances of this class are
returned from @ref AbstractImporter::mesh().
@section Trade-MeshData-usage Basic usage
@ -268,6 +274,8 @@ of data, which you can directly upload, and then use provided metadata to let
the GPU know of the format and layout:
@snippet MagnumTrade.cpp MeshData-usage-advanced
@see @ref AbstractImporter::mesh()
*/
class MAGNUM_TRADE_EXPORT MeshData {
public:
@ -441,7 +449,9 @@ class MAGNUM_TRADE_EXPORT MeshData {
* @brief Attribute name
*
* The @p id is expected to be smaller than @ref attributeCount() const.
* @see @ref attributeFormat(), @ref isMeshAttributeCustom()
* @see @ref attributeFormat(), @ref isMeshAttributeCustom(),
* @ref AbstractImporter::meshAttributeForName(),
* @ref AbstractImporter::meshAttributeName()
*/
MeshAttribute attributeName(UnsignedInt id) const;
@ -714,6 +724,11 @@ class MAGNUM_TRADE_EXPORT MeshData {
const void* importerState() const { return _importerState; }
private:
/* For custom deleter checks. Not done in the constructors here because
the restriction is pointless when used outside of plugin
implementations. */
friend AbstractImporter;
UnsignedInt attributeFor(MeshAttribute name, UnsignedInt id) const;
UnsignedInt _vertexCount;

320
src/Magnum/Trade/Test/AbstractImporterTest.cpp

@ -37,6 +37,7 @@
#include "Magnum/Trade/CameraData.h"
#include "Magnum/Trade/ImageData.h"
#include "Magnum/Trade/LightData.h"
#include "Magnum/Trade/MeshData.h"
#include "Magnum/Trade/MeshData2D.h"
#include "Magnum/Trade/MeshData3D.h"
#include "Magnum/Trade/MeshObjectData2D.h"
@ -156,6 +157,25 @@ struct AbstractImporterTest: TestSuite::Tester {
void object3DNoFile();
void object3DOutOfRange();
void mesh();
void meshCountNotImplemented();
void meshCountNoFile();
void meshForNameNotImplemented();
void meshForNameNoFile();
void meshNameNotImplemented();
void meshNameNoFile();
void meshNameOutOfRange();
void meshNotImplemented();
void meshNoFile();
void meshOutOfRange();
void meshCustomIndexDataDeleter();
void meshCustomVertexDataDeleter();
void meshCustomAttributesDeleter();
void meshAttributeName();
void meshAttributeNameNotImplemented();
void meshAttributeNameNotCustom();
void mesh2D();
void mesh2DCountNotImplemented();
void mesh2DCountNoFile();
@ -371,6 +391,25 @@ AbstractImporterTest::AbstractImporterTest() {
&AbstractImporterTest::object3DNoFile,
&AbstractImporterTest::object3DOutOfRange,
&AbstractImporterTest::mesh,
&AbstractImporterTest::meshCountNotImplemented,
&AbstractImporterTest::meshCountNoFile,
&AbstractImporterTest::meshForNameNotImplemented,
&AbstractImporterTest::meshForNameNoFile,
&AbstractImporterTest::meshNameNotImplemented,
&AbstractImporterTest::meshNameNoFile,
&AbstractImporterTest::meshNameOutOfRange,
&AbstractImporterTest::meshNotImplemented,
&AbstractImporterTest::meshNoFile,
&AbstractImporterTest::meshOutOfRange,
&AbstractImporterTest::meshCustomIndexDataDeleter,
&AbstractImporterTest::meshCustomVertexDataDeleter,
&AbstractImporterTest::meshCustomAttributesDeleter,
&AbstractImporterTest::meshAttributeName,
&AbstractImporterTest::meshAttributeNameNotImplemented,
&AbstractImporterTest::meshAttributeNameNotCustom,
&AbstractImporterTest::mesh2D,
&AbstractImporterTest::mesh2DCountNotImplemented,
&AbstractImporterTest::mesh2DCountNoFile,
@ -2031,6 +2070,287 @@ void AbstractImporterTest::object3DOutOfRange() {
CORRADE_COMPARE(out.str(), "Trade::AbstractImporter::object3D(): index 8 out of range for 8 entries\n");
}
void AbstractImporterTest::mesh() {
struct: AbstractImporter {
ImporterFeatures doFeatures() const override { return {}; }
bool doIsOpened() const override { return true; }
void doClose() override {}
UnsignedInt doMeshCount() const override { return 8; }
Int doMeshForName(const std::string& name) override {
if(name == "eighth") return 7;
else return -1;
}
std::string doMeshName(UnsignedInt id) override {
if(id == 7) return "eighth";
else return {};
}
Containers::Optional<MeshData> doMesh(UnsignedInt id) override {
/* Verify that initializer list is converted to an array with
the default deleter and not something disallowed */
if(id == 7) return MeshData{MeshPrimitive::Points, nullptr, {MeshAttributeData{MeshAttribute::Position, VertexFormat::Vector3, nullptr}}, &state};
else return {};
}
} importer;
CORRADE_COMPARE(importer.meshCount(), 8);
CORRADE_COMPARE(importer.meshForName("eighth"), 7);
CORRADE_COMPARE(importer.meshName(7), "eighth");
auto data = importer.mesh(7);
CORRADE_VERIFY(data);
CORRADE_COMPARE(data->importerState(), &state);
}
void AbstractImporterTest::meshCountNotImplemented() {
struct: AbstractImporter {
ImporterFeatures doFeatures() const override { return {}; }
bool doIsOpened() const override { return true; }
void doClose() override {}
} importer;
CORRADE_COMPARE(importer.meshCount(), 0);
}
void AbstractImporterTest::meshCountNoFile() {
struct: AbstractImporter {
ImporterFeatures doFeatures() const override { return {}; }
bool doIsOpened() const override { return false; }
void doClose() override {}
} importer;
std::ostringstream out;
Error redirectError{&out};
importer.meshCount();
CORRADE_COMPARE(out.str(), "Trade::AbstractImporter::meshCount(): no file opened\n");
}
void AbstractImporterTest::meshForNameNotImplemented() {
struct: AbstractImporter {
ImporterFeatures doFeatures() const override { return {}; }
bool doIsOpened() const override { return true; }
void doClose() override {}
} importer;
CORRADE_COMPARE(importer.meshForName(""), -1);
}
void AbstractImporterTest::meshForNameNoFile() {
struct: AbstractImporter {
ImporterFeatures doFeatures() const override { return {}; }
bool doIsOpened() const override { return false; }
void doClose() override {}
} importer;
std::ostringstream out;
Error redirectError{&out};
importer.meshForName("");
CORRADE_COMPARE(out.str(), "Trade::AbstractImporter::meshForName(): no file opened\n");
}
void AbstractImporterTest::meshNameNotImplemented() {
struct: AbstractImporter {
ImporterFeatures doFeatures() const override { return {}; }
bool doIsOpened() const override { return true; }
void doClose() override {}
UnsignedInt doMeshCount() const override { return 8; }
} importer;
CORRADE_COMPARE(importer.meshName(7), "");
}
void AbstractImporterTest::meshNameNoFile() {
struct: AbstractImporter {
ImporterFeatures doFeatures() const override { return {}; }
bool doIsOpened() const override { return false; }
void doClose() override {}
} importer;
std::ostringstream out;
Error redirectError{&out};
importer.meshName(42);
CORRADE_COMPARE(out.str(), "Trade::AbstractImporter::meshName(): no file opened\n");
}
void AbstractImporterTest::meshNameOutOfRange() {
struct: AbstractImporter {
ImporterFeatures doFeatures() const override { return {}; }
bool doIsOpened() const override { return true; }
void doClose() override {}
UnsignedInt doMeshCount() const override { return 8; }
} importer;
std::ostringstream out;
Error redirectError{&out};
importer.meshName(8);
CORRADE_COMPARE(out.str(), "Trade::AbstractImporter::meshName(): index 8 out of range for 8 entries\n");
}
void AbstractImporterTest::meshNotImplemented() {
struct: AbstractImporter {
ImporterFeatures doFeatures() const override { return {}; }
bool doIsOpened() const override { return true; }
void doClose() override {}
UnsignedInt doMeshCount() const override { return 8; }
} importer;
std::ostringstream out;
Error redirectError{&out};
importer.mesh(7);
CORRADE_COMPARE(out.str(), "Trade::AbstractImporter::mesh(): not implemented\n");
}
void AbstractImporterTest::meshNoFile() {
struct: AbstractImporter {
ImporterFeatures doFeatures() const override { return {}; }
bool doIsOpened() const override { return false; }
void doClose() override {}
} importer;
std::ostringstream out;
Error redirectError{&out};
importer.mesh(42);
CORRADE_COMPARE(out.str(), "Trade::AbstractImporter::mesh(): no file opened\n");
}
void AbstractImporterTest::meshOutOfRange() {
struct: AbstractImporter {
ImporterFeatures doFeatures() const override { return {}; }
bool doIsOpened() const override { return true; }
void doClose() override {}
UnsignedInt doMeshCount() const override { return 8; }
} importer;
std::ostringstream out;
Error redirectError{&out};
importer.mesh(8);
CORRADE_COMPARE(out.str(), "Trade::AbstractImporter::mesh(): index 8 out of range for 8 entries\n");
}
void AbstractImporterTest::meshCustomIndexDataDeleter() {
struct: AbstractImporter {
ImporterFeatures doFeatures() const override { return {}; }
bool doIsOpened() const override { return true; }
void doClose() override {}
UnsignedInt doMeshCount() const override { return 1; }
Containers::Optional<MeshData> doMesh(UnsignedInt) override {
return MeshData{MeshPrimitive::Triangles, Containers::Array<char>{data, 1, [](char*, std::size_t) {}}, MeshIndexData{MeshIndexType::UnsignedByte, data}};
}
char data[1];
} importer;
std::ostringstream out;
Error redirectError{&out};
importer.mesh(0);
CORRADE_COMPARE(out.str(), "Trade::AbstractImporter::mesh(): implementation is not allowed to use a custom Array deleter\n");
}
void AbstractImporterTest::meshCustomVertexDataDeleter() {
struct: AbstractImporter {
ImporterFeatures doFeatures() const override { return {}; }
bool doIsOpened() const override { return true; }
void doClose() override {}
UnsignedInt doMeshCount() const override { return 1; }
Containers::Optional<MeshData> doMesh(UnsignedInt) override {
return MeshData{MeshPrimitive::Triangles, Containers::Array<char>{nullptr, 0, [](char*, std::size_t) {}}, {MeshAttributeData{MeshAttribute::Position, VertexFormat::Vector3, nullptr}}};
}
} importer;
std::ostringstream out;
Error redirectError{&out};
importer.mesh(0);
CORRADE_COMPARE(out.str(), "Trade::AbstractImporter::mesh(): implementation is not allowed to use a custom Array deleter\n");
}
void AbstractImporterTest::meshCustomAttributesDeleter() {
struct: AbstractImporter {
ImporterFeatures doFeatures() const override { return {}; }
bool doIsOpened() const override { return true; }
void doClose() override {}
UnsignedInt doMeshCount() const override { return 1; }
Containers::Optional<MeshData> doMesh(UnsignedInt) override {
return MeshData{MeshPrimitive::Triangles, nullptr, Containers::Array<MeshAttributeData>{&positions, 1, [](MeshAttributeData*, std::size_t) {}}};
}
MeshAttributeData positions{MeshAttribute::Position, VertexFormat::Vector3, nullptr};
} importer;
std::ostringstream out;
Error redirectError{&out};
importer.mesh(0);
CORRADE_COMPARE(out.str(), "Trade::AbstractImporter::mesh(): implementation is not allowed to use a custom Array deleter\n");
}
void AbstractImporterTest::meshAttributeName() {
struct: AbstractImporter {
ImporterFeatures doFeatures() const override { return {}; }
bool doIsOpened() const override { return false; }
void doClose() override {}
MeshAttribute doMeshAttributeForName(const std::string& name) override {
if(name == "SMOOTH_GROUP_ID") return meshAttributeCustom(37);
return MeshAttribute{};
}
std::string doMeshAttributeName(UnsignedShort id) override {
if(id == 37) return "SMOOTH_GROUP_ID";
return "";
}
} importer;
CORRADE_COMPARE(importer.meshAttributeForName("SMOOTH_GROUP_ID"), meshAttributeCustom(37));
CORRADE_COMPARE(importer.meshAttributeName(meshAttributeCustom(37)), "SMOOTH_GROUP_ID");
}
void AbstractImporterTest::meshAttributeNameNotImplemented() {
struct: AbstractImporter {
ImporterFeatures doFeatures() const override { return {}; }
bool doIsOpened() const override { return false; }
void doClose() override {}
} importer;
CORRADE_COMPARE(importer.meshAttributeForName(""), MeshAttribute{});
CORRADE_COMPARE(importer.meshAttributeName(meshAttributeCustom(37)), "");
}
void AbstractImporterTest::meshAttributeNameNotCustom() {
struct: AbstractImporter {
ImporterFeatures doFeatures() const override { return {}; }
bool doIsOpened() const override { return false; }
void doClose() override {}
MeshAttribute doMeshAttributeForName(const std::string&) override {
return MeshAttribute::Position;
}
} importer;
std::ostringstream out;
Error redirectError{&out};
importer.meshAttributeForName("SMOOTH_GROUP_ID");
importer.meshAttributeName(MeshAttribute::Position);
CORRADE_COMPARE(out.str(),
"Trade::AbstractImporter::meshAttributeForName(): implementation-returned Trade::MeshAttribute::Position is neither custom nor invalid\n"
"Trade::AbstractImporter::meshAttributeName(): Trade::MeshAttribute::Position is not custom\n");
}
void AbstractImporterTest::mesh2D() {
struct: AbstractImporter {
ImporterFeatures doFeatures() const override { return {}; }

Loading…
Cancel
Save