Browse Source

MeshTools: a family of transform*() APIs operating on a MeshData.

And doing all the automagic of unpacking packed types, converting
positions *and* normals/tangents/bitangents, and also an overload for
transforming texture coordinates.

Such a simple thing and yet so complex and nasty to test.
pull/542/merge
Vladimír Vondruš 4 years ago
parent
commit
a40c721fac
  1. 4
      doc/changelog.dox
  2. 3
      src/Magnum/MeshTools/CMakeLists.txt
  3. 2
      src/Magnum/MeshTools/Test/CMakeLists.txt
  4. 1452
      src/Magnum/MeshTools/Test/TransformTest.cpp
  5. 311
      src/Magnum/MeshTools/Transform.cpp
  6. 160
      src/Magnum/MeshTools/Transform.h

4
doc/changelog.dox

@ -155,6 +155,10 @@ See also:
- Added @ref MeshTools::generateQuadIndices() for quad triangulation
including non-convex and non-planar quads
- New family of @ref MeshTools::transform2D(), @ref MeshTools::transform3D()
and @ref MeshTools::transformTextureCoordinates2D() APIs for converting
positions, normals, tangents, bitangents and texture coordinates directly
in @ref Trade::MeshData instances
@subsubsection changelog-latest-new-platform Platform libraries

3
src/Magnum/MeshTools/CMakeLists.txt

