From bae5eecf420ee2f60b24ae3ead1e53ea7f1a1a9c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Vladim=C3=ADr=20Vondru=C5=A1?= Date: Wed, 22 Jan 2020 17:02:14 +0100 Subject: [PATCH] MeshTools: compressIndices() now takes also an offset. --- doc/changelog.dox | 8 +-- doc/snippets/MagnumMeshTools.cpp | 12 ++++ src/Magnum/MeshTools/CompressIndices.cpp | 53 +++++++++----- src/Magnum/MeshTools/CompressIndices.h | 70 ++++++++++++++++--- .../MeshTools/Test/CompressIndicesTest.cpp | 44 ++++++++++++ 5 files changed, 153 insertions(+), 34 deletions(-) diff --git a/doc/changelog.dox b/doc/changelog.dox index 4a3ea2397..474e095cc 100644 --- a/doc/changelog.dox +++ b/doc/changelog.dox @@ -212,8 +212,8 @@ See also: - Added @ref MeshTools::compressIndices() that takes a @ref Corrade::Containers::StridedArrayView instead of a @ref std::vector - and additionally allows you to specify the smallest allowed type and is - working with 8- and 16-byte index types as well + and additionally allows you to specify the smallest allowed type, offset to + apply to each index and is working with 8- and 16-byte index types as well - Added @ref MeshTools::subdivide() that operates on a (growable) @ref Corrade::Containers::Array instead of a @ref std::vector - Added @ref MeshTools::subdivideInPlace() that operates on a partially @@ -368,8 +368,8 @@ See also: @ref Trade::ImageConverterFeatures enum sets and their corresponding enums placed directly in the namespace to have them shorter and unambiguous - @cpp MeshTools::compressIndices() @ce taking a @ref std::vector is - deprecated in favor of @ref MeshTools::compressIndices(const Containers::StridedArrayView1D&, MeshIndexType) and - its 8- and 16-byte overloads + deprecated in favor of @ref MeshTools::compressIndices(const Containers::StridedArrayView1D&, MeshIndexType, Long) + and its 8- and 16-byte overloads - @cpp MeshTools::flipNormals() @ce and @cpp MeshTools::flipFaceWinding() @ce and @cpp MeshTools::tipsify() @ce are deprecated in favor of @ref MeshTools::flipNormalsInPlace(), diff --git a/doc/snippets/MagnumMeshTools.cpp b/doc/snippets/MagnumMeshTools.cpp index a34dcc122..530c770db 100644 --- a/doc/snippets/MagnumMeshTools.cpp +++ b/doc/snippets/MagnumMeshTools.cpp @@ -24,6 +24,7 @@ */ #include "Magnum/Math/Color.h" +#include "Magnum/Math/FunctionsBatch.h" #include "Magnum/MeshTools/CombineIndexedArrays.h" #include "Magnum/MeshTools/CompressIndices.h" #include "Magnum/MeshTools/Duplicate.h" @@ -53,6 +54,17 @@ std::vector indices = MeshTools::combineIndexedArrays( /* [combineIndexedArrays] */ } +{ +/* [compressIndices-offset] */ +Containers::ArrayView indices; +UnsignedInt offset = Math::min(indices); +std::pair, MeshIndexType> result = + MeshTools::compressIndices(indices, offset); + +// use `offset` to adjust vertex attribute offset … +/* [compressIndices-offset] */ +} + { /* [compressIndicesAs] */ std::vector indices; diff --git a/src/Magnum/MeshTools/CompressIndices.cpp b/src/Magnum/MeshTools/CompressIndices.cpp index 1be656c4c..b9bf7091a 100644 --- a/src/Magnum/MeshTools/CompressIndices.cpp +++ b/src/Magnum/MeshTools/CompressIndices.cpp @@ -35,38 +35,38 @@ namespace Magnum { namespace MeshTools { namespace { -template inline Containers::Array compress(const Containers::StridedArrayView1D& indices) { +template inline Containers::Array compress(const Containers::StridedArrayView1D& indices, Long offset) { /* Can't use Utility::copy() here because we're copying from a larger type - to a smaller one */ + to a smaller one (and subtracting an offset in addition) */ Containers::Array buffer(indices.size()*sizeof(T)); for(std::size_t i = 0; i != indices.size(); ++i) { - T index = static_cast(indices[i]); + T index = static_cast(indices[i] - offset); std::memcpy(buffer.begin()+i*sizeof(T), &index, sizeof(T)); } return buffer; } -template std::pair, MeshIndexType> compressIndicesImplementation(const Containers::StridedArrayView1D& indices, const MeshIndexType atLeast) { - const T max = Math::max(indices); +template std::pair, MeshIndexType> compressIndicesImplementation(const Containers::StridedArrayView1D& indices, const MeshIndexType atLeast, const Long offset) { + const UnsignedInt max = Math::max(indices) - offset; Containers::Array out; MeshIndexType type; const UnsignedInt log = Math::log(256, max); /* If it fits into 8 bytes and 8 bytes are allowed, pack into 8 */ if(log == 0 && atLeast == MeshIndexType::UnsignedByte) { - out = compress(indices); + out = compress(indices, offset); type = MeshIndexType::UnsignedByte; /* Otherwise, if it fits into either 8 or 16 bytes and we allow either 8 or 16, pack into 16 */ } else if(log <= 1 && atLeast != MeshIndexType::UnsignedInt) { - out = compress(indices); + out = compress(indices, offset); type = MeshIndexType::UnsignedShort; /* Otherwise pack into 32 */ } else { - out = compress(indices); + out = compress(indices, offset); type = MeshIndexType::UnsignedInt; } @@ -75,33 +75,48 @@ template std::pair, MeshIndexType> compressIndi } -std::pair, MeshIndexType> compressIndices(const Containers::StridedArrayView1D& indices, const MeshIndexType atLeast) { - return compressIndicesImplementation(indices, atLeast); +std::pair, MeshIndexType> compressIndices(const Containers::StridedArrayView1D& indices, const MeshIndexType atLeast, const Long offset) { + return compressIndicesImplementation(indices, atLeast, offset); } -std::pair, MeshIndexType> compressIndices(const Containers::StridedArrayView1D& indices, const MeshIndexType atLeast) { - return compressIndicesImplementation(indices, atLeast); +std::pair, MeshIndexType> compressIndices(const Containers::StridedArrayView1D& indices, const MeshIndexType atLeast, const Long offset) { + return compressIndicesImplementation(indices, atLeast, offset); } -std::pair, MeshIndexType> compressIndices(const Containers::StridedArrayView1D& indices, const MeshIndexType atLeast) { - return compressIndicesImplementation(indices, atLeast); +std::pair, MeshIndexType> compressIndices(const Containers::StridedArrayView1D& indices, const MeshIndexType atLeast, const Long offset) { + return compressIndicesImplementation(indices, atLeast, offset); } -std::pair, MeshIndexType> compressIndices(const Containers::StridedArrayView2D& indices, const MeshIndexType atLeast) { +std::pair, MeshIndexType> compressIndices(const Containers::StridedArrayView1D& indices, const Long offset) { + return compressIndicesImplementation(indices, MeshIndexType::UnsignedShort, offset); +} + +std::pair, MeshIndexType> compressIndices(const Containers::StridedArrayView1D& indices, const Long offset) { + return compressIndicesImplementation(indices, MeshIndexType::UnsignedShort, offset); +} + +std::pair, MeshIndexType> compressIndices(const Containers::StridedArrayView1D& indices, const Long offset) { + return compressIndicesImplementation(indices, MeshIndexType::UnsignedShort, offset); +} + +std::pair, MeshIndexType> compressIndices(const Containers::StridedArrayView2D& indices, const MeshIndexType atLeast, const Long offset) { CORRADE_ASSERT(indices.isContiguous<1>(), "MeshTools::compressIndices(): second view dimension is not contiguous", {}); if(indices.size()[1] == 4) - return compressIndicesImplementation(Containers::arrayCast<1, const UnsignedInt>(indices), atLeast); + return compressIndicesImplementation(Containers::arrayCast<1, const UnsignedInt>(indices), atLeast, offset); else if(indices.size()[1] == 2) - return compressIndicesImplementation(Containers::arrayCast<1, const UnsignedShort>(indices), atLeast); + return compressIndicesImplementation(Containers::arrayCast<1, const UnsignedShort>(indices), atLeast, offset); else { CORRADE_ASSERT(indices.size()[1] == 1, "MeshTools::compressIndices(): expected index type size 1, 2 or 4 but got" << indices.size()[1], {}); - return compressIndicesImplementation(Containers::arrayCast<1, const UnsignedByte>(indices), atLeast); + return compressIndicesImplementation(Containers::arrayCast<1, const UnsignedByte>(indices), atLeast, offset); } } +std::pair, MeshIndexType> compressIndices(const Containers::StridedArrayView2D& indices, const Long offset) { + return compressIndices(indices, MeshIndexType::UnsignedShort, offset); +} + #ifdef MAGNUM_BUILD_DEPRECATED std::tuple, MeshIndexType, UnsignedInt, UnsignedInt> compressIndices(const std::vector& indices) { - /** @todo Performance hint when range can be represented by smaller value? */ const auto minmax = Math::minmax(indices); Containers::Array data; MeshIndexType type; diff --git a/src/Magnum/MeshTools/CompressIndices.h b/src/Magnum/MeshTools/CompressIndices.h index 23a4eb070..a594a41d5 100644 --- a/src/Magnum/MeshTools/CompressIndices.h +++ b/src/Magnum/MeshTools/CompressIndices.h @@ -47,35 +47,74 @@ namespace Magnum { namespace MeshTools { @brief Compress an index array @param indices Index array @param atLeast Smallest allowed type +@param offset Offset to subtract from each index @return Compressed index array and corresponding type @m_since_latest This function compresses @p indices to the smallest possible size. For example when your indices have the maximum vertex index 463, it's wasteful to store -them in array of 32bit integers, array of 16bit integers is sufficient. The +them in array of 32-bit integers, array of 16-bit integers is sufficient. The @p atLeast parameter allows you to specify the smallest type to use and it defaults to @ref MeshIndexType::UnsignedShort as 8-bit types are not friendly to many GPUs (and for example unextended Vulkan or D3D12 don't even support them). It's also possible to choose a type larger than the input type to -"inflate" an index buffer of a smaller type. - -Example usage: +"inflate" an index buffer of a smaller type. Example usage: @snippet MagnumMeshTools-gl.cpp compressIndices + +In case the indices all start from a large offset, the @p offset parameter can +be used to subtract it, allowing them to be compressed even further. For +example, if all indices are in range @f$ [ 75000 ; 96000 ] @f$ (which fits only +into a 32-bit type), subtracting 75000 makes them in range @f$ [ 0; 21000 ] @f$ +which fits into 16 bits. Note that you also need to update vertex attribute +offsets accordingly. Example: + +@snippet MagnumMeshTools.cpp compressIndices-offset + +A negative @p offset value will do an operation inverse to the above. See also +@ref compressIndices(const Trade::MeshData&, MeshIndexType) that can do this +operation directly on a @ref Trade::MeshData instance. */ -MAGNUM_MESHTOOLS_EXPORT std::pair, MeshIndexType> compressIndices(const Containers::StridedArrayView1D& indices, MeshIndexType atLeast = MeshIndexType::UnsignedShort); +MAGNUM_MESHTOOLS_EXPORT std::pair, MeshIndexType> compressIndices(const Containers::StridedArrayView1D& indices, MeshIndexType atLeast = MeshIndexType::UnsignedShort, Long offset = 0); /** @overload @m_since_latest */ -MAGNUM_MESHTOOLS_EXPORT std::pair, MeshIndexType> compressIndices(const Containers::StridedArrayView1D& indices, MeshIndexType atLeast = MeshIndexType::UnsignedShort); +MAGNUM_MESHTOOLS_EXPORT std::pair, MeshIndexType> compressIndices(const Containers::StridedArrayView1D& indices, MeshIndexType atLeast = MeshIndexType::UnsignedShort, Long offset = 0); /** @overload @m_since_latest */ -MAGNUM_MESHTOOLS_EXPORT std::pair, MeshIndexType> compressIndices(const Containers::StridedArrayView1D& indices, MeshIndexType atLeast = MeshIndexType::UnsignedShort); +MAGNUM_MESHTOOLS_EXPORT std::pair, MeshIndexType> compressIndices(const Containers::StridedArrayView1D& indices, MeshIndexType atLeast = MeshIndexType::UnsignedShort, Long offset = 0); + +/** +@overload +@m_since_latest + +Same as @ref compressIndices(const Containers::StridedArrayView1D&, MeshIndexType, Long) +with @p atLeast set to @ref MeshIndexType::UnsignedShort. +*/ +MAGNUM_MESHTOOLS_EXPORT std::pair, MeshIndexType> compressIndices(const Containers::StridedArrayView1D& indices, Long offset); + +/** +@overload +@m_since_latest + +Same as @ref compressIndices(const Containers::StridedArrayView1D&, MeshIndexType, Long) +with @p atLeast set to @ref MeshIndexType::UnsignedShort. +*/ +MAGNUM_MESHTOOLS_EXPORT std::pair, MeshIndexType> compressIndices(const Containers::StridedArrayView1D& indices, Long offset); + +/** +@overload +@m_since_latest + +Same as @ref compressIndices(const Containers::StridedArrayView1D&, MeshIndexType, Long) +with @p atLeast set to @ref MeshIndexType::UnsignedShort. +*/ +MAGNUM_MESHTOOLS_EXPORT std::pair, MeshIndexType> compressIndices(const Containers::StridedArrayView1D& indices, Long offset); /** @brief Compress a type-erased index array @@ -83,17 +122,26 @@ MAGNUM_MESHTOOLS_EXPORT std::pair, MeshIndexType> compre Expects that the second dimension of @p indices is contiguous and represents the actual 1/2/4-byte index type. Based on its size then calls one of the -@ref compressIndices(const Containers::StridedArrayView1D&, MeshIndexType) +@ref compressIndices(const Containers::StridedArrayView1D&, MeshIndexType, Long) etc. overloads. */ -MAGNUM_MESHTOOLS_EXPORT std::pair, MeshIndexType> compressIndices(const Containers::StridedArrayView2D& indices, MeshIndexType atLeast = MeshIndexType::UnsignedShort); +MAGNUM_MESHTOOLS_EXPORT std::pair, MeshIndexType> compressIndices(const Containers::StridedArrayView2D& indices, MeshIndexType atLeast = MeshIndexType::UnsignedShort, Long offset = 0); + +/** +@overload +@m_since_latest + +Same as @ref compressIndices(const Containers::StridedArrayView2D&, MeshIndexType, Long) +with @p atLeast set to @ref MeshIndexType::UnsignedShort. +*/ +MAGNUM_MESHTOOLS_EXPORT std::pair, MeshIndexType> compressIndices(const Containers::StridedArrayView2D& indices, Long offset); #ifdef MAGNUM_BUILD_DEPRECATED /** @brief Compress vertex indices @param indices Index array @return Index range, type and compressed index array -@m_deprecated_since_latest Use @ref compressIndices(const Containers::StridedArrayView1D&, MeshIndexType) +@m_deprecated_since_latest Use @ref compressIndices(const Containers::StridedArrayView1D&, MeshIndexType, Long) instead. The index range isn't returned anymore, use @ref Math::minmax(const Containers::StridedArrayView1D&) to get it if needed. @@ -108,7 +156,7 @@ Example usage: @see @ref compressIndicesAs() */ -CORRADE_DEPRECATED("use compressIndices(const Containers::StridedArrayView1D&, MeshIndexType) instead") MAGNUM_MESHTOOLS_EXPORT std::tuple, MeshIndexType, UnsignedInt, UnsignedInt> compressIndices(const std::vector& indices); +CORRADE_DEPRECATED("use compressIndices(const Containers::StridedArrayView1D&, MeshIndexType, Long) instead") MAGNUM_MESHTOOLS_EXPORT std::tuple, MeshIndexType, UnsignedInt, UnsignedInt> compressIndices(const std::vector& indices); #endif /** diff --git a/src/Magnum/MeshTools/Test/CompressIndicesTest.cpp b/src/Magnum/MeshTools/Test/CompressIndicesTest.cpp index 145b8edb8..611601291 100644 --- a/src/Magnum/MeshTools/Test/CompressIndicesTest.cpp +++ b/src/Magnum/MeshTools/Test/CompressIndicesTest.cpp @@ -44,6 +44,8 @@ struct CompressIndicesTest: TestSuite::Tester { template void compressUnsignedShort(); template void compressUnsignedInt(); void compressUnsignedByteInflateToShort(); + void compressOffset(); + template void compressOffsetNegative(); /* No compressErased(), as that's tested in the templates above */ void compressErasedNonContiguous(); void compressErasedWrongIndexSize(); @@ -62,6 +64,10 @@ CompressIndicesTest::CompressIndicesTest() { &CompressIndicesTest::compressUnsignedShort, &CompressIndicesTest::compressUnsignedInt, &CompressIndicesTest::compressUnsignedByteInflateToShort, + &CompressIndicesTest::compressOffset, + &CompressIndicesTest::compressOffsetNegative, + &CompressIndicesTest::compressOffsetNegative, + &CompressIndicesTest::compressOffsetNegative, &CompressIndicesTest::compressErasedNonContiguous, &CompressIndicesTest::compressErasedWrongIndexSize, @@ -145,6 +151,44 @@ void CompressIndicesTest::compressUnsignedByteInflateToShort() { TestSuite::Compare::Container); } +void CompressIndicesTest::compressOffset() { + const UnsignedInt indices[]{75000 + 1, 75000 + 256, 75000 + 0, 75000 + 5}; + std::pair, MeshIndexType> out = compressIndices(indices, 75000); + + CORRADE_COMPARE(out.second, MeshIndexType::UnsignedShort); + CORRADE_COMPARE_AS(Containers::arrayCast(out.first), + Containers::arrayView({1, 256, 0, 5}), + TestSuite::Compare::Container); + + /* Test the type-erased variant as well */ + out = compressIndices(Containers::arrayCast<2, const char>(Containers::stridedArrayView(indices)), 75000); + + CORRADE_COMPARE(out.second, MeshIndexType::UnsignedShort); + CORRADE_COMPARE_AS(Containers::arrayCast(out.first), + Containers::arrayView({1, 256, 0, 5}), + TestSuite::Compare::Container); +} + +template void CompressIndicesTest::compressOffsetNegative() { + setTestCaseTemplateName(Math::TypeTraits::name()); + + const T indices[]{1, 255, 0, 5}; + std::pair, MeshIndexType> out = compressIndices(indices, -75000); + + CORRADE_COMPARE(out.second, MeshIndexType::UnsignedInt); + CORRADE_COMPARE_AS(Containers::arrayCast(out.first), + Containers::arrayView({75000 + 1, 75000 + 255, 75000 + 0, 75000 + 5}), + TestSuite::Compare::Container); + + /* Test the type-erased variant as well */ + out = compressIndices(Containers::arrayCast<2, const char>(Containers::stridedArrayView(indices)), -75000); + + CORRADE_COMPARE(out.second, MeshIndexType::UnsignedInt); + CORRADE_COMPARE_AS(Containers::arrayCast(out.first), + Containers::arrayView({75000 + 1, 75000 + 255, 75000 + 0, 75000 + 5}), + TestSuite::Compare::Container); +} + void CompressIndicesTest::compressErasedNonContiguous() { const char indices[6*4]{};