diff --git a/src/Magnum/MeshTools/Interleave.h b/src/Magnum/MeshTools/Interleave.h index 8b572c7eb..43fccbb4b 100644 --- a/src/Magnum/MeshTools/Interleave.h +++ b/src/Magnum/MeshTools/Interleave.h @@ -41,94 +41,58 @@ namespace Magnum { namespace MeshTools { namespace Implementation { -class Interleave { - public: - Interleave(): _attributeCount(0), _stride(0) {} - - template std::tuple> operator()(const T&... attributes) { - /* Compute buffer size and stride */ - _attributeCount = attributeCount(attributes...); - Containers::Array data; - if(_attributeCount && _attributeCount != ~std::size_t(0)) { - _stride = stride(attributes...); - - /* Create output buffer */ - data = Containers::Array(_attributeCount*_stride); - - /* Save the data */ - write(data.begin(), attributes...); - } - - return std::make_tuple(_attributeCount, _stride, std::move(data)); - } - - template void operator()(Mesh& mesh, Buffer& buffer, BufferUsage usage, const T&... attributes) { - Containers::Array data; - std::tie(std::ignore, std::ignore, data) = operator()(attributes...); - - mesh.setVertexCount(_attributeCount); - buffer.setData(data, usage); - } - - /* Specialization for only one attribute array */ - template typename std::enable_if::value, void>::type operator()(Mesh& mesh, Buffer& buffer, BufferUsage usage, const T& attribute) { - mesh.setVertexCount(attribute.size()); - buffer.setData(attribute, usage); - } - - template 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, expected" << first.size() << "but got" << attributeCount(next...), 0); - - return first.size(); - } - - template static std::size_t attributeCount(std::size_t, const T&... next) { - return attributeCount(next...); - } - - template static std::size_t attributeCount(std::size_t) { - return ~std::size_t(0); - } - - template 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 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) { - 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) - std::memcpy(startingOffset+i*_stride, reinterpret_cast(&*it), sizeof(typename T::value_type)); - - return sizeof(typename T::value_type); - } - - /* Fill gap with zeros */ - std::size_t writeOne(char* startingOffset, std::size_t gap) { - for(std::size_t i = 0; i != _attributeCount; ++i) - std::memset(startingOffset+i*_stride, 0, gap); - - return gap; - } - - /* Terminator functions for recursive calls */ - static std::size_t attributeCount() { return 0; } - static std::size_t stride() { return 0; } - void write(char*) {} - - std::size_t _attributeCount; - std::size_t _stride; +/* Attribute count, skipping gaps. If the attributes are just gaps, returns + ~std::size_t{0}. It must be in the structure to have proper overload + resolution (the functions would otherwise need to be de-inlined to break + cyclic dependencies) */ +struct AttributeCount { + template typename std::enable_if::value, std::size_t>::type operator()(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, expected" << first.size() << "but got" << AttributeCount{}(next...), 0); + + return first.size(); + } + template std::size_t operator()(std::size_t, const T& first, const U&... next) { + return AttributeCount{}(first, next...); + } + constexpr std::size_t operator()(std::size_t) { return ~std::size_t(0); } + constexpr std::size_t operator()() { return 0; } }; +/* Stride, taking gaps into account. It must be in the structure, same reason + as above */ +struct Stride { + template typename std::enable_if::value, std::size_t>::type operator()(const T&, const U&... next) { + return sizeof(typename T::value_type) + Stride{}(next...); + } + template std::size_t operator()(std::size_t gap, const T&... next) { + return gap + Stride{}(next...); + } + constexpr std::size_t operator()() { return 0; } +}; + +/* Copy data to the buffer */ +template typename std::enable_if::value, std::size_t>::type writeOneInterleaved(std::size_t attributeCount, std::size_t stride, char* startingOffset, const T& attributeList) { + auto it = attributeList.begin(); + for(std::size_t i = 0; i != attributeCount; ++i, ++it) + std::memcpy(startingOffset + i*stride, reinterpret_cast(&*it), sizeof(typename T::value_type)); + + return sizeof(typename T::value_type); +} + +/* Fill gap with zeros */ +inline std::size_t writeOneInterleaved(std::size_t attributeCount, std::size_t stride, char* startingOffset, std::size_t gap) { + for(std::size_t i = 0; i != attributeCount; ++i) + std::memset(startingOffset + i*stride, 0, gap); + + return gap; +} + +/* Write interleaved data */ +inline void writeInterleaved(std::size_t, std::size_t, char*) {} +template void writeInterleaved(std::size_t attributeCount, std::size_t stride, char* startingOffset, const T& first, const U&... next) { + writeInterleaved(attributeCount, stride, startingOffset + writeOneInterleaved(attributeCount, stride, startingOffset, first), next...); +} + } /** @@ -176,12 +140,24 @@ See also @ref interleave(Mesh&, Buffer&, BufferUsage, const T&...), which writes the interleaved array directly into buffer of given mesh. */ /* enable_if to avoid clash with overloaded function below */ -template inline typename std::enable_if::value, std::tuple>>::type interleave(const T& first, const U&... next) { - return Implementation::Interleave()(first, next...); +template typename std::enable_if::value, std::tuple>>::type interleave(const T& first, const U&... next) { + /* Compute buffer size and stride */ + const std::size_t attributeCount = Implementation::AttributeCount{}(first, next...); + const std::size_t stride = Implementation::Stride{}(first, next...); + if(attributeCount && attributeCount != ~std::size_t(0)) { + + /* Create output buffer */ + Containers::Array data = Containers::Array(attributeCount*stride); + + /* Save the data */ + Implementation::writeInterleaved(attributeCount, stride, data.begin(), first, next...); + return std::make_tuple(attributeCount, stride, std::move(data)); + + } else return std::make_tuple(0, stride, nullptr); } /** -@brief %Interleave vertex attributes and write them to array buffer +@brief %Interleave vertex attributes, write them to array buffer and configure the mesh @param mesh Output mesh @param buffer Output vertex buffer @param usage Vertex buffer usage @@ -194,18 +170,31 @@ so you don't have to call @ref Mesh::setVertexCount() on your own. @attention You still must call @ref Mesh::setPrimitive() and @ref Mesh::addVertexBuffer() on the mesh afterwards. -For only one attribute array this function is convenient equivalent to the -following, without any performance loss: +@see @ref compressIndices(), @ref compile() +@todo rework so Mesh & Buffer doesn't need to be included in header +*/ +template void interleave(Mesh& mesh, Buffer& buffer, BufferUsage usage, const T&... attributes) { + Containers::Array data; + std::size_t attributeCount; + std::tie(attributeCount, std::ignore, data) = interleave(attributes...); + + mesh.setVertexCount(attributeCount); + buffer.setData(data, usage); +} + +/** +@brief Write vertex attribute to array buffer and configure the mesh + +Simplified specialization of the above function for only one attribute array, +equivalent to the following: @code buffer.setData(attribute, usage); mesh.setVertexCount(attribute.size()); @endcode - -@see @ref MeshTools::compressIndices() -@todo rework so Mesh & Buffer doesn't need to be included in header */ -template inline void interleave(Mesh& mesh, Buffer& buffer, BufferUsage usage, const T&... attributes) { - return Implementation::Interleave()(mesh, buffer, usage, attributes...); +template typename std::enable_if::value, void>::type interleave(Mesh& mesh, Buffer& buffer, BufferUsage usage, const T& attribute) { + mesh.setVertexCount(attribute.size()); + buffer.setData(attribute, usage); } }} diff --git a/src/Magnum/MeshTools/Test/InterleaveTest.cpp b/src/Magnum/MeshTools/Test/InterleaveTest.cpp index 00ec10389..55ad00b4b 100644 --- a/src/Magnum/MeshTools/Test/InterleaveTest.cpp +++ b/src/Magnum/MeshTools/Test/InterleaveTest.cpp @@ -56,30 +56,30 @@ InterleaveTest::InterleaveTest() { void InterleaveTest::attributeCount() { std::stringstream ss; Error::setOutput(&ss); - CORRADE_COMPARE((Implementation::Interleave::attributeCount(std::vector{0, 1, 2}, + CORRADE_COMPARE((Implementation::AttributeCount{}(std::vector{0, 1, 2}, std::vector{0, 1, 2, 3, 4, 5})), std::size_t(0)); CORRADE_COMPARE(ss.str(), "MeshTools::interleave(): attribute arrays don't have the same length, expected 3 but got 6\n"); - CORRADE_COMPARE((Implementation::Interleave::attributeCount(std::vector{0, 1, 2}, + CORRADE_COMPARE((Implementation::AttributeCount{}(std::vector{0, 1, 2}, std::vector{3, 4, 5})), std::size_t(3)); } void InterleaveTest::attributeCountGaps() { - CORRADE_COMPARE((Implementation::Interleave::attributeCount(std::vector{0, 1, 2}, 3, + CORRADE_COMPARE((Implementation::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)); + CORRADE_COMPARE(Implementation::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)); + CORRADE_COMPARE(Implementation::Stride{}(std::vector()), std::size_t(1)); + CORRADE_COMPARE(Implementation::Stride{}(std::vector()), std::size_t(4)); + CORRADE_COMPARE((Implementation::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)); + CORRADE_COMPARE((Implementation::Stride{}(2, std::vector(), 1, std::vector(), 12)), std::size_t(20)); } void InterleaveTest::write() {