@ -38,7 +38,8 @@ set(MagnumMeshTools_GracefulAssert_SRCS
GenerateNormals.cpp
Interleave.cpp
Reference.cpp
RemoveDuplicates.cpp)
RemoveDuplicates.cpp
Transform.cpp)
set(MagnumMeshTools_HEADERS
Combine.h

2
src/Magnum/MeshTools/Test/CMakeLists.txt

@ -35,7 +35,7 @@ corrade_add_test(MeshToolsReferenceTest ReferenceTest.cpp LIBRARIES MagnumMeshTo
corrade_add_test(MeshToolsRemoveDuplicatesTest RemoveDuplicatesTest.cpp LIBRARIES MagnumMeshToolsTestLib)
corrade_add_test(MeshToolsSubdivideTest SubdivideTest.cpp LIBRARIES Magnum MagnumPrimitives)
corrade_add_test(MeshToolsTipsifyTest TipsifyTest.cpp LIBRARIES MagnumMeshTools)
corrade_add_test(MeshToolsTransformTest TransformTest.cpp LIBRARIES MagnumMeshTools)
corrade_add_test(MeshToolsTransformTest TransformTest.cpp LIBRARIES MagnumMeshToolsTestLib)
# Graceful assert for testing
set_property(TARGET

1452
src/Magnum/MeshTools/Test/TransformTest.cpp

File diff suppressed because it is too large Load Diff

311
src/Magnum/MeshTools/Transform.cpp

@ -0,0 +1,311 @@
/*
This file is part of Magnum.
Copyright © 2010, 2011, 2012, 2013, 2014, 2015, 2016, 2017, 2018, 2019,
2020, 2021 Vladimír Vondruš <mosra@centrum.cz>
Permission is hereby granted, free of charge, to any person obtaining a
copy of this software and associated documentation files (the "Software"),
to deal in the Software without restriction, including without limitation
the rights to use, copy, modify, merge, publish, distribute, sublicense,
and/or sell copies of the Software, and to permit persons to whom the
Software is furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included
in all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
DEALINGS IN THE SOFTWARE.
*/
#include "Transform.h"
#include <Corrade/Containers/Optional.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) {
const Containers::Optional<UnsignedInt> positionAttributeId = data.findAttributeId(Trade::MeshAttribute::Position, id);
CORRADE_ASSERT(positionAttributeId,
"MeshTools::transform2D(): the mesh has no positions with index" << id,
(Trade::MeshData{MeshPrimitive::Triangles, 0}));
const VertexFormat positionAttributeFormat = data.attributeFormat(*positionAttributeId);
CORRADE_ASSERT(vertexFormatComponentCount(positionAttributeFormat) == 2,
"MeshTools::transform2D(): expected 2D positions but got" << positionAttributeFormat,
(Trade::MeshData{MeshPrimitive::Triangles, 0}));
/* Copy original attributes to a mutable array so we can update the
position attribute format, if needed. Not using Utility::copy() here as
the view returned by attributeData() might have offset-only attributes
which interleave() doesn't want. */
Containers::Array<Trade::MeshAttributeData> attributes{data.attributeCount()};
for(UnsignedInt i = 0; i != data.attributeCount(); ++i)
attributes[i] = data.attributeData(i);
/* If the position attribute isn't in a desired format, replace it with an
empty placeholder that we'll unpack the data into */
if(positionAttributeFormat != VertexFormat::Vector2)
attributes[*positionAttributeId] = Trade::MeshAttributeData{Trade::MeshAttribute::Position, VertexFormat::Vector2, nullptr};
/* 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);
/* If the position attribute wasn't in a desired format, unpack it */
if(positionAttributeFormat != VertexFormat::Vector2)
data.positions2DInto(out.mutableAttribute<Vector2>(*positionAttributeId), id);
/* Delegate to the in-place implementation and return */
transform2DInPlace(out, transformation, id);
return out;
}
Trade::MeshData transform2D(Trade::MeshData&& data, const Matrix3& transformation, const UnsignedInt id) {
/* 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
assert here again. */
if((data.indexDataFlags() & Trade::DataFlag::Owned) &&
(data.vertexDataFlags() & Trade::DataFlag::Owned) &&
data.attributeCount(Trade::MeshAttribute::Position) > id &&
data.attributeFormat(Trade::MeshAttribute::Position, id) == VertexFormat::Vector2)
{
transform2DInPlace(data, transformation, id);
return std::move(data);
}
/* Otherwise delegate to the function that does all the copying and format
expansion */
return transform2D(data, transformation, id);
}
void transform2DInPlace(Trade::MeshData& data, const Matrix3& transformation, const UnsignedInt id) {
CORRADE_ASSERT(data.vertexDataFlags() & Trade::DataFlag::Mutable,
"MeshTools::transform2DInPlace(): vertex data not mutable", );
const Containers::Optional<UnsignedInt> positionAttributeId = data.findAttributeId(Trade::MeshAttribute::Position, id);
CORRADE_ASSERT(positionAttributeId,
"MeshTools::transform2DInPlace(): the mesh has no positions with index" << id, );
CORRADE_ASSERT(data.attributeFormat(*positionAttributeId) == VertexFormat::Vector2,
"MeshTools::transform2DInPlace(): expected" << VertexFormat::Vector2 << "positions but got" << data.attributeFormat(*positionAttributeId), );
/** @todo this needs a proper batch implementation */
for(Vector2& position: data.mutableAttribute<Vector2>(*positionAttributeId))
position = transformation.transformPoint(position);
}
Trade::MeshData transform3D(const Trade::MeshData& data, const Matrix4& transformation, const UnsignedInt id) {
const Containers::Optional<UnsignedInt> positionAttributeId = data.findAttributeId(Trade::MeshAttribute::Position, id);
CORRADE_ASSERT(positionAttributeId,
"MeshTools::transform3D(): the mesh has no positions with index" << id,
(Trade::MeshData{MeshPrimitive::Triangles, 0}));
const VertexFormat positionAttributeFormat = data.attributeFormat(*positionAttributeId);
CORRADE_ASSERT(vertexFormatComponentCount(positionAttributeFormat) == 3,
"MeshTools::transform3D(): expected 3D positions but got" << positionAttributeFormat,
(Trade::MeshData{MeshPrimitive::Triangles, 0}));
const Containers::Optional<UnsignedInt> tangentAttributeId = data.findAttributeId(Trade::MeshAttribute::Tangent, id);
const VertexFormat desiredTangentVertexFormat = tangentAttributeId ?
(vertexFormatComponentCount(data.attributeFormat(*tangentAttributeId)) == 4 ?
VertexFormat::Vector4 : VertexFormat::Vector3) : VertexFormat{};
const Containers::Optional<UnsignedInt> bitangentAttributeId = data.findAttributeId(Trade::MeshAttribute::Bitangent, id);
const Containers::Optional<UnsignedInt> normalAttributeId = data.findAttributeId(Trade::MeshAttribute::Normal, id);
/* Copy original attributes to a mutable array so we can update the
position attribute format, if needed. Not using Utility::copy() here as
the view returned by attributeData() might have offset-only attributes
which interleave() doesn't want. */
Containers::Array<Trade::MeshAttributeData> attributes{data.attributeCount()};
for(UnsignedInt i = 0; i != data.attributeCount(); ++i)
attributes[i] = data.attributeData(i);
/* If the position/TBN attributes aren't in a desired format, replace them
with an empty placeholder that we'll unpack the data into */
if(positionAttributeFormat != VertexFormat::Vector3)
attributes[*positionAttributeId] = Trade::MeshAttributeData{Trade::MeshAttribute::Position, VertexFormat::Vector3, nullptr};
if(tangentAttributeId && data.attributeFormat(*tangentAttributeId) != desiredTangentVertexFormat)
attributes[*tangentAttributeId] = Trade::MeshAttributeData{Trade::MeshAttribute::Tangent, desiredTangentVertexFormat, nullptr};
if(bitangentAttributeId && data.attributeFormat(*bitangentAttributeId) != VertexFormat::Vector3)
attributes[*bitangentAttributeId] = Trade::MeshAttributeData{Trade::MeshAttribute::Bitangent, VertexFormat::Vector3, nullptr};
if(normalAttributeId && data.attributeFormat(*normalAttributeId) != VertexFormat::Vector3)
attributes[*normalAttributeId] = Trade::MeshAttributeData{Trade::MeshAttribute::Normal, VertexFormat::Vector3, nullptr};
/* 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);
/* If the position/TBN attributes weren't in a desired format, unpack them */
if(positionAttributeFormat != VertexFormat::Vector3)
data.positions3DInto(out.mutableAttribute<Vector3>(*positionAttributeId), id);
if(tangentAttributeId && data.attributeFormat(*tangentAttributeId) != desiredTangentVertexFormat) {
if(desiredTangentVertexFormat == VertexFormat::Vector4) {
data.tangentsInto(out.mutableAttribute<Vector4>(*tangentAttributeId).slice(&Vector4::xyz), id);
data.bitangentSignsInto(out.mutableAttribute<Vector4>(*tangentAttributeId).slice(&Vector4::w), id);
} else {
data.tangentsInto(out.mutableAttribute<Vector3>(*tangentAttributeId), id);
}
}
if(bitangentAttributeId && data.attributeFormat(*bitangentAttributeId) != VertexFormat::Vector3)
data.bitangentsInto(out.mutableAttribute<Vector3>(*bitangentAttributeId), id);
if(normalAttributeId && data.attributeFormat(*normalAttributeId) != VertexFormat::Vector3)
data.normalsInto(out.mutableAttribute<Vector3>(*normalAttributeId), id);
/* Delegate to the in-place implementation and return */
transform3DInPlace(out, transformation, id);
return out;
}
Trade::MeshData transform3D(Trade::MeshData&& data, const Matrix4& transformation, const UnsignedInt id) {
/* 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
assert here again. */
const Containers::Optional<UnsignedInt> positionAttributeId = data.findAttributeId(Trade::MeshAttribute::Position, id);
const Containers::Optional<UnsignedInt> tangentAttributeId = data.findAttributeId(Trade::MeshAttribute::Tangent, id);
const Containers::Optional<UnsignedInt> bitangentAttributeId = data.findAttributeId(Trade::MeshAttribute::Bitangent, id);
const Containers::Optional<UnsignedInt> normalAttributeId = data.findAttributeId(Trade::MeshAttribute::Normal, id);
if((data.indexDataFlags() & Trade::DataFlag::Owned) &&
(data.vertexDataFlags() & Trade::DataFlag::Owned) &&
positionAttributeId &&
data.attributeFormat(*positionAttributeId) == VertexFormat::Vector3 &&
(!tangentAttributeId || data.attributeFormat(*tangentAttributeId) == VertexFormat::Vector3 || data.attributeFormat(*tangentAttributeId) == VertexFormat::Vector4) &&
(!bitangentAttributeId || data.attributeFormat(*bitangentAttributeId) == VertexFormat::Vector3) &&
(!normalAttributeId || data.attributeFormat(*normalAttributeId) == VertexFormat::Vector3))
{
transform3DInPlace(data, transformation, id);
return std::move(data);
}
/* Otherwise delegate to the function that does all the copying and format
expansion */
return transform3D(data, transformation, id);
}
void transform3DInPlace(Trade::MeshData& data, const Matrix4& transformation, const UnsignedInt id) {
CORRADE_ASSERT(data.vertexDataFlags() & Trade::DataFlag::Mutable,
"MeshTools::transform3DInPlace(): vertex data not mutable", );
const Containers::Optional<UnsignedInt> positionAttributeId = data.findAttributeId(Trade::MeshAttribute::Position, id);
CORRADE_ASSERT(positionAttributeId,
"MeshTools::transform3DInPlace(): the mesh has no positions with index" << id, );
CORRADE_ASSERT(data.attributeFormat(*positionAttributeId) == VertexFormat::Vector3,
"MeshTools::transform3DInPlace(): expected" << VertexFormat::Vector3 << "positions but got" << data.attributeFormat(*positionAttributeId), );
const Containers::Optional<UnsignedInt> tangentAttributeId = data.findAttributeId(Trade::MeshAttribute::Tangent, id);
const VertexFormat tangentAttributeFormat = tangentAttributeId ? data.attributeFormat(*tangentAttributeId) : VertexFormat{};
CORRADE_ASSERT(!tangentAttributeId || tangentAttributeFormat == VertexFormat::Vector3 || tangentAttributeFormat == VertexFormat::Vector4,
"MeshTools::transform3DInPlace(): expected" << VertexFormat::Vector3 << "or" << VertexFormat::Vector4 << "tangents but got" << data.attributeFormat(*tangentAttributeId), );
const Containers::Optional<UnsignedInt> bitangentAttributeId = data.findAttributeId(Trade::MeshAttribute::Bitangent, id);
CORRADE_ASSERT(!bitangentAttributeId || data.attributeFormat(*bitangentAttributeId) == VertexFormat::Vector3,
"MeshTools::transform3DInPlace(): expected" << VertexFormat::Vector3 << "bitangents but got" << data.attributeFormat(*bitangentAttributeId), );
const Containers::Optional<UnsignedInt> normalAttributeId = data.findAttributeId(Trade::MeshAttribute::Normal, id);
CORRADE_ASSERT(!normalAttributeId || data.attributeFormat(*normalAttributeId) == VertexFormat::Vector3,
"MeshTools::transform3DInPlace(): expected" << VertexFormat::Vector3 << "normals but got" << data.attributeFormat(*normalAttributeId), );
/** @todo this needs a proper batch implementation */
for(Vector3& position: data.mutableAttribute<Vector3>(*positionAttributeId))
position = transformation.transformPoint(position);
/* If no other attributes are present, nothing to do */
if(!tangentAttributeId && !bitangentAttributeId && !normalAttributeId)
return;
const Matrix3x3 normalMatrix = transformation.normalMatrix();
if(tangentAttributeId) {
/** @todo this needs a proper batch implementation */
if(tangentAttributeFormat == VertexFormat::Vector3)
for(Vector3& tangent: data.mutableAttribute<Vector3>(*tangentAttributeId))
tangent = normalMatrix*tangent;
else for(Vector4& tangent: data.mutableAttribute<Vector4>(*tangentAttributeId)) {
tangent.xyz() = normalMatrix*tangent.xyz();
/** @todo figure out the fourth component, probably has to get
flipped when the scale changes handedness? */
}
}
/** @todo this needs a proper batch implementation */
if(bitangentAttributeId) for(Vector3& bitangent: data.mutableAttribute<Vector3>(*bitangentAttributeId))
bitangent = normalMatrix*bitangent;
/** @todo this needs a proper batch implementation */
if(normalAttributeId) for(Vector3& normal: data.mutableAttribute<Vector3>(*normalAttributeId))
normal = normalMatrix*normal;
}
Trade::MeshData transformTextureCoordinates2D(const Trade::MeshData& data, const Matrix3& transformation, const UnsignedInt id) {
const Containers::Optional<UnsignedInt> textureCoordinateAttributeId = data.findAttributeId(Trade::MeshAttribute::TextureCoordinates, id);
CORRADE_ASSERT(textureCoordinateAttributeId,
"MeshTools::transformTextureCoordinates2D(): the mesh has no texture coordinates with index" << id,
(Trade::MeshData{MeshPrimitive::Triangles, 0}));
/* Copy original attributes to a mutable array so we can update the
position attribute format, if needed. Not using Utility::copy() here as
the view returned by attributeData() might have offset-only attributes
which interleave() doesn't want. */
Containers::Array<Trade::MeshAttributeData> attributes{data.attributeCount()};
for(UnsignedInt i = 0; i != data.attributeCount(); ++i)
attributes[i] = data.attributeData(i);
/* If the position attribute isn't in a desired format, replace it with an
empty placeholder that we'll unpack the data into */
if(data.attributeFormat(*textureCoordinateAttributeId) != VertexFormat::Vector2)
attributes[*textureCoordinateAttributeId] = Trade::MeshAttributeData{Trade::MeshAttribute::TextureCoordinates, VertexFormat::Vector2, nullptr};
/* 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);
/* If the position attribute wasn't in a desired format, unpack it */
if(data.attributeFormat(*textureCoordinateAttributeId) != VertexFormat::Vector2)
data.textureCoordinates2DInto(out.mutableAttribute<Vector2>(*textureCoordinateAttributeId), id);
/* Delegate to the in-place implementation and return */
transformTextureCoordinates2DInPlace(out, transformation, id);
return out;
}
Trade::MeshData transformTextureCoordinates2D(Trade::MeshData&& data, const Matrix3& transformation, const UnsignedInt id) {
/* 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
assert here again. */
if((data.indexDataFlags() & Trade::DataFlag::Owned) &&
(data.vertexDataFlags() & Trade::DataFlag::Owned) &&
data.attributeCount(Trade::MeshAttribute::TextureCoordinates) > id &&
data.attributeFormat(Trade::MeshAttribute::TextureCoordinates, id) == VertexFormat::Vector2)
{
transformTextureCoordinates2DInPlace(data, transformation, id);
return std::move(data);
}
/* Otherwise delegate to the function that does all the copying and format
expansion */
return transformTextureCoordinates2D(data, transformation, id);
}
void transformTextureCoordinates2DInPlace(Trade::MeshData& data, const Matrix3& transformation, const UnsignedInt id) {
CORRADE_ASSERT(data.vertexDataFlags() & Trade::DataFlag::Mutable,
"MeshTools::transformTextureCoordinates2DInPlace(): vertex data not mutable", );
const Containers::Optional<UnsignedInt> textureCoordinateAttributeId = data.findAttributeId(Trade::MeshAttribute::TextureCoordinates, id);
CORRADE_ASSERT(textureCoordinateAttributeId,
"MeshTools::transformTextureCoordinates2DInPlace(): the mesh has no texture coordinates with index" << id, );
CORRADE_ASSERT(data.attributeFormat(*textureCoordinateAttributeId) == VertexFormat::Vector2,
"MeshTools::transformTextureCoordinates2DInPlace(): expected" << VertexFormat::Vector2 << "texture coordinates but got" << data.attributeFormat(*textureCoordinateAttributeId), );
/** @todo this needs a proper batch implementation */
for(Vector2& position: data.mutableAttribute<Vector2>(*textureCoordinateAttributeId))
position = transformation.transformPoint(position);
}
}}

