Browse Source

Trade: implement support for Weights & JointIds in MeshData.

This is the first builtin array attribute, with one of the objectives
being an ability to support an arbitrary count of per-vertex weights in
a single contiguous attribute without the complexity of having to go
through several four-component attributes.

On the shader side it still needs to get cut into at most four
components per attribute, but there's no reason for such limitation to
get propagated here as well.

Co-authored-by: Vladimír Vondruš <mosra@centrum.cz>
pull/499/head
Squareys 4 years ago committed by Vladimír Vondruš
parent
commit
f447e994d2
  1. 16
      doc/snippets/MagnumTrade.cpp
  2. 85
      src/Magnum/Trade/MeshData.cpp
  3. 203
      src/Magnum/Trade/MeshData.h
  4. 362
      src/Magnum/Trade/Test/MeshDataTest.cpp

16
doc/snippets/MagnumTrade.cpp

@ -887,6 +887,22 @@ static_cast<void>(triangleIds);
static_cast<void>(triangleCounts); static_cast<void>(triangleCounts);
} }
{
/* [MeshData-jointIdsAsArray] */
Trade::MeshData data = DOXYGEN_ELLIPSIS(Trade::MeshData{{}, 0});
Containers::Array<UnsignedInt> array = data.jointIdsAsArray();
Containers::StridedArrayView2D<UnsignedInt> array2D{array,
{data.vertexCount(), data.attributeArraySize(Trade::MeshAttribute::JointIds)}};
for(Containers::StridedArrayView1D<UnsignedInt> i: array2D) {
for(UnsignedInt j: i) {
DOXYGEN_IGNORE(static_cast<void>(j);)// do something with joint ID j in vertex i
}
}
/* [MeshData-jointIdsAsArray] */
}
#ifdef MAGNUM_BUILD_DEPRECATED #ifdef MAGNUM_BUILD_DEPRECATED
{ {
CORRADE_IGNORE_DEPRECATED_PUSH CORRADE_IGNORE_DEPRECATED_PUSH

85
src/Magnum/Trade/MeshData.cpp

@ -193,6 +193,18 @@ MeshData::MeshData(const MeshPrimitive primitive, Containers::Array<char>&& inde
} }
} }
} }
/* Verify that count and array sizes of skin joint IDs and weights match */
const UnsignedInt jointIdAttributeCount = attributeCount(MeshAttribute::JointIds);
const UnsignedInt weightAttributeCount = attributeCount(MeshAttribute::Weights);
CORRADE_ASSERT(weightAttributeCount == jointIdAttributeCount,
"Trade::MeshData: expected" << jointIdAttributeCount << "weight attributes to match joint IDs but got" << weightAttributeCount, );
for(UnsignedInt i = 0; i != jointIdAttributeCount; ++i) {
const UnsignedInt jointIdsArraySize = attributeArraySize(MeshAttribute::JointIds, i);
const UnsignedInt weightsArraySize = attributeArraySize(MeshAttribute::Weights, i);
CORRADE_ASSERT(weightsArraySize == jointIdsArraySize,
"Trade::MeshData: expected" << jointIdsArraySize << "array items for weight attribute" << i << "to match joint IDs but got" << weightsArraySize, );
}
#endif #endif
} }
@ -835,6 +847,77 @@ Containers::Array<Color4> MeshData::colorsAsArray(const UnsignedInt id) const {
return out; return out;
} }
void MeshData::jointIdsInto(const Containers::StridedArrayView2D<UnsignedInt>& destination, const UnsignedInt id) const {
const UnsignedInt attributeId = findAttributeIdInternal(MeshAttribute::JointIds, id);
CORRADE_ASSERT(attributeId != ~UnsignedInt{},
"Trade::MeshData::jointIdsInto(): index" << id << "out of range for" << attributeCount(MeshAttribute::JointIds) << "joint ID attributes", );
const MeshAttributeData& attribute = _attributes[attributeId];
#ifndef CORRADE_NO_ASSERT
const Containers::Size2D expectedSize{_vertexCount, attribute._arraySize};
#endif
CORRADE_ASSERT(destination.size() == expectedSize,
"Trade::MeshData::jointIdsInto(): expected a view with" << expectedSize << "elements but got" << destination.size(), );
CORRADE_ASSERT(destination.isContiguous<1>(),
"Trade::MeshData::jointIdsInto(): second view dimension is not contiguous", );
CORRADE_ASSERT(!isVertexFormatImplementationSpecific(attribute._format),
"Trade::MeshData::jointIdsInto(): can't extract data out of an implementation-specific vertex format" << reinterpret_cast<void*>(vertexFormatUnwrap(attribute._format)), );
const Containers::StridedArrayView1D<const void> attributeData = attributeDataViewInternal(attribute);
if(attribute._format == VertexFormat::UnsignedInt)
Utility::copy(Containers::arrayCast<2, const UnsignedInt>(attributeData, attribute._arraySize), destination);
else if(attribute._format == VertexFormat::UnsignedByte)
Math::castInto(Containers::arrayCast<2, const UnsignedByte>(attributeData, attribute._arraySize), destination);
else if(attribute._format == VertexFormat::UnsignedShort)
Math::castInto(Containers::arrayCast<2, const UnsignedShort>(attributeData, attribute._arraySize), destination);
else CORRADE_INTERNAL_ASSERT_UNREACHABLE(); /* LCOV_EXCL_LINE */
}
Containers::Array<UnsignedInt> MeshData::jointIdsAsArray(const UnsignedInt id) const {
const UnsignedInt attributeId = findAttributeIdInternal(MeshAttribute::JointIds, id);
CORRADE_ASSERT(attributeId != ~UnsignedInt{},
"Trade::MeshData::jointIdsAsArray(): index" << id << "out of range for" << attributeCount(MeshAttribute::JointIds) << "joint ID attributes", {});
const MeshAttributeData& attribute = _attributes[attributeId];
Containers::Array<UnsignedInt> out{_vertexCount*attribute._arraySize};
jointIdsInto(Containers::StridedArrayView2D<UnsignedInt>{out, {_vertexCount, attribute._arraySize}}, id);
return out;
}
void MeshData::weightsInto(const Containers::StridedArrayView2D<Float>& destination, const UnsignedInt id) const {
const UnsignedInt attributeId = findAttributeIdInternal(MeshAttribute::Weights, id);
CORRADE_ASSERT(attributeId != ~UnsignedInt{},
"Trade::MeshData::weightsInto(): index" << id << "out of range for" << attributeCount(MeshAttribute::Weights) << "weight attributes", );
const MeshAttributeData& attribute = _attributes[attributeId];
#ifndef CORRADE_NO_ASSERT
const Containers::Size2D expectedSize{_vertexCount, attribute._arraySize};
#endif
CORRADE_ASSERT(destination.size() == expectedSize,
"Trade::MeshData::weightsInto(): expected a view with" << expectedSize << "elements but got" << destination.size(), );
CORRADE_ASSERT(destination.isContiguous<1>(),
"Trade::MeshData::weightsInto(): second view dimension is not contiguous", );
CORRADE_ASSERT(!isVertexFormatImplementationSpecific(attribute._format),
"Trade::MeshData::weightsInto(): can't extract data out of an implementation-specific vertex format" << reinterpret_cast<void*>(vertexFormatUnwrap(attribute._format)), );
const Containers::StridedArrayView1D<const void> attributeData = attributeDataViewInternal(attribute);
if(attribute._format == VertexFormat::Float)
Utility::copy(Containers::arrayCast<2, const Float>(attributeData, attribute._arraySize), destination);
else if(attribute._format == VertexFormat::Half)
Math::unpackHalfInto(Containers::arrayCast<2, const UnsignedShort>(attributeData, attribute._arraySize), destination);
else if(attribute._format == VertexFormat::UnsignedByteNormalized)
Math::unpackInto(Containers::arrayCast<2, const UnsignedByte>(attributeData, attribute._arraySize), destination);
else if(attribute._format == VertexFormat::UnsignedShortNormalized)
Math::unpackInto(Containers::arrayCast<2, const UnsignedShort>(attributeData, attribute._arraySize), destination);
else CORRADE_INTERNAL_ASSERT_UNREACHABLE(); /* LCOV_EXCL_LINE */
}
Containers::Array<Float> MeshData::weightsAsArray(const UnsignedInt id) const {
const UnsignedInt attributeId = findAttributeIdInternal(MeshAttribute::Weights, id);
CORRADE_ASSERT(attributeId != ~UnsignedInt{}, "Trade::MeshData::weightsAsArray(): index" << id << "out of range for" << attributeCount(MeshAttribute::JointIds) << "weight attributes", {});
const MeshAttributeData& attribute = _attributes[attributeId];
Containers::Array<Float> out{_vertexCount*attribute._arraySize};
weightsInto(Containers::StridedArrayView2D<Float>{out, {_vertexCount, attribute._arraySize}}, id);
return out;
}
void MeshData::objectIdsInto(const Containers::StridedArrayView1D<UnsignedInt>& destination, const UnsignedInt id) const { void MeshData::objectIdsInto(const Containers::StridedArrayView1D<UnsignedInt>& destination, const UnsignedInt id) const {
const UnsignedInt attributeId = findAttributeIdInternal(MeshAttribute::ObjectId, id); const UnsignedInt attributeId = findAttributeIdInternal(MeshAttribute::ObjectId, id);
CORRADE_ASSERT(attributeId != ~UnsignedInt{}, "Trade::MeshData::objectIdsInto(): index" << id << "out of range for" << attributeCount(MeshAttribute::ObjectId) << "object ID attributes", ); CORRADE_ASSERT(attributeId != ~UnsignedInt{}, "Trade::MeshData::objectIdsInto(): index" << id << "out of range for" << attributeCount(MeshAttribute::ObjectId) << "object ID attributes", );
@ -896,6 +979,8 @@ Debug& operator<<(Debug& debug, const MeshAttribute value) {
_c(Normal) _c(Normal)
_c(TextureCoordinates) _c(TextureCoordinates)
_c(Color) _c(Color)
_c(JointIds)
_c(Weights)
_c(ObjectId) _c(ObjectId)
#undef _c #undef _c
/* LCOV_EXCL_STOP */ /* LCOV_EXCL_STOP */

203
src/Magnum/Trade/MeshData.h

@ -151,6 +151,47 @@ enum class MeshAttribute: UnsignedShort {
*/ */
Color, Color,
/**
* Skin joint IDs. Array attribute, type is usually
* @ref VertexFormat::UnsignedInt, but can be also
* @ref VertexFormat::UnsignedShort or
* @ref VertexFormat::UnsignedByte. Array size is not limited or enforced
* in any way, but shaders usually expect at most 8 items, or alternatively
* two instances of this attribute with at most 4 items in each.
*
* Count of instances of this attribute and array size of each instance is
* expected to match instance count and array sizes of
* @ref MeshAttribute::Weights.
*
* Corresponds to @ref Shaders::GenericGL::JointIds and
* @ref Shaders::GenericGL::SecondaryJointIds, divided between them based
* on array size or attribute count.
* @m_since_latest
* @see @ref MeshData::jointIdsAsArray()
*/
JointIds,
/**
* Skin weights. Array attribute, type is usually @ref VertexFormat::Float,
* but can be also @ref VertexFormat::Half,
* @ref VertexFormat::UnsignedByteNormalized or
* @ref VertexFormat::UnsignedShortNormalized. Array size is not limited or
* enforced in any way, but shaders usually expect at most 8 items, or
* alternatively two instances of this attribute with at most 4 items in
* each.
*
* Count of instances of this attribute and array size of each instance is
* expected to match instance count and array sizes of
* @ref MeshAttribute::JointIds.
*
* Corresponds to @ref Shaders::GenericGL::Weights and
* @ref Shaders::GenericGL::SecondaryWeights, divided between them based
* on array size or attribute count.
* @m_since_latest
* @see @ref MeshData::weightsAsArray()
*/
Weights,
/** /**
* (Instanced) object ID for editor selection or scene annotation. Type is * (Instanced) object ID for editor selection or scene annotation. Type is
* usually @ref VertexFormat::UnsignedInt, but can be also * usually @ref VertexFormat::UnsignedInt, but can be also
@ -406,10 +447,11 @@ class MAGNUM_TRADE_EXPORT MeshAttributeData {
* @param arraySize Array size. Use @cpp 0 @ce for non-array * @param arraySize Array size. Use @cpp 0 @ce for non-array
* attributes. * attributes.
* *
* Expects that @p data stride fits into a signed 16-bit value, * Expects that @p data stride fits into a signed 16-bit value, and for
* @p format corresponds to @p name and @p arraySize is zero for * builtin attributes that @p format corresponds to @p name,
* builtin attributes. The stride can be zero or negative, but note * @p arraySize is either zero for non-array attributes or non-zero for
* that such data layouts are not commonly supported by GPU APIs. * array attributes. The stride can be zero or negative, but note that
* such data layouts are not commonly supported by GPU APIs.
*/ */
explicit MeshAttributeData(MeshAttribute name, VertexFormat format, const Containers::StridedArrayView1D<const void>& data, UnsignedShort arraySize = 0) noexcept; explicit MeshAttributeData(MeshAttribute name, VertexFormat format, const Containers::StridedArrayView1D<const void>& data, UnsignedShort arraySize = 0) noexcept;
@ -422,8 +464,9 @@ class MAGNUM_TRADE_EXPORT MeshAttributeData {
* attributes. * attributes.
* *
* Expects that the second dimension of @p data is contiguous and its * Expects that the second dimension of @p data is contiguous and its
* size matches @p format and @p arraySize, that @p format corresponds * size matches @p format and @p arraySize, and for builtin attributes
* to @p name and @p arraySize is zero for builtin attributes. The * that @p format corresponds to @p name and @p arraySize is either
* zero for non-array attributes or non-zero for array attributes. The
* stride is expected to fit into a signed 16-bit value. It can be zero * stride is expected to fit into a signed 16-bit value. It can be zero
* or negative, but note that such data layouts are not commonly * or negative, but note that such data layouts are not commonly
* supported by GPU APIs. * supported by GPU APIs.
@ -483,9 +526,8 @@ class MAGNUM_TRADE_EXPORT MeshAttributeData {
* Detects @ref VertexFormat based on @p T and calls * Detects @ref VertexFormat based on @p T and calls
* @ref MeshAttributeData(MeshAttribute, VertexFormat, const Containers::StridedArrayView1D<const void>&, UnsignedShort) * @ref MeshAttributeData(MeshAttribute, VertexFormat, const Containers::StridedArrayView1D<const void>&, UnsignedShort)
* with the second dimension size passed to @p arraySize. Expects that * with the second dimension size passed to @p arraySize. Expects that
* the second dimension is contiguous. At the moment only custom * the second dimension is contiguous, and if @p name is a builtin
* attributes can be arrays, which means this function can't be used * attribute, it's an array attribute. See @ref MeshAttributeData(MeshAttribute, const Containers::StridedArrayView1D<T>&)
* with a builtin @p name. See @ref MeshAttributeData(MeshAttribute, const Containers::StridedArrayView1D<T>&)
* for details about @ref VertexFormat detection. * for details about @ref VertexFormat detection.
*/ */
template<class T> constexpr explicit MeshAttributeData(MeshAttribute name, const Containers::StridedArrayView2D<T>& data) noexcept; template<class T> constexpr explicit MeshAttributeData(MeshAttribute name, const Containers::StridedArrayView2D<T>& data) noexcept;
@ -506,10 +548,11 @@ class MAGNUM_TRADE_EXPORT MeshAttributeData {
* attribute construction time. Note that instances created this way * attribute construction time. Note that instances created this way
* can't be used in most @ref MeshTools algorithms. * can't be used in most @ref MeshTools algorithms.
* *
* Expects that @p arraySize is zero for builtin attributes and * For builtin attributes expects that @p arraySize is zero for
* @p stride fits into a signed 16-bit value. The stride can be zero or * non-array attributes and non-zero for array attributes. The
* negative, but note that such data layouts are not commonly supported * @p stride is expected to fit into a signed 16-bit value. It can be
* by GPU APIs. * zero or negative, but note that such data layouts are not commonly
* supported by GPU APIs.
* *
* Additionally, for even more flexibility, the @p vertexCount can be * Additionally, for even more flexibility, the @p vertexCount can be
* overridden at @ref MeshData construction time, however all attributes * overridden at @ref MeshData construction time, however all attributes
@ -686,13 +729,13 @@ The second simplest usage is accessing attributes through the convenience
functions @ref positions2DAsArray(), @ref positions3DAsArray(), functions @ref positions2DAsArray(), @ref positions3DAsArray(),
@ref tangentsAsArray(), @ref bitangentsAsArray(), @ref normalsAsArray(), @ref tangentsAsArray(), @ref bitangentsAsArray(), @ref normalsAsArray(),
@ref tangentsAsArray(), @ref textureCoordinates2DAsArray(), @ref tangentsAsArray(), @ref textureCoordinates2DAsArray(),
@ref colorsAsArray() and @ref objectIdsAsArray(). You're expected to check for @ref colorsAsArray(), @ref jointIdsAsArray(), @ref weightsAsArray() and
attribute presence first with either @ref hasAttribute() (or @ref objectIdsAsArray(). You're expected to check for attribute presence first
@ref attributeCount(MeshAttribute) const, as there can be multiple sets of with either @ref hasAttribute() (or @ref attributeCount(MeshAttribute) const,
texture coordinates, for example). If you are creating a @ref GL::Mesh, the as there can be multiple sets of texture coordinates, for example). If you are
usual path forward is then to @ref MeshTools::interleave() attributes of creating a @ref GL::Mesh, the usual path forward is then to
interest, upload them to a @ref GL::Buffer and configure attribute binding for @ref MeshTools::interleave() attributes of interest, upload them to a
the mesh. @ref GL::Buffer and configure attribute binding for the mesh.
The mesh can be also indexed, in which case the index buffer is exposed through The mesh can be also indexed, in which case the index buffer is exposed through
@ref indicesAsArray(). @ref indicesAsArray().
@ -848,7 +891,9 @@ class MAGNUM_TRADE_EXPORT MeshData {
* *
* The @p indices are expected to point to a sub-range of @p indexData. * The @p indices are expected to point to a sub-range of @p indexData.
* The @p attributes are expected to reference (sparse) sub-ranges of * The @p attributes are expected to reference (sparse) sub-ranges of
* @p vertexData. If the mesh has no attributes, the @p indices are * @p vertexData. Particular attributes can have additional
* restrictions, see documentation of @ref MeshAttribute values for
* more information. If the mesh has no attributes, the @p indices are
* expected to be valid (but can be empty). If you want to create an * expected to be valid (but can be empty). If you want to create an
* attribute-less non-indexed mesh, use * attribute-less non-indexed mesh, use
* @ref MeshData(MeshPrimitive, UnsignedInt, const void*) to specify * @ref MeshData(MeshPrimitive, UnsignedInt, const void*) to specify
@ -1378,11 +1423,9 @@ class MAGNUM_TRADE_EXPORT MeshData {
* *
* In case given attribute is an array (the equivalent of e.g. * In case given attribute is an array (the equivalent of e.g.
* @cpp int[30] @ce), returns array size, otherwise returns @cpp 0 @ce. * @cpp int[30] @ce), returns array size, otherwise returns @cpp 0 @ce.
* At the moment only custom attributes can be arrays, no builtin * The @p id is expected to be smaller than @ref attributeCount() const.
* @ref MeshAttribute is an array attribute. The @p id is expected to * You can also use @ref attributeArraySize(MeshAttribute, UnsignedInt) const
* be smaller than @ref attributeCount() const. You can also use * to directly get array size of given named attribute.
* @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 * Note that this is different from vertex count, which is exposed
* through @ref vertexCount(), and is an orthogonal concept to having * through @ref vertexCount(), and is an orthogonal concept to having
@ -1520,7 +1563,8 @@ class MAGNUM_TRADE_EXPORT MeshData {
* @ref positions2DAsArray(), @ref positions3DAsArray(), * @ref positions2DAsArray(), @ref positions3DAsArray(),
* @ref tangentsAsArray(), @ref bitangentSignsAsArray(), * @ref tangentsAsArray(), @ref bitangentSignsAsArray(),
* @ref bitangentsAsArray(), @ref normalsAsArray(), * @ref bitangentsAsArray(), @ref normalsAsArray(),
* @ref textureCoordinates2DAsArray(), @ref colorsAsArray() and * @ref textureCoordinates2DAsArray(), @ref colorsAsArray(),
* @ref jointIdsAsArray(), @ref weightsAsArray() and
* @ref objectIdsAsArray() accessors to get common attributes converted * @ref objectIdsAsArray() accessors to get common attributes converted
* to usual types in contiguous arrays, but note that these operations * to usual types in contiguous arrays, but note that these operations
* involve extra allocation and data conversion. * involve extra allocation and data conversion.
@ -1602,7 +1646,8 @@ class MAGNUM_TRADE_EXPORT MeshData {
* @ref positions2DAsArray(), @ref positions3DAsArray(), * @ref positions2DAsArray(), @ref positions3DAsArray(),
* @ref tangentsAsArray(), @ref bitangentSignsAsArray(), * @ref tangentsAsArray(), @ref bitangentSignsAsArray(),
* @ref bitangentsAsArray(), @ref normalsAsArray(), * @ref bitangentsAsArray(), @ref normalsAsArray(),
* @ref textureCoordinates2DAsArray(), @ref colorsAsArray() and * @ref textureCoordinates2DAsArray(), @ref colorsAsArray(),
* @ref jointIdsAsArray(), @ref weightsAsArray() and
* @ref objectIdsAsArray() accessors to get common attributes converted * @ref objectIdsAsArray() accessors to get common attributes converted
* to usual types in contiguous arrays, but note that these operations * to usual types in contiguous arrays, but note that these operations
* involve extra data conversion and an allocation. * involve extra data conversion and an allocation.
@ -1873,6 +1918,84 @@ class MAGNUM_TRADE_EXPORT MeshData {
*/ */
void colorsInto(const Containers::StridedArrayView1D<Color4>& destination, UnsignedInt id = 0) const; void colorsInto(const Containers::StridedArrayView1D<Color4>& destination, UnsignedInt id = 0) const;
/**
* @brief Skin joint IDs as unsigned int arrays
* @m_since_latest
*
* Convenience alternative to @ref attribute(MeshAttribute, UnsignedInt) const
* with @ref MeshAttribute::JointIds as the first argument. Converts
* the joint IDs array from an arbitrary underlying type and returns it
* in a newly-allocated array. Expects that the vertex format is *not*
* implementation-specific, in that case you can only access the
* attribute via the typeless @ref attribute(MeshAttribute, UnsignedInt) const.
*
* As it's an array attribute, the returned array has @ref vertexCount()
* times @ref attributeArraySize(MeshAttribute, UnsignedInt) const
* elements. You can make a 2D view onto the result to conveniently
* index the data:
*
* @snippet MagnumTrade.cpp MeshData-jointIdsAsArray
*
* @see @ref weightsAsArray(), @ref jointIdsInto(),
* @ref attributeFormat(),
* @ref isVertexFormatImplementationSpecific()
*/
/* Originally I tried with jointIdsWeightsAsArray() because these two
attributes are usually needed together, and they are also checked to
have matching counts and array sizes on construction. However, it'd
mean the two arrays would have to be interleaved for each vertex
(Array<Pair<UnsignedInt, Float>>) which makes them no longer
directly usable for mesh attributes, and there are other hurdles
such as unpackInto() / castInto() expecting the second dimension to
be contiguous. */
Containers::Array<UnsignedInt> jointIdsAsArray(UnsignedInt id = 0) const;
/**
* @brief Skin joint IDs as unsigned int arrays into a pre-allocated view
* @m_since_latest
*
* Like @ref jointIdsAsArray(), but puts the result into @p destination
* instead of allocating a new array. Expects that @p destination is
* sized to contain exactly all data --- first dimension being
* @ref vertexCount() and second
* @ref attributeArraySize(MeshAttribute. UnsignedInt) const. The
* second dimension is additionally expected to be contiguous.
*/
void jointIdsInto(const Containers::StridedArrayView2D<UnsignedInt>& destination, UnsignedInt id = 0) const;
/**
* @brief Skin weights as float arrays
* @m_since_latest
*
* Convenience alternative to @ref attribute(MeshAttribute, UnsignedInt) const
* with @ref MeshAttribute::Weights as the first argument. Converts the
* weights array from an arbitrary underlying types and returns them in
* a newly-allocated array. Expects that the vertex format is *not*
* implementation-specific, in that case you can only access the
* attribute via the typeless @ref attribute(MeshAttribute, UnsignedInt) const.
*
* As it's an array attribute, the returned array has @ref vertexCount()
* times @ref attributeArraySize(MeshAttribute, UnsignedInt) const
* elements. You can make a 2D view onto the result to conveniently
* index the data, see @ref jointIdsAsArray() for an example snippet.
* @see @ref weightsInto(), @ref attributeFormat(),
* @ref isVertexFormatImplementationSpecific()
*/
Containers::Array<Float> weightsAsArray(UnsignedInt id = 0) const;
/**
* @brief Skin weights as float arrays into a pre-allocated view
* @m_since_latest
*
* Like @ref weightsAsArray(), but puts the result into @p destination
* instead of allocating a new array. Expects that @p destination is
* sized to contain exactly all data --- first dimension being
* @ref vertexCount() and second
* @ref attributeArraySize(MeshAttribute. UnsignedInt) const. The
* second dimension is additionally expected to be contiguous.
*/
void weightsInto(const Containers::StridedArrayView2D<Float>& weightsDestination, UnsignedInt id = 0) const;
/** /**
* @brief Object IDs as 32-bit integers * @brief Object IDs as 32-bit integers
* *
@ -2265,6 +2388,15 @@ namespace Implementation {
format == VertexFormat::Vector2usNormalized || format == VertexFormat::Vector2usNormalized ||
format == VertexFormat::Vector2s || format == VertexFormat::Vector2s ||
format == VertexFormat::Vector2sNormalized)) || format == VertexFormat::Vector2sNormalized)) ||
(name == MeshAttribute::JointIds &&
(format == VertexFormat::UnsignedInt ||
format == VertexFormat::UnsignedByte ||
format == VertexFormat::UnsignedShort)) ||
(name == MeshAttribute::Weights &&
(format == VertexFormat::Float ||
format == VertexFormat::Half ||
format == VertexFormat::UnsignedByteNormalized ||
format == VertexFormat::UnsignedShortNormalized)) ||
(name == MeshAttribute::ObjectId && (name == MeshAttribute::ObjectId &&
(format == VertexFormat::UnsignedInt || (format == VertexFormat::UnsignedInt ||
format == VertexFormat::UnsignedShort || format == VertexFormat::UnsignedShort ||
@ -2274,7 +2406,16 @@ namespace Implementation {
} }
constexpr bool isAttributeArrayAllowed(MeshAttribute name) { constexpr bool isAttributeArrayAllowed(MeshAttribute name) {
return isMeshAttributeCustom(name); return
name == MeshAttribute::JointIds ||
name == MeshAttribute::Weights ||
/* Custom attributes can be anything */
isMeshAttributeCustom(name);
}
constexpr bool isAttributeArrayExpected(MeshAttribute name) {
/* Custom attributes don't *have to* be arrays */
return name == MeshAttribute::JointIds ||
name == MeshAttribute::Weights;
} }
#endif #endif
} }
@ -2288,7 +2429,9 @@ constexpr MeshAttributeData::MeshAttributeData(std::nullptr_t, const MeshAttribu
"Trade::MeshAttributeData: expected stride to fit into 16 bits but got" << data.stride()), "Trade::MeshAttributeData: expected stride to fit into 16 bits but got" << data.stride()),
Short(data.stride()))}, Short(data.stride()))},
_arraySize{(CORRADE_CONSTEXPR_ASSERT(!arraySize || Implementation::isAttributeArrayAllowed(name), _arraySize{(CORRADE_CONSTEXPR_ASSERT(!arraySize || Implementation::isAttributeArrayAllowed(name),
"Trade::MeshAttributeData:" << name << "can't be an array attribute"), arraySize)}, "Trade::MeshAttributeData:" << name << "can't be an array attribute"),
CORRADE_CONSTEXPR_ASSERT(arraySize || !Implementation::isAttributeArrayExpected(name), "Trade::MeshAttributeData:" << name << "has to be an array attribute"),
arraySize)},
_data{data.data()} {} _data{data.data()} {}
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: 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:

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

@ -76,6 +76,7 @@ struct MeshDataTest: TestSuite::Tester {
void constructAttributeWrongFormat(); void constructAttributeWrongFormat();
void constructAttributeWrongStride(); void constructAttributeWrongStride();
void constructAttributeWrongDataAccess(); void constructAttributeWrongDataAccess();
void constructAttributeOnlyArrayAllowed();
void constructArrayAttribute(); void constructArrayAttribute();
void constructArrayAttributeNonContiguous(); void constructArrayAttributeNonContiguous();
@ -117,6 +118,8 @@ struct MeshDataTest: TestSuite::Tester {
void constructIndicesNotContained(); void constructIndicesNotContained();
void constructAttributeNotContained(); void constructAttributeNotContained();
void constructInconsitentVertexCount(); void constructInconsitentVertexCount();
void constructDifferentJointIdWeightCount();
void constructInconsistentJointIdWeightArraySizes();
void constructNotOwnedIndexFlagOwned(); void constructNotOwnedIndexFlagOwned();
void constructNotOwnedVertexFlagOwned(); void constructNotOwnedVertexFlagOwned();
void constructIndicesNotOwnedFlagOwned(); void constructIndicesNotOwnedFlagOwned();
@ -164,6 +167,11 @@ struct MeshDataTest: TestSuite::Tester {
template<class T> void colorsAsArray(); template<class T> void colorsAsArray();
template<class T> void colorsAsArrayPackedUnsignedNormalized(); template<class T> void colorsAsArrayPackedUnsignedNormalized();
void colorsIntoArrayInvalidSize(); void colorsIntoArrayInvalidSize();
template<class T> void jointIdsAsArray();
void jointIdsIntoArrayInvalidSizeStride();
template<class T> void weightsAsArray();
template<class T> void weightsAsArrayPackedUnsignedNormalized();
void weightsIntoArrayInvalidSizeStride();
template<class T> void objectIdsAsArray(); template<class T> void objectIdsAsArray();
void objectIdsIntoArrayInvalidSize(); void objectIdsIntoArrayInvalidSize();
@ -250,6 +258,7 @@ MeshDataTest::MeshDataTest() {
&MeshDataTest::constructAttributeWrongFormat, &MeshDataTest::constructAttributeWrongFormat,
&MeshDataTest::constructAttributeWrongStride, &MeshDataTest::constructAttributeWrongStride,
&MeshDataTest::constructAttributeWrongDataAccess, &MeshDataTest::constructAttributeWrongDataAccess,
&MeshDataTest::constructAttributeOnlyArrayAllowed,
&MeshDataTest::constructArrayAttribute, &MeshDataTest::constructArrayAttribute,
&MeshDataTest::constructArrayAttributeNonContiguous, &MeshDataTest::constructArrayAttributeNonContiguous,
@ -294,6 +303,8 @@ MeshDataTest::MeshDataTest() {
&MeshDataTest::constructIndicesNotContained, &MeshDataTest::constructIndicesNotContained,
&MeshDataTest::constructAttributeNotContained, &MeshDataTest::constructAttributeNotContained,
&MeshDataTest::constructInconsitentVertexCount, &MeshDataTest::constructInconsitentVertexCount,
&MeshDataTest::constructDifferentJointIdWeightCount,
&MeshDataTest::constructInconsistentJointIdWeightArraySizes,
&MeshDataTest::constructNotOwnedIndexFlagOwned, &MeshDataTest::constructNotOwnedIndexFlagOwned,
&MeshDataTest::constructNotOwnedVertexFlagOwned, &MeshDataTest::constructNotOwnedVertexFlagOwned,
&MeshDataTest::constructIndicesNotOwnedFlagOwned, &MeshDataTest::constructIndicesNotOwnedFlagOwned,
@ -396,6 +407,15 @@ MeshDataTest::MeshDataTest() {
&MeshDataTest::colorsAsArrayPackedUnsignedNormalized<Color4ub>, &MeshDataTest::colorsAsArrayPackedUnsignedNormalized<Color4ub>,
&MeshDataTest::colorsAsArrayPackedUnsignedNormalized<Color4us>, &MeshDataTest::colorsAsArrayPackedUnsignedNormalized<Color4us>,
&MeshDataTest::colorsIntoArrayInvalidSize, &MeshDataTest::colorsIntoArrayInvalidSize,
&MeshDataTest::jointIdsAsArray<UnsignedInt>,
&MeshDataTest::jointIdsAsArray<UnsignedByte>,
&MeshDataTest::jointIdsAsArray<UnsignedShort>,
&MeshDataTest::jointIdsIntoArrayInvalidSizeStride,
&MeshDataTest::weightsAsArray<Float>,
&MeshDataTest::weightsAsArray<Half>,
&MeshDataTest::weightsAsArrayPackedUnsignedNormalized<UnsignedByte>,
&MeshDataTest::weightsAsArrayPackedUnsignedNormalized<UnsignedShort>,
&MeshDataTest::weightsIntoArrayInvalidSizeStride,
&MeshDataTest::objectIdsAsArray<UnsignedByte>, &MeshDataTest::objectIdsAsArray<UnsignedByte>,
&MeshDataTest::objectIdsAsArray<UnsignedShort>, &MeshDataTest::objectIdsAsArray<UnsignedShort>,
&MeshDataTest::objectIdsAsArray<UnsignedInt>, &MeshDataTest::objectIdsAsArray<UnsignedInt>,
@ -991,6 +1011,23 @@ void MeshDataTest::constructAttributeWrongStride() {
"Trade::MeshAttributeData: expected padding to fit into 16 bits but got -32769\n"); "Trade::MeshAttributeData: expected padding to fit into 16 bits but got -32769\n");
} }
void MeshDataTest::constructAttributeOnlyArrayAllowed() {
CORRADE_SKIP_IF_NO_ASSERT();
Vector2 data[3];
/* These should be fine */
MeshAttributeData{MeshAttribute::Weights, VertexFormat::Float, data, 2};
MeshAttributeData{meshAttributeCustom(25), VertexFormat::Vector2, data};
MeshAttributeData{meshAttributeCustom(25), VertexFormat::Float, data, 2};
std::ostringstream out;
Error redirectError{&out};
MeshAttributeData{MeshAttribute::Weights, VertexFormat::Float, data};
CORRADE_COMPARE(out.str(),
"Trade::MeshAttributeData: Trade::MeshAttribute::Weights has to be an array attribute\n");
}
void MeshDataTest::constructAttributeWrongDataAccess() { void MeshDataTest::constructAttributeWrongDataAccess() {
CORRADE_SKIP_IF_NO_ASSERT(); CORRADE_SKIP_IF_NO_ASSERT();
@ -2395,6 +2432,59 @@ void MeshDataTest::constructInconsitentVertexCount() {
"Trade::MeshData: attribute 1 has 2 vertices but 3 expected\n"); "Trade::MeshData: attribute 1 has 2 vertices but 3 expected\n");
} }
void MeshDataTest::constructDifferentJointIdWeightCount() {
CORRADE_SKIP_IF_NO_ASSERT();
struct Vertex {
/* Weights required to be here by the constructor */
Float weights[2];
UnsignedByte jointIds[2];
UnsignedShort secondaryJointIds[4];
} vertices[3]{};
auto view = Containers::stridedArrayView(vertices);
std::ostringstream out;
Error redirectError{&out};
MeshData{MeshPrimitive::Points, {}, vertices, {
/* Weights required to be here by the constructor */
MeshAttributeData{MeshAttribute::Weights, VertexFormat::Float,
view.slice(&Vertex::weights), 2},
MeshAttributeData{MeshAttribute::JointIds, VertexFormat::UnsignedByte,
view.slice(&Vertex::jointIds), 2},
MeshAttributeData{MeshAttribute::JointIds, VertexFormat::UnsignedShort,
view.slice(&Vertex::secondaryJointIds), 4}
}};
CORRADE_COMPARE(out.str(), "Trade::MeshData: expected 2 weight attributes to match joint IDs but got 1\n");
}
void MeshDataTest::constructInconsistentJointIdWeightArraySizes() {
CORRADE_SKIP_IF_NO_ASSERT();
struct Vertex {
/* Weights required to be here by the constructor */
Float weights[2];
UnsignedByte jointIds[2];
Half secondaryWeights[3];
UnsignedShort secondaryJointIds[4];
} vertices[3]{};
auto view = Containers::stridedArrayView(vertices);
std::ostringstream out;
Error redirectError{&out};
MeshData{MeshPrimitive::Points, {}, vertices, {
/* Weights required to be here by the constructor */
MeshAttributeData{MeshAttribute::Weights, VertexFormat::Float,
view.slice(&Vertex::weights), 2},
MeshAttributeData{MeshAttribute::JointIds, VertexFormat::UnsignedByte,
view.slice(&Vertex::jointIds), 2},
MeshAttributeData{MeshAttribute::Weights, VertexFormat::Half,
view.slice(&Vertex::secondaryWeights), 3},
MeshAttributeData{MeshAttribute::JointIds, VertexFormat::UnsignedShort,
view.slice(&Vertex::secondaryJointIds), 4}
}};
CORRADE_COMPARE(out.str(), "Trade::MeshData: expected 4 array items for weight attribute 1 to match joint IDs but got 3\n");
}
void MeshDataTest::constructNotOwnedIndexFlagOwned() { void MeshDataTest::constructNotOwnedIndexFlagOwned() {
CORRADE_SKIP_IF_NO_ASSERT(); CORRADE_SKIP_IF_NO_ASSERT();
@ -2586,6 +2676,7 @@ template<class> struct NameTraits;
#define _c(format) template<> struct NameTraits<format> { \ #define _c(format) template<> struct NameTraits<format> { \
static const char* name() { return #format; } \ static const char* name() { return #format; } \
}; };
/* Scalars are in Math::TypeTraits already */
_c(Vector2) _c(Vector2)
_c(Vector2h) _c(Vector2h)
_c(Vector2ub) _c(Vector2ub)
@ -3322,6 +3413,192 @@ void MeshDataTest::colorsIntoArrayInvalidSize() {
"Trade::MeshData::colorsInto(): expected a view with 3 elements but got 2\n"); "Trade::MeshData::colorsInto(): expected a view with 3 elements but got 2\n");
} }
template<class T> void MeshDataTest::jointIdsAsArray() {
setTestCaseTemplateName(Math::TypeTraits<T>::name());
/* Testing also that it picks the correct attribute. Needs to be
sufficiently representable to have the test work also for half
floats. */
struct Vertex {
/* Weights required to be here by the constructor */
Float otherWeights[3];
Float weights[5];
UnsignedInt otherJointIds[3];
UnsignedShort objectId;
T jointIds[5];
} vertices[]{
{{}, {}, {},
0,
{T(0), T(3), T(20), T(1), T(7)}},
{{}, {}, {},
0,
{T(9), T(1), T(15), T(2), T(3)}},
{{}, {}, {},
0,
{T(25), T(7), T(0), T(2), T(1)}},
};
auto view = Containers::stridedArrayView(vertices);
MeshData data{MeshPrimitive::Points, {}, vertices, {
/* Weights required to be here by the constructor */
MeshAttributeData{MeshAttribute::Weights, VertexFormat::Float,
view.slice(&Vertex::otherWeights), 3},
MeshAttributeData{MeshAttribute::Weights, VertexFormat::Float,
view.slice(&Vertex::weights), 5},
MeshAttributeData{MeshAttribute::JointIds,
Implementation::vertexFormatFor<T>(),
view.slice(&Vertex::otherJointIds), 3},
MeshAttributeData{MeshAttribute::ObjectId, view.slice(&Vertex::objectId)},
MeshAttributeData{MeshAttribute::JointIds,
Implementation::vertexFormatFor<T>(),
view.slice(&Vertex::jointIds), 5},
}};
CORRADE_COMPARE_AS(data.jointIdsAsArray(1), (Containers::arrayView<UnsignedInt>({
0, 3, 20, 1, 7,
9, 1, 15, 2, 3,
25, 7, 0, 2, 1
})), TestSuite::Compare::Container);
}
void MeshDataTest::jointIdsIntoArrayInvalidSizeStride() {
CORRADE_SKIP_IF_NO_ASSERT();
struct Vertex {
/* Weights required to be here by the constructor */
Float weights[2];
UnsignedByte jointIds[2];
} vertices[3]{};
auto view = Containers::stridedArrayView(vertices);
MeshData data{MeshPrimitive::Points, {}, vertices, {
/* Weights required to be here by the constructor */
MeshAttributeData{MeshAttribute::Weights, VertexFormat::Float,
view.slice(&Vertex::weights), 2},
MeshAttributeData{MeshAttribute::JointIds, VertexFormat::UnsignedByte,
view.slice(&Vertex::jointIds), 2}
}};
std::ostringstream out;
Error redirectError{&out};
UnsignedInt jointIds1[3*3];
UnsignedInt jointIds2[2*2];
UnsignedInt jointIds3[3*4];
data.jointIdsInto(Containers::StridedArrayView2D<UnsignedInt>{jointIds1, {3, 3}});
data.jointIdsInto(Containers::StridedArrayView2D<UnsignedInt>{jointIds2, {2, 2}});
data.jointIdsInto(Containers::StridedArrayView2D<UnsignedInt>{jointIds3, {3, 4}}.every({1, 2}));
CORRADE_COMPARE(out.str(),
"Trade::MeshData::jointIdsInto(): expected a view with {3, 2} elements but got {3, 3}\n"
"Trade::MeshData::jointIdsInto(): expected a view with {3, 2} elements but got {2, 2}\n"
"Trade::MeshData::jointIdsInto(): second view dimension is not contiguous\n");
}
template<class T> void MeshDataTest::weightsAsArray() {
setTestCaseTemplateName(Math::TypeTraits<T>::name());
/* Testing also that it picks the correct attribute. Needs to be
sufficiently representable to have the test work also for half
floats. */
struct Vertex {
/* Joint IDs required to be here by the constructor */
UnsignedInt otherJointIds[3];
UnsignedInt jointIds[5];
Float otherWeights[3];
UnsignedShort objectId;
T weights[5];
} vertices[]{
{{}, {}, {},
0,
{T(2.0f), T(1.0f), T(0.75f), T(3.0f), T(1.75f)}},
{{}, {}, {},
0,
{T(0.0f), T(-1.0f), T(1.25f), T(1.0f), T(2.25f)}},
{{}, {}, {},
0,
{T(-2.0f), T(3.0f), T(2.5f), T(2.5f), T(0.25f)}},
};
auto view = Containers::stridedArrayView(vertices);
MeshData data{MeshPrimitive::Points, {}, vertices, {
/* Joint IDs required to be here by the constructor */
MeshAttributeData{MeshAttribute::JointIds, VertexFormat::UnsignedInt,
view.slice(&Vertex::otherJointIds), 3},
MeshAttributeData{MeshAttribute::JointIds, VertexFormat::UnsignedInt,
view.slice(&Vertex::jointIds), 5},
MeshAttributeData{MeshAttribute::Weights,
Implementation::vertexFormatFor<T>(),
view.slice(&Vertex::otherWeights), 3},
MeshAttributeData{MeshAttribute::ObjectId, view.slice(&Vertex::objectId)},
MeshAttributeData{MeshAttribute::Weights,
Implementation::vertexFormatFor<T>(),
view.slice(&Vertex::weights), 5}
}};
CORRADE_COMPARE_AS(data.weightsAsArray(1), (Containers::arrayView<Float>({
2.0f, 1.0f, 0.75f, 3.0f, 1.75f,
0.0f, -1.0f, 1.25f, 1.0f, 2.25f,
-2.0f, 3.0f, 2.5f, 2.5f, 0.25f,
})), TestSuite::Compare::Container);
}
template<class T> void MeshDataTest::weightsAsArrayPackedUnsignedNormalized() {
setTestCaseTemplateName(Math::TypeTraits<T>::name());
struct Vertex {
/* Joint IDs required to be here by the constructor */
UnsignedByte jointIds[2];
T weights[2];
} vertices[2]{
{{}, {Math::pack<T>(1.0f), Math::pack<T>(0.8f)}},
{{}, {0, Math::pack<T>(0.4f)}}
};
auto view = Containers::stridedArrayView(vertices);
MeshData data{MeshPrimitive::Points, {}, vertices, {
/* Joint IDs required to be here by the constructor */
MeshAttributeData{MeshAttribute::JointIds, VertexFormat::UnsignedByte,
view.slice(&Vertex::jointIds), 2},
MeshAttributeData{MeshAttribute::Weights,
vertexFormat(Implementation::vertexFormatFor<T>(), 1, true),
view.slice(&Vertex::weights), 2}
}};
CORRADE_COMPARE_AS(data.weightsAsArray(), (Containers::arrayView<Float>({
1.0f, 0.8f,
0.0f, 0.4f
})), TestSuite::Compare::Container);
}
void MeshDataTest::weightsIntoArrayInvalidSizeStride() {
CORRADE_SKIP_IF_NO_ASSERT();
struct Vertex {
/* Joint IDs required to be here by the constructor */
UnsignedInt jointIds[2];
Half weights[2];
} vertices[3]{};
auto view = Containers::stridedArrayView(vertices);
MeshData data{MeshPrimitive::Points, {}, vertices, {
/* Joint IDs required to be here by the constructor */
MeshAttributeData{MeshAttribute::JointIds, VertexFormat::UnsignedInt,
view.slice(&Vertex::jointIds), 2},
MeshAttributeData{MeshAttribute::Weights, VertexFormat::Half,
view.slice(&Vertex::weights), 2}
}};
std::ostringstream out;
Error redirectError{&out};
Float weights1[3*3];
Float weights2[2*2];
Float weights3[3*4];
data.weightsInto(Containers::StridedArrayView2D<Float>{weights1, {3, 3}});
data.weightsInto(Containers::StridedArrayView2D<Float>{weights2, {2, 2}});
data.weightsInto(Containers::StridedArrayView2D<Float>{weights3, {3, 4}}.every({1, 2}));
CORRADE_COMPARE(out.str(),
"Trade::MeshData::weightsInto(): expected a view with {3, 2} elements but got {3, 3}\n"
"Trade::MeshData::weightsInto(): expected a view with {3, 2} elements but got {2, 2}\n"
"Trade::MeshData::weightsInto(): second view dimension is not contiguous\n");
}
template<class T> void MeshDataTest::objectIdsAsArray() { template<class T> void MeshDataTest::objectIdsAsArray() {
setTestCaseTemplateName(Math::TypeTraits<T>::name()); setTestCaseTemplateName(Math::TypeTraits<T>::name());
@ -3404,8 +3681,12 @@ void MeshDataTest::implementationSpecificVertexFormatWrongAccess() {
vertexFormatWrap(0xdead5), attribute}, vertexFormatWrap(0xdead5), attribute},
MeshAttributeData{MeshAttribute::Color, MeshAttributeData{MeshAttribute::Color,
vertexFormatWrap(0xdead6), attribute}, vertexFormatWrap(0xdead6), attribute},
MeshAttributeData{MeshAttribute::JointIds,
vertexFormatWrap(0xdead7), attribute, 2},
MeshAttributeData{MeshAttribute::Weights,
vertexFormatWrap(0xdead8), attribute, 2},
MeshAttributeData{MeshAttribute::ObjectId, MeshAttributeData{MeshAttribute::ObjectId,
vertexFormatWrap(0xdead7), attribute}}}; vertexFormatWrap(0xdead9), attribute}}};
std::ostringstream out; std::ostringstream out;
Error redirectError{&out}; Error redirectError{&out};
@ -3425,6 +3706,8 @@ void MeshDataTest::implementationSpecificVertexFormatWrongAccess() {
data.normalsAsArray(); data.normalsAsArray();
data.textureCoordinates2DAsArray(); data.textureCoordinates2DAsArray();
data.colorsAsArray(); data.colorsAsArray();
data.jointIdsAsArray();
data.weightsAsArray();
data.objectIdsAsArray(); data.objectIdsAsArray();
CORRADE_COMPARE(out.str(), CORRADE_COMPARE(out.str(),
"Trade::MeshData::attribute(): can't cast data from an implementation-specific vertex format 0xdead1\n" "Trade::MeshData::attribute(): can't cast data from an implementation-specific vertex format 0xdead1\n"
@ -3443,7 +3726,9 @@ void MeshDataTest::implementationSpecificVertexFormatWrongAccess() {
"Trade::MeshData::normalsInto(): can't extract data out of an implementation-specific vertex format 0xdead4\n" "Trade::MeshData::normalsInto(): can't extract data out of an implementation-specific vertex format 0xdead4\n"
"Trade::MeshData::textureCoordinatesInto(): can't extract data out of an implementation-specific vertex format 0xdead5\n" "Trade::MeshData::textureCoordinatesInto(): can't extract data out of an implementation-specific vertex format 0xdead5\n"
"Trade::MeshData::colorsInto(): can't extract data out of an implementation-specific vertex format 0xdead6\n" "Trade::MeshData::colorsInto(): can't extract data out of an implementation-specific vertex format 0xdead6\n"
"Trade::MeshData::objectIdsInto(): can't extract data out of an implementation-specific vertex format 0xdead7\n"); "Trade::MeshData::jointIdsInto(): can't extract data out of an implementation-specific vertex format 0xdead7\n"
"Trade::MeshData::weightsInto(): can't extract data out of an implementation-specific vertex format 0xdead8\n"
"Trade::MeshData::objectIdsInto(): can't extract data out of an implementation-specific vertex format 0xdead9\n");
} }
void MeshDataTest::mutableAccessNotAllowed() { void MeshDataTest::mutableAccessNotAllowed() {
@ -3530,9 +3815,14 @@ void MeshDataTest::indicesWrongType() {
void MeshDataTest::attributeNotFound() { void MeshDataTest::attributeNotFound() {
CORRADE_SKIP_IF_NO_ASSERT(); CORRADE_SKIP_IF_NO_ASSERT();
MeshAttributeData colors1{MeshAttribute::Color, VertexFormat::Vector3, nullptr}; MeshData data{MeshPrimitive::Points, nullptr, {
MeshAttributeData colors2{MeshAttribute::Color, VertexFormat::Vector4, nullptr}; MeshAttributeData{MeshAttribute::Color, VertexFormat::Vector3, nullptr},
MeshData data{MeshPrimitive::Points, nullptr, {colors1, colors2}}; MeshAttributeData{MeshAttribute::Weights, VertexFormat::UnsignedByteNormalized, nullptr, 3},
MeshAttributeData{MeshAttribute::JointIds, VertexFormat::UnsignedByte, nullptr, 3},
MeshAttributeData{MeshAttribute::Color, VertexFormat::Vector4, nullptr},
MeshAttributeData{MeshAttribute::Weights, VertexFormat::Float, nullptr, 6},
MeshAttributeData{MeshAttribute::JointIds, VertexFormat::UnsignedShort, nullptr, 6}
}};
/* This is fine */ /* This is fine */
CORRADE_COMPARE(data.attributeCount(MeshAttribute::Position), 0); CORRADE_COMPARE(data.attributeCount(MeshAttribute::Position), 0);
@ -3541,19 +3831,19 @@ void MeshDataTest::attributeNotFound() {
std::ostringstream out; std::ostringstream out;
Error redirectError{&out}; Error redirectError{&out};
data.attributeData(2); data.attributeData(6);
data.attributeName(2); data.attributeName(6);
data.attributeId(2); data.attributeId(6);
data.attributeFormat(2); data.attributeFormat(6);
data.attributeOffset(2); data.attributeOffset(6);
data.attributeStride(2); data.attributeStride(6);
data.attributeArraySize(2); data.attributeArraySize(6);
data.attribute(2); data.attribute(6);
data.attribute<Vector2>(2); data.attribute<Vector2>(6);
data.attribute<Vector2[]>(2); data.attribute<Vector2[]>(6);
data.mutableAttribute(2); data.mutableAttribute(6);
data.mutableAttribute<Vector2>(2); data.mutableAttribute<Vector2>(6);
data.mutableAttribute<Vector2[]>(2); data.mutableAttribute<Vector2[]>(6);
data.attributeId(MeshAttribute::Position); data.attributeId(MeshAttribute::Position);
data.attributeId(MeshAttribute::Color, 2); data.attributeId(MeshAttribute::Color, 2);
@ -3586,21 +3876,27 @@ void MeshDataTest::attributeNotFound() {
data.normalsAsArray(); data.normalsAsArray();
data.textureCoordinates2DAsArray(); data.textureCoordinates2DAsArray();
data.colorsAsArray(2); data.colorsAsArray(2);
/* jointIdsAsArray() and weightsAsArray() have their own assert in order to
fetch array size, have to test also Into() for these */
data.jointIdsAsArray(2);
data.jointIdsInto(nullptr, 2);
data.weightsAsArray(2);
data.weightsInto(nullptr, 2);
data.objectIdsAsArray(); data.objectIdsAsArray();
CORRADE_COMPARE(out.str(), CORRADE_COMPARE(out.str(),
"Trade::MeshData::attributeData(): index 2 out of range for 2 attributes\n" "Trade::MeshData::attributeData(): index 6 out of range for 6 attributes\n"
"Trade::MeshData::attributeName(): index 2 out of range for 2 attributes\n" "Trade::MeshData::attributeName(): index 6 out of range for 6 attributes\n"
"Trade::MeshData::attributeId(): index 2 out of range for 2 attributes\n" "Trade::MeshData::attributeId(): index 6 out of range for 6 attributes\n"
"Trade::MeshData::attributeFormat(): index 2 out of range for 2 attributes\n" "Trade::MeshData::attributeFormat(): index 6 out of range for 6 attributes\n"
"Trade::MeshData::attributeOffset(): index 2 out of range for 2 attributes\n" "Trade::MeshData::attributeOffset(): index 6 out of range for 6 attributes\n"
"Trade::MeshData::attributeStride(): index 2 out of range for 2 attributes\n" "Trade::MeshData::attributeStride(): index 6 out of range for 6 attributes\n"
"Trade::MeshData::attributeArraySize(): index 2 out of range for 2 attributes\n" "Trade::MeshData::attributeArraySize(): index 6 out of range for 6 attributes\n"
"Trade::MeshData::attribute(): index 2 out of range for 2 attributes\n" "Trade::MeshData::attribute(): index 6 out of range for 6 attributes\n"
"Trade::MeshData::attribute(): index 2 out of range for 2 attributes\n" "Trade::MeshData::attribute(): index 6 out of range for 6 attributes\n"
"Trade::MeshData::attribute(): index 2 out of range for 2 attributes\n" "Trade::MeshData::attribute(): index 6 out of range for 6 attributes\n"
"Trade::MeshData::mutableAttribute(): index 2 out of range for 2 attributes\n" "Trade::MeshData::mutableAttribute(): index 6 out of range for 6 attributes\n"
"Trade::MeshData::mutableAttribute(): index 2 out of range for 2 attributes\n" "Trade::MeshData::mutableAttribute(): index 6 out of range for 6 attributes\n"
"Trade::MeshData::mutableAttribute(): index 2 out of range for 2 attributes\n" "Trade::MeshData::mutableAttribute(): index 6 out of range for 6 attributes\n"
"Trade::MeshData::attributeId(): index 0 out of range for 0 Trade::MeshAttribute::Position attributes\n" "Trade::MeshData::attributeId(): index 0 out of range for 0 Trade::MeshAttribute::Position attributes\n"
"Trade::MeshData::attributeId(): index 2 out of range for 2 Trade::MeshAttribute::Color attributes\n" "Trade::MeshData::attributeId(): index 2 out of range for 2 Trade::MeshAttribute::Color attributes\n"
@ -3633,6 +3929,10 @@ void MeshDataTest::attributeNotFound() {
"Trade::MeshData::normalsInto(): index 0 out of range for 0 normal attributes\n" "Trade::MeshData::normalsInto(): index 0 out of range for 0 normal attributes\n"
"Trade::MeshData::textureCoordinates2DInto(): index 0 out of range for 0 texture coordinate attributes\n" "Trade::MeshData::textureCoordinates2DInto(): index 0 out of range for 0 texture coordinate attributes\n"
"Trade::MeshData::colorsInto(): index 2 out of range for 2 color attributes\n" "Trade::MeshData::colorsInto(): index 2 out of range for 2 color attributes\n"
"Trade::MeshData::jointIdsAsArray(): index 2 out of range for 2 joint ID attributes\n"
"Trade::MeshData::jointIdsInto(): index 2 out of range for 2 joint ID attributes\n"
"Trade::MeshData::weightsAsArray(): index 2 out of range for 2 weight attributes\n"
"Trade::MeshData::weightsInto(): index 2 out of range for 2 weight attributes\n"
"Trade::MeshData::objectIdsInto(): index 0 out of range for 0 object ID attributes\n"); "Trade::MeshData::objectIdsInto(): index 0 out of range for 0 object ID attributes\n");
} }

Loading…
Cancel
Save