Browse Source

Support for gaps in MeshTools::interleave().

pull/7/head
Vladimír Vondruš 14 years ago
parent
commit
98781b5052
  1. 98
      src/MeshTools/Interleave.h
  2. 49
      src/MeshTools/Test/InterleaveTest.cpp

98
src/MeshTools/Interleave.h

@ -39,7 +39,7 @@ class Interleave {
template<class ...T> std::tuple<std::size_t, std::size_t, char*> operator()(const T&... attributes) { template<class ...T> std::tuple<std::size_t, std::size_t, char*> operator()(const T&... attributes) {
/* Compute buffer size and stride */ /* Compute buffer size and stride */
_attributeCount = attributeCount(attributes...); _attributeCount = attributeCount(attributes...);
if(_attributeCount) { if(_attributeCount && _attributeCount != ~std::size_t(0)) {
_stride = stride(attributes...); _stride = stride(attributes...);
/* Create output buffer */ /* Create output buffer */
@ -62,29 +62,55 @@ class Interleave {
} }
/* Specialization for only one attribute array */ /* Specialization for only one attribute array */
template<class T> void operator()(Mesh* mesh, Buffer* buffer, Buffer::Usage usage, const T& attribute) { template<class T> typename std::enable_if<!std::is_convertible<T, std::size_t>::value, void>::type operator()(Mesh* mesh, Buffer* buffer, Buffer::Usage usage, const T& attribute) {
mesh->setVertexCount(attribute.size()); mesh->setVertexCount(attribute.size());
buffer->setData(attribute, usage); buffer->setData(attribute, usage);
} }
template<class T, class ...U> inline static std::size_t attributeCount(const T& first, const U&... next) { template<class T, class ...U> inline static typename std::enable_if<!std::is_convertible<T, std::size_t>::value, std::size_t>::type 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); 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(); return first.size();
} }
template<class T, class ...U> inline static std::size_t stride(const T&, const U&... next) { template<class... T> inline static std::size_t attributeCount(std::size_t, const T&... next) {
return attributeCount(next...);
}
template<class ...T> inline static std::size_t attributeCount(std::size_t) {
return ~std::size_t(0);
}
template<class T, class ...U> inline static typename std::enable_if<!std::is_convertible<T, std::size_t>::value, std::size_t>::type stride(const T&, const U&... next) {
return sizeof(typename T::value_type) + stride(next...); return sizeof(typename T::value_type) + stride(next...);
} }
template<class... T> inline static std::size_t stride(std::size_t gap, const T&... next) {
return gap + stride(next...);
}
private: private:
template<class T, class ...U> void write(char* startingOffset, const T& first, const U&... next) { template<class T, class ...U> inline void write(char* startingOffset, const T& first, const U&... next) {
/* Copy the data to the buffer */ write(startingOffset+writeOne(startingOffset, first), next...);
auto it = first.begin(); }
/* Copy data to the buffer */
template<class T> typename std::enable_if<!std::is_convertible<T, std::size_t>::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) for(std::size_t i = 0; i != _attributeCount; ++i, ++it)
memcpy(startingOffset+i*_stride, reinterpret_cast<const char*>(&*it), sizeof(typename T::value_type)); memcpy(startingOffset+i*_stride, reinterpret_cast<const char*>(&*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 */ /* Terminator functions for recursive calls */
@ -102,15 +128,14 @@ class Interleave {
/** /**
@brief %Interleave vertex attributes @brief %Interleave vertex attributes
@param attribute First attribute array
@param attributes Next attribute arrays This function takes list of attribute arrays and returns them interleaved, so
@return Attribute count, stride and interleaved attribute array. Deleting the data for each attribute are in continuous place in memory. Returned tuple
array is user responsibility. contains attribute count, stride and data array. Deleting the data array is up
to the user.
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 Size of the data buffer can be computed from attribute count and stride, as
buffer can be computed from attribute count and stride, as shown below. Example shown below. Example usage:
usage:
@code @code
std::vector<Vector4> positions; std::vector<Vector4> positions;
std::vector<Vector2> textureCoordinates; std::vector<Vector2> textureCoordinates;
@ -123,28 +148,41 @@ std::size_t dataSize = attributeCount*stride;
delete[] data; delete[] data;
@endcode @endcode
The only requirements to attribute array type is that it must have typedef It's often desirable to align data for one vertex on 32bit boundaries. To
`T::value_type`, forward iterator (to be used with range-based for) and achieve that, you can specify gaps between the attributes:
function `size()` returning count of elements. In most cases it will be @code
`std::vector` or `std::array`. std::vector<Vector4> positions;
std::vector<GLushort> weights;
std::vector<Color3<GLubyte>> 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&...), See also interleave(Mesh*, Buffer*, Buffer::Usage, const T&...),
which writes the interleaved array directly into buffer of given mesh. 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 */ /* enable_if to avoid clash with overloaded function below */
template<class T, class ...U> inline typename std::enable_if<!std::is_convertible<T, Mesh*>::value, std::tuple<std::size_t, std::size_t, char*>>::type interleave(const T& attribute, const U&... attributes) { template<class T, class ...U> inline typename std::enable_if<!std::is_convertible<T, Mesh*>::value, std::tuple<std::size_t, std::size_t, char*>>::type interleave(const T& first, const U&... next) {
return Implementation::Interleave()(attribute, attributes...); return Implementation::Interleave()(first, next...);
} }
/** /**
@brief %Interleave vertex attributes and write them to array buffer @brief %Interleave vertex attributes and write them to array buffer
@param attributes Attribute arrays
@param mesh Output mesh @param mesh Output mesh
@param buffer Output array buffer @param buffer Output vertex buffer
@param usage Array buffer usage @param usage Vertex buffer usage
@param attributes Attribute arrays and gaps
The same as interleave(const T&, const U&...), but this function writes the 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, output to given array buffer and updates vertex count in the mesh accordingly,

49
src/MeshTools/Test/InterleaveTest.cpp

@ -29,14 +29,20 @@ class InterleaveTest: public Corrade::TestSuite::Tester {
InterleaveTest(); InterleaveTest();
void attributeCount(); void attributeCount();
void attributeCountGaps();
void stride(); void stride();
void strideGaps();
void write(); void write();
void writeGaps();
}; };
InterleaveTest::InterleaveTest() { InterleaveTest::InterleaveTest() {
addTests(&InterleaveTest::attributeCount, addTests(&InterleaveTest::attributeCount,
&InterleaveTest::attributeCountGaps,
&InterleaveTest::stride, &InterleaveTest::stride,
&InterleaveTest::write); &InterleaveTest::strideGaps,
&InterleaveTest::write,
&InterleaveTest::writeGaps);
} }
void InterleaveTest::attributeCount() { void InterleaveTest::attributeCount() {
@ -50,12 +56,24 @@ void InterleaveTest::attributeCount() {
std::vector<std::int8_t>{3, 4, 5})), std::size_t(3)); std::vector<std::int8_t>{3, 4, 5})), std::size_t(3));
} }
void InterleaveTest::attributeCountGaps() {
CORRADE_COMPARE((Implementation::Interleave::attributeCount(std::vector<std::int8_t>{0, 1, 2}, 3,
std::vector<std::int8_t>{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() { void InterleaveTest::stride() {
CORRADE_COMPARE(Implementation::Interleave::stride(std::vector<std::int8_t>()), std::size_t(1)); CORRADE_COMPARE(Implementation::Interleave::stride(std::vector<std::int8_t>()), std::size_t(1));
CORRADE_COMPARE(Implementation::Interleave::stride(std::vector<std::int32_t>()), std::size_t(4)); CORRADE_COMPARE(Implementation::Interleave::stride(std::vector<std::int32_t>()), std::size_t(4));
CORRADE_COMPARE((Implementation::Interleave::stride(std::vector<std::int8_t>(), std::vector<std::int32_t>())), std::size_t(5)); CORRADE_COMPARE((Implementation::Interleave::stride(std::vector<std::int8_t>(), std::vector<std::int32_t>())), std::size_t(5));
} }
void InterleaveTest::strideGaps() {
CORRADE_COMPARE((Implementation::Interleave::stride(2, std::vector<std::int8_t>(), 1, std::vector<std::int32_t>(), 12)), std::size_t(20));
}
void InterleaveTest::write() { void InterleaveTest::write() {
std::size_t attributeCount; std::size_t attributeCount;
std::size_t stride; std::size_t stride;
@ -85,6 +103,35 @@ void InterleaveTest::write() {
delete[] data; delete[] data;
} }
void InterleaveTest::writeGaps() {
std::size_t attributeCount;
std::size_t stride;
char* data;
std::tie(attributeCount, stride, data) = MeshTools::interleave(
std::vector<std::int8_t>{0, 1, 2}, 3,
std::vector<std::int32_t>{3, 4, 5},
std::vector<std::int16_t>{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<char>(data, data+size), (std::vector<char>{
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<char>(data, data+size), (std::vector<char>{
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) CORRADE_TEST_MAIN(Magnum::MeshTools::Test::InterleaveTest)

Loading…
Cancel
Save