diff --git a/src/Magnum/Trade/MeshData.cpp b/src/Magnum/Trade/MeshData.cpp index f1e458bb9..cb930b30d 100644 --- a/src/Magnum/Trade/MeshData.cpp +++ b/src/Magnum/Trade/MeshData.cpp @@ -28,6 +28,7 @@ #include #include "Magnum/Math/Color.h" +#include "Magnum/Math/PackingBatch.h" #include "Magnum/Trade/Implementation/arrayUtilities.h" namespace Magnum { namespace Trade { @@ -365,10 +366,39 @@ void MeshData::positions2DInto(const Containers::StridedArrayView1D des CORRADE_ASSERT(destination.size() == _vertexCount, "Trade::MeshData::positions2DInto(): expected a view with" << _vertexCount << "elements but got" << destination.size(), ); const MeshAttributeData& attribute = _attributes[attributeId]; + const auto destination2f = Containers::arrayCast<2, Float>(destination); + /* Copy 2D positions as-is, for 3D positions ignore Z */ if(attribute._format == VertexFormat::Vector2 || attribute._format == VertexFormat::Vector3) Utility::copy(Containers::arrayCast(attribute._data), destination); + else if(attribute._format == VertexFormat::Vector2h || + attribute._format == VertexFormat::Vector3h) + Math::unpackHalfInto(Containers::arrayCast<2, const UnsignedShort>(attribute._data, 2), destination2f); + else if(attribute._format == VertexFormat::Vector2ub || + attribute._format == VertexFormat::Vector3ub) + Math::castInto(Containers::arrayCast<2, const UnsignedByte>(attribute._data, 2), destination2f); + else if(attribute._format == VertexFormat::Vector2b || + attribute._format == VertexFormat::Vector3b) + Math::castInto(Containers::arrayCast<2, const Byte>(attribute._data, 2), destination2f); + else if(attribute._format == VertexFormat::Vector2us || + attribute._format == VertexFormat::Vector3us) + Math::castInto(Containers::arrayCast<2, const UnsignedShort>(attribute._data, 2), destination2f); + else if(attribute._format == VertexFormat::Vector2s || + attribute._format == VertexFormat::Vector3s) + Math::castInto(Containers::arrayCast<2, const Short>(attribute._data, 2), destination2f); + else if(attribute._format == VertexFormat::Vector2ubNormalized || + attribute._format == VertexFormat::Vector3ubNormalized) + Math::unpackInto(Containers::arrayCast<2, const UnsignedByte>(attribute._data, 2), destination2f); + else if(attribute._format == VertexFormat::Vector2bNormalized || + attribute._format == VertexFormat::Vector3bNormalized) + Math::unpackInto(Containers::arrayCast<2, const Byte>(attribute._data, 2), destination2f); + else if(attribute._format == VertexFormat::Vector2usNormalized || + attribute._format == VertexFormat::Vector3usNormalized) + Math::unpackInto(Containers::arrayCast<2, const UnsignedShort>(attribute._data, 2), destination2f); + else if(attribute._format == VertexFormat::Vector2sNormalized || + attribute._format == VertexFormat::Vector3sNormalized) + Math::unpackInto(Containers::arrayCast<2, const Short>(attribute._data, 2), destination2f); else CORRADE_ASSERT_UNREACHABLE(); /* LCOV_EXCL_LINE */ } @@ -384,19 +414,71 @@ void MeshData::positions3DInto(const Containers::StridedArrayView1D des CORRADE_ASSERT(destination.size() == _vertexCount, "Trade::MeshData::positions3DInto(): expected a view with" << _vertexCount << "elements but got" << destination.size(), ); const MeshAttributeData& attribute = _attributes[attributeId]; - /* For 2D positions copy the XY part to the first two components and then - fill the Z with a single value */ - if(attribute._format == VertexFormat::Vector2) { + const Containers::StridedArrayView2D destination2f = Containers::arrayCast<2, Float>(Containers::arrayCast(destination)); + const Containers::StridedArrayView2D destination3f = Containers::arrayCast<2, Float>(destination); + + /* For 2D positions copy the XY part to the first two components */ + if(attribute._format == VertexFormat::Vector2) Utility::copy(Containers::arrayCast(attribute._data), Containers::arrayCast(destination)); + else if(attribute._format == VertexFormat::Vector2h) + Math::unpackHalfInto(Containers::arrayCast<2, const UnsignedShort>(attribute._data, 2), destination2f); + else if(attribute._format == VertexFormat::Vector2ub) + Math::castInto(Containers::arrayCast<2, const UnsignedByte>(attribute._data, 2), destination2f); + else if(attribute._format == VertexFormat::Vector2b) + Math::castInto(Containers::arrayCast<2, const Byte>(attribute._data, 2), destination2f); + else if(attribute._format == VertexFormat::Vector2us) + Math::castInto(Containers::arrayCast<2, const UnsignedShort>(attribute._data, 2), destination2f); + else if(attribute._format == VertexFormat::Vector2s) + Math::castInto(Containers::arrayCast<2, const Short>(attribute._data, 2), destination2f); + else if(attribute._format == VertexFormat::Vector2ubNormalized) + Math::unpackInto(Containers::arrayCast<2, const UnsignedByte>(attribute._data, 2), destination2f); + else if(attribute._format == VertexFormat::Vector2bNormalized) + Math::unpackInto(Containers::arrayCast<2, const Byte>(attribute._data, 2), destination2f); + else if(attribute._format == VertexFormat::Vector2usNormalized) + Math::unpackInto(Containers::arrayCast<2, const UnsignedShort>(attribute._data, 2), destination2f); + else if(attribute._format == VertexFormat::Vector2sNormalized) + Math::unpackInto(Containers::arrayCast<2, const Short>(attribute._data, 2), destination2f); + + /* Copy 3D positions as-is */ + else if(attribute._format == VertexFormat::Vector3) + Utility::copy(Containers::arrayCast(attribute._data), destination); + else if(attribute._format == VertexFormat::Vector3h) + Math::unpackHalfInto(Containers::arrayCast<2, const UnsignedShort>(attribute._data, 3), destination3f); + else if(attribute._format == VertexFormat::Vector3ub) + Math::castInto(Containers::arrayCast<2, const UnsignedByte>(attribute._data, 3), destination3f); + else if(attribute._format == VertexFormat::Vector3b) + Math::castInto(Containers::arrayCast<2, const Byte>(attribute._data, 3), destination3f); + else if(attribute._format == VertexFormat::Vector3us) + Math::castInto(Containers::arrayCast<2, const UnsignedShort>(attribute._data, 3), destination3f); + else if(attribute._format == VertexFormat::Vector3s) + Math::castInto(Containers::arrayCast<2, const Short>(attribute._data, 3), destination3f); + else if(attribute._format == VertexFormat::Vector3ubNormalized) + Math::unpackInto(Containers::arrayCast<2, const UnsignedByte>(attribute._data, 3), destination3f); + else if(attribute._format == VertexFormat::Vector3bNormalized) + Math::unpackInto(Containers::arrayCast<2, const Byte>(attribute._data, 3), destination3f); + else if(attribute._format == VertexFormat::Vector3usNormalized) + Math::unpackInto(Containers::arrayCast<2, const UnsignedShort>(attribute._data, 3), destination3f); + else if(attribute._format == VertexFormat::Vector3sNormalized) + Math::unpackInto(Containers::arrayCast<2, const Short>(attribute._data, 3), destination3f); + else CORRADE_ASSERT_UNREACHABLE(); /* LCOV_EXCL_LINE */ + + /* For 2D positions finally fill the Z with a single value */ + if(attribute._format == VertexFormat::Vector2 || + attribute._format == VertexFormat::Vector2h || + attribute._format == VertexFormat::Vector2ub || + attribute._format == VertexFormat::Vector2b || + attribute._format == VertexFormat::Vector2us || + attribute._format == VertexFormat::Vector2s || + attribute._format == VertexFormat::Vector2ubNormalized || + attribute._format == VertexFormat::Vector2bNormalized || + attribute._format == VertexFormat::Vector2usNormalized || + attribute._format == VertexFormat::Vector2sNormalized) { constexpr Float z[1]{0.0f}; Utility::copy( Containers::stridedArrayView(z).broadcasted<0>(_vertexCount), - Containers::arrayCast<2, Float>(destination).transposed<0, 1>()[2]); - /* Copy 3D positions as-is */ - } else if(attribute._format == VertexFormat::Vector3) { - Utility::copy(Containers::arrayCast(attribute._data), destination); - } else CORRADE_ASSERT_UNREACHABLE(); /* LCOV_EXCL_LINE */ + destination3f.transposed<0, 1>()[2]); + } } Containers::Array MeshData::positions3DAsArray(const UnsignedInt id) const { @@ -411,8 +493,16 @@ void MeshData::normalsInto(const Containers::StridedArrayView1D destina CORRADE_ASSERT(destination.size() == _vertexCount, "Trade::MeshData::normalsInto(): expected a view with" << _vertexCount << "elements but got" << destination.size(), ); const MeshAttributeData& attribute = _attributes[attributeId]; + const auto destination3f = Containers::arrayCast<2, Float>(destination); + if(attribute._format == VertexFormat::Vector3) Utility::copy(Containers::arrayCast(attribute._data), destination); + else if(attribute._format == VertexFormat::Vector3h) + Math::unpackHalfInto(Containers::arrayCast<2, const UnsignedShort>(attribute._data, 3), destination3f); + else if(attribute._format == VertexFormat::Vector3bNormalized) + Math::unpackInto(Containers::arrayCast<2, const Byte>(attribute._data, 3), destination3f); + else if(attribute._format == VertexFormat::Vector3sNormalized) + Math::unpackInto(Containers::arrayCast<2, const Short>(attribute._data, 3), destination3f); else CORRADE_ASSERT_UNREACHABLE(); /* LCOV_EXCL_LINE */ } @@ -428,8 +518,28 @@ void MeshData::textureCoordinates2DInto(const Containers::StridedArrayView1D(destination); + if(attribute._format == VertexFormat::Vector2) Utility::copy(Containers::arrayCast(attribute._data), destination); + else if(attribute._format == VertexFormat::Vector2h) + Math::unpackHalfInto(Containers::arrayCast<2, const UnsignedShort>(attribute._data, 2), destination2f); + else if(attribute._format == VertexFormat::Vector2ub) + Math::castInto(Containers::arrayCast<2, const UnsignedByte>(attribute._data, 2), destination2f); + else if(attribute._format == VertexFormat::Vector2b) + Math::castInto(Containers::arrayCast<2, const Byte>(attribute._data, 2), destination2f); + else if(attribute._format == VertexFormat::Vector2us) + Math::castInto(Containers::arrayCast<2, const UnsignedShort>(attribute._data, 2), destination2f); + else if(attribute._format == VertexFormat::Vector2s) + Math::castInto(Containers::arrayCast<2, const Short>(attribute._data, 2), destination2f); + else if(attribute._format == VertexFormat::Vector2ubNormalized) + Math::unpackInto(Containers::arrayCast<2, const UnsignedByte>(attribute._data, 2), destination2f); + else if(attribute._format == VertexFormat::Vector2bNormalized) + Math::unpackInto(Containers::arrayCast<2, const Byte>(attribute._data, 2), destination2f); + else if(attribute._format == VertexFormat::Vector2usNormalized) + Math::unpackInto(Containers::arrayCast<2, const UnsignedShort>(attribute._data, 2), destination2f); + else if(attribute._format == VertexFormat::Vector2sNormalized) + Math::unpackInto(Containers::arrayCast<2, const Short>(attribute._data, 2), destination2f); else CORRADE_ASSERT_UNREACHABLE(); /* LCOV_EXCL_LINE */ } @@ -445,20 +555,43 @@ void MeshData::colorsInto(const Containers::StridedArrayView1D destinati CORRADE_ASSERT(destination.size() == _vertexCount, "Trade::MeshData::colorsInto(): expected a view with" << _vertexCount << "elements but got" << destination.size(), ); const MeshAttributeData& attribute = _attributes[attributeId]; + const Containers::StridedArrayView2D destination3f = Containers::arrayCast<2, Float>(Containers::arrayCast(destination)); + const Containers::StridedArrayView2D destination4f = Containers::arrayCast<2, Float>(destination); + /* For three-component colors copy the RGB part to the first three - components and then fill the alpha with a single value */ - if(attribute._format == VertexFormat::Vector3) { + components */ + if(attribute._format == VertexFormat::Vector3) Utility::copy(Containers::arrayCast(attribute._data), Containers::arrayCast(destination)); - constexpr Float alpha[1]{1.0f}; - Utility::copy( - Containers::stridedArrayView(alpha).broadcasted<0>(_vertexCount), - Containers::arrayCast<2, Float>(destination).transposed<0, 1>()[3]); + else if(attribute._format == VertexFormat::Vector3h) + Math::unpackHalfInto(Containers::arrayCast<2, const UnsignedShort>(attribute._data, 3), destination3f); + else if(attribute._format == VertexFormat::Vector3ubNormalized) + Math::unpackInto(Containers::arrayCast<2, const UnsignedByte>(attribute._data, 3), destination3f); + else if(attribute._format == VertexFormat::Vector3usNormalized) + Math::unpackInto(Containers::arrayCast<2, const UnsignedShort>(attribute._data, 3), destination3f); + /* Copy four-component colors as-is */ - } else if(attribute._format == VertexFormat::Vector4) { + else if(attribute._format == VertexFormat::Vector4) Utility::copy(Containers::arrayCast(attribute._data), Containers::arrayCast(destination)); - } else CORRADE_ASSERT_UNREACHABLE(); /* LCOV_EXCL_LINE */ + else if(attribute._format == VertexFormat::Vector4h) + Math::unpackHalfInto(Containers::arrayCast<2, const UnsignedShort>(attribute._data, 4), destination4f); + else if(attribute._format == VertexFormat::Vector4ubNormalized) + Math::unpackInto(Containers::arrayCast<2, const UnsignedByte>(attribute._data, 4), destination4f); + else if(attribute._format == VertexFormat::Vector4usNormalized) + Math::unpackInto(Containers::arrayCast<2, const UnsignedShort>(attribute._data, 4), destination4f); + else CORRADE_ASSERT_UNREACHABLE(); /* LCOV_EXCL_LINE */ + + /* For three-component colors finally fill the alpha with a single value */ + if(attribute._format == VertexFormat::Vector3 || + attribute._format == VertexFormat::Vector3h || + attribute._format == VertexFormat::Vector3ubNormalized || + attribute._format == VertexFormat::Vector3usNormalized) { + constexpr Float alpha[1]{1.0f}; + Utility::copy( + Containers::stridedArrayView(alpha).broadcasted<0>(_vertexCount), + destination4f.transposed<0, 1>()[3]); + } } Containers::Array MeshData::colorsAsArray(const UnsignedInt id) const { diff --git a/src/Magnum/Trade/MeshData.h b/src/Magnum/Trade/MeshData.h index 3409e5ddc..468458483 100644 --- a/src/Magnum/Trade/MeshData.h +++ b/src/Magnum/Trade/MeshData.h @@ -55,37 +55,54 @@ enum class MeshAttribute: UnsignedShort { AbstractImporter::meshAttributeForName()) */ /** - * Position. Type is usually @ref Magnum::Vector2 "Vector2" for 2D and - * @ref Magnum::Vector3 "Vector3" for 3D. Corresponds to - * @ref Shaders::Generic::Position. - * @see @ref VertexFormat::Vector2, @ref VertexFormat::Vector3, - * @ref MeshData::positions2DAsArray(), + * Position. Type is usually @ref VertexFormat::Vector2 for 2D and + * @ref VertexFormat::Vector3 for 3D, but can be also any of + * @ref VertexFormat::Vector2h, @ref VertexFormat::Vector3h, + * @ref VertexFormat::Vector2ub, @ref VertexFormat::Vector2ubNormalized, + * @ref VertexFormat::Vector2b, @ref VertexFormat::Vector2bNormalized, + * @ref VertexFormat::Vector2us, @ref VertexFormat::Vector2usNormalized, + * @ref VertexFormat::Vector2s, @ref VertexFormat::Vector2sNormalized, + * @ref VertexFormat::Vector3ub, @ref VertexFormat::Vector3ubNormalized, + * @ref VertexFormat::Vector3b, @ref VertexFormat::Vector3bNormalized, + * @ref VertexFormat::Vector3us, @ref VertexFormat::Vector3usNormalized, + * @ref VertexFormat::Vector3s or @ref VertexFormat::Vector3sNormalized. + * Corresponds to @ref Shaders::Generic::Position. + * @see @ref MeshData::positions2DAsArray(), * @ref MeshData::positions3DAsArray() */ Position = 1, /** - * Normal. Type is usually @ref Magnum::Vector3 "Vector3". Corresponds to + * Normal. Type is usually @ref VertexFormat::Vector3, but can be also + * @ref VertexFormat::Vector3h. @ref VertexFormat::Vector3bNormalized or + * @ref VertexFormat::Vector3sNormalized. Corresponds to * @ref Shaders::Generic::Normal. - * @see @ref VertexFormat::Vector3, @ref MeshData::normalsAsArray() + * @see @ref MeshData::normalsAsArray() */ Normal, /** - * Texture coordinates. Type is usually @ref Magnum::Vector2 "Vector2" for - * 2D coordinates. Corresponds to @ref Shaders::Generic::TextureCoordinates. - * @see @ref VertexFormat::Vector2, - * @ref MeshData::textureCoordinates2DAsArray() + * Texture coordinates. Type is usually @ref VertexFormat::Vector2 for + * 2D coordinates, but can be also any of @ref VertexFormat::Vector2h, + * @ref VertexFormat::Vector2ub, @ref VertexFormat::Vector2ubNormalized, + * @ref VertexFormat::Vector2b, @ref VertexFormat::Vector2bNormalized, + * @ref VertexFormat::Vector2us, @ref VertexFormat::Vector2usNormalized, + * @ref VertexFormat::Vector2s or @ref VertexFormat::Vector2sNormalized. + * Corresponds to @ref Shaders::Generic::TextureCoordinates. + * @see @ref MeshData::textureCoordinates2DAsArray() */ TextureCoordinates, /** - * Vertex color. Type is usually @ref Magnum::Vector3 "Vector3" or - * @ref Magnum::Vector4 "Vector4" (or @ref Color3 / @ref Color4). - * Corresponds to @ref Shaders::Generic::Color3 or - * @ref Shaders::Generic::Color4. - * @see @ref VertexFormat::Vector3, @ref VertexFormat::Vector4, - * @ref MeshData::colorsAsArray() + * Vertex color. Type is usually @ref VertexFormat::Vector3 or + * @ref VertexFormat::Vector4, but can be also any of + * @ref VertexFormat::Vector3h, @ref VertexFormat::Vector4h, + * @ref VertexFormat::Vector3ubNormalized, + * @ref VertexFormat::Vector3usNormalized, + * @ref VertexFormat::Vector4ubNormalized or + * @ref VertexFormat::Vector4usNormalized. Corresponds to + * @ref Shaders::Generic::Color3 or @ref Shaders::Generic::Color4. + * @see @ref MeshData::colorsAsArray() */ Color, @@ -261,6 +278,27 @@ class MAGNUM_TRADE_EXPORT MeshAttributeData { * * Detects @ref VertexFormat based on @p T and calls * @ref MeshAttributeData(MeshAttribute, VertexFormat, const Containers::StridedArrayView1D&). + * For most types known by Magnum, the detected @ref VertexFormat is of + * the same name as the type (so e.g. @ref Magnum::Vector3ui "Vector3ui" + * gets recognized as @ref VertexFormat::Vector3ui), with the + * following exceptions: + * + * - @ref Color3ub is recognized as + * @ref VertexFormat::Vector3ubNormalized + * - @ref Color3us is recognized as + * @ref VertexFormat::Vector3usNormalized + * - @ref Color4ub is recognized as + * @ref VertexFormat::Vector4ubNormalized + * - @ref Color4us is recognized as + * @ref VertexFormat::Vector4usNormalized + * + * This also means that if you have a @ref Magnum::Vector2s "Vector2s", + * for example, and want to pick a + * @ref VertexFormat::Vector2sNormalized instead of the + * (autodetected) @ref VertexFormat::Vector2s, you need to specify + * it explicitly --- there's no way the library can infer this from the + * type alone, except for the color types above (which are generally + * always normalized). */ template constexpr explicit MeshAttributeData(MeshAttribute name, const Containers::StridedArrayView1D& data) noexcept; @@ -1164,6 +1202,7 @@ namespace Implementation { template<> constexpr MeshIndexType meshIndexTypeFor() { return MeshIndexType::UnsignedShort; } template<> constexpr MeshIndexType meshIndexTypeFor() { return MeshIndexType::UnsignedInt; } + /* Implicit mapping from a format to enum (1:1) */ template constexpr VertexFormat vertexFormatFor() { /* C++ why there isn't an obvious way to do such a thing?! */ static_assert(sizeof(T) == 0, "unsupported attribute type"); @@ -1173,6 +1212,8 @@ namespace Implementation { #define _c(format) \ template<> constexpr VertexFormat vertexFormatFor() { return VertexFormat::format; } _c(Float) + _c(Half) + _c(Double) _c(UnsignedByte) _c(Byte) _c(UnsignedShort) @@ -1180,28 +1221,128 @@ namespace Implementation { _c(UnsignedInt) _c(Int) _c(Vector2) + _c(Vector2h) + _c(Vector2d) + _c(Vector2ub) + _c(Vector2b) + _c(Vector2us) + _c(Vector2s) + _c(Vector2ui) + _c(Vector2i) _c(Vector3) + _c(Vector3h) + _c(Vector3d) + _c(Vector3ub) + _c(Vector3b) + _c(Vector3us) + _c(Vector3s) + _c(Vector3ui) + _c(Vector3i) _c(Vector4) + _c(Vector4h) + _c(Vector4d) + _c(Vector4ub) + _c(Vector4b) + _c(Vector4us) + _c(Vector4s) + _c(Vector4ui) + _c(Vector4i) #undef _c #endif template<> constexpr VertexFormat vertexFormatFor() { return VertexFormat::Vector3; } + template<> constexpr VertexFormat vertexFormatFor() { return VertexFormat::Vector3h; } + template<> constexpr VertexFormat vertexFormatFor() { return VertexFormat::Vector3ubNormalized; } + template<> constexpr VertexFormat vertexFormatFor() { return VertexFormat::Vector3usNormalized; } template<> constexpr VertexFormat vertexFormatFor() { return VertexFormat::Vector4; } + template<> constexpr VertexFormat vertexFormatFor() { return VertexFormat::Vector4h; } + template<> constexpr VertexFormat vertexFormatFor() { return VertexFormat::Vector4ubNormalized; } + template<> constexpr VertexFormat vertexFormatFor() { return VertexFormat::Vector4usNormalized; } + + /* Check if enum is compatible with a format (1:n). Mostly just 1:1 mapping + tho, so reusing vertexFormatFor(), with a few exceptions. */ + template constexpr bool isVertexFormatCompatible(VertexFormat type) { + return vertexFormatFor() == type; + } + #ifndef DOXYGEN_GENERATING_OUTPUT + #define _c(format_) \ + template<> constexpr bool isVertexFormatCompatible(VertexFormat format) { \ + return format == VertexFormat::format_ || \ + format == VertexFormat::format_ ## Normalized; \ + } + _c(UnsignedByte) + _c(Byte) + _c(UnsignedShort) + _c(Short) + _c(Vector2ub) + _c(Vector2b) + _c(Vector2us) + _c(Vector2s) + _c(Vector3ub) + _c(Vector3b) + _c(Vector3us) + _c(Vector3s) + _c(Vector4ub) + _c(Vector4b) + _c(Vector4us) + _c(Vector4s) + /* For Color[34]u[sb] we expect the format to be normalized, which is + handled by vertexFormatFor() properly already */ + #undef _c + #endif /* LCOV_EXCL_STOP */ } #endif constexpr MeshAttributeData::MeshAttributeData(const MeshAttribute name, const VertexFormat format, const Containers::StridedArrayView1D& data, std::nullptr_t) noexcept: _name{name}, _format{format}, _data{(CORRADE_CONSTEXPR_ASSERT( + /* Double formats intentionally not supported for any builtin attributes + right now -- only for custom formats */ (name == MeshAttribute::Position && (format == VertexFormat::Vector2 || - format == VertexFormat::Vector3)) || + format == VertexFormat::Vector2h || + format == VertexFormat::Vector2ub || + format == VertexFormat::Vector2ubNormalized || + format == VertexFormat::Vector2b || + format == VertexFormat::Vector2bNormalized || + format == VertexFormat::Vector2us || + format == VertexFormat::Vector2usNormalized || + format == VertexFormat::Vector2s || + format == VertexFormat::Vector2sNormalized || + format == VertexFormat::Vector3 || + format == VertexFormat::Vector3h || + format == VertexFormat::Vector3ub || + format == VertexFormat::Vector3ubNormalized || + format == VertexFormat::Vector3b || + format == VertexFormat::Vector3bNormalized || + format == VertexFormat::Vector3us || + format == VertexFormat::Vector3usNormalized || + format == VertexFormat::Vector3s || + format == VertexFormat::Vector3sNormalized)) || (name == MeshAttribute::Normal && - (format == VertexFormat::Vector3)) || + (format == VertexFormat::Vector3 || + format == VertexFormat::Vector3h || + format == VertexFormat::Vector3bNormalized || + format == VertexFormat::Vector3sNormalized)) || (name == MeshAttribute::Color && (format == VertexFormat::Vector3 || - format == VertexFormat::Vector4)) || + format == VertexFormat::Vector3h || + format == VertexFormat::Vector3ubNormalized || + format == VertexFormat::Vector3usNormalized || + format == VertexFormat::Vector4 || + format == VertexFormat::Vector4h || + format == VertexFormat::Vector4ubNormalized || + format == VertexFormat::Vector4usNormalized)) || (name == MeshAttribute::TextureCoordinates && - (format == VertexFormat::Vector2)) || + (format == VertexFormat::Vector2 || + format == VertexFormat::Vector2h || + format == VertexFormat::Vector2ub || + format == VertexFormat::Vector2ubNormalized || + format == VertexFormat::Vector2b || + format == VertexFormat::Vector2bNormalized || + format == VertexFormat::Vector2us || + format == VertexFormat::Vector2usNormalized || + format == VertexFormat::Vector2s || + format == VertexFormat::Vector2sNormalized)) || isMeshAttributeCustom(name) /* can be any format */, "Trade::MeshAttributeData:" << format << "is not a valid format for" << name), data)} {} @@ -1233,7 +1374,7 @@ template Containers::StridedArrayView1D MeshData::attribute(Un #ifdef CORRADE_GRACEFUL_ASSERT /* Sigh. Brittle. Better idea? */ if(!data.stride()[1]) return {}; #endif - CORRADE_ASSERT(Implementation::vertexFormatFor() == _attributes[id]._format, + CORRADE_ASSERT(Implementation::isVertexFormatCompatible(_attributes[id]._format), "Trade::MeshData::attribute(): improper type requested for" << _attributes[id]._name << "of format" << _attributes[id]._format, nullptr); return Containers::arrayCast<1, const T>(data); } @@ -1243,7 +1384,7 @@ template Containers::StridedArrayView1D MeshData::mutableAttribute(U #ifdef CORRADE_GRACEFUL_ASSERT /* Sigh. Brittle. Better idea? */ if(!data.stride()[1]) return {}; #endif - CORRADE_ASSERT(Implementation::vertexFormatFor() == _attributes[id]._format, + CORRADE_ASSERT(Implementation::isVertexFormatCompatible(_attributes[id]._format), "Trade::MeshData::mutableAttribute(): improper type requested for" << _attributes[id]._name << "of format" << _attributes[id]._format, nullptr); return Containers::arrayCast<1, T>(data); } @@ -1256,7 +1397,7 @@ template Containers::StridedArrayView1D MeshData::attribute(Me #ifndef CORRADE_NO_ASSERT const UnsignedInt attributeId = attributeFor(name, id); #endif - CORRADE_ASSERT(Implementation::vertexFormatFor() == _attributes[attributeId]._format, + CORRADE_ASSERT(Implementation::isVertexFormatCompatible(_attributes[attributeId]._format), "Trade::MeshData::attribute(): improper type requested for" << _attributes[attributeId]._name << "of format" << _attributes[attributeId]._format, nullptr); return Containers::arrayCast<1, const T>(data); } @@ -1269,8 +1410,8 @@ template Containers::StridedArrayView1D MeshData::mutableAttribute(M #ifndef CORRADE_NO_ASSERT const UnsignedInt attributeId = attributeFor(name, id); #endif - CORRADE_ASSERT(Implementation::vertexFormatFor() == _attributes[attributeId]._format, - "Trade::MeshData::mutableAttribute(): improper type requested for" << _attributes[attributeId]._name << "of type" << _attributes[attributeId]._format, nullptr); + CORRADE_ASSERT(Implementation::isVertexFormatCompatible(_attributes[attributeId]._format), + "Trade::MeshData::mutableAttribute(): improper type requested for" << _attributes[attributeId]._name << "of format" << _attributes[attributeId]._format, nullptr); return Containers::arrayCast<1, T>(data); } diff --git a/src/Magnum/Trade/Test/MeshDataTest.cpp b/src/Magnum/Trade/Test/MeshDataTest.cpp index 8d42f8bb6..cde87476e 100644 --- a/src/Magnum/Trade/Test/MeshDataTest.cpp +++ b/src/Magnum/Trade/Test/MeshDataTest.cpp @@ -29,6 +29,7 @@ #include #include "Magnum/Math/Color.h" +#include "Magnum/Math/Half.h" #include "Magnum/Trade/MeshData.h" namespace Magnum { namespace Trade { namespace Test { namespace { @@ -96,14 +97,28 @@ struct MeshDataTest: TestSuite::Tester { template void indicesAsArray(); void indicesIntoArrayInvalidSize(); template void positions2DAsArray(); + template void positions2DAsArrayPackedUnsigned(); + template void positions2DAsArrayPackedSigned(); + template void positions2DAsArrayPackedUnsignedNormalized(); + template void positions2DAsArrayPackedSignedNormalized(); void positions2DIntoArrayInvalidSize(); template void positions3DAsArray(); + template void positions3DAsArrayPackedUnsigned(); + template void positions3DAsArrayPackedSigned(); + template void positions3DAsArrayPackedUnsignedNormalized(); + template void positions3DAsArrayPackedSignedNormalized(); void positions3DIntoArrayInvalidSize(); template void normalsAsArray(); + template void normalsAsArrayPackedSignedNormalized(); void normalsIntoArrayInvalidSize(); template void textureCoordinates2DAsArray(); + template void textureCoordinates2DAsArrayPackedUnsigned(); + template void textureCoordinates2DAsArrayPackedSigned(); + template void textureCoordinates2DAsArrayPackedUnsignedNormalized(); + template void textureCoordinates2DAsArrayPackedSignedNormalized(); void textureCoordinates2DIntoArrayInvalidSize(); template void colorsAsArray(); + template void colorsAsArrayPackedUnsignedNormalized(); void colorsIntoArrayInvalidSize(); void mutableAccessNotAllowed(); @@ -202,17 +217,71 @@ MeshDataTest::MeshDataTest() { &MeshDataTest::indicesAsArray, &MeshDataTest::indicesIntoArrayInvalidSize, &MeshDataTest::positions2DAsArray, + &MeshDataTest::positions2DAsArray, &MeshDataTest::positions2DAsArray, + &MeshDataTest::positions2DAsArray, + &MeshDataTest::positions2DAsArrayPackedUnsigned, + &MeshDataTest::positions2DAsArrayPackedUnsigned, + &MeshDataTest::positions2DAsArrayPackedUnsigned, + &MeshDataTest::positions2DAsArrayPackedUnsigned, + &MeshDataTest::positions2DAsArrayPackedSigned, + &MeshDataTest::positions2DAsArrayPackedSigned, + &MeshDataTest::positions2DAsArrayPackedSigned, + &MeshDataTest::positions2DAsArrayPackedSigned, + &MeshDataTest::positions2DAsArrayPackedUnsignedNormalized, + &MeshDataTest::positions2DAsArrayPackedUnsignedNormalized, + &MeshDataTest::positions2DAsArrayPackedUnsignedNormalized, + &MeshDataTest::positions2DAsArrayPackedUnsignedNormalized, + &MeshDataTest::positions2DAsArrayPackedSignedNormalized, + &MeshDataTest::positions2DAsArrayPackedSignedNormalized, + &MeshDataTest::positions2DAsArrayPackedSignedNormalized, + &MeshDataTest::positions2DAsArrayPackedSignedNormalized, &MeshDataTest::positions2DIntoArrayInvalidSize, &MeshDataTest::positions3DAsArray, + &MeshDataTest::positions3DAsArray, &MeshDataTest::positions3DAsArray, + &MeshDataTest::positions3DAsArray, + &MeshDataTest::positions3DAsArrayPackedUnsigned, + &MeshDataTest::positions3DAsArrayPackedUnsigned, + &MeshDataTest::positions3DAsArrayPackedUnsigned, + &MeshDataTest::positions3DAsArrayPackedUnsigned, + &MeshDataTest::positions3DAsArrayPackedSigned, + &MeshDataTest::positions3DAsArrayPackedSigned, + &MeshDataTest::positions3DAsArrayPackedSigned, + &MeshDataTest::positions3DAsArrayPackedSigned, + &MeshDataTest::positions3DAsArrayPackedUnsignedNormalized, + &MeshDataTest::positions3DAsArrayPackedUnsignedNormalized, + &MeshDataTest::positions3DAsArrayPackedUnsignedNormalized, + &MeshDataTest::positions3DAsArrayPackedUnsignedNormalized, + &MeshDataTest::positions3DAsArrayPackedSignedNormalized, + &MeshDataTest::positions3DAsArrayPackedSignedNormalized, + &MeshDataTest::positions3DAsArrayPackedSignedNormalized, + &MeshDataTest::positions3DAsArrayPackedSignedNormalized, &MeshDataTest::positions3DIntoArrayInvalidSize, &MeshDataTest::normalsAsArray, + &MeshDataTest::normalsAsArray, + &MeshDataTest::normalsAsArrayPackedSignedNormalized, + &MeshDataTest::normalsAsArrayPackedSignedNormalized, &MeshDataTest::normalsIntoArrayInvalidSize, &MeshDataTest::textureCoordinates2DAsArray, + &MeshDataTest::textureCoordinates2DAsArray, + &MeshDataTest::textureCoordinates2DAsArrayPackedUnsigned, + &MeshDataTest::textureCoordinates2DAsArrayPackedUnsigned, + &MeshDataTest::textureCoordinates2DAsArrayPackedSigned, + &MeshDataTest::textureCoordinates2DAsArrayPackedSigned, + &MeshDataTest::textureCoordinates2DAsArrayPackedUnsignedNormalized, + &MeshDataTest::textureCoordinates2DAsArrayPackedUnsignedNormalized, + &MeshDataTest::textureCoordinates2DAsArrayPackedSignedNormalized, + &MeshDataTest::textureCoordinates2DAsArrayPackedSignedNormalized, &MeshDataTest::textureCoordinates2DIntoArrayInvalidSize, &MeshDataTest::colorsAsArray, + &MeshDataTest::colorsAsArray, &MeshDataTest::colorsAsArray, + &MeshDataTest::colorsAsArray, + &MeshDataTest::colorsAsArrayPackedUnsignedNormalized, + &MeshDataTest::colorsAsArrayPackedUnsignedNormalized, + &MeshDataTest::colorsAsArrayPackedUnsignedNormalized, + &MeshDataTest::colorsAsArrayPackedUnsignedNormalized, &MeshDataTest::colorsIntoArrayInvalidSize, &MeshDataTest::mutableAccessNotAllowed, @@ -1290,9 +1359,25 @@ template struct NameTraits; static const char* name() { return #format; } \ }; _c(Vector2) +_c(Vector2h) +_c(Vector2ub) +_c(Vector2b) +_c(Vector2us) +_c(Vector2s) _c(Vector3) +_c(Vector3h) +_c(Vector3ub) +_c(Vector3b) +_c(Vector3us) +_c(Vector3s) _c(Color3) +_c(Color3h) +_c(Color3ub) +_c(Color3us) _c(Color4) +_c(Color4h) +_c(Color4ub) +_c(Color4us) #undef _c template void MeshDataTest::indicesAsArray() { @@ -1324,12 +1409,13 @@ void MeshDataTest::indicesIntoArrayInvalidSize() { template void MeshDataTest::positions2DAsArray() { setTestCaseTemplateName(NameTraits::name()); + typedef typename T::Type U; Containers::Array vertexData{3*sizeof(T)}; auto positionsView = Containers::arrayCast(vertexData); - positionsView[0] = T::pad(Vector2{2.0f, 1.0f}); - positionsView[1] = T::pad(Vector2{0.0f, -1.0f}); - positionsView[2] = T::pad(Vector2{-2.0f, 3.0f}); + positionsView[0] = T::pad(Math::Vector2{U(2.0f), U(1.0f)}); + positionsView[1] = T::pad(Math::Vector2{U(0.0f), U(-1.0f)}); + positionsView[2] = T::pad(Math::Vector2{U(-2.0f), U(3.0f)}); MeshData data{MeshPrimitive::Points, std::move(vertexData), {MeshAttributeData{MeshAttribute::Position, positionsView}}}; CORRADE_COMPARE_AS(data.positions2DAsArray(), @@ -1337,6 +1423,70 @@ template void MeshDataTest::positions2DAsArray() { TestSuite::Compare::Container); } +template void MeshDataTest::positions2DAsArrayPackedUnsigned() { + setTestCaseTemplateName(NameTraits::name()); + + Containers::Array vertexData{3*sizeof(T)}; + auto positionsView = Containers::arrayCast(vertexData); + positionsView[0] = T::pad(Math::Vector2{2, 1}); + positionsView[1] = T::pad(Math::Vector2{0, 15}); + positionsView[2] = T::pad(Math::Vector2{22, 3}); + + MeshData data{MeshPrimitive::Points, std::move(vertexData), {MeshAttributeData{MeshAttribute::Position, positionsView}}}; + CORRADE_COMPARE_AS(data.positions2DAsArray(), + Containers::arrayView({{2.0f, 1.0f}, {0.0f, 15.0f}, {22.0f, 3.0f}}), + TestSuite::Compare::Container); +} + +template void MeshDataTest::positions2DAsArrayPackedSigned() { + setTestCaseTemplateName(NameTraits::name()); + + Containers::Array vertexData{3*sizeof(T)}; + auto positionsView = Containers::arrayCast(vertexData); + positionsView[0] = T::pad(Math::Vector2{2, 1}); + positionsView[1] = T::pad(Math::Vector2{0, -15}); + positionsView[2] = T::pad(Math::Vector2{-22, 3}); + + MeshData data{MeshPrimitive::Points, std::move(vertexData), {MeshAttributeData{MeshAttribute::Position, positionsView}}}; + CORRADE_COMPARE_AS(data.positions2DAsArray(), + Containers::arrayView({{2.0f, 1.0f}, {0.0f, -15.0f}, {-22.0f, 3.0f}}), + TestSuite::Compare::Container); +} + +template void MeshDataTest::positions2DAsArrayPackedUnsignedNormalized() { + setTestCaseTemplateName(NameTraits::name()); + + Containers::Array vertexData{2*sizeof(T)}; + auto positionsView = Containers::arrayCast(vertexData); + positionsView[0] = T::pad(Math::Vector2{Math::pack(1.0f), 0}); + positionsView[1] = T::pad(Math::Vector2{0, Math::pack(1.0f)}); + + MeshData data{MeshPrimitive::Points, std::move(vertexData), {MeshAttributeData{MeshAttribute::Position, + /* Assuming the normalized enum is always after the non-normalized */ + VertexFormat(UnsignedInt(Implementation::vertexFormatFor()) + 1), + positionsView}}}; + CORRADE_COMPARE_AS(data.positions2DAsArray(), + Containers::arrayView({{1.0f, 0.0f}, {0.0f, 1.0f}}), + TestSuite::Compare::Container); +} + +template void MeshDataTest::positions2DAsArrayPackedSignedNormalized() { + setTestCaseTemplateName(NameTraits::name()); + + Containers::Array vertexData{2*sizeof(T)}; + auto positionsView = Containers::arrayCast(vertexData); + positionsView[0] = T::pad(Math::Vector2{Math::pack(1.0f), 0}); + positionsView[1] = T::pad(Math::Vector2{0, Math::pack(-1.0f)}); + + MeshData data{MeshPrimitive::Points, std::move(vertexData), {MeshAttributeData{MeshAttribute::Position, + /* Assuming the normalized enum is always after the non-normalized */ + VertexFormat(UnsignedInt(Implementation::vertexFormatFor()) + 1), + positionsView}}}; + CORRADE_COMPARE_AS(data.positions2DAsArray(), + Containers::arrayView({{1.0f, 0.0f}, {0.0f, -1.0f}}), + TestSuite::Compare::Container); +} + void MeshDataTest::positions2DIntoArrayInvalidSize() { Containers::Array vertexData{3*sizeof(Vector2)}; MeshData data{MeshPrimitive::Points, std::move(vertexData), {MeshAttributeData{MeshAttribute::Position, Containers::arrayCast(vertexData)}}}; @@ -1351,18 +1501,91 @@ void MeshDataTest::positions2DIntoArrayInvalidSize() { template void MeshDataTest::positions3DAsArray() { setTestCaseTemplateName(NameTraits::name()); + typedef typename T::Type U; + + Containers::Array vertexData{3*sizeof(T)}; + auto positionsView = Containers::arrayCast(vertexData); + /* Needs to be sufficiently representable to have the test work also for + half floats */ + positionsView[0] = T::pad(Math::Vector3{U(2.0f), U(1.0f), U(0.75f)}); + positionsView[1] = T::pad(Math::Vector3{U(0.0f), U(-1.0f), U(1.25f)}); + positionsView[2] = T::pad(Math::Vector3{U(-2.0f), U(3.0f), U(2.5f)}); + + MeshData data{MeshPrimitive::Points, std::move(vertexData), {MeshAttributeData{MeshAttribute::Position, positionsView}}}; + CORRADE_COMPARE_AS(data.positions3DAsArray(), Containers::arrayView({ + Vector3::pad(Math::Vector::pad(Vector3{2.0f, 1.0f, 0.75f})), + Vector3::pad(Math::Vector::pad(Vector3{0.0f, -1.0f, 1.25f})), + Vector3::pad(Math::Vector::pad(Vector3{-2.0f, 3.0f, 2.5f})) + }), TestSuite::Compare::Container); +} + +template void MeshDataTest::positions3DAsArrayPackedUnsigned() { + setTestCaseTemplateName(NameTraits::name()); Containers::Array vertexData{3*sizeof(T)}; auto positionsView = Containers::arrayCast(vertexData); - positionsView[0] = T::pad(Vector3{2.0f, 1.0f, 0.3f}); - positionsView[1] = T::pad(Vector3{0.0f, -1.0f, 1.1f}); - positionsView[2] = T::pad(Vector3{-2.0f, 3.0f, 2.2f}); + positionsView[0] = T::pad(Math::Vector3{2, 1, 135}); + positionsView[1] = T::pad(Math::Vector3{0, 15, 2}); + positionsView[2] = T::pad(Math::Vector3{22, 3, 192}); MeshData data{MeshPrimitive::Points, std::move(vertexData), {MeshAttributeData{MeshAttribute::Position, positionsView}}}; CORRADE_COMPARE_AS(data.positions3DAsArray(), Containers::arrayView({ - Vector3::pad(T::pad(Vector3{2.0f, 1.0f, 0.3f})), - Vector3::pad(T::pad(Vector3{0.0f, -1.0f, 1.1f})), - Vector3::pad(T::pad(Vector3{-2.0f, 3.0f, 2.2f})) + Vector3::pad(Math::Vector::pad(Vector3{2.0f, 1.0f, 135.0f})), + Vector3::pad(Math::Vector::pad(Vector3{0.0f, 15.0f, 2.0f})), + Vector3::pad(Math::Vector::pad(Vector3{22.0f, 3.0f, 192.0f})) + }), TestSuite::Compare::Container); +} + +template void MeshDataTest::positions3DAsArrayPackedSigned() { + setTestCaseTemplateName(NameTraits::name()); + + Containers::Array vertexData{3*sizeof(T)}; + auto positionsView = Containers::arrayCast(vertexData); + positionsView[0] = T::pad(Math::Vector3{2, 1, -117}); + positionsView[1] = T::pad(Math::Vector3{0, -15, 2}); + positionsView[2] = T::pad(Math::Vector3{-22, 3, 86}); + + MeshData data{MeshPrimitive::Points, std::move(vertexData), {MeshAttributeData{MeshAttribute::Position, positionsView}}}; + CORRADE_COMPARE_AS(data.positions3DAsArray(), Containers::arrayView({ + Vector3::pad(Math::Vector::pad(Vector3{2.0f, 1.0f, -117.0f})), + Vector3::pad(Math::Vector::pad(Vector3{0.0f, -15.0f, 2.0f})), + Vector3::pad(Math::Vector::pad(Vector3{-22.0f, 3.0f, 86.0f})) + }), TestSuite::Compare::Container); +} + +template void MeshDataTest::positions3DAsArrayPackedUnsignedNormalized() { + setTestCaseTemplateName(NameTraits::name()); + + Containers::Array vertexData{2*sizeof(T)}; + auto positionsView = Containers::arrayCast(vertexData); + positionsView[0] = T::pad(Math::Vector3{Math::pack(1.0f), 0, Math::pack(1.0f)}); + positionsView[1] = T::pad(Math::Vector3{0, Math::pack(1.0f), 0}); + + MeshData data{MeshPrimitive::Points, std::move(vertexData), {MeshAttributeData{MeshAttribute::Position, + /* Assuming the normalized enum is always after the non-normalized */ + VertexFormat(UnsignedInt(Implementation::vertexFormatFor()) + 1), + positionsView}}}; + CORRADE_COMPARE_AS(data.positions3DAsArray(), Containers::arrayView({ + Vector3::pad(Math::Vector::pad(Vector3{1.0f, 0.0f, 1.0f})), + Vector3::pad(Math::Vector::pad(Vector3{0.0f, 1.0f, 0.0f})) + }), TestSuite::Compare::Container); +} + +template void MeshDataTest::positions3DAsArrayPackedSignedNormalized() { + setTestCaseTemplateName(NameTraits::name()); + + Containers::Array vertexData{2*sizeof(T)}; + auto positionsView = Containers::arrayCast(vertexData); + positionsView[0] = T::pad(Math::Vector3{Math::pack(1.0f), 0, Math::pack(1.0f)}); + positionsView[1] = T::pad(Math::Vector3{0, Math::pack(-1.0f), 0}); + + MeshData data{MeshPrimitive::Points, std::move(vertexData), {MeshAttributeData{MeshAttribute::Position, + /* Assuming the normalized enum is always after the non-normalized */ + VertexFormat(UnsignedInt(Implementation::vertexFormatFor()) + 1), + positionsView}}}; + CORRADE_COMPARE_AS(data.positions3DAsArray(), Containers::arrayView({ + Vector3::pad(Math::Vector::pad(Vector3{1.0f, 0.0f, 1.0f})), + Vector3::pad(Math::Vector::pad(Vector3{0.0f, -1.0f, 0.0f})) }), TestSuite::Compare::Container); } @@ -1380,16 +1603,36 @@ void MeshDataTest::positions3DIntoArrayInvalidSize() { template void MeshDataTest::normalsAsArray() { setTestCaseTemplateName(NameTraits::name()); + typedef typename T::Type U; Containers::Array vertexData{3*sizeof(T)}; auto normalsView = Containers::arrayCast(vertexData); - normalsView[0] = {2.0f, 1.0f, 0.3f}; - normalsView[1] = {0.0f, -1.0f, 1.1f}; - normalsView[2] = {-2.0f, 3.0f, 2.2f}; + /* Needs to be sufficiently representable to have the test work also for + half floats */ + normalsView[0] = {U(2.0f), U(1.0f), U(0.75f)}; + normalsView[1] = {U(0.0f), U(-1.0f), U(1.25f)}; + normalsView[2] = {U(-2.0f), U(3.0f), U(2.5f)}; MeshData data{MeshPrimitive::Points, std::move(vertexData), {MeshAttributeData{MeshAttribute::Normal, normalsView}}}; CORRADE_COMPARE_AS(data.normalsAsArray(), Containers::arrayView({ - {2.0f, 1.0f, 0.3f}, {0.0f, -1.0f, 1.1f}, {-2.0f, 3.0f, 2.2f}, + {2.0f, 1.0f, 0.75f}, {0.0f, -1.0f, 1.25f}, {-2.0f, 3.0f, 2.5f}, + }), TestSuite::Compare::Container); +} + +template void MeshDataTest::normalsAsArrayPackedSignedNormalized() { + setTestCaseTemplateName(NameTraits::name()); + + Containers::Array vertexData{2*sizeof(T)}; + auto normalsView = Containers::arrayCast(vertexData); + normalsView[0] = {Math::pack(1.0f), 0, Math::pack(1.0f)}; + normalsView[1] = {0, Math::pack(-1.0f), 0}; + + MeshData data{MeshPrimitive::Points, std::move(vertexData), {MeshAttributeData{MeshAttribute::Normal, + /* Assuming the normalized enum is always after the non-normalized */ + VertexFormat(UnsignedInt(Implementation::vertexFormatFor()) + 1), + normalsView}}}; + CORRADE_COMPARE_AS(data.normalsAsArray(), Containers::arrayView({ + {1.0f, 0.0f, 1.0f}, {0.0f, -1.0f, 0.0f} }), TestSuite::Compare::Container); } @@ -1407,12 +1650,13 @@ void MeshDataTest::normalsIntoArrayInvalidSize() { template void MeshDataTest::textureCoordinates2DAsArray() { setTestCaseTemplateName(NameTraits::name()); + typedef typename T::Type U; Containers::Array vertexData{3*sizeof(T)}; auto textureCoordinatesView = Containers::arrayCast(vertexData); - textureCoordinatesView[0] = {2.0f, 1.0f}; - textureCoordinatesView[1] = {0.0f, -1.0f}; - textureCoordinatesView[2] = {-2.0f, 3.0f}; + textureCoordinatesView[0] = {U(2.0f), U(1.0f)}; + textureCoordinatesView[1] = {U(0.0f), U(-1.0f)}; + textureCoordinatesView[2] = {U(-2.0f), U(3.0f)}; MeshData data{MeshPrimitive::Points, std::move(vertexData), {MeshAttributeData{MeshAttribute::TextureCoordinates, textureCoordinatesView}}}; CORRADE_COMPARE_AS(data.textureCoordinates2DAsArray(), Containers::arrayView({ @@ -1420,6 +1664,70 @@ template void MeshDataTest::textureCoordinates2DAsArray() { }), TestSuite::Compare::Container); } +template void MeshDataTest::textureCoordinates2DAsArrayPackedUnsigned() { + setTestCaseTemplateName(NameTraits::name()); + + Containers::Array vertexData{3*sizeof(T)}; + auto textureCoordinatesView = Containers::arrayCast(vertexData); + textureCoordinatesView[0] = {2, 1}; + textureCoordinatesView[1] = {0, 15}; + textureCoordinatesView[2] = {22, 3}; + + MeshData data{MeshPrimitive::Points, std::move(vertexData), {MeshAttributeData{MeshAttribute::TextureCoordinates, textureCoordinatesView}}}; + CORRADE_COMPARE_AS(data.textureCoordinates2DAsArray(), + Containers::arrayView({{2.0f, 1.0f}, {0.0f, 15.0f}, {22.0f, 3.0f}}), + TestSuite::Compare::Container); +} + +template void MeshDataTest::textureCoordinates2DAsArrayPackedSigned() { + setTestCaseTemplateName(NameTraits::name()); + + Containers::Array vertexData{3*sizeof(T)}; + auto textureCoordinatesView = Containers::arrayCast(vertexData); + textureCoordinatesView[0] = {2, 1}; + textureCoordinatesView[1] = {0, -15}; + textureCoordinatesView[2] = {-22, 3}; + + MeshData data{MeshPrimitive::Points, std::move(vertexData), {MeshAttributeData{MeshAttribute::TextureCoordinates, textureCoordinatesView}}}; + CORRADE_COMPARE_AS(data.textureCoordinates2DAsArray(), + Containers::arrayView({{2.0f, 1.0f}, {0.0f, -15.0f}, {-22.0f, 3.0f}}), + TestSuite::Compare::Container); +} + +template void MeshDataTest::textureCoordinates2DAsArrayPackedUnsignedNormalized() { + setTestCaseTemplateName(NameTraits::name()); + + Containers::Array vertexData{2*sizeof(T)}; + auto textureCoordinatesView = Containers::arrayCast(vertexData); + textureCoordinatesView[0] = {Math::pack(1.0f), 0}; + textureCoordinatesView[1] = {0, Math::pack(1.0f)}; + + MeshData data{MeshPrimitive::Points, std::move(vertexData), {MeshAttributeData{MeshAttribute::TextureCoordinates, + /* Assuming the normalized enum is always after the non-normalized */ + VertexFormat(UnsignedInt(Implementation::vertexFormatFor()) + 1), + textureCoordinatesView}}}; + CORRADE_COMPARE_AS(data.textureCoordinates2DAsArray(), + Containers::arrayView({{1.0f, 0.0f}, {0.0f, 1.0f}}), + TestSuite::Compare::Container); +} + +template void MeshDataTest::textureCoordinates2DAsArrayPackedSignedNormalized() { + setTestCaseTemplateName(NameTraits::name()); + + Containers::Array vertexData{2*sizeof(T)}; + auto textureCoordinatesView = Containers::arrayCast(vertexData); + textureCoordinatesView[0] = {Math::pack(1.0f), 0}; + textureCoordinatesView[1] = {0, Math::pack(-1.0f)}; + + MeshData data{MeshPrimitive::Points, std::move(vertexData), {MeshAttributeData{MeshAttribute::TextureCoordinates, + /* Assuming the normalized enum is always after the non-normalized */ + VertexFormat(UnsignedInt(Implementation::vertexFormatFor()) + 1), + textureCoordinatesView}}}; + CORRADE_COMPARE_AS(data.textureCoordinates2DAsArray(), + Containers::arrayView({{1.0f, 0.0f}, {0.0f, -1.0f}}), + TestSuite::Compare::Container); +} + void MeshDataTest::textureCoordinates2DIntoArrayInvalidSize() { Containers::Array vertexData{3*sizeof(Vector2)}; MeshData data{MeshPrimitive::Points, std::move(vertexData), {MeshAttributeData{MeshAttribute::TextureCoordinates, Containers::arrayCast(vertexData)}}}; @@ -1434,16 +1742,34 @@ void MeshDataTest::textureCoordinates2DIntoArrayInvalidSize() { template void MeshDataTest::colorsAsArray() { setTestCaseTemplateName(NameTraits::name()); + typedef typename T::Type U; Containers::Array vertexData{3*sizeof(T)}; auto colorsView = Containers::arrayCast(vertexData); - colorsView[0] = 0xff3366_rgbf; - colorsView[1] = 0x99aacc_rgbf; - colorsView[2] = 0x3377ff_rgbf; + /* Can't use e.g. 0xff3366_rgbf because that's not representable in + half-floats */ + colorsView[0] = {U(2.0f), U(1.0f), U(0.75f)}; + colorsView[1] = {U(0.0f), U(-1.0f), U(1.25f)}; + colorsView[2] = {U(-2.0f), U(3.0f), U(2.5f)}; + + MeshData data{MeshPrimitive::Points, std::move(vertexData), {MeshAttributeData{MeshAttribute::Color, colorsView}}}; + CORRADE_COMPARE_AS(data.colorsAsArray(), Containers::arrayView({ + {2.0f, 1.0f, 0.75f}, {0.0f, -1.0f, 1.25f}, {-2.0f, 3.0f, 2.5f}, + }), TestSuite::Compare::Container); +} + +template void MeshDataTest::colorsAsArrayPackedUnsignedNormalized() { + setTestCaseTemplateName(NameTraits::name()); + + Containers::Array vertexData{2*sizeof(T)}; + auto colorsView = Containers::arrayCast(vertexData); + colorsView[0] = T::pad(Math::Color4{Math::pack(1.0f), 0, Math::pack(1.0f), 0}); + colorsView[1] = T::pad(Math::Color4{0, Math::pack(1.0f), 0, Math::pack(1.0f)}); MeshData data{MeshPrimitive::Points, std::move(vertexData), {MeshAttributeData{MeshAttribute::Color, colorsView}}}; CORRADE_COMPARE_AS(data.colorsAsArray(), Containers::arrayView({ - 0xff3366_rgbf, 0x99aacc_rgbf, 0x3377ff_rgbf + Color4::pad(Math::Vector::pad(Vector4{1.0f, 0.0f, 1.0f, 0.0f}), 1.0f), + Color4::pad(Math::Vector::pad(Vector4{0.0f, 1.0f, 0.0f, 1.0f}), 1.0f) }), TestSuite::Compare::Container); }