diff --git a/src/MeshTools/Interleave.h b/src/MeshTools/Interleave.h index 6f85ef2b2..90105318c 100644 --- a/src/MeshTools/Interleave.h +++ b/src/MeshTools/Interleave.h @@ -39,7 +39,7 @@ class Interleave { template std::tuple operator()(const T&... attributes) { /* Compute buffer size and stride */ _attributeCount = attributeCount(attributes...); - if(_attributeCount) { + if(_attributeCount && _attributeCount != ~std::size_t(0)) { _stride = stride(attributes...); /* Create output buffer */ @@ -62,29 +62,55 @@ class Interleave { } /* Specialization for only one attribute array */ - template void operator()(Mesh* mesh, Buffer* buffer, Buffer::Usage usage, const T& attribute) { + template typename std::enable_if::value, void>::type operator()(Mesh* mesh, Buffer* buffer, Buffer::Usage usage, const T& attribute) { mesh->setVertexCount(attribute.size()); buffer->setData(attribute, usage); } - template inline static std::size_t attributeCount(const T& first, const U&... next) { - CORRADE_ASSERT(sizeof...(next) == 0 || attributeCount(next...) == first.size(), "MeshTools::interleave(): attribute arrays don't have the same length, nothing done.", 0); + template inline static typename std::enable_if::value, std::size_t>::type attributeCount(const T& first, const U&... next) { + CORRADE_ASSERT(sizeof...(next) == 0 || attributeCount(next...) == first.size() || attributeCount(next...) == ~std::size_t(0), "MeshTools::interleave(): attribute arrays don't have the same length, nothing done.", 0); return first.size(); } - template inline static std::size_t stride(const T&, const U&... next) { + template inline static std::size_t attributeCount(std::size_t, const T&... next) { + return attributeCount(next...); + } + + template inline static std::size_t attributeCount(std::size_t) { + return ~std::size_t(0); + } + + template inline static typename std::enable_if::value, std::size_t>::type stride(const T&, const U&... next) { return sizeof(typename T::value_type) + stride(next...); } + template inline static std::size_t stride(std::size_t gap, const T&... next) { + return gap + stride(next...); + } + private: - template void write(char* startingOffset, const T& first, const U&... next) { - /* Copy the data to the buffer */ - auto it = first.begin(); + template inline void write(char* startingOffset, const T& first, const U&... next) { + write(startingOffset+writeOne(startingOffset, first), next...); + } + + /* Copy data to the buffer */ + template typename std::enable_if::value, std::size_t>::type writeOne(char* startingOffset, const T& attributeList) { + auto it = attributeList.begin(); for(std::size_t i = 0; i != _attributeCount; ++i, ++it) memcpy(startingOffset+i*_stride, reinterpret_cast(&*it), sizeof(typename T::value_type)); - write(startingOffset+sizeof(typename T::value_type), next...); + return sizeof(typename T::value_type); + } + + /* Fill gap with zeros */ + std::size_t writeOne(char* startingOffset, std::size_t gap) { + char* data = new char[gap](); + for(std::size_t i = 0; i != _attributeCount; ++i) + memcpy(startingOffset+i*_stride, data, gap); + + delete[] data; + return gap; } /* Terminator functions for recursive calls */ @@ -102,15 +128,14 @@ class Interleave { /** @brief %Interleave vertex attributes -@param attribute First attribute array -@param attributes Next attribute arrays -@return Attribute count, stride and interleaved attribute array. Deleting the - array is user responsibility. - -This function takes two or more attribute arrays and returns them interleaved, -so data for each attribute are in continuous place in memory. Size of the data -buffer can be computed from attribute count and stride, as shown below. Example -usage: + +This function takes list of attribute arrays and returns them interleaved, so +data for each attribute are in continuous place in memory. Returned tuple +contains attribute count, stride and data array. Deleting the data array is up +to the user. + +Size of the data buffer can be computed from attribute count and stride, as +shown below. Example usage: @code std::vector positions; std::vector textureCoordinates; @@ -123,28 +148,41 @@ std::size_t dataSize = attributeCount*stride; delete[] data; @endcode -The only requirements to attribute array type is that it must have typedef -`T::value_type`, forward iterator (to be used with range-based for) and -function `size()` returning count of elements. In most cases it will be -`std::vector` or `std::array`. +It's often desirable to align data for one vertex on 32bit boundaries. To +achieve that, you can specify gaps between the attributes: +@code +std::vector positions; +std::vector weights; +std::vector> vertexColors; +std::size_t attributeCount; +std::size_t stride; +char* data; +std::tie(attributeCount, stride, data) = MeshTools::interleave(positions, weights, 2, textureCoordinates, 1); +@endcode +This way vertex stride is 24 bytes, without gaps it would be 21 bytes, causing +possible performance loss. + +@attention The function expects that all arrays have the same size. + +@note The only requirements to attribute array type is that it must have + typedef `T::value_type`, forward iterator (to be used with range-based + for) and function `size()` returning count of elements. In most cases it + will be `std::vector` or `std::array`. See also interleave(Mesh*, Buffer*, Buffer::Usage, const T&...), which writes the interleaved array directly into buffer of given mesh. - -@attention Each passed array should have the same size, if not, resulting - array has zero length. */ /* enable_if to avoid clash with overloaded function below */ -template inline typename std::enable_if::value, std::tuple>::type interleave(const T& attribute, const U&... attributes) { - return Implementation::Interleave()(attribute, attributes...); +template inline typename std::enable_if::value, std::tuple>::type interleave(const T& first, const U&... next) { + return Implementation::Interleave()(first, next...); } /** @brief %Interleave vertex attributes and write them to array buffer -@param attributes Attribute arrays @param mesh Output mesh -@param buffer Output array buffer -@param usage Array buffer usage +@param buffer Output vertex buffer +@param usage Vertex buffer usage +@param attributes Attribute arrays and gaps The same as interleave(const T&, const U&...), but this function writes the output to given array buffer and updates vertex count in the mesh accordingly, diff --git a/src/MeshTools/Test/InterleaveTest.cpp b/src/MeshTools/Test/InterleaveTest.cpp index 09f1ba2d3..6939fb96a 100644 --- a/src/MeshTools/Test/InterleaveTest.cpp +++ b/src/MeshTools/Test/InterleaveTest.cpp @@ -29,14 +29,20 @@ class InterleaveTest: public Corrade::TestSuite::Tester { InterleaveTest(); void attributeCount(); + void attributeCountGaps(); void stride(); + void strideGaps(); void write(); + void writeGaps(); }; InterleaveTest::InterleaveTest() { addTests(&InterleaveTest::attributeCount, + &InterleaveTest::attributeCountGaps, &InterleaveTest::stride, - &InterleaveTest::write); + &InterleaveTest::strideGaps, + &InterleaveTest::write, + &InterleaveTest::writeGaps); } void InterleaveTest::attributeCount() { @@ -50,12 +56,24 @@ void InterleaveTest::attributeCount() { std::vector{3, 4, 5})), std::size_t(3)); } +void InterleaveTest::attributeCountGaps() { + CORRADE_COMPARE((Implementation::Interleave::attributeCount(std::vector{0, 1, 2}, 3, + std::vector{3, 4, 5}, 5)), std::size_t(3)); + + /* No arrays from which to get size */ + CORRADE_COMPARE(Implementation::Interleave::attributeCount(3, 5), ~std::size_t(0)); +} + void InterleaveTest::stride() { CORRADE_COMPARE(Implementation::Interleave::stride(std::vector()), std::size_t(1)); CORRADE_COMPARE(Implementation::Interleave::stride(std::vector()), std::size_t(4)); CORRADE_COMPARE((Implementation::Interleave::stride(std::vector(), std::vector())), std::size_t(5)); } +void InterleaveTest::strideGaps() { + CORRADE_COMPARE((Implementation::Interleave::stride(2, std::vector(), 1, std::vector(), 12)), std::size_t(20)); +} + void InterleaveTest::write() { std::size_t attributeCount; std::size_t stride; @@ -85,6 +103,35 @@ void InterleaveTest::write() { delete[] data; } +void InterleaveTest::writeGaps() { + std::size_t attributeCount; + std::size_t stride; + char* data; + std::tie(attributeCount, stride, data) = MeshTools::interleave( + std::vector{0, 1, 2}, 3, + std::vector{3, 4, 5}, + std::vector{6, 7, 8}, 2); + + CORRADE_COMPARE(attributeCount, std::size_t(3)); + CORRADE_COMPARE(stride, std::size_t(12)); + std::size_t size = attributeCount*stride; + if(!Endianness::isBigEndian()) { + CORRADE_COMPARE(std::vector(data, data+size), (std::vector{ + 0x00, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x06, 0x00, 0x00, 0x00, + 0x01, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x07, 0x00, 0x00, 0x00, + 0x02, 0x00, 0x00, 0x00, 0x05, 0x00, 0x00, 0x00, 0x08, 0x00, 0x00, 0x00 + })); + } else { + CORRADE_COMPARE(std::vector(data, data+size), (std::vector{ + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x03, 0x00, 0x06, 0x00, 0x00, + 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x04, 0x00, 0x07, 0x00, 0x00, + 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x05, 0x00, 0x08, 0x00, 0x00 + })); + } + + delete[] data; +} + }}} CORRADE_TEST_MAIN(Magnum::MeshTools::Test::InterleaveTest)