From dc41fe80f279e31d7f8f444e6b71026c2a9ad88f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Vladim=C3=ADr=20Vondru=C5=A1?= Date: Mon, 9 Mar 2020 16:03:29 +0100 Subject: [PATCH] StanfordImporter: support custom vertex attributes. --- doc/changelog-plugins.dox | 1 + .../StanfordImporter/StanfordImporter.cpp | 55 +++++-- .../StanfordImporter/StanfordImporter.h | 12 +- .../StanfordImporter/Test/CMakeLists.txt | 4 +- .../Test/StanfordImporterTest.cpp | 143 ++++++++++++++++-- .../Test/custom-vertex-components-be.ply | Bin 0 -> 305 bytes ....in => custom-vertex-components-be.ply.in} | 14 +- .../custom-vertex-components-duplicate.ply | Bin 0 -> 366 bytes .../custom-vertex-components-duplicate.ply.in | 24 +++ ...nents.ply => custom-vertex-components.ply} | Bin 287 -> 308 bytes .../Test/custom-vertex-components.ply.in | 23 +++ 11 files changed, 242 insertions(+), 34 deletions(-) create mode 100644 src/MagnumPlugins/StanfordImporter/Test/custom-vertex-components-be.ply rename src/MagnumPlugins/StanfordImporter/Test/{ignored-vertex-components.ply.in => custom-vertex-components-be.ply.in} (51%) create mode 100644 src/MagnumPlugins/StanfordImporter/Test/custom-vertex-components-duplicate.ply create mode 100644 src/MagnumPlugins/StanfordImporter/Test/custom-vertex-components-duplicate.ply.in rename src/MagnumPlugins/StanfordImporter/Test/{ignored-vertex-components.ply => custom-vertex-components.ply} (63%) create mode 100644 src/MagnumPlugins/StanfordImporter/Test/custom-vertex-components.ply.in diff --git a/doc/changelog-plugins.dox b/doc/changelog-plugins.dox index 9e5b12dcc..b06cabace 100644 --- a/doc/changelog-plugins.dox +++ b/doc/changelog-plugins.dox @@ -35,6 +35,7 @@ namespace Magnum { @subsection changelog-plugins-latest-new New features - Vertex color import in @ref Trade::StanfordImporter "StanfordImporter" +- Custom vertex attribute import in @ref Trade::StanfordImporter "StanfordImporter" - Support for the `KHR_lights_punctual` extension in @ref Trade::TinyGltfImporter "TinyGltfImporter", replacing the obsolete unuspported `KHR_lights_cmn` (see [mosra/magnum-plugins#77](https://github.com/mosra/magnum-plugins/pull/77)) diff --git a/src/MagnumPlugins/StanfordImporter/StanfordImporter.cpp b/src/MagnumPlugins/StanfordImporter/StanfordImporter.cpp index b915997b3..5628238a7 100644 --- a/src/MagnumPlugins/StanfordImporter/StanfordImporter.cpp +++ b/src/MagnumPlugins/StanfordImporter/StanfordImporter.cpp @@ -25,6 +25,7 @@ #include "StanfordImporter.h" +#include #include #include #include @@ -46,6 +47,9 @@ struct StanfordImporter::State { UnsignedInt vertexStride{}, vertexCount{}, faceIndicesOffset{}, faceSkip{}, faceCount{}; MeshIndexType faceSizeType{}, faceIndexType{}; bool fileFormatNeedsEndianSwapping; + + std::unordered_map attributeNameMap; + Containers::Array attributeNames; }; StanfordImporter::StanfordImporter() { @@ -305,7 +309,18 @@ void StanfordImporter::doOpenData(Containers::ArrayView data) { } else if(tokens[2] == "alpha") { colorOffsets.w() = vertexComponentOffset; colorFormats.w() = componentFormat; - } else Debug{} << "Trade::StanfordImporter::openData(): ignoring unknown vertex component" << tokens[2]; + + /* Unknown component, add to the attribute list. Stride is + not known yet, using 0 until it's updated later. */ + } else { + auto inserted = state->attributeNameMap.emplace(tokens[2], + meshAttributeCustom(state->attributeNames.size())); + arrayAppend(state->attributeNames, tokens[2]); + arrayAppend(state->attributeData, MeshAttributeData{ + inserted.first->second, + componentFormat, + vertexComponentOffset, state->vertexCount, 0}); + } /* Add size of current component to total offset */ vertexComponentOffset += vertexFormatSize(componentFormat); @@ -376,11 +391,12 @@ void StanfordImporter::doOpenData(Containers::ArrayView data) { return; } - /* Gather all attributes */ - std::size_t attributeCount = 1; - if((colorOffsets < Vector4ui{~UnsignedInt{}}).any()) ++attributeCount; - state->attributeData = Containers::Array{attributeCount}; - std::size_t attributeOffset = 0; + /* Stride is known now, update it in custom attributes */ + for(MeshAttributeData& attribute: state->attributeData) { + attribute = MeshAttributeData{ + attribute.name(), attribute.format(), + attribute.offset({}), state->vertexCount, std::ptrdiff_t(state->vertexStride)}; + } /* Wrap up positions */ { @@ -400,10 +416,10 @@ void StanfordImporter::doOpenData(Containers::ArrayView data) { } /* Add the attribute */ - state->attributeData[attributeOffset++] = MeshAttributeData{ + arrayAppend(state->attributeData, Containers::InPlaceInit, MeshAttribute::Position, vertexFormat(positionFormats.x(), 3, false), - positionOffsets.x(), state->vertexCount, state->vertexStride}; + positionOffsets.x(), state->vertexCount, std::ptrdiff_t(state->vertexStride)); } /* Wrap up colors, if any */ @@ -427,11 +443,11 @@ void StanfordImporter::doOpenData(Containers::ArrayView data) { } /* Add the attribute */ - state->attributeData[attributeOffset++] = MeshAttributeData{ + arrayAppend(state->attributeData, Containers::InPlaceInit, MeshAttribute::Color, /* We want integer types normalized, 3 or 4 components */ vertexFormat(colorFormats.x(), colorFormats.w() == VertexFormat{} ? 3 : 4, colorFormats.x() != VertexFormat::Float), - colorOffsets.x(), state->vertexCount, state->vertexStride}; + colorOffsets.x(), state->vertexCount, std::ptrdiff_t(state->vertexStride)); } if(data.size() < state->vertexStride*state->vertexCount) { @@ -544,7 +560,10 @@ Containers::Optional StanfordImporter::doMesh(UnsignedInt, UnsignedInt } else if(formatSize == 4) { for(Containers::StridedArrayView1D component: Containers::arrayCast<2, UnsignedInt>(mutableData, componentCount).transposed<0, 1>()) Utility::Endianness::swapInPlace(component); - } else CORRADE_ASSERT_UNREACHABLE(); /* 8-byte types not supported */ + } else if(formatSize == 8) { + for(Containers::StridedArrayView1D component: Containers::arrayCast<2, UnsignedLong>(mutableData, componentCount).transposed<0, 1>()) + Utility::Endianness::swapInPlace(component); + } else CORRADE_ASSERT_UNREACHABLE(); /* LCOV_EXCL_LINE */ } if(faceIndexTypeSize == 2) @@ -554,8 +573,9 @@ Containers::Optional StanfordImporter::doMesh(UnsignedInt, UnsignedInt else CORRADE_INTERNAL_ASSERT(faceIndexTypeSize == 1); } - /* We need to copy the attribute data, so use that opportunity to turn them - from offset-only to absolute */ + /* We need to copy the attribute data (also because they use a forbidden + deleter), so use that opportunity to turn them from offset-only to + absolute */ Containers::Array attributeData{_state->attributeData.size()}; for(std::size_t i = 0; i != attributeData.size(); ++i) { attributeData[i] = MeshAttributeData{ @@ -570,6 +590,15 @@ Containers::Optional StanfordImporter::doMesh(UnsignedInt, UnsignedInt std::move(vertexData), std::move(attributeData)}; } +std::string StanfordImporter::doMeshAttributeName(UnsignedShort name) { + return _state && name < _state->attributeNames.size() ? + _state->attributeNames[name] : ""; +} + +MeshAttribute StanfordImporter::doMeshAttributeForName(const std::string& name) { + return _state ? _state->attributeNameMap[name] : MeshAttribute{}; +} + }} CORRADE_PLUGIN_REGISTER(StanfordImporter, Magnum::Trade::StanfordImporter, diff --git a/src/MagnumPlugins/StanfordImporter/StanfordImporter.h b/src/MagnumPlugins/StanfordImporter/StanfordImporter.h index 9fb5913ba..c37f85aeb 100644 --- a/src/MagnumPlugins/StanfordImporter/StanfordImporter.h +++ b/src/MagnumPlugins/StanfordImporter/StanfordImporter.h @@ -93,7 +93,7 @@ target_link_libraries(your-app PRIVATE MagnumPlugins::StanfordImporter) See @ref building-plugins, @ref cmake-plugins and @ref plugins for more information. -@section Trade-StanfordImporter-limitations Behavior and limitations +@section Trade-StanfordImporter-behavior Behavior and limitations In order to optimize for fast import, the importer supports a restricted subset of PLY features, which however shouldn't affect any real-world models. @@ -132,6 +132,14 @@ using either @ref MeshTools::generateFlatNormals() / @ref MeshTools::CompileFlag::GenerateFlatNormals / @ref MeshTools::CompileFlag::GenerateSmoothNormals to @ref MeshTools::compile(). +@subsection Trade-StanfordImporter-behavior-custom-attributes Custom attributes + +Custom and unrecognized vertex attributes of known types are present in the +imported mesh as well. Their mapping to/from a string can be queried using +@ref meshAttributeName() and @ref meshAttributeForName(). Attributes with +unknown types cause the import to fail, as the format relies on knowing the +type size. + @section Trade-StanfordImporter-configuration Plugin-specific config It's possible to tune various import options through @ref configuration(). See @@ -159,6 +167,8 @@ class MAGNUM_STANFORDIMPORTER_EXPORT StanfordImporter: public AbstractImporter { MAGNUM_STANFORDIMPORTER_LOCAL UnsignedInt doMeshCount() const override; MAGNUM_STANFORDIMPORTER_LOCAL Containers::Optional doMesh(UnsignedInt id, UnsignedInt level) override; + MAGNUM_STANFORDIMPORTER_LOCAL MeshAttribute doMeshAttributeForName(const std::string& name) override; + MAGNUM_STANFORDIMPORTER_LOCAL std::string doMeshAttributeName(UnsignedShort name) override; struct State; Containers::Pointer _state; diff --git a/src/MagnumPlugins/StanfordImporter/Test/CMakeLists.txt b/src/MagnumPlugins/StanfordImporter/Test/CMakeLists.txt index 3ee3c9d8d..4f8372d2a 100644 --- a/src/MagnumPlugins/StanfordImporter/Test/CMakeLists.txt +++ b/src/MagnumPlugins/StanfordImporter/Test/CMakeLists.txt @@ -51,6 +51,9 @@ corrade_add_test(StanfordImporterTest StanfordImporterTest.cpp colors-unsupported-type.ply colors4-not-same-type.ply colors4-not-tightly-packed.ply + custom-vertex-components.ply + custom-vertex-components-be.ply + custom-vertex-components-duplicate.ply crlf.ply empty.ply format-invalid.ply @@ -58,7 +61,6 @@ corrade_add_test(StanfordImporterTest StanfordImporterTest.cpp format-too-late.ply format-unsupported.ply ignored-face-components.ply - ignored-vertex-components.ply incomplete-face-specification.ply incomplete-vertex-specification.ply invalid-face-index-type.ply diff --git a/src/MagnumPlugins/StanfordImporter/Test/StanfordImporterTest.cpp b/src/MagnumPlugins/StanfordImporter/Test/StanfordImporterTest.cpp index b8042c4d2..a02472989 100644 --- a/src/MagnumPlugins/StanfordImporter/Test/StanfordImporterTest.cpp +++ b/src/MagnumPlugins/StanfordImporter/Test/StanfordImporterTest.cpp @@ -52,6 +52,10 @@ struct StanfordImporterTest: TestSuite::Tester { void parse(); void empty(); + void customAttributes(); + void customAttributesDuplicate(); + void customAttributesNoFileOpened(); + void triangleFastPath(); void openTwice(); @@ -114,35 +118,42 @@ constexpr struct { const char* filename; MeshIndexType indexType; VertexFormat positionFormat, colorFormat; + UnsignedInt attributeCount; } ParseData[]{ /* GCC 4.8 doesn't like just {}, needs VertexFormat{} */ {"positions-float-indices-uint", MeshIndexType::UnsignedInt, - VertexFormat::Vector3, VertexFormat{}}, + VertexFormat::Vector3, VertexFormat{}, 1}, {"positions-colors-float-indices-int", MeshIndexType::UnsignedInt, - VertexFormat::Vector3, VertexFormat::Vector3}, + VertexFormat::Vector3, VertexFormat::Vector3, 2}, /* Four-component colors */ {"positions-colors4-float-indices-int", MeshIndexType::UnsignedInt, - VertexFormat::Vector3, VertexFormat::Vector4}, + VertexFormat::Vector3, VertexFormat::Vector4, 2}, /* Testing endian flip */ {"positions-colors-float-indices-int-be", MeshIndexType::UnsignedInt, - VertexFormat::Vector3, VertexFormat::Vector3}, + VertexFormat::Vector3, VertexFormat::Vector3, 2}, /* Testing endian flip of unaligned data */ {"positions-colors4-float-indices-int-be-unaligned", MeshIndexType::UnsignedInt, - VertexFormat::Vector3, VertexFormat::Vector4}, + VertexFormat::Vector3, VertexFormat::Vector4, 4}, /* Testing various packing variants (hopefully exhausting all combinations) */ {"positions-uchar-indices-ushort", MeshIndexType::UnsignedShort, - VertexFormat::Vector3ub, VertexFormat{}}, + VertexFormat::Vector3ub, VertexFormat{}, 1}, {"positions-char-colors4-ushort-indices-short-be", MeshIndexType::UnsignedShort, - VertexFormat::Vector3b, VertexFormat::Vector4usNormalized}, + VertexFormat::Vector3b, VertexFormat::Vector4usNormalized, 2}, {"positions-ushort-indices-uchar-be", MeshIndexType::UnsignedByte, - VertexFormat::Vector3us, VertexFormat{}}, + VertexFormat::Vector3us, VertexFormat{}, 1}, {"positions-short-colors-uchar-indices-char", MeshIndexType::UnsignedByte, - VertexFormat::Vector3s, VertexFormat::Vector3ubNormalized}, + VertexFormat::Vector3s, VertexFormat::Vector3ubNormalized, 2}, /* CR/LF instead of LF */ - {"crlf", MeshIndexType::UnsignedByte, VertexFormat::Vector3us, VertexFormat{}}, + {"crlf", MeshIndexType::UnsignedByte, VertexFormat::Vector3us, VertexFormat{}, 1}, /* Ignoring extra components */ - {"ignored-face-components", MeshIndexType::UnsignedByte, VertexFormat::Vector3b, VertexFormat{}}, - {"ignored-vertex-components", MeshIndexType::UnsignedByte, VertexFormat::Vector3us, VertexFormat{}} + {"ignored-face-components", MeshIndexType::UnsignedByte, VertexFormat::Vector3b, VertexFormat{}, 1} +}; + +constexpr struct { + const char* filename; +} CustomAttributeData[]{ + {"custom-vertex-components"}, + {"custom-vertex-components-be"} }; constexpr struct { @@ -167,6 +178,12 @@ StanfordImporterTest::StanfordImporterTest() { addTests({&StanfordImporterTest::empty}); + addInstancedTests({&StanfordImporterTest::customAttributes}, + Containers::arraySize(CustomAttributeData)); + + addTests({&StanfordImporterTest::customAttributesDuplicate, + &StanfordImporterTest::customAttributesNoFileOpened}); + addInstancedTests({&StanfordImporterTest::triangleFastPath}, Containers::arraySize(FastTrianglePathData)); @@ -267,6 +284,8 @@ void StanfordImporterTest::parse() { auto mesh = importer->mesh(0); CORRADE_VERIFY(mesh); + CORRADE_COMPARE(mesh->attributeCount(), data.attributeCount); + CORRADE_VERIFY(mesh->isIndexed()); CORRADE_COMPARE(mesh->indexType(), data.indexType); CORRADE_COMPARE_AS(mesh->indicesAsArray(), @@ -315,6 +334,106 @@ void StanfordImporterTest::empty() { CORRADE_COMPARE(mesh->vertexCount(), 0); } +void StanfordImporterTest::customAttributes() { + auto&& data = CustomAttributeData[testCaseInstanceId()]; + setTestCaseDescription(Utility::String::replaceAll(data.filename, "-", " ")); + + Containers::Pointer importer = _manager.instantiate("StanfordImporter"); + CORRADE_VERIFY(importer->openFile(Utility::Directory::join(STANFORDIMPORTER_TEST_DIR, Utility::formatString("{}.ply", data.filename)))); + + /* The should be available even before the mesh is imported */ + const MeshAttribute indexAttribute = importer->meshAttributeForName("index"); + CORRADE_COMPARE(indexAttribute, meshAttributeCustom(0)); + CORRADE_COMPARE(importer->meshAttributeName(indexAttribute), "index"); + + const MeshAttribute weightAttribute = importer->meshAttributeForName("weight"); + CORRADE_COMPARE(weightAttribute, meshAttributeCustom(1)); + CORRADE_COMPARE(importer->meshAttributeName(weightAttribute), "weight"); + + /* Asking for attribute name that's out of bounds should be handled + gracefully */ + CORRADE_COMPARE(importer->meshAttributeName(meshAttributeCustom(1000)), ""); + + auto mesh = importer->mesh(0); + CORRADE_VERIFY(mesh); + CORRADE_COMPARE(mesh->attributeCount(), 3); + CORRADE_VERIFY(mesh->isIndexed()); + CORRADE_COMPARE(mesh->indexType(), MeshIndexType::UnsignedByte); + CORRADE_COMPARE_AS(mesh->indicesAsArray(), + Containers::arrayView(Indices), + TestSuite::Compare::Container); + + CORRADE_VERIFY(mesh->hasAttribute(MeshAttribute::Position)); + CORRADE_COMPARE(mesh->attributeFormat(MeshAttribute::Position), VertexFormat::Vector3us); + CORRADE_COMPARE_AS(mesh->positions3DAsArray(), + Containers::arrayView(Positions), + TestSuite::Compare::Container); + + /* Custom attributes */ + CORRADE_VERIFY(mesh->hasAttribute(indexAttribute)); + CORRADE_COMPARE(mesh->attributeFormat(indexAttribute), VertexFormat::UnsignedByte); + CORRADE_COMPARE_AS(mesh->attribute(indexAttribute), + Containers::arrayView({ + 0xaa, 0xab, 0xac, 0xad, 0xae + }), TestSuite::Compare::Container); + CORRADE_VERIFY(mesh->hasAttribute(weightAttribute)); + CORRADE_COMPARE(mesh->attributeFormat(weightAttribute), VertexFormat::Double); + CORRADE_COMPARE_AS(mesh->attribute(weightAttribute), + Containers::arrayView({ + 1.23456, 12.3456, 123.456, 1234.56, 12345.6 + }), TestSuite::Compare::Container); +} + +void StanfordImporterTest::customAttributesDuplicate() { + Containers::Pointer importer = _manager.instantiate("StanfordImporter"); + CORRADE_VERIFY(importer->openFile(Utility::Directory::join(STANFORDIMPORTER_TEST_DIR, "custom-vertex-components-duplicate.ply"))); + + const MeshAttribute weightAttribute = importer->meshAttributeForName("weight"); + CORRADE_COMPARE(weightAttribute, meshAttributeCustom(0)); + CORRADE_COMPARE(importer->meshAttributeName(weightAttribute), "weight"); + + auto mesh = importer->mesh(0); + CORRADE_VERIFY(mesh); + CORRADE_COMPARE(mesh->attributeCount(), 4); + CORRADE_VERIFY(mesh->isIndexed()); + CORRADE_COMPARE(mesh->indexType(), MeshIndexType::UnsignedByte); + CORRADE_COMPARE_AS(mesh->indicesAsArray(), + Containers::arrayView(Indices), + TestSuite::Compare::Container); + + CORRADE_VERIFY(mesh->hasAttribute(MeshAttribute::Position)); + CORRADE_COMPARE(mesh->attributeFormat(MeshAttribute::Position), VertexFormat::Vector3us); + CORRADE_COMPARE_AS(mesh->positions3DAsArray(), + Containers::arrayView(Positions), + TestSuite::Compare::Container); + + /* Custom attributes. The weight is there three times, it should always be + the same ID, but can be a different format. */ + CORRADE_COMPARE(mesh->attributeCount(weightAttribute), 3); + CORRADE_COMPARE(mesh->attributeFormat(weightAttribute, 0), VertexFormat::Float); + CORRADE_COMPARE_AS(mesh->attribute(weightAttribute, 0), + Containers::arrayView({ + 0.1f, 0.2f, 0.3f, 0.4f, 0.5f + }), TestSuite::Compare::Container); + CORRADE_COMPARE(mesh->attributeFormat(weightAttribute, 1), VertexFormat::Float); + CORRADE_COMPARE_AS(mesh->attribute(weightAttribute, 1), + Containers::arrayView({ + 0.7f, 0.1f, 0.3f, 0.6f, 0.4f + }), TestSuite::Compare::Container); + CORRADE_COMPARE(mesh->attributeFormat(weightAttribute, 2), VertexFormat::Double); + CORRADE_COMPARE_AS(mesh->attribute(weightAttribute, 2), + Containers::arrayView({ + 0.2, 0.7, 0.4, 0.0, 0.1 + }), TestSuite::Compare::Container); +} + +void StanfordImporterTest::customAttributesNoFileOpened() { + Containers::Pointer importer = _manager.instantiate("StanfordImporter"); + + CORRADE_COMPARE(importer->meshAttributeName(meshAttributeCustom(564)), ""); + CORRADE_COMPARE(importer->meshAttributeForName("thing"), MeshAttribute{}); +} + void StanfordImporterTest::triangleFastPath() { auto&& data = FastTrianglePathData[testCaseInstanceId()]; setTestCaseDescription(data.name); diff --git a/src/MagnumPlugins/StanfordImporter/Test/custom-vertex-components-be.ply b/src/MagnumPlugins/StanfordImporter/Test/custom-vertex-components-be.ply new file mode 100644 index 0000000000000000000000000000000000000000..1df7e5b99489c2c1c09b7013bd868e644b33f259 GIT binary patch literal 305 zcmXTOspLw_FUn0UQAoPt8loOw3a-)HC2p%}LEo%_~tTOD!r%txz!K zDk#b?0J19;N|Q4Zixe{RQc^3B1dB8Bi%JwQMJkchWagC^nkiHvL{joglX6lO%2PAb zGfLoArX?n)Di|SDP`QSt^vvcWgX(~>PyYvSH2d=0m)AGvMpGAoogKf cD^P&b;e^Z?AUMmwz`(-5$ixh!nVFeb09V&&ZU6uP literal 0 HcmV?d00001 diff --git a/src/MagnumPlugins/StanfordImporter/Test/ignored-vertex-components.ply.in b/src/MagnumPlugins/StanfordImporter/Test/custom-vertex-components-be.ply.in similarity index 51% rename from src/MagnumPlugins/StanfordImporter/Test/ignored-vertex-components.ply.in rename to src/MagnumPlugins/StanfordImporter/Test/custom-vertex-components-be.ply.in index e98074bb3..ca47f69ea 100644 --- a/src/MagnumPlugins/StanfordImporter/Test/ignored-vertex-components.ply.in +++ b/src/MagnumPlugins/StanfordImporter/Test/custom-vertex-components-be.ply.in @@ -4,17 +4,17 @@ property uchar index property ushort x property ushort y property uint16 z -property float weight +property double weight element face 2 property list uint32 char vertex_indices """ -type = 'xJYts&T0Z{k=wOXRRbUt8m0d3&eO;TAo?3r4~n+^a5zj_VN`7kKa$ z?Q=^r9r`vzg54i7{vy`X@&Vpv@_$wxhK<9^7ne;MQz5M8S!a8Kfg_6*RIQ2URZGi9 zRM;y%rfkvF7}ar_9y1|DE{dYihkeqAK2A^TOk_+`zd7kEb=UIbP1TS0lf4tuX;Mz=#J-?l3#0x&2M>I?xeqzbQ{viARK(LCDfsuiU0nB7(U`8