160
src/Magnum/MeshTools/Transform.h

@ -26,11 +26,13 @@
*/
/** @file
* @brief Function @ref Magnum::MeshTools::transformVectorsInPlace(), @ref Magnum::MeshTools::transformVectors(), @ref Magnum::MeshTools::transformPointsInPlace(), @ref Magnum::MeshTools::transformPoints()
* @brief Function @ref Magnum::MeshTools::transformVectorsInPlace(), @ref Magnum::MeshTools::transformVectors(), @ref Magnum::MeshTools::transformPointsInPlace(), @ref Magnum::MeshTools::transformPoints(), @ref Magnum::MeshTools::transform2D(), @ref Magnum::MeshTools::transform2DInPlace(), @ref Magnum::MeshTools::transform3D(), @ref Magnum::MeshTools::transform3DInPlace(), @ref Magnum::MeshTools::transformTextureCoordinates2D(), @ref Magnum::MeshTools::transformTextureCoordinates2DInPlace()
*/
#include "Magnum/Math/DualQuaternion.h"
#include "Magnum/Math/DualComplex.h"
#include "Magnum/MeshTools/visibility.h"
#include "Magnum/Trade/Trade.h"
namespace Magnum { namespace MeshTools {
@ -137,6 +139,162 @@ template<class T, class U> U transformPoints(const T& transformation, U vectors)
return result;
}
/**
@brief Transform 2D positions in a mesh data
@m_since_latest
Expects that the mesh contains a two-dimensional
@ref Trade::MeshAttribute::Position with index @p id. 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.
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 @ref transform3D(), @ref transformTextureCoordinates2D(),
@ref Trade::MeshData::attributeCount(MeshAttribute) const,
@ref Trade::MeshData::attributeFormat(MeshAttribute, UnsignedInt) const
*/
MAGNUM_MESHTOOLS_EXPORT Trade::MeshData transform2D(const Trade::MeshData& data, const Matrix3& transformation, UnsignedInt id = 0);
/**
@brief Transform 2D positions in a mesh data
@m_since_latest
Compared to @ref transform2D(const Trade::MeshData&, const Matrix3&, UnsignedInt)
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);
/**
@brief Transform 2D positions in a mesh data in-place
@m_since_latest
Expects that the mesh has mutable vertex data and contains a two-dimensional
@ref Trade::MeshAttribute::Position with index @p id. To avoid data loss with
packed types, the in-place operation requires the position type to be
@ref VertexFormat::Vector2 --- if you can't guarantee that, use
@ref transform2D() instead. Other attributes, position attributes other than
@p id, and indices (if any) are left untouched.
@see @ref transform3DInPlace(), @ref transformTextureCoordinates2DInPlace(),
@ref Trade::MeshData::vertexDataFlags(),
@ref Trade::MeshData::attributeCount(MeshAttribute) const,
@ref Trade::MeshData::attributeFormat(MeshAttribute, UnsignedInt) const
*/
MAGNUM_MESHTOOLS_EXPORT void transform2DInPlace(Trade::MeshData& data, const Matrix3& transformation, UnsignedInt id = 0);
/**
@brief Transform 3D positions, normals, tangents and bitangents in a mesh data
@m_since_latest
Expects that the mesh contains a three-dimensional
@ref Trade::MeshAttribute::Position with index @p id. If
@ref Trade::MeshAttribute::Normal, @ref Trade::MeshAttribute::Tangent or
@ref Trade::MeshAttribute::Bitangent with index @p id are present as well,
those get transformed with @ref Matrix4::normalMatrix() extracted out of
@p transformation. 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().
@see @ref transform2D(), @ref transformTextureCoordinates2D(),
@ref Trade::MeshData::attributeCount(MeshAttribute) const,
@ref Trade::MeshData::attributeFormat(MeshAttribute, UnsignedInt) const
*/
MAGNUM_MESHTOOLS_EXPORT Trade::MeshData transform3D(const Trade::MeshData& data, const Matrix4& transformation, UnsignedInt id = 0);
/**
@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)
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);
/**
@brief Transform 3D positions, normals, tangents and bitangents in a mesh data in-place
@m_since_latest
Expects that the mesh has mutable vertex data and contains at least a
three-dimensional @ref Trade::MeshAttribute::Position with index @p id;
optionally also @ref Trade::MeshAttribute::Normal,
@ref Trade::MeshAttribute::Tangent or @ref Trade::MeshAttribute::Bitangent with
index @p id, those get transformed with @ref Matrix4::normalMatrix() extracted
out of @p transformation. To avoid data loss with packed types, the in-place
operation requires the position, normal and bitangent types to be
@ref VertexFormat::Vector3 and tangent either @ref VertexFormat::Vector3 or
@ref VertexFormat::Vector4 --- if you can't guarantee that, use
@ref transform3D() instead. Other attributes, position/TBN attributes other
than @p id, and indices (if any) are left untouched.
@see @ref transform2DInPlace(), @ref transformTextureCoordinates2DInPlace(),
@ref Trade::MeshData::vertexDataFlags(),
@ref Trade::MeshData::attributeCount(MeshAttribute) const,
@ref Trade::MeshData::attributeFormat(MeshAttribute, UnsignedInt) const
*/
MAGNUM_MESHTOOLS_EXPORT void transform3DInPlace(Trade::MeshData& data, const Matrix4& transformation, UnsignedInt id = 0);
/**
@brief Transform 2D texture coordinates in a mesh data
@m_since_latest
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.
See also @ref transformTextureCoordinates2D(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 transformTextureCoordinates2DInPlace().
@see @ref transform2D(), @ref transform3D(),
@ref Trade::MeshData::attributeCount(MeshAttribute) const
*/
MAGNUM_MESHTOOLS_EXPORT Trade::MeshData transformTextureCoordinates2D(const Trade::MeshData& data, const Matrix3& transformation, UnsignedInt id = 0);
/**
@brief Transform 2D texture coordinates in a mesh data
@m_since_latest
Compared to @ref transformTextureCoordinates2D(const Trade::MeshData&, const Matrix3&, UnsignedInt)
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);
/**
@brief Transform 2D texture coordinates in a mesh data in-place
@m_since_latest
Expects that the mesh has mutable vertex data and contains a
@ref Trade::MeshAttribute::TextureCoordinates with index @p id. To avoid data
loss with packed types, the in-place operation requires the coordinate type to
be @ref VertexFormat::Vector2 --- if you can't guarantee that, use
@ref transformTextureCoordinates2D() instead. Other attributes, texture
coordinate attributes other than @p id, and indices (if any) are passed through
untouched.
@see @ref transform2DInPlace(), @ref transform3DInPlace(),
@ref Trade::MeshData::vertexDataFlags(),
@ref Trade::MeshData::attributeCount(MeshAttribute) const,
@ref Trade::MeshData::attributeFormat(MeshAttribute, UnsignedInt) const
*/
MAGNUM_MESHTOOLS_EXPORT void transformTextureCoordinates2DInPlace(Trade::MeshData& data, const Matrix3& transformation, UnsignedInt id = 0);
}}
#endif

Loading…
Cancel
Save