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