Browse Source

MeshTools: compressIndices() now takes also an offset.

pull/371/head
Vladimír Vondruš 6 years ago
parent
commit
bae5eecf42
  1. 8
      doc/changelog.dox
  2. 12
      doc/snippets/MagnumMeshTools.cpp
  3. 53
      src/Magnum/MeshTools/CompressIndices.cpp
  4. 70
      src/Magnum/MeshTools/CompressIndices.h
  5. 44
      src/Magnum/MeshTools/Test/CompressIndicesTest.cpp

8
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<const UnsignedInt>&, MeshIndexType) and
its 8- and 16-byte overloads
deprecated in favor of @ref MeshTools::compressIndices(const Containers::StridedArrayView1D<const UnsignedInt>&, 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(),

12
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<UnsignedInt> indices = MeshTools::combineIndexedArrays(
/* [combineIndexedArrays] */
}
{
/* [compressIndices-offset] */
Containers::ArrayView<const UnsignedInt> indices;
UnsignedInt offset = Math::min(indices);
std::pair<Containers::Array<char>, MeshIndexType> result =
MeshTools::compressIndices(indices, offset);
// use `offset` to adjust vertex attribute offset …
/* [compressIndices-offset] */
}
{
/* [compressIndicesAs] */
std::vector<UnsignedInt> indices;

53
src/Magnum/MeshTools/CompressIndices.cpp

@ -35,38 +35,38 @@ namespace Magnum { namespace MeshTools {
namespace {
template<class T, class U> inline Containers::Array<char> compress(const Containers::StridedArrayView1D<const U>& indices) {
template<class T, class U> inline Containers::Array<char> compress(const Containers::StridedArrayView1D<const U>& 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<char> buffer(indices.size()*sizeof(T));
for(std::size_t i = 0; i != indices.size(); ++i) {
T index = static_cast<T>(indices[i]);
T index = static_cast<T>(indices[i] - offset);
std::memcpy(buffer.begin()+i*sizeof(T), &index, sizeof(T));
}
return buffer;
}
template<class T> std::pair<Containers::Array<char>, MeshIndexType> compressIndicesImplementation(const Containers::StridedArrayView1D<const T>& indices, const MeshIndexType atLeast) {
const T max = Math::max(indices);
template<class T> std::pair<Containers::Array<char>, MeshIndexType> compressIndicesImplementation(const Containers::StridedArrayView1D<const T>& indices, const MeshIndexType atLeast, const Long offset) {
const UnsignedInt max = Math::max(indices) - offset;
Containers::Array<char> 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<UnsignedByte>(indices);
out = compress<UnsignedByte>(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<UnsignedShort>(indices);
out = compress<UnsignedShort>(indices, offset);
type = MeshIndexType::UnsignedShort;
/* Otherwise pack into 32 */
} else {
out = compress<UnsignedInt>(indices);
out = compress<UnsignedInt>(indices, offset);
type = MeshIndexType::UnsignedInt;
}
@ -75,33 +75,48 @@ template<class T> std::pair<Containers::Array<char>, MeshIndexType> compressIndi
}
std::pair<Containers::Array<char>, MeshIndexType> compressIndices(const Containers::StridedArrayView1D<const UnsignedInt>& indices, const MeshIndexType atLeast) {
return compressIndicesImplementation(indices, atLeast);
std::pair<Containers::Array<char>, MeshIndexType> compressIndices(const Containers::StridedArrayView1D<const UnsignedInt>& indices, const MeshIndexType atLeast, const Long offset) {
return compressIndicesImplementation(indices, atLeast, offset);
}
std::pair<Containers::Array<char>, MeshIndexType> compressIndices(const Containers::StridedArrayView1D<const UnsignedShort>& indices, const MeshIndexType atLeast) {
return compressIndicesImplementation(indices, atLeast);
std::pair<Containers::Array<char>, MeshIndexType> compressIndices(const Containers::StridedArrayView1D<const UnsignedShort>& indices, const MeshIndexType atLeast, const Long offset) {
return compressIndicesImplementation(indices, atLeast, offset);
}
std::pair<Containers::Array<char>, MeshIndexType> compressIndices(const Containers::StridedArrayView1D<const UnsignedByte>& indices, const MeshIndexType atLeast) {
return compressIndicesImplementation(indices, atLeast);
std::pair<Containers::Array<char>, MeshIndexType> compressIndices(const Containers::StridedArrayView1D<const UnsignedByte>& indices, const MeshIndexType atLeast, const Long offset) {
return compressIndicesImplementation(indices, atLeast, offset);
}
std::pair<Containers::Array<char>, MeshIndexType> compressIndices(const Containers::StridedArrayView2D<const char>& indices, const MeshIndexType atLeast) {
std::pair<Containers::Array<char>, MeshIndexType> compressIndices(const Containers::StridedArrayView1D<const UnsignedInt>& indices, const Long offset) {
return compressIndicesImplementation(indices, MeshIndexType::UnsignedShort, offset);
}
std::pair<Containers::Array<char>, MeshIndexType> compressIndices(const Containers::StridedArrayView1D<const UnsignedShort>& indices, const Long offset) {
return compressIndicesImplementation(indices, MeshIndexType::UnsignedShort, offset);
}
std::pair<Containers::Array<char>, MeshIndexType> compressIndices(const Containers::StridedArrayView1D<const UnsignedByte>& indices, const Long offset) {
return compressIndicesImplementation(indices, MeshIndexType::UnsignedShort, offset);
}
std::pair<Containers::Array<char>, MeshIndexType> compressIndices(const Containers::StridedArrayView2D<const char>& 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<Containers::Array<char>, MeshIndexType> compressIndices(const Containers::StridedArrayView2D<const char>& indices, const Long offset) {
return compressIndices(indices, MeshIndexType::UnsignedShort, offset);
}
#ifdef MAGNUM_BUILD_DEPRECATED
std::tuple<Containers::Array<char>, MeshIndexType, UnsignedInt, UnsignedInt> compressIndices(const std::vector<UnsignedInt>& indices) {
/** @todo Performance hint when range can be represented by smaller value? */
const auto minmax = Math::minmax(indices);
Containers::Array<char> data;
MeshIndexType type;

70
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<Containers::Array<char>, MeshIndexType> compressIndices(const Containers::StridedArrayView1D<const UnsignedInt>& indices, MeshIndexType atLeast = MeshIndexType::UnsignedShort);
MAGNUM_MESHTOOLS_EXPORT std::pair<Containers::Array<char>, MeshIndexType> compressIndices(const Containers::StridedArrayView1D<const UnsignedInt>& indices, MeshIndexType atLeast = MeshIndexType::UnsignedShort, Long offset = 0);
/**
@overload
@m_since_latest
*/
MAGNUM_MESHTOOLS_EXPORT std::pair<Containers::Array<char>, MeshIndexType> compressIndices(const Containers::StridedArrayView1D<const UnsignedShort>& indices, MeshIndexType atLeast = MeshIndexType::UnsignedShort);
MAGNUM_MESHTOOLS_EXPORT std::pair<Containers::Array<char>, MeshIndexType> compressIndices(const Containers::StridedArrayView1D<const UnsignedShort>& indices, MeshIndexType atLeast = MeshIndexType::UnsignedShort, Long offset = 0);
/**
@overload
@m_since_latest
*/
MAGNUM_MESHTOOLS_EXPORT std::pair<Containers::Array<char>, MeshIndexType> compressIndices(const Containers::StridedArrayView1D<const UnsignedByte>& indices, MeshIndexType atLeast = MeshIndexType::UnsignedShort);
MAGNUM_MESHTOOLS_EXPORT std::pair<Containers::Array<char>, MeshIndexType> compressIndices(const Containers::StridedArrayView1D<const UnsignedByte>& indices, MeshIndexType atLeast = MeshIndexType::UnsignedShort, Long offset = 0);
/**
@overload
@m_since_latest
Same as @ref compressIndices(const Containers::StridedArrayView1D<const UnsignedInt>&, MeshIndexType, Long)
with @p atLeast set to @ref MeshIndexType::UnsignedShort.
*/
MAGNUM_MESHTOOLS_EXPORT std::pair<Containers::Array<char>, MeshIndexType> compressIndices(const Containers::StridedArrayView1D<const UnsignedInt>& indices, Long offset);
/**
@overload
@m_since_latest
Same as @ref compressIndices(const Containers::StridedArrayView1D<const UnsignedShort>&, MeshIndexType, Long)
with @p atLeast set to @ref MeshIndexType::UnsignedShort.
*/
MAGNUM_MESHTOOLS_EXPORT std::pair<Containers::Array<char>, MeshIndexType> compressIndices(const Containers::StridedArrayView1D<const UnsignedShort>& indices, Long offset);
/**
@overload
@m_since_latest
Same as @ref compressIndices(const Containers::StridedArrayView1D<const UnsignedByte>&, MeshIndexType, Long)
with @p atLeast set to @ref MeshIndexType::UnsignedShort.
*/
MAGNUM_MESHTOOLS_EXPORT std::pair<Containers::Array<char>, MeshIndexType> compressIndices(const Containers::StridedArrayView1D<const UnsignedByte>& indices, Long offset);
/**
@brief Compress a type-erased index array
@ -83,17 +122,26 @@ MAGNUM_MESHTOOLS_EXPORT std::pair<Containers::Array<char>, 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<const UnsignedInt>&, MeshIndexType)
@ref compressIndices(const Containers::StridedArrayView1D<const UnsignedInt>&, MeshIndexType, Long)
etc. overloads.
*/
MAGNUM_MESHTOOLS_EXPORT std::pair<Containers::Array<char>, MeshIndexType> compressIndices(const Containers::StridedArrayView2D<const char>& indices, MeshIndexType atLeast = MeshIndexType::UnsignedShort);
MAGNUM_MESHTOOLS_EXPORT std::pair<Containers::Array<char>, MeshIndexType> compressIndices(const Containers::StridedArrayView2D<const char>& indices, MeshIndexType atLeast = MeshIndexType::UnsignedShort, Long offset = 0);
/**
@overload
@m_since_latest
Same as @ref compressIndices(const Containers::StridedArrayView2D<const char>&, MeshIndexType, Long)
with @p atLeast set to @ref MeshIndexType::UnsignedShort.
*/
MAGNUM_MESHTOOLS_EXPORT std::pair<Containers::Array<char>, MeshIndexType> compressIndices(const Containers::StridedArrayView2D<const char>& 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<const UnsignedInt>&, MeshIndexType)
@m_deprecated_since_latest Use @ref compressIndices(const Containers::StridedArrayView1D<const UnsignedInt>&, MeshIndexType, Long)
instead. The index range isn't returned anymore, use @ref Math::minmax(const Containers::StridedArrayView1D<const T>&)
to get it if needed.
@ -108,7 +156,7 @@ Example usage:
@see @ref compressIndicesAs()
*/
CORRADE_DEPRECATED("use compressIndices(const Containers::StridedArrayView1D<const UnsignedInt>&, MeshIndexType) instead") MAGNUM_MESHTOOLS_EXPORT std::tuple<Containers::Array<char>, MeshIndexType, UnsignedInt, UnsignedInt> compressIndices(const std::vector<UnsignedInt>& indices);
CORRADE_DEPRECATED("use compressIndices(const Containers::StridedArrayView1D<const UnsignedInt>&, MeshIndexType, Long) instead") MAGNUM_MESHTOOLS_EXPORT std::tuple<Containers::Array<char>, MeshIndexType, UnsignedInt, UnsignedInt> compressIndices(const std::vector<UnsignedInt>& indices);
#endif
/**

44
src/Magnum/MeshTools/Test/CompressIndicesTest.cpp

@ -44,6 +44,8 @@ struct CompressIndicesTest: TestSuite::Tester {
template<class T> void compressUnsignedShort();
template<class T> void compressUnsignedInt();
void compressUnsignedByteInflateToShort();
void compressOffset();
template<class T> void compressOffsetNegative();
/* No compressErased(), as that's tested in the templates above */
void compressErasedNonContiguous();
void compressErasedWrongIndexSize();
@ -62,6 +64,10 @@ CompressIndicesTest::CompressIndicesTest() {
&CompressIndicesTest::compressUnsignedShort<UnsignedInt>,
&CompressIndicesTest::compressUnsignedInt<UnsignedInt>,
&CompressIndicesTest::compressUnsignedByteInflateToShort,
&CompressIndicesTest::compressOffset,
&CompressIndicesTest::compressOffsetNegative<UnsignedByte>,
&CompressIndicesTest::compressOffsetNegative<UnsignedShort>,
&CompressIndicesTest::compressOffsetNegative<UnsignedInt>,
&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<Containers::Array<char>, MeshIndexType> out = compressIndices(indices, 75000);
CORRADE_COMPARE(out.second, MeshIndexType::UnsignedShort);
CORRADE_COMPARE_AS(Containers::arrayCast<UnsignedShort>(out.first),
Containers::arrayView<UnsignedShort>({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<UnsignedShort>(out.first),
Containers::arrayView<UnsignedShort>({1, 256, 0, 5}),
TestSuite::Compare::Container);
}
template<class T> void CompressIndicesTest::compressOffsetNegative() {
setTestCaseTemplateName(Math::TypeTraits<T>::name());
const T indices[]{1, 255, 0, 5};
std::pair<Containers::Array<char>, MeshIndexType> out = compressIndices(indices, -75000);
CORRADE_COMPARE(out.second, MeshIndexType::UnsignedInt);
CORRADE_COMPARE_AS(Containers::arrayCast<UnsignedInt>(out.first),
Containers::arrayView<UnsignedInt>({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<UnsignedInt>(out.first),
Containers::arrayView<UnsignedInt>({75000 + 1, 75000 + 255, 75000 + 0, 75000 + 5}),
TestSuite::Compare::Container);
}
void CompressIndicesTest::compressErasedNonContiguous() {
const char indices[6*4]{};

Loading…
Cancel
Save