Browse Source

MeshTools: unify naming of MeshData arguments.

pull/617/head
Vladimír Vondruš 3 years ago
parent
commit
d06ac097c1
  1. 40
      src/Magnum/MeshTools/Combine.cpp
  2. 8
      src/Magnum/MeshTools/Combine.h
  3. 54
      src/Magnum/MeshTools/Compile.cpp
  4. 18
      src/Magnum/MeshTools/Compile.h
  5. 48
      src/Magnum/MeshTools/CompressIndices.cpp
  6. 4
      src/Magnum/MeshTools/CompressIndices.h
  7. 30
      src/Magnum/MeshTools/Duplicate.cpp
  8. 12
      src/Magnum/MeshTools/Duplicate.h
  9. 148
      src/Magnum/MeshTools/FilterAttributes.cpp
  10. 30
      src/Magnum/MeshTools/FilterAttributes.h
  11. 66
      src/Magnum/MeshTools/GenerateIndices.cpp
  12. 4
      src/Magnum/MeshTools/GenerateIndices.h
  13. 162
      src/Magnum/MeshTools/Interleave.cpp
  14. 46
      src/Magnum/MeshTools/Interleave.h
  15. 90
      src/Magnum/MeshTools/Reference.cpp
  16. 14
      src/Magnum/MeshTools/Reference.h
  17. 20
      src/Magnum/MeshTools/RemoveDuplicates.cpp
  18. 4
      src/Magnum/MeshTools/RemoveDuplicates.h
  19. 196
      src/Magnum/MeshTools/Transform.cpp
  20. 18
      src/Magnum/MeshTools/Transform.h

40
src/Magnum/MeshTools/Combine.cpp

