From 9a6ef0a2202fd88998ef72514424d4e65592641d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Vladim=C3=ADr=20Vondru=C5=A1?= Date: Mon, 2 Mar 2020 20:55:54 +0100 Subject: [PATCH] MeshTools: new interleavedData() utility. --- doc/changelog.dox | 5 +- src/Magnum/MeshTools/Interleave.cpp | 40 ++++++++-- src/Magnum/MeshTools/Interleave.h | 14 +++- src/Magnum/MeshTools/Test/InterleaveTest.cpp | 80 ++++++++++++++++++++ 4 files changed, 128 insertions(+), 11 deletions(-) diff --git a/doc/changelog.dox b/doc/changelog.dox index 92f9386bb..ebabb2f28 100644 --- a/doc/changelog.dox +++ b/doc/changelog.dox @@ -116,8 +116,9 @@ See also: - Added @ref MeshTools::compile(const Trade::MeshData&) operating on the new @ref Trade::MeshData API -- New @ref MeshTools::isInterleaved() utility for checking if - @ref Trade::MeshData is interleaved +- New @ref MeshTools::isInterleaved() and @ref MeshTools::interleavedData() + utilities for checking if @ref Trade::MeshData is interleaved and for + getting an interleaved view - Added @ref MeshTools::interleavedLayout() for convenient creation of an interleaved mesh layout using the new @ref Trade::MeshData API - Added @ref MeshTools::interleave(const Trade::MeshData&, Containers::ArrayView), diff --git a/src/Magnum/MeshTools/Interleave.cpp b/src/Magnum/MeshTools/Interleave.cpp index ad629adb5..4f959bffc 100644 --- a/src/Magnum/MeshTools/Interleave.cpp +++ b/src/Magnum/MeshTools/Interleave.cpp @@ -32,23 +32,47 @@ namespace Magnum { namespace MeshTools { -bool isInterleaved(const Trade::MeshData& data) { - /* There is nothing, so yes it is (because there is nothing we could do - to make it interleaved anyway) */ - if(!data.attributeCount()) return true; +namespace { + +Containers::StridedArrayView2D interleavedDataInternal(const Trade::MeshData& data) { + /* There is no attributes, return a non-nullptr zero-sized view to indicate + a success */ + if(!data.attributeCount() || !data.vertexData()) + return Containers::StridedArrayView2D{data.vertexData(), {data.vertexCount(), 0}}; const UnsignedInt stride = data.attributeStride(0); std::size_t minOffset = data.attributeOffset(0); - std::size_t maxOffset = minOffset; - for(UnsignedInt i = 1; i != data.attributeCount(); ++i) { - if(data.attributeStride(i) != stride) return false; + std::size_t maxOffset = minOffset + vertexFormatSize(data.attributeFormat(0)); + for(UnsignedInt i = 0; i != data.attributeCount(); ++i) { + if(data.attributeStride(i) != stride) return nullptr; const std::size_t offset = data.attributeOffset(i); minOffset = Math::min(minOffset, offset); maxOffset = Math::max(maxOffset, offset + vertexFormatSize(data.attributeFormat(i))); } - return maxOffset - minOffset <= stride; + /* The offsets can't fit into the stride, report failure */ + if(maxOffset - minOffset > stride) return nullptr; + + return Containers::StridedArrayView2D{ + data.vertexData(), data.vertexData().data() + minOffset, + {data.vertexCount(), maxOffset - minOffset}, + {std::ptrdiff_t(stride), 1}}; +} + +} + +bool isInterleaved(const Trade::MeshData& data) { + /* If a nullptr value is returned but the mesh vertex data is not nullptr, + the mesh is not interleaved. Otherwise it is */ + return !!interleavedDataInternal(data).data() == !!data.vertexData().data(); +} + +Containers::StridedArrayView2D interleavedData(const Trade::MeshData& data) { + auto out = interleavedDataInternal(data); + CORRADE_ASSERT(out || !data.vertexData(), + "MeshTools::interleavedData(): the mesh is not interleaved", {}); + return out; } Trade::MeshData interleavedLayout(Trade::MeshData&& data, const UnsignedInt vertexCount, const Containers::ArrayView extra) { diff --git a/src/Magnum/MeshTools/Interleave.h b/src/Magnum/MeshTools/Interleave.h index 3d5f3cfb5..1e2f38e67 100644 --- a/src/Magnum/MeshTools/Interleave.h +++ b/src/Magnum/MeshTools/Interleave.h @@ -196,10 +196,22 @@ between minimal and maximal offset is not larger than the stride, @cpp false @ce otherwise. In particular, returns @cpp true @ce also if the mesh has just one or no attributes. @see @ref Trade::MeshData::attributeStride(), - @ref Trade::MeshData::attributeOffset() + @ref Trade::MeshData::attributeOffset(), @ref interleavedData() */ MAGNUM_MESHTOOLS_EXPORT bool isInterleaved(const Trade::MeshData& data); +/** +@brief Type-erased view on interleaved mesh data +@m_since_latest + +Returns a 2D view on @ref Trade::MeshData::vertexData() that spans all +interleaved attributes, with the first dimension being the vertex count and the +second being the attribute stride that's common for all attributes. Expects +that the mesh is interleaved. +@see @ref isInterleaved() +*/ +MAGNUM_MESHTOOLS_EXPORT Containers::StridedArrayView2D interleavedData(const Trade::MeshData& data); + /** @brief Create an interleaved mesh layout @m_since_latest diff --git a/src/Magnum/MeshTools/Test/InterleaveTest.cpp b/src/Magnum/MeshTools/Test/InterleaveTest.cpp index d3542b14b..142e381f7 100644 --- a/src/Magnum/MeshTools/Test/InterleaveTest.cpp +++ b/src/Magnum/MeshTools/Test/InterleaveTest.cpp @@ -58,6 +58,11 @@ struct InterleaveTest: Corrade::TestSuite::Tester { void isInterleavedUnordered(); void isInterleavedAttributeAcrossStride(); + void interleavedData(); + void interleavedDataNoAttributes(); + void interleavedDataNoVertices(); + void interleavedDataNotInterleaved(); + void interleavedLayout(); void interleavedLayoutExtra(); void interleavedLayoutExtraAliased(); @@ -100,6 +105,11 @@ InterleaveTest::InterleaveTest() { &InterleaveTest::isInterleavedUnordered, &InterleaveTest::isInterleavedAttributeAcrossStride, + &InterleaveTest::interleavedData, + &InterleaveTest::interleavedDataNoAttributes, + &InterleaveTest::interleavedDataNoVertices, + &InterleaveTest::interleavedDataNotInterleaved, + &InterleaveTest::interleavedLayout, &InterleaveTest::interleavedLayoutExtra, &InterleaveTest::interleavedLayoutExtraAliased, @@ -337,6 +347,76 @@ void InterleaveTest::isInterleavedAttributeAcrossStride() { CORRADE_VERIFY(!MeshTools::isInterleaved(data2)); } +void InterleaveTest::interleavedData() { + Containers::Array vertexData{100 + 3*40}; + Containers::StridedArrayView1D normals{vertexData, + reinterpret_cast(vertexData.data() + 100 + 24), 3, 40}; + Containers::StridedArrayView1D positions{vertexData, + reinterpret_cast(vertexData.data() + 100 + 5), 3, 40}; + + Trade::MeshData data{MeshPrimitive::Triangles, std::move(vertexData), { + Trade::MeshAttributeData{Trade::MeshAttribute::Normal, normals}, + Trade::MeshAttributeData{Trade::MeshAttribute::Position, positions} + }}; + + CORRADE_VERIFY(MeshTools::isInterleaved(data)); + Containers::StridedArrayView2D interleaved = MeshTools::interleavedData(data); + CORRADE_COMPARE(interleaved.data(), positions.data()); + CORRADE_COMPARE(interleaved.size()[0], 3); + CORRADE_COMPARE(interleaved.size()[1], 31); + CORRADE_COMPARE(interleaved.stride()[0], 40); + CORRADE_COMPARE(interleaved.stride()[1], 1); +} + +void InterleaveTest::interleavedDataNoAttributes() { + char a[1]; + Trade::MeshData data{MeshPrimitive::Lines, {}, a, {}, 15}; + CORRADE_VERIFY(MeshTools::isInterleaved(data)); + Containers::StridedArrayView2D interleaved = MeshTools::interleavedData(data); + CORRADE_COMPARE(interleaved.data(), static_cast(a)); + CORRADE_COMPARE(interleaved.size()[0], 15); + CORRADE_COMPARE(interleaved.size()[1], 0); + CORRADE_COMPARE(interleaved.stride()[0], 0); + CORRADE_COMPARE(interleaved.stride()[1], 1); +} + +void InterleaveTest::interleavedDataNoVertices() { + struct Vertex { + Vector3 normal; + Vector3 position; + }; + Vertex a[1]; + Trade::MeshData data{MeshPrimitive::Triangles, {}, a, { + Trade::MeshAttributeData{Trade::MeshAttribute::Normal, + Containers::stridedArrayView(a, &a[0].normal, 0, sizeof(Vertex))}, + Trade::MeshAttributeData{Trade::MeshAttribute::Position, + Containers::stridedArrayView(a, &a[0].position, 0, sizeof(Vertex))} + }}; + + CORRADE_VERIFY(MeshTools::isInterleaved(data)); + Containers::StridedArrayView2D interleaved = MeshTools::interleavedData(data); + CORRADE_COMPARE(interleaved.data(), static_cast(a)); + CORRADE_COMPARE(interleaved.size()[0], 0); + CORRADE_COMPARE(interleaved.size()[1], sizeof(Vertex)); + CORRADE_COMPARE(interleaved.stride()[0], sizeof(Vertex)); + CORRADE_COMPARE(interleaved.stride()[1], 1); +} + +void InterleaveTest::interleavedDataNotInterleaved() { + Containers::Array vertexData{100 + 3*20}; + Trade::MeshAttributeData positions{Trade::MeshAttribute::Position, + Containers::arrayCast(vertexData.suffix(100).prefix(3*8))}; + Trade::MeshAttributeData normals{Trade::MeshAttribute::Normal, + Containers::arrayCast(vertexData.suffix(100).suffix(3*8))}; + + Trade::MeshData data{MeshPrimitive::Triangles, std::move(vertexData), {positions, normals}}; + + std::ostringstream out; + Error redirectError{&out}; + MeshTools::interleavedData(data); + CORRADE_COMPARE(out.str(), "MeshTools::interleavedData(): the mesh is not interleaved\n"); +} + void InterleaveTest::interleavedLayout() { Containers::Array indexData{6}; Containers::Array vertexData{3*20};