From 1e85279872d4c79532e61d08d0aa819c690e3f19 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Vladim=C3=ADr=20Vondru=C5=A1?= Date: Thu, 16 Jan 2020 20:20:31 +0100 Subject: [PATCH] MeshTools: implement interleavedLayout(). This was originally meant to be an interleave() that operates on MeshData, but later I realized I need the same logic in duplicate(), so turned it into a private function. Now I am pretty sure I'll be using this function in *many* importer plugins :D --- doc/changelog.dox | 2 + doc/snippets/MagnumMeshTools.cpp | 35 +++ src/Magnum/MeshTools/CMakeLists.txt | 2 +- src/Magnum/MeshTools/Interleave.cpp | 85 ++++++ src/Magnum/MeshTools/Interleave.h | 37 ++- src/Magnum/MeshTools/Test/CMakeLists.txt | 2 +- src/Magnum/MeshTools/Test/InterleaveTest.cpp | 283 ++++++++++++++++++- 7 files changed, 442 insertions(+), 4 deletions(-) diff --git a/doc/changelog.dox b/doc/changelog.dox index 80c3892ec..77eaf7603 100644 --- a/doc/changelog.dox +++ b/doc/changelog.dox @@ -111,6 +111,8 @@ See also: - New @ref MeshTools::isInterleaved() utility for checking if @ref Trade::MeshData is interleaved +- Added @ref MeshTools::interleavedLayout() for convenient creation of an + interleaved mesh layout using the new @ref Trade::MeshData API - Added @ref MeshTools::subdivideInPlace() for allocation-less mesh subdivision - New @ref MeshTools::removeDuplicatesInPlace() variant that works on diff --git a/doc/snippets/MagnumMeshTools.cpp b/doc/snippets/MagnumMeshTools.cpp index e3e64c468..824fcf821 100644 --- a/doc/snippets/MagnumMeshTools.cpp +++ b/doc/snippets/MagnumMeshTools.cpp @@ -32,6 +32,7 @@ #include "Magnum/MeshTools/Interleave.h" #include "Magnum/MeshTools/RemoveDuplicates.h" #include "Magnum/MeshTools/Transform.h" +#include "Magnum/Trade/MeshData.h" using namespace Magnum; using namespace Magnum::Math::Literals; @@ -96,6 +97,40 @@ auto data = MeshTools::interleave(positions, weights, 2, vertexColors, 1); /* [interleave2] */ } +{ +Trade::MeshData data{MeshPrimitive::Lines, 0}; +UnsignedInt vertexCount{}; +Containers::Array indexData; +/* [interleavedLayout-extra] */ +Containers::ArrayView attributes = + data.attributeData(); + +/* Take just positions and normals and add a four-byte padding in between */ +Trade::MeshData layout = MeshTools::interleavedLayout( + Trade::MeshData{MeshPrimitive::Triangles, 0}, vertexCount, { + attributes[data.attributeId(Trade::MeshAttribute::Position)], + Trade::MeshAttributeData{4}, + attributes[data.attributeId(Trade::MeshAttribute::Normal)] + }); +/* [interleavedLayout-extra] */ +} + +{ +Trade::MeshData data{MeshPrimitive::Lines, 0}; +Containers::ArrayView extraAttributes; +UnsignedInt vertexCount{}; +Containers::Array indexData; +/* [interleavedLayout-indices] */ +Trade::MeshData layout = + MeshTools::interleavedLayout(data, vertexCount, extraAttributes); + +Trade::MeshIndexData indices; +Trade::MeshData indexed{data.primitive(), + std::move(indexData), indices, + layout.releaseVertexData(), layout.releaseAttributeData()}; +/* [interleavedLayout-indices] */ +} + { /* [removeDuplicates] */ Containers::ArrayView data; diff --git a/src/Magnum/MeshTools/CMakeLists.txt b/src/Magnum/MeshTools/CMakeLists.txt index 51d3b4727..be538c5e9 100644 --- a/src/Magnum/MeshTools/CMakeLists.txt +++ b/src/Magnum/MeshTools/CMakeLists.txt @@ -25,7 +25,6 @@ # Files shared between main library and unit test library set(MagnumMeshTools_SRCS - Interleave.cpp Tipsify.cpp) # Files compiled with different flags for main library and unit test library @@ -35,6 +34,7 @@ set(MagnumMeshTools_GracefulAssert_SRCS Duplicate.cpp FlipNormals.cpp GenerateNormals.cpp + Interleave.cpp RemoveDuplicates.cpp) set(MagnumMeshTools_HEADERS diff --git a/src/Magnum/MeshTools/Interleave.cpp b/src/Magnum/MeshTools/Interleave.cpp index 0a6d4d150..75db8a67f 100644 --- a/src/Magnum/MeshTools/Interleave.cpp +++ b/src/Magnum/MeshTools/Interleave.cpp @@ -49,4 +49,89 @@ bool isInterleaved(const Trade::MeshData& data) { return maxOffset - minOffset <= stride; } +Trade::MeshData interleavedLayout(const Trade::MeshData& data, const UnsignedInt vertexCount, const Containers::ArrayView extra) { + /* If there are no attributes, bail -- return an empty mesh with desired + vertex count but nothing else */ + if(!data.attributeCount() && extra.empty()) + return Trade::MeshData{data.primitive(), vertexCount}; + + const bool interleaved = isInterleaved(data); + + /* If the mesh is already interleaved, use the original stride to + preserve all padding, but remove the initial offset. Otherwise calculate + a tightly-packed stride. */ + std::size_t stride; + std::size_t minOffset; + if(interleaved && data.attributeCount()) { + stride = data.attributeStride(0); + minOffset = ~std::size_t{}; + for(UnsignedInt i = 0, max = data.attributeCount(); i != max; ++i) + minOffset = Math::min(minOffset, data.attributeOffset(i)); + } else { + stride = 0; + minOffset = 0; + for(UnsignedInt i = 0, max = data.attributeCount(); i != max; ++i) + stride += vertexFormatSize(data.attributeFormat(i)); + } + + /* Add the extra attributes and explicit padding */ + std::size_t extraAttributeCount = 0; + for(std::size_t i = 0; i != extra.size(); ++i) { + if(extra[i].format() == VertexFormat{}) { + CORRADE_ASSERT(extra[i].data().stride() > 0 || stride >= std::size_t(-extra[i].data().stride()), + "MeshTools::interleavedLayout(): negative padding" << extra[i].data().stride() << "in extra attribute" << i << "too large for stride" << stride, (Trade::MeshData{MeshPrimitive::Points, 0})); + stride += extra[i].data().stride(); + } else { + stride += vertexFormatSize(extra[i].format()); + ++extraAttributeCount; + } + } + + /* Allocate new data and attribute array */ + Containers::Array vertexData{Containers::NoInit, stride*vertexCount}; + Containers::Array attributeData{data.attributeCount() + extraAttributeCount}; + + /* Copy existing attribute layout. If the original is already interleaved, + preserve relative attribute offsets, otherwise pack tightly. */ + std::size_t offset = 0; + for(UnsignedInt i = 0; i != data.attributeCount(); ++i) { + if(interleaved) offset = data.attributeOffset(i) - minOffset; + + attributeData[i] = Trade::MeshAttributeData{ + data.attributeName(i), data.attributeFormat(i), + Containers::StridedArrayView1D{vertexData, vertexData + offset, + vertexCount, std::ptrdiff_t(stride)}}; + + if(!interleaved) offset += vertexFormatSize(data.attributeFormat(i)); + } + + /* In case the original is already interleaved, set the offset for extra + attribs to the original stride to preserve also potential padding at the + end. */ + if(interleaved && data.attributeCount()) + offset = data.attributeStride(0); + + /* Mix in the extra attributes */ + UnsignedInt attributeIndex = data.attributeCount(); + for(UnsignedInt i = 0; i != extra.size(); ++i) { + /* Padding, only adjust the offset for next attribute */ + if(extra[i].format() == VertexFormat{}) { + offset += extra[i].data().stride(); + continue; + } + + attributeData[attributeIndex++] = Trade::MeshAttributeData{ + extra[i].name(), extra[i].format(), Containers::StridedArrayView1D{vertexData, vertexData + offset, + vertexCount, std::ptrdiff_t(stride)}}; + + offset += vertexFormatSize(extra[i].format()); + } + + return Trade::MeshData{data.primitive(), std::move(vertexData), std::move(attributeData)}; +} + +Trade::MeshData interleavedLayout(const Trade::MeshData& data, const UnsignedInt vertexCount, const std::initializer_list extra) { + return interleavedLayout(data, vertexCount, Containers::arrayView(extra)); +} + }} diff --git a/src/Magnum/MeshTools/Interleave.h b/src/Magnum/MeshTools/Interleave.h index 623c4345b..81dd6ee71 100644 --- a/src/Magnum/MeshTools/Interleave.h +++ b/src/Magnum/MeshTools/Interleave.h @@ -26,7 +26,7 @@ */ /** @file - * @brief Function @ref Magnum::MeshTools::interleave(), @ref Magnum::MeshTools::interleaveInto(), @ref Magnum::MeshTools::isInterleaved() + * @brief Function @ref Magnum::MeshTools::interleave(), @ref Magnum::MeshTools::interleaveInto(), @ref Magnum::MeshTools::isInterleaved(), @ref Magnum::MeshTools::interleavedLayout() */ #include @@ -199,6 +199,41 @@ or no attributes. */ MAGNUM_MESHTOOLS_EXPORT bool isInterleaved(const Trade::MeshData& data); +/** +@brief Create an interleaved mesh layout +@m_since_latest + +Returns a @ref Trade::MeshData instance with its vertex data allocated for +@p vertexCount vertices containing attributes from both @p data and @p extra +interleaved together. No data is actually copied, only an interleaved layout is +created. If @p data is already interleaved, keeps the attributes in the same +layout, potentially extending them with @p extra. The @p extra attributes, if +any, are interleaved together with existing attributes. Returned instance +vertex data flags have both @ref Trade::DataFlag::Mutable and @ref Trade::DataFlag::Owned, so mutable attribute access is guaranteed. + +For greater control you can also pass an empty @ref Trade::MeshData instance +and fill @p extra with attributes cherry-picked from +@ref Trade::MeshData::attributeData() of an existing instance. By default the +attributes are tightly packed, you can add arbitrary padding using instances +constructed via @ref Trade::MeshAttributeData::MeshAttributeData(Int). +Example: + +@snippet MagnumMeshTools.cpp interleavedLayout-extra + +This function doesn't preserve index data information in any way, making the +output non-indexed. If you want to preserve index data, create a new indexed +instance with attribute and vertex data transferred from the returned instance: + +@snippet MagnumMeshTools.cpp interleavedLayout-indices +*/ +MAGNUM_MESHTOOLS_EXPORT Trade::MeshData interleavedLayout(const Trade::MeshData& data, UnsignedInt vertexCount, Containers::ArrayView extra = {}); + +/** + * @overload + * @m_since_latest + */ +MAGNUM_MESHTOOLS_EXPORT Trade::MeshData interleavedLayout(const Trade::MeshData& data, UnsignedInt vertexCount, std::initializer_list extra); + }} #endif diff --git a/src/Magnum/MeshTools/Test/CMakeLists.txt b/src/Magnum/MeshTools/Test/CMakeLists.txt index aa15700bc..899dc8826 100644 --- a/src/Magnum/MeshTools/Test/CMakeLists.txt +++ b/src/Magnum/MeshTools/Test/CMakeLists.txt @@ -28,7 +28,7 @@ corrade_add_test(MeshToolsCompressIndicesTest CompressIndicesTest.cpp LIBRARIES corrade_add_test(MeshToolsDuplicateTest DuplicateTest.cpp LIBRARIES MagnumMeshToolsTestLib) corrade_add_test(MeshToolsFlipNormalsTest FlipNormalsTest.cpp LIBRARIES MagnumMeshToolsTestLib) corrade_add_test(MeshToolsGenerateNormalsTest GenerateNormalsTest.cpp LIBRARIES MagnumMeshToolsTestLib MagnumPrimitives) -corrade_add_test(MeshToolsInterleaveTest InterleaveTest.cpp LIBRARIES MagnumMeshTools) +corrade_add_test(MeshToolsInterleaveTest InterleaveTest.cpp LIBRARIES MagnumMeshToolsTestLib) corrade_add_test(MeshToolsRemoveDuplicatesTest RemoveDuplicatesTest.cpp LIBRARIES MagnumMeshToolsTestLib) corrade_add_test(MeshToolsSubdivideTest SubdivideTest.cpp LIBRARIES Magnum) corrade_add_test(MeshToolsTipsifyTest TipsifyTest.cpp LIBRARIES MagnumMeshTools) diff --git a/src/Magnum/MeshTools/Test/InterleaveTest.cpp b/src/Magnum/MeshTools/Test/InterleaveTest.cpp index 8cb7267d6..f962a6c47 100644 --- a/src/Magnum/MeshTools/Test/InterleaveTest.cpp +++ b/src/Magnum/MeshTools/Test/InterleaveTest.cpp @@ -55,6 +55,16 @@ struct InterleaveTest: Corrade::TestSuite::Tester { void isInterleavedAliased(); void isInterleavedUnordered(); void isInterleavedAttributeAcrossStride(); + + void interleavedLayout(); + void interleavedLayoutExtra(); + void interleavedLayoutExtraAliased(); + void interleavedLayoutExtraTooNegativePadding(); + void interleavedLayoutExtraOnly(); + void interleavedLayoutAlreadyInterleaved(); + void interleavedLayoutAlreadyInterleavedAliased(); + void interleavedLayoutAlreadyInterleavedExtra(); + void interleavedLayoutNothing(); }; InterleaveTest::InterleaveTest() { @@ -73,7 +83,17 @@ InterleaveTest::InterleaveTest() { &InterleaveTest::isInterleavedGaps, &InterleaveTest::isInterleavedAliased, &InterleaveTest::isInterleavedUnordered, - &InterleaveTest::isInterleavedAttributeAcrossStride}); + &InterleaveTest::isInterleavedAttributeAcrossStride, + + &InterleaveTest::interleavedLayout, + &InterleaveTest::interleavedLayoutExtra, + &InterleaveTest::interleavedLayoutExtraAliased, + &InterleaveTest::interleavedLayoutExtraTooNegativePadding, + &InterleaveTest::interleavedLayoutExtraOnly, + &InterleaveTest::interleavedLayoutAlreadyInterleaved, + &InterleaveTest::interleavedLayoutAlreadyInterleavedAliased, + &InterleaveTest::interleavedLayoutAlreadyInterleavedExtra, + &InterleaveTest::interleavedLayoutNothing}); } void InterleaveTest::attributeCount() { @@ -285,6 +305,267 @@ void InterleaveTest::isInterleavedAttributeAcrossStride() { CORRADE_VERIFY(!MeshTools::isInterleaved(data2)); } +void InterleaveTest::interleavedLayout() { + Containers::Array indexData{6}; + Containers::Array vertexData{3*20}; + Trade::MeshAttributeData positions{Trade::MeshAttribute::Position, + Containers::arrayCast(vertexData.prefix(3*8))}; + Trade::MeshAttributeData normals{Trade::MeshAttribute::Normal, + Containers::arrayCast(vertexData.suffix(3*8))}; + + Trade::MeshIndexData indices{Containers::arrayCast(indexData)}; + Trade::MeshData data{MeshPrimitive::TriangleFan, + std::move(indexData), indices, + std::move(vertexData), {positions, normals}}; + CORRADE_VERIFY(!MeshTools::isInterleaved(data)); + + Trade::MeshData layout = MeshTools::interleavedLayout(data, 10); + CORRADE_VERIFY(MeshTools::isInterleaved(layout)); + CORRADE_COMPARE(layout.primitive(), MeshPrimitive::TriangleFan); + CORRADE_VERIFY(!layout.isIndexed()); /* Indices are not preserved */ + CORRADE_COMPARE(layout.attributeCount(), 2); + CORRADE_COMPARE(layout.attributeName(0), Trade::MeshAttribute::Position); + CORRADE_COMPARE(layout.attributeName(1), Trade::MeshAttribute::Normal); + CORRADE_COMPARE(layout.attributeFormat(0), VertexFormat::Vector2); + CORRADE_COMPARE(layout.attributeFormat(1), VertexFormat::Vector3); + CORRADE_COMPARE(layout.attributeStride(0), 20); + CORRADE_COMPARE(layout.attributeStride(1), 20); + CORRADE_COMPARE(layout.attributeOffset(0), 0); + CORRADE_COMPARE(layout.attributeOffset(1), 8); + CORRADE_COMPARE(layout.vertexCount(), 10); + /* Needs to be like this so we can modify the data */ + CORRADE_COMPARE(layout.vertexDataFlags(), Trade::DataFlag::Mutable|Trade::DataFlag::Owned); + CORRADE_VERIFY(layout.vertexData()); + CORRADE_COMPARE(layout.vertexData().size(), 10*20); +} + +void InterleaveTest::interleavedLayoutExtra() { + Containers::Array vertexData{3*20}; + Trade::MeshAttributeData positions{Trade::MeshAttribute::Position, + Containers::arrayCast(vertexData.prefix(3*8))}; + Trade::MeshAttributeData normals{Trade::MeshAttribute::Normal, + Containers::arrayCast(vertexData.suffix(3*8))}; + + Trade::MeshData data{MeshPrimitive::Triangles, + std::move(vertexData), {positions, normals}}; + CORRADE_VERIFY(!MeshTools::isInterleaved(data)); + + Trade::MeshData layout = MeshTools::interleavedLayout(data, 7, { + Trade::MeshAttributeData{1}, + Trade::MeshAttributeData{Trade::meshAttributeCustom(15), + VertexFormat::UnsignedShort, nullptr}, + Trade::MeshAttributeData{1}, + Trade::MeshAttributeData{Trade::MeshAttribute::Color, + VertexFormat::Vector3, nullptr}, + Trade::MeshAttributeData{4} + }); + CORRADE_VERIFY(MeshTools::isInterleaved(layout)); + CORRADE_COMPARE(layout.attributeCount(), 4); + CORRADE_COMPARE(layout.attributeName(0), Trade::MeshAttribute::Position); + CORRADE_COMPARE(layout.attributeName(1), Trade::MeshAttribute::Normal); + CORRADE_COMPARE(layout.attributeName(2), Trade::meshAttributeCustom(15)); + CORRADE_COMPARE(layout.attributeName(3), Trade::MeshAttribute::Color); + CORRADE_COMPARE(layout.attributeFormat(0), VertexFormat::Vector2); + CORRADE_COMPARE(layout.attributeFormat(1), VertexFormat::Vector3); + CORRADE_COMPARE(layout.attributeFormat(2), VertexFormat::UnsignedShort); + CORRADE_COMPARE(layout.attributeFormat(3), VertexFormat::Vector3); + CORRADE_COMPARE(layout.attributeStride(0), 40); + CORRADE_COMPARE(layout.attributeStride(1), 40); + CORRADE_COMPARE(layout.attributeStride(2), 40); + CORRADE_COMPARE(layout.attributeStride(3), 40); + CORRADE_COMPARE(layout.attributeOffset(0), 0); + CORRADE_COMPARE(layout.attributeOffset(1), 8); + CORRADE_COMPARE(layout.attributeOffset(2), 21); + CORRADE_COMPARE(layout.attributeOffset(3), 24); + CORRADE_COMPARE(layout.vertexCount(), 7); + CORRADE_COMPARE(layout.vertexData().size(), 7*40); +} + +void InterleaveTest::interleavedLayoutExtraAliased() { + Containers::Array vertexData{3*12}; + Trade::MeshAttributeData positions{Trade::MeshAttribute::Position, + Containers::StridedArrayView1D{vertexData, reinterpret_cast(vertexData.data()), 3, 12}}; + Trade::MeshData data{MeshPrimitive::Triangles, + std::move(vertexData), {positions}}; + + Trade::MeshData layout = MeshTools::interleavedLayout(data, 100, { + /* Normals at the same place as positions */ + Trade::MeshAttributeData{-12}, + Trade::MeshAttributeData{Trade::MeshAttribute::Normal, + VertexFormat::Vector3, positions.data()} + }); + CORRADE_VERIFY(MeshTools::isInterleaved(layout)); + CORRADE_COMPARE(layout.attributeCount(), 2); + CORRADE_COMPARE(layout.attributeName(0), Trade::MeshAttribute::Position); + CORRADE_COMPARE(layout.attributeName(1), Trade::MeshAttribute::Normal); + CORRADE_COMPARE(layout.attributeFormat(0), VertexFormat::Vector2); + CORRADE_COMPARE(layout.attributeFormat(1), VertexFormat::Vector3); + CORRADE_COMPARE(layout.attributeStride(0), 12); + CORRADE_COMPARE(layout.attributeStride(1), 12); + CORRADE_COMPARE(layout.attributeOffset(0), 0); + CORRADE_COMPARE(layout.attributeOffset(1), 0); /* aliases */ + CORRADE_COMPARE(layout.vertexCount(), 100); + CORRADE_COMPARE(layout.vertexData().size(), 100*12); +} + +void InterleaveTest::interleavedLayoutExtraTooNegativePadding() { + Containers::Array vertexData{3*12}; + Trade::MeshAttributeData positions{Trade::MeshAttribute::Position, + Containers::StridedArrayView1D{vertexData, reinterpret_cast(vertexData.data()), 3, 12}}; + Trade::MeshData data{MeshPrimitive::Triangles, + std::move(vertexData), {positions}}; + + std::ostringstream out; + Error redirectError{&out}; + MeshTools::interleavedLayout(data, 100, { + Trade::MeshAttributeData{Trade::MeshAttribute::Normal, + VertexFormat::Vector3, positions.data()}, + Trade::MeshAttributeData{-25} + }); + CORRADE_COMPARE(out.str(), "MeshTools::interleavedLayout(): negative padding -25 in extra attribute 1 too large for stride 24\n"); +} + +void InterleaveTest::interleavedLayoutExtraOnly() { + Trade::MeshData data{MeshPrimitive::Triangles, 0}; + + Trade::MeshData layout = MeshTools::interleavedLayout(data, 10, { + Trade::MeshAttributeData{4}, + Trade::MeshAttributeData{Trade::MeshAttribute::Position, + VertexFormat::Vector2, nullptr}, + Trade::MeshAttributeData{Trade::MeshAttribute::Normal, + VertexFormat::Vector3, nullptr} + }); + CORRADE_VERIFY(MeshTools::isInterleaved(layout)); + CORRADE_COMPARE(layout.attributeCount(), 2); + CORRADE_COMPARE(layout.attributeName(0), Trade::MeshAttribute::Position); + CORRADE_COMPARE(layout.attributeName(1), Trade::MeshAttribute::Normal); + CORRADE_COMPARE(layout.attributeFormat(0), VertexFormat::Vector2); + CORRADE_COMPARE(layout.attributeFormat(1), VertexFormat::Vector3); + CORRADE_COMPARE(layout.attributeStride(0), 24); + CORRADE_COMPARE(layout.attributeStride(1), 24); + CORRADE_COMPARE(layout.attributeOffset(0), 4); + CORRADE_COMPARE(layout.attributeOffset(1), 12); + CORRADE_COMPARE(layout.vertexCount(), 10); + CORRADE_COMPARE(layout.vertexData().size(), 10*24); +} + +void InterleaveTest::interleavedLayoutAlreadyInterleaved() { + Containers::Array indexData{6}; + /* Test also removing the initial offset */ + Containers::Array vertexData{100 + 3*24}; + Trade::MeshAttributeData positions{Trade::MeshAttribute::Position, + Containers::StridedArrayView1D{vertexData, reinterpret_cast(vertexData.data() + 100), 3, 24}}; + Trade::MeshAttributeData normals{Trade::MeshAttribute::Normal, + Containers::StridedArrayView1D{vertexData, reinterpret_cast(vertexData.data() + 100 + 10), 3, 24}}; + + Trade::MeshIndexData indices{Containers::arrayCast(indexData)}; + Trade::MeshData data{MeshPrimitive::Triangles, + std::move(indexData), indices, + std::move(vertexData), {positions, normals}}; + CORRADE_VERIFY(MeshTools::isInterleaved(data)); + + Trade::MeshData layout = MeshTools::interleavedLayout(data, 10); + CORRADE_VERIFY(MeshTools::isInterleaved(layout)); + CORRADE_VERIFY(!layout.isIndexed()); /* Indices are not preserved */ + CORRADE_COMPARE(layout.attributeCount(), 2); + CORRADE_COMPARE(layout.attributeName(0), Trade::MeshAttribute::Position); + CORRADE_COMPARE(layout.attributeName(1), Trade::MeshAttribute::Normal); + CORRADE_COMPARE(layout.attributeFormat(0), VertexFormat::Vector2); + CORRADE_COMPARE(layout.attributeFormat(1), VertexFormat::Vector3); + /* Original stride should be preserved */ + CORRADE_COMPARE(layout.attributeStride(0), 24); + CORRADE_COMPARE(layout.attributeStride(1), 24); + /* Relative offsets should be preserved, but the initial one removed */ + CORRADE_COMPARE(layout.attributeOffset(0), 0); + CORRADE_COMPARE(layout.attributeOffset(1), 10); + CORRADE_COMPARE(layout.vertexCount(), 10); + CORRADE_COMPARE(layout.vertexData().size(), 10*24); +} + +void InterleaveTest::interleavedLayoutAlreadyInterleavedAliased() { + Containers::Array indexData{6}; + Containers::Array vertexData{3*12}; + Trade::MeshAttributeData positions{Trade::MeshAttribute::Position, + Containers::StridedArrayView1D{vertexData, reinterpret_cast(vertexData.data()), 3, 12}}; + Trade::MeshAttributeData normals{Trade::MeshAttribute::Normal, + Containers::StridedArrayView1D{vertexData, reinterpret_cast(vertexData.data()), 3, 12}}; + + Trade::MeshIndexData indices{Containers::arrayCast(indexData)}; + Trade::MeshData data{MeshPrimitive::Triangles, + std::move(indexData), indices, + std::move(vertexData), {positions, normals}}; + CORRADE_VERIFY(MeshTools::isInterleaved(data)); + + Trade::MeshData layout = MeshTools::interleavedLayout(data, 10); + CORRADE_VERIFY(MeshTools::isInterleaved(layout)); + CORRADE_VERIFY(!layout.isIndexed()); /* Indices are not preserved */ + CORRADE_COMPARE(layout.attributeCount(), 2); + CORRADE_COMPARE(layout.attributeName(0), Trade::MeshAttribute::Position); + CORRADE_COMPARE(layout.attributeName(1), Trade::MeshAttribute::Normal); + CORRADE_COMPARE(layout.attributeFormat(0), VertexFormat::Vector2); + CORRADE_COMPARE(layout.attributeFormat(1), VertexFormat::Vector3); + CORRADE_COMPARE(layout.attributeStride(0), 12); + CORRADE_COMPARE(layout.attributeStride(1), 12); + CORRADE_COMPARE(layout.attributeOffset(0), 0); + CORRADE_COMPARE(layout.attributeOffset(1), 0); /* aliases */ + CORRADE_COMPARE(layout.vertexCount(), 10); + CORRADE_COMPARE(layout.vertexData().size(), 10*12); +} + +void InterleaveTest::interleavedLayoutAlreadyInterleavedExtra() { + Containers::Array vertexData{100 + 3*24}; + Trade::MeshAttributeData positions{Trade::MeshAttribute::Position, + Containers::StridedArrayView1D{vertexData, reinterpret_cast(vertexData.data() + 100), 3, 24}}; + Trade::MeshAttributeData normals{Trade::MeshAttribute::Normal, + Containers::StridedArrayView1D{vertexData, reinterpret_cast(vertexData.data() + 100 + 10), 3, 24}}; + + Trade::MeshData data{MeshPrimitive::Triangles, + std::move(vertexData), {positions, normals}}; + CORRADE_VERIFY(MeshTools::isInterleaved(data)); + + Trade::MeshData layout = MeshTools::interleavedLayout(data, 10, { + Trade::MeshAttributeData{1}, + Trade::MeshAttributeData{Trade::meshAttributeCustom(15), + VertexFormat::UnsignedShort, nullptr}, + Trade::MeshAttributeData{1}, + Trade::MeshAttributeData{Trade::MeshAttribute::Color, + VertexFormat::Vector3, nullptr}, + Trade::MeshAttributeData{4} + }); + CORRADE_VERIFY(MeshTools::isInterleaved(layout)); + CORRADE_COMPARE(layout.attributeCount(), 4); + CORRADE_COMPARE(layout.attributeName(0), Trade::MeshAttribute::Position); + CORRADE_COMPARE(layout.attributeName(1), Trade::MeshAttribute::Normal); + CORRADE_COMPARE(layout.attributeName(2), Trade::meshAttributeCustom(15)); + CORRADE_COMPARE(layout.attributeName(3), Trade::MeshAttribute::Color); + CORRADE_COMPARE(layout.attributeFormat(0), VertexFormat::Vector2); + CORRADE_COMPARE(layout.attributeFormat(1), VertexFormat::Vector3); + CORRADE_COMPARE(layout.attributeFormat(2), VertexFormat::UnsignedShort); + CORRADE_COMPARE(layout.attributeFormat(3), VertexFormat::Vector3); + /* Original stride should be preserved, with stride from extra attribs + added */ + CORRADE_COMPARE(layout.attributeStride(0), 24 + 20); + CORRADE_COMPARE(layout.attributeStride(1), 24 + 20); + CORRADE_COMPARE(layout.attributeStride(2), 24 + 20); + CORRADE_COMPARE(layout.attributeStride(3), 24 + 20); + /* Relative offsets should be preserved, but the initial one removed */ + CORRADE_COMPARE(layout.attributeOffset(0), 0); + CORRADE_COMPARE(layout.attributeOffset(1), 10); + CORRADE_COMPARE(layout.attributeOffset(2), 25); + CORRADE_COMPARE(layout.attributeOffset(3), 28); + CORRADE_COMPARE(layout.vertexCount(), 10); + CORRADE_COMPARE(layout.vertexData().size(), 10*44); +} + +void InterleaveTest::interleavedLayoutNothing() { + Trade::MeshData layout = MeshTools::interleavedLayout(Trade::MeshData{MeshPrimitive::Points, 25}, 10); + CORRADE_VERIFY(MeshTools::isInterleaved(layout)); + CORRADE_COMPARE(layout.attributeCount(), 0); + CORRADE_COMPARE(layout.vertexCount(), 10); + CORRADE_VERIFY(!layout.vertexData()); + CORRADE_COMPARE(layout.vertexData().size(), 0); +} + }}}} CORRADE_TEST_MAIN(Magnum::MeshTools::Test::InterleaveTest)