@ -43,20 +43,20 @@ Trade::MeshData combineIndexedImplementation(
#ifndef CORRADE_NO_ASSERT #ifndef CORRADE_NO_ASSERT
const char* assertPrefix, const char* assertPrefix,
#endif #endif
const MeshPrimitive primitive, Containers::Array<char>& combinedIndices, const UnsignedInt indexCount, const UnsignedInt indexStride, const Containers::Iterable<const Trade::MeshData>& data) const MeshPrimitive primitive, Containers::Array<char>& combinedIndices, const UnsignedInt indexCount, const UnsignedInt indexStride, const Containers::Iterable<const Trade::MeshData>& meshes)
{ {
/* Calculate attribute count and vertex stride */ /* Calculate attribute count and vertex stride */
UnsignedInt attributeCount = 0; UnsignedInt attributeCount = 0;
UnsignedInt vertexStride = 0; UnsignedInt vertexStride = 0;
for(std::size_t i = 0; i != data.size(); ++i) { for(std::size_t i = 0; i != meshes.size(); ++i) {
attributeCount += data[i].attributeCount(); attributeCount += meshes[i].attributeCount();
for(UnsignedInt j = 0; j != data[i].attributeCount(); ++j) { for(UnsignedInt j = 0; j != meshes[i].attributeCount(); ++j) {
const VertexFormat format = data[i].attributeFormat(j); const VertexFormat format = meshes[i].attributeFormat(j);
CORRADE_ASSERT(!isVertexFormatImplementationSpecific(format), CORRADE_ASSERT(!isVertexFormatImplementationSpecific(format),
assertPrefix << "attribute" << j << "of mesh" << i << "has an implementation-specific format" << reinterpret_cast<void*>(vertexFormatUnwrap(format)), assertPrefix << "attribute" << j << "of mesh" << i << "has an implementation-specific format" << reinterpret_cast<void*>(vertexFormatUnwrap(format)),
(Trade::MeshData{MeshPrimitive::Points, 0})); (Trade::MeshData{MeshPrimitive::Points, 0}));
vertexStride += vertexFormatSize(format)*Math::max(data[i].attributeArraySize(j), UnsignedShort{1}); vertexStride += vertexFormatSize(format)*Math::max(meshes[i].attributeArraySize(j), UnsignedShort{1});
} }
} }
@ -75,7 +75,7 @@ Trade::MeshData combineIndexedImplementation(
std::size_t indexOffset = 0; std::size_t indexOffset = 0;
std::size_t attributeOffset = 0; std::size_t attributeOffset = 0;
std::size_t vertexOffset = 0; std::size_t vertexOffset = 0;
for(const Trade::MeshData& mesh: data) { for(const Trade::MeshData& mesh: meshes) {
const UnsignedInt indexSize = mesh.isIndexed() ? const UnsignedInt indexSize = mesh.isIndexed() ?
meshIndexTypeSize(mesh.indexType()) : 4; meshIndexTypeSize(mesh.indexType()) : 4;
Containers::StridedArrayView2D<const char> indices{combinedIndices, Containers::StridedArrayView2D<const char> indices{combinedIndices,
@ -110,8 +110,8 @@ Trade::MeshData combineIndexedImplementation(
} }
Trade::MeshData combineIndexedAttributes(const Containers::Iterable<const Trade::MeshData>& data) { Trade::MeshData combineIndexedAttributes(const Containers::Iterable<const Trade::MeshData>& meshes) {
CORRADE_ASSERT(!data.isEmpty(), CORRADE_ASSERT(!meshes.isEmpty(),
"MeshTools::combineIndexedAttributes(): no meshes passed", "MeshTools::combineIndexedAttributes(): no meshes passed",
(Trade::MeshData{MeshPrimitive{}, 0})); (Trade::MeshData{MeshPrimitive{}, 0}));
@ -122,22 +122,22 @@ Trade::MeshData combineIndexedAttributes(const Containers::Iterable<const Trade:
MeshPrimitive primitive{}; MeshPrimitive primitive{};
UnsignedInt indexCount{}; UnsignedInt indexCount{};
UnsignedInt indexStride = 0; UnsignedInt indexStride = 0;
for(std::size_t i = 0; i != data.size(); ++i) { for(std::size_t i = 0; i != meshes.size(); ++i) {
CORRADE_ASSERT(data[i].isIndexed(), CORRADE_ASSERT(meshes[i].isIndexed(),
"MeshTools::combineIndexedAttributes(): data" << i << "is not indexed", "MeshTools::combineIndexedAttributes(): data" << i << "is not indexed",
(Trade::MeshData{MeshPrimitive{}, 0})); (Trade::MeshData{MeshPrimitive{}, 0}));
const MeshIndexType indexType = data[i].indexType(); const MeshIndexType indexType = meshes[i].indexType();
CORRADE_ASSERT(!isMeshIndexTypeImplementationSpecific(indexType), CORRADE_ASSERT(!isMeshIndexTypeImplementationSpecific(indexType),
"MeshTools::combineIndexedAttributes(): data" << i << "has an implementation-specific index type" << reinterpret_cast<void*>(meshIndexTypeUnwrap(indexType)), "MeshTools::combineIndexedAttributes(): data" << i << "has an implementation-specific index type" << reinterpret_cast<void*>(meshIndexTypeUnwrap(indexType)),
(Trade::MeshData{MeshPrimitive{}, 0})); (Trade::MeshData{MeshPrimitive{}, 0}));
if(i == 0) { if(i == 0) {
primitive = data[i].primitive(); primitive = meshes[i].primitive();
indexCount = data[i].indexCount(); indexCount = meshes[i].indexCount();
} else { } else {
CORRADE_ASSERT(data[i].primitive() == primitive, CORRADE_ASSERT(meshes[i].primitive() == primitive,
"MeshTools::combineIndexedAttributes(): data" << i << "is" << data[i].primitive() << "but expected" << primitive, (Trade::MeshData{MeshPrimitive{}, 0})); "MeshTools::combineIndexedAttributes(): data" << i << "is" << meshes[i].primitive() << "but expected" << primitive, (Trade::MeshData{MeshPrimitive{}, 0}));
CORRADE_ASSERT(data[i].indexCount() == indexCount, CORRADE_ASSERT(meshes[i].indexCount() == indexCount,
"MeshTools::combineIndexedAttributes(): data" << i << "has" << data[i].indexCount() << "indices but expected" << indexCount, (Trade::MeshData{MeshPrimitive{}, 0})); "MeshTools::combineIndexedAttributes(): data" << i << "has" << meshes[i].indexCount() << "indices but expected" << indexCount, (Trade::MeshData{MeshPrimitive{}, 0}));
} }
indexStride += meshIndexTypeSize(indexType); indexStride += meshIndexTypeSize(indexType);
} }
@ -149,7 +149,7 @@ Trade::MeshData combineIndexedAttributes(const Containers::Iterable<const Trade:
Containers::Array<char> combinedIndices{NoInit, indexCount*indexStride}; Containers::Array<char> combinedIndices{NoInit, indexCount*indexStride};
{ {
std::size_t indexOffset = 0; std::size_t indexOffset = 0;
for(const Trade::MeshData& mesh: data) { for(const Trade::MeshData& mesh: meshes) {
const UnsignedInt indexSize = meshIndexTypeSize(mesh.indexType()); const UnsignedInt indexSize = meshIndexTypeSize(mesh.indexType());
Containers::StridedArrayView2D<char> dst{combinedIndices, Containers::StridedArrayView2D<char> dst{combinedIndices,
combinedIndices.data() + indexOffset, combinedIndices.data() + indexOffset,
@ -167,7 +167,7 @@ Trade::MeshData combineIndexedAttributes(const Containers::Iterable<const Trade:
#ifndef CORRADE_NO_ASSERT #ifndef CORRADE_NO_ASSERT
"MeshTools::combineIndexedAttributes():", "MeshTools::combineIndexedAttributes():",
#endif #endif
primitive, combinedIndices, indexCount, indexStride, data); primitive, combinedIndices, indexCount, indexStride, meshes);
} }
Trade::MeshData combineFaceAttributes(const Trade::MeshData& mesh, const Trade::MeshData& faceAttributes) { Trade::MeshData combineFaceAttributes(const Trade::MeshData& mesh, const Trade::MeshData& faceAttributes) {

8
src/Magnum/MeshTools/Combine.h

@ -48,8 +48,8 @@ namespace Magnum { namespace MeshTools {
@brief Combine differently indexed attributes into a single mesh @brief Combine differently indexed attributes into a single mesh
@m_since{2020,06} @m_since{2020,06}
Assuming each @p data contains only unique vertex data, creates an indexed mesh Assuming all @p meshes contain only unique vertex data, creates an indexed mesh
that contains all attributes from @p data combined, with duplicate vertices that contains all attributes from @p meshes combined, with duplicate vertices
removed. For example, when you have a position and a normal array, each indexed removed. For example, when you have a position and a normal array, each indexed
with separate indices like this: with separate indices like this:
@ -85,7 +85,7 @@ Vertex data unreferenced by the index buffers are discarded. This means the
function can be also called with just a single argument to compact a mesh with function can be also called with just a single argument to compact a mesh with
a sparse index buffer. a sparse index buffer.
Expects that @p data is non-empty and all data have the same primitive and Expects that @p meshes is non-empty and all data have the same primitive and
index count. All inputs have to be indexed. For non-indexed attributes index count. All inputs have to be indexed. For non-indexed attributes
combining can be done much more efficiently using @ref duplicate(const Trade::MeshData&, Containers::ArrayView<const Trade::MeshAttributeData>), combining can be done much more efficiently using @ref duplicate(const Trade::MeshData&, Containers::ArrayView<const Trade::MeshAttributeData>),
alternatively you can turn a non-indexed attribute to an indexed one first alternatively you can turn a non-indexed attribute to an indexed one first
@ -95,7 +95,7 @@ implementation-specific format.
@see @ref isMeshIndexTypeImplementationSpecific(), @see @ref isMeshIndexTypeImplementationSpecific(),
@ref isVertexFormatImplementationSpecific() @ref isVertexFormatImplementationSpecific()
*/ */
MAGNUM_MESHTOOLS_EXPORT Trade::MeshData combineIndexedAttributes(const Containers::Iterable<const Trade::MeshData>& data); MAGNUM_MESHTOOLS_EXPORT Trade::MeshData combineIndexedAttributes(const Containers::Iterable<const Trade::MeshData>& meshes);
/** /**
@brief Combine per-face attributes into an existing mesh @brief Combine per-face attributes into an existing mesh

54
src/Magnum/MeshTools/Compile.cpp

@ -252,61 +252,61 @@ GL::Mesh compileInternal(const Trade::MeshData& meshData, const CompileFlags fla
} }
GL::Mesh compile(const Trade::MeshData& meshData, GL::Buffer&& indices, GL::Buffer&& vertices) { GL::Mesh compile(const Trade::MeshData& mesh, GL::Buffer&& indices, GL::Buffer&& vertices) {
return compileInternal(meshData, std::move(indices), std::move(vertices), {}); return compileInternal(mesh, std::move(indices), std::move(vertices), {});
} }
GL::Mesh compile(const Trade::MeshData& meshData, GL::Buffer& indices, GL::Buffer& vertices) { GL::Mesh compile(const Trade::MeshData& mesh, GL::Buffer& indices, GL::Buffer& vertices) {
return compileInternal(meshData, GL::Buffer::wrap(indices.id(), GL::Buffer::TargetHint::ElementArray), GL::Buffer::wrap(vertices.id(), GL::Buffer::TargetHint::Array), CompileFlag::NoWarnOnCustomAttributes); return compileInternal(mesh, GL::Buffer::wrap(indices.id(), GL::Buffer::TargetHint::ElementArray), GL::Buffer::wrap(vertices.id(), GL::Buffer::TargetHint::Array), CompileFlag::NoWarnOnCustomAttributes);
} }
GL::Mesh compile(const Trade::MeshData& meshData, GL::Buffer& indices, GL::Buffer&& vertices) { GL::Mesh compile(const Trade::MeshData& mesh, GL::Buffer& indices, GL::Buffer&& vertices) {
return compileInternal(meshData, GL::Buffer::wrap(indices.id(), GL::Buffer::TargetHint::ElementArray), std::move(vertices), CompileFlag::NoWarnOnCustomAttributes); return compileInternal(mesh, GL::Buffer::wrap(indices.id(), GL::Buffer::TargetHint::ElementArray), std::move(vertices), CompileFlag::NoWarnOnCustomAttributes);
} }
GL::Mesh compile(const Trade::MeshData& meshData, GL::Buffer&& indices, GL::Buffer& vertices) { GL::Mesh compile(const Trade::MeshData& mesh, GL::Buffer&& indices, GL::Buffer& vertices) {
return compileInternal(meshData, std::move(indices), GL::Buffer::wrap(vertices.id(), GL::Buffer::TargetHint::Array), CompileFlag::NoWarnOnCustomAttributes); return compileInternal(mesh, std::move(indices), GL::Buffer::wrap(vertices.id(), GL::Buffer::TargetHint::Array), CompileFlag::NoWarnOnCustomAttributes);
} }
GL::Mesh compile(const Trade::MeshData& meshData) { GL::Mesh compile(const Trade::MeshData& mesh) {
return compileInternal(meshData, {}); return compileInternal(mesh, {});
} }
GL::Mesh compile(const Trade::MeshData& meshData, CompileFlags flags) { GL::Mesh compile(const Trade::MeshData& mesh, CompileFlags flags) {
/* If we want to generate normals, prepare a new mesh data and recurse, /* If we want to generate normals, prepare a new mesh data and recurse,
with the flags unset */ with the flags unset */
if(meshData.primitive() == MeshPrimitive::Triangles && (flags & (CompileFlag::GenerateFlatNormals|CompileFlag::GenerateSmoothNormals))) { if(mesh.primitive() == MeshPrimitive::Triangles && (flags & (CompileFlag::GenerateFlatNormals|CompileFlag::GenerateSmoothNormals))) {
CORRADE_ASSERT(meshData.attributeCount(Trade::MeshAttribute::Position), CORRADE_ASSERT(mesh.attributeCount(Trade::MeshAttribute::Position),
"MeshTools::compile(): the mesh has no positions, can't generate normals", GL::Mesh{}); "MeshTools::compile(): the mesh has no positions, can't generate normals", GL::Mesh{});
/* This could fire if we have 2D positions or for packed formats */ /* This could fire if we have 2D positions or for packed formats */
CORRADE_ASSERT(meshData.attributeFormat(Trade::MeshAttribute::Position) == VertexFormat::Vector3, CORRADE_ASSERT(mesh.attributeFormat(Trade::MeshAttribute::Position) == VertexFormat::Vector3,
"MeshTools::compile(): can't generate normals for" << meshData.attributeFormat(Trade::MeshAttribute::Position) << "positions", GL::Mesh{}); "MeshTools::compile(): can't generate normals for" << mesh.attributeFormat(Trade::MeshAttribute::Position) << "positions", GL::Mesh{});
/* If the data already have a normal array, reuse its location, /* If the data already have a normal array, reuse its location,
otherwise mix in an extra one */ otherwise mix in an extra one */
Trade::MeshAttributeData normalAttribute; Trade::MeshAttributeData normalAttribute;
Containers::ArrayView<const Trade::MeshAttributeData> extra; Containers::ArrayView<const Trade::MeshAttributeData> extra;
if(!meshData.hasAttribute(Trade::MeshAttribute::Normal)) { if(!mesh.hasAttribute(Trade::MeshAttribute::Normal)) {
normalAttribute = Trade::MeshAttributeData{ normalAttribute = Trade::MeshAttributeData{
Trade::MeshAttribute::Normal, VertexFormat::Vector3, Trade::MeshAttribute::Normal, VertexFormat::Vector3,
nullptr}; nullptr};
extra = {&normalAttribute, 1}; extra = {&normalAttribute, 1};
/* If we reuse a normal location, expect correct type */ /* If we reuse a normal location, expect correct type */
} else CORRADE_ASSERT(meshData.attributeFormat(Trade::MeshAttribute::Normal) == VertexFormat::Vector3, } else CORRADE_ASSERT(mesh.attributeFormat(Trade::MeshAttribute::Normal) == VertexFormat::Vector3,
"MeshTools::compile(): can't generate normals into" << meshData.attributeFormat(Trade::MeshAttribute::Normal), GL::Mesh{}); "MeshTools::compile(): can't generate normals into" << mesh.attributeFormat(Trade::MeshAttribute::Normal), GL::Mesh{});
/* If we want flat normals, we need to first duplicate everything using /* If we want flat normals, we need to first duplicate everything using
the index buffer. Otherwise just interleave the potential extra the index buffer. Otherwise just interleave the potential extra
normal attribute in. */ normal attribute in. */
Trade::MeshData generated{MeshPrimitive::Points, 0}; Trade::MeshData generated{MeshPrimitive::Points, 0};
if(flags & CompileFlag::GenerateFlatNormals && meshData.isIndexed()) if(flags & CompileFlag::GenerateFlatNormals && mesh.isIndexed())
generated = duplicate(meshData, extra); generated = duplicate(mesh, extra);
else else
generated = interleave(meshData, extra); generated = interleave(mesh, extra);
/* Generate the normals. If we don't have the index buffer, we can only /* Generate the normals. If we don't have the index buffer, we can only
generate flat ones. */ generate flat ones. */
if(flags & CompileFlag::GenerateFlatNormals || !meshData.isIndexed()) if(flags & CompileFlag::GenerateFlatNormals || !mesh.isIndexed())
generateFlatNormalsInto( generateFlatNormalsInto(
generated.attribute<Vector3>(Trade::MeshAttribute::Position), generated.attribute<Vector3>(Trade::MeshAttribute::Position),
generated.mutableAttribute<Vector3>(Trade::MeshAttribute::Normal)); generated.mutableAttribute<Vector3>(Trade::MeshAttribute::Normal));
@ -320,7 +320,7 @@ GL::Mesh compile(const Trade::MeshData& meshData, CompileFlags flags) {
flags &= ~(CompileFlag::GenerateFlatNormals|CompileFlag::GenerateSmoothNormals); flags &= ~(CompileFlag::GenerateFlatNormals|CompileFlag::GenerateSmoothNormals);
CORRADE_INTERNAL_ASSERT(!(flags & ~CompileFlag::NoWarnOnCustomAttributes)); CORRADE_INTERNAL_ASSERT(!(flags & ~CompileFlag::NoWarnOnCustomAttributes));
return compileInternal(meshData, flags); return compileInternal(mesh, flags);
} }
#ifdef MAGNUM_BUILD_DEPRECATED #ifdef MAGNUM_BUILD_DEPRECATED
@ -555,16 +555,16 @@ CORRADE_IGNORE_DEPRECATED_POP
#endif #endif
#ifndef MAGNUM_TARGET_GLES2 #ifndef MAGNUM_TARGET_GLES2
Containers::Pair<UnsignedInt, UnsignedInt> compiledPerVertexJointCount(const Trade::MeshData& meshData) { Containers::Pair<UnsignedInt, UnsignedInt> compiledPerVertexJointCount(const Trade::MeshData& mesh) {
UnsignedInt primaryCount = 0, secondaryCount = 0; UnsignedInt primaryCount = 0, secondaryCount = 0;
for(UnsignedInt i = 0; i != meshData.attributeCount(); ++i) { for(UnsignedInt i = 0; i != mesh.attributeCount(); ++i) {
/* The mesh is expected to have the same count and array size of /* The mesh is expected to have the same count and array size of
JointIds and Weights, so it's enough to do it just for one of JointIds and Weights, so it's enough to do it just for one of
them */ them */
if(meshData.attributeName(i) != Trade::MeshAttribute::JointIds) if(mesh.attributeName(i) != Trade::MeshAttribute::JointIds)
continue; continue;
const UnsignedInt componentCount = meshData.attributeArraySize(i); const UnsignedInt componentCount = mesh.attributeArraySize(i);
for(UnsignedInt j = 0; j < componentCount; j += 4) { for(UnsignedInt j = 0; j < componentCount; j += 4) {
if(!primaryCount) if(!primaryCount)
primaryCount = Math::min(componentCount - j, 4u); primaryCount = Math::min(componentCount - j, 4u);

18
src/Magnum/MeshTools/Compile.h

@ -167,7 +167,7 @@ to access them afterwards. For alternative solutions see the
@see @ref shaders-generic @see @ref shaders-generic
*/ */
MAGNUM_MESHTOOLS_EXPORT GL::Mesh compile(const Trade::MeshData& meshData, CompileFlags flags); MAGNUM_MESHTOOLS_EXPORT GL::Mesh compile(const Trade::MeshData& mesh, CompileFlags flags);
/** /**
* @overload * @overload
@ -176,7 +176,7 @@ MAGNUM_MESHTOOLS_EXPORT GL::Mesh compile(const Trade::MeshData& meshData, Compil
/* Separately because this one doesn't rely on duplicate() / interleave() / /* Separately because this one doesn't rely on duplicate() / interleave() /
generate*Normals() and thus the exe can be smaller when using this function generate*Normals() and thus the exe can be smaller when using this function
directly */ directly */
MAGNUM_MESHTOOLS_EXPORT GL::Mesh compile(const Trade::MeshData& meshData); MAGNUM_MESHTOOLS_EXPORT GL::Mesh compile(const Trade::MeshData& mesh);
/** /**
@brief Compile mesh data using external buffers @brief Compile mesh data using external buffers
@ -196,8 +196,8 @@ by the mesh or not:
@snippet MagnumMeshTools-gl.cpp compile-external-attributes @snippet MagnumMeshTools-gl.cpp compile-external-attributes
If @p meshData is not indexed, the @p indices parameter is ignored --- in that If @p mesh is not indexed, the @p indices parameter is ignored --- in that case
case you can pass a @ref NoCreate "NoCreate"-d instance to avoid allocating an you can pass a @ref NoCreate "NoCreate"-d instance to avoid allocating an
unnecessary OpenGL buffer object. unnecessary OpenGL buffer object.
Compared to @ref compile(const Trade::MeshData&, CompileFlags), this function Compared to @ref compile(const Trade::MeshData&, CompileFlags), this function
@ -205,25 +205,25 @@ implicitly enables the @ref CompileFlag::NoWarnOnCustomAttributes flag,
assuming that custom attributes and attributes with implementation-specific assuming that custom attributes and attributes with implementation-specific
formats are explicitly handled on the application side. formats are explicitly handled on the application side.
*/ */
MAGNUM_MESHTOOLS_EXPORT GL::Mesh compile(const Trade::MeshData& meshData, GL::Buffer& indices, GL::Buffer& vertices); MAGNUM_MESHTOOLS_EXPORT GL::Mesh compile(const Trade::MeshData& mesh, GL::Buffer& indices, GL::Buffer& vertices);
/** /**
* @overload * @overload
* @m_since{2020,06} * @m_since{2020,06}
*/ */
MAGNUM_MESHTOOLS_EXPORT GL::Mesh compile(const Trade::MeshData& meshData, GL::Buffer& indices, GL::Buffer&& vertices); MAGNUM_MESHTOOLS_EXPORT GL::Mesh compile(const Trade::MeshData& mesh, GL::Buffer& indices, GL::Buffer&& vertices);
/** /**
* @overload * @overload
* @m_since{2020,06} * @m_since{2020,06}
*/ */
MAGNUM_MESHTOOLS_EXPORT GL::Mesh compile(const Trade::MeshData& meshData, GL::Buffer&& indices, GL::Buffer& vertices); MAGNUM_MESHTOOLS_EXPORT GL::Mesh compile(const Trade::MeshData& mesh, GL::Buffer&& indices, GL::Buffer& vertices);
/** /**
* @overload * @overload
* @m_since{2020,06} * @m_since{2020,06}
*/ */
MAGNUM_MESHTOOLS_EXPORT GL::Mesh compile(const Trade::MeshData& meshData, GL::Buffer&& indices, GL::Buffer&& vertices); MAGNUM_MESHTOOLS_EXPORT GL::Mesh compile(const Trade::MeshData& mesh, GL::Buffer&& indices, GL::Buffer&& vertices);
#ifdef MAGNUM_BUILD_DEPRECATED #ifdef MAGNUM_BUILD_DEPRECATED
/** /**
@ -333,7 +333,7 @@ similarly with other builtin shaders.
support which is not available in WebGL 1.0, thus neither this function is support which is not available in WebGL 1.0, thus neither this function is
defined in WebGL 1.0 builds. defined in WebGL 1.0 builds.
*/ */
MAGNUM_MESHTOOLS_EXPORT Containers::Pair<UnsignedInt, UnsignedInt> compiledPerVertexJointCount(const Trade::MeshData& meshData); MAGNUM_MESHTOOLS_EXPORT Containers::Pair<UnsignedInt, UnsignedInt> compiledPerVertexJointCount(const Trade::MeshData& mesh);
#endif #endif
}} }}

48
src/Magnum/MeshTools/CompressIndices.cpp

@ -125,61 +125,61 @@ Containers::Pair<Containers::Array<char>, MeshIndexType> compressIndices(const C
return compressIndices(indices, MeshIndexType::UnsignedShort, offset); return compressIndices(indices, MeshIndexType::UnsignedShort, offset);
} }
Trade::MeshData compressIndices(Trade::MeshData&& data, MeshIndexType atLeast) { Trade::MeshData compressIndices(Trade::MeshData&& mesh, MeshIndexType atLeast) {
CORRADE_ASSERT(data.isIndexed(), "MeshTools::compressIndices(): mesh data not indexed", (Trade::MeshData{MeshPrimitive::Triangles, 0})); CORRADE_ASSERT(mesh.isIndexed(), "MeshTools::compressIndices(): mesh data not indexed", (Trade::MeshData{MeshPrimitive::Triangles, 0}));
/* Transfer vertex data as-is, as those don't need any changes. Release if /* Transfer vertex data as-is, as those don't need any changes. Release if
possible. */ possible. */
Containers::Array<char> vertexData; Containers::Array<char> vertexData;
const UnsignedInt vertexCount = data.vertexCount(); const UnsignedInt vertexCount = mesh.vertexCount();
if(data.vertexDataFlags() & Trade::DataFlag::Owned) if(mesh.vertexDataFlags() & Trade::DataFlag::Owned)
vertexData = data.releaseVertexData(); vertexData = mesh.releaseVertexData();
else { else {
vertexData = Containers::Array<char>{NoInit, data.vertexData().size()}; vertexData = Containers::Array<char>{NoInit, mesh.vertexData().size()};
Utility::copy(data.vertexData(), vertexData); Utility::copy(mesh.vertexData(), vertexData);
} }
/* Compress the indices */ /* Compress the indices */
UnsignedInt offset; UnsignedInt offset;
Containers::Pair<Containers::Array<char>, MeshIndexType> result; Containers::Pair<Containers::Array<char>, MeshIndexType> result;
if(data.indexType() == MeshIndexType::UnsignedInt) { if(mesh.indexType() == MeshIndexType::UnsignedInt) {
auto indices = data.indices<UnsignedInt>(); auto indices = mesh.indices<UnsignedInt>();
offset = Math::min(indices); offset = Math::min(indices);
result = compressIndicesImplementation<UnsignedInt>(indices, atLeast, offset); result = compressIndicesImplementation<UnsignedInt>(indices, atLeast, offset);
} else if(data.indexType() == MeshIndexType::UnsignedShort) { } else if(mesh.indexType() == MeshIndexType::UnsignedShort) {
auto indices = data.indices<UnsignedShort>(); auto indices = mesh.indices<UnsignedShort>();
offset = Math::min(indices); offset = Math::min(indices);
result = compressIndicesImplementation<UnsignedShort>(indices, atLeast, offset); result = compressIndicesImplementation<UnsignedShort>(indices, atLeast, offset);
} else { } else {
CORRADE_ASSERT(!isMeshIndexTypeImplementationSpecific(data.indexType()), CORRADE_ASSERT(!isMeshIndexTypeImplementationSpecific(mesh.indexType()),
"MeshTools::compressIndices(): mesh has an implementation-specific index type" << reinterpret_cast<void*>(meshIndexTypeUnwrap(data.indexType())), "MeshTools::compressIndices(): mesh has an implementation-specific index type" << reinterpret_cast<void*>(meshIndexTypeUnwrap(mesh.indexType())),
(Trade::MeshData{MeshPrimitive{}, 0})); (Trade::MeshData{MeshPrimitive{}, 0}));
CORRADE_INTERNAL_ASSERT(data.indexType() == MeshIndexType::UnsignedByte); CORRADE_INTERNAL_ASSERT(mesh.indexType() == MeshIndexType::UnsignedByte);
auto indices = data.indices<UnsignedByte>(); auto indices = mesh.indices<UnsignedByte>();
offset = Math::min(indices); offset = Math::min(indices);
result = compressIndicesImplementation<UnsignedByte>(indices, atLeast, offset); result = compressIndicesImplementation<UnsignedByte>(indices, atLeast, offset);
} }
/* Recreate the attribute array */ /* Recreate the attribute array */
const UnsignedInt newVertexCount = vertexCount - offset; const UnsignedInt newVertexCount = vertexCount - offset;
Containers::Array<Trade::MeshAttributeData> attributeData{data.attributeCount()}; Containers::Array<Trade::MeshAttributeData> attributeData{mesh.attributeCount()};
for(UnsignedInt i = 0, max = attributeData.size(); i != max; ++i) { for(UnsignedInt i = 0, max = attributeData.size(); i != max; ++i) {
const UnsignedInt stride = data.attributeStride(i); const UnsignedInt stride = mesh.attributeStride(i);
attributeData[i] = Trade::MeshAttributeData{data.attributeName(i), attributeData[i] = Trade::MeshAttributeData{mesh.attributeName(i),
data.attributeFormat(i), mesh.attributeFormat(i),
Containers::StridedArrayView1D<const void>{vertexData, vertexData.data() + data.attributeOffset(i) + offset*stride, newVertexCount, stride}, Containers::StridedArrayView1D<const void>{vertexData, vertexData.data() + mesh.attributeOffset(i) + offset*stride, newVertexCount, stride},
data.attributeArraySize(i)}; mesh.attributeArraySize(i)};
} }
Trade::MeshIndexData indices{result.second(), result.first()}; Trade::MeshIndexData indices{result.second(), result.first()};
return Trade::MeshData{data.primitive(), std::move(result.first()), indices, return Trade::MeshData{mesh.primitive(), std::move(result.first()), indices,
std::move(vertexData), std::move(attributeData), newVertexCount}; std::move(vertexData), std::move(attributeData), newVertexCount};
} }
Trade::MeshData compressIndices(const Trade::MeshData& data, MeshIndexType atLeast) { Trade::MeshData compressIndices(const Trade::MeshData& mesh, MeshIndexType atLeast) {
/* Pass through to the && overload, which then decides whether to reuse /* Pass through to the && overload, which then decides whether to reuse
anything based on the DataFlags */ anything based on the DataFlags */
return compressIndices(reference(data), atLeast); return compressIndices(reference(mesh), atLeast);
} }
#ifdef MAGNUM_BUILD_DEPRECATED #ifdef MAGNUM_BUILD_DEPRECATED

4
src/Magnum/MeshTools/CompressIndices.h

@ -159,7 +159,7 @@ The mesh is expected to be indexed and the index type and the @p atLeast
parameter is expected to not be implementation-specific type. parameter is expected to not be implementation-specific type.
@see @ref isMeshIndexTypeImplementationSpecific() @see @ref isMeshIndexTypeImplementationSpecific()
*/ */
MAGNUM_MESHTOOLS_EXPORT Trade::MeshData compressIndices(const Trade::MeshData& data, MeshIndexType atLeast = MeshIndexType::UnsignedShort); MAGNUM_MESHTOOLS_EXPORT Trade::MeshData compressIndices(const Trade::MeshData& mesh, MeshIndexType atLeast = MeshIndexType::UnsignedShort);
/** /**
@brief Compress mesh data indices @brief Compress mesh data indices
@ -171,7 +171,7 @@ owned) to the returned instance instead of making a copy of it. Index and
attribute data are copied always. attribute data are copied always.
@see @ref Trade::MeshData::vertexDataFlags() @see @ref Trade::MeshData::vertexDataFlags()
*/ */
MAGNUM_MESHTOOLS_EXPORT Trade::MeshData compressIndices(Trade::MeshData&& data, MeshIndexType atLeast = MeshIndexType::UnsignedShort); MAGNUM_MESHTOOLS_EXPORT Trade::MeshData compressIndices(Trade::MeshData&& mesh, MeshIndexType atLeast = MeshIndexType::UnsignedShort);
#ifdef MAGNUM_BUILD_DEPRECATED #ifdef MAGNUM_BUILD_DEPRECATED
/** /**

30
src/Magnum/MeshTools/Duplicate.cpp

@ -80,14 +80,14 @@ void duplicateInto(const Containers::StridedArrayView2D<const char>& indices, co
} }
} }
Trade::MeshData duplicate(const Trade::MeshData& data, const Containers::ArrayView<const Trade::MeshAttributeData> extra) { Trade::MeshData duplicate(const Trade::MeshData& mesh, const Containers::ArrayView<const Trade::MeshAttributeData> extra) {
CORRADE_ASSERT(data.isIndexed(), "MeshTools::duplicate(): mesh data not indexed", (Trade::MeshData{MeshPrimitive::Triangles, 0})); CORRADE_ASSERT(mesh.isIndexed(), "MeshTools::duplicate(): mesh data not indexed", (Trade::MeshData{MeshPrimitive::Triangles, 0}));
CORRADE_ASSERT(!isMeshIndexTypeImplementationSpecific(data.indexType()), CORRADE_ASSERT(!isMeshIndexTypeImplementationSpecific(mesh.indexType()),
"MeshTools::duplicate(): mesh has an implementation-specific index type" << reinterpret_cast<void*>(meshIndexTypeUnwrap(data.indexType())), "MeshTools::duplicate(): mesh has an implementation-specific index type" << reinterpret_cast<void*>(meshIndexTypeUnwrap(mesh.indexType())),
(Trade::MeshData{MeshPrimitive{}, 0})); (Trade::MeshData{MeshPrimitive{}, 0}));
#ifndef CORRADE_NO_ASSERT #ifndef CORRADE_NO_ASSERT
for(std::size_t i = 0; i != data.attributeCount(); ++i) { for(std::size_t i = 0; i != mesh.attributeCount(); ++i) {
const VertexFormat format = data.attributeFormat(i); const VertexFormat format = mesh.attributeFormat(i);
CORRADE_ASSERT(!isVertexFormatImplementationSpecific(format), CORRADE_ASSERT(!isVertexFormatImplementationSpecific(format),
"MeshTools::duplicate(): attribute" << i << "has an implementation-specific format" << reinterpret_cast<void*>(vertexFormatUnwrap(format)), "MeshTools::duplicate(): attribute" << i << "has an implementation-specific format" << reinterpret_cast<void*>(vertexFormatUnwrap(format)),
(Trade::MeshData{MeshPrimitive::Points, 0})); (Trade::MeshData{MeshPrimitive::Points, 0}));
@ -101,14 +101,14 @@ Trade::MeshData duplicate(const Trade::MeshData& data, const Containers::ArrayVi
#endif #endif
/* Calculate the layout */ /* Calculate the layout */
Trade::MeshData layout = interleavedLayout(data, data.indexCount(), extra); Trade::MeshData layout = interleavedLayout(mesh, mesh.indexCount(), extra);
/* Copy existing attributes to new locations */ /* Copy existing attributes to new locations */
for(UnsignedInt i = 0; i != data.attributeCount(); ++i) for(UnsignedInt i = 0; i != mesh.attributeCount(); ++i)
duplicateInto(data.indices(), data.attribute(i), layout.mutableAttribute(i)); duplicateInto(mesh.indices(), mesh.attribute(i), layout.mutableAttribute(i));
/* Mix in the extra attributes */ /* Mix in the extra attributes */
UnsignedInt attributeIndex = data.attributeCount(); UnsignedInt attributeIndex = mesh.attributeCount();
for(UnsignedInt i = 0; i != extra.size(); ++i) { for(UnsignedInt i = 0; i != extra.size(); ++i) {
/* Padding, ignore */ /* Padding, ignore */
if(extra[i].format() == VertexFormat{}) continue; if(extra[i].format() == VertexFormat{}) continue;
@ -122,13 +122,13 @@ Trade::MeshData duplicate(const Trade::MeshData& data, const Containers::ArrayVi
/* Copy the attribute in, if it is non-empty, otherwise keep the /* Copy the attribute in, if it is non-empty, otherwise keep the
memory uninitialized */ memory uninitialized */
if(extra[i].data()) { if(extra[i].data()) {
CORRADE_ASSERT(extra[i].data().size() == data.vertexCount(), CORRADE_ASSERT(extra[i].data().size() == mesh.vertexCount(),
"MeshTools::duplicate(): extra attribute" << i << "expected to have" << data.vertexCount() << "items but got" << extra[i].data().size(), "MeshTools::duplicate(): extra attribute" << i << "expected to have" << mesh.vertexCount() << "items but got" << extra[i].data().size(),
(Trade::MeshData{MeshPrimitive::Triangles, 0})); (Trade::MeshData{MeshPrimitive::Triangles, 0}));
const Containers::StridedArrayView2D<const char> attributeData = const Containers::StridedArrayView2D<const char> attributeData =
Containers::arrayCast<2, const char>(extra[i].data(), Containers::arrayCast<2, const char>(extra[i].data(),
vertexFormatSize(extra[i].format())*Math::max(extra[i].arraySize(), UnsignedShort{1})); vertexFormatSize(extra[i].format())*Math::max(extra[i].arraySize(), UnsignedShort{1}));
duplicateInto(data.indices(), attributeData, layout.mutableAttribute(attributeIndex)); duplicateInto(mesh.indices(), attributeData, layout.mutableAttribute(attributeIndex));
} }
++attributeIndex; ++attributeIndex;
@ -137,8 +137,8 @@ Trade::MeshData duplicate(const Trade::MeshData& data, const Containers::ArrayVi
return layout; return layout;
} }
Trade::MeshData duplicate(const Trade::MeshData& data, std::initializer_list<Trade::MeshAttributeData> extra) { Trade::MeshData duplicate(const Trade::MeshData& mesh, std::initializer_list<Trade::MeshAttributeData> extra) {
return duplicate(data, Containers::arrayView(extra)); return duplicate(mesh, Containers::arrayView(extra));
} }
}} }}

12
src/Magnum/MeshTools/Duplicate.h

@ -135,8 +135,8 @@ MAGNUM_MESHTOOLS_EXPORT void duplicateInto(const Containers::StridedArrayView2D<
@brief Duplicate indexed mesh data @brief Duplicate indexed mesh data
@m_since{2020,06} @m_since{2020,06}
Returns a copy of @p data that's not indexed and has all attributes interleaved Returns a copy of @p mesh that's not indexed and has all attributes interleaved
and duplicated according to @p data's index buffer. The @p extra attributes, if and duplicated according to @p mesh's index buffer. The @p extra attributes, if
any, are duplicated and interleaved together with existing attributes (or, in any, are duplicated and interleaved together with existing attributes (or, in
case the attribute view is empty, only the corresponding space for given case the attribute view is empty, only the corresponding space for given
attribute type is reserved, with memory left uninitialized). The data layouting attribute type is reserved, with memory left uninitialized). The data layouting
@ -144,21 +144,21 @@ is done by @ref interleavedLayout(), see its documentation for detailed
behavior description. Note that offset-only @ref Trade::MeshAttributeData behavior description. Note that offset-only @ref Trade::MeshAttributeData
instances are not supported in the @p extra array. instances are not supported in the @p extra array.
Expects that @p data is indexed with a non-implementation-specific index type Expects that @p mesh is indexed with a non-implementation-specific index type
and each attribute in @p extra has either the same amount of elements as and each attribute in @p extra has either the same amount of elements as
@p data vertex count (*not* index count) or has none. All attributes are @p mesh vertex count (*not* index count) or has none. All attributes are
expected to not have an implementation-specific format. expected to not have an implementation-specific format.
@see @ref isMeshIndexTypeImplementationSpecific(), @see @ref isMeshIndexTypeImplementationSpecific(),
@ref isVertexFormatImplementationSpecific(), @ref isVertexFormatImplementationSpecific(),
@ref Trade::MeshData::attributeData() @ref Trade::MeshData::attributeData()
*/ */
MAGNUM_MESHTOOLS_EXPORT Trade::MeshData duplicate(const Trade::MeshData& data, Containers::ArrayView<const Trade::MeshAttributeData> extra = {}); MAGNUM_MESHTOOLS_EXPORT Trade::MeshData duplicate(const Trade::MeshData& mesh, Containers::ArrayView<const Trade::MeshAttributeData> extra = {});
/** /**
* @overload * @overload
* @m_since{2020,06} * @m_since{2020,06}
*/ */
MAGNUM_MESHTOOLS_EXPORT Trade::MeshData duplicate(const Trade::MeshData& data, std::initializer_list<Trade::MeshAttributeData> extra); MAGNUM_MESHTOOLS_EXPORT Trade::MeshData duplicate(const Trade::MeshData& mesh, std::initializer_list<Trade::MeshAttributeData> extra);
template<class IndexType, class T> inline void duplicateInto(const Containers::StridedArrayView1D<const IndexType>& indices, const Containers::StridedArrayView1D<const T>& data, const Containers::StridedArrayView1D<T>& out) { template<class IndexType, class T> inline void duplicateInto(const Containers::StridedArrayView1D<const IndexType>& indices, const Containers::StridedArrayView1D<const T>& data, const Containers::StridedArrayView1D<T>& out) {
duplicateInto(indices, Containers::arrayCast<2, const char>(data), Containers::arrayCast<2, char>(out)); duplicateInto(indices, Containers::arrayCast<2, const char>(data), Containers::arrayCast<2, char>(out));

148
src/Magnum/MeshTools/FilterAttributes.cpp

@ -49,7 +49,7 @@ bool hasAttribute(const Containers::ArrayView<const UnsignedInt> attributes, con
} }
Trade::MeshData filterOnlyAttributes(const Trade::MeshData& data, const Containers::ArrayView<const Trade::MeshAttribute> attributes) { Trade::MeshData filterOnlyAttributes(const Trade::MeshData& mesh, const Containers::ArrayView<const Trade::MeshAttribute> attributes) {
/* Not asserting here for existence of attributes since that'd be another /* Not asserting here for existence of attributes since that'd be another
O(n^2) operation */ O(n^2) operation */
/** @todo but that's not consistent with the ID-based variant, or maybe do /** @todo but that's not consistent with the ID-based variant, or maybe do
@ -58,10 +58,10 @@ Trade::MeshData filterOnlyAttributes(const Trade::MeshData& data, const Containe
/* Pick just attributes from the list */ /* Pick just attributes from the list */
Containers::Array<Trade::MeshAttributeData> filtered; Containers::Array<Trade::MeshAttributeData> filtered;
arrayReserve(filtered, data.attributeCount()); arrayReserve(filtered, mesh.attributeCount());
for(UnsignedInt i = 0; i != data.attributeCount(); ++i) { for(UnsignedInt i = 0; i != mesh.attributeCount(); ++i) {
if(hasAttribute(attributes, data.attributeName(i))) if(hasAttribute(attributes, mesh.attributeName(i)))
arrayAppend(filtered, data.attributeData(i)); arrayAppend(filtered, mesh.attributeData(i));
} }
/* Convert back to a default deleter to make this usable in plugins */ /* Convert back to a default deleter to make this usable in plugins */
@ -72,37 +72,37 @@ Trade::MeshData filterOnlyAttributes(const Trade::MeshData& data, const Containe
Trade::meshIndexData{data.indexType(), view} Trade::meshIndexData{data.indexType(), view}
because asking for index type would assert on non-indexed meshes. */ because asking for index type would assert on non-indexed meshes. */
Trade::MeshIndexData indices; Trade::MeshIndexData indices;
if(data.isIndexed()) indices = Trade::MeshIndexData{ if(mesh.isIndexed()) indices = Trade::MeshIndexData{
data.indexType(), mesh.indexType(),
Containers::StridedArrayView1D<const void>{ Containers::StridedArrayView1D<const void>{
data.indexData(), mesh.indexData(),
data.indexData().data() + data.indexOffset(), mesh.indexData().data() + mesh.indexOffset(),
data.indexCount(), mesh.indexCount(),
data.indexStride()}}; mesh.indexStride()}};
return Trade::MeshData{data.primitive(), return Trade::MeshData{mesh.primitive(),
{}, data.indexData(), indices, {}, mesh.indexData(), indices,
{}, data.vertexData(), std::move(filtered), {}, mesh.vertexData(), std::move(filtered),
data.vertexCount()}; mesh.vertexCount()};
} }
Trade::MeshData filterOnlyAttributes(const Trade::MeshData& data, std::initializer_list<Trade::MeshAttribute> attributes) { Trade::MeshData filterOnlyAttributes(const Trade::MeshData& mesh, std::initializer_list<Trade::MeshAttribute> attributes) {
return filterOnlyAttributes(data, Containers::arrayView(attributes)); return filterOnlyAttributes(mesh, Containers::arrayView(attributes));
} }
Trade::MeshData filterOnlyAttributes(const Trade::MeshData& data, const Containers::ArrayView<const UnsignedInt> attributes) { Trade::MeshData filterOnlyAttributes(const Trade::MeshData& mesh, const Containers::ArrayView<const UnsignedInt> attributes) {
#ifndef CORRADE_NO_ASSERT #ifndef CORRADE_NO_ASSERT
for(const UnsignedInt i: attributes) CORRADE_ASSERT(i < data.attributeCount(), for(const UnsignedInt i: attributes) CORRADE_ASSERT(i < mesh.attributeCount(),
"MeshTools::filterOnlyAttributes(): index" << i << "out of range for" << data.attributeCount() << "attributes", "MeshTools::filterOnlyAttributes(): index" << i << "out of range for" << mesh.attributeCount() << "attributes",
(Trade::MeshData{MeshPrimitive{}, 0})); (Trade::MeshData{MeshPrimitive{}, 0}));
#endif #endif
/* Pick just attributes from the list */ /* Pick just attributes from the list */
Containers::Array<Trade::MeshAttributeData> filtered; Containers::Array<Trade::MeshAttributeData> filtered;
arrayReserve(filtered, data.attributeCount()); arrayReserve(filtered, mesh.attributeCount());
for(UnsignedInt i = 0; i != data.attributeCount(); ++i) { for(UnsignedInt i = 0; i != mesh.attributeCount(); ++i) {
if(hasAttribute(attributes, i)) if(hasAttribute(attributes, i))
arrayAppend(filtered, data.attributeData(i)); arrayAppend(filtered, mesh.attributeData(i));
} }
/* Convert back to a default deleter to make this usable in plugins */ /* Convert back to a default deleter to make this usable in plugins */
@ -113,25 +113,25 @@ Trade::MeshData filterOnlyAttributes(const Trade::MeshData& data, const Containe
Trade::meshIndexData{data.indexType(), view} Trade::meshIndexData{data.indexType(), view}
because asking for index type would assert on non-indexed meshes. */ because asking for index type would assert on non-indexed meshes. */
Trade::MeshIndexData indices; Trade::MeshIndexData indices;
if(data.isIndexed()) indices = Trade::MeshIndexData{ if(mesh.isIndexed()) indices = Trade::MeshIndexData{
data.indexType(), mesh.indexType(),
Containers::StridedArrayView1D<const void>{ Containers::StridedArrayView1D<const void>{
data.indexData(), mesh.indexData(),
data.indexData().data() + data.indexOffset(), mesh.indexData().data() + mesh.indexOffset(),
data.indexCount(), mesh.indexCount(),
data.indexStride()}}; mesh.indexStride()}};
return Trade::MeshData{data.primitive(), return Trade::MeshData{mesh.primitive(),
{}, data.indexData(), indices, {}, mesh.indexData(), indices,
{}, data.vertexData(), std::move(filtered), {}, mesh.vertexData(), std::move(filtered),
data.vertexCount()}; mesh.vertexCount()};
} }
Trade::MeshData filterOnlyAttributes(const Trade::MeshData& data, std::initializer_list<UnsignedInt> attributes) { Trade::MeshData filterOnlyAttributes(const Trade::MeshData& mesh, std::initializer_list<UnsignedInt> attributes) {
return filterOnlyAttributes(data, Containers::arrayView(attributes)); return filterOnlyAttributes(mesh, Containers::arrayView(attributes));
} }
Trade::MeshData filterExceptAttributes(const Trade::MeshData& data, const Containers::ArrayView<const Trade::MeshAttribute> attributes) { Trade::MeshData filterExceptAttributes(const Trade::MeshData& mesh, const Containers::ArrayView<const Trade::MeshAttribute> attributes) {
/* Not asserting here for existence of attributes since that'd be another /* Not asserting here for existence of attributes since that'd be another
O(n^2) operation */ O(n^2) operation */
/** @todo but that's not consistent with the ID-based variant, or maybe do /** @todo but that's not consistent with the ID-based variant, or maybe do
@ -140,10 +140,10 @@ Trade::MeshData filterExceptAttributes(const Trade::MeshData& data, const Contai
/* Pick just attributes from the list */ /* Pick just attributes from the list */
Containers::Array<Trade::MeshAttributeData> filtered; Containers::Array<Trade::MeshAttributeData> filtered;
arrayReserve(filtered, data.attributeCount()); arrayReserve(filtered, mesh.attributeCount());
for(UnsignedInt i = 0; i != data.attributeCount(); ++i) { for(UnsignedInt i = 0; i != mesh.attributeCount(); ++i) {
if(!hasAttribute(attributes, data.attributeName(i))) if(!hasAttribute(attributes, mesh.attributeName(i)))
arrayAppend(filtered, data.attributeData(i)); arrayAppend(filtered, mesh.attributeData(i));
} }
/* Convert back to a default deleter to make this usable in plugins */ /* Convert back to a default deleter to make this usable in plugins */
@ -154,37 +154,37 @@ Trade::MeshData filterExceptAttributes(const Trade::MeshData& data, const Contai
Trade::meshIndexData{data.indexType(), view} Trade::meshIndexData{data.indexType(), view}
because asking for index type would assert on non-indexed meshes. */ because asking for index type would assert on non-indexed meshes. */
Trade::MeshIndexData indices; Trade::MeshIndexData indices;
if(data.isIndexed()) indices = Trade::MeshIndexData{ if(mesh.isIndexed()) indices = Trade::MeshIndexData{
data.indexType(), mesh.indexType(),
Containers::StridedArrayView1D<const void>{ Containers::StridedArrayView1D<const void>{
data.indexData(), mesh.indexData(),
data.indexData().data() + data.indexOffset(), mesh.indexData().data() + mesh.indexOffset(),
data.indexCount(), mesh.indexCount(),
data.indexStride()}}; mesh.indexStride()}};
return Trade::MeshData{data.primitive(), return Trade::MeshData{mesh.primitive(),
{}, data.indexData(), indices, {}, mesh.indexData(), indices,
{}, data.vertexData(), std::move(filtered), {}, mesh.vertexData(), std::move(filtered),
data.vertexCount()}; mesh.vertexCount()};
} }
Trade::MeshData filterExceptAttributes(const Trade::MeshData& data, std::initializer_list<Trade::MeshAttribute> attributes) { Trade::MeshData filterExceptAttributes(const Trade::MeshData& mesh, std::initializer_list<Trade::MeshAttribute> attributes) {
return filterExceptAttributes(data, Containers::arrayView(attributes)); return filterExceptAttributes(mesh, Containers::arrayView(attributes));
} }
Trade::MeshData filterExceptAttributes(const Trade::MeshData& data, const Containers::ArrayView<const UnsignedInt> attributes) { Trade::MeshData filterExceptAttributes(const Trade::MeshData& mesh, const Containers::ArrayView<const UnsignedInt> attributes) {
#ifndef CORRADE_NO_ASSERT #ifndef CORRADE_NO_ASSERT
for(const UnsignedInt i: attributes) CORRADE_ASSERT(i < data.attributeCount(), for(const UnsignedInt i: attributes) CORRADE_ASSERT(i < mesh.attributeCount(),
"MeshTools::filterExceptAttributes(): index" << i << "out of range for" << data.attributeCount() << "attributes", "MeshTools::filterExceptAttributes(): index" << i << "out of range for" << mesh.attributeCount() << "attributes",
(Trade::MeshData{MeshPrimitive{}, 0})); (Trade::MeshData{MeshPrimitive{}, 0}));
#endif #endif
/* Pick just attributes from the list */ /* Pick just attributes from the list */
Containers::Array<Trade::MeshAttributeData> filtered; Containers::Array<Trade::MeshAttributeData> filtered;
arrayReserve(filtered, data.attributeCount()); arrayReserve(filtered, mesh.attributeCount());
for(UnsignedInt i = 0; i != data.attributeCount(); ++i) { for(UnsignedInt i = 0; i != mesh.attributeCount(); ++i) {
if(!hasAttribute(attributes, i)) if(!hasAttribute(attributes, i))
arrayAppend(filtered, data.attributeData(i)); arrayAppend(filtered, mesh.attributeData(i));
} }
/* Convert back to a default deleter to make this usable in plugins */ /* Convert back to a default deleter to make this usable in plugins */
@ -195,22 +195,22 @@ Trade::MeshData filterExceptAttributes(const Trade::MeshData& data, const Contai
Trade::meshIndexData{data.indexType(), view} Trade::meshIndexData{data.indexType(), view}
because asking for index type would assert on non-indexed meshes. */ because asking for index type would assert on non-indexed meshes. */
Trade::MeshIndexData indices; Trade::MeshIndexData indices;
if(data.isIndexed()) indices = Trade::MeshIndexData{ if(mesh.isIndexed()) indices = Trade::MeshIndexData{
data.indexType(), mesh.indexType(),
Containers::StridedArrayView1D<const void>{ Containers::StridedArrayView1D<const void>{
data.indexData(), mesh.indexData(),
data.indexData().data() + data.indexOffset(), mesh.indexData().data() + mesh.indexOffset(),
data.indexCount(), mesh.indexCount(),
data.indexStride()}}; mesh.indexStride()}};
return Trade::MeshData{data.primitive(), return Trade::MeshData{mesh.primitive(),
{}, data.indexData(), indices, {}, mesh.indexData(), indices,
{}, data.vertexData(), std::move(filtered), {}, mesh.vertexData(), std::move(filtered),
data.vertexCount()}; mesh.vertexCount()};
} }
Trade::MeshData filterExceptAttributes(const Trade::MeshData& data, std::initializer_list<UnsignedInt> attributes) { Trade::MeshData filterExceptAttributes(const Trade::MeshData& mesh, std::initializer_list<UnsignedInt> attributes) {
return filterExceptAttributes(data, Containers::arrayView(attributes)); return filterExceptAttributes(mesh, Containers::arrayView(attributes));
} }
}} }}

30
src/Magnum/MeshTools/FilterAttributes.h

@ -41,10 +41,10 @@ namespace Magnum { namespace MeshTools {
@brief Filter a mesh to contain only the selected subset of named attributes @brief Filter a mesh to contain only the selected subset of named attributes
@m_since_latest @m_since_latest
Returns a non-owning reference to the vertex and index buffer from @p data with Returns a non-owning reference to the vertex and index buffer from @p mesh with
only the attributes that are listed in @p attributes. The index buffer, if only the attributes that are listed in @p attributes. The index buffer, if
present, is left untouched. Attributes from the list that are not present in present, is left untouched. Attributes from the list that are not present in
@p data are skipped. All duplicates of a listed attribute are kept --- if you @p mesh are skipped. All duplicates of a listed attribute are kept --- if you
want a different behavior, use the @ref filterOnlyAttributes(const Trade::MeshData&, Containers::ArrayView<const UnsignedInt>) want a different behavior, use the @ref filterOnlyAttributes(const Trade::MeshData&, Containers::ArrayView<const UnsignedInt>)
overload and pick attributes by their IDs instead. overload and pick attributes by their IDs instead.
@ -54,19 +54,19 @@ the output to @ref interleave(const Trade::MeshData&, Containers::ArrayView<cons
without @ref InterleaveFlag::PreserveInterleavedAttributes set. without @ref InterleaveFlag::PreserveInterleavedAttributes set.
@see @ref reference() @see @ref reference()
*/ */
MAGNUM_MESHTOOLS_EXPORT Trade::MeshData filterOnlyAttributes(const Trade::MeshData& data, Containers::ArrayView<const Trade::MeshAttribute> attributes); MAGNUM_MESHTOOLS_EXPORT Trade::MeshData filterOnlyAttributes(const Trade::MeshData& mesh, Containers::ArrayView<const Trade::MeshAttribute> attributes);
/** /**
* @overload * @overload
* @m_since_latest * @m_since_latest
*/ */
MAGNUM_MESHTOOLS_EXPORT Trade::MeshData filterOnlyAttributes(const Trade::MeshData& data, std::initializer_list<Trade::MeshAttribute> attributes); MAGNUM_MESHTOOLS_EXPORT Trade::MeshData filterOnlyAttributes(const Trade::MeshData& mesh, std::initializer_list<Trade::MeshAttribute> attributes);
/** /**
@brief Filter a mesh to contain only the selected subset of attributes @brief Filter a mesh to contain only the selected subset of attributes
@m_since_latest @m_since_latest
Returns a non-owning reference to the vertex and index buffer from @p data with Returns a non-owning reference to the vertex and index buffer from @p mesh with
only the attribute IDs listed in @p attributes. IDs specified more than once only the attribute IDs listed in @p attributes. IDs specified more than once
don't result in given attribute being added multiple times. The index buffer, don't result in given attribute being added multiple times. The index buffer,
if present, is left untouched. All attribute IDs are expected to be smaller if present, is left untouched. All attribute IDs are expected to be smaller
@ -78,44 +78,44 @@ the output to @ref interleave(const Trade::MeshData&, Containers::ArrayView<cons
without @ref InterleaveFlag::PreserveInterleavedAttributes set. without @ref InterleaveFlag::PreserveInterleavedAttributes set.
@see @ref reference() @see @ref reference()
*/ */
MAGNUM_MESHTOOLS_EXPORT Trade::MeshData filterOnlyAttributes(const Trade::MeshData& data, Containers::ArrayView<const UnsignedInt> attributes); MAGNUM_MESHTOOLS_EXPORT Trade::MeshData filterOnlyAttributes(const Trade::MeshData& mesh, Containers::ArrayView<const UnsignedInt> attributes);
/** /**
* @overload * @overload
* @m_since_latest * @m_since_latest
*/ */
MAGNUM_MESHTOOLS_EXPORT Trade::MeshData filterOnlyAttributes(const Trade::MeshData& data, std::initializer_list<UnsignedInt> attributes); MAGNUM_MESHTOOLS_EXPORT Trade::MeshData filterOnlyAttributes(const Trade::MeshData& mesh, std::initializer_list<UnsignedInt> attributes);
/** /**
@brief Filter a mesh to contain everything except the selected subset of named attributes @brief Filter a mesh to contain everything except the selected subset of named attributes
@m_since_latest @m_since_latest
Returns a non-owning reference to the vertex and index buffer from @p data with Returns a non-owning reference to the vertex and index buffer from @p mesh with
only the attributes that are not listed in @p attributes. The index buffer, if only the attributes that are not listed in @p attributes. The index buffer, if
present, is left untouched. Attributes from the list that are not present in present, is left untouched. Attributes from the list that are not present in
@p data are skipped. All duplicates of a listed attribute are removed --- if @p mesh are skipped. All duplicates of a listed attribute are removed --- if
you want a different behavior, use the @ref filterExceptAttributes(const Trade::MeshData&, Containers::ArrayView<const UnsignedInt>) you want a different behavior, use the @ref filterExceptAttributes(const Trade::MeshData&, Containers::ArrayView<const UnsignedInt>)
overload and pick attributes by their IDs instead. If @p attributes is empty, overload and pick attributes by their IDs instead. If @p attributes is empty,
the behavior is equivalent to @ref reference(). the behavior is equivalent to @ref reference().
This function only operates on the attribute metadata --- if you'd like to have This function only operates on the attribute metadata --- if you'd like to have
the vertex data repacked to contain just the remaining attributes as well, pass the vertex mesh repacked to contain just the remaining attributes as well, pass
the output to @ref interleave(const Trade::MeshData&, Containers::ArrayView<const Trade::MeshAttributeData>, InterleaveFlags) "interleave()" the output to @ref interleave(const Trade::MeshData&, Containers::ArrayView<const Trade::MeshAttributeData>, InterleaveFlags) "interleave()"
without @ref InterleaveFlag::PreserveInterleavedAttributes set. without @ref InterleaveFlag::PreserveInterleavedAttributes set.
*/ */
MAGNUM_MESHTOOLS_EXPORT Trade::MeshData filterExceptAttributes(const Trade::MeshData& data, Containers::ArrayView<const Trade::MeshAttribute> attributes); MAGNUM_MESHTOOLS_EXPORT Trade::MeshData filterExceptAttributes(const Trade::MeshData& mesh, Containers::ArrayView<const Trade::MeshAttribute> attributes);
/** /**
* @overload * @overload
* @m_since_latest * @m_since_latest
*/ */
MAGNUM_MESHTOOLS_EXPORT Trade::MeshData filterExceptAttributes(const Trade::MeshData& data, std::initializer_list<Trade::MeshAttribute> attributes); MAGNUM_MESHTOOLS_EXPORT Trade::MeshData filterExceptAttributes(const Trade::MeshData& mesh, std::initializer_list<Trade::MeshAttribute> attributes);
/** /**
@brief Filter a mesh to contain everything except the selected subset of attributes @brief Filter a mesh to contain everything except the selected subset of attributes
@m_since_latest @m_since_latest
Returns a non-owning reference to the vertex and index buffer from @p data with Returns a non-owning reference to the vertex and index buffer from @p mesh with
only the attribute IDs that are not listed in @p attributes. IDs specified only the attribute IDs that are not listed in @p attributes. IDs specified
multiple times behave like if specified just once. The index buffer, if multiple times behave like if specified just once. The index buffer, if
present, is left untouched. All attribute IDs are expected to be smaller than present, is left untouched. All attribute IDs are expected to be smaller than
@ -127,13 +127,13 @@ the vertex data repacked to contain just the remaining attributes as well, pass
the output to @ref interleave(const Trade::MeshData&, Containers::ArrayView<const Trade::MeshAttributeData>, InterleaveFlags) "interleave()" the output to @ref interleave(const Trade::MeshData&, Containers::ArrayView<const Trade::MeshAttributeData>, InterleaveFlags) "interleave()"
without @ref InterleaveFlag::PreserveInterleavedAttributes set. without @ref InterleaveFlag::PreserveInterleavedAttributes set.
*/ */
MAGNUM_MESHTOOLS_EXPORT Trade::MeshData filterExceptAttributes(const Trade::MeshData& data, Containers::ArrayView<const UnsignedInt> attributes); MAGNUM_MESHTOOLS_EXPORT Trade::MeshData filterExceptAttributes(const Trade::MeshData& mesh, Containers::ArrayView<const UnsignedInt> attributes);
/** /**
* @overload * @overload
* @m_since_latest * @m_since_latest
*/ */
MAGNUM_MESHTOOLS_EXPORT Trade::MeshData filterExceptAttributes(const Trade::MeshData& data, std::initializer_list<UnsignedInt> attributes); MAGNUM_MESHTOOLS_EXPORT Trade::MeshData filterExceptAttributes(const Trade::MeshData& mesh, std::initializer_list<UnsignedInt> attributes);
}} }}

66
src/Magnum/MeshTools/GenerateIndices.cpp

@ -576,77 +576,77 @@ void generateQuadIndicesInto(const Containers::StridedArrayView1D<const Vector3>
return generateQuadIndicesIntoImplementation(positions, quads, output); return generateQuadIndicesIntoImplementation(positions, quads, output);
} }
Trade::MeshData generateIndices(Trade::MeshData&& data) { Trade::MeshData generateIndices(Trade::MeshData&& mesh) {
CORRADE_ASSERT(!data.isIndexed() || !isMeshIndexTypeImplementationSpecific(data.indexType()), CORRADE_ASSERT(!mesh.isIndexed() || !isMeshIndexTypeImplementationSpecific(mesh.indexType()),
"MeshTools::generateIndices(): mesh has an implementation-specific index type" << reinterpret_cast<void*>(meshIndexTypeUnwrap(data.indexType())), "MeshTools::generateIndices(): mesh has an implementation-specific index type" << reinterpret_cast<void*>(meshIndexTypeUnwrap(mesh.indexType())),
(Trade::MeshData{MeshPrimitive{}, 0})); (Trade::MeshData{MeshPrimitive{}, 0}));
const UnsignedInt vertexCount = data.vertexCount(); const UnsignedInt vertexCount = mesh.vertexCount();
#ifndef CORRADE_NO_ASSERT #ifndef CORRADE_NO_ASSERT
UnsignedInt minVertexCount; UnsignedInt minVertexCount;
if(data.primitive() == MeshPrimitive::LineStrip || if(mesh.primitive() == MeshPrimitive::LineStrip ||
data.primitive() == MeshPrimitive::LineLoop) { mesh.primitive() == MeshPrimitive::LineLoop) {
minVertexCount = 2; minVertexCount = 2;
} else if(data.primitive() == MeshPrimitive::TriangleStrip || } else if(mesh.primitive() == MeshPrimitive::TriangleStrip ||
data.primitive() == MeshPrimitive::TriangleFan) { mesh.primitive() == MeshPrimitive::TriangleFan) {
minVertexCount = 3; minVertexCount = 3;
} else CORRADE_ASSERT_UNREACHABLE("MeshTools::generateIndices(): invalid primitive" << data.primitive(), } else CORRADE_ASSERT_UNREACHABLE("MeshTools::generateIndices(): invalid primitive" << mesh.primitive(),
(Trade::MeshData{MeshPrimitive::Triangles, 0})); (Trade::MeshData{MeshPrimitive::Triangles, 0}));
CORRADE_ASSERT(vertexCount == 0 || vertexCount >= minVertexCount, CORRADE_ASSERT(vertexCount == 0 || vertexCount >= minVertexCount,
"MeshTools::generateIndices(): expected either zero or at least" << minVertexCount << "vertices for" << data.primitive() << Debug::nospace << ", got" << vertexCount, "MeshTools::generateIndices(): expected either zero or at least" << minVertexCount << "vertices for" << mesh.primitive() << Debug::nospace << ", got" << vertexCount,
(Trade::MeshData{MeshPrimitive::Triangles, 0})); (Trade::MeshData{MeshPrimitive::Triangles, 0}));
#endif #endif
/* Transfer vertex / attribute data as-is, as those don't need any changes. /* Transfer vertex / attribute data as-is, as those don't need any changes.
Release if possible. */ Release if possible. */
Containers::Array<char> vertexData; Containers::Array<char> vertexData;
if(data.vertexDataFlags() & Trade::DataFlag::Owned) if(mesh.vertexDataFlags() & Trade::DataFlag::Owned)
vertexData = data.releaseVertexData(); vertexData = mesh.releaseVertexData();
else { else {
vertexData = Containers::Array<char>{NoInit, data.vertexData().size()}; vertexData = Containers::Array<char>{NoInit, mesh.vertexData().size()};
Utility::copy(data.vertexData(), vertexData); Utility::copy(mesh.vertexData(), vertexData);
} }
/* Recreate the attribute array with views on the new vertexData */ /* Recreate the attribute array with views on the new vertexData */
/** @todo if the vertex data were moved and this array is owned, it /** @todo if the vertex data were moved and this array is owned, it
wouldn't need to be recreated, but the logic is a bit complex */ wouldn't need to be recreated, but the logic is a bit complex */
Containers::Array<Trade::MeshAttributeData> attributeData{data.attributeCount()}; Containers::Array<Trade::MeshAttributeData> attributeData{mesh.attributeCount()};
for(UnsignedInt i = 0, max = attributeData.size(); i != max; ++i) { for(UnsignedInt i = 0, max = attributeData.size(); i != max; ++i) {
attributeData[i] = Trade::MeshAttributeData{data.attributeName(i), attributeData[i] = Trade::MeshAttributeData{mesh.attributeName(i),
data.attributeFormat(i), mesh.attributeFormat(i),
Containers::StridedArrayView1D<const void>{vertexData, vertexData.data() + data.attributeOffset(i), vertexCount, data.attributeStride(i)}, Containers::StridedArrayView1D<const void>{vertexData, vertexData.data() + mesh.attributeOffset(i), vertexCount, mesh.attributeStride(i)},
data.attributeArraySize(i)}; mesh.attributeArraySize(i)};
} }
/* Generate the index array */ /* Generate the index array */
MeshPrimitive primitive; MeshPrimitive primitive;
Containers::Array<char> indexData; Containers::Array<char> indexData;
if(data.primitive() == MeshPrimitive::LineStrip) { if(mesh.primitive() == MeshPrimitive::LineStrip) {
primitive = MeshPrimitive::Lines; primitive = MeshPrimitive::Lines;
indexData = Containers::Array<char>{NoInit, 2*(Math::max(vertexCount, 1u) - 1)*sizeof(UnsignedInt)}; indexData = Containers::Array<char>{NoInit, 2*(Math::max(vertexCount, 1u) - 1)*sizeof(UnsignedInt)};
if(data.isIndexed()) if(mesh.isIndexed())
generateLineStripIndicesInto(data.indices(), Containers::arrayCast<UnsignedInt>(indexData)); generateLineStripIndicesInto(mesh.indices(), Containers::arrayCast<UnsignedInt>(indexData));
else else
generateLineStripIndicesInto(vertexCount, Containers::arrayCast<UnsignedInt>(indexData)); generateLineStripIndicesInto(vertexCount, Containers::arrayCast<UnsignedInt>(indexData));
} else if(data.primitive() == MeshPrimitive::LineLoop) { } else if(mesh.primitive() == MeshPrimitive::LineLoop) {
primitive = MeshPrimitive::Lines; primitive = MeshPrimitive::Lines;
indexData = Containers::Array<char>{NoInit, 2*vertexCount*sizeof(UnsignedInt)}; indexData = Containers::Array<char>{NoInit, 2*vertexCount*sizeof(UnsignedInt)};
if(data.isIndexed()) if(mesh.isIndexed())
generateLineLoopIndicesInto(data.indices(), Containers::arrayCast<UnsignedInt>(indexData)); generateLineLoopIndicesInto(mesh.indices(), Containers::arrayCast<UnsignedInt>(indexData));
else else
generateLineLoopIndicesInto(vertexCount, Containers::arrayCast<UnsignedInt>(indexData)); generateLineLoopIndicesInto(vertexCount, Containers::arrayCast<UnsignedInt>(indexData));
} else if(data.primitive() == MeshPrimitive::TriangleStrip) { } else if(mesh.primitive() == MeshPrimitive::TriangleStrip) {
primitive = MeshPrimitive::Triangles; primitive = MeshPrimitive::Triangles;
indexData = Containers::Array<char>{NoInit, 3*(Math::max(vertexCount, 2u) - 2)*sizeof(UnsignedInt)}; indexData = Containers::Array<char>{NoInit, 3*(Math::max(vertexCount, 2u) - 2)*sizeof(UnsignedInt)};
if(data.isIndexed()) if(mesh.isIndexed())
generateTriangleStripIndicesInto(data.indices(), Containers::arrayCast<UnsignedInt>(indexData)); generateTriangleStripIndicesInto(mesh.indices(), Containers::arrayCast<UnsignedInt>(indexData));
else else
generateTriangleStripIndicesInto(vertexCount, Containers::arrayCast<UnsignedInt>(indexData)); generateTriangleStripIndicesInto(vertexCount, Containers::arrayCast<UnsignedInt>(indexData));
} else if(data.primitive() == MeshPrimitive::TriangleFan) { } else if(mesh.primitive() == MeshPrimitive::TriangleFan) {
primitive = MeshPrimitive::Triangles; primitive = MeshPrimitive::Triangles;
indexData = Containers::Array<char>{NoInit, 3*(Math::max(vertexCount, 2u) - 2)*sizeof(UnsignedInt)}; indexData = Containers::Array<char>{NoInit, 3*(Math::max(vertexCount, 2u) - 2)*sizeof(UnsignedInt)};
if(data.isIndexed()) if(mesh.isIndexed())
generateTriangleFanIndicesInto(data.indices(), Containers::arrayCast<UnsignedInt>(indexData)); generateTriangleFanIndicesInto(mesh.indices(), Containers::arrayCast<UnsignedInt>(indexData));
else else
generateTriangleFanIndicesInto(vertexCount, Containers::arrayCast<UnsignedInt>(indexData)); generateTriangleFanIndicesInto(vertexCount, Containers::arrayCast<UnsignedInt>(indexData));
} else CORRADE_INTERNAL_ASSERT_UNREACHABLE(); /* LCOV_EXCL_LINE */ } else CORRADE_INTERNAL_ASSERT_UNREACHABLE(); /* LCOV_EXCL_LINE */
@ -656,10 +656,10 @@ Trade::MeshData generateIndices(Trade::MeshData&& data) {
std::move(vertexData), std::move(attributeData), vertexCount}; std::move(vertexData), std::move(attributeData), vertexCount};
} }
Trade::MeshData generateIndices(const Trade::MeshData& data) { Trade::MeshData generateIndices(const Trade::MeshData& mesh) {
/* Pass through to the && overload, which then decides whether to reuse /* Pass through to the && overload, which then decides whether to reuse
anything based on the DataFlags */ anything based on the DataFlags */
return generateIndices(reference(data)); return generateIndices(reference(mesh));
} }
}} }}

4
src/Magnum/MeshTools/GenerateIndices.h

@ -508,12 +508,12 @@ MAGNUM_MESHTOOLS_EXPORT Trade::MeshData generateIndices(const Trade::MeshData& m
@m_since{2020,06} @m_since{2020,06}
Compared to @ref generateIndices(const Trade::MeshData&) this function can Compared to @ref generateIndices(const Trade::MeshData&) this function can
transfer ownership of @p data vertex buffer (in case it is owned) to the transfer ownership of @p mesh vertex buffer (in case it is owned) to the
returned instance instead of making a copy of it. Attribute data is copied returned instance instead of making a copy of it. Attribute data is copied
always. always.
@see @ref Trade::MeshData::vertexDataFlags() @see @ref Trade::MeshData::vertexDataFlags()
*/ */
MAGNUM_MESHTOOLS_EXPORT Trade::MeshData generateIndices(Trade::MeshData&& data); MAGNUM_MESHTOOLS_EXPORT Trade::MeshData generateIndices(Trade::MeshData&& mesh);
}} }}

162
src/Magnum/MeshTools/Interleave.cpp

@ -36,41 +36,41 @@ namespace Magnum { namespace MeshTools {
namespace { namespace {
inline std::size_t attributeSize(const Trade::MeshData& data, UnsignedInt i) { inline std::size_t attributeSize(const Trade::MeshData& mesh, UnsignedInt i) {
return vertexFormatSize(data.attributeFormat(i))*Math::max(data.attributeArraySize(i), UnsignedShort{1}); return vertexFormatSize(mesh.attributeFormat(i))*Math::max(mesh.attributeArraySize(i), UnsignedShort{1});
} }
inline std::size_t attributeSize(const Trade::MeshAttributeData& data) { inline std::size_t attributeSize(const Trade::MeshAttributeData& mesh) {
return vertexFormatSize(data.format())*Math::max(data.arraySize(), UnsignedShort{1}); return vertexFormatSize(mesh.format())*Math::max(mesh.arraySize(), UnsignedShort{1});
} }
Containers::Optional<Containers::StridedArrayView2D<const char>> interleavedDataInternal(const Trade::MeshData& data) { Containers::Optional<Containers::StridedArrayView2D<const char>> interleavedDataInternal(const Trade::MeshData& mesh) {
/* There is no attributes, return a zero-sized view to indicate a success */ /* There is no attributes, return a zero-sized view to indicate a success */
if(!data.attributeCount()) if(!mesh.attributeCount())
return Containers::StridedArrayView2D<const char>{data.vertexData(), {data.vertexCount(), 0}}; return Containers::StridedArrayView2D<const char>{mesh.vertexData(), {mesh.vertexCount(), 0}};
/* Technically zero and negative strides *may* also be categorized as /* Technically zero and negative strides *may* also be categorized as
interleaved if they are all the same, but it causes way too many interleaved if they are all the same, but it causes way too many
problems especially when used within interleavedLayout() etc. May problems especially when used within interleavedLayout() etc. May
tackle properly later. */ tackle properly later. */
const Int stride = data.attributeStride(0); const Int stride = mesh.attributeStride(0);
if(stride <= 0) return Containers::NullOpt; if(stride <= 0) return Containers::NullOpt;
std::size_t minOffset = ~std::size_t{}; std::size_t minOffset = ~std::size_t{};
std::size_t maxOffset = 0; std::size_t maxOffset = 0;
bool hasImplementationSpecificVertexFormat = false; bool hasImplementationSpecificVertexFormat = false;
for(UnsignedInt i = 0; i != data.attributeCount(); ++i) { for(UnsignedInt i = 0; i != mesh.attributeCount(); ++i) {
if(data.attributeStride(i) != stride) return Containers::NullOpt; if(mesh.attributeStride(i) != stride) return Containers::NullOpt;
const std::size_t offset = data.attributeOffset(i); const std::size_t offset = mesh.attributeOffset(i);
minOffset = Math::min(minOffset, offset); minOffset = Math::min(minOffset, offset);
/* If the attribute has implementation-specific format, remember that /* If the attribute has implementation-specific format, remember that
for later and optimistically use size of 1 byte for calculations */ for later and optimistically use size of 1 byte for calculations */
std::size_t size; std::size_t size;
if(isVertexFormatImplementationSpecific(data.attributeFormat(i))) { if(isVertexFormatImplementationSpecific(mesh.attributeFormat(i))) {
hasImplementationSpecificVertexFormat = true; hasImplementationSpecificVertexFormat = true;
size = 1; size = 1;
} else size = attributeSize(data, i); } else size = attributeSize(mesh, i);
maxOffset = Math::max(maxOffset, offset + size); maxOffset = Math::max(maxOffset, offset + size);
} }
@ -87,26 +87,26 @@ Containers::Optional<Containers::StridedArrayView2D<const char>> interleavedData
if(maxOffset - minOffset > UnsignedInt(stride)) return Containers::NullOpt; if(maxOffset - minOffset > UnsignedInt(stride)) return Containers::NullOpt;
return Containers::StridedArrayView2D<const char>{ return Containers::StridedArrayView2D<const char>{
data.vertexData(), data.vertexData().data() + minOffset, mesh.vertexData(), mesh.vertexData().data() + minOffset,
{data.vertexCount(), maxOffset - minOffset}, {mesh.vertexCount(), maxOffset - minOffset},
{std::ptrdiff_t(stride), 1}}; {std::ptrdiff_t(stride), 1}};
} }
} }
bool isInterleaved(const Trade::MeshData& data) { bool isInterleaved(const Trade::MeshData& mesh) {
return !!interleavedDataInternal(data); return !!interleavedDataInternal(mesh);
} }
Containers::StridedArrayView2D<const char> interleavedData(const Trade::MeshData& data) { Containers::StridedArrayView2D<const char> interleavedData(const Trade::MeshData& mesh) {
auto out = interleavedDataInternal(data); auto out = interleavedDataInternal(mesh);
CORRADE_ASSERT(out, "MeshTools::interleavedData(): the mesh is not interleaved", {}); CORRADE_ASSERT(out, "MeshTools::interleavedData(): the mesh is not interleaved", {});
return *out; return *out;
} }
Containers::StridedArrayView2D<char> interleavedMutableData(Trade::MeshData& data) { Containers::StridedArrayView2D<char> interleavedMutableData(Trade::MeshData& mesh) {
Containers::StridedArrayView2D<const char> out = interleavedData(data); Containers::StridedArrayView2D<const char> out = interleavedData(mesh);
CORRADE_ASSERT(data.vertexDataFlags() & Trade::DataFlag::Mutable, CORRADE_ASSERT(mesh.vertexDataFlags() & Trade::DataFlag::Mutable,
"MeshTools::interleavedMutableData(): vertex data is not mutable", {}); "MeshTools::interleavedMutableData(): vertex data is not mutable", {});
return Containers::StridedArrayView2D<char>{ return Containers::StridedArrayView2D<char>{
{nullptr, ~std::size_t{}}, /* to sidestep the range assertions */ {nullptr, ~std::size_t{}}, /* to sidestep the range assertions */
@ -116,32 +116,32 @@ Containers::StridedArrayView2D<char> interleavedMutableData(Trade::MeshData& dat
namespace Implementation { namespace Implementation {
Containers::Array<Trade::MeshAttributeData> interleavedLayout(Trade::MeshData&& data, const Containers::ArrayView<const Trade::MeshAttributeData> extra, const InterleaveFlags flags) { Containers::Array<Trade::MeshAttributeData> interleavedLayout(Trade::MeshData&& mesh, const Containers::ArrayView<const Trade::MeshAttributeData> extra, const InterleaveFlags flags) {
/* Nothing to do here, bye! */ /* Nothing to do here, bye! */
if(!data.attributeCount() && extra.isEmpty()) return {}; if(!mesh.attributeCount() && extra.isEmpty()) return {};
/* If we're not told to preserve the layout, treat the mesh as /* If we're not told to preserve the layout, treat the mesh as
noninterleaved always, forcing a repack. Otherwise check if it's already noninterleaved always, forcing a repack. Otherwise check if it's already
interleaved. */ interleaved. */
const bool interleaved = flags >= InterleaveFlag::PreserveInterleavedAttributes && isInterleaved(data); const bool interleaved = flags >= InterleaveFlag::PreserveInterleavedAttributes && isInterleaved(mesh);
/* If the mesh is already interleaved, use the original stride to /* If the mesh is already interleaved, use the original stride to
preserve all padding, but remove the initial offset. Otherwise calculate preserve all padding, but remove the initial offset. Otherwise calculate
a tightly-packed stride. */ a tightly-packed stride. */
std::size_t stride; std::size_t stride;
std::size_t minOffset; std::size_t minOffset;
if(interleaved && data.attributeCount()) { if(interleaved && mesh.attributeCount()) {
stride = data.attributeStride(0); stride = mesh.attributeStride(0);
minOffset = ~std::size_t{}; minOffset = ~std::size_t{};
for(UnsignedInt i = 0, max = data.attributeCount(); i != max; ++i) for(UnsignedInt i = 0, max = mesh.attributeCount(); i != max; ++i)
minOffset = Math::min(minOffset, data.attributeOffset(i)); minOffset = Math::min(minOffset, mesh.attributeOffset(i));
} else { } else {
stride = 0; stride = 0;
minOffset = 0; minOffset = 0;
for(UnsignedInt i = 0, max = data.attributeCount(); i != max; ++i) { for(UnsignedInt i = 0, max = mesh.attributeCount(); i != max; ++i) {
CORRADE_ASSERT(!isVertexFormatImplementationSpecific(data.attributeFormat(i)), CORRADE_ASSERT(!isVertexFormatImplementationSpecific(mesh.attributeFormat(i)),
"MeshTools::interleavedLayout(): attribute" << i << "has an implementation-specific format" << reinterpret_cast<void*>(vertexFormatUnwrap(data.attributeFormat(i))), {}); "MeshTools::interleavedLayout(): attribute" << i << "has an implementation-specific format" << reinterpret_cast<void*>(vertexFormatUnwrap(mesh.attributeFormat(i))), {});
stride += attributeSize(data, i); stride += attributeSize(mesh, i);
} }
} }
@ -166,11 +166,11 @@ Containers::Array<Trade::MeshAttributeData> interleavedLayout(Trade::MeshData&&
can take over the ownership and avoid an allocation. Otherwise we can take over the ownership and avoid an allocation. Otherwise we
allocate a new array and copy the prefix over so we can just patch the allocate a new array and copy the prefix over so we can just patch the
data array later. */ data array later. */
const UnsignedInt originalAttributeCount = data.attributeCount(); const UnsignedInt originalAttributeCount = mesh.attributeCount();
const UnsignedInt originalAttributeStride = originalAttributeCount ? const UnsignedInt originalAttributeStride = originalAttributeCount ?
data.attributeStride(0) : 0; mesh.attributeStride(0) : 0;
Containers::Array<Trade::MeshAttributeData> originalAttributeData = Containers::Array<Trade::MeshAttributeData> originalAttributeData =
data.releaseAttributeData(); mesh.releaseAttributeData();
Containers::Array<Trade::MeshAttributeData> attributeData; Containers::Array<Trade::MeshAttributeData> attributeData;
if(!extraAttributeCount && !originalAttributeData.deleter()) if(!extraAttributeCount && !originalAttributeData.deleter())
attributeData = std::move(originalAttributeData); attributeData = std::move(originalAttributeData);
@ -183,7 +183,7 @@ Containers::Array<Trade::MeshAttributeData> interleavedLayout(Trade::MeshData&&
preserve relative attribute offsets, otherwise pack tightly. */ preserve relative attribute offsets, otherwise pack tightly. */
std::size_t offset = 0; std::size_t offset = 0;
for(UnsignedInt i = 0; i != originalAttributeCount; ++i) { for(UnsignedInt i = 0; i != originalAttributeCount; ++i) {
if(interleaved) offset = attributeData[i].offset(data.vertexData()) - minOffset; if(interleaved) offset = attributeData[i].offset(mesh.vertexData()) - minOffset;
attributeData[i] = Trade::MeshAttributeData{ attributeData[i] = Trade::MeshAttributeData{
attributeData[i].name(), attributeData[i].format(), attributeData[i].name(), attributeData[i].format(),
@ -219,13 +219,13 @@ Containers::Array<Trade::MeshAttributeData> interleavedLayout(Trade::MeshData&&
} }
Trade::MeshData interleavedLayout(Trade::MeshData&& data, const UnsignedInt vertexCount, const Containers::ArrayView<const Trade::MeshAttributeData> extra, const InterleaveFlags flags) { Trade::MeshData interleavedLayout(Trade::MeshData&& mesh, const UnsignedInt vertexCount, const Containers::ArrayView<const Trade::MeshAttributeData> extra, const InterleaveFlags flags) {
Containers::Array<Trade::MeshAttributeData> attributeData = Implementation::interleavedLayout(std::move(data), extra, flags); Containers::Array<Trade::MeshAttributeData> attributeData = Implementation::interleavedLayout(std::move(mesh), extra, flags);
/* If there are no attributes, bail -- return an empty mesh with desired /* If there are no attributes, bail -- return an empty mesh with desired
vertex count but nothing else */ vertex count but nothing else */
if(!attributeData) if(!attributeData)
return Trade::MeshData{data.primitive(), vertexCount}; return Trade::MeshData{mesh.primitive(), vertexCount};
/* Allocate new data array */ /* Allocate new data array */
Containers::Array<char> vertexData{NoInit, attributeData[0].stride()*vertexCount}; Containers::Array<char> vertexData{NoInit, attributeData[0].stride()*vertexCount};
@ -241,53 +241,53 @@ Trade::MeshData interleavedLayout(Trade::MeshData&& data, const UnsignedInt vert
attribute.arraySize()}; attribute.arraySize()};
} }
return Trade::MeshData{data.primitive(), std::move(vertexData), std::move(attributeData)}; return Trade::MeshData{mesh.primitive(), std::move(vertexData), std::move(attributeData)};
} }
Trade::MeshData interleavedLayout(Trade::MeshData&& data, const UnsignedInt vertexCount, const std::initializer_list<Trade::MeshAttributeData> extra, const InterleaveFlags flags) { Trade::MeshData interleavedLayout(Trade::MeshData&& mesh, const UnsignedInt vertexCount, const std::initializer_list<Trade::MeshAttributeData> extra, const InterleaveFlags flags) {
return interleavedLayout(std::move(data), vertexCount, Containers::arrayView(extra), flags); return interleavedLayout(std::move(mesh), vertexCount, Containers::arrayView(extra), flags);
} }
Trade::MeshData interleavedLayout(const Trade::MeshData& data, const UnsignedInt vertexCount, const Containers::ArrayView<const Trade::MeshAttributeData> extra, const InterleaveFlags flags) { Trade::MeshData interleavedLayout(const Trade::MeshData& mesh, const UnsignedInt vertexCount, const Containers::ArrayView<const Trade::MeshAttributeData> extra, const InterleaveFlags flags) {
/* Pass through to the && overload, which then decides whether to reuse /* Pass through to the && overload, which then decides whether to reuse
anything based on the DataFlags */ anything based on the DataFlags */
return interleavedLayout(reference(data), vertexCount, extra, flags); return interleavedLayout(reference(mesh), vertexCount, extra, flags);
} }
Trade::MeshData interleavedLayout(const Trade::MeshData& data, const UnsignedInt vertexCount, const std::initializer_list<Trade::MeshAttributeData> extra, const InterleaveFlags flags) { Trade::MeshData interleavedLayout(const Trade::MeshData& mesh, const UnsignedInt vertexCount, const std::initializer_list<Trade::MeshAttributeData> extra, const InterleaveFlags flags) {
return interleavedLayout(data, vertexCount, Containers::arrayView(extra), flags); return interleavedLayout(mesh, vertexCount, Containers::arrayView(extra), flags);
} }
Trade::MeshData interleave(Trade::MeshData&& data, const Containers::ArrayView<const Trade::MeshAttributeData> extra, const InterleaveFlags flags) { Trade::MeshData interleave(Trade::MeshData&& mesh, const Containers::ArrayView<const Trade::MeshAttributeData> extra, const InterleaveFlags flags) {
/* Transfer the indices unchanged, in case the mesh is indexed */ /* Transfer the indices unchanged, in case the mesh is indexed */
Containers::Array<char> indexData; Containers::Array<char> indexData;
Trade::MeshIndexData indices; Trade::MeshIndexData indices;
if(data.isIndexed()) { if(mesh.isIndexed()) {
const MeshIndexType indexType = data.indexType(); const MeshIndexType indexType = mesh.indexType();
/* If we can steal the data and we're allowed to preserve a strided /* If we can steal the data and we're allowed to preserve a strided
layout or it's tightly packed, do the steal */ layout or it's tightly packed, do the steal */
if((data.indexDataFlags() & Trade::DataFlag::Owned) && ((flags & InterleaveFlag::PreserveStridedIndices) || (!isMeshIndexTypeImplementationSpecific(indexType) && data.indexStride() == Int(meshIndexTypeSize(indexType))))) { if((mesh.indexDataFlags() & Trade::DataFlag::Owned) && ((flags & InterleaveFlag::PreserveStridedIndices) || (!isMeshIndexTypeImplementationSpecific(indexType) && mesh.indexStride() == Int(meshIndexTypeSize(indexType))))) {
indices = Trade::MeshIndexData{indexType, indices = Trade::MeshIndexData{indexType,
Containers::StridedArrayView1D<const void>{ Containers::StridedArrayView1D<const void>{
data.indexData(), mesh.indexData(),
data.indexData().data() + data.indexOffset(), mesh.indexData().data() + mesh.indexOffset(),
data.indexCount(), mesh.indexCount(),
data.indexStride()}}; mesh.indexStride()}};
indexData = data.releaseIndexData(); indexData = mesh.releaseIndexData();
/* Otherwise, if we can't steal the data but we're told to preserve /* Otherwise, if we can't steal the data but we're told to preserve
strided indices, make a full copy including any extra offsets and strided indices, make a full copy including any extra offsets and
paddings */ paddings */
} else if(flags & InterleaveFlag::PreserveStridedIndices) { } else if(flags & InterleaveFlag::PreserveStridedIndices) {
indexData = Containers::Array<char>{NoInit, data.indexData().size()}; indexData = Containers::Array<char>{NoInit, mesh.indexData().size()};
indices = Trade::MeshIndexData{indexType, indices = Trade::MeshIndexData{indexType,
Containers::StridedArrayView1D<const void>{ Containers::StridedArrayView1D<const void>{
indexData, indexData,
indexData.data() + data.indexOffset(), indexData.data() + mesh.indexOffset(),
data.indexCount(), mesh.indexCount(),
data.indexStride()}}; mesh.indexStride()}};
Utility::copy(data.indexData(), indexData); Utility::copy(mesh.indexData(), indexData);
/* Otherwise, make a tightly packed copy, in which case we can't have /* Otherwise, make a tightly packed copy, in which case we can't have
an implementation-specific index type */ an implementation-specific index type */
@ -297,48 +297,48 @@ Trade::MeshData interleave(Trade::MeshData&& data, const Containers::ArrayView<c
(Trade::MeshData{MeshPrimitive{}, 0})); (Trade::MeshData{MeshPrimitive{}, 0}));
const std::size_t indexTypeSize = meshIndexTypeSize(indexType); const std::size_t indexTypeSize = meshIndexTypeSize(indexType);
indexData = Containers::Array<char>{NoInit, data.indexCount()*indexTypeSize}; indexData = Containers::Array<char>{NoInit, mesh.indexCount()*indexTypeSize};
Containers::StridedArrayView2D<char> out{indexData, Containers::StridedArrayView2D<char> out{indexData,
{data.indexCount(), indexTypeSize}, {mesh.indexCount(), indexTypeSize},
{std::ptrdiff_t(indexTypeSize), 1}}; {std::ptrdiff_t(indexTypeSize), 1}};
indices = Trade::MeshIndexData{out}; indices = Trade::MeshIndexData{out};
Utility::copy(data.indices(), out); Utility::copy(mesh.indices(), out);
} }
} }
/* If we're not told to preserve the layout, treat the mesh as /* If we're not told to preserve the layout, treat the mesh as
noninterleaved always, forcing a repack. Otherwise check if it's already noninterleaved always, forcing a repack. Otherwise check if it's already
interleaved. */ interleaved. */
const bool interleaved = flags >= InterleaveFlag::PreserveInterleavedAttributes && isInterleaved(data); const bool interleaved = flags >= InterleaveFlag::PreserveInterleavedAttributes && isInterleaved(mesh);
const UnsignedInt vertexCount = data.vertexCount(); const UnsignedInt vertexCount = mesh.vertexCount();
/* If the mesh is already interleaved and we don't have anything extra, /* If the mesh is already interleaved and we don't have anything extra,
steal that data as well */ steal that data as well */
Containers::Array<char> vertexData; Containers::Array<char> vertexData;
Containers::Array<Trade::MeshAttributeData> attributeData; Containers::Array<Trade::MeshAttributeData> attributeData;
if(interleaved && extra.isEmpty() && (data.vertexDataFlags() & Trade::DataFlag::Owned)) { if(interleaved && extra.isEmpty() && (mesh.vertexDataFlags() & Trade::DataFlag::Owned)) {
attributeData = data.releaseAttributeData(); attributeData = mesh.releaseAttributeData();
vertexData = data.releaseVertexData(); vertexData = mesh.releaseVertexData();
/* Otherwise do it the hard way */ /* Otherwise do it the hard way */
} else { } else {
/* Calculate the layout. Can't std::move() the data in to avoid copying /* Calculate the layout. Can't std::move() the data in to avoid copying
the attribute array as we need the original attributes below. */ the attribute array as we need the original attributes below. */
Trade::MeshData layout = interleavedLayout(data, vertexCount, extra, flags); Trade::MeshData layout = interleavedLayout(mesh, vertexCount, extra, flags);
#ifdef CORRADE_GRACEFUL_ASSERT #ifdef CORRADE_GRACEFUL_ASSERT
/* If interleavedLayout() gracefully asserted and returned no /* If interleavedLayout() gracefully asserted and returned no
attributes (but the original had some), exit right away to not blow attributes (but the original had some), exit right away to not blow
up on something else later. Sorry, yes, this is shitty. */ up on something else later. Sorry, yes, this is shitty. */
if(!layout.attributeCount() && (data.attributeCount() || extra.size())) if(!layout.attributeCount() && (mesh.attributeCount() || extra.size()))
return Trade::MeshData{MeshPrimitive::Points, 0}; return Trade::MeshData{MeshPrimitive::Points, 0};
#endif #endif
/* Copy existing attributes to new locations */ /* Copy existing attributes to new locations */
for(UnsignedInt i = 0; i != data.attributeCount(); ++i) for(UnsignedInt i = 0; i != mesh.attributeCount(); ++i)
Utility::copy(data.attribute(i), layout.mutableAttribute(i)); Utility::copy(mesh.attribute(i), layout.mutableAttribute(i));
/* Mix in the extra attributes */ /* Mix in the extra attributes */
UnsignedInt attributeIndex = data.attributeCount(); UnsignedInt attributeIndex = mesh.attributeCount();
for(UnsignedInt i = 0; i != extra.size(); ++i) { for(UnsignedInt i = 0; i != extra.size(); ++i) {
/* Padding, ignore */ /* Padding, ignore */
if(extra[i].format() == VertexFormat{}) continue; if(extra[i].format() == VertexFormat{}) continue;
@ -368,22 +368,22 @@ Trade::MeshData interleave(Trade::MeshData&& data, const Containers::ArrayView<c
attributeData = layout.releaseAttributeData(); attributeData = layout.releaseAttributeData();
} }
return Trade::MeshData{data.primitive(), std::move(indexData), indices, return Trade::MeshData{mesh.primitive(), std::move(indexData), indices,
std::move(vertexData), std::move(attributeData), vertexCount}; std::move(vertexData), std::move(attributeData), vertexCount};
} }
Trade::MeshData interleave(Trade::MeshData&& data, const std::initializer_list<Trade::MeshAttributeData> extra, const InterleaveFlags flags) { Trade::MeshData interleave(Trade::MeshData&& mesh, const std::initializer_list<Trade::MeshAttributeData> extra, const InterleaveFlags flags) {
return interleave(std::move(data), Containers::arrayView(extra), flags); return interleave(std::move(mesh), Containers::arrayView(extra), flags);
} }
Trade::MeshData interleave(const Trade::MeshData& data, const Containers::ArrayView<const Trade::MeshAttributeData> extra, const InterleaveFlags flags) { Trade::MeshData interleave(const Trade::MeshData& mesh, const Containers::ArrayView<const Trade::MeshAttributeData> extra, const InterleaveFlags flags) {
/* Pass through to the && overload, which then decides whether to reuse /* Pass through to the && overload, which then decides whether to reuse
anything based on the DataFlags */ anything based on the DataFlags */
return interleave(reference(data), extra, flags); return interleave(reference(mesh), extra, flags);
} }
Trade::MeshData interleave(const Trade::MeshData& data, const std::initializer_list<Trade::MeshAttributeData> extra, const InterleaveFlags flags) { Trade::MeshData interleave(const Trade::MeshData& mesh, const std::initializer_list<Trade::MeshAttributeData> extra, const InterleaveFlags flags) {
return interleave(std::move(data), Containers::arrayView(extra), flags); return interleave(std::move(mesh), Containers::arrayView(extra), flags);
} }
}} }}

46
src/Magnum/MeshTools/Interleave.h

@ -204,7 +204,7 @@ non-interleaved.
@see @ref Trade::MeshData::attributeStride(), @see @ref Trade::MeshData::attributeStride(),
@ref Trade::MeshData::attributeOffset(), @ref interleavedData() @ref Trade::MeshData::attributeOffset(), @ref interleavedData()
*/ */
MAGNUM_MESHTOOLS_EXPORT bool isInterleaved(const Trade::MeshData& data); MAGNUM_MESHTOOLS_EXPORT bool isInterleaved(const Trade::MeshData& mesh);
/** /**
@brief Type-erased view on interleaved mesh data @brief Type-erased view on interleaved mesh data
@ -219,7 +219,7 @@ count to cover all interleaved attributes, including any padding between them
but not before or after. but not before or after.
@see @ref isInterleaved() @see @ref isInterleaved()
*/ */
MAGNUM_MESHTOOLS_EXPORT Containers::StridedArrayView2D<const char> interleavedData(const Trade::MeshData& data); MAGNUM_MESHTOOLS_EXPORT Containers::StridedArrayView2D<const char> interleavedData(const Trade::MeshData& mesh);
/** /**
@brief Mutable type-erased view on interleaved mesh data @brief Mutable type-erased view on interleaved mesh data
@ -229,16 +229,16 @@ Same as @ref interleavedData(), but returns a mutable view. Expects that the
mesh is interleaved and vertex data is mutable. mesh is interleaved and vertex data is mutable.
@see @ref isInterleaved(), @ref Trade::MeshData::vertexDataFlags() @see @ref isInterleaved(), @ref Trade::MeshData::vertexDataFlags()
*/ */
MAGNUM_MESHTOOLS_EXPORT Containers::StridedArrayView2D<char> interleavedMutableData(Trade::MeshData& data); MAGNUM_MESHTOOLS_EXPORT Containers::StridedArrayView2D<char> interleavedMutableData(Trade::MeshData& mesh);
/** /**
@brief Create an interleaved mesh layout @brief Create an interleaved mesh layout
@m_since{2020,06} @m_since{2020,06}
Returns a @ref Trade::MeshData instance with its vertex data allocated for Returns a @ref Trade::MeshData instance with its vertex data allocated for
@p vertexCount vertices containing attributes from both @p data and @p extra @p vertexCount vertices containing attributes from both @p mesh and @p extra
interleaved together. No data is actually copied, only an interleaved layout is interleaved together. No data is actually copied, only an interleaved layout is
created. If @p data is already interleaved according to @ref isInterleaved() created. If @p mesh is already interleaved according to @ref isInterleaved()
and @ref InterleaveFlag::PreserveInterleavedAttributes is set in @p flags, and @ref InterleaveFlag::PreserveInterleavedAttributes is set in @p flags,
keeps the attributes in the same layout, potentially extending them with keeps the attributes in the same layout, potentially extending them with
@p extra. The @p extra attributes, if any, are interleaved together with @p extra. The @p extra attributes, if any, are interleaved together with
@ -265,42 +265,42 @@ This function will unconditionally allocate a new array to store all
@ref Trade::MeshAttributeData, use @ref interleavedLayout(Trade::MeshData&&, UnsignedInt, Containers::ArrayView<const Trade::MeshAttributeData>, InterleaveFlags) @ref Trade::MeshAttributeData, use @ref interleavedLayout(Trade::MeshData&&, UnsignedInt, Containers::ArrayView<const Trade::MeshAttributeData>, InterleaveFlags)
to avoid that allocation. to avoid that allocation.
All attributes in both @p data and @p extra are expected to not have an All attributes in both @p mesh and @p extra are expected to not have an
implementation-specific format, except for @p data attributes in case @p data implementation-specific format, except for @p mesh attributes in case @p mesh
is already interleaved, then the layout is untouched. is already interleaved, then the layout is untouched.
@see @ref isVertexFormatImplementationSpecific() @see @ref isVertexFormatImplementationSpecific()
*/ */
MAGNUM_MESHTOOLS_EXPORT Trade::MeshData interleavedLayout(const Trade::MeshData& data, UnsignedInt vertexCount, Containers::ArrayView<const Trade::MeshAttributeData> extra = {}, InterleaveFlags flags = InterleaveFlag::PreserveInterleavedAttributes); MAGNUM_MESHTOOLS_EXPORT Trade::MeshData interleavedLayout(const Trade::MeshData& mesh, UnsignedInt vertexCount, Containers::ArrayView<const Trade::MeshAttributeData> extra = {}, InterleaveFlags flags = InterleaveFlag::PreserveInterleavedAttributes);
/** /**
* @overload * @overload
* @m_since{2020,06} * @m_since{2020,06}
*/ */
MAGNUM_MESHTOOLS_EXPORT Trade::MeshData interleavedLayout(const Trade::MeshData& data, UnsignedInt vertexCount, std::initializer_list<Trade::MeshAttributeData> extra, InterleaveFlags flags = InterleaveFlag::PreserveInterleavedAttributes); MAGNUM_MESHTOOLS_EXPORT Trade::MeshData interleavedLayout(const Trade::MeshData& mesh, UnsignedInt vertexCount, std::initializer_list<Trade::MeshAttributeData> extra, InterleaveFlags flags = InterleaveFlag::PreserveInterleavedAttributes);
/** /**
@brief Create an interleaved mesh layout @brief Create an interleaved mesh layout
@m_since{2020,06} @m_since{2020,06}
Compared to @ref interleavedLayout(const Trade::MeshData&, UnsignedInt, Containers::ArrayView<const Trade::MeshAttributeData>, InterleaveFlags) Compared to @ref interleavedLayout(const Trade::MeshData&, UnsignedInt, Containers::ArrayView<const Trade::MeshAttributeData>, InterleaveFlags)
this function can reuse the @ref Trade::MeshAttributeData array from @p data this function can reuse the @ref Trade::MeshAttributeData array from @p mesh
instead of allocating a new one if there are no attributes passed in @p extra, instead of allocating a new one if there are no attributes passed in @p extra,
the attribute array is owned by the mesh and the attribute array is owned by the mesh and
@ref InterleaveFlag::PreserveInterleavedAttributes is set in @p flags. @ref InterleaveFlag::PreserveInterleavedAttributes is set in @p flags.
*/ */
MAGNUM_MESHTOOLS_EXPORT Trade::MeshData interleavedLayout(Trade::MeshData&& data, UnsignedInt vertexCount, Containers::ArrayView<const Trade::MeshAttributeData> extra = {}, InterleaveFlags flags = InterleaveFlag::PreserveInterleavedAttributes); MAGNUM_MESHTOOLS_EXPORT Trade::MeshData interleavedLayout(Trade::MeshData&& mesh, UnsignedInt vertexCount, Containers::ArrayView<const Trade::MeshAttributeData> extra = {}, InterleaveFlags flags = InterleaveFlag::PreserveInterleavedAttributes);
/** /**
* @overload * @overload
* @m_since{2020,06} * @m_since{2020,06}
*/ */
MAGNUM_MESHTOOLS_EXPORT Trade::MeshData interleavedLayout(Trade::MeshData&& data, UnsignedInt vertexCount, std::initializer_list<Trade::MeshAttributeData> extra, InterleaveFlags flags = InterleaveFlag::PreserveInterleavedAttributes); MAGNUM_MESHTOOLS_EXPORT Trade::MeshData interleavedLayout(Trade::MeshData&& mesh, UnsignedInt vertexCount, std::initializer_list<Trade::MeshAttributeData> extra, InterleaveFlags flags = InterleaveFlag::PreserveInterleavedAttributes);
/** /**
@brief Interleave mesh data @brief Interleave mesh data
@m_since{2020,06} @m_since{2020,06}
Returns a copy of @p data with all attributes interleaved. The @p extra Returns a copy of @p mesh with all attributes interleaved. The @p extra
attributes, if any, are interleaved together with existing attributes (or, in attributes, if any, are interleaved together with existing attributes (or, in
case the attribute view is empty, only the corresponding space for given case the attribute view is empty, only the corresponding space for given
attribute type is reserved, with memory left uninitialized). The data layouting attribute type is reserved, with memory left uninitialized). The data layouting
@ -314,32 +314,32 @@ implementation-specific type. Otherwise the behavior depends on presence of
@ref InterleaveFlag::PreserveStridedIndices. @ref InterleaveFlag::PreserveStridedIndices.
Expects that each attribute in @p extra has either the same amount of elements Expects that each attribute in @p extra has either the same amount of elements
as @p data vertex count or has none. This function will unconditionally make a as @p mesh vertex count or has none. This function will unconditionally make a
copy of all data even if @p data is already interleaved and needs no change, copy of all data even if @p mesh is already interleaved and needs no change,
use @ref interleave(Trade::MeshData&&, Containers::ArrayView<const Trade::MeshAttributeData>, InterleaveFlags) use @ref interleave(Trade::MeshData&&, Containers::ArrayView<const Trade::MeshAttributeData>, InterleaveFlags)
to avoid that copy. to avoid that copy.
All attributes in both @p data and @p extra are expected to not have an All attributes in both @p mesh and @p extra are expected to not have an
implementation-specific format, except for @p data attributes in case @p data implementation-specific format, except for @p mesh attributes in case @p data
is already interleaved, then the layout is untouched. is already interleaved, then the layout is untouched.
@see @ref isInterleaved(), @ref isMeshIndexTypeImplementationSpecific(), @see @ref isInterleaved(), @ref isMeshIndexTypeImplementationSpecific(),
@ref isVertexFormatImplementationSpecific(), @ref isVertexFormatImplementationSpecific(),
@ref Trade::MeshData::attributeData() @ref Trade::MeshData::attributeData()
*/ */
MAGNUM_MESHTOOLS_EXPORT Trade::MeshData interleave(const Trade::MeshData& data, Containers::ArrayView<const Trade::MeshAttributeData> extra = {}, InterleaveFlags flags = InterleaveFlag::PreserveInterleavedAttributes); MAGNUM_MESHTOOLS_EXPORT Trade::MeshData interleave(const Trade::MeshData& mesh, Containers::ArrayView<const Trade::MeshAttributeData> extra = {}, InterleaveFlags flags = InterleaveFlag::PreserveInterleavedAttributes);
/** /**
* @overload * @overload
* @m_since{2020,06} * @m_since{2020,06}
*/ */
MAGNUM_MESHTOOLS_EXPORT Trade::MeshData interleave(const Trade::MeshData& data, std::initializer_list<Trade::MeshAttributeData> extra, InterleaveFlags flags = InterleaveFlag::PreserveInterleavedAttributes); MAGNUM_MESHTOOLS_EXPORT Trade::MeshData interleave(const Trade::MeshData& mesh, std::initializer_list<Trade::MeshAttributeData> extra, InterleaveFlags flags = InterleaveFlag::PreserveInterleavedAttributes);
/** /**
@brief Interleave mesh data @brief Interleave mesh data
@m_since{2020,06} @m_since{2020,06}
Compared to @ref interleave(const Trade::MeshData&, Containers::ArrayView<const Trade::MeshAttributeData>, InterleaveFlags) Compared to @ref interleave(const Trade::MeshData&, Containers::ArrayView<const Trade::MeshAttributeData>, InterleaveFlags)
this function can transfer ownership of @p data index buffer (in case it is this function can transfer ownership of @p mesh index buffer (in case it is
owned) and vertex buffer (in case it is owned, already interleaved, there's no owned) and vertex buffer (in case it is owned, already interleaved, there's no
@p extra attributes and @ref InterleaveFlag::PreserveInterleavedAttributes is @p extra attributes and @ref InterleaveFlag::PreserveInterleavedAttributes is
set in @p flags) to the returned instance instead of making copies of them. set in @p flags) to the returned instance instead of making copies of them.
@ -347,18 +347,18 @@ set in @p flags) to the returned instance instead of making copies of them.
@ref Trade::MeshData::vertexDataFlags(), @ref Trade::MeshData::vertexDataFlags(),
@ref Trade::MeshData::attributeData() @ref Trade::MeshData::attributeData()
*/ */
MAGNUM_MESHTOOLS_EXPORT Trade::MeshData interleave(Trade::MeshData&& data, Containers::ArrayView<const Trade::MeshAttributeData> extra = {}, InterleaveFlags flags = InterleaveFlag::PreserveInterleavedAttributes); MAGNUM_MESHTOOLS_EXPORT Trade::MeshData interleave(Trade::MeshData&& mesh, Containers::ArrayView<const Trade::MeshAttributeData> extra = {}, InterleaveFlags flags = InterleaveFlag::PreserveInterleavedAttributes);
/** /**
* @overload * @overload
* @m_since{2020,06} * @m_since{2020,06}
*/ */
MAGNUM_MESHTOOLS_EXPORT Trade::MeshData interleave(Trade::MeshData&& data, std::initializer_list<Trade::MeshAttributeData> extra, InterleaveFlags flags = InterleaveFlag::PreserveInterleavedAttributes); MAGNUM_MESHTOOLS_EXPORT Trade::MeshData interleave(Trade::MeshData&& mesh, std::initializer_list<Trade::MeshAttributeData> extra, InterleaveFlags flags = InterleaveFlag::PreserveInterleavedAttributes);
namespace Implementation { namespace Implementation {
/* Used internally by interleavedLayout() and concatenate() */ /* Used internally by interleavedLayout() and concatenate() */
MAGNUM_MESHTOOLS_EXPORT Containers::Array<Trade::MeshAttributeData> interleavedLayout(Trade::MeshData&& data, Containers::ArrayView<const Trade::MeshAttributeData> extra, InterleaveFlags flags); MAGNUM_MESHTOOLS_EXPORT Containers::Array<Trade::MeshAttributeData> interleavedLayout(Trade::MeshData&& mesh, Containers::ArrayView<const Trade::MeshAttributeData> extra, InterleaveFlags flags);
} }

90
src/Magnum/MeshTools/Reference.cpp

@ -31,28 +31,28 @@
namespace Magnum { namespace MeshTools { namespace Magnum { namespace MeshTools {
Trade::MeshData reference(const Trade::MeshData& data) { Trade::MeshData reference(const Trade::MeshData& mesh) {
/* Can't do just Trade::MeshIndexData{data.indices()} as that would discard /* Can't do just Trade::MeshIndexData{data.indices()} as that would discard
implementation-specific types. And can't do implementation-specific types. And can't do
Trade::meshIndexData{data.indexType(), view} Trade::meshIndexData{data.indexType(), view}
because asking for index type would assert on non-indexed meshes. */ because asking for index type would assert on non-indexed meshes. */
Trade::MeshIndexData indices; Trade::MeshIndexData indices;
if(data.isIndexed()) indices = Trade::MeshIndexData{ if(mesh.isIndexed()) indices = Trade::MeshIndexData{
data.indexType(), mesh.indexType(),
Containers::StridedArrayView1D<const void>{ Containers::StridedArrayView1D<const void>{
data.indexData(), mesh.indexData(),
data.indexData().data() + data.indexOffset(), mesh.indexData().data() + mesh.indexOffset(),
data.indexCount(), mesh.indexCount(),
data.indexStride()}}; mesh.indexStride()}};
return Trade::MeshData{data.primitive(), return Trade::MeshData{mesh.primitive(),
{}, data.indexData(), indices, {}, mesh.indexData(), indices,
{}, data.vertexData(), Trade::meshAttributeDataNonOwningArray(data.attributeData()), {}, mesh.vertexData(), Trade::meshAttributeDataNonOwningArray(mesh.attributeData()),
data.vertexCount()}; mesh.vertexCount()};
} }
Trade::MeshData mutableReference(Trade::MeshData& data) { Trade::MeshData mutableReference(Trade::MeshData& mesh) {
CORRADE_ASSERT((data.indexDataFlags() & Trade::DataFlag::Mutable) && (data.vertexDataFlags() & Trade::DataFlag::Mutable), CORRADE_ASSERT((mesh.indexDataFlags() & Trade::DataFlag::Mutable) && (mesh.vertexDataFlags() & Trade::DataFlag::Mutable),
"MeshTools::mutableReference(): data not mutable", "MeshTools::mutableReference(): data not mutable",
(Trade::MeshData{MeshPrimitive::Points, 0})); (Trade::MeshData{MeshPrimitive::Points, 0}));
@ -61,57 +61,57 @@ Trade::MeshData mutableReference(Trade::MeshData& data) {
Trade::meshIndexData{data.indexType(), view} Trade::meshIndexData{data.indexType(), view}
because asking for index type would assert on non-indexed meshes. */ because asking for index type would assert on non-indexed meshes. */
Trade::MeshIndexData indices; Trade::MeshIndexData indices;
if(data.isIndexed()) indices = Trade::MeshIndexData{ if(mesh.isIndexed()) indices = Trade::MeshIndexData{
data.indexType(), mesh.indexType(),
Containers::StridedArrayView1D<const void>{ Containers::StridedArrayView1D<const void>{
data.indexData(), mesh.indexData(),
data.indexData().data() + data.indexOffset(), mesh.indexData().data() + mesh.indexOffset(),
data.indexCount(), mesh.indexCount(),
data.indexStride()}}; mesh.indexStride()}};
return Trade::MeshData{data.primitive(), return Trade::MeshData{mesh.primitive(),
Trade::DataFlag::Mutable, data.mutableIndexData(), indices, Trade::DataFlag::Mutable, mesh.mutableIndexData(), indices,
Trade::DataFlag::Mutable, data.mutableVertexData(), Trade::meshAttributeDataNonOwningArray(data.attributeData()), Trade::DataFlag::Mutable, mesh.mutableVertexData(), Trade::meshAttributeDataNonOwningArray(mesh.attributeData()),
data.vertexCount()}; mesh.vertexCount()};
} }
Trade::MeshData owned(const Trade::MeshData& data) { Trade::MeshData owned(const Trade::MeshData& mesh) {
return owned(reference(data)); return owned(reference(mesh));
} }
Trade::MeshData owned(Trade::MeshData&& data) { Trade::MeshData owned(Trade::MeshData&& mesh) {
/** @todo copy only the actually used range instead of the whole thing? */ /** @todo copy only the actually used range instead of the whole thing? */
/* If index data are already owned, move them to the output. This works /* If index data are already owned, move them to the output. This works
without any extra effort also for non-indexed meshes. */ without any extra effort also for non-indexed meshes. */
Containers::Array<char> indexData; Containers::Array<char> indexData;
Trade::MeshIndexData indices; Trade::MeshIndexData indices;
if(data.indexDataFlags() & Trade::DataFlag::Owned) { if(mesh.indexDataFlags() & Trade::DataFlag::Owned) {
indices = Trade::MeshIndexData{data.indices()}; indices = Trade::MeshIndexData{mesh.indices()};
indexData = data.releaseIndexData(); indexData = mesh.releaseIndexData();
/* Otherwise copy them, if the mesh is indexed. If not, the /* Otherwise copy them, if the mesh is indexed. If not, the
default-constructed instances are fine. */ default-constructed instances are fine. */
} else if(data.isIndexed()) { } else if(mesh.isIndexed()) {
indexData = Containers::Array<char>{NoInit, data.indexData().size()}; indexData = Containers::Array<char>{NoInit, mesh.indexData().size()};
indices = Trade::MeshIndexData{ indices = Trade::MeshIndexData{
data.indexType(), mesh.indexType(),
Containers::StridedArrayView1D<const void>{ Containers::StridedArrayView1D<const void>{
indexData, indexData,
indexData.data() + data.indexOffset(), indexData.data() + mesh.indexOffset(),
data.indexCount(), mesh.indexCount(),
data.indexStride()}}; mesh.indexStride()}};
Utility::copy(data.indexData(), indexData); Utility::copy(mesh.indexData(), indexData);
} }
/* If vertex data are already owned, move them to the output. Because /* If vertex data are already owned, move them to the output. Because
releasing them will clear vertex count, save that in advance, save also releasing them will clear vertex count, save that in advance, save also
original vertex data view for attribute offset calculation */ original vertex data view for attribute offset calculation */
const UnsignedInt vertexCount = data.vertexCount(); const UnsignedInt vertexCount = mesh.vertexCount();
const Containers::ArrayView<const char> originalVertexData = data.vertexData(); const Containers::ArrayView<const char> originalVertexData = mesh.vertexData();
Containers::Array<char> vertexData; Containers::Array<char> vertexData;
if(data.vertexDataFlags() & Trade::DataFlag::Owned) { if(mesh.vertexDataFlags() & Trade::DataFlag::Owned) {
vertexData = data.releaseVertexData(); vertexData = mesh.releaseVertexData();
/* Otherwise copy them */ /* Otherwise copy them */
} else { } else {
@ -122,12 +122,12 @@ Trade::MeshData owned(Trade::MeshData&& data) {
/* There's no way to know if attribute data are owned until we release /* There's no way to know if attribute data are owned until we release
them and check the deleter, but releasing them makes it impossible to them and check the deleter, but releasing them makes it impossible to
use the convenience MeshData APIs, so we have to do the hard way. */ use the convenience MeshData APIs, so we have to do the hard way. */
Containers::Array<Trade::MeshAttributeData> originalAttributeData = data.releaseAttributeData(); Containers::Array<Trade::MeshAttributeData> originalAttributeData = mesh.releaseAttributeData();
/* If the attribute data are owned *and* the vertex data weren't copied, /* If the attribute data are owned *and* the vertex data weren't copied,
we can reuse the original array in its entirety */ we can reuse the original array in its entirety */
Containers::Array<Trade::MeshAttributeData> attributeData; Containers::Array<Trade::MeshAttributeData> attributeData;
if(!originalAttributeData.deleter() && (data.vertexDataFlags() & Trade::DataFlag::Owned)) { if(!originalAttributeData.deleter() && (mesh.vertexDataFlags() & Trade::DataFlag::Owned)) {
attributeData = std::move(originalAttributeData); attributeData = std::move(originalAttributeData);
/* Otherwise we have to allocate a new one and re-route the attributes to /* Otherwise we have to allocate a new one and re-route the attributes to
@ -147,7 +147,7 @@ Trade::MeshData owned(Trade::MeshData&& data) {
} }
} }
return Trade::MeshData{data.primitive(), return Trade::MeshData{mesh.primitive(),
std::move(indexData), indices, std::move(indexData), indices,
std::move(vertexData), std::move(attributeData), std::move(vertexData), std::move(attributeData),
vertexCount}; vertexCount};

14
src/Magnum/MeshTools/Reference.h

@ -41,11 +41,11 @@ namespace Magnum { namespace MeshTools {
The returned instance has empty @ref Trade::MeshData::indexDataFlags() and The returned instance has empty @ref Trade::MeshData::indexDataFlags() and
@ref Trade::MeshData::vertexDataFlags() and references attribute data from the @ref Trade::MeshData::vertexDataFlags() and references attribute data from the
@p data as well. The function performs no allocation or data copy. Use @p mesh as well. The function performs no allocation or data copy. Use
@ref owned() for an inverse operation. @ref owned() for an inverse operation.
@see @ref mutableReference() @see @ref mutableReference()
*/ */
MAGNUM_MESHTOOLS_EXPORT Trade::MeshData reference(const Trade::MeshData& data); MAGNUM_MESHTOOLS_EXPORT Trade::MeshData reference(const Trade::MeshData& mesh);
/** /**
@brief Create a mutable reference on a @ref Trade::MeshData @brief Create a mutable reference on a @ref Trade::MeshData
@ -54,10 +54,10 @@ MAGNUM_MESHTOOLS_EXPORT Trade::MeshData reference(const Trade::MeshData& data);
The returned instance has @ref Trade::MeshData::indexDataFlags() and The returned instance has @ref Trade::MeshData::indexDataFlags() and
@ref Trade::MeshData::vertexDataFlags() set to @ref Trade::DataFlag::Mutable. @ref Trade::MeshData::vertexDataFlags() set to @ref Trade::DataFlag::Mutable.
The function performs no allocation or data copy. Use @ref owned() for an The function performs no allocation or data copy. Use @ref owned() for an
inverse operation. Expects that @p data is mutable. inverse operation. Expects that @p mesh is mutable.
@see @ref reference() @see @ref reference()
*/ */
MAGNUM_MESHTOOLS_EXPORT Trade::MeshData mutableReference(Trade::MeshData& data); MAGNUM_MESHTOOLS_EXPORT Trade::MeshData mutableReference(Trade::MeshData& mesh);
/** /**
@brief Create an owned @ref Trade::MeshData @brief Create an owned @ref Trade::MeshData
@ -67,12 +67,12 @@ The returned instance owns its index, vertex and attribute data --- both
@ref Trade::MeshData::indexDataFlags() and @ref Trade::MeshData::indexDataFlags() and
@ref Trade::MeshData::vertexDataFlags() have @ref Trade::DataFlag::Mutable and @ref Trade::MeshData::vertexDataFlags() have @ref Trade::DataFlag::Mutable and
@ref Trade::DataFlag::Owned set. This function unconditionally does an @ref Trade::DataFlag::Owned set. This function unconditionally does an
allocation and a copy even if the @p data is already owned, use allocation and a copy even if the @p mesh is already owned, use
@ref owned(Trade::MeshData&&) to make an owned copy only if the instance isn't @ref owned(Trade::MeshData&&) to make an owned copy only if the instance isn't
already owned. already owned.
@see @ref reference(), @ref mutableReference() @see @ref reference(), @ref mutableReference()
*/ */
MAGNUM_MESHTOOLS_EXPORT Trade::MeshData owned(const Trade::MeshData& data); MAGNUM_MESHTOOLS_EXPORT Trade::MeshData owned(const Trade::MeshData& mesh);
/** /**
@brief Create an owned @ref Trade::MeshData, if not already @brief Create an owned @ref Trade::MeshData, if not already
@ -86,7 +86,7 @@ already owned are simply moved to the output, otherwise the data get copied
into newly allocated arrays. into newly allocated arrays.
@see @ref reference(), @ref mutableReference() @see @ref reference(), @ref mutableReference()
*/ */
MAGNUM_MESHTOOLS_EXPORT Trade::MeshData owned(Trade::MeshData&& data); MAGNUM_MESHTOOLS_EXPORT Trade::MeshData owned(Trade::MeshData&& mesh);
}} }}

20
src/Magnum/MeshTools/RemoveDuplicates.cpp

@ -397,13 +397,13 @@ std::size_t removeDuplicatesFuzzyIndexedInPlace(const Containers::StridedArrayVi
return removeDuplicatesFuzzyIndexedInPlaceImplementation(indices, data, epsilon); return removeDuplicatesFuzzyIndexedInPlaceImplementation(indices, data, epsilon);
} }
Trade::MeshData removeDuplicates(const Trade::MeshData& data) { Trade::MeshData removeDuplicates(const Trade::MeshData& mesh) {
CORRADE_ASSERT(data.attributeCount(), CORRADE_ASSERT(mesh.attributeCount(),
"MeshTools::removeDuplicates(): can't remove duplicates in an attributeless mesh", "MeshTools::removeDuplicates(): can't remove duplicates in an attributeless mesh",
(Trade::MeshData{MeshPrimitive::Points, 0})); (Trade::MeshData{MeshPrimitive::Points, 0}));
#ifndef CORRADE_NO_ASSERT #ifndef CORRADE_NO_ASSERT
for(std::size_t i = 0; i != data.attributeCount(); ++i) { for(std::size_t i = 0; i != mesh.attributeCount(); ++i) {
const VertexFormat format = data.attributeFormat(i); const VertexFormat format = mesh.attributeFormat(i);
CORRADE_ASSERT(!isVertexFormatImplementationSpecific(format), CORRADE_ASSERT(!isVertexFormatImplementationSpecific(format),
"MeshTools::removeDuplicates(): attribute" << i << "has an implementation-specific format" << reinterpret_cast<void*>(vertexFormatUnwrap(format)), "MeshTools::removeDuplicates(): attribute" << i << "has an implementation-specific format" << reinterpret_cast<void*>(vertexFormatUnwrap(format)),
(Trade::MeshData{MeshPrimitive::Points, 0})); (Trade::MeshData{MeshPrimitive::Points, 0}));
@ -412,8 +412,8 @@ Trade::MeshData removeDuplicates(const Trade::MeshData& data) {
/* This has to be checked before passing the data to interleave() as there /* This has to be checked before passing the data to interleave() as there
it would die also, but with a confusing function name in the message */ it would die also, but with a confusing function name in the message */
CORRADE_ASSERT(!data.isIndexed() || !isMeshIndexTypeImplementationSpecific(data.indexType()), CORRADE_ASSERT(!mesh.isIndexed() || !isMeshIndexTypeImplementationSpecific(mesh.indexType()),
"MeshTools::removeDuplicates(): mesh has an implementation-specific index type" << reinterpret_cast<void*>(meshIndexTypeUnwrap(data.indexType())), "MeshTools::removeDuplicates(): mesh has an implementation-specific index type" << reinterpret_cast<void*>(meshIndexTypeUnwrap(mesh.indexType())),
(Trade::MeshData{MeshPrimitive{}, 0})); (Trade::MeshData{MeshPrimitive{}, 0}));
/* Turn the passed data into an interleaved owned mutable instance we can /* Turn the passed data into an interleaved owned mutable instance we can
@ -424,7 +424,7 @@ Trade::MeshData removeDuplicates(const Trade::MeshData& data) {
which is set by default, otherwise random padding bytes or filtered-out which is set by default, otherwise random padding bytes or filtered-out
attributes may contribute to the non-uniqueness of particular attributes may contribute to the non-uniqueness of particular
elements. */ elements. */
Trade::MeshData ownedInterleaved = owned(interleave(data, {}, InterleaveFlags{})); Trade::MeshData ownedInterleaved = owned(interleave(mesh, {}, InterleaveFlags{}));
/* Because the interleaved mesh was forced to be repacked, the vertex data /* Because the interleaved mesh was forced to be repacked, the vertex data
should span the whole stride -- this is relied on in the attribute should span the whole stride -- this is relied on in the attribute
@ -472,15 +472,15 @@ Trade::MeshData removeDuplicates(const Trade::MeshData& data) {
uniqueVertexCount}; uniqueVertexCount};
} }
Trade::MeshData removeDuplicatesFuzzy(const Trade::MeshData& data, const Float floatEpsilon, const Double doubleEpsilon) { Trade::MeshData removeDuplicatesFuzzy(const Trade::MeshData& mesh, const Float floatEpsilon, const Double doubleEpsilon) {
CORRADE_ASSERT(data.attributeCount(), CORRADE_ASSERT(mesh.attributeCount(),
"MeshTools::removeDuplicatesFuzzy(): can't remove duplicates in an attributeless mesh", "MeshTools::removeDuplicatesFuzzy(): can't remove duplicates in an attributeless mesh",
(Trade::MeshData{MeshPrimitive::Points, 0})); (Trade::MeshData{MeshPrimitive::Points, 0}));
/* Turn the passed data into an owned mutable instance we can operate on. /* Turn the passed data into an owned mutable instance we can operate on.
There's a chance the original data are already like this, in which case There's a chance the original data are already like this, in which case
this will be just a passthrough. */ this will be just a passthrough. */
Trade::MeshData owned = MeshTools::owned(std::move(data)); Trade::MeshData owned = MeshTools::owned(std::move(mesh));
/* Allocate an interleaved index array for all vertices times all /* Allocate an interleaved index array for all vertices times all
attributes */ attributes */

4
src/Magnum/MeshTools/RemoveDuplicates.h

@ -310,7 +310,7 @@ copies and interleaves the input vertex and index data.
@see @ref isMeshIndexTypeImplementationSpecific(), @see @ref isMeshIndexTypeImplementationSpecific(),
@ref isVertexFormatImplementationSpecific() @ref isVertexFormatImplementationSpecific()
*/ */
MAGNUM_MESHTOOLS_EXPORT Trade::MeshData removeDuplicates(const Trade::MeshData& data); MAGNUM_MESHTOOLS_EXPORT Trade::MeshData removeDuplicates(const Trade::MeshData& mesh);
/** /**
@brief Remove mesh data duplicates with fuzzy comparison for floating-point attributes @brief Remove mesh data duplicates with fuzzy comparison for floating-point attributes
@ -323,7 +323,7 @@ on floating-point attributes. For attributes with a known range (such as
direction) the @p floatEpsilon / @p doubleEpsilon is scaled appropriately, direction) the @p floatEpsilon / @p doubleEpsilon is scaled appropriately,
otherwise it's scaled to calculated value range. otherwise it's scaled to calculated value range.
*/ */
MAGNUM_MESHTOOLS_EXPORT Trade::MeshData removeDuplicatesFuzzy(const Trade::MeshData& data, Float floatEpsilon = Math::TypeTraits<Float>::epsilon(), Double doubleEpsilon = Math::TypeTraits<Double>::epsilon()); MAGNUM_MESHTOOLS_EXPORT Trade::MeshData removeDuplicatesFuzzy(const Trade::MeshData& mesh, Float floatEpsilon = Math::TypeTraits<Float>::epsilon(), Double doubleEpsilon = Math::TypeTraits<Double>::epsilon());
#ifdef MAGNUM_BUILD_DEPRECATED #ifdef MAGNUM_BUILD_DEPRECATED
template<class Vector> std::vector<UnsignedInt> removeDuplicates(std::vector<Vector>& data, typename Vector::Type epsilon) { template<class Vector> std::vector<UnsignedInt> removeDuplicates(std::vector<Vector>& data, typename Vector::Type epsilon) {

196
src/Magnum/MeshTools/Transform.cpp

@ -33,12 +33,12 @@
namespace Magnum { namespace MeshTools { namespace Magnum { namespace MeshTools {
Trade::MeshData transform2D(const Trade::MeshData& data, const Matrix3& transformation, const UnsignedInt id, const InterleaveFlags flags) { Trade::MeshData transform2D(const Trade::MeshData& mesh, const Matrix3& transformation, const UnsignedInt id, const InterleaveFlags flags) {
const Containers::Optional<UnsignedInt> positionAttributeId = data.findAttributeId(Trade::MeshAttribute::Position, id); const Containers::Optional<UnsignedInt> positionAttributeId = mesh.findAttributeId(Trade::MeshAttribute::Position, id);
CORRADE_ASSERT(positionAttributeId, CORRADE_ASSERT(positionAttributeId,
"MeshTools::transform2D(): the mesh has no positions with index" << id, "MeshTools::transform2D(): the mesh has no positions with index" << id,
(Trade::MeshData{MeshPrimitive::Triangles, 0})); (Trade::MeshData{MeshPrimitive::Triangles, 0}));
const VertexFormat positionAttributeFormat = data.attributeFormat(*positionAttributeId); const VertexFormat positionAttributeFormat = mesh.attributeFormat(*positionAttributeId);
CORRADE_ASSERT(!isVertexFormatImplementationSpecific(positionAttributeFormat), CORRADE_ASSERT(!isVertexFormatImplementationSpecific(positionAttributeFormat),
"MeshTools::transform2D(): positions have an implementation-specific format" << reinterpret_cast<void*>(vertexFormatUnwrap(positionAttributeFormat)), "MeshTools::transform2D(): positions have an implementation-specific format" << reinterpret_cast<void*>(vertexFormatUnwrap(positionAttributeFormat)),
(Trade::MeshData{MeshPrimitive::Points, 0})); (Trade::MeshData{MeshPrimitive::Points, 0}));
@ -50,9 +50,9 @@ Trade::MeshData transform2D(const Trade::MeshData& data, const Matrix3& transfor
position attribute format, if needed. Not using Utility::copy() here as position attribute format, if needed. Not using Utility::copy() here as
the view returned by attributeData() might have offset-only attributes the view returned by attributeData() might have offset-only attributes
which interleave() doesn't want. */ which interleave() doesn't want. */
Containers::Array<Trade::MeshAttributeData> attributes{data.attributeCount()}; Containers::Array<Trade::MeshAttributeData> attributes{mesh.attributeCount()};
for(UnsignedInt i = 0; i != data.attributeCount(); ++i) for(UnsignedInt i = 0; i != mesh.attributeCount(); ++i)
attributes[i] = data.attributeData(i); attributes[i] = mesh.attributeData(i);
/* If the position attribute isn't in a desired format, replace it with an /* If the position attribute isn't in a desired format, replace it with an
empty placeholder that we'll unpack the data into */ empty placeholder that we'll unpack the data into */
@ -61,73 +61,73 @@ Trade::MeshData transform2D(const Trade::MeshData& data, const Matrix3& transfor
/* Create the output mesh, making more room for the full formats if /* Create the output mesh, making more room for the full formats if
necessary */ necessary */
Trade::MeshData out = interleave(filterOnlyAttributes(data, Containers::ArrayView<const UnsignedInt>{}), attributes, flags); Trade::MeshData out = interleave(filterOnlyAttributes(mesh, Containers::ArrayView<const UnsignedInt>{}), attributes, flags);
/* If the position attribute wasn't in a desired format, unpack it */ /* If the position attribute wasn't in a desired format, unpack it */
if(positionAttributeFormat != VertexFormat::Vector2) if(positionAttributeFormat != VertexFormat::Vector2)
data.positions2DInto(out.mutableAttribute<Vector2>(*positionAttributeId), id); mesh.positions2DInto(out.mutableAttribute<Vector2>(*positionAttributeId), id);
/* Delegate to the in-place implementation and return */ /* Delegate to the in-place implementation and return */
transform2DInPlace(out, transformation, id); transform2DInPlace(out, transformation, id);
return out; return out;
} }
Trade::MeshData transform2D(Trade::MeshData&& data, const Matrix3& transformation, const UnsignedInt id, const InterleaveFlags flags) { Trade::MeshData transform2D(Trade::MeshData&& mesh, const Matrix3& transformation, const UnsignedInt id, const InterleaveFlags flags) {
/* Perform the operation in-place, if we can transfer the ownership and /* Perform the operation in-place, if we can transfer the ownership and
have positions in the right format already. Explicitly checking for have positions in the right format already. Explicitly checking for
presence of the position attribute so we don't need to duplicate the presence of the position attribute so we don't need to duplicate the
assert here again. */ assert here again. */
if((data.indexDataFlags() & Trade::DataFlag::Owned) && if((mesh.indexDataFlags() & Trade::DataFlag::Owned) &&
(data.vertexDataFlags() & Trade::DataFlag::Owned) && (mesh.vertexDataFlags() & Trade::DataFlag::Owned) &&
data.attributeCount(Trade::MeshAttribute::Position) > id && mesh.attributeCount(Trade::MeshAttribute::Position) > id &&
data.attributeFormat(Trade::MeshAttribute::Position, id) == VertexFormat::Vector2) mesh.attributeFormat(Trade::MeshAttribute::Position, id) == VertexFormat::Vector2)
{ {
transform2DInPlace(data, transformation, id); transform2DInPlace(mesh, transformation, id);
return std::move(data); return std::move(mesh);
} }
/* Otherwise delegate to the function that does all the copying and format /* Otherwise delegate to the function that does all the copying and format
expansion */ expansion */
return transform2D(data, transformation, id, flags); return transform2D(mesh, transformation, id, flags);
} }
void transform2DInPlace(Trade::MeshData& data, const Matrix3& transformation, const UnsignedInt id) { void transform2DInPlace(Trade::MeshData& mesh, const Matrix3& transformation, const UnsignedInt id) {
CORRADE_ASSERT(data.vertexDataFlags() & Trade::DataFlag::Mutable, CORRADE_ASSERT(mesh.vertexDataFlags() & Trade::DataFlag::Mutable,
"MeshTools::transform2DInPlace(): vertex data not mutable", ); "MeshTools::transform2DInPlace(): vertex data not mutable", );
const Containers::Optional<UnsignedInt> positionAttributeId = data.findAttributeId(Trade::MeshAttribute::Position, id); const Containers::Optional<UnsignedInt> positionAttributeId = mesh.findAttributeId(Trade::MeshAttribute::Position, id);
CORRADE_ASSERT(positionAttributeId, CORRADE_ASSERT(positionAttributeId,
"MeshTools::transform2DInPlace(): the mesh has no positions with index" << id, ); "MeshTools::transform2DInPlace(): the mesh has no positions with index" << id, );
CORRADE_ASSERT(data.attributeFormat(*positionAttributeId) == VertexFormat::Vector2, CORRADE_ASSERT(mesh.attributeFormat(*positionAttributeId) == VertexFormat::Vector2,
"MeshTools::transform2DInPlace(): expected" << VertexFormat::Vector2 << "positions but got" << data.attributeFormat(*positionAttributeId), ); "MeshTools::transform2DInPlace(): expected" << VertexFormat::Vector2 << "positions but got" << mesh.attributeFormat(*positionAttributeId), );
/** @todo this needs a proper batch implementation */ /** @todo this needs a proper batch implementation */
for(Vector2& position: data.mutableAttribute<Vector2>(*positionAttributeId)) for(Vector2& position: mesh.mutableAttribute<Vector2>(*positionAttributeId))
position = transformation.transformPoint(position); position = transformation.transformPoint(position);
} }
Trade::MeshData transform3D(const Trade::MeshData& data, const Matrix4& transformation, const UnsignedInt id, const InterleaveFlags flags) { Trade::MeshData transform3D(const Trade::MeshData& mesh, const Matrix4& transformation, const UnsignedInt id, const InterleaveFlags flags) {
const Containers::Optional<UnsignedInt> positionAttributeId = data.findAttributeId(Trade::MeshAttribute::Position, id); const Containers::Optional<UnsignedInt> positionAttributeId = mesh.findAttributeId(Trade::MeshAttribute::Position, id);
CORRADE_ASSERT(positionAttributeId, CORRADE_ASSERT(positionAttributeId,
"MeshTools::transform3D(): the mesh has no positions with index" << id, "MeshTools::transform3D(): the mesh has no positions with index" << id,
(Trade::MeshData{MeshPrimitive::Triangles, 0})); (Trade::MeshData{MeshPrimitive::Triangles, 0}));
const VertexFormat positionAttributeFormat = data.attributeFormat(*positionAttributeId); const VertexFormat positionAttributeFormat = mesh.attributeFormat(*positionAttributeId);
CORRADE_ASSERT(!isVertexFormatImplementationSpecific(positionAttributeFormat), CORRADE_ASSERT(!isVertexFormatImplementationSpecific(positionAttributeFormat),
"MeshTools::transform3D(): positions have an implementation-specific format" << reinterpret_cast<void*>(vertexFormatUnwrap(positionAttributeFormat)), "MeshTools::transform3D(): positions have an implementation-specific format" << reinterpret_cast<void*>(vertexFormatUnwrap(positionAttributeFormat)),
(Trade::MeshData{MeshPrimitive::Points, 0})); (Trade::MeshData{MeshPrimitive::Points, 0}));
CORRADE_ASSERT(vertexFormatComponentCount(positionAttributeFormat) == 3, CORRADE_ASSERT(vertexFormatComponentCount(positionAttributeFormat) == 3,
"MeshTools::transform3D(): expected 3D positions but got" << positionAttributeFormat, "MeshTools::transform3D(): expected 3D positions but got" << positionAttributeFormat,
(Trade::MeshData{MeshPrimitive::Triangles, 0})); (Trade::MeshData{MeshPrimitive::Triangles, 0}));
const Containers::Optional<UnsignedInt> tangentAttributeId = data.findAttributeId(Trade::MeshAttribute::Tangent, id); const Containers::Optional<UnsignedInt> tangentAttributeId = mesh.findAttributeId(Trade::MeshAttribute::Tangent, id);
const Containers::Optional<UnsignedInt> bitangentAttributeId = data.findAttributeId(Trade::MeshAttribute::Bitangent, id); const Containers::Optional<UnsignedInt> bitangentAttributeId = mesh.findAttributeId(Trade::MeshAttribute::Bitangent, id);
const Containers::Optional<UnsignedInt> normalAttributeId = data.findAttributeId(Trade::MeshAttribute::Normal, id); const Containers::Optional<UnsignedInt> normalAttributeId = mesh.findAttributeId(Trade::MeshAttribute::Normal, id);
/* Copy original attributes to a mutable array so we can update the /* Copy original attributes to a mutable array so we can update the
position attribute format, if needed. Not using Utility::copy() here as position attribute format, if needed. Not using Utility::copy() here as
the view returned by attributeData() might have offset-only attributes the view returned by attributeData() might have offset-only attributes
which interleave() doesn't want. */ which interleave() doesn't want. */
Containers::Array<Trade::MeshAttributeData> attributes{data.attributeCount()}; Containers::Array<Trade::MeshAttributeData> attributes{mesh.attributeCount()};
for(UnsignedInt i = 0; i != data.attributeCount(); ++i) for(UnsignedInt i = 0; i != mesh.attributeCount(); ++i)
attributes[i] = data.attributeData(i); attributes[i] = mesh.attributeData(i);
/* If the position/TBN attributes aren't in a desired format, replace them /* If the position/TBN attributes aren't in a desired format, replace them
with an empty placeholder that we'll unpack the data into */ with an empty placeholder that we'll unpack the data into */
@ -136,18 +136,18 @@ Trade::MeshData transform3D(const Trade::MeshData& data, const Matrix4& transfor
VertexFormat tangentAttributeFormat{}; VertexFormat tangentAttributeFormat{};
VertexFormat desiredTangentVertexFormat{}; VertexFormat desiredTangentVertexFormat{};
if(tangentAttributeId) { if(tangentAttributeId) {
tangentAttributeFormat = data.attributeFormat(*tangentAttributeId); tangentAttributeFormat = mesh.attributeFormat(*tangentAttributeId);
CORRADE_ASSERT(!isVertexFormatImplementationSpecific(tangentAttributeFormat), CORRADE_ASSERT(!isVertexFormatImplementationSpecific(tangentAttributeFormat),
"MeshTools::transform3D(): tangents have an implementation-specific format" << reinterpret_cast<void*>(vertexFormatUnwrap(tangentAttributeFormat)), "MeshTools::transform3D(): tangents have an implementation-specific format" << reinterpret_cast<void*>(vertexFormatUnwrap(tangentAttributeFormat)),
(Trade::MeshData{MeshPrimitive::Points, 0})); (Trade::MeshData{MeshPrimitive::Points, 0}));
desiredTangentVertexFormat = vertexFormatComponentCount(data.attributeFormat(*tangentAttributeId)) == 4 ? desiredTangentVertexFormat = vertexFormatComponentCount(mesh.attributeFormat(*tangentAttributeId)) == 4 ?
VertexFormat::Vector4 : VertexFormat::Vector3; VertexFormat::Vector4 : VertexFormat::Vector3;
if(tangentAttributeFormat != desiredTangentVertexFormat) if(tangentAttributeFormat != desiredTangentVertexFormat)
attributes[*tangentAttributeId] = Trade::MeshAttributeData{Trade::MeshAttribute::Tangent, desiredTangentVertexFormat, nullptr}; attributes[*tangentAttributeId] = Trade::MeshAttributeData{Trade::MeshAttribute::Tangent, desiredTangentVertexFormat, nullptr};
} }
VertexFormat bitangentAttributeFormat{}; VertexFormat bitangentAttributeFormat{};
if(bitangentAttributeId) { if(bitangentAttributeId) {
bitangentAttributeFormat = data.attributeFormat(*bitangentAttributeId); bitangentAttributeFormat = mesh.attributeFormat(*bitangentAttributeId);
CORRADE_ASSERT(!isVertexFormatImplementationSpecific(bitangentAttributeFormat), CORRADE_ASSERT(!isVertexFormatImplementationSpecific(bitangentAttributeFormat),
"MeshTools::transform3D(): bitangents have an implementation-specific format" << reinterpret_cast<void*>(vertexFormatUnwrap(bitangentAttributeFormat)), "MeshTools::transform3D(): bitangents have an implementation-specific format" << reinterpret_cast<void*>(vertexFormatUnwrap(bitangentAttributeFormat)),
(Trade::MeshData{MeshPrimitive::Points, 0})); (Trade::MeshData{MeshPrimitive::Points, 0}));
@ -156,7 +156,7 @@ Trade::MeshData transform3D(const Trade::MeshData& data, const Matrix4& transfor
} }
VertexFormat normalAttributeFormat{}; VertexFormat normalAttributeFormat{};
if(normalAttributeId) { if(normalAttributeId) {
normalAttributeFormat = data.attributeFormat(*normalAttributeId); normalAttributeFormat = mesh.attributeFormat(*normalAttributeId);
CORRADE_ASSERT(!isVertexFormatImplementationSpecific(normalAttributeFormat), CORRADE_ASSERT(!isVertexFormatImplementationSpecific(normalAttributeFormat),
"MeshTools::transform3D(): normals have an implementation-specific format" << reinterpret_cast<void*>(vertexFormatUnwrap(normalAttributeFormat)), "MeshTools::transform3D(): normals have an implementation-specific format" << reinterpret_cast<void*>(vertexFormatUnwrap(normalAttributeFormat)),
(Trade::MeshData{MeshPrimitive::Points, 0})); (Trade::MeshData{MeshPrimitive::Points, 0}));
@ -166,76 +166,76 @@ Trade::MeshData transform3D(const Trade::MeshData& data, const Matrix4& transfor
/* Create the output mesh, making more room for the full formats if /* Create the output mesh, making more room for the full formats if
necessary */ necessary */
Trade::MeshData out = interleave(filterOnlyAttributes(data, Containers::ArrayView<const UnsignedInt>{}), attributes, flags); Trade::MeshData out = interleave(filterOnlyAttributes(mesh, Containers::ArrayView<const UnsignedInt>{}), attributes, flags);
/* If the position/TBN attributes weren't in a desired format, unpack them */ /* If the position/TBN attributes weren't in a desired format, unpack them */
if(positionAttributeFormat != VertexFormat::Vector3) if(positionAttributeFormat != VertexFormat::Vector3)
data.positions3DInto(out.mutableAttribute<Vector3>(*positionAttributeId), id); mesh.positions3DInto(out.mutableAttribute<Vector3>(*positionAttributeId), id);
if(tangentAttributeId && tangentAttributeFormat != desiredTangentVertexFormat) { if(tangentAttributeId && tangentAttributeFormat != desiredTangentVertexFormat) {
if(desiredTangentVertexFormat == VertexFormat::Vector4) { if(desiredTangentVertexFormat == VertexFormat::Vector4) {
data.tangentsInto(out.mutableAttribute<Vector4>(*tangentAttributeId).slice(&Vector4::xyz), id); mesh.tangentsInto(out.mutableAttribute<Vector4>(*tangentAttributeId).slice(&Vector4::xyz), id);
data.bitangentSignsInto(out.mutableAttribute<Vector4>(*tangentAttributeId).slice(&Vector4::w), id); mesh.bitangentSignsInto(out.mutableAttribute<Vector4>(*tangentAttributeId).slice(&Vector4::w), id);
} else { } else {
data.tangentsInto(out.mutableAttribute<Vector3>(*tangentAttributeId), id); mesh.tangentsInto(out.mutableAttribute<Vector3>(*tangentAttributeId), id);
} }
} }
if(bitangentAttributeId && bitangentAttributeFormat != VertexFormat::Vector3) if(bitangentAttributeId && bitangentAttributeFormat != VertexFormat::Vector3)
data.bitangentsInto(out.mutableAttribute<Vector3>(*bitangentAttributeId), id); mesh.bitangentsInto(out.mutableAttribute<Vector3>(*bitangentAttributeId), id);
if(normalAttributeId && normalAttributeFormat != VertexFormat::Vector3) if(normalAttributeId && normalAttributeFormat != VertexFormat::Vector3)
data.normalsInto(out.mutableAttribute<Vector3>(*normalAttributeId), id); mesh.normalsInto(out.mutableAttribute<Vector3>(*normalAttributeId), id);
/* Delegate to the in-place implementation and return */ /* Delegate to the in-place implementation and return */
transform3DInPlace(out, transformation, id); transform3DInPlace(out, transformation, id);
return out; return out;
} }
Trade::MeshData transform3D(Trade::MeshData&& data, const Matrix4& transformation, const UnsignedInt id, const InterleaveFlags flags) { Trade::MeshData transform3D(Trade::MeshData&& mesh, const Matrix4& transformation, const UnsignedInt id, const InterleaveFlags flags) {
/* Perform the operation in-place, if we can transfer the ownership and /* Perform the operation in-place, if we can transfer the ownership and
have positions in the right format already. Explicitly checking for have positions in the right format already. Explicitly checking for
presence of the position attribute so we don't need to duplicate the presence of the position attribute so we don't need to duplicate the
assert here again. */ assert here again. */
const Containers::Optional<UnsignedInt> positionAttributeId = data.findAttributeId(Trade::MeshAttribute::Position, id); const Containers::Optional<UnsignedInt> positionAttributeId = mesh.findAttributeId(Trade::MeshAttribute::Position, id);
const Containers::Optional<UnsignedInt> tangentAttributeId = data.findAttributeId(Trade::MeshAttribute::Tangent, id); const Containers::Optional<UnsignedInt> tangentAttributeId = mesh.findAttributeId(Trade::MeshAttribute::Tangent, id);
const Containers::Optional<UnsignedInt> bitangentAttributeId = data.findAttributeId(Trade::MeshAttribute::Bitangent, id); const Containers::Optional<UnsignedInt> bitangentAttributeId = mesh.findAttributeId(Trade::MeshAttribute::Bitangent, id);
const Containers::Optional<UnsignedInt> normalAttributeId = data.findAttributeId(Trade::MeshAttribute::Normal, id); const Containers::Optional<UnsignedInt> normalAttributeId = mesh.findAttributeId(Trade::MeshAttribute::Normal, id);
if((data.indexDataFlags() & Trade::DataFlag::Owned) && if((mesh.indexDataFlags() & Trade::DataFlag::Owned) &&
(data.vertexDataFlags() & Trade::DataFlag::Owned) && (mesh.vertexDataFlags() & Trade::DataFlag::Owned) &&
positionAttributeId && positionAttributeId &&
data.attributeFormat(*positionAttributeId) == VertexFormat::Vector3 && mesh.attributeFormat(*positionAttributeId) == VertexFormat::Vector3 &&
(!tangentAttributeId || data.attributeFormat(*tangentAttributeId) == VertexFormat::Vector3 || data.attributeFormat(*tangentAttributeId) == VertexFormat::Vector4) && (!tangentAttributeId || mesh.attributeFormat(*tangentAttributeId) == VertexFormat::Vector3 || mesh.attributeFormat(*tangentAttributeId) == VertexFormat::Vector4) &&
(!bitangentAttributeId || data.attributeFormat(*bitangentAttributeId) == VertexFormat::Vector3) && (!bitangentAttributeId || mesh.attributeFormat(*bitangentAttributeId) == VertexFormat::Vector3) &&
(!normalAttributeId || data.attributeFormat(*normalAttributeId) == VertexFormat::Vector3)) (!normalAttributeId || mesh.attributeFormat(*normalAttributeId) == VertexFormat::Vector3))
{ {
transform3DInPlace(data, transformation, id); transform3DInPlace(mesh, transformation, id);
return std::move(data); return std::move(mesh);
} }
/* Otherwise delegate to the function that does all the copying and format /* Otherwise delegate to the function that does all the copying and format
expansion */ expansion */
return transform3D(data, transformation, id, flags); return transform3D(mesh, transformation, id, flags);
} }
void transform3DInPlace(Trade::MeshData& data, const Matrix4& transformation, const UnsignedInt id) { void transform3DInPlace(Trade::MeshData& mesh, const Matrix4& transformation, const UnsignedInt id) {
CORRADE_ASSERT(data.vertexDataFlags() & Trade::DataFlag::Mutable, CORRADE_ASSERT(mesh.vertexDataFlags() & Trade::DataFlag::Mutable,
"MeshTools::transform3DInPlace(): vertex data not mutable", ); "MeshTools::transform3DInPlace(): vertex data not mutable", );
const Containers::Optional<UnsignedInt> positionAttributeId = data.findAttributeId(Trade::MeshAttribute::Position, id); const Containers::Optional<UnsignedInt> positionAttributeId = mesh.findAttributeId(Trade::MeshAttribute::Position, id);
CORRADE_ASSERT(positionAttributeId, CORRADE_ASSERT(positionAttributeId,
"MeshTools::transform3DInPlace(): the mesh has no positions with index" << id, ); "MeshTools::transform3DInPlace(): the mesh has no positions with index" << id, );
CORRADE_ASSERT(data.attributeFormat(*positionAttributeId) == VertexFormat::Vector3, CORRADE_ASSERT(mesh.attributeFormat(*positionAttributeId) == VertexFormat::Vector3,
"MeshTools::transform3DInPlace(): expected" << VertexFormat::Vector3 << "positions but got" << data.attributeFormat(*positionAttributeId), ); "MeshTools::transform3DInPlace(): expected" << VertexFormat::Vector3 << "positions but got" << mesh.attributeFormat(*positionAttributeId), );
const Containers::Optional<UnsignedInt> tangentAttributeId = data.findAttributeId(Trade::MeshAttribute::Tangent, id); const Containers::Optional<UnsignedInt> tangentAttributeId = mesh.findAttributeId(Trade::MeshAttribute::Tangent, id);
const VertexFormat tangentAttributeFormat = tangentAttributeId ? data.attributeFormat(*tangentAttributeId) : VertexFormat{}; const VertexFormat tangentAttributeFormat = tangentAttributeId ? mesh.attributeFormat(*tangentAttributeId) : VertexFormat{};
CORRADE_ASSERT(!tangentAttributeId || tangentAttributeFormat == VertexFormat::Vector3 || tangentAttributeFormat == VertexFormat::Vector4, CORRADE_ASSERT(!tangentAttributeId || tangentAttributeFormat == VertexFormat::Vector3 || tangentAttributeFormat == VertexFormat::Vector4,
"MeshTools::transform3DInPlace(): expected" << VertexFormat::Vector3 << "or" << VertexFormat::Vector4 << "tangents but got" << data.attributeFormat(*tangentAttributeId), ); "MeshTools::transform3DInPlace(): expected" << VertexFormat::Vector3 << "or" << VertexFormat::Vector4 << "tangents but got" << mesh.attributeFormat(*tangentAttributeId), );
const Containers::Optional<UnsignedInt> bitangentAttributeId = data.findAttributeId(Trade::MeshAttribute::Bitangent, id); const Containers::Optional<UnsignedInt> bitangentAttributeId = mesh.findAttributeId(Trade::MeshAttribute::Bitangent, id);
CORRADE_ASSERT(!bitangentAttributeId || data.attributeFormat(*bitangentAttributeId) == VertexFormat::Vector3, CORRADE_ASSERT(!bitangentAttributeId || mesh.attributeFormat(*bitangentAttributeId) == VertexFormat::Vector3,
"MeshTools::transform3DInPlace(): expected" << VertexFormat::Vector3 << "bitangents but got" << data.attributeFormat(*bitangentAttributeId), ); "MeshTools::transform3DInPlace(): expected" << VertexFormat::Vector3 << "bitangents but got" << mesh.attributeFormat(*bitangentAttributeId), );
const Containers::Optional<UnsignedInt> normalAttributeId = data.findAttributeId(Trade::MeshAttribute::Normal, id); const Containers::Optional<UnsignedInt> normalAttributeId = mesh.findAttributeId(Trade::MeshAttribute::Normal, id);
CORRADE_ASSERT(!normalAttributeId || data.attributeFormat(*normalAttributeId) == VertexFormat::Vector3, CORRADE_ASSERT(!normalAttributeId || mesh.attributeFormat(*normalAttributeId) == VertexFormat::Vector3,
"MeshTools::transform3DInPlace(): expected" << VertexFormat::Vector3 << "normals but got" << data.attributeFormat(*normalAttributeId), ); "MeshTools::transform3DInPlace(): expected" << VertexFormat::Vector3 << "normals but got" << mesh.attributeFormat(*normalAttributeId), );
/** @todo this needs a proper batch implementation */ /** @todo this needs a proper batch implementation */
for(Vector3& position: data.mutableAttribute<Vector3>(*positionAttributeId)) for(Vector3& position: mesh.mutableAttribute<Vector3>(*positionAttributeId))
position = transformation.transformPoint(position); position = transformation.transformPoint(position);
/* If no other attributes are present, nothing to do */ /* If no other attributes are present, nothing to do */
@ -246,28 +246,28 @@ void transform3DInPlace(Trade::MeshData& data, const Matrix4& transformation, co
if(tangentAttributeId) { if(tangentAttributeId) {
/** @todo this needs a proper batch implementation */ /** @todo this needs a proper batch implementation */
if(tangentAttributeFormat == VertexFormat::Vector3) if(tangentAttributeFormat == VertexFormat::Vector3)
for(Vector3& tangent: data.mutableAttribute<Vector3>(*tangentAttributeId)) for(Vector3& tangent: mesh.mutableAttribute<Vector3>(*tangentAttributeId))
tangent = normalMatrix*tangent; tangent = normalMatrix*tangent;
else for(Vector4& tangent: data.mutableAttribute<Vector4>(*tangentAttributeId)) { else for(Vector4& tangent: mesh.mutableAttribute<Vector4>(*tangentAttributeId)) {
tangent.xyz() = normalMatrix*tangent.xyz(); tangent.xyz() = normalMatrix*tangent.xyz();
/** @todo figure out the fourth component, probably has to get /** @todo figure out the fourth component, probably has to get
flipped when the scale changes handedness? */ flipped when the scale changes handedness? */
} }
} }
/** @todo this needs a proper batch implementation */ /** @todo this needs a proper batch implementation */
if(bitangentAttributeId) for(Vector3& bitangent: data.mutableAttribute<Vector3>(*bitangentAttributeId)) if(bitangentAttributeId) for(Vector3& bitangent: mesh.mutableAttribute<Vector3>(*bitangentAttributeId))
bitangent = normalMatrix*bitangent; bitangent = normalMatrix*bitangent;
/** @todo this needs a proper batch implementation */ /** @todo this needs a proper batch implementation */
if(normalAttributeId) for(Vector3& normal: data.mutableAttribute<Vector3>(*normalAttributeId)) if(normalAttributeId) for(Vector3& normal: mesh.mutableAttribute<Vector3>(*normalAttributeId))
normal = normalMatrix*normal; normal = normalMatrix*normal;
} }
Trade::MeshData transformTextureCoordinates2D(const Trade::MeshData& data, const Matrix3& transformation, const UnsignedInt id, const InterleaveFlags flags) { Trade::MeshData transformTextureCoordinates2D(const Trade::MeshData& mesh, const Matrix3& transformation, const UnsignedInt id, const InterleaveFlags flags) {
const Containers::Optional<UnsignedInt> textureCoordinateAttributeId = data.findAttributeId(Trade::MeshAttribute::TextureCoordinates, id); const Containers::Optional<UnsignedInt> textureCoordinateAttributeId = mesh.findAttributeId(Trade::MeshAttribute::TextureCoordinates, id);
CORRADE_ASSERT(textureCoordinateAttributeId, CORRADE_ASSERT(textureCoordinateAttributeId,
"MeshTools::transformTextureCoordinates2D(): the mesh has no texture coordinates with index" << id, "MeshTools::transformTextureCoordinates2D(): the mesh has no texture coordinates with index" << id,
(Trade::MeshData{MeshPrimitive::Triangles, 0})); (Trade::MeshData{MeshPrimitive::Triangles, 0}));
const VertexFormat textureCoordinateAttributeFormat = data.attributeFormat(*textureCoordinateAttributeId); const VertexFormat textureCoordinateAttributeFormat = mesh.attributeFormat(*textureCoordinateAttributeId);
CORRADE_ASSERT(!isVertexFormatImplementationSpecific(textureCoordinateAttributeFormat), CORRADE_ASSERT(!isVertexFormatImplementationSpecific(textureCoordinateAttributeFormat),
"MeshTools::transformTextureCoordinates2D(): texture coordinates have an implementation-specific format" << reinterpret_cast<void*>(vertexFormatUnwrap(textureCoordinateAttributeFormat)), "MeshTools::transformTextureCoordinates2D(): texture coordinates have an implementation-specific format" << reinterpret_cast<void*>(vertexFormatUnwrap(textureCoordinateAttributeFormat)),
(Trade::MeshData{MeshPrimitive::Points, 0})); (Trade::MeshData{MeshPrimitive::Points, 0}));
@ -276,9 +276,9 @@ Trade::MeshData transformTextureCoordinates2D(const Trade::MeshData& data, const
position attribute format, if needed. Not using Utility::copy() here as position attribute format, if needed. Not using Utility::copy() here as
the view returned by attributeData() might have offset-only attributes the view returned by attributeData() might have offset-only attributes
which interleave() doesn't want. */ which interleave() doesn't want. */
Containers::Array<Trade::MeshAttributeData> attributes{data.attributeCount()}; Containers::Array<Trade::MeshAttributeData> attributes{mesh.attributeCount()};
for(UnsignedInt i = 0; i != data.attributeCount(); ++i) for(UnsignedInt i = 0; i != mesh.attributeCount(); ++i)
attributes[i] = data.attributeData(i); attributes[i] = mesh.attributeData(i);
/* If the position attribute isn't in a desired format, replace it with an /* If the position attribute isn't in a desired format, replace it with an
empty placeholder that we'll unpack the data into */ empty placeholder that we'll unpack the data into */
@ -287,47 +287,47 @@ Trade::MeshData transformTextureCoordinates2D(const Trade::MeshData& data, const
/* Create the output mesh, making more room for the full formats if /* Create the output mesh, making more room for the full formats if
necessary */ necessary */
Trade::MeshData out = interleave(filterOnlyAttributes(data, Containers::ArrayView<const UnsignedInt>{}), attributes, flags); Trade::MeshData out = interleave(filterOnlyAttributes(mesh, Containers::ArrayView<const UnsignedInt>{}), attributes, flags);
/* If the position attribute wasn't in a desired format, unpack it */ /* If the position attribute wasn't in a desired format, unpack it */
if(data.attributeFormat(*textureCoordinateAttributeId) != VertexFormat::Vector2) if(mesh.attributeFormat(*textureCoordinateAttributeId) != VertexFormat::Vector2)
data.textureCoordinates2DInto(out.mutableAttribute<Vector2>(*textureCoordinateAttributeId), id); mesh.textureCoordinates2DInto(out.mutableAttribute<Vector2>(*textureCoordinateAttributeId), id);
/* Delegate to the in-place implementation and return */ /* Delegate to the in-place implementation and return */
transformTextureCoordinates2DInPlace(out, transformation, id); transformTextureCoordinates2DInPlace(out, transformation, id);
return out; return out;
} }
Trade::MeshData transformTextureCoordinates2D(Trade::MeshData&& data, const Matrix3& transformation, const UnsignedInt id, const InterleaveFlags flags) { Trade::MeshData transformTextureCoordinates2D(Trade::MeshData&& mesh, const Matrix3& transformation, const UnsignedInt id, const InterleaveFlags flags) {
/* Perform the operation in-place, if we can transfer the ownership and /* Perform the operation in-place, if we can transfer the ownership and
have positions in the right format already. Explicitly checking for have positions in the right format already. Explicitly checking for
presence of the position attribute so we don't need to duplicate the presence of the position attribute so we don't need to duplicate the
assert here again. */ assert here again. */
if((data.indexDataFlags() & Trade::DataFlag::Owned) && if((mesh.indexDataFlags() & Trade::DataFlag::Owned) &&
(data.vertexDataFlags() & Trade::DataFlag::Owned) && (mesh.vertexDataFlags() & Trade::DataFlag::Owned) &&
data.attributeCount(Trade::MeshAttribute::TextureCoordinates) > id && mesh.attributeCount(Trade::MeshAttribute::TextureCoordinates) > id &&
data.attributeFormat(Trade::MeshAttribute::TextureCoordinates, id) == VertexFormat::Vector2) mesh.attributeFormat(Trade::MeshAttribute::TextureCoordinates, id) == VertexFormat::Vector2)
{ {
transformTextureCoordinates2DInPlace(data, transformation, id); transformTextureCoordinates2DInPlace(mesh, transformation, id);
return std::move(data); return std::move(mesh);
} }
/* Otherwise delegate to the function that does all the copying and format /* Otherwise delegate to the function that does all the copying and format
expansion */ expansion */
return transformTextureCoordinates2D(data, transformation, id, flags); return transformTextureCoordinates2D(mesh, transformation, id, flags);
} }
void transformTextureCoordinates2DInPlace(Trade::MeshData& data, const Matrix3& transformation, const UnsignedInt id) { void transformTextureCoordinates2DInPlace(Trade::MeshData& mesh, const Matrix3& transformation, const UnsignedInt id) {
CORRADE_ASSERT(data.vertexDataFlags() & Trade::DataFlag::Mutable, CORRADE_ASSERT(mesh.vertexDataFlags() & Trade::DataFlag::Mutable,
"MeshTools::transformTextureCoordinates2DInPlace(): vertex data not mutable", ); "MeshTools::transformTextureCoordinates2DInPlace(): vertex data not mutable", );
const Containers::Optional<UnsignedInt> textureCoordinateAttributeId = data.findAttributeId(Trade::MeshAttribute::TextureCoordinates, id); const Containers::Optional<UnsignedInt> textureCoordinateAttributeId = mesh.findAttributeId(Trade::MeshAttribute::TextureCoordinates, id);
CORRADE_ASSERT(textureCoordinateAttributeId, CORRADE_ASSERT(textureCoordinateAttributeId,
"MeshTools::transformTextureCoordinates2DInPlace(): the mesh has no texture coordinates with index" << id, ); "MeshTools::transformTextureCoordinates2DInPlace(): the mesh has no texture coordinates with index" << id, );
CORRADE_ASSERT(data.attributeFormat(*textureCoordinateAttributeId) == VertexFormat::Vector2, CORRADE_ASSERT(mesh.attributeFormat(*textureCoordinateAttributeId) == VertexFormat::Vector2,
"MeshTools::transformTextureCoordinates2DInPlace(): expected" << VertexFormat::Vector2 << "texture coordinates but got" << data.attributeFormat(*textureCoordinateAttributeId), ); "MeshTools::transformTextureCoordinates2DInPlace(): expected" << VertexFormat::Vector2 << "texture coordinates but got" << mesh.attributeFormat(*textureCoordinateAttributeId), );
/** @todo this needs a proper batch implementation */ /** @todo this needs a proper batch implementation */
for(Vector2& position: data.mutableAttribute<Vector2>(*textureCoordinateAttributeId)) for(Vector2& position: mesh.mutableAttribute<Vector2>(*textureCoordinateAttributeId))
position = transformation.transformPoint(position); position = transformation.transformPoint(position);
} }

18
src/Magnum/MeshTools/Transform.h

@ -165,7 +165,7 @@ copy, you can also do an in-place transformation using @ref transform2DInPlace()
@ref Trade::MeshData::attributeFormat(MeshAttribute, UnsignedInt) const, @ref Trade::MeshData::attributeFormat(MeshAttribute, UnsignedInt) const,
@ref isVertexFormatImplementationSpecific() @ref isVertexFormatImplementationSpecific()
*/ */
MAGNUM_MESHTOOLS_EXPORT Trade::MeshData transform2D(const Trade::MeshData& data, const Matrix3& transformation, UnsignedInt id = 0, InterleaveFlags flags = InterleaveFlag::PreserveInterleavedAttributes); MAGNUM_MESHTOOLS_EXPORT Trade::MeshData transform2D(const Trade::MeshData& mesh, const Matrix3& transformation, UnsignedInt id = 0, InterleaveFlags flags = InterleaveFlag::PreserveInterleavedAttributes);
/** /**
@brief Transform 2D positions in a mesh data @brief Transform 2D positions in a mesh data
@ -177,7 +177,7 @@ 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 owned, vertex data is mutable and the positions with index @p id are
@ref VertexFormat::Vector2. @ref VertexFormat::Vector2.
*/ */
MAGNUM_MESHTOOLS_EXPORT Trade::MeshData transform2D(Trade::MeshData&& data, const Matrix3& transformation, UnsignedInt id = 0, InterleaveFlags flags = InterleaveFlag::PreserveInterleavedAttributes); MAGNUM_MESHTOOLS_EXPORT Trade::MeshData transform2D(Trade::MeshData&& mesh, const Matrix3& transformation, UnsignedInt id = 0, InterleaveFlags flags = InterleaveFlag::PreserveInterleavedAttributes);
/** /**
@brief Transform 2D positions in a mesh data in-place @brief Transform 2D positions in a mesh data in-place
@ -194,7 +194,7 @@ packed types, the in-place operation requires the position type to be
@ref Trade::MeshData::attributeCount(MeshAttribute) const, @ref Trade::MeshData::attributeCount(MeshAttribute) const,
@ref Trade::MeshData::attributeFormat(MeshAttribute, UnsignedInt) const @ref Trade::MeshData::attributeFormat(MeshAttribute, UnsignedInt) const
*/ */
MAGNUM_MESHTOOLS_EXPORT void transform2DInPlace(Trade::MeshData& data, const Matrix3& transformation, UnsignedInt id = 0); MAGNUM_MESHTOOLS_EXPORT void transform2DInPlace(Trade::MeshData& mesh, const Matrix3& transformation, UnsignedInt id = 0);
/** /**
@brief Transform 3D positions, normals, tangents and bitangents in a mesh data @brief Transform 3D positions, normals, tangents and bitangents in a mesh data
@ -223,7 +223,7 @@ copy, you can also do an in-place transformation using @ref transform3DInPlace()
@ref Trade::MeshData::attributeFormat(MeshAttribute, UnsignedInt) const, @ref Trade::MeshData::attributeFormat(MeshAttribute, UnsignedInt) const,
@ref isVertexFormatImplementationSpecific() @ref isVertexFormatImplementationSpecific()
*/ */
MAGNUM_MESHTOOLS_EXPORT Trade::MeshData transform3D(const Trade::MeshData& data, const Matrix4& transformation, UnsignedInt id = 0, InterleaveFlags flags = InterleaveFlag::PreserveInterleavedAttributes); MAGNUM_MESHTOOLS_EXPORT Trade::MeshData transform3D(const Trade::MeshData& mesh, const Matrix4& transformation, UnsignedInt id = 0, InterleaveFlags flags = InterleaveFlag::PreserveInterleavedAttributes);
/** /**
@brief Transform 3D positions, normals, tangenta and bitangents in a mesh data @brief Transform 3D positions, normals, tangenta and bitangents in a mesh data
@ -236,7 +236,7 @@ owned, vertex data is mutable, positions, normals and bitangents (if present)
are @ref VertexFormat::Vector3 and tangents (if present) either are @ref VertexFormat::Vector3 and tangents (if present) either
@ref VertexFormat::Vector3 or @ref VertexFormat::Vector4. @ref VertexFormat::Vector3 or @ref VertexFormat::Vector4.
*/ */
MAGNUM_MESHTOOLS_EXPORT Trade::MeshData transform3D(Trade::MeshData&& data, const Matrix4& transformation, UnsignedInt id = 0, InterleaveFlags flags = InterleaveFlag::PreserveInterleavedAttributes); MAGNUM_MESHTOOLS_EXPORT Trade::MeshData transform3D(Trade::MeshData&& mesh, const Matrix4& transformation, UnsignedInt id = 0, InterleaveFlags flags = InterleaveFlag::PreserveInterleavedAttributes);
/** /**
@brief Transform 3D positions, normals, tangents and bitangents in a mesh data in-place @brief Transform 3D positions, normals, tangents and bitangents in a mesh data in-place
@ -258,7 +258,7 @@ than @p id, and indices (if any) are left untouched.
@ref Trade::MeshData::attributeCount(MeshAttribute) const, @ref Trade::MeshData::attributeCount(MeshAttribute) const,
@ref Trade::MeshData::attributeFormat(MeshAttribute, UnsignedInt) const @ref Trade::MeshData::attributeFormat(MeshAttribute, UnsignedInt) const
*/ */
MAGNUM_MESHTOOLS_EXPORT void transform3DInPlace(Trade::MeshData& data, const Matrix4& transformation, UnsignedInt id = 0); MAGNUM_MESHTOOLS_EXPORT void transform3DInPlace(Trade::MeshData& mesh, const Matrix4& transformation, UnsignedInt id = 0);
/** /**
@brief Transform 2D texture coordinates in a mesh data @brief Transform 2D texture coordinates in a mesh data
@ -280,7 +280,7 @@ copy, you can also do an in-place transformation using
@ref Trade::MeshData::attributeCount(MeshAttribute) const, @ref Trade::MeshData::attributeCount(MeshAttribute) const,
@ref isVertexFormatImplementationSpecific() @ref isVertexFormatImplementationSpecific()
*/ */
MAGNUM_MESHTOOLS_EXPORT Trade::MeshData transformTextureCoordinates2D(const Trade::MeshData& data, const Matrix3& transformation, UnsignedInt id = 0, InterleaveFlags flags = InterleaveFlag::PreserveInterleavedAttributes); MAGNUM_MESHTOOLS_EXPORT Trade::MeshData transformTextureCoordinates2D(const Trade::MeshData& mesh, const Matrix3& transformation, UnsignedInt id = 0, InterleaveFlags flags = InterleaveFlag::PreserveInterleavedAttributes);
/** /**
@brief Transform 2D texture coordinates in a mesh data @brief Transform 2D texture coordinates in a mesh data
@ -292,7 +292,7 @@ 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 owned, vertex data is mutable and the coordinates with index @p id are
@ref VertexFormat::Vector2. @ref VertexFormat::Vector2.
*/ */
MAGNUM_MESHTOOLS_EXPORT Trade::MeshData transformTextureCoordinates2D(Trade::MeshData&& data, const Matrix3& transformation, UnsignedInt id = 0, InterleaveFlags flags = InterleaveFlag::PreserveInterleavedAttributes); MAGNUM_MESHTOOLS_EXPORT Trade::MeshData transformTextureCoordinates2D(Trade::MeshData&& mesh, const Matrix3& transformation, UnsignedInt id = 0, InterleaveFlags flags = InterleaveFlag::PreserveInterleavedAttributes);
/** /**
@brief Transform 2D texture coordinates in a mesh data in-place @brief Transform 2D texture coordinates in a mesh data in-place
@ -311,7 +311,7 @@ untouched.
@ref Trade::MeshData::attributeCount(MeshAttribute) const, @ref Trade::MeshData::attributeCount(MeshAttribute) const,
@ref Trade::MeshData::attributeFormat(MeshAttribute, UnsignedInt) const @ref Trade::MeshData::attributeFormat(MeshAttribute, UnsignedInt) const
*/ */
MAGNUM_MESHTOOLS_EXPORT void transformTextureCoordinates2DInPlace(Trade::MeshData& data, const Matrix3& transformation, UnsignedInt id = 0); MAGNUM_MESHTOOLS_EXPORT void transformTextureCoordinates2DInPlace(Trade::MeshData& mesh, const Matrix3& transformation, UnsignedInt id = 0);
}} }}

Loading…
Cancel
Save