From bfdf640c9d5ce58096685eeed37892c07a1cc6db Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Vladim=C3=ADr=20Vondru=C5=A1?= Date: Fri, 21 Jan 2022 16:36:47 +0100 Subject: [PATCH] MeshTools: make transform*() aware of impl-spec index types. This ended up being far more complicated than anticipated -- the utils now make use of filterExceptAttributes() in order to properly preserve the index type when passing a MeshData through. Before an implementation-specific index type was lost in the process due to passing just the view to MeshIndexData constructor and not also the type, and because keeping the metadata is a bit involved process, this had to wait until there's an alternative to reference() that allows me to drop all attribute information. And that's what filterExceptAttributes() does. Then, interleave() implicitly doesn't preserve strided index buffers because GPUs don't like them, and since implementation-specific index types always result in strided index buffers, this was then immediately dying inside interleave() as it's not possible to tightly-pack an index buffer of an unknown type. Thus, the function now has to expose InterleaveFlags and propagate them to interleave() in order to allow the user to preserve the index buffer without repacking it. --- src/Magnum/MeshTools/Test/TransformTest.cpp | 77 ++++++++++++++++++++- src/Magnum/MeshTools/Transform.cpp | 34 ++++----- src/Magnum/MeshTools/Transform.h | 63 +++++++++-------- 3 files changed, 124 insertions(+), 50 deletions(-) diff --git a/src/Magnum/MeshTools/Test/TransformTest.cpp b/src/Magnum/MeshTools/Test/TransformTest.cpp index 6b0571e3c..680e4be6d 100644 --- a/src/Magnum/MeshTools/Test/TransformTest.cpp +++ b/src/Magnum/MeshTools/Test/TransformTest.cpp @@ -53,6 +53,7 @@ struct TransformTest: TestSuite::Tester { template void meshData2D(); void meshData2DNoPosition(); void meshData2DNot2D(); + void meshData2DImplementationSpecificIndexType(); void meshData2DImplementationSpecificVertexFormat(); void meshData2DRvaluePassthrough(); void meshData2DRvaluePassthroughIndexDataNotOwned(); @@ -68,6 +69,7 @@ struct TransformTest: TestSuite::Tester { template void meshData3D(); void meshData3DNoPosition(); void meshData3DNot3D(); + void meshData3DImplementationSpecificIndexType(); void meshData3DImplementationSpecificVertexFormat(); void meshData3DRvaluePassthrough(); void meshData3DRvaluePassthroughIndexDataNotOwned(); @@ -82,6 +84,7 @@ struct TransformTest: TestSuite::Tester { template void meshDataTextureCoordinates2D(); void meshDataTextureCoordinates2DNoCoordinates(); + void meshDataTextureCoordinates2DImplementationSpecificIndexType(); void meshDataTextureCoordinates2DImplementationSpecificVertexFormat(); void meshDataTextureCoordinates2DRvaluePassthrough(); void meshDataTextureCoordinates2DRvaluePassthroughIndexDataNotOwned(); @@ -286,6 +289,7 @@ TransformTest::TransformTest() { addTests({&TransformTest::meshData2DNoPosition, &TransformTest::meshData2DNot2D, + &TransformTest::meshData2DImplementationSpecificIndexType, &TransformTest::meshData2DImplementationSpecificVertexFormat}); addInstancedTests({&TransformTest::meshData2DRvaluePassthrough}, @@ -307,7 +311,8 @@ TransformTest::TransformTest() { }, Containers::arraySize(MeshData3DData)); addTests({&TransformTest::meshData3DNoPosition, - &TransformTest::meshData3DNot3D}); + &TransformTest::meshData3DNot3D, + &TransformTest::meshData3DImplementationSpecificIndexType}); addInstancedTests({&TransformTest::meshData3DImplementationSpecificVertexFormat}, Containers::arraySize(MeshData3DIMplementationSpecificVertexFormatData)); @@ -334,6 +339,7 @@ TransformTest::TransformTest() { }, Containers::arraySize(MeshDataTextureCoordinatesData)); addTests({&TransformTest::meshDataTextureCoordinates2DNoCoordinates, + &TransformTest::meshDataTextureCoordinates2DImplementationSpecificIndexType, &TransformTest::meshDataTextureCoordinates2DImplementationSpecificVertexFormat}); addInstancedTests({&TransformTest::meshDataTextureCoordinates2DRvaluePassthrough}, @@ -505,6 +511,29 @@ void TransformTest::meshData2DNot2D() { CORRADE_COMPARE(out.str(), "MeshTools::transform2D(): expected 2D positions but got VertexFormat::Vector3\n"); } +void TransformTest::meshData2DImplementationSpecificIndexType() { + const UnsignedShort indices[]{3, 1, 2, 0, 2}; + const Vector2 vertices[]{ + {0.0f, 0.0f}, + {1.0f, 0.0f}, + {0.0f, 2.0f} + }; + Trade::MeshData mesh{MeshPrimitive::Points, + {}, indices, Trade::MeshIndexData{meshIndexTypeWrap(0xcaca), Containers::stridedArrayView(indices).slice(1, 4)}, + {}, vertices, {Trade::MeshAttributeData{Trade::MeshAttribute::Position, Containers::arrayView(vertices)}}}; + + /* Just verify the index data get passed through with no information loss */ + Trade::MeshData out = transform2D(mesh, Matrix3{}, 0, InterleaveFlag::PreserveStridedIndices); + CORRADE_COMPARE(out.primitive(), MeshPrimitive::Points); + CORRADE_COMPARE(out.indexType(), meshIndexTypeWrap(0xcaca)); + CORRADE_COMPARE(out.indexOffset(), 2); + CORRADE_COMPARE(out.indexStride(), 2); + CORRADE_COMPARE(out.indexCount(), 3); + CORRADE_COMPARE_AS((Containers::arrayCast<1, const UnsignedShort>(out.indices())), + Containers::arrayView({1, 2, 0}), + TestSuite::Compare::Container); +} + void TransformTest::meshData2DImplementationSpecificVertexFormat() { #ifdef CORRADE_NO_ASSERT CORRADE_SKIP("CORRADE_NO_ASSERT defined, can't test assertions"); @@ -921,6 +950,29 @@ void TransformTest::meshData3DNot3D() { CORRADE_COMPARE(out.str(), "MeshTools::transform3D(): expected 3D positions but got VertexFormat::Vector2\n"); } +void TransformTest::meshData3DImplementationSpecificIndexType() { + const UnsignedShort indices[]{3, 1, 2, 0, 2}; + const Vector3 vertices[]{ + {0.0f, 0.0f, -1.0f}, + {1.0f, 0.0f, -2.0f}, + {0.0f, 2.0f, -1.0f} + }; + Trade::MeshData mesh{MeshPrimitive::Points, + {}, indices, Trade::MeshIndexData{meshIndexTypeWrap(0xcaca), Containers::stridedArrayView(indices).slice(1, 4)}, + {}, vertices, {Trade::MeshAttributeData{Trade::MeshAttribute::Position, Containers::arrayView(vertices)}}}; + + /* Just verify the index data get passed through with no information loss */ + Trade::MeshData out = transform3D(mesh, Matrix4{}, 0, InterleaveFlag::PreserveStridedIndices); + CORRADE_COMPARE(out.primitive(), MeshPrimitive::Points); + CORRADE_COMPARE(out.indexType(), meshIndexTypeWrap(0xcaca)); + CORRADE_COMPARE(out.indexOffset(), 2); + CORRADE_COMPARE(out.indexStride(), 2); + CORRADE_COMPARE(out.indexCount(), 3); + CORRADE_COMPARE_AS((Containers::arrayCast<1, const UnsignedShort>(out.indices())), + Containers::arrayView({1, 2, 0}), + TestSuite::Compare::Container); +} + void TransformTest::meshData3DImplementationSpecificVertexFormat() { auto&& data = MeshData3DIMplementationSpecificVertexFormatData[testCaseInstanceId()]; setTestCaseDescription(data.name); @@ -1388,6 +1440,29 @@ void TransformTest::meshDataTextureCoordinates2DNoCoordinates() { CORRADE_COMPARE(out.str(), "MeshTools::transformTextureCoordinates2D(): the mesh has no texture coordinates with index 1\n"); } +void TransformTest::meshDataTextureCoordinates2DImplementationSpecificIndexType() { + const UnsignedShort indices[]{3, 1, 2, 0, 2}; + const Vector2 vertices[]{ + {0.0f, 0.0f}, + {1.0f, 0.0f}, + {0.0f, 2.0f} + }; + Trade::MeshData mesh{MeshPrimitive::Points, + {}, indices, Trade::MeshIndexData{meshIndexTypeWrap(0xcaca), Containers::stridedArrayView(indices).slice(1, 4)}, + {}, vertices, {Trade::MeshAttributeData{Trade::MeshAttribute::TextureCoordinates, Containers::arrayView(vertices)}}}; + + /* Just verify the index data get passed through with no information loss */ + Trade::MeshData out = transformTextureCoordinates2D(mesh, Matrix3{}, 0, InterleaveFlag::PreserveStridedIndices); + CORRADE_COMPARE(out.primitive(), MeshPrimitive::Points); + CORRADE_COMPARE(out.indexType(), meshIndexTypeWrap(0xcaca)); + CORRADE_COMPARE(out.indexOffset(), 2); + CORRADE_COMPARE(out.indexStride(), 2); + CORRADE_COMPARE(out.indexCount(), 3); + CORRADE_COMPARE_AS((Containers::arrayCast<1, const UnsignedShort>(out.indices())), + Containers::arrayView({1, 2, 0}), + TestSuite::Compare::Container); +} + void TransformTest::meshDataTextureCoordinates2DImplementationSpecificVertexFormat() { #ifdef CORRADE_NO_ASSERT CORRADE_SKIP("CORRADE_NO_ASSERT defined, can't test assertions"); diff --git a/src/Magnum/MeshTools/Transform.cpp b/src/Magnum/MeshTools/Transform.cpp index d6d3d9fbd..f6e40578d 100644 --- a/src/Magnum/MeshTools/Transform.cpp +++ b/src/Magnum/MeshTools/Transform.cpp @@ -27,12 +27,13 @@ #include +#include "Magnum/MeshTools/FilterAttributes.h" #include "Magnum/MeshTools/Interleave.h" #include "Magnum/Trade/MeshData.h" namespace Magnum { namespace MeshTools { -Trade::MeshData transform2D(const Trade::MeshData& data, const Matrix3& transformation, const UnsignedInt id) { +Trade::MeshData transform2D(const Trade::MeshData& data, const Matrix3& transformation, const UnsignedInt id, const InterleaveFlags flags) { const Containers::Optional positionAttributeId = data.findAttributeId(Trade::MeshAttribute::Position, id); CORRADE_ASSERT(positionAttributeId, "MeshTools::transform2D(): the mesh has no positions with index" << id, @@ -60,10 +61,7 @@ Trade::MeshData transform2D(const Trade::MeshData& data, const Matrix3& transfor /* Create the output mesh, making more room for the full formats if necessary */ - Trade::MeshData out = interleave(Trade::MeshData{data.primitive(), - /* If data is not indexed, the reference will be also non-indexed */ - {}, data.indexData(), Trade::MeshIndexData{data.indices()}, - {}, data.vertexData(), {}, data.vertexCount()}, attributes); + Trade::MeshData out = interleave(filterOnlyAttributes(data, Containers::ArrayView{}), attributes, flags); /* If the position attribute wasn't in a desired format, unpack it */ if(positionAttributeFormat != VertexFormat::Vector2) @@ -74,7 +72,7 @@ Trade::MeshData transform2D(const Trade::MeshData& data, const Matrix3& transfor return out; } -Trade::MeshData transform2D(Trade::MeshData&& data, const Matrix3& transformation, const UnsignedInt id) { +Trade::MeshData transform2D(Trade::MeshData&& data, const Matrix3& transformation, const UnsignedInt id, const InterleaveFlags flags) { /* Perform the operation in-place, if we can transfer the ownership and have positions in the right format already. Explicitly checking for presence of the position attribute so we don't need to duplicate the @@ -90,7 +88,7 @@ Trade::MeshData transform2D(Trade::MeshData&& data, const Matrix3& transformatio /* Otherwise delegate to the function that does all the copying and format expansion */ - return transform2D(data, transformation, id); + return transform2D(data, transformation, id, flags); } void transform2DInPlace(Trade::MeshData& data, const Matrix3& transformation, const UnsignedInt id) { @@ -107,7 +105,7 @@ void transform2DInPlace(Trade::MeshData& data, const Matrix3& transformation, co position = transformation.transformPoint(position); } -Trade::MeshData transform3D(const Trade::MeshData& data, const Matrix4& transformation, const UnsignedInt id) { +Trade::MeshData transform3D(const Trade::MeshData& data, const Matrix4& transformation, const UnsignedInt id, const InterleaveFlags flags) { const Containers::Optional positionAttributeId = data.findAttributeId(Trade::MeshAttribute::Position, id); CORRADE_ASSERT(positionAttributeId, "MeshTools::transform3D(): the mesh has no positions with index" << id, @@ -168,10 +166,7 @@ Trade::MeshData transform3D(const Trade::MeshData& data, const Matrix4& transfor /* Create the output mesh, making more room for the full formats if necessary */ - Trade::MeshData out = interleave(Trade::MeshData{data.primitive(), - /* If data is not indexed, the reference will be also non-indexed */ - {}, data.indexData(), Trade::MeshIndexData{data.indices()}, - {}, data.vertexData(), {}, data.vertexCount()}, attributes); + Trade::MeshData out = interleave(filterOnlyAttributes(data, Containers::ArrayView{}), attributes, flags); /* If the position/TBN attributes weren't in a desired format, unpack them */ if(positionAttributeFormat != VertexFormat::Vector3) @@ -194,7 +189,7 @@ Trade::MeshData transform3D(const Trade::MeshData& data, const Matrix4& transfor return out; } -Trade::MeshData transform3D(Trade::MeshData&& data, const Matrix4& transformation, const UnsignedInt id) { +Trade::MeshData transform3D(Trade::MeshData&& data, const Matrix4& transformation, const UnsignedInt id, const InterleaveFlags flags) { /* Perform the operation in-place, if we can transfer the ownership and have positions in the right format already. Explicitly checking for presence of the position attribute so we don't need to duplicate the @@ -217,7 +212,7 @@ Trade::MeshData transform3D(Trade::MeshData&& data, const Matrix4& transformatio /* Otherwise delegate to the function that does all the copying and format expansion */ - return transform3D(data, transformation, id); + return transform3D(data, transformation, id, flags); } void transform3DInPlace(Trade::MeshData& data, const Matrix4& transformation, const UnsignedInt id) { @@ -267,7 +262,7 @@ void transform3DInPlace(Trade::MeshData& data, const Matrix4& transformation, co normal = normalMatrix*normal; } -Trade::MeshData transformTextureCoordinates2D(const Trade::MeshData& data, const Matrix3& transformation, const UnsignedInt id) { +Trade::MeshData transformTextureCoordinates2D(const Trade::MeshData& data, const Matrix3& transformation, const UnsignedInt id, const InterleaveFlags flags) { const Containers::Optional textureCoordinateAttributeId = data.findAttributeId(Trade::MeshAttribute::TextureCoordinates, id); CORRADE_ASSERT(textureCoordinateAttributeId, "MeshTools::transformTextureCoordinates2D(): the mesh has no texture coordinates with index" << id, @@ -292,10 +287,7 @@ Trade::MeshData transformTextureCoordinates2D(const Trade::MeshData& data, const /* Create the output mesh, making more room for the full formats if necessary */ - Trade::MeshData out = interleave(Trade::MeshData{data.primitive(), - /* If data is not indexed, the reference will be also non-indexed */ - {}, data.indexData(), Trade::MeshIndexData{data.indices()}, - {}, data.vertexData(), {}, data.vertexCount()}, attributes); + Trade::MeshData out = interleave(filterOnlyAttributes(data, Containers::ArrayView{}), attributes, flags); /* If the position attribute wasn't in a desired format, unpack it */ if(data.attributeFormat(*textureCoordinateAttributeId) != VertexFormat::Vector2) @@ -306,7 +298,7 @@ Trade::MeshData transformTextureCoordinates2D(const Trade::MeshData& data, const return out; } -Trade::MeshData transformTextureCoordinates2D(Trade::MeshData&& data, const Matrix3& transformation, const UnsignedInt id) { +Trade::MeshData transformTextureCoordinates2D(Trade::MeshData&& data, const Matrix3& transformation, const UnsignedInt id, const InterleaveFlags flags) { /* Perform the operation in-place, if we can transfer the ownership and have positions in the right format already. Explicitly checking for presence of the position attribute so we don't need to duplicate the @@ -322,7 +314,7 @@ Trade::MeshData transformTextureCoordinates2D(Trade::MeshData&& data, const Matr /* Otherwise delegate to the function that does all the copying and format expansion */ - return transformTextureCoordinates2D(data, transformation, id); + return transformTextureCoordinates2D(data, transformation, id, flags); } void transformTextureCoordinates2DInPlace(Trade::MeshData& data, const Matrix3& transformation, const UnsignedInt id) { diff --git a/src/Magnum/MeshTools/Transform.h b/src/Magnum/MeshTools/Transform.h index 3c0dc4a33..7fe568d59 100644 --- a/src/Magnum/MeshTools/Transform.h +++ b/src/Magnum/MeshTools/Transform.h @@ -31,6 +31,7 @@ #include "Magnum/Math/DualQuaternion.h" #include "Magnum/Math/DualComplex.h" +#include "Magnum/MeshTools/InterleaveFlags.h" #include "Magnum/MeshTools/visibility.h" #include "Magnum/Trade/Trade.h" @@ -146,31 +147,33 @@ template U transformPoints(const T& transformation, U vectors) Expects that the mesh contains a two-dimensional @ref Trade::MeshAttribute::Position with index @p id and that the attribute does not have an implementation-specific format. To avoid data loss with packed -types, the positions are always converted to @ref VertexFormat::Vector2. Other -attributes, position attributes other than @p id, and indices (if any) are -passed through untouched. +types, the positions are converted to @ref VertexFormat::Vector2 if not +already. In that case the data layouting is done by @ref interleavedLayout() +with the @p flags parameter propagated to it, see its documentation for +detailed behavior description. Other attributes, position attributes other than +@p id, and indices (if any) are passed through untouched. -See also @ref transform2D(Trade::MeshData&&, const Matrix3&, UnsignedInt) for a -potentially more efficient operation instead of always performing a full copy, -you can also do an in-place transformation using @ref transform2DInPlace(). +See also @ref transform2D(Trade::MeshData&&, const Matrix3&, UnsignedInt, InterleaveFlags) +for a potentially more efficient operation instead of always performing a full +copy, you can also do an in-place transformation using @ref transform2DInPlace(). @see @ref transform3D(), @ref transformTextureCoordinates2D(), @ref Trade::MeshData::attributeCount(MeshAttribute) const, @ref Trade::MeshData::attributeFormat(MeshAttribute, UnsignedInt) const, @ref isVertexFormatImplementationSpecific() */ -MAGNUM_MESHTOOLS_EXPORT Trade::MeshData transform2D(const Trade::MeshData& data, const Matrix3& transformation, UnsignedInt id = 0); +MAGNUM_MESHTOOLS_EXPORT Trade::MeshData transform2D(const Trade::MeshData& data, const Matrix3& transformation, UnsignedInt id = 0, InterleaveFlags flags = InterleaveFlag::PreserveInterleavedAttributes); /** @brief Transform 2D positions in a mesh data @m_since_latest -Compared to @ref transform2D(const Trade::MeshData&, const Matrix3&, UnsignedInt) +Compared to @ref transform2D(const Trade::MeshData&, const Matrix3&, UnsignedInt, InterleaveFlags) this function can can perform the transformation in-place, transferring the data ownership to the returned instance, if both vertex and index data is owned, vertex data is mutable and the positions with index @p id are @ref VertexFormat::Vector2. */ -MAGNUM_MESHTOOLS_EXPORT Trade::MeshData transform2D(Trade::MeshData&& data, const Matrix3& transformation, UnsignedInt id = 0); +MAGNUM_MESHTOOLS_EXPORT Trade::MeshData transform2D(Trade::MeshData&& data, const Matrix3& transformation, UnsignedInt id = 0, InterleaveFlags flags = InterleaveFlag::PreserveInterleavedAttributes); /** @brief Transform 2D positions in a mesh data in-place @@ -200,34 +203,36 @@ Expects that the mesh contains a three-dimensional those get transformed with @ref Matrix4::normalMatrix() extracted out of @p transformation. All these attributes are expected to not have an implementation-specific format. To avoid data loss with packed types, the -positions, normals and bitangents are always converted to -@ref VertexFormat::Vector3, tangents to either @ref VertexFormat::Vector3 or -@ref VertexFormat::Vector4. Other attributes, additional -position/TBN attributes other than @p id, and indices (if any) are passed -through untouched. - -See also @ref transform3D(Trade::MeshData&&, const Matrix4&, UnsignedInt) for a -potentially more efficient operation instead of always performing a full copy, -you can also do an in-place transformation using @ref transform3DInPlace(). +positions, normals and bitangents are converted to @ref VertexFormat::Vector3 +if not already, tangents to either @ref VertexFormat::Vector3 or +@ref VertexFormat::Vector4 if not already. In that case the data layouting is +done by @ref interleavedLayout() with the @p flags parameter propagated to it, +see its documentation for detailed behavior description. Other attributes, +additional position/TBN attributes other than @p id, and indices (if any) are +passed through untouched. + +See also @ref transform3D(Trade::MeshData&&, const Matrix4&, UnsignedInt, InterleaveFlags) +for a potentially more efficient operation instead of always performing a full +copy, you can also do an in-place transformation using @ref transform3DInPlace(). @see @ref transform2D(), @ref transformTextureCoordinates2D(), @ref Trade::MeshData::attributeCount(MeshAttribute) const, @ref Trade::MeshData::attributeFormat(MeshAttribute, UnsignedInt) const, @ref isVertexFormatImplementationSpecific() */ -MAGNUM_MESHTOOLS_EXPORT Trade::MeshData transform3D(const Trade::MeshData& data, const Matrix4& transformation, UnsignedInt id = 0); +MAGNUM_MESHTOOLS_EXPORT Trade::MeshData transform3D(const Trade::MeshData& data, const Matrix4& transformation, UnsignedInt id = 0, InterleaveFlags flags = InterleaveFlag::PreserveInterleavedAttributes); /** @brief Transform 3D positions, normals, tangenta and bitangents in a mesh data @m_since_latest -Compared to @ref transform3D(const Trade::MeshData&, const Matrix4&, UnsignedInt) +Compared to @ref transform3D(const Trade::MeshData&, const Matrix4&, UnsignedInt, InterleaveFlags) this function can can perform the transformation in-place, transferring the data ownership to the returned instance, if both vertex and index data is owned, vertex data is mutable, positions, normals and bitangents (if present) are @ref VertexFormat::Vector3 and tangents (if present) either @ref VertexFormat::Vector3 or @ref VertexFormat::Vector4. */ -MAGNUM_MESHTOOLS_EXPORT Trade::MeshData transform3D(Trade::MeshData&& data, const Matrix4& transformation, UnsignedInt id = 0); +MAGNUM_MESHTOOLS_EXPORT Trade::MeshData transform3D(Trade::MeshData&& data, const Matrix4& transformation, UnsignedInt id = 0, InterleaveFlags flags = InterleaveFlag::PreserveInterleavedAttributes); /** @brief Transform 3D positions, normals, tangents and bitangents in a mesh data in-place @@ -257,11 +262,13 @@ MAGNUM_MESHTOOLS_EXPORT void transform3DInPlace(Trade::MeshData& data, const Mat Expects that the mesh contains a @ref Trade::MeshAttribute::TextureCoordinates with index id. To avoid data loss with packed types, the coordinattes are -always converted to @ref VertexFormat::Vector2. Other attributes, texture -coordinate attributes other than @p id, and indices (if any) are passed through -untouched. +converted to @ref VertexFormat::Vector2 if not already. In that case the data +layouting is done by @ref interleavedLayout() with the @p flags parameter +propagated to it, see its documentation for detailed behavior description. +Other attributes, texture coordinate attributes other than @p id, and indices +(if any) are passed through untouched. -See also @ref transformTextureCoordinates2D(Trade::MeshData&&, const Matrix3&, UnsignedInt) +See also @ref transformTextureCoordinates2D(Trade::MeshData&&, const Matrix3&, UnsignedInt, InterleaveFlags) for a potentially more efficient operation instead of always performing a full copy, you can also do an in-place transformation using @ref transformTextureCoordinates2DInPlace(). @@ -269,19 +276,19 @@ copy, you can also do an in-place transformation using @ref Trade::MeshData::attributeCount(MeshAttribute) const, @ref isVertexFormatImplementationSpecific() */ -MAGNUM_MESHTOOLS_EXPORT Trade::MeshData transformTextureCoordinates2D(const Trade::MeshData& data, const Matrix3& transformation, UnsignedInt id = 0); +MAGNUM_MESHTOOLS_EXPORT Trade::MeshData transformTextureCoordinates2D(const Trade::MeshData& data, const Matrix3& transformation, UnsignedInt id = 0, InterleaveFlags flags = InterleaveFlag::PreserveInterleavedAttributes); /** @brief Transform 2D texture coordinates in a mesh data @m_since_latest -Compared to @ref transformTextureCoordinates2D(const Trade::MeshData&, const Matrix3&, UnsignedInt) +Compared to @ref transformTextureCoordinates2D(const Trade::MeshData&, const Matrix3&, UnsignedInt, InterleaveFlags) this function can can perform the transformation in-place, transferring the data ownership to the returned instance, if both vertex and index data is owned, vertex data is mutable and the coordinates with index @p id are @ref VertexFormat::Vector2. */ -MAGNUM_MESHTOOLS_EXPORT Trade::MeshData transformTextureCoordinates2D(Trade::MeshData&& data, const Matrix3& transformation, UnsignedInt id = 0); +MAGNUM_MESHTOOLS_EXPORT Trade::MeshData transformTextureCoordinates2D(Trade::MeshData&& data, const Matrix3& transformation, UnsignedInt id = 0, InterleaveFlags flags = InterleaveFlag::PreserveInterleavedAttributes); /** @brief Transform 2D texture coordinates in a mesh data in-place