diff --git a/doc/changelog.dox b/doc/changelog.dox index 1f291d811..81d97b130 100644 --- a/doc/changelog.dox +++ b/doc/changelog.dox @@ -115,8 +115,9 @@ See also: @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::interleave(const Trade::MeshData&, Containers::ArrayView) - and @ref MeshTools::duplicate(const Trade::MeshData&, Containers::ArrayView) +- Added @ref MeshTools::interleave(const Trade::MeshData&, Containers::ArrayView), + @ref MeshTools::duplicate(const Trade::MeshData&, Containers::ArrayView) + and @ref MeshTools::compressIndices(const Trade::MeshData&, MeshIndexType) that work directly on the new @ref Trade::MeshData API - Added @ref MeshTools::subdivideInPlace() for allocation-less mesh subdivision diff --git a/src/Magnum/MeshTools/CompressIndices.cpp b/src/Magnum/MeshTools/CompressIndices.cpp index b9bf7091a..81ecbd47b 100644 --- a/src/Magnum/MeshTools/CompressIndices.cpp +++ b/src/Magnum/MeshTools/CompressIndices.cpp @@ -28,8 +28,10 @@ #include #include #include +#include #include "Magnum/Math/FunctionsBatch.h" +#include "Magnum/Trade/MeshData.h" namespace Magnum { namespace MeshTools { @@ -115,6 +117,59 @@ std::pair, MeshIndexType> compressIndices(const Containe return compressIndices(indices, MeshIndexType::UnsignedShort, offset); } +Trade::MeshData compressIndices(Trade::MeshData&& data, MeshIndexType atLeast) { + CORRADE_ASSERT(data.isIndexed(), "MeshTools::compressIndices(): mesh data not indexed", (Trade::MeshData{MeshPrimitive::Triangles, 0})); + + /* Transfer vertex data as-is, as those don't need any changes. Release if + possible. */ + Containers::Array vertexData; + const UnsignedInt vertexCount = data.vertexCount(); + if(data.vertexDataFlags() & Trade::DataFlag::Owned) + vertexData = data.releaseVertexData(); + else { + vertexData = Containers::Array{Containers::NoInit, data.vertexData().size()}; + Utility::copy(data.vertexData(), vertexData); + } + + /* Compress the indices */ + UnsignedInt offset; + std::pair, MeshIndexType> result; + if(data.indexType() == MeshIndexType::UnsignedInt) { + auto indices = data.indices(); + offset = Math::min(indices); + result = compressIndicesImplementation(indices, atLeast, offset); + } else if(data.indexType() == MeshIndexType::UnsignedShort) { + auto indices = data.indices(); + offset = Math::min(indices); + result = compressIndicesImplementation(indices, atLeast, offset); + } else { + CORRADE_INTERNAL_ASSERT(data.indexType() == MeshIndexType::UnsignedByte); + auto indices = data.indices(); + offset = Math::min(indices); + result = compressIndicesImplementation(indices, atLeast, offset); + } + + /* Recreate the attribute array */ + const UnsignedInt newVertexCount = vertexCount - offset; + Containers::Array attributeData{data.attributeCount()}; + for(UnsignedInt i = 0, max = attributeData.size(); i != max; ++i) { + const UnsignedInt stride = data.attributeStride(i); + attributeData[i] = Trade::MeshAttributeData{data.attributeName(i), + data.attributeFormat(i), + Containers::StridedArrayView1D{vertexData, vertexData.data() + data.attributeOffset(i) + offset*stride, newVertexCount, stride}}; + } + + Trade::MeshIndexData indices{result.second, result.first}; + return Trade::MeshData{data.primitive(), std::move(result.first), indices, + std::move(vertexData), std::move(attributeData)}; +} + +Trade::MeshData compressIndices(const Trade::MeshData& data, MeshIndexType atLeast) { + return compressIndices(Trade::MeshData{data.primitive(), + {}, data.indexData(), Trade::MeshIndexData{data.indices()}, + {}, data.vertexData(), Trade::meshAttributeDataNonOwningArray(data.attributeData())}, atLeast); +} + #ifdef MAGNUM_BUILD_DEPRECATED std::tuple, MeshIndexType, UnsignedInt, UnsignedInt> compressIndices(const std::vector& indices) { const auto minmax = Math::minmax(indices); diff --git a/src/Magnum/MeshTools/CompressIndices.h b/src/Magnum/MeshTools/CompressIndices.h index a594a41d5..091e83829 100644 --- a/src/Magnum/MeshTools/CompressIndices.h +++ b/src/Magnum/MeshTools/CompressIndices.h @@ -35,6 +35,7 @@ #include "Magnum/Mesh.h" #include "Magnum/MeshTools/visibility.h" +#include "Magnum/Trade/Trade.h" #ifdef MAGNUM_BUILD_DEPRECATED #include @@ -136,6 +137,30 @@ with @p atLeast set to @ref MeshIndexType::UnsignedShort. */ MAGNUM_MESHTOOLS_EXPORT std::pair, MeshIndexType> compressIndices(const Containers::StridedArrayView2D& indices, Long offset); +/** +@brief Compress mesh data indices +@m_since_latest + +Does the same as @ref compressIndices(const Containers::StridedArrayView2D&, MeshIndexType, Long), +but together with adjusting vertex attribute offsets in the passed +@ref Trade::MeshData instance. This function will unconditionally make a copy +of all vertex data, use @ref compressIndices(Trade::MeshData&&, MeshIndexType) +to avoid that copy. +*/ +MAGNUM_MESHTOOLS_EXPORT Trade::MeshData compressIndices(const Trade::MeshData& data, MeshIndexType atLeast = MeshIndexType::UnsignedShort); + +/** +@brief Compress mesh data indices +@m_since_latest + +Compared to @ref compressIndices(const Trade::MeshData&, MeshIndexType) this +function can transfer ownership of @p data vertex buffer (in case it is +owned) to the returned instance instead of making a copy of it. Index and +attribute data are copied always. +@see @ref Trade::MeshData::vertexDataFlags() +*/ +MAGNUM_MESHTOOLS_EXPORT Trade::MeshData compressIndices(Trade::MeshData&& data, MeshIndexType atLeast = MeshIndexType::UnsignedShort); + #ifdef MAGNUM_BUILD_DEPRECATED /** @brief Compress vertex indices diff --git a/src/Magnum/MeshTools/Test/CompressIndicesTest.cpp b/src/Magnum/MeshTools/Test/CompressIndicesTest.cpp index 611601291..d05320743 100644 --- a/src/Magnum/MeshTools/Test/CompressIndicesTest.cpp +++ b/src/Magnum/MeshTools/Test/CompressIndicesTest.cpp @@ -32,8 +32,9 @@ #include #include -#include "Magnum/Math/TypeTraits.h" +#include "Magnum/Math/Vector3.h" #include "Magnum/MeshTools/CompressIndices.h" +#include "Magnum/Trade/MeshData.h" namespace Magnum { namespace MeshTools { namespace Test { namespace { @@ -53,6 +54,10 @@ struct CompressIndicesTest: TestSuite::Tester { void compressDeprecated(); #endif + template void compressMeshData(); + void compressMeshDataMove(); + void compressMeshDataNonIndexed(); + void compressAsShort(); }; @@ -70,11 +75,16 @@ CompressIndicesTest::CompressIndicesTest() { &CompressIndicesTest::compressOffsetNegative, &CompressIndicesTest::compressErasedNonContiguous, &CompressIndicesTest::compressErasedWrongIndexSize, - #ifdef MAGNUM_BUILD_DEPRECATED &CompressIndicesTest::compressDeprecated, #endif + &CompressIndicesTest::compressMeshData, + &CompressIndicesTest::compressMeshData, + &CompressIndicesTest::compressMeshData, + &CompressIndicesTest::compressMeshDataMove, + &CompressIndicesTest::compressMeshDataNonIndexed, + &CompressIndicesTest::compressAsShort}); } @@ -228,6 +238,86 @@ void CompressIndicesTest::compressDeprecated() { } #endif +template void CompressIndicesTest::compressMeshData() { + setTestCaseTemplateName(Math::TypeTraits::name()); + + struct { + Vector2 positions[103]; + Vector3 normals[103]; + } vertexData{}; + vertexData.positions[100] = {1.3f, 0.3f}; + vertexData.positions[101] = {0.87f, 1.1f}; + vertexData.positions[102] = {1.0f, -0.5f}; + vertexData.normals[100] = Vector3::xAxis(); + vertexData.normals[101] = Vector3::yAxis(); + vertexData.normals[102] = Vector3::zAxis(); + + T indices[] = {102, 101, 100, 101, 102}; + Trade::MeshData data{MeshPrimitive::TriangleFan, + {}, indices, Trade::MeshIndexData{indices}, + {}, Containers::arrayView(&vertexData, 1), { + Trade::MeshAttributeData{Trade::MeshAttribute::Position, Containers::arrayView(vertexData.positions)}, + Trade::MeshAttributeData{Trade::MeshAttribute::Normal, Containers::arrayView(vertexData.normals)} + }}; + CORRADE_COMPARE(data.vertexCount(), 103); + CORRADE_COMPARE(data.attributeOffset(0), 0); + CORRADE_COMPARE(data.attributeOffset(1), 103*sizeof(Vector2)); + + Trade::MeshData compressed = compressIndices(data); + CORRADE_COMPARE(compressed.indexCount(), 5); + CORRADE_COMPARE(compressed.indexType(), MeshIndexType::UnsignedShort); + CORRADE_COMPARE_AS(compressed.indices(), + Containers::arrayView({2, 1, 0, 1, 2}), + TestSuite::Compare::Container); + CORRADE_COMPARE(compressed.vertexCount(), 3); + CORRADE_COMPARE(compressed.attributeOffset(0), 100*sizeof(Vector2)); + CORRADE_COMPARE(compressed.attributeOffset(1), 103*sizeof(Vector2) + 100*sizeof(Vector3)); + CORRADE_COMPARE_AS(compressed.attribute(Trade::MeshAttribute::Position), + Containers::arrayView({{1.3f, 0.3f}, {0.87f, 1.1f}, {1.0f, -0.5f}}), + TestSuite::Compare::Container); + CORRADE_COMPARE_AS(compressed.attribute(Trade::MeshAttribute::Normal), + Containers::arrayView({Vector3::xAxis(), Vector3::yAxis(), Vector3::zAxis()}), + TestSuite::Compare::Container); +} + +void CompressIndicesTest::compressMeshDataMove() { + Containers::Array vertexData{103*24}; + Containers::StridedArrayView1D positionView{vertexData, + reinterpret_cast(vertexData.data()), 103, 8}; + Containers::StridedArrayView1D normalView{vertexData, + reinterpret_cast(vertexData.data() + 103*sizeof(Vector2)), 103, 12}; + UnsignedInt indices[] = {102, 101, 100, 101, 102}; + Trade::MeshData data{MeshPrimitive::TriangleFan, + {}, indices, Trade::MeshIndexData{indices}, + std::move(vertexData), { + Trade::MeshAttributeData{Trade::MeshAttribute::Position, positionView}, + Trade::MeshAttributeData{Trade::MeshAttribute::Normal, normalView} + }}; + CORRADE_COMPARE(data.vertexCount(), 103); + CORRADE_COMPARE(data.attributeOffset(0), 0); + CORRADE_COMPARE(data.attributeOffset(1), 103*sizeof(Vector2)); + + Trade::MeshData compressed = compressIndices(std::move(data)); + CORRADE_COMPARE(compressed.indexCount(), 5); + CORRADE_COMPARE(compressed.indexType(), MeshIndexType::UnsignedShort); + CORRADE_COMPARE_AS(compressed.indices(), + Containers::arrayView({2, 1, 0, 1, 2}), + TestSuite::Compare::Container); + CORRADE_COMPARE(compressed.vertexCount(), 3); + CORRADE_COMPARE(compressed.attributeOffset(0), 100*sizeof(Vector2)); + CORRADE_COMPARE(compressed.attributeOffset(1), 103*sizeof(Vector2) + 100*sizeof(Vector3)); + /* The vertex data should be moved, not copied */ + CORRADE_VERIFY(compressed.vertexData().data() == positionView.data()); +} + +void CompressIndicesTest::compressMeshDataNonIndexed() { + std::ostringstream out; + Error redirectError{&out}; + MeshTools::compressIndices(Trade::MeshData{MeshPrimitive::TriangleFan, 5}); + CORRADE_COMPARE(out.str(), + "MeshTools::compressIndices(): mesh data not indexed\n"); +} + void CompressIndicesTest::compressAsShort() { CORRADE_COMPARE_AS(MeshTools::compressIndicesAs({123, 456}), Containers::arrayView({123, 456}),