Browse Source

Trade: support array attributes in MeshData.

The last major bit needed for meshlet support.
pull/371/head
Vladimír Vondruš 6 years ago
parent
commit
c0e3a84250
  1. 28
      src/Magnum/Trade/MeshData.cpp
  2. 311
      src/Magnum/Trade/MeshData.h
  3. 276
      src/Magnum/Trade/Test/MeshDataTest.cpp

28
src/Magnum/Trade/MeshData.cpp

@ -51,7 +51,7 @@ MeshIndexData::MeshIndexData(const Containers::StridedArrayView2D<const char>& d
_data = data.asContiguous();
}
MeshAttributeData::MeshAttributeData(const MeshAttribute name, const VertexFormat format, const Containers::StridedArrayView1D<const void>& data) noexcept: MeshAttributeData{name, format, data, nullptr} {
MeshAttributeData::MeshAttributeData(const MeshAttribute name, const VertexFormat format, UnsignedShort arraySize, const Containers::StridedArrayView1D<const void>& data) noexcept: MeshAttributeData{name, format, arraySize, data, nullptr} {
/* Yes, this calls into a constexpr function defined in the header --
because I feel that makes more sense than duplicating the full assert
logic */
@ -60,12 +60,16 @@ MeshAttributeData::MeshAttributeData(const MeshAttribute name, const VertexForma
"Trade::MeshAttributeData: expected stride to be positive and enough to fit" << format << Debug::nospace << ", got" << data.stride(), );
}
MeshAttributeData::MeshAttributeData(const MeshAttribute name, const VertexFormat format, const Containers::StridedArrayView2D<const char>& data) noexcept: MeshAttributeData{name, format, Containers::StridedArrayView1D<const void>{{data.data(), ~std::size_t{}}, data.size()[0], data.stride()[0]}, nullptr} {
MeshAttributeData::MeshAttributeData(const MeshAttribute name, const VertexFormat format, UnsignedShort arraySize, const Containers::StridedArrayView2D<const char>& data) noexcept: MeshAttributeData{name, format, arraySize, Containers::StridedArrayView1D<const void>{{data.data(), ~std::size_t{}}, data.size()[0], data.stride()[0]}, nullptr} {
/* Yes, this calls into a constexpr function defined in the header --
because I feel that makes more sense than duplicating the full assert
logic */
CORRADE_ASSERT(data.empty()[0] || isVertexFormatImplementationSpecific(format) || vertexFormatSize(format) == data.size()[1],
#ifndef CORRADE_NO_ASSERT
if(arraySize) CORRADE_ASSERT(data.empty()[0] || isVertexFormatImplementationSpecific(format) || data.size()[1] == vertexFormatSize(format)*arraySize,
"Trade::MeshAttributeData: second view dimension size" << data.size()[1] << "doesn't match" << format << "and array size" << arraySize, );
else CORRADE_ASSERT(data.empty()[0] || isVertexFormatImplementationSpecific(format) || data.size()[1] == vertexFormatSize(format),
"Trade::MeshAttributeData: second view dimension size" << data.size()[1] << "doesn't match" << format, );
#endif
CORRADE_ASSERT(data.isContiguous<1>(),
"Trade::MeshAttributeData: second view dimension is not contiguous", );
}
@ -263,6 +267,12 @@ UnsignedInt MeshData::attributeStride(UnsignedInt id) const {
return _attributes[id]._stride;
}
UnsignedShort MeshData::attributeArraySize(UnsignedInt id) const {
CORRADE_ASSERT(id < _attributes.size(),
"Trade::MeshData::attributeArraySize(): index" << id << "out of range for" << _attributes.size() << "attributes", {});
return _attributes[id]._arraySize;
}
UnsignedInt MeshData::attributeCount(const MeshAttribute name) const {
UnsignedInt count = 0;
for(const MeshAttributeData& attribute: _attributes)
@ -307,6 +317,12 @@ UnsignedInt MeshData::attributeStride(MeshAttribute name, UnsignedInt id) const
return attributeStride(attributeId);
}
UnsignedShort MeshData::attributeArraySize(MeshAttribute name, UnsignedInt id) const {
const UnsignedInt attributeId = attributeFor(name, id);
CORRADE_ASSERT(attributeId != ~UnsignedInt{}, "Trade::MeshData::attributeArraySize(): index" << id << "out of range for" << attributeCount(name) << name << "attributes", {});
return attributeArraySize(attributeId);
}
Containers::StridedArrayView1D<const void> MeshData::attributeDataViewInternal(const MeshAttributeData& attribute) const {
return Containers::StridedArrayView1D<const void>{
/* We're *sure* the view is correct, so faking the view size */
@ -327,7 +343,8 @@ Containers::StridedArrayView2D<const char> MeshData::attribute(UnsignedInt id) c
return Containers::arrayCast<2, const char>(
attributeDataViewInternal(attribute),
isVertexFormatImplementationSpecific(attribute._format) ?
attribute._stride : vertexFormatSize(attribute._format));
attribute._stride : vertexFormatSize(attribute._format)*
(attribute._arraySize ? attribute._arraySize : 1));
}
Containers::StridedArrayView2D<char> MeshData::mutableAttribute(UnsignedInt id) {
@ -340,7 +357,8 @@ Containers::StridedArrayView2D<char> MeshData::mutableAttribute(UnsignedInt id)
auto out = Containers::arrayCast<2, const char>(
attributeDataViewInternal(attribute),
isVertexFormatImplementationSpecific(attribute._format) ?
attribute._stride : vertexFormatSize(attribute._format));
attribute._stride : vertexFormatSize(attribute._format)*
(attribute._arraySize ? attribute._arraySize : 1));
/** @todo some arrayConstCast? UGH */
return Containers::StridedArrayView2D<char>{
/* The view size is there only for a size assert, we're pretty sure the

311
src/Magnum/Trade/MeshData.h

@ -244,7 +244,7 @@ class MAGNUM_TRADE_EXPORT MeshAttributeData {
* initialization of the attribute array for @ref MeshData, expected to
* be replaced with concrete values later.
*/
constexpr explicit MeshAttributeData() noexcept: _data{}, _vertexCount{}, _format{}, _stride{}, _name{}, _isOffsetOnly{false} {}
constexpr explicit MeshAttributeData() noexcept: _data{}, _vertexCount{}, _format{}, _stride{}, _name{}, _arraySize{}, _isOffsetOnly{false} {}
/**
* @brief Type-erased constructor
@ -255,7 +255,21 @@ class MAGNUM_TRADE_EXPORT MeshAttributeData {
* Expects that @p data stride is large enough to fit @p type and that
* @p type corresponds to @p name.
*/
explicit MeshAttributeData(MeshAttribute name, VertexFormat format, const Containers::StridedArrayView1D<const void>& data) noexcept;
explicit MeshAttributeData(MeshAttribute name, VertexFormat format, const Containers::StridedArrayView1D<const void>& data) noexcept: MeshAttributeData{name, format, 0, data} {}
/**
* @brief Type-erased constructor for an array attribute
* @param name Attribute name
* @param format Vertex format
* @param arraySize Array size
* @param data Attribute data
*
* Expects that @p data stride is large enough to fit @p type, @p type
* corresponds to @p name and @p arraySize is zero for builtin
* attributes. Passing @cpp 0 @ce to @p arraySize is equivalent to
* calling the above overload.
*/
explicit MeshAttributeData(MeshAttribute name, VertexFormat format, UnsignedShort arraySize, const Containers::StridedArrayView1D<const void>& data) noexcept;
/**
* @brief Constructor
@ -266,10 +280,25 @@ class MAGNUM_TRADE_EXPORT MeshAttributeData {
* Expects that the second dimension of @p data is contiguous and its
* size matches @p type; and that @p type corresponds to @p name.
*/
explicit MeshAttributeData(MeshAttribute name, VertexFormat format, const Containers::StridedArrayView2D<const char>& data) noexcept;
explicit MeshAttributeData(MeshAttribute name, VertexFormat format, const Containers::StridedArrayView2D<const char>& data) noexcept: MeshAttributeData{name, format, 0, data} {}
/** @overload */
explicit MeshAttributeData(MeshAttribute name, VertexFormat format, std::nullptr_t) noexcept: MeshAttributeData{name, format, nullptr, nullptr} {}
explicit MeshAttributeData(MeshAttribute name, VertexFormat format, std::nullptr_t) noexcept: MeshAttributeData{name, format, 0, nullptr, nullptr} {}
/**
* @brief Construct an array attribute
* @param name Attribute name
* @param format Vertex format
* @param arraySize Array size
* @param data Attribute data
*
* Expects that the second dimension of @p data is contiguous and its
* size matches @p type and @p arraSize, that @p type corresponds to
* @p name and @p arraySize is zero for builtin attributes. Passing
* @cpp 0 @ce to @p arraySize is equivalent to calling the above
* overload.
*/
explicit MeshAttributeData(MeshAttribute name, VertexFormat format, UnsignedShort arraySize, const Containers::StridedArrayView2D<const char>& data) noexcept;
/**
* @brief Constructor
@ -305,6 +334,21 @@ class MAGNUM_TRADE_EXPORT MeshAttributeData {
/** @overload */
template<class T> constexpr explicit MeshAttributeData(MeshAttribute name, const Containers::ArrayView<T>& data) noexcept: MeshAttributeData{name, Containers::stridedArrayView(data)} {}
/**
* @brief Construct an array attribute
* @param name Attribute name
* @param data Attribute data
*
* Detects @ref VertexFormat based on @p T and calls
* @ref MeshAttributeData(MeshAttribute, VertexFormat, UnsignedShort, const Containers::StridedArrayView1D<const void>&)
* with the second dimension size passed to @p arraySize. Expects that
* the second dimension is contiguous. At the moment only custom
* attributes can be arrays, which means this function can't be used
* with a builtin @p name. See @ref MeshAttributeData(MeshAttribute, const Containers::StridedArrayView1D<T>&)
* for details about @ref VertexFormat detection.
*/
template<class T> constexpr explicit MeshAttributeData(MeshAttribute name, const Containers::StridedArrayView2D<T>& data) noexcept;
/**
* @brief Construct an offset-only attribute
* @param name Attribute name
@ -312,16 +356,19 @@ class MAGNUM_TRADE_EXPORT MeshAttributeData {
* @param offset Attribute data offset
* @param vertexCount Attribute vertex count
* @param stride Attribute stride
* @param arraySize Array size. Use @cpp 0 @ce for non-array
* attributes.
*
* Instances created this way refer to an offset in unspecified
* external vertex data instead of containing the data view directly.
* Useful when the location of the vertex data array is not known at
* attribute construction time. Note that instances created this way
* can't be used in most @ref MeshTools algorithms.
* @see @ref isOffsetOnly(),
* attribute construction time. Expects that @p arraySize is zero for
* builtin attributes. Note that instances created this way can't be
* used in most @ref MeshTools algorithms.
* @see @ref isOffsetOnly(), @ref arraySize(),
* @ref data(Containers::ArrayView<const void>) const
*/
explicit constexpr MeshAttributeData(MeshAttribute name, VertexFormat format, std::size_t offset, UnsignedInt vertexCount, std::ptrdiff_t stride) noexcept;
explicit constexpr MeshAttributeData(MeshAttribute name, VertexFormat format, std::size_t offset, UnsignedInt vertexCount, std::ptrdiff_t stride, UnsignedShort arraySize = 0) noexcept;
/**
* @brief Construct a pad value
@ -334,7 +381,7 @@ class MAGNUM_TRADE_EXPORT MeshAttributeData {
constexpr explicit MeshAttributeData(Int padding): _data{nullptr}, _vertexCount{0}, _format{}, _stride{
(CORRADE_CONSTEXPR_ASSERT(padding >= -32768 && padding <= 32767,
"Trade::MeshAttributeData: at most 32k padding supported, got" << padding), Short(padding))
}, _name{}, _isOffsetOnly{false} {}
}, _name{}, _arraySize{}, _isOffsetOnly{false} {}
/**
* @brief If the attribute is offset-only
@ -342,7 +389,7 @@ class MAGNUM_TRADE_EXPORT MeshAttributeData {
* Returns @cpp true @ce if the attribute doesn't contain the data view
* directly, but instead refers to unspecified external vertex data.
* @see @ref data(Containers::ArrayView<const void>) const,
* @ref MeshAttributeData(MeshAttribute, VertexFormat, std::size_t, UnsignedInt, std::ptrdiff_t)
* @ref MeshAttributeData(MeshAttribute, VertexFormat, std::size_t, UnsignedInt, std::ptrdiff_t, UnsignedShort)
*/
constexpr bool isOffsetOnly() const { return _isOffsetOnly; }
@ -352,6 +399,9 @@ class MAGNUM_TRADE_EXPORT MeshAttributeData {
/** @brief Attribute format */
constexpr VertexFormat format() const { return _format; }
/** @brief Attribute array size */
constexpr UnsignedShort arraySize() const { return _arraySize; }
/**
* @brief Type-erased attribute data
*
@ -383,7 +433,7 @@ class MAGNUM_TRADE_EXPORT MeshAttributeData {
}
private:
constexpr explicit MeshAttributeData(MeshAttribute name, VertexFormat format, const Containers::StridedArrayView1D<const void>& data, std::nullptr_t) noexcept;
constexpr explicit MeshAttributeData(MeshAttribute name, VertexFormat format, UnsignedShort arraySize, const Containers::StridedArrayView1D<const void>& data, std::nullptr_t) noexcept;
friend MeshData;
union Data {
@ -402,9 +452,10 @@ class MAGNUM_TRADE_EXPORT MeshAttributeData {
current largest reported stride is 4k so 32k should be enough */
Short _stride;
MeshAttribute _name;
UnsignedShort _arraySize;
bool _isOffsetOnly;
/* 3 bytes free for more stuff on 64b (21, aligned to 24) and on 32b
(17 used, aligned to 20) */
/* 1 byte free for more stuff on 64b (23, aligned to 24) and on 32b
(19, aligned to 20) */
};
/** @relatesalso MeshAttributeData
@ -940,6 +991,26 @@ class MAGNUM_TRADE_EXPORT MeshData {
*/
UnsignedInt attributeStride(UnsignedInt id) const;
/**
* @brief Attribute array size
*
* In case given attribute is an array (the equivalent of e.g.
* @cpp int[30] @ce), returns array size, otherwise returns @cpp 0 @ce.
* At the moment only custom attributes can be arrays, no builtin
* @ref MeshAttribute is an array attribute. You can also use
* @ref attributeArraySize(MeshAttribute, UnsignedInt) const to
* directly get array size of given named attribute.
*
* Note that this is different from vertex count, which is exposed
* through @ref vertexCount(), and is an orthogonal concept to having
* multiple attributes of the same name (for example two sets of
* texture coordinates), which is exposed through
* @ref attributeCount(MeshAttribute) const. See
* @ref Trade-MeshData-populating-custom for an example.
* @see @ref isMeshAttributeCustom()
*/
UnsignedShort attributeArraySize(UnsignedInt id) const;
/**
* @brief Whether the mesh has given attribute
*
@ -997,7 +1068,18 @@ class MAGNUM_TRADE_EXPORT MeshData {
UnsignedInt attributeStride(MeshAttribute name, UnsignedInt id = 0) const;
/**
* @brief Data for given attribute array
* @brief Array size of a named attribute
*
* The @p id is expected to be smaller than
* @ref attributeCount(MeshAttribute) const. Note that this is
* different from vertex count, and is an orthogonal concept to having
* multiple attributes of the same name --- see
* @ref attributeArraySize(UnsignedInt) const for more information.
*/
UnsignedShort attributeArraySize(MeshAttribute name, UnsignedInt id = 0) const;
/**
* @brief Data for given attribute
*
* The @p id is expected to be smaller than @ref attributeCount() const.
* The second dimension represents the actual data type (its size is
@ -1011,7 +1093,7 @@ class MAGNUM_TRADE_EXPORT MeshData {
Containers::StridedArrayView2D<const char> attribute(UnsignedInt id) const;
/**
* @brief Mutable data for given attribute array
* @brief Mutable data for given attribute
*
* Like @ref attribute(UnsignedInt) const, but returns a mutable view.
* Expects that the mesh is mutable.
@ -1020,36 +1102,61 @@ class MAGNUM_TRADE_EXPORT MeshData {
Containers::StridedArrayView2D<char> mutableAttribute(UnsignedInt id);
/**
* @brief Data for given attribute array in a concrete type
* @brief Data for given attribute in a concrete type
*
* The @p id is expected to be smaller than @ref attributeCount() const
* and @p T is expected to correspond to
* @ref attributeFormat(UnsignedInt) const. Expects that the vertex
* format is *not* implementation-specific, in that case you can only
* access the attribute via the typeless @ref attribute(UnsignedInt) const
* above. You can also use the non-templated @ref positions2DAsArray(),
* @ref positions3DAsArray(), @ref normalsAsArray(),
* @ref textureCoordinates2DAsArray() and @ref colorsAsArray()
* accessors to get common attributes converted to usual types, but
* note that these operations involve extra allocation and data
* conversion.
* above. The attribute is also expected to not be an array, in that
* case you need to use the overload below by using @cpp T[] @ce
* instead of @cpp T @ce. You can also use the non-templated
* @ref positions2DAsArray(), @ref positions3DAsArray(),
* @ref normalsAsArray(), @ref textureCoordinates2DAsArray() and
* @ref colorsAsArray() accessors to get common attributes converted to
* usual types, but note that these operations involve extra allocation
* and data conversion.
* @see @ref attribute(MeshAttribute, UnsignedInt) const,
* @ref mutableAttribute(MeshAttribute, UnsignedInt),
* @ref isVertexFormatImplementationSpecific()
* @ref isVertexFormatImplementationSpecific(),
* @ref attributeArraySize()
*/
template<class T, class = typename std::enable_if<!std::is_array<T>::value>::type> Containers::StridedArrayView1D<const T> attribute(UnsignedInt id) const;
/**
* @brief Data for given array attribute in a concrete type
*
* Same as above, except that it works with array attributes instead
* --- you're expected to select this overload by passing @cpp T[] @ce
* instead of @cpp T @ce. The second dimension is guaranteed to be
* contiguous and have the same size as reported by
* @ref attributeArraySize() for given attribute.
*/
template<class T> Containers::StridedArrayView1D<const T> attribute(UnsignedInt id) const;
template<class T, class = typename std::enable_if<std::is_array<T>::value>::type> Containers::StridedArrayView2D<const typename std::remove_extent<T>::type> attribute(UnsignedInt id) const;
/**
* @brief Mutable data for given attribute array in a concrete type
* @brief Mutable data for given attribute in a concrete type
*
* Like @ref attribute(UnsignedInt) const, but returns a mutable view.
* Expects that the mesh is mutable.
* @see @ref vertexDataFlags()
*/
template<class T> Containers::StridedArrayView1D<T> mutableAttribute(UnsignedInt id);
template<class T, class = typename std::enable_if<!std::is_array<T>::value>::type> Containers::StridedArrayView1D<T> mutableAttribute(UnsignedInt id);
/**
* @brief Data for given named attribute array
* @brief Mutable data for given array attribute in a concrete type
*
* Same as above, except that it works with array attributes instead
* --- you're expected to select this overload by passing @cpp T[] @ce
* instead of @cpp T @ce. The second dimension is guaranteed to be
* contiguous and have the same size as reported by
* @ref attributeArraySize() for given attribute.
*/
template<class T, class = typename std::enable_if<std::is_array<T>::value>::type> Containers::StridedArrayView2D<typename std::remove_extent<T>::type> mutableAttribute(UnsignedInt id);
/**
* @brief Data for given named attribute
*
* The @p id is expected to be smaller than
* @ref attributeCount(MeshAttribute) const. The second dimension
@ -1066,7 +1173,7 @@ class MAGNUM_TRADE_EXPORT MeshData {
Containers::StridedArrayView2D<const char> attribute(MeshAttribute name, UnsignedInt id = 0) const;
/**
* @brief Mutable data for given named attribute array
* @brief Mutable data for given named attribute
*
* Like @ref attribute(MeshAttribute, UnsignedInt) const, but returns a
* mutable view. Expects that the mesh is mutable.
@ -1075,7 +1182,7 @@ class MAGNUM_TRADE_EXPORT MeshData {
Containers::StridedArrayView2D<char> mutableAttribute(MeshAttribute name, UnsignedInt id = 0);
/**
* @brief Data for given named attribute array in a concrete type
* @brief Data for given named attribute in a concrete type
*
* The @p id is expected to be smaller than
* @ref attributeCount(MeshAttribute) const and @p T is expected to
@ -1093,16 +1200,38 @@ class MAGNUM_TRADE_EXPORT MeshData {
* @ref mutableAttribute(MeshAttribute, UnsignedInt),
* @ref isVertexFormatImplementationSpecific()
*/
template<class T> Containers::StridedArrayView1D<const T> attribute(MeshAttribute name, UnsignedInt id = 0) const;
template<class T, class = typename std::enable_if<!std::is_array<T>::value>::type> Containers::StridedArrayView1D<const T> attribute(MeshAttribute name, UnsignedInt id = 0) const;
/**
* @brief Mutable data for given named attribute array in a concrete type
* @brief Data for given named array attribute in a concrete type
*
* Same as above, except that it works with array attributes instead
* --- you're expected to select this overload by passing @cpp T[] @ce
* instead of @cpp T @ce. The second dimension is guaranteed to be
* contiguous and have the same size as reported by
* @ref attributeArraySize() for given attribute.
*/
template<class T, class = typename std::enable_if<std::is_array<T>::value>::type> Containers::StridedArrayView2D<const typename std::remove_extent<T>::type> attribute(MeshAttribute name, UnsignedInt id = 0) const;
/**
* @brief Mutable data for given named attribute in a concrete type
*
* Like @ref attribute(MeshAttribute, UnsignedInt) const, but returns a
* mutable view. Expects that the mesh is mutable.
* @see @ref vertexDataFlags()
*/
template<class T> Containers::StridedArrayView1D<T> mutableAttribute(MeshAttribute name, UnsignedInt id = 0);
template<class T, class = typename std::enable_if<!std::is_array<T>::value>::type> Containers::StridedArrayView1D<T> mutableAttribute(MeshAttribute name, UnsignedInt id = 0);
/**
* @brief Mutable data for given named array attribute in a concrete type
*
* Same as above, except that it works with array attributes instead
* --- you're expected to select this overload by passing @cpp T[] @ce
* instead of @cpp T @ce. The second dimension is guaranteed to be
* contiguous and have the same size as reported by
* @ref attributeArraySize() for given attribute.
*/
template<class T, class = typename std::enable_if<std::is_array<T>::value>::type> Containers::StridedArrayView2D<typename std::remove_extent<T>::type> mutableAttribute(MeshAttribute name, UnsignedInt id = 0);
/**
* @brief Indices as 32-bit integers
@ -1318,6 +1447,10 @@ class MAGNUM_TRADE_EXPORT MeshData {
/* Like attribute(), but returning just a 1D view */
Containers::StridedArrayView1D<const void> attributeDataViewInternal(const MeshAttributeData& attribute) const;
#ifndef CORRADE_NO_ASSERT
template<class T> bool checkAttributeTypeCompatibility(const MeshAttributeData& attribute, const char* prefix) const;
#endif
/* GPUs don't currently support more than 32-bit index types / vertex
counts so this should be enough. Sanity check:
https://www.khronos.org/registry/vulkan/specs/1.2-extensions/man/html/VkIndexType.html */
@ -1490,10 +1623,14 @@ namespace Implementation {
/* Custom attributes can be anything */
isMeshAttributeCustom(name);
}
constexpr bool isAttributeArrayAllowed(MeshAttribute name) {
return isMeshAttributeCustom(name);
}
}
#endif
constexpr MeshAttributeData::MeshAttributeData(const MeshAttribute name, const VertexFormat format, const Containers::StridedArrayView1D<const void>& data, std::nullptr_t) noexcept:
constexpr MeshAttributeData::MeshAttributeData(const MeshAttribute name, const VertexFormat format, const UnsignedShort arraySize, const Containers::StridedArrayView1D<const void>& data, std::nullptr_t) noexcept:
_data{data.data()}, _vertexCount{UnsignedInt(data.size())}, _format{format},
/** @todo support zero / negative stride? would be hard to transfer to GL */
_stride{(CORRADE_CONSTEXPR_ASSERT(!(UnsignedInt(data.stride()) & 0xffff8000),
@ -1501,9 +1638,12 @@ constexpr MeshAttributeData::MeshAttributeData(const MeshAttribute name, const V
Short(data.stride()))
}, _name{(CORRADE_CONSTEXPR_ASSERT(Implementation::isVertexFormatCompatibleWithAttribute(name, format),
"Trade::MeshAttributeData:" << format << "is not a valid format for" << name), name)
}, _isOffsetOnly{false} {}
}, _arraySize{(CORRADE_CONSTEXPR_ASSERT(!arraySize || Implementation::isAttributeArrayAllowed(name),
"Trade::MeshAttributeData:" << name << "can't be an array attribute"), arraySize)
}, _isOffsetOnly{(CORRADE_CONSTEXPR_ASSERT(!arraySize || !isVertexFormatImplementationSpecific(format),
"Trade::MeshAttributeData: array attributes can't have an implementation-specific format"), false)} {}
constexpr MeshAttributeData::MeshAttributeData(const MeshAttribute name, const VertexFormat format, const std::size_t offset, const UnsignedInt vertexCount, const std::ptrdiff_t stride) noexcept:
constexpr MeshAttributeData::MeshAttributeData(const MeshAttribute name, const VertexFormat format, const std::size_t offset, const UnsignedInt vertexCount, const std::ptrdiff_t stride, UnsignedShort arraySize) noexcept:
_data{offset}, _vertexCount{vertexCount}, _format{format},
/** @todo support zero / negative stride? would be hard to transfer to GL */
_stride{(CORRADE_CONSTEXPR_ASSERT(!(UnsignedInt(stride) & 0xffff8000),
@ -1511,9 +1651,14 @@ constexpr MeshAttributeData::MeshAttributeData(const MeshAttribute name, const V
Short(stride))
}, _name{(CORRADE_CONSTEXPR_ASSERT(Implementation::isVertexFormatCompatibleWithAttribute(name, format),
"Trade::MeshAttributeData:" << format << "is not a valid format for" << name), name)
}, _isOffsetOnly{true} {}
}, _arraySize{(CORRADE_CONSTEXPR_ASSERT(!arraySize || Implementation::isAttributeArrayAllowed(name),
"Trade::MeshAttributeData:" << name << "can't be an array attribute"), arraySize)
}, _isOffsetOnly{(CORRADE_CONSTEXPR_ASSERT(!arraySize || !isVertexFormatImplementationSpecific(format),
"Trade::MeshAttributeData: array attributes can't have an implementation-specific format"), true)} {}
template<class T> constexpr MeshAttributeData::MeshAttributeData(MeshAttribute name, const Containers::StridedArrayView1D<T>& data) noexcept: MeshAttributeData{name, Implementation::vertexFormatFor<typename std::remove_const<T>::type>(), data, nullptr} {}
template<class T> constexpr MeshAttributeData::MeshAttributeData(MeshAttribute name, const Containers::StridedArrayView1D<T>& data) noexcept: MeshAttributeData{name, Implementation::vertexFormatFor<typename std::remove_const<T>::type>(), 0, data, nullptr} {}
template<class T> constexpr MeshAttributeData::MeshAttributeData(MeshAttribute name, const Containers::StridedArrayView2D<T>& data) noexcept: MeshAttributeData{name, Implementation::vertexFormatFor<typename std::remove_const<T>::type>(), UnsignedShort(data.size()[1]), Containers::StridedArrayView1D<const void>{{data.data(), ~std::size_t{}}, data.size()[0], data.stride()[0]}, (CORRADE_CONSTEXPR_ASSERT(data.stride()[1] == sizeof(T), "Trade::MeshAttributeData: second view dimension is not contiguous"), nullptr)} {}
template<class T> Containers::ArrayView<const T> MeshData::indices() const {
Containers::StridedArrayView2D<const char> data = indices();
@ -1535,66 +1680,110 @@ template<class T> Containers::ArrayView<T> MeshData::mutableIndices() {
return Containers::arrayCast<1, T>(data).asContiguous();
}
template<class T> Containers::StridedArrayView1D<const T> MeshData::attribute(UnsignedInt id) const {
#ifndef CORRADE_NO_ASSERT
template<class T> bool MeshData::checkAttributeTypeCompatibility(const MeshAttributeData& attribute, const char* const prefix) const {
CORRADE_ASSERT(!isVertexFormatImplementationSpecific(attribute._format),
prefix << "can't cast data from an implementation-specific vertex format" << reinterpret_cast<void*>(vertexFormatUnwrap(attribute._format)), false);
CORRADE_ASSERT(Implementation::isVertexFormatCompatible<typename std::remove_extent<T>::type>(attribute._format),
prefix << "improper type requested for" << attribute._name << "of format" << attribute._format, false);
CORRADE_ASSERT(std::is_array<T>::value == !!attribute._arraySize,
prefix << "use T[] to access an array attribute", false);
return true;
}
#endif
template<class T, class> Containers::StridedArrayView1D<const T> MeshData::attribute(const UnsignedInt id) const {
Containers::StridedArrayView2D<const char> data = attribute(id);
#ifdef CORRADE_GRACEFUL_ASSERT /* Sigh. Brittle. Better idea? */
if(!data.stride()[1]) return {};
#endif
#ifndef CORRADE_NO_ASSERT
const MeshAttributeData& attribute = _attributes[id];
if(!checkAttributeTypeCompatibility<T>(_attributes[id], "Trade::MeshData::attribute():")) return {};
#endif
CORRADE_ASSERT(!isVertexFormatImplementationSpecific(attribute._format),
"Trade::MeshData::attribute(): can't cast data from an implementation-specific vertex format" << reinterpret_cast<void*>(vertexFormatUnwrap(attribute._format)), {});
CORRADE_ASSERT(Implementation::isVertexFormatCompatible<T>(attribute._format),
"Trade::MeshData::attribute(): improper type requested for" << attribute._name << "of format" << attribute._format, nullptr);
return Containers::arrayCast<1, const T>(data);
}
template<class T> Containers::StridedArrayView1D<T> MeshData::mutableAttribute(UnsignedInt id) {
template<class T, class> Containers::StridedArrayView2D<const typename std::remove_extent<T>::type> MeshData::attribute(const UnsignedInt id) const {
Containers::StridedArrayView2D<const char> data = attribute(id);
#ifdef CORRADE_GRACEFUL_ASSERT /* Sigh. Brittle. Better idea? */
if(!data.stride()[1]) return {};
#endif
const MeshAttributeData& attribute = _attributes[id];
#ifndef CORRADE_NO_ASSERT
if(!checkAttributeTypeCompatibility<T>(attribute, "Trade::MeshData::attribute():")) return {};
#endif
return Containers::arrayCast<2, const typename std::remove_extent<T>::type>(data);
}
template<class T, class> Containers::StridedArrayView1D<T> MeshData::mutableAttribute(const UnsignedInt id) {
Containers::StridedArrayView2D<char> data = mutableAttribute(id);
#ifdef CORRADE_GRACEFUL_ASSERT /* Sigh. Brittle. Better idea? */
if(!data.stride()[1]) return {};
#endif
#ifndef CORRADE_NO_ASSERT
const MeshAttributeData& attribute = _attributes[id];
if(!checkAttributeTypeCompatibility<T>(_attributes[id], "Trade::MeshData::mutableAttribute():")) return {};
#endif
CORRADE_ASSERT(!isVertexFormatImplementationSpecific(attribute._format),
"Trade::MeshData::mutableAttribute(): can't cast data from an implementation-specific vertex format" << reinterpret_cast<void*>(vertexFormatUnwrap(attribute._format)), {});
CORRADE_ASSERT(Implementation::isVertexFormatCompatible<T>(attribute._format),
"Trade::MeshData::mutableAttribute(): improper type requested for" << attribute._name << "of format" << attribute._format, nullptr);
return Containers::arrayCast<1, T>(data);
}
template<class T> Containers::StridedArrayView1D<const T> MeshData::attribute(MeshAttribute name, UnsignedInt id) const {
template<class T, class> Containers::StridedArrayView2D<typename std::remove_extent<T>::type> MeshData::mutableAttribute(const UnsignedInt id) {
Containers::StridedArrayView2D<char> data = mutableAttribute(id);
#ifdef CORRADE_GRACEFUL_ASSERT /* Sigh. Brittle. Better idea? */
if(!data.stride()[1]) return {};
#endif
const MeshAttributeData& attribute = _attributes[id];
#ifndef CORRADE_NO_ASSERT
if(!checkAttributeTypeCompatibility<T>(attribute, "Trade::MeshData::mutableAttribute():")) return {};
#endif
return Containers::arrayCast<2, typename std::remove_extent<T>::type>(data);
}
template<class T, class> Containers::StridedArrayView1D<const T> MeshData::attribute(MeshAttribute name, UnsignedInt id) const {
Containers::StridedArrayView2D<const char> data = attribute(name, id);
#ifdef CORRADE_GRACEFUL_ASSERT /* Sigh. Brittle. Better idea? */
if(!data.stride()[1]) return {};
#endif
#ifndef CORRADE_NO_ASSERT
const MeshAttributeData& attribute = _attributes[attributeFor(name, id)];
if(!checkAttributeTypeCompatibility<T>(_attributes[attributeFor(name, id)], "Trade::MeshData::attribute():")) return {};
#endif
CORRADE_ASSERT(!isVertexFormatImplementationSpecific(attribute._format),
"Trade::MeshData::attribute(): can't cast data from an implementation-specific vertex format" << reinterpret_cast<void*>(vertexFormatUnwrap(attribute._format)), {});
CORRADE_ASSERT(Implementation::isVertexFormatCompatible<T>(attribute._format),
"Trade::MeshData::attribute(): improper type requested for" << attribute._name << "of format" << attribute._format, nullptr);
return Containers::arrayCast<1, const T>(data);
}
template<class T> Containers::StridedArrayView1D<T> MeshData::mutableAttribute(MeshAttribute name, UnsignedInt id) {
template<class T, class> Containers::StridedArrayView2D<const typename std::remove_extent<T>::type> MeshData::attribute(MeshAttribute name, UnsignedInt id) const {
Containers::StridedArrayView2D<const char> data = attribute(name, id);
#ifdef CORRADE_GRACEFUL_ASSERT /* Sigh. Brittle. Better idea? */
if(!data.stride()[1]) return {};
#endif
const MeshAttributeData& attribute = _attributes[attributeFor(name, id)];
#ifndef CORRADE_NO_ASSERT
if(!checkAttributeTypeCompatibility<T>(attribute, "Trade::MeshData::attribute():")) return {};
#endif
return Containers::arrayCast<2, const typename std::remove_extent<T>::type>(data);
}
template<class T, class> Containers::StridedArrayView1D<T> MeshData::mutableAttribute(MeshAttribute name, UnsignedInt id) {
Containers::StridedArrayView2D<char> data = mutableAttribute(name, id);
#ifdef CORRADE_GRACEFUL_ASSERT /* Sigh. Brittle. Better idea? */
if(!data.stride()[1]) return {};
#endif
#ifndef CORRADE_NO_ASSERT
const MeshAttributeData& attribute = _attributes[attributeFor(name, id)];
if(!checkAttributeTypeCompatibility<T>(_attributes[attributeFor(name, id)], "Trade::MeshData::mutableAttribute():")) return {};
#endif
CORRADE_ASSERT(!isVertexFormatImplementationSpecific(attribute._format),
"Trade::MeshData::mutableAttribute(): can't cast data from an implementation-specific vertex format" << reinterpret_cast<void*>(vertexFormatUnwrap(attribute._format)), {});
CORRADE_ASSERT(Implementation::isVertexFormatCompatible<T>(attribute._format),
"Trade::MeshData::mutableAttribute(): improper type requested for" << attribute._name << "of format" << attribute._format, nullptr);
return Containers::arrayCast<1, T>(data);
}
template<class T, class> Containers::StridedArrayView2D<typename std::remove_extent<T>::type> MeshData::mutableAttribute(MeshAttribute name, UnsignedInt id) {
Containers::StridedArrayView2D<char> data = mutableAttribute(name, id);
#ifdef CORRADE_GRACEFUL_ASSERT /* Sigh. Brittle. Better idea? */
if(!data.stride()[1]) return {};
#endif
const MeshAttributeData& attribute = _attributes[attributeFor(name, id)];
#ifndef CORRADE_NO_ASSERT
if(!checkAttributeTypeCompatibility<T>(attribute, "Trade::MeshData::mutableAttribute():")) return {};
#endif
return Containers::arrayCast<2, typename std::remove_extent<T>::type>(data);
}
}}
#endif

276
src/Magnum/Trade/Test/MeshDataTest.cpp

@ -65,6 +65,15 @@ struct MeshDataTest: TestSuite::Tester {
void constructAttributeWrongStride();
void constructAttributeWrongDataAccess();
void constructArrayAttribute();
void constructArrayAttributeNonContiguous();
void constructArrayAttribute2D();
void constructArrayAttribute2DWrongSize();
void constructArrayAttribute2DNonContiguous();
void constructArrayAttributeTypeErased();
void constructArrayAttributeOffsetOnly();
void constructArrayAttributeNotAllowed();
void construct();
void constructZeroIndices();
void constructZeroAttributes();
@ -128,6 +137,9 @@ struct MeshDataTest: TestSuite::Tester {
void implementationSpecificVertexFormatWrongAccess();
void implementationSpecificVertexFormatNotContained();
void arrayAttribute();
void arrayAttributeWrongAccess();
void mutableAccessNotAllowed();
void indicesNotIndexed();
@ -188,6 +200,15 @@ MeshDataTest::MeshDataTest() {
&MeshDataTest::constructAttributeWrongStride,
&MeshDataTest::constructAttributeWrongDataAccess,
&MeshDataTest::constructArrayAttribute,
&MeshDataTest::constructArrayAttributeNonContiguous,
&MeshDataTest::constructArrayAttribute2D,
&MeshDataTest::constructArrayAttribute2DWrongSize,
&MeshDataTest::constructArrayAttribute2DNonContiguous,
&MeshDataTest::constructArrayAttributeTypeErased,
&MeshDataTest::constructArrayAttributeOffsetOnly,
&MeshDataTest::constructArrayAttributeNotAllowed,
&MeshDataTest::construct,
&MeshDataTest::constructZeroIndices,
&MeshDataTest::constructZeroAttributes,
@ -298,6 +319,9 @@ MeshDataTest::MeshDataTest() {
&MeshDataTest::implementationSpecificVertexFormatWrongAccess,
&MeshDataTest::implementationSpecificVertexFormatNotContained,
&MeshDataTest::arrayAttribute,
&MeshDataTest::arrayAttributeWrongAccess,
&MeshDataTest::mutableAccessNotAllowed,
&MeshDataTest::indicesNotIndexed,
@ -465,6 +489,7 @@ void MeshDataTest::constructAttribute() {
const Vector2 positionData[3];
MeshAttributeData positions{MeshAttribute::Position, Containers::arrayView(positionData)};
CORRADE_VERIFY(!positions.isOffsetOnly());
CORRADE_COMPARE(positions.arraySize(), 0);
CORRADE_COMPARE(positions.name(), MeshAttribute::Position);
CORRADE_COMPARE(positions.format(), VertexFormat::Vector2);
CORRADE_VERIFY(positions.data().data() == positionData);
@ -473,10 +498,12 @@ void MeshDataTest::constructAttribute() {
constexpr MeshAttributeData cpositions{MeshAttribute::Position, Containers::arrayView(Positions)};
constexpr bool isOffsetOnly = cpositions.isOffsetOnly();
constexpr UnsignedShort arraySize = cpositions.arraySize();
constexpr MeshAttribute name = cpositions.name();
constexpr VertexFormat format = cpositions.format();
constexpr Containers::StridedArrayView1D<const void> data = cpositions.data();
CORRADE_VERIFY(!isOffsetOnly);
CORRADE_COMPARE(arraySize, 0);
CORRADE_COMPARE(name, MeshAttribute::Position);
CORRADE_COMPARE(format, VertexFormat::Vector2);
CORRADE_COMPARE(data.data(), Positions);
@ -497,6 +524,7 @@ void MeshDataTest::constructAttribute2D() {
MeshAttributeData positions{MeshAttribute::Position, VertexFormat::Vector2, positionView};
CORRADE_VERIFY(!positions.isOffsetOnly());
CORRADE_COMPARE(positions.arraySize(), 0);
CORRADE_COMPARE(positions.name(), MeshAttribute::Position);
CORRADE_COMPARE(positions.format(), VertexFormat::Vector2);
CORRADE_COMPARE(positions.data().data(), positionView.data());
@ -528,6 +556,7 @@ void MeshDataTest::constructAttributeTypeErased() {
const Vector3 positionData[3]{};
MeshAttributeData positions{MeshAttribute::Position, VertexFormat::Vector3, Containers::arrayCast<const char>(Containers::stridedArrayView(positionData))};
CORRADE_VERIFY(!positions.isOffsetOnly());
CORRADE_COMPARE(positions.arraySize(), 0);
CORRADE_COMPARE(positions.name(), MeshAttribute::Position);
CORRADE_COMPARE(positions.format(), VertexFormat::Vector3);
CORRADE_VERIFY(positions.data().data() == positionData);
@ -536,6 +565,7 @@ void MeshDataTest::constructAttributeTypeErased() {
void MeshDataTest::constructAttributeNullptr() {
MeshAttributeData positions{MeshAttribute::Position, VertexFormat::Vector2, nullptr};
CORRADE_VERIFY(!positions.isOffsetOnly());
CORRADE_COMPARE(positions.arraySize(), 0);
CORRADE_COMPARE(positions.name(), MeshAttribute::Position);
CORRADE_COMPARE(positions.format(), VertexFormat::Vector2);
CORRADE_VERIFY(!positions.data().data());
@ -544,6 +574,7 @@ void MeshDataTest::constructAttributeNullptr() {
void MeshDataTest::constructAttributePadding() {
MeshAttributeData padding{-35};
CORRADE_VERIFY(!padding.isOffsetOnly());
CORRADE_COMPARE(padding.arraySize(), 0);
CORRADE_COMPARE(padding.name(), MeshAttribute{});
CORRADE_COMPARE(padding.format(), VertexFormat{});
CORRADE_COMPARE(padding.data().size(), 0);
@ -569,6 +600,7 @@ void MeshDataTest::constructAttributeOffsetOnly() {
MeshAttributeData a{MeshAttribute::TextureCoordinates, VertexFormat::Vector2, sizeof(Vector2), 2, 2*sizeof(Vector2)};
CORRADE_VERIFY(a.isOffsetOnly());
CORRADE_COMPARE(a.arraySize(), 0);
CORRADE_COMPARE(a.name(), MeshAttribute::TextureCoordinates);
CORRADE_COMPARE(a.format(), VertexFormat::Vector2);
CORRADE_COMPARE_AS(Containers::arrayCast<const Vector2>(a.data(vertexData)),
@ -577,6 +609,7 @@ void MeshDataTest::constructAttributeOffsetOnly() {
constexpr MeshAttributeData ca{MeshAttribute::TextureCoordinates, VertexFormat::Vector2, sizeof(Vector2), 2, 2*sizeof(Vector2)};
CORRADE_VERIFY(ca.isOffsetOnly());
CORRADE_COMPARE(ca.arraySize(), 0);
CORRADE_COMPARE(ca.name(), MeshAttribute::TextureCoordinates);
CORRADE_COMPARE(ca.format(), VertexFormat::Vector2);
CORRADE_COMPARE_AS(Containers::arrayCast<const Vector2>(a.data(vertexData)),
@ -642,6 +675,140 @@ void MeshDataTest::constructAttributeWrongDataAccess() {
"Trade::MeshAttributeData::data(): the attribute is a relative offset, supply a data array\n");
}
constexpr Vector2 ArrayVertexData[3*4];
void MeshDataTest::constructArrayAttribute() {
Vector2 vertexData[3*4];
Containers::StridedArrayView2D<Vector2> attribute{vertexData, {3, 4}};
MeshAttributeData data{meshAttributeCustom(35), attribute};
CORRADE_VERIFY(!data.isOffsetOnly());
CORRADE_COMPARE(data.name(), meshAttributeCustom(35));
CORRADE_COMPARE(data.format(), VertexFormat::Vector2);
CORRADE_COMPARE(data.arraySize(), 4);
CORRADE_VERIFY(data.data().data() == vertexData);
CORRADE_COMPARE(data.data().size(), 3);
CORRADE_COMPARE(data.data().stride(), sizeof(Vector2)*4);
constexpr Containers::StridedArrayView2D<const Vector2> cattribute{ArrayVertexData, {3, 4}};
constexpr MeshAttributeData cdata{meshAttributeCustom(35), cattribute};
CORRADE_VERIFY(!cdata.isOffsetOnly());
CORRADE_COMPARE(cdata.name(), meshAttributeCustom(35));
CORRADE_COMPARE(cdata.format(), VertexFormat::Vector2);
CORRADE_COMPARE(cdata.arraySize(), 4);
CORRADE_VERIFY(cdata.data().data() == ArrayVertexData);
CORRADE_COMPARE(cdata.data().size(), 3);
CORRADE_COMPARE(cdata.data().stride(), sizeof(Vector2)*4);
}
void MeshDataTest::constructArrayAttributeNonContiguous() {
Vector2 vertexData[4*3]{};
std::ostringstream out;
Error redirectError{&out};
MeshAttributeData{meshAttributeCustom(35),
Containers::StridedArrayView2D<Vector2>{vertexData,
{4, 3}}.every({1, 2})};
CORRADE_COMPARE(out.str(), "Trade::MeshAttributeData: second view dimension is not contiguous\n");
}
void MeshDataTest::constructArrayAttribute2D() {
char vertexData[3*4*sizeof(Vector2)];
MeshAttributeData data{meshAttributeCustom(35), VertexFormat::Vector2, 4, Containers::StridedArrayView2D<char>{vertexData, {3, 4*sizeof(Vector2)}}};
CORRADE_VERIFY(!data.isOffsetOnly());
CORRADE_COMPARE(data.name(), meshAttributeCustom(35));
CORRADE_COMPARE(data.format(), VertexFormat::Vector2);
CORRADE_COMPARE(data.arraySize(), 4);
CORRADE_VERIFY(data.data().data() == vertexData);
CORRADE_COMPARE(data.data().size(), 3);
CORRADE_COMPARE(data.data().stride(), sizeof(Vector2)*4);
}
void MeshDataTest::constructArrayAttribute2DWrongSize() {
char vertexData[3*4*sizeof(Vector2)]{};
std::ostringstream out;
Error redirectError{&out};
MeshAttributeData{meshAttributeCustom(35), VertexFormat::Vector2, 3,
Containers::StridedArrayView2D<char>{vertexData,
{3, 4*sizeof(Vector2)}}};
CORRADE_COMPARE(out.str(), "Trade::MeshAttributeData: second view dimension size 32 doesn't match VertexFormat::Vector2 and array size 3\n");
}
void MeshDataTest::constructArrayAttribute2DNonContiguous() {
char vertexData[4*3*sizeof(Vector2)]{};
std::ostringstream out;
Error redirectError{&out};
MeshAttributeData{meshAttributeCustom(35), VertexFormat::Vector2, 2,
Containers::StridedArrayView2D<char>{vertexData,
{3, sizeof(Vector2)*4}}.every({1, 2})};
CORRADE_COMPARE(out.str(), "Trade::MeshAttributeData: second view dimension is not contiguous\n");
}
void MeshDataTest::constructArrayAttributeTypeErased() {
Vector2 vertexData[3*4];
Containers::StridedArrayView1D<Vector2> attribute{vertexData, 3, 4*sizeof(Vector2)};
MeshAttributeData data{meshAttributeCustom(35), VertexFormat::Vector2, 4, attribute};
CORRADE_VERIFY(!data.isOffsetOnly());
CORRADE_COMPARE(data.name(), meshAttributeCustom(35));
CORRADE_COMPARE(data.format(), VertexFormat::Vector2);
CORRADE_COMPARE(data.arraySize(), 4);
CORRADE_VERIFY(data.data().data() == vertexData);
CORRADE_COMPARE(data.data().size(), 3);
CORRADE_COMPARE(data.data().stride(), sizeof(Vector2)*4);
}
void MeshDataTest::constructArrayAttributeOffsetOnly() {
MeshAttributeData data{meshAttributeCustom(35), VertexFormat::Vector2, sizeof(Vector2), 3, sizeof(Vector2), 4};
CORRADE_VERIFY(data.isOffsetOnly());
CORRADE_COMPARE(data.name(), meshAttributeCustom(35));
CORRADE_COMPARE(data.format(), VertexFormat::Vector2);
CORRADE_COMPARE(data.arraySize(), 4);
Vector2 vertexData[1 + 3*4];
CORRADE_VERIFY(data.data(vertexData).data() == vertexData + 1);
CORRADE_COMPARE(data.data(vertexData).size(), 3);
CORRADE_COMPARE(data.data(vertexData).stride(), sizeof(Vector2));
constexpr MeshAttributeData cdata{meshAttributeCustom(35), VertexFormat::Vector2, sizeof(Vector2), 3, sizeof(Vector2), 4};
CORRADE_VERIFY(cdata.isOffsetOnly());
CORRADE_COMPARE(cdata.name(), meshAttributeCustom(35));
CORRADE_COMPARE(cdata.format(), VertexFormat::Vector2);
CORRADE_COMPARE(cdata.arraySize(), 4);
}
void MeshDataTest::constructArrayAttributeNotAllowed() {
Vector2 positionData[3*3];
Containers::ArrayView<Vector2> positions{positionData};
Containers::StridedArrayView2D<Vector2> positions2D{positionData, {3, 3}};
auto positions2Dchar = Containers::arrayCast<2, const char>(positions2D);
/* This is all fine */
MeshAttributeData{MeshAttribute::Position, VertexFormat::Vector2, 0, positions};
MeshAttributeData{MeshAttribute::Position, VertexFormat::Vector2, 0, 3, 6*sizeof(Vector2), 0};
MeshAttributeData{meshAttributeCustom(35), vertexFormatWrap(0xdead), 0, positions};
MeshAttributeData{meshAttributeCustom(35), positions2D};
MeshAttributeData{meshAttributeCustom(35), VertexFormat::Vector2, 3, positions2Dchar};
MeshAttributeData{meshAttributeCustom(35), VertexFormat::Vector2, 0, 3, 6*sizeof(Vector2), 3};
/* This is not */
std::ostringstream out;
Error redirectError{&out};
MeshAttributeData{MeshAttribute::Position, VertexFormat::Vector2, 3, Containers::arrayView(positionData)};
MeshAttributeData{meshAttributeCustom(35), vertexFormatWrap(0xdead), 3, Containers::arrayView(positionData)};
MeshAttributeData{MeshAttribute::Position, positions2D};
MeshAttributeData{MeshAttribute::Position, VertexFormat::Vector2, 3, positions2Dchar};
MeshAttributeData{MeshAttribute::Position, VertexFormat::Vector2, 0, 3, 6*sizeof(Vector2), 3};
MeshAttributeData{meshAttributeCustom(35), vertexFormatWrap(0xdead), 0, 3, 6*sizeof(Vector2), 3};
CORRADE_COMPARE(out.str(),
"Trade::MeshAttributeData: Trade::MeshAttribute::Position can't be an array attribute\n"
"Trade::MeshAttributeData: array attributes can't have an implementation-specific format\n"
"Trade::MeshAttributeData: Trade::MeshAttribute::Position can't be an array attribute\n"
"Trade::MeshAttributeData: Trade::MeshAttribute::Position can't be an array attribute\n"
"Trade::MeshAttributeData: Trade::MeshAttribute::Position can't be an array attribute\n"
"Trade::MeshAttributeData: array attributes can't have an implementation-specific format\n");
}
void MeshDataTest::construct() {
struct Vertex {
Vector3 position;
@ -740,6 +907,11 @@ void MeshDataTest::construct() {
CORRADE_COMPARE(data.attributeStride(1), sizeof(Vertex));
CORRADE_COMPARE(data.attributeStride(2), sizeof(Vertex));
CORRADE_COMPARE(data.attributeStride(3), sizeof(Vertex));
CORRADE_COMPARE(data.attributeArraySize(0), 0);
CORRADE_COMPARE(data.attributeArraySize(1), 0);
CORRADE_COMPARE(data.attributeArraySize(2), 0);
CORRADE_COMPARE(data.attributeArraySize(3), 0);
CORRADE_COMPARE(data.attributeArraySize(4), 0);
/* Typeless access by ID with a cast later */
CORRADE_COMPARE((Containers::arrayCast<1, const Vector3>(
@ -817,6 +989,11 @@ void MeshDataTest::construct() {
CORRADE_COMPARE(data.attributeStride(MeshAttribute::TextureCoordinates, 0), sizeof(Vertex));
CORRADE_COMPARE(data.attributeStride(MeshAttribute::TextureCoordinates, 1), sizeof(Vertex));
CORRADE_COMPARE(data.attributeStride(meshAttributeCustom(13)), sizeof(Vertex));
CORRADE_COMPARE(data.attributeArraySize(MeshAttribute::Position), 0);
CORRADE_COMPARE(data.attributeArraySize(MeshAttribute::Normal), 0);
CORRADE_COMPARE(data.attributeArraySize(MeshAttribute::TextureCoordinates, 0), 0);
CORRADE_COMPARE(data.attributeArraySize(MeshAttribute::TextureCoordinates, 1), 0);
CORRADE_COMPARE(data.attributeArraySize(meshAttributeCustom(13)), 0);
/* Typeless access by name with a cast later */
CORRADE_COMPARE((Containers::arrayCast<1, const Vector3>(
@ -1996,6 +2173,99 @@ void MeshDataTest::implementationSpecificVertexFormatNotContained() {
"Trade::MeshData: attribute 1 [0xdead:0xdeaf] is not contained in passed vertexData array [0xbadda9:0xbaddac]\n");
}
void MeshDataTest::arrayAttribute() {
Vector2 vertexData[3*4]{
{1.0f, 2.0f}, {3.0f, 4.0f}, {5.0f, 6.0f}, {7.0f, 8.0f},
{1.1f, 2.2f}, {3.3f, 4.4f}, {5.5f, 6.6f}, {7.7f, 8.8f},
{0.1f, 0.2f}, {0.3f, 0.4f}, {0.5f, 0.6f}, {0.7f, 0.8f},
};
Containers::StridedArrayView2D<Vector2> positions2D{vertexData, {3, 4}};
MeshData data{MeshPrimitive::TriangleFan, DataFlag::Mutable, vertexData, {
MeshAttributeData{meshAttributeCustom(35), positions2D}
}};
CORRADE_COMPARE(data.vertexCount(), 3);
CORRADE_COMPARE(data.attributeArraySize(meshAttributeCustom(35)), 4);
/* Raw access is "as usual" */
auto attribute = Containers::arrayCast<2, const Vector2>(data.attribute(0));
auto attributeByName = Containers::arrayCast<2, const Vector2>(data.attribute(meshAttributeCustom(35)));
auto mutableAttribute = Containers::arrayCast<2, Vector2>(data.mutableAttribute(0));
auto mutableAttributeByName = Containers::arrayCast<2, Vector2>(data.mutableAttribute(meshAttributeCustom(35)));
CORRADE_COMPARE(attribute.size()[0], 3);
CORRADE_COMPARE(attributeByName.size()[0], 3);
CORRADE_COMPARE(mutableAttribute.size()[0], 3);
CORRADE_COMPARE(mutableAttributeByName.size()[0], 3);
for(std::size_t i = 0; i != 3; ++i) {
CORRADE_ITERATION(i);
CORRADE_COMPARE_AS(attribute[i], positions2D[i],
TestSuite::Compare::Container);
CORRADE_COMPARE_AS(attributeByName[i], positions2D[i],
TestSuite::Compare::Container);
CORRADE_COMPARE_AS(mutableAttribute[i], positions2D[i],
TestSuite::Compare::Container);
CORRADE_COMPARE_AS(mutableAttributeByName[i], positions2D[i],
TestSuite::Compare::Container);
}
/* Typed access */
attribute = data.attribute<Vector2[]>(0);
attributeByName = data.attribute<Vector2[]>(meshAttributeCustom(35));
mutableAttribute = data.mutableAttribute<Vector2[]>(0);
mutableAttributeByName = data.mutableAttribute<Vector2[]>(meshAttributeCustom(35));
CORRADE_COMPARE(attribute.size()[0], 3);
CORRADE_COMPARE(attributeByName.size()[0], 3);
CORRADE_COMPARE(mutableAttribute.size()[0], 3);
CORRADE_COMPARE(mutableAttributeByName.size()[0], 3);
for(std::size_t i = 0; i != 3; ++i) {
CORRADE_ITERATION(i);
CORRADE_COMPARE_AS(attribute[i], positions2D[i],
TestSuite::Compare::Container);
CORRADE_COMPARE_AS(attributeByName[i], positions2D[i],
TestSuite::Compare::Container);
CORRADE_COMPARE_AS(mutableAttribute[i], positions2D[i],
TestSuite::Compare::Container);
CORRADE_COMPARE_AS(mutableAttributeByName[i], positions2D[i],
TestSuite::Compare::Container);
}
}
void MeshDataTest::arrayAttributeWrongAccess() {
Vector2 vertexData[3*4]{
{1.0f, 2.0f}, {3.0f, 4.0f}, {5.0f, 6.0f}, {7.0f, 8.0f},
{1.1f, 2.2f}, {3.3f, 4.4f}, {5.5f, 6.6f}, {7.7f, 8.8f},
{0.1f, 0.2f}, {0.3f, 0.4f}, {0.5f, 0.6f}, {0.7f, 0.8f},
};
Containers::StridedArrayView1D<Vector2> positions{vertexData, 3, 4*sizeof(Vector2)};
Containers::StridedArrayView2D<Vector2> positions2D{vertexData, {3, 4}};
MeshData data{MeshPrimitive::TriangleFan, DataFlag::Mutable, vertexData, {
MeshAttributeData{MeshAttribute::Position, positions},
MeshAttributeData{meshAttributeCustom(35), positions2D}
}};
std::ostringstream out;
Error redirectError{&out};
data.attribute<Vector2[]>(0);
data.attribute<Vector2>(1);
data.mutableAttribute<Vector2[]>(0);
data.mutableAttribute<Vector2>(1);
data.attribute<Vector2[]>(MeshAttribute::Position);
data.attribute<Vector2>(meshAttributeCustom(35));
data.mutableAttribute<Vector2[]>(MeshAttribute::Position);
data.mutableAttribute<Vector2>(meshAttributeCustom(35));
CORRADE_COMPARE(out.str(),
"Trade::MeshData::attribute(): use T[] to access an array attribute\n"
"Trade::MeshData::attribute(): use T[] to access an array attribute\n"
"Trade::MeshData::mutableAttribute(): use T[] to access an array attribute\n"
"Trade::MeshData::mutableAttribute(): use T[] to access an array attribute\n"
"Trade::MeshData::attribute(): use T[] to access an array attribute\n"
"Trade::MeshData::attribute(): use T[] to access an array attribute\n"
"Trade::MeshData::mutableAttribute(): use T[] to access an array attribute\n"
"Trade::MeshData::mutableAttribute(): use T[] to access an array attribute\n");
}
void MeshDataTest::mutableAccessNotAllowed() {
const UnsignedShort indexData[]{0, 1, 0};
const Vector2 vertexData[]{{0.1f, 0.2f}, {0.4f, 0.5f}};
@ -2073,6 +2343,7 @@ void MeshDataTest::attributeNotFound() {
data.attributeFormat(2);
data.attributeOffset(2);
data.attributeStride(2);
data.attributeArraySize(2);
data.attribute(2);
data.attribute<Vector2>(2);
data.attributeId(MeshAttribute::Position);
@ -2083,6 +2354,8 @@ void MeshDataTest::attributeNotFound() {
data.attributeOffset(MeshAttribute::Color, 2);
data.attributeStride(MeshAttribute::Position);
data.attributeStride(MeshAttribute::Color, 2);
data.attributeArraySize(MeshAttribute::Position);
data.attributeArraySize(MeshAttribute::Color, 2);
data.attribute(MeshAttribute::Position);
data.attribute(MeshAttribute::Color, 2);
data.attribute<Vector2>(MeshAttribute::Position);
@ -2097,6 +2370,7 @@ void MeshDataTest::attributeNotFound() {
"Trade::MeshData::attributeFormat(): index 2 out of range for 2 attributes\n"
"Trade::MeshData::attributeOffset(): index 2 out of range for 2 attributes\n"
"Trade::MeshData::attributeStride(): index 2 out of range for 2 attributes\n"
"Trade::MeshData::attributeArraySize(): index 2 out of range for 2 attributes\n"
"Trade::MeshData::attribute(): index 2 out of range for 2 attributes\n"
"Trade::MeshData::attribute(): index 2 out of range for 2 attributes\n"
"Trade::MeshData::attributeId(): index 0 out of range for 0 Trade::MeshAttribute::Position attributes\n"
@ -2107,6 +2381,8 @@ void MeshDataTest::attributeNotFound() {
"Trade::MeshData::attributeOffset(): index 2 out of range for 2 Trade::MeshAttribute::Color attributes\n"
"Trade::MeshData::attributeStride(): index 0 out of range for 0 Trade::MeshAttribute::Position attributes\n"
"Trade::MeshData::attributeStride(): index 2 out of range for 2 Trade::MeshAttribute::Color attributes\n"
"Trade::MeshData::attributeArraySize(): index 0 out of range for 0 Trade::MeshAttribute::Position attributes\n"
"Trade::MeshData::attributeArraySize(): index 2 out of range for 2 Trade::MeshAttribute::Color attributes\n"
"Trade::MeshData::attribute(): index 0 out of range for 0 Trade::MeshAttribute::Position attributes\n"
"Trade::MeshData::attribute(): index 2 out of range for 2 Trade::MeshAttribute::Color attributes\n"
"Trade::MeshData::attribute(): index 0 out of range for 0 Trade::MeshAttribute::Position attributes\n"

Loading…
Cancel
Save