From 506740f9c83d840b98c73953638b8a27e5efa0b1 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Vladim=C3=ADr=20Vondru=C5=A1?= Date: Wed, 19 Feb 2020 18:50:18 +0100 Subject: [PATCH] Trade: typeless access to MeshData indices. Like with attributes, it returns a 2D strided view with the second dimension having the same size as the index type. --- src/Magnum/Trade/MeshData.cpp | 34 ++++++++++++++ src/Magnum/Trade/MeshData.h | 51 ++++++++++++++++----- src/Magnum/Trade/Test/MeshDataTest.cpp | 62 ++++++++++++++++++++++++++ 3 files changed, 137 insertions(+), 10 deletions(-) diff --git a/src/Magnum/Trade/MeshData.cpp b/src/Magnum/Trade/MeshData.cpp index 4d5524630..37b48e11e 100644 --- a/src/Magnum/Trade/MeshData.cpp +++ b/src/Magnum/Trade/MeshData.cpp @@ -40,6 +40,16 @@ MeshIndexData::MeshIndexData(const MeshIndexType type, const Containers::ArrayVi "Trade::MeshIndexData: view size" << data.size() << "does not correspond to" << type, ); } +MeshIndexData::MeshIndexData(const Containers::StridedArrayView2D& data) noexcept { + if(data.size()[1] == 4) _type = MeshIndexType::UnsignedInt; + else if(data.size()[1] == 2) _type = MeshIndexType::UnsignedShort; + else if(data.size()[1] == 1) _type = MeshIndexType::UnsignedByte; + else CORRADE_ASSERT(false, "Trade::MeshIndexData: expected index type size 1, 2 or 4 but got" << data.size()[1], ); + + CORRADE_ASSERT(data.isContiguous(), "Trade::MeshIndexData: view is not contiguous", ); + _data = data.asContiguous(); +} + MeshAttributeData::MeshAttributeData(const MeshAttribute name, const VertexFormat format, const Containers::StridedArrayView1D& data) noexcept: MeshAttributeData{name, format, data, nullptr} { /* Yes, this calls into a constexpr function defined in the header -- because I feel that makes more sense than duplicating the full assert @@ -181,6 +191,30 @@ MeshIndexType MeshData::indexType() const { return _indexType; } +Containers::StridedArrayView2D MeshData::indices() const { + CORRADE_ASSERT(isIndexed(), + "Trade::MeshData::indices(): the mesh is not indexed", {}); + const std::size_t indexTypeSize = meshIndexTypeSize(_indexType); + /* Build a 2D view using information about attribute type size */ + return {_indices, {_indices.size()/indexTypeSize, indexTypeSize}}; +} + +Containers::StridedArrayView2D MeshData::mutableIndices() { + CORRADE_ASSERT(_indexDataFlags & DataFlag::Mutable, + "Trade::MeshData::mutableIndices(): index data not mutable", {}); + CORRADE_ASSERT(isIndexed(), + "Trade::MeshData::mutableIndices(): the mesh is not indexed", {}); + const std::size_t indexTypeSize = meshIndexTypeSize(_indexType); + /* Build a 2D view using information about attribute type size */ + Containers::StridedArrayView2D out{_indices, {_indices.size()/indexTypeSize, indexTypeSize}}; + /** @todo some arrayConstCast? UGH */ + return Containers::StridedArrayView2D{ + /* The view size is there only for a size assert, we're pretty sure the + view is valid */ + {static_cast(const_cast(out.data())), ~std::size_t{}}, + out.size(), out.stride()}; +} + MeshAttribute MeshData::attributeName(UnsignedInt id) const { CORRADE_ASSERT(id < _attributes.size(), "Trade::MeshData::attributeName(): index" << id << "out of range for" << _attributes.size() << "attributes", {}); diff --git a/src/Magnum/Trade/MeshData.h b/src/Magnum/Trade/MeshData.h index e44a027ae..e7ab2cafc 100644 --- a/src/Magnum/Trade/MeshData.h +++ b/src/Magnum/Trade/MeshData.h @@ -159,7 +159,7 @@ introduction. class MAGNUM_TRADE_EXPORT MeshIndexData { public: /** @brief Construct for a non-indexed mesh */ - explicit MeshIndexData() noexcept: _type{} {} + explicit MeshIndexData(std::nullptr_t = nullptr) noexcept: _type{} {} /** * @brief Construct with a runtime-specified index type @@ -185,6 +185,15 @@ class MAGNUM_TRADE_EXPORT MeshIndexData { /** @brief Construct with unsigned int indices */ constexpr explicit MeshIndexData(Containers::ArrayView data) noexcept: MeshIndexData{MeshIndexType::UnsignedInt, data, nullptr} {} + /** + * @brief Constructor + * + * Expects that @p data is contiguous and size of the second dimension + * is either 1, 2 or 4, corresponding to one of the @ref MeshIndexType + * values. + */ + explicit MeshIndexData(const Containers::StridedArrayView2D& data) noexcept; + /** @brief Index type */ constexpr MeshIndexType type() const { return _type; } @@ -675,6 +684,26 @@ class MAGNUM_TRADE_EXPORT MeshData { /** * @brief Mesh indices * + * The view is guaranteed to be contiguous and its second dimension + * represents the actual data type (its size is equal to type size). + * Use the templated overload below to get the indices in a concrete + * type. + * @see @ref Corrade::Containers::StridedArrayView::isContiguous() + */ + Containers::StridedArrayView2D indices() const; + + /** + * @brief Mutable mesh indices + * + * Like @ref indices() const, but returns a mutable view. Expects that + * the mesh is mutable. + * @see @ref indexDataFlags() + */ + Containers::StridedArrayView2D mutableIndices(); + + /** + * @brief Mesh indices in a concrete type + * * Expects that the mesh is indexed and that @p T corresponds to * @ref indexType(). You can also use the non-templated * @ref indicesAsArray() accessor to get indices converted to 32-bit, @@ -685,7 +714,7 @@ class MAGNUM_TRADE_EXPORT MeshData { template Containers::ArrayView indices() const; /** - * @brief Mutable mesh indices + * @brief Mutable mesh indices in a concrete type * * Like @ref indices() const, but returns a mutable view. Expects that * the mesh is mutable. @@ -1140,21 +1169,23 @@ constexpr MeshAttributeData::MeshAttributeData(const MeshAttribute name, const V template constexpr MeshAttributeData::MeshAttributeData(MeshAttribute name, const Containers::StridedArrayView1D& data) noexcept: MeshAttributeData{name, Implementation::vertexFormatFor::type>(), data, nullptr} {} template Containers::ArrayView MeshData::indices() const { - CORRADE_ASSERT(isIndexed(), - "Trade::MeshData::indices(): the mesh is not indexed", {}); + Containers::StridedArrayView2D data = indices(); + #ifdef CORRADE_GRACEFUL_ASSERT /* Sigh. Brittle. Better idea? */ + if(!data.stride()[1]) return {}; + #endif CORRADE_ASSERT(Implementation::meshIndexTypeFor() == _indexType, "Trade::MeshData::indices(): improper type requested for" << _indexType, nullptr); - return Containers::arrayCast(_indices); + return Containers::arrayCast<1, const T>(data).asContiguous(); } template Containers::ArrayView MeshData::mutableIndices() { - CORRADE_ASSERT(_indexDataFlags & DataFlag::Mutable, - "Trade::MeshData::mutableIndices(): index data not mutable", {}); - CORRADE_ASSERT(isIndexed(), - "Trade::MeshData::mutableIndices(): the mesh is not indexed", {}); + Containers::StridedArrayView2D data = mutableIndices(); + #ifdef CORRADE_GRACEFUL_ASSERT /* Sigh. Brittle. Better idea? */ + if(!data.stride()[1]) return {}; + #endif CORRADE_ASSERT(Implementation::meshIndexTypeFor() == _indexType, "Trade::MeshData::mutableIndices(): improper type requested for" << _indexType, nullptr); - return Containers::arrayCast(reinterpret_cast&>(_indices)); + return Containers::arrayCast<1, T>(data).asContiguous(); } template Containers::StridedArrayView1D MeshData::attribute(UnsignedInt id) const { diff --git a/src/Magnum/Trade/Test/MeshDataTest.cpp b/src/Magnum/Trade/Test/MeshDataTest.cpp index be430b8aa..1b1ffd097 100644 --- a/src/Magnum/Trade/Test/MeshDataTest.cpp +++ b/src/Magnum/Trade/Test/MeshDataTest.cpp @@ -45,6 +45,10 @@ struct MeshDataTest: TestSuite::Tester { void constructIndexZeroCount(); void constructIndexTypeErased(); void constructIndexTypeErasedWrongSize(); + void constructIndex2D(); + void constructIndex2DWrongSize(); + void constructIndex2DNonContiguous(); + void constructIndexNullptr(); void constructAttribute(); void constructAttributeCustom(); @@ -142,6 +146,10 @@ MeshDataTest::MeshDataTest() { &MeshDataTest::constructIndexZeroCount, &MeshDataTest::constructIndexTypeErased, &MeshDataTest::constructIndexTypeErasedWrongSize, + &MeshDataTest::constructIndex2D, + &MeshDataTest::constructIndex2DWrongSize, + &MeshDataTest::constructIndex2DNonContiguous, + &MeshDataTest::constructIndexNullptr, &MeshDataTest::constructAttribute, &MeshDataTest::constructAttributeCustom, @@ -326,6 +334,49 @@ void MeshDataTest::constructIndexTypeErasedWrongSize() { CORRADE_COMPARE(out.str(), "Trade::MeshIndexData: view size 6 does not correspond to MeshIndexType::UnsignedInt\n"); } +void MeshDataTest::constructIndex2D() { + { + const UnsignedByte indexData[]{25, 132, 3}; + MeshIndexData indices{Containers::arrayCast<2, const char>(Containers::stridedArrayView(indexData))}; + CORRADE_COMPARE(indices.type(), MeshIndexType::UnsignedByte); + CORRADE_COMPARE(indices.data().data(), indexData); + } { + const UnsignedShort indexData[]{2575, 13224, 3}; + MeshIndexData indices{Containers::arrayCast<2, const char>(Containers::stridedArrayView(indexData))}; + CORRADE_COMPARE(indices.type(), MeshIndexType::UnsignedShort); + CORRADE_COMPARE(indices.data().data(), indexData); + } { + const UnsignedInt indexData[]{2110122, 132257, 3}; + MeshIndexData indices{Containers::arrayCast<2, const char>(Containers::stridedArrayView(indexData))}; + CORRADE_COMPARE(indices.type(), MeshIndexType::UnsignedInt); + CORRADE_COMPARE(indices.data().data(), indexData); + } +} + +void MeshDataTest::constructIndex2DWrongSize() { + const char data[3*3]{}; + + std::ostringstream out; + Error redirectError{&out}; + MeshIndexData{Containers::StridedArrayView2D{data, {3, 3}}}; + CORRADE_COMPARE(out.str(), "Trade::MeshIndexData: expected index type size 1, 2 or 4 but got 3\n"); +} + +void MeshDataTest::constructIndex2DNonContiguous() { + const char data[3*4]{}; + + std::ostringstream out; + Error redirectError{&out}; + MeshIndexData{Containers::StridedArrayView2D{data, {3, 2}, {4, 2}}}; + CORRADE_COMPARE(out.str(), "Trade::MeshIndexData: view is not contiguous\n"); +} + +void MeshDataTest::constructIndexNullptr() { + /* Just verify it's not ambiguous */ + MeshIndexData data{nullptr}; + CORRADE_VERIFY(!data.data()); +} + constexpr Vector2 Positions[] { {1.2f, 0.2f}, {2.2f, 1.1f}, @@ -500,6 +551,13 @@ void MeshDataTest::construct() { CORRADE_VERIFY(data.isIndexed()); CORRADE_COMPARE(data.indexCount(), 6); CORRADE_COMPARE(data.indexType(), MeshIndexType::UnsignedShort); + + /* Typeless index access with a cast later */ + CORRADE_COMPARE((Containers::arrayCast<1, const UnsignedShort>(data.indices())[1]), 1); + CORRADE_COMPARE((Containers::arrayCast<1, const UnsignedShort>(data.indices())[3]), 0); + CORRADE_COMPARE((Containers::arrayCast<1, const UnsignedShort>(data.indices())[4]), 2); + + /* Typed index access */ CORRADE_COMPARE(data.indices()[0], 0); CORRADE_COMPARE(data.indices()[2], 2); CORRADE_COMPARE(data.indices()[5], 1); @@ -1374,6 +1432,7 @@ void MeshDataTest::mutableAccessNotAllowed() { Error redirectError{&out}; data.mutableIndexData(); data.mutableVertexData(); + data.mutableIndices(); data.mutableIndices(); data.mutableAttribute(0); data.mutableAttribute(0); @@ -1383,6 +1442,7 @@ void MeshDataTest::mutableAccessNotAllowed() { "Trade::MeshData::mutableIndexData(): index data not mutable\n" "Trade::MeshData::mutableVertexData(): vertex data not mutable\n" "Trade::MeshData::mutableIndices(): index data not mutable\n" + "Trade::MeshData::mutableIndices(): index data not mutable\n" "Trade::MeshData::mutableAttribute(): vertex data not mutable\n" "Trade::MeshData::mutableAttribute(): vertex data not mutable\n" "Trade::MeshData::mutableAttribute(): vertex data not mutable\n" @@ -1396,6 +1456,7 @@ void MeshDataTest::indicesNotIndexed() { Error redirectError{&out}; data.indexCount(); data.indexType(); + data.indices(); data.indices(); data.indicesAsArray(); UnsignedInt a[1]; @@ -1404,6 +1465,7 @@ void MeshDataTest::indicesNotIndexed() { "Trade::MeshData::indexCount(): the mesh is not indexed\n" "Trade::MeshData::indexType(): the mesh is not indexed\n" "Trade::MeshData::indices(): the mesh is not indexed\n" + "Trade::MeshData::indices(): the mesh is not indexed\n" "Trade::MeshData::indicesAsArray(): the mesh is not indexed\n" "Trade::MeshData::indicesInto(): the mesh is not indexed\n"); }