Browse Source

Trade: implement support for tangents and bitangents in MeshData.

Originally wanted to offload this to someone else, but then realized I
need those for generic vertex attribute definitions, which I need for
instancing, which I need now. So here it is, at the bottom of the
dependency chain.
pull/430/head
Vladimír Vondruš 6 years ago
parent
commit
f4bca9b51e
  1. 9
      doc/snippets/MagnumTrade.cpp
  2. 105
      src/Magnum/Trade/MeshData.cpp
  3. 141
      src/Magnum/Trade/MeshData.h
  4. 194
      src/Magnum/Trade/Test/MeshDataTest.cpp

9
doc/snippets/MagnumTrade.cpp

@ -254,6 +254,15 @@ Trade::MeshIndexData data{indices};
/* [MeshIndexData-usage] */ /* [MeshIndexData-usage] */
} }
{
Vector3 normal;
Vector4 tangent;
/* [MeshAttribute-bitangent-from-tangent] */
Vector3 bitangent = Math::cross(normal, tangent.xyz())*tangent.w();
/* [MeshAttribute-bitangent-from-tangent] */
static_cast<void>(bitangent);
}
{ {
/* [MeshAttributeData-usage] */ /* [MeshAttributeData-usage] */
Containers::StridedArrayView1D<const Vector3> positions; Containers::StridedArrayView1D<const Vector3> positions;

105
src/Magnum/Trade/MeshData.cpp

@ -557,27 +557,106 @@ Containers::Array<Vector3> MeshData::positions3DAsArray(const UnsignedInt id) co
return out; return out;
} }
void MeshData::normalsInto(const Containers::StridedArrayView1D<Vector3> destination, const UnsignedInt id) const { namespace {
const UnsignedInt attributeId = attributeFor(MeshAttribute::Normal, id);
CORRADE_ASSERT(attributeId != ~UnsignedInt{}, "Trade::MeshData::normalsInto(): index" << id << "out of range for" << attributeCount(MeshAttribute::Normal) << "normal attributes", ); void tangentsOrNormalsInto(const Containers::StridedArrayView1D<const void> attributeData, const Containers::StridedArrayView1D<Vector3> destination, VertexFormat format) {
CORRADE_ASSERT(destination.size() == _vertexCount, "Trade::MeshData::normalsInto(): expected a view with" << _vertexCount << "elements but got" << destination.size(), );
const MeshAttributeData& attribute = _attributes[attributeId];
CORRADE_ASSERT(!isVertexFormatImplementationSpecific(attribute._format),
"Trade::MeshData::normalsInto(): 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);
const auto destination3f = Containers::arrayCast<2, Float>(destination); const auto destination3f = Containers::arrayCast<2, Float>(destination);
if(attribute._format == VertexFormat::Vector3) if(format == VertexFormat::Vector3)
Utility::copy(Containers::arrayCast<const Vector3>(attributeData), destination); Utility::copy(Containers::arrayCast<const Vector3>(attributeData), destination);
else if(attribute._format == VertexFormat::Vector3h) else if(format == VertexFormat::Vector3h)
Math::unpackHalfInto(Containers::arrayCast<2, const UnsignedShort>(attributeData, 3), destination3f); Math::unpackHalfInto(Containers::arrayCast<2, const UnsignedShort>(attributeData, 3), destination3f);
else if(attribute._format == VertexFormat::Vector3bNormalized) else if(format == VertexFormat::Vector3bNormalized)
Math::unpackInto(Containers::arrayCast<2, const Byte>(attributeData, 3), destination3f); Math::unpackInto(Containers::arrayCast<2, const Byte>(attributeData, 3), destination3f);
else if(attribute._format == VertexFormat::Vector3sNormalized) else if(format == VertexFormat::Vector3sNormalized)
Math::unpackInto(Containers::arrayCast<2, const Short>(attributeData, 3), destination3f); Math::unpackInto(Containers::arrayCast<2, const Short>(attributeData, 3), destination3f);
else CORRADE_ASSERT_UNREACHABLE(); /* LCOV_EXCL_LINE */ else CORRADE_ASSERT_UNREACHABLE(); /* LCOV_EXCL_LINE */
} }
}
void MeshData::tangentsInto(const Containers::StridedArrayView1D<Vector3> destination, const UnsignedInt id) const {
const UnsignedInt attributeId = attributeFor(MeshAttribute::Tangent, id);
CORRADE_ASSERT(attributeId != ~UnsignedInt{}, "Trade::MeshData::tangentsInto(): index" << id << "out of range for" << attributeCount(MeshAttribute::Tangent) << "tangent attributes", );
CORRADE_ASSERT(destination.size() == _vertexCount, "Trade::MeshData::tangentsInto(): expected a view with" << _vertexCount << "elements but got" << destination.size(), );
const MeshAttributeData& attribute = _attributes[attributeId];
CORRADE_ASSERT(!isVertexFormatImplementationSpecific(attribute._format),
"Trade::MeshData::tangentsInto(): can't extract data out of an implementation-specific vertex format" << reinterpret_cast<void*>(vertexFormatUnwrap(attribute._format)), );
/* If the tangent is four-component, ignore the last component; otherwise
copy/unpack given format directly */
VertexFormat format;
if(attribute._format == VertexFormat::Vector4)
format = VertexFormat::Vector3;
else if(attribute._format == VertexFormat::Vector4h)
format = VertexFormat::Vector3h;
else if(attribute._format == VertexFormat::Vector4ubNormalized)
format = VertexFormat::Vector3ubNormalized;
else if(attribute._format == VertexFormat::Vector4usNormalized)
format = VertexFormat::Vector3usNormalized;
else format = attribute._format;
tangentsOrNormalsInto(attributeDataViewInternal(attribute), destination, format);
}
Containers::Array<Vector3> MeshData::tangentsAsArray(const UnsignedInt id) const {
Containers::Array<Vector3> out{_vertexCount};
tangentsInto(out, id);
return out;
}
void MeshData::bitangentSignsInto(const Containers::StridedArrayView1D<Float> destination, const UnsignedInt id) const {
const UnsignedInt attributeId = attributeFor(MeshAttribute::Tangent, id);
CORRADE_ASSERT(attributeId != ~UnsignedInt{}, "Trade::MeshData::bitangentSignsInto(): index" << id << "out of range for" << attributeCount(MeshAttribute::Tangent) << "tangent attributes", );
CORRADE_ASSERT(destination.size() == _vertexCount, "Trade::MeshData::bitangentSignsInto(): expected a view with" << _vertexCount << "elements but got" << destination.size(), );
const MeshAttributeData& attribute = _attributes[attributeId];
CORRADE_ASSERT(!isVertexFormatImplementationSpecific(attribute._format),
"Trade::MeshData::bitangentSignsInto(): 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);
const auto destination1f = Containers::arrayCast<2, Float>(destination);
if(attribute._format == VertexFormat::Vector4)
Utility::copy(Containers::arrayCast<2, const Float>(attributeData, 4).transposed<0, 1>()[3], destination);
else if(attribute._format == VertexFormat::Vector4h)
Math::unpackHalfInto(Containers::arrayCast<2, const UnsignedShort>(attributeData, 4).suffix({0, 3}), destination1f);
else if(attribute._format == VertexFormat::Vector4bNormalized)
Math::unpackInto(Containers::arrayCast<2, const Byte>(attributeData, 4).suffix({0, 3}), destination1f);
else if(attribute._format == VertexFormat::Vector4sNormalized)
Math::unpackInto(Containers::arrayCast<2, const Short>(attributeData, 4).suffix({0, 3}), destination1f);
else CORRADE_ASSERT(false, "Trade::MeshData::bitangentSignsInto(): expected four-component tangents, but got" << attribute._format, );
}
Containers::Array<Float> MeshData::bitangentSignsAsArray(const UnsignedInt id) const {
Containers::Array<Float> out{_vertexCount};
bitangentSignsInto(out, id);
return out;
}
void MeshData::bitangentsInto(const Containers::StridedArrayView1D<Vector3> destination, const UnsignedInt id) const {
const UnsignedInt attributeId = attributeFor(MeshAttribute::Bitangent, id);
CORRADE_ASSERT(attributeId != ~UnsignedInt{}, "Trade::MeshData::bitangentsInto(): index" << id << "out of range for" << attributeCount(MeshAttribute::Bitangent) << "bitangent attributes", );
CORRADE_ASSERT(destination.size() == _vertexCount, "Trade::MeshData::bitangentsInto(): expected a view with" << _vertexCount << "elements but got" << destination.size(), );
const MeshAttributeData& attribute = _attributes[attributeId];
CORRADE_ASSERT(!isVertexFormatImplementationSpecific(attribute._format),
"Trade::MeshData::bitangentsInto(): can't extract data out of an implementation-specific vertex format" << reinterpret_cast<void*>(vertexFormatUnwrap(attribute._format)), );
tangentsOrNormalsInto(attributeDataViewInternal(attribute), destination, attribute._format);
}
Containers::Array<Vector3> MeshData::bitangentsAsArray(const UnsignedInt id) const {
Containers::Array<Vector3> out{_vertexCount};
bitangentsInto(out, id);
return out;
}
void MeshData::normalsInto(const Containers::StridedArrayView1D<Vector3> destination, const UnsignedInt id) const {
const UnsignedInt attributeId = attributeFor(MeshAttribute::Normal, id);
CORRADE_ASSERT(attributeId != ~UnsignedInt{}, "Trade::MeshData::normalsInto(): index" << id << "out of range for" << attributeCount(MeshAttribute::Normal) << "normal attributes", );
CORRADE_ASSERT(destination.size() == _vertexCount, "Trade::MeshData::normalsInto(): expected a view with" << _vertexCount << "elements but got" << destination.size(), );
const MeshAttributeData& attribute = _attributes[attributeId];
CORRADE_ASSERT(!isVertexFormatImplementationSpecific(attribute._format),
"Trade::MeshData::normalsInto(): can't extract data out of an implementation-specific vertex format" << reinterpret_cast<void*>(vertexFormatUnwrap(attribute._format)), );
tangentsOrNormalsInto(attributeDataViewInternal(attribute), destination, attribute._format);
}
Containers::Array<Vector3> MeshData::normalsAsArray(const UnsignedInt id) const { Containers::Array<Vector3> MeshData::normalsAsArray(const UnsignedInt id) const {
Containers::Array<Vector3> out{_vertexCount}; Containers::Array<Vector3> out{_vertexCount};
normalsInto(out, id); normalsInto(out, id);
@ -704,6 +783,8 @@ Debug& operator<<(Debug& debug, const MeshAttribute value) {
/* LCOV_EXCL_START */ /* LCOV_EXCL_START */
#define _c(value) case MeshAttribute::value: return debug << "::" << Debug::nospace << #value; #define _c(value) case MeshAttribute::value: return debug << "::" << Debug::nospace << #value;
_c(Position) _c(Position)
_c(Tangent)
_c(Bitangent)
_c(Normal) _c(Normal)
_c(TextureCoordinates) _c(TextureCoordinates)
_c(Color) _c(Color)

141
src/Magnum/Trade/MeshData.h

@ -72,6 +72,36 @@ enum class MeshAttribute: UnsignedShort {
*/ */
Position = 1, Position = 1,
/**
* Tangent, optionally including bitangent sign. In the first case the type
* is usually @ref VertexFormat::Vector3, but can be also
* @ref VertexFormat::Vector3h, @ref VertexFormat::Vector3bNormalized or
* @ref VertexFormat::Vector3sNormalized; in the second case the type is
* @ref VertexFormat::Vector4 (or @ref VertexFormat::Vector4h,
* @ref VertexFormat::Vector4bNormalized,
* @ref VertexFormat::Vector4sNormalized) and the fourth component is a
* sign value (@cpp -1.0f @ce or @cpp +1.0f @ce) defining handedness of the
* tangent basis. Reconstruct the @ref MeshAttribute::Bitangent can be then
* done like this:
*
* @snippet MagnumTrade.cpp MeshAttribute-bitangent-from-tangent
*
* Corresponds to @ref Shaders::Generic::Tangent.
* @see @ref MeshData::tangentsAsArray(),
* @ref MeshData::bitangentSignsAsArray()
*/
Tangent,
/**
* Bitangent. Type is usually @ref VertexFormat::Vector3, but can be also
* @ref VertexFormat::Vector3h, @ref VertexFormat::Vector3bNormalized or
* @ref VertexFormat::Vector3sNormalized. For better storage efficiency,
* the bitangent can be also reconstructed from the normal and tangent, see
* @ref MeshAttribute::Tangent for more information.
* @see @ref MeshData::bitangentsAsArray()
*/
Bitangent,
/** /**
* Normal. Type is usually @ref VertexFormat::Vector3, but can be also * Normal. Type is usually @ref VertexFormat::Vector3, but can be also
* @ref VertexFormat::Vector3h. @ref VertexFormat::Vector3bNormalized or * @ref VertexFormat::Vector3h. @ref VertexFormat::Vector3bNormalized or
@ -563,7 +593,8 @@ the @ref Primitives library.
@section Trade-MeshData-usage Basic usage @section Trade-MeshData-usage Basic usage
The simplest usage is through the convenience functions @ref positions2DAsArray(), The simplest usage is through the convenience functions @ref positions2DAsArray(),
@ref positions3DAsArray(), @ref normalsAsArray(), @ref textureCoordinates2DAsArray() @ref positions3DAsArray(), @ref tangentsAsArray(), @ref bitangentsAsArray(),
@ref normalsAsArray(), @ref tangentsAsArray(), @ref textureCoordinates2DAsArray()
and @ref colorsAsArray(). Each of these takes an index (as there can be and @ref colorsAsArray(). Each of these takes an index (as there can be
multiple sets of texture coordinates, for example) and you're expected to check multiple sets of texture coordinates, for example) and you're expected to check
for attribute presence first with either @ref hasAttribute() or for attribute presence first with either @ref hasAttribute() or
@ -1293,10 +1324,12 @@ class MAGNUM_TRADE_EXPORT MeshData {
* case you need to use the overload below by using @cpp T[] @ce * 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 * instead of @cpp T @ce. You can also use the non-templated
* @ref positions2DAsArray(), @ref positions3DAsArray(), * @ref positions2DAsArray(), @ref positions3DAsArray(),
* @ref normalsAsArray(), @ref textureCoordinates2DAsArray() and * @ref tangentsAsArray(), @ref bitangentSignsAsArray(),
* @ref colorsAsArray() accessors to get common attributes converted to * @ref bitangentsAsArray(), @ref normalsAsArray(),
* usual types, but note that these operations involve extra allocation * @ref textureCoordinates2DAsArray() and @ref colorsAsArray()
* and data conversion. * 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, * @see @ref attribute(MeshAttribute, UnsignedInt) const,
* @ref mutableAttribute(MeshAttribute, UnsignedInt), * @ref mutableAttribute(MeshAttribute, UnsignedInt),
* @ref isVertexFormatImplementationSpecific(), * @ref isVertexFormatImplementationSpecific(),
@ -1483,6 +1516,90 @@ class MAGNUM_TRADE_EXPORT MeshData {
*/ */
void positions3DInto(Containers::StridedArrayView1D<Vector3> destination, UnsignedInt id = 0) const; void positions3DInto(Containers::StridedArrayView1D<Vector3> destination, UnsignedInt id = 0) const;
/**
* @brief Tangents as 3D float vectors
*
* Convenience alternative to @ref attribute(MeshAttribute, UnsignedInt) const
* with @ref MeshAttribute::Tangent as the first argument. Converts the
* tangent 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.
*
* If the tangents contain a fourth component with bitangent direction,
* it's ignored here --- use @ref bitangentSignsAsArray() to get those
* instead. You can also use @ref tangentsInto() together with
* @ref bitangentSignsInto() to put them both in a single array.
* @see @ref bitangentsAsArray(), @ref normalsAsArray(),
* @ref attributeFormat(),
* @ref isVertexFormatImplementationSpecific()
*/
Containers::Array<Vector3> tangentsAsArray(UnsignedInt id = 0) const;
/**
* @brief Tangents as 3D float vectors into a pre-allocated view
*
* Like @ref tangentsAsArray(), but puts the result into @p destination
* instead of allocating a new array. Expects that @p destination is
* sized to contain exactly all data.
* @see @ref vertexCount()
*/
void tangentsInto(Containers::StridedArrayView1D<Vector3> destination, UnsignedInt id = 0) const;
/**
* @brief Bitangent signs as floats
*
* Counterpart to @ref tangentsAsArray() returning value of the fourth
* component. Expects that the type of @ref MeshAttribute::Tangent is
* four-component. You can also use @ref tangentsInto() together with
* @ref bitangentSignsInto() to put them both in a single array.
* @see @ref tangentsAsArray(), @ref bitangentsAsArray(),
* @ref normalsAsArray(), @ref attributeFormat(),
* @ref isVertexFormatImplementationSpecific()
*/
Containers::Array<Float> bitangentSignsAsArray(UnsignedInt id = 0) const;
/**
* @brief Bitangent signs as floats into a pre-allocated view
*
* Like @ref bitangentsAsArray(), but puts the result into
* @p destination instead of allocating a new array. Expects that
* @p destination is sized to contain exactly all data.
* @see @ref vertexCount()
*/
void bitangentSignsInto(Containers::StridedArrayView1D<Float> destination, UnsignedInt id = 0) const;
/**
* @brief Bitangents as 3D float vectors
*
* Convenience alternative to @ref attribute(MeshAttribute, UnsignedInt) const
* with @ref MeshAttribute::Bitangent as the first argument. Converts
* the bitangent 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.
*
* Note that in some cases the bitangents aren't provided directly but
* calculated from normals and four-component tangents. In that case
* you'll need to get bitangent signs via @ref bitangentSignsAsArray()
* and calculate the bitangents as shown in the documentation of
* @ref MeshAttribute::Tangent.
* @see @ref bitangentsInto(), @ref tangentsAsArray(),
* @ref normalsAsArray(), @ref attributeFormat(),
* @ref isVertexFormatImplementationSpecific()
*/
Containers::Array<Vector3> bitangentsAsArray(UnsignedInt id = 0) const;
/**
* @brief Bitangents as 3D float vectors into a pre-allocated view
*
* Like @ref bitangentsAsArray(), but puts the result into
* @p destination instead of allocating a new array. Expects that
* @p destination is sized to contain exactly all data.
* @see @ref vertexCount()
*/
void bitangentsInto(Containers::StridedArrayView1D<Vector3> destination, UnsignedInt id = 0) const;
/** /**
* @brief Normals as 3D float vectors * @brief Normals as 3D float vectors
* *
@ -1492,7 +1609,8 @@ class MAGNUM_TRADE_EXPORT MeshData {
* newly-allocated array. Expects that the vertex format is *not* * newly-allocated array. Expects that the vertex format is *not*
* implementation-specific, in that case you can only access the * implementation-specific, in that case you can only access the
* attribute via the typeless @ref attribute(MeshAttribute, UnsignedInt) const. * attribute via the typeless @ref attribute(MeshAttribute, UnsignedInt) const.
* @see @ref normalsInto(), @ref attributeFormat(), * @see @ref normalsInto(), @ref tangentsAsArray(),
* @ref bitangentsAsArray(), @ref attributeFormat(),
* @ref isVertexFormatImplementationSpecific() * @ref isVertexFormatImplementationSpecific()
*/ */
Containers::Array<Vector3> normalsAsArray(UnsignedInt id = 0) const; Containers::Array<Vector3> normalsAsArray(UnsignedInt id = 0) const;
@ -1862,7 +1980,16 @@ namespace Implementation {
format == VertexFormat::Vector3usNormalized || format == VertexFormat::Vector3usNormalized ||
format == VertexFormat::Vector3s || format == VertexFormat::Vector3s ||
format == VertexFormat::Vector3sNormalized)) || format == VertexFormat::Vector3sNormalized)) ||
(name == MeshAttribute::Normal && (name == MeshAttribute::Tangent &&
(format == VertexFormat::Vector3 ||
format == VertexFormat::Vector3h ||
format == VertexFormat::Vector3bNormalized ||
format == VertexFormat::Vector3sNormalized ||
format == VertexFormat::Vector4 ||
format == VertexFormat::Vector4h ||
format == VertexFormat::Vector4bNormalized ||
format == VertexFormat::Vector4sNormalized)) ||
((name == MeshAttribute::Bitangent || name == MeshAttribute::Normal) &&
(format == VertexFormat::Vector3 || (format == VertexFormat::Vector3 ||
format == VertexFormat::Vector3h || format == VertexFormat::Vector3h ||
format == VertexFormat::Vector3bNormalized || format == VertexFormat::Vector3bNormalized ||

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

@ -121,6 +121,16 @@ struct MeshDataTest: TestSuite::Tester {
template<class T> void positions3DAsArrayPackedUnsignedNormalized(); template<class T> void positions3DAsArrayPackedUnsignedNormalized();
template<class T> void positions3DAsArrayPackedSignedNormalized(); template<class T> void positions3DAsArrayPackedSignedNormalized();
void positions3DIntoArrayInvalidSize(); void positions3DIntoArrayInvalidSize();
template<class T> void tangentsAsArray();
template<class T> void tangentsAsArrayPackedSignedNormalized();
void tangentsIntoArrayInvalidSize();
template<class T> void bitangentSignsAsArray();
template<class T> void bitangentSignsAsArrayPackedSignedNormalized();
void bitangentSignsAsArrayNotFourComponent();
void bitangentSignsIntoArrayInvalidSize();
template<class T> void bitangentsAsArray();
template<class T> void bitangentsAsArrayPackedSignedNormalized();
void bitangentsIntoArrayInvalidSize();
template<class T> void normalsAsArray(); template<class T> void normalsAsArray();
template<class T> void normalsAsArrayPackedSignedNormalized(); template<class T> void normalsAsArrayPackedSignedNormalized();
void normalsIntoArrayInvalidSize(); void normalsIntoArrayInvalidSize();
@ -302,6 +312,22 @@ MeshDataTest::MeshDataTest() {
&MeshDataTest::positions3DAsArrayPackedSignedNormalized<Vector3b>, &MeshDataTest::positions3DAsArrayPackedSignedNormalized<Vector3b>,
&MeshDataTest::positions3DAsArrayPackedSignedNormalized<Vector3s>, &MeshDataTest::positions3DAsArrayPackedSignedNormalized<Vector3s>,
&MeshDataTest::positions3DIntoArrayInvalidSize, &MeshDataTest::positions3DIntoArrayInvalidSize,
&MeshDataTest::tangentsAsArray<Vector3>,
&MeshDataTest::tangentsAsArray<Vector3h>,
&MeshDataTest::tangentsAsArrayPackedSignedNormalized<Vector3b>,
&MeshDataTest::tangentsAsArrayPackedSignedNormalized<Vector3s>,
&MeshDataTest::tangentsIntoArrayInvalidSize,
&MeshDataTest::bitangentSignsAsArray<Float>,
&MeshDataTest::bitangentSignsAsArray<Half>,
&MeshDataTest::bitangentSignsAsArrayPackedSignedNormalized<Byte>,
&MeshDataTest::bitangentSignsAsArrayPackedSignedNormalized<Short>,
&MeshDataTest::bitangentSignsAsArrayNotFourComponent,
&MeshDataTest::bitangentSignsIntoArrayInvalidSize,
&MeshDataTest::bitangentsAsArray<Vector3>,
&MeshDataTest::bitangentsAsArray<Vector3h>,
&MeshDataTest::bitangentsAsArrayPackedSignedNormalized<Vector3b>,
&MeshDataTest::bitangentsAsArrayPackedSignedNormalized<Vector3s>,
&MeshDataTest::bitangentsIntoArrayInvalidSize,
&MeshDataTest::normalsAsArray<Vector3>, &MeshDataTest::normalsAsArray<Vector3>,
&MeshDataTest::normalsAsArray<Vector3h>, &MeshDataTest::normalsAsArray<Vector3h>,
&MeshDataTest::normalsAsArrayPackedSignedNormalized<Vector3b>, &MeshDataTest::normalsAsArrayPackedSignedNormalized<Vector3b>,
@ -1946,6 +1972,158 @@ void MeshDataTest::positions3DIntoArrayInvalidSize() {
"Trade::MeshData::positions3DInto(): expected a view with 3 elements but got 2\n"); "Trade::MeshData::positions3DInto(): expected a view with 3 elements but got 2\n");
} }
template<class T> void MeshDataTest::tangentsAsArray() {
setTestCaseTemplateName(NameTraits<T>::name());
typedef typename T::Type U;
Containers::Array<char> vertexData{3*sizeof(T)};
auto tangentView = Containers::arrayCast<T>(vertexData);
/* Needs to be sufficiently representable to have the test work also for
half floats */
tangentView[0] = {U(2.0f), U(1.0f), U(0.75f)};
tangentView[1] = {U(0.0f), U(-1.0f), U(1.25f)};
tangentView[2] = {U(-2.0f), U(3.0f), U(2.5f)};
MeshData data{MeshPrimitive::Points, std::move(vertexData), {MeshAttributeData{MeshAttribute::Tangent, tangentView}}};
CORRADE_COMPARE_AS(data.tangentsAsArray(), Containers::arrayView<Vector3>({
{2.0f, 1.0f, 0.75f}, {0.0f, -1.0f, 1.25f}, {-2.0f, 3.0f, 2.5f},
}), TestSuite::Compare::Container);
}
template<class T> void MeshDataTest::tangentsAsArrayPackedSignedNormalized() {
setTestCaseTemplateName(NameTraits<T>::name());
Containers::Array<char> vertexData{2*sizeof(T)};
auto tangentsView = Containers::arrayCast<T>(vertexData);
tangentsView[0] = {Math::pack<typename T::Type>(1.0f), 0, Math::pack<typename T::Type>(1.0f)};
tangentsView[1] = {0, Math::pack<typename T::Type>(-1.0f), 0};
MeshData data{MeshPrimitive::Points, std::move(vertexData), {MeshAttributeData{MeshAttribute::Tangent,
/* Assuming the normalized enum is always after the non-normalized */
VertexFormat(UnsignedInt(Implementation::vertexFormatFor<T>()) + 1),
tangentsView}}};
CORRADE_COMPARE_AS(data.tangentsAsArray(), Containers::arrayView<Vector3>({
{1.0f, 0.0f, 1.0f}, {0.0f, -1.0f, 0.0f}
}), TestSuite::Compare::Container);
}
void MeshDataTest::tangentsIntoArrayInvalidSize() {
Containers::Array<char> vertexData{3*sizeof(Vector3)};
MeshData data{MeshPrimitive::Points, std::move(vertexData), {MeshAttributeData{MeshAttribute::Tangent, Containers::arrayCast<Vector3>(vertexData)}}};
std::ostringstream out;
Error redirectError{&out};
Vector3 destination[2];
data.tangentsInto(destination);
CORRADE_COMPARE(out.str(),
"Trade::MeshData::tangentsInto(): expected a view with 3 elements but got 2\n");
}
template<class T> void MeshDataTest::bitangentSignsAsArray() {
setTestCaseTemplateName(Math::TypeTraits<T>::name());
Containers::Array<char> vertexData{3*sizeof(Math::Vector4<T>)};
auto tangentView = Containers::arrayCast<Math::Vector4<T>>(vertexData);
/* Needs to be sufficiently representable to have the test work also for
half floats */
tangentView[0] = {T(2.0f), T(1.0f), T(0.75f), T(-1.0f)};
tangentView[1] = {T(0.0f), T(-1.0f), T(1.25f), T(1.0f)};
tangentView[2] = {T(-2.0f), T(3.0f), T(2.5f), T(-1.0f)};
MeshData data{MeshPrimitive::Points, std::move(vertexData), {MeshAttributeData{MeshAttribute::Tangent, tangentView}}};
CORRADE_COMPARE_AS(data.bitangentSignsAsArray(), Containers::arrayView<Float>({
-1.0f, 1.0f, -1.0f
}), TestSuite::Compare::Container);
}
template<class T> void MeshDataTest::bitangentSignsAsArrayPackedSignedNormalized() {
setTestCaseTemplateName(Math::TypeTraits<T>::name());
Containers::Array<char> vertexData{2*sizeof(Math::Vector4<T>)};
auto bitangentsView = Containers::arrayCast<Math::Vector4<T>>(vertexData);
bitangentsView[0] = {Math::pack<T>(1.0f), 0, Math::pack<T>(1.0f), Math::pack<T>(-1.0f)};
bitangentsView[1] = {0, Math::pack<T>(-1.0f), 0, Math::pack<T>(1.0f)};
MeshData data{MeshPrimitive::Points, std::move(vertexData), {MeshAttributeData{MeshAttribute::Tangent,
/* Assuming the normalized enum is always after the non-normalized */
VertexFormat(UnsignedInt(Implementation::vertexFormatFor<Math::Vector4<T>>()) + 1),
bitangentsView}}};
CORRADE_COMPARE_AS(data.bitangentSignsAsArray(), Containers::arrayView<Float>({
-1.0f, 1.0f
}), TestSuite::Compare::Container);
}
void MeshDataTest::bitangentSignsAsArrayNotFourComponent() {
Containers::Array<char> vertexData{3*sizeof(Vector3s)};
MeshData data{MeshPrimitive::Points, std::move(vertexData), {MeshAttributeData{MeshAttribute::Tangent, VertexFormat::Vector3sNormalized, Containers::arrayCast<Vector3s>(vertexData)}}};
std::ostringstream out;
Error redirectError{&out};
Float destination[3];
data.bitangentSignsInto(destination);
CORRADE_COMPARE(out.str(),
"Trade::MeshData::bitangentSignsInto(): expected four-component tangents, but got VertexFormat::Vector3sNormalized\n");
}
void MeshDataTest::bitangentSignsIntoArrayInvalidSize() {
Containers::Array<char> vertexData{3*sizeof(Vector4)};
MeshData data{MeshPrimitive::Points, std::move(vertexData), {MeshAttributeData{MeshAttribute::Tangent, Containers::arrayCast<Vector4>(vertexData)}}};
std::ostringstream out;
Error redirectError{&out};
Float destination[2];
data.bitangentSignsInto(destination);
CORRADE_COMPARE(out.str(),
"Trade::MeshData::bitangentSignsInto(): expected a view with 3 elements but got 2\n");
}
template<class T> void MeshDataTest::bitangentsAsArray() {
setTestCaseTemplateName(NameTraits<T>::name());
typedef typename T::Type U;
Containers::Array<char> vertexData{3*sizeof(T)};
auto bitangentsView = Containers::arrayCast<T>(vertexData);
/* Needs to be sufficiently representable to have the test work also for
half floats */
bitangentsView[0] = {U(2.0f), U(1.0f), U(0.75f)};
bitangentsView[1] = {U(0.0f), U(-1.0f), U(1.25f)};
bitangentsView[2] = {U(-2.0f), U(3.0f), U(2.5f)};
MeshData data{MeshPrimitive::Points, std::move(vertexData), {MeshAttributeData{MeshAttribute::Bitangent, bitangentsView}}};
CORRADE_COMPARE_AS(data.bitangentsAsArray(), Containers::arrayView<Vector3>({
{2.0f, 1.0f, 0.75f}, {0.0f, -1.0f, 1.25f}, {-2.0f, 3.0f, 2.5f},
}), TestSuite::Compare::Container);
}
template<class T> void MeshDataTest::bitangentsAsArrayPackedSignedNormalized() {
setTestCaseTemplateName(NameTraits<T>::name());
Containers::Array<char> vertexData{2*sizeof(T)};
auto bitangentsView = Containers::arrayCast<T>(vertexData);
bitangentsView[0] = {Math::pack<typename T::Type>(1.0f), 0, Math::pack<typename T::Type>(1.0f)};
bitangentsView[1] = {0, Math::pack<typename T::Type>(-1.0f), 0};
MeshData data{MeshPrimitive::Points, std::move(vertexData), {MeshAttributeData{MeshAttribute::Bitangent,
/* Assuming the normalized enum is always after the non-normalized */
VertexFormat(UnsignedInt(Implementation::vertexFormatFor<T>()) + 1),
bitangentsView}}};
CORRADE_COMPARE_AS(data.bitangentsAsArray(), Containers::arrayView<Vector3>({
{1.0f, 0.0f, 1.0f}, {0.0f, -1.0f, 0.0f}
}), TestSuite::Compare::Container);
}
void MeshDataTest::bitangentsIntoArrayInvalidSize() {
Containers::Array<char> vertexData{3*sizeof(Vector3)};
MeshData data{MeshPrimitive::Points, std::move(vertexData), {MeshAttributeData{MeshAttribute::Bitangent, Containers::arrayCast<Vector3>(vertexData)}}};
std::ostringstream out;
Error redirectError{&out};
Vector3 destination[2];
data.bitangentsInto(destination);
CORRADE_COMPARE(out.str(),
"Trade::MeshData::bitangentsInto(): expected a view with 3 elements but got 2\n");
}
template<class T> void MeshDataTest::normalsAsArray() { template<class T> void MeshDataTest::normalsAsArray() {
setTestCaseTemplateName(NameTraits<T>::name()); setTestCaseTemplateName(NameTraits<T>::name());
typedef typename T::Type U; typedef typename T::Type U;
@ -2188,6 +2366,10 @@ void MeshDataTest::implementationSpecificVertexFormatWrongAccess() {
MeshData data{MeshPrimitive::TriangleFan, DataFlag::Mutable, vertexData, { MeshData data{MeshPrimitive::TriangleFan, DataFlag::Mutable, vertexData, {
MeshAttributeData{MeshAttribute::Position, MeshAttributeData{MeshAttribute::Position,
vertexFormatWrap(0xdead1), attribute}, vertexFormatWrap(0xdead1), attribute},
MeshAttributeData{MeshAttribute::Tangent,
vertexFormatWrap(0xdead2), attribute},
MeshAttributeData{MeshAttribute::Bitangent,
vertexFormatWrap(0xdead2), attribute},
MeshAttributeData{MeshAttribute::Normal, MeshAttributeData{MeshAttribute::Normal,
vertexFormatWrap(0xdead2), attribute}, vertexFormatWrap(0xdead2), attribute},
MeshAttributeData{MeshAttribute::TextureCoordinates, MeshAttributeData{MeshAttribute::TextureCoordinates,
@ -2207,6 +2389,9 @@ void MeshDataTest::implementationSpecificVertexFormatWrongAccess() {
data.mutableAttribute<Float>(MeshAttribute::Color); data.mutableAttribute<Float>(MeshAttribute::Color);
data.positions2DAsArray(); data.positions2DAsArray();
data.positions3DAsArray(); data.positions3DAsArray();
data.tangentsAsArray();
data.bitangentSignsAsArray();
data.bitangentsAsArray();
data.normalsAsArray(); data.normalsAsArray();
data.textureCoordinates2DAsArray(); data.textureCoordinates2DAsArray();
data.colorsAsArray(); data.colorsAsArray();
@ -2221,6 +2406,9 @@ void MeshDataTest::implementationSpecificVertexFormatWrongAccess() {
"Trade::MeshData::mutableAttribute(): can't cast data from an implementation-specific vertex format 0xdead4\n" "Trade::MeshData::mutableAttribute(): can't cast data from an implementation-specific vertex format 0xdead4\n"
"Trade::MeshData::positions2DInto(): can't extract data out of an implementation-specific vertex format 0xdead1\n" "Trade::MeshData::positions2DInto(): can't extract data out of an implementation-specific vertex format 0xdead1\n"
"Trade::MeshData::positions3DInto(): can't extract data out of an implementation-specific vertex format 0xdead1\n" "Trade::MeshData::positions3DInto(): can't extract data out of an implementation-specific vertex format 0xdead1\n"
"Trade::MeshData::tangentsInto(): can't extract data out of an implementation-specific vertex format 0xdead2\n"
"Trade::MeshData::bitangentSignsInto(): can't extract data out of an implementation-specific vertex format 0xdead2\n"
"Trade::MeshData::bitangentsInto(): can't extract data out of an implementation-specific vertex format 0xdead2\n"
"Trade::MeshData::normalsInto(): can't extract data out of an implementation-specific vertex format 0xdead2\n" "Trade::MeshData::normalsInto(): can't extract data out of an implementation-specific vertex format 0xdead2\n"
"Trade::MeshData::textureCoordinatesInto(): can't extract data out of an implementation-specific vertex format 0xdead3\n" "Trade::MeshData::textureCoordinatesInto(): can't extract data out of an implementation-specific vertex format 0xdead3\n"
"Trade::MeshData::colorsInto(): can't extract data out of an implementation-specific vertex format 0xdead4\n"); "Trade::MeshData::colorsInto(): can't extract data out of an implementation-specific vertex format 0xdead4\n");
@ -2430,6 +2618,9 @@ void MeshDataTest::attributeNotFound() {
data.attribute<Vector2>(MeshAttribute::Color, 2); data.attribute<Vector2>(MeshAttribute::Color, 2);
data.positions2DAsArray(); data.positions2DAsArray();
data.positions3DAsArray(); data.positions3DAsArray();
data.tangentsAsArray();
data.bitangentSignsAsArray();
data.bitangentsAsArray();
data.normalsAsArray(); data.normalsAsArray();
data.textureCoordinates2DAsArray(); data.textureCoordinates2DAsArray();
data.colorsAsArray(2); data.colorsAsArray(2);
@ -2457,6 +2648,9 @@ void MeshDataTest::attributeNotFound() {
"Trade::MeshData::attribute(): index 2 out of range for 2 Trade::MeshAttribute::Color attributes\n" "Trade::MeshData::attribute(): index 2 out of range for 2 Trade::MeshAttribute::Color attributes\n"
"Trade::MeshData::positions2DInto(): index 0 out of range for 0 position attributes\n" "Trade::MeshData::positions2DInto(): index 0 out of range for 0 position attributes\n"
"Trade::MeshData::positions3DInto(): index 0 out of range for 0 position attributes\n" "Trade::MeshData::positions3DInto(): index 0 out of range for 0 position attributes\n"
"Trade::MeshData::tangentsInto(): index 0 out of range for 0 tangent attributes\n"
"Trade::MeshData::bitangentSignsInto(): index 0 out of range for 0 tangent attributes\n"
"Trade::MeshData::bitangentsInto(): index 0 out of range for 0 bitangent attributes\n"
"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");

Loading…
Cancel
Save