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) {
/* 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<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());
buffer->setData(attribute, usage);
}
template<class T, class ...U> 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<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() || attributeCount(next...) == ~std::size_t(0), "MeshTools::interleave(): attribute arrays don't have the same length, nothing done.", 0);
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...);
}
template<class... T> inline static std::size_t stride(std::size_t gap, const T&... next) {
return gap + stride(next...);
}
private:
template<class T, class ...U> void write(char* startingOffset, const T& first, const U&... next) {
/* Copy the data to the buffer */
auto it = first.begin();
template<class T, class ...U> inline void write(char* startingOffset, const T& first, const U&... next) {
write(startingOffset+writeOne(startingOffset, first), next...);
}
/* 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)
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 */
@ -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<Vector4> positions;
std::vector<Vector2> 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<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&...),
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<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) {
return Implementation::Interleave()(attribute, 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()(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,

49
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<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() {
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::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() {
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<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)

Loading…
Cancel
Save