Browse Source

Trade: support mesh level import in AbstractImporter.

Similar to image mip level import, but this is largely left to be
importer-specific. For example PLY defines per-face data and sometimes
one might want to import them as-is, without them being turned into a
per-vertex property.
pull/371/head
Vladimír Vondruš 6 years ago
parent
commit
9425c23d0a
  1. 33
      src/Magnum/Trade/AbstractImporter.cpp
  2. 55
      src/Magnum/Trade/AbstractImporter.h
  3. 120
      src/Magnum/Trade/Test/AbstractImporterTest.cpp

33
src/Magnum/Trade/AbstractImporter.cpp

@ -462,6 +462,16 @@ UnsignedInt AbstractImporter::meshCount() const {
UnsignedInt AbstractImporter::doMeshCount() const { return 0; }
UnsignedInt AbstractImporter::meshLevelCount(const UnsignedInt id) {
CORRADE_ASSERT(isOpened(), "Trade::AbstractImporter::meshLevelCount(): no file opened", {});
CORRADE_ASSERT(id < doMeshCount(), "Trade::AbstractImporter::meshLevelCount(): index" << id << "out of range for" << doMeshCount() << "entries", {});
const UnsignedInt out = doMeshLevelCount(id);
CORRADE_ASSERT(out, "Trade::AbstractImporter::meshLevelCount(): implementation reported zero levels", {});
return out;
}
UnsignedInt AbstractImporter::doMeshLevelCount(UnsignedInt) { return 1; }
Int AbstractImporter::meshForName(const std::string& name) {
CORRADE_ASSERT(isOpened(), "Trade::AbstractImporter::meshForName(): no file opened", {});
return doMeshForName(name);
@ -477,10 +487,21 @@ std::string AbstractImporter::meshName(const UnsignedInt id) {
std::string AbstractImporter::doMeshName(UnsignedInt) { return {}; }
Containers::Optional<MeshData> AbstractImporter::mesh(const UnsignedInt id) {
Containers::Optional<MeshData> AbstractImporter::mesh(const UnsignedInt id, const UnsignedInt level) {
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);
#ifndef CORRADE_NO_ASSERT
/* Check for the range only if requested level is nonzero, as
meshLevelCount() is expected to return >= 1. This is done to prevent
random assertions and messages from a doMeshLevelCount() to be printed
(which are unlikely, but let's be consistent with what image*D() does). */
if(level) {
const UnsignedInt levelCount = doMeshLevelCount(id);
CORRADE_ASSERT(levelCount, "Trade::AbstractImporter::mesh(): implementation reported zero levels", {});
CORRADE_ASSERT(level < levelCount, "Trade::AbstractImporter::mesh(): level" << level << "out of range for" << levelCount << "entries", {});
}
#endif
Containers::Optional<MeshData> mesh = doMesh(id, level);
CORRADE_ASSERT(!mesh || (
(!mesh->_indexData.deleter() || mesh->_indexData.deleter() == Implementation::nonOwnedArrayDeleter || mesh->_indexData.deleter() == ArrayAllocator<char>::deleter) &&
(!mesh->_vertexData.deleter() || mesh->_vertexData.deleter() == Implementation::nonOwnedArrayDeleter || mesh->_vertexData.deleter() == ArrayAllocator<char>::deleter) &&
@ -489,15 +510,15 @@ Containers::Optional<MeshData> AbstractImporter::mesh(const UnsignedInt id) {
return mesh;
}
Containers::Optional<MeshData> AbstractImporter::doMesh(UnsignedInt) {
Containers::Optional<MeshData> AbstractImporter::doMesh(UnsignedInt, UnsignedInt) {
CORRADE_ASSERT(false, "Trade::AbstractImporter::mesh(): not implemented", {});
}
Containers::Optional<MeshData> AbstractImporter::mesh(const std::string& name) {
Containers::Optional<MeshData> AbstractImporter::mesh(const std::string& name, const UnsignedInt level) {
CORRADE_ASSERT(isOpened(), "Trade::AbstractImporter::mesh(): no file opened", {});
const Int id = doMeshForName(name);
if(id == -1) return {};
return mesh(id); /* not doMesh(), so we get the checks also */
return mesh(id, level); /* not doMesh(), so we get the checks also */
}
MeshAttribute AbstractImporter::meshAttributeForName(const std::string& name) {
@ -602,7 +623,7 @@ Containers::Optional<MeshData3D> AbstractImporter::mesh3D(const UnsignedInt id)
}
Containers::Optional<MeshData3D> AbstractImporter::doMesh3D(const UnsignedInt id) {
Containers::Optional<MeshData> out = doMesh(id);
Containers::Optional<MeshData> out = doMesh(id, 0);
if(out) return MeshData3D{*out};
return Containers::NullOpt;
}

55
src/Magnum/Trade/AbstractImporter.h

@ -245,9 +245,9 @@ checked by the implementation:
there is any file opened.
- All `do*()` implementations taking data ID as parameter are called only if
the ID is from valid range.
- For `doImage*()` and @p level parameter being nonzero, implementations are
called only if it is from valid range. Level zero is always expected to be
present and thus no check is done in that case.
- For @ref doMesh() and `doImage*()` and @p level parameter being nonzero,
implementations are called only if it is from valid range. Level zero is
always expected to be present and thus no check is done in that case.
@m_class{m-block m-warning}
@ -760,16 +760,27 @@ class MAGNUM_TRADE_EXPORT AbstractImporter: public PluginManager::AbstractManagi
* @m_since_latest
*
* Expects that a file is opened.
* @see @ref meshLevelCount()
*/
UnsignedInt meshCount() const;
/**
* @brief Mesh level count
* @param id Mesh ID, from range [0, @ref meshCount()).
* @m_since_latest
*
* Always returns at least one level, import failures are deferred to
* @ref mesh(). Expects that a file is opened.
*/
UnsignedInt meshLevelCount(UnsignedInt id);
/**
* @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(), @ref mesh(const std::string&)
* @see @ref meshName(), @ref mesh(const std::string&, UnsignedInt)
*/
Int meshForName(const std::string& name);
@ -786,24 +797,30 @@ class MAGNUM_TRADE_EXPORT AbstractImporter: public PluginManager::AbstractManagi
/**
* @brief Mesh
* @param id Mesh ID, from range [0, @ref meshCount()).
* @param level Mesh level, from range [0, @ref meshLevelCount())
* @m_since_latest
*
* Returns given mesh or @ref Containers::NullOpt if importing failed.
* Expects that a file is opened.
* @see @ref mesh(const std::string&)
* The @p level parameter allows access to additional data and is
* largely left as importer-specific --- for example allowing access to
* per-instance, per-face or per-edge data. Expects that a file is
* opened.
* @see @ref mesh(const std::string&, UnsignedInt),
* @ref MeshPrimitive::Instances, @ref MeshPrimitive::Faces,
* @ref MeshPrimitive::Edges
*/
Containers::Optional<MeshData> mesh(UnsignedInt id);
Containers::Optional<MeshData> mesh(UnsignedInt id, UnsignedInt level = 0);
/**
* @brief Mesh for given name
* @m_since_latest
*
* A convenience API combining @ref meshForName() and
* @ref mesh(UnsignedInt). Returns @ref Containers::NullOpt either if
* @ref meshForName() returns @cpp -1 @ce or if importing fails.
* Expects that a file is opened.
* @ref mesh(UnsignedInt, UnsignedInt). Returns
* @ref Containers::NullOpt either if @ref meshForName() returns
* @cpp -1 @ce or if importing fails. Expects that a file is opened.
*/
Containers::Optional<MeshData> mesh(const std::string& name);
Containers::Optional<MeshData> mesh(const std::string& name, UnsignedInt level = 0);
/**
* @brief Mesh attribute for given name
@ -1403,6 +1420,20 @@ class MAGNUM_TRADE_EXPORT AbstractImporter: public PluginManager::AbstractManagi
*/
virtual UnsignedInt doMeshCount() const;
/**
* @brief Implementation for @ref meshLevelCount()
* @m_since_latest
*
* Default implementation returns @cpp 1 @ce. Similarly to all other
* `*Count()` functions, this function isn't expected to fail --- if an
* import error occus, this function should return @cpp 1 @ce and the
* error state should be returned from @ref mesh() instead.
*
* Deliberately not @cpp const @ce to allow plugins cache decoded
* data.
*/
virtual UnsignedInt doMeshLevelCount(UnsignedInt id);
/**
* @brief Implementation for @ref meshForName()
* @m_since_latest
@ -1423,7 +1454,7 @@ class MAGNUM_TRADE_EXPORT AbstractImporter: public PluginManager::AbstractManagi
* @brief Implementation for @ref mesh()
* @m_since_latest
*/
virtual Containers::Optional<MeshData> doMesh(UnsignedInt id);
virtual Containers::Optional<MeshData> doMesh(UnsignedInt id, UnsignedInt level);
/**
* @brief Implementation for @ref meshAttributeForName()

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

@ -171,6 +171,10 @@ struct AbstractImporterTest: TestSuite::Tester {
#endif
void meshCountNotImplemented();
void meshCountNoFile();
void meshLevelCountNotImplemented();
void meshLevelCountNoFile();
void meshLevelCountOutOfRange();
void meshLevelCountZero();
void meshForNameNotImplemented();
void meshForNameNoFile();
void meshNameNotImplemented();
@ -179,6 +183,7 @@ struct AbstractImporterTest: TestSuite::Tester {
void meshNotImplemented();
void meshNoFile();
void meshOutOfRange();
void meshLevelOutOfRange();
void meshNonOwningDeleters();
void meshGrowableDeleters();
void meshCustomIndexDataDeleter();
@ -420,6 +425,10 @@ AbstractImporterTest::AbstractImporterTest() {
#endif
&AbstractImporterTest::meshCountNotImplemented,
&AbstractImporterTest::meshCountNoFile,
&AbstractImporterTest::meshLevelCountNotImplemented,
&AbstractImporterTest::meshLevelCountNoFile,
&AbstractImporterTest::meshLevelCountOutOfRange,
&AbstractImporterTest::meshLevelCountZero,
&AbstractImporterTest::meshForNameNotImplemented,
&AbstractImporterTest::meshForNameNoFile,
&AbstractImporterTest::meshNameNotImplemented,
@ -428,6 +437,7 @@ AbstractImporterTest::AbstractImporterTest() {
&AbstractImporterTest::meshNotImplemented,
&AbstractImporterTest::meshNoFile,
&AbstractImporterTest::meshOutOfRange,
&AbstractImporterTest::meshLevelOutOfRange,
&AbstractImporterTest::meshNonOwningDeleters,
&AbstractImporterTest::meshGrowableDeleters,
&AbstractImporterTest::meshCustomIndexDataDeleter,
@ -2239,6 +2249,10 @@ void AbstractImporterTest::mesh() {
void doClose() override {}
UnsignedInt doMeshCount() const override { return 8; }
UnsignedInt doMeshLevelCount(UnsignedInt id) override {
if(id == 7) return 3;
else return {};
}
Int doMeshForName(const std::string& name) override {
if(name == "eighth") return 7;
else return -1;
@ -2247,10 +2261,10 @@ void AbstractImporterTest::mesh() {
if(id == 7) return "eighth";
else return {};
}
Containers::Optional<MeshData> doMesh(UnsignedInt id) override {
Containers::Optional<MeshData> doMesh(UnsignedInt id, UnsignedInt level) 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};
if(id == 7 && level == 2) return MeshData{MeshPrimitive::Points, nullptr, {MeshAttributeData{MeshAttribute::Position, VertexFormat::Vector3, nullptr}}, &state};
else return {};
}
} importer;
@ -2260,11 +2274,11 @@ void AbstractImporterTest::mesh() {
CORRADE_COMPARE(importer.meshName(7), "eighth");
{
auto data = importer.mesh(7);
auto data = importer.mesh(7, 2);
CORRADE_VERIFY(data);
CORRADE_COMPARE(data->importerState(), &state);
} {
auto data = importer.mesh("eighth");
auto data = importer.mesh("eighth", 2);
CORRADE_VERIFY(data);
CORRADE_COMPARE(data->importerState(), &state);
} {
@ -2289,8 +2303,8 @@ void AbstractImporterTest::meshDeprecatedFallback() {
if(id == 7) return "eighth";
else return {};
}
Containers::Optional<MeshData> doMesh(UnsignedInt id) override {
if(id == 7) return MeshData{MeshPrimitive::Points, nullptr, {MeshAttributeData{MeshAttribute::Position, VertexFormat::Vector3, nullptr}}, &state};
Containers::Optional<MeshData> doMesh(UnsignedInt id, UnsignedInt level) override {
if(id == 7 && level == 0) return MeshData{MeshPrimitive::Points, nullptr, {MeshAttributeData{MeshAttribute::Position, VertexFormat::Vector3, nullptr}}, &state};
else return {};
}
} importer;
@ -2336,6 +2350,70 @@ void AbstractImporterTest::meshCountNoFile() {
CORRADE_COMPARE(out.str(), "Trade::AbstractImporter::meshCount(): no file opened\n");
}
void AbstractImporterTest::meshLevelCountNotImplemented() {
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.meshLevelCount(7), 1);
}
void AbstractImporterTest::meshLevelCountNoFile() {
struct: AbstractImporter {
ImporterFeatures doFeatures() const override { return {}; }
bool doIsOpened() const override { return false; }
void doClose() override {}
} importer;
std::ostringstream out;
Error redirectError{&out};
importer.meshLevelCount(7);
CORRADE_COMPARE(out.str(), "Trade::AbstractImporter::meshLevelCount(): no file opened\n");
}
void AbstractImporterTest::meshLevelCountOutOfRange() {
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.meshLevelCount(8);
CORRADE_COMPARE(out.str(), "Trade::AbstractImporter::meshLevelCount(): index 8 out of range for 8 entries\n");
}
void AbstractImporterTest::meshLevelCountZero() {
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&) override { return 0; }
UnsignedInt doMeshLevelCount(UnsignedInt) override { return 0; }
} importer;
std::ostringstream out;
Error redirectError{&out};
importer.meshLevelCount(7);
/* This should print a similar message instead of a confusing
"level 1 out of range for 0 entries" */
importer.mesh(7, 1);
importer.mesh("", 1);
CORRADE_COMPARE(out.str(),
"Trade::AbstractImporter::meshLevelCount(): implementation reported zero levels\n"
"Trade::AbstractImporter::mesh(): implementation reported zero levels\n"
"Trade::AbstractImporter::mesh(): implementation reported zero levels\n");
}
void AbstractImporterTest::meshForNameNotImplemented() {
struct: AbstractImporter {
ImporterFeatures doFeatures() const override { return {}; }
@ -2451,6 +2529,26 @@ void AbstractImporterTest::meshOutOfRange() {
CORRADE_COMPARE(out.str(), "Trade::AbstractImporter::mesh(): index 8 out of range for 8 entries\n");
}
void AbstractImporterTest::meshLevelOutOfRange() {
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&) override { return 0; }
UnsignedInt doMeshLevelCount(UnsignedInt) override { return 3; }
} importer;
std::ostringstream out;
Error redirectError{&out};
importer.mesh(7, 3);
importer.mesh("", 3);
CORRADE_COMPARE(out.str(),
"Trade::AbstractImporter::mesh(): level 3 out of range for 3 entries\n"
"Trade::AbstractImporter::mesh(): level 3 out of range for 3 entries\n");
}
void AbstractImporterTest::meshNonOwningDeleters() {
struct: AbstractImporter {
ImporterFeatures doFeatures() const override { return {}; }
@ -2458,7 +2556,7 @@ void AbstractImporterTest::meshNonOwningDeleters() {
void doClose() override {}
UnsignedInt doMeshCount() const override { return 1; }
Containers::Optional<MeshData> doMesh(UnsignedInt) override {
Containers::Optional<MeshData> doMesh(UnsignedInt, UnsignedInt) override {
return MeshData{MeshPrimitive::Triangles,
Containers::Array<char>{indexData, 1, Implementation::nonOwnedArrayDeleter}, MeshIndexData{MeshIndexType::UnsignedByte, indexData},
Containers::Array<char>{nullptr, 0, Implementation::nonOwnedArrayDeleter},
@ -2483,7 +2581,7 @@ void AbstractImporterTest::meshGrowableDeleters() {
void doClose() override {}
UnsignedInt doMeshCount() const override { return 1; }
Containers::Optional<MeshData> doMesh(UnsignedInt) override {
Containers::Optional<MeshData> doMesh(UnsignedInt, UnsignedInt) override {
Containers::Array<char> indexData;
Containers::arrayAppend<ArrayAllocator>(indexData, '\xab');
Containers::Array<Vector3> vertexData;
@ -2511,7 +2609,7 @@ void AbstractImporterTest::meshCustomIndexDataDeleter() {
UnsignedInt doMeshCount() const override { return 1; }
Int doMeshForName(const std::string&) override { return 0; }
Containers::Optional<MeshData> doMesh(UnsignedInt) override {
Containers::Optional<MeshData> doMesh(UnsignedInt, UnsignedInt) override {
return MeshData{MeshPrimitive::Triangles, Containers::Array<char>{data, 1, [](char*, std::size_t) {}}, MeshIndexData{MeshIndexType::UnsignedByte, data}};
}
@ -2536,7 +2634,7 @@ void AbstractImporterTest::meshCustomVertexDataDeleter() {
UnsignedInt doMeshCount() const override { return 1; }
Int doMeshForName(const std::string&) override { return 0; }
Containers::Optional<MeshData> doMesh(UnsignedInt) override {
Containers::Optional<MeshData> doMesh(UnsignedInt, UnsignedInt) override {
return MeshData{MeshPrimitive::Triangles, Containers::Array<char>{nullptr, 0, [](char*, std::size_t) {}}, {MeshAttributeData{MeshAttribute::Position, VertexFormat::Vector3, nullptr}}};
}
} importer;
@ -2559,7 +2657,7 @@ void AbstractImporterTest::meshCustomAttributesDeleter() {
UnsignedInt doMeshCount() const override { return 1; }
Int doMeshForName(const std::string&) override { return 0; }
Containers::Optional<MeshData> doMesh(UnsignedInt) override {
Containers::Optional<MeshData> doMesh(UnsignedInt, UnsignedInt) override {
return MeshData{MeshPrimitive::Triangles, nullptr, Containers::Array<MeshAttributeData>{&positions, 1, [](MeshAttributeData*, std::size_t) {}}};
}

Loading…
Cancel
Save